summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbcb704366cb5e333a626c18c308c7e0448a8e69f (patch)
treef0d6ab7d78ecdd9207cf46536376b44b91a1ca71
downloadtdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz
tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
-rw-r--r--AUTHORS3
-rw-r--r--COPYING347
-rw-r--r--COPYING-DOCS397
-rw-r--r--INSTALL176
-rw-r--r--Mainpage.dox12
-rw-r--r--Makefile.am.in10
-rw-r--r--Makefile.cvs16
-rw-r--r--README45
-rw-r--r--configure.in.bot69
-rw-r--r--configure.in.in129
-rw-r--r--dcoprss/Makefile.am28
-rw-r--r--dcoprss/article.cpp49
-rw-r--r--dcoprss/cache.cpp129
-rw-r--r--dcoprss/cache.h86
-rw-r--r--dcoprss/client.cpp75
-rw-r--r--dcoprss/document.cpp307
-rw-r--r--dcoprss/feedbrowser.cpp144
-rw-r--r--dcoprss/feedbrowser.h66
-rw-r--r--dcoprss/main.cpp39
-rw-r--r--dcoprss/query.cpp271
-rw-r--r--dcoprss/query.h120
-rw-r--r--dcoprss/rssnewsfeed.h115
-rw-r--r--dcoprss/rssservice.desktop88
-rw-r--r--dcoprss/service.cpp105
-rw-r--r--dcoprss/service.h290
-rw-r--r--dcoprss/test.sh24
-rw-r--r--dcoprss/xmlrpciface.cpp401
-rw-r--r--dcoprss/xmlrpciface.h185
-rw-r--r--doc/Makefile.am5
-rw-r--r--doc/kcontrol/Makefile.am6
-rw-r--r--doc/kcontrol/kcmktalkd/Makefile.am2
-rw-r--r--doc/kcontrol/kcmktalkd/index.docbook55
-rw-r--r--doc/kcontrol/lanbrowser/Makefile.am2
-rw-r--r--doc/kcontrol/lanbrowser/index.docbook15
-rw-r--r--doc/kdict/Makefile.am3
-rw-r--r--doc/kdict/applet.pngbin0 -> 3117 bytes
-rw-r--r--doc/kdict/conf.pngbin0 -> 16464 bytes
-rw-r--r--doc/kdict/index.docbook1052
-rw-r--r--doc/kdict/mainwin.pngbin0 -> 26100 bytes
-rw-r--r--doc/kdict/seteditor.pngbin0 -> 15934 bytes
-rw-r--r--doc/kget/Makefile.am3
-rw-r--r--doc/kget/fileopen.pngbin0 -> 1080 bytes
-rw-r--r--doc/kget/index.docbook751
-rw-r--r--doc/kget/kget1.pngbin0 -> 36694 bytes
-rw-r--r--doc/kget/kget2.pngbin0 -> 37365 bytes
-rw-r--r--doc/kget/kget3.pngbin0 -> 36381 bytes
-rw-r--r--doc/kget/kget4.pngbin0 -> 29974 bytes
-rw-r--r--doc/kget/kget5.pngbin0 -> 37999 bytes
-rw-r--r--doc/knewsticker/Makefile.am4
-rw-r--r--doc/knewsticker/TODO4
-rw-r--r--doc/knewsticker/about-icon.pngbin0 -> 223 bytes
-rw-r--r--doc/knewsticker/checknews-icon.pngbin0 -> 1076 bytes
-rw-r--r--doc/knewsticker/contextmenu.pngbin0 -> 5850 bytes
-rw-r--r--doc/knewsticker/help-icon.pngbin0 -> 510 bytes
-rw-r--r--doc/knewsticker/index.docbook1399
-rw-r--r--doc/knewsticker/kcmnewsticker-filters.pngbin0 -> 27073 bytes
-rw-r--r--doc/knewsticker/kcmnewsticker-general.pngbin0 -> 21593 bytes
-rw-r--r--doc/knewsticker/kcmnewsticker-newssitedialog.pngbin0 -> 14311 bytes
-rw-r--r--doc/knewsticker/kcmnewsticker-newssources.pngbin0 -> 33750 bytes
-rw-r--r--doc/knewsticker/kcmnewsticker-scrollerprefs.pngbin0 -> 26870 bytes
-rw-r--r--doc/knewsticker/kcontrol-icon.pngbin0 -> 2293 bytes
-rw-r--r--doc/knewsticker/knewsticker-childpanel.pngbin0 -> 4353 bytes
-rw-r--r--doc/knewsticker/knewsticker-icon.pngbin0 -> 312 bytes
-rw-r--r--doc/knewsticker/knewsticker-kicker.pngbin0 -> 13451 bytes
-rw-r--r--doc/knewsticker/knewsticker-ownwindow.pngbin0 -> 7496 bytes
-rw-r--r--doc/knewsticker/newarticle-icon.pngbin0 -> 641 bytes
-rw-r--r--doc/knewsticker/newssite-icon.pngbin0 -> 293 bytes
-rw-r--r--doc/knewsticker/oldarticle-icon.pngbin0 -> 494 bytes
-rw-r--r--doc/knewsticker/preferences-icon.pngbin0 -> 646 bytes
-rw-r--r--doc/kopete/Makefile.am3
-rw-r--r--doc/kopete/chatstyle.docbook363
-rw-r--r--doc/kopete/index.docbook1025
-rw-r--r--doc/kopete/menus.docbook876
-rw-r--r--doc/kpf/Makefile.am3
-rw-r--r--doc/kpf/index.docbook410
-rw-r--r--doc/kppp/Makefile.am4
-rw-r--r--doc/kppp/accounting.docbook158
-rw-r--r--doc/kppp/callback.docbook268
-rw-r--r--doc/kppp/chap.docbook191
-rw-r--r--doc/kppp/costsgraphs.fig55
-rw-r--r--doc/kppp/costsgraphs.pngbin0 -> 4683 bytes
-rw-r--r--doc/kppp/dialog-setup.docbook765
-rw-r--r--doc/kppp/getting-online.docbook52
-rw-r--r--doc/kppp/global-settings.docbook385
-rw-r--r--doc/kppp/hayes.docbook927
-rw-r--r--doc/kppp/index.docbook268
-rw-r--r--doc/kppp/kppp-account-accounting-tab.pngbin0 -> 16023 bytes
-rw-r--r--doc/kppp/kppp-account-dial-tab.pngbin0 -> 17949 bytes
-rw-r--r--doc/kppp/kppp-account-dns-tab.pngbin0 -> 14561 bytes
-rw-r--r--doc/kppp/kppp-account-execute-tab.pngbin0 -> 12672 bytes
-rw-r--r--doc/kppp/kppp-account-gateway-tab.pngbin0 -> 11884 bytes
-rw-r--r--doc/kppp/kppp-account-ip-tab.pngbin0 -> 11922 bytes
-rw-r--r--doc/kppp/kppp-account-login-script-tab.pngbin0 -> 14416 bytes
-rw-r--r--doc/kppp/kppp-config.pngbin0 -> 15047 bytes
-rw-r--r--doc/kppp/kppp-device-tab.pngbin0 -> 15882 bytes
-rw-r--r--doc/kppp/kppp-dialler-tab.pngbin0 -> 8420 bytes
-rw-r--r--doc/kppp/kppp-faq.docbook477
-rw-r--r--doc/kppp/kppp-graph-tab.pngbin0 -> 10742 bytes
-rw-r--r--doc/kppp/kppp-misc-tab.pngbin0 -> 12114 bytes
-rw-r--r--doc/kppp/kppp-modem-tab.pngbin0 -> 14686 bytes
-rw-r--r--doc/kppp/kppp-wizard.pngbin0 -> 9295 bytes
-rw-r--r--doc/kppp/kppp.faq.question54
-rw-r--r--doc/kppp/security.docbook96
-rw-r--r--doc/kppp/tricks.docbook175
-rw-r--r--doc/kppp/ttyS-cua.txt46
-rw-r--r--doc/kppp/wizard.docbook117
-rw-r--r--doc/krdc/Makefile.am4
-rw-r--r--doc/krdc/authentication.eps219
-rw-r--r--doc/krdc/authentication.pngbin0 -> 14321 bytes
-rw-r--r--doc/krdc/close.pngbin0 -> 963 bytes
-rw-r--r--doc/krdc/index.docbook750
-rw-r--r--doc/krdc/krdc_window.eps514
-rw-r--r--doc/krdc/krdc_window.pngbin0 -> 50520 bytes
-rw-r--r--doc/krdc/preferences_profilestab.eps284
-rw-r--r--doc/krdc/preferences_profilestab.pngbin0 -> 18191 bytes
-rw-r--r--doc/krdc/preferences_rdpdefaultstab.eps314
-rw-r--r--doc/krdc/preferences_rdpdefaultstab.pngbin0 -> 21334 bytes
-rw-r--r--doc/krdc/preferences_vncdefaultstab.eps307
-rw-r--r--doc/krdc/preferences_vncdefaultstab.pngbin0 -> 20061 bytes
-rw-r--r--doc/krdc/snapshot.eps301
-rw-r--r--doc/krdc/snapshot.pngbin0 -> 21710 bytes
-rw-r--r--doc/krdc/snapshot_connectionspeed.eps251
-rw-r--r--doc/krdc/snapshot_connectionspeed.pngbin0 -> 17158 bytes
-rw-r--r--doc/krdc/snapshot_nobrowse.eps294
-rw-r--r--doc/krdc/snapshot_nobrowse.pngbin0 -> 24598 bytes
-rw-r--r--doc/krdc/snapshot_vncentry.eps248
-rw-r--r--doc/krdc/snapshot_vncentry.pngbin0 -> 15766 bytes
-rw-r--r--doc/krdc/window_fullscreen.pngbin0 -> 1198 bytes
-rw-r--r--doc/krdc/window_nofullscreen.pngbin0 -> 1202 bytes
-rw-r--r--doc/krfb/Makefile.am4
-rw-r--r--doc/krfb/configuration_access.eps341
-rw-r--r--doc/krfb/configuration_access.pngbin0 -> 30281 bytes
-rw-r--r--doc/krfb/configuration_network.eps272
-rw-r--r--doc/krfb/configuration_network.pngbin0 -> 21137 bytes
-rw-r--r--doc/krfb/configuration_session.eps269
-rw-r--r--doc/krfb/configuration_session.pngbin0 -> 20531 bytes
-rw-r--r--doc/krfb/connection.eps593
-rw-r--r--doc/krfb/connection.pngbin0 -> 74362 bytes
-rw-r--r--doc/krfb/email_invitation.eps473
-rw-r--r--doc/krfb/email_invitation.pngbin0 -> 49395 bytes
-rw-r--r--doc/krfb/index.docbook652
-rw-r--r--doc/krfb/invitation_management.eps292
-rw-r--r--doc/krfb/invitation_management.pngbin0 -> 23613 bytes
-rw-r--r--doc/krfb/personal_invitation.eps708
-rw-r--r--doc/krfb/personal_invitation.pngbin0 -> 91931 bytes
-rw-r--r--doc/krfb/screenshot.eps683
-rw-r--r--doc/krfb/screenshot.pngbin0 -> 87269 bytes
-rw-r--r--doc/ksirc/Makefile.am4
-rw-r--r--doc/ksirc/index.docbook1446
-rw-r--r--doc/ktalkd/Makefile.am4
-rw-r--r--doc/ktalkd/index.docbook608
-rw-r--r--doc/kwifimanager/Makefile.am4
-rw-r--r--doc/kwifimanager/index.docbook549
-rw-r--r--doc/lisa/Makefile.am4
-rw-r--r--doc/lisa/index.docbook694
-rw-r--r--filesharing/Makefile.am5
-rw-r--r--filesharing/advanced/Makefile.am1
-rw-r--r--filesharing/advanced/kcm_sambaconf/ChangeLog53
-rw-r--r--filesharing/advanced/kcm_sambaconf/Makefile.am46
-rw-r--r--filesharing/advanced/kcm_sambaconf/TODO2
-rw-r--r--filesharing/advanced/kcm_sambaconf/common.cpp72
-rw-r--r--filesharing/advanced/kcm_sambaconf/common.h40
-rw-r--r--filesharing/advanced/kcm_sambaconf/dictmanager.cpp216
-rw-r--r--filesharing/advanced/kcm_sambaconf/dictmanager.h80
-rw-r--r--filesharing/advanced/kcm_sambaconf/expertuserdlg.ui263
-rw-r--r--filesharing/advanced/kcm_sambaconf/filemodedlg.ui430
-rw-r--r--filesharing/advanced/kcm_sambaconf/filemodedlgimpl.cpp118
-rw-r--r--filesharing/advanced/kcm_sambaconf/filemodedlgimpl.h64
-rw-r--r--filesharing/advanced/kcm_sambaconf/groupselectdlg.ui445
-rw-r--r--filesharing/advanced/kcm_sambaconf/groupselectdlg.ui.h63
-rw-r--r--filesharing/advanced/kcm_sambaconf/hi16-app-kcmsambaconf.pngbin0 -> 499 bytes
-rw-r--r--filesharing/advanced/kcm_sambaconf/hiddenfileview.cpp610
-rw-r--r--filesharing/advanced/kcm_sambaconf/hiddenfileview.h140
-rw-r--r--filesharing/advanced/kcm_sambaconf/joindomaindlg.ui239
-rw-r--r--filesharing/advanced/kcm_sambaconf/joindomaindlg.ui.h17
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcminterface.ui8507
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcminterface.ui.h124
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui1211
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui.h47
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcmsambaconf.cpp1442
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcmsambaconf.desktop97
-rw-r--r--filesharing/advanced/kcm_sambaconf/kcmsambaconf.h159
-rw-r--r--filesharing/advanced/kcm_sambaconf/konqinterface.ui543
-rw-r--r--filesharing/advanced/kcm_sambaconf/konqinterface.ui.h39
-rw-r--r--filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.cpp202
-rw-r--r--filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.h103
-rw-r--r--filesharing/advanced/kcm_sambaconf/passwd.cpp164
-rw-r--r--filesharing/advanced/kcm_sambaconf/passwd.h56
-rw-r--r--filesharing/advanced/kcm_sambaconf/printerdlgimpl.cpp241
-rw-r--r--filesharing/advanced/kcm_sambaconf/printerdlgimpl.h78
-rw-r--r--filesharing/advanced/kcm_sambaconf/programmingconventions.txt10
-rw-r--r--filesharing/advanced/kcm_sambaconf/qmultichecklistitem.cpp159
-rw-r--r--filesharing/advanced/kcm_sambaconf/qmultichecklistitem.h64
-rw-r--r--filesharing/advanced/kcm_sambaconf/sambafile.cpp701
-rw-r--r--filesharing/advanced/kcm_sambaconf/sambafile.h178
-rw-r--r--filesharing/advanced/kcm_sambaconf/sambashare.cpp334
-rw-r--r--filesharing/advanced/kcm_sambaconf/sambashare.h231
-rw-r--r--filesharing/advanced/kcm_sambaconf/share.ui3300
-rw-r--r--filesharing/advanced/kcm_sambaconf/share.ui.h258
-rw-r--r--filesharing/advanced/kcm_sambaconf/sharedlgimpl.cpp478
-rw-r--r--filesharing/advanced/kcm_sambaconf/sharedlgimpl.h100
-rw-r--r--filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.cpp89
-rw-r--r--filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.h46
-rw-r--r--filesharing/advanced/kcm_sambaconf/smbpasswdfile.cpp229
-rw-r--r--filesharing/advanced/kcm_sambaconf/smbpasswdfile.h141
-rw-r--r--filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui332
-rw-r--r--filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui.h112
-rw-r--r--filesharing/advanced/kcm_sambaconf/userselectdlg.ui301
-rw-r--r--filesharing/advanced/kcm_sambaconf/userselectdlg.ui.h50
-rw-r--r--filesharing/advanced/kcm_sambaconf/usertab.ui352
-rw-r--r--filesharing/advanced/kcm_sambaconf/usertab.ui.h39
-rw-r--r--filesharing/advanced/kcm_sambaconf/usertabimpl.cpp418
-rw-r--r--filesharing/advanced/kcm_sambaconf/usertabimpl.h95
-rw-r--r--filesharing/advanced/nfs/Makefile.am13
-rw-r--r--filesharing/advanced/nfs/hostprops.ui558
-rw-r--r--filesharing/advanced/nfs/hostprops.ui.h13
-rw-r--r--filesharing/advanced/nfs/nfsdialog.cpp216
-rw-r--r--filesharing/advanced/nfs/nfsdialog.h58
-rw-r--r--filesharing/advanced/nfs/nfsdialoggui.ui156
-rw-r--r--filesharing/advanced/nfs/nfsdialoggui.ui.h18
-rw-r--r--filesharing/advanced/nfs/nfsentry.cpp381
-rw-r--r--filesharing/advanced/nfs/nfsentry.h120
-rw-r--r--filesharing/advanced/nfs/nfsfile.cpp261
-rw-r--r--filesharing/advanced/nfs/nfsfile.h59
-rw-r--r--filesharing/advanced/nfs/nfshostdlg.cpp226
-rw-r--r--filesharing/advanced/nfs/nfshostdlg.h59
-rw-r--r--filesharing/advanced/propsdlgplugin/Makefile.am25
-rw-r--r--filesharing/advanced/propsdlgplugin/fileshare_propsdlgplugin.desktop102
-rw-r--r--filesharing/advanced/propsdlgplugin/propertiespage.cpp612
-rw-r--r--filesharing/advanced/propsdlgplugin/propertiespage.h87
-rw-r--r--filesharing/advanced/propsdlgplugin/propertiespagegui.ui478
-rw-r--r--filesharing/advanced/propsdlgplugin/propertiespagegui.ui.h56
-rw-r--r--filesharing/advanced/propsdlgplugin/propsdlgshareplugin.cpp126
-rw-r--r--filesharing/advanced/propsdlgplugin/propsdlgshareplugin.h44
-rw-r--r--filesharing/simple/Makefile.am19
-rw-r--r--filesharing/simple/controlcenter.ui486
-rw-r--r--filesharing/simple/controlcenter.ui.h22
-rw-r--r--filesharing/simple/fileshare.cpp442
-rw-r--r--filesharing/simple/fileshare.desktop169
-rw-r--r--filesharing/simple/fileshare.h82
-rw-r--r--filesharing/simple/groupconfigdlg.cpp418
-rw-r--r--filesharing/simple/groupconfigdlg.h68
-rw-r--r--filesharing/simple/groupconfiggui.ui200
-rw-r--r--filesharing/simple/groupconfiggui.ui.h14
-rw-r--r--filesharing/simple/krichtextlabel.cpp112
-rw-r--r--filesharing/simple/krichtextlabel.h65
-rwxr-xr-xinstall-sh238
-rw-r--r--kdenetwork.lsm11
-rw-r--r--kdict/AUTHORS25
-rw-r--r--kdict/ChangeLog79
-rw-r--r--kdict/LICENSE125
-rw-r--r--kdict/Makefile.am33
-rw-r--r--kdict/README47
-rw-r--r--kdict/TODO2
-rw-r--r--kdict/actions.cpp335
-rw-r--r--kdict/actions.h111
-rw-r--r--kdict/applet/Makefile.am19
-rw-r--r--kdict/applet/kdictapplet.cpp405
-rw-r--r--kdict/applet/kdictapplet.desktop147
-rw-r--r--kdict/applet/kdictapplet.h101
-rw-r--r--kdict/application.cpp71
-rw-r--r--kdict/application.h39
-rw-r--r--kdict/dcopinterface.h54
-rw-r--r--kdict/dict.cpp1632
-rw-r--r--kdict/dict.h204
-rw-r--r--kdict/hi128-app-kdict.pngbin0 -> 14431 bytes
-rw-r--r--kdict/hi16-app-kdict.pngbin0 -> 779 bytes
-rw-r--r--kdict/hi32-app-kdict.pngbin0 -> 2101 bytes
-rw-r--r--kdict/hi48-app-kdict.pngbin0 -> 3891 bytes
-rw-r--r--kdict/hi64-app-kdict.pngbin0 -> 5901 bytes
-rw-r--r--kdict/hisc-app-kdict.svgzbin0 -> 5220 bytes
-rw-r--r--kdict/kdict.desktop153
-rw-r--r--kdict/kdictui.rc59
-rw-r--r--kdict/main.cpp56
-rw-r--r--kdict/matchview.cpp473
-rw-r--r--kdict/matchview.h105
-rw-r--r--kdict/options.cpp947
-rw-r--r--kdict/options.h229
-rw-r--r--kdict/pics/Makefile.am2
-rw-r--r--kdict/pics/cr16-action-define_clip.pngbin0 -> 917 bytes
-rw-r--r--kdict/pics/cr16-action-query_erase.pngbin0 -> 396 bytes
-rw-r--r--kdict/pics/cr22-action-define_clip.pngbin0 -> 1228 bytes
-rw-r--r--kdict/pics/cr32-action-define_clip.pngbin0 -> 2109 bytes
-rw-r--r--kdict/queryview.cpp618
-rw-r--r--kdict/queryview.h178
-rw-r--r--kdict/sets.cpp326
-rw-r--r--kdict/sets.h68
-rw-r--r--kdict/toplevel.cpp778
-rw-r--r--kdict/toplevel.h149
-rw-r--r--kdnssd/Makefile.am1
-rw-r--r--kdnssd/ioslave/CONFIG_FORMAT53
-rw-r--r--kdnssd/ioslave/Makefile.am21
-rw-r--r--kdnssd/ioslave/_ftp._tcp5
-rw-r--r--kdnssd/ioslave/_http._tcp5
-rw-r--r--kdnssd/ioslave/_ldap._tcp2
-rw-r--r--kdnssd/ioslave/_nfs._tcp3
-rw-r--r--kdnssd/ioslave/_ssh._tcp4
-rw-r--r--kdnssd/ioslave/_telnet._tcp4
-rw-r--r--kdnssd/ioslave/_webdav._tcp5
-rw-r--r--kdnssd/ioslave/dnssd.cpp369
-rw-r--r--kdnssd/ioslave/dnssd.h87
-rw-r--r--kdnssd/ioslave/invitation.protocol62
-rw-r--r--kdnssd/ioslave/zeroconf.desktop56
-rw-r--r--kdnssd/ioslave/zeroconf.protocol63
-rw-r--r--kdnssd/kdedmodule/Makefile.am13
-rw-r--r--kdnssd/kdedmodule/dnssdwatcher.cpp94
-rw-r--r--kdnssd/kdedmodule/dnssdwatcher.desktop94
-rw-r--r--kdnssd/kdedmodule/dnssdwatcher.h49
-rw-r--r--kdnssd/kdedmodule/watcher.cpp72
-rw-r--r--kdnssd/kdedmodule/watcher.h50
-rw-r--r--kfile-plugins/Makefile.am1
-rw-r--r--kfile-plugins/RETURNED_ITEMS15
-rw-r--r--kfile-plugins/torrent/Makefile.am22
-rw-r--r--kfile-plugins/torrent/README7
-rw-r--r--kfile-plugins/torrent/bbase.h91
-rw-r--r--kfile-plugins/torrent/bdict.cpp220
-rw-r--r--kfile-plugins/torrent/bdict.h203
-rw-r--r--kfile-plugins/torrent/bint.cpp104
-rw-r--r--kfile-plugins/torrent/bint.h111
-rw-r--r--kfile-plugins/torrent/blist.cpp178
-rw-r--r--kfile-plugins/torrent/blist.h200
-rw-r--r--kfile-plugins/torrent/bstring.cpp128
-rw-r--r--kfile-plugins/torrent/bstring.h138
-rw-r--r--kfile-plugins/torrent/bytetape.cpp158
-rw-r--r--kfile-plugins/torrent/bytetape.h194
-rw-r--r--kfile-plugins/torrent/kfile_torrent.cpp404
-rw-r--r--kfile-plugins/torrent/kfile_torrent.desktop58
-rw-r--r--kfile-plugins/torrent/kfile_torrent.h84
-rw-r--r--kget/AUTHORS4
-rw-r--r--kget/Makefile.am50
-rw-r--r--kget/README30
-rw-r--r--kget/TODO10
-rw-r--r--kget/common.h41
-rw-r--r--kget/cr16-app-kget.pngbin0 -> 649 bytes
-rw-r--r--kget/cr16-mime-kget_list.pngbin0 -> 708 bytes
-rw-r--r--kget/cr22-app-kget.pngbin0 -> 1004 bytes
-rw-r--r--kget/cr22-mime-kget_list.pngbin0 -> 1051 bytes
-rw-r--r--kget/cr32-app-kget.pngbin0 -> 1479 bytes
-rw-r--r--kget/cr32-mime-kget_list.pngbin0 -> 1447 bytes
-rw-r--r--kget/cr48-app-kget.pngbin0 -> 3266 bytes
-rw-r--r--kget/cr48-mime-kget_list.pngbin0 -> 2234 bytes
-rw-r--r--kget/dlgAdvanced.cpp97
-rw-r--r--kget/dlgAdvanced.h51
-rw-r--r--kget/dlgAutomation.cpp135
-rw-r--r--kget/dlgAutomation.h61
-rw-r--r--kget/dlgConnection.cpp137
-rw-r--r--kget/dlgConnection.h59
-rw-r--r--kget/dlgDirectories.cpp203
-rw-r--r--kget/dlgDirectories.h62
-rw-r--r--kget/dlgIndividual.cpp379
-rw-r--r--kget/dlgIndividual.h115
-rw-r--r--kget/dlgLimits.cpp72
-rw-r--r--kget/dlgLimits.h51
-rw-r--r--kget/dlgPreferences.cpp164
-rw-r--r--kget/dlgPreferences.h77
-rw-r--r--kget/dlgSystem.cpp77
-rw-r--r--kget/dlgSystem.h51
-rw-r--r--kget/dlgadvancedbase.ui274
-rw-r--r--kget/dlgautomationbase.ui256
-rw-r--r--kget/dlgconnectionbase.ui401
-rw-r--r--kget/dlgdirectoriesbase.ui263
-rw-r--r--kget/dlglimitsbase.ui189
-rw-r--r--kget/dlgsystembase.ui169
-rw-r--r--kget/dockindividual.cpp99
-rw-r--r--kget/dockindividual.h48
-rw-r--r--kget/docking.cpp131
-rw-r--r--kget/docking.h81
-rw-r--r--kget/droptarget.cpp231
-rw-r--r--kget/droptarget.h87
-rw-r--r--kget/eventsrc360
-rw-r--r--kget/getfilejob.cpp37
-rw-r--r--kget/getfilejob.h36
-rw-r--r--kget/http_defaults.h59
-rw-r--r--kget/icons/Makefile.am2
-rw-r--r--kget/icons/cr22-action-bar0.pngbin0 -> 292 bytes
-rw-r--r--kget/icons/cr22-action-bar1.pngbin0 -> 277 bytes
-rw-r--r--kget/icons/cr22-action-bar2.pngbin0 -> 282 bytes
-rw-r--r--kget/icons/cr22-action-bar3.pngbin0 -> 281 bytes
-rw-r--r--kget/icons/cr22-action-bar4.pngbin0 -> 280 bytes
-rw-r--r--kget/icons/cr22-action-bar5.pngbin0 -> 278 bytes
-rw-r--r--kget/icons/cr22-action-bar6.pngbin0 -> 273 bytes
-rw-r--r--kget/icons/cr22-action-kget_dock.pngbin0 -> 978 bytes
-rw-r--r--kget/icons/cr22-action-kget_dock_download.pngbin0 -> 967 bytes
-rw-r--r--kget/icons/cr22-action-tool_clipboard.pngbin0 -> 3617 bytes
-rw-r--r--kget/icons/cr22-action-tool_delay.pngbin0 -> 883 bytes
-rw-r--r--kget/icons/cr22-action-tool_disconnect.pngbin0 -> 1309 bytes
-rw-r--r--kget/icons/cr22-action-tool_dock.pngbin0 -> 1357 bytes
-rw-r--r--kget/icons/cr22-action-tool_drop_target.pngbin0 -> 978 bytes
-rw-r--r--kget/icons/cr22-action-tool_expert.pngbin0 -> 1396 bytes
-rw-r--r--kget/icons/cr22-action-tool_logwindow.pngbin0 -> 1300 bytes
-rw-r--r--kget/icons/cr22-action-tool_normal.pngbin0 -> 624 bytes
-rw-r--r--kget/icons/cr22-action-tool_offline_mode_off.pngbin0 -> 1090 bytes
-rw-r--r--kget/icons/cr22-action-tool_offline_mode_on.pngbin0 -> 1588 bytes
-rw-r--r--kget/icons/cr22-action-tool_pause.pngbin0 -> 1262 bytes
-rw-r--r--kget/icons/cr22-action-tool_queue.pngbin0 -> 1059 bytes
-rw-r--r--kget/icons/cr22-action-tool_restart.pngbin0 -> 1420 bytes
-rw-r--r--kget/icons/cr22-action-tool_resume.pngbin0 -> 1259 bytes
-rw-r--r--kget/icons/cr22-action-tool_shutdown.pngbin0 -> 1398 bytes
-rw-r--r--kget/icons/cr22-action-tool_timer.pngbin0 -> 1488 bytes
-rw-r--r--kget/icons/cr22-action-tool_uselastdir.pngbin0 -> 1376 bytes
-rw-r--r--kget/kfileio.cpp174
-rw-r--r--kget/kfileio.h44
-rw-r--r--kget/kget.desktop77
-rw-r--r--kget/kget_download.desktop57
-rw-r--r--kget/kget_iface.h37
-rw-r--r--kget/kget_plug_in/Makefile.am13
-rw-r--r--kget/kget_plug_in/cr22-action-khtml_kget.pngbin0 -> 978 bytes
-rw-r--r--kget/kget_plug_in/kget_linkview.cpp150
-rw-r--r--kget/kget_plug_in/kget_linkview.h54
-rw-r--r--kget/kget_plug_in/kget_plug_in.cpp189
-rw-r--r--kget/kget_plug_in/kget_plug_in.desktop77
-rw-r--r--kget/kget_plug_in/kget_plug_in.h58
-rw-r--r--kget/kget_plug_in/kget_plug_in.rc11
-rw-r--r--kget/kget_plug_in/links.cpp41
-rw-r--r--kget/kget_plug_in/links.h32
-rw-r--r--kget/kgetui.rc71
-rw-r--r--kget/kmainwidget.cpp2548
-rw-r--r--kget/kmainwidget.h232
-rw-r--r--kget/logwindow.cpp218
-rw-r--r--kget/logwindow.h85
-rw-r--r--kget/main.cpp225
-rw-r--r--kget/pics/Makefile.am11
-rw-r--r--kget/pics/connect0.pngbin0 -> 515 bytes
-rw-r--r--kget/pics/connect1.pngbin0 -> 513 bytes
-rw-r--r--kget/pics/connect2.pngbin0 -> 504 bytes
-rw-r--r--kget/pics/connect3.pngbin0 -> 483 bytes
-rw-r--r--kget/pics/connect4.pngbin0 -> 519 bytes
-rw-r--r--kget/pics/connect5.pngbin0 -> 512 bytes
-rw-r--r--kget/pics/connect6.pngbin0 -> 514 bytes
-rw-r--r--kget/pics/connect7.pngbin0 -> 532 bytes
-rw-r--r--kget/pics/md_delayed.pngbin0 -> 537 bytes
-rw-r--r--kget/pics/md_finished.pngbin0 -> 443 bytes
-rw-r--r--kget/pics/md_queued.pngbin0 -> 362 bytes
-rw-r--r--kget/pics/md_scheduled.pngbin0 -> 420 bytes
-rw-r--r--kget/pics/retrying.pngbin0 -> 273 bytes
-rw-r--r--kget/pics/target.pngbin0 -> 4608 bytes
-rw-r--r--kget/pics/try0.pngbin0 -> 540 bytes
-rw-r--r--kget/pics/try1.pngbin0 -> 550 bytes
-rw-r--r--kget/pics/try2.pngbin0 -> 549 bytes
-rw-r--r--kget/pics/try3.pngbin0 -> 524 bytes
-rw-r--r--kget/pics/try4.pngbin0 -> 528 bytes
-rw-r--r--kget/pics/try5.pngbin0 -> 549 bytes
-rw-r--r--kget/pics/try6.pngbin0 -> 550 bytes
-rw-r--r--kget/pics/try7.pngbin0 -> 542 bytes
-rw-r--r--kget/safedelete.cpp35
-rw-r--r--kget/safedelete.h19
-rw-r--r--kget/settings.cpp287
-rw-r--r--kget/settings.h186
-rw-r--r--kget/slave.cpp331
-rw-r--r--kget/slave.h104
-rw-r--r--kget/slaveevent.cpp72
-rw-r--r--kget/slaveevent.h59
-rw-r--r--kget/sounds/KGet_Added.oggbin0 -> 4227 bytes
-rw-r--r--kget/sounds/KGet_Finished.oggbin0 -> 10557 bytes
-rw-r--r--kget/sounds/KGet_Finished_All.oggbin0 -> 11587 bytes
-rw-r--r--kget/sounds/KGet_Started.oggbin0 -> 6963 bytes
-rw-r--r--kget/sounds/Makefile.am4
-rw-r--r--kget/transfer.cpp1025
-rw-r--r--kget/transfer.h241
-rw-r--r--kget/transferlist.cpp283
-rw-r--r--kget/transferlist.h116
-rw-r--r--kget/version.h35
-rw-r--r--kget/x-kgetlist.desktop74
-rw-r--r--knewsticker/AUTHORS2
-rw-r--r--knewsticker/COPYING20
-rw-r--r--knewsticker/Makefile.am34
-rw-r--r--knewsticker/TODO10
-rw-r--r--knewsticker/common/Makefile.am13
-rw-r--r--knewsticker/common/configaccess.cpp674
-rw-r--r--knewsticker/common/configaccess.h135
-rw-r--r--knewsticker/common/configiface.h56
-rw-r--r--knewsticker/common/newsengine.cpp324
-rw-r--r--knewsticker/common/newsengine.h203
-rw-r--r--knewsticker/common/newsiconmgr.cpp159
-rw-r--r--knewsticker/common/newsiconmgr.h60
-rw-r--r--knewsticker/common/xmlnewsaccess.cpp132
-rw-r--r--knewsticker/common/xmlnewsaccess.h86
-rw-r--r--knewsticker/eventsrc335
-rw-r--r--knewsticker/hi16-app-knewsticker.pngbin0 -> 988 bytes
-rw-r--r--knewsticker/hi32-app-knewsticker.pngbin0 -> 2662 bytes
-rw-r--r--knewsticker/hi48-app-knewsticker.pngbin0 -> 4900 bytes
-rw-r--r--knewsticker/knewsticker-standalone.desktop95
-rw-r--r--knewsticker/knewsticker.cpp546
-rw-r--r--knewsticker/knewsticker.desktop104
-rw-r--r--knewsticker/knewsticker.h146
-rw-r--r--knewsticker/knewsticker.upd13
-rw-r--r--knewsticker/knewstickerconfig.cpp582
-rw-r--r--knewsticker/knewstickerconfig.h110
-rw-r--r--knewsticker/knewstickerconfigwidget.ui1178
-rw-r--r--knewsticker/knewstickerstub/Makefile.am13
-rw-r--r--knewsticker/knewstickerstub/knewstickerstub.cpp80
-rw-r--r--knewsticker/knewstickerstub/knewstickerstub.desktop202
-rwxr-xr-xknewsticker/knt-0.1-0.2.pl104
-rw-r--r--knewsticker/kntsrcfilepropsdlg/Makefile.am12
-rw-r--r--knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.cpp130
-rw-r--r--knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.desktop69
-rw-r--r--knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.h57
-rw-r--r--knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlgwidget.ui215
-rw-r--r--knewsticker/newsscroller.cpp596
-rw-r--r--knewsticker/newsscroller.h102
-rw-r--r--knewsticker/newssourcedlg.ui408
-rw-r--r--knewsticker/newssourcedlgimpl.cpp240
-rw-r--r--knewsticker/newssourcedlgimpl.h82
-rw-r--r--kopete/AUTHORS66
-rw-r--r--kopete/COPYING18
-rw-r--r--kopete/ChangeLog174
-rw-r--r--kopete/INSTALL239
-rw-r--r--kopete/KABC_INTEG_NOTES551
-rw-r--r--kopete/Makefile.am22
-rw-r--r--kopete/README29
-rw-r--r--kopete/TODO90
-rw-r--r--kopete/VERSION1
-rw-r--r--kopete/configure.in.in167
-rw-r--r--kopete/icons/Makefile.am1
-rw-r--r--kopete/icons/cr128-action-voicecall.pngbin0 -> 10616 bytes
-rw-r--r--kopete/icons/cr128-action-webcamreceive.pngbin0 -> 14943 bytes
-rw-r--r--kopete/icons/cr128-action-webcamsend.pngbin0 -> 16096 bytes
-rw-r--r--kopete/icons/cr16-action-account_offline_overlay.pngbin0 -> 475 bytes
-rw-r--r--kopete/icons/cr16-action-add_user.pngbin0 -> 1285 bytes
-rw-r--r--kopete/icons/cr16-action-contact_away_overlay.pngbin0 -> 271 bytes
-rw-r--r--kopete/icons/cr16-action-contact_busy_overlay.pngbin0 -> 285 bytes
-rw-r--r--kopete/icons/cr16-action-contact_food_overlay.pngbin0 -> 501 bytes
-rw-r--r--kopete/icons/cr16-action-contact_invisible_overlay.pngbin0 -> 372 bytes
-rw-r--r--kopete/icons/cr16-action-contact_phone_overlay.pngbin0 -> 563 bytes
-rw-r--r--kopete/icons/cr16-action-contact_xa_overlay.pngbin0 -> 218 bytes
-rw-r--r--kopete/icons/cr16-action-delete_user.pngbin0 -> 964 bytes
-rw-r--r--kopete/icons/cr16-action-edit_user.pngbin0 -> 866 bytes
-rw-r--r--kopete/icons/cr16-action-emoticon.pngbin0 -> 869 bytes
-rw-r--r--kopete/icons/cr16-action-kopeteavailable.pngbin0 -> 881 bytes
-rw-r--r--kopete/icons/cr16-action-kopeteaway.pngbin0 -> 938 bytes
-rw-r--r--kopete/icons/cr16-action-kopeteeditstatusmessage.pngbin0 -> 691 bytes
-rw-r--r--kopete/icons/cr16-action-kopetestatusmessage.pngbin0 -> 1164 bytes
-rw-r--r--kopete/icons/cr16-action-metacontact_away.pngbin0 -> 893 bytes
-rw-r--r--kopete/icons/cr16-action-metacontact_offline.pngbin0 -> 769 bytes
-rw-r--r--kopete/icons/cr16-action-metacontact_online.pngbin0 -> 798 bytes
-rw-r--r--kopete/icons/cr16-action-metacontact_unknown.pngbin0 -> 822 bytes
-rw-r--r--kopete/icons/cr16-action-newmsg.pngbin0 -> 688 bytes
-rw-r--r--kopete/icons/cr16-action-search_user.pngbin0 -> 876 bytes
-rw-r--r--kopete/icons/cr16-action-show_offliners.pngbin0 -> 810 bytes
-rw-r--r--kopete/icons/cr16-action-status_unknown.pngbin0 -> 571 bytes
-rw-r--r--kopete/icons/cr16-action-status_unknown_overlay.pngbin0 -> 582 bytes
-rw-r--r--kopete/icons/cr16-action-voicecall.pngbin0 -> 678 bytes
-rw-r--r--kopete/icons/cr16-action-webcamreceive.pngbin0 -> 917 bytes
-rw-r--r--kopete/icons/cr16-action-webcamsend.pngbin0 -> 919 bytes
-rw-r--r--kopete/icons/cr22-action-account_offline_overlay.pngbin0 -> 626 bytes
-rw-r--r--kopete/icons/cr22-action-add_user.pngbin0 -> 1724 bytes
-rw-r--r--kopete/icons/cr22-action-delete_user.pngbin0 -> 1810 bytes
-rw-r--r--kopete/icons/cr22-action-edit_user.pngbin0 -> 1239 bytes
-rw-r--r--kopete/icons/cr22-action-kopeteavailable.pngbin0 -> 1303 bytes
-rw-r--r--kopete/icons/cr22-action-kopeteaway.pngbin0 -> 1364 bytes
-rw-r--r--kopete/icons/cr22-action-kopeteeditstatusmessage.pngbin0 -> 1076 bytes
-rw-r--r--kopete/icons/cr22-action-search_user.pngbin0 -> 1322 bytes
-rw-r--r--kopete/icons/cr22-action-show_offliners.pngbin0 -> 1283 bytes
-rw-r--r--kopete/icons/cr22-action-voicecall.pngbin0 -> 1332 bytes
-rw-r--r--kopete/icons/cr22-action-webcamreceive.pngbin0 -> 1390 bytes
-rw-r--r--kopete/icons/cr22-action-webcamsend.pngbin0 -> 1386 bytes
-rw-r--r--kopete/icons/cr32-action-account_offline_overlay.pngbin0 -> 976 bytes
-rw-r--r--kopete/icons/cr32-action-add_user.pngbin0 -> 1979 bytes
-rw-r--r--kopete/icons/cr32-action-delete_user.pngbin0 -> 2272 bytes
-rw-r--r--kopete/icons/cr32-action-edit_user.pngbin0 -> 1968 bytes
-rw-r--r--kopete/icons/cr32-action-kopeteavailable.pngbin0 -> 1902 bytes
-rw-r--r--kopete/icons/cr32-action-kopeteaway.pngbin0 -> 1968 bytes
-rw-r--r--kopete/icons/cr32-action-kopeteeditstatusmessage.pngbin0 -> 1627 bytes
-rw-r--r--kopete/icons/cr32-action-metacontact_away.pngbin0 -> 1798 bytes
-rw-r--r--kopete/icons/cr32-action-metacontact_offline.pngbin0 -> 1592 bytes
-rw-r--r--kopete/icons/cr32-action-metacontact_online.pngbin0 -> 1676 bytes
-rw-r--r--kopete/icons/cr32-action-metacontact_unknown.pngbin0 -> 2483 bytes
-rw-r--r--kopete/icons/cr32-action-newmessage.mngbin0 -> 29239 bytes
-rw-r--r--kopete/icons/cr32-action-newmsg.pngbin0 -> 1411 bytes
-rw-r--r--kopete/icons/cr32-action-search_user.pngbin0 -> 2087 bytes
-rw-r--r--kopete/icons/cr32-action-show_offliners.pngbin0 -> 2140 bytes
-rw-r--r--kopete/icons/cr32-action-voicecall.pngbin0 -> 1596 bytes
-rw-r--r--kopete/icons/cr32-action-webcamreceive.pngbin0 -> 2299 bytes
-rw-r--r--kopete/icons/cr32-action-webcamsend.pngbin0 -> 2368 bytes
-rw-r--r--kopete/icons/cr48-action-kopeteavailable.pngbin0 -> 3107 bytes
-rw-r--r--kopete/icons/cr48-action-kopeteaway.pngbin0 -> 3588 bytes
-rw-r--r--kopete/icons/cr48-action-metacontact_away.pngbin0 -> 2848 bytes
-rw-r--r--kopete/icons/cr48-action-metacontact_offline.pngbin0 -> 2494 bytes
-rw-r--r--kopete/icons/cr48-action-metacontact_online.pngbin0 -> 2671 bytes
-rw-r--r--kopete/icons/cr48-action-voicecall.pngbin0 -> 2619 bytes
-rw-r--r--kopete/icons/cr48-action-webcamreceive.pngbin0 -> 4067 bytes
-rw-r--r--kopete/icons/cr48-action-webcamsend.pngbin0 -> 4218 bytes
-rw-r--r--kopete/icons/cr64-action-voicecall.pngbin0 -> 4485 bytes
-rw-r--r--kopete/icons/cr64-action-webcamreceive.pngbin0 -> 5949 bytes
-rw-r--r--kopete/icons/cr64-action-webcamsend.pngbin0 -> 6280 bytes
-rw-r--r--kopete/icons/crsc-action-account_offline_overlay.svgzbin0 -> 4833 bytes
-rw-r--r--kopete/icons/hi16-action-emoticon.pngbin0 -> 327 bytes
-rw-r--r--kopete/icons/hi16-action-kopeteavailable.pngbin0 -> 788 bytes
-rw-r--r--kopete/icons/hi16-action-kopeteaway.pngbin0 -> 712 bytes
-rw-r--r--kopete/icons/hi16-action-newmsg.pngbin0 -> 494 bytes
-rw-r--r--kopete/icons/hi16-action-status_unknown.pngbin0 -> 378 bytes
-rw-r--r--kopete/icons/hi16-action-status_unknown_overlay.pngbin0 -> 430 bytes
-rw-r--r--kopete/icons/hi22-action-kopeteavailable.pngbin0 -> 1262 bytes
-rw-r--r--kopete/icons/hi22-action-kopeteaway.pngbin0 -> 1173 bytes
-rw-r--r--kopete/icons/hi32-action-kopeteavailable.pngbin0 -> 2019 bytes
-rw-r--r--kopete/icons/hi32-action-kopeteaway.pngbin0 -> 1974 bytes
-rw-r--r--kopete/icons/hi32-action-newmessage.mngbin0 -> 29239 bytes
-rw-r--r--kopete/icons/hi48-action-kopeteavailable.pngbin0 -> 3370 bytes
-rw-r--r--kopete/icons/hi48-action-kopeteaway.pngbin0 -> 3278 bytes
-rw-r--r--kopete/kopete.api171
-rw-r--r--kopete/kopete/Makefile.am54
-rw-r--r--kopete/kopete/addaccountwizard/Makefile.am12
-rw-r--r--kopete/kopete/addaccountwizard/addaccountwizard.cpp222
-rw-r--r--kopete/kopete/addaccountwizard/addaccountwizard.h70
-rw-r--r--kopete/kopete/addaccountwizard/addaccountwizardpage1.ui144
-rw-r--r--kopete/kopete/addaccountwizard/addaccountwizardpage2.ui156
-rw-r--r--kopete/kopete/addaccountwizard/addaccountwizardpage3.ui153
-rw-r--r--kopete/kopete/addcontactwizard/Makefile.am12
-rw-r--r--kopete/kopete/addcontactwizard/addcontactwizard.cpp344
-rw-r--r--kopete/kopete/addcontactwizard/addcontactwizard.h85
-rw-r--r--kopete/kopete/addcontactwizard/addcontactwizard_base.ui487
-rw-r--r--kopete/kopete/addcontactwizard/fastaddcontactwizard.cpp135
-rw-r--r--kopete/kopete/addcontactwizard/fastaddcontactwizard.h64
-rw-r--r--kopete/kopete/addcontactwizard/fastaddcontactwizard_base.ui219
-rw-r--r--kopete/kopete/chatwindow/Makefile.am33
-rw-r--r--kopete/kopete/chatwindow/chatmemberslistwidget.cpp263
-rw-r--r--kopete/kopete/chatwindow/chatmemberslistwidget.h121
-rw-r--r--kopete/kopete/chatwindow/chatmessagepart.cpp1328
-rw-r--r--kopete/kopete/chatwindow/chatmessagepart.h248
-rw-r--r--kopete/kopete/chatwindow/chattexteditpart.cpp423
-rw-r--r--kopete/kopete/chatwindow/chattexteditpart.h209
-rw-r--r--kopete/kopete/chatwindow/chatview.cpp1087
-rw-r--r--kopete/kopete/chatwindow/chatview.h421
-rw-r--r--kopete/kopete/chatwindow/chatwindow.desktop126
-rw-r--r--kopete/kopete/chatwindow/emailwindow.desktop111
-rw-r--r--kopete/kopete/chatwindow/emoticonselector.cpp141
-rw-r--r--kopete/kopete/chatwindow/emoticonselector.h76
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindow.cpp1280
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindow.h243
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindow.rc64
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindowstyle.cpp287
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindowstyle.h129
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindowstylemanager.cpp390
-rw-r--r--kopete/kopete/chatwindow/kopetechatwindowstylemanager.h147
-rw-r--r--kopete/kopete/chatwindow/kopeteemailwindow.cpp565
-rw-r--r--kopete/kopete/chatwindow/kopeteemailwindow.h104
-rw-r--r--kopete/kopete/chatwindow/kopeteemailwindow.rc43
-rw-r--r--kopete/kopete/chatwindow/kopeteemoticonaction.cpp228
-rw-r--r--kopete/kopete/chatwindow/kopeteemoticonaction.h90
-rw-r--r--kopete/kopete/chatwindow/kopeterichtexteditpartfull.rc49
-rw-r--r--kopete/kopete/chatwindow/krichtexteditpart.cpp550
-rw-r--r--kopete/kopete/chatwindow/krichtexteditpart.h139
-rw-r--r--kopete/kopete/chatwindow/tests/Makefile.am17
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Info.plist0
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Header.html7
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/Content.html9
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/NextContent.html3
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/Content.html9
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/NextContent.html3
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Status.html3
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant1.css1
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant2.css1
-rw-r--r--kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/main.css1
-rw-r--r--kopete/kopete/chatwindow/tests/chatwindowstyle_test.cpp132
-rw-r--r--kopete/kopete/chatwindow/tests/chatwindowstyle_test.h39
-rw-r--r--kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.cpp326
-rw-r--r--kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.h45
-rw-r--r--kopete/kopete/config/Makefile.am3
-rw-r--r--kopete/kopete/config/accounts/Makefile.am18
-rw-r--r--kopete/kopete/config/accounts/kopete_accountconfig.desktop131
-rw-r--r--kopete/kopete/config/accounts/kopeteaccountconfig.cpp285
-rw-r--r--kopete/kopete/config/accounts/kopeteaccountconfig.h61
-rw-r--r--kopete/kopete/config/accounts/kopeteaccountconfigbase.ui268
-rw-r--r--kopete/kopete/config/appearance/Makefile.am20
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig.cpp870
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig.h70
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig_chatwindow.ui195
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig_colors.ui397
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig_contactlist.ui349
-rw-r--r--kopete/kopete/config/appearance/appearanceconfig_emoticons.ui214
-rw-r--r--kopete/kopete/config/appearance/kopete_appearanceconfig.desktop130
-rw-r--r--kopete/kopete/config/appearance/tooltipeditdialog.cpp226
-rw-r--r--kopete/kopete/config/appearance/tooltipeditdialog.h49
-rw-r--r--kopete/kopete/config/appearance/tooltipeditwidget.ui215
-rw-r--r--kopete/kopete/config/avdevice/Makefile.am24
-rw-r--r--kopete/kopete/config/avdevice/avdeviceconfig.cpp229
-rw-r--r--kopete/kopete/config/avdevice/avdeviceconfig.h79
-rw-r--r--kopete/kopete/config/avdevice/avdeviceconfig_videoconfig.ui624
-rw-r--r--kopete/kopete/config/avdevice/cr128-app-kopete_avdevice.pngbin0 -> 11490 bytes
-rw-r--r--kopete/kopete/config/avdevice/cr22-app-kopete_avdevice.pngbin0 -> 1167 bytes
-rw-r--r--kopete/kopete/config/avdevice/cr32-app-kopete_avdevice.pngbin0 -> 1948 bytes
-rw-r--r--kopete/kopete/config/avdevice/cr64-app-kopete_avdevice.pngbin0 -> 4793 bytes
-rw-r--r--kopete/kopete/config/avdevice/kopete_avdeviceconfig.desktop118
-rw-r--r--kopete/kopete/config/behavior/Makefile.am18
-rw-r--r--kopete/kopete/config/behavior/behaviorconfig.cpp305
-rw-r--r--kopete/kopete/config/behavior/behaviorconfig.h61
-rw-r--r--kopete/kopete/config/behavior/behaviorconfig_chat.ui308
-rw-r--r--kopete/kopete/config/behavior/behaviorconfig_events.ui388
-rw-r--r--kopete/kopete/config/behavior/behaviorconfig_general.ui211
-rw-r--r--kopete/kopete/config/behavior/kopete_behaviorconfig.desktop135
-rw-r--r--kopete/kopete/config/behavior/kopeteawayconfigbase.ui356
-rw-r--r--kopete/kopete/config/identity/Makefile.am15
-rw-r--r--kopete/kopete/config/identity/globalidentitiesmanager.cpp260
-rw-r--r--kopete/kopete/config/identity/globalidentitiesmanager.h143
-rw-r--r--kopete/kopete/config/identity/kopete_identityconfig.desktop112
-rw-r--r--kopete/kopete/config/identity/kopeteidentityconfig.cpp636
-rw-r--r--kopete/kopete/config/identity/kopeteidentityconfig.h80
-rw-r--r--kopete/kopete/config/identity/kopeteidentityconfigbase.ui541
-rw-r--r--kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfg15
-rw-r--r--kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfgc8
-rw-r--r--kopete/kopete/config/plugins/Makefile.am11
-rw-r--r--kopete/kopete/config/plugins/kopetepluginconfig.cpp120
-rw-r--r--kopete/kopete/config/plugins/kopetepluginconfig.h56
-rw-r--r--kopete/kopete/contactlist/Makefile.am28
-rw-r--r--kopete/kopete/contactlist/configure.in.in15
-rw-r--r--kopete/kopete/contactlist/customnotificationprops.cpp168
-rw-r--r--kopete/kopete/contactlist/customnotificationprops.h51
-rw-r--r--kopete/kopete/contactlist/customnotifications.ui238
-rw-r--r--kopete/kopete/contactlist/kabcexport.cpp249
-rw-r--r--kopete/kopete/contactlist/kabcexport.h56
-rw-r--r--kopete/kopete/contactlist/kabcexport_base.ui183
-rw-r--r--kopete/kopete/contactlist/kopeteaddrbookexport.cpp298
-rw-r--r--kopete/kopete/contactlist/kopeteaddrbookexport.h102
-rw-r--r--kopete/kopete/contactlist/kopeteaddrbookexportui.ui149
-rw-r--r--kopete/kopete/contactlist/kopetecontactlistview.cpp2134
-rw-r--r--kopete/kopete/contactlist/kopetecontactlistview.h252
-rw-r--r--kopete/kopete/contactlist/kopetegrouplistaction.cpp66
-rw-r--r--kopete/kopete/contactlist/kopetegrouplistaction.h44
-rw-r--r--kopete/kopete/contactlist/kopetegroupviewitem.cpp286
-rw-r--r--kopete/kopete/contactlist/kopetegroupviewitem.h82
-rw-r--r--kopete/kopete/contactlist/kopetegvipropswidget.ui156
-rw-r--r--kopete/kopete/contactlist/kopetelviprops.cpp570
-rw-r--r--kopete/kopete/contactlist/kopetelviprops.h102
-rw-r--r--kopete/kopete/contactlist/kopetemetacontactlvi.cpp1102
-rw-r--r--kopete/kopete/contactlist/kopetemetacontactlvi.h191
-rw-r--r--kopete/kopete/contactlist/kopetemetalvipropswidget.ui587
-rw-r--r--kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp51
-rw-r--r--kopete/kopete/contactlist/kopetestatusgroupviewitem.h43
-rw-r--r--kopete/kopete/cr16-mime-kopete_emoticons.pngbin0 -> 796 bytes
-rw-r--r--kopete/kopete/cr22-app-kopete_all_away.pngbin0 -> 1183 bytes
-rw-r--r--kopete/kopete/cr22-app-kopete_offline.pngbin0 -> 1458 bytes
-rw-r--r--kopete/kopete/cr22-app-kopete_some_away.pngbin0 -> 1339 bytes
-rw-r--r--kopete/kopete/cr22-app-kopete_some_online.pngbin0 -> 1451 bytes
-rw-r--r--kopete/kopete/cr22-mime-kopete_emoticons.pngbin0 -> 1067 bytes
-rw-r--r--kopete/kopete/eventsrc1950
-rw-r--r--kopete/kopete/groupkabcselectorwidget.ui91
-rw-r--r--kopete/kopete/hi128-app-kopete.pngbin0 -> 22564 bytes
-rw-r--r--kopete/kopete/hi16-app-kopete.pngbin0 -> 731 bytes
-rw-r--r--kopete/kopete/hi22-app-kopete.pngbin0 -> 1462 bytes
-rw-r--r--kopete/kopete/hi32-app-kopete.pngbin0 -> 2671 bytes
-rw-r--r--kopete/kopete/hi48-app-kopete.pngbin0 -> 4932 bytes
-rw-r--r--kopete/kopete/hi64-app-kopete.pngbin0 -> 7633 bytes
-rw-r--r--kopete/kopete/hisc-app-kopete2.svgzbin0 -> 3933 bytes
-rw-r--r--kopete/kopete/kconf_update/Makefile.am32
-rwxr-xr-xkopete/kopete/kconf_update/kopete-account-0.10.pl26
-rw-r--r--kopete/kopete/kconf_update/kopete-account-kconf_update.cpp251
-rw-r--r--kopete/kopete/kconf_update/kopete-account-kconf_update.sh10
-rw-r--r--kopete/kopete/kconf_update/kopete-account-kconf_update.upd9
-rw-r--r--kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.sh2
-rw-r--r--kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.upd4
-rw-r--r--kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.sh2
-rw-r--r--kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.upd4
-rw-r--r--kopete/kopete/kconf_update/kopete-nameTracking.cpp135
-rw-r--r--kopete/kopete/kconf_update/kopete-nameTracking.upd3
-rwxr-xr-xkopete/kopete/kconf_update/kopete-pluginloader.pl28
-rw-r--r--kopete/kopete/kconf_update/kopete-pluginloader.upd4
-rw-r--r--kopete/kopete/kconf_update/kopete-pluginloader2.cpp89
-rw-r--r--kopete/kopete/kconf_update/kopete-pluginloader2.sh10
-rw-r--r--kopete/kopete/kconf_update/kopete-pluginloader2.upd4
-rw-r--r--kopete/kopete/kimiface.h197
-rw-r--r--kopete/kopete/kimifaceimpl.cpp395
-rw-r--r--kopete/kopete/kimifaceimpl.h108
-rw-r--r--kopete/kopete/kopete.desktop126
-rw-r--r--kopete/kopete/kopeteaccountstatusbaricon.cpp60
-rw-r--r--kopete/kopete/kopeteaccountstatusbaricon.h60
-rw-r--r--kopete/kopete/kopeteapplication.cpp338
-rw-r--r--kopete/kopete/kopeteapplication.h94
-rw-r--r--kopete/kopete/kopeteballoon.cpp186
-rw-r--r--kopete/kopete/kopeteballoon.h77
-rw-r--r--kopete/kopete/kopeteeditglobalidentitywidget.cpp255
-rw-r--r--kopete/kopete/kopeteeditglobalidentitywidget.h99
-rw-r--r--kopete/kopete/kopeteiface.cpp344
-rw-r--r--kopete/kopete/kopeteiface.h184
-rw-r--r--kopete/kopete/kopeteui.rc107
-rw-r--r--kopete/kopete/kopetewindow.cpp1112
-rw-r--r--kopete/kopete/kopetewindow.h280
-rw-r--r--kopete/kopete/main.cpp109
-rw-r--r--kopete/kopete/systemtray.cpp435
-rw-r--r--kopete/kopete/systemtray.h104
-rw-r--r--kopete/kopete/x-kopete-emoticons.desktop57
-rw-r--r--kopete/libkopete/API-TODO248
-rw-r--r--kopete/libkopete/Makefile.am71
-rw-r--r--kopete/libkopete/PORTING93
-rw-r--r--kopete/libkopete/avdevice/Makefile.am18
-rw-r--r--kopete/libkopete/avdevice/bayer.cpp118
-rw-r--r--kopete/libkopete/avdevice/bayer.h30
-rw-r--r--kopete/libkopete/avdevice/kxv.cpp711
-rw-r--r--kopete/libkopete/avdevice/kxv.h260
-rw-r--r--kopete/libkopete/avdevice/qvideo.cpp154
-rw-r--r--kopete/libkopete/avdevice/qvideo.h68
-rw-r--r--kopete/libkopete/avdevice/qvideostream.cpp731
-rw-r--r--kopete/libkopete/avdevice/qvideostream.h112
-rw-r--r--kopete/libkopete/avdevice/sonix_compress.cpp180
-rw-r--r--kopete/libkopete/avdevice/sonix_compress.h8
-rw-r--r--kopete/libkopete/avdevice/videocontrol.cpp36
-rw-r--r--kopete/libkopete/avdevice/videocontrol.h82
-rw-r--r--kopete/libkopete/avdevice/videodevice.cpp2752
-rw-r--r--kopete/libkopete/avdevice/videodevice.h333
-rw-r--r--kopete/libkopete/avdevice/videodevicemodelpool.cpp68
-rw-r--r--kopete/libkopete/avdevice/videodevicemodelpool.h53
-rw-r--r--kopete/libkopete/avdevice/videodevicepool.cpp889
-rw-r--r--kopete/libkopete/avdevice/videodevicepool.h127
-rw-r--r--kopete/libkopete/avdevice/videoinput.cpp172
-rw-r--r--kopete/libkopete/avdevice/videoinput.h89
-rw-r--r--kopete/libkopete/clientiface.h56
-rw-r--r--kopete/libkopete/compat/Makefile.am8
-rw-r--r--kopete/libkopete/compat/kpixmapregionselectordialog.cpp127
-rw-r--r--kopete/libkopete/compat/kpixmapregionselectordialog.h107
-rw-r--r--kopete/libkopete/compat/kpixmapregionselectorwidget.cpp450
-rw-r--r--kopete/libkopete/compat/kpixmapregionselectorwidget.h170
-rw-r--r--kopete/libkopete/configure.in.in32
-rw-r--r--kopete/libkopete/connectionmanager.cpp153
-rw-r--r--kopete/libkopete/connectionmanager.h58
-rw-r--r--kopete/libkopete/kabcpersistence.cpp452
-rw-r--r--kopete/libkopete/kabcpersistence.h107
-rw-r--r--kopete/libkopete/kautoconfig.cpp450
-rw-r--r--kopete/libkopete/kautoconfig.h278
-rw-r--r--kopete/libkopete/kcautoconfigmodule.cpp114
-rw-r--r--kopete/libkopete/kcautoconfigmodule.h150
-rw-r--r--kopete/libkopete/knotification.cpp533
-rw-r--r--kopete/libkopete/knotification.h217
-rw-r--r--kopete/libkopete/kopete.kcfg12
-rw-r--r--kopete/libkopete/kopete_export.h30
-rw-r--r--kopete/libkopete/kopeteaccount.cpp581
-rw-r--r--kopete/libkopete/kopeteaccount.h554
-rw-r--r--kopete/libkopete/kopeteaccountmanager.cpp440
-rw-r--r--kopete/libkopete/kopeteaccountmanager.h233
-rw-r--r--kopete/libkopete/kopeteaway.cpp525
-rw-r--r--kopete/libkopete/kopeteaway.h217
-rw-r--r--kopete/libkopete/kopeteawayaction.cpp134
-rw-r--r--kopete/libkopete/kopeteawayaction.h91
-rw-r--r--kopete/libkopete/kopeteawaydialog.cpp143
-rw-r--r--kopete/libkopete/kopeteawaydialog.h201
-rw-r--r--kopete/libkopete/kopeteblacklister.cpp109
-rw-r--r--kopete/libkopete/kopeteblacklister.h124
-rw-r--r--kopete/libkopete/kopetechatsession.cpp515
-rw-r--r--kopete/libkopete/kopetechatsession.h405
-rw-r--r--kopete/libkopete/kopetechatsessionmanager.cpp197
-rw-r--r--kopete/libkopete/kopetechatsessionmanager.h192
-rw-r--r--kopete/libkopete/kopetecommandhandler.cpp490
-rw-r--r--kopete/libkopete/kopetecommandhandler.h219
-rw-r--r--kopete/libkopete/kopetecommandui.rc12
-rw-r--r--kopete/libkopete/kopeteconfig.kcfgc13
-rw-r--r--kopete/libkopete/kopetecontact.cpp863
-rw-r--r--kopete/libkopete/kopetecontact.h557
-rw-r--r--kopete/libkopete/kopetecontactlist.cpp1112
-rw-r--r--kopete/libkopete/kopetecontactlist.h405
-rw-r--r--kopete/libkopete/kopetecontactlistelement.cpp261
-rw-r--r--kopete/libkopete/kopetecontactlistelement.h172
-rw-r--r--kopete/libkopete/kopetecontactproperty.cpp204
-rw-r--r--kopete/libkopete/kopetecontactproperty.h195
-rw-r--r--kopete/libkopete/kopeteeventpresentation.cpp90
-rw-r--r--kopete/libkopete/kopeteeventpresentation.h56
-rw-r--r--kopete/libkopete/kopeteglobal.cpp346
-rw-r--r--kopete/libkopete/kopeteglobal.h176
-rw-r--r--kopete/libkopete/kopetegroup.cpp335
-rw-r--r--kopete/libkopete/kopetegroup.h187
-rw-r--r--kopete/libkopete/kopetemessage.cpp641
-rw-r--r--kopete/libkopete/kopetemessage.h424
-rw-r--r--kopete/libkopete/kopetemessageevent.cpp105
-rw-r--r--kopete/libkopete/kopetemessageevent.h129
-rw-r--r--kopete/libkopete/kopetemessagehandler.cpp111
-rw-r--r--kopete/libkopete/kopetemessagehandler.h225
-rw-r--r--kopete/libkopete/kopetemessagehandlerchain.cpp186
-rw-r--r--kopete/libkopete/kopetemessagehandlerchain.h96
-rw-r--r--kopete/libkopete/kopetemessagemanager.h3
-rw-r--r--kopete/libkopete/kopetemessagemanagerfactory.h3
-rw-r--r--kopete/libkopete/kopetemetacontact.cpp1442
-rw-r--r--kopete/libkopete/kopetemetacontact.h615
-rw-r--r--kopete/libkopete/kopetemimesourcefactory.cpp175
-rw-r--r--kopete/libkopete/kopetemimesourcefactory.h55
-rw-r--r--kopete/libkopete/kopetemimetypehandler.cpp215
-rw-r--r--kopete/libkopete/kopetemimetypehandler.h133
-rw-r--r--kopete/libkopete/kopetenotifydataobject.cpp152
-rw-r--r--kopete/libkopete/kopetenotifydataobject.h58
-rw-r--r--kopete/libkopete/kopetenotifyevent.cpp181
-rw-r--r--kopete/libkopete/kopetenotifyevent.h60
-rw-r--r--kopete/libkopete/kopeteonlinestatus.cpp302
-rw-r--r--kopete/libkopete/kopeteonlinestatus.h415
-rw-r--r--kopete/libkopete/kopeteonlinestatusmanager.cpp436
-rw-r--r--kopete/libkopete/kopeteonlinestatusmanager.h169
-rw-r--r--kopete/libkopete/kopetepassword.cpp502
-rw-r--r--kopete/libkopete/kopetepassword.h220
-rw-r--r--kopete/libkopete/kopetepasswordedaccount.cpp111
-rw-r--r--kopete/libkopete/kopetepasswordedaccount.h132
-rw-r--r--kopete/libkopete/kopetepicture.cpp197
-rw-r--r--kopete/libkopete/kopetepicture.h149
-rw-r--r--kopete/libkopete/kopeteplugin.cpp110
-rw-r--r--kopete/libkopete/kopeteplugin.desktop69
-rw-r--r--kopete/libkopete/kopeteplugin.h217
-rw-r--r--kopete/libkopete/kopetepluginmanager.cpp534
-rw-r--r--kopete/libkopete/kopetepluginmanager.h246
-rw-r--r--kopete/libkopete/kopeteprefs.cpp672
-rw-r--r--kopete/libkopete/kopeteprefs.h318
-rw-r--r--kopete/libkopete/kopeteproperties.cpp50
-rw-r--r--kopete/libkopete/kopeteproperties.h350
-rw-r--r--kopete/libkopete/kopeteprotocol.cpp340
-rw-r--r--kopete/libkopete/kopeteprotocol.desktop66
-rw-r--r--kopete/libkopete/kopeteprotocol.h269
-rw-r--r--kopete/libkopete/kopetesimplemessagehandler.cpp101
-rw-r--r--kopete/libkopete/kopetesimplemessagehandler.h90
-rw-r--r--kopete/libkopete/kopetetask.cpp108
-rw-r--r--kopete/libkopete/kopetetask.h162
-rw-r--r--kopete/libkopete/kopetetransfermanager.cpp271
-rw-r--r--kopete/libkopete/kopetetransfermanager.h212
-rw-r--r--kopete/libkopete/kopeteui.desktop60
-rw-r--r--kopete/libkopete/kopeteuiglobal.cpp60
-rw-r--r--kopete/libkopete/kopeteuiglobal.h72
-rw-r--r--kopete/libkopete/kopeteutils.cpp124
-rw-r--r--kopete/libkopete/kopeteutils.h114
-rw-r--r--kopete/libkopete/kopeteversion.h29
-rw-r--r--kopete/libkopete/kopetewalletmanager.cpp190
-rw-r--r--kopete/libkopete/kopetewalletmanager.h116
-rw-r--r--kopete/libkopete/managedconnectionaccount.cpp73
-rw-r--r--kopete/libkopete/managedconnectionaccount.h79
-rw-r--r--kopete/libkopete/networkstatuscommon.cpp32
-rw-r--r--kopete/libkopete/networkstatuscommon.h33
-rw-r--r--kopete/libkopete/private/Makefile.am13
-rw-r--r--kopete/libkopete/private/kopetecommand.cpp142
-rw-r--r--kopete/libkopete/private/kopetecommand.h109
-rw-r--r--kopete/libkopete/private/kopeteemoticons.cpp559
-rw-r--r--kopete/libkopete/private/kopeteemoticons.h184
-rw-r--r--kopete/libkopete/private/kopeteutils_private.cpp85
-rw-r--r--kopete/libkopete/private/kopeteutils_private.h60
-rw-r--r--kopete/libkopete/private/kopeteviewmanager.cpp364
-rw-r--r--kopete/libkopete/private/kopeteviewmanager.h103
-rw-r--r--kopete/libkopete/tests/Makefile.am41
-rw-r--r--kopete/libkopete/tests/README47
-rwxr-xr-xkopete/libkopete/tests/create_test.rb56
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-1.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-1.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-10.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-10.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-2.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-2.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-3.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-3.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-4.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-4.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-5.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-5.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-6.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-6.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-7.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-7.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-8.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-8.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-9.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/broken-9.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-1.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-1.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-2.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-2.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-3.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-3.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-4.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-4.output1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-5.input1
-rw-r--r--kopete/libkopete/tests/emoticon-parser-testcases/working-5.output1
-rw-r--r--kopete/libkopete/tests/kopetecontactlist_test.cpp55
-rw-r--r--kopete/libkopete/tests/kopetecontactlist_test.h35
-rw-r--r--kopete/libkopete/tests/kopeteemoticontest.cpp132
-rw-r--r--kopete/libkopete/tests/kopeteemoticontest.h39
-rw-r--r--kopete/libkopete/tests/kopetemessage.xsd180
-rw-r--r--kopete/libkopete/tests/kopetemessage_test.cpp324
-rw-r--r--kopete/libkopete/tests/kopetemessage_test.h56
-rw-r--r--kopete/libkopete/tests/kopetepasswordtest_program.cpp132
-rw-r--r--kopete/libkopete/tests/kopetepasswordtest_program.h16
-rw-r--r--kopete/libkopete/tests/kopetepropertiestest.cpp59
-rw-r--r--kopete/libkopete/tests/kopetepropertiestest.h36
-rw-r--r--kopete/libkopete/tests/kopetewallettest_program.cpp98
-rw-r--r--kopete/libkopete/tests/kopetewallettest_program.h17
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/broken-html-1.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/broken-html-1.output1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-html-1.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-html-1.output1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-html-2.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-html-2.output1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.output1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.output1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.input1
-rw-r--r--kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.output1
-rw-r--r--kopete/libkopete/tests/mock/Makefile.am14
-rw-r--r--kopete/libkopete/tests/mock/kopeteaccount_mock.cpp61
-rw-r--r--kopete/libkopete/tests/mock/kopeteaccount_mock.h54
-rw-r--r--kopete/libkopete/tests/mock/kopetecontact_mock.cpp44
-rw-r--r--kopete/libkopete/tests/mock/kopetecontact_mock.h49
-rw-r--r--kopete/libkopete/tests/mock/kopetemessage_mock.cpp20
-rw-r--r--kopete/libkopete/tests/mock/kopetemessage_mock.h39
-rw-r--r--kopete/libkopete/tests/mock/kopetemetacontact_mock.cpp20
-rw-r--r--kopete/libkopete/tests/mock/kopetemetacontact_mock.h41
-rw-r--r--kopete/libkopete/tests/mock/kopeteprotocol_mock.cpp49
-rw-r--r--kopete/libkopete/tests/mock/kopeteprotocol_mock.h53
-rw-r--r--kopete/libkopete/tests/template_test.cpp37
-rw-r--r--kopete/libkopete/tests/template_test.h35
-rw-r--r--kopete/libkopete/ui/Makefile.am31
-rw-r--r--kopete/libkopete/ui/accountselector.cpp186
-rw-r--r--kopete/libkopete/ui/accountselector.h92
-rw-r--r--kopete/libkopete/ui/addcontactpage.cpp29
-rw-r--r--kopete/libkopete/ui/addcontactpage.h65
-rw-r--r--kopete/libkopete/ui/addressbooklinkwidget.cpp99
-rw-r--r--kopete/libkopete/ui/addressbooklinkwidget.h81
-rw-r--r--kopete/libkopete/ui/addressbooklinkwidget_base.ui78
-rw-r--r--kopete/libkopete/ui/addressbookselectordialog.cpp89
-rw-r--r--kopete/libkopete/ui/addressbookselectordialog.h90
-rw-r--r--kopete/libkopete/ui/addressbookselectorwidget.cpp171
-rw-r--r--kopete/libkopete/ui/addressbookselectorwidget.h90
-rw-r--r--kopete/libkopete/ui/addressbookselectorwidget_base.ui175
-rw-r--r--kopete/libkopete/ui/addresseeitem.cpp63
-rw-r--r--kopete/libkopete/ui/addresseeitem.h68
-rw-r--r--kopete/libkopete/ui/contactaddednotifydialog.cpp178
-rw-r--r--kopete/libkopete/ui/contactaddednotifydialog.h175
-rw-r--r--kopete/libkopete/ui/contactaddednotifywidget.ui260
-rw-r--r--kopete/libkopete/ui/editaccountwidget.cpp49
-rw-r--r--kopete/libkopete/ui/editaccountwidget.h104
-rw-r--r--kopete/libkopete/ui/fileconfirmbase.ui151
-rw-r--r--kopete/libkopete/ui/kopete.widgets24
-rw-r--r--kopete/libkopete/ui/kopeteawaydialogbase.ui85
-rw-r--r--kopete/libkopete/ui/kopetecontactaction.cpp54
-rw-r--r--kopete/libkopete/ui/kopetecontactaction.h61
-rw-r--r--kopete/libkopete/ui/kopetefileconfirmdialog.cpp117
-rw-r--r--kopete/libkopete/ui/kopetefileconfirmdialog.h57
-rw-r--r--kopete/libkopete/ui/kopetelistview.cpp215
-rw-r--r--kopete/libkopete/ui/kopetelistview.h74
-rw-r--r--kopete/libkopete/ui/kopetelistviewitem.cpp1314
-rw-r--r--kopete/libkopete/ui/kopetelistviewitem.h462
-rw-r--r--kopete/libkopete/ui/kopetelistviewsearchline.cpp138
-rw-r--r--kopete/libkopete/ui/kopetelistviewsearchline.h66
-rw-r--r--kopete/libkopete/ui/kopetepassworddialog.ui109
-rw-r--r--kopete/libkopete/ui/kopetepasswordwidget.cpp132
-rw-r--r--kopete/libkopete/ui/kopetepasswordwidget.h109
-rw-r--r--kopete/libkopete/ui/kopetepasswordwidgetbase.ui98
-rw-r--r--kopete/libkopete/ui/kopetestdaction.cpp129
-rw-r--r--kopete/libkopete/ui/kopetestdaction.h119
-rw-r--r--kopete/libkopete/ui/kopeteview.cpp52
-rw-r--r--kopete/libkopete/ui/kopeteview.h176
-rw-r--r--kopete/libkopete/ui/kopeteviewplugin.cpp28
-rw-r--r--kopete/libkopete/ui/kopeteviewplugin.h59
-rw-r--r--kopete/libkopete/ui/kopetewidgets.cpp131
-rw-r--r--kopete/libkopete/ui/metacontactselectorwidget.cpp287
-rw-r--r--kopete/libkopete/ui/metacontactselectorwidget.h103
-rw-r--r--kopete/libkopete/ui/metacontactselectorwidget_base.ui107
-rw-r--r--kopete/libkopete/ui/userinfodialog.cpp277
-rw-r--r--kopete/libkopete/ui/userinfodialog.h91
-rw-r--r--kopete/libkopete/ui/widgets.cw21
-rw-r--r--kopete/libkopete/webcamwidget.cpp107
-rw-r--r--kopete/libkopete/webcamwidget.h66
-rw-r--r--kopete/plugins/Makefile.am12
-rw-r--r--kopete/plugins/addbookmarks/Makefile.am22
-rw-r--r--kopete/plugins/addbookmarks/addbookmarksplugin.cpp194
-rw-r--r--kopete/plugins/addbookmarks/addbookmarksplugin.h56
-rw-r--r--kopete/plugins/addbookmarks/addbookmarkspreferences.cpp114
-rw-r--r--kopete/plugins/addbookmarks/addbookmarkspreferences.h45
-rw-r--r--kopete/plugins/addbookmarks/addbookmarksprefssettings.cpp87
-rw-r--r--kopete/plugins/addbookmarks/addbookmarksprefssettings.h49
-rw-r--r--kopete/plugins/addbookmarks/addbookmarksprefsui.ui104
-rw-r--r--kopete/plugins/addbookmarks/kopete_addbookmarks.desktop126
-rw-r--r--kopete/plugins/addbookmarks/kopete_addbookmarks_config.desktop121
-rw-r--r--kopete/plugins/alias/Makefile.am19
-rw-r--r--kopete/plugins/alias/aliasdialog.ui193
-rw-r--r--kopete/plugins/alias/aliasdialogbase.ui107
-rw-r--r--kopete/plugins/alias/aliasplugin.cpp38
-rw-r--r--kopete/plugins/alias/aliasplugin.h31
-rw-r--r--kopete/plugins/alias/aliaspreferences.cpp502
-rw-r--r--kopete/plugins/alias/aliaspreferences.h56
-rw-r--r--kopete/plugins/alias/editaliasdialog.cpp51
-rw-r--r--kopete/plugins/alias/editaliasdialog.h38
-rw-r--r--kopete/plugins/alias/kopete_alias.desktop108
-rw-r--r--kopete/plugins/alias/kopete_alias_config.desktop104
-rw-r--r--kopete/plugins/autoreplace/Makefile.am21
-rw-r--r--kopete/plugins/autoreplace/autoreplaceconfig.cpp133
-rw-r--r--kopete/plugins/autoreplace/autoreplaceconfig.h58
-rw-r--r--kopete/plugins/autoreplace/autoreplaceplugin.cpp128
-rw-r--r--kopete/plugins/autoreplace/autoreplaceplugin.h63
-rw-r--r--kopete/plugins/autoreplace/autoreplacepreferences.cpp215
-rw-r--r--kopete/plugins/autoreplace/autoreplacepreferences.h64
-rw-r--r--kopete/plugins/autoreplace/autoreplaceprefs.ui219
-rw-r--r--kopete/plugins/autoreplace/icons/Makefile.am3
-rw-r--r--kopete/plugins/autoreplace/icons/cr32-app-autoreplace.pngbin0 -> 819 bytes
-rw-r--r--kopete/plugins/autoreplace/kopete_autoreplace.desktop128
-rw-r--r--kopete/plugins/autoreplace/kopete_autoreplace_config.desktop124
-rw-r--r--kopete/plugins/connectionstatus/Makefile.am12
-rw-r--r--kopete/plugins/connectionstatus/connectionstatusplugin.cpp137
-rw-r--r--kopete/plugins/connectionstatus/connectionstatusplugin.h58
-rw-r--r--kopete/plugins/connectionstatus/kopete_connectionstatus.desktop125
-rw-r--r--kopete/plugins/contactnotes/Makefile.am15
-rw-r--r--kopete/plugins/contactnotes/contactnotesedit.cpp55
-rw-r--r--kopete/plugins/contactnotes/contactnotesedit.h53
-rw-r--r--kopete/plugins/contactnotes/contactnotesplugin.cpp83
-rw-r--r--kopete/plugins/contactnotes/contactnotesplugin.h66
-rw-r--r--kopete/plugins/contactnotes/contactnotesui.rc12
-rw-r--r--kopete/plugins/contactnotes/kopete_contactnotes.desktop124
-rw-r--r--kopete/plugins/cryptography/Makefile.am25
-rw-r--r--kopete/plugins/cryptography/cryptographychatui.rc9
-rw-r--r--kopete/plugins/cryptography/cryptographyguiclient.cpp75
-rw-r--r--kopete/plugins/cryptography/cryptographyguiclient.h41
-rw-r--r--kopete/plugins/cryptography/cryptographyplugin.cpp326
-rw-r--r--kopete/plugins/cryptography/cryptographyplugin.h102
-rw-r--r--kopete/plugins/cryptography/cryptographypreferences.cpp49
-rw-r--r--kopete/plugins/cryptography/cryptographypreferences.h42
-rw-r--r--kopete/plugins/cryptography/cryptographyprefsbase.ui196
-rw-r--r--kopete/plugins/cryptography/cryptographyselectuserkey.cpp71
-rw-r--r--kopete/plugins/cryptography/cryptographyselectuserkey.h51
-rw-r--r--kopete/plugins/cryptography/cryptographyui.rc12
-rw-r--r--kopete/plugins/cryptography/cryptographyuserkey_ui.ui79
-rw-r--r--kopete/plugins/cryptography/icons/Makefile.am2
-rw-r--r--kopete/plugins/cryptography/icons/cr16-action-kgpg_key1.pngbin0 -> 267 bytes
-rw-r--r--kopete/plugins/cryptography/icons/cr16-action-kgpg_key2.pngbin0 -> 440 bytes
-rw-r--r--kopete/plugins/cryptography/icons/cr16-action-kgpg_key3.pngbin0 -> 671 bytes
-rw-r--r--kopete/plugins/cryptography/kgpginterface.cpp176
-rw-r--r--kopete/plugins/cryptography/kgpginterface.h91
-rw-r--r--kopete/plugins/cryptography/kgpgselkey.cpp245
-rw-r--r--kopete/plugins/cryptography/kgpgselkey.h64
-rw-r--r--kopete/plugins/cryptography/kopete_cryptography.desktop129
-rw-r--r--kopete/plugins/cryptography/kopete_cryptography_config.desktop126
-rw-r--r--kopete/plugins/cryptography/popuppublic.cpp514
-rw-r--r--kopete/plugins/cryptography/popuppublic.h78
-rw-r--r--kopete/plugins/highlight/Makefile.am21
-rw-r--r--kopete/plugins/highlight/filter.cpp32
-rw-r--r--kopete/plugins/highlight/filter.h54
-rw-r--r--kopete/plugins/highlight/highlightconfig.cpp206
-rw-r--r--kopete/plugins/highlight/highlightconfig.h46
-rw-r--r--kopete/plugins/highlight/highlightplugin.cpp106
-rw-r--r--kopete/plugins/highlight/highlightplugin.h63
-rw-r--r--kopete/plugins/highlight/highlightpreferences.cpp269
-rw-r--r--kopete/plugins/highlight/highlightpreferences.h58
-rw-r--r--kopete/plugins/highlight/highlightprefsbase.ui466
-rw-r--r--kopete/plugins/highlight/icons/Makefile.am3
-rw-r--r--kopete/plugins/highlight/icons/cr32-app-highlight.pngbin0 -> 1239 bytes
-rw-r--r--kopete/plugins/highlight/kopete_highlight.desktop129
-rw-r--r--kopete/plugins/highlight/kopete_highlight_config.desktop125
-rw-r--r--kopete/plugins/history/Makefile.am26
-rw-r--r--kopete/plugins/history/converter.cpp341
-rw-r--r--kopete/plugins/history/historychatui.rc17
-rw-r--r--kopete/plugins/history/historyconfig.kcfg35
-rw-r--r--kopete/plugins/history/historyconfig.kcfgc7
-rw-r--r--kopete/plugins/history/historydialog.cpp613
-rw-r--r--kopete/plugins/history/historydialog.h146
-rw-r--r--kopete/plugins/history/historyguiclient.cpp115
-rw-r--r--kopete/plugins/history/historyguiclient.h55
-rw-r--r--kopete/plugins/history/historylogger.cpp851
-rw-r--r--kopete/plugins/history/historylogger.h217
-rw-r--r--kopete/plugins/history/historyplugin.cpp194
-rw-r--r--kopete/plugins/history/historyplugin.h106
-rw-r--r--kopete/plugins/history/historypreferences.cpp88
-rw-r--r--kopete/plugins/history/historypreferences.h48
-rw-r--r--kopete/plugins/history/historyprefsui.ui187
-rw-r--r--kopete/plugins/history/historyui.rc12
-rw-r--r--kopete/plugins/history/historyviewer.ui347
-rw-r--r--kopete/plugins/history/kopete_history.desktop139
-rw-r--r--kopete/plugins/history/kopete_history_config.desktop141
-rw-r--r--kopete/plugins/latex/Makefile.am27
-rw-r--r--kopete/plugins/latex/icons/Makefile.am3
-rw-r--r--kopete/plugins/latex/icons/cr32-app-latex.pngbin0 -> 474 bytes
-rw-r--r--kopete/plugins/latex/kopete_latex.desktop71
-rw-r--r--kopete/plugins/latex/kopete_latex_config.desktop69
-rwxr-xr-xkopete/plugins/latex/kopete_latexconvert.sh234
-rw-r--r--kopete/plugins/latex/latexchatui.rc9
-rw-r--r--kopete/plugins/latex/latexconfig.kcfg19
-rw-r--r--kopete/plugins/latex/latexconfig.kcfgc7
-rw-r--r--kopete/plugins/latex/latexguiclient.cpp76
-rw-r--r--kopete/plugins/latex/latexguiclient.h53
-rw-r--r--kopete/plugins/latex/latexplugin.cpp259
-rw-r--r--kopete/plugins/latex/latexplugin.h77
-rw-r--r--kopete/plugins/latex/latexpreferences.cpp76
-rw-r--r--kopete/plugins/latex/latexpreferences.h48
-rw-r--r--kopete/plugins/latex/latexprefsbase.ui168
-rw-r--r--kopete/plugins/motionautoaway/COPYING.motion339
-rw-r--r--kopete/plugins/motionautoaway/Makefile.am24
-rw-r--r--kopete/plugins/motionautoaway/configure.in.in18
-rw-r--r--kopete/plugins/motionautoaway/kopete_motionaway.desktop115
-rw-r--r--kopete/plugins/motionautoaway/kopete_motionaway_config.desktop111
-rw-r--r--kopete/plugins/motionautoaway/motionawayconfig.kcfg25
-rw-r--r--kopete/plugins/motionautoaway/motionawayconfig.kcfgc8
-rw-r--r--kopete/plugins/motionautoaway/motionawayplugin.cpp308
-rw-r--r--kopete/plugins/motionautoaway/motionawayplugin.h93
-rw-r--r--kopete/plugins/motionautoaway/motionawaypreferences.cpp70
-rw-r--r--kopete/plugins/motionautoaway/motionawaypreferences.h45
-rw-r--r--kopete/plugins/motionautoaway/motionawayprefs.ui297
-rw-r--r--kopete/plugins/netmeeting/Makefile.am23
-rw-r--r--kopete/plugins/netmeeting/kopete_netmeeting.desktop81
-rw-r--r--kopete/plugins/netmeeting/kopete_netmeeting_config.desktop77
-rw-r--r--kopete/plugins/netmeeting/netmeetingchatui.rc9
-rw-r--r--kopete/plugins/netmeeting/netmeetingguiclient.cpp61
-rw-r--r--kopete/plugins/netmeeting/netmeetingguiclient.h60
-rw-r--r--kopete/plugins/netmeeting/netmeetinginvitation.cpp183
-rw-r--r--kopete/plugins/netmeeting/netmeetinginvitation.h56
-rw-r--r--kopete/plugins/netmeeting/netmeetingplugin.cpp91
-rw-r--r--kopete/plugins/netmeeting/netmeetingplugin.h46
-rw-r--r--kopete/plugins/netmeeting/netmeetingpreferences.cpp81
-rw-r--r--kopete/plugins/netmeeting/netmeetingpreferences.h46
-rw-r--r--kopete/plugins/netmeeting/netmeetingprefs_ui.ui148
-rw-r--r--kopete/plugins/nowlistening/DESIGN52
-rw-r--r--kopete/plugins/nowlistening/Makefile.am25
-rw-r--r--kopete/plugins/nowlistening/README41
-rw-r--r--kopete/plugins/nowlistening/configure.in.in59
-rw-r--r--kopete/plugins/nowlistening/kopete_nowlistening.desktop125
-rw-r--r--kopete/plugins/nowlistening/kopete_nowlistening_config.desktop121
-rw-r--r--kopete/plugins/nowlistening/nlamarok.cpp125
-rw-r--r--kopete/plugins/nowlistening/nlamarok.h40
-rw-r--r--kopete/plugins/nowlistening/nljuk.cpp112
-rw-r--r--kopete/plugins/nowlistening/nljuk.h41
-rw-r--r--kopete/plugins/nowlistening/nlkaffeine.cpp101
-rw-r--r--kopete/plugins/nowlistening/nlkaffeine.h40
-rw-r--r--kopete/plugins/nowlistening/nlkscd.cpp121
-rw-r--r--kopete/plugins/nowlistening/nlkscd.h41
-rw-r--r--kopete/plugins/nowlistening/nlmediaplayer.h58
-rw-r--r--kopete/plugins/nowlistening/nlnoatun.cpp147
-rw-r--r--kopete/plugins/nowlistening/nlnoatun.h41
-rw-r--r--kopete/plugins/nowlistening/nlxmms.cpp73
-rw-r--r--kopete/plugins/nowlistening/nlxmms.h41
-rw-r--r--kopete/plugins/nowlistening/nowlisteningchatui.rc9
-rw-r--r--kopete/plugins/nowlistening/nowlisteningconfig.kcfg54
-rw-r--r--kopete/plugins/nowlistening/nowlisteningconfig.kcfgc8
-rw-r--r--kopete/plugins/nowlistening/nowlisteningguiclient.cpp79
-rw-r--r--kopete/plugins/nowlistening/nowlisteningguiclient.h53
-rw-r--r--kopete/plugins/nowlistening/nowlisteningplugin.cpp554
-rw-r--r--kopete/plugins/nowlistening/nowlisteningplugin.h111
-rw-r--r--kopete/plugins/nowlistening/nowlisteningpreferences.cpp95
-rw-r--r--kopete/plugins/nowlistening/nowlisteningpreferences.h59
-rw-r--r--kopete/plugins/nowlistening/nowlisteningprefs.ui376
-rw-r--r--kopete/plugins/nowlistening/nowlisteningui.rc6
-rw-r--r--kopete/plugins/smpppdcs/Changelog.smpppdcs67
-rw-r--r--kopete/plugins/smpppdcs/Makefile.am35
-rw-r--r--kopete/plugins/smpppdcs/detector.h59
-rw-r--r--kopete/plugins/smpppdcs/detectordcop.cpp77
-rw-r--r--kopete/plugins/smpppdcs/detectordcop.h51
-rw-r--r--kopete/plugins/smpppdcs/detectornetstat.cpp76
-rw-r--r--kopete/plugins/smpppdcs/detectornetstat.h56
-rw-r--r--kopete/plugins/smpppdcs/detectornetworkstatus.cpp68
-rw-r--r--kopete/plugins/smpppdcs/detectornetworkstatus.h50
-rw-r--r--kopete/plugins/smpppdcs/detectorsmpppd.cpp71
-rw-r--r--kopete/plugins/smpppdcs/detectorsmpppd.h46
-rw-r--r--kopete/plugins/smpppdcs/iconnector.h45
-rw-r--r--kopete/plugins/smpppdcs/icons/Makefile.am2
-rw-r--r--kopete/plugins/smpppdcs/icons/cr32-app-smpppdcs.pngbin0 -> 426 bytes
-rw-r--r--kopete/plugins/smpppdcs/kinternetiface.h47
-rw-r--r--kopete/plugins/smpppdcs/kopete_smpppdcs.desktop108
-rw-r--r--kopete/plugins/smpppdcs/kopete_smpppdcs_config.desktop108
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am10
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp104
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h80
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp104
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h49
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp76
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h58
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp153
-rw-r--r--kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h49
-rw-r--r--kopete/plugins/smpppdcs/onlineinquiry.cpp45
-rw-r--r--kopete/plugins/smpppdcs/onlineinquiry.h45
-rw-r--r--kopete/plugins/smpppdcs/smpppdcs.kcfg29
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsconfig.kcfgc6
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsiface.h36
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsplugin.cpp221
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsplugin.h109
-rw-r--r--kopete/plugins/smpppdcs/smpppdcspreferences.cpp187
-rw-r--r--kopete/plugins/smpppdcs/smpppdcspreferences.h77
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsprefs.ui284
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsprefsimpl.cpp165
-rw-r--r--kopete/plugins/smpppdcs/smpppdcsprefsimpl.h76
-rw-r--r--kopete/plugins/smpppdcs/smpppdlocationui.ui149
-rw-r--r--kopete/plugins/smpppdcs/smpppdlocationwidget.cpp30
-rw-r--r--kopete/plugins/smpppdcs/smpppdlocationwidget.h39
-rw-r--r--kopete/plugins/smpppdcs/smpppdsearcher.cpp189
-rw-r--r--kopete/plugins/smpppdcs/smpppdsearcher.h102
-rw-r--r--kopete/plugins/smpppdcs/unittest/Makefile.am15
-rw-r--r--kopete/plugins/smpppdcs/unittest/clienttest.cpp121
-rw-r--r--kopete/plugins/smpppdcs/unittest/clienttest.h50
-rw-r--r--kopete/plugins/smpppdcs/unittest/main.cpp45
-rw-r--r--kopete/plugins/statistics/Makefile.am21
-rw-r--r--kopete/plugins/statistics/TODO3
-rw-r--r--kopete/plugins/statistics/images/black.pngbin0 -> 69 bytes
-rw-r--r--kopete/plugins/statistics/images/blue.pngbin0 -> 69 bytes
-rw-r--r--kopete/plugins/statistics/images/gray.pngbin0 -> 69 bytes
-rw-r--r--kopete/plugins/statistics/images/navy.pngbin0 -> 69 bytes
-rw-r--r--kopete/plugins/statistics/kopete_statistics.desktop111
-rw-r--r--kopete/plugins/statistics/kopetestatistics.kateproject7
-rw-r--r--kopete/plugins/statistics/sqlite/Makefile.am51
-rw-r--r--kopete/plugins/statistics/sqlite/attach.c329
-rw-r--r--kopete/plugins/statistics/sqlite/auth.c223
-rw-r--r--kopete/plugins/statistics/sqlite/btree.c4462
-rw-r--r--kopete/plugins/statistics/sqlite/btree.h124
-rw-r--r--kopete/plugins/statistics/sqlite/build.c2564
-rw-r--r--kopete/plugins/statistics/sqlite/date.c893
-rw-r--r--kopete/plugins/statistics/sqlite/delete.c419
-rw-r--r--kopete/plugins/statistics/sqlite/encode.c257
-rw-r--r--kopete/plugins/statistics/sqlite/expr.c1927
-rw-r--r--kopete/plugins/statistics/sqlite/func.c1018
-rw-r--r--kopete/plugins/statistics/sqlite/hash.c380
-rw-r--r--kopete/plugins/statistics/sqlite/hash.h109
-rw-r--r--kopete/plugins/statistics/sqlite/insert.c1018
-rw-r--r--kopete/plugins/statistics/sqlite/legacy.c138
-rw-r--r--kopete/plugins/statistics/sqlite/lempar.c687
-rw-r--r--kopete/plugins/statistics/sqlite/main.c1346
-rw-r--r--kopete/plugins/statistics/sqlite/opcodes.c128
-rw-r--r--kopete/plugins/statistics/sqlite/opcodes.h126
-rw-r--r--kopete/plugins/statistics/sqlite/os.h197
-rw-r--r--kopete/plugins/statistics/sqlite/os_common.h107
-rw-r--r--kopete/plugins/statistics/sqlite/os_mac.c738
-rw-r--r--kopete/plugins/statistics/sqlite/os_mac.h41
-rw-r--r--kopete/plugins/statistics/sqlite/os_unix.c1276
-rw-r--r--kopete/plugins/statistics/sqlite/os_unix.h89
-rw-r--r--kopete/plugins/statistics/sqlite/os_win.c747
-rw-r--r--kopete/plugins/statistics/sqlite/os_win.h40
-rw-r--r--kopete/plugins/statistics/sqlite/pager.c3205
-rw-r--r--kopete/plugins/statistics/sqlite/pager.h102
-rw-r--r--kopete/plugins/statistics/sqlite/parse.c3143
-rw-r--r--kopete/plugins/statistics/sqlite/parse.h129
-rw-r--r--kopete/plugins/statistics/sqlite/pragma.c754
-rw-r--r--kopete/plugins/statistics/sqlite/printf.c825
-rw-r--r--kopete/plugins/statistics/sqlite/random.c100
-rw-r--r--kopete/plugins/statistics/sqlite/select.c2628
-rw-r--r--kopete/plugins/statistics/sqlite/shell.c1786
-rw-r--r--kopete/plugins/statistics/sqlite/sqlite3.h1166
-rw-r--r--kopete/plugins/statistics/sqlite/sqliteInt.h1419
-rw-r--r--kopete/plugins/statistics/sqlite/table.c195
-rw-r--r--kopete/plugins/statistics/sqlite/tokenize.c707
-rw-r--r--kopete/plugins/statistics/sqlite/trigger.c804
-rw-r--r--kopete/plugins/statistics/sqlite/update.c450
-rw-r--r--kopete/plugins/statistics/sqlite/utf.c566
-rw-r--r--kopete/plugins/statistics/sqlite/util.c962
-rw-r--r--kopete/plugins/statistics/sqlite/vacuum.c262
-rw-r--r--kopete/plugins/statistics/sqlite/vdbe.c4450
-rw-r--r--kopete/plugins/statistics/sqlite/vdbe.h131
-rw-r--r--kopete/plugins/statistics/sqlite/vdbeInt.h408
-rw-r--r--kopete/plugins/statistics/sqlite/vdbeapi.c588
-rw-r--r--kopete/plugins/statistics/sqlite/vdbeaux.c1806
-rw-r--r--kopete/plugins/statistics/sqlite/vdbemem.c724
-rw-r--r--kopete/plugins/statistics/sqlite/where.c1210
-rw-r--r--kopete/plugins/statistics/statisticscontact.cpp515
-rw-r--r--kopete/plugins/statistics/statisticscontact.h260
-rw-r--r--kopete/plugins/statistics/statisticsdb.cpp208
-rw-r--r--kopete/plugins/statistics/statisticsdb.h36
-rw-r--r--kopete/plugins/statistics/statisticsdcopiface.h74
-rw-r--r--kopete/plugins/statistics/statisticsdialog.cpp543
-rw-r--r--kopete/plugins/statistics/statisticsdialog.h81
-rw-r--r--kopete/plugins/statistics/statisticsplugin.cpp283
-rw-r--r--kopete/plugins/statistics/statisticsplugin.h213
-rw-r--r--kopete/plugins/statistics/statisticsui.rc12
-rw-r--r--kopete/plugins/statistics/statisticswidget.ui246
-rw-r--r--kopete/plugins/texteffect/Makefile.am20
-rw-r--r--kopete/plugins/texteffect/icons/Makefile.am3
-rw-r--r--kopete/plugins/texteffect/icons/cr32-app-texteffect.pngbin0 -> 1621 bytes
-rw-r--r--kopete/plugins/texteffect/kopete_texteffect.desktop131
-rw-r--r--kopete/plugins/texteffect/kopete_texteffect_config.desktop126
-rw-r--r--kopete/plugins/texteffect/texteffectconfig.cpp140
-rw-r--r--kopete/plugins/texteffect/texteffectconfig.h62
-rw-r--r--kopete/plugins/texteffect/texteffectplugin.cpp198
-rw-r--r--kopete/plugins/texteffect/texteffectplugin.h71
-rw-r--r--kopete/plugins/texteffect/texteffectpreferences.cpp232
-rw-r--r--kopete/plugins/texteffect/texteffectpreferences.h58
-rw-r--r--kopete/plugins/texteffect/texteffectprefs.ui231
-rw-r--r--kopete/plugins/translator/Makefile.am25
-rw-r--r--kopete/plugins/translator/kopete_translator.desktop128
-rw-r--r--kopete/plugins/translator/kopete_translator_config.desktop127
-rw-r--r--kopete/plugins/translator/translatorchatui.rc9
-rw-r--r--kopete/plugins/translator/translatordialog.cpp44
-rw-r--r--kopete/plugins/translator/translatordialog.h51
-rw-r--r--kopete/plugins/translator/translatorguiclient.cpp100
-rw-r--r--kopete/plugins/translator/translatorguiclient.h63
-rw-r--r--kopete/plugins/translator/translatorlanguages.cpp101
-rw-r--r--kopete/plugins/translator/translatorlanguages.h94
-rw-r--r--kopete/plugins/translator/translatorplugin.cpp402
-rw-r--r--kopete/plugins/translator/translatorplugin.h113
-rw-r--r--kopete/plugins/translator/translatorprefs.cpp52
-rw-r--r--kopete/plugins/translator/translatorprefsbase.ui191
-rw-r--r--kopete/plugins/translator/translatorui.rc12
-rw-r--r--kopete/plugins/webpresence/DESIGN12
-rw-r--r--kopete/plugins/webpresence/Makefile.am27
-rw-r--r--kopete/plugins/webpresence/TODO5
-rw-r--r--kopete/plugins/webpresence/kopete_webpresence.desktop120
-rw-r--r--kopete/plugins/webpresence/kopete_webpresence_config.desktop116
-rw-r--r--kopete/plugins/webpresence/webpresence_html.xsl140
-rw-r--r--kopete/plugins/webpresence/webpresence_html_images.xsl59
-rw-r--r--kopete/plugins/webpresence/webpresence_xhtml.xsl139
-rw-r--r--kopete/plugins/webpresence/webpresence_xhtml_images.xsl60
-rw-r--r--kopete/plugins/webpresence/webpresenceplugin.cpp473
-rw-r--r--kopete/plugins/webpresence/webpresenceplugin.h123
-rw-r--r--kopete/plugins/webpresence/webpresencepreferences.cpp64
-rw-r--r--kopete/plugins/webpresence/webpresencepreferences.h50
-rw-r--r--kopete/plugins/webpresence/webpresenceprefs.ui369
-rw-r--r--kopete/protocols/Makefile.am21
-rw-r--r--kopete/protocols/configure.in.bot11
-rw-r--r--kopete/protocols/configure.in.in243
-rw-r--r--kopete/protocols/gadu/Makefile.am38
-rw-r--r--kopete/protocols/gadu/README.gadu43
-rw-r--r--kopete/protocols/gadu/gaduaccount.cpp1261
-rw-r--r--kopete/protocols/gadu/gaduaccount.h173
-rw-r--r--kopete/protocols/gadu/gaduaddcontactpage.cpp134
-rw-r--r--kopete/protocols/gadu/gaduaddcontactpage.h61
-rw-r--r--kopete/protocols/gadu/gaduaway.cpp86
-rw-r--r--kopete/protocols/gadu/gaduaway.h49
-rw-r--r--kopete/protocols/gadu/gaducommands.cpp411
-rw-r--r--kopete/protocols/gadu/gaducommands.h150
-rw-r--r--kopete/protocols/gadu/gaducontact.cpp384
-rw-r--r--kopete/protocols/gadu/gaducontact.h119
-rw-r--r--kopete/protocols/gadu/gaducontactlist.cpp207
-rw-r--r--kopete/protocols/gadu/gaducontactlist.h67
-rw-r--r--kopete/protocols/gadu/gadudcc.cpp186
-rw-r--r--kopete/protocols/gadu/gadudcc.h66
-rw-r--r--kopete/protocols/gadu/gadudccserver.cpp196
-rw-r--r--kopete/protocols/gadu/gadudccserver.h66
-rw-r--r--kopete/protocols/gadu/gadudcctransaction.cpp454
-rw-r--r--kopete/protocols/gadu/gadudcctransaction.h87
-rw-r--r--kopete/protocols/gadu/gadueditaccount.cpp263
-rw-r--r--kopete/protocols/gadu/gadueditaccount.h63
-rw-r--r--kopete/protocols/gadu/gadueditcontact.cpp203
-rw-r--r--kopete/protocols/gadu/gadueditcontact.h60
-rw-r--r--kopete/protocols/gadu/gaduprotocol.cpp243
-rw-r--r--kopete/protocols/gadu/gaduprotocol.h108
-rw-r--r--kopete/protocols/gadu/gadupubdir.cpp344
-rw-r--r--kopete/protocols/gadu/gadupubdir.h80
-rw-r--r--kopete/protocols/gadu/gaduregisteraccount.cpp212
-rw-r--r--kopete/protocols/gadu/gaduregisteraccount.h62
-rw-r--r--kopete/protocols/gadu/gadurichtextformat.cpp291
-rw-r--r--kopete/protocols/gadu/gadurichtextformat.h53
-rw-r--r--kopete/protocols/gadu/gadusession.cpp811
-rw-r--r--kopete/protocols/gadu/gadusession.h178
-rw-r--r--kopete/protocols/gadu/icons/Makefile.am2
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_away.pngbin0 -> 895 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_busy.pngbin0 -> 559 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_busy_d.pngbin0 -> 735 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_con.mngbin0 -> 9292 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_connecting.pngbin0 -> 715 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.pngbin0 -> 325 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_ignored.pngbin0 -> 1018 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_invi.pngbin0 -> 487 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_invi_d.pngbin0 -> 623 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_offline.pngbin0 -> 819 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_offline_d.pngbin0 -> 854 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_online.pngbin0 -> 395 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-action-gg_online_d.pngbin0 -> 478 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr16-app-gadu_protocol.pngbin0 -> 944 bytes
-rw-r--r--kopete/protocols/gadu/icons/cr32-app-gadu_protocol.pngbin0 -> 2127 bytes
-rw-r--r--kopete/protocols/gadu/kopete_gadu.desktop78
-rw-r--r--kopete/protocols/gadu/libgadu/COPYING504
-rw-r--r--kopete/protocols/gadu/libgadu/Makefile.am12
-rw-r--r--kopete/protocols/gadu/libgadu/common.c822
-rw-r--r--kopete/protocols/gadu/libgadu/compat.h29
-rw-r--r--kopete/protocols/gadu/libgadu/dcc.c1298
-rw-r--r--kopete/protocols/gadu/libgadu/events.c1580
-rw-r--r--kopete/protocols/gadu/libgadu/http.c522
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu-config.h.in30
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu.c1818
-rw-r--r--kopete/protocols/gadu/libgadu/libgadu.h1310
-rw-r--r--kopete/protocols/gadu/libgadu/pubdir.c689
-rw-r--r--kopete/protocols/gadu/libgadu/pubdir50.c467
-rw-r--r--kopete/protocols/gadu/ui/Makefile.am12
-rw-r--r--kopete/protocols/gadu/ui/empty.cpp0
-rw-r--r--kopete/protocols/gadu/ui/gaduadd.ui360
-rw-r--r--kopete/protocols/gadu/ui/gaduawayui.ui194
-rw-r--r--kopete/protocols/gadu/ui/gadueditaccountui.ui873
-rw-r--r--kopete/protocols/gadu/ui/gaduregisteraccountui.ui423
-rw-r--r--kopete/protocols/gadu/ui/gadusearch.ui692
-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
-rw-r--r--kopete/protocols/irc/Makefile.am40
-rw-r--r--kopete/protocols/irc/icons/Makefile.am2
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_away.pngbin0 -> 703 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_channel.pngbin0 -> 937 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_connecting.mngbin0 -> 3986 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_normal.pngbin0 -> 996 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_online.pngbin0 -> 812 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_op.pngbin0 -> 651 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_server.pngbin0 -> 996 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-action-irc_voice.pngbin0 -> 919 bytes
-rw-r--r--kopete/protocols/irc/icons/cr16-app-irc_protocol.pngbin0 -> 812 bytes
-rw-r--r--kopete/protocols/irc/icons/cr32-app-irc_protocol.pngbin0 -> 1778 bytes
-rw-r--r--kopete/protocols/irc/irc.protocol12
-rw-r--r--kopete/protocols/irc/ircaccount.cpp904
-rw-r--r--kopete/protocols/irc/ircaccount.h248
-rw-r--r--kopete/protocols/irc/ircaddcontactpage.cpp83
-rw-r--r--kopete/protocols/irc/ircaddcontactpage.h61
-rw-r--r--kopete/protocols/irc/ircchannelcontact.cpp749
-rw-r--r--kopete/protocols/irc/ircchannelcontact.h156
-rw-r--r--kopete/protocols/irc/ircchatui.rc10
-rw-r--r--kopete/protocols/irc/irccontact.cpp425
-rw-r--r--kopete/protocols/irc/irccontact.h153
-rw-r--r--kopete/protocols/irc/irccontactmanager.cpp297
-rw-r--r--kopete/protocols/irc/irccontactmanager.h117
-rw-r--r--kopete/protocols/irc/ircguiclient.cpp100
-rw-r--r--kopete/protocols/irc/ircguiclient.h42
-rw-r--r--kopete/protocols/irc/ircnetworks.xml1463
-rw-r--r--kopete/protocols/irc/ircprotocol.cpp1241
-rw-r--r--kopete/protocols/irc/ircprotocol.h228
-rw-r--r--kopete/protocols/irc/ircservercontact.cpp220
-rw-r--r--kopete/protocols/irc/ircservercontact.h80
-rw-r--r--kopete/protocols/irc/ircsignalhandler.cpp173
-rw-r--r--kopete/protocols/irc/ircsignalhandler.h334
-rw-r--r--kopete/protocols/irc/irctransferhandler.cpp183
-rw-r--r--kopete/protocols/irc/irctransferhandler.h65
-rw-r--r--kopete/protocols/irc/ircusercontact.cpp734
-rw-r--r--kopete/protocols/irc/ircusercontact.h146
-rw-r--r--kopete/protocols/irc/kcodecaction.cpp87
-rw-r--r--kopete/protocols/irc/kcodecaction.h47
-rw-r--r--kopete/protocols/irc/kopete_irc.desktop79
-rw-r--r--kopete/protocols/irc/ksparser.cpp265
-rw-r--r--kopete/protocols/irc/ksparser.h56
-rw-r--r--kopete/protocols/irc/libkirc/Makefile.am20
-rw-r--r--kopete/protocols/irc/libkirc/kircengine.cpp497
-rw-r--r--kopete/protocols/irc/libkirc/kircengine.h532
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_commands.cpp312
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_ctcp.cpp351
-rw-r--r--kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp570
-rw-r--r--kopete/protocols/irc/libkirc/kircentity.cpp132
-rw-r--r--kopete/protocols/irc/libkirc/kircentity.h128
-rw-r--r--kopete/protocols/irc/libkirc/kircmessage.cpp370
-rw-r--r--kopete/protocols/irc/libkirc/kircmessage.h198
-rw-r--r--kopete/protocols/irc/libkirc/kircmessageredirector.cpp97
-rw-r--r--kopete/protocols/irc/libkirc/kircmessageredirector.h86
-rw-r--r--kopete/protocols/irc/libkirc/kirctransfer.cpp365
-rw-r--r--kopete/protocols/irc/libkirc/kirctransfer.h191
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferhandler.cpp97
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferhandler.h79
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferserver.cpp154
-rw-r--r--kopete/protocols/irc/libkirc/kirctransferserver.h81
-rw-r--r--kopete/protocols/irc/libkirc/ksslsocket.cpp458
-rw-r--r--kopete/protocols/irc/libkirc/ksslsocket.h68
-rw-r--r--kopete/protocols/irc/ui/Makefile.am12
-rw-r--r--kopete/protocols/irc/ui/channellist.cpp346
-rw-r--r--kopete/protocols/irc/ui/channellist.h80
-rw-r--r--kopete/protocols/irc/ui/channellistdialog.cpp61
-rw-r--r--kopete/protocols/irc/ui/channellistdialog.h45
-rw-r--r--kopete/protocols/irc/ui/empty.cpp1
-rw-r--r--kopete/protocols/irc/ui/ircadd.ui163
-rw-r--r--kopete/protocols/irc/ui/irceditaccount.ui1022
-rw-r--r--kopete/protocols/irc/ui/irceditaccountwidget.cpp282
-rw-r--r--kopete/protocols/irc/ui/irceditaccountwidget.h60
-rw-r--r--kopete/protocols/irc/ui/networkconfig.ui382
-rw-r--r--kopete/protocols/irc/ui/networkconfig.ui.h26
-rw-r--r--kopete/protocols/jabber/Makefile.am67
-rw-r--r--kopete/protocols/jabber/TODO27
-rw-r--r--kopete/protocols/jabber/icons/Makefile.am1
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_away.pngbin0 -> 360 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_chatty.pngbin0 -> 917 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_connecting.mngbin0 -> 5825 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_group.pngbin0 -> 969 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_invisible.pngbin0 -> 1035 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_na.pngbin0 -> 387 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_offline.pngbin0 -> 828 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_online.pngbin0 -> 877 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_original.pngbin0 -> 20688 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_raw.pngbin0 -> 657 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_serv_off.pngbin0 -> 475 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_serv_on.pngbin0 -> 911 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-action-jabber_xa.pngbin0 -> 418 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_aim.pngbin0 -> 1021 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_gadu.pngbin0 -> 974 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_http-ws.pngbin0 -> 1022 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_icq.pngbin0 -> 904 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_irc.pngbin0 -> 963 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_msn.pngbin0 -> 982 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_qq.pngbin0 -> 1014 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_sms.pngbin0 -> 980 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_smtp.pngbin0 -> 882 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_tlen.pngbin0 -> 749 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_gateway_yahoo.pngbin0 -> 922 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr16-app-jabber_protocol.pngbin0 -> 877 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr32-app-jabber_protocol.pngbin0 -> 2221 bytes
-rw-r--r--kopete/protocols/jabber/icons/cr48-app-jabber_protocol.pngbin0 -> 3899 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_away.pngbin0 -> 339 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_chatty.pngbin0 -> 534 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_connecting.mngbin0 -> 2865 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_group.pngbin0 -> 548 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_invisible.pngbin0 -> 440 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_na.pngbin0 -> 392 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_offline.pngbin0 -> 353 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_online.pngbin0 -> 379 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_original.pngbin0 -> 72510 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_raw.pngbin0 -> 657 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_serv_off.pngbin0 -> 350 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_serv_on.pngbin0 -> 733 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-action-jabber_xa.pngbin0 -> 415 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi16-app-jabber_protocol.pngbin0 -> 379 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi32-app-jabber_protocol.pngbin0 -> 2500 bytes
-rw-r--r--kopete/protocols/jabber/icons/hi48-app-jabber_protocol.pngbin0 -> 4882 bytes
-rw-r--r--kopete/protocols/jabber/jabberaccount.cpp1752
-rw-r--r--kopete/protocols/jabber/jabberaccount.h309
-rw-r--r--kopete/protocols/jabber/jabberbasecontact.cpp676
-rw-r--r--kopete/protocols/jabber/jabberbasecontact.h185
-rw-r--r--kopete/protocols/jabber/jabberbookmarks.cpp149
-rw-r--r--kopete/protocols/jabber/jabberbookmarks.h68
-rw-r--r--kopete/protocols/jabber/jabberbytestream.cpp156
-rw-r--r--kopete/protocols/jabber/jabberbytestream.h65
-rw-r--r--kopete/protocols/jabber/jabbercapabilitiesmanager.cpp656
-rw-r--r--kopete/protocols/jabber/jabbercapabilitiesmanager.h211
-rw-r--r--kopete/protocols/jabber/jabberchatsession.cpp357
-rw-r--r--kopete/protocols/jabber/jabberchatsession.h103
-rw-r--r--kopete/protocols/jabber/jabberchatui.rc19
-rw-r--r--kopete/protocols/jabber/jabberclient.cpp1137
-rw-r--r--kopete/protocols/jabber/jabberclient.h600
-rw-r--r--kopete/protocols/jabber/jabberconnector.cpp132
-rw-r--r--kopete/protocols/jabber/jabberconnector.h65
-rw-r--r--kopete/protocols/jabber/jabbercontact.cpp1328
-rw-r--r--kopete/protocols/jabber/jabbercontact.h266
-rw-r--r--kopete/protocols/jabber/jabbercontactpool.cpp355
-rw-r--r--kopete/protocols/jabber/jabbercontactpool.h124
-rw-r--r--kopete/protocols/jabber/jabberfiletransfer.cpp326
-rw-r--r--kopete/protocols/jabber/jabberfiletransfer.h74
-rw-r--r--kopete/protocols/jabber/jabberformlineedit.cpp58
-rw-r--r--kopete/protocols/jabber/jabberformlineedit.h59
-rw-r--r--kopete/protocols/jabber/jabberformtranslator.cpp91
-rw-r--r--kopete/protocols/jabber/jabberformtranslator.h49
-rw-r--r--kopete/protocols/jabber/jabbergroupchatmanager.cpp163
-rw-r--r--kopete/protocols/jabber/jabbergroupchatmanager.h78
-rw-r--r--kopete/protocols/jabber/jabbergroupcontact.cpp378
-rw-r--r--kopete/protocols/jabber/jabbergroupcontact.h107
-rw-r--r--kopete/protocols/jabber/jabbergroupmembercontact.cpp168
-rw-r--r--kopete/protocols/jabber/jabbergroupmembercontact.h80
-rw-r--r--kopete/protocols/jabber/jabberprotocol.cpp345
-rw-r--r--kopete/protocols/jabber/jabberprotocol.h164
-rw-r--r--kopete/protocols/jabber/jabberresource.cpp171
-rw-r--r--kopete/protocols/jabber/jabberresource.h86
-rw-r--r--kopete/protocols/jabber/jabberresourcepool.cpp394
-rw-r--r--kopete/protocols/jabber/jabberresourcepool.h129
-rw-r--r--kopete/protocols/jabber/jabbertransport.cpp345
-rw-r--r--kopete/protocols/jabber/jabbertransport.h138
-rw-r--r--kopete/protocols/jabber/jingle/DESIGN121
-rw-r--r--kopete/protocols/jabber/jingle/Makefile.am28
-rw-r--r--kopete/protocols/jabber/jingle/configure.in.bot16
-rw-r--r--kopete/protocols/jabber/jingle/configure.in.in87
-rw-r--r--kopete/protocols/jabber/jingle/jinglesession.cpp72
-rw-r--r--kopete/protocols/jabber/jingle/jinglesession.h94
-rw-r--r--kopete/protocols/jabber/jingle/jinglesessionmanager.cpp205
-rw-r--r--kopete/protocols/jabber/jingle/jinglesessionmanager.h89
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicecaller.cpp376
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicecaller.h72
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesession.cpp333
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesession.h70
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesessiondialog.cpp208
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesessiondialog.h66
-rw-r--r--kopete/protocols/jabber/jingle/jinglevoicesessiondialogbase.ui369
-rw-r--r--kopete/protocols/jabber/jingle/jinglewatchsessiontask.cpp75
-rw-r--r--kopete/protocols/jabber/jingle/jinglewatchsessiontask.h39
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/AUTHORS1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/COPYING25
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/ChangeLog4
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/INSTALL229
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/Makefile.am4
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/NEWS1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/README59
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/libjingle.pro142
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am62
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h56
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc83
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h91
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc197
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h68
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc83
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h59
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc194
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h29
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h53
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc165
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h71
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/common.h231
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h120
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc99
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/host.h59
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc77
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h47
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h138
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h222
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h45
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c256
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc321
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h164
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc382
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/network.h136
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc1117
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h52
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h259
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h2700
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h158
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc1130
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h181
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc267
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h154
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc58
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h58
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h50
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h53
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h266
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc238
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/task.h186
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc92
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h64
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc273
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h141
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h101
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am16
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro19
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc62
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc390
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h88
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc196
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h82
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc148
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h46
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc172
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h44
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h213
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am15
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc93
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h71
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc73
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h73
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc144
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h57
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am47
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h118
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc129
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h51
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc910
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h164
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc869
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h367
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h91
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc640
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h93
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc657
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h210
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro14
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc75
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc421
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h140
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h42
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h94
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc173
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h86
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h133
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc273
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h101
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc576
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h364
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc171
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h72
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc198
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h126
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc160
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h73
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro14
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc66
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc250
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h116
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc117
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h81
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am11
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc667
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h172
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc545
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h104
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc149
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am3
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am18
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc119
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h73
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc258
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h97
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc203
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h81
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc170
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h75
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h55
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h95
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc267
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h122
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc331
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h69
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc331
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h129
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h72
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h111
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am1
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am92
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms34
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README3
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h43
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c640
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h50
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c343
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h171
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c301
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c574
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h81
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h130
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c342
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h81
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c132
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h65
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c124
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h64
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h64
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h61
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h64
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h74
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c130
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h66
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c99
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h87
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h90
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c94
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h75
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c250
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h67
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c96
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h61
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c94
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h61
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c168
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h73
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c537
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h201
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c194
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h72
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c244
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h84
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c82
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h60
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c148
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h77
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c247
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h78
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c91
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h60
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c56
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h49
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c182
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c246
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h81
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c163
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c211
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h64
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c39
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c39
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c218
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h69
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c192
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h66
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c193
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h136
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c114
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h68
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h55
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h62
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h61
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h96
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h74
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c121
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h63
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c495
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h47
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c315
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h35
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c209
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h143
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h111
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am18
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc167
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h87
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc151
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h79
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc65
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h61
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc491
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h231
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc205
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h62
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc250
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h108
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc190
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h49
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am34
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc331
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h300
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc477
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h144
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h80
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h85
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h67
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h59
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc68
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h74
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h65
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc372
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h157
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h94
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h332
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc480
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h262
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc279
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc357
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h95
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h163
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc104
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h96
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc168
-rw-r--r--kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h113
-rw-r--r--kopete/protocols/jabber/jingle/voicecaller.h96
-rw-r--r--kopete/protocols/jabber/kioslave/Makefile.am25
-rw-r--r--kopete/protocols/jabber/kioslave/jabberdisco.cpp399
-rw-r--r--kopete/protocols/jabber/kioslave/jabberdisco.h82
-rw-r--r--kopete/protocols/jabber/kioslave/jabberdisco.protocol53
-rw-r--r--kopete/protocols/jabber/kopete_jabber.desktop79
-rw-r--r--kopete/protocols/jabber/libiris/001_last_activity.patch113
-rw-r--r--kopete/protocols/jabber/libiris/002_offline_event.patch17
-rw-r--r--kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch14
-rw-r--r--kopete/protocols/jabber/libiris/004_xhtml_im.patch266
-rw-r--r--kopete/protocols/jabber/libiris/005_join_muc_with_password.patch163
-rw-r--r--kopete/protocols/jabber/libiris/006_private_storage.patch130
-rw-r--r--kopete/protocols/jabber/libiris/007_chatstates.patch132
-rw-r--r--kopete/protocols/jabber/libiris/008_chatstatesfix.patch38
-rw-r--r--kopete/protocols/jabber/libiris/Makefile.am2
-rw-r--r--kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING21
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/README13
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/TODO25
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am16
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp394
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h87
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp369
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h67
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp666
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h104
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp378
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/ndns.h88
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp112
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/servsock.h68
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp1223
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/socks.h160
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp320
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h65
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am12
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/TODO7
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp182
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/base64.h40
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp268
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h78
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp357
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/cipher.h79
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp24
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h14
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp119
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h60
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp196
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/sha1.h63
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp61
-rw-r--r--kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h33
-rw-r--r--kopete/protocols/jabber/libiris/iris/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/iris/TODO16
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/Makefile.am7
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/empty.cpp0
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/im.h721
-rw-r--r--kopete/protocols/jabber/libiris/iris/include/xmpp.h553
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/Makefile.am15
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp23
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp770
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h170
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp2538
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/s5b.h341
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp638
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h145
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp318
-rw-r--r--kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h114
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am30
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp719
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp670
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h31
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp409
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp798
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h86
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp1595
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h355
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h191
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp589
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h84
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp459
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h31
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp1762
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/td.h20
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp138
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp543
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h145
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am19
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp1522
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp1876
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp2120
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h485
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp1241
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h284
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp386
-rw-r--r--kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h71
-rw-r--r--kopete/protocols/jabber/libiris/jingle_iris.patch432
-rw-r--r--kopete/protocols/jabber/libiris/qca/COPYING504
-rw-r--r--kopete/protocols/jabber/libiris/qca/INSTALL12
-rw-r--r--kopete/protocols/jabber/libiris/qca/Makefile.am1
-rw-r--r--kopete/protocols/jabber/libiris/qca/README29
-rw-r--r--kopete/protocols/jabber/libiris/qca/TODO6
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/Makefile.am7
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qca.cpp1481
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qca.h466
-rw-r--r--kopete/protocols/jabber/libiris/qca/src/qcaprovider.h191
-rw-r--r--kopete/protocols/jabber/ui/Makefile.am31
-rw-r--r--kopete/protocols/jabber/ui/dlgaddcontact.ui105
-rw-r--r--kopete/protocols/jabber/ui/dlgbrowse.ui201
-rw-r--r--kopete/protocols/jabber/ui/dlgchangepassword.ui88
-rw-r--r--kopete/protocols/jabber/ui/dlgchatjoin.ui130
-rw-r--r--kopete/protocols/jabber/ui/dlgchatroomslist.ui185
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberbrowse.cpp144
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberbrowse.h54
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchangepassword.cpp135
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchangepassword.h51
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchatjoin.cpp129
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchatjoin.h65
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchatroomslist.cpp117
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchatroomslist.h54
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberchooseserver.ui107
-rw-r--r--kopete/protocols/jabber/ui/dlgjabbereditaccountwidget.ui993
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberregister.cpp117
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberregister.h58
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberregisteraccount.ui319
-rw-r--r--kopete/protocols/jabber/ui/dlgjabbersendraw.cpp116
-rw-r--r--kopete/protocols/jabber/ui/dlgjabbersendraw.h85
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberservices.cpp237
-rw-r--r--kopete/protocols/jabber/ui/dlgjabberservices.h73
-rw-r--r--kopete/protocols/jabber/ui/dlgjabbervcard.cpp563
-rw-r--r--kopete/protocols/jabber/ui/dlgjabbervcard.h118
-rw-r--r--kopete/protocols/jabber/ui/dlgregister.ui162
-rw-r--r--kopete/protocols/jabber/ui/dlgsendraw.ui159
-rw-r--r--kopete/protocols/jabber/ui/dlgservices.ui199
-rw-r--r--kopete/protocols/jabber/ui/dlgvcard.ui1064
-rw-r--r--kopete/protocols/jabber/ui/empty.cpp0
-rw-r--r--kopete/protocols/jabber/ui/jabberaddcontactpage.cpp224
-rw-r--r--kopete/protocols/jabber/ui/jabberaddcontactpage.h76
-rw-r--r--kopete/protocols/jabber/ui/jabberchooseserver.cpp149
-rw-r--r--kopete/protocols/jabber/ui/jabberchooseserver.h65
-rw-r--r--kopete/protocols/jabber/ui/jabbereditaccountwidget.cpp286
-rw-r--r--kopete/protocols/jabber/ui/jabbereditaccountwidget.h62
-rw-r--r--kopete/protocols/jabber/ui/jabberregisteraccount.cpp389
-rw-r--r--kopete/protocols/jabber/ui/jabberregisteraccount.h75
-rw-r--r--kopete/protocols/meanwhile/Makefile.am37
-rw-r--r--kopete/protocols/meanwhile/README53
-rw-r--r--kopete/protocols/meanwhile/icons/Makefile.am2
-rw-r--r--kopete/protocols/meanwhile/icons/cr128-app-meanwhile_protocol.pngbin0 -> 6751 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr16-action-meanwhile_away.pngbin0 -> 959 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr16-action-meanwhile_dnd.pngbin0 -> 920 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr16-action-meanwhile_idle.pngbin0 -> 395 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr16-action-meanwhile_unknown.pngbin0 -> 412 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr16-app-meanwhile_protocol.pngbin0 -> 800 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr22-app-meanwhile_protocol.pngbin0 -> 1138 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr32-app-meanwhile_protocol.pngbin0 -> 1573 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr48-app-meanwhile_protocol.pngbin0 -> 2493 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/cr64-app-meanwhile_protocol.pngbin0 -> 3351 bytes
-rw-r--r--kopete/protocols/meanwhile/icons/crsc-app-meanwhile_protocol.svgzbin0 -> 1131 bytes
-rw-r--r--kopete/protocols/meanwhile/kopete_meanwhile.desktop58
-rw-r--r--kopete/protocols/meanwhile/meanwhileaccount.cpp274
-rw-r--r--kopete/protocols/meanwhile/meanwhileaccount.h121
-rw-r--r--kopete/protocols/meanwhile/meanwhileaddcontactpage.cpp74
-rw-r--r--kopete/protocols/meanwhile/meanwhileaddcontactpage.h45
-rw-r--r--kopete/protocols/meanwhile/meanwhilecontact.cpp132
-rw-r--r--kopete/protocols/meanwhile/meanwhilecontact.h68
-rw-r--r--kopete/protocols/meanwhile/meanwhileeditaccountwidget.cpp194
-rw-r--r--kopete/protocols/meanwhile/meanwhileeditaccountwidget.h51
-rw-r--r--kopete/protocols/meanwhile/meanwhileplugin.cpp37
-rw-r--r--kopete/protocols/meanwhile/meanwhileplugin.h47
-rw-r--r--kopete/protocols/meanwhile/meanwhileprotocol.cpp126
-rw-r--r--kopete/protocols/meanwhile/meanwhileprotocol.h77
-rw-r--r--kopete/protocols/meanwhile/meanwhilesession.cpp965
-rw-r--r--kopete/protocols/meanwhile/meanwhilesession.h350
-rw-r--r--kopete/protocols/meanwhile/ui/Makefile.am8
-rw-r--r--kopete/protocols/meanwhile/ui/empty.cpp0
-rw-r--r--kopete/protocols/meanwhile/ui/meanwhileaddcontactbase.ui111
-rw-r--r--kopete/protocols/meanwhile/ui/meanwhileeditaccountbase.ui437
-rw-r--r--kopete/protocols/msn/Changelog106
-rw-r--r--kopete/protocols/msn/Makefile.am46
-rw-r--r--kopete/protocols/msn/ReleaseNotes31
-rw-r--r--kopete/protocols/msn/TODO5
-rw-r--r--kopete/protocols/msn/config/Makefile.am12
-rw-r--r--kopete/protocols/msn/config/kopete_msn_config.desktop123
-rw-r--r--kopete/protocols/msn/config/msnpreferences.cpp33
-rw-r--r--kopete/protocols/msn/config/msnprefs.ui217
-rw-r--r--kopete/protocols/msn/dispatcher.cpp647
-rw-r--r--kopete/protocols/msn/dispatcher.h107
-rw-r--r--kopete/protocols/msn/icons/Makefile.am2
-rw-r--r--kopete/protocols/msn/icons/cr128-app-msn_protocol.pngbin0 -> 16158 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_away.pngbin0 -> 661 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_blocked.pngbin0 -> 292 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_brb.pngbin0 -> 449 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_busy.pngbin0 -> 588 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_connecting.mngbin0 -> 4503 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_invisible.pngbin0 -> 687 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_lunch.pngbin0 -> 497 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_na.pngbin0 -> 387 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_newmsg.pngbin0 -> 688 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_offline.pngbin0 -> 819 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_online.pngbin0 -> 943 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-action-msn_phone.pngbin0 -> 523 bytes
-rw-r--r--kopete/protocols/msn/icons/cr16-app-msn_protocol.pngbin0 -> 943 bytes
-rw-r--r--kopete/protocols/msn/icons/cr32-app-msn_protocol.pngbin0 -> 2399 bytes
-rw-r--r--kopete/protocols/msn/icons/cr48-app-msn_protocol.pngbin0 -> 4758 bytes
-rw-r--r--kopete/protocols/msn/icons/cr64-app-msn_protocol.pngbin0 -> 7152 bytes
-rw-r--r--kopete/protocols/msn/incomingtransfer.cpp381
-rw-r--r--kopete/protocols/msn/incomingtransfer.h57
-rw-r--r--kopete/protocols/msn/kopete_msn.desktop99
-rw-r--r--kopete/protocols/msn/messageformatter.cpp192
-rw-r--r--kopete/protocols/msn/messageformatter.h40
-rw-r--r--kopete/protocols/msn/msnaccount.cpp1499
-rw-r--r--kopete/protocols/msn/msnaccount.h270
-rw-r--r--kopete/protocols/msn/msnaddcontactpage.cpp85
-rw-r--r--kopete/protocols/msn/msnaddcontactpage.h33
-rw-r--r--kopete/protocols/msn/msnchallengehandler.cpp151
-rw-r--r--kopete/protocols/msn/msnchallengehandler.h64
-rw-r--r--kopete/protocols/msn/msnchatsession.cpp775
-rw-r--r--kopete/protocols/msn/msnchatsession.h140
-rw-r--r--kopete/protocols/msn/msnchatui.rc27
-rw-r--r--kopete/protocols/msn/msncontact.cpp713
-rw-r--r--kopete/protocols/msn/msncontact.h199
-rw-r--r--kopete/protocols/msn/msndebugrawcmddlg.cpp73
-rw-r--r--kopete/protocols/msn/msndebugrawcmddlg.h53
-rw-r--r--kopete/protocols/msn/msnfiletransfersocket.cpp481
-rw-r--r--kopete/protocols/msn/msnfiletransfersocket.h119
-rw-r--r--kopete/protocols/msn/msninvitation.cpp100
-rw-r--r--kopete/protocols/msn/msninvitation.h126
-rw-r--r--kopete/protocols/msn/msnnotifysocket.cpp1309
-rw-r--r--kopete/protocols/msn/msnnotifysocket.h216
-rw-r--r--kopete/protocols/msn/msnprotocol.cpp179
-rw-r--r--kopete/protocols/msn/msnprotocol.h187
-rw-r--r--kopete/protocols/msn/msnsecureloginhandler.cpp131
-rw-r--r--kopete/protocols/msn/msnsecureloginhandler.h76
-rw-r--r--kopete/protocols/msn/msnsocket.cpp1099
-rw-r--r--kopete/protocols/msn/msnsocket.h362
-rw-r--r--kopete/protocols/msn/msnswitchboardsocket.cpp1142
-rw-r--r--kopete/protocols/msn/msnswitchboardsocket.h166
-rw-r--r--kopete/protocols/msn/outgoingtransfer.cpp432
-rw-r--r--kopete/protocols/msn/outgoingtransfer.h59
-rw-r--r--kopete/protocols/msn/p2p.cpp412
-rw-r--r--kopete/protocols/msn/p2p.h147
-rw-r--r--kopete/protocols/msn/sha1.cpp192
-rw-r--r--kopete/protocols/msn/sha1.h59
-rw-r--r--kopete/protocols/msn/transport.cpp356
-rw-r--r--kopete/protocols/msn/transport.h167
-rw-r--r--kopete/protocols/msn/ui/Makefile.am12
-rw-r--r--kopete/protocols/msn/ui/msnadd.ui97
-rw-r--r--kopete/protocols/msn/ui/msndebugrawcommand_base.ui107
-rw-r--r--kopete/protocols/msn/ui/msneditaccountui.ui1421
-rw-r--r--kopete/protocols/msn/ui/msneditaccountwidget.cpp369
-rw-r--r--kopete/protocols/msn/ui/msneditaccountwidget.h59
-rw-r--r--kopete/protocols/msn/ui/msninfo.ui221
-rw-r--r--kopete/protocols/msn/webcam.cpp891
-rw-r--r--kopete/protocols/msn/webcam.h91
-rw-r--r--kopete/protocols/msn/webcam/Makefile.am14
-rw-r--r--kopete/protocols/msn/webcam/libmimic/AUTHORS2
-rw-r--r--kopete/protocols/msn/webcam/libmimic/COPYING504
-rw-r--r--kopete/protocols/msn/webcam/libmimic/Makefile.am24
-rw-r--r--kopete/protocols/msn/webcam/libmimic/README40
-rw-r--r--kopete/protocols/msn/webcam/libmimic/bitstring.c88
-rw-r--r--kopete/protocols/msn/webcam/libmimic/colorspace.c161
-rw-r--r--kopete/protocols/msn/webcam/libmimic/deblock.c450
-rw-r--r--kopete/protocols/msn/webcam/libmimic/decode.c311
-rw-r--r--kopete/protocols/msn/webcam/libmimic/encode.c419
-rw-r--r--kopete/protocols/msn/webcam/libmimic/fdct_quant.c181
-rw-r--r--kopete/protocols/msn/webcam/libmimic/idct_dequant.c134
-rw-r--r--kopete/protocols/msn/webcam/libmimic/mimic-private.h117
-rw-r--r--kopete/protocols/msn/webcam/libmimic/mimic.c334
-rw-r--r--kopete/protocols/msn/webcam/libmimic/mimic.h73
-rw-r--r--kopete/protocols/msn/webcam/libmimic/query.c1
-rw-r--r--kopete/protocols/msn/webcam/libmimic/vlc_common.c1364
-rw-r--r--kopete/protocols/msn/webcam/libmimic/vlc_decode.c119
-rw-r--r--kopete/protocols/msn/webcam/libmimic/vlc_encode.c84
-rw-r--r--kopete/protocols/msn/webcam/mimicwrapper.cpp105
-rw-r--r--kopete/protocols/msn/webcam/mimicwrapper.h40
-rw-r--r--kopete/protocols/msn/webcam/msnwebcamdialog.cpp82
-rw-r--r--kopete/protocols/msn/webcam/msnwebcamdialog.h55
-rw-r--r--kopete/protocols/oscar/Makefile.am15
-rw-r--r--kopete/protocols/oscar/TODO53
-rw-r--r--kopete/protocols/oscar/aim/Makefile.am18
-rw-r--r--kopete/protocols/oscar/aim/aim.protocol13
-rw-r--r--kopete/protocols/oscar/aim/aimaccount.cpp924
-rw-r--r--kopete/protocols/oscar/aim/aimaccount.h146
-rw-r--r--kopete/protocols/oscar/aim/aimchatsession.cpp73
-rw-r--r--kopete/protocols/oscar/aim/aimchatsession.h77
-rw-r--r--kopete/protocols/oscar/aim/aimcontact.cpp517
-rw-r--r--kopete/protocols/oscar/aim/aimcontact.h102
-rw-r--r--kopete/protocols/oscar/aim/aimjoinchat.cpp94
-rw-r--r--kopete/protocols/oscar/aim/aimjoinchat.h62
-rw-r--r--kopete/protocols/oscar/aim/aimprotocol.cpp320
-rw-r--r--kopete/protocols/oscar/aim/aimprotocol.h85
-rw-r--r--kopete/protocols/oscar/aim/aimuserinfo.cpp224
-rw-r--r--kopete/protocols/oscar/aim/aimuserinfo.h59
-rw-r--r--kopete/protocols/oscar/aim/kopete_aim.desktop77
-rw-r--r--kopete/protocols/oscar/aim/ui/Makefile.am15
-rw-r--r--kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp83
-rw-r--r--kopete/protocols/oscar/aim/ui/aimaddcontactpage.h41
-rw-r--r--kopete/protocols/oscar/aim/ui/aimaddcontactui.ui64
-rw-r--r--kopete/protocols/oscar/aim/ui/aimeditaccountui.ui540
-rw-r--r--kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp172
-rw-r--r--kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h58
-rw-r--r--kopete/protocols/oscar/aim/ui/aiminfobase.ui246
-rw-r--r--kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui124
-rw-r--r--kopete/protocols/oscar/icons/Makefile.am2
-rw-r--r--kopete/protocols/oscar/icons/cr128-app-aim_protocol.pngbin0 -> 6380 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr128-app-icq_protocol.pngbin0 -> 13807 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-aim_away.pngbin0 -> 360 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-aim_connecting.mngbin0 -> 3121 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-aim_offline.pngbin0 -> 662 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-aim_online.pngbin0 -> 736 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_away.pngbin0 -> 360 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_connecting.mngbin0 -> 10423 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_dnd.pngbin0 -> 666 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_ffc.pngbin0 -> 884 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_invisible.pngbin0 -> 1009 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_na.pngbin0 -> 387 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_occupied.pngbin0 -> 761 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_offline.pngbin0 -> 643 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-action-icq_online.pngbin0 -> 1005 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-app-aim_protocol.pngbin0 -> 708 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr16-app-icq_protocol.pngbin0 -> 1103 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr32-app-aim_protocol.pngbin0 -> 1863 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr32-app-icq_protocol.pngbin0 -> 2890 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr48-app-aim_protocol.pngbin0 -> 3045 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr48-app-icq_protocol.pngbin0 -> 5307 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr64-app-aim_protocol.pngbin0 -> 4258 bytes
-rw-r--r--kopete/protocols/oscar/icons/cr64-app-icq_protocol.pngbin0 -> 7622 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-aim_away.pngbin0 -> 305 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-aim_connecting.mngbin0 -> 2998 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-aim_offline.pngbin0 -> 567 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-aim_online.pngbin0 -> 553 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_away.pngbin0 -> 318 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_connecting.mngbin0 -> 5475 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_dnd.pngbin0 -> 429 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_ffc.pngbin0 -> 702 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_invisible.pngbin0 -> 706 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_na.pngbin0 -> 529 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_occupied.pngbin0 -> 507 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_offline.pngbin0 -> 271 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-action-icq_online.pngbin0 -> 864 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-app-aim_protocol.pngbin0 -> 553 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi16-app-icq_protocol.pngbin0 -> 755 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi32-app-aim_protocol.pngbin0 -> 849 bytes
-rw-r--r--kopete/protocols/oscar/icons/hi32-app-icq_protocol.pngbin0 -> 923 bytes
-rw-r--r--kopete/protocols/oscar/icq/Makefile.am22
-rw-r--r--kopete/protocols/oscar/icq/icqaccount.cpp529
-rw-r--r--kopete/protocols/oscar/icq/icqaccount.h105
-rw-r--r--kopete/protocols/oscar/icq/icqcontact.cpp939
-rw-r--r--kopete/protocols/oscar/icq/icqcontact.h155
-rw-r--r--kopete/protocols/oscar/icq/icqpresence.cpp294
-rw-r--r--kopete/protocols/oscar/icq/icqpresence.h177
-rw-r--r--kopete/protocols/oscar/icq/icqprotocol.cpp820
-rw-r--r--kopete/protocols/oscar/icq/icqprotocol.h106
-rw-r--r--kopete/protocols/oscar/icq/icqreadaway.cpp106
-rw-r--r--kopete/protocols/oscar/icq/icqreadaway.h52
-rw-r--r--kopete/protocols/oscar/icq/kopete_icq.desktop78
-rw-r--r--kopete/protocols/oscar/icq/ui/Makefile.am17
-rw-r--r--kopete/protocols/oscar/icq/ui/icqadd.ui122
-rw-r--r--kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp126
-rw-r--r--kopete/protocols/oscar/icq/ui/icqaddcontactpage.h60
-rw-r--r--kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp73
-rw-r--r--kopete/protocols/oscar/icq/ui/icqauthreplydialog.h45
-rw-r--r--kopete/protocols/oscar/icq/ui/icqauthreplyui.ui196
-rw-r--r--kopete/protocols/oscar/icq/ui/icqeditaccountui.ui486
-rw-r--r--kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp190
-rw-r--r--kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h52
-rw-r--r--kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui611
-rw-r--r--kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui116
-rw-r--r--kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui68
-rw-r--r--kopete/protocols/oscar/icq/ui/icqsearchbase.ui493
-rw-r--r--kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp320
-rw-r--r--kopete/protocols/oscar/icq/ui/icqsearchdialog.h69
-rw-r--r--kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp190
-rw-r--r--kopete/protocols/oscar/icq/ui/icquserinfowidget.h58
-rw-r--r--kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui249
-rw-r--r--kopete/protocols/oscar/icq/x-icq.desktop60
-rw-r--r--kopete/protocols/oscar/liboscar/DESIGN12
-rw-r--r--kopete/protocols/oscar/liboscar/HACKING194
-rw-r--r--kopete/protocols/oscar/liboscar/Makefile.am26
-rw-r--r--kopete/protocols/oscar/liboscar/TODO37
-rw-r--r--kopete/protocols/oscar/liboscar/aimlogintask.cpp254
-rw-r--r--kopete/protocols/oscar/liboscar/aimlogintask.h82
-rw-r--r--kopete/protocols/oscar/liboscar/blmlimitstask.cpp94
-rw-r--r--kopete/protocols/oscar/liboscar/blmlimitstask.h43
-rw-r--r--kopete/protocols/oscar/liboscar/buddyicontask.cpp245
-rw-r--r--kopete/protocols/oscar/liboscar/buddyicontask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/buffer.cpp519
-rw-r--r--kopete/protocols/oscar/liboscar/buffer.h268
-rw-r--r--kopete/protocols/oscar/liboscar/bytestream.cpp270
-rw-r--r--kopete/protocols/oscar/liboscar/bytestream.h78
-rw-r--r--kopete/protocols/oscar/liboscar/changevisibilitytask.cpp150
-rw-r--r--kopete/protocols/oscar/liboscar/changevisibilitytask.h58
-rw-r--r--kopete/protocols/oscar/liboscar/chatnavservicetask.cpp355
-rw-r--r--kopete/protocols/oscar/liboscar/chatnavservicetask.h67
-rw-r--r--kopete/protocols/oscar/liboscar/chatservicetask.cpp359
-rw-r--r--kopete/protocols/oscar/liboscar/chatservicetask.h65
-rw-r--r--kopete/protocols/oscar/liboscar/client.cpp1353
-rw-r--r--kopete/protocols/oscar/liboscar/client.h521
-rw-r--r--kopete/protocols/oscar/liboscar/clientreadytask.cpp109
-rw-r--r--kopete/protocols/oscar/liboscar/clientreadytask.h46
-rw-r--r--kopete/protocols/oscar/liboscar/closeconnectiontask.cpp146
-rw-r--r--kopete/protocols/oscar/liboscar/closeconnectiontask.h62
-rw-r--r--kopete/protocols/oscar/liboscar/connection.cpp248
-rw-r--r--kopete/protocols/oscar/liboscar/connection.h209
-rw-r--r--kopete/protocols/oscar/liboscar/connectionhandler.cpp174
-rw-r--r--kopete/protocols/oscar/liboscar/connectionhandler.h118
-rw-r--r--kopete/protocols/oscar/liboscar/connector.cpp62
-rw-r--r--kopete/protocols/oscar/liboscar/connector.h59
-rw-r--r--kopete/protocols/oscar/liboscar/coreprotocol.cpp285
-rw-r--r--kopete/protocols/oscar/liboscar/coreprotocol.h108
-rw-r--r--kopete/protocols/oscar/liboscar/errortask.cpp66
-rw-r--r--kopete/protocols/oscar/liboscar/errortask.h39
-rw-r--r--kopete/protocols/oscar/liboscar/flapprotocol.cpp72
-rw-r--r--kopete/protocols/oscar/liboscar/flapprotocol.h46
-rw-r--r--kopete/protocols/oscar/liboscar/icbmparamstask.cpp143
-rw-r--r--kopete/protocols/oscar/liboscar/icbmparamstask.h56
-rw-r--r--kopete/protocols/oscar/liboscar/icqlogintask.cpp117
-rw-r--r--kopete/protocols/oscar/liboscar/icqlogintask.h47
-rw-r--r--kopete/protocols/oscar/liboscar/icqtask.cpp151
-rw-r--r--kopete/protocols/oscar/liboscar/icqtask.h63
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfo.cpp262
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfo.h213
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfotask.cpp234
-rw-r--r--kopete/protocols/oscar/liboscar/icquserinfotask.h77
-rw-r--r--kopete/protocols/oscar/liboscar/inputprotocolbase.cpp100
-rw-r--r--kopete/protocols/oscar/liboscar/inputprotocolbase.h72
-rw-r--r--kopete/protocols/oscar/liboscar/locationrightstask.cpp87
-rw-r--r--kopete/protocols/oscar/liboscar/locationrightstask.h57
-rw-r--r--kopete/protocols/oscar/liboscar/logintask.cpp218
-rw-r--r--kopete/protocols/oscar/liboscar/logintask.h144
-rw-r--r--kopete/protocols/oscar/liboscar/md5.c392
-rw-r--r--kopete/protocols/oscar/liboscar/md5.h93
-rw-r--r--kopete/protocols/oscar/liboscar/messagereceivertask.cpp461
-rw-r--r--kopete/protocols/oscar/liboscar/messagereceivertask.h79
-rw-r--r--kopete/protocols/oscar/liboscar/offlinemessagestask.cpp166
-rw-r--r--kopete/protocols/oscar/liboscar/offlinemessagestask.h54
-rw-r--r--kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp99
-rw-r--r--kopete/protocols/oscar/liboscar/onlinenotifiertask.h60
-rw-r--r--kopete/protocols/oscar/liboscar/oscarbytestream.cpp141
-rw-r--r--kopete/protocols/oscar/liboscar/oscarbytestream.h72
-rw-r--r--kopete/protocols/oscar/liboscar/oscarclientstream.cpp437
-rw-r--r--kopete/protocols/oscar/liboscar/oscarclientstream.h164
-rw-r--r--kopete/protocols/oscar/liboscar/oscarconnector.cpp108
-rw-r--r--kopete/protocols/oscar/liboscar/oscarconnector.h69
-rw-r--r--kopete/protocols/oscar/liboscar/oscardebug.h35
-rw-r--r--kopete/protocols/oscar/liboscar/oscarmessage.cpp301
-rw-r--r--kopete/protocols/oscar/liboscar/oscarmessage.h182
-rw-r--r--kopete/protocols/oscar/liboscar/oscarsettings.cpp69
-rw-r--r--kopete/protocols/oscar/liboscar/oscarsettings.h62
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypeclasses.cpp284
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypeclasses.h144
-rw-r--r--kopete/protocols/oscar/liboscar/oscartypes.h292
-rw-r--r--kopete/protocols/oscar/liboscar/oscarutils.cpp300
-rw-r--r--kopete/protocols/oscar/liboscar/oscarutils.h93
-rw-r--r--kopete/protocols/oscar/liboscar/ownuserinfotask.cpp137
-rw-r--r--kopete/protocols/oscar/liboscar/ownuserinfotask.h59
-rw-r--r--kopete/protocols/oscar/liboscar/prmparamstask.cpp72
-rw-r--r--kopete/protocols/oscar/liboscar/prmparamstask.h42
-rw-r--r--kopete/protocols/oscar/liboscar/profiletask.cpp119
-rw-r--r--kopete/protocols/oscar/liboscar/profiletask.h56
-rw-r--r--kopete/protocols/oscar/liboscar/rateclass.cpp246
-rw-r--r--kopete/protocols/oscar/liboscar/rateclass.h132
-rw-r--r--kopete/protocols/oscar/liboscar/rateclassmanager.cpp177
-rw-r--r--kopete/protocols/oscar/liboscar/rateclassmanager.h83
-rw-r--r--kopete/protocols/oscar/liboscar/rateinfotask.cpp173
-rw-r--r--kopete/protocols/oscar/liboscar/rateinfotask.h64
-rw-r--r--kopete/protocols/oscar/liboscar/rtf.cc2427
-rw-r--r--kopete/protocols/oscar/liboscar/rtf.ll864
-rw-r--r--kopete/protocols/oscar/liboscar/rtf2html.h207
-rw-r--r--kopete/protocols/oscar/liboscar/safedelete.cpp139
-rw-r--r--kopete/protocols/oscar/liboscar/safedelete.h79
-rw-r--r--kopete/protocols/oscar/liboscar/senddcinfotask.cpp107
-rw-r--r--kopete/protocols/oscar/liboscar/senddcinfotask.h41
-rw-r--r--kopete/protocols/oscar/liboscar/sendidletimetask.cpp57
-rw-r--r--kopete/protocols/oscar/liboscar/sendidletimetask.h46
-rw-r--r--kopete/protocols/oscar/liboscar/sendmessagetask.cpp447
-rw-r--r--kopete/protocols/oscar/liboscar/sendmessagetask.h55
-rw-r--r--kopete/protocols/oscar/liboscar/serverredirecttask.cpp173
-rw-r--r--kopete/protocols/oscar/liboscar/serverredirecttask.h72
-rw-r--r--kopete/protocols/oscar/liboscar/serverversionstask.cpp169
-rw-r--r--kopete/protocols/oscar/liboscar/serverversionstask.h59
-rw-r--r--kopete/protocols/oscar/liboscar/servicesetuptask.cpp135
-rw-r--r--kopete/protocols/oscar/liboscar/servicesetuptask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/snacprotocol.cpp109
-rw-r--r--kopete/protocols/oscar/liboscar/snacprotocol.h46
-rw-r--r--kopete/protocols/oscar/liboscar/ssiactivatetask.cpp50
-rw-r--r--kopete/protocols/oscar/liboscar/ssiactivatetask.h38
-rw-r--r--kopete/protocols/oscar/liboscar/ssiauthtask.cpp188
-rw-r--r--kopete/protocols/oscar/liboscar/ssiauthtask.h60
-rw-r--r--kopete/protocols/oscar/liboscar/ssilisttask.cpp174
-rw-r--r--kopete/protocols/oscar/liboscar/ssilisttask.h106
-rw-r--r--kopete/protocols/oscar/liboscar/ssimanager.cpp658
-rw-r--r--kopete/protocols/oscar/liboscar/ssimanager.h154
-rw-r--r--kopete/protocols/oscar/liboscar/ssimodifytask.cpp637
-rw-r--r--kopete/protocols/oscar/liboscar/ssimodifytask.h156
-rw-r--r--kopete/protocols/oscar/liboscar/ssiparamstask.cpp102
-rw-r--r--kopete/protocols/oscar/liboscar/ssiparamstask.h43
-rw-r--r--kopete/protocols/oscar/liboscar/stream.cpp31
-rw-r--r--kopete/protocols/oscar/liboscar/stream.h75
-rw-r--r--kopete/protocols/oscar/liboscar/task.cpp291
-rw-r--r--kopete/protocols/oscar/liboscar/task.h116
-rw-r--r--kopete/protocols/oscar/liboscar/tests/Makefile.am28
-rw-r--r--kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp305
-rw-r--r--kopete/protocols/oscar/liboscar/tests/chatnavtests.h50
-rw-r--r--kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp49
-rw-r--r--kopete/protocols/oscar/liboscar/tests/clientstream_test.h49
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp58
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ipaddrtest.h35
-rw-r--r--kopete/protocols/oscar/liboscar/tests/kunittest.cpp167
-rw-r--r--kopete/protocols/oscar/liboscar/tests/kunittest.h71
-rw-r--r--kopete/protocols/oscar/liboscar/tests/logintest.cpp56
-rw-r--r--kopete/protocols/oscar/liboscar/tests/logintest.h53
-rw-r--r--kopete/protocols/oscar/liboscar/tests/main.cpp35
-rw-r--r--kopete/protocols/oscar/liboscar/tests/redirecttest.cpp117
-rw-r--r--kopete/protocols/oscar/liboscar/tests/redirecttest.h51
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp73
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssigrouptest.h54
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssitest.cpp111
-rw-r--r--kopete/protocols/oscar/liboscar/tests/ssitest.h34
-rw-r--r--kopete/protocols/oscar/liboscar/tests/tester.h121
-rw-r--r--kopete/protocols/oscar/liboscar/tests/userinfotest.cpp67
-rw-r--r--kopete/protocols/oscar/liboscar/tests/userinfotest.h53
-rw-r--r--kopete/protocols/oscar/liboscar/transfer.cpp367
-rw-r--r--kopete/protocols/oscar/liboscar/transfer.h169
-rw-r--r--kopete/protocols/oscar/liboscar/typingnotifytask.cpp124
-rw-r--r--kopete/protocols/oscar/liboscar/typingnotifytask.h62
-rw-r--r--kopete/protocols/oscar/liboscar/userdetails.cpp555
-rw-r--r--kopete/protocols/oscar/liboscar/userdetails.h121
-rw-r--r--kopete/protocols/oscar/liboscar/userinfotask.cpp156
-rw-r--r--kopete/protocols/oscar/liboscar/userinfotask.h69
-rw-r--r--kopete/protocols/oscar/liboscar/usersearchtask.cpp315
-rw-r--r--kopete/protocols/oscar/liboscar/usersearchtask.h61
-rw-r--r--kopete/protocols/oscar/liboscar/warningtask.cpp96
-rw-r--r--kopete/protocols/oscar/liboscar/warningtask.h59
-rw-r--r--kopete/protocols/oscar/oscaraccount.cpp914
-rw-r--r--kopete/protocols/oscar/oscaraccount.h204
-rw-r--r--kopete/protocols/oscar/oscarcontact.cpp237
-rw-r--r--kopete/protocols/oscar/oscarcontact.h140
-rw-r--r--kopete/protocols/oscar/oscarencodingselectionbase.ui58
-rw-r--r--kopete/protocols/oscar/oscarencodingselectiondialog.cpp121
-rw-r--r--kopete/protocols/oscar/oscarencodingselectiondialog.h48
-rw-r--r--kopete/protocols/oscar/oscarlistcontactsbase.ui49
-rw-r--r--kopete/protocols/oscar/oscarlistnonservercontacts.cpp71
-rw-r--r--kopete/protocols/oscar/oscarlistnonservercontacts.h52
-rw-r--r--kopete/protocols/oscar/oscarmyselfcontact.cpp60
-rw-r--r--kopete/protocols/oscar/oscarmyselfcontact.h59
-rw-r--r--kopete/protocols/oscar/oscarversionupdater.cpp295
-rw-r--r--kopete/protocols/oscar/oscarversionupdater.h120
-rw-r--r--kopete/protocols/oscar/oscarvisibilitybase.ui170
-rw-r--r--kopete/protocols/oscar/oscarvisibilitydialog.cpp135
-rw-r--r--kopete/protocols/oscar/oscarvisibilitydialog.h70
-rw-r--r--kopete/protocols/sms/Makefile.am21
-rw-r--r--kopete/protocols/sms/icons/Makefile.am3
-rw-r--r--kopete/protocols/sms/icons/cr128-app-sms_protocol.pngbin0 -> 14177 bytes
-rw-r--r--kopete/protocols/sms/icons/cr16-app-sms_protocol.pngbin0 -> 1039 bytes
-rw-r--r--kopete/protocols/sms/icons/cr32-app-sms_protocol.pngbin0 -> 2760 bytes
-rw-r--r--kopete/protocols/sms/icons/cr48-app-sms_protocol.pngbin0 -> 5024 bytes
-rw-r--r--kopete/protocols/sms/icons/cr64-app-sms_protocol.pngbin0 -> 7219 bytes
-rw-r--r--kopete/protocols/sms/kopete_sms.desktop81
-rw-r--r--kopete/protocols/sms/serviceloader.cpp74
-rw-r--r--kopete/protocols/sms/serviceloader.h42
-rw-r--r--kopete/protocols/sms/services/Makefile.am18
-rw-r--r--kopete/protocols/sms/services/gsmlib.cpp462
-rw-r--r--kopete/protocols/sms/services/gsmlib.h151
-rw-r--r--kopete/protocols/sms/services/gsmlibprefs.ui100
-rw-r--r--kopete/protocols/sms/services/kopete_unix_serial.cpp445
-rw-r--r--kopete/protocols/sms/services/kopete_unix_serial.h70
-rw-r--r--kopete/protocols/sms/services/smsclient.cpp192
-rw-r--r--kopete/protocols/sms/services/smsclient.h65
-rw-r--r--kopete/protocols/sms/services/smsclientprefs.ui135
-rw-r--r--kopete/protocols/sms/services/smssend.cpp254
-rw-r--r--kopete/protocols/sms/services/smssend.h66
-rw-r--r--kopete/protocols/sms/services/smssendprefs.ui188
-rw-r--r--kopete/protocols/sms/services/smssendprovider.cpp288
-rw-r--r--kopete/protocols/sms/services/smssendprovider.h82
-rw-r--r--kopete/protocols/sms/smsaccount.cpp202
-rw-r--r--kopete/protocols/sms/smsaccount.h79
-rw-r--r--kopete/protocols/sms/smsaddcontactpage.cpp65
-rw-r--r--kopete/protocols/sms/smsaddcontactpage.h50
-rw-r--r--kopete/protocols/sms/smscontact.cpp142
-rw-r--r--kopete/protocols/sms/smscontact.h74
-rw-r--r--kopete/protocols/sms/smseditaccountwidget.cpp147
-rw-r--r--kopete/protocols/sms/smseditaccountwidget.h64
-rw-r--r--kopete/protocols/sms/smsprotocol.cpp97
-rw-r--r--kopete/protocols/sms/smsprotocol.h71
-rw-r--r--kopete/protocols/sms/smsservice.cpp63
-rw-r--r--kopete/protocols/sms/smsservice.h83
-rw-r--r--kopete/protocols/sms/smsuserpreferences.cpp63
-rw-r--r--kopete/protocols/sms/smsuserpreferences.h44
-rw-r--r--kopete/protocols/sms/ui/Makefile.am8
-rw-r--r--kopete/protocols/sms/ui/empty.cpp0
-rw-r--r--kopete/protocols/sms/ui/smsactprefs.ui435
-rw-r--r--kopete/protocols/sms/ui/smsadd.ui143
-rw-r--r--kopete/protocols/sms/ui/smsuserprefs.ui118
-rw-r--r--kopete/protocols/testbed/Makefile.am15
-rw-r--r--kopete/protocols/testbed/icons/Makefile.am3
-rw-r--r--kopete/protocols/testbed/icons/cr128-app-testbed_protocol.pngbin0 -> 12571 bytes
-rw-r--r--kopete/protocols/testbed/icons/cr16-app-testbed_protocol.pngbin0 -> 1003 bytes
-rw-r--r--kopete/protocols/testbed/icons/cr32-app-testbed_protocol.pngbin0 -> 2613 bytes
-rw-r--r--kopete/protocols/testbed/icons/cr48-app-testbed_protocol.pngbin0 -> 4576 bytes
-rw-r--r--kopete/protocols/testbed/icons/cr64-app-testbed_protocol.pngbin0 -> 6607 bytes
-rw-r--r--kopete/protocols/testbed/kopete_testbed.desktop97
-rw-r--r--kopete/protocols/testbed/testbedaccount.cpp176
-rw-r--r--kopete/protocols/testbed/testbedaccount.h105
-rw-r--r--kopete/protocols/testbed/testbedaccountpreferences.ui160
-rw-r--r--kopete/protocols/testbed/testbedaddcontactpage.cpp68
-rw-r--r--kopete/protocols/testbed/testbedaddcontactpage.h50
-rw-r--r--kopete/protocols/testbed/testbedaddui.ui107
-rw-r--r--kopete/protocols/testbed/testbedcontact.cpp141
-rw-r--r--kopete/protocols/testbed/testbedcontact.h95
-rw-r--r--kopete/protocols/testbed/testbededitaccountwidget.cpp62
-rw-r--r--kopete/protocols/testbed/testbededitaccountwidget.h52
-rw-r--r--kopete/protocols/testbed/testbedfakeserver.cpp64
-rw-r--r--kopete/protocols/testbed/testbedfakeserver.h66
-rw-r--r--kopete/protocols/testbed/testbedincomingmessage.cpp36
-rw-r--r--kopete/protocols/testbed/testbedincomingmessage.h55
-rw-r--r--kopete/protocols/testbed/testbedprotocol.cpp101
-rw-r--r--kopete/protocols/testbed/testbedprotocol.h74
-rw-r--r--kopete/protocols/testbed/ui/Makefile.am7
-rw-r--r--kopete/protocols/testbed/ui/testbedwebcamdialog.cpp80
-rw-r--r--kopete/protocols/testbed/ui/testbedwebcamdialog.h60
-rw-r--r--kopete/protocols/winpopup/Makefile.am22
-rw-r--r--kopete/protocols/winpopup/icons/Makefile.am3
-rw-r--r--kopete/protocols/winpopup/icons/cr128-app-wp_protocol.pngbin0 -> 12382 bytes
-rw-r--r--kopete/protocols/winpopup/icons/cr16-action-wp_away.pngbin0 -> 841 bytes
-rw-r--r--kopete/protocols/winpopup/icons/cr16-app-wp_protocol.pngbin0 -> 1026 bytes
-rw-r--r--kopete/protocols/winpopup/icons/cr32-app-wp_protocol.pngbin0 -> 2412 bytes
-rw-r--r--kopete/protocols/winpopup/icons/cr48-app-wp_protocol.pngbin0 -> 4129 bytes
-rw-r--r--kopete/protocols/winpopup/icons/cr64-app-wp_protocol.pngbin0 -> 6058 bytes
-rw-r--r--kopete/protocols/winpopup/kopete_wp.desktop85
-rw-r--r--kopete/protocols/winpopup/libwinpopup/Makefile.am9
-rw-r--r--kopete/protocols/winpopup/libwinpopup/libwinpopup.cpp363
-rw-r--r--kopete/protocols/winpopup/libwinpopup/libwinpopup.h92
-rw-r--r--kopete/protocols/winpopup/ui/Makefile.am10
-rw-r--r--kopete/protocols/winpopup/ui/empty.cpp1
-rw-r--r--kopete/protocols/winpopup/ui/wpaddcontactbase.ui190
-rw-r--r--kopete/protocols/winpopup/ui/wpeditaccountbase.ui358
-rw-r--r--kopete/protocols/winpopup/ui/wpuserinfowidget.ui219
-rwxr-xr-xkopete/protocols/winpopup/winpopup-install.sh37
-rwxr-xr-xkopete/protocols/winpopup/winpopup-send.sh44
-rw-r--r--kopete/protocols/winpopup/wpaccount.cpp209
-rw-r--r--kopete/protocols/winpopup/wpaccount.h107
-rw-r--r--kopete/protocols/winpopup/wpaddcontact.cpp115
-rw-r--r--kopete/protocols/winpopup/wpaddcontact.h58
-rw-r--r--kopete/protocols/winpopup/wpcontact.cpp189
-rw-r--r--kopete/protocols/winpopup/wpcontact.h86
-rw-r--r--kopete/protocols/winpopup/wpeditaccount.cpp138
-rw-r--r--kopete/protocols/winpopup/wpeditaccount.h57
-rw-r--r--kopete/protocols/winpopup/wpprotocol.cpp187
-rw-r--r--kopete/protocols/winpopup/wpprotocol.h94
-rw-r--r--kopete/protocols/winpopup/wpuserinfo.cpp114
-rw-r--r--kopete/protocols/winpopup/wpuserinfo.h61
-rw-r--r--kopete/protocols/yahoo/Makefile.am26
-rw-r--r--kopete/protocols/yahoo/icons/Makefile.am3
-rw-r--r--kopete/protocols/yahoo/icons/cr128-app-yahoo_protocol.pngbin0 -> 13310 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_away.pngbin0 -> 614 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_busy.pngbin0 -> 599 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_connecting.mngbin0 -> 7089 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_idle.pngbin0 -> 668 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_invisible.pngbin0 -> 688 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_mobile.pngbin0 -> 535 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_stealthed.pngbin0 -> 760 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-action-yahoo_tea.pngbin0 -> 878 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr16-app-yahoo_protocol.pngbin0 -> 975 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr22-action-yahoo_stealthed.pngbin0 -> 1031 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr32-action-yahoo_stealthed.pngbin0 -> 1511 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr32-app-yahoo_protocol.pngbin0 -> 2608 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr48-app-yahoo_protocol.pngbin0 -> 4548 bytes
-rw-r--r--kopete/protocols/yahoo/icons/cr64-app-yahoo_protocol.pngbin0 -> 6728 bytes
-rw-r--r--kopete/protocols/yahoo/kopete_yahoo.desktop83
-rw-r--r--kopete/protocols/yahoo/libkyahoo/Makefile.am23
-rw-r--r--kopete/protocols/yahoo/libkyahoo/bytestream.cpp289
-rw-r--r--kopete/protocols/yahoo/libkyahoo/bytestream.h78
-rw-r--r--kopete/protocols/yahoo/libkyahoo/changestatustask.cpp85
-rw-r--r--kopete/protocols/yahoo/libkyahoo/changestatustask.h50
-rw-r--r--kopete/protocols/yahoo/libkyahoo/chatsessiontask.cpp66
-rw-r--r--kopete/protocols/yahoo/libkyahoo/chatsessiontask.h45
-rw-r--r--kopete/protocols/yahoo/libkyahoo/client.cpp869
-rw-r--r--kopete/protocols/yahoo/libkyahoo/client.h618
-rw-r--r--kopete/protocols/yahoo/libkyahoo/conferencetask.cpp259
-rw-r--r--kopete/protocols/yahoo/libkyahoo/conferencetask.h57
-rw-r--r--kopete/protocols/yahoo/libkyahoo/configure.in.in38
-rw-r--r--kopete/protocols/yahoo/libkyahoo/connector.cpp62
-rw-r--r--kopete/protocols/yahoo/libkyahoo/connector.h59
-rw-r--r--kopete/protocols/yahoo/libkyahoo/coreprotocol.cpp228
-rw-r--r--kopete/protocols/yahoo/libkyahoo/coreprotocol.h107
-rw-r--r--kopete/protocols/yahoo/libkyahoo/crypt.c210
-rw-r--r--kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.cpp152
-rw-r--r--kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.h50
-rw-r--r--kopete/protocols/yahoo/libkyahoo/inputprotocolbase.cpp98
-rw-r--r--kopete/protocols/yahoo/libkyahoo/inputprotocolbase.h72
-rw-r--r--kopete/protocols/yahoo/libkyahoo/libyahoo.c532
-rw-r--r--kopete/protocols/yahoo/libkyahoo/libyahoo.h61
-rw-r--r--kopete/protocols/yahoo/libkyahoo/listtask.cpp108
-rw-r--r--kopete/protocols/yahoo/libkyahoo/listtask.h48
-rw-r--r--kopete/protocols/yahoo/libkyahoo/logintask.cpp303
-rw-r--r--kopete/protocols/yahoo/libkyahoo/logintask.h75
-rw-r--r--kopete/protocols/yahoo/libkyahoo/logofftask.cpp43
-rw-r--r--kopete/protocols/yahoo/libkyahoo/logofftask.h36
-rw-r--r--kopete/protocols/yahoo/libkyahoo/mailnotifiertask.cpp80
-rw-r--r--kopete/protocols/yahoo/libkyahoo/mailnotifiertask.h44
-rw-r--r--kopete/protocols/yahoo/libkyahoo/md5.c408
-rw-r--r--kopete/protocols/yahoo/libkyahoo/md5.h93
-rw-r--r--kopete/protocols/yahoo/libkyahoo/messagereceivertask.cpp148
-rw-r--r--kopete/protocols/yahoo/libkyahoo/messagereceivertask.h49
-rw-r--r--kopete/protocols/yahoo/libkyahoo/modifybuddytask.cpp116
-rw-r--r--kopete/protocols/yahoo/libkyahoo/modifybuddytask.h53
-rw-r--r--kopete/protocols/yahoo/libkyahoo/modifyyabtask.cpp205
-rw-r--r--kopete/protocols/yahoo/libkyahoo/modifyyabtask.h64
-rw-r--r--kopete/protocols/yahoo/libkyahoo/oscartypes.h31
-rw-r--r--kopete/protocols/yahoo/libkyahoo/picturenotifiertask.cpp157
-rw-r--r--kopete/protocols/yahoo/libkyahoo/picturenotifiertask.h51
-rw-r--r--kopete/protocols/yahoo/libkyahoo/pingtask.cpp46
-rw-r--r--kopete/protocols/yahoo/libkyahoo/pingtask.h36
-rw-r--r--kopete/protocols/yahoo/libkyahoo/receivefiletask.cpp243
-rw-r--r--kopete/protocols/yahoo/libkyahoo/receivefiletask.h83
-rw-r--r--kopete/protocols/yahoo/libkyahoo/requestpicturetask.cpp52
-rw-r--r--kopete/protocols/yahoo/libkyahoo/requestpicturetask.h41
-rw-r--r--kopete/protocols/yahoo/libkyahoo/safedelete.cpp139
-rw-r--r--kopete/protocols/yahoo/libkyahoo/safedelete.h79
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendauthresptask.cpp73
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendauthresptask.h46
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendfiletask.cpp189
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendfiletask.h68
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendmessagetask.cpp80
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendmessagetask.h44
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendnotifytask.cpp80
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendnotifytask.h48
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendpicturetask.cpp247
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sendpicturetask.h77
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sha1.c628
-rw-r--r--kopete/protocols/yahoo/libkyahoo/sha1.h72
-rw-r--r--kopete/protocols/yahoo/libkyahoo/statusnotifiertask.cpp184
-rw-r--r--kopete/protocols/yahoo/libkyahoo/statusnotifiertask.h53
-rw-r--r--kopete/protocols/yahoo/libkyahoo/stealthtask.cpp76
-rw-r--r--kopete/protocols/yahoo/libkyahoo/stealthtask.h46
-rw-r--r--kopete/protocols/yahoo/libkyahoo/stream.cpp31
-rw-r--r--kopete/protocols/yahoo/libkyahoo/stream.h76
-rw-r--r--kopete/protocols/yahoo/libkyahoo/task.cpp265
-rw-r--r--kopete/protocols/yahoo/libkyahoo/task.h93
-rw-r--r--kopete/protocols/yahoo/libkyahoo/tests/Makefile.am9
-rw-r--r--kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.cpp57
-rw-r--r--kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.h49
-rw-r--r--kopete/protocols/yahoo/libkyahoo/tests/logintest.cpp72
-rw-r--r--kopete/protocols/yahoo/libkyahoo/tests/logintest.h64
-rw-r--r--kopete/protocols/yahoo/libkyahoo/transfer.cpp26
-rw-r--r--kopete/protocols/yahoo/libkyahoo/transfer.h35
-rw-r--r--kopete/protocols/yahoo/libkyahoo/webcamtask.cpp689
-rw-r--r--kopete/protocols/yahoo/libkyahoo/webcamtask.h112
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yabentry.cpp201
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yabentry.h91
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yabtask.cpp160
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yabtask.h60
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoo_fn.c4620
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoo_fn.h33
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.cpp108
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.h77
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoobytestream.cpp140
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahoobytestream.h69
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahooclientstream.cpp418
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahooclientstream.h159
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahooconnector.cpp111
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahooconnector.h67
-rw-r--r--kopete/protocols/yahoo/libkyahoo/yahootypes.h182
-rw-r--r--kopete/protocols/yahoo/libkyahoo/ymsgprotocol.cpp347
-rw-r--r--kopete/protocols/yahoo/libkyahoo/ymsgprotocol.h44
-rw-r--r--kopete/protocols/yahoo/libkyahoo/ymsgtransfer.cpp239
-rw-r--r--kopete/protocols/yahoo/libkyahoo/ymsgtransfer.h76
-rw-r--r--kopete/protocols/yahoo/ui/Makefile.am14
-rw-r--r--kopete/protocols/yahoo/ui/empty.cpp1
-rw-r--r--kopete/protocols/yahoo/ui/yahooadd.ui97
-rw-r--r--kopete/protocols/yahoo/ui/yahooeditaccountbase.ui467
-rw-r--r--kopete/protocols/yahoo/ui/yahoogeneralinfowidget.ui647
-rw-r--r--kopete/protocols/yahoo/ui/yahooinvitelistbase.ui337
-rw-r--r--kopete/protocols/yahoo/ui/yahooinvitelistimpl.cpp165
-rw-r--r--kopete/protocols/yahoo/ui/yahooinvitelistimpl.h59
-rw-r--r--kopete/protocols/yahoo/ui/yahoootherinfowidget.ui119
-rw-r--r--kopete/protocols/yahoo/ui/yahoostealthsetting.ui96
-rw-r--r--kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp260
-rw-r--r--kopete/protocols/yahoo/ui/yahoouserinfodialog.h56
-rw-r--r--kopete/protocols/yahoo/ui/yahooverifyaccountbase.ui159
-rw-r--r--kopete/protocols/yahoo/ui/yahoowebcamdialog.cpp113
-rw-r--r--kopete/protocols/yahoo/ui/yahoowebcamdialog.h56
-rw-r--r--kopete/protocols/yahoo/ui/yahooworkinfowidget.ui233
-rw-r--r--kopete/protocols/yahoo/yahooaccount.cpp1831
-rw-r--r--kopete/protocols/yahoo/yahooaccount.h295
-rw-r--r--kopete/protocols/yahoo/yahooaddcontact.cpp72
-rw-r--r--kopete/protocols/yahoo/yahooaddcontact.h55
-rw-r--r--kopete/protocols/yahoo/yahoochatsession.cpp166
-rw-r--r--kopete/protocols/yahoo/yahoochatsession.h51
-rw-r--r--kopete/protocols/yahoo/yahoochatui.rc25
-rw-r--r--kopete/protocols/yahoo/yahooconferencemessagemanager.cpp115
-rw-r--r--kopete/protocols/yahoo/yahooconferencemessagemanager.h58
-rw-r--r--kopete/protocols/yahoo/yahooconferenceui.rc11
-rw-r--r--kopete/protocols/yahoo/yahoocontact.cpp835
-rw-r--r--kopete/protocols/yahoo/yahoocontact.h141
-rw-r--r--kopete/protocols/yahoo/yahooeditaccount.cpp197
-rw-r--r--kopete/protocols/yahoo/yahooeditaccount.h59
-rw-r--r--kopete/protocols/yahoo/yahooprotocol.cpp209
-rw-r--r--kopete/protocols/yahoo/yahooprotocol.h148
-rw-r--r--kopete/protocols/yahoo/yahooverifyaccount.cpp107
-rw-r--r--kopete/protocols/yahoo/yahooverifyaccount.h57
-rw-r--r--kopete/protocols/yahoo/yahoowebcam.cpp137
-rw-r--r--kopete/protocols/yahoo/yahoowebcam.h62
-rw-r--r--kopete/sounds/Kopete_Event.oggbin0 -> 27923 bytes
-rw-r--r--kopete/sounds/Kopete_Received.oggbin0 -> 26165 bytes
-rw-r--r--kopete/sounds/Kopete_Sent.oggbin0 -> 34817 bytes
-rw-r--r--kopete/sounds/Kopete_User_is_Online.oggbin0 -> 82767 bytes
-rw-r--r--kopete/sounds/Makefile.am4
-rw-r--r--kopete/styles/Clean/Contents/Makefile.am1
-rw-r--r--kopete/styles/Clean/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Clean/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Clean/Contents/Resources/Incoming/Action.html16
-rw-r--r--kopete/styles/Clean/Contents/Resources/Incoming/Content.html21
-rw-r--r--kopete/styles/Clean/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Clean/Contents/Resources/Incoming/NextContent.html3
-rw-r--r--kopete/styles/Clean/Contents/Resources/Incoming/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Clean/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Clean/Contents/Resources/Outgoing/Action.html16
-rw-r--r--kopete/styles/Clean/Contents/Resources/Outgoing/Content.html21
-rw-r--r--kopete/styles/Clean/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Clean/Contents/Resources/Outgoing/NextContent.html3
-rw-r--r--kopete/styles/Clean/Contents/Resources/Outgoing/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Clean/Contents/Resources/Status.html10
-rw-r--r--kopete/styles/Clean/Contents/Resources/images/Makefile.am4
-rw-r--r--kopete/styles/Clean/Contents/Resources/images/action.pngbin0 -> 661 bytes
-rw-r--r--kopete/styles/Clean/Contents/Resources/images/important.pngbin0 -> 937 bytes
-rw-r--r--kopete/styles/Clean/Contents/Resources/images/internal.pngbin0 -> 828 bytes
-rw-r--r--kopete/styles/Clean/Contents/Resources/main.css169
-rw-r--r--kopete/styles/Clean/Makefile.am1
-rw-r--r--kopete/styles/Clear/Contents/Makefile.am1
-rw-r--r--kopete/styles/Clear/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Clear/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Clear/Contents/Resources/Incoming/Action.html16
-rw-r--r--kopete/styles/Clear/Contents/Resources/Incoming/Content.html33
-rw-r--r--kopete/styles/Clear/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Clear/Contents/Resources/Incoming/NextContent.html3
-rw-r--r--kopete/styles/Clear/Contents/Resources/Incoming/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Clear/Contents/Resources/Outgoing/Action.html16
-rw-r--r--kopete/styles/Clear/Contents/Resources/Outgoing/Content.html32
-rw-r--r--kopete/styles/Clear/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Clear/Contents/Resources/Outgoing/NextContent.html3
-rw-r--r--kopete/styles/Clear/Contents/Resources/Outgoing/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/Status.html6
-rw-r--r--kopete/styles/Clear/Contents/Resources/Variants/Makefile.am5
-rw-r--r--kopete/styles/Clear/Contents/Resources/Variants/No_avatars.css23
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/Makefile.am4
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-background.pngbin0 -> 220 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-inbound-arrow.pngbin0 -> 215 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-inbound-avatar.pngbin0 -> 1021 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-inbound-background.pngbin0 -> 185 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-inbound-left.pngbin0 -> 237 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-inbound-right.pngbin0 -> 240 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-outbound-arrow.pngbin0 -> 217 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-outbound-avatar.pngbin0 -> 990 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-outbound-left.pngbin0 -> 232 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/body-outbound-right.pngbin0 -> 238 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-inbound-background.pngbin0 -> 185 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-inbound-left.pngbin0 -> 264 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-inbound-right.pngbin0 -> 197 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-outbound-background.pngbin0 -> 185 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-outbound-left.pngbin0 -> 197 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/footer-outbound-right.pngbin0 -> 261 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-inbound-background.pngbin0 -> 218 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-inbound-left.pngbin0 -> 342 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-inbound-right.pngbin0 -> 364 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-outbound-background.pngbin0 -> 201 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-outbound-left.pngbin0 -> 295 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/header-outbound-right.pngbin0 -> 316 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-action.pngbin0 -> 978 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-highlighted.pngbin0 -> 937 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-internal.pngbin0 -> 1041 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-me.pngbin0 -> 768 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-time.pngbin0 -> 857 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/images/icon-you.pngbin0 -> 865 bytes
-rw-r--r--kopete/styles/Clear/Contents/Resources/main.css376
-rw-r--r--kopete/styles/Clear/Makefile.am1
-rw-r--r--kopete/styles/Gaim/CREDITS7
-rw-r--r--kopete/styles/Gaim/Contents/Info.plist31
-rw-r--r--kopete/styles/Gaim/Contents/Makefile.am1
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Incoming/Action.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Incoming/Content.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Incoming/NextContent.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Outgoing/Action.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Outgoing/Content.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Outgoing/NextContent.html6
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Status.html5
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Variants/Contact-Colors.css10
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Variants/Makefile.am4
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Variants/Name-Colors.css13
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Variants/No-Colors.css22
-rw-r--r--kopete/styles/Gaim/Contents/Resources/Variants/Status-Colors.css9
-rw-r--r--kopete/styles/Gaim/Contents/Resources/main.css40
-rw-r--r--kopete/styles/Gaim/Makefile.am1
-rw-r--r--kopete/styles/Hacker/COPYRIGHT18
-rwxr-xr-xkopete/styles/Hacker/Contents/Info.plist42
-rw-r--r--kopete/styles/Hacker/Contents/Makefile.am5
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Header.html8
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/Action.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/Content.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/Context.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/NextContent.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/NextContext.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Incoming/buddy_icon.pngbin0 -> 2671 bytes
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/Action.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/Content.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/Context.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/NextContent.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/NextContext.html6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Outgoing/buddy_icon.pngbin0 -> 2494 bytes
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Status.html11
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Dark-Noback.css6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Dark.css6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Dark2-Noback.css9
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Dark2.css9
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Light-Noback.css40
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Light.css6
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Light2-Noback.css4
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Light2.css4
-rw-r--r--kopete/styles/Hacker/Contents/Resources/Variants/Makefile.am5
-rw-r--r--kopete/styles/Hacker/Contents/Resources/images/Makefile.am4
-rw-r--r--kopete/styles/Hacker/Contents/Resources/images/background.pngbin0 -> 325 bytes
-rw-r--r--kopete/styles/Hacker/Contents/Resources/images/background2.pngbin0 -> 283 bytes
-rw-r--r--kopete/styles/Hacker/Contents/Resources/images/kopete.pngbin0 -> 83743 bytes
-rw-r--r--kopete/styles/Hacker/Contents/Resources/main.css37
-rw-r--r--kopete/styles/Hacker/Makefile.am5
-rw-r--r--kopete/styles/Hacker/README8
-rw-r--r--kopete/styles/Hacker/gpl.txt340
-rw-r--r--kopete/styles/Konqi/Contents/Makefile.am1
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Incoming/Content.html10
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Incoming/NextContent.html2
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Incoming/buddy_icon.pngbin0 -> 10436 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Outgoing/Content.html10
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Outgoing/NextContent.html2
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Outgoing/buddy_icon.pngbin0 -> 7350 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Status.html4
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Makefile.am5
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_blue.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon_without_transparency.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_without_transparency.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_green.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_trans.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_transparency.css39
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/Makefile.am4
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre1.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre2.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre3.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre4.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre5.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre6.pngbin0 -> 109 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-blue.pngbin0 -> 492111 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-green.pngbin0 -> 516771 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-moon.jpgbin0 -> 72752 bytes
-rw-r--r--kopete/styles/Konqi/Contents/Resources/main.css50
-rw-r--r--kopete/styles/Konqi/Contents/Resources/puce.pngbin0 -> 4292 bytes
-rw-r--r--kopete/styles/Konqi/Makefile.am1
-rw-r--r--kopete/styles/Kopete/Contents/Makefile.am1
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Incoming/Action.html16
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Incoming/Content.html20
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Incoming/NextContent.html3
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Incoming/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Outgoing/Action.html16
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Outgoing/Content.html21
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Outgoing/NextContent.html3
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Outgoing/buddy_icon.pngbin0 -> 7657 bytes
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Status.html10
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Variants/Big_pictures.css35
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Variants/Contact_color.css7
-rw-r--r--kopete/styles/Kopete/Contents/Resources/Variants/Makefile.am5
-rw-r--r--kopete/styles/Kopete/Contents/Resources/images/Makefile.am4
-rw-r--r--kopete/styles/Kopete/Contents/Resources/images/action.pngbin0 -> 769 bytes
-rw-r--r--kopete/styles/Kopete/Contents/Resources/images/important.pngbin0 -> 937 bytes
-rw-r--r--kopete/styles/Kopete/Contents/Resources/images/system.pngbin0 -> 1041 bytes
-rw-r--r--kopete/styles/Kopete/Contents/Resources/main.css211
-rw-r--r--kopete/styles/Kopete/Makefile.am1
-rw-r--r--kopete/styles/Makefile.am2
-rw-r--r--kopete/styles/Retropete/Contents/Makefile.am1
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Footer.html0
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Header.html0
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Incoming/Action.html10
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Incoming/Content.html6
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Incoming/Makefile.am4
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Incoming/NextContent.html6
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Makefile.am5
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Outgoing/Action.html10
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Outgoing/Content.html6
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Outgoing/Makefile.am4
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Outgoing/NextContent.html6
-rw-r--r--kopete/styles/Retropete/Contents/Resources/Status.html3
-rw-r--r--kopete/styles/Retropete/Contents/Resources/main.css55
-rw-r--r--kopete/styles/Retropete/Makefile.am1
-rw-r--r--kpf/AUTHORS2
-rw-r--r--kpf/COPYING16
-rw-r--r--kpf/ChangeLog45
-rw-r--r--kpf/DESIGN109
-rw-r--r--kpf/Makefile.am12
-rw-r--r--kpf/NEWS1
-rw-r--r--kpf/README42
-rw-r--r--kpf/THANKS2
-rw-r--r--kpf/cr16-app-kpf.pngbin0 -> 1119 bytes
-rw-r--r--kpf/cr32-app-kpf.pngbin0 -> 3181 bytes
-rw-r--r--kpf/cr48-app-kpf.pngbin0 -> 6364 bytes
-rw-r--r--kpf/kpfapplet.desktop121
-rw-r--r--kpf/kpfpropertiesdialogplugin.desktop56
-rw-r--r--kpf/src/ActiveMonitor.cpp226
-rw-r--r--kpf/src/ActiveMonitor.h165
-rw-r--r--kpf/src/ActiveMonitorItem.cpp199
-rw-r--r--kpf/src/ActiveMonitorItem.h144
-rw-r--r--kpf/src/ActiveMonitorWindow.cpp90
-rw-r--r--kpf/src/ActiveMonitorWindow.h104
-rw-r--r--kpf/src/Applet.cpp488
-rw-r--r--kpf/src/Applet.h180
-rw-r--r--kpf/src/AppletItem.cpp385
-rw-r--r--kpf/src/AppletItem.h167
-rw-r--r--kpf/src/BandwidthGraph.cpp335
-rw-r--r--kpf/src/BandwidthGraph.h163
-rw-r--r--kpf/src/ByteRange.cpp176
-rw-r--r--kpf/src/ByteRange.h128
-rw-r--r--kpf/src/ConfigDialogPage.cpp318
-rw-r--r--kpf/src/ConfigDialogPage.h107
-rw-r--r--kpf/src/Defaults.cpp97
-rw-r--r--kpf/src/Defaults.h71
-rw-r--r--kpf/src/Defines.h33
-rw-r--r--kpf/src/DirSelectWidget.cpp115
-rw-r--r--kpf/src/DirSelectWidget.h66
-rw-r--r--kpf/src/DirectoryLister.cpp345
-rw-r--r--kpf/src/DirectoryLister.h72
-rw-r--r--kpf/src/ErrorMessageConfigDialog.cpp154
-rw-r--r--kpf/src/ErrorMessageConfigDialog.h88
-rw-r--r--kpf/src/Help.cpp62
-rw-r--r--kpf/src/Help.h44
-rw-r--r--kpf/src/KPFInterface.cpp130
-rw-r--r--kpf/src/KPFInterface.h68
-rw-r--r--kpf/src/Makefile.am89
-rw-r--r--kpf/src/PortValidator.cpp57
-rw-r--r--kpf/src/PortValidator.h47
-rw-r--r--kpf/src/PropertiesDialogPlugin.cpp890
-rw-r--r--kpf/src/PropertiesDialogPlugin.h82
-rw-r--r--kpf/src/Request.cpp377
-rw-r--r--kpf/src/Request.h252
-rw-r--r--kpf/src/Resource.cpp346
-rw-r--r--kpf/src/Resource.h149
-rw-r--r--kpf/src/Response.cpp194
-rw-r--r--kpf/src/Response.h102
-rw-r--r--kpf/src/RootValidator.cpp66
-rw-r--r--kpf/src/RootValidator.h47
-rw-r--r--kpf/src/Server.cpp1137
-rw-r--r--kpf/src/Server.h186
-rw-r--r--kpf/src/ServerPrivate.cpp54
-rw-r--r--kpf/src/ServerPrivate.h78
-rw-r--r--kpf/src/ServerSocket.cpp46
-rw-r--r--kpf/src/ServerSocket.h47
-rw-r--r--kpf/src/ServerWizard.cpp410
-rw-r--r--kpf/src/ServerWizard.h90
-rw-r--r--kpf/src/SingleServerConfigDialog.cpp98
-rw-r--r--kpf/src/SingleServerConfigDialog.h68
-rw-r--r--kpf/src/StartingKPFDialog.cpp139
-rw-r--r--kpf/src/StartingKPFDialog.h62
-rw-r--r--kpf/src/System.cpp71
-rw-r--r--kpf/src/System.h35
-rw-r--r--kpf/src/Utils.cpp414
-rw-r--r--kpf/src/Utils.h113
-rw-r--r--kpf/src/WebServer.cpp644
-rw-r--r--kpf/src/WebServer.h346
-rw-r--r--kpf/src/WebServerManager.cpp295
-rw-r--r--kpf/src/WebServerManager.h173
-rw-r--r--kpf/src/WebServerSocket.cpp44
-rw-r--r--kpf/src/WebServerSocket.h51
-rw-r--r--kppp/AUTHORS8
-rw-r--r--kppp/ChangeLog1711
-rw-r--r--kppp/DB/Makefile.am3
-rw-r--r--kppp/DB/Modem/modemDB.rc31
-rw-r--r--kppp/DB/Provider/Austria/.directory50
-rw-r--r--kppp/DB/Provider/Austria/Makefile.am5
-rw-r--r--kppp/DB/Provider/Austria/Simon%032Media25
-rw-r--r--kppp/DB/Provider/Belarus/.directory43
-rw-r--r--kppp/DB/Provider/Belarus/AtlantTelecom29
-rw-r--r--kppp/DB/Provider/Belarus/Makefile.am6
-rw-r--r--kppp/DB/Provider/Czech_Republic/.directory52
-rw-r--r--kppp/DB/Provider/Czech_Republic/AICOM32
-rw-r--r--kppp/DB/Provider/Czech_Republic/ARsystem32
-rw-r--r--kppp/DB/Provider/Czech_Republic/ASYS32
-rw-r--r--kppp/DB/Provider/Czech_Republic/ApexNet32
-rw-r--r--kppp/DB/Provider/Czech_Republic/BohemiaNet32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Brailcom32
-rw-r--r--kppp/DB/Provider/Czech_Republic/CITYNET32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Contactel32
-rw-r--r--kppp/DB/Provider/Czech_Republic/ES-servis32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Econnect32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Falco_computer32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Fortech32
-rw-r--r--kppp/DB/Provider/Czech_Republic/HP-NET32
-rw-r--r--kppp/DB/Provider/Czech_Republic/INTERNET_OnLine32
-rw-r--r--kppp/DB/Provider/Czech_Republic/INTERNEXT32
-rw-r--r--kppp/DB/Provider/Czech_Republic/IQNET32
-rw-r--r--kppp/DB/Provider/Czech_Republic/KPNQuest32
-rw-r--r--kppp/DB/Provider/Czech_Republic/M-soft32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Makefile.am18
-rw-r--r--kppp/DB/Provider/Czech_Republic/Nextra32
-rw-r--r--kppp/DB/Provider/Czech_Republic/ProfiNet32
-rw-r--r--kppp/DB/Provider/Czech_Republic/SeverNET32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Video_OnLine32
-rw-r--r--kppp/DB/Provider/Czech_Republic/Volny26
-rw-r--r--kppp/DB/Provider/Denmark/.directory52
-rw-r--r--kppp/DB/Provider/Denmark/Get2Net24
-rw-r--r--kppp/DB/Provider/Denmark/Makefile.am5
-rw-r--r--kppp/DB/Provider/France/.directory48
-rw-r--r--kppp/DB/Provider/France/ClubInternetFull26
-rw-r--r--kppp/DB/Provider/France/Makefile.am5
-rw-r--r--kppp/DB/Provider/Germany/.directory54
-rw-r--r--kppp/DB/Provider/Germany/CityWeb23
-rw-r--r--kppp/DB/Provider/Germany/FH%032Rhein%032Sieg%032%040Informatik%04124
-rw-r--r--kppp/DB/Provider/Germany/MUC%046DE26
-rw-r--r--kppp/DB/Provider/Germany/Makefile.am6
-rw-r--r--kppp/DB/Provider/Germany/Netsurf24
-rw-r--r--kppp/DB/Provider/Ireland/.directory43
-rw-r--r--kppp/DB/Provider/Ireland/Eircom26
-rw-r--r--kppp/DB/Provider/Ireland/IOL26
-rw-r--r--kppp/DB/Provider/Ireland/Makefile.am5
-rw-r--r--kppp/DB/Provider/Makefile.am3
-rw-r--r--kppp/DB/Provider/Netherlands/.directory53
-rw-r--r--kppp/DB/Provider/Netherlands/12Move25
-rw-r--r--kppp/DB/Provider/Netherlands/Bart26
-rw-r--r--kppp/DB/Provider/Netherlands/Betuwenet%032BFree27
-rw-r--r--kppp/DB/Provider/Netherlands/Betuwenet%032BQuality26
-rw-r--r--kppp/DB/Provider/Netherlands/Cistron29
-rw-r--r--kppp/DB/Provider/Netherlands/Concepts%032ICT26
-rw-r--r--kppp/DB/Provider/Netherlands/Cubic%032Circle28
-rw-r--r--kppp/DB/Provider/Netherlands/Dataweb25
-rw-r--r--kppp/DB/Provider/Netherlands/Daxis%032Internet25
-rw-r--r--kppp/DB/Provider/Netherlands/Demon%032Internet27
-rw-r--r--kppp/DB/Provider/Netherlands/Energis-Ision27
-rw-r--r--kppp/DB/Provider/Netherlands/Euronet%032anytime26
-rw-r--r--kppp/DB/Provider/Netherlands/Euronet%032professional25
-rw-r--r--kppp/DB/Provider/Netherlands/FlakkeeNet27
-rw-r--r--kppp/DB/Provider/Netherlands/FreeAcces27
-rw-r--r--kppp/DB/Provider/Netherlands/Freeler%032basis26
-rw-r--r--kppp/DB/Provider/Netherlands/Freeler%032compleet27
-rw-r--r--kppp/DB/Provider/Netherlands/Freeler%032voordelig27
-rw-r--r--kppp/DB/Provider/Netherlands/HCC%032NET26
-rw-r--r--kppp/DB/Provider/Netherlands/Hacom27
-rw-r--r--kppp/DB/Provider/Netherlands/HetNet%032Basis%032Surfen24
-rw-r--r--kppp/DB/Provider/Netherlands/HetNet%032Frequent%032Surfen24
-rw-r--r--kppp/DB/Provider/Netherlands/HetNet%032Regelmatig%032Surfen24
-rw-r--r--kppp/DB/Provider/Netherlands/IAE25
-rw-r--r--kppp/DB/Provider/Netherlands/ILimburg27
-rw-r--r--kppp/DB/Provider/Netherlands/InterNLnet24
-rw-r--r--kppp/DB/Provider/Netherlands/Interbox27
-rw-r--r--kppp/DB/Provider/Netherlands/Internet%032Acces%032Facilities25
-rw-r--r--kppp/DB/Provider/Netherlands/Internet%032Online26
-rw-r--r--kppp/DB/Provider/Netherlands/Interstroom25
-rw-r--r--kppp/DB/Provider/Netherlands/IntroWeb%032Hengelo%032e.o.24
-rw-r--r--kppp/DB/Provider/Netherlands/IntroWeb%032met%0323-cijferig%032kengetal25
-rw-r--r--kppp/DB/Provider/Netherlands/IntroWeb%032met%0324-cijferig%032kengetal25
-rw-r--r--kppp/DB/Provider/Netherlands/Kabelfoon66
-rw-r--r--kppp/DB/Provider/Netherlands/KeyAcces%032met%0323%032cijferig%032kengetal26
-rw-r--r--kppp/DB/Provider/Netherlands/KeyAcces%032met%0324%032cijferig%032kengetal26
-rw-r--r--kppp/DB/Provider/Netherlands/Luna26
-rw-r--r--kppp/DB/Provider/Netherlands/Macom26
-rw-r--r--kppp/DB/Provider/Netherlands/Makefile.am67
-rw-r--r--kppp/DB/Provider/Netherlands/Nederland.net28
-rw-r--r--kppp/DB/Provider/Netherlands/Planet%032Internet%032Premium28
-rw-r--r--kppp/DB/Provider/Netherlands/Planet%032Internet%032Standaard28
-rw-r--r--kppp/DB/Provider/Netherlands/Plant%032Acces27
-rw-r--r--kppp/DB/Provider/Netherlands/Popin26
-rw-r--r--kppp/DB/Provider/Netherlands/PublishNet27
-rw-r--r--kppp/DB/Provider/Netherlands/Raketnet26
-rw-r--r--kppp/DB/Provider/Netherlands/Solcon26
-rw-r--r--kppp/DB/Provider/Netherlands/Support%032Net26
-rw-r--r--kppp/DB/Provider/Netherlands/Telebyte26
-rw-r--r--kppp/DB/Provider/Netherlands/Tiscali%032Compleet26
-rw-r--r--kppp/DB/Provider/Netherlands/Tiscali%032Gratis26
-rw-r--r--kppp/DB/Provider/Netherlands/UwNet25
-rw-r--r--kppp/DB/Provider/Netherlands/Via%032Networks27
-rw-r--r--kppp/DB/Provider/Netherlands/Wannadoo%032budget26
-rw-r--r--kppp/DB/Provider/Netherlands/Wannadoo%032budget%320plus26
-rw-r--r--kppp/DB/Provider/Netherlands/Wannadoo%032smartpack25
-rw-r--r--kppp/DB/Provider/Netherlands/Wirehub26
-rw-r--r--kppp/DB/Provider/Netherlands/XS4All26
-rw-r--r--kppp/DB/Provider/Netherlands/Zeelandnet27
-rw-r--r--kppp/DB/Provider/Netherlands/Zon%032226
-rw-r--r--kppp/DB/Provider/Netherlands/Zon%032Gratis26
-rw-r--r--kppp/DB/Provider/Netherlands/Zon%032Inclusief25
-rw-r--r--kppp/DB/Provider/New_Zealand/.directory50
-rw-r--r--kppp/DB/Provider/New_Zealand/Makefile.am5
-rw-r--r--kppp/DB/Provider/New_Zealand/OrconInternet25
-rw-r--r--kppp/DB/Provider/New_Zealand/Paradise23
-rw-r--r--kppp/DB/Provider/New_Zealand/Voyager23
-rw-r--r--kppp/DB/Provider/New_Zealand/XTRA23
-rw-r--r--kppp/DB/Provider/New_Zealand/ihug23
-rw-r--r--kppp/DB/Provider/Norway/.directory53
-rw-r--r--kppp/DB/Provider/Norway/BGNett24
-rw-r--r--kppp/DB/Provider/Norway/Institutt%032for%032informatikk22
-rw-r--r--kppp/DB/Provider/Norway/Makefile.am5
-rw-r--r--kppp/DB/Provider/Portugal/.directory34
-rw-r--r--kppp/DB/Provider/Portugal/Clix23
-rw-r--r--kppp/DB/Provider/Portugal/Makefile.am5
-rw-r--r--kppp/DB/Provider/Portugal/Netc23
-rw-r--r--kppp/DB/Provider/Portugal/OniNet23
-rw-r--r--kppp/DB/Provider/Slovenia/.directory45
-rw-r--r--kppp/DB/Provider/Slovenia/AmisNet27
-rw-r--r--kppp/DB/Provider/Slovenia/Arnes26
-rw-r--r--kppp/DB/Provider/Slovenia/Kiss26
-rw-r--r--kppp/DB/Provider/Slovenia/Makefile.am5
-rw-r--r--kppp/DB/Provider/Slovenia/MojNet27
-rw-r--r--kppp/DB/Provider/Slovenia/SiOL26
-rw-r--r--kppp/DB/Provider/Slovenia/Volja27
-rw-r--r--kppp/DB/Provider/Sweden/.directory51
-rw-r--r--kppp/DB/Provider/Sweden/Makefile.am5
-rw-r--r--kppp/DB/Provider/Sweden/Tiscali26
-rw-r--r--kppp/DB/Provider/Sweden/Utfors26
-rw-r--r--kppp/DB/Provider/Switzerland/.directory48
-rw-r--r--kppp/DB/Provider/Switzerland/Bluewin23
-rw-r--r--kppp/DB/Provider/Switzerland/Makefile.am5
-rw-r--r--kppp/DB/Provider/Taiwan/.directory36
-rw-r--r--kppp/DB/Provider/Taiwan/EraNet23
-rw-r--r--kppp/DB/Provider/Taiwan/HiNet23
-rw-r--r--kppp/DB/Provider/Taiwan/Makefile.am5
-rw-r--r--kppp/DB/Provider/Taiwan/SeedNet23
-rw-r--r--kppp/DB/Provider/Ukraine/.directory67
-rw-r--r--kppp/DB/Provider/Ukraine/Adamant24
-rw-r--r--kppp/DB/Provider/Ukraine/IPTelecom29
-rw-r--r--kppp/DB/Provider/Ukraine/Makefile.am5
-rw-r--r--kppp/DB/Provider/Ukraine/NuVse27
-rw-r--r--kppp/DB/Provider/United_Kingdom/.directory54
-rw-r--r--kppp/DB/Provider/United_Kingdom/Demon%032Green%032212066623
-rw-r--r--kppp/DB/Provider/United_Kingdom/Demon%032Purple%032212166623
-rw-r--r--kppp/DB/Provider/United_Kingdom/Demon%032Red%032079866623
-rw-r--r--kppp/DB/Provider/United_Kingdom/FreeServe24
-rw-r--r--kppp/DB/Provider/United_Kingdom/Makefile.am9
-rw-r--r--kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032ISDN24
-rw-r--r--kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032Modem24
-rw-r--r--kppp/DB/Provider/United_Kingdom/UKPOST%032ISDN24
-rw-r--r--kppp/DB/Provider/United_Kingdom/UKPOST%032Modem24
-rw-r--r--kppp/DB/Provider/United_Kingdom/UTV26
-rw-r--r--kppp/DB/Provider/Yugoslavia/.directory42
-rw-r--r--kppp/DB/Provider/Yugoslavia/041Net23
-rw-r--r--kppp/DB/Provider/Yugoslavia/BeoTelNet23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Bar.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Berane.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.BijeloPolje.CG.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Budva.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Cetinje.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.HercegNovi.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Kotor.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Niksic.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Pljevlja.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Podgorica.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Tivat.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.Ulcinj.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/CG.yu23
-rw-r--r--kppp/DB/Provider/Yugoslavia/DrenikNet23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnet@Full23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnet@Lite23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetBeograd23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetCacak23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetKragujevac23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetNis23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetNoviSad23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetPristina23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetS0
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetSombor23
-rw-r--r--kppp/DB/Provider/Yugoslavia/EUnetSubotica23
-rw-r--r--kppp/DB/Provider/Yugoslavia/InfoSKY23
-rw-r--r--kppp/DB/Provider/Yugoslavia/Makefile.am13
-rw-r--r--kppp/DB/Provider/Yugoslavia/PTT23
-rw-r--r--kppp/DB/Provider/Yugoslavia/SCnet23
-rw-r--r--kppp/DB/Provider/Yugoslavia/Sezampro23
-rw-r--r--kppp/DB/Provider/Yugoslavia/SuOnline24
-rw-r--r--kppp/DB/Provider/Yugoslavia/TippNet24
-rw-r--r--kppp/DB/Provider/Yugoslavia/VeratNet23
-rw-r--r--kppp/DB/Provider/Yugoslavia/YUBCnet23
-rw-r--r--kppp/HISTORY37
-rw-r--r--kppp/INSTALL46
-rw-r--r--kppp/KPPPIface.h39
-rw-r--r--kppp/Kppp.desktop70
-rw-r--r--kppp/Makefile.am153
-rw-r--r--kppp/NEWS28
-rw-r--r--kppp/README105
-rw-r--r--kppp/README.ModemDB77
-rw-r--r--kppp/Rules/Argentina/Argentina_0610.rst26
-rw-r--r--kppp/Rules/Argentina/Argentina_Local.rst26
-rw-r--r--kppp/Rules/Argentina/Makefile.am8
-rw-r--r--kppp/Rules/Australia/Local.rst18
-rw-r--r--kppp/Rules/Australia/Makefile.am10
-rw-r--r--kppp/Rules/Australia/Optus_Residential.rst132
-rw-r--r--kppp/Rules/Australia/STD_Zone_1_-_25-50_kms.rst30
-rw-r--r--kppp/Rules/Australia/STD_Zone_2_-_50-85_kms.rst30
-rw-r--r--kppp/Rules/Australia/STD_Zone_3_-_85-165_kms.rst30
-rw-r--r--kppp/Rules/Australia/STD_Zone_4_-_165+_kms.rst30
-rw-r--r--kppp/Rules/Austria/Business_1/Local.rst19
-rw-r--r--kppp/Rules/Austria/Business_1/Long_Distance.rst19
-rw-r--r--kppp/Rules/Austria/Business_1/Makefile.am7
-rw-r--r--kppp/Rules/Austria/Business_1/Online.rst30
-rw-r--r--kppp/Rules/Austria/Business_2/Local.rst19
-rw-r--r--kppp/Rules/Austria/Business_2/Long_Distance.rst19
-rw-r--r--kppp/Rules/Austria/Business_2/Makefile.am7
-rw-r--r--kppp/Rules/Austria/Business_2/Online.rst30
-rw-r--r--kppp/Rules/Austria/Makefile.am6
-rw-r--r--kppp/Rules/Austria/Minimum/Local.rst19
-rw-r--r--kppp/Rules/Austria/Minimum/Long_Distance.rst19
-rw-r--r--kppp/Rules/Austria/Minimum/Makefile.am7
-rw-r--r--kppp/Rules/Austria/Minimum/Online.rst30
-rw-r--r--kppp/Rules/Austria/Standard/Local.rst19
-rw-r--r--kppp/Rules/Austria/Standard/Long_Distance.rst19
-rw-r--r--kppp/Rules/Austria/Standard/Makefile.am8
-rw-r--r--kppp/Rules/Austria/Standard/Online.rst30
-rw-r--r--kppp/Rules/Austria/Standard/UTA_easyinternet.rst50
-rw-r--r--kppp/Rules/Bangladesh/ATT00007.rst63
-rw-r--r--kppp/Rules/Bangladesh/ATT00010.rst59
-rw-r--r--kppp/Rules/Bangladesh/Makefile.am6
-rw-r--r--kppp/Rules/Belgium/Belgium_internet_euro.rst83
-rw-r--r--kppp/Rules/Belgium/Belgium_internet_frank.rst74
-rw-r--r--kppp/Rules/Belgium/Belgium_interzonal.rst81
-rw-r--r--kppp/Rules/Belgium/Belgium_zonal.rst81
-rw-r--r--kppp/Rules/Belgium/Makefile.am6
-rw-r--r--kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_home_-_bez_impulsa.rst67
-rw-r--r--kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_student_-_bez_impulsa.rst67
-rw-r--r--kppp/Rules/Bosnia_and_Herzegovina/Makefile.am8
-rw-r--r--kppp/Rules/Bosnia_and_Herzegovina/SmartNet_PERSONAL_bez_impulsa.rst65
-rw-r--r--kppp/Rules/Bosnia_and_Herzegovina/samo_impulsi.rst66
-rw-r--r--kppp/Rules/Brasil/Brasil.rst21
-rw-r--r--kppp/Rules/Brasil/Brasil_Ligbr.rst19
-rw-r--r--kppp/Rules/Brasil/Makefile.am8
-rw-r--r--kppp/Rules/Brasil/Rio_de_Janeiro.rst33
-rw-r--r--kppp/Rules/Brasil/SaoPaulo.rst33
-rw-r--r--kppp/Rules/Croatia/CARNet.rst67
-rw-r--r--kppp/Rules/Croatia/Makefile.am5
-rw-r--r--kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Business_Internet.rst23
-rw-r--r--kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Home_Internet.rst23
-rw-r--r--kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_180_+_660.rst23
-rw-r--r--kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_90_+_300.rst23
-rw-r--r--kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_universal.rst23
-rw-r--r--kppp/Rules/Czechia/Makefile.am9
-rw-r--r--kppp/Rules/Denmark/12Move_Analog.rst51
-rw-r--r--kppp/Rules/Denmark/12Move_ISDN.rst52
-rw-r--r--kppp/Rules/Denmark/Cybercity_Friabonnement.rst42
-rw-r--r--kppp/Rules/Denmark/Get2net_Betaling.rst56
-rw-r--r--kppp/Rules/Denmark/Get2net_Gratis.rst48
-rw-r--r--kppp/Rules/Denmark/Makefile.am10
-rw-r--r--kppp/Rules/Denmark/Mobilix_Wanadoo.rst62
-rw-r--r--kppp/Rules/Denmark/Teledanmark_Basis.rst42
-rw-r--r--kppp/Rules/Denmark/Teledanmark_Favoritinternet.rst85
-rw-r--r--kppp/Rules/Denmark/Worldonline-Analog.rst59
-rw-r--r--kppp/Rules/Denmark/Worldonline-ISDN.rst60
-rw-r--r--kppp/Rules/Estonia/Eesti_Telefon.rst58
-rw-r--r--kppp/Rules/Estonia/Makefile.am5
-rw-r--r--kppp/Rules/Finland/Makefile.am5
-rw-r--r--kppp/Rules/Finland/VLP.rst64
-rw-r--r--kppp/Rules/France/Cegetel_Local.rst44
-rw-r--r--kppp/Rules/France/Cegetel_National.rst44
-rw-r--r--kppp/Rules/France/France_Telecom_Internet.rst59
-rw-r--r--kppp/Rules/France/France_Telecom_Local.rst70
-rw-r--r--kppp/Rules/France/France_Telecom_National.rst70
-rw-r--r--kppp/Rules/France/Le_9_Local.rst44
-rw-r--r--kppp/Rules/France/Le_9_National.rst44
-rw-r--r--kppp/Rules/France/Makefile.am7
-rw-r--r--kppp/Rules/France/OneTel.rst44
-rw-r--r--kppp/Rules/France/Tele2_Local.rst44
-rw-r--r--kppp/Rules/France/Tele2_National.rst44
-rw-r--r--kppp/Rules/France/Wanadoo_Free.rst35
-rw-r--r--kppp/Rules/Germany/1und1_InternetZugang.rst43
-rw-r--r--kppp/Rules/Germany/2.5min.rst60
-rw-r--r--kppp/Rules/Germany/2.5s.rst60
-rw-r--r--kppp/Rules/Germany/AddCom_by_Call.rst50
-rw-r--r--kppp/Rules/Germany/Addcom.rst23
-rw-r--r--kppp/Rules/Germany/Arcor_Internet_by_Call_easy.rst52
-rw-r--r--kppp/Rules/Germany/CallOkaynet.rst39
-rw-r--r--kppp/Rules/Germany/Callino_Surf_Basic.rst51
-rw-r--r--kppp/Rules/Germany/Callino_Surf_Plus.rst33
-rw-r--r--kppp/Rules/Germany/Callisa_City.rst56
-rw-r--r--kppp/Rules/Germany/City_Activ_Plus_Option.rst21
-rw-r--r--kppp/Rules/Germany/Cityweb.rst25
-rw-r--r--kppp/Rules/Germany/CompuservePro.rst16
-rw-r--r--kppp/Rules/Germany/E-Plus-Online_Jedermann.rst60
-rw-r--r--kppp/Rules/Germany/Easynet_easy-call.rst36
-rw-r--r--kppp/Rules/Germany/Freenet_Enterprise.rst28
-rw-r--r--kppp/Rules/Germany/Freenet_Sorglos.rst33
-rw-r--r--kppp/Rules/Germany/Freenet_StandardTarif.rst32
-rw-r--r--kppp/Rules/Germany/Freenet_Super_CbC.rst40
-rw-r--r--kppp/Rules/Germany/Freenet_special_call_by_call.rst57
-rw-r--r--kppp/Rules/Germany/MSN.rst18
-rw-r--r--kppp/Rules/Germany/Makefile.am34
-rw-r--r--kppp/Rules/Germany/Mobilcom_Freenet.rst44
-rw-r--r--kppp/Rules/Germany/NGI_Call_By_Call.rst14
-rw-r--r--kppp/Rules/Germany/Netcom_Kassel.rst20
-rw-r--r--kppp/Rules/Germany/Nikoma.rst34
-rw-r--r--kppp/Rules/Germany/Nikoma_Internet_by_Call.rst35
-rw-r--r--kppp/Rules/Germany/Nikoma_Study_and_Surf.rst41
-rw-r--r--kppp/Rules/Germany/Planet-Interkom_Internet_by_call.rst23
-rw-r--r--kppp/Rules/Germany/Puretec.rst13
-rw-r--r--kppp/Rules/Germany/Telekom_City_Select_5_30.rst48
-rw-r--r--kppp/Rules/Germany/VR-Web.rst33
-rw-r--r--kppp/Rules/Germany/expressnet.rst20
-rw-r--r--kppp/Rules/Germany/knUUt-by-Call.rst22
-rw-r--r--kppp/Rules/Germany/talkline_by_call.rst19
-rw-r--r--kppp/Rules/Germany/vossnet_fun.rst19
-rw-r--r--kppp/Rules/Germany/vossnet_fun_light.rst19
-rw-r--r--kppp/Rules/Germany/vossnet_kompl.rst29
-rw-r--r--kppp/Rules/Greece/Hellas_EPAK_Zone1_in_euro.rst56
-rw-r--r--kppp/Rules/Greece/Hellas_EPAK_Zone2_in_euro.rst51
-rw-r--r--kppp/Rules/Greece/Hellas_EPAK_local_in_euro.rst64
-rw-r--r--kppp/Rules/Greece/Hellas_analog_local_in_euro.rst45
-rw-r--r--kppp/Rules/Greece/Hellas_digital_local_in_euro.rst43
-rw-r--r--kppp/Rules/Greece/Makefile.am9
-rw-r--r--kppp/Rules/HongKong/Hong_Kong_Telecom.rst18
-rw-r--r--kppp/Rules/HongKong/Makefile.am5
-rw-r--r--kppp/Rules/Hungary/Local.rst42
-rw-r--r--kppp/Rules/Hungary/LocalTop_MATAV.rst67
-rw-r--r--kppp/Rules/Hungary/Local_MATAV.rst35
-rw-r--r--kppp/Rules/Hungary/Makefile.am8
-rw-r--r--kppp/Rules/Hungary/PapaTel.rst51
-rw-r--r--kppp/Rules/Iceland/Iceland_general.rst110
-rw-r--r--kppp/Rules/Iceland/Makefile.am5
-rw-r--r--kppp/Rules/India/BSNL_Local.rst113
-rw-r--r--kppp/Rules/India/BSNL_Long_101_To_200.rst86
-rw-r--r--kppp/Rules/India/BSNL_Medium_51_To_100.rst86
-rw-r--r--kppp/Rules/India/Makefile.am7
-rw-r--r--kppp/Rules/India/Vsnl_local.rst102
-rw-r--r--kppp/Rules/Indonesia/Lokal_1_Metropolitan.rst60
-rw-r--r--kppp/Rules/Indonesia/Lokal_2_Metropolitan.rst60
-rw-r--r--kppp/Rules/Indonesia/Lokal_non_metropolitan.rst60
-rw-r--r--kppp/Rules/Indonesia/Makefile.am7
-rw-r--r--kppp/Rules/Ireland/Eircom_Internet.rst164
-rw-r--r--kppp/Rules/Ireland/Eircom_Local.rst164
-rw-r--r--kppp/Rules/Ireland/Eircom_National.rst173
-rw-r--r--kppp/Rules/Ireland/Eircom_Special.rst173
-rw-r--r--kppp/Rules/Ireland/Makefile.am8
-rw-r--r--kppp/Rules/Israel/Bezeq_Interurban.rst76
-rw-r--r--kppp/Rules/Israel/Bezeq_Local.rst76
-rw-r--r--kppp/Rules/Israel/Makefile.am6
-rw-r--r--kppp/Rules/Italy/Atlanet.rst59
-rw-r--r--kppp/Rules/Italy/Cheapnet.rst79
-rw-r--r--kppp/Rules/Italy/Infostrada_Internet_SpZero.rst44
-rw-r--r--kppp/Rules/Italy/Infostrada_Libero1055_Base.rst68
-rw-r--r--kppp/Rules/Italy/Infostrada_Libero1055_SpZero.rst73
-rw-r--r--kppp/Rules/Italy/Infostrada_Loc_Reg_Naz_SpZero.rst44
-rw-r--r--kppp/Rules/Italy/Infostrada_Locali_Base.rst65
-rw-r--r--kppp/Rules/Italy/Infostrada_Reg_Naz_Base.rst66
-rw-r--r--kppp/Rules/Italy/Makefile.am34
-rw-r--r--kppp/Rules/Italy/Tele2_Altri_ISP.rst64
-rw-r--r--kppp/Rules/Italy/Tele2_Internet_Tele2.rst64
-rw-r--r--kppp/Rules/Italy/Telecom_Interurbane_Fino15Km.rst61
-rw-r--r--kppp/Rules/Italy/Telecom_Interurbane_Oltre15Km.rst61
-rw-r--r--kppp/Rules/Italy/Telecom_Locali.rst79
-rw-r--r--kppp/Rules/Italy/Teleconomy24_Internet.rst40
-rw-r--r--kppp/Rules/Italy/Teleconomy24_Nazionali.rst39
-rw-r--r--kppp/Rules/Italy/Teleconomy_NoStop_Internet.rst48
-rw-r--r--kppp/Rules/Italy/Tiscali_Urbane.rst63
-rw-r--r--kppp/Rules/Italy/Wind_24ore_Internet_AltriISP.rst38
-rw-r--r--kppp/Rules/Italy/Wind_24ore_Internet_InWind.rst40
-rw-r--r--kppp/Rules/Italy/Wind_24ore_Interurbane.rst40
-rw-r--r--kppp/Rules/Italy/Wind_24ore_Urbane.rst40
-rw-r--r--kppp/Rules/Italy/Wind_Family+SuperLight_Internet_InWind.rst64
-rw-r--r--kppp/Rules/Italy/Wind_Family+SuperLight_Urbane_Interurbane.rst64
-rw-r--r--kppp/Rules/Italy/Wind_Family_Internet_AltriISP.rst60
-rw-r--r--kppp/Rules/Italy/Wind_Family_Internet_InWind.rst64
-rw-r--r--kppp/Rules/Italy/Wind_Family_Interurbane.rst64
-rw-r--r--kppp/Rules/Italy/Wind_Family_Urbane.rst64
-rw-r--r--kppp/Rules/Italy/Wind_Flat_Internet_AltriISP.rst38
-rw-r--r--kppp/Rules/Italy/Wind_Flat_Internet_InWind.rst40
-rw-r--r--kppp/Rules/Italy/Wind_Urbana_1088_Light.rst53
-rw-r--r--kppp/Rules/Jamaica/CWJ_InterParish.rst120
-rw-r--r--kppp/Rules/Jamaica/CWJ_Local.rst118
-rw-r--r--kppp/Rules/Jamaica/Makefile.am6
-rw-r--r--kppp/Rules/Japan/Makefile.am5
-rw-r--r--kppp/Rules/Japan/NTT_Local.rst58
-rw-r--r--kppp/Rules/Kazakhstan/Akparat_Sprint.rst95
-rw-r--r--kppp/Rules/Kazakhstan/Makefile.am5
-rw-r--r--kppp/Rules/Luxembourg/CMD_InternetGratuit.rst71
-rw-r--r--kppp/Rules/Luxembourg/LuxembourgOnline_FreeInternet.rst72
-rw-r--r--kppp/Rules/Luxembourg/Makefile.am6
-rw-r--r--kppp/Rules/Luxembourg/PetT_ClassicSurf.rst70
-rw-r--r--kppp/Rules/Luxembourg/PetT_KioskSurf.rst70
-rw-r--r--kppp/Rules/Macedonia/Macedonia_GenericISP_interurban.rst30
-rw-r--r--kppp/Rules/Macedonia/Macedonia_GenericISP_local.rst30
-rw-r--r--kppp/Rules/Macedonia/Macedonia_MTnet.rst33
-rw-r--r--kppp/Rules/Macedonia/Makefile.am7
-rw-r--r--kppp/Rules/Makefile.am9
-rw-r--r--kppp/Rules/Malaysia/Makefile.am5
-rw-r--r--kppp/Rules/Malaysia/TMNet_Jaring.rst33
-rw-r--r--kppp/Rules/Malaysia/malaysia.rst38
-rw-r--r--kppp/Rules/Netherlands/12Move.rst44
-rw-r--r--kppp/Rules/Netherlands/BelBasis_Buiten_Regio.rst31
-rw-r--r--kppp/Rules/Netherlands/BelBasis_Buiten_Regio_Nummervoordeel.rst30
-rw-r--r--kppp/Rules/Netherlands/BelBasis_Regio.rst35
-rw-r--r--kppp/Rules/Netherlands/BelBasis_Regio_Nummervoordeel.rst35
-rw-r--r--kppp/Rules/Netherlands/BelBudget_Buiten_Regio.rst30
-rw-r--r--kppp/Rules/Netherlands/BelBudget_Regio.rst36
-rw-r--r--kppp/Rules/Netherlands/BelPlus_Buiten_Regio.rst32
-rw-r--r--kppp/Rules/Netherlands/BelPlus_Buiten_Regio_Nummervoordeel.rst31
-rw-r--r--kppp/Rules/Netherlands/BelPlus_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/BelPlus_Regio_Nummervoordeel.rst37
-rw-r--r--kppp/Rules/Netherlands/Cistron36
-rw-r--r--kppp/Rules/Netherlands/Freeler_Basis.rst40
-rw-r--r--kppp/Rules/Netherlands/Freeler_Voordelig.rst40
-rw-r--r--kppp/Rules/Netherlands/HetNet_Regelmatig_Surfen.rst40
-rw-r--r--kppp/Rules/Netherlands/InterNLnet.rst43
-rw-r--r--kppp/Rules/Netherlands/Makefile.am34
-rw-r--r--kppp/Rules/Netherlands/OneTel_Spaarstand_Buiten_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/OneTel_Spaarstand_Regio.rst42
-rw-r--r--kppp/Rules/Netherlands/OneTel_Toets_1658_Buiten_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/OneTel_Toets_1658_Regio.rst42
-rw-r--r--kppp/Rules/Netherlands/Planet_Internet.rst41
-rw-r--r--kppp/Rules/Netherlands/Priority_Telecom_Nationaal.rst37
-rw-r--r--kppp/Rules/Netherlands/Priority_Telecom_Regionaal.rst32
-rw-r--r--kppp/Rules/Netherlands/Tele2_Extra_Buiten_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/Tele2_Extra_Regio.rst42
-rw-r--r--kppp/Rules/Netherlands/Tele2_Preselect_Buiten_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/Tele2_Preselect_Regio.rst42
-rw-r--r--kppp/Rules/Netherlands/Tele2_Toets_1609_Buiten_Regio.rst37
-rw-r--r--kppp/Rules/Netherlands/Tele2_Toets_1609_Regio.rst42
-rw-r--r--kppp/Rules/Netherlands/Wannadoo_Budget_Plus41
-rw-r--r--kppp/Rules/Norway/Local_Area.rst16
-rw-r--r--kppp/Rules/Norway/Long_Distance.rst16
-rw-r--r--kppp/Rules/Norway/Makefile.am7
-rw-r--r--kppp/Rules/Norway/Netcom.rst15
-rw-r--r--kppp/Rules/Poland/Internetia.rst19
-rw-r--r--kppp/Rules/Poland/Makefile.am12
-rw-r--r--kppp/Rules/Poland/Netia_100km_Niebieska.rst40
-rw-r--r--kppp/Rules/Poland/Netia_100km_Zielona.rst39
-rw-r--r--kppp/Rules/Poland/Netia_Lokalne_Niebieska.rst20
-rw-r--r--kppp/Rules/Poland/Netia_Lokalne_Zielona.rst21
-rw-r--r--kppp/Rules/Poland/TPSA.rst22
-rw-r--r--kppp/Rules/Poland/TP_100km.rst39
-rw-r--r--kppp/Rules/Poland/TP_Lokalne.rst19
-rw-r--r--kppp/Rules/Portugal/Makefile.am6
-rw-r--r--kppp/Rules/Portugal/PT_Local.rst54
-rw-r--r--kppp/Rules/Portugal/PT_Local_Ilhas.rst54
-rw-r--r--kppp/Rules/Portugal/PT_Regional.rst47
-rw-r--r--kppp/Rules/Portugal/PT_Regional_Ilhas.rst47
-rw-r--r--kppp/Rules/Portugal/PT_YesNET.rst49
-rw-r--r--kppp/Rules/Portugal/PT_YesNET_Ilhas.rst49
-rw-r--r--kppp/Rules/Romania/ClickNet.rst33
-rw-r--r--kppp/Rules/Romania/Easynet.rst20
-rw-r--r--kppp/Rules/Romania/Makefile.am9
-rw-r--r--kppp/Rules/Romania/RomTelecom_GSM.rst23
-rw-r--r--kppp/Rules/Romania/Romtelecom_Acces_Special_Internet.rst37
-rw-r--r--kppp/Rules/Romania/Romtelecom_Interjudetean.rst31
-rw-r--r--kppp/Rules/Romania/Romtelecom_Local.rst36
-rw-r--r--kppp/Rules/Romania/Zapp-Mobile.rst19
-rw-r--r--kppp/Rules/Russia/Makefile.am5
-rw-r--r--kppp/Rules/Russia/TEMPLATE.ru163
-rw-r--r--kppp/Rules/Russia/mtu-intel_standart.rst51
-rw-r--r--kppp/Rules/Singapore/Makefile.am5
-rw-r--r--kppp/Rules/Singapore/SingTel_Local.rst59
-rw-r--r--kppp/Rules/Slovakia/Internetovy_tarif_019XY.rst28
-rw-r--r--kppp/Rules/Slovakia/Makefile.am7
-rw-r--r--kppp/Rules/Slovakia/ST_medzimesto.rst25
-rw-r--r--kppp/Rules/Slovakia/ST_mesto.rst25
-rw-r--r--kppp/Rules/Slovenia/Makefile.am7
-rw-r--r--kppp/Rules/Slovenia/omrezje_0880.rst67
-rw-r--r--kppp/Rules/Slovenia/omrezje_0889.rst58
-rw-r--r--kppp/Rules/Slovenia/stacionarno_omrezje.rst53
-rw-r--r--kppp/Rules/SouthAfrica/Makefile.am5
-rw-r--r--kppp/Rules/SouthAfrica/South_Africa_local.rst73
-rw-r--r--kppp/Rules/SouthAfrica/South_Africa_long_distance.rst73
-rw-r--r--kppp/Rules/SouthAfrika/Makefile.am5
-rw-r--r--kppp/Rules/SouthAfrika/South_Afrika.rst328
-rw-r--r--kppp/Rules/SouthAfrika/South_Afrika_Justin.rst123
-rw-r--r--kppp/Rules/Spain/Infovia.rst44
-rw-r--r--kppp/Rules/Spain/Infovia_IVA.rst44
-rw-r--r--kppp/Rules/Spain/Makefile.am9
-rw-r--r--kppp/Rules/Spain/Telefonica_Interprovincial.rst43
-rw-r--r--kppp/Rules/Spain/Telefonica_Interprovincial_IVA.rst43
-rw-r--r--kppp/Rules/Spain/Telefonica_Local.rst44
-rw-r--r--kppp/Rules/Spain/Telefonica_Metropolitana.rst44
-rw-r--r--kppp/Rules/Spain/Telefonica_Metropolitana_IVA.rst44
-rw-r--r--kppp/Rules/Spain/Telefonica_Nacional.rst43
-rw-r--r--kppp/Rules/Spain/Telefonica_Provincial.rst43
-rw-r--r--kppp/Rules/Spain/Telefonica_Provincial_IVA.rst43
-rw-r--r--kppp/Rules/Sweden/ACN.rst66
-rw-r--r--kppp/Rules/Sweden/Abonnera_com.rst69
-rw-r--r--kppp/Rules/Sweden/CNEAB-Route66.rst65
-rw-r--r--kppp/Rules/Sweden/Crossnet-Affinity.rst66
-rw-r--r--kppp/Rules/Sweden/Glocalnet.rst66
-rw-r--r--kppp/Rules/Sweden/Gts.rst65
-rw-r--r--kppp/Rules/Sweden/Home_se.rst65
-rw-r--r--kppp/Rules/Sweden/Makefile.am12
-rw-r--r--kppp/Rules/Sweden/Nemtel.rst66
-rw-r--r--kppp/Rules/Sweden/Plusenergi.rst65
-rw-r--r--kppp/Rules/Sweden/RSLCom.rst66
-rw-r--r--kppp/Rules/Sweden/Rix_Telecom.rst66
-rw-r--r--kppp/Rules/Sweden/Supertel.rst66
-rw-r--r--kppp/Rules/Sweden/Svensk_Telekom.rst66
-rw-r--r--kppp/Rules/Sweden/Tele1_Europe.rst66
-rw-r--r--kppp/Rules/Sweden/Tele2.rst66
-rw-r--r--kppp/Rules/Sweden/Tele8.rst66
-rw-r--r--kppp/Rules/Sweden/Teleman.rst66
-rw-r--r--kppp/Rules/Sweden/Telenordia.rst66
-rw-r--r--kppp/Rules/Sweden/Telerian.rst66
-rw-r--r--kppp/Rules/Sweden/Telia.rst68
-rw-r--r--kppp/Rules/Sweden/Telia_Telebonus1.rst66
-rw-r--r--kppp/Rules/Sweden/Telia_Telebonus2.rst66
-rw-r--r--kppp/Rules/Sweden/Telitel.rst66
-rw-r--r--kppp/Rules/Sweden/Tiscali.rst65
-rw-r--r--kppp/Rules/Sweden/Transnet.rst65
-rw-r--r--kppp/Rules/Sweden/Universal_Telecom.rst66
-rw-r--r--kppp/Rules/Sweden/Utfors.rst66
-rw-r--r--kppp/Rules/Sweden/Vattenfall.rst66
-rw-r--r--kppp/Rules/Switzerland/Makefile.am10
-rw-r--r--kppp/Rules/Switzerland/Sunrise_Freetime.rst43
-rw-r--r--kppp/Rules/Switzerland/Sunrise_Local.rst51
-rw-r--r--kppp/Rules/Switzerland/Sunrise_Select_Internet.rst49
-rw-r--r--kppp/Rules/Switzerland/Swisscom_Local.rst38
-rw-r--r--kppp/Rules/Switzerland/Swisscom_Remote.rst43
-rw-r--r--kppp/Rules/Switzerland/Swisscom_Surf.rst35
-rw-r--r--kppp/Rules/TEMPLATE147
-rw-r--r--kppp/Rules/Turkey/Makefile.am6
-rw-r--r--kppp/Rules/Turkey/Turk_Telekom_Internet.rst74
-rw-r--r--kppp/Rules/Ukraine/IPTelecom_hourly.rst86
-rw-r--r--kppp/Rules/Ukraine/Makefile.am5
-rw-r--r--kppp/Rules/Ukraine/NuVse_hourly.rst91
-rw-r--r--kppp/Rules/Ukraine/TEMPLATE.uk149
-rw-r--r--kppp/Rules/Ukraine/Utel_Unet.rst23
-rw-r--r--kppp/Rules/United_Kingdom/BirminghamCable_Local.rst106
-rw-r--r--kppp/Rules/United_Kingdom/BirminghamCable_National.rst111
-rw-r--r--kppp/Rules/United_Kingdom/BirminghamCable_SameTelco.rst111
-rw-r--r--kppp/Rules/United_Kingdom/BritishTelecom_Local.rst110
-rw-r--r--kppp/Rules/United_Kingdom/BritishTelecom_National.rst109
-rw-r--r--kppp/Rules/United_Kingdom/BritishTelecom_Regional.rst109
-rw-r--r--kppp/Rules/United_Kingdom/British_OneTel.rst83
-rw-r--r--kppp/Rules/United_Kingdom/Connaught_Telecom.rst100
-rw-r--r--kppp/Rules/United_Kingdom/Makefile.am12
-rw-r--r--kppp/Rules/Uruguay/Adinet_cIVA.rst40
-rw-r--r--kppp/Rules/Uruguay/Makefile.am5
-rw-r--r--kppp/Rules/Yugoslavia/041_1xx_xxx.rst64
-rw-r--r--kppp/Rules/Yugoslavia/041_2xx_xxx.rst63
-rw-r--r--kppp/Rules/Yugoslavia/041_3xx_xxx.rst63
-rw-r--r--kppp/Rules/Yugoslavia/041_4xx_xxx.rst63
-rw-r--r--kppp/Rules/Yugoslavia/041_5xx_xxx.rst63
-rw-r--r--kppp/Rules/Yugoslavia/041_9xx_xxx.rst63
-rw-r--r--kppp/Rules/Yugoslavia/III_zona-preko_200km.rst65
-rw-r--r--kppp/Rules/Yugoslavia/II_zona-do_200km.rst65
-rw-r--r--kppp/Rules/Yugoslavia/I_zona-ista_mreza.rst65
-rw-r--r--kppp/Rules/Yugoslavia/Lokalni_poziv.rst80
-rw-r--r--kppp/Rules/Yugoslavia/Makefile.am8
-rwxr-xr-xkppp/Rules/checkrules24
-rw-r--r--kppp/TODO25
-rw-r--r--kppp/accounting.cpp477
-rw-r--r--kppp/accounting.h144
-rw-r--r--kppp/accounts.cpp484
-rw-r--r--kppp/accounts.h109
-rw-r--r--kppp/acctselect.cpp330
-rw-r--r--kppp/acctselect.h81
-rw-r--r--kppp/auth.h36
-rw-r--r--kppp/configure.in.in53
-rw-r--r--kppp/connect.cpp1535
-rw-r--r--kppp/connect.h157
-rw-r--r--kppp/convlog.tcl64
-rw-r--r--kppp/conwindow.cpp340
-rw-r--r--kppp/conwindow.h96
-rw-r--r--kppp/debug.cpp140
-rw-r--r--kppp/debug.h70
-rw-r--r--kppp/devices.h98
-rw-r--r--kppp/docking.cpp148
-rw-r--r--kppp/docking.h70
-rw-r--r--kppp/edit.cpp1239
-rw-r--r--kppp/edit.h256
-rw-r--r--kppp/general.cpp684
-rw-r--r--kppp/general.h137
-rw-r--r--kppp/icons/Makefile.am1
-rw-r--r--kppp/icons/hi128-app-kppp.pngbin0 -> 15252 bytes
-rw-r--r--kppp/icons/hi16-app-kppp.pngbin0 -> 814 bytes
-rw-r--r--kppp/icons/hi22-app-kppp.pngbin0 -> 1347 bytes
-rw-r--r--kppp/icons/hi32-app-kppp.pngbin0 -> 2268 bytes
-rw-r--r--kppp/icons/hi48-app-kppp.pngbin0 -> 4156 bytes
-rw-r--r--kppp/icons/hi64-app-kppp.pngbin0 -> 6229 bytes
-rw-r--r--kppp/iplined.cpp42
-rw-r--r--kppp/iplined.h40
-rw-r--r--kppp/kpppconfig.h139
-rw-r--r--kppp/kpppwidget.cpp1073
-rw-r--r--kppp/kpppwidget.h174
-rw-r--r--kppp/loginterm.cpp168
-rw-r--r--kppp/loginterm.h50
-rw-r--r--kppp/logview/Makefile.am45
-rw-r--r--kppp/logview/export.cpp276
-rw-r--r--kppp/logview/export.h201
-rw-r--r--kppp/logview/kppplogview.desktop77
-rw-r--r--kppp/logview/log.cpp129
-rw-r--r--kppp/logview/log.h40
-rw-r--r--kppp/logview/loginfo.cpp197
-rw-r--r--kppp/logview/loginfo.h63
-rw-r--r--kppp/logview/main.cpp127
-rw-r--r--kppp/logview/main.h45
-rw-r--r--kppp/logview/monthly.cpp729
-rw-r--r--kppp/logview/monthly.h80
-rw-r--r--kppp/macros.h15
-rw-r--r--kppp/main.cpp453
-rw-r--r--kppp/main.h46
-rw-r--r--kppp/maintainer.h7
-rw-r--r--kppp/miniterm.cpp281
-rw-r--r--kppp/miniterm.h103
-rw-r--r--kppp/modem.cpp619
-rw-r--r--kppp/modem.h94
-rw-r--r--kppp/modemcmds.cpp384
-rw-r--r--kppp/modemcmds.h131
-rw-r--r--kppp/modemdb.cpp244
-rw-r--r--kppp/modemdb.h85
-rw-r--r--kppp/modeminfo.cpp292
-rw-r--r--kppp/modeminfo.h88
-rw-r--r--kppp/modems.cpp277
-rw-r--r--kppp/modems.h92
-rw-r--r--kppp/newwidget.cpp17
-rw-r--r--kppp/newwidget.h19
-rw-r--r--kppp/opener.cpp722
-rw-r--r--kppp/opener.h147
-rw-r--r--kppp/pixmaps/Makefile.am10
-rw-r--r--kppp/pixmaps/dock_both.pngbin0 -> 1034 bytes
-rw-r--r--kppp/pixmaps/dock_left.pngbin0 -> 1037 bytes
-rw-r--r--kppp/pixmaps/dock_none.pngbin0 -> 1000 bytes
-rw-r--r--kppp/pixmaps/dock_right.pngbin0 -> 1034 bytes
-rw-r--r--kppp/pixmaps/folder.pngbin0 -> 2403 bytes
-rw-r--r--kppp/pixmaps/kppplogo.pngbin0 -> 696 bytes
-rw-r--r--kppp/pixmaps/modemboth.pngbin0 -> 1180 bytes
-rw-r--r--kppp/pixmaps/modemleft.pngbin0 -> 1203 bytes
-rw-r--r--kppp/pixmaps/modemnone.pngbin0 -> 1195 bytes
-rw-r--r--kppp/pixmaps/modemright.pngbin0 -> 1185 bytes
-rw-r--r--kppp/pixmaps/phone.pngbin0 -> 5519 bytes
-rw-r--r--kppp/pppdargs.cpp171
-rw-r--r--kppp/pppdargs.h79
-rw-r--r--kppp/pppdata.cpp1486
-rw-r--r--kppp/pppdata.h513
-rw-r--r--kppp/ppplog.cpp285
-rw-r--r--kppp/ppplog.h40
-rw-r--r--kppp/pppstatdlg.cpp428
-rw-r--r--kppp/pppstatdlg.h131
-rw-r--r--kppp/pppstats.cpp343
-rw-r--r--kppp/pppstats.h84
-rw-r--r--kppp/providerdb.cpp473
-rw-r--r--kppp/providerdb.h152
-rw-r--r--kppp/pwentry.cpp115
-rw-r--r--kppp/pwentry.h72
-rw-r--r--kppp/requester.cpp365
-rw-r--r--kppp/requester.h38
-rw-r--r--kppp/ruleset.cpp581
-rw-r--r--kppp/ruleset.h146
-rw-r--r--kppp/runtests.cpp278
-rw-r--r--kppp/runtests.h47
-rw-r--r--kppp/scriptedit.cpp174
-rw-r--r--kppp/scriptedit.h79
-rw-r--r--kppp/utils.cpp66
-rw-r--r--kppp/utils.h44
-rw-r--r--kppp/version.h3
-rw-r--r--krdc/Makefile.am42
-rw-r--r--krdc/README18
-rw-r--r--krdc/TODO17
-rw-r--r--krdc/_rfb._tcp4
-rw-r--r--krdc/close.pngbin0 -> 963 bytes
-rw-r--r--krdc/configure.in.in14
-rw-r--r--krdc/cr32-app-krdc.pngbin0 -> 2049 bytes
-rw-r--r--krdc/cr48-app-krdc.pngbin0 -> 4335 bytes
-rw-r--r--krdc/events.h204
-rw-r--r--krdc/hostpreferences.cpp192
-rw-r--r--krdc/hostpreferences.h85
-rw-r--r--krdc/hostprofiles.ui168
-rw-r--r--krdc/hostprofiles.ui.h100
-rw-r--r--krdc/iconify.pngbin0 -> 277 bytes
-rw-r--r--krdc/keycapturedialog.cpp143
-rw-r--r--krdc/keycapturedialog.h52
-rw-r--r--krdc/keycapturewidget.ui105
-rw-r--r--krdc/kfullscreenpanel.cpp115
-rw-r--r--krdc/kfullscreenpanel.h78
-rw-r--r--krdc/krdc.cpp871
-rw-r--r--krdc/krdc.desktop63
-rw-r--r--krdc/krdc.h175
-rw-r--r--krdc/kremoteview.cpp89
-rw-r--r--krdc/kremoteview.h289
-rw-r--r--krdc/kservicelocator.cpp760
-rw-r--r--krdc/kservicelocator.h309
-rw-r--r--krdc/main.cpp224
-rw-r--r--krdc/main.h60
-rw-r--r--krdc/maindialog.cpp78
-rw-r--r--krdc/maindialog.h48
-rw-r--r--krdc/maindialogbase.ui351
-rw-r--r--krdc/maindialogwidget.cpp359
-rw-r--r--krdc/maindialogwidget.h74
-rw-r--r--krdc/pindown.pngbin0 -> 322 bytes
-rw-r--r--krdc/pinup.pngbin0 -> 320 bytes
-rw-r--r--krdc/pointcursor.pngbin0 -> 143 bytes
-rw-r--r--krdc/pointcursormask.pngbin0 -> 146 bytes
-rw-r--r--krdc/preferencesdialog.cpp128
-rw-r--r--krdc/preferencesdialog.h57
-rw-r--r--krdc/rdp.protocol12
-rw-r--r--krdc/rdp/Makefile.am11
-rw-r--r--krdc/rdp/README.patch12
-rw-r--r--krdc/rdp/krdpview.cpp369
-rw-r--r--krdc/rdp/krdpview.h113
-rw-r--r--krdc/rdp/rdesktop.patch76
-rw-r--r--krdc/rdp/rdphostpref.cpp180
-rw-r--r--krdc/rdp/rdphostpref.h127
-rw-r--r--krdc/rdp/rdpprefs.ui509
-rw-r--r--krdc/rdp/rdpprefs.ui.h163
-rw-r--r--krdc/smartptr.cpp17
-rw-r--r--krdc/smartptr.h433
-rw-r--r--krdc/smb2rdc.desktop56
-rw-r--r--krdc/vidmode.cpp159
-rw-r--r--krdc/vidmode.h40
-rw-r--r--krdc/vnc.protocol13
-rw-r--r--krdc/vnc/Makefile.am16
-rw-r--r--krdc/vnc/colour.c415
-rw-r--r--krdc/vnc/d3des.c440
-rw-r--r--krdc/vnc/d3des.h51
-rw-r--r--krdc/vnc/desktop.c1613
-rw-r--r--krdc/vnc/hextile.c129
-rw-r--r--krdc/vnc/kvncview.cpp828
-rw-r--r--krdc/vnc/kvncview.h121
-rw-r--r--krdc/vnc/pointerlatencyometer.h83
-rw-r--r--krdc/vnc/rfbproto.c1335
-rw-r--r--krdc/vnc/rfbproto.h957
-rw-r--r--krdc/vnc/sockets.c325
-rw-r--r--krdc/vnc/threads.cpp392
-rw-r--r--krdc/vnc/threads.h126
-rw-r--r--krdc/vnc/tight.c610
-rw-r--r--krdc/vnc/vncauth.c161
-rw-r--r--krdc/vnc/vncauth.h30
-rw-r--r--krdc/vnc/vnchostpref.cpp127
-rw-r--r--krdc/vnc/vnchostpref.h52
-rw-r--r--krdc/vnc/vncprefs.ui165
-rw-r--r--krdc/vnc/vncprefs.ui.h53
-rw-r--r--krdc/vnc/vnctypes.h73
-rw-r--r--krdc/vnc/vncviewer.h186
-rw-r--r--krdc/vnc/zlib.c157
-rw-r--r--krfb/AUTHORS2
-rw-r--r--krfb/DCOP-INTERFACE9
-rw-r--r--krfb/Makefile.am8
-rw-r--r--krfb/NOTES27
-rw-r--r--krfb/README19
-rw-r--r--krfb/TODO28
-rw-r--r--krfb/configure.in.in21
-rw-r--r--krfb/kcm_krfb/Makefile.am20
-rw-r--r--krfb/kcm_krfb/configurationwidget.ui503
-rw-r--r--krfb/kcm_krfb/kcm_krfb.cpp180
-rw-r--r--krfb/kcm_krfb/kcm_krfb.h47
-rw-r--r--krfb/kcm_krfb/kcmkrfb.desktop192
-rw-r--r--krfb/kinetd/Makefile.am28
-rw-r--r--krfb/kinetd/README.debugging13
-rw-r--r--krfb/kinetd/eventsrc259
-rw-r--r--krfb/kinetd/kinetd.cpp658
-rw-r--r--krfb/kinetd/kinetd.desktop139
-rw-r--r--krfb/kinetd/kinetd.h200
-rw-r--r--krfb/kinetd/kinetdmodule.desktop148
-rw-r--r--krfb/krfb/Makefile.am34
-rw-r--r--krfb/krfb/configuration.cc474
-rw-r--r--krfb/krfb/configuration.h139
-rw-r--r--krfb/krfb/connection-side-image.pngbin0 -> 53735 bytes
-rw-r--r--krfb/krfb/connectiondialog.cc63
-rw-r--r--krfb/krfb/connectiondialog.h44
-rw-r--r--krfb/krfb/connectionwidget.ui208
-rw-r--r--krfb/krfb/cr16-app-krfb.pngbin0 -> 931 bytes
-rw-r--r--krfb/krfb/cr32-app-krfb.pngbin0 -> 2551 bytes
-rw-r--r--krfb/krfb/cr48-app-krfb.pngbin0 -> 5508 bytes
-rw-r--r--krfb/krfb/eventsrc1219
-rw-r--r--krfb/krfb/eyes-closed24.pngbin0 -> 1485 bytes
-rw-r--r--krfb/krfb/eyes-open.pngbin0 -> 2551 bytes
-rw-r--r--krfb/krfb/eyes-open24.pngbin0 -> 1734 bytes
-rw-r--r--krfb/krfb/invitation.cc125
-rw-r--r--krfb/krfb/invitation.h57
-rw-r--r--krfb/krfb/invitedialog.cc65
-rw-r--r--krfb/krfb/invitedialog.h54
-rw-r--r--krfb/krfb/invitewidget.ui197
-rw-r--r--krfb/krfb/kinetd_krfb.desktop150
-rw-r--r--krfb/krfb/krfb.desktop64
-rw-r--r--krfb/krfb/krfbiface.h24
-rw-r--r--krfb/krfb/krfbifaceimpl.cc27
-rw-r--r--krfb/krfb/krfbifaceimpl.h22
-rw-r--r--krfb/krfb/lo16-app-krfb.pngbin0 -> 305 bytes
-rw-r--r--krfb/krfb/lo32-app-krfb.pngbin0 -> 505 bytes
-rw-r--r--krfb/krfb/main.cpp191
-rw-r--r--krfb/krfb/manageinvitations.ui216
-rw-r--r--krfb/krfb/manageinvitations.ui.h15
-rw-r--r--krfb/krfb/personalinvitedialog.cc54
-rw-r--r--krfb/krfb/personalinvitedialog.h44
-rw-r--r--krfb/krfb/personalinvitewidget.ui241
-rw-r--r--krfb/krfb/rfbcontroller.cc902
-rw-r--r--krfb/krfb/rfbcontroller.h221
-rw-r--r--krfb/krfb/templates/cpp_template16
-rw-r--r--krfb/krfb/templates/header_template16
-rw-r--r--krfb/krfb/trayicon.cpp138
-rw-r--r--krfb/krfb/trayicon.h90
-rw-r--r--krfb/krfb/xupdatescanner.cc481
-rw-r--r--krfb/krfb/xupdatescanner.h112
-rw-r--r--krfb/krfb_httpd/Makefile.am3
-rw-r--r--krfb/krfb_httpd/kinetd_krfb_httpd.desktop96
-rw-r--r--krfb/krfb_httpd/krfb_httpd75
-rw-r--r--krfb/libvncserver/1instance.c141
-rw-r--r--krfb/libvncserver/CHANGES75
-rw-r--r--krfb/libvncserver/COPYING340
-rw-r--r--krfb/libvncserver/Makefile.am14
-rw-r--r--krfb/libvncserver/README417
-rw-r--r--krfb/libvncserver/TODO80
-rw-r--r--krfb/libvncserver/auth.c109
-rw-r--r--krfb/libvncserver/bdf2c.pl60
-rw-r--r--krfb/libvncserver/cargs.c135
-rw-r--r--krfb/libvncserver/corre.c355
-rw-r--r--krfb/libvncserver/cursor.c678
-rw-r--r--krfb/libvncserver/cutpaste.c39
-rw-r--r--krfb/libvncserver/d3des.c442
-rw-r--r--krfb/libvncserver/d3des.h56
-rw-r--r--krfb/libvncserver/default8x16.h261
-rw-r--r--krfb/libvncserver/draw.c61
-rw-r--r--krfb/libvncserver/example.c279
-rw-r--r--krfb/libvncserver/font.c192
-rw-r--r--krfb/libvncserver/fontsel.c71
-rw-r--r--krfb/libvncserver/hextile.c347
-rw-r--r--krfb/libvncserver/httpd.c423
-rw-r--r--krfb/libvncserver/keysym.h1639
-rw-r--r--krfb/libvncserver/mac.c604
-rw-r--r--krfb/libvncserver/main.c729
-rw-r--r--krfb/libvncserver/pnmshow.c81
-rw-r--r--krfb/libvncserver/pnmshow24.c90
-rw-r--r--krfb/libvncserver/radon.h195
-rw-r--r--krfb/libvncserver/rfb.h865
-rw-r--r--krfb/libvncserver/rfbproto.h848
-rw-r--r--krfb/libvncserver/rfbserver.c1590
-rw-r--r--krfb/libvncserver/rre.c322
-rw-r--r--krfb/libvncserver/selbox.c301
-rw-r--r--krfb/libvncserver/sockets.c592
-rw-r--r--krfb/libvncserver/sraRegion.c889
-rw-r--r--krfb/libvncserver/sraRegion.h65
-rw-r--r--krfb/libvncserver/stats.c103
-rw-r--r--krfb/libvncserver/storepasswd.c46
-rw-r--r--krfb/libvncserver/tableinit24.c157
-rw-r--r--krfb/libvncserver/tableinitcmtemplate.c84
-rw-r--r--krfb/libvncserver/tableinittctemplate.c142
-rw-r--r--krfb/libvncserver/tabletrans24template.c281
-rw-r--r--krfb/libvncserver/tabletranstemplate.c117
-rw-r--r--krfb/libvncserver/tight.c1809
-rw-r--r--krfb/libvncserver/translate.c484
-rw-r--r--krfb/libvncserver/vncauth.c171
-rw-r--r--krfb/libvncserver/vncev.c119
-rw-r--r--krfb/libvncserver/x11vnc.c578
-rw-r--r--krfb/libvncserver/zippy.c182
-rw-r--r--krfb/libvncserver/zlib.c304
-rw-r--r--krfb/srvloc/Makefile.am17
-rw-r--r--krfb/srvloc/getifaddrs.cpp261
-rw-r--r--krfb/srvloc/getifaddrs.h96
-rw-r--r--krfb/srvloc/kinetinterface.cpp277
-rw-r--r--krfb/srvloc/kinetinterface.h186
-rw-r--r--krfb/srvloc/kinetinterfacewatcher.cpp59
-rw-r--r--krfb/srvloc/kinetinterfacewatcher.h122
-rw-r--r--krfb/srvloc/kserviceregistry.cpp181
-rw-r--r--krfb/srvloc/kserviceregistry.h161
-rw-r--r--krfb/srvloc/uuid.cpp245
-rw-r--r--krfb/srvloc/uuid.h29
-rw-r--r--ksirc/Artistic131
-rw-r--r--ksirc/ChangeLog270
-rw-r--r--ksirc/FAQ73
-rw-r--r--ksirc/FilterRuleEditor.cpp244
-rw-r--r--ksirc/FilterRuleEditor.dlg215
-rw-r--r--ksirc/FilterRuleEditor.h53
-rw-r--r--ksirc/FilterRuleWidget.ui239
-rw-r--r--ksirc/KSOpenkSirc/Makefile.am26
-rwxr-xr-xksirc/KSOpenkSirc/convert-mIRC19
-rw-r--r--ksirc/KSOpenkSirc/enter_combo.cpp10
-rw-r--r--ksirc/KSOpenkSirc/enter_combo.h37
-rw-r--r--ksirc/KSOpenkSirc/open_ksirc.cpp377
-rw-r--r--ksirc/KSOpenkSirc/open_ksirc.h53
-rw-r--r--ksirc/KSOpenkSirc/open_ksircData.ui337
-rw-r--r--ksirc/KSOpenkSirc/serverDataType.h56
-rw-r--r--ksirc/KSOpenkSirc/serverFileParser.cpp114
-rw-r--r--ksirc/KSOpenkSirc/serverFileParser.h13
-rw-r--r--ksirc/KSOpenkSirc/servers.ini362
-rw-r--r--ksirc/KSOpenkSirc/servers.txt654
-rw-r--r--ksirc/KSPrefs/Makefile.am50
-rw-r--r--ksirc/KSPrefs/ksprefs.cpp244
-rw-r--r--ksirc/KSPrefs/ksprefs.h65
-rw-r--r--ksirc/KSPrefs/page_autoconnect.cpp309
-rw-r--r--ksirc/KSPrefs/page_autoconnect.h36
-rw-r--r--ksirc/KSPrefs/page_autoconnectbase.ui391
-rw-r--r--ksirc/KSPrefs/page_colors.cpp295
-rw-r--r--ksirc/KSPrefs/page_colors.h53
-rw-r--r--ksirc/KSPrefs/page_colorsbase.ui1063
-rw-r--r--ksirc/KSPrefs/page_font.cpp38
-rw-r--r--ksirc/KSPrefs/page_font.h62
-rw-r--r--ksirc/KSPrefs/page_general.cpp117
-rw-r--r--ksirc/KSPrefs/page_general.h35
-rw-r--r--ksirc/KSPrefs/page_generalbase.ui540
-rw-r--r--ksirc/KSPrefs/page_irccolors.cpp181
-rw-r--r--ksirc/KSPrefs/page_irccolors.h40
-rw-r--r--ksirc/KSPrefs/page_irccolorsbase.ui1275
-rw-r--r--ksirc/KSPrefs/page_looknfeel.cpp72
-rw-r--r--ksirc/KSPrefs/page_looknfeel.h37
-rw-r--r--ksirc/KSPrefs/page_looknfeelbase.ui323
-rw-r--r--ksirc/KSPrefs/page_rmbmenu.cpp206
-rw-r--r--ksirc/KSPrefs/page_rmbmenu.h38
-rw-r--r--ksirc/KSPrefs/page_rmbmenubase.ui252
-rw-r--r--ksirc/KSPrefs/page_servchan.cpp135
-rw-r--r--ksirc/KSPrefs/page_servchan.h38
-rw-r--r--ksirc/KSPrefs/page_servchanbase.ui172
-rw-r--r--ksirc/KSPrefs/page_shortcuts.cpp57
-rw-r--r--ksirc/KSPrefs/page_shortcuts.h40
-rw-r--r--ksirc/KSPrefs/page_shortcutsbase.ui57
-rw-r--r--ksirc/KSPrefs/page_startup.cpp126
-rw-r--r--ksirc/KSPrefs/page_startup.h40
-rw-r--r--ksirc/KSPrefs/page_startupbase.ui197
-rw-r--r--ksirc/KSProgress/Makefile.am11
-rw-r--r--ksirc/KSProgress/ksprogress.cpp60
-rw-r--r--ksirc/KSProgress/ksprogress.dlg167
-rw-r--r--ksirc/KSProgress/ksprogress.h47
-rw-r--r--ksirc/KSProgress/ksprogressdata.cpp72
-rw-r--r--ksirc/KSProgress/ksprogressdata.h48
-rw-r--r--ksirc/KSTicker/Makefile.am20
-rw-r--r--ksirc/KSTicker/kspainter.cpp186
-rw-r--r--ksirc/KSTicker/kspainter.h25
-rw-r--r--ksirc/KSTicker/ksticker.cpp548
-rw-r--r--ksirc/KSTicker/ksticker.h106
-rw-r--r--ksirc/KSTicker/ksttest.cpp93
-rw-r--r--ksirc/KSTicker/ksttest.h24
-rw-r--r--ksirc/KSTicker/libksticker.c0
-rw-r--r--ksirc/KSTicker/speeddialog.cpp61
-rw-r--r--ksirc/KSTicker/speeddialog.dlg117
-rw-r--r--ksirc/KSTicker/speeddialog.h42
-rw-r--r--ksirc/KSTicker/speeddialogData.cpp107
-rw-r--r--ksirc/KSTicker/speeddialogData.h51
-rw-r--r--ksirc/Makefile.am146
-rw-r--r--ksirc/NewWindowDialog.cpp73
-rw-r--r--ksirc/NewWindowDialog.h35
-rw-r--r--ksirc/advfollow.pl125
-rw-r--r--ksirc/ahistlineedit.cpp437
-rw-r--r--ksirc/ahistlineedit.h56
-rw-r--r--ksirc/alistbox.cpp508
-rw-r--r--ksirc/alistbox.h112
-rw-r--r--ksirc/autodcc.pl71
-rw-r--r--ksirc/baserules.cpp167
-rw-r--r--ksirc/baserules.h35
-rw-r--r--ksirc/boundscheckingarray.h67
-rw-r--r--ksirc/chanButtons.cpp268
-rw-r--r--ksirc/chanButtons.h85
-rw-r--r--ksirc/chanparser.cpp1047
-rw-r--r--ksirc/chanparser.h289
-rw-r--r--ksirc/charSelector.cpp77
-rw-r--r--ksirc/charSelector.h42
-rw-r--r--ksirc/colorpicker.cpp342
-rw-r--r--ksirc/colorpicker.h91
-rw-r--r--ksirc/control_message.h15
-rw-r--r--ksirc/dccManager.cpp403
-rw-r--r--ksirc/dccManager.h138
-rw-r--r--ksirc/dccManagerbase.ui202
-rw-r--r--ksirc/dccMgrTest.cpp44
-rw-r--r--ksirc/dccNew.cpp162
-rw-r--r--ksirc/dccNew.h44
-rw-r--r--ksirc/dccNewbase.ui237
-rw-r--r--ksirc/dccToplevel.cpp49
-rw-r--r--ksirc/dccToplevel.h33
-rw-r--r--ksirc/default.pl867
-rw-r--r--ksirc/displayMgr.h28
-rw-r--r--ksirc/displayMgrMDI.cpp306
-rw-r--r--ksirc/displayMgrMDI.h48
-rw-r--r--ksirc/displayMgrSDI.cpp40
-rw-r--r--ksirc/displayMgrSDI.h25
-rw-r--r--ksirc/dockservercontroller.cpp451
-rw-r--r--ksirc/dockservercontroller.h110
-rwxr-xr-xksirc/dsirc2721
-rw-r--r--ksirc/eventsrc364
-rw-r--r--ksirc/filters.pl171
-rw-r--r--ksirc/getdate.c7
-rw-r--r--ksirc/icons/Makefile.am5
-rw-r--r--ksirc/icons/X.pngbin0 -> 840 bytes
-rw-r--r--ksirc/icons/cr22-action-info.pngbin0 -> 1195 bytes
-rw-r--r--ksirc/icons/cr22-action-ksirc_dock.pngbin0 -> 1129 bytes
-rw-r--r--ksirc/icons/ctcpping.pngbin0 -> 1040 bytes
-rw-r--r--ksirc/icons/error.pngbin0 -> 1043 bytes
-rw-r--r--ksirc/icons/hi16-app-ksirc.pngbin0 -> 903 bytes
-rw-r--r--ksirc/icons/hi22-app-ksirc.pngbin0 -> 1227 bytes
-rw-r--r--ksirc/icons/hi32-app-ksirc.pngbin0 -> 2134 bytes
-rw-r--r--ksirc/icons/hi48-app-ksirc.pngbin0 -> 3911 bytes
-rw-r--r--ksirc/icons/hi64-app-ksirc.pngbin0 -> 4237 bytes
-rw-r--r--ksirc/img/Makefile.am18
-rw-r--r--ksirc/img/X.pngbin0 -> 854 bytes
-rw-r--r--ksirc/img/action.pngbin0 -> 842 bytes
-rw-r--r--ksirc/img/arrow.pngbin0 -> 1235 bytes
-rw-r--r--ksirc/img/blueball.pngbin0 -> 584 bytes
-rw-r--r--ksirc/img/bluepin.pngbin0 -> 434 bytes
-rw-r--r--ksirc/img/bminus.pngbin0 -> 854 bytes
-rw-r--r--ksirc/img/bplus.pngbin0 -> 870 bytes
-rw-r--r--ksirc/img/channel.xpm36
-rw-r--r--ksirc/img/channels.xpm60
-rw-r--r--ksirc/img/ctcpping.pngbin0 -> 863 bytes
-rw-r--r--ksirc/img/dcc.pngbin0 -> 812 bytes
-rw-r--r--ksirc/img/dccget.pngbin0 -> 832 bytes
-rw-r--r--ksirc/img/dccsend.pngbin0 -> 809 bytes
-rw-r--r--ksirc/img/elipsis.pngbin0 -> 765 bytes
-rw-r--r--ksirc/img/emoticons/Makefile.am5
-rw-r--r--ksirc/img/emoticons/biggrin.pngbin0 -> 803 bytes
-rw-r--r--ksirc/img/emoticons/clown.pngbin0 -> 751 bytes
-rw-r--r--ksirc/img/emoticons/cry.pngbin0 -> 1092 bytes
-rw-r--r--ksirc/img/emoticons/devil.pngbin0 -> 852 bytes
-rw-r--r--ksirc/img/emoticons/frown.pngbin0 -> 709 bytes
-rw-r--r--ksirc/img/emoticons/heart.pngbin0 -> 1037 bytes
-rw-r--r--ksirc/img/emoticons/loveit.pngbin0 -> 959 bytes
-rw-r--r--ksirc/img/emoticons/puh.pngbin0 -> 1101 bytes
-rw-r--r--ksirc/img/emoticons/puh2.pngbin0 -> 1116 bytes
-rw-r--r--ksirc/img/emoticons/redface.pngbin0 -> 778 bytes
-rw-r--r--ksirc/img/emoticons/sadley.pngbin0 -> 615 bytes
-rw-r--r--ksirc/img/emoticons/slime.pngbin0 -> 1130 bytes
-rw-r--r--ksirc/img/emoticons/smile.pngbin0 -> 609 bytes
-rw-r--r--ksirc/img/emoticons/wink.pngbin0 -> 619 bytes
-rw-r--r--ksirc/img/emoticons/yummie.pngbin0 -> 1126 bytes
-rw-r--r--ksirc/img/error.pngbin0 -> 870 bytes
-rw-r--r--ksirc/img/greenpin.pngbin0 -> 743 bytes
-rw-r--r--ksirc/img/info.pngbin0 -> 757 bytes
-rw-r--r--ksirc/img/info1.pngbin0 -> 831 bytes
-rw-r--r--ksirc/img/join.pngbin0 -> 835 bytes
-rw-r--r--ksirc/img/kick.pngbin0 -> 835 bytes
-rw-r--r--ksirc/img/ksirc.pngbin0 -> 3464 bytes
-rw-r--r--ksirc/img/ksirc_a.xpm46
-rw-r--r--ksirc/img/ksirc_b.xpm48
-rw-r--r--ksirc/img/ksirc_dock.pngbin0 -> 1192 bytes
-rw-r--r--ksirc/img/ksirc_dock.xpm192
-rw-r--r--ksirc/img/madsmiley.pngbin0 -> 701 bytes
-rw-r--r--ksirc/img/mdi.pngbin0 -> 12434 bytes
-rw-r--r--ksirc/img/mini-run.pngbin0 -> 254 bytes
-rw-r--r--ksirc/img/minus.pngbin0 -> 714 bytes
-rw-r--r--ksirc/img/misc1.pngbin0 -> 901 bytes
-rw-r--r--ksirc/img/misc2.pngbin0 -> 795 bytes
-rw-r--r--ksirc/img/misc3.pngbin0 -> 848 bytes
-rw-r--r--ksirc/img/misc4.pngbin0 -> 882 bytes
-rw-r--r--ksirc/img/misc5.pngbin0 -> 817 bytes
-rw-r--r--ksirc/img/misc6.pngbin0 -> 848 bytes
-rw-r--r--ksirc/img/misc7.pngbin0 -> 848 bytes
-rw-r--r--ksirc/img/misc8.pngbin0 -> 865 bytes
-rw-r--r--ksirc/img/misc9.pngbin0 -> 856 bytes
-rw-r--r--ksirc/img/miscA.pngbin0 -> 893 bytes
-rw-r--r--ksirc/img/mode.pngbin0 -> 843 bytes
-rw-r--r--ksirc/img/notice.pngbin0 -> 826 bytes
-rw-r--r--ksirc/img/ominus.pngbin0 -> 826 bytes
-rw-r--r--ksirc/img/oplus.pngbin0 -> 849 bytes
-rw-r--r--ksirc/img/part.pngbin0 -> 842 bytes
-rw-r--r--ksirc/img/plus.pngbin0 -> 791 bytes
-rw-r--r--ksirc/img/ppl.pngbin0 -> 462 bytes
-rw-r--r--ksirc/img/quit.pngbin0 -> 859 bytes
-rw-r--r--ksirc/img/sadsmiley.pngbin0 -> 685 bytes
-rw-r--r--ksirc/img/sdi.pngbin0 -> 4910 bytes
-rw-r--r--ksirc/img/server.xpm320
-rw-r--r--ksirc/img/servinfo.pngbin0 -> 858 bytes
-rw-r--r--ksirc/img/smiley.pngbin0 -> 687 bytes
-rw-r--r--ksirc/img/star.pngbin0 -> 566 bytes
-rw-r--r--ksirc/img/start.xpm29
-rw-r--r--ksirc/img/stop.xpm120
-rw-r--r--ksirc/img/tile.xpmbin0 -> 6236 bytes
-rw-r--r--ksirc/img/topic.pngbin0 -> 765 bytes
-rw-r--r--ksirc/ioBroadcast.cpp101
-rw-r--r--ksirc/ioBroadcast.h25
-rw-r--r--ksirc/ioDCC.cpp643
-rw-r--r--ksirc/ioDCC.h65
-rw-r--r--ksirc/ioDiscard.cpp23
-rw-r--r--ksirc/ioDiscard.h20
-rw-r--r--ksirc/ioLAG.cpp61
-rw-r--r--ksirc/ioLAG.h31
-rw-r--r--ksirc/ioNotify.cpp75
-rw-r--r--ksirc/ioNotify.h31
-rw-r--r--ksirc/iocontroller.cpp426
-rw-r--r--ksirc/iocontroller.h54
-rw-r--r--ksirc/ksirc.cpp151
-rw-r--r--ksirc/ksirc.desktop87
-rw-r--r--ksirc/ksirc.pl781
-rw-r--r--ksirc/ksircchannel.h35
-rw-r--r--ksirc/ksircprocess.cpp663
-rw-r--r--ksirc/ksircprocess.h92
-rw-r--r--ksirc/ksircrc62
-rw-r--r--ksirc/ksircserver.h34
-rw-r--r--ksirc/ksopts.cpp542
-rw-r--r--ksirc/ksopts.h218
-rw-r--r--ksirc/ksparser.cpp192
-rw-r--r--ksirc/ksparser.h43
-rw-r--r--ksirc/kstextview.cpp2269
-rw-r--r--ksirc/kstextview.h577
-rw-r--r--ksirc/kstextviewtest.cpp51
-rw-r--r--ksirc/ksview.cpp344
-rw-r--r--ksirc/ksview.h74
-rw-r--r--ksirc/logfile.cpp93
-rw-r--r--ksirc/logfile.h40
-rw-r--r--ksirc/mditoplevel.cpp260
-rw-r--r--ksirc/mditoplevel.h75
-rw-r--r--ksirc/messageReceiver.cpp73
-rw-r--r--ksirc/messageReceiver.h62
-rw-r--r--ksirc/nickColourMaker.cpp86
-rw-r--r--ksirc/nickColourMaker.h44
-rw-r--r--ksirc/objFinder.cpp160
-rw-r--r--ksirc/objFinder.h41
-rw-r--r--ksirc/openksircproc.dlg76
-rw-r--r--ksirc/puke/HOWTO-PUKE.pod325
-rw-r--r--ksirc/puke/Makefile.am43
-rw-r--r--ksirc/puke/commands-handler.pl61
-rw-r--r--ksirc/puke/commands-perl.pl605
-rw-r--r--ksirc/puke/commands.h1052
-rw-r--r--ksirc/puke/controller.cpp974
-rw-r--r--ksirc/puke/controller.h211
-rwxr-xr-xksirc/puke/convert_commands.pl13
-rw-r--r--ksirc/puke/dcc_progress.pm55
-rw-r--r--ksirc/puke/dcc_status.pm525
-rw-r--r--ksirc/puke/load_all.pm14
-rw-r--r--ksirc/puke/palistbox.cpp156
-rw-r--r--ksirc/puke/palistbox.h35
-rw-r--r--ksirc/puke/palistbox.pm117
-rw-r--r--ksirc/puke/pbase.pm265
-rw-r--r--ksirc/puke/pboxlayout.pm201
-rw-r--r--ksirc/puke/pbutton.cpp157
-rw-r--r--ksirc/puke/pbutton.h40
-rw-r--r--ksirc/puke/pbutton.pm79
-rw-r--r--ksirc/puke/pframe.cpp84
-rw-r--r--ksirc/puke/pframe.h28
-rw-r--r--ksirc/puke/pframe.pm57
-rw-r--r--ksirc/puke/pkfiledialog-cmd.h56
-rw-r--r--ksirc/puke/pkfiledialog.cpp123
-rw-r--r--ksirc/puke/pkfiledialog.h31
-rw-r--r--ksirc/puke/pkfiledialog.pm76
-rw-r--r--ksirc/puke/plabel.cpp127
-rw-r--r--ksirc/puke/plabel.h34
-rw-r--r--ksirc/puke/plabel.pm105
-rw-r--r--ksirc/puke/playout.cpp157
-rw-r--r--ksirc/puke/playout.h42
-rw-r--r--ksirc/puke/plined.cpp153
-rw-r--r--ksirc/puke/plined.h33
-rw-r--r--ksirc/puke/plined.pm84
-rw-r--r--ksirc/puke/plistbox.cpp224
-rw-r--r--ksirc/puke/plistbox.h37
-rw-r--r--ksirc/puke/plistbox.pm196
-rw-r--r--ksirc/puke/pmenudta.cpp79
-rw-r--r--ksirc/puke/pmenudta.h57
-rw-r--r--ksirc/puke/pmenudta.pm60
-rw-r--r--ksirc/puke/pmessage.h38
-rw-r--r--ksirc/puke/pobject.cpp138
-rw-r--r--ksirc/puke/pobject.h134
-rw-r--r--ksirc/puke/pobjfinder-cmd.h32
-rw-r--r--ksirc/puke/pobjfinder.cpp86
-rw-r--r--ksirc/puke/pobjfinder.h33
-rw-r--r--ksirc/puke/pobjfinder.pm89
-rw-r--r--ksirc/puke/ppopmenu.cpp120
-rw-r--r--ksirc/puke/ppopmenu.h36
-rw-r--r--ksirc/puke/ppopmenu.pm65
-rw-r--r--ksirc/puke/pprogress.cpp133
-rw-r--r--ksirc/puke/pprogress.h32
-rw-r--r--ksirc/puke/pprogress.pm86
-rw-r--r--ksirc/puke/ppushbt.cpp70
-rw-r--r--ksirc/puke/ppushbt.h33
-rw-r--r--ksirc/puke/ppushbt.pm30
-rw-r--r--ksirc/puke/ptabdialog.cpp101
-rw-r--r--ksirc/puke/ptabdialog.h41
-rw-r--r--ksirc/puke/ptabdialog.pm49
-rw-r--r--ksirc/puke/ptablevw.cpp76
-rw-r--r--ksirc/puke/ptablevw.h33
-rw-r--r--ksirc/puke/ptablevw.pm57
-rw-r--r--ksirc/puke/puke.pl225
-rw-r--r--ksirc/puke/pwidget.cpp492
-rw-r--r--ksirc/puke/pwidget.h45
-rw-r--r--ksirc/puke/pwidget.pm231
-rw-r--r--ksirc/puke/small.pl65
-rw-r--r--ksirc/puke/test.pl63
-rwxr-xr-xksirc/puke/tester.pl28
-rw-r--r--ksirc/puke/user_monitor.ks440
-rw-r--r--ksirc/puke/widgethdlr.h3
-rw-r--r--ksirc/relnotes107
-rw-r--r--ksirc/servercontroller.cpp978
-rw-r--r--ksirc/servercontroller.dlg33
-rw-r--r--ksirc/servercontroller.h267
-rw-r--r--ksirc/sirc.help.gzbin0 -> 7270 bytes
-rw-r--r--ksirc/ssfeprompt.cpp54
-rw-r--r--ksirc/ssfeprompt.h37
-rw-r--r--ksirc/ssfepromptdata.cpp68
-rw-r--r--ksirc/ssfepromptdata.h47
-rw-r--r--ksirc/stringparserstate.h71
-rw-r--r--ksirc/test/nicklist.pl77
-rw-r--r--ksirc/test/tester.pl65
-rw-r--r--ksirc/topic.cpp245
-rw-r--r--ksirc/topic.h70
-rw-r--r--ksirc/toplevel.cpp1848
-rw-r--r--ksirc/toplevel.h535
-rw-r--r--ksirc/usercontrolmenu.cpp167
-rw-r--r--ksirc/usercontrolmenu.h52
-rw-r--r--ksirc/version.h6
-rw-r--r--ktalkd/ChangeLog532
-rw-r--r--ktalkd/Makefile.am29
-rw-r--r--ktalkd/SNPRINTF_MISSING21
-rw-r--r--ktalkd/TODO16
-rw-r--r--ktalkd/configure.in.in73
-rw-r--r--ktalkd/kcmktalkd/Makefile.am15
-rw-r--r--ktalkd/kcmktalkd/answmachpage.cpp265
-rw-r--r--ktalkd/kcmktalkd/answmachpage.h83
-rw-r--r--ktalkd/kcmktalkd/cr128-app-ktalkd.pngbin0 -> 13158 bytes
-rw-r--r--ktalkd/kcmktalkd/cr16-app-ktalkd.pngbin0 -> 792 bytes
-rw-r--r--ktalkd/kcmktalkd/cr22-app-ktalkd.pngbin0 -> 1342 bytes
-rw-r--r--ktalkd/kcmktalkd/cr32-app-ktalkd.pngbin0 -> 2133 bytes
-rw-r--r--ktalkd/kcmktalkd/cr48-app-ktalkd.pngbin0 -> 3882 bytes
-rw-r--r--ktalkd/kcmktalkd/forwmachpage.cpp183
-rw-r--r--ktalkd/kcmktalkd/forwmachpage.h75
-rw-r--r--ktalkd/kcmktalkd/kcmktalkd.desktop207
-rw-r--r--ktalkd/kcmktalkd/main.cpp113
-rw-r--r--ktalkd/kcmktalkd/main.h64
-rw-r--r--ktalkd/kcmktalkd/soundpage.cpp330
-rw-r--r--ktalkd/kcmktalkd/soundpage.h90
-rw-r--r--ktalkd/ktalkd/.talkdrc78
-rw-r--r--ktalkd/ktalkd/Makefile.am41
-rw-r--r--ktalkd/ktalkd/announce.cpp502
-rw-r--r--ktalkd/ktalkd/announce.h51
-rw-r--r--ktalkd/ktalkd/defs.h87
-rw-r--r--ktalkd/ktalkd/find_user.cpp400
-rw-r--r--ktalkd/ktalkd/find_user.h39
-rw-r--r--ktalkd/ktalkd/includ.h79
-rw-r--r--ktalkd/ktalkd/ktalkd.wavbin0 -> 10026 bytes
-rw-r--r--ktalkd/ktalkd/ktalkdrc73
-rw-r--r--ktalkd/ktalkd/machines/Makefile.am14
-rw-r--r--ktalkd/ktalkd/machines/answmach.cpp405
-rw-r--r--ktalkd/ktalkd/machines/answmach.h86
-rw-r--r--ktalkd/ktalkd/machines/forwmach.cpp442
-rw-r--r--ktalkd/ktalkd/machines/forwmach.h166
-rw-r--r--ktalkd/ktalkd/machines/talkconn.cpp583
-rw-r--r--ktalkd/ktalkd/machines/talkconn.h135
-rw-r--r--ktalkd/ktalkd/options.cpp57
-rw-r--r--ktalkd/ktalkd/options.h71
-rw-r--r--ktalkd/ktalkd/otalkd.h64
-rw-r--r--ktalkd/ktalkd/print.c226
-rw-r--r--ktalkd/ktalkd/process.cpp285
-rw-r--r--ktalkd/ktalkd/process.h39
-rw-r--r--ktalkd/ktalkd/prot_talkd.h136
-rw-r--r--ktalkd/ktalkd/proto.h75
-rw-r--r--ktalkd/ktalkd/readcfg++.cpp261
-rw-r--r--ktalkd/ktalkd/readconf.cpp188
-rw-r--r--ktalkd/ktalkd/readconf.h69
-rw-r--r--ktalkd/ktalkd/repairs.c274
-rw-r--r--ktalkd/ktalkd/table.cpp217
-rw-r--r--ktalkd/ktalkd/table.h73
-rw-r--r--ktalkd/ktalkd/talkd.conf82
-rw-r--r--ktalkd/ktalkd/talkd.cpp401
-rw-r--r--ktalkd/ktalkd/threads.cpp99
-rw-r--r--ktalkd/ktalkd/threads.h39
-rw-r--r--ktalkd/ktalkd/unixsock.cpp145
-rw-r--r--ktalkd/ktalkd/unixsock.h6
-rw-r--r--ktalkd/ktalkdlg/Makefile.am15
-rw-r--r--ktalkd/ktalkdlg/ktalkdlg.cpp168
-rw-r--r--ktalkd/mail.local/Makefile.am8
-rw-r--r--ktalkd/mail.local/README.mail.local12
-rw-r--r--ktalkd/mail.local/mail.local.c983
-rw-r--r--ktalkd/mail.local/pathnames.h37
-rw-r--r--lanbrowsing/Makefile.am1
-rw-r--r--lanbrowsing/kcmlisa/Makefile.am18
-rw-r--r--lanbrowsing/kcmlisa/configure.in.in4
-rw-r--r--lanbrowsing/kcmlisa/findnic.cpp298
-rw-r--r--lanbrowsing/kcmlisa/findnic.h69
-rw-r--r--lanbrowsing/kcmlisa/kcmkiolan.cpp103
-rw-r--r--lanbrowsing/kcmlisa/kcmkiolan.desktop140
-rw-r--r--lanbrowsing/kcmlisa/kcmkiolan.h53
-rw-r--r--lanbrowsing/kcmlisa/kcmlisa.cpp394
-rw-r--r--lanbrowsing/kcmlisa/kcmlisa.desktop122
-rw-r--r--lanbrowsing/kcmlisa/kcmlisa.h79
-rw-r--r--lanbrowsing/kcmlisa/kcmreslisa.cpp250
-rw-r--r--lanbrowsing/kcmlisa/kcmreslisa.desktop123
-rw-r--r--lanbrowsing/kcmlisa/kcmreslisa.h67
-rw-r--r--lanbrowsing/kcmlisa/main.cpp49
-rw-r--r--lanbrowsing/kcmlisa/portsettingsbar.cpp51
-rw-r--r--lanbrowsing/kcmlisa/portsettingsbar.h45
-rw-r--r--lanbrowsing/kcmlisa/setupwizard.cpp569
-rw-r--r--lanbrowsing/kcmlisa/setupwizard.h103
-rw-r--r--lanbrowsing/kio_lan/AUTHORS2
-rw-r--r--lanbrowsing/kio_lan/Makefile.am31
-rw-r--r--lanbrowsing/kio_lan/README16
-rw-r--r--lanbrowsing/kio_lan/TODO3
-rw-r--r--lanbrowsing/kio_lan/kio_lan.cpp794
-rw-r--r--lanbrowsing/kio_lan/kio_lan.h87
-rw-r--r--lanbrowsing/kio_lan/lan.desktop81
-rw-r--r--lanbrowsing/kio_lan/lan.protocol15
-rw-r--r--lanbrowsing/kio_lan/lisa.desktop66
-rw-r--r--lanbrowsing/kio_lan/rlan.protocol15
-rw-r--r--lanbrowsing/lisa/AUTHORS2
-rw-r--r--lanbrowsing/lisa/COPYING347
-rw-r--r--lanbrowsing/lisa/ChangeLog8
-rw-r--r--lanbrowsing/lisa/INSTALL176
-rw-r--r--lanbrowsing/lisa/Makefile.am41
-rw-r--r--lanbrowsing/lisa/NEWS1
-rw-r--r--lanbrowsing/lisa/README419
-rw-r--r--lanbrowsing/lisa/addressvalidator.cpp228
-rw-r--r--lanbrowsing/lisa/addressvalidator.h55
-rw-r--r--lanbrowsing/lisa/client.cpp122
-rw-r--r--lanbrowsing/lisa/client.h45
-rw-r--r--lanbrowsing/lisa/configfile.cpp136
-rw-r--r--lanbrowsing/lisa/configfile.h33
-rw-r--r--lanbrowsing/lisa/ipaddress.cpp97
-rw-r--r--lanbrowsing/lisa/ipaddress.h55
-rw-r--r--lanbrowsing/lisa/lisadefines.h19
-rw-r--r--lanbrowsing/lisa/main.cpp290
-rw-r--r--lanbrowsing/lisa/mystring.cpp52
-rw-r--r--lanbrowsing/lisa/mystring.h42
-rw-r--r--lanbrowsing/lisa/netmanager.cpp1058
-rw-r--r--lanbrowsing/lisa/netmanager.h109
-rw-r--r--lanbrowsing/lisa/netscanner.cpp663
-rw-r--r--lanbrowsing/lisa/netscanner.h102
-rw-r--r--lanbrowsing/lisa/strictmain.cpp261
-rw-r--r--lanbrowsing/lisa/stringlist.cpp110
-rw-r--r--lanbrowsing/lisa/stringlist.h40
-rw-r--r--lanbrowsing/lisa/tcpnode.h29
-rw-r--r--librss/COPYING20
-rw-r--r--librss/Makefile.am17
-rw-r--r--librss/article.cpp146
-rw-r--r--librss/article.h151
-rw-r--r--librss/document.cpp525
-rw-r--r--librss/document.h231
-rw-r--r--librss/global.h138
-rw-r--r--librss/image.cpp157
-rw-r--r--librss/image.h172
-rw-r--r--librss/librss.doxyfile921
-rw-r--r--librss/librss.h22
-rw-r--r--librss/loader.cpp285
-rw-r--r--librss/loader.h322
-rw-r--r--librss/rss-faq.html396
-rw-r--r--librss/testlibrss.cpp41
-rw-r--r--librss/testlibrss.h24
-rw-r--r--librss/textinput.cpp96
-rw-r--r--librss/textinput.h121
-rw-r--r--librss/tools_p.cpp28
-rw-r--r--librss/tools_p.h31
-rw-r--r--wifi/Makefile.am56
-rw-r--r--wifi/asusled.cpp78
-rw-r--r--wifi/asusled.h40
-rw-r--r--wifi/configure.in.in71
-rw-r--r--wifi/hi128-app-kwifimanager.pngbin0 -> 10803 bytes
-rw-r--r--wifi/hi16-app-kwifimanager.pngbin0 -> 732 bytes
-rw-r--r--wifi/hi22-app-kwifimanager.pngbin0 -> 1123 bytes
-rw-r--r--wifi/hi32-app-kwifimanager.pngbin0 -> 1793 bytes
-rw-r--r--wifi/hi48-app-kwifimanager.pngbin0 -> 3134 bytes
-rw-r--r--wifi/hi64-app-kwifimanager.pngbin0 -> 4538 bytes
-rw-r--r--wifi/hisc-app-kwifimanager.svgzbin0 -> 3552 bytes
-rw-r--r--wifi/interface_dcop.h15
-rw-r--r--wifi/interface_wireless.cpp210
-rw-r--r--wifi/interface_wireless.h125
-rw-r--r--wifi/interface_wireless_wirelessextensions.cpp636
-rw-r--r--wifi/interface_wireless_wirelessextensions.h66
-rw-r--r--wifi/kcmwifi/Makefile.am19
-rw-r--r--wifi/kcmwifi/configadvanced.ui145
-rw-r--r--wifi/kcmwifi/configcrypto.ui365
-rw-r--r--wifi/kcmwifi/configcrypto.ui.h157
-rw-r--r--wifi/kcmwifi/configpower.ui158
-rw-r--r--wifi/kcmwifi/configpower.ui.h22
-rw-r--r--wifi/kcmwifi/ifconfigpage.cpp173
-rw-r--r--wifi/kcmwifi/ifconfigpage.h51
-rw-r--r--wifi/kcmwifi/ifconfigpagebase.ui500
-rw-r--r--wifi/kcmwifi/indent-options1
-rw-r--r--wifi/kcmwifi/kcmwifi.cpp382
-rw-r--r--wifi/kcmwifi/kcmwifi.desktop162
-rw-r--r--wifi/kcmwifi/kcmwifi.h62
-rw-r--r--wifi/kcmwifi/mainconfig.cpp108
-rw-r--r--wifi/kcmwifi/mainconfig.h51
-rw-r--r--wifi/kcmwifi/mainconfigbase.ui166
-rw-r--r--wifi/kcmwifi/vendorconfig.cpp271
-rw-r--r--wifi/kcmwifi/vendorconfig.h36
-rw-r--r--wifi/kcmwifi/wificonfig.cpp369
-rw-r--r--wifi/kcmwifi/wificonfig.h126
-rw-r--r--wifi/kwifimanager.cpp637
-rw-r--r--wifi/kwifimanager.desktop134
-rw-r--r--wifi/kwifimanager.h143
-rw-r--r--wifi/kwifimanagerui.rc19
-rw-r--r--wifi/kwireless/Makefile.am17
-rw-r--r--wifi/kwireless/kwireless.cpp89
-rw-r--r--wifi/kwireless/kwireless.desktop118
-rw-r--r--wifi/kwireless/kwireless.h46
-rw-r--r--wifi/kwireless/kwirelesswidget.cpp324
-rw-r--r--wifi/kwireless/kwirelesswidget.h103
-rw-r--r--wifi/kwireless/linuxwirelesswidget.cpp227
-rw-r--r--wifi/kwireless/linuxwirelesswidget.h36
-rw-r--r--wifi/kwireless/propertytable.cpp151
-rw-r--r--wifi/kwireless/propertytable.h45
-rw-r--r--wifi/kwireless/propertytablebase.ui68
-rw-r--r--wifi/locations/DE_BW_Karlsruhe_University.loc29
-rw-r--r--wifi/locations/DE_BW_Weissach.loc2
-rw-r--r--wifi/locations/Makefile.am6
-rw-r--r--wifi/locations/NOWHERE.loc1
-rw-r--r--wifi/locator.cpp56
-rw-r--r--wifi/locator.h41
-rw-r--r--wifi/main.cpp66
-rw-r--r--wifi/networkscanning.cpp155
-rw-r--r--wifi/networkscanning.h52
-rw-r--r--wifi/picture.cpp92
-rw-r--r--wifi/picture.h37
-rw-r--r--wifi/pixmaps/Makefile.am14
-rw-r--r--wifi/pixmaps/README2
-rw-r--r--wifi/pixmaps/ad_hoc.pngbin0 -> 5330 bytes
-rw-r--r--wifi/pixmaps/ad_hoc.svg1102
-rw-r--r--wifi/pixmaps/all_alone.pngbin0 -> 4220 bytes
-rw-r--r--wifi/pixmaps/all_alone.svg402
-rw-r--r--wifi/pixmaps/ap_connect.pngbin0 -> 4974 bytes
-rw-r--r--wifi/pixmaps/ap_connect.svg1105
-rw-r--r--wifi/pixmaps/excellent.pngbin0 -> 485 bytes
-rw-r--r--wifi/pixmaps/good.pngbin0 -> 489 bytes
-rw-r--r--wifi/pixmaps/marginal.pngbin0 -> 348 bytes
-rw-r--r--wifi/pixmaps/no_card.pngbin0 -> 4962 bytes
-rw-r--r--wifi/pixmaps/no_card.svg885
-rw-r--r--wifi/pixmaps/offline.pngbin0 -> 1041 bytes
-rw-r--r--wifi/pixmaps/oor_down.pngbin0 -> 254 bytes
-rw-r--r--wifi/pixmaps/oor_minimum.pngbin0 -> 298 bytes
-rw-r--r--wifi/speed.cpp64
-rw-r--r--wifi/speed.h34
-rw-r--r--wifi/statistics.cpp111
-rw-r--r--wifi/statistics.h33
-rw-r--r--wifi/status.cpp382
-rw-r--r--wifi/status.h43
-rw-r--r--wifi/strength.cpp166
-rw-r--r--wifi/strength.h41
4643 files changed, 691315 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..230af1f4
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,3 @@
+Look in the subdirs to get info about the authors.
+
+The package is maintained by Stephan Kulow <coolo@kde.org>
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..ded002ea
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,347 @@
+NOTE! The GPL below is copyrighted by the Free Software Foundation, but
+the instance of code that it refers to (the kde programs) are copyrighted
+by the authors who actually wrote it.
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING-DOCS b/COPYING-DOCS
new file mode 100644
index 00000000..4a0fe1c8
--- /dev/null
+++ b/COPYING-DOCS
@@ -0,0 +1,397 @@
+ GNU Free Documentation License
+ Version 1.2, November 2002
+
+
+ Copyright (C) 2000,2001,2002 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+0. PREAMBLE
+
+The purpose of this License is to make a manual, textbook, or other
+functional and useful document "free" in the sense of freedom: to
+assure everyone the effective freedom to copy and redistribute it,
+with or without modifying it, either commercially or noncommercially.
+Secondarily, this License preserves for the author and publisher a way
+to get credit for their work, while not being considered responsible
+for modifications made by others.
+
+This License is a kind of "copyleft", which means that derivative
+works of the document must themselves be free in the same sense. It
+complements the GNU General Public License, which is a copyleft
+license designed for free software.
+
+We have designed this License in order to use it for manuals for free
+software, because free software needs free documentation: a free
+program should come with manuals providing the same freedoms that the
+software does. But this License is not limited to software manuals;
+it can be used for any textual work, regardless of subject matter or
+whether it is published as a printed book. We recommend this License
+principally for works whose purpose is instruction or reference.
+
+
+1. APPLICABILITY AND DEFINITIONS
+
+This License applies to any manual or other work, in any medium, that
+contains a notice placed by the copyright holder saying it can be
+distributed under the terms of this License. Such a notice grants a
+world-wide, royalty-free license, unlimited in duration, to use that
+work under the conditions stated herein. The "Document", below,
+refers to any such manual or work. Any member of the public is a
+licensee, and is addressed as "you". You accept the license if you
+copy, modify or distribute the work in a way requiring permission
+under copyright law.
+
+A "Modified Version" of the Document means any work containing the
+Document or a portion of it, either copied verbatim, or with
+modifications and/or translated into another language.
+
+A "Secondary Section" is a named appendix or a front-matter section of
+the Document that deals exclusively with the relationship of the
+publishers or authors of the Document to the Document's overall subject
+(or to related matters) and contains nothing that could fall directly
+within that overall subject. (Thus, if the Document is in part a
+textbook of mathematics, a Secondary Section may not explain any
+mathematics.) The relationship could be a matter of historical
+connection with the subject or with related matters, or of legal,
+commercial, philosophical, ethical or political position regarding
+them.
+
+The "Invariant Sections" are certain Secondary Sections whose titles
+are designated, as being those of Invariant Sections, in the notice
+that says that the Document is released under this License. If a
+section does not fit the above definition of Secondary then it is not
+allowed to be designated as Invariant. The Document may contain zero
+Invariant Sections. If the Document does not identify any Invariant
+Sections then there are none.
+
+The "Cover Texts" are certain short passages of text that are listed,
+as Front-Cover Texts or Back-Cover Texts, in the notice that says that
+the Document is released under this License. A Front-Cover Text may
+be at most 5 words, and a Back-Cover Text may be at most 25 words.
+
+A "Transparent" copy of the Document means a machine-readable copy,
+represented in a format whose specification is available to the
+general public, that is suitable for revising the document
+straightforwardly with generic text editors or (for images composed of
+pixels) generic paint programs or (for drawings) some widely available
+drawing editor, and that is suitable for input to text formatters or
+for automatic translation to a variety of formats suitable for input
+to text formatters. A copy made in an otherwise Transparent file
+format whose markup, or absence of markup, has been arranged to thwart
+or discourage subsequent modification by readers is not Transparent.
+An image format is not Transparent if used for any substantial amount
+of text. A copy that is not "Transparent" is called "Opaque".
+
+Examples of suitable formats for Transparent copies include plain
+ASCII without markup, Texinfo input format, LaTeX input format, SGML
+or XML using a publicly available DTD, and standard-conforming simple
+HTML, PostScript or PDF designed for human modification. Examples of
+transparent image formats include PNG, XCF and JPG. Opaque formats
+include proprietary formats that can be read and edited only by
+proprietary word processors, SGML or XML for which the DTD and/or
+processing tools are not generally available, and the
+machine-generated HTML, PostScript or PDF produced by some word
+processors for output purposes only.
+
+The "Title Page" means, for a printed book, the title page itself,
+plus such following pages as are needed to hold, legibly, the material
+this License requires to appear in the title page. For works in
+formats which do not have any title page as such, "Title Page" means
+the text near the most prominent appearance of the work's title,
+preceding the beginning of the body of the text.
+
+A section "Entitled XYZ" means a named subunit of the Document whose
+title either is precisely XYZ or contains XYZ in parentheses following
+text that translates XYZ in another language. (Here XYZ stands for a
+specific section name mentioned below, such as "Acknowledgements",
+"Dedications", "Endorsements", or "History".) To "Preserve the Title"
+of such a section when you modify the Document means that it remains a
+section "Entitled XYZ" according to this definition.
+
+The Document may include Warranty Disclaimers next to the notice which
+states that this License applies to the Document. These Warranty
+Disclaimers are considered to be included by reference in this
+License, but only as regards disclaiming warranties: any other
+implication that these Warranty Disclaimers may have is void and has
+no effect on the meaning of this License.
+
+
+2. VERBATIM COPYING
+
+You may copy and distribute the Document in any medium, either
+commercially or noncommercially, provided that this License, the
+copyright notices, and the license notice saying this License applies
+to the Document are reproduced in all copies, and that you add no other
+conditions whatsoever to those of this License. You may not use
+technical measures to obstruct or control the reading or further
+copying of the copies you make or distribute. However, you may accept
+compensation in exchange for copies. If you distribute a large enough
+number of copies you must also follow the conditions in section 3.
+
+You may also lend copies, under the same conditions stated above, and
+you may publicly display copies.
+
+
+3. COPYING IN QUANTITY
+
+If you publish printed copies (or copies in media that commonly have
+printed covers) of the Document, numbering more than 100, and the
+Document's license notice requires Cover Texts, you must enclose the
+copies in covers that carry, clearly and legibly, all these Cover
+Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
+the back cover. Both covers must also clearly and legibly identify
+you as the publisher of these copies. The front cover must present
+the full title with all words of the title equally prominent and
+visible. You may add other material on the covers in addition.
+Copying with changes limited to the covers, as long as they preserve
+the title of the Document and satisfy these conditions, can be treated
+as verbatim copying in other respects.
+
+If the required texts for either cover are too voluminous to fit
+legibly, you should put the first ones listed (as many as fit
+reasonably) on the actual cover, and continue the rest onto adjacent
+pages.
+
+If you publish or distribute Opaque copies of the Document numbering
+more than 100, you must either include a machine-readable Transparent
+copy along with each Opaque copy, or state in or with each Opaque copy
+a computer-network location from which the general network-using
+public has access to download using public-standard network protocols
+a complete Transparent copy of the Document, free of added material.
+If you use the latter option, you must take reasonably prudent steps,
+when you begin distribution of Opaque copies in quantity, to ensure
+that this Transparent copy will remain thus accessible at the stated
+location until at least one year after the last time you distribute an
+Opaque copy (directly or through your agents or retailers) of that
+edition to the public.
+
+It is requested, but not required, that you contact the authors of the
+Document well before redistributing any large number of copies, to give
+them a chance to provide you with an updated version of the Document.
+
+
+4. MODIFICATIONS
+
+You may copy and distribute a Modified Version of the Document under
+the conditions of sections 2 and 3 above, provided that you release
+the Modified Version under precisely this License, with the Modified
+Version filling the role of the Document, thus licensing distribution
+and modification of the Modified Version to whoever possesses a copy
+of it. In addition, you must do these things in the Modified Version:
+
+A. Use in the Title Page (and on the covers, if any) a title distinct
+ from that of the Document, and from those of previous versions
+ (which should, if there were any, be listed in the History section
+ of the Document). You may use the same title as a previous version
+ if the original publisher of that version gives permission.
+B. List on the Title Page, as authors, one or more persons or entities
+ responsible for authorship of the modifications in the Modified
+ Version, together with at least five of the principal authors of the
+ Document (all of its principal authors, if it has fewer than five),
+ unless they release you from this requirement.
+C. State on the Title page the name of the publisher of the
+ Modified Version, as the publisher.
+D. Preserve all the copyright notices of the Document.
+E. Add an appropriate copyright notice for your modifications
+ adjacent to the other copyright notices.
+F. Include, immediately after the copyright notices, a license notice
+ giving the public permission to use the Modified Version under the
+ terms of this License, in the form shown in the Addendum below.
+G. Preserve in that license notice the full lists of Invariant Sections
+ and required Cover Texts given in the Document's license notice.
+H. Include an unaltered copy of this License.
+I. Preserve the section Entitled "History", Preserve its Title, and add
+ to it an item stating at least the title, year, new authors, and
+ publisher of the Modified Version as given on the Title Page. If
+ there is no section Entitled "History" in the Document, create one
+ stating the title, year, authors, and publisher of the Document as
+ given on its Title Page, then add an item describing the Modified
+ Version as stated in the previous sentence.
+J. Preserve the network location, if any, given in the Document for
+ public access to a Transparent copy of the Document, and likewise
+ the network locations given in the Document for previous versions
+ it was based on. These may be placed in the "History" section.
+ You may omit a network location for a work that was published at
+ least four years before the Document itself, or if the original
+ publisher of the version it refers to gives permission.
+K. For any section Entitled "Acknowledgements" or "Dedications",
+ Preserve the Title of the section, and preserve in the section all
+ the substance and tone of each of the contributor acknowledgements
+ and/or dedications given therein.
+L. Preserve all the Invariant Sections of the Document,
+ unaltered in their text and in their titles. Section numbers
+ or the equivalent are not considered part of the section titles.
+M. Delete any section Entitled "Endorsements". Such a section
+ may not be included in the Modified Version.
+N. Do not retitle any existing section to be Entitled "Endorsements"
+ or to conflict in title with any Invariant Section.
+O. Preserve any Warranty Disclaimers.
+
+If the Modified Version includes new front-matter sections or
+appendices that qualify as Secondary Sections and contain no material
+copied from the Document, you may at your option designate some or all
+of these sections as invariant. To do this, add their titles to the
+list of Invariant Sections in the Modified Version's license notice.
+These titles must be distinct from any other section titles.
+
+You may add a section Entitled "Endorsements", provided it contains
+nothing but endorsements of your Modified Version by various
+parties--for example, statements of peer review or that the text has
+been approved by an organization as the authoritative definition of a
+standard.
+
+You may add a passage of up to five words as a Front-Cover Text, and a
+passage of up to 25 words as a Back-Cover Text, to the end of the list
+of Cover Texts in the Modified Version. Only one passage of
+Front-Cover Text and one of Back-Cover Text may be added by (or
+through arrangements made by) any one entity. If the Document already
+includes a cover text for the same cover, previously added by you or
+by arrangement made by the same entity you are acting on behalf of,
+you may not add another; but you may replace the old one, on explicit
+permission from the previous publisher that added the old one.
+
+The author(s) and publisher(s) of the Document do not by this License
+give permission to use their names for publicity for or to assert or
+imply endorsement of any Modified Version.
+
+
+5. COMBINING DOCUMENTS
+
+You may combine the Document with other documents released under this
+License, under the terms defined in section 4 above for modified
+versions, provided that you include in the combination all of the
+Invariant Sections of all of the original documents, unmodified, and
+list them all as Invariant Sections of your combined work in its
+license notice, and that you preserve all their Warranty Disclaimers.
+
+The combined work need only contain one copy of this License, and
+multiple identical Invariant Sections may be replaced with a single
+copy. If there are multiple Invariant Sections with the same name but
+different contents, make the title of each such section unique by
+adding at the end of it, in parentheses, the name of the original
+author or publisher of that section if known, or else a unique number.
+Make the same adjustment to the section titles in the list of
+Invariant Sections in the license notice of the combined work.
+
+In the combination, you must combine any sections Entitled "History"
+in the various original documents, forming one section Entitled
+"History"; likewise combine any sections Entitled "Acknowledgements",
+and any sections Entitled "Dedications". You must delete all sections
+Entitled "Endorsements".
+
+
+6. COLLECTIONS OF DOCUMENTS
+
+You may make a collection consisting of the Document and other documents
+released under this License, and replace the individual copies of this
+License in the various documents with a single copy that is included in
+the collection, provided that you follow the rules of this License for
+verbatim copying of each of the documents in all other respects.
+
+You may extract a single document from such a collection, and distribute
+it individually under this License, provided you insert a copy of this
+License into the extracted document, and follow this License in all
+other respects regarding verbatim copying of that document.
+
+
+7. AGGREGATION WITH INDEPENDENT WORKS
+
+A compilation of the Document or its derivatives with other separate
+and independent documents or works, in or on a volume of a storage or
+distribution medium, is called an "aggregate" if the copyright
+resulting from the compilation is not used to limit the legal rights
+of the compilation's users beyond what the individual works permit.
+When the Document is included in an aggregate, this License does not
+apply to the other works in the aggregate which are not themselves
+derivative works of the Document.
+
+If the Cover Text requirement of section 3 is applicable to these
+copies of the Document, then if the Document is less than one half of
+the entire aggregate, the Document's Cover Texts may be placed on
+covers that bracket the Document within the aggregate, or the
+electronic equivalent of covers if the Document is in electronic form.
+Otherwise they must appear on printed covers that bracket the whole
+aggregate.
+
+
+8. TRANSLATION
+
+Translation is considered a kind of modification, so you may
+distribute translations of the Document under the terms of section 4.
+Replacing Invariant Sections with translations requires special
+permission from their copyright holders, but you may include
+translations of some or all Invariant Sections in addition to the
+original versions of these Invariant Sections. You may include a
+translation of this License, and all the license notices in the
+Document, and any Warranty Disclaimers, provided that you also include
+the original English version of this License and the original versions
+of those notices and disclaimers. In case of a disagreement between
+the translation and the original version of this License or a notice
+or disclaimer, the original version will prevail.
+
+If a section in the Document is Entitled "Acknowledgements",
+"Dedications", or "History", the requirement (section 4) to Preserve
+its Title (section 1) will typically require changing the actual
+title.
+
+
+9. TERMINATION
+
+You may not copy, modify, sublicense, or distribute the Document except
+as expressly provided for under this License. Any other attempt to
+copy, modify, sublicense or distribute the Document 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.
+
+
+10. FUTURE REVISIONS OF THIS LICENSE
+
+The Free Software Foundation may publish new, revised versions
+of the GNU Free Documentation 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. See
+http://www.gnu.org/copyleft/.
+
+Each version of the License is given a distinguishing version number.
+If the Document specifies that a particular numbered version of this
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that specified version or
+of any later version that has been published (not as a draft) by the
+Free Software Foundation. If the Document does not specify a version
+number of this License, you may choose any version ever published (not
+as a draft) by the Free Software Foundation.
+
+
+ADDENDUM: How to use this License for your documents
+
+To use this License in a document you have written, include a copy of
+the License in the document and put the following copyright and
+license notices just after the title page:
+
+ Copyright (c) YEAR YOUR NAME.
+ Permission is granted to copy, distribute and/or modify this document
+ under the terms of the GNU Free Documentation License, Version 1.2
+ or any later version published by the Free Software Foundation;
+ with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+ A copy of the license is included in the section entitled "GNU
+ Free Documentation License".
+
+If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
+replace the "with...Texts." line with this:
+
+ with the Invariant Sections being LIST THEIR TITLES, with the
+ Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
+
+If you have Invariant Sections without Cover Texts, or some other
+combination of the three, merge those two alternatives to suit the
+situation.
+
+If your document contains nontrivial examples of program code, we
+recommend releasing these examples in parallel under your choice of
+free software license, such as the GNU General Public License,
+to permit their use in free software.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 00000000..f8bad0c1
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,176 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an
+installation prefix other than `/usr/local/kde' by giving `configure'
+the option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/Mainpage.dox b/Mainpage.dox
new file mode 100644
index 00000000..54c065a0
--- /dev/null
+++ b/Mainpage.dox
@@ -0,0 +1,12 @@
+/** @mainpage KDENetwork
+*
+* KDENetwork is a collection of applications that
+* are network-oriented. This means that there
+* are mostly communications applications in here,
+* as well as some file-transfer things.
+*
+*
+* - Kopete is a multi-protocol instant message client.
+* - KSirc is an IRC client (i.e. single-protocol IM client).
+*
+*/
diff --git a/Makefile.am.in b/Makefile.am.in
new file mode 100644
index 00000000..f4fe7e70
--- /dev/null
+++ b/Makefile.am.in
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = foreign 1.6.1
+DISTCLEANFILES = inst-apps
+COMPILE_BEFORE_dcoprss= librss
+COMPILE_BEFORE_knewsticker= librss
+
+MAINTAINERCLEANFILES = subdirs configure.in acinclude.m4 SUBDIRS
+
+include admin/deps.am
+include admin/Doxyfile.am
+
diff --git a/Makefile.cvs b/Makefile.cvs
new file mode 100644
index 00000000..b4752bd8
--- /dev/null
+++ b/Makefile.cvs
@@ -0,0 +1,16 @@
+
+all:
+ @echo "This Makefile is only for the CVS repository"
+ @echo "This will be deleted before making the distribution"
+ @echo ""
+ @if test ! -d admin; then \
+ echo "Please recheckout this module!" ;\
+ echo "for cvs: use checkout once and after that update again" ;\
+ echo "for cvsup: checkout kde-common from cvsup and" ;\
+ echo " link kde-common/admin to ./admin" ;\
+ exit 1 ;\
+ fi
+ $(MAKE) -f admin/Makefile.common cvs
+
+.SILENT:
+
diff --git a/README b/README
new file mode 100644
index 00000000..aed6f722
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+In this file:
+
+* Common Mistakes
+* Debugging
+* More Info
+
+What it is
+----------
+
+* kdict: graphical client for the DICT protocol.
+* kit: AOL instant messenger client, using the TOC protocol
+* knewsticker: RDF newsticker applet
+* kpf: public fileserver applet
+* kppp: dialer and front end for pppd
+* ksirc: IRC client
+* ktalkd: talk daemon
+* kxmlrpc: KDE XmlRpc Daemon
+* lanbrowsing: lan browsing kio slave
+* krfb: Desktop Sharing server, allow others to access your desktop via VNC
+* krdc: a client for Desktop Sharing and other VNC servers
+* wifi: Wireless LAN tools
+
+Common Mistakes
+---------------
+
+If configure claims Qt cannot be found, have a look at ftp://ftp.trolltech.com
+and download the latest Qt 3.3.x release.
+
+Debugging
+---------
+
+You can use --enable-debug with the configure script, if you want to have
+debug code in your KDE apps and libs. I recommend to do this, since this
+is alpha software and this makes debugging things a whole lot easier.
+
+More Info
+---------
+
+Have a look at the individual subdirectories, if you want to know, what
+versions of apps are included.
+
+Please direct any bug reports to our bug list by visiting
+http://bugs.kde.org.
+
+General KDE discussions should go to the KDE mailing list (kde@kde.org).
diff --git a/configure.in.bot b/configure.in.bot
new file mode 100644
index 00000000..3cb0ade7
--- /dev/null
+++ b/configure.in.bot
@@ -0,0 +1,69 @@
+if test -z "$LIB_SLP"; then
+ echo ""
+ echo "You're missing OpenSLP, or the OpenSLP devel package."
+ echo "Browsing in krfb and krdc will not be possible."
+ echo "If you want browsing support in krfb, you should consider"
+ echo "installing it. "
+ echo "Have a look at http://www.openslp.org/ or find a binary"
+ echo "package for your platform."
+ echo ""
+ all_tests=bad
+fi
+
+if test "$have_ssl" != yes; then
+ echo ""
+ echo "You're missing openSSL, or your version is too old (before 0.9.5a)."
+ echo "krdc will not be compiled. If you want to use krdc, you should consider"
+ echo "installing or upgrading it."
+ echo "Have a look at http://www.openssl.org, or find a binary package for"
+ echo "your platform."
+ echo ""
+ all_tests=bad
+fi
+
+if test -z "$COMPILE_GADU"; then
+ echo ""
+ echo "You're missing libgadu or the libgadu development package."
+ echo "Kopete's Gadu-Gadu plugin will not be compiled."
+ echo "If you want Gadu-Gadu, a Polish messaging protocol, support in Kopete"
+ echo "you can download it from http://dev.null.pl/ekg/ or find a binary"
+ echo "package for your platform."
+ echo "You can find more information in ./kopete/protocols/gadu/README.gadu ."
+ echo ""
+ all_tests=bad
+fi
+
+if test "X$have_libidn" = Xno; then
+ echo ""
+ echo "You're missing libidn or the libidn development package"
+ echo "Kopete's Jabber plugin will not be compiled."
+ echo "If you want Jabber support in Kopete, You can download libidn from"
+ echo "http://www.gnu.org/software/libidn or find a binary package"
+ echo "for your platform."
+ all_tests=bad
+fi
+
+
+if test "x$with_xmms" = xcheck && test -z "$XMMS_LIBS"; then
+ echo ""
+ echo "You're missing the XMMS libraries, or the libxmms development package."
+ echo "Without libxmms Kopete's NowListening plugin won't be able to talk to"
+ echo "the XMMS music player. If you want Kopete's NowListening plugin to"
+ echo "support XMMS, have a look at http://www.xmms.org/ or find a"
+ echo "binary package for your system."
+ echo ""
+ all_tests=bad
+fi
+
+if test "x$with_wifi" = xcheck && test "$kde_libiw_installed" = "no"; then
+ echo ""
+ echo "You're missing the wireless tools libraries, or the wireless tools"
+ echo "header files. Without these, KWiFiManager and the kwireless applet"
+ echo "will not be compiled. You are unable to monitor wireless LAN"
+ echo "connections without these. If you want wireless LAN support enabled,"
+ echo "have a look at "
+ echo "http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html"
+ echo "or find a binary package for your system."
+ echo ""
+ all_tests=bad
+fi
diff --git a/configure.in.in b/configure.in.in
new file mode 100644
index 00000000..fc9ade39
--- /dev/null
+++ b/configure.in.in
@@ -0,0 +1,129 @@
+#MIN_CONFIG
+KDE_ENABLE_HIDDEN_VISIBILITY
+KDE_INIT_DOXYGEN([KDE Network API Reference], [Version $VERSION])
+
+
+dnl Checks for header files.
+AC_CHECK_HEADERS(linux/tcp.h linux/if_ppp.h)
+AC_CHECK_HEADERS(net/errno.h net/if_ppp.h)
+AC_CHECK_HEADERS(asm/param.h)
+AC_CHECK_HEADERS(sys/file.h sys/stat.h sys/time.h sys/cdefs.h sys/sockio.h)
+AC_CHECK_HEADERS(fcntl.h unistd.h fnmatch.h sysent.h strings.h paths.h)
+AC_CHECK_HEADERS(utmp.h re_comp.h getopt.h byteswap.h)
+AC_CHECK_HEADER([resolv.h],,,[#include <netinet/in.h>])
+
+
+AC_SYS_LARGEFILE
+if test "$ac_cv_sys_file_offset_bits" != no; then
+ CPPFLAGS="$CPPFLAGS -D_FILE_OFFSET_BITS=$ac_cv_sys_file_offset_bits"
+fi
+
+if test "x$ac_cv_sys_large_files" != "xno"; then
+ CPPFLAGS="$CPPFLAGS -D_LARGE_FILES=1"
+fi
+
+AC_CHECK_FUNCS(flock)
+AC_CHECK_USLEEP
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(socket fabsl strdup vsnprintf tzset)
+AC_CHECK_SETENV
+AC_CHECK_UNSETENV
+AC_CHECK_GETDOMAINNAME
+AC_CHECK_GETHOSTNAME
+AC_C_BIGENDIAN
+
+AC_CHECK_FUNC(res_init)
+if test "$ac_cv_func_res_init" = no; then
+ AC_CHECK_LIB(resolv, res_init, LIBRESOLV="-lresolv $LIBSOCKET", , $LIBSOCKET)
+fi
+AC_SUBST(LIBRESOLV)
+
+AC_CACHE_CHECK(for timezone variable, ac_cv_var_timezone,
+ AC_TRY_COMPILE([
+ #include <time.h>
+ ], [
+ timezone = 1;
+ ], ac_cv_var_timezone=yes, ac_cv_var_timezone=no))
+if test $ac_cv_var_timezone = yes; then
+ AC_DEFINE(HAVE_TIMEZONE, 1, [define if you have a timezone variable])
+fi
+AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff,
+ AC_TRY_COMPILE([
+ #include <time.h>
+ ], [
+ struct tm tm;
+ tm.tm_gmtoff = 1;
+ ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no))
+if test $ac_cv_struct_tm_gmtoff = yes; then
+ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm])
+fi
+
+# check for SLP
+dnl define the configure option that disables slp
+AC_ARG_ENABLE(slp, [ --disable-slp don't require libslp (Browsing in krfb and krdc not possible) ], with_slp=$enableval, with_slp=yes)
+if test "$with_slp" = "yes"; then
+AC_MSG_CHECKING(for SLP support)
+save_slptest_LIBS="$LIBS"
+save_slptest_LDFLAGS="$LDFLAGS"
+save_slptest_CPPFLAGS="$CPPFLAGS"
+LDFLAGS="$all_libraries $LDFLAGS"
+CPPFLAGS="$CPPFLAGS $all_includes"
+LIBS="-lslp"
+AC_TRY_LINK( [
+ #include <slp.h>
+ ],[
+ SLPOpen(0, SLP_FALSE, (SLPHandle*) 0);
+ ],[
+ AC_DEFINE(HAVE_SLP,1,[Define if SLP is available])
+ LIB_SLP="-lslp"
+ AC_MSG_RESULT(yes)
+ ],[
+ AC_MSG_RESULT(no)
+ LIB_SLP=""
+])
+CPPFLAGS=$save_slptest_CPPFLAGS
+LDFLAGS=$save_slptest_LDFLAGS
+LIBS=$save_slptest_LIBS
+fi
+AC_SUBST(LIB_SLP)
+
+KDE_CHECK_THREADING
+
+dnl For apps that NEED threads
+if test -z "$LIBPTHREAD" && test -z "$USE_THREADS"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kdict krfb krdc"
+fi
+CXXFLAGS="$CXXFLAGS $KDE_DEFAULT_CXXFLAGS"
+
+AH_VERBATIM(_osf_config_h,
+[
+#ifdef __osf__
+/* From Tom Leitner */
+#if __STDC__
+#include <stdarg.h>
+#include <stdlib.h>
+#else
+#include <varargs.h>
+#endif
+#ifndef __OSF_INCLUDED__
+#define __OSF_INCLUDED__
+#define MSG_NOSIGNAL 0
+#ifndef AF_LOCAL
+#define AF_LOCAL 1 /* is the same as AF_UNIX */
+#endif
+#ifndef herror
+#define herror(a) printf(a)
+#endif
+
+#include <sys/types.h>
+#ifdef __cplusplus
+extern "C" int sethostname (char *name, int name_len );
+extern "C" int flock(int filedes, int operation );
+#else
+int sethostname (char *name, int name_len );
+int flock(int filedes, int operation );
+#endif
+#endif
+#endif
+])
diff --git a/dcoprss/Makefile.am b/dcoprss/Makefile.am
new file mode 100644
index 00000000..2bc93d67
--- /dev/null
+++ b/dcoprss/Makefile.am
@@ -0,0 +1,28 @@
+bin_PROGRAMS = rssservice rssclient feedbrowser
+INCLUDES = -I$(top_srcdir) $(all_includes)
+
+rssservice_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+rssservice_LDADD = $(LIB_KIO) ../librss/librss.la
+rssservice_SOURCES = main.cpp service.cpp query.cpp document.cpp article.cpp query.skel service.skel xmlrpciface.cpp cache.cpp
+
+
+# client stuff
+rssclient_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+rssclient_LDADD = $(LIB_KDECORE)
+rssclient_SOURCES = client.cpp
+
+feedbrowser_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+feedbrowser_LDADD = $(LIB_KDEUI)
+feedbrowser_SOURCES = feedbrowser.skel feedbrowser.cpp
+
+noinst_HEADERS = service.h query.h xmlrpciface.h cache.h
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/dcoprss.pot
+
+
+service_DATA = rssservice.desktop
+servicedir = $(kde_servicesdir)
+
diff --git a/dcoprss/article.cpp b/dcoprss/article.cpp
new file mode 100644
index 00000000..d191bd74
--- /dev/null
+++ b/dcoprss/article.cpp
@@ -0,0 +1,49 @@
+/* $Id$ */
+/***************************************************************************
+ article.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <kdebug.h>
+#include <kurl.h>
+#include "service.h"
+
+RSSArticle::RSSArticle(Article *art) :
+ DCOPObject(), m_Art(art)
+{
+ kdDebug() << "New article..." << endl;
+ kdDebug() << m_Art->link().prettyURL() << endl;
+}
+
+RSSArticle::~RSSArticle()
+{
+ kdDebug() << "Article going away..." << endl;
+ delete m_Art;
+}
+
+QString RSSArticle::title()
+{
+ //kdDebug() << "Get title " << m_Art->title() << endl;
+ return m_Art->title();
+}
+
+QString RSSArticle::description()
+{
+ return m_Art->description();
+}
+
+QString RSSArticle::link()
+{
+ return m_Art->link().prettyURL();
+}
diff --git a/dcoprss/cache.cpp b/dcoprss/cache.cpp
new file mode 100644
index 00000000..9c80a9a3
--- /dev/null
+++ b/dcoprss/cache.cpp
@@ -0,0 +1,129 @@
+/*
+ * cache.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "cache.h"
+#include "xmlrpciface.h"
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <qdatastream.h>
+#include <qfile.h>
+
+bool CacheEntry::isValid() const
+{
+ // Cache entries get invalid after on hour. One shouldn't hardcode this
+ // but for now it'll do.
+ return m_timeStamp.secsTo( QDateTime::currentDateTime() ) < 3600;
+}
+
+Cache *Cache::m_instance = 0;
+
+Cache &Cache::self()
+{
+ if ( !m_instance )
+ m_instance = new Cache;
+ return *m_instance;
+}
+
+QString Cache::getCacheKey( const QString &server, const QString &method,
+ const QValueList<QVariant> &args )
+{
+ QString key;
+ key = server + QString::fromLatin1( "__" );
+ key += method + QString::fromLatin1( "__" );
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ key += KXMLRPC::Query::marshal( *it );
+
+ return key;
+}
+
+Cache::Cache()
+{
+ load();
+}
+
+Cache::~Cache()
+{
+ save();
+}
+
+void Cache::load()
+{
+ QFile file( cacheFileName() );
+ if ( !file.open( IO_ReadOnly ) ) {
+ kdDebug() << "Failed to open cache file " << cacheFileName() << endl;
+ return;
+ }
+
+ QDataStream stream( &file );
+ while ( !stream.atEnd() ) {
+ QString key;
+ stream >> key;
+
+ CacheEntry *entry = new CacheEntry;
+ stream >> *entry;
+
+ QDict<CacheEntry>::insert( key, entry );
+ }
+}
+
+void Cache::save()
+{
+ QFile file( cacheFileName() );
+ if ( !file.open( IO_WriteOnly ) ) {
+ kdDebug() << "Failed to open cache file " << cacheFileName() << endl;
+ return;
+ }
+
+ QDataStream stream( &file );
+
+ QDictIterator<CacheEntry> it( *this );
+ for ( ; it.current() != 0; ++it )
+ stream << it.currentKey() << *it.current();
+}
+
+void Cache::touch( const QString &key )
+{
+ CacheEntry *entry = find( key );
+ if ( !entry )
+ return;
+ entry->m_timeStamp = QDateTime::currentDateTime();
+}
+
+void Cache::insert( const QString &key, const KXMLRPC::Query::Result &result )
+{
+ CacheEntry *entry = new CacheEntry;
+ entry->m_timeStamp = QDateTime::currentDateTime();
+ entry->m_result = result;
+ QDict<CacheEntry>::insert( key, entry );
+}
+
+QString Cache::cacheFileName() const
+{
+ return locateLocal( "appdata", "cache/dcoprss.cache" );
+}
+
diff --git a/dcoprss/cache.h b/dcoprss/cache.h
new file mode 100644
index 00000000..8248b609
--- /dev/null
+++ b/dcoprss/cache.h
@@ -0,0 +1,86 @@
+/*
+ * cache.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef CACHE_H
+#define CACHE_H
+
+#include <qcstring.h>
+#include <qdatetime.h>
+#include <qdict.h>
+
+#include <xmlrpciface.h>
+
+class CacheEntry
+{
+ friend class Cache;
+ friend QDataStream &operator>>( QDataStream &s, CacheEntry &e );
+ public:
+ const QDateTime &timeStamp() const { return m_timeStamp; }
+ const KXMLRPC::Query::Result result() const { return m_result; }
+ bool isValid() const;
+
+ private:
+ QDateTime m_timeStamp;
+ KXMLRPC::Query::Result m_result;
+};
+
+class Cache : public QDict<CacheEntry>
+{
+ public:
+ static Cache &self();
+
+ static QString getCacheKey( const QString &server,
+ const QString &method,
+ const QValueList<QVariant> &args );
+
+ void load();
+ void save();
+
+ void touch( const QString &key );
+
+ void insert( const QString &key, const KXMLRPC::Query::Result &result );
+
+ private:
+ Cache();
+ Cache( const Cache &rhs ); // disabled
+ Cache &operator=( const Cache &rhs ); // disabled
+ ~Cache();
+
+ QString cacheFileName() const;
+
+ static Cache *m_instance;
+};
+
+inline QDataStream &operator<<( QDataStream &s, const CacheEntry &e )
+{
+ return s << e.timeStamp() << e.result();
+}
+
+inline QDataStream &operator>>( QDataStream &s, CacheEntry &e )
+{
+ return s >> e.m_timeStamp >> e.m_result;
+}
+
+#endif // CACHE_H
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/client.cpp b/dcoprss/client.cpp
new file mode 100644
index 00000000..b74894de
--- /dev/null
+++ b/dcoprss/client.cpp
@@ -0,0 +1,75 @@
+/* $Id$ */
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <qdatastream.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+#include <kdebug.h>
+#include <stdlib.h>
+/*
+class rssIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+
+ rssIface( KApplication *app)
+ {
+ // get our DCOP client and attach so that we may use it
+ DCOPClient *client = app->dcopClient();
+ client->attach();
+ QString error;
+ QCString appID;
+ kdDebug() << "Looking for rss service..." << endl;
+ if (!client->isApplicationRegistered("rssservice"))
+ {
+ kdDebug() << "Could not find service so I am starting it..." << endl;
+ if(KApplication::startServiceByName("rssservice",QStringList(), &error, &appID ))
+ {
+ kdDebug() << "Starting rssservice failed with message: " << error << endl;
+ exit(0);
+ }
+ }
+ kdDebug ()<< "Accessing rssservice..." << endl;
+
+ if (!connectDCOPSignal(0,0, "documentUpdated(DCOPRef)",
+ "refresh(DCOPRef)",false))
+ kdDebug() << "Could not attach signal..." << endl;
+ else
+ kdDebug() << "attached dcop signals..." << endl;
+
+ QString url("http://freshmeat.net/backend/fm.rdf");
+ DCOPRef m_rssservice("rssservice","RSSService");
+ m_rssservice.call("load(QString)", url);
+ QStringList returnList = m_rssservice.call("list()");
+ DCOPRef doc = m_rssservice.call("document(QString)", returnList[0]);
+ QString title = doc.call("title()");
+ QString link = doc.call("link()");
+ QString description = doc.call("description()");
+ kdDebug() << title << endl;
+ kdDebug() << link << endl;
+ kdDebug() << description << endl;
+ }
+
+ k_dcop:
+ virtual void refresh(DCOPRef doc)
+ {
+ QString title = doc.call("title()");
+ QString link = doc.call("link()");
+ QString description = doc.call("description()");
+ kdDebug() << title << endl;
+ kdDebug() << link << endl;
+ kdDebug() << description << endl;
+ }
+
+ private:
+
+};
+*/
+int main(int argc, char **argv)
+{
+ KApplication *app = new KApplication(argc, argv, "client", false);
+
+ app->exec();
+}
diff --git a/dcoprss/document.cpp b/dcoprss/document.cpp
new file mode 100644
index 00000000..b43f600e
--- /dev/null
+++ b/dcoprss/document.cpp
@@ -0,0 +1,307 @@
+/* $Id$ */
+/***************************************************************************
+ document.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <kdebug.h>
+#include <qdatetime.h>
+#include <kurl.h>
+#include "service.h"
+
+RSSDocument::RSSDocument(const QString& url) :
+ QObject(), DCOPObject(), m_Url(url)
+{
+
+ m_list.setAutoDelete( true );
+ m_Doc = 0L;
+ m_pix = QPixmap();
+ m_isLoading = false;
+ m_maxAge = 60;
+ m_Timeout = QDateTime::currentDateTime();
+ m_state.clear();
+}
+
+RSSDocument::~RSSDocument()
+{
+ kdDebug() << "Document going away..." << endl;
+
+ delete m_Doc;
+}
+
+void RSSDocument::loadingComplete(Loader *ldr, Document doc, Status stat)
+{
+
+
+ if( m_Doc != 0L)
+ {
+ delete m_Doc;
+
+ }
+
+ if (stat != RSS::Success)
+ {
+ kdDebug() << "Document error! Loader:" << ldr->errorCode() << " Parser:" << stat << endl;
+
+ m_isLoading = false;
+ m_Doc = 0L;
+ if( stat == RSS::ParseError )
+ documentUpdateError(DCOPRef(this), 1);
+ else if( stat == RSS::RetrieveError )
+ documentUpdateError(DCOPRef(this), 2);
+ else
+ documentUpdateError(DCOPRef(this), 3);
+ }
+ else
+ {
+ kdDebug() << "New Document is done..." << endl;
+ m_Doc = new Document(doc);
+ m_list.clear();
+ Article::List list = doc.articles();
+ for(Article::List::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ int state = m_state[(*it).title()];
+ if( state == 0 ) m_state[(*it).title()] = 1; // new
+ else if( state == 1 ) m_state[(*it).title()] = 2; // old message now
+ m_list.append( new RSSArticle( new Article(*it)));
+ }
+ Image *img = m_Doc->image();
+ if ( img )
+ {
+ connect(img, SIGNAL(gotPixmap(const QPixmap &)),
+ SLOT(pixmapLoaded(const QPixmap &)));
+ img->getPixmap();
+ pixmapUpdating(DCOPRef(this));
+ }
+ m_isLoading = false;
+ documentUpdated(DCOPRef(this));
+
+ kdDebug() << "Old Mod time " << m_Timeout.toString() << endl;
+ m_Timeout = m_Timeout.addSecs(m_maxAge * 60 );
+ kdDebug() << "New Mod time " << m_Timeout.toString() << endl;
+
+ }
+}
+
+void RSSDocument::pixmapLoaded(const QPixmap &pix )
+{
+ m_pix = pix;
+ pixmapUpdated(DCOPRef(this));
+}
+
+QString RSSDocument::webMaster()
+{
+ if( m_Doc != 0L)
+ return m_Doc->webMaster();
+ else
+ return "";
+}
+
+QString RSSDocument::managingEditor()
+{
+ if( m_Doc != 0L)
+ return m_Doc->managingEditor();
+ else
+ return "";
+}
+
+QString RSSDocument::rating()
+{
+ if( m_Doc != 0L)
+ return m_Doc->rating();
+ else
+ return "";
+}
+
+QDateTime RSSDocument::lastBuildDate()
+{
+ if( m_Doc != 0L)
+ return m_Doc->lastBuildDate();
+ else
+ return QDateTime::currentDateTime();
+}
+
+QDateTime RSSDocument::pubDate()
+{
+ if( m_Doc != 0L)
+ return m_Doc->pubDate();
+ else
+ return QDateTime::currentDateTime();
+}
+
+QString RSSDocument::copyright()
+{
+ if( m_Doc != 0L)
+ return m_Doc->copyright();
+ else
+ return "";
+}
+
+QStringList RSSDocument::articles()
+{
+ if( m_Doc != 0L)
+ {
+ kdDebug() << "Document giving articles..." << endl;
+ Article::List list = m_Doc->articles();
+ QStringList stringList;
+
+ for(Article::List::ConstIterator it = list.begin(); it != list.end(); ++it)
+ stringList.append((*it).title());
+ return stringList;
+ }
+ else
+ return QStringList();
+}
+
+DCOPRef RSSDocument::article(int idx)
+{
+ if(m_list.at(idx))
+ return DCOPRef(m_list.at(idx));
+ else
+ return DCOPRef();
+}
+
+int RSSDocument::count()
+{
+ if( m_Doc != 0L)
+ return m_Doc->articles().count();
+ return 0;
+}
+
+QString RSSDocument::link()
+{
+ if( m_Doc != 0L)
+ return m_Doc->link().prettyURL();
+ else
+ return "";
+}
+
+QString RSSDocument::description()
+{
+ if( m_Doc != 0L)
+ return m_Doc->description();
+ else
+ return "";
+}
+
+QString RSSDocument::title()
+{
+ if( m_Doc != 0L)
+ return m_Doc->title();
+ else
+ return "";
+}
+
+QString RSSDocument::verbVersion()
+{
+ if( m_Doc != 0L)
+ return m_Doc->verbVersion();
+ else
+ return "";
+}
+
+QString RSSDocument::pixmapURL()
+{
+ if( m_Doc != 0L)
+ if( m_Doc->image() )
+ return m_Doc->image()->url().prettyURL();
+ else
+ return "";
+ else
+ return "";
+}
+
+QPixmap RSSDocument::pixmap()
+{
+ return m_pix;
+}
+
+bool RSSDocument::documentValid()
+{
+ if (m_Doc != 0L)
+ return true;
+ else
+ return false;
+}
+
+bool RSSDocument::pixmapValid()
+{
+ return !m_pix.isNull();
+}
+
+void RSSDocument::refresh()
+{
+ kdDebug() << "Mod time " << m_Timeout.toString() << endl;
+ kdDebug() << "Current time " << QDateTime::currentDateTime().toString() << endl;
+
+ if(!m_isLoading && (QDateTime::currentDateTime() >= m_Timeout))
+ {
+ kdDebug() << "Document going to refresh" << endl;
+ m_isLoading = true;
+ Loader *loader = Loader::create(this,
+ SLOT(loadingComplete(Loader *, Document, Status)));
+ loader->loadFrom(KURL( m_Url ), new FileRetriever());
+ documentUpdating(DCOPRef(this));
+ }
+ else
+ {
+ documentUpdated(DCOPRef(this));
+ if(pixmapValid())
+ pixmapUpdated(DCOPRef(this));
+ /*
+ else
+ {
+ // Refactor this!
+ Image *img = m_Doc->image();
+ if ( img )
+ {
+ connect(img, SIGNAL(gotPixmap(const QPixmap &)),
+ SLOT(pixmapLoaded(const QPixmap &)));
+ img->getPixmap();
+ pixmapUpdating(DCOPRef(this));
+ }
+ }
+ */
+ }
+
+}
+
+int RSSDocument::maxAge()
+{
+ return m_maxAge;
+}
+
+void RSSDocument::setMaxAge(int _min)
+{
+ m_Timeout.addSecs(-m_maxAge);
+ m_maxAge = _min;
+ m_Timeout.addSecs(m_maxAge);
+}
+
+int RSSDocument::state( const QString &title) const
+{
+ return m_state[title];
+}
+
+void RSSDocument::setState( const QString &title, int s )
+{
+ m_state[title] = s;
+}
+
+void RSSDocument::read( const QString &title)
+{
+ m_state[title] = 3;
+}
+
+#include "service.moc"
diff --git a/dcoprss/feedbrowser.cpp b/dcoprss/feedbrowser.cpp
new file mode 100644
index 00000000..59a55040
--- /dev/null
+++ b/dcoprss/feedbrowser.cpp
@@ -0,0 +1,144 @@
+#include "feedbrowser.h"
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <dcopclient.h>
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qvbox.h>
+
+CategoryItem::CategoryItem( KListView *parent, const QString &category )
+ : KListViewItem( parent ),
+ m_category( category )
+{
+ init();
+}
+
+CategoryItem::CategoryItem( KListViewItem *parent, const QString &category )
+ : KListViewItem( parent ),
+ m_category( category )
+{
+ init();
+}
+
+void CategoryItem::init()
+{
+ m_populated = false;
+ m_dcopIface = 0;
+
+ setText( 0, m_category.mid( m_category.findRev( '/' ) + 1 ).replace( '_', ' ' ) );
+}
+
+void CategoryItem::setOpen( bool open )
+{
+ if ( open && !m_populated ) {
+ populate();
+ m_populated = true;
+ }
+ KListViewItem::setOpen( open );
+}
+
+void CategoryItem::populate()
+{
+ m_dcopIface = new DCOPRSSIface( this, "m_dcopIface" );
+ connect( m_dcopIface, SIGNAL( gotCategories( const QStringList & ) ),
+ this, SLOT( gotCategories( const QStringList & ) ) );
+ m_dcopIface->getCategories( m_category );
+}
+
+void CategoryItem::gotCategories( const QStringList &categories )
+{
+ delete m_dcopIface;
+ m_dcopIface = 0;
+
+ QStringList::ConstIterator it = categories.begin();
+ QStringList::ConstIterator end = categories.end();
+ for ( ; it != end; ++it )
+ new CategoryItem( this, *it );
+
+ if ( !categories.isEmpty() )
+ KListViewItem::setOpen( true );
+}
+
+DCOPRSSIface::DCOPRSSIface( QObject *parent, const char *name ) :
+ QObject( parent, name ), DCOPObject( "FeedBrowser" )
+{
+ connectDCOPSignal( "rssservice", "RSSQuery", "gotCategories(QStringList)",
+ "slotGotCategories(QStringList)", false );
+}
+
+void DCOPRSSIface::getCategories( const QString &cat )
+{
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly );
+ stream << cat;
+ kapp->dcopClient()->send( "rssservice", "RSSQuery",
+ "getCategories(QString)", data );
+}
+
+void DCOPRSSIface::slotGotCategories( const QStringList &categories )
+{
+ emit gotCategories( categories );
+}
+
+FeedBrowserDlg::FeedBrowserDlg( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "DCOPRSS Feed Browser" ),
+ Close, Close, true )
+{
+ m_dcopIface = new DCOPRSSIface( this, "m_dcopIface" );
+ connect( m_dcopIface, SIGNAL( gotCategories( const QStringList & ) ),
+ this, SLOT( gotTopCategories( const QStringList & ) ) );
+
+ QVBox *mainWidget = makeVBoxMainWidget();
+
+ m_feedList = new KListView( mainWidget, "m_feedList" );
+ m_feedList->setAllColumnsShowFocus( true );
+ m_feedList->setRootIsDecorated( true );
+ m_feedList->addColumn( i18n( "Name" ) );
+ connect( m_feedList, SIGNAL( executed( QListViewItem * ) ),
+ this, SLOT( itemSelected( QListViewItem * ) ) );
+ connect( m_feedList, SIGNAL( returnPressed( QListViewItem * ) ),
+ this, SLOT( itemSelected( QListViewItem * ) ) );
+
+ resize( 500, 400 );
+
+ getTopCategories();
+}
+
+void FeedBrowserDlg::getTopCategories()
+{
+ m_dcopIface->getCategories( "Top" );
+}
+
+void FeedBrowserDlg::gotTopCategories( const QStringList &categories )
+{
+ QStringList::ConstIterator it = categories.begin();
+ QStringList::ConstIterator end = categories.end();
+ for ( ; it != end; ++it )
+ new CategoryItem( m_feedList, *it );
+}
+
+void FeedBrowserDlg::itemSelected( QListViewItem *item )
+{
+ item->setOpen( !item->isOpen() );
+}
+
+int main( int argc, char **argv )
+{
+ KGlobal::locale()->setMainCatalogue( "dcoprss" );
+ KAboutData aboutData( "feedbrowser", I18N_NOOP( "Feed Browser" ), "0.1" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KApplication app;
+ FeedBrowserDlg *dlg = new FeedBrowserDlg( 0 );
+ app.setMainWidget( dlg );
+ dlg->show();
+ return app.exec();
+}
+
+#include "feedbrowser.moc"
diff --git a/dcoprss/feedbrowser.h b/dcoprss/feedbrowser.h
new file mode 100644
index 00000000..829ecd5e
--- /dev/null
+++ b/dcoprss/feedbrowser.h
@@ -0,0 +1,66 @@
+#ifndef FEEDBROWSER_H
+#define FEEDBROWSER_H
+
+#include <qobject.h>
+#include <dcopobject.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class DCOPRSSIface : public QObject, public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+ public:
+ DCOPRSSIface( QObject *parent, const char *name = 0 );
+
+ k_dcop:
+ void slotGotCategories( const QStringList &categories );
+
+ public slots:
+ void getCategories( const QString &cat = "Top" );
+
+ signals:
+ void gotCategories( const QStringList &categories );
+};
+
+class CategoryItem : public QObject, public KListViewItem
+{
+ Q_OBJECT
+ public:
+ CategoryItem( KListView *parent, const QString &category );
+ CategoryItem( KListViewItem *parent, const QString &category );
+
+ virtual void setOpen( bool open );
+
+ private slots:
+ void gotCategories( const QStringList &categories );
+
+ private:
+ void populate();
+ void init();
+
+ QString m_category;
+ bool m_populated;
+ DCOPRSSIface *m_dcopIface;
+};
+
+class FeedBrowserDlg : public KDialogBase
+{
+ Q_OBJECT
+ friend class CategoryItem;
+ public:
+ FeedBrowserDlg( QWidget *parent, const char *name = 0 );
+
+ private slots:
+ void itemSelected( QListViewItem *item );
+ void gotTopCategories( const QStringList &categories );
+
+ private:
+ void getTopCategories();
+
+ DCOPRSSIface *m_dcopIface;
+ KListView *m_feedList;
+};
+
+#endif // FEEDBROWSER_H
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/main.cpp b/dcoprss/main.cpp
new file mode 100644
index 00000000..fcebf8c0
--- /dev/null
+++ b/dcoprss/main.cpp
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+#include <kuniqueapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include "service.h"
+#include "query.h"
+
+int main (int argc, char *argv[])
+{
+ KLocale::setMainCatalogue("dcoprss");
+ KAboutData aboutdata("rssservice", I18N_NOOP("KDE RSS Service"),
+ "0.8", I18N_NOOP("A RSS data service."),
+ KAboutData::License_GPL, "(C) 2003, Ian Reinhart Geiser");
+ aboutdata.addAuthor("Ian Reinhart Geiser",I18N_NOOP("Developer"),"geiseri@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutdata );
+ // KCmdLineArgs::addCmdLineOptions( options );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!KUniqueApplication::start())
+ {
+ kdDebug() << "rssservice is already running!" << endl;
+ return (0);
+ }
+
+ KUniqueApplication app;
+ kdDebug() << "starting rssservice " << endl;
+ // This app is started automatically, no need for session management
+ app.disableSessionManagement();
+ RSSService *service = new RSSService;
+ QueryService *query = new QueryService(service);
+
+ return app.exec();
+}
diff --git a/dcoprss/query.cpp b/dcoprss/query.cpp
new file mode 100644
index 00000000..b2c29fdf
--- /dev/null
+++ b/dcoprss/query.cpp
@@ -0,0 +1,271 @@
+/* $Id$ */
+
+/***************************************************************************
+ query.cpp - A query interface to select RSS feeds.
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include "cache.h"
+#include "query.h"
+
+#include <kdebug.h>
+#include <krfcdate.h>
+
+#include "service.h"
+#include "xmlrpciface.h"
+
+using KXMLRPC::Server;
+
+void SlotCaller::call( QObject *object, const char *slot,
+ const KXMLRPC::Query::Result &result )
+{
+ SlotCaller caller;
+ connect( &caller, SIGNAL( signal( const KXMLRPC::Query::Result &) ),
+ object, slot );
+ emit caller.signal( result );
+}
+
+QueryService::QueryService( RSSService *service ) : QObject(), DCOPObject( "RSSQuery" ),
+ m_service( service )
+{
+ m_xmlrpcServer = new KXMLRPC::Server( KURL( "http://www.syndic8.com/xmlrpc.php"), this );
+}
+
+QStringList QueryService::listActive()
+{
+ if ( !m_service )
+ return QStringList();
+ return m_service->list();
+}
+
+void QueryService::cachedCall( const QString &method,
+ const QValueList<QVariant> &args,
+ const char *slot )
+{
+ kdDebug() << "Calling " << method << endl;
+
+ const QString cacheKey = Cache::getCacheKey( m_xmlrpcServer->url().url(),
+ method, args );
+
+ CacheEntry *cacheEntry = Cache::self().find( cacheKey );
+ if ( cacheEntry != 0 && cacheEntry->isValid() ) {
+ kdDebug() << "Using cached result." << endl;
+ SlotCaller::call( this, slot, cacheEntry->result() );
+ } else {
+ kdDebug() << "No cached result found, querying server." << endl;
+ m_xmlrpcServer->call( method, args, this, slot );
+ }
+}
+
+void QueryService::updateCache( const KXMLRPC::Query::Result &result )
+{
+ const QString cacheKey = Cache::getCacheKey( result.server(),
+ result.method(),
+ result.args() );
+
+ CacheEntry *cacheEntry = Cache::self().find( cacheKey );
+ if ( cacheEntry == 0 ) {
+ kdDebug() << "Inserting returned result into cache." << endl;
+ Cache::self().insert( cacheKey, result );
+ }
+}
+
+void QueryService::findFeeds( const QString &query )
+{
+ kdDebug() << "QueryService::findFeeds()" << endl;
+
+ QStringList args;
+ args << query << "headlines_rank";
+
+ cachedCall( "syndic8.FindFeeds", Server::toVariantList( args ),
+ SLOT( slotFoundFeeds( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::findSites( const QString& query )
+{
+ kdDebug() << "QueryService::findSites()" << endl;
+
+ cachedCall( "syndic8.FindSites", Server::toVariantList( query ),
+ SLOT( slotFoundFeeds( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::getFeedInfo( const QVariant& ids )
+{
+ kdDebug() << "QueryService::getFeedInfo()" << endl;
+
+ cachedCall( "syndic8.GetFeedInfo", Server::toVariantList( ids ),
+ SLOT( slotGotFeedInfo( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::getCategories( const QString &category )
+{
+ kdDebug() << "QueryService::getCategories()" << endl;
+
+ if ( category == "Top" ) {
+ cachedCall( "syndic8.GetCategoryRoots", Server::toVariantList( QString::fromLatin1( "DMOZ" ) ),
+ SLOT( slotGotCategories( const KXMLRPC::Query::Result & ) ) );
+ } else {
+ QStringList args;
+ args << "DMOZ" << category;
+ cachedCall( "syndic8.GetCategoryChildren", Server::toVariantList( args ),
+ SLOT( slotGotCategories( const KXMLRPC::Query::Result & ) ) );
+ }
+}
+
+void QueryService::getFeedsInCategory( const QString &category )
+{
+ kdDebug() << "QueryService::getFeedsInCategory()" << endl;
+
+ QStringList args;
+ args << "DMOZ" << category;
+
+ cachedCall( "syndic8.GetFeedsInCategory", Server::toVariantList( args ),
+ SLOT( slotGotFeedsInCategory( const KXMLRPC::Query::Result & ) ) );
+}
+
+void QueryService::slotFoundFeeds( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotFoundFeeds()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to query for feeds: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QValueList<int> ids;
+
+ const QValueList<QVariant> values = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = values.begin();
+ QValueList<QVariant>::ConstIterator end = values.end();
+ for ( ; it != end; ++it ) {
+ ids << ( *it ).toInt();
+ kdDebug() << "Found feed #" << ( *it ).toInt() << endl;
+ }
+ feedIds( ids );
+}
+
+void QueryService::slotGotFeedInfo( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotFeedInfo()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get feed info: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QMap<QString, QString> links;
+ QValueList<RSSNewsFeed> feeds;
+
+ const QValueList<QVariant> feedInfos = result.data();
+ QValueList<QVariant>::ConstIterator it = feedInfos.begin();
+ QValueList<QVariant>::ConstIterator end = feedInfos.end();
+ for ( ; it != end; ++it ) {
+ const QMap<QString, QVariant> feedInfo = ( *it ).toMap();
+
+ const QString name = feedInfo[ "sitename" ].toString();
+ const QString link = feedInfo[ "dataurl" ].toString();
+ links[ name ] = link;
+
+ RSSNewsFeed feed;
+ feed.m_id = feedInfo[ "feedid" ].toUInt();
+ feed.m_name = feedInfo[ "sitename" ].toString();
+ feed.m_homePage = feedInfo[ "siteurl" ].toString();
+ feed.m_sourceFile = feedInfo[ "dataurl" ].toString();
+ feed.m_imageUrl = feedInfo[ "imageurl" ].toString();
+ feed.m_webmaster = feedInfo[ "webmaster" ].toString();
+ feed.m_editor = feedInfo[ "editor" ].toString();
+ feed.m_publisher = feedInfo[ "publisher" ].toString();
+ feed.m_creator = feedInfo[ "creator" ].toString();
+ QDateTime dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_created" ].toString() ) );
+ feed.m_dateCreated = dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_approved" ].toString() ) );
+ feed.m_dateApproved = dateTime;
+ dateTime.setTime_t( KRFCDate::parseDate( feedInfo[ "date_xml_changed" ].toString() ) );
+ feed.m_dateXmlChanged = dateTime;
+ feed.m_fetchable = feedInfo[ "fetchable" ].toBool();
+ feed.m_description = feedInfo[ "description" ].toString();
+ feed.m_origin = feedInfo[ "origin" ].toString();
+ feed.m_languageCode = feedInfo[ "lang_code" ].toString();
+ feed.m_status = feedInfo[ "status" ].toString();
+ feed.m_version = feedInfo[ "rss_version" ].toString();
+ feed.m_views = feedInfo[ "views" ].toUInt();
+ feed.m_headlinesPerDay = feedInfo[ "headlines_per_day" ].toUInt();
+ feed.m_headlinesRank = feedInfo[ "headlines_rank" ].toUInt();
+ feed.m_toolkit = feedInfo[ "toolkit" ].toString();
+ feed.m_toolkitVersion = feedInfo[ "toolkit_version" ].toString();
+ feed.m_pollingInterval = feedInfo[ "cur_polling_interval" ].toUInt();
+ dateTime.setTime_t( feedInfo[ "last_poll_time" ].toUInt() );
+ feed.m_lastPoll = dateTime;
+ // ### feed.m_categories missing here!
+
+ feeds << feed;
+
+ kdDebug() << "Retrieved data for newsfeed '" << name << "' <" << link << ">" << endl;
+ }
+
+ feedInfo( links );
+ feedInfo( feeds );
+}
+
+void QueryService::slotGotCategories( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotCategories()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get the list of categories: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QStringList categories;
+
+ const QValueList<QVariant> cats = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = cats.begin();
+ QValueList<QVariant>::ConstIterator end = cats.end();
+ for ( ; it != end; ++it )
+ categories << ( *it ).toString();
+
+ kdDebug() << "Got categories: " << categories.join( ", " ) << endl;
+ gotCategories( categories );
+
+}
+
+void QueryService::slotGotFeedsInCategory( const KXMLRPC::Query::Result &result )
+{
+ kdDebug() << "QueryService::slotGotFeedsInCategory()" << endl;
+ if ( !result.success() ) {
+ kdWarning() << "Failed to get the feeds in the given category: " << result.errorString() << endl;
+ return;
+ }
+
+ updateCache( result );
+
+ QValueList<int> ids;
+
+ const QValueList<QVariant> values = result.data()[ 0 ].toList();
+ QValueList<QVariant>::ConstIterator it = values.begin();
+ QValueList<QVariant>::ConstIterator end = values.end();
+ for ( ; it != end; ++it ) {
+ ids << ( *it ).toInt();
+ kdDebug() << "Got feed in category: #" << ( *it ).toInt() << endl;
+ }
+
+ gotFeedsInCategory( ids );
+}
+
+#include "query.moc"
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/query.h b/dcoprss/query.h
new file mode 100644
index 00000000..d87e0505
--- /dev/null
+++ b/dcoprss/query.h
@@ -0,0 +1,120 @@
+/* $Id$ */
+#ifndef _QUERY_SERVICE
+#define _QUERY_SERVICE
+
+/***************************************************************************
+ query.h - A query interface to select RSS feeds.
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "rssnewsfeed.h"
+#include "xmlrpciface.h"
+
+#include <dcopobject.h>
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qvariant.h>
+
+class RSSService;
+
+/**
+ * Helper class which just calls the slot given it's QObject/const char*
+ * representation.
+ */
+class SlotCaller : public QObject
+{
+ Q_OBJECT
+ public:
+ static void call( QObject *object, const char *slot,
+ const KXMLRPC::Query::Result &value );
+
+ signals:
+ void signal( const KXMLRPC::Query::Result &value );
+
+ private:
+ SlotCaller() { }
+};
+
+class QueryService : public QObject, public DCOPObject
+{
+ K_DCOP
+ Q_OBJECT
+ public:
+ QueryService( RSSService *service );
+
+ k_dcop_signals:
+ void feedIds( QValueList<int> ids );
+ void feedInfo(QMap<QString, QString> links);
+ void feedInfo(QValueList<RSSNewsFeed> feeds);
+ void gotCategories( const QStringList &categories );
+ void gotFeedsInCategory( const QValueList<int> &ids );
+
+ k_dcop:
+ /**
+ * Lists the active feeds in use...
+ **/
+ QStringList listActive(); // just for testing...
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * string. The RSS ids are treturned in a integer list.
+ **/
+ void findFeeds( const QString& query );
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * string and matches it against the SiteURL feed of
+ * each feed in the feed list. The RSS ids are treturned
+ * in a integer list.
+ **/
+ void findSites( const QString& query );
+
+ /**
+ * Query the www.syndic8.com XML-RPC interface for the
+ * requested RSS feed(s). Returned is a QMap with the format
+ * of Name (Site URL), RSS URL
+ **/
+ void getFeedInfo( const QVariant& ids );
+
+ /**
+ * Returns the list of subcategories in the specified category.
+ * If no "Top" is specified, the root categories are returned.
+ */
+ void getCategories( const QString &category );
+
+ /**
+ * Queries the database for the list of needsfeed ID's which are
+ * associated with the given category.
+ */
+ void getFeedsInCategory( const QString &category );
+
+
+ private slots:
+ void slotFoundFeeds( const KXMLRPC::Query::Result &result );
+ void slotGotFeedInfo( const KXMLRPC::Query::Result &result );
+ void slotGotCategories( const KXMLRPC::Query::Result &result );
+ void slotGotFeedsInCategory( const KXMLRPC::Query::Result &result );
+
+ private:
+ void cachedCall( const QString &method, const QValueList<QVariant> &args,
+ const char *slot );
+ void updateCache( const KXMLRPC::Query::Result &result );
+
+ RSSService *m_service;
+ KXMLRPC::Server *m_xmlrpcServer;
+};
+#endif
diff --git a/dcoprss/rssnewsfeed.h b/dcoprss/rssnewsfeed.h
new file mode 100644
index 00000000..3c2e04b5
--- /dev/null
+++ b/dcoprss/rssnewsfeed.h
@@ -0,0 +1,115 @@
+/*
+ * rssnewsfeed.h
+ *
+ * Copyright (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef RSSNEWSFEED_H
+#define RSSNEWSFEED_H
+
+#include <qdatetime.h>
+#include <qstringlist.h>
+#include <qvariant.h>
+#include <kdatastream.h>
+
+class QueryService;
+
+class RSSNewsFeed
+{
+ friend QDataStream &operator>>( QDataStream &stream, RSSNewsFeed &feed );
+ friend QDataStream &operator<<( QDataStream &stream, const RSSNewsFeed &feed );
+ friend class QueryService;
+ public:
+ unsigned int id() const { return m_id; }
+ QString name() const { return m_name; }
+ QString description() const { return m_description; }
+ QString origin() const { return m_origin; }
+ QString languageCode() const { return m_languageCode; }
+ QString status() const { return m_status; }
+ QString version() const { return m_version; }
+ QString homePage() const { return m_homePage; }
+ QString sourceFile() const { return m_sourceFile; }
+ QString imageUrl() const { return m_imageUrl; }
+ QString webmaster() const { return m_webmaster; }
+ QString editor() const { return m_editor; }
+ QString publisher() const { return m_publisher; }
+ QString creator() const { return m_creator; }
+ const QDateTime &dateCreated() const { return m_dateCreated; }
+ const QDateTime &dateApproved() const { return m_dateApproved; }
+ const QDateTime dateXmlChanged() const { return m_dateXmlChanged; }
+ bool fetchable() const { return m_fetchable; }
+ unsigned int views() const { return m_views; }
+ unsigned int headlinesPerDay() const { return m_headlinesPerDay; }
+ unsigned int headlinesRank() const { return m_headlinesRank; }
+ QString toolkit() const { return m_toolkit; }
+ QString toolkitVersion() const { return m_toolkitVersion; }
+ unsigned int pollingInterval() const { return m_pollingInterval; }
+ const QDateTime &lastPoll() const { return m_lastPoll; }
+ QStringList categories() const { return m_categories; }
+
+ private:
+ unsigned int m_id;
+ QString m_name;
+ QString m_description;
+ QString m_origin;
+ QString m_languageCode;
+ QString m_status;
+ QString m_version;
+ QString m_homePage;
+ QString m_sourceFile;
+ QString m_imageUrl;
+ QString m_webmaster;
+ QString m_editor;
+ QString m_publisher;
+ QString m_creator;
+ QDateTime m_dateCreated;
+ QDateTime m_dateApproved;
+ QDateTime m_dateXmlChanged;
+ bool m_fetchable;
+ unsigned int m_views;
+ unsigned int m_headlinesPerDay;
+ unsigned int m_headlinesRank;
+ QString m_toolkit;
+ QString m_toolkitVersion;
+ unsigned int m_pollingInterval;
+ QDateTime m_lastPoll;
+ QStringList m_categories;
+};
+
+inline QDataStream &operator<<( QDataStream &stream, const RSSNewsFeed &feed )
+{
+ return stream << feed.m_id << feed.m_name << feed.m_description
+ << feed.m_origin << feed.m_languageCode << feed.m_status
+ << feed.m_version << feed.m_homePage << feed.m_sourceFile
+ << feed.m_imageUrl << feed.m_webmaster << feed.m_publisher
+ << feed.m_creator << feed.m_dateCreated << feed.m_dateApproved
+ << feed.m_dateXmlChanged << feed.m_fetchable << feed.m_views
+ << feed.m_headlinesPerDay << feed.m_headlinesRank
+ << feed.m_toolkit << feed.m_toolkitVersion
+ << feed.m_pollingInterval << feed.m_lastPoll
+ << feed.m_categories;
+}
+
+inline QDataStream &operator>>( QDataStream &stream, RSSNewsFeed &feed )
+{
+ int i;
+ stream >> feed.m_id >> feed.m_name >> feed.m_description
+ >> feed.m_origin >> feed.m_languageCode >> feed.m_status
+ >> feed.m_version >> feed.m_homePage >> feed.m_sourceFile
+ >> feed.m_imageUrl >> feed.m_webmaster >> feed.m_publisher
+ >> feed.m_creator >> feed.m_dateCreated >> feed.m_dateApproved
+ >> feed.m_dateXmlChanged >> i >> feed.m_views
+ >> feed.m_headlinesPerDay >> feed.m_headlinesRank
+ >> feed.m_toolkit >> feed.m_toolkitVersion
+ >> feed.m_pollingInterval >> feed.m_lastPoll
+ >> feed.m_categories;
+ feed.m_fetchable = i != 0;
+ return stream;
+}
+
+#endif
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/rssservice.desktop b/dcoprss/rssservice.desktop
new file mode 100644
index 00000000..1692b267
--- /dev/null
+++ b/dcoprss/rssservice.desktop
@@ -0,0 +1,88 @@
+[Desktop Entry]
+Type=Service
+Name=rssservice
+Name[bn]=আর-à¦à¦¸-à¦à¦¸ সারà§à¦­à¦¿à¦¸
+Name[ca]=Servei RSS
+Name[cs]=RSS služba
+Name[de]=RSS-Dienst
+Name[el]=υπηÏεσία rss
+Name[he]=שרות RSS
+Name[hi]=आरà¤à¤¸à¤à¤¸-सरà¥à¤µà¤¿à¤¸
+Name[hu]=RSS szolgáltatás
+Name[it]=Servizio RSS
+Name[ja]=rssサービス
+Name[lt]=rss tarnyba
+Name[nb]=rss-tjeneste
+Name[nds]=RSS-Deenst
+Name[nl]=RSS-dienst
+Name[nn]=rss-teneste
+Name[pl]=Usługa RSS
+Name[pt_BR]=serviço rss
+Name[ro]=Serviciu RSS
+Name[sv]=RSS-tjänst
+Name[ta]=rssசேவை
+Name[th]=บริà¸à¸²à¸£ rss
+Name[tr]=rssservisi
+Exec=rssservice
+X-DCOP-ServiceType=Unique
+X-KDE-StartupNotify=false
+Comment=RSS DCOP services
+Comment[ar]= خدمات RSS DCOP
+Comment[be]=СервіÑÑ‹ RSS Ð´Ð»Ñ DCOP
+Comment[bn]=আর-à¦à¦¸-à¦à¦¸ ডিকপ সারà§à¦­à¦¿à¦¸
+Comment[br]=Servijoù DCOP RSS
+Comment[bs]=RSS DCOP servisi
+Comment[ca]=Serveis RSS de DCOP
+Comment[cs]=RSS DCOP služby
+Comment[cy]=Gwasanaethau DCOP RSS
+Comment[da]=RSS DCOP-tjenester
+Comment[de]=RSS DCOP-Dienste
+Comment[el]=ΥπηÏεσίες RSS DCOP
+Comment[eo]=RSS-DCOP-servoj
+Comment[es]=Servicios RSS de DCOP
+Comment[et]=RSS DCOP-teenused
+Comment[eu]=RSS DCOP zerbitzuak
+Comment[fa]=خدمات RSS DCOP
+Comment[fi]=RSS-DCOP-palvelut
+Comment[fr]=Services DCOP RSS
+Comment[ga]=Seirbhísí DCOP RSS
+Comment[gl]=Servicios RSS de DCOP
+Comment[he]=שרותי RSS DCOP
+Comment[hi]=आरà¤à¤¸à¤à¤¸ डीकॉप सेवाà¤à¤
+Comment[hr]=RSS DCOP servisi
+Comment[hu]=RSS DCOP-szolgáltatás
+Comment[is]=RSS DCOP þjónustur
+Comment[it]=Servizi DCOP RSS
+Comment[ja]=RSS DCOP サービス
+Comment[ka]=RSS DCOP სერვისები
+Comment[kk]=RSS DCOP қызметтері
+Comment[km]=សáŸážœáž¶ RSS DCOP
+Comment[lt]=RSS DCOP tarnyba
+Comment[mk]=СервиÑи за RSS DCOP
+Comment[nb]=RSS DCOP -tjenester
+Comment[nds]=RSS-DCOP-Deenst
+Comment[ne]=RSS DCOP सेवा
+Comment[nl]=RSS DCOP-diensten
+Comment[nn]=RSS DCOP-tenester
+Comment[pa]=RSS DCOP ਸੇਵਾਵਾਂ
+Comment[pl]=Usługa DCOP dla RSS
+Comment[pt]=Serviços DCOP de RSS
+Comment[pt_BR]=Serviços DCOP RSS
+Comment[ro]=Serviciu DCOP RSS
+Comment[ru]=Службы RSS DCOP
+Comment[se]=RSS DCOP-bálvalusat
+Comment[sk]=RSS DCOP služby
+Comment[sl]=Storitve RSS DCOP
+Comment[sr]=RSS DCOP ÑервиÑи
+Comment[sr@Latn]=RSS DCOP servisi
+Comment[sv]=RSS DCOP-tjänster
+Comment[ta]=RSS DCOP சேவைகளà¯
+Comment[tg]=Хадамотҳои RSS DCOP
+Comment[th]=บริà¸à¸²à¸£ RSS DCOP
+Comment[tr]=RSS DCOP hizmetleri
+Comment[uk]=Служби RSS DCOP
+Comment[uz]=RSS DCOP xizmatlari
+Comment[uz@cyrillic]=RSS DCOP хизматлари
+Comment[zh_CN]=RSS DCOP æœåŠ¡
+Comment[zh_HK]=RSS DCOP æœå‹™
+Comment[zh_TW]=RSS DCOP æœå‹™
diff --git a/dcoprss/service.cpp b/dcoprss/service.cpp
new file mode 100644
index 00000000..74544dc1
--- /dev/null
+++ b/dcoprss/service.cpp
@@ -0,0 +1,105 @@
+/* $Id$ */
+/***************************************************************************
+ service.cpp - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include "service.h"
+#include "cache.h"
+
+RSSService::RSSService() :
+ DCOPObject("RSSService")
+{
+ m_list.setAutoDelete( true );
+
+ loadLinks();
+}
+
+RSSService::~RSSService()
+{
+}
+
+
+QStringList RSSService::list()
+{
+ QStringList lst;
+ QDictIterator<RSSDocument> itr(m_list);
+ for(; itr.current(); ++itr)
+ lst.append(itr.currentKey());
+ return lst;
+}
+
+DCOPRef RSSService::add(QString id)
+{
+ if(m_list.find(id) == 0L) { // add a new one only if we need to
+ m_list.insert(id, new RSSDocument(id));
+ added(id);
+ saveLinks();
+ }
+ return document(id);
+}
+
+void RSSService::remove(QString id)
+{
+ m_list.remove(id);
+ removed(id);
+ saveLinks();
+}
+
+DCOPRef RSSService::document(QString id)
+{
+ if( m_list[id] )
+ return DCOPRef(m_list[id]);
+ else
+ return DCOPRef();
+}
+
+void RSSService::exit()
+{
+ //Save all current RSS links.
+ saveLinks();
+ Cache::self().save();
+ kapp->quit();
+}
+
+
+void RSSService::loadLinks()
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("RSS Links");
+ const QStringList links = conf->readListEntry ("links");
+ QStringList::ConstIterator it = links.begin();
+ QStringList::ConstIterator end = links.end();
+ for ( ; it != end; ++it )
+ add( *it );
+}
+
+void RSSService::saveLinks()
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("RSS Links");
+ QStringList lst;
+ QDictIterator<RSSDocument> itr(m_list);
+ for(; itr.current(); ++itr)
+ lst.append(itr.currentKey());
+
+ conf->writeEntry("links", lst);
+ conf->sync();
+}
+
+
+
diff --git a/dcoprss/service.h b/dcoprss/service.h
new file mode 100644
index 00000000..35cf229e
--- /dev/null
+++ b/dcoprss/service.h
@@ -0,0 +1,290 @@
+/* $Id$ */
+#ifndef _RSS_SERVICE
+#define _RSS_SERVICE
+
+/***************************************************************************
+ service.h - A DCOP Service to provide RSS data
+ -------------------
+ begin : Saturday 15 February 2003
+ copyright : (C) 2003 by Ian Reinhart Geiser
+ email : geiseri@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <dcopobject.h>
+#include <dcopref.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qpixmap.h>
+#include <librss/global.h>
+#include <librss/loader.h>
+#include <librss/document.h>
+#include <librss/article.h>
+#include <librss/image.h>
+/**
+* This is a DCOP Service do not include this header in anything
+*
+**/
+using namespace RSS;
+
+class RSSDocument;
+class RSSArticle;
+
+class RSSService : public DCOPObject
+{
+ K_DCOP
+
+ private:
+
+ QDict<RSSDocument> m_list;
+
+ public:
+ RSSService();
+ ~RSSService();
+ void saveLinks();
+ void loadLinks();
+
+
+ k_dcop_signals:
+ /**
+ * Emmitted when a new document has been added. You can then
+ * use document(QString) to get the dcop ref for the object.
+ * Note: this document may or may not be valid at this
+ * point so you should connect your dcop signals and then
+ * do a documentValid() on the dcop ref to make sure of its
+ * state.
+ **/
+
+ void added(QString);
+ /**
+ * Emmitted when the document has been removed.
+ * note at this point the DCOPRef for this object is
+ * invalid and you will cannot access it any longer.
+ * When in doubt call a refresh on it, since if its in the
+ * process of loading the document call will be safely ignored
+ * and you will be notified of the updates.
+ **/
+ void removed(QString);
+ k_dcop:
+ /**
+ * Add a new rdf file resource. This will return a dcop reference to the resource. If its a new
+ * one it will be added otherwise an existing resource reference will be returned.
+ * once this reference has been returned you may connect dcop signals and then call
+ * refresh on the RSSDocument. The document will not be updated until refresh is called.
+ **/
+ DCOPRef add(QString url);
+ /**
+ * Return a list of current rss documents
+ **/
+ QStringList list();
+ /**
+ * Remove an rss document resource. NOTE: Be aware that others may be using this
+ * resource and if you remove it they may break. Likewise be aware that someone may
+ * decide to remove your resource on you so you should always check to see if the resource
+ * is valid before you access it.
+ **/
+ void remove(QString url);
+ /**
+ * Return the reference to a requested resource. If this resource is not present a null dcopref is
+ * returned.
+ **/
+ DCOPRef document(QString url);
+ /**
+ * Exit the RSSService. This will clean everything up and exit.
+ **/
+ void exit();
+};
+
+class RSSDocument : public QObject, public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+ private:
+ bool m_isLoading;
+ QString m_Url;
+ Document *m_Doc;
+ QPixmap m_pix;
+ QPtrList<RSSArticle> m_list;
+ QMap<QString,int> m_state;
+ QDateTime m_Timeout;
+ int m_maxAge;
+
+ private slots:
+ void pixmapLoaded(const QPixmap&);
+ void loadingComplete(Loader *, Document, Status);
+
+ public:
+ RSSDocument(const QString& url);
+ ~RSSDocument();
+
+ k_dcop_signals:
+ /**
+ * The pixmap is currently loading
+ **/
+ void pixmapUpdating(DCOPRef);
+ /**
+ * The pixmap is ready for viewing
+ * you can then use dcopref->call("pixmap()"); to return it.
+ *
+ **/
+ void pixmapUpdated(DCOPRef);
+ /**
+ * The document is currently updating
+ **/
+ void documentUpdating(DCOPRef);
+ /**
+ * The document is ready for viewing
+ * you can then use dcopref->call() to access its data
+ **/
+ void documentUpdated(DCOPRef);
+ /**
+ * The document failed to update, with and error...
+ * 1 - RSS Parse Error
+ * 2 - Could not access file
+ * 3 - Unknown error.
+ **/
+ void documentUpdateError(DCOPRef, int);
+
+ k_dcop:
+ /**
+ * Return the webmaster information from the RSS::Document
+ **/
+ QString webMaster();
+ /**
+ * Return the manageing editor from the RSS::Document
+ **/
+ QString managingEditor();
+ /**
+ * Returns the rating of the RSS::Document
+ **/
+ QString rating();
+ /**
+ * Returns the last build date from the RSS::Document
+ **/
+ QDateTime lastBuildDate();
+ /**
+ * Returns the publication date from the RSS::Document
+ **/
+ QDateTime pubDate();
+ /**
+ * Returns the copyright information from the RSS::Document
+ **/
+ QString copyright();
+ /**
+ * Returns a list of article titles
+ **/
+ QStringList articles();
+ /**
+ * Returns the number of articles
+ **/
+ int count();
+ /**
+ * Returns a dcop reference to the article from the index
+ **/
+ DCOPRef article(int idx);
+ /**
+ * Returns the link from the RSS::Document
+ **/
+ QString link();
+ /**
+ * Returns the description from the RSS::Document
+ **/
+ QString description();
+ /**
+ * Returns the title from the RSS::Document
+ **/
+ QString title();
+ /**
+ * Returns the text version from the RSS::Document
+ **/
+ QString verbVersion();
+ /**
+ * Returns the url for the pixmap from the RSS::Document
+ **/
+ QString pixmapURL();
+ /**
+ * Returns the actual pixmap from the RSS::Document's RSS::Image
+ **/
+ QPixmap pixmap();
+ /**
+ * Returns if the RSSDocument contains a valid RSS::Document yet.
+ **/
+ bool documentValid();
+ /**
+ * Returns if the RSSDocument contains a valid RSS::Image
+ **/
+ bool pixmapValid();
+ /**
+ * Refresh the current RSS::Document.
+ * This must be called before the document is valid.
+ **/
+ void refresh();
+
+ /**
+ * Return the maximum age of the RSS document (Default is 60 minutes)
+ **/
+ int maxAge();
+
+ /**
+ * Set the maximum age of the RSS document.
+ **/
+ void setMaxAge(int minutes);
+
+ /**
+ * Returns the state of the article
+ * 0 - not present (deleted from the rss service)
+ * 1 - new
+ * 2 - unread
+ * 3 - read
+ */
+ int state( const QString &title) const;
+
+ /**
+ * Set the article state
+ */
+ void setState( const QString &title, int s );
+
+ /**
+ * Convience method that will set a title to read.
+ */
+ void read( const QString &title);
+};
+
+class RSSArticle : public DCOPObject
+{
+ K_DCOP
+
+ private:
+ Article *m_Art;
+
+ public:
+ RSSArticle(Article *art);
+ ~RSSArticle();
+
+ k_dcop:
+ /**
+ * Return the articles title
+ **/
+ QString title();
+ /**
+ * Return the articles description
+ **/
+ QString description();
+ /**
+ * Return the link to the article
+ **/
+ QString link();
+};
+#endif
diff --git a/dcoprss/test.sh b/dcoprss/test.sh
new file mode 100644
index 00000000..d5d3e773
--- /dev/null
+++ b/dcoprss/test.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+ID=`dcopstart rssservice`
+#dcop $ID RSSService add "http://www.kde.org/dotkdeorg.rdf"
+#dcop $ID RSSService add "http://freshmeat.net/backend/fm.rdf"
+
+#dcop $REF1 refresh
+#dcop $REF2 refresh
+echo "Articles:"
+DOCS=`dcop rssservice RSSService list`
+for DOC in $DOCS
+do
+ DOCREF=`dcop rssservice RSSService document "$DOC"`
+ TITLE=`dcop $DOCREF title`
+ CNT=`dcop $DOCREF count`
+ echo $TITLE - $CNT
+ echo "------------------------------------"
+ while let "CNT >0"
+ do
+ let "CNT=CNT-1"
+ ART=`dcop $DOCREF article $CNT`
+ TEXT=`dcop $ART title`
+ echo "$CNT $TEXT"
+ done
+done
diff --git a/dcoprss/xmlrpciface.cpp b/dcoprss/xmlrpciface.cpp
new file mode 100644
index 00000000..e86639eb
--- /dev/null
+++ b/dcoprss/xmlrpciface.cpp
@@ -0,0 +1,401 @@
+/*
+ * kxmlrpcclient.cpp - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "xmlrpciface.h"
+
+#include <kdebug.h>
+#include <kio/job.h>
+#include <klocale.h>
+#include <kmdcodec.h>
+
+#include <qdom.h>
+
+using namespace KXMLRPC;
+
+Query *Query::create( QObject *parent, const char *name )
+{
+ return new Query( parent, name );
+}
+
+void Query::call( const QString &server, const QString &method,
+ const QValueList<QVariant> &args, const QString &userAgent )
+{
+ m_buffer.open( IO_ReadWrite );
+ m_server = server;
+ m_method = method;
+ m_args = args;
+
+ const QString xmlMarkup = markupCall( method, args );
+
+ QByteArray postData;
+ QDataStream stream( postData, IO_WriteOnly );
+ stream.writeRawBytes( xmlMarkup.utf8(), xmlMarkup.length() );
+
+ KIO::TransferJob *job = KIO::http_post( KURL( server ), postData, false );
+ job->addMetaData( "UserAgent", userAgent );
+ job->addMetaData( "content-type", "Content-Type: text/xml; charset=utf-8" );
+ connect( job, SIGNAL( infoMessage( KIO::Job *, const QString & ) ),
+ this, SLOT( slotInfoMessage( KIO::Job *, const QString & ) ) );
+ connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
+ this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ this, SLOT( slotResult( KIO::Job * ) ) );
+}
+
+void Query::slotInfoMessage( KIO::Job *, const QString &msg )
+{
+ emit infoMessage( msg );
+}
+
+void Query::slotData( KIO::Job *, const QByteArray &data )
+{
+ m_buffer.writeBlock( data );
+}
+
+void Query::slotResult( KIO::Job *job )
+{
+ Result response;
+ response.m_server = m_server;
+ response.m_method = m_method;
+ response.m_args = m_args;
+
+ response.m_success = false;
+
+ if ( job->error() != 0 ) {
+ response.m_errorCode = job->error();
+ response.m_errorString = job->errorString();
+ emit finished( response );
+ delete this;
+ return;
+ }
+
+ QDomDocument doc;
+ if ( !doc.setContent( m_buffer.buffer() ) ) {
+ response.m_errorCode = -1;
+ response.m_errorString = i18n( "Received invalid XML markup" );
+ emit finished( response );
+ delete this;
+ return;
+ }
+
+ m_buffer.close();
+
+ if ( isMessageResponse( doc ) )
+ response = parseMessageResponse( doc );
+ else if ( isFaultResponse( doc ) )
+ response = parseFaultResponse( doc );
+ else {
+ response.m_errorCode = 1;
+ response.m_errorString = i18n( "Unknown type of XML markup received" );
+ }
+
+ // parserMessageResponse and parseFaultResponse overwrite these fields.
+ response.m_server = m_server;
+ response.m_method = m_method;
+ response.m_args = m_args;
+
+ emit finished( response );
+ delete this;
+}
+
+bool Query::isMessageResponse( const QDomDocument &doc ) const
+{
+ return doc.documentElement().firstChild().toElement().tagName().lower() == "params";
+}
+
+Query::Result Query::parseMessageResponse( const QDomDocument &doc ) const
+{
+ Result response;
+ response.m_success = true;
+
+ QDomNode paramNode = doc.documentElement().firstChild().firstChild();
+ while ( !paramNode.isNull() ) {
+ response.m_data << demarshal( paramNode.firstChild().toElement() );
+ paramNode = paramNode.nextSibling();
+ }
+
+ return response;
+}
+
+bool Query::isFaultResponse( const QDomDocument &doc ) const
+{
+ return doc.documentElement().firstChild().toElement().tagName().lower() == "fault";
+}
+
+Query::Result Query::parseFaultResponse( const QDomDocument &doc ) const
+{
+ Result response;
+ response.m_success = false;
+
+ QDomNode errorNode = doc.documentElement().firstChild().firstChild();
+ const QVariant errorVariant = demarshal( errorNode.toElement() );
+ response.m_errorCode = errorVariant.toMap()[ "faultCode" ].toInt();
+ response.m_errorString = errorVariant.toMap()[ "faultString" ].toString();
+
+ return response;
+}
+
+QString Query::markupCall( const QString &cmd,
+ const QValueList<QVariant> &args ) const
+{
+ QString markup = "<?xml version='1.0' ?><methodCall>";
+
+ markup += "<methodName>" + cmd + "</methodName>";
+
+ if ( !args.isEmpty() ) {
+ markup += "<params>";
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ markup += "<param>" + marshal( *it ) + "</param>";
+ markup += "</params>";
+ }
+
+ markup += "</methodCall>";
+
+ return markup;
+}
+
+QString Query::marshal( const QVariant &arg )
+{
+ QString s = "<value>";
+ switch ( arg.type() ) {
+ case QVariant::String:
+ case QVariant::CString:
+ s += "<string>" + arg.toString() + "</string>";
+ break;
+ case QVariant::Int:
+ s += "<int>" + QString::number( arg.toInt() ) + "</int>";
+ break;
+ case QVariant::Double:
+ s += "<double>" + QString::number( arg.toDouble() ) + "</double>";
+ break;
+ case QVariant::Bool:
+ s += "<boolean>";
+ s += arg.toBool() ? "true" : "false";
+ s += "</boolean>";
+ break;
+ case QVariant::ByteArray:
+ s += "<base64>" + KCodecs::base64Encode( arg.toByteArray() ) + "</base64>";
+ break;
+ case QVariant::DateTime:
+ s += "<datetime.iso8601>" + arg.toDateTime().toString( Qt::ISODate ) + "</datetime.iso8601>";
+ break;
+ case QVariant::List: {
+ s += "<array><data>";
+ const QValueList<QVariant> args = arg.toList();
+ QValueList<QVariant>::ConstIterator it = args.begin();
+ QValueList<QVariant>::ConstIterator end = args.end();
+ for ( ; it != end; ++it )
+ s += marshal( *it );
+ s += "</data></array>";
+ break;
+ }
+ case QVariant::Map: {
+ s += "<struct>";
+ QMap<QString, QVariant> map = arg.toMap();
+ QMap<QString, QVariant>::ConstIterator it = map.begin();
+ QMap<QString, QVariant>::ConstIterator end = map.end();
+ for ( ; it != end; ++it ) {
+ s += "<member>";
+ s += "<name>" + it.key() + "</name>";
+ s += marshal( it.data() );
+ s += "</member>";
+ }
+ s += "</struct>";
+ break;
+ }
+ default:
+ kdWarning() << "Failed to marshal unknown variant type: " << arg.type() << endl;
+ return "<value/>";
+ };
+ return s + "</value>";
+}
+
+QVariant Query::demarshal( const QDomElement &elem )
+{
+ Q_ASSERT( elem.tagName().lower() == "value" );
+
+ if ( !elem.firstChild().isElement() )
+ return QVariant( elem.text() );
+
+ const QDomElement typeElement = elem.firstChild().toElement();
+ const QString typeName = typeElement.tagName().lower();
+
+ if ( typeName == "string" )
+ return QVariant( typeElement.text() );
+ else if ( typeName == "i4" || typeName == "int" )
+ return QVariant( typeElement.text().toInt() );
+ else if ( typeName == "double" )
+ return QVariant( typeElement.text().toDouble() );
+ else if ( typeName == "boolean" ) {
+ if ( typeElement.text().lower() == "true" || typeElement.text() == "1" )
+ return QVariant( true );
+ else
+ return QVariant( false );
+ } else if ( typeName == "base64" )
+ return QVariant( KCodecs::base64Decode( typeElement.text().latin1() ) );
+ else if ( typeName == "datetime" || typeName == "datetime.iso8601" )
+ return QVariant( QDateTime::fromString( typeElement.text(), Qt::ISODate ) );
+ else if ( typeName == "array" ) {
+ QValueList<QVariant> values;
+ QDomNode valueNode = typeElement.firstChild().firstChild();
+ while ( !valueNode.isNull() ) {
+ values << demarshal( valueNode.toElement() );
+ valueNode = valueNode.nextSibling();
+ }
+ return QVariant( values );
+ } else if ( typeName == "struct" ) {
+ QMap<QString, QVariant> map;
+ QDomNode memberNode = typeElement.firstChild();
+ while ( !memberNode.isNull() ) {
+ const QString key = memberNode.toElement().elementsByTagName( "name" ).item( 0 ).toElement().text();
+ const QVariant data = demarshal( memberNode.toElement().elementsByTagName( "value" ).item( 0 ).toElement() );
+ map[ key ] = data;
+ memberNode = memberNode.nextSibling();
+ }
+ return QVariant( map );
+ } else
+ kdWarning() << "Cannot demarshal unknown type " << typeName << endl;
+
+ return QVariant();
+}
+
+Query::Query( QObject *parent, const char *name ) : QObject( parent, name )
+{
+}
+
+QValueList<QVariant> Server::toVariantList( const QVariant &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( int arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( bool arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( double arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QString &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QCString &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QByteArray &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QDateTime &arg )
+{
+ QValueList<QVariant> args;
+ args << arg ;
+ return args;
+}
+
+QValueList<QVariant> Server::toVariantList( const QStringList &arg )
+{
+ QValueList<QVariant> args;
+ QStringList::ConstIterator it = arg.begin();
+ QStringList::ConstIterator end = arg.end();
+ for ( ; it != end; ++it )
+ args << QVariant( *it );
+ return args;
+}
+
+Server::Server( const KURL &url, QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ if ( url.isValid() )
+ m_url = url;
+}
+
+void Server::setUrl( const KURL &url )
+{
+ m_url = url.isValid() ? url : KURL();
+}
+
+void Server::call( const QString &method, const QValueList<QVariant> &args,
+ QObject *receiver, const char *slot )
+{
+ if ( m_url.isEmpty() ) {
+ kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
+ return;
+ }
+
+ Query *query = Query::create( this );
+ connect( query, SIGNAL( infoMessage( const QString & ) ),
+ this, SIGNAL( infoMessage( const QString & ) ) );
+ connect( query, SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
+ receiver, slot );
+ query->call( m_url.url(), method, args, m_userAgent );
+}
+
+void Server::call( const QString &method, const QValueList<QVariant> &args,
+ QObject *receiver, const char *slot,
+ QObject *infoObject, const char *infoSlot )
+{
+ if ( m_url.isEmpty() ) {
+ kdWarning() << "Cannot execute call to " << method << ": empty server URL" << endl;
+ return;
+ }
+
+ Query *query = Query::create( this );
+ connect( query, SIGNAL( infoMessage( const QString &msg ) ),
+ infoObject, infoSlot );
+ connect( query, SIGNAL( finished( const KXMLRPC::Query::Result & ) ),
+ receiver, slot );
+ query->call( m_url.url(), method, args, m_userAgent );
+}
+
+#include "xmlrpciface.moc"
+// vim:ts=4:sw=4:noet
diff --git a/dcoprss/xmlrpciface.h b/dcoprss/xmlrpciface.h
new file mode 100644
index 00000000..f7897e10
--- /dev/null
+++ b/dcoprss/xmlrpciface.h
@@ -0,0 +1,185 @@
+/*
+ * kxmlrpcclient.h - (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef KXMLRPCCLIENT_H
+#define KXMLRPCCLIENT_H
+
+#include <kurl.h>
+
+#include <qbuffer.h>
+#include <qdatastream.h>
+#include <qobject.h>
+#include <qvariant.h>
+#include <qvaluelist.h>
+
+class QDomDocument;
+class QDomElement;
+
+namespace KIO
+{
+ class Job;
+}
+
+namespace KXMLRPC
+{
+ class Query;
+ class QueryResult;
+ class Server;
+
+ class Query : public QObject
+ {
+ Q_OBJECT
+ public:
+ class Result
+ {
+ friend class Query;
+ friend QDataStream &operator>>( QDataStream &s, Query::Result &r );
+ public:
+ Result() { }
+
+ bool success() const { return m_success; }
+ int errorCode() const { return m_errorCode; }
+ QString errorString() const { return m_errorString; }
+ QValueList<QVariant> data() const { return m_data; }
+ QString server() const { return m_server; }
+ QString method() const { return m_method; }
+ QValueList<QVariant> args() const { return m_args; }
+
+ private:
+ bool m_success;
+ int m_errorCode;
+ QString m_errorString;
+ QValueList<QVariant> m_data;
+ QString m_server;
+ QString m_method;
+ QValueList<QVariant> m_args;
+ };
+
+ static Query *create( QObject *parent = 0, const char *name = 0 );
+ static QString marshal( const QVariant &v );
+ static QVariant demarshal( const QDomElement &e );
+
+ public slots:
+ void call( const QString &server, const QString &method,
+ const QValueList<QVariant> &args = QValueList<QVariant>(),
+ const QString &userAgent = "KDE-XMLRPC" );
+
+ signals:
+ void infoMessage( const QString &msg );
+ void finished( const KXMLRPC::Query::Result &result );
+
+ private slots:
+ void slotInfoMessage( KIO::Job *job, const QString &msg );
+ void slotData( KIO::Job *job, const QByteArray &data );
+ void slotResult( KIO::Job *job );
+
+ private:
+ bool isMessageResponse( const QDomDocument &doc ) const;
+ bool isFaultResponse( const QDomDocument &doc ) const;
+
+ Result parseMessageResponse( const QDomDocument &doc ) const;
+ Result parseFaultResponse( const QDomDocument &doc ) const;
+
+ QString markupCall( const QString &method,
+ const QValueList<QVariant> &args ) const;
+
+ Query( QObject *parent = 0, const char *name = 0 );
+
+ QBuffer m_buffer;
+ QString m_server;
+ QString m_method;
+ QValueList<QVariant> m_args;
+ };
+
+ class Server : public QObject
+ {
+ Q_OBJECT
+ public:
+ Server( const KURL &url = KURL(),
+ QObject *parent = 0, const char *name = 0 );
+
+ const KURL &url() const { return m_url; }
+ void setUrl( const KURL &url );
+
+ QString userAgent() const { return m_userAgent; }
+ void setUserAgent( const QString &userAgent) { m_userAgent = userAgent; }
+
+ template <typename T>
+ void call( const QString &method, const QValueList<T> &arg,
+ QObject *object, const char *slot );
+
+ static QValueList<QVariant> toVariantList( const QVariant &arg );
+ static QValueList<QVariant> toVariantList( int arg );
+ static QValueList<QVariant> toVariantList( bool arg );
+ static QValueList<QVariant> toVariantList( double arg );
+ static QValueList<QVariant> toVariantList( const QString &arg );
+ static QValueList<QVariant> toVariantList( const QCString &arg );
+ static QValueList<QVariant> toVariantList( const QByteArray &arg );
+ static QValueList<QVariant> toVariantList( const QDateTime &arg );
+ static QValueList<QVariant> toVariantList( const QStringList &arg );
+
+ signals:
+ void infoMessage( const QString &msg );
+
+ public slots:
+ void call( const QString &method, const QValueList<QVariant> &args,
+ QObject *object, const char *slot );
+ void call( const QString &method, const QValueList<QVariant> &args,
+ QObject *object, const char *slot,
+ QObject *infoObject, const char *infoSlot );
+
+ private:
+ KURL m_url;
+ QString m_userAgent;
+ };
+
+ inline QDataStream &operator>>( QDataStream &s, Query::Result &r )
+ {
+ return s >> r.m_errorCode >> r.m_errorString >> r.m_data
+ >> r.m_server >> r.m_method >> r.m_args;
+ }
+}
+
+template <typename T>
+void KXMLRPC::Server::call( const QString &method, const QValueList<T> &arg,
+ QObject *object, const char *slot )
+{
+ QValueList<QVariant> args;
+
+ typename QValueList<T>::ConstIterator it = arg.begin();
+ typename QValueList<T>::ConstIterator end = arg.end();
+ for ( ; it != end; ++it )
+ args << QVariant( *it );
+
+ call( method, args, object, slot );
+}
+
+inline QDataStream &operator<<( QDataStream &s, const KXMLRPC::Query::Result &r )
+{
+ return s << r.errorCode() << r.errorString() << r.data()
+ << r.server() << r.method() << r.args();
+}
+
+#endif // KXMLRPCCLIENT_H
+// vim:ts=4:sw=4:noet
diff --git a/doc/Makefile.am b/doc/Makefile.am
new file mode 100644
index 00000000..6812bd2d
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,5 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+SUBDIRS = $(AUTODIRS)
+
diff --git a/doc/kcontrol/Makefile.am b/doc/kcontrol/Makefile.am
new file mode 100644
index 00000000..930c270c
--- /dev/null
+++ b/doc/kcontrol/Makefile.am
@@ -0,0 +1,6 @@
+
+SUBDIRS = $(AUTODIRS)
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kcontrol/kcmktalkd/Makefile.am b/doc/kcontrol/kcmktalkd/Makefile.am
new file mode 100644
index 00000000..0f511040
--- /dev/null
+++ b/doc/kcontrol/kcmktalkd/Makefile.am
@@ -0,0 +1,2 @@
+KDE_LANG = en
+KDE_DOCS = kcontrol/kcmtalkd
diff --git a/doc/kcontrol/kcmktalkd/index.docbook b/doc/kcontrol/kcmktalkd/index.docbook
new file mode 100644
index 00000000..3d444f1d
--- /dev/null
+++ b/doc/kcontrol/kcmktalkd/index.docbook
@@ -0,0 +1,55 @@
+<?xml version="1.0" ?>
+<!DOCTYPE article PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN"
+"dtd/kdex.dtd" [
+<!ENTITY % addindex "IGNORE">
+<!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+
+<article lang="&language;">
+<title>Talk</title>
+<articleinfo>
+
+<authorgroup>
+<author>
+<firstname>Lauri</firstname>
+<surname>Watts</surname>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<date>2002-10-08</date>
+<releaseinfo>3.01.00</releaseinfo>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>KControl</keyword>
+<keyword>talk</keyword>
+</keywordset>
+</articleinfo>
+
+<sect1 id="talk">
+
+<title>Talk Configuration</title>
+
+<!-- FIXME: This module is installed with kdenetwork.. should it be
+documented in the kcontrol manual, or should this information be added
+to the ktalkd manual -->
+
+<sect2 id="talk-intro">
+<title>Introduction</title>
+
+<para>Please see the &ktalkd; manual for more information (you can
+read it by entering <userinput>help:/ktalkd/</userinput> in a
+&konqueror; window.)</para> </sect2>
+
+<sect2 id="talk-author">
+<title>Section Author</title>
+<para>This section
+written by: </para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+</sect2>
+</sect1>
+</article>
diff --git a/doc/kcontrol/lanbrowser/Makefile.am b/doc/kcontrol/lanbrowser/Makefile.am
new file mode 100644
index 00000000..09726472
--- /dev/null
+++ b/doc/kcontrol/lanbrowser/Makefile.am
@@ -0,0 +1,2 @@
+KDE_LANG = en
+KDE_DOCS = kcontrol/lanbrowser
diff --git a/doc/kcontrol/lanbrowser/index.docbook b/doc/kcontrol/lanbrowser/index.docbook
new file mode 100644
index 00000000..183d6d04
--- /dev/null
+++ b/doc/kcontrol/lanbrowser/index.docbook
@@ -0,0 +1,15 @@
+<?xml version="1.0" ?>
+<!DOCTYPE article PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN"
+"dtd/kdex.dtd" [
+<!ENTITY % addindex "IGNORE">
+<!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+<article>
+<sect1 id="lan-browsing">
+<title><acronym>LAN</acronym> Browsing</title>
+
+<para>Not yet documented</para>
+<!-- Merge the Lisa documentation into here -->
+
+</sect1>
+</article> \ No newline at end of file
diff --git a/doc/kdict/Makefile.am b/doc/kdict/Makefile.am
new file mode 100644
index 00000000..41691557
--- /dev/null
+++ b/doc/kdict/Makefile.am
@@ -0,0 +1,3 @@
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kdict/applet.png b/doc/kdict/applet.png
new file mode 100644
index 00000000..95e79613
--- /dev/null
+++ b/doc/kdict/applet.png
Binary files differ
diff --git a/doc/kdict/conf.png b/doc/kdict/conf.png
new file mode 100644
index 00000000..ed75dd9f
--- /dev/null
+++ b/doc/kdict/conf.png
Binary files differ
diff --git a/doc/kdict/index.docbook b/doc/kdict/index.docbook
new file mode 100644
index 00000000..b5454480
--- /dev/null
+++ b/doc/kdict/index.docbook
@@ -0,0 +1,1052 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kdict;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kdict; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Christian</firstname>
+<surname>Gebauer</surname>
+<affiliation>
+<address><email>gebauer@kde.org</email></address>
+</affiliation>
+</author>
+
+<othercredit role="developer">
+<firstname>Christian</firstname>
+<surname>Gebauer</surname>
+<affiliation>
+<address><email>gebauer@kde.org</email></address>
+</affiliation>
+<contrib>Developer</contrib>
+</othercredit>
+
+<othercredit role="reviewer">
+<firstname></firstname>
+<surname></surname>
+<affiliation><address><email></email></address></affiliation>
+<contrib>Reviewer</contrib>
+</othercredit>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+
+<copyright>
+<year>2002</year>
+<holder>Christian Gebauer</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2003-09-30</date>
+<releaseinfo>0.5.6</releaseinfo>
+
+<abstract><para>&kdict; is a graphical client for the
+<productname>DICT</productname> protocol. It enables you to search
+through dictionary databases for a word or phrase, then displays
+suitable definitions.</para></abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdict</keyword>
+<keyword>kdenetwork</keyword>
+<keyword>dictionary</keyword>
+<keyword>translation</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&kdict; is a graphical client for the
+<systemitem>DICT</systemitem> Protocol. It enables you to search
+through dictionary databases for a word or phrase, then displays
+suitable definitions. &kdict; tries to ease basic as well as advanced
+queries. A separate list offers a convenient way to deal with the
+enormous number of matching words that a advanced query can return.
+</para>
+
+<para>
+The remainder of &kdict;'s user interface resembles a web browser. For
+instance, you can jump to the definition of a synonym by simply clicking
+on the highlighted word. The back/forward functionality is also
+implemented, enabling you to quickly go back to the result of previous
+queries.
+</para>
+
+<para>
+&kdict; is able to process the content of the clipboard, so
+it's easy to combine &kdict; with your web browser or text
+editor.</para>
+
+<para>If your machine is behind a firewall, has no permanent internet
+connection or the server of dict.org is too slow for you, you can set up
+your own local server, all you need is available at <ulink
+url="http://www.dict.org">www.dict.org</ulink>. The advantages of a
+local server are optimal performance and the ability to install
+additional databases of your choice. This handbook contains a <link
+linkend="dictd-mini-howto">small tutorial for installation and links to
+databases.</link></para>
+
+</chapter>
+
+<chapter id="using-kdict">
+<title>Using &kdict;</title>
+
+<sect1 id="getting-started">
+<title>Getting Started</title>
+
+<para>After you have started &kdict; (using the panel menu, or by typing
+<command>kdict</command> at the command prompt) the &kdict; main window
+shows up:</para>
+
+<screenshot>
+<screeninfo>Starting &kdict;</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="mainwin.png" format="PNG"/></imageobject>
+<textobject><phrase>The &kdict; main window.</phrase></textobject>
+<caption><para>The &kdict; main window.</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>There are two different methods to lookup a word or phrase with
+&kdict;. You can use a <link
+linkend="define-query">define query</link>. &kdict; will look for exact
+matches in the databases and displays all matching definitions in the
+lower left view. Or you can use a <link
+linkend="match-query">match query</link>, in this case all matching
+database entries will be displayed in the list view on the right
+side. You can then decide what definitions you want to look at. This
+method has the advantage that you can use more sophisticated search
+strategies like prefix, suffix or regular expressions.</para>
+</sect1>
+
+<sect1 id="configuration-of-the-server">
+<title>Configuring the <systemitem>DICT</systemitem> server</title>
+
+<para>Before you can make your first query, you have to check if the
+preset server settings work with your setup. You can modify the settings
+in the <link linkend="preferences">preferences dialog</link>. The
+preset server is dict.org, which is a public server, so you don't have
+to change anything if you have a working internet connection.</para>
+
+<para>You can test your configuration by selecting <guimenuitem>Server
+Information</guimenuitem> from the <guimenu>Server</guimenu> menu, this
+displays some status information about the server.</para>
+
+<para>
+Now you should use <menuchoice><guimenu>Server</guimenu><guimenuitem>Get
+Capabilities</guimenuitem></menuchoice>, this will fetch a list of all
+available databases and strategies from the server. &kdict; is now able
+to show you all features of the server in its menus. (You should always
+call <guimenuitem>Get Capabilities</guimenuitem> after switching to a
+new server)
+</para>
+
+</sect1>
+
+<sect1 id="define-query">
+<title>Basic Queries: Define</title>
+
+<para>A define query will search for words/phrases in the selected
+database(s) that match the given text exactly. The definitions belonging
+to these words are displayed in the main view. If the server finds
+nothing suitable, &kdict; will use a search strategy optimized for spell
+checking to display a list of of similar words.</para>
+
+<para>In most databases some words (especially synonyms) are marked with
+brackets <quote>{}</quote>. These words are highlighted and by
+clicking on them with the &LMB; you start an new define-query
+with this word.</para>
+
+<para>You can select a part of the text with the &LMB;. The
+selection is automatically copied into the clipboard. This is very
+convenient, because you can use the &MMB; anywhere in the
+main view to define the content of the clipboard. So, if you want to
+get a definition for a word/phrase that is not tagged as synonym, you
+just mark it with the &LMB; and get the definition when you
+press the &MMB;.</para>
+
+<para>The &RMB; shows a popup menu, where you can choose to
+start queries with the selected text, the clipboard or the tagged
+synonym under the mouse pointer. The menu has also two entries
+<guimenuitem>Back</guimenuitem> and
+<guimenuitem>Forward</guimenuitem>. You can use them to browse through
+the results of previous queries.</para>
+
+</sect1>
+
+<sect1 id="match-query">
+<title>Advanced Queries: Match</title>
+
+<para>A match query uses the currently selected search strategy (the
+strategy selector is is located above the match list) to search in the
+selected database(s) for words similar to the given text. The result is
+a list of similar words that appears in the match list on the right. The
+entries are grouped according to the database they belong to. Now you
+have multiple options:</para>
+
+<itemizedlist>
+<listitem>
+<para>You can use the <guibutton>Get All</guibutton> button (located
+below the match list) to fetch all definitions. Please note that the
+number of listed words and fetched definitions may differ, because in
+some cases two or more words share one definition and &kdict; removes
+the duplicated definitions.</para>
+</listitem>
+
+<listitem>
+<para>You can use the mouse or the keyboard to select the most interesting
+words in the list and then press the <guibutton>Get
+Selected</guibutton> button (located below the match list) to fetch only
+them. If you want to get all definitions from one database just select
+the list item which contains the database name.</para>
+</listitem>
+
+<listitem>
+<para>When you press <keycap>Enter</keycap> (keyboard) or double click
+(mouse) on a list item, &kdict; will ignore the selection and fetch the
+definition for this item. When you do this with a
+<quote>root</quote> item (an item that contains the database name), you
+will get all definitions belonging to this database.</para>
+</listitem>
+
+<listitem>
+<para>You can use one of the words as a starting point for a new query,
+this is done via the popup menu (&RMB;).</para>
+</listitem>
+</itemizedlist>
+
+<para>A click with the &MMB; anywhere in the list will
+start a new match query with the content of the clipboard (similar to
+the main view).</para>
+
+<para>The &RMB; calls the popup menu for a list item, which
+has the following entries:</para>
+
+<variablelist>
+<varlistentry>
+<term><guimenuitem>Get</guimenuitem></term>
+<listitem><para>Shows the definition for the current
+item.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guimenuitem>Match</guimenuitem>,
+<guimenuitem>Define</guimenuitem></term>
+<listitem><para>Starts a match/define query with the current
+item.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<guimenuitem>Match Clipboard Content</guimenuitem>, <guimenuitem>Define Clipboard Content</guimenuitem>
+</term>
+<listitem><para>Starts a match/define query with the current content of
+the clipboard.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Get Selected</guimenuitem>, <guimenuitem>Get
+All</guimenuitem></term>
+<listitem><para>Same functions as the buttons below the
+list.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Expand List</guimenuitem>, <guimenuitem>Collapse
+List</guimenuitem></term>
+<listitem><para>(Un)folds all databases.</para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="database-sets">
+<title>Database Sets</title>
+
+<para>Sometimes it's useful to restrict a query to a subset of the
+available databases, for example all english-german dictionaries. This
+is achieved by defining <quote>database sets</quote>. These sets appear
+in the database selector as virtual databases.</para>
+
+<para>You can access the configuration dialog via
+<menuchoice><guimenu>Server</guimenu><guimenuitem>Edit Database
+Sets</guimenuitem></menuchoice> or the toolbar icon.</para>
+
+<screenshot>
+<screeninfo>Editing database sets</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="seteditor.png" format="PNG"/></imageobject>
+<textobject><phrase>The database set editor.</phrase></textobject>
+<caption><para>The database set editor.</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>The dialog has the following elements:</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Set</guilabel></term>
+<listitem><para>You must use this selector to select the set you want to
+modify. You can also rename a set here by entering a new name and
+pressing the <guibutton>Save</guibutton> button.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>Save</guibutton></term>
+<listitem><para>Saves changes you made in the current set. You must use
+this button before you select another set or leave the dialog, because
+otherwise all changes will be lost.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>New</guibutton></term>
+<listitem><para>This button creates a new database set.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>Delete</guibutton></term>
+<listitem><para>Deletes the currently selected set.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>Close</guibutton></term>
+<listitem><para>Closes the dialog without saving your
+changes.</para></listitem>
+</varlistentry>
+</variablelist>
+
+<para>The two lists (<guilabel>Selected Databases</guilabel> and
+<guilabel>Available Databases</guilabel>) show which databases are
+currently in the database set. You can use the arrow buttons between the
+lists to transfer items from one list to another.</para>
+
+<para>By the way, you can leave this configuration dialog open and
+continue your work with &kdict;. This is a nice way to test your changes
+immediately.</para>
+
+</sect1>
+
+<sect1 id="preferences">
+<title>Preferences</title>
+
+<para>You can modify many aspects of &kdict;'s behavior in the
+preferences dialog. The dialog can be opened via
+<menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure
+Kdict</guimenuitem></menuchoice> or the the toolbar icon.</para>
+
+<screenshot>
+<screeninfo>Configuring &kdict;</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="conf.png" format="PNG"/></imageobject>
+<textobject><phrase>The preferences dialog.</phrase></textobject>
+<caption><para>The preferences dialog.</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>The dialog is divided into several pages. The
+<guibutton>Default</guibutton> button restores the default values for
+the current page. The <guibutton>Apply</guibutton> button will apply
+your changes on all pages. The <guibutton>OK</guibutton> button will
+apply the changes and close the dialog. The
+<guibutton>Cancel</guibutton> button does this without saving the
+changes. By the way, you can leave the preferences dialog open and
+continue your work with &kdict;. This is a nice way to test your
+changes.</para>
+
+<sect2 id="preferences-server">
+<title>The <guilabel>Server</guilabel> Page</title>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Hostname</guilabel></term>
+<listitem><para>The internet hostname or the ip address of the
+<productname>DICT</productname> server.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Port</guilabel></term>
+<listitem><para>This is the port number the server listens on. 2628 is
+the default port and is used by the most servers.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Hold connection for</guilabel></term>
+<listitem><para>&kdict; is able to keep the connection open in short
+periods of inactivity. This feature avoids the lengthy login procedure
+before every query. A value of 0 seconds disables this feature. Very
+large values aren't useful, because in most cases the
+<productname>DICT</productname> server will close the connection after a
+couple of minutes.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Timeout</guilabel></term>
+<listitem><para>This value determines how long &kdict; will wait for a
+answer from the server.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Command Buffer</guilabel></term>
+<listitem><para>The <productname>DICT</productname> protocol allows the
+client to send multiple commands in one network packet. The size of the
+internal command buffer determines how many commands are send in
+parallel by &kdict;. You can try to tune this value for your network
+connection, but in most cases it is not worth the
+effort.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Encoding</guilabel></term>
+<listitem><para>With this selector the text encoding method of the databases can be
+specified. The default value is "utf8", this setting should work on
+most servers. If an encoding is selected that doesn't match the encoding
+used by the databases, you will see broken characters.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Server requires authentication</guilabel></term>
+<listitem><para>Activate this if you want to provide a authentication
+with username and password. (a server may require this for access to
+all databases) You have to enter a valid <guilabel>Username</guilabel>
+and <guilabel>Password</guilabel> combination below.</para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2 id="preferences-appearance">
+<title>The <guilabel>Appearance</guilabel> Page</title>
+
+<para>On this page you can customize the colors and fonts of the definition
+view. A proportional font for the normal text will increase readability,
+but will also destroy the hardcoded layout of tables and similar things
+in the definitions of some databases.</para>
+
+</sect2>
+
+<sect2 id="preferences-layout">
+<title>The <guilabel>Layout</guilabel> Page</title>
+
+<para>The layout of the result isn't really configurable yet. But you
+can decide how many headings (a heading states which database the
+definition belongs to) &kdict; should place in the result. The choices
+should be selfexplaining. Note that changes on this page won't have any
+effect until you start a new query.</para>
+
+</sect2>
+
+<sect2 id="preferences-misc">
+<title>The <guilabel>Miscellaneous</guilabel> Page</title>
+
+<para>On this page you can modify various limits that prevent &kdict;
+from eating up insane amounts of memory.</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Definitions</guilabel></term>
+<listitem><para>This limits the number of definitions you can fetch at once by
+selecting them in the match list.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Cached Results</guilabel></term>
+<listitem><para>This number determines how many previous results are held in a internal cache for fast access.
+You can set this to 0, but this will disable your ability to browse back to old results.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>History Entries</guilabel></term>
+<listitem><para>This is the number of past entries the input line remembers.
+Large values will cause a slower start and shutdown of
+&kdict;.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Save history on exit</guilabel></term>
+<listitem><para>If this is selected, &kdict; will remember your
+history between sessions.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Define selected text on start</guilabel></term>
+<listitem><para>If this is selected, &kdict; will immediately try to
+define the contents of the clipboard when it is
+started.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+
+</sect1>
+
+</chapter>
+
+<chapter id="panel-applet">
+<title>The Panel Applet</title>
+
+<para>If you use &kdict; frequently you may find it useful to use the
+included panel applet. You can get the applet via
+<menuchoice><guimenu>K-Menu</guimenu><guisubmenu>Configure
+Panel</guisubmenu><guisubmenu>Add</guisubmenu><guisubmenu>Applet</guisubmenu><guimenuitem>Dictionary</guimenuitem></menuchoice>.</para>
+
+<screenshot>
+<screeninfo>The Panel Applet</screeninfo>
+<mediaobject>
+<imageobject><imagedata fileref="applet.png" format="PNG"/></imageobject>
+<textobject><phrase>The Panel Applet</phrase></textobject>
+<caption><para>The Panel Applet</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>The input field behaves like the input field of the &kdict; main
+window. When you press <keycap>Enter</keycap> &kdict; opens and the
+query starts. Instead of typing you can also select a phrase you
+entered before from the drop down list.</para>
+
+<para>Additionally the applet features three push buttons:</para>
+
+<variablelist>
+<varlistentry>
+<term><guibutton>C</guibutton></term>
+<listitem><para>Define the current content of the clipboard.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guibutton>D</guibutton></term>
+<listitem><para>Define the current content of the input field.</para></listitem></varlistentry>
+<varlistentry>
+<term><guibutton>M</guibutton></term>
+<listitem><para>Start a match query with the current content of the input field.</para></listitem></varlistentry>
+</variablelist>
+
+</chapter>
+
+<chapter id="commands">
+<title>Command Reference</title>
+
+<sect1 id="file-menu">
+<title>The <guimenu>File</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Save</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Saves the current result as an html document</action>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;
+<keycap>P</keycap></keycombo></shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Print</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Prints the current result.</action>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Start Query</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Defines the content of the input field.</action>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Stop Query</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Aborts the current query.</action>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Ctrl;<keycap>Q</keycap></keycombo></shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Quit</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Quits</action> &kdict;.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="edit-menu">
+<title>The <guimenu>Edit</guimenu> Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Ctrl;<keycap>C</keycap></keycombo></shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Copy</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para><action>Copies the currently selected text into the clipboard.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Ctrl;<keycap>C</keycap></keycombo></shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Select All</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para><action>Selects the complete text.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><guimenu>Edit</guimenu>
+<guimenuitem>Define Clipboard Content</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para><link linkend="define-query">Defines the current content</link> of the clipboard.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Match Clipboard Content</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para><action>Find database entries</action> which <link
+linkend="match-query">match the current content</link> of the
+clipboard.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Ctrl;<keycap>F</keycap></keycombo></shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Find...</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para><action>Finds a string in the displayed definitions.</action></para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="history-menu">
+<title>The <guimenu>History</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Alt;<keycap>Left</keycap></keycombo></shortcut>
+<guimenu>History</guimenu>
+<guimenuitem>Back</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Displays the previous search result.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="simul">
+&Alt;<keycap>Right</keycap></keycombo></shortcut>
+<guimenu>History</guimenu>
+<guimenuitem>Forward</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Displays the next search result.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>History</guimenu>
+<guimenuitem>Clear History</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Clears the list of past queries.</action></para></listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>At the bottom of the <guimenu>History</guimenu> the ten last queries
+are listed.</para>
+
+</sect1>
+
+<sect1 id="server-menu">
+<title>The <guimenu>Server</guimenu> Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice> <guimenu>Server</guimenu>
+<guimenuitem>Get Capabilities</guimenuitem>
+</menuchoice></term> <listitem><para><action>Determines which databases
+and strategies are available</action> on the
+<productname>DICT</productname> server. You must call this once to be
+able to specify search strategy and database for a
+query.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Server</guimenu>
+<guimenuitem>Edit Database Sets...</guimenuitem>
+</menuchoice></term>
+<listitem><para>Opens the <link linkend="database-sets">database set editor</link>.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Server</guimenu>
+<guimenuitem>Database Information</guimenuitem>
+</menuchoice></term>
+<listitem><para>Submenu which offers a summary of the databases available, and detailed information for every database.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Server</guimenu>
+<guimenuitem>Strategy Information</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Displays a list with short descriptions of the search strategies
+available on the current server.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Server</guimenu>
+<guimenuitem>Server Information</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Displays some status information</action> (uptime, &etc;) about the current
+<productname>DICT</productname> server.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect1>
+
+<sect1 id="settings-menu">
+<title>The <guimenu>Settings</guimenu> Menu</title>
+<para>This menu provides options for configuring &kdict;, changing its
+appearance, shortcuts and standard behavior.</para>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Toolbars</guimenuitem>
+</menuchoice></term>
+<listitem><para>Submenu which toggles the toolbars on or off. You can
+toggle either the Main toolbar or the Query toolbar
+independently.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Statusbar</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Toggles the statusbar on/off.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Match List</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Hides (or shows) the match list.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Swallow Match List</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>This (un-)swallows the match list into the main window.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens a dialog for changing the key bindings.</action>
+Using this option you can change the standard key shortcut for &kdict;'s commands
+or create new ones.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Toolbars...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens a dialog for configuring the toolbar.</action> You
+can add and remove toolbuttons for &kdict;'s commands with this option.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Kdict...</guimenuitem>
+</menuchoice></term>
+<listitem><para>Opens the <link linkend="preferences">preferences dialog</link>.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="help-menu">
+<title>The <guimenu>Help</guimenu> Menu</title>
+
+&help.menu.documentation;
+
+</sect1>
+
+</chapter>
+
+<chapter id="command-line">
+<title>Command Line Options</title>
+
+<para>&kdict; can be started directly from a terminal like &konsole; or
+<application>xterm</application>. Several command line options are
+available.</para>
+
+<variablelist>
+<varlistentry>
+<term><command>kdict</command> <option>word/phrase</option></term>
+<listitem><para>lookup the given text. You will have to put the phrase into double quotes,
+if the phrase contains more than one word. For example: <command>kdict "double quote"</command></para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>kdict</command> <option>-c / --clipboard</option></term>
+<listitem><para>define the current content of the clipboard.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>kdict</command> <option>-v / --version</option></term>
+<listitem><para>Displays the version number of &kdict; (and that of
+&Qt;/&kde;).</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>kdict</command> <option>--license</option></term>
+<listitem><para>Shows under which licenses &kdict; is being
+published.</para></listitem>
+</varlistentry>
+</variablelist>
+
+<para>&kdict; also supports all other command line options common to
+&kde; and &Qt; programs. You can get a list of these options with
+<userinput><option>--help</option></userinput>,
+<userinput><option>--help-kde</option></userinput> and
+<userinput><option>--help-qt</option></userinput></para>
+
+</chapter>
+
+<chapter id="credits">
+<title>Credits and License</title>
+
+<para>&kdict; - The &kde; Dictionary Client</para>
+
+<para>Copyright (c) 1999-2001, Christian Gebauer</para>
+<para>Copyright (c) 1998, Matthias H&ouml;lzer-Kl&uuml;pfel</para>
+
+
+<para>&kdict; was originally written in 1998 by Matthias
+H&ouml;lzer-Kl&uuml;pfel <email>hoelzer@kde.org</email>. Currently it is
+maintained by Christian Gebauer <email>gebauer@kde.org</email>.</para>
+
+&underFDL; <!-- FDL: do not remove -->
+&underArtisticLicense; <!-- Artistic License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-kdict">
+<title>How to obtain &kdict;</title>
+
+&install.intro.documentation;
+&install.compile.documentation;
+
+</sect1>
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>Besides a working &kde; installation &kdict; requires the posix
+threads library which is available on all modern unices.</para>
+</sect1>
+
+</appendix>
+
+<appendix id="dictd-mini-howto">
+<title>Mini-Howto: Installing a local <productname>DICT</productname> server</title>
+
+<sect1 id="obtaining-dictd">
+<title>Obtaining and installing <command>dictd</command></title>
+
+<para>At first the <command>dictd</command> daemon has to be installed.
+The easiest way to install <command>dictd</command> is using a
+precompiled package. Such a package is included both in <ulink
+url="http://www.debian.org/">Debian</ulink> and <ulink
+url="http://www.suse.com/">SuSE</ulink> &Linux;. You can find packages
+that should work on all <productname>RPM</productname> based &Linux; distributions on <ulink
+url="http://rpmfind.net/linux/RPM/">rpmfind.net</ulink>.</para>
+
+<para>If you want to compile <command>dictd</command> yourself, download
+<ulink
+url="ftp://ftp.dict.org/pub/dict/dictd-1.9.1.tar.gz">ftp://ftp.dict.org/pub/dict/dictd-1.9.1.tar.gz</ulink>.
+Compilation is easy, just unpack the archive and run
+<userinput><command>./configure</command></userinput>,
+<userinput><command>make</command></userinput> and
+<userinput><command>make</command> <option>install</option></userinput>
+in the <filename class="directory">dictd</filename> folder. You might
+want to use the <option>--prefix</option> option of the configure script
+to install <command>dictd</command> in a different folder. By default
+<command>dictd</command> will be installed in <filename
+class="directory">/usr/local</filename>.</para>
+
+</sect1>
+
+<sect1 id="obtaining-databases">
+<title>Obtaining databases</title>
+
+<para>Now you need to download some databases. The standard set
+(webster, wordnet, jargon file, foldoc, ...) that is present on the
+<productname>DICT</productname> server of <ulink
+url="http://www.dict.org">dict.org</ulink>, is available from <ulink
+url="ftp://ftp.dict.org/pub/dict/pre/">ftp://ftp.dict.org/pub/dict/pre/</ulink>.
+These are also available as <ulink
+url="http://www.debian.org/">Debian</ulink> and <ulink
+url="http://rpmfind.net/linux/RPM/">rpm</ulink> packages.</para>
+
+<sect2 id="additional-databases">
+<title>Additional preformatted databases</title>
+
+<variablelist>
+
+<varlistentry>
+<term><ulink url="http://www.freedict.de/">www.freedict.de</ulink></term>
+<listitem><para>Translating dictionaries for Africaans, Czech, Danish,
+English, French, German, Greek, Hungarian, Irish, Italian, Japanese,
+Latin, Nederlands (Dutch), Portuguese, Russian, Serbo-Croatian, Swedish,
+Slovak, Spanish, Swahili, Swedish, Turkish and Welsh</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><ulink url="http://www.wh9.tu-dresden.de/~heinrich/dict/">http://www.wh9.tu-dresden.de/~heinrich/dict/</ulink></term>
+<listitem><para>Translating dictionaries for English, French, German, Italian, Latin, Portugue and Spanish.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><ulink url="http://purl.oclc.org/NET/voko/revodict.tgz">http://purl.oclc.org/NET/voko/revodict.tgz</ulink></term>
+<listitem><para>Esperanto dictionary</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><ulink url="http://www.bainsware.com/downloads/obi-bio.tar.gz">http://www.bainsware.com/downloads/obi-bio.tar.gz</ulink></term>
+<listitem><para><acronym>OBI</acronym>'s Online Biographical Dictionary</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><ulink url="http://www.bainsware.com/downloads/inaug.tar.gz"></ulink>http://www.bainsware.com/downloads/inaug.tar.gz</term>
+<listitem><para>The Inaugural Addresses of all the US presidents</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>You can find more databases on the link page of <ulink
+url="http://www.dict.org">www.dict.org</ulink>, but the majority of them
+are not formatted for <command>dictd</command>.</para>
+
+<para>Each database consists of two files: The <literal
+role="extension">*.index</literal> file contains the index and the
+<literal role="extension">*.dict.dz</literal> file the actual
+data. Unpack all packages into a folder of your choice, for example
+<filename class="directory">/usr/share/dict/</filename>.</para>
+
+</sect2>
+</sect1>
+
+<sect1 id="configuration-dictd">
+<title>Configuration</title>
+
+<para>You have to create/modify two configuration files. Both are stored
+in <filename class="directory">/usr/local/etc</filename> if you used the default
+installation prefix (<filename class="directory">/usr/local</filename>).</para>
+
+<para><filename>dict.conf</filename> belongs to the basic
+<command>dict</command> client. It contains only one line:
+<userinput>server localhost</userinput>. This tells
+<command>dict</command> to use the local server.</para>
+
+<para><filename>dictd.conf</filename> configures the server. First you
+must add the access statement: <userinput>access {allow localhost
+deny *}</userinput>.</para>
+
+<para> This example grants only local access and blocks all external
+connections. You can use more than one allow and deny rule, for
+example: <programlisting>access {allow localhost allow *.workgroup deny
+*}</programlisting> The database statement configures the location of
+the index and the data file for a database:
+</para>
+
+
+<programlisting>
+ database web1913 { data "/usr/share/dict/web1913.dict.dz"
+ index "/usr/share/dict/web1913.index" }
+ </programlisting>
+
+<para>You must add a statement for each database you want to use.</para>
+
+<para>Now you should be able to start <command>dictd</command>
+and to use it with <command>dict</command> and &kdict;.</para>
+
+<para>Please consult the man page of <command>dictd</command>
+for a complete description of <filename>dictd.conf</filename>.</para>
+</sect1>
+
+<sect1 id="starting-dictd">
+<title>Starting <command>dictd</command> automatically</title>
+
+<para>If you want to use the <command>dict</command> server
+frequently, you might want to start it automatically during the startup
+process of your system. Some of the precomplied packages install a
+suitable script, but you can also adapt the generic SYSV style script
+included in the source distribution: <ulink
+url="ftp://ftp.dict.org/pub/dict/INITSCRIPT">ftp://ftp.dict.org/pub/dict/INITSCRIPT</ulink>.</para>
+</sect1>
+
+</appendix>
+
+&documentation.index;
+
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+// vim:ts=2:sw=2:tw=78:noet
+-->
diff --git a/doc/kdict/mainwin.png b/doc/kdict/mainwin.png
new file mode 100644
index 00000000..77aa4782
--- /dev/null
+++ b/doc/kdict/mainwin.png
Binary files differ
diff --git a/doc/kdict/seteditor.png b/doc/kdict/seteditor.png
new file mode 100644
index 00000000..a089b850
--- /dev/null
+++ b/doc/kdict/seteditor.png
Binary files differ
diff --git a/doc/kget/Makefile.am b/doc/kget/Makefile.am
new file mode 100644
index 00000000..41691557
--- /dev/null
+++ b/doc/kget/Makefile.am
@@ -0,0 +1,3 @@
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kget/fileopen.png b/doc/kget/fileopen.png
new file mode 100644
index 00000000..037c2da9
--- /dev/null
+++ b/doc/kget/fileopen.png
Binary files differ
diff --git a/doc/kget/index.docbook b/doc/kget/index.docbook
new file mode 100644
index 00000000..eb8a2bab
--- /dev/null
+++ b/doc/kget/index.docbook
@@ -0,0 +1,751 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!-- Define an entity for your application if it is not part of KDE
+ CVS -->
+ <!ENTITY kget "<application>KGet</application>">
+ <!ENTITY kappname "&kget;"><!-- replace kget here
+ do *not* replace kappname-->
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kget; Handbook</title>
+
+<authorgroup>
+<author>
+<personname>
+<firstname>Jonathan</firstname>
+<othername>E.</othername>
+<surname>Drews</surname>
+</personname>
+<email>j.e.drews@att.net</email>
+</author>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>2003</year>
+<holder>Jonathan E. Drews</holder>
+</copyright>
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2005-08-31</date>
+<releaseinfo>0.8.4</releaseinfo>
+
+<!-- Abstract about this handbook -->
+
+<abstract>
+<para>
+&kget; allows you to group downloads. In some cases, &kget; can resume these
+downloads even if you shutdown your computer before the downloads have completed.
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdeutils</keyword>
+<keyword>kget</keyword>
+<keyword>kppp</keyword>
+<keyword>download</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>
+To download a document or package, drag and drop the &URL; on to &kget;.
+
+</para>
+</chapter>
+
+<chapter id="using-kget">
+<title>Using &kget;</title>
+
+<sect1 id="kget-features">
+<title>&kget; Tutorial</title>
+
+<para>Here is a brief tutorial that uses some of the features of &kget;. Below
+are three software packages that are to be downloaded. Suppose you want to
+download the middle one first and then the top and bottom ones second.
+</para>
+
+
+<orderedlist>
+<listitem>
+<para>
+Place &kget; in offline mode, by choosing the menu item
+<menuchoice><guimenu>Options</guimenu><guimenuitem>Offline
+Mode</guimenuitem></menuchoice>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+Click on the top entry with the &LMB;. Hold down the &Ctrl; key and click on
+the bottom entry. &kget; should look similar to this:
+
+<screenshot>
+<screeninfo>Screenshot of Kget</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kget1.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Items to be downloaded from Konqueror</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+
+</listitem>
+
+<listitem><para>
+Click on the delay button to prevent these items from being
+downloaded.
+</para></listitem>
+
+<listitem><para>
+Now click on the middle entry to highlight it. The top and bottom items will no
+longer be highlighted.
+<screenshot>
+<screeninfo>Screenshot of Kget</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kget2.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Items to be downloaded from Konqueror</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para></listitem>
+
+<listitem><para>
+Put &kget; back online by unchecking
+<menuchoice><guimenu>Options</guimenu><guimenuitem>Offline
+Mode</guimenuitem></menuchoice> and &kget; will download the middle item.
+</para></listitem>
+
+<listitem><para>
+Click on the top item, hold down the &Shift; key and click on the bottom item. &kget;
+should look like this:
+<screenshot>
+<screeninfo>Screenshot of Kget</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kget3.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Items to be downloaded from Konqueror</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+
+<note><para>
+Holding down the &Ctrl; key allows you to select individual items;
+holding down the &Shift; key allows you to select consecutive items,
+while clicking with the &LMB;.
+</para></note></para>
+
+</listitem>
+
+<listitem><para>
+Now click on the <guiicon>Queue</guiicon> icon or choose <menuchoice>
+<guimenu>Transfer</guimenu><guimenuitem>Queue</guimenuitem></menuchoice> to
+download the two highlighted items.
+</para></listitem>
+
+</orderedlist>
+</sect1>
+</chapter>
+
+<chapter id="configuration">
+<title>Special Configurations</title>
+<sect1 id="configuration-auto-disconnect">
+<title>Configuration of Auto-Disconnect</title>
+
+<para>
+This feature is used for automatically disconnecting your modem once a
+download has been completed. To configure &kget; for auto-disconnect do:
+</para>
+
+<itemizedlist>
+<listitem><para>
+Go to <menuchoice><guimenu>Options</guimenu><guimenuitem>Auto-Disconnect Mode
+</guimenuitem></menuchoice>
+<action> to disconnect the modem.</action> Usually this
+would be &kppp;. Expert Mode must be on to use this feature.</para>
+</listitem>
+
+<listitem><para>
+For &SuSE; users the command <userinput><command>cinternet
+<option>-i</option>
+<parameter>ppp0</parameter> <option>-0</option></command></userinput> must be
+substituted for <userinput><command>kppp <option>-k</option></command></userinput>
+in the <menuchoice><guimenu>Settings</guimenu>
+<guimenuitem>Configure &kget;...</guimenuitem></menuchoice>
+<guilabel> Automations</guilabel> menu.</para>
+</listitem>
+
+<listitem><para>
+ For Fedora Core users the command should be
+<userinput><command>/usr/sbin/usernetctl
+<parameter>ppp0</parameter> <option>down</option></command></userinput></para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="configuration-downloading-in-folders">
+<title>Downloading Into Designated Folders</title>
+
+<para>
+To download <acronym>JPEG</acronym> files into a designated folder do:
+</para>
+<itemizedlist>
+<listitem><para>
+ Go to <menuchoice><guimenu>Settings</guimenu>
+<guimenuitem>Configure &kget;...</guimenuitem></menuchoice>
+<guilabel>Folders</guilabel> menu.</para>
+</listitem>
+
+<listitem><para>
+Enter the files you wish to download using the extensions
+<literal role="extension">.jpg</literal> and <literal
+role="extension">.jpeg</literal> as shown below:
+</para>
+<screenshot>
+<screeninfo>Screenshot of Kget</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kget5.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>Downloading into designated files</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</listitem>
+
+<listitem>
+<para>Click on <interface>Apply</interface> and
+<interface>OK</interface>. When you download any
+<literal role="extension">.jpeg</literal> files they will be stored in
+<filename>/home/kdecvs/pics/JPEG</filename>.</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+</chapter>
+
+<chapter id="commands">
+<title>Command Reference</title>
+
+<sect1 id="kapp-mainwindow">
+<title>The main &kget; window</title>
+
+<sect2>
+<title>The <guimenu>File</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>O</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Open</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens the transfer window where you can paste &URL;'s.
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>V</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Paste</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Pastes the contents of the clipboard into the
+transfer window.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Export Transfer List...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Opens a Save As window </action> that allows you to save
+highlighted &URL;'s to a <literal role="extension">.kget</literal> file. To use this feature:
+</para>
+<itemizedlist>
+<listitem><para>
+Place &kget; in offline mode by clicking on the offline mode button in
+the toolbar or choosing
+<menuchoice><guimenu>Options</guimenu><guimenuitem>Offline
+Mode</guimenuitem></menuchoice>.
+</para></listitem>
+<listitem><para>
+Drag the &URL;'s you wish to download on to &kget;.
+</para></listitem>
+
+<listitem><para>
+Next click on the top entry so that it is highlighted.
+</para></listitem>
+
+<listitem><para>
+Hold down the <userinput><keycombo>
+&Shift;</keycombo></userinput>
+key and click on the bottom &URL; to highlight the entries like so:
+<screenshot>
+<screeninfo>Picture of kget saving to export file</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="kget4.png" format="PNG"/>
+ </imageobject>
+ </mediaobject>
+</screenshot>
+</para></listitem>
+
+<listitem><para>
+Now click on <menuchoice><guimenu>File</guimenu>
+<guimenuitem>Export Transfer List...</guimenuitem></menuchoice> and enter the name of the
+<literal role="extension">.kget</literal> file for your downloads.
+</para></listitem>
+</itemizedlist>
+
+
+<para>This feature is used to save items that will be downloaded on a
+regular basis, such as the &kde; snapshots above.
+</para>
+</listitem>
+
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Import Transfer List...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Loads <literal role="extension">.kget</literal> files that were created with
+<menuchoice><guimenu>File</guimenu>
+<guimenuitem>Export Transfer List...</guimenuitem></menuchoice>
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Import Text File...</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>
+Imports &URL;'s that are in text files.</action> This is a powerful feature that allows
+you to parse &URL;'s from text files and emails you may have received. It can
+discriminate between ordinary text and &URL;'s, provided the &URL; begins at the
+left margin of the document. &kget; will find these &URL;'s and load them into
+its main window for you.
+<note><para>This feature only ignores regular text when you have clicked on
+the Expert Mode icon. If the Expert Mode is not used then the text file must
+contain only &URL;'s.
+</para></note>
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut>
+<keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Quit</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Quits &kget;</action></para></listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>View</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu>
+<guimenuitem>Show Log Window</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>
+Opens a log window that shows the events that have occurred. This is useful
+for seeing what happened during a lengthy download. Here you can see if any
+packages were skipped or if a connection timed out.
+</action></para></listitem>
+</varlistentry>
+<!--
+<varlistentry>
+<term><menuchoice>
+<guimenu>View</guimenu>
+
+</varlistentry> -->
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>Transfer</guimenu> Menu</title>
+<note><para>
+In order for the entries in this menu to become active (not grayed out), you
+must highlight a download by clicking on the entry with the &LMB;.
+</para></note>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Copy &URL; to Clipboard</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>
+This pastes a highlighted line into the &kde; clipboard (Klipper).
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Open Individual Window</guimenuitem>
+</menuchoice></term>
+<listitem><para><action> Displays the selected download in its own window.
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Move To Beginning</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Moves a highlighted entry to the top of the download
+list.</action>
+<note><para>The topmost &URL; in &kget; is downloaded first.</para>
+</note>
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Move To End</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Moves a highlighted entry to the bottom of the download
+list.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Resume</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Resumes a download that has been paused.
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Pause</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Pauses a download that is running.
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Delete</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Deletes a highlighted item from the &kget; main
+window.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Restart</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Combines the functions of Resume and Pause in one
+button.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Queue</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Causes the highlighted entries in &kget; to begin
+downloading.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Timer</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Delays the download of the highlighted items by one
+minute. This is useful if you need to pause the download to check email or go
+to a website.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Transfer</guimenu>
+<guimenuitem>Delay</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Delays the download of the highlighted items
+indefinitely.</action>
+<note><para><guimenuitem>Queue</guimenuitem>,
+<guimenuitem>Timer</guimenuitem> and <guimenuitem>Delay</guimenuitem> are
+mutually exclusive; only one of them may be selected at a time.</para>
+</note>
+</para></listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+
+<sect2>
+<title>The Options Menu</title>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Use Animations</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>Toggle use of animations</action> to display &kget;'s
+state.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Use Sound</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>Toggle the use of sound</action> to indicate events, &eg; a
+file being added to the download list, or a download completing.</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Expert Mode</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Turns off prompting.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Use-Last-Folder Mode</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>&kget; will ignore the current folder settings and
+place all new transfers in the folder where the last transfer was placed.
+</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Offline Mode</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Toggles &kget; from being online (ready to download)
+to offline. The offline mode is used when you want to copy &URL;'s into &kget;
+without them being downloaded immediately.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Auto-Disconnect Mode</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Causes &kget; to disconnect the modem.</action> Usually this
+would be &kppp;. Expert Mode must be on to use this feature.</para>
+<para>
+You can find more information about the Auto-Disconnect Mode in <xref linkend="configuration-auto-disconnect" />.
+</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Options</guimenu>
+<guimenuitem>Auto Shutdown Mode</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Closes &kget; after all the downloads are
+completed. The Expert Mode must be turned on.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><guimenu>Options</guimenu><guimenuitem>Auto-Paste
+Mode</guimenuitem></menuchoice></term>
+<listitem><para>Enable grabbing of files to download from the
+clipboard.</para></listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>Settings</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Hide Statusbar</guimenuitem>
+</menuchoice></term>
+<listitem><para><action>Hide the statusbar</action>. The statusbar normally
+displays statistics about the currently downloading files.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Drop Target</guimenuitem>
+</menuchoice></term>
+<listitem><para>The drop target is a desktop icon that allows for
+hiding and restoring of &kget;. This is used on a cluttered desktop when you
+want to periodically check the status of a download.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para>Display the the familiar &kde; Keyboard Shortcut Configuration
+Dialog.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Toolbars</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Display the the familiar &kde; Toolbar Configuration Dialog.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure &kappname;</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Launch the main configuration dialog.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>The <guimenu>Help</guimenu> Menu</title>
+
+&help.menu.documentation;
+
+</sect2>
+
+</sect1>
+</chapter>
+
+<chapter id="credits">
+
+<title>Credits and License</title>
+
+<para>
+&kget;
+</para>
+<para>
+Program copyright 1998 Matej Moss
+</para>
+<para>
+Contributors:
+<itemizedlist>
+<listitem><para>Patrick Charbonnier <email>pch@freeshell.org</email></para>
+</listitem>
+<listitem><para>Carsten Pfeiffer <email>pfeiffer@kde.org</email></para>
+</listitem>
+</itemizedlist>
+</para>
+
+<para>
+Documentation Copyright &copy; 2003 Jonathan Drews <email>j.e.drews@att.net</email>
+</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL; <!-- FDL: do not remove -->
+
+
+&underGPL; <!-- GPL License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-kapp">
+<title>How to obtain &kget;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: xml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+vim:tabstop=2:shiftwidth=2:expandtab
+-->
diff --git a/doc/kget/kget1.png b/doc/kget/kget1.png
new file mode 100644
index 00000000..21407a43
--- /dev/null
+++ b/doc/kget/kget1.png
Binary files differ
diff --git a/doc/kget/kget2.png b/doc/kget/kget2.png
new file mode 100644
index 00000000..d9b19a2c
--- /dev/null
+++ b/doc/kget/kget2.png
Binary files differ
diff --git a/doc/kget/kget3.png b/doc/kget/kget3.png
new file mode 100644
index 00000000..5980418a
--- /dev/null
+++ b/doc/kget/kget3.png
Binary files differ
diff --git a/doc/kget/kget4.png b/doc/kget/kget4.png
new file mode 100644
index 00000000..067dccf8
--- /dev/null
+++ b/doc/kget/kget4.png
Binary files differ
diff --git a/doc/kget/kget5.png b/doc/kget/kget5.png
new file mode 100644
index 00000000..6d576c69
--- /dev/null
+++ b/doc/kget/kget5.png
Binary files differ
diff --git a/doc/knewsticker/Makefile.am b/doc/knewsticker/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/knewsticker/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/knewsticker/TODO b/doc/knewsticker/TODO
new file mode 100644
index 00000000..72208730
--- /dev/null
+++ b/doc/knewsticker/TODO
@@ -0,0 +1,4 @@
+TODO
+----
+* Make the developer's chapter use the <refentry> stuff if somebody finds out
+ how it works :-]
diff --git a/doc/knewsticker/about-icon.png b/doc/knewsticker/about-icon.png
new file mode 100644
index 00000000..d2866407
--- /dev/null
+++ b/doc/knewsticker/about-icon.png
Binary files differ
diff --git a/doc/knewsticker/checknews-icon.png b/doc/knewsticker/checknews-icon.png
new file mode 100644
index 00000000..ce2468a8
--- /dev/null
+++ b/doc/knewsticker/checknews-icon.png
Binary files differ
diff --git a/doc/knewsticker/contextmenu.png b/doc/knewsticker/contextmenu.png
new file mode 100644
index 00000000..c3b313a0
--- /dev/null
+++ b/doc/knewsticker/contextmenu.png
Binary files differ
diff --git a/doc/knewsticker/help-icon.png b/doc/knewsticker/help-icon.png
new file mode 100644
index 00000000..d21faa26
--- /dev/null
+++ b/doc/knewsticker/help-icon.png
Binary files differ
diff --git a/doc/knewsticker/index.docbook b/doc/knewsticker/index.docbook
new file mode 100644
index 00000000..1532f8b1
--- /dev/null
+++ b/doc/knewsticker/index.docbook
@@ -0,0 +1,1399 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+<!ENTITY kappname "&knewsticker;">
+<!ENTITY package "kdenetwork">
+<!ENTITY % addindex "IGNORE">
+<!ENTITY % English "INCLUDE">
+<!ENTITY RSS "<acronym>RSS</acronym>">
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &knewsticker; Handbook</title>
+
+<authorgroup>
+ <author>&Frerich.Raabe; &Frerich.Raabe.mail;</author>
+ <othercredit role="reviewer">&Jonathan.Singer; &Jonathan.Singer.mail;</othercredit>
+ <!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+ <year>2001, 2002, 2003</year>
+ <holder>&Frerich.Raabe;</holder>
+</copyright>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2003-10-14</date>
+<releaseinfo>2.00.00</releaseinfo>
+
+<abstract>
+ <para>&knewsticker; is a news ticker applet for the &kde; panel (also
+ known as &kicker;).</para>
+</abstract>
+
+<keywordset>
+ <keyword>KDE</keyword>
+ <keyword>KNewsTicker</keyword>
+ <keyword>kdenetwork</keyword>
+ <keyword>news ticker</keyword>
+ <keyword>applet</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&knewsticker; is an applet for the &kde; panel (also known as &kicker;)
+which provides an easy and convenient way to access the news as reported by
+many news sites (such as <ulink url="http://slashdot.org">Slashdot</ulink>,
+<ulink url="http://lwn.net">&Linux; Weekly News</ulink> or
+<ulink url="http://freshmeat.net">Freshmeat</ulink>).</para>
+
+<para>To achieve this, &knewsticker; requires the news sites to provide a
+special &RSS; file, which contains the headlines as well as
+pointers to the corresponding full articles. Such files are very common these
+days, and &knewsticker; already comes with a selection of good news sources
+which provide such files.</para>
+
+<sect1 id="rssfiles">
+<title>Brief Info On &RSS; Files</title>
+
+<para>&RSS; files are becoming more and more popular these
+days, and this applet is not the first application which takes advantage of
+them. But what are &RSS; files? This section tries to give
+a brief answer to this question, as well as pointers to other sources for
+further reference.</para>
+
+<para>The short answer: &RSS; is an
+<acronym>&XML;-based</acronym> format for syndicating web content.</para>
+
+<para>&RSS; is often used as an acronym for <quote>Rich Site
+Summary</quote> &ndash; that's not a common definition but it gives an idea on what
+the creators of &RSS; had in mind. There is no consensus on
+what &RSS; stands for, so it's actually not an acronym, it's a
+name.</para>
+
+<para>&RSS; originated in 1999 and was invented by
+<ulink url="http://www.netscape.com">NetScape</ulink> as a syndication format
+for their <ulink url="http://my.netscape.com">my.netscape.com</ulink> website;
+this very first &RSS; release was version 0.9. A few months
+after that, &Netscape; introduced &RSS; version 0.91, which
+incorporated many features of the <quote>&lt;scriptingNews&gt;</quote>
+format.</para>
+
+<para>The basic concept of all &RSS; files is to provide a
+clean, simple and portable way to distribute web content, in particular news:
+the news sites provide an &RSS; file which basically contains
+a set of records, and each record consists of a headline and a
+&URL; which points to the complete article. The
+&RSS; file also contains other general information about the
+particular news site, such as its name and the homepage, which is evaluated
+by &knewsticker;.</para>
+
+<para>Nowadays there are a few additional, more sophisticated versions of the
+&RSS; format
+(<ulink url="http://backend.userland.com/rss091">0.91</ulink>,
+<ulink url="http://backend.userland.com/rss092">0.92</ulink>,
+<ulink url="http://backend.userland.com/rss093">0.93</ulink>,
+<ulink url="http://web.resource.org/rss/1.0/">1.0</ulink> and the current
+format version
+<ulink url="http://backend.userland.com/rss">2.0</ulink>) but the first two
+versions still make up about 85% of the files provided on the web. Nevertheless,
+all versions up to 2.0 can be processed with &knewsticker;!</para>
+
+<para>Of course, this is only a short and highly incomplete attempt at
+explaining the basic ideas behind &RSS; files. If you're
+interested in this topic, you might want to visit any of the following links
+which point to further and more complete sources on this:</para>
+
+<variablelist>
+<varlistentry>
+ <term><ulink url="http://www.webreference.com/authoring/languages/xml/rss/intro/">http://www.webreference.com/authoring/languages/xml/rss/intro/</ulink></term>
+ <listitem><para>A very pragmatic introduction to the &RSS;
+ format, with concrete examples and guidelines. Together with the
+ authoritative specifications, this makes a good guide for people
+ who are thinking
+ about providing an &RSS; newsfeed on their home
+ page.</para></listitem>
+</varlistentry>
+<varlistentry>
+ <term><ulink url="http://www.oreillynet.com/rss/">http://www.oreillynet.com/rss/</ulink></term>
+ <listitem><para>The &RSS; page from O'Reilly features a
+ lot of general articles about employing and using &RSS; files,
+ up to date news about the &RSS; development community as well
+ as vital information for web developers who are considering taking advantage of
+ &RSS;.</para></listitem>
+</varlistentry>
+<varlistentry>
+ <term><ulink url="http://blogspace.com/rss/">http://blogspace.com/rss/</ulink></term>
+ <listitem><para>This page is another excellent source of news about the
+ &RSS; development, which is especially interesting to
+ developers working with &RSS;-based technology.</para></listitem>
+</varlistentry>
+<varlistentry>
+ <term><ulink url="http://www.w3.org/RDF/">http://www.w3.org/RDF/</ulink></term>
+ <listitem><para><emphasis>The</emphasis> authoritative source about
+ <acronym>RDF</acronym>, an &XML;-based language from which
+ modern &RSS; versions are derived, published by the
+ <ulink url="http://www.w3.org">World Wide Web Consortium</ulink>. It features
+ a comprehensive list of links to other sites on the topic as well as a timeline
+ of the <acronym>RDF</acronym> development, an overview over the architecture, an
+ archive with articles about <acronym>RDF</acronym> as well as a carefully
+ assembled list of tools for developers who intend to work with
+ <acronym>RDF</acronym>.</para></listitem>
+</varlistentry>
+</variablelist>
+
+<para>Developers will also want to check the authoritative specifications for
+the various &RSS; versions:</para>
+
+<orderedlist>
+<title>&RSS; Specifications</title>
+<listitem><para>
+ Version 0.90: <ulink url="http://www.purplepages.ie/rss/netscape/rss0.90.html">http://www.purplepages.ie/rss/netscape/rss0.90.html</ulink>
+</para></listitem>
+<listitem><para>
+ Version 0.91: <ulink url="http://backend.userland.com/rss091">http://backend.userland.com/rss091</ulink>
+</para></listitem>
+<listitem><para>
+ Version 0.92: <ulink url="http://backend.userland.com/rss092">http://backend.userland.com/rss092</ulink>
+</para></listitem>
+<listitem><para>
+ Version 0.93: <ulink url="http://backend.userland.com/rss093">http://backend.userland.com/rss093</ulink>
+</para></listitem>
+<listitem><para>
+ Version 1.0: <ulink url="http://web.resource.org/rss/1.0/">http://web.resource.org/rss/1.0/</ulink>
+</para></listitem>
+<listitem><para>
+ Version 2.0: <ulink url="http://backend.userland.com/rss">http://backend.userland.com/rss</ulink>
+</para></listitem>
+</orderedlist>
+
+<para>If you find any other sites or documents on this topic, and think they are
+worth being mentioned here, don't hesitate to send them to &Frerich.Raabe;
+&Frerich.Raabe.mail; so that they can be included in this document and
+help everybody.</para>
+
+<para>Thank you very much!</para>
+</sect1>
+</chapter>
+
+<chapter id="starting-knewsticker">
+<title>Starting &knewsticker;</title>
+
+<screenshot>
+ <screeninfo>Here is a screenshot of &knewsticker; in &kde;'s
+ panel.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="knewsticker-kicker.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>Here is a screenshot of &knewsticker; in &kde;'s
+ panel.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>
+&knewsticker; is started like every other &kicker; applet. You just have to add
+it to the panel (or any child panel of the main one). To do so, just right-click
+on the &kde; panel and choose <menuchoice><guimenu>Add</guimenu>
+<guisubmenu>Applet</guisubmenu><guimenuitem>&knewsticker;</guimenuitem>
+</menuchoice>.</para>
+
+<screenshot>
+ <screeninfo>Here is a screenshot of &knewsticker; in its own child
+ panel.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="knewsticker-childpanel.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>Here is a screenshot of &knewsticker; in its own child
+ panel.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>Another good way to use &knewsticker; is to put it into its own child
+panel. Just add a new child panel by choosing <menuchoice><guimenu>Add</guimenu>
+<guisubmenu>Extension</guisubmenu><guimenuitem>Child Panel</guimenuitem>
+</menuchoice> from the panel menu;. Now you can simply right-click on the child
+panel and select &knewsticker; as described above.</para>
+
+<screenshot>
+ <screeninfo>Here is a screenshot of &knewsticker; in its own
+ window.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="knewsticker-ownwindow.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>Here is a screenshot of &knewsticker; in its own
+ window.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>A third, popular, way to run &knewsticker; is by selecting <menuchoice>
+<guimenu>Internet</guimenu><guimenuitem>&knewsticker; (News
+Ticker)</guimenuitem></menuchoice> from the <guilabel>K</guilabel> menu. This
+will start &knewsticker; and make it run in its own window which you can then
+resize and move around as you wish.</para>
+</chapter>
+
+<chapter id="configuration">
+<title>Configuring &knewsticker;</title>
+
+<para>You can access &knewsticker;'s configuration dialog by right-clicking
+onto the scroll text, or by clicking on the button with the arrow on it and
+choosing the entry labelled <guimenuitem><inlinemediaobject><imageobject>
+<imagedata fileref="preferences-icon.png" format="PNG"/></imageobject>
+</inlinemediaobject> Preferences</guimenuitem> in the menu.</para>
+
+<sect1 id="config-general">
+<title>General Options</title>
+
+<screenshot>
+ <screeninfo>This is what the <guilabel>General</guilabel> tab of the
+ preferences dialog looks like.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="kcmnewsticker-general.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the <guilabel>General</guilabel> tab of the
+ preferences dialog looks like.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>Here you can define how fast the text should be scrolled around, what it
+should look like as well as other options for the applet. Here is a brief info
+on what each of the switches and buttons on this tab does:</para>
+
+<variablelist>
+<varlistentry>
+ <term><guilabel>Mousewheel sensitivity:</guilabel></term>
+ <listitem>
+ <para>This slider allows you to define how fast/slow the text should be
+ scrolled when using the mousewheel.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>News query interval</guilabel></term>
+ <listitem>
+ <para>Here you can define in what intervals &knewsticker; queries the
+ configured news sources for new headlines. This depends generally on
+ how fast you'd like to hear about news and how much load you want to
+ put on the network:</para>
+ <itemizedlist>
+ <listitem>
+ <para>A lower value (lower than 15 minutes) enables you to be
+ notified about news very quickly if you want or need to. Please
+ note, that it increases the network traffic significantly,
+ though. Therefore, such low values shouldn't be used if you
+ query popular news sites (such as
+ <ulink url="http://slashdot.org">Slashdot</ulink> or
+ <ulink url="http://freshmeat.net">Freshmeat</ulink>) as they
+ have generally already enough work with processing the incoming
+ queries.</para>
+ </listitem>
+ <listitem>
+ <para>A higher value (higher than 45 minutes) won't make you
+ hear about news that quick. For non-time-critical applications,
+ it should be suitable, though. The positive aspect of longer
+ intervals is that only very little load is put on the network;
+ this saves resources and nerves, for you and the system
+ administrators of the news sites you query.</para>
+ </listitem>
+ </itemizedlist>
+ <tip><para>The default value (30 minutes) should be appropriate and
+ reasonable in most cases.</para></tip>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Use custom names for news sites</guilabel></term>
+ <listitem>
+ <para>Check this box to make the news ticker use the names you
+ specified in the list of news sources (available on the tab
+ labeled <guilabel>News sources</guilabel>) instead of the ones
+ the news sites themselves report. This can be handy for news sites
+ which report a very long or useless name.</para>
+ </listitem>
+</varlistentry>
+</variablelist>
+</sect1>
+
+<sect1 id="config-newssources">
+<title>Configuration of the news sources</title>
+
+<screenshot>
+ <screeninfo>This is what the <guilabel>News Sources</guilabel> tab of the
+ preferences dialog looks like.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="kcmnewsticker-newssources.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the <guilabel>News Sources</guilabel> tab of
+ the preferences dialog looks like.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>On this tab you can manage and maintain the list of news sites
+&knewsticker; queries for news. Click on any entry with the right mouse button
+to open a context menu which lets you remove the current entry, or add a new
+entry. At the bottom of the page you can also find three buttons which have
+the same effect.</para>
+
+<sect2>
+<title>Adding a news site</title>
+<para>There are four ways to <emphasis>add</emphasis> a new news site to the
+list:</para>
+<itemizedlist>
+ <listitem>
+ <para>You can click on the button at the bottom labeled
+ <guilabel>Add...</guilabel>.</para>
+ </listitem>
+ <listitem>
+ <para>You can click with the right mouse button on the table and choose
+ <guimenuitem>Add news source</guimenuitem>.</para>
+ </listitem>
+ <listitem>
+ <para>You can drag any &RSS; file from another
+ application (such as &konqueror;) onto the table. This adds a new entry
+ to the list, sets the name to <quote>Unknown</quote> and sets the
+ maximum number of articles to 10.</para>
+ </listitem>
+ <listitem>
+ <para>And finally, you can just click on any &RSS;
+ file in the &konqueror; filemanager to have it added to the list
+ immediately.</para>
+ </listitem>
+</itemizedlist>
+<para>Either way will cause the <link linkend="config-news-site-dialog">News site
+dialog</link> to show up, presenting you with a form to enter the properties of
+the news site to add.</para>
+</sect2>
+
+<sect2>
+<title>Modifying an existing news site</title>
+<para>There are two ways to adjust the properties of an existing news
+site:</para>
+<itemizedlist>
+ <listitem>
+ <para>You can click on the button at the bottom labeled
+ <guilabel>Modify...</guilabel>.</para>
+ </listitem>
+ <listitem>
+ <para>You can right-click with the right mouse button on the news site
+ you'd like to edit and choose <guimenuitem>Modify '...'</guimenuitem>
+ from the menu.</para>
+ </listitem>
+</itemizedlist>
+<para>No matter which way you chose, it will cause the
+<link linkend="config-news-site-dialog">News site dialog</link> to pop up,
+showing the properties of the selected news site.</para>
+</sect2>
+
+<sect2>
+<title>Removing a news site</title>
+<para>Of course, you want to <emphasis>remove</emphasis> a news source from
+the list sometimes. To do this, you can either</para>
+<itemizedlist>
+ <listitem>
+ <para>click on the button at the bottom labeled
+ <guilabel>Remove</guilabel>, or</para>
+ </listitem>
+ <listitem>
+ <para>right-click with the right mouse button on the news site
+ you'd like to edit and choose <guimenuitem>Remove '...'</guimenuitem>
+ from the menu.</para>
+ </listitem>
+</itemizedlist>
+<para>In both cases, a confirmation box will pop up and make sure you didn't
+select the wrong entry.</para>
+<tip><para>You can also remove multiple news sites at once by holding &Ctrl;
+while clicking on the entries you wouldd like to remove, or by clicking the
+<mousebutton>left</mousebutton> mousebutton and dragging the mouse over all
+the entries you'd like to select.</para></tip>
+</sect2>
+
+<sect2 id="config-news-site-dialog">
+<title>The news site dialog</title>
+
+<screenshot>
+ <screeninfo>This is what the dialog for adding and editing news sites looks
+ like.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="kcmnewsticker-newssitedialog.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the dialog for adding and editing news sites
+ looks like.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>When adding or modifying a news site, the news site dialog (shown above)
+pops up and provides input facilities to edit the various properties of a news
+site:</para>
+
+<variablelist>
+<varlistentry>
+ <term><guilabel>Name:</guilabel></term>
+ <listitem>
+ <para>This is the name of the news source.</para>
+ <note><para>This text will only be used if <guilabel>Use custom names
+ for news sites</guilabel> on the tab labeled
+ <link linkend="config-general">General</link> is
+ activated.</para></note>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Source file:</guilabel></term>
+ <listitem>
+ <para>Here you can set the &URL; which references
+ the &RSS; file of this news site; this can either
+ be a local file, or a file saved on a remote server. You can also
+ click on the button at the right to open a convenient file-selection
+ dialog and browse to the file you would like to use, instead of typing
+ the &URL; by hand.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>The file is a program</guilabel></term>
+ <listitem>
+ <para>If this file is checked, &knewsticker; will not assume that the
+ &URL; (which was specified in the <guilabel>Source
+ file</guilabel> field) references an &RSS; file, but
+ rather that the &URL; refers a program (usually a
+ script). When querying this news site, &knewsticker; will execute the
+ program and treat whatever the program prints to stdout as
+ &RSS; markup. This is very convenient for conversion
+ scripts which download an &HTML; file and process it,
+ producing &RSS; markup which is suitable for use with
+ &knewsticker;.</para>
+ <tip><para>You can find some scripts which &eg; download stock data in
+ the kdeaddons module, in the
+ <filename class="directory">knewsticker-scripts</filename>
+ directory.</para></tip>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Category:</guilabel></term>
+ <listitem>
+ <para>Here you can specify into which category the news site belongs.
+ Arranging the news sites into categories makes it much easier to
+ maintain large lists of news sites.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Max. articles:</guilabel></term>
+ <listitem>
+ <para>This option lets you define how many articles &knewsticker; will
+ cache for this news site; the value will never be exceeded.</para>
+ <para>This is particularly handy for news sites which provide only
+ three news items at once, but you'd like to see the last ten items (for
+ instance); &knewsticker; will always download the three items and merge
+ them into its list, caching the last seven items.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Icon:</guilabel></term>
+ <listitem>
+ <para>Here you can specify a &URL; to an image file
+ (preferably 16x16 pixels in size) which should be used for this news
+ site. Icons make it much easier to distinguish multiple news sites, and
+ see which news site a headline appeared on as it scrolls by in
+ &knewsticker;.</para>
+ </listitem>
+</varlistentry>
+</variablelist>
+
+<para>At the button you will find the usual buttons. One of them deserves an
+extra note: clicking on the <guilabel>Suggest</guilabel> button will make
+&knewsticker; try to guess suitable values for most of the fields if you
+specify a &URL; to a valid source file.</para>
+
+<para>This means that you can usually just paste a &URL; to
+an &RSS; file in the input field labeled <guilabel>Source
+file</guilabel>, press the <guilabel>Suggest</guilabel> button and then modify
+the suggested values as needed.</para>
+</sect2>
+</sect1>
+
+<sect1 id="config-filters">
+<title>Filters</title>
+
+<screenshot>
+ <screeninfo>This is what the <guilabel>Filters</guilabel> tab of the
+ preferences dialog looks like.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="kcmnewsticker-filters.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the <guilabel>Filters</guilabel> tab of
+ the preferences dialog looks like.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>This tab allows you to define various filters which should be applied
+before showing the headlines in the scroll text.</para>
+
+<note><para>These filters only affect the headlines which are shown in the
+scrolltext; the menu will always show all of the headlines.</para></note>
+<para>The major part of the tab is occupied by a table which lists the
+currently configured filters. Each filter has a small checkable box - checking
+that box enables the filter, unchecking it temporarily disables it without
+removing it from the list.</para>
+
+<para>By default there are no filters, so chances are that the table is
+completely empty for you. Of course, this dialog provides you with ways
+to add new filters, and manage them in general:</para>
+
+<itemizedlist>
+<listitem>
+ <para>To <emphasis>add</emphasis> a filter, simply enter the filter
+ properties (see <link linkend="filter-components">Filter
+ Components</link> for a detailed description of the various filter
+ properties) using the input fields in the box labeled <guilabel>Filter
+ properties</guilabel> and then press the button labeled
+ <guilabel>Add</guilabel>.</para>
+</listitem>
+<listitem>
+ <para>To <emphasis>modify</emphasis> an existing filter, select the
+ filter you would like to edit in the table by left-clicking on it and
+ then change its properties in the box at the bottom.</para>
+</listitem>
+<listitem>
+ <para>To <emphasis>remove</emphasis> a filter, select it in the table
+ and then press the button labeled <guilabel>Remove</guilabel>.</para>
+</listitem>
+</itemizedlist>
+
+<sect2 id="filter-components">
+<title>Filter Components</title>
+
+<para>Each filter consists of four components:</para>
+
+<orderedlist>
+<title>Filter Components</title>
+<listitem>
+ <para>Action &ndash; this can be either <guilabel>Show</guilabel> or
+ <guilabel>Hide</guilabel> and defines what should happen to a headline
+ in case this filter matches.</para>
+</listitem>
+<listitem>
+ <para>News sources &ndash; here you can define whether the filter affects only
+ single news sources, or whether this filter should be applied to the
+ headlines of all news sources.</para>
+</listitem>
+<listitem>
+ <para>Condition &ndash; this is a verb which defines, together with the
+ Expression, whether a filter matches. A condition can be &eg;
+ <guilabel>contains</guilabel>, <guilabel>doesn't equal</guilabel> or
+ <guilabel>matches</guilabel>. See below for a more detailed
+ description.</para>
+</listitem>
+<listitem>
+ <para>Expression - this is a user-defined string which forms the body
+ of the filter, together with the Condition. See below for a more detailed
+ description of this component.</para>
+</listitem>
+</orderedlist>
+
+<para>All these components can be configured using the facilities in the frame
+labeled <guilabel>Filter properties</guilabel>. The possible states of the
+Condition component deserve a special explanation:</para>
+
+<itemizedlist>
+<listitem>
+ <para><guilabel>contains</guilabel>, <guilabel>doesn't contain</guilabel>:
+ this filter matches if the headline contains / doesn't contain the
+ specified expression.</para>
+ <note><para>The expression isn't treated case-sensitively, so the expressions
+ <quote>KDE</quote>, <quote>kDE</quote> or <quote>kde</quote> will all match
+ headlines which contain <quote>KDE</quote>.</para></note>
+</listitem>
+<listitem>
+ <para><guilabel>equals</guilabel>, <guilabel>doesn't equal</guilabel>:
+ this filter matches if the headline equals / doesn't equal the
+ specified expression.</para>
+ <note><para>The expression is treated case-sensitively, so of the expressions
+ <quote>&Linux;</quote>, <quote>linux</quote> or <quote>LINUX</quote>, only the
+ first will match <quote>&Linux;</quote>.</para></note>
+</listitem>
+<listitem>
+ <para><guilabel>matches</guilabel>: using this condition will make
+ &knewsticker; treat the given expression as a <quote>regular
+ expression</quote>. For further information on regular expressions you
+ might want to read
+ <ulink url="http://www.evolt.org/article/rating/20/22700/">this
+ article</ulink> which was published at
+ <ulink url="http://www.evolt.org">www.evolt.org</ulink>.</para>
+</listitem>
+</itemizedlist>
+</sect2>
+</sect1>
+
+<sect1 id="config-scroller">
+<title>Scroller Preferences</title>
+
+<screenshot>
+ <screeninfo>This is what the <guilabel>Scroller Preferences</guilabel> tab
+ of the preferences dialog looks like.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="kcmnewsticker-scrollerprefs.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the <guilabel>Scroller Preferences</guilabel>
+ tab of the preferences dialog looks like.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>This tab lets you define various options which affect &knewsticker;'s
+scroll text:</para>
+
+<variablelist>
+<varlistentry>
+ <term><guilabel>Scrolling speed</guilabel></term>
+ <listitem>
+ <para>This slider lets you define how fast the scrolltext should be
+ scrolling. If you have rather little space on your taskbar (and
+ therefore a rather small news ticker), you should probably set this
+ to a lower value so that you have a chance to read the headlines.
+ For wider news tickers (and better eyes), a faster text is probably
+ appropriate so that you have to wait for the next headline only as
+ little as possible.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Direction of scrolling</guilabel></term>
+ <listitem>
+ <para>These options allow you to define in what direction the text
+ should be scrolled, &eg; to the left or to the right, upwards or
+ downwards. You can also rotate the text by 90 or 270 degrees here,
+ which is not exactly readable but it makes sense for vertically
+ aligned panels.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Scrolltext font</guilabel></term>
+ <listitem>
+ <para>Click on the button at the right labeled <guilabel>Choose
+ Font...</guilabel> to choose the font which will be used for the
+ scrolling text.</para>
+ <tip><para>Certain fonts are harder to read than others, especially
+ when they are used for a scrolltext, so you should probably choose a font
+ which can even easily be read if it's moving.</para></tip>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Foreground color</guilabel></term>
+ <listitem>
+ <para>Click this button open a convenient color-selection dialog which
+ lets you choose the color which will be used for the foreground of the
+ scrolling text (&ie; the color of the text itself).</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Background color</guilabel></term>
+ <listitem>
+ <para>Click this button to open a convenient color-selection dialog
+ which lets you choose the color which will be used for the background
+ of the scrolling text.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Highlighted color</guilabel></term>
+ <listitem>
+ <para>Click this button to open a convenient color-selection dialog
+ which lets you choose the color which will be used for the color of
+ the headlines when they are highlighted (when you move the mouse over
+ them).</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Scroll the most recent headlines only</guilabel></term>
+ <listitem>
+ <para>Check this button to make the scrolltext show just the most
+ recent headline for each news site, instead of showing every
+ headline available from every news site.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Show icons</guilabel></term>
+ <listitem>
+ <para>Checking this box will make &knewsticker; show an icon (if
+ available) in front of each headline which is scrolled along; this
+ makes determining which news site provided each headline much
+ easier.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Temporarily slowed scrolling</guilabel></term>
+ <listitem>
+ <para>Check this box to make &knewsticker; slow the scrolling down
+ when you move the mouse cursor over the scrolling text. This makes
+ clicking on items and dragging away the icons (if enabled) a lot
+ easier.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Underline highlighted headline</guilabel></term>
+ <listitem>
+ <para>Check this box to have the currently highlighted headline
+ (&ie; the headline which is currently under the mouse cursor)
+ underlined.</para>
+ </listitem>
+</varlistentry>
+</variablelist>
+</sect1>
+</chapter>
+
+<chapter id="using-knewsticker">
+<title>Using &knewsticker;</title>
+
+<para>Using &knewsticker; is fairly straightforward and should give you no
+big problems, assuming that you have already
+<link linkend="configuration">configured it</link>. No matter
+whether you are running &knewsticker; in the main panel, in its own child
+panel or in its own window, it appears as an area with a scrolling text and
+a button with a small arrow next to it.</para>
+
+<sect1 id="applet-descr">
+<title>The Main Interface</title>
+
+<para>The area (it is white by default, but you can change the background
+color easily using the preferences dialog) with the scrolling text in it is
+called the <quote>news scroller</quote>. It keeps scrolling the downloaded
+headlines (or just the most recent headlines) continuously and provides easy
+access to the articles. If you see an interesting article, just click on it
+to open the &konqueror; web browser, showing the full article which belongs to
+the headline you clicked on. If you feel that a possibly interesting headline
+just scrolled out of view, you have some ways to influence the
+scrolling:</para>
+
+<itemizedlist>
+ <listitem>
+ <para>You can click on the news scroller with the
+ <mousebutton>left</mousebutton> mouse button and move the mouse around
+ (while holding the <mousebutton>left</mousebutton> mouse button pressed
+ down). The news scroller will continue scrolling as usual if you release
+ the left mouse button again.</para>
+ </listitem>
+ <listitem>
+ <para>If you own a so-called <quote>wheel mouse</quote>, you can use
+ the wheel on your mouse to scroll the headlines back and forth.</para>
+ </listitem>
+</itemizedlist>
+
+<para>There's also a very powerful <link linkend="contextmenu-descr">context
+menu</link>, which you can access either by clicking on the news scroller with
+the <mousebutton>right</mousebutton> mouse button, or by clicking on the
+arrow button with the <mousebutton>left</mousebutton> button. This context menu
+is the most interesting part of the applet, as it contains about all the
+functionality.</para>
+</sect1>
+
+<sect1 id="contextmenu-descr">
+<title>The Context Menu</title>
+
+<screenshot>
+ <screeninfo>This is what the context menu of &knewsticker; looks like, when
+ using just the default news sites.</screeninfo>
+ <mediaobject>
+ <imageobject><imagedata fileref="contextmenu.png" format="PNG"/></imageobject>
+ <textobject>
+ <phrase>This is what the context menu of &knewsticker; looks like,
+ when using just the default news sites.</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+
+<para>The context menu is basically split into two functional parts:</para>
+
+<para>The upper part shows a list of entries, each entry having a small icon like
+this <inlinemediaobject><imageobject>
+<imagedata fileref="newssite-icon.png" format="PNG"/></imageobject>
+</inlinemediaobject> next to it. This list represents
+the list of currently configured news sites. You can click on any of the news
+sources to open another menu which contains a listing of headlines which are
+available for that particular news site as well as an entry labeled <guilabel>
+<inlinemediaobject><imageobject><imagedata fileref="checknews-icon.png"
+format="PNG"/></imageobject></inlinemediaobject> Check news</guilabel> to
+refresh the headline list for this news site. Each of the headlines has an
+<inlinemediaobject><imageobject>
+<imagedata fileref="oldarticle-icon.png" format="PNG"/></imageobject>
+</inlinemediaobject> icon next to it indicating that that you
+have read that article already or the <inlinemediaobject><imageobject>
+<imagedata fileref="newarticle-icon.png" format="PNG"/></imageobject>
+</inlinemediaobject> if it is still
+unread.</para>
+
+<para>The lower part currently shows five entries (explained from top to
+bottom):</para>
+
+<variablelist>
+<varlistentry>
+ <term><guilabel><inlinemediaobject><imageobject><imagedata fileref="checknews-icon.png" format="PNG"/></imageobject></inlinemediaobject> Check news</guilabel></term>
+ <listitem>
+ <para>This entry has a little <inlinemediaobject><imageobject>
+ <imagedata fileref="checknews-icon.png" format="PNG"/></imageobject>
+ </inlinemediaobject> next to it. If you want to force &knewsticker;
+ to check the configured news sites for new articles, you can click
+ here.</para>
+ <note><para>This does not reset the internal timer which queries the
+ news sites for new headlines automatically in certain
+ intervals.</para></note>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel>Offline mode</guilabel></term>
+ <listitem>
+ <para>This entry in the context menu has no icon associated with it.
+ Click this button to enable a special <quote>offline mode</quote>
+ which pauses the internal timer for querying the news sites and
+ prevents any automatic download of new headlines. The offline mode
+ comes in handy if you have to leave your computer for a while
+ during which the system isn't connected to the Internet, as it
+ saves you from all the error messages which pop up if any of the
+ news sites couldn't be connected.</para>
+ <tip><para>You can still force a reload for single news sites as
+ well as for all the news sites by selecting the respective
+ <guilabel><inlinemediaobject><imageobject><imagedata
+ fileref="checknews-icon.png" format="PNG"/></imageobject>
+ </inlinemediaobject> Check news</guilabel> entry.</para></tip>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel><inlinemediaobject><imageobject><imagedata fileref="help-icon.png" format="PNG"/></imageobject></inlinemediaobject> Help</guilabel></term>
+ <listitem>
+ <para>This entry is marked with a small <inlinemediaobject>
+ <imageobject><imagedata fileref="help-icon.png" format="PNG"/>
+ </imageobject></inlinemediaobject>. Clicking on this entry opens
+ the &knewsticker; documentation (which you're reading in this
+ moment) which details all the features and abilities of
+ &knewsticker;.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel><inlinemediaobject><imageobject><imagedata fileref="knewsticker-icon.png" format="PNG"/></imageobject></inlinemediaobject> About</guilabel></term>
+ <listitem>
+ <para>This entry is marked with a small <inlinemediaobject>
+ <imageobject><imagedata fileref="knewsticker-icon.png" format="PNG"/>
+ </imageobject></inlinemediaobject>. Clicking on this entry opens
+ a small dialog showing who's to blame for &knewsticker; and credits
+ people who contributed significant enhancements for
+ it.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><guilabel><inlinemediaobject><imageobject><imagedata fileref="preferences-icon.png" format="PNG"/></imageobject></inlinemediaobject> Preferences</guilabel></term>
+ <listitem>
+ <para>This entry is easily recognizable due to the <inlinemediaobject>
+ <imageobject><imagedata fileref="preferences-icon.png" format="PNG"/>
+ </imageobject></inlinemediaobject> icon which is next to it. Select
+ this entry to open the <link linkend="configuration">preferences
+ dialog</link> which lets you customize all of the properties of
+ &knewsticker;</para>
+ </listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+</chapter>
+
+<chapter id="faq">
+<title>Frequently Asked Questions</title>
+
+&reporting.bugs;
+
+<qandaset id="faqlist">
+<qandaentry>
+ <question>
+ <para>Where do I find the &RSS; file for the news
+ site XYZ?</para>
+ </question>
+ <answer>
+ <para>It's possible that the news site you're referring to
+ doesn't provide any &RSS; file at all! Here's a short list of
+ websites which provide thousands of &RSS; feeds, sorted by
+ language and/or topic - for free:</para>
+
+ <itemizedlist>
+ <listitem><para>
+ <ulink url="http://www.webreference.com/services/news">WebReference.com</ulink>
+ </para></listitem>
+ <listitem><para>
+ <ulink url="http://www.newsisfree.com/syndicate.php">NewsIsFree</ulink>
+ </para></listitem>
+ <listitem><para>
+ <ulink url="http://w.moreover.com/categories/category_list_rss.html">MoreOver</ulink>
+ </para></listitem>
+ </itemizedlist>
+
+ <para>If you have found any interesting news sites which provide
+ such a backend, don't hesitate to send them to &Frerich.Raabe;
+ &Frerich.Raabe.mail; so that they can be included in
+ future releases. Thank you!</para>
+ </answer>
+</qandaentry>
+<qandaentry>
+ <question>
+ <para>How can I make &knewsticker; open articles in another browser
+ (&eg; Mozilla)?</para>
+ </question>
+ <answer>
+ <para>&knewsticker; will use whatever browser you have associated with
+ the text/html &MIME; type; the default browser used for viewing
+ &HTML; pages is &konqueror;.</para>
+ <para>You can find the dialog for changing this association by opening
+ the &kde; control center and browsing to <menuchoice><guimenu>KDE
+ Components</guimenu> <guisubmenu>File
+ Associations</guisubmenu></menuchoice>.</para>
+ </answer>
+</qandaentry>
+</qandaset>
+</chapter>
+
+<chapter id="credits">
+<title>Credits And License</title>
+
+<para>&knewsticker;</para>
+
+<para>Program copyright 2000, 2001, 2002, 2003 &Frerich.Raabe;
+&Frerich.Raabe.mail;</para>
+
+<para>Contributors:</para>
+
+<itemizedlist>
+<listitem>
+ <para>Malte Starostik <email>malte.starostik@t-online.de</email></para>
+</listitem>
+<listitem>
+ <para>&Wilco.Greven; &Wilco.Greven.mail;</para>
+</listitem>
+<listitem>
+ <para>Adriaan de Groot <email>adrig@sci.kun.nl</email></para>
+</listitem>
+</itemizedlist>
+
+<para>Documentation copyright 2001, 2002, 2003 &Frerich.Raabe;
+&Frerich.Raabe.mail;</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+&underFDL;
+&underBSDLicense;
+</chapter>
+
+<glossary id="glossary">
+<title>Glossary</title>
+
+<para>This chapter is intended to explain the various acronyms which have been
+used throughout the &knewsticker; documentation. If you feel any acronyms or
+terms are missing here, please don't hesitate to send an email to &Frerich.Raabe;
+&Frerich.Raabe.mail; so that they can be added. Thank you!</para>
+
+<glossentry id="gloss-rdf">
+ <glossterm>RDF</glossterm>
+ <glossdef>
+ <para>Resource Description Framework. A language derived from
+ &XML; which describes metadata. Commonly used as a
+ backend format for articles and other publications. For more detailed
+ information on <acronym>RDF</acronym> files, you might want to go
+ directly to the <ulink url="http://www.w3.org/RDF/">official page on
+ <acronym>RDF</acronym> files</ulink> at the
+ <ulink url="http://www.w3.org">World Wide Web
+ Consortium</ulink>.</para>
+ </glossdef>
+</glossentry>
+<glossentry id="gloss-rss">
+ <glossterm>&RSS;</glossterm>
+ <glossdef>
+ <para>The <acronym>RDF</acronym> Site Summary is actually an
+ extension to the <acronym>RDF</acronym> language. Quoting the official
+ <ulink url="http://www.purl.org/rss/1.0/">&RSS; v1.0
+ specification</ulink>:</para>
+ <para><quote><acronym>RDF</acronym> Site Summary
+ (&RSS;) is a lightweight multipurpose extensible
+ metadata description and syndication format. &RSS; is
+ an &XML; application, conforms to the
+ <acronym>W3C</acronym>'s <acronym>RDF</acronym> specification and is
+ extensible via &XML;-namespace and/or
+ <acronym>RDF</acronym> based modularization.</quote></para>
+ </glossdef>
+</glossentry>
+<glossentry id="gloss-xml">
+ <glossterm>&XML;</glossterm>
+ <glossdef>
+ <para>The Extensible Markup Language is the <quote>universal format
+ for structured documents and data on the Web</quote>. It's a derivative of
+ <acronym>SGML</acronym> which fits the needs of the world wide web. You
+ might want to check the the
+ <ulink url="http://www.w3.org/XML/">Extensible Markup Language</ulink>
+ page at the <ulink url="http://www.w3.org">World Wide Web
+ Consortium</ulink> for further information.</para>
+ </glossdef>
+</glossentry>
+<glossentry id="gloss-w3c">
+ <glossterm>W3C</glossterm>
+ <glossdef>
+ <para>An abbreviation for <quote>World Wide Web Consortium</quote>.
+ Quoting the <ulink url="http://www.w3.org">official homepage</ulink> of
+ the <acronym>W3C</acronym>, <quote>the World Wide Web Consortium
+ (<acronym>W3C</acronym>) develops interoperable technologies
+ (specifications, guidelines, software, and tools) to lead the Web to
+ its full potential as a forum for information, commerce, communication,
+ and collective understanding</quote></para>
+ </glossdef>
+</glossentry>
+<glossentry id="gloss-url">
+ <glossterm>&URL;</glossterm>
+ <glossdef>
+ <para>&URL; stands for <quote>Uniform Resource Locator</quote>,
+ a specially formatted string which can reference resources like images,
+ documents and other things on the Internet. Please refer to the
+ corresponding <ulink url="http://www.w3.org/Addressing/">webpage</ulink>
+ for more detailed information on this topic.</para>
+ </glossdef>
+</glossentry>
+<glossentry id="gloss-dcop">
+ <glossterm>&DCOP;</glossterm>
+ <glossdef>
+ <para>The Desktop COmmunication Protocol is a way for applications
+ to communicate to each other. For instance, &knewsticker;'s
+ <link linkend="configuration">configuration dialog</link> uses
+ &DCOP; to tell the applet itself about the current
+ configuration.</para>
+ <para>&knewsticker; provides an extensive
+ <link linkend="develinfo">&DCOP; interface</link>,
+ which makes it possible to control many of &knewsticker;'s functions
+ from the commandline.</para>
+ <para>For more detailled information on &DCOP;
+ you might want to visit
+ <ulink url="http://developer.kde.org/documentation/library/2.0-api/dcop/HOWTO.html">http://developer.kde.org/documentation/library/2.0-api/dcop/HOWTO.html</ulink>
+ for a complete explanation.</para>
+ </glossdef>
+</glossentry>
+</glossary>
+
+<appendix id="develinfo">
+<title>Information For Developers And Advanced Users</title>
+
+<para>&knewsticker; features a currently rather extensive, and steadily growing
+&DCOP; interface. This is not only used to communicate with
+other applications, it makes it possible to control &knewsticker; with a
+shell script as well. The more the interface is extended, the more useful it will
+become and the more flexible &knewsticker; will be controllable from a
+script.</para>
+
+<para>To use these &DCOP; functions you can either use the
+<application>dcop</application> commandline program or use the more convenient
+<application>KDCOP</application> application. Both provide the same
+functionality so it's actually just a matter of taste which program you prefer.
+:-)</para>
+
+<para>This chapter assumes that you're using the commandline program
+<application>dcop</application>. To access &knewsticker;'s
+&DCOP; functions make sure that &knewsticker; is <link
+linkend="starting-knewsticker">started</link> and then just enter something like
+this at the console:</para>
+
+<screen width="40">
+<prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>[function]</option></userinput>
+</screen>
+
+<note><para>If an error appears that tells you that <filename>dcop</filename>
+couldn't be found or executed, please check whether the file
+<filename>dcop</filename> exists in
+<filename class="directory">$<envar>KDEDIR</envar>/bin</filename> and make sure its
+permissions are set appropriately.</para></note>
+
+<para>In that command line, just replace <quote>[function]</quote> with the
+appropriate function name, &ie; type</para>
+
+<screen width="40">
+<prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>updateNews</option></userinput>
+</screen>
+
+<para>to make &knewsticker; check for new news items and download them if
+necessary.</para>
+
+<sect1 id="dcop-reference">
+<title>&DCOP; Reference</title>
+
+<para>In this section, all methods which are accessible via &knewsticker;'s
+&DCOP; interface are listed.</para>
+
+<variablelist>
+<varlistentry>
+ <term><command>updateNews</command></term>
+ <listitem>
+ <para>This function forces &knewsticker; to update the internal list
+ of articles (&ie; it queries the list of news sources which has been
+ <link linkend="config-newssources">configured</link> for new news) and
+ downloads them when necessary.</para>
+ <note><para>This also works if &knewsticker; is currently in offline
+ mode.</para></note>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>updateNews</option></userinput>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>reparseConfig</command></term>
+ <listitem>
+ <para>The <command>reparseConfig</command> command makes &knewsticker;
+ reload its configuration from the configuration file. This function is
+ used by the <link linkend="configuration">configuration dialog</link>
+ to talk to &knewsticker; but you can use it if you modified the
+ configuration file by hand.</para>
+ <tip><para>The configuration file is saved in
+ <filename>~/.kde/share/config/knewsticker_appletrc</filename></para></tip>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>reparseConfig</option></userinput>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>setOfflineMode [bool]</command></term>
+ <listitem>
+ <para>You can call this function to define whether &knewsticker; is
+ currently in the offline mode (&ie; whether &knewsticker; should query
+ the <link linkend="config-newssources">configured news sites</link> for
+ new news).</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>setOfflineMode</option> <option>true</option></userinput>
+ </screen>
+ <para>to enable the offline mode, or type</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>setOfflineMode</option> <option>false</option></userinput>
+ </screen>
+ <para>to disable offline mode.</para>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>interval</command></term>
+ <listitem>
+ <para>Returns the currently configured news query interval in
+ minutes.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>interval</option></userinput>
+ 30
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>scrollingSpeed</command></term>
+ <listitem>
+ <para>Returns the currently configured scrolling speed. The returned
+ scrolling speed is specified in pixels per second.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>scrollingSpeed</option></userinput>
+ 20
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>mouseWheelSpeed</command></term>
+ <listitem>
+ <para>Returns the number of pixels the scrolltext gets shifted
+ per mousewheel step.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>mouseWheelSpeed</option></userinput>
+ 15
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>scrollingDirection</command></term>
+ <listitem>
+ <para>Returns an integer which corresponds to the direction the
+ scrolltext is scrolling in:</para>
+ <itemizedlist>
+ <listitem><para>1 = To the left</para></listitem>
+ <listitem><para>2 = To the right</para></listitem>
+ <listitem><para>3 = Upwards</para></listitem>
+ <listitem><para>4 = Downwards</para></listitem>
+ <listitem><para>5 = Upwards, rotated</para></listitem>
+ <listitem><para>6 = Downwards, rotated</para></listitem>
+ </itemizedlist>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>scrollingDirection</option></userinput>
+ 1
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>customNames</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ uses custom names for the news sites.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>customNames</option></userinput>
+ false
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>endlessScrolling</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ has the endless scrolling option enabled.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>endlessScrolling</option></userinput>
+ true
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>scrollMostRecentOnly</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ currently only scrolls the most recent headlines for each news
+ site.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>scrollMostRecentOnly</option></userinput>
+ false
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>offlineMode</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ is currently in offline mode.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>offlineMode</option></userinput>
+ false
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>underlineHighlighted</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ was told to underline the headline which is currently below the mouse
+ cursor.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>underlineHighlighted</option></userinput>
+ true
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>showIcons</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ currently shows the icon of the news site each particular headline
+ was published at in front of the headline.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>showIcons</option></userinput>
+ true
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>slowedScrolling</command></term>
+ <listitem>
+ <para>Returns either 'true' or 'false', depending on whether &knewsticker;
+ has the <quote>slowed scrolling</quote> feature activated.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>slowedScrolling</option></userinput>
+ false
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>foregroundColor</command></term>
+ <listitem>
+ <para>Returns the currently configured foreground color as a string
+ in the format <quote>#rrggbb</quote>, where <quote>rr</quote>,
+ <quote>gg</quote> and <quote>bb</quote> are two-digit hexadecimal
+ values representing the intensity of the red, green and blue components
+ in a scale of 00-ff.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>foregroundColor</option></userinput>
+ #804000
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>backgroundColor</command></term>
+ <listitem>
+ <para>Returns the currently configured background color as a string
+ in the format <quote>#rrggbb</quote>, where <quote>rr</quote>,
+ <quote>gg</quote> and <quote>bb</quote> are two-digit hexadecimal
+ values representing the intensity of the red, green and blue components
+ in a scale of 00-ff.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>backgroundColor</option></userinput>
+ #0030ff
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>highlightedColor</command></term>
+ <listitem>
+ <para>Returns the currently configured highlight color as a string
+ in the format <quote>#rrggbb</quote>, where <quote>rr</quote>,
+ <quote>gg</quote> and <quote>bb</quote> are two-digit hexadecimal
+ values representing the intensity of the red, green and blue components
+ in a scale of 00-ff.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>highlightedColor</option></userinput>
+ #000080
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+<varlistentry>
+ <term><command>newsSources</command></term>
+ <listitem>
+ <para>Returns the list of currently registered news sources. Note that
+ this returns all news sources, not just the selected ones. In the
+ output, each news source name will be printed on a line.</para>
+ <para>Example:</para>
+ <screen width="40">
+ <prompt>%</prompt> <userinput><command>dcop</command> <option>knewsticker</option> <option>KNewsTicker</option> <option>newsSources</option></userinput>
+ Freshmeat
+ GNOME News
+ dot.kde.org
+ Slashdot.org
+ <prompt>%</prompt>
+ </screen>
+ </listitem>
+</varlistentry>
+</variablelist>
+</sect1>
+</appendix>
+
+&documentation.index;
+
+</book>
diff --git a/doc/knewsticker/kcmnewsticker-filters.png b/doc/knewsticker/kcmnewsticker-filters.png
new file mode 100644
index 00000000..6386a113
--- /dev/null
+++ b/doc/knewsticker/kcmnewsticker-filters.png
Binary files differ
diff --git a/doc/knewsticker/kcmnewsticker-general.png b/doc/knewsticker/kcmnewsticker-general.png
new file mode 100644
index 00000000..e0b3e58a
--- /dev/null
+++ b/doc/knewsticker/kcmnewsticker-general.png
Binary files differ
diff --git a/doc/knewsticker/kcmnewsticker-newssitedialog.png b/doc/knewsticker/kcmnewsticker-newssitedialog.png
new file mode 100644
index 00000000..cc3e0fdf
--- /dev/null
+++ b/doc/knewsticker/kcmnewsticker-newssitedialog.png
Binary files differ
diff --git a/doc/knewsticker/kcmnewsticker-newssources.png b/doc/knewsticker/kcmnewsticker-newssources.png
new file mode 100644
index 00000000..da54789c
--- /dev/null
+++ b/doc/knewsticker/kcmnewsticker-newssources.png
Binary files differ
diff --git a/doc/knewsticker/kcmnewsticker-scrollerprefs.png b/doc/knewsticker/kcmnewsticker-scrollerprefs.png
new file mode 100644
index 00000000..7f2538b1
--- /dev/null
+++ b/doc/knewsticker/kcmnewsticker-scrollerprefs.png
Binary files differ
diff --git a/doc/knewsticker/kcontrol-icon.png b/doc/knewsticker/kcontrol-icon.png
new file mode 100644
index 00000000..7fc0da9b
--- /dev/null
+++ b/doc/knewsticker/kcontrol-icon.png
Binary files differ
diff --git a/doc/knewsticker/knewsticker-childpanel.png b/doc/knewsticker/knewsticker-childpanel.png
new file mode 100644
index 00000000..a1eeb83d
--- /dev/null
+++ b/doc/knewsticker/knewsticker-childpanel.png
Binary files differ
diff --git a/doc/knewsticker/knewsticker-icon.png b/doc/knewsticker/knewsticker-icon.png
new file mode 100644
index 00000000..714e9834
--- /dev/null
+++ b/doc/knewsticker/knewsticker-icon.png
Binary files differ
diff --git a/doc/knewsticker/knewsticker-kicker.png b/doc/knewsticker/knewsticker-kicker.png
new file mode 100644
index 00000000..75bbf87d
--- /dev/null
+++ b/doc/knewsticker/knewsticker-kicker.png
Binary files differ
diff --git a/doc/knewsticker/knewsticker-ownwindow.png b/doc/knewsticker/knewsticker-ownwindow.png
new file mode 100644
index 00000000..f0da9e27
--- /dev/null
+++ b/doc/knewsticker/knewsticker-ownwindow.png
Binary files differ
diff --git a/doc/knewsticker/newarticle-icon.png b/doc/knewsticker/newarticle-icon.png
new file mode 100644
index 00000000..43b02977
--- /dev/null
+++ b/doc/knewsticker/newarticle-icon.png
Binary files differ
diff --git a/doc/knewsticker/newssite-icon.png b/doc/knewsticker/newssite-icon.png
new file mode 100644
index 00000000..a4be959f
--- /dev/null
+++ b/doc/knewsticker/newssite-icon.png
Binary files differ
diff --git a/doc/knewsticker/oldarticle-icon.png b/doc/knewsticker/oldarticle-icon.png
new file mode 100644
index 00000000..a16644e7
--- /dev/null
+++ b/doc/knewsticker/oldarticle-icon.png
Binary files differ
diff --git a/doc/knewsticker/preferences-icon.png b/doc/knewsticker/preferences-icon.png
new file mode 100644
index 00000000..0c95c52c
--- /dev/null
+++ b/doc/knewsticker/preferences-icon.png
Binary files differ
diff --git a/doc/kopete/Makefile.am b/doc/kopete/Makefile.am
new file mode 100644
index 00000000..41691557
--- /dev/null
+++ b/doc/kopete/Makefile.am
@@ -0,0 +1,3 @@
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kopete/chatstyle.docbook b/doc/kopete/chatstyle.docbook
new file mode 100644
index 00000000..991bee25
--- /dev/null
+++ b/doc/kopete/chatstyle.docbook
@@ -0,0 +1,363 @@
+<!-- Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net> -->
+<!-- Licensed under the GNU Free Documentation License -->
+
+<appendix id="chatwindowstyleguide">
+<title>&kopete; Chat Window Style Guide</title>
+<sect1 id="chatstyle-reference">
+<title>&kopete; Chat Window Style reference.</title>
+<para>
+Beginning with &kopete; 0.12, we are now using <ulink
+url="http://www.adiumx.com/">Adium</ulink> format for our
+Chat Window style. The theme format is based on <acronym>HTML</acronym> templates and <acronym>CSS</acronym>. They
+are easier to make and develop, only a knowledge of <acronym>HTML</acronym> and <acronym>CSS</acronym> is needed. Also, styles can have variants (defined as <acronym>CSS</acronym> file) which add more customisation value :).
+</para>
+
+<sect2 id="chatstyle-reference-guide">
+ <title>Reference guide.</title>
+<para>
+Adium format consist of a directory structure, <acronym>HTML</acronym> templates, <acronym>CSS</acronym> files and keywords that are replaced each time the template is processed. The final conversation is a <acronym>XHTML</acronym> page where messages are added with <acronym>DOM</acronym> operations. The central element is a div element named <filename>Chat</filename>. Before and after this div element goes the Header and Footer template. Messages are childs of the <filename>Chat</filename> div element.
+</para>
+
+<sect3 id="chatstyle-refrerence-directory">
+ <title>Directory Structure</title>
+<para>A style must respect this directory structure. Code in &kopete; are thinking around this directory structure. When archiving the style, archive the <filename>styleName</filename> directory. The directory structure is a structure of a <application>Mac OS X</application> bundle for those familiar with that operating system. Also you must respect the case displayed here, because a <acronym>UNIX</acronym> system is case-sensitive.</para>
+<programlisting>
+styleName\ (can have .adiumMessageStyle as suffix, because in Mac OS X it is a bundle)
+ Contents\
+ Info.plist
+ Resources\
+ main.css
+ Header.html
+ Footer.html
+ Status.html
+ Incoming\ (and Outgoing\)
+ Content.html
+ NextContent.html (for consecutive messages)
+ Context.html (for message history)
+ NextContext.html
+ Action.html
+ Variants\
+ *.css
+</programlisting>
+</sect3>
+
+<sect3 id="chatstyle-reference-divinsert">
+ <title>About &lt;div id="insert"&gt;&lt;/div&gt;</title>
+ <para>This is a special div element used internally. It is a placeholder to indicate where to insert the next message. If it's a new message, it is removed and the new message take place. But if it's a consecutive message, the div element is replaced with the content of new message. This special div element is <emphasis>required</emphasis> in Content,Context,NextContent,NextContext templates. Though it not harm to put it also in Action and Status template.
+ </para>
+</sect3>
+
+<sect3 id="chatstyle-reference-templates">
+ <title>HTML templates.</title>
+
+<variablelist><title>Template description.</title>
+
+<varlistentry><term><filename>Header.html</filename> (Required)</term>
+<listitem>
+<para>
+Use the Header template to display a nice header to the conversation. This template is insered before <filename>Chat</filename> div element. If you don't use it, just put an empty file.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>Footer.html</filename> (Required)</term>
+<listitem>
+<para>
+This is mostly the same as Header but it is for the fotter of a conversation. This template is insered after <filename>Chat</filename> div element. If you don't use it, just put an empty file.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>Status.html</filename> (Required)</term>
+<listitem>
+<para>
+This template is used to display a internal message. Internal messages such as status change, message from Kopete(ex: Incoming File Transfert). When the style do not supply a Action template, it is used to display Action message.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>Incoming/Content.html</filename></term>
+ <term><filename>Outgoing/Content.html</filename> (Required)</term>
+<listitem>
+<para>
+The content template is the message core. Think it as a block that will hold messages.. Make sure it is ready to receive consecutive messages, don't design it to only display one message. Consecutive messages will be inserted at the div insert element.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>Incoming/NextContent.html</filename></term>
+ <term><filename>Outgoing/NextContent.html</filename> (Required)</term>
+<listitem>
+<para>
+The NextContent template is a message fragment for consecutive messages. It will be inserted into the main message block. The HTML template should contain the bare minimum to display a message.
+</para>
+</listitem>
+</varlistentry>
+
+
+<varlistentry><term><filename>Incoming/Action.html</filename></term>
+ <term><filename>Outgoing/Action.html</filename> (Optional) (&kopete; Extension)</term>
+<listitem>
+<para>
+This template is a &kopete; extension to the Adium format. It is available for Incoming and Outgoing direction. Action messages are special message to tell we are doing a action. Example: "/me is installing &kopete;" would be displayed as "DarkShock is installing &kopete;".
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>Incoming/Context.html</filename></term>
+ <term><filename>Incoming/NextContext.html</filename></term>
+ <term><filename>Outgoing/Context.html</filename></term>
+ <term><filename>Outgoing/NextContext.html</filename> (Optional)</term>
+<listitem>
+<para>
+These templates are not used in Kopete. In Adium, they are used to display history. It is mostly the same thing as Content and NextContent but with some differences to distinguist from normal messages.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect3>
+
+<sect3 id="chatstyle-reference-css">
+ <title>About CSS styles and Variants</title>
+ <para>HTML template are used to describe how the structure is made. But all the style is described in <acronym>CSS</acronym> files. <filename>main.css</filename> is the main style, where variants are just alterations of the main style. Examples of variants are differents colors, no display of user photo. Both <filename>main.css</filename> and variants and imported in final <acronym>XHTML</acronym> page.</para>
+ <sect4 id="chatstyle-reference-css-main">
+ <title>-<filename>main.css</filename></title>
+ <para>This is the main <acronym>CSS</acronym> file which is common for all variants. This file should contain all the main description of the style.</para>
+ </sect4>
+ <sect4 id="chatstyle-refrence-css-variants">
+ <title>-Variants</title>
+ <para>Variants are <acronym>CSS</acronym> files located in <filename>Variants/</filename> directory. Each variant is a single <acronym>CSS</acronym> file that include the <filename>main.css</filename> and do alteration to the main style.</para>
+ </sect4>
+</sect3>
+
+<sect3 id="chatstyle-reference-debug">
+ <title>Debugging styles</title>
+ <para>Here is two tips for testing a style while creating it.</para>
+ <sect4 id="chatstyle-reference-debug-save">
+ <title>-Save a sample conversation.</title>
+ <para>In Chat Window, you can <action>save</action> a conversation. This is a copy of the internal XHTML page displayed. Use it in <application>Konqueror</application> to test your <acronym>CSS</acronym> files.</para>
+ </sect4>
+ <sect4 id="chatstyle-reference-debug-disable-cache">
+ <title>-Disable style cache.</title>
+ <para>A little configuration switch exist to disable the style cache. When enabled, it reload the HTML templates each time the style is asked. Add the following lines to your <filename>kopeterc</filename>. Very useful when testing a style in &kopete;</para>
+ <programlisting>
+[KopeteStyleDebug]
+disableStyleCache=true
+</programlisting>
+ </sect4>
+</sect3>
+
+</sect2>
+
+<sect2 id="chatstyle-reference-keywords">
+ <title>Keywords reference</title>
+ <para>Keywords are likes holes to fill with details. On each new message, they are replaced with the correct value corresponding to their context. To fully support all features of Kopete, we added some keywords extentions to the Adium. Also some keywords are only available in certain context.</para>
+
+<variablelist><title>Keywords list for Header and Footer templates. </title>
+<para>There keywords are processed at the beginning of the chat.</para>
+<varlistentry><term><filename>%chatName%</filename></term>
+<listitem>
+<para>
+This is the name of the current chat session. For a typical session, it display the name of the contact and his status. For <acronym>IRC</acronym>, it display the topic of a channel.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%sourceName%</filename></term>
+ <term><filename>%destinationName%</filename></term>
+<listitem>
+<para>
+These are the name of the contacts for a chatsession. <filename>%sourceName%</filename> is your name. <filename>%destinationName%</filename> is the name of the contact you are chatting with. Prefer <filename>%chatName%</filename> over those, because they could be confusing for groupchat and IRC.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%incomingIconPath%</filename></term>
+ <term><filename>%outgoingIconPath%</filename></term>
+<listitem>
+<para>
+These are the image/photo/avatar of the contacts for a chatsession. Incoming represent the contact photo and Outgoing represent your own photo. If they are no photo available, it use <filename>buddy_icon.png</filename> image which is located in <filename>Incoming</filename> or <filename>Outgoing</filename> directory.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%timeOpened%</filename></term>
+ <term><filename>%timeOpened{X}%</filename></term>
+<listitem>
+<para>
+It is the time when the chat session begin. <filename>%timeOpened%</filename> use the default time format for the current locale. If you want to use a specific time format, use <filename>%timeOpened{X}%</filename> where X is a string containing the time format. The time parameters are the same as the glibc function <function>strftime</function>. Do <command>man strftime</command> to get the list of available parameters.
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<variablelist><title>Keywords list for Content, NextContent, Context, NextContext, Action template</title>
+<para>There keywords are processed for each message.</para>
+
+<varlistentry><term><filename>%userIconPath%</filename></term>
+<listitem>
+<para>
+This is the image/photo/avatar of the contact associated with the message. If they are no photo available, it use <filename>buddy_icon.png</filename> image which is located in <filename>Incoming</filename> and <filename>Outgoing</filename> directory depending of the message direction.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%senderScreenName%</filename></term>
+<listitem>
+<para>
+This is the contact ID of the contact associated with the message. Examples: me@hotmail.com, 45566576, JohnSmith.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%sender%</filename></term>
+<listitem>
+<para>
+This is the name of the contact associated with the message. It use MetaContact display name as a source.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%service%</filename></term>
+<listitem>
+<para>
+Display the name of the service associated with the message. Examples: Jabber, Yahoo, MSN.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%textbackgroundcolor{X}%</filename></term>
+<listitem>
+<para>
+In &kopete;, this keyword is used to represent the highlight background color. Ignore parameter in the braces and only use it as <filename>%textbackgroundcolor{}</filename>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%senderStatusIcon%</filename> (&kopete; extension)</term>
+<listitem>
+<para>
+Display the status icon of the contact associated with the message. It's a file path.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%senderColor%</filename></term> <term><filename>%senderColor{N}%</filename> (&kopete; extension)</term>
+<listitem>
+<para>
+Generate a color from the sender contact id. Can be used to display a different color for contact nickname.
+</para>
+<para>
+<filename>%senderColor{N}%</filename> where N is a positive number. If N is greater than 100, it represent a lighter color than the contact's color.
+If N equal 150 it is a color which is 50% brighter. If N is less than 100 then it is a darker color. Usefull for having a background coloured differently for each contact.
+</para>
+<para>
+If you want to use theses colors in a variant, but not in the main style, you have to workaround.
+<programlisting>
+<![CDATA[
+<div style="color:%senderColor%;border:none;border-color:%senderColor{40}%;"><p class="message">...</p></div>
+]]>
+</programlisting>
+you can apply color ro the p.message element in your <filename>main.css</filename> file, and in your varient put something like
+<programlisting>
+ p.message { color:inherit; border-color:inherit; }
+</programlisting>
+
+</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<variablelist><title>Keyword list common for messages and Status.html</title>
+
+<varlistentry><term><filename>%message%</filename></term>
+<listitem>
+<para>
+The message itself. This is a HTML fragment.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%time%</filename></term>
+ <term><filename>%time{X}%</filename></term>
+<listitem>
+<para>
+The time when the message was received. <filename>%time%</filename> use the default time format for the current locale. If you want to use a specific time format, use <filename>%time{X}%</filename> where X is a string containing the time format. The time parameters are the same as the glibc function <function>strftime</function>. Do <command>man strftime</command> to get the list of available parameters.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry><term><filename>%messageDirection%</filename> (&kopete; Extension)</term>
+<listitem>
+<para>
+Represent the message direction, if the message must be displayed right-to-left or left-to-right. The values are either "rtl" or "ltr". Read <link linkend="chatstyle-guideline-direction">Message Direction guideline</link> to see how to use this keyword properly.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect2>
+</sect1>
+
+<sect1 id="chatstyle-guideline">
+ <title>&kopete; Chat Window Style Guideline</title>
+<para>The &kopete; Chat Window Style Guideline is a set of things that your Chat Window style must support to be compilant with Kopete.</para>
+<sect2 id="chatstyle-guideline-highlight">
+ <title>Support highlight</title>
+ <para>Your style must show hightlight. In Kopete and Adium, the <filename>%textbackgroundcolor{}%</filename> is replaced with the hightlight color. Add this style attribute: <userinput>background-color: %textbackgroundcolor{}%</userinput> to the HTML element that display the message.</para>
+</sect2>
+<sect2 id="chatstyle-guideline-consecutive">
+ <title>Consecutive message templates are required.</title>
+ <para>This guideline is for people rewriting old XSL styles to the new format. All styles must supply a template for consecutive messages. It is now a default feature.</para>
+</sect2>
+
+<sect2 id="chatstyle-guideline-encoding">
+ <title>Use <acronym>UTF-8</acronym> encoding.</title>
+ <para>The title said it all. You must save your files to <acronym>UTF-8</acronym>.</para>
+</sect2>
+
+<sect2 id="chatstyle-guideline-info">
+ <title>Supply <filename>Contents/Info.plist</filename> for interopability with Adium</title>
+ <para>The <filename>Contents/Info.plist</filename> file is not used in Kopete yet. But if you want your style to be compatible with <application>Adium</application>, you must supply that file. Here a basic example file. Strings to replace are enclosed with "$".</para>
+<programlisting>
+<![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleGetInfoString</key>
+ <string>$Your style full name$</string>
+ <key>CFBundleIdentifier</key>
+ <string>$Your style ID in the form: com.adiumx.smooth.operator.style$</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>1.0</string>
+ <key>CFBundleName</key>
+ <string>$Your style name here$</string>
+ <key>CFBundlePackageType</key>
+ <string>AdIM</string>
+ <key>MessageViewVersion</key>
+ <integer>3</integer>
+</dict>
+</plist>
+]]>
+</programlisting>
+
+</sect2>
+
+<sect2 id="chatstyle-guideline-buddyicon">
+ <title>Supply <filename>buddy_icon.png</filename></title>
+ <para>You must place a file named <filename>buddy_icon.png</filename> in the <filename>Incoming</filename> and <filename>Outgoing</filename>. These images will be used when the contact have no photo.</para>
+</sect2>
+
+<sect2 id="chatstyle-guideline-direction">
+ <title>Support right-to-left languages with <filename>%messageDirection%</filename> keyword.</title>
+ <para><filename>%messageDirection%</filename> keyword is present for languages in the world that write right-to-left. It define the message direction, if it's "rtl"(right-to-left) or "ltr"(left-to-right). Add this style attribute to the HTML element that display the message: <userinput>direction: %messageDirection%</userinput>. Style preview in appearance config include a right-to-left to check if your style display it correctly. It should begin the string from the right.</para>
+</sect2>
+</sect1>
+</appendix>
diff --git a/doc/kopete/index.docbook b/doc/kopete/index.docbook
new file mode 100644
index 00000000..c6fba958
--- /dev/null
+++ b/doc/kopete/index.docbook
@@ -0,0 +1,1025 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kopete "<application>Kopete</application>">
+ <!ENTITY Will.Stephenson "<personname><firstname>Will</firstname><surname>Stephenson</surname></personname>">
+ <!ENTITY Will.Stephenson.mail "<email>lists@stevello.free-online.co.uk</email>">
+ <!ENTITY Matt.Rogers "<personname><firstname>Matt</firstname><surname>Rogers</surname></personname>">
+ <!ENTITY Matt.Rogers.mail "<email>mattr@kde.org</email>">
+ <!ENTITY Michael.Larouche "<personname><firstname>Michaël</firstname><surname>Larouche</surname></personname>">
+ <!ENTITY Michael.Larouche.mail "<email>michael.larouche@kdemail.net</email>">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY kappname "&kopete;">
+ <!ENTITY im "<acronym>IM</acronym>">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE">
+ <!ENTITY kopetewww "<ulink url='http://kopete.kde.org'>http://kopete.kde.org</ulink>">
+ <!ENTITY kopete-menus SYSTEM "menus.docbook">
+ <!ENTITY kopete-chatstyle SYSTEM "chatstyle.docbook">
+]>
+<!--
+Intro (1st draft, Will)
+Getting Started (1st draft, Will)
+ Add an account
+ Go online
+ Start chatting
+Using Kopete (1st draft, Will)
+ Contact List window
+ Layout
+ Menu
+ Toolbar
+ Contact List view
+ Status bar
+ Set your presence
+ Start a chat
+ Organising contacts
+ Adding contacts
+ Metacontacts
+ Grouping Contacts
+ Within Metacontacts
+ Renaming Contacts
+ Configure &kopete;
+ Quit.
+ Shortcuts
+ Chat window
+ Layout
+ Chat members list
+ Status bar (typing notifications, etc )
+ Tabbing
+ Starting group chats
+ Shortcuts
+ File transfers
+Configuring Kopete (1st draft, Will)
+ Global Shortcuts
+ Configure Dialog
+ Adding/Editing Accounts (
+ Behaviour
+ Appearance
+ Loading Plugins
+Plugins (Outlined, Will)
+ (Section on each plugin, use, and configuration)
+Menus (Done, Will)
+ (Short description of each menu item)
+FAQ (1st draft, Will)
+Specialised Tasks
+ (Any tricks involving _use_ of Kopete)
+ Command line flags
+Credits (1st draft, Will)
+Appendix: Obtaining and installation (1st draft, Will)
+Appendix: Chat Window Style Guide (1st draft, Michaël)
+-->
+
+<book lang="&language;">
+<bookinfo>
+<title>The &kopete; Handbook</title>
+
+<authorgroup>
+
+<author>&Will.Stephenson; &Will.Stephenson.mail;</author>
+<author>&Matt.Rogers; &Matt.Rogers.mail;</author>
+<author>&Michael.Larouche; &Michael.Larouche.mail;</author>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2006-12-15</date>
+<releaseinfo>0.12</releaseinfo>
+
+<copyright>
+<year>2003, 2004, 2005, 2006</year>
+</copyright>
+
+<!-- Abstract about this handbook -->
+
+<abstract>
+<para>
+&kopete; is &kde;'s multi-protocol instant messenger client.
+</para>
+</abstract>
+
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>IM</keyword>
+<keyword>Instant</keyword>
+<keyword>Messaging</keyword>
+<keyword>Jabber</keyword>
+<keyword>IRC</keyword>
+<keyword>MSN</keyword>
+<keyword>ICQ</keyword>
+<keyword>AIM</keyword>
+<keyword>Yahoo</keyword>
+<keyword>Gadu-Gadu</keyword>
+<keyword>GroupWise</keyword>
+<keyword>Novell</keyword>
+<keyword>WinPopup</keyword>
+<keyword>SMS</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+<!--
+<chapterinfo>
+<authorgroup>
+<author>
+<firstname>Will</firstname>
+<surname>Stephenson</surname>
+<affiliation><address>
+<email>lists@stevello.free-online.co.uk</email>
+</address></affiliation>
+</author>
+</authorgroup>
+<date>2003-10-05</date>
+<releaseinfo>0.8</releaseinfo>
+</chapterinfo>
+-->
+<sect1 id="intro-to-kopete">
+<title>&kopete;, the &kde; instant messaging client</title>
+<sect2 id="intro-to-kopete-next">
+<title>Before starting...</title>
+<para>If you're not familiar with Instant Messaging, please read the <link linkend="getting-started">Getting Started</link> section to learn about this wonderful world before continuing.</para>
+</sect2>
+<sect2 id="intro-to-kopete-about">
+<title>What is &kopete;?</title>
+<para>&kopete; is the &kde; instant messaging (&im;) client. It allows you to communicate with your friends and colleagues using various instant messaging services. A single program is easy to learn and convenient if your friends or colleagues use more than one &im; service. </para>
+<para>&kopete; is designed to integrate well with your &kde; desktop; to make it immediately familiar. The user interface is clean and simple, without any frills to distract the user. At the same time, &kopete; aims to make communication the focus of &im;, by removing the differences between different &im; systems. One feature &kopete; introduced to support this is the <link linkend="organise-contacts-metacontacts">Metacontact</link>, combining the various means there are to contact someone into a single <quote>person</quote> in your contact list. Other multiprotocol instant messengers list the same person's various &im; accounts separately, making it confusing for non experienced people. &kopete; makes life easy: a metacontact is a person, and contacts are ways to communicate with that person. You will recognize contacts in a metacontact easily as small icons representing the &im; services you could use to communicate with that person.</para>
+<para>&kopete; is intended for all levels of users. Out of the box, it supports a minimal set of functions to make chatting as easy as possible. More advanced users can add extra functions such as Cryptography with &kopete;'s <link linkend="plugins">plugin system</link>.</para>
+</sect2>
+<sect2 id="intro-to-kopete-web">
+<title>More &kopete; Information on the Web</title>
+<para>For more info about the &kopete; project; the team maintains a website at &kopetewww;. The latest news and updates are always available there.</para>
+<para>If you need to contact the team, the &kopete; developers' mailing list is hosted at <ulink url="https://mail.kde.org/mailman/listinfo/kopete-devel">https://mail.kde.org/mailman/listinfo/kopete-devel</ulink>.</para>
+<para>If you want live support, there is an Internet Relay Chat channel for &kopete; where you can find the team discussing technical (well, not always) issues or just hanging out. You can use any IRC client to join the channel (including &kopete;), just add an IRC contact and use <userinput>irc.kde.org</userinput> as the server and <userinput>#kopete</userinput> as the channel name. See you there!</para>
+</sect2>
+</sect1>
+<sect1 id="intro-to-instant-messaging">
+<title>Introduction to Instant Messaging</title>
+<para>What is Instant Messaging (&im;)? &im; is a way for you to communicate instantly with your friends over the Internet. That might not sound so different to email. Have you ever noticed how cumbersome it is to have a brief conversation via email? You have to click Reply to each message, then find the right spot in the message to type something new, then send it. Then you have to wait for the next message to arrive! &im; lets you to have a conversation almost as naturally as on the phone or face to face, by typing messages into a window shared between you and your friend's screens.</para>
+<para>Another difference between &im; and email is that with &im; you can see your friends' presence, that is, whether they are actually on-line at the same time as you. This lets you send messages truly instantly, instead of sending off a mail and having to wait for your friend to check their mailbox. An &im; message pops up on the other person's screen as soon as you send it. Of course, if you'd rather not be interrupted, you can change your own presence so others will know not to disturb you.</para>
+<para>There are lots of other fun and useful &im; features you can explore with &kopete;, like group chats, file transfers and emoticons that reflect your mood. Read on to find out more!</para>
+</sect1>
+</chapter>
+
+<chapter id="getting-started">
+<title>Getting Started</title>
+<para>To use &kopete; you need to set up one or more accounts for the instant messaging services you wish to use.</para>
+<para>You've probably already chosen a messaging service, either because you already use &im;, or you need to use the same service as your friends. If you don't fit into either of these categories, please consider using a messaging service based on open standards, because these are designed for use by Free Software. Other messaging services are prone to changing the underlying technology without making the details freely available, making them harder for Free Software developers to support.</para>
+<para>The messaging services that &kopete; supports that are based on open standards are Jabber and IRC.</para>
+<para>The following section assumes you are registered with an &im; service already. If not, you can register with Gadu-Gadu, Jabber, and <trademark>MSN</trademark> from inside &kopete;; for other services, you'll have to register using their respective web site before creating an account in &kopete;.</para>
+<sect1 id="creating-accounts">
+<title>Creating Accounts</title>
+<para>To create an account, use <menuchoice><guimenu>Settings</guimenu> <guimenuitem>Configure &kopete;...</guimenuitem> </menuchoice> to display the Configure window.</para>
+<para>The Configure window is the main way to set up and customize &kopete;. On the left a column of icons control which aspect of &kopete; is being configured. Click the <guiicon>Accounts</guiicon> icon. The main pane will change to display the account management pane. This is currently empty, but will soon list your &im; accounts. Click <guibutton>New</guibutton> to display the <interface>Account Wizard</interface>.</para>
+<para>The <interface>Account Wizard</interface> helps you create an &im; account. After the <guilabel>Introduction</guilabel> page, you are asked to select the messaging service that you'd like to use. Click one of the services shown and then click <guibutton>Next</guibutton>. On the following page, you should enter your registration details for that instant messaging service.</para>
+<para>Most services just require you to enter a username or unique identifying number (<acronym>UIN</acronym>) and password. The special purpose services Winpopup and <acronym>SMS</acronym> work slightly differently, so please see their specific sections. There are a couple of other options that apply to most services that you should look at:</para>
+<variablelist>
+<varlistentry><term>Remember password</term><listitem><para>When this is checked, &kopete; will store the password for you, so you don't have to enter it every time you connect to the &im; service. If you are security-conscious or want to limit access to the &im; account you can leave this unchecked.</para></listitem></varlistentry>
+<varlistentry><term>Connect at startup</term><listitem><para>When this is checked, &kopete; will try to connect to the &im; service as when it starts. If you use a <acronym>LAN</acronym>, <acronym>DSL</acronym> or other <quote>always-on</quote> connection, this is appropriate; dial-up modem users should turn this off and connect manually when you have dialed up.</para></listitem></varlistentry>
+</variablelist>
+<para>Once you've entered your &im; details, you can proceed to the <guilabel>Finished!</guilabel> page and then dismiss the wizard and the Configure window.</para>
+</sect1><!-- creating-accounts -->
+<sect1 id="go-online">
+<title>Go Online and Start Chatting!</title>
+<para>Now you'll notice that an icon representing the account has appeared in the status bar at the bottom of the &kopete; <interface>Contact List</interface> window. This represents your current presence for this account. <mousebutton>Right</mousebutton> click on it and you can go online from the menu that appears. The status bar icon will animate while &kopete; connects to the &im; service.</para>
+<para>Once you're online, if you've used this &im; service before, your contacts will be fetched from the server and displayed in the <interface>Contact List</interface>. To start a chat with a contact, just click their name and a Chat window will appear. The upper part of the window is where the conversation appears - to say something, type into the bottom part of the window and click <guibutton>Send</guibutton>.</para>
+<para>If you've just created a new account you won't have any contacts. See <link linkend="organise-contacts-adding">Adding Contacts</link> for details on how to add contacts.</para>
+<tip><para>The shortcut for <guibutton>Send</guibutton> is set to <keycombo action="simul">&Ctrl;&Enter;</keycombo> by default; you can change it in the Chat window using <menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure
+Shortcuts...</guimenuitem></menuchoice>.</para></tip>
+</sect1><!-- go-online -->
+</chapter>
+
+<chapter id="using-kopete">
+<title>Using &kopete;</title>
+<para>This chapter gives an overview of &kopete;'s basic features. We will look first at the contact list, where your contacts are displayed, and then at the Chat window, where you carry out a conversation.</para>
+<sect1 id="contact-list">
+<title>The Contact List</title>
+<para>The <interface>Contact List</interface> appears when you start &kopete;. It's the main window where you can set your presence, start a chat, organize your contacts, configure &kopete; and quit.</para>
+<sect2 id="contact-list-layout">
+<title>Layout of the Contact List window</title>
+<sect3 id="contact-list-layout-menu"><title>Menu</title><para>You will usually find the menu bar at the top of the contact list. If it's not there, you might have turned it off; you can re-enable it with <keycombo action="simul">&Ctrl;<keycap>M</keycap></keycombo>. Details on each menu item can be found in the <link linkend="menus">chapter on menu structure</link>.</para>
+</sect3>
+<sect3 id="contact-list-layout-toolbar"><title>Tool bar</title><para>The toolbar holds the most frequently used &kopete; actions. You can customize it with <menuchoice><guimenu>Settings</guimenu> <guimenuitem>Configure Toolbars...</guimenuitem></menuchoice>. Notice the <guibutton>Show Offline Users</guibutton> and <guibutton>Show Empty Groups</guibutton> buttons. With these you can hide contacts and groups that are offline.</para>
+ <para>&kopete; makes it even easier to set a status message to let your contacts know about your mood or why you're busy at the moment. Click on the <guibutton>Set Status Message</guibutton> button and start typing to enter a new message, or choose from one of the previous messages you have used.</para>
+ <para>The <guilabel>Quick Search Toolbar</guilabel> quickly filters the contact list, by typing a few letters from a contact's name.</para>
+</sect3>
+<sect3 id="contact-list-layout-contactlist"><title>Contact List</title><para>The <interface>Contact List</interface> takes up the main part of the window. All your contacts are listed here, in the groups you have chosen for them. You can open or close groups by clicking the plus symbol adjacent to the group. You can reverse the order the groups are sorted in by clicking the <guilabel>Contacts</guilabel> label above the list.</para>
+<para>The context menu in the <interface>Contact List</interface> changes depending on the item under the mouse. <link linkend="organise-contacts-grouping">Groups</link>, <link linkend="organise-contacts-metacontacts">Metacontacts</link> and &im; system specific contacts have their own options. In empty areas of the <interface>Contact List</interface>, the context menu allows you to add contacts or groups, or change the viewing options for the list.</para>
+</sect3>
+<sect3 id="contact-list-layout-statusbar"><title>Status bar</title><para>The status bar shows an icon for each &im; account you have created. The icons represent the current presence of each account, which can be changed by <mousebutton>right</mousebutton>-clicking the account icon. </para>
+ <para>Kopete also shows your current status message in the <guilabel>Status Bar></guilabel>. By clicking on the note icon in the corner, you can change or clear the status message as well.</para>
+</sect3>
+</sect2>
+<sect2 id="contact-list-setting-presence">
+<title>Setting Your Presence</title>
+<para>We introduced you to setting presence <link linkend="go-online">in the previous chapter</link>. 'Presence' determines how visible you are on the &im; network. To use the network at all, you have to connect to the network, so you can send and receive messages and see others' presence. Once you are connected, most &im; systems allow you to indicate what you're doing and whether you want to chat by setting special types of presence such as <emphasis>Away</emphasis> or <emphasis>Free For Chat</emphasis>. The difference presence settings are particular to each away system; but &kopete; allows you some control all your &im; systems at once by setting them to <emphasis>Away</emphasis> or <emphasis>Available</emphasis> at the same time.</para>
+<para>You set your presence for individual &im; accounts by <mousebutton>right</mousebutton> clicking the account's icon in the <interface>status</interface> bar at the bottom of the <interface>Contact List</interface>. The context menu for each account lets you choose the possible presence settings for each &im; system.</para>
+<para>To change all your accounts' presence together, click the <guibutton>Status</guibutton>, or use the <menuchoice><guimenu>File</guimenu> <guimenuitem>Status</guimenuitem></menuchoice> menu.</para>
+</sect2>
+<sect2 id="contact-list-start-chat">
+<title>Start A Chat From The Contact List</title>
+<para>To start a chat from the <interface>Contact List</interface>, simply click a contact. A <link linkend="chat-window">Chat window</link> will appear.</para>
+<para>You can also <mousebutton>right</mousebutton> click a contact and select either <menuchoice><guimenuitem>Send Message</guimenuitem></menuchoice> or <menuchoice><guimenuitem>Start Chat</guimenuitem></menuchoice>. <menuchoice><guimenuitem>Send Message</guimenuitem></menuchoice> works differently in that it just sends a single message without opening the <interface>Chat window</interface>, using a simple dialog. Use it for <quote>fire-and-forget</quote> messages.</para>
+</sect2>
+<sect2 id="contact-list-send-file">
+ <title>Send A File</title>
+ <para>You can send files from the <interface>Contact List</interface>, using the context menu on the person you want to send to. If &kopete; supports file transfer on their &im; system, there will be a <menuchoice><guimenuitem>Send File...</guimenuitem></menuchoice> item. Alternatively, you can drag a file from anywhere else in KDE onto their name to start a file transfer.</para>
+</sect2>
+<sect2 id="organise-contacts">
+<title>Organising Contacts</title>
+<sect3 id="organise-contacts-metacontacts">
+<title>A Word about Metacontacts</title>
+<para>One of the principles behind &kopete; is that it offers a standardized way to use &im; systems. Differences between &im; systems are smoothed over, making it easier to communicate. We follow this principle in the way contacts are organized. When you use &kopete; you just find contacts by name; the actual &im; system used is less important. Some people have more than one &im; account - &kopete; puts the person using the account first.</para>
+<para>To support this, &kopete; introduced <quote>Metacontacts</quote>, which represent the person you want to chat with. One Metacontact contains all the different &im; IDs they may have, making it easy to see with a glance at the Metacontact 'smiley icon' whether someone is available, regardless of which &im; system they are using right now.</para>
+</sect3>
+<sect3 id="organise-contacts-grouping">
+<title>A Word about Grouping Contacts</title>
+<para>&kopete; lets you create groups to sort your contacts. A contact may be in more than one group. Where possible, groupings are saved on server side contact lists, so if you use other &im; programs, group memberships are kept in sync. However, if you change groups in another &im; program, &kopete; cannot know to move a metacontact automatically; it is up to you to resolve this by hand.</para>
+<para>To change the group a metacontact appears in, you can use its context menu to move it or copy it to a new group, or remove it from a group. You can also use drag and drop here - just drop the metacontact on a different group name.</para>
+</sect3><sect3 id="organise-contacts-adding">
+<title>Adding Contacts</title>
+<para>To add a contact, either select <menuchoice><guimenu>File</guimenu><guimenuitem>Add Contact...</guimenuitem></menuchoice> or click the <guibutton>Add Contact</guibutton> button on the toolbar. This brings up the Add Contact Wizard.</para>
+<para>The Add Contact Wizard creates a new Metacontact using one or more &im; systems, by leading you through the following pages.</para>
+
+<orderedlist>
+<listitem><para>Welcome Page. Here you can choose whether you want to use the &kde; Address Book for this contact. Storing &im; information in the &kde; Address Book will enable other &kde; &im; programs to share contact information with &kopete; and in future &kde; applications may use &kopete; to send information via &im;. If you prefer to keep your &im; contacts separated, clear the checkbox here.</para></listitem>
+<listitem><para>Choose &kde; Address Book entry. By choosing an entry from your &kde; Address Book, you can use its name as a Display Name in &kopete;. You can also create a new entry here. This page doesn't show if you chose not to use the &kde; Address Book.</para></listitem>
+<listitem><para>Select Display Name and Group. Here you can enter a Display Name (the name used for this person inside &kopete;), and choose the <link linkend="organise-contacts-grouping">groups</link> they are a member of.</para></listitem>
+<listitem><para>Select &im; Accounts. Here you can choose which accounts you want to use to chat to the new contact. If you only have one &im; account, you won't see this screen.</para></listitem>
+<listitem><para>Account-specific Add Contact Pages. For each account, you'll get one page where you can enter the <acronym>UIN</acronym>, buddy name or E-mail address, depending on the &im; system in use.</para></listitem>
+<listitem><para>Finish Screen. All done. Except if the &im; system requires authorisation (such as <trademark>ICQ</trademark>) to add a contact to your list - in which case, you'll be prompted after the wizard exits.</para></listitem>
+</orderedlist>
+
+<para>You can add contacts to an existing Metacontact using its context menu.</para>
+</sect3>
+
+<sect3 id="organise-contacts-renaming">
+<title>Renaming Contacts</title>
+<para>You can rename a contact using <menuchoice><guimenu>Edit</guimenu><guimenuitem>Rename Contact</guimenuitem></menuchoice> or with the same item on the metacontact context menu.</para>
+<para>Some &im; systems allow you to set a <quote>Display Name</quote> that is different to your username, such as <quote>Alice loves crypto!</quote>. If you change a contact's name manually, this will override their Display Name. To get it back, open the Properties dialog for that contact and check the <guilabel>Use the name given by the server</guilabel> checkbox.</para>
+</sect3>
+<sect3 id="organise-contacts-removing">
+<title>Removing Contacts</title>
+<para>If you no longer want a contact to be in the contact list, you can remove a Metacontact and all the contacts under it with <menuchoice><guimenu>Metacontact context menu</guimenu><guimenuitem>Remove Contact</guimenuitem></menuchoice>.</para>
+</sect3>
+<sect3 id="organise-contacts-moving-contact">
+<title>Moving Contacts between Metacontacts</title>
+<para>You can change the metacontact a contact belongs to. In practice, you only have to do this when you have just added multiple accounts to &kopete;, and you know that HotDog76 and mikejones@hotmail.com are both the same person.</para>
+<para>There are two ways to do this:</para>
+<variablelist>
+<varlistentry><term>Drag and Drop</term><listitem><para>The contact icon to the right of the metacontact name may be dragged from one metacontact to another.</para></listitem></varlistentry>
+<varlistentry><term>Contact Context Menu</term><listitem><para>The context menu for contacts (<mousebutton>Right</mousebutton>-click the contact icon) allows you to choose the new metacontact from a dialog.</para></listitem></varlistentry>
+</variablelist>
+<para>If the move would leave a Metacontact empty (with no contacts), you'll be asked if you want to delete this contact.</para>
+</sect3>
+<sect3 id="organise-contacts-removing-contacts-">
+<title>Removing Contacts from Metacontacts</title>
+<para>To remove a contact from a Metacontact, choose <menuchoice><guimenu>Contact context menu</guimenu><guimenuitem>Delete Contact...</guimenuitem></menuchoice>.</para>
+</sect3>
+</sect2>
+<sect2 id="contact-list-configure">
+<title>Configure &kopete;</title>
+<para>You can configure &kopete; using <menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure &kopete;...</guimenuitem></menuchoice>. See the <link linkend="configuring">next chapter</link> for details.</para>
+</sect2>
+<sect2 id="contact-list-quit">
+<title>Exiting &kopete;</title>
+<para>To exit &kopete; you should use <menuchoice><guimenu>File</guimenu><guimenuitem>Quit</guimenuitem></menuchoice>, <keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo>, or the &kopete; System Tray icon's context menu. If you just close the <interface>Contact List</interface> window, &kopete; will continue to run in the &kde; System Tray.</para>
+</sect2>
+<sect2 id="contact-list-shortcuts">
+<title>Keyboard shortcuts</title>
+<para>The following keyboard shortcuts are supported in the <interface>Contact List</interface> window:</para>
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Keyboard Shortcut</entry>
+<entry>Action</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><keycap>Up Arrow</keycap></entry>
+<entry>Select the previous item in the contact list.</entry>
+</row>
+<row>
+<entry><keycap>Down Arrow</keycap></entry>
+<entry>Select the next item in the contact list.</entry>
+</row>
+<row>
+<entry><keycap>Left Arrow</keycap></entry>
+<entry>Close the current group.</entry>
+</row>
+<row>
+<entry><keycap>Right Arrow</keycap></entry>
+<entry>Open the current group.</entry>
+</row>
+<row>
+<entry><keycap>Enter</keycap></entry>
+<entry>Start a chat with the selected contact.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>M</keycap></keycombo></entry>
+<entry>Show/Hide the menu bar.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>U</keycap></keycombo></entry>
+<entry>Show/Hide offline users.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>G</keycap></keycombo></entry>
+<entry>Show/Hide empty groups.</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+</sect2>
+</sect1><!-- contact-list -->
+
+<sect1 id="chat-window">
+<title>The Chat Window</title>
+<sect2 id="chat-window-layout"><title>Layout of the Chat Window</title>
+<sect3 id="chat-window-layout-chatview"><title>The Chat View</title><para>The Chat View usually takes up most of the Chat window and is where the conversation between you and your contacts takes place. Messages appear in the order they are received, with the earliest messages at the top of the view.</para>
+<para>You can <link linkend="configuring-appearance">control</link> the appearance of the <interface>Chat View</interface>, making it look like other &im; clients or create a completely individual look.</para>
+</sect3>
+<sect3 id="chat-window-layout-memberslist"><title>Chat Members List</title>
+<para>Since some &im; systems allow you to chat as a group, it's useful to see who's chatting at the moment. The Chat Members List appears to the left or the right of the Chat View. You can change this using <menuchoice><guimenu>Settings</guimenu><guisubmenu>Chat Members List</guisubmenu></menuchoice>. The contact context menu is available in the Chat Members List.</para></sect3>
+<sect3 id="chat-window-layout-input-area"><title>Input Area</title><para>The Input Area is below the Chat View. This is where you type messages before sending them. You can change the font and color of the message using the usual tools on the toolbar. If the &im; system supports this, your messages will appear in color when your contacts read them.</para><para>By default, the keyboard shortcut to send messages is <keycap>&Enter;</keycap>.</para></sect3>
+<sect3 id="chat-window-layout-statusbar"><title>Status Bar</title><para>The Status Bar contains temporary messages, such as notification that someone else is typing, as well as the <guibutton>Send</guibutton> button.</para></sect3>
+<sect3 id="chat-window-layout-tabbing"><title>Tabbing</title><para>&kopete; lets you carry on multiple conversations in one window, by putting each one in its own tab within the window. The tab titles change color to show when a new message has been received:</para>
+<variablelist>
+<varlistentry><term>Red</term><listitem><para>Someone typed a message.</para></listitem></varlistentry>
+<varlistentry><term>Green</term><listitem><para>Someone is typing a message.</para></listitem></varlistentry>
+<varlistentry><term>Blue</term><listitem><para>Someone typed a message containing your nickname.</para></listitem></varlistentry>
+</variablelist>
+<para>There are several different ways to control grouping. To configure this behavior, go to the Chat tab of the Behavior page of the Configure &kopete; dialog. You can also move chats between windows using the <menuchoice><guimenu>Tabs</guimenu></menuchoice> menu, and control the placement of the tabs in the window.</para></sect3>
+</sect2>
+<sect2 id="chat-window-group">
+<title>Group Chats in &kopete;</title>
+<para>You can use &kopete; to chat one to one, or in a group, where the &im; system supports this.</para>
+<para>To invite others into a chat, drag them from the Contact List into the chat window, or use <menuchoice><guimenu>Chat</guimenu><guisubmenu>Invite</guisubmenu><guimenuitem>&lt;contact name&gt;</guimenuitem></menuchoice>.</para>
+</sect2>
+<sect2 id="chat-window-file-xfer">
+<title>File Transfers</title>
+<para>Some &im; systems allow you to send and receive files. You can access this function from the contact's context menu. If you're already chatting, and want to send a file, simply drag the file from any other part of &kde; into the Chat Window, or select the <menuchoice><guimenu>Chat</guimenu><guimenuitem>Send File</guimenuitem></menuchoice> menu.</para>
+</sect2>
+<sect2 id="chat-window-shortcuts">
+<title>Keyboard Shortcuts</title>
+<para>The following keyboard shortcuts are supported in the Chat window:</para>
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Keyboard Shortcut</entry>
+<entry>Action</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><keycap>&Enter;</keycap></entry>
+<entry>Send the message in the <interface>Input Area</interface>.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>P</keycap></keycombo></entry>
+<entry>Print the contents of the Chat View.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo></entry>
+<entry>Save the contents of the Chat View.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>W</keycap></keycombo></entry>
+<entry>Close the current Chat View. The Chat window will close unless there is more than one tab in the window.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Alt;<keycap>Left Arrow</keycap></keycombo></entry>
+<entry>Change to the previous tab.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Alt;<keycap>Right Arrow</keycap></keycombo></entry>
+<entry>Change to the next tab.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;&Shift;<keycap>B</keycap></keycombo></entry>
+<entry>Detach a tabbed chat into a separate window.</entry>
+</row>
+<row>
+<entry><keycap>Tab</keycap></entry>
+<entry>Complete a partially typed nickname belonging to someone you're chatting with.</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+</sect2>
+
+</sect1><!-- chat-window -->
+</chapter><!-- Using-kopete -->
+
+<chapter id="configuring">
+<title>Configuring &kopete;</title>
+<para>To configure &kopete;, look in the <menuchoice><guimenu>Settings</guimenu></menuchoice> menu. </para>
+<sect1 id="configuring-global-shortcuts">
+<title>Global Shortcuts</title>
+<para>&kopete; defines some shortcuts which are valid in any &kde; application.</para>
+<informaltable>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Global Keyboard Shortcuts</entry>
+<entry>Action</entry>
+</row>
+</thead>
+<tbody>
+<row>
+<entry><keycombo action="simul">&Ctrl;&Shift;<keycap>I</keycap></keycombo></entry>
+<entry>Read Message. This is useful if you have hidden the <interface>Contact List</interface> window and the system tray icon is animating to tell you you have a new message.</entry>
+</row>
+<row>
+<entry><keycombo action="simul">&Ctrl;&Shift;<keycap>C</keycap></keycombo></entry>
+<entry>Show/hide (Dock) the <interface>Contact List</interface> window. <emphasis>Warning:</emphasis> If you have disabled &kopete;'s System Tray icon or don't have a system tray, this can make the <interface>Contact List</interface> vanish - the only way to restore it is to repeat this shortcut.</entry>
+</row>
+</tbody>
+</tgroup>
+</informaltable>
+</sect1>
+
+<sect1 id="configure-dialog">
+<title>The Configure &kopete; Dialog</title>
+<sect2 id="configuring-accounts">
+<title>Adding and Editing Accounts</title>
+<para>We briefly showed you how to add an account in <link linkend="getting-started">Getting Started</link>. To change an account's settings later, open up the <guilabel>Configure &kopete;</guilabel> dialog, with <menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure &kopete;</guimenuitem></menuchoice>. Much like the &kde; Control Center, the configuration is separated into sections. The icons on the left side of the dialog switch between sections.</para>
+<para>On the Accounts page, you can <link linkend="creating-accounts"><guibutton>Add</guibutton></link>, <guibutton>Remove</guibutton>, or <guibutton>Modify</guibutton> accounts. Editing accounts is much the same as adding them, but note that you can't change the UIN, buddy name, or whatever account identifier your &im; system uses. This is intrinsic to the account. If you want to change this, you will have to add another account with the new account identifier and (optionally) remove the old account.</para>
+<para>You can quickly distinguish between multiple accounts using the same &im; system by giving a custom color to each account's status bar entry and contact icons. To do so, select the account and click the color selector on the right side of the page. </para>
+<para>You can control the priority of accounts using the <guiicon>Up</guiicon> and <guiicon>Down</guiicon> icons on this page. If you have more than one way to message a contact, this determines the &im; system &kopete; will use to communicate them.</para>
+</sect2>
+<sect2 id="global-identity">
+<title>Global Identity</title>
+<para>&kopete;'s Global Identity lets you set your own nickname and photo once for all your &im; accounts. You can read these details from the KDE address book entry for yourself, from a single one of your contacts, or add a completely new nickname or photo. If you have an exciting dual life, you can create multiple identites and switch between them in the<guilabel>Identity</guilabel> section.</para>
+</sect2>
+<sect2 id="configuring-behavior">
+<title>Behavior</title>
+<para>Behavior covers the way &kopete; integrates with your desktop, <emphasis>Away</emphasis> settings, and chat user interface options.</para>
+<sect3 id="configuring-behavior-general">
+<title>The General tab</title>
+<para>Here you can customize &kopete;'s desktop integration, and control the way the contact list is laid out.</para>
+<variablelist>
+<varlistentry>
+<term><guilabel>Show system tray icon</guilabel></term>
+<listitem>
+<para>By default, &kopete; places an icon in the &kde; System Tray. If you prefer, you can turn this off here.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Start with hidden main window</guilabel></term>
+<listitem>
+<para>This causes &kopete; to start with the <interface>Contact List</interface> window hidden (docked). You can make it visible by clicking the system tray icon or with the <link linkend="configuring-global-shortcuts">Show Contact List global shortcut</link>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Open messages instantly</guilabel></term>
+<listitem>
+<para>New messages open chat windows as soon as they arrive.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Use message queue</guilabel></term>
+<listitem>
+<para>The message queue is where &kopete; puts messages when there is no chat window open. This allows you to be notified of new messages with popup <quote>speech bubbles</quote>; or by animating the System Tray icon. If you choose to disable the message queue, chat windows will open as soon as you receive a new message.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Use message stack</guilabel></term>
+<listitem>
+<para>If you use a message stack, &kopete; shows recently received messages starting with the last message received.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+<sect4 id="configuring-behavior-general-notifications">
+<title>Notifications</title>
+<variablelist>
+<varlistentry>
+<term><guilabel>Show a bubble on new message</guilabel></term>
+<listitem>
+<para>This option shows a <quote>speech bubble</quote>; coming from the System Tray icon when you receive a new message. You can start a chat or ignore the message. <acronym>URL</acronym>s are extracted from the message; if you click a link, your preferred browser will open the link and the message will be dismissed.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Flash the system tray on new messages</guilabel></term>
+<listitem>
+<para>This option causes the System Tray icon to animate when you receive a new message. Clicking the icon will show the message in a chat window.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Enable events while away</guilabel></term>
+<listitem>
+<para>If you do not wish to be distracted by these notifications while you are set <emphasis>Away</emphasis>, uncheck this box.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Configure Sounds &amp; Events</guilabel></term>
+<listitem>
+<para>Sounds, flashing taskbar entries, passive popups and more exotic notifications are supported in &kopete; using the &kde; notification system. Type <userinput>help:/kcontrol/kcmnotify</userinput> in &konqueror; or select the Help tab in the <guilabel>System Notifications</guilabel> section of the &kcontrol; for more information.</para>
+<para>To add custom notifications for a contact, right click that contact in the Contact List and select Properties. This lets you start chats, play a custom sound effect, or display a message for that contact or group. Otherwise you can use the <quote>Execute a program</quote> notification to perform custom notifications. As an example, if you have <application>XOSD</application> (X On-Screen Display) installed, you can get OSD online notifications by executing the following command when the <quote>User goes online</quote> event takes place:</para>
+<para><userinput>echo %s | osd_cat -o 100 -p bottom -A center -f -*-helvetica-*-r-*-*-24-*-*-*-*-*-*-* -O 2 -c gold</userinput></para>
+<para>OhReally at the KDE Forum suggests having your online notifications read out by a speech synthesiser, using <ulink url="http://mambo.ucsc.edu/psl/mbrola">MBROLA</ulink> like so:</para>
+<para><userinput>echo %s | sed -e 's/online/onlaain/i' | /usr/local/bin/mbrdico.dutch.female</userinput></para>
+<para>The 'sed' in the middle phoneticises &kopete;'s output to so the synthesizer has a better Dutch pronunciation.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect4>
+</sect3>
+<sect3 id="configuring-behavior-away">
+<title>Away Settings</title>
+<variablelist>
+<varlistentry>
+<term><guilabel>Notify all open chats when I go away</guilabel></term>
+<listitem><para>Be careful if you enable this item; it will cause a message to be sent to open chats when you become away, which some people may find intrusive.</para></listitem>
+</varlistentry>
+</variablelist>
+<sect4 id="configuring-behavior-away-auto">
+<title>Auto Away</title>
+<para>Here you can choose to have &kopete; set you away after a period of inactivity.</para>
+</sect4>
+<sect4 id="configuring-behavior-away-messages">
+<title>Predefined Away Messages</title>
+<para>You can define as many custom away messages as you like here, and choose from them when you go <emphasis>Away</emphasis> using the <guibutton>Status</guibutton> button on the <link linkend="contact-list-layout-toolbar">main toolbar</link>.</para>
+</sect4>
+</sect3>
+<sect3 id="configuring-behavior-chat">
+<title>Chat Settings</title>
+<variablelist>
+<varlistentry>
+<term><guilabel>Raise window/tab on new messages</guilabel></term>
+<listitem><para>This causes a chat window to pop up when it receives a new message.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Show events in chat window</guilabel></term>
+<listitem><para>Some &im; systems can give extra information, like people joining or leaving chats. This option lets you receive these messages in your chat window.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Highlight messages containing your nickname</guilabel></term>
+<listitem><para>This simply emphasizes messages in a chat that contain your nickname. For more powerful control over highlighting and other events, see the <link linkend="plugins-highlight">Highlight plugin</link>.</para></listitem>
+</varlistentry>
+</variablelist>
+<sect4 id="configuring-behavior-chat-interface">
+<title>Interface Preference</title>
+<para>&kopete; can send messages using either a <quote>fire and forget</quote> interface that does not wait for a reply, or a chat window where the conversation is visible as it unfolds. Here you can choose which style to use by default.</para>
+</sect4>
+<sect4 id="configuring-behavior-chat-grouping">
+<title>Chat Window Grouping Policy</title>
+<para>If you wish to group chats within tabs in a single window, &kopete; lets you choose several ways to determine the grouping.</para>
+<itemizedlist>
+<listitem><para>Open all messages in a new chat window</para></listitem>
+<listitem><para>Group all messages from the same account in the same chat window</para></listitem>
+<listitem><para>Group all messages in the same chat window</para></listitem>
+<listitem><para>Group all messages in the same group in the same chat window</para></listitem>
+<listitem><para>Group all messages from the same metacontact in the same chat window</para></listitem>
+</itemizedlist>
+</sect4>
+<sect4 id="configuring-behavior-chat-lines">
+<title>Chat Window Line Limit</title>
+<variablelist>
+<varlistentry>
+<term><guilabel>Maximum number of chat window lines</guilabel></term>
+<listitem><para>This limits the number of lines of text the chat window can display.</para></listitem>
+</varlistentry>
+</variablelist>
+</sect4>
+</sect3>
+</sect2> <!-- configuring-behavior -->
+<sect2 id="configuring-appearance">
+<title>Appearance</title>
+<para>Appearance governs the style of the Chat window, its colors and fonts, and lets you choose your preferred emoticons.</para>
+<sect3 id="configuring-appearance-emoticons">
+<title>Emoticons</title>
+<para>Emoticons (also known as smileys) are combinations of characters such as <userinput>;-)</userinput>that look like a face, and communicate moods or expressions. &kopete; can optionally use graphical emoticons in place of the characters themselves.</para>
+<para>On this tab, you can select which emoticon set you prefer, or turn off graphical emoticons altogether.</para>
+<para>See <link linkend="specialised">Specialized Actions</link> for details of how to install extra emoticon sets.</para>
+</sect3>
+<sect3 id="configuring-appearance-chat">
+<title>Chat Window</title>
+<sect4 id="configuring-appearance-chat-styles">
+<title>Styles</title>
+<para>The style of the chat view can be altered to look like other clients. Installed styles are shown in the list on the left and are previewed in the main panel. See <link linkend="chatwindowstyleguide">Chat Window Style guide</link> for a document how to make your own style.</para>
+<para>Third party styles are available at <ulink url="http://kde-look.org">http://kde-look.org</ulink>. &kopete; 0.12 now supports styles from Adium(an &im; program on Mac OS X). So you can download styles from Adium here: <ulink url="http://www.adiumxtras.com/">Adium Xtras</ulink> and select Message View Styles.</para>
+<para>To install a style, click<guibutton>Instal...</guibutton>. Select a archive file containing the style. To delete a style, select a style in the list and click <guibutton>Delete</guibutton>.</para>
+<para><guilabel>Group consecutive messages</guilabel> is a useful option to make your chats more readable. If you receive several messages in a row from teh same contact, they are grouped without repeating the sender name.</para>
+</sect4>
+</sect3>
+<sect3 id="configuring-behavior-general-contactlist">
+<title>Contact List</title>
+<variablelist>
+<varlistentry>
+ <term><guilabel>Arrange metacontacts by group</guilabel></term>
+<listitem>
+<para>By disabling this, &kopete;'s groups are hidden, and contacts are divided only into <guilabel>Online Contacts</guilabel> and <guilabel>Offline Contacts</guilabel>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Show tree branch lines</guilabel></term>
+<listitem>
+<para>Usually &kopete; displays contacts and groups as a tree, where group members are indented. For a simpler appearance, you can disable this, so the contact list becomes a flat list. You can also control whether branches are indented here.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Contact Display Mode</guilabel></term>
+<listitem>
+ <para>There are several ways you can present the contact list here. Of particular interest may be the <guilabel>Use contact photos when available</guilabel> option, that shows the contact list using photos chosen by your contacts or the KDE Address Book</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Contact List Animations</guilabel></term>
+<listitem>
+ <para>This controls the degree of animation of the contact list. Turning this off will make &kopete; more responsive on slower machines.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Contact List Auto-Hide</guilabel></term>
+<listitem>
+ <para>By enabling this, the contact list will automatically disappear a few seconds after the pointer leaves the window.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Change Tooltip Contents...</guilabel></term>
+<listitem>
+ <para>You have a lot of control over how much or how little detail appears inthe tooltips shown on the contact list using this dialog.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect3>
+<sect3 id="configuring-appearance-colors-fonts">
+<title>Colors and Fonts</title>
+<sect4 id="configuring-appearance-colors-fonts-chat">
+<title>Chat Window Colors</title>
+<para>Here you can alter the base font and text colors used for chatting.</para>
+</sect4>
+<sect4 id="configuring-appearance-colors-fonts-overrides">
+<title>Formatting Overrides</title>
+<para>If your contacts tend to choose fonts and colors that you dislike, you can tell &kopete; to ignore these and use your regular font.</para>
+</sect4>
+<sect4 id="configuring-appearance-colors-fonts-contactlist">
+<title>Contact List</title>
+<para>Some &im; systems let you see whether contacts are idle at their computers. This option enables you to change the color used for idle contacts.</para>
+</sect4>
+</sect3>
+</sect2> <!-- configuring-appearance -->
+<sect2 id="configuring-devices">
+<title>Devices</title>
+<para>The Devices section allows you to choose and configure which multimedia devices are used for A/V chatting. Whether this works for you is highly dependent on the hardware you have and how well it is supported by your operating system.</para>
+<sect3 id="configuring-devices-video">
+<title>Video</title>
+<para>&kopete; uses the Video4Linux 2 system for video. This shows a blue square if no video device is found, or a preview if the camera is working. For up-to-date information on &kopete; webcam support, see the <ulink url="http://wiki.kde.org/tiki-index.php?page=Kopete%20Webcam%20Support">Kopete Webcam Support wiki page</ulink>.</para>
+</sect3>
+<sect3 id="configuring-devices-audio">
+<title>Audio</title>
+ <para>Audio support in &kopete; is at an experimental stage. If you have an Audio tab, you are probably using a preview build of &kopete;.</para>
+</sect3>
+</sect2>
+</sect1>
+
+<sect1 id="configuring-load-plugins-dialog">
+<title>Loading Plugins</title>
+<para>You can customize &kopete; with special functions that may be useful or just a bit of fun. Bring up the <guilabel>Configure Plugins</guilabel> dialog with <menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure Plugins...</guimenuitem></menuchoice>.</para>
+<para>Plugins can be turned on or off in the list on the left side of the page. Each plugin may be configured on the right side. See the <link linkend="plugins">chapter on plugins</link> for details on each plugin.</para>
+</sect1>
+</chapter> <!-- configuring -->
+
+<chapter id="protocols">
+ <title>&kopete;'s Protocols</title>
+ <para>&kopete; calls different &im; systems 'Protocols'. When you add an account, it is specific to a single protocol. Although &kopete; tries to make instant messaging appear the same, no matter what protocol you use, there are some differences in the level of support for advanced features such as file transfer and multimedia.</para>
+ <sect1 id="protocols-list">
+ <title>Protocols</title>
+ <sect2 id="protocols-aim">
+ <title>AIM</title>
+ <para>AIM supports chatrooms. Use the <guilabel>Join Chat...</guilabel> command on the AIM account menu to join a chatroom. Contact pictures and custom emoticons are also supported.</para>
+ </sect2>
+ <sect2 id="protocols-icq">
+ <title>ICQ</title>
+ <para>ICQ has an Invisibility feature which allows you to hide from selected contacts. You may also search the ICQ user directory when adding a contact. A wide range of contact details can be set using the <guilabel>Properties</guilabel> option.</para>
+ </sect2>
+ <sect2 id="protocols-msn">
+ <title>MSN</title>
+ <para>MSN supports the sending and receiving of webcams, if your camera is supported by the Video4Linux 2 (v4l2) standard. To view someone's webcam, right click on their MSN buttefly icon and select <menuchoice><guimenuitem>View Contact's Webcam</guimenuitem></menuchoice>. File transfer and multi user chats work. To transfer a file, drag the file from Konqueror or the desktop into the chat window. To invite someone else into a chat, drag them from the Contact List into the chat window. The <menuchoice><guimenu>File</guimenu></menuchoice> menu also contains these commands. In addition, MSN supports custom emoticons.</para>
+ <para>To use file transfer or a webcam, make sure port 6891 is forwarded to your computer.</para>
+ </sect2>
+ <sect2 id="protocols-yahoo">
+ <title>Yahoo</title>
+ <para>Yahoo can send and receive webcam video. It also supports Yahoo mail and the Yahoo addressbook from the account menu. Conferencing is also possible.</para>
+ </sect2>
+ <sect2 id="protocols-jabber">
+ <title>Jabber</title>
+ <para>Jabber, also known as XMPP, supports file transfer, conferencing and any other services supplied by the Jabber server. For example, many Jabber servers have a user directory, and some provide transports to other messaging systems. To access services, use <menuchoice><guimenu>Services...</guimenu></menuchoice> on the account menu. Jabber file transfer can work without port forwarding, but enjoys better performance where a direct connection is possible. By default, port 8010 is used for port forwarding, but this is configurable in each account's settings.</para>
+ </sect2>
+ <sect2 id="protocols-gtalk">
+ <title>Google Talk</title>
+ <para>Since Google Talk is based upon Jabber, it is well supported in &kopete; with the exception of voice chat, which is worked upon.</para>
+ <para>To configure &kopete; for Google Talk: Use your complete Google Mail address as the user name. Check <guilabel>Use protocol encryption (SSL)</guilabel>, <guilabel>Allow plain-text password authentication</guilabel> and <guilabel>Override default server information</guilabel>. The server is <quote>talk.google.com</quote> or <quote>gmail.com</quote> and ports 443 or 5223 should be used.</para>
+ </sect2>
+ <sect2 id="protocols-groupwise">
+ <title>Novell GroupWise</title>
+ <para>GroupWise Messenger is an enterprise messenging system from Novell Inc. The full range of features are supported, including privacy, group chat, rich text and user search.</para>
+ </sect2>
+ <sect2 id="protocols-gadu-gadu">
+ <title>Gadu-Gadu</title>
+ <para>Gadu-Gadu is a chat system originating from Poland. At present, &kopete; supports basic chat functions.</para>
+ </sect2>
+ <sect2 id="protocols-winpopup">
+ <title>WinPopup</title>
+ <para>WinPopup is a way to use &kopete; to send and receive messages with Windows computers on the local network. The WinPopup protocol only supports single, plain-text messages.</para>
+ </sect2>
+ <sect2 id="protocols-others">
+ <title>Other protocols</title>
+ <para>As well as the protocols named above, &kopete; has support for several other protocols. In most cases, this is not enabled by default or an additional plugin must be installed. Meanwhile, SMS, Skype and SILC are provided in this way. See &kopetewww; for details, however, the &kopete; team are not responsible for these protocols.</para>
+ </sect2>
+ </sect1>
+</chapter>
+
+<chapter id="plugins">
+<title>&kopete;'s Plugins</title>
+<para>&kopete; offers plugins that provide functions that aren't essential for messaging, but are useful for some people.</para>
+<sect1 id="plugins-list">
+<title>Plugins</title>
+<sect2 id="plugins-alias">
+<title>Alias</title>
+<para><guilabel>Alias</guilabel> lets you define your own commands, eg /hello, in &kopete; that run scripts and output the result in the chat window. If you know how the alias Unix command works, this is just the same</para>
+</sect2>
+<sect2 id="plugins-auto-replace">
+<title>Auto Replace</title>
+<para><guilabel>Auto Replace</guilabel> allows you to correct frequently misspelled words or save typing certain words using abbreviations.</para>
+</sect2>
+<sect2 id="plugins-bookmarks">
+<title>Bookmarks</title>
+<para>The <guilabel>Bookmarks</guilabel> plugin creates bookmarks in your KDE bookmarks list from URLs that are received in &im; messages.</para>
+</sect2>
+<sect2 id="plugins-connection-status">
+<title>Connection Status</title>
+<para><guilabel>Connection Status</guilabel> is useful for modem users or others who don't have a permanent Internet connection. It watches for an active Internet connection and sets your accounts online when it detects you have dialed up.</para>
+</sect2>
+<sect2 id="plugins-contact-notes">
+<title>Contact Notes</title>
+<para><guilabel>Contact Notes</guilabel> allows you to note down any useful bits of information on a metacontact.</para>
+</sect2>
+<sect2 id="plugins-cryptography">
+<title>Cryptography</title>
+<para><guilabel>Cryptography</guilabel> lets you use <application>GnuPG</application> to encrypt conversations. Note that this is not the same as an SSL secured chat session. SSL protects the message from alteration and snooping of the message contents while it is in transit, but it doesn't guarantee the person reading the message is the intended recipient. Cryptography encrypts the message to a single individual - only the holder of the matching key can read it. However, it doesn't guarantee that the message the recipient reads is the message you sent! Since anyone who has the recipient's public key can encrypt to him/her, it is possible that your message could be replaced en route by a completely different message, and the recipient would not know.</para>
+<para>To configure Cryptography, select your <application>GnuPG</application> key in the configuration page. If you select <guilabel>Encrypt outgoing messages with this key</guilabel>, then messages will be encrypted to you as well as the recipient, which is useful if you want to read your own chat logs later. Then, using <menuchoice><guimenuitem>Select Cryptography Public Key</guimenuitem></menuchoice> from each contact's context menu, choose their public key. You will be prompted for your passphrase when using this plugin.</para>
+</sect2>
+<sect2 id="plugins-highlight">
+<title>Highlight</title>
+<para><guilabel>Highlight</guilabel> works a little like e-mail filters, in that it allows you to make things happen in response to particular messages. As well as highlighting the text, you can play sounds.</para>
+</sect2>
+<sect2 id="plugins-history">
+<title>History</title>
+<para>The <guilabel>History</guilabel> plugin, when activated, records conversations using any &im; system and allows you to view old conversations later. A <menuchoice><guimenuitem>History</guimenuitem></menuchoice> item appears in each Metacontact's context menu so you can view the message history for that metacontact.</para>
+<para>The following item is added to the Contact List's menus:</para>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>View History</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) This displays the History browser for the selected contact.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+<para>The following items are added to the Chat window's menus:</para>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>Left Arrow</keycap></keycombo>
+</shortcut>
+<guimenu>Tools</guimenu>
+<guimenuitem>History Previous</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This enables you to view the next oldest set of messages from the History in the Chat window.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Alt;&Shift;<keycap>Right Arrow</keycap></keycombo>
+</shortcut>
+<guimenu>Tools</guimenu>
+<guimenuitem>History Next</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This shows the next newest set of messages from the History in the Chat window.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Tools</guimenu>
+<guimenuitem>History Last</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This shows the most recent set of messages from the History in the Chat window.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect2>
+
+<sect2 id="plugins-kopetex">
+<title>KopeteTeX</title>
+<para>KopeteTeX allows scientists and mathematicians to hold conversations using the LaTeX markup language. Expressions entered within $$ are rendered as a graphic in the chatwindow, and can be cut and pasted as the original Latex. To use this plugin you must have LaTeX installed</para>
+</sect2>
+<sect2 id="plugins-motion-auto-away">
+<title>Motion Auto-Away</title>
+<para>In conjunction with a webcam and the <application>Video4Linux</application> package, this lets you detect when you're no longer at your computer and have &kopete; automatically become <emphasis>Away</emphasis>.</para>
+</sect2>
+<sect2 id="plugins-now-listening">
+<title>Now Listening</title>
+<para>With the <guilabel>Now Listening plugin</guilabel>, let people you're chatting with know what you're listening to, by typing <userinput>/media</userinput> in a chat, or with <menuchoice><guimenu>Tools</guimenu><guimenuitem>Send Media Info</guimenuitem></menuchoice> in the Chat window.</para>
+</sect2>
+<sect2 id="plugins-statistics">
+<title>Statistics</title>
+<para>This plugin uses a database to gather information about your contacts' activity patterns. You can use this to see when a contact is usually online, for example.</para>
+</sect2>
+<sect2 id="plugins-text-effect">
+<title>Text Effect</title>
+<para>Text Effect applies funny effects to your messages before sending them, like coloring them or changing the case of the words. Just don't forget you have activated it - we've had bug reports from forgetful <guilabel>Text Effect</guilabel> users!</para>
+</sect2>
+<sect2 id="plugins-translator">
+<title>Translator</title>
+<para>The Translator plugin lets you define a preferred language for each Metacontact, and then translates messages to or from them using web based translation services such as <trademark>Google</trademark> and <trademark>Babelfish</trademark>. Set your own preferred language in the Configure Plugins dialog. Each contact's preferred language can be set on its context menu.</para>
+<para>The following item is added to the Chat window's menus:</para>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>T</keycap></keycombo>
+</shortcut>
+<guimenu>Tools</guimenu>
+<guimenuitem>Translate</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>If you did not turn on automatic translation, this translates the current chat.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect2>
+<sect2 id="plugins-web-presence">
+<title>Web Presence</title>
+<para>Web Presence allows you to publicize your &im; presence on the Web. Give it the path to a file on an FTP server (for example), and it will upload a short piece of HTML to that file, which you can include in your homepage. &kde;'s network transparency makes this simple. Useful for bloggers to make friends with, or you could use it to use &im; in your business.</para>
+<para>Example: <userinput>sftp://username@somehost.org/path/to/homes/user/im.html</userinput> uses the <acronym>SFTP</acronym> protocol to upload your presence directly onto the webserver.</para>
+<para>See <ulink url="help:/kioslave/index.html">the KIO manuals</ulink> for tips on specific network protocols.</para>
+</sect2>
+</sect1>
+<sect1 id="plugins-contributing">
+<title>Contributing a plugin</title>
+<para>&kopete; is designed to make it easy to create plugins that give it extra functions. So if you've got a great idea to make &kopete; even better, <link linkend="intro-to-kopete-web">get in touch</link>!</para>
+</sect1>
+</chapter><!-- plugins -->
+
+&kopete-menus;
+
+<chapter id="faq">
+<title>Frequently Asked Questions</title>
+<qandaset>
+<qandaentry>
+<question><para>What does &kopete; mean? How do I pronounce it?</para></question>
+<answer><para>&kopete;'s name comes from the chilean word Copete, meaning <quote>a drink with your friends</quote>. Duncan, the original author, recorded an <ulink type="audio/vorbis" url="http://kopete.kde.org/files/kopete_en.ogg">audio sample</ulink>.</para></answer>
+</qandaentry>
+<qandaentry>
+<question><para>When I have more than one messaging service under a user's name in my contact list and I click on that user's name, it will message them on the wrong messaging service.</para>
+</question>
+<answer><para>You can change the order of accounts &kopete; tries to message people with by using the Up and Down arrows in the bottom right corner of the account configuration screen. &kopete; will try to connect with accounts starting from the top. However, if one service has a higher status value than the others for that user, &kopete; will use that one. For example, if a person has three services and two are marked as away and the third is marked as online, &kopete; will always try to message the user using the online service.</para><tip><para>If you click on the small protocol icon on the right of the menu item, instead of on the person's name, you will always try to contact the person using that service!</para></tip>
+</answer>
+</qandaentry>
+<qandaentry>
+<question><para>I need to connect via a SOCKS proxy, but I can't find any proxy configuration options in &kopete;. How do I set up &kopete; to use SOCKS?</para>
+</question>
+<answer><itemizedlist>
+<listitem><para><trademark>MSN</trademark>, <trademark>ICQ</trademark>, <trademark>AIM</trademark>, Jabber, and <trademark>Yahoo</trademark> use the &kde; network infrastructure. Their SOCKS proxy details are configured with the rest of &kde;, in <application>Control Center</application>, <menuchoice><guimenu>Internet &amp; Network</guimenu><guimenuitem>Proxy</guimenuitem></menuchoice>.</para></listitem>
+</itemizedlist>
+</answer>
+</qandaentry>
+<qandaentry>
+<question><para>Is it possible to customize the icons I see in &kopete;?</para></question>
+<answer><para>You can switch between different emoticons using the Emoticons tab of the Appearance page of the Configure &kopete; dialog.</para>
+<para>To install new emoticons, first look at <ulink url="http://www.kde-look.org/content/search.php">KDE-Look.org</ulink>, where there are a lot of additional emoticon sets to download.</para>
+
+<para>The emoticons are easy to install - you just place a directory containing
+the icon files along with an XML file describing the mapping from text to picture in
+$KDEDIR/share/apps/kopete/pics/emoticons (or $KDEHOME, for example, in /home/joeuser/.kde/).</para>
+
+<orderedlist>
+<listitem><para>Copy the extracted directory to $KDEDIR/share/apps/kopete/pics/emoticons or
+ $HOME/.kde/share/apps/kopete/pics/emoticons (or wherever $KDEHOME is)</para></listitem>
+<listitem><para>Select Configure &kopete; from the Settings menu and click on Appearance in the left panel of the Preferences window and click on the Emoticons tab</para></listitem>
+<listitem><para>Select the emoticons set you just installed from the list</para></listitem>
+<listitem><para>Now you can use the newly installed emoticons in &kopete;</para></listitem>
+</orderedlist>
+<para>To replace the protocol icons, you'll have to replace the icons in
+$KDEDIR/share/apps/kopete/icons, or provide replacements to override them in
+the same dir under $KDEHOME. At present there aren't any complete replacement
+sets that you can simply extract there.</para>
+</answer>
+</qandaentry>
+</qandaset>
+</chapter> <!-- faq -->
+
+<chapter id="specialised">
+<title>Specialized Actions</title>
+<para>Command line parameters</para>
+<para>Installing emoticon sets</para>
+</chapter> <!-- specialised -->
+
+<chapter id="credits-and-licenses">
+<title>Credits and Licenses</title>
+<para>
+&kopete;: copyright 2001-2005, &kopete; Developers
+</para>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+&underFDL;
+&underGPL;
+<sect1 id="team">
+<title>Current Development Team</title>
+<!-- please keep in sync with the authors list on the webpage -->
+<itemizedlist>
+<listitem><para>Duncan Mac-Vicar Prett (duncan at kde org): Original author, developer, and project leader</para></listitem>
+<listitem><para>Till Gerken (till at tantalo net): Developer, Jabber maintainer</para></listitem>
+<listitem><para>Olivier Goffart (ogoffart at tiscalinet be): Lead Developer, MSN Plugin Maintainer</para></listitem>
+<listitem><para>Andy Goossens (andygoossens at telenet be): Developer</para></listitem>
+<listitem><para>Grzegorz Jaskiewicz (gregj at pointblue com pl): Developer, Gadu-gadu Plugin Maintainer</para></listitem>
+<listitem><para>Jason Keirstead (jason at keirstead org): Developer</para></listitem>
+<listitem><para>Matt Rogers (mattr at kde org): Lead Developer, AIM and ICQ plugin maintainer</para></listitem>
+<listitem><para>Richard Smith (lilachaze at hotmail com): Developer, UI maintainer</para></listitem>
+<listitem><para>Will Stephenson (lists at stevello free-online co uk): Developer, icons, plugins, manual author</para></listitem>
+<listitem><para>Michel Hermier (michel.hermier at wanadoo fr): IRC Plugin Maintainer</para></listitem>
+<listitem><para>Andre Duffeck (andre at duffeck de): Developer: Developer, Yahoo plugin maintainer</para></listitem>
+<listitem><para>Michaël Larouche (michael.larouche at kdemail net): Developer, MSN, Chat Window.</para></listitem>
+</itemizedlist>
+</sect1>
+<sect1 id="ex-team">
+<title>Former Developers (&kopete; Hall Of Fame)</title>
+<para>These people have moved on from &kopete;, so don't contact them for &kopete; support. We're eternally grateful for their contributions. </para>
+<itemizedlist>
+<listitem><para>Christopher TenHarmsel (tenharmsel at users sourceforge net)Developer, Oscar hacker</para></listitem>
+<listitem><para>Ryan Cumming (ryan at kde org): Core developer</para></listitem>
+<listitem><para>Richard Stellingwerff (remenic at linuxfromscratch org): Developer</para></listitem>
+<listitem><para>Hendrik vom Lehn (hennevl at hennevl de): Developer</para></listitem>
+<listitem><para>Stefan Gehn (sgehn at gmx net): Developer</para></listitem>
+<listitem><para>Robert Gogolok (robertgogolock at gmx de): Developer</para></listitem>
+<listitem><para>Nick Betcher (nbetcher at kde org): Original author of ICQ, AIM and IRC plugins</para></listitem>
+<listitem><para>Daniel Stone (dstone at kde org): Original Jabber plugin author</para></listitem>
+<listitem><para>James Grant (topace at lightbox org): Developer, importer Plugin author</para></listitem>
+<listitem><para>Zack Rusin (zack at kde org): Developer, old Gadu-gadu Plugin author</para></listitem>
+<listitem><para>Gav Wood (gav at kde org): WinPopup Plugin author</para></listitem>
+<listitem><para>Martijn Klingens (klingens at kde org): Developer, MSN hacker</para></listitem>
+</itemizedlist>
+</sect1>
+<sect1 id="documentation">
+<title>Documentation</title>
+<para>
+Documentation copyright 2003,2004,2005 Will Stephenson (lists at stevello free-online co uk), copyright 2005 Matt Rogers (mattr at kde org), copyright 2005,2006 Michaël Larouche (michael.larouche at kdemail net).
+</para>
+</sect1>
+</chapter><!-- credits-and-licenses -->
+
+<appendix id="installation">
+<title>Installation</title>
+<sect1 id="getting-kopete">
+<title>How to obtain &kopete;</title>
+&install.intro.documentation;
+<para>Development versions may be downloaded at &kopetewww;.</para>
+</sect1>
+<sect1 id="requirements">
+<title>Required Libraries</title>
+<para>If you installed &kopete; as part of your distribution, you probably have these installed already.</para>
+<para>The Gadu-gadu plugin requires the libgadu package, see <ulink url="http://gj.pointblue.com.pl/projects/kopete/">this page</ulink> for details.</para>
+<para>The Now Listening plugin needs libxmms if you want to access what <application>xmms</application> is currently playing; this should be available in your distribution but is available as part of the xmms package at <ulink url="http://www.xmms.org">the xmms homepage</ulink>.</para>
+</sect1>
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+&install.compile.documentation;
+</sect1>
+</appendix>
+
+&kopete-chatstyle;
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+// vim:ts=2:sw=2:tw=78:noet
+-->
diff --git a/doc/kopete/menus.docbook b/doc/kopete/menus.docbook
new file mode 100644
index 00000000..334b1114
--- /dev/null
+++ b/doc/kopete/menus.docbook
@@ -0,0 +1,876 @@
+<!-- Kopete Handbook, Menus Chapter copyright 2003 Will Stephenson. Licensed under the GNU Free Documentation License -->
+<chapter id="menus">
+<title>Menu Entries</title>
+
+<para>Each menu item is discussed below. When there is a keyboard shortcut that
+performs a menu item function, the default shortcut is listed with the menu item.</para>
+<sect1 id="menus-contactlist">
+<title>The Contact List Window's Menus</title>
+<sect2 id="menus-contactlist-file">
+<title><guimenu>File</guimenu> Menu</title>
+
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>File</guimenu>
+<guisubmenu>Set Status</guisubmenu>
+<guimenuitem>Online</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Go online with all accounts</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>File</guimenu>
+<guisubmenu>Set Status</guisubmenu>
+<guimenuitem>Away</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Set all connected accounts away</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>File</guimenu>
+<guisubmenu>Set Status</guisubmenu>
+<guimenuitem>Offline</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Set all accounts offline</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Add Contact...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This displays the Add Contact Wizard, which allows you to add a new contact to your list</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>File</guimenu>
+<guimenuitem>Create New Group...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Prompts you for the new group's name and adds it to the contact list.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo>
+</shortcut>
+<guimenu>File</guimenu>
+<guimenuitem>Quit</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Disconnects you from all &im; systems, closes all the windows and exits the application.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+<sect2 id="menus-contactlist-edit">
+<title><guimenu>Edit</guimenu> Menu</title>
+<variablelist>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>Z</keycap></keycombo>
+</shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Undo</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Reverts the last change that was made to the contact list.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;&Shift;<keycap>Z</keycap></keycombo>
+</shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Redo</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Reverts the last change that was made to the contact list by <guimenu>Edit</guimenu><guimenuitem>Undo</guimenuitem></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Send Single Message...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Opens an email-style message window with the selected contact, to send a single message.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Start Chat...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Opens a chat window with the selected contact, to have a conversation.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Send File...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>If supported by the &im; system, this opens a file selector to choose a file to send to the selected contact.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guisubmenu>Move To</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Choose another group from the sub-menu, and the contact will move to that group.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guisubmenu>Copy To</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Choose a group from the sub-menu, and the contact will be copied to group. &im; systems that allow contacts to be in more than one group on the server contact list will be updated accordingly.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo><keycap>Delete</keycap></keycombo>
+</shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Remove</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Removes a contact from the contact list entirely.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Rename Contact</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Renames the contact on the contact list. If you do this, the contact list entry will no longer change if the contact changes their display name remotely. You can re-enable this using the contact's Properties dialog.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guisubmenu>Add Contact...</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Choose another account from the submenu, and you can add another way to message that person using that account.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Add to Your Contact List...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a contact is selected) Sometimes people will message you who aren't on your contact list already. In this case, &kopete; creates a temporary entry for them, but to keep them on your contact list, use this function.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Properties</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>The Properties dialog lets you choose custom icons for the selected item, and change its name.</para>
+<para>For contacts, you can change the &kde; Address Book entry that they are associated with.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Remove Group</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a group is selected) Removes a group from the contact list entirely. Any contacts that are only in this group are moved to the top level.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Edit</guimenu>
+<guimenuitem>Rename Group</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>(Enabled when a group is selected) Renames the group.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+<sect2 id="menus-settings">
+<title><guimenu>Settings</guimenu> Menu</title>
+<variablelist>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>M</keycap></keycombo>
+</shortcut>
+<guimenu>Settings</guimenu>
+<guimenuitem>View Menubar</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Shows/hides the menu bar.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>View Toolbar</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Shows/hides the tool bar.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>View Statusbar</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Shows/hides the status bar.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>U</keycap></keycombo>
+</shortcut>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Offline Users</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This shows/hides contacts who are currently offline. They will become visible when they go online again.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>G</keycap></keycombo>
+</shortcut>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Empty Groups</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>This shows/hides groups which do not have any members, or where all the members are offline and hidden (see above).</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Shows the &kde; standard Configure Shortcuts dialog, where you can change keyboard shortcuts that work in the Contact List's windows.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Global Shortcuts...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Displays the &kde; standard Configure Global Shortcuts dialog, where you can change keyboard shortcuts that work all the time under &kde;.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Toolbars...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Displays the &kde; standard Configure Toolbars dialog, which lets you customise the Contact List's toolbars.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Kopete...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Displays the <link linkend="configure-dialog">Configure Kopete</link> dialog. </para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Plugins...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Displays the <link linkend="configuring-load-plugins-dialog">Configure Plugins</link> dialog.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+<sect2 id="menus-help">
+<title><guimenu>Help</guimenu> Menu</title>
+<para>These are the &kde; standard items for the <guimenu>Help</guimenu> menu:</para>
+
+&help.menu.documentation;
+
+</sect2>
+</sect1> <!-- menus-contactlist -->
+
+<sect1 id="menus-chatwindow">
+<title>The Chat Window's Menus</title>
+<sect2 id="menus-chatwindow-chat">
+<title><guimenu>Chat</guimenu> Menu</title>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>&Enter;</keycap></keycombo>
+</shortcut>
+<guimenu>Chat</guimenu>
+<guimenuitem>Send Message</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Sends a message.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>S</keycap></keycombo>
+</shortcut>
+<guimenu>Chat</guimenu>
+<guimenuitem>Save</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Saves the content of the chat to a file.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>P</keycap></keycombo>
+</shortcut>
+<guimenu>Chat</guimenu>
+<guimenuitem>Print...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Prints off a hard copy of the chat.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Chat</guimenu>
+<guisubmenu>Contacts</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu lists all the people in the chat. You have access to the same contact menu you get by <mousebutton>right</mousebutton>-clicking a contact's name in the Contact List, allowing you to perform contact specific actions such as sending them a file, viewing their user info or blocking them.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>W</keycap></keycombo>
+</shortcut>
+<guimenu>Chat</guimenu>
+<guimenuitem>Close</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Closes the current chat. If there are chats taking place in other tabs in this window, the window will stay open.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>Q</keycap></keycombo>
+</shortcut>
+<guimenu>Chat</guimenu>
+<guimenuitem>Quit</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Closes all chats taking place in this window.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect2>
+<sect2 id="menus-chatwindow-edit">
+<title><guimenu>Edit</guimenu> Menu</title>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>X</keycap></keycombo></shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Cut</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Cutting text works as with most editors: the selected text is
+removed and put into the clipboard. Note that you can also select text and
+drag it to a new position.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>C</keycap></keycombo>
+</shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Copy</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Copying text works as with most editors: the selected text is copied to the clipboard.
+Note that you can also select text while holding the &Ctrl; key, and drag it to a new
+position to copy it.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>V</keycap></keycombo></shortcut>
+<guimenu>Edit</guimenu>
+<guimenuitem>Paste</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Pasting works the same as with most editors: the text from the
+clipboard is pasted at the current cursor position.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</sect2>
+<sect2 id="menus-chatwindow-format">
+<title><guimenu>Format</guimenu> Menu</title>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Format</guimenu>
+<guisubmenu>Add Smiley</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu contains all the smileys/emoticons that the current emoticon scheme includes. You can change the scheme <link linkend="configuring-appearance-emoticons">in the Configure Kopete Dialog</link>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Format</guimenu>
+<guimenuitem>Text Color...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Opens a color selector that modifies the text color.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Format</guimenu>
+<guimenuitem>Background Color...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Opens a color selector that modifies the background color.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Format</guimenu>
+<guisubmenu>Font</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu allows you to change the font used in the chat.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Format</guimenu>
+<guisubmenu>Font Size</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu allows you to change the font size used in the chat.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+<sect2 id="menus-chatwindow-tabs">
+<title><guimenu>Tabs</guimenu> Menu</title>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Tabs</guimenu>
+<guisubmenu>Tab Placement</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu allows you to change whether tabs appear at the top or the bottom of the chat view.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;&Shift;<keycap>B</keycap></keycombo>
+</shortcut>
+<guimenu>Tabs</guimenu>
+<guimenuitem>Detach Chat</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Separates the current chat into its own window.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Tabs</guimenu>
+<guisubmenu>Move Chat to Window</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>You can move chats between windows using this menu. Choose the chat window the tab should move to from the menu.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+<sect2 id="menus-chatwindow-tools">
+<title><guimenu>Tools</guimenu> Menu</title>
+<para>This menu contains items added by the plugins you have loaded. See the <link linkend="plugins">plugins chapter</link> for details.</para>
+</sect2>
+<sect2 id="menus-chatwindow-settings">
+<title><guimenu>Settings</guimenu> Menu</title>
+<variablelist>
+<varlistentry>
+<term>
+<menuchoice>
+<shortcut><keycombo action="simul">&Ctrl;<keycap>M</keycap></keycombo>
+</shortcut>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Menubar</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Separates the current chat into its own window.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guisubmenu>Toolbars</guisubmenu>
+<guimenuitem>Show Main Toolbar (Kopete)</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para>Shows/hides the main toolbar.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guisubmenu>Toolbars</guisubmenu>
+<guimenuitem>Show Status (Kopete)</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para>Shows/hides &kopete;'s status bar. This is where buddy pictures appear.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guisubmenu>Toolbars</guisubmenu>
+<guimenuitem>Show Format Toolbar (Kopete)</guimenuitem></menuchoice>
+</term>
+<listitem>
+<para>Shows/hides the text formatting toolbar.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Show Statusbar</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Separates the current chat into its own window.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guisubmenu>Show Chat Members List</guisubmenu>
+</menuchoice>
+</term>
+<listitem>
+<para>This menu controls whether the Chat Members List appears on the left or right of the Chat View, and whether it is visible at all.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Shortcuts...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Shows the &kde; standard Configure Shortcuts dialog, where you can change keyboard shortcuts that work in the chat window.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Settings</guimenu>
+<guimenuitem>Configure Toolbars...</guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para>Displays the &kde; standard Configure Toolbars dialog, which lets you customise the chat window's toolbars.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+</sect2>
+<sect2 id="menus-chatwindow-help">
+<title><guimenu>Help</guimenu> Menu</title>
+<para>These are the &kde; standard items for the <guimenu>Help</guimenu> menu:</para>
+
+&help.menu.documentation;
+
+</sect2>
+<!--
+<varlistentry>
+<term>
+<menuchoice>
+<guimenu>Chat</guimenu>
+<guimenuitem></guimenuitem>
+</menuchoice>
+</term>
+<listitem>
+<para></para>
+</listitem>
+</varlistentry>
+
+-->
+</sect1> <!-- menus-chatwindow -->
+</chapter> <!-- menus -->
+<!--
+CHAT
+Send Message
+Save
+Print...
+Contacts>
+Close
+Quit
+EDIT
+Cut
+Copy
+Paste
+FORMAT
+Add Smiley>
+Text Color...
+Background Color...
+Font>
+Font Size>
+TABS
+Tab Placement>
+Detach Chat
+Move Chat to Window>
+(TOOLS
+Send Media Info)
+SETTINGS
+Show Menubar
+Toolbars>
+ Show Main Toolbar (Kopete)
+ Show Status (Kopete)
+ Show Format Toolbar (Kopete)
+Show Statusbar
+Chat Members List>
+ Place to Left of Chat Area
+ Place to Right of Chat Area
+ Show
+Configure Shortcuts...
+Configure Toolbars...
+HELP
+-->
+<!--FILE
+Connection>
+ Connect All
+ Disconnect All
+Status>
+ Set Available Globally
+ Set Away Globally
+Add Contact...
+Create New Group...
+Save ContactList
+Quit
+EDIT
+Send Message..
+Start Chat...
+Send File...
+Move To>
+ (Group List)
+Copy To>
+ (Group List)
+Remove From Group
+(on Group)
+Remove Group
+Rename Group
+(on Contact)
+Remove Contact
+Rename Contact
+Add Contact
+Add To Contact List
+View History
+Properties
+SETTINGS
+View Menubar
+View Toolbar
+View Statusbar
+Show Offline Users
+Show Empty Groups
+Configure Shortcuts...
+Configure Global Shortcuts...
+Configure Toolbars...
+Configure Plugins...
+Configure Kopete...
+HELP
+Kopete Handbook...
+What's This?
+Report Bug
+About Kopete
+About KDE
+-->
diff --git a/doc/kpf/Makefile.am b/doc/kpf/Makefile.am
new file mode 100644
index 00000000..e786da56
--- /dev/null
+++ b/doc/kpf/Makefile.am
@@ -0,0 +1,3 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
diff --git a/doc/kpf/index.docbook b/doc/kpf/index.docbook
new file mode 100644
index 00000000..5dff1c74
--- /dev/null
+++ b/doc/kpf/index.docbook
@@ -0,0 +1,410 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kpf;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+ <bookinfo>
+
+ <title>The &kpf; Handbook</title>
+
+ <authorgroup>
+
+ <author>
+ <firstname>Rik</firstname>
+ <surname>Hemsley</surname>
+ <affiliation>
+ <address>&Rik.Hemsley.mail;</address>
+ </affiliation>
+ </author>
+
+ <!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+ </authorgroup>
+
+ <copyright>
+ <year>2002</year>
+ <holder>&Rik.Hemsley;</holder>
+ </copyright>
+
+ <legalnotice>&FDLNotice;</legalnotice>
+
+ <date>2003-09-30</date>
+ <releaseinfo>1.0.1</releaseinfo>
+
+ <abstract>
+ <para>
+ &kpf; allows you to share files over a network.
+ </para>
+ </abstract>
+
+ <keywordset>
+ <keyword>KDE</keyword>
+ <keyword>public</keyword>
+ <keyword>fileserver</keyword>
+ <keyword>HTTP</keyword>
+ </keywordset>
+
+ </bookinfo>
+
+ <chapter id="introduction">
+
+ <title>Introduction</title>
+
+ <para>
+ &kpf; provides simple file sharing using &HTTP; (the Hyper Text
+ Transfer Protocol,) which is the same protocol used by web sites to
+ provide data to your web browser. &kpf; is strictly a public
+ fileserver, which means that there are no access restrictions to shared
+ files. Whatever you select for sharing is available to anyone.
+ </para>
+
+ <para>
+ &kpf; is designed to be used for sharing files with friends, not to
+ act like a fully-fledged web server such as
+ <application>Apache</application>. &kpf; was primarily conceived
+ as an easy way to share files with others while chatting on
+ <acronym>IRC</acronym> (Internet Relay Chat, or <quote>chat
+ rooms</quote>.)
+ </para>
+
+ <para>
+ Typical usage: &kpf; is set up to serve files from the <filename
+ class="directory">public_html</filename> folder in your home
+ folder. You wish to make a file available to some people with
+ whom you are chatting online. Rather than send them each an
+ email with the file attached (some may not even be interested,)
+ you copy the file into your <filename
+ class="directory">public_html</filename> folder and announce
+ to those listening that your file is available at
+ http://www.mymachine.net:8001/thefile
+ </para>
+
+ </chapter>
+
+ <chapter id="using-kpf">
+
+ <title>Using &kpf;</title>
+
+ <sect1 id="kpf-basics">
+
+ <title>&kpf; basics</title>
+
+ <para>
+ &kpf; runs as an applet inside &kicker;. This means that it
+ takes up little space on your screen and its status is always
+ visible. To start the &kpf; applet,
+ <mousebutton>right</mousebutton> click on &kicker; and choose
+ <guimenu>Add Applet to Panel...</guimenu> to open the <guilabel>Add
+ Applet</guilabel> dialog. Select <guilabel>Public File Server</guilabel> and
+ click the <guibutton>Add to Panel</guibutton> button.
+ </para>
+
+ <para>
+ &kpf; employs the concept of shared folders. You may choose
+ one or more folders to make public, and all files in that folder
+ (and any subfolders) will be shared.
+ </para>
+
+ <para>
+ Please be extremely careful about which folders you
+ share. Remember that all files in the folder and its
+ subfolders, including <quote>hidden</quote> files
+ (<quote>dotfiles</quote> to the techies) will be made
+ available to the world, so be careful not to share sensitive
+ information, such as passwords, cryptographic keys, your
+ addressbook, documents private to your organization, &etc;.
+ </para>
+
+ <para>
+ Once &kpf; is running, you will see a square applet with a
+ thin sunken bevel and an icon depicting an <guiicon>hot air
+ balloon</guiicon>. The balloon is visible when no folders are being
+ shared.
+ </para>
+
+ <para>
+ To share a folder, <mousebutton>right</mousebutton> click
+ on the balloon icon and a popup menu will appear, containing
+ only one item, <guimenuitem>New
+ Server...</guimenuitem>. Selecting this entry will cause a
+ <quote>wizard</quote> to appear, which will ask you a few
+ simple questions. Completing the questions will set up a
+ folder for sharing.
+ </para>
+
+ <para>
+ There is an alternative to using the applet directly when you
+ want to share a folder. &kpf; is integrated with &konqueror;.
+ </para>
+
+ <para>
+ With &konqueror; open and displaying a folder,
+ <mousebutton>right</mousebutton> click on the background and
+ bring up the <quote>Properties</quote> dialog. On install,
+ &kpf; added a <guilabel>Sharing</guilabel> tab to this
+ dialog. You will be offered the option of starting &kpf; if it
+ is not running. Choosing <guibutton>Ok</guibutton> will send a
+ signal to the &kpf; applet, asking it to add a new share.
+ </para>
+
+ </sect1>
+
+ </chapter>
+
+ <chapter id="share-config">
+
+ <title>Share configuration</title>
+
+ <sect1 id="listen-port">
+
+ <title>Listen port</title>
+
+ <para>
+ For each folder that is shared by &kpf;, a new network
+ <quote>port</quote> is opened. A <quote>port</quote> is simply a number used to uniquely identify a
+ network service. When someone uses a program (&eg; a web browser)
+ to connect to a machine, it is directed to the service by specifying
+ the address of the machine and the <quote>port</quote> on which the service is
+ running.
+ </para>
+
+ <para>
+ The <quote>port</quote> concept allows one machine to run more
+ than one network service. Services you might use every day
+ include &HTTP; (the web,) usually connected to by port 80,
+ &SMTP; (mail sending,) usually on port 25,
+ and POP3 (mail receiving,) usually on port 110.
+ </para>
+
+ <para>
+ Usually, when you connect to a network service, you do not need
+ to specify which <quote>port</quote> you want to connect
+ to. This is because the ports are standardized, so anyone
+ connecting to port 80 on a network machine expects to find an
+ &HTTP; (web) server.
+ </para>
+
+ <para>
+ &kpf; is not a <quote>standard</quote> service, so 8001 was
+ chosen for the default port.
+ </para>
+
+ <para>
+ The second folder you share will offer to listen on port 8002,
+ with the number being incremented each time you start a new share.
+ </para>
+
+ <para>
+ Within certain boundaries, you are free to choose whichever port
+ number you wish, for a share.
+ </para>
+
+ <para>
+ It is usual for port numbers below 1000 to be reserved for
+ <quote>system</quote> services, &ie; those under the control
+ of the machine's administrator, therefore you may find that
+ attempting to use anything below 1000 will simply not work.
+ </para>
+
+ <para>
+ &kpf; tries to warn you when it cannot <quote>listen</quote>
+ on a port. It does this by displaying a <guiicon>broken
+ connection</guiicon> icon over the top-left corner of the
+ graph. &kpf; attempts to stop you assigning more than one
+ share to the same port, but it will not attempt to stop you
+ setting a share to listen on a port which is already occupied
+ by another service, for example your <quote>real</quote> web
+ server.
+ </para>
+
+ <para>
+ If you see the <guiicon>broken connection</guiicon> icon,
+ <mousebutton>right</mousebutton> click on the bandwidth graph
+ and choose <guimenuitem>Configure...</guimenuitem> Now try
+ changing the listen port and pressing
+ <guibutton>Ok</guibutton>. Assuming that this time you picked
+ a free port, you should see that the <guiicon>broken
+ connection</guiicon> icon disappears, and you should now be
+ able to connect to the share.
+ </para>
+
+ </sect1>
+
+ <sect1 id="bandwidth-limit">
+
+ <title>Bandwidth limit</title>
+
+ <para>
+ The term <quote>bandwidth</quote> refers to the amount of data
+ that may be transmitted over a connection during a period of
+ time. Techies may be overheard referring to how
+ <quote>fat</quote> their <quote>pipe</quote> is. The analogy
+ is apt.
+ </para>
+
+ <para>
+ &kpf; allows you to set a limit on how much bandwidth will be
+ used by a particular share. This is useful when you want to
+ avoid your network connection being saturated by people
+ downloading from your shares. If you are on a modem, for
+ example, you only have a few kilobytes per second to
+ yourself. Limiting the bandwidth used by your &kpf; shares
+ will allow you to keep a portion of the bandwidth for your own
+ use.
+ </para>
+
+ <para>
+ As just mentioned, &kpf; measures bandwidth in kilobytes per
+ second, or kB/s for short. A typical modem transfers about 5kB/s on
+ average, so limiting the total use of all &kpf; shares to a value
+ less than this may be wise, depending on how you are using &kpf;.
+ </para>
+
+ </sect1>
+
+ <sect1 id="follow-symlinks">
+
+ <title>Follow symbolic links</title>
+
+ <para>
+ A symbolic link is a special file which is a reference to another
+ file (or folder) in your filesystem. By following the link,
+ you reach the file or folder referred to - the link is generally
+ transparent.
+ </para>
+
+ <para>
+ By default, a &kpf; share does not allow the following of
+ symbolic links. This means that, for example, if you have a
+ share pointing to <filename
+ class="directory">/your/home/folder/public_html</filename>
+ and you create a link inside <filename
+ class="directory">public_html</filename>, pointing to
+ <filename class="directory">/tmp</filename>, then anyone
+ requesting <filename class="directory">/tmp</filename> will
+ see the contents of your <filename>/tmp</filename> folder.
+ </para>
+
+ <para>
+ In general, it's a bad idea to allow the following of symbolic
+ links in this way. The main reason this is allowed is so that
+ you may have symbolic links inside the shared folder, which
+ point to another place inside the shared folder. This can
+ be useful if you're serving up an entire
+ <quote>website</quote> - which as mentioned previously, is not
+ the intended use of &kpf;.
+ </para>
+
+ <para>
+ Just be careful not to link to anywhere on your file system that
+ might hold sensitive information (or have a symbolic link in it
+ somewhere that points to sensitive information!)
+ </para>
+
+ </sect1>
+
+ </chapter>
+
+ <chapter id="faq">
+
+ <title>Questions and Answers</title>
+
+ <qandaset id="faq-questions">
+
+ <qandaentry>
+
+ <question>
+ <para>Why does &kpf; not include any security mechanisms?</para>
+ </question>
+
+ <answer>
+
+ <para>
+ In truth, &kpf; does include various measures designed to help
+ prevent the user accidentally providing access to sensitive
+ information. There is no password protection and no encryption.
+ This is by design, as will be explained.
+ </para>
+
+ <para>
+ The more security measures that are added to a service, the
+ safer people feel when using it. Sadly, to ensure real security,
+ the user needs to have a good understanding of the issues involved.
+ For example, providing password protection is no use if the user
+ does not know how to pick a good password. Therefore the decision
+ was made to provide zero security, in the hope that the user will
+ find it easier to understand what this means than to spend months
+ or years learning about the complexities of network security.
+ </para>
+
+ <para>
+ The concept is simple. If you specify that a folder is
+ shared, it's shared to the world. If you don't want to make
+ it available to the world, don't share it.
+ </para>
+
+ </answer>
+
+ </qandaentry>
+
+ </qandaset>
+
+ </chapter>
+
+ <chapter id="credits">
+
+ <title>Credits and License</title>
+
+ <para>
+ &kpf;
+ </para>
+
+ <para>
+ Program copyright 2002 &Rik.Hemsley; &Rik.Hemsley.mail;
+ </para>
+
+ <para>
+ Documentation copyright 2002 by &Rik.Hemsley; &Rik.Hemsley.mail;
+ </para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+ &underFDL;
+
+ <para>
+ &kpf; is released under the MIT license.
+ </para>
+
+ </chapter>
+
+ <appendix id="installation">
+
+ <title>Installation</title>
+
+ <sect1 id="getting-kpf">
+
+ <title>How to obtain &kpf;</title>
+
+ &install.intro.documentation;
+
+ </sect1>
+
+ </appendix>
+
+ &documentation.index;
+
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes: nil
+sgml-general-insert-case: lower
+End:
+-->
+
+<!-- vim:tabstop=2:shiftwidth=2:expandtab -->
diff --git a/doc/kppp/Makefile.am b/doc/kppp/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kppp/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kppp/accounting.docbook b/doc/kppp/accounting.docbook
new file mode 100644
index 00000000..ec6db43c
--- /dev/null
+++ b/doc/kppp/accounting.docbook
@@ -0,0 +1,158 @@
+<appendix id="appendix-accounting-template">
+<title>An example template for Telephone cost accounting.</title>
+
+<para>If you can't find a rule for your region you will have to write one by
+following the following template. Don't be afraid though it is really
+easy.</para>
+
+<para>Don't forget to submit your newly created rules file to &kppp;'s
+maintainer. The newly created rules file can be checked for valid syntax with
+the <userinput><option>-r</option>
+<replaceable>rule_file</replaceable></userinput> command line option to &kppp;
+and must be installed in <filename
+class="directory">${KDEDIR}/share/apps/kppp/Rules</filename> or in <filename
+class="directory">${HOME}/.kde/share/apps/kppp/Rules</filename> before you will
+be able to select it in this dialog.</para>
+
+<programlisting>
+################################################################
+#
+# Disclaimer/License
+# This Template ist (c) by Mario Weilguni &lt;mweilguni@kde.org&gt;
+# It ist licensed under the same terms as the kppp package,
+# which it is part of
+#
+################################################################
+#
+# This is a sample rule set for kppp. You can use it as a
+# template when you have to create your own ruleset. If you do
+# so, remove all comments and add your own. This will allow
+# other users to check your ruleset more easily.
+#
+# Please sign the the tarif file with your name an email address
+# so that I can contact you if necessary.
+#
+# NOTE: the rules in this rule set do not make much sense and
+# are only for demonstration purposes
+#
+# NOTE ON FILENAMES:
+# when you create your own ruleset, use "_" in filename
+# instead of spaces and use ".rst as extension
+# i.e. "Austria city calls"
+# --> file should be saved as "Austria_city_calls.rst"
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=default
+
+################################################################
+# currency settings
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=ATS
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.1, 72)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+on () between () use (0.2, 2)
+
+# same as above
+on (monday..sunday) between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# applies on friday, saturday, sunday and monday 8am until 1pm
+on (friday..monday) between (8:00..13:00) use(0.3,72)
+
+# ATTENTION:
+on(monday..friday) between (21:00..5:00) use (0.4,2)
+# does NOT include saturday 0:00-5:00, just monday..friday, as it says.
+
+# applies on a given date (christmas)
+on (12/25) between () use (0.3,72)
+
+# a range of dates and one weekday
+on (12/25..12/27, 12/31, 07/04, monday) between () use (0.4, 72)
+
+# use this for easter
+on (easter) between () use (0.3,72)
+
+# easter + 50 days (Pfingstmontag/ Pentecost Monday )
+on (easter+50) between () use (0.3,72)
+
+on (thursday) between (20:00..21:52) use (8.2, 1)
+
+
+# The "on()" rules above all relates to current time only. You can also
+# make a rule depend on the number of seconds you have been connected
+# by specifying this time as a third argument to "use()".
+# For instance, let's say normal rate in the evening is 0.20 per minute,
+# and it drops by 20% after one hour of connect time. This can be modelled
+# like:
+
+on () between (19:30..08:00) use (0.20, 60)
+on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# Note that these rules, just like other rules, are sensitive to the
+# order in which they appear.
+</programlisting>
+
+</appendix>
diff --git a/doc/kppp/callback.docbook b/doc/kppp/callback.docbook
new file mode 100644
index 00000000..93f79238
--- /dev/null
+++ b/doc/kppp/callback.docbook
@@ -0,0 +1,268 @@
+<chapter id="callback">
+<title>Configuring &kppp; for callback</title>
+
+<para>This chapter is based on material provided by Martin H&auml;fner,
+<email>mh@ap-dec717c.physik.uni-karlsruhe.de</email></para>
+
+<sect1 id="unix-callback-server">
+<title>&UNIX; or &Linux; callback server</title>
+
+<para>This section introduces &UNIX; (&Linux;) callback, and how &kppp; can be
+configured to connect to a &UNIX; callback server, especially to a script based
+&Linux; <link linkend="callback-resources">callback server</link></para>
+
+<sect2>
+<title>An Introduction to callback</title>
+
+<para>There are several reasons to consider using callback. Some of these are:</para>
+
+<itemizedlist>
+<listitem>
+<para>To increase the security of your local network</para>
+</listitem>
+<listitem>
+<para>To reduce expenses of external co-workers</para>
+</listitem>
+<listitem>
+<para>To control telephone costs where calls are claimed as business
+expenses</para>
+</listitem>
+</itemizedlist>
+
+<para>Think about someone calling the number of your dial in server, and then
+cracking a password. Why bother to maintain a firewall for your internet
+connection, if access to your network is that easy?.</para>
+
+<para>Callback software generally asks for your name, and then hangs up the
+line. It then calls you back, usually at a number that is stored
+<emphasis>on the server</emphasis> in a database. The client then picks up the
+phone line and continues with the dial-in as if nothing had happened. The
+server now requests your username and password, knowing that you are who you
+said you were when you first dialled in, or at the least, you are where you said
+you were. The connection is established normally, and the
+<application>pppd</application> is started.</para>
+
+<para>Now the big question is, how to tell the client to pick up the phone, when
+the server calls you back. Do you need a special program, such as
+<application>mgetty</application>? The answer is, <emphasis>no</emphasis>, you
+don't need a special client program. In general, any client can be used for
+callback connections, you could even use an ordinary terminal program such as
+<application>minicom</application> to connect.</para>
+
+<para>The only thing you have to do is tell your modem to
+<command>AutoAnswer</command> the phone when a
+<computeroutput>RING</computeroutput> is detected by the modem. This is done
+with the following modem command:</para>
+
+<screen>
+<userinput><command>AT&amp;SO=1</command></userinput>
+</screen>
+
+<para>This tells the modem to pick the phone up after one
+<computeroutput>RING</computeroutput>.</para>
+
+<para>Like a lot of other client programs, &kppp; checks to see if the
+connection is closed by the server, and then stops the current session if a
+<computeroutput>NO CARRIER</computeroutput> is detected. This, then, is the
+real problem when setting up callback. <computeroutput>NO
+CARRIER</computeroutput> will of course be detected the moment the callback
+server hangs up the line. Some servers therefore use a special login program.
+So how do you solve this problem? You tell your modem to show
+<computeroutput>CARRIER UP</computeroutput> at all times (which causes no
+problems if you tell the client to hang up the line.) You can do this with the
+following modem command:</para>
+
+<screen>
+<userinput><command>AT&amp;C0</command></userinput>
+</screen>
+
+<para>If you want to test this, you can first use an ordinary terminal program
+such as <application>minicom</application>, and call your callback server, to
+see what hapens.</para>
+
+</sect2>
+
+<sect2>
+<title>The &kppp; setup</title>
+
+<para>So, now that you've seen the theory in action, how do you go about setting
+up &kppp; to handle the connection? </para>
+
+<para>The procedure is quite straightforward, as follows.</para>
+
+<procedure>
+<step>
+<para>First tell the modem to accept connections, and to not stop the
+negotiation when the callback server hangs up the line for the first time. You
+can add both these options in the <guilabel>Modem</guilabel> tab of the &kppp;
+configuration, by adding to the option <guilabel>Dial String</guilabel> the
+string <command>AT&amp;C0S0=1DT</command></para>
+<para>There are no other changes with configuration for &kppp;. If you meet
+trouble with modem init and reset, check the <link
+linkend="callback-troubleshooting">Troubleshooting</link> section for more
+information.</para>
+</step>
+<step>
+<para>Think about your server for a moment. Remember that &UNIX;, &Windows; and
+Macintosh operating systems have differing opinions about how to end a line in a
+text file, and therefore, in login procedures too. If you are connecting to a
+&Windows; server, use <userinput>CR/LF</userinput>, if you are connecting to a
+&UNIX; server, use <userinput>CR</userinput>, and if you are connecting to a
+Macintosh server, use <userinput>LF</userinput>
+
+</para>
+</step>
+<step>
+<para>We are assuming for these instructions that you are calling a &Linux;
+callback package which uses ordinary login (not <acronym>PAP</acronym> or
+such).</para>
+<para>Set the <guilabel>Authentication</guilabel> style in the
+<guilabel>Dial</guilabel> tab of the account configuration to
+<guilabel>Script-based</guilabel></para>
+</step>
+<step>
+<para>Now you have to build the login script. Editing of login scripts is one
+of the very cool features of &kppp; You can find it in the <guilabel>Login
+Script</guilabel> tab of the <guilabel>Edit Account</guilabel> dialog.</para>
+
+<para>In this example, the user <systemitem>userxyz</systemitem> needs the
+following script to be called. The callback server already knows the table of
+names and their applicable phone numbers, so you select the phone number to be
+used with an alias, for security purposes.</para>
+
+<para>For each line, choose the criteria from the drop down list on the left of
+the dialog, and type in the action in the text box on it's right. Choose the
+<guibutton>Add</guibutton> to add each line to the script. You can use
+<guibutton>Insert</guibutton> to add a line into the middle of the script, and
+<guibutton>Remove</guibutton> to delete a line if you made a mistake.</para>
+
+<para>The entire script should look something like this (without the comments,
+shown here starting with a #)</para>
+
+<screen>
+Expect ogin: <lineannotation># remember, we do ordinary terminal login</lineannotation>
+ID "" <lineannotation># kppp sends the id you configured in
+the main dialog</lineannotation>
+Expect for userxyz: <lineannotation># a list of available numbers is
+shown, the user should choose one</lineannotation>
+Send userxyz-home <lineannotation># the user wants to be called back
+on their home number</lineannotation>
+Expect ogin: <lineannotation># The callback process is now
+running, a new connection, and so a new login.</lineannotation>
+ID
+Expect assword: <lineannotation># Now send your password</lineannotation>
+Expect &gt; <lineannotation># Wait for the command prompt (the
+prompt may vary)</lineannotation>
+Send start_ppp <lineannotation># this command starts the pppd</lineannotation>
+</screen>
+
+<para>After waiting for the login request, the user sends his ID and waits for a
+list of available phone numbers for that username. Then he tells the server
+which of the numbers offered he would like to be called back on. &kppp; can
+open a dialog for this, if your location changes often, &eg; you are a sales
+representative and move from hotel to hotel. Now the server is expecting login
+and password for authentication, but in the meantime, the server hangs up and
+calls the user back. The authentication information is sent, and &kppp; waits
+for a command prompt, and then starts a small script (here called
+<filename>start_ppp</filename> which fires up <application>pppd</application> on
+the server.</para>
+
+<para>The <filename>start_ppp</filename> script might look something like the
+following:</para>
+
+<programlisting>
+#!/bin/sh
+stty -echo
+exec /usr/sbin/pppd -detach silent modem
+</programlisting>
+
+<para>Of course, setting up a <acronym>PPP</acronym> server is not within the
+scope of this document. For detailed information, see the
+<application>pppd</application> man pages. An excellent description of a
+callback server can be found at <ulink
+url="http://ap-dec717c.physik.uni-karlsruhe.de/~mh/callback">
+http://ap-dec717c.physik.uni-karlsruhe.de/~mh/callback</ulink></para>
+</step>
+</procedure>
+
+<para>All other configuration issues, such as <application>pppd</application>
+configuration or <acronym>IP</acronym> settings work as normal, and no special
+software is required to pick up the line.</para>
+
+<note>
+<para>&kppp; callback and other programs such as
+<application>mgetty</application> or any other faxgetty can be run on the same
+serial port. There are no problems with the dial in, as &kppp; creates a lock
+file which will tell the getty program that another application (in this case,
+&kppp; of course,) is using the line at that time.</para>
+</note>
+
+</sect2>
+
+<sect2 id="callback-troubleshooting">
+<title>Troubleshooting</title>
+
+<para>There are some known problems with &kppp; in callback mode:</para>
+
+<itemizedlist>
+<listitem>
+<para>As you initialize the modem to auto answer, you need to reset the modem
+after your connection is closed. Otherwise, your modem will continue to pick up
+the line for you, which is not a good idea if the line in question is your main
+phone line.</para>
+</listitem>
+<listitem>
+<para>&kppp; has some small problems when sharing a line with another program,
+such as <application>mgetty</application>. If <application>mgetty</application>
+is running on the same modem line, &kppp; is not able to initialize the modem
+correctly. <!-- This happens on the second try (does this mean it can initialize -->
+<!-- on the second try, or it fails on the second time? --> </para>
+</listitem>
+<listitem>
+<para>&kppp; is unable to prompt for certain user input during a scripting based
+login. Unfortunately, when using the example script above, &kppp; also asks for
+the user name the second time the callback server requests it. You can get rid
+of this by hardcoding your userid into the login script (not very portable or
+nice, but it works.</para>
+</listitem>
+</itemizedlist>
+
+</sect2>
+
+<sect2 id="callback-resources">
+<title>Internet Resources for server software</title>
+
+<para>&Linux; callback server software bundles are available in many
+places.</para>
+
+<para>The well known <application>mgetty</application> is a very powerful
+program, and is also able to handle callback connections. A description of how
+to set up <application>mgetty</application> for this purpose is maintained at
+<ulink url="http://www.dyer.demon.co.uk/slug/tipscrip.htm">
+http://www.dyer.demon.co.uk/slug/tipscrip.htm</ulink>, by Colin McKinnon,
+<email>colin@wew.co.uk</email>.</para>
+
+<para>There is also a ready to use package for &Linux; at <ulink
+url="http://www.icce.rug.nl/docs/programs/callback/callback.html">
+http://www.icce.rug.nl/docs/programs/callback/callback.html</ulink>. This
+package is maintained by Frank B. Brokken, <email>frank@icce.rug.nl</email>. As
+the setup, although straightforward, is not very easy, I have written a short
+introduction for it at <ulink
+url="http://ap-dec717c.physik.uni-karlsruhe.de/~mh/callback">http://ap-dec717c.physik.uni-karlsruhe.de/~mh/callback/</ulink>,
+which also contains a more general introduction to callback.</para>
+
+</sect2>
+</sect1>
+
+<sect1 id="nt-callback">
+<title>&Windows; NT <acronym>RAS</acronym> callback</title>
+
+<para>&Windows; NT uses a completely different approach than the one described
+above. NT requires an extension to the <acronym>PPP</acronym> protocol itself,
+called <acronym>CBCP</acronym> (Call Back Control Protocol).
+<application>pppd</application> has support for this protocol, but you must
+recompile <application>pppd</application>. If anybody has experience with
+successfully connecting to an NT callback server, please let us know.</para>
+
+</sect1>
+</chapter>
diff --git a/doc/kppp/chap.docbook b/doc/kppp/chap.docbook
new file mode 100644
index 00000000..ebbdd3b9
--- /dev/null
+++ b/doc/kppp/chap.docbook
@@ -0,0 +1,191 @@
+<chapter id="chap-and-pap">
+<title><acronym>PAP</acronym> and <acronym>CHAP</acronym></title>
+
+<para>Starting with version 0.9.1, &kppp; has supported directly the most
+commonly used form of <acronym>PAP</acronym> authentication. </para>
+
+<sect1 id="pap-with-kppp">
+<title><acronym>PAP</acronym> with &kppp;</title>
+
+<para>There are two different ways to use <acronym>PAP</acronym>.</para>
+
+<sect2 id="client-side-authentication">
+<title>Client side authentication</title>
+
+<para>This variant is used by many commercial <acronym>ISP</acronym>'s. It
+basically means that you (or rather, your computer) must authenticate yourself
+to the <acronym>ISP</acronym>'s <acronym>PPP</acronym> server. The
+<acronym>PPP</acronym> server does not need to authenticate itself to your
+computer. This is no security issue, as you should know which computer you just
+tried to dial to.</para>
+
+<para>If your <acronym>ISP</acronym> gives you a username and password, and
+tells you to use <acronym>PAP</acronym> authentication, this is the variant you
+should choose.</para>
+
+</sect2>
+
+<sect2 id="two-way-authentication">
+<title>Two way authentication</title>
+
+<para>As above, but in this case your computer requires the
+<acronym>ISP</acronym> <acronym>PPP</acronym> server to authenticate itself. In
+order to establish a connection, you must chose the authentication method
+<guilabel>Script based</guilabel>, not <guilabel>PAP</guilabel>, and you will
+have to manually edit <filename>/etc/ppp/pap-secrets</filename>. While &kppp;
+doesn't provide built in support for this variant, it is nevertheless easy to
+establish a connection.</para>
+
+</sect2>
+
+<sect2 id="preparing-kppp-for-pap">
+<title>Preparing &kppp; for <acronym>PAP</acronym></title>
+
+<procedure>
+<step>
+<para>Make sure that the file <filename>/etc/ppp/options</filename> (and
+<filename>&tilde;/.ppprc</filename> if it exists) do <emphasis>not</emphasis>
+contain one of the following arguments:</para>
+
+<itemizedlist>
+<listitem>
+<para><option>+pap</option></para>
+</listitem>
+<listitem>
+<para><option>-pap</option></para>
+</listitem>
+<listitem>
+<para><option>papcrypt</option></para>
+</listitem>
+<listitem>
+<para><option>+chap</option></para>
+</listitem>
+<listitem>
+<para><option>+chap</option></para>
+</listitem>
+<listitem>
+<para><option>+ua</option></para>
+</listitem>
+<listitem>
+<para><option>remotename</option></para>
+</listitem>
+</itemizedlist>
+
+<para>It is very unlikely that any of these options are already there, but just
+to be sure, please check.</para>
+</step>
+<step>
+<para>Start &kppp;</para>
+</step>
+<step>
+<para>Click <guibutton>Setup</guibutton></para>
+</step>
+<step>
+<para>Choose the account you want to use <acronym>PAP</acronym> with and click
+<guibutton>Edit</guibutton></para>
+</step>
+<step>
+<para>Choose the <guilabel>Dial</guilabel> tab</para>
+</step>
+<step>
+<para>Select <acronym>PAP</acronym> in the <guilabel>Authentication</guilabel>
+drop down box.</para>
+</step>
+<step>
+<para>If you do not want to retype the password each time you dial in, select
+<guilabel>Store password</guilabel>. This will save the password to a file, so
+make sure that nobody else has access to your account.</para>
+</step>
+<step>
+<para>That's it. Close the dialogs, type in the username and password your
+<acronym>ISP</acronym> supplied, and click
+<guibutton>Connect</guibutton>.</para>
+</step>
+</procedure>
+
+
+</sect2>
+
+</sect1>
+
+<sect1 id="pap-and-chap-alternate-method">
+<title>An alternative method of using <acronym>PAP</acronym> and
+<acronym>CHAP</acronym> with &kppp;</title>
+
+<para>This section is based on an email from Keith Brown
+<email>kbrown@pdq.net</email> and explains how to make &kppp; work with a
+generic <acronym>PAP</acronym> or <acronym>CHAP</acronym> account. If your
+<acronym>ISP</acronym> just gave you a user id and a password for an account,
+you probably can skip this section, and the instructions in the previous one
+will be all you need.</para>
+
+<para><acronym>PAP</acronym> seems a lot more complicated at first glance than
+it really is. The server (the machine you are connecting to) basically tells
+the client (your machine) to authenticate using <acronym>PAP</acronym>. The
+client (<application>pppd</application>) looks in a specific file for an entry
+that contains a matching server name, and a client name for this connection, and
+then sends the password it finds there. That's about it!</para>
+
+<para>Now here's how to make that happen. I am assuming a
+<acronym>pppd</acronym> version of 2.2.x or better and a standard installation
+of configuration files under <filename
+class="directory">/etc/ppp</filename>.</para>
+
+<para>For the purposes of illustration, imagine that you have an internet
+account with <systemitem>glob.net</systemitem> with the username
+<systemitem>userbaz</systemitem> and the password
+<literal>foobar</literal></para>
+
+<para>First, you need to add all this to a file called
+<filename>/etc/ppp/pap-secrets</filename>. The format of an entry for our
+purposes is:</para>
+
+<screen><userinput>USERNAME SERVERNAME PASSWORD</userinput></screen>
+
+<para>So you would add the following line to
+<filename>/etc/ppp/pap-secrets</filename> and then save it :</para>
+
+<screen><userinput>userbaz glob foobar</userinput></screen>
+
+<note>
+<para>You can use any name for the server you wish, so long as you use the
+same name in the <application>pppd</application> arguments, as you'll see
+shortly. Here it's been shortened to <userinput>glob</userinput>, but this name
+is only used to locate the correct password.</para>
+</note>
+
+<para>Next you need to set up the connection in &kppp;. The basics are the same
+as any other connection, so we won't go into details here, except to say that
+you probably want to make sure that <filename>/etc/ppp/options</filename> is
+empty, and you don't want to create a login script either.</para>
+
+<para>In the &kppp; settings dialog, at the bottom of the
+<guilabel>Dial</guilabel> tab, is a <guibutton>pppd arguments</guibutton>
+button. This brings up an editing dialog. Here you can enter values that will
+be sent to <application>pppd</application> as command line arguments, and in the
+case of multiple value arguments, you need to enter each value as a separate
+entry in the listbox, in the correct order.</para>
+
+<para>You can put in any other arguments you want first. Then add the arguments
+that <application>pppd</application> uses to handle <acronym>PAP</acronym>
+authentication. In this example, we are going to add
+<userinput>user</userinput>, <userinput>userbaz</userinput>,
+<userinput>remotename</userinput> and <userinput>glob</userinput> in that
+order.</para>
+
+<para>The <option>user</option> tells the <application>pppd</application> what
+user name to look for in the <filename>pap-secrets</filename> file and then to
+send to the server. The remotename is used by <application>pppd</application>
+to match the entry in the <filename>pap-secrets</filename> file, so again, it
+can be anything you want so long as it is consistent with the entry in the
+<filename>pap-secrets</filename> file.</para>
+
+<para>That's all there is to it, and you should now be able to set up your own
+connection to a server with <acronym>PAP</acronym> authentication.
+<acronym>CHAP</acronym> is not much different. You can see the &Linux; Network
+Administrators Guide for a <filename>chap-secrets</filename> file format, and
+the <application>pppd</application> arguments used, and the rest should be
+simple.</para>
+
+</sect1>
+</chapter>
diff --git a/doc/kppp/costsgraphs.fig b/doc/kppp/costsgraphs.fig
new file mode 100644
index 00000000..0c1f5d66
--- /dev/null
+++ b/doc/kppp/costsgraphs.fig
@@ -0,0 +1,55 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+0
+1200 2
+2 2 0 1 0 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 900 900 900 900
+2 2 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+ 900 900 6000 900 6000 3900 900 3900 900 900
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+ 900 2700 2400 2700
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+ 900 3900 2400 2700
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+ 2400 2700 4800 900
+2 2 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+ 900 4500 6000 4500 6000 7500 900 7500 900 4500
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+ 900 6300 2700 4500
+2 2 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 5
+ 6900 900 12000 900 12000 3900 6900 3900 6900 900
+2 1 0 1 0 7 0 0 -1 4.000 0 0 -1 0 0 3
+ 6900 3000 9600 3000 10800 900
+2 1 1 1 0 7 0 0 -1 4.000 0 0 -1 0 0 2
+ 9600 3000 9075 3900
+2 1 0 1 0 7 0 0 -1 0.000 0 0 -1 0 1 2
+ 2 1 1.00 60.00 120.00
+ 9150 4050 9675 4275
+4 0 0 0 0 0 12 0.0000 4 180 4740 7125 5475 These graphs illustrate, why the new keyword 'flat_init_costs'\001
+4 0 0 0 0 0 12 0.0000 4 180 4830 7125 6150 minimum_cost and pre_connection alone. The situation depicted\001
+4 0 0 0 0 0 12 0.0000 4 180 165 750 525 a)\001
+4 0 0 0 0 0 12 0.0000 4 180 165 6975 600 c)\001
+4 0 0 0 0 0 12 0.0000 4 180 165 600 4350 b)\001
+4 0 0 0 0 0 12 0.0000 4 180 2055 8625 4500 Note: This is not the origin!\001
+4 0 0 0 0 0 12 0.0000 4 135 330 11400 4200 time\001
+4 0 0 0 0 0 12 0.0000 4 135 330 5325 4125 time\001
+4 0 0 0 0 0 12 0.0000 4 135 330 5475 7725 time\001
+4 0 0 0 0 0 12 0.0000 4 105 330 6300 975 cost\001
+4 0 0 0 0 0 12 0.0000 4 105 330 300 975 cost\001
+4 0 0 0 0 0 12 0.0000 4 105 330 300 4650 cost\001
+4 0 0 0 0 0 12 0.0000 4 135 1215 10650 7500 Bernd Wuebben\001
+4 0 0 0 0 0 12 0.0000 4 180 1395 10650 7725 wuebben@kde.org\001
+4 0 0 0 0 0 12 0.0000 4 180 4875 7125 6675 c) could also not be generated with 'rules' since rules depend on\001
+4 0 0 0 0 0 12 0.0000 4 180 4860 7125 6900 'absolut times 'such as '3:00pm', not on 'relative times' such as\001
+4 0 0 0 0 0 12 0.0000 4 180 2925 7125 7125 'the first 3 minutes' during connection.\001
+4 0 0 0 0 0 12 0.0000 4 180 1560 2550 675 minimum_cost graph\001
+4 0 0 0 0 0 12 0.0000 4 180 1620 2775 4275 per_connection graph\001
+4 0 0 0 0 0 12 0.0000 4 180 1560 8850 675 flat_init_costs graph\001
+4 0 0 0 0 0 12 0.0000 4 180 4920 7125 5700 was necessry. A cost graph such as the one show above, labeled\001
+4 0 0 0 0 0 12 0.0000 4 180 4425 7125 5925 'flat_init_cost graph' could not be generated with the rules\001
+4 0 0 0 0 0 12 0.0000 4 180 4455 7125 6375 in the graph c) above is the one encounted in France today.\001
diff --git a/doc/kppp/costsgraphs.png b/doc/kppp/costsgraphs.png
new file mode 100644
index 00000000..2e5d88fd
--- /dev/null
+++ b/doc/kppp/costsgraphs.png
Binary files differ
diff --git a/doc/kppp/dialog-setup.docbook b/doc/kppp/dialog-setup.docbook
new file mode 100644
index 00000000..ce76ac49
--- /dev/null
+++ b/doc/kppp/dialog-setup.docbook
@@ -0,0 +1,765 @@
+<chapter id="dialog-setup">
+<title>Setting up a connection with the dialogs</title>
+
+<para>Setting up a connection with the dialog based setup is not too much more
+difficult than using the wizard.</para>
+
+<para>You can reach the setup dialog the same way you did the wizard. Start
+&kppp; from your <guimenu>K</guimenu> menu, where you will find its entry in the
+<guisubmenu>Internet</guisubmenu> as <guimenuitem>Internet
+Dialer</guimenuitem>.</para>
+
+<para>The following dialog will appear:</para>
+
+<screenshot>
+<screeninfo>The &kppp; dialer startup screen</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-dialler-tab.png" format="PNG"/>
+</imageobject>
+<textobject><phrase>The &kppp; dialer startup screen</phrase>
+</textobject>
+<caption><para>The &kppp; dialer startup screen</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>It will probably not have any entries to begin with, and that's what we're
+about to do now.</para>
+
+<para>Click the <guibutton>Setup</guibutton> button to begin setting up a new
+Internet connection.</para>
+
+<para>This time, choose <guilabel>Dialog setup</guilabel> and you'll see the
+following Dialog appear:</para>
+
+<screenshot>
+<screeninfo>The <guilabel>New Account</guilabel> Dialog</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-dial-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>New Account</guilabel> Dialog</phrase>
+</textobject>
+<caption>
+<para>The <guilabel>New Account</guilabel> Dialog</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<!-- Make a screenshot of the actual new Account Dialog with no entries -->
+
+<para>The <guilabel>New Account</guilabel> dialog contains the following
+sections:</para>
+
+<itemizedlist>
+<listitem>
+<para><link linkend="account-dial"><guilabel>Dial</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link linkend="account-ip"><guilabel>IP</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link linkend="account-gateway"><guilabel>Gateway</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link linkend="account-dns"><guilabel>DNS</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link linkend="account-login-script"><guilabel>Login
+Script</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link linkend="account-execute"><guilabel>Execute</guilabel></link></para>
+</listitem>
+<listitem>
+<para><link
+linkend="account-accounting"><guilabel>Accounting</guilabel></link></para>
+</listitem>
+</itemizedlist>
+
+<para>You normally won't need to fill in all these, although each of them is
+described in the following sections.</para>
+
+<sect1 id="account-dial">
+<title>The <guilabel>Dial</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>Dial</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-dial-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>Dial</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>Dial</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>The <guilabel>Dial</guilabel> tab has the following options:</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Connection Name:</guilabel></term>
+<listitem>
+<para>You must give the account a name. This can be anything you like, but if
+you have more than one account, each name must be unique.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Phone Number:</guilabel></term>
+<listitem>
+<para>Specify the phone number to dial. You can use characters such as
+<quote>-</quote> to make the number more legible. If you concatenate a series
+of numbers separated by a colon (&eg;
+<userinput>1111111:2222222:3333333</userinput>, &kppp; will try these numbers one
+after the other whenever it receives a busy signal. You can use the
+<guibutton>Add</guibutton> button to add another number,
+<guibutton>Remove</guibutton> to remove a number from the list, and the
+<guiicon>up</guiicon> and <guiicon>down</guiicon> arrows to change the order of
+the list.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Authentication</guilabel></term>
+<listitem>
+<para>Choose the appropriate method of authentication that &kppp; should use to
+log into the server. Check with your provider for more information. Use of
+<acronym>PAP</acronym> and <acronym>CHAP</acronym> are described in the chapter
+<xref linkend="chap-and-pap"/>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Store password</guilabel></term>
+<listitem>
+<para>Check this option if you want &kppp; to remember your password between
+sessions.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guibutton>Customize pppd arguments...</guibutton></term>
+<listitem>
+<para>This will bring up the <application>pppd</application> arguments dialog.
+You can use this dialog to add any desired options that you want &kppp; to hand
+to <application>pppd</application>. See the <application>pppd</application> man
+page for a list of available options, but unless you know exactly what you are
+doing, you should probably restrain yourself from tinkering with these.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="account-ip">
+<title>The <guilabel>IP</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>IP</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-ip-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>IP</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>IP</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Dynamic IP Address</guilabel></term>
+<listitem>
+<para>Check this if your <acronym>ISP</acronym> uses dynamic
+<acronym>IP</acronym> address assignment. In this case, your
+<acronym>IP</acronym> address will change every time you establish a
+connection.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Static IP Address</guilabel></term>
+<listitem>
+<para>Check this if your <acronym>ISP</acronym> has given you a static
+<acronym>IP</acronym> address. In that case you will also need to fill in that
+address in the <guilabel>IP Address</guilabel> box, and any <guilabel>Subnet
+Mask</guilabel> if applicable. Ask your <acronym>ISP</acronym> if
+unsure. Dynamically assigned addresses are used in the huge majority if
+<acronym>ISP</acronym>'s and leaving this checked will in most cases be the
+right choice.</para>
+</listitem>
+</varlistentry>
+<varlistentry id="auto-configure-hostname">
+<term><guilabel>Auto-configure hostname from this IP</guilabel></term>
+<listitem>
+<para>Select this option if you want &kppp; to set the hostname and domain for
+your machine after a successful <acronym>ppp</acronym> connection.</para>
+<para>This is done by querying the defined Domain Name Server with the
+<acronym>IP</acronym> assigned for the <acronym>ppp</acronym> link.</para>
+<para>This option is useful for those stand-alone machines which want to use
+protocols such as talk, which require the hostname to be the same as your
+machine is known on the internet. It overrides the <guilabel>Domain
+Name</guilabel> option in the <guilabel>DNS</guilabel> section, and the machine
+defaults are restored to their original values when you close the
+<acronym>ppp</acronym> connection.</para>
+<para>This option is <emphasis>not</emphasis> useful if you just want to connect
+to the internet and surf, check mail, or chat. It has the side-effect of
+disallowing any new connections to your X server - in other words, you can't
+open any more <acronym>GUI</acronym> programs.</para>
+<para>Only turn this on if you are absolutely sure you need it.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="account-gateway">
+<title>The <guilabel>Gateway</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>Gateway</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-gateway-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>Gateway</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>Gateway</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Default Gateway</guilabel></term>
+<listitem>
+<para>Check this if you want <application>pppd</application> to use the default
+Gateway for your machine. This is the default.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Static Gateway</guilabel></term>
+<listitem>
+<para>Check this if you want to specify the Gateway to be used in place of the
+default.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Assign the Default Route to this Gateway</guilabel></term>
+<listitem>
+<para>You almost certainly will need this to be checked (the default).</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="account-dns">
+<title>The <guilabel>DNS</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>DNS</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-dns-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>DNS</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>DNS</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Domain Name:</guilabel></term>
+<listitem>
+<para>Specify the domain name for your machine. As with <acronym>DNS</acronym>
+addresses, it is restored to the original specified in
+<filename>/etc/resolv.conf</filename> when the connection goes down. If it is
+left blank, no changes are made to the domain name specified in
+<filename>/etc/resolv.conf</filename></para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Configuration:</guilabel></term>
+<listitem>
+<para>Choose between <guilabel>Automatic</guilabel> (the <acronym>ISP</acronym>
+will automatically issue you <acronym>DNS</acronym> server addresses when you
+connect) and <guilabel>Manual</guilabel>. If you choose manual, the
+<guilabel>DNS IP Address</guilabel> section is then enabled.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>DNS IP Address</guilabel></term>
+<listitem>
+<para>This section is only enabled if you chose <guilabel>Manual</guilabel> in
+the previous option. Add the Domain Name Servers assigned to you by your
+<acronym>ISP</acronym>. You must specify at least one Domain Name Server for
+your <acronym>OS</acronym> to be able to resolve human readable
+<acronym>IP</acronym> addresses such as
+<systemitem>ftp.kde.org</systemitem>. The <acronym>DNS</acronym> server
+addresses supplied must be in numeric form, &eg;
+<systemitem>128.231.231.233</systemitem>. These addresses will be added at
+runtime to <filename>/etc/resolv.conf</filename>.</para>
+<para>Choose the <guibutton>Add</guibutton> button to add each new
+<acronym>DNS</acronym> server address to the list box below. Choose
+<guibutton>Remove</guibutton> to remove an entry from the list.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Disable existing DNS Servers during Connection</guilabel></term>
+<listitem>
+<para>If you check this box, any <acronym>DNS</acronym> servers listed in
+<filename>/etc/resolv.conf</filename> will be disabled while the connection
+remains up.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="account-login-script">
+<title>The <guilabel>Login Script</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>Login Script</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-login-script-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>Login Script</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>Login Script</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Use this dialog to compose a dial in script for your
+<acronym>ISP</acronym> dialup connection. You can use the mini-terminal and the
+information supplied by your <acronym>ISP</acronym> to find out what sequence of
+actions needs to be executed.</para>
+
+<para>Choose an option from the drop down box on the left, and then add any
+parameters for that action in the edit box on the right. Use
+<guibutton>Add</guibutton> to add each entry to the <emphasis>bottom</emphasis>
+of the script, which is displayed in the lower part of the dialog. Use
+<guibutton>Insert</guibutton> to insert an entry anywhere in the script, and use
+<guibutton>Remove</guibutton> to delete a line from the script.</para>
+
+<para>The options available are:</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Expect</guilabel></term>
+<listitem>
+<para>&kppp; will wait for the specified string to be received.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Send</guilabel></term>
+<listitem>
+<para>&kppp; will send the specified string.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Scan</guilabel></term>
+<listitem>
+<para>&kppp; will scan the input stream for the specified string, and will
+store any character from the end of the string up to the next newline, in an
+internal buffer. Trailing and leading whitespace will be stripped off.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Save</guilabel></term>
+<listitem>
+<para>Permanently store the previously scanned string in the specified register.
+Currently the only valid register is <varname>password</varname>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Pause</guilabel></term>
+<listitem>
+<para>Pause for the specified number of seconds.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Hangup</guilabel></term>
+<listitem>
+<para>&kppp; will send the <command>hangup</command> to the modem.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Answer</guilabel></term>
+<listitem>
+<para>&kppp; will set the modem into answer mode.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Timeout</guilabel></term>
+<listitem>
+<para>Change the default timeout to the specified number of seconds dynamically
+during the script. You can change the timeout several times during script
+execution if necessary.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Prompt</guilabel></term>
+<listitem>
+<para>Prompt the &kppp; user to enter a string, given the specified string as a
+hint. The user will see what is typed. If the specified string includes the
+mark <userinput>##</userinput>, the mark will be replaced with the current
+content of the internal scan buffer, as previously stored with the
+<guilabel>scan</guilabel> command.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>PWPrompt</guilabel></term>
+<listitem>
+<para>Prompt the &kppp; user to enter a string, given the specified string as a
+hint. An asterisk will be printed for each character the user types.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>ID</guilabel></term>
+<listitem>
+<para>If the <guilabel>Login ID</guilabel> field on &kppp;'s main dialog is filled in,
+send that <acronym>ID</acronym>. If the <guilabel>Login ID</guilabel> field is not
+filled in, prompt the &kppp; user to enter an <acronym>ID</acronym>, given the
+specified string as a hint. The user will see what is typed. On a second pass,
+such as in a loop on a second iteration, or during callback authentication, the
+prompt will be displayed regardless of whether the <guilabel>Login ID</guilabel> field
+is filled in.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Password</guilabel></term>
+<listitem>
+<para>If the <guilabel>Password</guilabel> field on &kppp;'s main dialog is
+filled in, send that password. If the <guilabel>Password</guilabel> field is
+not filled in, prompt the &kppp; user to enter a password, with the specified
+string as a hint. An asterisk will be printed for each character typed. On a
+second pass, such as in a loop on a second iteration, or during callback
+authentication, the prompt will be displayed regardless of whether the
+<guilabel>Password</guilabel> field is filled in.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>LoopStart</guilabel></term>
+<listitem>
+<para>&kppp; will wait for the specified string to be received. It will save
+the string for use by <varname>LoopEnd</varname>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>LoopEnd</guilabel></term>
+<listitem>
+<para>&kppp; will wait for the specified string to be received to exit the loop.
+If the string given by the corresponding <varname>LoopStart</varname> is
+received first, it will trigger a jump to the line after
+<varname>LoopStart</varname>, enabling repetition of username/password style
+paired dialogs.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<sect2>
+<title>Example Scripts</title>
+
+<example>
+<title>A simple example login script</title>
+<para>Here is a simple example script I could use to connect to my
+<acronym>ISP</acronym></para>
+
+<screen>
+ Expect ID: <lineannotation># wait for ID:</lineannotation>
+ Send myid <lineannotation># you have to substitute myid with your id</lineannotation>
+ Expect word: <lineannotation># wait for 'password'</lineannotation>
+ Send 4u3fjkl <lineannotation># send my password '4u3fjkl'</lineannotation>
+ Expect granted <lineannotation># My ISP send 'Permission granted' on login success.</lineannotation>
+ Send ppp <lineannotation># This starts a ppp connection for
+ # me on the ISP side.</lineannotation>
+</screen>
+
+</example>
+
+<example>
+<title>A login script that prompts for ID and password, and has loops.</title>
+
+<para>Here is a script for the same account with an <acronym>ID</acronym> and
+password prompt. This script will prompt for <acronym>ID</acronym> and password
+each time, no matter what is typed into the <guilabel>Login ID</guilabel> and
+<guilabel>password</guilabel> fields on &kppp;'s main screen.</para>
+
+<para>This script also illustrates the use of the LoopStart/LoopEnd structure.
+If something goes wrong during the login procedure, for example, I mistype the
+password, my <acronym>ISP</acronym> will print an error message and restart the
+id/password loop by issuing the string <computeroutput>ID:</computeroutput>
+again. If the string <computeroutput>ID:</computeroutput> is caught before the
+LoopEnd keyword was parsed, &kppp; will start the script again, from the line
+after the LoopStart keyword.</para>
+
+<screen>
+ LoopStart ID: <lineannotation># wait for ID:</lineannotation>
+ Prompt Enter ID: <lineannotation># Prompt me for my ID and send it off.</lineannotation>
+ Expect word: <lineannotation># wait for 'password'</lineannotation>
+ PWPrompt Enter Password: <lineannotation># Prompt me for my password and send it off.</lineannotation>
+ LoopEnd granted <lineannotation># My ISP send 'Permission granted' on login success.</lineannotation>
+ Send ppp <lineannotation># This starts a ppp connection for me</lineannotation>
+</screen>
+</example>
+
+<example>
+<title>Prompts for information not filled in on the main dialog.</title>
+
+<para>Here is the script that I actually use to connect to my
+<acronym>ISP</acronym>. This script will prompt for <acronym>ID</acronym> and
+password only if I haven't filled in the respective fields on &kppp;'s main
+dialog.</para>
+
+<screen>
+ LoopStart ID: <lineannotation># wait for ID:</lineannotation>
+ ID Enter ID: <lineannotation># Prompt me for my ID and send it off.</lineannotation>
+ Expect word: <lineannotation># wait for 'password'</lineannotation>
+ Password Enter Password <lineannotation># Prompt me for my password and send it off.</lineannotation>
+ LoopEnd granted <lineannotation># My ISP send 'Permission granted' on login success.</lineannotation>
+ Send ppp <lineannotation># This starts a ppp connection for me</lineannotation>
+ <lineannotation># on the ISP side</lineannotation>
+</screen>
+
+</example>
+
+<example>
+<title>A script for an <acronym>ISP</acronym> using challenge/response
+authentication.</title>
+
+<para>Here is a script that I use to connect to an <acronym>ISP</acronym> which
+is using some sort of challenge/response authentication. Usually you got a
+hardware token (a smart card with a display and calculator like keypad) from the
+<acronym>ISP</acronym>. You have to know a password to use the token. After
+dialing in your <acronym>ISP</acronym> displays your challenge. You have to
+type in the challenge to your token and get a dynamic password as a
+response. Then you have to enter that password.</para>
+
+<screen>
+ LoopStart ID: <lineannotation># wait for ID:</lineannotation>
+ ID Enter ID: <lineannotation># Prompt me for my ID and send it off.</lineannotation>
+ Scan Challenge: <lineannotation># Scan for 'Challenge' and store everything behind up to the next newline.</lineannotation>
+ Expect Password: <lineannotation># wait for 'password'</lineannotation>
+ Prompt Your token is ## - Enter Password # Prompt me for my password and send it off.
+ LoopEnd granted <lineannotation># My ISP sends 'Permission granted' on login success.</lineannotation>
+ Send ppp <lineannotation># This starts a ppp connection for me
+ # on the ISP side</lineannotation>
+</screen>
+</example>
+
+<example>
+<title>Using Scan and Save in scripts</title>
+
+<para>The following log shows the login procedure of a fictitious
+<acronym>ISP</acronym> that provides a new password on each login. The new
+password has to be verified and recorded for the next session. </para>
+
+<screen> University of Lummerland
+
+ Login:mylogin
+ Password:
+ The password for your next session is: YLeLfkZb
+ Please record and enter it for verification.
+ Verification:YLeLfkZb
+
+ 1 = telnet
+ 2 = SLIP
+ 3 = PPP
+
+ Your choice:
+</screen>
+
+<para>&kppp; can be used to this cumbersome task for you, eliminating the risk
+of losing that little sheet of paper that holds your current password at the
+same time. The key part of the following script is the combination of Scan/Save
+keywords.</para>
+
+<screen>
+7 Expect Login: <lineannotation># wait for login prompt</lineannotation>
+ ID <lineannotation># send ID</lineannotation>
+ Expect Password: <lineannotation># wait for password prompt</lineannotation>
+ Password <lineannotation># send password</lineannotation>
+ Scan is: <lineannotation># wait for '... next session is:' and
+ # scan the preceding password</lineannotation>
+ Save password <lineannotation># save the new password for next login</lineannotation>
+ Expect Verification: <lineannotation># wait for 'Verification:'</lineannotation>
+ Password <lineannotation># send new password</lineannotation>
+ Expect choice: <lineannotation># wait for a prompt that let's you choose</lineannotation>
+ <lineannotation># between different options (telnet, SLIP, PPP)</lineannotation>
+ Send 3 <lineannotation># choose option 3, i.e. PPP</lineannotation>
+</screen>
+</example>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="account-execute">
+<title>The <guilabel>Execute</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>Execute</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-execute-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>Execute</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>Execute</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Here you can select commands to run at certain stages of the connection.
+These commands are run with your real user id, so you cannot run any commands
+here requiring root permissions, unless you are of course dialled in as root (a
+bad thing to do for many reasons!)</para>
+
+<para>Make sure to supply the whole path to the program, otherwise &kppp; may
+not be able to find it.</para>
+
+<para>You can add commands to be run at four distinct times during the
+connection process:</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Before Connect</guilabel></term>
+<listitem>
+<para>Run this command before the dialing is initiated, so it is already
+running when you connect to your <acronym>ISP</acronym>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Upon Connect</guilabel></term>
+<listitem>
+<para>Run this command only after a successful connection is
+made.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Before disconnect</guilabel></term>
+<listitem>
+<para>Run this command while still connected, before hanging up the
+modem.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Upon disconnect</guilabel></term>
+<listitem>
+<para>Run this command after the connection has been closed.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>You might for example want to run <application>leafnode</application> as
+soon as you have connected, or check your mail. You might want to make sure any
+mail in your queue is sent, before you close your connection down. You might
+want a <quote>clean-up</quote> script to tidy up logs and clear your cache after
+you have disconnected.</para>
+
+</sect1>
+
+<sect1 id="account-accounting">
+<title>The <guilabel>Accounting</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The Accounts <guilabel>Accounting</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-account-accounting-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The Accounts <guilabel>Accounting</guilabel> tab</phrase>
+</textobject>
+<caption><para>The Accounts <guilabel>Accounting</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Check the <guilabel>Enable Accounting</guilabel> box to enable or disable
+telephone cost accounting for this account.</para>
+
+<para>Select from the list the applicable rule for your telecoms
+provider.</para>
+
+<para>If you can't find one, you can write one yourself by copying the supplied
+template, which you will find in an <link
+linkend="appendix-accounting-template">appendix</link>.</para>
+
+<para>The final option on this page is <guilabel>Volume Accounting</guilabel>,
+described below.</para>
+
+<sect2>
+<title>Volume Accounting</title>
+
+<sect3>
+<title>What is volume accounting?</title>
+
+<para>Basically, it means to count the number of bytes transmitted to and from
+the Internet. &kppp; can count incoming bytes, outgoing bytes, or both. It's
+up to you what you want (or must) use.</para>
+
+</sect3>
+
+<sect3>
+<title>Why should I use volume accounting?</title>
+
+<para>Many Internet Service Providers bill their customers based on the number
+of bytes transferred. Even more commonly, <acronym>ISP</acronym>'s offer a flat
+rate up to some arbitrary transfer limit, and then charge more for every
+megabyte above this limit. &kppp; shows you your current volume and can help
+you keep your bills to the minimum. Of course, even if you're not billed based
+on volume, you can turn on volume accounting just to satisfy your own
+curiosity.</para>
+
+</sect3>
+
+<sect3>
+<title>What type of volume accounting should I select?</title>
+
+<para>That depends mainly on your provider. Many of them only count how many
+megabytes you download from the Internet,and ignore how much you send. In that
+case you should choose <guilabel>Bytes In</guilabel>. If you have to pay for
+both, you should choose <guilabel>Bytes In and Out</guilabel>. <guilabel>Bytes
+Out</guilabel> is really only here for completeness, as we're not aware of any
+providers using it as a billing basis. It might be useful to those of you
+running a web or &FTP; server at home though.</para>
+
+</sect3>
+
+<sect3>
+<title>Drawbacks</title>
+
+<para>Unfortunately, there is a drawback on volume accounting. &kppp; will only
+count the number of bytes, regardless of their origin. Many providers set their
+limit only for Internet access, and not for data on their own network. Some
+providers set different limits for data that is on their own network, in the
+same country, and coming from overseas. So, if you're doing not much
+websurfing, and getting most of your pages from your <acronym>ISP</acronym>'s
+own proxy cache, then your provider is probably not charging you for that data.
+&kppp; will not know these <acronym>IP</acronym> packets are coming from the
+proxy, and so it will count them. So if you this situation applies to you, or,
+as another example, your provider uses a caching news server such as
+<application>nntpcached</application>, then the volume reported by &kppp; may be
+higher than the amount you are going to be billed for. On the bright side, at
+least &kppp; will never underestimate your bills.</para>
+
+</sect3>
+
+</sect2>
+
+</sect1>
+
+</chapter>
diff --git a/doc/kppp/getting-online.docbook b/doc/kppp/getting-online.docbook
new file mode 100644
index 00000000..97d2ba66
--- /dev/null
+++ b/doc/kppp/getting-online.docbook
@@ -0,0 +1,52 @@
+<chapter id="getting-online">
+<title>Getting online the easy way</title>
+
+<sect1 id="things-to-prepare">
+<title>A few things you should have ready before you start</title>
+
+<para>If you have a fairly modern &Linux; distribution, you might find the rest
+of this document superfluous. &kppp; comes with a clever little wizard that in
+many cases can have you up and running with an internet connection in just a few
+minutes.</para>
+
+<para>Whether using the wizard or not, you should know the following information
+before you begin:</para>
+
+<itemizedlist>
+<listitem><para>Your <acronym>ISP</acronym> modem pool phone
+number.</para></listitem>
+<listitem><para>Your username and password for your
+<acronym>ISP</acronym>.</para></listitem>
+<listitem><para>Your <acronym>ISP</acronym>'s <acronym>DNS</acronym> servers
+(one is sufficient, but two is better).</para></listitem>
+</itemizedlist>
+
+<para>Other optional information you should find out to fully access your
+<acronym>ISP</acronym>'s services are:</para>
+
+<itemizedlist>
+<listitem><para>The incoming mail server address (often <systemitem
+class="systemname">pop.yourisp.com</systemitem> or <systemitem
+class="systemname">mail.yourisp.com</systemitem>)</para><para>Also find out if
+your <acronym>ISP</acronym> uses the POP3 protocol or IMAP.</para></listitem>
+<listitem><para>The outgoing (<acronym>SMTP</acronym>) mail server address (it
+could be the same as the incoming mail server, or it is often called something
+like <systemitem
+class="systemname">smtp.yourisp.com</systemitem>).</para></listitem>
+<listitem><para>The Usenet News (<acronym>NNTP</acronym>) server address (possibly
+<systemitem class="systemname">news.yourisp.com</systemitem> or <systemitem
+class="systemname">nntp.yourisp.com</systemitem>).</para></listitem>
+<listitem><para>Any proxy servers your <acronym>ISP</acronym> has set
+up.</para></listitem>
+</itemizedlist>
+
+<para>All this information is probably available on any paperwork you received
+from your <acronym>ISP</acronym> when you signed up with them, or you can find
+it out from your <acronym>ISP</acronym>'s support telephone line.</para>
+
+<para>Armed with the above, and a fairly recent default installation of &Linux;,
+you may well find that setting up an internet connection is as simple as running
+the &kppp; wizard.</para>
+</sect1>
+
+</chapter>
diff --git a/doc/kppp/global-settings.docbook b/doc/kppp/global-settings.docbook
new file mode 100644
index 00000000..a11cc8d4
--- /dev/null
+++ b/doc/kppp/global-settings.docbook
@@ -0,0 +1,385 @@
+<chapter id="global-settings">
+<title>Global &kppp; settings</title>
+
+<para>The changes made here affect all accounts you have set up in &kppp;</para>
+
+<sect1 id="global-accounts">
+<title>The <guilabel>Accounts</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The <guilabel>Accounts</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-config.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>Accounts</guilabel> tab</phrase>
+</textobject>
+<caption><para>The <guilabel>Accounts</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>In this dialog, you can manage the accounts themselves. The names of the
+accounts appear in a list on the left of the dialog.</para>
+
+<para>To delete an account, select the <guibutton>Delete</guibutton> button.
+You will be asked to confirm before the account is finally deleted.</para>
+
+<para>You can make a copy of an account with the <guibutton>Copy</guibutton>
+button. You could use this for example, to separate different users in the
+family, although that would normally be better done by having them be different
+users in the OS as well! Or perhaps you just have more than one account with
+the same <acronym>ISP</acronym> and wish to use both of them.</para>
+
+<para>Choosing <guibutton>Edit...</guibutton> will take you to the dialog
+described in <link linkend="dialog-setup">Dialog Setup</link>, but with the
+selected accounts details.</para>
+
+<para>Choosing <guibutton>New...</guibutton> will offer you the choice between
+the <link linkend="wizard">Wizard</link> or the <link
+linkend="dialog-setup">Dialog Setup</link> already described.</para>
+
+<para>If you select an account, and you have turned on <link
+linkend="account-accounting">accounting</link> then the accumulated information
+for that account will appear in the two panels labelled <guilabel>Phone
+Costs:</guilabel> and <guilabel>Volume:</guilabel> respectively.</para>
+
+<para>To the left of the accounting display, are two buttons:
+<guibutton>Reset...</guibutton> and <guibutton>View Logs</guibutton>.</para>
+
+<para>Pressing <guibutton>Reset...</guibutton> will reset the <guilabel>Phone
+Costs:</guilabel> and <guilabel>Volume:</guilabel> information to 0. You would
+typically want to do this once a month or quarter, when you have received your
+phone bill and reconciled the telephone costs. You can reset either
+independently, and are offered the choice of which item you want to reset, when
+you press the <guibutton>Reset</guibutton> button.</para>
+
+<para>Pressing <guibutton>View Logs</guibutton> will open another window, where
+a log of all the calls made with &kppp; will be displayed. If you have kept
+logs, you can move forward and backward, in monthly steps. This might be useful
+if you have received an abnormally large phone bill and are investigating
+why!</para>
+
+</sect1>
+
+<sect1 id="global-device">
+<title>The <guilabel>Device</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The <guilabel>Device</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-device-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>Device</guilabel> tab</phrase>
+</textobject>
+<caption><para>The <guilabel>Device</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Here you can select and configure your modem.</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Modem Device</guilabel></term>
+<listitem>
+<para>Choose the device appropriate for your hardware.</para>
+<variablelist>
+<varlistentry>
+<term><filename>/dev/ttys0</filename></term>
+<listitem>
+<para>DOS or &Windows; users will know this as COM1, while COM2 is
+<filename>/dev/ttys1</filename> and so on. These devices are
+the ones normally used on &Linux; systems.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><filename>/dev/cua0</filename></term>
+<listitem>
+<para>The first serial line (COM1). COM2 is usually
+<filename>/dev/cua1</filename> and so on. These devices are commonly used on
+BSD systems, namely FreeBSD, NetBSD and OpenBSD. Older &Linux; systems may also
+have these, although on &Linux; they were renamed some time ago to <filename>/dev/ttyS<replaceable>x</replaceable></filename>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><filename>/dev/ttyI0</filename></term>
+<listitem>
+<para>On &Linux; these belong to internal <acronym>ISDN</acronym> cards. These
+devices emulate a common Hayes compatible modem.
+<filename>/dev/ttyI0</filename> is for the first,
+<filename>/dev/ttyI1</filename> is for the second
+<acronym>ISDN</acronym> card and so on. These devices are only available in the
+&Linux; version.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><filename class="symlink">/dev/modem</filename></term>
+<listitem>
+<para>Many &Linux; distributions make a symbolic link from the real modem device
+to <filename class="symlink">/dev/modem</filename>. <emphasis>You should avoid
+using this one.</emphasis>. Use the real device that it is pointing to
+instead.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Flow Control</guilabel></term>
+<listitem>
+<para>Select from Hardware (CRTSCTS), Software (XON/XOFF) and no flow control.
+The recommended setting is Hardware flow control.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Line Termination</guilabel></term>
+<listitem>
+<para>Choose the correct <quote>Enter</quote> character sequence for your
+modem. Most modems will use <quote>CR/LF</quote>, however some modems need a
+different setting. If you experience trouble while running a login script, play
+with this parameter.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Connection Speed</guilabel></term>
+<listitem><para>Choose from the list of connection speeds supported by your
+serial port. Note that the serial port supports much higher speeds than your
+modem in most cases. You should probably start with the highest number
+available, and only reduce it if you have connection problems.
+</para></listitem>
+</varlistentry>
+<varlistentry>
+<term id="lock-files"><guilabel>Use Lock File</guilabel></term>
+<listitem>
+<para>Activate this option if you want &kppp; to create a lockfile. Under
+&Linux; the folder for such a file will be <filename
+class="directory">/var/lock</filename>. Programs such as
+<application>mgetty</application> depend on the existence of such lock files,
+and &kppp; will not work with <application>mgetty</application> if the lock file
+is not set. Make sure that you don't use the option <option>lock</option> for
+<application>pppd</application> if you want &kppp; to lock the modem, since the
+<application>pppd</application> option <option>lock</option> will induce
+<application>pppd</application> to try to lock the modem device. Since &kppp;
+will have already locked the device, <application>pppd</application> will fail,
+and &kppp; will display the error <errorname>pppd died
+unexpectedly</errorname>.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Modem Timeout</guilabel></term>
+<listitem>
+<para>This is the time in seconds that &kppp; will wait for the
+<returnvalue>CONNECT</returnvalue> response from your modem. A setting of about
+30 seconds should be sufficient for most purposes.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="global-modem">
+<title>The <guilabel>Modem</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The <guilabel>Modem</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-modem-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>Modem</guilabel> tab</phrase>
+</textobject>
+<caption><para>The <guilabel>Modem</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>Busy Wait</guilabel></term>
+<listitem>
+<para>This is the length of time the modem should wait before redialing, after
+it has received a busy signal. Note there are requirements by telecom providers
+in some countries, which ask you to not set this too low.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Modem volume</guilabel></term>
+<listitem>
+<para>Use the slider to set the modem volume. Left is low volume, center is
+medium volume, and right is high volume. On some modems, low volume is the same
+as turning the volume off, and on other modems, medium and high are effectively
+the same thing.</para>
+</listitem>
+</varlistentry>
+<varlistentry id="modem-commands">
+<term><guibutton>Modem Commands</guibutton></term>
+<listitem>
+<para>In this dialog you can fill in any particular commands appropriate for
+your modem. If you own a Hayes compatible modem, you most likely won't need to
+change any of the defaults, but you are encouraged to read the <link
+linkend="appendix-hayes-commands">Hayes Commands</link> Appendix in this help file. The
+information supplied there can be very helpful in cases where you experience
+trouble setting up a stable connection with your <acronym>ISP</acronym>'s
+modems. In particular the two settings for <guilabel>Pre-Init Delay</guilabel>
+and for <guilabel>Post-Init Delay</guilabel> if you are experiencing modem
+lockups. These settings make &kppp; pause a little just before and just after
+sending the initialization string to your modem. The <guilabel>Pre-Init
+Delay</guilabel> will by default also send a CR, unless you have set it the
+delay interval to 0.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guibutton>Query Modem</guibutton></term>
+<listitem>
+<para>Pushing this button will make &kppp; ask your modem to identify itself.
+On success, your modems response will be displayed in a dialog. This may or may
+not prove to be informative, depending on your modem.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guibutton>Terminal</guibutton></term>
+<listitem>
+<para>Pushing the <guibutton>Terminal</guibutton> button will bring up a mini
+terminal. You can use the mini terminal to test your modem and to experiment
+with the negotiation protocol for initializing a ppp connection with your
+<acronym>ISP</acronym>. You no longer need a terminal program such as
+<application>minicom</application> or <application>Seyon</application>.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="global-graph">
+<title>The <guilabel>Graph</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The <guilabel>Graph</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-graph-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>Graph</guilabel> tab</phrase>
+</textobject>
+<caption><para>The <guilabel>graph</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Here you can set the colors used by the &kppp; graph. You can set
+different colors for <guilabel>Background color</guilabel>, <guilabel>Text
+color</guilabel>, <guilabel>Input bytes color</guilabel> and <guilabel>Output
+bytes color</guilabel>.</para>
+
+</sect1>
+
+<sect1 id="global-misc">
+<title>The <guilabel>Misc</guilabel> tab</title>
+
+<screenshot>
+<screeninfo>The <guilabel>Misc.</guilabel> tab</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-misc-tab.png" format="PNG"/>
+</imageobject>
+<textobject>
+<phrase>The <guilabel>Misc.</guilabel> tab</phrase>
+</textobject>
+<caption><para>The <guilabel>Misc.</guilabel> tab</para>
+</caption>
+</mediaobject>
+</screenshot>
+
+<para>Here are some options that don't really fit in with other sections, but
+can be very useful nonetheless.</para>
+
+<variablelist>
+<varlistentry>
+<term><guilabel>pppd Version</guilabel></term>
+<listitem>
+<para>The version number of the pppd daemon on your system.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>pppd Timeout</guilabel></term>
+<listitem>
+<para>&kppp; will wait this amount of time after running the script and starting
+<application>pppd</application> for <application>pppd</application> to establish
+a valid <acronym>ppp</acronym> link before giving up and killing
+<application>pppd</application></para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Dock into Panel on Connect</guilabel></term>
+<listitem>
+
+<para>If this option is chosen, &kppp; will dock into the panel where it will be
+symbolized by a small animated icon. Use the <mousebutton>left</mousebutton>
+mouse button on this icon to restore &kppp;'s window. The
+<mousebutton>right</mousebutton> mouse button will open a popup menu that offers
+to restore the window, show transfer statistics, or close the connection. This
+option overrides <guilabel>Minimize Window on Connect</guilabel>.</para>
+
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Automatic Redial on Disconnect</guilabel></term>
+<listitem>
+<para>Selectintg this will have &kppp; try to reconnect if you are
+disconnected.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Show Clock on Caption</guilabel></term>
+<listitem>
+<para>This will have &kppp; display the time connected on the caption of the
+&kppp; window, while you are online.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Disconnect on X-server shutdown</guilabel></term>
+<listitem>
+<para>Checking this will cause &kppp; to terminate the <acronym>ppp</acronym>
+link, disconnect the modem, and terminate accounting in an orderly fashion, when
+the X-server shuts down. This is useful if you are prone to forgetting you are
+online, when you shut down the X-server, or if you simply don't want to worry
+about manually disconnecting your session. If you don't want &kppp; to hang up
+the modem on X-server exit, you should leave this checkbox empty. Beware that
+if you have accounting enabled, and you leave this option turned off, you will
+have an unterminated accounting entry in your logs, from each time the X-server
+exits and &kppp; terminates.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Quit on Disconnect</guilabel></term>
+<listitem>
+<para>If enabled, &kppp; will exit when you disconnect from the internet. If disabled, &kppp; will stay open after disconnection.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><guilabel>Minimize Window on Connect</guilabel></term>
+<listitem>
+<para>If this option is chosen, &kppp; will be minimized after a connection is
+established. The elapsed connection time will be shown in the taskbar.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+</sect1>
+
+<sect1 id="global-about">
+<title>The <guilabel>About</guilabel> tab</title>
+
+<para>The <guilabel>About</guilabel> tab shows version, license, and author
+information about &kppp;.</para>
+
+</sect1>
+
+</chapter>
diff --git a/doc/kppp/hayes.docbook b/doc/kppp/hayes.docbook
new file mode 100644
index 00000000..a8f6db7f
--- /dev/null
+++ b/doc/kppp/hayes.docbook
@@ -0,0 +1,927 @@
+<appendix id="appendix-hayes-commands">
+<title>The Hayes Modem Command Set</title>
+
+<para>Here is a description of the Hayes Command Set. Most modems follow this
+command set to large extent. If you lost your modem manual or never had one in
+the first place, this reference might come in handy. I for instance finally found
+out how to turn my modems speaker off: <command>ATM0</command> -- Finally:
+Silence!</para>
+
+<para>The modem initialization string consists of a series of commands. It
+prepares the modem for communications, setting such features as dialing mode,
+waits, detection of the busy signal and many other settings. Newer modem
+communications programs reset the initializations string for you according to
+which menu options you select, which features you enable, &etc;.</para>
+
+<para>For many years Hayes modems have been the standard. As the field of modem
+manufactures has grown, most have adhered at least loosely to the Hayes
+standard. The following is a partial list of the Hayes command set. (called the
+<quote>AT</quote> commands). The Hayes Command Set can be divided into four
+groups:</para>
+
+<variablelist>
+<varlistentry>
+<term>Basic Command Set</term>
+<listitem><para>A capital character followed by a digit. For example,
+<command>M1</command>.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term>Extended Command Set</term>
+<listitem><para>An <quote>&amp;</quote> (ampersand) and a capital character
+followed by a digit. This is an extension of the basic command set. For example,
+<command>&amp;M1</command>. Note that <command>M1</command> is different from
+<command>&amp;M1</command>.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term>Proprietary Command Set</term>
+<listitem><para>Usually started by either a backslash (<quote>\</quote>), or a
+percent sign (<quote>&percnt;</quote>), these commands vary widely among modem
+manufacturers. For that reason, only a few of these commands are listed
+below.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term>Register Commands</term>
+<listitem><para><command>S<replaceable>r</replaceable>=<replaceable>n</replaceable></command>
+where <replaceable>r</replaceable> is the number of the register to be changed,
+and <replaceable>n</replaceable> is the new value that is
+assigned.</para>
+
+<para>A <quote>register</quote> is computerese for a specific physical location
+in memory. Modems have small amounts of memory onboard. This fourth set of
+commands is used to enter values in a particular register (memory location). The
+register will be storing a particular <quote>variable</quote> (alpha-numeric
+information) which is utilized by the modem and communication software. For
+example, <command>S7=60</command> instructs your computer to <quote>Set register
+#7 to the value 60</quote>.</para></listitem>
+</varlistentry>
+</variablelist>
+
+<note><para>Although most commands are defined by a letter-number combination
+(<command>L0</command>, <command>L1</command> &etc;), the user of a zero is
+optional. In this example, <command>L0</command> is the same as a plain
+<command>L</command>. Keep this in mind when reading the table
+below!</para></note>
+
+<para>Here are some of the most important characters that may appear in the
+modem initialization string. These characters normally should not be
+changed.</para>
+
+<variablelist>
+<varlistentry>
+<term><command>AT</command></term>
+<listitem><para>Tells the modem that modem commands follow. This must begin
+each line of commands.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>Z</command></term>
+<listitem><para>Resets the modem to it's default state</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>,</command> (a comma)</term>
+<listitem><para>makes your software pause for a second. You can use more than
+one <command>,</command> in a row. For example, <command>,,,,</command> tells
+the software to pause four seconds. (The duration of the pause is governed by
+the setting of register <varname>S8</varname>.</para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>^M</command></term>
+<listitem><para>Sends the terminating Carriage Return character to the modem.
+This is a control code that most communication software translates as
+<quote>Carriage Return</quote></para></listitem></varlistentry>
+</variablelist>
+
+<sect1 id="hayes-basic-commands">
+<title>The Basic Hayes Command Set</title>
+
+<para>In alphabetical order:</para>
+
+ <table>
+ <title>Basic Hayes Command Set</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Command</entry>
+ <entry>Description</entry>
+ <entry>Comments</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><command>A0</command> or <command>A</command></entry>
+ <entry>Answer incoming call</entry>
+ <entry></entry>
+ </row>
+ <row>
+ <entry><command>A/</command></entry>
+ <entry>Repeat last command</entry>
+ <entry>Don't preface with <command>AT</command>. Enter usually
+ aborts.</entry>
+ </row>
+ <row>
+ <entry><command>B0</command> or <command>B</command></entry>
+ <entry>Call negotiation</entry>
+ <entry>V32 Mode/CCITT Answer Seq.</entry>
+ </row>
+ <row>
+ <entry><command>B1</command></entry>
+ <entry>Call negotiation</entry>
+ <entry>Bell 212A Answer Seq.</entry>
+ </row>
+ <row>
+ <entry><command>B2</command></entry>
+ <entry>Call negotiation</entry>
+ <entry>Verbose/Quiet On Answer</entry>
+ </row>
+ <row>
+ <entry><command>D</command></entry>
+ <entry>Dial</entry>
+ <entry><para>Dial the following number and then handshake in originate
+ mode.</para><variablelist>
+ <varlistentry>
+ <term><command>P</command></term>
+ <listitem><para>Pulse Dial</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>T</command></term>
+ <listitem><para>Touch Tone Dial</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>W</command></term>
+ <listitem><para>Wait for the second dial tone</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>,</command></term>
+ <listitem><para>Pause for the time specified in register
+ <varname>S8</varname> (usually 2 seconds</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>;</command></term>
+ <listitem><para>Remain in command mode after dialing.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>!</command></term>
+ <listitem><para>Flash switch-hook (Hang up for a half second, as in
+ transferring a call.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><command>L</command></term>
+ <listitem><para>Dial last number</para></listitem>
+ </varlistentry>
+ </variablelist></entry>
+ </row>
+ <row>
+ <entry><command>E0</command> or <command>E</command></entry>
+ <entry>No Echo</entry>
+ <entry>Will not echo commands to the computer</entry>
+ </row>
+ <row>
+ <entry><command>E1</command></entry>
+ <entry>Echo</entry>
+ <entry>Will echo commands to the computer (so one can see what one
+ types)</entry>
+ </row>
+ <row>
+ <entry><command>H0</command></entry>
+ <entry>Hook Status</entry>
+ <entry>On hook - Hang up</entry>
+ </row>
+ <row>
+ <entry><command>H1</command></entry>
+ <entry>Hook status</entry>
+ <entry>Off hook - phone picked up</entry>
+ </row>
+ <row>
+ <entry><command>I0</command> or <command>I</command></entry>
+ <entry>Inquiry, Information, or Interrogation</entry>
+ <entry>This command is very model specific. <command>I0</command>
+ usually returns a number or code, while higher numbers often provide much
+ more useful information.</entry>
+ </row>
+ <row>
+ <entry><command>L0</command> or <command>L</command></entry>
+ <entry>Speaker Loudness. Modems with volume control knobs will not have
+ these options.</entry>
+ <entry>Off or low volume</entry>
+ </row>
+ <row>
+ <entry><command>L1</command></entry>
+ <entry></entry>
+ <entry>Low Volume</entry>
+ </row>
+ <row>
+ <entry><command>L2</command></entry>
+ <entry></entry>
+ <entry>Medium Volume</entry>
+ </row>
+ <row>
+ <entry><command>L3</command></entry>
+ <entry></entry>
+ <entry>Loud or High Volume</entry>
+ </row>
+ <row>
+ <entry><command>M0</command> or <command>M</command></entry>
+ <entry>Speaker off</entry>
+ <entry><command>M3</command> is also common, but different on many
+ brands</entry>
+ </row>
+ <row>
+ <entry><command>M1</command></entry>
+ <entry></entry>
+ <entry>Speaker on until remote carrier detected (&ie; until the other
+ modem is heard)</entry>
+ </row>
+ <row>
+ <entry><command>M2</command></entry>
+ <entry></entry>
+ <entry>Speaker is always on (data sounds are heard after CONNECT)</entry>
+ </row>
+ <row>
+ <entry><command>N0</command> or <command>N</command></entry>
+ <entry>Handshake Speed</entry>
+ <entry>Handshake only at speed in <link linkend="hayes-s37"><varname>S37</varname></link></entry>
+ </row>
+ <row>
+ <entry><command>N1</command></entry>
+ <entry></entry>
+ <entry>Handshake at highest speed larger than <link linkend="hayes-s37"><varname>S37</varname></link></entry>
+ </row>
+ <row>
+ <entry><command>O0</command> or <command>O</command></entry>
+ <entry>Return Online</entry>
+ <entry>See also <link linkend="hayes-basic-x1"><command>X1</command></link> as dial tone
+ detection may be active.</entry>
+ </row>
+ <row>
+ <entry><command>O1</command></entry>
+ <entry></entry>
+ <entry>Return Online after an equalizer retrain sequence</entry>
+ </row>
+ <row>
+ <entry><command>Q0</command> or <command>Q1</command></entry>
+ <entry>Quiet Mode</entry>
+ <entry>Off - Displays result codes, user sees command responses (&eg;
+ <computeroutput>OK</computeroutput>)</entry>
+ </row>
+ <row>
+ <entry><command>Q1</command></entry>
+ <entry>Quiet Mode</entry>
+ <entry>On - Result codes are suppressed, user does not see
+ responses.</entry>
+ </row>
+ <row>
+ <entry><command>S<replaceable>n</replaceable>?</command></entry>
+ <entry></entry>
+ <entry>Query the contents of S-register
+ <replaceable>n</replaceable></entry>
+ </row>
+ <row>
+ <entry><command>S<replaceable>n</replaceable>=<replaceable>r</replaceable></command></entry>
+ <entry>Store</entry>
+ <entry>Store the value of <replaceable>r</replaceable> in S-register
+ <replaceable>n</replaceable></entry>
+ </row>
+ <row>
+ <entry><command>V0</command> or <command>V</command></entry>
+ <entry>Verbose</entry>
+ <entry>Numeric result codes</entry>
+ </row>
+ <row>
+ <entry><command>V1</command></entry>
+ <entry></entry>
+ <entry>English result codes (&eg;
+ <computeroutput>CONNECT</computeroutput>,
+ <computeroutput>BUSY</computeroutput>, <computeroutput>NO
+ CARRIER</computeroutput> &etc;)</entry>
+ </row>
+ <row>
+ <entry><command>X0</command> or <command>X</command></entry>
+ <entry>Smartmodem</entry>
+ <entry>Hayes Smartmodem 300 compatible result codes</entry>
+ </row>
+ <row>
+ <entry><anchor id="hayes-basic-x1"/><command>X1</command></entry>
+ <entry></entry>
+ <entry>Usually adds connection speed to basic result codes (&eg;
+ <computeroutput>CONNECT 1200</computeroutput></entry>
+ </row>
+ <row>
+ <entry><command>X2</command></entry>
+ <entry></entry>
+ <entry>Usually adds dial tone detection (preventing blind dial, and
+ sometimes preventing <command>AT0</command>)</entry>
+ </row>
+ <row>
+ <entry><command>X3</command></entry>
+ <entry></entry>
+ <entry>Usually adds busy signal detection</entry>
+ </row>
+ <row>
+ <entry><command>X4</command></entry>
+ <entry></entry>
+ <entry>Usually adds both busy signal and dial tone detection</entry>
+ </row>
+ <row>
+ <entry><command>Z0</command> or <command>Z</command></entry>
+ <entry>Reset</entry>
+ <entry>Reset modem to stored configuration. Use <command>Z0</command>,
+ <command>Z1</command> &etc; for multiple profiles. This is the same as
+ <command>&amp;F</command> for factory default on modems without
+ <acronym>NVRAM</acronym> (non voltaile memory)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
+ <sect1 id="hayes-extended-commands">
+ <title>The Extended Hayes Command Set</title><subtitle>Ampersand Commands</subtitle>
+
+ <table>
+ <title>The Extended Hayes Command Set</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Command</entry>
+ <entry>Description</entry>
+ <entry>Comments</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><command>&amp;B0</command> or <command>&amp;B</command></entry>
+ <entry>Retrain Parameters</entry>
+ <entry>Disable auto retrain function</entry>
+ </row>
+ <row>
+ <entry><command>&amp;B1</command></entry>
+ <entry>Retrain Parameters</entry>
+ <entry>Enable auto retrain function</entry>
+ </row>
+ <row>
+ <entry><command>&amp;B2</command></entry>
+ <entry>Retrain Parameters</entry>
+ <entry>Enable auto retrain, but disconnect if no line improvement over
+ the period dictated by <link
+ linkend="hayes-s7"><varname>S7</varname></link></entry>
+</row>
+ <row>
+ <entry><command>&amp;C0</command> or <command>&amp;C1</command></entry>
+ <entry>Carrier detect</entry>
+ <entry>Signal always on</entry>
+ </row>
+ <row>
+ <entry><command>&amp;C1</command></entry>
+ <entry>Carrier detect</entry>
+ <entry>Indicates remote carrier (usual preferred default)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;D0</command> or <command>&amp;D</command></entry>
+ <entry>Data Terminal Ready (<acronym>DTR</acronym></entry>
+ <entry>Signal ignored (This is modem specific, you must see your manual
+ for information on this one!)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;D1</command></entry>
+ <entry>Data Terminal Ready (<acronym>DTR</acronym></entry>
+ <entry>If <acronym>DTR</acronym> goes from On to Off the modem goes into
+ command mode (Some modems only)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;D2</command></entry>
+ <entry>Data Terminal Ready (<acronym>DTR</acronym></entry>
+ <entry>Some modems hang up on <acronym>DTR</acronym> On to Off transition
+ (This is the usual preferred default)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;D3</command></entry>
+ <entry>Data Terminal Ready (<acronym>DTR</acronym></entry>
+ <entry>Hang up, reset the modem, and return to command mode upon
+ <acronym>DTR</acronym></entry>
+ </row>
+ <row>
+ <entry><command>&amp;F0</command> or <command>&amp;F</command></entry>
+ <entry>Factory defaults</entry>
+ <entry><para>Generic Hayes-compatible defaults. </para><para>This is
+ usually a good thing to use in your init string, since the
+ <command>&amp;F1</command>-<command>&amp;F3</command> settings can vary
+ among modems, and they may actually be the cause of connection
+ problems. (Since you never know exactly what Brand X's
+ <command>&amp;F2</command> really changes.</para><para>On the other hand,
+ it pays to try out the other options below; many people's problems can be
+ solved by replacing a complicated init string with a simple
+ <command>&amp;F2</command> or the like. However, if you're building an
+ init string, it's best to start with a simple <command>&amp;F</command>,
+ and not use the <quote>customized</quote> form of
+ defaults.</para></entry>
+ </row>
+ <row>
+ <entry><command>&amp;F1</command></entry>
+ <entry>Factory Defaults</entry>
+ <entry>Factory Defaults tailored to an IBM <acronym>PC</acronym>
+ compatible user</entry>
+ </row>
+ <row>
+ <entry><command>&amp;F2</command></entry>
+ <entry>Factory Defaults</entry>
+ <entry>Factory defaults for a Mac w/software handshaking</entry>
+ </row>
+ <row>
+ <entry><command>&amp;F3</command></entry>
+ <entry>Factory Defaults</entry>
+ <entry>Factory defaults for a Mac w/hardware handshaking</entry>
+ </row>
+ <row>
+ <entry><command>&amp;G0</command> or <command>&amp;G</command></entry>
+ <entry>Guard tones</entry>
+ <entry>Disable guard tones</entry>
+ </row>
+ <row>
+ <entry><command>&amp;K0</command> or <command>&amp;K</command></entry>
+ <entry>Local flow control</entry>
+ <entry>Disable local flow control</entry>
+ </row>
+ <row>
+ <entry><command>&amp;K1</command></entry>
+ <entry>Local flow control</entry>
+ <entry>Enable RTS/CTS hardware local flow control</entry>
+ </row>
+ <row>
+ <entry><command>&amp;K2</command></entry>
+ <entry>Local flow control</entry>
+ <entry>Enable XON/XOFF software local flow control</entry>
+ </row>
+ <row>
+ <entry><command>&amp;K3</command></entry>
+ <entry>Local flow control</entry>
+ <entry>Enable RTS/CTS hardware local flow control</entry>
+ </row>
+ <row>
+ <entry><command>&amp;K4</command></entry>
+ <entry>Local flow control</entry>
+ <entry>Enable XON/XOFF software local flow control</entry>
+ </row>
+ <row>
+ <entry><command>&amp;L0</command> or <command>&amp;L</command></entry>
+ <entry>Dial mode</entry>
+ <entry>Select dial-up mode</entry>
+ </row>
+ <row>
+ <entry><command>&amp;M0</command> or <command>&amp;M</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Select asynchronous non-<acronym>EC</acronym> mode (the same as
+ <command>&amp;Q0</command>)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;P0</command> or <command>&amp;P</command></entry>
+ <entry>Pulse dialing ratio</entry>
+ <entry>U.S./Canada pulse dialing 39% make / 61% break ratio</entry>
+ </row>
+ <row>
+ <entry><command>&amp;P1</command></entry>
+ <entry>Pulse dialing ratio</entry>
+ <entry>U.K./Hong Kong pulse dialing 33% make / 67% break ratio</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Q0</command> or <command>&amp;Q</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Asynchronous non-<acronym>EC</acronym> more. No data
+ buffering. <acronym>ASB</acronym> disabled.</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Q5</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Select V.42 <acronym>EC</acronym> operation (requires flow
+ control)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Q6</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Asynchronous mode with <acronym>ASB</acronym> (requires flow
+ control)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Q8</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Select alternate <acronym>EC</acronym> protocol
+ (<acronym>MNP</acronym>)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Q9</command></entry>
+ <entry>Error control mode</entry>
+ <entry>Conditional data compression: V.42bis = yes, MNP5 = no.</entry>
+ </row>
+ <row>
+ <entry><command>&amp;S0</command> or <command>&amp;S</command></entry>
+ <entry><acronym>DSR</acronym> action select</entry>
+ <entry>Always on (default)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;S1</command></entry>
+ <entry><acronym>DSR</acronym> action select</entry>
+ <entry>Follows <acronym>EIA</acronym> specification (Active following
+ carrier tone, and until carrier is lost.)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;T0</command> or <command>&amp;T</command></entry>
+ <entry>Self test</entry>
+ <entry>Model specific self test on some modems</entry>
+ </row>
+ <row>
+ <entry><command>&amp;U0</command> or <command>&amp;U</command></entry>
+ <entry>Trellis code modulation</entry>
+ <entry>Enable V.32 <acronym>TCM</acronym></entry>
+ </row>
+ <row>
+ <entry><command>&amp;U1</command></entry>
+ <entry>Trellis code modulation</entry>
+ <entry>Disable V.32 <acronym>TCM</acronym></entry>
+ </row>
+ <row>
+ <entry><command>&amp;V0</command> or <command>&amp;V1</command></entry>
+ <entry>View active</entry>
+ <entry>(and often stored) configuration profile settings (or
+ <command>ATI4</command></entry>
+ </row>
+ <row>
+ <entry><command>&amp;W0</command> or <command>&amp;W</command></entry>
+ <entry>Store profile</entry>
+ <entry>In <acronym>NVRAM</acronym> (<command>&amp;W0</command>,
+ <command>&amp;W1</command> etc. for multiple profiles) Some settings
+ cannot be stored. These often don't show on <command>&amp;V</command> or
+ <command>ATI4</command></entry>
+ </row>
+ <row>
+ <entry><command>&amp;Y0</command> or <command>&amp;Y</command></entry>
+ <entry>Select configuration loaded at power-up</entry>
+ <entry>Load profile 0 (default)</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Y1</command></entry>
+ <entry>Select configuration loaded at power-up</entry>
+ <entry>Load profile 1</entry>
+ </row>
+ <row>
+ <entry><command>&amp;Z<replaceable>n</replaceable>=<replaceable>x</replaceable></command></entry>
+ <entry>Soft reset and load stored profile number
+ <replaceable>n</replaceable></entry>
+ <entry>Note that all items after the <command>&amp;Z</command> on the
+ command line are ignored</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ </sect1>
+
+ <sect1 id="hayes-backslash-commands">
+ <title>Backslash and Percent Commands</title>
+
+ <table>
+ <title>Backslash and Percent Commands</title>
+ <tgroup cols="3">
+ <thead>
+ <row>
+ <entry>Command</entry>
+ <entry>Description</entry>
+ <entry>Comments</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><command>\A0</command> or <command>\A</command></entry>
+ <entry>Character maximum <acronym>MNP</acronym> block size</entry>
+ <entry>64 character maximum</entry>
+ </row>
+ <row>
+ <entry><command>\A1</command></entry>
+ <entry>Character maximum <acronym>MNP</acronym> block size</entry>
+ <entry>128 character maximum</entry>
+ </row>
+ <row>
+ <entry><command>\A2</command></entry>
+ <entry>Character maximum <acronym>MNP</acronym> block size</entry>
+ <entry>192 character maximum</entry>
+ </row>
+ <row>
+ <entry><command>\A3</command></entry>
+ <entry>Character maximum <acronym>MNP</acronym> block size</entry>
+ <entry>256 character maximum</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;C0</command> or
+ <command>&percnt;C</command></entry>
+ <entry>Data Compression Enable/Disable</entry>
+ <entry>Disabled</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;C1</command></entry>
+ <entry>Data Compression Enable/Disable</entry>
+ <entry>MNP5 enabled</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;C2</command></entry>
+ <entry>Data Compression Enable/Disable</entry>
+ <entry>V.42bis (<acronym>BTLZ</acronym>) Enabled</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;C3</command></entry>
+ <entry>Data Compression Enable/Disable</entry>
+ <entry>MNP5 &amp; V.42bis (<acronym>BTLZ</acronym>) Enabled</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;D0</command> or
+ <command>&percnt;D</command></entry>
+ <entry>Data compression</entry>
+ <entry>512 BLTZ dictionary size</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;D1</command></entry>
+ <entry>Data compression</entry>
+ <entry>1024 BLTZ dictionary size</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;D2</command></entry>
+ <entry>Data compression</entry>
+ <entry>2048 BLTZ dictionary size</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;D3</command></entry>
+ <entry>Data compression</entry>
+ <entry>4096 BLTZ dictionary size</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E0</command> or
+ <command>&percnt;E1</command></entry>
+ <entry>Escape method</entry>
+ <entry>ESCAPE DISABLED</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E1</command></entry>
+ <entry>Escape method</entry>
+ <entry><command>+++AT</command> method (default)</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E2</command></entry>
+ <entry>Escape method</entry>
+ <entry><computeroutput>Break</computeroutput> <command>AT</command>
+ method</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E3</command></entry>
+ <entry>Escape method</entry>
+ <entry>BOTH methods enabled</entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E4</command></entry>
+ <entry>Escape method</entry>
+ <entry>Disable <computeroutput>OK</computeroutput> to
+ <command>+++</command></entry>
+ </row>
+ <row>
+ <entry><command>&percnt;E5</command></entry>
+ <entry>Escape method</entry>
+ <entry>Enable <computeroutput>OK</computeroutput> to
+ <command>+++</command></entry>
+ </row>
+ <row>
+ <entry><command>\J0</command> or <command>\J</command></entry>
+ <entry><acronym>DTE</acronym> Auto Rate Adjustment</entry>
+ <entry>Disabled</entry>
+ </row>
+ <row>
+ <entry><command>\J1</command></entry>
+ <entry><acronym>DTE</acronym> Auto Rate Adjustment</entry>
+ <entry><acronym>DTE</acronym> rate is adjusted to match carrier rate.</entry>
+ </row>
+ <row>
+ <entry><command>\N0</command> or <command>\N</command></entry>
+ <entry>Connection type</entry>
+ <entry>Normal connection (see below for definitions)</entry>
+ </row>
+ <row>
+ <entry><command>\N1</command></entry>
+ <entry>Connection type</entry>
+ <entry>Direction connection</entry>
+ </row>
+ <row>
+ <entry><command>\N2</command></entry>
+ <entry>Connection type</entry>
+ <entry><acronym>MNP</acronym> Auto-reliable connection</entry>
+ </row>
+ <row>
+ <entry><command>\N3</command></entry>
+ <entry>Connection type</entry>
+ <entry>Auto-reliable connection</entry>
+ </row>
+ <row>
+ <entry><command>\N4</command></entry>
+ <entry>Connection type</entry>
+ <entry>V.42bis reliable link with phase detection</entry>
+ </row>
+ <row>
+ <entry><command>\N5</command></entry>
+ <entry>Connection type</entry>
+ <entry>V.42bis auto-reliable link with phase detection</entry>
+ </row>
+ <row>
+ <entry><command>\N6</command></entry>
+ <entry>Connection type</entry>
+ <entry>V.42 reliable link with phase detection</entry>
+ </row>
+ <row>
+ <entry><command>\N7</command></entry>
+ <entry>Connection type</entry>
+ <entry>V.42 auto-reliable link with phase detection</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+<para>A direct connection is a simple straight-through connection without any
+error connection or data compression. In this case, the computer-to-modem and
+modem-to-modem speeds must be identical.</para>
+
+<para>A normal connection uses flow control (either software or hardware) to
+buffer the data being sent or received, so that the modem can transmit data at a
+different rate than the computer is actually sending or receiving it. For
+example, a computer may send actual data at 57kbps, but using compression, the
+modem only actually sends 28.8kbps. This is the mode use by most modems.</para>
+
+<para>A reliable connection is a type of normal connection; if, for some reason,
+data compression or error correction cannot be established or maintained, the
+connection will hang up. (In essence, such a modem ensures that all connections
+are reliable, for it will hang up if the connection isn't.)</para>
+
+<para>Likewise, an auto-reliable connection is virtually the same, except that
+the modem will try to renegotiate the connection in order to establish a
+reliable connection. Again, this is the mode that most modems use.</para>
+
+</sect1>
+
+<sect1 id="hayes-sregisters">
+<title>S-Registers</title>
+
+ <table>
+ <title>S Registers</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Register</entry>
+ <entry>Range</entry>
+ <entry>Default</entry>
+ <entry>Function</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><varname>S0</varname></entry>
+ <entry>0-255 rings</entry>
+ <entry>1-2</entry>
+ <entry>Answer on ring number. Don't answer if 0</entry>
+ </row>
+ <row>
+ <entry><varname>S1</varname></entry>
+ <entry>0-255 rings</entry>
+ <entry>0</entry>
+ <entry>if <varname>S0</varname> is greater than
+ <returnvalue>0</returnvalue> this register counts the incoming
+ rings.</entry>
+ </row>
+ <row>
+ <entry><varname>S2</varname></entry>
+ <entry>0-127 <acronym>ASCII</acronym></entry>
+ <entry>43 +</entry>
+ <entry>Escape to command mode character</entry>
+ </row>
+ <row>
+ <entry><varname>S2</varname></entry>
+ <entry>&gt;127</entry>
+ <entry></entry>
+ <entry>no ESC</entry>
+ </row>
+ <row>
+ <entry><varname>S3</varname></entry>
+ <entry>0-127 <acronym>ASCII</acronym></entry>
+ <entry>13 CR</entry>
+ <entry>Carriage return character</entry>
+ </row>
+ <row>
+ <entry><varname>S4</varname></entry>
+ <entry>0-127 <acronym>ASCII</acronym></entry>
+ <entry>10 LF</entry>
+ <entry>Line feed character</entry>
+ </row>
+ <row>
+ <entry><varname>S5</varname></entry>
+ <entry>0-32, 127 <acronym>ASCII</acronym></entry>
+ <entry>8 BS</entry>
+ <entry>Backspace character</entry>
+ </row>
+ <row>
+ <entry><varname>S6</varname></entry>
+ <entry>2-255 seconds</entry>
+ <entry>2</entry>
+ <entry>Dial tone wait time (blind dialing, see <link
+ linkend="hayes-basic-x1">X<replaceable>n</replaceable></link></entry>
+ </row>
+ <row>
+ <entry><anchor id="hayes-s7"/><varname>S7</varname></entry>
+ <entry>1-255 seconds</entry>
+ <entry>30-60</entry>
+ <entry>Wait time for remote carrier</entry>
+ </row>
+ <row>
+ <entry><varname>S8</varname></entry>
+ <entry>0-255 seconds</entry>
+ <entry>2</entry>
+ <entry>Comma pause time used in dialing</entry>
+ </row>
+ <row>
+ <entry><varname>S9</varname></entry>
+ <entry>1-255 1/10ths second</entry>
+ <entry>6</entry>
+ <entry>Carrier detect time required for recognition</entry>
+ </row>
+ <row>
+ <entry><varname>S10</varname></entry>
+ <entry>1-255 1/10ths second</entry>
+ <entry>7-14</entry>
+ <entry>Time between loss of carrier and hangup</entry>
+ </row>
+ <row>
+ <entry><varname>S11</varname></entry>
+ <entry>50-255 milliseconds</entry>
+ <entry>70-95</entry>
+ <entry>Duration and spacing of tones when tone dialing</entry>
+ </row>
+ <row>
+ <entry><varname>S12</varname></entry>
+ <entry>0-255 1/50th seconds</entry>
+ <entry>50</entry>
+ <entry>Guard time for pause around <command>+++</command> command
+ sequence</entry>
+ </row>
+ <row>
+ <entry><varname>S36</varname></entry>
+ <entry><para>Fallback options when error correction link
+ fails:</para><itemizedlist>
+ <listitem><para>0 - Disconnect</para>
+ </listitem>
+ <listitem><para>1 - Establish Direct Connection</para>
+ </listitem>
+ <listitem><para>3 - Establish Normal Connection</para>
+ </listitem>
+ <listitem><para>4 - Establish an <acronym>MNP</acronym> connection if
+ possible, else disconnect</para>
+ </listitem>
+ <listitem><para>5 - Establish an <acronym>MNP</acronym> connection if
+ possible, else Direct Connection.</para>
+ </listitem>
+ <listitem><para>7 - Establish an <acronym>MNP</acronym> connection if
+ possible, else Normal connection</para>
+ </listitem>
+ </itemizedlist></entry>
+ <entry>7</entry>
+ <entry>Negotiation Failure Treatment</entry>
+ </row>
+ <row>
+ <entry><anchor id="hayes-s37"/><varname>S37</varname></entry>
+ <entry><itemizedlist>
+ <listitem><para><returnvalue>1</returnvalue> = 300 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>5</returnvalue> = 1200 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>6</returnvalue> = 2400 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>7</returnvalue> = 1200/75 bps (v.23
+ mode)</para>
+ </listitem>
+ <listitem><para><returnvalue>8</returnvalue> = 4800 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>9</returnvalue> = 9600 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>10</returnvalue> = 12000 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>11</returnvalue> = 14400 bps</para>
+ </listitem>
+ <listitem><para><returnvalue>12</returnvalue> = 7200 bps</para>
+ </listitem>
+ </itemizedlist></entry>
+ <entry>0</entry>
+ <entry>Negotiation Speed (Initial handshake)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+<para>Many modems have dozens, even hundreds, of S registers, but only the first
+dozen or so are fairly standard. They are changed with a command like
+<command>ATS<replaceable>n</replaceable>=<replaceable>N</replaceable></command>,
+and examined with <command>ATS<replaceable>n</replaceable>?</command> (&eg;
+<userinput><command>AT</command> <command>S10</command><option>=70</option>
+<command>S1?</command></userinput> would tell the modem not to hang up for seven
+seconds should it not hear the answering modem, and return the number of times
+the phone last rang.)</para>
+
+</sect1>
+</appendix>
diff --git a/doc/kppp/index.docbook b/doc/kppp/index.docbook
new file mode 100644
index 00000000..ea172828
--- /dev/null
+++ b/doc/kppp/index.docbook
@@ -0,0 +1,268 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&kppp;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY getting-online SYSTEM "getting-online.docbook">
+ <!ENTITY wizard SYSTEM "wizard.docbook">
+ <!ENTITY dialog-setup SYSTEM "dialog-setup.docbook">
+ <!ENTITY global-settings SYSTEM "global-settings.docbook">
+ <!ENTITY security SYSTEM "security.docbook">
+ <!ENTITY chap-and-pap SYSTEM "chap.docbook">
+ <!ENTITY tricks SYSTEM "tricks.docbook">
+ <!ENTITY callback SYSTEM "callback.docbook">
+ <!ENTITY kppp-faq SYSTEM "kppp-faq.docbook">
+ <!ENTITY hayes-reference SYSTEM "hayes.docbook">
+ <!ENTITY accounting SYSTEM "accounting.docbook">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &kppp; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Lauri</firstname>
+<surname>Watts</surname>
+<affiliation>
+<address><email>lauri@kde.org</email></address>
+</affiliation>
+</author>
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<copyright>
+<year>2001</year>
+<holder>Lauri Watts</holder>
+</copyright>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2001-06-11</date>
+<releaseinfo>1.01.00</releaseinfo>
+
+<!-- Abstract about this handbook -->
+
+<abstract>
+<para>&kppp; is a dialer and front end for <application>pppd</application>,
+allowing for interactive script generation and network setup.</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kppp</keyword>
+<keyword>kdenetwork</keyword>
+<keyword>dialer</keyword>
+<keyword>internet</keyword>
+<keyword>ppp</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&kppp; is a dialer and front end for <application>pppd</application>. It
+allows for interactive script generation and network setup. It will automate the
+dialing in process to your <acronym>ISP</acronym> while letting you conveniently
+monitor the entire process. </para>
+
+<para>Once connected &kppp; will provide a rich set of statistics and keep track
+of the time spent online for you.</para>
+
+<para>A built-in terminal and script generator will enable you to set up your
+connection with ease. You will no longer need an additional terminal program
+such as <application>seyon</application> or <application>minicom</application>
+to test and setup your connection.</para>
+
+<para>&kppp; features elaborate phone cost accounting, which enables you to
+easily track your online costs.</para>
+
+<para>We hope you enjoy this dialer, and that it eases your way onto the
+internet.</para>
+
+</chapter>
+
+&getting-online;
+
+&wizard;
+
+&dialog-setup;
+
+&global-settings;
+
+&security;
+
+&chap-and-pap;
+
+&tricks;
+
+&callback;
+
+&kppp-faq;
+
+<chapter id="credits">
+
+<title>Credits and License</title>
+
+<para>&kppp;</para>
+
+<para>&kppp; is derived from <application>ezppp</application> 0.6, by Jay
+Painter. However, nearly everything in &kppp; was rewritten so
+<application>ezppp</application> and &kppp; do not have much in common any
+longer.</para>
+
+<para>Primary Developers:</para>
+
+<itemizedlist>
+<listitem><para>Bernd Johannes Wuebben <email>wuebben@kde.org</email></para>
+</listitem>
+<listitem><para>Mario Weilguni <email>mweilguni@sime.com</email></para>
+</listitem>
+<listitem><para>Harri Porten <email>porten@kde.org</email> (Current
+maintainer)</para>
+</listitem>
+</itemizedlist>
+
+<para>Many thanks to the following people who have contributed code to
+&kppp;</para>
+
+<itemizedlist>
+<listitem><para>Jesus Fuentes Saaverdra
+<email>jesus.fuentes@etsi.tel.uva.esfor</email> implementing several options and
+miscellaneous work.</para>
+</listitem>
+<listitem><para>Markus Wuebben <email>wuebben@eure.de</email> for the ATI query
+dialog</para>
+</listitem>
+<listitem><para>Peter Silva <email>peter.silva@videotron.ca</email> for pop up
+dialogs and other contributions</para>
+</listitem>
+<listitem><para>Martin A. Brown <email>MABrown@etcconnect.org</email></para>
+</listitem>
+<listitem><para>Martin H&auml;fner
+<email>mh@ap-dec717c.physik.uni-karlsruhe.de</email> for the section on callback.</para>
+</listitem>
+<listitem><para>Olaf Kirch <email>okir@caldera.de</email> for the introduction
+to the mysteries of file descriptor passing.</para>
+</listitem>
+</itemizedlist>
+
+
+<para>Documentation copyright 2001 Lauri Watts
+<email>lauri@kde.org</email>, although largely based on the original by
+Bernd Johannes Wuebben <email>wuebben@kde.org</email></para>
+
+&underFDL; <!-- FDL: do not remove -->
+
+&underGPL; <!-- GPL License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-kppp">
+<title>How to obtain &kppp;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+</sect1>
+
+<sect1 id="preparing-your-computer">
+<title>Preparing your Computer for a <acronym>PPP</acronym> Connection</title>
+
+<para>The following sections contain some fairly generic information for several
+common operating systems which might run &kppp;. The following sites may be of
+interest for further information about the <acronym>ppp</acronym> protocol,
+<application>pppd</application> and networking in general:</para>
+
+<itemizedlist>
+<listitem><para>The &Linux; <acronym>PPP</acronym> &FAQ;: <ulink
+url="http://metalab.unc.edu/mdw/FAQ/PPP-FAQ.html">
+http://metalab.unc.edu/mdw/FAQ/PPP-FAQ.html</ulink></para></listitem>
+<listitem><para>The &Linux; <acronym>PPP</acronym> HOWTO: <ulink
+url="http://metalab.unc.edu/mdw/HOWTO/PPP-HOWTO.html">
+http://metalab.unc.edu/mdw/HOWTO/PPP-HOWTO.html</ulink></para></listitem>
+<listitem><para><ulink url="http://www.thoughtport.com:8080/PPP/index.html">
+http://www.thoughtport.com:8080/PPP/index.html</ulink></para></listitem>
+<listitem><para>The Network Administrators' Guide: <ulink
+url="http://metalab.unc.edu/mdw/LDP/nag/nag.html">
+http://metalab.unc.edu/mdw/LDP/nag/nag.html</ulink></para></listitem>
+</itemizedlist>
+
+<sect2 id="preparing-linux-for-ppp">
+<title>Preparing a &Linux; system for <acronym>PPP</acronym></title>
+
+<para>In order for &kppp; (or indeed, <application>pppd</application>) to work,
+your kernel must have ppp support compiled in. If this is not the case, get
+yourself the latest version of <application>pppd</application> from any of the
+popular &Linux; archives (such as <ulink
+url="ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/ppp/">ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/ppp/</ulink>,
+and recompile your kernel with <acronym>ppp</acronym> support enabled.</para>
+
+<para>Don't fret, since this sounds a lot scarier than it actually is. Don't
+forget to install <application>pppd</application> afterwards.</para>
+
+<para>If you're not sure if you have a kernel with ppp support, issue the
+<command>dmesg</command> at the command prompt and look for something like
+this:</para>
+
+<informalexample>
+<screen><computeroutput>
+PPP: version 2.3.0 (demand dialing)
+TCP compression code copyright 1989 Regents of the University of California
+PPP Dynamic channel allocation code copyright 1995 Caldera, Inc.
+PPP line discipline registered
+</computeroutput></screen>
+</informalexample>
+
+<para>&kppp; tries to find out for itself if your kernel supports
+<acronym>PPP</acronym>. If not, you will be notified as soon as &kppp; starts
+up.</para>
+
+<para>For &Linux; 2.x kernels, the <application>pppd</application> daemon should
+be version 2.3 or greater. You can find out what version your system has, by
+issuing the command <userinput><command>pppd</command>
+<option>--version</option></userinput> on the command line. None of the
+<application>pppd</application> daemons actually have a
+<option>--version</option>, but putting the option in will cause the
+<application>pppd</application> daemon to issue an error message, and then to
+print out a list of options and other information, which includes the version of
+the <application>ppd</application> daemon.</para>
+
+</sect2>
+
+<!--<sect2 id="preparing-bsd-for-ppp">
+<title>Preparing your FreeBSD computer for ppp connections</title>
+
+<para>to be written</para>
+</sect2> -->
+
+</sect1>
+
+</appendix>
+
+&hayes-reference;
+
+&accounting;
+
+&documentation.index;
+</book>
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes: nil
+sgml-general-insert-case: lower
+End:
+-->
+
diff --git a/doc/kppp/kppp-account-accounting-tab.png b/doc/kppp/kppp-account-accounting-tab.png
new file mode 100644
index 00000000..a0c4321d
--- /dev/null
+++ b/doc/kppp/kppp-account-accounting-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-dial-tab.png b/doc/kppp/kppp-account-dial-tab.png
new file mode 100644
index 00000000..2c0dc3ae
--- /dev/null
+++ b/doc/kppp/kppp-account-dial-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-dns-tab.png b/doc/kppp/kppp-account-dns-tab.png
new file mode 100644
index 00000000..b6215c2f
--- /dev/null
+++ b/doc/kppp/kppp-account-dns-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-execute-tab.png b/doc/kppp/kppp-account-execute-tab.png
new file mode 100644
index 00000000..ef0e5ed1
--- /dev/null
+++ b/doc/kppp/kppp-account-execute-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-gateway-tab.png b/doc/kppp/kppp-account-gateway-tab.png
new file mode 100644
index 00000000..cebf1f5a
--- /dev/null
+++ b/doc/kppp/kppp-account-gateway-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-ip-tab.png b/doc/kppp/kppp-account-ip-tab.png
new file mode 100644
index 00000000..ccc9b7db
--- /dev/null
+++ b/doc/kppp/kppp-account-ip-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-account-login-script-tab.png b/doc/kppp/kppp-account-login-script-tab.png
new file mode 100644
index 00000000..293cfb2f
--- /dev/null
+++ b/doc/kppp/kppp-account-login-script-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-config.png b/doc/kppp/kppp-config.png
new file mode 100644
index 00000000..d9a5000d
--- /dev/null
+++ b/doc/kppp/kppp-config.png
Binary files differ
diff --git a/doc/kppp/kppp-device-tab.png b/doc/kppp/kppp-device-tab.png
new file mode 100644
index 00000000..3d3b5257
--- /dev/null
+++ b/doc/kppp/kppp-device-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-dialler-tab.png b/doc/kppp/kppp-dialler-tab.png
new file mode 100644
index 00000000..7dea2838
--- /dev/null
+++ b/doc/kppp/kppp-dialler-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-faq.docbook b/doc/kppp/kppp-faq.docbook
new file mode 100644
index 00000000..57ef26ff
--- /dev/null
+++ b/doc/kppp/kppp-faq.docbook
@@ -0,0 +1,477 @@
+<chapter id="faq">
+<title>Questions and Answers</title>
+
+&reporting.bugs;
+
+<qandaset id="faqlist">
+
+<qandadiv id="faq-dialing">
+<title>Questions about Dialing</title>
+<qandaentry>
+<question><para>I can't get &kppp; to work. &kppp; tells me
+<application>pppd</application> has died or that a timeout has expired. What's
+going on?</para></question>
+
+<answer><para>Did you read this manual carefully? Here are once more the most common pitfalls:</para>
+
+<itemizedlist>
+
+<listitem><para> Click on the <guibutton>Details</guibutton> button. &kppp; will
+you give an excerpt from the <acronym>PPP</acronym> log messages (may not work
+on non-&Linux; systems, or even on some &Linux; distributions). The log will
+help you to track down the bug.</para></listitem>
+
+<listitem><para> Make sure that <application>pppd</application> is the actual
+<application>pppd</application> binary not a script</para></listitem>
+
+<listitem><para> Make sure that <application>pppd</application> is setuid
+<systemitem>root</systemitem>. You may set this mode by issuing
+<userinput><command>chmod</command> <option>u+s pppd</option></userinput> as
+<systemitem>root</systemitem>. </para></listitem>
+
+<listitem><para> Make sure that your <filename>/etc/ppp/options</filename> file
+exists and that it doesn't contain any conflicting entries. If in doubt: Leave
+this file empty. </para></listitem>
+
+<listitem><para> Make sure that you <emphasis>don't</emphasis> use the option
+<option>lock</option> as an argument for <application>pppd</application> (&kppp;
+is already taking care of device locking).</para></listitem>
+
+<listitem><para> Remove the <option>lock</option> option from
+<emphasis>both</emphasis> your <filename>/etc/ppp/options</filename>
+<emphasis>and</emphasis> <filename>&tilde;/.ppprc</filename>
+files!</para></listitem>
+<listitem><para> Using the symbolic link <filename
+class="symlink">/dev/modem</filename> may cause some conflicts. Eliminate this
+source of trouble by using the real device, &ie; <filename>/dev/cuaX</filename>
+or <filename>/dev/ttySX</filename>. </para>
+<note><para><hardware>COM1</hardware> equals <filename>ttyS0</filename>,
+<hardware>COM2</hardware> is <filename>ttyS1</filename> and so
+on. </para></note></listitem>
+
+<listitem><para>Make sure you set the right permission. In case of trouble you
+might want to run it as root first and then later, when everything is working
+fine give it less harmful permission if you can not afford to run &kppp; setuid
+<systemitem>root</systemitem>. The proper way to proceed would
+probably be creating a <systemitem>modem</systemitem>
+group.</para></listitem>
+
+<listitem><para>You might be launching <application>pppd</application> too
+early, &ie; before the remote server is ready to negotiate a
+<acronym>PPP</acronym> connection. If you are using a login script, you should
+use the built-in terminal to verify your login procedure. Some providers will
+require you to issue a simple <command>Send</command> or <command>Send
+ppp</command> to launch <acronym>PPP</acronym>. Some users even reported, that
+they had to append <command>Pause 1</command> or <command>Pause 2</command> to
+their script to solve timing conflicts.</para></listitem>
+
+</itemizedlist>
+
+<para>If nothing helps, you might obtain some debugging info from your systems
+log by issuing:</para>
+
+<screen><prompt>#</prompt> <userinput><command>tail</command> <filename>/var/log/messages</filename></userinput></screen>
+
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>pppd died - The remote system is required to authenticate itself ...</para></question>
+
+<answer>
+<para>Typical error message in system log:</para>
+<screen>
+pppd[699]: The remote system is required to authenticate itself
+pppd[699]: but I couldn't find any suitable secret (password) for it to use to do so.
+pppd[699]: (None of the available passwords would let it use an IP address.)
+</screen>
+<para>As far as I can tell there are two causes for this problem: </para>
+<itemizedlist>
+<listitem><para><filename>/etc/ppp/options</filename> contains the
+<option>auth</option> option. Simply put a <prompt>#</prompt> comment in
+front and try again. </para></listitem> <listitem><para>Your system already
+has a default route. Have you set up a local network? In this case recent
+versions of pppd will behave as if <option>auth</option> had been
+specified. To override this you may add <option>noauth</option> to the pppd
+arguments in kppp' setup dialog. Alternatively you could take down the local
+network prior to dialing in. I'd be thankful if someone could provide
+instructions on how to peacefully combine the two network
+connections. </para></listitem>
+</itemizedlist>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>pppd dies with 2.4.x Linux kernel</para></question>
+<answer>
+<para>Typical error messages in the system log:</para>
+
+<screen>
+pppd[1182]: pppd 2.3.11 started by user, uid 500
+pppd[1182]: ioctl(PPPIOCGFLAGS): Invalid argument
+pppd[1182]: tcsetattr: Invalid argument
+pppd[1182]: Exit.
+</screen>
+<para>Install pppd 2.4.0b1 or better. See
+<filename>Documentation/Changes</filename> in the kernel sources for more
+info.</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Why does &kppp; tell me <errorname>Unable to open the
+modem</errorname>?</para></question>
+
+<answer><para>This means that &kppp; doesn't have permissions to open the modem
+device or that you selected a modem device on the <guilabel>Modem</guilabel> Tab
+Dialog that is not valid. First make sure you selected the right modem
+device. Once you are sure you have selected the right modem device, you must
+give &kppp; the right permission to access the modem device and to be able to
+modify <filename>/etc/resolv.conf</filename> in case you want &kppp; to
+configure <acronym>DNS</acronym> correctly for you. If you can afford to run
+&kppp; setuid <systemitem>root</systemitem> this would solve all access problems
+for you, if not you will have to figure out what the right permissions are for
+your purposes. In order to give &kppp; setuid <systemitem>root</systemitem>
+permissions do the following:</para>
+
+<screen><prompt>%</prompt> <userinput><command>su</command> <option>root</option></userinput>
+<prompt>#</prompt> <userinput><command>chown</command> <option>root:root &dollar;KDEDIR/bin/kppp</option></userinput>
+<prompt>#</prompt> <userinput><command>chmod</command> <option>+s &dollar;KDEDIR/bin/kppp</option></userinput>
+<prompt>#</prompt> <userinput><command>exit</command></userinput>
+</screen>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Why does &kppp; tell me it can't create a modem lock
+file?</para></question>
+
+<answer><para>This in most instances means that you have installed &kppp;
+without SETUID bit on while you, the person executing &kppp;, doesn't have write
+access to the lock file folder which by default is <filename
+class="directory">/var/lock</filename>. This for example is the case on &RedHat;
+systems. Check the modem dialog for the precise location you have chosen. The
+solution is easy -- either run &kppp; SETUID if you can afford to, or give
+regular users write access to <filename class="directory">/var/lock</filename>
+or create a modem group that will have access to the <filename
+class="directory">/var/lock</filename> file.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Why is &kppp; installed with the SETUID bit
+on?</para></question>
+
+<answer><para>para>There is no need for the SETUID bit, if you know a bit of
+&UNIX; systems administration. Simply create a <systemitem>modem</systemitem>
+group, add all users that you want to give access to the modem to that group and
+make the modem device read/writable for that group. Also if you want
+<acronym>DNS</acronym> configuration to work with &kppp;, then
+<filename>/etc/resolv.conf</filename> must be read/writable by the members of
+that group. The same counts for <filename>/etc/ppp/pap-secrets</filename> and
+<filename>/etc/ppp/chap-secrets</filename> if you want to use the built-in
+<acronym>PAP</acronym> or <acronym>CHAP</acronym> support, respectively.</para>
+
+<para>The &kppp; team has lately done a lot of work to make
+&kppp; setuid-safe. But it's up to you to decide if you
+install and how you install it.</para>
+
+<para>You might also want to read the <link linkend="security">Security</link>
+section.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>What do I do when &kppp; just sits there and waits with the
+message: <computeroutput>Expecting OK</computeroutput></para></question>
+
+<answer><para>Have you played with the CR/LF setting? Try CR, LF or
+CR/LF.</para>
+
+<para>Alternatively, your modem might need some time to respond to its
+initialization. Open the <guilabel>Modem Commands</guilabel> dialog on the
+<guilabel>Modem</guilabel> tab and adjust the <guilabel>Pre-Init</guilabel> and
+<guilabel>Post-Init</guilabel> delays. See if you are successful when
+drastically increasing their values, and then do some fine-tuning
+later.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>The connection works fine, but I can't start any
+applications!</para></question>
+
+<answer><para>You have probably selected the <link
+linkend="auto-configure-hostname">Auto Configure Host Name</link> option, and
+the X Server has problems connecting to your newly named host. If you really
+need this option (and chances are you really don't), you are unfortunately on
+your own to set up the appropriate authorizations. Issuing
+<userinput><command>xhost</command> <option>+</option></userinput> before
+starting the connection would do the job, but be warned of the security risks
+involved, since this effectively gives everyone else access to your X
+Server.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>&kppp; reports a successful connection, but &konqueror; just says
+<errorname>Unknown host <replaceable>hostname</replaceable></errorname>, and
+&Netscape; reports <errorname>The server does not have a DNS
+entry</errorname>.</para></question>
+
+<answer><para>Try pinging another server by its <acronym>IP</acronym> number,
+&eg; <userinput><command>ping</command>
+<option>195.0.254.76</option></userinput>. If that works, you could try the
+following:</para>
+
+<itemizedlist>
+<listitem><para>Check if you have provided &kppp; with at least one
+<acronym>DNS</acronym> address.</para></listitem>
+
+<listitem><para>Check the contents of <filename>/etc/host.conf</filename>. There
+should be a line saying something similar to <literal>order hosts,
+bind</literal>. The <option>bind</option> keyword advises the resolver library
+to include a name server query when performing an address lookup. If such a
+line is not there, try adding it.</para></listitem>
+</itemizedlist></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>How do I make &kppp; send a <keysym>\n</keysym> or a
+<keysym>\r</keysym></para></question>
+
+<answer><para>Just send an empty string such as in the following script:</para>
+
+<informalexample>
+<screen>
+Send # send an empty string
+Expect ID:
+Send itsme
+Expect word:
+Send forgot
+Expect granted
+Send ppp
+</screen>
+</informalexample>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>How can I stop &kppp; complaining: <errorname>Can't create lock
+file</errorname>?</para></question>
+<answer><para>This happens because you don't have permissions to create a lock
+file. If you chose to use a lock file, you must have write permission to the
+folder (typically <filename class="directory">/var/lock</filename>). This is
+of course no problem if you have given &kppp; setuid permissions. Please read
+the section on <link linkend="lock-files">Lock files</link>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Why is my modem making so much noise when
+dialing?</para></question>
+
+<answer><para>Click on <guibutton>Setup</guibutton>, then
+<guilabel>Modem</guilabel>. You can control the modem volume here in three
+steps: Off, medium and high. For most modems, medium or high result in the same
+volume. If changing this setting doesn't work, make sure the correct settings
+for your modem are specified in <guibutton>Setup</guibutton>,
+<guilabel>Modem</guilabel>, <guibutton>Modem
+Commands</guibutton>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>I turned the modem volume to <quote>Off</quote> and verified the
+modem commands, but I still hear that awful noise during dialing.
+Why?</para></question>
+
+<answer><para>The volume initialization string can get lost if your modem can't
+cope with the speed it is receiving commands from &kppp;. Increase the value of
+<guilabel>Post-Init Delay</guilabel> in <guibutton>Setup</guibutton>,
+<guilabel>Modem</guilabel>, <guibutton>Modem
+Commands</guibutton>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>&kppp; keeps reporting unusual modem speeds like
+<quote>115200</quote> or <quote>57600</quote></para></question>
+
+<answer><para>Many modems only report the speed of the serial line and not the
+speed over the telephone line as default. You must configure these modems to
+report the true line speed by adding some commands to the modem init or dial
+strings. For many modems this command is <command>ATW2</command>. If you want
+to add it to the dial string (which normally starts with
+<command>ATD</command>), the new dial string would be
+<command>ATW2D</command>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Why does &kppp; report <quote>Unknown
+speed</quote></para></question>
+
+<answer><para>New modems often have very complex connection messages like
+<computeroutput>CONNECT LAP.M/V42.bis/115000:RX/31200:TX</computeroutput>, and
+&kppp; cannot parse this message correctly. Turn on <guibutton>Show
+Log</guibutton> and you'll see the connection speed.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>I get a slow connection speed</para></question>
+
+<answer><para>If you are not satisfied with the modem speed, make sure you've
+set the connection speed (you can reach it by clicking on
+<guibutton>Setup</guibutton>, <guilabel>Device</guilabel>, <guibutton>Connection
+Speed</guibutton>) to 57600 or higher. Make sure your serial ports support
+higher speeds. Many older systems based on i486 do not work correctly if you
+set the speed to 115200. If you have an old <hardware>8250 UART</hardware>
+chip, it won't work. If you have a <hardware>16550</hardware> or
+<hardware>16550A</hardware> it should work flawlessly.</para>
+
+<para>Additionally, you should consult your modem manual to look for init
+strings that enable a high speed mode.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>I get a <emphasis>REALLY</emphasis> slow connection
+speed!</para></question>
+
+<answer><para>If data drips on at a rate of just a few bytes per second, you
+should check your hardware setup. If moving your mouse speeds up the
+transmission this is definitely a hardware issue!</para>
+
+<para>You can obtain some information about your serial port with
+<userinput><command>setserial</command> <option>-a
+<replaceable>/dev/ttySx</replaceable></option></userinput> and check for interrupt
+conflicts with other components of your system. The &kcontrol; module
+<guilabel>Information</guilabel> might also be of help here.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>My phone line needs pulse dialing instead of tone dialing (or
+vice-versa). How do I change that?</para></question>
+<answer><para>You must modify your modem dial string. Nearly all modems support
+the following AT commands:</para>
+
+<variablelist>
+<varlistentry>
+<term><command>ATDT</command></term>
+<listitem><para><action>Selects tone dialing</action></para></listitem>
+</varlistentry>
+<varlistentry>
+<term><command>ATDP</command></term>
+<listitem><para><action>Selects pulse dialing</action></para></listitem>
+</varlistentry>
+</variablelist>
+</answer>
+
+</qandaentry>
+
+</qandadiv>
+
+<qandadiv id="faq-telephone-cost-rules">
+<title>Questions about Telephone Cost Rules</title>
+<qandaentry>
+<question><para>How do I write a telephones cost rules file?</para></question>
+<answer><para>Just follow the <filename>TEMPLATE</filename> rules file supplied
+with &kppp;. You should be able to find a copy in <filename
+class="directory">$KDEDIR/doc/HTML/<replaceable>yourlang</replaceable>/kppp/</filename>.
+Use the <option>-r</option> &kppp; command line option to check the syntax of
+your proposed rules file.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>I have written a telephone cost rules for my region. Where can
+I submit it so that others can make use of it?</para></question>
+<answer><!-- LW: Find out -->
+<!-- lukas: the answer is: send it to the kppp maintainer, Harri Porten --></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>Can my phone cost rulefile contain fractional time units like
+"(0.17, 45.5)"?</para></question>
+<answer><para>Yes this is possible. But you shouldn't use unusually small time
+units below a tenth of a second, because this would result in higher
+<acronym>CPU</acronym> load, although you probably won't notice on a modern
+<acronym>CPU</acronym>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>My country observes other <quote>moving</quote> holidays than
+Easter.</para></question>
+<answer><para>In that case, you need to write new code that allows for the
+computation of that holiday. Please have a look at
+<filename>ruleset.cpp</filename> and emulate the <quote>easter</quote> example.
+Then send in the patches!.</para></answer>
+</qandaentry>
+</qandadiv>
+
+<qandadiv id="faq-system-logs">
+<title>Questions about the System Logs</title>
+<qandaentry>
+<question><para>I see a message saying <errorname>Serial line is looped
+back</errorname>. What does this mean?</para></question>
+
+<answer><para>Short answer: You didn't start the <acronym>PPP</acronym> software
+on the peer system.</para>
+
+<!-- this doc doesn't exist.. help -->
+<!-- http://www.dejanews.com/getdoc.xp?AN="184945314" -->
+
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>The logs show <errorname>Signal 15</errorname></para></question>
+<answer><para>If you see the following lines, you've probably just received a
+timeout error from &kppp;. &kppp; has been waiting for the
+<acronym>PPP</acronym> interface to come up and gave up after the specified
+timeout. <application>pppd</application> was signalled to shut down, with signal
+number 15, &ie; <errorcode>SIGTERM</errorcode>.</para>
+
+<informalexample>
+<screen><computeroutput>
+pppd[26921]: pppd 2.3.5 started by me, uid 500
+pppd[26921]: Using interface ppp0
+pppd[26921]: Connect: ppp0 &lt;--&gt; /dev/ttyS0
+pppd[26921]: Terminating on signal 15.
+pppd[26921]: Connection terminated.
+pppd[26921]: Exit.
+</computeroutput></screen>
+</informalexample>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>What about <errorname>Receive serial link is not 8-bit
+clean</errorname></para></question>
+<answer><para>The <acronym>PPP</acronym> daemon is alarmed by the fact that all the
+data it receives has bit 8 set to zero. In most cases this simply indicates
+that the remote <acronym>PPP</acronym> server isn't running yet. You might
+still be confronted by a login prompt that echoes back all the data sent by your
+<application>pppd</application>.</para></answer>
+</qandaentry>
+
+<qandaentry>
+<question><para>and <errorname>can't locate module ppp-compress</errorname>?
+What's this?</para></question>
+<answer><para>Do you see the following messages?</para>
+
+<informalexample>
+<screen><computeroutput>
+modprobe: can't locate module ppp-compress-21
+modprobe: can't locate module ppp-compress-26
+modprobe: can't locate module ppp-compress-24
+</computeroutput></screen>
+</informalexample>
+
+<para>Just add the lines:</para>
+
+<screen><userinput>
+alias ppp-compress-21 bsd_comp
+alias ppp-compress-24 ppp_deflate
+alias ppp-compress-26 ppp_deflate </userinput></screen>
+
+<para> to your <filename>/etc/conf.modules</filename> file.</para>
+</answer>
+</qandaentry>
+
+</qandadiv>
+
+</qandaset>
+
+</chapter>
diff --git a/doc/kppp/kppp-graph-tab.png b/doc/kppp/kppp-graph-tab.png
new file mode 100644
index 00000000..0597810a
--- /dev/null
+++ b/doc/kppp/kppp-graph-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-misc-tab.png b/doc/kppp/kppp-misc-tab.png
new file mode 100644
index 00000000..b8bc0840
--- /dev/null
+++ b/doc/kppp/kppp-misc-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-modem-tab.png b/doc/kppp/kppp-modem-tab.png
new file mode 100644
index 00000000..58534642
--- /dev/null
+++ b/doc/kppp/kppp-modem-tab.png
Binary files differ
diff --git a/doc/kppp/kppp-wizard.png b/doc/kppp/kppp-wizard.png
new file mode 100644
index 00000000..e648d413
--- /dev/null
+++ b/doc/kppp/kppp-wizard.png
Binary files differ
diff --git a/doc/kppp/kppp.faq.question b/doc/kppp/kppp.faq.question
new file mode 100644
index 00000000..e4800ab0
--- /dev/null
+++ b/doc/kppp/kppp.faq.question
@@ -0,0 +1,54 @@
+X-RDate: Fri, 12 Dec 1997 19:59:12 -0500 (EST)
+X-UIDL: 26006
+Return-Path: <tuhlmann@debis.com>
+Received: from cornell.edu (cornell.edu [132.236.56.6]) by
+ postoffice2.mail.cornell.edu (8.8.5/8.8.5) with ESMTP id BAA01394 for
+ <bw18@POSTOFFICE2.MAIL.CORNELL.EDU>; Fri, 12 Dec 1997 01:06:00 -0500 (EST)
+Received: (from daemon@localhost) by cornell.edu (8.8.5/8.8.5) id BAA05503 for
+ bw18@postoffice3.mail.cornell.edu; Fri, 12 Dec 1997 01:06:00 -0500 (EST)
+Received: from polygon.math.cornell.edu (POLYGON.MATH.CORNELL.EDU
+ [128.84.234.110]) by cornell.edu (8.8.5/8.8.5) with SMTP id BAA05461 for
+ <bw18@cornell.edu>; Fri, 12 Dec 1997 01:05:58 -0500 (EST)
+Received: from sun03.berlin2.debis-sfi.de (proxy.debis-sfi.de) by
+ polygon.math.cornell.edu (5.x/SMI-SVR4) id AA14318;
+ Fri, 12 Dec 1997 01:05:52 -0500
+Received: from merlin.debis-sfi.de(138.201.4.1) by sun03 via smap (V2.0) id
+ xma001088; Fri, 12 Dec 97 07:04:21 +0100
+Received: by b1.debis.com id HAA28426; Fri, 12 Dec 1997 07:05:37 +0100
+Received: by c1.debis.com id HAA16797; Fri, 12 Dec 1997 07:05:39 +0100 (MET)
+Message-Id: <199712120605.HAA16797-c1@debis.com>
+X-PH: V4.1@cornell.edu (Cornell Modified)
+Date: Fri, 12 Dec 97 07:04:48 +0100
+Reply-To: "Torsten Uhlmann" <tuhlmann@debis.com>
+X-Mailer: debis Systemhaus's Registered PMMail 1.9 For OS/2
+Mime-Version: 1.0
+Content-Type: text/plain; charset="iso-8859-1"
+Content-Transfer-Encoding: 7bit
+XFMstatus: 0002
+From: Torsten Uhlmann <tuhlmann@debis.com>
+To: Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+Subject: RE: kppp
+
+On Wed, 10 Dec 1997 14:07:24 -0500 (EST), Bernd Johannes Wuebben wrote:
+
+>
+>Hello Torsten,
+>
+>On 10-Dec-97 Torsten Uhlmann wrote:
+>> I've got a problem using kppp (I think it's the latest version, I got it
+>> recently from your project page). I use S.u.S.E. Linux 5.0 and KDE
+>> beta 2.
+>>
+>
+>OK this is a problem with you pppd options and PAP configuration.
+>However lots of Germans are using kppp and I imagine that if you
+>were to post your question to kde@kde.org and kde-user@kde.org
+>someone will be able to help you. Let me know...
+>
+>Bernd
+>
+
+I figured it out. The problem was I handed over USER "LOGIN" as it has to be in ppp-up.
+(At T-Online LOGIN is <Number><Tel-No>#<Number> which has to be quoted in order
+to not be substituted by any shell. Well between kppp and pppd there is probably no shell,
+so the quotes are obsolete :-)
diff --git a/doc/kppp/security.docbook b/doc/kppp/security.docbook
new file mode 100644
index 00000000..d3012f8b
--- /dev/null
+++ b/doc/kppp/security.docbook
@@ -0,0 +1,96 @@
+<chapter id="security">
+<title>&kppp; and security issues</title>
+
+<para>This section is mainly for superusers (<systemitem>root</systemitem>)
+people with high security demands, or simply technically interested people. It
+is not necessary to read this if you only use &Linux; at home for yourself,
+although you may learn a thing or two in any case.</para>
+
+<sect1 id="security-restricting-access">
+<title>Restricting access to &kppp;</title>
+
+<para>A system administrator might want to restrict access as to who is allowed
+to use &kppp;. There are two ways to accomplish this.</para>
+
+<sect2 id="security-group-permissions">
+<title>Restricting access with group permissions</title>
+
+<para>Create a new group (you might want to name it
+<systemitem>dialout</systemitem> or similar), and put every user that should be
+allowed to use &kppp; into that group. Then type at the prompt:</para>
+
+<screen><prompt>#</prompt> <userinput><command>chown</command> <option>root.dialout</option> <filename>/opt/kde/bin/kppp</filename></userinput>
+<prompt>#</prompt> <userinput><command>chmod</command> <option>4750</option> <filename>/opt/kde/bin/kppp</filename></userinput>
+</screen>
+
+<para>This assumes that &kde; was installed in <filename class="directory">
+/opt/kde/</filename> and that your new group is named
+<systemitem>dialout</systemitem>.</para>
+
+</sect2>
+
+<sect2 id="security-kppps-way">
+<title>Restricting access &kppp;'s way</title>
+
+<para>Before doing anything, &kppp; checks if there is a file named
+<filename>/etc/kppp.allow</filename>. If such a file exists, only users named in
+this file are allowed to dial out. This file must be readable by everyone (but
+of course <emphasis>NOT</emphasis> writable.) Only login names are recognized,
+so you cannot use <acronym>UID</acronym>'s in this file. Here is a short
+example:</para>
+
+<screen>
+# /etc/kppp.allow
+# comment lines like this are ignored
+# as well as empty lines
+
+fred
+karl
+daisy
+</screen>
+
+<para>In the example above, only the users <systemitem>fred</systemitem>,
+<systemitem>karl</systemitem> and <systemitem>daisy</systemitem> are allowed to
+dial out, as well as every user with a <acronym>UID</acronym> of 0 (so you don't
+have to explicitly list root in the file).</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="security-why-suid">
+<title>&kppp; has the <acronym>SUID</acronym> bit on? What about
+security?</title>
+
+<para>It's virtually impossible to write a dialer without the
+<acronym>SUID</acronym> bit that is both safe and easy to use for inexperienced
+users. &kppp; addresses the security issues with the following strategy.</para>
+
+<itemizedlist>
+<listitem>
+<para>Immediately after the program starts, &kppp; forks.</para>
+</listitem>
+<listitem>
+<para>The master process, which handles all the <acronym>GUI</acronym> operations
+(such as user interaction), drops the <acronym>SUID</acronym> state after the
+fork, and runs with normal user privileges.</para>
+</listitem>
+<listitem>
+<para>The slave process keeps its privileges, and is responsible for all
+actions that need <systemitem>root</systemitem> privileges. To
+keep this part safe, no &kde; or &Qt; library calls are used here, just simple
+library calls. The source code for this process is short (around 500 lines) and
+well documented, so it's easy for you to check it for security holes.</para>
+</listitem>
+<listitem>
+<para>Master and slave processes communicate with standard &UNIX;
+<acronym>IPC</acronym>.</para>
+</listitem>
+</itemizedlist>
+
+<para>Special thanks to Harri Porten for writing this excellent piece of code.
+It was thought to be impossible, but he managed it within a week.</para>
+
+</sect1>
+
+</chapter>
diff --git a/doc/kppp/tricks.docbook b/doc/kppp/tricks.docbook
new file mode 100644
index 00000000..c2abc3bf
--- /dev/null
+++ b/doc/kppp/tricks.docbook
@@ -0,0 +1,175 @@
+<chapter id="modem-tricks">
+<title>Modem Tricks and Hints</title>
+
+<para>This section should get the fearful started on the (not so) arcane art of
+modem tweaking. The commands here are all Hayes AT standard, but all modems are
+not equal, so your mileage may vary.</para>
+
+<sect1 id="modem-sessions">
+<title>Modem Sessions</title>
+
+<para>A Modem session allows you to interact with the modem directly. You type
+commands, and it will respond. To obtain a modem session, when no connection is
+active, go into <guibutton>Setup</guibutton>, then <guilabel>Modem</guilabel>
+<guibutton>Terminal</guibutton> dialog. This will open a window for interactive
+configuration of the modem. Try typing
+<userinput><command>ATZ</command></userinput> (which resets your modem) Your
+should get an OK response. Use
+<menuchoice><guimenu>File</guimenu><guimenuitem>Close</guimenuitem></menuchoice>
+to end the session.</para>
+
+</sect1>
+
+<sect1 id="modem-profiles">
+<title>Modem Profiles</title>
+
+<para>One reason you might want to send the modem commands directly is if you
+have a set of modem configurations you want to keep, and not have to specify for
+every connection. A good way to do that is via modem profiles. Modems can have
+several stored profiles numbered 0,1,... <command>AT&amp;V</command> can be used to
+view them all. The default profile is usually 0 (this can be changed via
+<command>AT&amp;Y</command>.) The profile currently in use is called the
+<quote>active</quote> profile.</para>
+
+<para>When you change a setting, the active profile is modified. The
+<command>ATZ</command> command will have the modem load the default profile,
+erasing any changes you have made. To save changes, Load the profile you want to
+change via <command>ATZ<replaceable>n</replaceable></command> (where
+<replaceable>n</replaceable> is the profile number). Make the changes you want,
+then save it with <command>AT&amp;W<replaceable>n</replaceable></command>. To
+have kppp use the profile you want, change the modem initialization string
+(<guibutton>Setup</guibutton> <guilabel>Modem</guilabel> <guibutton>Modem
+Commands</guibutton> <guilabel>Initialization String</guilabel>.) For example
+<command>ATZ1</command> will have the kppp reset the modem and use stored
+profile #1.</para>
+
+<para>If you want reset you modem to get back to some known starting point, use
+<command>AT&amp;F&amp;W</command> to set the active profile to the factory
+defaults, and store those settings as the default profile.</para>
+
+<para>Examples of profile changes are in the next section</para>
+
+</sect1>
+
+<sect1 id="modem-hangup">
+<title>Getting the modem to hang up</title>
+
+<para>Sometimes you may find that &kppp; has difficulties hanging up the modem.
+This is likely the result of a mismatch between &kppp; settings and those of the
+modem. A standard modem uses two methods to decide to hangup: <link
+linkend="hangup-command-method">Command</link>, and <link
+linkend="hangup-dtr-method"><acronym>DTR</acronym></link>. The Command method involves
+sending an escape sequence to the modem, which puts it in command mode, then
+issuing the hangup command (<command>ATH</command>).</para>
+
+<para>Outside of &kppp;, when configuring the <application>pppd</application>
+package manually, it's often helpful to use the command method, so that one can
+exit a terminal session, and then start <application>pppd</application> without
+having the modem hangup. In most other situations, the <acronym>DTR</acronym>
+method is preferred, as it is simpler.</para>
+
+<sect2 id="hangup-dtr-method">
+<title><acronym>DTR</acronym> (<command>AT&amp;Dn</command>) method</title>
+
+<para>The <acronym>DTR</acronym> method will have the modem hangup whenever
+&kppp; stops using the modem. If you obtain a modem session, and query the
+state via <command>AT&amp;V</command>, and you can see among the displayed
+settings for the active profile a <command>&amp;D0</command>, then the
+<acronym>DTR</acronym> hangup method is disabled. To enable the
+<acronym>DTR</acronym> method, use the <guibutton>Terminal</guibutton> button to
+get a modem session, then:</para>
+
+<screen>
+<userinput><command>ATZ</command></userinput> <lineannotation># reset to default profile</lineannotation>
+<userinput><command>AT&amp;D2</command></userinput> <lineannotation># Set to hang up on DTR drop</lineannotation>
+<userinput><command>AT&amp;W</command></userinput> <lineannotation># Write to default profile</lineannotation>
+</screen>
+
+<sect3>
+<title>How the <acronym>DTR</acronym> method works</title>
+
+<para>Whenever the Data Terminal Ready (<acronym>DTR</acronym>) line on the
+serial line between the host computer and the modem goes high, the modem hangs
+up. When &kppp; opens the serial port, the <acronym>DTR</acronym> line is pulled
+low, on an external modem, you can see the <acronym>DTR</acronym> (or
+<acronym>TR</acronym>) light come on when this happens. When the
+<acronym>TR</acronym> light goes out (because &kppp; has closed the serial port,
+or something worse!), the modem will hangup.</para>
+</sect3>
+
+</sect2>
+
+<sect2 id="hangup-command-method">
+<title>Command method</title>
+
+<para>The other way to have a modem hang up when connected (used when
+<command>AT&amp;D<replaceable>n</replaceable></command> where
+<replaceable>n</replaceable> is not <returnvalue>2</returnvalue>) is to have the
+modem accept the command when a session is in progress. To have it hang up
+properly, get a modem session, and set the guard time to a short interval like
+so:</para>
+
+<screen>
+<userinput><command>ATZ</command></userinput>
+<userinput><command>ATS12=5</command></userinput>
+<userinput><command>AT&amp;W</command></userinput>
+</screen>
+
+<para>Then use the <guilabel>Guard Time</guilabel> slider in the Modem commands
+section to match the register (<varname>S12</varname> to this value
+<returnvalue>5</returnvalue>. The modem should then hangup properly.</para>
+
+<sect3>
+<title>How the Command Method Works</title>
+
+<para>When the local modem is connected to a remote modem, it is in the
+<quote>connect</quote> state, where it passes all characters it receives to the
+remote modem without interpretation. To have the modem accept the characters
+as commands for itself, one must put the modem into the command state. The
+escape code does this.</para>
+
+<para>The escape code is defined as being three intervals of time whose length
+is defined by <varname>S12</varname> in fiftieths of a second.</para>
+
+<itemizedlist>
+<listitem>
+<para>Quiet (must last more than <varname>S12</varname>/50 seconds)</para>
+</listitem>
+<listitem>
+<para>Escape character (defined by the register <varname>S2</varname>, the
+default is <quote>+</quote>), repeated three times (less than
+<varname>S12</varname>/50 seconds between each.</para>
+</listitem>
+<listitem>
+<para>Quiet (must last more than <varname>S12</varname>/50 seconds)</para>
+</listitem>
+</itemizedlist>
+
+<para>Once the modem is in the command state, you can send it commands. To have
+it hang up, send the command <command>ATH</command>. The escape codes and the
+hangup string used by &kppp; are shown in the <link
+linkend="modem-commands"><guilabel>Modem Commands</guilabel></link> dialog.
+These should match your modem.</para>
+
+</sect3>
+</sect2>
+</sect1>
+
+<sect1 id="tone-dialing-speedup">
+<title>Make Tone dialing faster</title>
+
+<para>If you can use tone dialing, the amount of time it takes to dial can be
+changed using the <varname>S11</varname> register. It gives the duration (in
+100hundreds of a second) to send each tone while dialing. The default is
+usually 95 (almost a second.) How fast you can dial depends on the phone
+company's switching equipment which handles your line. The minimum duration is
+50, almost twice as fast, and that speed often works. </para>
+
+<screen>
+<userinput><command>ATZ</command></userinput> <lineannotation># reset to default profile</lineannotation>
+<userinput><command>ATS11=50</command></userinput> <lineannotation># fastest possible dialing, use a higher number if it doesn't work</lineannotation>
+<userinput><command>AT&amp;W</command></userinput> <lineannotation># write to default profile</lineannotation>
+</screen>
+
+</sect1>
+</chapter>
diff --git a/doc/kppp/ttyS-cua.txt b/doc/kppp/ttyS-cua.txt
new file mode 100644
index 00000000..2369fd5e
--- /dev/null
+++ b/doc/kppp/ttyS-cua.txt
@@ -0,0 +1,46 @@
+From: "Theodore Y. Ts'o" <tytso@mit.edu>
+To: Tony Nugent <tonyn@sctnugen.ppp.gu.edu.au>
+Cc: linux-net@vger.rutgers.edu, linux-ppp@vger.rutgers.edu
+Subject: Re: /dev/cua? Vs /dev/ttyS? (was: Re: co-existence of pppd and mgetty ?)
+Date: Mon, 13 May 1996 19:51:04 +0200
+Status: ROr
+
+ Date: Mon, 13 May 1996 07:57:09 +1000
+ From: Tony Nugent <tonyn@sctnugen.ppp.gu.edu.au>
+
+ Can someone kindly explain the difference between the /dev/cua? and
+ /dev/ttyS? devices?
+
+/dev/ttySxx devices are fully POSIX-compliant TTY devices. If you are
+only going to be using one set of tty devices, you should be using
+/dev/ttySxx.
+
+/dev/cuaXX devices are different from /dev/ttySXX in two ways --- first
+of all, they will allow you to open the device even if CLOCAL is not set
+and the O_NONBLOCK flag was not given to the open device. This allows
+programs that don't use the POSIX-mondated interface for opening
+/dev/ttySxx devices to be able to use /dev/cuaXX to make outgoing phone
+calls on their modem (cu stands for "callout", and is taken from SunOS).
+
+The second way in which /dev/cuaXX differs from /dev/ttySXX is that if
+they are used, they will trigger a simplistic kernel-based locking
+scheme: If /dev/ttySXX is opened by one or more processes, then an
+attempt to open /dev/cuaXX will return EAGAIN. If /dev/cuaXX is opened
+by one or more processes, then an attempt to open /dev/ttySXX will
+result the open blocking until /dev/cuaXX is closed, and the carrier
+detect line goes high.
+
+While this will allow for simple lockouts between a user using a modem
+for callout and a getty listening on the line for logins, it doesn't
+work if you need to arbitrate between multiple programs wanting to do
+dialout --- for example, users wanting to do dialout and UUCP.
+
+I originally implemented the cuaXX/ttySXX lockout mechanism back before
+FSSTND established a standard convention for the use of tty lock files.
+Now that it's there, people should use the tty lock files and not try
+using /dev/cuaXX. The only reason why /dev/cuaXX hasn't disappeared yet
+is for backwards compatibility reasons.
+
+ - Ted
+
+
diff --git a/doc/kppp/wizard.docbook b/doc/kppp/wizard.docbook
new file mode 100644
index 00000000..6f26e711
--- /dev/null
+++ b/doc/kppp/wizard.docbook
@@ -0,0 +1,117 @@
+<chapter id="wizard">
+<title>The &kppp; wizard</title>
+
+<sect1 id="starting-the-wizard">
+<title>Starting the Wizard.</title>
+
+<para>You can start the wizard from &kppp;'s initial screen. Start &kppp; from
+your <guimenu>K</guimenu> menu, where you will find it's entry in the
+<guisubmenu>Internet</guisubmenu> as <guimenuitem>Internet
+Dialer</guimenuitem>.</para>
+
+<para>The following dialog will appear:</para>
+
+<screenshot>
+<screeninfo>The &kppp; dialer startup screen</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-dialler-tab.png" format="PNG"/>
+</imageobject>
+<textobject><phrase>The &kppp; dialer startup screen</phrase>
+</textobject>
+<caption><para>The &kppp; dialer startup screen</para></caption>
+</mediaobject>
+</screenshot>
+
+<para>It will probably not have any entries to begin with, and that's what we're
+about to do now.</para>
+
+<para>Click the <guibutton>Setup</guibutton> button to begin setting up a new
+Internet connection.</para>
+
+<para>The wizard will offer you three choices, <guibutton>Wizard</guibutton>,
+<guibutton>Dialog Setup</guibutton> and <guibutton>Cancel</guibutton></para>
+
+<screenshot>
+<screeninfo>The wizard asks you what you want to do...</screeninfo>
+<mediaobject>
+<imageobject>
+<imagedata fileref="kppp-wizard.png" format="PNG"/>
+</imageobject>
+<textobject><phrase>The wizard asks you what you want to
+do...</phrase></textobject>
+<caption><para>The wizard asks you what you want to do</para></caption>
+</mediaobject>
+</screenshot>
+
+<variablelist>
+<varlistentry>
+<term><guibutton>Cancel</guibutton></term>
+<listitem><para>Choose this if you really don't want to be setting up a new
+account right now. The message box will go away, and you will be left with the
+dialer screen as before.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>Wizard</guibutton></term>
+<listitem><para>If you have a fairly standard modem, and use one of the larger
+ISP's for your country, the wizard will probably be able to set you up
+immediately with a working Internet Connection. Try this first, before you try
+to set up the connection manually.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guibutton>Dialog Setup</guibutton></term>
+<listitem><para>If you don't succeed with the Wizard, or you just want to do
+things yourself, choose this. The wizard currently is only useful for a small
+subset of countries and Internet Providers.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>For the purposes of this chapter, we'll assume you are choosing
+<guibutton>Wizard</guibutton>, and the dialog based setup will be described in a
+later chapter.</para>
+
+</sect1>
+
+<sect1 id="finishing-the-wizard">
+<title>The Rest of the Wizard</title>
+
+<para>The first screen you see contains just introductory text, explaining the
+things you read about in the first section of this chapter. Press
+<guibutton>Next</guibutton> to move on.</para>
+
+<para>The second screen asks you to choose the country you live in. Not all
+countries are represented here, and if the country you live in is not listed,
+you will have to press <guibutton>Cancel</guibutton>, in which case the <link
+linkend="dialog-setup">Dialog based setup</link> will start for you to continue
+with.</para>
+
+<para>On the next screen, you will be given a choice of Internet Providers that
+&kppp; knows about, based on your choice of location in the previous screen.
+Again, if your <acronym>ISP</acronym> is not listed here, you will have to press
+<guibutton>Cancel</guibutton> and do your setup in the <link
+linkend="dialog-setup">Dialog based setup</link></para>
+
+<para>You will now be asked to enter your username and password for your
+internet connection. Please note, that for some <acronym>ISP</acronym>s this
+differs from your mail account user name and password, so make sure you use the
+right one. Choose <guibutton>Next</guibutton> to continue.</para>
+
+<para>On the next screen, you have a chance to enter any special dial prefixes
+you might have - for example, if you must dial <quote>0</quote> for an outside
+line, or if have a prefix you can dial to turn off call waiting. Choose
+<guibutton>Next</guibutton> to continue.</para>
+
+<para>And that's all! If you want to revisit any of your choices, you can use
+the <guibutton>Back</guibutton> and <guibutton>Next</guibutton> buttons to move
+back and forth through the dialogs. When you're happy, press the
+<guibutton>Finish</guibutton> button, and you're all done.</para>
+
+<para>Of course, any of this information can be edited at a later time, from the
+&kppp; Configuration dialog.</para>
+
+</sect1>
+
+</chapter>
diff --git a/doc/krdc/Makefile.am b/doc/krdc/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/krdc/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/krdc/authentication.eps b/doc/krdc/authentication.eps
new file mode 100644
index 00000000..4b730630
--- /dev/null
+++ b/doc/krdc/authentication.eps
@@ -0,0 +1,219 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 380 183
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sat Oct 11 17:09:39 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 637]ST
+B P1
+NB
+W BC
+/mask 4416 string uc
+î½*¾î1:7*¾-M.õ0*Þåñ/:ÞëA*î÷º¿*öõ85<ØÀÉ8*I°ØÉÑ9*1°ØÉÑ9*+ÐØÉÑ9:ý+Qú/îYäY:½*Pú/
+îMèYÆÖ5.¾1íý5Üýí»ý½úüýöûýïùýáõýÅíéÝ*
+d
+/sl 34960 string uc
+î½¼**ûEH*<NÏû@Kååé6JÑæÉìQ¼Cîׯ¹F*VXßÖåYºÝÐKJ,9öéÄ1*GE¹ýüú÷íöÒñ×,ÈG´éÜ1*ú5¼½û
+ùÕÆYåø?D*õ=Ý6öêé*µ´çõ9G¿ÌæÒTÕÈOɲ9ñ5ÌGÝIõÕÕK²=<Z·/áHIÚ/öÝÐA+9¶0û´Þù=Ýø@*ÚèÕ±
+õH@»EµLÚ¸æáFæÐ;¾·õõYÙ³0?Ø15Æ××-ÚÞòEø<×áäνƴýû±ÏÜ/*9UÙ°äÀé4OÇ<ÀòøB1ø².ZõüÖÏ°
+ÛÝSáÝîùõ½ùàÜN²ÔA*¾59í¼¼ùêÁͶ¾°ãÈ·½îO9-¾±Ë7ׯÚV¿ÐN2óÛ»Û-U;+÷ý½ÁÂ,ÈëÝ»æÒµ<òÔÄÂ
+³øå**VåÉ,Rôúüû=@»3ïT9*¾õ»ÛG¼÷êÏIQåSí»;,RóõüÁÅH0*íçøìܱÏô6ÇüS±ùä.TïâùÜÕÙîR,.X
+ßõFZà¾èõ0ù²3æýý¾X±ßÏ.ZÔÖáíõ½ÝûºÐïîFXÂLè-ZîùEWD37÷ñòéé½YHºõêGùòåËE½H¯4ÞÚùù¹ÜA
+úMôÍ»Zêü´´ñîõá1YGHY8FôØAìëâõå4ò¯*öÍÁûÐÛTÒã¿@ìÖ×CÍÊJú0@¸ö½ùÀÕÛî0,æýýý8¸¾,èÒï»
+ùMí@+*¸Þ*õÖî0,úTù÷½äÀNÊOÆ>˲ÚF1*ÒL;ó+ÞìûçøºØ6òÚÄúóòèãõÍýü»YYHùñãÇÝ@D¯4Öìûùí5
+ô/ÞY6Ùõë7Gºúùé¹ÅâÃ=ÝWõÃ3Öûû¿øº7*ìÓ°ÒLO0ºÅ7/5ùï33ÂB·ÍJýÕÕâñQçôü¼îNá-úí÷øG1HY½
+°ôç±°÷úãMHLJýýùºß-Qâ*¯Äû½öá5=¾·ÛõíI½×*GÃY¸¯ß/F.3ÖÚì÷1½Î0Ìù½ÇÃöãÚç;Ö¹Â-ĵúûÉ9
+GÞÃPæÍÉßñí9½CL2Þ?OFRÀê,*Oõ16=çED,*0*XçÆAXüúøùûóåõöé½QÜúöà>=P+AÙ½½áïøõI6Â@*öÑ
+À¿EýüܼúºÜ¸íßÅ5Hø*/RýüEÍ-¾é6ÛÑâ¿øàGMÚè°ºL,+SüÑñýZ5ëúúÝ1ÃäýXÉýý½ó8ýù1Úïý÷ðöïý
+ÃSÓûI´7+*¸ß-AâJµìNøIºYAPJéýõPçEMá,0ÚùPèÀ+Âãýµî*5Yú÷ýíT-UýÝÒ¹ñ*ÕT+ïÌú½¼ÚE1¶Ï3
+Ú÷ýäÚDï¹²ä;0î´5°-*NÙý´ÇHÍæRçÉ**îÆÄÒêïÞÅÉÅILÃë½1üù¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?
+½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×R¾øõÅ÷F*úâäH1è=AëIàEöà=<BÞòý99T0=ûúÁBÜöäÉûýYç6¾Iùý
+ÝÏöýU?±LýE9AÄJ*ߺß-1â¾+ßÂSñýûÅXʾØýYDêÉ-Á/ÎKýýFÓ6V¶/1ÈçÔÊýß9E³Zûý9,·8î41îÐZý
+ýE¸ñ*AD¿åýµóUNÇû0C8Tà¿<â0*B·½Û6³HÏ;+*JæÆ**îÆĺåï×·¹Yíü»µÝܹì×ÅõÊö4/0çXÐJÂ*Ú9
+ø,*ìÎ*õìÝÝÓøó·H**ðæ¹ö5Ù5ÛMÕ?ØV>ZùùÝV÷F*ÚéÑöäßÂFôKÞöP.àµBÖúæÑÏV×ý:Êæå>Ü-LÜB*í
+ýI;ÄAýíå²ÄîÂ1XÆ¿ÔV×òýñÀXʾÀëûýýö²Á/VôûÝ08@Æüý9ß¿¾²CÆ´è:ì5RÌ-Fá¿SÄE1¶Ï3¯²Ñ=A°
+îÊ<T3°-*éÚ½VÂãJKMôEù÷óåÒ¾ôEDð7¿ÑÌ»Ûú÷òûº÷é±Ñ0ÜïßM>¶ZRCÏ*î6ùLïáåº**ôYöíÙYæ5è0
+HúIÄÃ,Ò½½Çõ+Þ·°ÔøBïÁ6AH4¿ö6A÷õýÜ8O¼Õ99+,.ÆÎ÷¾5-îïýå4çñ½ñÀè-XöÞ3CJÇÐý±Ûòâ.ºI¹
+ÃýÍÍõý½ñäÝòÀÑ-õî¾¾üAÜè¾Vý1×êï81Ì7ÞÃPÖöý¿Ãº¿6Q?Ïî;×*Jáûí@F÷ÂÄ2¸öðMÄ*øMDðý¼É´7
+û¹öïGúõæWÉüû9ßE>¶,ÖõRJνI<¶?/F=¼úÔÚ¹µ÷**îFìÞ±9Y²Îæ·-AûWßL+¹Ý½ãÅå+Þ?ôëEFõÒ³/5
+Ê5ÍÂJºÄ½6Cú½Ç9ûüµ°UÔ>ÙHîà*ºîý°?Àüíð<úæüýW9YöÞ+CZ¼¹»ýÅüçÖ5=Þ¼½ÄÂ÷½öLÏ0´÷½Ê9-5
+ìÞ¿ýýÍ3ãèýK´D³îGßÙ½4±NÏÆYïÞ˲Ò-Úæ.?úIòÆ?ÝÔ·FEÆ=OÆ7*ZòüQ3ÄF/TD,êMÍÞùUDðð¾Í´ûº
+¹õíÛùôåWÍ0XÌÞE>¶°YõRJ̵I<¶?/êðú÷ÔÐQI÷**²ÛÙß±õÄH¾¾ÛMõWúWZ+5/Ê÷üOCH1*Ý6HîâXóüG
+SæOº<,ïCÌ;MÎùß1â2?0åY½ù¸ñ+Q0ÞÙLåýÛçJèB<ÜôýIï¿-°Þý÷UIÙçÐÁAâ¾×Ýõ9ýóXÊL6êâõñ÷7@
+F¿ÁÆ,òýGSJêTDFNØåõíIÝÙ×MF¶>7Ó:1D¿EXGÌý¼÷õÄ;Ì7âË<N@*æ¹ýÈ,IHÐ;èüGú±DðãÀÙÄûú¸ôë
+Gùóå¯Ù8ÜêÝÊRîÞá8Ï*ÍúüÛ?ñäKH**ýÅ:öì×ÁQ1Þá½ÅXºöÀØü?4Öãû3Qº7*¼Î¹÷뻵0Ç8>º³,,2ÎJ
+*åQÆ;¿3ׯ<N0¯T9¶°*2W<KÈÒ/9ƳÂ,-8öÞ+CJJÀ@/MB<îÞ.>=L¿4ÛðµàÞ4LTNîZß/F.@CJ¿>O¹;7
+FÎÕNÀ0KLóP?ÌOM?âÃZJ³Ê.3ÀÒäïêJì>KT,Î7ñõAAZá±D4õ»NM@5¶ìØÍ2Õï0H5ÝÚR*Ö9ûÜK+0SÛ¸U
+àõº+*XÊSîܹ=åHHÁÕ̺óPß.Ô3ÞÒûÑQÂöB*öKÖòMÁVÕîó6VÎ.àÜôº7µ:Jãô1ÛWJÄÕ²N**¶PMÔ3öî
+ÜÍBÉ39ÜÇÜÒR:D6ÜÝÈ,ìöú·UÜ»Uº+*8Y>ÏÝÁ9Í8HÑÍì²ÒMé2HJ+-ÜÝêÀÕ+Þ*8ºÅî÷5êî.àÜ´Û714¾
+9Xíͱھ3U¿ÌéÆ9,8ÚöðæÑUõéÝÍÝÜF÷Kõ°P+ÓRÚ¶U×±I·HµæWеJ;5ýÊ×ÔáÁÆÔ¹09Î*ÐëÍüá¾Ë+*,
+2ÎÛÉ-5ìý·Ö¸,9äKMX1¶îßÅEµüÚMÝüÛúôëóÒÉC/>LíÜW×´ûPøºÞ·ÎöÎ;Áä7òÁSÕ¶*ÍÎ*ÐìýºL¾Û9*
+ú³ýüýÐã=öL¿XéÆÎ<ñ6*ÂÍ·CÑ*AÊGÙ?çÐ1H+*AöÍÛ±ÚR¿L/·G±õÍÞ=>Î4¿õÅÌÐÝÔ1*1¶¶ýü<ÁÇéõÂ
+¿Ìé2ÓÑÓç;*2WóÐP+VÛØ?åÌY¯ÍýÙåÌF¶ðêë-ýXË*±âÛ½½ä;÷³¹*¶¿RÓõíY°A´*íÓ-GÔ;èµìÙM*:ñø
+OË,2D²UÈ?Q³¾ÜY¯úûù±ÅêBºëî*J¹;Ý8íßÌï÷ØÔìܼÙÒ³Úº×?áÄñ°JÚµ¯¯ðûùG*º±ËׯMAíܳðDH
+¼êÈZßÇC¿éÁÅÌßÏNûð?ºóô´*öÔD?@-ÔêضàÌÜ»è5ñ=VÖ?ݼÙO0*Ì7E³åÈ35ô5öèë*îݼ+5´Êײé*Ù
+êX²7L:¼ûÌ»+ôí9*Üý»»ýõûüíùûÝõù½íõýÜíý»Ýýù¼ýõÛð»ÏÄ2=ü¾ùð²CôÛWÕNļççÑ´½Ü»÷î7Ã,Ý
+ëD°ìÌÚàUÕNKDäöÕÙAÅBжîÛÅ5Xê/ñä4îÚWÄÔ3PñÜÚ»Ïò6S1»÷òëü±74Ù±ºÌG¹êð<K=/ùÁÙAICÕ¶î
+Ù˽ñOÃíÓçÕï³=,äB=½Êü<¼ãµYX¯²îÙWí0Ö:ÑVH<UÞ/A83BêÈÜûøíôã=ØÜë¸ñç+IèÇýCüÂU°ßA°Åû
+Å>ÉíëÜÚßåÔEÑ@ú;NÔöÝKÊWÓ¼¸¾,²òÐJíëܺßáËKÍIIHMìÖýPÆNÁT¸Ï,NÈÒÀÝëÜVÝäÆå9*¶ÙæÕ´ÒS
+Ñ;ê1T¾ä4<òÕë5L>Õد÷;Y¾¼:¾XX2°NKÉYÞ*+бöMÙA9ÈíìýáMÅAíåÉõI½ú÷ñý×Â2AǺK->W<¿íêÜ
+úöÁKõÙý5A¹8Y¼ûãQéñ9ú°+-ØDô,1æåHå¼×»÷Ã5×ÙQÉ´YñHÛùöñϱ/Tôî5×ÙÂêÃ8:Ö´*û¯ùðAE89I
+µTã¼G³ðãÕÁ9íü°Ý1:Lûïåýô.¶Ûö+°»ãÍ9´YÌ@âÙíëÔâê×õû¸¹öñä×ÅõðíýNµÌâúïSû¼UY.+óVáçý
+ØA98ÐSò½?4ôë=íûÈÒììàÉùòìáÉIÍ:ÚµR÷»÷ÅCÜB,ص1ÖêÀ+*NÈ;1Ó:D:ÝGâ46SÁâ4ò°9ùð6Y<QëÍ
+UóÔûëàYÉPÃ3E¶MìÚ¸ö¿Yú8E´äçÙQö,328@WÑÉÌNW°ßùØIAêçܺãÈÇ»ùñS»·A»¸ÛÇRóÛéP-Ø@ÜÚ·ô
+ÁYúí׶âåѵÓȾ°°Ç<Ý6GZ3+*JGÞS-FT¾5>¶;-1::,îÊ5ìZßË+F+Á-JRé¶</1BîPBXâÞ+-ÚÚ¿Eض³
+13Bã?¼F=ÅT¾ûêɹºÙÙåµãÕõô±ç¸¼õUØSPFÈR°¯5Y8Û¹9Yܺ÷JųÑÉòIUEÈÞÔH÷,¯°Ý¶AOÕ7îP+XZ
+ßÇ?F÷Ëñ>ÆÞ9X6æ7-ÛÐÐMÇÆJ-8TãÏGF´ÄMÈ·N8Q6ZIùðAñ8áêü¶ì×ÓäW¿¿úß°LJ-PüËÚ·ó+ÝXܺÕÝ
+¼»UQtܺÇ2ÇC°ùñù¸K+1=ôä.ìíé·4ÚüÁÑN¸C9åáõî:Ì·ãÓGF´ÈÙì¹Ö/¹×A1÷C¼ôܯåTXÍ»5ÀÛKK
++,8=åÝÛ¿AµÜ»îÉÍIYFàWÙü:¯Nàò¶@ÛYÂÊ-*ÕõóN/è-XS½0ù¸ß/µÖî.ÂìZÞ+:FÜÕEA¶Ô3õ´îRIT.X
+ëßÏ2Ú°ÈÓìɵVÜÚZó¸·×MÎÊJ·*,Ì7Zâñ±5µüÚ²9Iöãó9ÛCÊÍ@¿È+èç÷àÛÊÜ÷¾=7¶0.÷êéëS**1Ç:-
+0ÚÒ¾Éù¸·:ǾßßáF2¾?ìÉMºGý8ãð2?Kã×5X9îÙÖÌÓ9C´íá÷ó+ÝäüòÊ/9¸KÌùÈKJÍìS,ù¸È,C:òï»
+ÛJ¿Á0¶¶-1ßZ>:+UFÁÔÉÇ×л÷Ç´99=>8B>îö+1¶Ì½»ÜÅúBóëXG¸ñêßÍQIÏ>W?SYFTM*î¾QÌDÞÏ,F;
+ͱ7¶ÁFÅ´ñL,X>Þ+8F+ÀÙȸó6AÐîÈ2ì7â32FçÃMP·=UA4îÎ@X÷Lìü¸¼Y¸CK¸ÅBï¸Ð+¼ÚèåɹYW6Øñ
+HçùùóíéÑ>LSO»6éùÀÚÏ=YBâÛ5÷½ß÷»Sôê±ÄÀðóÇ/öÐóI൸óXèÌ>Q²çZÕIÚÐÖ¹µÝB×C»³Ø»ÛÐ*Å·
+úßóñÅ9OéåÜÊõ7¹LÕ:4ÌÍôù<UÑÕôÙAí6@íÞóíÕQëÒàâçS¶*ËFÓç¼WÐÝÇ;Ó»·ËÜ5ÔÅíÒêñ0ÒOQÞIÓ¸
+ãµYܯù¹ìçÙQÜÍ+àÓ8×ÕOYCëÉ1Óù¯ðöáóýµÅÖÙFóµçUPùð9ñ@>Yû¯³9OBÃÍUÜ°ØAµ2ÚCøÝÓÉQÄX3L
+òóëÉAÂFëµðÁíĵô8±åùñëɵH÷=OõÔ½YMŸ+øÜð?Í»·Â»KÇIûùø²ôÏDIÛÝÜ»·ÍGÏåìEïîÇ:éýÀÕÂô
+ã×ýéUØö¹µì*HííÜFù¿Ê³±Ùúº?Jøûë5°ÝµûëBI6ÜðØAâ8ãÙKVÝÞ»Cô¶æ6ü-õóËßÞùÝÕõýÜíý»Ýåù,
+d
+380 92[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 4368 string uc
+*Jà**9ÜýAºýÝùüýöûýïùýáõýÅíý9ÝýI¼Oý,%
+d
+/sl 34580 string uc
+äÕí´êÝI*Ö´¼ûØøüõóûÝõù½íõýÜíý»Ýýù¼ýõû:í5Rû7**ýA»Yîýí*¾9½Y*ÞùÝÕ@¸ÅÂô/*6*NºVS¶ß
+ñ×Õ²Dóãëé/ýHT¾L¿--¶Z-*Þ+/ºXù¸ÐVÅ4îæÜì.êC-ºQõG*¶òZ1>îP+ì2Þ?-úVõ»ÀZã*1Bîëëé;TÛ
+Lí-+öðôóò¸ÍGÞ3-úWù4иZÚ/,A4îâ1ä0ìRÞ3Á»PõÛÓ/ºGù4ÏW,öüôó7ÜõØ>*R»7÷øïõó·æéÐõóÏÖ
+àùÝÕõýÜíý»Iéù,T97*ð×AöýÏ+JHùøò>îûíéù½íõýÜíý»ù-Aö°ÏöFë×MQ°*Þ¹ôêï.ÍæãÝèܾä:Ó:¾
+.õ³.*²üSù,ðXÂTÐù<¹âGè-ùÜH¼û¹+*ÀÝüùüÝAJC@ÃÎûGO=Ïé¯Ï/B*ÈQøÞÛÁQÝÜýùíÍ5ðßÕ**ìEõ
+íÚ½1åì»õí5J8Ç,@5ΰ¹Ð-P,@Á6Jï-»îçÁ1ýûÚ»ýøS»ùóÕ**8H·êÓ±åÌëEJíúù°Dûö×ô*ΰ¹Ð5P°ì
+úöÏ÷J9FõéØYå@ͽ½Ø½½ü?*¾ÝI½ú²æÔ±ÝPÕÉYíøùðë2;øø¸´éÖǺüô=-*YíFóåÒ±Ý0U2;¸ÏLìC´éÍ
+O½ÝÝÝòå½1*îûõÁÝË·ñéÖÇ°èäÙüܺ>PÊ4áPÛ·²ðHýüê*ÞãõäÊ·³êÃC3PÏ8ò¿1¼7D°åéëéÍì8ж+ÕÍ
+¼öãÆKÝ8V.-íøùðÒRÏè5Ù±áËøøåH.*.*Ò-ÚK¾D*Ž¼¶ãÄGáìEæîJ9ºWéÉ1Y¼¼Ü»¿YýC*ÊHÚ-¿á6¶
+ò-8Ýû÷ß;9ÜêDÓÓ¹¹éíÉñàÃ5VèÇ+M@âãIÑÕ=.¶°*A.ÞåÉ9üBîâÒß0DFæ-ÍTø¯ÝÅ¿Á9Yèѽ/**Þ7,N
+°*źÞéÅ1ýÙ¯ÝÅOÑÒ¶³µû»·±äÊ+7»öXà²Eùøµ***Ú¯¾-,¶=*ìGùëÂõ887²Îß¿HÖËÎ1é7¸JëáÁ½.**
+ÞÏ.**±+*JFÞÏ0ÚK¿=*:ïÙ1TJÛÁKíºÙõõãáÃ5Að±ß·1=ëçáÝ+**îÄ+*¾=5¶Î*¼»¸ëÁñ4üF°Îß¿HöÅ
+Í-á?FE¸óåL+*FÁ¾B**Z0îR-8CÞ/+¶óæ±5ÄEöñÅ˽Y¼íÜ»1G1¼EBßPÜû7***µ6:ÆøÆ/ºùôåÇá8ú´Z
+ïúZÎß¿H¾ÀM1ôD÷ñçY+**¶Ç+ñ+A6îL18³Þ6*GQüøÎ,>êúõ³19Hâµ*ÃTý¼ü0**¾E0¶J*µ,¾¾ýX»Ê.ö
+/DFæÁ-¸Jܽ-½/**ÞÏ..*Fö¾50¶¯,ÅDZ0J¼ù¼¾4ËýÜXÐNÇîF-´5ÖÎìû´Í8ÛçÇ*öìÛS-´5Ö±¶¿01ø*
+àGÎ-îû6**¾±22-¶ã*Ÿî03À*8Î,Âéúù°H»ø²@*ãúÈÌÛù;***Q2î0,82¾çÌWØíZ<Z@æîJ9*ØÎ-û¹Ø
+ôëÚµÝ,**î,+ä+Я¾-,¾ÚµéHûµÎ,JéúùðWÙ´ëó·îÙ¼SòéØY+**2*R1¶Î+QVÞ¯Ý8ûõëÞ͹/DFæ-öÓê
+äZåÔGùC*ÊHÚ¶*Æ»,:<îL-ìBJSJ7*çÌ7¹¿8úèúùð»ùô?OD¯Û¾/²ê÷+¾Ä0VA¯çÐ,78ò+Ü,»BìÝCèK<
+CÎÏ+áG6øÓäÝAÍF·°NÓ?ý7ú4ÎÄW96ÞS<÷êÖQ=0V·³N¿01øÞFϼʵæ+â¼³.·îK492õÿ÷HPëSñíÉÕP
+Ëø<äÏ79PÍWÝÌ6*ú´ëÔ=M8Û÷<L˸ó-»îÄRµ8VBòé×O´ê×óK,?2ùÐÒ75ìÚØÏäÝAYÌF÷²YS½ÌÊBÍÌGC
+*¶íݱ½0ÛDê²ÂF÷ð1ø¾KK@7C¯åÓ±ÑÁN°Þ·ñ¼»×°çÇ;³ü»÷öôëõÛNCóçòC²ìÇ+¾ýäÌFµëÌCóOôëŽÎ
+1üWTèÏ?±@ûØæÔ»3øðÝ¿é0G¸³ÓÖNXëW½Y8ܽýüO8TD×Ò´GE*ZíèáÅäØÆâ:-*ýJ9*ì-´MÑYéÌWì<,G
+°¶AõÔÝAßì¼NÈ4üÔÅ4?S<-*ÓLÃÈHí.1B*ùQø*@@Ù¿è÷ãW,?Z×»ùôGJå̳WVºííÉZÅI@?±+*úÅø¾è4
+±=L´=,Þ²L?òØÕóñ˽Y?Ý÷4ÞËø0+îóãYAÜ5õëË*¾àÛÙöóðé¹ýÜ:¹öóïñÍðÛ6½úóßÝá-Ú8Û¶ìØõ,*Â
+Ù¶òåòÓçõÓ¹Nðûíéù½íõýÜíý»Ýýù¼ýõûüíù÷ê5Þº**ZñÑ-¾øÔ¸Y?T>SÏJ½¼¹*JFµäº¯Y4=äËJÁ,ûô
+**»ìºçÐã¸Y5S<OÊÍÞýõÍ80=OÇ;黳áÄ9KÄF÷ë+*ÐNCðáÅóÜ@¯àÂ4IH*ÆÃ6-*è»ýÞ**Uí:**%
+d
+380 91[1 0 0 1 0 0]sl 8 mask 0 92 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/authentication.png b/doc/krdc/authentication.png
new file mode 100644
index 00000000..73b203a7
--- /dev/null
+++ b/doc/krdc/authentication.png
Binary files differ
diff --git a/doc/krdc/close.png b/doc/krdc/close.png
new file mode 100644
index 00000000..2415dfc7
--- /dev/null
+++ b/doc/krdc/close.png
Binary files differ
diff --git a/doc/krdc/index.docbook b/doc/krdc/index.docbook
new file mode 100644
index 00000000..574bb326
--- /dev/null
+++ b/doc/krdc/index.docbook
@@ -0,0 +1,750 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&krdc;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE"> <!-- ONLY If you are writing non-English
+ original documentation, change
+ the language here -->
+
+ <!-- Do not define any other entities; instead, use the entities
+ from entities/general.entities and $LANG/user.entities. -->
+]>
+<!-- Based on kdoctemplate v0.9 January 10 2003 -->
+
+<book lang="&language;">
+
+<!-- This header contains all of the meta-information for the document such
+as Authors, publish date, the abstract, and Keywords -->
+
+<bookinfo>
+<title>The &krdc; Handbook</title>
+
+<authorgroup>
+<author>
+&Brad.Hards;
+&Brad.Hards.mail;
+</author>
+</authorgroup>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+<copyright>
+<year>2003</year>
+<holder>&Brad.Hards;</holder>
+</copyright>
+<!-- Translators: put here the copyright notice of the translation -->
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<date>2003-09-27</date>
+<releaseinfo>1.0.0</releaseinfo>
+
+<abstract>
+<para>
+&krdc; is a client application that allows you to view or even control
+the desktop session on another machine that is running a compatible
+(VNC) server.
+</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdenetwork</keyword>
+<keyword>krfb</keyword>
+<keyword>VNC</keyword>
+<keyword>RFB</keyword>
+<keyword>krdc</keyword>
+<keyword>Desktop Sharing</keyword>
+<keyword>Remote Control</keyword>
+<keyword>Remote Assistance</keyword>
+<keyword>Remote Desktop</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>
+&krdc; is a client application that allows you to view or even control
+the desktop session on another machine that is running a compatible
+(VNC) server.
+</para>
+
+<para>
+You would typically use &krdc; with the &kde; VNC server, which is
+&krfb;, since it closely matches the special features of &krdc;.
+</para>
+
+<para>
+Please report any problems or feature requests to the &kde; mailing
+lists or file a bug at <ulink
+url="http://bugs.kde.org">http://www.bugs.kde.org</ulink>.
+</para>
+</chapter>
+
+<chapter id="what-is-RFB">
+<title>The Remote Frame Buffer protocol</title>
+
+<para>
+This chapter provides a brief description of the Remote Frame Buffer
+protocol used by &krdc; and by other compatible systems. If you are
+already familiar with Remote Frame Buffer, you can safely skip this
+chapter.
+</para>
+
+<para>
+The high level implementation of a system using the Remote Frame
+Buffer protocol is known as Virtual Network Computer, or more often
+just as <acronym>VNC</acronym>.
+</para>
+
+<para>
+Remote Frame Buffer (or <acronym>RFB</acronym> for short) is a simple
+protocol for remote access to graphical user interfaces. It works at
+the frame-buffer level, which roughly corresponds to the rendered
+screen image, which means that it can be applied to all windowing
+systems (including X11, &MacOS; and &Microsoft; &Windows;). Remote
+Frame Buffer applications exist for many platforms, and can often be
+free re-distributed.
+</para>
+
+<para>
+In the Remote Frame Buffer protocol, the application that runs on the
+machine where the user sits (containing the display, keyboard and
+pointer) is called the client. The application that runs on the
+machine where the framebuffer is located (which is running the
+windowing system and applications that the user is remotely
+controlling) is called the server. &krdc; is the &kde; client for the
+Remote Frame Buffer protocol. &krfb; is the &kde; server for the
+Remote Frame Buffer protocol.
+</para>
+
+<para>
+It takes a reasonable amount of network traffic to send an image of
+the framebuffer, so Remote Frame Buffer works best over high
+bandwidth links, such as a local area network. It is still possible to
+use &krdc; over other links, but performance is unlikely to be as good.
+</para>
+
+</chapter>
+
+<chapter id="using-krdc">
+<title>Using &krdc;</title>
+
+<para>
+It is very easy to use &krdc; - it has a simple interface, as shown in
+the screenshot below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>Here's a screenshot of &krdc;</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="snapshot.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="snapshot.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; main window</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+If you click on the <guibutton>Browse &lt;&lt;</guibutton> button, you
+will get an even simpler interface, as shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; main window, without browse functionality</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="snapshot_nobrowse.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="snapshot_nobrowse.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; main window, without browse functionality</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+If you click on the <guibutton>Browse &gt;&gt;</guibutton> button, you
+will get the normal interface back.
+</para>
+
+<sect1 id="compatible-versions"><title>Connecting &krdc; to compatible servers</title>
+
+<para>
+&krdc; is a client, and it needs to be used with compatible
+servers. There are three ways to connect to those servers:
+</para>
+
+<itemizedlist>
+<listitem>
+<para>Directly typing the server name (or IP address) into the
+<guilabel>Remote desktop:</guilabel> combo box.</para>
+</listitem>
+<listitem>
+<para>By using an invitation that you received. &krfb; uses
+invitations, and can send them by email.</para>
+</listitem>
+<listitem>
+<para>By using Service Location Protocol browsing.</para>
+</listitem>
+</itemizedlist>
+
+<para>
+Let's look at each of these in turn.
+</para>
+
+<sect2><title>Server name entry</title>
+<para>
+If you know the host name (or IP address) of the server you want to
+connect to, you can enter it directly into the <guilabel>Remote
+desktop:</guilabel> combo box.
+</para>
+
+<para>
+If you want to connect to a machine named megan, which is
+running a <acronym>VNC</acronym> server on screen 1, you can enter
+<userinput>megan:1</userinput> or alternatively as
+<userinput>vnc:/megan:1</userinput> into the <guilabel>Remote
+desktop:</guilabel> combo box.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>Entering a hostname into &krdc;</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="snapshot_vncentry.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="snapshot_vncentry.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>Entering a hostname into &krdc;</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+Similary, if you are using a <acronym>RFB</acronym> server on that
+machine, you can enter <userinput>rfb:/megan</userinput>. RFB does not
+need the screen number to be specified.
+</para>
+
+</sect2>
+
+<sect2><title>Using an invitation</title>
+<para>
+Within the &krfb; server application, it is possible to send
+invitations over email (and in other ways, although email is the most
+useful). If you receive this type of email invitation, you can just
+click on the link provided in the mail.
+This will start &krdc; if it is not already running, and connect to
+the server specified in the invitation.
+</para>
+
+</sect2>
+
+<sect2><title>Using Service Location Protocol</title>
+<para>
+The third way to use &krdc; is to browse using Service Location
+Protocol. A list of compatible servers that are registered with the
+Service Location Protocol system is shown in a list in the center of
+the main window:
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; showing service browsing</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="snapshot.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="snapshot.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; showing service browsing</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<note>
+<para>
+If your main window doesn't contain the table, remember that you can
+use the <guibutton>Browse &gt;&gt;</guibutton> to get back to the full
+&krdc; window. Also, not all compatible servers support automatic
+registration - one that does is the &krfb; server which is part of
+&kde;.
+</para>
+</note>
+
+<para>
+If you click once on an entry in the table, it will be selected, and
+you can use the <guibutton>Connect</guibutton> to establish a
+connection to the server. As a short-cut, you can just double-click on
+an entry, which will also establish a connection.
+</para>
+
+<para>
+While Service Location Protocol will usually detect new servers
+becoming available, you can also force the &krdc; to scan for new
+servers. This is done using the <guibutton>Rescan</guibutton>. When
+you ask for a scan, the button is disabled (greyed out) while the scan
+is being performed - this typically takes a few seconds.
+</para>
+
+<para>
+When using Service Location Protocol, the concept of Scopes is
+important. If there are a lot of services being advertised, it can
+become unwieldy to scan through a list. A re-scan can also produce
+a lot of network traffic. To avoid this problem, administrator's can
+configure Service Location Protocol with a set of Scopes, and only
+register services in certain scopes. For example, a host may be
+registered in the "third_floor" scope and the "logistics" scope, but
+not in the "engineering" scope or "maintenance" scope. In smaller
+setups, everything is only registered in the "DEFAULT" scope. &krdc;
+supports selection of a scope other than "DEFAULT", using the
+<guilabel>Scope:</guilabel> drop-down box in the top right hand corner
+of the main window.
+</para>
+</sect2>
+
+</sect1>
+
+<sect1 id="connection"><title>What happens when you connect</title>
+
+<para>
+No matter how you select the server to connect to, the next thing that
+happens is that &krdc; asks you about the network connection to the
+server, as shown below:
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; connection speed selection</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="snapshot_connectionspeed.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="snapshot_connectionspeed.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; connection speed selection</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+There are three speed settings:
+</para>
+
+<itemizedlist>
+<listitem><para>High Quality (LAN, direct connection), which is the
+default, and you should evaluate how well this setting performs before
+selecting a lower performance option that uses less bandwidth.
+</para></listitem>
+<listitem><para>Medium Quality (DSL, Cable, fast
+Internet).</para></listitem>
+<listitem><para>Low Quality (Modem, ISDN, slow
+Internet).</para></listitem>
+</itemizedlist>
+
+<para>
+If you always operate over the same link type, you can deselect the
+checkbox labelled <guilabel>Show this dialog again for this
+host</guilabel>, which means that you won't be asked about the
+connection type again for this host, providing you identify it in the
+same way. For example, if a host has two names, and deselect the
+checkbox when connecting using one name, you won't get asked if you
+connect using that name, although you will be asked if you use the
+other name, or the IP address.
+</para>
+
+<para>
+You select the appropriate speed setting, and select the
+<guibutton>Connect</guibutton> to proceed.
+</para>
+
+<para>
+You will then see a small window containing a progress bar, which
+fills in as &krdc; negotiates the connection.
+</para>
+
+<para>
+Depending on the configuration of the server, you may (and almost
+certainly will) need to provide a password to authenticate to the
+server. &krdc; will provide a password dialog similar to that shown
+below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; password entry</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="authentication.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="authentication.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; password entry</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+After authentication, you will be connected to the remote server, and
+can begin using &krdc; to observe or control the remote desktop.
+</para>
+
+</sect1>
+
+<sect1 id="controlling-remote"><title>Controlling the remote desktop connection</title>
+
+<para>
+Having connected to the remote server, you would normally use the
+keyboard and mouse to control the windowing system and applications on
+that remote machine.
+</para>
+
+<para>
+You can view the remote desktop either as a full screen, or as a
+window on the local desktop. You can change between these modes using
+icons shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; full screen mode selection</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="window_fullscreen.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; full screen mode selection</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; window mode selection</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="window_nofullscreen.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; window mode selection</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+Full screen mode is normally better when you are helping a remote
+user, because you can see all of what they can see. Window mode is
+most useful when you are working both remotely and locally - perhaps
+referring to some local documentation and then using those
+instructions on the remote machine.
+</para>
+
+<sect2><title>Using window mode</title>
+
+<para>
+&krdc; in window mode looks something like the screenshot below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; window</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="krdc_window.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="krdc_window.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; window</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+In window mode, you can terminate the connection by closing the window.
+</para>
+
+</sect2>
+
+<sect2><title>Using full screen mode</title>
+
+<para>
+In full screen mode, you can terminate the connection by selecting the
+red "close" icon, which is shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; close icon</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="close.png" format="PNG"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; close icon</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+</sect2>
+
+</sect1>
+
+<sect1 id="managing-configuration"><title>Managing &krdc; configuration</title>
+<para>
+Using the <guibutton>Preferences...</guibutton> button in the bottom
+left hand corner of the the &krdc; main window, you can open a dialog
+to modify the behaviour of &krdc;. Selecting that button brings up a
+window as shown below:
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; preferences - Host Profiles tab</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="preferences_profilestab.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="preferences_profilestab.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; preferences - <guilabel>Host Profiles</guilabel> tab</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; preferences - <guilabel>VNC Defaults</guilabel> tab</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="preferences_vncdefaultstab.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="preferences_vncdefaultstab.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; preferences - <guilabel>VNC
+Defaults</guilabel> profiles tab</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krdc; preferences - <guilabel>RDP Defaults</guilabel> tab</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="preferences_rdpdefaultstab.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="preferences_rdpdefaultstab.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krdc; preferences - <guilabel>RDP
+Defaults</guilabel> profiles tab</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+
+</sect1>
+
+</chapter>
+
+<chapter id="dcop">
+<title>Developer's Guide to &krdc;</title>
+
+<para>
+&krdc; supports a small number of &DCOP; commands, which are described
+in this chapter. If you aren't familiar with &DCOP;, then you don't
+need to worry about this. However if you'd like to automate some of
+your &krdc; (or other &kde; application) actions, &DCOP; is a useful
+tool. You can find out more about &DCOP; in its on-line documentation,
+and in tutorials on <ulink
+url="http://developer.kde.org">http://developer.kde.org</ulink>.
+</para>
+
+<para>
+You can shut down the &krdc; application using the quit command, as
+shown in this example:
+</para>
+
+<informalexample>
+<screen>
+<prompt>&percnt;</prompt>dcop krdc-25550 MainApplication-Interface quit
+</screen>
+</informalexample>
+
+<note>
+<para>
+You will need to change the <userinput>krdc-25550</userinput> in the
+example to match the instance of &krdc; that you actually want to
+shutdown. If you run <command>dcop</command> with no options, you will
+get a list of all applications that are running and &DCOP; can
+control.
+</para>
+</note>
+
+</chapter>
+
+<chapter id="faq">
+<title>Questions and Answers</title>
+
+<!-- (OPTIONAL but recommended) This chapter should include all of the silly
+(and not-so-silly) newbie questions that fill up your mailbox. This chapter
+should be reserved for BRIEF questions and answers! If one question uses more
+than a page or so then it should probably be part of the
+"Using this Application" chapter instead. You should use links to
+cross-reference questions to the parts of your documentation that answer them.
+This is also a great place to provide pointers to other FAQ's if your users
+must do some complicated configuration on other programs in order for your
+application work. -->
+
+
+&reporting.bugs;
+&updating.documentation;
+
+<qandaset id="faqlist">
+<qandaentry>
+<question>
+<para>When I start &krdc;, I get a message box that reads
+<computeroutput> Browsing the network is not possible. You probably
+did not install SLP support correctly.</computeroutput>
+What is wrong?</para>
+</question>
+<answer>
+<para>SLP is Service Location Protocol, and is typically provided by
+<application>OpenSLP</application>, or by <application>The
+Knot</application>. </para>
+<para>
+If you compiled &krdc; yourself, this probably
+means that &krdc; has been compiled against the SLP libraries, but
+the server (probably called <command>slpd</command> or
+<command>knotd</command>) isn't running. You normally need to start
+these servers as the superuser, which may mean requesting that your
+system administrator does this, if you can't do this yourself.
+</para>
+<para>
+If you are running a packaged version of &krdc;, then you may have some missing
+dependencies. There are so many ways this can happen that you'd need
+to seek support from whoever did the packaging.</para>
+</answer>
+</qandaentry>
+</qandaset>
+
+</chapter>
+
+<chapter id="credits">
+
+<!-- Include credits for the programmers, documentation writers, and
+contributors here. The license for your software should then be included below
+the credits with a reference to the appropriate license file included in the KDE
+distribution. -->
+
+<title>Credits and License</title>
+
+<para>
+&krdc;
+</para>
+<para>
+Program copyright 2002 Tim Jansen <email>tim@tjansen.de</email>
+</para>
+<para>
+Contributors:
+<itemizedlist>
+<listitem><para>Ian Reinhart Geiser <email>geiseri@kde.org</email></para>
+</listitem>
+</itemizedlist>
+</para>
+
+<para>
+Documentation Copyright &copy; 2003 &Brad.Hards; &Brad.Hards.mail;
+</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL; <!-- FDL: do not remove -->
+
+&underGPL; <!-- GPL License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-krdc">
+<title>How to obtain &krdc;</title>
+
+<!-- This first entity contains boiler plate for applications that are
+part of KDE CVS. You should remove it if you are releasing your
+application -->
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+<!-- This entity contains the boilerplate text for standard -->
+<!-- compilation instructions. If your application requires any -->
+<!-- special handling, remove it, and replace with your own text. -->
+
+&install.compile.documentation;
+
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: xml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+vim:tabstop=2:shiftwidth=2:expandtab
+-->
diff --git a/doc/krdc/krdc_window.eps b/doc/krdc/krdc_window.eps
new file mode 100644
index 00000000..fde2af12
--- /dev/null
+++ b/doc/krdc/krdc_window.eps
@@ -0,0 +1,514 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 677 308
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sat Oct 11 17:24:58 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 512]ST
+B P1
+NB
+W BC
+/mask 6545 string uc
+î½*¾î1:W*¾1M0Ç7*¾ÍA?Þ¾.S+¾åÉ4*J´ÏÆB>ÐE,ö:ñQãÅ.ZJäÍÈïÅ°üÒúÆJü+ÜGT¾IéM/÷+îôR+H
+ê9?´µ4,Þ¿õÍ4í½ýÜýü»ýûùüùõûõíùíÝõݽí½ýÜýü»ýûÉMT*%
+d
+/sl 52129 string uc
+î½¼**ûýG*N¶°IM3HHÏÌ+ºIMÓùÇY0º´ê11*@ÕZêñÝ2ÑPñø.HîÕý8*òêêûùñåIËƳö7âTÒÔ½ôÖWÝH*
+ÞÝîùéÝýC>óCÑÄ0æûâA-HEô¯*BÙòùÅÌÉÒ²TÓUãÐOøY??ÌGÝIõÕÕIÔåOÞð4Ä»¼Ú>î½³Ò.HîÕ3*¾+íÍ
+úè²*ÞÓ¼G¹õ¯çÙ·=àÓÁäñ.½11Zìûûé´Ë+ã¸Ú/âêê;ÑZøËÏÈ꯱*»NÙýü×RÑ+*9UÙ°äÀé4OÇ<ÀòDÍ=
+ºHF¾ÛùáÄȳ0@éð@¾½ßõíýô/ÐÈæVY**AHÜûúô×1Iï*ãÈ?ñüÞÉä-*̳´âÄï°/È<KÒõô;4:ѳ?QÎ/·Þ
+Þï2âT/U¶,÷ý½EÃ,ÈëÝ»æÒÅ<Ôå<îô:óøå**VåÉ,Rôúüû=ÔÏ4ïT9*¾õ»ÛG¼÷êÏIQåSí»;,RóõüÁÅä
+-*ÜÑóÛ»åLëC<ûÑäôË3¶ð÷ýýݵòæÒHú¼ùÌÅ8ãHIïÍXôG6öÖàóùå½òRÛô»½÷½YÆ¿³Ä9½ñ-3¶ß/1ÓîV
+*èÓãùÜ÷ÒÂÞ?úÒ6VýøÛßHW,øýIÎéV¶>+Ê?ôöùûíµI;*ÛEÅ5ÏÖÎ+BöûË@»ðZ»ô*ÎõïKøñòéé½YHºõê
+GùòåËE½H¯4ÞÚùù¹ÜAúÄôÍÃØ*Þíä´8ü¼úîÓáñã¿áXù¾.:T÷û×,Ë1*úåß¼çX?èðJ5µêêÊQPJ8å5VñJ
+¿ðü9ÐàýPCRâÜøûÕGð?6¶ÓõÓÍáÂë:θü½·ýá9VBμýɳ÷Ýåо5X¶R/å*:X*5Gúݻ߲TÀüýýõÐ6ZÑÄ
+õë9»Ù.Ï>á*õÖî0,úTù÷½äÀNÊOÆ>Ï*ÛE1*ÒL¯K-¾ÛùÑó÷òÃÁ³C-FIN½GÛ¸÷ûñݽíéÉåüº·ðÕÃPÊ,Ü
+¹ýüùüõí9F>»**ܵµòîïíYíG<AÌú´ÛAPÞôõ1è±L*îX?M?T°Þ¹êRÖÖ9íÊ*Ú5üñH»äýÝ2àýÒ÷HÍô´üÕ
+Ýè?6¶êýéË4·±LÍTýý¯ýÙMTAÞãýåÀöýð¾³+AÚîÌ4Ì+ÞÚ¾-ûÝÜ<ÍIOÝQù¿=79î½åéõFöºûÇ×QÍÇãñA
+9÷2+ûýíñ/9F;-ÂCôýß9YÊ*äóܽýúã,ò>¼çÃ/Aî:NÞð¸åIú¿D¶ìýO@àAñPÅÞì;8BÜïõY¼ò*ADK½X0
+ͽ½úã2K*Õ>ïJ3ô:*R¸½ÞBÇܶÖ@±¾CN*Z*îè:»ïðåÕÝíY½¼ÅÝü¹ðãÅAPC¯4¶×ûùE8Õ½ýßJ²+¾I?6Ú
+÷õõóáåñÓù8¼ÚîÏ1R¾øõÝC=*¶ËϹÈFRYÚ±ÂUWÄÅR:TæµìâýÝ;ÍýéñTEµÄûIÚÎUBîðýíÂí»éËEÓ¯ýã
+ûÕÊÒV¾ïýµ²ðýö×86ß/AFÑÀ±+2AîIGý1Ùæäåý»R²üõØúýýUõöݽÞ7ýÍAÅ9ýYÊTëý¯é2*Î5H¶á;Äò½
+öÜXʾÔýíËеÄÄ/6¶õËÒ/-2ÉýíÞ+@ÜöñýÝÓ0Ôü½Sõä+XÓ,àG÷ýú¶µ8îL=¶ñýË·³àõæÊM6Þë@ê+*:ë
+üÙOøºÞZWÔ*±Æ³Ò,*¾MÁÒ°Å-IXHüÎQ´ýGöíáã°ºÄ,,*¾Ñ>:**Ê>2**ìξÁí½üÕú÷ÕüV*îGO=Aü»»ú
+õ»»õß·9Åú°åL+èÝIäÊ*ÞQ´ô·ÎH·çü;Ü¿AHL¿*EÅ3ñ÷ýúøüÝÍ5ß64üý¾ÌXß¾õý9ïäýÕ;àÒQéÕý9µÜ
+°+ìý½ÎÈý÷IT5¶à/åÒÎFJüý½K2;*üöΰBçåI.ÍĵÜêýýÈä*ú×ýý7ÃýÝTC=øÝ÷¶ã6AºîÄMÊäýù9ÛB+
+³ýݲÖA105JÁüý·TCÖî48>ÑWCýÁIô*Oâ:Æñ*Õ¶*A/ùýÝÏE1¶Ï3ºü½UÝLÈêµÆïÎ<8>ß8**=õÕÉÏä-E
+é°/BÁ>**ÂK4**87ÐõFHEµõóëÝõôé¹åGùöÌ3YÏÞ²ôO4Â2¾ù¯Ò*Þ74îìûùÙTÝHå-*J8»1½¿ÞßÞòØLÉ
+Ã,´½ýÉ?±,*õø>Ü·B;Å»2BÜD¿ÞDY×<èïóóü½Ì/óêü½QJöXß¾éýIÇÜýøSÓââý=ùúIóÆá,îüæÍÂA²>*
+¸HïÀ±TB**¾,*X2,-É9»DÈ?ùÍÔ4ûÊà3Jâ5-îûý=àöðýûÊè8XöÞ7ÅÖÐýÍ5¸¯,4´õýýáÑ7@Z×õýD¶ÖJ
+øý½.1,ÐãæêS-UýÝ>Èñ*å¶ZÈïä+X²LZôêåÃ6FÑÂTÀG*¾³ìI@à-/é±.2Á²Ý×ÍY½ö3²ÝCNí2FóêéãÍQ
+íåÉÝHE³î79HL¿-êÊ3/*åJT4I¼ä-*ZýÀýÜûàRÏ´îßý¯V>JûùÉÊ4*FQäT9=¼Æïà±=2F5/î-GÃù9@?ó
+ý3RJëÌß¾GýYÇâ2SA·ý½L8ýéµ³V¾ÁýÍóñ½äÄL,*äEX¶P/QµßýüíéÏâõø9ÃÙ,Kñ+A0ÞáýÍ?Ðåýä/Ó1
+Úî¿E8¯Ðý±Ûòâ.ºI¹ÃýÍÍõý½ñäÝòÀÑ-õî¾¾üAÜè¾Vý1×Rñ81Ì7ÞÃPÖöý¿Ãº¿6Q?ïÞ¿´J*æ¶ýÙ/QÜÞ
+×8±¾CNÀ8Üú:N¾üæ:ûÝíÐWMý8¼º1ýG¸éнýMö0/X²=*¶Å,,ûù;¿USÞCïáµãÛ¹É1**åï0HùöL-ÃÌE¶
+çí4@4ÖùûU¼Ê3*ÚàÜ»ÁñÌØÔZ¿·ïÏ3.Ì÷Å9ÑÌò3ûºTR÷ýí¾Ô?3F¿+ÍùýßÕýëDøRÓýAQóýã¿ÊüïM×ýS
+ìûí9Ö44:×ýIîÀÁT¶0*ñ,H°öý<IøûY@ÓVS´»ÞÃ+öÞýãT.ûÝãOöÏûýÙHÜî¿-°Þúôøü9ûѯAP¾ûü63ñ
+ýîÃL7êðýBI0@Ú¿-ýýI=ÈÓýÁê²èÞ¹Àµý>äÆL;Ýà¿EçR1¶Ï3Tö½æ;U¼Wñ¶´:QtZ1*¾ÏùIOBð/ÌóB-Ò
+K,ôßÊîûÓK¸·ÞQÙ¼ÜÛùõì»ùñÔQ-AQî74FKè¹>:åÙ93ÚÈ,´·üºUçÑͺ**Øìëî×¹M9JÞìÏù@ü@Ö¾Ã,ä
+ºýÐ6±,*Ý6HîâXóüGSæOº<,î½+*±Òí½üêç8ÛÞ¾J+*ò.0úêôøO±ðÃÏQ?GýûæÎT/â·Ä6çÄQý?:¸K+ÅÚ
+îÊ4Ì+J»¿-Û9¼JÄRÁñQøÙÕ¹*510ÞÙLåýÛçJèB<ÜôýIï¿5:ÏúÓÍë²çß5°ÞêíùÅý¸AP;0´ðù÷ú05ÌÞK
+N+øýÌ>:´?78<ëñùõÍíëê;8FÈÄTÆ-ËÞ7Õ8å½Ýú¹á2å0ðP3Ô:*æ¹ýÈ,IÜÞ×8±¾-ÁNëÝÁ½´ÆÜV¶éÖÍY
+åÜÛ±ÝüF´éßÉûÉ3/8Þó;4îÇíÝÙ°ìFâ-*îý2,íG¹öò*ºúõæÃ1YRéÝL+ÙÚIïÂ3+¾9òX¼¹íËKÐ-/YË**
+T*Î31/LòÈÝ=621¶--ôêÏKÖà5ÒÝSÀ2çLÖZ*=H±â2ØDÀMX¶P/å,ÎÜÞ÷3°NZL´Ê.ÇKÊXÞà¿+J²Ç.SÐ@
+ºJÔ;46¶Þ-AJà.W4ÄZNÞ¿3BPÂ,?TD¿35LTNîZß/FÂTDJ¿>O¹;7FÎÕNÀ0KLóP?ÌOM?âÃZJ³Ê.3ÀÒäï
+êJì>ß,.JEäíYXÞÄA¶Ôá6îâ:+¾±Á²×¾ñÔûJH?**Ú¿åàAA42ôëÍÏ*ÁÒXÛ?ïàEÜ**Aä>¶íÛDZ9ÍßUQÜ
+¸ÑZ,é.Zèüç=LÊ1*öKÖòMÁVÕZLNæ±G2Në<4Ï°ùÁì@:á=Fé¯1¶ZÆ,*¾±Á²Ë¾1õûâMËöîËòOÃ,´Gùôí
+YO+µºüÚ?íÜ?Ü**ÅAÈæíßÅQ1ÍçQµDèϳ.9ξ+íY´ßÂ*Þ*8ºÅZPLæ²H2Në;3ß1ÕõåCXÞÂQ¶Ôá8îú:û
+:îÞÃAÅHÛ¸ÝüûúóãÇ9¸A±4RÃàÃÙÈGù3Û¹Áì<»-4×öåʳGH²۱ö*/:ëüòI0êó*:¾*ó2ÂZßûI1UQîß
+=Fé¯1*0Y9Ðé+8ºöïäËIõæÕ½AÝ»¹Gó°P+ÃæÙµUÔWÝß¹9¶8>H²âη-ûZè?8Þ=>¾ÒùIÍ2îñ-*üؽý½ç
+ð3º±±W0+Õ1SRÇ·0*àåÚÊS¾5äÌëȲç-Í*¾5úåìCXÒJÏÀÚÌ×ùQî34RÃÞùMQçíå-*1¶¶ýü<ÁÇéõPßøê
++Ì9<ÓÑÓç;*2WóÐP+VÛØ?åÌYûÛýÙåÌF¶ðêë-ýXË*±âÛ½½ä;÷û¸*î,ÏTíÝÝâXê*ܱ0é±4¶ºÆÒíÚµÅ*
+JäóÉD/:²æÔ>UÌÈó»Ýàöùõå8ׯöøÝ+*ëÃú¸¶5¹ÄåéظùøOO±ûû-¶÷°UÄ7å?ù¶íààâù½¹*î˳ä<Ä7Û
+ºùðÈ×ü0,ºúÖ?ßÀ=±,Õ19GÁMÇøãUöèûê*ÞÙèÒÖ6Ö°éà3¹ø¼ÂÏ°¼À/ÙðéµÙâ*ÞׯÑôVC¯ß¼+YÍ7*úÕ
+YÞÀ7±@ëDÞ@Eɳ¶;2ݼ1SOüûíI*¸ÊS6ï´¼û8Ð,úíI*ØêSÌ:?Öýü+ÎãÌð.SKû»-¾Yµº?SÐTïÐÜö¸êÀ
+8¶¯âØM=0ۻͻOáLã3ÐÀ÷õ*Þ×TòR¯K¾ùê÷¶êÎ3ñ/5ÓQTSÏJ½î깶.Ùæ*QG>S×¼--äÉÜÈG¼QÑÖRN¶ä
+ÍýýýÍ»YëGä?Gé>QÆèâÉýÍö.*ÆY*î4ýäGÙÓçÍB»âÂ,óËÙÄ6AOãSÑPÄäFÖ¿ã>¾½X×óYáÈ?AÄÌK5Pèú
+00Kàú1*:ñÈ´<Ã52áÈMSÇüÛÝÕø6ýáAÌ:Ó¾1ý*S+ú,*1CAWÕRÃìNçEHúõ8ڲ߲SSËD³ÀÒä¿7êO+úö
+ÈV1ÉäûÙ´ëêB²ëÁÑì*;.NÉå*J2üÝÏËN<-L¶ïRT3íóXÍï»*á3Ð4õì9:?¶Y*îB³ÝPGØñãÆGïÞ»óÜ?ùó
+T³ÈòRÏG=GÖ¿Û>¾Íë·µ²ðêÛÕääSáÜWÚSë5J.ùæðýôµ*¾¶ùø½ME·¼,Lõõ°*»EøýýüûHÖ¾ÍÅ*QÆTص½
+¾Ò1èQ¾ü½0*ì½ýû÷íÙ1íéÏ4ìM,ºàÖÁ³±Qíü²ÌHÚ÷ñæK±Û*L:Å@ÙI,4ø÷èý7ëâ÷3:ÏJ·9.±ÔðõíÝí×
+ïWLá9ÏÃ=N?éÕIJTÖÜ*J½üݵíܽA.BÄT5йÔÊùX1´îV+*¾<¾Ê-Þñç»Ê¹¹ùé»ñõèÕ±å¼ÛáÚÜî¼BJáJ
+TQGÙöïåÛÅIíêý6ó½à,ÇAWúçîD³YÕÛ>¹WÝH»øÐ÷äÙ>ÉßÅýZ/QÆTÒU½¾ÒJ»+*ö6N/-97?å½YZÚ°ê¹/
+6**1*îJ3À-È*°8*ååµ°ãËYñÔÌãµý8ü¹õ19ß¾èÃM¿*/Dì¼ßûZ*Ä¿ÒX¸öóëÝÉQíÜßý·çý-?ÆK:8³ãK
+S6-:²µÆåò?8òùë<1ùVìû**Þ»+NA4¾à+ÆEÍÐââ:Y0,Ì:ÓJÅü*S+ø,*ë÷ÝIÁ:±HDTÃTÐIµè·/6**1¼
+îN+À-XÄÞ6*:½V,å@Þá+ÄÊØôëèá±õîåÑIIüÝMBÎJ8ô8XØéÙY¶G6.GøÿÒìù¸õîãѵóÝëýÇæýº,ï@.
+XÆLB*;N,¹TVÎ<ÍMIËÑõ-5:BGIÉ+U8ÁÃX÷>ODúö³4î.Sã÷ñ1Æ4ÌA*ZÌý=üõ3?ë5MóöMéGDüHòÙ,0*
+¾-I*åå+à*82¾S²K¾¿ö*>ÌV.+4à.úÞ.àÍþÎR³¶üÀ+ò·*WÏRÎõ,8À*ÚÙÛÉ+ÍüGG¹0½ÜûùôSѺ.-<¿
+ò52*à9N,ì;¯*ÄÆ=6AHï6*ÚLÉÛ½Ìú·øºYýÜÓýû½ø÷ýJ/ì.,,23LÇáµìîNPâ÷ñµNïL¯Rå5ÈåE8F+=L
+ôú·ñFÞH÷Ê=,JJí4½+*ø¹ðà<75½>-B²ÉÉ6ºÅ-æíÔ>>*îº÷¿ò*ùäB8Òå¿»FÇÆ-Sö¸*î.SO÷ð1Æ4ÈA*
+*ËßÑðýëK¾CTµÍ¼ñöíÛEÕ?Z**Fö+R¼8:0î¾*Ê,ÛàYÅ1J´¾>5QGPÞ±ëäô,íéÃN¿ðA6JÉ-¶-öí0Aî:.
+ÞIÜÚF÷ñìçÙñöóñíÍí:ðMBJñÂ6ºÎ+¾ë¿NJÏοEûZÀïà=Ì+¾Q²Ç:KÀÕýÕõõñÙøýýQFý9ÒÞËJJ*¾JÑÁ
+ò0JÌâÂ>YüÄ>8>+Ç?ï´»á¿=¶È?ÄAÍØ-÷ðAHñÂ*과ͷA*¶ë¾ù<ÔTÓSî¯,îºã*<4CúÃÅï*.7@ìK4á1
+Ð?ÆM´·ÎH1@ñNL8Nßé+¾=N?âÇIJTJÜ**°ÌÍÌ+FBTHIü¹õëÇå4B**4*à+ä,Ì2¾JÏÔê*ZFÞ-1JE÷5éÆ
+=1TD:½ß*ÂF¾æ1ÞîÀ*δZÁSɼ¿ÒÒØÓèÅZ¾ÇYOÃLǼÝíNÀ;I8¸ï4Ä=ƶ?*;>:±:?ßý¿ý:ÓÆ÷ýÀ/Ì.,
+,BËÚò1µ*ðNPà*ÌÆà+2ÎC¾ÄÍ-Ú¶,ÓG+TðÎÜÛ÷ÊE-¾Ëñ³?ë¸*¾µÄÚCTÎ?á47ÝR+ÎÊYúPìÌ˾*Ï;÷ÆÀ
+èÌî>÷Uò87á¿øFÖÅ=3·:/Y1*Ì:Ó2Aü*S¿ö,*¾÷öRç¸ï×,0*¾-I*àå+à*82¾MÄ?,5ØàÃ9*ÝÍæ*6RÈ/
+ÜYBÞ3@Ú3*9Ùë°ìÜͱ1YåÙI76KßÕZJÃ598´ÏMï1?3B¶<IϲÓZF¿ÂÁ**2-SëÅ9°â.ÐÈ?ýíÒÞ¿J.EÆí
+-ìÚßËÊF<Ã-:JÄYöD1¶ð.ÚçÓè2,5Aüà±8/¾U0BìßÇ.ýKôÕÑôÒS²QÕGKõ-@*ñÈ7*î-Y3оËXãìîöÛ¯
+Ê/Ó»FÉƱ4øZ>î.SOöî1Æ4ÀA*¾É=ÒðôýEMÓ´ÙYÍ8FÔ¾+*îÞ1Jö¶*îF0î¾*ºöîÝMô2Hº0öùÂ0â1²?+
+A6ÎÂî.84+FV°T»IÝìGºHÈÉàÏ@-,»P->úó,Q<KÂÆ»Z1*ÞÇQÚN*¶BÆTøýBSKIÉú½C2ß0àÖÄEµ¶==ÅN
+ï,.ÆX1çXKÃëêî9ÊÀ¾øʱ+îÎýøúùôGÏDûæÓV×Dã4=õL,×+µ>¾í·@ºö0ÊÁÁ8ÐZEERÊñÊWÈ+8êáÇZ¾0
+1Ðîñ.Þ3ÐÈíÝ9:?úX*î@ýíùÍðçÛù¿ü-Mã³ÍèÊXîµV+-*Þ¿9Þí7òìW=@î¾*úQäÙÇÐöû¹EÙñ¶ôÙ³,4î
+ÀÊÕµµ¼à*Ã0QîîJ+ÚóÂçÎSÙÄWSìÚöK¹M9àºÏ,8íèäÚSíMñEú¼:3Ú¿åëÇ;,ÆFòî.<80*B6N»**òñÅý
+1NîP::¾íÔW?¹-:µ°ðÆ>4*À+RU.óìº/üÔÛ¿À¾ß,îñ¿åÚ7*úÈ°Uس-*ÊéÒ=³112ÎJ0/FK4Ù½E,µ×ÍÖ
+.ÑÛ¿ÁÄKMÕÄ;0îÞHÌ.áÃ>úF+¶ÀÒàÙõ-N/Ü5*>ãýùôGº÷âRÛùôBA0²ÕEÔ¾+*îÞ1ε¸3Æ:75¶J*X.À+
+RÆøôWÙóàÎÞÄ-L¾*½6¾îá7-Ê2F¿ÁÑ0Îé<ÕôTÎâ-5Þ´´R:*À=âQ<¿±ôB¾NHPBÄ8XJàß*¾ÀÅÆT°ú½J,
+µ,++6Æ*TóE*·==ÕN¯,Z6¾¾@*öæAR?,ËM+Xºäë*¶ê:ÍøóùõíÛ¼AXÑ9¯ùôû2NE>îÈ/ÖòûÕÆ16å<V?/
+Ò»I±É+Ä5ù¶;6QÐîï2Þ3Ð4ìÚ9:?îX*BN/ù87?YÍìYÛ´Ú¯GÖ¿Û>¾NÈòÞ*T*Bé¾áS¹óQ¯øÅ*.9;ÓÆöý
+ð0*¾/æÂÎL>99¶é5ÉDÄúõëÔP³ëÌAA*69R7Ì:ÓêÜû*Sßô,*:·Æ4úGDTëµìذٯGÖ¿Û>*N4-*Ò1+È*ö
+S»âSµËµZôÅZ-3Üü÷**RL0ýYì**ƽP2ó/==,,YëÎæåµß0ÃÈÄÐÛA½ZúJ²¶ÀÒðùõ-N/Ý5*FNû÷ëÑAIü
+ùöS½ÜúöîÞ½õäË´ÕÄóÞ0ÔÔ*êòFðÑ×ÐñA·FöÀ.»÷Ø.Q332***0üÝÚ+*:½ÃÎÇ7°å>.8ÙJͳëð>T>ºËû
+UHÏBFKTC¼ú+ÐÀï/*X8LÀ*÷ßM18µÉäÊBYØZG**ÖñÞ0ôÔ*ØòÙðÐÇ@Èú7NäýÕ*ZÁE·5,ÌùÝÆW3½Ëè>Î
+Ù,J.SA+VCVØñCÌ:ÓF½ü*Sß÷ã,*N¸Á*A0*͸ÁÛéÙëàá¼2ß»í¹¶>SÎñÈXß9Ì:³É»Q¹6â3ÐÀóÉ0*Ç>ç
+¿+F4*ñÅ2D¾Ù0ÆÃò¿49ÖÊÙ³NAï11ÄØåÚÑG0ð.SßùQ1*±Ò9QÊÌ:?Êýó*J**ö·Þò.Sßûõ9*Wô>QÆ4ô½
+ý*æ°Q·ÀÒ:ñ/**ý·+*óå½½°NöüÁZUÈ:DïÛ-*Î÷8SñèÁ2è*¾õñX´Iýüûý6@·ºôóùÕí¼ûøÌH»öéÏE½8
+í?ÐÄ8RüèWµ@Fö»*¶åìûéÅìEóâîö1,*üêø÷ùúû5Í*Õ¼Ä4¶ºíÕÙõÝIýûÈÝüøéÅõ¼ÄÒ¯Ò²¼±ÞS*¾ÌôÝ
+I»èÃI<*æâæõõíܶ8àø>Z*ïWÅH¼ýSTÙíHùëåÆDÄÈÀ1ôE/*Rçüùõ¼Ê¶@*îÇí4ÄFC°â°ÑCÀåZ-¶¸ûüç
+÷ä¿V½,0*ÜJTåÝI½*úóÕÝX¶õ¼ÅÒ°¹í5*ÞÎ͹IíûõåQF:.*-Ùí͹îº*ÞGåXêÕ³éÎ/Éн=¸ÀÍÎ-öÖûü
+àõÜÀÓ1L+1Æ4+**:Iû¹N³üûMÝù**ºðëëïÉå8û·¸XEð5;ιõ@íÝúíº*Þ3ÉäûE´èÐAEá¶ÁôËR4¼À8Jå
+øû-ÌùäÓ¼2.*ºÁÒ¯Ò,*¾ÉNOú/éW*î±E1½I¼öèë÷ðÞ¯Ý:+ÓùÕ½ûÅ÷P-*ìõ²éÖ¹õÔËÖäã5õéͶñÏÀ5
+0:ýýQò,SúÞK7âÐ.U¿;ųÖ-PííEëÚAXóÃBÞ:³7?+*Zû?ÐDýüËá»**X¶µòîïíYíÛ<AÌú´Þµ9Ê*ËôI
+ºLÓîÅWÕ¼W¹³ç¯ÓéÞ½õTGøÞ.âýýçÂ+ûýíçÃ-Ò*Ú¼¿-1¶È*L÷û±D¶Ô.;-¹,¾,3,HIïï3EVÕXïA¿</Ì
+NÅ,ÖBýõVéÜÖÔAà*¿ÒAÄT**¶ÅÒ,¸ë,*QUB¶ñííéÅÍåUõGú¶ßZ9ξ7ííøë²*Þõ°P˸²çÓ7ñÃR+ÑëDÒ
+ã->Jýý¹ÈÃ6ÌòJFJ+Þó5Úè,¾ùP,ƵêÖXçÞ×<Â6*Ù÷>OÔFBÕîÔ÷í²óòýýý7äÒµëI¹ÄÖȲ==7:øüçÜ
+ZÓIÐ*2í¸òï÷AÏ4øéY;ÓçÕÍõê,*ATD·ïíéá½íé¹9Ìù¶âHá@4Ö÷ú+ÑË0*FïåÕO½8GØR´R°éÑM³Ìº¾,
+;·ó*Ö¸ûüòUÅ-öØÔåõõIIWõPݽøÉ9óÞç<Ê1Îèóû18¸Q²éôÄÍý÷ÚK,üD½×ÇáùõY½ú*ËQøX2+ëËݹÂß
+Û<*Ø;-µ´ß@ÑåíøÏ-F32÷ýýÇ+F¶¿5;·ß6å*:Â+EùíDTå¾*²Ì×LßóåCìîà;ØâÍ*B»½ÞSãHÞÔAÐ*C:?
+Û½Iô+*:ÅÜÁÕ8Ê+ÖîÕCPW>JúõÍ»ß-õ7ÞóX8ë·¯áÌõ¸ÃFÇüÊD³âÈ1T:<µ6ÞSýíVÊìý¹áøÖÖýÑÌ´ÉßT
+è·EºýáźÐ-µÉ:H*Oý9»6Ô·+ÍûIU¸ü/Yûíýù1IèûýùX¯»ÒàôÛY6ã:,ºHü@Øý9@ßâÞËWÞÜNýÑIöâ+Ì
+ëàÃ+Fö¿5;·+2åLÁýýåÕÝYR<1,:WûîXÙH·¶/AQö<½,Þîú79û¸3¼æì·Æ+S3»øX9*¾û5üùô5MÃØ¿1ý1
+²Ï*÷ÝݳD*îSåÌ7Ù@¯ãEGàʵ+áG¹>ã-?2ÇE0îòý5UÅôýñ,»éøýÅ·±øòMûèÑíýýWG翶¾3¾MO2³¾ðý
+A51óú¾ÙÕ9Tà÷åëûõýÝ¿Ðóüýè»ûTÉWQ-IOó½úýϽÄÛÌ@ßâÞËWJVïüýQóâ+ÌëàÃ+Fö¿5;·+2ABðLOÊ
+÷ý:5@.J¿ý5´9¸ðî4XÌîOü.¾ÚõQä·óôû:02N/î9:ÓÒåü4SSúöV9**Ðá7ºôïó-4GµîÐáßV>Zö÷ÁÓD
+5*öÊT5éó5CëÀÜÏßÒ¸Ñ<ËÇï¿4Ââ7-öüýM+Ûøý¼W++8èQÓT7åííûâÞÃ8Ê-6*ÆüýY>Kº¾óýýÇåø+æ½æ
+Rôé4<îCÓü/üý½÷ÀËèóYÕ/7BìúÓ׺Ø.;-Õ´+ÈÜBÄ·¾E+·ð8Q¼JøýI;·,2Ñ;M;1TàèãÝí²K*ÐØ9Cü°
+Ï/åîð.YðQ*F¼ÝXÊQY²é1¯*ŵÛ÷óø÷ñÝYõéÕI¼õíµÆTÚ¹½KRØíëî×¹M9MãìÏù@üÔCÈÃ,´G½ÝQÑ+*ý
+¶¶ìÜÂGø=ÇSÈÚDZPÂ49ÎJNìB¾ÍýÝVçÖýÍÁ42îéÊH»øü½·ÎP0XòÞë<Â1Júý±Gã¸X¾âíQ?æÖÚÞÍH/·Z
+ðýòUÐüì60;ÖºüUåº×.îUâÞ×WÚ¸¾±Ù*UÍë¾Êý½LðÂCÌ¿Pò¾×óÍüæÀ*Uü½ñSß*Q·¯1*¾é÷Y³Çò<ÄÙÛ
+ñÞïW½HÛºùôëñøóéíÝìúø=NOéÕM»**ì;KîÛ±1Í8¾Åý8Ûöî/ÇÓV>ZÔùéHD7*¶ÉNíàã¸8HÄ6òTÎNÉL6
+÷*/LðÄ+Fñýµ·àüݱ@HØ÷ýçLÏô7é±ZýýýóN1ÌFÞË<Â1Þëý¹DG¼XJ=ýÅù²TÞ1ÂÞ=ÁWøòýï>Í5RGå-Æ
+òýLY@+B8C¶´5Õ0îä@Vüýç¶IÙ¿-;·à6å*Z¸ÞøüÝ8òJ¾SüýïØ-N»Å5U**ÃD½ýÀ@ÇÜæÔ9à*8í¹ôêÝÁ1
+ÍG½ýüGûûôëIKTÀ8»ç5*îèÊ,õÛE·ôïCºóâU¹4õ°Ë*ìóYÜRÍç*¾EÒD@ßµüôÛÒ¸ùÀÓ¸±à-=*ý9*¾ù²E
+»ýöYIëñH»ÐWÌI½øX×ùùÐSíÅû°FíÃ<F¶¾=O29¾NQí½Ü´Û¾ÈGÜý¼ö0É,úùùüïñÌÄ×X:ÔâÞÆÀQYðß<¾
+ê;-ÞõÓWFE¾=ÙJÉñAY¯¶°*1L,:H*.Zöê±T1êïýÞ·8.:¼Ì,0-FãÃ?*¾PäIùIÉMͲé-·*Ï3íG**ËAÆÈ
+Æ=Aø,*ÚàRL½1IHGºPIÛçRÌúﺾ,äFýâEÑ+*I÷ïä´ö:ÀFRϸ/T±N¾AR*FG**ÝCíÓQÖòçÚçµèÒQé=R
+Õ.TUÐØW-óUòÞÇ<F¶¾=O29¾AÉ¿àóZÛ*ðÑóS>W1ÂÞµËð²èDûúîG:-¹çèÒÅåȾXßåA°îê@Ì7ÞÇWֹϰ
+7ºô,1LðÂCÌ+JÛÀ»ñÒéã5H30¶È552*ô·¼½Ö/¯,»ÐW9â+.K¿õ**2ÜJT4HFæ5*îH+Àø0>°ïÍC鸾FWé
+æñ+4ÞßüíIÃC7*ö¿÷à?>3ÑO¾Æ8F¶òüã=/éÞ+¹é»>ÅSåJåÒ*ÙäIùÚÃD½HçÔ1ð*ÀÎú±*¶¾Ò
+d
+677 77[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6545 string uc
+*Þô**8ºýÒöüùõûõíùíÝõݽí½ýÜýü»ýûùüùõûõíùíÝõݽí¸ý*
+d
+/sl 52129 string uc
+èÕí´Ü»ùº,*XÑLX¿êƯÁ1HÐãËKÊQR*¹õÕ8>ãV*ÞK,FæïNÈ>QLÃ/3<ÂB7>Vâ0âÝVÇåJÝÚ*IäíIP½;µ
+ÜßÔ1,+ÌκàÛÛQµìÚ=í7×°Ù±/<²ëúø³?ÆÈ´íä÷.*úPÊ7÷·Mô5CòÄYMÊEÍξ0³Ì½¹Ô9Ý<*öÇÔ÷ùÍÈX
+àȵVóÁF¾=>9ÖòòéÍIIN/Y9;?ßÄWñ;*¶Ê=EPºB¯áÇ*ñÓ3îÏáùñÝ7?áµ.*¼ðY÷úûM/Q:¾ºôTýÁ,0SÛ
+÷òåË=å7C°2ù2SSóèB±,*µC¸üüúåíÔë2=Púõõ+*1»Ù·úúùá03á+êÆðãÒÔó?ú*:FN7M?àµÝMT<W¸ãÒ
+*ÞQ0/GÛýüû÷=üùûýH*îù÷õõýØé³ôÊJÄ<SäOWY+2*ßLTLÌFõÁNOÍIÑÊ5*ÚNÆ6¹ÔW¹·°Ç6÷õ+*åé³R
+»ñð+ÂÆFÈFCײ»/K*.9:?*½ÇÒ²÷ð¿Ç+¾56ö9ݺôæË-ðÜ9*ö·ñèÛÙåYJ¿×GéY:,JB1Ð,ëíI*DRO*ßF
+Æ4´½ý8ê¸N?Úµ½ý*æI>+.ñJTñÛ¹µíÍ9*Jõõ+*¾Z8N³Ý»ùüû÷+ÞýƹJB1ÐDîݽ-ýú*î½âG:Ê-SKöõ9
+*7Ò<¾Z8N³áÃ5Iü÷+*2ýH**2ä¿ÒÄúöïâÍ,**¶E,AG:A:êä1X6á¸ÓïÐFÔçCA4î½âG:Ê-SK÷=***8*¾
+>1:>î0/8KÞ39FÞ¾<*æ÷ê1âBAã*ÐQàù»¶O+ÊCA8.2¶2<ÑZÚKÆ>D¿*¶Ö»6*>ãÒ7Cîßã³F¯¿áSδëN:
+:¿ÄM9BñC-ÆÉG8*ß;=âý>7½0ÌÎF±èÜ»ùÄË°Ù0WB¯ðÌ;?B½,**ÞË.*ñÕ>îÚ1Ì*ßG3:Þ*ìÄ,:ã5²ÝÜÁ
+±5îDZæéºÚ´ÞD±4ÄEÛ¶Ô,JúC92.1M+N°/åÕ<G3â³F14J½>Úûù±Ô1ë:I·F?Ð?FOÕÙ¿:>ý6*Ì;ÓK½üF
+ø,**î¾+*ø¶*1*ûö±<:>°ûL.ì÷*L1ìÈÀ,³.*J*Þ?:ÆEØ,+NÚU=-AøÞÓFÂ̹+·æÍÉ3ÁYC¿ÒèõN,ÒöX
+6Bî4ü¶BXÚJ¶Þ+³F×2,ZZÀ±9;ÙÈÂ,2±Ú8û¾W²K¾¿:¿50¾¹N6¾;°²/Óо޷åÚæÄ5é¶N8AøîÄ*NÀç18
+îL:äºè»P¾ÊU>4òG3Ä=ô¶÷5Õø<.ÞNÀR,÷¿8¾ñí-*FK·äÒ»ùô7Eä´7ØZàâC¸ÃÒ:û3***Å8Þ+NõôàYë
+ÄRÛ׿UHZçÑã,üÞ28ÇJ;*ÕÒ¾¹øñCâÌ6Úæ-9@TøÌ5êNÝ5´F±,S;øÇ.æ³ÍZá0?ëïZÚ¶¿î¾-趰;.¾Ôí
+åHÀý,ÆD»õ0Á9û¾SæéÚô¾¿50îOS9ÍøKøòæÞÔ±À¶ùCAÔïÆFXòß7+Ö¸L4°H1¶Á:AòZúMï6@ÚKøò1ÀïÂ
+GXüàÓÜ.+ÖJÒëO¸Å:9--.K÷å**.ÜLTÎü.**¾,¾¶¾;*»ß˸óNEÚ׿UHJ赺¼¯.Ñ*Õ,JÇŶRûS<=/Jû
+ÃH¶ËCÒÝàÅÞµ¼ÎéC=2,äÍN¾àîVÛ¸åB81áÃ5Â.FÎÄ=5;*¾Ýù2ÓX1;ó-/Ûø+ÈÐWð¿¿50ZèÑÙÜF.K°¾Ì
+¾±XîãóF<Å=»¶Á*4ÔÓ4P*<½Â:AòZú-XA-F2ÄEô¶ÖCÕø<.ÞZUÅûßßXú57î.ãCÑôëÙµê>Ù³æÀ3=õ6S
+ë¸ôêÕA***16*ÔÅ-:*é3ÒZ+ñANîÊ1æø+æY6ß;3FR*Ê0ãøOüT4Xò¿Í0¾÷/ëOÊì+HÍ?¿ÊæºÂ**ÂG*,+
+3,G¿á߶ï,ß+1ÒïâL8¸--Öü±¾áHÒÜØ40»É.A¹ÕY½Þ*2C=JÓºÃBF>¹½¶³7Åâï,;Ì/¾S½ùÆй.·¯*A
+6îàÁÌàß¿YÚéÉ:¾=í+êÝ1*2ÀðÍ+ÞCÐÀôQ***F¸*-λƴµXîÐ1Z¹1ÕÙíXM¶N,14*=-*4ßóÈÀ=»îIÀã
+çÜVÕ.ÜÍ?¿¶¸ôÂ=²0æëÙ½Ìï¶BJÄ-A28¶J7QDïÎE>¾ôèµJ¿ðîTæøÊHÌB¾:ßùJ66Âèë+IõÊ5˶¯:Åâï
+0;Ì7¾6Yî8KX6Þ/-F2ÄMû¶õ5õø<.JJí¶5;Í,.FK·äÒ»ùô7Eä´7ØZàâC¸ÃÒÞû3***Å8Þ+¶ÍÆßRñè³Ý
+׿UH¾ìAßÁWR7+·Õ*ùC:Jõìõ²ÆòJ4Ìò+áLJÎùÑ.êÚM,ºÚKÕÞåÉ9òÙRÛ¹ÅØô¶BXÚJ¶Þ+³FçÂE+·Ð/â
+9*ÆåØ+ÓäÌü³Ã=0î8ÐP½G<¾éìÏ=¹Ò-/¶ëCADïàD8Çá/LÚ3*0öÔôóà1Ü·Á6±H±*1,²6îJBÌòáãYÚé5
+.Þ»÷Wê8ÈçÝæ*KRüC**¿ÕÆèÜÁ1ý»+**¶Þ*î»FJ2*P6ÀJÀ:ALî¾/8*¾¼ýÜ·32A96¿RÁî62îùõF+F»+
+2Aø¾O,ÖúG÷,õ8@¿Y´?-¿.L̺åíÑýØÎKÕñÕÀïà+XÚJ¶Þ/³ÚæÂ=I¶é?A¼*ÉNJZOê=0îO½:¶LÎ>7í7ò
+=;Ð0ÑÚóÊ5½¶¯7Åâï,;ì.*íç¯ß¾2ÒXÝKÄCÍC¾-+D0¶;6QïîÞAìó/,JJß?ñ1¸*Þ½9*î.ãCÑôëÙµê>Ù
+³æÀ3=õ6S3úöïõÎ47´W°¸Æ³³àÊåçùâB6*Þ;/¾öí-*.K÷å*î6SÍÊ**¶+*õÎîR,ÌöJ4IÆ62XDKTÆÆÆ;
+XGLLS¾ô°=YÓV8»Æ3CðUËÂÀï68<ñÄÅ*.ÌÎF±èÜ»ùÄ7±Ù0WB¯ð6ÛLTEÍHû÷Û6*æ6Â.ÍÈõÆZ**Å>*æ½
+9**.íÇ?2ºÃÒ¼ûøýMÊ@Ø5Îüù**QRÌC³íÜ»áP×WÁ@ÊB·åÆÈèÙµý½ÂçL¹+ê½9*:.ãI-¾±N?õï½ý*æI
+>ï.ãCÑôëÙµêBÙ³æÀ3=åæ?Ð4ú÷ݽ*ò9È2ÀðÍ+*2ºÃÒ<Ýüõ9*ûÁ<ÌκÞÐËIQ8ÚðÜ7CðÖ±Í;óùíÞÜMT
+ǽýù1¾üK3JB1Ð,úíI*DRO*ßFÆ4ê½ý*æJ?+.ñJTRý6*Nòç+*æCðàµíܱ¿>C¾BýWF*J398FBèò·:¿·
+åéîäÊ8;9;ê+1Ú¸ÙôBSÈòÆ;4õÒø¹ARò;ÑQåæH×÷ãï2¶÷ä¯É@ûD÷öÇñJ2*AÄV¹²àÎÙåË8»10Ç:?ʽ0
+*¼øýýýùÙIýGË@üôÛ5âÆ+À·ùö-üÍIü÷òã,Í¿2¶¹Ý6½Ø7ƹDÃÆÅ2@¿-XC@UòH0ËD@È/È9ÔÛQç=Oùâ=
+6йç6èÞQ=ËB9G¸ö8<ÏÎáåÊ-±*À÷ñî3àÜÕôʲÐC»*2ä¿ÒPÜúýÂQ*¾ú+¾LTÄͼúçæÑÝW:¶Æ*¼¹îܾç
+¸ÛøÓYWD²åÊ>KÐ=:ÍIú+æºÆÈõÕ1IÛ¸ÖÝYù¿ÉAÝúêðÝíQÖÕ8»Ý-Ã*÷2ß<¶ÞãMôÂÍâÆEÇ°ÑPϼ/úAØ
+-ì·¾*AÆD3ùNâ9*ô3.Bô°?í¼Ü0ì¹R·ó:ì.Ù@5ã2ÍÙÃÍßåÈ-A6J6U8I;øõPJTÛÈ,Ç:Ó¾1ý*S+ú+¾øD
+Ý9Iûçðõý¼ÙLî8+¶Zðâý½6æ¯ÏX2*ïû2îß92äìM9Ö»öèÇ=1ÙùïH¶öãëÀV5ü½ýB½ðíîÎÝô¿C¾ºÂZ3F2
+,ÞàúË6çÐRÑ+*2³VܽÁÛÈ¿-NËû<ð1ÙBÔðë/+0Bùú¸Á+*ðÉ´îÝGXñÆÖ¶÷òG»øåIíê´Y¯îHQ8Ú¿*;Ú
+¶úÂÂõDÅÊòR6NÀÒàÛù-N/ì1*ì:?àýý¼øÙO,å2¾õðäûIÕ³/Í:?·û2çß9.ÐÈûóáR˵íX·Ýã-Ê»ôäáïÅ
+AZûÞêMлé-±*÷2ß<¶Î2Þß+H×ðPÏOÉ°ÑPÍÄÓE¶ÔUC¶ÃÒ3ÖQLü¾³ÕèúÇú2ÂUüýýîí¸ã+-ÄíÛ¿ñìÖXè
+ìèÕÍó±åXݼûöÚ÷RHåëíð¾XA½½ìóéÍðÞ6¾æûÛ6JíÄüýݽÝ1@ÎÇ3:3ÐÈôë9:?²I*¶¾Ò>Ý»Ó1*ÎPî4+
+ÖëéÖý±ñæò3Ð<Ò*/Gú+âêG½ùíáR7´êY·ÝÒ-ÒG@æâø17BýúØUQIúìSWNO.÷úK´Úîëó»HCñPÏN9:Ó¶Ü
+óT0Ì:?ãµYð+*â¯=¸û¯Ö°?ɼC6üEPÒDß´í8GÆ7ÁÎæé°í×òß¹·îFÕCãGºö×êû½ø±¿19ü¹WùäзôA·Þ
+Á1ýýÝüÚßÁÝâ¿C*;4òJÈñäTüüûùGE¼üDÛ¸,Ç:ÓVÝü*Sá÷ãøóó³ÜݼúîßµAÛ×Z0×Pî4+>VçÑýY7FTÝ
+,Þøé6ååG9¶ð¼ÅÁåìF¶ëØøñäO1A½@-@DêÓÒU;MáB»ÞÝË19AöçWâM.ÉÞ¿E2ZóúAÔ·ÆÈÉD»DôôØÃXãÞ
+/Ðܯ9¾õô8X.Áõ7×=éÕW»Ô»C6Ü>YÎÁûXõÉÛýýí½ý×÷ïÀ?ÕÀÝà+íFXMäÔWÍ»ìöýýíø**ÈÔ3XZò1·:,
+:¶ßòå³ëøG8>,öÑß³èÜX¹Sôؼ»øòOñ»ëDëT,1ÆÈÊ=Aü@Søó1Æ4Ò9*FJTÝ9ýúôàMÁü6LÔ3ÚL*ÝØC±
+öíÛµåìÕUÍ8G¸ðÑ34²¹ì÷¾.ÔE0åÕL?è±PÉ5éÀÜå°JÄ,8NU+îúïÅQøF÷íL¿ÞÑÙÆÅ2@¿-µ:ÞåÃåY½òÛ
+¯ÇðÖ¼¹QõÇ7ÏUC¶ÀÒQê-îÛÛÁ-ßî´SÀ/ÚBZÞÅãTÚ1Á¿E5óçóõÜÁåIöûèسé.÷ñÏ7öñIçÃÓV÷Vî¿-àÛ
+ð+*ÂÊôÛÈÜíïÞ.*ÍXýý½ú·?Iá¿-/îJÝí@·Öåðì1**ì-Méâ.Î.ãõ/ëú÷RTá7C°×õÆTÎM½¾Ò*û*J»Ëñ
+Õí¼¸ïܱÝ@ÔMÔ3ÚL*Y4CZß¼óÜGéÝX»T²äÃ*ß3Zµ8ë¾HYÌùZûò-*ú¹òàµÕüú·RçÛú;8æÖÓ³Ñ8¼½êÀù
+ÍÍüùÑãÁíPÍÃÛ¼·5÷2ß<¶î0,ºÚôæñíåY½»ÌH÷áµëÖ7¸?-ÁH<Hý°9¿F¹Ö1Þ¹¹1?6HÚÞÕÊ¿åÄÛÖW¼»>
+óÁßÎ2éèâÌAùß3XÍüWøPßì¸N-¼¹ß¾·ýÌË»ÝýõYûîݽýðÓSKH²åYñ±F:À19**äïAмì±F¶Î*Tð·òÑ·
+ø¼Z+,ÑñáÓçÕ;î.ãýOÊú·ñâï¶ðÐOÍ»ÅÒðú÷-N/å1*ä麽úôåIIìEµàWÙâD¿U.¾I1áO5´*Óõ²ç¯Jºï
+Ò;=ÖæÚÏ+FHØ°éóŶ۽üùóåÅí¼»âì×ZåãÍò¾YXøýýî³/õôøõÕ9IEUðÝW´è,WÜÚúÂZ3F¶-+88ùñõë
+µÕûA7ÃRì:óÓ6.ø:õÌIÅ×ö÷ÅAȲËÙ´ºµõÎÀß9ìâ5úùñÕ»EíßEµ»B6XÔTÎÁå9üýÝé½IÜùßä71?ðÞÜÏ
++ëúìá×±S15Çû±9**ðáAÍFÜÕ¶*,ADGB*Sýð?8FR*,øÔö5*ÎÝ=A@çÖöáÕçÓ;Î.ãùGêDõ¾QêËF·à½Í;
+Ó>Yü*SK÷+¾LTîö>*È0ä*ÚPÝ´ùè³5ÔSõçÐáÁ+õÓ5PL÷M-RرäÃñIøÔC*À9ÖòEHîIÇÉÕV¼¸ÛH»Wèí
+5ÉëWÙ³=Ûõ@쵶îJ+îQðýÉýÍÀ2,1Þý/îÞÇ-öµêÍÙYÝ»´ÝLòßJT×PæÓ6.øöæÃù0ôÜÛðÃDîÛÝà½á¹¹1
+ã9X;éÞÞ×å¼8ÛOÍ8ÛãÁßîÜ+H/øIWûûG¼øíÕ-YºÚP¯Èë77æáñRLÓºýÏ3SùR÷çùÄô?86ÞÜ;*Rú418>*
+Ûäõ·ÒëÞ7-ìÉÖTÓP¿Å3¾ñŽÐKMJ2Ƚ0*åÆTÆ=½¾Ò¾ú*Jº7óµíïG³<üû¿?7Ê+¶È·ùüíÍüøð8ÚôèÍ?=
+ô5ËNèðÄ0>ïðÅóØÝý8*2½ê½íö¾Á6²Å;GÆø¸õ¶=ÈF÷Õ¯âÐëñãéÖÜß¿-/Î:?ëÏMQÓ6ÂJôàW6Ç*1ðÇÁ=
+1îëÔEµÝ¼¹ê»Kæ-N»?-¿Hȼî×SÝD8MùÇ=A8íëG>½¶À°+EÔFFúóäûùô8Y0ÁÕú4òóíÖñÙõî×MõXô¸Ø
+XO=åQ°*7ÍáBÑò±ÌÀ»÷ëù´ô?8ÆÜö9ë¾âõø41XB*´ÌÕÓ²é+*ÜCðäR¿Ô6U>ú¼Ê=»D,ÀÎÞ¹*¶ÃÒLÚö-N
+/à1*ÞòBTQíïºõÙ÷ý½íß½åHû÷ð¼÷ÈàÜÈ0ä*úMØ·õÉÍÜڵͻøñâÂûà?P0S·M-ÂÔùøµ+Jƹ»Ð-Ìì9Và
+ÂLÙÓÅåM¼ÕWÃ׳Á´¿÷@P÷Æ?4+ºLHÖ´3ò/É8:÷Ü2FßJ¸òV,ÉI¼Q¼õßXÔLÓTö61P8âK·SâßÇ-¶´èÉÝY
+½ûòØ6ÑH³êÖW5òT0,»ZýõX¿Ô6MÓ»ÑIµÌFÜÙ¹îà¯Y.ýýNÓ¸ûZìæÓÁQݼ³¾ö°BÚ°ÆHÛüQ5ýºõXÛ÷òZÓ
+ÅÔSDPº<-D·G¾2¿Ô½òÎøGã¼ùÞÓ»Ã-üîTØYݸëØ?â9F:´÷Ûà¿=0îÈ+Ŷ<ß½ÆûóÒG³4ëç³Qæû¶Qز.
+.ßÝS:Ó.9ü*SKö+*Iûý»ùôGüIì÷TìÌHºõèÙUçXY°»êUçÞ6¾Mú4ÔõáÀûäó³ß°³T²åÐÙ.Í=µ²ÞÛ,9Üú
+·òçÓÝ**Ì8û·ðß¹-»¿-HÓ9YÜòûýèÉùèñ±ôðßµáÔÒIQ@ù9ÑE¹B-îSõö°äùýËÜÂAGåÃÞOSð³ÍÚWÁò·ø
+Ò?0ÑÄYI³ð3ØMF2·ÎÜøûYÍÍÕù·¶ñ*¼Ë¸öùíYIË@ÙºAÊÕYçüÚÈ¿J9¹ýÜ÷˯Ò,µù¶··8íëG.°¶ÞýCÕU
+6öòðÈÛºúø9ð»D6ÜíUËêE=1½ÛIÕXAµòäÉ°/ÈìêÀ9²AZÞÉìýýQö¯<F*Î-ý+VQíí±FB+îý6ÐôýIüÖüÞ
+ÝUF¶â*8øYͽíÜ=TÑ9ÛÈ?UÑV¯ð¼ë=7T,ü>³ìàÙÕÝíý/*¾ÑÅÆT¼ý¼¾Òîù**Ëü9ý:ÓíAݶ¯çù×¹åéãÐ
+ê¹·óYO-±*¼:TÐÜMY´VÖõ»¹´éÒMÇÔ<µÈÚÏ+åø¸óèÓWÝÄWZå8*Z֯ݴ»F*¸ÜÞ+÷.é/ÐøW¾È=ýG¹³æ¶
+÷ãVì>Ý×ÁC*÷.U6ÓÔAåÉAVÈ85¿IÊâUãýÊ»÷ð¾ÌÄ>PëJøØËD³Æ4Ûß¿ß/¿@º>ó÷FF°*ÝÄHûûöÓÉXɯè
+Eä5ÕW»XãJÎ1ßÅI¸6Ó¹ñÅFùòæËá¹¹1üùõâ:ýýAæѹXÛúøöõAA,¹Í0Áµ?4²Wû¿ÙUõä¹9ÔüÚ·ßJÃÙÑA
+Z¾¶å.A²è»Äýý½øÇúöï½,/<N2Xá=¸¶¾-Ì:K¾üXýýýIÒ»Æ-A7*ÝÐêÈ´ë**ñDóNë3.K±ÔJ<N?Ü»IJTî
+»*:Ü0ì1Ðøù÷ñ¹ì4FüæVÒVäÌOíüÓ²Z0ÞùóÊ·ØZÝ·å¸ÖTË,Û÷ðPQ<ÄÆEDÞPEYXVÖîÞµ*J/5¼º·ðÊA¶
+ÞòòD2W:Óáè1Ë8ëÞµÙÄÒÙXùóóËFÚ:,½äùýýÊݼ?ÈÎçSüÔ2¾ÍBTSÍY6ÕæÀ@ÄTýåÒPË×HÅðJÃ3ûø?ìý
+ÏÒJåïFFÀ*ý0ݹùïÍɼɳüá0ëÕV¸XãÞ:ó6ÓÙMÄ5ÛïèÑI1öXõ¿ù8·×½ôý0Øôâѵ1SèG»å¾èÁGîÓÙ·Û
+·øîñåÛùØéÏUõº@RÇ4éÇ6¶Þî,¸úöïR÷öý½ø8GÄ.JÅ+Û¯Gû:1úMÅ8ýýÍíAâÉF¶ö*8EâѼûüû¹AìÃéò
+Mê32KYÔJ<N?Ú·IJTÞ»*î,SGûûÚ/ùÄ5óP¼T<ZéÕ¹ýTDB-îóNÄò6öXÙZ»éÑL¹äNç<<×,â77¶Ó¾+U==
+0ÊAA*¾ßÂKÁÍîÚHYî¾é;ö½¶ÁýãèôýÝD°Ù¹ôßüÂÔȵìîâ+Æ8¾ý9**»ÓÁúW;¶J/ýýÝüøóéXÌÌVøæýYY
+ÙçÔûÏÈM·*Y±ÇÁM3îêü9Y½H¸è¶Cå¾íÖ³ùõæÓ6.øÊÏðßÖËÛ¸³XöJèñ¼îµYìYIüè»+»ÞºHï+5¶°HJûÎ
+1ºXºRÁ°8WûZõ7ûðÑD³ü>5ðF¿+ÕË°ÚÈD³èú4áýýY±ä¾*VC8LâÔYð*èÒÇù½ßµ·îð+ÚóÃO7Müù´UÑØ´
+åÉC2KUØJ<N?ÙµIJTÖ»*ÞQýµíH»Åýó¹ëY×ŵWÕíæÈ·âÞÖ³ýTDB-îë>PÒúµXØYÛèÐJµØ6Cð:¯àá77ö
+ÜZíÀðîÆãÉ+*ìðNÆ6;84;6ãW4ô/8JGéÀýAI6çéýÑÑØòÇÚ4,çµêî0,¾Ì:,ýYµêÖ¯Õç+8NI?,+>°?>Ô
+½R1?Å1JTZH·>1*WÒ¿¸?ý½û½¸8åßÏ.¶¯æýùÅY»³ÛêÑJÀAÝD1ôT0,Sûå¾õ2ÑIݸB¸ó4íJèñ¼ÞÓó?Ií
+Gòð?6æÌëã5ÑÛ²0ÝÇïSÕ¶ÅM@êÅèÒS³ã¯àÆÜ¿+ź:ÝÌMÅ8GUâI8/ÞM¹ðéµÔ²ëQ·¾ÄLGùD1XL¾Õ´Ròñ
+ÙZÁLOMçìÉÀ2¸ð/ÞçFÆT¼ý¼¾Òîù*¾Íü½½üú8ýõôVÛÁÕìÚ¶îÜóõè51õüÓ²Z0ÞÉïÉåAì×XϸÐI³Ô.·Ï
+KEÐ-ÅEDÞÎÉ4ëÇÐïú-*¼Üùñà·ÅGC=ó²Ç÷/8ÖQÞâ79×òô5,ù.ó׺ÃíÎß?.>;BïïÎK¹X.ìQ×Íýé1VÔÊ
+1ñâ5DÎýé=²æÎ3éñT86D²ãøäÁ5±*XÅïîïýýÝÝHYAIGúýë×ÚÈ¿JT1ý¯¯ùòåÍAËÀÓËHÆøü9²æÌî½í8Ü
+À¿U5MU×KZÓ9ü¾õáS?êÙ°Í<ÃNù?TB÷Ó5ZíÈ1ܹSóè,*ÔøðG½µMõÌ;1ÌP¾á@æ±WÙFÃ4?YûO°.ûB-î²
+8N?áÅIJTB¼*ÞWýê×±QÅýÕ¸÷íÓMYÌÚöí¸ù´ê8/ùTDB-îN?;ÐóÈ?TÏFÒPÉ<K°Ò23ß¿ï77RïÒï47T¼N
+ÒöÕIÆèÁíÜVÐPÖÎÁÝÞ+Õ4RH¿åýÝI3ñ;<Äëë++*î,*ìö¿7ÕøQÜ:,MEYÜIAÂîê*FLTòÒüIË8GÝ.¯AWò
+ÒüåëæÏ=Âð×Fκ=ý1X¾I·îà6¾åUãõöñëáÙ1øáRæõRïXã*Ëüí¼*J6ÓÉýÜGùòæËá¹è9Ã*ÈøFðÔ+é¹:6
+D¹@ÖÇ/ظö-H¹CÑ·ïØ7¸Êâð÷<8ºöã<å3Ò·³Uí¸ÓP¿ñ>àO7¾åÇ-QAÞ·ý5×?ïÕSË,¸ç:KAìJ<N?åÍIJ
+T¶ü0*ºâúÖ¿íÔGE³AGøñãÆ=1P7¼Gпº<ÞÃõûìÛ¹õêϹïÛYÉäV×RðÕ²Þ?8øµ<°Ïý<ÇKL9ºÞÇMÜ>,ʹ
+ðÝKÇX2ß×,öëμKú4>àV8èCÏÔºKúø>K>?êßF¾Á¿5ZæÇFÚäÃC*NæPØÏÓ-S;÷ÖëÖö¹0-¼øýûùõåÍý¼F
+ÓÆ1IÊ4îOÞEô:çÃåÌê.ìP/@SFA½¾MÕÐóÍ?¾Î9á¿E1ÜAã¼ñìÛáðô»>E¸õð×ðÞç0¶íù88O4òÚÐ3?K9ô
+J<N/éí,*·.ôé3**Y+*ð5ÕáL¶I,ÌúVÔUÕXÓDÓI³ØòRÏMÇ8½´-ü³>NÆ:ÛKQúÇ1FZâ¹¼¶T.å8Þ½±VÒT
+?PÅR08æº:;øàíÛ8âÄ5±*̸ßT°Þ¾÷à/¿,¶øÛ52éöX,J,Ü;¿Ã7½F+3T³ÄÓ.ÊîØ.ÚøñÜGùÃùÔX²Êøõ
+ðèñã¿Ñ9Îà-@ñ´7çFïÏñÎ:3ÐÀõ7**áÆ7Æ5Í6@ÞçFÆ4äõ**2**Ø:AüBW¾Ñ·:?Úýü+οT,Ìä¿ÒZýù1¾
+0T3¶D1Ð4ßµøC*Æ2Ëöì+*çâÌT**Zíçóç¾³+QñJTçí»I-¾±ÂåܼýøëÕõóçÑ1½G¸ñåCHTýýýQÊTQÕQ-
+Ô/FË-Sýüû6*X<ÜõéëùíÍIíåY½ûóá¹9ÐLýü3ÑðÝÏéÑ+é,ÌPÀÒ77*¸<ÜóæáõIO³ùë±Ýû*S+ùJèõ3*
+î²<N/ûùC*.Nº¿Ò̽½ü*öéY½Gî¼ÂÒ7ÛH7Ö@îò5JT2÷*S;***ðÑýÃÇ4ö1*Âß/+Ê.Ò+ÚƾM:æÂ2*<5S
+ɵì;³ïÞGø*üSÒåW2º¯9*Q=KT¾I***6,FD¾58¶×,ûÕ+:ð:ó¿¿-*åƸ´Î¼ñ2·ÐîÌô²ÎüL4¶D3ÐÀ÷+*
+*Þ:Þ6JRì/1..FZ¾-:¶=.Gõ*J°:óÃÏ<**¶ILè׿=Ñ·KH¿FÅöÑ´Î6îà,¼Ù+¾Ñ·:ÓòýH***6,.+Ê-Â+
+Ú:¾-0¶L+7B*ÁÒ<Hý<TL½GõENWó·àDÕÆñÄ*XDÞ½=*¶D3ÐÀó+*öñ*10î¾*XDÞK+ÒBFJÁ¼<¾>ÆøÀ1X
+Ôå¯üÚî¿ýP*î²<N/å-**¾K¾:¾·¾B¾-,R0B*¶ò-Å6î.SíàJå¾*ÊÊ+Sí*ßÓóFà͹ó·ß*ÍW+ÞçNÆ4È1*
+**Á*K*ñ*¯*1.îÂ+XûÞ?-FBèáAæC;*>-S+ú-NOó×ñ¼ÂÒ±-ÓA1Õñ2½ÌúÞË0º´/*Q=KT:H***6,.+Ê-
+Â+Ú:¾-0¶Ï+µ-ZìõØ/¸Ä.úOºéËYÕÍI½Ü½Ý¼ûï¹ý¼ÁÒµèñè¸8ãåË5Fç¾ýP*îò³Ð,%%
+d
+677 77[1 0 0 1 0 0]sl 8 mask 0 77 di
+/mask 6545 string uc
+*Þô**8ºýÒöüùõûõíùíÝõݽí½ýÜýü»ýûùüùõûõíùíÝõݽí¸ý*
+d
+/sl 52129 string uc
+ìÕí´ÉA9êͺ***B.R2**Ä,è**¾50¶,,µ/ΰÎÓ.Þ±àÙüº¸÷ôë¹ùóéÕݽÛ÷ó69Óýü×=PÓ豶-*F¶*JH
+8:2î2½XÚÞÃ.»¶*îQSñ@Ü»ûûüY**HëäÕYUAÆ4Èõ±*öÌöܱåüGºöÌûùõíçíµíü3ÐP<0*JMÁRAçN@N/
+çùC*2RËöì+¾æNó*S=3-*ÎÏKÒɲ,ÌPÀÒ¾»I-*>»èñ¾è+Ðä+0*JM½REëâ9JT¾D³ÐüÀBÆ/Sßø¼0*ÆD-
+дòø²ÞØNÀÒÀýö¼0*¾D+Ðôòø²ÞØNÀÒ,ºè¼0*JD+Ðôòø²ÞØNÀÒî½8**Â9H¾ýÀ7¹´ê×YáPÚúð9*:É·/
+3:***FJ¾2¾Æ¾5:B7¶L+µLÎ1*:+Ð,øí=*ÐMèÑǺÇÒÔÉñåÚ0*êUü´é0**X1ò·F¸>øN,X³Þ×2ò7Ðø×Æ
+;=PúÕí¼YASÍP*:8ë6+2N/*L*ò½WÃY/ã½*¾¾0/D*JZû9@*ýÍ:óË.Ú辸¾M;æBNó¹ãÅÕXº³è*9öÆ+*
+¾H¾EU¸>+é¾¾:ÃB¾3¾ùGÆY:Ì>ßCLVRQø:SÍP<:¼ö*úèXS9G¸ò¶0*F0,*S+0³Öå¸ø0.ÌRJGÞ¯Wò0Ðø
+åH³ôòÊDÞ¿ãM-ȵԲM0ºµ4Õ·:ÙIÚZÀM3¶ZÝí*SÍPÞOÐøT±A÷ùõÍݽÞY>JB*V¸MÍÕÆø0.XTJõ:ÓÙý8
+ÛôâͳTYO»*Èÿ*Þ/-ÚLéؾ¶¿U0¶Á,Aîî6/8ëÞËKÚ=ÂÁá¶Í,APð2:ÌRà+<ú,N»DÇAÇèO¯O²ìF·ñâÛ
+**JòVJYWòÏ0Ú6¿ÌKTýêòæÌE?åÒXòû4H¸*ËA:îÂUYæçK5F¯¾=²Æ//ð:¹á¶Í,Õæî2-8ÎÞ73º*N»D+
+¾ö3*Fî*-Ã>ÍüÅ*JàðCü>**Ò,¾ýµVø*.Ä58RJCÞ¿9ò6иøë»ÙìV÷ãß<ôÓîÏ5LV2R6¶K+ÕÄö´J=J¿¹
+Á2ú·Þ4åÙ:ËUÚ°ÂE5¶ó/Ý*SÍQðÓ3:QNéFMTRÑ3ì·INóîÉøÒSÓðÄ+/NRßMµÇظ÷ÀB9BJÔN»-0µJ÷Ú:
+зÃU4·L+AOî/:³P3ZÊ°¸3ù4*¾ÉÌNTý8ù°ãÈ=íùRØܸàÁµöö¾+ì*±ÀG»ÃÒµ6î<A=³¾U²¶.,AúÙÇÉD
+GÐ,*ÕÃõê.Â<>å*ÎáÄD1ÐøóL³TÔV·ßÁðÑÓîÏ-HºÏ*1ÕBÓSÜðÀáEÆÀ:ìÑßOºÚPè=@ö/ÆØ¿=:-иÛå·
+áHë×ðÄÂ>MQ1×2*Fƾ3á5+B5¶Ï,µÊî.28¶ßW4ÚàÀáÀ¶´6õSî47XÊNSÞóÖ´¼Ð,*ÕÃõú.ø=SûLÌC¯ÔF
+C<E,³çÊîÏóãYðÑæãýÚ=дî.â¾ÒU+ãÓÇñ²ß¹KT*îØöðD+èÄWÙ*¾ÜNèãûZèÐ**´µ:ÛÉÒçQ¾UÉ9½¼úö
+ïUéÇNû:G:GÐ,*ÕÃAÑ3Þ±ïáÅAI,JòK5*:åγDдÍÒú/=Ç8S±Í*SPû**PÕH»øòÏ*L»×±Ú»üÙíISAÄÒ
+åUCÑ32¯ßY**î¸**ËÄÒ¼@°âÂ3ýóÙYÁ:ãÑKèê=Þ½ßÄGEôêÕ¯=X¼÷1*¾ãÁÒ.***12:,îäS1,è*T.È2ä
+1̾ÞôÇèõÈ9YÛöèÒ+ÜöÇàÆUV×6+ÚÆËÉ/¶ã+ÏJ18:176-Ú,ÐER¶A1QëïæIÌRÞãKÚEÃ.,ïð1XSLD¶ÖY
+Ðä±Hå9Nýýíã÷±X-*ô?½Ù³-**ÕE8W6¸N/ÕTî2-8³ÞøÇÈIùÒ×ѱè*9úåÛÁêRNXFðøHSÄEÛº5+ųîò
+4íåK@̺âÜÕT:1íHÓÉÁÆ·I-ÙQè²ýóJ*ò½WÃY/ã½*¾¾0/D**Z7769FçÎáN»Æ+U7èVÆèë½ÅX¹ðåDÓA*
+Û4É-ÔºÒ»Q@:XîÈ+ìÎÞ/FFM¿UEBC¶â*1ÐîÄE8ßá»7ÚCÀE1·²/Që;¿CNÀÒõ×ðàJP?ìVâ/:ڱʼJèñ*
+Ç3OöÓÛÐH¸òæï6*¶6.*ÐȲP»ì:ò71Fö¾-.¶±,1JZýNóÍË8ë·ÑðÊèÃÜYżô*,X»ã×SÝR˱ڷ¸-1Sîæ
+8Xïà×8ÚȾéD¶B:µ2ïÂ-ì×á;MÛ¿¾±à¶P+QÔîHUXÃàÛDÚG¿HJèê=ÞUËÛâíݽüü+½Ï*ß*ZçOêFç¾U5¶
+õ±Q²ZûNóòÙù´Û×ðÉÁOêUżÔ:ÎBîÀ,X»Y,Ò¹É,Å1ïÆ-X@Þ?VFÕ¿;Ç3¾=7B8¶ñ5Õùï2I8Câ7MF>ÕÁ@
+·º@åÇøJ7XûL-:ó÷½â²<X4ULñäÇEQì/**LCBQ·ÞSÆIVÐÛ=ÓÁYèÑPÓÈE0>:KGøÇÀ¾-6BֹϹ16:UÐÜ
+,Á=ÀR-¶=/S:óG7ÚCÀÉS¼ß,µùïð6ìïßKÐÜ<̱C¶ÈÒåöÔ@ñøNôÌÒ×Q¾¿LÈQýM*:¯÷F¶×T*öýîÌXºÞ+
+òÚ¿Ù5:Ö¼ÐøõÖAUÔF÷ÜÅÐóÓ-5·.@AñFáÌH.ݱ:¶4ε::·ÐÜCÈéû¶¶01õîÎL=ÞÈÙ϶ÆÒQÏÏ-ÆøúI°D
+3ð²˱N/³´CBUMTÝXÒPãÈ?AºR¼Ø¸÷ZñäK*M+·úëÓ0ÚðÀáE¶ÃºÅÐÎ:îL,ÌÔïG7ÚPèMR¶.4A2ð43ÌZ
+ß×ÝFõÄÂ:ñ8S±BÇÛ=³ô3ZÊ°¸3ù4*¾ÉW0ääÆèóHå7ù²åÈE<Ð6GGêØ-9-*8×YWÞÃ4Ú¯À=J¶Þ/µSî45°
+¾¾TÀ57¶ñ/A³Z6îP´9+ßÃ4FξM.·Ã.óBµòîæ°ìÛáS-FNÎEó¶*,ÅXïN/¸*SÍýMT=;ÈÎF-¾W,äèÆèAð
+Ê3±ãÇ;×;RÉäâJIU;¿³¸XìQÉûNìYßHS=Gдî.â¾ÒÉÞÙ»:í4¿¸·N˾4ø,SÍ-âÌÑÓ=Ƹ·å×GSNL7·Xá
+PÎZÏÑÇ8SE¶,ðMè1ç+ç;ó·=ÝE+èÄWÙ*¾üNTJý»,J¸NËT,ÚØúöñáÅ9ÝØõÍÒ»ÀÒ*Þ³ïÛD+º@¼úöðM*Â
+H²+*ð6ØÌNTI5C°àÂýøëõó<³P3:ãÒ9*ÞòèÑQÍH4JÒA¹Ö-ÅÁùúºE/SûX÷ïÓ5YËDÞ-H°**Î1îäÈ8OÞD
+::N+æÕNÊ-6*F8À6ìîâ/XOÞH:ðê9XNÞÀ±ûN»ÜÈÕPJÂ/½+*¾é+*â+/׶LÕ:Ó½ôS±QåÒ+8VH¸8·¹Sè
+<¾¯¾¸¾À¾ÑJ¶Ý0AZîR,ÔM;WÈÒCO¾ýÀ7¹´ê×YáPÚúð9**É1SáøYîÞNÞ.Þ33>1F-ÀÜKTµýG¸ëÇIéÒÜÈ
+ø*>É/+¾50¶/¹³+ï,Õ6î0/XÞßC4FØ¿=ù»Á*å>Ï@îì@ì¾áSÐܾÇ=6¶Ó6õ6ú°OìGâçàF+¿ÔMèñͺ³,Ô
+ÉñåÚ0*êUü´é0**X1Þî.2èQíÆÞß0ò9ÐøåPÅôÚS·PóLíÕîÏ-æº++Õôõ45YÖÞ3-FKÁVRîâ>8;óWAÚ>Å
+-V¶@ѵ>îR2ì¯áKÐÜ/ÆM·ÆÀHØ8S=Æ**îL*ò½WÃY/ã½*¾¾0/D**Z7769F¯ÀÑ4æSNóòÙù´Û×ðÉÁOêUß
+MAúÔ:ÎBîÀ,ìÞ¯<*ºÇ,õ0+*Ú*嶾Ⱦñ»ºë7õà÷ÈIÌøàK¿ÜÓÈM7¶æ2õòÏóÆøÚ=YË*ÝØUèMÅ8GÌ+*Å¿
+*JTîÞ*J?Þ;ÐÜç¾4¿-7æINóîÉøÒSÓðÄ+/NRßMµÇØT÷Â@É+°OèEZ¶°,µ¾øÒ<<Oè5I·O>å2ïä0ÌZö+¹
+Ú¸ÆM6¶L0AÔÎñÆDÑ.öT±A÷ùõÍݽÞY>JB*V¸ÖØÅ°øÈ,Ø,RÑKTý8ù°ãÈ=íùRØÜ4HTµ¹MÇ16îÊ6=PèE0
+¶?4Q¿:/ÐHоÉ>¶Û1QêðàNÍ»áßãFÍÈÌ+è>ÇøÚ>Y˾ÉàÈæÚ·ðäǹ+*¾æÏÈÜQT*îT.Ø1Sû¸ÏD?UÔÚîK
+÷çTöæ×ã+*1éöÖõX°YDE¶ÄÑÕ°:-GÜèĹ3¶Ô3µåîØT8¿áGêÚÕÅÔMèÊ<ÞJ;ãÑý;*ÆÖº0JQLY:Ó*öÝ@Z
+Nî¾RUR9+Kå:Óé»ÌEôçÌ?µNNC¸4H¸Õ¶?+Ñ·A,Z@îL/ìBß3:FîÀÙ>¶Ã/-+ÅäöL08BJ·áW²H4¿E-·Ð/
+ÅBî4EXëß/ÓÚ¿Å=¶·à/?*Å»ÏëÆøú=YË*·LØäCÐÀDWÏÍ5KTÝƱPÊ?QÄâÄJWD=º²»XÉâ>Ø01Ù7S±È*á
+>ÑÉÖÕ**Ú²NÐP3ÈY=S+¾é๴,L3ȱ*æ¯MË;SAõÄ×ÆàELè0̾úÆøK¸J¸N/*X5í÷3²QÐôOé²YÞNÅÒ7H
+IICS+¾éµ¶µ,Ò7Ù´+*7ÈÒ¼@°âÂ3ýóÙYå³äYJèÒ<ÞÓãÅII¼úöÓóQÐøÛðáUAÜD³¾A@Ì*ú1îP6̱»ð<2
+жá4O+;*1XîN,XZÞÛ3F+¿É³2Ŷ¯.1ðî>U8ûß34F¯Ã±ä¶ã31ÈîâK8=ãÛXFËËñ>¶201*ðP+´ÃÒåEïè
+.î×öïáÉ9+:øÎ/*ƱæðÒÅ8LTüêÑäÌÌS-ºîIõ@?çTãÁ-ÅÂôÊG9JÞ;;ÚKÀá:¶û*QÒî468èÞCÐHN¿¹ù»
+Ó.å´î24Ìî´0ÖÇ0»ì?KÜÉÈ8S±È*SPû**PÕH»øòÏ*L»×á;·Ý@3JQ±:ÓÙý8ÛôâͳTY*¸/È1êõÊW8VJÚ
+Þ?-ÚK¿5¶¶Ä,Å´î<59³ÞÇVâî*×:ó3·Ú¿ÀéD¶Ò9åTïÒWYKà¯ÐFNèUζLD1ÑÑâîXÇÌ>ßGQ㲿ç;ó÷JÚ
+D+;@ú/**X1*:-=FüÁ°Ð/ÆèIEGØñPãCÓ5»ÝìJñ/.FðÖ¹K¹+YÕHùô08ÐÞ?UFçÂÉNW¶¶S7ÅJ:EÐHâä
+ÑüºÖ5÷:óWÐÚßå±,¸.E1ßñºÀXLÞ;0V.S±È*ý/EôêÖ±ÝÄ˶÷ãI**@1ÐH<ó502/¶BTÃ*÷ÞN9:ÓÑíìÙó
+äÈY8@¯YGðÓÀ¾à¾54¶OBµÏ@+4ÚE°ÃÑýU*¶>51D÷Þ.8=óÃ?F=ÓE¸ºN7Ÿï².ìOò7QÚKÂ=D¶É>A0;1
+ÐH/¿M¶¶´IÕÛZ?N»¼2ÕPÞ¸·üÊé*¾»ãí´ë**ÞóÀ»ìCÞÛàâà4Û¿Ò8¼SÍDÓTìBξ.ó.îÌ·V³ìÇ:Ü+âê6
+îà5Ì4ëÃ7FÍØÁã¹±,åZïÊ:ì/àGàHSÀÑV¶ÃÒÅZîT6<JȸÂMö?;ÕÞñÆÄè,X4ß;.ÚÇÒ5ÍÖ.ÐP:+ñ=³>
+RUïUK÷9*:ÎK¿0**VõØðÌ+ì¯NLAç.Nóí¯YÔV·ð»9OéUQôðîßì+PàN»á*QË÷Ú@ÌËÞ»ÐH¹¾ÁO¶È4AÈî
+H9ÌDà×,ÚQtúÆøÔ9°Jè5Ķ.4¹Jèñ¾¶³,úèXS9G¸ò¶0*F0,*SÇZ7úYÑ8*ÆTOØ+Sû¸ÏD?UÔÚîK÷çèçU
+ÛÀÊêÆøÈ5°¾Ø¹1¶á4QHî01ìÀÞ÷HÚKÀMX¶ø-ÅSîB,X?ß·¹ÚßÀáB·ÉÒåêõê1XLÞ;0V.S±È*Y³ô<½ýü÷
+ù1úÃ,.-¾O3çëCì²9иÛå·áHë×ðÄÂ>MQ1õ,*FƾçÔ5+B5¶Ï,µÊî.28¶ßW4FÃÎRÀ+¾5F¶Â5A>îæ.XH
+Þ¿/F¸¾5ʶ==Q4îì>ì>Þ74ÚSÃÁI¶M3ÅÇðè*Ì/ßÇôF.ÍEòS+¶¶ò?0AÌîÞ0@ÀÒå-ïè.Ú>ÔÂäË=µÌÚ5*
+*Â̳ãòáË0F5ÓÂTÐ5ÆèAðÊ3±ãÇ;×;RÉäâJIU;¿59èQ,æBJÇ8S±È*-Ã>ÍüÅ*Jàð·ÅÒãÒAàú8SY3½Ý:
+óK*ºØN»H@ÕP¾F;Wñ6SK˼÷ËÓ:óðØÔÚÜÑÈر¼<GÐä>+ÄSPA¯Y+*¶çAÝ×3мÆ8Æ8SÍÙø×32ORÌ+J5ä
+å+Ð,øí=*ÐMè0̾ÍËÒ*Þ³ïGI¿üNTI5C°àÂýøëõó<³-QJ0Nó¹ãÅÕXº³è*9öÆ+*¾H¾EU¸>+é¾¾:ÃB¾2
+¾U<Æí7ðÊÀFîïN9Xïá4J¯2=ÉJââ41øîP+ÔÁÒ*Þ³ïÛE+èÄWÙ*¾8OTýêÑäÌÌS-Föðà8ZÞ¼ÈØ.Î8îR.ð
+±Á.Ï:ÕIÚȾ±TÌI¯ÌNà¯9N³;åD°ENËT,ÚØúöñáÅ9ÝØ0OTµýG¸ëÇIéÒÜÈø2ùÁñ8BÞÓWÔ/Â5Ú°¾E>¶+
+1åRîæ0ì:óÓ@ÚÊÀU6¶çÀõêï°GX:ãÛGÚDÅÑN»<,Å3îÈHìæà/ÙÚMʺÄñ°¿ìæá6ßÇ:ÚÖ¾F:</Ð,%
+d
+677 77[1 0 0 1 0 0]sl 8 mask 0 154 di
+/mask 6545 string uc
+*Þô**8ºýÒöüùõûõíùíÝõݽí½ýÜýü»ýûùüùõûõíùíÝõݽí¸ý*
+d
+/sl 52129 string uc
+êÕí´ÉA9úý0**¾M4¶H+öVúöïãÅ*2»æ,*âC²3¯KÑò¾IOËÙöôóõù½+*ü>Óýü×=PÃÜíûêòæÌE?åâèóûL
+ÍH»*M*¿*/+Ã+O+?*1WîÂ+ÌÎK@>¶K2µMî>.XËJOÞ+.ÚÓÃÙì¶?,åÞîT.048ïÞ³+FòÁ-ØB,¶ÉB·6åÇJ
+Ú°ÇU;¶Ù*SJN;ÞSÓ×´¼ûظÈÒQ=*2I+¾-8:ãÒ9*ÞòèÑQÍH4JÒA5Ó»âØÕ>ÎáÆèçµõ븱ãA1ÉÖÕ83é÷ò
+Û4Úò6îZæ0*8ÄÞç4Hè¿=è¶;Éå³ï°Qì¼àß2F¹ÃV´öÈ9Ì?ã³0Fº¿-V·I9ÙNèÑ庿ÑîýÃ-2¯ßY**î¸**
+GÇÒ8¼SÍDÓTìBξ.óFSUA5+8ZJÜN»-0µD÷R.ìËà+¸FC¿5»¶ï2QKøî;0/ÌìßÃ2ÚB¾±3¶ÃÓÕXñä-ÌRÞ
+KOIMÊU˶O:AäîÒTìÃÞï,V.SÍPÞOÐøYÖ¯åÄGù³êÂÉYü+*Z÷/ÐÜB¾-0B/æ°Nóí¯YÔV·ð»9OéUQJóÈö
+ÌWTÄG»æ·ÕJî80XÄí³ÚGÁä¹÷¹36ÅÜðR8Ìëß×.FGÄÙê¶ô=õ¼ûÔVÌTå»Óâ¾ÒAÚZ9N»DÊAÇ踷üÊé*¾»
+ãí´ë**Þó4QåQÍRJÉ;ÓYATÑ°ãÈ5H>IWG/ÓÁíÈÈOY·ßï7FÐÝÙù¹ù¿ÕÚ;Ñ>FDæÁ¹¶C,3=óûSHǾ߾º*
+Ѹî45Ì»ßÜÆø²,*ï×Q+*J:³.>ýýYUïUK÷9*:ÎK¿0*2VýÉ1à30+à*D8SÛìñÚ¯ÍôV·MLÈÏÑÁô¸*S;EÅ
+<ÕPîZåÌ*ß+AÚO¿UV¶/-AÃîLðÌóÞ/<ÚÆÆE±¶*7AÑîL+ÌKãÓ2FVÅ=Ú¶Ù*A8ð¾,8TøÇøÚ·ÇÉ··Ó=ANî
+L2XÛæ3JFÏÌ>6<SÐH/PJúî+öÓÛÐH¸òæï6*¶6.*Ð,6,*ä7,+à*P-è-ø5SûLÌC¯ÔFC<E,³çÊÚö²+FÖÜ
+Sò=Sâ0HÉKèÑ3¶ÇÒÜèêOüüûñõ9ö5/20*ÉXâ²CÞ3/òÌ*â¾Ò·ýèøäÔóÇDÑ:NÅÒQÇ÷<SÛ>ÔÂäË=µÌÚ5*
+*ÂÌó>*¶óÓûÅÒÉ5ãëÇLU<³TJÆ8SÍP*:¼,*1FÞJ;ãÑý;*ÆÖºXÆòÇÐÌ.òÌÏPÆÒ9Ð-ÐMèñߺ³,â3çDåÆ
+4è²ÌÅäÈÒGÄA7í>ç;³P3ZÊ°¸3ù4*¾ÉõÍÒË¿Ò*ÞóEFE+Ï.ãC*²ßúÚJTI5C°àÂýøëÕã6ñO:³Áåå/ÐøÛ
+ðáUAÜD³¾A@Ì*ú1îP6̱»ð<2ÐÆX3ì:JÇKD3¶Ø0ÇÛLù<ó·êòIôâÅÒú×QËGGÑ0öÞ½À·ÔÄÒÕJ;=PÜ÷æE
+5¶H+I,S=Í*¾ÛÙ*:/иùíÏéÌú×Pó/J1ÆØL2*ÚZ¾U²W52A¶ã*µRî,8ÌÏÞ×7FCÁ-8¶õ+QÆî0D8èòç3Ú
+VÁÁé¶ø9Õ,ϱÇøF´<D¿BJèÄWÙ*¾ìMTýêòæÌE?åÒXòû¸-ËA:îÂU9Hï/5F¯¾=²Æ//Ì÷Þ¿EH8ı̺ÄÒQ
+Äú6SíéàC2Úà¾HPèâ=Þ¯áÔ@Iüúöï5úèD9SGûôÜWµÔúðZ3ùèÏF@.>0ÚξU½ºÙÀ3ÎÞßKF?âÁÛRó¶ÍÒµ
+Úî6SíçàK6ÚáÀѺçÕN»8E3˾à¼@¼úöðM*ÂH²+*ð6Ø´MT1ÝÒQËT?µ6RJÀ¸àÃå̵*Î4Îâö.7±NèUZ¶<
++QIîÐEÍ<óó.Ú÷Á5³¶Å=ß*AïÏÃÇD÷.:êãÒ9*ÞòèÑQÍH4JÒAé³Nóí¯YÔV·ð»9OéUQÚöî8í˺àN»á*õ
+<øÔ5ÌSÞ?²â:+A?î@/ÌÍJõ<ó·êN²,.ÙÖîA**¶G*¾XMTÝXÒPãÈ?AºR¼Ø¸DÇ3ê¯40¶ã/ŵîÒ@ì+ß3/Ú
+1åÙ3¶×0åÕï:ODBS=Í*Åû5´êׯå¼ëZñðɽ**VHNO***B/21¶â*µRZLî</ÌBÞäÇèóHå7ù²åÈE<Ð6G/
+âÚ4H*îJ,XCíVÞÃ4Ú¯À=J¶Þ/µSî6.Ì.ßÛ1FMÀ-D¶ù0õNï.3ìàÞ¿RÊÁFOÇä5*Ï3íG**ËåÇøF´<D+ÂÛ
+ÉñåÚ0*êUü´é0*.ÐH¿æàæE0¶¿,1µî8/ÔÌÒ¼;åÊBUÌ6Ç7¿Ø²P0ä×43òÝ=ô-¼¶ºôóùÕí¼ûøÌH»öéÏE½
+8í·ÐPD+=ó±*4ÙÔàÕÀðI*JJÁ,7**Ö8Úî¯ÉP¿,1<îÂ,ÔËÒQKñI<Ó½ÝìÜýüúõñÅøéYIܹ,N»ÜT3˾á¼
+³ÕÒÅ8G¸F-*8-+¾Ò+å*¾R¾Ç¾â6îà5D-SAAÈÙ=ÃÜPTÙ8»úüÑÒ´Ý»ôÙ±Jèâ=Þ¯ÕD¹ÇݽýúǽüL+À+ÞÐ
+F¹ø4Sµ3Ò·<óÁò*¾AÆè÷õå9*½ûóÕ-ìC:ó·ûQ²,8Ù>ÔÂäË=µ8ƹ+*¾RX@/*ÐÖ¿Ë=?ÎýH+î*S;***ðÑ
+Í-S=Í*Â74SHû9+¾Âã±ÞC8¸,:<Î.î¾,0QÅ+ìÑÞDÅøõ<³¶Î**:3Ð,*Õ7DÏ3BêÌÆÔ·ÄÒÎäDÙ·¯Nó±;M
+PÊöYÝûÜPÐÔÏ4**ðJèâ=*¯±ÈçOêU**XD7SûX÷ïÓ5YËDÞ-H4¹1*ìVçÏ.V-,À>*ûÈÂK¾·ÕUD¶á*QÄîÊ
+7̾ÞGC²·ÐÜBT,**õ6Æ4*Ú´æÂPÞZáÔ.ãC*òÌÏä¿Òü×QËGGÑ0¶Z÷æG@ìæ0YÊàÇ+>.ÚÇ¿M:¶Ö7µØîT,
+ØGSOºüOÒÂü¸ìC:³ð3æH>Ë<SÛûõæ³QüVÑøTT*îÂ**û*Ð-0B/¶+ų+ï,Õ6î0/XÞßC4FØ¿=ù»°3õÚZÈ
+î0GX0ßÛ.ú5N/*XE²L=æÄNóÍË8ë·ÑðÊèÃÜYßM*Jãï/.FÇá5¸¸æ,Q6î.7°K¿=ѶJÝÕ?ïT+ìJß?/Ú¸ð
+Q¿Ò7F+î²0NóòÙù´Û×ðÉÁOêUßM*ÞÇMHºäE+¶HÎ3ÎÞÛK6µG3Â=ж×Ö·:óS¿ú2N/*X5YX=JëÖ³5*æON
+?./Ü-*Jö.SGúÐH³TÓÚ¯J+3Tö2*¶ÈÒ?OÃ*1BîR0°°¾5Y¶1ÎAV:3ÐܾÄMý¶A+7ÍÒÖýüùôëQÀÒçQ¾UÉ
+9½¼úöïUéON/ðÍ+Þ;ÐøõÖAUÔF÷ÜÅÐóÓ=°ºàÀõ²CåÌÜLèÑ>»:,×:óW´Úɾ½¿Ò*Þ³ïã²,öVúöïãÅ*2»
+æ,*âC²7ÂÒ̶µõçËEQÜÙKÔêôêMNóéÃèÒV×ðÀ1ãQUßMõâöTA±MèUX¶¼ÎQ9ÎÚî°RY³áYKèê=JÈSI*¾ç
+ÓQÍH»>¾RY@Çêô,ÐTÞÐÑY½üú:?º»ÂÒXõ·ìÖQ¹ÔÚ;;ãæçKW,*1<Z:÷L*ä,XÒÞ7=F+À-X¶É,ÕÖîL?8¸
+ß+:FØÁM붱1õ.ÐçǸÐ>.:1ÐHÝãÊPJÂ/½+*¾é+*NÀ0û¾ÒìºøñäÇñÄ6C»ÂÒ¼;åÊBUÌ6Ç7¿Ø²PöæøF
+Öó÷KNãúùí½íÜ»ø·:³ô3îÝZáÌ7¹ôéÖ3AÝú-*ÞðKT-*Þ½*ã*OÓA6Î?ÆDÄTÂí¼ÅÒ1+îç<ó·Qä²,ÔÉñ
+åÚ0*êUü´é0**X/0ñÐóÒµ6ÎCÆÈÆ=åG9ÐXX>µQ4í+S=Ë**îL*ò½WÃY/ã½*¾¾0/D**Z7769F±¾Ì:*Jö
+*S/¶ìç:óÁò¾LT*Z*:3ÐH9ãÊP¾Yë?óÏMÅ8å*¾áJ*:ÓåÆØCXð¹.N¸NË»,ºÓ×ÉúûùåíIîA4:6*@»ýNÛ
+¶ÐH9ãÊPÞO¯O²ìF·ñâÛ**JòJP9<ó·K²0Ðø×Æ;=PúÕí¼7¿=Í1S±Í*-Ã>ÍüÅ*JàðC=O*ü-R0RF»ß*ËÁ
+Ò¼ÕúöèÃÕP7îùIJÉ56Ö͹.SÊü6-Ú;¿úì:å>â+9Qøîº,<öÄÂHðÀ2ìQKP¼¶Î1µ4:AÐÔBË+*ìC:ó·Lâ
+²,â3çDåÆ4èNã:ÛÀÒú×QËGGÑ0öÞ½ì3èÀÆøä>°ÄÃ4üÃÖáÃIF;éXÆÍCÍ>à·9âÒ1õ4<?ÐÀöAÄTÔñÌ+S
+±Í*á>ÑÉÖÕ**Úê-=@ÌÒìü¹òØ=½ÔS»+æ;ó-*1è@ÌòP:8VJÚÞ?-ÚK¿5¶¶Ä,Å´î0SY³ßã>Úà¾5ë¶ß01ë
+ï°GX:ãÛGÚDÅñDË7Síîà/ÚڱʺÈñð¿ìîá6ßÇ:ÚÖ¾>:ð,S+ýZ,,**ûFÆøº9ÐD+Ï.ãC*âß9ÛÁÒü×çÏG
+µTÌSÛæùó0ÖXJÞÛòÛ×¾5@¶à*Qæ:54F×ÜÑ;¶4,µ?öà+XèàWµÚ4¿±Z¶?,-/Õ4ñØ*XLå+X6+Ú³ÊFÈñÂ:
+XßâÓ2ÚW¾><ð*S3ºüûMWÊüøê·å¾Ò7F+¾Z0NóòÙù´Û×ðÉÁOêUžÓ:ÎBîÀ,XWìWYP¾,õ0;ÜÓVï@SYËà
+S´FÇãñض×=µIïZ.ÌPù·+FHÀÁA»Ç+1Yî¾Uìíá0ÆèËYÕÍI½*ùóEûï¹ýÌ*SÍõKÈ=æºNóîÉøÒSÓðÄ+/N
+RåÇظ×2î*-8ãJP0¶ï/1QôT.ìËà+¸FC¿5»¶ï2QKøî;0/ÌìßÃ2ÚB¾±3¶<=Aìïê6Ìë`F°Å±Ä·ó3Õ2
+ðà6Ì°ãÛ2FX¾NÆèÍQAíÜ»ùóéÉøùáUí=³-QJ4Nóí¯YÔV·ð»9OéUQFóÈöÌOTÄG»Æ·ÕJî80X¼íW´ÚÐÞ¹
+ض66ÅÜðR8Ìëßç.ÚA¾ÑH¶ØÇåÇø>¹ì±O0N»ß/S:Ó½åÌû¹öî×½ÕíÝÑÝíÜûùN»ì1·Ë*³áÔW*J<öæMNóé
+ÃèÒV×ðÀ1ãQUQGóZÙаçUX¶º-Åîõ´üìËá7´FIÁñµº//Åôî°.8PÞ»+Úñ¿ùѺ<+Á+÷2PóÞ?@FùÀ6ÇTä
+à¹1*Zô/Ð,Ë1W¾îçNöC,*Fëá*¶ÇÎQEJ²°½3*AÆÈî»Ýü4*î¹ùö.ÈíÀ**ðX0J1,*3úɸ9-Þß´ÜÚ*øìH
+Ü>ÆèÖIµ8û7*Æóø-S/¯Þ72*àÊúÁ+æ»Àß»ýø?YÎJì»6**íÛ´ÜʾÞåFÆèÐY1Iü3*2ùü-SOµç72*LËúÁ
++æ»ÀKÎ398N/àùB**ܹëº3+¿ÍS:?½AYI½üú+Â+JüúùùÕIÆÈÚQµÜý*JÜǽB*ëµBÀÚóJTæÜÅ+*îGéäM
+.R9BúÊ,SGºùúùõ1*Jý+*ðÝÝûJTɼ»øñë1*QÇØý2-¶1ÐÀø»6**í×´0.F·¾¿¾ßåVÆTö-¶**Ľ½üó-S
+Ï÷ñåË=í»*¶ÈÒËABâú·ñÁÒZ½9-Þ:óÇ´01Ò+ÚÖåàå>ÆTÖÙ½ð.+/Zý+ÐDñâÇ-íº*:êâI6¾ä56LìF÷KT
+îÝÅ+*îÛèP¸ýüûD¾±úÊ,SãùøQÊ*+îÝÍIûJè:»;̹õ+ÞSдÜÞBïâÅéìæN»ÓËâÜ»ù³*Q*ZöC3Ð,ûQʾ
+òíÕ½Æè¸õ仹ù+¾V=ýZ*¸ÍZ2µðÛÙDSí²ñOÐ,ûÁÍ×:óÇìØøôéÁ9N/ƹ:*.D÷1-Îû0MN=íéN»ÐËÂH»ø
+³*ADÎöC/иøùá±Ý8*¾ó-*Âûøó¿ÒL×ðôñGÁ6·B÷1-Îû0ÑÓÇøVEãøóµ8Z:ZöC/Ððûé¹ñäËE¾B*âËùô
+ïæ-SÇëÕ¯ù9*ÂB÷1-Îù.ÁâÌOÚÏÇøVE9;J»Þî±4N³ôËýìû2*¾÷JTBÙ-+JâǽB*ûÅBîµM=óß´ÜÒ*ZH3
+ÐÈóåÍ+¾Þ=Ö5+*6ÛF¾8>¾´PèÁëºÇ+S:ÓæÂõ:*ÂPÀÍ;*ÞçH0*Bù°EY¸*æÊ*S+ä-**Eݽ*²ÒÌF¾Sïéµ
+:Ó4?ÈÒRÎIݽ*²ÓÍÖÌFµäº¿Ò4=äËJÁ,ûü+ÎÕ¾À»ìºçÐOݼ-S¯NÇ>¯ìúü+ÎÙÂL¯Ð>CÐYÝ¿ÒدáÅ:MÌ
+úü+ÎÕ¾ÀSÐ6÷ïMݼ-SWàÂ3?¼ý*æWL;5C0*2ݼ-SÝý*²ÚÔ*UíI*ø8V*%%%
+d
+677 77[1 0 0 1 0 0]sl 8 mask 0 231 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/krdc_window.png b/doc/krdc/krdc_window.png
new file mode 100644
index 00000000..423233cf
--- /dev/null
+++ b/doc/krdc/krdc_window.png
Binary files differ
diff --git a/doc/krdc/preferences_profilestab.eps b/doc/krdc/preferences_profilestab.eps
new file mode 100644
index 00000000..b9358718
--- /dev/null
+++ b/doc/krdc/preferences_profilestab.eps
@@ -0,0 +1,284 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 602 516
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 20:09:35 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 304]ST
+B P1
+NB
+W BC
+/mask 7904 string uc
+î½*¾î1îD*J¾Ç¿â1*ÞIÝ.+îYüÉ*ÞíY,úÆF<ìILø>:ýüÝÝÐúQ*¼âZOçÑ*FÆßÈÐQ+DîIîGW,.ÖÓO:*ý
++Xè<ÞݳWàÐ>K*ú÷üAïûIìùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ùÝ.%
+d
+/sl 62608 string uc
+î½¼**ûÙ6*<òÊI×.»ôȱ*9A@ó¼ðÉ+Í7ÙÕ*Þð4Ä»üñÚãùý½ß*.HîÕW0*¸´ôüû÷ñÍ0±ÜØ7âøÈÔ½ôÖW¹
+7*îYöûóíý0³÷ê@M-ò¼ðÉ+Í7ùB*ÊWøûMå÷DÓèÓ°ç<ë<رåõåûó³ãú´<Z·/áHIB3öÝÀO,9öé.*Þ¾
+õQ¼³D*îTÝÌÛùÖòëÚ3ïèKñ·À³ñ*äÛݽëÔCºäY±+CE¹¶<ʼ-É<¹ê6ü9çôݽ@è²*ÞáèÔÊ×îØLSÐÂ:Ûä
+ñ.±*J³XKÖ;:Þìû¯MãDÁ溴@¾½ßõíýôÃVÈæVY**AHÜûúô×1Iï*ãÈ?ñüÞÉä-*̳´âÄï°/È<KÒõ´LR¾
+·Ùõý½²LLÖÑ8J×ûÙ1Ëø2¯¼É.ÚÃI¯ËüC**é÷Ð*ÈGݽýÂ/ÉLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏ÷*¾õ
+²»µí×R¹ÄNýÒ×»±,ãÓ°üíÁR¾îýñ-/RLÉÖÒ8BöûË@»LBÝã+JíáöñåçÕÕýܺöì×¹ôçÍEµüºà>¾·õõõº
+Y6:ëI5³+¾ÝËëFúûößUÅåÉ-ÅÛô+3JÒðù±/D9*öÍÁûÐÛTÒã¿@ìÖ×CÍʾýý9æ:½ýÙ5F5*KÍξ÷ýYRàE
+K*JíÖÞ,9*¹õQéñç5À÷V6îFüúóòèãõÍýü»YYHùñãÇÝ@D¯4öìûùíùݽ½îÎò+*öÜÛÏÁŽ½»õÆÚ¶ðÙóÜ
+B+ÙÝIRÍ3+¾¹Ó6ÓRÇ+í¯Mß༺L,;Éæ=æýö?/.¶äù·ü>ûü¼îÀòý**î÷¾,Ì×JV¾ñ7½ý°ÍH½X´î÷õÅýN
+2ZXõûùåÍ3HÐ×+>ÛIZÊNY2䱯¾¿I*Z*î4üºïðåÕÝíY½¼ÅÝü¹ðãÅAPC¯4¶×ûùE8Õ½ýßJ²+¾I?6Ú÷õõ
+óáåñÓù8¼ÚîÏ1R¾øõÝC=*¶ËϹÈFRYÚ±ÂUWÄQNÞýý1.òñýÂ1/.¶ýì3»üI>Dý°ÕãîP*òÄ,¾ýU@î¶QüI
+Iìääý¯±XËöG¸Ï,Øü½µÈÜCÈÈH<Ç+NÙý´ÇH9.ä±S¾¿ÝÀO+*Þ;úS×á¿9A9½º;Ùý8úõï°CHM++*ÞS42*
+*P4.**µRÞßõIýUüúU½@*öÌÐÇ5ýÜHüùÜÜùîÚÅM¼×±Ï*óí9±P*î=EùFRÍÚ²ý2íÞ5ÍÆ*ú9¸²ò5¹Ø?:Þ
+Å3ؼüKöà5ñãL@+äE4¶ä,¼SÖýý7ßFýIØø?Ðâ¼ATJëýA7ôã¿SçM<C¾ÇùéãRñ+éº@/Bö=**.ú3**8ß
+ÍõFHEµõóëÝõôé¹åGùöÌ3YÏÞ2òO4Â2¾ù+É*Þ74îìûùÙTÝHå-*J8»1½;Õ¯ÕòØLÉÃ,´½ýÉ?±,*õø>Ü·
+B;Å»2BÜ3¿JN.>R<¯91,RßÈû-Dç½ûÊ,/XSÞC5¾Æ1²*07JÚR+/ÙùýýïçP?9Çâ¾³ìI@à-¾÷W2:îå¼±I
+ÝüÂ;æ¼-½Ü;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1ÀB=4*Ì;É>¼úÊ1*Þü/ý»ù/VLëÞÁýáÖR¾øõAC?*¶ÌÊÓHPú:
+áÃÅL:Ðö4,ö¹ûÅ*+,NæVÞ?/F±¿C*5ýIÖÆ;0AT¾Oô±Õöý÷÷ðØÅ>X*7õ½Ùîâ5Õ÷U6ZîÅÎÁAIì1ÎIÀIý
+õÙ²éæÝÁ9íß½ÅìØòíý2ÜK¿5L.*ìϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæJÏñ0XøÙÀÃ,ô¼ýÈ9±,*ÕÖµí
+îÚÑÔ?VZXº<,;ÃÕ:JûüAÙ°O׳+Õ>îÊ/ä*ÒîýÎMÔúøN2ÖíÍ;¯ü9ÜRTøÆ-0ZòüQ3ĺïD½Ã+4ü*ùÖKöü
+4üÚFîÑWIíìû¹õÜû·éÑ¿É=ö0/ÌâðG4ÆñëÅ.XO+ÙF½ÜÓòçQH**Wõ´öêÛÏ1:Zõæ»5½5VÞL+±Ü½SÄC+¾
+Y09¶°Õ¸ýÌ>ò<H3¿Æà±2ÎRÁñÕÝ»ÛؾU4¶ã,±*ÚïèïúõɽõL2BùùüïÝìÃSTøÆ-0Îõü?/¼ºïD½Ã+8Þ9
+³ý8üCú÷°ßWá¼¼GùôÌûøñÖW1YôYP>¶ÎËÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõêßÑ-îïÝMAHºKWý4/êðüÂ=P.*¼Î
+¹÷뻵0Ç8>º³,,2ι2¾êB3DÂç¿/ÚоU@R9¾èB3<.çÇ:RPÂ,?¸ãYPøÆ-0²áúûã/êöà³ü4-F¿I*î6üV
+@îÚ³IîL/**µî×öãÃ,À»ù±>Þ:ÓUµOÚöÄA*ÞÃC/ì¹µðêá±öèÇÕì>ÊÞD+ÊؽøÂ2ñ+*¼2@ûæ:é?Ê23´Ü
+-T´½æë,°ºðD½ã+8ÞÍ**î6üV=îß»IKN=Hº=»SϾWåÜûùUSÞ7ÍIµãÙµÃA*ÞïÃ<ø¹öïçß±øçËĸ²7ß-
+>ZÞùÉWB;*B¾-í;ÊÇ2´ì-úîøüFá/Êöâ³üÈ-FÅII.F¶ïãÏQÕìÕ½I½»·ðÁìãÊ,T϶ïÔ°å¼<ûìÎÙNí¾L
+@üC±WÅ1;Wõ6HJ+NÙIûÅ+¹8*ÖîüÍÁÓ=öîÇéú?1*0YáÍé+8ºöïäËIõæÕ½AÝ»¹Gó°P+ÃæÙµUÔWÝ×¼9¶
+8>H²âη-ûZè?8Þ=>¾ÒùIÍ2ZÝ+*½ëÝýݲ÷.ܯËå,+ÕïQRÇ·0*àåÚÊS¾5äÌëȲç-Í*¾5úåìCXÒJÏÀÚ
+Ì×ùQî34RÃÞùMQçYÑ+¾-FÚ½½Çßâó¹-BÝã+Ìá9ÓÑÓç;*2WóÐP+VÛØ?åÌY·ÝýÙåÌF¶ðêë-ýXË*±âÛ½½
+ä;÷³B*¶¿RÓõíY°A´*í×Þú@4¶6½ÒíÚµÅ*JäóÉD/:²æÔ>UÌâý9>¾õU6½ýüê;E0YÙ4*æXâÕMTÖ±ºÜÔÓ
+ÙÕYTÓ÷ç¼*AY´ãÖï>üý0,îÜÃÃÇõí¯*î˳ä<Ä7ÛºùðLËÍ0.öö¯UÀ/Qä.X9H¸0Å<óÉÕîÓÙÍ*ÞÙèÒÖ6Ö
+°éà3¹øüLV°¼À/ÙðéµÙâ*ÞׯÑôVC¯ß¼+Yû.*üéAZßÄCÉ´7ZÉW²ÜD;2ݼQ@+ùõ9*5ëÃõIÉí½@ÝýV¼ý
+×úü±÷ûÑàùÍãõIÉí½@ÝýV¼ý×úü?;ãVàÞùNûï±·*Z½Në5úûåðÏ*RÈÖî°í½ûð**ÏÍìè4ãõIÉQ.*+ýàW
+üýó+¾X½ìFôY¼ÝýVÜB*-¶ô·ôùÉOÜíÚ¶ñ³û×èÈ*:ÕB¼K×ò×´AAìýýý*ô>*µ*JåñX¼¹ÔêÉ/*æ³ÍÛ*˽
+6WHÌO±Óºõì?ÉE,¾MÆÎù³ñXìèöã,*òØåX:äËYûJ×ÂØÈáÄËýÈV÷8-ÒüéÕÉéÀ¼×**Ôô×5Ä·±U6IÎVøY
+ÞSÕÌíݽ*O*;*14î2+Xº**B*¶T-=,CºK2¾àYËDöYåA5*ÞèH¹ßDYôÉé-ÈOWÔ¹òçóù9***Õ4î2+XºJC
+Þß7Nî/Å<JéÍ8Û¹×ðQ***FS¾E2242*¶à-Õ@î,.̳ÞÓFÚO*Zóè68:à7PFUÃ7.Á¯ÔÊ×Ó1>ɼ˴åÎãíÝ
+½¾ëÝ9Þü+¾S¾:ÖоR¾54¶¾-ñ+Ã*Q°ÎJîì5Þü¾Â5+ÎðK¹Ô»7÷=***Ì>**R3B.¶-+AAîÈ/8?Þâßç3FR
+*Zó:7ÌJßÄÀKDQüÀ°Çßãäê×ùúõ-**¾K*¶I4Z:î¾+XZÞ3861Úü¿¹9¶=*FݹåʱÁ5ü0**¾E-¶;+á*õ¸
+îà1*¾×¿E.¶Ò/APîV74+¾ÓÁ¶=5Q:îL6´:6ÒCðãIR@»ÛêÉE±µÝ¼,SݼÚè2JJ*Ò96¶Þ*1BZHî¾0ÌêÞ
+Ã9ÚS¾MY2ƶ:*êHùäɱÕñû0**¾E-¶;+á*õ¸îP/8BÞ?=Ú+¿Ñ´B-*Ô8<à7;F¿¾5ZÖ¿ZÎäÆ?ýJ×âùZUä
+FGûõ+**Þ:*º9/B2R-¶,+µ¶îè0Ì3Þ*ÞÇAÚ;*PIG±ãÖíï¼-**ÞÏ+Ú;¾ÁG¶Ñ,Ŷî8.ÌÌß/-*VÅ2¯*î8
+=X¾Þ@À¿BIÌú-ãÐ1ºW÷ðñîÕÍZéÕ-Ö¼*:4Z:î8-ìºÞÏ7FÆ¿E/¶Ò-+31,¾ßIÝV÷ó¹Ö9+**2+R0¶K+Ó+
+Ŷîà04-Y>ßÏ.F;¾B¿±³R.*Ûì;àÇ+Ú3ÁË.-S´6÷1ÈOPö¸°ããÕM9***0+*øR¾¿¾-0¶¿+µ¶îä0ìûÞÛ1
+Ú3*ÜÜÚðâÕíÓ¼.**J.JCÞ//Ò0Fà¿5/¶Ï,A<îÌ+ì6ßß*¾X13ï64ÌWß0À+<ýM×òáÑAÍϺ·*ã1*P+.**
+³+Ó*1>î,1X:ß¿9Â+F:¾¹Y2ƶ:*FûöãȯÝ5û4**¾M/**P+8NÞÓ7FK¿B¾-,¶ß*Ñ/å.îZ*JÕÁÞ¶L.µÀ
+ï<1°80Æú3ãÐàÔVC·óáA1*øÚ¶äɱéÉK@Íú°/ñ3/ÚÆ¿EóF3¶Î¼Õúõ@@Kòé׺À3õ°·+ßJÆ5Iü¿°ç7ÜÔ
+º×·ôà·*ï?Y±*JúH**¶F¿*´HF±ãÖ+´ÜV*¾XùêÃQ>ÉÊïóâÈÅQ9¼·*ÞãÁYÔúØßåA*îäW;ÞÐ8*¸ã;-é
+CÅQ>ÉH<´ãÈÃI9ÜáÈJ÷0<ö÷ïÈ=ÝÜ2ø°+*¸ë±AR°áƽÎVðÊØCY8ìÚöã+*QµX×°ê;OY@*JÕ»ôÀV¼ú/ã
+Ð5ëû·ñîãÉåÖÑ*F+*ìXû°äرÐÜV*¾Xùê/Þú1ã<U/¼ø±ïäÍY8*¶û÷ËIõ8Øø°+*¸ë±AÂñâYR@÷êíÖO1
+½ìúð*¾ïáA»D¶ÓÕA5*ÎéH¹ßJUÌFë-ÈO8VÙµéÜ¿õÜF*îö÷¯á0¼@ôã,*òØåXÖçËE¹û¾°3KÝôËÙôû+¾ô
+¼ìù´ÞýÝ·**T÷â<úýXJüÁÖæùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/Aá
+ùÍã/AMÐ×.Òܽ*âê/õÜBçV4ßµô/*JNÔÍÆô4¼ß>*;EÆOçíûÜ**Bäø3çXßAÊ,Þ¶-×>ü÷í5*¾âVI;ë?ú
+ÀS*<Õ:É5-*B·ÁUö/Ó*PXK@Ký»+*¾Êó=ÐÛÀYB/*°Áâ<ÂÊó=ÐÛÀYB/:ð4°3ö1***Á*¾ý¿,FL¾Ñ9ÆÌ-
+¾â÷.*î**ðÙÀ-W¶Å81.îQ0*ÆÚÀV,ú+**Þ:*Þ½Î*Å2îò-Ðñ*2×Hïö<̹޿?Ò>FðÀƾòíïÉÇ+ÂÚÀVÀ÷
++**Þ:*ÊI/:**Ò/Ê,ÚξÂ1*NÔQÚ2Ã,Ã;¾=Ò¶241Ôîä.È1,+ä+ìÓß/ÊNÌ;9T6¾â7âÐòýH***6,¾õÏ*
+-+16î,-Ð@*:×HïL>̲à;QÚ=Ã5UBH¶Ã/µ4î¸,üÝ*êK@ßø***î**,,0-8BÞ+0NW*J°»àÃJÚèÂM0¶>*
+QÔ:úï85ì>ÞO-ú¾,ÖÁVÀñ+**Þ:*F9/B2R-¶,+S5*ÊVͶ;4åJ¯P;,îú4ì÷ÞC+Ú7ÇýQ*Zð4°3È1***Á
+*κ>ÞJÞG0NW*¾°»à;LF+Ãñ̶Z/ã,źîJ,8SâÛÆú½+*>Õ:ÉJº***B.*àM+0,8BÞ/0NW*î°»àÏR>RF
+ð¹жZ/O,Õóïè-@ß;íÇ6Þã?â<:9***0+*÷S*+*JWJ?Þ+/Nº*:±»àÇRF¾/*îL<XêàË+ÚÖÀZ¿UH2.B
+,¶Ï;AJî¾+¼J*¾ÊUÆOR¼Y**G9¼,*¶Á*Õ1îð4<ß¾>*ïß×*òÚÀVÀòù,**øì=*¾U/¶Ê*1Õ:Á/6HN+29Ì
+/*æ?âð?0êÄ,Jç±Í**ìCJLUÆÁ,.**Súßű+Røì5ùJ@+èVI;ë?úÀS*RÕ:ÉZûÜ**Ðäø3çXßAÊ,Ƹ-×>
+úÅí5**èVI;ë?úÀS*Rµ:É,ºè»+*:Ëó=ÐÛÀYB/¾ò0°3ü½ý*Æ×5ÜýV¼ý×ú:°3*ÒI2**è+XBJ@A¶F+1Ï
+îÂ.XëJæÞËTÚ¹¾5BÆ*1ì.ßS9â¾3ùJ¿.Âð¾¯ìàÞãïF<¿ÔÍO%%%
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+Êèýý***öI*Z2:0îÀ+0-ж¿Ñ3¶2/Å´îÎ/XÚÞK,F¿¿ñE¶S.QOZ±-Æ¿R8KQT@ÖHÈò2S
+FùàU5Zëúø³?ÍVÜTÓ5G¸ÀNåØóº@98ßË3F<äU?˾*8Oá/Á8-Ú?è5¶ÀÖÅ2î4/84à?-FâÄD¾áD¶ÓF1É
+î*HÀ,XÕåË@FRÆÁÎöI°÷Ì2ÍÄëC>FëÝ5KƳ@íÆß¿<ÚUÁÁÙ¶L.AÊîN,ìTÞ?·Ú+¿Á¶·Æ2õ8=5JFÒʱ׺
+°93KåAÆãÏáEàºJ*µ²ýRÔìÏíK²ÛÏÓUCºE¶Å=Îæ;Ë1ܶÇÙÓ¶ã*AMî:/8Oá·>ÚØ¿ÁÐÆJ@íÎã/ÉFÔÆí;
+ÉÕøôÈ-8áçK»ÛÃÀȾU:ÆÊðíôêßVHÙÇM̹=+õ>îR78ÎJFÞï±ÚÔ¿Á÷¶á.Å·÷È5XÀßYÇãÑì¸6Q1=î@4T
+,ÐÊã=Q¶DÉ1,î8C8Àô?8FҾɳ¶Å91P;-7FáÈñâ»-<½Q@9äêCµÛ·¿>±÷Ê10ü9ûÞS+N:Éå>ïÄ7XÛÞ»B
+Nß*õ±÷Ô08XQè׺/0ÝM@ÁÅ4Å0*XÆÞ×5F￱3¶¯/AÏîæ-8WÞ3>ÚG¾5B¶R5µFîR;8GàSÀÚM¿ÑX30¶O2
+QKñÊA8Nâ+JÚ·ÂȾ5·º¯7õ:ïNFìNÞ3Pº3×êñ-¶G½Ç°ZDSæßÐú±°·¿ÖFBöü±CýLP5Éï4*JMáð±ÇãIN
+Gí½@ÝýV¼ý×úü±÷ûåðùYÀõIÉí½@ÝýV¼ý×úü±÷ûíÞùÍãõIÉí½@ÝýV¼ý×úüMîûåðùÍãõIÉí½@ÝýV¼ý1
+öü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãE+¶A/í½4ÝýV¼ý×úü±÷ûåðù
+ÍãõIÉí½@ÝýV¼ý×úü±÷ûåðÃ+ÚÉÀõIÃí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷à*ìOßùÍàõIÉí
+½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý׺¯*õÐîûQïùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýVÜB¾
+¹SöüÑöûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@Y6ÞÛ>ºýSúü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷Õâ
+<%%%
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 104 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+Àèýõ9*C¹ß×AÜØ*éäÕYUí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí
+½@ÝýV¼ý×úü×îûåðùÍãõIÉí½@ÝýV¼ýLöü±÷ûåðùÍãõIÉí½@ݽ6ºý×úü±÷ûåðùÍãõIÉíÝ-ÜýV¼ý×úü
+±÷ûåðùÍãõÍ*í½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±SÖ±½A/í½4ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@
+ÝýV¼ý×úü±÷ûåðÃ+ÚÉÀõIÃí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷à*ìOßùÍàõIÉí½@ÝýV¼ý×
+úü±÷ûåðùÍãõIÉí½@ÝýV¼ý׺¯*õÐîûQïùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýVÜB¾¹SöüÑöûå
+ðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@Y6ÞÛ>ºýSúü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðÅÊO
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 208 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+Àèýõ9*C¹ß×AÜØ*éäÕYUí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí
+½@ÝýV¼ý×úü×îûåðùÍãõIÉí½@ÝýV¼ýLöü±÷ûåðùÍãõIÉí½@ݽ6ºý×úü±÷ûåðùÍãõIÉíÝ-ÜýV¼ý×úü
+±÷ûåðùÍãõÍ*í½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±SÖ±½A/í½4ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@
+ÝýV¼ý×úü±÷ûåðÃ+ÚÉÀõIÃí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýV¼ý×úü±÷à*ìOßùÍàõIÉí½@ÝýV¼ý×
+úü±÷ûåðùÍãõIÉí½@ÝýV¼ý׺¯*õÐîûQïùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@ÝýVÜB¾¹SöüÑöûå
+ðùÍãõIÉí½@ÝýV¼ý×úü±÷ûåðùÍãõIÉí½@Y6ÞÛ>ºýSúü±÷ûåðùÍãõIÉí½@ÝýVìº@ݽ>X¶@ݽ/Vá8Üý
+VX¶CÌä<%
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 312 di
+/mask 7600 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝÊàO*%%%
+d
+/sl 60200 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöQ°çÜÕÅY½5+*ßYý»ÙRã
+OÅ.+º=×òìßÑYíýûõå/÷îG-*ìEõíÚ½1åì»ÅRã¯ÅÊ+ú<×òçÁ1ýûÚ»ýøS»ùó¹0*FºðÖUåÌGÙµ¾ÜKÒðÔ
+M4+¼ÇV¸ìÕ³ÝÌWHýü²ýüúG-*üüúïÑKÙÌûDÛX¼»¹RãWÅ>,ú<×ò̯ÙüVøùûûO½üW-*Üý¼öÙCµ¼7ESPë
+º¹RãWÅ>,ú<×ò¿YÁÄê÷¸ùøßüä+*ÕͼöãÆKÝ8V.-í9T+øY¸N;ÞAÇOYéøðßÉÍ͵Yîé½.**Þ*ÞÓ1NP+1
+ßîô88ÀÞÃ0FZ¾ÑU¶ò+8Ýû÷ß;9ÜêDÓÓ¹ùØÒð4H°ÂÆ6FÐ2îÉâÐQ?ûÖíáÞßÅAóçI,**î0+°F¾ÉZ¶U11K
+îL-8BÞç?Fø*YIHúíÄûH@ËÐàÓ»UTC°¶À0CLN08S.öO°çµÀHVAïëêÙÉöî9+**¶À*,*¾T¾×¾-=¶ï+å¸
+î06X,ß+8>-FL¾¯¾=W¶Þ*õÔîê>Fúöë×íPVõZ,»ñë5ÈÙ9ÓÚBÀ°¿Ú:ðT78¹âUÇOýö·ZÚÀ±MÕüê.*¾5.
+B1B0**È*8»ÞG-F2Á-2¶>-µÓîT,XÎß7CÚMÀ=Ï2,îèÏå@6µîå9ÍRìÄÒðìSð¿ÆÂFÐ2îÉâÐAòFöëÞÕÇI
+1çY+JºÛ**Z8îÞ*Ì3Þ¯-ÚCÁÑZ¶à*SÎK?áÖßÛ?F4/ÍüF÷é¿í0ô64HÇã/Sãß@â-;Õ@ðØM4+¼ÇV,¼á³Ô
+ØõíÞY+**¶°*-*O+µ4îà0XóÞ×DÚ,ÁE3¶U-ÕRîÀ0ì¶á;8Ê+ÎíÜU9ã+¼Q°·KT·°+õ¸<-ÂÚùËÑ8S.öO°
+çá·0ê@ZèÛ»I-**î,+è+ÌNÞÃ1FB¾=3Æ06ìÞÞS2ÚÙ¿UÔ¶ô.Ã*ÚE³Þ´+¹8õÂð@O?ÕÆ:=Ââà;Ã*ÝN@+Ø
+îDû¹ØôëÚµI,**î,+ä+8BÞË,Ú׿U7Æ+6ì0à37FMÁñä¶;*Æù´ëÑý0ã+²Q>µST·¶7·JNL8S.öO°3¼/Õ
+SÝЯåÔGù;***Q2J**R/¶L*AHî,2ì²Þ+/FJÁ=Z¶À-AJZüî..Xòß3LFÙÀ=.R,¾Ø±á@ëµR8ú¸L·ùÏÈE
+?¶ï.÷Jâ;ÄÚôÆL¾Y<Éú4ò߶ýÀÄÛ¿*JM-ĹBòUQ>ÕAÒðÎM4+¼ÇV¸QÒ9ñ76DôêÕ*?ðÜ.Î50Õ2Þ±á@GB
+íáÒGGÏÚéTÇÙ,Ìê0*Ö6Å>,ú<×òÄRµ8VBòé×S´êÕ¹0*Àð>³áÀEÕ´òÂðDÀTì¸N;ÞAÇO1/ײâÂGÕÌÛ¹*
+Þ·ñ¼»×°çÇ;GÏÚÌÎÈÑ8S.öO°çÝOOGØ°äÐY5ëØÙJêðÆFý7Ú´æÏUUÃOðèì8T·é;Ã*ÝN@GìOÈSÍü6ø²¸
+WEõÔ+*ýËG·ìØG±°Rôðì9T·è;Ã*ÝN@GùÃÁÈPX븳Së¸é*¾åôëÙõæV5GMW55í8T·ê;YO@;ùØàêRçL*
+Þ²L?òØÕóñWL?AG<.îÍâ<YYü¸ïÛ¶í´:*RÚ¹5ÝX¼»ÍRãKÅ.+ú=×ÒêÕMAÌÙô@,*àWFøUÇé5T·æ;YO@Ý
+;ýºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°N>ɾèù1¾?GLH×ú
+ü?Z»Ñöü±÷ûåðùÍãõIÉí½@ݽTºý×úü±÷ûåðùÍãÍ+@º×RºÌôêÏ=C*îG¹ô¶ÀQòðõIÉ÷Ñ+æ6Z6,ÆZ@Å*
+¾äõIÉ×É+ƹïÃÇäÐê;õR9êùøöóí/*2üøí½õV,È;Ã.WêéS<:Û85W¾+κÐ1ìGºøóûÝýüÚ:9¼/*îÜ·ýä
+û¹¶ôìýôV,âïÂÞ°÷Z´/º¾¾Þ°Æ+>¼Î1H»öîÝÍÙõYIÓµÝü4*Þß=ÅÛ¸ôê×»-òY¹@ËPI¼ÔGîßê4ö,+ËEI
+¼²Hæ-åûØôé×ãñíÍôíͽ/*îõå9I7ØóêÕçóðéIôVø´Â2»»GÙ³êNH½ùÇ+¾ÕµÌø±è×YÁ?Â2ÛBIõ6ÙóåÐ
+ÝííYøñÝ-*öüùßíäÚ÷³êNC³ñ9¹@»>PÊ4áPÛ·²ðHýüê*ÞãõäÊ·³êÃC3PÏ8ò¿1¼7D°åéëéÍì8ж+Õͼ
+öãÆKÝ8V.-ýÖ±çSÏLÓA´åÄE5óòͳ9*2*R1¶Á*³*8ýúîÈ7¹ÄÛµÎß¿HöØÔA9Üúúºù,Ýü±*B»¶1,ÅCîæ
+1F¼ùñÁMHº×³TUõôÅGÉÌ5AXZSQ06×:Aý6@Í:ÞÇ+Ú:*IY¼öá¾=Ñ0EæîJ9ºTçÅûH08¼ºSÍü?***µ2:Ç
++Fð+YIHúíÄûH@ËÐàÓû¯åÐE±ä6ææÁõ<PÙ×Õ½-**Þ7-F:¾E+îì×íPºõîæJ-78ò¿ë.»ÖìÎ-èHHúM***
+8O**B5**à+8³Þ74F+*3Ý»Î,èG6øäÙI¹@»Ú¶ïL¼@FõZÐG»úE***ìB**¶°+14ÞÕQÕ»¶XßÍM0DFæ-ýö
+·ZÚÀ±MÕüV>*¾±22-**ä*8³Þ;-Ú:*Õ8»´ß¶-½üö³åÐíÙµíKå¿í0PÖǵ½-**Þ7-2**XVÞ¿,öñâ¯1¼E
+ÖñÁ¿01ø*48GÖéâÍQ½/**ÞO0Ê1ÚZ¾5G¶Ò+±*òF÷éÁ4ÐîëÍGÉôîßÁúÄ1Sóíåí***¶á*1.îP*2öíÓ1+
++ÝJÁ-»¶J1âùµîÕ***FȾ*¾-9¶ß*APîR-ä*âµí1R+ìüê×ò<OÈ.åCZÙ¾/óìµµöR/Í.¾õìËDZÙ¾GÁ5²
+¶Î1:ì+G*íå***FÁ¾B¾U0¶Ò-µÆ:.î*?JÚÕGÉäÑQÍäÀ1ËYç×¹I,**î.+ìJÞ++Bë×Où7..ä,78ò+J5¸
+νQéÜËùôU***X:ÞFJD0¶J*Æù´ëÑý0T*²Y¹@»éÔW¹»8úÔY4ûØôU***,*è+8²Þ35¶êÕMý»EöñX¿01ø
+*¼?ÈDê׳å¼0*Ñ1A8*°Í*Â.Úæ¾E0R4R-JرáXX/ÝÜôVøÜ»ùÈ<ËÖXÞ,Dôú*JM-ÔÉÖ²S¿01ø*Y¿H6õí
+6ó:Ç6ææ¾ï80ûèéÌOÍF·°NÓ?ý7úÈ1+¾E*¶T¾HEôçÂ+UX7SÎß¿HBQÒ9ñ7¸*×Y7K8ºÆà-º´êÐ-ñ»ÊØÑ
+ÕGÉÜD³èÉBÅ´ºB»´û¸°*îÙ³ÙÌ6·òäÉ2³èÓ9ò¿ELÛ·ZßÎYå@ׯåÔ13ÒJâÐдV¶ñèÄÜôVøðâÇQùÈøñâ
+Â÷òêÇ+¾ùüGù±æÓßPMâÇA½Î164°çÆ3¹XGEGN<-ÌEõéÉ?ÉÌ6çéÌO9íÜûÜÉ>ÑHËHÁäGC*öíêÇñÔëCÑÜ
+âÜûö1øÞÝOOGØ°äÐY5ëØA+ÉüE¶ëÎMÕÔ4ÉâÓû¯åÐÁõíݽÇÁÈP@ÓWå0*êÙøöÏ×@<Câ*ÞIæ-¾ù*OçòéØ
+±¹çÂÚÈ6ìãû¯åÐZµIÌR/½éMÃÈ>Ç+¾TÏLO͵À-6¾û=»*55OKóúXD¿4ÖêÜ»ù8X³åØ@@ü±åÐZÅIÐB±+*
+úÅø¾è4±=Ü·=,Þ²L?òØÕóñY¹@/¹ùSá<Ö½Ã1º»÷éÃõ໹=*ζõ@¼ÛúøÝGÉÆGúøö÷Q×ÈÄIüøîíï+XÅX
+Fµë¹+*àWFø±Öúб׹NðûåðùÍãõIÉí½@ÝýV¼ýÒöü±÷ûåðùÍãõIÉí½@ݽOºý×úü±×ß±3ZH**Ö÷û-Þô
+NïJ=ÃYÝ;ÉTÒRÐL¿üúü+Z½ÒZG·ìÊ÷L@?PÊE¿0/øû-Þ,U¿»ìºçÐOݼM@¯NÇ>¯ìúü+ZÃÕÎBSÈ6çÕíÆO
+ëÖïMÆ;Q¼ý*ÖÞTZÈ>M¼úæÕíÆOÉB¯LÃíI*Ôö4°;á**ïéµâðù9¾ë4-Öøü+Zßã*%
+d
+602 100[1 0 0 1 0 0]sl 8 mask 0 416 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/preferences_profilestab.png b/doc/krdc/preferences_profilestab.png
new file mode 100644
index 00000000..5b735f77
--- /dev/null
+++ b/doc/krdc/preferences_profilestab.png
Binary files differ
diff --git a/doc/krdc/preferences_rdpdefaultstab.eps b/doc/krdc/preferences_rdpdefaultstab.eps
new file mode 100644
index 00000000..ba5ce6ec
--- /dev/null
+++ b/doc/krdc/preferences_rdpdefaultstab.eps
@@ -0,0 +1,314 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 602 516
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 20:10:23 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 304]ST
+B P1
+NB
+W BC
+/mask 7904 string uc
+î½*¾î1îD*J¾Ç¿â1*ÞIÝ.+îYüÉ*ÞíY,úÆF<ìILø>:ýüÝÝÐúQ*¼âZOçÑ*FÆßÈÐQ+DîIîGW,.ÖÓO:*ý
++Xè<ÞݳWàÐ>K*ú÷üAïûIìùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ùÝ.%
+d
+/sl 62608 string uc
+î½¼**ûÙ6*<òÊI×.»ôȱ*9A@ó¼ðÉ+Í7ÙÕ*Þð4Ä»üñÚãùý½ß*.HîÕW0*¸´ôüû÷ñÍ0±ÜØ7âøÈÔ½ôÖW¹
+7*îYöûóíý0³÷ê@M-ò¼ðÉ+Í7ùB*ÊWøûMå÷DÓèÓ°ç<ë<رåõåûó³ãú´<Z·/áHIB3öÝÀO,9öé.*Þ¾
+õQ¼³D*îTÝÌÛùÖòëÚ3ïèKñ·À³ñ*äÛݽëÔCºäY±+CE¹¶<ʼ-É<¹ê6ü9çôݽ@è²*ÞáèÔÊ×îØLSÐÂ:Ûä
+ñ.±*J³XKÖ;:Þìû¯MãDÁ溴@¾½ßõíýôÃVÈæVY**AHÜûúô×1Iï*ãÈ?ñüÞÉä-*̳´âÄï°/È<KÒõ´LR¾
+·Ùõý½²LLÖÑ8J×ûÙ1Ëø2¯¼É.ÚÃI¯ËüC**é÷Ð*ÈGݽýÂ/ÉLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏ÷*¾õ
+²»µí×R¹ÄNýÒ×»±,ãÓ°üíÁR¾îýñ-/RLÉÖÒ8BöûË@»LBÝã+JíáöñåçÕÕýܺöì×¹ôçÍEµüºà>¾·õõõº
+Y6:ëI5³+¾ÝËëFúûößUÅåÉ-ÅÛô+3JÒðù±/D9*öÍÁûÐÛTÒã¿@ìÖ×CÍʾýý9æ:½ýÙ5F5*KÍξ÷ýYRàE
+K*JíÖÞ,9*¹õQéñç5À÷V6îFüúóòèãõÍýü»YYHùñãÇÝ@D¯4öìûùíùݽ½îÎò+*öÜÛÏÁŽ½»õÆÚ¶ðÙóÜ
+B+ÙÝIRÍ3+¾¹Ó6ÓRÇ+í¯Mß༺L,;Éæ=æýö?/.¶äù·ü>ûü¼îÀòý**î÷¾,Ì×JV¾ñ7½ý°ÍH½X´î÷õÅýN
+2ZXõûùåÍ3HÐ×+>ÛIZÊNY2䱯¾¿I*Z*î4üºïðåÕÝíY½¼ÅÝü¹ðãÅAPC¯4¶×ûùE8Õ½ýßJ²+¾I?6Ú÷õõ
+óáåñÓù8¼ÚîÏ1R¾øõÝC=*¶ËϹÈFRYÚ±ÂUWÄQNÞýý1.òñýÂ1/.¶ýì3»üI>Dý°ÕãîP*òÄ,¾ýU@î¶QüI
+Iìääý¯±XËöG¸Ï,Øü½µÈÜCÈÈH<Ç+NÙý´ÇH9.ä±S¾¿ÝÀO+*Þ;úS×á¿9A9½º;Ùý8úõï°CHM++*ÞS42*
+*P4.**µRÞßõIýUüúU½@*öÌÐÇ5ýÜHüùÜÜùîÚÅM¼×±Ï*óí9±P*î=EùFRÍÚ²ý2íÞ5ÍÆ*ú9¸²ò5¹Ø?:Þ
+Å3ؼüKöà5ñãL@+äE4¶ä,¼SÖýý7ßFýIØø?Ðâ¼ATJëýA7ôã¿SçM<C¾ÇùéãRñ+éº@/Bö=**.ú3**8ß
+ÍõFHEµõóëÝõôé¹åGùöÌ3YÏÞ2òO4Â2¾ù+É*Þ74îìûùÙTÝHå-*J8»1½;Õ¯ÕòØLÉÃ,´½ýÉ?±,*õø>Ü·
+B;Å»2BÜ3¿JN.>R<¯91,RßÈû-Dç½ûÊ,/XSÞC5¾Æ1²*07JÚR+/ÙùýýïçP?9Çâ¾³ìI@à-¾÷W2:îå¼±I
+ÝüÂ;æ¼-½Ü;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1ÀB=4*Ì;É>¼úÊ1*Þü/ý»ù/VLëÞÁýáÖR¾øõAC?*¶ÌÊÓHPú:
+áÃÅL:Ðö4,ö¹ûÅ*+,NæVÞ?/F±¿C*5ýIÖÆ;0AT¾Oô±Õöý÷÷ðØÅ>X*7õ½Ùîâ5Õ÷U6ZîÅÎÁAIì1ÎIÀIý
+õÙ²éæÝÁ9íß½ÅìØòíý2ÜK¿5L.*ìϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæJÏñ0XøÙÀÃ,ô¼ýÈ9±,*ÕÖµí
+îÚÑÔ?VZXº<,;ÃÕ:JûüAÙ°O׳+Õ>îÊ/ä*ÒîýÎMÔúøN2ÖíÍ;¯ü9ÜRTøÆ-0ZòüQ3ĺïD½Ã+4ü*ùÖKöü
+4üÚFîÑWIíìû¹õÜû·éÑ¿É=ö0/ÌâðG4ÆñëÅ.XO+ÙF½ÜÓòçQH**Wõ´öêÛÏ1:Zõæ»5½5VÞL+±Ü½SÄC+¾
+Y09¶°Õ¸ýÌ>ò<H3¿Æà±2ÎRÁñÕÝ»ÛؾU4¶ã,±*ÚïèïúõɽõL2BùùüïÝìÃSTøÆ-0Îõü?/¼ºïD½Ã+8Þ9
+³ý8üCú÷°ßWá¼¼GùôÌûøñÖW1YôYP>¶ÎËÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõêßÑ-îïÝMAHºKWý4/êðüÂ=P.*¼Î
+¹÷뻵0Ç8>º³,,2ι2¾êB3DÂç¿/ÚоU@R9¾èB3<.çÇ:RPÂ,?¸ãYPøÆ-0²áúûã/êöà³ü4-F¿I*î6üV
+@îÚ³IîL/**µî×öãÃ,À»ù±>Þ:ÓUµOÚöÄA*ÞÃC/ì¹µðêá±öèÇÕì>ÊÞD+ÊؽøÂ2ñ+*¼2@ûæ:é?Ê23´Ü
+-T´½æë,°ºðD½ã+8ÞÍ**î6üV=îß»IKN=Hº=»SϾWåÜûùUSÞ7ÍIµãÙµÃA*ÞïÃ<ø¹öïçß±øçËĸ²7ß-
+>ZÞùÉWB;*B¾-í;ÊÇ2´ì-úîøüFá/Êöâ³üÈ-FÅII.F¶ïãÏQÕìÕ½I½»·ðÁìãÊ,T϶ïÔ°å¼<ûìÎÙNí¾L
+@üC±WÅ1;Wõ6HJ+NÙIûÅ+¹8*ÖîüÍÁÓ=öîÇéú?1*0YáÍé+8ºöïäËIõæÕ½AÝ»¹Gó°P+ÃæÙµUÔWÝ×¼9¶
+8>H²âη-ûZè?8Þ=>¾ÒùIÍ2ZÝ+*½ëÝýݲ÷.ܯËå,+ÕïQRÇ·0*àåÚÊS¾5äÌëȲç-Í*¾5úåìCXÒJÏÀÚ
+Ì×ùQî34RÃÞùMQçYÑ+¾-FÚ½½Çßâó¹-BÝã+Ìá9ÓÑÓç;*2WóÐP+VÛØ?åÌY·ÝýÙåÌF¶ðêë-ýXË*±âÛ½½
+ä;÷³B*¶¿RÓõíY°A´*í×Þú@4¶6½ÒíÚµÅ*JäóÉD/:²æÔ>UÌâý9>¾õU6½ýüê;E0YÙ4*æXâÕMTÖ±ºÜÔÓ
+ÙÕYTÓ÷ç¼*AY´ãÖï>üý0,îÜÃÃÇõí¯*î˳ä<Ä7ÛºùðLËÍ0.öö¯UÀ/Qä.X9H¸0Å<óÉÕîÓÙÍ*ÞÙèÒÖ6Ö
+°éà3¹øüLV°¼À/ÙðéµÙâ*ÞׯÑôVC¯ß¼+Yû.*üéAZßÄCÉ´7ZÉW²ÜD;2ݼQ@+ùõ9*5ëÃõIÉí½@ÝýV¼ý
+×úü±÷ûÑàùÍãõIÉí½@ÝýV¼ý×úü?Ïç±3-í=óÅÍû+¾û;³YîÌãе=ÉÈ<@ÚêùYÝI*JòÑÙØLׯÙòéâ<,üÃ
+ÙúýE.*ÛüÚ·êÝúôUAW¸@7áUÕVóM*B¾±Í±ÝàVèûäÃEYéí-ÈOâöÈõï×õ2*Îé<³ï0,ºÔëãÃùýýI¾»/Þ3
+*Þ÷ÚÉíÌó+ã<Ü4Wøôï¹.*æ³Çض-+äT÷ìÛU@µ/*µB+õéäÛÚÓ1>ÉæTñåÓÏí;*¾ÓOèà7.JWã¯áä½WÔº´
++Rýóéã³»K×NËQIÝM**UÉÒÃE2îݾÑXGݽý*À*0*¾ýM1¶.+AAî,.̳Þ+JÚؾ-+¾öUÑ0üé1ÈOÙÊ×òð/
+**ÞÓ,FL¾59RA¹T-=,QSîZ-¾ÓÉŶ-0Ó*ÕPïôAÌ´M<èÎã¸ÅHÛ¼ý+**î,+ÌFÞ;.ÚÚ¾U@¶Ò*Qîîì*Zûç
+ϹAù¿°ç>Uüʺ+**¶Ó*Å2îà-È+8ìJ,Y¶Ö+Þèï@**µ>ïê>ÌüàWUÊFNZ6¼Ë´åÎãíݽ¾ëÝ9Þü*¾J¾¸¾E
+2¶Î+ÅR*ôÅßHM¾-=B,¶¯,ß+¿31,*ãÁôVùØ9>É@Cðäß5**¾E/2ßT42/¶K+1¶ZHÎ2îN04.ì»ßã**UÅ>
+ÎÎÎ8îL,ì³ßO¼FL¾MW¶¯,µÅ¿ßãäê×ùúõ-**¾5.R1¶-+1Dî:/8BÞÏ.F,¿E¼R.Î÷Û¹äFEÚQ>ÉÞ¹+**¶
+Ð*-+16î,-ÌîÞ¶Þó9Úù¾Á+*Ô8SÞÃ<Â-FøÀÁG¶Ë3C²áG+ö¸×Aµäì¼û.мûÖQ,*Jû¶¾B¾æ¾M:¶ß+ARî
+Æ+ÌÀÞ75.PÒ*RùêEYÌÛì5ÈO¸ºCõ,¾°B+*JJÞZÞ+-FÞ¾=F¶ß+żîP+8»ßK,*VÅ>îº88ôÞ³PÚ-¿6Ä¿õ
+ßÔÊ·¸øí-**¾U-R3¶*+12î2/0*XJßÏ.Ú÷À3¿E+ÎòѱÔ6ùÛ9>É0ׯãÖ5**¾=/B2R-¶,+µ¶îè0Ì3Þ*Þ
+ÇAF>*æóè,*¾-¶Î1AÊï41Ìîá+PFöÂE3¶L*åÅß-ÜÔº÷·öéQÖóé+V9*2.R1¶.+12î>/XNÞG-ÚCÀÞÂ4
+*.ýú°ãÕýÎVÐàÇUA***Q4Z:î8-ìºÞÏ7FÆ¿E/¶Ó-õ.*¸ÛØ**î¾18òßGENî7ÅÊïìCì8-=ºG×ððéÏ1**
+*A2îâ+8JÞÃ+ÚM¿-N¶?+µJîÂ986¾WYX·ðY>É,·ò,**îN+0,8BÞ/0Úß¿EE¶õ-õHîZ*JÕ±6¶-6K+Q8ï
+ìAìðà3UFëĹ,B×òáÑAÍϺ·*ã-*,+*¾+¾55¶=*µRZ<îà0P,è5T+4,è*J÷ÞÂ4*ʼú°ãÖÝÎVÀáµ±C<õ
+ÈýX»îÏ5ÜúÝUY¶R*SâðËMÚ·ÃE5¶Á5?+1X÷4B99-=ÂW×°ðèÅí9*ò·ïÊAåÔõ3ã<²ÜM*¾XÉÒÃE2î0YUÜ
+êF¹ïF¾öÙ*RX7,¶F-SÃÞ3PRñßCUÄ7î=ÈOöº*îÂß*¾è¾7;*ò¸,+ùéÃ-/Þ6YQÔººøîù-*WHú°ãÕ7üJ×
+²Æ7Q0»Å**Û@S5µ:ÞMàÔV×öñß¹VU>ÍZÀHIX·ðé5ÝÎVÐáÉOí;*JÕOèà7.îÊØCY8ìÚöû+¾PµX×°ê;ýÎ
+V4äÒõ2*Îé<³ï0,ÚæìÍE1YܺĻ/Þ/*æôèA±ÔGñ5ÈOÀÊ·óG,*òØNWÚ¿*Ô4úòåàËIÝI*æøñE½ìGò-ÈO
+Æ@°3Xí;*JÕOèà7.î×ݯÉ8üÚ÷û+¾àÅYø²îTIÎVLÕÆ;UÄ»Å**Û@S5µ:¾-éôëصîÛõ1*D¼¼êÖ¿Íû¾°3
+üõO*JæÕ»ñÔ»Ã*¾ÛÍùÜ7¶±8ç,U+RO>Éí/â½25»ý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Ú
+î°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°ºý×Úî°êJ@+¾*¾.¿;Ê:á>úêOÖ¾VÔ?P2ÄíÝ?5+ðÍãMW>**Ê/**ï*
+Å6î,/8BÞ:JëÞÛ:ºõ-ÖÆVHæàðàU0¶¿,µ6î4-XRÞ¹A¾/ã°40¶ÄÉÕ6îÝ1JÐ×.ÒX*J°×F*¶Á.õ±÷â<Üô
+-ÚùàN±3üI*¾â°÷æ0ÐÆã5ÈöØ7Z1׺²+ÓÄõO÷F/8åð1A¾ëâÐZ1:*îà+P+8Üïß7**ÕPîâ*ÜýVäBÉíÝ,
+ÜýV¼ý×úü±÷ûåðùÍãõ9*í½@ÝýV¼ý×úü±CÏ8Öõóïéý?*¾îÕýÜ5BÔW±çÛÁQÝÜýùíÍ5ðßI-*¶íÚ½ñüGF
+¹õËίÛDÉH»öîÝÍÙõYIÓµÝü±**1ÍFôè×±åô7Îõ+ÅóèV¸ìÕ³ÝÌWHýü²ýüúÍ+*úû÷áQÁ´Gù³¸ÛúøH*×
+HËOý6ù³æÐßííYøñíø**åU*ì+¾õÝ9Ü´ä̯áPÉWIí-:éÑ×òÂYÅÌú÷øùùEI*Þ÷4¾É*îòÁYÌFøóßÃKöH
+*×HËOÕ5¹ñàÊÑͽ9ðõûV,ï?*õ*ÞñÝIIºðà˯=Ûº¹õ+ÆOIÉ%%%
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+¾èC0VÚ,ºVèÈ-QXXH»·9½é-*êX+¶1*üH»÷ß91ÔÊDÃËU¹9¾V0@¶Q¾èÓ/Ö´¼ûØèI×ò
+PMAìéÖµõí7»÷*âI*¾=1¶Î*Ï+1Kîð0*¾è¿Á0¶Ï-ų:ÅNVý/¾ò4¾¹*îòݹíÛ¶íàÏÃçÙëEÉKZ,*¾ÙÖ¶
+:,ÅY:52FàÀƾFðÞ³ÀHVAïëêÙáöî+**½*îÀ+Ì:Þß0Úá¾Î¿ç¿Ù0¶.1õÁÎO.*@Õ*F:Í5+îïÙ±ÝËÖìßÎ
+¿ä0ÌãE.ÖÔVä;*8ðñÞ¶/åÄXG´í8*¾ô-*ã*ÏVA@:8î¾*8BÞÃ/FÆÀUA¶Ó+1ðîÂ381KÉ2*BX+¶2*XGøê
+Àï0ü6°=å°¯ZöXSJWÞ2J¿Þ¿460Ú+ÁE?¶¿,QÂîJ;È-8»àÓFÊ+öÁÌ-Ý76¹÷ñ>»¸*;*A4î*,T,XBÞ/2Â
+8FZÀ-9B;¶K-AëÎ:ïÖ0ÌêK¹>**X+¶2*üº·ê¿í0ôú:Úëå׺ÒÁ1V÷Z/XRÞÃPÜÚÀU6¶ê,ÅDZ.**ÌVTéÞ
+Ã=Q8J.ÞÃ,F¾¾U0¶A+Á+1HîB48RÞ3,ÚÔ¿-¹æ8/*¸U*XVä*JF*31ìøâÐ*ºÙá±÷À9ÍÔÞÇVHD¾E¹ÆP@9
+ãß6*ÞE×ÒÓ»ý08F¾;¾L**ÜÎÓ<î..ÌOÞZÞ¿1Ú¸¿U02¼ÆÂ08êK¹>*ÎW+¶¯C1Þá¾ýôÛøâÐÞ¹Ùϱ·*î.?
+ÑÈã¹UÆ´@ûEå¾ë,PÛ¹µðëE¾;¾L¾S¾3¾¯¾M:¶=+1¹îÎ18@ßW-F×ÁôR*îØ,îà°0*à+ºõìÒ+Ù/ê÷ÙÌÌã
+±<**XàîçVHÚÀV±3ÌÃVøÝѳíä»E¾ë°ùô5-¶â**Þ/-Úɾæ¿MU¶ö/ß*ã-16îÒ0ìêK¹>*JW+RV·,*Æù´
+ëÑýÄVÀ±ËFÉQúõÊ/9üî;»HÝÀU6ÆS@+ÖÆOõUDôêر-ÆW°å¼±**Þ>JBÞ²Þ+2Úƾ59¶ï.ÅÎîR-8NÞ»7Ú
+×ÁôR*:Ø,î¾°8*¾äÔGE´ç¼M@+Ö0Ìã+¾=3¶Þ*Á*³*ãÂAHî,38æÞÛ?Ú;Àø¾-Ûº¾+Å<ïJ7À*ºÔåÀíø7û
+Dôê+**êÕ6ôB0î±/Þ/ã6*6.öéÏýÜ?ú·ÔÙëEÉ3PÞ36ìÃ+Ò>çÃó,0G½ËóâV,Í?*Ų÷*,²²Þ¸5É@óIâð
+²¹è»¶O+XSóâ¼-ÁÄGù¼³CµBê·@YVÄåÄWøîÝÇUUø9°·=2îÃP¹@VBòé×S´êÕ¹H*¾å4¾¹*2×ÒÒ9=ôÊDÏ
+Y:ɹø,9ãöòâÂGÕÌÛ÷+*¾Ô*F.*åËùóãÉQIÌòGãнÉȸ²ãÊOÝ@سµûV,Ï?*1¯ç½-Õ7¸òèT¯ÇÚØù*âø
+²@GìOÈSÍü6ø²¸WEõÌ1*2Ø,î:*úµôâ·éôÊÇK×ɹ9¾VÍäбݶRçâÓWÕ@ÓWý0*î×»ùôGDéLåRéààE+â
+ø²@;ùØàêR÷?*¾çÂTæ³Yéå¹+âø²@÷øðÕ5íÃùôÍ**ùµïéãÕõ,ÆóçVTôéÏ5åWùÕ**àWFøÜ¿ã0Êãý¸9
+í½@ÝýV¼ý×úü±÷ûåðùAÀ1;ɾèØ+Î3ÞÒ-ÊñXº*ÓÌÍOJºç**å¹K@E¾·HÀVXòAú´ìß¿ñÄ7E´UGD²æû:É
+5ûÃ6ÊñËãÐÉH7ù¶ïßÁý´×KÁüÚɱC-ñº±×òÍEÅ0ÌÚ¶ïà7õäÊGõðVäÞFûEÉ*ÞÏ+6-ÚǾ56¶AÉÀBïÀÕÁ
+0ÖÎ+ÆäÃ-+J¾B*ÀÚ¶ñËãð¿âFøÊÙ3¶OGGÞFWGÉÅL:¿VÔ¸G³¾7/¾RüEZS,.2Ô:½?í²@ÍäÞçVìܷظ@É
++*¾Z**îL+´GÉàöì3A7:*ùÞ¹7í¯@å³ÌNÞ+ö62FR¿Á׺¯EU´ÌüËã-ßD/¶ß*ÃWÅ6îÈÚ2¹àÂÍFéìâO4á
+WïÀ+,òÃÙö4±÷è¶XúäÏ0F×ØÀËÛEQ5åð¿*Ê.FâÕè¾¹ð¸´WE=A¶ù,,.íÙ¹Éã-*22B6¶L*A7îâè0´8îä
+ØËñËã<;´Eû+*:Û´3YW@Í>Þ;ZP>Éá;Q7ZÏäçÞ׺ðU7;É°¶ë=ð<³1í¯@íºJ9â°0äçî׺æø¹äº¶ùÂÚÞ
+ë±ÊÝÐíF´ñ5ä·ð,QÔÔ+ÊÑÙ±·Þó¾ÛXÔéÆ5¯Qʶ7éK-Væ*æ¯Û+8**óÓùö4±CVñâ¸XBåÃïFæÌ=×Ú»ñòî
+VäÅ.OXÙ>*Ð6¶ø4AÉHBZãEÉãºÞ÷<+¸¾·Øµ@ÁïÍ2JMâÐF̼ä/ÑQÅ6÷ð7äð?ñ6,>.ê»@=ZÌôËã>ÍZÕ×
+>ÉÑÅ·Ã+:çÌEíZ@Y@±-*¸,ä÷ä°çAYÜúC²æÎQEêØUÑÜëV´+JåÞFýJ@ß÷W-JAâ°-BåEÆOö½í*æøä÷0°
+÷Ï3äùÍãõIÉí½@ÝýV¼ý×úüÃîûåð9ÇOùÜH¼ûÝ,*2üøíÕHZGFÉìGºøóûÝýüÚ:9üC*¾ùäûEõíß׹ݷ¿³
+îVøòßÁý¼ìܽûÒÜûøÍ+*FºðÖUåÌGÙµ¾¼ï-éÞ×òÚYé¼GÙºüûçüû÷I-*öùñÅÍ0ë¸ôéò¸÷ó5YÖôËOý6ù
+³æÐßííYøñíø**åU*ì+¾õÝ9Ü´ä̯áPÉWIÝöÀ³íV¸àÕMQüºûûûË9*îº/ÞO*¶øßAQ8ûøîà:úà5Ô»±çó
+àìÚBñòñíÁú»IÉJº/ÞG*öúõå1ÍÚ¶±êNAíì5YÖôËO9ÉØðÞÇÉÉQYìáíø**ÙU*ì+¾ÝQY¼öá¿?ÑP¯ñèÜö
+À³íV¸ÑÏ5õ³êÙùõÂܺ*/*JüO*.2ÚC¿UFR4B.¶µ0µùî¸=øN*¾ò4¾¹*îòݹíÛ¶íàÏÃçÙGGÉ*Þ7,FE¾Ù
+:¶´+A¶:Í;Ú=.Õ3øZÛµ±íìàÁ1*ü·*FU¿U5R42.¶ð*µÕ:-/âP.U8,ÖÚ,Îìð.*ºúôêµ=Ô¹B²Î×Û±÷ö
+2A@É8ðñÞ¶/åÄXÛ³í8Jù-*J*Ø-R42/21¶J*1NîN0È,è-ìÎß74ÂÍGÊÀ-?¶N2»,*Þ¶/ÞK*¶óæ±5ÄEö±
+ÆÛGÉ10BÚô¾-8ÆJ³Þ/4Ú׿ǿT¾5·¶Æ0õDîL8è*úKå¿í0ÄÛú·ÉHG¾ò1***..Ú:¾M3¶ô+Ã.Q2ZPî.88
+7ÞÓ>Ú;¿MJæ:,*¾U*8,¾IY8¹îÙ¿GI.µù¸@YÞðÏ°Höá±7¶Æ,Å°÷ð1ÌÐÞÇW,¾=ÉæD¶ïâÇŵ0+Â-*ð¯+
+å6î,-ìÆÞ3/FS¿5<¶á-ÅöîÊ/ÌßK½6*æW+¶¯>1òáÆ51ìøâÐ*ºÙÓ±÷,4ÑKàÁź?,S+ñ/+*º=ÉÚØõíÞ¿
+-*ü**ä+à*8CJOÞ/1FSÀ°¾±¯¶à--*QÐîê,X2àËC²0-*Ø?*Aòð¾T6öíÜUÍV4ºµÙ¸@É*TBåB9,ð7/F×
+¿E;¶4-ÅçîT@ûEå¾ë,PÛ¹µñëE*í1*Z8:0îÆ+8ÞÞ;3FKÀEE¶³,Å?îè,ÌÓJ±B*¶W+¶¯>-*ï*ܹµè¾ë,
+ôúWµåðCAÄ/N¾Âõ±÷R.ðÇO¶@°çýÌÔ»Eõë*´I*¾·¾B¾;¾50¶Ó*QtÎ6îÀ.ÌËÞ;HFùÂMƶÑ,Ëß**³/J/
+ã3*JìÙ³ÍüE°3̳õVÜPà-;Æ*B=Éã5¼¶<**VâÐø?7¹´ë×9îÈâÌG.*°Õ+**á*QJîP/à*Ä*8VßÏ-FÏ¿-
+8¶@/A:ï¾:8ÓJYF*2W+¶ÁCK*DÙôêÖOù7×.Þø¹@9²î7/>?H¶¾M0¶ÎÅñ,Å°÷,0ì>àÇ@FöÂ=Rîê±»@úÔ
+íæÕ+ôù***<Z¿>T:EAâF4éê,ö×,îLX0*0,úóæ½í4üFéëÌÌãÙ3¶à¿÷DJL²ÎÐNAÔ5BòéöDæèå×.HU*8
+çð+.ææ¾ó@@WTQå°K¸2Þ;2Ný:CÏ¿UTG×íÞίå¼Ë¾QÖ³UØê?ÖVÕ.HYèÃåÄWøîÝÇUUèç×Ê÷2ñÅOåÜæ²
+åÀ/Á¼ë°åÄû¸å-*öV+¶1*;ÉÈø¯àÌS=è÷×ÚI6öRÄQQPê·óêË9**²/ÞK*¶ìÚYYܺøïNíS@ûÙÒÒMUäVø
+³ÏW¹éå<2W+¶2ÉüE¶ëÎMÕÔ4ÉâÓ»ï/éÜ×ò¶ASP»ø±æÑç³ëÚ¹H*Jæ4¾Á*îÝ×=åWÙ±P0ãXì5YÖôËOå¼ï
+ÎÐÇUÙXWTÙü7*Þ±ùôë¹²ÔÃÌÏÔÃÃíºßص@;ùØàêR÷?*¾çÂTæ³YéåÕÜZëEɺ»÷éÃõà»ùQ*¾àÛÙöóðéÕ
+ÜZëEÉÈû¸òà·éü?*¾¶5ÅÜËZ×é°÷óÀáùÍãõIÉí½@ÝýV¼ý×úüÃîûåðùÍãõIÉíY@ݽ*âê/5ÆOöýû-JèY
+¶óÃÇäðùÕ¾³êßùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AMÐ×>îáI-**ýû±¯ÜVƲ@3ôüü9+¾îÅû
+û±¯ÜVƲ@Wïûü5I+*ñí½ïðí½5ÑÛÀN±3éðõIUÕ5*ZüûÕOûJ/*¾+¾ºÀïX1°ÏÀοÂ:ïâ3ðUÀº¯+ÀÖ*D
+N÷DW¹.Q±3%%%
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 104 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+¶>½ýà¾Ý**ñíýRä1T**ÚK¾>0î81°ÇÀοº8;×Jâ=-SDâì°Zëéæ@è*ÒXW¾³ñéÕÓN°3K
+ðõýËý=*:ûKHÉãè=+*ÞÇK+*¯+ó*á*µVÎ8îÎ0ÌâßÇ2Ú鿱ÈBD¶ã31¼î2/Ìàß7-F×ÉØÃUO·ó?1²îL3Ì
+WLHW·S7ÅÊðÔK8Úä¯ÒFÏÇH¼¾N±3,HÉOßIîçVÜMÕ±;¶À,1.îÊ08Xëç8Úѹ̶<+µ?îTF8¿J0K¶?+ÅÈ
+îÊ?ÌLäëÔFÔÆV6ð@TøØ@=ËOTµÝýM×>ùéO=ÉQ±ó84ìÑéëBÚÕÂÙ>ÊÂDìëëW9Nµ7·³ãû:Úê¾6ÚÏÕWC.:
+çVвõûIÈOÏIWè×.êÅé±Êã+¾áȶÎ,żî:/ÌOàÃIFKÂÑöW»QÉîZ.ÌÏß?7â;ÉÅRðê·Ìïà7˲°-NDÉÒ
+É»ýÅ°Sâõȹ@Í5êÃ*Úì¿M°¶Ê51ËïÐ38ÏÞ·9F;.êðÚ?8´O<׺T.åÛÏÕ³CçV<SóûIÈO¿Xè×VÜÇöM=¶
+üVÕóü27XõÞ²ÞGEâ¿2SBÞÓ»N<ÉM-AÈî²IðçȲîïôDÐÎÇPX¾N±ç8¿õ9îËV4ìÅÌâð*ÞÇ.ÚÆÀE/U;¶-µ
+õ¾ôJ/ìW7*¶Ø-Ó+µêîÂ=ìOÞC4Ú°Ã=0¶°A1´ïN2XJá7<F4ƱÉÆRU8TáS¿ÜÙ¿±ì¶>.Q8ÏÅ9:çV¸IãÑU
+I½GøóÊG¹óâ¹ñôñL@µØ*ù/ÖäXÜóÍMÐ×ò´°ñäûE÷ñã··òê×MÅìPä×6°½;÷3ÙKì¯Æ×50BÜàVȼYÌ+*Ú
+È·@µ+½EÓãêÝùݾKÐײêS=Ûö2*.Sè9êáD³°°ËOTÌDñM*:ÒɽýWÊô4°Ëãõ½+ÙÎÁõIÉ5XÄõIÉ5XÄõIÉ
+5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõ
+IÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5X
+ÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ
+5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõ
+IÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5X
+ÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ
+5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄõIÉ5XÄÑ×.
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 208 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßã¶DÉ
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 312 di
+/mask 7600 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝÊàO*%%%
+d
+/sl 60200 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãÆR@+Óõ9*U¸Âº°÷ûUÞøPïûåðùÍãõIÉí½
+@ÝýV¼ýÒöü±÷ûåðùÍãõIÉI-Vö°ÏöFë×MQ°*Þ¹ôêï.Íæãí½@ñQ-ÎCÞB.:ßV8+*Ëí½@±A-:õà5=ËO×M
+ìÏHÖõóïéÝ5*:úóÝýì×.>M43ØÖÕÑNJ¸G@Ø*-J÷N9Ú¹öòéù½ýû·KHú4*Þ»ñüËùõîêÛýë×.Æá3¿ãðßê
+4ö*+¿ã:-RúJ9ºøîß½IµíݼTí¼û?*¾ÁQ8¹óê×±ù0æÝôVD˼úV¹ÞÁ×?î/,Dµ¼úæºÎ1Ìù³ëÕ±ÉåÝIëÝ
+Iý4*ÞíÍI¼D²é×YÑéãÕ½ê×òë2;øø¸´éÖǺüô=-*YíFóåÒ±Ý0U2;¸¯¼ìC´éÍO½ÝÝÝòå½1*îûõÁÝË·ñ
+éÖÇ°èäIôVøRÊB?Ä˸ñæâ»üû×+¾ÉíËCñèÖ5±<ÊLGæ-9úD²âÌÕÙÕIÛGNï,XIûîÉ;Á¼GÖ20ü¯åÐÑLÃT
+YêÌ7µ@èçIéH*:*Î8î0+è*Fü÷ß?Eô6¹íJÁ-»î³WYHº÷÷÷ô/¼ûå*Zøî8.8±ÞÏ9¶úôå1źö°éÒÔìë9¹
+@GAXÚÞÐÌ6B°KXüCVHK¾=-¶K*¼ÜúîÅ+QP7´Îß¿HöÒÐ9ù»6FúöÐHûU***ì:J<-¶â-ܼºöÝ7ù»VDOÃU
+ùáÌOµäÊCÎÏ1íOÊ´±Yý0**¾E0¶J*µ,ÞÛ±ÝËöìßÏ¿0DFæ-Ù3øZÛK1Ò»ºöÅ***FÈ**Z@**Â-Fè¾E>¶,
+*<¼ùJ/Ò¹Bò˵½ôVø¶ïàÃúV¶ìßN¹øöµ***Ú¯**îâ,8>¾YÍXùîÚÀIÅ6²¶Î1üïñÞ¶/åÄXû×R**å::0*
+*Ê+Fè¾M0¶K*XGøêÀï0üúïéÌOݵíÜÁÌ-Ý7ÊZ=íü0**¾E0:**ÚÖ¾-/îåÇá8ú´Zå1-78ò+>F¸ZÕÇIÍü
+4**¾É6B9¶ß*A¸îR-ä*æ·ðÕ1?NßÙI¹@ëßÁ1÷79ÐèÝÍÝ+**îÄ+82ÞË*:îÝU9,,¼¿01øî¾8ÆõíÞY+**
+¶>++*1HîÀ+XÊÞÏ0Ê+ÆíÜ9Î,Úû×±çOÈ>3̱޴+5èÛíìîÏ4H3*íÛE³Þ´+¹0AæîJ9JÚ-¸*ÜÍ+**¶0+¯
+*Õ6îR1ì:K2Þ+T¾¶Y¹@ËQÍHË/9DÝбõ¼.**Þ3,Ú¿¾-,ZرÉôE22Ê/DFæ-¾@òJýÌÔ»EõëÕ***ÚJ¾·¾
+²6î¾*:õëØQý7Ò*æÜôVøÔWÙôøFöWÝ>ø³ëÕ***.*Ò-Fæ¾=@îÖYÅüù´îåÛ,78ò+úT>³Ö±éÌû6*P9XF*
+âH+23¶Ï+µ6Î>Î0¾²åÄÛÚ4¼»ë×ò»ùô?OD¯Û¾/²ê÷+¾Ä0VA¯çÐ,78ò+Ü,»BìÝCèK<CÎÏ+áG6øÓÕGÉH
+·ðâÆTUüEö?9,*µ*îÒ*»´êÑ3-ÔÚDÐJÁ-»ZÌRIäEò*°ÝDÀFö:Ã1öêÖO1äùB³QY¹@»³èÒA¯8ëöZøêøó
+â*Þµé´GCðæËA;èÒUIæ-µÂ¸ñÞÀKÝÌW°áÌW9<R¿ÆOOëÖîäÓ7»ë×òãÇ=Íô?óåÇ3ñç×=-*õû¹ôåÎUÁËÄ
+Æ=YüJ9B>âÐ;=ôÚ¸´¸ÆN0FµìÕAU@GCÐÕGÉHÜ»ù»ASP»D»0˹°*îÝ×=åWÙ±P»Ç»ùï9ò¿½Éȸ²ãÊOÝ@
+سY,@ûµîØKÅXW?@ÇUùáÌO1íݽý<1?ËVTÙÌ7*ÖµóïM±WN°Æ+¾½Î1*õ+ÈÐçÕ³åôÐ3·?CÚÉùáÌOßì¼F
+Ï4üÔÅ4?S<-*ÓLÃÈHí.1B*ùQø*@@ÈÀè÷Û²,?Z×»ùôGÚè̳WVúåÌOß8½N¯ä,*ö9ó+Ó?äPºñP.¾çÂTæ
+³YéåÝôV4ôôÑÄOZý49öøðÕ5íÃùôP*JïìWú¸÷ó½¹@;¹öóïñÍ°?7½úóßÝá-Ú8Û¶ìØõ,*ÂÙ¶òåZ÷Oå°õ
+ÆâùÍãõIÉí½@ÝýV¼ý×úüSïûåðùÍãõIÉí½@ÝýV¼ýÈöü±÷ûå°Áå<Þº**Zñù1¾ëÇà¿P4ݼM@ÓRÏNÃ,û÷
+û-ÞüR߸ðÚCñÃVTÊBµ,74òù1¾/Ô,ùÚ÷ÐOɼûÄVàÆ<SàÚ÷û-Þ4YK¯Ð>CÐYÝ;ÉدáÅ:MÌúü+Z¿ÓÞ>SÄ
+úöÏYÝ;É@¯àÂ4ݽ*Vï?âLÄ+*àÕíÆãõI*Ù?0Zóû-ÞÀÉ+%%
+d
+602 100[1 0 0 1 0 0]sl 8 mask 0 416 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/preferences_rdpdefaultstab.png b/doc/krdc/preferences_rdpdefaultstab.png
new file mode 100644
index 00000000..9a8d939d
--- /dev/null
+++ b/doc/krdc/preferences_rdpdefaultstab.png
Binary files differ
diff --git a/doc/krdc/preferences_vncdefaultstab.eps b/doc/krdc/preferences_vncdefaultstab.eps
new file mode 100644
index 00000000..1289a805
--- /dev/null
+++ b/doc/krdc/preferences_vncdefaultstab.eps
@@ -0,0 +1,307 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 602 516
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 20:09:54 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 304]ST
+B P1
+NB
+W BC
+/mask 7904 string uc
+î½*¾î1îD*J¾Ç¿â1*ÞIÝ.+îYüÉ*ÞíY,úÆF<ìILø>:ýüÝÝÐúQ*¼âZOçÑ*FÆßÈÐQ+DîIîGW,.ÖÓO:*ý
++Xè<ÞݳWàÐ>K*ú÷üAïûIìùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ùÝ.%
+d
+/sl 62608 string uc
+î½¼**ûÙ6*<òÊI×.»ôȱ*9A@ó¼ðÉ+Í7ÙÕ*Þð4Ä»üñÚãùý½ß*.HîÕW0*¸´ôüû÷ñÍ0±ÜØ7âøÈÔ½ôÖW¹
+7*îYöûóíý0³÷ê@M-ò¼ðÉ+Í7ùB*ÊWøûMå÷DÓèÓ°ç<ë<رåõåûó³ãú´<Z·/áHIB3öÝÀO,9öé.*Þ¾
+õQ¼³D*îTÝÌÛùÖòëÚ3ïèKñ·À³ñ*äÛݽëÔCºäY±+CE¹¶<ʼ-É<¹ê6ü9çôݽ@è²*ÞáèÔÊ×îØLSÐÂ:Ûä
+ñ.±*J³XKÖ;:Þìû¯MãDÁ溴@¾½ßõíýôÃVÈæVY**AHÜûúô×1Iï*ãÈ?ñüÞÉä-*̳´âÄï°/È<KÒõ´LR¾
+·Ùõý½²LLÖÑ8J×ûÙ1Ëø2¯¼É.ÚÃI¯ËüC**é÷Ð*ÈGݽýÂ/ÉLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏ÷*¾õ
+²»µí×R¹ÄNýÒ×»±,ãÓ°üíÁR¾îýñ-/RLÉÖÒ8BöûË@»LBÝã+JíáöñåçÕÕýܺöì×¹ôçÍEµüºà>¾·õõõº
+Y6:ëI5³+¾ÝËëFúûößUÅåÉ-ÅÛô+3JÒðù±/D9*öÍÁûÐÛTÒã¿@ìÖ×CÍʾýý9æ:½ýÙ5F5*KÍξ÷ýYRàE
+K*JíÖÞ,9*¹õQéñç5À÷V6îFüúóòèãõÍýü»YYHùñãÇÝ@D¯4öìûùíùݽ½îÎò+*öÜÛÏÁŽ½»õÆÚ¶ðÙóÜ
+B+ÙÝIRÍ3+¾¹Ó6ÓRÇ+í¯Mß༺L,;Éæ=æýö?/.¶äù·ü>ûü¼îÀòý**î÷¾,Ì×JV¾ñ7½ý°ÍH½X´î÷õÅýN
+2ZXõûùåÍ3HÐ×+>ÛIZÊNY2䱯¾¿I*Z*î4üºïðåÕÝíY½¼ÅÝü¹ðãÅAPC¯4¶×ûùE8Õ½ýßJ²+¾I?6Ú÷õõ
+óáåñÓù8¼ÚîÏ1R¾øõÝC=*¶ËϹÈFRYÚ±ÂUWÄQNÞýý1.òñýÂ1/.¶ýì3»üI>Dý°ÕãîP*òÄ,¾ýU@î¶QüI
+Iìääý¯±XËöG¸Ï,Øü½µÈÜCÈÈH<Ç+NÙý´ÇH9.ä±S¾¿ÝÀO+*Þ;úS×á¿9A9½º;Ùý8úõï°CHM++*ÞS42*
+*P4.**µRÞßõIýUüúU½@*öÌÐÇ5ýÜHüùÜÜùîÚÅM¼×±Ï*óí9±P*î=EùFRÍÚ²ý2íÞ5ÍÆ*ú9¸²ò5¹Ø?:Þ
+Å3ؼüKöà5ñãL@+äE4¶ä,¼SÖýý7ßFýIØø?Ðâ¼ATJëýA7ôã¿SçM<C¾ÇùéãRñ+éº@/Bö=**.ú3**8ß
+ÍõFHEµõóëÝõôé¹åGùöÌ3YÏÞ2òO4Â2¾ù+É*Þ74îìûùÙTÝHå-*J8»1½;Õ¯ÕòØLÉÃ,´½ýÉ?±,*õø>Ü·
+B;Å»2BÜ3¿JN.>R<¯91,RßÈû-Dç½ûÊ,/XSÞC5¾Æ1²*07JÚR+/ÙùýýïçP?9Çâ¾³ìI@à-¾÷W2:îå¼±I
+ÝüÂ;æ¼-½Ü;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1ÀB=4*Ì;É>¼úÊ1*Þü/ý»ù/VLëÞÁýáÖR¾øõAC?*¶ÌÊÓHPú:
+áÃÅL:Ðö4,ö¹ûÅ*+,NæVÞ?/F±¿C*5ýIÖÆ;0AT¾Oô±Õöý÷÷ðØÅ>X*7õ½Ùîâ5Õ÷U6ZîÅÎÁAIì1ÎIÀIý
+õÙ²éæÝÁ9íß½ÅìØòíý2ÜK¿5L.*ìϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæJÏñ0XøÙÀÃ,ô¼ýÈ9±,*ÕÖµí
+îÚÑÔ?VZXº<,;ÃÕ:JûüAÙ°O׳+Õ>îÊ/ä*ÒîýÎMÔúøN2ÖíÍ;¯ü9ÜRTøÆ-0ZòüQ3ĺïD½Ã+4ü*ùÖKöü
+4üÚFîÑWIíìû¹õÜû·éÑ¿É=ö0/ÌâðG4ÆñëÅ.XO+ÙF½ÜÓòçQH**Wõ´öêÛÏ1:Zõæ»5½5VÞL+±Ü½SÄC+¾
+Y09¶°Õ¸ýÌ>ò<H3¿Æà±2ÎRÁñÕÝ»ÛؾU4¶ã,±*ÚïèïúõɽõL2BùùüïÝìÃSTøÆ-0Îõü?/¼ºïD½Ã+8Þ9
+³ý8üCú÷°ßWá¼¼GùôÌûøñÖW1YôYP>¶ÎËÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõêßÑ-îïÝMAHºKWý4/êðüÂ=P.*¼Î
+¹÷뻵0Ç8>º³,,2ι2¾êB3DÂç¿/ÚоU@R9¾èB3<.çÇ:RPÂ,?¸ãYPøÆ-0²áúûã/êöà³ü4-F¿I*î6üV
+@îÚ³IîL/**µî×öãÃ,À»ù±>Þ:ÓUµOÚöÄA*ÞÃC/ì¹µðêá±öèÇÕì>ÊÞD+ÊؽøÂ2ñ+*¼2@ûæ:é?Ê23´Ü
+-T´½æë,°ºðD½ã+8ÞÍ**î6üV=îß»IKN=Hº=»SϾWåÜûùUSÞ7ÍIµãÙµÃA*ÞïÃ<ø¹öïçß±øçËĸ²7ß-
+>ZÞùÉWB;*B¾-í;ÊÇ2´ì-úîøüFá/Êöâ³üÈ-FÅII.F¶ïãÏQÕìÕ½I½»·ðÁìãÊ,T϶ïÔ°å¼<ûìÎÙNí¾L
+@üC±WÅ1;Wõ6HJ+NÙIûÅ+¹8*ÖîüÍÁÓ=öîÇéú?1*0YáÍé+8ºöïäËIõæÕ½AÝ»¹Gó°P+ÃæÙµUÔWÝ×¼9¶
+8>H²âη-ûZè?8Þ=>¾ÒùIÍ2ZÝ+*½ëÝýݲ÷.ܯËå,+ÕïQRÇ·0*àåÚÊS¾5äÌëȲç-Í*¾5úåìCXÒJÏÀÚ
+Ì×ùQî34RÃÞùMQçYÑ+¾-FÚ½½Çßâó¹-BÝã+Ìá9ÓÑÓç;*2WóÐP+VÛØ?åÌY·ÝýÙåÌF¶ðêë-ýXË*±âÛ½½
+ä;÷³B*¶¿RÓõíY°A´*í×Þú@4¶6½ÒíÚµÅ*JäóÉD/:²æÔ>UÌâý9>¾õU6½ýüê;E0YÙ4*æXâÕMTÖ±ºÜÔÓ
+ÙÕYTÓ÷ç¼*AY´ãÖï>üý0,îÜÃÃÇõí¯*î˳ä<Ä7ÛºùðLËÍ0.öö¯UÀ/Qä.X9H¸0Å<óÉÕîÓÙÍ*ÞÙèÒÖ6Ö
+°éà3¹øüLV°¼À/ÙðéµÙâ*ÞׯÑôVC¯ß¼+Yû.*üéAZßÄCÉ´7ZÉW²ÜD;2ݼQ@+ùõ9*5ëÃõIÉí½@ÝýV¼ý
+×úü±÷ûÑàùÍãõIÉí½@ÝýV¼ý×úü?ÏK°3-í=óÅÍ?,¾û;³YîWYäÝÊOO35XôûÕY;*:øçWWÏÚ²äºÉ×¾½ï
+@ýý<+*ÕIµ8ùÕG±äéÇOáöè?U¹.*-¶ô·ôÙÊOÚíÚ¶ñ³A×A±*ð4áA>ÉÆï?íá±í;*ºÔëãÃùýýI¾Ë/Þ7*Þ
+÷ÚÉíì³Ç¼3,*˽6WHÌOº?Øòëáõ2*äT÷ìÛU@õ/*ÅF+õéäÛºÕOøO2*¯ìúè1ÈO²Ó÷ñèæõ2*RU·êÖ÷YÊ?
+í¯*è½û¸·UÉòÉ:*áÚ×?ÄYR@Sñçåõ2*ü5¶èÓñùõ9J3J.ÞC../.*Ò0Fξ=5¶+,åDîT8ÌîK7*àYËD¶UÉ
+òÉ:*éº×YXIR@ë°äÐÉA***Õ4î2+XºJãíS7Ê2FO¿Á7Îã¸ÅHÛ¼ý+**îN+*¾È¾K¾E2¶·+ÕVîR+P8ÌÌ*õ
+íÝüòæÛWG-ÇLÈEV·Ô@µ-ñò±<Þ6áVC±UIR@Ë°ãÍÇA***Õ4î2+XºJCÞß7Nî/1EÞÕçô6Øúûù1Öûùõ-¶±
+*A<Z6îÄ*8ZJã**BE¶Á*ÅØîà.À-Ä=82*È1ë×´U1M<2î0UÌÓãçÞÚÃÉ=¶¶¶*зðI>É@Cðäß5**¾E/2Á
+U42/¶K+1¶ZHÎ2îN04.ì»ßã*Z¶×C¹ôHýû+**îä*XNÞBÞ7+FÞ¾5?¶Þ*ÕÊî0.XØ¿äõìCñêÔì²á;ÐFÁ¾
+5?·çEÅ2îÞ.È´8ÊÞÛÞÖäÆAÙø°3ºA***Q4Z:î¾+XZÞ3861Úü¿¹9¶B*ÜXôãË×ÙYI¿ÓYÝH¾M-¶M*Å,î
+*-XÒÞ;8Ú-¿E@BÌR,Îô×µÜFÙÓEë¶Á>å<ñXZ8¹O0Í·M*ÒC°ã½R@çïãÚ5*LêÅ*,,0-8BÞ+0Fß¿58¶ò-
+µ4îæ58<*íÁWCñòòÝA***;*ã*A>ÎDîâ*8ZÞÒÞ*ÞÃ<FȾ=,2=¶Ô0ȼ¸äȯá¸Ñ7åÆð¾NX¾Þ;âÚóȹ:¶
+ÛBÂ÷ïâYR@7°áȯA***Q4Z:Î0î.,ìîÞÓ7F=¾+¾=Y¶R*Hî³ãÉÍÁݼ,V9***L*È+XRJ³ÞÇ+FÞ¾M?¶;+
+õ6îä2È=ä*>úñÉ?ÝìóPEXïâÏâF῱X·:@µ<îR¼8@0S´6÷9ÈOTF·ó,**îN+0,ì¯ÞË1FØ¿-C¶Ñ*Õ¸îÌ
+*BßéÇ?AÝ8û+***Å4**Ê,FƾUE¶À,¯*1.îÀ+P5Ì3ÞË*æóã?Q¼ÛéK½Ú+ǹC·IDÅ:ñX°ì?0NüÄ°S:ÅX
+***Ì>ÞJÞ+-ÚÞ¾EF¶±-õ¼îì-80Þâ<8ÍܺÄð-**Þç,**+*Ó+1>îL0è+è*L,à+X>JKJ7*>½Þ3K*Dûöã
+ȯõ¸Ñ7ã9Q6ðNTÌðÞOÕÚƾÑÍ·M*:÷=ÈO¶ú´@á×8Bê¿/Úà¿=I¹:-Ÿôè5XäÐàÔVC·óáÕ2*öÚ¶äɱÝÌ
+ã+¾±³·¿@Q@ñÂYT,8Úð?ZHV6ÀÚöïY>ÉÎ÷8+Þ7ÜԺ׷ôà·*ïó¸±*Jú´**¶F¿*³HF±ãÖ¹WÐYÀ*HÐVÀå
+-*µôÕDZM.Þ6YQÔººøîU.*WHú°ãÕ¹ãÏ9é3ËY>É0×ïâÎõ2*H<´ãÈÃI9ÜáàJ÷Ä=ò÷ïÈ=Ýì³Ç¼3,JÇ7M
+üÁ°Ç8Y@»Å*ÞC³±ÜFÚ·ïU.*ËìÚ°ãÖ¹WÐYÀ*âðáÝR@/±è¹.*AØùñäßÉAͯ±+¶,*ØÛøãʳõØNÝ.+*äÝ
+R@3°äÓõ2*V?öçÍÃE½¼Ã*ÎóåµüÚ¹ÕOøO2¾¶Ìú/ã<N5×.Õõ2*ºôµêÐÁIµ¼Ã*ÎöïÉH7ºÕOøO2¾¿ÔF·Ø
+1>Éæ?°âȯí;*¶ZëÜWù0Üù4+JáåÅ7EöXÉòÉ:*EÛ·±Õ9R@ßý»,*À¼ëEµëÙ.*éûÚõë57ʵÐ5¾TL×úXJ
+üÁÖæùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/AáùÍã/Aá
+0°3*.*N26ß.CPßåXÀ/â°W¯ÀZúüW×2:ýVHèL+*ÞV**21¶â*ARî¾+0+è0ì/ß¹I¾Ãâð¿@@AÜ°¾5>¶á*
+ÕBîÀ,ܹ+VR@ÑZ¾±Úºã*ý»*<ËOJó,*<Éã+¾EN¶IÉQ@ï³IÞÛAPDÉîý+*N@ÉÅ´:ÃVÜÎÂÕë*¹ÆãM5R³º
+YÈåSî¶@½¶+êP@/¹*+*A8Z4îî79ì**¶³,Q0îûå°Ä×úü?îûåðùÍãõIÉí½@ÝýV¼ý*öü±÷ûåðùÍãõIÉÕ
+OÞÜÕÅYýÙ**ÀÝüùÓ5V-åй1ͼ»ýõÝIAâÁý0*îÜ·ýäû¹¶ôìµ¼ZûEÉH»öîÝÍÙõYIÓµÝüµ**1ÍFôè×±å
+ô7ÎYFßøµ@GõéØYå@ͽ½Ø½½üí**üüúïÑKÙÌûDÛX¼ûà7Ô¹±çIá¼7زöùùÉüúùH*Þ÷4¾¹*îûõÁÕË×±ê
+Öç°éåAìZÛEÉÌöóæÇIY½½½õ-*FY+¶3*̼öãÇMÝHÚB.ݶÁóìV¸ÛÖEAäFûúù:½ñå<2Y+¶1*Hýû÷ß=Aì
+6EçãÙùà7´Q×.
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+¾èC0êÔ,ºVèÈ-QXXH»·9½í-*êX+¶1*üH»÷ß91ÔÊDÃËUùà7@ë?Ú=LJóè,VEݼW³ÈV¸
+ÑÏ5õ³êÙùõÄܺ*ð1*L+8JÞ/0F4**:*îê-ì2ßGCFS¿Ñ¶Ô*A÷îH?<8¾ù,Ç4*¾ò4¾¹*îòݹíÛ¶íàÏÃç
+ÙWGÉÑ·3¸MBßû>Nã1X=ò߸3íäÜÛÃ19*ú+¾-/¶¿*åBÎ@îX1X3ßã5.1Ú3+*îL8Ìý߯QF¼Åú>îF6ðL.Ö
+Ú,î*QX.¾ÅíÌû´à¸1Á0DÙ¸@É/=Òã>È:ÓH¶ÆÍ-á?FE¸óÔ»¹*ë1*L+0,.**1<:8î**XFÞ39>;ÚƾSÀð
+À=N¶Ð.A@Î>î:2T.X2á+7FæÂ-0¶¯+1Sî.MÌÊâSBF=Ç=CÇR*¾ï4¾Á*îèÏå@6µîå:¹¸@9ÓZÆS»ÞC-ÚJ
+¿-0B.RE¶A.ÕXîÔ7X6¾IòFöëÞÖÉIµçÑ-Ƽ*:4îÀ*Ì6J×ÞÃ8ÚÖ¿Á0¶Ñ*åÔîÀ,ìV**¶Õ,AôîTDììàÏG
+F¯¾¹WÆKKX¼LH+*¾U*8,¾IY8¹îÙ¿GI.µ¹º@ÍêZûî7-ÚJ¿E0¶Ã+A>îZ2ìØß2**F×ÂÔ¿5Q̺Âñ2JJJ3
+Þ?-Ê*F°¾Á>¶P*åÔîÀ,ìVÞ;<F¿U·¶ò-Å7îôNìÀßïØÚàÄù3*¸U*X*à+ûBðà¿ÙH×>*íôúVPß*å¶÷È+
+8ÛJ,EB-*öP@·³íÝ¿-1*ú*J>Þ++Ú°¾5A¶¶-*úVêÓ3ÚÆ¿UU¶Z+1Êîà.ÌÒÞ¯RÚçÂ=E¶X9å6î>68Õã?´
+ÚÍ*JÙ,îÎSæµíÓQ@/ÜÙýJ@1*°Êã=:¶â0Q,ÞÝ·0ê@ZèÛ¹EíÜ+öH*2/¶J*Õ6îà/ä,ììÞ×AÚÎÀÙ´¶F.Å
+Æî¸C̵Þ÷¹F>Àá³·Ã7õ=*ÚÔ*Ú*Â+¾¶*íÛE³Þ´+¹üô;âðG<â<ÉK+å°÷ÎSÞ×ÆOý¹ØôëÚµ-Ö»*:4îÀ*8
+BÞ/5FÖ¾=:¶³-µ.:6îÊ4XVÞCTF¶Á=+¶â2QÚî8?̳M¼0¶¹,µF</-*Ò?*1XðL*:õëØQý7×.常@9>î»
+SHQ¿Ò°÷Ì2Ì.**5×ò¼OáÌW¹´*×1*L+0,.**µ<ÎVÎNî*/XêÞºJFÞ¿<./FÉÀ5W¶;2µRï:>ì2M¿J³ß3-
+FÖ¾Ñ>¶Ó=Õßîæ.Ì7JD,*LU*ìêäî+èÌ7Ù²Ý;É*VUå°²îºZGÞ>ÞßEHÚ+*îè.Ì6Þ¿5ÚÖ¾*ÀÉÉB,î×H5¼
+éµòéÖ9-**¶»,÷ÆÞ;BâM1·Ýà0Ü:Õ±¾Í?*AR°*Z:ÞYÅüùÔîå×ì´óVìæ,Xâð·*RSÐ5é/6ø÷ÈOG¾ÕÜ:7
+GVC@=ù5î×,îÆ@9J¾Ä0Vµ¯çÐHÉV0B/,FξùJîÈTQôEB²ê×ÍØê3²W*ðôêÅC¼N°×ºK@HE´çÁýPXWç±Ç
+ãUÐÊJ@»ÏçÌ/50û¸´È7ùóÔ1*î×,î8*L@?óáÂGÑPòßâ°HFîÏ6ÍÌÊÖñè×¹I**æ4¾Á*îÚ·Ýܺ÷òáǼÑV
+øµSSÅÔÊ×òéLÙôÜÍO:Ø,î:@ûµîØKÅXW?@ÇUùà7Ô¹±çïXÐÊøòåÎQÑéØ·µ»*¾Î?*1+Þ½±QÌÙ´åÊ6ÈÛÚ
+AìZÛEÉÌûàKO=Õ´ÛØÒ´ûG*¾åôëÙõæV5GMW55Ý·ÁóìVLô³Ã×ÏðW**Ñ3ÓÎéÜÔÍYÛ߸µ@÷øðÕ5íÃùôÑ*
+*ùµïéãÕYÛ߸µ@?ùóæÃñÔûW**ï@8»Aß°Õãðé/ÅõIÉí½@ÝýV¼ý×úü±÷û5ßùÍãäÉOKÜùC**ðíI;µBÉ
+>D÷ñÅ*JÆÅüü3·ÁײÁÃIIú.*ê÷ðÁɽýÃÚÞVL×áÍÅTó,*ê÷ð°SIR+JÚ*î²EX¾Þë5Nõ+¿0CÚKè̶ö/õ
+D:QOÚÅÀVZ¯5ì·ñ25+ÃäÉO6Èú÷VÆY*¾GYH>ñ58,ý5*̸ç+2ÚZ¾ÊÓ?æîü7*ï¹Cǯ<ìÌâ·7â·2ÇßLð4
+*ý9µSÝçð°°3G<½Üê÷=*:âGÜÇã51Jê3=X1R0:ÂýÝZ¾=:¶Z.1RîZ4T*ä+XÞÞÖJ¿ÞG@FÏı¸¶M.Qøî8
+EX?âÃMÚοÁC·?2åÃðâµìÏã?OÚÞÌU0·´<Åöð:»8÷à¿CFëÐËV³éã<*ºAÉ*îçV4àê1Ö2¯ôýÉ+X²Jºú+
+2>;FÚ¿U7BÐ4T¶À0-,1×îÀ9P/Xçß/-FéÁE궴<A¾*üAÓÚÊÇ5F¶ïBµÃ,2IÇãÁ÷¶M31Dî2;XÌâ;ÜFËÉ
+M/¶/IQ¼:8Îë±Çæ7½üR@㶸Q°×*î*Ù*ïÞÀMÃU0¶Ó´Q>îæ0P5ÌÚßSDFF¿U¼R±¶¯-A,ïX4X²ß¿BFÒÂE
+Ķå,QNõâ¼84ãËDÚÔÂEW¶Ã2žîÀ+ÐöÁ=G¶ÝOÕ2ÑÙ±ÇÎ3½üR@ö²Q°3üA*Â-Ê/FÆ䱿TöºðüAµîÞ98
+²JÒá?DÚUÀUS2Á¶23Õ,ï,IìãðSñGZÆ-D¶é7QÄ</¼FÕÂ5W¶³@µNZ8ÓÔïö9XÚÞóÂÚ*ÊÄÍOà?ú÷M×>Ü·
+ÛÇãÁ¸Lêýý½×´-*Õ2øè0ìú+:¼À,µæ:ÃV4ôýMáXKàSØÛßÄÙC¶=?A.ïÆ/LIìVàSÝÚQÄáW¶Á2ANîP>äD
+8ýßÇ5FËÆE¾·±?ûGÉ>4öñÅ°S²ÅÈK@YB*:¼²O;+ÕÂîZ4XúKÞê/âå>ÉÕ<ô8.XOß/´ÒBF,ñ8ºB6Á¿-À
+3*ðÞß»6FO¿5¶>âå¼ñÊLÈ+Ì?Þ÷@³úVDMÛÍÕ¹=ÉÀØÏ=×V*¶ÑVñ+¾û¾»BÞ/>Ó.F×À±ú¸Ñ+¯+M.QæZú
+î>5̲é+PÚBÃE0¶Ñ»QLî°í+ßÇ4ÚÊÇUà¶âAQÂð<;ÍÒâ7OÚéÀ=+SEVò·±IÅ>ðNNÌ>Jçã÷DzøV¸IÖµñ
+ü»Ù´ëÊEµìÕUÑôñL@/ùý/*1èÎ0:BÎ,î6.8Wß34Úé¾5>¹À0á1Q×ZÆî¾:8Kà;-6;Ú4¿=B¹+4Q¾îN/Xä
+â?¯FðÉ=K¶<-AçðÄ<ì³ß;3F2ËMR¶°EÕâðTLÌ´çÇÅôöVøDÐSÑ´û¸´ê*¹ê<çÔÛä¿VįA+V.QX*½PÌÈ=
+ð9¶LI2ÞS¿â·A1øÎéZ·E×>ûÚF-*ZóJ@åÖÉ*´áZ·»T̽ëEÖÊê´ðçÁÌÌOáÌDçÃÅ*JÆÐÜ?ÌÚJÄÚHÓçÞó
+´TðÓÉ´Áãóâ<?QË÷;*2èãÙW½Ü×ÓêP@Ýý.ºý×úü±÷ûåðùÍãõIÉíI5ÝýV¼ý×úü±÷ûåðùÍãE´öü+:±AV
+J@ßýõ9*S½ßÓAPDÉíÝ,Ô¯1í½@AÚ6í½@AÚ6í½@AÚ6í½@AÚ6í½@AÚ6í½@AÚ6Çäо9ý6**úõÍÃ÷°KÐ×N
+Öùù½.*ÀIôõÍÃ÷°KÐײÃõùYú.*ʽýÃɽýWÊô4<ËOVÉÝýVÛX*¾÷õÝ@ó-?**0*ð3ĹGÆÃ4À0<ÀÂ=QÆY
+3<ðÃ/2à,æ:ãé²ë;NDɶ>½ýà¾Ý**ñíýRäµì>ÑFëV@ÕβSDâø°×ý-:çVLÆúû½ñý.*N½Æå°×øN**¶°R
+*JVJGÞBÞ75Ò-FÒ¿=·¶<,õ¸îÊ<Ä0X·à¿9FL¿Ñ¶¶á*QôдïTSX»ã+7ÚRÀ±ÉÆQUXTá;ÑÚ³Æ-Õ·6?Q²Ð
+Å9:çVÀ¾å°Sö1ºDÉõæóV.ÌÎÞ++FÑ¿ÁU¹Ø-õ>ïÌ=ÌNÞW/Ú4ÅM:ÆK2XOÞÏ<F±ÃÑÆ·Ù?Å?<5ÁÚÃÈÜÔãN
+±ÇèëõýR@ãü¸çâðçVÛÁÀùò¸E0õ?ï@/=ÏĹ¹¹é-çWMXW·I.AE:-µò³U0+NDÉ>ë»ýÅ°SòQɸ@+ùïø6±
+·*îÖ<8ÒÞÏ9FN¿±Ç¶ï1Q2ï²;XUíç<F6¿±²¶O-·âð;ÈF¹Ì±Ú¶M=Ëê*Çä<ó°íýÏV4÷ÛðÌãñ,¹;*Õ¹îÆ
+68ñàK±Ú²ÀM>¶ì-å=:+ÙFµÃÁWÇN@Í4ß·µò³WPDÉNT»ýÅ°SÎɸ@ɵ<üæ.ÌIé³ÛILÁéGB7¶å0·:L40¶
+³9ÇâðæÞÃ<FëźØ<7ºÚÛÄ>²ÐÇ5:çVØMîû-º=ÉÀùÏ=×F*¶<+AÐîPßT.ìÞëGÎGR¿¹U-*ÌôJ³Þ7EFï¹
+3¶Ä,ÕVïÂ+Ìêã¿WF3À5æ¶Á.Å,ðê<<èÈÁè¶TÎõ´îÊE8/ßÇMòï-NDÉìQ÷òèåYżG±åXG÷ìÚûÚÆãË@Þ¼
++Ô×ɵûñƲ@ÛËêÚ·ýPüº·X8G¹ôæÏÙÇ·@ÁêYN¼à´Æ¹6°ôÀ+ÄÕBÉÐíɱ**Õ°ÌãWÞíP³CùõüAZƲ@7¹è
+NAH,*KÈø-ùÖPËÊV±ÇÈÑäú2*.óðí½5ÑÛÀV±÷ûYÞ@òîûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉ
+ïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåðÀÉïûåð
+ÀÉïûåðÀÉïûåðÀÉïûåðÀÉï²@+
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 104 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßã¶DÉ
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 208 di
+/mask 7904 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝúÐ81%%%
+d
+/sl 62608 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßã¶DÉ
+d
+602 104[1 0 0 1 0 0]sl 8 mask 0 312 di
+/mask 7600 string uc
+*ÞÐ**üºýÉöü9õûýðùýãõýÉíýAÝýY¼ýÝúü½÷ûýðùýãõýÉíýAÝýY¼ýÝÊàO*%%%
+d
+/sl 60200 string uc
+äÕí´ê9+*ú½³+íÝ,Ô¯1¾èÓ/Ö´¼ûØøü±·¿Óöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãö
+ü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ß
+ãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±
+·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãöü±·ßãÆR@+Óõ9*U¸Âº°÷ûUÞøPïûåðùÍãõIÉí½
+@ÝýV¼ýÒöü±÷ûåðùÍãõIÉI-Vö°ÏöFë×MQ°*Þ¹ôêï.Íæãí½@ñQ-ÎCÞB.:ßV8+*Ëí½@±A-:õà5=ËO×M
+ìÏHÖõóïéÝ5*:úóÝýì×.>M43ØÖÕÑNJ¸G@Ø*-J÷N9Ú¹öòéù½ýû·KHú4*Þ»ñüËùõîêÛýë×.Æá3¿ãðßê
+4ö*+¿ã:-RúJ9ºøîß½IµíݼTí¼û?*¾ÁQ8¹óê×±ù0æÝôVD˼úV¹ÞÁ×?î/,Dµ¼úæºÎ1Ìù³ëÕ±ÉåÝIëÝ
+Iý4*ÞíÍI¼D²é×YÑéãÕ½ê×òë2;øø¸´éÖǺüô=-*YíFóåÒ±Ý0U2;¸¯¼ìC´éÍO½ÝÝÝòå½1*îûõÁÝË·ñ
+éÖÇ°èäIôVøRÊB?Ä˸ñæâ»üû×+¾ÉíËCñèÖ5±<ÊLGæ-9úD²âÌÕÙÕIÛGNï,XIûîÉ;Á¼GÖ20ü¯åÐÑLÃT
+YêÌ7µ@èçIéH*:*Î8î0+è*Fü÷ß?Eô6¹íJÁ-»î³WYHº÷÷÷ô/¼ûå*Zøî8.8±ÞÏ9¶úôå1źö°éÒÔìë9¹
+@GAXÚÞÐÌ6B°KXüCVHK¾=-¶K*¼ÜúîÅ+QP7´Îß¿HöÒÐ9ù»6FúöÐHûU***ì:J<-¶â-ܼºöÝ7ù»VDOÃU
+ùáÌOµäÊCÎÏ1íOÊ´±Yý0**¾E0¶J*µ,ÞÛ±ÝËöìßÏ¿0DFæ-Ù3øZÛK1Ò»ºöÅ***FÈ**Z@**Â-Fè¾E>¶,
+*<¼ùJ/Ò¹Bò˵½ôVø¶ïàÃúV¶ìßN¹øöµ***Ú¯**îâ,8>¾YÍXùîÚÀIÅ6²¶Î1üïñÞ¶/åÄXû×R**å::0*
+*Ê+Fè¾M0¶K*XGøêÀï0üúïéÌOݵíÜÁÌ-Ý7ÊZ=íü0**¾E0:**ÚÖ¾-/îåÇá8ú´Zå1-78ò+>F¸ZÕÇIÍü
+4**¾É6B9¶ß*A¸îR-ä*æ·ðÕ1?NßÙI¹@ëßÁ1÷79ÐèÝÍÝ+**îÄ+82ÞË*:îÝU9,,¼¿01øî¾8ÆõíÞY+**
+¶>++*1HîÀ+XÊÞÏ0Ê+ÆíÜ9Î,Úû×±çOÈ>3̱޴+5èÛíìîÏ4H3*íÛE³Þ´+¹0AæîJ9JÚ-¸*ÜÍ+**¶0+¯
+*Õ6îR1ì:K2Þ+T¾¶Y¹@ËQÍHË/9DÝбõ¼.**Þ3,Ú¿¾-,ZرÉôE22Ê/DFæ-¾@òJýÌÔ»EõëÕ***ÚJ¾·¾
+²6î¾*:õëØQý7Ò*æÜôVøÔWÙôøFöWÝ>ø³ëÕ***.*Ò-Fæ¾=@îÖYÅüù´îåÛ,78ò+úT>³Ö±éÌû6*P9XF*
+âH+23¶Ï+µ6Î>Î0¾²åÄÛÚ4¼»ë×ò»ùô?OD¯Û¾/²ê÷+¾Ä0VA¯çÐ,78ò+Ü,»BìÝCèK<CÎÏ+áG6øÓÕGÉH
+·ðâÆTUüEö?9,*µ*îÒ*»´êÑ3-ÔÚDÐJÁ-»ZÌRIäEò*°ÝDÀFö:Ã1öêÖO1äùB³QY¹@»³èÒA¯8ëöZøêøó
+â*Þµé´GCðæËA;èÒUIæ-µÂ¸ñÞÀKÝÌW°áÌW9<R¿ÆOOëÖîäÓ7»ë×òãÇ=Íô?óåÇ3ñç×=-*õû¹ôåÎUÁËÄ
+Æ=YüJ9B>âÐ;=ôÚ¸´¸ÆN0FµìÕAU@GCÐÕGÉHÜ»ù»ASP»D»0˹°*îÝ×=åWÙ±P»Ç»ùï9ò¿½Éȸ²ãÊOÝ@
+سY,@ûµîØKÅXW?@ÇUùáÌO1íݽý<1?ËVTÙÌ7*ÖµóïM±WN°Æ+¾½Î1*õ+ÈÐçÕ³åôÐ3·?CÚÉùáÌOßì¼F
+Ï4üÔÅ4?S<-*ÓLÃÈHí.1B*ùQø*@@ÈÀè÷Û²,?Z×»ùôGÚè̳WVúåÌOß8½N¯ä,*ö9ó+Ó?äPºñP.¾çÂTæ
+³YéåÝôV4ôôÑÄOZý49öøðÕ5íÃùôP*JïìWú¸÷ó½¹@;¹öóïñÍ°?7½úóßÝá-Ú8Û¶ìØõ,*ÂÙ¶òåZ÷Oå°õ
+ÆâùÍãõIÉí½@ÝýV¼ý×úüSïûåðùÍãõIÉí½@ÝýV¼ýÈöü±÷ûå°Áå<Þº**Zñù1¾ëÇà¿P4ݼM@ÓRÏNÃ,û÷
+û-ÞüR߸ðÚCñÃVTÊBµ,74òù1¾/Ô,ùÚ÷ÐOɼûÄVàÆ<SàÚ÷û-Þ4YK¯Ð>CÐYÝ;ÉدáÅ:MÌúü+Z¿ÓÞ>SÄ
+úöÏYÝ;É@¯àÂ4ݽ*Vï?âLÄ+*àÕíÆãõI*Ù?0Zóû-ÞÀÉ+%%
+d
+602 100[1 0 0 1 0 0]sl 8 mask 0 416 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/preferences_vncdefaultstab.png b/doc/krdc/preferences_vncdefaultstab.png
new file mode 100644
index 00000000..630e9ab7
--- /dev/null
+++ b/doc/krdc/preferences_vncdefaultstab.png
Binary files differ
diff --git a/doc/krdc/snapshot.eps b/doc/krdc/snapshot.eps
new file mode 100644
index 00000000..2e224e1b
--- /dev/null
+++ b/doc/krdc/snapshot.eps
@@ -0,0 +1,301 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 551 339
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 15:04:38 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 481]ST
+B P1
+NB
+W BC
+/mask 7797 string uc
+î½*¾î1:?*¾+á.37*¾Í5;*¾â2+¾QÄ2îQL¿Æ6:<ÙÌâH>ÙóâôK·++úÇëÁð,,÷+ö´2+HV1;â+¾ÙK.BZI
+ÂÖËK2*+ܽLºýQúüÍöûIïù½àõýÂíý3Ýý=¼ýQúüÍöûIïù½àõýÂýù3+
+d
+/sl 62263 string uc
+î½¼**û½0*NÖPGÝ.Y-3å*ÜÅ<èûâA-HEôÉ**ÉUBô÷Y*4ÑDZ,9öéÍ+*¸´ôüû÷ñÍäÊDò0°OLéI¹êÔÍ-*
+îYöûóííêQº0ÃÏ+¸I÷ã¾åÄ»6*ä@ûüÏñ<=ËTóTײÇôú=à×ñùñüø¸ÊøÒ.VXßÖåÅ6,üÕÈÆÞ-üD+*BîûÇ
+YË0*ÚèÕ±õH@»EµLÚ¸æºÌι·*ñìíÝ´é6êÒMWJää8E.Ñí>Ïâ8E-CMØûõÍÃD7*öÖØ?±@ÚÔRÈ>;NÕ÷FK
+ùð*ôì9ETÔCÞSÉØ*ü/ݽýÙAÊRL±»+*ÚööôñÙåHúÂ->UÔÊù-YD9*¶Ô×;EÄÉ?RÈ.ÏÜÙ3KKüG:ýýµÑRJ
+×ûÙ1ËøæÈøN,ì@ÌÖPý6*¾óºS*ãÌíݽàTL;Xó+*öÜÕÙáAÍGøñòFóÛAL*Óüì½öæº*Þ¹ØÜÙõVÒGMнèê
+ÜC¿ØQ÷ܵHµ:¶/Ýñ¾3-TÝEêÑOJýý-¸Ë/Å<¾×à8½Ýýûô=<ȹñ¶;45D*-ÝÝçÐQÐÒQ8*øHXF½Ì¼»ûõãÁ
+õÌÛ±ÍüÚ·ñå=P+ʹ½½åùÀåæü×R5*úëêäßíÅ9ܸºÜF¶ê³9Æ*L?ÝIUÎ÷*¾ýFêAëÓLW8²ïÄËË÷â.ÀàZÏ
+ÁýÙAÊä7æýýý8¸¾,èÒï»ùMí@+*¸Þ*õÖî0,úTù÷½äÀNÊOÆ>ÏÏÚE1*ÒL¯K-¾ÛùÑó÷òÃ>³3-FYF½GÛ¸÷
+ûñݽíéÉåüº·ðÕÃPÊ,ܹýüùüõí9F>»**ܵµòîïíYíG<AÌú´ÛAPÞôõ1è±L*îX?M?T°Þ¹êRÖÖ9ÍÆJîÌ
+ù.ø½»OH½È´õõ-QDHÞýÌÕí·î÷ø=±ÍH=ÉåYHð;,øýÝå5H¶L02±êýÁIÜB+Êé»ýü÷É/æSúÐ55XÞKƾãó
+̽ö-³îÚýÉVÂYäË8¿ÛMFZºáíÝúæ+X²ÀüÚ6HýüöÉ;À*XSà¿<êK*Îòü¾¯<»;OWà*Ѷ*Þ*ÞÓà÷àãÍY½Ý
+Ýüú8½ûõâÉ9YÊ°à>î°ùõµFXýüÁ¿æ,*½TB¶ñííéÅÍåUõGú¶ßM9Î*óí½±P*îDMõ>·Îܶå2ÕØ6ÍÆJÊÍý
+òúIêLEIýõKÏ÷ÝéðýýYÛáýü+µú½ÜF¼ú½¯T³ýÅWM*¾XöÞ/CæËEÇü1íéÃ3æøýÛSøäæÖ¾+õÜÇØZ*¯÷ý7
+FÞà5½üýÙèÞèýAóìFÞÓTJÚ=ýÍ9õä+X²,µüÝCÕPú8Ë·Â+ÚëÀG**Ðë½ÙNÍ1ÎÒO4Z4ÕÈR**¶Ò¶Tô¶îáã
+á9úáôÝ-ý»ÚVÄåRJ**¶È,,*¾Ç,+*Þ74¶öûåýÈIýèÍ/*ÜѲðàÝÕÅýÜÕõHFõïÆYôV>JûùáÊ3*úâäH1è
+=AëIàEöà=<B´õý99T0=ûúÁBÜöäÉûýYç6¾IùýÝÏöýU?±LýE9AÄJ*ߺß-1â¾+ßÂSñýûÅXʾØýYDêÉ-
+Á/ÎKýýFÓ6V¶/1ÈçÔÊýß9E³Zûý9,·8î41îÐZýýE¸ñ*AD¿åýµóUNÇû0C8Tà¿<ê+*Zðü¸CèºÎQWÐ*Ѷ
+,*¾RF+*ÞÏà÷Ìá±ñôÜÜûùì¼»õÚ±9íCï?4ÊQÜN¿2+¶IÈ2*ÚK+ìÛ½½Uóéñº**âÏõîA6D:ÈXU²×RÞôõ½
+×°P*îXÍàE1=ðÖ1+áE;24áÞðMÍÄ°ãüñÃLIÑÎC2÷à*ºýýÂBÜú½IÑCÀ=H¶K0Ø°ãÐýÍ5¸¯,4´õýýáÑ7@
+Z×õýD¶ÖJøý½.1,ÐãöâS-UýÝ>Èñ*å¶ZÈïä+X²L¶ðòâÃ6FÑÂTÀG*¾³ìI@à-Qè=.2ñÝ×ÍY½ú1²ÝSFí
+2FóêéãÍQíåÉÝHE³î79HL¿-ìÉ3/*åZL4I¼ä-*ZýÀýÜûL3Ï´îßý¯V>JûùÉÊ4*FQäT9=¼ÆïàM;2øð<ý
+üíéÏâõøï;J¾*3D9¶¯*HüýV;»üµXR»Þ/9F;->Sø½ÔÉÌʾõ±Õöý÷÷üýµìúÉX¶¸î882æýàU7Æóý:ÉGì
+ïÞ×+Úæ.5ýIÖÆAÆïÒ<4HÔ:*æ¶ýÙ/QÜîÑ8¯¾SFÀ8ÜúJF¾üRïúÝíÐWMý8¼º1ýG¸éнýMö0/Xº9*¶Å,,
+ûù;¿USÞCïáµãÛ¹É1**åï0HùöL-ÃÌE¶çí4@4ÖùûU¼Ê3*ÚàÜ»ÁñÌØÔZ¿·ïÇ2.»÷Å;ÁýA÷ÏíýP´ãÈ°é
+-X6¾1üYÔLæýWXÀ9ëýÝóï/9F:-äµÕõýæI7¹¯,êõåÆBý1Y>èÞ@ýÅ÷¯Zß7FöýýCÏVûIâPÑ0Ü=ºùQ¯Ä>
+4÷?8ÚÇĸ¾5ËÎÀýÁ8×ðéDű-·Ð.¹**6ÛýSÀ;YöSÅBJ?8Z9æ-íY?ÌÉ-¼ØóñëÛ½õìÙIµ»D¶·,íÎÞ3=Ü
+M+·ÜûÆÞÓ>ZÉñåÙÈHûâ-*Îóì0ÍÛEø*,ô8۵ﵿ/V>ZäùAãB=*öãî+QôTõíÇÀXà=;2øð6C,Û9¼JÄR¯
+¶üøÙÕE9¶°*Ü?¸üíË0ÐÄ;ï³ýý39F:-üÍÝø×Ë@GºK0Êû»ýöYõà>àÞP¼IÍíZß3FÂ>JýíÇ,àÔÐßKл¼ý
+ü÷ÛËGà¿MçR/·Î3Úïèïúõɽµ6ïêJì>ß4,*»õYSîá5¼è;0¶Î¶Òû¹öå4åÉ/µ7ù÷óêÙ¹ôéÍá´Gº×½CÏÞ
++GHL+H÷û¹UäËÁº**ü9KîÛ±1Í8¾Åý8Ûöî/³ûU>ZÉù=ÌB;*ö¿ìãµõÛCN¸ÎîÓ32J¾,+FIKÄ.PâÄÇ:CÂ
+S»ÞÇ+J²Ç.SÐ@ºJÔ;46¶Þ-1â*+3Ø>6ßƾ-=ÎÊ2/TòÈÝ3-U2S;¿Á/Aî:Xå*/Ð>ëòî¾Ü<3D.3ÓEÓ¶@
+7Ó:AÀ+Ó¯<N2ÐDű-·Ñ.5:*ëB½½·+EÜîÑ8¯¾=Ã-*Ì?Ìã,ÌÙô-ùÑ**î0H5ÝÚRJÖµ½Ä,6иóÔÂ5íö,*
+ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2¯H*Þ1ßÐ97°ÛÀ3;ßÔ¸:ʶåéÆÇ1ÒêüÎÙ/âöÂ͸â+8>Ì**îVïV=îß
+»I3K=Hº=»SϾWåÜûùUSÞ7ÍIµãÙµÃA*ÞïÃ<ø¹öïçß±øçËĸ²7ß->ZÞùÉWB;*B¾-í;ÊÇ2Ôã-ßÂÙìIJ
+´¾9Xíͱھ3ÍöSÅFÞ÷à÷KÞ¿5Y8»¸ó¼ûù÷éÉ=IòXä>Î4Ã5µ?¹ô=Fô0ÛOø0>°ïÍC鸺2ç¸åî+4JØûç½
+6ÖY*J*+è;2ßÀù½8ÔÌÞÁQúÒM8*6ÜXFÕ-FöîáËE½ìÏYýX¼ùô¸èãÊ,4ϵíÔVÙ¼ááHîFRºæÆKñ0øßÒUF
+¾QR*Sõ½H;Þõ+*ú³ýüýÐã=öPÍØ.,XAÌÎ<ñ6*ÂÍ·CÑ*AÊGÙ?çÐ1H+*AöÍÛ±ÚR¿L/·G±õÍÞ=>Î4¿õÅÌ
+ÐÝÝ+*8îîüûO0=ÕíË=óÇ-FYFTQUÑM*:ØèOË,Ö¸³UÌGÝ4°ýµÍG·îâ×Ù1üÛD+äƹýüÊMðùA*Þ/LÓܽ½
+ÇÛÖ+ºåÎÒQ>î¶ïRÝ·í8+¾ÊéA³4JæÎWSÔF?Fù¼ÃïõíÍG°áîóÅ**Ø5÷ó*;ô6ÍÕ³óôóÉ4Íøñ1îðãÔ6EÌ
+éÌïÜÃÃÇõýB*ÞEéÊO6E¸÷ôã³Qû3.öö¯UÀ/Qä.X9H¸0Å<óÉÕîÓùG*¾µÓS¯CZãÔÃ=ôòóö<°¼À/ÙðéµÙ
+â*ÞׯÑôVC¯ß¼+Yí+*úÕYÞÀ7±@ëDÞ@EF³R;2ݼÅ3+ùõ9*×TßùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂí
+Ý3Ýý:ºåP²íéáÕ½Á*Jöé½ýÐËæÛÁQÝÜýùíÍ5ðßA0*ìEõíÚ½1åì»°PÚÏÑHHçYã+H»öîÝÍÙõYIÓµÝüà*
+Þß=ÅÛ¸ôê×»-òSCï¸ÌD¿ÇüT7îÚYé¼GÙºüûçüû÷A0*üüúïÑKÙÌûDÛX¼»HPÚÍÑPZÐ5å*J³ß**ýõÍEüC
+ôéÎOÁÝÝÝòå½7**åU*ì+¾õÝ9Ü´ä̯áPÉWIÍÅ3õñÒ3Êòà?JRûÝZ*êIµFïéÏ=½ÜüüüÃ**FY+¶3*̼öã
+ÇMÝHÚB.ÍÅ3Ó±êÔIÉü¼=QóûõÁÝË·ñéÖÇ°èäIB=¸ÛÖEAäFûúù:ýHÊ2ÂÕ*Ú-¾2¼9H·ïäÖóðëG9=øÞÕK
+MôºÛ»û5·TÎIPT;Á¼GÖZî9Ê3»VèÈ-QXXH»·9ýD**´Õ*Ú-*½ÍÜúîÅ-UPËàäÓG9=øØÔA9Üúúºù,ÝüM*
+**0*X»JX4îöëÍ98÷îãÔSWÛÙIB=¸ÑÏ5õ³êÙùõÀÜúØC:I*Î::<îâ+Ä.8?ÞÞÞ¿0Úø+:Û,î8*Fûõì¹Aì
+Ù¶²¯ø´LPòSOIôùB¶öïO»øÅ***ÚK¾61ÞéÅ1ýÙ¯ÝÅOÉҶͯáP>Z¸=Í9Fò¾ä±àÒ¾ñÈ,ÚPæÁó<ÜË»¹59
+H*ö+*Á*/+A@î¾**¾MFB3¶--µ:*@Õ*>9F+*9ÝGùëÂó807²äâÂZñ7¾F,¾M4+*8RÞÃ,Ú°¾:ÇMI+îÍZ-
+êºÅ7EKÒE.Æ02¸Ï3ñ,ÌU4î:I1ÄU¶´òèWùô*Ø9*Â,F¾¾=:¶¾,³+Q2î00ìJ*BX+¶2*XGøêÀï0ü6°PP.
+9Y*â·ÞÊ-FZ¾¹6R0¶K+A.î,EÞáÓCï/¸I°**î*IXRÞC°³J=H1ÌU7¶L*üÎñÞµ-áXüÚOÍ8Jø+¾R¾-2¶,
+,1PÎBÎJî428/+*X+¶2*üº·ê¿í0ôúZPP./1Jâ³ð+:Å,:Â,*ÚLÅEC·ó8µHð,E.÷éÁ7ÎîÔÊöÀÉ*ù.ºø
+Æ,̲TíC¯HÞ·Ã,M**î>7Ù0XF·ð*Æ9*Â,>ÐG·¾5=RG¶À+1Ðî..ä*¾ì4¾-*¶à0NF¶é=µ*ZPP.ú0JV³í
+Ý¿Ý,**îâ*0*è+X¾JïJËÞ×2.+ÎíÜU9´*¼é°à5Ÿ°*õBóL-ÌRJ-Dï?îÊ/FêàMBB-*öì>ë+¸Û2Ö=V¹-
+±µVô428/+ÎW+¶Ö/Að0îãâÂH̱޴+ÑìëùC***A2Î8îN.XZÞBÞÃ0Fð+õë¹µè¾ë,ô6àWÊ3Yö¸.JÇè/²
+94ÚÖ¿=4B-æ¼Pº*Fµ<îÊ3YÒå/+¾µ1È@Êô*ÛXïêß-F¿¿±»¹-+Þ³/Þ/A6*6ÚGÆEö@==,²+:øõ²ëÙ·í¼
+.**Þ/,Ê-FZ¾¹6¶Ó+A,*·íÔ»øíÞ0¾´UPVNÈLÏXÃÞÇ*òG=ä-FÕZ±0*ÌÛSØ=E¹ÑY19îÀäìFÞ×:ÆëRU*
+È1Ì*ÞÇÚ¿±CC;ú4*ϼOáÌW¹ôM***Ì:JFLSÞÃ,Ú¯¾3¾5PR-¾Ø±á@ëµZ-úØK=ܳ¾±4¶K*ûæÂÕ6î¾,Xð
+0*¹Ñ²Ã¹A8Ô<îNß8FÞÃØG¯¾EÇU,*LU*8Fß/IFÇE*@==ðUÎ1áû5DôÛ*¾Ä06õZæÕ´ãÂ+PMPó*Q6Z,îP
+.XܹíCï·îÂ,Â-Fé¾-*Þ×H5¼éµòéECDö×,î**T31,öóæ½í4üFUPPòÌRIäEB²ê×Y³àò@Lâ¶*HE´ç¿
+÷HPëçØÊB±A²H=PZÅõU*OÇXVAîÛZHöV+¶ÒºQ¾íò@@WTPPòÄRµ8VBòé×S´êÕA+*ßÂÈدßËUEØ>=H×è
+FßÎYËö>½ÊÐèÇûÜâúÓB;Þ¼ÎÂ+ú¶öíâÓ?ãâÂ84°çÆ3¹XGùL*Þ·ñ¼»×°çÇ;WÈ3ñ0Á¼³àÑ.ZÃP¹@ºAÊA
+Õ»Ô*Ú/¼ºÊֱ谰Lý@?óæÈCɼW²éÜÄ<øíÞ³Á8ÛØTV:õY°à.TÞLCHG÷°B9¾Ô*F0¼ÙºøïNCCÏáÛNCó
+çÍKÍPÕ³ñÜ/*öíêÇñÔëC=Á°Aõ1äÂùߺÇW²/J´Þ½êÂÌûàKO=Õ´ÛØÒ´»5*î×»ùôGDéLåRéààù¾3µÚÂî
+ýü·-îì1ñKæ4¾ê¾ýØ3;ùØàêR×7*ZDÏ4¸ëéø÷HZàÄÄÅóZ¼Ð޽캻÷éÃõ໹A*ζõ@¼Ûúø9ä¾ûÄBý
+ú.8-ý·=TôéÏ5åWù´*¾¶5ÅüÅ?ïÌBIIîݵà1Ù+Uàúò¾ý5EïØÕïûEïùµàõíÂíÝ3ݽ=ü7P.Òܽ*FI+öI
+;=4ßµD1*¾ÆåÅ<JNPP>ûÛµ8**ÄÍ9O*áÊÊRúñíF**<IIÈ*ÇSC¯õ**ÊúúR,PPãÂνñ**¾úúR,JôH=ÀÆ
+Í9OJâÒÊ2ö1***Á*¾ý¿,ò0,*ÇÁ<*Þ+*²ÇÇ*üÃ<M*ÅNðL,¸ßR8Îâ³>O¯<SÚ*ÆÊÈ3+ü**+*J?Þ:J¹6*
+ÂV>3@ç<:C-¶Â<µVð,,¸¿S8ÎâóÜO¯<SÚ*ÂÊÈ3ßú***:,Z4:<**FƾR¾¿¾59Ö·+*ÇÃ<Q,ðÞ*ÀND+Nä
+*8Ïâ+¾ÂÁF*Ñ5G·M=å4ZßS¸ðNÅ4ð2+XDJ4?+<±°Lçý»***2+B/¶á*+,³,AHZñ+*<5O8òá?Ç>-F¯¿Û
+9Þ/ÈF+ÆU<·N+ã+Õ4ZáS¸ðNµ5<+-ÚíËÒÊ2Ô1***Q.î8-XÆÞ/5ê¸+¾âLÇ5:·K:17Zù+¶¿<Q*<+ÉÚSÒ
+79ã+È**-*ñ*õ4ðìMX´ÞïìNãÂîº***R-R/2*R9¶Ï*1HZñ+*=5O8Öâ/ÂÚ:È8*ÇC¾5>·,:QHðÈ+ÌâÞ:
+ÞÃ,V¶ÈÌRáßÉÚÐǹAÆJµÐ°LRH***Ò+Â,F¾¾53¶ï+ÉA*J×RðJMXÞâÓÊò¾*FJÇ=<·ô;ÅDîL*ì:ßÄÑòN
+D8HçßBãòQ¾°TP.à-**¾L¾R¾¿¾-0¶Ñ*1¶Zí-*A5OXÖNúM?NE¾â6Þ+ÈF;ÇØƹ0¶½Q5ÌÐ=ç¶Ù:3BÞûì
+N㾺***B.*âQ+2***à+L,82J»Þì+*°@ÈF¯Ç-G3>·î*é1ÇC¾->·@OÕ6îÊPÌ?ÞÀÑòÂDX?â:ÞËÅF¹¿
+>:;?=ÀðË-*ÎñKÈ**µ2Î70î<OøQt´ÏESC;NíF**JíÏ.*¶Ó*Û¾*1Sнв7ÛÇÈ3Y¯JÉR*>»Ä<*ÞG-ò2
+Ȳ3/¾ÐUL3RØùÊ2μ¼>+RÑ°LÖÜ·**м¼>+S±°Lß½ºñ**JûúR,ÎäâÂ,ºèä+*Jø÷Ï.:ËÇ3/¾ýí½*8ÅJ
+XPúü+î@.*¾ÊÊöûÍî»4NâÂ*Þ/*Æý*Xéß÷3âßÏ¿/ÕÜ:UGNî3±<CCà/8FöÉ.¶Z?0ÇðTÔé6ù>ÊÊÊöÞ2ð
+ÞÁNËï>5@<QÓ1ÅS;+=ÜؾóE¯XÌëYEÆÆ3í6QËêOÊ?8ÚRû-¯¼Ð*åPîJ4ä.Ì7à´ÊöìGXÂá3/Ê5F±àUU·
+J01@°>î¶UÔ¾<A<ïÊN0,X6J-2ÞÓóÚUÇÒ5;;=HÇÄ-*U,¶Q:Q4ð¾,ì>ÞÓ3ÚÙÈUÁÖÇ=ÜÞÝùï¶M71ÄîòT
+ÌÚç÷ÞV0ãXØàË.ò¾*Ú/Ø4¿â/:;=ܷù.ºO:µ²îæ08Lçß2VTÌ8âû÷ÀFѾ.ÊöÂ98ÙìPPðFJô=5U;ǹù
+Ǽ;<°à=çÆÁ3+¾*¾53R7¶K,ɳàÁBº>8õ÷ï*58æJFÞ3,êÊ3ÍÆàC´Ú+¿@2**:,Z4Z<Î6Z+,ÆÆ3í6åS=
+HXè¹³½>.1×ZMPÊÞö8ÅáË?Fï¿E<¶ÑQU²à¹?æÈP.*°°à-MÆÐ3í;Z=Bï/°F±àÉD¶2+QØZ=ʹñÏå@ðV,
+ØÞ3ÍÕNè.ÆÆ3íÚàS=ÄUG¶¿Z¾3¾5,·±,AHîà48æÞÐʶÂïêB8?NSJCÞ7¼ÚÖÀ±D¶<1ÃÁAHî,3@±<µ3²A
+×Ö2ÉÚ3ÖOV.*ÆÇ3ÕO?Å÷ÐU0γHгF<;=ìRÍÜ°=,*CCïÐÌïòGø0ÀH4*NâÂ*JúçùI¾Ñ9¶èÂ
+d
+551 113[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7797 string uc
+*Þ´**,ºýÂöüÍöûIïù½àõýÂíý3Ýý=¼ýQúüÍöûIïù½àõýÂíý3Ýý=¼ýQêì=,%%%
+d
+/sl 62263 string uc
+äÕí´ê9+¾Îøýû-Þ÷3ÚLKéäÕYUíÝ3ݽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ý
+PúüËöûEïùµàõíÂíý-ܽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûAÞùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµ
+àõíÂíÝ3Á*ܽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEï;*ºýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöû
+EïùµàM*öüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂÅ*îûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂ
+íÝ39+ÞùµàõíÂ5´àõ9À×EïùÕ¾Ú¾îûEïùµàõíÂíÝ3ݽ=¼½P²íéáÕýý**ßYýûàÑÔ7ÊæÛÁQÝÜýùíÍ5ðß
+Ý7*¶íÚ½ñüGF¹õùÔØXZLÍHºöíåëùJLÔÊ÷K*J´Ô½8*8H·êÓ±åÌëEJ½>Sô»»úøÕ1*.½ûõËßÂÌù³ëÕ±É
+QÐÊÚéö¿**C»Eý·*öùñÅÍ0ë¸ôéò¸÷óA:ãÛÁQÝÜýùíÍ5ðßµ/*ìEõíÚ½1åì»YBϽÄûD²çîÕÚ-ý½2*Jà
+Ö÷ñ½Ü,î8*ºýûî³±ô6E´Øêø÷-ÅðçÁ1ýûÚ»ýøS»ùóµ/*8H·êÓ±åÌëEJí4PòÂYÅÌú÷øC7Ý2=àñýõí½Ü
+,î<*FûîÉ=ż»¶¯2üÞÏ>õéØYå@IúRôåÝ?*öùñÅÍ0ë¸ôéò¸÷ó7ßÂTAôäÃCQýRÊ2ÂÕ*Ú-¾*F9H·ïäÖó
+ðëûÞÏÚ±êÔIÉü¼½½çÍýØ*Þùí1½EñäÕ¯=ãÒ˵.=èÖÒ?1ÌÚúE¯LÀ9ýô4¾¹*îõçÉ9ü¶ZãÒSêÚØ9FÈ1¼7
+D°åéëé½Öǹç*ÕͼöãÆKÝ8V.-í2PÒPMAìéÖõµàÂNܺç?*õ*Þç½õܹïÜÃM5ѵYBÏÕ³»÷îãååÙAöóI,
+**Z*îè-<Õ*ÅíüúîÆ1Y´ËèèÛG;=@³Nкųî².8ºJ8ÀÎDK9ÔÉöôµàÂ.Húã?*-*Ó¯±*H¼¹ôÙ3éG6Dæº
+àÂHÓòá¼9-1IÜÒQý2**¾E.ÖÀ+öøïßý´êõ¯ç<?ì5äÆÁ3ñKÜãBÏÀúFBXß×Ý3=Ðôë7Õ*F.*Õ8»´ß¶-½0
+÷Â3ßäð´àÜ»úðâ-.**X:**R.**T,8ÒJ»ÞùÜ,ÁÙ4¶ÐÏZñ3±âãÂ+4?+Ï*1PZHî2+X²KûÞÇ2Ú2ÁKÁMQ
+B6¶ó5øÎñÞµ-áüPÊÆÐQ1Ú,î:*ú÷ðÖ-Ý7ê÷ß=P.9ÉÙ/¸óåõÔöó.í8OÞË4ÚèÙU0R.¶ÏÏî¯ÈPº<¼ÓºÕH
+îÌDìÇâ79Fð¾UJ¶L;*îB;ÖüPÊÆÆ=ÕÙ,î**àØ/**ï*NF¶éáÂ*î3=ÀÀñRòD÷ñå>×ÔZ8îæ*Ì3ÞÓ6FæÀ±
+0¶ÐÏÀúó¯0RBA=8*ÚCæ±ûɾ39ìJ+àO²HðÀC*¾µº-T½=PNÞ¿ÑW+¶**ó¯ï*òÙõèñµ*í3=,¼óRàùõ5P¶
+Z/A2Z8îà*ì2JJàÓPÚî¾E0¶Á*AÊ+öããÂQ0î¾5ÍEï/NÜ°à=غÁ*ÇBϼñ6ÖWÞÒÝ3=<íÜÛÔ*F*¾ØÓ¶*í
+ÛE³Þ´+¹üôâíJÚ¸>æûL3XVß/,Ê-ÚB**îL*XãÞË@ÚR¿±=2åRä*ïбà=Û¹>ÁõÊöô>9ÃJ¯Þß9ܲL¶@ºM
+üÌüPÊÆ´íÄØ,Z*Îƶ0*·íÔ»øíöE¶ôâí*ظ>лEõ¸À-.**X:ÞFÞó+ÚÈ¿5Y¶J+A6Z0îÂC*زËöê29Dï
+?LPZ/ÕTîZ@+Öî7ïUøE¯LÈGÙæ4¾,¾5OÒ׳åÄWØíöE*ôâíÞÕ¸>¾GZî2*8RKFÞ/0>-Fø¾U?Æà*ÌB-úÈ
+Ð3ÁõXäî¿1>úÛ¯ÝƾMERF¶ÏÁÅÌîÞ,ìNÏäøVöWÝÏÝ3=<êÖ½Ô*F*¾ÐÝ¿*¼GøíÙÀIå¸àÂø4ò߶ýÀ¼¿.é
+°*77BìßÎYOã¹پEкÎ*TÈòà¸+ÁüRÊ2ºÔ*F¸à-2ÎÏ+éWVØÒ=½ñÌRIäEB²ê×Y+YÃð7AVJ+º´êÐ-ñ»
+ÊØѳCï/ÉâÂ.1G*?ÓÌêµîµáÂ*ù+ùÃÝßß½=ÕÔòâíÚ;èË-50û¸´È7ùóW+JBÁдV¶ñèÄÔãÂ3XðK1ÞLÑG
+5@úÍB;Û¼ë±/ÞëÝßáÂGÑPòâíF>âÐ;=ôÚ¸ôË+¾ñäûø°ãÐ=Mع=Üò*9ãöòâÂGͯT2IýK*ÞüåÌ7²/ÞïÝ
+×ãÑ9Q¸ðµüWTèÏ?±@ûØæÔ»YOòÝ¿é0G¸³ÓÖJìÌZà-ÜKEíàìÑýÝØQÎê×ÃÔ*FVàѽñ¶ASP»ø±æÑç³ëÚÕ
+/*üE¹ðÚ³¹äâÎêãÛ;ʶÁÜçÕÊ*KU*8,ÞëÝ÷ÃÁÈPXë¸óèÞøØ*Þ±ùôë¹²ÔÃÌÏÔÃÃݲL%
+d
+551 113[1 0 0 1 0 0]sl 8 mask 0 113 di
+/mask 7797 string uc
+*Þ´**,ºýÂöüÍöûIïù½àõýÂíý3Ýý=¼ýQúüÍöûIïù½àõýÂíý3Ýý=¼ýQêì=,%%%
+d
+/sl 62263 string uc
+äÕí´êÕ7*¼ïÎÐÇUÙXÛDÖüµ*Þ±ùôë¹²ÔÃÌÏÔÃÃI¿>UUÊ7?é-*P=TKÕ»WI½O´ÔËYÝÔÔì¹òËÜPÃɽáé
+ÉÝXºAìÙÉ-*ùµïéãÕµ/=ìϵøØ>Xü7XÅXFµëé-*àWFø±Oê*PÚNíò³TFûÙ7YJ-BïíóÄõíÂíÝ3ݽ=¼ý
+PúüËöûEïùµàõíÂíÝ3ݽ=¼ýKÆÉ3+¾,¾M8¶ù0;,ÅSîî1<¾ÁùVRPÆÈ>ðàÄáüÖÀNâã1çüL´ÕÇß1ЯÁñE
+¶¹,UZÎFËB91êÊ39Wß5´L*ÎÐ9*ÚK¾+¿-ýI2R;¶+-1ÚîN.T38BÞ+,Ú¿¿E/¶ñ/µ´:;SÚçÅ52¶Þ*1>îÚ
+QÐKÂU0¶µ-ÕAîÎ:XúàÓKFçÀE7·TB3ÚäÛ2.,ÚùÁ=ضï4µó°4î20Ì÷J0À·Ø1õ°±GPº:?Å8ò0HÌëÞ3AF
+CÀME¶N+Å6ï2½8×J´Ð¶ï-M*QÏ:+ÍâÀ2õ/ïÐL8÷ßCKÚ·Õ±Á24¶ÃÔÕùïø³XôáÛIF<ÁMÓº÷21Ïñö¿8CÞ
+÷HÚ·ÃÓB¯+J0ýRJ¶D<µðîÚTìïß;@N¯±AÁõX4°æÎFºðH;<Z¾¹S¶êDA½ïö´8Oâç¼NîDQ+ñÖ´ì³X@IÈà
+¿Ð¿ÍûBïW1Û¸áɱ·*-1Tîð@9»å¿;F¾ÀMX¶>.SÆLÌPÊÀQì=àï³ÚËÀGB¯ÂàÓöNñDõø;==HÍÓ5B¶Ò7׶
+P,/×G=Üÿɷ¸Õ/Å7î8@8ùODÐÆÍ3í·ßã¹NÎOQ÷ïZ48Øãã@ÚFÂ-4¶:*Õú:M?ÛúÁ¹ØÆÞEìÈèKÙâ0IS5
+JÜPºöNÅåðT5ìCÞûðÛ±¿êNï::XQJ,H·Í2÷²ÞWIFÔÈÉó¶ø*µù;ÙéÚHÂ=0¶íJõ¶ïÜç°çÁ=BÖ5=@*FÈÃ
+D¾±Ý26¶±DQâð¾,8¶ß+6>0F¿¿-;¶Ñ*ÅVî.±ð:ñS¶L*µ>;/ÍNÁ2õ²ôì08Æß?IF4ùݶ:/õ´ñæ-XÓJ
+4Õ·B,ó71Iïê*XÎÞ×MÚÉ̹±¶ëCQOïÌ°ÔßÂWó+÷8åHÔZççæPæöTÊöK>ïY¯à9ï6GæÂYíUܽ=¼ýPúüËÖ
+óÊ2*FS¾E2242*Æá;XâÞ6ßRÞÇ-FÀ¾>1îÞF̶LØ·ÖPRFºÁM7*H4*°àÁÒ¼ÎGËö*ùìR¯4ŶäZQT@×Dâ+
+3Ù°àU:S3ÖÀJFúÂ=PÊ89D×3í29*¶<+AÏÒúB¶î¾+9>ß/CFèÀ±YºÒ0ÅöîÄB8×Þ:JÛÞÃ;ÚKÁ5å¶å11Ãî
+*8XKâ×°Ú¯¿±Á¶Â4µÆñð³Ì²Þ3ËÚáÌMPV02.:HºÊ.*9O+1:î¾+ÌâÞ¿1ò·=HC¿E,¹Ô±ÅØîÐ3YOà+CÚ:
+ßMƹõ3õVîÆ=8ìáï2ÚÏÅùPÊ9?XÄâëÍÚ³à5Aº¯À¯¿M*/+A±îR6ØÔ39HéÛ:F²Ø-05+¶á+1.üPÓì´¯<±
+¹µ.åîî@GìEâïZÚÛŲR;Ù0Fïß,¾À¾=;¶R*ÅBîÂ+øÓ3ÁÁ4ÁÌÃú×8>üO¾X1ËöÌ>ð¯àEѶSõã,AèïÀ3Ì
+Þà+ÐÚ5¿EU¶Ê6Õôï@?°ùß¹ñ·ÂÀQÜöN7=ZàÑËêíPºýRQZîR5ð³àÑ@¶Ó-õ.ïZDÌìáÓ0F2¾.ÊöF?°±¾M
+¿¶çµWöö-XÄKÅDïÏòÛ,õ5>¶G¹åËöZÒÌÕßËGÚ³¿ÑZºò4õèôêCX4î3FF²àNRϲ:ÁÆN¾GÅÊÖ6*ºF´àº
+ÞÖµËö.59ûæRÞ.ÞÃHGξER¶Z/åX÷R7ìBÞ?.H°¿Ø¾M=¶¾5ÕèïÌ98÷âÏ2ÚàÀM-¶J*1Rîê:ìÏàG¹FËÄ-
+ÁºA/1¼Ö*JÖùR5R8¶ÎÃQ.:HÎ<ΰÎ6îÂGØÒ3õV,õ<×;@î6¹µ±*G¶À½É3¿Åò+ÎSÚñãC¯TÃÚÏMú1PúXõ
+ïûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂí½2ܽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûÝÞùµàõíÂ
+íÝ3ݽ=Pû/º×æ¹Ëôêϱ=*Þ¹ôêçòÌæã½Ú3¿ä*Ü:¾ëBï2*æê*P²íéáÕ½Ý*Jöé½íýO.ðXúìÄÊRôÆÙ×ÛG
+¼×éPòìßÑYíýûõå/÷îé.*õËùµìÝÁ±õÜùÉ2RÄê×±YS5Ý8*:èYÍÃP*ÈÉØÙ.XôÖ3»øîß½IµíݼTí¼û³,
+¾ÁQ8¹óê×±ù0æù¼3+ð·Gû7ºöðà¯í.*ò³éÕWáì»è34IÎÃëêW,A¹êÂÌù³ëÕ±ÉåÝIëÝIýD+ÞíÍI¼D²é×
+YÑéãÕõº=¸Ë¼úRÁ7¹ôë<º÷GÆý/-¶ëÓOÕXÛDô´âÅ9Ë1²¯E¹ÉÞã8EϹÄëø±çíõõAû÷í=*ºýûîµ±ìúD´
+ÐÊØ÷̵LåÄ2ÏÔÒWÍPW¼=Ý4=øîÙMµ@ÛD³ÕÂ2;´1²¯E¹ÉÞã8EÏ1¼7D°åéëé1ùµ4*XIûîÉ;Á¼GÖ20ÜñË
+æIWزB³çÉG5íáõ,*üùóâÇOÑHºÇVø²S¹-øÍÝ6³ñéõO=øØÔA9Üúúºù,ÝüM***0*X»Þ¿3Ú.¿ÂGÞïÙII
+FðßÉWÑظµõº=¸SÐNëO»÷ZãÀ¹ö±ï-Â-*<ÚßMPîà¿á¼ºö°è¹M><×Ù-»ÉSá8ûJ,A¹êÂHÓòá¼9-1IÜÒQ
+ý2**¾E.¶R,ÙB*YIHúíÄûH@ËÐàÓÛñËæµëØã³²á¼75ëÝíë9ûýý½ÀQ>ÂýIÇ*1üEôà¼7ÅôDL;ÇÛGåÙÙ6
+¶â-3Úê¯=øËÎ1éOºEùô<HúM***X:**R.**ì;Þ?46/FÎÀÉ0¶O3Q8Þáµå¼E¯ÛÁKÉ˵ÙÜ3»×°ãº¼8FÕZ
+¸Û¹öE*JÂÁýI02.JÂ3=X9¶ä*ARî¾+XVÞ6¾ùìûØîÚ¿Iù6ÏÆçì8>X<M¹Â.3Úê¯=øÅÍ-á?FE¸óå°-*ÚJ
+**Z0îÀ,8OÞW/F<ÀUS¶T*XGøêÀï0üú¯íEÏÁ-1ìáä½í,PÔ·-½-*Àöý=2ZÞýýQF6:8îÈ+XÎÞ/-¾ýQÖÞ
+¹ñ´7öëÝËIÄRFÎÃFôô.ï°CغXôÖ3»Kå¿í0¼Ëú·Æü4*ÀF/**ñ*Å0î¾*XCÞã2FN¿é,îåÇÝ8ú´Zå/ùÜ3
+ÛH»ø4°ôÙÔYTWÙõE*:ÂýÝÀ¾B¾ÎÞ¹0¶¿,+.1DîJ*FµëÓûÔóÉ·ß2Ã-ض½µAC:ÓÏÛ½×áP.úÄDR³íÝ9SA
++18ÞÓ->C@/FȾ¹4¶á/AR:8îê<ÌF*ûôÛø¶.îéùP²/5@ÒµÊÆNáäû0*.ïýÕ::0îÐ+0*8²Þ×?BìׯÉHP
+.øL><ÙÙ-9ÉßæX/ëûÉG´L8åCZÙ¾Sõ¼>ÛK**ÞÓ-ÚÖ¾Z¾M3¶S+ÕDîî9ìC*õP7ÖWÞÌáó»=¸Æ:K¼áÚAëÜ
+>D´ëµ*J2ý½/+¯*Q7Z*î.3ìV¿éÄ7¹òܲù°0íL><ÙÙ±F¹¹²å¾ôúã8E;J5èνQéÜËùôM***XGÞÃ+F:¾
+U0¶F,1ÑîÐ*:õëØQý78+æøÜ37QÌFÉ/=ØÛÍWá¼.*¾ö/,2àýé*XEÞÃ7F:À-+ZÕW½ÜEÊ2ÈÃRN°µ1ö@5Ì
+G.Ø.XôÖ3ßÕÎ0óUDôêرI,**îê+.**á*³*µ<î6/ì²Þ;-FZ¿-,¶;*R¹´êÐû0žY»í¼óèÓÙDùW¹A»P
+ûø?**à+*ðøý¸*¹ûý³à=+µ6î,/Ìê*:¼°0ÜDóåºéøëºUÂL<¹U0¹á¯=*ÕÅ@ÓÉG´L»/øZÚ½KÝÀìGT¾Ä0
+6õZæÕøÜ3ûìÛ¹M»@6µYïÚøG*¾ý+*A8Z4îÞ*ìRÞÛ7Ò/Ò+æå»ï4üFTÂàØôôøã8EϹLûBíÞίå¼ó0áϱ
+*ùLLÍ7ÙòÞº9ÑôÒõËæ9I¼6Dzâ»ûܲàL*Jú**XåÞ·3¶éÓK5ì5ײN;4·ëW̺¹ý°ÅËæ7ÏìFÖZæÕ±ÑêÖY
+Y=*ÀM>³áÀEÕ´ÒõËæQÉ@WS°åÁ-ÑäÑSI0*öý**XåÞ³3öéÕQIXê×°ONØòç¹ØÙ.XôÖ3G>âÐ;=ôÚ¸ôÓ,¾
+ñäûø°ãÐ=MèùPòݵíÄÔ³òäÄÇ°æÔµ,*HÙ´éËGÉô47ëÜûù+WVåììÝê¯=øµSSÅÔÊ×òéLÙôÃ@Ïý0XEòæÓ
+Óà,·ÙÜ3û·ïàóT¸FD²çê÷8âýÀ+ºôìØYÕäºÇX<Xú÷-ØÖËÛOî°ÅËæïXÐÊøòåÎQÑéØ·í=*úµôâ·éôÊÇK
+×ɹåEÏY½üú÷ÆÁÄD@ÐQY/*B´ìÞIUW>°º,V½J5é×ÙùA¹êÂÌûàKO=Õ´ÛØÒ´»Í*î×»ùôGDéLåRéàà¹Ü3
+/¹óï¸øÕÙHSÐJ×3*æ>SÐòïôµ²,R½L5É×Ù.XôØ3;ùØàêR×Y*ZDÏ4¸ëéø÷ÔµLßH»âìØ.*ÐIÐÃûêëïÉG
+µLÜÜúóà¹ïÜÛW*RÚ¹5ÝX¼»íEÏZݼYOº0R5ØÙÙA¹ìÂÈû¸òà·éÜY*:ìàÏõü<0+2óçµ²Â<ÛøóëëçÉÛµà
+õÝ+3óÞùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂíÝ3Ýý:F³L¾÷**Þäõ9*SûÞ+74ݼÁ3ÓRÏNÃ,û÷û-J°KZ
+G·ìÊ÷À3?PÊE¿0/øû-J´LÞHµÜ²çÐYÝßÂBÐNÈBµ¼ý*Æ93>PÈ<MøóEÊRE´ú2°âÇíI*°=,PÇ°âÅ9ûø7P
+²3=PÒöû-J¼N:5C0*2ݼÁ3Ýý*Nû/Þèù1¾êã*%
+d
+551 113[1 0 0 1 0 0]sl 8 mask 0 226 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/snapshot.png b/doc/krdc/snapshot.png
new file mode 100644
index 00000000..50bec4d7
--- /dev/null
+++ b/doc/krdc/snapshot.png
Binary files differ
diff --git a/doc/krdc/snapshot_connectionspeed.eps b/doc/krdc/snapshot_connectionspeed.eps
new file mode 100644
index 00000000..a8d6d34e
--- /dev/null
+++ b/doc/krdc/snapshot_connectionspeed.eps
@@ -0,0 +1,251 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 465 204
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 17:50:37 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 616]ST
+B P1
+NB
+W BC
+/mask 6018 string uc
+î½*¾î1:M*¾IÑ/õ0*ÞQS1îßWG*îSè+6¶ö0SØßç1âøFQI*üNøFQI*HNøFQI¾8ú1º/¸*-@K1>Þ½¾UÎ-
+*Z7¸ÖÁF2*ýܽGºýÕùüE÷ûµðùíâõÝÇí½=ÝýP¼ýËúüE÷+/5%%%
+d
+/sl 47430 string uc
+î½¼**û½=*NBWùWÀõâ·±*Ëð·¸I÷ã¾åÄûÄ*Þð4Ä»üàOAËV<ö¾ÝüP*α±õíÍIý7õ½HUJ×á´ûÎ**ýÀíY
+ýü?ÄìCçÃ0æûâA-HEô¯*BÙòùÅÌäR²TÓUãÐOøãÂ8ÌGÝIõÕÕɳ=<Z·/áHI01öÝøF,9öé.*Þ¾õQ¼³0*î
+TÝÌÛùÖòëÚ3ïèKÑ:ÀÝï,Bõüü³ÙäJWùZ+CE¹>8ʼñ·<¹ê6N3çôݽ@è6*ÞáèÔÊ×îØLSÐÂ:ÛPK.U¸+θ
+ñµÖNEÞìû¯M㸽ûŽßõíýô/æÇæVY**AHÜûúô×1Iï*ãÈ?ñüÞÉP+*̳´âÄï°/È<KÒõ´8MÖ86ÞÚëùýI¸
+QµJPéT̾5ð¾¿á;ÆÓ4̼*ÈëÝ»æÒµºóôÙ³øå**VåÉ,Rôúüû=Ôå3ïT9*¾õ»ÛG¼÷êÏIQåSí»;,RóõüÁ
+ÅP+*ÜÑóÛ»åLëC<ûÑäô33:îýAÅ,æÔü/9ûý½Þ¿úýID¾×à8½Ýýûôé14.4+84¾Þ?úÞ+èAãúÝ1·*ßýå14
+ÎF4<36Ú½-QHýýýùÄ8ËèÁíµí÷æÏêñ=üçãÜÁ+9UÚ¼ýúÅ8ÓÉÜõAýÂKV±úöEIƾ5X¶R/SÕ.óµØ²<íðåç
+ÕÕýܺöì×¹ôçÍEµüºà>¾·õõõºY*4ëI5³+¾ÝËëFúûößUÅåÉ-ÅÛô+3JÒðù±/D/*öÍÁûÐÛTÒã¿@ìÖ×CY
+ƾñû9ÙGÎJAéõÅ*üý½ùIÎýû½JJÕØÍý±@ÁNÊãNñ--,12îZ*æýýýXF¾,Dâíýô,å,Jæ=FZüý5Èö.B.RÎ
+µV:Õ-FºÁ5Í:Åñ?½¶ìýKTúí>RâæUÝýØMÜÀ+ÕøÜø·ÊÖ7,TåýAõ½ú¿/-èõ½×@ýùV4¶ß/1Ó:±*Þìûçøº
+Dŵ÷éçÓÉíIýûùÜܺôåÉ=½W²à>ZÛùõÝA¾X¾A»**ܵµòîïíYíG<AÌú´ÛAPÞôõ1è±8*îX?M?T°Þ¹êRÖ
+Ö9Y¾-WýýEÍH×µäôüýíýJÍKNì=õý<5ïɷѶVµ¼ZJÎ2*=ÉåYHð*+Êé»ýü÷M.âýýó1ñ-0ò½úÈ,,Fñ
+ûF½È¼½I¶Jضî¾,Ì×JV¾ñ7½ý°ÍH½X´î÷õÅýN28¾à¿OÂEF·Äø°+¶Õ0àñýY.ï½èùºL¹EýéYóÞX40Fô
+ýóPÃÚCÏQÓýýÖýë;Ó5îðý±Kú½·Þؾ5X¶Q/C-*Èì9BPеÜ/*6*Úø@YÚú÷óõùéÍíïÕý̺÷ïÃSPÊ,X´ý
+üÄáóí½B2W*îQ/-µüû»ûö÷ºóÜÁ9Aºò+4ÎüûµP,*X±òÌ<1è5õ6ïTÉï33¶èýÇ´Úø½ßZ+¸Ò¶ò¾KN¶±ûý
+ÐìÛ3ÁKÔ:*1òöýàÒèü9éµìVýÝEOí6Ã52J¾8ú¾,,ø÷½àÁ,,Ú½õÂHý94˽×éðîÞ7/*ýÕVÞïÌú½¼ÚËËý
+áäÚDï¹òL/X¾à/P*ÉQÌÏîîâJÀDFýýĺüYÝèPÚCôýîÀìîÂ-ØSüýËöÛµóEµDôÝÚýÈÇÈ/BüýÄPü9Ié¿;
+ÚîÀ±TÆ·*:ëüÙOøºðWó·,*¾MíаÅ-IXHüZD´ýGöíáã°ºÄ,,*¾Ñ>:**Ê>2**ìξÁí½üÕú÷ÕüV*îGO=
+Aü»»úõ»»õß·9Åú°åL+èÝIäN*ÞQ´ô·ÎH·çü;Ü¿AH;+íøIø³Ëë=ðQ¯üýÍëý»KN2±ýýÁJÜ3Á×C*>2Þá
+ýYÓÊæýäá¯ÊôýÉ0ùðÞ·¿5IÎýûÑYêçÔ¾*IPæ<ø÷1ß5YÊAEöM+åVÞA/ùýÝ;ÊñýMUÝLÈêõL/X¾à/´Ú·
+ÄÀÁE-ïB¯HýÍYíýù·;ÊKßý9æç;̶ÊûýMÚ÷ý³Â6ñçøóýáËÕV¾ùý9Òð½üÅè,̶ßC?â6*B·½Û6³H÷@+*
+JºÉ**îæÙºåï×·¹Yíü»µÝܹì×ÅõÊö4/PüXÐJÂ*Ú9Í-*ìÎ*õìÝÝÓøó·H**ðæ¹ö5Z6°NÕ?ØV>ZùùÝVC
+3*¶ÕQïËÁ3·êÁ¾ï;3¶ôýñ¿IúæâÈKH<ºïJ*RCÀ8RÜç¿+¿K*íýI;ÄAýíåÂUÀëûýýö²9ÚÞ¿9JJ¿<3ù¶:
+¾4Ôê½DYò±DF¶N+åV*LHÎ+B²üïL/X¾à/PÆüýYFM»àÓ¿¶ÒáééûýÚÎÌÖûý̾îÛÀ+Õý½<»ýóÑTÇÇýQô÷
+½è;Å/ÞûJMøIÜÎÏ*æ÷Â5ÌSKä*ZËùÅÃÖ´?Üã¼±IÝüJ7æ¼ñWÜ;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1Z@=4*Ì+¸
+>¼úÊ1*Þü/ý»ùÃåKëÞÁýáÖR¾øõAC5*¶ÌÊÓHPú:áÃÕNÞ9ýåûýÛZ<O0÷2×ü-+ÊNéýKHø¿+¿K*9ý½ÔÆH
+ýEAâOÈêåXïýññÓ5F÷¿E2JíóI4Î1Àä,XSÞC5Ê+Ööý¿Ã6¯¶Ï,Jð+RÚB.3ýýñڿÃ=áî-GÃù9@?óý3R
+JëÌß¾GýYÇâæEA·ý½L8ýéµ³V¾ÁýÍóñ½äÄL,*äEX¶P/C-*Dìý´ßÇQÜÃÎÁAIÚ/ÎIø@ýõÙ²éæÝÁ9íß½Å
+ìØòíý2ÜK¿=è¼Å,,ûù;¿USÞCïáµãÛ¹É1**åï0HùöL-ÃÌE¶çí4@4ÖùûU¼Ê.*ÚàÜ»ÁñÌØÔZ¿·ï73öÌü
+ýY9ÂíáÅKH,<øJ*ýÖ=ºùQ¯Ä>4:Õ8626,öÞýãT.ûÝãOüÏíÍ;¯ü9Ü2×ý½éÏ9Ú¿*óùÝêÈ?ãÔ/Úо±@B-
+ÎÀýÁ8×ðé=K*¼9¾àÃKÚ·ÄEÖ¶K5HóNÍæåÑÐúIû0²WÇÞ36öÚý9¼ûíÔÏÅTû½·TýY8²ò9IÈûÍðìýü¾ÓÎ*
+Çûý+AFÒÀÊ+*Mõ½ÈZâÏÓø@ÞHN0ÜÝø@A1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿=ιÅ,ðºù;¿USÞ@å͵?»øÇ1*Jé
+Û7H¹µò+.êG¸íàí,5ÖRÞÊõYȯ<*îÉß-ÌêÓìÝ=/ÛÂÅPÞÀýÍDSBJÆÅZV¹2ÞCÀNùúüûñ¹-±Á-/,Á*Ü?¸
+üíË0ÐÄϱ½íIüéÛBïOôý1ºñ-Å:JÏ0åY½ù¸³+Õ>îÈ/ä*¶áÓá÷íAýìÃ:Ú*Ã=Á¶ð6ÕZ¯,¾CèõI½ô²ÅXZ
+JOßÃ/ö×ëóÉäâ5MÍT¸üùÏKÓT´Î²áÑý42ÛξMX¶P/C-*øìÝÐÞÅÅÜÃؽ1ýêÙº×îÔ¯IÝÌ»¹å¼û·êÔÁAù
+A=4FJÜM>Þ=ݽµãÚ·Æ1*Þý;.ܹôîç+ööíÏ59ÜÎÔ½Ã,´·½à3ñ**IæÛúôÜEÁN14ÜU+</RÖ¯*..RàÂí0
++ºà¾Ü<3D.3Óà¿,¿K*Â@Ç:Oèà,F=L¿4ÛðAïDJ²Zîà18K*°áNæ:P1@îÈ,X×J»*TáNÆ:NQÀî,>Ì6àDZ
+ÚÀÃ3*ɶR:LÏ.ABîÀ<è,ê×MÁZ/æR½Ñ.;ÐÃî+EÖ±Ä6KUM¶â/åÒ:í*æÄ÷ùÉ5Öï×µÃÎIø@É/FµëQBì¶-
+ÍÃYX>*ê1çíÎ*ÁÒXÛ?ïàEÜ**Aä>¶íÛDZ9ÍßUQܸÑZ,é.Zèüç=LÊ,*öKÖòMÁVÕZ8IöHZJãô1ÛWJÄÕÜ
+O**¶ôWÔ3öîÜÍêË39ÜÇÜÒR:ì8ÜÝÈ,ìöú·UÜ»Uº+*8Y>ÏÝÁ9Í8HÑÍì²ÒMé2HJ+-ÜÝêÀ·*¾+Fö8ßN¸î
+»â*IÚÜIå¶+=@WGý@I.F¶ïãÏQÕìÕ½I½»·ðÁìãÊ,T϶ïÔ°å¼ÀÅìÎÙNí¾L@üC±WÅ1;Wõ6HJ+NÙIûÅ+I
+?*ZßûI1UQîßFÜãó*1Hú¶ñä͹òéÝÉíÜÛ̸CѾLòëÙ?éÔíöÏ1F149D°æÚ¿¼Bó41î34JèûÍQ.öO+*½ë
+Ýýݲ÷.ÜÎÓéí<ÒâF-*ï±Xä>Þ/±å´OØò¿Q*Þ/ü±õ6ATÎRKXåêû=ö./ÒLîûÏÑòõI+¾-FÚ½½Çßâó¹°Uå
+ÝÃèçèò2*ÂÔ¸SÑ*ÔXëȱåÕ,çýë±Q8F·ôô¿½ÕP¾CðìÝIñÆúü<*¶¿RÓõíY°A´*í?XGü@èµìÙM*:ñøOË
+,2D²UÈ?Q/ÉÜY¯úûù±ÅêBºûÃ*¾ôL¼G2ÃGáñ³WÛ»û@öýÃ*¶÷°UÄ7å³É¶íààâù½P*î˳ä<Ä7ÛºùôõýA
+6*9I´OÖîâÊÞóßMåîRм÷4ܸÝ:*öÔD?@-ÔêضàÌÜ»¼8ñ=VÖ?ݼÙO0*Ì7E³åÈ35ô5öøÀ*Þ½û,@êB±ç
+Ô+´×¯éDÂJúøGÎ-êý²*ºýÎùüÁüû1ûù9øõIòí½æÝýνýKýüÁüû1ûù9øËÁ8*2*Æ:<À3°ÊÀµ2¸O²3Hë½
+êÚÒJõ2ç8JG*21¶â*ARî¾+0+è0ì/ß9/Û-NUO¹ã*ARîÄ+X¯Þ//úÓÎÉÀ+ÌËì?-ºÆÎ-ÒX*J°ÎE*¶Á.õK
+õâ<¼Ø-¾ý3¿8ú½**Ç¿ìÏ7N+¸A>ïÑCJXι²+óõ=õF/8ÁìÁ=¾VKQÖ-2*¶ï*Ñ*1Ûôî0*¾U=¶°*Í=òüU
+/ºýÎùüÁüû1ûù9øõIòí½æÝýÎý¹Î±íéáÕýW**ßYý»IÀ̹1ͼ»ýõÝIAâÁý0*îÜ·ýäû¹¶ôì¹;òºøîß½I
+µíݼTí¼ûí**8H·êÓ±åÌëEJíÂÎñÚYé¼GÙºüûçüû÷½-*öùñÅÍ0ë¸ôéò¸÷óW2¸üCôéÎOÁ1*¹í1*ïåýº
+/ÞG*öüùßéäêC´ê²×óñW2¸ÌöóæÇIY1*¹í-*ëÝýº/ÞO*¶øßAQ8ûøîà:º5ÀÌèÃÛ·¯äçEýñ6¾ûùÃÕ*Ú-
+*ÍýüúîÇ5µÄËòðëÛ/KQáÐÔF¶ððÛIò.Iüë?*õ*ÞíÑAIúïÞÈSÑÖ·³µKæSÑÏ5õ³ê¹Ëó¿ÚH¿¾M<¶°Ø+*Å
+YòÞ5Ìà**¶É2å4î<9ìÑLäí¼X:3Ã+ÇAY¸/ÞG*¶øíÛõXÚµïæàòë7Àì:TÀÔ2P¶I/S÷¿è.»ÖXàÙQ½XÉéC
+0Ò/FXÐÚÎôàô8Zîï=Nï±·ÁXèÂJîßYX+B*BäT-îïÙ±ÝËÖìßοä7ÀX³VÀÏÉL3Ðè-/÷·ZÚÀ±Ý7øöÕȾE
+>½ï*QºîJê;ÞRÞÏ3ÜSÀ¹Î¹1.AÂõÂõ1+XÂíBÞÃ/FN¿=6·°<QßîÊNÌâN@+Z*صµÚ,î:*Ú¸ò×/á7úCâ
+/Àì;ÄG¾¸*:8îÆ+XÎÞ+-6,Ò7Ú5ÀUA¶é0A0*9¸8ú´îU기VÃL³*A*òÄ+XVß;-ÚS¾±U¶0¿AÈóT/XêßÓ
+²ÚÛÃM¹¶à*õØ:ÁÀÚúÂV,¾ÐQ1Ú,î:*ú÷ðÖ-Ý7ê÷ß/0û7ºG;×°×E0¶¿,µ6î4-XRÞß:Ú³Á;**¶J1VÝÖì
+;¾éX¸ÀUåS:SæGKÀÁB¶?1åùöZ4<Z¾Ñƶó3Ç.¾+JÌÚì4¾-*Rø31Jà¿ÙHÎ-*µDòµõô°-ÌËì?-F¸¿.´Z
+0*î7æ¿èÅü=7æÛúÕUû¹æ³õJõ¾3XÊJ<ιÑ3Q´îÚHÌCÞSBâ6¸0E¶î²U*8*J·LF*ûôÛø¾8ÞÙïÎI²¾+¾6
+Kõ..8×ßÇ*æµÌ+Ù/Êø;Iã=ò?°µ½ó0-XÁìCTÚ4ÀùKºØ03¿ìS;NGòâÙµÕ?*1*ÎT>8Þ¹õP7ÖWÞÌݳ¿Îå
+*µ1KA>î6ò8¸.F5Î=½QIøÍMM¹RWQLõò0Ð/Û¸ÁEϸá2Õ*ô¶éìQXHÔºô3×.*ëÜ7³/Þ*J·ì6*ðÜWùòÝ+
+¸îê,Kõ*ÝìQì·4N/¸õÂîN**Ö*GïUø¯PIáö5Ü4*¶³-ñ+12õÀäXOßÃ@ÚÏ×±2¶á.µÂïàøXCÞ¿/FO¿ÉX¹
+30ÅPîê*ð:*-:¹´Ï?*/*AHQ±éÌ7Ù²Ý+¸*ê0KA2´6Ô8Z4îÎé8ì**¶³,åJõä,,2XXà2¾äøVöWÝÏÍíQH
+9¸½N3èKìÕÖ¿Aâó6î±/ÞÏÔ@BîéÏýÜ?ú·Ô70ûÈ>î,òÌ-¾È<ÛÖXÞÎͱRèö¸ó/çæßÈIAÑÝÂ8*Í?*ÅLõ*
+,²²Þ¸5É@ÓÅJÕFü¾ð<*GÓÒ¸°Ý¿KÝãÍEÝ×¾Ië·ë°úÆù5¶1¸Aâà±á@»öíâÓ?óÑÎÝÆÚ.JQ5ËôVÖZæËïY
+¹ýH¸ûØå4¾¹*îÖ±áPûB¯åÒ+ϼ,Ûº¶JMCHGC¯åÛ;èUý0ÈGEæ4¾Á*îÚ·Ýܺ÷òáÇD;òúÙÒÒMUä6E¾Ü9*
+ÈGÙæ4¾ÁÍñÝ¿é0G¸³ÓÖJìH.GŹǰèÑIÁHËXéäÛó+*ÀÔ*F.*ýËG·ìØG±°RôðìÀÎñêA8TD·³éÓO³éí+
+*º´íÜ»ÅäØæCèØÖöH.GâÜÔBETÜ/*ZDÏ4¸ëéø÷P.GÜÜúóà¹ïÜû>*JïìWú¸÷óË2¸Èû¸òà·éü@*¾¶5ÅH
+çåó8æûNîöüKõû1ûù9øõIòí½æÝ?æ3ðÛÍ+*:ýÊKÅL=½ü2*.÷æÍCòêZïå1I+*¹Üúîð½CòÎêïåMÓ¸+*ô
+º·×Ò9Ò*:X*¶Ø4AJîô/Ðù¾JÁ6ì:³QFúÀ¹7ÆÑ<ìMK@ÖÖ/õڷ»¾LÐÎÁKÇIÍ/÷/*ÖñãÁ°¼Ï:î½+ÞçÐF
+*¿50Æ·¶,7ìÝ¿:äH±QFòŹ7ƱPìMK@ÖÆÚ,ÞýáWØÄæO4¸æ;÷ñãÌI+*Kìï7æÛ¶¾Öø5¯YE·ß*QJîNñ9Q
+K/ÞFÞ/665>2ÚáÀ=è¶P1µÂîN9ì¯áÓÀÚ¶Â=2¼A=Õ,ïÚRìZé׺ÚÐÂ5ê·ã:åPð2Y8Ðå;YFBÁÑWØ0;P4¸
+*îGæ+¾QKQ2±IÕ3ÃX0¶Ï+ÏÍ1JÎÂîî0X³ÞNPRß3D62FâÀ5¼B?¶O1A6îV9ìZá×ÇÚ*,öÝÒð°PXîÞÃáÚ?
+4Jú.ò8äá7OFæ¾M¶·<Åöð²X8?Þ?úF÷¿¶¾O¿8
+d
+465 102[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6018 string uc
+*Þ8**ü»ý¸öüYõûµðùíâõÝÇí½=ÝýP¼ýËúüE÷ûµðùíâYû¸
+d
+/sl 47430 string uc
+äÕí´ê9+¾ÎøýÄ,:ÛïåÉG½,*²FGä1T*2A*Â-Æýýá*.¹ôK*ß*ADî¾*è/Ä4ÌìÞ²ßÇEFOÁÁ=¶ã1³0A´îL
+:ìOßÃ@F2ÁÁȶá2õVîL+8òÞWÏÚSÁéȶÑ/Õ2ï22XBJ4½¶Ð-õÙò4¯ÐľU¯Ñê×µñéÕÓFJQÎ3½/¸LFØ=Î
+-ý5*à+ä,8*íCÚÒõÛJËÑE¶î11Dîìç8@ßÓ>ÂKFÂÂU¿¶¿9õJõ²Õ8Bâ+7ÚóÄ=MÇ,IÌUàÃ@ÚDÉE<2Ï·Z7
+Åýîà/XQâ/ÞêAò.ÕîÕÀXºñ¸-¸1áÀÖýýý<É0*XëíÓ7Ú÷-Jú./ìÎK4Î=êý1,ÛÀÂÑU¸Á7µ±îPTX2à;5Â
+¼Ú×ÂѼ·Í6ÅÙî0;XÆÞËRʳFüÁ=A¶D;µ*ñäTÔ3¸<4öUKAØMã*¸A6îPT8òä;;FÊÀ5½29¸ßÔC¿ì?*Û-¿
+US¶¿7?0ÅJïVß80á6êFV,µE¶¶9-QSî,;8ßò×íFÑÆ°¾±/¶ÜOù/G±Òù÷¸µJQRE¸,¸/*µ¶±H*Ö9Í0-Úö
+ÇȾ=U¶´HµD:@ZÂî.7Ä9Ì°ß3ðFÞÂ5Ѷá*µÆóN.Ì.ðG:FS¿ÉC·O6ÅúðÂRXìëÇÈÚϹW¶<:O°ÃHµôñÆ
+LÌÂâÇ,>ÛFIÇÛÀÌù¿»EõíÙ³éäÚ·ñ¼ÛD³IAJQÖýY*21B5¶î+ï*¯*åJîæ4ÌÎÞÛ0ÚÖÎ=²B¼¶È/Á.1.ï2
+<8CÞ2ßÓ2F÷Î5ζ,.QTî°PX3á;ÝF3À=C¶Ç@µ>ïX58ÇÞ¿æFÀÀ=Ù·O@Õ<ð²ÉÌBîLKåéÆPËØóéׯ-êK¸
+NÙôD1JAL×.Z1ý¶Î21FEÑàâçÔéöÎ7ßßS¿NVìÙ*øVKQø··0*ÞèÃÎÅR@+VLJìòQXÄÇÑ7ùäñ<Ù˺øZ¿Î
+±êS=Ûö2*.SèÉBÅ>*@Aáò/ÒOGDEÎɳFQÔÎMãS±¼.*À¸÷WÎáEÑÁVKõßPÞù1ÛõIòí½æÝýνýKýüÁüû1
+ûù9øõIòí½æÝýÎYGòHY*N.¸îýõ/¾ºJõ=UÞù1ÛõIòí½æÝýνýKý¼JQÞŽ0**ü±0GÀÛÝýM*:ú¯½äαÃ
+õùYú.*ʽýÃÉýãÎÁ³÷ùÍÓé/*Býüéмß,*J*ÞBJ<È2D¶Ï-µÇîÆ0<<Âá´¶Õ2Qñ:Á¶F1ÄNçϵKÅÅðõYV
+¾*Þ-*²¾*üû9ãú4ÇìÛV@ιX-õÌ:ý*F¼Á6àï±¾8ÀâíýÙ*±·Á*ÐIâ±ÎUB¸îâóóQâüP.XòJ7Þ7KÓ5ÚB¿
+MG¶¯0ÅZïX.ìNßǵF3Å5AÆQFì7ã¿5Úξü1G2BJµBÕRQö1º+¸QÚòàï8×èÇ+FZ¿EG¶¯.QWîÈ0XNÞÏ<Ú
+4¿á7¶-,Açî¼FX¹á/7²½æ׸ùû½UH9ÍMüõÕ¹,¸A6óRÇ8IèO5F¯ÁMD¶7¸ÕÎî°.ìOßëYâB8µXî5¾ÌN¹I
+Àü.òÂü²ÅJõØà<+Û-ü¹ß,ÅPî@ÍÍîßÏ7ÚÍÀáÝƯFÌÚÞA*Gçã¼.ûºìÂÌÆíó,ò4ÂXÁì¿*F;Àñ>¼¶æS¾ì
+ïTå7¸CVàKCò¼æCSóû½UÊE.:æOã-¸µ8î´18à»<ιàè1M:»æÛ5Å>²ÎûKå8¿õ9îºìÂ8÷åûæ/æñ3**Ä
++Ì6J³Þ71F2À¹È¸à,AXî.7X:ß/IFæ¾5ݶ߷õÐî>ïí:N@ﶸ>1GÐõKåû>ÍXûúóæÕ±ôêÓ=íËÙÍ=ÎÙL-
+FÄÔ@ÕÕ?ÁÌÙÇËEõíâÍAåãϱå8G¸E1Jµ¼üàæµÄ·1Oï¶.H=æǼYÌ+*ÚØåÎI@àÛÏï±*GáÌDçÃÅ*JÆÐüÆ
+¼ÓGJÅÈÑäú2*.óðÍCòHð/ºýÎùüÁüû1ûù9øõIòí½æµä3º×RºÌôêϱ=*Þ¹ôêï.ÍæãÝ;ò¾äÎÖ:¾ë¾ì2*
+æú5K1âÛÆÖ7¿Ìê;õÆ@êùøöóí/*2üøíý7æ+ȯZ.WêéÉ¿Õ:*¸G@Øæ.J÷Â@Ú¹öòéù½ýû·KHú4*Þ»ñüËù
+õîêÛýDÎ-ÆMÞ¾ãÀÕGÒé/-FÖAZCH+4IÎ/ÍHºöíåëùÕÍèÙY½/*îîÇáXG¹ôêÜ+øQ-¸PÑ9Ié8Þé8èóÀ+X
+êÉ·ñáÅä-ØîÚYé¼GÙºüûçüû÷Õ**üüúïÑKÙÌûDÛX¼ûÉ+GíÄ2ÏÕÕ±ÙÄËâñµ½=*îóËÑüʸôéZ/;LAÒãG
+á´û×òõùùÉüúõ**Üý¼öÙCµ¼7ESPëúÉ+G9Ã3ÑàÖSµ8ËÚåݽ?*îð¹±äÚDôàÊ.ÑR1²ß-Ý07×ñóôóYá>Ú
+¼øßAM0û¸Z;6úÉ+GQÃ4ÓÜÖGEìVÒѽôR*J*JÀï¸â.8ýúîÈ7¹ÄÛµÂà+WöØÔA9Üúúºù,Ýü±*Bض1,ÅCî
+æ1F¼ùñÁMHº×³TUõôA-¸Ì5AXZSQ06×:Aý²ç<6ÛMOîñã1½6¶°èZM>8æÀQ?ûÖíáÞßÅAóçÝ,**î0+<C¾
+MHîóáÁýëÖíáÐSTÚØÝ6æ·±äÊ+7»öXà²Eùøµ***FÈ**Z@**Â-F°¾5>¶Þ*A@Z0ÞÛ±ÝËöìßÏ¿ÄRFÎ/Ù3
+øZÛï4Ò»ºöÅ***FÈ**Z@**Â-Fè¾E>¶,*<¼ùJ/Ò¹Bò˵ý6æ÷¶ïàÃúV¶ìßN¹øöµ***FÁ¾B**Z0îÆ+XÎ
+Þ;-F:*ÝHÛôß·/½8CLï¾@úáå¿ï4Ì7Ûø±ì**ÌKJ6**B-¶Ò+Å6îÀ*Ú¸ò×/á7ú÷áQ-¸¼íÜ»1G1¼EBßPÜ
+û7***å::0:1-FÙ,íüF·ê¿í0ô68ÄRFÎ/R¶XßX=½Hû?***ACZHîÀ+XòÞÏ0Ê+ÎñâY9T¾ÀÍPæëßÁ1÷7º
+ÏèÝÍÝ+**î6,à*XDÞ*Þ7<Ú×,¹8úõè±Y¾YÂà+WF¶/ðûEöU***8OJ*Þ¿1ÚZ¾5=¶Ò+±*ðÙõ-Òö²ÀìOÈ>
+3̱޴+5èÛYÕé?µGäÕÑKñÛ¹Ñ0ê@ZåîL>8æ,¹î4ÞùC***å::0îÈ+8óÞ7<.+FJ/2õç¾Ì÷òçÑ×¾AñU¸
+ôìÅ***ÚJ¾·¾ê6î¾*ÊôêÐûÄ»+±;41²+ÎÃUæÝѳíä»ù?***A2Z8:7-F:*â»Ùôç½-?*ØQ-¸Ü³éÔÛH1¼
+éAüWù?***+*A8Z4îÞ*ìRÞÛ7Ò/Ê+¶êÕMý»EöñX;41²+¼?>Dê׳å¼0*M5A8*°Í*Â.Úæ¾E0R4R-Jر
+áXO.ÝH7æ÷Ü»ùÈ<ËÖXÞ,Dôã*¾Ä0VA¯çÐL>8æ,Ü,»Bìݹ·/77BìßÎY»DÎñÇ=QÄÒØðÝ¿ÍáéZ¸2JÄ÷ð×
+¯ÍP6ÖðèÆL>8æÀ·LûBí¶/Æû¶>ðFÃJ/?GBíáÒG»DÎñÓSÑÜ·²áÁç±éÕµ,*¼Ù´éÇ;Åìº3ÏPÛø+W¶MÒE
+1@6øóê<EôGÊL¾ÌдV¶ñèÄH7æ÷ðâÇQùÈøñâÂ÷òêE+*õû¹ôåÎUÁËÄÆ=Yü¾@B>âÐ;=ôÚøHRß·ñ¼»×°ç
+Ç;»DÎñïëÙí¹Ç°èÑç±æÚµ,*üE¹ðÚ³¹äÒA×µIü+WöµSSÅÔÊ×òéLÙôOºÌý0XEòæÓÓà<·³½CÎñîûùõYð
+ÎÐÇO³éU+*ôW»úæV5Ç6Í*î9æ,Þ»*òø³ë¹SL°¶AõA-¸ÀÛû2B?úW9?TÐê.*Ò4?Tøº=FD+êIÏ/ÖÖÎ2T
+íà4W´íÜ»Å<رW55ý±JQÖá9-Ù@+*úŲ¿è4±=ʵ=,Þ²L?òØÕóñÍ0òèæAO*µ¼H.¾ÕA½ÛöXÚµµ,*4õìà
+ÕÉY½Ò4îýU+Gâ8¼»úúõÁ·ÄIüøîíã,XÅXFµë¹+*àWFø±Í-Òü4æóEÔÆýÒH´æÃå+ôíDòäñÞýY7¸ó·¾èÑ
+0Û-ÍùÒKõàñ*ùA7¸QH1üýæιñ·¾èÁ0ÛMÍùÎKõèñ*ùõ6¸åH1üI*G¾÷**ÞäÅ6*12ÕÀXÃ4OT>æÍYC*VQ
+ÌE±Ü*GÃ3ñPÎK¿üã*¾HµÜ²ç°/ÛÃ>Ç<äËûýýñ.ÁÇÐâÆ?òدáÅ:MÌúÉ+*SÐ6÷ïá4¸ÉB¯LÃÝC*âLÄ+*?
+ò9¹*¾ÓÝ7*%%%
+d
+465 102[1 0 0 1 0 0]sl 8 mask 0 102 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/snapshot_connectionspeed.png b/doc/krdc/snapshot_connectionspeed.png
new file mode 100644
index 00000000..2f7390fd
--- /dev/null
+++ b/doc/krdc/snapshot_connectionspeed.png
Binary files differ
diff --git a/doc/krdc/snapshot_nobrowse.eps b/doc/krdc/snapshot_nobrowse.eps
new file mode 100644
index 00000000..37a7afb9
--- /dev/null
+++ b/doc/krdc/snapshot_nobrowse.eps
@@ -0,0 +1,294 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 551 189
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sat Sep 27 14:33:09 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 631]ST
+B P1
+NB
+W BC
+/mask 6555 string uc
+î½*¾î1:?*¾+á.37*¾Í5;*¾â2+¾QÄ2îQL¿Æ6:<ÙÌâH>ÙóâôK·++úÇëÁð,,÷+ö´2+HV1;â+¾ÙK.BZI
+ÂÖËK2*+ܽLºýQúüÍöûIïù½àõýÂíý3Ýý=¼ýQúüÍöûI¯µÍ2%%%
+d
+/sl 52345 string uc
+î½¼**û½0*NÖPGÝ.Y-3å*ÜÅ<èûâA-HEôÉ**ÉUBô÷Y*4ÑDZ,9öéÍ+*¸´ôüû÷ñÍäÊDò0°OLéI¹êÔÍ-*
+îYöûóííêQº0ÃÏ+¸I÷ã¾åÄ»6*ä@ûüÏñ<=ËTóTײÇôú=à×ñùñüø¸ÊøÒ.VXßÖåÅ6,üÕÈÆÞ-üD+*BîûÇ
+YË0*ÚèÕ±õH@»EµLÚ¸æºÌι·*ñìíÝ´é6êÒMWJää8E.Ñí>Ïâ8E-CMØûõÍÃD7*öÖØ?±@ÚÔRÈ>;NÕ÷FK
+ùð*ôì9ETÔCÞSÉØ*ü/ݽýÙAÊRL±»+*ÚööôñÙåHúÂ->UÔÊù-YD9*¶Ô×;EÄÉ?RÈ.ÏÜÙ3KKüG:ýýµÑRJ
+×ûÙ1ËøæÈøN,ì@ÌÖPý6*¾óºS*ãÌíݽàTL;Xó+*öÜÕÙáAÍGøñòFóÛAL*Óüì½öæº*Þ¹ØÜÙõVÒGMнèê
+ÜC¿ØQ÷ܵHµ:¶/Ýñ¾3-TÝEêÑOJýý-¸Ë/Å<¾×à8½Ýýûô=<ȹñ¶;45D*-ÝÝçÐQÐÒQ8*øHXF½Ì¼»ûõãÁ
+õÌÛ±ÍüÚ·ñå=P+ʹ½½åùÀåæü×R5*úëêäßíÅ9ܸºÜF¶ê³9Æ*L?ÝIUÎ÷*¾ýFêAëÓLW8²ïÄËË÷â.ÀàZÏ
+ÁýÙAÊä7æýýý8¸¾,èÒï»ùMí@+*¸Þ*õÖî0,úTù÷½äÀNÊOÆ>ÏÏÚE1*ÒL¯K-¾ÛùÑó÷òÃ>³3-FYF½GÛ¸÷
+ûñݽíéÉåüº·ðÕÃPÊ,ܹýüùüõí9F>»**ܵµòîïíYíG<AÌú´ÛAPÞôõ1è±L*îX?M?T°Þ¹êRÖÖ9ÍÆJîÌ
+ù.ø½»OH½È´õõ-QDHÞýÌÕí·î÷ø=±ÍH=ÉåYHð;,øýÝå5H¶L02±êýÁIÜB+Êé»ýü÷É/æSúÐ55XÞKƾãó
+̽ö-³îÚýÉVÂYäË8¿ÛMFZºáíÝúæ+X²ÀüÚ6HýüöÉ;À*XSà¿<êK*Îòü¾¯<»;OWà*Ѷ*Þ*ÞÓà÷àãÍY½Ý
+Ýüú8½ûõâÉ9YÊ°à>î°ùõµFXýüÁ¿æ,*½TB¶ñííéÅÍåUõGú¶ßM9Î*óí½±P*îDMõ>·Îܶå2ÕØ6ÍÆJÊÍý
+òúIêLEIýõKÏ÷ÝéðýýYÛáýü+µú½ÜF¼ú½¯T³ýÅWM*¾XöÞ/CæËEÇü1íéÃ3æøýÛSøäæÖ¾+õÜÇØZ*¯÷ý7
+FÞà5½üýÙèÞèýAóìFÞÓTJÚ=ýÍ9õä+X²,µüÝCÕPú8Ë·Â+ÚëÀG**Ðë½ÙNÍ1ÎÒO4Z4ÕÈR**¶Ò¶Tô¶îáã
+á9úáôÝ-ý»ÚVÄåRJ**¶È,,*¾Ç,+*Þ74¶öûåýÈIýèÍ/*ÜѲðàÝÕÅýÜÕõHFõïÆYôV>JûùáÊ3*úâäH1è
+=AëIàEöà=<B´õý99T0=ûúÁBÜöäÉûýYç6¾IùýÝÏöýU?±LýE9AÄJ*ߺß-1â¾+ßÂSñýûÅXʾØýYDêÉ-
+Á/ÎKýýFÓ6V¶/1ÈçÔÊýß9E³Zûý9,·8î41îÐZýýE¸ñ*AD¿åýµóUNÇû0C8Tà¿<ê+*Zðü¸CèºÎQWÐ*Ѷ
+,*¾RF+*ÞÏà÷Ìá±ñôÜÜûùì¼»õÚ±9íCï?4ÊQÜN¿2+¶IÈ2*ÚK+ìÛ½½Uóéñº**âÏõîA6D:ÈXU²×RÞôõ½
+×°P*îXÍàE1=ðÖ1+áE;24áÞðMÍÄ°ãüñÃLIÑÎC2÷à*ºýýÂBÜú½IÑCÀ=H¶K0Ø°ãÐýÍ5¸¯,4´õýýáÑ7@
+Z×õýD¶ÖJøý½.1,ÐãöâS-UýÝ>Èñ*å¶ZÈïä+X²L¶ðòâÃ6FÑÂTÀG*¾³ìI@à-Qè=.2ñÝ×ÍY½ú1²ÝSFí
+2FóêéãÍQíåÉÝHE³î79HL¿-ìÉ3/*åZL4I¼ä-*ZýÀýÜûL3Ï´îßý¯V>JûùÉÊ4*FQäT9=¼ÆïàM;2øð<ý
+üíéÏâõøï;J¾*3D9¶¯*HüýV;»üµXR»Þ/9F;->Sø½ÔÉÌʾõ±Õöý÷÷üýµìúÉX¶¸î882æýàU7Æóý:ÉGì
+ïÞ×+Úæ.5ýIÖÆAÆïÒ<4HÔ:*æ¶ýÙ/QÜîÑ8¯¾SFÀ8ÜúJF¾üRïúÝíÐWMý8¼º1ýG¸éнýMö0/Xº9*¶Å,,
+ûù;¿USÞCïáµãÛ¹É1**åï0HùöL-ÃÌE¶çí4@4ÖùûU¼Ê3*ÚàÜ»ÁñÌØÔZ¿·ïÇ2.»÷Å;ÁýA÷ÏíýP´ãÈ°é
+-X6¾1üYÔLæýWXÀ9ëýÝóï/9F:-äµÕõýæI7¹¯,êõåÆBý1Y>èÞ@ýÅ÷¯Zß7FöýýCÏVûIâPÑ0Ü=ºùQ¯Ä>
+4÷?8ÚÇĸ¾5ËÎÀýÁ8×ðéDű-·Ð.¹**6ÛýSÀ;YöSÅBJ?8Z9æ-íY?ÌÉ-¼ØóñëÛ½õìÙIµ»D¶·,íÎÞ3=Ü
+M+·ÜûÆÞÓ>ZÉñåÙÈHûâ-*Îóì0ÍÛEø*,ô8۵ﵿ/V>ZäùAãB=*öãî+QôTõíÇÀXà=;2øð6C,Û9¼JÄR¯
+¶üøÙÕE9¶°*Ü?¸üíË0ÐÄ;ï³ýý39F:-üÍÝø×Ë@GºK0Êû»ýöYõà>àÞP¼IÍíZß3FÂ>JýíÇ,àÔÐßKл¼ý
+ü÷ÛËGà¿MçR/·Î3Úïèïúõɽµ6ïêJì>ß4,*»õYSîá5¼è;0¶Î¶Òû¹öå4åÉ/µ7ù÷óêÙ¹ôéÍá´Gº×½CÏÞ
++GHL+H÷û¹UäËÁº**ü9KîÛ±1Í8¾Åý8Ûöî/³ûU>ZÉù=ÌB;*ö¿ìãµõÛCN¸ÎîÓ32J¾,+FIKÄ.PâÄÇ:CÂ
+S»ÞÇ+J²Ç.SÐ@ºJÔ;46¶Þ-1â*+3Ø>6ßƾ-=ÎÊ2/TòÈÝ3-U2S;¿Á/Aî:Xå*/Ð>ëòî¾Ü<3D.3ÓEÓ¶@
+7Ó:AÀ+Ó¯<N2ÐDű-·Ñ.5:*ëB½½·+EÜîÑ8¯¾=Ã-*Ì?Ìã,ÌÙô-ùÑ**î0H5ÝÚRJÖµ½Ä,6иóÔÂ5íö,*
+ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2¯H*Þ1ßÐ97°ÛÀ3;ßÔ¸:ʶåéÆÇ1ÒêüÎÙ/âöÂ͸â+8>Ì**îVïV=îß
+»I3K=Hº=»SϾWåÜûùUSÞ7ÍIµãÙµÃA*ÞïÃ<ø¹öïçß±øçËĸ²7ß->ZÞùÉWB;*B¾-í;ÊÇ2Ôã-ßÂÙìIJ
+´¾9Xíͱھ3ÍöSÅFÞ÷à÷KÞ¿5Y8»¸ó¼ûù÷éÉ=IòXä>Î4Ã5µ?¹ô=Fô0ÛOø0>°ïÍC鸺2ç¸åî+4JØûç½
+6ÖY*J*+è;2ßÀù½8ÔÌÞÁQúÒM8*6ÜXFÕ-FöîáËE½ìÏYýX¼ùô¸èãÊ,4ϵíÔVÙ¼ááHîFRºæÆKñ0øßÒUF
+¾QR*Sõ½H;Þõ+*ú³ýüýÐã=öPÍØ.,XAÌÎ<ñ6*ÂÍ·CÑ*AÊGÙ?çÐ1H+*AöÍÛ±ÚR¿L/·G±õÍÞ=>Î4¿õÅÌ
+ÐÝÝ+*8îîüûO0=ÕíË=óÇ-FYFTQUÑM*:ØèOË,Ö¸³UÌGÝ4°ýµÍG·îâ×Ù1üÛD+äƹýüÊMðùA*Þ/LÓܽ½
+ÇÛÖ+ºåÎÒQ>î¶ïRÝ·í8+¾ÊéA³4JæÎWSÔF?Fù¼ÃïõíÍG°áîóÅ**Ø5÷ó*;ô6ÍÕ³óôóÉ4Íøñ1îðãÔ6EÌ
+éÌïÜÃÃÇõýB*ÞEéÊO6E¸÷ôã³Qû3.öö¯UÀ/Qä.X9H¸0Å<óÉÕîÓùG*¾µÓS¯CZãÔÃ=ôòóö<°¼À/ÙðéµÙ
+â*ÞׯÑôVC¯ß¼+Yí+*úÕYÞÀ7±@ëDÞ@EF³R;2ݼÅ3+ùõ9*×TßùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂí
+Ý3Ýý:ºýPêÂP²íéáÕýå**ßYý»->WUBϹ1ͼ»ýõÝIAâÁ½?*îÜ·ýäû¹¶ôìõí²GßÂH»öîÝÍÙõ¿ÂVCñU*
+¾êVýF*FºðÖUåÌGÙµ¾üRÍêùøöóY;*2üøíEÃ3GõéØYå@ÍNC·ÕïÑ**°ø´üñ*îõå9I7ØóêÕçóðéYîǹ1
+ͼ»ýõÝIAâÁí6*ÚµìÝ·ý8ÌÛùH¯Lý6ù³æÐßY·1üý4*¾Â¯ñåýº/ÞG*öüùßéäêC´ê²×óñ1ôâÑ19üù·ùü
+óÑøôéí6*FºðÖUåÌGÙµ¾ÜOÊæ3Ý@W÷±ô±DX>Ü*¾¼±LºüýüûåU*X,¾Ñ1¼×°çÕ3E0à9êÇð¼WùóêðÁ9ãü
+úE-*½½üöçÎWå¼ËXAÝÜKÊRõàìÚ¶ñòÍÁ3ßö4¾¹*î÷ïÍ9H·ïäÖçìÜÛ9êǽÄûD²çîõõAû÷Y7*ºýûî³±ô
+6E´Øêø÷0¯LGÙØðÞÇ˽7PNöïõX+¶2*ÌÜúîÆ-UX»ÔâÛûÞÌöîéÏ=½ÜüüKPìR-FûîÉ=ż»B¯2ÜKÊRÑÈ´
+êÕ¯îáÂNHë¶3ÒS8B¶F,SRKéC;ÏQݸ/ÞG*öøîÝù´êÕ¯ç>óWë1ôâñàäÊBñòñíÁú¹MLÍýüúîÇ3±ÄËðìã
+»¹P.*ð,¿Ù9ÎHM9ÜÉöô¯L·ÏçZ8ÈÅO¿/Àï*R1Â,´NIRÑ-BîýýMO°ÞÁýýÓ³N66Ò÷ùJ02ß3=ä4À*X¶ßK
+?FDÀÔ±L4I¼ñ4¾+**î.*Úºõêµ=ܹ¶²î÷Â3ûéçÇ+MPXüº¶½·*îñã1½6¶ðçY+ì5èÆÀ3ÁOR¾7IÑ+H:DS
+=ÜÔÀ=1Î=I5̹Öô¯LîÝBåÃË5YF<ÎýýMÚéïÍ.ñJË8Þ¾ýA6ÆõûåýýÕQÀÐ9ý<ÃÀ+»TäöîYÐ@/ÚîÀÁTB+
+¶Z.WåÂòû¹â?*1+Þ×UÕ»¶XßÍQH¯LIDD¯Ü÷µõ8äAQHJ¾¶EÈÞÃèU:ÆÝ,úºõë·Aäɶ²BøÂDðZLâÌG·Ç
+?ЯàÙ=ÊÂ3YUßÇ-æÃÌ-á?FùB;úYàÑ:àýýYºÌè<¿ýÞÇEÓðÇR×é,ÒÃãýýÅHÑ>J+ÛûýÝëJQÆî¾5ÌSßVÞ
+¿;²G=<óè/Õ*F.*Å캴߶-¹ÜöÂ3ÃOæMê²»¹öÕ*J+¾ýHºN4O+¾û×5âº+Ðûø3S+ë°´PòêÃMÞÌû÷²<2À
+M×á-ºS5:B<»ß¿KåÌ»÷Y7DöTHê4*Fß¿3*N8éÀ,*춿·ºK¾¿T.EËLZôM/AèJ×-R3LÇãÕîÆ3¸8ú´îé±
+ìBÏHöüýýö6TÉÂZGQìÒÈåÕF?ðÞÎîýÛà3ñ6ä°ÜV*JÂ?2õ;Ì:ß+AFÑÀ+¾-Mæ¸PNæÏñW+¶**ñQ*JF¾EI
+ü¸îÙ¿G=º°µÌÏñÞ¶/å¼HGV½/**J6¾*í:2Þ+>ÊZ»÷Y6B¶YâÓNÚü¹+¸¼Û¸4ÄEØ:Þ*WÝìº6ËBÀû¾5âÒá
+19îN+Nøñ?о¯°PòéYMñÂG¹¶AÊîìÅTÃ1¶OÐï@JOÙG1Ï/2èôëÏÔËòGß¿3*ØPéö:¿=;Z5*QíÛ4K.Û¹¯
+ÌQèÞ.ÛĵçÓ±*¼áÂÎø;=èMìäYIPß²*·íÁøG=¸/áÒÉíÔæýî20:-ûðA5R+ûýýå½ÞÃ;ÚîÀ±TB*¶Z.Ó,Û
+äÂ.F¶Ù?*1*ÎÊB8¾¼ùX»Ê2öG×Ù-à@áËÝæâ-ÐÂæÉIPãíJ¾éð7ûØQÖ¯ÁðçÀÃÀÌñ,Eæûì7æ?îWHE¸Äìë
+/Ý×7ϸÞ/@ÏÅSÃÍGÞÇ+F7çFÊB*ÞË*åÌûõê×-ѯÁ,*÷B*TÌÛç2IÃïC¹ôͯÊûGß¿3*×+ø¶îÇ2Î>ì@Õ
+L:ÅWÎ9-Â+FS-ÑË»ô1ÕÌKç·0ê@ZèÅÊRÇ=°:âSAMèLÇóEµTã=5âÅ/ø-ý3×ûý·ÍWã,,àùæK2ïÀÔX¶ßC
+?6*FBÀÔ±Løû¹ê4¾-*RP61JÜ»Ñ8V<ÌçG×Ù-à@ܽÝæâEÐ.å4,Ú³îÞY>08º¯½¿<0îÑê;RWëã,ü.ØÞ;-
+èLDîÞ¹´¶ï/¶¹ß8Íøܸ¾5,¶DзBϱåHô*Õê×=RM+åUøà*Ä3?¿òê×QQZDî.10*Þ¾R+*îÄ5ZòW<Úî7:
+:TäÛÞÂU³2/¶.*¾WBÏúGûÆ3Å*ÖµSýýUÍÑùÏE?6öEàýý1Ià½-Þü7+TK´ýýÅURSÞ7AFÑÀMMæ¸PNÛ¹±W
++B*RÏD-¾Û·ñHûµB;Z¹°µZX3ÙöôûL=9Æ<üÜWÕÐZ/+üùõ+Ú½ý仾M0:¶ÃåM¸àï@½E¼?,5×Ýß¾è.Öû,
+6Zô»1Xß¼ó*1.îLNßÚêÊR×=Á¯Eù:ATîÈ3³Äåê׹įDî.1ä*Î-éê³-¶á/ÆG¹óC»ÂB:Ú2æÜÞÁM³:͵Ú
+¸0J@ÒMúȼãÂüF½îÁ0ýýY3ZV¯,*è.4+ºåMàãÉ-ô½6.ÅGú¿¸40òèAßÉÍÇØ>îÀ58Tß*Þ¿;²G=<ëØËÔ*
+>*ÚRLÓ»ÙôêÐûÄÛ+°Qô0Ò?¯ÉëÚÍ+**2-:Ï;.,6.¶6¿·ñ0ú,õ8VU7ìÜXKîFLJ³¹â¿ùÅ°ÅÚ*WùÑKèíU
+1¶M*Z¹Ã>RÉÌ3Ûö9ËÖMâJ>·¿8ùG=¸µÎ´í0O7¹/àDù=FB+¾ÉìÛ8.ÞË2ÞÒÕ͹W¯à*ùÛZËGùÎ,ÞüL7:*
+ôá³W¹ó6êéñÞµûÄH=ÊÖ@NýýýZ*3ÒKOADîÞ*È,ú½¿?>Þ,ýIµ²>+ÁRâ50:P:ºµ.1ÓZ<îÞ2ØÌ33´ê
+KU*8*JÓÄáÄGEôéÏýÜO¿Iù¸àÂJV3ÙßÌ»¹ôM*Î*°ÝÔ6;ÄÀ*ÍG¹×NÇ=Q4ä±ÁDîX´Ê.àöê×µDX7òÂêØí
+ÀÈÏWI¼Þ/N-ÒÛõ8î,+æôêÖUN¿ÖòÊæ×É7J<¹ÂK2Æ.KìCÏEõ³K-Xâ×M=2ÖIß¿3*ÙÊ.ÂÈ¿-AL*ÇNJJãé
+3-ÙìÅÌòO/Aè*+:ÎÞèö5ÜB-¾ÕJ5ÄùµCïD-Î÷Ê2ºÔ*F¸à¸¾ZLÁUBòUQô0´?¯ÁéQtîÂ4æ¾9F1,à*ú²U
+4@Î,7EK1*A´å.ÖßÄëÃJBî.RêÖ÷C9-:ö¶Þ/Jõ/EF·¾E0¶:ÐZUTPÊë,ÞøòÓîä1YB¾À<ÛöXÞν4=Ô÷7
++ææ¿õ@@7èÇÛçSÏ3å/6øSîëæ-²²Þ·3ÅH³EPºÅ+ÏÖM*ÇGßã1ÞNÓ=ý7úÕBï¿,öêÖO9üE·óNQôPAÓÄêµ
+Zæß,Å*îÀß*¾=ÕÚ,-öêÖO5ô5·óOU±àFG÷ð-Î;çÍ15ÜU=´ÚXÞñýYÝü×,î8*àì>óáÂIÍXòâìF;èÌ/50
+û¸´Ç7ùK>VP-ÈDêZåÓ3³+=Hû*9ãöòâÂGͯT2Iý?*ÞüåÌG²/ÞK*¶ìÚYYܺøïNQôôÞÌJóÉOÖ91FÓÄÔZ
+Lý@?óæÈCíº5ZõáýYë=RôêLU*8èËý0XEòæÓÓà,·Yêë¿GCçÕ³éÜÙ<HÓÄÔZLŹǰèÑIÁHËXéäûY**.W
++¶2*üE¹ðÚ³¹äâÎêã»Õ¹¸T7WßÂÌûàKO=Õ´ÛØÒ´ûÕ*¾åôëÙõæV5GMW55ÝèGGÔÄÔZLâÜÔBETü6*ZDÏ4
+¸ëéø÷óìçO³²/=ðóãYAÜ5õë9-*4õìàÕÉYÝíGGÒÄÔZLã¼G¸ïÚ³ýC*JÚÃM½OµùÚÑÁèæ4Pú±ëïûEïùµà
+õíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂí½2ܽ=´S=Àîë¿:ýÉ3+¾ê¶:Í;FÈ*4÷ßÀ.WÏ·´:µÕVßµÐñÔ/OMÄP.
+*8*ÞË1*ÏÅñZNZUP><4IÈÄGîP0P×ÅON02÷¸O¸ÝÇÀF´áÂEîÎ>ÊBË1*òµÂ:Q=H:ÁêØ:CUNÅ2AË°9ÉÆ0
+:X4M°ãÇîµ<ö+¾HIµã>3EîÎÒÊÆ+ÂðKIô*,*ò÷²<:FÈGLJ6PRB2*:ê+Ò8FÇ¿3*å±,+6>×±QÌZWR:0
+ïÀ+2*Á3Ì6¿·ºK¾¿ZÀ50B,Îã½³æÞU÷ðÂNV+ËI=KÞÀ=Uîê+>³+,MÐAÂ-Áï/Ô<Â<ÉÍîU4P·J6å;ÏÎïð
+E82ßGÆÚ,ÂM°·<KÀG¾5-38¶*/XÃLôÞ5Òá¼ÛζúÞ·XOP0Ý·A,±.Å.ï4E:*ôÑS.¼*0AÌó,ZXFé.Þ߶*
+*CÒäK*GÉÆ7BÏã¯ÊÖÁø*/õÀîìÅT//ÆWàí2+*RG¶<-=*8ó³ñVIZå¼ËæD¼¾îÀ+2¾WàÍ6¿W*F»õZÀ50R
++¿Î´í8³ë˶NÞ4öË:æç³ÅÔÚÖ,ÔNDì,Ò²ÙEB¯ÀXáÙòL7ðZÈ5´B*¶33A¸ñìEX2ßCÆÚ,±°·L6Jæ¿¿.1
+FÆ+IP4¹Î6êZõ5´Ú¶Ì±ÇÇÞµ8ÄJ6ß;KÚà0-¾ÛιA.ðà×X*ä/µË*F61*îä>XÔäG*GÉÆÂËÖ*Z:*ZI÷Aº
+ï7¹ôPåÒ8êæ¿õÒ8FÇ¿4*ØüDñÆ+×+¸+ß/-2*àí6¿Nï@ìWÖ±½ß*±*4á±ôêIá¶æµW+ZFîÀ=U*âá1R·ô
+PÅ@Æ´ô×ÅÉìBï·ÑÚZÁ4ÂÁƶÏEõÜïàDÌ¿â?JFéÁ=ද:ß+1°Þ·äÞ8UÝ,ßù¿XAÚñV°<öËÁ+3M¶N2ÕÖß
+*¶ê,ûÇ¿F¶Ô5¾·ïVÅ°óR<°R˹*¸@;3EïC.FöAJÎð2ÌIúÚ¸¾=C2*¾ÇC+TãUÒ¾6ÆܶîÀ+Xó+ÅÜOÀ5Üê
+×ÝZ¾3*J:ÄJÐ=ÆÆ?è=8XK6JC¯ïKPP·ß0-*õ;ïîEX2K0¾Æ¾VÌ6â¿6¶è*BÝÖËâçG*ò/õFÉÊ.¼ñZ.L-8
+3àCÓ2?ñXÆáDZÛæÂ5åD+¶B8·ÒäG*GÉÆÓB;.·¿.ÅIõN0À*âÆÛ1TÞFÖäXù;-Úè-âÌÛøÊHXC*¯åHMÚ@¶
+8ÕËÄ?2¹7ÇÉ+XWß;=ÈåET-*ôNÊðÀ70*ìMàßµÚ:À6*;+×¾å10ð.4úÅZ·ÖAé?Kõ5ì·@C3úäß2Ê0Ú;±
+TK¶;λ:.QÌóL=XFé;ZGÈÂ6Tñ8¾ÌOâHÊæ?Öä8;-ÏâÏ-êŲ:NîL1ÌâJ.¾¹¾øºóÆ*T±µÉìÛ8>¶îÀ+À*
+Ì6+P»ûÚé4µ6Z,JÛ¿Ø»E>³3×À±QåÅòðÀ×±½³/µÊÆÄéØ@ì¿4EÄ=3ÑÚZÁ+¾ùƶö7AÂZîî<:*¾5Ò¶S@A
+0°FîL0B@,,×EWÎä3õFÉÊ.¼ñZ.ä2ì2àGÓƼA8Ò.¶¶ïVA*ñà×P*8ÄáÏSÚV˹ӶÑ/µêAO=H*¾=½KêÏK
+2ç±K2N2ðJ.Ä*Ä.XòÞÃ6Â*öÏI2B3BT0ô´C32?-0XBÞBßÃ;J<32R÷øÄ+P*Ò·Y5DîLH;U¶ôåųRÎ-¹ô
+ÕÔîÂ3G+/ÔıåÌÍ,¯ð³<P·ß0-*õ;ïîEX2ßÞßOJÆP0ìÉãÿ61ÚÆ+83J°ôß5Ö¹áGÕ<=¿íF6¿CÀEÀ¶Q?
+´Û9GçÍGéÃEÚ¶Ô3¾Áﶲ4AUñ:¾Ì²KèPºù+Z»¼<ÖÌ2Nê4A¯±WP·,1Å*KF,¸9/KÂ×ÙL¸øæ5ÜÈØ3Ù22ð
+öíóÛ¾.ò»ëëæ@éBP.
+d
+551 95[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6486 string uc
+*Þ´**,ºýÂöüÍöûIïù½àõýÂíý3Ýý=¼ýQúüÍöûIïù½àõý½ó3+
+d
+/sl 51794 string uc
+äÕí´êÙ:**¾HÊɼá4éäÕYUëæÂI8Xܽ=¼ýPúüËÖÕË2*êìPXÊà̲;:¯±ÐÆü;¾Å7ëMµBÏFùÐ6*ý¶â8JÛ
+¿**:0¾W=-à*+1I°¶Â3À¿,.*J¾7I9J*·*Nô+.B*1Ò¶ËAO¾æëËì¿QVÃ>PP8ÐàÇ**91*:CKòT=Ðà*öÄ
+,îå6¾±1¾¾ì-8ðϱÎñ6ÛöãT±¶ÂMýLâOÔR2ZæSQÊ:ÌBLÞÐáÌBMÜ>·Æ·3Ë8JÍW.ìOïÃ.NI**ûÇIÚ4ÃE
+PÊ<;¸Ó33¹FÙ,+.ÛV+TNÇ2ïÏ*ÄGùVôX÷*ðC¿*0È¿-@@È/û,03Ô//XûÉ2Åî.ÎÂÞI1LJÖ-I<2,îäÍÇ:
+Z+¹ôãûÕ07º?å8JS´+,MÐAFB6OZ?K>×úâ?*¾*¾DÛ->î*ÖIÍP,ÀM×92,¸ºK¾+ôµLÞ+BÊÞæCôêÕ59³Y
+ÃÞÃ;Ê*¶?4×-YÎ8ùóÀàï-Mºà>?5ÅWϼîTH8BÞ3Õ2Á3PPÌÒßÓøFéIY·O´ôU3Så8Q»3°äåJKõD05ÊWã
+RCC>Þ¾°÷ñ÷J¿MÕå/3XÚ×±µDãúJð2:ØÍ3CP/Ý×UÍÏ´N3ßXïU>¾¯åìLòUH¾RYEåøKî¾å¯ÊÖ-ôêÕç,á
+Q28BÏW½7ËÑÀ¾**BG¶::¾Ê9ü²N:.*Ý3,GÛôJ1.³ùôRéÀJÓ²Ú/Rç´µÈÞ.ÛĵçÏUÚÞØ->î*ÖÁ¹K¶õMÓ
+82,X*F»µôµLÞ+ê<HÑ/óêåÀ:µÄ:Lîà2P*úBSê¿°ZÁÛYÖÏHïÀO̶á¯UFËÅ-0¶à?;5¯-Qè*ÔGTͱPòé
+K+U8åXôê;VëåçX3>òÝVýGðVõÀTÀ,6@ë1¿7>¶¾°8ýä/7¹ôôN¯óÝ9+LùCÏ@9+;ùÒMèÊæç*RÅÁ.+òå¼
+¾ÔÝäºD/Z9::ôEJÍ=FöÕ**¯,ºÛK*¾FYÎLÞ4.×@ú+9*6¯ñ;**MÎD¼Ä¿G*ðß-ÒF¹Ñ=3NEùêïÁ¿Êó4AÖ
+ô*/B*ôAܯåì²Zð.¾Nï´0ïN.F*ïêÃQîÈÛ@¾õV.*ù-MB,îF±ZÅÓY¿îûJÕøÂMPS׶ê4Qñï¾+̶OâLêÞ
+ÇDÚTÍ°à,¾¾*ùÊ.Ò±ONLEòî*ïϺ6*CåìóR*óäÌùÞÏ°Añ9¹ôXá7Ã:½+5ûæÂÆ»D>êCÃäÊ5;Y@°Å,¾4Ï
+LQ,*=±,ÒÈÕçÒ-J:-ÐIξ-7J¯1¶»*+ÄG¹çDìÞÞ¿,¾+;/;*¾JB¾NQîÂ9È3/¹ô+,AY;.:R:<*2ʼ4*å
+é8²òW<ÚîãüN.P4:>WáGº¼±.=,1ÄZ*ÞS-Þúà´<Qõ*ÎAËÖ°ïÖ?ìäM+âKÕFSÁUóSPº=+üÇ1áê×¹8äÈJ
+-µ´ö¾0È37¹ôèÁÊ0¾:?È6GùèëÚB<úR:ØÍ3;°*J;Ö¼îLNºÆOÏ*ÞT:øÇ*Æ;¹í²èB¾·ZËÛôMý°êÇßÖû¾
+02¹¾Ø8ÂÞ7+27ÇÉ+JÄR²îFºüÌ3çñ7êYÜL2JÎJN*.·õ=L»äÆÔ5;BS¾MGÆâÁûË;FöÄCÀ,*ý;V8ôã¸Oæ
+ûÔ3M@8Ùàú*Þ¼@9/:1ÛðN7¾ÁMó·åÂÊÓ;OáÖ±í±üõÔ3ã²EÑ+°QÝ=UJéÅà·P:ËåÂÄûÌ0ÇÌÒÉâG:4ÞEÕ
+¾0CøïÀ=Ô:SÒåUÅ.Îø3F×OõX1D4¶ß/NúÅPá,¶ûóÀAFI2¿E,ZòúÆXHüNÛ¿Ø»E>³1ËÆÄéØôä?¾À*èE>
+23Î2C2O8Gô-ÀÎSYIõØ,ù=;BÀ¾äGºRÇûË;Ê2FBÀ,*ÄþJôP5Ò·ï3Åʶ°ïÖ?:¹1Ã7á·@9ã*ÕÚ<°ðN7
+XÒ1*¶K4QÊæÉáïUCÇÓêÓRÖæ¯ñò+ùK=Ððêô7-öÈRÔ@ͼE¹ôBÔ´ò´ßÚZÁ@²LØ3Jç=õ=êéË,îæܶ>Î:â
+Ò÷*Hø-+=+Mã¶ðº´C32?YCϯAE¾âæ=-*LÑâQLîâ*î÷¯74ò2ÖÁÜ@²ÞÃúG=D,4V7½ñ4,7À-ÑU>B3¾2Á
+:Æ:â+KJ<32R÷øP.TAÆÔ/àM+XÃÞÃ;Ê*îÆ*<±9ܾÛ-ͶâÂã7ÕWßöSZðóÓºFZ¾UXCC·Ï0AèñÄ39*¾Z1
+BXç?Èï˳Ì:½FØ°²ÖùBD:ê×A=Ø:PÐ?Y7î?õ.S°1¹ô¼VÇ:ÁêôN=Xº.ì¯<¯å¼»?J.ë0-:ÓVFMíÁ-¶:=
+÷=Þ¯54J-C¯ÅÒ³Ô?+Hô**7=IIË3ÑÉ⻳LýEïUûÅõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂíÝ3ݽ=¼ýKöüË
+öûEïùµàõíÂíÝ3ݽ=¼ýPúüËöûEïùµàõý,íÝ3ݽ=¼ýPúüËöûEï°Á¿A5ÃåçÌG¸Ô,*ºåÌGÌî3ÛúÃE;æ6
+Ý5,Ö8=8+*Ë7ÞÂô»»úøµ7*.½ûõ7ÝÂJì/íWÇ3ã8·EÙÙá;¹ëÂìGºøóûÝýüÚ:9¼×*îÜ·ýäû¹¶ôìÙ»3+ã
+¾GÀ@ôó/7EÛ85W¶,ÎÚÒÛO:°ÅËæÑ19üù·ùüóÑøôéY@*FºðÖUåÌGÙµ¾ÜçÉ2:1é+É?ï¹Kô-Ï-@->Jï-Ù
+ÖËÛ+öõMôÖ3GõéØYå@ͽ½Ø½½üã,¾ÝI½ú²æÔ±ÝPÕÉYíðP²Dûö×ôJñôÀêALåÚ÷ïMñ¿Wêäì<2×áPòͯÝ
+0WDúûûO½üÙ/*í½IºëÊGáÄË@Ù¼ÜãËæß;LÂó³ëÔ¯5÷úõàÂÌH·èÎUå¼Û?;L.ì¿Wêäì<2×áPòÁYÅÌú÷ø
+ùùµ6*̼öãÇMÝHÊB.íðPòÏ>ST6ÙóåÏã¹û¹+*ÝIü´äËYáXÊ>S4³U¹¹H°ÒËYÝAËæñàäÊBñòñíÁú·»,º
+Ý6<øúõå1ÍÊB±ê>õXìWìÂXÈ>S¾Y½¼ê×Rµ½3ð½ß*XIûîÈ9½¼7¶ÆLè¹É·âREݼYDϽGGCZââãÍ1õ±,*
+¸3àÀÒÂ:Ù9ö÷ðÁI0F÷òÕ,Õô@µLÍ?UÔØÔA5ÔVÌÍÝ5°ùøñÁM@º×óØK43íëW2**CÃê¯=øÏÏ3ñWʹ¹õ°¼
+úM***,*æ¿?¯YþÃ9YLð¹8Ö*0ö÷ìÙñXÊAïæ°òëHµL9@VÖÒÐ7ù³Ê¾½Íý2*¶ö²L8:î:âÓ2âK+Zó;/¶ö
+í»EôéÖòÒK4ÇêËÅÕëV2-.F*Çåß*¾ÊLôÖ3ÛOæÀñ8Ô»»¹*ý4*B*öîÙñÜRáR*Z½³,Å,îP/м*í¼Ûôß·/
+Á´×çËæÓSÑ´=ò߸3ÑëêåÝ+*½WáGèíÏÅôøR.<Ç**îÆ,º¹ôÙ3éG6øä:ÃNôäUéZQD¹ñÌ×äáZÕBõ¼â8EÏ
+±òFBXß×ÍQ1éY+**B.îÝ;VÝ8Jß.ä·D326ßÜ8J*Þ//Æó*ìÒ¾Y1*@È/úÔYÕãËÇ:Zßø=>Z7W2ZîÊ=XS¾
+Q½HÙZÚ¿G±êòPÂòæÏùö÷25J½N±L5Ü52îçÁ³æ*6¸ö*6ë>5ü/Ü?-öóéNJ¾ÒEºöLGØB?*½ìóÂ7¸òÁQð
+÷á:ÃNôPZÓ¹++Æå.,BPä:²/º×Âê¯=¸ÀÌ-Ý7úø¶ñ*û?*¶*æÏ91êS=ò8G*WÜÛÛîEÙBLÓPîÄ,XÓ¾áCF¶
+-òæÙ/LËÉݼñ-P+íð×Í·à?/¶ñáY1¼EÖ±ÀËìÂÄ9I¼/ÉÖ*÷M=:@LIïB1³ùôRéÖ-Æ-7=ì¾Gù-0îîX.ìû
+R?ôêëEKÎÑÅ<+*ÅêJÊ=Yº,/Á><×ESß÷/.JÐÑÅMû:ÁÎ/äã=°ÅË2ö7³Öèܽ-½/*;ÖÔÞ1±Ó-,Ú³î@Ð+1
+ܱÁB++8ú7/ÚT+æÚ°JX.H´¶À,ð.Øð¾ñ-JÝ»ÕH>+úDÛ3çßÁ1Y2é;ËÇ+âYJ¾áÞ6G¸äBÎ-*ÓUì8B*Üæ0
+GøÏ+¹,-ð=°½<*Qö>NïÞ@ÌíË6ÞûäØN±ßK±õÁ@J:ÍÆG´LIåCZÙ¾Sùô7¼Ý,**î4,Þ¾BM,*ÞôN>ÎF*¶Í
+@FK+²úT.@°Í»àÏ.¶íÜS-´5Öñ·ËìÂ0<NÆð82é;Ë34ÄÍÊ8ô08ºõ+Ó´=¸ã*14/¶Õ7F¶¿=+¶¿QJXÆL<¹
+=>4I¶*ò5S9R×Àäì1ë7îã:°ÅË2Ò/³ÖÝѵñ¼0**Þ?.Æ9ã°Ð¾ï*¿¯õZÞ34JìÚ³¹âï8>8O*ëHûµÎ.Réò
+P²EµìÂÔÂO²ù¼-*<4Ø52îB0ùôWö/í*À,à*8B¾ú¶ôÞ0NÄÜã*Á,A.J´éXº,æÀ><×E+óê¿-Æ:X9æêûÎÛ
++Ü6LôÖ3¿ÖÎ0óUDôêÙ±I,**î4,²Ý/?MÙ¼ÚP:+/61J³·,Í@ùä1îPáÓ¿Ì2A2B¶°*A,*µåÄWØíJ3:ÔçË
+æÎLÃøé+¯¿ëµ*ÞýÌ*@A´ç±ý÷ëä>WÜVÇÖë*JÊJ6Þ+-¶½5VÌÄG÷ôë¼ËÌßÓåìÜG+B¼á0Aö.¼/SN°µÌõÂ
+YµWÁ@é9µÎ×øZÀ*0Q:²SÃê¯=øWÍ-ÝóEø³êå***FÁ*âåá*.H8ÊJ,42-¾Ò³WRZáË9J;20F+D´:Zîâ*ì
+.*Ý8ûõëÞ͵³¸=¸Ù´ëÜ,WBìÝ<øóUÉñ¾WçJ:¿1P×?³U4@Ò4ûÂðXB*òEÃL×±ÕHÑ°×±-¹îP70Í6XßÎY/
+SN°µ.ùYU³5Ú<Å+MèS¸;¿ÎO?°ÅËRQÃPFAZæ1ѯË7ÎÏ+åOFøÒPµLAVÖZQÑ7ñ7º1à¸Ò¾HE´ç¿÷DHëSÆ
+àØôô¸â8EÏWT7×íB6Æûè7*¾=*Ö4¾F8+Í7ٲ߼;ÕÜÒçËæC±äê2³ä¿/-æAçý4Ø<ã=GÛ¶ZäÓ7/SVèìàH
+9Åê¯=DLÒG58Á¯×±á¼»×*2>ÏÒ99ôÚDÏPµLÅëسQÇOMPVFÕÜÉH÷ëÙWUÌVØ°Qâسê¹ØÙ.LôÖ3W>áÑ=Å
+ß¾ê50*åËùóãÉQIÌÒçËæÍAYÜ7UèÎ=5äÐõÀQüE¶ëÍKÕìØ7?YüúÐôô¼â8EϽÉȸ²ãÊOÝ@سYµ3M¸>òæ
+ÓÓàÈÙåËæáÙµÝõ<ãÒQÑåηY/*úµôâ·éôÊSY°í¼ú-ÙÖËÛO:°ÅË2îHB6DûØôÛBPÐçK0õðìðPòîûùõYð
+ÎÐÇO³é¹+*ôW»úæV5Ç6Î*î9êà¸ôô¼â8EÏZÝÖ0-Tíį<5,ÉëÙµí;GôVéààÙÚ3/¹ù߸TöÙHTÒN×4*R?
+TÒò÷P¶¶,Ö½°5É×Ù.LôØ37ùØàê.¾ê×*BËR/Ûô³ûºÍEÏZÅIZµY,*ö9ëà¼ôô¶âÌE;YYü¸ïÛ¶íìC*>ìÛ
+ÃYAÝÜçËRÖÙÝÖ<,ù¹ÙÙÙ;¹ìÂÈû¸òà·éܳ*:ìàϵù<TôêßÕÅÍýÀÂæöáY9üôôô²âìEïùí¾Â¸îûEïùµà
+õíÂíÝ3ݽ=¼ýPúüËöûEïùµàõíÂí½2ܽ=¼ýPúüËÖåÊ2ZH**Ö÷û-JTI¶JÁàÕíZL?T>SÏJ½¼ý*ÆV2VQÌ
+E±ÜZLÃ3ñPÎK¿¼ý*ÆË2ÖåËAWØòéµB;Ä>Ç<äËíI*ðM,ÃÇÐâƼû0=èÄ7IÀ6·ðù1¾ê¾SÐ6÷ïMݼÁ3Wà
+Â3?¼ý*Æ93âLÄ+*àÕíZàõI*S½+Öøü+:¹C*%%%
+d
+551 94[1 0 0 1 0 0]sl 8 mask 0 95 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/snapshot_nobrowse.png b/doc/krdc/snapshot_nobrowse.png
new file mode 100644
index 00000000..161e9a64
--- /dev/null
+++ b/doc/krdc/snapshot_nobrowse.png
Binary files differ
diff --git a/doc/krdc/snapshot_vncentry.eps b/doc/krdc/snapshot_vncentry.eps
new file mode 100644
index 00000000..05d85b85
--- /dev/null
+++ b/doc/krdc/snapshot_vncentry.eps
@@ -0,0 +1,248 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 561 183
+%%BoundingBox: 0 0 595 841
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Sun Oct 5 16:06:17 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 10.416 3.125 ] [ 3.125 10.416 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d
+/pageinit {
+35.52 24 translate
+% 185*280mm (portrait)
+0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 637]ST
+B P1
+NB
+W BC
+/mask 6532 string uc
+î½*¾î1:?*¾+á.S7*¾ÍÉ;*¾ò6+¾Qä2îQTÁÆÊ:<5ÀòX>E:¾IçÛR°K*9йÏâÀ¾8ú1ºGÄ*-@â2>Þ½¾é¯
+.*ZOÄÖÍL2*ýܽMºýµúüíöûÝïù½áõýÄíý7ÝýE¼ýµúüíöûݯùë2%%%
+d
+/sl 51612 string uc
+î½¼**û½0*NÖP½J3Ü0AÌ+º>YÒùÇY0º´êA+*@ÕZêñI-CPÑç.HîÕI-*òêêûùñåIËC³ë7âèÃÔ½ôÖWI1*
+ÞÝîùéÝÝëáöÊ6M-ò¼ðÉ+Í7ùB*ÊWøûMåÃQDÓèÓ°ç<ë÷¯Â±åõåûóóBóX3ÖÚÀ¯Í9M.úY»;¿1ú³,*ZÞù=
+ÝD7*¶ÓYåì»Vø´ì¶óÎ÷FKõð*äÛݽëÔCÖSíؾÊËGÈ2PÝ÷LÇG´0ÄIJùíI5³D*UäV¶WÏ>SLÆXñ·À
+ôã+êÛI´ÒV±¾Ñä³+ú5¼ýüµY¾ÏÂäø,*¶ïïëåµÍ»ö31RÔVCõ1ܲH*îV±M´6AUÎ>3L»µ=ÀÀú¹JüýíPϾ
+°ùµ9DóÏ?óÌ.ÚûF¯ËüC**é÷Ð*ÈGݽýÂóÃLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏ÷*¾õ²»µí×R¹ÄNýÒ×
+»±,ÓÎð»íºìJî4¼å+=0Ò¼µÖQɾüý1òD58O*±ÃGü¼ýùëQN>õäïL>@²*0¼½ÑOÍNSõF*ò»ú·üFûøøíÉ1
+íG¹åHû·ñäÍQÊ,BõüüÌõ/ÒÏû±Ï@*öÙ×ËÁÝ9Iºóöº·îÖéH:+ÂT¼½ÔJñ+*ý·ÖYØUÃØFæà7EEñÇ3.ÈßL
+1ýµYBËEÎýýýGò*/ÒSáùôÅÜW,*ò¾+ì¯ß7.öÓôñýÊ/ÇBÉ:SLM·µ8*RÃàÀ0*¹õQéñç5SèF0¶ü·ü¸¸óð
+ùå½ýÜÕAÍû÷ðâY5ËB/ºõüûõûíÝI¶Rø**ºíìæßáÝÝܹNXF÷ë¸Yʾëí9ÒåÂ*ÞÛTÄTÒâ¾õÖÏZ¯IH;¿ZH
+õ3òýøȺü>ëìí1̲º¾ýGYÝñÞñóQäH»P@ÍݺâM.òý½ÍAºîÂ6:äÖý1½º¯,BÕùüûñA5ÎÑöOA@Ú¾Á:+Éé
+Gýî1èÞ·ýA×2ÝÊEG,¹Å¶ÞöÄݽ÷Ï-Úæ.û·CºüûïAM.+ÚÐÂ-OÖÁ*Jçû+áNøLÈDÅ+äñ*¾+¾UÍñÃÉIÝü¼
+½û÷GüøíÇAIÜBãÂSÞãôíí¶Úüû1-Ï/*üÒZîäÝÝÕ9IÍÕì¹öïÀÅHJ+èÝýäÊ*Þ³ÄìSðJ»ïÌ;X³CH;¿æIý
+ç÷½Öô¼üíÁLñ½ÕãýýݸÅýû-ìöýº·úöýàÒèü9ÙÄ**Ûî¿5°ÎEµ<û9ÜÕ5=Îóý¹ÑòËϯ+-ì»=³ß*àðýE
+¶¾ÃAüúýµÓ¿ÓýYèÛ·¾UÓ¾¶QüIIìË-Úæ.ìú½±XËöGDñ2-¶Ù/¹**NÙý´ÇH9JSñ>ÞºX»Ï**îæñÒêïÞÅÉ
+ÅI6Æë½1üù¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×R¾øõÅC=*öÇË»8Ò
+QXؽµîÃQNZ>íýIHÒ6Pø÷1¯ºïËAùýÝÐC*½ôý½MïýÕTäÂüµHX6¿*À÷À18Æ+-À3Ñäýù9ÛB+³ýݲÖA1
+05JÁüý·TCÖî48>ÑWCýÁI´èÞøýI.ðFÞ?8ÞOßüýµòä+X²,ÍýíèÕÆ<ù7°FÒÂ-OÖ-*Þâûó°Ò÷JÍDQ+äñ
+.**ï·,*¾MÍñGÅåäë»»ùõÛûøì·åHܱàU>B׺Ç,;,;*¶Á,Ú¹ýüÔèÕå÷**ÆMíßY¾²Æ>ÛÔæ°Ï¾ëíý
+°ãÊ*ÞÛHõ8Pâ¯9,ĵL:RÄ¿ãÅH7ãÈûQ8üP¿²:ðÃ+öüý3¯º÷ý¼P±.QºîÀ6²ãÈOýIAòà.>êìýýÅQEV
+Þ°íý³îZ¿òýý28.NÉ;ÉÑ0Ôü½S>å+ÌïÞ>áË-Úæ¾ãçÇ5C¶P3Ó.¹**éÚ½VÂ1ÌÒ¹2:Hå¼±IÝüJ:æ¼ñ·Ü
+;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1îA=4*Ì+Ä>¼úÊ1*Þü/ý»ùÃALëÞÁýáÖR¾øõAC?*¶ÌÊÓHPú:áÃÅL:2äOü
+ûÝÕMÇíóáM¾*+<²Hîà*ºúý×LøúíÚÎø¾5H¶L0RÐòýVAGC+íåXïýññûýíÚ÷AÛîòÞGF:ÎýÃÕD:éýK@¹Ú
+῱-¶Ï3@ü½Z;Y:áSO>ºVK*Îïüµ5̺ßQ»á*ñ·.Gº÷ß·*ûÏå÷½ÝOÙÄüGúö8ü¹òÔOýüÅî74ÚÆI*î8/.
+øõM,Õо±àÅíȹõ@9**Ìá7ºôïÃ04GµîÐÝ?V>ZõùÕúB=*¶Ã»ù0åG³Wß,ñà=;2.ñ9M0ýYðMÝýËêÈ?ãÔ
+1ÚB*9úÝVÃÎýÙÚ.IØý½éá5H¶J0ÊíXíýϽDôà.ÖíÍ;¯ü9ÜRÒ¿Wü9ñáÞÀE¶îýý±L×ø½ÆËP7ºQöôÍà6S
+>ðUF¶=7ó*ADK/ý1G°ãÕ³8å0ðN3ô**B¸ýÑ.MÜîÑܯ¾øFÞHâ1ÜÝøFA1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿=Zº
+Å,ðºù;¿USÞ@å͵?»øÇ1*JéÛ7H¹µò+.êG¸íàí,5ÖRÞÊõYȯP*îÉß-ÌêÓìÝ=/ÛÂQL:2äC°.¸Iú¾6Ïà
+îúóµYµHîâ*ºUòúÝE7N7Màéüý=H¶J0úI½ó±EW¸öÀ6BùùüïÝìÃS¿Ëú¼HÝßÀ=¶2S¾üÝ=/ÂWOÁÁNùúü
+ûñ¹E¹Â-ÅÐÏ4ðJ=¶áÓá÷íAýìBà׿ÚSÀ?.*øìÝÐÞÅAúÒµ6îÞñRùõîÍ»ÌA5ìDôñé×µõêÕIÅë¸ö°ý°L¿
+-ºÂ,ºðùõÔÊE1÷**úIÀÞ¹å8HG*9ýG¸ïß5èøÕRÞ@õQF¯L*î-ÛÉíì¹±ÆòJßU=:¾*/,¶¼À63ÊÆ7=K°2
+Ñø¾=-¾æ<3ÐNWö¾VM>Bî¾18Æ+,<²SBÀ;+1PJC;4Òæ?½=0Ô:ÐL,15XÞK.Í+4NSØ5çÞ+»O<²2<TµTïV
+DTKX.-TáNÆ:N³8å0ðP3@J*دüüð,´ºßQ»á*QH1*FùFÉ/Fµë14R+*Þ7º@¼·Ï¾Zíü6/BNóèW3AÜï/*
+¶¯P¿ùìËFùö6ܸðÓE/=ÖP*ÑôÍH;àº*¾9ÀOIDâ¸/=LÀWóJ¾ïÌÕ;=9R×ûKµ5Æï3IóË-FöF+*Þ×å×PÞÁ
+ù¼°ÁPºöPøÐL+ÙÌ»ùõÕоEH½ìȵí4Y*¾á5OòõîáÑÁåòÑE7óæDÀ1RÞ¾õAÙZL*Z*1ÜMB=;VÉ1,6µÛ7ç
+ê*IÚÜIå¶+=HïÑÜ·¾ñÍñÁ¾-AÜFøòèûøõñÕAQ¼æÛÊSJ?4AìTôêQÞê7¸Éò7RâàI±Ôóö:ÐóÌß->¾²ùÑý
+BZÝ*¾*,ÒM:À/õýFVG¿1ÍöSíF*Bºû·X1¶îßÅEµüÚMÝüÛúôëóÒÉC/>LíÜW×´ûÄʻ޷ÎöÎ;Áä7òÁSÕ¶
+*ÍÎ*ÐìýºL¾í-*öéüûýOÉQîËH³8.ÚüFKOäC*2Iñ°P+XB¹´UÐO9º,*XîI¹å¶Ï,Ã4ð¸äìI¿QRJ?,í9G
+O½½-*FÞßûùÉ6PXÝEQèG1¶ü·ÒÌÔPÅ*J²ÓÉD/ZóèÔF¹¼ÓäüíH¹ðÞDZµ9ú¹³,Ê;õüûCÅâõY*¾5ÂT»ýü
+<¹¯-öÌKSõRÞïåϼñÜG,*CÕYè>¾ÎKÙÐV·TÊôû4áíÝI¹âÄßé9+*²Aðé+LêCHYéèëéA?Hóê9ÞãÉWC´F
+ÕQá»55=íý¯*¾µÔCÉB´òðëÉé̸@2îïáÔ.5ÌÊ3ÚHºò68OèAYßUõ¹**íTÑà°ÞÈW5QêçéùYâú.5´ãÕí´
+Ç+¾±áPë×°àÀû,ÜÜ-*öYݾ/EäVس¾W´¶èØL:¼û8B,ôíI*ðQÁõ9ÄíI6ݽB¼ýZúüßöûÁïù1áõ9ÄíI6Ý
+½,ºýZúüßöӾϻY9ÝüÍ.*.½ûõ5IÙ´*Mõ8Hûøüí½½XÆ1ý¾*Þ»ñüËùõîêÛíEë¸+6»øîß½Iµí-3×°äY+
+*××ü·*¶öâ¯ÕÌG¹´í*ûÏS×õóïéÝH*:úóݵ0B¸ìÕ³ÝÌWHÇ°ðXáÕ,*âòëúå+ÞíÍI¼D²é×YÑéãÕÝ:?õ8
+Hûøüí½½XÆ1Ý?*¶íÚ½ñüGF¹õ»+ÄüCôéÎOÁÝð8úýU**3áäÍý÷4¾¹*îûõÁÕË×±êÖç°éå9ÂÈQ9Húõñôû
+éQóëÕÝ?*¶öâ¯ÕÌG¹´í*»U¾Ï=¼G¸ðåéå²úVÞ2Íýݽý÷4¾É*îòÁYÌFøóßÃKö-ÏÐÚYé¼GÙú¾N×IýÔ*Þ
+íÍI¼D²é×YÑéãÕµ.BTÛÖEAäF»4Z.*¼0B,ï?*õ*:>åÁQXÚC´Ûº¹I2?õ6ÙóåÐÝííYøñÝ@*öüùßíäÚ÷³
+êNC³ñ7+ÄD@³ã¿=AýZÞÆîáÝÛ,î8*ú»øðÁI8VC³4EÕô-Ïð¿YÁÄê÷¸ùø9ÜðXDÞóÑ9¼C°æÕ-5ßÞE,6Óç
+æÃùDôG;BPéÄËVÊöú½÷ðOÕ*Ú-*QýÛùìÃù@8WÊÜ»+ÄÜÔóãÁAIIíÜÞÕý2**¾+¾U9ÆÚ,F¼ùñÁMHº×³TU
+õôÂÞÖÒ;ÇõGÔ¿Ñ;¶î+·2Àè.»ÖXàÙÕ.į?Û6Ä.Húã?*-*ó°±*H¼¹ôÙ3éG6Dæº,ÄHÓòá¼9-1IÜÒQý2*
+*¾E.ÖÀ+öøïßý´êõ¯ç<?ì5äÆ-6ñïÜã¾ÏÀúFBXß×16Åøà**¾RÝ÷ÏØÝ-3R>¶À+Q¸îÞ5T-ÌÆß/@òî*Þñ
+ýÙµµÚ,î:*Ú¸ò×/á7úCâ=Z.EÉÞ3ùôï5@9:**¶¿**J;**R3¶Î,ó+A,ø46ìTÞÇÉÀËQÊ;AB0RÒ.Â,FB¿
+÷¾M.¶Ï/ó-QLîL6078GßZÞÓYæÁÌ-Ý76ùCBHÎßU2º¾,M*ÅÆîä3XÈKÇJ-5*HýI»¸Þ?*1+ÞÍAÅGöëÞÌ9
+H+MöÖ.XÎU½Ü¶ãZQ6õR,ìÒÞ3àÛ°¾=.¶ïÐî¯ÈZº°½ó»ÕHîÎDÌÇâ79Fð¾UJ¶Ð;*î¾;Ö¼0ÄÅ6î0-X¾Þß
+8ÚÎßÁ7¶Ð,û5ÄÂF·Û?*1*:üW**21Jà¿ÙHZ.*;;>FÏLÕÇI½Äȳ/ñ*Å0îÊ*XãÞ/AF±¾5A?âY9±¾À·¿
+ï*ÞËÄH¹Þ.Þöî04:XÈñ;AÊ+*ºáEÎø¾¯Víß9Hà¾UZF*¶ó,õDîN6ØB63¶î²U*8*JSRF*ûôÛøN8Þ¹,ľ
+YÃÈöüûÀ38Vß/,Ê-ÚB¾E,2ƶÓ3µ¶ïÊ+ä*XB-îÉ56Í6Þ+FH1á5к/ÄQÆ÷0+<+MûäCZÙ¾SQ4Ä1öò43¸
+G6ǵíXU*8*J?UF¾õìËDZÙ¾G½¹<·:XÃ4ò¼Ï.AÔî,+ä+X6**¶;*Õ°îÆ5XÖÞG4FZ02AGBH·ÜMMº1ĵ¹
+÷R.4-8PïOBÀ±ßEðõò,á-ê¹Å¾QÂö86YNJñÁ;ëÜ7³/Þ*J·ï6*ðÜWùòÝ;·îêÇâ+²?TNù´ìó.12**ÚJ¾
+·¾é-¶?-O5å6î,+ÌB-JÉ@6ͳïKBÜðâ.ÔîT/88=*5Ü;¼OI0á*ß-?º2Ä1Vî00P-XNÞ?I²Z+Þñý±å@Ø,
+Î*îà5³´ëׯÉôE<1¾GÇFöÓL/Z16F,¾-T21¶¿+ã*ÅHîT/ÐB¾=ä*ÝÐ.á¾ÜÉMºî+O¾µ*B<îæ041XFïÏ=
+FÖ¾E±Ò×H5¼éµò-Zºº*çöYDEæÞZNÖ¯ýV+¶**þ-+ú¸òݵ/½Ìó.6ó?æÁïü/û,3Ôã*DDZÚÁKÝÈ46õ´+
+µ¶÷J+Ò>çÃó,0G³BÌ5N¯-ë6ÄÞ±/ÞÏBH¾*77ÖìàÐOGÇFõÂø¯Ü¿KáÌûÈøè@í¶ß,4Þ±á@GBíáÒGW56YÖ
+NPN¶æ+RSóâ¼-å7Äî½BMå½;ú½5Ä*ù+*Äâßß½=ÕÔòNðÚ;èË-50û¸´È7ùóW+JBÁдV¶ñèÄÔ/Ä3ÔðK1Þ
+LÑG5@F1B¼5?¾åýݼë±/ÞëâßáÂGÑPòNðF>âÐ;=ôÚ¸ôË+¾ñäûø°ãÐ=MعBÜò*9ãöòâÂGͯT2IýU*Þü
+åÌ7²/Þïâ×ãÑ9Q¸<·üWTèÏ?±@ûØæÔ»YYòÝ¿é0G¸³ÓÖJìÌ*á-ÜKEíàìÇýÝì¯Îê×ÃÔ*FúàÑÇñ¶ASP»ø
+±æÑç³ëÚÕ/*üE¹ðÚ³¹äâÎêãÛ;Þ¶ÁÜçßÊ*KU*8,Þëâ÷ÃÁÈPXë¸óèÞøØ*Þ±ùôë¹²ÔÃÌÏÔÃõ.B,ôùñË
+XåÂéDZ³Ã×Ï°»*Þ²L?òØÕóñG+ľû4»ÑéSï°ðöøðÕ5íÃùôð*JïìWú¸÷óW+ĹòËXAÃÉÇÁéÏ5åWùø*¾¶
+5Åüê5E*6AçEÛÌ4Q=µá5æ*ZúÃÛïûÁïù1áõ9ÄíI6ݽB¼ýZúüßöûÁïù1áõÍ*íI6ݽB´PB,*/*ÅFîô7L
+.8ÑÞß9N*1õ×ÎÊ:?SâÃ7Åû¯/ÇÆÉ9ÐûÃêX=Á9Ná0åµîô.°±È-L¶±>1<ôêü°.áºFîVBð·ÆÂÄîö4üKBH
+Sù5²Ú*B,*ó2/Ì1èCJîàJYêÞ36FîÀ=;¶*9ã*12î00ì>ÞËAÚ׿ÂNïPIXJÞ3ë6/FõDz2ïÈ+ìëÞó/F>Â
+5ݶ³2QØîPMÌ4P,Õ·A,¿*õüîÂ@XÚàWÛÊ,FÀ¿±HÆ¿ZÌôß×êÚ¸Ò5ý¶Ã-ÇÁï+JF4ÏE,·´-QÚîRFÌêÞ;/
+F°ÂM=·B>CÇàÃ96+FÃÀ.º<5LÚAÂÉ4·â1å.ïPÞÌ7LRÞG¹ÛëÅéÕ·×9õüîÆ68ûðãMFÂÌá1¸â*åùîä?8ï
+ÞÓºFØ¿VßÖ*:ÁÂFÀÈU0¶ó9Åçð898¯K4O¹·»õÐ:Á5ãß=õ3;+-ÚÉÀ6ÞöÖ-Ìéâç¼NîDQ+ñÖ´ìëW@IÈà¿
+пÍUÔ¶é:×ÁïW6Û4âÉ»·*-1Tîä2Ìäå¿;F¾ÀMX¶>.SÆLÌZÊÀQì=àï³ÚËÀG¾¯ÂàÓöNñDõø;=BHÍÓ5B¶
+Ò7׶P,/·Ï-Ó+1¸îè¶Ô,ĵ¯îðÎì³ß¯-ÚÁÃáÆÇÄ>ð-á¹X¶÷83ÚçS¼FÊÀÁ´·×/A1ï¾,8.Þ?IâROAýîÌ
+@<öÄÙиæ@×ßåC+ÚRÄó¿ïßBGìÉUW¶å*17ïγÐ*á>ÔñîAÌOJ,H·Í2÷²ÞWIFÔÈÉó¶ø*µù;ÙéÚHÂ=0¶í
+Jõ¶ïÜç°çÁ=B¶â-ANÎVïJ,Ì/K´ZV*¶²6³*åÐ;BîPµÌîã+/FîÀ-BR6¶,-1LîP+8×Þ3éâK4åÐîÂ*ìRL
+4HÇ0;ì;ëÛ7ÚÞÑM¼¶>4õ¼ïJ4ìëäÏ1ÚT¿>XñZ.èE8¼à×+ÚJ¿±Å¶AGõäîرÌÈàGãF;ÄU/2J¶Ò-3À¯U»
+J8Í×Ù5V¸DD6DÜ4B¼7YVZ.%%%
+d
+561 92[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6461 string uc
+*Þ¼**ü»ýÄöüíöûÝïù½áõýÄíý7ÝýE¼ýµúüíöûÝïù½áõýÄ9°è*
+d
+/sl 51051 string uc
+äÕí´ê¹8**¾Ô-ÉHû4éäÕYUÛ1ÄÍîUܽB¼ýZúüßÖÓß2*ÚZ¾+¾U1¶.+S8JZß++ÚTû>Þ¼Jïî78³*öR*Æ
+39Ðø¿½*ÄÅÂô*-ìJJàDÙ?BÜ,ÇȾ?.àßQF+áòöîG¾;+**Þ>JFÞ¿0./F¶¿¹I¹@ÅÅèî29ÌÊð?56,Ò5ÚB
+ÀE²¶ï6õúîÎ28Þß?ÂFUÄE@¶´2ÅRïà*ìÖáã²ÚÏǹê·.Äã*¿*àÁÅÇ*îÏ>î*,8BÞÇ6Fö¾ì1á=ݸÁ¯åÌô
+À.XKÞKàGàÁ;¾±Ã¶À3õ°ïì9ì÷߯RÚÑÅùZÊ9?XÄâëÍÚÎÁMZº;ÂA¸BT¶.Î:î°08ÃKáÁïϲ۰¾5=½=ôA
+¶ó*.X¶ÞÃ4ÚÈßÁ¼¶4¶Q¹îÌ9XTÞÓ>F8ÄÙùÆ0?°é¾=RV*R2¶<,1/î2-ÌBÞ-*á-06,*ê9CJÛÞÓ+Ú׿E²
+Æ26íÊàOåA5ÚRÅM=W=**ìÞßË2Ú×Àáⶳ9µS;Ù>ÜEÍMHº<ÅQüB+BH»ãÌ1áFæûÀïX¶JÜZºè,Õ¸îÌ:8P
+áç»Úè¾-+Æ*6ÍÑL´0¶>2Aßö@29½ÞãBú.ZºðóõÞöîÔÌÕßËGÚ³¿Ñ¸ºò4õüôêCX°î3FF.áNRϲ:ÁÆN¾G
+ÅÞÖ6*ºF0áºòÖëßBäN*ZI+23R7R0¶Ï+1P³Àî..ÌÓß7-Ê<ÚR¾M@R5¶²,1¶ïä:Xøß7YÚ4¿UV¶²*1.î*
+/Ì7àGTÚ±ÅÑç¶ÎÇõÐîJ45*.¹9³+ã+1ìöÂ*à-T,T0È+ÌÖMåÁïØJîì>Eà,Ì×õ´0ÎÓ¹ö÷¾;öÆH*DùHI
+BPóÇE¸ÂÍ+ÄýDAܽB¼ýZúüßöûÁïù1áõ9ÄíI6ݽB¼ýZúü3îûÁïù1áõ9ÄíI6ݽB¼ýZúüßöûÁïù1¿õ9Ä
+íI6ݽB¼ýZúüßöûÁïù1áõ9Äý61ö°ÏöFë×MåP*¾õê×á3HÏɽ¹B,ËK»K*Ù+á;*Î×+ÞæÜÕÅYý¼+¾îÕýü
+=º;ƹ;»E,M×Mìä4õ¶æXÁϹ1ͼ»ýõÝIAâÁY=*ÚµìÝ·ý8ÌÛù½îþ°69K5ùø¼øËXÅÃ@D+RXTï<2ØáZò
+çÁ1ýûÚ»ýøS»ùóÕ3*8H·êÓ±åÌëEJý/Ü2:Åî+ÉãñUïõ-Ù-@á=Jï-ØÖËà+öõÁôB6GõéØYå@ͽ½Ø½½ü³
+,¾ÝI½ú²æÔ±ÝPÕÉYý-Þæ²øï±ë+êWÃÝYêÌ·ñáÅä-ØÖËàO:²ÅßæGá´û×òõùùÉüúõ3*Üý¼öÙCµ¼7ESPë
+ú9*MíÄ2ÏÕÕ±ÙÄËâñµ½=*îóËÑüʸôéZ/;LA>µEÃÉJæ8ÁÏ1¼7D°åéëé1ùµ4*XIûîÉ;Á¼GÖ20ü1¾ÏÅà
+ÂS¯êÒEÅ@ìñíOÑÚúìÊCÕÄÛB=ß>èÐ41òI9DèäÕíÉZò³WYHº÷÷÷ô/¼ûÅ***6*Úø¾-=¶3,3¹¾áµ½¼¶âÁ
+AÙP³óìü-ÞæSÏLÓA´åÄE5óòÍGëVUÀϹâ.8ýúîÈ7¹ÄÛµÂL<ùî¶CÛêÏ9N:²ÅßæÑȼêõ¯îîáÉøò9+**
+¶Á*1OZ5-îóáÁýëÖíáÐSTÚØý*B¸XÚ¶¿OGCZâÀÚúÑ/ÊZ69?¿ÍAIú¯ÞÇSÁ7ÏÆNïÌEìW0Fð¿.ÀôB6ûPæ
+Áó<ÜË»¹39ü;***A2**>,**õ2î4/Ä,8RßO-ÚÐÂ=1îïÙ±ÝËÖìßÎãäÙÍ*6×C±ä¾ÄHºµ²5Ò½-**ÞÏ.**
+±+*JFÞ;-ÚJ¿-0¶¯+±*Þ»ZÈëGFDæ2ÃÊ4õÞñ<çØMNÆJæ8ÁϽúFBXß×ÏU½@E*Þ/,**±*A>îR,ìTÞ;;Ú
+ÈÀÑ,îèÏå@6µîå9½+Bø¶ïàÃúV¶ìßN¹øöµ***FÁ¾B**Z0îÆ+XÎÞ;-F:*ÝHÛôß·/½8CL;ÑàGïÚVØ@ÍÎ
+G0MYæ·îÙ¿Y=½8ÐÝ,¾ÞQ+*ÞFÞÏ+F:¾U0¶C,1SîØ*ú·ðÕ-Ý7ê·ßY*Ä´íÜ»1QÙ2´ðëí***¶0+¯*·BÞ×
+?ÖôåÇáHÊ,1M>°SÚÉ6ÆTéì1ë¯B,¼M7ÔØõíÞY+**¶ó*ÃÅO+Å<îÌ,ì¶ß/4Â-FÙÂ=1JÝ»ÕH8+ú¼+BÐÁ1
+9Hæ@÷ûú7***å::0îÐ+0*ì:ßË?ÎñÞ½IÞÒ2Ã-ØÆPï8âV0W¹³³õÁôB6»Få¾ë,PÛÙõ±***XGÞÃ/.-FȾ
+É4¶Ó+1ýîÌ+¶µè¾ë,ôFìA*M=SÐN¶1Øó¸óX·ì¯ìG7Jæû*Ñ*Ú2Ãâ4õÊçì´ëCÓG9ØáZ.æÀDòíçصñÜû2
+**¾U1¶¯*1.îÈ+8ÅÞß>Ú>*â»Ùôç½ÁM*ØY*ÄäÑQÍäLÙÊYç×¹I,**î,+ä+°±¾-,ZرÉôEB/ÊÃRNж16
+@5ÑGÒØ.ÀôB6ßÕ<6ÁGÙôêÅ***F¹*+*ÞBJ7ÞË.FM¿E7¶â*1Vî¾*X.*çÌ7¹71úø/ÞæÙ´ëØéñÞµûÐæÕ³
+Ý,¾R=*îà+P+86Þ7/ÚٿؾC*Åû¸òݵ-½ì?L;Çà?ÆÛ¿×3¾éWÉTæ8Á;Ý,»BìÝίí@*²²Þ¶1Á¼ó5¾Ïõë
+ÙÕ>çÂó,ÜÌ´çæ¾ó<@WTÂàØ4õ¸æ8ÁϹLûBíÞίå¼ó0-ѱ*ùðàöU6Êõ¯è̼+BøâÇ=MTW÷íÞå¯A¯·÷êÖ
+Q=0V·³N;4·?XÌÄ=ׯÝ8ØáZòÄRµ8VBòé×S´êÕÕ3*ß;ÈدßËUEø/ÞæUÑP»3ñæÄ1ÉåÔÕÐS¼Ù´éÇ;Åìº
+3ÏPÛøÌ4A¿ÎG0M1/ײâÂGÕÌ»Ó*îÚ·ÝÜV×òâƼ+BøðâÇQùÈøñâÀ÷òÓZÈùüGù±æÓßPMâÇAÝÒàÛ9ØáZò
+íÐÐ8WC±çÕôë5ûLý7Ú´æÏUUÃ/ð¼,ZòïëÙí¹Ç°èÑç±æÚµ,*üE¹ðÚ³¹äÒA×µIü+WêPï<2ØáZ.¶920Ë
+¼WùT4=Ýï:Á¹·ý+ZòîûùõYðÎÐÇO³éU+*ôW»úæV5Ç6Í*î9æà¸4õ¼æ8ÁÏZÝÖ.-TYP¯ðß+ÉëÙµí¯FôVé
+ààý*B4ôôMöÒîµ»ÒRÇ°=*ÎTÒRçñËîæ.ZýÂ@@Ѷ3.ë³BDô³Ã×3*×W+ZDÏ4¸ëéø÷Q*ÄÀGüBܳ3*Þ½N5
+ùSÚáK¹8ĺ»÷éÃõ໹Ù*ζõ@¼ÛúøY*ÄÀÛû»»;ÖÝÙàÛ7ØñZÒêÕMAÌÙôû,*ï@8ûIöLãÌGúøö÷í157½ú
+óßíXïÌ0ë»B¼ý7Æð3ܽB¼ýZúüßöûÁïù1áõ9ÄíI6ݽB¼ýZúü3îûÁïù1áõ9ÄíI6½7B,Ê1**´¼ý*Ʋ48
+:ÏöóEÞRÃ4OT>æÍíI*ÐÛ,éÇñäÊAÞR¯àÚ3²RÎíI*ðã,ô·ñOÉ@ûø7ZÂ;Oзñù1¾ÊÛJ¯Ð>CÐYÝ+Äدá
+Å:MÌúü+:Y@ZÈ>M¼úæÕí*MÉB¯LÃíI*°ô,×Ư*¾öóEÞöû1Þ<ð*V»ý*Ö1H*
+d
+561 91[1 0 0 1 0 0]sl 8 mask 0 92 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krdc/snapshot_vncentry.png b/doc/krdc/snapshot_vncentry.png
new file mode 100644
index 00000000..b55c3fa3
--- /dev/null
+++ b/doc/krdc/snapshot_vncentry.png
Binary files differ
diff --git a/doc/krdc/window_fullscreen.png b/doc/krdc/window_fullscreen.png
new file mode 100644
index 00000000..5c9864f5
--- /dev/null
+++ b/doc/krdc/window_fullscreen.png
Binary files differ
diff --git a/doc/krdc/window_nofullscreen.png b/doc/krdc/window_nofullscreen.png
new file mode 100644
index 00000000..bff406c3
--- /dev/null
+++ b/doc/krdc/window_nofullscreen.png
Binary files differ
diff --git a/doc/krfb/Makefile.am b/doc/krfb/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/krfb/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/krfb/configuration_access.eps b/doc/krfb/configuration_access.eps
new file mode 100644
index 00000000..93169561
--- /dev/null
+++ b/doc/krfb/configuration_access.eps
@@ -0,0 +1,341 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 342 453
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sun Sep 21 21:21:52 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 367]ST
+B P1
+NB
+W BC
+/mask 6493 string uc
+î½*¾î1:-*¾*Ñ-õ0*ÞQK/,ÞÝÑT*ÞñÎ,öïì3ðZÀÉ8*1<·ÈáµÇâS̼*úÉâS̼¾ú+FQ?¾9ÇT¿º*BQ?¾-
+×TKÌS:*0ºýTöü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ½¼¾%%
+d
+/sl 51642 string uc
+î½¼**ûÙ>*<òÌû@KÍÌè6JÑÎÈìQ¼Cîׯù<*VXßÖåYúÓÐïG,9öé@/*GE¹ýüú÷íö¿ñ×,ÈG´éX/*ú5¼½û
+ùÕæTåX?D:ÃïEÙòùżO<JòO*²IUß×ñùñüøظ´L,@ÕZêñ-Ð*ýUÒÈáö7öüâÕÜ,*/¸33DS¾Ü**,ÇKÞ±E
+æ±5¾·õõYÙ³0í×15Æ××ÁÐÞòEÒÈáö;ëüùåL»/*Úñã;ÔØNè×*Þî3+Õñ×7²Ø/Þìû¯M㸽1ĽßõíýôÃTÇ
+æVY**AHÜûúô×1Iï*ãÈ?ñüÞÉH-*åDZڷ¿2¿áC**WØÁÂ=å¹Fæ±6¾°ùµ9DóËïÚO9¯ËüC**é÷Ð*ÈGݽý
+ÂÃTLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏå*Þ³èÒS3>DHRÄ-2G°,+ìïÞO0ÎÞößQá-ëZ-0¼½ÑOY.å³øö
+ñòéé½YHºõêGùòåËE½H¯4ÞÚùù¹ÜATóâöûëêäßíÅ9ܸºÜF¶ê³YV×ÇíÍ?æ±**B-*ú?XËúÞÂ÷Ð3:C@îJ
+/²ìðöûñ½ý84À*ÒúýîJûýý²/X½Jæ8ïí¼öç3Ê,2ÐWY¹+äóܽ½ûQÀîÔ6ðE*êÛIWÍQñJ9½GÛ¸÷ûñݽíé
+Éåüº·ðÕÃPÊ,Ô¹ýüùÀT+ö5Aô»¹M19ýüøì;·ïâµé»¯,´½½ÎHÍ+*Ö6*î;òLÒæÚ<êä,4/ÚXýé÷SD³9Ð
+¹SîH»øýIÆL;*ÎÉ?¶ë+úý½JÛ¶ýíøò.T+JâáXÎòý½Á.WÏSÁÉBÆìD8>*Èì9BPÐ/FÁH*Þ*Þ?øöàãÍY½
+ÝÝüú8½ûõâÉ9YÊ°à>î°ùõµFXýüÁ¿æ,*½TB¶ñííéÅÍåUõGú¶ßM9Î*óí½ñG**È-*Èë7-:GGT4R>*ç-O
+GàÊ<+0ÛWݼÝ7òäËýýµÔ9XÅ,öý¾²üý½±éöν>J¶Þ2J6*IÕÇ>·üïãÜLÃö;Ú3ó½ì÷8Ú¾G¼íýüD+3º¼2
+,ú½Â4ôBüöWÅ5Òüý4CàÂBJÞ¿è½»ñPõúñÁâ1VÏÀ¯ðïÂF<3*NÙý´ÇHÍÎQÏÈ**î2»ÒêïÞÅÉÅIÔÂë½1üù
+¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×RJøõIñ½²*Jð*¾ÀMLW̯8É,4
+/êüýÄ.Ì-¶ëý¯LúÝöNÊøýý¼ýï950*¾ä*æ¶ÙËL+P,8¾¾Ñ±óÙöZų=ºÂ4á-Pñ½ûÎ8Ú*ìýÝÉÒK¯,Ô4üÌ
+ÉL6ÞìEÒ¾9³üQ7¯¶¿PôýýùàCÍ>-X7ôIüµã4?6³,Aâ;3*Þâûó°Ò÷.»B**öíÕ1*Þ;øöÌá±ñôÜÜûùì¼»
+õÚ±9íCï?46½ÛN¿2+¶IR/*ÚK+ìÛ½½Uóéñº**âÏõîAè@ìÄXU²×RÞôõ½×ðC**Æ+*ܯHMëîû÷ÏïVJϾç
+ýIçâ51¾ûüÕýÓ°÷06D0B-Jø»Ó5/¶¾,/,àíü:RÈYTãÖ*öÝüZXÞ²üYYæ¿5JôýSíòBPÞ:ù´âY²ß?TÖ¸ý
+ñÕDYÎ3õìý½Cã,/EòñðçAÔ¯ÖîLHXÆM<*ZËùÅÃÖ¾=æá¼±IÝü²3æ¼ÁHÜ;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1¸
+>=4*ÌÏT>¼úÊ1*Þü/ý»ù/TKëÞÁýáÖRJøõY<»Á*ί*¾Ù;ÑÏLUÈB÷C5R>JàýíU·/÷MÕêýøÂNHî˯¾C*
+Ø9Sâ57¶À,DÝXJ¾*74Z20BµB=ZZýµPòÞ/îäâýÇI6=JZï½EC¶Ã4ôðý÷À4FÎÞºýïù¸*4Nû½ÐÕÔï,CXÆ
+M<*¾ÄùÝW¶ð3ø:òîãÅ÷*òÅæáýû@ëDøõîá¹öíÏÙ@ûùIÀµRî.GíϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæ
+JÏñ0XøÙÀÃ,ô¼ýÈ9Í+¾YÚ¶;O=3AXÂçP/øàRZJϾEÃíGSÉ;¹äóÕý=VùÝXL99êäBÞ6*O½Aö²*ëýÍí34
+Þèô/Ìòø4?úýýÚàÄõIXÓ-A¾OWüYÝSÊ*YøÌ·ßúýIÛL/Ùù½íLß/VÔäÆZýýHòJCÒüýàüýÕòâDXâá/·N,
+*B¸ýÑ.MÜK9ÞHX.ÜÝL9A1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿=Ó¸Å,ðºù;¿USÞ@å͵?»øÇ1*JéÛ7H¹µò+.êG¸
+íàí,5ÖRÞÊõYÈïC*æÂ3Ï3¯@¯*¾KÑÓ2Ͼ¾-?î;óTå½½»ïö¶¯ùõA±àÃï¿ë6-Ê+¶×óõüÅ92MçàÜå=>Î<
+/ÑÝíûÛMÒîýýýÕíÙÜJ÷8Ú*ÈåIÜKL-16ô½Ç¿éýíö0?ZýûÑIç4XÞøùù½ý¸ÖP1È¿3ÝÕêD½Ý3YæàúÚ:Å
+N**»õYSîá÷Ë-òKëÝÁ½ÄHÜV¶éÖÍYåÜÛ±ÝüF´éßÉûÉ3/8Né;4îÇíÝÙ°ìFâ-*îý2,íG¹öò*ºúõæÃ1YR
+éÝL+ØÚIïCÒE-¾07ØEâZ+¾L+AÄ0Á0YÖ¾ùÙ?Ç:KÅL?òØ2;ØÓZà¾*Î;¾5-îKë/úíøÎÂ3SALãÌÎ*DÚÃ
+ÆÎOÕT¯îYN×5ÛRAZÇ.OðÔÌJá+øô6;JAR;Øâ,3¸³î¶¿8<>RÏTûX>ÁDEÒ4@K×̺á++F;Å>,JEäíYXÞÄ
+åæ=Ôø2»V@îÚ³I¯Sá7º@¼·Ï*ZIX»Á,6иóÔÂ5íö,*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2ï?*ÖÀRà>º+
+*Öè/>»4HÙ¿6@JáíÝÍÙÕQ×KKLG2ÓÈñ4Ð<1?ÙIò´+CÜL9+*ÞCøÖPÞÁù¼,RPºöPøÐL¿2VöüT4¶áñåW÷
+ôWï/*¶ºÏÂü8¼º¸öʼ¸ÑÏ8WMö*/2öü°ÍBè6*7ò´î3*Þ¯¿,7Dôê0ÐÖ*ÂNÆÎRøÐÁÀ¸J*+Èò4<<1¼¶»
+½Ìï,PVÝ·ÌH¼2¶îàÉMÍXÛYý¼üøðâ1ÛÉC/ÒLïàWãÌûNîÚKµÇÜ+ÃVú±äØ89LØìCº¾,Æ´½ø9-ôV*ZßûI
+1UQîßFæáó*1Hú¶ñä͹òéÝÉíÜÛ̸CѾLòëÙ?éÔí6Ì1F149D°æÚ¿¼Bó41î34JèûÍQ.ÖY+¾ÝôíýYغ,
+Yúä³ø2T°Ì+¾öCA±4æÀ×±Ù<WøË繿ýêGáÃ4>TÆéC¹ý.HK+Ó2º½ò>û³T*¶¾ÁùõACÊÚ¼Q¾ñF»RGWË8+
+¾æTA³4ÞèÓW·ôúÈïúÝ»ôâ¿=åìHöõè.BMìûù±8ÇYÁ,¾5ÂT»ýü<¹¯-öX/å¯øμñÜG,*CÕYè>¾ÎKÙÐV·
+TÛñû4áíÝI¹âÄßéÔ.*Ø5÷óÆ6ô6ÍÕ³óôóÙO1áãÉWC´FAîß»55=íÝM+ÞEéÊO6E¸÷ôW=ÂïïáÔ.5ÌÊ3ÚH
+ºò68OèAYßUµI,¾µÓS¯CZãÔÃ=ôòë=ÓBIßÀW÷óÙW°*îêÖS¹ÔÊÖZݾռ.*ýó5ÖZáÊOÙ0Öã@Hé:,ïéõÒ
+4ZýÖ*îûÕèùÝèõ½ÓíýTÝýÓ¼ýUûÀÕ4-í=óÅÍÚ+ÞüÆØAöAS?O35XôûÕå7*2ûòÔÔRü;ÓÀ¾½ï@ý½ô*ÎéÍ
+EÅûÉíAÐT0îêñêÑY?ÜíÚ¶ñ³û×è.*:ÕBôÐTßVÙÉ5õI»Uç÷ÚÉíÌ?ùH**¸W-9JñP¯ÓáÓ°ÔÈõìGýTçûØ·
+Aµ³úH**¸W-92ñäÕDSÓÎÔðÕ¯ñͼÓÒüéÕÉéÀ¼9**ÔÔ¿1·±UWÀÓÅUO¿ûHÛD¼ý×-¾àY»øµZåõ+*ÞèDö
+ÞDYXFU9ÓÈDá7زøû5*2ø,<9JJ¼/ÎòOÁäëV÷»**ÖØ0HBäÈCÙèMÓÐê²µüVüü,*ض,Ë-2ÆY+Òúå˳±E
+ü.*:ä9-6Ò¶Ñ+Õ:**FQ¿50*ÔìN¾*öõÊ4*ðò:KDQìOÓàï°±ôÚG¼ù-*Ì/U,ÌëÙµûÄ:ÖîL+Ú´I0ÀÞ³íÜ
+E¿*KòÞ¿,Ú;*ÄÝÛ±ä×á+½,*ö¿PìÕ1Ýç¾6;*VÛÇ*,ÎßïJ¾E7¶V/ÎùÏ8ÚZ*é6×°U»ÒTúXôãÊ×ÕQI*Þ3
+=KYSFøò3ǹë--T+úÏÀìÝHKÍü+¶»ºîJ+ì6*ÕåÌ÷ðêõºI+*·ïõ>¾AÄѾ1ÑÜ»==28.0FR*1îC=/4F¿ô
+ë*òáJèË@ÚZ*ZóP,2¾XÂ,+¾õ-½î***¶ðÓµOÅƵٷÌÙ:¸J:̸Ä1Ô÷ïâÔøRÓ²IêÈCII½<ÞÉçáü=0
+óê;¾îÁ+Ó**KúÄ0ÆÌûÎV×--8>Þ7+î÷åCUÄûÌô=*îE¿6ÁUÙôIõ¶³ÌM²é¼:öÆÞ¿,¶*ȸ·Ü65=ë0*Ùß/
+ì²ß/-¾Wµ<¾+¶´¿Ð´*ØYú¿-ÎGUÜéLPÞ@öæGß-ÜäØ´*Àò¼Ä1²×¯âËÒTÌî³ãÉ͵ÕXGSYHL64,ÎT»,ì
+¿+J»?¾ZJ¹>ÞÒEA2äË,Ú;*ìüú°ãÕíÛ¼-*øCL=ñXIB0ÁÒYõ+Ó¶ÂÞ¿,¶*3@ݼ7×Àé0¾óé*8³ß¿,¾Xµ<
+¾+Ú8ç,±*ÕYú¿-¶»;ÏÎÖäZ?ºØHßÁ;,×-ðGH¾â7ÓTH×°ðèËEµ¼=Æ3.îÔLCQÒW¶êä¿÷T*QOåðïÓËE9W
+@Q2D¶Î*µ.¾Ó=ÕF÷ó¹ÑY+*4ÜZØ,Ï?+òCÛ?µ4Þ+R¶ºø²á4P-*K+åØZ0*¸ÛǾÎ+,>5¾=½î*Pé?ïéO³é
+Æ0ç4ìî¿Ë0ÞÈÒ¹ºæRÓðÖ?U@8ûöïáã:Æ¿*Ì@ÀéÆÝ6R¯F6Y/îÔMSR¾áöïA.,¶K-14îÄ*B¸ïÊAå¼Aø3*
+¶È¼EŲ;D=DüôñÞÏMõ»5KÆÞ¿,¶*Ų?X6éÏæ*JRÞË@ÚZ*æóP,À0Vì/*LÝö-QZ¶ò5A@1ûJ<üÂ1BD*ã
+M2Há-ÁÚö¯UÛ²ÓR÷ðñëÃå7*꺶äȯõùú.*ºÈ0,¸Öúº¶<ÍÎJÄåÍUFZ@NîJ+F*ÞÂ:N¶>ö±*:>îP5XB*
+ò¸=+M-Â8¾åAüÞGOö6ÇÎåÊA2¿Í9ïK2+WóZó?HòÑT¯ÃÕ=UPX»öù***4*<Ö+XHú°ãÕ3¸Ü1*¾XSÞ*²Ì7
+9øSÓòMêÈ?5ÅH¿FOÑ,êÚ¶ãÇYU.û,*8ú5*¶à+CH*ò¸ç1÷ü+NìPÓÐA±YÜFÚ÷âUÃÏAÕVCôÅÐõ+*ÎéDö¾
+@EÄÚÇTÿðôÊ·öðâñ0*Ü8ûðã×YÌÜ1*¾XéÞ-ßú¶AÓ4Q/Xø±ïäÍå7*ÊܺäÌ·-W»9**ÛÔ¿1ÐCðXT??5ú
+³çßÏQÜTFIüæпQ³»9**ÛÔ¿1ô×°âÞøRÓBZëÜWù0ܹì*ÞðóÝÄ7ÚVêù**æ³7ºZÐE±ìØÅT/KÝôËÙôò+J
+¹Iõ»Eîý½***ÓºT/üõÆõ³GÁºýÓøüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûúÕ4*Fó**ûÂ,,,XðÞÑ×èÙ¶¸-,ý¿Ò@
+¯Ó8Jß0ÖÖ1ØÇG°Õ.@¹*+*Æû5/4êË.¶ô*¶ÅØZßÒìÀG¾3,áX7À-9OÓ*Ó5*¶ÎTÜìò,êQPàXÙ.×HF:JÞH
+IÅFß¿6¶RãºíüO4¹*=.Âû+æõôÎË9îìÈ<W?öý**8êçZ4øìÞEà¶á+4*¹óVóOE@2,î4¼±å¼ù,ô*P26¹K
+×ZÕM²´*êKÓÌNÞ/ISôê=Ë6-÷¿T×Pñ.ݼß,ZKÒ4ò8ÓT7Jå1À·O2ëV+ÉT?Ø×ÂÊð*Y¾ù¿Þã÷,îG:Þ×-*
+ãêµíâJÛ2,°û·ÀÝÑTNºÀÂã+XéÀ¿9Jú,²QJ*±1Z¹Ñ.5LÃXÓ:Î52è»CÓ¸ú*ZûW.òû-êXÓ¼Á6ºýÓøüÕø
+ûYóùÝèõ½ÓíýTÙPÓ.DI¿*,ÒÎÑÖ**ÔÑ=13¿*Z1ûÂ,,,ô-6»ÒT6F0Îê¹93G;2ë2-ìÎLEÇè¼Tã×ýâ¿FÕ
+Äê·áR5@Êè»ìñJºF¾À7Z¯åüÃ,ÕÞ8ùJE×±ÕNßÄäÑ*ÆÖŲîà5*ùE¶Z.G.+úà28êßNß¿?F*.ß8J*Þ¿R6
+.**Qîî.5D´ÓF¹KÄúVå8TCñ»ùASÕTãéËX¾Gùåå;ù.êQPàX¹ÆóÆRJñTÉFп5Y*êíÞ¿ãØ7ÏË5M¶Z1=/
+1Ôî*:P-XÂLCá3FFßÀÌW?Ý6*±ôê?úäÌÖíÜîTÓP¿Ú¸ÔøÐ+Þì¹-ò7¯Fô2,BO¾òµ²îà58ß¿WåXº¿5MRJ
+RS¶Z/1*¯D*ÖI2ðÄM¶¶¾/GÙTOJZK61òHÓ,é;RV¾³DQöó¾*̲°6âêCÖ»ã/*ÞÇAÚÀ-àÜÙÁ5M¶N±A>
+Î*ÏDîL>Ä*ìÂá/@²íT·Rä¼ùP04ù,ÀDÛUÓð1,Aå5¾µôE»3Cê×ÙØñ·¸ÎûÆ0*Ò-Y¶-12*ùðëNÛBÀÙÓT¾
+25:/¶Ý2/U¾E¹*A,ÏÍUãµµÈZK>ìêYÇ01YÓÈ9ú*àAJ¹Ö5õ3<°åìEÓ//JÅMD¶ï/µî*+ÎƾÇô5M¶ÙT/2
+¯+LéÀ5Ï*ØàRÍÞßÓAòéTWå,<²;Öü@,øå+¸XÓ¸63øNÓøó=ÜýT»ýUûüÕøûYóáÜTùÜH¼ûÛ0*.½ûõÛÓTì
+GºøóûÝýüÚ:9Ü×*Þ»ñüËùõîêÛõÓT÷òßÁý¼ìܽûÒÜûøË/*8H·êÓ±åÌëEJíTÓðÚYé¼<E-ü+êü+÷5òÓ*
+ö-ñµÊôà¸ôéò¸÷ó7ÓTÜC´éYL½ÕÍÝòåÝÛ*ÞõÅÝË·ÊéÖ?ÉçäETÓúíéÍ9¹ÔìܼϽ½µ+¾éÕͼ¶ãÅIÝP½-
+ýìRÓð²UUäM×·÷ó/üúÛ/*8½»÷ß;ñÏÚDÏ˹¹MÓÈI?Û¶YáÜÛ½AòåI,*G5Æ-+4´Ú,¿50*Ôͯ*ãÙZë+.2
+*Á,+âG*ÅÛî@.X8¿*Bý0>,,Ð-.QýÛùìÄù@@ËÌÜÓÛÏTãYÀ@ÚæZëéÙ-öÅ*¾×Ð*¸ºùíÆÅ,*ùU@Jîß-¶Ä
++Nß°.1:J+ÆÖ7HºBõ´:Á/ÚGÀE:ÆX9ä*ºùóØ1A<6øãÔT?Õæ·ZÚÀ±EÅ8ÒI,*Sáñ/.¶*ó@ºÜÑ*ÆÕXÛøA
+4>ÎÑ8¾**îÀ-P*æÀÊ°X>¿é°RÛWY*ÒUYN¾è+:âîè/BRó¾Á²*JûÏCÂ;**Ñ-DRPóè=·GÍ**ìâÀU1JEÒ/
+5Në¼ï1>7À-ÕLð0,F¸ñ×/á7ê×àÔT?9æ·îM¿W1I8ÈI,*¿Zù/.¶*î¾5è1Fç<ܺ÷âÞ/QRÜÚ*RüÀ-¶÷ðà
+AXR<÷Ã<æÒ°Èýø4Þ7<ζË5F+-Ï=ð×CÂIÆð*P1òMMãS-KHFî27À*Ò=¯ÕUõ,óà9â+ãÕJðR,æöîÔÁUÂñ
+¾WÓTü̱޴+ÑôëEú¼.*,Kó52Z*¼ýÄ:6S-îìWòF°¿+*Þ/1.7îSåÜÐæFöÄT,ìëÀYAÜ+ú¼À.Þ¹FßÃ/2.
+ÖÍËߺ,*BG¾ÐCÆ0Rë¶Þ;D.+æ.ѽýüVß¿:Öí¼R³¼?<µ:Þ»ùP7ÖWÞÌí³ÓT·´Ì+Mø¹DµìÎù;*2B<YJ*,
+æÛù,Ó-+0çDÙÈ--.¾ì1F:.¾7ÎìëW;ÎÉ8>+/MÁWêÞ7<FØ*¹ÞD³*A.ïà=82¾;Èöãì×¾¹²Î4Lõìû×À5K
+J×ó··?<µ:Þ·ñHûA<ÞÌ׳ÓT+¯¯ÓèÕ²ê×µå¼-*Hñ-ÂÈU5ÜCÙå0:¹ôQ´úØôDZE2¹¸U*ÊS´,È9¶J**½3
+OÖóE@è8/J*××,¾E4î,3,7*¾=ZZV°°-îÀ:8Nß¿8Öê8ŶS8:.>ÝÇ/Í7JÐXËCíÈòL22¯Ý28¿â7.Þëׯ
+ÉôEÔ4³ÓT÷VÎ/EøEø³êå*Þ·ÔϾJ/IÖ¯¹*Ö¯YìNK¿78ùG19Xë¯-ÙÆÐî¾-,*öíJ1@ÚׯíRJ>K.¾*¾û
+,3À-*¾=ZZ>Aº¹îÀ:XNß++Îå2ø?12S4ìâÀá0*×;.4>O¼;.Òà+³åÞîP*öóæ½730GUUÓȼDëöXÞÎíÏT
+<6¶ÆYׯ*ÓÔÖOHI¶-4·äÀÄ8ºÕïçÍØTÓÚ;èÊù20û¸´È7ùó/***¶,*ÕµÞ<2*àò0VïH/FôêÖQAìÁ·³ZÒ
+ÕÔÈ;ÉÄÊÖñè×E6*úôëÔ?U@GCÐÔT?ý@?ó@ÈãƼà²=ÇÛNY/¾5÷ëÁ´AÏUUOÖðèìRÓð¶ASP»ø±æÑç³ëÚ¹
+5*úµôâ·éôÊÇK×ɹMÓȱݶRçâÓWÕ@ÓWõ@*öêÜ»ù8˳ϱҳ¯ïGÓTÆ»W¯´ÒòD*ZDÏ4¸ëéø÷ÔT?ÜÜúóà
+¹ïÜë±*ζõ@¼ÛúøÔT?ã¼G¸ïÚ³õD*:ìàÏí=Ó@ÒT½á,ÜýT»ýUûüÕøûYóùÝèõÐTI/*@ÎTîýU+¾UU¸ØT%
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+íÄ,*ÓU5Zëúø³óûÕèùÝèõ½ÓíýTÝýÓ¼7U/*ì:ÞË0ÚØ+*û²4B¹Õ¼F2ÎJMT33µ¶R3G½Ó
+1Uó¼1ÌO+UOßñõòÙ5Q?;»TTE×T-ÎZ-V-+*ÞF¾1ØÇG¹OZ×-2*îÏ÷>*<@¹ëÊÂ,4?/Ð0æ-´ÎÚMWÖZTõ=
+³B/¶*/Æ»±D.@6F2ô*ì3+SÁîJ±Õ4θ+*ÕTÃèËXÎT³8JQPàX¹ÆóÑ+ÌGϾäÓ@ßXȶUüJ4.,ï1*KK?ñ»
+ùASQèZ4î*46ùJòÈÝ*ùL:1JݹÅÔ¸¾¹UȲÓÞý-*¶ÓTÁRñRñ*æ¹-í2áÓ02ô2àGW¾ò7ºÝÆ,¿Jú+JJ/Ý
+×±ÝGRøJ7P+8J+¯Å:ú3RÚ=,<>5ôDÆ0-¾GÒèÇ.¾-Kɾº?óÃTÓ-ÖN=áJ¼Î+À¿ëS,UJFùóÓT°På-´*5À
+ÊJõ:ÑM¶¶*/»ÀìQT/@F=,¸¼:ÊUüæT4ÖÉTSIð¸=åAäçTÏËOâÂ7¶*ê5î´³åñòTEùD2·éTÓðQKÎâûFó
+Òê?>±Ý»=-óP7L+8J¿¯Y.Ì÷¶?U¾E¹*õ¾V½ÌÎëU/CýÇÞ*͸ç±ÓLà=N6Eî*ÖéØÞEÓ//²1ô»½¶¾ÖTã×
+YÓ±K¿5ÂÖáëË<V2?òò288J¿±ý60+5ê×é4M»Q.ò,À¸ëÜTCG*VÍï3̼Þ4ìÎßUó+BßùYÓõ½ÓíýTÝýÓ´¾
+T3öïÍ+*¾ýÏTÃZõõý2*.½êITÓVïûü5I+*ñí½ïð9TÓZWüü±ó¸+*äÝýØÒIÒTïÏúû55:*ö**7:¾½ýMC½
+RÓÀ:÷ùý@Þ¾è:¾ò1÷NÓ2ô,3M+*¾2¾º*îÎ12*ôÎñÖ¿²B:è:S@òÕT¿¾ÏT=T?UÀ½Þ=ÓL.O¾ÇXÓÐSÔNóÎ
+YN³IÓ°óôùýÔÎDRÃúíYõNÓÊÈÐ.6ÕT·ìÏ@VBÓ¹4ÛÈÛ=Pê/õCD2-D,J/EÞTJB1î¿´°åX3Vô*,*º²H/¾
+ÂãXE=Ͼà¿?ßä²2VÞü*;ÌÊß>Þ+=*ôÓ-Ï0QÌïÀ2ÆD.1D°ÓNØù¯ÓQTOòQ¹ÏTæÕ2ôÅÚ?D·RãºíüOTÖ¿4
+GÇ4è/øEE**82*ÓEÛJ+ßF¾ÌÊÂÛô:éQ-F¹L+ËUWÀÛ>ïÔú:K.*0LÕF¹ô12ïâ6P+8B+ZYGR̶Q.ð*ò´J
+ÓëÒÈò°IÔ¸R³VOâõ¸ÏTØÊHôJ¯RIPð4¼±å¼ùÀÊɲêZ¶ø0ø¹´îLÓ0Ï02ä+Îõ0Ü;ÄU7:ê;ºØ*çEöXN2
++:2À1,FJÂ=±B/¶Þ.ÀGÀç¿L¿¹LJÀ°ßìÆÓ°ÐèùýÔL³2JÎɸÎTàºÂߺãÈG*ô*@Âôöì5JÑÎPóGºÐáøß+
+-¾û¸*Ñ*++-.ó*@çâBÆ9²¾ZZ¹Ä¾42å¼Æ:,J<ò8.*54;ìÊK>Þ+=*ôÓ-?,1Ìïà1æ÷¿ZD°Óê;öü+Ôü1U
+;ýÚ9GR?+¸P:¾âD³ÊâKCùëECRê³J7ãíÄBÓ¶Þ4ÎP1ÞçTÏËOâÒ8÷,ÚWë÷FÛ´ôÔ0LìØÀ/L*²;¶ä¹±1ó*
+;X×ß+=*ôÓ-Ï0µÌïÞ1Æóå3D°ÓÖÍðçÓÍÝ8»>>TQÜEµI1TO4×6.J¹;ú8²ðÙEOÖ:4µôÐî<ôæEÎVFJÕDF
+ºÙTNºÀÂ;-8*¹W5ºÑØξôÞÜõ1129Ò4¼0Á¾/ôA2ïÈ78B+ZYG:ê³ÃÏÜ=,¸+KÛÄV?µ=EµìÝÇIYÌÉMåÌG
+¸ò´8Òè<2:¸@ù*VJ7¸SÓNÝAå**XOÔT*J+JáBRíÒȯQËòàM*:NSõ±AW¸ÓR·èÊ9+¾ÎXüMÓèõƾõÝTíý
+TÝýÓ¼ýUë*Ó<îáI-**ýMÓ4ßìíý;*2üÖ½ÒT×àùûA¼,*äÝýàãIÒTßØúûåèó,*ʽý³S½RÓàM÷ùA@J*î+
+*DJ*ýüÅ°üÎT/KðõýW¾+ÓK*ç9ðÇT;ê/<XRR*¾Ê¼¾J±åH5;Â8NX4ÛÏT:ZÒèÂTãÈî9öNÓ2K3Z@<=*»Ð
+óÈOÇXé*JÍä5ØNÓVÛûüÝ?Ò0T;ýùéGS?ÑÐ>+á¿DÒZ-VA4¾·ÅØZßÒìê9:æ¿5öä²2VÞHDÍ,,Nê<-Ò0ß+
+ôêY¶¾-ZÅKÎËê×U,ZM58DßÇMÒJ.>2**8²àÏMÚð¾æ+ÍLA¹QÊÖ5ìñâ7ÂG0K4L°ÔÌT?Çô¼VóÇT3ûçXÒ4
+ø?Àû;µ+FÒìò,A4¾K?ñ»ùASêYÒTÛ¾1LÕF¹ôÂ,DATÉÒC¾-áóAFÞ+ι¼°<ôØÁ¾óÏ/1Ëîâ;T:äÎX²àÏM
+Fñ*1¾»Ö¿æÇÒÎX.ø¹ð0Êì·ô*L=çÛÑTO¸×9UGÒDÔ<ð¹ÛR?YP9¹ÎBÒ±1ZTòJ+ÒÀíê×íÜÎF:.ÎXJ:.9S
+*2à=O¾ò±0NR@ê¯,1F:¸*»µî7¯Úç,¾îÌ3ÌFL/àÃTË/F¸ÂпEÅîCAÃ70QíJ½9ûÁ¯1*æå»Ðè´TãNÓõý
+WÃè:¾JAóJÓÂ÷2¿D2*Uô*ÖI¯>ß¼KÒ0òHÓ+éíÎ*NB¯>6êHÇ.º¼:6ôêY¶Jò*°Í²°6âñ.1Ë*@F·ÂDÀMÝ
+¶ò2åFÞ+Z1ËÀDÓõîÇ8éíúMξ.áÓÓTã8¿õ9Zó½Ö4÷åûæ¿T3α,+NÔSåTãKCùëECRê52èòìξA,庵ô
+Ôôº@ç³ADJéÍ=ZåüÀ-L16ùËOâÒ5G²,*óÞ;8.L.¾>ÞûÞ,8GàC1Â8R7DÈÝ7¯èÓEJô*ÆIS×ÑTãû>ÍXûú
+óæÕ±ôêÓ=íËÙÍ-Ó<ÃV0,ÎÛ2¼ÝT?ÝÛSÀ/϶±½ODFºÁ,üC6,@ÖùP³¿>æÏæ¾XJRAìý¾-0½Æö.3éÆ8DßÃ
+MÚS?ÎóàW9GàC1Â8RSÉÖMí¾ðÓEÂ8:ß:0µÒT·E×·ñüËú÷ðÚF¸ôêÏMµñJÓÔË*ï·×.JLGæMÓ<ûÜF-*¶³
+ÕÓ,*4*ÔØ6WÏTÄG³Ð59+¾:OÛÏÜÑåTÃÈÑäú2*.óðITÓ¼Å.ºýÓøüÕøûYóùÝè0R?+IüC**öITÓ2ôüü9+
+¾îÅûMÓÈ?Üí½ï=*ZüûAXüKÓ4Ôãí½ÔXA*ÞúùYÉøÁT?HÈݽ¯+,*9*Î+,öõ½>ñNÔKÖÛæ?ö×QÄVPÓ:Nüü
+Ý/BÎD.:ûKÜR?óèÅ-+4²FÚ0ñ¾C¹*÷Zâß8:*êKå¯-çZJÒK0V¶ä,¿;·-J<×ÖÙT¿¾ÏT=T?UÀ½Þ-VßTSö
+úGûôàõS-Îå×Bå.=çTO</CºLÌ:ÇñJ@ÑTÔì¼ýí4T-ÓÆýûó8ÒÈÒ+ø*1FõR5@ÊèËG*YÖ+@ô***ÞH,Þë¯
+Þ8JH.áî3.ÛÌNã2Þ*ßòÞF¾0ØÇG¹OZå5F¾æº4*3ÉÛ´àN+Ã-Ï¿Ëç:Ö¾ûCTF2Á-P*êU1ÈCì3+SÁîÞÃ4C
+À8À*¼Z/Ï.QÄJ꯶ß1;*ôÃ,/ôX>á¿PÚÎ+*Zðî,Zè>Æî:8°CÔãÒÈN¹IÔøNÓÂü²1TO:2½¾-8TCíÙ½Çà
+8*ÓEÛÞ/J·*ò̱1*¯²*Ò:²-åTó,244òâNê³1×¼DÞ×=4æÚè<êO3µã13L*R:¸ãÍÐà>Þ+=*ôÓ-ã3õÂ*8
+ÂÚIÖ0ɳELEAÔ**Ú¯.Kõú/3Â*Öåå;Q¶ð2Ó*QØZðî,Zè>.ÍÈAL°ÁÓ<ÛêÅÓ8T7U3÷Û8Ô4TöËGùëïűN
+¾û+ÈÚÇÒ¹Ó¸+.//æ¹-í2áGÖ,¾:¼æ,NíÞ¹;ßÚ,.½É4Ñ*1ÊJÖMBGR>¶Q.NR@êATãÔ±AÜÞÃ?**µ6;Tï¾
+.>¹ôè×èÉ´HÎ8Ï4îN5P8X3ÃZÍîâ÷´ÖTPT»ýíTÒ0+.¸S1TOäòº¾-ZKâ/òX·ÇNÓÞøÁG2,ZÇPÄ¿úJ-.-
+Ù±.Ò¾¶ôÁ.;*<KõùÒ¶Þ.¾»óJÃÞÛ¹Îñ-ßÚRÓ¶-îîà4*¾UĶK,ÈGùPñ.ÝH°TÅR/¶Ñ/»µ1åÂÀ:>I¼ÍÓ
+°ÅÞù1ÖøIVÃúñ¼òJÓúS2>·IQG?¹/ãêµíâJOÑTÒ*³¸µUô@;ÑÈÞ5î´³åñòØ×ÙD2·é¸ZBÔ2FñÛYÑà+=
+*ôÓ-Ï0ÀG¾¹õJÔIQ¶ÐTÒºESAÔ**Ú°Â5<îê×I2ÚFY*Ü9>á¿PÚç,TÜËÒÈÃ5ÑJZÌØÙåT·IãÑUI½Gøóïå
+ĺµì¼8ÒȱÝTåÀ,A2¯ÅÙEOÖ:èVN8ÓLÕDFºÙTNºÀÂÓ.Xé,íÒ@>æ¿Ûóûß+à-Á¯Íß:ZïD?8B+ZYG¾ê³W
+Þ8ÞG;Î52èÛR?M;+=ëAÔ**Ú°Â5<îê×9ú*àAJ¹8>á¿PFçÀÇÁöÆEL¯LEæÁZËÓð´°ñäûE÷ñã··òê×MÅì
+äÃTWR+Àè°9¾Fè*Èí+LXÚòÓVÎòAßV±ÓNÝAå**XëUÓ@´3*J+Þ,òî>.ÊÀ+MìX±Ó6¹èNAH,*Kȸ=ÓèÐÑ
+¯EÓLãS±¼.*¾ø<Èû¯ÒèõƾõÝTíýTÝýÓ¼ýUë*Ó<îáI-**ýMÓ4ßìíý;*2üÖ½ÒT×àùûA¼,*äÝýàãIÒTß
+Øúûåèó,*ʽý³S½RÓàM÷ùA@J*î+*DJ*ýüÅ°üÎT/KðõýW¾+ÓK*ç9ðÇT;ê/<Ä,**;*÷*ÞK9:*êKå¯-ç
+ZJÒK0V¶ô3õíîÐEìQàã2Fî¾²°¯CÓ,+MÓPÒTÔ.ý¾QTÃ2È*=ÛTO-BÇèK<SÆæ:T1*ø±62.ƯÓÐÇÃãÒÈÓ
+ÙíýÙÀèJ?ð½½Û=Ó°TÈ<ZÚTã·ÅØZßÒìSòTôÌB¯AÚåÏÚB*/EÞTJB1î¿´°åX3Vô*,*º²H/¾ÂãXE=Ͼà¿
+ÙÞä²2VÞü*;ÌÊß>Þ+=*ôÓ-Ï0QÌïÀ2ÆD.18æ1*¶ð,å=ÏìJ꯶L,1ÜîJ:Xö,Z½+;AFî¾?6*ô<±ÛÈ/.¾
+ÎÁ:¾ÕÔT<ëûÖè=Ó<øÑÛR?òU.ùMì4ËFҰܵý<?êJÃÌN/ó,ûÛT?Tµ¸¿,À·*GC3¹ëKÔÍ0¶ôÂ,DÕØ.¹Sà
+W÷KÀ2*6ÂX·ôê9:àÇCÊ,FZ,ÞܸÎFïÌ2Þ¿=I1*ÌæK,Ç2,:òÌI;¿=ECD¶ß5¾û/ÂÚî¾-ÔJ*L=3O¹=52J
+Ôë2ëVWÓNAù¯ÓÁTáTL¼Õ5ÓÐãò¯14Áø¯<ÕîE¹ôÙAâÚNá0ÁѵÎÝÕT³2³¾Z8*»µî7¯Úè*ß8K>Î/ÆÜ¿õL
+À2*¿:º:Þ+LFCÁS¾-P:ê;Ê8>4Ú=,<>5ô=Ó¸ï,3NL3Þ33FÇÂUÜ:ê×I¾Æ58¶Þ4.*ÎÍå°;>+ÂÅÎWVÓÊÒ
+øüÝÓR7,2òÐ8ÒÈB9¯B9Ö8=¿/¯9Üù,æ>ÒS»1ÍòÖüJXÞøEÏ3JR1ZÇPÄ¿úJ-.-Ù±.Ò¾¶ôÁ.;*:ßòGVRÂ
+¶±0Ï*1Ê*ÖÕ8Ò2F¶Ä5IJå1À·ÐTAò:ÇNÚ<¿M>¶ß5ÀGù-2·ß+1V¿*.áóV@à5Nµ1AØZQÓ°ÅÞù1ÖøIVÃú
+ñ¼òJÓ.æC¿*<éÒ¯<1ãêµíâJ¯Õ+³>½EàÒà+T¾DÛÙÓLEÉÆSSï/¶ÙØñ·¸ëêW7ÂÚ³GR?W.Ì÷ìêKGJÂU´¶
+Þ.¾»óJÒß˱Fö+°û·À5>,ÚYæßûNÊE*ôÕNîJ<Ä0XöÀZåü*LX¶Þ+UB*âÍÒº?52*ùðëNWWÓÖÍðçÓÍÝ8»
+>>TQÜEµI1TO4×6.J¹;ú8²ðÙEOÖ:4µôÐî<ôæEÎVFJÕDFºÙTNºÀÂÓTXé,íÒ@>æ¿Ûóûß+à-ÓÀÍß:Z¿û
+/LÚ°Á-P*êU12¹W5ìFá¿9Î52èGæ3ϳßÃ5ÚÍ·ÁRúE<¶:3ÕºKÖ±½*;AFî¾?:*ôÀK6¼>50*Ã.Ê4ÞÔÓT
+ìP´ìÚ½=½ÜFAÅÌG¹òæëFRÓO:JòVô+ÆN7<øÂáGÆòIÌNß+üëVÓNÝAå**XÛU?*î¸Fø´.ôÕTÄG³Ð59+¾:
+OÛÙ9?A:êPÓR·èÊ9+¾ÎXüMÓèõƾõÝTíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýT9ØßùÝè¼V?/**¾+D¾Pç8*JÈ
+Rö4T/×±9ìÐÄêà+ÎýFÓðêÅEÞüÃ,/ô:¹ôè:F28R/îSò°õÂ3¹ß»FõR5@ÊèËG*³Å¿´P4RÂ@²±VWUÓú³°
+¾Ö½¼8·,áÌû+æM,Ó*HêJòNTRAæÍÁTäùôYÐÖ-¾òÌÉðÔú²ßÁ×T1*Þ-QÓÓ±ÓÅÑ+ÞÌÛÎVÖÁ.T+ºäO¯Áßç
+EæYÔîE¹ôÙAZ9F:7ºÅ@ºCÓT/ZËä3¼G<¾Ì9.=BÎ4ÞKÑÎPóGºGÔÁ/ÏTιÓÈ?:ÌûøÅÑTüJîðº+öäìغÌ
+:BîJ+Ú07ãíÄBÃG>²ôÙµ°ÎÄ3V7ÂÚ³»FÓð-I¾ö/æ8ôA.À¸¿+14ÞOWà8»-ÈêµáP3@Âà»Tãøüî*¯BJEá
+èÌTóYºÞXU/*Ú*¾>ÝîßÒè-ÙèõÝTíýTÝýÓôßT+Tý**ì´æÈT/üý-*öüUÓ/5ïCX?
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 151 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+¾èC0F*¿½**µEò6-XW¾³ñéÕÓõÝTë<8íýTÙZ;ÜýÓ´ßLºýUëÀÃöüÕØ/5ïíXóMJÓ?Ôèá
+î¾õÝTíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýT9ØßÑTÓÍ,*5R?öýÓ*ÞÓ?OØTíÄ,ÜýT»ýUûüÕøûYóùÝèõ½Óíý
+TÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕ
+øûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèÔÛTJó/+JHÓøÌ>îûÕèùÝèõ½ÓíýTAÒT¾è5,¾ºT/ü½K*ÆQÓÜ=/
+ºýÓøüÕøûYó-U?ÜV>HQ¹ôæÇ4*öÌG¹Fß=¸·IROÑÓ4æ6X4,ÆXÓÄ*¾ä´ÖÈWÒÈ»Y9ÝüÛ**ßYý»À++ðX²S
+ÈÓ<¹âGØ-ÉÜú¶ïç-*7YHûÔÞñÖ¸XÕȹ1ͼ»ýõÝIAâÁY+*ÚµìÝ·ý8ÌÛùòR/RÄ=;²¯YQöAò¸V²ß/¾ðÇG
+¶ñÝ¿1YXÚõëLEô²+¾YÙ¼ëDôìäA.H3RåïT÷òßÁý¼ìܽûÒÜûøU**1ÍFôè×±åô7ÎQÄT*·>3ÖVÄÉ5Z8ú/
+Ö6Z*Ã9ê¿MÅ7¹ôëßÉ5Íö¿úµëÓOÕXÛD´Rü0ºá.ô*H*ü»óFX?åûØôé×ãñíÍôíͽ/*îõå9I7ØóêÕçóðé
+ÑT?ÑçÅͳ1T=AÞÚö5ñäÅ9Ë1ô޵Ѵ˸óíâÇÁï÷,¾51XE²åÑUÑ0ÔÍîâHÞ=+óFX?í6ÙóåÏÝéåYøñÝ.*ö
+ûïµ±ìúD´ãZøÓ±Q:+¼WÏL¯YåXûDÃåí¼T*îóËÕüʸôé¯SÏLé8ê¿UP»¸ñåÝÅA1âñ/*Y¼úµéÇAÍP7:Iß
+Çþå»óFX?ýùøñáÌÓÙÕYòíÝ-*¶ûøç1ÕúöñéòµîíSÓÈÁèÒ3ïéÒCÁ@ëñQä4XÜõãÉSÝPÚDz,óÞ8¶YèË5
+±äWºàT7,ÞÞ¯IXÚCóÜQ¶°9î;øóIÕ8æôôTGëèÈ/U´X¼»¾IÅø4·B°°¯ÅÝÜúîÆ/UXËæäÛGÓTßTÓTëÃG¶
+NÐÑÍ5ZFü÷ß=Aì6ùì:Á-¹î°OIüùÖ´îß¿öÂ+ÞÁýÄÛöíáÐãHßÇâÞ=µóFX?½T¸ïÜÄ»¹ýXæͽ.*4*¾+Hú
+ýÐ+ð¿òUD2F:ñWÝù*I½Hûõì¹Eì55ɼøÔôÒTã¼ûø³óî2/1Yý;*JÌO4>çC8J+æÕ9ØÇìQTÞàÁý붲2V
+7°¶Z1Üòñß¹3åôûE.½0*âEïèõH¾À1Qúð-úµíÖ3íGú·ç=F·8H¸@:1,>ê¶çDõä=9óFX?½3ØîÚÁ³Yí8Þ
+Ý,*/*H¸ÛK.Hè¾¹***ÌÀ¾Á1Ýû¸ëÁñ4@ÎYPÔTHWزEK9ÔÉBëäÌü7*öÏJ*¹ýù-SøÈH*øÞÏ.¼á,ÜGùëÂ
+ó80»±Æß¿GöÈÌ+Ý7ê¸õìîùC*BèÄM±½1,òEïý1IÞ¹õ´GöWÞÌUH·ÎéîßVS*GÜûµâQWñêºðÏÌáÓð4G1Ä
+U¶´ñæ¿øU***à*úT<èIíS*âUÕ8***èÝRÎ*æ?56+62β28Úø,Å캴߶-¹ÔB×T׸óè³Vî*å9ÇÃÎçD2J
+ÖÔ3¹Kçíúºâ,<û÷L/æBâîÞ8FLä¼ëü³»¹õ¹*îÇ¿A0ݹµàÐǯâ×Í>¾ÔPÛ¹õäÕ93+í´ûÕWÝÊ;HßÇéî;³
+ºÜ¾¾5åï.CÚÐXG±D.ÞÀ5,T:Èø8Õ4/òF¶.:/IÜú?***¯*ÜE¿ðÀø+QØÂ>YHDÞ<-AFK×J3J:¾FÎúê5â
+öîß¿>Öñ¾ÇÓ4.6*¶*8ÅPM829.:ß;ÂÛ2TíXSå3T.*FÖ-ýÅ×íؼQåÔ7´Ý,*µ7C¼åÞÁíP6³IYHØö5/Þç
+SIÍ7C»ðQÂÞ³åHëµWÝÊýGßÇéî;2´Ý¾¾5åïïDARÜú9¶ÝO÷ðÅ.>7óFX?½åCZÙ¾Sùôû6Ë+¾B*±Wßµ.ÏÐ
+<*õæ*éWòF°¯,Q.:2¾úÎèúè5ÚµíÒ+Ù/ê÷ÛâTãB°âæì+¶¾ïµ*J:ê8.îØ=6I8ÞÙ@Ö9×Þ/T*¹K0AÌéCõ
+OÙµ²êÖ±Ý,*íº¸¼çÈÙåP6ýÁÝÛÖû5/Îù7Á1GDüELJÖ¯Å8µ:ÊéGßÇéî;2TI¶¾=å+ÜŶ.,è=-Q.>9óFX
+?Þ@ÆßüÌäËùCU-*¯*¶>Ëì5ÒÜæ*Á²ËWã¯,.JµìÓ*¿*>ÙHAÕÜÞ¶ñHûµR-ZÉVÓVäÊCÓ?¶¾ëÅPMà*7.:L
+áÊÕÇSº8Û¾îÜÛR+Ò/CFÖ-ÝÄ·í׺KÝÄû6*¯à+*¼G´àý¯Ù´êýÇ,Jäå=EÝüæ2ö³æºåôãºë3ÌÚ09+Mð¿
+-Ú·Ì;8¿î·ÂÞ7+ãMGÅU/Â/°JÝЯåÜûT*G¾êÔÓ,CCù=±áÕóâÑ+ìJêçW+ZÌÛ*3Ô0Þ¶¾<ÎFE¯Ý28·+ìÌ
+7Ù²ÝK7JV°Ó°SÑPûó*F8?ÜÙêÏó³ÔHPìÔÙ+ê¼WÅÑEÞ½¿4îUÆß¿G¾Z¯Ó¸õñèÔYÙ¼-*8¸ðûýýéÌGÚ<üÝ
+D±äúÍ>*ÀÔê÷»ëQÂ*YÙXû÷ìÖT2ô.QXÁ1åú*ÏðèÀµH·ÌL8ÊûÅCèÉ93RHè·ÚTD5²ß¶ýļB¾8**+J6àÄ
+»×³ÐÎÞÄæ*ÅûÌÁ1ÕôÖ¿ëHWÖ1Þ¶¾ÈÐ*¹Ã-èÌ¿+Ý8ûAXßαOÔT8AXÚÔJ9ÄùA¾¼Ô1BÏ1,J:8¹JêÖCE3ô
+æ/²²Þ¶1ÁX?âîÞ8ú?å½ëðûʸóU*îÈYÙYÛزáì?¯ãÃ?M1ýL+îýèÄÑͽÃ.Êø±Ü´ý¸¼ó.QÌÁ1ñVM+¿4¼
+ZݶÌL8ÃëµÉ+>ZÝ;κÒñ¶ÓîÑçÄõ,¼ÅFÇWÞKÇ>*ßÞ¹7Íü>×T÷áÅ9UTG÷íÞ׿MÒ:8üE·³OK01ôJG¿HF
+µYååRãTÕHͺ:¾ùìÍöýûûáá¼Î*Øø½èÅ÷5½Ä.ø×íÚÁK¹¸À=çÁå×/ÚÔÌáÓðÄR±8VBòé×S´êÕÍ***¶+*
+µJ:=..*ççÃ-±XëR°ÓðÒQÍÔÒCòàÁã±éÕZúL*XôÃéÔÛ¸ðáÎGULÑQѼ;?HذݽGÑ´ëØÒPû.**ï-*JÒ3
+ýY7R/¾ÒS±ìFý;;öèÔUÁHúÕ°çÞæ3Ìê-º<ÎÌéUãÆOMPê·óêé**ݻٳãÈOQÄ>×T·ðáÅM5Éø±âÐ÷òÚPæ
+»ýÌû÷±è¯ÏÏïáÇÝÖT¯ú·¾ÄÆ´û6*Þý-*:ÔýIæ¾5?:é×I4,½7ùóçÅ9½¼68Û×軾å»óFX?ý@?óæÈCɼW
+²éÜ-T·ìÝ¿é0G¸³Ó,UìÚTãáÙµÝõ<ãÒQÑåηQ/*üE¹ðÚ³¹äÒA×µIü¿GîµQM½¼F÷ñÞè²-Þ¯åÔëØñåÐÏ
+ÀUQ¶,ÜW-º<ÎÌáÓ,¶9:1˼W¹XåÇ4¾ÆßÛÚÑT?1íݽý<1?Ë6TQäKêÙøöÏ×@<CÞ*ÞIÖ-ÅUçïæÎE¹0WRÙ
+ÌVèÍËÛôÔû¸³äÉìîâLÞñܸ8ÕÈZÝÖÆ-TåôFSÚ¾åôëÙõæV5GMW55ÍTÓ.¹ùÏNTöÙHTÒNÇ4*Ò4?Tøº=FÚ
+*êI×-ݶæRáÐQÉ@7PQ5*ØD´êÖIU»Ä=é*Í.Òñ¸Ó6ùØàêÂÞêÂ*BËR/Ûô³ûºXÓ<êïÅ=ãU**ýÏGÎØßêâÃ
+¯åQî<QJëþå¯óFY?ÜÜúóà¹ïÜÛ3*RÚ¹5ÝX¼»°ÓTWËÓ*ìNóDÐð:QÔ,HµÓñºÓ>ùóæÃñÔ»A*JÚÃMÍ8Ó>
+¹ôîéáåIßRCüöéÁÝÎÒFâÇÓ¾åSóÚYÓÝÒ+í½ÓÜýÓ¼ýUûüÕøûYóùÝèQ¹Ó*ñ+*¾ËÝ3*8NTWÓ4?ÈÒRÎIý=
+*ÖÌFµäºÏT4=äËJÁ,ûÎ**»ìºçÐãØT5S<OÊíÇý5+6PÈ<MÔÓèÄ7IÀ6·ðÁ,*NÇ°âÅ9éTWàÂ3?¼S*:5C0
+*ÒÓüX+*ÔHB**
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 302 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/configuration_access.png b/doc/krfb/configuration_access.png
new file mode 100644
index 00000000..c19b5b32
--- /dev/null
+++ b/doc/krfb/configuration_access.png
Binary files differ
diff --git a/doc/krfb/configuration_network.eps b/doc/krfb/configuration_network.eps
new file mode 100644
index 00000000..593f9c4f
--- /dev/null
+++ b/doc/krfb/configuration_network.eps
@@ -0,0 +1,272 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 342 453
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sun Sep 21 21:22:29 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 367]ST
+B P1
+NB
+W BC
+/mask 6493 string uc
+î½*¾î1:-*¾*Ñ-õ0*ÞQK/,ÞÝÑT*ÞñÎ,öïì3ðZÀÉ8*1<·ÈáµÇâS̼*úÉâS̼¾ú+FQ?¾9ÇT¿º*BQ?¾-
+×TKÌS:*0ºýTöü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ½¼¾%%
+d
+/sl 51642 string uc
+î½¼**ûÙ>*<òÌû@KÍÌè6JÑÎÈìQ¼Cîׯù<*VXßÖåYúÓÐïG,9öé@/*GE¹ýüú÷íö¿ñ×,ÈG´éX/*ú5¼½û
+ùÕæTåX?D:ÃïEÙòùżO<JòO*²IUß×ñùñüøظ´L,@ÕZêñ-Ð*ýUÒÈáö7öüâÕÜ,*/¸33DS¾Ü**,ÇKÞ±E
+æ±5¾·õõYÙ³0í×15Æ××ÁÐÞòEÒÈáö;ëüùåL»/*Úñã;ÔØNè×*Þî3+Õñ×7²Ø/Þìû¯M㸽1ĽßõíýôÃTÇ
+æVY**AHÜûúô×1Iï*ãÈ?ñüÞÉH-*åDZڷ¿2¿áC**WØÁÂ=å¹Fæ±6¾°ùµ9DóËïÚO9¯ËüC**é÷Ð*ÈGݽý
+ÂÃTLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏå*Þ³èÒS3>DHRÄ-2G°,+ìïÞO0ÎÞößQá-ëZ-0¼½ÑOY.å³øö
+ñòéé½YHºõêGùòåËE½H¯4ÞÚùù¹ÜATóâöûëêäßíÅ9ܸºÜF¶ê³YV×ÇíÍ?æ±**B-*ú?XËúÞÂ÷Ð3:C@îJ
+/²ìðöûñ½ý84À*ÒúýîJûýý²/X½Jæ8ïí¼öç3Ê,2ÐWY¹+äóܽ½ûQÀîÔ6ðE*êÛIWÍQñJ9½GÛ¸÷ûñݽíé
+Éåüº·ðÕÃPÊ,Ô¹ýüùÀT+ö5Aô»¹M19ýüøì;·ïâµé»¯,´½½ÎHÍ+*Ö6*î;òLÒæÚ<êä,4/ÚXýé÷SD³9Ð
+¹SîH»øýIÆL;*ÎÉ?¶ë+úý½JÛ¶ýíøò.T+JâáXÎòý½Á.WÏSÁÉBÆìD8>*Èì9BPÐ/FÁH*Þ*Þ?øöàãÍY½
+ÝÝüú8½ûõâÉ9YÊ°à>î°ùõµFXýüÁ¿æ,*½TB¶ñííéÅÍåUõGú¶ßM9Î*óí½ñG**È-*Èë7-:GGT4R>*ç-O
+GàÊ<+0ÛWݼÝ7òäËýýµÔ9XÅ,öý¾²üý½±éöν>J¶Þ2J6*IÕÇ>·üïãÜLÃö;Ú3ó½ì÷8Ú¾G¼íýüD+3º¼2
+,ú½Â4ôBüöWÅ5Òüý4CàÂBJÞ¿è½»ñPõúñÁâ1VÏÀ¯ðïÂF<3*NÙý´ÇHÍÎQÏÈ**î2»ÒêïÞÅÉÅIÔÂë½1üù
+¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×RJøõIñ½²*Jð*¾ÀMLW̯8É,4
+/êüýÄ.Ì-¶ëý¯LúÝöNÊøýý¼ýï950*¾ä*æ¶ÙËL+P,8¾¾Ñ±óÙöZų=ºÂ4á-Pñ½ûÎ8Ú*ìýÝÉÒK¯,Ô4üÌ
+ÉL6ÞìEÒ¾9³üQ7¯¶¿PôýýùàCÍ>-X7ôIüµã4?6³,Aâ;3*Þâûó°Ò÷.»B**öíÕ1*Þ;øöÌá±ñôÜÜûùì¼»
+õÚ±9íCï?46½ÛN¿2+¶IR/*ÚK+ìÛ½½Uóéñº**âÏõîAè@ìÄXU²×RÞôõ½×ðC**Æ+*ܯHMëîû÷ÏïVJϾç
+ýIçâ51¾ûüÕýÓ°÷06D0B-Jø»Ó5/¶¾,/,àíü:RÈYTãÖ*öÝüZXÞ²üYYæ¿5JôýSíòBPÞ:ù´âY²ß?TÖ¸ý
+ñÕDYÎ3õìý½Cã,/EòñðçAÔ¯ÖîLHXÆM<*ZËùÅÃÖ¾=æá¼±IÝü²3æ¼ÁHÜ;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1¸
+>=4*ÌÏT>¼úÊ1*Þü/ý»ù/TKëÞÁýáÖRJøõY<»Á*ί*¾Ù;ÑÏLUÈB÷C5R>JàýíU·/÷MÕêýøÂNHî˯¾C*
+Ø9Sâ57¶À,DÝXJ¾*74Z20BµB=ZZýµPòÞ/îäâýÇI6=JZï½EC¶Ã4ôðý÷À4FÎÞºýïù¸*4Nû½ÐÕÔï,CXÆ
+M<*¾ÄùÝW¶ð3ø:òîãÅ÷*òÅæáýû@ëDøõîá¹öíÏÙ@ûùIÀµRî.GíϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæ
+JÏñ0XøÙÀÃ,ô¼ýÈ9Í+¾YÚ¶;O=3AXÂçP/øàRZJϾEÃíGSÉ;¹äóÕý=VùÝXL99êäBÞ6*O½Aö²*ëýÍí34
+Þèô/Ìòø4?úýýÚàÄõIXÓ-A¾OWüYÝSÊ*YøÌ·ßúýIÛL/Ùù½íLß/VÔäÆZýýHòJCÒüýàüýÕòâDXâá/·N,
+*B¸ýÑ.MÜK9ÞHX.ÜÝL9A1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿=Ó¸Å,ðºù;¿USÞ@å͵?»øÇ1*JéÛ7H¹µò+.êG¸
+íàí,5ÖRÞÊõYÈïC*æÂ3Ï3¯@¯*¾KÑÓ2Ͼ¾-?î;óTå½½»ïö¶¯ùõA±àÃï¿ë6-Ê+¶×óõüÅ92MçàÜå=>Î<
+/ÑÝíûÛMÒîýýýÕíÙÜJ÷8Ú*ÈåIÜKL-16ô½Ç¿éýíö0?ZýûÑIç4XÞøùù½ý¸ÖP1È¿3ÝÕêD½Ý3YæàúÚ:Å
+N**»õYSîá÷Ë-òKëÝÁ½ÄHÜV¶éÖÍYåÜÛ±ÝüF´éßÉûÉ3/8Né;4îÇíÝÙ°ìFâ-*îý2,íG¹öò*ºúõæÃ1YR
+éÝL+ØÚIïCÒE-¾07ØEâZ+¾L+AÄ0Á0YÖ¾ùÙ?Ç:KÅL?òØ2;ØÓZà¾*Î;¾5-îKë/úíøÎÂ3SALãÌÎ*DÚÃ
+ÆÎOÕT¯îYN×5ÛRAZÇ.OðÔÌJá+øô6;JAR;Øâ,3¸³î¶¿8<>RÏTûX>ÁDEÒ4@K×̺á++F;Å>,JEäíYXÞÄ
+åæ=Ôø2»V@îÚ³I¯Sá7º@¼·Ï*ZIX»Á,6иóÔÂ5íö,*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2ï?*ÖÀRà>º+
+*Öè/>»4HÙ¿6@JáíÝÍÙÕQ×KKLG2ÓÈñ4Ð<1?ÙIò´+CÜL9+*ÞCøÖPÞÁù¼,RPºöPøÐL¿2VöüT4¶áñåW÷
+ôWï/*¶ºÏÂü8¼º¸öʼ¸ÑÏ8WMö*/2öü°ÍBè6*7ò´î3*Þ¯¿,7Dôê0ÐÖ*ÂNÆÎRøÐÁÀ¸J*+Èò4<<1¼¶»
+½Ìï,PVÝ·ÌH¼2¶îàÉMÍXÛYý¼üøðâ1ÛÉC/ÒLïàWãÌûNîÚKµÇÜ+ÃVú±äØ89LØìCº¾,Æ´½ø9-ôV*ZßûI
+1UQîßFæáó*1Hú¶ñä͹òéÝÉíÜÛ̸CѾLòëÙ?éÔí6Ì1F149D°æÚ¿¼Bó41î34JèûÍQ.ÖY+¾ÝôíýYغ,
+Yúä³ø2T°Ì+¾öCA±4æÀ×±Ù<WøË繿ýêGáÃ4>TÆéC¹ý.HK+Ó2º½ò>û³T*¶¾ÁùõACÊÚ¼Q¾ñF»RGWË8+
+¾æTA³4ÞèÓW·ôúÈïúÝ»ôâ¿=åìHöõè.BMìûù±8ÇYÁ,¾5ÂT»ýü<¹¯-öX/å¯øμñÜG,*CÕYè>¾ÎKÙÐV·
+TÛñû4áíÝI¹âÄßéÔ.*Ø5÷óÆ6ô6ÍÕ³óôóÙO1áãÉWC´FAîß»55=íÝM+ÞEéÊO6E¸÷ôW=ÂïïáÔ.5ÌÊ3ÚH
+ºò68OèAYßUµI,¾µÓS¯CZãÔÃ=ôòë=ÓBIßÀW÷óÙW°*îêÖS¹ÔÊÖZݾռ.*ýó5ÖZáÊOÙ0Öã@Hé:,ïéõÒ
+4ZýÖ*îûÕèùÝèõ½ÓíýTÝýÓ¼ýUûüÕ¸?½¾¿õÇøáQø*Z½Në5úÃÒÈÐÂ/AùüéÑ1*¼¸UU>IÆTKÞݶÉý½+*
+ÎéÍEÅûÉíPT?áöè?UG**0îêñêµÎT¸Ý·ïäéøéMÓÀ6Y÷HHù4**¸ëK84+ZÔëãÃùÝ+Uß÷ÚÉíÌÓÅT/YÃ@»
+ùö?**òØÁF>,BUã¹õÜÀTßûØ·AµÓÅT/W³üÚDø?**ÖØÁF>,JWãYáä½-ÓÀøYÝXXUAÓ4³Hûú4**VëK84+
+>ÖíÑUáõ½+*¾öUÍü7V=ÓÈWäV¸÷?**ÖØÁF>,R±ÚKÉPÝý*¾*æ¶2Î>:.*¼I¿¾±1¶Ñ-14*çÉ0ËÙ×PT?ÑF
+÷ñï1*¾J=BöL,в*ZóP,:ä9-6Ò¶X.*J@¼Îê²µüVüü+*,Þ¿õJ¾Ð¾ç¾=2¶ò*îüÏ8FR*>ý·±ëêDSÓV°â
+Ë¿I**èAÀÉ53ÆÐ+¾ÓE3¾9²ð¼»¶ä»îÞáãäê·¹úô-*2¾±É,+Ú»ë¼6.¿éH*1*ÞÈÞôO»°í=ýSÜèÀ/ÕÊEù
+*ô¯2G:A·7?FB*ÃÝÛ±ä×áóÐTÏáÊ»I**Å°ñ*ÛÜ»18M0R¶Ï*ÜBÌ,HÊøôËÊJ¶:¸îJ+84*Ö¸=+ðàíS¾éZ
+î*H±ÐJ¿=æÜ7¾¼6NGó-¼Aù°äêéÑ9*î*ÊáRÈH*øÅVKõYÍÂ0¶øâ8ÀµÐÀºÁXî>²ãÅ5UÃÜ-¶õG»*ÖË>IT
+¶Z*ØH¹äɱÝéNÓ°Æ7YÔÛ*ÞÝ,ÏWÆCëØùƳÒ--T+ú͹ӷ8ÏWù+2ººîJ+88*ê¸=+¸=Ư¶³é¼708¾ÐXÌA
+áàNÙ+Þ´¶¿Ñ¼ÎûZUäúúúS21¾ûQ6Þ+ZÅ:ÊT,N97îCAÃ70QYK½ÓÀíê×ñÜ:º:ÂJ,÷ºã@¶ü·äȯµÑTÏàÇ
+YA*öWíXŲÏøó-Î÷ZJ?*FBâ¿ÒYõ+Ó¶ZÞ¿,F>*æóÆ,î±ÂPäÛ¼708JÀãÕYMÔîØ+ZûD¾Ñ¼Þ.YUÜúº¹ó+
+*8¾¸óÅÄ+ÎÌ+ÎG627îÍSNÁèìÛ8çóÀìèÓñÜZºÞÒ-*´UïÞ+,ÞôåAU¼ÛÍTO:ÍH*:ÌI.3K*CQMZºDÎ4J>
+2WCJÍçW·Ñ*1/*¸Gá*<ºßòéZî*>FH»Ø¯/Ñ+¾Î¾Ñ¼Þ.YQÔÊÚ¸ñ+*8¾·ñÊC*ZÌÈé׺º³ñä3Úôä³æ*X´
+ÂRäÓѯر1ñ²º*Ë-îëNâÏÀ--ZèÇUÌÚËTã8AÄFø*¾²ò<2ÞGO4ÛåBäÂMÁæÓ*Ì3ËÙÍ-Å8ÛÕÚÃ:²îJ+84
+*ò¸=+ÈÐíä;W¼708Þ;WÃ5á¸>D*24¶è1Ê:´ãÈÃMIH***Â+¶¾*ÄÙ8OK+8¼ú¶U4ĽÀ²Ú-åîïöçÉ<R:Ì
+RA9J*áõÀÞ¹ÔÎ0*Ï5康ôÄT?öº*Jø66.îN9:´¹ã¾3ÍßñT*IFSÀ,CH½XK8B5D¶Î*1/*¸ÛÇ*Ëп¾8@I
+Ý0-1*B;.38/ü6*./Fó-PöØðãåÙ5½6*:úó3Fê*³HF±ãÖùóÑT+åU**æ³1·R.î4YQÔÊÚøîßO¶*A*:>:
+M+æðßAU¼ËZEÓÈN0G-*FÎÌ>¸:.*¸GÊÀ²Òîú.òMêÈ?5ÅHSOÛÐ*³XÚðâÕ?´ÐTCâÄAÉÜ,*¾X¹ÆÁL*»Cù
+ðãßËåÝT4»·ãȯ½êOÓLÅCÍÜ,*¾X¹ÆÁL*0ÈÙ±äßÉ=ÍH*ò¸òÉAå¼×ÉTÃ:±X»/**Ûô:1Ã*F?¶çÍÃE½H»
+*RøðC¹ä7²9Ó4*5Ó,ÔÕ**Îé8ðÎ2Jã,½W¸¶ò÷ÕÈäÅIزîSùRÓZUâÆ?áÜ,*¾X¹ÆÁL*0VÙµéÜ¿õÌH*Òº
+ûÕ¯-AÙÅT/üõ,*JæÕ»ñÔ»-*¾ÛÍùÜ7¶Aê/ÎÈRÓ¸âùØÌKÜýT»ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼üU?*28,F
+ç**ûÜ2ìJ*,*Z¾0ÎÕ°îÜ5ÜÁÓ<*8L1,Úç¾áJ*æíó*/µSá÷6ú7U?á:/,RôÏ÷>*<ôZÒQÏÉÛ=Pê/õïÚMW
+ÖZTµô*ÂG0Ï-L°Ô-U2-:á7FÈ¿îÀ½ÏT¾è/*Â>÷íA¾°1¾GϾâÚè<Ï+ֿ̻æÇÒÎXÎù-ÓÊõëÝN¯õ*æGå8
+J²<ÛU7>*ôêY=-òV0XSLÝHÞÛ?OØTîý+*Ê>SÎB¾-Þ8KÚÑ.ÆHDÞ±X4E6ÌÜ¿üT/ÝױݻKÛ3+*òñÃéL**
+H¶TûV0Ä?¸A*ôÎTQ<:ZN9;-¾G-ÙADÞKÑÎPóGºGÔÁ/¯QÎùÃGÇZÚìVK.*V¶?-ÕNÏW?ZGÓðÏE*ÞìèÌåç
+Û´ôê5îäXÁ0Yü´ÂSêKCù˺=-³º*×-JüNã=U2+*Î8ãJ¶ß9S?åÌ:*ôÜSWRÞ¹ZåXé,ôÐ@·ôæEÎâ¼¹Ñ.²
+K>ì1TãÖ50¯öÇ?ÂüIÈ¿îÀ1ÐTA9*ÔQê9C¹ß5RÓ1ß,í½ÓÜýÓ¼ýUûüÕØ+TOÞŽ0**¼Â*Ææ3+æÌÓLÖùù½
+.*ÀIôTÓå²ÓÔö¼ýÃÍ*¾÷õݶ÷Ô2Ë0ÛTVÉÝýVÛX*¾÷õÝ@ó0·ËÔV?HÈݽ¯+,*9*Î+,öõ½>ñ1³Ó2<½ýí,
+6R7,Æü:MÓè³2ô,3YT-*ÆèL*òêLîP48´,*¶÷+»×T:ZÒèÂTãÈî9öâT3K3Z<Ì2¾ü<È8ZS,*¶ù,ÕTÓKÕ
+èèëõýWK³Î4÷ÝÝìÇÓ¸?O3BäÌÛD.îJVÞ¿,J*ê/1ï:öá*>ê*Uã¿-JæK2ÞÚMWî5Póä8¾G0**¾È;ùL¿,¹
+Á/=O,*¾=HÎ÷:6Á1KÕ5GBSÎ*ÖµPÁÞÀMXÞ*,ëâÌ»Ð×ÛKTùS?Çô¼VóÇT3ûçXVÓù?Àû;µê×IRÁ½Î*0*¹
+H?UÉRÛç/è2F¾VÙ;Ø+ÓʵÜûÉ>ô*æGIE2+ZÍÍMÌZÖÎÒ*ÖIï+ÃBD¾ÀÎ7Úé+4ÂX÷UXÎ*PPàü¸ÇHÛTçã¼
+VóÎèÖÈÆíóàT»C»ê¿àÎÌÛ:Ç/¹ZÞ¿,B*ê=>Y9,éQ+0.ú¸ôà5RöËG¹×»KÃJÓ¾SåTYØ=Ð4/¶â-K*à*ù±
+G:2ÀIñ,/Þ¹Áì-÷óÄRWRÓÊÒøüÝÓR7,2òÐ8VÓB9;ÈX+ZãüúJBÏ+14:8ÂÈ91Z8:>ÎT/×±íëßÀZ=æûTÓ
+VÑ·ÀYÕ:¾óÓ*Zß79ÒTÛç¿CDæûÊ5>*ôÇN´KÛQÕè8¿õ9Zó½Ö4÷åûæ¿Ó4αL0ZÔSêW½8H1ê9>¾,Þ8äS¹
+*ä+À,8ÒÈAíP4>±Ý»=-ÎT.N+ì¿*üJîðº+öäܾÎPAN¾4î0óÄ1TÓÌò*î88ZÃÞCíû..PÓæä8ø-Ò´ÒTì
+Q÷òèåYż»²Ú;íËÙÍ-U?ÃV0,æÛ2¼Ý¾ÎXJÞ¿,R*êôÎ1ßFJRJÓÈÑƾÇôëË<V*Ï0Ç6¹ôØ@.,ö¿9Jú,²
+1ùUÐßß:+ϳß;9ÒTÛæ¿Æ,/¾û±B.VFïÀS?<H+æSÃ0ÛTµ=EµìÝÇIYÌÉMåÌG¸ò´8V³0+JçYÀ·Ó+1´ó4+
+LäX0Þï2N;/ÆGùéEÌõ0RëRÓNÝAå**XGÖèù.**4*X»Þà>MàOU0ÆâòØWÓ6¹èNAH,*KȸDUëðBÖËTÏðÒ
+CI,*KÛº·¯E.JÁÔèõƾõÝTíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýT9ØßùÝèõ½ÓíýTQYô9*õ;òX/¸°Ó*ïë-*
+FíêÓد²¶ëÙ¿1åG¹ôêÔ¸²æÎIÓT÷æÐY9IXÚ¶îPE²æÍIÓTßåÈEùHHXã¸YÌê÷ÑT?ßV¶:*.2-JÄ/±ÜìTS
+àÂ3AÄúù?2¾ñõËÎßT?B=LßÉYX6ظôÔ9ÙáÕ×±½Î*ÂûïÛ¿TFJ*ôê-í»ºÝÓÐýýñͼ×ú=8Zé×=ôüü3DVÓ
+8ÞÒùÜ8Ù³ëÌÏMA̵IR*áÌ»ïÚÁTDJ*ô¼ñ@ÜîT/,¶áöOE´V¸¸ôÂB´áÑ»U¼÷7ÔÈAÜÌÛøâÉDZQêDZ¼Î*Ï
+Tî´Ï402¾õëàEX?ÍÜD²êÓéååWYµQFF»ý¿ÕõìÎëTã5QHGG¹ôƹùóÙ5AÔÌG¹óÞö¿,Âð2Ùç9**ç³Éö¼U
+ã3=¼VØúáóíñôè9áQúÝû¯ÔG-ÞèTã×MµûC¹ôÜ9ÙâÌ7E8Í<é5IR*áÌûîØQTOè8ÜîÓøýýùáµV½¹ô<Ûï¶
+è+¿ùQæZÓ4ÆT0JÏG±8ùQáC±X/2Â/µYÒ4ë»ÙöÀU³4JMùÜX»³çÓTÐöüùQæZÓLé³YH/îÞ5ýÜë·¸òÌßô-
+/¾ê×QìEÞ+*ÎÝ»=Y¸Ó¶¸éÅ7ÉH9ýZ*ϼÚûì,·ãÓLÄ5µ8FóÄ5QäËG¹éÅãïéQ4ÞÖUPF»ÏÈOQÊöðóÔèÃ;
+æôöûùÅÝ»¿¿Þõýì7ØWÓÞø>Z¾>Y̼V/ÑQ1ëCñûÔè¿Ö@*â,.7ÔT·Þ*V8Ó,ç-ÓV¸ëÒQY¸Ó.ùý/*ç.ßüT
+/ÌYI*@ÛT7Z-1@µõ/æçÓè?4÷üUÓ½U/%%%
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+¾èC0º84¶åM¾èÓ/Ö´¼ûØøüUóûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýT¹ZÓ¼4*V
+JÓÞýÕ,*ÕÔ>çT»E4öüUóûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝ
+ýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøû
+YóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½Óíý
+TÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½Ó7°Ó%
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 151 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+íÄ,*ÓU5Zëúø³óûÕèùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ
+½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ý
+UûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝñÓ,TY2*ðSÓõN+í
+½ÓÜýÓ¼ýUûüÕ¸KÓ,ÒÜ:*âÑTîý5,¾ºTóGSÞùYÓõ½ÓíýTIÖTïãLï·Ø±ÅÌÒ*¾õê×á3HÏÉù¿TDWÓ¾äîÐ:
+¾òT³.*²´ÁÓêLÓêùøöóí/*2üøí=62:õLÉÐV?×MìÏGÖòãÅ5ÍH*æöðçµ/HÅÓñ¸ÓÚ¹öòéù½ýû·KHú4*Þ
+»ñüËùõîêÛQÅT¾°F3K5ù¸À½KÕáL5X*>Íê¿Eý7ºöðà»í@دQ5*úØóéÔ¯õ´¼KîǾ¸8ÕÈQ9HúõñôûéQó
+ëÕÝ,*¶öâ¯ÕÌG¹´í*»°Ò,ÆMǾã°ÚÚ*ñÞY¾ã*-RúÞ8F¸êÖ±í8ÜÚöÂ9â½éXW¸óèÓ¯ÁðµÞEPZ1î+îíUå
+ïT·ìÕ³ÝÌWHýü²ýüúU**½½üöçÎWå¼ËXAÝHÓTC˼úV¹ÎD·+äÃÝBµ¼úæºZ1¼E³éÒUýPÌF6ÍA*Ú¶îØK¹
+HÛD³Zû.Qô+H3RåïT÷äÖW½8ûÛ¼üÐIýO*¾í9½EñäÕ¯U0ÑYG¹,3îé4?4ù¸ôèÕSºüôÑ,*YíºóåÒ±Ý8É4
+?ØôÞ8Ú°èÑE½ü»º·JHY*öðá½Ù̺÷²è*÷3ÌV.ºìUåïT÷ÝÕIIôVÛ»ûLýüG*¾éÕͼ¶ãÅIÝP½-ýÌTÓFÒP
+Ë2ÝPËC²çH½°Ôîð»YÜÊø³àËN=R1ô¾ùÐìÚB±ìà=Ôè:*08ùïàËSõ»À=ù*9ÏYý·ó¾´µÕèçÔÔVÖ°ñðé-ú
+·ÒÕÆÁ;=5¹úóã1ÅVÖðè´ìëSÓ4ÔTÓèXê¿M<GûÚ*áïÍ9H·ïäÖõ0CFÖ-AUøïÝų-98ÂQ6*HúµêÃýH@W
+ô3ÌN0H·UåïT÷ÑÐ5õ³êÙùõÀüúM*Î**6îßYú=7:9NÝпÞ+CíøÛ1ö÷ñç½õÜÙïÜÖÖòѵ³QÓTôéÕYU1ÅR
+¶öø9+*ðVÐJÇÌï*4¾¼ûÎËò¼Ð,@HúíÄMÁÞè:Á-¹îOI9Üɶ´íÝKö±*JÜ3Õ¼õ+>º¶àAIÞ½ùÄËöìßÍÉHß
+Çñîϱ+·:JßÄÉÔ·µH÷RåïT÷ÉÎ/åGVùøô*ü?*R*îÏé7LîÏ0Ú-**î>.FºöëÕéHFÕZ+û°°ÓðçÐOÙ3øZÛ
+Âç´ôòí*¾9/,ÖùÝIÆÐÕò+Î18OîE@îë×íPVõZæEÁ68ê¿Õò6öëÞÔ¹õ0ÝÍ+¾Ï´FDù¹:JÝ3ý½ö+ܹµé¿í
+,ôÚðÇ-Û09äÈ,æïë½M¼èDåä=9óFX?Õæ·ZÚÀ±EÅ8ÒÝ,**:0ÞÕ<Ïü÷Ì,Jܸó***ÎüÃ,/¾X×Þ2Þ¾*OÁî
+ÞÓA¶òä±5ÄEÖ±ÃÇÓÈÓUÕXá,1¶üÆV.ËÔ¿*À³ËÖ5ÈüäåM@:çÍAT¾ÄK01ôÞ;°ôéôYéÙ¹Ý-*Í6º¯öÛ¹=@
+Ë6MÌûN+²³èÛ¹µ¼ûÆ2ö´é½ëøã6ï3ÌÚ09Sáñ/.Ú¶8QÆß?óèCÑK*@Ú:Î,ÏVÒñ¶ÓRJå¿M*SöïãY+**2-
+îÛ3>AÒ9¶ÐON÷ðÏ+@C¶ß3È/È*,+â+ãåÜJÄ198N¿G1ÊVÓJÞ*¾-Dð¾öJ*38KêÃÎøôȸÌÎL*Þ¿Gö½
+ÊûÔ󹸴ëZù?*¶éÆï¹0Hú´àRù÷ðÏÃÝR*ÌÌø÷êÆç=½L.X¹ôç¼éøãúí3ÌÚ09¿Zù/.Ú¶89Õ·¿ðãý¾ùWÈ
+A½NJçRåïT÷¹Ì+Ù/Êصíåæ6*Â+Bé4¼M2??+¶Å0ÖìLå;5=¶L*¿*â1ÓäÕÜÞ»ùP7ÖWÞÌíOÔTÄ;MÄô8¾-
+6½-*,ßôJ*ÕGßöï*ܳ¿ûÆ/XÎ,Ö5°¶ïÚÌ·YØ»MáÄGù?*öäÑñÉÔÚ»´àöIúëÃëÝR*ÛíBºæÏïÝ;,À7¹ò¶
+-ßÚì3ÌÚ09¿Îø¿-F·8î»ÂM:ÎHC¶LJ÷RåïT+´¿6ôõ²ìÚÍ×D*2-¾MçòÜJóÃ0BNéêT8=J*¸ñX/2.J×ó·
+·ó/ÄEõç½ÁD*ÙâTã°äÊSW¿-ê¼²D<0æJ*;DäºËÊàñæ/.õëÃ4JWÆß¿Gö³ÊùÌã5ø³êå*J6=8*îé¯=ü9Ù
+³áüÍ>*°¼H×÷óÅľYÁä¹´YäéÌîâ³ö2B<9FÞËñ6ï2.ÍM.ì2RHè·ÚTJV:-ø?7¹ôëÕ,æ+â´W?ÆÇ×ICE¼
+WQH7î0àÌì4*ñê/ÆZ³*Ä->+ã×3ùÃîÆ5îôêÖOù7è*à<U?ÉD³èY1ÞïRïÛã8WY±ó¯ð´Û7Þôé¸FÛ+ü5Ò*
+ÝÀ68ê+.5UÓ¹IÕ´ûØóE*îÎ=íýýÝôêß?ïýÓC±äýN+:²ãÌíé¼L.öØóèÍõÄÓÀZQ¶ðDº¶ä12?Õ@ºñÇñ>ð
+Þê½ÊÏÜúƾðÏÌáÓÐ×J5ÄùµòÁ+ò**2*à:´êÉW=//´Â0¶êõFº¶³Å7êôçÀ»*Ä-Ò>/ÖUFÎô66öóæ½ï40G
+U°Óð¶ïà³/øZÚ½+ò±»¾3»:*,ïÖ-àÄË×ÇZÅXJM-ĹBòTK01ôÞY·üé@íåÒUÝ,*ÕúØûèÓOEôX3UXVC¸ö
+A4*ýÕ´FûúUNÞÒIñ³ùÕñUQ¶ðFºBåD42Òî-ùÃñ>ðRê¼Ù6J+ù7+ãMGÅU/IË´ºAî¹âÇê,8ÈN+20ÜéöòQ
+ÇÓÈI¼úÖÐèÇý0Ì7FL/ïîÝÇUU4°¶Z1è3òß·ù¸¼ÄTÔ¸ó÷â-+ÚõüÂýííIHô-/ÎÓýѼÊÝö±NÎÍûäG6ØÑ=
+FÇHºÌWÞ³óFX?µÂDñÞÀKÝÌË°á¼û.**¾5*¶-,CKJ*ÆÌXFBñèÄ<U?Q»ø²OËKAHVHÙ¼/áA,î°YÚ´ëÓ=I
+0ë×<D»Dó5SïÏ?ùûéC³éÔO³èQ**2I**LËöýè¾T*NËDñä÷93¿Õ´ÛDòß½?É0ÄÌîâHÞ=+óÚÜTÄVD°àÌU
+åÜ0*öëÙWUÔV¸°NÇÓÈ=I¼FØÖÒIM@ËQå³ÀìùõêÍIÑ8577IÌúÃÓ4áÍ-²Â²éå**üI**¯ûý¿0ÚR+×ÌûÏ:
+öéÖYͼúöñáîæËÓì-ºìUåïT÷µSSÅÔÊ×òéLÙôGÎÈñü7Ú´æÏUU?ÖðäÓTHÜ»ù»ASP»D»0˹T*îÝ×=åWÙ
+±P»Ç»ùï9ê+½¹DøñáÇI1ÔPE*8¹´ëÔG½@7?Ú¸À=îëDÞ=+óFX?¾ù*·æòéØñ¸ÌÒ*Â6ìãGÓT·öüûùA·Rç
+âθ°8àÜÓÅ9˳;Ç+0*ü¿G¶ÚÈ8Å0Û×±æÀØóâÐüêê³µëÕU±Üò0Q@,HõÓñ¶Ó.ùÃÃFθ´åÇà/º´íÜ»ÅäØ
+æCèØÖöÒTOÖÙ9OÐÀÝóÏLOÈÒ*JÓRÏÐåEßß/ÞüÇGöÃÁÄD@»Ø²çZ¸Ø*ÎÓ¯áÄû×è±F×0öNJGÕUãÖÕ?äP.
+äP.¾çÂTæ³YéåñT?ß8½FSÜ,*ö9ë+Ó7äPX6¹¼,A·,èX.º8Uå÷TïóãYAÜ5õëË*¾àÛÙöóðé=UÓèèV/îP
+TÕ;?1·°?î·UGåUO×YÅXFµë¹+*àWFøòRO×±1ÝH¼ü3ÄÈïÅÝHú/OãKÌV/ºÌTåûTûO7öüUóûYóùÝèõ½Ó
+íýTÝýÓ¼ØU/B9**êúË*îJÐèTÓRÏNÃ,û÷I+¾óâ·±ä5ÓÐB¯ì.D>æ1/*æñäÉ@WÔÓØÆ<SàúÌúÝ2ÞZÐ>C°
+WÓ´êö;âÆ=I>*JÈ>M¼úÖÔè<PÊRïÉ,*×Ư*JWóõ4*Zó¿+*
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 302 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/configuration_network.png b/doc/krfb/configuration_network.png
new file mode 100644
index 00000000..dbc927da
--- /dev/null
+++ b/doc/krfb/configuration_network.png
Binary files differ
diff --git a/doc/krfb/configuration_session.eps b/doc/krfb/configuration_session.eps
new file mode 100644
index 00000000..ebb2e69b
--- /dev/null
+++ b/doc/krfb/configuration_session.eps
@@ -0,0 +1,269 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 342 453
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sun Sep 21 21:22:12 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 367]ST
+B P1
+NB
+W BC
+/mask 6493 string uc
+î½*¾î1:-*¾*Ñ-õ0*ÞQK/,ÞÝÑT*ÞñÎ,öïì3ðZÀÉ8*1<·ÈáµÇâS̼*úÉâS̼¾ú+FQ?¾9ÇT¿º*BQ?¾-
+×TKÌS:*0ºýTöü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ½¼¾%%
+d
+/sl 51642 string uc
+î½¼**ûÙ>*<òÌû@KÍÌè6JÑÎÈìQ¼Cîׯù<*VXßÖåYúÓÐïG,9öé@/*GE¹ýüú÷íö¿ñ×,ÈG´éX/*ú5¼½û
+ùÕæTåX?D:ÃïEÙòùżO<JòO*²IUß×ñùñüøظ´L,@ÕZêñ-Ð*ýUÒÈáö7öüâÕÜ,*/¸33DS¾Ü**,ÇKÞ±E
+æ±5¾·õõYÙ³0í×15Æ××ÁÐÞòEÒÈáö;ëüùåL»/*Úñã;ÔØNè×*Þî3+Õñ×7²Ø/Þìû¯M㸽1ĽßõíýôÃTÇ
+æVY**AHÜûúô×1Iï*ãÈ?ñüÞÉH-*åDZڷ¿2¿áC**WØÁÂ=å¹Fæ±6¾°ùµ9DóËïÚO9¯ËüC**é÷Ð*ÈGݽý
+ÂÃTLÚè-*î»YµÅYH¹òåç·è¹YÂ*TûÛýîÏå*Þ³èÒS3>DHRÄ-2G°,+ìïÞO0ÎÞößQá-ëZ-0¼½ÑOY.å³øö
+ñòéé½YHºõêGùòåËE½H¯4ÞÚùù¹ÜATóâöûëêäßíÅ9ܸºÜF¶ê³YV×ÇíÍ?æ±**B-*ú?XËúÞÂ÷Ð3:C@îJ
+/²ìðöûñ½ý84À*ÒúýîJûýý²/X½Jæ8ïí¼öç3Ê,2ÐWY¹+äóܽ½ûQÀîÔ6ðE*êÛIWÍQñJ9½GÛ¸÷ûñݽíé
+Éåüº·ðÕÃPÊ,Ô¹ýüùÀT+ö5Aô»¹M19ýüøì;·ïâµé»¯,´½½ÎHÍ+*Ö6*î;òLÒæÚ<êä,4/ÚXýé÷SD³9Ð
+¹SîH»øýIÆL;*ÎÉ?¶ë+úý½JÛ¶ýíøò.T+JâáXÎòý½Á.WÏSÁÉBÆìD8>*Èì9BPÐ/FÁH*Þ*Þ?øöàãÍY½
+ÝÝüú8½ûõâÉ9YÊ°à>î°ùõµFXýüÁ¿æ,*½TB¶ñííéÅÍåUõGú¶ßM9Î*óí½ñG**È-*Èë7-:GGT4R>*ç-O
+GàÊ<+0ÛWݼÝ7òäËýýµÔ9XÅ,öý¾²üý½±éöν>J¶Þ2J6*IÕÇ>·üïãÜLÃö;Ú3ó½ì÷8Ú¾G¼íýüD+3º¼2
+,ú½Â4ôBüöWÅ5Òüý4CàÂBJÞ¿è½»ñPõúñÁâ1VÏÀ¯ðïÂF<3*NÙý´ÇHÍÎQÏÈ**î2»ÒêïÞÅÉÅIÔÂë½1üù
+¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×RJøõIñ½²*Jð*¾ÀMLW̯8É,4
+/êüýÄ.Ì-¶ëý¯LúÝöNÊøýý¼ýï950*¾ä*æ¶ÙËL+P,8¾¾Ñ±óÙöZų=ºÂ4á-Pñ½ûÎ8Ú*ìýÝÉÒK¯,Ô4üÌ
+ÉL6ÞìEÒ¾9³üQ7¯¶¿PôýýùàCÍ>-X7ôIüµã4?6³,Aâ;3*Þâûó°Ò÷.»B**öíÕ1*Þ;øöÌá±ñôÜÜûùì¼»
+õÚ±9íCï?46½ÛN¿2+¶IR/*ÚK+ìÛ½½Uóéñº**âÏõîAè@ìÄXU²×RÞôõ½×ðC**Æ+*ܯHMëîû÷ÏïVJϾç
+ýIçâ51¾ûüÕýÓ°÷06D0B-Jø»Ó5/¶¾,/,àíü:RÈYTãÖ*öÝüZXÞ²üYYæ¿5JôýSíòBPÞ:ù´âY²ß?TÖ¸ý
+ñÕDYÎ3õìý½Cã,/EòñðçAÔ¯ÖîLHXÆM<*ZËùÅÃÖ¾=æá¼±IÝü²3æ¼ÁHÜ;¶è×ÕÉIÍÜÍA½»´èÞEHºÂ,1¸
+>=4*ÌÏT>¼úÊ1*Þü/ý»ù/TKëÞÁýáÖRJøõY<»Á*ί*¾Ù;ÑÏLUÈB÷C5R>JàýíU·/÷MÕêýøÂNHî˯¾C*
+Ø9Sâ57¶À,DÝXJ¾*74Z20BµB=ZZýµPòÞ/îäâýÇI6=JZï½EC¶Ã4ôðý÷À4FÎÞºýïù¸*4Nû½ÐÕÔï,CXÆ
+M<*¾ÄùÝW¶ð3ø:òîãÅ÷*òÅæáýû@ëDøõîá¹öíÏÙ@ûùIÀµRî.GíϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæ
+JÏñ0XøÙÀÃ,ô¼ýÈ9Í+¾YÚ¶;O=3AXÂçP/øàRZJϾEÃíGSÉ;¹äóÕý=VùÝXL99êäBÞ6*O½Aö²*ëýÍí34
+Þèô/Ìòø4?úýýÚàÄõIXÓ-A¾OWüYÝSÊ*YøÌ·ßúýIÛL/Ùù½íLß/VÔäÆZýýHòJCÒüýàüýÕòâDXâá/·N,
+*B¸ýÑ.MÜK9ÞHX.ÜÝL9A1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿=Ó¸Å,ðºù;¿USÞ@å͵?»øÇ1*JéÛ7H¹µò+.êG¸
+íàí,5ÖRÞÊõYÈïC*æÂ3Ï3¯@¯*¾KÑÓ2Ͼ¾-?î;óTå½½»ïö¶¯ùõA±àÃï¿ë6-Ê+¶×óõüÅ92MçàÜå=>Î<
+/ÑÝíûÛMÒîýýýÕíÙÜJ÷8Ú*ÈåIÜKL-16ô½Ç¿éýíö0?ZýûÑIç4XÞøùù½ý¸ÖP1È¿3ÝÕêD½Ý3YæàúÚ:Å
+N**»õYSîá÷Ë-òKëÝÁ½ÄHÜV¶éÖÍYåÜÛ±ÝüF´éßÉûÉ3/8Né;4îÇíÝÙ°ìFâ-*îý2,íG¹öò*ºúõæÃ1YR
+éÝL+ØÚIïCÒE-¾07ØEâZ+¾L+AÄ0Á0YÖ¾ùÙ?Ç:KÅL?òØ2;ØÓZà¾*Î;¾5-îKë/úíøÎÂ3SALãÌÎ*DÚÃ
+ÆÎOÕT¯îYN×5ÛRAZÇ.OðÔÌJá+øô6;JAR;Øâ,3¸³î¶¿8<>RÏTûX>ÁDEÒ4@K×̺á++F;Å>,JEäíYXÞÄ
+åæ=Ôø2»V@îÚ³I¯Sá7º@¼·Ï*ZIX»Á,6иóÔÂ5íö,*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2ï?*ÖÀRà>º+
+*Öè/>»4HÙ¿6@JáíÝÍÙÕQ×KKLG2ÓÈñ4Ð<1?ÙIò´+CÜL9+*ÞCøÖPÞÁù¼,RPºöPøÐL¿2VöüT4¶áñåW÷
+ôWï/*¶ºÏÂü8¼º¸öʼ¸ÑÏ8WMö*/2öü°ÍBè6*7ò´î3*Þ¯¿,7Dôê0ÐÖ*ÂNÆÎRøÐÁÀ¸J*+Èò4<<1¼¶»
+½Ìï,PVÝ·ÌH¼2¶îàÉMÍXÛYý¼üøðâ1ÛÉC/ÒLïàWãÌûNîÚKµÇÜ+ÃVú±äØ89LØìCº¾,Æ´½ø9-ôV*ZßûI
+1UQîßFæáó*1Hú¶ñä͹òéÝÉíÜÛ̸CѾLòëÙ?éÔí6Ì1F149D°æÚ¿¼Bó41î34JèûÍQ.ÖY+¾ÝôíýYغ,
+Yúä³ø2T°Ì+¾öCA±4æÀ×±Ù<WøË繿ýêGáÃ4>TÆéC¹ý.HK+Ó2º½ò>û³T*¶¾ÁùõACÊÚ¼Q¾ñF»RGWË8+
+¾æTA³4ÞèÓW·ôúÈïúÝ»ôâ¿=åìHöõè.BMìûù±8ÇYÁ,¾5ÂT»ýü<¹¯-öX/å¯øμñÜG,*CÕYè>¾ÎKÙÐV·
+TÛñû4áíÝI¹âÄßéÔ.*Ø5÷óÆ6ô6ÍÕ³óôóÙO1áãÉWC´FAîß»55=íÝM+ÞEéÊO6E¸÷ôW=ÂïïáÔ.5ÌÊ3ÚH
+ºò68OèAYßUµI,¾µÓS¯CZãÔÃ=ôòë=ÓBIßÀW÷óÙW°*îêÖS¹ÔÊÖZݾռ.*ýó5ÖZáÊOÙ0Öã@Hé:,ïéõÒ
+4ZýÖ*îûÕèùÝèõ½ÓíýTÝýÓ¼ýUûüÕT×1ÜQè9I×-¾û;³YîO?NåÔÈÐÂ/Aùüé±1*¼¸UU>õâ:7ÙT2öAìð
+ýå9*¶û·ñÖ½WÇK¸QÓ6áUÕVW+*6Þ×å×YJÓî¼ñàËÕÛÁ¹Ê+ÆU6¹SÓ:áUÜÅåÜ,*ÞVÙÉ5õ½+ÔÀñ·AÝÛé8î
+;.*Dµ4UGX?º?ØòëáÕ**äT÷ìÛµÎT¶Ý´ìã¹ë+HÀ*2ñäÕDSÓÎÓäÍUMÝ,*JWãYáä½+ÓÀøYÝXìè8î;.¾B
+µ´ÓOÖøºÓRñçåÕ**4êõçÓïùÍI*:üèѽÙØ-º.+ÖäÉ5åèNÓ°ÉCÉX¼/*Îä¶Á@˼ý+*ʸâ¾-AT,X<**¶Ì
+,K*ÆܲæÚWë+HÀ*6±ãÊW³ÐTCãÇI9½**.D¿¿A<:O-R´ØE½@½ý**ÄÞè¼úê?+C<*ÈýÚ×´Uµ:K*úE.¶Ð+
+*÷÷³Úç¿50:Ë=µÑT×âÆE-½**ÒY.AA<:O-î¶×C¹ì8Ýû**Ç¿ø>¾éôáJ2Úôë¹=2:.0FR*1κÓ/5XCµí*
+VêZé2ßÃ+RúëG±Ìëè7N:*781,Úç¾áç*Û9óÞ/-ÖäÆ?ÙèOÓLÅCù¼**8ãä+¸»ù8FÄ6ÎîL+º¯F/ºBóëEC
+¿îJòÞ¿,F>*¼Aù°äêéÑ9*îXǶéÊHûM/×ùÕQæѽîøÆÞ¿,¶*ÑÎ?IXV?ø0*Hë0ä-X6¾ÔåÌ÷ðêÔìJ,+æÃ
+R:*ö¼*88***QÜøÔòÆù´Eå±P4>¶Àλ-ö´Kò¾JFóêHRñáǹSÓ6°áɳA*öÕJ²5°PÙôH°7óÞ¾T*ýñX³Ì
+M²é¼*,99FR¾Á-ÎûZUäúúú¸.OìÐȽê;.2ç*ç×9È?¹ÂÞ¿,¶*4¼±åÌù,ô0Þ¸ô*X×Á̽Ì×°êÙìJ,-îê,
+Oë*²Ý*11J¹ÔºÕÃʾëïÏÑÒÉñãÖB·ë*Ô×Ô7¾Z;Î÷×»ÎãÄ;µÑTÏàÇYA*öWíXŲÏøó-Î÷ZJ?*FBNÀÒYõ
++Ó¶ZÞ¿,F>*PöØðãåÙU½**ö¸-ó6æÔ.:ñ,ßÚS*1ZKµóè·YÎ8*./Ú¾ÀC*ëüú°ãÕ¹Û¿./î¸N5Ê+F+11î
+øLLK¯Ëßè÷²ËZ÷óéÆ?.9.;ÆG:íë-NìSÓ<ÆQ9*2å9À²:¾ÊÑ;BH7R/:4ÂÔ6ÎåòÔÚS¾Á,Þ.YQÔÊÚ¸ñ+*
+¼Ò²ïI±BQOÅYHÜØ·ñÏAQ2<¶Î*8¾Ñ¯Ø±1ñâF*24BR¶¯*TG·ãÇYõ¸-36Þòç¾°+Bû,FF¾ç¸Oú¸SëDÕKØ
+ÀáÑQEÔZºÞP-ZOèÛ»²ÒT×áÃ;ÅH*:7Û.,¶QÇLõCÄCïæ:¸?¾±LñôñÞÏMõ?µ;.7FR¾Á,Þ.¯UÔVFøï+*Ü
+ãÁ+JÈ8µGÈUR¾áöïA:¾5<¶Î*8ÞòO3>¶L»F*24BR¶¯*4Û¶äɱõ¸-34Jòæ·A*¶,8ÌÞîæAXV8ø¿Q4ûÆÔ
+àïGDH¾Ï+RHÀî×»BâÃ9ÙèQÓ,Í1*NÍ¿¿*Ìò+à´Õ6BïCºX/îÁ=OR:áñå?â+±K-14îÎ*BßéÈAIíXºô*
+ÎäÁ±Ô6¹ÕEÆ**à*2G*ÍY¾Á¹ÈîC<KÍCYî,I-P.KÀM¿É1:¾åUGV»³9»ÓTÞ÷4*Þ?ÜÌVC·óßÁÉî*8*JNJ
+Ä,ÎãÁYÔú¸Õ>X+Då´9WÓT<Cò6*î¾ÈÐæ¿:*û2Ù°ãàÏQ¸SÅ<JË5ÕF÷óX1Ï;9³Ú/+ÆÚÍTã:EÜVø4*JYä
+ܺ·¶ñçÕÈLYX×°êÛô*9K*²×¯âëÒTòBñçU**0ÈÙ±äßÉ=ÍG*ò¸òÉAåì³1ö2,:Ê9õÑT;âÊUY+*¶TîÐI5
+µüºô*Îòã±ôʹÕGÞM2¾²ÄÚÉT/¾ÃT+éU**?5ú³çßÏQÝTDIüæпõØ-º.+ÖåÈ;-éMÓÀW;MÔ6ù4*¾+éôë
+صîÛ±1*?Yý³êÞ¹ë+HÀ*ÊòäÊÙôÏT¿ýé**.ûصìØA**Ôù·íÙõ¯9G¿1¾TLÓè;íé¸0÷üUóûYóùÝèõ½Óí
+ýTÝýÓ¼ýUûüÕØÁÕTÖÛÖØRòçÓàÍK0BZïF0*¾Ù:¾**6Î+ò?221ÊýÆTC2LÍ9ÍZÅÀ×R¶ÉD,,*øM:¶<*QN
+îOVÓÞÖº4ÞMñS*NVôêÓ3.1,-8>¾-ÞGèÀ/Ѹ±µ*4´¾ÓÐZ2OÅEÞ8¾G01Ø:ôøÀ7*¾=<:ê+>@Öä²2V¶R4
+7ÜT¾è/*¶ôêÑ4×ôB¹ôÌRJñTÉZåü*¾HF¶Î*8*Ұܵý<ÃÛ+Rµ¸¿DÁR?¼Ë6:ô*æG½Ï/*Þ3E**ÅNÎÖ¾,Ï
+UüÐ>ì¾MÅGÞÛ?OØTîý+*./Rð@Óúá8KÞÑ0ÆÌÛÎVÖM.8>¾-ÎîE¹ôÙAZA*ÔÍQ72J**H¶T»BîL5*¾=´¶O
+,Ã/,KöîÀ¹Ð*êYºMÅCÞ8Tó4-:ô0êCÖW3Jå1À·Ñ*,ÞÀR?æGT?>îâTYÆóÃ@**QêîÊ.0-NÀìK+õNÏñ.Z
+AÓðRYIQD-¹U¹úñòØ×±µû·À:¿-/î*JCùëECRêYÕèÃ@**QêîÊ.0-V;¶ä¹I>ÃÔY?ýðß¾:V×ùPãA>æ¿G
+ù52è/3FR*1Z¹Ñ.5Lïäü¸ÓX²+*¶à0åNZBÞHÁKÞàKS²ûTû91öüUóûYóùÝèõ½ÓíøT/úöå**ÞÅ2*3Û@*
+WÒT.¹½ýÅ*Jöá8;ÖàÓ²ùÓØàùûA¼,*äÝýàãÙW½DçT¯Wüü±ó¸+*äÝýØÒA·?ë¼Ó¶>½ýà¾Ý**ñíýRä9èT
+;NüüÝ÷½,*ÐIâRÓØ4ÀÛJàR**:A¾Â:FNÀ-7¶³,Å1;×TF½Â¼ÙT2öVÓ.ý¾QÔÈ2È*=ãTãÒKSÓ4JÍ;@øëT
+OÛûÕÔTôÕ¹°ÓPOS,Äὺ4ÛêG¯àX¶9åüÃ,/ôÖ1ØçױͲ@¹³28>GBëÊ>ÎRU»*,O.ÌG*À8À*ä8*ÄäÑ*Æ
+Ö1ºÒJî*1V±D.@6BZÇ×Ö±AFÂÁß-µÅ+õR5@ÊèGT:Z*×-RÖ+Ô=ÖBºîê+âÁZ6H.ÛÌöLÓß;X²ÏTDzõYWÓ
+æÍôàTÏY;ê9·5Òïß0LQÐÀ¸¿üFùåå;QÞ¼°<äü¸3ôê9¾¼¾.NÈÙð1@ò*.È2F¹*×´M²ÁG*M>ηÓK/-M/,
+1¶¾,ÏU¼L¾M=çËG¹H?59FÛñÎÈ÷ÜûÉÒ-Ǿê+ÒEÛÞ3V6?¶ô*:Ä0Î:²-QºÏèî.ADçTOAùÕÔTâõ¸¯ÓÜÊH
+ôJ¯Rå8¾åÚ>´BF»-ûóè×èÉ0*¹í7ºõ¿úÖ±A>5ôÐ-çÞù+.+ô/,1ÂÞ×-ÖÌÛö,ê+R.***2:û>+*B:¶¿-,
+Kö4++ò±>*N¶K°ÊÛÀíê×íÜîNÓÞøR¾5ÖBT¶ß,ÞÞ3YÒDFàÃ@T?;ÓüÓÓ4òÐ8VÓB9;ìà+Zã8*²4èÒø¹ö¸
+Ví²Ìîé*JöÝÉêÀD¹ôå1ÀSOZ/±ÝÜæÑJîÀ2À*²µ1ê+ZZù.B4.Xî*N2MG+0ÚL+2Àðá9¶+V2Ó,ÛQÓ8ZL
+JÞ/VÚUÓ5¿R³¶À5˲Óô2ºý*íÒÈî¼òÞT/æC;-<éÒö6ÕKM÷Ù;P¯½Þ-Aå5¾µôE¹¾L+-âøñ.O1Ö0LìØ*6
+ø>2Ú:,1¾»÷´ÐÖ-ZãôºÌXù/6/2ÚÞ+T.Ì÷¶?1¾úÇÈê×Á·4ê?H¶ôÎÊûôË6>ôÒ*³¸QTó,@ìÓèÃJÒDFàÃ
+@T?õѺ¸óñÕMÝH.íÆõäëå¿ÓÈL@-+úX.Ý+ZA³3¹ÅÜ+Oô-I¾öO+ëßXº,Î+-ò,À¸Ó.Ôõ1125»4.X2¿-J
+;+=ëê+ö´Ò@>æï*Ä0.Xî¾»0Á¾Ã+-A6àZôLEæÁZ@Þ×íì>ßÀRÚ×ÑU78HSÓ5ÖBº¶*,ÏTA,Ïèî.ADçTÛË
+êÚ·ýPüº·X8G¹ôæÏÙ·ZÓS.*ÛÜïºT;+ãñ,¿=1JçYÀÇ4=XÉÞ,èZóÓ<ûÜF-*¶óÞTÑJ*3AWìK+*4*Xá¾S
+BZâëÚE+ÖÝUÁVüÕ=TãÖÑP¸ï;*2ÐÒñ×è8Í¿ëÒ4?QË÷;**èñóJLÍ/J=ÓèõƾõÝTíýTÝýÓ¼ýUûüÕøûY³
+ÁÒèQ+¾/Ò4úýT*îèÈ<WÓµM+í½ÓÜýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUû
+LÕ4%
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+íÄ,*ÓU5Zëúø³óûÕèùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ
+½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ý
+UûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝ
+èõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ
+¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝËÓ,%%%
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 151 di
+/mask 6493 string uc
+*JÌ**-ܽ?ºý-úü1öû9îùIÞõ½¾íý*Ýý+¼ý-úü1öû9îùIÞ庾%
+d
+/sl 51642 string uc
+äÕí´ê9+*ú½³+íÄ,*ÓU5Zëúø³óûÕèùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ
+½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ý
+UûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝýÓ¼ýUûüÕøûYóùÝèõ½ÓíýTÝñÓ,TY2*ðSÓõN+í
+½ÓÜýÓ¼ýUûüÕ¸KÓ,ÒÜ:*âÑTîý5,¾ºTóGSÞùYÓõ½ÓíýTIÖTïãLï·Ø±ÅÌÒ*¾õê×á3HÏÉù¿TDWÓ¾äîÐ:
+¾òT³.*²´ÁÓêLÓêùøöóí/*2üøí=62:õLÉÐV?×MìÏGÖòãÅ5ÍH*æöðçµ/HÅÓñ¸ÓÚ¹öòéù½ýû·KHú4*Þ
+»ñüËùõîêÛQÅT¾°F3K5ù¸À½KÕáL5X*>Íê¿Eý7ºöðà»í@دQ5*úØóéÔ¯õ´¼KîǾ¸8ÕÈQ9HúõñôûéQó
+ëÕÝ,*¶öâ¯ÕÌG¹´í*»°Ò,ÆMǾã°ÚÚ*ñÞY¾ã*-RúÞ8F¸êÖ±í8ÜÚöÂ9â½éXW¸óèÓ¯ÁðµÞEPZ1î+îíUå
+ïT·ìÕ³ÝÌWHýü²ýüúU**½½üöçÎWå¼ËXAÝHÓTC˼úV¹ÎD·+äÃÝBµ¼úæºZ1¼E³éÒUýPÌF6ÍA*Ú¶îØK¹
+HÛD³Zû.Qô+H3RåïT÷äÖW½8ûÛ¼üÐIýO*¾í9½EñäÕ¯U0ÑYG¹,3îé4?4ù¸ôèÕSºüôÑ,*YíºóåÒ±Ý8É4
+?ØôÞ8Ú°èÑE½ü»º·JHY*öðá½Ù̺÷²è*÷3ÌV.ºìUåïT÷ÝÕIIôVÛ»ûLýüG*¾éÕͼ¶ãÅIÝP½-ýÌTÓFÒP
+Ë2ÝPËC²çH½°Ôîð»YÜÊø³àËN=R1ô¾ùÐìÚB±ìà=Ôè:*08ùïàËSõ»À=ù*9ÏYý·ó¾´µÕèçÔÔVÖ°ñðé-ú
+·ÒÕÆÁ;=5¹úóã1ÅVÖðè´ìëSÓ4ÔTÓèXê¿M<GûÚ*áïÍ9H·ïäÖõ0CFÖ-AUøïÝų-98ÂQ6*HúµêÃýH@W
+ô3ÌN0H·UåïT÷ÑÐ5õ³êÙùõÀüúM*Î**6îßYú=7:9NÝпÞ+CíøÛ1ö÷ñç½õÜÙïÜÖÖòѵ³QÓTôéÕYU1ÅR
+¶öø9+*ðVÐJÇÌï*4¾¼ûÎËò¼Ð,@HúíÄMÁÞè:Á-¹îOI9Üɶ´íÝKö±*JÜ3Õ¼õ+>º¶àAIÞ½ùÄËöìßÍÉHß
+Çñîϱ+·:JßÄÉÔ·µH÷RåïT÷ÉÎ/åGVùøô*ü?*R*îÏé7LîÏ0Ú-**î>.FºöëÕéHFÕZ+û°°ÓðçÐOÙ3øZÛ
+Âç´ôòí*¾9/,ÖùÝIÆÐÕò+Î18OîE@îë×íPVõZæEÁ68ê¿Õò6öëÞÔ¹õ0ÝÍ+¾Ï´FDù¹:JÝ3ý½ö+ܹµé¿í
+,ôÚðÇ-Û09äÈ,æïë½M¼èDåä=9óFX?Õæ·ZÚÀ±EÅ8ÒÝ,**:0ÞÕ<Ïü÷Ì,Jܸó***ÎüÃ,/¾X×Þ2Þ¾*OÁî
+ÞÓA¶òä±5ÄEÖ±ÃÇÓÈÓUÕXá,1¶üÆV.ËÔ¿*À³ËÖ5ÈüäåM@:çÍAT¾ÄK01ôÞ;°ôéôYéÙ¹Ý-*Í6º¯öÛ¹=@
+Ë6MÌûN+²³èÛ¹µ¼ûÆ2ö´é½ëøã6ï3ÌÚ09Sáñ/.Ú¶8QÆß?óèCÑK*@Ú:Î,ÏVÒñ¶ÓRJå¿M*SöïãY+**2-
+îÛ3>AÒ9¶ÐON÷ðÏ+@C¶ß3È/È*,+â+ãåÜJÄ198N¿G1ÊVÓJÞ*¾-Dð¾öJ*38KêÃÎøôȸÌÎL*Þ¿Gö½
+ÊûÔ󹸴ëZù?*¶éÆï¹0Hú´àRù÷ðÏÃÝR*ÌÌø÷êÆç=½L.X¹ôç¼éøãúí3ÌÚ09¿Zù/.Ú¶89Õ·¿ðãý¾ùWÈ
+A½NJçRåïT÷¹Ì+Ù/Êصíåæ6*Â+Bé4¼M2??+¶Å0ÖìLå;5=¶L*¿*â1ÓäÕÜÞ»ùP7ÖWÞÌíOÔTÄ;MÄô8¾-
+6½-*,ßôJ*ÕGßöï*ܳ¿ûÆ/XÎ,Ö5°¶ïÚÌ·YØ»MáÄGù?*öäÑñÉÔÚ»´àöIúëÃëÝR*ÛíBºæÏïÝ;,À7¹ò¶
+-ßÚì3ÌÚ09¿Îø¿-F·8î»ÂM:ÎHC¶LJ÷RåïT+´¿6ôõ²ìÚÍ×D*2-¾MçòÜJóÃ0BNéêT8=J*¸ñX/2.J×ó·
+·ó/ÄEõç½ÁD*ÙâTã°äÊSW¿-ê¼²D<0æJ*;DäºËÊàñæ/.õëÃ4JWÆß¿Gö³ÊùÌã5ø³êå*J6=8*îé¯=ü9Ù
+³áüÍ>*°¼H×÷óÅľYÁä¹´YäéÌîâ³ö2B<9FÞËñ6ï2.ÍM.ì2RHè·ÚTJV:-ø?7¹ôëÕ,æ+â´W?ÆÇ×ICE¼
+WQH7î0àÌì4*ñê/ÆZ³*Ä->+ã×3ùÃîÆ5îôêÖOù7è*à<U?ÉD³èY1ÞïRïÛã8WY±ó¯ð´Û7Þôé¸FÛ+ü5Ò*
+ÝÀ68ê+.5UÓ¹IÕ´ûØóE*îÎ=íýýÝôêß?ïýÓC±äýN+:²ãÌíé¼L.öØóèÍõÄÓÀZQ¶ðDº¶ä12?Õ@ºñÇñ>ð
+Þê½ÊÏÜúƾðÏÌáÓÐ×J5ÄùµòÁ+ò**2*à:´êÉW=//´Â0¶êõFº¶³Å7êôçÀ»*Ä-Ò>/ÖUFÎô66öóæ½ï40G
+U°Óð¶ïà³/øZÚ½+ò±»¾3»:*,ïÖ-àÄË×ÇZÅXJM-ĹBòTK01ôÞY·üé@íåÒUÝ,*ÕúØûèÓOEôX3UXVC¸ö
+A4*ýÕ´FûúUNÞÒIñ³ùÕñUQ¶ðFºBåD42Òî-ùÃñ>ðRê¼Ù6J+ù7+ãMGÅU/IË´ºAî¹âÇê,8ÈN+20ÜéöòQ
+ÇÓÈI¼úÖÐèÇý0Ì7FL/ïîÝÇUU4°¶Z1è3òß·ù¸¼ÄTÔ¸ó÷â-+ÚõüÂýííIHô-/ÎÓýѼÊÝö±NÎÍûäG6ØÑ=
+FÇHºÌWÞ³óFX?µÂDñÞÀKÝÌË°á¼û.**¾5*¶-,CKJ*ÆÌXFBñèÄ<U?Q»ø²OËKAHVHÙ¼/áA,î°YÚ´ëÓ=I
+0ë×<D»Dó5SïÏ?ùûéC³éÔO³èQ**2I**LËöýè¾T*NËDñä÷93¿Õ´ÛDòß½?É0ÄÌîâHÞ=+óÚÜTÄVD°àÌU
+åÜ0*öëÙWUÔV¸°NÇÓÈ=I¼FØÖÒIM@ËQå³ÀìùõêÍIÑ8577IÌúÃÓ4áÍ-²Â²éå**üI**¯ûý¿0ÚR+×ÌûÏ:
+öéÖYͼúöñáîæËÓì-ºìUåïT÷µSSÅÔÊ×òéLÙôGÎÈñü7Ú´æÏUU?ÖðäÓTHÜ»ù»ASP»D»0˹T*îÝ×=åWÙ
+±P»Ç»ùï9ê+½¹DøñáÇI1ÔPE*8¹´ëÔG½@7?Ú¸À=îëDÞ=+óFX?¾ù*·æòéØñ¸ÌÒ*Â6ìãGÓT·öüûùA·Rç
+âθ°8àÜÓÅ9˳;Ç+0*ü¿G¶ÚÈ8Å0Û×±æÀØóâÐüêê³µëÕU±Üò0Q@,HõÓñ¶Ó.ùÃÃFθ´åÇà/º´íÜ»ÅäØ
+æCèØÖöÒTOÖÙ9OÐÀÝóÏLOÈÒ*JÓRÏÐåEßß/ÞüÇGöÃÁÄD@»Ø²çZ¸Ø*ÎÓ¯áÄû×è±F×0öNJGÕUãÖÕ?äP.
+äP.¾çÂTæ³YéåñT?ß8½FSÜ,*ö9ë+Ó7äPX6¹¼,A·,èX.º8Uå÷TïóãYAÜ5õëË*¾àÛÙöóðé=UÓèèV/îP
+TÕ;?1·°?î·UGåUO×YÅXFµë¹+*àWFøòRO×±1ÝH¼ü3ÄÈïÅÝHú/OãKÌV/ºÌTåûTûO7öüUóûYóùÝèõ½Ó
+íýTÝýÓ¼ØU/B9**êúË*îJÐèTÓRÏNÃ,û÷I+¾óâ·±ä5ÓÐB¯ì.D>æ1/*æñäÉ@WÔÓØÆ<SàúÌúÝ2ÞZÐ>C°
+WÓ´êö;âÆ=I>*JÈ>M¼úÖÔè<PÊRïÉ,*×Ư*JWóõ4*Zó¿+*
+d
+342 151[1 0 0 1 0 0]sl 8 mask 0 302 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/configuration_session.png b/doc/krfb/configuration_session.png
new file mode 100644
index 00000000..9eec284f
--- /dev/null
+++ b/doc/krfb/configuration_session.png
Binary files differ
diff --git a/doc/krfb/connection.eps b/doc/krfb/connection.eps
new file mode 100644
index 00000000..bd64f345
--- /dev/null
+++ b/doc/krfb/connection.eps
@@ -0,0 +1,593 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 432 417
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.92 (alpha2, CVS >= 20030921)
+%%CreationDate: Tue Sep 23 20:29:37 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 403]ST
+B P1
+NB
+W BC
+/mask 7506 string uc
+î½*¾î1:C*Ê=FF**IÙD¾Iµé*¾åÔ0îßKCâ@0QI*,âµDµK*üOµDµK*¼NµDµKJH*̼7Þ¿úéJ0*ʼ7J¾/
+ÒKø²öüåîûÍíùIÝõ½¼íýúÜý÷»ýñùüåõûÍíùIÝõ½¼íýúÜý÷»·å,%%%
+d
+/sl 60048 string uc
+î½¼**ûý2*NöQ5YÀQг±*Û᳸I÷ã¾åÄûF*Þð4Ä»¼1ÂWD¿Nî+½ûM*JåäìÝI½üUQúºÔ¾°ÅëøË**ü/ÝÝ
+üû¹/Ù±¯57¾ùÇY0ºôóöEÙòùÅüQØ@.*åÌíÍùéé¹Ø<3ÖÚÀ¯Í9L-úY·Ë¾1F-õEîûÇYË.*.=²õ**BãL/ë
+V-Bõüü³Ùä*R5°+CE¹:7ʼá³<¹ê6ò2çôݽ@èN*JÎ;ºä³À:ØVÒÎͳ*¹õÅ7?éÃ<Hü/ݽýÙAÜPL±»+*Ú
+ööôñÙåHúÂ->UÔÊù-YD5*2²æP+Ä=8³Jù9*ÈëÝ»æÒÅè>Í.ÚG@¯ËüC**éSÎ*ÈGݽýÂã³LÚè-*î»YµÅY
+H¹òåç·è¹YÂ*TûÛýîÏ×*¾MÁ´òTÓ:3ð±0÷CÒØDî/*¾¿íí²ç=±Èå+X9ÉIQÝÜüùðß¹åì×Q½ìÚ÷ñ·Î*äÛ
+ÝÝñ»ï³°ýê-Òõµ±E1½I¼öèï÷ðÞ¯Õ»+ûP¼½ÔJ±+*Å07RÊúÒ3=0ïMÅXèç¾,*÷9ø:Þ÷AæÚãïùåý¼D϶
+ß*À²éZÂùÀ¶:6öý½üë1IG¿=¶¶E*BÕùüûóÝ0ÃVÀ;±6ÞìûçøºøàÊЯ¾ñáðÕÑUAݽüùõ»»÷êÍAQüØæFR
+Þ¸õí½YöD*½¿øܵµòîïíYíG<AÌú´ÛAPÞôõ1è±@*îæÎKÓäçË=QúÇEÂZ3Á47Ø-?Jý÷Õ5.**8¹¿éó9ÛÓ
+1CH¯ÆÄ>X2,üÅæÑQ*Õ/ïÂ1:¾=Îýõº¸,Qîî²*>ûýÂ>Ò¿Zèî<0<3ÄÊY*Oõ16=ç5±ÈÄ+äÖ*¾+¾UáðÃÉI
+Ýü¼½û÷GüøíÇAIÜBãFRÞãôíí¶Úüû1-Ï/*üÒZîäÝÝÕ9IÍÕì¹öïÀÅHJ+èÝýä²*ÞÏKÉÕë°;E°ö7J<Yì´
+:D²1TÞý±åûíäý*´¸Ðâ÷ÅAUøúËÈõýÙGóA7Bîçý-ȯ9PÄê²æëøóí0DÎÞüµÔýø7->AæËN»åõI½úCú9@
+¶÷.Å,:ð/µ,ûI¸Ö40Ú÷ö»ýùé.Nî÷-1ÅèÞ8á9Ýó¶¿èý½ÊJÉù,.*Þ·0ñ̽üâÑ-ðÚäLÄÐ/èD¼ýÞ¾O**Ð
+ë½ÙNÍ1â>Ë,ʯ?Õ3**87ÉÈG8ú¶÷öÁäÖûõÞ½9AÉï742**0/.**µRÞßõIýUüúU½@*öÌÐÇ5ýÜHüùÜÜùî
+ÚÅM¼×±Ï*óí9±D*îæÎ7ù½ÐÅ7Zú09=ýýI07Ø-?îýQûY4ð½*Bûõî±æ¼0ø½5Aû½IÅí9JÂ3¯ìý9¿Ú+,Õû
+½ÝZýëÀÂSýý9óý»HQ6ÎÚ¾é+ýÝÐÂ<2,Ý:¾ýýÇü½õ.Å*:º*¯èúIýÄ-°¾ùýM¯ÏJʾ뾽¯Â¾ÁM³¾è4üµÊ
+Y¶¿PôýI¹àCÍB,X/ñý»R¿+ÐRÇZÂ5=JáÝÔBÑÖ-*Þâûó°Ò÷ÞÏÐS¾C@+*J6É**îæÖºåï×·¹Yíü»µÝܹì
+×ÅõÊö0/LóÞûîD*î0/¶õüûWÓYÍñ+*:ÅÜÁÝÎÝÞI¸WÏãL+ÙÝýãÈ×**KÁPêËÕ×ãàÎK¹Î,üI*áýíû1Î<ê
+9êóößöÝ?G°ØMï5+-Pýý/;12à+î×ýðéÒVY,.ÌBJ·+ùÎý½ÁP:+Ä6.5Æ÷ýíGKâÀúýýJBÀ1Êäýð1F:-
+âüݯÈ2á.ÊWì3Ô/?Úè/ÙóIü;ñ/B8Sûý+1S8MÎÍÉIÝJ1æÔJβ¿òÁß³P¯1*ZËùÅÃöJÊðK¾B@ùËûúøß³
+,¾ôEáð7¿ÑÌ»Ûú÷òûº÷é±Ñ0ÜïßM>¶¾¯ÅR*¶ÄÕÏöï±H**ùAúõëAÚ0³-9ü9áL+èÝÝâ²*ÞÏK=·YO:E°Þ
+7°°Y8´DÎ*:ÎÈ˱ý9¾âúMá<F¿óTÙµñ8÷KÖ=ZÞÂýýÂï*¿åý½+âýð9ƺîOQ6*ðAAÖúýOV6ÀR@2Y¹ô.+
+*;.?6Ï+0ôýÜ18.-ìÊùIµ½¯,ÒðûM=-Õ¸ßXü1½JÇ.عóÝ»D16¿Òýýö,GXÚà²*ëåÞïÃZVÞ*Îïüµ5̺+
+S×BÞ65K1Y¼Î/J½²VüíµçÔϽ1IÜÁý8Û³çÝý;ºÃ,Á¯ÕR¾±àÅíȹõ@9**Ìá7ºôïã³4GµîÐÝ?V>ZõùÕú
+B5*¶²æH¼ºõéÇ=ôâÚYõ8²Þ.*ü1î;çü¿*Üøú¯=Û¼>ÛÝØGñý¼EÝ,0ÚSùçYØ6×±X¼ûÝÔ×ýÅHBµ4,¶à*1
+ÚÞ±ýåBÏåÅ÷ý-4ß,°òý¹Q¶.*üýý<¯áõ9Òâ+CÞÐ<ýɵ;PÞFVÝÉYÞßÓXÖÛýÍL2XÞ°³Ä.ù½ó,16Kóýåð
+ý½0Ñ-AX¯²¿ÐU4úZB°Ô*6ÛýSÀ;YJT@0Rá,êÁßÞùUáðð¾Í´ûº¹õíÛùôåWÍ0XÌÞU>B7¹Ã,êðú÷ÔÐQI÷
+**²ÛÙß±õÄH¾¾ÛMõWúWZ+5/Ê÷üOCä,*,7Øñ7¾ý+ZâRµ:¾ÉFEü¼õãèüÕæâÒ9G¾ÞÇ+µ¶ò¶üùé-9Äíе
+½öÁC69Zç3-Fî,QçÔºüôó/@Yü56*¹ö.Å*ÞýýýIÝ´R+ÐG:->ÍIºY103Réýñ,8*àE³¾ûõYP+P¶+éííý
+»çUXFZ.PÚHXçúÏDº.9M5Rôº×ýíI5ÄÊ?*»õYSîá5ÆÈÃ+865³ý8üׯ÷°ßWá¼¼GùôÌûøñÖW1Yôí;4ÒH
+-*ÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõêßÑ-îïÝMAHºKWý4/êðüÂ=ä,*07KZæ8+¾:ÂôÀ/*¾B*ö=2Þ/Ì·ZÞÂÂÖ*B
+S3=Þ¶:-¼ÌÊ1+0VöK:À¯îJʾ+/*Þ2Þ3-*¿ã/8:Ê-+>NÊòÞÊÞã=F4¾Z6T*.×=°Z9,ºZÓƾ¶+¾?îîLA
+àßV¾+Sú7Aæ,+*N¶Lì/MæM/ÊV3îã?º.+æÎàXûÅÊJDèÇå0²áúûã/êö,а¯¾=á4ݼ²V@5¶ìØ9âÊ,*¾E
+öVúÌÏ*ZIà¼Á,6иóÔÂ5íö,*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2¯<*îæ:@ê2;RÆBGPÊJKÀÂ3>ØVÒZ÷
+D2YÉñ4²úûùYMç¯1èΰùÁì@:á=ÆÈã+86É**îêÖV=îß»I÷D=Hº=»ÕR:èËÜ-*î0/¶áñåW÷ôWï/*¶ºÏÂ
+ü8¼º¸öʼ¸ÑÏ8WMö*/6öü°5P,*JéO*âL/ÙÕ++*JÐU¾806*,ì4ο9Xíͱھ3ÍJT@1¶¼V¼2¶îàÉMÍXÛ
+Yý¼üøðâ1ÛÉ./ÒLïàWãÌû.3ÛKµÇÜ+ÃVú±äØ89LØìCº¾,Æ´½ø9-¼?*ÞÀù½8ÔÌÞÁ×KFBº»VX1¶îßÅEµ
+üÚMÝüÛúôëóÒÉ./>LíÜW×´ûÄ3»Þ·ÎöÎ;Áä7òÁSÕ¶*ÍÎ*ÐìýºL¾9/*öéüûýOÉQZ¼4½»VÀÈʱ*:¼äOÀ
+,ÚZôêÔNÉHö.*ÚÞ½ôÌïL/4?âóÊÛ½,ÍξT.ÜI¸Èü¼3*¶¾ÁùõACÊÚ¼µà>Í-FI@TQUÑM*:ØèãÀ,Ö¸³UÌ
+GÝÈLýµÍG·îâ×Ù1üÛD+äƹýüÊMðùK*Þ/LÓܽ½ÇÛÖ+ºåâ>9/FH@èµìÙM*:ñøãÀ,2D²UÈ?Q/ÆÜY¯úûù
+±ÅêBºûE*¾ôL¼G²ÂGáñ³WÛ»ûÔ¹÷¶÷°UÄ7å³Æ¶íààâù½;*î˳ä<Ä7ÛºùôÙ÷öö¯UÀ/Qä.X9H¸0Å<óÉÕ
+îÓù¯*¾µÓS¯CZãÔÃ=ôòWÖéBIßÀW÷óÙW°*îêÖS¹ÔÊÖZݾÕÅ+*üéAZßÄCÉ´7ZÉËÎôM.àÕí÷DÞüã,Þùý
+ØõýéíýÕÝýY½ýÝüü½ûûýøùýóåó³¾èÐ+*üýQY-*Ô6*ÞýÅøDî>1*¾Q16ÞKÓ¸/U@UÍ:CLÀ.74+2/*ÁL+
+à*FÏLÄ8OðNè?Æó6*ÚÔVÖZÛ°ãÈ?YÓ4ÓSÐNÇ,ãF³èâS6¾²ðòO»ËõèÃUÕXÛ¶ï4,2+B4B728¶*+ÜÉAZã
+ÌKÉä6ïL/èO*¾RÃ*ÌÈÔXÞÂ;±ô6OÑ@ë+**³++27رåÊ/*Þ±äÈ3ñ³D¿´óàóQûø/MINóU?îÏVßÈ?ÉÓPÃ
+ÀÓç:KÂ0+È*È-L*B;LÃ8WøÂT6ß+YA°9-*V¯ê;*Ì4?TÑH³øÒç-ùØÞ*öò*+è/Ä+¾ÒöDÐã4ÑÇDC²æÃ,6
+ÑGÖ¾ÈEY.ç<QÎL˸óJéTëÚÔVÖZ߸/É?»ãèÓUÕN/OúSYU@-ÍLç4èìFëÃáÇQÍ8úØVL²Rû*çTéÄëÌ?J>
+ùN+æ4*¶îé1ìPX×D*JÓXÓøÐÜ0J7*ÆQÍà*L*Ì*J·Þ/-.EéWÙX¯TÂRT*ÞÄ>¯ð.4?VÏ/Õ,î.í*ÔAó³Îé
+3**Xµ<J¸óT5*Ú8B¾-±0WD³ãZøîµ14õÈôìÖJ;¾ÉÀóéB±åÎK-çÌ+*L¹ôÊC±YèéÞÑÊ*¾J˸?ÂVTSÈÉ
+Õ9>*EÀNçÐPÎRËCÚ4¾*ô½ó³Kâ·ºËÎÀ?ÞÃRST>MÖ/+ZMÆB¿LãèÓ·-ϳé3×»YݼĴA@JKÃD/ÉÔW·ºW0
+?ÎÄ:OØÒSÒ5â8°5,öÐÜäLÃ4OT2J*ÞNÞ+*Ê2Ò*Â/öSÕ²ïàÃUÓOðÆÏÊ+Þ9W4ÓÈÓU׶ÏXÛ¸Ã+*XYèéÀÐ
+ÄÒ8>J:Jî6+ã*üÉB±æÎG-ÔTºE¾4*2õ8-B22/¶J*QVZLüL0è+ºÒSÒRÛè³êC¾@*JÐOùÐÜÔÀÃL߸ó
+***Ó-îTö¸ZZHT?Vزß4?Ç?TÂRÏ+*ºÒSÑPÂXVJ?ÞZ*:T;B/R0¶±+ÌR;OÌJÇLã4ÛгéÙøD1óTãÈ>UÐ
+NSSÈ>2WAS1Èúà*úÑRÔ°ëØó4>¾Ò¾@.JWWòæïÎK=³á°12î¾*8>ë;âGB+²â2<OË,;üÓÕ3°ýY**ìYÜóÙ
+RÅÞäI·øÂ4ÔVØ´ÏUÍ8G3B@õüýý¹ø.¸:°2S@²äXÓQI8¾U>ZÅ>³øòO»7ÊD*îäüýåÝG¸òæG¹úýõ¹8Õç=
+:ÓT:.ÞOÏÐÓÉÕYÜ°;RÈ:Û**JAÜäâÖ÷ÑR¯¾á4ùÝúÑ¿0C,ÅÌò¾JÀòÚ,,,JÐAó³Ü5AXÚ¸»ùýݹQçìÛº2
+-ÅåýõA@0èÓWÏ,M*¼ÉA°æÎOÁ@6ß</èι6*æ5YÑUà.Øò3ÒZ¶ýÃô½÷Oû:Ø.F7ÇÅ2>;Â/6,Â*Â0>-6.º
+SYñÏLÃ,?4×ýýûöQHÄFÇÐRUÕýݷ¹6³,ãUûÈÔËدÔN·VÙ´ïØóÉB°ÆêAWÈ4/*ö¶ôýýíÏÂ7K6UÔVÕHG
+Á¹¼ë+ó;ÒRÏO×À·°ÝVSTâRÏLÌS?VÑ<¿à³Ê3Á/3Ï;Á:+SâæÕøD3/TºË5õ.â4C´ê6ñÝýííD1ÎOÜFKÓÈ
+µÅÌ5ÏAµOé@Zñ¾,Pë¯Q´5ÎÔLÂ4+·ï³¼á2EàæÌû4ÐÙÑ5úýúÔ»Ðñ¶,KàãZWåÖëðÒúT/Ì·²JåÀÂPÐYè@
+¸V¸É4êÜÓÕ+R-´ñé8T3D¾6CØÃÔýýÍB>FÔ¸H1ý1*Ú¹ÏÝTçØQC¼ýÍHúúüëÍ/O,7TYñO°Sü½I³·096¿Ü
+>ñQ×Ú÷é,ú¸ÐØ.7ÈAù½ÈÚµùï*»Ü9ÏJÂ6/*ºTYÑÓVÓTãTØöDAOO9OÐP/ÙèýýõÚé˽àýùöÕ=ÍôÜݹÅÜ
+4AÙóýåÌØÑáëÜýý¸½.Ëñõýýõ+æïýûGõ´³Ø»Ìüý¼AÜÔÛ@ÍÒäÝíÓ½XVøùýíW4OÀàéúû÷Ø=±PXý½W7éÍ
+KÆ</**OYèé*Õ+*ö@ÜäU·C²è@XæÖøýýO=±ÂóýùÜôÓBÊÈ´ýYÖMÁ´øýý+õNïZ¸´8ËZÍJóàéÚ8ÑýùùäA
+ã4IMÇèýíUè2àáÖÁüÝ÷ǽÈöMOÛýý6ë9»XùýýÀ·»ÇÑÍ9õý½ÞJÂ@»4*JÎúýýÎÜ0N7*¾°ï³HT4HÇLÃøó
+Òýý9¸AVÉýQõ>QËT?:ôýá»ÙUÊùý9V¯Cµ/é@Q¹ÖÖçäëýý9òýýïù·:JÌ¿>óýõÌ°0:º´üݽÔéï+.3Qº
+ýÍõ*ẹûýº±IFÆ<XèÜýYÓ8XñQ;MÄ6GøÑÜ0LGùDAOêéýý;Á¸ä¿ýYIÚóéÖ»ù÷ýïѹᱹýüý½ùÜïÀÚ/
+0³·áWºËýýíI¸MGƺï·H¯û9Üã8Pº=Øýß½ò;Â49Ñ1çýýYÜ×äWý9ËçÄ<XæöÃêý±Ôñ/ÍKíèéÄéÂ>±Q¼ë
+O»ÑúýýÚ¶Î÷íýë5ÍWù´ì¼ÙôêåàÖD¸Ø:YIâ0ýÙìôPÌøDÕýIüÍQÒÞ3âS»û9üO:UT÷ÛýÏ1òïâ6H8Ræýý
+Áì·äØýIú22@¶ü6âçý±¼MO8KíèéÞÓ+*Î@ÜäQ¯÷ñÇ?W1Ñùýôµ4Kôø½ììÏ׳îâÃñÄË9TÁÆ=³-ÇðóÍÕñ
+ýÝC;Hïúûýý½ÕûýIºAôÃÍMØàü1í¯4÷ÐTëýíKL<XöB<?¸ìWȯÔøýæÎÊöDÀ:ßòüÝYEºãÌLÈ>SàòO»7î
+D**ìàéº?AÁPóÈõõ½ýIùD=ÓúüíëãKÏ<äÏYä²·KÛ4¼>åÅÈDÜ4QöýýAÈMXºËÉ=ÁùýûÅ3÷ïǸÇû9¼ä<°
+º=Ìúýé?NÄ:õÂíòýïé7¿äWýAýÌÊFÅÀ÷ãøýÑúÀÃ9OôçÑQÎÜÒÕ·SÑNËDãêýýXÛÝZíòWÚÉÜýAÝò7=ÃÆä
+õøñ<-ãéOåÚÛTW3ËýÝW9<ý=ÆHóýÅÙßàÏ×ýýýÃ7÷ðñýñ;ÔçÁèéî,QñP͹àý9Qç³ç<åôúýÃM·:ãTýýÍ
+¶FVÌ6çõýëU±<ÃàCâÆÏMÆHÓÕ/*ÂÕ·ûÞ³õ8ìûýýÌHìRKÔæ¯åØñÕY¹Yåïýµõ=G;¹Fý½êTGæÑ×óé93ã3
+¶@Üä±ö¾=øýüÖU·EÎÔüÕíøá±8Å>Ííý½ÝºÕдýIøÀXäÕûûùÚ+M@ýý½ýÙä1@¹0G¸N8OøÐÜ´Ú´ÜÔ,Þ÷¹
+µW?Ô:QæJKÑ<´üHHÞïùôCÈ8L+5ÉóúQ1î¶ØÅÀNCÝôEÀÆÇÕü½ùU3ÍL6M3ڹʳöæ°DFü2,Q9¹NHJÐZ±ë
+ý=åéùüýýã±ãØÄ:;ñQÏöØIóé:é*¾=éùôUÓSÓ°óRMTñá.5EÂúÑS×àãÒUöX½Dí:Cº>¿èäU°ã5BíÕýü¶
+<Päî<¹ôT:8O8NQÝXË3ÆéÇó±íÞ±é¿ïº,ËDöÒXÞ¶ûä³µÂJKÁ8´ý9ECºUûàÃÇSCCà/ÍMÊ,*¾ÑAó³¾U+
+*ѸÒÄÈ=TÖ.*ö@°òB4/ÉÔUì3:MÎ2Çï°Äå¯ÒTÔQ¼ê>ðÆ.³·ðÂ4C¼ÚÏL¹ÖJ+ñå;IòäÈ=Wð2TâC³L.16
+HÃ6÷H·0OØMOÇ=¼/ËIø²ýÅWÈý*Ñ2Í>Nʶ¯¸4¯ø>**L»SY=ÒPË*öQÅöÒC±ä6DÌP7F»2W,³ÈÍ<ÃèA¯Û
+TKðºö:ðâè?UÒºÏCWÔV×àÃ6Í-2ÏKºäÂSáû:<²åÊA¯îöI¾ôÆßÀOÊF;ð°æÊ:,åðO7HÀîãüíYÉ,,OÉ@U
+Ö¶B´ú:¸.3<NðÆеó³ß±æÇ<MÂ4/*¶MÇ@·4³4ÓW¼ìäÛÅ5ýûØÐÅÁó4AWòÄ<òÇðÞ¿=1ÍIÁºÏMÄ0ûï-2;
+µòæÎI»ø*Dâ37èMQ8H¾H·¾PßóÏ-Q8GÎQæ*æEYñMÜÚÏ=åÌH¿JÏïDÀ.3ÑIó³/*¶ÈÒRÏJ7*öRÏNËDÃ4@
+¾üö·ìÄ+MìÛÛ¹XÆéÔV4ÇÑãÒú*<ÂR;M¿YðæÏRGºò2ÐMÄÀõÞÏæ*QÂ2ÏLÖÒX9çúú3²R3ߺ7ÍT°ÄÍKÁ2Û
+æDÀ>ÇÑAó³ZU+*æ¸Þ*Q4Þá÷ë¼õݯ×Ä·½¸áYH1É@ÄÊFëS¼4WðòF*ÞÑT*Íã¯ë¹»,GàÒBÞÑ㺸;:?¸8¿
+P¯+á-öç=QÏX»SYáÛ¸ïèÃ5AXÓ*ÎUZJG¾µVH×CI¸òêï»ÂGP9<?T3G·×Ëò6Øò3>,óçÑ;¾°¾-5¶+,VÈÔ
+XÈô*¸>²@DÞGOè-ä4C¼<?>Ê>BRÍNçøÐÜÄTÓL?,Z4î4*úOúT÷ʯÁ1:AÂ2äì:CC<æý³.TÛÀýÚ;PÆ<7
+*Z±ö<LÚQêÏÞ*õÂÊØRÐæûùãCïÈ@48HÕÚ·WÌÎ3ÊRíÔJü0ËE½6ç¸åÎ=òÔç>µà@O<ÊT»TY¹B*ó´HñüÚÊ
+æò·Âǹ<ãËK°Á,+òàå,.5E÷ëÈ»OPÄ:OøN¯ZYëº÷é¸5P¸â;ÛÐ×@FÇÎCùºÏHüëÅ1:ÀìAMÅÁ»Eµ7ÕéÁÒ
+1:LíØÌôáÁU´·ìîÅÈÆÏPÕüÒÕ3UÔPæ¾Ó2¾J¾S¾2*Õöú·ÏíÀÓ<ãUçÐOè;LÂ*¸ïÚ±Aô÷ÐéôÜûFKÂ:ßMX
+Û¹ËCñWóÙÑ+ððËÃ8@µì°ÛDQQÓ½ÚñåPý¼ØòÝI¿4üìC9ñ±Æ×Ýï?¹ÎAåøüé¼æL>æÐVYèéJÕ**Þ?,î**
+ä+à*ÚÚÙóÝÕ@ê?QÝRÍDZØXðæÅýÅé68DÉÐøøV37Ø´ýÉÜÇËDÓÐÐíÇÅAèÓ@üëWéïðÅBGÛüïÌJGû¼óP
+±ëç=ê¯Q½îÎNPá-ôäùÊï>±¹ýHûÎI;»SY-Úì-¾ÉMÈCøûùáýôËç@NÔé?ÂâG÷¯VÔƯÑW¹½ú1ÍMHçPLá
+áÊ×Mî»Ö±øöV³íñËÒãÉC¼ÇSó6çê×÷Ý-´Ã/6D·¾Èõ?1ÓøFÛÚFZºÊDÄRñÅ×ä7ÞòQ»7Ö¸**îèÃ-FR*Y/
+È?³úT¼»êÍíYíCÝUÎ2øï¹3?×Àä̱̻ñF¯úæ¸/Ź,?ëFÜõÝTï±LÇÓüÎ3>YÚ7QQÑMêÀäOýÍñ5=XìîKç
+×Ýò=7õßÍÖö@²ôîCèÜOA.ÄÏWíèé8Z7Ú¸°´ï*ÌT=L¸ø²ð/Í=û7½TXÈ04õX-K;Ñ1;µêÛñFYôR,ö>Yõ5
+ø²òõÛL3áIÃOÜìÇ?´üÖTTÑIFYñ<ÍRS-R;ËêGäHñ¾Á³Âã6íÁRÏÄñ¯¼÷L8ïøÐÜ0X/Ñ7+>ØÓ-öUÕÊûMA
+UÑMH2ÍF¶BÃFH×9-òͶÕÁØÞïÁ<Ãéü×ÌA¸Úäìí=ÉËØ°TµÝÓÇAÌà<µQÓÂçáÆÔÓ1ÜäQP÷°ÅÏNEÙñʼä+
+õØÆÉB-Í>ðúʯÞJèóO»7ú¸7*XZ¯ÙðZÛT³ÈÁøâÇEæ×Ï÷ïýÂÁÃÒP:PÏFHõÎûÊ;Pû¶µÖÊÒO¾ôÛõèÓÎKÃ
+@÷º¯ò¶MÍ»ÓXÏ6GXñýÈYê:ðNÈ-óÌÑ:±Ø-¹4±îÎL×Níñ7E;×Aó³ÔUÕXÚL*,+8+¾ýEGºLRÒZãðײç
+ØÅرZ¶*Ý÷D1²çѹìIõ;á=èý4ÅÊB±øZÇõÛPCMèêøDõ.Êéò3ßÞÇçýÕòàä:PͼøôE-3ØÚFÖDüÅÐQÍòà
+íç0×çÂêÉøD¶ó2*.@VÓ*üè?²³ëõÓÄÇÁT/5Õ´XÙÁóÍäÞ5ìÓÏBºÚ;OðؽÜñOLÕýÔ5@¯òZEGÜäߺ<ÉúÛ
+A´.÷NÚÖP½ÖO7ÊüýN@°üJØÂѵÕçQ6Ïì¹4ð·2S<2Ûý˱RÊNÝÜÒÕ+Vß²*é/ÓG;EÕÓZôöÉÅTNæA0OVG´
+»»æÞºßÛ@4=ÜPä³9YñÚÞUÝBËÚÎÌ»²û̽÷ï>@·»åã7KÇ´GûDý=ÄAáßÙè¹å¹èPíIPB¯>»TYUW¹;*1Þä
+¯Ûè³îXñïÁ¹ìÝÕ=õùY<EüäHïX7Çßá2VTYñ¼T°ÍìD´ìPCGîòýÈà.´úDÚ·ûéüÅ6IëîXí7ÙEI6Õ»ÉWàV
+,ÞÜÒÕ¿ZQ+Þ¿+5@ÊAXåî·ÄGÇ8³3Î×PXRÎLÝ,Lñ=Å=ø²TÖÁóðÚï¿RóÁ×ñà·ÏF:¶<ù+DÂ7I9ãÄ9OÏ°Ë
+5æ¯ÓßÑVLX±ôöMÎP·U2ê8GÌæЯ+³ü626ÍFí°ÊEÑлSY-ÛX**î¸,+K*Ì5ÕUÌ47¸NDZÀ²æ:LÃ8SðÈ8+
+ȱ1ÏPÎúÑN¾îâ;.ç:µÂR8C³êÚ+âíÂÇ=RÎG·*·NÌH¯LÂ18IÜ2;HµöFèòǵ¸Òå7Kî,Èð½4;T±0êùòÊÉ
+EÏÈ»SYÑ×ZßÈ?5ÔS*¾ó**è+úÓSË8/ð=2;µ.S=QËBW¸â1û+âÆ<PÊ:·K¼ü>Àâæ9GìPËE¸øîÇ=Åñ;²2Î
+IÁ:WàÊ>?,âÅ:M¿*>æÏOï/HÆÍKQ³Mº.ÅÙ¯M7öôÇÔÚ;Ì»SY5F5X5¯U*B0B¾ñ×X×<ã3=QÑF?UÙ²ÏøÂ
+ç<¿âR²Ù±àN3<MÄ<WðÈ8;4.Æ:LÅÚ;LÁ4GÀòÆÏîØÎ+½ò2<OÇ8CÀòÞ3úÏM:Ñ-í×ÍHý×ØúDzÚî;/ÄûSÝ
+¼,*AàôP+F@UÔ²û,PêBðÓèÑ2ô;Cæ>0:::6Þ?OÈNçÐOÉ>7,>3Â36/Ò=6=¶PÉBç44Føî8Fúé>7·3MÆÖ
+TôO»76¹1*JêÓ-¶¯âηù08F³Üé?UÓTÓ°ã4ÒD0B3¶ã*A@:BîÂ1ì>¾ÍøÓëKÈAó;áÄèÒ+NT׶YèéJÕ5*
+*ÜL*X,¾ùD0ËD³èÄÛ<ÏJO9*üç<KÀ,+T±0ÉK=QÏTáÒ»SYá×°çÈOÉ@W±*-+18:P:XÞ·ûLäÊC±ÜN3QËð
+1*Ýâ2ÎG¶êÚ¿M÷2Ø.ÉB±Yèéäåì6J³ëË+BXÞÈG5Y-Ï@Þ³Ü3ÑOÇ67<N2=¸>3>WáÌûYY¹Æ+ñ³òÊ×XUöD
+íÒ3=PÉ>SØNC³DÃU×±YèéJ×É**ä8S¾Iá@8Û·ðÌë,Õð,*Z?,:5*BYçàËùÐÜä»÷èãÉAYÛ¸¿X¯*á*Ñ+M
+*++8êÖ²éÔKù³4¿4?è2Þ+*./ÚǾM*25¶ß+16îR6Ä+ÞÓ¾Â.ÜÄò*ÆB;×2**¼ÄÕ¿WÇ*¾õ4ä6رâ´Ã,Ì,
+*ò=QÍH·è>0JF³D82,B1*Ëà/Ð+¾BÀ3-¹³6D²õÝäD1**PG*Ö»Z*A¹î´.¼ÃÕ+XÕÝPÃGÁôêBWó³1ä´ê´
+:Îø´ö´HîÀêÞÑ÷¾=S¶¾+12ÞF»ÀÃV0YÁôZÊ+ÎöÃLIÌò½Zé·ºËîà?Ô´2:à´æ´8îJ*ÂA¯ïé>6ÔRÑY10Î
+:ôî*ìÖêòë72ÚÎÀ55:ÖÊOû1²/+ZEB¹:*DúÔ10îFH9Z5µRZµL:>¾ËÁÉ¿¾Ä¶äM,0/üÀÕC²æÎG5ÄH
+*R/*æXZ¾I¹äÊصñèϱíÄëÈóD*¾Ôñ>Æà.6×·óèGýDH5*ìè,XNIJU-ÎÓ*òñÛÂÌÏ*EÕC*/AT¾Á+2ÕÚE
+*ßL,4/ÜÀÕ×Yݺ÷4,:ܼÓ*6*FR¾:*9äVسèÌ÷°ãPÇ<?TL**Î<¸+áOëسåÝijëÖÌÛ8*Síº7VBÐ02ì¶
+Þ+-î0*¶±å7¾Á=>ö4Üä·óèÃUAXÚ¶Ë+.+¾Xá+¯+Ü5ÕWÛÂ?±0ËQ¹ÄêÕWÕRÃ<ÎHÛ2¾**÷òSBJ:J¯ÞNÞ+
+,Ò2ÚR¿58¶+/´UزçX½Ë6åZ¼P*¾¸-²â+*Ï/-õÜ0FÂÕ¿íýàé8úVßë+-ÒØ·XÞÈCUäêײH7D±Þ¸ã°ÃÈÊ
+4ó¾åXÊëC0F²¿E<¶À+B4A²çÐÛüDµçÞ³ôêÍݶ*¹±Ý*JôC*»0*ÊåáËÜ÷ØS+1*¾B*ïÄÅ-*ÀGK¿Õî³á³å
+1Þ·ï4äÊC°âÈ÷²èÒOÁôVC°@VAVÐìêVà2¾Ï¶OÉPëóÕ3IØP+BI*6×ýá*Þ6¾LÅõ-+/NìºëÒ¾-*îúÙ5-²
+¾-äÍÎÞUöDδ*:R0Ò7Ê/*Ò-Ò.¶µñäÇ5µXÛ¹ð8Ü,*YåëׯàÀ÷à/´?*ÚZ¾L¾±5¶É-K*àÉØ:ÂéYùUJîú
+éÞÕ+*î?,î,,È-à-ÚAYáÌG±ô6ØÏFÛ´7E´êÔ˱±¯ÙVÇ<?TÒ-,21¶Ð*Ã,Ó+¯+³,16θJT+5¼öÐÜ0ºÛ+
+*èÓUÕK¾-5Jܺû4Y-ã,ê,*LÛ5ëúسçÄûØO5@S³TOʾ,?0:,Z>îÀ-X¾Þ¿.FJ+-OÈÓZ¿1½èÕ¿Õ×ò;¾·
+¾U7BY±äÊGÉ´*:à@â@Ê7Ô/ÚK²áºãTó,ÍUб,*;óèåøDùãéAYÑ*Aè´LZJ¾ÆK¹´º7ÒÓÖËZI2EåÄëرâ
+ÀûàO°×¸O°¿´1ìôPí8C8ÏгVÖßÉøDÖ@1*Z¶óæËUåìÛ=*:öîË+ÞºøôçÙµ**ïËÉPVÕ**Þ?A*RZæäãÙµ
+íÍøóF*¾Ã;=´ÊC³éèÕUÕUÖ7E´êØ7*¶´éÌ7506AU°ÈÒÒ*¾T÷°@Üîúéîå:-6ÖÆßT*¾NFîÐ0<è**ûÔ<°
+ì¾ñ¾2=æ0Y-Þè*P+X2J¿*.5/:âÎ7ÓÛðD×´W.ìÒ7²5+îêØG-´éÓTÒÞÒºæÄX+Á@Öîúé:.ÏåűÌúéìS*
+JÍ,0<¶ÂâEîF:ÐLÃÒR°7Y-Þ,*LåP+XRÞÃ0ÊÕSZàÈKÙÄë+Ð/ÔGöØ+¾ÖDõÔWEµêÎGMÄÂ÷Ø?Í°éUàÒ³á
+äë÷Õ36ÉQ+FõR5@ÊèËG*Í1ßBº2¸¯9ëö>*<´ÆÆ;ò:.î-DîØCSJ+*ùJE7¹ôÇäHæ<Ð0ß¿5¶<ôBFöÀ-M2
+D2G¶;2Ã..***;,8Jß0ÖÖ±AF.¶¶*WôÃ,/ô4,V±D.@Ú÷ÅDÀT¾5¾RGR¾¶Q.Ã4ALJÖ±½Þ<Q¾îJJÂG0Éú
+ØÏ×é-¶±µ6êÖ±æÎOÉôJç8;¾ZÙ@Øô,*ºD°Þ´ß¸ã4Ó6æP°?ES¶ü³HÓƳÅ×±1ÓÊùÜûÉ>ô*:Äô*4À´¾GÏ
+¾äÃè<¿H@é´Â´8æ¾ÉðÔF¹*êQP<êYÅ5ÒÕÇ-ÎÍJÕιüÞ586KæJ>ÞÃJFJÀ4+1Îìò,ê×ñS·D:,ÞHIÅFKK
+*/LÕúϽÒ;Ò,Ú*Âè¿,¹LR=¶;,¾û/ÉF+À-X:òÌYðéNE´;*¿*Ñ*;*ïä;*¿*Ñ*ï*ÐëØ¿C7Å8H»÷ð7+Â
++ƺöà*Ä,L*úÜ»úöïÙA9H´ÕÌÚ´ä¾÷4*:Û¸/+¾X-+ñ*M*ÐVÛKÀñYñêÇÙÒð9¹/Ý3¹ôÙ-°é¸Æ+ô2LY»¾
+òê+VåXº¿-D:7º97Jí7¸9âõLÉÆÎVÖYº7Z½ï/1Äî*;X.à+<FöDÖ¼8*<>çSôêÓ±ÓAë+Ú3.ß-+ÌúM7KO
+ß/J*.Ñ-Ã,õÂÎPîL.*ù5@·µB1ÄZÃÝ0ßPÐ9:Þ*JGÞ/3¾ZÁ*?ëLV×S´Öô*ïàØÅ+*Ù¯¾S´ö´.ÞÙOMäé@
+WÖT3SÑ,*8:J?¾Ë<³ÉØKöùYÑê×áì¾ûÀÖ8æûæé.-ÙQ<ĸ¹ÖβÞ42å¼»¶¾O<×/ó5DJå1À·ñ³1Üîà2*¾
+ÇÀ5¿¶¾.1ÜôJôN÷ïOSÔçFßÕ9ÊÈNæG2îìIT+X*LóJÃÞGÏÒ3ÚJÁ*¾°ÇEJ¶¾:ÙúØ5NE.JßÂ3U7B:2+¶*
+*¼V×±áÄ/ù?60=´6ײñíZW*J·W;Þʺ²6º5+îêÔSÁ´ê@UÒ.?Tó²µ2¾´Sù@LõúéFèéåÇäÞ8ÈCùëECRµ
+Ü0Wë÷FÛôK9UÜË>RO-Ô0LìØÀ0æä8ÃÌ:7×üFß3/*ô1Üîà2V+èò*F3Â-N:/¶Ý÷D,ñÒÛÐ>»:¶·Ü*ºÍ.Ö
+LîÊõ¼ö9Ó*A*ϸÎ*ïÌR4?XÎK*J×â/:¾,á9AÄZËY-ß,è¾ÖJÙBCTÄêÖ5´E0îå<XO¹ÔçËõRD·ãÈì2D²æ
+,×U×íº7¶EZ=ìÜ?*ö³ãÀûèOU@UåâP´WEC¶ü³üãÁ+KÖ°ÝÛ3OÖ:èÒè¿ÕKíÒ@>æ;æáRNÞNù+7ÖÜIFJ6D
+Jà=*»À²ò,À¸Q>îà5X6¿Ûèî:¾=¿¶¾.JéÒõ¿WBÏ7òKB²Î-I¾ö/æÌÇ*ù60+51ýÏ4î,:T1*ù+¹LRÒ¶;
+,+*ã<AFÎúïà2´ö³°êÖö×J*J´ºäÇCÉPWD²ãÆ+°+,M*X6RéÙMM86BùËU>Ú°àÄ7åÇ°æ,æèÒÍG7¾E-Jï
+àÃ51îìÖGEÔ5@UÔNÓT;ë¾T>¾îè´ZîúéÐ3+Rü´.æó7å=<Á.Ì.ÔIÐ*Èü<Êêéò4:ÌÑLX@×ÖÕû/+öµðà
+·ÙPWD´ü7Gºöê×MQ̾ù´RäÙW54ùöñ俹èÊ9=üEÕJº:X.WV¿Ã5ÕÌÜ+*ÇAYC*êU/îøôëáµ9I¼úºÝ¼úò
+ÛM1XEÝ0ëÖYܼ÷ðã½÷¸09ݼúøOû³ÅMJàOÉ32иWÑZÞÆÙÉMÓÚV°¯Y-àäÓÛB±çÐ?-ÔÉA4P6ØJDåÄËر
+ãÄ35äÃ3EµÔµñéÙùõïÝMÕWØðâ̶ëºòÐ3ù/Ê+5´ÊØçFßÄë+²1,¾µ=ëQÖ»0*Û¯±ôUÔJ´NÓ4?XêLÛñüó
+õÎ/åÝ0ÄËÕ÷°èÖWÉäVC°ÞVF6SÉ´WZ¾QÑ07×ZàðÓóßÉ-ÕHýûøóÛüøê¿ÑԺׯµùXD²áÆßð/ÌËÓ?+ÒÕG
+*Ù-**î8*¾µÑä6C¯ÜZÓLÔ¸¯E8öÐÜ0Æ7YÁ±K²Ì7Ù³èØ¿-Å7K¹PW¶Ë±ñäËD±ã20ôî3í+ÃÔµõøûýýüûí
+1Iº´9GD°ÚöøßUPÂS¹¿éÕ.ZÔ0ÜÃÜG3E11ÌE²Þ²ç<0Ú»UYñäÆ7MäVD³è*Ù;°5QXFµ+µÔËD³ëPËGEÕÝ
+ãÑ/-**ILíííí÷ùõYüý9°ñÖO¹Üú¶é¶ò6D¶ìÖÃÊ¿¶K*<XÚZ¾R*8AÌF·íÖ¯5´±ïâËEA.*äð=³ëâ//:
+·üý½RY5:0í0ØÚGºõêßÑQYH¾A*ÞëÏMåXÛ·ôòï¶UÉÙå¸/¾+*PáÍïìîùý9*îûç1íËøòåÀóß¿üÞÛ¹Aâ:
+,*õçÙµíÜS*åÅYH¸óîÛ¹1ðÃ3B¼úöó¹**++¾YüY-Î9É*=P?M+,6´FÄ¿Ö,*:Å3ÚË¿¿¿3¾¹8Æí><ÓÈùÍ
+ÖÊY¹°âÈCÉ´WE´LWØγ״WFÙ²é@DÞÏSá07èïó·,;ó³8**,VBP8̼6¹ÆËÍAÜE´èÊý<>÷êËÄÅ°9°5¶í
+L*FFµéÔ³ÙPWEê´WE¶î0,OF·1ðGýÕÛà<¾ü¾+ºì59WUÅHÚóU<¶-Xõý:ɵOô³åýîZGÌSKH+R´Æ<ýÐ÷D
+µÔVC±æÖ·é0ÓOÑ´ëضEïäWÂÙ½éÄëD´éäóæñµÒïàéä*Î;ÌÊ¿ÅI¯ñýûͽ8F´èÏ»YÀøà;ÙÄëE+AX.J;
+*º5½¾êÒ¯áPëÞÍ»5QÌÚJF3QX,YñËç:Ö¾GõR5úÀSÛ×-Æ1ßBÀ1KÕB7NæK2ÞêUÈLFZ²H/¾Â75:ôBF:-ç
+áñ7:/3ÖA4>Ö53J/EÞDUB¯B4Zê/1ïΰÒPÎL*+ZÕ0ß¿°-ÍLA¹QÊÖ5ì±ÖZÈ+ÖIK.ß8Þ¿ÂÚ+ÀU½¶Ï:1è
+ïÂNX¾ã¿DFÐÈKÀ<¾4Ź½3N¶ÏGÉöØÞ³TôêØöîÎØRó·´Þ´Â7ÚÃ×S¹²Ñé½ÕßÓÕ·**8²<×·õú?´ÌI»ñܱ
+á8û@óUAWÂKÒê*ÞNE¾5»*ë,èáûÊÚ·ïÜÓ°ÝýD9ÂX·ôêK?ñëµý<ÃG*ÂÏ+ÎÀÎ7.èÞVÙ;Øßü¾ºB*ôÂ,DÕ
+ØNJ¾Î¹ü¾6N,ïµÜ@NÞHIÅFKN*ÓEÛÞ/A**O+ÀQãç¾CÀMF:1JIZ-9´:¸<?Ò5òµÖîJ3X:ÀZ1îJLì*ßÓI
+ÚRÆ3+Kõú3Ç*Â1ÞðâYÌ;K3JÃáßÝ**QØZ3YñçÐOÙä7Ú¶íRÙ´íàÄÁÛ¾Ùç¾D¯0QÍWöüÏ?¶õ³K*2LÏÖÈÇ
+A×÷ýûåE1X¹´Ý0ïI¾ö,èîÚ·òæÓE*ZòäË=µìäßÇUåìÚ¶ï濸;*åÝP:.9.Î4¼±å¼ùÀ5áCY±é×5Yîäé*
+À*13:ûVÞ/CÂ@Òù83BóVóOE3*éí¶KîKF¾NRüèéXî*3È/¹Ç¾åÚ>´BF»-ûGVRO¶À2ÀG¾-3·-.ÕüîLK8
+ÂÞ;Ç>ÖÚ÷ɹN2,Rï¶öA*ÞÇ@VàéÚÚ¶ðèßÉ1YGTõ8ÜJ*Á1ÍÛ¸/+üXHºôì3ÒEÜâíóÒéä*>À±ZK9ÅÝâä÷
+ŵü¹ôéÌã¸Å+ã×ÁQíܼ1*Þ÷ïáµíܺ÷ô×¼üöç¹¾,åÝP<ò8.*5LZJõ±Y±ê×õô:@*ÔÕ220´ÍÞé¿*ÆD3í
+JÊ°åÜçFßÕÍNëÃÕG2?S¼úÏÕÛR*¸¼:Ê<+0ËÀDÓõîóB/¹î³µ2K*VR3·-.ÕüîLK8ÂÞ;Ç>ÖÚ÷ɱNBºSï¶
+öA*ÞÇ@Vàéú×±çØ¿ùÔëDíôWE´éØ·áäÕ¯ñäWE´êÚ/1æ´+ýÌÝÌè÷Õ·**.:¯´óðïÎý7×SëÖó¸NöXÖ¿Ã5
+µ8Û2¾2ô÷³DµXÚÎøç¾ñYñÃÞCíËG>±Ýǵ°ÎìöQJ*AíP4ù,Òç/1î,ÚWë÷FÛôп-+¾X;ʹêú3@ç-¿Éñ/
+Þù/0Æ,Úíº7¿·èì+BÒ3FJ+°û·À-¿ÎD²>½EàÒ-ÚEO¶Á2,*Ò-3·,.;*QüîLK8Þé;ÇÒÕÛöÉETC´RºSï¶
+öAÐ*õ5áÆòé*%
+d
+432 139[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7506 string uc
+*Þù1¾õ½¼íýúÜý÷»ýñùüåõûÍíùIÝõ½¼íýúÜý÷»ýñùüåõûÍíùIÝL*%
+d
+/sl 60048 string uc
+äÕí´ê9+¾Ñ,Îê׳ëدñéØ·ùÔ7Ù´ì²E´ëØ·á@:ÞÝ·éäËE¾¼çÑëÈ*N+*XÞ1²ºCYÝý½*¾ùÍÍG·ñèÁ-óæ
+ÐWáä7Ú¶ðêÚ·òä?*¶·ñâ»ùPÌF·´ÌFýýY×á/-öåZ2ÖÞ¼¹Ñ.ºK>ìê+ZåH.Þ7Zå8*Ã.ʲEôÕDF68ÞØ@ö
+P³K¿8O-á-;*6õÐ4->>ÇÐNJ1¼*ï5ÎGAòJ²W22¶J+P²-»245è.8Î*Û¾Îì+¸RSÉÖMí¾ðÒJ·ß;L**A<ð
+.2XûßÃÀFJÆM<SZ·Ï0µÒ<,î*YTFXûO»àÃ@N2*ÔËYÝÔ̼7EÁPëD³çÔÃÒÃÐ+µÏ³øËE´ëÜ¿5.ÞìØ3Fʼ
+ñJ·ºW2¾<ÆL:WæäÅ꺽øéÛIYèÊC¶´â@ü¾âËMµ**åÇ518ÚÀÙñY¹I,A²ñJ+Þø6çZ0îè4RøÉZ<ýÁÊP;U
+½êå=°öDÏÄW*¾³-µ8Ú¶¯2Õ²6â7,ö¶ïâÇáÔ;òùÐõ˲Ü@.ÞAæ¯Õôõýýõ¹öèÕ-9¸WD²87زèÔ³ù@ÌÍUå
+ÌGºöîÛñóæϹõ8ܹô@î³5Ö*ÑZÞÆÙÉSÂä2ÂñYñëÜÇ=ÕÌÛºùFH¹ôêÏUõ8HÇ1Y6¾Ý±õXH»õ࿳éíMÓ<Ñ
+Õ/+ö4+ýýÕÝGøóèùæ¸ïÚ¯5Ü8NQîâÏ5Òîûú÷áQíܼûRI½ûðúØYß.åÝ0ÉCÉPWÙس.õWÙ´íà*¾ÙK*+AX
+¾EÄëXÝO9ǻ˱îôý½úêѱõÙ³Àé´7D±âúB¯áÄ;Uô6DδÊëåÃ=ÕXG¸óæ»SYñèÐSáÄ7Ù´íFF¶ðâÃ-AìÙ
+îDî@0ÞáÃùôËÚ×î÷É¿<<ùD×+ðº7X弸ã·ß±ÜWØ°âÆ?±ÔÊB±@à0Ô/úÇ·ó0*ÜÒÕϵíÜ/V-´áè¿êÏêñê
+K,üëÙ¶óæÐõÍ8NÆêéú3ÓýýÝÕ¼û¸õV°Û¸ôêË-1×SÁôÊ×,*¸0WØÎJ4µ8ÛQó³üìGºöêÓEQXFËÅEµXG¹õ
+îÛñóäÏMQÌF¸ô²IÛó÷I=:×Õ·DÅýýµY»ùóü¶à¸øòçÉA9áUAÌE´êÔÃ+0Ú-µï¾PXG¹ö<ùD*û.,*¾ò@ñ<
+38¶Þ7K²ðÕ÷ºøòëÙµÍH»0íG¹óÀÅãGº+,;,HYH»÷üçOÍHöÈÊé2*JàáéF¸ôéçÌUÅYIõõíݽûòã±1ä/Ô
+-·/*°ÌÚ¯ÞâÜôPÞìJÞ÷-*òQÙîê6ðÇÁ0ûDØÄËÙ¾³QÕXG¸ðÞ»ñ0Ø/2ű¹¸óæË5ÕëGÛÒ¼¸U×»ËRÀIÜäÑ
+±Í̳سìà/÷÷¹ôê×Mõ7E³DËD³éÔW5çH*¶û³HÃXôÌB¯AÚÅ°ýL¿,¹ê-<:ùä²2V2óL/EÞDIî¿´°åHçVì
+ÏòÀ/Ѹ±1:X:.Jê¯2BîØCSJ+*ù溺+:¯×±Õ0ß¿ÈD;Cé2àÇ@BÀ8À*ìÝY=åÐ/Ïǵïàæ:Û·ñâÃ5±ñìÚ»
+-Q8G·ì²D¼¾éáÜ>ÙÕ÷PýýùQI»ùñ4ÓC±æÖÃ5Y5+JôæÇ5¯5Z-ê,*êسçÐÛüD9´:¸<?Ò5ò1×½¼8·ÀðB4
+ÂX÷×ATµ¸¿÷+IC3ùéOÖÁTäºíüOèÇÕ3¸å-0Æ>µã--FϾäÓ@ßÌû+*Û°ËL=ç»ÀîN5VÔë2ëòíÕ3²æ8>B»
+5QX?ÙÊ5µàäßÃù´ëÖÄÒí5â¾<ñD9îçýýúáQåìÆ9»ôêËÕËÖ+¶Oêß5Á*ÕÝä±X4E6ÌÜ¿üDóVóO¹Þ¯,KöØ
+AÖ¼@Þ×±õÛKõ¿úÖU/ÝױݻßÇÕ?FÆ0Ü1-*À×1ÊÌÛÎVÖå6.ÎÍÑ*µØ¾Ô±AÜäÕµÝÄÚ¯éÔ**ÜÃÔ·¹öðãÑQ
+YHºüÌG¹ôâ·±@Ó³Åý@ËâÜä5æøýýüõæ×ùíJÛ¿MõÌH¼-*Ü9ݼûöç¹µÌÁñÔWE´ëÖ¯=ìÜÛüD1ËÀDÓõîóÖ
+ìDË=úø¿Ã¿.¯âÕXP=ÇêÀDù/;ZÎùº7V½ñ,Ò¾¶ô¿+À¿ëà*¸¼:ÊC-3ÄUã+V¶Ð/»µ1ûù³B8ÚµíÞ/Qɵð
+LÕëÛºøô˲·¼ùòÛ=åëUöNýç>ǻ˶ö@ÜäßYíìEæ·ööC½ýüûE-R,¾üô×MQXºÛ¯ÙäWÚéYñê³æRü´ÂSêû
+¿Þã÷,îË»+âË»;¶äMêìÚÊíêÏ2¹ôÎÊûäÜÇ¿àÜ*³¸µXÞéJâW5*ôÔôöåç³±åìøñ.=³ÂúÇÈ+·ÀEÒ-W¾*Ö
+AíPTý¼ËUÑ@ËD³èÐKáåÎSéäËEµKµL8ÛÆ´½ÕÌÚµYãñY3¸Nô³H-áýýóµÕÌÛõüçËÚ¸õîß-÷LçOHºï*-ó
+ï³ØWD±âÈK¹ü³Xè<ôæEÎâ¼Î½*ï5ÎGê³W¾êIÁKÞàÓÕ·ï3¾°å¼¹Ñ.ºK>ìêY³¶öä/ºí98:,ÞìE?ÝÎJ1ô
+êYJRµÜÄ×50¯B¹W5X²+,J;+=ëAØγÝPÒWEZ-çàëúسèÔ¯é0ÌFBµÞÄëÏ=Éü½Â><ùDµLóýýöÓUåìµíâ
+ä7Ú¶ñæK¹õîãÑÕæ´.*í×E1XÙöÞñY¹ç+ZûS2ÎéEÌåFFQ.Ï?Ñ@ÉÕ¿³/*±³>7ز+*´ËE¶í0××Û»µçùåÅ
+K°YñY.ýýýÜøóéW°¿äÖ/õKµïä×¹õ,éÆH»øP×WÛ·íÔãô³µKJàOÙÀò²ðÕK¶ïÞÃ5A.J࿹**îWZËÁ5QX
+Û¹÷*ä³òñµU:×ÕÇEÔåéÚøó·çÇMQÌF9AÌÚ·óìçáåù÷ùåÍݽ*ý/*J½üöãMAûÕ»/¯¶ü³?VJX3J:JZJÊW
+¾ËÍUå8ÞÜôÝùÈÊé2¾Iæá麸óçíìéÅYHÀåìÚÖÝîHÁµíI¾Ï³ÞY**ðÙA9ܸYèéæD´êÀ*L*8J*V¹.*öÍZ
+ùâWãô³¼2ûýüëQÝüÚ³AØPéË-Õ7E³Ï1Ì7Ú¹øòãÁõÌÙUåXÛ¶íدùÐÜÄÖ¯é´Ø@2îâ+T,úD³è8P´íøÐê
+Üä7ëýÝIûòé¯5ÍõPåFG·îܯÑ07DÊôËG»øäíàêßÙöÙYó³/*16î2-BE³E·ÙYúQ°Xñ-NûýÍÝúòé7ðÁóè
+/Ì/Õï´éÒOÙäëÚïEMÅÌÚµ¯3+õYèéêÚ·òäG**JÕ:*æÓ1Â+æ´SÕÙµÙP°Yñ6Îûý½Ý¹òè/¯ÀßçÏ1ß2XìF
+¸öô÷ñÅÍðÑQíHºòàîL>ÜÒÕËO/ZÃ*6,Ò-ÊY··ñÖKã8Ýè?Ç»ËÑöõý½ûêÍUõÀû×UQ8F¶îºF·òêçáåÍIí
+µÍܺøòç±QàÃEÏ*ðÜÒÕSµëÚ³éäë+*Fξ;¾3+âèÛ¼ÊH<ùDÕ;ðýýöÕMÝì-åùÔëزèÖ×µïä×5R5éôóèÓ
+EµÌE³ë,*ÂÙ´½èéÐ**?õ³ºü0MNô³<NæöDÕHû¸ÝôÜ·áä׳5Õ8H»øð÷¹ôèÏMÅìF·@ìE´éT-KÙ½ó³îëÓ
+ÑÃ/>+æ·ZãùµÕJ°YñRæüýý¼øòè¿ëR²,Q8Û×óïØÖ³3õWâDZUX=íÚ»SY±ôê×Uµ0:*::îà*à,ÒÛ»±ááÍ
+@CÕ;*W2ÅÕ·òèÕÝ?Õ8ܼ8ZJ¾*û÷éÅ9ðÙQÍHºMêO3Á5Yèé>Û·òäËEA4Î*:<î.,îHØïÓùÈëéº7ëýûÙÍ
+Ìû¸´ÙXH¼üü÷ESMùð×EÎRÊBØBÞöÐÜ0Ü?**¶±*Á*?+O*¶9WöÓGãô³ÜåùýüéMÝHEõºQíH»÷ì?PE·íÚ·
+¹ð2A6îQ»7¾5*RVE2*îäé4,²ÙºµáÕì>CÕ÷*ÃöýåY¼¸ôâ·áº+*8íG¹ôèË-å7²5XEÇBɶ¾¿YèéXFëG,
+ÊÜ·³9?5ͼAGHîKPYñ.Jûý½Iºòë3±ÃöÀè.Û·+*<ÌÙðó5Ƕ;4Yèé2ܹöÌ*ÞN¾Ú¹A4Z:Þå3?Û·÷úã;E
+ÔýæFÆäéú¿VýýéõH»¹Ó6Ïݼúð/IÊIÊ,6+ÊNFö/¶õO»7ÕUÅXG¸òæÃ+¾¸-*/+?*ÜÒ²ãÚÕµ½½ÝXYúóÙ>
+0ðöDçZWºË±ÍÜÛï¾õì×UÜ8ËRжÎ41,JèÏùÐÜ0àG***õà+ìB¾ÝÉ4¸±íãÇQõìáéõYºîÎæJÉ6âäQöùý½
+üóç×¹ßøÞ»ùôWF¶-*ÅHÏ6Î2îO»7ƹղÏ+1JÞãÓÉØ÷°íâ¿ùîâÏYýHü¼ú4ÚïÒéV.¶û³¼MãýýïQÍXÛúÃ
+õLè°:¾Ió³µ*:Æ0â/+Ú¾*Yõ;äÖÜÅ-AìÅAÅ8û¸öîçùüøÁùÓTè/+QÝPƾ7YáèÓYíùWDµ/2³¼Q:ïÀ,¼Ó
+YÉ»ø8*JGÞ:*ê5.B,î*ó;ò3·¶ïß1êÚ¸òéÝ͵ÝݼÂGÖNT78¼Ë¹ïúݽùåÓUµÌQÜÁõ*;<B¾Aó³ÎìC**¹
+O*A,ÞåË=õÍMÎÌÇùñåÏIYÌÚøôü»úøõûÅÝ7Ö6ëòêéÂ7ò1ÜPNåºA3µCîO»7ʹ+*Jì¿+J·òð÷Éõâ-ºF¶
+ðà+>×Ò³ÂìF¶ïäáÖÛùöðëõýí¼´ñé<Ê-QÜäùêýÍÝúòéÏUçIçÇEQìÒß3QFÖ¾¿¾C¾Õó³QÌÃã×±æ²UÇøÌ
+º¾Å-ÅXÜÂBËÈôßÅQÍ8ûøôïûºùòñõýY¼²,VîÎ-Yñ½üýÝ9ÜøôçS´Ü@íÎß7*H¿Ý:¾Qó³¾X.Þ>J*¾ÕÙ¶Ó
+ÝVòÚèäÆ÷àÊåëF¸1XÞçϱAìIÄÈËùñæÍMåìÛ¹÷ü»ûöõùùåHE÷ËÆã¿Õ÷úêϵåÜØN¯²ìEµQÖ@òÎ,:,Ï4
+ïS»ËïéÕ9Ý5+îüè3Ê5ØYÓºG¶á¸910Û³KÙ1YI¼ß-+.XÍܼ+/õ8üFGÕüºÂ˹9YHüóøýõõíUíãÄLÒýüÛ
+¼õèÙ¹µ7Î>EµìE¹+0¾í*Jãß+0F*¿Aó³Þì/*ƹ÷àÁûMͯOµô7»°µºÕWàÅKÅGÇHè5õâ1FØæë»éX8»¸Ý
+Ì»¹ôêãÍÝéµý8½ý½üåMùìü÷áåX»ùõ4øHOâü+ºá/µ9îO»ËÕMÅ8Û¸òè×ñìèãéR:MÒËÛ¯ÚÄ1ÕüÛزÍEõ
+íظíPÂÅò·5µ¼ÞéÛÒ̽ºíÓ¯¹íçßÍåÜÛ¹ùÜõ³ÚØ;½ÍüÝõÍ9½½úê×½õëAÑÁâUJ¿ìÞÚöÂ-ж-*?,ÎìÍø
+Ø+*½µXÛ¼2-Dòæ¹4HÊ@°Þ¼9¹ßÒQÑÌ»öYàüÏÙ¸0·8Â1ÌÍUåë²üýù¹õÔ3ýWF¹÷öó»ú÷èÕÁ´ËÕ³íÚ½»î
+óïÕÅìÕµÕ8BCLä@<Ë+ãÃPÂìÛ2¾Æ¾-,*óHÓÕ·¹óæ»ÁQÎÙXX:OÊF1½ÜÉÖ=äECíâÅ+ÅôÇQù8H@H²ñúG¼
+6óßÁAî¶ýýý½ü÷×ÍÜéÛÐûUÙ¸û÷ñ¹øñËù¼ËݺíÜH¼÷è×¹ÕXÖõéÕÑSÏ0áÓ-ôì²ëG.ºSYñýüGÚE9Ñ·ïº
+ÝÄò.°Ûúµ¯ÕVÕ°Þ½9ñãê79´ÚÕòéÒíøÇûÞ¯EQ:Wæ<¼ý*EýÝIüõÒé0ûçÛ,8̼ýüùå¹÷òÛñHÝHºòÌG¹ð
+ÆÑHÃØÒ-»Bíݽüüû-ýä*83Þ²ÞÉøDÅÅÏRãƽ,úä×çUõîäÌPõ¸ÕÃ39ìVÖ°ãìÕðÝÉ==´ÛØ9Å»AO¾BÙ´ô
+ÛQÁAÝIºèâGýôÛíÐêAÎLý·µõøûùéÍì¿ÕÅIܸõí½¹àQÇ<GTïE·îìC**º;*A<îO»ËÙñô8äÚϹQôP/HÑ
+ä;êåÒGíد99´Ú¶YAëöZã½IMäêMUHìÙóðÀ;ùÚÁQÁì8E¹û8½ùôî¹9GµÊ=ÙÔ8ãÚàõÍõâñÕýüÚùõÛ»íÎ
+HÁ°6AXGZõ:*ÞÛ=2öÑÜäÉ·MÅ;äÒñ¹ïÿøêèóîçXùòà¶ùØÓº×=¼ëײäÃMEôÑMU0ÌDµæ¼G³áÃ;IüVF
+´9ÌݽüóÕÑ»åÕ×¹µ9LÑÃÁùúûñÍYYÜõÜR²RÄñâÃÍÃ*FKظ¾56öÐÜäíÓ±QìضôPøί?éXHEZÐH>éàÔ½
+ÍHÊÕíO7D²æ¿OMôÃSEÜ»øñÜûúÞÛËU¹äUØQõ1±TééýùÅíÓ½/RíÞÂ1E¸Ë²Y±íÌÜúëä²G·ñ,åð*¾Aó³H
+ÎÞ¾+ý+ʵÌéù1ÑRÖÇ»AõÓ÷PO4´±ñæÛôÚZGɼ7¸°AX¹ôà°YͼêÝT²¶òòáóÁø·ëô363´÷ýìýûîK?X·
+Ñõ²3ñPÏUí´O8í/Ê1*ÜÒÕ+º1*ÞíÓÁÔëGÄAɸÚÌ·9µËµçÑܵëÂ4IðòÙ×µä4óèÀ2ÇPóýå?æZÛ¼;GVÖ
+H;ÜáÄ6RõYýýüï+µNæOØNÇïÞ¼úÚÇ°îXJL40ö×Ü@àäíÏÉÔWF½KÙ¸ÝÒÉIµÛ¶ä´XÌHÄ=G@²1ÏíN3¯K¿ö
+6Aô-UÀêö9ÞÃOûìéKýÜõͼ¼ýýýûéBáC-äÉCNBîJ¾÷Ü÷µíÚ·éÔëEÆúBùæ4ýGâõÇ»7âEÍ4.î÷ìÛE1ËÛ
+ÅFÛRÕ¾YYYÜÕOÕAXÛ¿>+ÌùåOØò@9WöàAø·îB°ò·:Þ¿ìñUäéûáÝIݹåÍýºéSÝÇðå¾÷¸´VÖZÞÚD¸ïÜ·
+Ù@¯Rý÷Cûú÷ï5ÓõLèÈAüÒÕ¿ºïS:Y*J;¾åÉ1ÍG¶éÚ;òMÏÔëúúùì¸Gã»ø.ÌêBWHåç·´×ÈãEåè;øÁ÷±X
+ñWM¸µ+WLÔÌý»úHý½ýúßý?Õ1¯TõX*û+Ë÷ºøFüÒ×;Pï´ÝûÒüTéP,/ܺYèé:KÀ-18F-B,R1î,ýË-Õìß
+ÍV»YèéßíÄÙ;HÍæZKÉG=T-åãÌíÍýZÙÚIùêÓ×µ8éîEÀ¼ó²ùôã½AüCôã3ÌÚ¹AúZ,îJ,²ßÄÌíµÅ÷R?ÜÒ
+Õ+»¿èJí:ÞFÞ7/¶»ðÒ·Éæ²êÞX¹¹ôѵ½ã1ÌõÁBÏLØëÅYôèM¹´ºúññÚÛôÓ?á÷T廼ËWÖXÒòâ7FÚöÁ-Ä
+Rþ;³HíIð¿2.+úSYEÛTR¹:+³+-+HXD´ì@<7ÉÖÉì7Ûô×1çÛñ2Ñ°ô6·³äÚGñèÂùÌ.·ÐIíôϲëÆó°WQ
+â×UI¹¿1?+ó+M+@Ú½ÆùXÞÞÉøDÖõÀ×ÌE¾ÉÙÄWÚÄSëHôÛÙÅI7óíçYÛYîî9µPºBO½æ6D¹G±Íµ7ýèÃEòÙ
+VüJñ+úɼñ¿1>îQ»W<*îøôëáå9G¶4ÛRÓ²95õü¼ºÍÛôUߺ1µâå9ì=ÅÏôôÓóTÚ°ÏPòÔX>KS+æU2¶³1?
+*Ö<¹ý4óÃ0úSÝ0.±+*YæÎKÂ2=äUåïçÙ3³VÜÎÏù÷çÇ-´çVڴ̱ôòÍûܳ¹>³ÚAî;@¾¿36ÀóòìÝã·¾íô
+éßéÔíݽ*-*ºFµñæë²4ÔYA»úúïPMXXìKÕ75ôSµîú3½û8¿øßYI°´U¸ü½¼ß*Ýèé*Ý;*ßÅYCÞÃÑ?1B¼ú
+øóEX-üÀëºJ·ïæ».T>UÕé¹¹ìÁÝXóè·¼.A8Þ½*ý¿¼QâZ²ôÞ8ìÂÞ¿,æ¾Çí19ÆíÍøØ-Q¹Þ±A,îÞ-,.ºÝ
+¼ü*46ö9I½õÌH½*+06åã×áÆÑåÔVÃDÐóäËEô0*QÝãݽ¿´Ï11Òô,<8Öë++T¿Ëõ9ÚÅíÉøDæõ<*¾Ý±/î*
+-4FR:J¼÷êGºøôËÛ÷¿ïäÛMõ=Ü·ÀÍÞ=**+1*äêîݽüøï5+¶Ô2A2*IíµÖYÝAó³ÎîO**ÁÅ>Þ2@ÂR¯*¾J
+NBJ@8ë-<º2¯àÂ3翸Á,µ0:L¾<LÂB.¾:LºBâJ2<º44ÚZKà÷½@VöUûø34I³*àÕ9Þ;²-¶9ܹ-*49ܺø
+ôï5ãX*:-1Ä+ÚB´*¶Ä7*Äè/¯1P*¼+*ÅVK÷ïÁ5üì½½èÕ±ÖØEYÁ-/üáAY»W;¾ãÙÕÍÝî×÷Ø4¾·¾;Ù*ÙB
+*3BZÆë2ëËJ6*JÙøõøéõS»Ë÷éåÍÝRÔB¾S¾¿¾-829*+Qáàé*Ü3ôJ*¾AÄWñçѵYI¾1ÐôÆ*èØÉ×ãݽ³´
+M*A8¾ÒäÝøSûD*6**ýè*ÞÝ-0¶+*ßXüYÞ¿.5@NB:óÙQí;*.Õ/ÖÓ¯18F5*KÆZ2¾6FB0J.06ºBÍ2Ë23¾
+·áE.öíÉøDZö,*Jß=2¶ó*üZàÃ5ATº¶ZÒ÷ß*.ÔC¿-5LFHJ5A°º**ºïLÃ3;DVBÌ»°Þ<PÂöîÆÀ@VæZMø
+Ãá/ÜÒÕϽýú7O5½üä+L+8:*öE*Jý*46W´,ûëÉA**î7+FL¾**ÏÅYJK3ß.¾5.æíܹñÖCñÞZÏÈãVG½,-
+×åÔÓݼ2ïPµB¶ïS»W,+B³E6=/1HöدØ5RU.B-ÎøðëéõYÞ3EIæíÛ·ðâÛÑåé·¹äU?SÏT;°ö,0²Å*æ½.
+ý8I4ZZàÙøؾî×î=2¶à+2ZÞ¿.5ÄÕ+»Å¼´Äôà,¾Ü;*ÍQíH¼üú+.æñÃ5ÅìÚµëä÷ºóØ7ÉÃÈ>RÖ3Õ¸,3Ì
+á×¾UÍÅVʶü³*J*¾Â:C1ÕÆîÉñçÊʺZ1F:**N¿+ëï³:Z:ö6J2Þ//Ò0>,ö¿/7HºÖ:Þ¼¾í³*¾»Õ@Þùç=
+AYJÀ-ù¯·ëÜÃùôVÕ*´õ>SÓ·.7DV2+̴ܲàÆ´3RZ,Y±êï,:;KFãë-×ð*Ví³Y-1=+*ÅBïLÄ7EXºÖ2Õ*
+:6Zôó,-XF¾1úµíG»1GÀò:?ø5Õ6D·êFBVÒN»Ð.æ=QA;MÂ5ÍUäÆ>TÊB*:ÁÕ·ñ*>ZåìØÍ,<Âëæ*õ@¿>
+µ4¼¯õ7S¯Þ¾èõèµÞü¾0,*úÞ2+:ZÕÂ+·á¼¯7ô³+ÕºîP8P>ð0*1*J8ôO6²æÅ×±½ÛÇ-+7à-ÆÛ/Û8ÏNåº
+Ë+6B:Þ*¾K¾=-222+2.î,3@²æ:K½ùÊÜ»ùò?*.-./ÚÞ¾ç¾3*YAë×·/MÜâç³ÌÂR9Û»úÞ÷¯ØTÇ,³Ç:HH
+3F¾4IåÜçÎ>NB**0äî³H>*Lê×Mè-ÚB,=D*Ì8+¾-¿<@ÑO*ÓÈ1*ù-D2*î2*¾¼ËÛQ*H7¹>F×Õ/Úð¿E·R
+=ÆúéVUô½QŹRôÅ*æM¼·º*.GȶL5Gñ³=±C.¾3FÆÎß=YE¯¹:³±+=+?,+*ü8DXäêONÇÑMWèðLÁ7C,òð
+ÈëTóS=N¾F9GÏò÷GÛ=à:.RB*:¿ÕC**>ÊH5-*ÀÉ.**A.JA*:XF*0á-á²*ÞîDZåH/*DÉ×AÕ6>ÇÃ+GùØ
+¾UH¶=1÷-JÚ+ÓPÅèò+O**ú¿1FÈÅä÷Ø=**,Y½Ë?²öîßöز*Þó˱07ظ2Q¸ËNÃÔʶ¯ãÅñ³X4<MÂüîϱ
+ÅëáÇ°à2Â*E¯æZ0ÝäH+ÚáÌ»-UADîäé*÷=Y¹J-K*8E<*NÖ±½ã¾+ÐÅGù-5¶â-µðZ/YMU*¾F»J5*ê¼Ñ3
+ÅRЯÜä2<N¶Z.ÞK*Â163¶Á2=TÊæZÞ½ÞY@*îúæã-ÆÌÚ¿9GãÉ@M@6æ¯MýQXCWË67,ÂûîÇ-Qм45*Jà*
+¾6Üä×1=î?¹V5êÈðU9ÓÉÔ,:ñO*ÓÄÍN´ô1²:*JüÄHðÔU+B¾ËGù-5¶â-ÅÜ´0:ùÕ·/*0-*:FùQ0¾ØðËÍ
+Rà7W²ÊÕ+ÂÇ*¾QÒÆÏáÄ7?@>-/8*Ò,Fξ5.R6îýêë¹ö:H»1ÛáÅ9?ðÑAÏLë1ìÖVÍ:ÃÕïG·ìîÇÕÏá=*Æ
+à麷@?T-ÅÓK+2ðRÛúõS4BÒðËG¹ñÅ+*ô1²:*Þ±:ÆÏ<¹ôçS¾Vµ@îÆ1ìâK7JôYñø<.ز*>ôÚË>0-LFÏ
+ÂEØæBY¹0ZAAÞ1<º2ïKÁ-ýS¼ùôïÙÅí*ÞFJN*êE:2>B*îöØ×=ÆÏK¹öFÞÁ7=à¹õZKÅÅWBVÍ87,âùê¿á
+P7².9ÀÁAöõúé<9+ðºßíXöÑ0÷د°BB2îäÌßÞ3¾++RBÖÃ8<úÕXô8+Èè8ºËÙ±0XHÀ4ù·ñí0èÂ3îG¿ùº
+Ú±ÛPKDN2:åM1ÌD²¶÷U¶0¯QYMéùÚçóX¹ý³+±Zî·*Ê×·Á1=Lº¶îݼÅßôR,ºÛZçâçM2ÏGÅ@ÎäÈ1ùëEå
+Ó/Á³ÈÐLÃ,·G·ìÒ¯=ÉܾÁò0¯àÂ3õñ³GY2åÝ06H+*¶öÆ+Þß*¾Z*ͶïLÄ5?D>Æ,,.B.Î,îÞ+̳¾Ýá³Ê
+D·÷0ôñæâCºR:Ý·êFCXÓP»Øâæ9ÍñÄ7D¼Âï=R.<6ßP5E¼úÐÜP<T6úG,¶ð*Å6ÞE¸òöÏMÄ25<À.18+.¸
+A-*À-*Ñ*ñ+Ì:HVÛÄS-õÌåñPê×´îÜSñâºßDO4>N¾ÞÌî³¹ñæC¾±/Ö:LÄ:Q¼SY¹0Z=Z/¯?,Âîß¿·èÎ
+Þ½üøó¹J*ï*Q4*ú/*6,æ¼ñRÛà?VRËYé?È>VÚ¼óðÖRÇ4³ÇÎH¹BÌE´,»¸PY:¿ôéAÎKÃ6ÝÒÕGÎ×ñ/î/9
+PÒR¯àÁ,ëݽüú/+F2DöAüÓ>:LÎ:ÞåÇLãÈ@YàÀ;VÎD/*öSÔRÇ,ãçÏI¾QöDäÇÂ4Ù»³/@îKÃ4ýÓÕ¿Àµ4
+îJ,XºËRÒÖ¼»Ö0ü7*6-.ÒË+ÚK*DAÈÒ2EÅÓÈ=PÉ@WÐÂGÇ</È=OÁÄé:7HÍ´ÇUÐÁý0LÒRïÉøDÁB;àÂ1*
+Î8*:Ô1.,Â,¶Ä8E¸êB;KÀ²ÎÞ¿Q*îR,ÌF¾áM/T>SÑRÇðË<OÈ.Ç<QÏFÒPÇ4ó¯áP7O±äÍSâòÃAZ¿-5Lâ
+ÖÏáAó³¯æS2¾µ.÷;âÅ57@F**.9:Þ¿*Ò0>/¶À/ù÷àòçÒ³ì<ÞQt8G¸>ǵøÂS<L»èÆï²êÎC=2A´ý4èïß
+Ã8I¼úÐÜ04HöÖÁ.R=î0;XÚ¶ÏLÁ+ëݽ*+Ì+Â,Ò+Â/6,FR*1ÖíØOÖDøÃì×Ðà>SÏLÆ@COÅ.ë3XñµøâKá
+R×·û44îJÁ3?TúÐÜä6D²Öî/+F2¾È¾;E·BïºË>6¶ÐôNéÈê84¾éUÅ?òÙQíË4ÖäÂ6CDòQ8Dß³Êå;L¸LW
+ÑõJ2îJÂ4AXúÓÜôÈ*àç8FJ6*KÒƯûøëÎGÙ*ý1ÎãâÛ¯¸èâ=;T²ÆÎJ¼ööF´æί=ÅÍL4ÈÓSáæ÷UL:+58
+ú»SY5È<5?ÄýH3¾Ï*ζ,Þ?°ò2<NÆ8?8àÛ3¾-6¶Á*Ì:JÄ»Gåì3ÉÕSñä7F¶òîK´äÊ3Wñ»0O,³é×´÷ÞÞ
+5X¾4I¼.3ðÙøDÛ¶;áÃ6CÄPIB¾J¾3*íö¯âÆ;IXºöZÞí¯¾µòD·øáI-<=¿â6*öD¶èί-åÌJüRÑSÙÊ·±Q
+îÇ<ÆÃ:-<ÜÒÕ¿Á-**¯?J;*JV+B;î3A°êÖïLÁ,ÇJ¿+M*ÂÞ¾-*Ó*Q@Þ?°ËúFóÔ8-à¾úÄ3°=*ÞíÞÇM1Í
+IÃ@¯RÔºGñÔÌÝ-W2¸:1E0NºSY=2;Hö×,¾50B=BYÑ7E´ÒæîÞ*ÙE±¹¾*Ñ*X:JÅ´ÇÕPù<:F5îúȯøÁ1Í
+H½FÎKÅ@»T³éBÑôëÛ½1ê:.J0H6Å*7½ÓÕG?¾TE¸2ÏàR¯Þöäéì@¾8úÊú°É*YZ¾ù-1V>OÆ>¯ðÍNÓØOVD
+µñJÜMXM,1LúÔÜ´@³0*FH±¾T*üBÐNÇ;IXºÕ¶µ0J7HÊêHXÉÅVì94Â1ÌD²æп³¯³ÔíÝ¿0Y6»ïÛ²>C*î
+Q»ë>DZ6.R0B3¶¯*¾¯ºËÃ.çÏàRA?2:Þ¾+70.F<*7Ç÷»×/3Ü=+UR:J½+/²:KÁO/Å2JDÈ6ÇöÐÜ0<ä**
+8ïLÅ7G¸êBÏEN¶îIÃ?±äÒ>Q0â1îÞ=*Z+OÝÝø0-°À*LBÏá*¾Aó³J¯êV6ÞBJ3ÞVJ¯*XòæïáÅï³¼îJͺ
+99äÙÔÈGØ=ã½üÀÍM*ÎßÇ÷åüE4-.9:Þ/-Ê,Ò/Fƾ5*Î/?¼òöÏáIó³Q*ZÔ0>0+Fß*ßö¯**Z6Ýô+ãäüë
+EôæÇG¯Ý´ÅüúõÍÝ:CMC¹ûÀ+²Q,îÈ,ä*äìT+ä+êJÀ0AÌNºSYEÉL9IÀºÞ*å4JLÐ6*î<GXÒ2¯ììã»øòå
+ÇýÄë÷¯1ºÓçÎ,1LÒSòÉ»M¹µÄõÐ+ìÖ*²VÇöÐÜäF¸êÖ¯6JCJOJ**¶6*î7KÌF·ÐNÆ6»LÂ8ý,żü»Í½Üú
+õêÇñHë=-ü¸?Sô-LÂ0í;ÂBG*ÆÂ6Q<ÞâAó³RïLÃ4AÌ*FK¾-*2@ô:KÀúÆ;LÁ/ÛNàëIÝ-Dۼݽ½½ûôá
+QõëSµPº´VÎ*-¸¹ù4@NÖ:KÀÑ*-*1:ZHîÂ-T*æßÂ7Mí+âAó³A¼ôÖ+äéFâÄ25@>éD»AùNÇPôøÄííýùõ
+ÝI¼÷êÁùéË1Á/¹³P¾ÎYÆ7?VæîÃÊOÞ*òîàÅ=3Yèé*á××L¾U-B9*8Q×àéºÝÜáíA?,**ý¾Ä=À½ñùýYüý
+9°ñìÇÙ8»C¯×MËܽûøÃÇ3Á0Á*8ÖW>Þ++Fæ*±Ö:LÄï³@°NýýIÒÕ¿Æ1+*NÜ¿*IçÐOÉ?O¸âÆùãOü¼O8*
+0*òÃCÑò·äIàäýùÕ½HºðÖQùâ³ß87ÆîK¾¯@*J8OÔ6Ç*@ÝÒÕ¿Å/*Þ¯.Þ+,Ê,Ò/öÆ=QÐF3ÐàÂD:Yò÷5
+ØRYñ**2:;åÑÆÚQêÀêF»öäµÑÔVAX³TZÞ½+=8**ÂÓ*±**ïÃ/¶Â3CÀ>Ç<ãÇZðÉøØFX6H8´¹Ä27DúUÛ¼
+µÞßÃÕË+J8ºRÛNûÀêºûöå³ÑÔʵWõÇíI¿07H²Æ8HºB4:*:8Z4:,J>¸VYýÐÜäD´êÆï6J:J6J.Þ:Þ>¾½
+6SÐâÆ7=HÒ2+ÁíIÉ¿UYñ**6î+1AÇêUêJý;EåÛøðÜN+ô=¶Ú´äÔ8Î::0¾>¼>C:ïW»ë3+ÊÙ7+ÊÛÏÄ7K8
+±÷Ä39T6º¼½O:ÞÛÕ/+æ-FüÖðØûÇ°¹öã·¹ì¹ÕWÌNîÝ¿O³å2*@ÄùÃ*ºSY±=SÐFC**R@-FC¾ï*µ3=PÊA
+Q¸ò2QLXIÜÎÑXE-Î/È.àÔóø5°-ôÁêF³ã½¿HÒÆÏMæÖ,:*Î2JNÜ6Æ:B±¼SY-99**¾6+¶<+ã+:°3̽ÚR
+ÏNãô»í·ü³8**+8ÆòSP@êY°ñûóÙõHFõéÐ7ðÎ3?0*¾ÂÓ*-+úïâÈ?SÐV÷O»7¶V407-+8ÇöR¾¾6CÊëM°
+Ê2¯OäôG-D3ºW2*1Þ¾NζÇéA°¹üõÝUõܺUÂ-*:ïÃ+BÂ2EÈVZýÒÜ@¿*öÅ<QÐ>3<á¸îâåöû´ÇáéP×ó
+Á>Äø+´IàÄõÁÝÌG¶Ó39F7JF12-¶î³ZðåøDööòéºâÇ>SÌ6×ÏLãåÔ÷úPÇNñ³K*̾.<ÍâüýIݴͼ»µÒ3
+9,*·FÇðRàÉøDR÷0*¾ã5-¶J*HñPËE±ÜFCÐ8.ÚÛüÆE°øØ:*ýîíýýô×Må8µIû2ðá,**×:ÞMÔÊSÑäËD±
+¼SY-;-**â*¾·¾Ï¾Þ¾K*9×ÐOÉ@UÈòÆC<66ü¼»<<ùDå¯ðýýõÙYåìPù¶¯**SÒRïB¾WÚƯâ-5úÐÜôÞÀ
+í@¾ÅFÇ<OÈ=K´â410PÝÝC/Ç»ËUÇøýýøèÕYAʵ0Ó3Ã3É*P*FMÆ>UÐN×<ãµó³,<â:**C8Î6Z.JIÄN×°
+OÈñ³øZ¯úýW7ÆêéFÍßýýÙåÜû¸Ø6ñß-R¹Î*°¯áø³KÇðÍøØ:ØÙ,Z9IÈFCÞäQ¼â¶;áàôï-OÇ·Ë-ÞNùýý
+ñϵµéï@Nö*è8>¾ZúÖÜPTÔúÐÜ0XP**HñPÌF·è²·ÐÈò2C¼õðùDåOüýýóÓ±ÍììÀ6Sð/*¾Æ;+²°ãÊ+3
+K*YèéJâ*¾ÇÓ51¶<*¼ÐãÉAWÐ.×Ï°6C÷ûë»°ùDQäýýýðϱ½Xä°º-*ºïLÄ6EÀN·úQVØúÔÜPLÀºñ²1<Þ
+KÀF×ÐOÉ@Q¸Å67Ì.7½ðD×»ËAùýI½÷èÛAAYAÄÑÃ*ÊÔ/-ÂÕ/N:É@UüUYU.¶ÎéA4*RÔÕ÷Å6ùãË8IW@Ç·
+Ë9Æôý½ûëͱAÜÓHøóC*ïBð¿ÕûSÝ0<-æÄ*8Fë3×+ÇïéîðúØøñ5ü¼ÐN<ùD×îXºËÀÝìûôÏ39¼7Æ6ö-A½
+ÓÕ¿Ê3*¾íêçñQÍEWÐÒ//ÅÝÍÏÄâÜ䳺úý½üôèÙEëRMк0*æÐäÌI½0O*ͽèÕïâÇ<QtV/23R4B72ÜôBY
+ÐúÖ¯JÝôçý¼=<ùDÕ,óýýôÕUíì´EãÖïC*ÞÅ=YÜÂC**XüSYEI¹:*-µÃµ;*ÌðãÉAWÌúÖÏD>úüý´6<ùDÕ
+MöýýóÓYÝÌRõ²öLè¾á-YEÆ*BM*ÜÒÕ?Ï7WÕ+>/Ê+¾ÈÏà˴Vêܽ¯1<ùDS²¸ºËXÝÌ»ë¼.ÁèàèÈ*Æá
+Ä:5<¼ÓYI+ع1:>S8±·É<I¸Òæöû÷¾S°XM*H79YÑæÏUQEÏá5*R³±7MØÂ×*îQûDêV**DÍC*æ60îH½üú
+×ÑäÉ?¯PåöÉAÈëéF@÷ýýÑÝÌG¸6éÏOÉ?Y*ÞJ*ãò3ò*¾úú÷O»7Î6*2ÍP>SÌV0¶Î*Æ<OÉB±ÔYáÊÎïáü=
+ÙÕ·XöýõIIÛøï>ÔáÅQ¾Û:*OàÊC=äÖ.¾÷W»ëë+ÆÇ?áéºPÊ?M¼ºøø»¯êP´Yñßüýí½ü¸ôÆKäZDö¶Ð0H0
+*.-¾Çï2=**ñ5øD%%
+d
+432 139[1 0 0 1 0 0]sl 8 mask 0 139 di
+/mask 7506 string uc
+*Þù1¾õ½¼íýúÜý÷»ýñùüåõûÍíùIÝõ½¼íýúÜý÷»ýñùüåõûÍíùIÝL*%
+d
+/sl 60048 string uc
+äÕí´ê9+*Ð,¾°OÈA**TÔ*F**µ3ñOÊC±à²ÇI4Ô8½V@*ð,*Í:ìýý÷ÛMÍÌúÀúB**F8,.+Â-ÞÇB³äÊC.¾
+°øýýÈG¼6/Ö´¼ûظø³Îð³*.Õ×ÊB¯è6*Î/÷°ÍͲÇâÜPXJ6YñêÍYõ5<NÆ9å*ÞáS¾:*KCñBL,Y=îáI-*
+*ýߺ7î6**E1+Ì7¾õú3òQÍI¿,ãJEôó¼ýâ8Ç»Ë-¿÷ýýùèÍUQ¼Qܺ1*àðäSÂ;¾éYMÖùù½.*ÀIô9òéÞð
+6ÞúØåÊSñäÄÀáíAùNNô³<Ãç÷DÐÌ»íZóÐó¸¾J¾-Ý8ß¼û³Ôö¼ýÃÍ*¾÷õݶ÷1ñ³ä°OÉ@9:ë¿+>-ÎÊD±8
+*ZË:-ÑõI7ÀâÜäáÒûý½»óçÓÉÚ6áèX3¾ÃVCñäæDÖ÷üéZWüü±ó¸+*äÝýØÒýÉÕ+Éå*:ÐÔ6*XHæDÔC±Nß
+óùÝÈÊéF¾GÅýýµIÛ¸×ü´ºëöø3Õ3Ç?1éÄêX½79ãíÝÖ*+¾1*æ*+úùIÈ÷Í÷D¸÷ðã5ZE2ZAWÜÂC¾O²ì6*
+¾?/Ñ1íS´Yñ¿Nýý½ü·óèçXÆ;Í+¾ÈN÷ðPÌF1<ÞäéYÁ:÷ùý@ÞRë:¾ò1÷âé2ô,3M+*¾2¾N+*JY03ìóJë
+᯾âK<õ¯:WÞâ÷³Ò=å̵*î¾,FåÌF»ü.ø=R9D>æÎE5Ñí72ãô³üSøýüçQåìFÙ³3=PÊA¯0*Ô-æÊE»07T
+ò2**»û³:ZºëVWËËî9öâé2K3Z<÷D¾HéAÛо°ÁÂÆθîÂA°ûØN²¯½Y5ËXÞH,RÝHèÄB±èÊC±*Ùè·öºá
+¶°ùDÅ/ýýüïÍUÕ6¶Üñ³5S°OÊáé=êÕÝPèë9YUZÁüûùÝÝìÇÕC?O3BXYñÚMWÖZTõ>¸?¹QÊÖ5ìÑ,Âë¯2Ë
+Q+:¯M,¶H.áîKæÃû¿;¾ڶ¿KÀ<*Ò0ß+ôêÍZ¿ÖáZX**Ú2ÃK,éÊ0¿/Aö*×-RÖ+Ôç¿ïÃ=<¶*3N°@ô1.ß
+-ëâÌ»Ð×ê+BÀ8À*¼K.ôÃ,/ôÈ@Ê´P4RFÎ×5<B̶Â,1Ñð¸Q°üØ;ØàØ-2B02Y-BáÄûPÌ=³L09ù@ÐÏß¾Ê
+Úä12ùýý¼öèÛ5âB9¼7̲CñPÌ+*3ºËN¹I¼ûâéÂü²AÜ0û4ßüÆÙøD1ÓÊõëÝN/ERÏ=SKÛJ½³IÊì·L+ËUW
+<<¾;Ä0Î:²-ê96K²ßÃ56<>+Æ-ÎÁ,ÌZ¿Î¹ü*0X²K*¾/LÕFùEºJóPA¶Þ0Àì÷EÍBYJÏÇò¼ï4´°<ôÕ1<V
+Ôë2ëÚ:,ùúFð.O5NÈÙð1Ü*ÖÝ:¿ïÂM>¶E?ñ,å¸<õÕÛ@*¾C.:¼ë;Ä?ûØúø¶å×FI@.ÆäéF,³ýýéí8»¹W
+êÐâÆ<O8VÍ,JÇ;QÜÊ·Ú´ÂûøéNAùïéá³-´L¼Õ5YÑãò¯14Áø÷DÕîE¹ôÙAâÚNá0ÁѵÎÝA>0:.µÆ*çYÖÅ
+BÞüÞ2À-XÚÞËÕ;K×ZIáÀ-B¶Ï/+*,KöJ¾=º*éYÊK2ÞËQÊA2JIÍ2,øìÎùJ¼;,´G¹ï/82¿SåTYØ-°:7º
+ÕF2ôêÍÆÞ¶à74FÌÈï¾±GÇöéJä,*¾÷¾+8?J¾¾éÒ·=MØÎ99õõóñÅí¶CDEJÀNâäEÒùýIüôèÛ¹à>QÐF·ð
+NÈÁ*M*äÐPÌH1*RåÌGY½ËÇTíýÙØØK*¿Xè¿Õ3ñ;0>*é÷DÖ:Àá¹Y*ËÀDÓõîÇ8éYêÅZZùX¿ÞÀDÀî¾5AR
+D*éÁ0æ÷¿Z̯ß+6F2Ã×*Çι:Þ7HFøØEÍBY:߶Û÷0ÉN°5è/.ÐÙÁ-³ZËä3¼û¾6R+ïê1F.¹ôÅNJ*Z½Á,
+ÅÑ°XîVQ<üDï×±¶èÆT?Þ3..,.*¶Í;íÀìêE÷ðH¼üûûýµã/0îÆL×ãÃÁﳶ»¸ô¿W8ò3ÊBM+è*ÊäÍKÃ@*
+ÆÎKY½ËG,íIÞïØ+EðÍùÏ-Y=Jå.,ÆVÑäéF>±Ý»=-ÃÛ0ÒÏúë2ÑÌê1ØØñ·¸ëÅ4.JGB,S¾ùÃ5Â*Î,Òç/7
+:éÍ=FáÀÀ+ÞûL>,*V;¶ä¹IßÁ5ô,Ú1,X.ß¿Õ;ðQTH;.Ø×MÏø1TÜËÒ-³Î1,Aå5¾õ¿,U-;µWó/ÅNJ*Z½
+Á,µÑð´Q<üD¾÷0*ÞãM4¶Î*üðOƶEµ8ÌÚ9IüÚøøøóõÕçÍ8UÑ-¿ñYñJZüýíܸóé·íÅ<OÌFSð3*ÎÇ?³8
+ùßÌG·ðâ÷øéÖÍðçÓÍÝ8»âCTQÜEµI1ÜP4×6.J¹;ú8YñÙEOÖ:4µôÐî<ôæEκ°IæEÓ//¸FTÎ5M:Ø@Ú¶¿
+2*¸ÓßßÎ+¸+KÛ±VB+:Ø@ÚæÀ**ù60+5QöîÞ<¾ÔTóM<B>JÀK6¼K2Nº,Î=,ÄL,PØ92¿8ú*àAJ¹04Ví98
+:ÔAF<++¾û74FËÈáG×¾ÕÃ4Ý3¾;ð,Eõ2´YâÝÅý8ìÇEµHGùùø÷±ýîÏHLáL¿ÙY=VÚÄÕïòçÓñëö¯â-¯E,
+Z=QÔÚ×Ê̸ðê÷úéÚËêÚ·ýPüº·X8G¹ôæÏÙ·î³EJ¾JÝâ,:;<âÑ-òøÄñÏ*ÎúDÂ<÷BÚýDÆ÷JåJä4Ù-R¹-
+,H°OÆVGñ8XÚ9Q8»¸óêÓ¹õöõñýIÜàQW.Ä+1Ý0ñFüí½ûö³-ºÏKÅá³K³O*à<å-Ý8ß¼û³Ðíɱ**Õôï³C
+¹ß+Ùâ40<XØÃUW8YñE³ìÚSÑPËCóäËF·ðÚC*:B:.îà+úPÅ5QäÛ6DµIìF¸òæËMÝìÙÉÍÍIýýÏIÐM±XFù
+Dµ>ú½ýòÍUÕ8OUÃç°ãU*îB¹üú3òåÍIÇQÍIY½Ë¯QËòàM*:NSí¼ÂDÈF̽WRT¶ÈùêçñQÎKÁ,?I»Ì*JÍF
+³ìâCRÎ.ÞUXâö°íäÓÅùòäÃ5ÍÌû¸ôHüúûúûõýëÎ/,ñ¿¾=YñÎæýí½Høóç7²ÓF¹0ò/×A+BÊFÁ8W*JLÃ4
+»ü³ÒF³äÅ*JRAýYÜ0°8O-Ê+*@ñäÌGUÖÛäÊAKTêÖ=SÑ÷°äÊ1òãEDòƱ¯æÑ·ùîâÓEÝÌûùõÌüüûûûíõ6
+8KàÐà¿Õ÷Uýýé1YG¹ñÄØUÊDá9FPÉ?SÐFÇ<OK÷ðËø+ÍåÝôÓÂÞ÷Õ+Ëá²+´<ÑäÖ?å×ÑåÌCUðêç=蹶µð
+ÕN»ÐÈ@±Ä@1Ø·ËCEк÷õ¯íH»¸ñèÕÁ9YÜÅYY½üûüëù³*3»ïîà麹óëÓÙüøÐPáÇÑQÌ=´K´ÂðãÌßóáõÍ
+èéîäÌH»ôFÔX1îG·Ü.×ïUàAûÚÅÃQµÔV@SÊçðNÉDY*¾Iºæ¯XìáËAõäÓUåH¼ùöíG»ûöùùQ9öEQÂ.ÀXý
+ûáÝìѵí8ØG²?OÈÇF5>Äê*äæØ2¾µó³=T8>RÎI¿074Òýâ÷ÐãÃ,±ÜóÈIEÜ7ûYݼۯë¿Õܲ·=OÇÇñ/*ö
+¾ÌÅùHüº¸ñÌG¹õëßÉ99ÙµIY½ýýëÉ0ñCûõÅíû¸õíûñ·ÛúOêB±æ+*ûÇÑPËF½@/.W*ÎYèéàèºRÏMÃ4?È
+>S1T=PÅ6Mì-ç½èã6C´éÎOùÞ¾íHH˵éÒÖ;ãÊ@¹@>¹Æëé=Íû¹¶ñPÛùõêáÖGXèÌËQÅýü½õÍý¼üôÕYõì
+ÛEü´SBÁ>³ÈÏN-*æRÏK1?FSÐMÇ8GTòæAó³ÏäÒÑQÍM*ü<ãÃ3KüéBÏÙʹCíܼûÈôÓ±Õ@Ú¶XÛÁûSÂ0?¸
+FÇ°NÅSÑâà÷ýáIìOµ0ËE¸õìßùõïQí0V¸îîìÙýúíëíYYÛ±õÌ»í°ÇÆON@Uôº:ô=**²ò»SíÍøØÎDõ×=å
+ÌFQ´Êæ7ÐÁR¯áɱ=¹Üº;ùOêAïä¼·ôèÉEÅìæ˽=öÐLÄ-OÐò0ÛýýýI»ðÏûZÜÄKùÜü»»µIü¶îÍSÍäíÝ-
+YýÜúòêÙ¹óµÍ+5RñåÉJñ*ë+ÕÓÙ70úSY=H½ÌÕ·ÌR5ðéÕËØÀXÏâÒÈ9-ÄêÕ9HêÕðÝÄ?=ôÏO-1ú±ß½ðöG
+¿,=ô긼ºËýý9I÷å¶Ý¸ó¾M91Iý¼ûñë·ñ÷ÙÅÕ+öïéìÚíÆBÑH7È<ÚVYUÙX¯E1öÐÜä¿07DòåÍEÅøáHÙ×
+=Å9K·ìZTÙ³/õ/úÖ-¼VB¯ä¿;ýó¿CÉü7·ìÏ7Gܺ9÷ää¼Û¼ÍýIüóòñ-QêÉÔNCòìæàß¹ûøñÕÅ8F»ùü»¸
+õê¿9ÜD?DGȱâʯ4HòæÏLÁ0?TÒ3GTÒ毾Aó³ßTX²RÎ<µÜ»ºÕMO·ÎH¶üT4ó·-ÑóÉõïâÂÛ°ßÇ91ìF¶°
+õVD·êÑçDóéã´Ô6·÷îöÃùêëõ¹YìCVÂììF¾?Ý°@¼ÜéÝÜúûùíѽQì·ñ7DñIYÔò·òæÏN3çÐN9>Þ+*ºSY
+ñI½üòçñP¿ýÚâйEµü¹ÓM92ñVáË;58ê3õGʵ¯æÁIùâÆI=07·±î8ù¶ïÌCÙóV×QôÛC·÷æÑÕõ÷ÙMXTZD
+±íÚÞ˸WùXHI¼?ÝÛúõÙïÄÔMÌF½,/D.¾¾0/øJ+8*ÞÕøDý÷±åÄAOD6絸OVD±Ý¾ýø׳ýLüº÷ðßܵïßÌ
+U¹HËÖµäÚöóâϳ¹ôÎË@ûúBñá½G²ÞV/éôìܽÍÝØèÂùîïMæSTGº¹ù÷ðéEöØãð6U1K-*äçP+ÌB¾*7øUû
+¸¶ÖùòC=NÉ:AÜòJç@8Ëر޳»VÚ¿EÅXë×°µF¶²åÉOE´ëWµH7ÖóÞ?U¸ÐX-ÉôùúN·XÎNÖÁ¹ÑõýÜÅÔÈ;à
+Ã7MàòNñDܺسàO/äÍѳA0:Xïà+¼ÓÕKÎKÃ47D0ÞÃ4/¸=OÉ5QøËNåDPËø°ß8õVÜÉYMäCÒÁÓù·ñãÉ/A
+ôL¹<ô¹÷ïÊýºãÒNAAóRîIÍÅYáâü÷õýì51ãB<äÊ=I¸Ä8O̺çñåÎRòæÏNÉÄ*.*¾ÏK+1*:8îO»74GÈòR
+Ï¿V=*ÌÒQÇAY¸.CÑIÓECóéÍ/íó¾¿àùAÎOÓYÛèÄDÁDºÖ<O9¸Eí¼8÷7Væ·4GD²RÀ;Cùüö÷ýÅ9¹UÄHîI
+»ù0üéQÍJêCD»ÞPÇ@WØÒSÑ¿´ï*A.îO»Ëû.4òå**ï÷±J¾-û3²RË:SÈò6¯8óصñéÖû³Ú;/à5¶:LÕöìI
+Å=GÜñåU41ûíß¾81øÅ0U<óÙ³ñøÜÝ»ûúïéÅHÖù>¶ÊA¹ðî¯>A·0*ÖÌG»ø6Å2Z1KM+,î82ÞÉøD,4²æ¾Ø
+U,îI¿è6×ðNÇBûR׿QñHGBTISîIÃý0@êå?8²¸÷òé¸ÑøÂ55ø.×ÑVÐ8>RÚËÃÍͽÜéÝIݸæU5HÁÌÐÓâ×
+ñ¾ëÇÖ7*ÂØ7ÚÃ-úSÝ85¼ZU;³/*ïµì=äÇ=KÌÒ÷ÒéãFÙ´ç¾Ïàò09XNAZQÛZ»àÎØáÉ0Ú=T16CöÞ¸µØòæ
+»èW7ûûù÷ëùõÝ÷HFÄÍQÎð±åÞØ2*î×à@VßÂ8G¸ò¿×»SY5Í4Ñ,N5*RSÐNÍR¾=/4ÑãÊAWøòWýÀÔ»ù²ÛM
+»WÉý2¸ºöXIÅùHºðÔ/ÝÜê¹ÕÌÌØZÉ*ó÷ÀVåÕY8¹ñÛ8?RËHÅ@O¸²Vø¿ü7XÅOÖÐOÉDG,*PÝÒÕ¿Ð-´J²Ë
+7>7*BT¹L´ÌòäÈ:MàúçÓ-0GùòíÇÑÀò*ù+ÊB´öéÆ»öäÔ?íÜ6øWIÙÑKµ÷Ô<íË;Ý?ØñãÍP1*Jç4Ù=YUÔ
+BÓ2¯ùÐÜ0ÀHö?*¸*ZÀÓ.:FZÜãÁ4Ë×ÐãÆ:UøÍ°-YÄÛE²Ü¼ÒMѾ÷H¼ÛÚIX7×ðÒH÷ïñ.GLWÊø°Þ·ëçÄB
+»Üæ×ß-÷ç±.J+**G+RÉ*KIÓÕ7Y9ØG¶ÓDÙ¶Ôà¾×*åç°ãÉ@µPÃé5Íì˹ñíâ-ùÙűýÃê>QÄìíؼW=ÝPÛ
+Bä7U²âÇK²Ï»ï³M´;3óѳQê0¾¼SY5AECXÓHãH7*Ká×8»¹Ò¾Ý²C<ãÉIëDôϳ5AüØñ踻ç·óî³ÚD¶³
+IÛÖXÖM»Ü6S´ø6**Jáé*ç;¾<Á-¿R±UêüÐÜ´ÆãÉ@³èÒçÑPËÐÐOÉÌ/ÆÐMáí,Èá,P*0,FSÊ@¯TFS±R9É
+µ±êÝËñÜé/0NøB³ä½íðÖJ¹0ûS>çCD=ÈÒçÑQ*JÊ@7<¶.*ѼYèéäÎ+ç**RØæ@4*Æ0Õ¿Î1æÛÜ6Â0/>··
+É?¯TN÷<T×ÌAðéàEñÃúÖõG¹ÓæÉ6³8G¹BÕ8½2ñUX5Å:NÅ0L>+ÂPÂHºSYÑI¿üú3>æEJU8@J¿Ì4.5.6F
+À¾Æ*Q×ðãÅA±@óèû´0VôTËEYDÌA¯ü6¹2¸:KÃÄÙG5¾RÂÆ-æC*:6:.îO»ëBÊûúC²æM°Eé,KÅåÈçL,X¾
+7.G1îLYìâ×ÏâÈGóTÉ>QÜ·ÊδZÜô>êàâGæÍH¹øò÷Ñå*¾Aó³AàóJ+82E:7+2°UÙ¼ï¯ó³Q¼ÞYäÒ×îâ
+ÊA¹05.åËñGR¾ç¿5V=OÉÄ+2ÐNáÛU×áêÜÒÕ7XÛC¯Sè²+*JG*JQËD»,-вçÇ*WØÒçÑQÏ09æÑRÍìâç²Ê
+JÂJÂ+F»L/ß.î²ç+NW.2*BÄ:OÇ0¼5ÏMÝÒÕ¿ÏßàLòæ2¾**2D:ZÄ6ÈÔØÏÑPÉÌÀ?3><*ÐáäK+IJçÑQÅæ
+P1JçZ¾Ï¾Ö¾JÂ×Á3Ú÷¿K¾¸,JDôW¾0ûÐÜ@BóÄ**æ+¾¶*:D.ÎòÓ4´DZ2î*0>RÎIÁ0GD²:¶Z¼ô,*4-äã
+äÐYöÞ*JÒ*2WÔöÜÜôÄëàÛXVÊ>WDâ³¾Û@ô¾1Jæ2ô5Ü0N13,,Jçè¾TÀæÁö¿-9:NÇܾH2øYûø.ø¿TGçÓ
+KÕ¾ðBATè²ç<×-ö¸¾*1H´*üàÔéê0ô¾æÆ-ù3²RÐÃ01Jî,@ÜÒÕ¿Ð-*Þ².ÞK*ÊÆ÷ÐPÍHÃ4³TÒ:ó**KÃø
+òË;2ÑQ9ÂJ7*J»+*î²*JÒ¹æ7FW¯RZB°R-*PÝÒÕ¿Ïõ*:NÚ,*ÂÜ++LȲçZÕÆÙßBR¸¼:L*ÈÄ2Â*.3*ÑÁ
+°=°Qð:èÛ´:VîÄ.ä*ÜÒÕCÏLÃ07-¶S*O,>>æÎ+*Kµ¿´À²çCØæ±;T²ÂÊ:OD2îÞ-Ä0äõ¾æ×ÀÞÀ¿ÀT¿Z¿
+MÖ:KÁüTYE,*MÉ*ÌÕëîJÆ7>»Þ䱯Ï*Îò+ֿп*+,=1:²îì²WêJËÞ¿4F<¾½ó³OUA¼ó¾+8¯UNÞJV+*>
+W±±QÍDWØN@4:î´*ÎÜîJé8NKÖJÎøõøD¶ø.*Àß8.*B»N+å¶JËHÃ4³TÓ1**ÔÄ*:ÓSÑ0+:ÒRÍÜBÚ5G*S
+1*8ÒÞ¿DÚÞÂAó³AFF--ÒU1,.KA.ÆÐOÅÖÁÝ*è:C¶¸TôÞÕMÔÉó0*à2ÌâßÓ+ºSY5ê,LÉÚ8:Þ×/>,>2FJ
+*<T²æB*D¸²çSÙ5µ=PÉÄ563>ÒËåË1Â2F¾ÂMÌöÓÜ@O+.Ô7Â+ÏMåÎòDz5ÑOÅÛ5,0**çSÀñºöÞÜ0ÆÜK¾
+É-¶²,ñ¯NòæÐ/*+´,óçÈó-+*NÅéÌÀßáø³¾D?*¾ÑA6îà+XJ¾N˸0ÞÓTÛÈ?UÔUÕDÓTÓPW*¶Ñ/ÅîîL+Ü
+ÒÕ3ÐOÁÃÍ6J:J6ÞBJ¿Þ¿-Ò,FÖ¿;¾ø*JØ*Z¼»*:6*Ê83¾ÏÏ-Ã/14ïä,0,üÒÕ3ÐM9îé6*6W5*Má+T,,
++ä,XçÞ//F2ÀLÀB*Røâ.T+XºLûß>Þ/>ºWY¹;³QNôæîXQÞÇã7*BÏMÇ@»Þ³A¿î:6ÞæQó³ç1ÞÓ·ÝÒØ>@
+îO»7TËT³èÒ1.îL*P,X²Þ34Úà+Îøð.ÊèÒRÏHOÀUŶ¾,Ï*YèéJçã**:ËQ/XFÞÉøDKز¾4:7/B2¶¯*¯
++Q2î8,è,8.ÞÃ06+¾ÐQTôP18ÖÞ+=Úö*RøS»ë/´ÛJÖ¹J¼¯Wó*ARîÄåXÊê¿ÌÛ¶×-J¶K·ã4µÞîW»ë/×Û
+=¾TØEB¶;¯ÅPîè0Xëì?1F:ÁM¯ùÐÜ0ÒH,*Öøð+ôã*YèéJç7-ÞÝùDZD.:0îê-4/<ÎÀÉV¶Þ,YèéÀÔP*J
+ç;Ô<¾-1¶äYµLÎBîÆ,ÌÖëÇ/FÇ¿E@¶ï2ÞòÊßÙøDZ¸.JÈ@»:+1FîN,ìJì+0ÚÎôD¿=*RG¶Á2ñ,AÀÎÔîN
+0ÜÒÕKÔU×°ÛD*:2îÌ*8ÎJOJ.J+K/Þ:JãJKÞ/GÒ0F3Á°¿DÁ-µ¶:0ÍèÕÇSÑOÍJ÷7*6/ÚÇðE>¶5.Qîî0
+:Ì.ßÃGÚ,¿9ó³ÞýÌ+îI»7úݳ*ºQYùR?îû½ëùýóõýéíýÕÝýY½ýÝüü½ûûý¸ñK-Y@4ÍÑG¹òêE*öÌG¹Fß
+=¸÷ù÷DÎCô>.ZõYE,*DEëéêùøöóíI*2üøíÍG.ƹKQùýD×MXɻ˹1ͼ»ýõÝIAâÁYE*ÚµìÝ·ý8ÌÛùM1
++>-Ë2ØÖÕµýÄò¸V²¯9¾°Á»ËQ9HúõñôûéQóëÕÝ´*¶öâ¯ÕÌG¹´í*ûÇÁ*âKPà<úÏá»Ý;.½îÄÞ°ò->ìYY
+ñÚYé¼GÙºüûçüû÷Õ7*üüúïÑKÙÌûDÛX¼û¿Á¶Ë¼úV5HúÅñ0¿ý.óÕG*X-ô>×Éâôë7÷ïM±½»ËµÄëøñæíó
+ñAû÷YF*úüöÙCµ¼7Ù°Cëú¿Áö³Ã4ûG¹Èé>H½ùù0*YíºóåÒ±Ý8É4?ØÔÝÜäýÜüúÖ±óôó5ûùõ7*Xݼøß
+?IüúDû7úù¿Á¶RËD;¼ËD±æлüÃMÂíAíùðCÒY=PËD¿¸CMÎWýúØÑWW×ZãäãÕAöñíE*FüøðÁM@V·³LEõ
+ô-1ïÓTÓØ5¹ñFÊO¸øSÞÈÊÁ¯»Xñöå1ÍÚ¶±ê¹áIøééúSçÃùDôëû¹K½ü?*Îí/DHñ¾É5*ÔU+òVJ8.RÖ;5
+=>,Â?**åYÞç½õÜÙïÜÃOÑѵ³I¶2ýóéÕÓÐ9ý»ÊÀÁÕý0*>***2Z¯ùN-âÁêÅ°îV,.ìJ+;¾M,E¶>+¾¹¶à
+÷5ööî½EüñÖòÓï9ûó³ü<²ß·1éÜÜG¾½0*ÒÎ.CõHî:-*¾K*äÈ,æïë½MÌÃ34+è4Ì9¾µÝXùîÚÀKݺð³HW
+زEK9PÂBëäÌü7*R*òçEåBáÓ0F9*üÈÀæñïűÌ*1ìWàû*öôêµ=Ô¹BòËï9ûó³XÏñÞ¶/å´8GR½0*XãR,
+ÃÓUåÊÄ:ÖîL+úñG/¾ºÓXG*EÖK2ÝÏÜK.ÐÄå-TîØKWÖJè´ó*2K0Õà1P+XVKÛßV+*¶Q.Q,ÞÏEÍWBìÞÌ?
+H÷DUÕXÛÄO;îúKô÷ñ5ÞÕìÑ*2Ø*¼Rà¾J2¾¯Áî*ú5>2*:´ÂHÞ,1XèÀ/ÒÀÁ1¾8´Æ?ÈZ4îÀ2V²E.@FæÁ¹
+L¶;*Hû÷êÀ7/ôÚ¯úM½»Ë1òFöëÞÔÁ91ãY+¾á?кϳãÉý;î¼10Ò,¶Kû¾=Wк·*èT³À³È-BM:2´IÖ,õN
+êóèÛJ?1¾à³X:¼Z4îà48òKV+*¶Q.Q,¾Ä1µGîKF1ºð³:Z**·8úRRâÏç·*èϵäOËøKô9·ÒOP-,.P+â+
+å¯-ARÞÒUÅ*ú+@öQâ¶åô?ȸ*²Xò,Ï.Ñ*A¾,ÇMÜ@KÒ-¶Q.A,îâK,*øééúGå¾ë,PëÙµöû4*úÕ·ÝWÜæ
+ûE,J»BÎ4*:ÂÍø*Ú½-îû¹ó,ðE¾°+RûÞ4²Àçݽ½ÔZ°:V:¼**F×À-»B>¶Q.Q,Þ»ùP7ÖWÞÌíG÷DáÆ;M¹
+1>,ÄÝ+¾¾.-Zîöï/4.ú¶*+,O*2R?æáÅÝJ¿æ+Æû*5º,éáÅÍÔZF*:ÜÈZ4îÀ2.,ê³3K*õÂîL*äK6<ÞÌë
+ýòÕÕ+´¿Nöõ²ìÚ¹I,*¿7àJÇ+ë7ßñç¾T*×J³ã¾,æÛùñ0ÛM+VõìZNHÞ,Ó:¸õìÍ?Ê6F;¾5U¶Î1=,JßC
+;F3*áËùòÝï³¾ëÇÕ÷ÊC±Tñ6BÐÝ°øôMÑ5ÜÑÛôòü»ßè=7J*¼ù8/¾Ùõ·÷ûù/4ÖíÝáʺî*JKÒ:Â8?º+/Ï
+*AÂJ:¼Ð1JßC;Ú2*ß»¹G.R5ýæYY-ààéÒYçÖ±íÌû0*ÂâA¿*H4Ñê½ÊÏÜFEâX/îÍêö³G·ôê*¶´çâ@ãP
+J·ú¾åú*ÏðèÀµäÔǸ³äºUÐÆ¿Þ¿-/¶¯/1ø:ÔJ*N¸P.A,*µåÄWØí¶<:ôâéVèÒS½GÎ2Öû0*F¾ëÙUY¿´ù
+=±-B´ãÓ+Ò*ï³Ï*äBZ.ÙÍξíÜWñÁÂÊY=ôFµUðó×±XÈP3Ì2Þ/;¶JåVµÜ»E+ä²E*OåÂîL*äKàÕßÕöÅü
+øDÍ/ØZÚ½MÝÄû4*âÍ¿*¼ó+ä¼Ù6J+EÞX/îÉESRZ-´ê*Ö..Ã:á@Oß>¿ñVM+¿4¼ZMäÄ:RÎ>C´*¿-A.î
+à48òKV+*¶Q.µ,¾YÅü5ÕZæ×̺ËáÃ5Õ?æÁ7û/ÎY3߶+çëÞð3¼JêÖIWÖA4ÞTFÎ0ùÇ°Â*V/L²²Þ¶1ÁX?
+üÏÝÜ0IË´ºAî±NÈÄ,XBåÛìã²+ÇV¿Ä8ºÕïçÍ̺ËI¼úÖÐèÇý0ÌÐLð´À6:<¼71ýÇÚØÐöÅüøDµÂDñÞÀKÝ
+ÌË°á¼û.**¾-*¶ñ+Å:îú.@J,ïN¸¸ïÞÊUE¸ð³ÌøòçÈDÁֺ̱´ûR1Y*Þ4-Õ2Þ³éXG÷¯æÌ?ÏçÑSµÖY±â
+Ð;=ôÚ¸ôÓ-¾íÜëØ°ãÐ=M¸ð³ÌúöïæàÐü6ײH»ÍH¹5,ìùõêÍÝÎ8577I̺½»Ë½Éȸ²ãÊOÝ@سíÙéFõíÞ
+³Á8ÛØTÀ?Õô-1ïïëÙí¹Ç°?Ñë5æL¶Y¹*ú1ôD¶éôÊSY°í¼úüøDŹǰèÑIÁHËXéä»í*JÝ×=IQL0õð¼î:
+8ܽýüO8TD×Ò´»ï*ÞÜÓÅ9˳;ÇÏ9*¼½»ËZÝÖÆ.TíúÇV´íÜ»Å=×±W55½ï:Þ¼ÆÇ4üÔÅ4?Sô7*TÃ4?»Ü3
+8ù*ôÍàR1»Ë¯YUÊËJÉë5*Ñ3ÓÎéÜÔÍI·2¾IæÒ3.*îÝ»Ï@øDÜÜúóà¹ïÜÛ»*RÚ¹5ÝX¼û7Â*ùÛÞ³µ¹ÛÇÜ
+P×YÅXFµëù1*àWFøMXÑê×ÁY9IýëYËïÅÝHúé@èMYùKLðû½ëùýóõýéíýÕÝýY½ýÝüü½ûûýøùýóõýéíýÕ
+ÝýYYÞé*ä-**EýS*FBØø³?T>SÏJ½üÒ*ZG·ìÊ÷àé>PÊE¿0/øÓ,*øÚ÷ÐOÉóé@ÐNÈBùäýI1ÆBÊ>OÄVÝÔ
+7E¼.CðâÕ4*Æ<ãÆ9IÔYÙÂ3=TúV+J@°6*RÝüµ-*V»Û**%%
+d
+432 139[1 0 0 1 0 0]sl 8 mask 0 278 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/connection.png b/doc/krfb/connection.png
new file mode 100644
index 00000000..c4d236e1
--- /dev/null
+++ b/doc/krfb/connection.png
Binary files differ
diff --git a/doc/krfb/email_invitation.eps b/doc/krfb/email_invitation.eps
new file mode 100644
index 00000000..e1a5381f
--- /dev/null
+++ b/doc/krfb/email_invitation.eps
@@ -0,0 +1,473 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 532 447
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sat Sep 20 14:07:13 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 373]ST
+B P1
+NB
+W BC
+/mask 7504 string uc
+î½*¾î1:I*¾1±/C6*¾ÍÎ:Þ¾V.+¾QR2*J4½ÆÏ9ð¾+ö;ã-O<-îJÈ1ÈN0J¾F9>É:+Þ¹;.öµCÀ:½*ìÃ:Þ
+Åë2àX,K*ºöü/ïûÝîù½ßõýÀíý/Ýý5¼ýAúüYöûÝîù½ßõýÀíý/Ýý5´ç/+%%
+d
+/sl 59584 string uc
+î½¼**ûý9*N¶U7Ö.9R.å*¸×.ó¼ðÉ+Í7ùï*¾ã?6ùúÌVß²ØÉÞ-üøý*¾ÌËÛ½½üúÑÇӼ׿VMæøå8Ùó5,*
+ú5¼½ûùù6/ÍÓZ²¾ìQ¼Cîׯ9-¾×OݽòÂêáõèÒ4BðàÃO¼:>±åõåûóóU³73ÖÚÀ¯Í90.úYÇ:¿1ú³,*ZÞ
+ù=ÝD9*¶ÔÓá9ÔÉG¹îNöIÒ3Kß1æ+ʹ½ýØW±ÞÓį+CE¹J:ʼMÀ<¹ê6Ê5çôݽ@èº*Þõ<ÅýIúòãYÙä¯ï;
+ËR4¼¾8Þìû¯MãD-Yó65ÞÝîùõ½ùà.O²ÔA*¾59í¼¼ùêÁͶ¾°ãÈ·½îOñ+*åøFúøýýååVëðOUòÛÍ3Wß-æ
++>Ù½ùÎS9ÃèX=JVѽ0*Þ¸Ü>¾°åõíI¯ÇÎ2Õø**ºíéëïÉå8û·¸Ìøì5;¾T½õIº²H*îÆåÁYYý¹óRÓæâÂ÷
+äêAÏ?VQ1¾ë>Z*ðXôH´Þ00¼½ÑOͺè´9ÞÍÑYYý»÷îÛ±õêÑIµìú÷ÂS*ñìíí÷ÜBKؽ@è,*½EÙ·öùïÁÕ8
+ÍA18¹ë-<¾Rãõå4²D*îWÁÅ1½G¹÷Ò×ÕÊÆá<Ë2ë+4.²<üë1IÂ,÷ýý>¾:ëøø:ZXõûùé½<.µãîî*Fîõ6Á
+õÅ4>R6ÉÝçýÎå2FRηÉáõÍý»,øGë.è:HîRGP,ÌÙàÓ¶¶Ööó,ûýHRÎî÷Ï=Â.âÀ0·ß*ôìÍÔåÑAH³Ù0îÕ
+ÑUAݽüùõ»»÷êÍAQüØæÂSÞ¹õí½íýüû¿ÁÐ1*ÞùôÄ8HüûóÛMðàÇíÔùà.êüüJ»P-*Ì×øûîÓ½õH<á,õÒä
+ÇýÈGJ+üýIÎXÚýµ»1/¯,Ó*:ðïKRøýÝKÀÔæÒßO6*JQKÞ»´GY;ÒÝýO¸ÉHKõ@òI½Å+48ÆXýé÷SD³ÍK°ø
+úá.èJ*+¸Ò8?+ÅWï4Súç÷º9;úíÝDçCûÓÅ6Kâ¿0·Û*Èì9BPÐAH³Ù0îÃÉIÝü¼½û÷GüøíÇAIÜBãÂSÞãô
+íí¶Úüû1-Ï/*üÒZîäÝÝÕ9IÍÕì¹öïÀÅHJ+èÝýä²*Þã@AÝÛôìë5ù°2Y¸>D²E´1T:*ãµLû͹HÐâ÷ñö°µ
+H¼íýüD+4ºüý**7¸Ù¼ûÙ¿82ÖáöáµGXÎøýUâ2=à*+1TýóËEÛñÍ9<Í8ÖñDýý90*Ëå.Réöû--,*ºôÛÉN
+üÊÍDGÝ3ÜæWýß=íæµü1>û½¶Î:ðÊ<+ìòUÕá-ú2,B³ÎÝóùÚCöøÞ¾3*Ö×VÏ<Jüý½N:Ó5Å,Þýý½>GòOô´
+àýÙYÈÚ¼4ñ.MȲÔ**Æ´ýë<»HîÒó@¾ÈG8ú¶÷öMøÖûõÞ½9AÉïC42**Ìо**ZÐJ**¶Á,8¼ý÷ÝðåÝø±+¾
+õ>ËÚöõóïÝõóÛ1å»FðÉG5/RýüBÑ+*ÝSÕìÙ¼·MÍæÅC>ºD¯ÛAÙGR¿*>ÅSñ½ûμ59ä9éP¶ýýXÐ0Ã3ÖTö
+¹X4Øà¹ýáÂö½ïÇ+,öÏäýÒ;0Aæâüý½YF±¾¾õù9ýBÊUÁûAöÝ/02¸ÔÀ;âýý³,²ÚëP±48=ä÷ðèßÂSÙýÍ´
+?ýVöüýñýóÌú9æüýľÞT1DøûÖ5ú¶:3Võ½À?ÇÍ?Ó4K*+¸à4MüÅ.иï2*òýý¾ßÕ¸@8Þδô×÷ÂáËDFîÅ
+/2*PìYAMË1ijÛ/îGÅåäë»»ùõÛûøì·åHܱàU>**,*XÐJÂ*Ú9<.*ìÎ*õìÝÝÓøó·H**ðæ¹öCÁD*5üØL
+ÉÃ,´½ýÉ?±+*ÍA?¶ãÑÍùóãá7Á¸·TÍ3ÃGR¿*>WSñýøÀô1ÉñúÐÖýQ»ÑáB+ÁêÙ;½KUVýýNûüÕý³ÆÞèûÍ
+Ý躿PÚ¹ýýã>UÜêÎÍÉQÅ,Ê86@FB+USÝÕ¸±4Ü°ãüÊBÔVçÔýÕ3÷´K:ý-<ÌýÅ3ÄQ¾ÇÓ8ò<½OAöïJ<¶Pú
+Â9ð½ôë¿1F°ÃB*RýÆJPAä6Þý5¶3X.>á+ÊGú¾¹Ä?FTÁ-üÖ*+ÖäûáLºÎSùO*í2FóêéãÍQíåÉÝHE³î79
+HL¿-ÙÉ3/*åBK4I¼ä-*ZýÀýÜûÀ.Ï´îßý¯V>JûùÉÊ0*ºN¶ÜAÉ8Ìì>íA:IÆ:A<5ôJ/,ÎÚNÖýE=ÈYA:1
+.æÎËÇý=½BP¾Þàý´°*ðÅßÏýó37/ÖÉýå5TZ¾,ñûÅí鿺;óýÇ8IÚB¾-,Z<õM4·6ÞOÈVITNÇ<WèùÛÓ-Ã
+O°âÛY1ýI:ïýõ·VÂ:EéAPFT-³WýUîÀ>Ü´ÃBß=,·Þ4J°Fã¿GBMÙõÙ61ZÇöÍQô8MXFKü-¾ÄùÝW¶ð/°ë
+Ô+úÝíÐWMý8¼º1ýG¸éнýMö0/̾ýG42òíÅ.XO+åÂ9Ý?õìWH**FÅEöêá57>¸ìÞO½UÖRÞìõY÷¯D*îöÄ
+PäÊÔ9Nò¶Q×GË@³ôë+4öýý·Ã7í½ÚTÜØ»¿=Û¼Vè<ûYÝSʾYøÌ·ßúýIÛÔý3ÔûYAÏ*+.²õýÜÃÀ5ÖVË;ß
+üý»æ¿ºSûýÃûýYçòÞ6Þ++ÖÅçí/H0¾9IàÄB¯>¾Ôü½ý?·9á/úçüý+CÃíGSµC°åÚ9ëؼò*IÌÒ+¸ûÍIæT
+ñ+Ï8AV;<:JJòü½M:1VïÂ*L-êãóñG0WÎïÇ5ÛáÆÁQÆÖ,ZòüQ3ĺ¿VÙ³*A1ú²éåÙ¹ýìÛµ½ìø²îð.ÜK¿
+==ºÅ,ðºù;¿USÞ@å͵?»øÇ1*JéÛ7H¹µò+.êG¸íàí,5ÖRÞÊõYȯH*îåì<¼PÅ>S9Ó³ì¿ëà²ôÞ.úýýýó
+ùÔARHASBûûìíÇåÍàEN>PÞ+¿ûY°îøýEèàôíYäÂU¾*ôݽø=Wß/V½½ýõY1éòJÜBïùÈWñõE¯NÞÆß++ZÄ
+°éíû9IÎÎ,DغÜR*ëøI°K2CÄ52Ä¿-+ºNÛè÷íYYÚGßìïZàÓDJÅñï±,µH:è+,IZÃö1¶B¸øÄJè38+Þ²¾
+ïºÍÚàZ*CãTZÞ;ÄFºÀÂ?*»õYSîá5âØW-ö°ßWá¼¼GùôÌûøñÖW1YôYP>¶îÂÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõê
+ßÑ-îïÝMAHºKWý4/êðüÂ=ä-*H9J˶>C±QÊÔ?ÊÒäÁØXξ-,.æ:Sô<Pò0µ;<²SàNÀN<µ±,H¯FJíÌ;,Ò
+:èLÂæÔÀ-W@ÆÎU-Å5ò:K¿4ÓèµÀ:.JË7T/ÉÎV=+AJ:.JÁØ5öÝóKM/òô³ÁÏ*Â8ÎTÑ6¯*JV:¾ùÙ?Ç:KÅ
+°é?ÊòÛÞÅ5,¾3;HDÒZ»JÒá/UÂ..2Ú:Æ=Q¶>*Ñ+Ä9;P.5/ì¯âß=N¹+دüüð,´º¿VÙ³*É/Fµë1ÎQ+*Þ
+7º@¼·Ï¾Zíü6/BNóèW3AÜï/*¶¯P¿ùìËFùö6ܸðÓE/=ÖP*ÑôÍH;àº*ÞÇßLòVÝÁ-½KOÄ2WÐ?;?Úá¾Bß
+FÕJáíÝÍÙÕQO+,=Ò6ľÖÀÀ@é¼êØ4ÒêüÎÙ/âö.×@ù*Ô3öîÜÍöÍ39ÜÇÜÒRÞÔ±íüûÓ>îÄåÍÙðëÙà5*îö
+L3ûGúöòîCûòPMGØÄî+4Bîûã@Ê1*ºIZãì7VÖîJFAëC±í<ÐÀÁ72XÍ=*:<KÀLç¹J+·6-¾É@I@»/úîøü
+Fá/Êö.×@ù*¼2¶îàÉMÍXÛYý¼üøðâ1ÛÉC/ÒLïàWãÌû.²ÛKµÇÜ+ÃVú±äØ89LØìCº¾,Æ´½ø9-¼M*ÞÀù½
+8ÔÌÞÁQƳ×1-9¼Ú·ñåGøóíãµíXQÛÊSJ;øôëȳéµNé-Ì-Ã17C²ìJIʸÃ-ö./:óüå=,úÂ*¾ÝôíýY
+غ,í6é°;JRÇ·0*àåÚÊS¾5äÌëȲç-Í*¾5úåìCXÒJÏÀÚÌ×ùQî34RÃÞùMQçí1,*1¶¶ýü<ÁÇéõPÑÔù*Ò
+ÌÔPÅ*J²ÓÉD/ZóèÔF¹¼ÓØüíH¹ðÞDZµ9ú¹³,Ê;õüûCÅâõù*¾5ÂT»ýü<¹¯-öÌ·è°?¾øËùô2*âÚ¼SÑ*À
+0ëÈ°ãS¯ÕõU6½ýüê;E0Yý7*ÎÛÆY1E¯åöºWUµYÝÒÂÓW¼*AY´ãÖïÚÜ5õWVʺýõ-*Ü×äVàÖÏɵYXßè@J
+¾Å9Ù<V¶°Pîøîϱ¶>Sݺ/YÛÍ0*ºUË4É+U´WF¯QYáÇ:ñ=VÖ?ݼÙO0*Ì7E³åÈ35ô5öø²*Þ½û,@êB±çÔ
++´×÷TÝ2¿öó¹P2²½ý*Æ¿/*ï5=.ÙݽüãÞ,QPR¸ñüü+:¿4FCKñÛ¹µíÝ2*Jõ´/¾==²Þ½ý0ü5+*úìÔ*¶°
+À@F¶îß½;*òUîHCK:üû-J0@¶°ÀÄúöïâ½;*¾ïë4*QP2ÈI+*+ÞQ:¾¾-îä-°Â**Î*Î<Z.*>ÝÐ+¯ãV:*;
+1з<Ѿ:Ã::<ðË0ÂGâá9ND¿ÀÏ*Ï.åÆï*HìÀâã3ÚÊ.Ò3â*-?X×MT÷¶ðE3²L͸ÞÇ3ØñäËIY*Þ*LʼüÚ
+5*1B:ç,F,ÁȾ±½ÞZQ2½üúUIÇê+íòß´öØG´HÔÂ.:¾*Ä*ñJBXLßëEâ/,2ÀQïÜ;ÝèÀÒðïδ̳+BíÔ*¶
+°À*û3*¶*×4LQÙ5*òì÷Ì,JÚLÅHËJ¶:RîJ+Ú5/4äKÅìA2æîP18/**R/ZÕ?SB;<Z¾üè0ÚÆÌæÏéT3G¾-
+²îSìLíN3¸Ýº8û¾Æ·D2JÊÌ3µZ,E*ß¹ôÞ+Þ0O¿,S×ÏE*-CÖ¸Ð9µ.ÞÒ2GZ¶3MJß+ÇBã²0R2»RV¶·<1:
+ïÂ=XJä3+26ÆQ+8Öâ/RÂ*>MÒðFËÅLËEú3TBY·Q8ûÇ+QP2ÐI+*1¾óÊòßâ,Æì1ûEü²Çòç9*åÁ,14ÞE=
+µFKøò¹PÀòµ¸îR*TÒVøRëÂ18Â9.,F4*åµRøîB¾Q1î¾>FÖ¾ãÇζ8:ÝMI:1À1,2î2à76@ê0ë>JöQâ´á
+ô?T¸*²8ò,Qøïâ*êX;ç7/Õ>±Ú:3,Nìö;ÆàÀ±=3-¶*3QFïÐ<ÎS;è4Q½°C1J;òÐ6ÏÈñ°H8:å7ü.?ÒÕò
+W*FCKÖü.*F¾òA:ÅÏ8Þ¸¾:øM-JÝX2Ôé¿,Ó*HÛèºç*VÝH¿;Ý8Þø5G¶.*+-;*ÀKõ*Â;¾-8¶¿*ÆØ¿çC±
+Ã4FJ-ÅS3VZ¶ö0ðùæ1.Àï¿*LºÀ2,Ѽ»ö,/2X¿õóé³YÖ8*RI7¶Z:Q.JÇãÙì=Äò1Ê+ÐI:3¿*º3N¯ßãÇ
+F¾Â=̶ßCµ.ÞÄÕÒàíÇã>R6Û>M¾.ÑFAñï¾AìîQSKºOí,ÞÇ3DôëÙ±Y*î*ÖM¹±8¹*ê/ÁØã+òÚÞBQ4ÞEÇ
+=6E*ôê;Öðá+Z½Ï-Å2:Z:,Þ42å¼»N**¶ß+Q2Þ8ë5>´ÒÞ+D¶æ>ÒO»1YÅóźJæN7.:Uç>LÆì8ä8B¾/Ý
+×±í»KSÞÇ¿Ú:*¯HàVGV»J-ANL<ò¼K:1ë¯D*@>NÚï¹ÈB-:ÖCF×Ê-¼R¸·ÐF1ñïÀAìîQSKºã**²íÔ*¶
+°ÀÞû3*J*ì÷,ÎF4ÞWë3»Gµõì¯Éæ3,/T+ú+ßåù,*ìG6¿øìË8F4**Î4ÞêNòWUN¾+î̾õ±FZM±*çHÂî,
+7òÄ7åïÄBóÊ1ò1Üçõ,+¸ìÞʹ=ã»ê+-2ãV¶îâLËT.+FBÆ5,:Jç.,ÆVÑPB.+íANà4¿Áü¹Oß¿ÀFE32Ó:
+ïÄ=ìSÀ¸Í4¹øÐDÌVP¿LÊß+MÒðÚòÀ-ܶÁIM/ñ?»5*QPæÜÁ1ý»+**¶Ï*HºÓ//¸ºý8ÁJZAFÎ4Þ3¼*+V:
+HíÇØ0ù:º½Ï-Å,ZöX+*öùÁÁÂ+20Þ¹W5Ìï*WY¿ì2ß/DæSÉÜYíÞð×9ÞÉK46··2+SNÞÒùùÖ-0ÞÙÑ.5Nû
+H63,¶Z:A.*67>ÎÜ2ÂÅ<2W5XÆÂÇ@Á¾ÃÈÉ-º·±<¯*1:ïÄ=ìSÀ¼1ñß¾:WÍVP¿LÊß+MÒðFÊÅ5ܶMMK/ó
+?-**¼Ù,ÞÇ3ÀöÇ,*·ÕSP°N*î±/ÃE2**8BJÀ¹2OÆIC°.ÃòÈñàUØ´+ÌÊÎÇI½Üú+¾-í9,R7¶Ì*Pß¾<¹í
+ÐJD²Æ4>8ÇM<MƲ=°»À²ÂìèæÏ.¾÷ë4*QPÆäÑQÝYY±ñì*òÙ?*ÌÊÎÓµí¼ûY.*Þ¼Ù,ÞÇ34ùõݽ*NÎ
+,QPRîåýü+:¿4FCKÇYýù1¾.Tîâ.ÝýûOï,QPRðíýü+:¿4FCK¾ý9,*ÎÈ/*@Ý;QP2êýÁ*JÒ<ÙÌ-*@Ý;Q
+P2èå+*äGYÁE=ç¿ù>:ÏQ1*JåÆ5Ý»EÏ=JôÀODÂCñWâ3/.¼òáQO*NÝ***DÃC2ÛP-*Jì?Ä.ß/ï0CÞÒX
+QÍH»ÙÖ¿*µ/*úÝ°,AH@RÔU»Äë»ÃJ·ß*QP2äå**ÚÛYÄIë0âG+8ÆéæQ+JùÙAîÑýýýÝÝû÷íç²ïµÑ¶Ã:*
+9»³æÞäÏE6H5;VÁ0Æ°ýÃÍÈ*Æ8AÏ÷ëUåXDæ.âJØì6JJý³°Ëá>úS*QåÍ1***êÁúúàHP+Ì-¾òXD+HJÚÓ
+J³ÈB+7Ê3LËçTêî÷ø.9?¿Y8TÚýýýùý@GûìÎÊXBÞÇ3D÷ðý=H+*ðí+¾AI¹Ïêõù¯Y/M9ØÞ¿,¶çQÍä?½Û
+°ÆZðë6Ò3°úòáÍÄHûëñ·2,î×õèGËÜ»÷ë3¸âS±2ÀÓÖ*OüYõB*ê¼OD±ÀÊIüôêOÕ0øÔ5>¹Þ¯±¸ú½Ê6Ù¹
+ÂC@î.PßÓ+ÎPܺ*³éø@1ŽÆåÒźղ*ÞH¿*áXÔðëåõ¾ñàð?CEO1íÝÀÑ°7G¸²×ü2ÀTTê±8ûK**ýµÆÞ9
+íºÍÝúòåÓEûõµ0ÜßCGR¾==>îßIBK*ý4*º·Ù:éÀýí½¼ÉúEFòåXÎ2ÞQY/ÚGöͺMØÖÉM̼¹-áë¼I¼øÑã
+íýûôëF>,¾Õ;ÜZýÊ:;Qöº,KÖÈ4ÎãýÝ8ÃÆåÞ9ÇJËÎã1ÍGæ*¾ÑXÎM1éÄÖöö,*VúδßMíÍÀù¾å-ºö*ÞÁ
+õçîÓê,HºÐöXç¾W6U÷ôÀ*-Ê»¼Ûß**÷OGöÚÚÀË0íFØLZî÷U¶TÐÅUØ@Q;RÚUÕMFV¯HüàK4+¾ý=MîàCØ
+ÅÝESÇ6³äàëåU>¼¿±¸Î*QPRÚ¹½ZÀÞû7*Þê±µH8½´1é1äH<ëMAR.îÒÙ8ôXýøÚDM+ƲÞNYð*¶½±À2ýý
+ùçíHë¿L*ï¹ûÇ34×Qí-¿·íùWå.ZZ0ÞUõúõWåÂí8,ÜÇ2¿ýËYÒ¹õÉ/ý1Î9ÇÞÊÎõÍYHùñ×A-»è/¶ñøó-
+5Ý**=¶»øÊåOß-¹îìKZõ*öJH*Lݹõð0ZëÓD½<I´ðÈýýIúÙX·Þ¾ï¹H?=ëÛ¹¾¹SCK+YáÀÑ4GúÞ5Å
+DÃð×ÈùAÉÆDIâðîµ**úIC,ÍEö¼ïúò¸õXM966ýìÚÇÚå°°îâ.Oùô1P2ØÝ**ÒG@æâøÅôBýúØUQIúìÇ5
+Ê+öòÊë@üXÝùÜÜѯùüùÙIÊÛÞUüV1À*8Ê:ãµYó+*Úê¿L*CÜôÇ3<ðøÆI1,VUUé²³ÍáJ2¾<ÍBËçéï-*V
+Y¿ô´åZ.Üûêõ@Ù5üùÝÂõùÆ.ä.Û½üñÝñ0ëCæÑX¾CËEÕ¹î/*BÛäÃÕºX4ðXÅ2*è8ÞÛ¶*·õìã7öíBÏè.Ù
+»¹òJ´6ÁLYÉ»åÐ1Íó¾çG0ËòJ?»ÃèZ¯»úGÄ/Q?D¶îE»Êàã»?éäXÞÃ@Ý3=Þí**ö½°.æϸöZÛ8.Þà7³9
+õBÃøVå/ÌÊÎVÝüÞ.ßø,*57ôTè?Àüý½ûò+ý´HÚÂ5ú*0À*Føä³OÕÕýÛZÅ4ø»ýüûåIíüLY¸ó78FR¾DKí
+YI¼öîÙÉìVBÁº×-14JìH½°ÀTôKº.¿è¼ç-êB@ÕÖ/5MùÜ8:òZEKÙ¼+*²±BïõÅÞÕY½ØºàÛ¼á=ðQËI<¿C
+KúÍýºôÐY***»ã/*ð3ÊIå¿50îVÖêßÀã¸*éû¸Ï¹PPؼ۱Öñ61ºSÜJ1ùÛìAùZQì½´¼ÚZY/½Ã-âêÖâá*
+ÊÖ<R¿»çÏõ@@ä.G¶ÙAX¯á@ìG³ÇYàÌÏNGDß=M¾1ñAQ¶¸O¾ëÉÊûß6æ±I@¶°ÀLÛø-=.è5*æRÞú9ÂÎýå9
+½ºÐåÇý@L/H¾¿:*åVµ·ûîõ´ËGJæûùåíüõáãKÁ¹µ¶îJ+8ÊÎ펼¹ïÏK½0;YôÞ¿,2î³YÊÎÕôɲڲðÖë
+ºÕ-ì×Aͽýüóè5Æ»G1ì×Õ¾-ÑÏLó5Wâص*¾BÒõÜAÚ¸óEµíñÈF³¿·ú?3Â3Lý÷ëM9¼´RñÙÕÌÛ²ÎÅ´J8ô
+¼+*å·îJ+º±*ÙÓí0ÎC*ÕXû×NË+ÆÚW¯çÏXÛ¸JÃøòø¹íß±Pú¼³ëسÀú÷È86ä¯÷ÀÍ-æÔÃBûÛÓäçA+
+ó³À8ºôDÙìÏUöËXùÒÒåBðìÊîÇ;Ö¾R·¸¹:+ÔÌïñ+±JÍûÖÞÇ34òæ9Ê:JY*ίUéPGúüî7õIIûõQÉ1ÝËH
+5¹ûJ0F¸òßéÀåû·S÷ëúÆÎõ½Yüîåà¹A.³ÍîÞ¿,Ò3¸üøëQAì¹ôéà¸âÁF¶ð*<ôúBKÕXEïÙL/°ùãLßÑçï
+Ó7BäÚD@G*JCY¿»ù0ìEASãÌØÄ8ÛÙìÇÉHÐNÇ,?Ü0Qî5VÐæÎÔ5Q6üÊ.¾ò:º¹ýû·ñá¼ÊIüôêOÕ0øä0âI
+êA*îF18>¾ÅÒÒ?¸òÆL,2*îJÏ5.*¾-Ü¿3ÚN/íö¾LVU=¹Üêåʶ»øµçE/¹é×GÖÈÝìûH1ÞU/KèùT6¸A±¿
+RÃ8Ü*Þù*ñõÎÉS³*CËI½Ö»:,<Úý=MZÌ7E´úJ,VÚßÜùOZH5T¿==>åÍIBK¶Ü*ÞAAûý½öØÀ¹¹ûùéÅÍË?
+÷í@E³¿@9âJ2¾õWXýºÕâÕ±ôÈÃùÕÝü¹ÓâËõê²ýÛß¿L¾CKí9Ýû÷åÅùä˶é@÷:1ÌF¾Ù¼¹ÝºðÒ1¹ó?1ÔÁ
+P¯ïýý/GÚµ²Þ>íTÕï+*:¿ÑÚH»-øATç53JÛÙS5L:ñ<H7,½ú´XáåÍÉEæ<ÝÓåùG6.õßúïÝYÍÜêåðÁå8²
+*ÞÇ72¹ÑY*Þ·8ÚZ3Qú5âñÈ?µëTØTH7ÎWéÄÉ>SæMHî°Ó,GºüîýýÛüùïÛ9´ì¶R;1ôCHBÇÚäóH1ÞSù>Ø
+¹T5¶1±¿RÇ@ü**Ú:¯1ó×éñá3<¶ZÅ5;*ü9åÇÃ5ê¸Ýá?ÖÒ2FCKSÜú¿3Àð1*J¼<9CðçóøVÉøùà°·µëÔO
+õÛÃù/7öðÓýèÕíDYýìýºáRùÌäÜÑôÜÛD²±ÒÍîJ;JË:¶º4*õÂÞɵüDAºðÑ÷øÒECPòøÚýüÔIýáüìê¾á4
+/5@W-18ÒëZßDóSðÁµ:ã¯G++ýùÕWVï08ÂQÔû7*¶ò-üØNÕÇOõWøÖßÚÚYÛE±·Î*XøýüòãÁå¼ûÖÍõÍYH
+ùñ×A-»°-,ìñ5*¶Ì-Q8Jü+¹ÒBèµÚPÔ@S»Ã-ìEÞÑJýÑÓÝÝáQýû¸îÃÅé>P×GØ+Wº7±ÆÖÜ?ÅRѸ*ßû.O
+¾éÓ*2TÓÍN¾É¿Õ:ãËëV2KÈVø*,Þý+-2âV9+¾¸>øÒNÔÆÍìâÐHîâ./·ð1P2ÆI**×5µâÆýýíGìÂ<Qüã
+Ä;Éüº÷DÔA²ÞÇ=ÍèÛ¼ûCë-ýÏÀXÖMÇI?Iüê×æÊö01ä*à.S½ù8U:Ì:*ïí9êG;¶ÓCSÑÕ8ÙíÖÌ>TåôõíÕ
+YÝãQ͹ÒEFBâñçæóéÅËÓؽßí5=:ÅWÛù-QÌFðPÏRÏÈÄùóé15?TÍ8ï¾ñXRµèÍ°IõÃÀÉ4N´ùOCBçýóͽ
+°ÀìIý÷í·Áô6òÇ7¶µÈ?UXúAíÛZõ·8F·*¹è8CÃ1<:Læ¶ëTÓõÛ8öÈY¿Üü7¹¼ù8H¹òìãXQéÓ²ðúÄ0Bï¯
+æ>±VÓW¿.=WÁÅWPNÐGÉ-KãøÇϹ۹ÆÊ3óÌJN@À3=è4.¾ý-KZ6¯±Ýü6*.³ÄQµ°S9¶°ÀLÚö-=.à9*¾=ã
+.7D¼Õ÷áõâõØÓBÙ´æ¿7íà>»ÛÎ+AXºðàøï5Hêûùåíüµâ¿÷TëÐ3é³Eé78Ê+¾çKÀÌÜá÷ìµñýýÜÁýÌ»øñ
+ãûð/Ã9ûß6¾AFë¸ôóÍùTôáñáÅýü»ùëWñÚ°=Û-,QBVܺ÷VGÉûÀ-2æOøì÷J2*SëâW2ß2*;MãöË4öÈQE
+ð?8DXçú¸òÒLý<ôûX6ÖòýôåÉ9ý»ËÎýå½HùéÒO-»°-ÖW¯à·ë8*Få¿=1¾Ï¿ãê0¯F¾õ7å3A±ÙN?¼Ö-XC
+NÍXýÑýܹÕ캸ÖèMé>7=HØ+Hù5Câ3KOUÌßKáÞKéÆÅ°Õð¾RÛÄü2*º±ÌáìÊϱà+Ï*ò²*ö½:.Òíöêõ*
+J,õ¹¸=Éø¾==>ßÁIBK2Ü*Þå3ÀÓ>ÝT´Ø¸øÒ±A9¼E´èö·°×ýBGο:*íËòúùäSøõíñÉýúÙöìÕMEäQ½3º
+Á-±*¼øýùôë¹ú½ÚñÓÚG»öìÓµÕÐÛÜâøÖÕ¹Lî,=ÍäûÙùöô»ûøòçÍý7öZÉëöXMõ¾Þõ´4Å8G¸ÑN÷ÈÌÁ1õ
+ê=ÕNL4ô¹HB-JRAS5LZòæ/=óÀWä*FHÑFÁ1Ýë?±½úñòßÅ1-/û66ÖòýñåÅ1ÝûÊÎùÍíHøîܹé0JÍ꾯-
+Ê´ê,¾ñF¶ñ*ìêáÇä4ð?14,ÁäÊ?±ÕèïJ+ºÝÕD×µP8ü¸½XÛXìæËAã4>ÛØ+0É5Bá1ECUÃÏHÙÎ;¹RÄUFJ
+ÒZU½2*êEèUH86¶C<*úI2,ð0ùò±*J¼Ý¸¹¹=¹,¿==>ݽIBKö»*ÞO<ðOöýøúº·EÊ7AQü¹ôè4øðÉ×ÚSX
+DR.îµQÕåHò²üóOùãÏ-¶ØíµêöHØ¿C*DûIüCKÝY¼ïàÐõ±õÌÕÉO×õðèÝì3¿üáËC²îèMë»úôÔ÷XäûôIÀ
+ÄEÏÁÂJìL¿5*îáLO¸òæ<RÎ.Òß1¹Î2¾ËPTäËEµÚìë¾1V×òõíLëìóCÙÀ0ëö°äEÍ»+Éä½üº÷î×Å3¸ýûí
+½½ìº¶îàEæ=EJéæ°»**å·îä+Fâ¹8;0=ÃÊEÂäVÓ°Ôíô¾µ?4²Wû¿ÙUõä¹9ÔüÚ·ßJÃÙÑå¶J¯óTÊÎ<¯Ó0
+¶=ÈÛý°ÚØ+L?ùøÙ*ZæK1V?6*úI2,øODñU*J¹Û°»æSùà¾==>Ü»IBKî»**6/3Àý¿ÀÜTFçã¿í´ËسÛôé
+éÀ¿5.îÒ½Ì18¸äùðÛµëßµ¹ÜæÉè±EB-2=.õá.ûûú÷Gµ/8½²@T@±åеý¸Q.ÌY¶ïÑA=õìÛIÕÚµ³éµ3À²
+ÀYUNó:;V³RÐÒÛ**±P4ÒÞQÅÏ2¾ÉP4P7ÔM¶ÍǾáET8OÃJãÄòÖËP¼¸¶UÛZ+îUE*JY*¾Éüý½üöíÕQµäÃ
+11øâ0NDÒA*îF1ìF¾ÅGôÎÞ»óÚK±ãLÛMûÖMºó´XµÌHúú·õÜô¸òèGÍ/TÐà¸Ô-º³ÓVÁ-@ø³ì*1LÏéYÄU
+DJÒ¶Á½**ÊGöéÉ9Å0Ü@¾¾4UF4*úI2,4Ó×ïE*οå´ÛD³Òù:Ø94FCKßËù¿3,ì-*8æéýÙýÕýÝÜÉé?F÷ì
+×1ù=ØT@5FL*Å4E³Îú¹IWÅù0TÊPèA·J¶ñ¼¯ÀÌü¼ìÀ»áø=I?3ÖóéÛ½Û=,ÕAø·òÝ5åPê5ñPûóã×ãã
+MôæÇíûöZ<Ú°°ÉT7UÔVCê?T<¹Ñí8á¿,JÐÇNGLøX.ÚÜÇMÍ°O×ѳåë±ìÙÚ·õÚ5ÒÜX-Aû½½øðß±ÝìÌA9
+8òÇ32°MY*Þ·8F¸*¯óYÆÎGUô3Þù8ï·ïK7È·²óÁÛµòÉÍèÒ÷ÑVÆñX1ºTRÔ,õ=²É²äËÙJ>¶ùã¶?-ÀTõK
+JõVîBZÕNãØãN»D/ðº*çó:ºÊî¿:ÎQÄýH**íÉÕWÕVÓL³4Hã:LÙ¯¿==>ÙµIBKÖ»*Þ1?»ÚêýéEMù´ÓÈ-
+A»<ÕÀJ¸ÓÞ?,¶âBO0ì豸éäAÛÀÉÍìJ2¾ÍüíÜ»ø8ýéõØÝ°9íØXÝÏ?ñÆ¿¯éüóÌ2Úî¾¼×µÍXD¶XÛÔèéô
+óÙI=NâCɹ÷2ÌòÖMÇDGÐó9*¼»6A6¾üGLø:.Ò²Áõî/úD:ÞQ°?ºù´Æ¸ÍÜP-=ûÝü÷á¼0Û·ïJÍʾ6ñ÷,*
+ÌñÞÏ-¶»êæ·¹@ê=ÇC¶ÂíâöÒUÚá;5äM³èÒØðB¯AµàÍ´D/öÜHÖâS¿,ÏÚãAá?6Ò3ླྀN°¼¼@B.JFW6.J
+Ø·J8=º:.üSZÚ¹õä»ÙÔÍ7ù³éÓSÍ@?MIO°LUÇ¿==>ݽIBKö»*Þû:MÄê½êâÆïXâÀM»@YïD¿Á.î½³ýÄ»
+íïÁ÷ÞíéEîݽÍü½½üú8ûýìVÛÁÕìÚ¶1ìùõè51õüóî2Úð¿OáÍÝXFXUúý½ý³ÀÄØN¾µíµà0RÞC°O÷ùóÁQ
+ÉÜÔ+@ûÝû÷ðá½Yô¹°,ÂTãÊ1*îF18G¾JÓ?NR@GËMGºIÌTßÜ?ù>SNØYÏÀÇ9õ¿UøN¶HG³à²¯RòøÉïè/F
+?ïñåÁµLúºÉ»@ö÷-*8ÖÜäñåMG;VR*ö½:.üÇYܸïØOUÔÁóÈÓTÒPÇ4?JIWéÈ8´ÞÇ3Èïá9Ê:6I*îûXËE
+õIõ·ñÎ;ÇøÖÛW;¶Ì*ò°Ù×Câ¾Ùü×±åÌ8ýYóðÝUÅÜF·ïÝóôëÖG4ôó<3FµÅ<ÛYõGµN+ºýÊÎÏXCÎîFÐ;½
+6¶ÈO?4Vå±àãÕ³ÞOýI½¼ºöëÕÁñß¼µRJR/¹**õ·î0,>òPåºÓÈDǸöêãÊäÃé´æ¯<ÉåùH¾S5WLêµ+¾*Ê
+ïÈ/ä.ûú»½Ò¼ôèµZíE´åÆNé<óø2S·º2úI2,ýN¶ìÙ³åÄûèÙXËø²çÏK½ÈÌÍ@ÔOå@îâ.ã÷ñ1P2Ì9*öë
+ÓÆKºé<àS.èCÏó+÷=¾ÅÊ9EöëرÑôÐM½Ü6·ðÞÒßÕÙKîO÷×˵9VâT?ºûÓEز»3çÆJ/Ö³¸LÇ<Ë3ï·7¶ç
+úåÍA9Hùó*»ÖYàFÎåÍI±³Tð²ê¿MÕÐóÍ?¾ÎÑî+ÕT2<Æ8CTÂÆ4ÀÈïT/2Óïò-*FXYº:CLâ2<¼.R7FÍô?
+H<Jòçù½:.ü3WÙ²ãÀóÈÓUÃè>SÏJ»ð>6IOÁ:5׿==.éí+¾ý8ÖæÔTÄ@Ï.à9ØJÜ7J8¿û¸,*ÞU*¾úàóÖ2
+RëÁÄM+Z´ÎNÑìÜ/²Nï,ö×Ú»ïHÜ96*ø4*<ä+Ú¼ÛäXÊõÏ<¼³9G³à²¿ÀÂöÆï4/8ÊZ2¾=YëCñäҿɽß+K
+*Þý¿:2ÃÍX,*RÚÅÀâÁ-QP2ÜýÁ*¶±ÀÅ7*úY¼MÌÊ:Êý1+¾R?µI/**ýÉíÆ==.õýK*:Tàûå+*ÉíÆ==.ùÝ
+½*âÞ,QP2Ö½ý*Æà/Üý3¼ý=úüQÖÍP2R»Ò**ýõ½ÀîýM/¾-O±óíåÕIG*Îúõé½÷=ÒíãÕåý¼öâ**½ý8ÌGú
+9½ÀDÚöòùûÕÍ6*>µìرù̼E=.*üè3°é×µý@ÝÝü**×ͽH/ÎêµÕÄûDóý¼Àú¶ôèÕßåÝIC*RøâMͼëö½I
+K¼ù¸±æéçéI**÷ñS*O9ûñäÕíÜFK*î**¼ô3°ÒÐ=µX8Hû+*RIÍ,RöâEM@7óã385ä4ÀK*íó.·OæÃ?ùäü
+»°*Îê»UX6¸ã.PR·ÅØZßÒì÷÷î¹GKÄæ÷ZâØÍQÍ6*ò·ëÆ1¹ÄB¿3TRãºíüOT´;>ê×½Õ=ÒùF9¼6¹öò**¯
+IHê÷Ü.PR?úäÌûô/½ø.×EåÀ9Ýüë¹°*ÎÝYI@ê×Ù.P2@Â3ÀìÍø.×ÖåÀ5ÕÜWù*JP/¾Ó55üVV,=ÒÎÊûôË
+6ÒI»Àú4²àÄUåÄGC*æêÓ9A0ûT,=ÒÙEOÖ:4õðßÒ.îÛÌίLG·ðÑ=.*Ì2ÞÛ3LéÈ=Ñäòö.±´¾õ¸Àææ±æÉ
+QÝFßù7ÎÛ±½87DÏý¼ÀÊÉVèÎMÝÌ»**²åÑ*ÈúµéÔç´ØË3¼Ò*BMá-ASXû¸³ìÇ+¾¼åüù±PÞüù3Tôî0;Ëê
+¸´*¾èW¹W²TÃß±IKã¼:<BTÐN0*P/ÊHÞ8¾ßHPöÑÈîÃ3äÛ*ùBß9Ü0íý.Ýý3¼ý=êQ=.ÒHT*¾ýÙBK
+d
+532 112[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7504 string uc
+*ÞX**HºýÀöüYöûÝîù½ßõýÀíý/Ýý5¼ýAúüYöûÝîù½ßõýÀíý/Ýý5üÝA.%%
+d
+/sl 59584 string uc
+äÕí´êñ1*KóýM/¾-O±óíåÕIG*ÎúõéI*-éäÕYU9û.ó¹÷ó÷Ý9HC*ÞíÝÁ±1ýáÍÎ0AHû¼ýó=-*ãËÙôêÜÑ
+ýõ=âÔ±íüW¼½û+*°Iýº4J×íX7ù³èýû.÷ïêÓYÁͽ½°*ÎòÇÅHûØïý¼ÀúôóäÎÕÑÕ½**ðåÑ*ÈHøåËYÝ»¸
+ÀèÓ91,>æ¶ò*·/Þ¹GKÄT¸ðäãßÑI**óñS*/ͺ±âÐÏÜFKÔÈÀåíçµA¼ñ*Ç+Þ¹GKX3¸¯ãÜ×½Í6*>¹íÈ5Á
+X÷ð.O7Î1Ðõâ+.¹*í÷.CLåÁ;é¼H»°*Îå³MHêCàÉ=>2ÂIÆÜ=1ÆV+Üé3è»Ì1IÄGº¸*¾Ö99ôºí½IKôä×
+îáÕ½ùÌ6*òõéÅ/¹ôôð.ãÊÌ1Ðõâ+<U¾õ¸ÀVê±ßÃUíÔ»*:Ñ,ÞèÃ/½@ôð.÷S2>·IQGóË-N0*íó.÷?æÂ7
+ÕÌ7¹°*Î×UIX6øÓÉ=âêÕè×ÎÞ/LÅ8**ºå=²ÊRQÌúò.+¾=,¶µÀæØðâÒCGGßÚ3îÛÌ:Dø6øðçAQö¼-²õê
+ÍMÁPòð.+¾5*öÖQÆ·ÇO7¸òé×1*¾ôº4JÇý´ëDëPAQB÷+ºÕ=â-ASXû¸³ìÇ+¾¼åüù±PÞüù3Tôî0;Ë긴
+*¾èW¹W²TÃß±IKã¼:<BTÐN0*P/ÊHÞ8¾ßHPöÑÈîÃ3äÛ*ùBß9Ü0íý.Ýý3¼ý=êQ=.ÒHT*¾ýùIKöýÏ,Þù
+IßÒ¼îûÍZTIöüQBÓ9ºý=Êè1Øó.ûóCÞ*òüùP2½0CÞ¾Ã=B**X?Þ3,*æA÷:PR+*B*.ßü1?,*Ò,¾îµÃ,
+Õ<Cî*Ä:ÁÉ@¶ø+øý½Yæ¿Ç50¾üÇ:1îï+:¶ÜÁH2ÖÎáýÓÎî·C2*ýíVMJÞ¯Qú+:ÖË3L2LÍ9åúî.9<Þç3Ê
+3àýõ7=ÏöÎL8ÒêïËC3YóÕ9ç½2-Zý¯ãöÖûü9´KýýýD¶Ïý*H2IZîµWR7ͽùèýçýXÉíñå7Sú4:ÚÎ346É
+Íâí2¾=ò:.Zô*Lñï/¾ÂãÌ»±D.î,ô***BC¶·ÀüýýÛQü59â*9÷ùýý·3:>ÊÏÌÞü1@¶õÖBZÎóû½*ôï-<Í
+ÅRâ0ì??8*N1ÝüýI±¿VöAÒJ*´2X**T.R²<98Æ̵ðúý½=WýýÕáÆü1ZËÑ÷.ÏÁóÃð2ÞØ82à+LæMQýý1ã
+õRÁ*öÝH¯:îÄG4?ü¿*êä.WT;é8IDKͳELEê+:4æËUWÀGùJòÈIÆ¿¹+¶¯À켿ýÈ@ä½EÃûý9KöÏ2-JFÈ
+õ=-üTEöëÍãø9¾+é¼ðÌ·+±é.Ûéá-ì9·-ýý-Õ9ER2N:ûò9îÓ2ÆJKU=úýý?äýý9Ï<ýíüÄøý+ÂÕ+ØÄ4Æ
+.òY>źÕïâ.Å6òåQý×=ï½DJüý½ï3K*QêÏRï9.Þ´P2×=µSãÝE=âé×5Y¾G*.KíBJ¸ô2ÀÍ9âZÀÔHÁý3Ä
+3:ýýƾS99J*6WG/îýAÂýý½¹0ý7*ýýçIæC±YÒÉÂ+-ѶԽXÀð0î8*íÂ*ûÕî¼·¾ýý½¾ýýýËIJãË0EEJ
+VµÆ½³ÀÐI¿==F°¾îG¾ISY³ÞÓQ**Åêï¾=H+,Vå.7¹ôÖEÊú¿ó1VR¹¹Þ8*-Ùó,<KõVP2÷ð÷Ý+ïL2ÐݯË
+3EÇÄê>¾úå:¶0Ü1ÌÃ,*2AÈLÒ¹R°Î*üÑàQ¸øM=V2J3ìÕ½¾¶LÀûJ*éÒîÏÝã,Ø3E*ýïN»ï1LÜÃ==VEZÍ
+¼Ø¿ûJ-8»À*ÞîÄG¼Ñ3¸ÒÕÍ=Ë¿Gù,ÀDÛDKYÜË>ô*Vë÷ÊÑéäÌ»;¶äM/O+ìÕQæ÷M´ÐóýX¾¾±üêýý½8ÚÀ
+Î21ÞüÈÎýýAîXY·59HÙUK.òÜÛVM>Ö@¸û¯·6C.3R.ÎïY0*üVÞ7î;D²+HJF·â¸:¾»Û¯Âý°+Aì+¾ý²O*
+óÌóAµùý½âÏT2ÌÆ+GËðP³¾>îüà=V+õý+Ú¯ÅèõJ*ÙCKýðß¾:V×±½ODFF=ò><ZÐËG*íÒúÀ,GÖ±ý60+
+³¿G¹W5ÖÅ6FFK1Z¾ÇÍýýÁÝãCB*ðý½73ú/ö*ýOÉBû½B0ÖG?/6Z/Ð;54NÃAðýýåÂB¾Nìï¿2,ëBÀ±Ä
+*Ë/.0FîEùè+ß.OCðòàJ*Øý,ÝØ+øýýáJ*Åù1Á,C>ûýYÐóWK¶L0ÂÜ,ÈÓ+<6Á5ÍZßBú1îÂG8òßE:¾ëÊ
+îG,¾ðÁ*,¸ÁÁ>*é9¸JÊZ@+2ä½+¶C,*ïIØPG*÷Yã»0K:¹ßã²7µæïIBÞÌP2*>*>/Î464äÁ13ËîÇ-N·À
+´ß¾=Ã9N¾¶QH-;¾äýÀúð,ÀGËî?/¾ðCïKÀ=ú.ÇZ»=ÊT/Üý3PÓ,íý.ÑT¿ÍTÀùIß9Ü0íý.Ýý3¼ý=Úó3,
+TíI*X¸*ú½¸Àîýù1¾É8öüQöûÍîùIßõ½À½ë.OÛ6»UDéIK3ÅR:¾²ÃòÒ4*¾N4;Q7:*ýÁÝÛ¿±;¶ð*àÐæJ
+*òýWùU³á+úÓýýQ960ÉAÎ@ÍâFIÆB++Ϲܯ¶ß*¯<A¹:2àÃÅÙüýÓU×Ö3ÕûÁ-4*T;:**.ØæËâ;8·Á+B*
+.ßÎÁá,·HJG±ÀVüÄÔýÝI;¼K=ÊP;8VÞ·HöéäôýýíDßýòTÖóá¯ú××áóùÍIHç°F,ÄGNôë3Ðöñ¶9-Öú×Ý
+ÇG1¶O/.üâ7÷³ÅÛµÊÂàñIÆÓÍØÞ.ûýÓIê=Á÷ݹÂÃ+-Wí9÷Æ-*çßÕýýåVî5üû½òòýýÎÒYï¿66÷ù¾0êá
+1NHIî¾5>**45Ä08Úß¿B6T-*1ÀÞÖÉØÞLÛLÂÞò.*ÒX6*¶¶ÇýýÅ·öýUα:ÈÛøÝ9ðM¯Kñ<J2°½Î6³+1
+L*üISÂE÷¶¾9*Þ:áòÁº4FSæÊDÒMF<8¹OÝIáá*¯*L¸ÚCÇMáZýýÕåîä»òÞ.é.PRô,Íì²å3>ÆÛQ8èÃ+ö
+Ý=ÖU+,*äÏDæ.û½NÊ0÷ýýTE-Ýû×VÕ.ãû;µîý½îø¯¹/ý½¼øÍG3ýRçàúµÑFÎ+-ÔûâG1Fî,/*¾àÀÏ¿-µ
+¶:0Ñ+îý¿:ÎVõìI·/öýLD,*TA0*FVÂýñQÁýý?Y9WðýÕüõ¸ýRV.Àî¿*AÈÏ@îJ.â½*QÜðNHÈ@86MòÁN
+Í<YæÚ2ÊM;:ÕR0EHÖJÇYU¶Þ*Þ/?ÖüÞÉϹñH¸Ð¶*:ÁI¶ÂQõP¿üÙYîÎX2*ïRàÀí.RGT1ËÎñ0Îùýý5àý
+QýÙ±¶ORM¸Ü½XÀð*ú1í+ÀISíý.+Ú<*ý5D:ò7Fî¾5>RVRD¶î0*¼Ãß²Þ/E21îíÚ,ú½;7îýÃX6*JõÚØ=
+¼·.0;Q±ýèý±=²Æ¾SýýÂHè,8¾ú1îâUÌâMçà¿/Jö8ܵ²BYæÚîÌM;JÕÐäE.C1>ö½7ÊÚCÄ**B÷PN8î,
+F¶ÂQõP¿üÕåîÎX2*MUÍ/RÂ.ν-ØG±ÀóA¾KF/ºIÔ+üÍêWV¾ÏàQ¸ø¾*üòÑ7êýE:¼Ð2;,ðGÂ-8¶¿,Åîó
+À,T6T-8.+CJýýËU*01Ð+üIçÃ3*ÑæÓßèOYTÏåUý÷C¼>@N½áDµÈÏ@îJ.â½*µÜðLHÌ?»èCP÷ÊKÑ.îR
+.îÓý@3ìY:KýQýUB·N/²7Ïü1í-8FØĹÑJý÷ð·RA.JD°å,Ò8óƸDå.û?µïß±¹óÝ+SÜí?K·.ÎïY0*üý
+ÑHãËýý¹+,ÌIZ½K-òÜÛV1Fî,/ìÌZÃKV¾>îüJ2ÊÌÇìÁG2,SÞûòÆýýYòà*¾SÌäËýB2ýýý¸ZÕüµH0+Í
+ÝÝûñÀ+¸8ÚÀEøR5¶:,ðI¾5ö½Àò>ÆO÷ÀÄ>îüJ18þÜø½IßÇü-NV½ëî<AÊFD0ÀYÄÜÁõ+1ÌVáÇôF¾,á
+9×ÇÔ1óà*ÖÝ<òÓ*ÓMÛÇÃìCKíÌÈ,0ÖÚýõOFƺORܯ.3å6+øÍÜUJ÷ý½öÙBB*J8æ¾×ÎJ4ù-8¶¿,µQÖà:
+@JÀ¯¶:.Ä×22=9.¿.áîLDÞý=ÛB*JúËOW3MýýÕ7.ÎÎ-üÐ/7àÀ+Ð;¿ÝEøR5¶:,ðI¾5öLúµâ°*¾æUæD
+Yæ¾Z³ô*Þ?ï.ÀöøóÄü½óT@¶*ö¶J+Þ3?;ì:âܹ9¶î.Í̲QK¿+BÞâåɸ¿*üÉ@¯øÏ*+*ñRÃ/Û±ÀCE*ÚÍ
+ó4LýY0Þï2ê7.2¸9ø=0Á?ËNÀŶÐ.1ÔØKQÖÕèJòÇéYP÷õPöíÖäùIßõ½Àíý.õ½ÀÈõÎ1,@òÆÈ8Vöýý+F
+G¿Á:2K¶:**ÞC6ÚϾ6Öîì5ì0M8ú¶»0åÀ:/NúB=âµWR7ͽùèSçK°ã¸Ã±åQB3QFؾ>Æ;Xîô.YÙMØW÷Q
+PRòZ»Z-ìG@06²»ý9*¿òîUÊè>¾59âÞÆÝýýùô·2Zö9***ý+¾¹L2+2<R4Jñ<J262KZ±@VöÕVî¾8:¹S5
+V*¶°9?ÃK*µÖ:*βϰîN,NQZµAðJ5ÌÃâÃPÚÎ4öýÞ6ò½*9I:÷úL2XRõÇú½?SýM5ø¸PR<νZ-üTEöë
+Íãø9*÷MìJÊè>¾UØBýë>ýýI<ûîAÀÇý*ì3KËÞ¿@2.ß9²*ü1XI´/ã,1ò¾°Û¹½TîJ*L/è2ìZK.Þ3W**Q
+<¾ÂM÷üôLXîáÓÆÚBÁ5T·¯0øI*ZëQì·;.AÒàѳÚ9ãÃü6;¼K=ò5àýýÝGÁý*Âýý=ZÑ*:KúF0Þý½À÷á-Á
+,õÂî*äÈ,2<ZèU-VýÚ.ã?ú+*1òJF¾Yµ¶:*Ï,Ó+QÖÎôïÀ@àD8N¾BØøöLXîáÓÆFCß-T·¯0=.ÀÜ¿5K¶K
+?ܲÒÝçøZ¼â÷éCK¿=ó¿7*0ÏRî»F꿾Ë,Æ0Â50¾À°ö3+ÞîÊ2XC9çíÖP³¾ÏàQ¸ø-=ºJ1ä*ûýüíËî²Þ3
+@6*FÏ/*î.CND°ÅAð,IXÃâÇ3HR¿5±B5:íKÚ2À5TïßWʹEÎJ:üÍÒßÉCKQU,RõøÉÙûYY·59HÙUµú?4Þ
+¹+,ÌIùýýAǺ÷-êV+õý+Ú=ÀU=Nå¿U27î/ÚM*7ý°*îÅ/8æ¿çCç»ÖU=N0Èõ57¶À/±,1²¯6ïL,Æû?Kå/
+ÌLáîáÓÆÚÎÅ5Q·¯0¯+òÜÛVAÀîÂT²Å8ö5ÍÎóËÆîû3øý¶ýZR:ÁÐýÖG?/6ZÛÖÕ-/îZ*âýýÞ³7,Büý
+9@êüÌ2XË:çÆÞY²¾1áί.3å6+D@¶J1Ä×22=Y¯+÷ðJð?@Ê3FÎïÂ=3JÐOÊ8A¶47Á9ÕLðÀHÌFã¿Cæý*
+VR:ÃûÃ:FÏ4Ë?èýû8¾H¯ÂQ±ÀC<*öÝÁ:*NÁ*<ÚÁÒG;¯¼ÎýýíåE4ýç.A¯/ZY6*Ûæ*ðÀ×ïáÌîMWÄõ½Àí
+ý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý+òÖ34ùÁÑN¿òîî¿ËÎBMF.**Ú=+,î½
+ö=422¶¶-ã-/*ö±3-Qt09F**ø9¾ÏB+æõà08úKZÞ3HRãÐRBÖ,±J¾Ú+±NÞO¹¯¾SúýX:26WU18ξÙ.°J
+ÁýýóXK/Ä277Îûå¿*ïÌýç¿-P¶´.ðýýMGöÜPRÜîI,ƾûXÁî78îÓL<彺ÂêENÎ*RNýýýÊ5Y¶Þ*ÅPæQ8
+SèûûÜT6ÐýýéÕ7öDQçñùôÝôúL´Äð¿L+ÕØÞêûùýýQßòàI¹Kʾ°êáÚ·I=Q18Bß×;Æýý9óÞIDK½òòýýÎ
+úü1ÞK¸öóý½í·2*0?´ÀøÓ;¾Zü¯¿=L2*¾ýåVÖîíN¾ý1ÞG@06²»½+1ñº/+ðäëB/·ÉåâLÊJÒ*4=÷ݯ±A
+BR+*ÞXè,5±/üï-<ÍýáË<ÛDÂÏÀAê@̲+<¶ýýº9Á¿¿:1àÞüýå±TO¾K×øIéî/üÍOðýý?½·Î-1Êîæ2.¾
+ýå¸î5ËÎýúòI¹<üý-JüÆE2J>ûîIÆ,×2ò2JâíÖÞ3;Â*ÞýÞóáÔî¾*óÓ´îÙIÉó,8L*8J¾JZ½Ö´L½Æ:T*
+÷à÷Yëî-=6;J×ìÛYºîÞØÍÚñIÍÛ+³ÛõMÕ¼íîN5:1ÍôQ<21:NÑÛ*ÉJ+áJRÆ1¯ÐR÷*AõúÅ2*-+>,ý¿-G
+¶Þ.ÅÄJüý½Ô-Ýæ.;º1îQè5ûDñMý=ʽ;/°2ø;¾RÞ¯+*ýÀ2L*VýÚ.ãËç/+æ/ïýýíÌß/FÂ*F2ÂDKZÝÈö
+Þ.¯½7×éö9¸**È7Â-ö1öÞûýQíÞý.öOæðAM2YôêÇ@BÑ9¾*4JNÏWÛ½ß:¿,¼*OùH¹úýíÊËVö,æ2+¶:èQ
+ìMRF¶ß.ÅÄJüý½Ó-íæ.ïýQIëÞ¼*³ôý½/í9J¿Æͱ.Î4¶VîÂ2>¾ýÒöÇÌüà.+ÁBKöHPÖDîÞäÀ*âËÙK+ð
+ôÉ5:B<Jîöò¹;.¿+â*ûý+Âñ+üGÂýBFÝ÷J8/÷Jë3ëË*ÞS»ü5I?°õ.6ú½9ñýÝYîý½êîü½ë9L.ò5âýýY
+0±´îÂ387¿úýýR1HÐ3ÐÑSìÚ3GØ>YüTK÷Èí2ÞË3H;,YÞG,æöA-*I:Þý-¶ãWºöêXÛ2ÀE¿ÞTÃRÔ9·2ÊA
+JZN¾>-ÆûL2à9ÊÌÇìÁ?>öùô°ûÕ¶.ìùµ´°ÂÞùñQÙ¾èýÂHIM+ýÄ9ûдüýå߰ܵTN¾Ìòñº¸L?å0*Ï5P
+¶².ðýýMGöÆPÆæUJ÷Pò6öD.ZÊ/2±ÃÅ*ìÊî3;öZ6DÄ:ïÊ;J++úI¾ôÒ>Z¾?´öâ8Xî:ÔÚ+**º/5APöÄ,
+Þýö-áV.ÂÇ-/ìÀ,SêýýÕS/;Æßð+:.*ÞýýE2*<D÷Þ,Ä*0,ºúO0ÚÆüý½YÁÀ¾Éá¿,èIÛ1;ð÷½Ñ-14îà2
+ìòÞYDßñ.:Yö9+F0¾EìÈäÛîºÂM¾Û˾0ÌÖ+ÚýµêÔIPRRÝD,Ö.*ÎøHIíL9Hæ3¼ùAöüQöûÍîùIß?Í:%%
+d
+532 112[1 0 0 1 0 0]sl 8 mask 0 112 di
+/mask 7504 string uc
+*ÞX**HºýÀöüYöûÝîù½ßõýÀíý/Ýý5¼ýAúüYöûÝîù½ßõýÀíý/Ýý5üÝA.%%
+d
+/sl 59584 string uc
+äÕí´ê-*.ÓýíI*X¸ÞJγñéÕÓõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍZUVÁÒÉÏëÙ±?=Â
+JË>Z<²F¾/ýýI¾6î:Ã5VÉ/ÂOÏ¿*æýÙôÕèÄ-öUýýÍHB6@YJë?:**;*å*:ÜðX1Ìûãïã22Û±ZÂÙSƯ-8
+øMáÌ:Y?<·¼âKÖ@LGê;Ö½íF;ÕËõRé.ýÝ4´ûÊ:½´ô¶ûüñÅåLµÕIîÄ.ìXäC7⺾CBæÓLVFÈ´¹Àð0ìJ
+*½5å½***ú1ÞK¸öóÝöJ0ÄöÝýý-*ÖKÉâü½?SýM5ø9*üIA+ÀýQ°Þýý½òòýýÍ,UõÖBZÎó»îJúÞ*¾ùÌÁ*
+ÁýýåV:5üÃHÊNFè×Á¼*ýY7ß+O64¶¶ÇýýÅ·öýUÐÑàCKç1÷ÏÙF¾ÃîÇ=DC¶BY;ýýI<ÈE½Cò¶·C?,µÚ,*
+FƾUÛ·î0>DÇ11:-+ºú¾âñ=N;*EFï4GBßD¿3ÈÛBÃ*ÃMR¸ÞMA6ÒEQR:Ö9.:ó=FæEõÅÚ¿ýÈ@ä½5ÐÂ/ö
+¼øѳÚ9ãÃüÀ;àÎ+¿:º>ßÃ6öýý½¼øI»ÈýøTEöëÍãTµRÇJÅøß9/ZÞóáÔ:LZ<ïæ.9ø+ú½³.1>¯*ÞßLúI
+½DúýY÷øæ<ý½ó½ÕùÅìð*@ÀìÌæNïLÏá?ÏF÷É=ó¶³C?,µÚ,*Fƾ±¸Rµ:¿Àܾ6.*VùǹÌJÄòÑýÂUòNï*
+J0½îäÅXKç¿íòI+ÚIKÆÎP¶ä=.*úÅ,¾.KÞ<*ýï*YDèí²»BIðF@¾:+ÑHÒîJ0úýýîGºíæî½Û:üýýëCÒE
+FÆ.BI2ÞøåORìÂÞNàÏ3HòÁ±M¶J3-*èGÕôÂYü98ÂÆÑ×½óý×3À.-5ß2*Þ3Ë>XÒYÚ<ÈÑó¶³C?,µÚ,*FÆ
+¾±¸Rµ:4ìIç-ñýI»-QÆðÆUÈ*Þ¿MI¸.O1¼ÑSQRîÚ7.:·ïFC9-î½-àó,¯+FÚ÷µ-4.°Þ.óµVñýý5C2,
+Î͸ÄSüIÁÎ?ö¼Æ/úIÎ>Ïî»:¾ÏàQ¸øE¾TÄBG¶æ1õÄÎ>Ï*Þ>¸³ÖøÊ:ýÕæñÓýú6I¸ÉÀÃÏÙMD·ð5ÕÂðZG
+ìWPÃ*Z¶à?*Þ+ÚÚÓ-Èî¼ûE»:íKFLȹX+18ûæÏÄFîÏPGKýT°ûÇÅZûÍüAîü¾»ZAJÞ÷±I2îýýõæ+½ï3
+Ø0/î·î4üý½;-;*ò>ÆOϵìWºöêXûµÎFò¹JS¼SÜúíýM*7IW0*ü¿N6*Fæ¿Á¼¶µ.O3?*P·DµüM?úýýé¿
+ÛøÝ÷B.ºüüôÍ9ßL¶+*¶N=K*ï?õÂðâY0*XXPãë/Õ.MÚ¾ÉM¸¶î0Â0W¾QPÆìÙÉó2Sì·/F¶àBоùURTÝç
+Ò=ÒýÃ<SÞNæ¶*2´0GÀ-ßíàÞ*Æ¿*ýý½ÔRýõ¹*¸R*³Nîõýý5C2+îò?²ûý½¹T4B-VìýùðJð+O·î³2ÞöÝ
+¯.3òÊ;J1<¯*îJ18øßÛ;>OÒ*:üäÐÄ,Â3Èý³-+>ò¾Ý²KÁÖZÎZ;Ú**FÐÇ:¾öȹL·ðA-*AAÑÌõÀUÀ;XN
+Þ;GF¶-ùè+ß.1ÔÞVR:Ãû;ÐÚñ4¶î¯NB0ÕÎÒ¼ñàHØñ.í>J:»È+Jæ»æÝ<ÈVÀ<̼ã×1â¸CÅ<¾ö9Wñà9Ñ
+õÅ0ò¾ÊPBU12»ðôÂ<ÙÐÂËö¿WBîNïÀ¾ôùüäBßQG1íý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍî
+ùIßõ½Àíý.Ýý3¼ý-ÖÇ34ùÁÑN¿òîîãÌ:*¶Á*Å0*ÀI¿*·-äâ¾Éôà*ÑöýÛJ:BØÜL+µ3â¾0ýýéÛÀ46;DD
+JùÍ-+àGýÑ-1Êîê2âýýŸî3Íî71¾ø?-ÜäSøúÜûÕGÝÆËï2/Úè,±õíýýI/Ñ5ýê/¯,ȯ9ñäûÊFG¶Þ.åÄ
+Jüý½Ò-ýô.Û¹V6BæøüýFÇ*K²¾**î¾/Xö*<¶ýýº9Á¿¾ß;42HÑT,:ÈÓý×ü41ÎV<ýýY÷É5ÆVP¶².,Þý±
+GöYQÖ*ÞýÝè0¼ù±×ü5.ßé.8ËßR*ðîçüN,ï*À²ØM>¾6./÷î²FÎ7îðÌý2+³*ýýýö9Fæ¿-P¶².ðýý±Gö
+YQö*+úÀöýýµåîI<+PûÇ3FDKµø+2J7üý9WVÛ½Â,¼*OùH¹úýíÊËVö,æ2+¶:èQìMRF¶ß.ÅÄJüý½Ó-íô
+.+ÁBKûÌß½û7éFC¿M=6,¾ÑøúA¼TâìË3¸9ñýÝYîýý0ôü½ë9L.ò5âýýY0±´îÂ387¿úýýR1üë3øUìàÁå
+ôèýñ,ÚÀî*SÞûËC69öüúç@ÎøIÏååRÞÝïMÝÒËýý·ÖÊõË43;Î=ûFÍÌRã×+JòÀ387¿úýýR1üë3øôÒ>Z¾
+ãû½7<¿Ýî*3Äß+5Æý*,*òýý7.*¿÷â,Ä*0,ºúO0ÚÆüý½YÁÀ¾Éá¿,èIÛ1;ð÷½Ñ-14îà2ìòÞÍGßù.öJý
+Ô;PöçåÞùIßõ½Àíý.Ýý3ôÍ3T***6æϳÈÀ**ÌJ*Ú¸Þ*8öÙWï0ýýýè+*ÎýÕ4Ä3¾ÒA´*ÔI¿À59ö³Q2ýý
+IY²Åêø*ôÝHK¼ÚëôûØÛ¯¼ÁÂø4Cöýý幯¾16î-ÍÎýYýÞýC5@úÁýýÍÉ*K¶J?¾µÇú½?SýM5ð쯯ÞJéùý
+*>Þ0êOWZ;Øî¾/û?ÙÞVüý½íñÎ*öFîîâ.ïãÙý±éº¿¿øè?÷7»CE=²³Á»ùûñÌÎMåäæ½îø¯UZýýM2¼NÎ4
+ÞѳÚ9ãÃü6;øUëÞµ½@é½*ÖÍüVõìÍ@F2*½O³ùWýýñÈI>JîùT:R.¾Ñ÷ûí÷ø¿3Ä2:ÇOñÍ5ÞU@åáóõíÌÎ
+-*:L?ñûñ<âð*úý93BóK,Ó*ܲÒÝçøZ¼âCY2ýýýô6ü1821îM5ìÊÎöýIØÕP2ð1802>,æüæÊéñýèÎà¾
+*UÝX:Ô*ìÕ;ú×=V/ZÒöÇÌH<¾òµV=BÎ4ÞÁÙBõ´J¿Jú-0¯Àî»ËÎCJýÝýM=V,:ùH1/ÆåôX¾ÞÆ*/ûAó+/
+60ÎWõý1:Ì12øàHí3¸.ÎïY0*üýñ,ÚÀÞ¾-/îG¸ÞYºÀÔýÕÊÙöî×ÃúéýýóÊòHêã3ðáë¶íÕÛëì5:B<JÚY
+C½J+üý½WñùßGý°K+ãYTðñGKíBÀ±Ä*ûÕK<ö56¶Î*HUÒýùG*ûÝGÐо+@õ±ÀÄ×22=Á+8¾ß¿3èý·ÑÛ¸
+ãò32F:¿54îýýÝúÓ¹E¯¶14ÊCY7ã½Ù=Ú4,ÞùZÛ×úF+7*ýA8*ðýÎ9Úá3àýýïýûûÉÐY-ÊîI´Âõ½Àíý.Ý
+ý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý+ºý=ÚÊ3<ó/RÄ,:*ýÁQß0+-*ÞG2F꾺C
+**ÚßÁ>2ï´EÌÍß×:ÚÄÂáÈ·A,1Bî@DÂOÏ¿*æýÙôÕèÄ-öUýýÍHB6@YJWPÌçÞ=N8SÒ·1YK*ZýGEÚÀ¿ëÊ
+ZTÂ2A¶¹Àµå:Ñ3H@¿U;*ùYöÞW²Æéäà»Ò³ÀýY/Ù¼PÆIE¹Ú¼ý÷áA¯GÛ¶ÀäÃV,ÔMÆ,*ÎöJ0Äú74î*Eú1
+*ÐÁYýïýC5@úECîý+F¸øý½R¼Í*ß²¾º4Fôî*,Ú¹V6BæøüI¾Æ¿4/ñG1+0AöJü-RÆ2+¶¾,MÍQ<J¸,EA¶
+À1AÌÎد@îàDÄ.P*È1.¾ý1äJý-öö+ÇåQDðJ:ºÉâýUÇøIØÎ-Á¶Ï4+*²X,.1TïàM.Þ0Á+õÏßßOüýIäß
+ýYÇÌ4å.OIâÅðÒ2ï,4è,8ÂÞÇ»FTÌ0GKêêâýPõ8Þ5TØBýë>K+ÜÍüý*ú;µîIü1XI´/M-üI¾ÍÍü±Qàý3
+:Â0¾òï*ðýý-2ÎÕØ¿íüVÔ¸À</¸Ò9üR¾5ºRƶÁ,á6A<JÂMS±¹À1QÌZ4¯@îàDXÊß+9**AäJý-*ÔûâGQ
+DðJ:ºØÄùÏÚæÍ;à-¯¶Ï4+*,Kö5Ó¶ï;ÁECJÁÁÃö½ü²öýÝðóÏOüýèýXõ5/4+4;XJß+RÚZÃÑ»çÎ=Òë2ü
+å¸MýÀAÆ:ôV,î922ê+üÁ+VýÚ.ãG¾H0*Ü-ö=óû½,.2ÜÁõ778HF¾*Û:üýýëCÒ=>SÆ.ñ-+ÌîßJà*JÏÞ+
+±FÆ*¯²³ÛÞ3HF·À=±¶¯7Aäî¾1*¾5±BK:ò7FçÆ-¿îOMûËÔ1ñMÌ9ÂßÃT.*:NÞÒÕKFàÊFÎ+ÛñèÌöãRTû
+9·DùÕüIEßLÂ5R¶*4AVï²Yøñ.Oü3;G0Jìý*L*N-àã.*Þ/BßýÒöÇÌüà.ïýQIëÞ¼..ôSåBãÉ,4,*Á¾;
+ö¼P2ú9GÎýý?6äIáÁ+¾L¿°Ä53:çÆÓ5*0Qöîà3ÌÊßóÚBÁ5?¶¯0±+ðGÂ=7·Þ9HÚ÷µ-4.àý+9ÚRÃ**
+ÐݯË,ÁM9Ç-Dî½ÃX7ÔH=âý?øºóIIáµX*üALÚ¾À-ζß4QùÐIQRÖà3.:Þ²Ãèî,ì¹+,ÌIùU=ò2JáÝ6*ú
+ý/¾ùÖ*ºDãËýýí*+G/°ç+S>.öUìàÁåôè½-кÝ-BÀÉðãýüý/HÚçÐL¿èÛ53JôÕ.GA*Õ8îßË=Ò´Â/ÚÖÄ
+Á=º¯0¯+òÜÛVQDðL:²Å8ö5ÍÎÓºJ4¶½Ï4+*òF¶/Ó4ÅH°ÂïHDê½ãçÚÃS0EöóÞXûíº6î÷²½ùå/-Ã2AÎî
+*>8>ÞÇ»²ê3ÈOL-+Úð.ï+ú9Ä+ºÙÿ+:ÕPæEÄ:ïÊ;JýÁË-Mø4Rüýý022»/W9ÆLK¾µQO+-Vìýù<8NH¾
+;-Ü0+6QöîPÉ4/TõÌN*Ç@¯¸ÚÞ/HÚ·ÀèÄÖ¾5鶶Àîý¿Cæý*VR:ÃûÇÄÚ2.Ë?èýû8¾ÔBÆZ6XÊJ**çÆÞY
+ÃÞÏÅ6¯ÚÅ0ùÍÝ×ÒÑÒ¯*δ:¾À8öÉ@²2504;XJß+RFR¾=ùæÖ=º³*Kâ3.Þ¯6:òHòݾO?J80·O+î¼PI¶
+º2õLî*-ÐÁÄÜ@ÀÊPBU12»ðÌLÞ5´¾OËîÑÌÁõ½Àíý.Ýý3ôÁ3<*ýÁ9,FξÒ+îP6<ÕÁµ¸À/¹÷Ç8êÍë.Y9
+KL¿ö@QæÙNüÝÈÒýÏ/Çý*,*ö½*:GúøýýÇ-öýý9GûýýÑÞèG@06²»9@2CZý+*ê:ZîÂ7ÎöJ0Äú7AZ9I:÷
+ÚPÖ:¾**êîÞ6ìò,ÌÉ*ßýýQÖÎ,ïÉÌÎQé¶IÈ5ûCLF2Òýý-îÅÚ¿R*÷9JG¾ýýýñÑýçÑúÕÕØ¿íüVÔø¿:*ç
+Q½ÞÞ3EJO5ñÝË<XÞ¾³I·QËZK*+æQý¿CÚç/¿:ºîÞ3@ÒJúÐ=òOMûËÔ1ñMÌYÊ:öY/:ý+Ò-öýýß¹öÜÏßý
+¸KúýýÙ±¶:-1X*üI¯-Ë****+öÍE*øÃÆ1::ÄîÞ6ìò,L,PûýýQÖîSÄZ,=âßWʹEÎJ:±¾µäõÂ,L*òÑ̯
+Èýå:¼1ÜÍ3,ýµXîÂ7N-àã+¿,ðGÂM22/*6è6ìò,èíÖäýýÅÖîÏÌ:ýíÏ-üà=²ËäÇ,ì9ÍÎI/°çýýÕÚÃåî
+×ÛøíJ÷¼µXîÂ7ZAJÞ÷±IVJõëã¸î»-+¯.AäîNAî·î4üý½Á/Yó.³ÔRýõ¹*¸DQÀ¯¶ºÀÌÜÀÔýýµQO+±¾ã
+û½Y8NHDß5´¾îµ¯Z*NZ+×ÎJT²YòJKJ6ßÃCFç/Ò;¿ÝýýÍZßFßIGßU.*ü9É+KõK½¸×*õíLÞÉ»¾ÒPöø-
+ÂH·YA÷@¹À½Û2Üý3¼ý=úüQ¶ã=.%%%
+d
+532 112[1 0 0 1 0 0]sl 8 mask 0 224 di
+/mask 7437 string uc
+*ÞX**HºýÀöüYöûÝîù½ßõýÀíý/Ýý5¼ýAúüYöûÝîù½ßõýÀíý/Ýý5¼¼;-%%
+d
+/sl 59052 string uc
+äÕí´ê-*.ÓýíI*X¸ÞJγñéÕÓõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöAÍÎÖIFO4N11Íô.+
+¾T*2Ü.îR*ÂCàK*ð0Êß+AëÃ+Pïý¹¿JZ²WC¶¾,ì<Æ+7üýÕ¹/?BL²²¾ôI1,¹üQ18Bß×;6¯.*ÚÞÀ-S¶
+ð/Ï6Ýò.M+8JÞ/,ÆAúÁ*Jûà+Yñ>»¼íüéÌYâäRCR>îT5ÊÛ½ýýý>ÊXú±AÂ3RÄ»ÊEõ±ðòÞ+=FEÀEàRV¶
+:/QÜ:Èï¿Ì:¿òîUOÜ-*îI¾Å9ƺÔý+úýÍøÓ;¾Þ¿B*Nîüý÷H0-+Á=¯ZýýñCÓ<*ÏVûÍóÕ¶*ã<÷ýýÈÝFæ
+¿-P¶².µØ×ÖîJ4XºK3áýFKðÅÚ¿R¾-,ZÞØÍÚ±RJ@,G,R=Îý·ö²½<¿¶*KDëÇBîýýC24ðßçæGEÞãGý;,
+è*üýýïI¶Î-1Êîæ2L*ê¾9ÛÎ1,À+¿/1Ðîà5ÈBüå34ýýY¾Z2*÷ýͼ*Ïûý½2ø;¾D+ýíò-:¾DúýÍLÙ¸ýN
+6L÷FOùH¹úýíêNSöàÚ8F2á=õ;>8ÚZÀMM2+ÎüÑÙEòýýàÀ-S¶ï/Ï6Iò.94¾º1;PJâåC,¶¾-=*¶TÍýàQ
+ÃáÛ3-õ¯üýù/üýíÞìýÕûK¾¼¿úýýãZä0ÌBßÏ;Ò*¾ø;ËÃ/1ÐîJ+82á=GKSÞû2*,öýÜÛV¯,ð?Ý;¾-Fî
+ýùñQÙ¾èýÂÞñ±¯îíöÏYèäýýFVäùPÃÂ2æǼÌQQÒðê*:¸ß.ÅÄZ,*ñÅÚðýýàÀ-S¶Î*1ÂïÇÌÎ.áZ.*+ÖÎ
+J4ùî*¯Ôö2¾ú1:*:¼Í.*¿÷â,Ä*0,ºúO0ÚÆüý½YÁÀ¾Éá¿,èIÛ1;ð÷½Ñ-14îà2À-2Á,µÀ4558Nß¿A6¯
+ºÎ=ºß*Þý2JúW/¾ºß½.ZQ*Æ¿<ÔO6ýé.5+XÊÎμ³.ZÓ*ÖW¯ºQ=úãÏîûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍ
+îùIßõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½*Ýç.ÃûßW½À*Þ*JÔÂJ*ü1ݾ¼.EF¶ÄUë¶29Õ²ï05ì1â.J0DÆ*H
+L*Ú5.BØüí½8.0ïÌýýýË3¼*+RØ5-Êà112àX8óÞÃ;Ö3-VÒV¶:µÞÊTFSÐ5GÆCËØì.98+êÍÆ:Ä18ÄKPZ
+¶õ³·Cè7:N>/·ÊîûÞFÖ,ÕGÝê6ýCô¯ÌËùC÷ýýA39J*Ô·ÈHݵß+¾ýÓ8ÚB,ÁÝýY0/1μ¯á3¶Úæ¿0/ßÂP
+æÛá྿Ôõý+2¾7P-úÚ.2üýAHÆ+HðõýýÜñ:2E¸*¸á-=âøæçýýI/X»:*EÚ¶,G±ßÀ½ýýÕFFß.,çßÕ5ɶ
+*ëðI¾4+*îJ38îM;ߺÀ¾ðÙ²+üÕM>@Ú±.ýý½ÉâýUÇøYL5D²R³·*9XÏ9ýàÙÞý9¾»ú¾âñ¿¿E:3+¶±3/*
+ÜHÁýV<ýýY×»÷ÍøÓ52î.ìýIíá½èÕÌóÞ/7ÂL,CAìòæ.+¾=¿¶±ÀüTëZí¼ý½Ó?ôèdzÕÊQRÎKQæY³-Ýû×
+Vý+ƼøQ-.Àî¿*XçàúµÑúýýÆòÁý0÷Å1äáêåQýÑ17Ê5ƼøYF¿+Õ9ÙÀ5·¶À3â9·-A@ï,D*¾4+*îJ38Ê
+J;Kº,¯Qà½ç,ú½î2Á/õ6ßýýÍÔï¼>Aø5Ä2íÊî+ºJü±TÜ4EØÞ¾³I·åÞÞ7ÆÂ*Ú×Â4*MDë½-¯C2ý¿ý5¿Ü
+¾*åßÑûÇ.üY.øV1Xæ¾GP³Q÷M·¿ý1úæ/ûVÞÃ8Fï*ýýÈã÷¶ûû½OïX½ãô¸XAÜÎCQÖ¸ìAÂýý½¹0ý+F¾,Ã
+.Êøýý=Â5Þ*úýQBÝÈîÂ*èá:º1íEDºÎ1QÌÞøåORXZJãß3PÊðGÖÂ=ÀR;**8>ß¿3><Jö8ÞÍÒõ¾@XFà2Þ
+GMöýýÉÄøEW9äðÆñE=º+>ò×,N=3äý½ø08ÞÞÃ5Æýý9Wà.*Ä´óüý-;¸öHÀýÁÎݾ*´ø+2J7ü94ܹÒ-A²
+ÞæÅ´´ê+F>´Býçë7D5¶Ï-QFÞýÝðýýûWýY/.ÆýýEó.U+*ÁöAöüä.×üijüýí¾¶W¾RöüJ3¾ýU=²ýIó6ñ
+*Ïá,8F¿ÏàQ¸øÞ¾È-Þî0=À*T.ì×<*¶Î.APÖNÞHû²-ù¿0Ú¶Â;¾M=S@ßßWʹEÎJ:=Â1Pö,R2S7È8òÀº
+1ï*0XÚÞËÇFJ7;¼T²Ì½¿úýýãZâå+ý9JÞÚ¾Êè=F*üÝè¿ED:ó;ÚDKùý½»-Ñ*ŸÎFZ>Ræ5éÞ=ÒÕÚÃ1Íë
+Ó5ûïýÍ-ÕîýýX¾F¶üêýýÝ>YüRK°ÎºÝE=Ò/Úý½4âÐ-S2;¶î.6+²úÉ+¾ýÝðî2=À*T.P*À7ÌÊîË3FCK¸
+DãË5ضï2Á/ÅPð¾O²Å8ö5ÍÎó*7P.8Þ-ÈåÐú3ÐÁ,òÜÛV1Zî,XÞý==3É2*:ÂH1,öǼÌQQÒðU¼A:Jýõå
+Í´+Óß+å¸îÀ0VüûKÉÚ¶Ñð1ÈIðȯ¾5G*ýY¶*ý½ó½üÐ<ýõí3àôÒ>Z¾Óæ´,Þ³Nîõý½öÙB¶-Jüý1Ü0+6ö
+ý½Y8NÜCK7Äß½ø4ØHÆÀ2¿-QÎ7Mƶä2Î-·¶Â3K*?,=*ß0QPöä.ÌÊ:ÏÕ¾ð/WÚ¶ÂßÀ¹Ä2+Î×LûÝí+æ-²
+B;¶*9¼XãýÃWRý9¾×ÎJ4ù-B¶+AîýÇÇÂO6X.*öýüç»2;ͽ±Îö/.æý7.*¿÷½¶Þ×8ÚÎ+ÉJÞ3à1Ä°²Þü±
+RîPÖïÇ;¾ý1FZP2Ë÷èRüýE=º+RG·À×=*øý9Û1ûçÙK¸ØÆ?5ÌWJD6¹¸3ð/ý²F:+0ÚFÀE824êÅ+ÖÐ3Øý
+ýóÇéDÙÇ?æ+óÁŹç+C±ÞÑBßQG1íý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöûÍîùIßõ½Àíý.Ýý3¼
+ý-öüQöûÍîùIßõ½Àíý.Ýý3¼ý=úüQöÉÊîùAÞÒPöû9Þ»4ºý=úüQöûÍîùIßõ½Àíý.ùò.+ÓÝ4*îý<Æ@SG
+Â>ÚDßÝèõ-¹¹ÓÁõ½À×;áùIßÒÆïÓÍÎ:YEã3HÈÀõ¹ÀèÓ91,>æ¶Ð,*JD°:°ÖFî¿=¸¶Î+åãîT4¼í3°Ó>º
+ü̼¹ïúP2ÄÝöÝÁ=ºÌ*ÕÐîùÌÎÒ+ø¾->õR5@ÊèGÊABµ.JñKB09ÀX++ÒZ-V?,Õâöî.Á6ä2Z²H/¾Â»G/Ý
+õ./,ï1FJK?ñ»ùASÅPR;Ä0Î:²-++Òìò,Åæ:Á3Ì;JL+ËUWð¸4¼ï3T/ÝױݻßÏ3Ô,Ê3Àó-+CËî6¿L**
+*¾*ç9QßõGKCÑñ+1¾/¯ÀÎù¿3Ðó*ÖÅ,..*ô±ÀO*CËî:¾ZZùÌAÜï3øÑ:Rð¼Ì¸Tô4È×íÜÇ¿4ÙM/RÑÉý.
+ÏÓGÍÑé¼G/Ýõ.GùÓ¸@>¶¿2¹ÛÛSÀ/϶ÅëÏU78HHß5û5*¶P1ñ+=À/.æEÓ//¸ÚÌÀõ¹ÀÙîâúÆïûÍZTâöü
+QB?°FDKöý×,Þ@Æ@SGFEßõI*CÖÞ¿3,Ê1**´¼ý*¶Å+18Rúø7=ÒLÃ<?4òåõ9*AGÞ¸ðÚCñÃ3TÊBµ,74ò
+ù1¾Ñ9ÞHµÜ²çÐYݯÀBÐNÈBµ¼ý*¶F,4=OÇ;ûø7=Ò7E¼.Cðâõ9*QI¾SÐ6÷ïMݼá.WàÂ3?¼ý*¶F,°;á*
+*ïéµÊîù9¾º¶*V»ý*Æè1*
+d
+532 111[1 0 0 1 0 0]sl 8 mask 0 336 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/email_invitation.png b/doc/krfb/email_invitation.png
new file mode 100644
index 00000000..addd3b05
--- /dev/null
+++ b/doc/krfb/email_invitation.png
Binary files differ
diff --git a/doc/krfb/index.docbook b/doc/krfb/index.docbook
new file mode 100644
index 00000000..d8beeab3
--- /dev/null
+++ b/doc/krfb/index.docbook
@@ -0,0 +1,652 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&krfb;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE"> <!-- ONLY If you are writing non-English
+ original documentation, change
+ the language here -->
+
+ <!-- Do not define any other entities; instead, use the entities
+ from entities/general.entities and $LANG/user.entities. -->
+]>
+<!-- Based on kdoctemplate v0.9 January 10 2003 -->
+
+<book lang="&language;">
+
+<!-- This header contains all of the meta-information for the document such
+as Authors, publish date, the abstract, and Keywords -->
+
+<bookinfo>
+<title>The &krfb; Handbook</title>
+
+<authorgroup>
+<author>
+&Brad.Hards;
+&Brad.Hards.mail;
+</author>
+</authorgroup>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+<copyright>
+<year>2003</year>
+<holder>&Brad.Hards;</holder>
+</copyright>
+<!-- Translators: put here the copyright notice of the translation -->
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<!-- Date and version information of the documentation
+Don't forget to include this last date and this last revision number, we
+need them for translation coordination !
+Please respect the format of the date (YYYY-MM-DD) and of the version
+(V.MM.LL), it could be used by automation scripts.
+Do NOT change these in the translation. -->
+
+<date>2003-09-17</date>
+<releaseinfo>1.0.1</releaseinfo>
+
+<!-- Abstract about this handbook -->
+
+<abstract>
+<para>
+&krfb; is a server application that allows you to share your current
+session with a user on another machine, who can use a VNC client to
+view or even control the desktop.
+</para>
+</abstract>
+
+<!-- This is a set of Keywords for indexing by search engines.
+Please at least include KDE, the KDE package it is in, the name
+ of your application, and a few relevant keywords. -->
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdenetwork</keyword>
+<keyword>krfb</keyword>
+<keyword>VNC</keyword>
+<keyword>RFB</keyword>
+<keyword>krdc</keyword>
+<keyword>Desktop Sharing</keyword>
+<keyword>Remote Control</keyword>
+<keyword>Remote Assistance</keyword>
+<keyword>Remote Desktop</keyword>
+</keywordset>
+
+</bookinfo>
+
+<!-- The contents of the documentation begin here. Label
+each chapter so with the id attribute. This is necessary for two reasons: it
+allows you to easily reference the chapter from other chapters of your
+document, and if there is no ID, the name of the generated HTML files will vary
+from time to time making it hard to manage for maintainers and for the CVS
+system. Any chapter labelled (OPTIONAL) may be left out at the author's
+discretion. Other chapters should not be left out in order to maintain a
+consistent documentation style across all KDE apps. -->
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<!-- The introduction chapter contains a brief introduction for the
+application that explains what it does and where to report
+problems. Basically a long version of the abstract. Don't include a
+revision history. (see installation appendix comment) -->
+
+<para>
+&krfb; is a server application that allows you to share your current
+session with a user on another machine, who can use a VNC client to
+view or even control the desktop.
+</para>
+
+<para>
+You would typically use &krfb; with the &kde; VNC client, which is
+&krdc;, since it closely matches the special features of &krfb;.
+</para>
+
+<para>
+&krfb; doesn't require you to start a new X session - it can share
+the current session. This makes it very useful when you want someone
+to help you perform a task.
+</para>
+
+<para>
+Please report any problems or feature requests to the &kde; mailing
+lists or file a bug at <ulink
+url="http://bugs.kde.org">http://bugs.kde.org</ulink>.
+</para>
+</chapter>
+
+<chapter id="what-is-RFB">
+<title>The Remote Frame Buffer protocol</title>
+
+<para>
+This chapter provides a brief description of the Remote Frame Buffer
+protocol used by &krfb; and by other compatible systems. If you are
+already familiar with Remote Frame Buffer, you can safely skip this
+chapter.
+</para>
+
+<para>
+The high level implementation of a system using the Remote Frame
+Buffer protocol is known as Virtual Network Computer, or more often
+just as <acronym>VNC</acronym>.
+</para>
+
+<para>
+Remote Frame Buffer (or <acronym>RFB</acronym> for short) is a simple
+protocol for remote access to graphical user interfaces. It works at
+the frame-buffer level, which roughly corresponds to the rendered
+screen image, which means that it can be applied to all windowing
+systems (including X11, &MacOS; and &Microsoft; &Windows;). Remote
+Frame Buffer applications exist for many platforms, and can often be
+free re-distributed.
+</para>
+
+<para>
+In the Remote Frame Buffer protocol, the application that runs on the
+machine where the user sits (containing the display, keyboard and
+pointer) is called the client. The application that runs on the
+machine where the framebuffer is located (which is running the
+windowing system and applications that the user is remotely
+controlling) is called the server. &krfb; is the &kde; server for the
+Remote Frame Buffer protocol. &krdc; is the &kde; client for the
+Remote Frame Buffer protocol.
+</para>
+
+<para>
+It takes a reasonable amount of network traffic to send an image of
+the framebuffer, so Remote Frame Buffer works best over high
+bandwidth links, such as a local area network. It is still possible to
+use &krfb; over other links, but performance is unlikely to be as good.
+</para>
+
+</chapter>
+
+<chapter id="using-krfb">
+<title>Using &krfb;</title>
+
+<!-- This chapter should tell the user how to use your app. You should use as
+many sections (Chapter, Sect1, Sect3, etc...) as is necessary to fully document
+your application. -->
+
+<para>
+It is very easy to use &krfb; - it has a simple interface, as shown in
+the screenshot below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>Here's a screenshot of &krfb;</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshot.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="screenshot.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; main window</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+When you want to allow someone to access your desktop, you can create
+an personal invitation using the <guibutton>Create Personal
+Invitation...</guibutton> button, which will bring up a window
+containing the information needed to access your desktop. An example
+is shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>Example &krfb; personal invitation</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="personal_invitation.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="personal_invitation.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>Example &krfb; personal invitation</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+To increase security, the invitation is only valid for an
+hour after it is created, and of course the person connecting has to
+have the correct password.
+</para>
+
+<para>
+Since you may want to invite someone to access your desktop by email,
+&krfb; can create invitations as email messages. You can create such
+an invitation using the <guibutton>Invite via Email...</guibutton>
+button on the &krfb; main window. This will usually bring up an email
+message that looks like the following, ready for you to type in the
+email address of the person you are sending the invitation to.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>Example &krfb; email invitation</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="email_invitation.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="email_invitation.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>Example &krfb; email invitation</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<warning>
+<para>
+&krfb; will warn you about the security implications of sending this
+information across an insecure link. You must heed those warnings.
+</para>
+<para>
+If you cannot encrypt the email (or otherwise secure the link),
+sending invitations by email is a very serious security risk, since
+anyone can read the password and address from the email as it passes
+over the network. This means that they can potentially take control of
+your machine.
+</para>
+<para>
+If you cannot encrypt the email message, it may be better to use a
+personal invitation, telephone the person you are giving access to,
+verify the identity of that person, and provide the required
+invitation information that way.
+</para>
+</warning>
+
+<sect1 id="krfb-managing-invitations">
+<title>Managing &krfb; invitations</title>
+
+<para>
+Having created an invitation (either a personal invitation or one that
+was sent by email), &krfb; allows you to manage those invitations. The
+dialog to control these is available using <guibutton>Manage
+Invitations...</guibutton> on the &krfb; main window. If you select
+that button, &krfb; will bring up a window as shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krfb; invitation management</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="invitation_management.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="invitation_management.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; invitation management</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+The invitation management window allows you to create more invitations
+(using the <guibutton>New Personal Invitation...</guibutton> and
+<guibutton>New Email Invitation...</guibutton> buttons, which have the
+same effect as the <guibutton>Create Personal Invitation...</guibutton>
+and <guibutton>Invite via Email...</guibutton> buttons on the &krfb; main
+window.
+</para>
+
+<para>
+The invitation managment window also allows you to delete existing
+invitations. To just delete one of the invitations, select it with the
+mouse or keyboard tabs (it should become highlighted), and then select
+the <guibutton>Delete</guibutton>. To delete all invitations, just
+select the <guibutton>Delete All</guibutton> button.
+</para>
+
+<!-- OK, so this is obvious, I only did it for completeness -->
+<para>
+Selecting <guibutton>Close</guibutton> closes this dialog.
+</para>
+
+</sect1>
+
+<sect1 id="krfb-configuration">
+<title>Configuring &krfb;</title>
+<para>
+In addition to the main &krfb; interface shown and described above, you can also
+control &krfb; using its control module, which you can access using
+the normal &kde; control center, and you can also access using the
+<guibutton>Configure...</guibutton> on the &krfb; main window. The &krfb;
+configuration is controlled using a tabbed window, as shown in the
+screenshot below:
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krfb; Configuration (Access Tab)</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="configuration_access.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="configuration_access.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; Configuration (Access Tab)</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+The <guilabel>Access</guilabel> tab allows you configure settings
+related to access to the &krfb; server.
+</para>
+
+<para>
+The <guibutton>Create and Manage Invitations...</guibutton> takes you to
+the <link linkend="krfb-managing-invitations">&krfb; invitation management window</link>,
+which was described previously.
+</para>
+
+<para>
+The <guilabel>Announce service on the network</guilabel> checkbox
+controls whether &krfb; announces invitations over the network using
+Service Location Protocol. This is normally a good idea, but only
+works really well with a Service Location Protocol aware client, such
+as &krdc;.
+</para>
+
+<para>
+The <guilabel>Allow uninvited connections</guilabel> checkbox controls
+whether &krfb; allows connection without an invitation. If uninvited
+connections are allowed, then you should probably specify a
+password. You can also use the checkboxes here to choose whether you
+have to confirm the connection before it proceeds, and whether the
+person connecting can control the desktop, or only view.
+</para>
+
+<para>
+If the machine is a workstation, and you choose to allow uninvited
+connections, you probably want to select the <guilabel>Confirm
+uninvited connections before accepting</guilabel>. Conversely, if the
+machine is a server and you are using &krfb; for remote
+administration, you probably want to deselect <guilabel>Confirm
+uninvited connections before accepting</guilabel>.
+</para>
+
+<note>
+<para>
+&krfb; uses the normal RFB password system, which does not transfer
+your password in the clear across the network. Instead, it uses a
+challenge-response system. This is reasonably secure, as long as the
+password is securely guarded.
+</para>
+</note>
+
+<para>
+&krfb; allows you to control whether the background image is passed to
+the client, or not. This is controlled using a checkbox in the
+<guilabel>Session</guilabel> tab, as shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krfb; Configuration (Session Tab)</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="configuration_session.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="configuration_session.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; Configuration (Session Tab)</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+If you check the box, &krfb; will not transfer the background
+image. If you leave it blank, it is up to the client whether the
+background image is transferred or not transferred.
+</para>
+
+<para>
+The <guilabel>Network</guilabel> tab allows control over the port that
+&krfb; uses, as shown below.
+</para>
+
+<para>
+<screenshot>
+<screeninfo>&krfb; Configuration (Network Tab)</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="configuration_network.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="configuration_network.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; Configuration (Network Tab)</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+If you select the <guilabel>Assign port automatically</guilabel>
+checkbox, then &krfb; will locate a suitable port, and invitations
+will match this port. If you deselect the <guilabel>Assign port
+automatically</guilabel> checkbox, you can specify a particular
+port. Specifying a particular port may be useful if you are using
+port-forwarding on the firewall. Note that if Service Location
+Protocol is turned on, this will automatically deal with identifying
+the correct port.
+</para>
+
+</sect1>
+
+<sect1 id="krfb-connection">
+<title>What happens when someone connects to &krfb;</title>
+
+<para>
+When someone connects to &krfb; on your machine, you will get a pop-up
+notification that looks like the following screenshot, unless you are
+accepting uninvited connections without warning.
+</para>
+<para>
+<screenshot>
+<screeninfo>&krfb; Connection Window</screeninfo>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="connection.png" format="PNG"/>
+ </imageobject>
+ <imageobject>
+ <imagedata fileref="connection.eps" format="EPS"/>
+ </imageobject>
+ <textobject>
+ <phrase>&krfb; Connection Window</phrase>
+ </textobject>
+ </mediaobject>
+</screenshot>
+</para>
+
+<para>
+If you <guibutton>Accept Connection</guibutton>, the client can
+proceed to authenticate (which requires the correct password for a
+personal invitation or email invitation). If you <guibutton>Refuse
+Connection</guibutton>, then the attempt to connect will be terminated.
+</para>
+
+<para>
+The <guilabel>Allow remote user to control keyboard and
+mouse</guilabel> checkbox determines whether this client can only
+observe, or can take control of your machine.
+</para>
+
+<para>
+If the client connection is successful, and used the password from a
+personal invitation or email invitation, then that invitation is
+deleted and cannot be used again. You will also get a small pop-up
+window in the dock, that shows that the connection has been made.
+</para>
+
+</sect1>
+</chapter>
+
+<chapter id="dcop">
+<title>Developer's Guide to &krfb;</title>
+
+<para>
+&krfb; supports a small number of &DCOP; commands, which are described
+in this chapter. If you aren't familiar with &DCOP;, then you don't
+need to worry about this. However if you'd like to automate some of
+your &krfb; (or other &kde; application) actions, &DCOP; is a useful
+tool. You can find out more about &DCOP; in its on-line documentation,
+and in tutorials on <ulink
+url="http://developer.kde.org">http://developer.kde.org</ulink>.
+</para>
+
+<para>
+You can shut down the &krfb; application using the quit command, as
+shown in this example:
+</para>
+
+<informalexample>
+<screen>
+<prompt>&percnt;</prompt>dcop krfb-1507 MainApplication-Interface quit
+</screen>
+</informalexample>
+
+<note>
+<para>
+You will need to change the <userinput>krfb-1507</userinput> in the
+example to match the instance of &krfb; that you actually want to
+shutdown. If you run <command>dcop</command> with no options, you will
+get a list of all applications that are running and &DCOP; can
+control.
+</para>
+</note>
+
+</chapter>
+
+<chapter id="faq">
+<title>Questions and Answers</title>
+
+<!-- (OPTIONAL but recommended) This chapter should include all of the silly
+(and not-so-silly) newbie questions that fill up your mailbox. This chapter
+should be reserved for BRIEF questions and answers! If one question uses more
+than a page or so then it should probably be part of the
+"Using this Application" chapter instead. You should use links to
+cross-reference questions to the parts of your documentation that answer them.
+This is also a great place to provide pointers to other FAQ's if your users
+must do some complicated configuration on other programs in order for your
+application work. -->
+
+
+&reporting.bugs;
+&updating.documentation;
+
+<!-- Needs some content.
+<qandaset id="faqlist">
+<qandaentry>
+<question>
+<para>A question </para>
+</question>
+<answer>
+<para>and an answer.</para>
+</answer>
+</qandaentry>
+</qandaset>
+-->
+</chapter>
+
+<chapter id="credits">
+
+<!-- Include credits for the programmers, documentation writers, and
+contributors here. The license for your software should then be included below
+the credits with a reference to the appropriate license file included in the KDE
+distribution. -->
+
+<title>Credits and License</title>
+
+<para>
+&krfb;
+</para>
+<para>
+Program copyright 2002 Tim Jansen <email>tim@tjansen.de</email>
+</para>
+<para>
+Contributors:
+<itemizedlist>
+<listitem><para>Ian Reinhart Geiser <email>geiseri@kde.org</email></para>
+</listitem>
+</itemizedlist>
+</para>
+
+<para>
+Documentation Copyright &copy; 2003 &Brad.Hards; &Brad.Hards.mail;
+</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL; <!-- FDL: do not remove -->
+
+&underGPL; <!-- GPL License -->
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-krfb">
+<title>How to obtain &krfb;</title>
+
+<!-- This first entity contains boiler plate for applications that are
+part of KDE CVS. You should remove it if you are releasing your
+application -->
+
+&install.intro.documentation;
+
+</sect1>
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+<!-- This entity contains the boilerplate text for standard -->
+<!-- compilation instructions. If your application requires any -->
+<!-- special handling, remove it, and replace with your own text. -->
+
+&install.compile.documentation;
+
+</sect1>
+
+</appendix>
+
+&documentation.index;
+</book>
+
+<!--
+Local Variables:
+mode: xml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+vim:tabstop=2:shiftwidth=2:expandtab
+-->
diff --git a/doc/krfb/invitation_management.eps b/doc/krfb/invitation_management.eps
new file mode 100644
index 00000000..2c258a9d
--- /dev/null
+++ b/doc/krfb/invitation_management.eps
@@ -0,0 +1,292 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 596 253
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sun Sep 21 20:40:17 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 567]ST
+B P1
+NB
+W BC
+/mask 6375 string uc
+î½*¾î1:E*¾1=/·7*¾ÍÌ<Þ¾ú>+¾QQ3*J°ÅÆÍ;°=,ö;ß=Oµ.îJÀQtì2J¾6Í>ÝO+Þ¹Ð.öµSÈ:½*ìÇ<Þ
+ÅWSàÌ<K*ºöü?ïû1ìù½ãõÍ¿íý?ÝýU¼ý6úüY÷ûÝðùåÞYüÈ
+d
+/sl 50660 string uc
+î½¼**ûý5*N¶S9L3HÊRÌ+úDÐÒùÇY0º´êÅ,*@ÕZêñ½ÄTPÑí.HîÕý/*òêêûùñåIËNóñ7âDÈÔ½ôÖWÝ6*
+ÞÝîùéÝý1Îð×TÄ0æûâA-HEô¯*BÙòùÅü±LC.*åÌíÍùééÅèè<Z·/áHI63öÝ<O,9öé.*Þ¾õQ¼³<*:ºRÛ
+-*Z=5?²·7ÞÚùùÕëD-P³²5Æ××-<ßòEÐ>×áäÞìÆ´ýû±ÏP,*Â4ãºÕ*J:ìÈÃ4øÚ0ZõüÖϰ˿ѸØ/îYöû
+ùÝ»¯È<Dé5*ÞÃŵIÝ»ôßQFJ×°ãÚIöÐ×*¾ÑÐÕêݽ*OÈN<Mü5*:Ô´O±/W¼-R´ýôKÑH4ÒÔȾÙð=ÔçI-*
+ZGY4J×ñùõÍÖNSÂU»**Üõóôöã±ÅüFGQ»õÃ2JÓÝù9HD5*¶TøøûEðLÃܯÜį·0³³UDUæñEZîùEWøâÍ
+Øè+XÉÍIQÝÜüùðß¹åì×Q½ìÚ÷ñ3Ñ*äÛÝÝñ»¯È²ýVÒ/*ü´´ñîõá1YGHY8FôØ1N*ÏÈíÍ?æV*ÞSçÙõ²à·
+äÈ÷°éè÷¹TÒÓW1T¾ýÝ14òñýýÑ¿ÁE2¶è.ï+À²é=6B3Æé;ÀÃ3ýôÁÍÕà;NÚ¸*Êé»ýüøYÙLÄYÇË2Zõü²»
+H»ïQGã*åãåYQÕX¼ýúõíùøð×IYÌú³Ï3ѾõìÝýÜýûù-1O9*¾õë7Gºúùé¹ÅâÃ=ÝWõÃ3Öûû¿øÊ/*F?»3
++Ô4.061±1=UäÌØØóJ/ú½ýè¸PýýIβðîâ+ìD+,,òýýƵ¶:<**ãò2;2Ô5õýWYÕà;NÚ°*OýýENæ4G¼Å
+ÚÌ;å8Jçû+áNøà͸È+Äú*¾+¾UðñÃÉIÝü¼½û÷GüøíÇAIÜBãÂSÞãôíí¶Úüû1-Ï/*üÒZîäÝÝÕ9IÍÕì¹ö
+ïÀÅHJ+èÝýä²*ÞSç±/6åKG²ö7K²ìëPËÓW1T*ýí¼SðøJEÚçGýúÇÁ-òýäøYíÚ÷*-ºéöûýýÝ·=SM̽º×
+³/î½6ðú¾üÔíñäÞéá-P*8âKO¾ýýIò´ûøµ-»ñ2ôö»ýùÍ7âJÁKû͹Ü-жÎ-¯,<öúïÁM¹ZÅïÅí¸ÚJóýÕ
+Æ;PÂ+,*ò;ÈÖýýÍéVèRÞ¶K>¹2*NÙý´ÇH9öSQ?ÞÆYÇÐ**îÆúÒêïÞÅÉÅIÐÆë½1üù¶×6ÍϾ**î>/.**=
+/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×R¾øõÅC7*¶TøÞZ8ê.0>¼MŵýãÝ°³é¿4ÞýREùêFÒ
+å¯X7ôIüµBýéýûüáI0ñ+0Îï´E/ö¯ÊÝÃ1¹VEß×ÀCã÷+ÔùIßÈâåÈTá>¾*GÇ-*>D¾õüÅ3ïýà=ÍF,ìýÝÉ
+ÒKÕ0°Fß²üÕ9è+SFæ+ûý½?úGA/IFØ-úRñ½è2Á5²°ýýÝAÌ÷:OêýýòVÛ¿Æ=L×-*Þâûó°Ò÷ÞÒ¸T+Jå:I
+0**üùó+*¶N¼ÜñöêÚÛÕµýÜÙYíGõêá¹PºÃ,ÁÍAS:L*ì1S,*µR¾¹õííTûøF9**·òGúÃ×ÄØÐé4W@4Öûû
+YÔÊ/*à>GõØJ´¶+ÐáEòͺ;UÙ8Ò*üµ1IîÌß°úñðçýý9T×Ø00ÛÞ*ÐíUÛÆ+X·ýýýýñÉDURÁ6IÞCï=¼Æý
+×µ1HîR8à*¶ûýÇøûYýéÃMæÈêýÑÜç¯ã+ú½Î7¿.PñýøÀ8Æà¿8æýý;ù´âY¾¿æýýä8VÛý÷UËAæ¹õýÝÊð
+GÈÂ86@Ú*Ä5L×-*ÖäûáLºÎQWÃ*K¼ôEù÷óå4¿ôEðñ7¿ÑÌ»Ûú÷òûº÷é±Ñ0ÜïßM>¶ZÁCÏ*î6ÑNïáåº**
+ôYöíÙYÆ<è0HúIÄÃ,Ò½½Çç*¾ÑÐÕéÚÍW²êîCÐFO8UAUÙ8Ò*üÍÂýº°ß°Nû½ÐÏ*Jð¿ÔÛÞ*Ø9Sâ¯*ËYA:
+J¾0Ë?ÚÎF1NгûY¿5Ðö*-8ãK6JÊÐËúÝHÏ5TÎ<µPüÍú߯Ä2ÂèÄB+-ù½±L1NïJ1îýýZï½EC:÷ýÝ×-¹÷
+ýºKã=RZÜýö»ÛMÇMOC7¶K>¹**²ÚýëÀ=YÎTMCÞÆ9K1Y¼Â9J½N¼üíµçÔϽ1IÜÁý8Û³çÝý;ºÁ,A¶2*ÚM
++¿üûÆÞÓ>îÊöïÙðìÛã-*¾ñ¶-Í»ºÏ¿Lå7Úòµ/5/êûü?Iä,*ÌÈH¶ùPÓSÐÂA¿0É<Á9Wë¸Î,öIÎïÇ5/ɯ
+ûýÃûýYçá-T¹¿+>ûÛÞï+ø¯³ù,Q¸WU¶L·øÏéýú¿ÔD9J÷ý½¸¿/;++,Võ½À³ýéOòÀ4òáÐ@ýÕíòèJúýýÚ
+àÄõIXÓ-жÎ-øýýOíCA6¼:ÕýÝ×-Ùù½íLß/VÔäÆZýýHò8OÄ?8Kâ/Ïê+*ZòüQ3ĺ¿T7ã*O¼¾»Ò:º½O¼
+X8öçÔ͵õüÛ¹íüÚóçÞã3ºÁ,QP÷8/â÷ôM,ÕоWÌIíTøò=9*¾Ô¹Eºôìæ-2Ö¹òÜÃÝ/@ZϾCíÝ>áV*¾Rç
+ñ@4*Ü/*R¿6CX²³é¿4Þý¾BÌ°?VÄ3ÝÕêD½Ý39Rë/0¶×óõüÅ92MÇRBûûìíË?FÏFãæïºúÊÞË1ÖÞóý1¼¾
+/@¶ûý1Á1³¯ùõA±àóÀ4>M÷úáENòèJúýýýóùÔARÜ-жÎ-K/.ô½Çß1ÎïËGÞüùQ½Ð?Ú¾óõõýüóZË8ZàÏ
+ÎÚÀÆ5L×-*JíûU4úö,ÓDÈ+8Æ9³ý8üC÷÷°ßWá¼¼GùôÌûøñÖW1YôYP>¶îÊÅR¾Q¼ýìÈ·ñ:9*¾ýM2ºõêß
+Ñ-îïÝMAHºKWý4/êðüÂ=ä,*ÊèßRÒT×T?*Z2JÜë¸ß,*R;¾-ÈæêÎTÖ.ãD32,¶¾+HÒÛ¾ýW9ÈB;DÚÃÆÎO
+AÊî¾*ZLÂÅ0?V9Jå2+¸;1;++,ÖÎ@L´Gο·,ÉNR<µå2à++,²Æ>¹3=H:Ã-G2QJï*»5¼*/9êKVÏ/<òè
+Þï,GNRÎLÓøÛMÇ°ÃE2·L>E2*ëB½½·+EÜJÓÄ°¾=ð-*ÌÇÍã,ÌÙô-RÒ**î0H5ÝÚRJÖµ½Ä,6иóÔÂ5íö,
+*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2¯<*îÒHøùBZæÆ>ÛPÈKM¼NCòÔVÒîÀ+²úûù·ùø>×=êV2à>ñÚÀË2Ã
+Èë9¸Ù¾6ÍÎTMGÞ+÷3**ÌÇͳ,HÚõ=¼²àÁµÐA?4ÖóêÙ½½?/Úï÷ñÔºùÔö,*FÜRL½1IHGºPIÛçRÅÔ;º¾,
+0º½×/=+*SMæG*¾@×RßÇ-JNÀ.ÃÐAÙ=êÊ2.:JÇXßHÂÀ9Xíͱھ3ÍÎTMGÞ·÷÷KÞ¿5Y8»¸ó¼ûù÷éÉ=Iò
+Xä>Î4Ã5µ?¹ô=Þô0ÛOø0>°ïÍC鸺2ç¸åî+4JØûç½6öë**5ìý·Ö¸,9HKÓĸ*ZöØúÚ8îÞÁ9µìú·Å¼û¹
+÷ëÙéSA±4RÂٰܻëø7¯ù¾ñJïKM0ËEæ1ÑXï*HK+NÛý÷Â*I7*îÕûùýÉ@ÍÞEÑèS2¶W¼ÀÈʱ*:¼äãÊ,ÚZ
+ôêÔNÉHö.*ÚÞ½ôÌïL/4?âóÊÛ½,ÍξT.ÜI¸Èü¼6*¶¾ÁùõACÊÚ¼µøÒÓ8îÚúRGWË8+¾æTA³4ÞèÓW·ôúT
+×ûÝ»ôâ¿=åìHöõè.BMìûù±8Çíµ+*A2ÓøüûOôà0îGñÑÔоáðMû仹.*°XÝÒS*KÁ´O×ðÒöëù?Äݽ½ôÆ
+7ÁÕý.*æXâÕÁÇÖ±ºÜÔÓÙÕYTÐèäI¾ÉAÙ°ê¶XýÄù@@PÜý¹+*íV±@¯êæãÙAÕR³±:ÞáÅW3@FC=¶»öæCFÈ
+ÒYÜÀÕìQ,*ÜÓPÃã¾?Ù@ÌÖÑõ;Æ<ñ=VÖ?ݼÙO0*Ì7E³åÈ35ô5öøN*Þ½û,@êB±çÔ+´×·TI5¿öó¹P3Ö½ý
+*ÖàUºýÑúüQ÷ûÍðùIãõ½Èíý>ݽ0ºýÑúüQ÷ûÍðùIãżȾèá.*öÝIQS.Ý×½**;Gî87Ó8JNÁÌÐòõ¼ý**
+6¹ÞGDTG*á0GOïÍýü+*Nô¾¹²Ò¸*ÇG¸È7**J°1ÚMM×-ÆêÏÑ.ûõ9**°1ÚMMÓ-¾úÅP3Lü9Ñ.;¹ò>¿ü**
+ÃEÆ-+4¶Ê.FF*Þûè,*¾áHÖÃ2¾Æó8*J+ÞG6Fé¾îÅÃÓMU¯LÉÌÐÕYÝHV4MÍÉAâCÅ.Æøï1+ì6**Æß,@M+
+JðÚáÛYÚо=.×B·ò@P*Ç7¸ÈØIºöîë27.B,î*î¯RÛQ+:²GºöZ>R¾1H*+*Þ+1JíË>Î*îà2@â*JðÚ-¾½
+Z4XÎÌõÓÓN19¾Cò:âî:¿J2*ýÂIFº*ÞOWÖ¾T¶.9Þß0×ô/:ûÁ3W¸È¶ûøó2À1,6+¶*ñÊ=,Öºè<¼û¸åä9
+QÖ¼Û*Öü¾-Z>µâÕ-¶¯.5°*:·ì+ÔÅD¸**ÔMý<;ÒÏ¿+8ÉôÁ´Ô-Ö8¾Ê±üÄIú¸*ÎÆóÔûU>Ú8*Gç/ô/:ûÁ
+3J°ÅÌ<H½üFÀì¿*M**JVJ>Í0JÜúç¹å,-.¾ñ1FJBâXEÖÓ*¾Çñ8ñ7=*²µÀJÁ9äú÷ì/À.,/ìöÁI1J¿ôñ
+åYUJå+B½À/ëÚ*Z°ÅÌ<<QÌÚÊÉ¿*M*2*·ð5Ü8.BOíöÀ86:*ÆIî*UJE÷ïß+Ù4*R·ì¯Ü¯,¾NÜ:ò*I³1
+K,ï*µúßÇ-ÎîD·ðÔ?>/ê5Jø1=Jã8GOI¼ú¶Î9FÐüëC5G¼F¾ïáO÷FÅ¿ÉHüJììØ@*éá3μÞ+ÄÄJèÔà0´
+?**ñÚáºM.*ܼÀ×2ÇÁ9¹ØQK,¿,µúßÅ-æNʳéÞD74V6Jø1=*ä8Gã÷¯Q4Å,,@.;Qì+:Qü¸>//¶¶ðüH
+¾ÞóQ8ðàÈÀ-8Zµ»¶BB´ZU+*EéG*¾,*½Ô:·Ö=L=1JQNƲD·*;*¾ú7½¶ð*RYÈJ¿ÑèÁ,5-Æüß.Ö×MQ3Ì
+Q-*ÞºHGÖ/8¾ÍÅí+*êØI¸æ?Þå8GOÖ¼ý**M9Ý-*æ*3ç@ó*M¹ò>EJLÞç8¶ÅÄT1¶ØMQ×+L8Ðô¾¹²Ò¸¾Ò
+7¸ÈÀýöû-*Jë+õæRó*REò>/öÒù1*JØ-ìÏÏè+Æ´æS4*ýÝã*¾ýG¸ÈÝ<*.¾ýû·ÈíÐ>Á*øñ>+¾6¶Mê9ì*
+Òè,õæ9ó:*üõåSøýûå¿*ïÌýýXØ5-ÊàýýíÊ=ô,+ÄÔýýÝ+Z4EY¶Þ¿;ÚÆ,E¯¾Ãȱ-AF*óXSZÂæ÷¾-A¶×
+-*Þ*J·Ç-*ZJÅÔý+B45*ûP.X*ß8ìï,8ð¼ÅÄ<¾üFO1¾¯9ñäûÊúíéFOÍíÙýýݶüØñÉËïýýø¯ä+*AFîÞ
+2XN¿ßíýAÁ׿58¾>òàI¹ßº*ÞYA¶×-+*àÔÒ-,õýå5ëïI*4ççýý±ä@Ôð8Aî:õ¹²=+2*ýùñ>û½@ÇýýÕú
+ã½Qîüý÷HüѼ?°-èAÌ˽Gï¾-M¶;/ôý½ë°02X¶*L×øIéYÏK+*1XZ4**F¯/úýýûAãͶÀÕ½îI*¯<ýE0ä
+0-Ôð81î:÷¹²>+òåS<*0î1ðù-ý·ö²½<¿ý°;æ¶7KDë3ÄGï¾-M¶;/òBÓGÓ³AF*?9îðÌý÷¾-A¶×-ôýý¼
+35ÉÀ=F¸´Äü-Þ5å½Ï´öêà/:êßG8ÞKðõæP,:*üõåSÈý1,ßú».ý˼*,R-ý½2øýýMÙ¸ýÞµÞýE8¶Z.AÐÞ
+æÅ´´ê*ß/1B±=5ÜB,ºï,A¹¾üí@ÐDßý-**JÂTäýU*û³âXáýêà/:êßG8ÞKðõæR,æÍÑâ*ÜJüýý°BñC¼T
+9÷·üýA¼TÒê¿ëÛï¾-M¶=/μÊë34æý7ìýÕûß+2G¶¿-/¶À/,Tá=,½Ï4.8ͼÃ+U·ÅÀÁêíÏË.J*úíÍÑòý
+Qø·HGÏÈý»½ü²5RûÈí½øÍæñ=Ý+ýí¶Þ¿;ÚÆ,ɽ½æ<-.AF*·âºíÒÆ÷¾-A¶×->ù+õµFïJ?Æ°*×5-X*ß8
+ìÏî:ù¹²>+òåS¸*üç»2;ÍýýÚ¿*:BýEUFø?*îL+ì¶*öYM¶;/@¿¾=ÂŽXÞ¿ôÃÀ¾ÉáýÁ68ÚÞ¿,F;-øÍ;
+Î5V4JB+¶Þ*Ñ*AúZ1õR¶ÆûGØÇ*.¾ýû·ÈõPMÜîÍÄ-@Ç+Wî8Ëá6û·ÈåòXRݸõEAGZÇ6æ,õæEä:*üõåS
+¼+¼éK@çÍÑ.*,ÅIã+¾ýû·È¾üÝ.¾õ½ÈUûÃõ½ÈUûÃÄHOß¼õT¼²+¿æ,õæ:±¶³Èº×RºÌôêÏÑU*¾õê×á3H
+ÏɱEOAíëÁ1ðäÑÍêÕB¾òF»øßöø?1V>OÍHË@*Þ6å/1FBÀ5SZ<0Òö¶´îÀ-ÎV>/ÀêF9¶î,Q¸*æI2Â5Ôî
+òç-**Þ¾7O3»*Áà,*ÜÇ¿ÓÚáËVâú8ûN*ÅP3²ÄÞ/+òÊS8+*ËÇËÐEÒÁEõG7çÉïJÓÝÑÄH»@ïG²õTYÎQ½G
+ûøÕ58¶Z.AÐÞØɽ¹ßêÞ/1Þ3°ÙYÓZHîÞ/Ìò*ν:2AV;´åçÑͼñ0ø*PÖíòçÁCCÖȹÚÇÃÊíÏÍ.îä>¿Ú5
+¾éGÑ>¹âã³ÈHOZøò/±KÍ*µQIG1øÉQË0@Ì˽Gï¾-M¶;/ÄH»æY02X¶*DKȼçW1<öÞ/P+È-0+8Öà/Qöó
+1æ-îÃ;»Ý:êZÞTìï*8<½Å¼<¾=Ñ.>Áì6ØÖÕóµ7*Îé¯ÏÃÊ*ÈSËÐBæHÆí9ÎÕï<ùÄ.æÉÀC-´NQ°QBôÂ-8
+6ßÃ>ÖÝÎîêHJ¶ß+Rö¾?ðäÍ1F¶¿=G¶ß·1Vï¾=Úõ±Áæ-JÙ0»S´õâÞTìïP@°¼Å¼<¾5Ñ.Æ-ìÖOöòéù½Iº
+Õ**ñüËùõîêWÄ˾àAQ3%%
+d
+596 85[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6375 string uc
+*ÞÌ**HºýÈöü-õûÝðùåÞõýÈíý?ݽ0¼ýÕúüY÷û±îù½ãõýÈíäÈ*
+d
+/sl 50660 string uc
+äÕí´ê9+¾ÑLýJ¹øòÙIîò½¸îÏ1-ø¾Îµøò;É4;Jòç-2-¶à+1ÄîL4ʸâß¼7:Úî*6ÔïÝ4Cå-8ÚÞÇ8Ú2Â-
+ÔBÌî**¾2ÃE»*ð70æ°äÍ6VãGìâLäõæQ,*ý9ÒÊéòáåØîß½IQôûÕí?*îâ¯ÕÌG¹´íìÆ9IDY/гñéÕÓÑÑ
+ÒV*ÑQY¶É+7ýN1¸EòçáüÒ²µ°ôÄ-86ßË>Æó102X¶*°æÌH²11LòÞ/8>ÞÃë?UÚîÂ-=JéðÞU6ÖȹÚÇÃÊí
+ÏÍ.îà>ûØLÃàÜÌÛø³4ÍÝûX+*YíºóåÒ±Ý8É4?Ø@ÑSøŸý8ò;»÷HHé,MÍÜMÍÜ.°úPäì¶Þ¿;ÚÆ,ñ8Üä
+;-.AF*³Êêü=Æ÷¾-A¶Ð-AFÕVï.-è.ÆZ*Î5-Ôð8µ°;ñ¹ò=+ÚBO1ÓS=àÕS±0ËØåÝÝW*ÞãùܺCóéÂEÇP
+ÎX/<³ñéõëSø÷íF¾JµH»0T¾1ÇòÁó7û4*¶Ï*µFîÞ2XN¿K:Î.í÷Í5¶Î³Ë:ÖðAû:Á-A¶Î*Q,+æAU6*Â/
+Ê*FZ¾S¾ÓÚá¿VNü8ûN*APçUÓT³AôäÃCÉòòÙÃ>Gü÷ß=Aì6ùìæÇU¿Nëúø»¹Èå*JÖÂÍÄ-@Ç+Wî8»á6AP
+ç½ûø³Óòá½9ÑîîóÝ+*+¾ï:ÞÏ1FÀ+.*Þ*8SYáÞW>ÚK¾±=*ÚÍRJJÞ·YFÈ*99üµá½9ÉÜD¸<çSÈãÑì+åO
+1V<-ØÞGDÅCXÊÐQÉ@ëPæÁó<@Ù×IÐ+,Þ+ôJ¾é±2=Jæµì¶á+*¼8ÚRº¶E2µJîú@쯾µå¼E¯ÛÁKí6GÇò>
+Iá/ÚBOYÕXÛÄÌ-á?Ê°I1ý0*2*°Ç,+Fúè»4*K5Ü4ì5üÑPî5ùSB2-:å7F¶+19*H0î×K¶ÞßÁ1XèÀ/ÒÀ¹
+ö*8´Æ/HºBN¿,º1ÎZØ**Ê3öKGßÁݲ@º*+**¼÷ÞJ**¶Õ0Q-ÞÑIÍWBìÞÌEÄÌúÜÑ.+-*ÖñÞµ-Ù8Xû7*¶
+*ÛYöÒ8¾øKü9·ÒOÒ-5æF<K+GJ¼/-RÛøèá*G¶+Ô8¾Úðè9¶8?îÍÆïÌëU>ó*úÚæ/ÒUíÚÚ¯ÆÎÎÎØ**Ê3R
+ÇÆÒá8<»¶J¾Þ79**õèî²*ööîÔ-Ý7ê÷îNLÑ.ýIP*FRCåÆ;M¹Ì+Ù/ÊRùHü0*F¾ïC¯Ú0ZF,1**¾*ÞQLÐ
+4»×¯ùåëÀ+.*ÞÁOË9ÚJ>¿9I8R?æáÅQîÔÅ9IW߶*àÁ.V¶ÐóÈ/6Ê76:**±,æD-é-×71Fñ+*îV7ì,*÷ô
+˸ʶ´A¸ðùÌÐãÊCÑØBOù¹øôíµ*Þ+úü»ÆA*ÝA¾ÓÁ+Üö.°JßÃÙßù/6Â*F¶¿S,ý»ùHAݽUBë¼ý8Wßî¾
+,öG3ÙÙÊ5,8*ß++BL5ÝòG5:*¼Ið++JßÓDÚ/*á»ùòݯÈJWòâõGOÉD³èY¯ÈäÕ²êÙí*¾-Ö»Ùî/*´½ð´*
+áÔºù´6BVóÒ:DG¿¿2¾5AB6îÄ+MÖ»Ù<YÍDZXÈ01F¾ë1âÔô4¾ñ-RR:B/J9źS8:.BÝî+N¾÷´*ÈXÓßW*
+ÖêÖOù7Ñ.ÝLQ¼íS¸Ú¶ïØ,»BìÝ,DôM***à*ºH*ÖAµÐÎÞ0Å7ù<LÖUY¶ö00.´êAXZBÞUFÎ0E´âÄ::/È<
+98¶*Öí·öîè8E1ÎÎJZ4¾HMæÕFJNS¶ß-PÈ,ê*ÌÓßÏ,Ú3*77BìßÎUÃÌúÜÑòÅ9IÔÒ¸ðÝ¿ÍËð8?*XÝþ6
+8Þ±á@»öíâÓ?ÃÌúÜÑòÒQÍÔÒCòàÁã±éÇÇ:*¶**ÙãQÕW¹óâÅK¹ÔÂPÍPûùÌÐ=I¼FØÖÒIM@ËQíÔ4ñÂ,õû
+¹ôåÍSÅÃÄÄ9QüùÌÐŵí¼íOÈSÍPÍKñì?*îÝ×=åWÙ±P»Ç»ùï½íS¸öüûùA·RçâÐØó´+*ôW»úæV5Ç6E+î
+½íS4ôô-ìÒîµ»ÒRÇÐU*ÞÓRÏÐåEßüû5ýôY+ðÑGOß8½æW53*Þ½PÈÝô>/¹ù»NÈÞüGS¼ÙÑÂêßÕÅͽíºÐï
+ÅÝHúÁÐúµQ×ã<îûÍðùIãÁ7öüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·Óݶð°ô»»úøÙ1*2üøíÝîS¸
+õîçÕõýüùñÀºöX+*õËùµìÝÁ±õü+ÍÐQ9HúõñôûéQóëÕí?*îîÇáXG¹ôêÜ+øý¹ÈÌù³ëÕ±ÉåÝIëÝI½Õ*¾
+ÝI½ú²æÔ±ÝPÕÉYý»QçEá´û·òõø÷Éüúù/*úüöÙCµ¼7Ù°CëúõGOýùøñáÌÓÙÕYòíí@*îøóÑ9X÷ïåÕçíÞ
+ÝY/<³ñéõ½GOQÙØ°ßÈËÉYYîåí@*îöéÉ98×ZãÓ3ëÚÙ½íSøÑÐ5õ³êÙùõÀüúá*:*:8,FÅ¿,¾Ù?¶Z/AY¾
+*ʵûÂ,,,̵à³3¶øíÛõ´Úµ¯çòòWëÝõ>ûOæÀñ8Ô»»¹*ýD*2*1Ó-+ÌáÞ÷@6BÚå¿E:ÆÀ7XɾµÝXùîÚÀK
+Ýú½Qç?¸86ÕîêäÏ-óÕ+¾*2¹>2ÞÓ½ÜÑ*ÆÕèÑZÚèGWÐ8BÍÀ¿5Iîö:2¹1KAóèë5/4æË4Ò5.,¶*.CÚÐHR
+PóR»<Õè+.**ó/.-+8¾´R5@ÆØWG*BKÆ/*F¸ñ×/á7ê×àý¹ÈHÎñÞµ-Ù8¼F>½7*8*ô5EÉ-RXÆõ-̲ÇðÜ
+ú/ÅNR6íQYßÞ*JB¾=Ú6JJã0ÈAµÇ¿5Ì:F:,Z¶:.Þá³XÎúMMãS5:â9ÒÞ3:öð*F3¹Û¸ARÐF¾3ÀV*ÞÅ1µ
+GöëÞÌ-üÝÑòõF-´5B³íÜßõé*î*¼Ùº¿D¾í2,:»*ì:7GãÓÄVïÁô+6Úö¿Ç*³î¹µÏñÆÓ/Ò5Ê.*íÏ*RD-ç
+*ÓGR¿EJîÝ+NBûùõå48.K@*îÜ»Ñ0ê@ZåõüíS,´¯Èöõ²ìÚ¹Ý0*1ιµKÛ*òÜ*Ò80îß<Ä÷ú0ìÄÀÀ¾¿5I
+2,Î0¸MØäÔðؾ¸¿-?2,ZÂ<¼×ùJ/ì*¿õ0JKòìÛIU¶;.Ç.¾ïä»øíBOÞôýÌ<25ÑÒ¼OáÌ»¹ôá*î*°å0º+
+¾êÍÚ7ÝÔ=åÌ7-0Eûò-PQZîà1À*Ú¿ê0àÌì4*ñÖ¾¸¿-?2,J8ŶS8:.>Ý*,QJ·´*Ǹ³äV´O¸;.·.*ìÌ7
+ٲݯÈJêýGOÍ/ØZÚ½MÝÄûF**¾B*Í1*ôãË>>Ö߯áÜÂ2ôè58H¾¿,¾÷¿:*µ*ùG19Xë¯=FZÞîL/À*æ±.û
+È-Â>/A¾ÞOS¾7*°M2>2OÇ9;ß¿,F8*¼GøYÚÀKåø½QSIË´ºAZæõO·Q>¶=Ô3æ³,>ÎÏ-í³ºøÑý¹Èì2³ä¿
+/Á¼GEãÄûøS***A*î@0@5,8¹´êÑ51äÚDBø½QçIK@7C¯åÓ±õ6*öëÙWUÔV¸°Nýõ>ûÙÒÒMUäVø³ÏW¹ÙÆ
+Ðñü7Ú´æÏUU?Öð¼ÝÑò¶ASP»ø±æÑç³ëÚÙ/*úµôâ·éôÊÇK×ÉùõGOå¼ïÎÐÇUÙXWTÙì@*î×»ùôGDéLåRé
+ààÝíSLô³Ã×Ïл*¾çÂTæ³YéåýíSðóãYAÜ5õëä+*ùµïéãÕýÝÑÒêÕMAÌÙôô+*àWFø±Ðú´Q×å:îûÍðù
+IãÁ7öüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·ÓÝöüQ·Óݶð°ô»»úøÙ1*2üøíÝîS¸õîçÕõýüùñÀºöX+*õË
+ùµìÝÁ±õü+ÍÐQ9HúõñôûéQóëÕí?*îîÇáXG¹ôêÜ+øý¹ÈÌù³ëÕ±ÉåÝIëÝI½Õ*¾ÝI½ú²æÔ±ÝPÕÉYý»Qç
+Eá´û·òõø÷Éüúù/*úüöÙCµ¼7Ù°CëúõGOýùøñáÌÓÙÕYòíí@*îøóÑ9X÷ïåÕçíÞÝY/<³ñéõ½GOQÙØ°ßÈ
+ËÉYYîåí@*îöéÉ98×ZãÓ3ëÚÙ½íSøÑÐ5õ³êÙùõÀüúÚ+J+*BÞÁÝöµDîà+¾ó±LJÝ5²FR¶Á-SD¾Ñý컵á
+»5ÉPQíèü»QçYÀ@FõZëéÙ-öÇ-¾,*9Ûì:,9³ÞÇ4Ú,*TRÀÊп¹>Æì,ºùóØ1å?6øãý¹ÈXÏñÞ¶/å´8GRÍ
+F**¾B*ÝÈÂøå¹4*÷ÈÒU1îÞ+2***X¾Þ.¾SCÈJï¿ÀÏÀº/ÞÏEÍWBìÞÌ?üÝÑò+G1¼EÖóîáK÷â+**Â+öì.
+È5è1Fç<Òº·*Q8î..L*:X;ã5WERBTÆÕ*úöîÔ-Ý7ê·J¶ùûÌÐÝñ6ÖWÞÒ»ù0Ý=1**:0ÞWUö7+***,:ìÝ
++F·¾=:2*ÎÝäû´÷Lõ:¿ÏÀº/Þ»ùP7ÖWÞÌíûÝÑ.êà>ïíçÚ·õÌF**¾B*FÈPõ/T9²ÞÇ-F++éÛE´2ÏÉ,*æ
+:¿ÏÀº/Þ¶ñHûµÊ<Zù½Q3ÂÃSTÝЯåÜG¹ì*î*°ÙÔ4ââêÍÊ7ÝÔ=Å0Ú¶*KJ÷/.Öê×O¼3.:ÏB/>**Cé,ìÌ
+7ٲݯÈJêýGOÍ/ØZÚ½MÝÄG¹*¶**.*¯2EôãÔÇÀ,EL-XF¾ÆÐî,,6´ê½<0,æÓ1B-¾7JìÚÞÕMýÃUBòVýõ
+>óÑçÄõ,0G»SPï,÷ýòï2ææ¿õDHûçýGOµÂDñÞÀKÝÌË°á¼ÛU*Þ¯åÄ»¸ïÞÊU-Mýõ>û=âÐ;=ôÚ¸ôÌ+*Ý»
+Ù³ãÈOQÄòýÌнÉȸ²ãÊOÝ@سµ5S¸ìÝ¿é0G¸³Ó,Uìõ¹È8õ<ãÒQ½0»DÛÔ˹Ù*¾ýäÌFµëÌCC>¹·ýÜÑòê
+A8TD·³éÓO³éë,*HÙµíÜM±Wò6³WVúùGOâÜÔBETØ1*BËR/Ûô³ûúýGOÜÜúóà¹ïÜëF*ζõ@¼Ûúøý¹ÈÈû
+¸òà·éìH*JÚÃM½PȳÈ%%%
+d
+596 85[1 0 0 1 0 0]sl 8 mask 0 85 di
+/mask 6225 string uc
+*ÞÌ**HºýÈöü-õûÝðùåÞõýÈíý?ݽ0¼ýÕúüY÷û±îù½ãõýÈ7Ò<%
+d
+/sl 49468 string uc
+äÕí´ê9+¾Îøý¯,Þ?ËóB/Ö´¼ûØøüQ÷ûÍðÏ.ºýÑÚèYºýÑÚèYºýÑÚèYºýÑÚèYºýÑÚèYºýÑÚèYF·CÉÜú¶
+ïë-*²º÷òYö>Û÷íÞÁAAìù´Ï7¹Ù**Ý´ûسêÛ˽ÞÑÂæ¯áÌ»¹Î<.íA*îݳÕ@Û¸óèÖK÷ý¹È¼E³éÒUýPÌF
+6íA*îà¿é0ë÷òèÒÇóðé½íS¸ãÒQµüúùöð¾ºÝ*¾AIüÙóâÉQÑPÆQEý»QçùÐìÚB±ìàŵ7*¶îÖ9AìʸíÞî
+ùõGOAUøïÝų-98µ6*öîݯAüùÖ²×<õôí¹ÈÜòñß¹3åôûE.Ý-*JàÂ5MPûR-XF*òÜ.RÊ+öíܯ=ܹöñÐ
+ðÔôí¹ÈÜб޵-ÙìÛEúÜ-*RàýùÅåºùÏ0Ú¶*B¼3Ï2,îÛ¹Ù8ú@Zå?ßëõ¹È8;±Ý´ýØÜÛù<*Þ3ýíëEõäëµ
+ÙãÃ;Ý87*öé+2¯àÂY¾Þ0,¾µÙüéÔYä2ýíSø½ÊûÔ󹸴ëZù8*Þ3ýÝ»Ùôà÷AùçòÙµP¼E¯éA8*üݾ¾/**
+çÌ»¸ÚÂZüõýÌÐÕáÚAëÜϯá0×õ**ÌöýêׯAôÝB:8*Zõ±=´úà+Þý5:Ö**¶êÖMõOÙAñ¸ýõ>ûDä»åð/ûD
+ôO*¾=ü9EôçÇùA0î1?2´àÅYF*ú½+,E**¼7Dí×»CYù½QçÁ¯ìÉôìåÓWÉéÔÕ**ÌöýèÑ9áX½×ÇäÛ¸óæ*Î
+ûà+Þü5:ƽ*ú³éÓIíOÙA±2ôýÌÐÝðúÕWÜÍSÕÜ.*¶àÂ5IX7ݶèÌøïàįÕPºý¿-ÎãÅYI¾¾7**ÑüÊÕëÝÌ
+YûÝÑÒÌJ9ÌÙÕñWÆÐÕXûý½ýöÇÕÔÓYÁíí½üæ¾51:íùݾ¾7.¾¼ôEõZæÌüíS¸MÏ?ùûéC³é⸳è5**Jú,*
+´Þ13Ò¾Õ´ÛDòß½?É0ÄüíS¸QÄIA8ÊײéÉ*¾=ÍÖ/KÚ/+½7ùóçÅ9½¼68ûÝÑòíÌÄüú¶ðåÒÕ?ÀÉ3.ÅGÙ´é
+ÌIÉÄTXãû¼QçïT¸FDòäÌK=èÔYõ4*¶ê×¹é¼ÛDñOâîXìÝõ>GùÃÁÄD@»Ø²;»¸Ù*¾ÕÄ7EôåT5GMW55ý¼Q
+3×Õ7äPÈô+*P=TKÕ»WIý½QÇÕA½ÛöXÚµE1*>ìÛÃYAÝHÍSüÊк±ÈÈû¸òà·éìH*JÚÃM½PÈÕQ÷ûUÞàDïû
+ÍðùIãõ½Èíý>ÝýS¼ýÑúüCîûÍðùIãã0Á»Y9Ýüô+*ÀÝüùAºÈìGºøóûÝýüÚ:9ÜU*Þ»ñüËùõîêÛ½ÞÑòçÁ
+1ýûÚ»ýøS»ùó¹/*FºðÖUåÌGÙµ¾üýÌÐñ¼Wùóêð÷õQùõåí?*îõå9I7ØóêÕçóðé½íSøäÖW½8ûÛ¼üÐIý¼
++*ÝIü´äËYáÔVPEý»QçýÜüúÖ±óôó5ûùÙ/*Ú¼ûòÁU¼ú÷³øEöõUUKëúøûYQççÔÔVÖ°ñðé5ú÷Ù/*Füøð
+ÁM@V·³LEõôí¹ÈüÒòà»7ùôüÛÎI½1*î±+·:JßÄ÷ñ+¾O2àç1¶øíÛõ´Úµ¯çòòWëÝõ>ûOæÀñ8Ô»»¹*½1*
+îVS*GÜûµâî/º*ÉBÂÙ:îëÕéHFÕZæAýíS¸ÃÌ-á?FÙ·òJû8*Jèöº++8¾´R5@ÆØWÛQÁóÍõ>:JÜX,?
+F¸ñ×/á7ê×àý¹ÈHÎñÞµ-Ù8¼F>Ý-*2.ÙYJ¾-îNêóèÛJ?A1>10JKû¾=W°3ÒîáÁÙ8ú´Zå+ßëõ¹Èü̱޴
++ÑôëEúÜ-*2.ÓYJ¾-ÆZøõíÍ?úô¼R³üûE,**2¾*Úõ¾4ìÙµè¾ë,ôúìýGOÞ@P3ýÌä˹õG*¾¾;÷,,F*/Ï
+¹õüVKµÌWÍÉìÛARÛÂR.+BìÚQý7Ñ.êüíS,¯¯ÈèÕ²ê×µåÜ,*öÊ9:TXWöäêHE¾NÛD±Ü?ÇåPêAÀê×I×K¼
+WQÜÁSÞëׯÉôEP3ÂûÝÑò×J5ÄùµòéÖµ+*̱7/.Tö¿7Ù-¾ê2,/È<áô?8RÁ7ùðP4N¿7ÙJ/¼GøYÚÀKåø½
+QSIË´ºAZæé:ã7ì¿Ä8ºÕïçÍüíS¸MÒC1@6øóê>EôéE***î,*Ôî1åQÞ¯åÄ»¸ïÞÊU-Mýõ>û=âÐ;=ôÚ¸ô
+Ì+*ݻٳãÈOQÄòýÌнÉȸ²ãÊOÝ@سµ5S¸ìÝ¿é0G¸³Ó.UXëÝõ>GìOÈSÍü6ø²¸WEõ´+*ýËG·ìØG±°Rô
+ðü»Qç×YFÒ²ðèÔUÉèÔÙ/*º´íÜ»ÅäØæCèØÖöõ¹ÈÆ»W¯´Ò²9*ZDÏ4¸ëéø÷ý¹Èº»÷éÃõà»Ù·*JïìWú¸÷
+óýõ>?ùóæÃñÔÛ»*¾¶5ÅüÊ>ÝëSôCÒºýÑúüQ÷ûÍðùIãõ½Èíý>ݽ0ºýÑúüQ÷ûÍðùIãå»È*ñ+*¾ËíI*ôÃ
+318Súø7ÑÒLÃ<?4òåõ9*Eñ.éÇñäÊAPS¯àÚ3²RÎíI*Ôô3êñäÉ@WøóEP3MÈN3ñäõ9*É5/ÃÇÐâƼûÄSè
+Ä7IÀ6·ðù1¾Ó»,ÑNCðáżûÄSØÂ3=Túü+Z·Ï:5C0*2ݼá>Ýý*ê=A¾Óõ9*Uû/%%
+d
+596 83[1 0 0 1 0 0]sl 8 mask 0 170 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/invitation_management.png b/doc/krfb/invitation_management.png
new file mode 100644
index 00000000..e85298b5
--- /dev/null
+++ b/doc/krfb/invitation_management.png
Binary files differ
diff --git a/doc/krfb/personal_invitation.eps b/doc/krfb/personal_invitation.eps
new file mode 100644
index 00000000..133a34eb
--- /dev/null
+++ b/doc/krfb/personal_invitation.eps
@@ -0,0 +1,708 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 643 413
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sat Sep 20 11:30:58 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 407]ST
+B P1
+NB
+W BC
+/mask 6723 string uc
+î½*¾î1:E*¾9=/C8*¾Í8>Þ¿VK+¾Q14:JÈËÆ8=<G,ö=ßIO¸.îKÀ½Èò2Þ¾.ý>ç;Î*ý+Y6>*¾Û¿Àú-¶±
+Î*½ô04Eù.+îáù1ÃõݺíýKÝýÁ¼ý1ûü9øûIòù½æõýÎíýKÝýÁü,Á,%%
+d
+/sl 53369 string uc
+î½¼**ûÝ5*NRS9R3ü5¾Ì+ú°+ÓùÇY0º´ê½,*@ÕZêñ½<ÀPÁù.HîÕí/*òêêûùñåI7Nû5EÆ?JWýê¯ÙüB*
+¾½ßõÕ½ýÍCæ±.77ÎùÇY0º´êá*Z´çõ9ûã*²2*ÌGÝIõÕÕ7ó³Æ¾ã?6ùúRQÞýTJ3ºÞY=**-ÜI÷ÓÇ*¾îKó
+8*¾ËVÒÎá³*ñìíÝ´é6@ó¯Ö¾ÊËGì3PÝ3RÇG´0ÓŲùíI5³<*JÒRä¹/*,ïÔVÒÎã³*¹õÅ7?é6Dó±Ö*ü/Ý
+½ýÙA.TL±»+*ÚööôñÙåHúÂ->UÔÊù-YD5*¶TøÈûù1ÎNÇN°â½+*àèÔ²ÔÎáµ*ãôíH²èM¾èØ3¶EÂàDû±*
+*ÔñO+>¹¼ýü3UJöÓ1*ÞùÜì8ݺôæÍÑñÒõÜ2+Òø¹ýßM±+*QOYÝÖµMXÒæEäéB@åÎÔ´O±/W¼-0¼½ÑOÍö
+SéHÞó:øñòéé½YHºõêGùòåËE½H¯4ÞÚùù¹ÜA.ÈôÍÃØ*Þíä´8ü¼úîÓáñã¿áXù¾.:T÷û×,Ë/*F?»ùÌPF
+åVCY´WG½QÃØØóJ/à³ë½üíóP-1Q8Jüý½Ò*õTJÆ´»À+=+Q¿îì4ê½üë1IYÃMÀ¶ó*BÕùüûóÝ0ÃVÀ;±6Þ
+ìûçøºøÀ͸Ô+Ì1ÏÝÌXÛúü÷íÝõóã±½ÜF÷éL=P+íÛ½ý»ýùõ18ÈH**íÙE¸ööõÕõ8Ç5Q¼Ùì5=Zùù-óC5*
+¶Tø<,V?26B8ä8PÔÊG³³é¿4öýý+?LöI¹Àçâß/-.?ÚS¾U,¾¾*ûýIð7;Â.*JA¿îX:VAìýÙÜYÃ=À¶ã*È
+üýµÆÎ?¸F8×.MäA¾Ðù-ÄÇò/IóV-Ê:,*-*ÕÀæ5A½¼ûûüøñ¹úóÝ=Y¼º¯È3ѾÉëÝÝï¶ûù90L5*úSßÞ˽
+½YI¼HYÛõîá/9»¾,Ò½ýËç*¾ÑÐå4BÌÁ¸æîEÀæÚÙËDUÙ8ÒJúÌíâ÷IüWâ÷Å1°--ú·ËÅíùݽ±RÚWÝüëJQ
+1º±ûÕµìò̽¼ÆºB**G*Á-?ÎÝóùÚCöøö¾3*Ö×ÂÎ<Jüý½×1Å,Z,+ÏüåG9çèÄÈÄC+3ºüÞßÏDÖáöáµGXÎ
+øýUâ2=à*+*ø6Kâ·ËÃ6O5Ò³úü¿+É**NÙý´ÇH9òSå@Þ.¯/Ò**îR2ÓêïÞÅÉÅIZÇë½1üù¶×6ÍϾ**î>/
+.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×R¾øõÅC7*¶TøÞZ8ê.0>¼MŵýãÝ°³é¿4Â4øÍ
+Îý1XVºÖáXEßýýç×ØýÝõ<³ÆôýB;üAÃQÄ8ö¼ýï9Á·³Öý,ýÙÞ**ò*6âýýÑ,Võ½À?ÇÍ?ÓNT*+¸M.Ï+Zõ
+ßHF,¾3.Ò÷ݼRÝUETDà.V?úGA/FFT-úRñ½è2Á5²°ýýÝAÌ÷>K.GUµ.àZ8µÙç¿Èê¾*Zðü¸CèºJTGÙ*Î
+±.;-**ýûø**F4Lí·º´ììéÙ½íëÕõ̹ôïG=ÜL¿×èÉ>2;*õMJ+¾E>ÞÛùõµÓ¼»Ì1*¾Fø8üÀ¶-·ç³Ã@5/
+êüüAUä,*KÎ̹W:EÚ*çï7øQÜÆÓW1TÞýýýݽúÝÉðÅ-¾úáÇì9ùýÒµ¸ÒýIPèõ½ûYÏ-¿»´æåãÑM+0-46Ä
+FM/¶PúÂ9ð½ôë¿1Ú<,ùý½ûýåøî*CXZ--TSñýøÀô12ÆúÐTDà.6°íÄú/?ÚD1Uõ½üTñ/DïìûýAѺÈ:Á/
+A²¿òùTõJO´J*Ô·ýÖ29îSé<J.Ïûä¼¼ûÏOÎûP2»MîÒ±YA½H»IYüøêÒ¿µºö2/8R°P>*FÁJSúöC9*¾û5
+üùô5+ÃØ¿1ýůÏ*óíY°D*îÒòóDõ±57EºÄ>QÇáèãèÔ-?2¿ñ¾.SD0ÝXJ¾.ÎK¿+XðäâýÇIÊýIPD÷½»LÑ
+-K*Nû½Ð=-±*/01.¾OØÔý?¶K49ZÞ?;>¿ÜèÁSÄÞÃ5êÎ<ßüíÊRüèÄÈÄB+,5ýÙÇ¿UñZAýÍAÒ¸,/äù9ÝÕ
+Ù.·N:²IÐÆAÀ¯+-¾ÄùÝW¶ðÃS³V-Ê:<òîã1:,òÅÀæýû@ëDøõîá¹öíÏÙ@ûùIÀµRîà<+¾¹>:æÝ93ÚÈ,Ì
+3I¼UìÛÙº**¶8µî×ÅADRòÚ¿ÉüÔZϾÛíÝðáV*ÞSç-µíÒDOÈö<æZS°öÏÓW1T:,µ6*UÙA¶Ðéà*YèðýøË
+XýQæèüÕÉæ¾J2¾èýYêýýÈXRßËýçÅXýñ1Øèº*ãýýç/;Â..22ÛýÍóß?¿öýý·Ã7í½ÚT1ØÏÐ;PÞ@»åÚZÞ
+ßÓ·ÖÛýù@4Xΰ³Ä.ùýõ¿ïÁ.·N:>ûÛÞ1Á;±/ÞæûÍ<6÷àÒDÔ+T2+ùJNöüT2ÛFîÑWIíìû¹õÜû·éÑ¿É=ö
+0/Ì2òG4ÆñëÅ.XO+ÙF½ÜÓòçQH**Wõ´öêÛÏ1:Zõæ»5½5VÞL+±Ü½SÄ×**ÏÐåW>*º5*Î,C°ÚæèÔ-?2¿E
+0Î</ÑÝíûÛß*AͽºÁÂP@´íìYäÂéòJ2*=¼Y׳ü¼=HÎÀEýÑ6GÌÅÍ3Ú÷*5ÚüýÞ1ÂÞ,ÕýýZÊðÛHÚ<2ýýý
+½ûÜó/ÈõJETD032êý<-8JáE³¾ûõÍüNU¶+éííýûéßDGZÂU.·N:R´ûÛýïI5ÄÊ?*»õYSîá5ÑèÖ0¶:ÂÒû
+¹öåÀæÉ/µ7ù÷óêÙ¹ôéÍá´Gº×½CÏÞ+ÉHL+H÷û¹UäËÁº**ü9KîÛ±1Í8¾Åý8Ûöî/³ûU>ZÉù=ÌB5*ZT1M
+ÏTãTÓ*¾K*÷´é/52J¾È+ÎßWÂ.WT6îÇ.Oð@¯KWÂ2ÏW/U1À*Ò×KÓZ3ÈóÀ48ÆÞV*Kº²æ+;αÂ*Û:Àƾ*
++RöX9X+¾-,.æ:Sô<PºÎ7?7=.>8ε+1Aä;²ç,3¸³î¶¿8<>RÏTû8=â7¿æÎô,üµ»RDóÇå0²áúûã/êöà
+ÒDÔ+Ì.;*¶@Â@5¶ìØ9ÚÐ,*¾EöVúðL+ßÜûC4ZÆèÓÙ<Xºá5*îàÊ,õÛE·ôïCºóâUµ4PZË*PëI»LÂÇ*¾Ñ
+øæíá¾KMÐòDS07÷;ãÎÙ°ÏÀ¼Î¿:æFÕJáíÝÍÙÕQ¹Î0?ÙIò´+CHCÓÄ»¾-K>**F5Lé.º¶íQØçÂ1íNYT>Z
+é×µýüT4¶áñåW÷ôWï/*¶ºÏÂü8¼º¸öʼ¸ÑÏ8WMö*/6öü°5P,*ÐÄι**W°ÏÀÄÎ++.RÐU¾Æ.34Oí4ο9
+Xíͱھ3ÍÊTáHÞ÷:øKÞ¿5Y8»¸ó¼ûù÷éÉ=IòXä>Î4Ã5µ?¹ô=½õ0ÛOø0>°ïÍC鸺2ç¸åî+4JØûç½6öé
+**5ìý·Ö¸,9HCÓÄ»*Zö¸2Û8îÞÁ9µìú·Å¼û¹÷ëÙéSA±4RÂٰܻëøËÞù¾ñJïKM0ËEæ1ÑXï*HK+NÛý÷Â
+*ý6*îÕûùýÉ@ÍÞEÐè×2¶GÂÀÈʱ*:¼äãÊ,ÚZôêÔNÉHö.*ÚÞ½ôÌïL/4?âóÊÛ½,ÍξT.ÜI¸ÈüH6*¶¾Áù
+õACÊÚ¼µöÒ³9îº2SGWË8+¾æTA³4ÞèÓW·ôú4ïûÝ»ôâ¿=åìHöõè.BMìûù±8Çí±+*A2ÓøüûOôà0îGéÑÔ
+Ó¾áKNû仹.*°XÝÒS*KÁ´O×ðÒ´íù?Äݽ½ôÆ7ÁÕí.*æXâÕMÇÖ±ºÜÔÓÙÕYTÏø@¼*AY´ãÖïÚº9õWVʺý
+í,*Ü×äVàÖÏɵYXÎø@¾*9I´OÖîâÊÞóßMåîRм÷4ܸý2*öÔD?@-ÔêضàÌÜÅ=ÎÊÍZßÔúøë@C*¶´êÒGU
+PVÖYÞé9+*ýó5ÖZáÊOÙ0ÖãÔSýÀ2ÂYÝMξûù1¾8¯ÆõYÎíÝJݽ¿¼ý,ûüýîû5òùAæõYÎíÝKܽ¿¼ý,ûü/
+øû5òùõÁõYÎËXξèÐ+*ü½ýïÊÌJ+¾?-*öýü/DK¿¸TÌR·DãçÒQQ,JN¿T+*Ï4*À,ÚÒTÔ´ë,@é>Íòæ:MÃ
+63¸.2Ó*¾JÑ*8ç;OÈPÛè³5,éã5ÕY¯*âUAXÏ*¼5AWÚ°çÀ³ÉÙÐãÈ?TÖHÃ4Û*ÐL*ܽ¿äRÎÈUBXÞ¶+5Ä.
+ÝÀ;ãAXï*ÔéAYÜä*ÂAYïÈìéAZäÐOÑ0Ëî°?Zø2TͶÓÍLÃ5A¯ßÊG=èÔÛ,*Ï@W*¾MÉô6D±çÈ;ñݾëø³é
+AXßܽ¿äRÎÊÈÑUÌP»°óÂ?W>SÏR7´LSÏNÇ4?0ÞQø³UAWÝ°×RÅ6?4+:Â8Ë*¾JÏ*RæÏLÄ.Ë3¶NÅ@·L/
+5AXæUÕXÛº?,ÆWÜà*ºÕWÛXëȳÉ?Ýó4ATÕFÏðòû5²Ä,óÌN·,ã3ÒQÏP*L,ÞѶ*+óS**¾4*R1îÑ°çèOÉ
+@OÃÎ:LÀ434÷.¿L**Î2R9âÆ:L¿23°â1?<âÆ;PÍL·SÒ²ëسUÔV¾U6¾íÐÃÉÔWÖZßðÙXã°OÉÓVÐR>QÏÜ
+½¿äRÎè4ÒSÍTÃT-îÎR»</èÒRÏR>TÐä*2>SÑÀ-F?UÖ¶ëøóè=½âæ:MÃ6;°N2G¸;*ÞæºÞÏ*öMÇH×ÐÃ5Õ
+Xì5ÕXÛºïðÃÉúØ?4*³÷ØÓ5Ô2*TãéÓõYÎC4Ò¼L/4?RÑ8.*¸:*NT>Sµ*ÞMÇDOé@YÚZÏPÅ0ùÀK=*2ç
+:MÁ:Ë.*KÑ+ÑЯ*ÏÎüæÐQÎNÛÐÓÉïèÃÉÕ÷QàãéÔ2RÕÓéÔXÕ´çèóû5²Ä,óà¼3-PêÕZßJÖC**Ô,JÀ+¹´
+*ÌVDµêÜGá³ÈÓDãÈÒB*5ãÈ>U10¾PÓ<³0*TË+¶TÒPÏLÃè>TüTÕ¯äÔSéÄWÆ¿²ù@0JÞ¯5+¾çÚSÙäêùAæ
+6/THǸ:*2TÒQÏJÛ5¾52îÒ°çè³UÓNÅâ:M=*LæÏ5+:Ä.Ó1Â+¾L=*ÂR;L/*ìÆÐOÊFð?ÉðÐÃéÔXØX.¾
+ãà?5ÕõYÎC4ÒÙ,³4>UÑTÇðÓPËDÓè>SÎä+2ÓSÑà-ºÒSÒPÃLÃ4@ÕãÉÕYÕBC°.4Ã*2LÅÌ.Jº¾J°ò**ä,
+È*P*JÏæ*ÝâSÓUØ°÷àã½ïøó5BWÝ´CZ×¾çøü/DM¿<QÐHÓ*RRÏJÃø>è>ÞÃ+.*.-¶SÐRãÐÃéÔTNÇÏK?*
+Òæ;K±*ðÆÎè¾C*NâRÏLÂP*ÀI+ÏÀ*YÒæ;NÉ@»</¯ãàOÉÕ2*ê?UÕVÜܽ¿äRÎÜÈ>VÒTËÀÓÈÝLóT?TÒX×
+,Ôè*ÎÓTÑRÓ4*îÔXÓÐÃUBYÞ2@QÆ0*JÐC*ZÒ>î0+à.Þϸ¿:¾5-¶:*ìÇÒVÖ¶ç4@Ê8é³6Õ¯Ûܽ¿äRÎÔé
+ÕYÝì6*ö¸5*ÌéA°åÐSÉ@Ê×</TÒ2*BÓ4¾L¿LÓ4Î0*LÃ3¾Rï*NÈ>R++M*¯+;,HTÒTÓX×ðã6YÑ@WD³èÒ
+»ý,ËÏJÛ?SÔNÓDÓU@í³5AWÚ²ãDãUËLO4?TÒP?+.-öTØ´÷ðÓÉ>OòÆ;MK*èæÏNÌF¿D1¶Z*îæFÞRJ÷Þ3
+,Ú¸+ÕNÈ>VÓ´ß,0º»ý,ËÏJû=SÏN»ÐÓ<Pµ÷°OÉ@S4Úä»Ï4/4ÒRÐL3RÏ,*À+FÓUÙ¶óÈ?T<½²ÆÏLÅ0GT
+N±O>çÏ;J1@*6Ã3:MÂ6Ó+ÖLÂ6CD-:Â4/1Ê+FÏ+Ä.çÐPÑVóøü/DM¿ðTÏVÇTÓêý5*îüÙÑGÅ4ÒSÑ2?TÒ
+à*úÒSÐRÛØÓéAí³U>OÅ8G°.6/+ö¸õýýÝ-ÆÎLàæÏZRÒ²ÉرØÀ,Ä0°C*ɲç>R¹>R+*Þ<À:×Gºµ,óÑT×
+TÓTØýýG½õçÑUíXý½õÝDGRÏR×°ãTÏLÓT6¾Ùèó5BZÞ²Ë<Ê<G**¯.Y2ÒÃK=2ÏÖ*½>S@ÙýåYGÂ0KÀ²²J
+:G4<23¶±*AâJ9OÀ>3ÐOS2>*B*Z/Xúø»/Ò·âÆ+3C/KÐCöô.èÃû4ÔÉÖ¶ýý¹õCùç-¶ïï÷üÝýãåTëð/Ê
+1-ô5ÖZÞº7ñçÔSé´W×VÔÊÒTÏTË+¾¼KÎRO7KÏLÃ/¶SãìüQÝ?ÎMìÇ>SÒNËDÃ4ÊD*24R+:ÏRG;->Ã4>U
+Ïð;æ:*J³/ÒÇÊJ5+Â@Fç¶ÞI>ææLA³T×ýýÅÁèßÇJÇ,çVüýÓùŹ:·4/ÉÖ³HÌÙ±ÖPçàÓÉõèOV×±äÀïÐÉ
+6C¸ÒRÛ:RõÏâËKÙ²çÀãMOáùýºÓ¿.÷IÁ6CèN5×±¼VÔOÄ8G°ÒÇÝØÃT<RÜÆ;±Ø<?°>,*97¸ÒæÏN.È×±9
+8<**7XÜ*>Í<³0:°=æÉEÆÄ4ÜH¿àUÏXËTÓ.èÃZSñ0Ì°âýùü°õ.ð.6LÖHØöé½G8ÑãÉýLä8TáÙ¹Åä¸5ì
+¸Òæ;¸KμM3Eâêè?éÐé5ÚúýûÛÁèñ·0Sø³;YèÜ»òÕýXK°âæDµø´O´C1í´ëïàéÒæ;MÅ6Ã3*Mç2>°A8*
+*.ß*T·=21Ø*G=î¼Ý6ÞÌG³WD6¿+2P.6ZéÜZ>¾ßÉóÇ+í7ë/,Wø°åÌC3çZJÃTÓY*à;¿4**X.+ç,ÎßB@
+ËSJ¹/KîJBÞÓDFÞÀ·ÁéÛöõ.TOÇDÃÈ>±KÎXÈ3E¾8GèÓÔýýݶÒFÖ¼L1ý1*úúÑáVëð1ؼýýüºüýëÝGÃ.
+7TA.è·:ßO÷ýûÒã¿Â³.ú»äB¾Î÷óÝ@ÞÕIãÃRÏ»ØýÑãÁÝ91æôý83TòR*¾?¸âýýF.èѱÔ/*¾Fê×Éã:00
+Ã*ׯ,¾LÖ¾ZQ=D¾2ƶ<Ä2²66:*DVÅ6*:=+Îã¿K<**ô>JF>æ5KZ¯+*æCTU*R¸:.Á,õèî¾-PJYõàÝ=R
+Ýóè@UÙÒûýíÅÑØÀQ=TÕÄ·ÖýýÅ8ôÄÞïü½úéÇS½í½ÜÉÊóUYúý³ÅãN+ÁõýÝÜJOÎÜÁ:âýùñì0ŹñVûýûà
+ïìñWN»ó½ÝøåÃÛòõýãíâÂRIÝû½½úØ=±´ìõýåË5ÎLÈ@¿OK*Ç2²/¾M¾ÉGùS*4¾³Ûå*Þ>PJ²Ó8DZ/*Z³
+ÜÄÞ»+*:ê¶Z***Y;-8ÄTð²+6ÞÌGõXHGøÛ*26JÚÐÂK¾5Kîéµ*ZÂG¹¸*Jæ=,GL¶×0Ó*å¹ïí3ÒùðãéAY
+îüýõ⯻KÂTAYèºéýýQEÁÒó½Åý´ÔBÌиýí7UÙÔøýý-÷ö>ZÄÄ@ëZÍJ*Y2ÒíKø½½íûÐVï³>³ûIüO7KÚ
+ʺêýEµCR3½ÒÒùýÎÕ+èå·ýýýöÕA¶0¸ñüý9.ÒæÐRøÇÒQÑúýÍÂJË¿:Ú81/ö3*,¯åü9*7¯,O-¶4*NUÉ9
+.ö5**¶äÜC***Mâ*NY±Þê×YíE°I¹1ä÷È<0+Ì.¿Å-¾*ôóHLB½PÎAéÎ4îºAüG¿øUÏXË°Óêýý¼ú>@¼BË
+°O6V7,óß´·üÍÛäUÔú?ÁÙýE1QU±íýIZÄæZç4äK+àðäEùýýùÎýýÅõQ.2>HäÐÔýãíÒKÂù÷÷½½Ú¼¼2ïP
+Hóû9IKDñºôýñËû2àÂ5ÝEýýÔ¼ì.°â:CÀòR<,»M+ÎÞ¶>+*ÎÞÇ3öóN7;ÎÎçSôÌG+**ʱÅ8**¶<-Ï*ØB
+4*,NÞ¶YÎQT¿9-*î:W*àXÆJ;ÞçDÒ,ÚìÃÅQÎTÈÒSÓNG5,óÐÐöײ4?±¯ûýýÇOO5üI¼QXÙ´ìàåýííëß;
+4Üýýý÷¹î¼Kô4ÁÃ˶KÎüý½ýæ9óJ,Äí÷>ôýöB»FïÌæüQÝÐ1;Tº6IPýýÅ÷PEÅûI³QDƶKáG°ýݸ9C¹4
+çMÅ8Oðý½X,OF*Æ·¸,üDD**²¯½+*-æÊÛ²**ZFLLù,*âï1Î:1¿°ôZ*¾ß/*ì±,»Ø0Ó*Õ¹ïï3>ØLãè>U
+MμÈ4IÌPÓ<õ×ýý͵ͳâýÝõâ³íìËEõëÙ×ØCÙÐдì¼>>ýÍ7ÌËGñì1.¶ûýï-ðO8KüTêýð¹,çðÈýûÍû
+K9Rèöï¿ÀüýY²Í³ÕûýâÈÀ²Ï=ÆLÝü½³GTð6ÀÂ8O¸>E¿0µ<µÓÕID+9üúøñV+*¶Þ×ù,*JBðâ*2*Ü8;ÏA
+*¾=·ðÃ+**é±;Rº2**.LÌZ*8**G¹ôÅèÎFîJ0Xõàá=R¿OU.*Z5,óÒÖ*,?UÕÃèûýùFÃ>ÉûýµA3WÙ·óä
+¿-å5ׯòçDÂQ÷¸çëùýõÊ29¸KÎüEìýýáAùÐÅMàÃôýöFÅÂïÖîüAYð2A»FÐTZýý½·ÄD½ûý²SQê:Ðâµ¼ý
+½Ö7?·8CPÉD·ÈËJ/µ=¿ÆñÆAPîJ-:¾PêUéR2:DVILLîñ,1ÂXóÇ2>àP**¾ºQB*Ý**èúëM³R8¶:-Õ¹ïï
+3Òüè?ÊÕYîÀJ»?BÁT/ñµö½ý½ÙE=Õ.ýýëéKÏ°0<¯æ´÷KÝ<À²1RÉD¼ÉæöýýAбX2ÒÍUñù½üÌ5ûïɾ×û
+Yüå<²ú=Ïüýí?NÄ:1/Îóýïé?×äYý½YY7GÈÂ÷æùýÕ>ÁÃÍNT4ÒSÏúýÍÉJG¹îðÆBRÞàË==î´°N:Z+ʾä
+M3ó@5.Zö°û4BG´T84Z¯×X,CJº<,Ãê7+2î÷30Þá¼<*:îR7È-XBßïXºº,óÒVÏ°ÓÈØýýÛ¸¿áà÷ùƵ¼ý
+ͼTFP6CÛíùù³@ÈÖÊн¹ÔÙFHý͹ÝOH2ϼéýEÅÁñ±ýý½6Eú÷åýåµ²ñQ?Øß/ÜÄÌIøÌýIÌÐíàãììùý7
+ÍðKÐÛüý½¯¸Øú×ÑíýéåäN4ñæ;NÅ>OKÎI½/Ì5>ØTãTÓUKμÝ÷îßÍÉýýÝÙõÙTÆ@ºðçôúùõ0ºØúýë»ã
+Qä:åýIùUAØ?µûû¯TÏ,Ô/¿¸7IîÖüݽõU81ÒÔý÷í@×ËíK/òùýíGýó×ñýÍ8;Ê<êúóýöó9AÚC»ýý½¯FK
+ØòSâ<GÈ>çùAæ6/4Rþ<TçüI**íIHôÕÜÂFS-3Óà2øùö*ÝIFRS¹4;ºVÜõIG¾åóL<°ã2Òí2<ÄHúýëYO
+»4»6²óíµÔß±éSïøM2Ú»ì@ùúÆðW´ùýÚZðK0ÜüðWNÊÎ+âÆKÀ.ÇÐõYÎC4ÒñÈÓ5AXãøàóXÓPÛ¸ÓèÉíú:
+>Wì>4óè@QµÑXߺÓðUâãòß²TFËYù¸Þ»Ý<úÑ.4EìÉVØU¿N¿=DZû¸³PJ¹´7ó¼-ÙÓAÄ9KÒºKÔ¹+áóCÌ8
+ILO7¹×ýñ´äù·ó5AQURÄöB½:³èÒÇÑQíÝJ±>RõóUÕYܺÛÈâéR¿°ã4@VÖ¶A±ôF<?UAVìS:NÏ4ÏïLÄé·
+âÈ@S¾îFðÇ2·¿ðâÈC¼ú;MºØF+-Æ;Yòä4>Wð:°âE·¸>1ÊHÄ6·I¸2SàMÃÇ=¼OËJú´ýÝËÈý2á2Í>Nʶï
+¹8³4óÇ>RÐܽ¿äRÎüèÓUÔXÛ<?Çù¯ÑäÊD²êÖï¸ü>èNT?UÒú?¯â¼ßØÒÅ9µ>4ÔVÖXCTáÙ²×àã5D·,ïLÂ
+öÒ3áäÍZ*QMä6G¿,ëC-ÚÂØøLÐT;LñãÈ?Ö/ÊD»0/¿DìÒÀJ3¯îVàÚ+ÂÆ<NÆR<OÊܽ¿äRÎìÈ?UÕR×D/4
+X¸âRÏM+*¼SÑRÒTÛèóÌùê5ׯßZYÔñÙ°÷à/YSÁÇú8F·ðò÷±æ;Å.3ÏI½*?Èâ;7,ÒåÍJÃ6·L½ôêç-RÒ¿
+2?GI¸ò.;öJÃ8óÙûýôTÜN6Fºòêç=ÆEÀ>SÐNÈ<·øü/DO¿ÐTÕX/ÁDTÒNËT+îÒR׸óèÔÀËG¹íÅ+Uü7H³
+Í2VÕW8Éݳã+/DÒÆÏMÆ>çNÈ>G°.SF¾N¸>FÞ;OÀòRÏLÃ8÷WWÙÙú-æÎLM.83Ýëý·Õ°Ð0*47B*/ÉÀ6âÐ
+QíÝJ±>RêÓÉAXÛ,*¾ÕD¾S¾:*YµÖúÞ.Éë¸ä+ð79øGWÚHøðµS+ÈÂç=R,TÒ2*:Ó4îæ*0*ºÕƶõ÷;NçÑ*
++1/4ÙÌRE0ØLôN¸Þ/<TÒR*;OéùAæ6/è¿÷,ÔéAYݺ3ZÜ86Þ*¾ý4Sû°³½òêï»ú6ÁúüâUÕÚáð±GëSÂS>
+I³T>SÐNÃ<OPǸñ*-+K*ÊTÕYÊø.È>Ô·ZÞCû3ÑãÊIÊNGð,SÃéùAæ6/4±×Àó4ÔB*·óè¿+*Uñ*8±¼Ó»æ
+ÖçMÆ=°²=öÇÎ:?òáãH</Íà¿ïÊ=NÉ>Ã*ö²ýF°êÅ6ÐNXÇÐQæÖÓL?Í=FÔçA³Ððòï×âëøNËßÄûÚÕKµêÞ+
+.SAåůà¶ÍPÇMäBO,óèùAæ6/4¯×È/5ÔB*¸óTÔ:RÈ/ÉÓ·*Ý×½XP²úJèâMÓ4Q;ØLÁ.øïµ1Àã7G¶ãH±=
+NÇ@ÇèRÖñÔH»ôÝ3AGðȲûºÙìOèÎûÝS;-1ò=æKQF<NáàÓåÜé÷Sø=æÏ·X¯ùñäCéVõÖâPò2ÒVíÝJ±:R%
+d
+643 83[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 6723 string uc
+*Þä**¼ºýÎöüÉõûIòù½æõýÎíýKÝýÁ¼ý1ûü9øûIòù½æõýÎíýKEPR*%
+d
+/sl 53369 string uc
+äÕí´ê9+¾Ð,*éÓVÕè*.@V¯*18ÞY·Úìúæ·ãZ÷T׸ïLO3ÏLXî¶íÜ91»SÔÅýÜ8;MÉôCõëãèÒCõ¹îìÚ:M
+ÛðPµUF¶YéË>ÓëSô3â·ÐÅËØîÎM7¹öÎË+T>WZøü×æʲÇ=¼ÝÒH<ÒÇ@ýýY×áùÕ¾-/Zëúø³óÒJëÕYݾÃ*
+FZ¾Ç¾¯*µÕA¼öÖ3ÅGèKðé´6ÕUÑBøðýõLɱPÑÕì¼êMÄDãë½Ñ½¸á0@ÔóÅøïüëÓIFVºÞNÐQKÎÌÅ:Íü×
+MãÃÅ-T0×ûÀÂ>Sóð1ÅùÁ0>ÌïÓýϽ*ÇDôû5²È,/Ü8*>ú¸î*1@Þ¿×ÑøC¹ýûéùîÍïPº0ÊÔÄÜÚ÷°VÖÊ·=
+̹ýFÆ9NNñTð´àÊïÑ:üÙ³úÍW¶ñ1Üâ/6QáÇèô:éòßçý9Ìã/ÊD¹Æ¸øB5ãDû»ÛFHä6EÅXõíç0ß÷NêùAæ
+6/T±ÛÈ?U@VA0Z>:8Þ¯ÛD/UÙ½B߸ãè÷áQûY@SüîöÞÅÄZ¯±ÅUáÜø8C¾¸ëð¶JÃÐäXZú±û4DÑèXÕæN4ý
+ðP>ÓêC´KáYüíCäÇA¶ø¶Ëì³,àÔù¯SÜÃÉDºøR»9Ñ»;ñ2ÕõYÎS4òî=æ4*7ø.Ø8>²2¾ÙøÒæ8IÍÂëñPÅù
+íèéÐÀâÛêKRäö¯>X¹Q»QÊHÉ4Hãé+ÁXëGü×Qâ¶S³Éá¹Ð/ÌJðTé-,ó=ÏR¯=ÆÏÌíûäIöÂÑÃÒ/7ýÁæ;Æñ
+µÀ+M>÷øü/DM¿<VØZ/¿Ä+Ê¿8Å0*ö²-ëë,?ç:IX1ÌP?ÉYX@Æ9FHôÎA·0SðóÚÙÔM09Îà»»â±ÑÜêø¼Vµ
+<PÒØâïW;áûFàãóøÃWè¶ÊýQTï°Å;Oüñ¹Ì¾ê/AùƽÇÁÆÐO=åý²¯àN,ôû5²Ä,óà¼35PêÕ¯àÊÖZàÂÃ.¶
+¯ß¾3=ä:°ÚîXÒÒÙD?è=·Ð¹Uñ½.8IUÇËLÌFÓ»ì¯-T-ë¹Í¼Ï×SË.+ÂíåTSáÈXñüâöGÆN?EÉÛáCXåäý@
+ßÞ¯.ÈÍT4üåLç´AôÎì6ø²TÓÊò¯RFµLOùü/DM¿¸YÛºûðÓ5ÖYòÉÕY+**VV¾ýð³î³ð8M×ðØ´çàÃÉÝéÐÛ
+BFûÂÑÓÒÄ:í>4HõÖùÊïæóÑÓ±P6C¼Tøù¯ÓÊKó´÷»XÄÌMÍ¿ãÜÏÅ·=ñ½ÈYì:ðÌL7ûÜñÎKÊG¸¯´,¯4OT
+ZË9òËEÌÈ»ý,ËÏJ7AWØ°Ë+*Wï*ZU:J6¾åØ0A¼ßìHËðÖ´û,µ@úØÐF7»àãLDá1<íÒXùè¹à¶Òíû¸çLäÌ
+µÖïù×Sâ¸ÙÚHAüÄ:OÜÞ°åæÃÕ·÷IO@²*SðÊÀ¼õ3RÊÏìûåK¸6S¸¶Û½ê×PÊPàܽ¿äRÎM̱*ÏÍßÍ?ÏX5
+AÁ×åÁÄØÏI6÷²ïôÜûûï´ú79ù2ËHÅú<¹ìWñ;áé»ýHÐظMKÑÜÍQ5ñ-TWð÷ÄJ»AÕé?¸S-ñ½IÇã6JÇD/û
+ì·,4ñ5°ïYù·»L»éýí=6B°çÖõYÎC4ÒùÈÓÉÔX׶ëÀÛ²ïØÃU6¾íÐÃÉÔVÞ6üóéå5íWº¹ñÁûJP¶ðúEX@<
+I-æ;QôØÙìáßöǷܹéÙǹÑôÆÕA6ÉÑØÜúHA¶0Sð/ÔZå¶.èS´C-æÐPL߹;äú+QA¹íX¹¹ã¼íå.MIJ,ô
+û5²Ä,ÃáÄ;M@V6*252¾ßÄ7EPV.¾1-äÉD»7½<óN¯4ÊäÇ:KÃFB¾èüê¯>4?Aä:HGµ,Óøå±ðòöÌÒJðL¸½
+À2³ùµèËIÌç>Xô2è°ÑÅAÉÖ5ËGÇPÓðï:4á±Å;OâºJÌI/4ÉǼP==1Ó³íÝJ±>R=0VBY±*66B¯ÝÀ+5@0Þ
+½û,ôéÔRÃ0·OÉDSTÂR;N83ÑPÇ*ëçñƵD?èÐJ·ò:ðÆ4G°>QËD¶ú7K¶âæC>æ8Õ=Æ<SÏH?èáñú·Ò29F¼
+:÷RÐJKð=å:·ì:?JÄ4ÓKÁÔàYõ/ÌTçܽ¿äRδÉ@WØà*N@W¿*A@:,Þ±Ïðò2ÎIÀ6÷MÈFÃøÒSÑN¼2ÎKÄB
+¯ðNÇ=ø-R;OÄ.çï´êÞ×áQ9G·N9K;,ÌR<PË@C4òÅ9¸ÒRÎIÀ:¯ðÌBK<ò1;ÔÝÛàM·êÚßPú¹ÒÂ/ÌRäܽ¿
+äRÎìU@Yغçè³5ò¸Z*Ì5ÕWÚ¶ïØÃÉéÀÃT>QÎV×ð׺÷¸³è=QÇF<NËH»øâ3<ÅâRÑPÌ8G4â13°â2<LÄ4S
+NÇ:GÐÂSâ¾YÈ.S<PÉ:GðÈD·ðâS=PÆFÐÇçÁ9/öÍR´ëÜäDYôÖøü/DO¿¸Wز÷ÐOé@XZé6*î4/72292-î
+ÖZë4PêB±ÞF@RÍD³àÒS=ýâç=PË>³à²4*B/µØPÉB¯4/2-BòF7ZËBWà²,Ø2èµ3WÌÛ¶ïï±ñÊÆÜÆKòè×õ
+YÎC4ÒEUôVC±äÌ?-åè*L,ÖC±ãÊ?UäêV¾±=QXÛ¸ë¼çð×Z×È?UÔU×êÓUÕ²Ë*¶VØVçÀ?ÉÓVò4@V1X:8¾
+ZÛÀÓ0ZBÞ¯ÛÈ/5ÔTáÞ÷Ã@·è±ôæËDPæÒ´ñܽ¿äRÎìÉBYݺ/ñó5ú¸´*8ÊÕYÞ¾û,äÊYéÔWÙ¯ÖPËÐÏNÃ<
+OÈÒS¾D¾ö*<³TÒJ*öÒTZ°îì+43ºÒOÁ274òÄ6@ÑÄ8LÔȳùü/DM¿ÈYÙP*¾Õ2*ýÓÉAXܶóàã»óèÃUÕXÛ
+¶÷XÜÂGÉP7D¯ØTÒQÎH¿D*:ÍLÛ6¾-?¶=*1GÞIGDÒQÌE¶îRJÁFãTôêùAæ6/ȳëÐãÉÔXÙP*ÊAWÚ¶Ã-2X
+Úä*L*ÚÕZåÎKÁô5?ö>L3F/Z<QÍFÃ+¾PA,ÎP:XîN*,*,+è+àKÅ.ºÑNÆ2;4âæ;êNTÕ¯çܽ¿äRÎð5Õ2*
+çãéÇ+ÖXܺ÷¸¯*ÜéA°èгÙ@6Ñ4óS>RÎJÃ+¾Q;+µ0*NÃ4F¶+2?>D,î0*008îJNÒ¹è.èÑUÛÌOùü/DM¿
+¸±æÊK¹0Ë×±îê6*F¹´*ü6DµðæÛ±QËñÀ?U@VÖ²ß<ÖZGC*Öóè,à*Ä1Ì9Þ7;BYçÞ×ùü/DM¿ø¯ÝÄ+EôÉB
+ZA,JÀ/¹Z*XVBZßÄGùô˽áÄÊÓTÑTÏðÓPÏ<ÓÈ>TÏF?SÒPÓ<ÓTÒÖ¾E+B5JÑPÏ4,B-B2¶Þ,O0Á-ÔU×´é
+ܽ¿äRθÉÕXܶ÷D-*ÚL,NÕXï+ß*àÉÕZ*ý³6D³çÌóD/I¿,?4ÒJ**³0ÞK¿4óçÑQÎF?/FB¿Ö*ö²0:>*H
+G·¾5XBPÎÌHÛT0WØõYÎC4Òýø/6ÕYݼó,ßà*êAYݸÃ/JYÞ¼÷ð?0Þ¿?Ñ´WE²ÝP·SÎN¿D?è>R?*@4ÓQÒ
+à*â>SÓ+ï+Ñ-JT6?È4OÈÒR/*-,ß/³*K.TT>XãàSùü/DM¿<XݶûÐXܸË+Ú2¾*¾Z*å/6×±çÐ;ѳ¼3J
+ïH*¾*Pù²è=R¿É*T*ñÚ*º4=2:24RMB4*ÏX.ÞVJö¿ã¸@ëØõYÎC4ÒµÉ0ÌØ´çÜS±êÒ·ÙÔ0*ÔË+Æ´é,,
+ºÙ·öðß-õVAó³UÕXܲÛ2*É/ÊÔY±*AHÞ¸çè³U.¾îà³0¾·÷ð³5AXÛà*À6*Õß¿:,ÜãUÔ´õôßùü/DM¿ÈY
+ßP*>ÖZݾ+-´0îÄ*êÕYáÎ/ø²ÞZÓL³4?SÄÈ>SÐP+8PJ6*ʸ9*¯.ÅÎÞ¸»áÔëØ.TÖÛö16QÆáÜÒO*äÉÐ
+ÂG2Ò9ùOÊA¯ÞÀû¸°*:Vò=.5.2,îåÔSÉä7Ù±ßúÕVÖPÓDÃ4>VO4?RÔà+îÓRÒJ×45îÒRÏ4ÓÈÒSÏLK¿ÒB
+QCÃè:¾Ê<?V*OÓD³4ÒÆ*,³4Ó:->óUD¸èÐ:RQÊTÄ,.BêÞXÖ-¶D-.D¹ôAQîÂ-¾ÁVîò¶78DàW8>²âá1×
+»ÞOEF5Ã.ùî<788.*¶24ôè³ôH<¾.JG±ÙBèê½ì¹²8¾î:J´>È·µð<Rå?6AZݼó,0¸GB*ïãU7J.¾ù4ô6
+D±åЯñéÒCEÔéÔUÕ,=¿Ò2*Cóç¿+*S±*?*:T>*Ã4?è6Þ++*SÁ*ã,A,Þ¶WÙÄW0,ó·Yº²WGøîÔYéJÒQ0
+7ºã,*»@E¶ü/å½îÐ1ð;æÙí¶í0CAô+ANÁ0å´ÞÆÐáÌ»0,Ë¿/ðR¿ö,¯ÃõËÀ21Ï-+ì²Þ̸=A¿¸¯ÜÄ+=ôÉ
+BZ:êÇ+BZßÀ+5-¾Ý¼?ÙPW*¾Y-ÕËÙ²ìÒSñâÂã°³ÈÓSÒÂÒUA0¾WÇL³È?TÒà.ÞÒ¶+DÃTÓC*P³T?TÒNÏ4
+@21:ÑT7;.?ÖZèÞOÉ¿JO9͵Zá-ëâÌÛ<ÖÏ÷Î,JàÔ1¶**Ö-RÖ+@ôµL*ÖÝ*¿Æ+åGèÀ/Ѹ±1Þë¯Þ8JH.á
+î·.ÛÖ±ýL¿,¹1èîN1058úH·2,ö/MÚÎ-ÍLA¹QÊÖ5ìñ*DX¾K,ò¶Ä1ï7R7À-ê×Ý6ÄE´¶Â:ÀGù÷6DzôÇ>
+ÊÜ.*Ú·ÅÀÀEÍ·ç:Qб.K>¿GD**8¿ÂFøEôæOP×ÄÛõ°Q̶±Ý//äX6BôðöÒ3Oïßá¿36=ÖæÉÀF·K.1Úî*
+7²óSMòÞãüJ1à5.¹×-11ÎMÄKDö¶¾:åàï¼µ´OÎP7E³ëL*îE³êÔ¯5î6-¾êØWá´WöѽUÕ8G¸ñèã-ø4*º
+ܸðÎC=ÄVAðOéÕ4*î³,Þ´÷ØÓé¿**X1,Î2:0îÀ/,2À3.Ö²ï1C2ÒN:üP¾úâNê³1×ÌRJ°X³Ç-:2,ZóPA
+ÞüV.*¾+¿¶*1ÓÊõëÝN¯1ÞçGå8*àæ*4¸æ0Ö±ýúFð.1èîN145ä7VÔë2ëÚZÂ5Fîß0LQÐÀ¸¿ü8Öà/BN¾8
+åö:ÜK8¾8>¶E6Aêî2K.¹ôïCð¸ÇES¶¿,µìï,4Xºä¯Á>QF:Ïç¾U:;=ÀÅP¸ÀÕÅZQä8¶íHÌ×·:Ñù+±Ûºâ
+¶²JºZ½Z,±+ôèJñêY2ß+AFJ-Ìãï·×½Ö±YG2YJê¹ÝRÒBñJÝY*»OUõì󶵴OÎÜÊÖ°ÞÆ/M06=EÄÊB°àÄ
+Û2*:@4ÞË·éPËDµëÚ·´îÚÃé´ÌÚ¶ÌWF³ìÆ/¹ã4ËÀÃT7¾Ï¸ÓTÓ=-¾ÓVÓLÓT6*Ö41B-¶O-Ê4@UãÒÓ-A-
+¿,.=ôóè8¾õ0Ü;Äá8KZ¾.Æü.¼ÄÝÌÃ**B:21*4À7âô/±+1H¾×EÞXGU»ç8V**¶Ð-Ã/M.´G¹ï/XBà/8ö
+ÊÉà0ÁѵÎÝ1Vï,6<J1×Ìû;HÂ1BK×Z±½ñ9µÀïÀ78;.±åì¹Áì-÷ÛÈÈL¿=õ¶,/AHñÖKÈ=8Ææ²Þ?ÆÆ+éá
+8ï¸ôHÖE35óÜÍ6/0õÚQÁã?à1.øÞ.ä,.Çï¿*AF*º6AFR*KàM**SåÌâòÞξRQÎöÏ俾à*üÈÔ8Qê×ìêÈ
+JÛÕZÚÀû4ÔUBý/VAZݾ÷,Ô0*¼Ã+öZâÐSÁô6سT7D²êÔ³5+îíÔ»ÑÄ7ׯÙÒAUØNË4óJ*Ú>SÐJËDOTÒC
+¾Ö*=³èÒRï*:èFJ>J6¾Íø²È>WßÞS-ï°:RS=ï7ß·ôÇPÄ¿ÊG-ÙÅNò¾/ÌÃÞÇ3¾/ïNÎùK¿ØPñ.ݼß-ZßË
+8Ú+Lçëß/MÚÞ+0ËÀDÓõîó2˹ã3CÎÁZíû09à*ê÷¿ZÖÝøÅM,G/¶Â:;*@Çâ@Ò9O+VY>¶ð8ÕØðæ:ÌCâ+5
+¾NMLÓ+ÅJLJ:Y¹NHÖI3ÝòÝÍÎ0MÍVÁ1+ÌÈÊ==B5*O,=̶J·ß¿,2>º5;T1P..¶4ÕÎM8;¹OUÅ=ôXµ@OÎ
+Ì6B°ßÆ+EP675´êÇ+ίàÂÃ+ö¯äÔSÙÄWE´8ÌEµéÞ·ù´ËÅá@ìD¸íàSññÎSùãUÓUÐú?TÒP×LÃ4?¸ÃÈÓ2
+*J?4JPÓT*R1BDB-2PÎ×V¯áPÌØ6.4ÛLBõÖ÷5ßïEÉÆßÌÛÓGÍO¸ëO-Ð*õ-@¶>,Å.TKCùëECR±+µ.T9.
+XÌA*íÓ0*èË8FßGTÜËÒ5ĶÀ-TÁÄðµMʸ+7AÞ:-¸¾ðÅö:HÞV½Ì¾ûÓ½F2Ä=´¶Â:+*²±ÅLµÐ¾4ÎPõJ4X
+Úá/>F*Â-X¶ä:QÐ-´83Ã/Æ,Ú9¿Â.°úL=G׹߿ÕMÄ»7Váºí@äÈ0Æ2íâ8ÊJ*¾ç,öúõ=L¶OÎFËÒçè¹±å
+ܸ:ܾÖåÛÒðî*ÐNò0µ²óVFìõäÐ.è¿/-PÊÕZÝÀ¯ZÞ¼+ù?VÖ-3îß¾ûø?ÊײæöײçÔWá@W/¾Rñ@,*ÞÃ*
+ö¶çÎ?EôÉAWNÉÒSµ,JNÃ4-¶:*-+°TÒ·¾5+ÎÒ¶7-AWÚ6.è×áÛÒà*/OÐÊî¶ï3<°åìEÓ/ÐJÅÈ+γ>HVî
+R.83>ìì>ßÀR/Mµ.È1¼*ï5ÎGêQ78óÞ/¿D43Bç¼ß2Q¶JOWà8»-ÈAÎïà.<Jűº29ZA:ÒáüèI8.á3EFL
+Æ**<H+æESJÕDFº:/Aìï,48*à+AF±Æ=ç;à=.3Ã/2W58¿Âê6öL=ÛõZOÌ>ñËÛC.Ä»ø>IÉ02Ù=Ü9ÊJ*¾
+ËÄ>NÊ=L¶OÎ6ú×=MîZåܸ:ܾÖ1ùøPÇà**ßEظ@8õ¹±S,ãôÚËÙÔìE·¯*HXÙ·ëà³=åËÁñ´8ÚµíÞ»ñòô
+çÁ1YI¼úúH½úøëùµYIÕõíܽø0ðEæ÷CÚµ:IÀ÷ìC³ë¾3¹:*>ÊBZ¿ÕÅ.î¾.X.*ý°@7H¼/÷OKÎ×Ò*VÍI·
+XÕ+>ÍÓ6ìøß¿ÍãÚ8ã+òøÄñ÷ëßÇHNñGÕ´JÔ¹TOËÈ3<Â1ï4Wô>Ø*ÊãKÎ,VÖîM:´ìöè*È,*×**ÍÄËE´ê
+Ò³ÑÄÖWé@,¾ÛSé´7F³ïP*úD¶çÎ7E0VAµãÈÒSÐNÃDOPÏ4123B.¶µ*Bè>TßÌ/DâXÎ7,034ò÷3ƶç@B<
+Âù²9¹,*î¹5ÌÚ<¿¸°ÞÊ3U06C¯ÜÊÖ²àÄ/EP6<=ÄêB°¯*XÊC²ãÄGÑÔ7±518Ú³ïÜÓ+¶³ðÚÇÑ´XÙ·HËÛ¶
+ñÞÓ5QÌÅU1Ë×XÛXãàÔZ׸Óè?UA**X//öUÓZÓÀÃ4ÔT8éÒVÒº/=AÌÊùÔ1/ø¯×ã·¿4ZàX.*.5/:ßÀË+¾
+Z³*.ÊB¯¯*¶6زëÔÃ*B³êÒ·5+*îL*êÙ²ïÐÃ-¶µîÖ¿ùä7D³ÜVCZÚX×<³T.¾ÇLÃÈÒÀ*N³è>SÓÌ6¾ØT@
+7Ú²ïܽ¿äRÎÌËB±ßÔ3UPÊU=ÔêÖ²áÈ;ñçÄ;U07C°äúC°áÌCEPÊCY0WD¶éÚWEåßÇñÄìE·éæ÷µñØ×ñ´X
+Ù¸üËÚµóâÇ5åÌÉEA8Ú±â¼÷ÐÚ²Û¸óÈÓ:*R?4ÞUß°/5?VÓZ·T×RãDÓÉC·,8ÜõYÎS4Ò½5Õ7F¶ïÔ¿ñëÞ·
+ùä7F´ìRÙµëà*¾Ù¶*î@:ÞÛ·ñÄëEµïâK¹ööãéA,*úË**ºÓ*±*¼YI¼*÷ûáÅÍåÉåÌÚ´çÀûðܾûðã5BZÜ
+ÚÕYÞºûøóUAýóUÖXÝÊßÙÅ*ì5ö13ÒZ¹ÏÓ/èË+ÙPV×Zè·²àÒ;¹´ÊØ°Ì6C²âÈ?¹ÄÊ9±ä6ׯåÈ?ñáÊ;M
+P6D±éºØ¶èܯMAìFõÔXÙ¶êè»=åÝÏùPÌE¸ïâ÷¶ôâË=ÕÌÚ·X8F±ßº÷èó4Ü°óTÓU¯*8U?VÒ°ÓÀ³ÈéL/ê
+عïè»ÉÜJ+¾ºFîÚ2ÌW*¾¼¶+×NKè2Ǹ-ÌONĺ·>+à+ñ/;N=3Ò.MôJ*ÎÖ¯ßÆ75**àì.J;¾1UäVدãÂ3
+58îäÆWñôËD¶èÒE³íØ¿ÙÔ,*ÚÃ*æµîÞÇ-AXV>¸éÄËØ°±ÎêT?TÒVϸ?4ÞYÏ°³è?WëÞÏ·íäOUÎ*û¼.øÊ
+0à,´:¶³;EçÝ>ú:9UÚOÎH@RÝP6D¯èÂKEäÓ?U´VD±çη´æÎGÉäV×°8Ë×°åÊCÙôϽã5õéÎúÔëñæÌ7Ùô
+6Ù±ïFÙ¶êèÃ=Å7G1AXE¸ïà¿MÅâË5A0ÞäÏ=Qì6¾ÉáÔÊÖZݸ߰ÖTÛL?0ÞQãT/éÒYíæ;¶õàãJÎHDÍ,Jà
+°9²Ñ¯åX3ê-5Zê+ÖáZHÞ=Ëã8*K1K*¹ìÏ@VBÓÙ´PN+/*ó.ï,ÌGù>¸×¹àÂÛ5ìÁG¾À7Z¯½J.á3P¶21*X
+Ê,-***¹³28FæÙÖ**ZÐîÀBÄ.ä@>ùA4>ÖáüÀ3T+ÌÞà/SÂÃFÆÅ+*êÊ0¿/êYÆKÛá*JÏÞÇ@>4Ò,Ú¾Äè¿ç
+Å-*Rà¶Ò?³:ÅNðìAÂG0Å6ñ*ÎùÖJËÖ°áÈÃ+¶±âÄ7MPVC²ÌëD´æÌCUÄÊ:U@0ÞÇ7UTÙ¶éÎ5¹âÀ-IOÕêB
+¯+/ÜÊزïØÃÙP̽=åËÚµïØÇMîâù:*:XF²B>Æ5DóçÆ+É/é?VÔºÔTÕTßT/UA*AÆ4æãJÎÌRJñTÉZICÇ
+¯ÕGâø-5Zê+ιHH*XÍ.öVÙ;Ø+ÓÊõëúÉ>Sìȱ1ZÌZXÞ×±9´:¸<?Ò¹¿üÖ-Rµ¸+ô1ÂZXJöÒ6O¶ð2ÄGù-
+ÎYBá.ìNß/7Ê2ÊWZôÍÍM̾Gù-PR/¶,5ANß*ιXTãá**/LÕº>Þ+CÒ¹ÚK¿=WR>R/¶+71âî09ÉB8³O7â
+;ÇÚù/Kõú;áò²¿¸¸ìì¿EõËÛ¸Ü8Ú·ìÜ¿U1ÍùÙÕYܺõìÏ5Ñ6ÌòTñA9ܸêÓG²åÀ/EúÅXG2AÒÞãßMåYÝ
+¾ü.ø¼ýú/ò1îI¿îí6¾ýùAîI¾*/06,î+ùóÑÕ8F·éºØZåÀ7ù/WBèPíÞ*À6.4:Âðñ*绵î7¯Ú55îê+Zå
+ì¹Áì-+ÝJAÝÌÛö,4¼±åì´YæÞù6êÃ5¶ôê±X4E6ÌÜ<æí;,+¾û+;Ê5¾.áÃÅ8¿.ãÀûÆJQ4XæÞÂÞêÀSåTY
+Øο-PR/¶,5AN;¶îJHèTJöJ¾5°RËR¾C>¶Ð/Ã,åÎÏâîL4À*ÈB8³O7â;Ç>OF÷Ã=DT϶L*7VÎH6ׯâÀ?
+¹äËQÁ@êÕZßÆSñìÜ·-õ7Ù²åÚׯáÂ7MAõ¶ýü»û÷öãÁíëµ¹HûBî»ôÒÏ°ÜÂ/Cîµíà»-µ7ÚÎ,*Õ**âÛB*
+3QìÃ*¶¶ëÐ/éãTÔTüT?VÜÜ¿E18ÊÉ¿J/-Ùã*BçâBÆ×ÝÖ¾Ï@ÉN°5èÃXÒÙÁ/;´ØùÀ,à×Y98¶î,ÌG¹æ>Ò
+S»19ò¶J¼ÀûÀ<ÀTFT¼F¾=Ñ2DJå1À·NÎÑ*A²îJ.²µ1Ëä3¼G:î¾3T+ÌÞ࿸Ê<ÚÆÅ÷WæG2îÀ6ìÆZÏÞ/¾
+Úèà5æRG¶;/K*O6ÅØÐ0ðÆN458ûà;á**³I»VÎÜWC²ßÒKùôËÍé´ËC´çÚ»ñðسùPìÙ´ìFF´äÊKñQA¸õ¼
+ÚøúøùñõíñÙµÜù²éÇý¸»íÐW±äéA¶üËÚµòàÃ5µÌÆEµ0Þå×5A8Û¸ñâÃ+ö·óèÏEåXG¸ü7Ú²çÆ3áOÉàT0
+8G·÷Ð:Rµ³åñòØ×±ÝçÈÆßÌûÃ/6³ÏËG4Ó¼÷ðëN¿°ôÙµ°2T7ÂÚ³/6F¶¿:*ÓKM÷Ù;PÛ*D*ÓÂZVÞÞF³1Þ
+ÇSæ,Ú×ÙüFß+AÂ/Ò>Úο=ÙÞ1,Aå5¾µô1ÊîJ+XÞ࿸6DÚÆÅ2*V.Ì÷ìêYÆß73@4Ú*ÆU,»+7Ó-Ñ91*ÎÂ
+ïRUèJ8ÇâãYFð:T¾DÛ´@RPôVB°àÒW5òòìÚ¯-AÌÚµéFD´ëÔWù08Fí07ØÁÇéQôáKÏó7Hýýýû¹ýùó9AÌ
+DõãD¶Ü½ùÌ;5N?ëÞG+*3Aì¿,ƶð,,ÞÙZ*:ATD2¾áO5P5A¯ïà,Ð:RõËèÀ,GÖ±AX<Nº±+MWNº,ÎM²ÌL
+,PصµÈZ¿ÄàÛõ112-08ÚJ2¾ÈØÂGø0>Yºí+;65BÏ7òK¶ð2Té,ôA:Ò¹Þ/¯+?/A²îâ@òÞ1:¼+ØñÀ38>Þ
+/VÚ:ÃJÁ5÷2+¾Íß:Z¿û/CÚ;ßL¿MW¶L*AÎϸîLIÌ2áÏÔÒíF<ÇàÀMݶâKòTÁÁÃ4ÛÇÎå/TÖC5+îçà³=Q
+ìG·ñ:Ûºòâ¿5Ã.ìÄ=ûF·íæPYÍ@61*ÞƾĿÁí9ü1*öü÷Ë9ÕG¹ñæ¼ÔG¹ìâÇùôÌÑUÅXH¸òæßñòæ×ÁåXG
+¹õÆG¹ó,JM,RÛºõà*ÚÛ¸íä·=ÅìHTA9Û6.D-4ÎéEÌÅ,¾ðCÚJ@,JçYÀ·±+ZûÓ5FÙ¾-,Û´ºÏóê/Gµ¿áÝ
+çé:¶XÎHÌÚµïàÏ5ÕÌáá1Yܺúöëñ÷òçÙAíÛ¹÷ÚÛ¸öôÃéÈÛ¼à¸/¾+*OáZðìðûÕÀøüàÍÙôW¸ñÂöZÞ·öô
+/C¶¼ùöóéÅÍݼ¼9I¼üüûÑå8ÙÙQYH»ûøÃ/Ú2**¶2:¯¿È¶ç<¼¿=1Z406·EßZêÙJ3¹ôð@ì7ûPÎÌWC³ãÖ
+SéôËÕ-õ7Ù¶îÞ·ñðâÏ=Å8F¶ïúE´ëÞ¯ùØ»¼ßSN3Ò-*¾*5ÈÈÁÙY/óý÷I½8ºõåÇGL½øâ»ù0ÌGAÕ8G¹òè
+ÓUåçÓ±ÕÌFµîâS¶ëäÇ-åWƾÍÑÅ8Ü·÷æßÁðòËñýýÅJÖåKÎü6D±çÊWé@̽ñÄ7E´ìÖ³ÑìÞ¿ùôËÚï*íÔW
+GØîùµÒïÀJå*ÎÏÐË¿ÍY.óýóMͼÙõèÅÛß»ñ̳ù@ìÚ**<µ8ÛS*9AXÚ´êÚ»Ù@T¾áÇMÕ8G·ï00*ÛõYÎC4
+ÒU±äËØ´ëêÃÑíÖÇ-1ÌÙ2*ý08Ú¶ñæÃ5åçÏ-QíX÷ûí¿0Õ.è+*¶Î3åäÛõIRRå½G¸ïÞWý?Rô·5øâòêÓUÕ
+ÌG¹M*¼8G·ñÞ¿-Q̽EµìÛ¸÷âÓÁðîÏÁQìùAæ6/èÕ¯Ñô7F¸ò⿵+ÍHXÚ¶ðàÃ-µÌÊ=A2èçË=Å9X÷ýÏ¿,
+õ.D.*K>ÐUC÷5/óøÏIýGÚZÅ-ãJ»+1ÌXÛ¹õê×Á1íZ>Ò5>óñäÓ¹AÍG¸òà.âG¹íÝJ±>RÕµìÛºýöóÉå÷ë
+éµ9ݺúö÷»÷òóéAYܼì9Ý»ú80XYIò¸Dα*4ß×ÚÎÇéY.óüí±ÍXÚµè¶ÛâÀôöïñAÎJ9BîJ¿+ý76æ+ø5Ö
+ÍI½.-ø½ú,ø5æÍK½íÝJ±>RÍ´WÙ¶ïâ»ñäÝÇ-åWÙµîÜÃ+¶¶ìÚ»5AìÙµ48ßßòÙÕ¸XÎ8**+NÌËÛºº;Òýõ
+í¼¸ñÞ½õó<;äñ0ÌÚ·óºÛ¸ôì×±Õ8GOQÌÚ¸òêGR*CåìùAæ>/èÑË-µÌE¸îâǶòàGûÚµíàÓ-õWFQµÌF¶ñ
+ä7VÊì1õ>+¿0+Z+IÐûãÊÛýTRí½üøõñÏEëäÙ³5µXG¸õî·ºôðãÁÅXܸüXÛºñèËÑÅÌßEåýýõIÌ÷3ÒIÙ´
+WE´éØÃ=íÚþ¸¶ìÜ¿518F¶>ÌÚ¶¯Ï4ÌKáôI5ÈÁJÁ*>¿>.õÀ¼¼4èûÝAíÜùÔçÎײéÖ³5¸ÎñèÓ¹õìÛ2R»
+µ8Û¶õæÃ.2ýýÝý¶äQÎBìE¶íä/C¸ºïæϹÅÌÛ¸BÍG¹óîÃ+¶¹õê¿E÷ؼßJQD,7,î5òâáùùýýûûû÷òÑMó
+ÔWEåPËÙµíÜÏUåïßáµYI¼ýô÷ºõôïáµÍI»íÝJ±>RíÄìF¹ôðïéÅóãÁAíÛ¹ùX*¾éÑÅYÝ»ñìÈQ÷ÓXÇÇJÁ
+*NþKÎÌüù÷âÑÜE1̽ùä7F¶ïàË=òì/Kö¾*/üñÕÍÞ½NîݽíÝJ±>RÍ@7Ú´ñÞÛUåçǹQ8Úµòâ··ïæË=Q
+ìF¸ÌìÚ·òðÔí½=.ÇÊJG-µýýíY½H»1ü=Û¶ìØWÁäËGÁ0ëC³éØ·´ñæ×UA9Û¹öÌHºøè»ý,ËÏJ»E´ìÜ¿ùô
+ËF;Åì*¾ÍE18Ú¶ðÞ¿ÁðæË-AX2>ïIRL°,?EJ0¿ø÷ðçáÌÇÌG¹¼ìE³çÊC¹@WBÒQÑÄWÙµðæÓÁöêß¹1íùA
+æ6/4êôø¸ïà»EAìF·ðXÛÖ¾¶RMQìÚ·Ï*DYYúýçOÆÊJ/4ÔKÎÌHû÷ÔYàÑQÍåUÅXFµéÔWñçÐWÑPWØ´êº
+F¶óèÛ±µYÜõYÎC4ÒåÙQíHºøèÏEó²óìßÉA2çñß¹õXÛ¸öòS¾ÀóùÅPJ°,?´@1¿ð÷òãµì2YÝ**ÝQ9H¸ïÜ
+·ñPÜ¿5×óïâÇ=ÕÌGºö*ÝõYÎC4Òõá1Îݽü,ìñøòïÁ1YH¼ùFI¼ýø÷-2:IÝåYÝ¿¿ïå9@×¾¸*¿ÈýýI½úø
+̼ì¹÷ü+FÆÎ9>RîI¼÷ìÓñòæßUÕXÛ¹ôÒÛ¸øîëÉõû5²Ä,ÓìÜ»EÕìÛ+*UµXF¶+*HÌF¸ôîß¹å8Ñ5µYZûë
+±ãPÎÌSýýýóÉIIÜÁãÔËÚ¸õðçñøðãÁõÌFµë:E´êÚ»5ݾíÚ³ñäëùAæ6/èÙ¯-õWÛ¸óæߺõì×ÉA8ÞéÃ5A
+8Û¸óì·ºñØ/ÊFýôP×3ÒµøýݽúïéÁõXÑOáPXÛºùúH»øôçÁÅ8G1õëصìÞ·51ÛÃá08ÙõYÎC4ÒYÙäWÙ´ì
+Ú¿ñïä×¹19HºõæG¸ð,KÛÚ¸ñÜG5Çù¼ßVR°+ó+HûýÝIüöñûøÑéÒW-ÕXHÈQíÜRRÝQíÛ·îÚWáÄÙ³áÔËÙ´
+êܽ¿PSÎ8ìÙ·íÞ¿E18ÏUµYêÏùéåÍݽúòãñøðÓ=µëéøúOìDÇ3Ò>Î?.èíÉQýºç·ôì÷»ý,üùAZJ¾üÍÞ¾
+ýøë¹ÅÌÄ=A8ÚµðL¶Ý½¿äRÎÌìÚ¶ðæËUQÌÇUõ8ÜBRëåíI¾*ýø¼õàË»íÝè?Ç3Ò½2õý½üôåÑõãÇÔÙõÍ
+JÀ,ËÞ¿,+0,¾¾ß*ÌÍܹòæË-õ7¯5ÅìùAæ6/è߯éÄëE´ëÒ·´êÜ»EQXÚ·Ü8Û¸õðãÑQÍí¹Q8ëùüUôÆÊJû
+ÄÅýýíQ½H»µÜÒÚ¸÷ôïÙåùóáQYI¼ùô·½ûöãÑõÌÛ·TXÙ²ãÖSùü/DM¿<µêÞ/¿¸µêÖ¯ñÔWEµ+*üëÚ¸óîã
+ÑA9éÁåôûý2SNPÎüNéýýóÉA½H91øËÚ·òêÃGJ¼ûöïáA<4øÃĸ¼øêË-õ7Ù³íÝJÑ>RÝ@XÙµêÞ·ñ@:Ø6è
+ÙË=Q8G¹ôìïºýöÜõÍ9Áâ.TàJH,óòåÁ´RëEµìFF·òèçÙÅíHêåYÝ»üàJÛݼùðç¹QÌÙõYÎC4>ÔEÅXG¹¯
+*ï*Ì8Û·ñèÓ=µÌÕMÕÌÛºùø3»íõ1ÕJ°,?·²9¿¸÷ð+á±õÌG¹Ì8G¹öðçñ1Î-6¶ÎÞ¾+186,¾,+,âõìùA
+æ6/DææéÛ±ÕÌG¹óèǸõ0:+ÛØRµQYH¿¿ïÉ1@×¾¸*¾ÖýýI½ú÷HüY¾+û÷é19ÖEÏZ÷ôëñAôç-<>B:Þ¿*ù
+;¼úî»ý,ËÏJûÙµîÜ»ñ0ìEðä7Ú¾*ùÔëEµ;*üËÙ´ìÜ7²FÝFÅ?E¿¸LõýùÍIH»õ8õH½úòã¹Å8Â5ÉîìØ¿M
+µÍI½úFݺùîã¹ÕXF+åëùAæN/Dü×6D2Z4Ø6ÞÛ³éÄï±ú×GãPÎ,Çý<RýËòH¼ùòãÁQêÇE»îèØËÑÕÍI¼ù>
+H»óæÓEA²ùû5²Ä,Wö.ïàKá*JÚ¶¾C¾B+11ÌÙÎáïQ@>×¾ø+Ðüýõý¼ú÷ìCM»÷ì×±ÕÌÍ51ÌE´ëÚÇñóôëá
+AÍܹô¶Ú¶îܳ-1ìùAæ6/Èï×¹Õ8ܹô86*Þ5-2Äòöê×±19ÚÕñS¼Ûµ<=Rå*ëýýûé½IY;EòÅ9ܹß1+3üX
+H¼*+4.öÍóùåÍÜö/¹µ8H¹íÝJ±>æöL°å8ÛB¾·¾ïá=1ÎîÜÐÝíÓÉâ.èSúùýI½øìã¹á*ïßUÅÌÚ·ÌXG¹ùü
++6¶Î-.ÖÍI¼ûöë-òTA*ÛõYÎC4>Á5AXF¶îâÛB*4AìÙB¾-=¾±ëñå3N°,?ER0¿ø÷íåAìA8ÚµÜ7Ùµðä×Ñ
+ÅÍõéÅYܺôðßñõèÇùôëÙ¶ð.F¶íÝJ±:R%%
+d
+643 83[1 0 0 1 0 0]sl 8 mask 0 83 di
+/mask 6723 string uc
+*Þä**¼ºýÎöüÉõûIòù½æõýÎíýKÝýÁ¼ý1ûü9øûIòù½æõýÎíýKEPR*%
+d
+/sl 53369 string uc
+äÕí´ê9+¾Ñ,JÌF·ïäÇ-ïà*ÂÚ¶±+M*¼XÚ¶ïâ×¹éÜïµB,¾Ú*ÞÃ:ûýIý÷ðç¹àùäÃùôWÚ¸89I½üôïÑAÍÝ
+Éõ8ܸõä¿-ëà<LAÂýýÕêïûUÞ¿,Ö´¼ûظTÎïÁKÂ*Xæ±6JF*Ã51X2²F¾Ü¹ùܽ¹/<=RÍðôýýùãÁI9ôÌK
+¶¸öòïéå9Iº:Y2ÒÖMµ6¿Ú»5A°ïû5²Ì,Óøìï¹Q9Ü3*ÖÕ4*ðÃHÌ*æºýôØÕÍW°+ó*ÚBüýݼöðûúÜôòó-
+¶:KµÆÎÞ¾--üù1*0ÂD»õðß5Ó:õôÃ*ºý,ËÏJÛÛ¹öè×U1íÛ¶*¶A0*êÃ-Â+ö¹óêûÙù¼ú¯×3ÒÝïûݽûïã
+Íõ¸íïùAZJ¾*ïݼúúó5ÉRÄ2,BÓ:óê»ý,ËÐJ»Û·òàÏ=ÅÌFTµìÚC*CÅX7¾ÉEÅìÝÛò¯ÁÈËJûÍøýûå9½¼
+¶ADíݽüúïÑåñçÉõ8G·ðàÃTÆ·ðL=à*LBÞÚõYÎC4ò¶=ËEAX6ÞFJ6*ƹZ+BXIÝô±åPÎH¾ÏüýùÝA½ÜÕ0
+·9I¼ùôëñøîß±QXF¶ï*ÚÎ.2µF;D+ÞÃ+ºý,ËÐJ³F¹òæËUµ0:Ø2èÃCÄ+Â0ö´>OUýXöGHîKP,ó.JûýíI
+üöó·óÆûðßÉõX*¾ÖMõà¯Q=æF¹ñTAJÚõYÎC4ÒáÙµ9Iºúòïá÷öçÙAíH»+*²íH»ï*ÌÌSëÒÕù½8×»7Æä¿
+².è9âøýý¼øðå¹ç7/,òÅÍܺ-2;3Ï*Ï3ZYÓLòÀÞÙõû5²Ä,Ãúîß¹QíË+Ö¹öðß¹Z*8íTêÏÉÑQYIùåÜöé
+µS6âKÎÜßØýý÷ÑAÝܶ´BÖçêßÁåXö+Û¹QY3ÀÞ¹A6*ìË+2¼öܽ¿äRÎÚ8G·ñæÏEA*:8:4îâ*ÚÜÔÔQAåÌ
+»ùRRûYºîÑèØâÎμÀÝýýóÁAYÜTäQìÚ¶ïT<X6Þ×/Ƹòܽ¿äRÎ8íFºòèËÉÅÌÒMA8*èÃ-Ö¸õæÏ5-îöÚØ
+PûºùòèÛ¹÷ðíáíY½¶²6´E5+X5ÒõÞ÷ýýûñãÑAÇû/E¶¹óæ×±Å8Û¹±*ÔXÛ·öä*TAÂÛ·íÝJ±>ROµXG¸ñä
+ÛB¾îLBõÞ*FÛ¹=¹Ô³HG¸ÅÌû¸öìçͽíëõíYÚíÖÒX*+Q/èÉÆûý½¼öñãÙàòÔ·5ÅOñæÏ5Ä*ò8F*ÒU-¶Î5
+íÝJ±>RAÖ9J½+ûûñõà*âI¾³*ì9J¾.ùKV4ØUÕXû¸õïáÍõïçÙÕ½Ýýü×WîÊÓDF>R1ÁøýýøßÍIíVåâåYæ
++ú¹¾**Z6¾/òåYÝ-ÓZ*ù+êõû5²Ä,ÓöîÛÁ19ÜB*ºõìGº÷0+À*è*FÛ¸ô.ÈNèظ1Ý»úôìÑÑ9íïÉÝýÜý÷
+Ù+5ÔËÌ3ÒÝ×üý½úëåÁAÙ·ÌF.E*¹Ï*Ó2=*1NZ2îû5²Ä,ó÷äëM19G»óè*¶ÛºóðóMÖYÂïJ<Ö5.óó2àÒØ
+¹úõçûøõñßѵ9I¼õýýýú×ûü.-8F;Rí±ý½ýöåÕ1Y´AG±:ôêõÀ¸;+±*ÀXÜ-ÓÞòôÏɵÌýýí½Fñ=RKåX
+â=×UõX²±6ÞÚ;XêTôO½ï+ìXÛ¸óìË=åÌñáƱèëã×¹õñ×Õ9YÝ»ýóÛýüýéÅ8ºïÎ-,?½ü1¿ðõóÝ11VñÜ3
+Á¶.RA@;4JæÓ5Ñ»:,.9Û¸íÝJ±>RÚÅXH¸ùà4¿ÜöLÕ1YâÖ/¹ç=éÅÉ@ʱI·óî;ºöêË+öºøê;FÇøùöHüù
+öòíÝÍýÜî½Iê3Ûå0V<É-,óúçŵÍܵÊåFÜ·ïîß¹å8H·õXç¿å¹AÍG»ôîÓÑùèãMµ9ÛõYÎC4Òý-æíݽýú
++6.î+ïGòEÙîÖ»»öã¾CI´WôKçµYÎJö¾-úÅYݽ15MõãáYQYÛ»ööGºûòû±ýý½½9ݸY¸>LûõíùÙ1Ýüúì
+,QC¹ôÞßEÅB°B´¶î**Z/-*úü/DM¿ø¼õúßá1ÍI»ÌYݺ÷ôDzèÔé@ëÚ¹æÈ/ñóÈEåÌù°O½óºH¼ùöãáQ9I
+Õ主òÛÛÅIíçÙµíH»ûø3ùìçïýýÍÝøæÜÙýüùéѵýÜå8æSØÀ/çç-ùÀ5æH»ùÀ6âHºß0,YÝB*ñAYݹíÝJ
+±>RYA9Ûºôð×ÁåÛãÙµðÞÄVSùâ»CYÔÛú´ê̵íà¸+ñãRÍñ-ÕXÛ¹óî×ñF×ùIýF´ïà»û÷ûÛå1YIZ/¼A·ã
+õÑÅÀøúü÷Õ9íÜúçìXÐY,5À±19à±õÌÛ¹M0á0ÐÌÛæ/ÂÅXÜ·÷ܽ¿äRÎÌíG»óöãÑõÌAÒR¯àÈI=Mô»MIHê
+÷ïë×ÛµïÇ5¹ÄSìHÝRòß¼óìÛUåWñýÝI»îÄ;ùâáÛååI½ü÷X¼´íùÕì9ÝQåÍݼ÷óëÑõÈÓOà0<RÁö¶Ü¹÷
+ðã¹QY7¾ç±µ9ÜæÃ30ÎÕ±.TÆÕË</ÐÁÁ>ç+ZQî,¾÷<NÏG/èëã±1íÙ».Û·Z1Yø.¸CóßHCñÞÎ-½äúÖÝ0W
+ùöñ´ñO±öòóéBÂè/8ýýýýüøé¹éÃáü/êúøüX½ýùõÑIÕÛÝÕ½üHýøñáÕõí=ùñµ>UÑ4?Û¹õîß¹A2Z,¾êß
+¹A¸;À¿ð×ÑAE¿4**86ÞÛ*ÆîÉÕAKL0É·0AVO+MÝ×U·JV*»Ã,¼¯Å+6Ö±íø.?*à°²¾TÕä¿-µéQ18Bß×;
+2ôê9óJ¼-ZÕX±õ¯8Î2Z0î,,âå»éEÌH=RYRZâÞ*GóEå7ÀêåZWåÇI¹ç½SU¼Ú×ïèÜ·°äÁµ1å7ÖáNÆ8Û¹
+4÷ä@ôûÅ¿øýýñÑõGõèÎ8µîóøûýÝýÜÝÕÍüµûùñÅõôéYí6ôèÕUÛçÃü4@>RZKQ¶ÎÞÀ-54²æ1<>æÎJÀ,9
+L¿./øý½+,»5,ùï*CÌûÖÀÁ×SúÚ±ê?EAOYFL+õ×¾Sòæ×±AßØ@µTKʾ4ÂѸóF2Q18Bß×;2ôê9óJ¸-:N
+çÛæåÑ,OêWïÛHð²C¿ø¹:ÉÔÔFÓÞ·ìëHº7»4ÓÈÖÉëF÷ðçÇKÅôÃKA8GØ°íØG÷êÀ±0ÒçÔ²YÜØû÷ûßùXì÷
+½½Hõä½2C¸Ï»ÍÕõíݼô8G¼üóåÉYÍÚYÔ4?èÑ8ãÏÑ÷ðßÉAYÜB*Ï1íÃ*î¹øìë¹µÍýF/è»Ñà¾+Óíê×Ù¿;
+,J292;ÖHÁ8SJ,ã×1ݯô3@FL+·ÁSO*<÷ÇGRVï*4ÃXôêõX·ïÔã.ÅÄJ*Z½Ô-Ç?¾FøùÄJ¯óSMòÞG2îHL
+T×ÑB¹×ÑÐFÂô?RýõWÞ˱EíêÝ<.ÕËOØ×¹¹à¹ñ°ì6øðßÌE²ãÏ;åôºØéHWÛ·íë7ÁôºCÙÔËÜö÷ø»½úôë
+EíØÌEYÔìÛ̹Õñ½íÕõÝIIºúõÏùáRÅH/çìÛ»¼íܼúôãéµ9î5-Îùò÷ÑÅ9ÜB*ÐõÍÜú.ØÞçS+Æç<¾Tê×E
+.*JÝH>¶Z*é=RÊK<·Â.à+.äZÅR*Aòïßßä¾OEJÃÙó;,ØJ·ôêÉE¶Î-1Êîæ2.¹ôå¸:×,Æ3ß-G5*>ùV8Í
+Éû=DÞ×ùÞä¹ãPê¹Á5É-ÌP=RYõêE¹7½XóêµUÍFÓæØ°C¹êàY±ìÉBíæHøðìѯÉÜêعÄGÙñòÕÇÕôů½ÄV
+¸ðêáûöïûýõ½ÍÛWõÕëG»+A°»êÓåõÝݼúõÅùàPɸNÅ8H»,YÜ4*ÅõXHºøìã±Åò×ÙA4Jð×ÉÎJé*ú,*àÃ
+-Þ¾G¹É,*Ëã1RZ»+BÔZDGòÖ=ÎK.½4Ǻ::ÌGù+3ÇÉÒ¿Û°õ/ïÜ4>M.¾¿KP¶Î¶îÀ387¿ZåüT1°M*B:P5
+º6.:9êÇ6Â,óMθG¹ðMÖUI2ü-.è÷ßñå8F¹ûV¸Ó²U9õ¼F°Ò8³êåÞÓõìêÖ1ÔW¹´éƵÉôÉ·ÁPìD´Þ/ûK
+àÕ¿ÅXº¹TýQÒTïðýýÅíÛå7RÎKÇ4W¸Í¼½ÙµÍ½¼ïXóܸõôïÙµ9î5-¾ûöïÑÕYZ¾ôÑÅÍH½ø86JÜ,/âÅJ7
+´N*ÆFJ¿?4²2*°ÀÁ»²PQ,4Ö14ö7ß×AY¾G¹0ÔF¹ã8L.òÈÚê×ñã°´îÂ387ß×8Nµ*,,÷E,?RöÔ2.¿°ôê
+½Uß8FÃ/<QÁ3Ò1²BÎK¾,óÓñø0ìÊÈÔòîêÛ´àVÑØW»û¹9üõWèÖ¹áôêÏù0H×ìëÚ?¹Ú8E@ãG׻ϼÌE¿-O
+ÄûüÝýÍYÛRÃ7¹øòM±ôò3?êÝY¯N»-/8º+*XîJ¿,/<>¶Î2NB8*6Ì+ƾ,=?RY/JäÉ*T¯åXüÄH2O5*âXB
+ÖH¿¸óçÍ@îÇÛ9ÞåRÞÍË=H±6ôê±BÅã³ü2;Î2ñÖ¼ëÅà×+îñÀ387¿ZåüR1ÐE*3âö7¿à²ÐPUíû¾JÓ÷¼äÖ
+åÛ²·Þç²,ó*ù÷é1ZݼúúܲñôËÚ×?YÍÛF¹îÂÕ¸Gêõ¼.çðæàÐSùÖZß´âÖ?éäüFïÀ9ïGFSÕÅËDÏJ´íÀü
+õúýùAI×ãÁEGPÌ:A0F¶XEQÅíI½ûø÷ùA.:8:6*ùûáÕ9ݶ*ÖÕÀ+öOTÎüù>/6T÷äÌÛåJ:MOôêû¯**40´
+0Î+*ÈG¹0,¸²º°+³9Ñ9ÜR+?À×±ýÈ.;Î,µÎ¾XÝäß-¶éíòÞ¿,ÚBÀEGÆ;*âä¾µ.4¿I¹°.Ì<R0Ñ.ÖìêÁ»
+ÛDî²E¿D»úîëE+î÷òã±AëE·+GSØÁµQå¼E±å3ÌÚ»;Y¸VæóS.ø°MÂ,ñ·æÆ3Ôºçïݾ¼ÐSáÖÏüËíÝíÍýý
+½úÖ»ÈñæÀ?ÁBïÞÀ,?I¶ïÞ¿ù@*ÞSXìX¸á¾5õC»÷ôÛ5-ÞóòßÙå8Iºç3²U-+WõÂùë3>Ç4Õæøú¾Ì5Ò÷á1
+:ÝC*íÕíI¼úößÑäóÏN4Óëàßã¹ó÷,ºÅ9àËÜîÜ¿D³ÜáÅÕÍÕܯݶ@EPó.C<Òø?´÷ýû½ýûéýåݼµíøX?
+µâÊSÁäÇC=äëHºóà÷¶:Ó<»G½üú8ýæ½úüïéÅ9úUÛ*üÜ-¿DôÚQù¶9*àõø½JÎX9I»ùòëÙµ9Þ5-2,îøìÇ
+á0ìßÑWGíçëç19V=JõQîâÇêðïºë·áëVü÷×47øÊPóÈõèðEÂüðòóüïõÍýÝùåÍH°·¼FÛ=öò/*ö»ùðçÙÅ
+9IÇXÒåÆFÅíÅí?/ÚAYêL.¾î±õI2òµ2Îñ,ó85XVÖ¯KÃ07MÁ39Ì+¶Ã0;8ÚåÌHÄÌòêßØååý7ÖÉúÕÏáÂI
+ÁÈòëòÏGÝIýß¾»÷ëÚÃÑÍWÔN9õ¯UíùûíÍíÓá0Ê>Û¹ù>øÁ2=TÂ2¯àÂ8ïKÃ3=HÚ2ÏY.÷¸¼ùFCDÂ47´²R
+¯.áùAæ6/èòïѵí¿*Ú-*IåëÙÄTïD0ìáͽËúV¾ðø·Â9AôûÌ»±ø¼ØôáìKÍåÛÚYãCÌÕ¹õõ9ÅÄ1Q»QìKG
+F·*FÇ=øü<U@àB*ÝõYÎC4Ò-âÅ:I¾û0ð±ýú;êÕ0Þý/êå9J¿üú·¹îâÇ=C³XâXù÷öÛ÷XêQÍÅÚøD²çܯA
+õ×»Áôù±âÍ*ûGÆÜÅÉ8Ú@H˵íݽ-û?Eö¾+û÷52ÎݼHîI½û2üùÕÍ5FìÝNÁûüïM0÷/úü/DO¿¸»üòëѵí
+ܼ;*14:8*öÛ**YåëE·<×øÓêËÅÍÝøéÞÜå¸âáïMÌÛÖ±ÈüÎC²õÆèÄ9ELäEÖæ¶Qâ:úîÔ*FKßóEEÚÕÍHà
+Q9ÝõYÎC4ÒEâAZÝÀû*ôñ/û3úÅÎݽýúJ½ý*4òõYJA2:Þ¾+-,NæïÓᶳUÜÈÕ¹úùï=½UAïá¼·P¼<÷óñÖ
+ÚÑ1ÌÕîØ°Cà-÷ìÆK׿1úåÍJ¾ýú7²ýüóM¶0Þ±°õ½°I¾ú6¼ý,ËÐJ/KÂ;*îî.*>F+*0M+¾ß-*î/7Hº2¯
+àÃ4ÛH¸ùð7ÛTAZÍW»üóí/ùºäá·MTE»æÇñàÞOOðEöÆ6ÌÖKÚßZÞ¿NöÀ1¹¼õIñÞÁÀ:àõYÎC4Ò8òÕÎI¿ü
+ä*FK½++8òAîII2ZÞ¾,-,6B0Þ.0N¶Z¾Ð9ÂAÌG¹NÏè󾷱ͽGVÇÕÛùë´÷Ä7EÌG.æîݾü8Æ¿-ú1îݾ+
+-,>*/0,ÂJƽ-Å0ºÝ½ü4XGýIKàµÎÝõYÎC4ÒíñµYÞ»ûôóáúøóñÅYI¼¯*ÎYV¾öùAF:.Þ-0FRîJ¾ûø÷¾
+*éñ1ðSÐì@YòíÍAÔÉ?äã·<Ú¸KËLÎݾÁ5+*úÄ8NI¼;1ã+AD¿õCÒ6½÷Áùܽ¿PSÎìÎÝ¿ü4ü=öÍ:úA0Þ
++4.Æ:Þ¾+/°¾+/462ÎÞR*VöÎK¿.á<»ßÀ05èUQYMµø=ìÔXÇøEåó;²BZLÀ0/¸Â.38ºBÎÞ¾°:Þ÷À¾¾¯-
+2¶¶¸DÏTK?ÔäÍZK½íÝJ±>RñQZݽïÂøíݾýúû¹¯*6ZÞ½ý*,úA8Þ38N¶îJ½ùî÷»úú÷=BÎß¿XìF»óÎä¹
+å9B6û.0á*RK¿.M*æÞ¾--I/Þ½M0.ÎI½;1àYÞ¶*6æíI½,á,:´ü1û3úü/DM¿øÃ1GPÒ2¯MÃX;àÄ2?PâB
+ÏUʶ¯àÂ4CTò3?XÒRÏáÅ:ûMÇ7=<ÂÆÎKI2ÏßÂ3K°6çCøÉÅ<áÈ6SàÄ>EÌò¶,¾L¸.C,JI´.ÇÏLÃM1N¯à
+Â;1*¯Nà6*N6Ó¾Çó=ãöîùAæ6/Tú/V+**Q*ÞÝM+îýü÷ùõ9Þ¿/-*úJ½ùðãÑAYHÝQíÜ»*ù3>25<²ÆZß3
+¾Æ¾î¿B*N¶:ß¾,-*ZÞ¾+ýóE¸¶Z2Þí¿TR½¿çA0BZá2ÒZ¹ïU4>IòÅîIÁ*1Ì+¶¾/+86Æ:J¿Jî¾Ð9>BÎÞ
+¾-7Lò174FæYÝ»ýÞÜC*åQÍJ½./PV27DÚö:àC*¸RïßB*µæîKÂ/5DÂ27DÂÖîÞZ-32Z.L²,/úµZ.¾DêU
+½íÁü°=R*:*¾4>×DQå2**ô:Kâ+±ôÃ1¿*èÓ91,>æ¶Î9ÕJïÂ1VÂ+>>ì¿CH6¿ð¼+û/òQîÝC*,æ9ÞRM/2
+:Þ½++0-ö½*-8NÖZK¿XÎÝ»øðãÉQ9ëÉQYF¾ñáÅíܾü4@â15@VöZßÁ+.Îî6J2¾;F¶ÎÞ:*;2Z6>ú5»B-
+*üà<ÒÝÖ÷á,6BI¿Lôä±,¾;Ûâ¾óAÜZ/*2·¶¯ÊV?/ÍÝÑíXúà-Ì6*J½à-Æ»·ó¯Aê¿CH6¿TÁ*;8VBZRÐ5
+ºRZK¿0/<Â-7<N¶Zº<:6Øò3?PÊB;K¿üFJ½üø3êåYI9ÖíI¼,÷ûáå7üUÖZàÁ2;¸Ä2?PÚB¯Lï*¼;MÂ2
+=PÂöZ7*B6ÇJ.306ÏB-¾¹ãM²2ZÀ,óêÇ-¾Î¿0û>¹ô-M.¾îL+ºD7ê¿,Öøú,HëE?06RûHR¾·°M¸¾Þºß
+,64*.EÚBPS-òÇ3Úð+¯Åß39.*2ãX¼D¿0C0±HC¾;¾M9î4A¸òæ¯áÂ.+ß¾SJöÞ*ÊJÀ+30V¸Á,7@ÂB¯MÄ
+ô;áÅ7Q*à3L4FMÃ29DVÆ:K·R¯Î²ÖòÎÒEÊ2<»â6=PÊÎJGµ´*BûG7Þ+ãÌG,*ÎYÝR*A4*ò°*îÛ?<Þê°Ø
+.ÎèJ¿³7/¾â÷ÀD¾ò1Ø.Z8J:2½N,µ¸JæG½à-ÆGìP´Rý²,ó038FæîÞ¿,å*ºK¿-1@NRÎÞB*IÆîJ¿-5@²
+æ?P²RZÞ¼ûö×¾úøë5-Îú.ìéQÍÞ·*àÕYÝSS»æîßÂ5;Ô+ö¿+÷×ù´7×W85A³÷0D²æÎMVÆZJÁ+1°Ò¿Õ¹.
+æÎÞú.è+:Ö¿6°¸Ò*Z*ê×Q+¾äð5/î×-ÞÒLRϺÞBù/*Â51¾ßJ¿¯í*Þö¯¾?*Þ·¿8*Ê,Fο5H¾êÃ5ô³éQ
+=RñÅîÝ¿1,*+8úQ:Þ2*2B4*,8*ö¿.7HºöÎK¿Ú9Ý»ùòïÙõP*XV*ê5?óúü/6ÖZßÁ0»ß¾÷äËMQYI½18D
+ZÖR˸OÆãE½?,50í°áã×4Fæ9ßú.¸4¾Ô**·:ÞZM,¾âÇ,¶=Ì°Ú>ÏÅQôKξú/¿Ð,*ÊÝöÀ?2ÒIRÌG¹ð/Ö
+Í+G9ÇÞÓ8*ôAº:*JÄIÖ-±ºB,³-3HNBÔÇ2<NB0*4Ì+¾À¯*18Þ5PʶÏLÂ,+ø¼*ûóáAZݽÌÍJ¼ýö/âõ9
+îE12-*ûà.âI¿KÓÜZݸòîÓ=1ÌÝá19Ú±ÝX×ðÒJûàåîàÁ5ûKÂ.åTÍ·Îß2ÄF/è?*B6*¾îêýÀæÁË14Þ½3
+:?C*,××éT*°Ü.ÎTK¿Ó+ZÓà÷À7B²Ò¿ù;ÕE·¼¯ÖIÓ:Rð¼Ì¸èƹG*êYö*°µ8»O;IËJÓÞÀ154VBÜS6<
+6-Bý.-á,,.À*ÚKÂ4?XÂÆ:JùõíI½µ0*øÃ-Ê+FÖ¾-?îúêïMöÎK¾òºE¶ñÞOù/U?I/È<Uò6LÊæ;8ÚCûì
+Ä23<Á,7äSÎXYÀJÉ0¾àHÁ;ÆÏ<ù5/îB+²Úü¿4ôêóõS4BÒðÛÎÞúô2ßîL.èCýÇÞ*9âê×éÓ¸@>¶¿RNàÛ8
+*ôQºÞ:2BàôQ=R1CïáÂ9?¸ÒÆI°ò¶ïáÃ7Q*L,2MÄ7Á*úMÆ:Qt>·;LÅöÎKÂ07@Âæ6HVBï¿+ο33Ì/BÁ
+.;@6Xóýô÷á¶ÐãÈ;»Þ·çÔÓù´6AÅÃÈ>PÀHçéæO°FÇÛ²Æ<GDÆ6MXÊËJݹ:FARQÆZJÀ.14Föâ*L*Fß¿-
+/<NRÎJQÖÎßÁ2=L>æû÷ñÕYI¼ûø¿¼á*¯*ÐíÝB*ÞA,¾úóáA6ÞíWÑÄ8LÈC»¸Í@?ð±ÕÍI·ÜVAUÑN³D²ÅQ
+<µÎMèß»PÒ17HºöÎßõYÎC4ÒENB;ßÀ.;<>/5Ì+¾ÁA0*60½¸Á/7TÒ¶ÏLÃTîݼ+ý/V-Þ,û+âµZݾ±*ÔÍ
+J½ýè*ºI³ÝÐëáöðQõDóäÅ5MÀVæëKñ/U>RÈ.÷HºL+²ºùQÅF¯MÂ79°ºöû5²Ä,ó-3<FRîJÀ-ãÞÀ±*´îJ
+¿.M.à.âÞÀ+*üZàÂ474úåÍðùåYݼüX6ÞVÞ¿+*¼ï+Ì9Û±èÒ×ÑÆÐõÊè>äÄ8±Èò-éGñÃTÐMÃFÎH¸ôö=4
+Ñà2.¾B.*>¼ý,ËÏJÛàÁ39XÂ2;Lζ0*9LÊB;àM-Þ4E´.çÏLÀ+ÑHZK¿-+@6¶Î-²B:K¾0+<N+7,6=*+á
+*úIºù8ÜMå:O½·ñPÉ47ÌFç1ÌÁô5>NÃ,·J»öâßòYâÅH°MÇ6M°>ÇïùAæ6/4=°âÆïà=-:5AH×*ÝƯMÄ6
+EÀ>çOÄòBÏKÁ/5DÁ/3@6*B425B9¶ï*XZܽ4=èéQÏÉFSÐá½ûD¸òýâ?á?T<L¿Ú9H¸îòß1ÐN5S°âÇ;*J
+ðùAæ6/è7<ÂÆÎKÀ17<Á090°Ì+*Â?ËìîKÁ/;°êÖÏYÂÆZJ½,ý+.ýè*:J¾û0øU-:ý0Hö*ÝQÌHÁ9Aèáå1
+°6×ïÜ»7?ؼïÈçD,ó½ôæ·ñäDÃ4ÛMÄ9A¼âö¯àõYÎC4>G²ÆZ¾ñ×+6¾Ì+¶À0=Tê¶ÏLÁNîݽ;ÌQ,¾úû5ö
+æ-à+ÚܲðöCêRZF9åÎåË,ñ?úåçOùÓT<LÁ,÷H¹îÖKñUJÂ@ïàÃ7Q*ܽ¿äRÎPÏKÁ0å*JàRåB*¶B0Þ;P²
+æîKÂ/5øÂ5C´ò¶ïK¿X:Þ¾*/,.öÍ9.2:J¿ý*,V1*üä.à*ºH°ëîûÑÖïHÕ´ÎåÊ4+ø¹åÙ7áÓTÑMÆ.·H¸ð
+Ò¿MÉIÀ8ïßÄ6G´úÖïùAæ>/4<Ü5*϶¯6¾RÒB4Þ=PâæÏáÅ8GTÃ018NÆZ2¾:FB,*.Ô*Ê-ÚB+ÕÕÉ×´óøC
+úåÑËÉB¯K½óÚ÷°ÜXÏ<ã3ÐJX1ÌE³ü6ùåÎAºBïàÄ9MÈúü/DM¿¸Æ6M°.×;âÄð¯áB*ß2°Î±Ö=²6/î7IÈF
+ÇðâÇ:ÛMÃ19PºöÎßZ*¶B0Þ7P²öÎßÃ09ÀÁó+ìîß»ÙÀCáÄÌåÙ1XײíèÏñìÌ/Ñó4ÓSÈÚÎH¹îÚÇM3EIöî
+LÂ5;À.SZÜúü/DM¿À¯È*ïÇ*¾Á=*-*Ï*Ü:àÃ7C´â2ÏA>BÎJ¾,/0V*22Þ*-0>B:Þ¾¯*¯+8ÎÞ¹Ô²÷D0
+˹5åWÕRÒ°ûðà¼ë°Ã4?QÁÚÍG·ìÚCNÈDîÕâÞ2Tò2ðùAæ6/èCHÒæÎLÂ47,Ãó*@;LÃ21*Jà2*¸¶Ïá**Õ
+ÖÎÞ¿-/4Fæ.86¶ZF>:6øÂ-186ÆZ6JÖ¾1F2YÓTظ+ñâÄç4ãçÑPÌÚÓVÕRÇ4?S:íM18E¹@ßø0*@ABÀ3K
+´úü/DM¿ØÂ1=D6îò0=HÂöZLÂ2ßßöåÆ*¿öZʾRêB*J>@6÷6ý62?+-Ä+Ê-*Àñ+ïͱ*89?TÓTÛÐóȽàÂ
+3ÑOÉJÏðÒP¿àÂRÍG¶Ö7D¼LóT1ZêÓ0NöîàÅ7ݽ¿äRÎøïLÆ5GX6-î6I°òÖÏMÄ7KáR¾B¾**1SðâÇ<EPÂ
+æ9@Â2ÏKÁ27ØÁ0=D6-:19ÄÎÈÁ.å-à*úHVÑRϸ/U?õ²Ç<OÈ@ÃDãOÃèâÆ9FµêîD²,×ÈÔÍß;-Êæ;âÇ<Ù
+ÒJ/÷Ø7øBÛLÎÒ¯áÅÏËå0îÂJÛáÆ>SÐFçÏLÅB¯LÂ4;Lº2:0,...¾<Â1?0ÊÄ0Â3¶¾ÞHÇTóËä¾H4ÑNÆ:S
+ð>ȽØÒÆ9E±èÖÏ´æ6ð/ÌÞ<,ÚöÏâÇ?±=R*Î*îò.X¯¾.Ýú<.XBJ4AÆM.Ú¸³8F¶ÙÌÛZÕоÏíê×ù°5ÓFZ
++>ù5>¶æ+ARÞL6JK¯ã¿Æß°ÌϾZ*î*,êà8-BÚ3Ê·*öBWÄú/,ó5=X¶¯LÃ3á*è+¾àæOÏRïF¾UâöïáÅ8
+=8ò-5<FÆZKÀ,÷ÞÀ-1<NÆZF¾:>BH:P:HÞ50òÔSÕÇÀÕùÔÕFS¸òRÐPÜÇ<L»èÊS=1ùÏÑP<²õ*@EÞÀ5E¼
+òBI¿ä*-ïÀ÷*K8¶ÆÀ/ÚJFÓUå¿ÆG6ÝSúÚ±ê×YÇ8?EYø,ù5>*êí·Þ/4ÖðèáXR·:á×ͳ>:6*>.JÍ/ÊLÅ
+ü*L8öÀæë<³Æú-,»ßË1æS>P6./39º¾UÚæïáÅ:GP..-E+ß*å3¾*渾ؾ,SIÆZݸXWíËèõÓø²Ç;MÆ:÷
+L¿òÎKÑ0ÌH4çÏGÒÜó5L2+¾0A¸.ç¯Ì,ÓD+ú¹ÜNàÞ*E2àÅÞ±,8*¾FêÉBæÎYJJ³¾øPÃ+-TÝÅ5ÆÛ/Û8ÚK
+ÛJ2¾¹ÉJÈE/ÝÖ±ÅÔû>2àôÃQFI*ÅLA¹ô¹ÕZQ¾É±9¼-¹àÎÄ@ã,ôARîÎ-XÎJZ=¶·Ð>1>DLØ<::JìZ¸,-
+1´¾ZE·Fźº9,Ã5?°Ò¶ï7¿@»Ü¶*áæïMÆKÎæZß¿.5ij,Á10J2D6ÓV-2+/0áBûPãÛµÌøÒç;½Òæ:K¾øâ
+·áéη±åÍMвSUåî7NÖZR¿<ÂæïMÆ9ñ=æ×NãüÅÀ±VÐ10BÃS+Þ302,ÎW×ýG7Þ¿+ÀC*O/JPÉ*TË.ÆÎ*,
+/48è*2**QÅ*à.:ÐHÞ7¿S@ÆJ*º¿ðê×Aµ¾»0@ÇFL,ê-Ð6;JBÁ¯½¿,1Gî,/6ìP´RISÈT¾*À-*22¾5øö
+îZ±¯¾9Þßè³ôçùö.êñÆÅõKÎ,ðáÁ*BX.8M*úáÅ:QÔV×<OïƯòÒVÂB0Þ=T¶¯LÄ3C<Â59Ä+Â-¶Ã2=4.
+çõ±ìXÒ@E´ðâ¿=Åìç-17D³,JG;PѸW-åíß¿Á¾,úÆÐâÈ=ñ=æ°*Ùê**ÙÃ+âF*-1°çÖÕPÅè/±Ä+æ/*Â5
+1¾ß/¸H>æ==*2à°9ò²,.*00@û+ôDGòÖÁ+¹õñD¹ôÀI?åü,/8¸Þ/4BïÀ»ë¸Ð>Á*ñ//*-+¶JLð<³Ò0îó
+ÕÙ÷ßX7ØæƵMÎ:°Æñ¿+ÎÆ:MÌ.2Æ:M-À+âáÆMÎÚðMÄ4ATÚö*I96*KâöîÃ-öÂ<å¸ì8CSÁìVÎC²äÊCáÄ
+å·á0ÌF».OàÎTóÈP8HÂQÞ*IÔ>ç<ãN/è4A*J²¯*31ÞßÃSÐ?*JÜ=RJH=¿È0Ô<È0Û<A¹ôº×Ê.¶P¶¾ÑßÌ
+»²P;È1RîÐ-ìN+6½LØM8/.=*/+¶M¶ôã5Úæ+CÜJ*â.ÞÕ;RãR;MÄ6A0+Â-³0,¾Ä¯+Z¯6¾µú2°NÆ9?H
+N/5<F-î03@Vö:KÁ.í6¾A.çÓ°ôØãÈÒ.5ü±´æË**Å=ÅÌG».GèâQç4ÄWÚº+5øÀ25@V2;K¶¯NÆ<KäSÎ
+¸»7³L²2I6J;+¸¯ÈX+¾ß**.弿*ÆGDJU+Æ÷<¾Tå.øÞ/83¾°Å³¿ëðUÍòÞ³,¶Â·VÝôM;¹óHûç,HÃÌá
+ÌÛö+ú0/X¸ÞÃ>BñæU4·Ð>¯*ó/M*/+öêFïCáFö*°ìîÇ-È÷K,D-¿0µÀÃMÌÏÍï*1F¸HZ.4M¸ÂöÎß:R±æ
+:KÁïÇK͵6È7ÈÚÙÚóÙEÁ0ºKÀ*Í·,ÒQ9õ-ÆÎLÆBÇÀO»Ké´XH¿1ðD6D.TLÄÊÎJQ*Òà:ãÛ,Î@B¾.B¶òN
+@ÛÜ.¿@7*ÃÛIÞZDJ½ÍоK?íá/M*-*1XîÆ*>¹îÖD>2ðN1X=¾YÝäß-¶é×±Î,¸²âìéë-÷¶¿,AGîL46,+
+MêÜCÈE/¶++ö¹Ï,T.A1Î//âFÞç½MÎÐ;á3¾-*B1¶°+Ï+;ÎBÐáÂ07ÄN4Á21JWKÁ.;Ä+¾Â+Ï8ïÞÂÏɽä
+·ÎINÖîßÁ.54òôÄÓàÂSÑQϺÓUÜʳEõÌI:ö¾6À5²KÂ6OÄ>CG¿Üúæ7ÑÆVÇH71NÊ+éÖÁQJÎLðâ3*3Sð¿
+-*Ç-*<ðâ¸*Q÷ðOÉ@S¸Úæ?°ÚRïLÄ6?ÀÃ+*X¯áÃ4?´ÒR¯Z¾°´IÕPÄ5é0À-FMÁúä³ÑPëDü0ÌÛ¼*1H6S
+R°*6á1²áÇBWàº×Ã.T2ÁÈÄ-ü@0ÚAR.S°NÇ;1*à+ÀIå,À+,+Jã2RT·ÐáJQOÖîJÁ.C0½8¼,ų*ìðñù²
+;HêB;R6J»;*HÏß¿-1<²ö:Oʶï¶JÞ*²V°2,O?±ÔÊØJ½ö@FARØƯá¶LçÖ¯B*²6-*6í6*¾6:ó<M´Êöî
+ß¾8ÛPÎI±àN2:K¹æZßÀ¯*êîß¿ÔíY³B8*4Ä/F**ZÖ,:2îÂ,8NJ*¾Pê¶ÐNÈ:ݽ¿äRÎìÏáÅ7G´úæ;´òæ
+¯áį+ÎïºJV¾¹6SðâÇ>O¸ò3=HNØÖ±âÁCíÚPÃèVBZÂÐEÒöZáÈ÷ë@N0=Hº;**¯>¾KÂö:à,¾Z¾5<¶:*è
+îLÅ>MÔúü/DS¿ÀÃKÎÏÎÜïàÄ7C°êæÏZâ¶BÈD¸.C.èQÀâB;LÀ¶Åùðâ»åôº÷ZÝ8Óç¿+/DºBÏäÊëÜàÁ1M
+EáDá+äG-,Jà¶æ<R¶¶P:2ZÂ:ä4GÌ>·ÐâõYÎC4ÒÙ>ÇðNÉ<WÌò?OÐF×<OÈ>OãDZ*;+ì°PËCµàN3ÐÁê2
+Ö÷ôðáÁõéÏ-Aë·ð×R»èÀ3?4Ûý´M*÷J*ÂáÅ?*ï*+*ÞïVJ>*³ú毾¾²úB¾ÞI¸>ç<QÊDY¼ý,ËÏJËNÆ<M
+H2¾+*<C<â¸*93ðáÇ@WغçS¼âÆ;OàçÝùùù÷ÝÕÍܹòìF´æÃëÀÓøÏéOR¯L»0A°>6AP³*ÌÏáÅ>Yܲçðù
+Aæ6.4%%%
+d
+643 83[1 0 0 1 0 0]sl 8 mask 0 166 di
+/mask 6723 string uc
+*Þä**¼ºýÎöüÉõûIòù½æõýÎíýKÝýÁ¼ý1ûü9øûIòù½æõýÎíýKEPR*%
+d
+/sl 53369 string uc
+äÕí´ê9+¾Ñ,*ÐMÆ8O¸.9Í.¾º.÷L,ÚMÇ?WØVC°MͶ;äãñWé±èÛíõÍݽûùãùõä¹ÕäºAëÑXÎKº+;Xʶ;
+KÒB¯.*RV/21:3=H¾*ÉR;àÃã-¼;MÅ:Wĺ·ÐöýýÈGܽ/FR*éäÕYUQ/DÖ÷¾FY.Þ3ĸÅ:SÐN·<âÃH¯àÚ
+ÚëÕÃ,ý°ÅÖúüýýùõüùÑ9ÍûôèÆGì×±¹86öYLÇ2¯ÒÑJºB0*:@/Fö¾¿¾5-Þ6GÈF·ðNÈíÝJ±>R;ç¯/¾½
+6÷ïâÅ:IȲ9KÀ6·0ÞF¼>ç°*¾ÉêRÏJáêñYóN**ö*å=ÀÅ»¼ýýýýíÝݺI½ÛµëÓCÑÓèû?.RZàÃ4?Ô+*Ã1
+**@H2*ÆöJ*FMÄ=MÜV÷ðâõYÎC4Òϲ×ÐãU-2**>1+ºãÈCµèÚS=PA÷;ÞÞèñáê+*6*æ5µRçUÛ½:²BÒÕQ
+í»õëÌ÷ÔÓT3LÒÆ;âæU*·2îâ+T,è+FâÈ@±èêCÑäõYÎC4ÒÚ>·,¾R̲÷J**ã2æD*°÷°ä**µCÐMÂÂßñÍ2
+Z>¿¸**.ÆÞñSNðÃÎìI½øóÙAí»Ø-ÜÉÓKÂ-9¸â8E¸úÖ¯áÅß*âïMÄ?*18¾Mà·±PËBݽ¿äRÎ8ÐMÆ9K¼ú
+2оòB0*H@**Æ-+HÐNÈ?UÐ.·;UÒööùûîFÚ:æC*â+2?ÊÙUKûýüöëÁý»ù±IºAS¾,5TÒæ>PÊRïÇ*BÂ4=
+X6-Z4;´>CLUTÔúü/DM¿¸È8U¼N÷;OÆZð6J.¾É.·°âÇ;OÄò=Sغ3ÑOÇ8·MÆÇåõÍãÞ?¿¸**0öÞÁéâø±
+Îìݽüõë¹QìÙÑH6óß¿1?´>7CH**ë¶;áÄ7AÔ*BÅ@Uä6+öü/DM¿ØÅ9K¼6*¾:IÀ.3°6*ºî*ü;âÆ<SØV
+·Ð½ÒRÏMãõûY,+ì3²2¾HÞKÆÓÕòQKûýýûïÙåXF³IVBYÏ01DÒRATÒRïà±*;*8ïàÂ:UÔ²ç<Êúü/DO¿ø
+ËBµÜÒ÷ÐPÊ;*òÐäÊï*HÑäÊC¯äÊSÑñêçÑQÍBUÌÂDKáYÍæç¾Ì+æ,P2¯ñT¼;ÒùÝý¼»ôá¹ÍôÂÉ´úR<ãÇ>
+ûâÈ=QtV·<ã×*¼÷±QÏE½ôúü/DM¿àÆ>MÈ6CÔROÔ6SÐâDZ*J°F¾É>×ÐOÊC¯Øò;G¸NSؼñ¼Ì5Ò-*J¾-<
+¾ÈËW.óýýñÅݼ¸ïÚ;ôäNC´6**8U+À*úàÆAµà²÷<åõYÎC4ÒØ6×<âÈ;M*àJKãÆæ4*I·ðâÉA¯äÂçYÈúÖ
+;åè÷KÁD32²2*1Þ¾ÂζÊõÍ¿¸ýüóÑQíÜù×ïÍ;*î¯>J7*ÁÒçðäÈE¯¼ý,ËÏJ¯NÇ;QÄF-*:M,,,0+*âÖ*
+I×<äÉ@S¼âR7Ø»ÝîA·2²2*5öJ/мÁ÷1ÀðûôáͽÜóà-*Z6?Xê¶0¾MÔÊç°PÉDݽ¿äRÎò°NÉ=*R°ãÆ?
+OÌ*2È=M*ÚNÈ>YÜÊ·±P½S<N¾ºÝñ½4ÈNMÎK*̾Â<ÒîüýÍIÙAÝÜ·Õ9GÔ*ÞÄ:EÄêB0¾Mð·=äÌ@ݽ¿äR
+Î<ÑäÁ+B*B1:G»0*¶ÌCUH>ʼýÆZMP,7,î9*üýýÜúõñ»öÓ<WÐN·°ãÆê2WI×<ãÊF¿ð6T¶üúü/DM¿øË<
+µÔÒ·<äÇX=OË@±ÔÊçÐáV3ÑOÉ?WÜòE·ðâS=PÈ<?Lâöùé=;C¿¸9AýýÝÝH»ùØESÆ;OÀF+î=KÈú¶<NÈ??
+QËI³üº÷û5²Ä,ó;QÄNCÐâÆ=ÓNÇ<QtFC8Z<ÞLÈN3±*¾ÙF3°áÁÁëùAä>NPμOèýýéÅ9YHÍüǯáÄ7A0,
+*Ä?*ì;ãÉD¯èÂCñùAæ6/è±ÌÊC°PÈBODÊ>YÌF-ZAUØNC0JTì6*îDWÈ6çÏñûýÃÓ+Ç3Ò-ÈúýýûôëÍõÒ¹
+´ú2ðMÆ8»âÅ;GÄêRðMY×°åÊH¯øÊ÷û5²Ä,ó:UÀVC°ãÆ=WâÈ;SÄ*JÇ<SÈFC,ÞUÜÂCÑäÉ<GèÄ:=ÝÅYS°
++ó*BëüýýúõñÏúÝ17´6**8å*FáÄ6GÀV÷ÐP¾C,îû5²Ä,óI³0Óç=QÎEM*FQÎF½ìêÇñQæC,Þ½4?TòæÍF
+¯TÊ<IíY9S´,óÞîýýYI»ùñ;ZÉBYäº:*Þ°.¾áâçñæÎN¿</Jݽ¿äRÎ>ÐOÇÅ,JTÌF+*>-*ÚOËEµìº·<N
+óÆ<ñùÚùâPÎÜÊýýýå½IíGãûæï¿**ÆÓ*H°NËB¹àò3=êúü/DM¿¸Ë@³ÔÊ×°PÉ:±6¾á²3±ãÊA¯ÜòBWÜÊÇ
+=QÌG»äÈ;GäÂ˽²¾=G¿øýýùáQý¼¹Ù8ðNÇ;KÄ.·ð.¾½>C<äÊI³,OC½äúü/DM¿øÇ@KØF×°NÉÌ°OÇ?SÐ
+>×<ÓNS°.¾ÑVCÑPËDYÄâ8ù+ôH½W@Ç/Ò9ÆôýýýðÛÉõâá¸úöÏMÅ8ßá5+î<OäÂ×=äÌAKåõYÎC4Òæ²·,*
+±ÜÊçÐäB*È3±äB*Å3±PËF¹øúç±Ø>·Îïúü×,>Ç3ÒY¶õýýü÷éÝõßÓ¸>C<OÇ=3âÈ;á*FNÈ;³ä.T=RØC²
+äõYÎC4Òíò·=RËH·üNF¹ðº°*ì±åÌH»ü>¸Ò9û3±äÁÆëíA7³NPÎXááýýûÍ1½Ü+õØðãöV·3±Ò@Þ6ÙóE·
+07ȲæÐJ¿æõYÎS4>ÚF+ZDYèÂ÷P*Jä**Ý×±åÍIµØ>S7DÌýÝÞ4Ç3ÒU¿øý½ûôéÙõáÝÀN·°OÇ?ûNÉ;WȲ
+3ðOI·±äÎGÁä>4°¼ý,ËÏJ/OÉA,:ìæRÔF×°7¾ÏVCñä**µ3ðáÁÈ÷ýí0BNPÎÜåïýýõÁ1IÜíHTïáÅ9K¸º
+<*Ü<OÍB»Üú÷<òúü/DM¿àÌ@¹ÜâC,Þ³ÜÚ÷ÐäÉD¯øËB³äÒCñPÌìÑåÍI³ÜF·<´ÌýÝÖ-Ç3ÒADûýýúôéÑQ
+¸3XFÁó@QØ>ÇÐâÉ=»ãÈI·0óC²Q;DñùAæ6/TQØVç<Oɱ*¾°â;Ò6»ò?UØÂ÷<äËEûäÊ?KÄâÆC½×9T°+ó
+*PSýýýüõí7ºÙ5I0*BÇ9K¸º:*ÜðOÍA»ìúç<úúü/DM¿¸ÑIÇôV4òRÍZ²6*3û·².¾0/T²æÏP-*FçÍGµ4
+û½¸¶>E¿øWõýýÅÝü»÷2AãÌGµÌ*¶ÍD»ðòSÑåÌ8ÓRÓNÓ4ÓDòùAæ6/èUìVS±OË>³øÈ@UزCÑãÊÜÐãÉC
+YܲCÑíâÇÑPÈ:Iè²Õù-I>E¿øYûýõÅýGºóÌ@NÆ:OÄ.3ÐÊúB0ÞGÀúÖ°PÍE½DÊIYüúü/DM¿ØÌC½Ü6-îC
+·àò3=QÊGGäËC·äÒS±Qç3²*¾íÊçÐUîø7ÙÇËJ»ÖýýýáYíܲ/Ø°ãÈ>WÄ*ÖÉ>YÌ6+î@¿ø>èÑæÌLKåõYÎ
+C4ÒÕÚçÐäÈDWè>@±Ì*¾É?*ìÐãËFµðâÇÑõÚS<¯÷ûí¼ÇVJ»À¶ýýåQýGºYºÐNÇ;KÀ>çMÄ6·<âÆ:Q¸ÆC·
+üÒ÷=åÍ<ñåõYÎC4Ò-Ë÷ñãÍD½àòH³ðÚ×±PÍESåËFM,ºRÎH¹,7DÒRÓÆÖ»üNÉNPÎHMáýýùÕ9ÝÜQÄ÷ÐOÉ
+>¯ÔÂASÜNC±.¾ÝÊSòQÏHÅø>N»¼ý,ËÏJ?QÏFÃô6øL*.RÍ1,¾Â4GD.ÞÅ<G4ðíøýQÀ8Ç3Ò-¿÷ýýùîÙ½å
+ÂYèÚ·ñPËÅ,ÞÁ0ODÒçÎQ¿ÀÐíÝJ±>R,Dñå2*õCÑQËH³ôÒǺìêC,Þµð.øñQÍIÁ¸ÎKMPÌÝýH3Ç3Òµïøý
+½üóçÙåÄUغ3±ãÉ;*,=ã4*ÍCòQÏHÃøF4¸¼ý,ËÏJGäËB¹àÒ3ÑPµS=äÊA³äÚç¯äÂC±PËE¹¸ÌF·øòç=ã
+ßÔüý²2<=R9áôýýôßÁ1íý¼FÇðâÇ<Q4Ç>M*ºOÇ=Mìê3ÒQú×±æÌIݽ¿äRÎð±ä3*ïSñ¿*öÌD¹ìê·=QÌü
+ñQÎI½0/4>ÔÊF½ýUÇ/Ò-öÑ÷ýýøåÑõó-Ų×ðOÉB»ãÉ?¯ÐÂ×°PY·ÑäÎIÃüFȸ8ë÷û5²Ä,óC»äÚC±åÊE
+MIûPËF³ìÚÇÑPÕ·±åÌI½øòC0*±@P9üÑê.è/ËüýI»õïáÙÞ=SÔF+*=M+úNÉ=WÜòç±æ2*,è=æõYÎC4Ò;
+/Ȳæ3*CTòÓ*BÐPÉD7*ÞOË,ã6I·N×3ÒÍ,ýý½ûòé¹A½µÄÚøÌI¹ôâ÷=åÌÌ=æÌLÃL³T?<ËزèÏíÝJ±>R
+ÂS=PÌCÍ.*çÒ·±/*ðò×±QÍÁÇÖñ¯øÅñâPÎ85üýýç¹AíCûN·ðÇ*öÈ=UÈV3ÑåÌ6ÒQÎGÁô6øû5²Ä,óJ±,
+Ó3ÒäÍE9.¾öâ÷J*úQÍI½,?T²Põ3Ø»îß´²B:Ѿø+àüýíýHûùâ·QÊ/*îð.*â6+îI»4/Ø>æÐHOSÍíÝJ±
+>RÍç±äÍC·äâS²ôF-*EM*ÂäË/*̱QÍICàÃûÙÙåÍõÚFJ@.ÆÄJG,³ýýùQýG»À6=ã-**?-*úâÈ=Wàòç=
+æ2*4رæõYÎC4Ò4ÛS²åÎFQ*6RÌJ¹Ì*¾Í;*Þ±*¾-7DÒNÝØCMõùóõí9·CDEJÀN4èEÒùýI½ùòíÙâC±èº
+@*¼=æÎOÃD7è>/OøñùAæ6/T¾47躰*A-ÞÃÌê鸳ðêÓùøúùùýÝ?ÕCÀáÀ64èYFúýý¼÷îå¹áF·ðê×=QÌ
+æ=åÌF»Ä/¶ÎKË<»¸òSÐ2óRÑüý½ýïÊÎJGåÍE¿ôú·ñåD*ïDZ6¾óòç±åD*íç°YèÕÕ¹íì×ÅY9IýûüáMÒá
+ìòJîÚJG2ÊýýéQ½¼»QÚ<PÊB±àÂÇX亷0*³ä>D²ç3*,Ø>æõYÎC4ÒæúC,¾¸ìò·0J´ô6+îF·ôâÇ=åÌFK
+åZ*I3ÕZåâÕ=ÍìÑÁÝXHºûûùñý÷ÑH¸áL¿Ù,óVÚüý½½ºöðûYÆ<SÔ²ç°OTçðãZ*µ×°PÍL¿074Å,74²æõ
+YÎC4>-ó÷ÑåÍF½Ä+Â.ÖÌI»øºZ*î±>¾ýêçÐäÈYUAõèÕ±íHܺøóûºùøûñýYýLù.0Ä+1/èñJüí½ü÷òçù
+á<CÐÊC=PÊôÐPÊDQ+úäÏKÇ0³D>S4Ø>æõYÎC4>÷.ȱ.*ô.è=æͳÎ=Î;*14Þ»ø>÷ÏOÞͯùîêÓÉíXܸ÷
+ìG¼ùúûùýIÙ»ºâäñ,óÈÎýõ9ÝùöïGôÖF³èÊ÷<P·3±*¾Ýº3ÑåÎNÃ@/Lå*ÆRýýÝý¶äQÎì²æÑLË0WTÒA
+Gè>SÒNË8óOÅ<GȲRÎJOæÏ-+¯*X±MÇF5ÕôËÛÉýÌûø÷óéÙõöóíýíݽñ0WÎC9.F;RI/ýýüõáÕA9YÁ7T
+òå×V-ØñQÌ;ØHòçÑRÍP³D??ËèòùAæ6/èµüúC>QÍD¹,Ë+ÄH>æÍI½øê×ÑéFǯâÎR»ì.G5*ÚQÌ:;ÈúÇC
+³å7ú¸òòÕÕ1íñÉíÝIýýý÷Méî:»Sï¾JûÓýýõÙA½Ü¹ÑË@åÌï1ÌÑPÊAWܲCÑÝê3>çÎNÁ8³KÁ0Gøû5²Ä,
+óI»</TòåÍEëQÍNÃÄ@¸ÏL½ð>¸ÒM½VÏîæ´Ó4ËCVJ¼ü6BóH½¼Ò×ñ¯åäÛú÷îãѽ9½¼ÅIݽûýûÝYê*<4á
+ß+¿¸üóãÙIýEìËð=æJÂJRÝCÑPÊDµ<?èÉ<³D²çÎP¿¼ý,ËÏJ»åÎKÅ074òQõCÒRÐJ¿ü.èµÐ>Çô°ÍóH¸ã
+ÙSż5ÓQËüðäÍG»ôòÇÑùò·ÏàƺÉMõîÓÅõÜHúúîW¼÷ûùÅÀ¸ùÄÝ?¸,KÕýü½üöîçÍYËÍYÁçðãÎLÁ,óK½
+ôÒS±äÌJá³Q´OæÎíÝJ±>RµTòSÏRÅHGèË@»È²çÍE³øÆ.»ð?7EñìÌG°àÁG5µFÔðâCòPÍMM?ßæ2*ÅBøø
+ñéßÉ1íçÙYíüûúõ¿¹÷ôóýý9ÜèZHѽýùëѵýÜͼA6KÅ<¹HóOÍHøÒRÍK·æÑOÏ@ø².*2ûü/DM¿<ÐOÇ
+0BÌ*¶ÒO½ì.÷;ã¼ÜðRÞÑK-åûØåÌê¶íåØ/ÉóWKäêC=RÐMûRÏOWȵݻµÕÜFºõöãÝ1íóÁ16Ù³óê÷ùúýý
+ùíݽ¼÷¼H»øÉáWD´;ßò3³+¾E³ØòSÒRÑL/SÁ@âçÑíÝJ±>R94ÒæÎO¿<?èÉ,ã÷<âÃ@3øÇ6·ü?GÖïáÜB´
+íàµQôVÖ-̺?áÂ9OàòCWô.Øð°ûýû¼ôÙWáP8¼»½9IüøÕÅAÌë5Uµìݼøù÷¹ûñéÙQ½Ö×NDËáËDÃ@7ÕOO
+É87-¾OË@WT²>A2ûü/DM¿¸ÎKÃ8?D²RÏüñãÆ6=Ôá2ÐÁÂ3A²ßÀGAôÈ3UH»DöëÏÛóé8ã¿UÆñM1ÇZOÉ3Ý
+ýýíýÑÝܸ±ÞËUùðîïáåÍÜøïüØ´ëûãAÝYÝÙ1½Hûö¹Õ/Ñ-»<Û×ñæ**M¸²çÏ;Ù²²SÏÏ*LÒçõYÎC4><?È
+²RÐ;ÎüÓà¾üä»ÑÖÏåêTØñâÏ3UôÇ3Å´úײãÖû´òÃËX²ÆXIOöÎLϱå1¿0+¶óÔùØG5C´ñìüüüûíµÅ¼ÝÍ
+ÅXýüùðéÕõí3áò÷³éÑC+åöQM¸²çÐïÍ+*à>ç¶RRøû5²Ä,óMÍ8OȲSÎQ»ñÏîìÛ5öîFÕæóìÛÆ1Iüê7µÔ
+º×òáÎ7ùâѵEýÊÕSÇH9ÝŽ¿ÅõÜÝñý½IúùøÓMôT±äºT¶³ðóû¼ûùïÁµXݼµ9¼û÷å=ÅûTÙP/È<åÐ+Íâò
+çÑ¿*ì²SÑQË<»È>Fûü/DM¿øÑQÍ@»Ø>PÍÜÕ±ßX·P.RÌ9ºCóíá¸79ôÌKµ@7C²æÈÛ³ãÐYáXìù´ÕÙóZÛÐ
+MÅÔìÕáÝHÚúûô×¹çµEðMÕÎãØü÷öùúáYݽÝÝAý¼ûð¿Ï@ÃRÇìÊS²ÎÀUÃT?TÒQÑHÃUÏX»D³.ÞùAæ6/èÃ
+<?TÒæÎ13øÉV-ÍHG×UÆÌZPÚÊSÁ¼êÖUPF×îäÕ?ÕôÏIÙÔFÙ±éäÛöòçOÕäùø±IWFóòóÙ͵íù¹õêÒK´åä
+÷¿J-é@XGýû8ýºúóÅõWØÒE/øñæÏNÅ<³NÉ@W¸4ÞË@OØ>SÐLɼý,ËÏJ+çBRYT>ãÌB=ØúçëXÄ»¸ïàÃñ¸
+ÜÄIÙ8GØðßì÷ðçÙYíÌÊØÕì6FòëàSÝôU×àüêøñàÕG°Ødz͵ýݼÕGôM¿ú,DÒçÝT08¼¼úùíéï´ÁH³Ø²2
+¾>³D0JÌDG*75¾OÉ8WØòùAæ6/D,èÇ8G¸>SÍ=³¸È:·DÓU׳ìܸ¯ÜµùHHGÙñXW¸ñáÕUÑô×Cíü˸´áÞû
+¯ÉDÕÌ»úD°½8ñÛÁ@íLôëïùÅýØéÆ6E¸Ç@¹PûÉCòêÊÙðÒFÅ<Wø¾Gå+âRÐ18*ÆÄ2*ýíÝJ=>R±È>èЯ*ì
+>TÑQ½èÊ3ÐÙ.øÒìäÙ¹ÅôÅûèGºøõåKÛç×½¯Õ8û÷°åºòRåÄM½üç5ÔúÈó°Ó>5øÀø.¸úêIýý¼üïÑ;Wðú3
+ÑÍúBÐOÊFÇ<óPËHË4³èÒS+èJ*Á4óSÒó*P³TÓSݽ¿PSÎ0?TÑP-JM*úSÒLWìÒC°Oý·?ZåؽÝÄê-M¼Ø9
+ß¿DßøØZQ0Ã4ïàËX=èîÁ/¼>ö:SFø²Ê¿Q>×·ýûýýáõØçÀNRîݽ0×Þ½üD4Oز6ÒF»D0TÐTG27-21B-û
+ü/DM¿°ÐOÉ<W¸>ç3*JD4ÞÉüNCÑãÅFÍø×ÃSý0ìEZÉ8¯IÁ/E¸êAÎÍÂÇ<Ü¿EÑÙô0=H>SÏNÄ?ûèà±OÙõY
+ݽåYýüôÅÃ@²äÅç¹QíMÌKÃ4ÏLá*ÞæÎRTØòçD*±ØòSÑñ*ÞýùAæ6/DêÇÈ@G¸8¸6èÉ<OØÒåÈC¯øÉFÏà»
+6E¶ë8CUÊ.1¸VÖÎQ6öïJÔб1õºÝ¼ê¶ïæÈKGXÒPÍôó6G¼åY½ýüùÙÅô4?P-³WäöQ*¸*::D<+ÊÄS.½,Ñ
+íÝJ±Aæ<¾·æZáÎMIÈ=äÉ?YøêèýÐ0ü¹´áX»¸Ä5MرB>Z×ü;èëó¯-HèÓE8ëúïÞKµ¼òN-áü¼ý¼ûø³úðÀ
+?D-Bü¾Ã,/D²æÎMáLßæ</¯ø²*¾>»D6øû5²Ä,/Rá1+è2*Éø²TÒ;*Ó*8?èÒSÉðÊ×ÑõâÇòêàÔ¿1õÙ-IÛ
+5=KÂ>Gà¼*ûäYÍFµåÚÙòïÝÝù¼êYDúÅÏêùø×¹îÑ/µWøñRÒR³TÒQÉÜ*¾àåæ.ÕDÓTÒTÓXÓøû5²Ä,/U-4
++èD*ÆDÖÖ6ÈÐTËØ**ÏüÊ·ÐãRRµFùõêãI½Oç70VRïëòÛGùñÕ·Á8ܸóÝ8ÕæÃíüÃËÌF½üé²RÌNÕ0**ÓÁ
+ÎôÒSÑO-JSSÑP5µ+è:RÆøû5²Ì,?QÉ0BðÐOÉ<OزçJá<*·è²²JO¾ñÊSÑOÉEËäóÅMùHüØZÑ=ÛTáÀE-Õ
+ÜCµA7øUÐ*-LNçÕTìWøïÝWM4ÌLQG+è×V½ØòæÏLÅ8OèÀ8G¸²ÒÀ;Oè>ç20Zøû5²Ä,OPÉH7ð.Q-5-Lå4
+ãçÑ++OÎ1BÞÁèÚ·ÑPÍYû¸ãÙÃ1AëÚ¸âܵ²íÃG¹?ØÏ9òÄ:WæÞ¯¹ô¼ñHO÷ÐæÏL3SÑPÉJ/èÑßÌÁβ>çÏï
+2Á+´²SÐOݽ¿äRÎÎòJÑB»¸4JÈD7ùF7B·642D2,óQËDWȱPÉE»QлAÝä7G¹Ý7EõßVíïÉåS¼ËêÙ´ã¿í
+àÒN·Ð.D.TÄ<7WB./RQ6ÀJÉIå*¾æ¯æõYÎC4ÒEÃ4?èÒTÑPOTÕT7/:RÍÌ/6º8PÀÒ/*+.8óSÍE¹´ÂçÑE
+ÃUö³ìè×5õ¾7HÚÈCµçÇ·íÙO¿<Gøò+Ò¸ÑSÓTÛ¸ÓèÓÐ?éÆ.À4ÓçÎæ2*¸è>è¯*Røû5²È,OOÏ@Gä>TÑÄ¿
+ÌÀÌÁÈÐPÅ7MHM<ãçÐÏÆ=ÈX±PË9±ô.UÕ1üV¹øèÁG¹äÁõÈÓèÑâÌOÖ+/>?èÒ+ÎVóTÔVÕXÛD:ÉÊLWD8+Ç
+DO4?ç:.Røû5²Ä,?MÇÄ*¾Ð-*ßÌò>èÑKÍâ>çÏ-*Ð>ç¿¿×¾¸¾î*A4±PÌ=¹ìÊèã<ÄÛBìÕF½¸ËKµì>ȲS
+Ðä>TÑO19,*8>ÞF*B7°ÞLÁ0/4òRÏ+2JòùAæ6/èÈ8WTòú°:<:W.óRÑLÃèÒçÐN-*êRÐNÉ@¼Ä+.9öÐKÉ
+@»T±RÍH°ãËNáðÒçÐîâ÷L1¾ç**¹øòçÒ-*.?TÓ¯*:ó>ÀI»¸ÒSÏL¿@ÃKÇ4WDòùAæ6/¸Î×,ÞÈ8?¸²Z°R
+°êï/À86ÒÂ4ÐNÁ-æRÐPá0FèÑFµøÊ2±QÖCÚ:T*ÈÄC2ÑOéL1L+è<V¶¸.¸>UÎD7À¾L¿4GÈòùAæ6/ÈÒLÛ
+DÚ¶0ÞÑHÃDóèÒTÓøÓU×°ëسUÔÄóTÓTÁ;3TÒTÁ/P>ãèÒ+-X?UÒXÕ¸ó·>NW´2CJU×´GSBù.Sé8¾é¹+î
+WÑPWè²SÒRݽ¿äRÎäòçÑQ96¾C³ØÒç+*¸D³è¿æK*ÇD³ÛJú*>7+2BJTÍL762ç6UGòF+BT2L/R-;L5à-
+RèÓTM*ÞèR¾ÎSÑDÓSÐ+*ÎòùAæ6/èÌ@³¸²êñ6*;WȲ6J*ÞʲÂ<R7ËNSÏH7::PÉ@=RÑRËDGزF*QÃÈó
+SÔQÕ0<ÂÇ,Ò³*¿Ï;/¯ËβB>KÓ4ó6?WÃT³Æ+G?ز.ÞùAæ6/4ÈÜÆå-+BÁ.Ná;+çJæÆNÇ4ó6ÒB³D.ÎÊÎ
+Ö:8:Ä3ÓPÃT³èCÀ2¾CQÆD8ZòZø¾ÈHËD*ØZ*Ì0ÛÔVºý,ËÏJCèÏQÉLWø²îð*=GWØò6J±Ú=G³D³**WÃ
+T³RÒJ³¸B3Ì0>*Ò;*HóSÔSÕHÛ4?VGú>RÕÄ9*Ó¯ÓÁÓ³ÖâòSÓKοÎÞò+E*ÑK0íÝJ±>æÏMƸ*CØ÷ä*4*
+à+ÊTÔU×8*æÔVÕ°Û¸ó+6.@2ÓTá.L.Ì?¾VÛD2+Ò0¹0È4ÔWÅ.ÂTÔ³Õ;4Á0íÝJ±:R%%%
+d
+643 83[1 0 0 1 0 0]sl 8 mask 0 249 di
+/mask 6561 string uc
+*Þä**¼ºýÎöüÉõûIòù½æõýÎíýKÝýÁ¼ý1ûü9øûIòù½æõýÎY»÷*
+d
+/sl 52083 string uc
+äÕí´ê9+¾Ð,JóSÓNá*öçÒQÑDË4ó¿*BÒRÍHG-*PM,ºTÓS×TÛD³UÒØÓTÔS-4¾çÞ,ĸ³TÔSÕT7=:S×Ä8
+¾Ô¯.¿0îòÞß¾¿Uã¸?UÓTÑXORÑ@7ÓZQÏHÓDæJúý½ã8íÝ,8>¾³ñéÕÓÑ,ãNÍ<WÈÒçÐÑ*A,JÈD»ï*À>çÀ
+*ZD@DL4Î8TÄÉ,ÐßÑ=*,³èJä-*JQËH7WR+JPÍP7*B¼7MR>*P9.ÞùAæ6/TÎ@W+*PU+ZSÒPÍDW½6+R.
+B-2FîQÕLÛ4?éÒVççÔQá*6èÔQÑÌ2Â1ÎÓRÓ0¿4ÒPÁWKçÖ,ÎD*JÔTG<2-:OÍHBÃT¾õYÎC4Ò:»¸,¾ÉDO
+èÒçÐNM*,<Kç×¾**Kزã°6¾:»ø**àKéL3èÓQMCá*P+T2NSÑ¿/ð²è¾åD+ZDÖ¾È8G¸.ϼKÆHûü/DM¿<
+ÕTÙH2*ÆD4JÔ°G+JTÕTG1¶¾+AN:²JÓ¸Óø³éÖ=*ì³êÔ°×ÀãøÓ±û¸ÓéÓWÑ°óSÓTãȳUÔXËéÕYÛ@*BÔW
+ÓX7@RÓBÕJTÛLûü/DM¿øÑSÉP»4ÓSÒ,?è5-*Qå*Þç=-¶:*âÒçÐK-æòSÑSÏÄ*·ÀÓ¿ÉÌÓTÔSÑPÃèò*¾
+KÃDóè**ÈDÓè2¾J¿:Á¶Â-+*OUBܽ¿äRÎ<³çCL¿ø²>ïNò.Þ3º8.ÚJ+RØ4*ÆHîNåøÒéÑWÏ´ËèÙTëT?é
+ÑRÉÈÑQÅHãTÓ;Î1ú÷PIá<49À7ܽ¿äRδòSÑQå**綾B*³ØòSÑOÉ0-ÎÐPÍÜC*ZDºÎ0*ÇHGøòæJã,¾R
+å2*ÎDÞ3ÍPÓTÓè->ÎRÍDOزçM°¶;5°òçõYÎC4ÒN»¸,:V·0¾ÊL»øJ*ì*J·Þ38BÑRÉPW+îT͸»Ø?èÔS
+»éÒX×°Ó¸³èB¾3¾3æ**ZØê:æ*ÐÌ+Â/ÂZ¾ÐíÝJ±>RÏسî¯6*XëTÓèÒKǶ?UÓTÓÄ+ÆÓUå*æTÓVM*ä+R
+éÒTÅ32èÔRͶK6*YóèóéÕYÛ0**Ô-*ñ.Ñ1Z³Î²F,²G´¶Î5L³éõYÎC4ÒV»D,*ÏX»T?è2*ÆD4*ÐÜ0¾à¿
+:ÀZÀ2¾*-µ¸ÓSÓPÕPóD,*Ø0¯0¶àÒSÏL»øÂ5,-À+07À6PAâèÑíÝJ±>RW4³SÒQÍÄ+Ê,ÆÑPÍ.*@WèÒçÑ
+++á*=*ÎòÇ*.AÊ+¾Ï¯+¾ò/@ÂFÂ*>CÒ+Ú:ÁK-JØÊ:Ú*μý,ËÏJÃèÑÅ,JÐLG+¶5*ã,µÆîÈ5úçÒOÑDË4
+ÓèᲶJ0=*á.Á/íÝJ±>R·4³.¾FW¸4:8¾ÌH³Ø0Z4î,,XZJ++>71*OU*8¶+67-*N9·KÇ+B»:2M0Ðòç3
+¾;-·4óùAæ6/TãT?U³êÓ;*ÌóUÔZ×Àëø³/*·ëè³+JJÞ¶¾¿óD@é2*ÚD´éÖT16à*À62êÓZ-7æUÔXQ*Né
+ÕAàZø:JZDï,7Þè+ÁõYÎC4>JË+*Þç¹0¶Õ*ÞòO;*Ñ;+µÌZ:îÈ-ìFÞùAæ6/4ÙDÓøòèB*ÕøòTÒTÍPÃ4Ô
+Ä+Ú**ÎD<ÞÔTËøò.*V7BZVÍT³øN*Ä7à-ä08Æ+NW´:TÑ8*Â0>CÎÒQ×ÄS.?Â*ºý,ËÏJÏSÒPÏH»B*õ*î
+<1Ä5Ì6J,´¶è,AÚîû5²Ä,óTË´»¸óSÕQûTÒXÏTøóSÍDóTÒRÑPÃÈÎTÓD0ÞÑP˸?èÒQ×07Â+.=¶ÓP×D
+ÓèòUÑJ³Î+UÃèóçÒR×0C*ÔK0á/Z³ºK2JÖKºà;C*Õ¿0íÝJÑ>RõTóéÓYÕ¸ÛèÙXû¸³UÔY×ÐÕWÛÄ*ÎÔWÛ
+@,Â*¾ÕÁ,¾ó:K7J¾+·Ó4´OJÆß¿7*Ôñ.¯*À?é-°JX׸7@:W×ÌSÊ*:ÖTß¼ý,ËÏJGêÑW͸»ÈóSå4?éÒW
+ÏXË4ÙPÓT?é2*ÝD³èÓTÓPãD0JÙPÛ4³éÒ+.ß.γ.*¿»ø³.¾ZÃDê*ÎH:-ØT?é¯-ÉTóèÓQâ¾ÓLã4óTÒX
+-7ä*âéÑíÝJ±>R¯4ó.*F»@*A+î*/Jè-F¶Þ/O0J³<²¶õ31Úîû5²Ä,ÃWËÀ»ø³.*¯Ãè³.*´ÃTÓTÔ=*D³
+èÓUÑPW4ÎTÓLë4óèî+ÞDÎÞÖH/é²Æ¿BãøÀ6ä/êèÒVÑ8E¾ÓM0²ÓTÓß0P³èÔSÉ0âSÕK0î³ÆßùAæ6/TÊT
+7+îRÍT»4óçÒQ+è3*¿D³.¾J»D:Z4:8îL*È1¾ç3¾Ù°2-¶-*1øîN*,3ÆSÓQÁ5ܽ¿äRÎÌôUØYå¸GÉÓÍó
+¸@V×Xá´ó°ßÄûD@Vׯ»VÖYÝÀ7EÔV4U@ê2*î¸,:¾ÔÌûDÞ¾ØÐãDæ*ÔH:-=ÙÓêÕQâ*à0B*Öß*M0.ôVÕ
+±*¼@VØXåTOùóùAæ6/4ËT³Tóç3*Éø>èѯ*,³è3*¶Ø*JÎLG/B8B+*RM*,406à*è,È6XÎß3CÒI>C6B6
+=*Òá/íÝJ=>Rî½å*öü/DG¿ÀýÙ0¾õIÌ×3òùÕ¾õYÎíÝJݽ¿¼ýÏöü/øû5òùAæõYÎí½,ܽ¿´åßîãLï·Ø±
+Å̲+¾õê×á3HÏÉݽ¿Xí>¾äöâ:¾Ò/¸.*²¼ý,ÛÚR*·AFC»¿ÈGðܽ¿Xë=*ã¾NÁ@ô³û·-*Û85Wæ+Îúü/¸
+¯Ë*ÆÁ;×Oöòéù½IºØ*ÞÚ½ñüGF¹É¿1ÎöùA滯ÀéòáåØîß½IQôûÕÍ@*F·êÓ±åÌëÙEðáÅäõYÎùà.ÝÔRÏ
+ÖÕ±Õ¼ËàñµIW*öøäU½PGùóÖÒRϳíÝJõÃ38TÑPÂYÑä6D³Í½½Ü*îð»YÜÊø³àËN=RíÝJõÃ3ÜTÓT¹¯µXÊ
+×NQIí¾¸úñÁQXÚCôÛß1íÝJõÃ3üéÕYUOIüùB/1Yý7*¶Ú¿F2ξÁ¼û*Þ;3FË*99üµá½9ÉÜDúîû5òÜBßÑ
+OÉ´=ò߸3ÑëêçI+*ýÈÀæñïűPYæ-¶M,µ;ÞÙ±ÝËÖìßε0Iºý,ÛAPö¸óè³òFBXß>ù·öQ*Jê2»++8*¶S
+5@Î48ܱÁî*ºöÓù>:JæÜC,Íüº´ß¶-¹ìBúîû5òÜB+,0*Zå¿í0´GÚø=*:2¾Ý¾*1î3Cì×»ÃÐX8¶86¾Áü
+;ÅØâË;îïßW1¼EÖñçÁõYÎùà.åÆ;M¹Ì+Ù/ÊRùHü.*.ÀêA:Þ+NZ»úöç4ĹJÓJ9I8+**.J*îQľºùPGæ
+JFÙ5¼öü/¸YÊZC±äÒ@.èüÌÜûù3*ÆB=YJ¾-ÎZøõíÏ?úäÙõ·÷ûùÀÓµ¾:.Þ¶íHûµ2>R5¼öü/¸YÊZSÑPû
+?.èúÈÄ»ù3*¶Ë9<°¼ËBåë¼E*OÜH¹äOÇí´6BÀëÙI×K¾¯U¼±.@E´ç¼ÁJßÕîÁõYÎùà.Å5AXUÎ1áûUJá¼
+.*̱7/.Tö¿7Ù-¾ê2,/È<áô?8RÁ7ùðP4N¿7ÕÆJM-ĹBòTß1íÝJõÃ3üöïáÈTQüEöC¿äP+HE´çÁýPXW
+SößùA滯ÀÑH»ØÐäÎ59@ÍWÝïO*î,*Ð;RÕW¹óâÅK¹ÔÂPÍPûû5òÜBßÇ9I8WVèÍ;ÉäÑÑ3åÙ:îܽåüúCó
+Öææöïâݽ¿ì5=ººùôùXÐÊø²ø6Dõ²+Þ½±QÌÙ´åÊø<ùôá½ý,ÛAP¶öüûùA·RçâÐØóº+¾»U9IDéL<-9*ú
+õYÎ5é.ZI6S/½éMÃÈ>ç,¾TÏLO͵ÀÁ8¾û±Éöü/¸GÁ*ö-<õJ**ýGOºý,Û9L*ùÉ2RZýZ<ܽ¿XR;6úøö÷
+QìÍÄIüøîYLàùAæÌïÆõYÎíÝJݽ¿¼ý,ûüýîû5òùAæõYÎíÝKܽ¿¼ý,ûü/øû4>Þº**Zñù1¾´ÖÅ-ô>¼û0
+¿TÏLÇ4/øñù1¾Äæ1éÇñäÊA.T¯àÚ3²RÎíI*¸ÀIÖåËAWØòéµ2>Ä>Ç<äËíI*ø,J>PÈ<MøóE.TE´ú2°âÇ
+íI*¸ÀIBOÈ;I¼òéµ2ÒOÊBÏàõ9*»+:°;á**ïéµ2òù9¾´×2Zóû-Jå¶.
+d
+643 81[1 0 0 1 0 0]sl 8 mask 0 332 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/personal_invitation.png b/doc/krfb/personal_invitation.png
new file mode 100644
index 00000000..e95b25d8
--- /dev/null
+++ b/doc/krfb/personal_invitation.png
Binary files differ
diff --git a/doc/krfb/screenshot.eps b/doc/krfb/screenshot.eps
new file mode 100644
index 00000000..124ffea3
--- /dev/null
+++ b/doc/krfb/screenshot.eps
@@ -0,0 +1,683 @@
+%!PS-Adobe-1.0
+%%BoundingBox: 0 0 569 415
+%%BoundingBox: 0 0 595 842
+%%Creator: KDE 3.1.91 (CVS >= 20030907)
+%%CreationDate: Sat Sep 20 11:23:48 2003
+%%Orientation: Portrait
+%%Pages: 1
+%%DocumentFonts:
+
+%%EndComments
+%%BeginProlog
+% Prolog copyright 1994-2003 Trolltech. You may copy this prolog in any way
+% that is directly related to this document. For other use of this prolog,
+% see your licensing agreement for Qt.
+/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D
+/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D
+/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D
+/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read
+pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end
+d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi
+false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88
+0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{
+LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{
+gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get
+SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7
+bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL
+0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB
+exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL
+ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1
+eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if
+64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3
+i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll
+putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3
+1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D
+/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3
+colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1
+QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray
+QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2
+add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq
+{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC
+imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel
+where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d
+/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7
+DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d
+/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height
+h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4
+DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{
+pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC
+WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{
+1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie}
+if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4
+2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg
+RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0
+exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if
+BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h
+add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0
+6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT
+}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h
+D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div
+add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1
+ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT
+0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy
+MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R
+{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h
+ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry
+D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y
+w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul
+200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90
+x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0
+-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h
+ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale
+NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS}
+D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT
+x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP
+ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255
+div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0
+B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25
+/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true
+exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3
+-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch
+/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup
+maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding
+fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length
+dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end
+definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d}
+D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty
+MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch
+stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT
+1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0
+exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore
+showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop
+pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D
+/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt
+ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP}
+D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D
+
+/LArr[ [] [] [ 9.305 2.791 ] [ 2.791 9.305 ] [ 2.791 2.791 ] [ 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 ] [ 4.652 2.791 2.791 2.791 2.791 ] [ 2.791 4.652 2.791 2.791 2.791 2.791 ] ] d
+/pageinit {
+35.4627 23.6418 translate
+% 185*281mm (portrait)
+0 795.224 translate 1.07463 -1.07463 scale/defM matrix CM d } d
+%%EndProlog
+%%BeginSetup
+%%EndSetup
+%%Page: 1 1
+%%BeginPageSetup
+QI
+%%EndPageSetup
+[1 0 0 1 -36 405]ST
+B P1
+NB
+W BC
+/mask 7488 string uc
+î½*¾î1:9*¾I±.ç7*¾Íæ;ÞÁ>9+¾Qò2ZJ¼ÂÆæ:°ê¿Ú´>á8¾ISëRÄG*9ÐØÏ6¹¾8ú1ºIÅ*-´ò2>Þ½¾ù·
+.*ZQÅÖ:M2*ýÜýMºýYùüýöûýïùýáõýÅíý9ÝýI¼ý½úüýöûýïùýáõýÅëî;%
+d
+/sl 59176 string uc
+î½¼**û¹I*<ÊϽÐ.ëíı*Íç7ó¼ðÉ+Í7Ùë+Þð4Ä»üÁÏá²ðËÞ-üØù+JåäìÝI½üçÚRùíJÔ´²û±Åëø+**
+ü/ÝÝüûYM8Í篲¾ìQ¼Cîׯ9-¾×Oݽò9ÞÄK*¶ôúüÚÝÜXÏ9J,@ÕZêñá4+ýUµNî+½Ë**6öüâÕP,*À1Ó¹
+**´°ÏÀ0Ò+ʹ½ýØW±ÎÏüµ+CE¹²;ʼñÄ<¹ê6â7çôݽ@èN*¾RÏÊõ4*.àW×RK1è*ôì9ETÔCîæ-Z+ú5¼ý
+üµYÞÏÂäø,*¶ïïëåµÍ»ö31RÔVCõ1ܲ@*îÒò?ùõ9JÇ<ÇâÆý,*ÂÓWçVK-ì*ÈëÝ»æÒÅHÒ-=îôó³øå**
+VåÉ,Rôúüû=ÔE4ïT9*¾õ»ÛG¼÷êÏIQåSí»;,RóõüÁÅä,*ÌÈܼ¯íÄÚRϵÊÕ¯VÌKWëÈä4¸î06úüPÉHçÎ
+8º¾éÕñåçÕÕýܺöì×¹ôçÍEµüºà>¾·õõõºYÞ;ëI5³+¾ÝËëFúûößUÅåÉ-ÅÛô+3JÒðù±/D5*¶TøôG˶Ì
+×°ÜêظüÌ4³³é¿4Æýý9@Kº¾:ëø.-P,Ì,ßÛ?ÖýúÙ9¼ù1+*Å.ïè+ZXõûùé½74×.MäB¾ÛùÑó÷òÃ0³J-F
+½G½GÛ¸÷ûñݽíéÉåüº·ðÕÃPÊ,ܹýüùüõí9F>»**ܵµòîïíYíG<AÌú´ÛAPÞôõ1è±@*îÒòO.ÖT:BZFÊ
+GÊVC¹èèÔ-?Jýý-¸æ.22ÎýýMÜÖ¾È**ò<28OJê¿óàû½éÑ=R/¶L2Õ6Jçýý03ØLÅá;ÉZâú,R¸½ÞBÇÜæÎ
+0²¾·G*Z*îèóºïðåÕÝíY½¼ÅÝü¹ðãÅAPC¯4¶×ûùE8Õ½ýßJ²+¾I?6Ú÷õõóáåñÓù8¼ÚîÏ1R¾øõÝC7*¶T
+øV+á·R17¼M2˹ÙS±³é¿4.á÷ýýEé½»ñµûäñäÞéá-P*îPõúñQX52.ÎKµéÕ½EæÏÀ·ÀU*2,òEÏüåG9Sð
+ú·º×E9Ýõ½½Ë¾.H½îîR7ê¯úïÙ8ARûý?°Â3¯¾**7ì*EUB,-â·ËÃ6O5Ò³úü¿+É**NÙý´ÇH9ZR-@ÞÚXÛ
+Ï**îæóÒêïÞÅÉÅIFÆë½1üù¶×6ÍϾ**î>/.**=/,*¾E>îîùÍý?½üÓI5*ºQçâýY9ý»Yí»¶ìá;Ýê×R¾
+øõÅC7*¶TøÞZ8ê.0>¼MŵýãÝ°³é¿4>Fø¼ýï9ÑùIßÈÂúVS/Ç62ÎÓßüáý´¶:D¾õüÅ3ïýà=M-ä3XÃLö¾
+áÒ÷ݼRÝÃ1ñų=Úý½ASÁà.V?úGA/FFT-úRñ½è2Á5²°ýýÝAÌCJÎ6ïNB.GUµ.àZ8µÙçÊà8**=õÕÉÏä-
+Kè¾/*GáóB**öíÕ1*ÞÏé÷Ìá±ñôÜÜûùì¼»õÚ±9íCï?4ÊZÜN¿2+¶Iì2*ÚK+ìÛ½½Uóéñº**âÏõîAZD²È
+XU²×RÞôõ½×°@*2¶òÚé,×ß/Æ8íνðÃVë¸Î,1ÎæýýI>=¼à1÷ɱí8º¾üÚºøýýÁ01à*¶ûýÇøûYýé+?¾K
+³0îýÇGæÈDýÕA²½¾*åAÃû½èEGÄ3BâÜ7÷5T:øýÝS-Uõ½üTñ/DïìûýAѺÞ2ÅÀ¯ÖîÀ@ƼYóä>ô**Ô·ýÖ
+29VR1<JÚÌûä¼¼û+KÎûäóºMîÒ±YA½H»IYüøêÒ¿µºö2/8*ZP>*F-8SúöC9*¾û5üùô5µÂØ¿1ýůÏ*óí
+Y°D*îÒòóDõ±57EºÄ>QÇáèãèÔ-?¶Ð´8ÆNéøÝ,ANï+0:óýÇÄ,68Â+.FØÇýé=¸K/Á0,2ΰ¾ç*/ù½±L÷
+ó02æKE=ýQüZÊ*¿Ãýëâ¾âýýP7êãýñ/?´J¿÷üáõó¾;ì/á3¿ÎûÆLÜ2Ä/6*Dìý´ßÇAÂÒý6ZXÅÎÁAIV1Î
+I¸GýõÙ²éæÝÁ9íß½ÅìØòíý2ÜK¿5+.*ìϾJý¼NîT4¶PúöW·õìð+*Þ·Ú¿åHÜæJÏñ0XøÙÀÃ,ô¼ýÈ9±+*
+QO9Ú»Ñè>SàÉJÁOÇßÅÔ´GR¿E³Ñ9Ûüå9²Ó÷*Íýý=ûýÃûýY>»632:Þ¸ý=VùÝXLAÒîT>úýýÚàÄõIX?Yë
+ÜÞÇXI@³ÇüÕí>PÞ@»åÚZÞ¿ÚüýP7êìý»5/AZ×DMÀûýùÎ-Ķ;6-4Å.ÀÐÝ5úZB°Ô*6ÛýSÀ;Y2SýCJÛ8Z
+9ø-íYÛÌÉ-¼ØóñëÛ½õìÙIµ»D¶·,íÎÞ3FÜM+·ÜûÆÞÓ>ZÉñåÙÈHûâ-*Îóì0ÍÛEø*,ô8۵ﵿ/V>ZäùA
+ãB5*:TøÚÃ,¾µ+*T:MÄ5Wë¸Î,µèPC¸F9I=¶ñ+H¶ûý¿íU´ËÝøËB¶ûý1M,³¯ùõA±àóÀ4XÃÀýýýݼíø,
+ãñ</ÑÝíûG÷úSºÁÂ032êý<-8JáE³¾ûõÍüNU¶+éííýûéßDGZ.Kú¼/á¾à;¿J×óõüÅýVB°Ô*òÛ½O¿9Y2
+SýCÞ¿éÏôíßI¹GY@Ú³êåÕ±íì×Y½8ÙóîãüãÂ,1B÷2/öâõíWCµ8ð+*ö½.¿õÌGº¸*Hü¹òàÁAÒóYϾWìÍ
+¶àV*¾SG6ÃT?UÓ,*0+âÙWAVJ*+¸K*B;àMÁ4Ô1ö±Â*CEÒ4@K:éNJ¾*@ÒÃ2ÏW1²ZÌîR*F:J¾0Ç°A¯Lµ
+¶5;KÉ<P<N<·UNJï*»5¶*/9ÒK¾Î/<òèÞï,GNRÎLÓøÛZÂ5ßBζM:DÒÛ¾ýW9È0¯ð×+ëB½½·+EÜ:Ðü±¾
+=Õ-*ÌÛÌã,ÌÙô-7Ò**î0H5ÝÚRJÖµ½Ä,6иóÔÂ5íö,*ÚBÑÞ»õPÌ»º0YG÷èËÀ3V=¾SùQÍ2¯<*îÒHøùB
+ZæÆ>ÛPÈKM¼NCòÔVÒZWD2¶Êñ4²úûù·ùø²1èΰùÁì@:á=Lè½1¶î¸,*¾±Õ±Ë¾1õûâ2ËöîËòOÃ,´Gùôí
+YO+µºüÚ?íÜ?Ü**ÅAÈæíßÅQ1ÍçQµDèϳ.9ξ+íY´ßN*Jè2¸1*ÎO@TÖA72J¾<Û,ÂNÆÎRøÔ*7úîøüFá
+/ÊöÂÆøí+ÌÝÌ9,8ÚöðæÑUõéÝÍÝÜF÷Kõ°P+ÓRÚ¶U×±Iß³µæWеJ;5ýÊ×ÔáÁÆÔ¹09Î*ÐëÍüá¾Í**Öîü
+ÍÁÓ=öî3;óÝ-*ÁÕYåó*1Hú¶ñä͹òéÝÉíÜÛ̸CѾLòëÙ?éÔíöØ1F149D°æÚ¿¼Bó41î34JèûÍQ.ö/**
+½ëÝýݲ÷.Ü3Ð0¾éÕ=ÒâF-*ï±Xä>Þ/±å´OØò¿Q*Þ/ü±õ6ATÎRKXåêû=ö./ÒLîûÏÑòÕý+Þ+8ìÝÝâZð
+øÛ×Î8º¾ñÕM󲳸.*LUÛÒS*UÕ´ã×ñUßôýô×=1ÌF¹ùÞÝU=Þ6·õíÍ·N¼»9*X:TóûùÉêÃ7Þ¹ÄKGR+ÅYÅ
+øËùô2*âÚ¼SÑ*À0ëÈ°ãSßÙõU6½ýüê;E0Y¹G*æXâÕ-»Ö±ºÜÔÓÙÕYTJèûI¾ÉAÙ°ê¶XùÄù@@Pܽñ-¾µÔ
+CÉB´òðëÉéŸI2îïáÔ.5ÌÊ3ÚHºò68OèAYßUµÉ-¾µÓS¯CZãÔÃ=ôòû9µâú.5´ãÕí´Ç+¾±áPë×°àÀû,Ü
+ØF*úÕYÞÀ7±@ëDÞ@E8³½;2ݼ18+ùõ9*ùÞÁõ9ÅíI8ݽF¼ý¶úüïöûáïùÅáõ9Åí½,ܽF¼ý¶úüïöûáïùÅ
+áÍ4žèÐ+*ü½ê¶.*Ô6*ÞýéÄM3ãç.*¾¸<*îç.ÞC*¶RÐTãÈÓ5ÔR8Ç;KÃ2?4â2:T+R1îÅ8OÐ.T?WغAW
+Ú²ïØÃéÔ,*Õ?É@V×X߸ã¯ÓÀÓèÒSÓH¿QÏ5îS.ÞíÄMå/6ÕYÚ¼ëøã·÷àãUAYÚ¼Ã+Ê-2YÛ,*È,FB±æÐO¹
+ÄU?.?4ÒRÍP*¾Ò4¾Ø*1O4ÓWÚÀ+UÄÌOÑô4*ND²;*ü6رåÊKU´6÷ð³U.¾î,ôµáÏÅðÂÈÑRÌP·,ÎT*º>R
+ÎJÇøòç=./èÑRÏè+F>UײãèóÈ=°Ò2Ï3*Z²,*,Ã,Â+ÆLÀL.²ÏMÅ>³4?ÆJ¶ë¸,*L5Õ2*Õ?ÉÔUÖXß°/±
+×ÈÃèÒ2ÄÝ7ÅÏÄîS.Þ¿EÄ.ÞQÎH¿ð>*:è2HË-¶RÑZãà?U@OÈÆÎK¿2/4YF-î¾6/T.RÏJÁL*º;KÂ,?°Âç
+=-O4ÓV¿Ä85ÔWØZßÐ?5î4µ>ÕXÃGÄF4VÐà@ü´FàSÍL·D?Ø2O¿,óÇÒSá**2?öB0*LÃ-ÖRÐRÛ40òÔDCT
+NR;LÃ.;Mµ,*4Ó1FC*±ò2ÐP?Ÿé@XÙ¶ç¸;*óÅTÉÓWÓ´×øÙïRHË4Y>ÐJG5*2ó8*úÒVظë¸ÓÇ;B>Ø20
+Ë*ÞMÁ47¸²<*.Ó1FRâ2*¼NÇÑQÏVã4µNÚ¸ë¸<*¯ÅT5ÕUزïøÙï2¼O/*XêC´éØCѳÈÊ4ó¼*¾Ò-+¶+*Ü
+èÔZâÐOÙPWÆßÃ*¶µéÚOÑ07D±½E8ÃÑR=*ð3Ò±+2*2023*Íà,ú>V׶çL?Ç;;ÒR.¾C<âRÎ4¾Æ¾÷*S²æ¿
+**LQ*Þ5OвÇ=RÓZÏWضG3*ËÃUÔW×´ûë¶ÒÑHÃ,³TÒ3*7OèÒRÎHË-6+:SÏJÃ,¶RÐN×ÈÃ5ÕXæèÐLÃ2Ë
+**MQ,¾6CT>0îÀ*à/ÂÏL+*Ñ*ß+HSÑSÓZßà³ÉõØÓUAYظã,ÜL*ü´FDQÏHÃD-:ÍJG3¾-*:ÍHÓ.öRÐRã
+ÐÃÉ@TVS;KÃ0C<>à2.Ã<,K1,J47T+ZÃ27L>*:8î,*º;MÆ>Wð>4@ųU@Xظçàã°ïÈÓé³F¸IQÖ±½2ÞX
+ô²ù*ç1JJ**ÆÉZNöãåDZÞ¾2ÁÖD8Þ*>ÖÀðºÞ.Þ¿6**?,QÙJÖ±½B2A6*ÛTKZ*Áé*ÇWNÜ*FøSÐVËDOT?
+T;*¸èÒSÓRÏT-JÒPÇ47*Ò0*FÔWܸ÷°ÃçÏ+*¿.S6*BD2R8¶J,±,18Î>Z²ÎºÞAøãÈ@WݼSZÛ¼ë,Äé³
+Fø»*X±Õ.*KôбÇNËôÎ2L-æ³84é+6ð?*RÜè¿Æ¾4¿á¹¶Í62-¾ßã÷ÜÛ+ÖüIÒ¯0¶²Úº÷ðóä*JÕÙ*îÚº7
+±@WD²ÞVÓRÍJ/*BSÎJ»4+:ÎPÓ,Ò*ÆRÐ8N¾Â4óÞ*XBK.¾ÑLóÈÓXãÒO=æÐ/*ÚÄMÅ5*Qó9F²àì³KáìØÍ
+,KêÌÙ÷éËF2æÞE?0Â+Óíñ¶ìW,Sì>úàÌû¿9Ê22**ÌòJ·*;9áVG*°åÜ@*?ó*ÅƶLî*8êÞ2+:Ò32ÇDÌ/
+À,*Z:Â9¿Å56-*;ÑÕD¶¹5¸¯åÜ¿0ÎíZ*âC°:**Ò3:ßJ2FèÃõ*żTÒSÏNÇØ?U/*߸OT*JºÞÏ*öSÕ°ï
+à³UÒNöR;MÄ6Ã+JNËF»°òà,XFÞ.ÞÃ-FÞ¾U@îÈJðÃ4@VÛJÕé¶òæAöðÉÀÕ5ê»ÄÒê;ó+ì2ʯD½.¾HðÉ
+S+ÆÇV,R³ò0Ì8+Î-ê×Iö¿-:ºÐ-Ó+ÀSÆ-îÁê×A1ÎJÛð¿ì7-Ê2F¾-Á**òé±,²ØÁÓ=-żß>P/*嶿ì5*
+¾0Àæ-.NR*Ï7ðõòS*,*Jé-¶V=:¼NÔSº4¶òÍL¿4óSÔÈAÛOÉ>UØNSZDí³4ÒQÎLÃ,óÞ*F>RÑVëØÃ5@-?
+ÇÏKÏÀXÆ;LÖ<ÄòÖ2+TÂæV*>4=*ÃX6¾:Lòî*Ìæ¾=¸òÆÐPÐVï¸7ÅÌÛ:;,KÛ.ÎÒBBH°1*ÂÌ·**ÖQ**F¹
+ô¸+*ïà+ZRû¹J*;½*ÞòWÇÞÇ8Ò0ÎS*:HR+Þ¼PîÆôP,P.8Þ¿O+¾äðC+CNRãÞÓY:ñ*6ÒA:ÝÒ4.OHæ0TË
+8ÓïÁ5-¶;:LßöUÛö8îÆOÃD?È>°ýY*¾õÕH´FSÍLÃ,Ð8*JJ¾ÙȳUAX×JOðÅ6CTâÆ;MúÛùýýí+N2Ï×*
+³>çÔ²å²/RÜÖ¿E0¶÷*0ÇÐRÎÌáÏý-¾ãMå*Øö¯ê×µZóP/ßôÀMJü:89úÞ7FÔ8Ò0Öð±2²W?ZêÕ,æÑ8îä3
+*ù3¿-¶ZÖ;+J°P¾ÂKÖúV:À,ƶ*8XÂ+70*ßôÎ?*JHÄM¸ÞºøÍ+ÅÌT?TÒÎûýÍݵÍ8»¸ô÷ýõõÏã»,OTÓ
+TÒæÒSÒà*º?Xܼ+ùOÉ>×.ç+*ÆLô,8W6D¿0Ã/¶MÆZËû½ÜTK±+K*ðæÏÁ-*ÄXÆ¿DÀò2ÐMÈÌáÏí/*Þ°ôC
+**âê×áØÞ´÷Ó-²ü3*³ãEÕ,:=Ú6ý½ã´1¼:*¾@*ÆûN18öÎÛÔß+ð¶*ÒÚ²1+ZÉ4ÌÞï¿2F¾-÷Eø¿=ÐíAüF
+5Âî,43JÒï,Gèø·Ò*ÊÚæ0Ó*ÞZ**ÀÝÂMè°?ÞMÛö8îæ½÷ðÓéA¶ýý¹õAñãáQïZ÷ø½ýâãPç4X>ܸ/+öXß
+ÌOÉ´Wد@5?RÐL*ê>RÑö/FD?².·49îÑJ3±ùýúÒ¾Î;PÎLǸ:*NTÒRQ>ÎÞî*7JÒÎÀñ¶òçEêÞÌÛ>*¿C¹
+ôÞ¯³R:â*Ëñ@Ãß¾2ÝQSJ6T÷¼ä´1¼:*¾ú¯*îN18Þϱ9V¿Æë***J3ì¶G9B+¶:-*JÃÞ+FÖ6.°ÒVWJI+Þ
+º?24**+08ãHXÎ30.ö¾2ï5ëÎø<.ز*ÒɶÂJCÖ-îÞÙ¾M3³TRÍ=ùýýöQGÆFïS6¿ÛýIøùF<QÏX/ÑôWE
+Y0ÉÒVÙ¶ïèãÁ;UÔ6ÖWÈ4CMÄ4Ï5¶òHÈÚ;>5ÔVÕ2>²¹-8»ÎJ¼.?TÂS@EäVÖUO.ÌÇÓVÑ<»àOÊ5É?3ÏLÂ
+6;ÀÄ0CLâæ·F´Ã2FBä.1*ù@14¾Êµ+ÅÄTÒTÐ8ÞÏ5ßÀÄ7FâÀýõùÝè3ÂçAÁW»±íÒ÷X605AíO6ÛЯíìÊ
+ï4ÒãR;Mñ,8û0<´Æ×»EÔNÅÅÕöýø±-»åï2ÐÒ5àÚÏ·ùåVûÛ¸âR;³¼²WÇçT0¼ËDÓWÅSJÐB°>PÎAï2±>Z
+:åïµ+ÅBT>SÑL7F¸SOèú¯âæ@Øüý½àÎðÚð3GúI*îîÇ4U³UIäöûýøðóýµíÔ>4²2Û¾Må¿â@ãýõÑ@1:Ô;
+ZõBá*¸ÄõüØ*ÝìòÔKÂôçüÍ>-ô¼F*Ñõ±N2ÏÏá0ÀáI=<åïRZÓ4+*ç,8GÓ?ºBËLOVUýýݱYµ,8ýíáÝ̺
+ØùüîIøUÜòØýY¹UÎÊÕ¹ýýï+<²îæ¹.Àú½½ìë춵ÌÑýÝYÚWºÜ/>ÔùõUýÚBÉüüIúÊÉ2/F¸ûô±QäÚÚüýD
+ÙÔIÂ<S4*JÉúýý×ïRºó4+îíüýQüÈ5LѺÕXçÔüýY¸C½²ó½úµ±ÃçâÍ´ýͶD³Òýùý-÷N+ñññæÔZÍJõ,8
+û8ÐýùùìAã4Q=3éýîWè2àáÚÉüݷȾÈöMOÛý1WëÍ»¼ùýý¹»¿ñÍ=õýýÞIÂ@¿ÐÌL»<ãýýÍ7ÅÌTÒTÐR?
+ùýÝ9I³O9OÏR+ñRýý9ûU@äýû¸ÊÔR×DBù½Yü7Ôäûý-ÜæËÛÈÓéÑHUêöµôýý9øýýöìÍ2:QàRàøÝÅÅSM2
+HGýñõ´õú:Ðâ=Üý»»¿±âãü½ÜCý+N3A¶ïýåèùÖ+âæ;MÅ8ûë¶ÒÏLÇT?È×¾MA?OÍOÐP;-éýýÍF4Øîýíù
+ïÕ¯é0¼ü½ûÜ88ÎìûýýIÝýä5<ÕBVê¾5¶òýýùÅü21<Yú8QYýÁ½çá3í²ô½÷<».ÏàáöSøý½ÊµÖ÷éýOµÐ¿
+Â5DÌ;ùýÏ÷Â×ÁÆC°ò2ÐÝ7ÅÀèÒ2¾5¶òÊNË,Å÷ýý¸ç̲à-8»´ëÚ·ñÜW¹D?-ÉççâéÅGèýÖÝ×ÏMìÞÏéý9
+Iæ=Uî.ðÔàü9I=Æ@ÓFíýç3ø¶ÐÄ91>òýýL÷N±ëýEÌ.N5G¿6¸óýMåÏ,åÎMæ2ÐMüýýê¶>Õ°Ã**´+żè4
+JÍZëÐRÛýµÍÑ2°Õýùô:ͯ=ÅìÚµëú:EÄHWMæ>Y1Ë»ýýÌ3ëKåíýýýùëýýáù¯YöJسôýöXñÆBÕêü99Ð9
+ïÐåÏSYýýõAåJ±û½òRPÚËKDz¸ý9¶7Ô<Å;PÈB³øÙïR¹óøÓUÕµ+ÅÜèÉKÒ¾ï5úÝýÝÛËÇT¾ýõôòÎR;-SÖ
+±ØúÎX1KÔ-ÂO7Åó±úýý5ãCAîæåÇçûýüãÃú¶OÜæü9ݱ3Dìçåüýõ0<MƽðÅøýöóÆRñÕýÑÍÅÄ8NßÚ±ûý
+UÈßàQ<-ãS>R½E8ãÒT;*Hëýýè+0-µÍµÚùüý÷Qí>ïÃÈèݽACRÞFäåÛ×ìÐæú½åÛY<JÀöWýíÇÝ×ÀËûýý
+¯¶7ùöýI½¯7IÀÈêî,Y=åͺãý9Qç³ëLAµûýÃO÷:æVýýÍÖÚVÜÊçõýï±±<ÃÜ;òæÏMÇü´F,T+*ÎTÞÏéIÜ
+E¶ðîýýõóõÌ4Ðã̹×GýûÙûÀ8ý½¹YçS×á÷ýåäèÃTÓWý¼ÈSÐ*³+ÅXÁ1FÒýõÝëÈÝ+ÃóýûÖMTñ¸P+ûüýù
+ä»û³úýÑEúÀÂD½ûI¼úÝ-UHíýýAú0Æ@»2CNÄ:KøÙïÆQÓLãè>²ý9*¾ñýüDéWLÌÒ¿ÁPWúú»º¾å18²ÒGÃ
+0H@ë÷Í8Þðµ93Ï°îæµ.3=ÍûýôÕ<HÂF0³¸õCçîÏã²öú;.ÜÜôǺ¼Nï0ÙûYìµö:-IY÷@<Oæ*Tâ8G¸>çí
+ÄMÕ?éÔWØÆóÃéÛT˸óTÒ>Ù÷JRÖÖK.T?WXìPÛÀ÷PÛÅÆÉß±Ç>¶BÛôó¾ù¼?ö1Ç=³ÚA×±Õ,Ç,1<Íøóèʾ
+òæ,èù0XùXÊHÀP·ÀTõ,ÄÙ±EF¼NÈDó°ýÍÛÊééèAìÊÒÌ6ïZúFàÒ3ÑPÍü´FøXÛ¶÷èÓU?NÌÄ4>UÔZßÀõ
+CMÅ;RÖ´ßðË*G,Ó27A°Ú8MÒ²Ãøñä;ͲÆ5>QÒÆïá9CàÑã3B¾6·L´ÔJç<åÎLÌÇÑM¶ÞòSâÆõÇÁÆ<HTÊB
+ïUè*àµùýêÑÚ1BÄöJKàÂ5ÕáR=RÍL¿4?/F¸*ê×=H<D¹*2Þ¾8øÒ11>*>6**1Lî<5L**òÙ¾IÁ6ZÞLWà
+³AÚ@Å6Z:Á4FùÄÁ·**XYN4ã·ö0ç6á,@ðä;ìØJ<Ù¶=-C¿æ·F<UÔTÃ*öRÆøÒK±P7DÜP7G¼2¯4³ÈÕDãT
+B°ÜXSðºøBøò4ÔUÕÚ;DWÖVçðã6Å-R;Lºè¾Sáý:D²åÊB±ð¶J¿ôÆïàÃÊGÜRÒTÂä¶×Lõ÷Ð0ÍKø²ýõ×Q
+/¿ÐOÉ?VF6E½2O¸.3<DzS<¶NÖËêG0Zãï0+öVZ.*ô1Lî:58âÞS*J2LÍ9åúÒ½*øN4A¯FPÑ/CPLX¶ºE,
+Còã×=FÝÅ>Qî°5°ÅMQÓT?UÑTÇ,/?GTÒÆÏ**ÍÂ3>SÒTïTå·ðÃêBîÕ@å·WÖ¸ë°Õ²ßNX1ÌF·öúCâÆMÀ²
+Æ9I¾2KðÅ.ûçáQ:LÃú:I¹òò+.ÈJºTX9G¸À26:LÄö/F¸TNÞÞ×=å8HBÆÏMÆ<Ã*¾P·Þ;?µÑñÃÚ2ÕM8àÏ
+ZåHLÞMñS*NVôêÓ3.1T3ÚâVê+**¾¹>ÝZßÒìê+:òJÀ*HU:B@I:-YÖ+@ôêÝRÎ*ê/?2¹6P2U.Æî:88Úà
+/72ô*8*ÀQÏÉÛ=Pê/õ+ê9òÀ±åHæPôê=Ë»Ð5ADðæE8ÎñÆàJ¿R,¹²**Ú*È2¾TÂ=ºÎ÷:6Á1KÕ=¼SS·J-
+RE=/>*ÚÆÄE=3Ŷò=á6à8Á=·¶+31öîìEÌÖ³PÁÞÀUŶÑM³I1ýðPºì²ß+HF¶ÐÂïæSÓ°ÃÈÓTÒN¿R/*X
+èÒSÓTÓгÎQÌÜù¯ÞÈ»½õÏ;üà?5áð³?ÓI¿.;T>2B**670ØNÆ8?41*ÆÀ+à/ÚÔÓêSø÷±2Ï:,·.Sß;CSñ
+Q²*@¸>S<OK0LçÑFîRT¿U02-:â5ÒðFBÒ=;îÂ,DÕØ.¹ô1*òD+3ËNöÅ-Þ8TCíÙ½ÇÐê+âØ7ÏË·+ÉTâØá
+á-Ðìò:.ùúFð.¿.Àì÷SÜ4+ßÑ°³ÀZåüÀ0.¹*1*ßÁ7ÂÌN/ó,ûÛæ/äÌûâNêYA·Ñ510ðèE8ÞïÆLJ+ç8°û
+²*Z½+>;*Ó2QöJ;Ä0Î:²-QúÐÐð¾0Æ>µãM78Ná×ÇFøǯ0KõúÇF*Â1:ï0O8»áÃàBô¿æ?½ô2QVîÞ¼8üã
+ËöÚçÀ-º¶îN÷àÏñØÓé@XÙ¶ï°Û¶ïà³0Î.Þå+üìÕJ°ØÊ·¾¹á¹XAÉÔÄܺëS½8Wðò3¹,ó3ÒQÏHË,FD¾+*
+Ý/ð뺼.OðÒBÞÕëÂȯÎ?¸:¿Q1Â*JÃ,ÆRÖð¾MLõOöà7àÌûöÏíMB2-2;*2,<³¾òê/ãÀ»PJ*ûVCî4¼±å
+¼ù,ô*æ¸ôà9ºÅ;µSãݾ¿ÏFÑÌÓܲAäAXµLJÖQBT5*¶M-*Þ+JöÊÉà0ÁѵÎݾû¿XZô껵æ»:ÝEÛ¶.ÅAL
+îÆ>Îõ0Ü;ÄU7¶,>+*Ó2õö*6FïÉÈÈ-D:7º97FÆÄ*¾UX·ð=1öñN8XJàËUFøÄ5Ã;ê;6JG¸Â=@¶ZI1ýðN
+º8³KïßÃ=ã5ÅôUAYÛ0*P+¾Õ=5î9ÏUÄäÅÌG»ºÍ¯ÆëŲã4MQ¼´=GÂ@¿4OL¿T+*ÐT+*Ò÷¾J¾3*@³UAOº
+*K4OF8ÞÈúÂçLÄÍOÕÚJPëÈ-87¹V½ÌI¾=ïøäSFÄ220´U-æ÷¿Z05NBX?-ZKJ2òXG=ÛGÚî,¿Ûß¿DKôËä
+3¼û289îÆ>IÜXïÄ0.¾//28D/³Tíßé¯GZ½Î5K*@Çâ@Ò9ó,V½,YAÜïÈ>VÐǯ:Iæ¾5J·°>ŲÐÜõL1TT8
+>,?:ÌûÚ,ND綴<żðÞ58ãKKàËUFøÄ5ÃKê=ò¼õ2QVî4¼íúãÇöFèÀàÁMUÆ>8ã?U1+*T7,öÉ÷ÓðCá0Í
+J¼R;ËÜM¸¸ÂÎ99éR>¹1+À±Ì:SÈ>,ÞÏïÙB;I¯Æ:÷NÈF?Ù´è>¹XZBPÜÐÃÈÑÅáø´é=²-AðñÚ.Û¯-åÍMÛ
+ºÜÂ0ß»<ó7Cá>çÑUÇÞ;GõÁèºÕOÍU.æÁ¶V;îÔôºÌXE¹ôÔãñ.±,ÌúÇÈê×AÞ74DùëECRK.ÒºES±/2óóñ
+N±Úî9»:¶·Ü*ºÝÁM,ñÒÛ¸ÃED:*JG*.ÕKM÷Ù;P¯åüJAÀ*îä8ÃÖ±½Ï5Ò*³¸1DðäEXÇ,ëVLP³¾>Þû,RL
+*T;ìæâ¿õÛæ¿ÈÈ-ÃZ³:ðÔ?²ÎÐïæNN+ìÅ@ÎZï.¼ÌJà3ÈÚøÄ=ïÃÞCíûè;ÌÖÞ70HùÉ5ï·Ò/Ã1ÅÔ:SF<
+UÔXÃG8IHB*R³8Þ³ôÍÉ3ËHÅB·RÒÜKVÄÆξ¼ºW¿Z³EíW×åâòR<OÏD4ô×ÏIÅËÕKOĺ<VGëÈMSرåQ4N
+Þýø*°æ-½úÆB¶òÖñø»Çú*°NÌUéÜú¶äDç?8Ç3Cð.U<¶òê½QÏÄG**-ÂÆÇá+/*/-*JþùP³K¿8ê×Ý:2è
+?@¶ß:0±Õ³ßôP>ßÀRKNÂL,P³RöãÁ+KÖÜî9Gö+ÂY¾ôÊ/ÚSH=Ø¿H.á.µ²**Ú*.?³3¹ò7RHçÃ:*;HKîó
+/8ÎU78HÞÒEí¶O4NºÀÂÓ+LéÀ5J3+RöL=AÜõL1TT8æ¾óûß+à,ÂÆÄ2¾5=;Ø@Fؾ-?¸<1A:ïà?Ì»áÇà
+ÖåZ2Ö¶ó2QVî0-ÍûãÃöFèÀàÁMUÖ-FHÖ*íBÛ÷ÏïÄÛLãYëس4ÐKÂ,¸ïÛµE0øðéõìGGLÃ>ç±¼ÛÁÓS±Xô
+ÛÕ/ðöÛüôµí²GERR×Éê-æPAíøòÞJÃ<0íIAùÁ2ØÞñC¹ÏBç4AúHSM>2ÑVSÞ¯ø?*é9ϱÀ+JçYÀÇ:4æ
+ó7åYÕÞǵN±;3C*>½E,QÑ;ßMNô7å½:ÇCâÖ8åïæº÷ðóU.Þ*ÞFÞ¿/¶¸¸ë½µ»BVÎì;I´à´Û<?Ï=ýYADF
+±BWóôÛ@TÂçëüCÛÑE³XSO9²9YâUìúÜÝØñO8·4¶òº.÷êÍGèWºÚͯÅé=NNST9YÖÙI±HëúXû1óñ1>¯éÀ
+á=1Æ<8ËÀ+MìPB/<XØã?O,?Öò²êÄ:Ìá;õèÓÉÕXܺ?,Ú3*ýÃÌçäèùõIí¿¹XùÎ×YÖ@øðãǯ߰Ä˵Üû°
+J»:/³EÇÕ3±0Í¿öçÐï¼²×ÃýÕ<AYúÊPR×¹VÑ1QýYöÓ@Zèê3èåÙNâT²ôñðöB°èFTÛýPE.ÅÑZéÁáIA4å
+ï2XG1*µOè?´üX¼ÃêÏñµI¸ÝUÏ6øï¼57ßàä8³Ì»²G°üêÀ/Ž4?WÛÝõßXï±NËÓ8ÏÇ>YÚËQRÕUêÀäOýÍ
+=Ê=Yîî¿çÙáú±7õàÐضA³ôî¯èüãA>äÏWÍE8+Ô¾àC*¸óTÔ-0*Ö,*ºÓPÂôöï.ÊHµDú¼ÓÚ>;Fìß4ÀLPÍ
+LîØùæ¸àîÏNîSÜüAòèêíÁ»<ÄÁDɺÛ=Wî.<ÔÔH¾MI÷ÂÈD¾6ïòì±ôSAÚêMWä¶IÂ6·FYñ5B,SÕå7Å:éÆ
+¯æ¯.Þ3Fø±+éçø.ÇÎHXåËP=ÇUP@Æ1>8ô:A·0OLòØÍ8¶òG½ÉÅV´TÉUëûÕPÞ.Fø¹Uâò¯²5Uó<¯É¿MÍà
+ãÌLȺ¸ÕçûÎ-8Ô;A·2·OÈNÌíCP6NîéíÄM5äUÖY;*>VBZ?*H6ÖYß¾7²úÔÕÉóÈ?TÐJ·ðîLíÔôQÞï°»Ð
+DÁDÇØåG³ýÁ0Côã;@EãC/èõHV?³T÷ÙãõÇÏá2Ò¯8ô¸Ä°ðæúÝÐÁ·Ñæ=SMÝõòÂÌç@úÇ7KÌÚÒSAç-Ãñä=
+Æ@MF,*CóÞ¿9âÑ.*¸ÐͶV31FîÌ:ÌF¾öZ?2J0øÆã<8í*μ:1AÉîJ/8öJDÍæQ¶òÛ¸óðÓUÕYÜVAYܺÓ+
+Â1ö¾ÓÅY.Ó4@WXéÔWúTÅ@0Åñ=X@ÐD¿B»ðõÚÑì±0Mû°Û8C¯âöC÷üUÕLäÒØâñÃÏá7·4ôó¸Ä¯ìöÊýS¼·
+ÁR=RXòùËÀ.¯´HÊÙ+²ç=R¼ñ-³µì²L´I¶º,¹÷>Kï*JÍè=°*ÀV¼K»¶ÏËGùMÕÎ1ÐÝ7Àᱶ½9µTÎÝèB-F
+àVÖZßÈ?,Z6:4Z0Þ¯O¹ùüY´ÌN÷U׸·-ùûVRÌPÜî¯9ËçñƱDÕÛ²G¯@èõ¸Û²;MåÙèõGê>¯JÏOüÌ5µMR
+<XWÕUòàçÚú½<ÉC¾<÷OHÛíú-äRõìÑ:GÃ>;ÊìIôN=äÐZSÞÏ»ÏJ¿ÛÆDYÖ+@ôØCSJï,ìFK1K*ñ8³ß,GÓ
+E¹³28Â2FR*,¾G¾Ç+å8JÎËê×QD¹Ò*EÑKZ+ù5T**P;.**Ï.à8Á=<2L¶¾/ÅJZ.Jê¯2B¶*8BDÍ,JàH40
+;Cé2àÇ@>µÚîHåÄ<¹ÝîºZõëÉG·àêµôH4T>ÓQ7Ï-µë¾G°àNß2,5SãÌàQº:F¾ÖùîϲB:ÞOP×ÄûÓÐê×É
+Yµ1/Â1ô/0-+ÞáÇAÊ:BôøÆ:籶6³6WV+R¶6.îÙ4ä@í×SÄ0ÛMÐÂ×íÜåQ7ÝQºVM±øBØâé³I»Q6Xñ×ùÄ
+èæ7ûºøüæ.ÊÍQ-ÅîäÞ;TÍæO7Ê-ÅüQ<PIÛéú=ÄýÀVúU¼÷ºìäôûí.-Ä<¯SÞÏ»¼8·ÀöDXµ¸+ôÐÚ?Yæ58
+î×´M²-TCíÙïASê9¾H;¶Î*KÀ=-ÊG¾ÇÇÖÝXÐÒ*-ÏUÌGù5T**P;è//ÛñQN:Âî*58¿Þ2¾Î¹HZ*N?¶BL+
+ËU7ɶ;6>DÇì=.QØÎÚï,8ÛëOêçïó¿GCµ1Úõ¶äÖùBRÏåðöÚêI³-²DøäK2æ-Eí?EGï¿î¾<¾Ç-åì-ßéM
+ÖùòãÖõܱû²¾Zåü,6À*̺ß.+¯QÀ¸ùÚï»F¸XÖ²ßÐ?ÉÔV°UÔS*ÐOUÔR*½ãUKÜæã5íëÁµY8BJO¶îZ½àá
+HDñåϾMAËåÉHAß±µë³-µØXöø¿U÷P¯Çáñðä:OÊÉéó4ÛÒ·÷08GåI±úºÑÉÛµÕGÛ¯ÛõY¼;MB+É+8˸Ôø
+Ð7âçóê+ZíÞ¹JÞ+FøóêÃAÎîE¹ô¸ù,ôÀ°ß8;¶Î*+Å=-ÊG*í7¸9âÕÒJ:.9.¶K/Ó2A²öÂ.L18Zß;2F:¾
+2¾-îÎ2***:Jø5åJ**ûVå¶Ð/ã7AîæÕÉï6ç¸9ê˶:íQÖ1ø3<H,*¾øÎ+ÙÌ@L»Á¾´G¹ðM8÷ÞCFÌALîS
+¾Õ6ÖDî06À*̺ß.¿¯ÅÃëô@ÁM>´êÖ¯Þà*VB¯àÂGB*9@6ÖYÛÐãéæ»8Oø°Þ±Ë.÷J¼+ÊäÅÌL84?¯0ëã·
+=ÆÑð1=åÄóN+ðîLô5²æD½ÐÚ7IÉPçLµ:@YÁQ×ÂÛæê¿âOÏ,å:ÖG¸4·Oá,ìúSZÖɸHäB·PSÉ+8WÑ·ÀYI
+×FUJFùóAî2×±íëßÀÖEë»+ùñ-ßÂ2FR¾¾áÇ¿¶*ÈN°É-FA3¸1,¾/1Òî0/Á*XòK»L¯ßË;ʯFZÄ-î:6ê@
+0Ú203ÄUGá;IÆì8ÌÞϯÍåçÞæG¶<Ð1¼Õ64>ºYæ¾/=2×ô-1NÝÓF·à-¯Ê=,1îÖ>Þº÷ÊÒ÷¿0F,-îïÆ=À*
+2¸ÆÆXÆLYßÏ-ñóÉÕYݼ÷<ܼÓ.Â+6*öVÍ47Ȳ3=OÜæÎLÄ8SØ.Ç-ÐÁÅ;PÏJSð¿îæC.3ÏMú8D³ìÚ3.Q
+6ÍM2:G¸*S,ãK·TÂQÌIÄ4·H¶úJ,?è=NìQÌKð0Ì,âÆAT±ÄVúòÚÞ;ÐÌã*ÅøJîðº+ö¼¾»Ô0LìØÌÞÏAíP
+4>±Ý»=ÁÎÖÙüFKÂÞ¿,6FÄ6Â-ÎËG4G*.W.Ì÷ìêKGÎÀ-;>/¶9.ÚæÁøÂàÀ5MJ/Rѹ-Å1ÞKWë÷FÛ´ôAÌß
+*:½Ðð2à7I6µF¿M<êÁñR*OôX¶îóÆ/1JôçNËÔ¼ìÈ×=¯êÚZ9ÞÁ.Ê÷¼äÆGî+D,ùÞ2,;àùCFÐн>Ó02;Ü
+Y¾Kö*ö¶â3K*>ùò<W0ÅPUÔV×ä*Ä*8W¾áDÓÇ;J¼,?ðÅ<·,Ó3=PÆú:JÀ4WØÒS<IâåÍLÇ6/ÈáëÒ¿±Q9H
+·îRHÀ0û÷Áæ2±¶+3°ÒRÊ¿IØÒçÐMÀú6ðT·Qº>Å7E궶õÈÔÞ+ÃV0¶Ò-I¾ö/æÌ-ð±»½¶¾ö,8GÃ.ʲíì>
+ß,7ÃEù52èÃ2FR¾¾áÇ¿¶*<H+æ-¾¾Íß:Z¿û/?F2KϳßJÞ;32ôê55X¾*Ûèîî7FH*1òËèÀ,G8/Á40¯B¹
+W5Ìöß/µFîÝ-.ÂÜÁ?·ÐVÂ9±å8ú×=Mî64¶ÆUÖ±O4¹92P3ZÛ»Iî+Ð@/ã2,ßôâ¿CFZKKÜNβV¾Z¹ð,6
+À1¾ÅMÌ2,J°Æ:º:71.ÎËîæ·ßà?5ÕVÙ°ÇWØ8/¾éгU@UÑJ³ðÍRÓÀÃ5AUÏÚ=PÆ8KØÒS=òÂçÏMÄä/ÛÏJ
+À.C°òæ:YÂæ;NÆ6OØâG·àN3<NÇ>·OÅ6OàÂS=P¼ÇÐMÆLP9üÑýÙAÒóøBéºK¹R?É+8=J¿-S¹Ì*ÎúÃ-Îé
+EÌå¯.ZûS2NÁ/3ÒÞ3KN,;åQ*Êå´;1Á<+9ò74FÅMË?UÔX×°Ã>ÀV¯*;ÄLÉÔ2¿¾*Í?éÓUؾ3EäÉ+¹óÇ=
+PÊ@·PËF³ð²3=OÀçÐS*Ø>çÐؾö¾Ö+èÒSÑ3À2¾R¿Þ¾;*åNÉRïçÁ-9í±ÝºòÃÊKÔÌë28ÍF¾ÂÉ@UQâÉ/S
+.@öÖº*VKWô5ÅÄ6×°ãä*J×D¾K¾R¾5:îèÞ¿=QÌDY×ú?UÓXÛ¸ãè?¯ãè>Þ*J>¾ÞTÓHîä/ä*JÓ-JR5îÒÂ
+·Éæ<äºäö;E³,ÇÈPXܶúDÓîF8¯AZÛºó4-*Üä*ÞÕM-îݼ÷øÓêسêFE¯ÕLÃ,/4>2ó´*à.ÞÑE32MBPR1
+¶¾/8ÈÐKÀ0+T=0ÉS=1ÏTâÖ»ì¶>ضG2*Ö³,*´Ë,6-6+¶XßÊGÁô6BTøÇÑQÌF»¸-*Ï+¯*µ>îR-Ì?Jξµ
+°NR9FµìÞϽ.³À³ê×Ý7ÅBéÔVÛ°ÓF¸WÚ°çسÉÔW¯ÆAîÆ¿C¹ôêCYÑP7KÑD*æ²0îL*L0Q1íÊÞ?*ÂE85¶
+PÆ87D>R;MÆS>XàÐûã¶.%
+d
+569 104[1 0 0 1 0 0]sl 8 mask 0 0 di
+/mask 7488 string uc
+*ÞÀ**ü»ýÅöüÝôûýïùýáõýÅíý9ÝýI¼ý½úüýöûýïùýáõýÅíý9ÝýI¼ÓY-%%
+d
+/sl 59176 string uc
+äÕí´ê9+*Ð,¾é@XS*¾ùD07D³ç¼ËLÍF»D,¶<*ÞS/Þ70Ò2Ú/¾-AîÌB³àÒÇ=QÒZA±æúý½ã8YÕ,VEݼWG
+ÃM±äVC±36¾E¹ÔËF¸óæOñÙXãÀóèÓVÕâÓUC6*ÞD-2L¶´*õâÞ¸KùÔìíÄM.ä5BYÝì,¾*-@2ÞÁCáÄ7Ù³à
+TÇSÐP*NÒR±*HT>SÐNÃ<?4ÎD5¶¯*ZèÃ4.,>.6-Â5F:ÁÞ+>O5C³éü´FØWÛ¶óT-*ÚP+2AXØà*2AXÛP+
+ÚA¯åÐO±ÄÉ>òâçÑQÎà*ÞÑC¾Î*ö>8*D?/FB¿;*æ>À*FÓ+¾QQ0îÀ0öÑPÌV;¹@ëíÄMçãUº¯6¾öð?Ú¶,:
+ä¶êB0*½;ÁPËؾM*?ÔRJ¿D/¾ÏHÃ,?è>JºJ?Þ¿5Ò7Ò-Ú+¿E@BNJØƯùÚïÆ´÷ØÓÜöð*,*úÔYáÌOÁ´V@
+2³,ö0*T9á+X6ÞâÞöÞ;F@2ÚÎMîÒÎ*Kß°0ËØÝ7ÅT7D´æÒO¹D*-*8ëضôì×ñÔÊíØOéÔWÚ²ï,×8.¾øÈÓ
+U@YÖè,à.à0ä*X.¾îàóÄ,T+XÆ¿KUõìÛÝ7Åà5Ö2*öóR*ì*¾ùðOêC³éÔGñÝXÏDOè>SÐÊÒRÏPÛ2¾=32>
+ÆÀ*à2À1à*046AµêÔû붺ÒÄÞéZïÇ*Ê/¶°æÎKÙ@WCZÔé?UÐL5ßÑD*A?4>TÎL¿DÃJÃø>T2MË,/4Òæß*
+¾50:ÏJË?./¾Q+-ò3>Sá0ÄUDµçü´FTXݶûðÓU>J6*ôðÓUÕX10:4Þ¿C±ÔÊC²éÒ÷²äÂïÐóÈ?T³Ã.ÈÒQ
+Ï*îSBïÇ-ÊFÄ+6JHZ¾=8¶N.´UD³èü´FàYÜÀ÷,@,*¼Ã,ÆYÝè*ÞÕJ¾JNI@WD²åÎSéäÕWÁPëױᾯVÒN
+Ï<OÈÒE-*Î8F*ʸZ*ZèÏ+¾Ró+á,<TÒÐ,ù?WÙ²½E8Oس=*LWÙB*Ø@WØC*ÝÄËسèÒOÉäÛÏUÅìF·óîǺ
+øL*ºHºóàKUPÊBäÓÉÔX׸ç¸;*Zé.*î¸?*õBJ¶ë¸*/ð5Ø:ÂÝ7ÅPVB¯ÝT*JÖ**+0V6Þ.JJ¾,U´ËD²æÀ
+>ûØ´êܯùôËEÝÔ7سáºÓ<ÃPÓDóî**Ó¶¾B*J?8JRϸá+ó*O.ÖÈB³ïÜûë¶òܾï,äéAXÝ86*ò41ZÛ¼ûð
+?0Þ¿;Á@ëC±æÒDzåL9+Ù2*õ´ËE³éÄ3Ñã¹×ÈOÈÒRÐLÇRÏä*NÒQ¿+:è6Þ.¾Ç</4Ò-1:ÏLG¾*ü>È>VßÚ
+SùÚïRÀ+ÉJ*6ÖZâÎ/*Ö³éÖW5³F-îèÚSñÔëÙ²ìúױݴÏLO4?<OÈÒB*GÓÈÒSÑPÃ-Ò+Ê*FÖ¾5H21ÎÔVC
+ÙôWØÝ7Å86BZߺ+ñ/Ê+ùãéÕR¾-320îܾ?ÁôÊC²æÊD³éÐÃ*¶²éÒ¯ÑÔËصXëÙ²íÐGUPÊûè?UÔZß=,:Ï
+LGH¾=+JÒ´35²öÙïæß³-Q7Ú´íÔ÷µìÜWù´ËÙ³à7Ù2*ïÔW*¾ÉÁõXG¹÷ðç¹?*ÈYܼù*èE+ÎüîÏÉôêÕú*
+¾ÄGUAâîµá;*E**ßX8JË*65*îæÒ³Ù´WD´çúD³êÒWÑÔËDàPëÙ=+îçÌ7=ô5AUÓÚ>SÐLÃ<³4Ó¶¾ñ,¾ÏN
+Ï,ôV¾ßíÄM40êÖ2*60ÊÖZãL+RÖZà8.¾--´6ׯßÄG±éÔ³é@Ü2ÖGB*èÄëÙC*Çôë+*¶¶íÌ;áOUÓUÚÈÓS
+ÓRÓLó,*À,ÞÒM+¾ÔPë4ÄWVßíÄáçàD**@4*¾Û·¾Ï¾¶*APêC²êÔ¯Ñä×SÙP7E³ëÒçµèÜOé@WâÏUùäëE´
+íÚ³ñæÐ?Môé@UÓ¾Ò,*C³è¿-*Tï*¾èFÞ6¾ÖD´ÞîµáÏE-´VB±ßÄ/±ãÀ3=´0*ÆGB*IÄVÖZâÄ/5äÃ/U0ë
+زêÒ;µëÜË**³Ó*10ÞÝ»-18Ú¶îà÷µêÈ3ñ³É@VæT?TÒTË+BTÓP×4-B+*ÔL*æ@°íÀ3ü´FеîÖÃ*À³+Á
+¾ë6¾µáä7Ù´ëÚ³MëØ·54B/2DÞîàÓÁµYH¼;*ÔYܼöT*L,L-úH¼úüëñµ9IÍ1ÍÛ¶ëÎ/ñûûðãUÕYÝä*J
+ÕÖ¾B¾¶*WôÌܽø*üë¶òá¾C-´6Ö°ßL*¾ÖR*IPÊB°àÄ7EPÀ/¹Z*ï+HÊÖZâÆKÁ´ËJá@ÂSÜ·EÚ>íÚÓ*¾´
+ï+,XÚ4*1õëD°Ý¶ëÐÃVÓLÓ,¾P×DóÞ*úÓSÚÔÃù@ìÙÝ7ÅÏÄ;*:êöZCïÃ*.1¾Zó*ÂVC²KDZ+Ü6C³ëÚSñ
+P˵ÑÔ7ÙµèÜS=ìÔÃFÌG05,¶;ÅÜ7Ù³çÈï°ãÈÓTÃ4ÓSMK³Tã;ÕȳëÙ¶ìàûë¶òäÀC=Ô6Ö°àº×¯àÀ?MôÊ
+C½äÊC°ãÆ7E0Æ/*ö¯âЯâ4ÕëÓüÑܲõÊ;Eä6AÉô6E³ìÖ/?<´íÚÓC¸µíÞÃùô7Ú¶+ÅO*ì7د޼óÐóÈÖL
+?ð¶øBðƶ·5A²÷µáÏ0M@6C¯âä7ÛÖ¯àÆCÙ´ëD¾@ò¶î2Ä/2öÎÖ½ÑܺB¯Þصç·âÀ/5JîãÎK-Õ7Ú³ïÂE¶
+=*ÀWÚBáC¾**9QÌÚ¶ïâÃùäß³ÁÄêAVÖV÷UÓXÓ°Ãè?TúTAµîäÃEõµáÏÂñ´,ÞãÏ=õËE´êÜ÷¶ôôãÉõÌÛ¸
+ÌXF¶ìÚ¿ùäËAëûºùóãWÅôÉ=-ìÉÎH·îÞÙ2M9ÕìÚºúüïéå÷ïáÕ9ݼûúÓ*μúú/*¾½/*¼YܹñÜ·ÁäÊ-=
+ô5ÖYá¼SÁöü÷ùåííÄM9´ÊÖ¯ÞÆGéäÏG-äéAZáÒ÷´ìØ»ñ´7D±ÀÊÖ**Iå@¶óëáŵíá±åëø°åÄõøºóÒ;ñ
+?6Ö¯XÊEµíÚ·Ùô˲ùô,**Ú:¾D¾-+îïÜ»á07ÖWÔF@TÔTÛðãëE4õWÚÝ7ÅúêÖ°ßÈGé@6æÑCÁôêDµìÖËG
+ø³êØSUÄV×»ü´÷òßÓåÕYÝéí9½úïÖIÁôÆóèá0Ë×°ÙâA³ßÉ1âÖÜRáÇùôWF·ï*:XR¾É5AìF·ïÚ³ñåÌ7-
+PéÓUßêÙ¶ïæ»ì¶òâÊ/=PVD³ìFÙ´ëدñ@XFõP7سêÒOéäÝ»áôêCÁÃãùáºJÏó7Hýýüݽýùé5í»ØÙÔºA
+H½øÈ;MêØ»5M¶;*ÓÅÏ*.XF¶ïÇï*LXÚæáRNÂ06ÔWßT-ü´Fø²ãÌ;É07Ù³ü7F·îàËUµ8ÁñÔËEµêÖÓ0¸µ
+¯ÇÑQ50**ºNIàÞßõýI*ÞùÕQìÙóçÊC¹×ñæO5õËÙ·TìÚ·òâǹZ*¾XV¾ÑMQÌÙµïâÇMòæÓ55*óL-ÚE´íä
+ÓMåìÚÝ7ÅßÁ88Ú·ïæÛÙõÌáÁµ9I»öðãñøì×¹1YG¸õÚH¶VÏáå¸/¾+*N=:ðìï9êÏåAXÚóêÏ9Mò/+ȹQ9
+H¼úúH»úöïÙÕ9Iݵ9ݼüúãUåèãÉ1YÆJVÞ×*úë¶>åÆÃ*ö²éØÃñÄëØ´èËE´ìàÇ5ÉòíܳɴËE³Uë÷ùFÃ
+>F¸**2Z÷B·öøÞýêÏýÍÍÚ¶ìÖG=ô0÷ßéäWÙµðVF·ðäÃ*Â,ö·ðÞ³éôWE´*XBJº*ÈUµXG·1,Júýý×ïæË
+GU0ËC³ëÜÃEø³êÖWáäëEµàËÙÎMæ@DJçËã¼½M7Aî¶0*7+ã6ÚúÐMõ9I¸ðرÍ@Ê5è=Õ6EµîÀ8Á9å8+Ú*
+¾;RÕÄëE¶íàËMQäÃ5FJñäËùÙïæÍCÉ@ËD´ïÜ+´¿»äWE´êà*ÚÙµîà»ù@XFô@9Xõùí³¶/Å8*¾-ËX´ìWý
+¿GøúæÅõä»øì½@9E¶îÀ7á7á3-3M9¯ÚµëØ»5ÁZòæÓ-µZJèÃùÙïæÓWÉÔëE·ñà·µìàÇ-18Ú¶*X¶ÍÆ5AØ
+åçÇ5µíëöýÏ¿,õîB.*K>°UC÷5¹òõÍ=íë¹ZÄ+ÃÞº¯1X8G¹ôè×¹õ8ÓMÅX*°ÊL¾¾á±µÌF¸óê×-òü´Fø¸
+ðæÛÙµÍHº4ÍÜ»÷îß5-îõîçÉõìG»ùÚܺ÷2øGY½ÕѵF-Î2ÌÏ/»ºüØMíݼ¸ðܵÅXéI4NAíH¼üü?*¶½*û
+÷áå9Ý»Ü9Þ½ûôóéå9ó5ÖííÄMÅP7Ùµïà·éäÝÃùÔ7EµíÚË+ö´ëÚ¿-åËÙµRZZøéSGÅM1*¾*<åÚXH8úIÖ
+MQ9ûöìÚ÷À²2îÃL¶·ñæÏUåXÛ¸ÌìÚ·ïàËEÕìÃ,θüýýê¶òåлù@XE¶ìFÚµîÚ»-1XEè0ÌÚSN2ALJá/>
+êÜõO3ÞïêÏüÆäA±ôúé8ûüõåÓ½Y¼ÒDÝÄËE¶ñäϱ1éϱåÌÛ÷LKÕXÚïNÞíÄMYPËØ´èÒ¯ùÄÛ¯ñôëÙµì09
+¿ÙBM÷ÔWR<<VÚÜñJ·Þ¯2*=Æ°îWæåùÅF¸ýôÛÉÝH?ر8ËسêØ·5Q8Î5ºòôæ×MÕìÚ·îJÛÆMZõµá;Z5Uò
+íÜÇUÅ8F·ñ.Û·ï»Ä8Û·óàùFÛ¸óØ¿N7IZÆ=7F0+ö/¸ìßûûýýüÌܹöâÉ´ÙPËRÉÔðæå×¹AYH»ùô÷ºöê×
+ÑQíH»*ííÄMí´ÌÚ¸óîçáAÀÅí×±AÍH»øàùºHºöôïÉAìVùH½è?C߯2¾Õ2öý½üï×½õßËÔ=õËÙ´ëæÙµîL
+K1?FÞ½+ùïÙ1ZI4æÍÝÝ7ÅüVسéÞ»EÅÌÍ5µ8Ú´ìà¿ñîÞÃ5AìE¶ðÚF·îàÏ7½IP*<CF¸0ìüýÕ9ܹöìEå
+¶íØSÁäÊ<MäVC±ää?ÚF¸óêËUõÌG¸19ÛÝ7ÅÆëØ´ìÚÃBضñâË5WòïÚ»-1ìÙµïÞÚÞMGåÔºü-ON0Å°-â-
+¶òÓWÌUÕÌFµçÞ×ZM½äê×±æÒWáäÙÃEÅìÛ¸õè+¹½E8ÃF¶ÏÃ0ÌEµí,67G¸ðà7H¸¶îâÃ5AÌÚ¹2Õûýò<N0
+Åüâèýýñ½19ÜÉXç8HºõæÃáíÖOÉ0ëC²+*HËسìÜÃMÅ8×¹õìíÄMÄQ9H»ôîÓ5¯òïâÏUõìÛº÷FܹõêÓEÅ
+ìGÌõYíùûÍ3Æ68OËâ+Åì»úõÇAÜáÅÍñÙQYH¹ðÞ·±êØ·ù´,JÞÃ5¶NõìëùÙïæõçáÅYݼûò÷ºöðÛ±õXH»
+LíÜ-9îøöë-ÖÕûùÞ<¹M1:æöýýïµ1íÛÝOõXI½*-ø¾*ýóÑ1YÛ·PìF¸òÀ*RG¸òà?*ÜÝ7żWEµðâ×Uå8Ò
+5çñîÜ·ñäëÙ¶ðFÛ¹õêÏEõWHéùüøU×ßÏVÐÝîæÙ¹ÛÄ7Ú·ôî/*æ¹ôèÃñ´W**±éÄëE´ëÖ/*úë¶òçÔ·é@ì
+Ú·òL8WÛ¸òäËHȵðÄ-ÖÚ³ü2ÝÕËS´¶òËüýõí¼ùöêûT²åÔ¿Eõ8HÅAYܺöêË5åسÙÔW+Jê;VùÙïæÓSá´
+7Ù´êÚ·¶ðäÓ±õÌG¹ìÌF¶îÞ»5µÌÉéÔêMê÷?1Èê7»¾QýýõYÝGºÅì²Ø²èÜËÁ1ñßÉQÍÜÎMXQÌE´èÔW5ÜJ
+êÔWùÚïæÜ·-åë6¾ÉMõ8Hºøôï5*îøîÛ¹19G¶ç:U÷øïÔÇ68»+WýýñIÝGºIX>Ú·óðóùQû÷ñõíI½ß*ÊYÜ
+¸ðÞ·ù@*¾Ø»-õµáÏÅ=1XÚ¶ðäÃñíä×¹õXH»úJÝRIèÕ9ÝJI/A@»üRÓN0Åà+Ú¿Mý8ÜöÎòòó-2/0.B:Þ*
+*2B**ýïÉåìÚÎ/ñ´ëE·½E8WÙ³éÔÃ+ö²çÒ³ñ0XF¶¯ÆÌÌG¹õîßÁÕÌÄ=Ù¼ýÈG<ÁMÕßïýýùá¹1íÃ5/µ8Ü
+ºøð;»øòÃ+öº÷ôëÑAíÛ¸ÜÌFµéÌ;Á@ëíÄMÞ@êÆØWá@úæÕWáÄ7Ù´ëÚ϶ñæÓFè¹²ëýÅÆL°¶òPÖüýIݹõ
+î7öÍëÞGÖ-ϵYί6¾êáµöRéÃéÔëD³íE8+ÙÎMçÄWîÏRÑ´ôæÝÃ5QìF¸óî缸ëùåÅK°¶òZ4ýý9ÝùõíW±
+ÀæØúT¶ðæßÑQYÚÏîÑAîB,Jñã¹Õ8Ú´½E8ÏF·ðäË5-B1¾ðàÃEµXÒÏÍEÅìG»ú,ì³ôõ¹U:CF4FÖ18Ûùõ
+ºëÓ±Å8ÛBQµõXH¼üü+.2*Ä+ö¾ý,06öíݺÞìíÄMGÕìF¸òèË,Â-*¸14C,ÞçÛÁAîíùî½OôÄ.Þ9ó+ÅÜÛù
+ô¹ûûéÅÍç¹Õ8ÛÆ.³AÍܼ*/ÄEø¾ýú+òµíܺîìíÄMçäW.*²5/îêØ»éä7E´ëJÙD*ÝÔWÙ¾ÃíA½?EF¸Lôý
+ù½ÝûùóÜÔH¼øðßUQ8¾5É2ÁîððóÙµÍHºöÖÛ¹ôâ¿ùÔëùÄMöÔî2Ø7FHZNî@0¾ÕWÉÆ÷»êË°ÁM·Ò½ß;ï@D
+YÚÏá±ÅXÚµìÖWñçÔÃÉÅíH»÷îÛ¹òâÏEÈúÚï2ÜÃ*B¶ëÜ·E-ZíÚ·ñ´0:.¾Ù¯ñ3úûá¿<¹MIJèýýûéµýìA
+UÂõÌG¸òæ;·íÚøø´îèãÉQ9ܹÜ8Û¶ìܳáÔ7¶ùÙïæéÓUÅXG¹óæ3¹ôà*à÷à+4*ÖÛµVÝáÅéP°¶ò6ÎûýÍ
+ýúóìϯÀùòÛ¹Z1+4XÍݽ*ûóáÅÍñÉÕ8G¸óâÓ-óü´F,¸+¹Q-:òåçÏñijºûVÙN0ÅÌàÝýýõÁÝìÛ=üõÌÛ·
+ðàÃñðäÓÁÅíI¾+ÛI½úôçÙµ9HCµXËàíÄMô0ìE¶ìÜ»U-*í8FJ/*¹ñôÓúûD¿N0żÁâýýó½åüÛ9Ü1ìÙ´ê
+Ô³ñîâÓÁµ9I¼øFܹôî×±Å8FçÔWÓàíÄM÷0ì*ÞÏ,.IHR*OQôºü0MN0Å<âæ¾MòÜûùÞö,8¯Ùµòìã5³.÷À
+7ÛÛ¸ôèÏ5å7Ù-ÙöÙï2Ú??,µAî2Ü7*ƶïP+²Û×òûÙU:CF¸>³ýýÍí»¹õ8ÕEµïà˱AÍèÙÅíܹõ,8OG·á
+2ÐËÙ÷/2õ·áÏÞ±õT*ÞÛ-*R1¾úêÐÍYW°µò*Ú>üýI¼ôìû¹Ûòîçé1îJIB:Þ½*ûóéQøï5¼²óì×¹Õ°Kð×ù
+ÙïÆéÓ±ÅXG¸ôÀ*¾ÛD*RÕ0ÞéÏUÕíWøëÁã0Åì0õýüïµýüÛá³µYݽ*ýûMüöë5±VÁ*ôÀ@0*Ä+ü´Fè¶ïà¿
+-1X>ÞS+Þ¼¶ãÝü>EFÈIñ-8GùõÛ¿ÜáÕ9IÚA9Hºõà5;F¶íÞÃ5ÓB.B0:îà»ì¶.ðL*NÚµÑ*ï*ãÆã*ô7IÛ
+òÑÁM9ÞRýýûéµ1YO½ÒQíܺ+*üÌÛ¸ïÞ·ùô7Ã5Qì6Þ.ÞòàíÄM>AìF·ð,ô,*0+ä÷è,FEÇÌ˱ýé89ö:=F
+<,Îü9FàöÎEìÁAä2éÓMÕìÚÞåçQ<Q8Ú=Ø:ðàûë¶òõòãÙõìHºù.H»?*D9ܺ÷òã51îòÈØ0GG½ñÆëé¾²8
+N-ÅüJÔýýõÉíHܽÜ2îݼøðß5½2Á2+¶î3±*Ï5ðÌÜå7ÅôXÛ¹ôP*ì+¾Õ±åXèèÌÁÁõïñùÝÜöéµS¾¿FîRD
+V5¶òïÙÅÕºZHºõRG¹ó,*L9ÞÛøÂR.Zµ¸ßèÛ¹õìíÄMNQXF·ñL*JÚ2¾±*îöÖÌ0WF¸òé»ú÷øý59ʲDæ<Á
+Gø3üüýÍíÛ¹öêöܶïÞ¿EAðîÞ;8?Þ6¾Î=õµáÏÕ=ÕÌÚ·ñèÇMðäÏ5,B521Îñæ˹ÅÓò¾MYìºøôíåÕåÍï-
+µÊ?E5+XáÏîÆ·îæؽQVYú:Ê5»*òÀ3á9;Û·ñâÓ51R-2BûÙï2äËôÃ+ÊøG¯¾;+µAðPØàË=YìÏMå¼ûùöò
+éùüùÁùë¸è/+QïRÆÂ7¶âëݹ9VÌØ´Õ<ïÞ+ä<Y.ÞíÄMêÅYݼû86Þ>Þ++¶¼úø÷=ÆYKÏÌ··ñãÍUíÜÛ¹åü
+Ûºù÷ûñÅÖ-ÙVÓßKFøAÏýýÅÍÛ¹õP¸ãºù08P*ì>àÊL7¾îáõµáÏÖ¹A,*êÃ,6*ιôèÃ,Ö¸õìÓ5-îòæÏ52
+ÐRçåûøòêÑQ½üû¹IIHüùüãí0VÌ´¸0ÅüPøýýíUýÜÛÚÔ÷¾8ÂÛ¸ï*O+å0JèÛùÙï2íǹµÌÛ=+îòèϹµYÞ
+»óà+JÛJ*Iµ8Û·,UHÓëÙUYìÚ¸õêß¹÷ôééõýIüêäõPB1ÌÞÏõ×ý½½ùìݱQÕÃØE²B´¶<*µXßìǹQìíÄá
+×àRÜCMµÅ8Û¸ôæßÁRѼOÇYG¸/*ÜÌÚ¶óôWÒ3ÓÝH8G¹õèß½õôçéQ½½ýýñW²Þ11G¾MýñýýüóÙÅåì¸õ»S
+í¿CÚR¾RåT/BµDTæÏùÙïÆí˹ÅÌÛ·ô86¾Ùéæ³ÞWõàüÄÊýèÚAX><Ò5*îôè×¹ÅÌÞÅÍû¸ôêÕ±1Yüº·½¼ê
+;ýõÝ»ôÝ8×F¾MÝ9Hùöî³ÑNÄèÃ-µÞ*èÃ>@EθôêGÆ/¹ÅìÛ·½E8ÓI½ûú÷ñµ0Þû+²Õîìè¾ÛøÜß;õ˺ö²
+Öò:ݺÑ,M-¼9I½/19I¼GQý8»úõòÝÅGøóúýõ1ݸY¶ÌLÒýüõÍíXÜÅÝWÎÒ¸óÞÛ¹Ð3îíÇTF·¾Ý7ÅNíÛº=
+*8ÍH¼ôìÛ5·Òѳ7ØöóÊ;ùó·9UPÛ´OÇúòFºöðãÁA2ÞÑÓ@½FµóêÛùõïáÅ1ýü»ÝÌE·øüýñÅHØõôÝÝüöí
+ãÑåÔû³¸PÎJ·AÚZ.ZT¯°¯¼KòÛùÙïæéßE1YÛ¹óê·µöò¿FÆïTæ8÷ìâů5½ÛØéÛUöëÜ·AäÁùÜËUåX.¾á
+ÚÜݺïÔ¹-õìáåí¼ü¹û¼GµÛÝ=ÍÍüݽÝIݽûîÝÍ1íIÕÉòUJÀíè+¹Á/10ÞíϹÅXÛ·õèǹñàBü´F¸ºôìÓ
+ÁAÍÛ¹8ZLÁ2OøVêÖáëʶîãÃYÙôعE@F÷N¶÷VLÍ/ñ/G¸¶Ò÷ýíYHõà¼ö¯íçíå½ýÜÁýÌØôÞ×MõõõÉéÝ9
+½¹öðûùàå6OéÖ=KVõÌG¹ôì6J²JÚï6ÞñÄMY1YÛ¹íò7òä½;²·ÑåáÏý¸áÆû´ôÉ·ðßÌDñéÞÇÍÃAËÙÁÕYá
+»öð/òáýýýͽüöÓGZÖ¸+-ÝÜݼåíÜ»òà±õIíÃÙåÍÜùõñÙùàüú+ËÈòß·³ÄA,*èG3*ÀÕ8ÜÝ7ÅXZÞüõ³
+ÀQÌ5ò29KÖÊ3YôË÷¸XºöðÞÎÛ°áÈ+ÙäûDYµCÎF¶ó6àOWæù9·òýûåQYùëÐJûêÞãïùõ½½»Q9ݺíöóÕíì
+ᎼCéÒUÏøÏ4÷3>2:J¿HZÞ¾,-86RîÇ*J¾++øýý±áÏÙùÆòñâN-Äñëò×ÑÖ=RÒ½GêâÅ9Á´Ú×±9Ú÷ZåË
+9é¼ëÅåôù<ÞʯIùîÒçÍí9HôðÌݹﵯøU¶Ï1ãUº»ýüóYõâíñIíGú÷àû¯ÒPË@ëBìFÀ1íÛ5+JöðÛ¹;*Þ
+X.ÞíÄMÕõ7I˽U5åêÓ<â´WN×Ò¯¹Þ¶éPĺC°ÞüD±áÌ3Ñä6ØÙü6Û¶ëç3±ô¸;ÑXG¼õõøûü÷ïå1ÅÈ8DY0
+ÌGʸÍñµíÏñÕÝHù÷òÅùÞNÁ<ë¶8Gº:í**Ú¹¯*1@Jîç5+öÙïæç?ùÔXáËT?ùêã9=/D@VâÜصèÇõ0Ìé×¹
+¼FEòèÍ;ÑôÈUÙäʺóîÐûZéÉ7µÄÊø¶õWF½ýûë±Qéó¯±1ÍÝÂS7¹ñ÷ûñÅý¼»îHBçÏ7çϹ1í¿*¶¹õêß¹A
+ÍGºÚXH¹÷êßUA,*ú½ë¶òøêãMÕ7Gº>ÛS×ͽQõ»ÖSQèÓ°ëã¯M8êûÜ´Ûø²àÔE¹âÖA-YËØY¾ÜîYçÙI-¼Ë
+ÒýéS?õõýýáõëç0òÕÎMÂ;»QÛÙáÉÍíüµÂ4íÚ¸÷ì-¾æÁµ,îµáÏ1VB:K¾+ñÏñø.èVT´ñíçG´ÞUÍÌ7Gû¸
+íËõVæÓµÑäêËõäûÖWêÕ;¹Ø6=<Ã7׻ϺXÙ½,MÀÓHòæË1ÓÆïäÌJY¸ËFɸÛé?⺲ZJ¿A*::¾,0FB2*10
+6B:ßB*TBZÞÝ7ÅZY+¾éÑõXض÷HäòSñÐ88û´ÞRÛTÞ¶C´>3ÒZ17øUÔX=PÂèÓPHìõJÃô0¸Ç³á8ÃÆ9Ù²8
+ò½üýùÉQVÐEºç<PÅ1ý+²ïÐËÑõJ<X?*ãÑQYÎÞÃ5úë¶NøòßU+î÷ðßUAËÙ¶*ûæÖ¿YIÍHE°ÕçËF»9U¸Væ
+ïOê×ÐàÁ,ñ÷ãÄ1ÔNçïݾü¯SÞÖÉüËíÝáÍýýýùÕµÄñåÀ?¹öÎJÀ+ûܵîÞ¿ñäëEý@<ðôÐC,Ò2õãÉQíÛB*
+VA9H»ôàA¼µFÌ*¾»Ó*HYH¸çîÇ.ØÒÉ?Êù¹ó¿±ôñòî?ÂSîÛ¾?GP·øîȵÌ;H1A°áÏ.78NèÝÄüìI½ýûãù
+ýúçåܸ8?³XÊÖ²äÂ3-ÄËáMQìEµ6É0ãÑíåÍ9ÝR¼1²ïL+¼¶F<»øîGZ*ÍåÌÚ³í4@ÛèïÀÔHüõÜAù·¼,QÀ
+Ú@9åç¼¹WäðͽËRÎåFTA¹Õ=Û;òAEYõ9½ýúýÛ½üð3ÉéÑÇG¿AYÖñæÑéÙÅ9âÎGKìÂáõáí4îÇT2»ôHµFL
+Â.;<º¶,*4H2*µÆZK¾õæßÑæü/úø¹øÛ÷øÅù@Pº¶=RżËÛ×ñùñIºÕ1YWEµöÑã°òî<°Ë»ýüùòÛ÷ç½½Ä
+UAÍKRB,Î*Z::0J;¸F7ýºË?5¯*,;ßÝ7ÅJY,*îñØ»Ù2³WàÛû¹öÔ½ÉóAXIÅÆïLÚìͱüìOѼ6GòôÚ¿ùÓ
+Bá÷TµÜ¼íÆöYÒôâOJ2RðPòûOÜç¾Ý7ÅÌÍH½øøçéµ9ïáõY7*ò5+îùø÷áÅíF´îºÚÅPéD¼Wº¹åû´Lº÷DT
+?ëIµÄW¸¶çØ?ùÜCKäúµìMêì¹ñÜZëÓÉÕÍîñAò*ö/E6GÂ+*¼;*îY¾¾õ=ìÝâ¾ùöç-*áBü´F<»úò/*ÊóG
+R¾¸¾5*îóØ·5õïèÛÆ»öõóSY/6¹VIVF»âÑ5QPç9DÁäXRëöYáß¹ù47õÂ6Ä;Ì8*86Ú¼±À1ÀïµáÏ-ÚåíH
+¾ùøë=ýöË*æ¼úúïéÕY7Þ2¾ùñ1ZG·5ËXó¶=EIYÝûí´Gëß¿9ÍâÕ;IÅÕ;ööØóôG5ÙTFõ8ݾÁ5°:úöÃDÚ
+C*ýÅ9Ý»ýX°õ9FÄX2¾ùü´Fø¿11DNæîÞÀ¾Z>J6ÞF*>º**Ì:KÁ07PÒBÏéEQYHÍSëøóÓÑÝÝHÛZËäºFµJ
+±¹ýÊBXÚÉòM¼,9ÇK7Þ7.ÚZ+Qæ:Qõü¯8²R0HúÙï2+ð-ÖíÝ3*Üõ9ݽûúï5+R*:ûø?-ö¾+/8FR:Ûµ¼XG
+ÎQéüÃ7Ûå½¼³âæçWÕãµá°û´Ìݾ1ÞJ÷ûñÕ9I¼+0Ã0ðÍÝ:+çõíÞ¾íéÅíÝÒñýýMø*ðùÙïRõïÙ1îH¼=*Ò
+YݻϽ¯*Ä9Ý»ûL,Ûݼùø+.RZJAö9I¼*ýÏ5åé»F¸²VÜáÙùîÂéXOÉÑN¶¶ìI¿ý,0BÌA.+¾»±2Q6;@:°*
+ô//î»/=áYìîÜå7Å2ZI¾ûüG3*îA4¾ú÷ñµ0Î.Þý+>Æ:K¾ýò÷ºú*8F¶ÎÛ·ü8áÎHç´ÃÈÒåEÕÌÞ¿*38ò-
++8>B:J¿ý,8ÆI½ûÌÒKúKOàÃ*æÀÏõ9.ÖíYÄM%%
+d
+569 104[1 0 0 1 0 0]sl 8 mask 0 104 di
+/mask 7488 string uc
+*ÞÀ**ü»ýÅöüÝôûýïùýáõýÅíý9ÝýI¼ý½úüýöûýïùýáõýÅíý9ÝýI¼ÓY-%%
+d
+/sl 59176 string uc
+äÕí´ê9+¾Ñ,ÞYJ¼ûú+>üúÃ+ƼýÌ6*üñõíݽ¯+HîJ¿-/4òQÍÝɵYI½++<ò,áÇÉÅìRºôÎÝÀ-30F-î.-
+86RZJ¿*ÇJ¾+17ÎI¼ýüó502Ï21:*ù/DÂ/Zèú7ø5æýýUôö¸?ÞØ÷óéèSF¸Â0;HÂæ:LÂðÎß2¾E*B92Aî
+5C°âÖÏàÁ,ßÞöKI¶îKÂ6A°Ú2ïßñ¶ïá4*ïÆï.¾²âBTJCXÒÆïLÂ+1Õ0ïÞ=à@î¯ûM=DúÙïRù+ê1ÎݽQ-
+Þý÷ñÕÍI½ý0,À+*RZݺ÷îÃ*¶»÷òçùÕYÞ¾ÀîÞ2¾+*:Ö4:.Þ18>BZÞ¾,-T¾*+ôáåYæß¿LμûüÓ.ÞÜòý
+ü-öµáÏ.êA,*úÃ,2½*Q*ÞÝ-/Zýü+.Bä1,Ì4D¾ýôçE³>÷ô/Jؼú,,F¶N05DF-*/5*8Ð7NRZÞJQö
+õÞ<¾Ý¶áç/ÚUIY¿ûü´Fà¼*ù+òµè2,ÄõÃAÄ+ÂD8,¶½ý*4FÖ:ß¾BZI»÷îÃ*2»÷,8·Ü»øôçùÕYâ¯F¯êï
+Êï¿+ÞÀ-146B.J,,Vñº?*Ö³¼Õý*üë¶Â-ý7>¶Z6*4.RZJ¿10J.0V-î+/8²öîßÁ0»Þ½úöëáµÍÝT*æÕ,
+J*ø5Ð>/5Ä8Ú;*À2Ïß=**/á:M:ÂÞ½¯3;*¸îX÷01üùÙïR8LV+2÷5ù¹:*-+M,8¯LÃ7E´âBÏ7>BîÞ¹+Z
+.-@6Bêæ;°ÚÖ;MÅ6GøÄ8C´êæ¯MÄÊïàÂ17@NBÜÇ<PÒB,:4;ìÇ;¼NýÖàÁ2ý´Fо,-8*J¾+/,.öà+ÞÝ-
+5î*+4NÖîKÁ.GJ½øòëáQíHÆA0ÞõïɵYܼùô¿»±*îYºK2¿EÂæZKÁ15<ò+ûçUåWD¯ØÚ?WæîûEÆ:KM¶Z
+Þ½¿2@;Yó3M4ü´F4½,9.*ú5ã¹²**î*¾1>ÖÎKÁ030Núôç5¸V-B+¶ñ*ZY>¾ù-2îJÀ/3<ò*ïË=µ8Ü»õú
+E²ÝXÏ<ãèB¿QZâÏ1N¶ÎJÀÀÅI/+áAü´Fؾ,/4ºZ*=*-*14Z0Þ1H²öîßÀ*û÷»úôëÙÕíH¼Zí¿+F3*ÞµD
+¾ø÷ùAÀç÷ËEÕXÛ¶ëæ·»ôÚ?áã4?SìSÕº.9@ºÖ:@>ö?øÈ?5ÍE8GßÀ.-<NÆîÞ4*O¶îÞ¿,1H¶*I¶ZßÁ2=
+PºR0ø5**ûX6*î¹=*±+ßÄÑ,üíH¸ø0@VöÍÍá0ÌÚµæºÛðÒN»ÐÒ4GÀ1ûßÁ,±Lµ¹ïK=ÖZÞÀ½E83MÂ5M*Ú
+LÂ5?TÊR;LÏBïÏ*öÂ4C´òöïMÅ0¯ßÀ/M@í3*26+¶î*¯ÊHZIºú<ÄF·Ðµ.R8D³ñÚ3ñØTË4³SÎQôúLÅ4I
+Tõê¯MâÆÏLÄ4ý´FØ¿,+4º±*FZJ¿+-0>¶8:0Þ1<VæîKÂ0-¸¼ûöïáµíÜ»ÀYÝR¾×¾ø¾2¾÷*ݵYG³çäG>
+ç¯ôâÇ°à»óö÷¼ïÈï°³T>PXæÌNï.°>ÛEîFõN05<úÛï220DÄ+F*¾¹1î.9LÂ2ïK¾ûîܼûúï5-îùúïéµí
+I¼ý86¾õéÅÍݼûôãñçºGÁµ¯ãÌUGèÊ7;¸òRîFµÔé?RÌ</àáóÂèAöóÌ59TÂ1=@ÂÖîíÄM2Bê2*0GȾ.á
+*æÞ¿+å-L*À+,8Kß¾MYBZI¼úøïÙ1öGB*àÅÍÝD¾=9îùæCÁ@WGÂ<ûPÑNYXâæ<NAÖÌ×XÑ<GTâýòϱÅIÍ
+@=ÄJ*ÁÏÍ*ïíÄM°æîß4¾ñ*î2?Xê¶;L¿++Þ4*öA,¾ú3úõT*ä.ºI¹ø0ØEÕÎNYC±PÈ23Ä.ç-ȹÔé=NÂ,
+/FøFÉú³âÖÏàĶ¯MÃ8?üë¶ò2APÚRÏLÃ5M*,*JàK¾È¾÷*Ýö;NÆ9ALV238FÆ:ß:*J¶0*.ÜD¾ö*9öÌIÂ
+2ñïÉæKÄ6ÇÏI¼6EL½îÈë,?îæûîÏ-19»8KÜ**ööµá¯îí.Þ*JFJ*ÞJÞBJ¶;B6Ñò250úÕÍI½üJÝR*êõí
+I½úà+.I½±+ï+ï,HíEº.ELÂQÍ5Ê2ðàº÷>HòøÚ7É/4ÐK¿ÚÍG·ìÖÏä:à--?5;¼ì¶V³º¼*PZKÂ258Oݼ
+±ÄàíÝBMûÅYݼýöÃHÔ/Â1ö¼÷ÎÃÑÕîL¾ü7FÀF±,µÎõE1ëÕTÇ27ð¿øæ¿á07YýúßÁ4;XÂÆ;LÖöµá;F6
+L²03<Vöò*ºKÀ-7LʶïLÂÖ:J½ý0*à8-9ãݼÑ+¯*Ï+?*üÌÖ³òøGÒ2ÍQ=ÆñOÂýöÏñêÂçL³ÇÏM¿Ú8G¶è
+ÚdzÍJSæZ:,WÚÆïñÄMÀ2;àE-B.R,*3á,úKÃ7C¸âÆ;LWRÎÞ¾--Ä+Ò-ο*1Ì,Fæ¾B*͵UC´òô?òåÑÇÁ
+öîÞ¼òØ÷°ÛVË4Ó3<JF1ÌE³ú2ñAúS9PÒÖÏáƽE87áÃ7AÜC*ÏƯ+¾VÚ¶2ÞC¼6C°NÆ8AÈÁ/5*Ä+XRÞË/
+Â+öº×¼;ÑP8H»ü7C²ìâÇáäÊýÀÃèÒRÇ,ïï¸ìÖ¿-Çؾ0ßß2*áö;ãȽE8çKÀ0U*XÆÎ>²öN+FKÁ2?TÚRï
+KEBîI¾=*2îI½*ý7-FJ¾=-îôTãèOê×´îºÙXÎPãð/ÊAÍÓè>SÊ.óÏÁñÚ¯MR>³ùT3ÊÞÂ6Iüë¶â27HNöÎ
+KÁKÃàZ߶*ZÖ@:H:8Þ;°Ú¶;MÂ-+°¾++,.BZÞ-¼F¶V92320î*ëÏL/é@YàÚÖVÎF·èâ3?ÅÓèÒRÍ:+à=ì
+RóRTÓWݾ+ÅBÎJÂ6Cüë¶ò17HNöîßÁ/U*ÞßJáçá+N½öZLÄ5C°Â2.HZà3¾5À>*/ÜØ*½µè>TÒVã°ÃF¯Ø
+>*ÞIËDOÈ=OÁø¶G¶èÎç-èÔ·³ÊRZßÂ6Cü붲5?°Òöê*ÂàÃ/*J¯J¾µúö;âÅ57@Ò/3@²Æ:ßR*Kæî>JK*
+?FÆZ+¾AÒåÈÒSÑT×ðÒDSÐ>S<OÎÚÒRÍ@?èñä7´Á0ËIÒÊëU´:2AÄNºì¶>5EÄ=Ä+*ĵ0*B0*Â3¶Ä8MÄ>
+3<MÂîZ*¾L²öÎßÁ1Á*êßÁ07Ä/Ò1Ò3ö½ÞF¿LÓ7ä¾8è<NÆ:Oð.ȵÐÒRÍD±èÖϳæ2èÓWÞ<,ÊÖ;NÆ<ý´F
+4Â1Í.Jöî¿+*Á18Þ;TâÖ¯MÄ1-о,/0+B¿*/464J+/,6625R=2-î*ÙWÐ/<ZóVÛÓPÇ6CÀÂç<Ų2ÍD°æ
+ÞïMóοò6Û½O/¶ZLÄ6Eüë¶>1=Ä;ÀÂ;*Õø2:Ä.¶Â2=°êæÏMÄîZúZ3ÞJïÆï+BHæ¾C*ÝÅXéç·ÏäMãEOT
+ÒÆÏMÂ*·G²äηÑå;MPÅÒ´ùèF³JÀ3E´êöµá¯*¯:¯2ÞV¯ÃïÃ12Â49Þ;66Fºº*:îË-¶Êȹå6ãç<MÜæ:K
+¿üî¿ñÄÑKáÄÌIÄLã°ÓÈ×-B¾:Ê*9°òÖÏáÝ7ÅØÏMÄ8C´V+B1J6C´F.2/î7GÄF·ðâÆ6¯àÁ19LºZ*ä:àÁ
+3M*.LÁ¯*òZLÁ¿,XZáÜ͵ýÊÓ0Õ¿-å7F¹öÂD¸D´òöCNÇ>X@ëE¼/-7Ä6ZMÇ;OÈúÙï2F0F0*¾ÄÁ*K*ïÃ
+¯*¾¯ÊÞ+FèÇ7?LÂ2ï¶ïÃ+Ê/.FÐÂ2=Ä,öÅZA±ÅVòÞ°HæÊC±âʯ-ÅÙSñP8H¿:·ÐÒ¶GùôXß=´2¶>=Qüë
+¶6+N5=T6+*2M+>àÂ5=PÒöà,úàÄ8I¼êö:KH¶îÞÀ-346+B/¶:+±+¼ÎáÔÇÑÝ05Ð8FÖ9CK´,*FÌF·òò3
+â2=9/5B³îêûE200E.GÆÀ4á;Þáå7Å;Â*¯Þ;J6¯F-R¶6.2ÁF¶²59<NBÆ22HÎà5-R¼ò-KàWÜØëÊ/Á1ú
+Þ¼äD+èÑÅ9ý=2ÏMÉJ×è/˯-õ8Þ:À=¶N9K¸ê@¶âÉÃàñ²å8G7.*ºåÌGÌî3ÛÚ×îR>XV+¶**Z¯Òï6Þ+0Â
+EÈÅ6-8K߶àCÞ3*ZB4*60F¸Á+7ÄXì·ä¾¿Æ=Å8îF¯Ñ@SàÒÇÁDã4A°éàÓ±ù,8NÖêB¶Î³î2²ÄË/+²<F8
++*Ë»/Åð;â3¾Bà·*2CÒBÆQJÀ61î9MÐVÇ<ãÆ7¿à¾NÆÖ0*<ܶ¾Ï*ÃB¯°ù·¯Ô1F:+YRÍF´çÎSéPÝÏÑåY
+ÞÀµÒZ0JJÔ6Çæ=¶.ðXæVMßÏê;ÕÕîBäB,îJ+46ãMÅß*ìÏMÆ<SÐFSÐÁâR¯LÂ-/4²-/LÂBÄ2:Ì,*ÂßÀ¸
+Ðñù°7@ºî+=*±ÇÌZJ¾+/<Væ:GÂ2¯6+J6°º¾-:°ÙÞ;Î6>±æàÜÔÝÉ*¾XÅÃ@Æ,Rô>FØÂ5A°6-:5?Ì+2Ä
+69FJ2*Sê2°*¾¹ÚöZßÀ+AÜòFµØV·ÎI¿/;KÀ.5@6,Þ,QÝÝÈîJÀ1Hî¶*,0P3P4ºßÀ0CÄ.Cðá8¿M¾FNÄ
+WÀQÝÜýùßE2*BõíÚ½1åì°B/>ìR¶Ò4EXâR;áB¾E¶.6M,À*ÚMÆ:MÄú¶ïKµÖZRÞÈ5-Äéé@û3±J¾.5¸Á/
+7DÊÆ°üØ:î/<Ì1Â,FH¾R¾4*¶ÖWîæ³øï±WöîIÌE.ü=ÒíI+îâËÒX<¹´íìÆ9IDE483áÄ3Á5ëàÄ6AHÏMÖ
+öÞ*ÚáÆ;OÄòR;Lµ¶ZWíàÁíXëAI8ºµSÏ++,¾K¿ô°´ù3É6SKÁ113å+L+ä*Ä*Ä=9Öï>*IêB<âÆ:ûµ2Fø
+³Ã4ûG¹Èé>H½ù7.*ÜÜ÷èÍÓâ¼G@?T²WMß;Æ6Ó±<MÌ>C8*N0?4Ç<Á-¾â·*I÷ÐãÉBUÄòæEXêUº¸õë×¹
+òâ·ÁÔÊöéÑÚòÞÀ3»èQE¸çD°âÆï6Þ2Þ×/.º@2¾Ãñ*̯MÆ?¯Ø²×°ÅÞÏÁèÒ3ïéÒCÁPëñííK*îð»YÜÊø³
+àËN=R7/Å6ðáÅ:M¼.C,:**JÔ,Ê+¶Å7MÔN×°OÇÜïLÃ;/=ÝÜÜéÕíÜ»÷ìÍ-õÔE9@ÙÓçĵ»ÞÂ1ñ;ÊRïLÉ
+BÏLÃÇ6*V6-Î7CÈN×ðâ@¿MYÓTÓÛÖEåßVÐÑíKµòöå1ÍFQ±ê¹-TD48?áÄ6E¸ê¶4JB°F/*7YV¾Yú¶ðâÇ
+<ITò1;ÔÂ6ÜòºMG¹ûú÷ùÕIݺåXFôæÅíÈOè+8ºÕÍKÂ1;<Á19Ä,Ò+ÆÁ0Á+¾ßK¿-+RH:2AÄNBÆ=Oøí:¶
+òݼûØTøïÝÅS¶öøí*¾à7â¿*/F8»ÞÇ-*ìÕ@J+**Z*ÅÒÅÅî@.*¾E.¶´,µ>;:î<1X´¾Å1ýÙïÝÅOY7RçR
+¶FR.8Á5Y4¾±âæÏNÇ<M¸ò39H²E¹û×QúÇQô,Åíýý½õÝÝü÷ìËå8ë7Ñ7ùóP¿ýøË@ÄB0,Ò+Fî¾±,î0?´.
+CÐâÆ=¹.8»Ø²ç´ÀHÊ2¯ØËGû=*ÞYS2»Hü¶äM+*ü74Ê+**1A¾ÎíÚïÄ-8LßWDFÉÁÁȶE-QIÞÙ±ÝËBÏßÎ
+µ0Ò²>F,ÅßÄ10*HÄAÀÅ1DJG¼êÆïáÇ+*üÏàÁý,IÅ9Ó9+*Þ-EÍ24ô÷ûýýý½ýûð»ºòØWÁĺôSãQÎݽ1;
+H4¾E2¶ñ*@ïàÅ:-<¾â8¿MYÕXÛÄÌ-á?Ê°I1ý.*Â7ÏA:Þ+ÞáθÍ,Jæ¸öîßRÎ*9º*,*¾58BE2-:å7Úö¾
+¾,1Í7À-îß-AãAVJ?Fº*1EÎVîßAD>>Ê@**±,¸RQöîAËGÇ**ìÊÀ¾1îX7Ì0¾Q½HÙZÚ¿Gµ6RçR¶²<SÌN
+C0*PÔ+¾Ç¿*Q,JVà6*î?K´òÕÕ·úAKÅ×90îÀ7TøLõ9¸òýýñQYܸëÕGñÚ±Í4²2ÏàEûºâε>¾Oк3ÑäÉ
+B¹.8KÞ**ôFöÏÞÔÁU½,*,+ö52î*â?Iò9·ÒOÚ¸óËÉEÌJÛ¸+Jû/1Ê7JóèU9-òà->*·ãÓIîFTÞI;áGÙÕ
+Rè+Þ·Ï5RÕü.78²+*B=Î<;SÅGNøî¾*¿EH**ìÓßç*ÎïßW1Í*åñ>7/Å°<â3*53ÐâÆ1-¾PвC*ÞQ¸âÖ:
+îóø9À5/Å8**+<ôº43ºçÏùåÍüúóÞ¯¹ô»ñPWÆZJÀ6çàÃ6Y7J/¾ZÒB,¾KÔ²çðOÉ@¹.8GMÄ6ë·0ê@ZLë
+ùö=*:2°Ý¾*-Zú¶.¯FD*öîNé?56:*ÄIî¾Ø+.*ÞÁOËYºJê¾91Ò4òïá=ÖäáÅÍÔZF*ïK,µæîJ<*¾C+²Ë
+¿ó¿êÄ-Ì÷**¶Ô0µ+Þ»ùP7ÖWÞÌë/Ò²>F<Å7G0*.,F¶*æöâ+úMÆ<SÐ>ç¯LÌ2ïZôøÝ·¶+ű*ÆÁ9TAYÝÖ
+M½½üøìÖCýÃéÁø-¶:àÂ29DÂ3;L612*î1;LÒöZàÁ3ÛKÄ:QtFÇðâ8¿MÍC±P³Ì+MøÉPíüû.*NÊÇA:*+ú
+ü»ßè¿*ÉÂëWä¿+,Þõ-8¶¯-;*AHZBÞµíñðùµ/ѽÑíÏ?682*ÝíÎß+:F:*M²ïÉܱ+*ÞÏSÎí*>ÚTÁÙ*îÚ
+µÍü53Zåè,TD48GâÄ;EÀòB°MöB0JK¼.çïMÅÏ*üïMÅ:MÐV×ÐÍúRÏLÄÃãõA×6ÚÀM1*Þ*9¸¶Ç.Ýòæûñµ
+Íü¸îØI±ÞP3,NöZà5**496*JF5Z6Oв×SîæÑPËøɱ޴û<çÖµI+*Õ÷KLäÕ3ñÚëµ-Ö»ù=E¾¯¹ãÙ9ÆWë
+?*å²E+ä1FZ*:´:,îà-0-Ú¯Þ2´íÔâé±ðÊÉÐß++Zù/D>>>2Ê,ÎÅMÜ>12,ÊíÊ,ë0JçÓDÚ/*Í7ٲݴ+¹
+ü?ÎÐÏî2FÜ-¾2á=3î;QÔNSÐáÂ3³MáóûY4î88Á*ò+8¾V»7½ôæýùåýüúòÝM¹Þ½ñøNRZKÂßÃ8¯àÁ2;Pº
+BÏMÒæZàÁ9QÌN>SÈê<¶ò¶ïà³/øZM½/²êÅ*Þ·ÔϾJ/IÖ¯¹*Ö¯YìNK¿78ùG19Xë¯-ÙÆÐî¾-0*à0L*Xº
+ÞZ¾Õ¶J7´êÆ7KJ4>OHF:*ZÝJÁÀÀ,¿S*ø6KÝð*;OßËS2ê*ÌÓßÏ,Ú3*77ÖÏßÎU/Ò²>FDÉAWÔ6+*Aé+æ
+OÉ@U,P+ºPËEµì²C°N²Ç×û÷ÁÌÜï6¾@RL2EÕì9êÏ齽ܸîÖG5ôM?°ò2°âÆ:ÇâÆ9á*FâÆ:K¼V·ñPæC>
+ÏMîæ9I¼VSóâ½-IÁáF>*XQÅ.38ß7ON³4HE´çÁýPXWSJOMßÏÁF3°NÆ<KÌ.;9F¾À6S<âÆ;á*âNÈ+ÆÌ<
+âÄ6Q´ÄYFî·8Å8**+8ÆöS´@ò½Zżý»÷ã½åXWC5Ó¶;áÃ6CÄ,.-æÃ6=ÀVC±Æ>âê;¶òÒQÍÔÒCòFÁã±é÷
+Üí/öâ¹05Þ,ÕW¹óâµJ¹ÔÂPÍPÛÏîRL¼V+2³61*:í*¾ÅF×°OÉ?Q¼â6Aà.7ý±D3Þ¯2*1Þ¾ÂζÉïAGøü÷
+ã½IYGVÃLïà,¾¯*Õ¶ÏLÅ?SØFSVØê;¶òâÅ9Å@@óåÆOñçÙOÞòåÞ»ýÌû÷±è¯ÏÏïáǵ>FÄB<Ç:MÄ*¾ÅáÃ
+;*8ðMÆ8IÀ6SÐѲ×<OÇ8?<Â?Qå1êO8¶6,Þ,HN·ÒÍîúKżýºõîDZËRï.*R6/:5AÄ+:Æ>WÄ*¾ÉG,ÅHÜ
+»ù»ASÔ°Ø»+W.õü3*ýW,·;ØG±èÉêÙ9ÝÏîRN¼6µ.9M6å*8.JîÏÁ6·°ãÉAWÌò9CøÁ¹ÛüÑNпá:*NJL3
+S³-8×úôîÁUã¶ï×*ÆÄ9á;ÆOÊ>¹.8GºýüûÉFÒ²°Së¸1+*ùÔH¼²ÔÃN0Ñ*ö7/Å,ñã±+¶.*¼=QÌFµè²SÐ
+D>FHýÆE°Àá:*ý*îýýõÝY1ͽU7·ÐNÇ;í.¾É²SÑQËHµìê;¶>êë1ÇSßíøRÏ<O-+¾UÏLO͵ÀûùAüëµÈE
+ÜÅMµ3ÐãÇ?MÌ6SUÄV·<OÇ10ÞOÈ>ÇðãÊC±øÉ>M¸²æBûüLÍÂâîæ¹Òúý½üöíáUêKC¸êÖ¯2¾µòÖÏMÄ8G¼
+â<YàÒ÷ÐäÈW,ÅÀGüJ×ì<*¾ÝUìöH8/NÇ;*:°>J7Þ+*JÆ:M̲C*ÞSÈòÆÏKßóû5±4<ÁMÝ<óýýóÙµõìIõ
+ÚÆ;MÄ4A,Äß*Á*ÔïNÉAM*ÆOýýÙN¿¾ûô¸MZÝWìöH8GOÇ>IÌ>·<NB×0JNĺ¯*¼ÐOÊBYÜF÷;µÚR·üýÔ0
+N0ÅüÅïýýïÅA9H»ÔC¯Þ¯2*³ê¶¯.¾µ6·<äÉCWäNAûýí=0°îéáåÝËææöáY9¼ÝÚï»F4Æ>M*êNÆ<KÄETÆ
+<MÄ6Sð.¾ÉNçÐãÉBUÄâ7C¸ºêH¼OÇÛÏ-Þ²ùýýñ×ÁµìõDVö*8Á8ûLÄ5E¼FçðO»÷ÐãÉüýµÊ/ûE7·8ÅX±
+äÌCµìâC=ëÒÇñ.¾âÒC4:@J¸ü6*îG±ØV3ððúïßÏêîæ»ÓýýýûõîÛ¹Ü=SÐV×°OÈ=*è<ãÈ@¯ðFÇ:J½üë¶
+N:Uĺ:*ÀÐâ=+î=OÐF×<äÊC»PÉ;I¸F·÷»ßÛQ´¶òâúýý½HùõèÓëÄ8E¸ò¶2Î0*KÀº÷Ðä2*¾÷µáÏÐVÇ<
+OÈ=Í6*Ë>Ç°ºJR*ÖF¶òAM¸Òæ<³ûËßËêîæÉûýݽûõñÑM×7K¼êB**FÄHÀÅÏ*¼ðãËBµÜÊ÷ðíÄMA×ðMÈ<
+SÀN3NÌ*ÆÇ;M7¯NÇ<QÌ6·òB±Ø.ׯIÜéS¼ØV<¹MC:WÞ;ÄÝü»¶ÕÑÄ10*FÄ*ÎÆ;WÄD<Ë@³üë¶>>UÜ2*J
+C4*Rܶ*YÇðOÊDµì²çO¼úAÖûüRËN0żßÛýý÷Õ1½Üý¼¸ÏMÅ:I¼Â8M¸úÖ¯6¾½²÷ðPÊE³ì.Aý´FàËEµô
+ÊC0¾µèÚÇñPËD96¾íâDZåÍKÁôòAW@NÊüüÄD<ÁMÏï¯ÞÏÅíüÛõÓ=WØÂ@YÔV×ð¿+öÉD½,7øñæÍîñíÄáî
+Ûñ,îB±èÚ·±PÇ9óÞÞõýýM;CF¸?RýýÅ9ü¹÷8ºèÄ;KÄúBп>÷ÏâC*9ç<PÌD·àâ3Zü붲=UÌN÷**¾â--
+*=å*úâÉD±äÒç<NôöίûýÙ¯Æ68ãÌà+ÅÜÛ¹õÖ½Lòæ;¸úæïáÄ1,ÞSÐÚ÷<QÉFY,Ë»,Åô»»úø¹4*2üøíE
+58ããÈ1,*TÄ,.**É1*ÞYäÒ·=åËAS°Æ9;ÝýYê¿âîæ5Ëüýý»öîßùÚ*9È.C°NÇü¯âÅ9KÈ63ÐÇÚS=å+*¶
+×OîæÛÁQÝÜýùíÍ5ðßE2*ÚµìÝ·ý8ÌÛùÜÀMF×°OÈ>å*P*XVJöÏÛÊSÑä¶M/ǯ°ûôGOôÄ8JÒñýýáݼ۳Y
+UÏá*¾RMæöJ*6ãÉEWÄG4ÉF¹.8»øîß½IµíݼTí¼û0,*1ÍFôè×±åô7ε?FØÎH½ðF-îI»øâç±QÎGûåÌH
+¹üú3òæ**9ØÑäÊGOé½86ã0ÅXUûýýëÁ9ÝÛÕ7SÑäÊC¯èF+*CM+úäÐKË4³4ÒS*ØMîæ·ÝÔû¸´÷úùÑûùñµ
+:*öùñÅÍ0ë¸ôéò¸÷óû+P·ÞÏÒºC,ÞVк×ð**×Nç°:J*¾ÕºSñäËBS¼Ò8±ÌÔÍEÎêîæ»ïýÝÝ»ôëËù×9I¼
+63ðMÆLÐáD*üÆ°PÌD·Ü6+æ;¶ò˯ÙüFø¹ûúO½üW.*¼½úëÊEÝÄW×Ê´ÜIÞ>1FØÉA³Ô6-¾AYØÊ×°6¾×²÷
+<äB*ÍS=åÌG·àVSOTÛGݯE×ßÏ1ùýý½ùîáµ´FMÄ>÷V*2ãËF9*JÅÞÏý»û÷¯åèëéAøõõ:*¶ûøç1Õúöñé
+òµîí7/ÅØðãÈASÜF/R-*@1*VOÉC³0*öËE±ÄFêÛ¼ØÎO°µò3àûýI½ºôêKîÖL1÷ïáÆ:IÀ>çKÀúR<NÉE·
+ÀÊßÄ<=å8¿MQÙØ°ßÈËÉYYîåíK*îöéÉ98×ZãÓ3ëÚÙE48ÛPÊDUèÂCÑOÕ3ñOÊCWäÂç±äÂ÷<PÊF¹øËD·ø
+ò×=LÞPܼÐO<ÁMÅKïýýúá±1Y3ÑFSÐâƱ*P°âÇ<á+úãÌF¹ôòÇÑQÞ×MîæÍÈXÚÕ¯íìÝ5øñÑ-**îÒ-¾ó²
+Âî¾À8-ßW3Nñ+Ìü¹õÛ7õWVDG»U¹MßÏê.¸,J¶üº<*ÜñåÌH¹,7D>*7-îMüò¹¼ýÇG<ÁM1,ñýýõÙUíÌ0
+ÙÊ·*:ú×,Þ¯äú3ÒRÎOÁÐÐJÅô2FøÉÎ/åGVùøô*¼¼***ð,¿ºØ:GDÚ/+í¼Ûôß·/Á¼×Óî2³ÜÚ÷°ä3*ÀS=
+ä¾ÜC*½÷ÐPÌFµðêç¹ôò÷;ïøûõ+3ÇßÏUÏøýýúíÝÅõ¾OÈN·ÐâÇ;í.*Ûò×ñ¿Æô2F¸ÃÌ-á?FÙ·òJ»¼***
+À*FK0ÕÒ1ÈSÛ¸ÈâÓøÇ8úºQ+:Yå0ÌÊ*TýL¿,¸QØÎæÞïK:ô8ÀXèÓI¾+*îâ/°V*Å캴߶-¹ÔÖÓîRZÜ6¯
+²AYܲ×0Þ¯à²çÐãÉBYDËEµè6»âFQ4Xý½D.ÇßÏÅSùýýøëÙµQ¹GÈ6*:;OܶM>3<PÌGÁ;ÂQÌW,ÅHÎñÞµ
+-Ù8¼F>Í9**¾:*9Õ·¿ð97?QW¾:98Þ*È5è1Fç<±-µPÞ<-AFß¿@6*.-¶ÀÉ+.ÒêJ÷ð1ZZæ:W*öïßW1¼E
+Öñ¾W/ŲñãË;Å:±òïË+¾Ê¯Äα*¾ùò×ñâàõùÝÈê7G¾HÈýýÅíÛ¹÷̵ÞÏÊN÷J7ûâÇ>QÜÂç±Q÷Çñ.JÅÞÏ
+Ýñ6ÖWÞÒ»ù0Ýñ-**:.*ºÅ,Jç6N-Ò´FJ*úHÃ@+¶Å7*çAPÞ¸5GÅVFß¿@FK-ùûÙ¿íAZZæ:W*¶íÜS-´5Ö
+ñ¹W/Å;Ã*±²;ÞF1*CÍ6*ê6¶F-ÎG¯<P9ÜÑêîæ/ËüýI»óìÙ-ÝáÄÅ,ÞTØâ×±âï>²ÅÞ;Z5¶Â½QñäÛ¹¼**
+*Ì2¾;Èöãì×*-Îù,ÓQ¶îà.6г´?Å:FæÀM²:0¸Ý¾¿KÁ²+Þ¶ñHûµÞ;ZéT¶ÂJ½,/4òÃ+¾ÍQ0¾À<GD*ÞÇ
+<Oè±ðùÞ7ã0ÅHTùýýï½A½Ûé÷=QÌD·è>D³Ì+öÌE½,OØòçÏIJçÐM¹.8¿Ö¾MóUDôêÙ±Q1**Þ3+:ÅMÚ>
+1Z,ÈI×ïé¸ðà¿5=Î0àÌì4*ñ5W¶Â0âJ´-1Z**F·¿.5*µåÄWØí¾M:ÔÓîR¯ÜÂS=äÊA,JXäº**V±QÌE·ô
+êC4Þ4ͽ6Q´¶òÖîýýÍܸõÊSåÇ<Í/¾Ñ·±åÍG»ôNI¹ôê;¶ò×J5ÄùµòéÖ=.**îÂ*æ±.ûÈ-Ö>/ÉESRZM
+ïÞÃ3æ¹8HÚØáP³K+ÞÃB2-ÖYÞÞ¾ÞËMNA*ú¸òݶ/ÁÌÓÓîR¶àº;*HñäÊF³ìÊ·ñ6*ãâDZÚÏý.ØðPäñ·ùL
+DD6Æç7û*¯ýýõYíûºäV±OÈ?UÔºZ*=*¼=QÍHÁü6È=.ë<¶ÒÍPEÜ5Bò6íöØ.Ð>å³B¿Ä8ºÕïçÍØ48á6ãä
+Ê-*Ñ*²±PËïÄ/*++ÜÑQòUíPIÝèGA1Æ/+пM¿JWÞ;ÅåHüïÉ=OÔ*ÖÇ>QÐ61î>¯ðêç±åÍGÇåÌH¹.8Û;
+èÊ-50û¸´È7ùóÓ+ÞÃ/ëN;FôêÖQA8Ê·³ZÒÓîæ¹äê·±åÊG±4ÌDí.*êº:*ÜÑQÍ9ñÔÔêÚåÕÝI½âãæê*3<
+×MíÎìýýøã¹Ií7Õ²çðOÉ@WÌ+..öÉB·ü64²RÎN>æÍW,Åü3°çÆ3¹XG¹=+¾íÜëØ°ãÐ=MèT¶.I1**æM-*
+JÍ7¾Éʹ¸òîæÍÑõù÷ùýÝ?ACFÞÀ6ôRXF6¶òí×ÁQFÑPËEãP˯ͱ*¼ñPÍJÉ8³T>B7-JNûýí9,ðíÐÐ8WC
+±çÕôëGòÄÌùµîØKÅXW?ßÈU¹½¾S8¶²F³ðÂCÄ1°HC¾îÜ:¾-ýñDOè7W¸÷òæG¸ôíííÕí½¹Â¸ÊË8.ÌáÏ-
+¿÷ý½úìÝÁõ¿OÔV×ÐOÉ>+ãZ¾Ç*½÷ñQÏIÁü6Ⱥ0ó×MîæïXÐÊøòåÎQÑéØ·õ:*öíêÇñÔëC=Á°AõÄÀMÉ×±
+PÌ=*NñP˱*Õ,:2ÞMÐ/º×öñàɹñéϵõHý¼üê½ûÒÅèÖRî@8G5ÕýýåíÌÛ¹ñù¯âǿı*¿Åì<OÉF¿üú3Ò
+.ûC*ÎMîæ×YFÒ²ðèÔUÉèÔ¹2*º´íÜ»ÅäØæCèØÖöÄÀMÞ·**²Ä+ÆËGá*À*X6Þ.¾íF3±âÔÐÁAõåÍMåÜû¹
+öìûúúú÷ýYYï=ø,á¾Á¶ò·8ýõýÜùõíûZÅ3Mغç°OU÷°ãÉÑ*H=æÍK¿4ûC>û6è±ÉÞ;°YUÊ7?G/*ÊPÒÀX
+ùؼÜÑîƸìò·,Þ¹üúç±QÍF¹<ÌH¹ÄJ4ÌG-*Ä+ºNÅ@÷´XûE¸ÅÜÛøôæ×µAíçííÝýýè99ÇDAÌßÏMLüݽù
+èÙµõÓÛèÊCñOÉAãOÊK*ìÐãÉG¿87ÈÒ0GDòRÎKù.8÷øðÕ5íÃù´I+¾àÛÙöóðéE48ãRÎ;¹8ÒæÎLÃ8GØ>6
+7á-Ká,îåÌH½0G22-îAA¼ÒC¶òê廹óå͵19¼ºµ9ý¼ýüû5µæ,Ã÷JÞ3FøRôýõYÝGúô̸éÍJ»øò×±úÑî
+ÚC0¾¾@³D**ÉDOè²çH¿Mã¼G¸ïÚ³õ>*:ìàÏõÈEÜÅMÕ×±åÍE·äÚ3³ðò÷=æ*áîHåCÑâÃ9¿DãDzðÚ×,Þ·
+¼ºBÐQâЯ¹íä˹ÅüÛùøHÜüüúûýÝYØ¿ÁÒOÃ-¶òÕüýݽÜùõèdzÖDé6GPÉ?WÔV÷ÐOÝ÷ÑRÎM¿0ûǼü6ØS
+îöùFâ·F4ÌGáóºQÌC·ð6D²Rý3>RÎF±0/èAðÁöµñ×Q¿ÀÉß,α*JZйº·ñîãàÛµõéÛ½YÝü»ùõ»½ûüûÙ
+A6:;+áß/F¸÷ìã½Õ»@äÌÞ±JÏñÊ3=äÉAWÜâEÁ0?¸ÒRÎ;*îññÄMý3²RÎI»ôêç¿,?øÑQÍI±¸Ç:ãLÔÇìM
+âü¸²å³ÍðÂ×<æF´òFµðêS;àŸG¶ñèϱåüÛ¹µýÛûöø÷åõíûÕIú7;=2¸÷ýýñ½í8HºçDYØÉ>Wü6ÎîI¹ì
+Ê÷ðãËIÇRÎMM*,.ü´F¸ÏKÇ,OT>SϼòæÐLÅøÊ3ѵ>¶ÑXåÑ=ñôè310V×µàW;PÊH¯ô6×òI¿ü.4òJæåûö
+ñêÛU1Iüº9Íü»øÚßÕÕíûýýü²ÖÊüûùûðßÉIY¸ÛYHîàÅEÇ<OØÒA7ø±åÌIÇ8óOÅ@?زæÐLIµFÄD4ÑO-*
+ÚSÑM·äòæÏNåµ°æÜÍCñÌëQÕXºÖìãÔý¸ÖTGÜâ÷ÐQÏH²æÎLQĵIÛíX¼ú¸ôñáÉõíë½åéزòéÛ¼úýûõåÝ
+I¼ýüÛº÷ÇßW<ä;Øâ×òçÑPËèÑOÉ<³øò*JæÀBWøµáÏùú÷=RÏIÁ,óL¿è²×ïàÇ,GâÃC÷¼ôEöZAÚ¸´íÖ5U
+ìé÷DÌD;LÄ9UøÉ>·øN3·üýXý÷éÏSýì¼ÜÍYͼûèÝ1ÙôÁÉ5õYݺûúG¼õïãÁ1ÚV<µÄ;PÊIÇ8GÈÃ<GTÒR
+ÐMM*ÒæÎLÅ47ø¹áÏ-74òQÍK»Ôò9?LVQ;MÅA»Wä½ýX@ú¶Z½ú÷±ìÖGÅHëGÀ-ÅZPÄ81¸È?;¼ýýý½øüÚ²
+â¼=É@ìÛѽYýúñàQÝôÕùÅIH½üøêGúöëñP-P:QAD=QÏKÁ0?4Á87¸>æ*N*D4J¾<ûÙïBÀå¿4/4ÒQÔ2ý·
+½ïÖKÚÖðTå¼·ïä¿9E8Ú×5Üê÷¯èÔÇ5ôL94NQíJÀ4çQÖñ-8ûýýùÍÉܹ@VQÚØ÷÷ùùñYíÓMÕ9¼·úøç¹õï
+ãU9ÊðäÓTóæÉDÃ47´òMÇ8?¸>SÏNãæÏß+¾òñÄM¯D²úÌùV¸Cæµõæûñ0ãGâ4Aìà¾ûZäÁ?EÄúC¯åº¶¯æÕ
+ÅYÔéÉÀʵíáÜÚÛ¹ôúóýÍÝÛûúÜÚ¯ÒAW<ó¹Ø9I½Ý¼ûõÙ9õöñÑåHHúïÄÛUÔSËô>S=SIÈ>çÐPË8W4ÆÄ*æ
+ÐOÃD?è²ÍÞÏ»Y9ÝüÌ,*ÀÝüù8ßÐVîæÇHOØ>SÐ>¹øÛÉûXÃçZݶ8Îãк3Ý?úÕUäF×ñàÊAAôÐ7¹HëD÷ëÕ
+GXÓ¿ë¼ôÚ·¸µ8ý÷ìîõÍÍìKÑÓB9Û¿?å¸åàéíý8û¼û̼ù÷ðÁùW¸ÒA?DñãÍPÉDóQÍH³èÒçÑPSèÑRÅ,Þç
+@¿Mõ8Hûøüí½½XÆ1íJ*îÜ·ýäû¹¶ôìENÉ°¿M9T²æÎK½@>æSPó5DòàWIø¿BéP8Ë·ïßü¶Zá½=ѼVصÜV
+ø¯èÉM9õÛËAý6¸XçËÛ±îÏËÑÝì»»Ýíû°Ñ.ÓG5å/ôò5¹öòùó¹ùîëÉý»ÕRϲòQÍ¿*XòæÏNÅ<7ÈÒ5OÈ>S
+ÏNÃ8?KÇô2FøòßÁý¼ìܽûÒÜûø7.*8H·êÓ±åÌëEJíU¶6*îLÁÄÚÇðKÈGÛVâÓK5HVÖW͹BñçÌGE0ê=E0
+ûøòéÅKùæÆ·±´û¹òçH³èçÊMY@ÊØ=0éÖ´öôýéõÕÙ°òQÍÝÀA»éáÜÝݵII»õËôåÐMÅ@W4Ä0AÐÏNÉ@EÌ/
+ÂN´3F¸ìÕ³ÝÌWHýü²ýüú7.*üüúïÑKÙÌûDÛX¼»½¾S<¶NLÁ87·òL·Èº·°áÊMGXâÒ¯ÉHFAWÕÉBòè×O¹Ô
+ê3Ñ@û×óâ׳ùæÔ1íXê<äÑ8@ïèÁÍÕNÅÎÑFÙ¶µùüíÑôVGPâ2ðNËQGíâÌYá¼úòäBLSزίî;?ODòίï¯
+ÍÞϵÄëøñæíóñAû÷µ;*öûïµ±ìúD´ã°Ø÷ü*ÑE¶.%%%
+d
+569 104[1 0 0 1 0 0]sl 8 mask 0 208 di
+/mask 7416 string uc
+*ÞÀ**ü»ýÅöüÝôûýïùýáõýÅíý9ÝýI¼ý½úüýöûýïùýáõýÅíý9ÝýI¼¯Y-%%
+d
+/sl 58607 string uc
+äÕí´ê9+¾Ð,¾òRÑNá*ºçÐNÇôº÷ÐMQçÑçÙȯáôê5í»ùõñëɽ¸Ð°÷Ð8ë×ñáH×RÎÇ/¹ÜÊÑ5V×?èãQM<ò
++ñ+òÆ×½üüÛºáNGÌÚDZO9ç;NÈ@³4?èÆ<³ø²+ÞB¾D³èòSÑRM,:èÑRûý½ã8ËJ+üõóåÅGUµYÝæÝÝÁ*Þó
+éQIÚðáÍYÑÝ¿½õ1B?,Ö´¼ûظ5ÅâÒçÐϻβ/¾-WS±äÅ<·LóºCÝÜ»¸¯ÝÂGç»,/àêT´U1×=çÁ3¯ÜVè»-
+0æïÝÀ1½¸ÐOÅ8»R°ñüH½ýýö±Áó¶Î-êÕ9ß¾üöWÂJÅ<OȲJàR;N7X.P9F¾CËø²è8¿MQÙØ°ßÈËÉYYîåÝ
+¹*2öâ9F*ÞÌQ3FüøðÁM@V·³LEõôü*ÑFîæÃ<?¸òRÏKÃDÏLÁ87.îM·È²Ç°MËOGêÞεñäËAOY¶íJÀ4Eè
+-æKØ.÷8JÊãQ¹¿23À²B°àÈ8³ZÖÍëõÅÝÝñͽݸàJ1<ñßîÓÉÕÏäÍJ/æͯ*8>RÏNÅ<WÈò6*<OزSÏï*G
+,ÅüÒòà»7ùôüÛÎIýB*.¾K¿*ð8¾éC¶E/QG*VIϾ¾¾>*ß±ó8ÝëÁ1ïâÉÏýÐÏ/QýÛùìÄù@@ËÌÜÓÛIÞ>3F
+,ÏÁº@>SÏN19ÃæÏßÆÌñâÉ?SèVØÔ=´ëù³àTU8ò+C4NöîÝÄ-»èåÕ·íãÈ;MÅBÒâͳË8Gèó¸Xüüüùûù¹û
+éK±»2ë=³îðÖÏöú×òRÀ47°òLÇ@W¸²çÐO1:¹.8ûOæÀñ8Ô»»¹*ýD*B*3ÙßÁ1-Þ·7FËÀFJ:ÇH¶ÑôÞ9Ê-
+E´GP;úU½Ñ×/í¼Ûôß·/Á¼×ßîÖ,:ä2Ä0F¸ÐMÅ0Óç°ãÇÜ<QÓºKñìÛØ9ܸ±àÂ8é;òIùÄãÆòô÷ÓGZÎU1±
+HÌÖYõ3ÑáλY±ÝíõéÅ9ÜöÝ2É¿½ÏÌ;Å1îƼ0G¸P+ÇDWè²çîMSزÅÞÏUòFBXß×ËM1èY-¾-ÎãθÙÜ*RÅ
+èÝRξX¶*<âîȸó1@Þ+1ç*¾øå¹42:Y9NK2Þò¾1ÈSÛøNXÓ-2*¶¾0-+6¾´R5@ÆØûà9RD.18òÀEèUG4Ú
+èÓå³UÒÁ?óºIÌF¾MµHÙZÚ¿GUêT¶²PË<³¸*¾ÌD»¸*îJ+À*úRËAµèÂC>VAº¸µíÔù@ÃéY4²BðK»ýô¸úñ
+Çå¼VÙ±îXEùêÃY8â1ϽW9I¸ìÎýTóL¹4³4óçÑO+ç,¾RMRDâBDßÏLÃø>èÒRÑô2Fø¾Ì-Ý7ê¸öïÎú³*¶*
+Sã>ýó?¾ðÖº°îÀ=.1Þ¶Ë5>YüÞ,ÚÌÆã+>AÒ9¶KØâ¿>.+Ê8æÅÄÈÑ0Àº¶Þ;D.+RQàXÕì/èÃIÆ-ÈYòÀÉÎ
+Ûº¿íãÉý´0ÈݼïºIÌF¾99ì¸îÙ¿G1êT¶NRË@7´F-2⹿Äó*¿*ìñãÆ=³4/¹ÖÕäëØöäVO8òý.TNéÚôãâ
+ÛòêË»Á@»ÕW1DïE¼YÍ1åVó<ûCÑæÒ+*ÏÂà²çØáS*RØND:;VÎOîæí·0ê@ZèÜ»ÁíÕ+Þ+ò´ßÕ5í*¼áìLå
+;-:FÞºý0îùÃ/2.Öá,>,***.ÞÛÐ:.Z¸*O±:7ÎØï¾M²2,Î3Pýüû×À-KZÝûÎèúJAú*íÝ7âüûE´¹î¹õ+
+åùïâ+ÚµíÒ+Ù/ê÷ÛÔÀMR¸ÊQÇ8OTòæÏ?+̲SÐOÉ<G¸>67/îNÇìºC=ãÇBûæØÂGí0ÌøYQ4<èß½?í¼ì?á
+ìÚCéÎüü·Á<ÓPÌëC¯ÜPÓMËJá5Ä3»çÐOÅ074²R;T²6*.7Ó:LÅ0XÂTò2F,´+Åöõ²ìÚ¹Ý0*1ιBDê¸-æ
+7;W±UC¾ï¾M5Þ1VÑ0Îù,ÓÅÐîJ1ZµL3ÝêG5¶Å0TKòìÛIU¶;.>ÙHA1äÞIÒì7OéìÛßØ>ÛOɶÜ9Q8Þ¶ñH
+ûµÞ;ZéT¶6ÑE*2TF¼67*P-6ßæ5ÄFCîI±àÒ3±QÔ¹GðéݽÁPüº¯ÕÉ÷´ßÉÓüâæ-´-B@±ìÕ?¹Ü³ÉÌVC²æ
+ΰ>çÇáB*W¸ÒRÏLÁ0GÈÏL58ÓæÎLÇ4OØMî2Â/FTÝЯåÜGù¯*J*××,¾E4J7ÂGÛ?*ä++Å@Þ×ââ0JôQ´ú
+4éÇYNß¿8Öê8ŶS8:.>ݾ,QJ·´*Ǹ³äV´O¸;.ÂÖY.1¸ß÷ÖËÛ97àÄñB½×Ó<¸Ë9Q8*µåÄWØí¾M:ÔÓîB
+¾2È0*Ê,Â>ÄA8LÄú¿Ï-*=-ñ,H=PÈA³4ËE×ÑÜÛú·ëѱýóRëãÁ1°éàØûóá»çD7DñNé3>RÎßÁ=ÅM*ï21
+îöJ**æR*:ØMî汿@6õíæÕ¯I1**îL+òG19X믱FJ*Þ¼¯+ÌðÀµ1ZYìN¿>ÖïN482*»ÄÎY·¾ÆÐî,4Ú>/
+Ö+J´.¿,Ï>÷Ã:Î41ÒGæ/íÖ8ôéàB¶ZäXáÌ/òÓJF8*¼GøYÚÀKåèT¶.N-øúçÒQÏLË4³T·4³W¾FWD**ÊÄ
++ÎÑQÏÄ*¾Ñ±¿Ì±PË6UðFÈÔý?»¹÷òßû<ò.ÍTÌûC¯Û²ëæÍLÁ09ÊBÖÐQÏP7*ZTÕTËD0+ÊÔJÒNÊÊ,ÐG,Å
+øç²á¹+Á¼ýÄ÷Î*ÓÔÖOHI¶MÏ3Êò.¿FÈú*,ÂQYCWÁÄ8ºÕïçÍø48?SÑNËL³¸ÂÆÎHËD0:ä1Æ0*ÎÐNÉÄú7
+3Ä/Â-¶ÐO±ÜÒÇÏOËìÓWÝɳE9ûÕYÔ6öëÕP³¼.F1H-F¿ç¾-ÆDü:ðBøBFßÉ@GØòæÏLÅÀÏï*Ï3G,Åì2³ä
+¿/Á¼GEãÄûøS***Q*î´0òóé3.*¯øØ/ð?+¿6ö¿-ÆÔÔÑS,ÅGEôçÃ-±X76³?FÈÎMM*.çÏMÃñÁÞ²>¾:?D
+*JÀ07ó5ôE5B12,òK¿ØºSñNËEÛSÖÀQ5XÙóPêCñåÊCå0JçÎä×-¯øò+,>-*ÐK1F>æÍI½0/DÀ+Àô2FøQ
+ÆOMPê·óêË.*¼ù´éÈ?ÉÌ6SUßï6¯î927¸NMÇ@G¹>QËÄGÈÏLM/P6åýÌF¾1?È>SËH»¸ò=Y0óTñOÈC3QÏ
+NM8à0,6L8Þç,*¯4³ÚïF¾BWD¸ÞÁüúS>RÎKÃ,ÎG,ÅüWTèÏ?±@ûØæÔÛÁìæ·ý0XEòæÓÓ<V·³õ1BO,ż²
+RÏKÅ0GT>:7S²NÇ@³ø**VSÐMÅ@CÔ,.,Â1ÂE@9öÊDµP²Ç±Pί,?*¯/ÏÌ´>çÐOÉGå6M+à*ÊSÐPÇ0µ4Î
+IM8¹.8GìOÈSÍü6ø²¸WEõ8,*ýËG·ìØG±°Rôðì9ZÈ-8ççÑRáöà*,*RçÑR-*ºèÒTÕXÓ¸ÓTÐD?è-@RL2
+*2D2>îTÏTÓT³åÍRKè°-ÆDPJÔT7³BP*Qå8,9Ì/¾SÃè>çöR¶ØMîæ×YFÒ²ðèÔUÉèÔ¹2*º´íÜ»ÅäØæCè
+ØÖö0+Ñ>îæÆDWè²6J+*67-:NÉÄ1018*.FH¾+°Ø>ç2*RDÌÖÖ:ºBÀ:î¶*:Ò*Î0¶0WÌ=Ê»ÔGÆÒTå*L<B
+èÐMÃ8?ȲÉÞ;°YUÊ7?G/*ÊPÒÀXùؼÜMÞ>-FÈÐNáóÌ6Þ**<GÈ>çÐßÅàÒçöJ¸øÒç*,J¸*:¾ÈDW¸2B8
+JÍP³DÓSÒ¯.ïÃÞ²6ÞÇ>.KÂH>G.O0U0CÎÐKÅÄ*ò3FðóãYAÜ5õë¼,*ùµïéãÕõ2Bã*Å°>çÎÞB¾¸¾ÈMC
+Tòö9>7;6<6ù=PÉ8**Ðï,KÆÁ.L>çÆKÇ4³òî6ß+H8FÀѯ.¿1¯ÎÔòçÑRÉ/ÂSÑAâJÄøí<¶ÒêÕMAÌÙôÌ,
+*áWFøóWðOCÞ;>Gå5;6Ù5õ-N96*:71:NÉÌýÏÑQÏÄ*Ê+0<*Ï-/ß,+Å:³Ò;KÃ4óÖïúÞ.JVÞòðJLæJF°
+Ê¿BWøJ@NSÐË,ÅÝåKÌáïÒYBYCJJJNJ²Þ+1Â/BÓTÓXW*ZSÕPÓøÂ/:TÒSÕTW+BAB/25ÞTÑTÃD³èÒOËL
+?é;¿*¿+Á°O¿4óíÄM»èòçÏQËÌ*2ÑN9FJN*CWØò6*:7/2ó-SÁ4á/Q8ççÑO-2,,KèSã×àæ฿öÀ¿Á*Á
+¶OÆDºZà2Æ01:ÑOË8Dúë¶òLË4G¸>çÏOçRÐMY**:W3¶á+²²çб½/À-ÁJ²÷ZÆ°ºÞ¿°ÀÑï-ÎòÞK2*>7@
+2ÃF°:PËÔ32ÐLU*ü´FØÐOÉ<7+R.*N5+XÂÞ/061.GLÒPÏHËDð2ÐÄ.:ÓRÓÄ4,Ò?Ç*³ç¯Âß.=;W4³+Jâ
+;B7-*M1<X:,:ûÙïRÃD?èòæÐ;*¿¹¿½ß*ï*áÄÀòæÏMBDF¶PB¸B.JÌT»<*¯+à²çJM·è²R+>G´20:PÏÌ
+M86Ê*:ÐKÃÌNÂXʯúë¶>TÑH2*Ç4óÃ../ÆÒRM+à+è,4/,1P,2UÔWQ*ÚéÔVÝXë¸?UáȳèÓ¿/H?TÒRÑT
+ÛT³ºÞ/+.F.3ÊR*ÒM1ñ*½E8ÛçÑNÍD»ØòS²è²çÑQá*à+À*ä-¾çZ¿Þ¾D*UØÒSÐÑ*öòSÒQÏ00ÀÒ;Ä;*.
+óSÒÑ,¾²Æ+FGå6¿NRÏH7*¶J01,*ÆÔZºì¶.OQöLü8¾ïÓ*61F3+KÈò>*2W6²PÏHÓ¸,:ø2ÓTËè²çο-Í
+ºZÄA3ïÂ+ü´F,ÏK¿°ÒçD*S¸ò¿:ÜB*Zøà*è-À0ÂçÏÏ*°Òç+-¶¸,ÞÎLË4³òïö°N*JG=2*JOÇ8»à2O0á
+0ÔÒSÏPý´FÀÐÁ»K*³*Á*ó+K+O*³,18îä.,-ÖSÑTËT»Dóâ;VG±6-B+B¸2*2íº=41¸;:îµáÏUãTÓTÓV
+ÓP71JSÑX75*Uá,à*Ä+à+¾èƾ¾¾Î*¾ØV*ÎH¶¾:-¾¸ê*×´óè³é**߸ó*J>+N»Þ0,óèξ;Â-Û2<öÙï2
+Ìä9*β/*KÓ4³F¯/¾P»èÒçÐO9+¿B³ø°6ü´FèÐPÉD³èò6*B73¶=*Ð>ç-::NÉÜL¿U3*LÁ2à*T*Þç-=*
+Ná4Ä5È6XÖ+671BP:LËüë¶V¯ºË*ÎòG-Úõ¿B*RD,JÌL»²,Å2øÌ3à+¼¶FÀÐAÓõà-0,ìZÞ?8Fï+:D0*Ä
+Ü5À56¶*,QÒZ0îµá϶Ӹ,*ØÄ*:ÔUÙÄ**ÔA,JÔ°G**Uá+L*NUÔK-Á.¾ó+?¾ÔÃ0¾³.*V7/:UÓ8=Ú*¾+
+Á**ÎØ@ïÀ4ä*ü´FÀÑÇ-*>»æ+Þòã1NÎ+½E87TÑPËÌ+BÑQÏH71B-¶<*¯Àà>è592F¶P-O0AHZÊîR+È<Y
+ÆßC=ú붺ÿùÒ¾í7Å*³.¾S»4óçC*ÂèÒçÑSá*ÞçZ¿×¾,*°è²è-D:PÓÄ+Ú¸*¶D8îÎ68¾KWÞ3CÂ/*Ó¿/
+½E8ËèÔTÛÄ*RÔT×X븳6*Z»:*A*îÀ-À0ä*04¾é*Á-°2²¶ï,µæJÔX»P0¾óºLKK6ÞíÄMÜè>èÑTÍL7/:
+RÏÄ-2ÒSå*P*îçÑSÏL»±*J³6ÞÃ*BÑUËXG°B´R°¶á0î²:Þ3C*ÑÃ/AʾÌX»øµá;BË;*.èѵM:OG>,úë
+¶òRËX³TÓSÓQ3èÑTU*2TÒSQ*Þç¾*·ø²SJJÞ+9Â+Â@ÀÓ;ı*µPZâ:Ú:ÌZâî¾7XÇ¿FÃD¼KÎDûÙïB.ÆÎ
+D³D¸1ÊÌ+Ê*¾Ñ/*18îJ.8>*J»Ñ+O0Qêî²*8ØßÎK·ßíÄMö¸*ÞÙ¸Ûè³éÕYÛÄ-Â,.*Ê,2ÔVå+8.*²7DB
+-*TU.RéÕV-2ìÆKãJÇßÏ,FJÁ5-ZYÓ¼ûøµáÏBø,*ÌÄ*2ÑP96*F7**P5*8FJïJ6JWÞË*Ò5>AÚJÁ=°B
++¶.0M0á0ÞòéÄMî½å*öÖï2üíD*ü³F¼ý4öüïöûáïùÅáõ9ÅíI8ݽF¼ý¶úüµîûáïùÅáõ9ÅíI8EJGêùøö
+óY@*2üøíõÁ8A9ï5àϹ1ͼ»ýõÝIAâÁí;*ÚµìÝ·ý8ÌÛùÓÎáïMúÀ¶òçÁ1ýûÚ»ýøS»ùóõ2*8H·êÓ±åÌë
+EJÝ3¸Ú»Âµ2ÅÌù³ëÕ±ÉåÝIëÝIý8+ÞíÍI¼D²é×YÑéãÕí-GìHàEÂMí6ÙóåÏÝéåYøñÝO*öûïµ±ìúD´ã°
+Ø÷ÃÎáçMºÁ¶ò½Y½¼ê׸ùøÃüûÉ.*ÕYIûîÈ9½¼Ëü0ü»9ò:øóݺÂL*èäÕíõ1ÅÌ´³ãÀ?EAÝÜÞÍI;*2·B°
+,ÀV²ÞïÕAIF°ßÈU=Ø·µQÊ8SGàEÂM½T¸ïÜÄ»¹ýXæͽ.*¸@:1,>ê¶/,K*ô¿ò÷*8ÄÞ7.:éñåQK-14Þç½
+õÜÙïÜÃOÑѵ³QÄ8×RàëMºÁ¶òAK5̹Öôóë+úM*ÞVS*GÜûµâÕL:.**Fò¾C*Þüà+XéÞO1öôé³9ÌUBòÉQ
+ÞA7¸NÑ3Q9ï7àÏUòFBXß×ËM1è9+¾Òï÷Þ+Ú´R5@ÆØWG*QÆß?ó***ÎÌ+R1FN-*èá+À,Ú¸*ó@RýL¿,Jõ
+¾0ÌW߯*¶òä±5ÄEÖ±ÃQÞA5¸ÚÏ.Í0ß+óèIíSJ¾ºËâ;ÜKFø¾Ì-Ý7ê¸öïÎú;*2.Ù9F¾¹<ô¸óXÎÈ-ÎïDA
+RÜF¾;*Ï=ð¿-Ò,Z¶ÞÉQ0X¶À-6¶µ/µ-¾Ä1µGÈKF1º,¼×Îá7OÆ-È--ÎμÞÇ@Çâ;ÜKFø¹Ì+Ù/Êصíîù;
+*2.Ó9F¾QBûùõå4¼-¾Ü½-öû¹3¾J¾ûÄ,P+VµÏñÆÛÞ¾¹U¶E*ìÙµè¾ë,ôúì3öãÀ8µ>¿úÎèF>,+**:*õF
+8ï7à;Z5ÁÁ½QñäÛù;*2B<9F¾U2Û¹õå4¸-:Û¹I;*¸ÞDó¸*Þû*.6г´?ÝÎ+Îîê4ì0¾ïä»øí*IÞô.ú°K
+ÅEÈÞ×ó·çÛù,Ó÷FàEÂM:@0QûÈÄGùôêµ*Þ±¼¾R·³ßE±ùNÛD±Ü?S´*<¹ô=,JV°°51J/.U¹*,ìJêçW+Z
+Ì˾SÌ7JбU¶D*¶¹´êÐû0ö+¯QÞA5¸ÚÏ.QêAÀê×I×ïUVQÌâ;ÜKFøVÎ/áûEø³êå*Þ·ÔϾJ/IÖ;¹M2>R
+È6E*ê*ù3+¶È5ÜG¾7ùÑ/Zí+2ö¹8HÚØá0´SN+´*ÅÕZ4îP*öóæ½ï40Gé3öãÀ8,E¾5Èî?8RÁ7ùðP4N¿7
+ÇFàEÂMû²ØïÛ¾KÝ=ÚVB¾TUêð¸/80î»0ææ¿õDHûç3öOÊ8å@îî;ÜKF¸MÒC1@6øóê>EôéQ+**>*F5+ÉJ
+*2ѵË:C2¶êׯÍXFBñèÞæ3öÛPÅ-ê¶æ7·GàEÂMý.ײâÂGÕÌ»Í*îÙµÙÔV×òâÆ̾í4æïò;ÜKFøµSSÅÔÊ
+×òéLÙôOÜÏñü7Ú´æÏUUOÖðèÜ1¸Ú»Âµ2Å8õ<ãÒQ½0»DÛÔËù<+Þ½±QÌÙ´åÊ6ÈÛÚAÀ8Q9ï7àϱݶRçâÓ
+WÕ@ÓWY<*º´íÜ»ÅäØæCèØÖö»ÎáïMºÁ¶Âêé4±Mã°+¾çÂTæ³YéåõÀ8A9ï7à;YYü¸ïÛ¶íX?*>ìÛÃYAݼ
+Cò¶ö2í:8?ùóæÃñÔ»×*JÚÃM½ó»ÉJÅÁÅö0ïÖ=ûðûáïùÅáõ9ÅíI8ݽF¼ý¶úüïöûáïù;õ9ÅíI8Ù=8+ä
+-**Eݽ*NH01ÖRúø7¶ÒLÃ<?4òåõ9*çÍ¿óâ·±ä5¶ÒB¯ì.D>æõ9*÷Ñ+ùÚ÷ÐOɼû0FàÆ<SàÚ÷û-Þ0êJ¯
+Ð>CÐYÝ+ÅدáÅ:MÌúü+:½DZÈ>M¼úæÕí¾MÉB¯LÃíI*´Þ0°;á**ïéµÞïù9¾ã.,Öøü+ZIÂ*%
+d
+569 103[1 0 0 1 0 0]sl 8 mask 0 312 di
+
+QP
+%%Trailer
+%%Pages: 1
+%%DocumentFonts:
+%%EOF
diff --git a/doc/krfb/screenshot.png b/doc/krfb/screenshot.png
new file mode 100644
index 00000000..54472e52
--- /dev/null
+++ b/doc/krfb/screenshot.png
Binary files differ
diff --git a/doc/ksirc/Makefile.am b/doc/ksirc/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/ksirc/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/ksirc/index.docbook b/doc/ksirc/index.docbook
new file mode 100644
index 00000000..abead63a
--- /dev/null
+++ b/doc/ksirc/index.docbook
@@ -0,0 +1,1446 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY mirc "<application>mIrc</application>">
+ <!ENTITY kappname "&ksirc;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY irc "<acronym>IRC</acronym>">
+ <!ENTITY CTCP "<acronym>CTCP</acronym>">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &ksirc; Handbook</title>
+<authorgroup>
+<author>
+&Andrew.Stanley-Jones;
+&Andrew.Stanley-Jones.mail;
+</author>
+<author>
+&Philip.Rodrigues;
+&Philip.Rodrigues.mail;
+</author>
+<author>
+&Anne-Marie.Mahfouf;
+&Anne-Marie.Mahfouf.mail;
+</author>
+</authorgroup>
+
+<copyright>
+<year>1997</year>
+<holder>Andrew Stanley-Jones</holder>
+</copyright>
+
+<copyright>
+<year>2001</year><year>2002</year><year>2004</year>
+<holder>
+Philip Rodrigues
+</holder>
+
+</copyright>
+
+<copyright>
+<year>2005</year>
+<holder>
+Anne-Marie Mahfouf
+</holder>
+
+</copyright>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+<date>2005-05-10</date>
+<releaseinfo>1.3.12</releaseinfo>
+
+<legalnotice>&FDLNotice;</legalnotice>
+
+<abstract>
+<para>This documentation describes &ksirc; 1.2.1, the &kde; &irc;
+client.</para>
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>KSirc</keyword>
+<keyword>irc</keyword>
+<keyword>Internet relay chat</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&ksirc; is the default &kde; &irc; client. It supports scripting
+with <acronym>Perl</acronym> and has a lot of compatibility with &mirc;
+for general use.</para>
+
+</chapter>
+
+<chapter id="using-ksirc">
+<title>Using &ksirc;</title>
+
+<sect1 id="irc-basics">
+<title>If you haven't used &irc; before...</title>
+
+<para>Simply put, &irc; is a chat protocol, defined by official Internet
+standards, and capable of stealing many hours of your life. To use
+&irc;, you must connect to a server, and then join a channel (equivalent
+to a chat room).</para>
+
+<para>To connect to a server, open &ksirc; and select
+<menuchoice><guimenu>Connections</guimenu><guimenuitem>New
+Server...</guimenuitem></menuchoice> or press <keycap>F2</keycap>. In the
+dialog box that is displayed, select a group from the leftmost drop-down
+box and then a server from the middle drop-down box. The port, shown on
+the right, will almost always be 6667. A new window will be displayed
+showing the messages sent by the server to the client. When these
+messages have finished scrolling past, you want to choose your
+channel. To display a list of available channels, type
+<userinput><command>/list</command></userinput>. To join a channel,
+type: <userinput><command>/join</command>
+<replaceable>#channelname</replaceable></userinput>. To leave a channel,
+just type <userinput><command>/part</command></userinput>.</para>
+
+<para>That's the basics; for more detailed information, read
+on...</para>
+
+</sect1>
+
+<sect1 id="ksirc-basics">
+<title>&ksirc; Basics</title>
+
+<sect2 id="servers">
+<title>Servers</title>
+
+<para>&ksirc; allows you to connect to any number of
+servers. To connect to a server, select
+<menuchoice><guimenu>Connections</guimenu><guimenuitem>New
+Server</guimenuitem></menuchoice> or press <keycap>F2</keycap>.The
+<guilabel>Connect to Server</guilabel> Dialog appears, and you can select a
+<guilabel>Group</guilabel>, then a <guilabel>Server</guilabel>, and also
+a <guilabel>Port</guilabel> to connect to.</para>
+
+<para>Just click <guilabel>Connect</guilabel> when you're done, and a
+new window for the server is displayed. You can keep track of the
+servers you're connected to in the &ksirc; <guilabel>Server
+Control</guilabel> window.</para>
+
+<para>You can get help on most &irc; commands by typing <userinput>/help
+<replaceable>commandname</replaceable></userinput>.
+ Typing <userinput>/help</userinput> on its own gives a list of available
+commands.</para>
+</sect2>
+
+<sect2 id="channels">
+<title>Channels</title>
+
+<para>You can connect to channels with the normal &irc; command
+<userinput><command>/join</command> <replaceable>#channelname</replaceable>
+</userinput>, by selecting
+<menuchoice><guimenu>Connections</guimenu><guimenuitem>Join
+Channel...</guimenuitem></menuchoice> or by pressing
+<keycap>F3</keycap>.</para>
+
+<para>Channels you are connected to are shown in the <guilabel>Server
+Control</guilabel> window under the server to which the they belong. To
+leave a channel, you can <userinput>/part</userinput>, select
+<menuchoice><guimenu>Channel</guimenu><guimenuitem>Close</guimenuitem>
+</menuchoice> or press <keycombo action="simul">&Ctrl;<keycap>W</keycap></keycombo>.</para>
+
+</sect2>
+
+<sect2 id="channel-options">
+<title>Channel Options</title>
+
+<para>The <guimenu>Channel</guimenu> menu, available in any channel
+window, contains some useful options:</para>
+
+<variablelist>
+
+<varlistentry>
+<term><menuchoice>
+<shortcut><keycombo>&Ctrl;<keycap>S</keycap>
+</keycombo></shortcut>
+<guimenuitem>Save to Logfile...</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>Saves the contents of the channel to a
+file.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice>
+<guimenuitem>Time Stamp</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>If selected, prepends each thing said in the channel with the
+time it was said, in the form [HH:MM:SS]</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice> <shortcut><keycombo action="seq">&Alt;<keycap>C</keycap>
+<keycap>T</keycap></keycombo></shortcut>
+<guimenuitem>Ascii Table</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>Displays a table of Ascii characters</action>, from which you can
+choose any characters you require.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><menuchoice><shortcut><keycombo action="seq">&Alt;<keycap>C</keycap>
+<keycap>P</keycap></keycombo></shortcut>
+<guimenuitem>Beep on Change</guimenuitem>
+</menuchoice></term>
+<listitem>
+<para><action>If selected, &ksirc; emits a beep every time something is said on
+a channel.</action></para></listitem>
+</varlistentry>
+
+
+</variablelist>
+
+</sect2>
+
+<sect2 id="user-options">
+<title>User Options</title>
+
+<para>In a channel, the user list is displayed along the right hand
+side. Right-clicking a nickname in that list gives you a pop-up menu of
+options, most of which are self-explanatory. Those actions can be configured (changing the order, adding or removing some) in the Configure KSirc... dialog in the User Menu tab. The default actions are the following:</para>
+
+<variablelist>
+
+<varlistentry>
+<term><guimenuitem>Refresh Nicks</guimenuitem></term>
+<listitem>
+<para><action>Sends a message to the server to request the list of users in the
+channel.</action> This ensures that the list of nicknames is up-to-date.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Follow</guimenuitem></term>
+<listitem>
+<para><action>Highlights the user's nick in every message they send to
+the channel. This way, you can follow someone's thread of conversation
+in a busy channel. The user's nick color is picked at random.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>unFollow</guimenuitem></term>
+<listitem>
+<para><action>Stops following the user.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Whois</guimenuitem></term>
+<listitem>
+<para><action>Sends a &CTCP; whois query to the user, which
+provides information about the user, such as his hostname and what
+server he is connected to.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Ping</guimenuitem></term>
+<listitem>
+<para><action>Sends a &CTCP; ping request to the user, to
+determine his echo time.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Version</guimenuitem></term>
+<listitem>
+<para><action>Sends a &CTCP; version request to the user, to
+determine what software he is using.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Abuse</guimenuitem></term>
+<listitem>
+<para><action>Abuses the user in one of various amusing
+ways.</action></para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>The following commands are only available if you are an operator
+on the channel:</para>
+
+<variablelist>
+<varlistentry>
+<term><guimenuitem>Kick</guimenuitem></term>
+<listitem>
+<para><action>Removes the user off the channel.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Ban</guimenuitem></term>
+<listitem>
+<para><action>Bans the user from joining the channel.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>unBan</guimenuitem></term>
+<listitem>
+<para><action>Removes the ban on the user joining the channel.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>Op</guimenuitem></term>
+<listitem>
+<para><action>Gives the user operator privileges.</action></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guimenuitem>DeOp</guimenuitem></term>
+<listitem>
+<para><action>Removes operator privileges from the user.</action></para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</sect2>
+</sect1>
+
+<sect1 id="autoconnecting">
+<title>Autoconnecting</title>
+<para>If you have a number of servers and/or channels which you
+connect to every time you use &ksirc;, then you can set up &ksirc; to
+automatically connect to them: In the <guilabel>Server
+Control</guilabel> window, select
+<menuchoice><guimenu>Settings</guimenu><guimenuitem>Configure
+KSirc...</guimenuitem></menuchoice>, and in the dialog which appears,
+select the <guilabel>Auto Connect</guilabel> page. To add a channel on
+a new server, fill in the <guilabel>Server</guilabel> and
+<guilabel>Channel</guilabel> text boxes, and then click on
+<guibutton>Add</guibutton>. To add another channel on this server,
+select the server name in the tree view, enter the channel name in the
+<guilabel>Channel</guilabel> text box, and click on
+<guibutton>Update/Add</guibutton>. You can repeat this process as many
+times as you like to add several servers and channels for &ksirc; to
+connect to at startup.</para>
+</sect1>
+
+</chapter>
+
+<chapter id="ksirc-configuration">
+<title>&ksirc; Configuration</title>
+
+<sect1 id="config-intro">
+<title>Introduction to Configuration</title>
+<para>The &ksirc; configuration dialog is available from
+<menuchoice><guimenu>Options</guimenu><guimenuitem>Preferences...</guimenuitem></menuchoice>
+in the &ksirc; <guilabel>Server Control</guilabel> window.
+</para>
+</sect1>
+
+<sect1 id="look-and-feel">
+<title>The <guilabel>Look and Feel</guilabel> Tab</title>
+<sect2 id="windowmode">
+<title><guilabel>Window Mode</guilabel></title>
+<para>
+<variablelist>
+
+<varlistentry>
+<term><guilabel>SDI Mode (old behavior)</guilabel></term>
+<listitem><para>Causes &ksirc; to use single document interface mode, in which
+each new channel or <command>/query</command> window has its own, separate
+window.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Paged MDI mode (XChat)</guilabel></term>
+<listitem><para>Causes &ksirc; to use multiple document interface mode, in the
+style of <application>XChat</application>, in which each new channel or
+<command>/query</command> window has a tab in one main &ksirc; window. Each tab
+can be brought to the front by clicking its tab, and you can switch tabs using
+<keycombo>&Alt;<keysym>Left Arrow</keysym></keycombo> and
+<keycombo>&Alt;<keysym>Right Arrow</keysym></keycombo>.</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+</para>
+</sect2>
+<sect2 id="wallpaper-config">
+<title><guilabel>Wallpaper</guilabel></title>
+<para>
+In the <guilabel>Wallpaper</guilabel> section, you can change the image placed
+in the background of your &ksirc; windows. You can enter the name of the file
+to use in the text box, or browse for files using the button to the right of
+the textbox. Image files used must be in &GIF;, <acronym>JPEG</acronym> or
+<acronym>PNG</acronym> format. A preview of the image is shown at the right of
+the dialog box.</para>
+<para>If you don't want a wallpaper, simply right-click in the path field and choose clear in the context menu. Or remove the wallpaper location to leave the field empty and click Apply. No wall paper will be set then.</para>
+</sect2>
+
+</sect1>
+
+<sect1 id="general-config">
+<title>The <guilabel>General</guilabel> Tab</title>
+
+<sect2 id="general-global-config">
+<title><guilabel>Global Options</guilabel> </title>
+<para>
+<variablelist>
+<varlistentry>
+<term><guilabel>Dock in system tray</guilabel></term>
+<listitem><para>This allows KSirc to be docked in the system tray. By default this is not enabled. When KSirc is docked in the system tray, you are able to access several options by clicking on the KSirc icon. When you close KSirc window, the icon stays in the systray until you quit KSirc.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Color picker popup</guilabel></term>
+<listitem><para>If selected, a popup window from which to select the color of
+your text is presented when you press
+<keycombo action="simul">&Ctrl;<keycap>K</keycap></keycombo>. If not, you have
+to type the color codes manually. See also <xref
+linkend="sectcolors"/>. This option is selected by default.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Auto create window</guilabel></term> <listitem><para>If
+selected, &ksirc; will automatically create a new window for each user who
+sends a <command>/msg</command> command to you. If not selected, any text sent
+to you with <command>/msg</command> is displayed in the current window and you
+can use <command>/query <replaceable>username</replaceable></command> to create
+a window to chat to that user. It is selected by default.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Auto-rejoin</guilabel></term>
+<listitem><para><action>Rejoins channels automatically if you are
+disconnected.</action></para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Auto create on notice</guilabel></term>
+<listitem><para>If someone sends you a /notice then if this option is checked it will create a new window.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Announce away messages</guilabel></term>
+<listitem><para>If this is checked, you will see the messages when a user selects the away option. By default this option is not checked. It is checked by default.</para></listitem>
+</varlistentry>
+
+<varlistentry id="nick-completion">
+<term><guilabel>Nick completion</guilabel></term> <listitem><para>
+If selected, switches nickname completion on. Nickname completion works as
+follows:
+<orderedlist>
+
+<listitem><para>Type the first letters of a user's nickname.</para></listitem>
+
+<listitem><para>Press <keysym>TAB</keysym>. The text you typed will be
+completed to match the username, including changes in capitalization if
+necessary. &eg; <userinput>phi&lt;TAB&gt;</userinput> becomes
+<computeroutput>PhilRod</computeroutput> if there is a user called
+<quote>PhilRod</quote> on the channel.</para></listitem>
+
+<listitem><para>If more than one user's nickname on the channel matches the
+text you have typed, the first name in the list is chosen. Pressing
+<keysym>TAB</keysym> subsequent times displays the next nickname in the
+list. For example, if there are users <quote>PhilA</quote> and
+<quote>PhilRod</quote> on a channel, and you type
+<userinput>phi&lt;TAB&gt;</userinput>, the text will first be completed to
+<computeroutput>PhilA</computeroutput>, and if you press <keysym>TAB</keysym>
+again, it will change to <computeroutput>PhilRod</computeroutput>.</para>
+</listitem>
+
+</orderedlist>
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Use color nick list</guilabel></term>
+<listitem><para>If selected, it will use the colors set in the Colors tab of the Configure KSirc dialog for coloring the nicknames.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Display topic in caption</guilabel></term>
+<listitem><para><action>Displays the topic of the current channel in the window
+caption.</action> If not selected, the topic is only displayed inside the
+window.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Dock passive popups</guilabel></term>
+<listitem><para>This option is only available if Dock in system tray is checked. If it checked, the passive popups will be displayed docked to the system tray.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>One line text entry box</guilabel></term>
+<listitem><para>If this is not selected, the entry box where you write your text in each channel will expand on several lines depending of the text length. If this is selected, only one line will be used and that means you will not see the beginning of the text you are writing if it is longer than the line.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>History Length</guilabel></term>
+<listitem><para>Stores up to this many lines of chat from each window, allowing
+you to scroll upwards and see what has already been said.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Auto save history</guilabel></term>
+<listitem><para>If this is selected, private message history is saved. When you open a private window the second time with the same user, you will see a log (the history) of your previous chat with him.</para></listitem>
+</varlistentry>
+
+</variablelist>
+</para>
+</sect2>
+
+<sect2 id="misc-general-config">
+<title><guilabel>Per Channel Options</guilabel> </title>
+<para>
+
+<variablelist>
+
+<varlistentry>
+<term><guilabel>Override existing channels options</guilabel></term>
+<listitem><para>If this is selected, the settings in this tab will override each channel options so these settings will be applied in each channel, independently of your channel settings in the Channel menu. This setting will only work until next time you open the configuration dialog and it will be reset unchecked then. This is because you probably don't want to override the existing channels options all the time.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Time Stamp</guilabel></term> <listitem><para><action>Prepends
+each thing said in the channel with the time it was said,</action> in the form
+[HH:MM:SS].</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Show topic</guilabel></term>
+<listitem><para>Displays the channel topic on top of each channel window.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Beep on change</guilabel></term>
+<listitem><para>If selected, a system beep will be generated whenever a user on
+your <command>/notify</command> list signs onto the &irc; server you are
+on.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Enable logging</guilabel></term>
+<listitem><para>Creates log files for each channel per day. These log files are stored in $KDEHOME/share/apps/ksirc/logs/ and the files look like: /$KDEHOME/share/apps/ksirc/logs/#channelname_year_month_day_servername.log</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Hide part/join messages</guilabel></term>
+<listitem><para>Don't show part/join messages. This is useful in a channel with many people.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Default encoding</guilabel></term>
+<listitem><para>Fixes the default encoding for all channels. Choose the encoding in the drop-down list.</para></listitem>
+</varlistentry>
+
+</variablelist>
+</para>
+</sect2>
+
+
+</sect1>
+
+<sect1 id="startup-tab">
+<title>The <guilabel>Startup</guilabel> Tab</title>
+
+<para>
+Here you can configure &ksirc;'s startup settings.
+</para>
+
+<sect2 id="server">
+<title><guilabel>Server</guilabel></title>
+
+<para><!--to be written-->
+</para>
+
+</sect2>
+
+<sect2 id="name-settings">
+<title><guilabel>Name Settings</guilabel></title>
+
+<para>
+
+<variablelist>
+
+<varlistentry>
+<term><guilabel>Nick Name</guilabel></term>
+<listitem><para>Set your &irc; nickname.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Alternative Nick</guilabel></term>
+<listitem><para>Set the nickname to use if your first choice is already in use
+by another user.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>Real Name</guilabel></term>
+<listitem><para>Your real name, as returned by a <command>/whois</command>
+query on you.</para></listitem>
+</varlistentry>
+
+<varlistentry>
+<term><guilabel>User ID</guilabel></term>
+<listitem><para>Your real name, as returned by a <command>/whois</command>
+query on you.</para></listitem>
+</varlistentry>
+
+</variablelist>
+</para>
+</sect2>
+
+<sect2 id="notify-list">
+<title><guilabel>Notify List</guilabel></title>
+
+<para>
+Here you can enter a list of users who you want added to your
+<command>/notify</command> list at startup. You will be alerted when each of
+the users in the list is online.</para>
+</sect2>
+
+</sect1>
+
+<sect1 id="colors-tab">
+<title>The <guilabel>Colors</guilabel> Tab</title>
+<para>
+Here you can configure &ksirc;'s use of color.
+</para>
+
+<sect2 id="chat-colors">
+<title><guilabel>Chat colors</guilabel> Configuration</title>
+<para>
+Here you can set colors for each of the types of text. Clicking the color
+button to the right of each text type's label will display a color selector box
+in which you can choose the color in which you want this type of text to be
+displayed.
+</para>
+</sect2>
+
+<sect2 id="nick-colors">
+<title><guilabel>Nick Colors</guilabel></title>
+<para>
+<guilabel>Foreground:</guilabel> and <guilabel>Background:</guilabel> set the
+colors for the foreground and background of users' nicknames in chat
+windows. The <guilabel>Color for messages containing your nick:</guilabel>
+option sets the color for any message appearing in a channel which contains
+your nickname.</para>
+</sect2>
+
+<sect2 id="color-codes">
+<title><guilabel>Color codes</guilabel></title>
+<para>
+Here you can set whether &ksirc; allows native color codes (see <xref
+linkend="sectcolors"/>) and also whether &mirc; color codes are allowed.</para>
+</sect2>
+
+</sect1>
+
+<!--<sect1 id="irc-colors-tab">
+<title>The <guilabel>IRC Colors</guilabel> Tab</title>
+<para>
+</para>
+</sect1>-->
+
+<sect1 id="user-menu-tab">
+<title>The <guilabel>User Menu</guilabel> Tab</title>
+<para>This page allows configuration for the RMB (Right Mouse Button) menu for the nick list located on the right in each channel. When you right click on a nickname, some actions are defined by default. These are the actions listed on the left. You can change these actions order, delete some and add others.
+</para>
+</sect1>
+
+<sect1 id="server-channel-tab">
+<title>The <guilabel>Server/Channel</guilabel> Tab</title>
+<para>Here are listed the servers and channels you join to using the New Server (<keycap>F2</keycap>) and New Channel (<keycombo action="simul">&Ctrl;<keycap>N</keycap></keycombo>) dialogs via the Channel menu.
+</para>
+<sect2 id="server-config">
+<title>Server</title>
+<para>You add there the servers you want to use. Write the server name in the field and click the Add Server to List button. If you want to delete a server from the list, you click on the server name and then on the Delete Server from List button.</para>
+</sect2>
+<sect2 id="channels-config">
+<title>Channels</title>
+<para>You add there the channels you want to connect to. Write the channel name in the field and click the Add Channel to List button. If you want to delete a channel from the list, you click on the channel name and then on the Delete Channel from List button.</para>
+</sect2>
+</sect1>
+
+<sect1 id="auto-connect-tab">
+<title>The <guilabel>Auto Connect</guilabel> Tab</title>
+<para>
+</para>
+<sect2 id="auto-connect-setup">
+<title>Auto Connect Setup</title>
+<para>You set here the server and channels names that you want to connect directly when KSirc starts.</para>
+<para>You need to use the Auto Connect Setup dialog first to add new channels. You enter there the server name. The server port is set by default for most servers. If you are not sure, just leave it as it is. Usually servers don't ask for passwords so if you don't have a password, leave the Password field empty. Then add a channel name and click the Add button. The channel will be displayed in the Auto Connect List.</para>
+<para>
+If you want to add more channel for a same server, click on the server in the Auto Connect List and the server name will be displayed in the Auto Connect Setup dialog. Write the channel name in the Channel field and click on Update/Add.
+</para>
+<para>
+In order to remove a channel form the Auto Connect List, click on the channel name in the list and then click the Delete button.
+</para>
+</sect2>
+</sect1>
+
+<sect1 id="fonts-tab">
+<title>The <guilabel>Fonts</guilabel> Tab</title>
+<para>
+You can change here the font for the main window. Select the font you want then click the Apply button to preview the change. Ok applies the changes and quits the dialog.
+</para>
+</sect1>
+
+<!--<sect1 id="shortcuts-tab">
+<title>The <guilabel>Shortcuts</guilabel> Tab</title>
+<para>
+</para>
+
+<sect2 id="global-shortcuts">
+<title>Global Shortcuts</title>
+<para></para>
+
+</sect2>
+<sect2 id="shortcuts-selected-action">
+<title>Shortcut for Selected Action</title>
+<para></para>
+</sect2>
+</sect1>-->
+
+</chapter>
+
+
+<chapter id="sectcolors">
+<title>&ksirc; Colors</title>
+
+<para>&ksirc; follows the color scheme used by
+&mirc; and a slight modification for more
+powerful in house use.</para>
+
+<screen>&lt;fg&gt; == foreground
+&lt;bg&gt; == background
+&lsqb;&rsqb; == optional
+</screen>
+
+<sect1 id="mirccompatibility">
+<title>&mirc; compatibility</title>
+
+<para>Format:</para>
+<itemizedlist>
+<listitem>
+<para><userinput>0x03&lt;fg&gt;&lsqb;,&lt;bg&gt;&rsqb;</userinput>
+sets the foreground and background</para>
+</listitem>
+<listitem>
+<para><userinput>0x03</userinput>
+resets to defaults for <emphasis>that</emphasis> line</para>
+</listitem>
+</itemizedlist>
+</sect1>
+
+<sect1 id="ksircnative">
+<title>&ksirc; native</title>
+
+<para>Format:</para>
+
+<itemizedlist>
+<listitem>
+<para><userinput>&tilde;&lt;fg&gt;&lsqb;,&lt;bg&gt;&rsqb;</userinput>
+sets the foreground and background</para>
+</listitem>
+<listitem>
+<para><userinput>&tilde;c</userinput> resets to defaults</para>
+</listitem>
+<listitem>
+<para><userinput>&tilde;b</userinput> sets bold font</para>
+</listitem>
+<listitem>
+<para><userinput>&tilde;u</userinput> sets underline</para>
+</listitem>
+<listitem>
+<para><userinput>&tilde;i</userinput> sets italics</para>
+</listitem>
+<listitem>
+<para><userinput>&tilde;r</userinput> sets reverse video</para>
+</listitem>
+</itemizedlist>
+
+<para>Why did I change &ksirc; to use &tilde; instead of 0x03
+(<keycombo action="simul">&Ctrl;<keycap>C</keycap></keycombo>)?
+Well, it's hard to use 0x03 in scripts and not all C functions seem to
+like it. <userinput>&tilde;<replaceable>letter</replaceable></userinput>
+also allows more commands while not stomping on &mirc;'s future changes.</para>
+
+</sect1>
+
+<sect1 id="colornumbers">
+<title>Color Numbers</title>
+
+<orderedlist>
+<listitem>
+<para> white</para>
+</listitem>
+<listitem>
+<para> black</para>
+</listitem>
+<listitem>
+<para> blue</para>
+</listitem>
+<listitem>
+<para> green</para>
+</listitem>
+<listitem>
+<para> red</para>
+</listitem>
+<listitem>
+<para> brown</para>
+</listitem>
+<listitem>
+<para> purple</para>
+</listitem>
+<listitem>
+<para> orange</para>
+</listitem>
+<listitem>
+<para> yellow</para>
+</listitem>
+<listitem>
+<para> lightGreen</para>
+</listitem>
+<listitem>
+<para> cyan</para>
+</listitem>
+<listitem>
+<para> lightCyan</para>
+</listitem>
+<listitem>
+<para> lightBlue</para>
+</listitem>
+<listitem>
+<para> pink</para>
+</listitem>
+<listitem>
+<para> gray</para>
+</listitem>
+<listitem>
+<para> lightGray</para>
+</listitem>
+</orderedlist>
+</sect1>
+
+<sect1 id="sendingboldunderlinereverseandcolor">
+<title>Sending Bold, Underline, Reverse, and Color</title>
+
+<para>You can use the following key combinations to insert control codes
+in text:</para>
+
+<itemizedlist>
+<listitem>
+<para><keycombo action="simul">&Ctrl;<keycap>B</keycap></keycombo> for
+bold text</para>
+</listitem>
+<listitem>
+<para><keycombo action="simul">&Ctrl;<keycap>U</keycap></keycombo> for
+underlined text</para>
+</listitem>
+<listitem>
+<para><keycombo action="simul">&Ctrl;<keycap>R</keycap></keycombo> for
+reverse text</para>
+</listitem>
+<listitem>
+<para><keycombo action="simul">&Ctrl;<keycap>K</keycap></keycombo> for
+colored text</para>
+</listitem>
+<listitem>
+<para><keycombo action="simul">&Ctrl;<keycap>O</keycap></keycombo> for
+plain text</para>
+</listitem>
+</itemizedlist>
+
+</sect1>
+
+<sect1 id="examplessetcolors">
+<title>Examples</title>
+
+<para>To underline a single word in a sentence:</para>
+
+<procedure>
+<step>
+<para>Type <keycombo
+action="simul">&Ctrl;<keycap>U</keycap></keycombo></para>
+</step>
+<step>
+<para>Type in the word</para>
+</step>
+<step>
+<para>Type <keycombo action="simul">&Ctrl;<keycap>U</keycap></keycombo>
+again</para>
+</step>
+</procedure>
+
+<para>Only the text that is enclosed by the start and end codes will be
+affected. You can use this method with all of the other control
+codes.</para>
+
+<para>The <keycombo action="simul">&Ctrl;<keycap>K</keycap></keycombo>
+control code is slightly different because it allows you to specify a
+color number. To color a single word in a sentence:</para>
+
+<procedure>
+<step>
+<para>Type <keycombo
+action="simul">&Ctrl;<keycap>K</keycap></keycombo></para>
+</step>
+<step>
+<para>Type a number between 0 and 15</para>
+</step>
+<step>
+<para>Type the word</para>
+</step>
+<step>
+<para>Type <keycombo
+action="simul">&Ctrl;<keycap>K</keycap></keycombo> again</para>
+</step>
+</procedure>
+
+<para>If you also want to change the background color of a word, you
+would need to type two numbers separated by a comma instead of just one
+number. The first number is the text color, the second number is the
+background color. The colors range from 0 to 15, the index is in the
+previous section.</para>
+
+<para>You can enclose text in multiple control codes, so for example you
+could have a bold, underlined, and colored word.</para>
+
+</sect1>
+</chapter>
+
+<chapter id="filters">
+<title>Filters</title>
+
+<sect1 id="filterrulesandhowtomakethem">
+<title>Filter Rules and How to Make them</title>
+
+<para>If you just can't figure it out, wait. I want to build a nice
+<quote>filter builder</quote> where you can just click your way through
+it. Though, it might be a while.</para>
+
+<para>The filter tries to find the <quote>match</quote> string then use
+the <quote>From</quote> and <quote>To</quote> as a substitution. The
+match, from and to are all <application>Perl</application> regex
+expressions. Rules are evaluated in descending order. The top rule is
+evaluated first, then the second from the top, &etc; All strings are
+evaluated as:</para>
+
+<para><varname>$<replaceable>name</replaceable></varname> is expanded to
+the environment variable
+<varname><replaceable>name</replaceable></varname>. This is done
+immediately when you insert the rule, and will not change after that
+time. Therefore it's probably of limited value.</para>
+
+<para><varname>$$<replaceable>name</replaceable></varname> is
+substituted with the <acronym>Perl</acronym>
+<varname>$<replaceable>name</replaceable></varname> variable during the
+match. This can be substrings such as <varname>$1</varname>,
+<varname>$2</varname> in the substitution, or normal variables available
+under <application>sirc</application> (such as <varname>$nick</varname>,
+<varname>$channel</varname>, &etc;).</para>
+
+<para><varname>&tilde;<replaceable>name</replaceable>&tilde;</varname>
+<emphasis>prepended once and only once</emphasis> to the line will send
+the line of text to the window called <replaceable>name</replaceable>.
+If the window does not exist it will go to the last window which had
+focus. There are several special windows, all prefixed by a single
+<literal>!</literal>:</para>
+
+<para><variablelist>
+<varlistentry>
+<term><varname>!default</varname></term>
+<listitem>
+<para>The current default window. Guaranteed to exist.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><varname>!all</varname></term>
+<listitem>
+<para>Send to every window. This might not show up on all windows,
+depending on how the text is parsed. For example, channel windows won't
+show a <command>/part</command> or <command>/quit</command> unless the nickname is on the channel.</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term><varname>!discard</varname></term>
+<listitem>
+<para>discards the text.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+</para>
+
+<para>The rest of the expression is dealt with as normal
+<acronym>Perl</acronym> regex. A
+good understanding of the <command>perlre</command> man page will
+definitely help, but a basic understanding of regex is most certainly
+required.</para>
+
+</sect1>
+
+<sect1 id="examplesfilters">
+<title>Examples:</title>
+
+<orderedlist><listitem>
+<para>Want to convert all boren from boren to BoreN</para>
+
+<screen>Match: .*
+From: boren
+To: BoreN</screen>
+
+<para>Pretty straight forward, match anything, then substitute boren
+with BoreN.</para>
+
+</listitem>
+<listitem>
+<para>You want to match everything with boren in it and send to the
+window called <quote>boren</quote></para>
+
+<screen>Match: boren
+From: &circ;
+To: &tilde;boren&tilde;</screen>
+
+<para>Looks for <quote>boren</quote> if found, substitutes the beginning
+of the string (&circ;) with &tilde;boren&tilde;.</para>
+
+</listitem>
+<listitem>
+
+<para>Though the previous example works, if the string already has
+<literal>&tilde;somewindow&tilde;</literal> on it, you'll now have two
+<literal>&tilde;boren&tilde;&tilde;somewindow&tilde;...</literal> So you
+can do this instead.</para>
+
+<screen>Match: boren
+From: ^(?:&tilde;\S+&tilde;)
+To: &tilde;boren&tilde;</screen>
+
+<para>Ok, the from line is a little bit more complicated. It means: match
+0 or 1 copies of <literal>&tilde;\S+&tilde;</literal>. Which is 1
+tilde, one or more non-whitespaces, and then another tilde. The
+paranoid might do <literal>(*:&tilde;\S+&tilde;)</literal>, which means:
+match 0 or more channel directives in case prior rules are
+broken.</para>
+
+</listitem>
+
+<listitem>
+<para>Server kill messages tend to be long, ugly, annoying, &etc; A basic
+message on dalnet looks like:</para>
+
+<screen>*** Notice -- Received KILL message for
+BOBO!ANDY@line82-basel.datacomm.ch from NickServ
+Path: empire.ny.us.dal.net[209.51.168.14]!trapdoor.ca.us.dal.net
+[206.86.127.252]!caris.ca.us.dal.net[208.1.222.221]
+!services.dal.net[2008.1.222.222]!services.dal.net
+(NickServ Enforcement)</screen>
+
+<para>When you're <literal>+s</literal> you get tons of them; you don't
+want all of them flying across your screen. I'm going to make 3 rules
+to deal with them one bit at a time. You could do it in less rules, but
+it'll show you the basic rule structure, in nice steps, and how to use
+multiple rules to parse a message. The first step is to remove the
+Path: portion of the message:</para>
+
+<screen>Match: ^\*\*\*.* KILL message for.*
+From: Path: \S+
+To: .</screen>
+
+
+<para>Match looks for the message starting with <literal>***</literal>,
+the <literal>*</literal>'s have to be quoted with <literal>\</literal>
+since by themselves they mean 0 or more of the prior character.
+<literal>.*</literal> means: match anything until you find <literal>
+KILL message for</literal>. This allows us to avoid typing in
+<literal>-- Received...</literal> &etc; The trailing
+<literal>.*</literal> means: match anything to the end of the line. (not
+needed, I think)</para>
+
+<para>The From line means: substitute " Path: " and any
+non-whitespace characters with the To. To is a <literal>"."</literal>
+therefore the entire path turns into a single period.</para>
+
+<para>The message now looks like:</para>
+
+<screen>*** Notice -- Received KILL message for BOBO!ANDY@line82-basel.datacomm.ch
+from NickServ. (NickServ Enforcement)</screen>
+
+<para>Notice the new <literal>"."</literal> after
+<literal>NickServ</literal>?</para>
+</listitem>
+
+<listitem>
+<para>Ok, the message is a lot cleaner, but KILLs from nickserv aren't
+really that important, so let's forward them to the
+<literal>!discard</literal> window.</para>
+
+<screen>Match: ^\*\*\*.*KILL message.*\(NickServ Enforcement\)
+From: ^(?:&tilde;\S+&tilde;)
+To: &tilde;!discard&tilde;</screen>
+
+<para>Match rule searches for the KILL message and makes sure it's from
+NickServ. Notice the <literal>\(</literal> and <literal>\)</literal>
+are both used in regex, therefore we have to quote
+them. This is very similar to what we said two examples before.</para>
+
+</listitem>
+<listitem>
+<para>We've now filtered out all the nickserv kills, but the message is
+still pretty hard to read by simply glancing at it. So let's reorder it
+to something like:</para>
+
+<screen>*** [KILL] <replaceable>KILLER</replaceable>; killed <replaceable>KILLED</replaceable>; (<replaceable>REASON</replaceable>)</screen>
+
+<screen>Match: \*\*\*.*KILL message
+From: \*\*\*.*for (.*?) from (.*?)\. \((.*?)\).*
+To: *** [KILL] $$2 killed $$1 ($$3)
+</screen>
+
+<para>Ok, the match looks for
+<literal>***<replaceable>something</replaceable> KILL message</literal>.
+We can't use <literal>&circ;</literal> since we may have just appended
+<literal>&tilde;&lt;window&gt;&tilde;</literal>.</para>
+
+<para>The from line gets a little more interesting. The <literal>"for
+(.*?) "</literal> looks for the word "for" then some text.
+<literal>.*?</literal> means: match zero or more of anything except
+newline, but isn't greedy. The rule is to stop when the first terminating condition is
+found, not the last. In other words it matches anything until a space is encountered.
+The surrounding <literal>()</literal> means: save the contents. Each
+() saves the matched data in <literal>$&num;</literal> where
+<literal>&num;</literal> starts at 1 for the first substring, &etc; In
+this case, <varname>$1</varname> gets the nick/user-info of the person
+killed. <varname>$2</varname> is then filled with the name of the
+killer. Between the <literal>()</literal> we have the reason for the
+kill. Here the <literal>(</literal> and <literal>\(</literal> get a
+little confusing. Remember <literal>\(</literal> matches the actual
+character <literal>'('</literal>.</para>
+
+</listitem>
+<listitem>
+<para>How to colorize your life.</para>
+
+<para>Ok, you want to add some color to
+&ksirc;. See the <link linkend="sectcolors">Colors section</link> for
+color info, but here's a filter rule to highlight the nickname between
+<literal>&lt;NICK&gt;</literal> on each line: </para>
+
+<screen>Match: ^(?:&tilde;\S+&tilde;)&lt;\S+&gt;
+From: &lt;(\S+)&gt;
+To: &lt;&tilde;4$$1&tilde;c&gt;</screen>
+
+<para>Takes the nickname and adds color &num;4 between the two
+<literal>&lt;&gt;</literal>. <literal>&tilde;c</literal> clears the color.</para>
+
+</listitem>
+</orderedlist>
+
+</sect1>
+</chapter>
+
+<chapter id="keys">
+<title>Keys</title>
+
+<para>This is a listing of the shortcut and command keys available
+under &ksirc;.</para>
+
+<table>
+<title>Keybindings</title>
+<tgroup cols="2">
+<thead>
+<row>
+<entry>Keybinding</entry>
+<entry>Action</entry>
+</row>
+</thead>
+
+<tbody>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>N</keycap></keycombo></entry>
+<entry>Open a new Channel/Query Window.</entry>
+</row>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>Q</keycap></keycombo></entry>
+<entry>Quit</entry>
+</row>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>Enter</keycap></keycombo></entry>
+<entry>Begin a message to the most recent person to send you a
+message.</entry>
+</row>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;&Shift;<keycap>Enter</keycap></keycombo></entry>
+<entry>Begin a message to the last-but-one person to send you a
+message.</entry>
+</row>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>K</keycap></keycombo></entry>
+<entry>Start color code with a number. Text you type after this will be
+colored.</entry>
+</row>
+
+<row>
+<entry><keycombo
+action="simul">&Ctrl;<keycap>U</keycap></keycombo></entry>
+<entry>Begin or end underlining text. Text after the first occurrence
+of this will be underlined until you type the shortcut again.</entry>
+</row>
+
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>I</keycap></keycombo></entry>
+<entry>Begin or end italic text. Text after the first occurrence
+of this will be italic until you type the shortcut again.</entry>
+</row>
+
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>B</keycap>
+</keycombo></entry>
+<entry>Begin or end bold text. Text after the first occurrence
+of this will be bold until you type the shortcut again.</entry>
+</row>
+
+<row>
+<entry><keycombo action="simul">&Ctrl;<keycap>R</keycap></keycombo></entry>
+<entry>Begin or end reverse video text. Text after the first occurrence
+of this will be reversed until you type the shortcut again.</entry>
+</row>
+
+<row>
+<entry><keycap>TAB</keycap></entry>
+<entry>Tries to complete the nickname of someone on the channel, based
+on the text you entered. You are probably familiar with this from your
+commandline shell, although it does not necessarily work in exactly the same
+way. See <xref linkend="nick-completion"/>.</entry>
+</row>
+
+</tbody>
+</tgroup>
+</table>
+
+</chapter>
+
+<!-- annma: chapter title not good-->
+<chapter id="tips">
+<title>Tips</title>
+
+<sect1 id="kde-channels">
+<title>Joining &kde; channels</title>
+
+<para>
+&kde; &irc; channel are hosted on the Freenode network. Use the <guilabel>Server/Channel</guilabel> tab or the
+<guilabel>Auto Connect</guilabel> tab in <guilabel>Configure KSirc</guilabel> to add these channels. The network
+should be <userinput><systemitem class="domainname">irc.freenode.org</systemitem></userinput> on port <userinput>6667</userinput>. Below are listed the most
+important &kde; channels and their goals. Choose the channels you are
+interested in.
+</para>
+
+<para>You can find some tips about asking questions on &kde; channels on
+the <ulink
+url="http://wiki.kde.org/tiki-index.php?page=Asking+Questions">&kde;
+community wiki</ulink>.
+</para>
+
+<table>
+ <title>KDE IRC channels</title>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Channel</entry>
+ <entry>Target</entry>
+ </row>
+ </thead>
+<tbody>
+ <row>
+ <entry>#kde</entry>
+ <entry>User help</entry>
+ </row>
+ <row>
+ <entry>#kde-devel</entry>
+ <entry>&kde; development</entry>
+ </row>
+ <row>
+ <entry>#kdevelop</entry>
+ <entry>KDevelop questions</entry>
+ </row>
+ <row>
+ <entry>#kontact</entry>
+ <entry><application>Kontact</application> (&kmail;, &korganizer;, &knotes;, ...) related questions</entry>
+ </row>
+ <row>
+ <entry>#amarok</entry>
+ <entry>Community channel about <application>amaroK</application>: user questions, development</entry>
+ </row>
+ <row>
+ <entry>#debian-kde</entry>
+ <entry>Debian KDE related questions</entry>
+ </row>
+ <row>
+ <entry>#kde-freebsd</entry>
+ <entry>FreeBSD KDE related questions</entry>
+ </row>
+</tbody>
+</tgroup>
+</table>
+</sect1>
+
+<sect1 id="autoidentify">
+<title>Auto identify</title>
+
+<para>If you intend to use &irc; on a regular basis you will probably
+want to register your nickname. This protects your nickname so that
+only you can use it. On DALnet, after you log on, you register your
+nickname using the command <userinput><command>/msg</command>
+nickserv register <replaceable>PASSWORD</replaceable></userinput>,
+where <replaceable>PASSWORD</replaceable> is your password. You'll get a
+confirmation your nickname has been registered.
+</para>
+
+<para>
+Then each time you log on to DALnet you get a message asking you to
+identify yourself. You will then need to type
+<userinput><command>/msg nickserv identify
+<replaceable>PASSWORD</replaceable></command></userinput>.
+</para>
+
+<para>
+&ksirc; can identify you automatically when you log into any
+channel. You simply need to add the following Perl script in your home
+directory:
+</para>
+
+<para>
+The following script will work on Freenode. This script should be
+saved as <filename>.sircrc.pl</filename> in your home
+directory. When you run &ksirc;, this script will automatically have
+you identified.
+</para>
+
+<programlisting>
+sub hook_nick_notice {
+my $n = shift;
+my $m = shift;
+
+
+if($who =~ /NickServ/){
+if($m =~ /If this is your nickname/){
+$silent = 1;
+&amp;msg("nickserv", "identify <replaceable>XXXXXX</replaceable>");
+$silent = 0;
+}
+}
+}
+&amp;addhook("notice", "nick_notice"); # join on the "end of MOTD" numeric
+</programlisting>
+<para>
+Replace <replaceable>XXXXXX</replaceable> with your password.
+</para>
+
+<para>
+The above script will work on other networks than Freenode where
+nickserv sends you the request "identify" but on some networks like
+Undernet you will need the following script:
+</para>
+
+<programlisting>
+sub hook_connected {
+if($server =~ /undernet.org$/){
+ &amp;msg("X\@channels.undernet.org", "login <replaceable>YOUR_NAME</replaceable> <replaceable>XXXX</replaceable>");
+ &amp;docommand("umode +x");
+}
+}
+&amp;addhook("255", "connected"); # join on the "end of MOTD" numeric
+</programlisting>
+
+<para>
+Replace <replaceable>YOUR_NAME</replaceable> with your login name and
+<replaceable>XXXXXX</replaceable> with your password.
+</para>
+
+<para>
+ You can find more about SIRC programming on this page: <ulink
+ url="http://www.iagora.com/~espel/sirc/PROGRAMMING">http://www.iagora.com/~espel/sirc/PROGRAMMING</ulink>
+</para>
+</sect1>
+</chapter>
+<!-- end annma -->
+
+<chapter id="credits-and-license">
+<title>Credits and Licenses</title>
+
+<para>&ksirc; copyright 1997-2002, the &ksirc; developers.</para>
+
+<para>Portions of documentation copyright 1997, &Andrew.Stanley-Jones;</para>
+
+<para>Documentation updated for &kde; 3.0 by &Philip.Rodrigues;
+&Philip.Rodrigues.mail;.</para>
+
+<para><xref linkend="tips"/> was written by &Anne-Marie.Mahfouf; &Anne-Marie.Mahfouf.mail;.</para>
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="getting-ksirc">
+<title>Where to get &ksirc;</title>
+
+&install.intro.documentation;
+
+</sect1>
+
+
+<sect1 id="compilation">
+<title>Compilation and Installation</title>
+
+&install.compile.documentation;
+
+</sect1>
+</appendix>
+
+&documentation.index;
+
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-omittag: nil
+sgml-shorttag: t
+sgml-indent-step: nil
+End:
+-->
+
+
+
+
diff --git a/doc/ktalkd/Makefile.am b/doc/ktalkd/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/ktalkd/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/ktalkd/index.docbook b/doc/ktalkd/index.docbook
new file mode 100644
index 00000000..7cfbbb6f
--- /dev/null
+++ b/doc/ktalkd/index.docbook
@@ -0,0 +1,608 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kappname "&ktalkd;">
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE" > <!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+<title>The &ktalkd; Handbook</title>
+<authorgroup>
+<author>
+<firstname>David</firstname>
+<surname>Faure</surname>
+<affiliation>
+<address><email>faure@kde.org</email></address>
+</affiliation>
+</author>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+</authorgroup>
+
+<legalnotice>
+&FDLNotice;
+</legalnotice>
+
+<copyright>
+<year>2001</year>
+<holder>David Faure</holder>
+</copyright>
+
+<date>2001-05-02</date>
+<releaseinfo>1.05.02</releaseinfo>
+
+<abstract>
+<para>
+&ktalkd; is an enhanced <command>talk</command> daemon - a program
+to handle incoming <command>talk</command> requests, announce them and
+allow you to respond to it using a talk client.
+</para>
+
+</abstract>
+<keywordset>
+<keyword>KTALKD</keyword>
+<keyword>talk</keyword>
+<keyword>talkd</keyword>
+<keyword>otalk</keyword>
+<keyword>ntalk</keyword>
+<keyword>ktalkdlg</keyword>
+<keyword>kcmktalkd</keyword>
+</keywordset>
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>
+&ktalkd; is an enhanced <command>talk</command> daemon - a program to
+handle incoming <command>talk</command> requests, announce them and
+allow you to respond to it using a <command>talk</command> client.
+</para>
+
+<important>
+<para>
+Note that &ktalkd; is designed to run on a single-user workstation, and
+shouldn't be run on a multi-user machine: since it reads users'
+configuration files, users can get the <command>talk</command> daemon to
+run any command, which is particularly dangerous. Do not use &ktalkd; if
+you create accounts on your machine, to people you don't fully trust.
+</para>
+</important>
+
+<para>
+In this document, if somebody wants to talk to you, you are designated
+as the <quote>callee</quote>.
+</para>
+
+<para>&ktalkd; has the following features :</para>
+
+<variablelist>
+<varlistentry>
+<term>Answering machine</term>
+<listitem>
+<para>
+If the callee isn't logged on, or doesn't answer after
+the second announcement, an answering machine is launched, takes the
+message, and mails it to the callee.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>Sound</term>
+<listitem>
+<para>
+If desired, a sound is played with the announcement.
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>
+X Announce
+</term>
+<listitem>
+<para>
+If compiled with &kde; installed, &ktalkd; will use
+<command>ktalkdlg</command>, a &kde; dialog, for announcement. If
+&ktalk; is running, it will be asked to make the announcement
+itself. (New since 0.8.8).
+</para>
+</listitem>
+</varlistentry>
+<varlistentry>
+<term>Multiple displays announcement</term>
+<listitem>
+<para>
+If you are logged remotely (&eg; with an
+<userinput><command>export</command>
+<envar>DISPLAY</envar>=<replaceable>...</replaceable></userinput>
+command), the X announcement will be made on this display too. Answer on
+the one you want! If you're also logged in a text terminal, and if
+you're <emphasis>not</emphasis> using xterms (internal restriction),
+then you'll see a text announcement too, in case you're using the text
+terminal at the time of the announcement.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Forwarding <emphasis>(New since 0.8.0)</emphasis></term>
+<listitem>
+<para>
+You can set up a forward to another user even to another host
+if you're away. There are 3 different forwarding methods. See section
+<link linkend="usage">Usage</link>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Configuration</term>
+<listitem>
+<para>
+If &ktalkd; is compiled for &kde;, it reads config from &kde; config
+files, the sitewide
+(<filename>$<envar>KDEDIR</envar>/share/config/ktalkdrc</filename>) and
+the user one, in its home folder. The sitewide one has to be manually
+edited by the administrator, but there is now a configuration dialog for
+the user one. It's called <command>kcmktalkd</command> and can be found
+in the &kcontrol; after installing &ktalkd;. On non-&kde; systems,
+&ktalkd; will read <filename>/etc/talkd.conf</filename>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Internationalization</term>
+<listitem>
+<para>
+Under &kde;, the announcement will be in your language provided that you
+set it in the &kde; menus and that someone translated
+<command>ktalkdlg</command> to your language. The same goes for the
+configuration dialog, <command>kcmktalkd</command>.
+</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Support for <command>otalk</command> and <command>ntalk</command>
+<emphasis>(New since 0.8.1)</emphasis></term>
+<listitem>
+<para>
+&ktalkd; now supports both protocols, even when forwarding. &ktalk;
+supports both protocols as well.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>I hope you will enjoy this talk daemon,</para>
+
+<para>David Faure <email>faure@kde.org</email></para>
+
+</chapter>
+
+<chapter id="usage">
+<title>Usage</title>
+
+<para>
+To use &ktalkd;, you need a <command>talk</command> client. The
+text-based <command>talk</command> is available on most &UNIX; systems. Try <userinput><command>talk</command> <replaceable>your&lowbar;username</replaceable></userinput> to see what happens when you
+receive a <command>talk</command> request.
+</para>
+
+<para>
+You can also try the answering machine the same way: initiate a
+<command>talk</command> to yourself, ignore the announcement twice, and
+you'll see the answering machine.
+</para>
+
+<para>
+There is a <command>talk</command> client with a graphical interface for
+&kde;, &ktalk;. It's not yet shipped with &kde; packages, but you can
+find it on ftp://ftp.kde.org. It should be in <ulink
+url="ftp://ftp.kde.org/pub/kde/stable/latest/apps/network">ftp://ftp.kde.org/pub/kde/stable/latest/apps/network</ulink>
+</para>
+
+<para>
+The announcement dialog box is trivial: <guibutton>respond</guibutton>
+or <guibutton>ignore</guibutton>.
+</para>
+
+<para>
+The configuration dialog should be rather straight forward, except for
+setting up a forward to another user (or even to another host).
+</para>
+
+<sect1 id="choosing-a-forwarding-method">
+<title>Choosing a Forwarding Method</title>
+
+<para>
+None is perfect, they all have pros (+) and cons (-).
+</para>
+
+<variablelist>
+<varlistentry>
+<term><acronym>FWA</acronym> - Forward announcement only.</term>
+<listitem>
+<para>
+Direct connection. Not recommended.
+</para>
+<itemizedlist>
+<listitem>
+<para>
+(+) You know who the caller is, but
+</para>
+</listitem>
+<listitem>
+<para>
+(-) Caller will have to respond to an announcement from you. Annoying.
+</para>
+</listitem>
+<listitem>
+<para>
+(-) Don't use if you have an answering machine on your
+<quote>away</quote> location. (The answering machine can't popup an
+announcement, it would be confusing!)
+</para>
+</listitem>
+</itemizedlist>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><acronym>FWR</acronym> - Forward all requests, changing info when
+necessary</term>
+<listitem>
+<para>
+Direct connection.
+</para>
+<itemizedlist>
+<listitem>
+<para>
+(+) Caller won't know that you're away, but
+</para>
+</listitem>
+<listitem>
+<para>
+(-) You won't really know who the caller is - only his username, (so you
+might see <computeroutput>talk from
+Wintalk@my&lowbar;host</computeroutput>)
+</para>
+</listitem>
+</itemizedlist>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><acronym>FWT</acronym> - Forward all requests and take the
+ talk.</term>
+<listitem>
+<para>No direct connection.</para>
+
+<itemizedlist>
+<listitem>
+<para>
+(+) Same as above, but also works if you and caller can't be in
+direct contact one with the other (&eg; firewall).
+</para>
+</listitem>
+<listitem>
+<para>
+(+) You'll be told who's really talking to you when you accept the talk
+</para>
+</listitem>
+<listitem>
+<para>
+(-) But as in <acronym>FWR</acronym>, you won't know his machine name in
+the announcement
+</para>
+</listitem>
+</itemizedlist>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>
+In short, use <acronym>FWT</acronym> it you want to use it behind a
+firewall (and if &ktalkd; can access both networks), and
+<acronym>FWR</acronym> otherwise.
+</para>
+
+</sect1>
+</chapter>
+
+<chapter id="questions-and-answers">
+<title>Questions and Answers</title>
+
+<qandaset>
+<qandaentry>
+<question>
+<para>
+Why doesn't <systemitem class="username">root</systemitem> receive &kde;
+announcements?
+</para>
+</question>
+
+<answer>
+<para>
+Because this would be security hole, with the current user
+detection. You can bypass the limitation by adding two lines in
+<command>xdm</command> config files (which are the same as &kdm; ones).
+</para>
+
+<note>
+<para>
+The S.u.S.E &Linux; distribution includes those lines by default.
+</para>
+</note>
+
+<para>
+Those config files are normally in a folder such as <filename
+class="directory">/etc/X11/xdm</filename>, or <filename
+class="directory">/usr/X11R6/lib/X11/xdm</filename> on other
+systems. The following supposes that they are in <filename
+class="directory">/etc/X11/xdm</filename>, so you might have to
+translate them for another folder.</para>
+
+<para>Here is what you have to do:</para>
+
+<procedure>
+<step>
+<para>Edit the file <filename>Xstartup</filename>, or create it, (in the
+<command>xdm</command> config folder) so that it reads:
+</para>
+<screen>#!/bin/sh
+/etc/X11/xdm/GiveConsole
+sessreg -a -l $DISPLAY -x /etc/X11/xdm/Xservers $USER</screen>
+</step>
+<step>
+<para>and the file <filename>Xreset</filename> so that it reads:
+</para>
+<screen>#!/bin/sh
+/etc/X11/xdm/TakeConsole
+sessreg -d -l $DISPLAY $USER</screen>
+</step>
+<step>
+<para>
+Make sure that <filename>xdm-config</filename> make reference to those
+two files:
+</para>
+<screen>DisplayManager._0.startup: /etc/X11/xdm/Xstartup
+DisplayManager._0.reset: /etc/X11/xdm/Xreset </screen>
+</step>
+</procedure>
+
+<para>
+This will make &kdm; (or <command>xdm</command>) log
+the user into utmp, which is the right thing to do. It's not up to
+&konsole;, nor <command>xterm</command>, to log the user, but to
+<command>xdm</command> and &kdm;, in my
+opinion. However, this will not log the user as an X user when using
+<command>startx</command>... Any hint about that ?
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+Why don't I, as a normal user, receive &kde; announcements?
+</para>
+</question>
+
+<answer>
+<para>
+If you're running a &Linux; system (with <filename
+class="directory">/proc</filename> enabled), this behavior is a
+bug. Please send me a description of it so that I correct it.
+</para>
+
+<para>
+If you're running &Linux; 2.0.35, this is a known bug in the kernel,
+which doesn't let <systemitem class="username">root</systemitem> read
+<filename class="directory">/proc</filename>. The solution is the same
+as in the previous question, provided that you run
+&kdm; or <command>xdm</command> to log into X. Or
+upgrade!
+</para>
+
+<para>
+Otherwise, this is normal. &ktalkd; can't find the user, as &kde;
+doesn't log him into utmp and the &Linux; based (<filename
+class="directory">/proc</filename>) detection is disabled. The solution
+is the same as in the previous question, provided that you run
+<acronym>kdm</acronym> or <acronym>xdm</acronym> to log into X. Another
+solution is to make sure you always have an
+<application>xterm</application> running.
+</para>
+</answer>
+</qandaentry>
+
+<qandaentry>
+<question>
+<para>
+How do I get debug output from &ktalkd;?
+</para>
+</question>
+
+<answer>
+<para>
+As it is a daemon, there is no debug output on standard output. To get
+debugging output (for instance before submitting me a bug report!),
+update the lines in <filename>inetd.conf</filename> which launches
+&ktalkd; and &kotalkd; to be:
+</para>
+
+<screen>talk dgram udp wait root /usr/sbin/tcpd /opt/kde/bin/ktalkd -d
+ntalk dgram udp wait root /usr/sbin/tcpd /opt/kde/bin/ktalkd -d</screen>
+
+<para>Notice the <option>-d</option> option.
+</para>
+
+<para>
+Then edit <filename>/etc/syslog.conf</filename> to add the following
+line:
+</para>
+
+<screen>*.* /var/log/all_messages</screen>
+
+<para>To make it work, you then have to restart <command>inetd</command>
+and <command>syslogd</command>:</para>
+
+<screen><prompt>%</prompt> <userinput><command>killall</command> <option>-HUP inetd</option></userinput>
+<prompt>%</prompt> <userinput><command>killall</command> <option>-HUP syslogd</option></userinput></screen>
+
+<para>Finally, run a <command>talk</command> session and see the result
+in <filename>/var/log/all&lowbar;messages</filename> </para>
+
+<para>When submitting a bug report, never forget to include the
+debugging output, but also &ktalkd;'s version number and the
+<command>./configure</command> output. Thanks.</para>
+
+</answer>
+</qandaentry>
+</qandaset>
+
+</chapter>
+
+<chapter id="copyright-and-license">
+<title>Copyright and Licenses</title>
+
+<para>
+&ktalkd; is maintained and improved by David Faure,
+<email>faure@kde.org</email>
+</para>
+
+<para>
+The original program was written by Robert Cimrman,
+<email>cimrman3@students.zcu.cz</email>
+</para>
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<sect1 id="how-to-obtain-ktalkd">
+<title>How to obtain &ktalkd;</title>
+
+<para>
+&ktalkd; is now a core application of the &kde; project <ulink
+url="http://www.kde.org">http://www.kde.org</ulink>, part of the
+kdenetwork package.
+</para>
+
+<para>
+You can always download the latest &ktalkd; from the main &FTP; site of
+the &kde; project, <ulink
+url="ftp://ftp.kde.org/pub/kde">ftp://ftp.kde.org/pub/kde</ulink> and
+from its mirrors. It's usually found in <ulink
+url="ftp://ftp.kde.org/pub/kde/stable/latest/apps/network">ftp://ftp.kde.org/pub/kde/stable/latest/apps/network</ulink>
+</para>
+</sect1>
+
+<sect1 id="requirements">
+<title>Requirements</title>
+
+<para>
+In order to successfully compile &ktalkd;, you need the latest versions
+of the &kde; libraries as well as the &Qt; C++ library. All required
+libraries as well as ktalkd itself can be found on <ulink
+url="ftp://ftp.kde.org/pub/kde/">ftp://ftp.kde.org/pub/kde/</ulink>.
+</para>
+</sect1>
+
+<sect1 id="compilation-and-installation">
+<title>Compilation and Installation</title>
+
+<para>
+In order to compile and install &ktalkd; on your system, type the
+following in the base folder of the &ktalkd; distribution:
+</para>
+
+<screen><prompt>%</prompt> <userinput><command>./configure</command></userinput>
+<prompt>%</prompt> <userinput><command>make</command></userinput>
+<prompt>%</prompt> <userinput><command>make</command> <option>install</option></userinput></screen>
+
+<para>As &ktalkd; is a daemon, <userinput><command>make</command>
+<option>install</option></userinput> will require <systemitem
+class="username">root</systemitem> privileges.</para>
+
+<para>
+Don't forget to update <filename>/etc/inetd.conf</filename>. For
+example, on a &Linux; system, if &kde; is in <filename
+class="directory">/opt/kde</filename>, change the lines concerning
+<command>talk</command> and <command>ntalk</command> to:
+</para>
+
+<screen>talk dgram udp wait root /usr/sbin/tcpd /opt/kde/bin/ktalkd
+ntalk dgram udp wait root /usr/sbin/tcpd /opt/kde/bin/ktalkd</screen>
+
+<para>A script is provided, to make the necessary change automatically.
+Update your <filename>inetd.conf</filename> file just by running</para>
+
+<screen><prompt>%</prompt> <userinput><command>./post-install.sh</command></userinput></screen>
+
+<para>
+Anyway, you'll have to <emphasis>restart inetd</emphasis> after this.
+On most &Linux; systems, do:
+</para>
+
+<screen><prompt>%</prompt> <userinput><command>killall</command> <option>-HUP inetd</option></userinput></screen>
+
+<para>
+On newer systems, using <command>xinetd</command>, there is no more
+<filename>/etc/inetd.conf</filename>, and you should edit or create
+<filename>/etc/xinetd.d/talk</filename> instead, with those lines:
+</para>
+
+<screen>service talk
+{
+ socket_type = dgram
+ wait = yes
+ user = root
+ server = /usr/bin/ktalkd
+}
+
+service ntalk
+{
+ socket_type = dgram
+ wait = yes
+ user = root
+ server = /usr/bin/ktalkd
+}</screen>
+
+<para>
+then restart <command>xinetd</command>.
+</para>
+
+<para>
+Please inform me of any modification you had to undertake in order to
+get &ktalkd; to compile or work on your platform.
+</para>
+</sect1>
+
+</appendix>
+
+</book>
+
+<!--
+Local Variables:
+mode: sgml
+sgml-minimize-attributes:nil
+sgml-general-insert-case:lower
+sgml-indent-step:0
+sgml-indent-data:nil
+End:
+
+// vim:ts=0:sw=2:tw=78:noet
+-->
+
+
diff --git a/doc/kwifimanager/Makefile.am b/doc/kwifimanager/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/kwifimanager/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/kwifimanager/index.docbook b/doc/kwifimanager/index.docbook
new file mode 100644
index 00000000..07cc908d
--- /dev/null
+++ b/doc/kwifimanager/index.docbook
@@ -0,0 +1,549 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY kwifimanager "<application>KWiFiManager</application>">
+ <!ENTITY LAN "<acronym>LAN</acronym>">
+ <!ENTITY kappname "&kwifimanager;">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY % English "INCLUDE"> <!-- change language only here -->
+ <!ENTITY % addindex "IGNORE">
+]>
+<book lang="&language;">
+ <bookinfo>
+ <title>The &kwifimanager; Handbook</title>
+ <authorgroup>
+ <author>
+ <firstname>Stefan</firstname>
+ <surname>Winter</surname>
+ <affiliation>
+ <address>
+ <email>swinter@kde.org</email>
+ </address>
+ </affiliation>
+ </author>
+ <!-- TRANS:ROLES_OF_TRANSLATORS -->
+ </authorgroup>
+ <legalnotice>&FDLNotice;</legalnotice>
+ <copyright>
+ <year>2002-05</year>
+ <holder>Stefan Winter</holder>
+ </copyright>
+ <date>2005--05-11</date>
+ <releaseinfo>3.5.0</releaseinfo>
+ <abstract>
+ <para>The &kwifimanager; suite can be used to configure and monitor wireless &LAN;
+ cards. It consists of a stand-alone application and a module for the &kde; Control
+ Center.</para>
+ </abstract>
+ <keywordset>
+ <keyword>KDE</keyword>
+ <keyword>wireless</keyword>
+ <keyword>Wi-Fi</keyword>
+ <keyword>wlan</keyword>
+ <keyword>WEP</keyword>
+ <keyword>wlan-ng</keyword>
+ </keywordset>
+ </bookinfo>
+ <chapter id="introduction">
+ <title>Introduction</title>
+ <para> The &kwifimanager; suite is a set of tools which allows you to manage your wireless
+ &LAN; Network Interface card (PC-Card, PCI or miniPCI) under the K Desktop Environment. It
+ provides information about your current connection and lets you set up up to ten independent
+ configurations and use up to four configrations that are pre-configured by distribution-specific
+ scripts. If you are in a place where none of your preconfigured networks is available,
+ you can also dynamically switch to an available network with almost no configuration effort.
+ &kwifimanager; supports every wireless &LAN; card that uses the wireless extensions
+ interface. This includes virtually all wireless &LAN; cards that are operational at all
+ under the &Linux; operating system.</para>
+ </chapter>
+ <chapter id="using">
+ <title>Using the &kwifimanager; suite</title>
+ <sect1 id="kwifimanager-application">
+ <title>The &kwifimanager; application</title>
+ <para> Purpose of the main &kwifimanager; application is to show the currently active
+ network configuration and to display connection quality and access points.</para>
+ <para>The main application is launched by either typing<userinput>kwifimanager</userinput> at
+ the command prompt of a console window or via the K Menu, where it is located by default in
+ the<guisubmenu>Applications</guisubmenu> group. If &kwifimanager; is already running
+ but minimised to the system tray then it can be restored by clicking once on the <link
+ linkend="systrayicon">system tray icon</link>. If there is more than one wireless &LAN;
+ card in your system, just open more than one instance of &kwifimanager;: every instance
+ will show information about a different card automatically.
+ The &GUI; elements of the application
+ are explained in the following subsections.</para>
+ <sect2 id="kwifimanager-main">
+ <title>Main window</title>
+ <para> The &kwifimanager; main window consists of five parts:</para>
+ <sect3 id="signalquality">
+ <title>Signal quality display</title>
+ <para> Here you can see the quality and type of the active connection. The uppermost icon
+ displays the general state of the wireless network via a set of pictograms:<itemizedlist>
+ <listitem>
+ <para> a wireless &LAN; card with a question tag means that no card was detected
+ or its state could not be determined</para>
+ </listitem>
+ <listitem>
+ <para> a single laptop means that a wireless &LAN; card is inserted and in
+ Infrastructure mode, but there is no radio signal from access-points. The card is
+ out of range and can not communicate to the infrastructure network.</para>
+ </listitem>
+ <listitem>
+ <para> a laptop that is connected to an access point means that a connection to an
+ access point is established.</para>
+ </listitem>
+ <listitem>
+ <para> two laptops mean that your system is in Ad-Hoc mode without access points. It
+ may or may not have established a Peer-to-Peer connection.</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para> Below these pictograms is a small quality meter. It displays, in a cellular-like
+ manner, the quality level of the current connection. This information is only available
+ in Infrastructure mode. In Ad-Hoc mode, the level is always 0.</para>
+ <para> This graphical information is supplemented by an integer value below the icon. It
+ shows the signal quality, and is computed in one of two ways: <itemizedlist>
+ <listitem>
+ <para>a directly reported value from the card if the card supports
+ <quote>Quality</quote> reporting</para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>(signal strength in dBm) - (noise level in dBm)</literal> for cards that
+ do not support that.</para>
+ </listitem>
+ </itemizedlist> You can manually change the method used by turning
+ <guimenu>File</guimenu>, <guimenuitem>Use alternate strength calculation</guimenuitem>
+ on or off. Turning the option on means to use the second method. If your card is out of
+ range, the value is 0; if no card is inserted or your card is in Ad-Hoc mode it will
+ show N/A.</para>
+ </sect3>
+ <sect3 id="speed">
+ <title>Connection speed</title>
+ <para> An indicator for the current connection speed is shown at the right-hand side of
+ the main window above the configuration info. If the speed settings are set to AUTO, the
+ value will change once in a while as the card adjusts the connection speed according to
+ the signal quality. The scale of the bar graph will automatically adjust to up to 108
+ MBit/s when the current connection speed exceeds 11 MBit/s.</para>
+ </sect3>
+ <sect3 id="current-config">
+ <title>Current configuration</title>
+ <para>Here you can find information about your card configuration. It displays the
+ following information:</para>
+ <itemizedlist>
+ <listitem>
+ <para> the network with which the card is connected to / tries to connect to
+ (<guilabel>Searching for network:</guilabel> or <guilabel>Connected to
+ network:</guilabel>)</para>
+ </listitem>
+ <listitem>
+ <para> the <acronym>MAC</acronym> address of the access point to which the card is
+ connected.</para>
+ <para> If the card is in Infrastructure mode but out of range, an appropriate warning
+ (<computeroutput>- no access point -</computeroutput>) is displayed to indicate
+ that no connection is established.</para>
+ <para> In Ad-Hoc mode, the field shows an address that is associated with one of the
+ cards in the Ad-Hoc network. It displays a MAC address that has a non-global scope:
+ its second bit is set to 1, which often results in a prefix of <quote>02:</quote>
+ instead of <quote>00:</quote>. Many people think this is an error, but in fact it is
+ done on purpose to show that the cell you are connected to is not an actual physical
+ device, but rather an imaginary access point without a real physical address.</para>
+ <informalexample>
+ <para> Your card is the first card that enters Ad-Hoc mode with a given SSID. Then all other cards
+ entering Ad-Hoc mode with the same SSID will see your MAC-address, slightly modified: instead
+ of<computeroutput>00:xx:yy:zz:aa:bb</computeroutput> it will
+ show<computeroutput>02:xx:yy:zz:aa:bb</computeroutput>. This behavior is
+ intentional.</para>
+ </informalexample>
+ </listitem>
+ <listitem>
+ <para> on most cards (those that have the capability to report it), the frequency on
+ which the card is transmitting data and the corresponding channel number is
+ displayed.</para>
+ </listitem>
+ <listitem>
+ <para> your local IP (version 4) address, if available. If no address could be
+ retrieved from the networking subsystem, the word <guilabel>unavailable</guilabel>
+ is displayed.</para>
+ </listitem>
+ <listitem>
+ <para> encryption status (only if you have started &kwifimanager; as<systemitem
+ class="username">root</systemitem>). The display will only show
+ <guilabel>off</guilabel> or <guilabel>active</guilabel>, but never the real key.
+ This is intentional in order to not reveal the <acronym>WEP</acronym> key to people
+ passing by the users screen.</para>
+ </listitem>
+ </itemizedlist>
+ </sect3>
+ <sect3 id="ap-info">
+ <title>Access Point information (bottom area)</title>
+ <para> The last line of the main window shows information about your AccessPoint. This
+ requires that your system administrator provided a list of MAC addresses with a
+ corresponding information. An example for such a list can be found
+ in<filename>$<envar>KDEDIR</envar>/share/apps/kwifimanager/locations/DE_BW_Karlsruhe_University.loc</filename>
+ </para>
+ <para>If you want to set up a new list, simply create a file in the same format and copy
+ it into the folder <filename class="directory"
+ >$<envar>KDEDIR</envar>/share/apps/kwifimanager/locations/</filename>
+ </para>
+ <para>It will be automatically parsed at the next start of &kwifimanager;. If you have
+ a list and want to have it included in future releases of &kwifimanager;, simply
+ send it to the author or current maintainer.</para>
+ </sect3>
+ <sect3 id="scanning">
+ <title>Information about available networks</title>
+ <para> The lower-left area of the main window contains a button named <guibutton>Scan for
+ networks...</guibutton>. If you click on this button, &kwifimanager; will attempt
+ to retrieve a list of all networks that are in range of your card. The outcome of this
+ scan depends on two factors: <itemizedlist>
+ <listitem>
+ <para>the overall ability of your card and driver to perform network scans</para>
+ </listitem>
+ <listitem>
+ <para>if you have root permissions or not</para>
+ </listitem>
+ </itemizedlist> If your card or driver arent able to scan the network, your scanning
+ results will always be empty. If you are not the root user, the list may be incomplete
+ or outdated. </para>
+ <para> In order to receive a reliable, current list of access points you will need to
+ start the scan with root privileges, for example by using the &kdesu; utility to
+ start &kwifimanager;</para>
+ <para> If at least one network was found, you are presented with a table showing details
+ of the network. It has four columns that inform you about <itemizedlist>
+ <listitem>
+ <para>the network name (or the string <guilabel>(hidden cell)</guilabel> if the name
+ is not disclosed by the access point during the scan)</para>
+ </listitem>
+ <listitem>
+ <para>the type (whether is a <guilabel>Managed</guilabel> or an
+ <guilabel>Ad-Hoc</guilabel> network)</para>
+ </listitem>
+ <listitem>
+ <para>the signal strength of the network</para>
+ </listitem>
+ <listitem>
+ <para>and whether or not <acronym>WEP</acronym> encryption is used</para>
+ </listitem>
+ </itemizedlist> In case of an active <acronym>WEP</acronym> encryption, you can click on
+ that column and enter the network key. &kwifimanager; will automatically try to
+ guess if the key is a hexadecimal number or a string. </para>
+ <para> If the network information for the highlighted network is complete (&ie; all
+ columns contain meaningful information), you can use the button <guibutton>Switch to
+ network</guibutton> to enter the selected network. If &kwifimanager; has no root
+ privileges, you will be prompted with a password prompt to enter the root password in
+ order to change the network. </para>
+ <para> Clicking on <guibutton>Close</guibutton> dismisses the network information screen
+ without changes to the existing settings. </para>
+ </sect3>
+ </sect2>
+ <sect2 id="statistics">
+ <title>Statistics Viewer</title>
+ <para> Optionally, by selecting <guimenuitem>Connection statistics</guimenuitem> in the
+ <guimenu>File</guimenu> menu, a separate window can be shown which displays the signal
+ level and noise level graphs of the last 240 seconds. The signal level is displayed in
+ blue and the noise level in red. The difference (SIGNAL minus NOISE) is the connection
+ quality which is displayed in the main window.</para>
+ <para>Some cards do not report meaningful noise information. If this is the case for your
+ card and you get annoyed by the irrelevant red line, you can disable showing the noise
+ level in the statistics window by unselecting <menuchoice>
+ <guimenu> Config </guimenu>
+ <guimenuitem> Show noise level in statistics </guimenuitem>
+ </menuchoice> in the &kwifimanager; main window.</para>
+ </sect2>
+ <sect2 id="config-edit">
+ <title>Configuration Editor</title>
+ <para> By selecting<menuchoice>
+ <guimenu>Config</guimenu>
+ <guimenuitem>Configuration Editor</guimenuitem>
+ </menuchoice> you are taken to the <link linkend="control-center">control center
+ module</link> of &kwifimanager;. In case you are not the <systemitem class="username"
+ >root</systemitem> user, a window will pop up requesting the <systemitem
+ class="username">root</systemitem> password. This is because the configuration module
+ allows you to change network connectivity und uses <userinput>ifconfig</userinput> to make
+ changes, which requires root privileges.</para>
+ </sect2>
+ <sect2 id="misc">
+ <title>Miscellaneous</title>
+ <para>There are some minor additional features worth of being mentioned.</para>
+ <sect3 id="acoustic-scanning">
+ <title>Acoustic Scanning</title>
+ <para>First, there is a feature named <guilabel>Acoustic Scanning</guilabel>. If this
+ option is enabled, the connection quality is converted into an acoustic signal. A higher
+ signal quality leads to a higher frequency of the <quote>beep</quote> output and to a
+ more rapid beeping. If you've ever seen the Star Trek(tm) series you will see some
+ parallels to their <quote>tricorders</quote>
+ </para>
+ </sect3>
+ <sect3 id="logging">
+ <title>Network logging</title>
+ <para>A second feature is network logging. It just means that &kwifimanager; will log
+ the name of the network you are connecting to every time your network changes.
+ This option is most useful when searching for the special network name
+ <quote>any</quote>. In this mode, the card will log into any network it finds. The
+ logfile's position is <filename class="directory"
+ >$<envar>HOME</envar>/.kde/share/apps/kwifimanager/wireless-log</filename>
+ </para>
+ </sect3>
+ <sect3>
+ <title>Disabling the wireless network</title>
+ <para>You can completely disable the card by selecting the option <guimenu>File</guimenu>
+ <guimenuitem>Disable radio</guimenuitem>. Using this option will turn off the cards
+ transmitter which effectively turns it off and saves a little bit of energy. This will
+ only work for your card if it accepts changes to its
+ <computeroutput>txpower</computeroutput> property.</para>
+ </sect3>
+ </sect2>
+ </sect1>
+ <sect1 id="systrayicon">
+ <title>The system tray icon</title>
+ <para>When &kwifimanager; is launched, it installs a small icon in the system tray. The icon contains
+ parts of the information of the main window, namely the bar graph and optionally the signal strength
+ number. If you hover over the icon with the mouse for a few seconds, a tooltip will appear that
+ contains the currently connected network name. Whether or not the strength number shall be
+ shown can be configured via <guimenu>Config</guimenu>,
+ <guimenuitem>Show Strength Number in System Tray</guimenuitem>.</para>
+ <para>If you have configured &kwifimanager; to stay in the system tray when clicking on the
+ <guibutton>X</guibutton> button, the icon will stay in the tray persistently unless you really exit
+ the application by clicking on <guimenu>File</guimenu>, <guimenuitem>Quit</guimenuitem>.</para>
+ <para>You can always hide the main application to the system tray by clicking on the tray icon. Similarly,
+ to restore the main application from the tray, just click on it once.</para>
+ </sect1>
+ <sect1 id="control-center">
+ <title>The Control Center module</title>
+ <para>The configuration module in the &kcontrolcenter; is perhaps the most useful part of
+ the &kwifimanager; suite. Here you can actually change the basic settings of your
+ wireless &LAN; card. The module can manage up to ten independent configurations for the
+ card. If you dont need that many configurations, you can reduce the number of configs
+ shown at any time by changing the <guilabel>Number of Configurations</guilabel> entry.
+ If you have configured your wireless settings with a distribution-specific tool, chances are good
+ that the &kcontrolcenter; module will automatically detect this and also read in and show that
+ configuration. In any case these configurations will be read-only, because it is the distribution's
+ job to handle updating these settings and the module should not interfere with their internal magic.
+ Up to five additional preset configurations can be shown in addition to the ten
+ that are self-definable. These configurations will have the name <guilabel>Vendor x</guilabel>
+ to distinguish them from the others.
+ The &kcontrolcenter; can even automatically set your card up whenever you start the module.
+ Since establishing (or bringing down) a network connection is a security sensitive operation,
+ any changes to the configuration can only be done by <systemitem class="username">root</systemitem>.</para>
+ <sect2 id="configuration-tab">
+ <title>The Configuration Tabs</title>
+ <para>The configurations are split up in three parts: <itemizedlist>
+ <listitem>
+ <para>general configuration settings (like the network name)</para>
+ </listitem>
+ <listitem>
+ <para>encryption settings</para>
+ </listitem>
+ <listitem>
+ <para>power saving settings</para>
+ </listitem>
+ </itemizedlist>
+ These parts are explained in the following sections.
+ </para>
+ <sect3 id="config_general">
+ <title>General settings</title>
+ <para>The upper part of the control center module consists of one to ten tabs labelled
+ <guilabel>Config 1</guilabel> through <guilabel>Config 10</guilabel>. Each of these tabs
+ can hold a configuration for your WLAN card. In addition (as explained above) up to five vendor-specific
+ configurations may be visible, labelled <guilabel>Vendor 1</guilabel> through <guilabel>Vendor 5</guilabel>.</para>
+ <para>The most important settings are always visible, the cryptography and power management
+ options are only shown when activated. The perhaps most important element in each
+ configuration tab is the field<guilabel>Network name</guilabel>. Here you can specify
+ which network you would like to log into. You can either specify the name of your network
+ directly, or you can try a scan on all available networks by setting the network name to
+ <userinput>any</userinput>.</para>
+ <para>In addition to the network name, you have to specify the type of network to log into.
+ That's the purpose of the button group<guilabel>Operation mode</guilabel>. The
+ option<guilabel>Managed</guilabel> means that the network consists of designated
+ base stations, so-called <quote>access points</quote> or sometimes <quote>residential
+ gateways</quote>. This is the most common operation mode for company networks. The second
+ option,<guilabel>Ad-hoc</guilabel> means that your network is just a direct
+ connection between computers, without access points. The three other options
+ (<guilabel>Repeater</guilabel>, <guilabel>Master</guilabel> and <guilabel>Secondary</guilabel>)
+ are only very seldomly used. If you want to use them, please be aware that these settings are simply
+ passed to the iwconfig program and have not been tested extensively. In case something doesnt
+ work as expected, you are welcome to send a bug report.</para>
+ <para>You can optionally set the connection speed for your connection. The setting
+ <guilabel>auto</guilabel> should do for most uses, since the card will determine the
+ appropriate speed itself. However, if you find that the speed changes every few seconds,
+ for example when you have a weak connection, you can set the speed manually.</para>
+ <para>Below these configuration items you will find a field named<guilabel>Execute script on
+ connect:</guilabel>. Here you can enter the name of a script to execute after setting up
+ the network connection. It will be
+ executed whenever you hit the <guilabel>Activate configuration</guilabel> button and,
+ optionally, automatically when you start the Control Center module. The script will have
+ <systemitem class="username">root</systemitem> rights. This may lead to problems
+ if you want to start an X application in the script and the X server belongs to someone
+ else than root. You can make such scripts work correctly if you execute the X application
+ via<userinput>
+ <command>kdesu</command>
+ <option>-u</option>
+ <replaceable>USERNAME</replaceable>
+ <option>-c</option>
+ <replaceable>COMMAND</replaceable>
+ </userinput>. Or, you can instruct your X server to also allow connections coming from
+ root. You can do this with the <command>xhost</command> program.</para>
+ </sect3>
+ <sect3 id="config_crypto">
+ <title>Cryptography settings</title>
+ <para>The checkbox <guilabel>Use encryption</guilabel> determines whether or not encryption
+ shall be activated. If it is checked, a button labelled <guibutton>Configure...</guibutton> becomes
+ available which allows you to configure the details of encryption.
+ After pushing the button, you are presented the following settings in a new dialog: </para>
+ <variablelist>
+ <varlistentry>
+ <term>
+ <guilabel>Key to use:</guilabel>
+ </term>
+ <listitem>
+ <para>You can define up to four secret keys for each configuration; in this field you
+ can set which one you want to use to send encrypted packets. The card can always
+ receive packets that are encrypted with <emphasis>any</emphasis> of the keys.</para>
+ <tip><para>You can achieve asymetrical encryption (different keys for sending and receiving)
+ if you configure your access point to send packets with a different key than the card. Just make
+ sure that the partner station has the required key in any one of its key slots.</para></tip>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <guilabel>Crypto mode:</guilabel>
+ </term>
+ <listitem>
+ <para>When encryption is activated, there are two ways to deal with incoming
+ non-encrypted packets: discard or allow. When you set your card for <guilabel>Open</guilabel>,
+ the card will also listen to non-encrypted packets.
+ <guilabel>Restricted</guilabel> will only allow encrypted network packets,
+ any other packets are discarded.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>
+ <guilabel>Crypto keys:</guilabel>
+ </term>
+ <listitem>
+ <para>This box lets you specify the secret keys to use for cryptography. To protect
+ your passwords, only asterisks will be shown when you enter a password. The &kcontrolcenter;
+ module will automatically try to guess whether you want to set an encryption string
+ or a hexadecimal number by checking the input length: string keys are usually 5 or 13
+ characters long (for 64- or 128-Bit key lengths) whereas hex values are 10 or 26 characters
+ long (please do not put a <quote>0x</quote> in front of hex keys).</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>Be aware that the built-in cryptography support (named WEP for Wired Equivalent
+ Privacy) is not very safe at all. See <xref linkend="wep"/> for details.</para>
+ </sect3>
+ <sect3 id="config_power">
+ <title>Power saving settings</title>
+ <para>The last configuration element that remains to be described is the power management.
+ When checking the box <guilabel>Enable Power Management</guilabel> a button for the
+ configuration of the setting will become active. After clicking this button, a new dialog will open
+ and you will be presented
+ some options that can help you save energy. The first two input fields named
+ <guilabel>Sleep timeout</guilabel> and <guilabel>Wakeup period</guilabel>
+ describe the periodicity of network online times for your wireless &LAN; card. The card will
+ turn the radio antenna off for the time period (in seconds) specified in
+ <guilabel>Sleep timeout</guilabel>. Afterwards it will be active for <guilabel>Wakeup
+ period</guilabel> and will in that time establish the network connection and send/receive
+ packets that queued up during the sleep time. If no network
+ connection is found, it will go to sleep again immediately and the cycle begins again. The box named
+ <guilabel>Receive packets</guilabel> below lets you specify which packets the card
+ should listen to when awaken. You can either select <guilabel>Unicast only</guilabel>
+ (which will only let your card listen to packets sent specifically to your card),
+ <guilabel>Multi/Broadcast only</guilabel> (will only listen to packets sent to multiple
+ machines and discard packets directed to your card) or <guilabel>Both</guilabel>. Most
+ people should select the default value <guilabel>Both</guilabel>.</para>
+ </sect3>
+ </sect2>
+ <sect2 id="auto-configuration">
+ <title>Auto-configuration on &kcontrolcenter; Module startup</title>
+ <para> If you wish, you can make &kwifimanager; initialize your wireless &LAN; card
+ whenever you start the &kcontrolcenter; module. To do so, check the box <guilabel>Load
+ preset configuration on startup</guilabel> and select the configuration you want to use
+ in the listbox below. If you want to set the card to
+ these settings at once, push the button <guilabel>Activate
+ configuration</guilabel>.</para>
+ </sect2>
+ <sect2 id="autodetect">
+ <title>Autodetecting your device</title>
+ <para>&kwifimanager; needs to know the interface name of your wireless &LAN; card to
+ apply any settings. You can either enter the information (&eg;
+ <userinput>eth1</userinput> or<userinput>wlan0</userinput>) manually in the input field
+ on the right-hand side of<guilabel>Settings apply to interface:</guilabel>, or let &kwifimanager;
+ auto-detect the interface. To do so, push the button<guibutton>Autodetect
+ interface</guibutton>. This will perform a scan on all interfaces listed in <filename
+ class="directory">/proc/net/dev</filename> to find your card. The result of the scan
+ will show up in the input field beside the button. If the field remains empty, no card was
+ found. Please note that &kwifimanager; uses the wireless extensions to detect cards.
+ If you use a card controlled by the wlan-ng package, &kwifimanager; only shows correct
+ results if your driver has a compatibility layer for the wireless extensions
+ built-in. In the case that there are multiple wireless LAN cards present on the system,
+ scanning stops after the first card found. So, if you want to apply the settings to a different
+ card than the one that was detected during the scan, you need to enter its interface name
+ by hand.</para>
+ </sect2>
+ </sect1>
+ </chapter>
+ <chapter id="credits">
+ <title>License and contributors</title>
+ <para>Documentation copyright &copy; Stefan
+ Winter<email>mail@stefan-winter.de</email>.</para> &underFDL; &underGPL;
+ <!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+ </chapter>
+ <appendix id="further-information">
+ <title>Further Information</title>
+ <para>This appendix contains some extra information of items concerning wireless &LAN; that
+ are not directly related to &kwifimanager;.</para>
+ <sect1 id="macaddress">
+ <title>Notes on the MAC address display in Ad-hoc mode</title>
+ <para>At first glance, the MAC address in the field <guilabel>Access Point</guilabel> seems to
+ be wrong in Ad-hoc mode because it changes the first two digits of the MAC address
+ to<computeroutput>02</computeroutput>. But actually, this is a hardcoded feature in
+ wireless &LAN; cards.</para>
+ <para>Usually a card is connected to a <quote>real</quote> access point. Then the correct MAC
+ address is shown. If you change to Ad-hoc (or <quote>Peer-to-peer</quote>) mode, one of the
+ computers must act as a server for the other computers. The first computer that enters a
+ network will set itself as server. So, all other computers connecting to the same Ad-hoc
+ network will see that first computer as network server. But since this computer is not a
+ <quote>real</quote> server (that is, it is not a permanently available access point),
+ clients should be aware that the network they are connecting to is not a permanent one. IEEE
+ standards for MAC addresses have a place reserved for such (rare) occasions: MAC addresses that are
+ not globally valid have a bit set to one that shows that these addresses are
+ <quote>locally administered</quote>. This bit is the second bit in transmit order, and the seventh
+ bit in logical order and will hence raise the number of the MAC's first digit block from 00 to 02.</para>
+ <para>You can compare this sort of address to the non-global IP addresses like<quote>192.168.*.*.</quote>
+ </para>
+ <para>So, the implementors of wireless networking agreed to give these<quote>virtual</quote>
+ network servers a MAC address that is within the <quote>locally administered</quote> scope.
+ To keep this virtual MAC address unique, they used a little trick: they only changed the
+ first segment of the MAC address of the wireless &LAN; card, and since the remaining
+ segments are still unique in the world, they have a unique address to use as network
+ server.</para>
+ </sect1>
+ <sect1 id="wep">
+ <title>Security considerations on <acronym>WEP</acronym> cryptography</title>
+ <para>
+ <acronym>WEP</acronym> cryptography is not very secure at all. A paper from
+ cryptography analysts called the encryption algorithm <quote>kindergarten
+ cryptography</quote>. Actually, software exists that exploits a huge security hole in the
+ encryption standard. This software listens to the encrypted network traffic, analyzes it,
+ and after only a few hours it reveals the password to enter the network in clear text. The
+ more traffic on the network, the easier it is to find out the password because some packets are
+ particularly weak because they carry a bad so-called initialisation vector (IV). Recent access
+ points try to avoid these bad IVs, so it is getting harder to exploit the hole.</para>
+ <para>If you are truly concerned about your security, do<emphasis>not</emphasis> use plain
+ <acronym>WEP</acronym>. If you are just setting up a two-computer home network, well, then
+ I guess<acronym>WEP</acronym> should do.</para>
+ <para>There are many alternatives to <acronym>WEP</acronym> encryption. Its successors WPA and
+ WPA2 are better designed and do a better job protecting your traffic, for example by dynamically changing
+ the keys after a while.
+ If you dont want to rely on the basic safety of the network link you could use
+ <acronym>SSH</acronym> to communicate over the network. <acronym>SSH</acronym> is a
+ program suite that encrypts data with its own algorithm, which is very secure. Another
+ option is to use <acronym>PPTP</acronym>, the Point-to-Point-Tunneling protocol. However,
+ even <acronym>PPTP</acronym> seems to be a bit leaky concerning encryption security. And
+ finally, you could set up an IPSec tunnel (VPN connection) for your encrypted connections. As of yet, this
+ encryption seems to be very safe and flexible.</para>
+ </sect1>
+ </appendix>
+ <appendix id="compile">
+ <title>Compilation and Installation</title> &install.intro.documentation;
+ &install.compile.documentation;</appendix> &documentation.index;</book>
diff --git a/doc/lisa/Makefile.am b/doc/lisa/Makefile.am
new file mode 100644
index 00000000..085981d9
--- /dev/null
+++ b/doc/lisa/Makefile.am
@@ -0,0 +1,4 @@
+
+KDE_LANG = en
+KDE_DOCS = AUTO
+
diff --git a/doc/lisa/index.docbook b/doc/lisa/index.docbook
new file mode 100644
index 00000000..d93e31d5
--- /dev/null
+++ b/doc/lisa/index.docbook
@@ -0,0 +1,694 @@
+<?xml version="1.0" ?>
+<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
+ <!ENTITY lisa "<application>LISa</application>">
+ <!ENTITY package "kdenetwork">
+ <!ENTITY reslisa "<application>resLISa</application>">
+ <!ENTITY kappname "&lisa;"><!-- replace kapp here -->
+ <!ENTITY % addindex "IGNORE">
+ <!ENTITY % English "INCLUDE"><!-- change language only here -->
+]>
+
+<book lang="&language;">
+
+<bookinfo>
+
+<title>The &lisa; Handbook</title>
+
+<authorgroup>
+<author>
+<firstname>Alexander</firstname>
+<surname>Neundorf</surname>
+<affiliation>
+<address><email>neundorf@kde.org</email></address>
+</affiliation>
+</author>
+
+<!-- TRANS:ROLES_OF_TRANSLATORS -->
+
+</authorgroup>
+
+<copyright>
+<year>2001</year>
+<holder>Alexander Neundorf</holder>
+</copyright>
+
+<date>2001-07-07</date>
+<releaseinfo>0.01.00</releaseinfo>
+
+<abstract>
+<para>&lisa; is intended to provide a kind of <quote>network
+neighborhood</quote>, but only relying on the TCP/IP protocol stack, no
+<acronym>SMB</acronym> or anything else required.</para>
+
+<para>This is the handbook to both the <acronym>LAN</acronym>
+Information Server (&lisa;) and the Restricted <acronym>LAN</acronym>
+Information Server (&reslisa;)</para>
+
+</abstract>
+
+<keywordset>
+<keyword>KDE</keyword>
+<keyword>kdenetwork</keyword>
+<keyword>LAN</keyword>
+<keyword>network</keyword>
+<keyword>network neighborhood</keyword>
+</keywordset>
+
+</bookinfo>
+
+<chapter id="introduction">
+<title>Introduction</title>
+
+<para>&lisa; is intended to provide a kind of <quote>network
+neighborhood</quote>, but only relying on the TCP/IP protocol stack, no
+smb or whatever.</para>
+
+<para>It is completely independent from &kde;/&Qt;.</para>
+
+<para>The list of running hosts is provided via <acronym>TCP</acronym>
+port 7741.</para>
+
+<para>&lisa; supports two ways of finding hosts:</para>
+
+<orderedlist>
+<listitem>
+<para>You give &lisa; a range of <acronym>IP</acronym> addresses, then
+&lisa; will send <acronym>ICMP</acronym> echo requests to all given
+<acronym>IP</acronym> addresses, and wait for the answers.</para>
+</listitem>
+<listitem>
+<para>You can tell &lisa; to execute <command>nmblookup</command>
+<option>"*</option>. The command line tool
+<command>nmblookup</command> must be installed from the Samba package.
+<command>nmblookup</command> <option>"*"</option> sends a broadcast to
+the attached networks, and all hosts running <acronym>SMB</acronym>
+services will answer this broadcast.</para>
+</listitem>
+
+</orderedlist>
+
+</chapter>
+
+<chapter id="how-it-works">
+<title>How it works</title>
+
+<para>In the configuration file you provide a range of IP-addresses
+which &lisa; should check to see whether they are running.</para>
+
+<para>In the most simple case this could be your network
+address/subnetmask, then &lisa; would check every possible host of your
+network to see if it is running.</para>
+
+<para>The hosts are checked using <acronym>ICMP</acronym> echo
+requests. To be able to send and receive <acronym>ICMP</acronym> echo
+requests and replies the program has to open a so-called <quote>raw
+socket</quote>. Therefore it needs <systemitem
+class="username">root</systemitem> privileges. This socket is opened
+right after the start of the program, after successfully opening the
+socket root privileges are dropped immediately (see
+<filename>main.cpp</filename> and
+<filename>strictmain.cpp</filename>).</para>
+
+<para>If you configure &lisa; so that it also uses
+<command>nmblookup</command>, it will <literal>popen("nmblookup
+\"*\"")</literal> and then parse the results.</para>
+
+<para>Since the <acronym>ICMP</acronym> requests and the broadcasts can
+cause some network traffic if there are more than one such server
+running in one network, the servers cooperate with each other. Before
+they start pinging (or <command>nmblookup</command>), they send a
+broadcast on port 7741.</para>
+
+<para>If somebody answers this broadcast, they will retrieve the
+complete list of running hosts via <acronym>TCP</acronym> port 7741 from
+this host and will not start to ping (or
+<command>nmblookup</command>).</para>
+
+<para>If nobody answers, the host which sent the broadcast will start
+pinging the hosts (or <command>nmblookup</command>) and then open a
+socket which listens for the mentioned broadcasts. If the host received
+an answer to his broadcast, it won't have the socket for listening to
+the broadcasts open. So usually exactly one of the servers will have
+this socket open and only this one will actually ping (or
+<command>nmblookup</command>) the hosts. </para>
+
+<para>In other words, the servers are lazy, they work like <quote>I will
+only do something if nobody else can do it for me</quote>.</para>
+
+<para>There is another feature which reduces the network load.</para>
+
+<para>Let's say you configured &lisa; to update every 10 minutes. Now
+you don't access your server very often. If nobody accesses the server
+for the last update period, the server will update (either itself or
+from the one which actually does the work) and then double its update
+period, &ie; the next update will happen after 20 minutes.</para>
+
+<para>This will happen 4 times, so if nobody accesses the server with
+update period 10 minutes for a long time, its update interval will
+increase up to 160 minutes, almost three hours. If then somebody
+accesses the data from the server, he will get an old list ( up to 160
+minutes old). With accessing the server will reset its update interval
+to its initial value, &ie; 10 minutes and immediately start updating if
+the last update is more than these 10 minutes over. This means if you
+get a very old list, you can try some seconds later again and you should
+get a current version.</para>
+
+<para>This will have fast effect for the servers, which don't ping (or
+nmblookup) theirselves, since only one user usually accesses them, and
+it will have less effect for the server which does the pinging (or
+<command>nmblookup</command>), since this server is accessed from all
+other servers in the network.</para>
+
+<para>This way it is possible that many hosts in a network run this
+server, but the net load will remain low. For the user it is not
+necessary to know wether there is a server (&ie; a name server or
+fileserver or whatever) in the network which also runs &lisa;. He can
+always run &lisa; locally and &lisa; will detect if there is one,
+transparently to the user.</para>
+
+<para>The first client for &lisa; is an ioslave for &kde; 2, so the user
+can enter there <userinput>lan://localhost/</userinput> or
+<userinput>lan:/</userinput>, which will both contact &lisa; on the own
+system.</para>
+
+<para>If there is a machine which runs all the time and the user knows
+that this machine also runs &lisa;, he can use his &lisa; client
+directly with this server (would be with the mentioned ioslave
+<userinput>lan://the_server_name/</userinput>).</para>
+
+<para>If you don't want that your &lisa; takes part in the broadcasting,
+but always does the pinging itself, make it use another port with the
+command line option <option>--port</option> or <option>-p</option>. This
+is not recommended!</para>
+
+<para>If you send <command>SIGHUP</command> to &lisa;, it will reread
+its configfile. If you send <command>SIGUSR1</command> to &lisa;, it
+will print some status information to stdout.</para>
+
+<para>The data provided over the socket has a simple format:
+
+<computeroutput>&lt;decimal ip address in network byte order&gt;&lt;one space
+0x20&gt;&lt;full name of the host&gt;&lt;a terminating
+'\0'&gt;&lt;newline '\n'&lt;</computeroutput>
+and the last line
+<computeroutput>0 succeeded&lt;'\n'&gt;</computeroutput>
+</para>
+
+<para>For example,</para>
+
+<screen><computeroutput>17302538 some_host.whatever.de
+18285834 linux.whatever.de
+17827082 nameserver.whatever.de
+0 succeeded</computeroutput></screen>
+
+<para>This should make it easy parseable.</para>
+
+<para>If there are very strict security rules in your network, some
+people might consider the pinging a potential attack. If you have
+problems with this, try the restricted version, &reslisa;.</para>
+
+</chapter>
+
+<chapter id="reslisa">
+<title>&reslisa;</title>
+
+<para>If you hav very strict security rules in your network or you don't
+want to have another port open or whatever, you can use
+&reslisa;.</para>
+
+<para>With &reslisa; you can't ping whole networks and address ranges,
+you can give &reslisa; up to currently 64 hosts by their names in its
+config file. These will be pinged. You are still able to use
+<command>nmblookup</command>.</para>
+
+<para>&reslisa; will also only provide the information over a unix
+domain socket, &ie; not over the network. The name of the socket is
+<filename>/tmp/resLisa-YourLoginname</filename> so &reslisa; can be
+safely run by more users on one machine.</para>
+
+<para>Since it should also not produce a security risk of any kind it is
+safe to install &reslisa; setuid <systemitem
+class="username">root</systemitem>. <systemitem
+class="username">root</systemitem> privileges will be dropped right
+after startup (see <filename>strictmain.cpp</filename>), they are only
+needed to create a raw socket for sending the <acronym>ICMP</acronym>
+echo requests.</para>
+
+<para>It will also not send or receive broadcasts. The first client for
+this is also an ioslave for &kde; 2 (<userinput>rlan:/</userinput> in
+&konqueror; for example.)</para>
+
+</chapter>
+
+<chapter id="config-file-format">
+<title>The Configuration File</title>
+
+<para>Now an example config file:</para>
+
+<screen>
+PingAddresses = 192.168.100.0/255.255.255.0;192.168.100.10-192.168.199.19;192.168.200.1;192-192.168-168.100-199.0-9;
+PingNames = bb_mail;
+AllowedAddresses = 192.168.0.0/255.255.0.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #also try nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+</screen>
+
+<sect1 id="pingaddresses">
+<title><option>PingAddresses</option></title>
+
+<para>This is probably the most important entry.</para>
+
+<para>Here you say which addresses will be pinged. You can specify
+multiple ranges, they are divided by semicolons.</para>
+
+<para>There are four possible ways to define addresses:</para>
+
+<variablelist>
+<varlistentry>
+<term>net address/network mask</term>
+<listitem>
+<para>192.168.100.0/255.255.255.0, &ie; an <acronym>IP</acronym> address
+ and the assigned network mask.</para>
+
+<para>This doesn't have to be the network address and netmask of your
+machine. For example, if you have 10.0.0.0/255.0.0.0 as your own
+address, you could specify 10.1.2.0/255.255.255.0 if you are only
+interested in these addresses. The combination <acronym>IP</acronym>
+address-network mask must be divided by a slash <quote>/</quote> and the
+address does not have to be a real network address, it can also be a
+host address of the desired network, &ie; 10.12.34.67/255.0.0.0 is the
+same as 10.0.0.0/255.0.0.0 .</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>a range of <acronym>IP</acronym> addresses</term>
+<listitem>
+<para>For example: 192.168.100.10-192.168.199.19</para>
+
+<para>An <acronym>IP</acronym>-address where pinging will start and an
+<acronym>IP</acronym>-address where pinging will end.</para>
+
+<para>Both addresses must be divided by a <quote>-</quote>.</para>
+
+<para>In this example this would produce 199-100+1=100, 100*256=25.600,
+25.600+(19-10+1)=25.590 addresses</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>An <acronym>IP</acronym> address, as represented by ranges of each
+of the four decimal numbers</term>
+<listitem>
+<para>An <acronym>IP</acronym> address can be represented by its four
+decimal numbers, and you can specify ranges four each of these four
+numbers: 192-192.169-171.100-199.0-9
+</para>
+
+<para>In this example all <acronym>IP</acronym> addresses with first
+number 192, second number from 168 to 168, third number from 100 up to
+199 and last number from 0 up to 9 will be pinged. This would give
+1*1*100*10=1.000 addresses.</para>
+
+<para>This is probably only useful in very seldom cases. Here you have
+to provide ranges for every four numbers, always divided by
+<quote>-</quote>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term>Single <acronym>IP</acronym> addresses or host names</term>
+<listitem>
+<para>The <acronym>IP</acronym> address or host name of any machine you
+are particularly interested in.</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
+<para>It is also valid to leave this entry empty.</para>
+
+</sect1>
+
+<sect1 id="pingnames">
+<title><option>PingNames</option></title>
+
+<para>Here you can additionally specify hosts to ping using their names.
+The names have to be divided by semicolons.</para>
+
+<para>It is also valid to leave this entry empty.</para>
+
+</sect1>
+
+<sect1 id="allowedaddresses">
+<title><option>AllowedAddresses</option></title>
+
+<para>This is also very important. &lisa; will only ping addresses,
+accept clients and answer broadcasts from addresses, which are covered
+by the addresses given in this line. You can add up to 32 network
+addresses/network masks or single addresses. Divide them by ; and don't
+put empty space between the addresses!</para>
+
+<para>For example, 192.168.0.0/255.255.0.0;192.169.0.0</para>
+
+<para>A complete network and a single address are valid. Always make
+this as strict as possible, usually your network address/subnetmask is a
+good choice.</para>
+
+</sect1>
+
+<sect1 id="broadcastnetwork">
+<title><option>BroadcastNetwork</option></title>
+
+<para>This entry contains exactly one network address/subnet mask. To
+this network broadcasts will be sent. Usually this should be your own
+network address/subnetmask, for example: 192.168.0.0/255.255.0.0</para>
+
+</sect1>
+
+<sect1 id="searchusingnmblookup">
+<title><option>SearchUsingNmblookup</option></title>
+
+<para>Here you can give <parameter>0</parameter> or
+<parameter>1</parameter>. <parameter>1</parameter> means that &lisa;
+will execute <command>nmblookup</command> <option>"*"</option> and parse
+the output from this command. This produces less network traffic than
+the pinging, but you will only get hosts which have a
+<acronym>SMB</acronym> service running (&Windows; machines or machines
+running samba).</para>
+
+<para>If you enable this option and also give <acronym>IP</acronym>
+addresses to ping, then <command>nmblookup</command> will be executed
+first and then the pinging will start. Then only addresses will be
+pinged, which were not already delivered from
+<command>nmblookup</command>. This should slightly decrease the network
+load.</para>
+
+</sect1>
+
+<sect1 id="firstwait">
+<title><option>FirstWait</option></title>
+
+<para>If &lisa; pings, &ie; if it sends the <acronym>ICMP</acronym> echo
+requests, it sends a bunch of requests at once, and the it will wait for
+the number of hundredth seconds you specify here. Usually values from 5
+to 50 should be good, the maximum is 99 (gives 0.99 seconds, a very long
+time). Try to make this value as small as possible while still finding
+all running hosts.</para>
+
+</sect1>
+
+<sect1 id="secondwait">
+<title><option>SecondWait</option></title>
+
+<para>After &lisa; has sent the echo requests the first time, it can be
+possible that some hosts were not found. To improve the results, &lisa;
+can ping a second time. This time it will only ping hosts, from which it
+didn't receive answers. If you have good results with pinging only once,
+you can disable the second time with setting SecondWait to
+<userinput>-1</userinput>.</para>
+
+<para>Otherwise it might be a good idea to make this value a little bit
+bigger than the value for <option>FirstWait</option>, since the hosts
+which were not found on the first try, are probably slower or further
+away so they might take some milliseconds longer to answer. Usually
+values from 5 to 50 should be good or -1 to disable the second scan.
+The maximum is 99 (gives 0.99 seconds, a very long time).</para>
+
+</sect1>
+
+<sect1 id="updateperiod">
+<title><option>UpdatePeriod</option></title>
+
+<para>This is the interval after which &lisa; will update. After this
+time &lisa; will again ping or <command>nmblookup</command> or get the
+list of hosts from the &lisa; server which actually does the
+pinging.</para>
+
+<para>Valid values are between 30 seconds and 1800 seconds (half an
+hour). If you have a big network, don't make the interval too small (to
+keep network load low). Values from 300 to 900 seconds (5 to 15 minutes)
+might be a good idea.</para>
+
+<para>Keep in mind that the update period is doubled if nobody accesses
+the server, up to 4 times, so the interval will become 16 times the
+value given here and will be reseted to the value given here if somebody
+accesses the server.</para>
+
+</sect1>
+
+<sect1 id="deliver-unnamed-hosts">
+<title><option>DeliverUnnamedHosts</option></title>
+
+<para>If an answer to an echo request from an IP address was received,
+were &lisa; could not determine a name, it will be only delivered over
+the port if you set this to 1.</para>
+
+<para>I am not really sure if this is a useful feature, but maybe there
+are some infrastructure devices in your network without assigned names,
+so they don't have to be published. Set this to 0 if you want to keep
+them secret ;-) If unsure, say 0.</para>
+
+</sect1>
+
+<sect1 id="max-pings-at-once">
+<title>MaxPingsAtOnce</title>
+
+<para>When sending the pings (echo requests), &lisa; sends a bunch of
+these at once and then waits for the answers. By default there are 256
+pings sent at once, usually you should not need the change this
+value. If you make it much bigger, the internal receive buffers for the
+answers to the echo requests may become to small, if you make it to
+small, the updating will be slower.</para>
+
+</sect1>
+
+<sect1 id="examples">
+<title>Three more example files</title>
+
+<example>
+<title>FIXME</title>
+
+<para>You are member of a small network with 24 bit network mask, &ie;
+up to 256 hosts:</para>
+
+<screen>
+PingAddresses = 192.168.100.0/255.255.255.0
+AllowedAddresses = 192.168.100.0/255.255.255.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 0 #don't use nmblookup
+FirstWait = 20 #20 hundredth seconds
+SecondWait = 30 #30 hundredth seconds on the seconds try
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+</screen>
+
+</example>
+
+<example>
+<title>Configuration file for hosts running <acronym>SMB</acronym>
+only</title>
+
+<para>You are only interested in hosts running <acronym>SMB</acronym>
+services and you don't have routers in your network:</para>
+
+<screen>
+AllowedAddresses = 192.168.100.0/255.255.255.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #use nmblookup
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+</screen>
+</example>
+
+<example>
+<title>Configuration file using both <command>nmblookup</command> and
+pinging</title>
+
+<para>The same network, but here both nmblookup and pinging is
+used.</para>
+
+<screen>
+PingAddresses = 192.168.100.0/255.255.255.0
+PingNames = bb_mail
+AllowedAddresses = 192.168.0.0/255.255.0.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #also try nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+</screen>
+
+</example>
+
+<example>
+<title>Configuration file for &reslisa;</title>
+
+<para>And now a configuration file for &reslisa;, PingAddresses is not
+used by &reslisa;, neither is BroadcastNetwork.</para>
+
+<screen>
+PingNames = bb_mail;some_host;some_other_host
+AllowedAddresses = 192.168.0.0/255.255.0.0
+SearchUsingNmblookup = 1 # use nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 1 #also publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+</screen>
+
+</example>
+</sect1>
+</chapter>
+
+<chapter id="command-line-options">
+<title>Command Line Options and Other Usage</title>
+
+<para>The following command line options are supported:</para>
+
+<variablelist>
+<varlistentry>
+<term><option>-v</option>, <option>--version</option></term>
+<listitem>
+<para>Prints brief version information.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-h</option>, <option>--help</option></term>
+<listitem>
+<para>Gives an overview of the command line options</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-u</option>, <option>--unix</option></term>
+<listitem>
+<para>Search at first for
+<filename>$<envar>HOME</envar>/.lisarc</filename>, then for
+<filename>/etc/lisarc</filename>. This is the default behavior.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-k</option>, <option>--kde1</option></term>
+<listitem>
+<para>Search first for
+<filename>$<envar>HOME</envar>/.kde/share/config/lisarc</filename>, then
+for
+<filename>$<envar>KDEDIR</envar>/share/config/lisarc</filename>.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-K</option>, <option>--kde2</option></term>
+<listitem>
+<para>Looks for the file <filename>lisarc</filename> in every folder
+returned by running <userinput><command>kde-config</command>
+<option>--path</option> <parameter>config</parameter></userinput></para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-c</option>,
+<option>--config=</option><parameter>FILE</parameter></term>
+<listitem>
+<para>Read <parameter>FILE</parameter> and no other configuration
+file.</para>
+</listitem>
+</varlistentry>
+
+<varlistentry>
+<term><option>-p</option>, <option>--port</option>
+<parameter>PORTNR</parameter></term>
+<listitem>
+<para>Start the server on this portnumber. If you use this, &lisa;
+won't be able to cooperate with other &lisa;'s on the network. This
+option is not available for &reslisa;</para>
+</listitem>
+</varlistentry>
+
+</variablelist>
+
+<para>If you send the Hangup-Signal to &lisa; or &reslisa;, it will reread its
+configuration file (<userinput><command>killall</command> <option>-HUP
+lisa</option></userinput>).</para>
+
+<para>If you send the User1-Signal to &lisa; or &reslisa;, it will print
+some status information to the standard output
+(<userinput><command>killall</command> <option>-USR1
+lisa</option></userinput>). You won't see anything if the console from
+which &lisa;/&reslisa; was started has terminated.</para>
+
+
+
+</chapter>
+
+<chapter id="credits-and-license">
+<title>Credits and Licenses</title>
+
+<para>&lisa; and &reslisa; copyright 2000, 2001, Alexander
+Neundorf</para>
+
+
+
+<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
+
+<para>Have fun, Alexander Neundorf <email>neundorf@kde.org</email></para>
+
+&underFDL;
+&underGPL;
+
+</chapter>
+
+<appendix id="installation">
+<title>Installation</title>
+
+<para>&lisa; and &reslisa; need a libstdc++ (it uses only the
+string-class from it), it <emphasis>does not</emphasis> need either &Qt;
+nor &kde;.</para>
+
+&install.compile.documentation;
+
+<sect1 id="other-requirements">
+<title>Other Requirements</title>
+
+<para>Both &reslisa; and &lisa; open a so called <quote>raw
+socket</quote> to send and receive <acronym>ICMP</acronym> echo requests
+(pings). To do this, they need <systemitem
+class="username">root</systemitem> privileges.</para>
+
+
+<para>&lisa; offers a service on <acronym>TCP</acronym> port 7741, and
+should be installed by <systemitem class="username">root</systemitem>
+and started when the system comes up. It depends greatly on your &OS;
+how to achieve this.</para>
+
+<para>&reslisa; is intended to be started per user, it doesn't offer
+anything to the network. It needs to be installed setuid <systemitem
+class="username">root</systemitem>.</para>
+
+<para>If you use the <userinput>rlan</userinput> ioslave from &kde; 2,
+&reslisa; can be started automatically.</para>
+
+<para>&lisa; reads the file <filename>lisarc</filename>, &reslisa; reads
+the file <filename>reslisarc</filename>. If you want to be able to
+configure both from &kcontrol;, you have to start them using the command
+line switch <option>-K</option>.</para>
+
+<para>For more information where they look for configuration files read
+the chapter on <xref linkend="command-line-options"/>.</para>
+
+</sect1>
+</appendix>
+</book>
diff --git a/filesharing/Makefile.am b/filesharing/Makefile.am
new file mode 100644
index 00000000..61d66854
--- /dev/null
+++ b/filesharing/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = advanced simple
+
+messages: rc.cpp
+ $(EXTRACTRC) `find . -name '*.ui'` > rc.cpp
+ $(XGETTEXT) rc.cpp `find . -name '*.cpp' -or -name '*.h'` -o $(podir)/kfileshare.pot
diff --git a/filesharing/advanced/Makefile.am b/filesharing/advanced/Makefile.am
new file mode 100644
index 00000000..135d9702
--- /dev/null
+++ b/filesharing/advanced/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = nfs kcm_sambaconf propsdlgplugin
diff --git a/filesharing/advanced/kcm_sambaconf/ChangeLog b/filesharing/advanced/kcm_sambaconf/ChangeLog
new file mode 100644
index 00000000..218c7bd3
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/ChangeLog
@@ -0,0 +1,53 @@
+- 0.5
+ * New features :
+ - Added new options of Samba 3.0.2
+ * Bugfixes :
+ - Fixed a crash if files are deleted from a directory
+ that is configured by KSambaPlugin at the same time.
+- 0.5 Beta 2
+ * New features :
+ - Support of Samba 3.0.1
+ * Bugfixes :
+ - Should now be compileable with Qt 3.0
+- 0.5 Beta
+ * New features :
+ - Works with Samba 3.x
+ - Supports all options of Samba 3.0 and Samba 2.x
+ - Supports loading and saving of remote Samba configuration files
+ with KIO slaves (e.g. fish)
+ - Complete rewrite of the user permission tab
+ - Accelerator keys added where possible
+ - Tab ordering fixed everywhere
+ - Redesigned big parts of the interface
+
+ * Bugfixes :
+ - Fixed crash, when smb.conf wasn't found
+ - Some other bugs fixed
+
+- 0.4.1
+ * Bugfixes :
+ - "user only" changed to "only user"
+ - Changings in the advanced tab will enable the apply button
+ * New options :
+ - Many new options have been added to the Share dialog
+ - The layout of the Share dialog has been changed
+ * Automake :
+ - Doesn't depend on automake 1.7, but on version 1.5.x
+
+- 0.4b
+ * Bugfixes :
+ - General :
+ * adding a blank line between two sections in the smb.conf
+ - Konqueror Plugin :
+ * availabe changed to available
+ * when changing a share to be not shared the entry is removed from the smb.conf
+ - KControl Center Module :
+ * Changing of the socket options works now
+ * New layout :
+ - The KControl Center module layout has changed much
+ - The Konqueror plugin layout has been changed a little
+ * New options :
+ - Many new options have been added to the KControl Center module
+
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/Makefile.am b/filesharing/advanced/kcm_sambaconf/Makefile.am
new file mode 100644
index 00000000..55cd699d
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/Makefile.am
@@ -0,0 +1,46 @@
+KDE_ICON = AUTO
+noinst_LTLIBRARIES = libfilesharesamba.la
+kde_module_LTLIBRARIES = kcm_kcmsambaconf.la
+
+AM_CPPFLAGS = $(all_includes)
+METASOURCES=AUTO
+
+kcm_kcmsambaconf_la_SOURCES = \
+ kcminterface.ui \
+ kcmprinterdlg.ui \
+ printerdlgimpl.cpp \
+ kcmsambaconf.cpp \
+ joindomaindlg.ui
+kcm_kcmsambaconf_la_COMPILE_FIRST = usertab.h share.h
+
+
+kcm_kcmsambaconf_la_LIBADD = \
+ -lkdeprint \
+ libfilesharesamba.la
+
+kcm_kcmsambaconf_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+libfilesharesamba_la_SOURCES = \
+ sambafile.cpp \
+ share.ui \
+ sharedlgimpl.cpp \
+ sambashare.cpp \
+ socketoptionsdlg.ui \
+ common.cpp \
+ userselectdlg.ui \
+ groupselectdlg.ui \
+ usertab.ui \
+ usertabimpl.cpp \
+ filemodedlg.ui \
+ filemodedlgimpl.cpp \
+ smbpasswdfile.cpp \
+ passwd.cpp \
+ hiddenfileview.cpp \
+ dictmanager.cpp \
+ qmultichecklistitem.cpp \
+ smbconfconfigwidget.cpp \
+ linuxpermissionchecker.cpp \
+ expertuserdlg.ui
+
+
+xdg_apps_DATA = kcmsambaconf.desktop
diff --git a/filesharing/advanced/kcm_sambaconf/TODO b/filesharing/advanced/kcm_sambaconf/TODO
new file mode 100644
index 00000000..01abf9f2
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/TODO
@@ -0,0 +1,2 @@
+- Translations
+- Wizards
diff --git a/filesharing/advanced/kcm_sambaconf/common.cpp b/filesharing/advanced/kcm_sambaconf/common.cpp
new file mode 100644
index 00000000..1a8a8c13
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/common.cpp
@@ -0,0 +1,72 @@
+/***************************************************************************
+ common.cpp - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <qstring.h>
+#include <qcombobox.h>
+#include <qlistbox.h>
+
+#include "common.h"
+
+void setComboToString(QComboBox* combo,const QString & s)
+{
+ int i = combo->listBox()->index(combo->listBox()->findItem(s,Qt::ExactMatch));
+ combo->setCurrentItem(i);
+}
+
+bool boolFromText(const QString & value, bool testTrue)
+{
+ QString lower = value.lower();
+
+ if (testTrue) {
+ if (lower=="yes" ||
+ lower=="1" ||
+ lower=="true" ||
+ lower=="on")
+ return true;
+ else
+ return false;
+ } else {
+ if (lower=="no" ||
+ lower=="0" ||
+ lower=="false" ||
+ lower=="off")
+ return false;
+ else
+ return true;
+ }
+}
+
+QString textFromBool(bool value)
+{
+ if (value)
+ return "yes";
+ else
+ return "no";
+}
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/common.h b/filesharing/advanced/kcm_sambaconf/common.h
new file mode 100644
index 00000000..a839e883
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/common.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ common.h - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+class QString;
+class QComboBox;
+
+
+void setComboToString(QComboBox*,const QString &);
+bool boolFromText(const QString & value,bool testTrue=true);
+QString textFromBool(bool value);
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/dictmanager.cpp b/filesharing/advanced/kcm_sambaconf/dictmanager.cpp
new file mode 100644
index 00000000..51f63266
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/dictmanager.cpp
@@ -0,0 +1,216 @@
+/***************************************************************************
+ dictmanager.cpp - description
+ -------------------
+ begin : Wed Jan 1 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qtooltip.h>
+#include <qstringlist.h>
+
+#include <kurlrequester.h>
+#include <klocale.h>
+
+#include "sambashare.h"
+#include "dictmanager.h"
+#include "common.h"
+
+
+DictManager::DictManager(SambaShare* share):
+ lineEditDict(40,false),
+ checkBoxDict(40,false),
+ urlRequesterDict(40,false),
+ spinBoxDict(40,false),
+ comboBoxDict(20,false),
+ comboBoxValuesDict(20,false)
+{
+ _share = share;
+}
+
+DictManager::~DictManager() {
+}
+
+void DictManager::handleUnsupportedWidget(const QString & s, QWidget* w) {
+ w->setEnabled(false);
+ QToolTip::add(w,i18n("The option <em>%1</em> is not supported by your Samba version").arg(s));
+}
+
+void DictManager::add(const QString & key, QLineEdit* lineEdit) {
+ if (_share->optionSupported(key)) {
+ lineEditDict.insert(key,lineEdit);
+ connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(changedSlot()));
+ } else
+ handleUnsupportedWidget(key,lineEdit);
+}
+
+void DictManager::add(const QString & key, QCheckBox* checkBox){
+ if (_share->optionSupported(key)) {
+ checkBoxDict.insert(key,checkBox);
+ connect(checkBox, SIGNAL(clicked()), this, SLOT(changedSlot()));
+ } else
+ handleUnsupportedWidget(key,checkBox);
+}
+
+void DictManager::add(const QString & key, KURLRequester* urlRq){
+ if (_share->optionSupported(key)) {
+ urlRequesterDict.insert(key,urlRq);
+ connect(urlRq, SIGNAL(textChanged(const QString &)), this, SLOT(changedSlot()));
+ } else
+ handleUnsupportedWidget(key,urlRq);
+}
+
+void DictManager::add(const QString & key, QSpinBox* spinBox){
+ if (_share->optionSupported(key)) {
+ spinBoxDict.insert(key,spinBox);
+ connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(changedSlot()));
+ } else
+ handleUnsupportedWidget(key,spinBox);
+}
+
+void DictManager::add(const QString & key, QComboBox* comboBox, QStringList* values){
+ if (_share->optionSupported(key)) {
+ comboBoxDict.insert(key,comboBox);
+ comboBoxValuesDict.insert(key,values);
+ connect(comboBox, SIGNAL(activated(int)), this, SLOT(changedSlot()));
+ } else
+ handleUnsupportedWidget(key,comboBox);
+}
+
+
+void DictManager::load(SambaShare* share, bool globalValue, bool defaultValue){
+ QDictIterator<QCheckBox> checkBoxIt( checkBoxDict );
+
+ for( ; checkBoxIt.current(); ++checkBoxIt ) {
+ checkBoxIt.current()->setChecked(share->getBoolValue(checkBoxIt.currentKey(),globalValue,defaultValue));
+ }
+
+ QDictIterator<QLineEdit> lineEditIt( lineEditDict );
+
+ for( ; lineEditIt.current(); ++lineEditIt ) {
+ lineEditIt.current()->setText(share->getValue(lineEditIt.currentKey(),globalValue,defaultValue));
+ }
+
+ QDictIterator<KURLRequester> urlRequesterIt( urlRequesterDict );
+
+ for( ; urlRequesterIt.current(); ++urlRequesterIt ) {
+ urlRequesterIt.current()->setURL(share->getValue(urlRequesterIt.currentKey(),globalValue,defaultValue));
+ }
+
+ QDictIterator<QSpinBox> spinBoxIt( spinBoxDict );
+
+ for( ; spinBoxIt.current(); ++spinBoxIt ) {
+ spinBoxIt.current()->setValue(share->getValue(spinBoxIt.currentKey(),globalValue,defaultValue).toInt());
+ }
+
+ loadComboBoxes(share,globalValue,defaultValue);
+
+}
+
+void DictManager::loadComboBoxes(SambaShare* share, bool globalValue, bool defaultValue) {
+ QDictIterator<QComboBox> comboBoxIt( comboBoxDict );
+
+ for( ; comboBoxIt.current(); ++comboBoxIt ) {
+ QStringList *v = comboBoxValuesDict[comboBoxIt.currentKey()];
+ QString value = share->getValue(comboBoxIt.currentKey(),globalValue,defaultValue);
+
+ if (value.isNull())
+ continue;
+
+ value = value.lower();
+
+
+ int comboIndex = 0;
+
+ QStringList::iterator it;
+ for ( it = v->begin(); it != v->end(); ++it ) {
+ QString lower = (*it).lower();
+ if ( lower == "yes" &&
+ boolFromText(value))
+ break;
+
+ if ( lower == "no" &&
+ ! boolFromText(value,false))
+ break;
+
+ if ( lower == value )
+ break;
+
+ comboIndex++;
+ }
+
+ comboBoxIt.current()->setCurrentItem(comboIndex);
+ }
+}
+
+
+void DictManager::save(SambaShare* share, bool globalValue, bool defaultValue){
+ QDictIterator<QCheckBox> checkBoxIt( checkBoxDict );
+
+ for( ; checkBoxIt.current(); ++checkBoxIt ) {
+ share->setValue(checkBoxIt.currentKey(),checkBoxIt.current()->isChecked(), globalValue, defaultValue );
+ }
+
+ QDictIterator<QLineEdit> lineEditIt( lineEditDict );
+
+ for( ; lineEditIt.current(); ++lineEditIt ) {
+ share->setValue(lineEditIt.currentKey(),lineEditIt.current()->text(), globalValue, defaultValue );
+ }
+
+ QDictIterator<KURLRequester> urlRequesterIt( urlRequesterDict );
+
+ for( ; urlRequesterIt.current(); ++urlRequesterIt ) {
+ share->setValue(urlRequesterIt.currentKey(),urlRequesterIt.current()->url(), globalValue, defaultValue );
+ }
+
+ QDictIterator<QSpinBox> spinBoxIt( spinBoxDict );
+
+ for( ; spinBoxIt.current(); ++spinBoxIt ) {
+ share->setValue(spinBoxIt.currentKey(),spinBoxIt.current()->value(), globalValue, defaultValue );
+ }
+
+ QDictIterator<QComboBox> comboBoxIt( comboBoxDict );
+
+ for( ; comboBoxIt.current(); ++comboBoxIt ) {
+ QStringList* values = comboBoxValuesDict[comboBoxIt.currentKey()];
+
+ int i = comboBoxIt.current()->currentItem();
+ share->setValue(comboBoxIt.currentKey(),(*values)[i], globalValue, defaultValue );
+ }
+
+}
+
+void DictManager::changedSlot() {
+ emit changed();
+}
+
+
+
+#include "dictmanager.moc"
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/dictmanager.h b/filesharing/advanced/kcm_sambaconf/dictmanager.h
new file mode 100644
index 00000000..765c8c6e
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/dictmanager.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ dictmanager.h - description
+ -------------------
+ begin : Wed Jan 1 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef _DICTMANAGER_H_
+#define _DICTMANAGER_H_
+
+class SambaShare;
+class QLineEdit;
+class QCheckBox;
+class QSpinBox;
+class QComboBox;
+class QString;
+class SambaShare;
+class QStringList;
+
+/**
+ * @author Jan Schäfer
+ **/
+class DictManager : public QObject
+{
+Q_OBJECT
+ public :
+ DictManager(SambaShare *share);
+ virtual ~DictManager();
+
+ void add(const QString &, QLineEdit*);
+ void add(const QString &, QCheckBox*);
+ void add(const QString &, KURLRequester*);
+ void add(const QString &, QSpinBox*);
+ void add(const QString &, QComboBox*, QStringList*);
+
+ void load(SambaShare* share, bool globalValue=true, bool defaultValue=true);
+ void save(SambaShare* share, bool globalValue=true, bool defaultValue=true);
+
+ protected :
+ QDict<QLineEdit> lineEditDict;
+ QDict<QCheckBox> checkBoxDict;
+ QDict<KURLRequester> urlRequesterDict;
+ QDict<QSpinBox> spinBoxDict;
+ QDict<QComboBox> comboBoxDict;
+ QDict<QStringList> comboBoxValuesDict;
+
+ SambaShare* _share;
+
+ void handleUnsupportedWidget(const QString &, QWidget*);
+ void loadComboBoxes(SambaShare*, bool, bool);
+
+ protected slots:
+ void changedSlot();
+
+ signals:
+ void changed();
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/expertuserdlg.ui b/filesharing/advanced/kcm_sambaconf/expertuserdlg.ui
new file mode 100644
index 00000000..a088dcae
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/expertuserdlg.ui
@@ -0,0 +1,263 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>ExpertUserDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>ExpertUserDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>463</width>
+ <height>221</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>User Settings</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Valid users:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>validUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>validUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel12_2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Admin users:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>adminUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>adminUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel12_2_2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Invalid users:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>invalidUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>invalidUsersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QFrame" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>frame16</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer97</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel12_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Write list:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>writeListEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>writeListEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>readListEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel12_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Read list:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>readListEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>ExpertUserDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>ExpertUserDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>validUsersEdit</tabstop>
+ <tabstop>readListEdit</tabstop>
+ <tabstop>writeListEdit</tabstop>
+ <tabstop>adminUsersEdit</tabstop>
+ <tabstop>invalidUsersEdit</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/filemodedlg.ui b/filesharing/advanced/kcm_sambaconf/filemodedlg.ui
new file mode 100644
index 00000000..a5b77c92
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/filemodedlg.ui
@@ -0,0 +1,430 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>FileModeDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>FileModeDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>370</width>
+ <height>220</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Access Modifiers</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Access Permissions</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="Line" row="0" column="4" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>Line4</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>VLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ </widget>
+ <spacer row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Spacer37</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>43</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="6" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>Spacer38</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Others</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>TextLabel4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Read</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1">
+ <property name="name">
+ <cstring>othersReadChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Exec</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Write</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="2">
+ <property name="name">
+ <cstring>groupWriteChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="2">
+ <property name="name">
+ <cstring>othersWriteChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="2">
+ <property name="name">
+ <cstring>ownerWriteChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="3">
+ <property name="name">
+ <cstring>othersExecChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>groupReadChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>ownerReadChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Owner</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="3">
+ <property name="name">
+ <cstring>groupExecChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Group</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="3">
+ <property name="name">
+ <cstring>ownerExecChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="5">
+ <property name="name">
+ <cstring>stickyBitChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sticky</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="5">
+ <property name="name">
+ <cstring>setGIDChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Set GID</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="5">
+ <property name="name">
+ <cstring>setUIDChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Set UID</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="5">
+ <property name="name">
+ <cstring>TextLabel8</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Special</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonHelp</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Help</string>
+ </property>
+ <property name="accel">
+ <string>F1</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>FileModeDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>FileModeDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>ownerReadChk</tabstop>
+ <tabstop>ownerWriteChk</tabstop>
+ <tabstop>ownerExecChk</tabstop>
+ <tabstop>groupReadChk</tabstop>
+ <tabstop>groupWriteChk</tabstop>
+ <tabstop>groupExecChk</tabstop>
+ <tabstop>othersReadChk</tabstop>
+ <tabstop>othersWriteChk</tabstop>
+ <tabstop>othersExecChk</tabstop>
+ <tabstop>setUIDChk</tabstop>
+ <tabstop>setGIDChk</tabstop>
+ <tabstop>stickyBitChk</tabstop>
+ <tabstop>buttonHelp</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.cpp b/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.cpp
new file mode 100644
index 00000000..7c61751d
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.cpp
@@ -0,0 +1,118 @@
+/***************************************************************************
+ filemodedlgimpl.cpp - description
+ -------------------
+ begin : Thu Jul 18 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <assert.h>
+
+#include <stdio.h>
+
+#include <qlineedit.h>
+#include <qcheckbox.h>
+
+#include "filemodedlgimpl.h"
+
+FileModeDlgImpl::FileModeDlgImpl(QWidget* parent, QLineEdit* edit)
+ : FileModeDlg(parent)
+{
+ assert(edit);
+ _edit = edit;
+
+ init();
+}
+
+FileModeDlgImpl::~FileModeDlgImpl()
+{
+}
+
+void FileModeDlgImpl::init()
+{
+ bool ok;
+ int mod = _edit->text().toInt(&ok,8);
+
+ if (!ok)
+ mod = 0;
+
+ stickyBitChk->setChecked( mod & 01000 );
+ setGIDChk->setChecked( mod & 02000 );
+ setUIDChk->setChecked( mod & 04000 );
+
+ ownerExecChk->setChecked( mod & 0100 );
+ ownerWriteChk->setChecked( mod & 0200 );
+ ownerReadChk->setChecked( mod & 0400 );
+
+ groupExecChk->setChecked( mod & 010 );
+ groupWriteChk->setChecked( mod & 020 );
+ groupReadChk->setChecked( mod & 040 );
+
+ othersExecChk->setChecked( mod & 01 );
+ othersWriteChk->setChecked( mod & 02 );
+ othersReadChk->setChecked( mod & 04 );
+
+}
+
+void FileModeDlgImpl::accept()
+{
+ QString s = "";
+
+ s += QString::number(
+ (stickyBitChk->isChecked() ? 1 : 0) +
+ (setGIDChk->isChecked() ? 2 : 0) +
+ (setUIDChk->isChecked() ? 4 : 0)
+ );
+
+ s += QString::number(
+ (ownerExecChk->isChecked() ? 1 : 0) +
+ (ownerWriteChk->isChecked() ? 2 : 0) +
+ (ownerReadChk->isChecked() ? 4 : 0)
+ );
+
+ s += QString::number(
+ (groupExecChk->isChecked() ? 1 : 0) +
+ (groupWriteChk->isChecked() ? 2 : 0) +
+ (groupReadChk->isChecked() ? 4 : 0)
+ );
+
+ s += QString::number(
+ (othersExecChk->isChecked() ? 1 : 0) +
+ (othersWriteChk->isChecked() ? 2 : 0) +
+ (othersReadChk->isChecked() ? 4 : 0)
+ );
+
+ // it's an octal number so start with a 0
+ // but remove all the other trailing 0's
+ s = QString::number( s.toInt());
+ s = "0"+s;
+
+ _edit->setText(s);
+
+
+
+ FileModeDlg::accept();
+}
+
+#include "filemodedlgimpl.moc"
+
diff --git a/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.h b/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.h
new file mode 100644
index 00000000..e855d288
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/filemodedlgimpl.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ filemodedlgimpl.cpp - description
+ -------------------
+ begin : Thu Jul 18 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef FILEMODEDLGIMPL_H
+#define FILEMODEDLGIMPL_H
+
+/**
+ *@author Jan Schäfer
+ */
+
+#include "filemodedlg.h"
+
+class QLineEdit;
+
+/**
+ * Small dialog to change the UNIX access rights
+ * It is called with a QLineEdit as parameter.
+ * The class then takes the text from the QLineEdit and
+ * interprets it as an oktal UNIX access right string
+ * e.g. 0744
+ * After the user has changed the access rights with the dialog
+ * the class sets the new access rights as a new octal string
+ * of the QLineEdit
+ * Implements the filemodedlg.ui interface
+ **/
+class FileModeDlgImpl : public FileModeDlg
+{
+Q_OBJECT
+public:
+ FileModeDlgImpl(QWidget* parent, QLineEdit* edit);
+ ~FileModeDlgImpl();
+protected:
+ QLineEdit* _edit;
+ void init();
+protected slots:
+ virtual void accept();
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui b/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui
new file mode 100644
index 00000000..1722dd18
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui
@@ -0,0 +1,445 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>GroupSelectDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>GroupSelectDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>406</width>
+ <height>370</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Select Groups</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>285</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <number>0</number>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="accel">
+ <number>0</number>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QFrame" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>frame16</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer90</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>groupBox87</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Selec&amp;t Groups</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>GID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>groupListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="1">
+ <property name="name">
+ <cstring>accessBtnGrp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Acc&amp;ess</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>defaultRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Default</string>
+ </property>
+ <property name="accel">
+ <number>8388676</number>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>readRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Read access</string>
+ </property>
+ <property name="accel">
+ <number>8388690</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>writeRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Write access</string>
+ </property>
+ <property name="accel">
+ <number>8388695</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>adminRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Admin access</string>
+ </property>
+ <property name="accel">
+ <number>8388673</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>noAccessRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;No access at all</string>
+ </property>
+ <property name="accel">
+ <number>8388686</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="1">
+ <property name="name">
+ <cstring>kindBtnGrp</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Kind of Group</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>unixRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;UNIX group</string>
+ </property>
+ <property name="accel">
+ <number>8388693</number>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>nisRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>NI&amp;S group</string>
+ </property>
+ <property name="accel">
+ <number>8388691</number>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>bothRadio</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>UNIX and NIS gr&amp;oup</string>
+ </property>
+ <property name="accel">
+ <number>8388687</number>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>GroupSelectDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>GroupSelectDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>groupListView</tabstop>
+ <tabstop>defaultRadio</tabstop>
+ <tabstop>bothRadio</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in declaration">qstringlist.h</include>
+ <include location="local" impldecl="in implementation">passwd.h</include>
+ <include location="local" impldecl="in implementation">groupselectdlg.ui.h</include>
+</includes>
+<variables>
+ <variable>QString groupKind;</variable>
+ <variable>int access;</variable>
+ <variable>QStringList selectedGroups;</variable>
+</variables>
+<slots>
+ <slot>init( const QStringList &amp; specifiedGroups )</slot>
+ <slot access="protected">accept()</slot>
+ <slot returnType="QStringList">getSelectedGroups()</slot>
+ <slot returnType="int">getAccess()</slot>
+ <slot returnType="QString">getGroupKind()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui.h b/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui.h
new file mode 100644
index 00000000..090e270f
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/groupselectdlg.ui.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void GroupSelectDlg::init(const QStringList & specifiedGroups)
+{
+ QStringList unixGroups = getUnixGroups();
+
+ for (QStringList::Iterator it = unixGroups.begin(); it != unixGroups.end(); ++it)
+ {
+ if ( ! specifiedGroups.contains(*it))
+ new QListViewItem(groupListView, *it, QString::number(getGroupGID(*it)));
+ }
+}
+
+void GroupSelectDlg::accept()
+{
+ QListViewItemIterator it( groupListView);
+
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->isSelected() )
+ selectedGroups << it.current()->text(0);
+ }
+
+ access = accessBtnGrp->id(accessBtnGrp->selected());
+
+ if (unixRadio->isChecked())
+ groupKind = "+";
+ else
+ if (nisRadio->isChecked())
+ groupKind = "&";
+ else
+ if (bothRadio->isChecked())
+ groupKind = "@";
+
+ QDialog::accept();
+}
+
+
+
+
+QStringList GroupSelectDlg::getSelectedGroups()
+{
+ return selectedGroups;
+}
+
+
+int GroupSelectDlg::getAccess()
+{
+ return access;
+}
+
+
+QString GroupSelectDlg::getGroupKind()
+{
+ return groupKind;
+}
diff --git a/filesharing/advanced/kcm_sambaconf/hi16-app-kcmsambaconf.png b/filesharing/advanced/kcm_sambaconf/hi16-app-kcmsambaconf.png
new file mode 100644
index 00000000..e587589b
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/hi16-app-kcmsambaconf.png
Binary files differ
diff --git a/filesharing/advanced/kcm_sambaconf/hiddenfileview.cpp b/filesharing/advanced/kcm_sambaconf/hiddenfileview.cpp
new file mode 100644
index 00000000..988e2594
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/hiddenfileview.cpp
@@ -0,0 +1,610 @@
+/***************************************************************************
+ hiddenfileview.cpp - description
+ -------------------
+ begin : Wed Jan 1 2003
+ copyright : (C) 2003 by Jan Sch�er
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <assert.h>
+
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qgroupbox.h>
+#include <qcursor.h>
+
+
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <krestrictedline.h>
+#include <klocale.h>
+#include <kfiledetailview.h>
+#include <kdirlister.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+
+#include "hiddenfileview.h"
+#include "sharedlgimpl.h"
+#include "sambashare.h"
+
+
+#define COL_NAME 0
+#define COL_HIDDEN 1
+#define COL_VETO 2
+#define COL_VETO_OPLOCK 3
+#define COL_SIZE 4
+#define COL_DATE 5
+#define COL_PERM 6
+#define COL_OWNER 7
+#define COL_GROUP 8
+
+#define HIDDENTABINDEX 5
+
+HiddenListViewItem::HiddenListViewItem( QListView *parent, KFileItem *fi, bool hidden=false, bool veto=false, bool vetoOplock=false )
+ : QMultiCheckListItem( parent )
+{
+ setPixmap( COL_NAME, fi->pixmap(KIcon::SizeSmall));
+
+ setText( COL_NAME, fi->text() );
+ setText( COL_SIZE, KGlobal::locale()->formatNumber( fi->size(), 0));
+ setText( COL_DATE, fi->timeString() );
+ setText( COL_PERM, fi->permissionsString() );
+ setText( COL_OWNER, fi->user() );
+ setText( COL_GROUP, fi->group() );
+
+ setOn(COL_HIDDEN,hidden);
+ setOn(COL_VETO,veto);
+ setOn(COL_VETO_OPLOCK,vetoOplock);
+
+ _fileItem = fi;
+}
+
+HiddenListViewItem::~HiddenListViewItem()
+{
+}
+
+KFileItem* HiddenListViewItem::getFileItem()
+{
+ return _fileItem;
+}
+
+
+void HiddenListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ QColorGroup _cg = cg;
+
+ if (isOn(COL_VETO))
+ _cg.setColor(QColorGroup::Base,lightGray);
+
+ if (isOn(COL_HIDDEN))
+ _cg.setColor(QColorGroup::Text,gray);
+
+ QMultiCheckListItem::paintCell(p, _cg, column, width, alignment);
+}
+
+
+
+
+HiddenFileView::HiddenFileView(ShareDlgImpl* shareDlg, SambaShare* share)
+{
+ _share = share;
+ _dlg = shareDlg;
+
+ _hiddenActn = new KToggleAction(i18n("&Hide"));
+ _vetoActn = new KToggleAction(i18n("&Veto"));
+ _vetoOplockActn = new KToggleAction(i18n("&Veto Oplock"));
+
+ initListView();
+
+ _dlg->hiddenChk->setTristate(true);
+ _dlg->vetoChk->setTristate(true);
+
+ connect( _dlg->hiddenChk, SIGNAL(toggled(bool)), this, SLOT( hiddenChkClicked(bool) ));
+ connect( _dlg->vetoChk, SIGNAL(toggled(bool)), this, SLOT( vetoChkClicked(bool) ));
+ connect( _dlg->vetoOplockChk, SIGNAL(toggled(bool)), this, SLOT( vetoOplockChkClicked(bool) ));
+
+ _dlg->hiddenEdit->setText( _share->getValue("hide files") );
+ connect( _dlg->hiddenEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateView()));
+
+ _dlg->vetoEdit->setText( _share->getValue("veto files") );
+ connect( _dlg->vetoEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateView()));
+
+ _dlg->vetoOplockEdit->setText( _share->getValue("veto oplock files") );
+ connect( _dlg->vetoOplockEdit, SIGNAL(textChanged(const QString &)), this, SLOT(updateView()));
+
+// new QLabel(i18n("Hint")+" : ",grid);
+// new QLabel(i18n("You have to separate the entries with a '/'. You can use the wildcards '*' and '?'"),grid);
+// new QLabel(i18n("Example")+" : ",grid);
+// new QLabel(i18n("*.tmp/*SECRET*/.*/file?.*/"),grid);
+
+ _dir = new KDirLister(true);
+ _dir->setShowingDotFiles(true);
+
+ connect( _dir, SIGNAL(newItems(const KFileItemList &)),
+ this, SLOT(insertNewFiles(const KFileItemList &)));
+
+ connect( _dir, SIGNAL(deleteItem(KFileItem*)),
+ this, SLOT(deleteItem(KFileItem*)));
+
+ connect( _dir, SIGNAL(refreshItems(const KFileItemList &)),
+ this, SLOT(refreshItems(const KFileItemList &)));
+
+ connect( _hiddenActn, SIGNAL(toggled(bool)), this, SLOT(hiddenChkClicked(bool)));
+ connect( _vetoActn, SIGNAL(toggled(bool)), this, SLOT(vetoChkClicked(bool)));
+ connect( _vetoOplockActn, SIGNAL(toggled(bool)), this, SLOT(vetoOplockChkClicked(bool)));
+}
+
+void HiddenFileView::initListView()
+{
+ _dlg->hiddenListView->setMultiSelection(true);
+ _dlg->hiddenListView->setSelectionMode(QListView::Extended);
+ _dlg->hiddenListView->setAllColumnsShowFocus(true);
+
+ _hiddenList = createRegExpList(_share->getValue("hide files"));
+ _vetoList = createRegExpList(_share->getValue("veto files"));
+ _vetoOplockList = createRegExpList(_share->getValue("veto oplock files"));
+
+ _popup = new KPopupMenu(_dlg->hiddenListView);
+
+ _hiddenActn->plug(_popup);
+ _vetoActn->plug(_popup);
+ _vetoOplockActn->plug(_popup);
+
+ connect( _dlg->hiddenListView, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
+ connect( _dlg->hiddenListView, SIGNAL(contextMenu(KListView*,QListViewItem*,const QPoint&)),
+ this, SLOT(showContextMenu()));
+
+ connect( _dlg->hideDotFilesChk, SIGNAL(toggled(bool)), this, SLOT(hideDotFilesChkClicked(bool)));
+ connect( _dlg->hideUnreadableChk, SIGNAL(toggled(bool)), this, SLOT(hideUnreadableChkClicked(bool)));
+
+ connect( _dlg->hiddenListView, SIGNAL(mouseButtonPressed(int,QListViewItem*,const QPoint &,int)),
+ this, SLOT(slotMouseButtonPressed(int,QListViewItem*,const QPoint &,int)));
+}
+
+HiddenFileView::~HiddenFileView()
+{
+ delete _dir;
+}
+
+void HiddenFileView::load()
+{
+ if (_dlg->hiddenListView)
+ _dlg->hiddenListView->clear();
+
+ _dir->openURL( _dlg->pathUrlRq->url() );
+}
+
+void HiddenFileView::save()
+{
+ QString s = _dlg->hiddenEdit->text().stripWhiteSpace();
+ // its important that the string ends with an '/'
+ // otherwise Samba won't recognize the last entry
+ if ( (!s.isEmpty()) && (s.right(1)!="/"))
+ s+="/";
+ _share->setValue("hide files", s);
+
+ s = _dlg->vetoEdit->text().stripWhiteSpace();
+ // its important that the string ends with an '/'
+ // otherwise Samba won't recognize the last entry
+ if ( (!s.isEmpty()) && (s.right(1)!="/"))
+ s+="/";
+ _share->setValue("veto files", s);
+
+ s = _dlg->vetoOplockEdit->text().stripWhiteSpace();
+ // its important that the string ends with an '/'
+ // otherwise Samba won't recognize the last entry
+ if ( (!s.isEmpty()) && (s.right(1)!="/"))
+ s+="/";
+ _share->setValue("veto oplock files", s);
+
+}
+
+void HiddenFileView::insertNewFiles(const KFileItemList &newone)
+{
+ if ( newone.isEmpty() )
+ return;
+
+ KFileItem *tmp;
+
+ int j=0;
+
+ for (KFileItemListIterator it(newone); (tmp = it.current()); ++it)
+ {
+ j++;
+
+ bool hidden = matchHidden(tmp->text());
+ bool veto = matchVeto(tmp->text());
+ bool vetoOplock = matchVetoOplock(tmp->text());
+
+ new HiddenListViewItem( _dlg->hiddenListView, tmp, hidden, veto, vetoOplock );
+
+ }
+}
+
+
+
+void HiddenFileView::columnClicked(int column) {
+
+
+ switch (column) {
+ case COL_HIDDEN : hiddenChkClicked( !_dlg->hiddenChk->isOn() );break;
+ case COL_VETO : vetoChkClicked( !_dlg->vetoChk->isOn() );break;
+ case COL_VETO_OPLOCK : vetoOplockChkClicked( !_dlg->vetoOplockChk->isOn() );break;
+ default : break;
+ }
+}
+
+void HiddenFileView::deleteItem( KFileItem *fileItem )
+{
+ HiddenListViewItem* item;
+ for (item = dynamic_cast<HiddenListViewItem*>(_dlg->hiddenListView->firstChild());item;
+ item = dynamic_cast<HiddenListViewItem*>(item->nextSibling()))
+ {
+ if (item->getFileItem() == fileItem)
+ {
+ delete item;
+ break;
+ }
+ }
+
+}
+
+void HiddenFileView::refreshItems( const KFileItemList& /*items*/ )
+{
+ updateView();
+}
+
+
+void HiddenFileView::showContextMenu()
+{
+ _popup->exec(QCursor::pos());
+}
+
+
+void HiddenFileView::selectionChanged()
+{
+ bool veto = false;
+ bool noVeto = false;
+ bool hide = false;
+ bool noHide = false;
+ bool vetoOplock = false;
+ bool noVetoOplock = false;
+
+ int n = 0;
+
+ HiddenListViewItem* item;
+ for (item = static_cast<HiddenListViewItem*>(_dlg->hiddenListView->firstChild());item;
+ item = static_cast<HiddenListViewItem*>(item->nextSibling()))
+ {
+ if (!item->isSelected())
+ continue;
+
+ n++;
+
+ if (item->isOn(COL_VETO))
+ veto = true;
+ else
+ noVeto = true;
+
+ if (item->isOn(COL_VETO_OPLOCK))
+ vetoOplock = true;
+ else
+ noVetoOplock = true;
+
+ if (item->isOn(COL_HIDDEN))
+ hide = true;
+ else
+ noHide = true;
+ }
+
+
+ _dlg->selGrpBx->setEnabled(n>0);
+
+ if (veto && noVeto)
+ {
+ _dlg->vetoChk->setTristate(true);
+ _dlg->vetoChk->setNoChange();
+ _dlg->vetoChk->update();
+ }
+ else
+ {
+ _dlg->vetoChk->setTristate(false);
+ _dlg->vetoChk->setChecked(veto);
+ }
+
+ if (vetoOplock && noVetoOplock)
+ {
+ _dlg->vetoOplockChk->setTristate(true);
+ _dlg->vetoOplockChk->setNoChange();
+ _dlg->vetoOplockChk->update();
+ }
+ else
+ {
+ _dlg->vetoOplockChk->setTristate(false);
+ _dlg->vetoOplockChk->setChecked(vetoOplock);
+ }
+
+
+ if (hide && noHide)
+ {
+ _dlg->hiddenChk->setTristate(true);
+ _dlg->hiddenChk->setNoChange();
+ _dlg->hiddenChk->update();
+ }
+ else
+ {
+ _dlg->hiddenChk->setTristate(false);
+ _dlg->hiddenChk->setChecked(hide);
+ }
+}
+
+void HiddenFileView::checkBoxClicked(QCheckBox* chkBox,KToggleAction* action,QLineEdit* edit, int column,QPtrList<QRegExp> & reqExpList,bool b) {
+ // We don't save the old state so
+ // disable the tristate mode
+ chkBox->setTristate(false);
+ action->setChecked(b);
+ chkBox->setChecked(b);
+
+ HiddenListViewItem* item;
+ for (item = static_cast<HiddenListViewItem*>(_dlg->hiddenListView->firstChild());item;
+ item = static_cast<HiddenListViewItem*>(item->nextSibling()))
+ {
+ if (!item->isSelected())
+ continue;
+
+ if (b == item->isOn(column))
+ continue;
+
+ if (!b) {
+ QRegExp* rx = getRegExpListMatch(item->text(0),reqExpList);
+
+ // Perhaps the file was hidden because it started with a dot
+ if (!rx && item->text(0)[0]=='.' && _dlg->hideDotFilesChk->isChecked()) {
+ int result = KMessageBox::questionYesNo(_dlg,i18n(
+ "<qt>Some files you have selected are hidden because they start with a dot; "
+ "do you want to uncheck all files starting with a dot?</qt>"),i18n("Files Starting With Dot"),i18n("Uncheck Hidden"), i18n("Keep Hidden"));
+
+ if (result == KMessageBox::No) {
+ QPtrList<HiddenListViewItem> lst = getMatchingItems(QRegExp(".*",false,true));
+ deselect(lst);
+ } else {
+ _dlg->hideDotFilesChk->setChecked(false);
+ }
+ continue;
+ } else {
+ if (rx) {
+ // perhaps it is matched by a wildcard string
+ QString p = rx->pattern();
+ if ( p.find("*") > -1 ||
+ p.find("?") > -1 )
+ {
+ // TODO after message freeze: why show three times the wildcard string? Once should be enough.
+ // TODO remove <b></b> and use <qt> instead
+ int result = KMessageBox::questionYesNo(_dlg,i18n(
+ "<b></b>Some files you have selected are matched by the wildcarded string <b>'%1'</b>; "
+ "do you want to uncheck all files matching <b>'%1'</b>?").arg(rx->pattern()).arg(rx->pattern()).arg(rx->pattern()),
+ i18n("Wildcarded String"),i18n("Uncheck Matches"),i18n("Keep Selected"));
+
+ QPtrList<HiddenListViewItem> lst = getMatchingItems( *rx );
+
+ if (result == KMessageBox::No) {
+ deselect(lst);
+ } else {
+ setState(lst,column,false);
+ reqExpList.remove(rx);
+ updateEdit(edit, reqExpList);
+ }
+ continue;
+ } else {
+ reqExpList.remove(rx);
+ updateEdit(edit, reqExpList);
+ }
+ }
+ }
+ }
+ else {
+ reqExpList.append( new QRegExp(item->text(0)) );
+ updateEdit(edit, reqExpList);
+ }
+
+ item->setOn(column,b);
+ }
+
+ _dlg->hiddenListView->update();
+}
+
+void HiddenFileView::hiddenChkClicked(bool b)
+{
+ checkBoxClicked(_dlg->hiddenChk, _hiddenActn, _dlg->hiddenEdit, COL_HIDDEN,_hiddenList,b);
+
+}
+
+void HiddenFileView::vetoOplockChkClicked(bool b) {
+ checkBoxClicked(_dlg->vetoOplockChk, _vetoOplockActn, _dlg->vetoOplockEdit, COL_VETO_OPLOCK,_vetoOplockList,b);
+}
+
+void HiddenFileView::vetoChkClicked(bool b)
+{
+ checkBoxClicked(_dlg->vetoChk, _vetoActn, _dlg->vetoEdit, COL_VETO,_vetoList,b);
+}
+
+/**
+ * Sets the text of the QLineEdit edit to the entries of the passed QRegExp-List
+ **/
+void HiddenFileView::updateEdit(QLineEdit* edit, QPtrList<QRegExp> & lst)
+{
+ QString s="";
+
+ QRegExp* rx;
+ for(rx = static_cast<QRegExp*>(lst.first()); rx;
+ rx = static_cast<QRegExp*>(lst.next()) )
+ {
+ s+= rx->pattern()+QString("/");
+ }
+
+ edit->setText(s);
+}
+
+
+void HiddenFileView::setState(QPtrList<HiddenListViewItem> & lst, int column, bool b) {
+ HiddenListViewItem* item;
+ for(item = static_cast<HiddenListViewItem*>(lst.first()); item;
+ item = static_cast<HiddenListViewItem*>(lst.next()) )
+ {
+ item->setOn(column,b);
+ }
+}
+
+
+void HiddenFileView::deselect(QPtrList<HiddenListViewItem> & lst)
+{
+ HiddenListViewItem* item;
+ for(item = static_cast<HiddenListViewItem*>(lst.first()); item;
+ item = static_cast<HiddenListViewItem*>(lst.next()) )
+ {
+ item->setSelected(false);
+ }
+}
+
+
+QPtrList<HiddenListViewItem> HiddenFileView::getMatchingItems(const QRegExp & rx)
+{
+ QPtrList<HiddenListViewItem> lst;
+
+ HiddenListViewItem* item;
+ for (item = static_cast<HiddenListViewItem*>(_dlg->hiddenListView->firstChild());item;
+ item = static_cast<HiddenListViewItem*>(item->nextSibling()))
+ {
+ if (rx.exactMatch(item->text(0)))
+ lst.append(item);
+ }
+
+ return lst;
+}
+
+void HiddenFileView::updateView()
+{
+ _hiddenList = createRegExpList(_dlg->hiddenEdit->text());
+ _vetoList = createRegExpList(_dlg->vetoEdit->text());
+ _vetoOplockList = createRegExpList(_dlg->vetoOplockEdit->text());
+
+ HiddenListViewItem* item;
+ for (item = static_cast<HiddenListViewItem*>(_dlg->hiddenListView->firstChild());item;
+ item = static_cast<HiddenListViewItem*>(item->nextSibling()))
+ {
+ item->setOn(COL_HIDDEN,matchHidden(item->text(0)));
+ item->setOn(COL_VETO,matchVeto(item->text(0)));
+ item->setOn(COL_VETO_OPLOCK,matchVetoOplock(item->text(0)));
+ }
+
+ _dlg->hiddenListView->repaint();
+}
+
+
+QPtrList<QRegExp> HiddenFileView::createRegExpList(const QString & s)
+{
+ QPtrList<QRegExp> lst;
+ bool cs = _share->getBoolValue("case sensitive");
+
+ if (!s.isEmpty())
+ {
+ QStringList l = QStringList::split("/",s);
+
+ for ( QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
+ lst.append( new QRegExp(*it,cs,true) );
+ }
+ }
+
+ return lst;
+}
+
+bool HiddenFileView::matchHidden(const QString & s)
+{
+ QPtrList<QRegExp> hiddenList(_hiddenList);
+
+ if (_dlg->hideDotFilesChk->isChecked())
+ hiddenList.append( new QRegExp(".*",false,true) );
+
+ return matchRegExpList(s,hiddenList);
+}
+
+bool HiddenFileView::matchVeto(const QString & s)
+{
+ return matchRegExpList(s,_vetoList);
+}
+
+bool HiddenFileView::matchVetoOplock(const QString & s)
+{
+ return matchRegExpList(s,_vetoOplockList);
+}
+
+bool HiddenFileView::matchRegExpList(const QString & s, QPtrList<QRegExp> & lst)
+{
+ if (getRegExpListMatch(s,lst))
+ return true;
+ else
+ return false;
+}
+
+
+QRegExp* HiddenFileView::getHiddenMatch(const QString & s)
+{
+ return getRegExpListMatch(s,_hiddenList);
+}
+
+QRegExp* HiddenFileView::getVetoMatch(const QString & s)
+{
+ return getRegExpListMatch(s,_vetoList);
+}
+
+QRegExp* HiddenFileView::getRegExpListMatch(const QString & s, QPtrList<QRegExp> & lst)
+{
+ QRegExp* rx;
+
+ for ( rx = lst.first(); rx; rx = lst.next() )
+ {
+ if (rx->exactMatch(s))
+ return rx;
+ }
+
+ return 0L;
+}
+
+void HiddenFileView::hideDotFilesChkClicked(bool)
+{
+ updateView();
+}
+
+void HiddenFileView::hideUnreadableChkClicked(bool)
+{
+ updateView();
+}
+
+void HiddenFileView::slotMouseButtonPressed( int, QListViewItem*, const QPoint&, int c ) {
+ columnClicked(c);
+}
+
+
+#include "hiddenfileview.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/hiddenfileview.h b/filesharing/advanced/kcm_sambaconf/hiddenfileview.h
new file mode 100644
index 00000000..c15b772f
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/hiddenfileview.h
@@ -0,0 +1,140 @@
+/***************************************************************************
+ hiddenfileview.h - description
+ -------------------
+ begin : Wed Jan 1 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef _HIDDENFILEVIEW_H_
+#define _HIDDENFILEVIEW_H_
+
+#include <kfileitem.h>
+#include <klistview.h>
+
+#include "qmultichecklistitem.h"
+
+class KDirLister;
+class QRegExp;
+class ShareDlgImpl;
+class SambaShare;
+
+class HiddenListViewItem : public QMultiCheckListItem
+{
+Q_OBJECT
+public:
+ HiddenListViewItem( QListView *parent, KFileItem *fi, bool hidden, bool veto, bool vetoOplock );
+ ~HiddenListViewItem();
+
+ virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+
+ KFileItem* getFileItem();
+protected:
+ KFileItem *_fileItem;
+};
+
+class KToggleAction;
+class KPopupMenu;
+class ShareDlgImpl;
+
+
+/**
+ * Widget which shows a list of files
+ * from a directory.
+ * It gets the directory from the SambaShare
+ * It also interprets the hidden files parameter
+ * of the SambaShare an offers the possibility of
+ * selecting the files which should be hidden
+ **/
+class HiddenFileView : public QObject
+{
+Q_OBJECT
+
+public:
+ HiddenFileView(ShareDlgImpl* shareDlg, SambaShare* share);
+ ~HiddenFileView();
+
+ /**
+ * Load the values from the share and show them
+ **/
+ void load();
+
+ /**
+ * Save changings to the share
+ **/
+ void save();
+
+protected:
+ SambaShare* _share;
+ ShareDlgImpl* _dlg;
+
+ KDirLister* _dir;
+ QPtrList<QRegExp> _hiddenList;
+ QPtrList<QRegExp> _vetoList;
+ QPtrList<QRegExp> _vetoOplockList;
+
+ KToggleAction* _hiddenActn;
+ KToggleAction* _vetoActn;
+ KToggleAction* _vetoOplockActn;
+
+ KPopupMenu* _popup;
+
+ void initListView();
+
+ QPtrList<QRegExp> createRegExpList(const QString & s);
+ bool matchHidden(const QString & s);
+ bool matchVeto(const QString & s);
+ bool matchVetoOplock(const QString & s);
+ bool matchRegExpList(const QString & s, QPtrList<QRegExp> & lst);
+
+ QRegExp* getHiddenMatch(const QString & s);
+ QRegExp* getVetoMatch(const QString & s);
+ QRegExp* getRegExpListMatch(const QString & s, QPtrList<QRegExp> & lst);
+
+ QPtrList<HiddenListViewItem> getMatchingItems(const QRegExp & rx);
+
+ void setState(QPtrList<HiddenListViewItem> & lst,int column, bool b);
+ void deselect(QPtrList<HiddenListViewItem> & lst);
+
+ void updateEdit(QLineEdit* edit, QPtrList<QRegExp> & lst);
+
+protected slots:
+ // slots for KDirListener :
+ void insertNewFiles(const KFileItemList &newone);
+ void deleteItem( KFileItem *_fileItem );
+ void refreshItems( const KFileItemList& items );
+
+ void selectionChanged();
+ void hiddenChkClicked(bool b);
+ void vetoChkClicked(bool b);
+ void vetoOplockChkClicked(bool b);
+ void checkBoxClicked(QCheckBox* chkBox,KToggleAction* action,QLineEdit* edit,int column,QPtrList<QRegExp> &reqExpList,bool b);
+ void columnClicked(int column);
+ void showContextMenu();
+ void updateView();
+ void hideDotFilesChkClicked(bool);
+ void hideUnreadableChkClicked(bool);
+ void slotMouseButtonPressed( int button, QListViewItem * item, const QPoint & pos, int c );
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui b/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui
new file mode 100644
index 00000000..c928a409
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui
@@ -0,0 +1,239 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>JoinDomainDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>JoinDomainDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>427</width>
+ <height>215</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Join Domain</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>domainEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>domainControllerEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>usernameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Verify:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>verifyEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel5_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>usernameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Domain co&amp;ntroller:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>domainControllerEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Domain:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>domainEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="3" column="1">
+ <property name="name">
+ <cstring>passwordEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="4" column="1">
+ <property name="name">
+ <cstring>verifyEdit</cstring>
+ </property>
+ </widget>
+ <widget class="Line" row="6" column="0" rowspan="1" colspan="2">
+ <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>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer65</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>JoinDomainDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>JoinDomainDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>domainEdit</tabstop>
+ <tabstop>domainControllerEdit</tabstop>
+ <tabstop>usernameEdit</tabstop>
+ <tabstop>passwordEdit</tabstop>
+ <tabstop>verifyEdit</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">kpassdlg.h</include>
+ <include location="global" impldecl="in implementation">kmessagebox.h</include>
+ <include location="global" impldecl="in implementation">klocale.h</include>
+ <include location="local" impldecl="in implementation">joindomaindlg.ui.h</include>
+</includes>
+<slots>
+ <slot>accept()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui.h b/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui.h
new file mode 100644
index 00000000..bcc5fe53
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/joindomaindlg.ui.h
@@ -0,0 +1,17 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+void JoinDomainDlg::accept() {
+ if (passwordEdit->text() != verifyEdit->text()) {
+ KMessageBox::sorry(this, "Sorry", "You entered two different passwords. Please try again.");
+ return;
+ }
+
+ QDialog::accept();
+}
diff --git a/filesharing/advanced/kcm_sambaconf/kcminterface.ui b/filesharing/advanced/kcm_sambaconf/kcminterface.ui
new file mode 100644
index 00000000..0c8b31b3
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcminterface.ui
@@ -0,0 +1,8507 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KcmInterface</class>
+<comment>
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+</comment>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KcmInterface</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>714</width>
+ <height>578</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>mainTab</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;
+Here you can edit the Samba users, stored in the smbpasswd file.
+&lt;p&gt;
+Every Samba user must have a corresponding UNIX user.
+On the right you can see all UNIX users, stored in the passwd file and not configured as Samba users.
+You can see the Samba users on the left-hand side.
+&lt;p&gt;
+To add new Samba users simply press the &lt;em&gt;&amp;lt; add&lt;/em&gt; button.
+The selected UNIX users will then become Samba users and will be
+removed from the UNIX users list (but they will remain UNIX users).
+&lt;p&gt;
+To remove Samba users click the &lt;em&gt;&amp;gt; remove&lt;/em&gt; button.
+The selected Samba users will be removed from the smbpasswd file
+and reappear on the right-hand side, as UNIX users which are not Samba users.
+&lt;/qt&gt;</string>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Base Settings</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout38</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_6</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>MShape</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>MShadow</enum>
+ </property>
+ <property name="text">
+ <string>Samba config file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>configUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>configUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>loadBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Load</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Server Identification</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Wor&amp;kgroup:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>workgroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>workgroupEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter here the name of your workgroup/domain.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>serverStringEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>netbiosNameEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Server strin&amp;g:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>serverStringEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_6</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>NetBIOS name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>netbiosNameEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>securityLevelBtnGrp</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Securit&amp;y Level</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>shareRadio</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Share</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>userRadio</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>User</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>serverRadio</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Server</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>domainRadio</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Domai&amp;n</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>adsRadio</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>ADS</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>securityLevelHelpLbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Use the &lt;i&gt;share&lt;/i&gt; security level if you have a home network or a small office network.&lt;br&gt; It allows everyone to read all share names before a login is required.</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ <property name="indent">
+ <number>5</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox12</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Further Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>passwordServerLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Password server address/name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordServerEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>passwordServerEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>realmLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Real&amp;m:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>realmEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>allowGuestLoginsChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Allo&amp;w guest logins</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="1">
+ <property name="name">
+ <cstring>layout39</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel6_5</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Guest acc&amp;ount:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer44</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>realmEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox10</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Help</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>For detailed help about every option please look at:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="KURLLabel">
+ <property name="name">
+ <cstring>KURLLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>man:smb.conf</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer38</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer36</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>shareTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Shares</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </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>Path</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Comment</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Properties</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>shareListView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout54</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>editDefaultShareBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Edit Defau&amp;lts...</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>addShareBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Add &amp;New Share...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>editShareBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Edit Share...</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeShareBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;move Share</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Prin&amp;ters</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </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>Printer</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Comment</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Properties</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>printerListView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout53</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>editDefaultPrinterBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Edit Defau&amp;lts</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer21_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>addPrinterBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Add Ne&amp;w Printer</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>editPrinterBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Edit Pri&amp;nter</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removePrinterBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;move Printer</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Users</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout56</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox51_2</cstring>
+ </property>
+ <property name="title">
+ <string>Sa&amp;mba Users</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </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>UID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>No Password</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>sambaUsersListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout55</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer71_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>addSambaUserBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer71</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>removeSambaUserBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rem&amp;ove</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer71_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox52_2</cstring>
+ </property>
+ <property name="title">
+ <string>UNI&amp;X Users</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </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>UID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>unixUsersListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>5</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout55</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>sambaUserPasswordBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Chan&amp;ge Password...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>joinADomainBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Join Domain</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer26</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>240</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>advanced</cstring>
+ </property>
+ <attribute name="title">
+ <string>Advan&amp;ced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>advancedFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame26</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout92</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>advancedWarningPixLbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Here you can change advanced options of the SAMBA server.
+Only change something if you know what you are doing.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>advancedDumpTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Advanced Dump</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>advancedTab</cstring>
+ </property>
+ <property name="tabShape">
+ <enum>Rounded</enum>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Security</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget4</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer90</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox66</cstring>
+ </property>
+ <property name="title">
+ <string>PAM</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>obeyPamRestrictionsChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Obey PAM restrictions</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>pamPasswordChangeChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>PAM password change</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>groupBox67</cstring>
+ </property>
+ <property name="title">
+ <string>Other Switches</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allowTrustedDomainsChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>A&amp;llow trusted domains</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>paranoidServerSecurityChk</cstring>
+ </property>
+ <property name="text">
+ <string>Paranoid server security</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox68</cstring>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Auth methods:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>authMethodsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Root director&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rootDirectoryEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_5_2</cstring>
+ </property>
+ <property name="text">
+ <string>I&amp;nterfaces:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>interfacesEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel2_5_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Map to guest:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mapToGuestCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="3">
+ <property name="name">
+ <cstring>bindInterfacesOnlyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Bind interfaces only</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>Never</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bad User</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Bad Password</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>mapToGuestCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Hosts e&amp;quiv:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hostsEquivUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>rootDirectoryEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>authMethodsEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>interfacesEdit</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Spacer37_2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>180</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel6_7_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Algorithmic rid base:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>algorithmicRidBaseSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="6" column="1">
+ <property name="name">
+ <cstring>algorithmicRidBaseSpin</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="6" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Spacer37_2_2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>117</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KURLRequester" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>hostsEquivUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2_3_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Private dir:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>privateDirUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="5" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>privateDirUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Pass&amp;word</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer57</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="2">
+ <property name="name">
+ <cstring>groupBox73</cstring>
+ </property>
+ <property name="title">
+ <string>Migration</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>updateEncryptedChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Update encr&amp;ypted</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox75</cstring>
+ </property>
+ <property name="title">
+ <string>Samba Passwords</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>encryptPasswordsChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>E&amp;ncrypt passwords</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Smb passwd file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>smbPasswdFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>smbPasswdFileUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Passdb bac&amp;kend:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passdbBackendEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>passwdChatEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>passdbBackendEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Passwd chat:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwdChatEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>passwdChatDebugChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Passwd chat debug</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="3">
+ <property name="name">
+ <cstring>TextLabel5_2_3_4_3</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel3_5_3_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Passwd chat timeout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwdChatEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="4" column="2">
+ <property name="name">
+ <cstring>passwdChatTimeoutSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox74</cstring>
+ </property>
+ <property name="title">
+ <string>Password</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_7</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Password level:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordLevelSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel6_3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Min password length:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>minPasswdLengthSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Machine password timeout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>machinePasswordTimeoutSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>machinePasswordTimeoutSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>minPasswdLengthSpin</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="3" column="3">
+ <property name="name">
+ <cstring>Spacer31_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_3_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>nullPasswordsChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Nu&amp;ll passwords</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>passwordLevelSpin</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox76</cstring>
+ </property>
+ <property name="title">
+ <string>UNIX Passwords</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2_3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Passwd program:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwdProgramUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>passwdProgramUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>unixPasswordSyncChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>UNI&amp;X password sync</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Userna&amp;me</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>User&amp;name map:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>usernameMapUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>usernameMapUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6_4_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Username &amp;level:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>usernameLevelSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>usernameLevelSpin</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="maxValue">
+ <number>255</number>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer52</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>hideLocalUsersChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Hide local users</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>restrictAnonymousChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Restrict anon&amp;ymous</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>useRhostsChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Use rhosts</string>
+ </property>
+ </widget>
+ <spacer row="5" column="0">
+ <property name="name">
+ <cstring>spacer53</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>170</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Authenticati&amp;on</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>groupBox13</cstring>
+ </property>
+ <property name="title">
+ <string>Client</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;lient signing:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>clientSigningCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>clientPlaintextAuthChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Client plainte&amp;xt authentication</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>clientLanmanAuthChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Client lanman authentication</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="5" column="1">
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Mandatory</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>clientSigningCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>clientSchannelCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Client channel:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>clientSchannelCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>clientUseSpnegoChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Client use spnego</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>clientNTLMv2AuthChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Client NTLMv&amp;2 authentication</string>
+ </property>
+ </widget>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>spacer54</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox13_2</cstring>
+ </property>
+ <property name="title">
+ <string>Server</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_2_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Server signing:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>serverSigningCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>lanmanAuthChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Lanman authentication</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="5" column="1">
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Mandatory</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Disabled</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>serverSigningCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>serverSchannelCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Server channel:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>serverSchannelCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>useSpnegoChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Use sp&amp;nego</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>ntlmAuthChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>NTLM authentication</string>
+ </property>
+ </widget>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>spacer54_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>120</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer55</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Logging</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>loggingFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox36_2</cstring>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel4_3_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>L&amp;og file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>logFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TextLabel3_3_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>KB</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Ma&amp;x log size:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxLogSizeSpin</cstring>
+ </property>
+ </widget>
+ <spacer row="2" column="3">
+ <property name="name">
+ <cstring>spacer60_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5_3_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;yslog:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>syslogSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>maxLogSizeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_2_2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>76</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Log &amp;level:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logLevelEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>logLevelEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>syslogSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>4</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox48</cstring>
+ </property>
+ <property name="title">
+ <string>S&amp;witches</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>syslogOnlyChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Syslog o&amp;nly</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>statusChk</cstring>
+ </property>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>timestampChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Ti&amp;mestamp</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>microsecondsChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>microseconds</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>debugPidChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Debug pid</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>debugUidChk</cstring>
+ </property>
+ <property name="text">
+ <string>Debu&amp;g uid</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer114_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Tuning</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox37_2</cstring>
+ </property>
+ <property name="title">
+ <string>Modules</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel4_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;load modules:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preloadModulesEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>preloadModulesEdit</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="1">
+ <property name="name">
+ <cstring>groupBox47</cstring>
+ </property>
+ <property name="title">
+ <string>Numbers</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Max smbd processes:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxSmbdProcessesSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_2_2_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Max open files:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxOpenFilesSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>maxSmbdProcessesSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>maxOpenFilesSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer61</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>Spacer60</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox46</cstring>
+ </property>
+ <property name="title">
+ <string>Sizes</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_2_2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Max disk size:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxDiskSizeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_2_2_2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Read si&amp;ze:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>readSizeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_2_2_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Stat cache size:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>statCacheSizeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>readSizeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65536</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>statCacheSizeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>maxDiskSizeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="0" column="3">
+ <property name="name">
+ <cstring>spacer62</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>MB</string>
+ <comment>mega byte</comment>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>maxXmitSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4_2_7_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ma&amp;x xmit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxXmitSpin</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>groupBox45</cstring>
+ </property>
+ <property name="title">
+ <string>Times</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Change notify timeout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>changeNotifyTimeoutSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_3_4</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Keepalive:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>keepaliveSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>changeNotifyTimeoutSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>keepaliveSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Min</string>
+ <comment>minutes</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Deadtime:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deadtimeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>deadtimeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Lp&amp;q cache time:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lpqCacheTimeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>lpqCacheTimeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_3_2_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name cache timeout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameCacheTimeoutSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="4" column="1">
+ <property name="name">
+ <cstring>nameCacheTimeoutSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox44</cstring>
+ </property>
+ <property name="title">
+ <string>Switches</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>getwdCacheChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Getwd cache</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>useMmapChk</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;mmap</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kernelChangeNotifyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Kernel change notif&amp;y</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>hostnameLookupsChk</cstring>
+ </property>
+ <property name="text">
+ <string>H&amp;ostname lookups</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>readRawChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Read ra&amp;w</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>writeRawChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Write raw</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Printing</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox52</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;General</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Total print &amp;jobs:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>totalPrintJobsSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>totalPrintJobsSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>Spacer22</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox51</cstring>
+ </property>
+ <property name="title">
+ <string>Drivers</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>OS&amp;2 driver map:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>os2DriverMapUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3_4</cstring>
+ </property>
+ <property name="text">
+ <string>Printcap na&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>printcapNameUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>printcapNameUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>os2DriverMapUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>printerDriverLbl</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Pri&amp;nter driver file: </string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>printerDriverFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>printerDriverFileUrlRq</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox50</cstring>
+ </property>
+ <property name="title">
+ <string>Commands</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Enumports command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>enumportsCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Addprinter command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addprinterCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Deleteprinter command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deleteprinterCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>addprinterCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>deleteprinterCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>enumportsCommandEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox49</cstring>
+ </property>
+ <property name="title">
+ <string>S&amp;witches</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>loadPrintersChk</cstring>
+ </property>
+ <property name="text">
+ <string>L&amp;oad printers</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>disableSpoolssChk</cstring>
+ </property>
+ <property name="text">
+ <string>Disab&amp;le spools</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>showAddPrinterWizardChk</cstring>
+ </property>
+ <property name="text">
+ <string>Show add printer wi&amp;zard</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer48</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Domain</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="7" column="2">
+ <property name="name">
+ <cstring>spacer58</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>
+ <widget class="KIntSpinBox" row="4" column="1">
+ <property name="name">
+ <cstring>osLevelSpin</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>localMasterChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>L&amp;ocal master</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>domainMasterChk</cstring>
+ </property>
+ <property name="text">
+ <string>Domai&amp;n master</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>domainLogonsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Domain lo&amp;gons</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>preferredMasterChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Preferred &amp;master</string>
+ </property>
+ </widget>
+ <spacer row="4" column="2">
+ <property name="name">
+ <cstring>Spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>323</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>OS &amp;level:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>osLevelSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3_3_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Domain admin group:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>domainAdminGroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3_3_4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Domain guest group:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>domainGuestGroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>domainGuestGroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>domainAdminGroupEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>WINS</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup4</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>7</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>noWinsSupportRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Deactivate &amp;WINS</string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>winsSupportRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Act as a WI&amp;NS server</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>otherWinsRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Use an&amp;other WINS server</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox35</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>WINS Server Settin&amp;gs</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4_2_4_3_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max WINS tt&amp;l:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxWinsTtlSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4_2_5_3_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Min WINS ttl:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>minWinsTtlSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>maxWinsTtlSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>minWinsTtlSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TextLabel6_5_2_4_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TextLabel6_5_2_2_3_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ <spacer row="2" column="3">
+ <property name="name">
+ <cstring>spacer59_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>winsHookLbl</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>WINS hoo&amp;k:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winsHookEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>winsHookEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>dnsProxyChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>DNS prox&amp;y</string>
+ </property>
+ <property name="tristate">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox36</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>WINS Server IP or DNS Name</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>winsServerEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox37</cstring>
+ </property>
+ <property name="title">
+ <string>General Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>WINS partners:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winsPartnersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>winsPartnersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>winsProxyChk</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>WINS pro&amp;xy</string>
+ </property>
+ <property name="tristate">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer60_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>120</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Filenames</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox54</cstring>
+ </property>
+ <property name="title">
+ <string>Ge&amp;neral</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>stripDotChk</cstring>
+ </property>
+ <property name="text">
+ <string>Strip d&amp;ot</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox55</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Mangling</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>mangledStackSpin</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mangled stac&amp;k:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mangledStackSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mangle prefi&amp;x:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>manglePrefixSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>manglePrefixSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>6</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer56</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox53</cstring>
+ </property>
+ <property name="title">
+ <string>Specia&amp;l</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>statCacheChk</cstring>
+ </property>
+ <property name="text">
+ <string>Stat cache</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer61</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>280</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Locking</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox59</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;General</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kernelOplocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Use ker&amp;nel oplocks</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox56</cstring>
+ </property>
+ <property name="title">
+ <string>Direct&amp;ories</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Loc&amp;k directory:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lockDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>lockDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pid director&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>pidDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>pidDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox57</cstring>
+ </property>
+ <property name="title">
+ <string>Lock Spin</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>lockSpinTimeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>lockSpinCountSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>microseconds</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Lock spin count:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lockSpinCountSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Lock spin ti&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lockSpinTimeSpin</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer60</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox58</cstring>
+ </property>
+ <property name="title">
+ <string>Very Advanced</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Oplock break &amp;wait time:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>oplockBreakWaitTimeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>oplockBreakWaitTimeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>milliseconds</string>
+ </property>
+ </widget>
+ <spacer row="0" column="4">
+ <property name="name">
+ <cstring>spacer101</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer59</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>120</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Charset</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox40</cstring>
+ </property>
+ <property name="title">
+ <string>Samba &amp;3.x</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_4_2_2_2_2_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>D&amp;OS charset:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>dosCharsetEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>dosCharsetEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_4_2_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>UNI&amp;X charset:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>unixCharsetEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>unixCharsetEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_4_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Displa&amp;y charset:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>displayCharsetEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>displayCharsetEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>unicodeChk</cstring>
+ </property>
+ <property name="text">
+ <string>U&amp;nicode</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox39</cstring>
+ </property>
+ <property name="title">
+ <string>Samba &amp;2.x</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Character set:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>characterSetEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>characterSetEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>clientCodePageEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Va&amp;lid chars:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>validCharsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>codePageDirUrlRq</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>validCharsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_4_3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Code page directory:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>codePageDirUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>codingSystemEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_4_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Codin&amp;g system:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>codingSystemEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2_3_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Client code page:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>clientCodePageEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer185</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Logon</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox60</cstring>
+ </property>
+ <property name="title">
+ <string>Add Scripts</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Add user script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addUserScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>addUserScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Add user to group script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addUserToGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_5</cstring>
+ </property>
+ <property name="text">
+ <string>Add gr&amp;oup script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>addGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>addUserToGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_6</cstring>
+ </property>
+ <property name="text">
+ <string>Add machine script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addMachineScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>addMachineScriptEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>Spacer64</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>groupBox61</cstring>
+ </property>
+ <property name="title">
+ <string>Delete Scripts</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>deleteUserScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>deleteUserFromGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>deleteGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Delete group script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deleteGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Delete user script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deleteUserScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Delete user from group script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deleteUserFromGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox64</cstring>
+ </property>
+ <property name="title">
+ <string>Primary Group Script</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel7_2_5_3</cstring>
+ </property>
+ <property name="text">
+ <string>Set primar&amp;y group script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>SetPrimaryGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>SetPrimaryGroupScriptEdit</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox62</cstring>
+ </property>
+ <property name="title">
+ <string>Shutdown</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Shutdo&amp;wn script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>shutdownScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Abort shutdown script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>abortShutdownScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>shutdownScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>abortShutdownScriptEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>groupBox63</cstring>
+ </property>
+ <property name="title">
+ <string>Logon</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Logo&amp;n path:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logonPathUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1">
+ <property name="name">
+ <cstring>logonHomeUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel8_2</cstring>
+ </property>
+ <property name="text">
+ <string>Logon ho&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logonHomeUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>logonDriveEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Logon drive:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logonDriveEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>logonPathUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel7_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Lo&amp;gon script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>logonScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>logonScriptEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Socket</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout25</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel15</cstring>
+ </property>
+ <property name="text">
+ <string>Socket address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>socketAddressEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>socketAddressEdit</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox7</cstring>
+ </property>
+ <property name="title">
+ <string>Socket Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>SO_KEEPALIVEChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_&amp;KEEPALIVE</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>SO_SNDBUFChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_S&amp;NDBUF:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="2">
+ <property name="name">
+ <cstring>SO_RCVLOWATSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="2">
+ <property name="name">
+ <cstring>SO_SNDLOWATSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>SO_BROADCASTChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_BROADCAST</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>TCP_NODELAYChk</cstring>
+ </property>
+ <property name="text">
+ <string>TCP_NODELA&amp;Y</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>IPTOS_LOWDELAYChk</cstring>
+ </property>
+ <property name="text">
+ <string>IPTOS_LOWDELAY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1">
+ <property name="name">
+ <cstring>SO_RCVLOWATChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_RCV&amp;LOWAT:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>SO_REUSEADDRChk</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;O_REUSEADDR</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>SO_SNDLOWATChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_SNDLO&amp;WAT:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>SO_SNDBUFSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>IPTOS_THROUGHPUTChk</cstring>
+ </property>
+ <property name="text">
+ <string>IPTOS_THROU&amp;GHPUT</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>SO_RCVBUFChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_RCVBUF:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="2">
+ <property name="name">
+ <cstring>SO_RCVBUFSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="0" column="3">
+ <property name="name">
+ <cstring>Spacer69</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>80</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer62</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>70</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>SSL</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>sslChk</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;nable SSL</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enables or disables the entire SSL mode</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This enables or disables the entire SSL mode. If it is set to no, the SSL-enabled Samba behaves exactly like the non-SSL Samba. If set to yes, it depends on the variables ssl hosts and ssl hosts resign whether an SSL connection will be required.
+
+This is only available if the SSL libraries have been compiled on your system and the configure option --with-ssl was given at configure time.</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>SSLFrame</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout81</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL h&amp;osts:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslHostsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="6" column="1">
+ <property name="name">
+ <cstring>Layout232_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>sslEntropyFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel12_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL entrop&amp;y bytes:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslEntropyBytesSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>sslEntropyBytesSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>sslHostsResignEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="3" column="1">
+ <property name="name">
+ <cstring>sslCACertDirUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="7" column="1">
+ <property name="name">
+ <cstring>sslCiphersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>sslEgdSocketEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>TextLabel14_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL ciphers:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslCiphersEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_7</cstring>
+ </property>
+ <property name="text">
+ <string>SSL hosts resi&amp;gn:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslHostsResignEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>Layout80</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>ssl2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>ssl3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>ssl2or3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>tls1</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>sslVersionCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>sslCompatibilityChk</cstring>
+ </property>
+ <property name="text">
+ <string>SSL co&amp;mpatibility</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer223_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4</cstring>
+ </property>
+ <property name="text">
+ <string>SSL CA certDir:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslCACertDirUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="4" column="1">
+ <property name="name">
+ <cstring>sslCACertFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel11_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL entropy file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslEntropyFileUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel10_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL egd socket:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslEgdSocketEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel15_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL version:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslVersionCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>sslHostsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel5_4_3</cstring>
+ </property>
+ <property name="text">
+ <string>SSL CA certFile:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslCACertFileUrlRq</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout231</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester" row="1" column="3">
+ <property name="name">
+ <cstring>sslClientCertUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>sslRequireClientcertChk</cstring>
+ </property>
+ <property name="text">
+ <string>SSL require clientcert</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TextLabel9_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SSL client key:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslClientKeyUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>sslRequireServercertChk</cstring>
+ </property>
+ <property name="text">
+ <string>SSL re&amp;quire servercert</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="3">
+ <property name="name">
+ <cstring>sslClientKeyUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_6_2</cstring>
+ </property>
+ <property name="text">
+ <string>SS&amp;L server cert:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslServerCertUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>TextLabel8_4</cstring>
+ </property>
+ <property name="text">
+ <string>SSL client cert:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslClientCertUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>sslServerCertUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>sslServerKeyUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7_5</cstring>
+ </property>
+ <property name="text">
+ <string>SSL server &amp;key:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sslServerKeyUrlRq</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer46</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Protocol</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>Spacer63</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox69</cstring>
+ </property>
+ <property name="title">
+ <string>Limits</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>maxTtlSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4_4_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Ma&amp;x mux:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxMuxSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel4_4_2_3_3_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Max tt&amp;l:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxTtlSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="1">
+ <property name="name">
+ <cstring>maxMuxSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <spacer row="1" column="3">
+ <property name="name">
+ <cstring>spacer86</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>330</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>TextLabel6_5_4_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ <comment>seconds</comment>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox70</cstring>
+ </property>
+ <property name="title">
+ <string>S&amp;witches</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>timeServerChk</cstring>
+ </property>
+ <property name="text">
+ <string>Ti&amp;me server</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>largeReadWriteChk</cstring>
+ </property>
+ <property name="text">
+ <string>Lar&amp;ge readwrite</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>unixExtensionsChk</cstring>
+ </property>
+ <property name="text">
+ <string>UNIX extensions</string>
+ </property>
+ <property name="accel">
+ <string>Alt+
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>readBmpxChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Read bmpx</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>groupBox71</cstring>
+ </property>
+ <property name="title">
+ <string>Protocol Versions</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Max protocol:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxProtocolCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Announce version:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>announceVersionEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel3_6_2</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;nnounce as:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>announceAsCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Min protocol:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>minProtocolCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pr&amp;otocol:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>protocolCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer87</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>NT1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CORE</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>COREPLUS</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>protocolCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <item>
+ <property name="text">
+ <string>NT1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CORE</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>COREPLUS</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>maxProtocolCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="1">
+ <item>
+ <property name="text">
+ <string>NT1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>LANMAN1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>CORE</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>COREPLUS</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>minProtocolCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>NT</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>NT Workstation</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>win95</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>WfW</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>announceAsCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>announceVersionEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>4.2</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox72</cstring>
+ </property>
+ <property name="title">
+ <string>Listening SMB Ports</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel5_4_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>SMB ports:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>smbPortsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>smbPortsEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Browsing</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>LM i&amp;nterval:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lmIntervalSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5</cstring>
+ </property>
+ <property name="text">
+ <string>L&amp;M announce:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lmAnnounceCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="3" column="3">
+ <property name="name">
+ <cstring>Spacer40</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="QLineEdit" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>remoteBrowseSyncEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TextLabel27_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>lmIntervalSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel14</cstring>
+ </property>
+ <property name="text">
+ <string>Remote browse s&amp;ync:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>remoteBrowseSyncEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>lmAnnounceCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>browseListChk</cstring>
+ </property>
+ <property name="text">
+ <string>Bro&amp;wse list</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>enhancedBrowsingChk</cstring>
+ </property>
+ <property name="text">
+ <string>Enhanced browsin&amp;g</string>
+ </property>
+ </widget>
+ <spacer row="6" column="0">
+ <property name="name">
+ <cstring>Spacer39</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>300</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;load:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preloadEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>preloadEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Winbind</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel21</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Winbind/Idmap UID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winbindUidEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>winbindUidEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>winbindGidEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel21_2</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind/Idmap &amp;GID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winbindGidEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel22</cstring>
+ </property>
+ <property name="text">
+ <string>Template h&amp;omedir:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>templateHomedirEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>templateHomedirEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel23</cstring>
+ </property>
+ <property name="text">
+ <string>Temp&amp;late shell:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>templateShellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>templateShellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel24</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind separator:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winbindSeparatorEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>winbindSeparatorEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel24_2</cstring>
+ </property>
+ <property name="text">
+ <string>Template primary group:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>templatePrimaryGroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>templatePrimaryGroupEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="3">
+ <property name="name">
+ <cstring>TextLabel27</cstring>
+ </property>
+ <property name="text">
+ <string>Sec</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel25</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind cache ti&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>winbindCacheTimeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="6" column="2">
+ <property name="name">
+ <cstring>winbindCacheTimeSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <spacer row="6" column="4">
+ <property name="name">
+ <cstring>Spacer35</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>370</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox" row="7" column="2" rowspan="1" colspan="2">
+ <item>
+ <property name="text">
+ <string>Windows NT 4</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Windows 2000</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>aclCompatibilityCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel3_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Acl compatibilit&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>aclCompatibilityCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="8" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>winbindEnumUsersChk</cstring>
+ </property>
+ <property name="text">
+ <string>Wi&amp;nbind enum users</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="9" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>winbindEnumGroupsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind enum groups</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="10" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>winbindUseDefaultDomainChk</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind use default domain</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="11" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>winbindEnableLocalAccountsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind enable local accounts</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="12" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>winbindTrustedDomainsOnlyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind trusted domains only</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="13" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>winbindNestedGroupsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Winbind nested groups</string>
+ </property>
+ </widget>
+ <spacer row="14" column="1">
+ <property name="name">
+ <cstring>Spacer34</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>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>NetBIOS</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>Spacer220</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>280</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>NetBIOS sc&amp;ope:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>netbiosScopeEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;NetBIOS aliases:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>netbiosAliasesEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>disableNetbiosChk</cstring>
+ </property>
+ <property name="text">
+ <string>Disab&amp;le netbios</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Na&amp;me resolve order:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameResolveOrderEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>netbiosScopeEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>netbiosAliasesEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>nameResolveOrderEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>150</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>lmhosts host wins bcast</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>VFS</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>hostMsdfsChk</cstring>
+ </property>
+ <property name="text">
+ <string>H&amp;ost msdfs</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer221</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>LDAP</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP suffi&amp;x:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapSuffixEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP machine suffix:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapMachineSuffixEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP user suffix:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapUserSuffixEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP &amp;group suffix:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapGroupSuffixEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP idmap suffix:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapIdmapSuffixEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_5</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP filter:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapFilterEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_6</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP ad&amp;min dn:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapAdminDnEdit</cstring>
+ </property>
+ </widget>
+ <spacer row="12" column="0">
+ <property name="name">
+ <cstring>spacer46</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>130</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="11" column="0">
+ <property name="name">
+ <cstring>ldapDeleteDnChk</cstring>
+ </property>
+ <property name="text">
+ <string>LDAP delete d&amp;n</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="10" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>LDAP s&amp;ync:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapSyncCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="9" column="0">
+ <property name="name">
+ <cstring>TextLabel3_5_4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;LDAP ssl:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ldapSslCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>TextLabel3_4_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Idmap bac&amp;kend:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>idmapBackendEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="8" column="0">
+ <property name="name">
+ <cstring>TextLabel3_4_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>LDAP replication sleep:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>idmapBackendEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="7" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>idmapBackendEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="6" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapAdminDnEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapFilterEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapIdmapSuffixEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapGroupSuffixEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapUserSuffixEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapMachineSuffixEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ldapSuffixEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="8" column="1">
+ <property name="name">
+ <cstring>ldapReplicationSleepSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="8" column="2">
+ <property name="name">
+ <cstring>textLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>milliseconds</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="9" column="1">
+ <item>
+ <property name="text">
+ <string>Off</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Start_tls</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>On</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>ldapSslCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="10" column="1">
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Only</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>ldapSyncCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="9" column="2">
+ <property name="name">
+ <cstring>spacer88</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>330</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Commands</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_5</cstring>
+ </property>
+ <property name="text">
+ <string>Add share c&amp;ommand:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addShareCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Change share command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>changeShareCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>De&amp;lete share command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>deleteShareCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Messa&amp;ge command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>messageCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Dfree co&amp;mmand:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>dfreeCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel1_5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Set &amp;quota command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>setQuotaCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Get quota command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>getQuotaCommandEdit</cstring>
+ </property>
+ </widget>
+ <spacer row="8" column="0">
+ <property name="name">
+ <cstring>spacer52_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>230</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>TextLabel20</cstring>
+ </property>
+ <property name="text">
+ <string>Pa&amp;nic action:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>panicActionEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>messageCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>dfreeCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>setQuotaCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1">
+ <property name="name">
+ <cstring>getQuotaCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="7" column="1">
+ <property name="name">
+ <cstring>panicActionEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>deleteShareCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>changeShareCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>addShareCommandEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Misc</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox12</cstring>
+ </property>
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel17</cstring>
+ </property>
+ <property name="text">
+ <string>Time &amp;offset:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>timeOffsetSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>sourceEnvironmentEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>defaultServiceEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Default service:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>defaultServiceEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Remote a&amp;nnounce:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>remoteAnnounceEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>remoteAnnounceEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel19</cstring>
+ </property>
+ <property name="text">
+ <string>Source environment:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sourceEnvironmentEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>CheckBox68</cstring>
+ </property>
+ <property name="text">
+ <string>Hide &amp;local users</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>timeOffsetSpin</cstring>
+ </property>
+ <property name="maxValue">
+ <number>1440</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TextLabel18</cstring>
+ </property>
+ <property name="text">
+ <string>Min</string>
+ <comment>minurtes</comment>
+ </property>
+ </widget>
+ <spacer row="2" column="3">
+ <property name="name">
+ <cstring>spacer63</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox11</cstring>
+ </property>
+ <property name="title">
+ <string>NIS</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>nisHomedirChk</cstring>
+ </property>
+ <property name="text">
+ <string>NIS homedir</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel16</cstring>
+ </property>
+ <property name="text">
+ <string>Homedir map:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>homedirMapEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>homedirMapEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox9</cstring>
+ </property>
+ <property name="title">
+ <string>UTMP</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel7_3</cstring>
+ </property>
+ <property name="text">
+ <string>Utmp director&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>utmpDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>wtmpDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel8_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Wtmp directory:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>wtmpDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>utmpDirectoryUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>CheckBox51</cstring>
+ </property>
+ <property name="text">
+ <string>Ut&amp;mp</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer33</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Debug</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ntStatusSupportChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;NT status support</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ntSmbSupportChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>NT S&amp;MB support</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ntPipeSupportChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>NT pipe supp&amp;ort</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer81</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>320</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>shareListView</sender>
+ <signal>doubleClicked(QListViewItem*)</signal>
+ <receiver>editShareBtn</receiver>
+ <slot>animateClick()</slot>
+ </connection>
+ <connection>
+ <sender>printerListView</sender>
+ <signal>doubleClicked(QListViewItem*)</signal>
+ <receiver>editPrinterBtn</receiver>
+ <slot>animateClick()</slot>
+ </connection>
+ <connection>
+ <sender>KURLLabel1</sender>
+ <signal>leftClickedURL()</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>KURLLabel1_leftClickedURL()</slot>
+ </connection>
+ <connection>
+ <sender>serverRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>domainRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>lmAnnounceCombo</sender>
+ <signal>activated(int)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>lmAnnounceCombo_activated(int)</slot>
+ </connection>
+ <connection>
+ <sender>allowGuestLoginsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>allowGuestLoginsChk_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowGuestLoginsChk</sender>
+ <signal>pressed()</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>SO_SNDBUFChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_SNDBUFSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_RCVBUFChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_RCVBUFSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_SNDLOWATChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_SNDLOWATSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_RCVLOWATChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_RCVLOWATSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowGuestLoginsChk</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>userRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowGuestLoginsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>serverRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowGuestLoginsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>domainRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowGuestLoginsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareRadio</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>userRadio</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>serverRadio</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>domainRadio</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>passwordServerEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>KcmInterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>sslChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SSLFrame</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>timestampChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>microsecondsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>adsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>adsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>adsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>realmLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>adsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>realmEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowGuestLoginsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>TextLabel6_5</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowGuestLoginsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>guestAccountCombo</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>domainRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>domainRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>serverRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>serverRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>passwordServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>adsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowGuestLoginsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>TextLabel6_5</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>guestAccountCombo</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox35</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>otherWinsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox36</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>dnsProxyChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>winsHookEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>winsHookEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>maxWinsTtlSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>minWinsTtlSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>otherWinsRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>winsServerEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>winsSupportRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>winsHookLbl</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>ntStatusSupportChk</tabstop>
+ <tabstop>ntSmbSupportChk</tabstop>
+ <tabstop>ntPipeSupportChk</tabstop>
+ <tabstop>CheckBox68</tabstop>
+ <tabstop>defaultServiceEdit</tabstop>
+ <tabstop>timeOffsetSpin</tabstop>
+ <tabstop>remoteAnnounceEdit</tabstop>
+ <tabstop>sourceEnvironmentEdit</tabstop>
+ <tabstop>nisHomedirChk</tabstop>
+ <tabstop>homedirMapEdit</tabstop>
+ <tabstop>CheckBox51</tabstop>
+ <tabstop>utmpDirectoryUrlRq</tabstop>
+ <tabstop>wtmpDirectoryUrlRq</tabstop>
+ <tabstop>advancedTab</tabstop>
+ <tabstop>addShareCommandEdit</tabstop>
+ <tabstop>changeShareCommandEdit</tabstop>
+ <tabstop>deleteShareCommandEdit</tabstop>
+ <tabstop>messageCommandEdit</tabstop>
+ <tabstop>dfreeCommandEdit</tabstop>
+ <tabstop>setQuotaCommandEdit</tabstop>
+ <tabstop>getQuotaCommandEdit</tabstop>
+ <tabstop>panicActionEdit</tabstop>
+ <tabstop>ldapSuffixEdit</tabstop>
+ <tabstop>ldapMachineSuffixEdit</tabstop>
+ <tabstop>ldapUserSuffixEdit</tabstop>
+ <tabstop>ldapGroupSuffixEdit</tabstop>
+ <tabstop>ldapIdmapSuffixEdit</tabstop>
+ <tabstop>ldapFilterEdit</tabstop>
+ <tabstop>ldapAdminDnEdit</tabstop>
+ <tabstop>idmapBackendEdit</tabstop>
+ <tabstop>ldapSslCombo</tabstop>
+ <tabstop>ldapSyncCombo</tabstop>
+ <tabstop>ldapDeleteDnChk</tabstop>
+ <tabstop>hostMsdfsChk</tabstop>
+ <tabstop>disableNetbiosChk</tabstop>
+ <tabstop>netbiosAliasesEdit</tabstop>
+ <tabstop>netbiosScopeEdit</tabstop>
+ <tabstop>nameResolveOrderEdit</tabstop>
+ <tabstop>winbindUidEdit</tabstop>
+ <tabstop>winbindGidEdit</tabstop>
+ <tabstop>templateHomedirEdit</tabstop>
+ <tabstop>templateShellEdit</tabstop>
+ <tabstop>winbindSeparatorEdit</tabstop>
+ <tabstop>templatePrimaryGroupEdit</tabstop>
+ <tabstop>winbindCacheTimeSpin</tabstop>
+ <tabstop>aclCompatibilityCombo</tabstop>
+ <tabstop>winbindEnumUsersChk</tabstop>
+ <tabstop>winbindEnumGroupsChk</tabstop>
+ <tabstop>winbindUseDefaultDomainChk</tabstop>
+ <tabstop>winbindEnableLocalAccountsChk</tabstop>
+ <tabstop>winbindTrustedDomainsOnlyChk</tabstop>
+ <tabstop>enhancedBrowsingChk</tabstop>
+ <tabstop>browseListChk</tabstop>
+ <tabstop>lmAnnounceCombo</tabstop>
+ <tabstop>lmIntervalSpin</tabstop>
+ <tabstop>remoteBrowseSyncEdit</tabstop>
+ <tabstop>preloadEdit</tabstop>
+ <tabstop>timeServerChk</tabstop>
+ <tabstop>largeReadWriteChk</tabstop>
+ <tabstop>unixExtensionsChk</tabstop>
+ <tabstop>readBmpxChk</tabstop>
+ <tabstop>announceAsCombo</tabstop>
+ <tabstop>announceVersionEdit</tabstop>
+ <tabstop>protocolCombo</tabstop>
+ <tabstop>maxProtocolCombo</tabstop>
+ <tabstop>minProtocolCombo</tabstop>
+ <tabstop>maxMuxSpin</tabstop>
+ <tabstop>maxTtlSpin</tabstop>
+ <tabstop>smbPortsEdit</tabstop>
+ <tabstop>sslChk</tabstop>
+ <tabstop>sslVersionCombo</tabstop>
+ <tabstop>sslCompatibilityChk</tabstop>
+ <tabstop>sslHostsEdit</tabstop>
+ <tabstop>sslHostsResignEdit</tabstop>
+ <tabstop>sslCACertDirUrlRq</tabstop>
+ <tabstop>sslCACertFileUrlRq</tabstop>
+ <tabstop>sslEgdSocketEdit</tabstop>
+ <tabstop>sslEntropyFileUrlRq</tabstop>
+ <tabstop>sslEntropyBytesSpin</tabstop>
+ <tabstop>sslCiphersEdit</tabstop>
+ <tabstop>sslRequireServercertChk</tabstop>
+ <tabstop>sslServerCertUrlRq</tabstop>
+ <tabstop>sslServerKeyUrlRq</tabstop>
+ <tabstop>sslRequireClientcertChk</tabstop>
+ <tabstop>sslClientCertUrlRq</tabstop>
+ <tabstop>sslClientKeyUrlRq</tabstop>
+ <tabstop>socketAddressEdit</tabstop>
+ <tabstop>TCP_NODELAYChk</tabstop>
+ <tabstop>IPTOS_LOWDELAYChk</tabstop>
+ <tabstop>IPTOS_THROUGHPUTChk</tabstop>
+ <tabstop>SO_KEEPALIVEChk</tabstop>
+ <tabstop>SO_REUSEADDRChk</tabstop>
+ <tabstop>SO_BROADCASTChk</tabstop>
+ <tabstop>SO_SNDBUFChk</tabstop>
+ <tabstop>SO_SNDBUFSpin</tabstop>
+ <tabstop>SO_RCVBUFChk</tabstop>
+ <tabstop>SO_RCVBUFSpin</tabstop>
+ <tabstop>SO_SNDLOWATChk</tabstop>
+ <tabstop>SO_SNDLOWATSpin</tabstop>
+ <tabstop>SO_RCVLOWATChk</tabstop>
+ <tabstop>SO_RCVLOWATSpin</tabstop>
+ <tabstop>addUserScriptEdit</tabstop>
+ <tabstop>addGroupScriptEdit</tabstop>
+ <tabstop>addUserToGroupScriptEdit</tabstop>
+ <tabstop>addMachineScriptEdit</tabstop>
+ <tabstop>shutdownScriptEdit</tabstop>
+ <tabstop>abortShutdownScriptEdit</tabstop>
+ <tabstop>SetPrimaryGroupScriptEdit</tabstop>
+ <tabstop>deleteUserScriptEdit</tabstop>
+ <tabstop>deleteGroupScriptEdit</tabstop>
+ <tabstop>deleteUserFromGroupScriptEdit</tabstop>
+ <tabstop>logonScriptEdit</tabstop>
+ <tabstop>logonDriveEdit</tabstop>
+ <tabstop>logonPathUrlRq</tabstop>
+ <tabstop>logonHomeUrlRq</tabstop>
+ <tabstop>unicodeChk</tabstop>
+ <tabstop>displayCharsetEdit</tabstop>
+ <tabstop>unixCharsetEdit</tabstop>
+ <tabstop>dosCharsetEdit</tabstop>
+ <tabstop>characterSetEdit</tabstop>
+ <tabstop>clientCodePageEdit</tabstop>
+ <tabstop>codePageDirUrlRq</tabstop>
+ <tabstop>codingSystemEdit</tabstop>
+ <tabstop>validCharsEdit</tabstop>
+ <tabstop>mainTab</tabstop>
+ <tabstop>configUrlRq</tabstop>
+ <tabstop>loadBtn</tabstop>
+ <tabstop>workgroupEdit</tabstop>
+ <tabstop>netbiosNameEdit</tabstop>
+ <tabstop>serverStringEdit</tabstop>
+ <tabstop>shareRadio</tabstop>
+ <tabstop>userRadio</tabstop>
+ <tabstop>serverRadio</tabstop>
+ <tabstop>domainRadio</tabstop>
+ <tabstop>adsRadio</tabstop>
+ <tabstop>passwordServerEdit</tabstop>
+ <tabstop>realmEdit</tabstop>
+ <tabstop>guestAccountCombo</tabstop>
+ <tabstop>allowGuestLoginsChk</tabstop>
+ <tabstop>shareListView</tabstop>
+ <tabstop>editDefaultShareBtn</tabstop>
+ <tabstop>addShareBtn</tabstop>
+ <tabstop>editShareBtn</tabstop>
+ <tabstop>removeShareBtn</tabstop>
+ <tabstop>printerListView</tabstop>
+ <tabstop>editDefaultPrinterBtn</tabstop>
+ <tabstop>addPrinterBtn</tabstop>
+ <tabstop>editPrinterBtn</tabstop>
+ <tabstop>removePrinterBtn</tabstop>
+ <tabstop>sambaUsersListView</tabstop>
+ <tabstop>addSambaUserBtn</tabstop>
+ <tabstop>removeSambaUserBtn</tabstop>
+ <tabstop>unixUsersListView</tabstop>
+ <tabstop>sambaUserPasswordBtn</tabstop>
+ <tabstop>joinADomainBtn</tabstop>
+ <tabstop>tabWidget4</tabstop>
+ <tabstop>mapToGuestCombo</tabstop>
+ <tabstop>interfacesEdit</tabstop>
+ <tabstop>bindInterfacesOnlyChk</tabstop>
+ <tabstop>rootDirectoryEdit</tabstop>
+ <tabstop>authMethodsEdit</tabstop>
+ <tabstop>hostsEquivUrlRq</tabstop>
+ <tabstop>privateDirUrlRq</tabstop>
+ <tabstop>algorithmicRidBaseSpin</tabstop>
+ <tabstop>obeyPamRestrictionsChk</tabstop>
+ <tabstop>pamPasswordChangeChk</tabstop>
+ <tabstop>allowTrustedDomainsChk</tabstop>
+ <tabstop>paranoidServerSecurityChk</tabstop>
+ <tabstop>encryptPasswordsChk</tabstop>
+ <tabstop>smbPasswdFileUrlRq</tabstop>
+ <tabstop>passdbBackendEdit</tabstop>
+ <tabstop>passwdChatEdit</tabstop>
+ <tabstop>passwdChatDebugChk</tabstop>
+ <tabstop>passwdProgramUrlRq</tabstop>
+ <tabstop>unixPasswordSyncChk</tabstop>
+ <tabstop>nullPasswordsChk</tabstop>
+ <tabstop>passwordLevelSpin</tabstop>
+ <tabstop>minPasswdLengthSpin</tabstop>
+ <tabstop>machinePasswordTimeoutSpin</tabstop>
+ <tabstop>updateEncryptedChk</tabstop>
+ <tabstop>usernameLevelSpin</tabstop>
+ <tabstop>usernameMapUrlRq</tabstop>
+ <tabstop>hideLocalUsersChk</tabstop>
+ <tabstop>restrictAnonymousChk</tabstop>
+ <tabstop>useRhostsChk</tabstop>
+ <tabstop>lanmanAuthChk</tabstop>
+ <tabstop>ntlmAuthChk</tabstop>
+ <tabstop>useSpnegoChk</tabstop>
+ <tabstop>serverSchannelCombo</tabstop>
+ <tabstop>serverSigningCombo</tabstop>
+ <tabstop>clientLanmanAuthChk</tabstop>
+ <tabstop>clientPlaintextAuthChk</tabstop>
+ <tabstop>clientNTLMv2AuthChk</tabstop>
+ <tabstop>clientUseSpnegoChk</tabstop>
+ <tabstop>clientSchannelCombo</tabstop>
+ <tabstop>clientSigningCombo</tabstop>
+ <tabstop>logFileUrlRq</tabstop>
+ <tabstop>logLevelEdit</tabstop>
+ <tabstop>maxLogSizeSpin</tabstop>
+ <tabstop>syslogSpin</tabstop>
+ <tabstop>syslogOnlyChk</tabstop>
+ <tabstop>statusChk</tabstop>
+ <tabstop>timestampChk</tabstop>
+ <tabstop>microsecondsChk</tabstop>
+ <tabstop>debugPidChk</tabstop>
+ <tabstop>debugUidChk</tabstop>
+ <tabstop>preloadModulesEdit</tabstop>
+ <tabstop>getwdCacheChk</tabstop>
+ <tabstop>useMmapChk</tabstop>
+ <tabstop>kernelChangeNotifyChk</tabstop>
+ <tabstop>hostnameLookupsChk</tabstop>
+ <tabstop>readRawChk</tabstop>
+ <tabstop>writeRawChk</tabstop>
+ <tabstop>changeNotifyTimeoutSpin</tabstop>
+ <tabstop>deadtimeSpin</tabstop>
+ <tabstop>keepaliveSpin</tabstop>
+ <tabstop>lpqCacheTimeSpin</tabstop>
+ <tabstop>nameCacheTimeoutSpin</tabstop>
+ <tabstop>maxDiskSizeSpin</tabstop>
+ <tabstop>readSizeSpin</tabstop>
+ <tabstop>statCacheSizeSpin</tabstop>
+ <tabstop>maxXmitSpin</tabstop>
+ <tabstop>maxSmbdProcessesSpin</tabstop>
+ <tabstop>maxOpenFilesSpin</tabstop>
+ <tabstop>totalPrintJobsSpin</tabstop>
+ <tabstop>printcapNameUrlRq</tabstop>
+ <tabstop>os2DriverMapUrlRq</tabstop>
+ <tabstop>printerDriverFileUrlRq</tabstop>
+ <tabstop>addprinterCommandEdit</tabstop>
+ <tabstop>deleteprinterCommandEdit</tabstop>
+ <tabstop>enumportsCommandEdit</tabstop>
+ <tabstop>loadPrintersChk</tabstop>
+ <tabstop>disableSpoolssChk</tabstop>
+ <tabstop>showAddPrinterWizardChk</tabstop>
+ <tabstop>preferredMasterChk</tabstop>
+ <tabstop>localMasterChk</tabstop>
+ <tabstop>domainMasterChk</tabstop>
+ <tabstop>domainLogonsChk</tabstop>
+ <tabstop>osLevelSpin</tabstop>
+ <tabstop>domainAdminGroupEdit</tabstop>
+ <tabstop>domainGuestGroupEdit</tabstop>
+ <tabstop>noWinsSupportRadio</tabstop>
+ <tabstop>dnsProxyChk</tabstop>
+ <tabstop>winsHookEdit</tabstop>
+ <tabstop>maxWinsTtlSpin</tabstop>
+ <tabstop>minWinsTtlSpin</tabstop>
+ <tabstop>winsServerEdit</tabstop>
+ <tabstop>winsProxyChk</tabstop>
+ <tabstop>winsPartnersEdit</tabstop>
+ <tabstop>stripDotChk</tabstop>
+ <tabstop>mangledStackSpin</tabstop>
+ <tabstop>manglePrefixSpin</tabstop>
+ <tabstop>statCacheChk</tabstop>
+ <tabstop>kernelOplocksChk</tabstop>
+ <tabstop>lockDirectoryUrlRq</tabstop>
+ <tabstop>pidDirectoryUrlRq</tabstop>
+ <tabstop>lockSpinCountSpin</tabstop>
+ <tabstop>lockSpinTimeSpin</tabstop>
+ <tabstop>oplockBreakWaitTimeSpin</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">qptrlist.h</include>
+ <include location="global" impldecl="in declaration">share.h</include>
+ <include location="global" impldecl="in implementation">kprocess.h</include>
+ <include location="local" impldecl="in implementation">kiconloader.h</include>
+ <include location="local" impldecl="in implementation">kcminterface.ui.h</include>
+</includes>
+<signals>
+ <signal>changed()</signal>
+</signals>
+<slots>
+ <slot>init()</slot>
+ <slot>changedSlot()</slot>
+ <slot>securityLevelCombo_activated( int i )</slot>
+ <slot>KURLLabel1_leftClickedURL()</slot>
+ <slot>lmAnnounceCombo_activated( int i )</slot>
+ <slot>allowGuestLoginsChk_toggled( bool b )</slot>
+ <slot>mapToGuestCombo_activated( int i )</slot>
+ <slot>updateSecurityLevelHelpLbl()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurllabel.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/kcminterface.ui.h b/filesharing/advanced/kcm_sambaconf/kcminterface.ui.h
new file mode 100644
index 00000000..708667ec
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcminterface.ui.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+void KcmInterface::init()
+{
+ addShareBtn->setIconSet(SmallIconSet("filenew"));
+ editShareBtn->setIconSet(SmallIconSet("edit"));
+ removeShareBtn->setIconSet(SmallIconSet("editdelete"));
+ editDefaultShareBtn->setIconSet(SmallIconSet("queue"));
+
+ addPrinterBtn->setIconSet(SmallIconSet("filenew"));
+ editPrinterBtn->setIconSet(SmallIconSet("edit"));
+ removePrinterBtn->setIconSet(SmallIconSet("editdelete"));
+ editDefaultPrinterBtn->setIconSet(SmallIconSet("print_class"));
+
+ advancedWarningPixLbl->setPixmap(SmallIcon("messagebox_warning"));
+
+}
+
+
+
+void KcmInterface::changedSlot()
+{
+ emit changed();
+}
+
+
+void KcmInterface::securityLevelCombo_activated( int i )
+{
+ passwordServerEdit->setDisabled(i<2);
+ allowGuestLoginsChk->setDisabled(i==0);
+ updateSecurityLevelHelpLbl();
+}
+
+void KcmInterface::KURLLabel1_leftClickedURL()
+{
+ KProcess* p = new KProcess();
+
+ *p << "konqueror";
+ *p << "man:smb.conf";
+ p->start();
+
+
+
+}
+
+
+
+
+void KcmInterface::lmAnnounceCombo_activated( int i)
+{
+ lmIntervalSpin->setEnabled(i==0);
+}
+
+
+
+
+void KcmInterface::allowGuestLoginsChk_toggled( bool b)
+{
+ int i = 0;
+ if (b)
+ i = 1;
+
+ mapToGuestCombo->setCurrentItem(i);
+}
+
+
+void KcmInterface::mapToGuestCombo_activated( int i)
+{
+ allowGuestLoginsChk->setChecked(i>0);
+}
+
+void KcmInterface::updateSecurityLevelHelpLbl()
+{
+ if (shareRadio->isChecked()) {
+ securityLevelHelpLbl->setText(i18n("Use the <i>share</i> security level if you have a home network "
+ "or a small office network.<br> It allows everyone to read the list "
+ "of all your shared directories and printers before a login is required."));
+
+ } else if (userRadio->isChecked()) {
+ securityLevelHelpLbl->setText(i18n("Use the <i>user</i> security level if you have a bigger network "
+ "and you do not want to allow everyone to read your list of shared "
+ "directories and printers without a login.<p>"
+ "If you want to run your Samba server as a <b>Primary Domain controller</b> (PDC) "
+ "you also have to set this option."));
+} else if (serverRadio->isChecked()) {
+ securityLevelHelpLbl->setText(i18n("Use the <i>server</i> security level if you have a big network "
+ "and the samba server should validate the username/password "
+ "by passing it to another SMB server, such as an NT box."));
+ } else if (domainRadio->isChecked()) {
+ securityLevelHelpLbl->setText(i18n("Use the <i>domain</i> security level if you have a big network "
+ "and the samba server should validate the username/password "
+ "by passing it to a Windows NT Primary or Backup Domain Controller."));
+ } else if (adsRadio->isChecked()) {
+ securityLevelHelpLbl->setText(i18n("Use the <i>ADS</i> security level if you have a big network "
+ "and the samba server should act as a domain member in an ADS realm."));
+ }
+
+}
diff --git a/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui b/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui
new file mode 100644
index 00000000..7aefef4f
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui
@@ -0,0 +1,1211 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+ <class>KcmPrinterDlg</class>
+ <comment>
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+</comment>
+ <widget class='QDialog'>
+ <property name='name'>
+ <cstring>KcmPrinterDlg</cstring>
+ </property>
+ <property name='geometry'>
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>533</width>
+ <height>381</height>
+ </rect>
+ </property>
+ <property name='caption'>
+ <string>Printer Settings</string>
+ </property>
+ <vbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QTabWidget'>
+ <property name='name'>
+ <cstring>_tabs</cstring>
+ </property>
+ <widget class='QWidget'>
+ <property name='name'>
+ <cstring>tab</cstring>
+ </property>
+ <attribute name='title'>
+ <string>&amp;Base Settings</string>
+ </attribute>
+ <vbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class='QLayoutWidget'>
+ <property name='name'>
+ <cstring>Layout51</cstring>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>0</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QFrame'>
+ <property name='name'>
+ <cstring>pixFrame</cstring>
+ </property>
+ <property name='frameShape'>
+ <enum>Box</enum>
+ </property>
+ <property name='frameShadow'>
+ <enum>Sunken</enum>
+ </property>
+ <vbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QLabel'>
+ <property name='name'>
+ <cstring>printerPixLbl</cstring>
+ </property>
+ <property name='sizePolicy'>
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name='text'>
+ <string>Pixmap</string>
+ </property>
+ <property name='scaledContents'>
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class='QGroupBox'>
+ <property name='name'>
+ <cstring>printerGrp</cstring>
+ </property>
+ <property name='enabled'>
+ <bool>true</bool>
+ </property>
+ <property name='title'>
+ <string>Pr&amp;inter</string>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QLayoutWidget'>
+ <property name='name'>
+ <cstring>Layout49</cstring>
+ </property>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>0</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget row='1' class='KURLRequester' column='1'>
+ <property name='name'>
+ <cstring>pathUrlRq</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name='text'>
+ <string>Pa&amp;th:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>pathUrlRq</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel4_2</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;Queue:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>queueCombo</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QLayoutWidget' column='1'>
+ <property name='name'>
+ <cstring>Layout48</cstring>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>0</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='KComboBox'>
+ <property name='name'>
+ <cstring>queueCombo</cstring>
+ </property>
+ </widget>
+ <widget class='QCheckBox'>
+ <property name='name'>
+ <cstring>printersChk</cstring>
+ </property>
+ <property name='enabled'>
+ <bool>true</bool>
+ </property>
+ <property name='text'>
+ <string>Sha&amp;re all printers</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class='QGroupBox'>
+ <property name='name'>
+ <cstring>identifierGrp</cstring>
+ </property>
+ <property name='title'>
+ <string>I&amp;dentifier</string>
+ </property>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>Lbl_shareName</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;Name:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>shareNameEdit</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name='text'>
+ <string>Comm&amp;ent:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>commentEdit</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='KLineEdit' column='1'>
+ <property name='name'>
+ <cstring>commentEdit</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='KLineEdit' column='1'>
+ <property name='name'>
+ <cstring>shareNameEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class='QGroupBox'>
+ <property name='name'>
+ <cstring>GroupBox4</cstring>
+ </property>
+ <property name='title'>
+ <string>Main Properties</string>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QCheckBox'>
+ <property name='name'>
+ <cstring>availableBaseChk</cstring>
+ </property>
+ <property name='text'>
+ <string>A&amp;vailable</string>
+ </property>
+ </widget>
+ <widget class='QCheckBox'>
+ <property name='name'>
+ <cstring>browseableBaseChk</cstring>
+ </property>
+ <property name='text'>
+ <string>Bro&amp;wseable</string>
+ </property>
+ </widget>
+ <widget class='QCheckBox'>
+ <property name='name'>
+ <cstring>publicBaseChk</cstring>
+ </property>
+ <property name='text'>
+ <string>Pub&amp;lic</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name='name'>
+ <cstring>Spacer18</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Vertical</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class='QWidget'>
+ <property name='name'>
+ <cstring>tab</cstring>
+ </property>
+ <attribute name='title'>
+ <string>&amp;Printing</string>
+ </attribute>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row='8' column='1'>
+ <property name='name'>
+ <cstring>Spacer24</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Vertical</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget row='3' class='QLabel' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>TextLabel5_2_8</cstring>
+ </property>
+ <property name='font'>
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name='text'>
+ <string>Printer dri&amp;ver:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>printerDriverEdit</cstring>
+ </property>
+ </widget>
+ <widget row='3' class='QLineEdit' rowspan='1' column='2' colspan='2'>
+ <property name='name'>
+ <cstring>printerDriverEdit</cstring>
+ </property>
+ </widget>
+ <widget row='4' class='QLabel' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>TextLabel5_2_2_2</cstring>
+ </property>
+ <property name='font'>
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name='text'>
+ <string>Print&amp;er driver location:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>printerDriverLocationEdit</cstring>
+ </property>
+ </widget>
+ <widget row='4' class='QLineEdit' rowspan='1' column='2' colspan='2'>
+ <property name='name'>
+ <cstring>printerDriverLocationEdit</cstring>
+ </property>
+ </widget>
+ <widget row='5' class='QCheckBox' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>postscriptChk</cstring>
+ </property>
+ <property name='text'>
+ <string>PostScr&amp;ipt</string>
+ </property>
+ </widget>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>PrintingLbl</cstring>
+ </property>
+ <property name='text'>
+ <string>Printin&amp;g:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>printingCombo</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QComboBox' column='2'>
+ <item>
+ <property name='text'>
+ <string>sysv</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>aix</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>hpux</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>bsd</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>qnx</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>plp</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>lprng</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>softq</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>cups</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>nt</string>
+ </property>
+ </item>
+ <item>
+ <property name='text'>
+ <string>os2</string>
+ </property>
+ </item>
+ <property name='name'>
+ <cstring>printingCombo</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='QLabel' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>TextLabel3_2_2</cstring>
+ </property>
+ <property name='text'>
+ <string>Max reported print &amp;jobs:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>maxReportedPrintJobsSpin</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLabel' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>TextLabel3_2</cstring>
+ </property>
+ <property name='text'>
+ <string>Ma&amp;x print jobs:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>maxPrintJobsSpin</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='KIntSpinBox' column='2'>
+ <property name='name'>
+ <cstring>maxReportedPrintJobsSpin</cstring>
+ </property>
+ <property name='font'>
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name='maxValue'>
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget row='1' class='KIntSpinBox' column='2'>
+ <property name='name'>
+ <cstring>maxPrintJobsSpin</cstring>
+ </property>
+ <property name='font'>
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name='maxValue'>
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row='1' column='3'>
+ <property name='name'>
+ <cstring>Spacer26</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Horizontal</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>200</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget row='6' class='QCheckBox' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>useClientDriverChk</cstring>
+ </property>
+ <property name='text'>
+ <string>Use c&amp;lient driver</string>
+ </property>
+ </widget>
+ <widget row='7' class='QCheckBox' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>defaultDevmodeChk</cstring>
+ </property>
+ <property name='text'>
+ <string>De&amp;fault devmode</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class='QWidget'>
+ <property name='name'>
+ <cstring>tab</cstring>
+ </property>
+ <attribute name='title'>
+ <string>&amp;Security</string>
+ </attribute>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <widget row='3' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name='text'>
+ <string>Hosts &amp;deny:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>hostsDenyEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget row='3' class='QLineEdit' rowspan='1' column='1' colspan='2'>
+ <property name='name'>
+ <cstring>hostsDenyEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget row='2' class='QLineEdit' rowspan='1' column='1' colspan='2'>
+ <property name='name'>
+ <cstring>hostsAllowEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget row='2' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name='text'>
+ <string>Hosts a&amp;llow:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>hostsAllowEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget row='1' class='QLineEdit' rowspan='1' column='1' colspan='2'>
+ <property name='name'>
+ <cstring>printerAdminEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <spacer row='4' column='0'>
+ <property name='name'>
+ <cstring>Spacer21</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Vertical</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget row='1' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6_2</cstring>
+ </property>
+ <property name='text'>
+ <string>P&amp;rinter admin:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>printerAdminEdit</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;Guest account:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ <property name='whatsThis' stdset='0'>
+ <string>This is a username which will be used for access to services which are specified as guest ok. Whatever privileges this user has will be available to any client connecting to the guest service. Typically this user will exist in the password file, but will not have a valid login. The user account \"ftp\" is often a good choice for this parameter. If a username is specified in a given service, the specified username overrides this one.</string>
+ </property>
+ </widget>
+ <widget row='0' class='QComboBox' column='1'>
+ <property name='name'>
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ </widget>
+ <spacer row='0' column='2'>
+ <property name='name'>
+ <cstring>Spacer27</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Horizontal</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>263</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class='QWidget'>
+ <property name='name'>
+ <cstring>tab</cstring>
+ </property>
+ <attribute name='title'>
+ <string>Co&amp;mmands</string>
+ </attribute>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2</cstring>
+ </property>
+ <property name='text'>
+ <string>Pr&amp;int command:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>printCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>printCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_2</cstring>
+ </property>
+ <property name='text'>
+ <string>lpq comma&amp;nd:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>lpqCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>lpqCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>lprmCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_3</cstring>
+ </property>
+ <property name='text'>
+ <string>lprm comman&amp;d:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>lprmCommandEdit</cstring>
+ </property>
+ </widget>
+ <widget row='4' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>lpresumeEdit</cstring>
+ </property>
+ </widget>
+ <widget row='3' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>lppauseEdit</cstring>
+ </property>
+ </widget>
+ <widget row='4' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_5</cstring>
+ </property>
+ <property name='text'>
+ <string>lp&amp;resume:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>lpresumeEdit</cstring>
+ </property>
+ </widget>
+ <widget row='5' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_6</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;queuepause:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>queuepauseEdit</cstring>
+ </property>
+ </widget>
+ <widget row='3' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_4</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;lppause:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>lppauseEdit</cstring>
+ </property>
+ </widget>
+ <widget row='5' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>queuepauseEdit</cstring>
+ </property>
+ </widget>
+ <widget row='6' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>queueresumeEdit</cstring>
+ </property>
+ </widget>
+ <widget row='6' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel5_2_7</cstring>
+ </property>
+ <property name='text'>
+ <string>qu&amp;eueresume:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>queueresumeEdit</cstring>
+ </property>
+ </widget>
+ <spacer row='7' column='0'>
+ <property name='name'>
+ <cstring>Spacer23</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Vertical</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class='QWidget'>
+ <property name='name'>
+ <cstring>tab</cstring>
+ </property>
+ <attribute name='title'>
+ <string>&amp;Advanced</string>
+ </attribute>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <widget row='1' class='QGroupBox' rowspan='1' column='0' colspan='2'>
+ <property name='name'>
+ <cstring>GroupBox31</cstring>
+ </property>
+ <property name='title'>
+ <string>Miscella&amp;neous</string>
+ </property>
+ <grid>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget row='1' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>postExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>preExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='0' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6_3</cstring>
+ </property>
+ <property name='text'>
+ <string>p&amp;reexec:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>preExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6_3_3</cstring>
+ </property>
+ <property name='text'>
+ <string>root pr&amp;eexec:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>rootPreExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='2' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>rootPreExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='3' class='QLineEdit' column='1'>
+ <property name='name'>
+ <cstring>rootPostExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='3' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6_3_4</cstring>
+ </property>
+ <property name='text'>
+ <string>root postexec:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>rootPostExecEdit</cstring>
+ </property>
+ </widget>
+ <widget row='1' class='QLabel' column='0'>
+ <property name='name'>
+ <cstring>TextLabel6_3_2</cstring>
+ </property>
+ <property name='text'>
+ <string>poste&amp;xec:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>postExecEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row='2' column='0'>
+ <property name='name'>
+ <cstring>Spacer22</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Vertical</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget row='0' class='QGroupBox' column='0'>
+ <property name='name'>
+ <cstring>GroupBox27</cstring>
+ </property>
+ <property name='title'>
+ <string>Tunin&amp;g</string>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QLabel'>
+ <property name='name'>
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name='sizePolicy'>
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name='text'>
+ <string>M&amp;in print space:</string>
+ </property>
+ <property name='buddy' stdset='0'>
+ <cstring>minPrintSpaceSpin</cstring>
+ </property>
+ </widget>
+ <widget class='KIntSpinBox'>
+ <property name='name'>
+ <cstring>minPrintSpaceSpin</cstring>
+ </property>
+ <property name='font'>
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name='maxValue'>
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class='QLabel'>
+ <property name='name'>
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name='text'>
+ <string>kB</string>
+ <comment>kilo Byte</comment>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget row='0' class='QGroupBox' column='1'>
+ <property name='name'>
+ <cstring>GroupBox17</cstring>
+ </property>
+ <property name='title'>
+ <string>&amp;Logging</string>
+ </property>
+ <vbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>11</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QCheckBox'>
+ <property name='name'>
+ <cstring>statusChk</cstring>
+ </property>
+ <property name='text'>
+ <string>S&amp;tatus</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class='QLayoutWidget'>
+ <property name='name'>
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name='name'>
+ <cstring>unnamed</cstring>
+ </property>
+ <property name='margin'>
+ <number>0</number>
+ </property>
+ <property name='spacing'>
+ <number>6</number>
+ </property>
+ <widget class='QPushButton'>
+ <property name='name'>
+ <cstring>buttonHelp</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;Help</string>
+ </property>
+ <property name='accel'>
+ <string>F1</string>
+ </property>
+ <property name='autoDefault'>
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name='name'>
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name='orientation'>
+ <enum>Horizontal</enum>
+ </property>
+ <property name='sizeType'>
+ <enum>Expanding</enum>
+ </property>
+ <property name='sizeHint'>
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class='QPushButton'>
+ <property name='name'>
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;OK</string>
+ </property>
+ <property name='autoDefault'>
+ <bool>true</bool>
+ </property>
+ <property name='default'>
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class='QPushButton'>
+ <property name='name'>
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name='text'>
+ <string>&amp;Cancel</string>
+ </property>
+ <property name='autoDefault'>
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <connections>
+ <connection>
+ <sender>printersChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>queueCombo</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>printersChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>shareNameEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmPrinterDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmPrinterDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>printersChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmPrinterDlg</receiver>
+ <slot>printersChkToggled(bool)</slot>
+ </connection>
+ </connections>
+ <tabstops>
+ <tabstop>queueCombo</tabstop>
+ <tabstop>printersChk</tabstop>
+ <tabstop>pathUrlRq</tabstop>
+ <tabstop>shareNameEdit</tabstop>
+ <tabstop>commentEdit</tabstop>
+ <tabstop>availableBaseChk</tabstop>
+ <tabstop>browseableBaseChk</tabstop>
+ <tabstop>publicBaseChk</tabstop>
+ <tabstop>minPrintSpaceSpin</tabstop>
+ <tabstop>statusChk</tabstop>
+ <tabstop>preExecEdit</tabstop>
+ <tabstop>postExecEdit</tabstop>
+ <tabstop>rootPreExecEdit</tabstop>
+ <tabstop>rootPostExecEdit</tabstop>
+ <tabstop>printCommandEdit</tabstop>
+ <tabstop>lpqCommandEdit</tabstop>
+ <tabstop>lprmCommandEdit</tabstop>
+ <tabstop>lppauseEdit</tabstop>
+ <tabstop>lpresumeEdit</tabstop>
+ <tabstop>queuepauseEdit</tabstop>
+ <tabstop>queueresumeEdit</tabstop>
+ <tabstop>guestAccountCombo</tabstop>
+ <tabstop>printerAdminEdit</tabstop>
+ <tabstop>hostsAllowEdit</tabstop>
+ <tabstop>hostsDenyEdit</tabstop>
+ <tabstop>printingCombo</tabstop>
+ <tabstop>maxPrintJobsSpin</tabstop>
+ <tabstop>maxReportedPrintJobsSpin</tabstop>
+ <tabstop>printerDriverEdit</tabstop>
+ <tabstop>printerDriverLocationEdit</tabstop>
+ <tabstop>postscriptChk</tabstop>
+ <tabstop>useClientDriverChk</tabstop>
+ <tabstop>defaultDevmodeChk</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+ <tabstop>buttonHelp</tabstop>
+ <tabstop>_tabs</tabstop>
+ </tabstops>
+ <includes>
+ <include location='local' impldecl='in implementation'>kiconloader.h</include>
+ <include location='local' impldecl='in implementation'>kcmprinterdlg.ui.h</include>
+ </includes>
+ <slots>
+ <slot>init()</slot>
+ <slot>accept()</slot>
+ <slot>reject()</slot>
+ <slot>printersChkToggled( bool )</slot>
+ </slots>
+ <layoutdefaults margin='11' spacing='6'/>
+ <includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ </includehints>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui.h b/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui.h
new file mode 100644
index 00000000..a02204dd
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcmprinterdlg.ui.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+void KcmPrinterDlg::init()
+{
+ printerPixLbl->setPixmap(DesktopIcon("printer1"));
+}
+
+void KcmPrinterDlg::accept()
+{
+ QDialog::accept();
+}
+
+void KcmPrinterDlg::reject()
+{
+ QDialog::reject();
+}
+
+void KcmPrinterDlg::printersChkToggled( bool )
+{
+
+}
diff --git a/filesharing/advanced/kcm_sambaconf/kcmsambaconf.cpp b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.cpp
new file mode 100644
index 00000000..f6ee4f75
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.cpp
@@ -0,0 +1,1442 @@
+/***************************************************************************
+ kcmsambaconf.cpp - description
+ -------------------
+ begin : Mon Apr 8 13:35:56 CEST 2002
+ copyright : (C) 2002 by Christian Nitschkowski,
+ email : segfault_ii@web.de
+
+ copyright : (C) 2002-2004 by Jan Schaefer
+ email : janschaefer@users.sourceforge.net
+***************************************************************************/
+
+/******************************************************************************
+* *
+* This file is part of KSambaPlugin. *
+* *
+* KSambaPlugin 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. *
+* *
+* KSambaPlugin 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 General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with KSambaPlugin; if not, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#include <assert.h>
+#include <unistd.h>
+
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qpainter.h>
+#include <qcheckbox.h>
+#include <qlistbox.h>
+#include <qradiobutton.h>
+#include <qbuttongroup.h>
+#include <qtabwidget.h>
+#include <qtabbar.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <klineedit.h>
+#include <kurlrequester.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <knuminput.h>
+#include <krestrictedline.h>
+#include <kmessagebox.h>
+#include <kjanuswidget.h>
+#include <klistview.h>
+#include <ksimpleconfig.h>
+
+#include "sambashare.h"
+#include "sambafile.h"
+#include "sharedlgimpl.h"
+#include "printerdlgimpl.h"
+#include "dictmanager.h"
+#include "kcmsambaconf.h"
+#include "smbpasswdfile.h"
+#include "passwd.h"
+#include "qmultichecklistitem.h"
+#include "joindomaindlg.h"
+#include "smbconfconfigwidget.h"
+
+
+#define COL_DISABLED 2
+#define COL_NOPASSWORD 3
+
+
+ShareListViewItem::ShareListViewItem(QListView * parent, SambaShare* share)
+ : QListViewItem(parent)
+{
+ setShare(share);
+}
+
+SambaShare* ShareListViewItem::getShare() const
+{
+ return _share;
+}
+
+void ShareListViewItem::setShare(SambaShare* share)
+{
+ assert(share);
+ _share = share;
+ updateShare();
+}
+
+void ShareListViewItem::updateShare()
+{
+ assert(_share);
+
+ setText(0,_share->getName());
+ setText(2,_share->getValue("comment"));
+
+ if (_share->isPrinter())
+ {
+ if ( _share->getName() == "printers" )
+ setPixmap(0,SmallIcon("print_class"));
+ else
+ setPixmap(0,SmallIcon("print_printer"));
+ setText(1,_share->getValue("printer name"));
+ }
+ else
+ {
+ if ( _share->getName() == "homes" )
+ setPixmap(0,SmallIcon("folder_home"));
+ else
+ setPixmap(0,SmallIcon("folder"));
+ setText(1,_share->getValue("path"));
+ }
+
+ setPixmap(3,createPropertyPixmap());
+}
+
+QPixmap ShareListViewItem::createPropertyPixmap()
+{
+ // Create a big pixmap wich holds the
+ // icons which are needed
+
+ int numberOfPix = 4; // the max number of pixmaps to join
+
+ int w = 22; // Standard size of one pixmap
+ int margin = 4; // Margin between pixmaps
+ int h = 22;
+
+ int totalWidth = (w+margin)*numberOfPix;
+
+ QPixmap pix(totalWidth,h);
+
+ pix.fill(); // Fill with white
+
+ QPainter p(&pix);
+
+ int x = 0;
+
+ if (_share->getBoolValue("public"))
+ {
+ p.drawPixmap(x,0,SmallIcon("network"));
+ x = x+w+margin;
+ }
+
+ if (!_share->getBoolValue("read only"))
+ {
+ p.drawPixmap(x,0,SmallIcon("edit"));
+ x = x+w+margin;
+ }
+
+ if (_share->getBoolValue("printable"))
+ {
+ p.drawPixmap(x,0,SmallIcon("fileprint"));
+ x = x+w+margin;
+ }
+
+ if (_share->getBoolValue("browseable"))
+ {
+ p.drawPixmap(x,0,SmallIcon("run"));
+ x = x+w+margin;
+ }
+
+ if (!_share->getBoolValue("available"))
+ p.drawPixmap(x,0,SmallIcon("no"));
+
+
+ p.end();
+
+ return QPixmap(pix);
+}
+
+KcmSambaConf::KcmSambaConf(QWidget *parent, const char *name)
+ : KCModule(parent,name)
+{
+ _dictMngr = 0L;
+ _sambaFile = 0L;
+ m_smbConfConfigWidget = 0L;
+
+
+ QBoxLayout * l = new QHBoxLayout( this );
+ l->setAutoAdd( TRUE );
+
+ QString smbFile = SambaFile::findSambaConf();
+ if (smbFile.isNull()) {
+ createSmbConfigWidget();
+ return;
+ }
+
+ slotSpecifySmbConf(smbFile);
+
+}
+
+
+KcmSambaConf::~KcmSambaConf() {
+ delete _dictMngr;
+}
+
+void KcmSambaConf::createSmbConfigWidget() {
+ m_smbConfConfigWidget = new SmbConfConfigWidget(this);
+ connect( m_smbConfConfigWidget, SIGNAL(smbConfChoosed(const QString &)),
+ this, SLOT(slotSpecifySmbConf(const QString &)));
+}
+
+void KcmSambaConf::slotSpecifySmbConf(const QString & smbConf) {
+ if (m_smbConfConfigWidget) {
+ m_smbConfConfigWidget->hide();
+ }
+
+ init();
+ initAdvancedTab();
+ load(smbConf);
+
+
+ if (getuid() != 0) {
+ for (int i=0;i<_interface->mainTab->count();i++) {
+ QWidget* w = _interface->mainTab->page(i);
+ w->setEnabled(false);
+ }
+ }
+
+ _interface->show();
+}
+
+void KcmSambaConf::init() {
+
+ _interface = new KcmInterface(this);
+
+
+ connect ( _interface->sambaUserPasswordBtn, SIGNAL(clicked()), this, SLOT(sambaUserPasswordBtnClicked()));
+
+ connect ( _interface->editShareBtn, SIGNAL(clicked()), this, SLOT(editShare()));
+ connect ( _interface->addShareBtn, SIGNAL(clicked()), this, SLOT(addShare()));
+ connect ( _interface->removeShareBtn, SIGNAL(clicked()), this, SLOT(removeShare()));
+
+ connect ( _interface->editPrinterBtn, SIGNAL(clicked()), this, SLOT(editPrinter()));
+ connect ( _interface->addPrinterBtn, SIGNAL(clicked()), this, SLOT(addPrinter()));
+ connect ( _interface->removePrinterBtn, SIGNAL(clicked()), this, SLOT(removePrinter()));
+
+ connect ( _interface->editDefaultPrinterBtn, SIGNAL(clicked()), this, SLOT(editPrinterDefaults()));
+ connect ( _interface->editDefaultShareBtn, SIGNAL(clicked()), this, SLOT(editShareDefaults()));
+
+ connect( _interface->domainRadio, SIGNAL(toggled(bool)),
+ _interface->joinADomainBtn, SLOT( setEnabled(bool) ));
+
+ connect(_interface->nullPasswordsChk,SIGNAL(toggled(bool)),
+ this, SLOT(nullPasswordsEnabled(bool)));
+
+ connect( _interface->addSambaUserBtn, SIGNAL(clicked()),
+ this, SLOT( addSambaUserBtnClicked() ));
+
+ connect( _interface->removeSambaUserBtn, SIGNAL(clicked()),
+ this, SLOT( removeSambaUserBtnClicked() ));
+
+ _interface->removeSambaUserBtn->setIconSet(SmallIconSet("1rightarrow"));
+ _interface->addSambaUserBtn->setIconSet(SmallIconSet("1leftarrow"));
+
+
+ connect( _interface->sambaUsersListView, SIGNAL(mouseButtonPressed(int,QListViewItem*,const QPoint &,int)),
+ this, SLOT(slotMouseButtonPressed(int,QListViewItem*,const QPoint &,int)));
+
+ connect( _interface->joinADomainBtn, SIGNAL(clicked()),
+ this, SLOT( joinADomainBtnClicked() ));
+
+ connect( _interface->loadBtn, SIGNAL(clicked()),
+ this, SLOT( loadBtnClicked() ));
+
+ connect( _interface, SIGNAL(changed()), this, SLOT(configChanged()));
+}
+
+
+void KcmSambaConf::initAdvancedTab()
+{
+ QVBoxLayout *l = new QVBoxLayout(_interface->advancedFrame);
+ l->setAutoAdd(true);
+ l->setMargin(0);
+ _janus = new KJanusWidget(_interface->advancedFrame,0,KJanusWidget::TreeList);
+ _janus->setRootIsDecorated(false);
+ _janus->setShowIconsInTreeList(true);
+
+ QWidget *w;
+ QFrame *f;
+ QString label;
+ QPixmap icon;
+
+ for (int i=0;i<_interface->advancedTab->count();)
+ {
+ w = _interface->advancedTab->page(i);
+ label = _interface->advancedTab->label(i);
+
+ if (label == i18n("Security"))
+ icon = SmallIcon("password");
+ else
+ if (label == i18n("Logging"))
+ icon = SmallIcon("history");
+ else
+ if (label == i18n("Tuning"))
+ icon = SmallIcon("launch");
+ else
+ if (label == i18n("Filenames"))
+ icon = SmallIcon("folder");
+ else
+ if (label == i18n("Locking"))
+ icon = SmallIcon("lock");
+ else
+ if (label == i18n("Printing"))
+ icon = SmallIcon("fileprint");
+ else
+ if (label == i18n("Logon"))
+ icon = SmallIcon("kdmconfig");
+ else
+ if (label == i18n("Protocol"))
+ icon = SmallIcon("core");
+ else
+ if (label == i18n("Charset"))
+ icon = SmallIcon("charset");
+ else
+ if (label == i18n("Socket"))
+ icon = SmallIcon("socket");
+ else
+ if (label == i18n("SSL"))
+ icon = SmallIcon("encrypted");
+ else
+ if (label == i18n("Browsing"))
+ icon = SmallIcon("konqueror");
+ else
+ if (label == i18n("Misc"))
+ icon = SmallIcon("misc");
+ else
+ if (label == i18n("Commands"))
+ icon = SmallIcon("konsole");
+ else {
+ icon = QPixmap(16,16);
+ icon.fill();
+ }
+ //SmallIcon("empty2");
+
+ f = _janus->addPage( label,label,icon );
+ l = new QVBoxLayout(f);
+ l->setAutoAdd(true);
+ l->setMargin(0);
+
+ _interface->advancedTab->removePage(w);
+
+ w->reparent(f,QPoint(1,1),TRUE);
+
+ }
+
+ w = _interface->mainTab->page(5);
+ _interface->mainTab->removePage(w);
+ delete w;
+ _interface->advancedWarningPixLbl->setPixmap(DesktopIcon("messagebox_warning"));
+
+
+}
+
+void KcmSambaConf::editShare()
+{
+ ShareListViewItem* item = static_cast<ShareListViewItem*>(_interface->shareListView->selectedItem());
+
+ if (!item)
+ return;
+
+ ShareDlgImpl* dlg = new ShareDlgImpl(_interface,item->getShare());
+ connect(dlg, SIGNAL(changed()), this, SLOT(configChanged()));
+ dlg->exec();
+ item->updateShare();
+
+ disconnect(dlg, SIGNAL(changed()), this, SLOT(configChanged()));
+
+ delete dlg;
+
+}
+
+void KcmSambaConf::addShare()
+{
+ SambaShare* share = _sambaFile->newShare(_sambaFile->getUnusedName(),"");
+ ShareListViewItem* item = new ShareListViewItem( _interface->shareListView, share );
+ _interface->shareListView->setSelected(item,true);
+
+ ShareDlgImpl* dlg = new ShareDlgImpl(_interface,share);
+ dlg->exec();
+
+ if (dlg->result() == QDialog::Rejected )
+ removeShare();
+ else {
+ item->updateShare();
+ emit changed(true);
+ }
+
+ delete dlg;
+}
+
+void KcmSambaConf::removeShare()
+{
+ ShareListViewItem* item = static_cast<ShareListViewItem*>(_interface->shareListView->selectedItem());
+
+ if (!item)
+ return;
+
+ SambaShare *share = item->getShare();
+ delete item;
+ _sambaFile->removeShare(share);
+
+ emit changed(true);
+}
+
+
+void KcmSambaConf::editPrinter()
+{
+ ShareListViewItem* item = static_cast<ShareListViewItem*>(_interface->printerListView->selectedItem());
+
+ if (!item)
+ return;
+
+ PrinterDlgImpl* dlg = new PrinterDlgImpl(_interface,item->getShare());
+ dlg->exec();
+ item->updateShare();
+
+ delete dlg;
+
+ emit changed(true);
+}
+
+void KcmSambaConf::addPrinter()
+{
+ SambaShare* share = _sambaFile->newPrinter(_sambaFile->getUnusedName(),"");
+ ShareListViewItem* item = new ShareListViewItem( _interface->shareListView, share );
+ _interface->printerListView->setSelected(item,true);
+
+ PrinterDlgImpl* dlg = new PrinterDlgImpl(_interface,share);
+ dlg->exec();
+
+ if (dlg->result() == QDialog::Rejected )
+ removePrinter();
+ else
+ {
+ item->updateShare();
+ emit changed(true);
+ }
+
+ delete dlg;
+}
+
+void KcmSambaConf::removePrinter()
+{
+ ShareListViewItem* item = static_cast<ShareListViewItem*>(_interface->printerListView->selectedItem());
+
+ if (!item)
+ return;
+
+ SambaShare *share = item->getShare();
+ delete item;
+ _sambaFile->removeShare(share);
+
+ emit changed(true);
+}
+
+void KcmSambaConf::editShareDefaults()
+{
+ SambaShare* share = _sambaFile->getShare("global");
+
+ ShareDlgImpl* dlg = new ShareDlgImpl(_interface,share);
+ dlg->directoryGrp->setEnabled(false);
+ dlg->identifierGrp->setEnabled(false);
+ dlg->exec();
+ delete dlg;
+
+ emit changed(true);
+}
+
+void KcmSambaConf::editPrinterDefaults()
+{
+ SambaShare* share = _sambaFile->getShare("global");
+
+ PrinterDlgImpl* dlg = new PrinterDlgImpl(_interface,share);
+ dlg->printerGrp->setEnabled(false);
+ dlg->identifierGrp->setEnabled(false);
+ dlg->exec();
+
+ delete dlg;
+
+ emit changed(true);
+}
+
+
+
+void KcmSambaConf::loadBtnClicked() {
+ load( _interface->configUrlRq->url());
+}
+
+void KcmSambaConf::load(const QString & smbFile)
+{
+ kdDebug(5009) << "loading " << smbFile << endl;
+ _smbconf = smbFile;
+
+ if (_sambaFile)
+ delete _sambaFile;
+
+
+ _sambaFile = new SambaFile(_smbconf,false);
+
+ connect( _sambaFile, SIGNAL(completed()), this, SLOT(fillFields()));
+ connect( _sambaFile, SIGNAL(canceled(const QString &)), this, SLOT(loadCanceled(const QString &)));
+
+ _sambaFile->load();
+
+}
+
+void KcmSambaConf::loadCanceled(const QString & msg) {
+ KMessageBox::sorry(0L,msg,i18n("Error while opening file"));
+}
+
+void KcmSambaConf::fillFields()
+{
+ // Fill the ListViews
+
+ SambaShareList* list = _sambaFile->getSharedDirs();
+
+ SambaShare *share = 0L;
+ _interface->shareListView->clear();
+ for ( share = list->first(); share; share = list->next() )
+ {
+ new ShareListViewItem(_interface->shareListView, share);
+ }
+
+ share = 0L;
+ _interface->printerListView->clear();
+ list = _sambaFile->getSharedPrinters();
+ for ( share = list->first(); share; share = list->next() )
+ {
+ new ShareListViewItem(_interface->printerListView, share);
+ }
+
+ share = _sambaFile->getShare("global");
+
+ if ( !share)
+ share = _sambaFile->newShare("global");
+
+ Q_ASSERT( share);
+ if (_dictMngr)
+ delete _dictMngr;
+
+ _dictMngr = new DictManager(share);
+
+
+ _interface->configUrlRq->setURL( _smbconf );
+ _interface->configUrlRq->setMode( KFile::File | KFile::ExistingOnly);
+
+
+ loadBaseSettings( share );
+ loadSecurity( share );
+ loadTuning( share );
+ loadLogging( share );
+ loadDomain( share );
+ loadWins( share );
+ loadPrinting( share );
+ loadFilenames( share );
+ loadLocking( share );
+ loadProtocol( share );
+ loadSocket( share );
+ loadSSL( share );
+ loadLogon( share );
+ loadCharset( share );
+ loadWinbind( share );
+ loadNetbios( share );
+ loadVFS( share );
+ loadLDAP( share );
+ loadBrowsing( share );
+ loadCommands( share );
+ loadMisc( share );
+ loadDebug( share );
+
+ _dictMngr->load( share, false,true );
+
+ loadUserTab();
+
+ connect(_dictMngr, SIGNAL(changed()), this, SLOT(configChanged()));
+
+}
+
+
+void KcmSambaConf::loadBaseSettings(SambaShare* share)
+{
+
+ _dictMngr->add("workgroup", _interface->workgroupEdit);
+ _dictMngr->add("server string", _interface->serverStringEdit);
+ _dictMngr->add("netbios name", _interface->netbiosNameEdit);
+ _dictMngr->add("netbios aliases", _interface->netbiosAliasesEdit);
+ _dictMngr->add("netbios scope", _interface->netbiosScopeEdit);
+ _dictMngr->add("interfaces", _interface->interfacesEdit);
+
+ _interface->guestAccountCombo->insertStringList( getUnixUsers() );
+ setComboIndexToValue(_interface->guestAccountCombo,"guest account",share);
+
+ QString value = share->getValue("map to guest",false,true);
+
+ _interface->allowGuestLoginsChk->setChecked( value.lower()!="never" );
+
+ _dictMngr->add("guest ok",_interface->allowGuestLoginsChk);
+
+ _dictMngr->add("bind interfaces only",_interface->bindInterfacesOnlyChk);
+
+ QString s = share->getValue("security",false,true).lower();
+ int i = 0;
+
+ if ( s == "share" ) i = 0; else
+ if ( s == "user" ) i = 1; else
+ if ( s == "server" ) i = 2; else
+ if ( s == "domain" ) i = 3; else
+ if ( s == "ads" ) i = 4;
+
+ _interface->securityLevelBtnGrp->setButton(i);
+ _interface->updateSecurityLevelHelpLbl();
+
+}
+
+
+void KcmSambaConf::loadSecurity(SambaShare*)
+{
+
+ _dictMngr->add("map to guest",_interface->mapToGuestCombo,
+ new QStringList(QStringList() << "Never" << "Bad User" << "Bad Password" ));
+
+
+ _dictMngr->add("password server", _interface->passwordServerEdit);
+ _dictMngr->add("passwd chat", _interface->passwdChatEdit);
+ _dictMngr->add("root directory", _interface->rootDirectoryEdit);
+ _dictMngr->add("passdb backend", _interface->passdbBackendEdit);
+ _dictMngr->add("auth methods", _interface->authMethodsEdit);
+ _dictMngr->add("realm", _interface->realmEdit);
+
+ _dictMngr->add("password level", _interface->passwordLevelSpin);
+ _dictMngr->add("min passwd length", _interface->minPasswdLengthSpin);
+ _dictMngr->add("username level", _interface->usernameLevelSpin);
+ _dictMngr->add("algorithmic rid base", _interface->algorithmicRidBaseSpin);
+ _dictMngr->add("passwd chat timeout", _interface->passwdChatTimeoutSpin);
+
+ _dictMngr->add("encrypt passwords",_interface->encryptPasswordsChk);
+ _dictMngr->add("update encrypted",_interface->updateEncryptedChk);
+ _dictMngr->add("passwd chat debug",_interface->passwdChatDebugChk);
+ _dictMngr->add("unix password sync",_interface->unixPasswordSyncChk);
+ _dictMngr->add("use rhosts",_interface->useRhostsChk);
+ _dictMngr->add("hide local users",_interface->hideLocalUsersChk);
+
+
+ _dictMngr->add("allow trusted domains",_interface->allowTrustedDomainsChk);
+ _dictMngr->add("obey pam restrictions",_interface->obeyPamRestrictionsChk);
+ _dictMngr->add("pam password change",_interface->pamPasswordChangeChk);
+ _dictMngr->add("restrict anonymous",_interface->restrictAnonymousChk);
+ _dictMngr->add("null passwords",_interface->nullPasswordsChk);
+ _dictMngr->add("paranoid server security",_interface->paranoidServerSecurityChk);
+
+ _dictMngr->add("smb passwd file",_interface->smbPasswdFileUrlRq);
+ _dictMngr->add("passwd program",_interface->passwdProgramUrlRq);
+ _dictMngr->add("username map",_interface->usernameMapUrlRq);
+ _dictMngr->add("hosts equiv",_interface->hostsEquivUrlRq);
+ _dictMngr->add("private dir",_interface->privateDirUrlRq);
+
+ // Authentification
+
+ _dictMngr->add("lanman auth",_interface->lanmanAuthChk);
+ _dictMngr->add("ntlm auth",_interface->ntlmAuthChk);
+ _dictMngr->add("use spnego",_interface->useSpnegoChk);
+ _dictMngr->add("server schannel",_interface->serverSchannelCombo,
+ new QStringList(QStringList() << "Yes" << "No" << "Auto" ));
+ _dictMngr->add("server signing",_interface->serverSigningCombo,
+ new QStringList(QStringList() << "Auto" << "Mandatory" << "Disabled" ));
+
+ _dictMngr->add("client lanman auth",_interface->clientLanmanAuthChk);
+ _dictMngr->add("client plaintext auth",_interface->clientPlaintextAuthChk);
+ _dictMngr->add("client ntlmv2 auth",_interface->clientNTLMv2AuthChk);
+ _dictMngr->add("client use spnego",_interface->clientUseSpnegoChk);
+ _dictMngr->add("client schannel",_interface->clientSchannelCombo,
+ new QStringList(QStringList() << "Yes" << "No" << "Auto" ));
+ _dictMngr->add("client signing",_interface->clientSigningCombo,
+ new QStringList(QStringList() << "Auto" << "Mandatory" << "Disabled" ));
+
+
+
+
+
+}
+
+void KcmSambaConf::loadLogging(SambaShare* )
+{
+ _dictMngr->add("log file",_interface->logFileUrlRq);
+
+ _dictMngr->add("max log size", _interface->maxLogSizeSpin);
+ _dictMngr->add("syslog", _interface->syslogSpin);
+ _dictMngr->add("log level", _interface->logLevelEdit);
+
+ _dictMngr->add("status",_interface->statusChk);
+ _dictMngr->add("debug uid",_interface->debugUidChk);
+ _dictMngr->add("debug pid",_interface->debugPidChk);
+ _dictMngr->add("debug hires timestamp",_interface->microsecondsChk);
+ _dictMngr->add("syslog only",_interface->syslogOnlyChk);
+ _dictMngr->add("debug timestamp",_interface->timestampChk);
+ _dictMngr->add("use mmap",_interface->useMmapChk);
+
+
+}
+
+void KcmSambaConf::loadTuning(SambaShare* )
+{
+ _dictMngr->add("change notify timeout", _interface->changeNotifyTimeoutSpin);
+ _dictMngr->add("deadtime", _interface->deadtimeSpin);
+ _dictMngr->add("keepalive", _interface->keepaliveSpin);
+ _dictMngr->add("lpq cache time", _interface->lpqCacheTimeSpin);
+ _dictMngr->add("max open files", _interface->maxOpenFilesSpin);
+ _dictMngr->add("read size", _interface->readSizeSpin);
+ _dictMngr->add("max disk size", _interface->maxDiskSizeSpin);
+ _dictMngr->add("stat cache size", _interface->statCacheSizeSpin);
+ _dictMngr->add("max smbd processes", _interface->maxSmbdProcessesSpin);
+ _dictMngr->add("name cache timeout", _interface->nameCacheTimeoutSpin);
+
+ _dictMngr->add("getwd cache",_interface->getwdCacheChk);
+ _dictMngr->add("use mmap",_interface->useMmapChk);
+ _dictMngr->add("hostname lookups",_interface->hostnameLookupsChk);
+ _dictMngr->add("kernel change notify",_interface->kernelChangeNotifyChk);
+
+}
+
+void KcmSambaConf::loadLocking(SambaShare* )
+{
+ _dictMngr->add("kernel oplocks",_interface->kernelOplocksChk);
+ _dictMngr->add("lock directory",_interface->lockDirectoryUrlRq);
+ _dictMngr->add("pid directory",_interface->pidDirectoryUrlRq);
+ _dictMngr->add("oplock break wait time",_interface->oplockBreakWaitTimeSpin);
+ _dictMngr->add("lock spin time",_interface->lockSpinTimeSpin);
+ _dictMngr->add("lock spin count",_interface->lockSpinCountSpin);
+
+
+}
+
+void KcmSambaConf::loadDomain(SambaShare*)
+{
+ _dictMngr->add("preferred master",_interface->preferredMasterChk);
+ _dictMngr->add("local master",_interface->localMasterChk);
+ _dictMngr->add("domain master",_interface->domainMasterChk);
+ _dictMngr->add("domain logons",_interface->domainLogonsChk);
+ _dictMngr->add("machine password timeout", _interface->machinePasswordTimeoutSpin);
+ _dictMngr->add("os level", _interface->osLevelSpin);
+ _dictMngr->add("domain admin group", _interface->domainAdminGroupEdit);
+ _dictMngr->add("domain guest group", _interface->domainGuestGroupEdit);
+
+}
+
+void KcmSambaConf::loadWins(SambaShare* share)
+{
+ _dictMngr->add("wins proxy",_interface->winsProxyChk);
+ _dictMngr->add("dns proxy",_interface->dnsProxyChk);
+ _dictMngr->add("wins server", _interface->winsServerEdit);
+ _dictMngr->add("wins hook", _interface->winsHookEdit);
+ _interface->winsSupportRadio->setChecked( share->getBoolValue("wins support",false,true));
+ _interface->otherWinsRadio->setChecked( !share->getValue("wins server",false,true).isEmpty() );
+}
+
+
+void KcmSambaConf::loadPrinting(SambaShare* )
+{
+ _dictMngr->add("load printers",_interface->loadPrintersChk);
+ _dictMngr->add("disable spoolss",_interface->disableSpoolssChk);
+ _dictMngr->add("show add printer wizard",_interface->showAddPrinterWizardChk);
+
+ _dictMngr->add("addprinter command", _interface->addprinterCommandEdit);
+ _dictMngr->add("deleteprinter command", _interface->deleteprinterCommandEdit);
+ _dictMngr->add("enumports command", _interface->enumportsCommandEdit);
+
+ _dictMngr->add("printcap name",_interface->printcapNameUrlRq);
+ _dictMngr->add("os2 driver map",_interface->os2DriverMapUrlRq);
+ _dictMngr->add("printer driver file",_interface->printerDriverFileUrlRq);
+
+ _dictMngr->add("total print jobs", _interface->totalPrintJobsSpin);
+}
+
+void KcmSambaConf::loadFilenames(SambaShare* )
+{
+ _dictMngr->add("strip dot",_interface->stripDotChk);
+ _dictMngr->add("stat cache",_interface->statCacheChk);
+
+
+ _dictMngr->add("mangled stack", _interface->mangledStackSpin);
+ _dictMngr->add("mangle prefix", _interface->manglePrefixSpin);
+
+}
+
+void KcmSambaConf::loadProtocol(SambaShare*)
+{
+ // Protocol
+
+ _dictMngr->add("write raw",_interface->writeRawChk);
+ _dictMngr->add("read raw",_interface->readRawChk);
+ _dictMngr->add("read bmpx",_interface->readBmpxChk);
+ _dictMngr->add("large readwrite",_interface->largeReadWriteChk);
+ _dictMngr->add("nt smb support",_interface->ntSmbSupportChk);
+ _dictMngr->add("nt pipe support",_interface->ntPipeSupportChk);
+ _dictMngr->add("time server",_interface->timeServerChk);
+ _dictMngr->add("unix extensions",_interface->unixExtensionsChk);
+
+ _dictMngr->add("max mux", _interface->maxMuxSpin);
+ _dictMngr->add("max xmit", _interface->maxXmitSpin);
+ _dictMngr->add("max ttl", _interface->maxTtlSpin);
+ _dictMngr->add("max wins ttl", _interface->maxWinsTtlSpin);
+ _dictMngr->add("min wins ttl", _interface->minWinsTtlSpin);
+
+ _dictMngr->add("announce version", _interface->announceVersionEdit);
+ _dictMngr->add("name resolve order", _interface->nameResolveOrderEdit);
+ _dictMngr->add("smb ports", _interface->smbPortsEdit);
+
+ _dictMngr->add("announce as", _interface->announceAsCombo,
+ new QStringList(QStringList() << "NT" << "NT workstation" << "win95" << "WfW"));
+
+ _dictMngr->add("protocol", _interface->protocolCombo,
+ new QStringList(QStringList() << "NT" << "lanman2" << "lanman1" << "core" << "coreplus" ));
+
+ _dictMngr->add("max protocol", _interface->maxProtocolCombo,
+ new QStringList(QStringList() << "NT" << "lanman2" << "lanman1" << "core" << "coreplus"));
+
+ _dictMngr->add("min protocol", _interface->minProtocolCombo,
+ new QStringList(QStringList() << "NT" << "lanman2" << "lanman1" << "core" << "coreplus"));
+
+}
+
+void KcmSambaConf::loadSocket(SambaShare* share)
+{
+ // SOCKET options
+
+ _dictMngr->add("socket address", _interface->socketAddressEdit);
+
+ QString s = share->getValue("socket options");
+ s = s.simplifyWhiteSpace();
+
+ // The string s has now the form :
+ // "OPTION1=1 OPTION2=0 OPTION3=2234 OPTION4"
+
+ _interface->SO_KEEPALIVEChk->setChecked(getSocketBoolValue( s, "SO_KEEPALIVE") );
+ _interface->SO_REUSEADDRChk->setChecked( getSocketBoolValue( s, "SO_REUSEADDR") );
+ _interface->SO_BROADCASTChk->setChecked( getSocketBoolValue( s, "SO_BROADCAST") );
+ _interface->TCP_NODELAYChk->setChecked( getSocketBoolValue( s, "TCP_NODELAY") );
+ _interface->IPTOS_LOWDELAYChk->setChecked( getSocketBoolValue( s, "IPTOS_LOWDELAY") );
+ _interface->IPTOS_THROUGHPUTChk->setChecked( getSocketBoolValue( s, "IPTOS_THROUGHPUT") );
+
+ _interface->SO_SNDBUFChk->setChecked( getSocketBoolValue( s, "SO_SNDBUF") );
+ _interface->SO_RCVBUFChk->setChecked( getSocketBoolValue( s, "SO_RCVBUF") );
+ _interface->SO_SNDLOWATChk->setChecked( getSocketBoolValue( s, "SO_SNDLOWAT") );
+ _interface->SO_RCVLOWATChk->setChecked( getSocketBoolValue( s, "SO_RCVLOWAT") );
+
+ _interface->SO_SNDBUFSpin->setValue( getSocketIntValue( s, "SO_SNDBUF") );
+ _interface->SO_RCVBUFSpin->setValue( getSocketIntValue( s, "SO_RCVBUF") );
+ _interface->SO_SNDLOWATSpin->setValue( getSocketIntValue( s, "SO_SNDLOWAT") );
+ _interface->SO_RCVLOWATSpin->setValue( getSocketIntValue( s, "SO_RCVLOWAT") );
+
+}
+
+void KcmSambaConf::loadSSL(SambaShare*)
+{
+ // SSL
+
+ _dictMngr->add("ssl version",_interface->sslVersionCombo,
+ new QStringList(QStringList() << "ssl2" << "ssl3" << "ssl2or3" << "tls1" ));
+
+ _dictMngr->add("ssl",_interface->sslChk);
+ _dictMngr->add("ssl require server cert",_interface->sslRequireServercertChk);
+ _dictMngr->add("ssl compatibility",_interface->sslCompatibilityChk);
+ _dictMngr->add("ssl require clientcert",_interface->sslRequireClientcertChk);
+
+ _dictMngr->add("ssl hosts edit", _interface->sslHostsEdit);
+ _dictMngr->add("ssl hosts resign", _interface->sslHostsResignEdit);
+ _dictMngr->add("ssl egd socket", _interface->sslEgdSocketEdit);
+ _dictMngr->add("ssl ciphers edit", _interface->sslCiphersEdit);
+
+ _dictMngr->add("ssl CA cert dir",_interface->sslCACertDirUrlRq);
+ _dictMngr->add("ssl CA cert file",_interface->sslCACertFileUrlRq);
+ _dictMngr->add("ssl entropy file",_interface->sslEntropyFileUrlRq);
+ _dictMngr->add("ssl client cert",_interface->sslClientCertUrlRq);
+ _dictMngr->add("ssl client key",_interface->sslClientKeyUrlRq);
+ _dictMngr->add("ssl server cert",_interface->sslServerCertUrlRq);
+ _dictMngr->add("ssl server key",_interface->sslServerKeyUrlRq);
+
+ _dictMngr->add("ssl entropy bytes", _interface->sslEntropyBytesSpin);
+
+}
+
+void KcmSambaConf::loadLogon(SambaShare* )
+{
+ // Logon
+
+ _dictMngr->add("add user script", _interface->addUserScriptEdit);
+ _dictMngr->add("add group script", _interface->addGroupScriptEdit);
+ _dictMngr->add("add machine script", _interface->addMachineScriptEdit);
+ _dictMngr->add("add user to group script", _interface->addUserToGroupScriptEdit);
+ _dictMngr->add("delete user script", _interface->deleteUserScriptEdit);
+ _dictMngr->add("delete group script", _interface->deleteGroupScriptEdit);
+ _dictMngr->add("delete user from group script", _interface->deleteUserFromGroupScriptEdit);
+ _dictMngr->add("set primary group script", _interface->addGroupScriptEdit);
+ _dictMngr->add("shutdown script", _interface->shutdownScriptEdit);
+ _dictMngr->add("abort shutdown script", _interface->abortShutdownScriptEdit);
+ _dictMngr->add("logon script", _interface->logonScriptEdit);
+ _dictMngr->add("logon drive", _interface->logonDriveEdit);
+ _dictMngr->add("logon path",_interface->logonPathUrlRq);
+ _dictMngr->add("logon home",_interface->logonHomeUrlRq);
+
+}
+
+
+void KcmSambaConf::loadCharset(SambaShare* )
+{
+ _dictMngr->add("coding system", _interface->codingSystemEdit);
+ _dictMngr->add("client code page", _interface->clientCodePageEdit);
+ _dictMngr->add("code page directory",_interface->codePageDirUrlRq);
+ _dictMngr->add("display charset", _interface->displayCharsetEdit);
+ _dictMngr->add("unix charset", _interface->unixCharsetEdit);
+ _dictMngr->add("dos charset", _interface->dosCharsetEdit);
+ _dictMngr->add("character set", _interface->characterSetEdit);
+ _dictMngr->add("valid chars", _interface->validCharsEdit);
+
+ _dictMngr->add("unicode",_interface->unicodeChk);
+}
+
+void KcmSambaConf::loadWinbind(SambaShare* )
+{
+ _dictMngr->add("winbind uid", _interface->winbindUidEdit);
+ _dictMngr->add("winbind gid", _interface->winbindGidEdit);
+ _dictMngr->add("template homedir", _interface->templateHomedirEdit);
+ _dictMngr->add("template shell", _interface->templateShellEdit);
+ _dictMngr->add("winbind separator", _interface->winbindSeparatorEdit);
+ _dictMngr->add("template primary group", _interface->templatePrimaryGroupEdit);
+
+ _dictMngr->add("winbind cache time", _interface->winbindCacheTimeSpin);
+ _dictMngr->add("acl compatibility",_interface->aclCompatibilityCombo,
+ new QStringList(QStringList() << "winnt" << "win2k" << ""));
+
+ _dictMngr->add("winbind enum users",_interface->winbindEnumUsersChk);
+ _dictMngr->add("winbind enum groups",_interface->winbindEnumGroupsChk);
+ _dictMngr->add("winbind use default domain",_interface->winbindUseDefaultDomainChk);
+ _dictMngr->add("winbind trusted domains only",_interface->winbindTrustedDomainsOnlyChk);
+ _dictMngr->add("winbind enable local accounts",_interface->winbindEnableLocalAccountsChk);
+ _dictMngr->add("winbind nested groups",_interface->winbindNestedGroupsChk);
+
+
+}
+
+void KcmSambaConf::loadNetbios(SambaShare* )
+{
+ _dictMngr->add("disable netbios",_interface->disableNetbiosChk);
+
+ _dictMngr->add("netbios aliases", _interface->netbiosAliasesEdit);
+ _dictMngr->add("netbios scope", _interface->netbiosScopeEdit);
+}
+
+void KcmSambaConf::loadVFS(SambaShare*)
+{
+ _dictMngr->add("host msdfs",_interface->hostMsdfsChk);
+
+}
+
+void KcmSambaConf::loadLDAP(SambaShare*)
+{
+ _dictMngr->add("ldap suffix", _interface->ldapSuffixEdit);
+ _dictMngr->add("ldap machine suffix", _interface->ldapMachineSuffixEdit);
+ _dictMngr->add("ldap user suffix", _interface->ldapUserSuffixEdit);
+ _dictMngr->add("ldap group suffix", _interface->ldapGroupSuffixEdit);
+ _dictMngr->add("ldap idmap suffix", _interface->ldapIdmapSuffixEdit);
+ _dictMngr->add("ldap filter", _interface->ldapFilterEdit);
+ _dictMngr->add("ldap admin dn", _interface->ldapAdminDnEdit);
+ _dictMngr->add("idmap backend", _interface->idmapBackendEdit);
+
+ _dictMngr->add("ldap replication sleep",_interface->ldapReplicationSleepSpin);
+
+ _dictMngr->add("ldap delete dn",_interface->ldapDeleteDnChk);
+
+ _dictMngr->add("ldap ssl", _interface->ldapSslCombo,
+ new QStringList(QStringList() << "No" << "Start_tls" << "Yes"));
+
+ _dictMngr->add("ldap sync", _interface->ldapSyncCombo,
+ new QStringList(QStringList() << "Yes" << "No" << "Only"));
+
+
+}
+
+void KcmSambaConf::loadBrowsing(SambaShare*)
+{
+ _dictMngr->add("enhanced browsing",_interface->enhancedBrowsingChk);
+ _dictMngr->add("browse list",_interface->browseListChk);
+ _dictMngr->add("lm interval", _interface->lmIntervalSpin);
+ _dictMngr->add("remote browse sync", _interface->remoteBrowseSyncEdit);
+ _dictMngr->add("preload", _interface->preloadEdit);
+
+ _dictMngr->add("lm announce", _interface->lmAnnounceCombo,
+ new QStringList(QStringList() << "Yes" << "No" << "Auto"));
+}
+
+void KcmSambaConf::loadCommands(SambaShare*)
+{
+ _dictMngr->add("add share command", _interface->addShareCommandEdit);
+ _dictMngr->add("change share command", _interface->changeShareCommandEdit);
+ _dictMngr->add("delete share command", _interface->deleteShareCommandEdit);
+ _dictMngr->add("message command", _interface->messageCommandEdit);
+ _dictMngr->add("dfree command", _interface->dfreeCommandEdit);
+ _dictMngr->add("set quota command", _interface->setQuotaCommandEdit);
+ _dictMngr->add("get quota command", _interface->getQuotaCommandEdit);
+ _dictMngr->add("panic action", _interface->panicActionEdit);
+
+}
+
+void KcmSambaConf::setComboIndexToValue(QComboBox* box, const QString & value, SambaShare* share)
+{
+ int i = box->listBox()->index(box->listBox()->findItem(share->getValue(value,false,true),Qt::ExactMatch));
+ box->setCurrentItem(i);
+}
+
+
+void KcmSambaConf::loadMisc(SambaShare*)
+{
+ _dictMngr->add("preload modules", _interface->preloadModulesEdit);
+ _dictMngr->add("default service", _interface->defaultServiceEdit);
+ _dictMngr->add("remote announce", _interface->remoteAnnounceEdit);
+ _dictMngr->add("homedir map", _interface->homedirMapEdit);
+ _dictMngr->add("source environment", _interface->sourceEnvironmentEdit);
+
+ _dictMngr->add("utmp directory",_interface->utmpDirectoryUrlRq);
+ _dictMngr->add("wtmp directory",_interface->wtmpDirectoryUrlRq);
+
+ _dictMngr->add("NIS homedir", _interface->nisHomedirChk);
+ _dictMngr->add("time offset", _interface->timeOffsetSpin);
+}
+
+void KcmSambaConf::loadDebug(SambaShare*) {
+ _dictMngr->add("nt status support", _interface->ntStatusSupportChk);
+}
+
+
+
+void KcmSambaConf::loadUserTab()
+{
+ // Remote editing of users isn't supported yet
+ if ( _sambaFile->isRemoteFile()) {
+ _interface->mainTab->page(3)->setEnabled(false);
+ return;
+ } else
+ _interface->mainTab->page(3)->setEnabled(true);
+
+
+ SambaShare* share = _sambaFile->getShare("global");
+
+ QStringList added;
+
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+ SambaUserList sambaList = passwd.getSambaUserList();
+ _interface->sambaUsersListView->clear();
+ SambaUser *user;
+ for ( user = sambaList.first(); user; user = sambaList.next() )
+ {
+ QMultiCheckListItem* item = new QMultiCheckListItem(_interface->sambaUsersListView);
+ item->setText(0,user->name);
+ item->setText(1,QString::number(user->uid));
+ item->setOn(COL_DISABLED,user->isDisabled);
+ item->setOn(COL_NOPASSWORD,user->hasNoPassword);
+
+ if ( ! _interface->nullPasswordsChk->isOn())
+ item->setDisabled(COL_NOPASSWORD, true);
+
+ added.append(user->name);
+
+
+ }
+
+ _interface->unixUsersListView->clear();
+
+ UnixUserList unixList = getUnixUserList();
+ UnixUser *unixUser;
+ for ( unixUser = unixList.first(); unixUser; unixUser = unixList.next() )
+ {
+ QStringList::Iterator it;
+
+ it=added.find(unixUser->name);
+ if (it == added.end())
+ new KListViewItem(_interface->unixUsersListView, unixUser->name, QString::number(unixUser->uid));
+ }
+
+ _interface->unixUsersListView->setSelectionMode(QListView::Extended);
+ _interface->sambaUsersListView->setSelectionMode(QListView::Extended);
+
+}
+
+void KcmSambaConf::joinADomainBtnClicked() {
+ JoinDomainDlg *dlg = new JoinDomainDlg();
+ dlg->domainEdit->setText(_interface->workgroupEdit->text());
+ dlg->domainControllerEdit->setText(_interface->passwordServerEdit->text());
+
+ int result = dlg->exec();
+
+ if (result == QDialog::Accepted) {
+ SmbPasswdFile passwd;
+ if (!passwd.joinADomain(dlg->domainEdit->text(),
+ dlg->domainControllerEdit->text(),
+ dlg->usernameEdit->text(),
+ dlg->passwordEdit->text()))
+ {
+ KMessageBox::sorry(0,i18n("Joining the domain %1 failed.").arg(dlg->domainEdit->text()));
+ }
+ }
+ delete dlg;
+}
+
+
+void KcmSambaConf::slotMouseButtonPressed(int,QListViewItem* item,const QPoint &,int col) {
+ if (col < 2)
+ return;
+
+ SambaShare* share = _sambaFile->getShare("global");
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+ QMultiCheckListItem* i = static_cast<QMultiCheckListItem*>(item);
+ SambaUser user( item->text(0), item->text(1).toInt() );
+ user.isDisabled = i->isOn(COL_DISABLED);
+ user.hasNoPassword = i->isOn(COL_NOPASSWORD);
+
+ if (!i->isDisabled(col))
+ {
+
+ switch(col) {
+ case COL_DISABLED :
+ if (i->isOn(col))
+ passwd.enableUser(user);
+ else
+ passwd.disableUser(user);
+ break;
+ case COL_NOPASSWORD :
+ if (i->isOn(col)) {
+ sambaUserPasswordBtnClicked();
+ return; // the item is already set off by the btnClicked method
+ }
+ else
+ passwd.setNoPassword(user);
+ break;
+ }
+
+ i->toggle(col);
+ }
+}
+
+void KcmSambaConf::nullPasswordsEnabled(bool b)
+{
+ QListViewItemIterator it( _interface->sambaUsersListView );
+ for ( ; it.current(); ++it ) {
+ QMultiCheckListItem* sambaItem = static_cast<QMultiCheckListItem*>(it.current());
+ sambaItem->setDisabled(COL_NOPASSWORD,!b);
+
+ }
+}
+
+void KcmSambaConf::saveUserTab()
+{
+}
+
+void KcmSambaConf::addSambaUserBtnClicked()
+{
+ QPtrList<QListViewItem> list = _interface->unixUsersListView->selectedItems();
+
+ SambaShare* share = _sambaFile->getShare("global");
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+
+ QListViewItem* item;
+ for ( item = list.first(); item; item = list.first() )
+ {
+ SambaUser user( item->text(0), item->text(1).toInt() );
+
+ QCString password;
+ int passResult = KPasswordDialog::getNewPassword(password,
+ i18n("<qt>Please enter a password for the user <b>%1</b></qt>").arg(user.name));
+ if (passResult != KPasswordDialog::Accepted) {
+ list.remove(item);
+ continue;
+ }
+
+ if (!passwd.addUser(user,password))
+ {
+ KMessageBox::sorry(0,i18n("<qt>Adding the user <b>%1</b> to the Samba user database failed.</qt>").arg(user.name));
+ break;
+ }
+
+ QMultiCheckListItem* sambaItem = new QMultiCheckListItem(_interface->sambaUsersListView);
+ sambaItem->setText(0,user.name);
+ sambaItem->setText(1,QString::number(user.uid));
+ sambaItem->setOn(COL_DISABLED,false);
+ sambaItem->setOn(COL_NOPASSWORD,false);
+ if ( ! _interface->nullPasswordsChk->isOn())
+ sambaItem->setDisabled(COL_NOPASSWORD, true);
+
+
+ list.remove(item);
+ delete item;
+ }
+}
+
+void KcmSambaConf::removeSambaUserBtnClicked()
+{
+ QPtrList<QListViewItem> list = _interface->sambaUsersListView->selectedItems();
+
+ SambaShare* share = _sambaFile->getShare("global");
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+
+ QListViewItem* item;
+ for ( item = list.first(); item; item = list.first() )
+ {
+ SambaUser user( item->text(0), item->text(1).toInt() );
+ if (!passwd.removeUser(user))
+ {
+ KMessageBox::sorry(0,i18n("Removing the user %1 from the Samba user database failed.").arg(user.name));
+ continue;
+ }
+
+ new KListViewItem(_interface->unixUsersListView, item->text(0), item->text(1));
+ list.remove(item);
+ delete item;
+ }
+}
+
+void KcmSambaConf::sambaUserPasswordBtnClicked()
+{
+ QPtrList<QListViewItem> list = _interface->sambaUsersListView->selectedItems();
+
+ SambaShare* share = _sambaFile->getShare("global");
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+
+ QListViewItem* item;
+ for ( item = list.first(); item; item = list.next() )
+ {
+ SambaUser user( item->text(0), item->text(1).toInt() );
+
+ QCString password;
+ int passResult = KPasswordDialog::getNewPassword(password,
+ i18n("Please enter a password for the user %1").arg(user.name));
+ if (passResult != KPasswordDialog::Accepted)
+ return;
+
+ if (!passwd.changePassword(user,password))
+ {
+ KMessageBox::sorry(0,i18n("Changing the password of the user %1 failed.").arg(user.name));
+ } else {
+ static_cast<QMultiCheckListItem*>(item)->setOn(COL_NOPASSWORD,false);
+ }
+
+ }
+
+}
+
+
+void KcmSambaConf::defaults() {
+ // insert your default settings code here...
+ emit changed(true);
+}
+
+#define FILESHARECONF "/etc/security/fileshare.conf"
+
+void KcmSambaConf::save() {
+ SambaShare *share = _sambaFile->getShare("global");
+ assert(share);
+
+ kdDebug(5009) << "saving ... " << endl;
+
+ // Base settings
+
+ _smbconf = _interface->configUrlRq->url();
+ KSimpleConfig config(QString::fromLatin1(FILESHARECONF),false);
+ config.writeEntry("SMBCONF",_smbconf);
+ config.sync();
+
+ // Security
+
+ QString s;
+
+ switch (_interface->securityLevelBtnGrp->id(_interface->securityLevelBtnGrp->selected())) {
+ case 0 : s = "share";break;
+ case 1 : s = "user";break;
+ case 2 : s = "server";break;
+ case 3 : s = "domain";break;
+ case 4 : s = "ads";break;
+ }
+
+ share->setValue("security",s);
+
+
+// share->setValue("security",_interface->securityLevelCombo->currentText());
+ share->setValue("map to guest",_interface->mapToGuestCombo->currentText());
+
+ share->setValue("guest account",_interface->guestAccountCombo->currentText());
+
+ if (_interface->otherWinsRadio->isChecked())
+ share->setValue("wins server",_interface->winsServerEdit->text(), false,true);
+ else
+ share->setValue("wins server",QString(""), false,true);
+
+ // socket options
+
+ s = socketOptions();
+ share->setValue("socket options",s,false,true);
+
+
+ _dictMngr->save( share,false,true );
+
+ _sambaFile->slotApply();
+
+}
+
+bool KcmSambaConf::getSocketBoolValue( const QString & str, const QString & name )
+{
+ QString s = str;
+ int i = s.find(name ,0,false);
+
+ if (i > -1)
+ {
+ s = s.remove(0,i+1+QString(name).length());
+
+ if ( s.startsWith("=") )
+ {
+ s = s.remove(0,1);
+ if ( s.startsWith("0"))
+ return false;
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+
+ return false;
+}
+
+int KcmSambaConf::getSocketIntValue( const QString & str, const QString & name )
+{
+ QString s = str;
+ int i = s.find(name ,0,false);
+
+ if (i > -1)
+ {
+ s = s.remove(0,i+name.length());
+ if ( s.startsWith("=") )
+ {
+ s = s.remove(0,1);
+
+ i = s.find(" ");
+ if (i < 0)
+ i = s.length();
+ else
+ i++;
+
+ s = s.left( i );
+
+ return s.toInt();
+ }
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+QString KcmSambaConf::socketOptions()
+{
+ QString s = "";
+
+ if ( _interface->SO_KEEPALIVEChk->isChecked() )
+ s+="SO_KEEPALIVE ";
+
+ if ( _interface->SO_REUSEADDRChk->isChecked() )
+ s+= "SO_REUSEADDR ";
+
+ if ( _interface->SO_BROADCASTChk->isChecked() )
+ s+= "SO_BROADCAST ";
+
+ if ( _interface->TCP_NODELAYChk->isChecked() )
+ s+= "TCP_NODELAY ";
+
+ if ( _interface->IPTOS_LOWDELAYChk->isChecked() )
+ s+= "IPTOS_LOWDELAY ";
+
+ if ( _interface->IPTOS_THROUGHPUTChk->isChecked() )
+ s+= "IPTOS_THROUGHPUT ";
+
+ if ( _interface->SO_SNDBUFChk->isChecked() ) {
+ s+= "SO_SNDBUF=";
+ s+= QString::number( _interface->SO_SNDBUFSpin->value() );
+ s+= " ";
+ }
+
+ if ( _interface->SO_RCVBUFChk->isChecked() ) {
+ s+= "SO_RCVBUF=";
+ s+= QString::number( _interface->SO_RCVBUFSpin->value() );
+ s+= " ";
+ }
+
+ if ( _interface->SO_SNDLOWATChk->isChecked() ) {
+ s+= "SO_SNDLOWAT=";
+ s+= QString::number( _interface->SO_SNDLOWATSpin->value() );
+ s+= " ";
+ }
+
+ if ( _interface->SO_RCVLOWATChk->isChecked() ) {
+ s+= "SO_RCVLOWAT=";
+ s+= QString::number( _interface->SO_RCVLOWATSpin->value() );
+ s+= " ";
+
+ }
+
+ return s;
+
+}
+
+
+int KcmSambaConf::buttons () {
+ // KCModule::Default|KCModule::Apply|KCModule::Help;
+ return KCModule::Apply;
+}
+
+void KcmSambaConf::configChanged() {
+ // insert your saving code here...
+ emit changed(true);
+}
+
+QString KcmSambaConf::quickHelp() const
+{
+ return i18n("<h1>Samba Configuration</h1> here you can configure your SAMBA server.");
+}
+
+// ------------------------------------------------------------------------
+
+extern "C"
+{
+ KDE_EXPORT KCModule *create_KcmSambaConf(QWidget *parent, const char *name)
+ {
+ KGlobal::locale()->insertCatalogue("kfileshare");
+ return new KcmSambaConf(parent, name);
+ }
+}
+
+#include "kcmsambaconf.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/kcmsambaconf.desktop b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.desktop
new file mode 100644
index 00000000..f180d216
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.desktop
@@ -0,0 +1,97 @@
+[Desktop Entry]
+Exec=kcmshell kcmsambaconf
+Icon=kcmsambaconf
+Type=Application
+
+Terminal=false
+X-KDE-FactoryName=KcmSambaConf
+X-KDE-Library=kcmsambaconf
+X-KDE-ModuleType=Library
+X-KDE-SubstituteUID=false
+X-KDE-HasReadOnlyMode=true
+X-KDE-RootOnly=true
+X-KDE-ParentApp=kcontrol
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
+OnlyShowIn=KDE;
+
+Comment=A module to configure shares for Microsoft Windows
+Comment[be]=Модуль наÑÑ‚Ð°ÑžÐ»ÐµÐ½Ð½Ñ Ð°Ð³ÑƒÐ»ÑŒÐ½Ñ‹Ñ… Ñ€ÑÑурÑаў Ð´Ð»Ñ Microsoft Windows
+Comment[bg]=ÐаÑтройване ÑподелÑнето на реÑурÑи Ñ ÐœÐ°Ð¹ÐºÑ€Ð¾Ñофт УиндоуÑ
+Comment[bn]=মাইকà§à¦°à§‹à¦¸à¦«à§à¦Ÿ উইনà§à¦¡à§‹à¦œà§‡à¦° জনà§à¦¯ ভাগাভাগি কনফিগার করতে à¦à¦•à¦Ÿà¦¿ মডিউল
+Comment[bs]=Modul za konfigurisanje dijeljenja direktorija sa Microsoft Windowsom
+Comment[ca]=Un mòdul per configurar els recursos compartits de Microsoft Windows
+Comment[cs]=Modul pro konfiguraci sdílených prostředků MS Windows
+Comment[da]=Et modul til at indstille shares delt med Microsoft Windows
+Comment[de]=Ein Modul zum Einrichten von Microsoft Windows-Freigaben
+Comment[el]=Ένα άÏθÏωμα για τη ÏÏθμιση κοινόχÏηστων για τα Microsoft Windows
+Comment[es]=Un módulo para configurar recursos compartidos de Microsoft Windows
+Comment[et]=Microsoft Windowsi jagatud ressursside seadistusmoodul
+Comment[eu]=Microsoft Windows-en partekatzeak konfiguratzeko modulu bat
+Comment[fa]=یک پیمانه جهت پیکربندی مشترکات برای میکروساÙت ویندوز
+Comment[fi]=Moduli Microsoft Windows jakojen asetuksiin
+Comment[fr]=Un module permettant de configurer les partages Microsoft Windows
+Comment[gl]=Un módulo para configurar comparticións con Microsoft Windows
+Comment[he]=מודול להגדרת ×©×™×ª×•×¤×™× ×¢× ×—×œ×•× ×•×ª של מיקרוסופט
+Comment[hu]=Beállítómodul Microsoft Windows-os megosztásokhoz
+Comment[is]=Eining til að stilla sameignir fyrir Microsoft Windows
+Comment[it]=Un modulo per configurare le condivisioni per Microsoft Windows
+Comment[ja]=Microsoft Windows ã¨ã®å…±æœ‰è¨­å®š
+Comment[ka]=Microsoft Windows-ის სáƒáƒ–იáƒáƒ áƒáƒ”ბის დáƒáƒ™áƒáƒœáƒ¤áƒ˜áƒ’ურირების მáƒáƒ“ული
+Comment[kk]=Microsoft Windows-пен ортақ реÑурÑтарды баптау модулі
+Comment[km]=ម៉ូឌុល​ដើម្បី​កំណážáŸ‹â€‹ážšáž…នា​សម្ពáŸáž“្ធ​ការ​ចែក​រំលែក​សម្រាប់​ម៉ៃក្រូសូហ្វ​វ៉ីនដូ
+Comment[lt]=Modulis MS Windows dalinamų diskų konfigūravimui
+Comment[nb]=En modul som setter opp delte ressurser for Microsoft Windows
+Comment[nds]=Moduul för't Instellen vun Microsoft-Windows-Ressourcen
+Comment[ne]=माइकà¥à¤°à¥‹à¤¸à¤«à¥à¤Ÿ विनà¥à¤¡à¥‹à¤œà¤•à¤¾ लागि साà¤à¥‡à¤¦à¤¾à¤°à¥€ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨ मोडà¥à¤¯à¥à¤²
+Comment[nl]=Een module voor het instellen van shares voor Microsoft Windows
+Comment[nn]=Ein modul for oppsett av delte Microsoft Windows-ressursar
+Comment[pl]=Moduł do konfigurowania udziałów Microsoft Windows
+Comment[pt]=Um módulo para configurar partilhas para o Microsoft Windows
+Comment[pt_BR]=Um módulo para configurar compartilhamentos com o Microsoft Windows
+Comment[ru]=Модуль наÑтройки общих реÑурÑов Ð´Ð»Ñ Microsoft Windows
+Comment[sk]=Modul na konfiguráciu zdieľania pre Microsoft Windows
+Comment[sl]=Modul za nastavitev souporabe map za Microsoft Windows
+Comment[sr]=Модул који подешава дељења за Microsoft Windows
+Comment[sr@Latn]=Modul koji podešava deljenja za Microsoft Windows
+Comment[sv]=En modul för att ställa in kataloger delade med Microsoft Windows
+Comment[tr]=Microsoft Windows paylaşımlarını yapılandıran modül
+Comment[uk]=Модуль Ð´Ð»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñпільних реÑурÑів Ð´Ð»Ñ Microsoft Windows
+Comment[zh_CN]=é…ç½® Microsoft Windows 共享的模å—
+Comment[zh_HK]=設定 Microsoft Windows 分享資æºçš„模組
+Comment[zh_TW]=設定 Microsoft Windows 分享的模組
+Keywords=KcmSambaConf,kcmsambaconf,samba,windows,share
+Keywords[be]=агульны Ñ€ÑÑурÑ,KcmSambaConf,kcmsambaconf,samba,windows,share
+Keywords[bg]=Самба, УиндоуÑ, мрежа, ÑподелÑне, KcmSambaConf, kcmsambaconf, samba, windows, share
+Keywords[ca]=KcmSambaConf,kcmsambaconf,samba,windows,share,recurs compartit
+Keywords[cs]=Samba,nastavení,windows,sdílení
+Keywords[de]=KcmSambaConf,kcmsambaconf,samba,windows,freigabe
+Keywords[et]=KcmSambaConf,kcmsambaconf,samba,windows,jagatud ressursid
+Keywords[eu]=KcmSambaConf,kcmsambaconf,samba,windows,partekatze
+Keywords[fa]=KcmSambaConf، kcmsambaconf، samba، ویندوز، مشترک
+Keywords[fi]=KcmSambaConf,kcmsambaconf,samba,windows,jako
+Keywords[fr]=KcmSambaConf,kcmsambaconf,samba,windows,partage
+Keywords[gl]=KcmSambaConf,kcmsambaconf,samba,widows,compartir
+Keywords[he]=KcmSambaConf,kcmsambaconf,samba,windows,share, סמבה, חלונות, שיתוף, שיתופי חלונות
+Keywords[hu]=KcmSambaConf,kcmsambaconf,samba,windows,megosztás
+Keywords[is]=KcmSambaConf,kcmsambaconf,samba,windows,sameign
+Keywords[it]=KcmSambaConf,kcmsambaconf,samba,windows,condivisione
+Keywords[ja]=KcmSambaConf,kcmsambaconf,samba,windows,共有
+Keywords[km]=KcmSambaConf,kcmsambaconf,samba,បង្អួច,ចែក​រំលែក
+Keywords[lt]=KcmSambaConf,kcmsambaconf,samba,windows,share,dalinimasis,dalintis,pasidalinti
+Keywords[nb]=KcmSambaConf,kcmsambaconf,samba,windows,delt,ressurs
+Keywords[nds]=KcmSambaConf,kcmsambaconf,samba,windows,share,freegaav,freegaven
+Keywords[ne]=KcmSambaConf,kcmsambaconf,सामà¥à¤¬à¤¾,विनà¥à¤¡à¥‹à¤œ,साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Keywords[nl]=KcmSambaConf,kcmsambaconf,samba,windows netwerk,share,gedeelde mappen,gedeelde printers
+Keywords[nn]=KcmSambaConf,kcmsambaconf,samba,Windows,delt ressurs
+Keywords[pl]=KcmSambaConf,kcmsambaconf,samba,Samba,serwer,SMB,smb,windows,udział,dysk sieciowy
+Keywords[pt]=KcmSambaConf,kcmsambaconf,Samba,Windows,partilha
+Keywords[sl]=KcmSambaConf,kcmsambaconf,Samba,samba,souporaba,deljeno
+Keywords[sv]=IM SAMBA-inställning,IM samba-inställning,Windows,dela
+Keywords[uk]=KcmSambaConf,kcmsambaconf,samba,windows,share,Ñпільні реÑурÑи
+Keywords[zh_CN]=KcmSambaConf,kcmsambaconf,samba,windows,share,共享
+Name=Samba
+Name[bg]=Самба
+Name[bn]=সামà§à¦¬à¦¾
+Name[he]=סמבה
+Name[ne]=सामà¥à¤¬à¤¾
+Name[pa]=ਸਾਂਬਾ
diff --git a/filesharing/advanced/kcm_sambaconf/kcmsambaconf.h b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.h
new file mode 100644
index 00000000..32e37784
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/kcmsambaconf.h
@@ -0,0 +1,159 @@
+/***************************************************************************
+ kcmsambaconf.h - description
+ -------------------
+ begin : Mon Apr 8 13:35:56 CEST 2002
+ copyright : (C) 2002 by Christian Nitschkowski
+ email : segfault_ii@web.de
+ copyright : (C) 2002-2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+***************************************************************************/
+
+/******************************************************************************
+* *
+* This file is part of KSambaPlugin. *
+* *
+* KSambaPlugin 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. *
+* *
+* KSambaPlugin 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 General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with KSambaPlugin; if not, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#ifndef KCMSAMBACONF_H_
+#define KCMSAMBACONF_H_
+
+#include <kcmodule.h>
+#include <kcminterface.h>
+#include <share.h>
+#include <qptrlist.h>
+#include <qlistview.h>
+
+
+class SambaShare;
+class SambaFile;
+class QPixmap;
+/**
+* A QListViewItem which holds a SambaShare object
+**/
+class ShareListViewItem : public QListViewItem
+{
+public:
+ ShareListViewItem(QListView * parent, SambaShare* share);
+
+ SambaShare* getShare() const;
+ void setShare(SambaShare* share);
+ void updateShare();
+
+protected:
+ SambaShare* _share;
+ QPixmap createPropertyPixmap();
+};
+
+
+class KJanusWidget;
+class QLineEdit;
+class QCheckBox;
+class QSpinBox;
+class DictManager;
+class SmbConfConfigWidget;
+
+class KcmSambaConf: public KCModule
+{
+ Q_OBJECT
+ public:
+ KcmSambaConf(QWidget *parent = 0L, const char *name = 0L);
+ virtual ~KcmSambaConf();
+
+ void load(const QString &);
+ void save();
+ void defaults();
+ int buttons();
+ QString quickHelp() const;
+
+ public slots:
+ void configChanged();
+
+ void editShare();
+ void addShare();
+ void removeShare();
+
+ void editPrinter();
+ void addPrinter();
+ void removePrinter();
+
+ void editShareDefaults();
+ void editPrinterDefaults();
+
+
+ protected:
+ /**
+ * The path of the smb.conf file
+ **/
+ QString _smbconf;
+ SambaFile* _sambaFile;
+
+ DictManager* _dictMngr;
+
+ void init();
+ void loadUserTab();
+ void saveUserTab();
+ bool getSocketBoolValue( const QString & str, const QString & name );
+ int getSocketIntValue( const QString & str, const QString & name );
+ QString socketOptions();
+ void setComboIndexToValue(QComboBox* box, const QString & value, SambaShare* share);
+
+ void loadBaseSettings(SambaShare*s=0L);
+ void loadSecurity(SambaShare* );
+ void loadLogging(SambaShare* );
+ void loadTuning(SambaShare* );
+ void loadDomain(SambaShare* );
+ void loadWins(SambaShare* );
+ void loadPrinting(SambaShare* );
+ void loadFilenames(SambaShare* );
+ void loadLocking(SambaShare* );
+ void loadCharset(SambaShare*);
+ void loadLogon(SambaShare* );
+ void loadSocket(SambaShare* );
+ void loadSSL(SambaShare* );
+ void loadProtocol(SambaShare* );
+ void loadWinbind(SambaShare* );
+ void loadNetbios(SambaShare* );
+ void loadVFS(SambaShare* );
+ void loadBrowsing(SambaShare* );
+ void loadCommands(SambaShare*);
+ void loadMisc(SambaShare* );
+ void loadDebug(SambaShare* );
+ void loadLDAP(SambaShare*);
+
+ void initAdvancedTab();
+
+ void createSmbConfigWidget();
+
+ protected slots:
+ void addSambaUserBtnClicked();
+ void removeSambaUserBtnClicked();
+ void sambaUserPasswordBtnClicked();
+ void slotMouseButtonPressed(int,QListViewItem*,const QPoint &,int);
+ void joinADomainBtnClicked();
+ void nullPasswordsEnabled(bool);
+ void loadBtnClicked();
+ void loadCanceled(const QString & msg);
+ void fillFields();
+ void slotSpecifySmbConf(const QString &);
+
+ private:
+ KcmInterface* _interface;
+ KJanusWidget* _janus;
+ SmbConfConfigWidget* m_smbConfConfigWidget;
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/konqinterface.ui b/filesharing/advanced/kcm_sambaconf/konqinterface.ui
new file mode 100644
index 00000000..e3a11b7e
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/konqinterface.ui
@@ -0,0 +1,543 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>KonqInterface</class>
+<comment>
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+</comment>
+<author>Jan Schäfer &lt;janschaefer@users.sourceforge.net&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>konqinterface</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>368</width>
+ <height>417</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>btnGrp</cstring>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>notSharedRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Not share&amp;d</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>sharedRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Shared</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>baseGrp</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Bas&amp;e Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>commentEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Comment</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a text field that is seen next to a share when a client queries the server, either via the network neighborhood or via net view, to list what shares are available.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>nameEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Name of the share</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the name of the share</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="textFormat">
+ <enum>PlainText</enum>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameEdit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the name of the share</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Commen&amp;t:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>commentEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a text field that is seen next to a share when a client queries the server, either via the network neighborhood or via net view, to list what shares are available.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>securityGrp</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Security Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>denyEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>allowEdit</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Hosts den&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>denyEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Hosts allow:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>allowEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Guest &amp;account:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a username which will be used for access to services which are specified as guest ok. Whatever privileges this user has will be available to any client connecting to the guest service. Typically this user will exist in the password file, but will not have a valid login. The user account \"ftp\" is often a good choice for this parameter. If a username is specified in a given service, the specified username overrides this one.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>readOnlyChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Read only</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, then users of a service may not create or modify files in the service's directory.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>guestOkChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>G&amp;uests allowed</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked , then no password is required to connect to the service. Privileges will be those of the guest account.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a username which will be used for access this directory if guests are allowed</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>otherGrp</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="title">
+ <string>Other Options</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>browseableChk</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Bro&amp;wseable</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This controls whether this share is seen in the list of available shares in a net view and in the browse list.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>availableChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>A&amp;vailable</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Checkbox lets you \"turn off\" a service. If not checked, then ALL attempts to connect to the service will fail. Such failures are logged.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>moreOptionsBtn</cstring>
+ </property>
+ <property name="text">
+ <string>More Opt&amp;ions</string>
+ </property>
+ </widget>
+ </hbox>
+ </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>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>nameEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>commentEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>readOnlyChk</sender>
+ <signal>clicked()</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>guestOkChk</sender>
+ <signal>clicked()</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>allowEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>denyEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>browseableChk</sender>
+ <signal>clicked()</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>availableChk</sender>
+ <signal>clicked()</signal>
+ <receiver>konqinterface</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>moreOptionsBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>konqinterface</receiver>
+ <slot>moreOptionsPressed()</slot>
+ </connection>
+ <connection>
+ <sender>guestOkChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>guestAccountCombo</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>notSharedRadio</tabstop>
+ <tabstop>sharedRadio</tabstop>
+ <tabstop>nameEdit</tabstop>
+ <tabstop>commentEdit</tabstop>
+ <tabstop>readOnlyChk</tabstop>
+ <tabstop>guestOkChk</tabstop>
+ <tabstop>guestAccountCombo</tabstop>
+ <tabstop>allowEdit</tabstop>
+ <tabstop>denyEdit</tabstop>
+ <tabstop>browseableChk</tabstop>
+ <tabstop>availableChk</tabstop>
+ <tabstop>moreOptionsBtn</tabstop>
+</tabstops>
+<signals>
+ <signal>changed()</signal>
+</signals>
+<slots>
+ <slot>changedSlot()</slot>
+ <slot>moreOptionsPressed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/konqinterface.ui.h b/filesharing/advanced/kcm_sambaconf/konqinterface.ui.h
new file mode 100644
index 00000000..195d9c72
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/konqinterface.ui.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+void KonqInterface::changedSlot()
+{
+ emit changed();
+}
+
+void KonqInterface::moreOptionsPressed()
+{
+
+}
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.cpp b/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.cpp
new file mode 100644
index 00000000..17b782ab
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.cpp
@@ -0,0 +1,202 @@
+/***************************************************************************
+ begin : Tue May 17 2003
+ copyright : (C) 2003 by Jan Sch�er
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+#include <qregexp.h>
+#include <qstringlist.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "passwd.h"
+#include "sambashare.h"
+#include "linuxpermissionchecker.h"
+
+LinuxPermissionChecker::LinuxPermissionChecker(SambaShare* share,QWidget* parent = 0L)
+{
+ m_sambaShare = share;
+ m_parent = parent;
+
+ if (!share) {
+ kdWarning() << "WARNING: LinuxPermissionChecker: share is null !" << endl;
+ return;
+ }
+
+ m_fi = QFileInfo(share->getValue("path"));
+
+ if ( ! m_fi.exists()) {
+ kdDebug(5009) << "LinuxPermissionChecker: path does not exists !" << endl;
+ }
+
+}
+
+
+LinuxPermissionChecker::~LinuxPermissionChecker()
+{
+}
+
+bool LinuxPermissionChecker::checkAllPermissions() {
+ if (! m_sambaShare )
+ return true;
+
+ if ( ! m_fi.exists())
+ return true;
+
+ if (! checkPublicPermissions())
+ return false;
+
+ if (! checkAllUserPermissions())
+ return false;
+
+ return true;
+}
+
+bool LinuxPermissionChecker::checkAllUserPermissions() {
+ if (! m_sambaShare )
+ return true;
+
+ if ( ! m_fi.exists())
+ return true;
+
+ QStringList readList = QStringList::split(QRegExp("[,\\s]+"),m_sambaShare->getValue("read list"));
+
+ for ( QStringList::Iterator it = readList.begin(); it != readList.end(); ++it )
+ {
+ if (!checkUserReadPermissions(*it))
+ return false;
+ }
+
+ QStringList writeList = QStringList::split(QRegExp("[,\\s]+"),m_sambaShare->getValue("write list"));
+
+ for ( QStringList::Iterator it = writeList.begin(); it != writeList.end(); ++it )
+ {
+ if (!checkUserWritePermissions(*it))
+ return false;
+ }
+
+ return true;
+}
+
+bool LinuxPermissionChecker::checkPublicPermissions() {
+ if (! m_sambaShare )
+ return true;
+
+ bool isPublic = m_sambaShare->getBoolValue("public");
+ if (!isPublic)
+ return true;
+
+ QString guestAccount = m_sambaShare->getValue("guest account");
+
+ if ( ! checkUserReadPermissions(guestAccount,false))
+ {
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(
+ 0L,i18n(
+ "<qt>You have specified <b>public read access</b> for this directory, but "
+ "the guest account <b>%1</b> does not have the necessary read permissions;<br>"
+ "do you want to continue anyway?</qt>").arg(guestAccount)
+ ,i18n("Warning")
+ ,KStdGuiItem::cont()
+ ,"KSambaPlugin_guestAccountHasNoReadPermissionsWarning"))
+ return false;
+ }
+
+
+ if ( ! checkUserWritePermissions(guestAccount,false))
+ {
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(
+ 0L,i18n(
+ "<qt>You have specified <b>public write access</b> for this directory, but "
+ "the guest account <b>%1</b> does not have the necessary write permissions;<br>"
+ "do you want to continue anyway?</qt>").arg(guestAccount)
+ ,i18n("Warning")
+ ,KStdGuiItem::cont()
+ ,"KSambaPlugin_guestAccountHasNoWritePermissionsWarning"))
+ return false;
+ }
+
+ return true;
+}
+
+bool LinuxPermissionChecker::checkUserPermissions(const QString & user) {
+ if ( ! checkUserReadPermissions(user))
+ return false;
+
+ if ( ! checkUserWritePermissions(user))
+ return false;
+
+ return true;
+}
+
+bool LinuxPermissionChecker::checkUserWritePermissions(const QString & user, bool showMessageBox) {
+ // If no write permissions are given, we don't need to check them.
+ if (m_sambaShare->getBoolValue("read only"))
+ return true;
+
+ if (! ((m_fi.permission(QFileInfo::WriteOther)) ||
+ (m_fi.permission(QFileInfo::WriteUser) && user == m_fi.owner()) ||
+ (m_fi.permission(QFileInfo::WriteGroup) && isUserInGroup(user, m_fi.group())))
+ )
+ {
+ if (!showMessageBox)
+ return false;
+
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(
+ 0L,i18n(
+ "<qt>You have specified <b>write access</b> to the user <b>%1</b> for this directory, but "
+ "the user does not have the necessary write permissions;<br>"
+ "do you want to continue anyway?</qt>").arg(user)
+ ,i18n("Warning")
+ ,KStdGuiItem::cont()
+ ,"KSambaPlugin_userHasNoWritePermissionsWarning"))
+ return false;
+ }
+
+ return true;
+}
+
+bool LinuxPermissionChecker::checkUserReadPermissions(const QString & user, bool showMessageBox) {
+ if (! ((m_fi.permission(QFileInfo::ReadOther)) ||
+ (m_fi.permission(QFileInfo::ReadUser) && user == m_fi.owner()) ||
+ (m_fi.permission(QFileInfo::ReadGroup) && isUserInGroup(user, m_fi.group())))
+ )
+ {
+ if (!showMessageBox)
+ return false;
+
+ if (KMessageBox::Cancel == KMessageBox::warningContinueCancel(
+ 0L,i18n(
+ "<qt>You have specified <b>read access</b> to the user <b>%1</b> for this directory, but "
+ "the user does not have the necessary read permissions;<br>"
+ "do you want to continue anyway?</qt>").arg(user)
+ ,i18n("Warning")
+ ,KStdGuiItem::cont()
+ ,"KSambaPlugin_userHasNoReadPermissionsWarning"))
+ return false;
+
+ }
+
+ return true;
+}
+
diff --git a/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.h b/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.h
new file mode 100644
index 00000000..f18494b1
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/linuxpermissionchecker.h
@@ -0,0 +1,103 @@
+/***************************************************************************
+ begin : Tue May 17 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+#ifndef LINUXPERMISSIONCHECKER_H
+#define LINUXPERMISSIONCHECKER_H
+
+#include <qfileinfo.h>
+
+class SambaShare;
+class QWidget;
+
+/**
+ * A class to check if the Samba permissions specified in the SambaShare fit
+ * to the un?x permissions of the directory. E.g. a user specifies a SambaShare as writeable
+ * for the public, but the guest account has no write permissions for the directory of the
+ * SambaShare. This will cause a dialog (which can be turned of) to popup and inform the
+ * user about the missing rights and ask him to continue nevertheless or to cancel.
+ * This class works for all un?x systems not only for Linux, but I couldn't call the
+ * class Un?xPermissionChecker ;-)
+ * @author Jan Schaefer
+ */
+class LinuxPermissionChecker{
+public:
+ LinuxPermissionChecker(SambaShare*,QWidget* parent );
+ ~LinuxPermissionChecker();
+
+ /**
+ * Checks all possible errors that the user could made
+ * @returns <ul>
+ * <li><code>false</code> if the un?x permissions of the directory doesn't fit
+ * to the specified permissions in the Samba share and the user has pushed
+ * the cancel button of one of the dialogs, that informed her.
+ * <li><code>true</code> if either there haven't been any problems with the un?x
+ * permissions, or there have been problems, but the user pressed always the continue button.
+ */
+ bool checkAllPermissions();
+
+ /**
+ * Checks only the public permissions of the directory. First checks
+ * if the Samba share is specified public and then checks if the guest account
+ * has the appropriate rights.
+ * @return @see #check
+ */
+ bool checkPublicPermissions();
+
+ /**
+ * Checks all permissions of all users specified in
+ * the Samba share
+ * @return @see #check
+ */
+ bool checkAllUserPermissions();
+
+protected:
+
+
+ /**
+ * Checks all permissions of the given un?x user
+ * @return @see #check
+ */
+ bool checkUserPermissions(const QString & user);
+
+ /**
+ * Checks the write permissions of the given un?x user
+ * Does not show a dialog if showMessageBox is false
+ * @return @see #check
+ */
+ bool checkUserWritePermissions(const QString & user,bool showMessageBox = true);
+
+ /**
+ * Checks the read permissions of the given un?x user
+ * Does not show a dialog if showMessageBox is false
+ * @return @see #check
+ */
+ bool checkUserReadPermissions(const QString & user,bool showMessageBox = true);
+
+ QWidget* m_parent;
+ SambaShare* m_sambaShare;
+ QFileInfo m_fi;
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/passwd.cpp b/filesharing/advanced/kcm_sambaconf/passwd.cpp
new file mode 100644
index 00000000..532e42db
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/passwd.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+ passwd.cpp - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schaefer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <iostream>
+
+#include "passwd.h"
+
+UnixUserList getUnixUserList()
+{
+ UnixUserList list;
+
+ struct passwd* p;
+
+ while ((p = getpwent()))
+ {
+ if (!p) continue;
+
+ UnixUser *u = new UnixUser();
+ u->name = p->pw_name;
+ u->uid = p->pw_uid;
+ list.append(u);
+ }
+
+ endpwent();
+
+ list.sort();
+
+ return list;
+}
+
+QStringList getUnixUsers()
+{
+ QStringList list;
+
+ struct passwd* p;
+
+ while ((p = getpwent()))
+ {
+ if (p)
+ list.append(QString(p->pw_name));
+ }
+
+ endpwent();
+
+ list.sort();
+
+ return list;
+}
+
+QStringList getUnixGroups()
+{
+ QStringList list;
+
+ struct group* g;
+
+ while ((g = getgrent()))
+ {
+ if (g)
+ list.append(QString(g->gr_name));
+ }
+
+ endgrent();
+
+ list.sort();
+
+ return list;
+}
+
+int getUserUID(const QString & name)
+{
+ if (name.isNull()) return -1;
+
+ struct passwd* p;
+
+ p = getpwnam(name.local8Bit());
+
+ if (p)
+ return p->pw_uid;
+
+ return -1;
+}
+
+int getUserGID(const QString & name)
+{
+ if (name.isNull()) return -1;
+
+ struct passwd* p;
+
+ p = getpwnam(name.local8Bit());
+
+ if (p)
+ return p->pw_gid;
+
+ return -1;
+}
+
+int getGroupGID(const QString & name)
+{
+ if (name.isNull()) return -1;
+
+ struct group* g;
+
+ g = getgrnam(name.local8Bit());
+
+ if (g)
+ return g->gr_gid;
+
+ return -1;
+}
+
+bool isUserInGroup(const QString & user, const QString & group) {
+ struct group* g;
+
+ while ((g = getgrent()))
+ {
+ if (g && QString(g->gr_name) == group) {
+ char** names = g->gr_mem;
+
+ int i = 0;
+ char* name = names[0];
+ while (name != 0L) {
+ i++;
+ if (QString(name) == user) {
+ endgrent();
+ return true;
+ }
+ name = names[i];
+ }
+ break;
+ }
+ }
+
+ endgrent();
+ return false;
+}
diff --git a/filesharing/advanced/kcm_sambaconf/passwd.h b/filesharing/advanced/kcm_sambaconf/passwd.h
new file mode 100644
index 00000000..490e0381
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/passwd.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ passwd.h - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef PASSWD_H
+#define PASSWD_H
+
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qptrlist.h>
+
+
+
+class UnixUser
+{
+public:
+ QString name;
+ int uid;
+};
+
+typedef QPtrList<UnixUser> UnixUserList;
+
+UnixUserList getUnixUserList();
+QStringList getUnixUsers();
+QStringList getUnixGroups();
+int getUserUID(const QString &);
+int getUserGID(const QString &);
+int getGroupGID(const QString &);
+bool isUserInGroup(const QString &, const QString &);
+
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/printerdlgimpl.cpp b/filesharing/advanced/kcm_sambaconf/printerdlgimpl.cpp
new file mode 100644
index 00000000..2910a1a7
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/printerdlgimpl.cpp
@@ -0,0 +1,241 @@
+/***************************************************************************
+ printerdlgimpl.cpp - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+
+/**
+ * @author Jan Schäfer
+ **/
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qpainter.h>
+#include <qgroupbox.h>
+#include <qpixmap.h>
+#include <qbitmap.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+
+#include <klineedit.h>
+#include <kurlrequester.h>
+#include <knuminput.h>
+#include <kdebug.h>
+#include <kdeprint/kmmanager.h>
+#include <kdeprint/kmprinter.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <assert.h>
+
+#include "sambafile.h"
+#include "printerdlgimpl.h"
+#include "usertabimpl.h"
+#include "passwd.h"
+#include "dictmanager.h"
+
+
+PrinterDlgImpl::PrinterDlgImpl(QWidget* parent, SambaShare* share)
+ : KcmPrinterDlg(parent,"sharedlgimpl")
+{
+ if (!share) {
+ kdWarning() << "PrinterDlgImpl::Constructor : share parameter is null!" << endl;
+ return;
+ }
+
+ _share = share;
+ _dictMngr = new DictManager(share);
+
+ initDialog();
+}
+
+void PrinterDlgImpl::initDialog()
+{
+ // Base settings
+ if (!_share)
+ return;
+
+ QPtrList<KMPrinter> *printerList = KMManager::self()->printerListComplete();
+
+ for (QPtrListIterator<KMPrinter> it(*printerList); it.current(); ++it)
+ {
+ if (!it.current()->isSpecial()){
+ queueCombo->insertItem(it.current()->printerName());
+ }
+ }
+
+ setComboToString(queueCombo,_share->getValue("printer name"));
+
+
+ _dictMngr->add("path",pathUrlRq);
+ printersChk->setChecked( _share->getName() == "printers" );
+
+ shareNameEdit->setText( _share->getName() );
+ _dictMngr->add("comment",commentEdit);
+
+ _dictMngr->add("available",availableBaseChk);
+ _dictMngr->add("browseable",browseableBaseChk);
+ _dictMngr->add("public",publicBaseChk);
+
+ // Users
+
+ _userTab = new UserTabImpl(this,_share);
+ _tabs->insertTab(_userTab,i18n("&Users"),1);
+ _userTab->load();
+ connect(_userTab, SIGNAL(changed()), this, SLOT(changedSlot()));
+
+
+ // Printing
+
+ _dictMngr->add("postscript",postscriptChk);
+ _dictMngr->add("use client driver",useClientDriverChk);
+ _dictMngr->add("default devmode",defaultDevmodeChk);
+
+ _dictMngr->add("max print jobs",maxPrintJobsSpin);
+ _dictMngr->add("max reported print jobs",maxReportedPrintJobsSpin);
+ _dictMngr->add("printing",printingCombo,
+ new QStringList(QStringList()
+ << "sysv" << "aix" << "hpux" << "bsd" << "qnx"
+ << "plp" << "lprng" << "softq" << "cups" << "nt" << "os2"));
+
+ _dictMngr->add("printer driver",printerDriverEdit);
+ _dictMngr->add("printer driver location",printerDriverLocationEdit);
+
+ // Commands
+
+ _dictMngr->add("print command",printCommandEdit);
+ _dictMngr->add("lpq command",lpqCommandEdit);
+ _dictMngr->add("lprm command",lprmCommandEdit);
+ _dictMngr->add("lppause",lppauseEdit);
+ _dictMngr->add("lpresume",lpresumeEdit);
+ _dictMngr->add("queuepause",queuepauseEdit);
+ _dictMngr->add("queueresume",queueresumeEdit);
+
+ // Security
+
+ _dictMngr->add("printer admin",printerAdminEdit);
+ _dictMngr->add("hosts allow",hostsAllowEdit);
+ _dictMngr->add("hosts deny",hostsDenyEdit);
+
+ guestAccountCombo->insertStringList( getUnixUsers() );
+ setComboToString(guestAccountCombo,_share->getValue("guest account"));
+
+ // Advanced
+
+ _dictMngr->add("min print space",minPrintSpaceSpin);
+ _dictMngr->add("status",statusChk);
+ _dictMngr->add("preexec",preExecEdit);
+ _dictMngr->add("postexec",postExecEdit);
+ _dictMngr->add("root preexec",rootPreExecEdit);
+ _dictMngr->add("root postexec",rootPostExecEdit);
+
+ _dictMngr->load( _share );
+ connect(_dictMngr, SIGNAL(changed()), this, SLOT(changedSlot()));
+
+}
+
+void PrinterDlgImpl::accept()
+{
+ if (!_share)
+ return;
+
+ // Security
+
+ _share->setValue("guest account",guestAccountCombo->currentText( ) );
+ _share->setValue("printer name",queueCombo->currentText());
+
+ if (printersChk->isChecked())
+ {
+ _share->setName("printers");
+ }
+ else
+ _share->setName(shareNameEdit->text());
+
+
+ _userTab->save();
+ _dictMngr->save( _share );
+
+
+ KcmPrinterDlg::accept();
+}
+
+PrinterDlgImpl::~PrinterDlgImpl()
+{
+}
+
+void PrinterDlgImpl::printersChkToggled(bool b)
+{
+ if (b)
+ {
+ shareNameEdit->setText("printers");
+ shareNameEdit->setEnabled(false);
+
+ int dist = 10;
+ int w = 64 + dist;
+ int h = 64 + 2*dist;
+
+ QPixmap pix(w,h);
+ pix.fill(); // fill with white
+
+ QPixmap pix2 = DesktopIcon("printer1");
+
+ // Draw the printericon three times
+ QPainter p(&pix);
+ p.drawPixmap(dist+dist/2,0,pix2);
+ p.drawPixmap(dist/2,dist,pix2);
+ p.drawPixmap(dist+dist/2,2*dist,pix2);
+ p.end();
+
+ QBitmap mask(w,h);
+
+ mask.fill(Qt::black); // everything is transparent
+
+ p.begin(&mask);
+
+ p.setRasterOp(Qt::OrROP);
+ p.drawPixmap(dist+dist/2,0,*pix2.mask());
+ p.drawPixmap(dist/2,dist,*pix2.mask());
+ p.drawPixmap(dist+dist/2,2*dist,*pix2.mask());
+ p.end();
+
+ pix.setMask(mask);
+
+ printerPixLbl->setPixmap(pix);
+ pixFrame->layout()->setMargin( 2 );
+ }
+ else
+ {
+ shareNameEdit->setEnabled(true);
+ shareNameEdit->setText( _share->getName() );
+ printerPixLbl->setPixmap(DesktopIcon("printer1"));
+ pixFrame->layout()->setMargin( 11 );
+ }
+}
+
+#include "printerdlgimpl.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/printerdlgimpl.h b/filesharing/advanced/kcm_sambaconf/printerdlgimpl.h
new file mode 100644
index 00000000..d7488985
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/printerdlgimpl.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ printerdlgimpl.h - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef PRINTERDLGIMPL_H
+#define PRINTERDLGIMPL_H
+
+
+/**
+ * @author Jan Schäfer
+ **/
+
+#include "kcmprinterdlg.h"
+
+class SambaShare;
+class QWidget;
+class UserTabImpl;
+class DictManager;
+
+/**
+ * This class implements the tcmprinterdlg.ui interface
+ **/
+class PrinterDlgImpl : public KcmPrinterDlg
+{
+Q_OBJECT
+
+public :
+
+ PrinterDlgImpl(QWidget* parent, SambaShare* share);
+ ~PrinterDlgImpl();
+
+protected :
+
+ /**
+ * Fills all dialog fields with the values
+ * of the SambaShare object
+ **/
+ void initDialog();
+
+ /**
+ * The share object to change with this dialog
+ **/
+ SambaShare* _share;
+
+ UserTabImpl* _userTab;
+ DictManager* _dictMngr;
+
+protected slots:
+ void accept();
+ virtual void printersChkToggled(bool);
+};
+
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/programmingconventions.txt b/filesharing/advanced/kcm_sambaconf/programmingconventions.txt
new file mode 100644
index 00000000..b9019e74
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/programmingconventions.txt
@@ -0,0 +1,10 @@
+Programming conventions :
+
+Class member variable : starting with _ e.g. _name
+
+QCheckBox variable : ending to Chk e.g. nameChk
+QLineEdit variable : ending to Edit e.g. nameEdit
+KUrlRequerster variable : ending to UrlRq e.g. pathUrlRq
+QPushButton : ending to Btn e.g. addBtn
+
+QListView: ending to ListView e.g. sharesListView
diff --git a/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.cpp b/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.cpp
new file mode 100644
index 00000000..44383f6d
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.cpp
@@ -0,0 +1,159 @@
+/***************************************************************************
+ qmultichecklistitem.cpp - description
+ -------------------
+ begin : Sun Jan 26 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <qpen.h>
+#include <qpainter.h>
+#include <qvbox.h>
+#include <qheader.h>
+
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include "qmultichecklistitem.moc"
+#include "qmultichecklistitem.h"
+
+static const int BoxSize = 16;
+
+
+QMultiCheckListItem::QMultiCheckListItem( QListView *parent=0) :
+ QListViewItem(parent) {
+}
+
+void QMultiCheckListItem::setOn(int column, bool b) {
+ if (column >= (int) checkBoxColumns.size()) {
+ checkBoxColumns.resize(column*2);
+ checkStates.resize(column*2);
+ }
+
+ checkStates.setBit(column,b);
+ checkBoxColumns.setBit(column);
+ kdDebug(5009) << "setOn : " << column << endl;
+ repaint();
+}
+
+bool QMultiCheckListItem::isOn(int column) {
+ return checkStates.testBit(column);
+}
+
+bool QMultiCheckListItem::isDisabled(int column) {
+ return disableStates.testBit(column);
+}
+
+void QMultiCheckListItem::toggle(int column) {
+ if (column >= (int) checkBoxColumns.size()) {
+ checkBoxColumns.resize(column*2);
+ checkStates.resize(column*2);
+ }
+
+ checkBoxColumns.setBit(column);
+ checkStates.toggleBit(column);
+ emit stateChanged(column,checkStates.testBit(column));
+
+ repaint();
+}
+
+void QMultiCheckListItem::setDisabled(int column, bool b) {
+ if (column >= (int) disableStates.size()) {
+ disableStates.resize(column*2);
+ }
+
+ disableStates.setBit(column,b);
+// KMessageBox::information(0L,QString("setDisabled"),QString("disable %1 ").arg(column));
+ repaint();
+}
+
+void QMultiCheckListItem::paintCell(QPainter *p,const QColorGroup & cg, int col, int width, int align)
+{
+
+ if ( !p )
+ return;
+
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+
+ QListViewItem::paintCell(p,cg,col,width,align );
+
+ int marg = lv->itemMargin();
+// int width = BoxSize + marg*2;
+ // use a provate color group and set the text/highlighted text colors
+ QColorGroup mcg = cg;
+
+ if (checkBoxColumns.testBit(col)) {
+ // Bold/Italic/use default checkboxes
+ // code allmost identical to QCheckListItem
+ Q_ASSERT( lv ); //###
+ // I use the text color of defaultStyles[0], normalcol in parent listview
+// mcg.setColor( QColorGroup::Text, ((StyleListView*)lv)->normalcol );
+ int x = 0;
+ if ( align == AlignCenter ) {
+ QFontMetrics fm( lv->font() );
+ x = (width - BoxSize - fm.width(text(0)))/2;
+ }
+ int y = (height() - BoxSize) / 2;
+
+ if ( !isEnabled() || disableStates.testBit(col))
+ p->setPen( QPen( lv->palette().color( QPalette::Disabled, QColorGroup::Text ), 2 ) );
+ else
+ p->setPen( QPen( mcg.text(), 2 ) );
+
+ if ( isSelected() && lv->header()->mapToSection( 0 ) != 0 ) {
+ p->fillRect( 0, 0, x + marg + BoxSize + 4, height(),
+ mcg.brush( QColorGroup::Highlight ) );
+ if ( isEnabled() )
+ p->setPen( QPen( mcg.highlightedText(), 2 ) ); // FIXME! - use defaultstyles[0].selecol. luckily not used :)
+ }
+ p->drawRect( x+marg, y+2, BoxSize-4, BoxSize-4 );
+ x++;
+ y++;
+ if ( checkStates.testBit(col) ) {
+ QPointArray a( 7*2 );
+ int i, xx, yy;
+ xx = x+1+marg;
+ yy = y+5;
+ for ( i=0; i<3; i++ ) {
+ a.setPoint( 2*i, xx, yy );
+ a.setPoint( 2*i+1, xx, yy+2 );
+ xx++; yy++;
+ }
+ yy -= 2;
+ for ( i=3; i<7; i++ ) {
+ a.setPoint( 2*i, xx, yy );
+ a.setPoint( 2*i+1, xx, yy+2 );
+ xx++; yy--;
+ }
+ p->drawLineSegments( a );
+ }
+
+ }
+
+
+}
+
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.h b/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.h
new file mode 100644
index 00000000..0f0d2410
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/qmultichecklistitem.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ qextendedchecklistitem.h - description
+ -------------------
+ begin : Sun Jan 26 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef _QMULTICHECKLISTITEM_H_
+#define _QMULTICHECKLISTITEM_H_
+
+#include <qlistview.h>
+
+#include <qbitarray.h>
+
+class QMultiCheckListItem : public QObject, public QListViewItem {
+Q_OBJECT
+
+ public:
+ QMultiCheckListItem( QListView *parent);
+ ~QMultiCheckListItem() {};
+
+ virtual bool isOn(int column);
+ virtual bool isDisabled(int column);
+
+
+ protected:
+ /* reimp */
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+
+ public slots:
+ virtual void setOn(int, bool);
+ virtual void toggle(int);
+ virtual void setDisabled(int, bool);
+
+ signals:
+ void stateChanged(int, bool);
+ private:
+ QBitArray checkStates;
+ QBitArray checkBoxColumns;
+ QBitArray disableStates;
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/sambafile.cpp b/filesharing/advanced/kcm_sambaconf/sambafile.cpp
new file mode 100644
index 00000000..f1f48263
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sambafile.cpp
@@ -0,0 +1,701 @@
+/*
+ Copyright (c) 2002-2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <qfile.h>
+#include <qprocess.h>
+#include <qfileinfo.h>
+
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+#include <qfileinfo.h>
+#include <kio/job.h>
+#include <kprocess.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <ksambashare.h>
+
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "sambafile.h"
+
+#define FILESHARE_DEBUG 5009
+
+SambaConfigFile::SambaConfigFile(SambaFile* sambaFile)
+{
+ QDict<QString>(10,false);
+ setAutoDelete(true);
+ _sambaFile = sambaFile;
+}
+
+QString SambaConfigFile::getDefaultValue(const QString & name)
+{
+ SambaShare* defaults = _sambaFile->getTestParmValues();
+ QString s = defaults->getValue(name,false,false);
+
+ return s;
+}
+
+SambaShare* SambaConfigFile::addShare(const QString & name)
+{
+ SambaShare* newShare = new SambaShare(name,this);
+ addShare(name,newShare);
+ return newShare;
+}
+
+
+void SambaConfigFile::addShare(const QString & name, SambaShare* share)
+{
+ insert(name,share),
+ _shareList.append(name);
+}
+
+void SambaConfigFile::removeShare(const QString & name)
+{
+ remove(name);
+ _shareList.remove(name);
+}
+
+
+QStringList SambaConfigFile::getShareList()
+{
+ return _shareList;
+}
+
+SambaFile::SambaFile(const QString & _path, bool _readonly)
+ : readonly(_readonly),
+ changed(false),
+ path(_path),
+ localPath(_path),
+ _sambaConfig(0),
+ _testParmValues(0),
+ _sambaVersion(-1),
+ _tempFile(0)
+{
+}
+
+SambaFile::~SambaFile()
+{
+ delete _sambaConfig;
+ delete _testParmValues;
+ delete _tempFile;
+
+}
+
+bool SambaFile::isRemoteFile() {
+ return ! KURL(path).isLocalFile();
+}
+
+/** No descriptions */
+QString SambaFile::findShareByPath(const QString & path) const
+{
+ QDictIterator<SambaShare> it(*_sambaConfig);
+ KURL url(path);
+ url.adjustPath(-1);
+
+ for ( ; it.current(); ++it )
+ {
+ SambaShare* share = it.current();
+
+ QString *s = share->find("path");
+ if (s) {
+ KURL curUrl(*s);
+ curUrl.adjustPath(-1);
+
+ kdDebug(5009) << url.path() << " =? " << curUrl.path() << endl;
+
+ if (url.path() == curUrl.path())
+ return it.currentKey();
+ }
+ }
+
+ return QString::null;
+}
+
+bool SambaFile::save() {
+ return slotApply();
+}
+
+
+bool SambaFile::slotApply()
+{
+ if (readonly) {
+ kdDebug(FILESHARE_DEBUG) << "SambaFile::slotApply: readonly=true" << endl;
+ return false;
+ }
+
+ // If we have write access to the smb.conf
+ // we simply save the values to it
+ // if not we have to save the results in
+ // a temporary file and copy it afterwards
+ // over the smb.conf file with kdesu.
+ if (QFileInfo(path).isWritable())
+ {
+ saveTo(path);
+ changed = false;
+ return true;
+ }
+
+ // Create a temporary smb.conf file
+ delete _tempFile;
+ _tempFile = new KTempFile();
+ _tempFile->setAutoDelete(true);
+
+ if (!saveTo(_tempFile->name())) {
+ kdDebug(5009) << "SambaFile::slotApply: Could not save to temporary file" << endl;
+ delete _tempFile;
+ _tempFile = 0;
+ return false;
+ }
+
+ QFileInfo fi(path);
+ KURL url(path);
+
+ if (KURL(path).isLocalFile()) {
+ KProcess proc;
+ kdDebug(5009) << "SambaFile::slotApply: is local file!" << endl;
+
+ QString suCommand=QString("cp %1 %2; rm %3")
+ .arg(_tempFile->name())
+ .arg(path)
+ .arg(_tempFile->name());
+ proc << "kdesu" << "-d" << suCommand;
+
+ if (! proc.start(KProcess::Block)) {
+ kdDebug(5009) << "SambaFile::slotApply: saving to " << path << " failed!" << endl;
+ //KMessageBox::sorry(0,i18n("Saving the results to %1 failed.").arg(path));
+ delete _tempFile;
+ _tempFile = 0;
+ return false;
+ }
+ else {
+ changed = false;
+ delete _tempFile;
+ _tempFile = 0;
+ kdDebug(5009) << "SambaFile::slotApply: changes successfully saved!" << endl;
+ return true;
+ }
+ } else {
+ kdDebug(5009) << "SambaFile::slotApply: is remote file!" << endl;
+ _tempFile->setAutoDelete(true);
+ KURL srcURL;
+ srcURL.setPath( _tempFile->name() );
+
+ KIO::FileCopyJob * job = KIO::file_copy( srcURL, url, -1, true );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ this, SLOT( slotSaveJobFinished ( KIO::Job * ) ) );
+ return (job->error()==0);
+ }
+
+ return true;
+}
+
+ /**
+ * Returns a name which isn't already used for a share
+ **/
+QString SambaFile::getUnusedName(const QString alreadyUsedName) const
+{
+
+ QString init = i18n("Unnamed");
+ if (alreadyUsedName != QString::null)
+ init = alreadyUsedName;
+
+ QString s = init;
+
+ int i = 2;
+
+ while (_sambaConfig->find(s))
+ {
+ s = init+QString::number(i);
+ i++;
+ }
+
+ return s;
+}
+
+
+
+SambaShare* SambaFile::newShare(const QString & name)
+{
+ if (_sambaConfig->find(name))
+ return 0L;
+
+ SambaShare* share = new SambaShare(name,_sambaConfig);
+ _sambaConfig->addShare(name,share);
+
+ changed = true;
+
+ return share;
+
+}
+
+SambaShare* SambaFile::newShare(const QString & name, const QString & path)
+{
+ SambaShare* share = newShare(name);
+ if (share)
+ {
+ share->setValue("path",path);
+ }
+
+ return share;
+}
+
+SambaShare* SambaFile::newPrinter(const QString & name, const QString & printer)
+{
+ SambaShare* share = newShare(name);
+
+ if (share)
+ {
+ share->setValue("printable",true);
+ share->setValue("printer name",printer);
+ }
+
+ return share;
+}
+
+
+/** No descriptions */
+void SambaFile::removeShare(const QString & share)
+{
+ changed = true;
+
+ _sambaConfig->removeShare(share);
+}
+
+void SambaFile::removeShare(SambaShare* share)
+{
+ removeShare(share->getName());
+}
+
+void SambaFile::removeShareByPath(const QString & path) {
+ QString share = findShareByPath(path);
+ removeShare(share);
+}
+
+/** No descriptions */
+SambaShare* SambaFile::getShare(const QString & share) const
+{
+ SambaShare *s = _sambaConfig->find(share);
+
+ return s;
+}
+
+/**
+* Returns a list of all shared directories
+**/
+SambaShareList* SambaFile::getSharedDirs() const
+{
+ SambaShareList* list = new SambaShareList();
+
+ QDictIterator<SambaShare> it(*_sambaConfig);
+
+ for( ; it.current(); ++it )
+ {
+ if (!it.current()->isPrinter() &&
+ it.current()->getName() != "global")
+ {
+ list->append(it.current());
+ }
+ }
+
+ return list;
+}
+
+/**
+* Returns a list of all shared printers
+**/
+SambaShareList* SambaFile::getSharedPrinters() const
+{
+ SambaShareList* list = new SambaShareList();
+
+ QDictIterator<SambaShare> it(*_sambaConfig);
+
+ for( ; it.current(); ++it )
+ {
+ if (it.current()->isPrinter())
+ list->append(it.current());
+ }
+
+ return list;
+}
+
+int SambaFile::getSambaVersion() {
+ if (_sambaVersion > -1)
+ return _sambaVersion;
+
+ KProcess testParam;
+ testParam << "testparm";
+ testParam << "-V";
+ _parmOutput = QString("");
+ _sambaVersion = 2;
+
+ connect( &testParam, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(testParmStdOutReceived(KProcess*,char*,int)));
+
+
+
+ if (testParam.start(KProcess::Block,KProcess::Stdout)) {
+ if (_parmOutput.find("3") > -1)
+ _sambaVersion = 3;
+ }
+
+ kdDebug(5009) << "Samba version = " << _sambaVersion << endl;
+
+ return _sambaVersion;
+}
+
+
+SambaShare* SambaFile::getTestParmValues(bool reload)
+{
+ if (_testParmValues && !reload)
+ return _testParmValues;
+
+
+ KProcess testParam;
+ testParam << "testparm";
+ testParam << "-s";
+
+ if (getSambaVersion() == 3)
+ testParam << "-v";
+
+
+ testParam << "/dev/null";
+ _parmOutput = QString("");
+
+ connect( &testParam, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(testParmStdOutReceived(KProcess*,char*,int)));
+
+ if (testParam.start(KProcess::Block,KProcess::Stdout))
+ {
+ parseParmStdOutput();
+ } else
+ _testParmValues = new SambaShare(_sambaConfig);
+
+ return _testParmValues;
+}
+
+void SambaFile::testParmStdOutReceived(KProcess *, char *buffer, int buflen)
+{
+ _parmOutput+=QString::fromLatin1(buffer,buflen);
+}
+
+void SambaFile::parseParmStdOutput()
+{
+
+ QTextIStream s(&_parmOutput);
+
+ if (_testParmValues)
+ delete _testParmValues;
+ _testParmValues = new SambaShare(_sambaConfig);
+
+ QString section="";
+
+ while (!s.atEnd())
+ {
+ QString line = s.readLine().stripWhiteSpace();
+
+ // empty lines
+ if (line.isEmpty())
+ continue;
+
+ // comments
+ if ('#' == line[0])
+ continue;
+
+ // sections
+ if ('[' == line[0])
+ {
+ // get the name of the section
+ section = line.mid(1,line.length()-2).lower();
+ continue;
+ }
+
+ // we are only interested in the global section
+ if (section != KGlobal::staticQString("global"))
+ continue;
+
+ // parameter
+ // parameter
+ int i = line.find('=');
+
+ if (i>-1) {
+ QString name = line.left(i).stripWhiteSpace();
+ QString value = line.mid(i+1).stripWhiteSpace();
+ _testParmValues->setValue(name,value,false,false);
+ }
+
+ }
+
+
+
+}
+
+/**
+* Try to find the samba config file position
+* First tries the config file, then checks
+* several common positions
+* If nothing is found returns QString::null
+**/
+QString SambaFile::findSambaConf()
+{
+ return KSambaShare::instance()->smbConfPath();
+}
+
+void SambaFile::slotSaveJobFinished( KIO::Job * job ) {
+ delete _tempFile;
+ _tempFile = 0;
+}
+
+void SambaFile::slotJobFinished( KIO::Job * job )
+{
+ if (job->error())
+ emit canceled( job->errorString() );
+ else
+ {
+ openFile();
+ emit completed();
+ }
+}
+
+bool SambaFile::load()
+{
+ if (path.isNull() || path.isEmpty())
+ return false;
+
+ kdDebug(FILESHARE_DEBUG) << "SambaFile::load: path=" << path << endl;
+ KURL url(path);
+
+ if (!url.isLocalFile()) {
+ KTempFile tempFile;
+ localPath = tempFile.name();
+ KURL destURL;
+ destURL.setPath( localPath );
+ KIO::FileCopyJob * job = KIO::file_copy( url, destURL, 0600, true, false, true );
+// emit started( d->m_job );
+ connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobFinished ( KIO::Job * ) ) );
+ return true;
+ } else {
+ localPath = path;
+ bool ret = openFile();
+ if (ret)
+ emit completed();
+ return ret;
+ }
+}
+
+bool SambaFile::openFile() {
+
+ QFile f(localPath);
+
+ if (!f.open(IO_ReadOnly)) {
+ //throw SambaFileLoadException(QString("<qt>Could not open file <em>%1</em> for reading.</qt>").arg(path));
+ return false;
+ }
+
+ QTextStream s(&f);
+
+ delete _sambaConfig;
+
+ _sambaConfig = new SambaConfigFile(this);
+
+ SambaShare *currentShare = 0L;
+ bool continuedLine = false; // is true if the line before ended with a backslash
+ QString completeLine;
+ QStringList comments;
+
+ while (!s.eof())
+ {
+ QString currentLine = s.readLine().stripWhiteSpace();
+
+ if (continuedLine)
+ {
+ completeLine += currentLine;
+ continuedLine = false;
+ } else
+ completeLine = currentLine;
+
+ // is the line continued in the next line ?
+ if ( completeLine[completeLine.length()-1] == '\\' )
+ {
+ continuedLine = true;
+ // remove the ending backslash
+ completeLine.truncate( completeLine.length()-1 );
+ continue;
+ }
+
+ // comments or empty lines
+ if (completeLine.isEmpty() ||
+ '#' == completeLine[0] ||
+ ';' == completeLine[0])
+ {
+ comments.append(completeLine);
+ continue;
+ }
+
+
+ // sections
+ if ('[' == completeLine[0])
+ {
+ // get the name of the section
+ QString section = completeLine.mid(1,completeLine.length()-2);
+ currentShare = _sambaConfig->addShare(section);
+ currentShare->setComments(comments);
+ comments.clear();
+
+ continue;
+ }
+
+ // parameter
+ int i = completeLine.find('=');
+
+ if (i>-1)
+ {
+ QString name = completeLine.left(i).stripWhiteSpace();
+ QString value = completeLine.mid(i+1).stripWhiteSpace();
+
+ if (currentShare)
+ {
+ currentShare->setComments(name,comments);
+ currentShare->setValue(name,value,true,true);
+
+ comments.clear();
+ }
+ }
+ }
+
+ f.close();
+
+ // Make sure there is a global share
+ if (!getShare("global")) {
+ _sambaConfig->addShare("global");
+ }
+
+ return true;
+}
+
+bool SambaFile::saveTo(const QString & path)
+{
+ QFile f(path);
+
+ if (!f.open(IO_WriteOnly))
+ return false;
+
+ QTextStream s(&f);
+
+ QStringList shareList = _sambaConfig->getShareList();
+
+ for ( QStringList::Iterator it = shareList.begin(); it != shareList.end(); ++it )
+ {
+ SambaShare* share = _sambaConfig->find(*it);
+
+ // First add all comments of the share to the file
+ QStringList comments = share->getComments();
+ for ( QStringList::Iterator cmtIt = comments.begin(); cmtIt != comments.end(); ++cmtIt )
+ {
+ s << *cmtIt << endl;
+
+ kdDebug(5009) << *cmtIt << endl;
+ }
+
+ // If there are no lines before the section add
+ // a blank line
+ if (comments.isEmpty())
+ s << endl;
+
+ // Add the name of the share / section
+ s << "[" << share->getName() << "]" << endl;
+
+ // Add all options of the share
+ QStringList optionList = share->getOptionList();
+
+ for ( QStringList::Iterator optionIt = optionList.begin(); optionIt != optionList.end(); ++optionIt )
+ {
+
+ // Add the comments of the option
+ comments = share->getComments(*optionIt);
+ for ( QStringList::Iterator cmtIt = comments.begin(); cmtIt != comments.end(); ++cmtIt )
+ {
+ s << *cmtIt << endl;
+ }
+
+ // Add the option
+ s << *optionIt << " = " << *share->find(*optionIt) << endl;
+ }
+
+
+ }
+
+ f.close();
+
+ return true;
+}
+
+
+SambaConfigFile* SambaFile::getSambaConfigFile(KSimpleConfig* config)
+{
+ QStringList groups = config->groupList();
+
+ SambaConfigFile* samba = new SambaConfigFile(this);
+
+ for ( QStringList::Iterator it = groups.begin(); it != groups.end(); ++it )
+ {
+ QMap<QString,QString> entries = config->entryMap(*it);
+
+ SambaShare *share = new SambaShare(*it,samba);
+ samba->insert(*it,share);
+
+ for (QMap<QString,QString>::Iterator it2 = entries.begin(); it2 != entries.end(); ++it2 )
+ {
+ if (!it2.data().isEmpty())
+ share->setValue(it2.key(),QString(it2.data()),false,false);
+ }
+
+ }
+
+ return samba;
+
+}
+
+KSimpleConfig* SambaFile::getSimpleConfig(SambaConfigFile* sambaConfig, const QString & path)
+{
+ KSimpleConfig *config = new KSimpleConfig(path,false);
+
+ QDictIterator<SambaShare> it(*sambaConfig);
+
+ for ( ; it.current(); ++it )
+ {
+ SambaShare* share = it.current();
+
+ config->setGroup(it.currentKey());
+
+ QDictIterator<QString> it2(*share);
+
+ for (; it2.current(); ++it2 )
+ {
+ config->writeEntry(it2.currentKey(), *it2.current());
+ }
+
+ }
+
+ return config;
+}
+
+#include "sambafile.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/sambafile.h b/filesharing/advanced/kcm_sambaconf/sambafile.h
new file mode 100644
index 00000000..4eba9705
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sambafile.h
@@ -0,0 +1,178 @@
+/*
+ Copyright (c) 2002-2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef SAMBAFILE_H
+#define SAMBAFILE_H
+
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qobject.h>
+#include <kio/job.h>
+
+#include "sambashare.h"
+
+
+class KSimpleConfig;
+class KProcess;
+class KConfig;
+class KTempFile;
+
+class SambaFile;
+
+class SambaFileLoadException
+{
+ public:
+ SambaFileLoadException(const QString & msg) { message = msg; };
+
+ QString getMessage() { return message; };
+ protected:
+ QString message;
+};
+
+class SambaConfigFile : public QDict<SambaShare>
+{
+public:
+ SambaConfigFile(SambaFile*);
+ QString getDefaultValue(const QString & name);
+ QStringList getShareList();
+ void addShare(const QString & name, SambaShare* share);
+ SambaShare* addShare(const QString & name);
+ void removeShare(const QString & name);
+protected:
+ SambaFile* _sambaFile;
+ QStringList _shareList;
+};
+
+class SambaFile : public QObject
+{
+Q_OBJECT
+public:
+ SambaFile(const QString & _path, bool _readonly=true);
+ ~SambaFile();
+
+ /** No descriptions */
+ QString findShareByPath(const QString & path) const;
+ void removeShareByPath(const QString & path);
+
+
+ SambaShare* newShare(const QString & name);
+ SambaShare* newShare(const QString & name, const QString & path);
+ SambaShare* newPrinter(const QString & name, const QString & printer);
+
+ void removeShare(const QString & share);
+ void removeShare(SambaShare* share);
+
+ void renameShare(const QString & oldName, const QString & newName);
+
+ SambaShare* getShare(const QString & share) const;
+
+ /**
+ * Returns a list of all shared directories
+ **/
+ SambaShareList* getSharedDirs() const;
+
+ /**
+ * Returns a list of all shared printers
+ **/
+ SambaShareList* getSharedPrinters() const;
+
+ /**
+ * Returns a name which isn't already used for a share
+ * if the alreadyUsedName parameter is given, then
+ * a name based on this name is returned.
+ * E.g.: if public is already used, the method could return
+ * public2
+ **/
+ QString getUnusedName(const QString alreadyUsedName=QString::null) const;
+
+ /**
+ * Returns all values of the global section
+ * which are returned by the testparam program
+ * if the values were already loaded then these
+ * values are returned except the reload parameter
+ * is true
+ **/
+ SambaShare* getTestParmValues(bool reload=false);
+
+ static QString findSambaConf();
+
+
+ int getSambaVersion();
+
+ /**
+ * Load all data from the smb.conf file
+ * Can load a remote file
+ **/
+ bool load();
+
+ bool save();
+
+ /**
+ * Reads the local smb.conf
+ */
+ bool openFile();
+
+ bool isRemoteFile();
+
+ /**
+ * Save all data to the specified file
+ * if successful returns true otherwise false
+ **/
+ bool saveTo(const QString & path);
+
+protected:
+ bool readonly;
+ bool changed;
+ QString path;
+ QString localPath;
+ SambaConfigFile *_sambaConfig;
+ SambaShare* _testParmValues;
+ QString _parmOutput;
+ int _sambaVersion;
+
+ void parseParmStdOutput();
+ SambaConfigFile* getSambaConfigFile(KSimpleConfig* config);
+ KSimpleConfig* getSimpleConfig(SambaConfigFile* sambaConfig, const QString & filename);
+
+
+
+private:
+ void copyConfigs(KConfig* first, KConfig* second);
+ KTempFile * _tempFile;
+public slots:
+
+ /**
+ * Saves all changes to the smb.conf file
+ * if the user is not allowed she'll be asked
+ * for a root password
+ **/
+ bool slotApply();
+protected slots:
+ void testParmStdOutReceived(KProcess *proc, char *buffer, int buflen);
+ void slotJobFinished( KIO::Job *);
+ void slotSaveJobFinished( KIO::Job *);
+
+signals:
+ void canceled(const QString &);
+ void completed();
+
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/sambashare.cpp b/filesharing/advanced/kcm_sambaconf/sambashare.cpp
new file mode 100644
index 00000000..b30d7db7
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sambashare.cpp
@@ -0,0 +1,334 @@
+/***************************************************************************
+ sambashare.cpp - description
+ -------------------
+ begin : Mon Jun 12 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+***************************************************************************/
+
+/******************************************************************************
+* *
+* This file is part of KSambaPlugin. *
+* *
+* KSambaPlugin 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. *
+* *
+* KSambaPlugin 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 General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with KSambaPlugin; if not, write to the Free Software *
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+* *
+******************************************************************************/
+
+#include <kdebug.h>
+#include <qstringlist.h>
+
+#include "sambafile.h"
+#include "sambashare.h"
+#include "common.h"
+
+SambaShare::SambaShare(SambaConfigFile* sambaFile)
+ : QDict<QString>(10,false)
+{
+ _sambaFile = sambaFile;
+ setName("defaults");
+ setAutoDelete(true);
+}
+
+SambaShare::SambaShare(const QString & name, SambaConfigFile* sambaFile)
+ : QDict<QString>(10,false)
+{
+ _sambaFile = sambaFile;
+ setName(name);
+ setAutoDelete(true);
+}
+
+const QString& SambaShare::getName() const
+{
+ return _name;
+}
+
+bool SambaShare::setName(const QString & name, bool testWetherExists)
+{
+
+ if ( testWetherExists &&
+ _sambaFile->find(name) &&
+ _sambaFile->find(name) != this)
+ return false;
+
+ _name = name;
+return true;
+}
+
+bool SambaShare::optionSupported(const QString & name)
+{
+ QString defaultValue = _sambaFile->getDefaultValue(name);
+// kdDebug(5009) << name << " = " << defaultValue << " null : " << defaultValue.isNull() << endl;
+ return ! defaultValue.isNull();
+}
+
+/**
+* Returns the value of the given parameter
+* if no value is set yet the default value
+* will be returned.
+**/
+QString SambaShare::getValue(const QString & name, bool globalValue, bool defaultValue)
+{
+ QString synonym = getSynonym(name);
+
+ QString* str = find(synonym);
+ QString ret;
+
+ if (str) {
+ ret = *str;
+ }
+ else
+ if (globalValue)
+ ret = getGlobalValue(synonym,defaultValue);
+ else
+ if (defaultValue)
+ ret = getDefaultValue(synonym);
+
+
+ if (name=="writable" || name=="write ok" || name=="writeable")
+ ret = textFromBool( ! boolFromText(ret) );
+
+ return ret;
+}
+
+bool SambaShare::getBoolValue(const QString & name, bool globalValue, bool defaultValue)
+{
+ return boolFromText(getValue(name,globalValue,defaultValue));
+}
+
+
+QString SambaShare::getGlobalValue(const QString & name, bool defaultValue)
+{
+ if (!_sambaFile)
+ return getValue(name,false,defaultValue);
+
+ SambaShare* globals = _sambaFile->find("global");
+
+ QString s = globals->getValue(name,false,defaultValue);
+
+ return s;
+}
+
+
+/**
+* Returns the default synonym for the given parameter
+* if no synonym exists the original parameter in lower
+* case is returned
+**/
+QString SambaShare::getSynonym(const QString & name) const
+{
+ QString lname = name.lower().stripWhiteSpace();
+
+ if (lname == "browsable") return "browseable";
+ if (lname == "allow hosts") return "hosts allow";
+ if (lname == "auto services") return "preload";
+ if (lname == "casesignames") return "case sensitive";
+ if (lname == "create mode") return "create mask";
+ if (lname == "debuglevel") return "log level";
+ if (lname == "default") return "default service";
+ if (lname == "deny hosts") return "hosts deny";
+ if (lname == "directory") return "path";
+ if (lname == "directory mode") return "directory mask";
+ if (lname == "exec") return "preexec";
+ if (lname == "group") return "force group";
+ if (lname == "lock dir") return "lock directory";
+ if (lname == "min passwd length") return "min password length";
+ if (lname == "only guest") return "guest only";
+ if (lname == "prefered master") return "preferred master";
+ if (lname == "print ok") return "printable";
+ if (lname == "printcap") return "printcap name";
+ if (lname == "printer") return "printer name";
+ if (lname == "protocol") return "max protocol";
+ if (lname == "public") return "guest ok";
+ if (lname == "writable") return "read only";
+ if (lname == "write ok") return "read only";
+ if (lname == "read only") return "read only";
+ if (lname == "root") return "root directory";
+ if (lname == "root") return "root dir";
+ if (lname == "timestamp logs") return "debug timestamp";
+ if (lname == "user") return "username";
+ if (lname == "users") return "username";
+ if (lname == "idmap uid") return "winbind uid";
+ if (lname == "idmap gid") return "winbind gid";
+ if (lname == "vfs object") return "vfs objects";
+
+
+ return lname;
+}
+
+void SambaShare::setValue(const QString & name, const QString & value, bool globalValue, bool defaultValue)
+{
+ QString synonym = getSynonym(name);
+
+ QString newValue = value;
+
+ if (newValue.isNull())
+ newValue = "";
+
+ if (getName().lower() == "global")
+ globalValue = false;
+
+ if (name=="writable" || name=="write ok" || name=="writeable")
+ {
+ synonym = "read only";
+ newValue = textFromBool(!boolFromText(value));
+ }
+
+ QString global = "";
+
+ if (globalValue && !hasComments(synonym))
+ {
+ global = getGlobalValue(synonym, false);
+
+ if ( newValue.lower() == global.lower() )
+ {
+ remove(synonym);
+ _optionList.remove(synonym);
+ return;
+ }
+ }
+
+ // If the option has a comment we don't remove
+ // it if the value is equal to the default value.
+ // That's because the author of the option has thought about it.
+ if (defaultValue && global.isEmpty() && !hasComments(synonym))
+ {
+ if ( newValue.stripWhiteSpace().lower() == getDefaultValue(synonym).stripWhiteSpace().lower() )
+ {
+ kdDebug(5009) << getName() << " global: " << global << " remove " << synonym << endl;
+ remove(synonym);
+ _optionList.remove(synonym);
+ return;
+ }
+
+ }
+
+ if (!find(synonym))
+ {
+ _optionList.append(synonym);
+ }
+
+ replace(synonym,new QString(newValue));
+}
+
+void SambaShare::setValue(const QString & name, bool value, bool globalValue, bool defaultValue)
+{
+ setValue(name,textFromBool(value),globalValue, defaultValue);
+}
+
+void SambaShare::setValue(const QString & name, int value, bool globalValue, bool defaultValue)
+{
+ setValue(name,QString::number(value),globalValue, defaultValue);
+}
+
+/**
+* Returns the default value of the parameter
+**/
+QString SambaShare::getDefaultValue(const QString & name)
+{
+ QString defaultValue = _sambaFile->getDefaultValue(name);
+ if (defaultValue.isNull())
+ defaultValue = "";
+
+ return defaultValue;
+}
+
+bool SambaShare::getDefaultBoolValue(const QString & name)
+{
+
+ return boolFromText(getDefaultValue(name));
+}
+
+/**
+* Sets the comments for the passed option
+**/
+void SambaShare::setComments(const QString & name, const QStringList & commentList)
+{
+ // Only add inempty lists
+ if (commentList.empty())
+ return;
+
+ QString synonym = getSynonym(name);
+
+ _commentList.replace(name,new QStringList(commentList));
+}
+
+/**
+* Returns the comments of the passed option
+**/
+QStringList SambaShare::getComments(const QString & name)
+{
+ QStringList* list = _commentList.find(getSynonym(name));
+
+ if (list)
+ return QStringList(*list);
+ else
+ return QStringList();
+}
+
+
+bool SambaShare::hasComments(const QString & name)
+{
+ return 0L != _commentList.find(getSynonym(name));
+}
+
+/**
+* Returns the comments of the share
+* e.g. the text above the [...] section
+**/
+QStringList SambaShare::getComments()
+{
+ return _comments;
+}
+
+/**
+* Sets the comments for the share
+* e.g. the text above the [...] section
+**/
+void SambaShare::setComments(const QStringList & commentList)
+{
+ _comments = commentList;
+}
+
+QStringList SambaShare::getOptionList()
+{
+ return _optionList;
+}
+
+/**
+* Returns true if this share is a printer
+**/
+bool SambaShare::isPrinter()
+{
+ QString* str = find("printable");
+
+ if (!str)
+ str = find("print ok");
+
+ return str!=0;
+}
+
+/**
+* Returns true if the share name is
+* global, printers or homes
+**/
+bool SambaShare::isSpecialSection()
+{
+ if ( _name.lower() == "global" ||
+ _name.lower() == "printers" ||
+ _name.lower() == "homes" )
+ return true;
+ else
+ return false;
+}
diff --git a/filesharing/advanced/kcm_sambaconf/sambashare.h b/filesharing/advanced/kcm_sambaconf/sambashare.h
new file mode 100644
index 00000000..d6c0e3b7
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sambashare.h
@@ -0,0 +1,231 @@
+/***************************************************************************
+ sambashare.h - description
+ -------------------
+ begin : Mon 12 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef SAMBASHARE_H
+#define SAMBASHARE_H
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+
+class SambaConfigFile;
+class QStringList;
+
+/**
+ * A class which represents a Samba share
+ * @author Jan Sch&auml;fer
+ **/
+class SambaShare :public QDict<QString>
+{
+public:
+
+ /**
+ * Creates a new SambaShare with an empty name
+ **/
+ SambaShare(SambaConfigFile* sambaFile);
+
+ /**
+ * Creates a new SambaShare with the given name
+ * @param name the name of the share
+ **/
+ SambaShare(const QString & name, SambaConfigFile* sambaFile);
+
+ /**
+ * Returns the name of the share
+ **/
+ const QString& getName() const;
+
+
+ /**
+ * Sets the name of the share
+ * returns false if the name already exists and the testWetherExists
+ * parameter is set to true
+ **/
+ bool setName(const QString & name, bool testWetherExists=true);
+
+ /**
+ * Tests wether or not the given option is supported by Samba.
+ * It does this by testing the output of testparm for this option
+ **/
+ bool optionSupported(const QString & name);
+
+ /**
+ * Returns the value of the given parameter
+ * if the parameter doesn't exists, the
+ * global and then the default value is returned.
+ * @param name the name of the parameter
+ * @param gobalValue if the gobal value should be returend
+ * @param defaultValue if the default value should be returned or a null string
+ **/
+ QString getValue(const QString & name, bool globalValue=true, bool defaultValue=true);
+
+ /**
+ * Same as above but for boolean parameters
+ * Don't use defaultValue = false with this function
+ * because you can't distinguish an unset parameter
+ * from a false parameter
+ **/
+ bool getBoolValue(const QString & name, bool globalValue=true, bool defaultValue=true);
+
+ /**
+ * Returns the value from the [globals] section
+ **/
+ QString getGlobalValue(const QString & name, bool defaultValue=true);
+
+ /**
+ * Sets the parameter name to the given value
+ * @param value the value of the parameter
+ * @param name the name of the parameter
+ * @param globalValue if true then the value is only set if it is different to the global value
+ * @param defaultValue if true then the value is only set if it is different to the default value
+ * if globalValue and defaultValue is true then the value is set when a global value
+ * exists and it it is different to it. If no global value exists then it is only
+ * set if different to the default value.
+ **/
+ void setValue(const QString & name,const QString & value, bool globalValue=true, bool defaultValue=true);
+
+ /**
+ * Same as above but for boolean parameters
+ **/
+ void setValue(const QString & name, bool value, bool globalValue=true, bool defaultValue=true);
+
+ /**
+ * Same as above but for integer parameters
+ **/
+ void setValue(const QString & name, int value, bool globalValue=true, bool defaultValue=true);
+
+ /**
+ * Returns the default value of the parameter
+ * @param name the name of the parameter
+ **/
+ QString getDefaultValue(const QString & name);
+
+ /**
+ * Same as above but for booleans
+ **/
+ bool getDefaultBoolValue(const QString & name);
+
+ /**
+ * Returns the default synonym for the given parameter
+ * if no synonym exists the original parameter in lower
+ * case is returned
+ **/
+ QString getSynonym(const QString & name) const;
+
+ /**
+ * Returns the comments of the share
+ * e.g. the text above the [...] section
+ **/
+ QStringList getComments();
+
+ /**
+ * Sets the comments for the share
+ * e.g. the text above the [...] section
+ **/
+ void setComments(const QStringList & commentList);
+
+ /**
+ * Sets the comments for the passed option
+ **/
+ void setComments(const QString & name, const QStringList & commentList);
+
+ /**
+ * Returns the comments of the passed option
+ **/
+ QStringList getComments(const QString & name);
+
+ /**
+ * Returns true if the passed option has comments
+ * otherwise returns false
+ **/
+ bool hasComments(const QString & name);
+
+ /**
+ * Returns the list of all options
+ * the order of the options is exactly the
+ * order of the insertion of the options
+ **/
+ QStringList getOptionList();
+
+ /**
+ * Returns true if this share is a printer
+ * that's if printable = true
+ **/
+ bool isPrinter();
+
+ /**
+ * Returns true if the share name is
+ * global, printers or homes
+ **/
+ bool isSpecialSection();
+
+protected:
+ /**
+ * The name of the share
+ * could be also printers, global and homes
+ **/
+ QString _name;
+ SambaConfigFile* _sambaFile;
+
+ /**
+ * This attribute stores all option comments.
+ * the comments which stood above the option name
+ * are stored in this QStringList
+ **/
+ QDict<QStringList> _commentList;
+
+ /**
+ * The comments for this share
+ **/
+ QStringList _comments;
+
+ /**
+ * An extra list which holds
+ * all stored options
+ * You might say, hey for what is this ?
+ * We have them already stored in the QDict.
+ * That's right, but there is a problem :
+ * QDict doesn't preserve the order of
+ * the inserted items, but when saving
+ * the options back to the smb.conf
+ * we want to have exactly the same order
+ * so this QStringList is only for saving
+ * the order of the options.
+ * On the other side we need a very fast lookup
+ * of the options, because we lookup very frequently
+ * so this is the best way to do both.
+ **/
+ QStringList _optionList;
+
+};
+
+typedef QPtrList<SambaShare> SambaShareList;
+
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/share.ui b/filesharing/advanced/kcm_sambaconf/share.ui
new file mode 100644
index 00000000..91d16843
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/share.ui
@@ -0,0 +1,3300 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KcmShareDlg</class>
+<comment>
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+</comment>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>KcmShareDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>706</width>
+ <height>535</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Add/Edit Share</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>_tabs</cstring>
+ </property>
+ <property name="tabPosition">
+ <enum>Top</enum>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>baseTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Base Settings</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout52</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>pixmapFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>directoryPixLbl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pixmap</string>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>directoryGrp</cstring>
+ </property>
+ <property name="title">
+ <string>D&amp;irectory</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Path:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>pathUrlRq</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>pathUrlRq</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>homeChk</cstring>
+ </property>
+ <property name="text">
+ <string>Share all home &amp;directories</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>identifierGrp</cstring>
+ </property>
+ <property name="title">
+ <string>Iden&amp;tifier</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>Lbl_shareName</cstring>
+ </property>
+ <property name="text">
+ <string>Na&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>shareNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Comme&amp;nt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>commentEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>commentEdit</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>shareNameEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Main P&amp;roperties</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>readOnlyBaseChk</cstring>
+ </property>
+ <property name="text">
+ <string>Read onl&amp;y</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>publicBaseChk</cstring>
+ </property>
+ <property name="text">
+ <string>Pub&amp;lic</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>browseableBaseChk</cstring>
+ </property>
+ <property name="text">
+ <string>Bro&amp;wseable</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>availableBaseChk</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;vailable</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer42</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>90</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>securityTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Security</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox19</cstring>
+ </property>
+ <property name="title">
+ <string>Gu&amp;ests</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>Spacer27</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>guestAccountLbl</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Guest account:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>guestAccountCombo</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is a username which will be used for access to services which are specified as guest ok. Whatever privileges this user has will be available to any client connecting to the guest service. Typically this user will exist in the password file, but will not have a valid login. The user account \"ftp\" is often a good choice for this parameter. If a username is specified in a given service, the specified username overrides this one.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>guestOnlyChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Only allow guest connect&amp;ions</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked , then no password is required to connect to the service. Privileges will be those of the guest account.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox18</cstring>
+ </property>
+ <property name="title">
+ <string>Hos&amp;ts</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>hostsDenyEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Hosts allo&amp;w:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hostsAllowEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Hosts &amp;deny:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hostsDenyEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The opposite of hosts allow - hosts listed here are NOT permitted access to services unless the specific services have their own lists to override this one. Where the lists conflict, the allow list takes precedence.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>hostsAllowEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox17</cstring>
+ </property>
+ <property name="title">
+ <string>Sy&amp;mbolic Links</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>followSymlinksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Allow following of symbolic lin&amp;ks</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>wideLinksChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Allow following of symbolic links that &amp;point to areas outside the directory tree</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;qt&gt;Validate password against the following usernames if the client cannot supply a username:&lt;/qt&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>userNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>userNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>onlyUserChk</cstring>
+ </property>
+ <property name="text">
+ <string>Only allow connections with use&amp;rnames specified in this username list</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked , then no password is required to connect to the service. Privileges will be those of the guest account.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer31</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>hiddenFilesTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Hidden &amp;Files</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </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>Hidden</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Veto</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Veto Oplock</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Size</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Date</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Permissions</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>Group</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>hiddenListView</cstring>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>selGrpBx</cstring>
+ </property>
+ <property name="title">
+ <string>Se&amp;lected Files</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>hiddenChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hi&amp;de</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>vetoChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Veto</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>vetoOplockChk</cstring>
+ </property>
+ <property name="text">
+ <string>Veto oploc&amp;k</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox13_2</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Manual Configuration</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout18</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Ve&amp;to files:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>vetoEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Veto oplock f&amp;iles:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>vetoOplockEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>hiddenEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>vetoOplockEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>vetoEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Hidde&amp;n files:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>hiddenEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout52</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>hideUnwriteableFilesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hide un&amp;writable files</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>hideSpecialFilesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hide s&amp;pecial files</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>hideDotFilesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hide files startin&amp;g with a dot </string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>hideUnreadableChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hide un&amp;readable files</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>advancedTab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Advanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>advancedFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame26</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout92</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>PixmapLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_4</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Here you can change advanced options of the SAMBA server.
+Only change something if you know what you are doing.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>advancedDumpTabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Advanced Dump</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>advancedDumpTab</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Security</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="3" column="0">
+ <property name="name">
+ <cstring>GroupBox11_2</cstring>
+ </property>
+ <property name="title">
+ <string>Force Modes</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Forc&amp;e directory security mode:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceDirectorySecurityModeEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Fo&amp;rce security mode:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceSecurityModeEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_3_2_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Force director&amp;y mode:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceDirectoryModeEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Force create mo&amp;de:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceCreateModeEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="2" column="2">
+ <property name="name">
+ <cstring>forceDirectoryModeBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="3" column="2">
+ <property name="name">
+ <cstring>forceDirectorySecurityModeBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="0" column="1">
+ <property name="name">
+ <cstring>forceCreateModeEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="1" column="1">
+ <property name="name">
+ <cstring>forceSecurityModeEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="2" column="1">
+ <property name="name">
+ <cstring>forceDirectoryModeEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="3" column="1">
+ <property name="name">
+ <cstring>forceDirectorySecurityModeEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="2">
+ <property name="name">
+ <cstring>forceSecurityModeBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="2">
+ <property name="name">
+ <cstring>forceCreateModeBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>Spacer62</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="3" column="1">
+ <property name="name">
+ <cstring>GroupBox10_2</cstring>
+ </property>
+ <property name="title">
+ <string>Masks</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_4_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Directory security mask:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>directorySecurityMaskEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_4_3</cstring>
+ </property>
+ <property name="text">
+ <string>Security &amp;mask:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>securityMaskEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_4_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Direc&amp;tory mask:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>directoryMaskEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="2">
+ <property name="name">
+ <cstring>securityMaskBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="2" column="2">
+ <property name="name">
+ <cstring>directoryMaskBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="3" column="2">
+ <property name="name">
+ <cstring>directorySecurityMaskBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="1" column="1">
+ <property name="name">
+ <cstring>securityMaskEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="2" column="1">
+ <property name="name">
+ <cstring>directoryMaskEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="3" column="1">
+ <property name="name">
+ <cstring>directorySecurityMaskEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Create mas&amp;k:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>createMaskEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="0" column="1">
+ <property name="name">
+ <cstring>createMaskEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="2">
+ <property name="name">
+ <cstring>createMaskBtn</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>groupBox56</cstring>
+ </property>
+ <property name="title">
+ <string>ACL</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>profileAclsChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Profile acls</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>inheritAclsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Inherit ac&amp;ls</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>ntAclSupportChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;NT ACL support</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Force unkno&amp;wn acl user:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceUnknownAclUserEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This parameter is a comma, space, or tab delimited set of hosts which are permitted to access a service.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mapAclInheritChk</cstring>
+ </property>
+ <property name="text">
+ <string>Map acl &amp;inherit</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine" row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>forceUnknownAclUserEdit</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>40</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="validChars">
+ <string>01234567</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox58</cstring>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>inheritPermissionsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Inherit permissions from parent directory</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>deleteReadonlyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Allow deletion of readonly files</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>groupBox57</cstring>
+ </property>
+ <property name="title">
+ <string>DOS Attribute Mapping</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mapArchiveChk</cstring>
+ </property>
+ <property name="text">
+ <string>Map DOS archi&amp;ve to UNIX owner execute</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mapHiddenChk</cstring>
+ </property>
+ <property name="text">
+ <string>Map DOS hidden to UNI&amp;X world execute</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mapSystemChk</cstring>
+ </property>
+ <property name="text">
+ <string>Map DOS system to UNIX &amp;group execute</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>storeDosAttributesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Store DOS attributes onto extended attribute</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="1">
+ <property name="name">
+ <cstring>groupBox76</cstring>
+ </property>
+ <property name="title">
+ <string>OS/2</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>eaSupportChk</cstring>
+ </property>
+ <property name="text">
+ <string>OS/2 style extended attributes support</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Tuning</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>syncAlwaysChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Sync al&amp;ways</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>strictSyncChk</cstring>
+ </property>
+ <property name="text">
+ <string>Strict s&amp;ync</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>strictAllocateChk</cstring>
+ </property>
+ <property name="text">
+ <string>St&amp;rict allocate</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>useSendfileChk</cstring>
+ </property>
+ <property name="text">
+ <string>Use sen&amp;dfile</string>
+ </property>
+ </widget>
+ <spacer row="8" column="1">
+ <property name="name">
+ <cstring>Spacer53</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>120</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Bloc&amp;k size:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>blockSizeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Client-side cachin&amp;g policy:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cscPolicyCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="3">
+ <property name="name">
+ <cstring>TextLabel1_7_2</cstring>
+ </property>
+ <property name="text">
+ <string>bytes</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel5_3_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Write cache si&amp;ze:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>writeCacheSizeSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="7" column="2" rowspan="1" colspan="2">
+ <item>
+ <property name="text">
+ <string>manual</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>documents</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>programs</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>disable</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cscPolicyCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="4" column="4">
+ <property name="name">
+ <cstring>Spacer55</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>143</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="5" column="3">
+ <property name="name">
+ <cstring>TextLabel1_7</cstring>
+ </property>
+ <property name="text">
+ <string>bytes</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="4" column="2">
+ <property name="name">
+ <cstring>maxConnectionsSpin</cstring>
+ </property>
+ <property name="buttonSymbols">
+ <enum>UpDownArrows</enum>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="6" column="2">
+ <property name="name">
+ <cstring>blockSizeSpin</cstring>
+ </property>
+ <property name="buttonSymbols">
+ <enum>UpDownArrows</enum>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel5_3_4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Maximum number of simultaneous connections:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>maxConnectionsSpin</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="5" column="2">
+ <property name="name">
+ <cstring>writeCacheSizeSpin</cstring>
+ </property>
+ <property name="buttonSymbols">
+ <enum>UpDownArrows</enum>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Filenames</string>
+ </attribute>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout54</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>330</x>
+ <y>1</y>
+ <width>323</width>
+ <height>270</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Other Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>hideTrailingDotChk</cstring>
+ </property>
+ <property name="text">
+ <string>Hide traili&amp;ng dot</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox15</cstring>
+ </property>
+ <property name="title">
+ <string>DOS</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>dosFilemodeChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;DOS file mode</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>dosFiletimesChk</cstring>
+ </property>
+ <property name="text">
+ <string>DOS f&amp;ile times</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>dosFiletimeResolutionChk</cstring>
+ </property>
+ <property name="text">
+ <string>DOS file time resolution</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer61</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>152</x>
+ <y>277</y>
+ <width>20</width>
+ <height>119</height>
+ </rect>
+ </property>
+ </spacer>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox5</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>1</x>
+ <y>1</y>
+ <width>323</width>
+ <height>270</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Name Mangling</string>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_2_2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>179</y>
+ <width>168</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Mangling cha&amp;r:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>manglingCharEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout14</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>185</x>
+ <y>179</y>
+ <width>127</width>
+ <height>24</height>
+ </rect>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>manglingCharEdit</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>40</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer40_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_5</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>209</y>
+ <width>168</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Mangled ma&amp;p:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mangledMapEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mangledMapEdit</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>185</x>
+ <y>209</y>
+ <width>127</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mangledNamesChk</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>21</y>
+ <width>168</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Enable na&amp;me mangling</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mangleCaseChk</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>47</y>
+ <width>168</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Man&amp;gle case</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>237</y>
+ <width>168</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Mangling method:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>manglingMethodCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>hash</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>hash2</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>manglingMethodCombo</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>185</x>
+ <y>237</y>
+ <width>127</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>preserveCaseChk</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>70</y>
+ <width>168</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Preser&amp;ve case</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>shortPreserveCaseChk</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>90</y>
+ <width>168</width>
+ <height>20</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Short pr&amp;eserve case</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_3</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>150</y>
+ <width>168</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Defau&amp;lt case:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>defaultCaseCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Lower</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Upper</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>defaultCaseCombo</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>185</x>
+ <y>151</y>
+ <width>127</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Automatic</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>caseSensitiveCombo</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>185</x>
+ <y>121</y>
+ <width>127</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_3_3</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>120</y>
+ <width>168</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Case sensi&amp;tive:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>caseSensitiveCombo</cstring>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Locking</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>Spacer51</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox47</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="title">
+ <string>Locki&amp;ng</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>oplocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Issue oppo&amp;rtunistic locks (oplocks) </string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="5" column="0">
+ <property name="name">
+ <cstring>groupBox59</cstring>
+ </property>
+ <property name="title">
+ <string>O&amp;plocks</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>oplockContentionLimitSpin</cstring>
+ </property>
+ <property name="buttonSymbols">
+ <enum>UpDownArrows</enum>
+ </property>
+ <property name="maxValue">
+ <number>2147483647</number>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer23</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>299</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5_3_3_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <property name="text">
+ <string>Oplock contention li&amp;mit:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>oplockContentionLimitSpin</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>level2OplocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Le&amp;vel2 oplocks</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0">
+ <property name="name">
+ <cstring>fakeOplocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Fak&amp;e oplocks</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>shareModesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Share mo&amp;des</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>posixLockingChk</cstring>
+ </property>
+ <property name="text">
+ <string>Posi&amp;x locking</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;trict locking:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>strictLockingCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Automatic</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Yes</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>No</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>strictLockingCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>blockingLocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Blockin&amp;g locks</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>lockingChk</cstring>
+ </property>
+ <property name="text">
+ <string>Enable lock&amp;ing</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>VFS</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>Spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>260</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>vfsObjectsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1_6</cstring>
+ </property>
+ <property name="text">
+ <string>Vfs ob&amp;jects:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>vfsObjectsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1_6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Vfs o&amp;ptions:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>vfsOptionsEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>vfsOptionsEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Exec</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>preexecCloseChk</cstring>
+ </property>
+ <property name="text">
+ <string>preexec c&amp;lose</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>rootPreexecCloseChk</cstring>
+ </property>
+ <property name="text">
+ <string>root pree&amp;xec close</string>
+ </property>
+ </widget>
+ <spacer row="6" column="1">
+ <property name="name">
+ <cstring>Spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>284</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Pos&amp;texec:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rootPreexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2</cstring>
+ </property>
+ <property name="text">
+ <string>Root pr&amp;eexec:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>postexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>P&amp;reexec:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>preexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel5_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Root &amp;postexec:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rootPostexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>postexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>rootPreexecEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>rootPostexecEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Misc</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer row="9" column="1">
+ <property name="name">
+ <cstring>Spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Fst&amp;ype:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fstypeEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>fstypeEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>magicScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Ma&amp;gic script:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>magicScriptEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Volume:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>volumeEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>magicOutputEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Mag&amp;ic output:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>magicOutputEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>fakeDirectoryCreateTimesChk</cstring>
+ </property>
+ <property name="text">
+ <string>Fa&amp;ke directory create times</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="8" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>msdfsRootChk</cstring>
+ </property>
+ <property name="text">
+ <string>Ms&amp;dfs root</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>setDirectoryChk</cstring>
+ </property>
+ <property name="text">
+ <string>Setdir command allo&amp;wed</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Do &amp;not descend:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>dontDescendEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>dontDescendEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel6_2_3_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Msdfs pro&amp;xy:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>msdfsProxyEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>msdfsProxyEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>volumeEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonHelp</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Help</string>
+ </property>
+ <property name="accel">
+ <string>F1</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>shareNameEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>checkValues()</slot>
+ </connection>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>homeChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>homeChkToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>strictSyncChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>syncAlwaysChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>forceCreateModeEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>forceCreateModeBtn</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>forceDirectoryModeEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>forceDirectoryModeBtn</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>createMaskEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>createMaskBtn</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>directoryMaskEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>inheritPermissionsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>directoryMaskBtn</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>followSymlinksChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wideLinksChk</receiver>
+ <slot>setChecked(bool)</slot>
+ </connection>
+ <connection>
+ <sender>followSymlinksChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wideLinksChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>publicBaseChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>publicBaseChk_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>pathUrlRq</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>pathUrlRq_textChanged(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>lockingChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>lockingChk_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>vetoOplockChk</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>lockingChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox47</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>oplockContentionLimitSpin</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>oplockContentionLimitSpin_valueChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>fakeOplocksChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>fakeOplocksChk_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>oplocksChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>groupBox59</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>forceCreateModeBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>forceSecurityModeBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>forceDirectoryModeBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>forceDirectorySecurityModeBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>createMaskBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>securityMaskBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>directoryMaskBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>directorySecurityMaskBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>accessModifierBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>storeDosAttributesChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>storeDosAttributesChk_toggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>buttonHelp</sender>
+ <signal>clicked()</signal>
+ <receiver>KcmShareDlg</receiver>
+ <slot>buttonHelp_clicked()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>volumeEdit</tabstop>
+ <tabstop>fstypeEdit</tabstop>
+ <tabstop>magicScriptEdit</tabstop>
+ <tabstop>magicOutputEdit</tabstop>
+ <tabstop>dontDescendEdit</tabstop>
+ <tabstop>setDirectoryChk</tabstop>
+ <tabstop>fakeDirectoryCreateTimesChk</tabstop>
+ <tabstop>msdfsRootChk</tabstop>
+ <tabstop>preexecEdit</tabstop>
+ <tabstop>rootPreexecEdit</tabstop>
+ <tabstop>postexecEdit</tabstop>
+ <tabstop>rootPostexecEdit</tabstop>
+ <tabstop>preexecCloseChk</tabstop>
+ <tabstop>rootPreexecCloseChk</tabstop>
+ <tabstop>lockingChk</tabstop>
+ <tabstop>blockingLocksChk</tabstop>
+ <tabstop>posixLockingChk</tabstop>
+ <tabstop>shareModesChk</tabstop>
+ <tabstop>oplocksChk</tabstop>
+ <tabstop>level2OplocksChk</tabstop>
+ <tabstop>oplockContentionLimitSpin</tabstop>
+ <tabstop>fakeOplocksChk</tabstop>
+ <tabstop>mangledNamesChk</tabstop>
+ <tabstop>mangleCaseChk</tabstop>
+ <tabstop>preserveCaseChk</tabstop>
+ <tabstop>shortPreserveCaseChk</tabstop>
+ <tabstop>defaultCaseCombo</tabstop>
+ <tabstop>manglingCharEdit</tabstop>
+ <tabstop>mangledMapEdit</tabstop>
+ <tabstop>manglingMethodCombo</tabstop>
+ <tabstop>hideTrailingDotChk</tabstop>
+ <tabstop>dosFilemodeChk</tabstop>
+ <tabstop>dosFiletimesChk</tabstop>
+ <tabstop>dosFiletimeResolutionChk</tabstop>
+ <tabstop>strictSyncChk</tabstop>
+ <tabstop>syncAlwaysChk</tabstop>
+ <tabstop>strictAllocateChk</tabstop>
+ <tabstop>useSendfileChk</tabstop>
+ <tabstop>maxConnectionsSpin</tabstop>
+ <tabstop>writeCacheSizeSpin</tabstop>
+ <tabstop>blockSizeSpin</tabstop>
+ <tabstop>cscPolicyCombo</tabstop>
+ <tabstop>inheritPermissionsChk</tabstop>
+ <tabstop>deleteReadonlyChk</tabstop>
+ <tabstop>mapArchiveChk</tabstop>
+ <tabstop>mapHiddenChk</tabstop>
+ <tabstop>mapSystemChk</tabstop>
+ <tabstop>forceCreateModeEdit</tabstop>
+ <tabstop>forceSecurityModeEdit</tabstop>
+ <tabstop>forceDirectoryModeEdit</tabstop>
+ <tabstop>forceDirectorySecurityModeEdit</tabstop>
+ <tabstop>ntAclSupportChk</tabstop>
+ <tabstop>inheritAclsChk</tabstop>
+ <tabstop>profileAclsChk</tabstop>
+ <tabstop>mapAclInheritChk</tabstop>
+ <tabstop>forceUnknownAclUserEdit</tabstop>
+ <tabstop>createMaskEdit</tabstop>
+ <tabstop>securityMaskEdit</tabstop>
+ <tabstop>directoryMaskEdit</tabstop>
+ <tabstop>directorySecurityMaskEdit</tabstop>
+ <tabstop>advancedDumpTab</tabstop>
+ <tabstop>hiddenListView</tabstop>
+ <tabstop>hiddenChk</tabstop>
+ <tabstop>vetoChk</tabstop>
+ <tabstop>vetoOplockChk</tabstop>
+ <tabstop>hiddenEdit</tabstop>
+ <tabstop>vetoEdit</tabstop>
+ <tabstop>vetoOplockEdit</tabstop>
+ <tabstop>hideUnreadableChk</tabstop>
+ <tabstop>hideUnwriteableFilesChk</tabstop>
+ <tabstop>hideDotFilesChk</tabstop>
+ <tabstop>hideSpecialFilesChk</tabstop>
+ <tabstop>pathUrlRq</tabstop>
+ <tabstop>homeChk</tabstop>
+ <tabstop>shareNameEdit</tabstop>
+ <tabstop>commentEdit</tabstop>
+ <tabstop>readOnlyBaseChk</tabstop>
+ <tabstop>publicBaseChk</tabstop>
+ <tabstop>browseableBaseChk</tabstop>
+ <tabstop>availableBaseChk</tabstop>
+ <tabstop>guestAccountCombo</tabstop>
+ <tabstop>guestOnlyChk</tabstop>
+ <tabstop>hostsAllowEdit</tabstop>
+ <tabstop>hostsDenyEdit</tabstop>
+ <tabstop>followSymlinksChk</tabstop>
+ <tabstop>wideLinksChk</tabstop>
+ <tabstop>userNameEdit</tabstop>
+ <tabstop>onlyUserChk</tabstop>
+ <tabstop>_tabs</tabstop>
+ <tabstop>vfsObjectsEdit</tabstop>
+ <tabstop>vfsOptionsEdit</tabstop>
+ <tabstop>buttonHelp</tabstop>
+ <tabstop>buttonOk</tabstop>
+ <tabstop>buttonCancel</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">kiconloader.h</include>
+ <include location="global" impldecl="in implementation">qptrlist.h</include>
+ <include location="global" impldecl="in implementation">kmessagebox.h</include>
+ <include location="global" impldecl="in implementation">kprocess.h</include>
+ <include location="local" impldecl="in implementation">share.ui.h</include>
+</includes>
+<slots>
+ <slot access="private">checkValues()</slot>
+ <slot access="private" specifier="non virtual">init()</slot>
+ <slot>trytoAccept()</slot>
+ <slot>homeChkToggled( bool )</slot>
+ <slot>addAllowedUserBtnClicked()</slot>
+ <slot>removeAllowedUserBtnClicked()</slot>
+ <slot>guestOnlyChk_toggled( bool b )</slot>
+ <slot>userOnlyChk_toggled( bool b )</slot>
+ <slot access="protected">accessModifierBtnClicked()</slot>
+ <slot access="protected">changedSlot()</slot>
+ <slot>publicBaseChk_toggled( bool b )</slot>
+ <slot>pathUrlRq_textChanged( const QString &amp; )</slot>
+ <slot>oplocksChk_toggled( bool b )</slot>
+ <slot>lockingChk_toggled( bool b )</slot>
+ <slot>fakeOplocksChk_toggled( bool b )</slot>
+ <slot>oplockContentionLimitSpin_valueChanged( int i )</slot>
+ <slot>storeDosAttributesChk_toggled( bool b )</slot>
+ <slot>buttonHelp_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/share.ui.h b/filesharing/advanced/kcm_sambaconf/share.ui.h
new file mode 100644
index 00000000..414d055f
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/share.ui.h
@@ -0,0 +1,258 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+void KcmShareDlg::checkValues()
+{
+/*
+ bool state = true;
+ // Check if the ok-button should be enabled
+ if (directory->isChecked()){
+ if (homes->isChecked()){
+ state = true;
+ }else{
+ if (shareName->text() == "")
+ state = false;
+ if (path->url() == "")
+ state = false;
+ }
+ }else{
+ if (printers->isChecked()){
+ state = true;
+ }else{
+ if (shareName->text() == ""){
+ state = false;
+ }
+ }
+ }
+ buttonOk->setEnabled(state);
+*/
+}
+
+
+
+
+
+
+void KcmShareDlg::init()
+{
+ directoryPixLbl->setPixmap(DesktopIcon("folder"));
+ PixmapLabel1->setPixmap(SmallIcon("messagebox_warning"));
+/*
+ QListBoxItem* item = 0;
+ QPtrList<QListBoxItem>* selectedList = new QPtrList<QListBoxItem>;
+ selectedList->setAutoDelete(false);
+ for (int i=0; i < possible->count(); i++){
+ item = possible->item(i);
+ if (item->isSelected()){
+ selectedList->append(item);
+ }
+ }
+ selected->clearSelection();
+ for (QPtrListIterator<QListBoxItem> it(*selectedList); it.current(); ++it){
+ possible->takeItem(it.current());
+ selected->insertItem(it.current());
+ }
+ delete selectedList;
+*/
+}
+
+
+
+
+void KcmShareDlg::trytoAccept()
+{
+/*
+ bool error = false;
+ if (directory->isChecked()){
+ if (!homes->isChecked()){
+ if (shareName->text() == "[homes]"){
+ KMessageBox::sorry(this, i18n("Sorry, but you can't create a share named \"[homes]\".\nIf you want to share your home-directorys, please click on \"Share homes\" on the \"Base settings\"-tab."));
+ error = true;
+ }
+ }
+ }else{
+ if (!printers->isChecked()){
+ if (shareName->text() == "[printers]"){
+ KMessageBox::sorry(this, i18n("Sorry, but you can't create a share named \"[printers]\".\nIf you want to share all your printers, please click on \"Share all printers\" on the \"Base settings\"-tab."));
+ error = true;
+ }
+ }
+ }
+ if (!error){
+ updateShareData();
+ accept();
+ }
+*/
+}
+
+
+
+
+
+void KcmShareDlg::homeChkToggled( bool )
+{
+
+}
+
+void KcmShareDlg::addAllowedUserBtnClicked()
+{
+
+}
+
+void KcmShareDlg::removeAllowedUserBtnClicked()
+{
+
+}
+
+void KcmShareDlg::guestOnlyChk_toggled( bool b)
+{
+ if (b)
+ {
+ onlyUserChk->setChecked(false);
+ publicBaseChk->setChecked(true);
+ }
+
+ onlyUserChk->setDisabled(b);
+ publicBaseChk->setDisabled(b);
+}
+
+
+void KcmShareDlg::userOnlyChk_toggled( bool b)
+{
+ if (b)
+ {
+ guestOnlyChk->setChecked(false);
+ publicBaseChk->setChecked(false);
+ }
+
+ guestOnlyChk->setDisabled(b);
+ publicBaseChk->setDisabled(b);
+}
+
+void KcmShareDlg::accessModifierBtnClicked()
+{
+
+
+}
+
+
+void KcmShareDlg::changedSlot()
+{
+
+}
+
+
+void KcmShareDlg::publicBaseChk_toggled( bool b)
+{
+ guestOnlyChk->setEnabled(b);
+ if (!b) {
+ guestOnlyChk->setChecked(false);
+ }
+ guestAccountCombo->setEnabled(b);
+ guestAccountLbl->setEnabled(b);
+
+}
+
+
+void KcmShareDlg::pathUrlRq_textChanged( const QString & )
+{
+
+}
+
+
+void KcmShareDlg::oplocksChk_toggled( bool b)
+{
+ if (b)
+ fakeOplocksChk->setChecked(false);
+}
+
+
+void KcmShareDlg::lockingChk_toggled( bool b)
+{
+ // Its Dangerous to disable locking !
+/*
+ if (!b) {
+ enableLockingWarnPix->setPixmap(SmallIcon("messagebox_warning"));
+ enableLockingWarnPix->show();
+ } else {
+ enableLockingWarnPix->hide();
+ }
+*/
+
+}
+
+
+void KcmShareDlg::fakeOplocksChk_toggled( bool b)
+{
+/*
+ if (b) {
+ fakeOplocksWarnPix->setPixmap(SmallIcon("messagebox_info"));
+ fakeOplocksWarnPix->setText(i18n("Better use the real oplocks support than this parameter"));
+ fakeOplocksWarnPix->showMaximized();
+ fakeOplocksWarnPix->show();
+ } else {
+ fakeOplocksWarnPix->hide();
+ }
+*/
+}
+
+
+void KcmShareDlg::oplockContentionLimitSpin_valueChanged( int i)
+{
+/*
+ oplockContentionLimitWarnPix->setMaximumWidth(32767);
+ oplockContentionLimitWarnPix->setPixmap(SmallIcon("messagebox_critical"));
+*/
+ //oplockContentionLimitWarnPix->show();
+
+}
+
+
+void KcmShareDlg::storeDosAttributesChk_toggled( bool b)
+{
+ mapArchiveChk->setDisabled(b);
+ mapSystemChk->setDisabled(b);
+ mapHiddenChk->setDisabled(b);
+
+ if (b) {
+ mapArchiveChk->setChecked(false);
+ mapSystemChk->setChecked(false);
+ mapHiddenChk->setChecked(false);
+ }
+}
+
+
+void KcmShareDlg::buttonHelp_clicked()
+{
+ KProcess* p = new KProcess();
+ *p << "konqueror";
+ *p << "man:smb.conf";
+ p->start();
+}
+
+
diff --git a/filesharing/advanced/kcm_sambaconf/sharedlgimpl.cpp b/filesharing/advanced/kcm_sambaconf/sharedlgimpl.cpp
new file mode 100644
index 00000000..aca5ea39
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sharedlgimpl.cpp
@@ -0,0 +1,478 @@
+/***************************************************************************
+ sharedlgimpl.cpp - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+
+/**
+ * @author Jan Schäfer
+ **/
+
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qtabwidget.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qgrid.h>
+#include <qcursor.h>
+#include <qtable.h>
+#include <qlistbox.h>
+#include <qtoolbutton.h>
+#include <qpixmap.h>
+#include <qframe.h>
+#include <qwidget.h>
+#include <qtabwidget.h>
+
+
+#include <klineedit.h>
+#include <kurlrequester.h>
+#include <knuminput.h>
+#include <kdebug.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kfiledetailview.h>
+#include <kdirlister.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <krestrictedline.h>
+#include <kjanuswidget.h>
+
+#include "smbpasswdfile.h"
+#include "sambafile.h"
+#include "common.h"
+#include "passwd.h"
+#include "usertabimpl.h"
+#include "filemodedlgimpl.h"
+#include "hiddenfileview.h"
+#include "dictmanager.h"
+
+#include "sharedlgimpl.h"
+
+
+
+
+ShareDlgImpl::ShareDlgImpl(QWidget* parent, SambaShare* share)
+ : KcmShareDlg(parent,"sharedlgimpl")
+{
+ if (!share) {
+ kdWarning() << "ShareDlgImpl::Constructor : share parameter is null!" << endl;
+ return;
+ }
+
+ _dictMngr = new DictManager(share);
+ _share = share;
+
+ initDialog();
+ initAdvancedTab();
+}
+
+void ShareDlgImpl::initDialog()
+{
+ if (!_share)
+ return;
+
+ // Base settings
+ _fileView = 0L;
+
+ pathUrlRq->setMode(2+8+16);
+
+ homeChk->setChecked(_share->getName().lower() == "homes");
+ shareNameEdit->setText( _share->getName() );
+
+ _dictMngr->add("path",pathUrlRq);
+
+ _dictMngr->add("comment",commentEdit);
+ _dictMngr->add("available",availableBaseChk);
+ _dictMngr->add("browseable",browseableBaseChk);
+ _dictMngr->add("public",publicBaseChk);
+
+ _dictMngr->add("read only",readOnlyBaseChk);
+
+ // User settings
+
+ _userTab = new UserTabImpl(this,_share);
+ _tabs->insertTab(_userTab,i18n("&Users"),1);
+ _userTab->load();
+ connect(_userTab, SIGNAL(changed()), this, SLOT(changedSlot()));
+
+ // Filename settings
+
+ _dictMngr->add("case sensitive",caseSensitiveCombo,
+ new QStringList(QStringList() << "auto" << "yes" << "no"));
+ _dictMngr->add("preserve case",preserveCaseChk);
+ _dictMngr->add("short preserve case",shortPreserveCaseChk);
+ _dictMngr->add("mangled names",mangledNamesChk);
+ _dictMngr->add("mangle case",mangleCaseChk);
+ _dictMngr->add("mangling char",manglingCharEdit);
+ _dictMngr->add("mangled map",mangledMapEdit);
+
+
+ _dictMngr->add("mangling method",manglingMethodCombo,
+ new QStringList(QStringList() << "hash" << "hash2"));
+
+ _dictMngr->add("default case",defaultCaseCombo,
+ new QStringList(QStringList() << "Lower" << "Upper"));
+
+ _dictMngr->add("hide dot files",hideDotFilesChk);
+ _dictMngr->add("strip dot",hideTrailingDotChk);
+ _dictMngr->add("hide unreadable",hideUnreadableChk);
+ _dictMngr->add("hide unwriteable files",hideUnwriteableFilesChk);
+ _dictMngr->add("hide special files",hideSpecialFilesChk);
+ _dictMngr->add("dos filemode",dosFilemodeChk);
+ _dictMngr->add("dos filetimes",dosFiletimesChk);
+ _dictMngr->add("dos filetime resolution",dosFiletimeResolutionChk);
+
+ // Security tab
+
+ _dictMngr->add("guest only",guestOnlyChk);
+ _dictMngr->add("hosts allow",hostsAllowEdit);
+
+ _dictMngr->add("only user",onlyUserChk);
+ _dictMngr->add("username",userNameEdit);
+
+
+ guestAccountCombo->insertStringList( getUnixUsers() );
+ setComboToString(guestAccountCombo,_share->getValue("guest account"));
+
+ _dictMngr->add("hosts deny",hostsDenyEdit);
+ _dictMngr->add("force directory security mode",forceDirectorySecurityModeEdit);
+ _dictMngr->add("force directory mode",forceDirectoryModeEdit);
+ _dictMngr->add("force security mode",forceSecurityModeEdit);
+
+ _dictMngr->add("force create mode",forceCreateModeEdit);
+ _dictMngr->add("directory security mask",directorySecurityMaskEdit);
+ _dictMngr->add("directory mask",directoryMaskEdit);
+ _dictMngr->add("security mask",securityMaskEdit);
+ _dictMngr->add("create mask",createMaskEdit);
+ _dictMngr->add("inherit permissions",inheritPermissionsChk);
+ _dictMngr->add("inherit acls",inheritAclsChk);
+ _dictMngr->add("nt acl support",ntAclSupportChk);
+ _dictMngr->add("delete readonly",deleteReadonlyChk);
+
+ _dictMngr->add("wide links",wideLinksChk);
+ _dictMngr->add("follow symlinks",followSymlinksChk);
+
+ _dictMngr->add("map hidden",mapHiddenChk);
+ _dictMngr->add("map archive",mapArchiveChk);
+ _dictMngr->add("map system",mapSystemChk);
+ _dictMngr->add("store dos attributes",eaSupportChk);
+
+ _dictMngr->add("ea support",eaSupportChk);
+
+
+ _dictMngr->add("force unknown acl user",forceUnknownAclUserEdit);
+ _dictMngr->add("profile acls",profileAclsChk);
+ _dictMngr->add("map acl inherit",mapAclInheritChk);
+
+
+ // Advanced
+
+ _dictMngr->add("blocking locks",blockingLocksChk);
+ _dictMngr->add("fake oplocks",fakeOplocksChk);
+ _dictMngr->add("locking",lockingChk);
+ _dictMngr->add("level2 oplocks",level2OplocksChk);
+ _dictMngr->add("posix locking",posixLockingChk);
+ _dictMngr->add("strict locking",strictLockingCombo,
+ new QStringList(QStringList() << "Auto" << "yes" << "no"));
+ _dictMngr->add("share modes",shareModesChk);
+ _dictMngr->add("oplocks",oplocksChk);
+
+
+ _dictMngr->add("oplock contention limit",oplockContentionLimitSpin);
+ _dictMngr->add("strict sync",strictSyncChk);
+
+ // Tuning
+
+ _dictMngr->add("strict allocate",strictAllocateChk);
+
+ _dictMngr->add("max connections",maxConnectionsSpin);
+ _dictMngr->add("write cache size",writeCacheSizeSpin);
+ _dictMngr->add("block size",blockSizeSpin);
+
+
+ _dictMngr->add("sync always",syncAlwaysChk);
+ _dictMngr->add("use sendfile",useSendfileChk);
+
+ _dictMngr->add("csc policy",cscPolicyCombo,
+ new QStringList(QStringList() << "manual" << "documents" << "programs" << "disable"));
+
+
+
+ // VFS
+
+ _dictMngr->add("vfs objects",vfsObjectsEdit);
+ _dictMngr->add("vfs options",vfsOptionsEdit);
+
+ // Misc
+
+ _dictMngr->add("preexec",preexecEdit);
+ _dictMngr->add("postexec",postexecEdit);
+ _dictMngr->add("root preexec",rootPreexecEdit);
+ _dictMngr->add("root postexec",rootPostexecEdit);
+
+ _dictMngr->add("preexec close",preexecCloseChk);
+ _dictMngr->add("root preexec close",rootPreexecCloseChk);
+
+ _dictMngr->add("volume",volumeEdit);
+ _dictMngr->add("fstype",fstypeEdit);
+ _dictMngr->add("magic script",magicScriptEdit);
+ _dictMngr->add("magic output",magicOutputEdit);
+ _dictMngr->add("dont descend",dontDescendEdit);
+ _dictMngr->add("set directory",setDirectoryChk);
+ _dictMngr->add("fake directory create times",fakeDirectoryCreateTimesChk);
+
+ _dictMngr->add("msdfs root",msdfsRootChk);
+ _dictMngr->add("msdfs proxy",msdfsProxyEdit);
+
+ _dictMngr->load( _share );
+
+
+ connect( _tabs, SIGNAL(currentChanged(QWidget*)), this, SLOT(tabChangedSlot(QWidget*)));
+ connect(_dictMngr, SIGNAL(changed()), this, SLOT(changedSlot()));
+}
+
+ShareDlgImpl::~ShareDlgImpl()
+{
+ delete _fileView;
+}
+
+void ShareDlgImpl::initAdvancedTab()
+{
+
+ QVBoxLayout *l = new QVBoxLayout(advancedFrame);
+ l->setAutoAdd(true);
+ l->setMargin(0);
+ _janus = new KJanusWidget(advancedFrame,0,KJanusWidget::TreeList);
+ _janus->setRootIsDecorated(false);
+ _janus->setShowIconsInTreeList(true);
+
+ QWidget *w;
+ QFrame *f;
+ QString label;
+ QPixmap icon;
+
+ for (int i=0;i<advancedDumpTab->count();)
+ {
+ w = advancedDumpTab->page(i);
+ label = advancedDumpTab->label(i);
+
+ if (label.lower() == "security")
+ icon = SmallIcon("password");
+ else
+ if (label.lower() == "tuning")
+ icon = SmallIcon("launch");
+ else
+ if (label.lower() == "filenames")
+ icon = SmallIcon("folder");
+ else
+ if (label.lower() == "printing")
+ icon = SmallIcon("fileprint");
+ else
+ if (label.lower() == "locking")
+ icon = SmallIcon("lock");
+ else
+ if (label.lower() == "logon")
+ icon = SmallIcon("kdmconfig");
+ else
+ if (label.lower() == "protocol")
+ icon = SmallIcon("core");
+ else
+ if (label.lower() == "coding")
+ icon = SmallIcon("charset");
+ else
+ if (label.lower() == "socket")
+ icon = SmallIcon("socket");
+ else
+ if (label.lower() == "ssl")
+ icon = SmallIcon("encrypted");
+ else
+ if (label.lower() == "browsing")
+ icon = SmallIcon("konqueror");
+ else
+ if (label.lower() == "misc")
+ icon = SmallIcon("misc");
+ else {
+ icon = QPixmap(16,16);
+ icon.fill();
+ }
+ //SmallIcon("empty2");
+
+ f = _janus->addPage( label,label,icon );
+ l = new QVBoxLayout(f);
+ l->setAutoAdd(true);
+ l->setMargin(0);
+
+ advancedDumpTab->removePage(w);
+
+ w->reparent(f,QPoint(1,1),TRUE);
+
+ }
+
+ w = _tabs->page(5);
+ _tabs->removePage(w);
+ delete w;
+
+
+}
+
+
+void ShareDlgImpl::tabChangedSlot(QWidget* w)
+{
+ // We are only interrested in the Hidden files tab
+ if ( QString(w->name()) == "hiddenFilesTab" )
+ loadHiddenFilesView();
+
+}
+
+void ShareDlgImpl::loadHiddenFilesView()
+{
+
+ if (_fileView)
+ return;
+
+ _fileView = new HiddenFileView( this, _share );
+
+ if ( ! _share->isSpecialSection())
+ _fileView->load();
+
+
+}
+
+void ShareDlgImpl::accept()
+{
+ // Base settings
+ if (!_share)
+ return;
+
+ if (homeChk->isChecked())
+ _share->setName("homes");
+ else
+ _share->setName(shareNameEdit->text());
+
+ // User settings
+
+ _userTab->save();
+
+ // Security
+
+ _share->setValue("guest account",guestAccountCombo->currentText( ) );
+
+
+ // Hidden files
+ if (_fileView)
+ _fileView->save();
+
+ _dictMngr->save( _share );
+
+ KcmShareDlg::accept();
+}
+
+void ShareDlgImpl::homeChkToggled(bool b)
+{
+ shareNameEdit->setDisabled(b);
+ pathUrlRq->setDisabled(b);
+
+ if (b)
+ {
+ shareNameEdit->setText("homes");
+ pathUrlRq->setURL("");
+ directoryPixLbl->setPixmap(DesktopIcon("folder_home",48));
+
+ }
+ else
+ {
+ shareNameEdit->setText( _share->getName() );
+ pathUrlRq->setURL( _share->getValue("path") );
+ directoryPixLbl->setPixmap(DesktopIcon("folder"));
+ }
+}
+
+void ShareDlgImpl::accessModifierBtnClicked()
+{
+ if (!QObject::sender()) {
+ kdWarning() << "ShareDlgImpl::accessModifierBtnClicked() : QObject::sender() is null!" << endl;
+ return;
+ }
+
+
+ QString name = QObject::sender()->name();
+
+ QLineEdit *edit = 0L;
+
+ if (name == "forceCreateModeBtn")
+ edit = forceCreateModeEdit;
+ else
+ if (name == "forceSecurityModeBtn")
+ edit = forceSecurityModeEdit;
+ else
+ if (name == "forceDirectoryModeBtn")
+ edit = forceDirectoryModeEdit;
+ else
+ if (name == "forceDirectorySecurityModeBtn")
+ edit = forceDirectorySecurityModeEdit;
+ else
+ if (name == "createMaskBtn")
+ edit = createMaskEdit;
+ else
+ if (name == "securityMaskBtn")
+ edit = securityMaskEdit;
+ else
+ if (name == "directoryMaskBtn")
+ edit = directoryMaskEdit;
+ else
+ if (name == "directorySecurityMaskBtn")
+ edit = directorySecurityMaskEdit;
+
+ if (!edit) {
+ kdWarning() << "ShareDlgImpl::accessModifierBtnClicked() : edit is null! name=" << name << endl;
+ return;
+ }
+
+ FileModeDlgImpl dlg(this, edit);
+
+ dlg.exec();
+}
+
+void ShareDlgImpl::changedSlot() {
+ m_changed = true;
+ kdDebug(5009) << "ShareDlgImpl::changedSlot()" << endl;
+ emit changed();
+}
+
+void ShareDlgImpl::pathUrlRq_textChanged( const QString & s)
+{
+ if (_fileView && ! _share->isSpecialSection())
+ _fileView->load();
+}
+
+
+#include "sharedlgimpl.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/sharedlgimpl.h b/filesharing/advanced/kcm_sambaconf/sharedlgimpl.h
new file mode 100644
index 00000000..da6e178d
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/sharedlgimpl.h
@@ -0,0 +1,100 @@
+/***************************************************************************
+ sharedlgimpl.h - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef SHAREDLGIMPL_H
+#define SHAREDLGIMPL_H
+
+
+/**
+ * @author Jan Schäfer
+ **/
+
+#include <qptrlist.h>
+
+#include "share.h"
+
+class SambaShare;
+class QWidget;
+class KPopupMenu;
+class KToggleAction;
+class QGroupBox;
+class UserTabImpl;
+class HiddenFileView;
+class DictManager;
+class KJanusWidget;
+
+/**
+ * This class implements the share.ui interface
+ **/
+class ShareDlgImpl : public KcmShareDlg
+{
+Q_OBJECT
+
+public :
+
+ ShareDlgImpl(QWidget* parent, SambaShare* share);
+ ~ShareDlgImpl();
+
+ bool hasChanged() { return m_changed; };
+
+protected :
+
+ /**
+ * Fills all dialog fields with the values
+ * of the SambaShare object
+ **/
+ void initDialog();
+
+ /**
+ * The share object to change with this dialog
+ **/
+ SambaShare* _share;
+
+ HiddenFileView* _fileView;
+ UserTabImpl* _userTab;
+ KJanusWidget* _janus;
+ bool m_changed;
+ DictManager* _dictMngr;
+
+ void loadHiddenFilesView();
+ void initAdvancedTab();
+
+protected slots:
+ virtual void accept();
+ virtual void homeChkToggled(bool);
+ virtual void accessModifierBtnClicked();
+ virtual void changedSlot();
+ virtual void pathUrlRq_textChanged( const QString & );
+
+ void tabChangedSlot(QWidget* w);
+signals:
+ void changed();
+};
+
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.cpp b/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.cpp
new file mode 100644
index 00000000..ce276979
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ smbconfconfigwidget.cpp - description
+ -------------------
+ begin : Tue May 16 2003
+ copyright : (C) 2003 by Jan Sch�er
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <qfileinfo.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+
+#include <kapplication.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+
+#include "smbconfconfigwidget.h"
+
+SmbConfConfigWidget::SmbConfConfigWidget(QWidget* parent)
+ : QWidget(parent,"configWidget")
+{
+
+ QVBoxLayout *layout = new QVBoxLayout(this,5);
+
+ QLabel *lbl = new QLabel(i18n("<p>The SAMBA configuration file <strong>'smb.conf'</strong>" \
+ " could not be found;</p>" \
+ "make sure you have SAMBA installed.\n\n"), this);
+
+ QHBoxLayout *hbox = new QHBoxLayout(this);
+ QPushButton *btn = new QPushButton(i18n("Specify Location"), this);
+ connect(btn, SIGNAL(pressed()), this, SLOT( btnPressed()));
+
+ btn->setDefault(false);
+ btn->setAutoDefault(false);
+
+ hbox->addStretch();
+ hbox->addWidget(btn);
+
+ layout->addWidget(lbl);
+ layout->addLayout(hbox);
+ layout->addStretch();
+}
+
+void SmbConfConfigWidget::btnPressed() {
+ QString smbConf = KFileDialog::getOpenFileName("/",
+ "smb.conf|Samba conf. File\n"
+ "*|All Files",0,i18n("Get smb.conf Location"));
+
+ if (smbConf.isEmpty())
+ return;
+ if ( ! QFileInfo(smbConf).isReadable() ) {
+ KMessageBox::sorry(this,i18n("<qt>The file <i>%1</i> could not be read.</qt>").arg(smbConf),i18n("Could Not Read File"));
+ return;
+ }
+
+ KConfig config("ksambaplugin");
+
+ config.setGroup("KSambaKonqiPlugin");
+ config.writeEntry("smb.conf",smbConf);
+ config.sync();
+
+ emit smbConfChoosed(smbConf);
+
+}
+
+#include "smbconfconfigwidget.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.h b/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.h
new file mode 100644
index 00000000..bd00357a
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/smbconfconfigwidget.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ smbconfconfigwidget.h - description
+ -------------------
+ begin : Tue May 16 2003
+ copyright : (C) 2003 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef SMBCONFCONFIGWIDGET_H
+#define SMBCONFCONFIGWIDGET_H
+
+#include <qwidget.h>
+
+class SmbConfConfigWidget : public QWidget {
+Q_OBJECT
+public:
+ SmbConfConfigWidget(QWidget*);
+
+protected slots:
+ void btnPressed();
+signals:
+ void smbConfChoosed(const QString &);
+};
+
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/smbpasswdfile.cpp b/filesharing/advanced/kcm_sambaconf/smbpasswdfile.cpp
new file mode 100644
index 00000000..594864b9
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/smbpasswdfile.cpp
@@ -0,0 +1,229 @@
+/***************************************************************************
+ smbpasswdfile.cpp - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qstringlist.h>
+
+#include <kurl.h>
+#include <kdebug.h>
+#include <kpassdlg.h>
+#include <klocale.h>
+#include <kprocess.h>
+
+#include "sambafile.h"
+#include "smbpasswdfile.h"
+#include "passwd.h"
+
+
+QStringList SambaUserList::getUserNames()
+{
+ QStringList list;
+
+ SambaUser *user;
+ for ( user = first(); user; user = next() )
+ {
+ list.append(user->name);
+ }
+
+ return list;
+}
+
+
+SmbPasswdFile::SmbPasswdFile() {
+}
+
+
+SmbPasswdFile::SmbPasswdFile(const KURL & url)
+{
+ setUrl(url);
+}
+
+SmbPasswdFile::~SmbPasswdFile()
+{
+}
+
+void SmbPasswdFile::setUrl(const KURL & url) {
+ _url = url;
+}
+
+/**
+ * Returns a list of all users stored in
+ * the smbpasswd file
+ **/
+SambaUserList SmbPasswdFile::getSambaUserList()
+{
+ QFile f(_url.path());
+
+ SambaUserList list;
+
+ if ( f.open(IO_ReadOnly) )
+ {
+ QTextStream t( &f );
+ QString s;
+ while ( !t.eof() )
+ {
+ s = t.readLine().stripWhiteSpace();
+
+ // Ignore comments
+ if (s.left(1)=="#")
+ continue;
+
+ QStringList l = QStringList::split(":",s);
+
+ SambaUser* user = new SambaUser(l[0],l[1].toInt());
+ user->gid = getUserGID(l[0]);
+ user->isUserAccount = l[4].contains('U');
+ user->hasNoPassword = l[4].contains('N');;
+ user->isDisabled = l[4].contains('D');;
+ user->isWorkstationTrustAccount = l[4].contains('W');;
+ list.append(user);
+ }
+ f.close();
+ }
+
+ return list;
+}
+
+bool SmbPasswdFile::executeSmbpasswd(const QStringList & args) {
+ KProcess p;
+ p << "smbpasswd" << args;
+
+ connect( &p, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(smbpasswdStdOutReceived(KProcess*,char*,int)));
+
+ _smbpasswdOutput = "";
+
+ bool result = p.start(KProcess::Block,KProcess::Stdout);
+
+ if (result)
+ {
+ kdDebug(5009) << _smbpasswdOutput << endl;
+ }
+
+ return result;
+}
+
+/**
+ * Tries to add the passed user to the smbpasswd file
+ * returns true if successful otherwise false
+ **/
+bool SmbPasswdFile::addUser(const SambaUser & user,const QString & password)
+{
+ KProcess p;
+ p << "smbpasswd" << "-a" << user.name;
+
+ p << password;
+
+ connect( &p, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(smbpasswdStdOutReceived(KProcess*,char*,int)));
+
+ _smbpasswdOutput = "";
+
+ bool result = p.start(KProcess::Block,KProcess::Stdout);
+
+ if (result)
+ {
+ kdDebug(5009) << _smbpasswdOutput << endl;
+ }
+
+ return result;
+}
+
+/**
+ * Tries to remove the passed user from the smbpasswd file
+ * returns true if successful otherwise false
+ **/
+bool SmbPasswdFile::removeUser(const SambaUser & user)
+{
+ QStringList l;
+ l << "-x" << user.name;
+ return executeSmbpasswd(l);
+}
+
+bool SmbPasswdFile::changePassword(const SambaUser & user, const QString & newPassword)
+{
+ return addUser(user,newPassword);
+}
+
+
+void SmbPasswdFile::smbpasswdStdOutReceived(KProcess *, char *buffer, int buflen)
+{
+ _smbpasswdOutput+=QString::fromLatin1(buffer,buflen);
+}
+
+
+/**
+ * Returns the Url of the smbpasswd file
+ * specified in the [global] section of
+ * the smb.conf file.
+ * If there is no entry in the [global] section
+ * it tries to guess the location.
+ **/
+KURL SmbPasswdFile::getUrlFromSambaFile(const SambaFile * /*file*/)
+{
+ kdWarning() << "SmbPasswdFile::getUrlFromSambaFile unimplemeneted!" << endl;
+ return KURL("");
+}
+
+bool SmbPasswdFile::enableUser(const SambaUser & user) {
+ QStringList l;
+ l << "-e" << user.name;
+ return executeSmbpasswd(l);
+}
+
+bool SmbPasswdFile::disableUser(const SambaUser & user) {
+ QStringList l;
+ l << "-d" << user.name;
+ return executeSmbpasswd(l);
+}
+
+bool SmbPasswdFile::setNoPassword(const SambaUser & user) {
+ QStringList l;
+ l << "-n" << user.name;
+ return executeSmbpasswd(l);
+}
+
+bool SmbPasswdFile::setMachineTrustAccount(const SambaUser & user) {
+ QStringList l;
+ l << "-m" << user.name;
+ return executeSmbpasswd(l);
+}
+
+bool SmbPasswdFile::joinADomain(const QString & domain, const QString & server,
+ const QString & user, const QString & password) {
+ QStringList l;
+ l << "-j" << domain;
+ l << "-r" << server;
+ l << "-U" << user << "%" << password;
+ return executeSmbpasswd(l);
+}
+
+
+#include "smbpasswdfile.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/smbpasswdfile.h b/filesharing/advanced/kcm_sambaconf/smbpasswdfile.h
new file mode 100644
index 00000000..6a15c8c0
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/smbpasswdfile.h
@@ -0,0 +1,141 @@
+/***************************************************************************
+ smbpasswdfile.h - description
+ -------------------
+ begin : Tue June 6 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef SMBPASSWDFILE_H
+#define SMBPASSWDFILE_H
+
+
+/**
+ *@author Jan Schäfer
+ */
+
+#include <qstring.h>
+#include <kurl.h>
+
+class SambaFile;
+class KProcess;
+
+/**
+ * Simple class to store a Samba User
+ **/
+class SambaUser
+{
+public:
+ SambaUser(const QString & aName = QString::null, int anUid = -1) {name = aName; uid = anUid;};
+
+ QString name;
+ int uid;
+ int gid;
+ bool isUserAccount;
+ bool hasNoPassword;
+ bool isDisabled;
+ bool isWorkstationTrustAccount;
+};
+
+/**
+ * A QPtrList of SambaUser
+ **/
+class SambaUserList : public QPtrList<SambaUser>
+{
+public:
+ QStringList getUserNames();
+};
+
+/**
+ * This class represents the SAMBA smbpasswd file.
+ * It provides :
+ * - getting a list of all SAMBA users
+ * - adding a new user -> uses smbpasswd program
+ * - removing an existing user -> uses smbpasswd program
+ **/
+class SmbPasswdFile : public QObject
+{
+Q_OBJECT
+public:
+ SmbPasswdFile();
+ SmbPasswdFile(const KURL &);
+ ~SmbPasswdFile();
+
+ void setUrl(const KURL &);
+
+ /**
+ * Returns a list of all users stored in
+ * the smbpasswd file
+ **/
+ SambaUserList getSambaUserList();
+
+ /**
+ * Calls smbpasswd with the given arguments
+ **/
+ bool executeSmbpasswd(const QStringList & args);
+
+ /**
+ * Tries to add the passed user to the smbpasswd file
+ * returns true if successful otherwise false
+ **/
+ bool addUser(const SambaUser &, const QString & password);
+
+ /**
+ * Tries to remove the passed user from the smbpasswd file
+ * returns true if successful otherwise false
+ **/
+ bool removeUser(const SambaUser &);
+
+ /**
+ * Tries to change the password of the passed user
+ * if it fails returns false otherwise true
+ **/
+ bool changePassword(const SambaUser &, const QString & password);
+
+ bool enableUser(const SambaUser &);
+
+ bool disableUser(const SambaUser &);
+
+ bool setNoPassword(const SambaUser &);
+
+ bool setMachineTrustAccount(const SambaUser &);
+
+ bool joinADomain(const QString &, const QString &, const QString &, const QString &);
+
+ /**
+ * Returns the Url of the smbpasswd file
+ * specified in the [global] section of
+ * the smb.conf file.
+ * If there is no entry in the [global] section
+ * it tries to guess the location.
+ **/
+ static KURL getUrlFromSambaFile(const SambaFile *file);
+protected:
+ KURL _url;
+ QString _smbpasswdOutput;
+
+protected slots:
+ void smbpasswdStdOutReceived(KProcess*,char*,int);
+};
+
+#endif
diff --git a/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui b/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui
new file mode 100644
index 00000000..acfaf91f
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui
@@ -0,0 +1,332 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>SocketOptionsDlg</class>
+<comment>
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+</comment>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>SocketOptionsDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>457</width>
+ <height>240</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Socket Options</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout106</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>SO_SNDLOWATChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_SNDLOWAT:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>IPTOS_THROUGHPUTChk</cstring>
+ </property>
+ <property name="text">
+ <string>IPTOS_THROUGHPUT</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>SO_SNDBUFChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_SNDBUF:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>SO_KEEPALIVEChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_KEEPALIVE</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>SO_RCVBUFChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_RCVBUF:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>SO_SNDBUFSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="2">
+ <property name="name">
+ <cstring>SO_RCVLOWATSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>SO_BROADCASTChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_BROADCAST</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>IPTOS_LOWDELAYChk</cstring>
+ </property>
+ <property name="text">
+ <string>IPTOS_LOWDELAY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>TCP_NODELAYChk</cstring>
+ </property>
+ <property name="text">
+ <string>TCP_NODELAY</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1">
+ <property name="name">
+ <cstring>SO_RCVLOWATChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_RCVLOWAT:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="2">
+ <property name="name">
+ <cstring>SO_RCVBUFSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="2" column="2">
+ <property name="name">
+ <cstring>SO_SNDLOWATSpin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>SO_REUSEADDRChk</cstring>
+ </property>
+ <property name="text">
+ <string>SO_REUSEADDR</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame8</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonHelp</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Help</string>
+ </property>
+ <property name="accel">
+ <string>F1</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>SocketOptionsDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>SocketOptionsDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>SO_RCVLOWATChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_RCVLOWATSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_SNDLOWATChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_SNDLOWATSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_RCVBUFChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_RCVBUFSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>SO_SNDBUFChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>SO_SNDBUFSpin</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="local" impldecl="in implementation">assert.h</include>
+ <include location="local" impldecl="in implementation">sambashare.h</include>
+ <include location="local" impldecl="in implementation">socketoptionsdlg.ui.h</include>
+</includes>
+<forwards>
+ <forward>class SambaShare;</forward>
+</forwards>
+<variables>
+ <variable>SambaShare* _share;</variable>
+</variables>
+<slots>
+ <slot>setShare( SambaShare * share )</slot>
+ <slot access="private" returnType="bool">getBoolValue( const QString &amp; str, const QString &amp; name )</slot>
+ <slot access="private" returnType="int">getIntValue( const QString &amp; str, const QString &amp; name )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui.h b/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui.h
new file mode 100644
index 00000000..bc8c5d49
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/socketoptionsdlg.ui.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+void SocketOptionsDlg::setShare( SambaShare * share )
+{
+ assert(share);
+
+ _share = share;
+
+ QString s = _share->getValue("socket options");
+
+ s = s.simplifyWhiteSpace();
+
+ // The string s has now the form e.g. :
+ // "OPTION1=1 OPTION2=0 OPTION3=2234 OPTION4"
+
+SO_KEEPALIVEChk->setChecked(getBoolValue( s, "SO_KEEPALIVE") );
+SO_REUSEADDRChk->setChecked( getBoolValue( s, "SO_REUSEADDR") );
+SO_BROADCASTChk->setChecked( getBoolValue( s, "SO_BROADCAST") );
+TCP_NODELAYChk->setChecked( getBoolValue( s, "TCP_NODELAY") );
+IPTOS_LOWDELAYChk->setChecked( getBoolValue( s, "IPTOS_LOWDELAY") );
+IPTOS_THROUGHPUTChk->setChecked( getBoolValue( s, "IPTOS_THROUGHPUT") );
+SO_SNDBUFChk->setChecked( getBoolValue( s, "SO_SNDBUF") );
+SO_RCVBUFChk->setChecked( getBoolValue( s, "SO_RCVBUF") );
+SO_SNDLOWATChk->setChecked( getBoolValue( s, "SO_SNDLOWAT") );
+SO_RCVLOWATChk->setChecked( getBoolValue( s, "SO_RCVLOWAT") );
+
+SO_SNDBUFSpin->setValue( getIntValue( s, "SO_SNDBUF") );
+SO_RCVBUFSpin->setValue( getIntValue( s, "SO_RCVBUF") );
+SO_SNDLOWATSpin->setValue( getIntValue( s, "SO_SNDLOWAT") );
+SO_RCVLOWATSpin->setValue( getIntValue( s, "SO_RCVLOWAT") );
+
+}
+
+
+
+bool SocketOptionsDlg::getBoolValue( const QString & str, const QString & name )
+{
+ QString s = str;
+ int i = s.find(name ,0,false);
+
+ if (i > -1)
+ {
+ s = s.remove(0,i+1+QString(name).length());
+ if ( s.startsWith("=") )
+ {
+ s = s.remove(0,1);
+ if ( s.startsWith("0"))
+ return false;
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+
+ return false;
+}
+
+int SocketOptionsDlg::getIntValue( const QString & str, const QString & name )
+{
+ QString s = str;
+ int i = s.find(name ,0,false);
+
+ if (i > -1)
+ {
+ s = s.remove(0,i+1+QString(name).length());
+ if ( s.startsWith("=") )
+ {
+ s = s.remove(0,1);
+
+ i = s.find(" ");
+ if (i < 0)
+ i = s.length();
+ else
+ i++;
+
+ s = s.left( i );
+
+ return s.toInt();
+ }
+ else
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/filesharing/advanced/kcm_sambaconf/userselectdlg.ui b/filesharing/advanced/kcm_sambaconf/userselectdlg.ui
new file mode 100644
index 00000000..8fa38372
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/userselectdlg.ui
@@ -0,0 +1,301 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>UserSelectDlg</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>UserSelectDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>485</width>
+ <height>269</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Select Users</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>groupBox87</cstring>
+ </property>
+ <property name="title">
+ <string>Select &amp;Users</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>UID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>GID</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizeable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>userListView</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="1">
+ <property name="name">
+ <cstring>accessBtnGrp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Acc&amp;ess</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>defaultRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Default</string>
+ </property>
+ <property name="accel">
+ <number>8388676</number>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>readRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Read access</string>
+ </property>
+ <property name="accel">
+ <number>8388690</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>writeRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Write access</string>
+ </property>
+ <property name="accel">
+ <number>8388695</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>adminRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Admin access</string>
+ </property>
+ <property name="accel">
+ <number>8388673</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>noAccessRadio</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;No access at all</string>
+ </property>
+ <property name="accel">
+ <number>8388686</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer90</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QFrame" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>frame16</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>285</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <number>0</number>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="accel">
+ <number>0</number>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>UserSelectDlg</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>UserSelectDlg</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="local" impldecl="in declaration">sambashare.h</include>
+ <include location="local" impldecl="in implementation">smbpasswdfile.h</include>
+ <include location="local" impldecl="in implementation">kurl.h</include>
+ <include location="local" impldecl="in implementation">userselectdlg.ui.h</include>
+</includes>
+<variables>
+ <variable>QStringList selectedUsers;</variable>
+ <variable>int access;</variable>
+</variables>
+<slots>
+ <slot>init( const QStringList &amp; specifiedUsers, SambaShare * share )</slot>
+ <slot access="protected">accept()</slot>
+ <slot specifier="non virtual" returnType="QStringList">getSelectedUsers()</slot>
+ <slot specifier="non virtual" returnType="int">getAccess()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/userselectdlg.ui.h b/filesharing/advanced/kcm_sambaconf/userselectdlg.ui.h
new file mode 100644
index 00000000..af323fe6
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/userselectdlg.ui.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void UserSelectDlg::init(const QStringList & specifiedUsers, SambaShare* share)
+{
+ SmbPasswdFile passwd( KURL(share->getValue("smb passwd file",true,true)) );
+ SambaUserList sambaList = passwd.getSambaUserList();
+
+ for (SambaUser * user = sambaList.first(); user; user = sambaList.next() )
+ {
+ if (! specifiedUsers.contains(user->name))
+ new QListViewItem(userListView, user->name, QString::number(user->uid), QString::number(user->gid));
+ }
+
+}
+
+
+void UserSelectDlg::accept()
+{
+ QListViewItemIterator it( userListView);
+
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->isSelected() )
+ selectedUsers << it.current()->text(0);
+ }
+
+ access = accessBtnGrp->id(accessBtnGrp->selected());
+
+ QDialog::accept();
+
+}
+
+
+QStringList UserSelectDlg::getSelectedUsers()
+{
+ return selectedUsers;
+}
+
+
+int UserSelectDlg::getAccess()
+{
+ return access;
+}
diff --git a/filesharing/advanced/kcm_sambaconf/usertab.ui b/filesharing/advanced/kcm_sambaconf/usertab.ui
new file mode 100644
index 00000000..43e556dc
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/usertab.ui
@@ -0,0 +1,352 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>UserTab</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>UserTab</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>548</width>
+ <height>396</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Users</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox53</cstring>
+ </property>
+ <property name="title">
+ <string>All U&amp;nspecified Users</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Allow</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Reject</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>allUnspecifiedUsersCombo</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox77</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Spec&amp;ified Users</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTable" row="0" column="0" rowspan="5" colspan="1">
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>UID</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>GID</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Access Rights</string>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>userTable</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>50</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>4</number>
+ </property>
+ <property name="showGrid">
+ <bool>false</bool>
+ </property>
+ <property name="columnMovingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>MultiRow</enum>
+ </property>
+ <property name="focusStyle">
+ <enum>FollowStyle</enum>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>addUserBtn</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd User...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="4" column="1">
+ <property name="name">
+ <cstring>expertBtn</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xpert</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>addGroupBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Add &amp;Group...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>removeSelectedBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Remo&amp;ve Selected</string>
+ </property>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer88</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>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox35</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>All Users Should be Forced to the Follo&amp;wing User/Group</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Forc&amp;e user:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceUserCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>forceUserCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel13</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Fo&amp;rce group:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>forceGroupCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>forceGroupCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>addUserBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>addUserBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>addGroupBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>addGroupBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>removeSelectedBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>removeSelectedBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>expertBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>expertBtnClicked()</slot>
+ </connection>
+ <connection>
+ <sender>allUnspecifiedUsersCombo</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>addUserBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>addGroupBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>removeSelectedBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>expertBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>forceUserCombo</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>forceGroupCombo</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>UserTab</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>allUnspecifiedUsersCombo</tabstop>
+ <tabstop>userTable</tabstop>
+ <tabstop>addUserBtn</tabstop>
+ <tabstop>addGroupBtn</tabstop>
+ <tabstop>removeSelectedBtn</tabstop>
+ <tabstop>expertBtn</tabstop>
+ <tabstop>forceUserCombo</tabstop>
+ <tabstop>forceGroupCombo</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">userselectdlg.h</include>
+ <include location="local" impldecl="in implementation">groupselectdlg.h</include>
+ <include location="local" impldecl="in implementation">usertab.ui.h</include>
+</includes>
+<signals>
+ <signal>changed()</signal>
+</signals>
+<slots>
+ <slot>addUserBtnClicked()</slot>
+ <slot>removeSelectedBtnClicked()</slot>
+ <slot>addGroupBtnClicked()</slot>
+ <slot>expertBtnClicked()</slot>
+ <slot>changedSlot()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/kcm_sambaconf/usertab.ui.h b/filesharing/advanced/kcm_sambaconf/usertab.ui.h
new file mode 100644
index 00000000..088f468a
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/usertab.ui.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void UserTab::addUserBtnClicked()
+{
+
+}
+
+
+void UserTab::removeSelectedBtnClicked()
+{
+
+}
+
+
+void UserTab::addGroupBtnClicked()
+{
+
+}
+
+
+
+void UserTab::expertBtnClicked()
+{
+
+}
+
+
+void UserTab::changedSlot()
+{
+ emit changed();
+}
diff --git a/filesharing/advanced/kcm_sambaconf/usertabimpl.cpp b/filesharing/advanced/kcm_sambaconf/usertabimpl.cpp
new file mode 100644
index 00000000..b6d2b0da
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/usertabimpl.cpp
@@ -0,0 +1,418 @@
+/***************************************************************************
+ usertabimpl.cpp - description
+ -------------------
+ begin : Mon Jul 15 2002
+ copyright : (C) 2002 by Jan Sch�er
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#include <unistd.h> // for getuid
+
+#include <qcombobox.h>
+#include <qstringlist.h>
+#include <passwd.h>
+#include <qregexp.h>
+#include <qtable.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+
+#include <klistview.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kinputdialog.h>
+
+#include "usertabimpl.h"
+#include "sambashare.h"
+#include "smbpasswdfile.h"
+#include "passwd.h"
+#include "groupselectdlg.h"
+#include "userselectdlg.h"
+#include "expertuserdlg.h"
+
+#include <assert.h>
+
+/**
+ * @pre share is not null
+ * @post _share = share
+ */
+UserTabImpl::UserTabImpl(QWidget* parent, SambaShare* share)
+ : UserTab(parent)
+{
+ if (share == 0L) {
+ kdWarning() << "WARNING: UserTabImpl constructor: share parameter is null!" << endl;
+ return;
+ }
+
+ _share = share;
+ userTable->setLeftMargin(0);
+ // userTable->setColumnStretchable ( 3, true );
+
+}
+
+UserTabImpl::~UserTabImpl()
+{
+}
+
+void UserTabImpl::load()
+{
+ if (_share == 0L)
+ return;
+
+ loadForceCombos();
+ loadUsers(_share->getValue("valid users"),
+ _share->getValue("read list"),
+ _share->getValue("write list"),
+ _share->getValue("admin users"),
+ _share->getValue("invalid users"));
+}
+
+void UserTabImpl::loadUsers(const QString & validUsersStr,
+ const QString & readListStr,
+ const QString & writeListStr,
+ const QString & adminUsersStr,
+ const QString & invalidUsersStr)
+{
+ userTable->setNumRows(0);
+ QStringList validUsers = QStringList::split(QRegExp("[,\\s]+"),validUsersStr);
+ QStringList readList = QStringList::split(QRegExp("[,\\s]+"),readListStr);
+ QStringList writeList = QStringList::split(QRegExp("[,\\s]+"),writeListStr);
+ QStringList adminUsers = QStringList::split(QRegExp("[,\\s]+"),adminUsersStr);
+ QStringList invalidUsers = QStringList::split(QRegExp("[,\\s]+"),invalidUsersStr);
+
+ // if the valid users list contains no entries
+ // then all users are allowed, except those that are
+ // in the invalid list
+ if (validUsers.empty())
+ allUnspecifiedUsersCombo->setCurrentItem(0);
+ else
+ allUnspecifiedUsersCombo->setCurrentItem(1);
+
+ removeDuplicates(validUsers,readList,writeList,adminUsers,invalidUsers);
+
+ addListToUserTable(adminUsers,3);
+ addListToUserTable(writeList,2);
+ addListToUserTable(readList,1);
+ addListToUserTable(validUsers,0);
+ addListToUserTable(invalidUsers,4);
+
+}
+
+
+void UserTabImpl::loadForceCombos() {
+ forceUserCombo->insertItem("");
+ forceGroupCombo->insertItem("");
+
+ QStringList unixGroups = getUnixGroups();
+
+ forceUserCombo->insertStringList( getUnixUsers() );
+ forceGroupCombo->insertStringList( unixGroups );
+
+ setComboToString(forceUserCombo, _share->getValue("force user"));
+ setComboToString(forceGroupCombo, _share->getValue("force group"));
+}
+
+/**
+ * Remove all duplicates of the different list, so that
+ * all lists are disjunct.
+ */
+void UserTabImpl::removeDuplicates( QStringList & validUsers, QStringList & readList,
+ QStringList & writeList, QStringList & adminUsers,
+ QStringList & invalidUsers)
+{
+ removeAll(adminUsers, writeList);
+
+ removeAll(writeList, readList);
+ removeAll(adminUsers, readList);
+
+ removeAll(readList, validUsers);
+ removeAll(writeList, validUsers);
+ removeAll(adminUsers, validUsers);
+
+ removeAll(invalidUsers, validUsers);
+ removeAll(invalidUsers, readList);
+ removeAll(invalidUsers, writeList);
+ removeAll(invalidUsers, adminUsers);
+}
+
+/**
+ * Remove all entries of entryList from the fromList
+ */
+void UserTabImpl::removeAll(QStringList & entryList, QStringList & fromList) {
+ for (QStringList::Iterator it = entryList.begin(); it != entryList.end(); ++it) {
+ fromList.remove((*it));
+ }
+}
+
+
+void UserTabImpl::addListToUserTable(const QStringList & list, int accessRight) {
+
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ addUserToUserTable(*it,accessRight);
+ }
+}
+
+void UserTabImpl::addUserToUserTable(const QString & user, int accessRight) {
+ int row = userTable->numRows();
+ userTable->setNumRows(row+1);
+ setAllowedUser(row, user);
+
+ QComboTableItem* comboItem = static_cast<QComboTableItem*>(userTable->item(row,3));
+ comboItem->setCurrentItem(accessRight);
+}
+
+QString UserTabImpl::removeQuotationMarks(const QString & name) {
+ QString result = name;
+ if (name.left(1) == "\"") {
+ result = result.left(result.length()-1);
+ result = result.right(result.length()-1);
+ }
+ return result;
+}
+
+QString UserTabImpl::removeGroupTag(const QString & name) {
+ QString result = name;
+
+
+ if (nameIsGroup(result)) {
+ result = result.right(result.length()-1);
+ if (nameIsGroup(result))
+ result = result.right(result.length()-1);
+ }
+
+ return result;
+}
+
+
+bool UserTabImpl::nameIsGroup(const QString & name) {
+ QString quoteless = removeQuotationMarks(name);
+ if (quoteless.left(1) == "@" ||
+ quoteless.left(1) == "+" ||
+ quoteless.left(1) == "&")
+ return true;
+
+ return false;
+
+}
+
+void UserTabImpl::setAllowedUser(int i, const QString & name)
+{
+ QStringList accessRights;
+ accessRights << i18n("Default") << i18n("Read only") << i18n("Writeable") << i18n("Admin") << i18n("Reject");
+
+ QString uid;
+ QString gid;
+
+ QString name2 = name;
+
+ if (nameIsGroup(name2))
+ {
+ QString name3 = removeGroupTag(name2);
+ uid = "";
+ gid = QString::number(getGroupGID(name3));
+ _specifiedGroups << name3;
+ }
+ else
+ {
+ uid = QString::number(getUserUID(name2));
+ gid = QString::number(getUserGID(name2));
+ _specifiedUsers << name2;
+ }
+
+ if (name2.contains(' '))
+ name2 = "\""+name2+"\"";
+
+
+ QTableItem* item = new QTableItem( userTable,QTableItem::Never, name2 );
+ userTable->setItem(i,0,item);
+
+ item = new QTableItem( userTable,QTableItem::Never, uid );
+ userTable->setItem(i,1,item);
+
+ item = new QTableItem( userTable,QTableItem::Never, gid );
+ userTable->setItem(i,2,item);
+
+ QComboTableItem* comboItem = new QComboTableItem( userTable,accessRights);
+ userTable->setItem(i,3,comboItem);
+
+}
+
+void UserTabImpl::addUserBtnClicked()
+{
+ if (getuid() == 0) {
+ UserSelectDlg *dlg = new UserSelectDlg();
+ dlg->init(_specifiedUsers,_share);
+
+ QStringList selectedUsers = dlg->getSelectedUsers();
+
+ if (dlg->exec()) {
+ for (QStringList::Iterator it = selectedUsers.begin(); it != selectedUsers.end(); ++it)
+ {
+ addUserToUserTable(*it,dlg->getAccess());
+ }
+ }
+
+ delete dlg;
+ } else {
+ bool ok;
+ QString name = KInputDialog::getText(i18n("Add User"),i18n("Name:"),
+ QString::null,&ok );
+
+ if (ok)
+ addUserToUserTable(name,0);
+ }
+}
+
+void UserTabImpl::removeSelectedBtnClicked()
+{
+ QMemArray<int>rows;
+
+ int j=0;
+
+ for (int i=0; i<userTable->numRows(); i++)
+ {
+ if (userTable->isRowSelected(i))
+ {
+ if (nameIsGroup(userTable->item(i,0)->text())) {
+ _specifiedGroups.remove( removeGroupTag(removeQuotationMarks(userTable->item(i,0)->text())));
+ } else
+ _specifiedUsers.remove(userTable->item(i,0)->text());
+
+ rows.resize(j+1);
+ rows[j] = i;
+ j++;
+ }
+ }
+
+ userTable->removeRows(rows);
+}
+
+
+void UserTabImpl::addGroupBtnClicked()
+{
+ GroupSelectDlg *dlg = new GroupSelectDlg();
+ dlg->init(_specifiedGroups);
+ QStringList selectedGroups = dlg->getSelectedGroups();
+ if (dlg->exec()) {
+ for (QStringList::Iterator it = selectedGroups.begin(); it != selectedGroups.end(); ++it)
+ {
+ kdDebug(5009) << "GroupKind: " << dlg->getGroupKind() << endl;
+ QString name = dlg->getGroupKind() + (*it);
+ addUserToUserTable(name,dlg->getAccess());
+ }
+ }
+
+ delete dlg;
+}
+
+void UserTabImpl::expertBtnClicked() {
+ ExpertUserDlg *dlg = new ExpertUserDlg();
+ QString validUsersStr;
+ QString readListStr;
+ QString writeListStr;
+ QString adminUsersStr;
+ QString invalidUsersStr;
+
+ saveUsers(validUsersStr,readListStr,writeListStr,adminUsersStr,invalidUsersStr);
+
+ dlg->validUsersEdit->setText(validUsersStr);
+ dlg->readListEdit->setText(readListStr);
+ dlg->writeListEdit->setText(writeListStr);
+ dlg->adminUsersEdit->setText(adminUsersStr);
+ dlg->invalidUsersEdit->setText(invalidUsersStr);
+
+ if (dlg->exec()) {
+ loadUsers(dlg->validUsersEdit->text(),
+ dlg->readListEdit->text(),
+ dlg->writeListEdit->text(),
+ dlg->adminUsersEdit->text(),
+ dlg->invalidUsersEdit->text());
+
+ }
+
+ delete dlg;
+}
+
+void UserTabImpl::save()
+{
+ QString validUsersStr;
+ QString readListStr;
+ QString writeListStr;
+ QString adminUsersStr;
+ QString invalidUsersStr;
+
+ saveUsers(validUsersStr,readListStr,writeListStr,adminUsersStr,invalidUsersStr);
+
+ _share->setValue("valid users",validUsersStr);
+ _share->setValue("read list", readListStr);
+ _share->setValue("write list", writeListStr);
+ _share->setValue("admin users", adminUsersStr);
+ _share->setValue("invalid users", invalidUsersStr);
+
+ _share->setValue("force user",forceUserCombo->currentText( ) );
+ _share->setValue("force group",forceGroupCombo->currentText( ) );
+}
+
+void UserTabImpl::saveUsers(QString & validUsersStr,
+ QString & readListStr,
+ QString & writeListStr,
+ QString & adminUsersStr,
+ QString & invalidUsersStr)
+{
+ QStringList validUsers;
+ QStringList writeList;
+ QStringList readList;
+ QStringList adminUsers;
+ QStringList invalidUsers;
+
+ bool allowAllUsers = allUnspecifiedUsersCombo->currentItem() == 0;
+
+ for (int i=0; i<userTable->numRows(); i++)
+ {
+ QTableItem* item = userTable->item(i,0);
+ QComboTableItem* comboItem = static_cast<QComboTableItem*>(userTable->item(i,3));
+
+ if (! allowAllUsers && comboItem->currentItem() < 4)
+ validUsers.append(item->text());
+
+ switch (comboItem->currentItem())
+ {
+ case 0 : break;
+ case 1 : readList.append(item->text());break;
+ case 2 : writeList.append(item->text());break;
+ case 3 : adminUsers.append(item->text());break;
+ case 4 : invalidUsers.append(item->text());break;
+ }
+ }
+
+ validUsersStr = validUsers.join(",");
+ readListStr = readList.join(",");
+ writeListStr = writeList.join(",");
+ adminUsersStr = adminUsers.join(",");
+ invalidUsersStr = invalidUsers.join(",");
+}
+
+
+
+#include "usertabimpl.moc"
diff --git a/filesharing/advanced/kcm_sambaconf/usertabimpl.h b/filesharing/advanced/kcm_sambaconf/usertabimpl.h
new file mode 100644
index 00000000..78d3697a
--- /dev/null
+++ b/filesharing/advanced/kcm_sambaconf/usertabimpl.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ usertabimpl.h - description
+ -------------------
+ begin : Mon Jul 15 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/******************************************************************************
+ * *
+ * This file is part of KSambaPlugin. *
+ * *
+ * KSambaPlugin 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. *
+ * *
+ * KSambaPlugin 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with KSambaPlugin; if not, write to the Free Software *
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
+ * *
+ ******************************************************************************/
+
+#ifndef USERTABIMPL_H
+#define USERTABIMPL_H
+
+#include "usertab.h"
+#include "common.h"
+#include "qstringlist.h"
+
+/**
+ *@author Jan Schäfer
+ */
+
+class KListViewItem;
+class SambaShare;
+
+/**
+ * Implements the usertab.ui interface
+ * A Widget where you can add SambaUsers to the valid users, rejected users,
+ * write list, read list and admin list parameters of the smb.conf file
+ **/
+class UserTabImpl : public UserTab
+{
+Q_OBJECT
+public:
+ UserTabImpl(QWidget* parent, SambaShare* share);
+ ~UserTabImpl();
+
+ void load();
+ void save();
+private:
+ SambaShare* _share;
+ QStringList _specifiedUsers;
+ QStringList _specifiedGroups;
+
+ void setAllowedUser(int, const QString &);
+ void removeDuplicates( QStringList & validUsers, QStringList & readList,
+ QStringList & writeList, QStringList & adminUsers,
+ QStringList & invalidUsers);
+
+ void removeAll(QStringList & entryList, QStringList & fromList);
+ void addListToUserTable(const QStringList & list, int accessRight);
+ void loadForceCombos();
+
+ void loadUsers(const QString & validUsersStr,
+ const QString & readListStr,
+ const QString & writeListStr,
+ const QString & adminUsersStr,
+ const QString & invalidUsersStr);
+
+ void saveUsers(QString & validUsersStr,
+ QString & readListStr,
+ QString & writeListStr,
+ QString & adminUsersStr,
+ QString & invalidUsersStr);
+
+ bool nameIsGroup(const QString & name);
+ QString removeGroupTag(const QString & name);
+ QString removeQuotationMarks(const QString & name);
+ void addUserToUserTable(const QString & user, int accessRight);
+
+protected slots:
+ virtual void addUserBtnClicked();
+ virtual void addGroupBtnClicked();
+ virtual void removeSelectedBtnClicked();
+ virtual void expertBtnClicked();
+};
+
+#endif
diff --git a/filesharing/advanced/nfs/Makefile.am b/filesharing/advanced/nfs/Makefile.am
new file mode 100644
index 00000000..f9395529
--- /dev/null
+++ b/filesharing/advanced/nfs/Makefile.am
@@ -0,0 +1,13 @@
+METASOURCES = AUTO
+AM_CPPFLAGS= $(all_includes)
+
+noinst_LTLIBRARIES = libfilesharenfs.la
+
+libfilesharenfs_la_SOURCES = \
+ nfsdialog.cpp \
+ nfshostdlg.cpp \
+ hostprops.ui \
+ nfsdialoggui.ui \
+ nfsfile.cpp \
+ nfsentry.cpp
+
diff --git a/filesharing/advanced/nfs/hostprops.ui b/filesharing/advanced/nfs/hostprops.ui
new file mode 100644
index 00000000..4ff97e28
--- /dev/null
+++ b/filesharing/advanced/nfs/hostprops.ui
@@ -0,0 +1,558 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>HostProps</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HostProps</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>333</width>
+ <height>332</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>propertiesGrp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>180</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Host Properties</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name/address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>nameEdit</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Name / Address field&lt;/b&gt;
+&lt;p&gt;
+Here you can enter the host name or address.&lt;br&gt;
+The host may be specified in a number of ways:
+&lt;p&gt;
+&lt;i&gt;single host&lt;/i&gt;
+&lt;p&gt;
+ This is the most common format. You may specify a host either by an abbreviated name recognized by the resolver, the fully qualified domain name, or an IP address.
+&lt;/p&gt;
+
+&lt;i&gt;netgroups&lt;/i&gt;
+&lt;p&gt;
+ NIS netgroups may be given as @group. Only the host part of each netgroup members is consider in checking for membership. Empty host parts or those containing a single dash (-) are ignored.
+&lt;/p&gt;
+
+&lt;i&gt;wildcards&lt;/i&gt;
+&lt;p&gt;
+ Machine names may contain the wildcard characters * and ?. This can be used to make the exports file more compact; for instance, *.cs.foo.edu matches all hosts in the domain cs.foo.edu. However, these wildcard characters do not match the dots in a domain name, so the above pattern does not include hosts such as a.b.cs.foo.edu.
+&lt;/p&gt;
+
+&lt;i&gt;IP networks&lt;/i&gt;
+&lt;p&gt;
+ You can also export directories to all hosts on an IP (sub-) network simultaneously. This is done by specifying an IP address and netmask pair as address/netmask where the netmask can be specified in dotted-decimal format, or as a contiguous mask length (for example, either `/255.255.252.0' or `/22' appended to the network base address result in identical subnetworks with 10 bits of host).
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>publicChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Public access</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Public access&lt;/b&gt;
+&lt;p&gt;
+If you check this, the host address will be a single wildcard , which means public access.
+This is just the same as if you would enter a wildcard in the address field.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>GroupBox7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>readOnlyChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Writable</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Writable&lt;/b&gt;
+&lt;p&gt;
+Allow both read and write requests on this NFS volume.
+&lt;/p&gt;
+&lt;p&gt;
+The default is to disallow any request which changes the filesystem
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>secureChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Insecure</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Insecure&lt;/b&gt;
+&lt;p&gt;
+If this option is checked, it is not required that requests originate on an internet port less than IPPORT_RESERVED (1024).
+&lt;/p&gt;
+&lt;p&gt;
+If unsure leave it unchecked.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>syncChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Sync</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Sync&lt;/b&gt;
+&lt;p&gt;
+This option requests that all file writes be committed to disk before the write request completes. This is required for complete safety of data in the face of a server crash, but incurs a performance hit.
+&lt;/p&gt;
+&lt;p&gt;
+The default is to allow the server to write the data out whenever it is ready.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>wdelayChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>No w&amp;delay</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;No wdelay&lt;/b&gt;
+&lt;p&gt;
+This option only has effect if sync is also set. The NFS server will normally delay committing a write request to disk slightly if it suspects that another related write request may be in progress or may arrive soon. This allows multiple write requests to be committed to disk with the one operation which can improve performance. If an NFS server received mainly small unrelated requests, this behavior could actually reduce performance, so no wdelay is available to turn it off. &lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>hideChk</cstring>
+ </property>
+ <property name="text">
+ <string>No &amp;hide</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;No hide&lt;/b&gt;
+&lt;p&gt;
+This option is based on the option of the same name provided in IRIX NFS. Normally, if a server exports two filesystems one of which is mounted on the other, then the client will have to mount both filesystems explicitly to get access to them. If it just mounts the parent, it will see an empty directory at the place where the other filesystem is mounted. That filesystem is "hidden".
+&lt;/p&gt;
+&lt;p&gt;
+Setting the nohide option on a filesystem causes it not to be hidden, and an appropriately authorized client will be able to move from the parent to that filesystem without noticing the change.
+&lt;/p&gt;
+&lt;p&gt;
+However, some NFS clients do not cope well with this situation as, for instance, it is then possible for two files in the one apparent filesystem to have the same inode number.
+&lt;/p&gt;
+&lt;p&gt;
+The nohide option is currently only effective on single host exports. It does not work reliably with netgroup, subnet, or wildcard exports.
+&lt;/p&gt;
+&lt;p&gt;
+This option can be very useful in some situations, but it should be used with due care, and only after confirming that the client system copes with the situation effectively.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>subtreeChk</cstring>
+ </property>
+ <property name="text">
+ <string>No su&amp;btree check</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;No subtree check&lt;/b&gt;
+&lt;p&gt;
+This option disables subtree checking, which has mild security implications, but can improve reliability is some circumstances.
+&lt;/p&gt;
+&lt;p&gt;
+If a subdirectory of a filesystem is exported, but the whole filesystem is not, then whenever a NFS request arrives, the server must check not only that the accessed file is in the appropriate filesystem (which is easy) but also that it is in the exported tree (which is harder). This check is called the subtree_check.
+&lt;/p&gt;
+&lt;p&gt;
+In order to perform this check, the server must include some information about the location of the file in the "filehandle" that is given to the client. This can cause problems with accessing files that are renamed while a client has them open (though in many simple cases it will still work).
+&lt;/p&gt;
+&lt;p&gt;
+subtree checking is also used to make sure that files inside directories to which only root has access can only be accessed if the filesystem is exported with no_root_squash (see below), even the file itself allows more general access.
+&lt;/p&gt;
+&lt;p&gt;
+As a general guide, a home directory filesystem, which is normally exported at the root and may see lots of file renames, should be exported with subtree checking disabled. A filesystem which is mostly read-only, and at least does not see many file renames (e.g. /usr or /var) and for which subdirectories may be exported, should probably be exported with subtree checks enabled.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0">
+ <property name="name">
+ <cstring>secureLocksChk</cstring>
+ </property>
+ <property name="text">
+ <string>Insecure loc&amp;ks</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Insecure locks&lt;/b&gt;
+&lt;p&gt;
+This option tells the NFS server not to require authentication of locking requests (i.e. requests which use the NLM protocol). Normally the NFS server will require a lock request to hold a credential for a user who has read access to the file. With this flag no access checks will be performed.
+&lt;/p&gt;
+&lt;p&gt;
+Early NFS client implementations did not send credentials with lock requests, and many current NFS clients still exist which are based on the old implementations. Use this flag if you find that you can only lock files which are world readable.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>GroupBox3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>User Mapping</string>
+ </property>
+ <property name="alignment">
+ <set>AlignAuto</set>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allSquashChk</cstring>
+ </property>
+ <property name="text">
+ <string>All s&amp;quash</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;All squash&lt;/b&gt;
+&lt;p&gt;
+Map all uids and gids to the anonymous user. Useful for NFS-exported public FTP directories, news spool directories, etc. &lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>rootSquashChk</cstring>
+ </property>
+ <property name="text">
+ <string>No &amp;root squash</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;No root squash&lt;/b&gt;
+&lt;p&gt;
+Turn of root squashing. This option is mainly useful for diskless clients.
+&lt;/p&gt;
+&lt;i&gt;root squashing&lt;/i&gt;
+&lt;p&gt;
+Map requests from uid/gid 0 to the anonymous uid/gid. Note that this does not apply to any other uids that might be equally sensitive, such as user bin.
+&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Anonym. &amp;UID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>anonuidEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Anonym. UID/GID&lt;/b&gt; &lt;p&gt; These options explicitly set the uid and gid of the anonymous account. This option is primarily useful for PC/NFS clients, where you might want all requests appear to be from one user. &lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>anonuidEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>100</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>FF</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Anonym. &amp;GID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>anongidEdit</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;b&gt;Anonym. UID/GID&lt;/b&gt; &lt;p&gt; These options explicitly set the uid and gid of the anonymous account. This option is primarily useful for PC/NFS clients, where you might want all requests appear to be from one user. &lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>anongidEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>50</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>FF</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>publicChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nameEdit</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>readOnlyChk</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>secureChk</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>syncChk</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>wdelayChk</sender>
+ <signal>pressed()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>subtreeChk</sender>
+ <signal>pressed()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>secureLocksChk</sender>
+ <signal>pressed()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>allSquashChk</sender>
+ <signal>pressed()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>rootSquashChk</sender>
+ <signal>pressed()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>nameEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>hideChk</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>syncChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>wdelayChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>anonuidEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+ <connection>
+ <sender>anongidEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>HostProps</receiver>
+ <slot>setModified()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="local" impldecl="in implementation">hostprops.ui.h</include>
+</includes>
+<signals>
+ <signal>modified()</signal>
+</signals>
+<slots>
+ <slot>setModified()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/nfs/hostprops.ui.h b/filesharing/advanced/nfs/hostprops.ui.h
new file mode 100644
index 00000000..5d456a22
--- /dev/null
+++ b/filesharing/advanced/nfs/hostprops.ui.h
@@ -0,0 +1,13 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+
+void HostProps::setModified()
+{
+ emit modified();
+}
diff --git a/filesharing/advanced/nfs/nfsdialog.cpp b/filesharing/advanced/nfs/nfsdialog.cpp
new file mode 100644
index 00000000..7e88f2be
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsdialog.cpp
@@ -0,0 +1,216 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+#include <qradiobutton.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qlineedit.h>
+#include <qbuttongroup.h>
+#include <qlayout.h>
+
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kaccel.h>
+
+#include "nfsdialog.h"
+#include "nfsentry.h"
+#include "nfshostdlg.h"
+#include "nfsfile.h"
+#include "nfsdialoggui.h"
+
+NFSDialog::NFSDialog(QWidget * parent, NFSEntry* entry)
+ : KDialogBase(Plain, i18n("NFS Options"), Ok|Cancel, Ok, parent),
+ m_nfsEntry(entry),
+ m_modified(false)
+{
+ if (m_nfsEntry)
+ m_workEntry = m_nfsEntry->copy();
+ else
+ kdWarning() << "NFSDialog::NFSDialog: entry is null!" << endl;
+
+ initGUI();
+ initSlots();
+ initListView();
+}
+
+NFSDialog::~NFSDialog()
+{
+ delete m_workEntry;
+}
+
+void NFSDialog::initGUI() {
+ QWidget* page = plainPage();
+ m_gui = new NFSDialogGUI(page);
+
+ QVBoxLayout *layout = new QVBoxLayout( page );
+ layout->addWidget( m_gui );
+
+ KAccel* accel = new KAccel( m_gui->listView );
+ accel->insert( "Delete", Qt::Key_Delete, this, SLOT(slotRemoveHost()));
+}
+
+void NFSDialog::initSlots()
+{
+ connect( m_gui->removeHostBtn, SIGNAL(clicked()), this, SLOT( slotRemoveHost()));
+ connect( m_gui->addHostBtn, SIGNAL(clicked()), this, SLOT( slotAddHost()));
+ connect( m_gui->modifyHostBtn, SIGNAL(clicked()), this, SLOT( slotModifyHost()));
+ connect( m_gui->listView, SIGNAL(doubleClicked(QListViewItem*)),
+ this, SLOT( slotModifyHost()));
+
+}
+
+void NFSDialog::initListView()
+{
+ if (m_workEntry) {
+ HostIterator it = m_workEntry->getHosts();
+
+ NFSHost* host;
+ while ( (host = it.current()) != 0 ) {
+ ++it;
+ createItemFromHost(host);
+ }
+ }
+}
+
+QListViewItem* NFSDialog::createItemFromHost(NFSHost* host)
+{
+ if (!host)
+ return 0;
+
+ QListViewItem* item = new QListViewItem(m_gui->listView);
+ updateItem(item, host);
+ return item;
+}
+
+void NFSDialog::updateItem(QListViewItem* item, NFSHost* host)
+{
+ item->setText(0,host->name);
+ item->setText(1,host->paramString());
+}
+
+void NFSDialog::slotAddHost()
+{
+ NFSHost *host = new NFSHost();
+
+ // Set some secure parameters
+ //host->allSquash=true;
+ host->readonly=true;
+
+ HostList hostList;
+ hostList.append(host);
+
+ NFSHostDlg *dlg = new NFSHostDlg(this, &hostList, m_workEntry);
+ dlg->exec();
+
+
+ if (dlg->result()==QDialog::Accepted) {
+ m_workEntry->addHost(host);
+ createItemFromHost(host);
+ setModified();
+ } else {
+ delete host;
+ }
+
+ delete dlg;
+}
+
+void NFSDialog::slotOk() {
+ if (m_modified) {
+ m_nfsEntry->copyFrom(m_workEntry);
+ }
+ KDialogBase::slotOk();
+}
+
+void NFSDialog::slotRemoveHost()
+{
+ QPtrList<QListViewItem> items = m_gui->listView->selectedItems();
+ if (items.count()==0)
+ return;
+
+ QListViewItem *item;
+ for ( item = items.first(); item; item = items.next() ) {
+ QString name = item->text(0);
+ m_gui->listView->takeItem(item);
+
+ NFSHost* host = m_workEntry->getHostByName(name);
+ if (host) {
+ m_workEntry->removeHost(host);
+ } else {
+ kdWarning() << "NFSDialog::slotRemoveHost: no host "
+ << name << " << found!" << endl;
+ }
+
+ }
+
+ m_gui->modifyHostBtn->setDisabled(true);
+ m_gui->removeHostBtn->setDisabled(true);
+ setModified();
+}
+
+void NFSDialog::slotModifyHost()
+{
+ QPtrList<QListViewItem> items = m_gui->listView->selectedItems();
+ if (items.count()==0)
+ return;
+
+ HostList hostList;
+
+ QListViewItem *item;
+ for ( item = items.first(); item; item = items.next() ) {
+
+ NFSHost* host = m_workEntry->getHostByName(item->text(0));
+ if (host)
+ hostList.append(host);
+ else
+ kdWarning() << "NFSDialog::slogModifyHost: host "
+ << item->text(0) << " is null!" << endl;
+ }
+
+ NFSHostDlg *dlg = new NFSHostDlg(this, &hostList, m_workEntry);
+ if (dlg->exec() == QDialog::Accepted &&
+ dlg->isModified())
+ {
+ setModified();
+ }
+
+ delete dlg;
+
+ NFSHost* host = hostList.first();
+ for ( item = items.first(); item; item = items.next() ) {
+ if (item && host)
+ updateItem( item,host);
+ host = hostList.next();
+ }
+}
+
+void NFSDialog::setModified()
+{
+ m_modified = true;
+}
+
+bool NFSDialog::modified() {
+ return m_modified;
+}
+
+#include "nfsdialog.moc"
+
diff --git a/filesharing/advanced/nfs/nfsdialog.h b/filesharing/advanced/nfs/nfsdialog.h
new file mode 100644
index 00000000..f6128230
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsdialog.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef NFSDIALOG_H
+#define NFSDIALOG_H
+
+#include <kdialogbase.h>
+
+class NFSEntry;
+class NFSHost;
+class QListViewItem;
+class NFSFile;
+class NFSDialogGUI;
+
+class NFSDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ NFSDialog(QWidget * parent, NFSEntry* entry);
+ ~NFSDialog();
+ bool modified();
+protected:
+ NFSEntry * m_nfsEntry;
+ NFSEntry * m_workEntry;
+ NFSDialogGUI* m_gui;
+
+ bool m_modified;
+ QListViewItem* createItemFromHost(NFSHost* host);
+ void updateItem(QListViewItem* item, NFSHost* host);
+ void initGUI();
+ void initListView();
+ void initSlots();
+ void initWorkEntry();
+protected slots:
+ void slotAddHost();
+ void slotRemoveHost();
+ void slotModifyHost();
+ virtual void slotOk();
+ void setModified();
+};
+
+#endif
diff --git a/filesharing/advanced/nfs/nfsdialoggui.ui b/filesharing/advanced/nfs/nfsdialoggui.ui
new file mode 100644
index 00000000..002ae30a
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsdialoggui.ui
@@ -0,0 +1,156 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>NFSDialogGUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>NFSDialogGUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>466</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Allowed &amp;Hosts</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A list of allowed hosts</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see a list of hosts which are allowed to access this directory via NFS.
+The first column shows the name or address of the host, the second column shows the access parameters. The name '*' donates public access.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>addHostBtn</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add Host...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>modifyHostBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Mo&amp;dify Host...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>removeHostBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Remove Host</string>
+ </property>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KListView" row="0" column="0" rowspan="4" colspan="1">
+ <column>
+ <property name="text">
+ <string>Name/Address</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Parameters</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>listView</cstring>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Extended</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>listView</sender>
+ <signal>selectionChanged()</signal>
+ <receiver>NFSDialogGUI</receiver>
+ <slot>listView_selectionChanged()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>addHostBtn</tabstop>
+ <tabstop>modifyHostBtn</tabstop>
+ <tabstop>removeHostBtn</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">nfsdialoggui.ui.h</include>
+</includes>
+<slots>
+ <slot>listView_selectionChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/nfs/nfsdialoggui.ui.h b/filesharing/advanced/nfs/nfsdialoggui.ui.h
new file mode 100644
index 00000000..85894340
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsdialoggui.ui.h
@@ -0,0 +1,18 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename slots use Qt Designer which will
+** update this file, preserving your code. Create an init() slot in place of
+** a constructor, and a destroy() slot in place of a destructor.
+*****************************************************************************/
+
+
+
+
+
+void NFSDialogGUI::listView_selectionChanged()
+{
+ bool empty = listView->selectedItems().isEmpty();
+ modifyHostBtn->setDisabled(empty);
+ removeHostBtn->setDisabled(empty);
+}
diff --git a/filesharing/advanced/nfs/nfsentry.cpp b/filesharing/advanced/nfs/nfsentry.cpp
new file mode 100644
index 00000000..4144cc7c
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsentry.cpp
@@ -0,0 +1,381 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <kdebug.h>
+
+#include "nfsentry.h"
+
+NFSHost::NFSHost(const QString & hostString)
+{
+ readonly = true;
+
+ QString s = hostString;
+
+ int l = s.find('(');
+ int r = s.find(')');
+
+ initParams();
+
+ // get hostname
+ if (l>=0)
+ name = s.left(l);
+ else
+ name = s;
+
+ kdDebug(5009) << "NFSHost: name='" << name << "'" << endl;
+
+ if (l>=0 && r>=0)
+ {
+ QString params = s.mid(l+1,r-l-1);
+
+ parseParamsString(params);
+ }
+}
+
+NFSHost::NFSHost() {
+ initParams();
+ name="";
+}
+
+NFSHost::~NFSHost()
+{
+}
+
+/**
+ * Set the parameters to their default values
+ */
+void NFSHost::initParams()
+{
+ readonly = true;
+ sync = false;
+ secure = true;
+ wdelay = true;
+ hide = true;
+ subtreeCheck = true;
+ secureLocks = true;
+ allSquash = false;
+ rootSquash = true;
+
+ anonuid = 65534;
+ anongid = 65534;
+}
+
+
+void NFSHost::parseParamsString(const QString & s)
+{
+
+ if (s.isEmpty())
+ return;
+
+ int i;
+
+ QString rest = s;
+ QString p;
+
+ do
+ {
+ i = rest.find(",",0);
+
+ if (i==-1)
+ p = rest;
+ else
+ {
+ p = rest.left( i );
+ rest = rest.mid(i+1);
+ }
+
+ setParam(p);
+ }
+ while (i>-1);
+
+}
+
+QString NFSHost::paramString() const
+{
+ QString s;
+
+ if (!readonly) s+="rw,";
+ if (!rootSquash) s+="no_root_squash,";
+ if (!secure) s+="insecure,";
+ if (!secureLocks) s+="insecure_locks,";
+ if (!subtreeCheck) s+="no_subtree_check,";
+ if (sync)
+ s+="sync,";
+ else
+ s+="async,";
+
+ if (!wdelay) s+="wdelay,";
+ if (allSquash) s+="all_squash,";
+ if (!hide) s+="nohide,";
+
+ if (anongid!=65534)
+ s+=QString("anongid=%1,").arg(anongid);
+
+ if (anonuid!=65534)
+ s+=QString("anonuid=%1,").arg(anonuid);
+
+ // get rid of the last ','
+ s.truncate(s.length()-1);
+
+ return s;
+}
+
+QString NFSHost::toString() const
+{
+ QString s = name;
+
+ s+='(';
+ s+=paramString();
+ s+=')';
+
+ return s;
+
+}
+
+NFSHost* NFSHost::copy() const {
+ NFSHost* result = new NFSHost();
+
+ result->name = name;
+
+ result->readonly = readonly;
+ result->sync = sync;
+ result->secure = secure;
+ result->wdelay = wdelay;
+ result->hide = hide;
+ result->subtreeCheck = subtreeCheck;
+ result->secureLocks = secureLocks;
+ result->allSquash = allSquash;
+ result->rootSquash = rootSquash;
+
+ result->anonuid = anonuid;
+ result->anongid = anongid;
+
+ return result;
+}
+
+bool NFSHost::isPublic() const {
+ return name == "*";
+}
+
+void NFSHost::setParam(const QString & s)
+{
+ QString p = s.lower();
+
+ if (p=="ro") {
+ readonly = true;
+ return; }
+
+ if (p=="rw") {
+ readonly = false;
+ return; }
+
+ if (p=="sync") {
+ sync = true;
+ return; }
+
+ if (p=="async") {
+ sync = false;
+ return; }
+
+ if (p=="secure") {
+ secure = true;
+ return; }
+
+ if (p=="insecure") {
+ secure = false;
+ return; }
+
+ if (p=="wdelay") {
+ wdelay = true;
+ return; }
+
+ if (p=="no_wdelay") {
+ wdelay = false;
+ return; }
+
+ if (p=="hide") {
+ hide = true;
+ return; }
+
+ if (p=="nohide") {
+ hide = false;
+ return; }
+
+ if (p=="subtree_check") {
+ subtreeCheck = true;
+ return; }
+
+ if (p=="no_subtree_check") {
+ subtreeCheck = false;
+ return; }
+
+ if (p=="secure_locks" ||
+ p=="auth_nlm") {
+ secureLocks = true;
+ return; }
+
+ if (p=="insecure_locks" ||
+ p=="no_auth_nlm" ) {
+ secureLocks = true;
+ return; }
+
+ if (p=="all_squash") {
+ allSquash = true;
+ return; }
+
+ if (p=="no_all_squash") {
+ allSquash = false;
+ return; }
+
+ if (p=="root_squash") {
+ rootSquash = true;
+ return; }
+
+ if (p=="no_root_squash") {
+ rootSquash = false;
+ return; }
+
+ int i = p.find("=",0);
+
+ // get anongid or anonuid
+ if (i>-1)
+ {
+ QString name = p.left(i).lower();
+ kdDebug(5009) << name << endl;
+
+ QString value = p.mid(i+1);
+ kdDebug(5009) << value << endl;
+
+ if (name=="anongid")
+ anongid = value.toInt();
+
+ if (name=="anonuid")
+ anonuid = value.toInt();
+ }
+
+}
+
+NFSEntry::NFSEntry(const QString & path)
+{
+ _hosts.setAutoDelete(true);
+ setPath(path);
+}
+
+NFSEntry::~NFSEntry()
+{
+}
+
+void NFSEntry::clear() {
+ _hosts.clear();
+}
+
+NFSEntry* NFSEntry::copy() {
+ NFSEntry* result = new NFSEntry(path());
+ result->copyFrom(this);
+ return result;
+}
+
+void NFSEntry::copyFrom(NFSEntry* entry) {
+ clear();
+ HostIterator it = entry->getHosts();
+
+ NFSHost* host;
+ while ( (host = it.current()) != 0 ) {
+ ++it;
+ addHost(host->copy());
+ }
+}
+
+QString NFSEntry::toString() const
+{
+ QString s = _path.stripWhiteSpace();
+
+ if (_path.find(' ') > -1) {
+ s = '"'+s+'"';
+ }
+
+ s += ' ';
+
+ HostIterator it = getHosts();
+
+ NFSHost* host;
+
+ while ( (host = it.current()) != 0 )
+ {
+ ++it;
+ s+= host->toString() ;
+ if (it.current())
+ s+= " \\\n\t ";
+ }
+
+
+ return s;
+}
+
+void NFSEntry::addHost(NFSHost * host)
+{
+ _hosts.append(host);
+}
+
+void NFSEntry::removeHost(NFSHost * host)
+{
+ _hosts.remove(host);
+}
+
+NFSHost* NFSEntry::getHostByName(const QString & name) const
+{
+ HostIterator it = getHosts();
+ NFSHost* host;
+
+ while ( (host = it.current()) != 0 )
+ {
+ ++it;
+
+ if (host->name==name)
+ return host;
+ }
+
+ return 0;
+}
+
+NFSHost* NFSEntry::getPublicHost() const
+{
+ NFSHost* result = getHostByName("*");
+ if (result)
+ return result;
+
+ return getHostByName(QString::null);
+}
+
+
+HostIterator NFSEntry::getHosts() const
+{
+ return HostIterator(_hosts);
+}
+
+QString NFSEntry::path() const
+{
+ return _path;
+}
+
+void NFSEntry::setPath(const QString & path)
+{
+ _path = path;
+}
+
diff --git a/filesharing/advanced/nfs/nfsentry.h b/filesharing/advanced/nfs/nfsentry.h
new file mode 100644
index 00000000..6003412d
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsentry.h
@@ -0,0 +1,120 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef NFSENTRY_H
+#define NFSENTRY_H
+
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+
+class NFSHost
+{
+public:
+ NFSHost(const QString & hostString);
+ ~NFSHost();
+ NFSHost();
+
+ bool readonly;
+ bool sync;
+ bool secure;
+ bool wdelay;
+ bool hide;
+ bool subtreeCheck;
+ bool secureLocks;
+ bool allSquash;
+ bool rootSquash;
+
+ int anonuid;
+ int anongid;
+
+ QString name;
+
+ QString paramString() const;
+ QString toString() const;
+ bool isPublic() const;
+
+ /** returns a copy of this host */
+ NFSHost* copy() const;
+protected:
+ void parseParamsString(const QString &);
+ void setParam(const QString &);
+ void initParams();
+};
+
+
+
+typedef QPtrList<NFSHost> HostList;
+typedef QPtrListIterator<NFSHost> HostIterator;
+
+class NFSLine {
+public:
+ virtual QString toString() const = 0;
+};
+
+typedef QPtrList<NFSLine> NFSLineList;
+typedef QPtrListIterator<NFSLine> NFSLineIterator;
+
+class NFSEmptyLine : public NFSLine {
+public:
+ virtual QString toString() const { return QString::fromLatin1("\n"); }
+ virtual ~NFSEmptyLine() {};
+};
+
+class NFSComment : public NFSLine {
+public:
+ NFSComment(const QString & s) { comment = s; }
+ virtual ~NFSComment() {};
+ QString comment;
+ virtual QString toString() const { return comment; }
+};
+
+class NFSEntry : public NFSLine {
+public:
+ NFSEntry(const QString & path);
+ virtual ~NFSEntry();
+
+ void addHost(NFSHost * host);
+ void removeHost(NFSHost * host);
+ NFSHost* getHostByName(const QString & name) const;
+ NFSHost* getPublicHost() const;
+ HostIterator getHosts() const;
+
+ /** Creates a deep copy of this entry */
+ NFSEntry * copy();
+
+ /** clears all hosts and makes a deep copy of
+ * the given entry
+ */
+ void copyFrom(NFSEntry* entry);
+
+ /** removes all hosts */
+ void clear();
+ QString path() const;
+ void setPath(const QString &);
+
+ virtual QString toString() const;
+
+protected:
+ HostList _hosts;
+ QString _path;
+};
+
+#endif
diff --git a/filesharing/advanced/nfs/nfsfile.cpp b/filesharing/advanced/nfs/nfsfile.cpp
new file mode 100644
index 00000000..229eb983
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsfile.cpp
@@ -0,0 +1,261 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <qfileinfo.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qstringlist.h>
+
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <knfsshare.h>
+#include <ktempfile.h>
+#include <kprocio.h>
+
+#include "nfsfile.h"
+
+NFSFile::NFSFile(const KURL & url, bool readonly)
+{
+ _lines.setAutoDelete(true);
+ _entries.setAutoDelete(false);
+ _url = url;
+ _readonly = readonly;
+}
+
+NFSFile::~NFSFile()
+{
+}
+
+void NFSFile::addEntry(NFSEntry *entry)
+{
+ _lines.append(entry);
+ _entries.append(entry);
+}
+
+void NFSFile::removeEntry(NFSEntry *entry)
+{
+ _entries.remove(entry);
+ _lines.remove(entry);
+}
+
+bool NFSFile::hasEntry(NFSEntry *entry)
+{
+ return 0 < _entries.contains(entry);
+}
+
+
+NFSEntry* NFSFile::getEntryByPath(const QString & path)
+{
+ QString testPath = path.stripWhiteSpace();
+ if ( testPath[testPath.length()-1] != '/' )
+ testPath += '/';
+
+ for (NFSEntry* entry = _entries.first(); entry; entry = _entries.next())
+ {
+ if (entry->path()==testPath)
+ return entry;
+ }
+
+ return 0L;
+}
+
+bool NFSFile::removeEntryByPath(const QString & path) {
+ NFSEntry* entry = getEntryByPath(path);
+ if (!entry)
+ return false;
+
+ removeEntry(entry);
+ return true;
+}
+
+EntryIterator NFSFile::getEntries()
+{
+ return EntryIterator(_entries);
+}
+
+
+
+
+bool NFSFile::load()
+{
+ QFile f(_url.path());
+
+ if ( !f.open(IO_ReadOnly) ) {
+ kdError() << "NFSFile::load: Could not open " << _url.path() << endl;
+ return false;
+ }
+
+ _entries.clear();
+ _lines.clear();
+
+ QTextStream s( &f );
+
+ bool continuedLine = false; // is true if the line before ended with a backslash
+ QString completeLine;
+
+ while ( !s.eof() )
+ {
+ QString currentLine = s.readLine().stripWhiteSpace();
+
+ if (continuedLine) {
+ completeLine += currentLine;
+ continuedLine = false;
+ }
+ else
+ completeLine = currentLine;
+
+ // is the line continued in the next line ?
+ if ( completeLine[completeLine.length()-1] == '\\' )
+ {
+ continuedLine = true;
+ // remove the ending backslash
+ completeLine.truncate( completeLine.length()-1 );
+ continue;
+ }
+
+ // empty lines
+ if (completeLine.isEmpty()) {
+ _lines.append(new NFSEmptyLine());
+ continue;
+ }
+
+ // comments
+ if ('#' == completeLine[0]) {
+ _lines.append(new NFSComment(completeLine));
+ continue;
+ }
+
+ QString path;
+ QString hosts;
+
+ // Handle quotation marks
+ if ( completeLine[0] == '"' ) {
+ int i = completeLine.find('"',1);
+ if (i == -1) {
+ kdError() << "NFSFile: Parse error: Missing quotation mark: "
+ << completeLine << endl;
+ continue;
+ }
+ path = completeLine.mid(1,i-1);
+ hosts = completeLine.mid(i+1);
+
+ } else { // no quotation marks
+ int i = completeLine.find(' ');
+ if (i == -1)
+ i = completeLine.find('\t');
+
+ if (i == -1)
+ path = completeLine;
+ else {
+ path = completeLine.left(i);
+ hosts = completeLine.mid(i+1).stripWhiteSpace();
+ }
+ }
+
+ // normalize path
+ if ( path[path.length()-1] != '/' )
+ path += '/';
+
+ kdDebug(5009) << "KNFSShare: Found path: '" << path << "'" << endl;
+ NFSEntry *entry = new NFSEntry(path);
+ QStringList hostList = QStringList::split(' ', hosts);
+
+ if (hostList.isEmpty()) {
+ NFSHost* host = new NFSHost("*");
+ entry->addHost(host);
+ } else {
+ QStringList::iterator it;
+ for ( it = hostList.begin(); it != hostList.end(); ++it ) {
+ NFSHost* host = new NFSHost((*it).stripWhiteSpace());
+ entry->addHost(host);
+ kdDebug(5009) << "KNFSShare: Found host: " << (*it) << " name='"
+ << host->name << "'" << endl;
+ }
+ }
+
+ kdDebug(5009) << "KNFSShare: Found hosts: " << hosts << "'" << endl;
+ this->addEntry(entry);
+ }
+
+ f.close();
+
+
+ return true;
+
+}
+
+void NFSFile::saveTo(QTextStream * stream) {
+ QPtrListIterator<NFSLine> it(_lines);
+
+ NFSLine *line;
+ while ( (line = it.current()) != 0 ) {
+ ++it;
+ *stream << line->toString() << endl;
+ }
+}
+
+bool NFSFile::saveTo(const QString& fileName) {
+ QFile file(fileName);
+ if (!file.open(IO_WriteOnly))
+ return false;
+
+ QTextStream stream(&file);
+ saveTo(&stream);
+ file.close();
+ return true;
+}
+
+bool NFSFile::save()
+{
+ if (QFileInfo(_url.path()).isWritable() ) {
+ saveTo(_url.path());
+ } else
+ {
+
+ KTempFile tempFile;
+ saveTo(tempFile.name());
+ tempFile.close();
+ tempFile.setAutoDelete( true );
+
+ KProcIO proc;
+
+ QString command = QString("cp %1 %2")
+ .arg(KProcess::quote( tempFile.name() ))
+ .arg(KProcess::quote( _url.path() ));
+
+ if (restartNFSServer)
+ command +=";exportfs -ra";
+
+ if (!QFileInfo(_url.path()).isWritable() )
+ proc<<"kdesu" << "-d" << "-c"<<command;
+
+ if (!proc.start(KProcess::Block, true)) {
+ return false;
+ }
+
+
+ }
+ return true;
+}
+
diff --git a/filesharing/advanced/nfs/nfsfile.h b/filesharing/advanced/nfs/nfsfile.h
new file mode 100644
index 00000000..3d493a04
--- /dev/null
+++ b/filesharing/advanced/nfs/nfsfile.h
@@ -0,0 +1,59 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef NFSFILE_H
+#define NFSFILE_H
+
+
+#include <kurl.h>
+#include "nfsentry.h"
+
+typedef QPtrList<NFSEntry> EntryList;
+typedef QPtrListIterator<NFSEntry> EntryIterator;
+
+class QTextStream;
+
+class NFSFile {
+public:
+ NFSFile(const KURL & , bool readonly = true);
+ ~NFSFile();
+
+ void addEntry(NFSEntry *);
+ void removeEntry(NFSEntry *);
+ bool hasEntry(NFSEntry *);
+ NFSEntry* getEntryByPath(const QString &);
+ bool removeEntryByPath(const QString &);
+ EntryIterator getEntries();
+
+ bool save();
+ bool saveTo(const QString& fileName);
+ void saveTo(QTextStream * stream);
+ bool load();
+
+ bool restartNFSServer;
+
+
+protected:
+ KURL _url;
+ bool _readonly;
+ EntryList _entries;
+ NFSLineList _lines;
+};
+
+#endif
diff --git a/filesharing/advanced/nfs/nfshostdlg.cpp b/filesharing/advanced/nfs/nfshostdlg.cpp
new file mode 100644
index 00000000..073170d6
--- /dev/null
+++ b/filesharing/advanced/nfs/nfshostdlg.cpp
@@ -0,0 +1,226 @@
+/***************************************************************************
+ nfshostdlg.cpp - description
+ -------------------
+ begin : Mon Apr 29 2002
+ copyright : (C) 2002 by Jan Sch�er
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qcheckbox.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qwhatsthis.h>
+#include <qgroupbox.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include <assert.h>
+
+#include "nfshostdlg.h"
+#include "hostprops.h"
+#include "nfsentry.h"
+
+
+NFSHostDlg::NFSHostDlg(QWidget* parent, HostList* hosts, NFSEntry* entry)
+ : KDialogBase(Plain, i18n("Host Properties"), Ok|Cancel, Ok, parent),
+ m_hosts(hosts), m_nfsEntry(entry), m_modified(false)
+{
+
+ QWidget* page = plainPage();
+
+ m_gui = new HostProps(page);
+
+ QVBoxLayout *layout = new QVBoxLayout( page, 0, 6 );
+ layout->addWidget( m_gui );
+
+ connect( m_gui, SIGNAL(modified()), this, SLOT(setModified()));
+
+ init();
+
+}
+
+NFSHostDlg::~NFSHostDlg()
+{
+}
+
+void NFSHostDlg::init()
+{
+ if (m_hosts->count()==1) {
+ NFSHost* host = m_hosts->first();
+ assert(host);
+ m_gui->nameEdit->setText(host->name);
+ m_gui->publicChk->setChecked(host->isPublic());
+
+ setHostValues(host);
+
+ m_gui->nameEdit->setFocus();
+ } else {
+ m_gui->nameEdit->setDisabled(true);
+ m_gui->publicChk->setDisabled(true);
+
+ m_gui->readOnlyChk->setTristate();
+ m_gui->allSquashChk->setTristate();
+ m_gui->rootSquashChk->setTristate();
+ m_gui->hideChk->setTristate();
+ m_gui->secureChk->setTristate();
+ m_gui->secureLocksChk->setTristate();
+ m_gui->subtreeChk->setTristate();
+ m_gui->syncChk->setTristate();
+ m_gui->wdelayChk->setTristate();
+
+ for (NFSHost* host = m_hosts->first(); host; host = m_hosts->next()) {
+ setHostValues(host);
+ }
+ }
+}
+
+void NFSHostDlg::setHostValues(NFSHost* host) {
+ setCheckBoxValue(m_gui->readOnlyChk, ! host->readonly);
+ setCheckBoxValue(m_gui->allSquashChk, host->allSquash);
+ setCheckBoxValue(m_gui->rootSquashChk, ! host->rootSquash);
+ setCheckBoxValue(m_gui->hideChk, ! host->hide);
+ setCheckBoxValue(m_gui->secureChk, ! host->secure);
+ setCheckBoxValue(m_gui->secureLocksChk, ! host->secureLocks);
+ setCheckBoxValue(m_gui->subtreeChk, ! host->subtreeCheck);
+ setCheckBoxValue(m_gui->syncChk, host->sync);
+ setCheckBoxValue(m_gui->wdelayChk, ! host->wdelay);
+
+ setEditValue(m_gui->anonuidEdit,QString::number(host->anonuid));
+ setEditValue(m_gui->anongidEdit,QString::number(host->anongid));
+}
+
+void NFSHostDlg::setEditValue(QLineEdit* edit, const QString & value) {
+ if (edit->text().isEmpty())
+ return;
+
+ if (edit->text() == "FF")
+ edit->setText(value);
+ else
+ if (edit->text() != value)
+ edit->setText(QString::null);
+}
+
+void NFSHostDlg::setCheckBoxValue(QCheckBox* chk, bool value) {
+ if (chk->state() == QButton::NoChange)
+ return;
+
+ if (chk->isChecked()) {
+ if (! value)
+ chk->setNoChange();
+ } else {
+ if (value)
+ chk->setChecked(true);
+ }
+}
+
+
+void NFSHostDlg::slotOk()
+{
+ if (m_hosts->count()==1) {
+ NFSHost* host = m_hosts->first();
+ if (! saveName(host))
+ return;
+
+ saveValues(host);
+ } else {
+ for (NFSHost* host = m_hosts->first(); host; host = m_hosts->next()) {
+ saveValues(host);
+ }
+ }
+
+ KDialogBase::slotOk();
+}
+
+bool NFSHostDlg::saveName(NFSHost* host) {
+ if (m_gui->publicChk->isChecked()) {
+ NFSHost* publicHost = m_nfsEntry->getPublicHost();
+ if (publicHost && publicHost != host) {
+ KMessageBox::sorry(this,i18n("There already exists a public entry."),
+ i18n("Host Already Exists"));
+ m_gui->publicChk->setChecked(false);
+ return false;
+ }
+ host->name="*";
+ } else {
+ QString name = m_gui->nameEdit->text().stripWhiteSpace();
+ if (name.isEmpty()) {
+ KMessageBox::sorry(this,
+ i18n("Please enter a hostname or an IP address.").arg(name),
+ i18n("No Hostname/IP-Address"));
+ m_gui->nameEdit->setFocus();
+ return false;
+ } else {
+ NFSHost* host2 = m_nfsEntry->getHostByName(name);
+ if (host2 && host2 != host) {
+ KMessageBox::sorry(this,i18n("The host '%1' already exists.").arg(name),
+ i18n("Host Already Exists"));
+ m_gui->nameEdit->setFocus();
+ return false;
+ }
+ }
+ host->name=name;
+ }
+
+ return true;
+}
+
+
+void NFSHostDlg::saveValues(NFSHost* host) {
+
+ saveCheckBoxValue(host->readonly,m_gui->readOnlyChk,true);
+ saveCheckBoxValue(host->allSquash,m_gui->allSquashChk,false);
+ saveCheckBoxValue(host->rootSquash,m_gui->rootSquashChk,true);
+ saveCheckBoxValue(host->hide,m_gui->hideChk,true);
+ saveCheckBoxValue(host->secure,m_gui->secureChk,true);
+ saveCheckBoxValue(host->secureLocks,m_gui->secureLocksChk,true);
+ saveCheckBoxValue(host->subtreeCheck,m_gui->subtreeChk,true);
+ saveCheckBoxValue(host->sync,m_gui->syncChk,false);
+ saveCheckBoxValue(host->wdelay,m_gui->wdelayChk,true);
+
+ saveEditValue(host->anonuid,m_gui->anonuidEdit);
+ saveEditValue(host->anongid,m_gui->anongidEdit);
+}
+
+void NFSHostDlg::saveEditValue(int & value, QLineEdit* edit) {
+ if ( edit->text().isEmpty())
+ return;
+
+ value = edit->text().toInt();
+}
+
+void NFSHostDlg::saveCheckBoxValue(bool & value, QCheckBox* chk, bool neg) {
+ if (chk->state() == QButton::NoChange)
+ return;
+
+ if (neg)
+ value = ! chk->isChecked();
+ else
+ value = chk->isChecked();
+}
+
+
+
+bool NFSHostDlg::isModified() {
+ return m_modified;
+}
+
+
+void NFSHostDlg::setModified()
+{
+ m_modified = true;
+}
+
+#include "nfshostdlg.moc"
+
+
diff --git a/filesharing/advanced/nfs/nfshostdlg.h b/filesharing/advanced/nfs/nfshostdlg.h
new file mode 100644
index 00000000..6580b2bd
--- /dev/null
+++ b/filesharing/advanced/nfs/nfshostdlg.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ nfshostdlg.h - description
+ -------------------
+ begin : Mon Apr 29 2002
+ copyright : (C) 2002 by Jan Schäfer
+ email : janschaefer@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 NFSHOSTDLG_H
+#define NFSHOSTDLG_H
+
+#include <kdialogbase.h>
+#include "nfsentry.h"
+
+class NFSHost;
+class HostProps;
+class NFSEntry;
+class QCheckBox;
+class QLineEdit;
+
+
+class NFSHostDlg : public KDialogBase
+{
+Q_OBJECT
+public:
+ NFSHostDlg(QWidget* parent, HostList* hosts, NFSEntry* entry);
+ virtual ~NFSHostDlg();
+ bool isModified();
+protected:
+ HostList* m_hosts;
+ NFSEntry* m_nfsEntry;
+ HostProps* m_gui;
+ bool m_modified;
+
+ void init();
+protected slots:
+ virtual void slotOk();
+ void setModified();
+
+private:
+ void setHostValues(NFSHost* host);
+ void setEditValue(QLineEdit* edit, const QString & value);
+ void setCheckBoxValue(QCheckBox* chk, bool value);
+ bool saveName(NFSHost* host);
+ void saveValues(NFSHost* host);
+ void saveEditValue(int & value, QLineEdit* edit);
+ void saveCheckBoxValue(bool & value, QCheckBox* chk, bool neg);
+};
+
+#endif
diff --git a/filesharing/advanced/propsdlgplugin/Makefile.am b/filesharing/advanced/propsdlgplugin/Makefile.am
new file mode 100644
index 00000000..7322f0a8
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/Makefile.am
@@ -0,0 +1,25 @@
+METASOURCES = AUTO
+
+
+kde_module_LTLIBRARIES = fileshare_propsdlgplugin.la
+
+fileshare_propsdlgplugin_la_SOURCES = \
+ propsdlgshareplugin.cpp
+
+fileshare_propsdlgplugin_la_COMPILE_FIRST = propertiespagegui.h
+
+fileshare_propsdlgplugin_la_LIBADD = \
+ libpropsdlgplugin_common.la \
+ ../nfs/libfilesharenfs.la \
+ ../kcm_sambaconf/libfilesharesamba.la \
+ $(LIB_KIO)
+
+fileshare_propsdlgplugin_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+
+AM_CPPFLAGS = -I../kcm_sambaconf $(all_includes)
+
+kde_services_DATA = fileshare_propsdlgplugin.desktop
+
+# Shared with the 'simple' kcm
+noinst_LTLIBRARIES = libpropsdlgplugin_common.la
+libpropsdlgplugin_common_la_SOURCES = propertiespage.cpp propertiespagegui.ui
diff --git a/filesharing/advanced/propsdlgplugin/fileshare_propsdlgplugin.desktop b/filesharing/advanced/propsdlgplugin/fileshare_propsdlgplugin.desktop
new file mode 100644
index 00000000..f6064ed5
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/fileshare_propsdlgplugin.desktop
@@ -0,0 +1,102 @@
+[Desktop Entry]
+Type=Service
+Icon=fileshare
+Name=Fileshare Konqueror Directory Properties Page
+Name[bg]=ÐаÑтройки за приÑтавката за браузъра за ÑподелÑна на файлове
+Name[bn]=ফাইল ভাগাভাগি কনকরার ডিরেকà§à¦Ÿà¦°à§€ বৈশিষà§à¦Ÿà§à¦¯à¦¾à¦¬à¦²à§€ পাতা
+Name[bs]=Stranica sa osobinama dijeljenja direktorija u Konqueroru
+Name[ca]=Pàgina de propietats de compartició de fitxers Konqueror
+Name[cs]=Stránka vlastností adresáře sdílení Konqueroru
+Name[da]=Fileshare Konqueror-mappens side med egenskaber
+Name[de]=Dateifreigabe Konqueror Ordner-Eigenschaftenseite
+Name[el]=Σελίδα ιδιοτήτων κοινόχÏηστου καταλόγου του Konqueror
+Name[eo]=dosierujo-eco-paÄo
+Name[es]=Página de propiedades del directorio para compartir archivos de Konqueror
+Name[et]=Failijagamise Konquerori kataloogi omaduste lehekülg
+Name[eu]=Fitxategiak partekatzeko Konqueror direktorio propietateen orria
+Name[fa]=صÙØ­Û€ ویژگیهای Ùهرست راهنمای اشتراک پروندۀ Konqueror
+Name[fi]=Konquerorin tiedostojakojen ominaisuussivu
+Name[fr]=Page des propriétés d'un dossier de partage de Konqueror
+Name[gl]=Páxina coas Propiedades do Directorio de Ficheiros Compartidos de Konqueror
+Name[he]=דף מ×פייני ספרית ×§×‘×¦×™× ×ž×©×•×ª×¤×ª של Konqueror
+Name[hu]=Fájlmegosztási lap a Konqueror könyvtártulajdonságainál
+Name[is]=Skráastjóraeiginleikar Konqueror
+Name[it]=Pagina delle proprietà di Konqueror per la condivisione
+Name[ja]=Konqueror ã§ãƒ•ã‚¡ã‚¤ãƒ«å…±æœ‰ã™ã‚‹ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã®ãƒ—ロパティã®ãƒšãƒ¼ã‚¸
+Name[ka]=Konqueror-ის ფáƒáƒ˜áƒšáƒ—რგáƒáƒ–იáƒáƒ áƒ”ბის თვისებების გვერდი
+Name[kk]=Konqueror файлдарын ортақтаÑтыру каталогының қаÑиеттер беті
+Name[km]=ទំពáŸážšâ€‹áž›áž€áŸ’ážážŽáŸˆâ€‹ážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážáž Konqueror សម្រាប់​ការ​ចែក​រំលែក
+Name[lt]=Bylų dalinimosi Konqueror aplanko nustatymų puslapis
+Name[mk]=Страница Ñо параметри за делен именик во Konqueror
+Name[nb]=Fildeler – Side i Konquerors mappegenskaper
+Name[nds]=Konqueror Ornerfreegaav-Egenschappensiet
+Name[ne]=फाइल साà¤à¥‡à¤¦à¤¾à¤°à¥€ कनà¥à¤•à¥à¤µà¥‡à¤°à¤° डाइरेकà¥à¤Ÿà¤°à¥€ विशेषता पृषà¥à¤ 
+Name[nl]=Configuratiepagina Konqueror bestanden delen
+Name[nn]=Side for katalogeigenskapar til fildeling i Konqueror
+Name[pa]=ਫਾਇਲ ਸਾਂਠਕੋਨਕਿਊਰੋਰ ਡਾਇਰੈਕਟਰੀ ਵਿਸ਼ੇਸ਼ਤਾ ਸਫ਼ਾ
+Name[pl]=Strona współdzielenia we właściwościach katalogu w Konquerorze
+Name[pt]=Página de Propriedades da Pasta de Partilha do Konqueror
+Name[pt_BR]=Página de Propriedades do Diretório de Compartilhamento de Arquivos do Konqueror
+Name[ru]=Страница ÑвойÑтв каталога Ð´Ð»Ñ Ð¾Ð±Ñ‰ÐµÐ³Ð¾ доÑтупа
+Name[sk]=Nastavenie zdieľania prieÄinkov pre Konqueror
+Name[sl]=Stran z lastnostmi za souporabo mape
+Name[sr]=Страна Konqueror-а Ñа ÑвојÑтвима директоријума за дељење фајлова
+Name[sr@Latn]=Strana Konqueror-a sa svojstvima direktorijuma za deljenje fajlova
+Name[sv]=Konquerors fildelningssida med katalogegenskaper
+Name[ta]=Fileshare Konqueror அடைவ௠பணà¯à®ªà¯à®•à®³à®¿à®©à¯ பகà¯à®•à®®à¯
+Name[tg]=Саҳифаи ФеҳриÑти ХуÑуÑиÑтҳо оиди Konqueror ИÑтифодабарии Муштараки Файлҳо
+Name[tr]=Dosya Paylaşımı Konqueror Dizin Özellikleri Sayfası
+Name[uk]=Сторінка влаÑтивоÑтей каталогу Ð´Ð»Ñ Ñпільного доÑтупу
+Name[zh_CN]=文件共享 Konqueror 目录属性页
+Name[zh_HK]=檔案分享 Konqueror 目錄屬性é 
+Name[zh_TW]=Konqueror 檔案分享目錄屬性é 
+Comment=Konqueror properties dialog plugin to share a directory with the local network
+Comment[bg]=ÐаÑтройка на приÑтавката за браузъра Konqueror за ÑподелÑна на директории в локалната мрежа
+Comment[bn]=সà§à¦¥à¦¾à¦¨à§€à§Ÿ নেটওয়ারà§à¦•à§‡à¦° সঙà§à¦—ে à¦à¦•à¦Ÿà¦¿ ডিরেকà§à¦Ÿà¦°à§€ ভাগাভাগি করতে কনকরার বৈশিষà§à¦Ÿà§à¦¯à¦¾à¦¬à¦²à§€ ডায়ালগ পà§à¦²à¦¾à¦—িন
+Comment[bs]=Dodatak za prozor Konqueror postavki za dijeljenje direktorija u lokalnoj mreži
+Comment[ca]=Diàleg de propietats Konqueror de l'endollable per compartir un directori amb la xarxa local
+Comment[cs]=Modul dialogu vlastností Konqueroru pro sdílení adresářů v lokální síti
+Comment[da]=Konqueror egenskaber-dialog plugin til at dele en mappe med det lokale netværk
+Comment[de]=Konqueror Eigenschaftendialog-Plugin zur Freigabe eines Ordners in einem lokalen Netzwerk
+Comment[el]=ΠÏόσθετος διάλογος Ïυθμίσεων του Konqueror για την κοινή χÏήση ενός καταλόγου με το τοπικό δίκτυο
+Comment[en_GB]=Konqueror properties dialogue plugin to share a directory with the local network
+Comment[es]=Complemento de diálogo de propiedades de Konqueror para compartir un directorio con la red local
+Comment[et]=Konquerori omaduste dialoogi plugin kataloogi jagamiseks kohtvõrgus
+Comment[eu]=Konqueror-en propietateen elkarrizketa-koadro plugina direktorio bat sare lokalean partekatzeko
+Comment[fa]=ویژگیهای وصلۀ محاورۀ Konqueror برای اشتراک Ùهرست راهنما با شبکۀ محلی
+Comment[fi]=Konqueroroin laajennus, jolla voi jakaa kansioita lähiverkossa
+Comment[fr]=Module de Konqueror pour le partage d'un dossier sur le réseau local
+Comment[gl]=Diálogo de propiedades do plugin de Konqueror para compartir coa rede local
+Comment[he]=תוסף מ×פייני דו-שיח של Konqueror כדי לשתף סיפריה ×¢× ×¨×©×ª מקומית
+Comment[hu]=Konqueror tulajdonságablak-modul könyvtár megosztásához a helyi hálózaton
+Comment[it]=Plugin delle proprietà di Konqueror per condividere una cartella con la rete locale
+Comment[ja]=ローカルãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã§ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’共有ã™ã‚‹ Konqueror プロパティダイアログã®ãƒ—ラグイン
+Comment[ka]=Konqueror თვისებების დიáƒáƒšáƒáƒ’ის მáƒáƒ“ული ლáƒáƒ™áƒáƒšáƒ£áƒ  ქსელთáƒáƒœ გáƒáƒ¡áƒáƒ–იáƒáƒ áƒ”ბლáƒáƒ“
+Comment[kk]=Каталогты жергілікті желімен ортақтаÑтыратын Konqueror қаÑиеттер диалогының плагин модулі
+Comment[km]=ប្រអប់​លក្ážážŽáŸˆâ€‹ážŸáž˜áŸ’áž”ážáŸ’ážáž· Konqueror ដើម្បី​ចែក​រំលែក​ážážâ€‹áž“ៅ​ក្នុង​បណ្ដាញ​មូលដ្ឋាន
+Comment[lt]=Konqueror nustatymų dialogo įskiepis, skirtas dalintis aplanku vietiniame tinkle
+Comment[mk]=Приклучок Ñо дијалог за ÑвојÑтва во Konqueror за делење на датотеки Ñо локалната мрежа
+Comment[nb]=Programtillegg for Konquerors egenskapsdialog for å dele en mappe på lokalnettet
+Comment[nds]=Moduul för en Konqueror-Egenschappendialoog för't Freegeven vun Ornern in't lokale Nettwark
+Comment[ne]=सà¥à¤¥à¤¾à¤¨à¥€à¤¯ सञà¥à¤œà¤¾à¤²à¤¸à¤à¤— डाइरेकà¥à¤Ÿà¤°à¥€ साà¤à¥‡à¤¦à¤¾à¤° गरà¥à¤¨ कनà¥à¤•à¥à¤µà¥‡à¤°à¤° विशेषता संवाद पà¥à¤²à¤—इन
+Comment[nl]=Konqueror-plugin met instellingen om bestanden te delen via het lokale netwerk
+Comment[nn]=Konqueror-vising av eigenskapar til ein delt katalog i det lokale nettverket
+Comment[pl]=Wtyczka właściwości dla Konquerora umożliwiająca współdzielenie katalogu w sieci lokalnej
+Comment[pt]='Plugin' de janela de propriedades do Konqueror para partilhar uma pasta na rede local
+Comment[pt_BR]=Plug-in de diálogo de propriedades do Konqueror para o compartilhamento de uma pasta em uma rede local
+Comment[ru]=Модуль ÑвойÑтв Konqueror Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð¾Ð±Ñ‰ÐµÐ³Ð¾ доÑтупа к каталогу из локальной Ñети
+Comment[sk]=Modul Konquerora pre zdieľanie prieÄinku v lokálnej sieti
+Comment[sl]=Vstavek za Konqueror s pogovornim oknom za lastnosti za souporabo mape v krajevnem omrežju
+Comment[sr]=Прикључак Konqueror-а за подешавање ÑвојÑтава дељења директоријума у локалној мрежи
+Comment[sr@Latn]=PrikljuÄak Konqueror-a za podeÅ¡avanje svojstava deljenja direktorijuma u lokalnoj mreži
+Comment[sv]=Konqueror-insticksprogram med egenskapsdialogruta för att dela en katalog i det lokala nätverket
+Comment[ta]=Konqueror பணà¯à®ªà¯à®•à®³à®¿à®©à¯ உரையாடல௠செரà¯à®•à¯à®•à®³à¯ அடைவை சமà¯à®ªà®¾ சேவையகதà¯à®¤à¯à®Ÿà®©à¯ பகிரவேணà¯à®Ÿà®¿à®¯ செரà¯à®•à¯à®•à®³à¯
+Comment[tg]=Модули муколамаи хуÑуÑиÑтҳои Konqueror барои иÑтифодабарии муштараки феҳриÑÑ‚ бо шабакаи маҳалллӣ
+Comment[tr]=Yerel ağ ile dizin paylaşımı için Konqueror iletişim eklentisi özellikleri
+Comment[uk]=Втулок діалогу влаÑтивоÑтей Konqueror Ð´Ð»Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»Ñƒ Ñпільного доÑтупу до каталогу з локальної мережі
+Comment[zh_CN]=å¯å°†ç›®å½•ä¸Žå±€åŸŸç½‘共享的 Konqueror 属性页对è¯æ¡†æ’件
+Comment[zh_HK]=用於與本地網絡分享目錄的 Konqueror 屬性å°è©±ç›’æ’件
+Comment[zh_TW]=Konqueror 屬性å°è©±æ¡†å¤–掛程å¼ï¼Œç”¨æ–¼åœ¨æœ¬åœ°ç«¯ç¶²è·¯ä¸Šåˆ†äº«ç›®éŒ„
+X-KDE-Library=fileshare_propsdlgplugin
+X-KDE-Protocol=file
+ServiceTypes=KPropsDlg/Plugin,inode/directory
diff --git a/filesharing/advanced/propsdlgplugin/propertiespage.cpp b/filesharing/advanced/propsdlgplugin/propertiespage.cpp
new file mode 100644
index 00000000..e5b349da
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propertiespage.cpp
@@ -0,0 +1,612 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#include <qcheckbox.h>
+#include <qtooltip.h>
+#include <qbuttongroup.h>
+#include <qfileinfo.h>
+#include <qlabel.h>
+#include <kpushbutton.h>
+#include <qtimer.h>
+
+#include <kfileshare.h>
+#include <knfsshare.h>
+#include <ksambashare.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <kfile.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <klineedit.h>
+#include <kprocio.h>
+#include <ktempfile.h>
+
+// NFS related
+#include "../nfs/nfsfile.h"
+#include "../nfs/nfsentry.h"
+#include "../nfs/nfsdialog.h"
+
+// Samba related
+#include "../kcm_sambaconf/sambafile.h"
+#include "../kcm_sambaconf/sambashare.h"
+#include "../kcm_sambaconf/sharedlgimpl.h"
+
+#include "propertiespage.h"
+
+#define FILESHARE_DEBUG 5009
+
+PropertiesPage::PropertiesPage(QWidget* parent, KFileItemList items,bool enterUrl)
+ : PropertiesPageGUI(parent),
+ m_enterUrl(enterUrl),
+ m_items(items),
+ m_nfsFile(0),
+ m_nfsEntry(0),
+ m_sambaFile(0),
+ m_sambaShare(0),
+ m_sambaChanged(false),
+ m_nfsChanged(false),
+ m_loaded(false)
+{
+ if (m_items.isEmpty()) {
+ shareFrame->setDisabled(true);
+ } else {
+ shareFrame->setEnabled(true);
+ // currently only one dir is allowed
+ m_path = m_items.first()->url().path(1);
+ }
+
+ if (m_enterUrl) {
+ shareChk->hide();
+ urlRq->setMode(KFile::Directory |
+ KFile::ExistingOnly |
+ KFile::LocalOnly );
+ urlRq->setURL(m_path);
+ connect( urlRq, SIGNAL(textChanged(const QString&)),
+ this, SLOT(urlRqTextChanged(const QString&)));
+ } else {
+ urlRq->hide();
+ folderLbl->hide();
+ }
+
+
+ enableSamba(false,i18n("Reading Samba configuration file ..."));
+ enableNFS(false,i18n("Reading NFS configuration file ..."));
+
+
+ //QTimer::singleShot(1, this, SLOT(load));
+ load();
+}
+
+PropertiesPage::~PropertiesPage()
+{
+ delete m_nfsFile;
+ delete m_sambaFile;
+}
+
+void PropertiesPage::urlRqTextChanged(const QString&) {
+ if (!m_enterUrl)
+ return;
+
+ KURL url(urlRq->url());
+ if (url.isLocalFile()) {
+ QFileInfo info(url.path(1));
+ if (info.exists() &&
+ info.isDir())
+ {
+ shareFrame->setEnabled(true);
+ return;
+ }
+ }
+
+ shareFrame->setDisabled(true);
+}
+
+void PropertiesPage::load() {
+ loadNFS();
+ loadSamba();
+
+ bool nfsShared = KNFSShare::instance()->isDirectoryShared(m_path);
+ bool sambaShared = KSambaShare::instance()->isDirectoryShared(m_path);
+
+ nfsChk->setChecked(nfsShared);
+ sambaChk->setChecked(sambaShared);
+
+ if (!m_enterUrl)
+ shareChk->setChecked(nfsShared || sambaShared);
+
+ m_loaded = true;
+}
+
+void PropertiesPage::enableNFS(bool b, const QString & message) {
+ nfsChk->setEnabled(b);
+ nfsGrp->setEnabled(b);
+ QToolTip::add(nfsChk,message);
+ QToolTip::add(nfsGrp,message);
+}
+
+void PropertiesPage::enableSamba(bool b, const QString & message) {
+ sambaChk->setEnabled(b);
+ sambaGrp->setEnabled(b);
+ QToolTip::add(sambaChk,message);
+ QToolTip::add(sambaGrp,message);
+}
+
+
+bool PropertiesPage::save() {
+ if (!hasChanged()) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: nothing changed." << endl;
+ return true;
+ }
+
+ if (!checkURL()) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: url check failed." << endl;
+ return false;
+ }
+
+ updateNFSEntry();
+ if (!updateSambaShare()) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: updateSambaShare failed!" << endl;
+ return false;
+ }
+
+ return save(m_nfsFile, m_sambaFile, m_nfsChanged, m_sambaChanged);
+}
+
+bool PropertiesPage::save(NFSFile* nfsFile, SambaFile* sambaFile, bool nfs, bool samba)
+{
+ QString nfsFileName = KNFSShare::instance()->exportsPath();
+ bool nfsNeedsKDEsu = false;
+
+ if (nfs) {
+ if (QFileInfo(nfsFileName).isWritable()) {
+ nfsFile->saveTo(nfsFileName);
+ } else {
+ nfsNeedsKDEsu = true;
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: nfs needs kdesu." << endl;
+ }
+ } else
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: nfs has not changed." << endl;
+
+
+ QString sambaFileName = KSambaShare::instance()->smbConfPath();
+ bool sambaNeedsKDEsu = false;
+ if (samba) {
+ if (QFileInfo(sambaFileName).isWritable()) {
+ sambaFile->saveTo(sambaFileName);
+ } else {
+ sambaNeedsKDEsu = true;
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: samba needs kdesu." << endl;
+ }
+ } else
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: samba has not changed." << endl;
+
+
+ if (nfsNeedsKDEsu || sambaNeedsKDEsu) {
+ KTempFile nfsTempFile;
+ nfsTempFile.setAutoDelete(true);
+ KTempFile sambaTempFile;
+ sambaTempFile.setAutoDelete(true);
+
+ KProcIO proc;
+
+ QString command;
+
+ if (nfsNeedsKDEsu) {
+ nfsFile->saveTo(nfsTempFile.name());
+ command += QString("cp %1 %2;exportfs -ra;")
+ .arg(KProcess::quote( nfsTempFile.name() ))
+ .arg(KProcess::quote( nfsFileName ));
+ }
+
+ if (sambaNeedsKDEsu) {
+ sambaFile->saveTo(sambaTempFile.name());
+ command += QString("cp %1 %2;")
+ .arg(KProcess::quote( sambaTempFile.name() ))
+ .arg(KProcess::quote( sambaFileName ));
+ }
+
+ proc<<"kdesu" << "-d" << "-c"<<command;
+
+ if (!proc.start(KProcess::Block, true)) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: kdesu command failed" << endl;
+ return false;
+ }
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::save: Saving successfull." << endl;
+ return true;
+}
+
+bool PropertiesPage::saveSamba() {
+ if (!updateSambaShare()) {
+ return false;
+ }
+
+ if (m_sambaChanged) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::saveSamba: saving..." << endl;
+ return m_sambaFile->save();
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::saveSamba: samba has not changed." << endl;
+ return true;
+}
+
+bool PropertiesPage::saveNFS() {
+ updateNFSEntry();
+ if (!m_nfsChanged) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::saveNFS: NFS did not change." << endl;
+ return true;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::saveNFS: saving..." << endl;
+ return m_nfsFile->save();
+}
+
+
+
+
+bool PropertiesPage::checkURL() {
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL" << endl;
+
+ if (!m_enterUrl)
+ return true;
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: enterUrl=true" << endl;
+
+ KURL url(urlRq->url());
+ QString path = url.path(1);
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: m_path='"
+ << m_path << "'" << endl;
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: path='"
+ << path << "'" << endl;
+
+ if (m_path == path) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: paths are equal" << endl;
+ return true;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: different path" << endl;
+
+
+ if (!url.isValid()) {
+ KMessageBox::sorry(this,i18n("Please enter a valid path."));
+ urlRq->setFocus();
+ urlRq->lineEdit()->selectAll();
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: url is valid" << endl;
+
+ if (!url.isLocalFile()) {
+ KMessageBox::sorry(this,i18n("Only local folders can be shared."));
+ urlRq->setFocus();
+ urlRq->lineEdit()->selectAll();
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: url is local file" << endl;
+
+ QFileInfo info(path);
+
+ if (!info.exists())
+ {
+ KMessageBox::sorry(this,i18n("The folder does not exists."));
+ urlRq->setFocus();
+ urlRq->lineEdit()->selectAll();
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: folder exits" << endl;
+
+
+ if (!info.isDir())
+ {
+ KMessageBox::sorry(this,i18n("Only folders can be shared."));
+ urlRq->setFocus();
+ urlRq->lineEdit()->selectAll();
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: path is dir" << endl;
+
+ if (KSambaShare::instance()->isDirectoryShared(path) ||
+ KNFSShare::instance()->isDirectoryShared(path))
+ {
+ KMessageBox::sorry(this,i18n("The folder is already shared."));
+ urlRq->setFocus();
+ urlRq->lineEdit()->selectAll();
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::checkURL: folder not shared yet" << endl;
+ m_path = path;
+
+ return true;
+}
+
+bool PropertiesPage::loadNFS() {
+ if (!KFileShare::nfsEnabled()) {
+ enableNFS(false,i18n("The administrator does not allow sharing with NFS."));
+ return false;
+ }
+
+ delete m_nfsFile;
+ m_nfsFile = new NFSFile(KNFSShare::instance()->exportsPath());
+
+ if (!m_nfsFile->load()) {
+ enableNFS(false,i18n("Error: could not read NFS configuration file."));
+ return false;
+ }
+
+ enableNFS(true,"");
+
+ loadNFSEntry();
+ return true;
+}
+
+void PropertiesPage::loadNFSEntry() {
+ m_nfsEntry = m_nfsFile->getEntryByPath(m_path);
+ m_nfsChanged = false;
+
+ if (!m_nfsEntry) {
+ nfsChk->setChecked(false);
+ return;
+ }
+
+
+ NFSHost* publicHost = m_nfsEntry->getPublicHost();
+
+ if (publicHost) {
+ publicNFSChk->setChecked(true);
+ writableNFSChk->setChecked(!publicHost->readonly);
+ } else
+ publicNFSChk->setChecked(false);
+}
+
+void PropertiesPage::updateNFSEntry() {
+ if (shareChk->isChecked() &&
+ nfsChk->isChecked())
+ {
+ if (!m_nfsEntry) {
+ m_nfsEntry = new NFSEntry(m_path);
+ m_nfsFile->addEntry(m_nfsEntry);
+ m_nfsChanged = true;
+ }
+
+ NFSHost* publicHost = m_nfsEntry->getPublicHost();
+
+ if (publicNFSChk->isChecked()) {
+ if (!publicHost) {
+ publicHost = new NFSHost("*");
+ publicHost->allSquash=true;
+ m_nfsEntry->addHost(publicHost);
+ m_nfsChanged = true;
+ }
+
+ if (publicHost->readonly != !writableNFSChk->isChecked()) {
+ publicHost->readonly = !writableNFSChk->isChecked();
+ m_nfsChanged = true;
+ }
+ } else {
+ if (publicHost) {
+ m_nfsEntry->removeHost(publicHost);
+ m_nfsChanged = true;
+ }
+ }
+ } else { // unshare
+ if (m_nfsEntry) {
+ m_nfsFile->removeEntry(m_nfsEntry);
+ m_nfsEntry = 0;
+ m_nfsChanged = true;
+ }
+ }
+}
+
+void PropertiesPage::moreNFSBtn_clicked() {
+ updateNFSEntry();
+ NFSDialog* dlg = new NFSDialog(this,m_nfsEntry);
+ if (dlg->exec()==QDialog::Accepted &&
+ dlg->modified())
+ {
+ kdDebug(FILESHARE_DEBUG) << "NFSDialog::ok" << endl;
+ loadNFSEntry();
+ m_nfsChanged = true;
+ emit changed();
+ }
+ delete dlg;
+}
+
+bool PropertiesPage::loadSamba() {
+ if (!KFileShare::sambaEnabled()) {
+ enableSamba(false,i18n("The administrator does not allow sharing with Samba."));
+ return false;
+ }
+
+ delete m_sambaFile;
+ m_sambaFile = new SambaFile(KSambaShare::instance()->smbConfPath(), false);
+ if (! m_sambaFile->load()) {
+ enableSamba(false,i18n("Error: could not read Samba configuration file."));
+ return false;
+ }
+
+ enableSamba(true,"");
+ QString shareName = m_sambaFile->findShareByPath(m_path);
+ if (shareName.isNull()) {
+ sambaChk->setChecked(false);
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::loadSamba: shareName is null!"
+ << endl;
+ return false;
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::loadSamba: shareName="
+ << shareName << endl;
+
+ m_sambaShare = m_sambaFile->getShare(shareName);
+
+ loadSambaShare();
+ return true;
+}
+
+
+void PropertiesPage::loadSambaShare() {
+ if (! m_sambaShare) {
+ sambaChk->setChecked(false);
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::loadSambaShare: no share with name "
+ << m_sambaShare->getName() << endl;
+ return;
+ }
+
+ if (m_sambaShare->getBoolValue("public")) {
+ publicSambaChk->setChecked(true);
+ writableSambaChk->setChecked(m_sambaShare->getBoolValue("writable"));
+ } else
+ publicSambaChk->setChecked(false);
+
+
+ sambaNameEdit->setText(m_sambaShare->getName() );
+}
+
+void PropertiesPage::sambaChkToggled( bool b ) {
+ if (!m_loaded)
+ return;
+
+ if (sambaNameEdit->text().isEmpty())
+ sambaNameEdit->setText(getNewSambaName());
+}
+
+bool PropertiesPage::updateSambaShare() {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::updateSambaShare" << endl;
+
+ if (shareChk->isChecked() &&
+ sambaChk->isChecked())
+ {
+ if (m_enterUrl) {
+ if (m_path != urlRq->url()) {
+ m_path = urlRq->url();
+ }
+ }
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::updateSambaShare: m_path"
+ << m_path << endl;
+
+ if (!m_sambaShare) {
+ createNewSambaShare();
+ m_sambaChanged = true;
+ }
+
+ setSambaShareBoolValue("public", publicSambaChk);
+ setSambaShareBoolValue("writable", writableSambaChk);
+
+ if (sambaNameEdit->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("You have to enter a name for the Samba share."));
+ sambaNameEdit->setFocus();
+ return false;
+ }
+
+ if (sambaNameEdit->text() != m_sambaShare->getName()) {
+ SambaShare* otherShare = m_sambaFile->getShare(sambaNameEdit->text());
+ if (otherShare && otherShare != m_sambaShare) {
+ // There is another Share with the same name
+ KMessageBox::sorry(this, i18n("<qt>There is already a share with the name <strong>%1</strong>.<br> Please choose another name.</qt>").arg(sambaNameEdit->text()));
+ sambaNameEdit->selectAll();
+ sambaNameEdit->setFocus();
+ return false;
+ }
+ m_sambaShare->setName(sambaNameEdit->text());
+ m_sambaChanged = true;
+ }
+
+ if (m_sambaShare->getValue("path") != m_path) {
+ m_sambaShare->setValue("path", m_path);
+ m_sambaChanged = true;
+ }
+
+ } else {
+ if (m_sambaShare) {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::updateSambaShare: removing share" << endl;
+ m_sambaFile->removeShare(m_sambaShare);
+ m_sambaShare = 0;
+ m_sambaChanged = true;
+ }
+ }
+ return true;
+}
+
+void PropertiesPage::setSambaShareBoolValue(const QString & value,
+ QCheckBox* chk)
+{
+ bool v = m_sambaShare->getBoolValue(value);
+ if (v == chk->isChecked())
+ return;
+
+ m_sambaShare->setValue(value,chk->isChecked());
+ m_sambaChanged = true;
+}
+
+QString PropertiesPage::getNewSambaName() {
+ QString path = m_path;
+ if (path.isNull() && m_enterUrl) {
+ path = urlRq->url();
+ }
+
+ QString shareName = KURL(path).fileName();
+
+ if (!sambaNameEdit->text().isEmpty())
+ shareName = sambaNameEdit->text();
+
+ // Windows could have problems with longer names
+ shareName = shareName.left(12).upper();
+
+ if ( m_sambaFile->getShare(shareName) )
+ shareName = m_sambaFile->getUnusedName(shareName);
+
+ return shareName;
+}
+
+void PropertiesPage::createNewSambaShare() {
+
+ m_sambaShare = m_sambaFile->newShare(getNewSambaName(),m_path);
+
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::createNewSambaShare: "
+ << m_sambaShare->getName() << endl;
+
+}
+
+
+void PropertiesPage::moreSambaBtnClicked() {
+ kdDebug(FILESHARE_DEBUG) << "PropertiesPage::moreSambaBtnClicked()" << endl;
+ updateSambaShare();
+ ShareDlgImpl* dlg = new ShareDlgImpl(this,m_sambaShare);
+ dlg->directoryGrp->hide();
+ dlg->pixmapFrame->hide();
+ if (dlg->exec() == QDialog::Accepted &&
+ dlg->hasChanged()) {
+ m_sambaChanged = true;
+ changedSlot();
+ loadSambaShare();
+ }
+ delete dlg;
+}
+
+#include "propertiespage.moc"
+
diff --git a/filesharing/advanced/propsdlgplugin/propertiespage.h b/filesharing/advanced/propsdlgplugin/propertiespage.h
new file mode 100644
index 00000000..3becf6e7
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propertiespage.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef PROPERTIESPAGE_H
+#define PROPERTIESPAGE_H
+
+#include <kfileitem.h>
+#include "propertiespagegui.h"
+
+class NFSFile;
+class NFSEntry;
+class SambaFile;
+class SambaShare;
+class QCheckBox;
+
+class PropertiesPage : public PropertiesPageGUI
+{
+Q_OBJECT
+public:
+ PropertiesPage(QWidget* parent, KFileItemList items, bool enterUrl=false);
+ virtual ~PropertiesPage();
+
+ bool save();
+
+ static bool save(NFSFile* nfsFile, SambaFile* sambFile, bool nfs, bool samba);
+public slots:
+ void load();
+
+protected:
+
+ bool m_enterUrl;
+ QString m_path;
+ KFileItemList m_items;
+ NFSFile *m_nfsFile;
+ NFSEntry *m_nfsEntry;
+
+ SambaFile *m_sambaFile;
+ SambaShare *m_sambaShare;
+ bool m_sambaChanged;
+ bool m_nfsChanged;
+ bool m_loaded;
+
+protected slots:
+ // inherited from PropertiesPageGUI
+ virtual void moreNFSBtn_clicked();
+ virtual void moreSambaBtnClicked();
+ virtual void sambaChkToggled( bool b );
+ virtual void urlRqTextChanged(const QString&);
+
+private:
+ bool loadNFS();
+ void loadNFSEntry();
+ void updateNFSEntry();
+ bool saveNFS();
+
+ bool loadSamba();
+ void loadSambaShare();
+ bool updateSambaShare();
+ bool saveSamba();
+
+ bool checkURL();
+ void setSambaShareBoolValue(const QString & value, QCheckBox* chk);
+ void createNewSambaShare();
+ QString getNewSambaName();
+
+ void enableNFS(bool b,const QString & message);
+ void enableSamba(bool b,const QString & message);
+
+};
+
+#endif
diff --git a/filesharing/advanced/propsdlgplugin/propertiespagegui.ui b/filesharing/advanced/propsdlgplugin/propertiespagegui.ui
new file mode 100644
index 00000000..4f756d3b
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propertiespagegui.ui
@@ -0,0 +1,478 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>PropertiesPageGUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PropertiesPageGUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>433</width>
+ <height>348</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>folderLbl</cstring>
+ </property>
+ <property name="text">
+ <string>Folder:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>urlRq</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>shareChk</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;hare this folder in the local network</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </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="QFrame">
+ <property name="name">
+ <cstring>shareFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>nfsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Share with &amp;NFS (Linux/UNIX)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>nfsGrp</cstring>
+ </property>
+ <property name="title">
+ <string>NFS Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>publicNFSChk</cstring>
+ </property>
+ <property name="text">
+ <string>Pu&amp;blic</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>writableNFSChk</cstring>
+ </property>
+ <property name="text">
+ <string>W&amp;ritable</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>moreNFSBtn</cstring>
+ </property>
+ <property name="text">
+ <string>More NFS Op&amp;tions</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>156</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>sambaChk</cstring>
+ </property>
+ <property name="text">
+ <string>Share with S&amp;amba (Microsoft(R) Windows(R))</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>sambaGrp</cstring>
+ </property>
+ <property name="title">
+ <string>Samba Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sambaNameEdit</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>publicSambaChk</cstring>
+ </property>
+ <property name="text">
+ <string>P&amp;ublic</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>writableSambaChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Writable</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>moreSambaBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Mor&amp;e Samba Options</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>sambaChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>sambaGrp</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>nfsChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nfsGrp</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>publicSambaChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>writableSambaChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>publicNFSChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>writableNFSChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>shareFrame</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>sambaChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>publicSambaChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>writableSambaChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>nfsChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>publicNFSChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>writableNFSChk</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>moreNFSBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>moreNFSBtn_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>sambaChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>sambaChkToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>sambaNameEdit</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>publicSambaChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>publicSambaChkToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>publicNFSChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>publicNFSChkToggled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>moreSambaBtn</sender>
+ <signal>clicked()</signal>
+ <receiver>PropertiesPageGUI</receiver>
+ <slot>moreSambaBtnClicked()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="local" impldecl="in implementation">propertiespagegui.ui.h</include>
+</includes>
+<variables>
+ <variable>bool m_hasChanged;</variable>
+</variables>
+<signals>
+ <signal>changed()</signal>
+</signals>
+<slots>
+ <slot access="protected">changedSlot()</slot>
+ <slot>moreNFSBtn_clicked()</slot>
+ <slot>sambaChkToggled( bool )</slot>
+ <slot>publicSambaChkToggled( bool b )</slot>
+ <slot>publicNFSChkToggled( bool b )</slot>
+ <slot>moreSambaBtnClicked()</slot>
+</slots>
+<functions>
+ <function returnType="bool">hasChanged()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/advanced/propsdlgplugin/propertiespagegui.ui.h b/filesharing/advanced/propsdlgplugin/propertiespagegui.ui.h
new file mode 100644
index 00000000..89d4a56e
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propertiespagegui.ui.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void PropertiesPageGUI::changedSlot()
+{
+ m_hasChanged = true;
+ emit changed();
+}
+
+
+void PropertiesPageGUI::moreNFSBtn_clicked()
+{
+
+}
+
+
+bool PropertiesPageGUI::hasChanged()
+{
+ return m_hasChanged;
+}
+
+
+void PropertiesPageGUI::sambaChkToggled( bool )
+{
+
+}
+
+
+void PropertiesPageGUI::publicSambaChkToggled( bool b)
+{
+ if (!b) {
+ writableSambaChk->setChecked(false);
+ }
+}
+
+
+void PropertiesPageGUI::publicNFSChkToggled( bool b)
+{
+ if (!b) {
+ writableNFSChk->setChecked(false);
+ }
+
+}
+
+
+void PropertiesPageGUI::moreSambaBtnClicked()
+{
+
+}
diff --git a/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.cpp b/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.cpp
new file mode 100644
index 00000000..70d94519
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.cpp
@@ -0,0 +1,126 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+#include <qstring.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <kfileshare.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kdialog.h>
+#include <kglobal.h>
+
+#include "propertiespage.h"
+#include "propsdlgshareplugin.h"
+
+typedef KGenericFactory<PropsDlgSharePlugin, KPropertiesDialog> PropsDlgSharePluginFactory;
+
+K_EXPORT_COMPONENT_FACTORY( fileshare_propsdlgplugin,
+ PropsDlgSharePluginFactory("fileshare_propsdlgplugin") )
+
+class PropsDlgSharePlugin::Private
+{
+ public:
+ PropertiesPage* page;
+};
+
+PropsDlgSharePlugin::PropsDlgSharePlugin( KPropertiesDialog *dlg,
+ const char *, const QStringList & )
+ : KPropsDlgPlugin(dlg), d(0)
+{
+ KGlobal::locale()->insertCatalogue("kfileshare");
+
+ if (KFileShare::shareMode() == KFileShare::Simple) {
+ kdDebug(5009) << "PropsDlgSharePlugin: Sharing mode is simple. Aborting." << endl;
+ return;
+ }
+
+
+ QVBox* vbox = properties->addVBoxPage(i18n("&Share"));
+ properties->setFileSharingPage(vbox);
+
+ if (KFileShare::authorization() == KFileShare::UserNotAllowed) {
+
+ QWidget* widget = new QWidget( vbox );
+ QVBoxLayout * vLayout = new QVBoxLayout( widget );
+ vLayout->setSpacing( KDialog::spacingHint() );
+ vLayout->setMargin( 0 );
+
+
+ if (KFileShare::sharingEnabled()) {
+ vLayout->addWidget(
+ new QLabel( i18n("You need to be authorized to share directories."),
+ widget ));
+ } else {
+ vLayout->addWidget(
+ new QLabel( i18n("File sharing is disabled."), widget));
+ }
+
+ KPushButton* btn = new KPushButton( i18n("Configure File Sharing..."), widget );
+ connect( btn, SIGNAL( clicked() ), SLOT( slotConfigureFileSharing() ) );
+ btn->setDefault(false);
+ QHBoxLayout* hBox = new QHBoxLayout( (QWidget *)0L );
+ hBox->addWidget( btn, 0, Qt::AlignLeft );
+ vLayout->addLayout(hBox);
+ vLayout->addStretch( 10 ); // align items on top
+ return;
+ }
+
+
+ d = new Private();
+
+ d->page = new PropertiesPage(vbox, properties->items(),false);
+ connect(d->page, SIGNAL(changed()),
+ this, SIGNAL(changed()));
+
+ kdDebug(5009) << "Fileshare properties dialog plugin loaded" << endl;
+
+}
+
+void PropsDlgSharePlugin::slotConfigureFileSharing()
+{
+ KProcess proc;
+ proc << KStandardDirs::findExe("kdesu") << locate("exe", "kcmshell") << "fileshare";
+ proc.start( KProcess::DontCare );
+}
+
+
+PropsDlgSharePlugin::~PropsDlgSharePlugin()
+{
+ delete d;
+}
+
+void PropsDlgSharePlugin::applyChanges()
+{
+ if (!d->page->save()) {
+// KMessageBox::sorry(d->page,
+// i18n("Saving the changes failed"));
+
+ properties->abortApplying();
+ }
+}
+
+
+#include "propsdlgshareplugin.moc"
+
diff --git a/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.h b/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.h
new file mode 100644
index 00000000..acf889fa
--- /dev/null
+++ b/filesharing/advanced/propsdlgplugin/propsdlgshareplugin.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef KONQFILESHAREPLUGIN_H
+#define KONQFILESHAREPLUGIN_H
+
+#include <kpropertiesdialog.h>
+
+class PropsDlgSharePlugin : public KPropsDlgPlugin
+{
+Q_OBJECT
+public:
+ PropsDlgSharePlugin( KPropertiesDialog *dlg, const char *, const QStringList & );
+ virtual ~PropsDlgSharePlugin();
+ virtual void applyChanges();
+
+protected slots:
+ void slotConfigureFileSharing();
+
+private:
+ class Private;
+ Private *d;
+
+};
+
+#endif
+
+
diff --git a/filesharing/simple/Makefile.am b/filesharing/simple/Makefile.am
new file mode 100644
index 00000000..7a3e2994
--- /dev/null
+++ b/filesharing/simple/Makefile.am
@@ -0,0 +1,19 @@
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kcm_fileshare.la
+
+kcm_fileshare_la_SOURCES = fileshare.cpp \
+ controlcenter.ui \
+ groupconfiggui.ui \
+ groupconfigdlg.cpp \
+ krichtextlabel.cpp
+
+kcm_fileshare_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+kcm_fileshare_la_LIBADD = ../advanced/propsdlgplugin/libpropsdlgplugin_common.la \
+ ../advanced/nfs/libfilesharenfs.la \
+ ../advanced/kcm_sambaconf/libfilesharesamba.la \
+ $(LIB_KIO)
+
+AM_CPPFLAGS = -I$(srcdir)/../advanced/propsdlgplugin -I../advanced/propsdlgplugin $(all_includes)
+
+xdg_apps_DATA = fileshare.desktop
diff --git a/filesharing/simple/controlcenter.ui b/filesharing/simple/controlcenter.ui
new file mode 100644
index 00000000..eea9166e
--- /dev/null
+++ b/filesharing/simple/controlcenter.ui
@@ -0,0 +1,486 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ControlCenterGUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ControlCenterGUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>525</width>
+ <height>535</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KRichTextLabel">
+ <property name="name">
+ <cstring>infoLbl</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="text">
+ <string>SMB and NFS servers are not installed on this machine, to enable this module the servers must be installed.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>shareGrp</cstring>
+ </property>
+ <property name="title">
+ <string>Enable Local Networ&amp;k File Sharing</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>simpleRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Si&amp;mple sharing</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>frame4_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>11</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KRichTextLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Enable simple sharing to allow users to share folders from their HOME folder, without knowing the root password.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>advancedRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Advanced sharin&amp;g</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>frame4_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>11</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="KRichTextLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Enable advanced sharing to allow users to share any folders, as long as they have write access to the needed configuration files, or they know the root password.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>frame4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>11</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>nfsChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Use &amp;NFS (Linux/UNIX)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>sambaChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Use Sam&amp;ba (Microsoft(R) Windows(R))</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>allowedUsersBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Allo&amp;wed Users</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>sharedFoldersGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Shared Folders</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Path</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Samba</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>NFS</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>listView</cstring>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>NoSelection</enum>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>shareBtnPnl</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>addShareBtn</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>changeShareBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Chang&amp;e...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>removeShareBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Rem&amp;ove</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>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>advancedRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nfsChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>advancedRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>sambaChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>shareGrp</sender>
+ <signal>clicked(int)</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>simpleRadio</sender>
+ <signal>clicked()</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>advancedRadio</sender>
+ <signal>clicked()</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>sambaChk</sender>
+ <signal>clicked()</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>nfsChk</sender>
+ <signal>clicked()</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>changedSlot()</slot>
+ </connection>
+ <connection>
+ <sender>listView</sender>
+ <signal>selectionChanged()</signal>
+ <receiver>ControlCenterGUI</receiver>
+ <slot>listView_selectionChanged()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>sambaChk</tabstop>
+ <tabstop>nfsChk</tabstop>
+ <tabstop>listView</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in implementation">controlcenter.ui.h</include>
+ <include location="local" impldecl="in implementation">krichtextlabel.h</include>
+</includes>
+<signals>
+ <signal>changed()</signal>
+</signals>
+<slots>
+ <slot access="private">changedSlot()</slot>
+ <slot>listView_selectionChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>krichtextlabel.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/filesharing/simple/controlcenter.ui.h b/filesharing/simple/controlcenter.ui.h
new file mode 100644
index 00000000..6fb3f62f
--- /dev/null
+++ b/filesharing/simple/controlcenter.ui.h
@@ -0,0 +1,22 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void ControlCenterGUI::changedSlot()
+{
+ emit changed();
+}
+
+void ControlCenterGUI::listView_selectionChanged()
+{
+ bool empty = listView->selectedItems ().isEmpty();
+ changeShareBtn->setDisabled(empty );
+ removeShareBtn->setDisabled(empty );
+
+}
diff --git a/filesharing/simple/fileshare.cpp b/filesharing/simple/fileshare.cpp
new file mode 100644
index 00000000..a0c5bb06
--- /dev/null
+++ b/filesharing/simple/fileshare.cpp
@@ -0,0 +1,442 @@
+/*
+ Copyright (c) 2002 Laurent Montel <lmontel@mandrakesoft.com>
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <unistd.h>
+
+#include <qlayout.h>
+#include <qvbuttongroup.h>
+#include <qvgroupbox.h>
+#include <qlabel.h>
+#include <qdir.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kdialogbase.h>
+#include <kgenericfactory.h>
+#include <klistview.h>
+#include <kiconloader.h>
+#include <knfsshare.h>
+#include <ksambashare.h>
+#include <kfileshare.h>
+#include <kstandarddirs.h>
+#include <ksimpleconfig.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kuser.h>
+#include <kurl.h>
+#include <kprocess.h>
+#include <krichtextlabel.h>
+
+#include "../advanced/propsdlgplugin/propertiespage.h"
+#include "../advanced/nfs/nfsfile.h"
+#include "../advanced/kcm_sambaconf/sambafile.h"
+
+#include "controlcenter.h"
+#include "fileshare.h"
+#include "groupconfigdlg.h"
+
+typedef KGenericFactory<KFileShareConfig, QWidget > ShareFactory;
+K_EXPORT_COMPONENT_FACTORY (kcm_fileshare, ShareFactory("kcmfileshare") )
+
+
+#define FILESHARECONF "/etc/security/fileshare.conf"
+#define FILESHARE_DEBUG 5009
+
+KFileShareConfig::KFileShareConfig(QWidget *parent, const char *name, const QStringList &):
+ KCModule(ShareFactory::instance(), parent, name)
+{
+ KGlobal::locale()->insertCatalogue("kfileshare");
+
+ QBoxLayout* layout = new QVBoxLayout(this,0,
+ KDialog::spacingHint());
+
+/*
+ QVButtonGroup *box = new QVButtonGroup( i18n("File Sharing"), this );
+ box->layout()->setSpacing( KDialog::spacingHint() );
+ layout->addWidget(box);
+ noSharing=new QRadioButton( i18n("Do &not allow users to share files"), box );
+ sharing=new QRadioButton( i18n("&Allow users to share files from their HOME folder"), box);
+*/
+ m_ccgui = new ControlCenterGUI(this);
+ connect( m_ccgui, SIGNAL( changed()), this, SLOT(configChanged()));
+ connect( m_ccgui->allowedUsersBtn, SIGNAL( clicked()),
+ this, SLOT(allowedUsersBtnClicked()));
+
+ QString path = QString::fromLocal8Bit( getenv( "PATH" ) );
+ path += QString::fromLatin1(":/usr/sbin");
+ QString sambaExec = KStandardDirs::findExe( QString::fromLatin1("smbd"), path );
+ QString nfsExec = KStandardDirs::findExe( QString::fromLatin1("rpc.nfsd"), path );
+
+ if ( nfsExec.isEmpty() && sambaExec.isEmpty())
+ {
+ m_ccgui->shareGrp->setDisabled(true);
+ m_ccgui->sharedFoldersGroupBox->setDisabled(true);
+ }
+ else
+ {
+ if (nfsExec.isEmpty()) {
+ m_ccgui->nfsChk->setDisabled(true);
+ m_ccgui->nfsChk->setChecked(false);
+ QToolTip::add(m_ccgui->nfsChk,i18n("No NFS server installed on this system"));
+ }
+
+ if (sambaExec.isEmpty()) {
+ m_ccgui->sambaChk->setDisabled(true);
+ m_ccgui->sambaChk->setChecked(false);
+ QToolTip::add(m_ccgui->sambaChk,i18n("No Samba server installed on this system"));
+ }
+
+ m_ccgui->infoLbl->hide();
+ layout->addWidget(m_ccgui);
+ updateShareListView();
+ connect( KNFSShare::instance(), SIGNAL( changed()),
+ this, SLOT(updateShareListView()));
+ connect( KSambaShare::instance(), SIGNAL( changed()),
+ this, SLOT(updateShareListView()));
+
+
+ }
+
+ if((getuid() == 0) ||
+ ((KFileShare::shareMode() == KFileShare::Advanced) &&
+ (KFileShare::authorization() == KFileShare::Authorized)))
+ {
+ connect( m_ccgui->addShareBtn, SIGNAL(clicked()),
+ this, SLOT(addShareBtnClicked()));
+ connect( m_ccgui->changeShareBtn, SIGNAL(clicked()),
+ this, SLOT(changeShareBtnClicked()));
+ connect( m_ccgui->removeShareBtn, SIGNAL(clicked()),
+ this, SLOT(removeShareBtnClicked()));
+ m_ccgui->listView->setSelectionMode(QListView::Extended);
+ m_ccgui->shareBtnPnl->setEnabled(true);
+ }
+
+
+ if (getuid()==0) {
+ setButtons(Help|Apply);
+ } else {
+ setButtons(Help);
+ m_ccgui->shareGrp->setDisabled( true );
+ }
+
+ load();
+}
+
+void KFileShareConfig::updateShareListView()
+{
+ m_ccgui->listView->clear();
+ KNFSShare* nfs = KNFSShare::instance();
+ KSambaShare* samba = KSambaShare::instance();
+
+ QStringList dirs = nfs->sharedDirectories();
+ QStringList sambaDirs = samba->sharedDirectories();
+
+ for ( QStringList::ConstIterator it = sambaDirs.begin(); it != sambaDirs.end(); ++it ) {
+ // Do not insert duplicates
+ if (nfs->isDirectoryShared(*it))
+ continue;
+
+ dirs += *it;
+ }
+
+ QPixmap folderPix = SmallIcon("folder",0,KIcon::ShareOverlay);
+ QPixmap okPix = SmallIcon("button_ok");
+ QPixmap cancelPix = SmallIcon("button_cancel");
+
+ for ( QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it ) {
+ KListViewItem* item = new KListViewItem(m_ccgui->listView);
+ item->setText(0,*it);
+ item->setPixmap(0, folderPix);
+
+ if (samba->isDirectoryShared(*it))
+ item->setPixmap(1,okPix);
+ else
+ item->setPixmap(1,cancelPix);
+
+ if (nfs->isDirectoryShared(*it))
+ item->setPixmap(2,okPix);
+ else
+ item->setPixmap(2,cancelPix);
+
+ }
+
+}
+
+void KFileShareConfig::allowedUsersBtnClicked() {
+ GroupConfigDlg dlg(this,m_fileShareGroup,m_restricted,m_rootPassNeeded,
+ m_ccgui->simpleRadio->isChecked());
+ if (dlg.exec() == QDialog::Accepted) {
+ m_fileShareGroup = dlg.fileShareGroup().name();
+ m_restricted = dlg.restricted();
+ m_rootPassNeeded = dlg.rootPassNeeded();
+ configChanged();
+ }
+
+}
+
+
+void KFileShareConfig::load()
+{
+ KSimpleConfig config(QString::fromLatin1(FILESHARECONF),true);
+
+ m_ccgui->shareGrp->setChecked( config.readEntry("FILESHARING", "yes") == "yes" );
+
+ m_restricted = config.readEntry("RESTRICT", "yes") == "yes";
+
+ if (config.readEntry("SHARINGMODE", "simple") == "simple")
+ m_ccgui->simpleRadio->setChecked(true);
+ else
+ m_ccgui->advancedRadio->setChecked(true);
+
+ m_fileShareGroup = config.readEntry("FILESHAREGROUP", "fileshare");
+
+ m_ccgui->sambaChk->setChecked(
+ config.readEntry("SAMBA", "yes") == "yes");
+
+ m_ccgui->nfsChk->setChecked(
+ config.readEntry("NFS", "yes") == "yes");
+
+ m_rootPassNeeded = config.readEntry("ROOTPASSNEEDED", "yes") == "yes";
+
+ m_smbConf = KSambaShare::instance()->smbConfPath();
+}
+
+bool KFileShareConfig::addGroupAccessesToFile(const QString & file) {
+ KProcess chgrp;
+ chgrp << "chgrp" << m_fileShareGroup << file;
+ KProcess chmod;
+ chmod << "chmod" << "g=rw" << file;
+
+ if (!chgrp.start(KProcess::Block) && chgrp.normalExit()) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::addGroupAccessesToFile: chgrp failed" << endl;
+ return false;
+
+ }
+
+ if(!chmod.start(KProcess::Block) && chmod.normalExit()) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::addGroupAccessesToFile: chmod failed" << endl;
+ return false;
+ }
+
+ return true;
+
+}
+
+bool KFileShareConfig::removeGroupAccessesFromFile(const QString & file) {
+ KProcess chgrp;
+ chgrp << "chgrp" << "root" << file;
+ KProcess chmod;
+ chmod << "chmod" << "g=r" << file;
+
+ if (!chgrp.start(KProcess::Block) && chgrp.normalExit()) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::removeGroupAccessesFromFile: chgrp failed" << endl;
+ return false;
+
+ }
+
+ if(!chmod.start(KProcess::Block) && chmod.normalExit()) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::removeGroupAccessesFromFile: chmod failed" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool KFileShareConfig::setGroupAccesses() {
+ if (m_rootPassNeeded || ! m_ccgui->sambaChk->isChecked()) {
+ if (!removeGroupAccessesFromFile(KSambaShare::instance()->smbConfPath()))
+ return false;
+ }
+
+ if (m_rootPassNeeded || ! m_ccgui->nfsChk->isChecked()) {
+ if (!removeGroupAccessesFromFile(KNFSShare::instance()->exportsPath()))
+ return false;
+ }
+
+ if (! m_rootPassNeeded && m_ccgui->sambaChk->isChecked()) {
+ if (!addGroupAccessesToFile(KSambaShare::instance()->smbConfPath()))
+ return false;
+ }
+
+ if (! m_rootPassNeeded && m_ccgui->nfsChk->isChecked()) {
+ if (!addGroupAccessesToFile(KNFSShare::instance()->exportsPath()))
+ return false;
+ }
+
+
+ return true;
+}
+
+void KFileShareConfig::save()
+{
+ setGroupAccesses();
+
+ QDir dir("/etc/security");
+ if ( !dir.exists())
+ dir.mkdir("/etc/security");
+
+ QFile file(FILESHARECONF);
+ if ( ! file.open(IO_WriteOnly)) {
+ KMessageBox::detailedError(this,
+ i18n("Could not save settings."),
+ i18n("Could not open file '%1' for writing: %2").arg(FILESHARECONF).arg(
+ file.errorString() ),
+ i18n("Saving Failed"));
+ return;
+ }
+
+
+ QTextStream stream(&file);
+
+ stream << "FILESHARING=";
+ stream << (m_ccgui->shareGrp->isChecked() ? "yes" : "no");
+
+ stream << "\nRESTRICT=";
+ stream << (m_restricted ? "yes" : "no");
+
+ stream << "\nSHARINGMODE=";
+ stream << (m_ccgui->simpleRadio->isChecked() ? "simple" : "advanced");
+
+ stream << "\nFILESHAREGROUP=";
+ stream << m_fileShareGroup;
+
+ stream << "\nSAMBA=";
+ stream << (m_ccgui->sambaChk->isChecked() ? "yes" : "no");
+
+ stream << "\nNFS=";
+ stream << (m_ccgui->nfsChk->isChecked() ? "yes" : "no");
+
+ stream << "\nROOTPASSNEEDED=";
+ stream << (m_rootPassNeeded ? "yes" : "no");
+
+ stream << "\nSMBCONF=";
+ stream << m_smbConf;
+
+ file.close();
+}
+
+void KFileShareConfig::defaults()
+{
+ m_ccgui->shareGrp->setChecked( false );
+}
+
+QString KFileShareConfig::quickHelp() const
+{
+ return i18n("<h1>File Sharing</h1><p>This module can be used "
+ "to enable file sharing over the network using "
+ "the \"Network File System\" (NFS) or SMB in Konqueror. "
+ "The latter enables you to share your files with Windows(R) "
+ "computers on your network.</p>");
+}
+
+void KFileShareConfig::addShareBtnClicked() {
+ showShareDialog(KFileItemList());
+}
+
+
+PropertiesPageDlg::PropertiesPageDlg(QWidget*parent, KFileItemList files)
+ : KDialogBase(parent, "sharedlg", true,
+ i18n("Share Folder"), Ok|Cancel, Ok, true)
+{
+ QVBox* vbox = makeVBoxMainWidget();
+
+ m_page = new PropertiesPage(vbox,files,true);
+}
+
+bool PropertiesPageDlg::hasChanged() {
+ return m_page->hasChanged();
+}
+
+void PropertiesPageDlg::slotOk() {
+ if (hasChanged()) {
+ if (!m_page->save())
+ return;
+ }
+
+ KDialogBase::slotOk();
+}
+
+
+
+void KFileShareConfig::showShareDialog(const KFileItemList & files) {
+ PropertiesPageDlg* dlg = new PropertiesPageDlg(this,files);
+ if (dlg->exec() == QDialog::Accepted) {
+ if ( dlg->hasChanged() ) {
+ updateShareListView();
+ }
+ }
+ delete dlg;
+}
+
+void KFileShareConfig::changeShareBtnClicked() {
+ KFileItemList files;
+ QPtrList<QListViewItem> items = m_ccgui->listView->selectedItems();
+
+ QListViewItem* item;
+ for ( item = items.first(); item; item = items.next() ) {
+ files.append(new KFileItem(KURL::fromPathOrURL(item->text(0)),"",0));
+ }
+
+ showShareDialog(files);
+}
+
+void KFileShareConfig::removeShareBtnClicked() {
+
+ QPtrList<QListViewItem> items = m_ccgui->listView->selectedItems();
+ QListViewItem *item;
+
+ bool nfs = false;
+ bool samba = false;
+
+ for ( item = items.first(); item; item = items.next() ) {
+
+ if (KNFSShare::instance()->isDirectoryShared(item->text(0)))
+ nfs = true;
+
+ if (KSambaShare::instance()->isDirectoryShared(item->text(0)))
+ samba = true;
+ }
+
+ NFSFile nfsFile(KNFSShare::instance()->exportsPath());
+ if (nfs) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::removeShareBtnClicked: nfs = true" << endl;
+ nfsFile.load();
+ for ( item = items.first(); item; item = items.next() ) {
+ nfsFile.removeEntryByPath(item->text(0));
+ }
+ }
+
+ SambaFile smbFile(KSambaShare::instance()->smbConfPath(),false);
+ if (samba) {
+ kdDebug(FILESHARE_DEBUG) << "KFileShareConfig::removeShareBtnClicked: samba = true" << endl;
+ smbFile.load();
+ for ( item = items.first(); item; item = items.next() ) {
+ smbFile.removeShareByPath(item->text(0));
+ }
+ }
+
+ PropertiesPage::save(&nfsFile, &smbFile, nfs,samba);
+
+ updateShareListView();
+}
+
+#include "fileshare.moc"
diff --git a/filesharing/simple/fileshare.desktop b/filesharing/simple/fileshare.desktop
new file mode 100644
index 00000000..6ed8dd8b
--- /dev/null
+++ b/filesharing/simple/fileshare.desktop
@@ -0,0 +1,169 @@
+[Desktop Entry]
+Exec=kcmshell fileshare
+Icon=share
+Type=Application
+
+X-KDE-ModuleType=Library
+X-KDE-Library=fileshare
+X-KDE-ParentApp=kcontrol
+X-KDE-RootOnly=true
+
+Name=File Sharing
+Name[be]=ÐŸÑƒÐ±Ð»Ñ–ÐºÐ°Ñ†Ñ‹Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž
+Name[bg]=СподелÑне на файлове
+Name[bn]=ফাইল ভাগাভাগি
+Name[br]=Rannañ restroù
+Name[bs]=Dijeljenje datoteka
+Name[ca]=Compartició de fitxers
+Name[cs]=Sdílení souborů
+Name[cy]=Rhannu Ffeiliau
+Name[da]=Fildeling
+Name[de]=Dateifreigabe
+Name[el]=Κοινή χÏήση αÏχείων
+Name[eo]=Dosierkomunigado
+Name[es]=Compartir archivos
+Name[et]=Failijagamine
+Name[eu]=Fitxategi partekatzea
+Name[fa]=اشتراک پرونده
+Name[fi]=Tiedostojen jakaminen
+Name[fr]=Partage de fichiers
+Name[ga]=Roinnt na gComhad
+Name[gl]=Compartición de ficheiros
+Name[he]=שיתוף קבצי×
+Name[hu]=Fájlmegosztás
+Name[is]=Deila skrám
+Name[it]=Condivisione di file
+Name[ja]=ファイル共有
+Name[ka]=ფáƒáƒ˜áƒšáƒ—რგáƒáƒ–იáƒáƒ áƒ”ბáƒ
+Name[kk]=Файлдарды ортақтаÑтыру
+Name[km]=ការ​ចែក​រំលែក​ឯកសារ
+Name[lt]=Dalinimasis bylomis
+Name[mk]=Делење на датотеки
+Name[nb]=Fildeling
+Name[nds]=Dateifreegaav
+Name[ne]=फाइल साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Name[nl]=Bestanden delen
+Name[nn]=Fildeling
+Name[pa]=ਫਾਇਲ ਸ਼ਾਂà¨
+Name[pl]=Współdzielenie plików
+Name[pt]=Partilha de Ficheiros
+Name[pt_BR]=Compartilhamento de Arquivos
+Name[ro]=Partajare fiÅŸiere
+Name[ru]=СовмеÑтное иÑпользование файлов
+Name[se]=Fiilajuohkkin
+Name[sk]=Zdieľanie súborov
+Name[sl]=Souporaba datotek
+Name[sr]=Дељење фајлова
+Name[sr@Latn]=Deljenje fajlova
+Name[sv]=Filutdelning
+Name[ta]=கோபà¯à®ªà¯ பகிரà¯à®µà¯
+Name[tg]=ИÑтифодабарии муштараки файлҳо
+Name[tr]=Dosya Paylaşımı
+Name[uk]=Спільний доÑтуп до файлів
+Name[uz]=Fayl bilan boʻlishish
+Name[uz@cyrillic]=Файл билан бўлишиш
+Name[zh_CN]=文件共享
+Name[zh_HK]=檔案分享
+Name[zh_TW]=檔案分享
+
+Comment=Enable or disable file sharing
+Comment[be]=Уключыць/выключыць выкарыÑтанне агульных Ñ€ÑÑурÑаў
+Comment[bg]=ÐаÑтройване ÑподелÑнето на файлове
+Comment[bn]=ফাইল ভাগাভাগি সকà§à¦°à¦¿à§Ÿ অথবা নিষà§à¦•à§à¦°à¦¿à§Ÿ করà§à¦¨
+Comment[bs]=UkljuÄi ili iskljuÄi dijeljenje datoteka
+Comment[ca]=Habilita o deshabilita la compartició de fitxers
+Comment[cs]=Povolit nebo zakázat sdílení souborů
+Comment[da]=Aktivér eller deaktivér fildeling
+Comment[de]=Dateifreigabe aktivieren/deaktivieren
+Comment[el]=ΕνεÏγοποίηση ή απενεÏγοποίηση της κοινής χÏήσης αÏχείων
+Comment[eo]=Åœalti aÅ­ malÅalti dosierkomunigadon
+Comment[es]=Habilitar o deshabilitar el compartir archivos
+Comment[et]=Failide jagamise lubamine või keelamine
+Comment[eu]=Gaitu edo ezgaitu fitxategi partekatzea
+Comment[fa]=Ùعال یا غیرÙعال‌سازی اشتراک پرونده
+Comment[fi]=Ota tiedostojen jakaminen käyttöön tai pois käytöstä
+Comment[fr]=Activer ou désactiver le partage de fichiers
+Comment[gl]=Habilitar ou deshabilitar a compartición de fichieros
+Comment[he]=×פשר ×ו מנע שיתוף קבצי×
+Comment[hu]=A fájlmegosztás ki-be kapcsolása
+Comment[is]=Virkja eða slökkva á skráardeilingu um net
+Comment[it]=Abilita o disabilita la condivisione dei file
+Comment[ja]=ファイル共有を有効ã¾ãŸã¯ç„¡åŠ¹ã«ã—ã¾ã™
+Comment[ka]=ფáƒáƒ˜áƒšáƒ—რგáƒáƒ–იáƒáƒ áƒ”ბის ჩáƒáƒ áƒ—ვრáƒáƒœ გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=Файл ортақтаÑтыруды Ñ€Ò±Ò›Ñат ету/етпеу
+Comment[km]=អនុញ្ញាហឬ មិន​អនុញ្ញាážâ€‹áž€áž¶ážšâ€‹áž…ែក​រំ​លែក​ឯកសារ
+Comment[lt]=Įjungti ar išjungti bylų dalinimąsi
+Comment[mk]=Овозможува или оневозможува делење на датотеки
+Comment[nb]=Slå på og av fildeling
+Comment[nds]=Dateifreegaven an- oder utmaken
+Comment[ne]=फाइल साà¤à¥‡à¤¦à¤¾à¤°à¥€ सकà¥à¤·à¤® वा अकà¥à¤·à¤® पारà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Bestanden delen in- en uitschakelen
+Comment[nn]=Slå på eller av fildeling
+Comment[pl]=Włącza lub wyłącza współdzielenie plików
+Comment[pt]=Activa ou desactiva a partilha de ficheiros
+Comment[pt_BR]=Habilita ou desabilita o compartilhamento de arquivos
+Comment[ro]=Activează sau dezactivează partajarea de fişiere
+Comment[ru]=Включить или выключить общий доÑтуп к файлам
+Comment[se]=Suova dahje ale suova fiilajuohkkima
+Comment[sk]=Povolenie alebo zakázanie zdieľania súborov
+Comment[sl]=OmogoÄi ali onemogoÄi souporabo datotek
+Comment[sr]=Укључи или иÑкључи дељење фајлова
+Comment[sr@Latn]=UkljuÄi ili iskljuÄi deljenje fajlova
+Comment[sv]=Aktivera eller inaktivera filutdelning
+Comment[ta]=கோபà¯à®ªà¯ பகிரà¯à®µà¯ˆ செயலà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯ அலà¯à®²à®¤à¯ செயலà¯à®¨à¯€à®•à¯à®•à¯
+Comment[tg]=Фаъол Ñохтан Ñ‘ хомӯш кардани иÑтифобадарии муштараки файлҳо
+Comment[tr]=Dosya paylaşımını etkinleştir/kapat
+Comment[uk]=Ð£Ð²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñпільного доÑтупу до файлів
+Comment[zh_CN]=å¯ç”¨æˆ–ç¦ç”¨æ–‡ä»¶å…±äº«
+Comment[zh_HK]=啟用或åœç”¨æª”案分享
+Comment[zh_TW]=開啟或關閉檔案分享
+
+Keywords=Share
+Keywords[be]=Ðгульны Ñ€ÑÑурÑ
+Keywords[bg]=наÑтройки, локална, мрежа, ÑподелÑне, share
+Keywords[br]=Rannañ
+Keywords[bs]=dijeljenje
+Keywords[ca]=Compartit
+Keywords[cs]=sdílení
+Keywords[da]=Del
+Keywords[de]=Freigabe
+Keywords[el]=ΚοινόχÏηστο
+Keywords[eo]=Komunaĵo
+Keywords[es]=Compartir
+Keywords[et]=Jagamine
+Keywords[fa]=مشترک
+Keywords[fi]=Jakaminen,Jako
+Keywords[fr]=partage
+Keywords[gl]=Compartir
+Keywords[he]=שיתוף, share
+Keywords[hu]=Megosztás
+Keywords[is]=Deild
+Keywords[it]=Condivisione
+Keywords[ja]=共有
+Keywords[ka]=სáƒáƒ–იáƒáƒ áƒ
+Keywords[km]=ការ​ចែក​រំ​លែក
+Keywords[lt]=Share,dalinimasis,dalintis,pasidalinti
+Keywords[mk]=Делена
+Keywords[nb]=Dele
+Keywords[nds]=Delen,Freegaav,Freegaven
+Keywords[ne]=साà¤à¥‡à¤¦à¤¾à¤°
+Keywords[nl]=delen
+Keywords[nn]=deling
+Keywords[pa]=ਸਾਂà¨
+Keywords[pl]=współdzielenie,pliki
+Keywords[pt]=Partilhar
+Keywords[pt_BR]=compartilhamento de arquivos
+Keywords[ro]=partajare
+Keywords[ru]=Общий реÑурÑ
+Keywords[sk]=zdieľaný disk,share
+Keywords[sl]=Souporaba
+Keywords[sr]=Share,дељење
+Keywords[sr@Latn]=Share,deljenje
+Keywords[sv]=Dela
+Keywords[ta]=பகிரà¯à®µà¯
+Keywords[tg]=Муштаракан доштан
+Keywords[tr]=PaylaÅŸ
+Keywords[uk]=Спільний реÑурÑ
+Keywords[zh_CN]=Share,共享
+
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
diff --git a/filesharing/simple/fileshare.h b/filesharing/simple/fileshare.h
new file mode 100644
index 00000000..120b93f6
--- /dev/null
+++ b/filesharing/simple/fileshare.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (c) 2002 Laurent Montel <lmontel@mandrakesoft.com>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef __fileshare_h__
+#define __fileshare_h__
+
+#include <kfileitem.h>
+
+#include "kcmodule.h"
+class QRadioButton;
+class QLabel;
+class QBoxLayout;
+class ControlCenterGUI;
+class QListViewItem;
+
+class KFileShareConfig : public KCModule
+{
+ Q_OBJECT
+
+ public:
+ KFileShareConfig(QWidget *parent, const char *name, const QStringList &);
+
+ virtual void load();
+ virtual void save();
+ virtual void defaults();
+ virtual QString quickHelp() const;
+
+ protected:
+ ControlCenterGUI* m_ccgui;
+ QString m_fileShareGroup;
+ bool m_restricted;
+ bool m_rootPassNeeded;
+ QString m_smbConf;
+ void showShareDialog(const KFileItemList & files);
+ bool addGroupAccessesToFile(const QString & file);
+ bool removeGroupAccessesFromFile(const QString & file);
+ bool setGroupAccesses();
+
+ protected slots:
+ void configChanged() { emit changed( true ); };
+ void updateShareListView();
+ void allowedUsersBtnClicked();
+
+ virtual void addShareBtnClicked();
+ virtual void changeShareBtnClicked();
+ virtual void removeShareBtnClicked();
+
+};
+
+class PropertiesPageDlg : public KDialogBase
+{
+Q_OBJECT
+public:
+ PropertiesPageDlg(QWidget * parent, KFileItemList files);
+ ~PropertiesPageDlg() {};
+ bool hasChanged();
+protected:
+ PropertiesPage* m_page;
+
+protected slots:
+ virtual void slotOk();
+
+};
+
+
+
+#endif
diff --git a/filesharing/simple/groupconfigdlg.cpp b/filesharing/simple/groupconfigdlg.cpp
new file mode 100644
index 00000000..4d62faa9
--- /dev/null
+++ b/filesharing/simple/groupconfigdlg.cpp
@@ -0,0 +1,418 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+
+#include <qradiobutton.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qvbox.h>
+#include <qhbox.h>
+
+#include <klocale.h>
+#include <kuser.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kinputdialog.h>
+#include <klistbox.h>
+#include <kprocess.h>
+#include <kpushbutton.h>
+#include <kcombobox.h>
+
+#include "groupconfiggui.h"
+#include "groupconfigdlg.h"
+
+
+static QString groupListToString(const QValueList<KUserGroup> & list);
+static QString prettyString(const KUser &user);
+static QString fromPrettyString(const QString & s);
+static void removeList(QValueList<KUser> & from, const QValueList<KUser> & that);
+static bool userMod(const QString & user, const QValueList<KUserGroup> & groups);
+
+
+
+GroupConfigDlg::GroupConfigDlg(QWidget * parent,
+ const QString & fileShareGroup, bool restricted,
+ bool rootPassNeeded, bool simpleSharing)
+ : KDialogBase(parent,"groupconfigdlg", true,
+ i18n("Allowed Users"), Ok|Cancel, Ok, true) ,
+ m_fileShareGroup(fileShareGroup),
+ m_restricted(restricted) ,
+ m_rootPassNeeded(rootPassNeeded),
+ m_simpleSharing(simpleSharing)
+
+{
+ initGUI();
+
+ setFileShareGroup(m_fileShareGroup);
+}
+
+GroupConfigDlg::~GroupConfigDlg() {
+}
+
+void GroupConfigDlg::initUsers() {
+ m_origUsers = m_fileShareGroup.users();
+ m_users = m_origUsers;
+}
+
+void GroupConfigDlg::initGUI() {
+ m_gui = new GroupConfigGUI(this);
+ setMainWidget(m_gui);
+ setFileShareGroup(m_fileShareGroup);
+
+ m_gui->allUsersRadio->setChecked(!m_restricted);
+ m_gui->groupUsersRadio->setChecked(m_restricted);
+ m_gui->writeAccessChk->setChecked(!m_rootPassNeeded);
+
+ connect( m_gui->addBtn, SIGNAL(clicked()),
+ this, SLOT(slotAddUser()));
+ connect( m_gui->removeBtn, SIGNAL(clicked()),
+ this, SLOT(slotRemoveUser()));
+ connect( m_gui->otherGroupBtn, SIGNAL(clicked()),
+ this, SLOT(slotChangeGroup()));
+
+ if (m_simpleSharing) {
+ // if simple sharing users never need the root password
+ m_gui->writeAccessChk->setDisabled(true);
+ }
+}
+
+void GroupConfigDlg::updateListBox() {
+ m_gui->listBox->clear();
+ QValueList<KUser>::iterator it;
+ for ( it = m_users.begin(); it != m_users.end(); ++it ) {
+ m_gui->listBox->insertItem(prettyString(*it));
+ kdDebug(5009) << "GroupConfigDlg::updateListBox: " << (*it).loginName() << endl;
+ }
+}
+
+QString prettyString(const KUser &user) {
+ return user.fullName()+" ("+user.loginName()+")";
+}
+
+QString fromPrettyString(const QString & s) {
+ // Jan Schaefer (jan)
+ // i j
+ int i = s.find('(');
+ int j = s.find(')');
+ QString loginName = s.mid(i+1,j-i-1);
+ return loginName;
+}
+
+bool GroupConfigDlg::restricted() {
+ return m_restricted;
+}
+
+void GroupConfigDlg::slotAddUser() {
+ QValueList<KUser> allUsers = KUser::allUsers();
+
+ removeList(allUsers,m_users);
+
+ if (allUsers.count()==0) {
+ KMessageBox::information(this,
+ i18n("All users are in the %1 group already.")
+ .arg(m_fileShareGroup.name()));
+ return;
+ }
+
+ QStringList stringList;
+
+ QValueList<KUser>::iterator it;
+ for ( it = allUsers.begin(); it != allUsers.end(); ++it ) {
+ QString s = (*it).fullName()+" ("+(*it).loginName()+")";
+ stringList.append(s);
+ }
+
+ stringList.sort();
+
+ bool ok;
+ QString userName = KInputDialog::getItem(
+ i18n("Select User"),
+ i18n("Select a user:"),
+ stringList,
+ 0,
+ false,
+ &ok);
+
+ if (!ok)
+ return;
+
+ QString loginName = fromPrettyString(userName);
+ KUser user(loginName);
+ m_users.append(KUser(loginName));
+ updateListBox();
+}
+
+void removeList(QValueList<KUser> & from, const QValueList<KUser> & that) {
+ QValueList<KUser>::ConstIterator it;
+ for ( it = that.begin(); it != that.end(); ++it ) {
+ from.remove(*it);
+ }
+
+}
+
+bool GroupConfigDlg::addUser(const KUser & user, const KUserGroup & group) {
+ QValueList<KUserGroup> groups = user.groups();
+ groups.append(group);
+ if (!userMod(user.loginName(),groups)) {
+ KMessageBox::sorry(this,i18n("Could not add user '%1' to group '%2'")
+ .arg(user.loginName()).arg(group.name()));
+ return false;
+ }
+ return true;
+}
+
+
+bool GroupConfigDlg::removeUser(const KUser & user, const KUserGroup & group) {
+ QValueList<KUserGroup> groups = user.groups();
+ groups.remove(group);
+ if (!userMod(user.loginName(),groups)) {
+ KMessageBox::sorry(this,i18n("Could not remove user '%1' from group '%2'")
+ .arg(user.loginName()).arg(group.name()));
+ return false;
+ }
+ return true;
+}
+
+bool GroupConfigDlg::rootPassNeeded() {
+ return m_rootPassNeeded;
+}
+
+void GroupConfigDlg::slotOk() {
+ m_restricted = m_gui->groupUsersRadio->isChecked();
+ m_rootPassNeeded = ! m_gui->writeAccessChk->isChecked();
+ if (m_restricted && !m_fileShareGroup.isValid()) {
+ KMessageBox::sorry(this,i18n("You have to choose a valid group."));
+ return;
+ }
+
+ QValueList<KUser> addedUsers = m_users;
+ removeList(addedUsers,m_origUsers);
+ QValueList<KUser> removedUsers = m_origUsers;
+ removeList(removedUsers,m_users);
+
+ QValueList<KUser>::ConstIterator it;
+ for ( it = addedUsers.begin(); it != addedUsers.end(); ++it ) {
+ addUser(*it, m_fileShareGroup);
+ }
+
+ for ( it = removedUsers.begin(); it != removedUsers.end(); ++it ) {
+ removeUser(*it, m_fileShareGroup);
+ }
+
+
+ KDialogBase::slotOk();
+}
+
+bool userMod(const QString & user, const QValueList<KUserGroup> & groups) {
+ KProcess proc;
+ proc << "usermod" << "-G" << groupListToString(groups) << user;
+ return proc.start(KProcess::Block) && proc.normalExit();
+}
+
+void GroupConfigDlg::slotRemoveUser() {
+ QListBoxItem* item = m_gui->listBox->selectedItem();
+ if (!item)
+ return;
+
+ QString loginName = fromPrettyString(item->text());
+ KUser user(loginName);
+ m_users.remove(KUser(loginName));
+ updateListBox();
+ m_gui->removeBtn->setEnabled(false);
+}
+
+QString groupListToString(const QValueList<KUserGroup> & list) {
+ QValueList<KUserGroup>::ConstIterator it;
+ QString result;
+
+ for ( it = list.begin(); it != list.end(); ++it ) {
+ result+=(*it).name()+",";
+ }
+
+ // remove last ,
+ result.truncate(result.length()-1);
+ return result;
+}
+
+void GroupConfigDlg::slotChangeGroup() {
+ QValueList<KUserGroup> allGroups = KUserGroup::allGroups();
+
+ QStringList stringList;
+
+ QValueList<KUserGroup>::iterator it;
+ for ( it = allGroups.begin(); it != allGroups.end(); ++it ) {
+ QString s = (*it).name();
+ stringList.append(s);
+ }
+
+ stringList.sort();
+
+ KDialogBase* dlg = new KDialogBase(this,"groupconfigdlg", true,
+ i18n("Allowed Users"), Ok|Cancel, Ok, true);
+
+ QVBox* vbox = dlg->makeVBoxMainWidget();
+
+ QHBox* hbox = new QHBox(vbox);
+ QLabel* lbl = new QLabel(i18n("New file share group:"),hbox);
+ KComboBox* combo = new KComboBox(hbox);
+ combo->insertStringList(stringList);
+ combo->setEditable(true);
+ combo->setCurrentText(m_fileShareGroup.name());
+
+ QCheckBox* addChk = new QCheckBox(
+ i18n("Add users from the old file share group to the new one"),
+ vbox);
+
+ QCheckBox* removeUsersChk = new QCheckBox(
+ i18n("Remove users from old file share group"),
+ vbox);
+
+ QCheckBox* removeGroupChk = new QCheckBox(
+ i18n("Delete the old file share group"),
+ vbox);
+
+ if (dlg->exec() == QDialog::Accepted) {
+ QString groupName = combo->currentText();
+ if (groupName != m_fileShareGroup.name()) {
+ QString oldGroup = m_fileShareGroup.name();
+ if (allGroups.contains(KUserGroup(groupName)))
+ setFileShareGroup(KUserGroup(groupName));
+ else {
+ if (!createFileShareGroup(groupName)) {
+ delete dlg;
+ return;
+ }
+ }
+
+ if (removeGroupChk->isChecked())
+ deleteGroup(oldGroup);
+ else
+ if (removeUsersChk->isChecked())
+ emptyGroup(oldGroup);
+
+ if (addChk->isChecked()) {
+ addUsersToGroup(m_users,KUserGroup(groupName));
+ // reread the users
+ m_fileShareGroup = KUserGroup(groupName);
+ }
+
+
+ initUsers();
+ updateListBox();
+
+ }
+ }
+
+ delete dlg;
+
+}
+
+void GroupConfigDlg::setFileShareGroup(const KUserGroup & group) {
+ m_fileShareGroup = group;
+
+ if (m_fileShareGroup.isValid()) {
+ initUsers();
+ updateListBox();
+ m_gui->groupUsersRadio->setText(
+ i18n("Only users of the '%1' group are allowed to share folders")
+ .arg(m_fileShareGroup.name()));
+ m_gui->usersGrpBx->setTitle(i18n("Users of '%1' Group")
+ .arg(m_fileShareGroup.name()));
+ m_gui->otherGroupBtn->setText(i18n("Change Group..."));
+ m_gui->usersGrpBx->show();
+ } else {
+ m_gui->groupUsersRadio->setText(i18n("Only users of a certain group are allowed to share folders"));
+ m_gui->otherGroupBtn->setText(i18n("Choose Group..."));
+ m_gui->usersGrpBx->hide();
+ }
+
+
+
+}
+
+bool GroupConfigDlg::addUsersToGroup(QValueList<KUser> users,const KUserGroup & group) {
+ QValueList<KUser>::ConstIterator it;
+ bool result = true;
+ for ( it = users.begin(); it != users.end(); ++it ) {
+ if (!addUser(*it, group))
+ result = false;
+ }
+ return result;
+}
+
+bool GroupConfigDlg::emptyGroup(const QString & s) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(this,
+ i18n("Do you really want to remove all users from group '%1'?").arg(s), QString::null, KStdGuiItem::del(), KStdGuiItem::cancel())) {
+ return false;
+ }
+
+ QValueList<KUser> allUsers = KUser::allUsers();
+ bool result = true;
+ KUserGroup group(s);
+ QValueList<KUser>::ConstIterator it;
+ for ( it = allUsers.begin(); it != allUsers.end(); ++it ) {
+ if (!removeUser(*it, group))
+ result = false;
+ }
+ return result;
+}
+
+bool GroupConfigDlg::deleteGroup(const QString & s) {
+ if (KMessageBox::No == KMessageBox::questionYesNo(this,
+ i18n("Do you really want to delete group '%1'?").arg(s), QString::null, KStdGuiItem::del(), KStdGuiItem::cancel())) {
+ return false;
+ }
+
+ KProcess proc;
+ proc << "groupdel" << s;
+ bool result = proc.start(KProcess::Block) && proc.normalExit();
+ if (!result) {
+ KMessageBox::sorry(this,i18n("Deleting group '%1' failed.").arg(s));
+ }
+
+ return result;
+}
+
+bool GroupConfigDlg::createFileShareGroup(const QString & s) {
+ if (s.isEmpty()) {
+ KMessageBox::sorry(this,i18n("Please choose a valid group."));
+ return false;
+ }
+
+ if (KMessageBox::No == KMessageBox::questionYesNo(this,
+ i18n("This group '%1' does not exist. Should it be created?").arg(s), QString::null, i18n("Create"), i18n("Do Not Create")))
+ return false;
+
+ //debug("CreateFileShareGroup: "+s);
+ KProcess proc;
+ proc << "groupadd" << s;
+ bool result = proc.start(KProcess::Block) && proc.normalExit();
+ if (!result) {
+ KMessageBox::sorry(this,i18n("Creation of group '%1' failed.").arg(s));
+ } else {
+ setFileShareGroup(KUserGroup(s));
+ }
+
+ return result;
+}
+
+
+#include "groupconfigdlg.moc"
diff --git a/filesharing/simple/groupconfigdlg.h b/filesharing/simple/groupconfigdlg.h
new file mode 100644
index 00000000..79df8664
--- /dev/null
+++ b/filesharing/simple/groupconfigdlg.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef GROUPCONFIGDLG_H
+#define GROUPCONFIGDLG_H
+
+#include <kdialogbase.h>
+#include <qvaluelist.h>
+#include <kuser.h>
+
+class GroupConfigGUI;
+
+class GroupConfigDlg : public KDialogBase
+{
+Q_OBJECT
+public:
+ GroupConfigDlg(QWidget * parent, const QString & fileShareGroup, bool restricted,
+ bool rootPassNeeded, bool simpleSharing);
+ ~GroupConfigDlg();
+ KUserGroup fileShareGroup() { return m_fileShareGroup; }
+ bool restricted();
+ bool rootPassNeeded();
+protected:
+ GroupConfigGUI* m_gui;
+
+ void initGUI();
+ void initUsers();
+protected slots:
+ void slotAddUser();
+ void slotRemoveUser();
+ void slotChangeGroup();
+ void updateListBox();
+ virtual void slotOk();
+
+private:
+ bool createFileShareGroup(const QString & s);
+ bool deleteGroup(const QString & s);
+ bool emptyGroup(const QString & s);
+ bool addUser(const KUser & user, const KUserGroup & group);
+ bool removeUser(const KUser & user, const KUserGroup & group);
+ bool addUsersToGroup(QValueList<KUser> users,const KUserGroup & group);
+ void setFileShareGroup(const KUserGroup & group);
+
+ QValueList<KUser> m_origUsers;
+ QValueList<KUser> m_users;
+ KUserGroup m_fileShareGroup;
+ bool m_restricted;
+ bool m_rootPassNeeded;
+ bool m_simpleSharing;
+};
+
+#endif
diff --git a/filesharing/simple/groupconfiggui.ui b/filesharing/simple/groupconfiggui.ui
new file mode 100644
index 00000000..c12e1d2c
--- /dev/null
+++ b/filesharing/simple/groupconfiggui.ui
@@ -0,0 +1,200 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupConfigGUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupConfigGUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>521</width>
+ <height>371</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>allUsersRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Allow all users to share folders</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>groupUsersRadio</cstring>
+ </property>
+ <property name="text">
+ <string>Only users of the '%1' group are allowed to share folders</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>usersGrpBx</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Users of '%1' Group</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListBox" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>listBox</cstring>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>removeBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Remove User</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>addBtn</cstring>
+ </property>
+ <property name="text">
+ <string>Add User</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>writeAccessChk</cstring>
+ </property>
+ <property name="text">
+ <string>Group members can share folders without root password</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>180</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>otherGroupBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Change Group...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>groupUsersRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>usersGrpBx</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>groupUsersRadio</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>otherGroupBtn</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>listBox</sender>
+ <signal>selectionChanged(QListBoxItem*)</signal>
+ <receiver>GroupConfigGUI</receiver>
+ <slot>listBox_selectionChanged(QListBoxItem*)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="local" impldecl="in implementation">groupconfiggui.ui.h</include>
+</includes>
+<slots>
+ <slot>listBox_selectionChanged( QListBoxItem * i )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/filesharing/simple/groupconfiggui.ui.h b/filesharing/simple/groupconfiggui.ui.h
new file mode 100644
index 00000000..35e82f7f
--- /dev/null
+++ b/filesharing/simple/groupconfiggui.ui.h
@@ -0,0 +1,14 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void GroupConfigGUI::listBox_selectionChanged( QListBoxItem * i)
+{
+ removeBtn->setEnabled(i);
+}
diff --git a/filesharing/simple/krichtextlabel.cpp b/filesharing/simple/krichtextlabel.cpp
new file mode 100644
index 00000000..0157a8f0
--- /dev/null
+++ b/filesharing/simple/krichtextlabel.cpp
@@ -0,0 +1,112 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "krichtextlabel.h"
+
+#include <qtooltip.h>
+#include <qstylesheet.h>
+#include <qsimplerichtext.h>
+
+#include <kglobalsettings.h>
+
+static QString qrichtextify( const QString& text )
+{
+ if ( text.isEmpty() || text[0] == '<' )
+ return text;
+
+ QStringList lines = QStringList::split('\n', text);
+ for(QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ {
+ *it = QStyleSheet::convertFromPlainText( *it, QStyleSheetItem::WhiteSpaceNormal );
+ }
+
+ return lines.join(QString::null);
+}
+
+KRichTextLabel::KRichTextLabel( const QString &text , QWidget *parent, const char *name )
+ : QLabel ( parent, name ) {
+ m_defaultWidth = QMIN(400, KGlobalSettings::desktopGeometry(this).width()*2/5);
+ setAlignment( Qt::WordBreak );
+ setText(text);
+}
+
+KRichTextLabel::KRichTextLabel( QWidget *parent, const char *name )
+ : QLabel ( parent, name ) {
+ m_defaultWidth = QMIN(400, KGlobalSettings::desktopGeometry(this).width()*2/5);
+ setAlignment( Qt::WordBreak );
+}
+
+void KRichTextLabel::setDefaultWidth(int defaultWidth)
+{
+ m_defaultWidth = defaultWidth;
+ updateGeometry();
+}
+
+QSizePolicy KRichTextLabel::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum, false);
+}
+
+QSize KRichTextLabel::minimumSizeHint() const
+{
+ QString qt_text = qrichtextify( text() );
+ int pref_width = 0;
+ int pref_height = 0;
+ QSimpleRichText rt(qt_text, font());
+ pref_width = m_defaultWidth;
+ rt.setWidth(pref_width);
+ int used_width = rt.widthUsed();
+ if (used_width <= pref_width)
+ {
+ while(true)
+ {
+ int new_width = (used_width * 9) / 10;
+ rt.setWidth(new_width);
+ int new_height = rt.height();
+ if (new_height > pref_height)
+ break;
+ used_width = rt.widthUsed();
+ if (used_width > new_width)
+ break;
+ }
+ pref_width = used_width;
+ }
+ else
+ {
+ if (used_width > (pref_width *2))
+ pref_width = pref_width *2;
+ else
+ pref_width = used_width;
+ }
+
+ return QSize(pref_width, rt.height());
+}
+
+QSize KRichTextLabel::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+void KRichTextLabel::setText( const QString &text ) {
+ QLabel::setText(text);
+}
+
+void KRichTextLabel::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "krichtextlabel.moc"
diff --git a/filesharing/simple/krichtextlabel.h b/filesharing/simple/krichtextlabel.h
new file mode 100644
index 00000000..35087fac
--- /dev/null
+++ b/filesharing/simple/krichtextlabel.h
@@ -0,0 +1,65 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KRICHTEXTLABEL_H
+#define KRICHTEXTLABEL_H
+
+#include <qlabel.h>
+
+#include <kdelibs_export.h>
+
+/**
+ * @short A replacement for QLabel that supports richtext and proper layout management
+ *
+ * @author Waldo Bastian <bastian@kde.org>
+ */
+
+/*
+ * QLabel
+ */
+class KDEUI_EXPORT KRichTextLabel : public QLabel {
+ Q_OBJECT
+
+public:
+ /**
+ * Default constructor.
+ */
+ KRichTextLabel( QWidget *parent, const char *name = 0 );
+ KRichTextLabel( const QString &text, QWidget *parent, const char *name = 0 );
+
+ int defaultWidth() const { return m_defaultWidth; }
+ void setDefaultWidth(int defaultWidth);
+
+ virtual QSize minimumSizeHint() const;
+ virtual QSize sizeHint() const;
+ QSizePolicy sizePolicy() const;
+
+public slots:
+ void setText( const QString & );
+
+protected:
+ int m_defaultWidth;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KRichTextLabelPrivate;
+ KRichTextLabelPrivate *d;
+};
+
+#endif // KRICHTEXTLABEL_H
diff --git a/install-sh b/install-sh
new file mode 100755
index 00000000..67c94290
--- /dev/null
+++ b/install-sh
@@ -0,0 +1,238 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f "$src" -o -d "$src" ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd "$src" $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/kdenetwork.lsm b/kdenetwork.lsm
new file mode 100644
index 00000000..0f60d3f2
--- /dev/null
+++ b/kdenetwork.lsm
@@ -0,0 +1,11 @@
+Begin4
+Title: kdenetwork
+Version: 3.5.10
+Entered-date: 2008-08-26
+Description: Network related utilities for the K Desktop Environment (KDE)
+Keywords: KDE X11 desktop Qt
+Author: http://bugs.kde.org/ (KDE Bugtracking System)
+Primary-site: http://www.kde.org/download/
+Platforms: Unix, Qt
+Copying-policy: GPL, Artistic
+End
diff --git a/kdict/AUTHORS b/kdict/AUTHORS
new file mode 100644
index 00000000..b81599fe
--- /dev/null
+++ b/kdict/AUTHORS
@@ -0,0 +1,25 @@
+Developers:
+Christian Gebauer <gebauer@kde.org>
+Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+
+Documentation:
+Christian Gebauer <gebauer@kde.org>
+
+Translators:
+Wolfram Diestel <wolfram@steloj.de>
+Otto Bruggeman <otto.bruggeman@home.nl>
+Mattias Newzella <newzella@linux.nu>
+Andris Maziks <andris.m@delfi.lv>
+Jaime Robles <jaime@kde.org>
+Pedro Morais <morais@kde.org>
+Andrey S. Cherepanov <andrey_tiger@i.am>
+Claudiu Costin <claudiuc@geocities.com>
+Erik Kjær Pedersen <erik@binghamton.edu>
+Stanislav Visnovsky <visnovsky@nenya.ms.mff.cuni.cz>
+Andy Rysin <arysin@yahoo.com>
+Dimitris Kamenopoulos <el97146@mail.ntua.gr>
+Meni Livne <meni@mail.com>
+Matthias Kiefer <kiefer@kde.org>
+Sinohara <shinobo@leo.bekkoame.ne.jp>
+Tamas Szanto <tszanto@mol.hu>
+Görkem Çetin <gorkem@kde.org> \ No newline at end of file
diff --git a/kdict/ChangeLog b/kdict/ChangeLog
new file mode 100644
index 00000000..d9513c75
--- /dev/null
+++ b/kdict/ChangeLog
@@ -0,0 +1,79 @@
+0.5.3
+ + recognize http and ftp urls in cross references
+ + convert characters that are incompatible with html
+ like <,>,& into &amp, etc.
+
+0.5.2
+ + Kdict is now part of the offical kdenetwork package
+ + support for IPv6 and socks-proxies
+ + up-to-date docbook-handbook
+ + code cleanup
+
+0.5
+ + dcop interface
+ + panel applet
+ + bugfixes: session management, small problems
+ with the toolbar editor
+
+0.4.1
+ + fixed CSS-issues with KDE 2.0.x
+ + fixed saving of results as html file
+
+0.4
+ + ported to KDE2
+ + full unicode support
+ + improved portability, should work in non-linux environments now
+ + the german gui-translation is missing in this release,
+ I will add it later on
+ + documentation is not yet converted to docbook
+
+0.3.1
+ + essential bugfix for older, libc5-based linux systems
+ (Thanks to Osvaldo Fornaro for testing)
+ + mini-howto for installing a local DICT-server
+
+0.3
+ + integrated implementation of the DICT-protocol,
+ dict(1) is no longer needed.
+ + match mode: At first all matches for a query are
+ displayed in a separate list, the user can then
+ choose the most interesting definitions.
+ + database sets: The user can create individual
+ compilations of the available databases and use
+ them in the same way as a single database.
+ + nicer/more configurable html-layout
+ + find dialog for locating text in the definitions
+ + the user can paste text into Kdict with the middle
+ mouse button. Kdict starts a new query with the pasted text
+ (analogue to Netscape).
+ + command to show detailed database information is
+ implemented ("show info")
+ + speed improvements:
+ * faster html-generation
+ * results are stored in a local cache, so they
+ are displayed instantly when the users wants to
+ browse back.
+ * Kdict holds the connection to the server
+ for a configurable amount of time.
+ + various gui improvements:
+ * improved statusbar
+ * unified preferences dialog
+ * all toolbars are now removable and remember
+ their position.
+ * new toolbar icons
+ + ...
+
+0.2
+ fixed some bugs (and introduced new ones ;-)
+ added various features:
+ + saving/printing of the queryresult
+ + query history
+ + improved html output
+ + improved command line interface
+ + html help
+ + Preferences dialogbox for fonts, colors, etc.
+ + Synonyms get displayed and handled like hyperlinks
+ + Automatically lookup of a word in the X clipboard
+ + ...
+
+0.1 Initial release
diff --git a/kdict/LICENSE b/kdict/LICENSE
new file mode 100644
index 00000000..4def90fc
--- /dev/null
+++ b/kdict/LICENSE
@@ -0,0 +1,125 @@
+kdict - The KDE Dictionary Client
+
+Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+copyright (C) 1998 by Matthias Hoelzer <hoelzer@kde.org>
+
+
+ Preamble
+
+ The intent of this document is to state the conditions under which a
+ Package may be copied, such that the Copyright Holder maintains some
+ semblance of artistic control over the development of the package,
+ while giving the users of the package the right to use and
+ distribute the Package in a more-or-less customary fashion, plus the
+ right to make reasonable modifications.
+
+ Definitions:
+
+ * "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ * "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes of
+ the Copyright Holder.
+
+ * "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ * "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ * "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people
+ involved, and so on. (You will not be required to justify it to
+ the Copyright Holder, but only to the computing community at
+ large as a market that must bear the fee.)
+
+ * "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+ 1. You may make and give away verbatim copies of the source form of
+ the Standard Version of this Package without restriction, provided
+ that you duplicate all of the original copyright notices and
+ associated disclaimers.
+
+ 2. You may apply bug fixes, portability fixes and other
+ modifications derived from the Public Domain or from the Copyright
+ Holder. A Package modified in such a way shall still be considered
+ the Standard Version.
+
+ 3. You may otherwise modify your copy of this Package in any way,
+ provided that you insert a prominent notice in each changed file
+ stating how and when you changed that file, and provided that you do
+ at least ONE of the following:
+
+ a) place your modifications in the Public Domain or
+ otherwise make them Freely Available, such as by posting
+ said modifications to Usenet or an equivalent medium, or
+ placing the modifications on a major archive site such as
+ ftp.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation
+ or organization.
+
+ c) rename any non-standard executables so the names do not
+ conflict with standard executables, which must also be
+ provided, and provide a separate manual page for each
+ non-standard executable that clearly documents how it
+ differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright
+ Holder.
+
+ 4. You may distribute the programs of this Package in object code or
+ executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and
+ library files, together with instructions (in the manual
+ page or equivalent) on where to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable
+ source of the Package with your modifications.
+
+ c) accompany any non-standard executables with their
+ corresponding Standard Version executables, giving the
+ non-standard executables non-standard names, and clearly
+ documenting the differences in manual pages (or
+ equivalent), together with instructions on where to get
+ the Standard Version.
+
+ d) make other distribution arrangements with the Copyright
+ Holder.
+
+ 5. You may charge a reasonable copying fee for any distribution of
+ this Package. You may charge any fee you choose for support of this
+ Package. You may not charge a fee for this Package itself. However,
+ you may distribute this Package in aggregate with other (possibly
+ commercial) programs as part of a larger (possibly commercial)
+ software distribution provided that you do not advertise this
+ Package as a product of your own.
+
+ 6. The scripts and library files supplied as input to or produced as
+ output from the programs of this Package do not automatically fall
+ under the copyright of this Package, but belong to whomever
+ generated them, and may be sold commercially, and may be aggregated
+ with this Package.
+
+ 7. C or perl subroutines supplied by you and linked into this
+ Package shall not be considered part of this Package.
+
+ 8. The name of the Copyright Holder may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+ 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
+
+
+
diff --git a/kdict/Makefile.am b/kdict/Makefile.am
new file mode 100644
index 00000000..5cb60755
--- /dev/null
+++ b/kdict/Makefile.am
@@ -0,0 +1,33 @@
+## Makefile.am for kdict
+
+KDE_CXXFLAGS = $(USE_THREADS)
+
+SUBDIRS = applet pics
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = kdict.la
+
+# set the include path for X, qt and KDE
+AM_CPPFLAGS = $(all_includes)
+
+kdict_la_LDFLAGS = $(KDE_RPATH) $(all_libraries) -module $(KDE_PLUGIN)
+kdict_la_LIBADD = $(LIB_KFILE) $(LIB_KHTML) $(LIBPTHREAD) $(LIBRESOLV)
+kdict_la_SOURCES = dcopinterface.skel main.cpp actions.cpp dict.cpp options.cpp \
+ queryview.cpp toplevel.cpp sets.cpp matchview.cpp application.cpp
+
+# these are the headers for your project
+noinst_HEADERS = actions.h dict.h options.h queryview.h toplevel.h sets.h matchview.h application.h
+
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kdict.pot
+
+KDE_ICON = AUTO
+
+# this is where the kdelnk file will go
+xdg_apps_DATA = kdict.desktop
+
+rcdir = $(kde_datadir)/kdict
+rc_DATA = kdictui.rc
diff --git a/kdict/README b/kdict/README
new file mode 100644
index 00000000..894983ab
--- /dev/null
+++ b/kdict/README
@@ -0,0 +1,47 @@
+Kdict - The KDE Dictionary Client
+Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+copyright (c) 1998 by Matthias Hoelzer <hoelzer@kde.org>
+
+maintained by Christian Gebauer <gebauer@kde.org>
+
+Homepage: http://www.rhrk.uni-kl.de/~gebauerc/kdict/
+
+See LICENSE for the license of this programm.
+
+**************************************************************************
+
+Kdict is a graphical client for the DICT Protocol.
+It enables you to search through dictionary-like databases
+for a word or phrase, then displays suitable definitions.
+
+Kdict trys to ease basic as well as advanced queries. A separate list
+offers a convenient way to deal with the enormous number of matching
+words that a advanced query can return.
+
+The remainder of Kdict's user interface resembles a web browser:
+For instance, you can jump to the definition of a synonym by
+simply clicking on the highlighted word. The back/forward functionality
+is also implemented, enabling you to quickly go back to the result of
+previous queries.
+
+Kdict is able to process the content of the X clipboard, so it's easy
+to combine Kdict with your web browser or text editor.
+
+Kdict is now a real client, no additional software is needed.
+
+If your machine is behind a firewall, has no permanent internet
+connection or the server of dict.org is too slow for you, you can
+set up your own local server, all you need is available at
+http://www.dict.org.
+The advantages of a local server are optimal performance and
+the ability to install additional databases of your choice.
+The handbook contains a small tutorial for installation
+and links to databases.
+
+**************************************************************************
+
+For installing Kdict you need:
+
+- KDE 3.x
+- POSIX Threads
+
diff --git a/kdict/TODO b/kdict/TODO
new file mode 100644
index 00000000..5a1d2446
--- /dev/null
+++ b/kdict/TODO
@@ -0,0 +1,2 @@
+* offline-support:
+ accessing local database-files without running a dictd-server \ No newline at end of file
diff --git a/kdict/actions.cpp b/kdict/actions.cpp
new file mode 100644
index 00000000..5ca1aade
--- /dev/null
+++ b/kdict/actions.cpp
@@ -0,0 +1,335 @@
+/* -------------------------------------------------------------
+
+ actions.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ DictComboAction, special KAction subclasses used
+ DictLabelAction, in the toolbar
+ DictButtonAction
+
+ ------------------------------------------------------------- */
+
+#include "actions.h"
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+#include <kcombobox.h>
+#include <ktoolbar.h>
+
+
+DictComboAction::DictComboAction( const QString &text, QObject *parent, const char *name,
+ bool editable, bool autoSized )
+ : KAction( text, 0, parent, name ), m_editable(editable), m_autoSized(autoSized), m_compMode(KGlobalSettings::completionMode())
+{
+}
+
+
+DictComboAction::~DictComboAction()
+{
+}
+
+
+int DictComboAction::plug( QWidget *widget, int index )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar* bar = static_cast<KToolBar*>( widget );
+ int id_ = KAction::getToolButtonID();
+
+ m_combo = new KComboBox(m_editable,bar);
+ m_combo->setCompletionMode(m_compMode);
+
+ bar->insertWidget( id_, m_combo->sizeHint().width(), m_combo, index );
+ bar->setItemAutoSized(id_,m_autoSized);
+
+ if ( m_combo ) {
+ connect(bar->getCombo(id_), SIGNAL(activated(const QString&)), SLOT(slotComboActivated(const QString&)));
+ connect(bar->getCombo(id_), SIGNAL(activated(int)), SLOT(slotComboActivated(int)));
+
+ if (m_editable)
+ m_combo->setInsertionPolicy( QComboBox::NoInsertion );
+ }
+
+ addContainer( bar, id_ );
+ connect( bar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+ return containerCount() - 1;
+ }
+
+ return -1;
+}
+
+
+void DictComboAction::unplug( QWidget *widget )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *bar = (KToolBar *)widget;
+
+ int idx = findContainer( bar );
+
+ if ( idx != -1 )
+ {
+ bar->removeItem( itemId( idx ) );
+ removeContainer( idx );
+ }
+
+ return;
+ }
+}
+
+
+QWidget* DictComboAction::widget()
+{
+ return m_combo;
+}
+
+
+void DictComboAction::setFocus()
+{
+ if (m_combo)
+ m_combo->setFocus();
+}
+
+
+QString DictComboAction::currentText() const
+{
+ if (m_combo)
+ return m_combo->currentText();
+ else
+ return QString::null;
+}
+
+void DictComboAction::selectAll()
+{
+ if (m_combo)
+ {
+ m_combo->lineEdit()->selectAll();
+ m_combo->lineEdit()->setFocus();
+ }
+}
+
+
+void DictComboAction::setEditText(const QString &s)
+{
+ if (m_combo && m_editable)
+ m_combo->setEditText(s);
+}
+
+
+void DictComboAction::setCurrentItem(int index)
+{
+ if (m_combo)
+ m_combo->setCurrentItem(index);
+}
+
+
+void DictComboAction::clearEdit()
+{
+ if (m_combo && m_editable)
+ m_combo->clearEdit();
+}
+
+
+void DictComboAction::clear()
+{
+ if (m_combo) {
+ m_combo->clear();
+ if (m_editable && m_combo->completionObject())
+ m_combo->completionObject()->clear();
+ }
+}
+
+
+void DictComboAction::setList(QStringList items)
+{
+ if (m_combo) {
+ m_combo->clear();
+ m_combo->insertStringList(items);
+ if (m_editable && m_combo->completionObject())
+ m_combo->completionObject()->setItems(items);
+ if (!m_autoSized)
+ m_combo->setFixedWidth(m_combo->sizeHint().width());
+ }
+}
+
+
+KGlobalSettings::Completion DictComboAction::completionMode()
+{
+ if (m_combo)
+ return m_combo->completionMode();
+ else
+ return m_compMode;
+ }
+
+
+void DictComboAction::setCompletionMode(KGlobalSettings::Completion mode)
+{
+ if (m_combo)
+ m_combo->setCompletionMode(mode);
+ else
+ m_compMode = mode;
+}
+
+
+void DictComboAction::slotComboActivated(int i)
+{
+ emit(activated(i));
+}
+
+
+void DictComboAction::slotComboActivated(const QString &s)
+{
+ emit(activated(s));
+}
+
+
+//*********************************************************************************
+
+
+DictLabelAction::DictLabelAction( const QString &text, QObject *parent, const char *name )
+ : KAction( text, 0, parent, name )
+{
+}
+
+
+DictLabelAction::~DictLabelAction()
+{
+}
+
+
+int DictLabelAction::plug( QWidget *widget, int index )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *tb = (KToolBar *)widget;
+
+ int id = KAction::getToolButtonID();
+
+ QLabel *label = new QLabel( text(), widget, "kde toolbar widget" );
+ label->setMinimumWidth(label->sizeHint().width());
+ label->setBackgroundMode( Qt::PaletteButton );
+ label->setAlignment(AlignCenter | AlignVCenter);
+ label->adjustSize();
+
+ tb->insertWidget( id, label->width(), label, index );
+
+ addContainer( tb, id );
+
+ connect( tb, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+
+ m_label = label;
+
+ return containerCount() - 1;
+ }
+
+ return -1;
+}
+
+
+void DictLabelAction::unplug( QWidget *widget )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *bar = (KToolBar *)widget;
+
+ int idx = findContainer( bar );
+
+ if ( idx != -1 )
+ {
+ bar->removeItem( itemId( idx ) );
+ removeContainer( idx );
+ }
+
+ return;
+ }
+}
+
+
+void DictLabelAction::setBuddy(QWidget *buddy)
+{
+ if (m_label && buddy)
+ m_label->setBuddy(buddy);
+}
+
+
+//*********************************************************************************
+
+
+DictButtonAction::DictButtonAction( const QString& text, QObject* receiver,
+ const char* slot, QObject* parent, const char* name )
+ : KAction( text, 0, receiver, slot, parent, name )
+{
+}
+
+
+DictButtonAction::~DictButtonAction()
+{
+}
+
+
+int DictButtonAction::plug( QWidget *widget, int index )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *tb = (KToolBar *)widget;
+
+ int id = KAction::getToolButtonID();
+
+ QPushButton *button = new QPushButton( text(), widget );
+ button->adjustSize();
+ connect(button,SIGNAL(clicked()),this,SLOT(activate()));
+ tb->insertWidget( id, button->width(), button, index );
+
+ addContainer( tb, id );
+
+ connect( tb, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+
+ m_button = button;
+
+ return containerCount() - 1;
+ }
+
+ return -1;
+}
+
+
+void DictButtonAction::unplug( QWidget *widget )
+{
+ if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *bar = (KToolBar *)widget;
+
+ int idx = findContainer( bar );
+
+ if ( idx != -1 )
+ {
+ bar->removeItem( itemId( idx ) );
+ removeContainer( idx );
+ }
+ }
+}
+
+
+int DictButtonAction::widthHint()
+{
+ if (m_button)
+ return m_button->sizeHint().width();
+ else
+ return 0;
+}
+
+
+void DictButtonAction::setWidth(int width)
+{
+ if (m_button)
+ m_button->setFixedWidth(width);
+}
+
+#include "actions.moc"
diff --git a/kdict/actions.h b/kdict/actions.h
new file mode 100644
index 00000000..568a9f7c
--- /dev/null
+++ b/kdict/actions.h
@@ -0,0 +1,111 @@
+/* -------------------------------------------------------------
+
+ actions.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ DictComboAction, special KAction subclasses used
+ DictLabelAction, in the toolbar
+ DictButtonAction
+
+ ------------------------------------------------------------- */
+
+#ifndef _ACTIONS_H_
+#define _ACTIONS_H_
+
+#include <qguardedptr.h>
+#include <qptrlist.h>
+#include <kaction.h>
+#include <kglobalsettings.h>
+
+class KComboBox;
+class QLabel;
+class QPushButton;
+
+
+class DictComboAction : public KAction
+{
+ Q_OBJECT
+
+ public:
+ DictComboAction( const QString& text, QObject* parent,
+ const char* name, bool editable, bool autoSized );
+ ~DictComboAction();
+
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+
+ QWidget* widget();
+ void setFocus();
+
+ QString currentText() const;
+ void selectAll();
+ void setEditText(const QString &s);
+ void setCurrentItem(int index);
+ void clearEdit();
+
+ void clear();
+ void setList(QStringList items);
+
+ KGlobalSettings::Completion completionMode();
+ void setCompletionMode(KGlobalSettings::Completion mode);
+
+ signals:
+ void activated(int);
+ void activated(const QString&);
+
+ private slots:
+ void slotComboActivated(int);
+ void slotComboActivated(const QString&);
+
+ private:
+ QGuardedPtr<KComboBox> m_combo;
+ bool m_editable, m_autoSized;
+ KGlobalSettings::Completion m_compMode;
+};
+
+
+class DictLabelAction : public KAction
+{
+ Q_OBJECT
+
+ public:
+ DictLabelAction( const QString &text, QObject *parent = 0, const char *name = 0 );
+ ~DictLabelAction();
+
+ virtual int plug( QWidget *widget, int index = -1 );
+ virtual void unplug( QWidget *widget );
+
+ void setBuddy(QWidget *buddy);
+
+ private:
+ QGuardedPtr<QLabel> m_label;
+
+};
+
+
+class DictButtonAction : public KAction
+{
+ Q_OBJECT
+
+ public:
+ DictButtonAction( const QString& text, QObject* receiver,
+ const char* slot, QObject* parent, const char* name );
+ ~DictButtonAction();
+
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+
+ int widthHint();
+ void setWidth(int width);
+
+ private:
+ QGuardedPtr<QPushButton> m_button;
+};
+
+#endif
diff --git a/kdict/applet/Makefile.am b/kdict/applet/Makefile.am
new file mode 100644
index 00000000..db1f4bc2
--- /dev/null
+++ b/kdict/applet/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = kdict_panelapplet.la
+
+kdict_panelapplet_la_SOURCES = kdictapplet.cpp
+
+METASOURCES = AUTO
+noinst_HEADERS = kdictapplet.h
+
+lnkdir = $(kde_datadir)/kicker/applets
+lnk_DATA = kdictapplet.desktop
+
+EXTRA_DIST = $(lnk_DATA)
+
+kdict_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kdict_panelapplet_la_LIBADD = $(LIB_KSYCOCA) $(LIB_KDEUI)
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kdictapplet.pot
diff --git a/kdict/applet/kdictapplet.cpp b/kdict/applet/kdictapplet.cpp
new file mode 100644
index 00000000..ad907864
--- /dev/null
+++ b/kdict/applet/kdictapplet.cpp
@@ -0,0 +1,405 @@
+/* -------------------------------------------------------------
+
+ kdictapplet.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2001 Christian Gebauer <gebauer@kde.org>
+
+ The applet is loosely based on the "Run" applet included in KDE.
+ Copyright (c) 2000 Matthias Elter <elter@kde.org> (Artistic License)
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ PopupBox helper class
+ DictApplet a small kicker-applet
+
+ ------------------------------------------------------------- */
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qtimer.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+
+#include <kconfig.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kprocess.h>
+
+#include "kdictapplet.h"
+
+//********* PopupBox ********************************************
+
+PopupBox::PopupBox()
+ : QHBox(0, 0, WStyle_Customize | WType_Popup ), popupEnabled(true)
+{
+}
+
+
+PopupBox::~PopupBox()
+{}
+
+
+bool PopupBox::showBox()
+{
+ if (!popupEnabled) // prevents that the popup is shown again immediatly
+ return false;
+ else {
+ show();
+ return true;
+ }
+}
+
+
+void PopupBox::hideEvent(QHideEvent *)
+{
+ emit(hidden());
+ popupEnabled = false;
+ QTimer::singleShot(100, this, SLOT(enablePopup()));
+}
+
+
+void PopupBox::enablePopup()
+{
+ popupEnabled = true;
+}
+
+
+//********* DictApplet ********************************************
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile)
+ {
+ KGlobal::locale()->insertCatalogue("kdictapplet");
+ return new DictApplet(configFile, KPanelApplet::Stretch, 0, parent, "kdictapplet");
+ }
+}
+
+
+DictApplet::DictApplet(const QString& configFile, Type type, int actions, QWidget *parent, const char *name)
+ : KPanelApplet(configFile, type, actions, parent, name), waiting(0)
+{
+ // first the widgets for a horizontal panel
+ baseWidget = new QWidget(this);
+ QGridLayout *baseLay = new QGridLayout(baseWidget,2,6,0,1);
+
+ textLabel = new QLabel(i18n("Dictionary:"), baseWidget);
+ textLabel->setBackgroundOrigin(AncestorOrigin);
+ QFont f(textLabel->font());
+ f.setPixelSize(12);
+ textLabel->setFont(f);
+ baseLay->addWidget(textLabel,0,1);
+ QToolTip::add(textLabel,i18n("Look up a word or phrase with Kdict"));
+
+ iconLabel = new QLabel(baseWidget);
+ iconLabel->setBackgroundOrigin(AncestorOrigin);
+ QPixmap pm = KGlobal::iconLoader()->loadIcon("kdict", KIcon::Panel, KIcon::SizeSmall, KIcon::DefaultState, 0L, true);
+ iconLabel->setPixmap(pm);
+ baseLay->addWidget(iconLabel,1,0);
+ iconLabel->setAlignment(Qt::AlignCenter | Qt::AlignVCenter);
+ iconLabel->setFixedWidth(pm.width()+4);
+ QToolTip::add(iconLabel,i18n("Look up a word or phrase with Kdict"));
+
+ f.setPixelSize(10);
+ clipboardBtn = new QPushButton(i18n("C"),baseWidget);
+ clipboardBtn->setBackgroundOrigin(AncestorOrigin);
+ clipboardBtn->setFont(f);
+ clipboardBtn->setFixedSize(16,16);
+ connect(clipboardBtn, SIGNAL(clicked()), SLOT(queryClipboard()));
+ baseLay->addWidget(clipboardBtn,0,3);
+ QToolTip::add(clipboardBtn,i18n("Define selected text"));
+
+ defineBtn = new QPushButton(i18n("D"),baseWidget);
+ defineBtn->setBackgroundOrigin(AncestorOrigin);
+ defineBtn->setFont(f);
+ defineBtn->setFixedSize(16,16);
+ defineBtn->setEnabled(false);
+ connect(defineBtn, SIGNAL(clicked()), SLOT(startDefine()));
+ baseLay->addWidget(defineBtn,0,4);
+ QToolTip::add(defineBtn,i18n("Define word/phrase"));
+
+ matchBtn = new QPushButton(i18n("M"),baseWidget);
+ matchBtn->setBackgroundOrigin(AncestorOrigin);
+ matchBtn->setFont(f);
+ matchBtn->setFixedSize(16,16);
+ matchBtn->setEnabled(false);
+ connect(matchBtn, SIGNAL(clicked()), SLOT(startMatch()));
+ baseLay->addWidget(matchBtn,0,5);
+ QToolTip::add(matchBtn,i18n("Find matching definitions"));
+
+ completionObject = new KCompletion();
+
+ internalCombo = new KHistoryCombo(baseWidget);
+ internalCombo->setBackgroundOrigin(AncestorOrigin);
+ internalCombo->setCompletionObject(completionObject);
+ internalCombo->setFocus();
+ internalCombo->clearEdit();
+ internalCombo->lineEdit()->installEventFilter( this );
+ connect(internalCombo, SIGNAL(returnPressed(const QString&)), SLOT(startQuery(const QString&)));
+ connect(internalCombo, SIGNAL(textChanged(const QString&)), SLOT(comboTextChanged(const QString&)));
+ QToolTip::add(internalCombo,i18n("Look up a word or phrase with Kdict"));
+
+ baseLay->addMultiCellWidget(internalCombo,1,1,1,5);
+
+ baseLay->setColStretch(2,1);
+
+ // widgets for a vertical panel
+ verticalBtn = new QPushButton(this);
+ connect(verticalBtn, SIGNAL(pressed()), SLOT(showExternalCombo()));
+ QToolTip::add(verticalBtn,i18n("Look up a word or phrase with Kdict"));
+
+ popupBox = new PopupBox();
+ popupBox->setFixedSize(160, 22);
+ connect(popupBox, SIGNAL(hidden()), SLOT(externalComboHidden()));
+ externalCombo = new KHistoryCombo(popupBox);
+ externalCombo->setCompletionObject(completionObject);
+ connect(externalCombo, SIGNAL(returnPressed(const QString&)), SLOT(startQuery(const QString&)));
+ externalCombo->setFixedSize(160, externalCombo->sizeHint().height());
+
+ connect(internalCombo, SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+ this, SLOT(updateCompletionMode(KGlobalSettings::Completion)));
+ connect(externalCombo, SIGNAL(completionModeChanged(KGlobalSettings::Completion)),
+ this, SLOT(updateCompletionMode(KGlobalSettings::Completion)));
+
+ // restore history and completion list
+ KConfig *c = config();
+ c->setGroup("General");
+
+ QStringList list = c->readListEntry("Completion list");
+ completionObject->setItems(list);
+ int mode = c->readNumEntry("Completion mode",
+ KGlobalSettings::completionMode());
+ internalCombo->setCompletionMode((KGlobalSettings::Completion)mode);
+ externalCombo->setCompletionMode((KGlobalSettings::Completion)mode);
+
+ list = c->readListEntry("History list");
+ internalCombo->setHistoryItems(list);
+ externalCombo->setHistoryItems(list);
+}
+
+
+DictApplet::~DictApplet()
+{
+ // save history and completion list
+ KConfig *c = config();
+ c->setGroup("General");
+
+ QStringList list = completionObject->items();
+ c->writeEntry("Completion list", list);
+ c->writeEntry("Completion mode", (int) internalCombo->completionMode());
+
+ list = internalCombo->historyItems();
+ c->writeEntry("History list", list);
+ c->sync();
+
+ delete completionObject;
+}
+
+
+int DictApplet::widthForHeight(int height) const
+{
+ if (height >= 38)
+ return textLabel->sizeHint().width()+55;
+ else
+ return textLabel->sizeHint().width()+25;
+}
+
+
+int DictApplet::heightForWidth(int width) const
+{
+ return width;
+}
+
+
+void DictApplet::resizeEvent(QResizeEvent*)
+{
+ if (orientation() == Horizontal) {
+ verticalBtn->hide();
+ baseWidget->show();
+ baseWidget->setFixedSize(width(),height());
+
+ if (height() < internalCombo->sizeHint().height())
+ internalCombo->setFixedHeight(height());
+ else
+ internalCombo->setFixedHeight(internalCombo->sizeHint().height());
+
+ if (height() >= 38) {
+ textLabel->show();
+ clipboardBtn->show();
+ defineBtn->show();
+ matchBtn->show();
+ iconLabel->hide();
+ internalCombo->setFixedWidth(width());
+ } else {
+ textLabel->hide();
+ clipboardBtn->hide();
+ defineBtn->hide();
+ matchBtn->hide();
+ iconLabel->show();
+ internalCombo->setFixedWidth(width()-iconLabel->width()-1);
+ }
+
+ baseWidget->updateGeometry();
+ } else { // orientation() == Vertical
+ verticalBtn->show();
+ baseWidget->hide();
+ verticalBtn->setFixedSize(width(),width());
+
+ KIcon::StdSizes sz = width() < 32 ? KIcon::SizeSmall : (width() < 48 ? KIcon::SizeMedium : KIcon::SizeLarge);
+ QPixmap pm = KGlobal::iconLoader()->loadIcon("kdict", KIcon::Panel, sz, KIcon::DefaultState, 0L, true);
+ verticalBtn->setPixmap(pm);
+ }
+}
+
+
+bool DictApplet::eventFilter( QObject *o, QEvent * e)
+{
+ if (e->type() == QEvent::MouseButtonRelease)
+ emit requestFocus();
+
+ return KPanelApplet::eventFilter(o, e);
+}
+
+
+void DictApplet::sendCommand(const QCString &fun, const QString &data)
+{
+ if (waiting > 0) {
+ waiting = 1;
+ delayedFunc = fun.copy();
+ delayedData = data;
+ return;
+ }
+
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isApplicationRegistered("kdict")) {
+ KApplication::startServiceByDesktopName("kdict");
+ waiting = 1;
+ delayedFunc = fun.copy();
+ delayedData = data;
+ QTimer::singleShot(100, this, SLOT(sendDelayedCommand()));
+ return;
+ } else {
+ QCStringList list = client->remoteObjects("kdict");
+ if (list.findIndex("KDictIface")==-1) {
+ waiting = 1;
+ delayedFunc = fun.copy();
+ delayedData = data;
+ QTimer::singleShot(100, this, SLOT(sendDelayedCommand()));
+ return;
+ }
+ }
+
+ client->send("kdict","default",fun,data);
+}
+
+
+void DictApplet::sendDelayedCommand()
+{
+ if (waiting > 100) { // timeout after ten seconds
+ waiting = 0;
+ return;
+ }
+
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isApplicationRegistered("kdict")) {
+ waiting++;
+ QTimer::singleShot(100, this, SLOT(sendDelayedCommand()));
+ return;
+ } else {
+ QCStringList list = client->remoteObjects("kdict");
+ if (list.findIndex("KDictIface")==-1) {
+ waiting++;
+ QTimer::singleShot(100, this, SLOT(sendDelayedCommand()));
+ return;
+ }
+ }
+
+ client->send("kdict","default",delayedFunc,delayedData);
+ waiting = 0;
+}
+
+
+void DictApplet::startQuery(const QString &s)
+{
+ QString query = s.stripWhiteSpace();
+ if (query.isEmpty())
+ return;
+
+ internalCombo->addToHistory(query);
+ externalCombo->addToHistory(query);
+ internalCombo->clearEdit();
+ externalCombo->clearEdit();
+
+ sendCommand("definePhrase(QString)",query);
+
+ if (orientation() == Vertical)
+ popupBox->hide();
+}
+
+
+void DictApplet::comboTextChanged(const QString &s)
+{
+ defineBtn->setEnabled(!s.isEmpty());
+ matchBtn->setEnabled(!s.isEmpty());
+}
+
+
+void DictApplet::queryClipboard()
+{
+ sendCommand("defineClipboardContent()",QString::null);
+}
+
+
+void DictApplet::startDefine()
+{
+ startQuery(internalCombo->currentText());
+}
+
+
+void DictApplet::startMatch()
+{
+ QString query = internalCombo->currentText().stripWhiteSpace();
+ internalCombo->addToHistory(query);
+ externalCombo->addToHistory(query);
+ internalCombo->clearEdit();
+ externalCombo->clearEdit();
+
+ sendCommand("matchPhrase(QString)",query);
+}
+
+
+void DictApplet::showExternalCombo()
+{
+ QPoint p;
+ if (position() == pLeft)
+ p = mapToGlobal(QPoint(-popupBox->width()-1, 0));
+ else
+ p = mapToGlobal(QPoint(width()+1, 0));
+ popupBox->move(p);
+ if (popupBox->showBox())
+ externalCombo->setFocus();
+ else
+ verticalBtn->setDown(false);
+}
+
+
+void DictApplet::externalComboHidden()
+{
+ verticalBtn->setDown(false);
+}
+
+void DictApplet::updateCompletionMode(KGlobalSettings::Completion mode)
+{
+ internalCombo->setCompletionMode(mode);
+ externalCombo->setCompletionMode(mode);
+}
+
+//--------------------------------
+
+#include "kdictapplet.moc"
diff --git a/kdict/applet/kdictapplet.desktop b/kdict/applet/kdictapplet.desktop
new file mode 100644
index 00000000..23d26f3f
--- /dev/null
+++ b/kdict/applet/kdictapplet.desktop
@@ -0,0 +1,147 @@
+[Desktop Entry]
+Comment=Lookup phrases in a dictionary
+Comment[af]=Opkyk frases in 'n woordeboek
+Comment[ar]=ابحث عن الكلمات ÙÙŠ القاموس
+Comment[az]=Lüğətdən kəlimələrə baxın
+Comment[be]=Пошук выразаў у Ñлоўніку
+Comment[bg]=ТърÑене на фрази в речника
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ অভিধানে শবà§à¦¦à¦¸à¦®à¦·à§à¦Ÿà¦¿à¦° খোà¦à¦œ করো
+Comment[bs]=Potražite fraze u rjeÄniku
+Comment[ca]=Cerca expressions en un diccionari
+Comment[cs]=VyhledávaÄ pojmů ve slovníku
+Comment[cy]=Edrych am ddywediadau mewn geiriadur
+Comment[da]=Slå sætninger op i en ordbog
+Comment[de]=Ausdrücke in einem Lexikon nachschlagen
+Comment[el]=Αναζήτηση φÏάσεων σε λεξικό
+Comment[eo]=Serĉi kapvortojn en vortaroj
+Comment[es]=Busca expresiones en un diccionario
+Comment[et]=Fraaside otsimine sõnaraamatust
+Comment[eu]=Bilatu esaldiak hiztegi batean
+Comment[fa]=مراجعه به واژه‌نامه برای عبارتها
+Comment[fi]=Hae lauseita sanakirjasta
+Comment[fr]=Recherche de phrases dans un dictionnaire
+Comment[ga]=Cuardaigh frásaí i bhfoclóir
+Comment[gl]=Buscar expresións no diccionario
+Comment[he]=חיפוש ×‘×™×˜×•×™×™× ×‘×ž×™×œ×•×Ÿ
+Comment[hi]=शबà¥à¤¦à¤•à¥‹à¤¶ में वाकà¥à¤¯à¤¾à¤‚शों को देखे
+Comment[hr]=Potraži fraze u rjeÄniku
+Comment[hu]=Szótárkezelő alkalmazás
+Comment[is]=Fletta upp í orðabók
+Comment[it]=Cerca frasi in un dizionario
+Comment[ja]=辞書ã§èªžå¥ã‚’検索
+Comment[ka]=ფრáƒáƒ–ების ლექსიკáƒáƒœáƒ¨áƒ˜ ძებნáƒ
+Comment[kk]=Сөздікте іздеу
+Comment[km]=រក​មើល​ប្រយោគ​នៅ​ក្នុង​វចនានុក្រម​មួយ
+Comment[ko]=사전ì—ì„œ 글귀를 찾아ì¤ë‹ˆë‹¤
+Comment[lt]=Ieškoti frazių žodyne
+Comment[lv]=SkatÄ«t frÄzes vÄrdnÄ«cÄ
+Comment[mk]=Барајте за изрази во речник
+Comment[mn]=Толь бичигÑÑÑ Ò¯Ð³ харах
+Comment[ms]=Mencari frasa di dalam kamus
+Comment[mt]=Fittex frażijiet fid-dizzjunarju
+Comment[nb]=Finn fraser i en ordbok
+Comment[nds]=Begrepen in en Wöörbook nakieken
+Comment[ne]=शबà¥à¤¦à¤•à¥‹à¤¶à¤®à¤¾ वाकà¥à¤¯à¤¾à¤‚श खोजी गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Zoek bepaalde uitdrukkingen op in een woordenboek
+Comment[nn]=Slå opp ord i ei ordboka
+Comment[nso]=Nyaka mantsu ka gare ga pukuntsu
+Comment[pl]=Szukanie zwrotów w słowniku
+Comment[pt]=Procura por frases num dicionário
+Comment[pt_BR]=Busca frases em um dicionário
+Comment[ro]=Caută fraze într-un dicţionar
+Comment[ru]=ПоиÑк Ñлов в Ñловаре
+Comment[se]=Oza sániid sátnegirjjis
+Comment[sk]=Hľadanie fráz v slovníku
+Comment[sl]=Iskanje Izrazov v slovarju
+Comment[sr]=Потражите фразе у речнику
+Comment[sr@Latn]=Potražite fraze u reÄniku
+Comment[sv]=Leta upp fraser i en ordlista
+Comment[ta]=ஒர௠அகராதியிலà¯à®³à¯à®³ தேடறà¯à®šà¯Šà®±à¯à®¤à¯Šà®Ÿà®°à¯à®•à®³à¯
+Comment[tg]=ҶуÑтуҷӯи ибораҳо дар луғат
+Comment[th]=ค้นหาวลีในพจนานุà¸à¸£à¸¡
+Comment[tr]=Sözlükten kelimelere bakın
+Comment[uk]=Пошук фраз у Ñловнику
+Comment[ven]=Todani fhungo kha bugu talutshedza maipfi
+Comment[wa]=Cweri des fråzes dins on diccionaire
+Comment[xh]=Jonga amabinzana kwincwadi enekcazelo zamagama
+Comment[zh_CN]=在字典中查找短语
+Comment[zh_HK]=在字典中尋找片語
+Comment[zh_TW]=在字典中尋找片語
+Comment[zu]=Bheka amagama kwisichazamagama
+Icon=
+Name=Dictionary
+Name[af]=Woordeboek
+Name[ar]=القاموس
+Name[az]=Lüğət
+Name[be]=Слоўнік
+Name[bg]=Речник
+Name[bn]=অভিধান
+Name[br]=Geriadur
+Name[bs]=RjeÄnik
+Name[ca]=Diccionari
+Name[cs]=Slovník
+Name[cy]=Geiriadur
+Name[da]=Ordbog
+Name[de]=Lexikon
+Name[el]=Λεξικό
+Name[eo]=Vortaro
+Name[es]=Diccionario
+Name[et]=Sõnaraamat
+Name[eu]=Hiztegia
+Name[fa]=واژه‌نامه
+Name[fi]=Sanakirja
+Name[fr]=Dictionnaire
+Name[ga]=Foclóir
+Name[gl]=Diccionario
+Name[he]=מילון
+Name[hi]=शबà¥à¤¦à¤•à¥‹à¤¶
+Name[hr]=RjeÄnik
+Name[hu]=Szótár
+Name[is]=Orðabók
+Name[it]=Dizionario
+Name[ja]=辞書
+Name[ka]=ლექსიკáƒáƒœáƒ˜
+Name[kk]=Сөздік
+Name[km]=វចនានុក្រម
+Name[ko]=사전
+Name[lt]=Žodynas
+Name[lv]=VÄrdnÄ«ca
+Name[mk]=Речник
+Name[mn]=Толь бичиг
+Name[ms]=Kamus
+Name[mt]=Dizzjunarju
+Name[nb]=Ordbok
+Name[nds]=Wöörbook
+Name[ne]=शबà¥à¤¦à¤•à¥‹à¤¶
+Name[nl]=Woordenboek
+Name[nn]=Ordbok
+Name[nso]=Pukuntsu
+Name[pa]=ਸ਼ਬਦ-ਕੋਸ਼
+Name[pl]=SÅ‚ownik
+Name[pt]=Dicionário
+Name[pt_BR]=Dicionário
+Name[ro]=Dicţionar
+Name[ru]=Словарь
+Name[se]=Sátnegirji
+Name[sk]=Slovník
+Name[sl]=Slovar
+Name[sr]=Речник
+Name[sr@Latn]=ReÄnik
+Name[sv]=Ordlista
+Name[ta]=அகராதி
+Name[tg]=Луғат
+Name[th]=พจนานุà¸à¸£à¸¡
+Name[tr]=Sözlük
+Name[uk]=Словник
+Name[uz]=Lugʻat
+Name[uz@cyrillic]=Луғат
+Name[ven]=Bugu yau talutshedza maipfi
+Name[wa]=Motî
+Name[xh]=Incwadi eneenkcazelo zamagama
+Name[zh_CN]=å­—å…¸
+Name[zh_HK]=å­—å…¸
+Name[zh_TW]=å­—å…¸
+Name[zu]=Isichazamagama
+Icon=kdict
+X-KDE-Library=kdict_panelapplet
+X-KDE-UniqueApplet=true
diff --git a/kdict/applet/kdictapplet.h b/kdict/applet/kdictapplet.h
new file mode 100644
index 00000000..a9738148
--- /dev/null
+++ b/kdict/applet/kdictapplet.h
@@ -0,0 +1,101 @@
+/* -------------------------------------------------------------
+
+ kdictapplet.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2001 Christian Gebauer <gebauer@kde.org>
+
+ The applet is loosely based on the "Run" applet included in KDE.
+ Copyright (c) 2000 Matthias Elter <elter@kde.org> (Artistic License)
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ PopupBox helper class
+ DictApplet a small kicker-applet
+
+ ------------------------------------------------------------- */
+
+#ifndef _DICTAPPLET_H_
+#define _DICTAPPLET_H_
+
+#include <qhbox.h>
+#include <kpanelapplet.h>
+
+class QLabel;
+class QPushButton;
+class KHistoryCombo;
+
+
+//********* PopupBox ********************************************
+
+class PopupBox : public QHBox
+{
+ Q_OBJECT
+
+public:
+ PopupBox();
+ ~PopupBox();
+
+ bool showBox();
+
+signals:
+ void hidden();
+
+public slots:
+ void enablePopup();
+
+protected:
+ void hideEvent(QHideEvent *);
+
+private:
+ bool popupEnabled;
+
+};
+
+//********* DictApplet ********************************************
+
+class DictApplet : public KPanelApplet
+{
+ Q_OBJECT
+
+public:
+ DictApplet(const QString& configFile, Type t = Stretch, int actions = 0, QWidget *parent = 0, const char *name = 0);
+ virtual ~DictApplet();
+
+ int widthForHeight(int height) const;
+ int heightForWidth(int width) const;
+
+protected:
+ void resizeEvent(QResizeEvent*);
+ bool eventFilter( QObject *, QEvent * );
+
+ void sendCommand(const QCString &fun, const QString &data);
+
+protected slots:
+ void sendDelayedCommand();
+ void startQuery(const QString&);
+ void comboTextChanged(const QString&);
+ void queryClipboard();
+ void startDefine();
+ void startMatch();
+ void showExternalCombo();
+ void externalComboHidden();
+ void updateCompletionMode(KGlobalSettings::Completion mode);
+
+private:
+ KHistoryCombo *internalCombo, *externalCombo;
+ KCompletion *completionObject;
+ QLabel *textLabel, *iconLabel;
+ QPushButton *verticalBtn, *clipboardBtn, *defineBtn, *matchBtn;
+ QWidget *baseWidget;
+ PopupBox *popupBox;
+
+ int waiting;
+ QCString delayedFunc;
+ QString delayedData;
+
+};
+
+#endif
diff --git a/kdict/application.cpp b/kdict/application.cpp
new file mode 100644
index 00000000..28e3a398
--- /dev/null
+++ b/kdict/application.cpp
@@ -0,0 +1,71 @@
+/* -------------------------------------------------------------
+
+ application.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ ------------------------------------------------------------- */
+
+#include <kwin.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+
+#include "application.h"
+#include "toplevel.h"
+
+
+Application::Application()
+ : KUniqueApplication()
+{
+ m_mainWindow = new TopLevel( 0, "mainWindow" );
+}
+
+
+Application::~Application()
+{
+ delete m_mainWindow;
+}
+
+
+int Application::newInstance()
+{
+ kdDebug(5004) << "Application::newInstance()" << endl;
+ KUniqueApplication::newInstance();
+
+ // process parameters...
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ m_mainWindow->show();
+
+ if (args->isSet("clipboard"))
+ {
+ m_mainWindow->defineClipboard();
+ }
+ else
+ {
+ if (args->count()>0)
+ {
+ QString phrase;
+ for (int i=0;i<args->count();i++)
+ {
+ phrase += QString::fromLocal8Bit(args->arg(i));
+ if (i+1 < args->count())
+ phrase += " ";
+ }
+ m_mainWindow->define(phrase);
+ }
+ else
+ {
+ m_mainWindow->normalStartup();
+ }
+ }
+
+ return 0;
+}
+
+//--------------------------------
+
+#include "application.moc"
diff --git a/kdict/application.h b/kdict/application.h
new file mode 100644
index 00000000..eddb7f44
--- /dev/null
+++ b/kdict/application.h
@@ -0,0 +1,39 @@
+/* -------------------------------------------------------------
+
+ application.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ ------------------------------------------------------------- */
+
+#ifndef APPLICATION_H
+#define APPLICATION_H
+
+#include <kuniqueapplication.h>
+#include <qguardedptr.h>
+
+#define KDICT_VERSION "0.6"
+
+class TopLevel;
+
+class Application : public KUniqueApplication
+{
+ Q_OBJECT
+
+ public:
+ Application();
+ ~Application();
+
+ /** Create new instance of Kdict. Make the existing
+ main window active if Kdict is already running */
+ int newInstance();
+
+ private:
+ QGuardedPtr<TopLevel> m_mainWindow;
+
+};
+
+#endif
diff --git a/kdict/dcopinterface.h b/kdict/dcopinterface.h
new file mode 100644
index 00000000..f088a842
--- /dev/null
+++ b/kdict/dcopinterface.h
@@ -0,0 +1,54 @@
+/* -------------------------------------------------------------
+
+ dcopinterface.h (part of The KDE Dictionary Client)
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ KDictDCOPInterface abstract base class that defines the
+ DCOP interface of Kdict
+
+ ------------------------------------------------------------- */
+
+#ifndef _DCOPINTERFACE_H
+#define _DCOPINTERFACE_H
+
+#include <dcopobject.h>
+#include <qstringlist.h>
+
+class KDictIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+
+ /** Quit Kdict **/
+ virtual void quit() = 0;
+ virtual void makeActiveWindow() = 0;
+
+ /** define/match a word/phrase **/
+ virtual void definePhrase(QString phrase) = 0;
+ virtual void matchPhrase(QString phrase) = 0;
+ virtual void defineClipboardContent() = 0;
+ virtual void matchClipboardContent() = 0;
+
+ /** get info **/
+ virtual QStringList getDatabases() = 0;
+ virtual QString currentDatabase() = 0;
+ virtual QStringList getStrategies() = 0;
+ virtual QString currentStrategy() = 0;
+
+ /** set current database/strategy (returns true on success) **/
+ virtual bool setDatabase(QString db) = 0;
+ virtual bool setStrategy(QString strategy) = 0;
+
+ /** navigate in history **/
+ virtual bool historyGoBack() = 0;
+ virtual bool historyGoForward() = 0;
+
+};
+
+#endif
diff --git a/kdict/dict.cpp b/kdict/dict.cpp
new file mode 100644
index 00000000..b36f1ac1
--- /dev/null
+++ b/kdict/dict.cpp
@@ -0,0 +1,1632 @@
+/* -------------------------------------------------------------
+
+ dict.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias Hölzer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ JobData used for data transfer between Client and Interface
+ DictAsyncClient all network related stuff happens here in asynchrous thread
+ DictInterface interface for DictAsyncClient, job management
+
+ ------------------------------------------------------------- */
+
+#include <config.h>
+
+#include "application.h"
+#include "options.h"
+#include "dict.h"
+
+#include <qregexp.h>
+#include <qtextcodec.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kmdcodec.h>
+#include <kextsock.h>
+#include <ksocks.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+
+//********* JobData ******************************************
+
+
+JobData::JobData(QueryType Ntype,bool NnewServer,QString const& Nserver,int Nport,
+ int NidleHold, int Ntimeout, int NpipeSize, QString const& Nencoding, bool NAuthEnabled,
+ QString const& Nuser, QString const& Nsecret, unsigned int NheadLayout)
+ : type(Ntype), error(ErrNoErr), canceled(false), numFetched(0), newServer(NnewServer),server(Nserver), port(Nport),
+ timeout(Ntimeout), pipeSize(NpipeSize), idleHold(NidleHold), encoding(Nencoding), authEnabled(NAuthEnabled),
+ user(Nuser), secret(Nsecret), headLayout(NheadLayout)
+{}
+
+
+//********* DictAsyncClient *************************************
+
+DictAsyncClient::DictAsyncClient(int NfdPipeIn, int NfdPipeOut)
+: job(0L), inputSize(10000), fdPipeIn(NfdPipeIn),
+ fdPipeOut(NfdPipeOut), tcpSocket(-1), idleHold(0)
+{
+ input = new char[inputSize];
+}
+
+
+DictAsyncClient::~DictAsyncClient()
+{
+ if (-1!=tcpSocket)
+ doQuit();
+ delete [] input;
+}
+
+
+void* DictAsyncClient::startThread(void* pseudoThis)
+{
+ DictAsyncClient* newthis = (DictAsyncClient*) (pseudoThis);
+
+ if (0!=pthread_setcanceltype(PTHREAD_CANCEL_ENABLE,NULL))
+ qWarning("pthread_setcanceltype failed!");
+ if (0!= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL))
+ qWarning("pthread_setcanceltype failed!");
+
+ signal(SIGPIPE,SIG_IGN); // ignore sigpipe
+
+ newthis->waitForWork();
+ return NULL;
+}
+
+
+void DictAsyncClient::insertJob(JobData *newJob)
+{
+ if (!job) // don't overwrite existing job pointer
+ job = newJob;
+}
+
+
+void DictAsyncClient::removeJob()
+{
+ job = 0L;
+}
+
+
+void DictAsyncClient::waitForWork()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+ int selectRet;
+ char buf;
+
+ while (true) {
+ if (tcpSocket != -1) { // we are connected, hold the connection for xx secs
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ tv.tv_sec = idleHold;
+ tv.tv_usec = 0;
+ selectRet = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ if (selectRet == 0) {
+ doQuit(); // nothing happend...
+ } else {
+ if (((selectRet > 0)&&(!FD_ISSET(fdPipeIn,&fdsR)))||(selectRet == -1))
+ closeSocket();
+ }
+ }
+
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ } while (select(FD_SETSIZE, &fdsR, NULL, NULL, NULL)<0); // don't get tricked by signals
+
+ clearPipe();
+
+ if (job) {
+ if ((tcpSocket!=-1)&&(job->newServer))
+ doQuit();
+
+ codec = QTextCodec::codecForName(job->encoding.latin1());
+ input[0] = 0; //terminate string
+ thisLine = input;
+ nextLine = input;
+ inputEnd = input;
+ timeout = job->timeout;
+ idleHold = job->idleHold;
+
+ if (tcpSocket==-1)
+ openConnection();
+
+ if (tcpSocket!=-1) { // connection is ready
+ switch (job->type) {
+ case JobData::TDefine :
+ define();
+ break;
+ case JobData::TGetDefinitions :
+ getDefinitions();
+ break;
+ case JobData::TMatch :
+ match();
+ break;
+ case JobData::TShowDatabases :
+ showDatabases();
+ break;
+ case JobData::TShowDbInfo :
+ showDbInfo();
+ break;
+ case JobData::TShowStrategies :
+ showStrategies();
+ break;
+ case JobData::TShowInfo :
+ showInfo();
+ break;
+ case JobData::TUpdate :
+ update();
+ }
+ }
+ clearPipe();
+ }
+ if (write(fdPipeOut,&buf,1) == -1) // emit stopped signal
+ ::perror( "waitForJobs()" );
+ }
+}
+
+
+void DictAsyncClient::define()
+{
+ QString command;
+
+ job->defines.clear();
+ QStringList::iterator it;
+ for (it = job->databases.begin(); it != job->databases.end(); ++it) {
+ command = "define ";
+ command += *it;
+ command += " \"";
+ command += job->query;
+ command += "\"\r\n";
+ job->defines.append(command);
+ }
+
+ if (!getDefinitions())
+ return;
+
+ if (job->numFetched == 0) {
+ job->strategy = ".";
+ if (!match())
+ return;
+ job->result = QString::null;
+ if (job->numFetched == 0) {
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("No definitions found for \'%1'.").arg(job->query));
+ resultAppend("</p>\n</html></body>");
+ } else {
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("No definitions found for \'%1\'. Perhaps you mean:").arg(job->query));
+ resultAppend("</p>\n<table width=\"100%\" cols=2>\n");
+
+ QString lastDb;
+ QStringList::iterator it;
+ for (it = job->matches.begin(); it != job->matches.end(); ++it) {
+ int pos = (*it).find(' ');
+ if (pos != -1) {
+ if (lastDb != (*it).left(pos)) {
+ if (lastDb.length() > 0)
+ resultAppend("</pre></td></tr>\n");
+ lastDb = (*it).left(pos);
+ resultAppend("<tr valign=top><td width=25%><pre><b>");
+ resultAppend(lastDb);
+ resultAppend(":</b></pre></td><td width=75%><pre>");
+ }
+ if ((*it).length() > (unsigned int)pos+2) {
+ resultAppend("<a href=\"http://define/");
+ resultAppend((*it).mid(pos+2, (*it).length()-pos-3));
+ resultAppend("\">");
+ resultAppend((*it).mid(pos+2, (*it).length()-pos-3));
+ resultAppend("</a> ");
+ }
+ }
+ }
+ resultAppend("\n</pre></td></tr></table>\n</body></html>");
+ job->numFetched = 0;
+ }
+ }
+}
+
+
+QString htmlString(const QString &raw)
+{
+ unsigned int len=raw.length();
+ QString ret;
+
+ for (unsigned int i=0; i<len; i++) {
+ switch (raw[i]) {
+ case '&' : ret += "&amp"; break;
+ case '<' : ret+="&lt;"; break;
+ case '>' : ret+="&gt;"; break;
+ default : ret+=raw[i];
+ }
+ }
+
+ return ret;
+}
+
+
+QString generateDefineLink(const QString &raw)
+{
+ QRegExp http("http://[^\\s<>()\"|\\[\\]{}]+");
+ QRegExp ftp("ftp://[^\\s<>()\"|\\[\\]{}]+");
+ int matchPos=0, matchLen=0;
+ bool httpMatch=false;
+ QString ret;
+
+ matchPos = http.search(raw);
+ matchLen = http.matchedLength();
+ if (-1 != matchPos) {
+ httpMatch = true;
+ } else {
+ matchPos = ftp.search(raw);
+ matchLen = ftp.matchedLength();
+ httpMatch = false;
+ }
+
+ if (-1 != matchPos) {
+ ret = htmlString(raw.left(matchPos));
+ ret += "<a href=\"http://";
+ if (httpMatch) {
+ ret += "realhttp/";
+ ret += raw.mid(matchPos+7, matchLen-7);
+ } else {
+ ret += "realftp/";
+ ret += raw.mid(matchPos+6, matchLen-6);
+ }
+ ret += "\">";
+ ret += htmlString(raw.mid(matchPos, matchLen));
+ ret += "</a>";
+ ret += htmlString(raw.right(raw.length()-matchLen-matchPos));
+ } else {
+ ret = "<a href=\"http://define/";
+ ret += raw;
+ ret += "\">";
+ ret += htmlString(raw);
+ ret += "</a>";
+ }
+
+ return ret;
+}
+
+
+bool DictAsyncClient::getDefinitions()
+{
+ QCString lastDb,bracketBuff;
+ QStrList hashList;
+ char *s;
+ int defCount,response;
+
+ // html header...
+ resultAppend("<body>\n");
+
+ while (job->defines.count()>0) {
+ defCount = 0;
+ cmdBuffer = "";
+ do {
+ QStringList::iterator it = job->defines.begin();
+ cmdBuffer += codec->fromUnicode(*it);
+ defCount++;
+ job->defines.remove(it);
+ } while ((job->defines.count()>0)&&((int)cmdBuffer.length()<job->pipeSize));
+
+ if (!sendBuffer())
+ return false;
+
+ for (;defCount > 0;defCount--) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 552: // define: 552 No match
+ break;
+ case 150: { // define: 150 n definitions retrieved - definitions follow
+ bool defineDone = false;
+ while (!defineDone) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 151: { // define: 151 word database name - text follows
+ char *db = strchr(thisLine, '\"');
+ if (db)
+ db = strchr(db+1, '\"');
+ char *dbdes = 0;
+ if (db) {
+ db+=2; // db points now on database name
+ dbdes = strchr(db,' ');
+ if (dbdes) {
+ dbdes[0] = 0; // terminate database name
+ dbdes+=2; // dbdes points now on database description
+ }
+ } else {
+ job->error = JobData::ErrServerError;
+ job->result = QString::null;
+ resultAppend(thisLine);
+ doQuit();
+ return false;
+ }
+
+ int oldResPos = job->result.length();
+
+ if (((job->headLayout<2)&&(lastDb!=db))||(job->headLayout==2)) {
+ lastDb = db;
+ resultAppend("<p class=\"heading\">\n");
+ if (dbdes)
+ resultAppend(codec->toUnicode(dbdes,strlen(dbdes)-1));
+ resultAppend(" [<a href=\"http://dbinfo/");
+ resultAppend(db);
+ resultAppend("\">");
+ resultAppend(db);
+ resultAppend("</a>]</p>\n");
+ } else
+ if (job->headLayout==1)
+ resultAppend("<hr>\n");
+
+ resultAppend("<pre><p class=\"definition\">\n");
+
+ KMD5 context;
+ bool bodyDone = false;
+ while (!bodyDone) {
+ if (!getNextLine())
+ return false;
+ char *line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ bodyDone = true;
+ }
+ if (!bodyDone) {
+ context.update(QCString(line));
+ if (!bracketBuff.isEmpty()) {
+ s = strchr(line,'}');
+ if (!s)
+ resultAppend(bracketBuff.data());
+ else {
+ s[0] = 0;
+ bracketBuff.remove(0,1); // remove '{'
+ bracketBuff += line;
+ line = s+1;
+ resultAppend(generateDefineLink(codec->toUnicode(bracketBuff)));
+ }
+ bracketBuff = "";
+ }
+ s = strchr(line,'{');
+ while (s) {
+ resultAppend(htmlString(codec->toUnicode(line,s-line)));
+ line = s;
+ s = strchr(line,'}');
+ if (s) {
+ s[0] = 0;
+ line++;
+ resultAppend(generateDefineLink(codec->toUnicode(line)));
+ line = s+1;
+ s = strchr(line,'{');
+ } else {
+ bracketBuff = line;
+ bracketBuff += "\n";
+ line = 0;
+ s = 0;
+ }
+ }
+ if (line) {
+ resultAppend(htmlString(codec->toUnicode(line)));
+ resultAppend("\n");
+ }
+ }
+ }
+ resultAppend("</p></pre>\n");
+
+ if (hashList.find(context.hexDigest())>=0) // duplicate??
+ job->result.truncate(oldResPos); // delete the whole definition
+ else {
+ hashList.append(context.hexDigest());
+ job->numFetched++;
+ }
+
+ break; }
+ case 250: { // define: 250 ok (optional timing information here)
+ defineDone = true;
+ break; }
+ default: {
+ handleErrors();
+ return false; }
+ }
+ }
+ break; }
+ default:
+ handleErrors();
+ return false;
+ }
+ }
+ }
+
+ resultAppend("</body></html>\n");
+ return true;
+}
+
+
+bool DictAsyncClient::match()
+{
+ QStringList::iterator it = job->databases.begin();
+ int response;
+ cmdBuffer = "";
+
+ while (it != job->databases.end()) {
+ int send = 0;
+ do {
+ cmdBuffer += "match ";
+ cmdBuffer += codec->fromUnicode(*(it));
+ cmdBuffer += " ";
+ cmdBuffer += codec->fromUnicode(job->strategy);
+ cmdBuffer += " \"";
+ cmdBuffer += codec->fromUnicode(job->query);
+ cmdBuffer += "\"\r\n";
+ send++;
+ ++it;
+ } while ((it != job->databases.end())&&((int)cmdBuffer.length()<job->pipeSize));
+
+ if (!sendBuffer())
+ return false;
+
+ for (;send > 0;send--) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 552: // match: 552 No match
+ break;
+ case 152: { // match: 152 n matches found - text follows
+ bool matchDone = false;
+ while (!matchDone) {
+ if (!getNextLine())
+ return false;
+ char *line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ matchDone = true;
+ }
+ if (!matchDone) {
+ job->numFetched++;
+ job->matches.append(codec->toUnicode(line));
+ }
+ }
+ if (!nextResponseOk(250)) // match: "250 ok ..."
+ return false;
+ break; }
+ default:
+ handleErrors();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void DictAsyncClient::showDatabases()
+{
+ cmdBuffer = "show db\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(110)) // show db: "110 n databases present - text follows "
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Available Databases:"));
+ resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend("<tr valign=top><td width=25%><pre><a href=\"http://dbinfo/");
+ char *space = strchr(line,' ');
+ if (space) {
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("\">");
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("</a></pre></td><td width=75%><pre>");
+ line = space+1;
+ if (line[0]=='"') {
+ line++; // remove double quote
+ char *quote = strchr(line, '\"');
+ if (quote)
+ quote[0]=0;
+ }
+ } else { // hmmm, malformated line...
+ resultAppend("\"></a></pre></td><td width=75%>");
+ }
+ resultAppend(line);
+ resultAppend("</pre></td></tr>\n");
+ }
+ }
+ resultAppend("</table>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showDbInfo()
+{
+ cmdBuffer = "show info ";
+ cmdBuffer += codec->fromUnicode(job->query);
+ cmdBuffer += "\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(112)) // show info db: "112 database information follows"
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Database Information [%1]:").arg(job->query));
+ resultAppend("</p>\n<pre><p class=\"definition\">\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend(line);
+ resultAppend("\n");
+ }
+ }
+
+ resultAppend("</p></pre>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showStrategies()
+{
+ cmdBuffer = "show strat\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows "
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Available Strategies:"));
+ resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend("<tr valign=top><td width=25%><pre>");
+ char *space = strchr(line,' ');
+ if (space) {
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("</pre></td><td width=75%><pre>");
+ line = space+1;
+ if (line[0]=='"') {
+ line++; // remove double quote
+ char *quote = strchr(line, '\"');
+ if (quote)
+ quote[0]=0;
+ }
+ } else { // hmmm, malformated line...
+ resultAppend("</pre></td><td width=75%><pre>");
+ }
+ resultAppend(line);
+ resultAppend("</pre></td></tr>\n");
+ }
+ }
+ resultAppend("</table>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showInfo()
+{
+ cmdBuffer = "show server\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(114)) // show server: "114 server information follows"
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Server Information:"));
+ resultAppend("\n</p>\n<pre><p class=\"definition\">\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend(line);
+ resultAppend("\n");
+ }
+ }
+
+ resultAppend("</p></pre>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::update()
+{
+ cmdBuffer = "show strat\r\nshow db\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows "
+ return;
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ char *space = strchr(line,' ');
+ if (space) space[0] = 0; // terminate string, hack ;-)
+ job->strategies.append(codec->toUnicode(line));
+ }
+ }
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+
+ if (!nextResponseOk(110)) // show db: "110 n databases present - text follows "
+ return;
+
+ done = false;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ char *space = strchr(line,' ');
+ if (space) space[0] = 0; // terminate string, hack ;-)
+ job->databases.append(codec->toUnicode(line));
+ }
+ }
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+// connect, handshake and authorization
+void DictAsyncClient::openConnection()
+{
+ if (job->server.isEmpty()) {
+ job->error = JobData::ErrBadHost;
+ return;
+ }
+
+ KExtendedSocket ks;
+
+ ks.setAddress(job->server, job->port);
+ ks.setTimeout(job->timeout);
+ if (ks.connect() < 0) {
+ if (ks.status() == IO_LookupError)
+ job->error = JobData::ErrBadHost;
+ else if (ks.status() == IO_ConnectError) {
+ job->result = QString::null;
+ resultAppend(KExtendedSocket::strError(ks.status(), errno));
+ job->error = JobData::ErrConnect;
+ } else if (ks.status() == IO_TimeOutError)
+ job->error = JobData::ErrTimeout;
+ else {
+ job->result = QString::null;
+ resultAppend(KExtendedSocket::strError(ks.status(), errno));
+ job->error = JobData::ErrCommunication;
+ }
+
+ closeSocket();
+ return;
+ }
+ tcpSocket = ks.fd();
+ ks.release();
+
+ if (!nextResponseOk(220)) // connect: "220 text capabilities msg-id"
+ return;
+
+ cmdBuffer = "client \"Kdict ";
+ cmdBuffer += KDICT_VERSION;
+ cmdBuffer += "\"\r\n";
+
+ if (job->authEnabled)
+ if (strstr(thisLine,"auth")) { // skip auth if not supported
+ char *msgId = strrchr(thisLine,'<');
+
+ if ((!msgId)||(!job->user.length())) {
+ job->error = JobData::ErrAuthFailed;
+ closeSocket();
+ return;
+ }
+
+ KMD5 context;
+ context.update(QCString(msgId));
+ context.update(job->secret.local8Bit());
+
+ cmdBuffer += "auth " + job->user.local8Bit() + " ";
+ cmdBuffer += context.hexDigest();
+ cmdBuffer += "\r\n";
+ }
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(250)) // client: "250 ok ..."
+ return;
+
+ if (job->authEnabled)
+ if (!nextResponseOk(230)) // auth: "230 Authentication successful"
+ return;
+}
+
+
+void DictAsyncClient::closeSocket()
+{
+ if (-1 != tcpSocket) {
+ ::close(tcpSocket);
+ tcpSocket = -1;
+ }
+}
+
+
+// send "quit" without timeout, without checks, close connection
+void DictAsyncClient::doQuit()
+{
+ fd_set fdsW;
+ timeval tv;
+
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ int ret = KSocks::self()->select(FD_SETSIZE, NULL, &fdsW, NULL, &tv);
+
+ if (ret > 0) { // we can write...
+ cmdBuffer = "quit\r\n";
+ int todo = cmdBuffer.length();
+ KSocks::self()->write(tcpSocket,&cmdBuffer.data()[0],todo);
+ }
+ closeSocket();
+}
+
+
+// used by getNextLine()
+bool DictAsyncClient::waitForRead()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (ret == -1) { // select failed
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // Nothing happend, timeout
+ if (job)
+ job->error = JobData::ErrTimeout;
+ doQuit();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+ doQuit();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)) // all ok
+ return true;
+ }
+
+ if (job) {
+ job->result = QString::null;
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+}
+
+
+// used by sendBuffer() & connect()
+bool DictAsyncClient::waitForWrite()
+{
+ fd_set fdsR,fdsW,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, &fdsW, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (ret == -1) { // select failed
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // nothing happend, timeout
+ if (job)
+ job->error = JobData::ErrTimeout;
+ closeSocket();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+ doQuit();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)||FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsW)) // all ok
+ return true;
+ }
+ if (job) {
+ job->result = QString::null;
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+}
+
+
+// remove start/stop signal
+void DictAsyncClient::clearPipe()
+{
+ fd_set fdsR;
+ timeval tv;
+ int selectRet;
+ char buf;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn,&fdsR);
+ if (1==(selectRet=select(FD_SETSIZE,&fdsR,NULL,NULL,&tv)))
+ if ( ::read(fdPipeIn, &buf, 1 ) == -1 )
+ ::perror( "clearPipe()" );
+ } while (selectRet == 1);
+}
+
+
+bool DictAsyncClient::sendBuffer()
+{
+ int ret;
+ int todo = cmdBuffer.length();
+ int done = 0;
+
+ while (todo > 0) {
+ if (!waitForWrite())
+ return false;
+ ret = KSocks::self()->write(tcpSocket,&cmdBuffer.data()[done],todo);
+ if (ret <= 0) {
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ } else {
+ done += ret;
+ todo -= ret;
+ }
+ }
+ return true;
+}
+
+
+// set thisLine to next complete line of input
+bool DictAsyncClient::getNextLine()
+{
+ thisLine = nextLine;
+ nextLine = strstr(thisLine,"\r\n");
+ if (nextLine) { // there is another full line in the inputbuffer
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+ }
+ unsigned int div = inputEnd-thisLine+1; // hmmm, I need to fetch more input from the server...
+ memmove(input,thisLine,div); // save last, incomplete line
+ thisLine = input;
+ inputEnd = input+div-1;
+ do {
+ if ((inputEnd-input) > 9000) {
+ job->error = JobData::ErrMsgTooLong;
+ closeSocket();
+ return false;
+ }
+ if (!waitForRead())
+ return false;
+
+ int received;
+ do {
+ received = KSocks::self()->read(tcpSocket, inputEnd, inputSize-(inputEnd-input)-1);
+ } while ((received<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (received <= 0) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ closeSocket();
+ return false;
+ }
+ inputEnd += received;
+ inputEnd[0] = 0; // terminate *char
+ } while (!(nextLine = strstr(thisLine,"\r\n")));
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+}
+
+
+// reads next line and checks the response code
+bool DictAsyncClient::nextResponseOk(int code)
+{
+ if (!getNextLine())
+ return false;
+ if (strtol(thisLine,0L,0)!=code) {
+ handleErrors();
+ return false;
+ }
+ return true;
+}
+
+
+// reads next line and returns the response code
+bool DictAsyncClient::getNextResponse(int &code)
+{
+ if (!getNextLine())
+ return false;
+ code = strtol(thisLine,0L,0);
+ return true;
+}
+
+
+void DictAsyncClient::handleErrors()
+{
+ int len = strlen(thisLine);
+ if (len>80)
+ len = 80;
+ job->result = QString::null;
+ resultAppend(codec->toUnicode(thisLine,len));
+
+ switch (strtol(thisLine,0L,0)) {
+ case 420:
+ case 421:
+ job->error = JobData::ErrNotAvailable; // server unavailable
+ break;
+ case 500:
+ case 501:
+ job->error = JobData::ErrSyntax; // syntax error
+ break;
+ case 502:
+ case 503:
+ job->error = JobData::ErrCommandNotImplemented; // command not implemented
+ break;
+ case 530:
+ job->error = JobData::ErrAccessDenied; // access denied
+ break;
+ case 531:
+ job->error = JobData::ErrAuthFailed; // authentication failed
+ break;
+ case 550:
+ case 551:
+ job->error = JobData::ErrInvalidDbStrat; // invalid strategy/database
+ break;
+ case 554:
+ job->error = JobData::ErrNoDatabases; // no databases
+ break;
+ case 555:
+ job->error = JobData::ErrNoStrategies; // no strategies
+ break;
+ default:
+ job->error = JobData::ErrServerError;
+ }
+ doQuit();
+}
+
+
+void DictAsyncClient::resultAppend(const char* str)
+{
+ if (job)
+ job->result += codec->toUnicode(str);
+}
+
+
+void DictAsyncClient::resultAppend(QString str)
+{
+ if (job)
+ job->result += str;
+}
+
+
+
+//********* DictInterface ******************************************
+
+DictInterface::DictInterface()
+: newServer(false), clientDoneInProgress(false)
+{
+ if (::pipe(fdPipeIn ) == -1 ) {
+ perror( "Creating in pipe" );
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+ if (::pipe(fdPipeOut ) == -1 ) {
+ perror( "Creating out pipe" );
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ if (-1 == fcntl(fdPipeIn[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking
+ perror("fcntl()");
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ if (-1 == fcntl(fdPipeOut[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking
+ perror("fcntl()");
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ notifier = new QSocketNotifier(fdPipeIn[0],QSocketNotifier::Read,this);
+ connect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone()));
+
+ // initialize the KSocks stuff in the main thread, otherwise we get
+ // strange effects on FreeBSD
+ (void) KSocks::self();
+
+ client = new DictAsyncClient(fdPipeOut[0],fdPipeIn[1]);
+ if (0!=pthread_create(&threadID,0,&(client->startThread),client)) {
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nUnable to create thread."));
+ kapp->exit(1);
+ }
+
+ jobList.setAutoDelete(true);
+}
+
+
+DictInterface::~DictInterface()
+{
+ disconnect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone()));
+
+ if (0!=pthread_cancel(threadID))
+ kdWarning() << "pthread_cancel failed!" << endl;
+ if (0!=pthread_join(threadID,NULL))
+ kdWarning() << "pthread_join failed!" << endl;
+ delete client;
+
+ if ( ::close( fdPipeIn[0] ) == -1 ) {
+ perror( "Closing fdPipeIn[0]" );
+ }
+ if ( ::close( fdPipeIn[1] ) == -1 ) {
+ perror( "Closing fdPipeIn[1]" );
+ }
+ if ( ::close( fdPipeOut[0] ) == -1 ) {
+ perror( "Closing fdPipeOut[0]" );
+ }
+ if ( ::close( fdPipeOut[1] ) == -1 ) {
+ perror( "Closing fdPipeOut[1]" );
+ }
+}
+
+
+// inform the client when server settings get changed
+void DictInterface::serverChanged()
+{
+ newServer = true;
+}
+
+
+// cancel all pending jobs
+void DictInterface::stop()
+{
+ if (jobList.isEmpty()) {
+ return;
+ } else {
+ while (jobList.count()>1) // not yet started jobs can be deleted directly
+ jobList.removeLast();
+
+ if (!clientDoneInProgress) {
+ jobList.getFirst()->canceled = true; // clientDone() now ignores the results of this job
+ char buf; // write one char in the pipe to the async thread
+ if (::write(fdPipeOut[1],&buf,1) == -1)
+ ::perror( "stop()" );
+ }
+ }
+}
+
+
+void DictInterface::define(const QString &query)
+{
+ JobData *newJob = generateQuery(JobData::TDefine,query);
+ if (newJob)
+ insertJob(newJob);
+}
+
+
+void DictInterface::getDefinitions(QStringList query)
+{
+ JobData *newjob = new JobData(JobData::TGetDefinitions,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newjob->defines = query;
+ newServer = false;
+ insertJob(newjob);
+}
+
+
+void DictInterface::match(const QString &query)
+{
+ JobData *newJob = generateQuery(JobData::TMatch,query);
+
+ if (newJob) {
+ if (global->currentStrategy == 0)
+ newJob->strategy = "."; // spell check strategy
+ else
+ newJob->strategy = global->strategies[global->currentStrategy].utf8();
+
+ insertJob(newJob);
+ }
+}
+
+
+// fetch detailed db info
+void DictInterface::showDbInfo(const QString &db)
+{
+ QString ndb = db.simplifyWhiteSpace(); // cleanup query string
+ if (ndb.isEmpty())
+ return;
+ if (ndb.length()>100) // shorten if necessary
+ ndb.truncate(100);
+ JobData *newjob = new JobData(JobData::TShowDbInfo,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newServer = false;
+ newjob->query = ndb; // construct job...
+ insertJob(newjob);
+}
+
+
+void DictInterface::showDatabases()
+{
+ insertJob( new JobData(JobData::TShowDatabases,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+void DictInterface::showStrategies()
+{
+ insertJob( new JobData(JobData::TShowStrategies,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+void DictInterface::showInfo()
+{
+ insertJob( new JobData(JobData::TShowInfo,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+// get info about databases & stratgies the server knows
+void DictInterface::updateServer()
+{
+ insertJob( new JobData(JobData::TUpdate,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+// client-thread ended
+void DictInterface::clientDone()
+{
+ QString message;
+
+ cleanPipes(); // read from pipe so that notifier doesn´t fire again
+
+ if (jobList.isEmpty()) {
+ kdDebug(5004) << "This shouldn´t happen, the client-thread signaled termination, but the job list is empty" << endl;
+ return; // strange..
+ }
+
+ clientDoneInProgress = true;
+ QStringList::iterator it;
+ JobData* job = jobList.getFirst();
+ if (!job->canceled) { // non-interupted job?
+ if (JobData::ErrNoErr == job->error) {
+ switch (job->type) {
+ case JobData::TUpdate :
+ global->serverDatabases.clear();
+ for (it = job->databases.begin(); it != job->databases.end(); ++it)
+ global->serverDatabases.append(*it);
+ global->databases = global->serverDatabases;
+ for (int i = global->databaseSets.count()-1;i>=0;i--)
+ global->databases.prepend(global->databaseSets.at(i)->first());
+ global->databases.prepend(i18n("All Databases"));
+ global->currentDatabase = 0;
+
+ global->strategies.clear();
+ for (it = job->strategies.begin(); it != job->strategies.end(); ++it)
+ global->strategies.append(*it);
+ global->strategies.prepend(i18n("Spell Check"));
+ global->currentStrategy = 0;
+ message = i18n(" Received database/strategy list ");
+ emit stopped(message);
+ emit infoReady();
+ break;
+ case JobData::TDefine:
+ case JobData::TGetDefinitions:
+ if (job->type == JobData::TDefine) {
+ switch (job->numFetched) {
+ case 0:
+ message = i18n("No definitions found");
+ break;
+ case 1:
+ message = i18n("One definition found");
+ break;
+ default:
+ message = i18n("%1 definitions found").arg(job->numFetched);
+ }
+ } else {
+ switch (job->numFetched) {
+ case 0:
+ message = i18n(" No definitions fetched ");
+ break;
+ case 1:
+ message = i18n(" One definition fetched ");
+ break;
+ default:
+ message = i18n(" %1 definitions fetched ").arg(job->numFetched);
+ }
+ }
+ emit stopped(message);
+ emit resultReady(job->result, job->query);
+ break;
+ case JobData::TMatch:
+ switch (job->numFetched) {
+ case 0:
+ message = i18n(" No matching definitions found ");
+ break;
+ case 1:
+ message = i18n(" One matching definition found ");
+ break;
+ default:
+ message = i18n(" %1 matching definitions found ").arg(job->numFetched);
+ }
+ emit stopped(message);
+ emit matchReady(job->matches);
+ break;
+ default :
+ message = i18n(" Received information ");
+ emit stopped(message);
+ emit resultReady(job->result, job->query);
+ }
+ } else {
+ QString errMsg;
+ switch (job->error) {
+ case JobData::ErrCommunication:
+ errMsg = i18n("Communication error:\n\n");
+ errMsg += job->result;
+ break;
+ case JobData::ErrTimeout:
+ errMsg = i18n("A delay occurred which exceeded the\ncurrent timeout limit of %1 seconds.\nYou can modify this limit in the Preferences Dialog.").arg(global->timeout);
+ break;
+ case JobData::ErrBadHost:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\nCannot resolve hostname.").arg(job->server).arg(job->port);
+ break;
+ case JobData::ErrConnect:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\n").arg(job->server).arg(job->port);
+ errMsg += job->result;
+ break;
+ case JobData::ErrRefused:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\nThe server refused the connection.").arg(job->server).arg(job->port);
+ break;
+ case JobData::ErrNotAvailable:
+ errMsg = i18n("The server is temporarily unavailable.");
+ break;
+ case JobData::ErrSyntax:
+ errMsg = i18n("The server reported a syntax error.\nThis shouldn't happen -- please consider\nwriting a bug report.");
+ break;
+ case JobData::ErrCommandNotImplemented:
+ errMsg = i18n("A command that Kdict needs isn't\nimplemented on the server.");
+ break;
+ case JobData::ErrAccessDenied:
+ errMsg = i18n("Access denied.\nThis host is not allowed to connect.");
+ break;
+ case JobData::ErrAuthFailed:
+ errMsg = i18n("Authentication failed.\nPlease enter a valid username and password.");
+ break;
+ case JobData::ErrInvalidDbStrat:
+ errMsg = i18n("Invalid database/strategy.\nYou probably need to use Server->Get Capabilities.");
+ break;
+ case JobData::ErrNoDatabases:
+ errMsg = i18n("No databases available.\nIt is possible that you need to authenticate\nwith a valid username/password combination to\ngain access to any databases.");
+ break;
+ case JobData::ErrNoStrategies:
+ errMsg = i18n("No strategies available.");
+ break;
+ case JobData::ErrServerError:
+ errMsg = i18n("The server sent an unexpected reply:\n\"%1\"\nThis shouldn't happen, please consider\nwriting a bug report").arg(job->result);
+ break;
+ case JobData::ErrMsgTooLong:
+ errMsg = i18n("The server sent a response with a text line\nthat was too long.\n(RFC 2229: max. 1024 characters/6144 octets)");
+ break;
+ case JobData::ErrNoErr: // make compiler happy
+ errMsg = i18n("No Errors");
+ }
+ message = i18n(" Error ");
+ emit stopped(message);
+ KMessageBox::error(global->topLevel, errMsg);
+ }
+ } else {
+ message = i18n(" Stopped ");
+ emit stopped(message);
+ }
+
+ clientDoneInProgress = false;
+
+ client->removeJob();
+ jobList.removeFirst(); // this job is now history
+ if (!jobList.isEmpty()) // work to be done?
+ startClient(); // => restart client
+}
+
+
+JobData* DictInterface::generateQuery(JobData::QueryType type, QString query)
+{
+ query = query.simplifyWhiteSpace(); // cleanup query string
+ if (query.isEmpty())
+ return 0L;
+ if (query.length()>300) // shorten if necessary
+ query.truncate(300);
+ query = query.replace(QRegExp("[\"\\]"), ""); // remove remaining illegal chars...
+ if (query.isEmpty())
+ return 0L;
+
+ JobData *newjob = new JobData(type,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding, global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newServer = false;
+ newjob->query = query; // construct job...
+
+ if (global->currentDatabase == 0) // all databases
+ newjob->databases.append("*");
+ else {
+ if ((global->currentDatabase > 0)&& // database set
+ (global->currentDatabase < global->databaseSets.count()+1)) {
+ for (int i = 0;i<(int)global->serverDatabases.count();i++)
+ if ((global->databaseSets.at(global->currentDatabase-1))->findIndex(global->serverDatabases[i])>0)
+ newjob->databases.append(global->serverDatabases[i].utf8().data());
+ if (newjob->databases.count()==0) {
+ KMessageBox::sorry(global->topLevel, i18n("Please select at least one database."));
+ delete newjob;
+ return 0L;
+ }
+ } else { // one database
+ newjob->databases.append(global->databases[global->currentDatabase].utf8().data());
+ }
+ }
+
+ return newjob;
+}
+
+
+void DictInterface::insertJob(JobData* job)
+{
+ if (jobList.isEmpty()) { // Client has nothing to do, start directly
+ jobList.append(job);
+ startClient();
+ } else { // there are other pending jobs...
+ stop();
+ jobList.append(job);
+ }
+}
+
+
+// start client-thread
+void DictInterface::startClient()
+{
+ cleanPipes();
+ if (jobList.isEmpty()) {
+ kdDebug(5004) << "This shouldn´t happen, startClient called, but clientList is empty" << endl;
+ return;
+ }
+
+ client->insertJob(jobList.getFirst());
+ char buf; // write one char in the pipe to the async thread
+ if (::write(fdPipeOut[1],&buf,1) == -1)
+ ::perror( "startClient()" );
+
+ QString message;
+ switch (jobList.getFirst()->type) {
+ case JobData::TDefine:
+ case JobData::TGetDefinitions:
+ case JobData::TMatch:
+ message = i18n(" Querying server... ");
+ break;
+ case JobData::TShowDatabases:
+ case JobData::TShowStrategies:
+ case JobData::TShowInfo:
+ case JobData::TShowDbInfo:
+ message = i18n(" Fetching information... ");
+ break;
+ case JobData::TUpdate:
+ message = i18n(" Updating server information... ");
+ break;
+ }
+ emit started(message);
+}
+
+
+// empty the pipes, so that notifier stops firing
+void DictInterface::cleanPipes()
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ char buf;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fdPipeIn[0],&rfds);
+ if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv)))
+ if ( ::read(fdPipeIn[0], &buf, 1 ) == -1 )
+ ::perror( "cleanPipes" );
+ } while (ret == 1);
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fdPipeOut[0],&rfds);
+ if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv)))
+ if ( ::read(fdPipeOut[0], &buf, 1 ) == -1 )
+ ::perror( "cleanPipes" );
+ } while (ret == 1);
+}
+
+//--------------------------------
+
+#include "dict.moc"
diff --git a/kdict/dict.h b/kdict/dict.h
new file mode 100644
index 00000000..adb87ed9
--- /dev/null
+++ b/kdict/dict.h
@@ -0,0 +1,204 @@
+/* -------------------------------------------------------------
+
+ dict.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias Hölzer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ JobData used for data transfer between Client and Interface
+ DictAsyncClient all network related stuff happens here in an asynchrous thread
+ DictInterface interface for DictAsyncClient, job management
+
+ -------------------------------------------------------------*/
+
+#ifndef _DICT_H_
+#define _DICT_H_
+
+#include <pthread.h>
+#include <qptrlist.h>
+#include <qsocketnotifier.h>
+
+class QSocketNotifier;
+struct in_addr;
+
+
+//********* JobData ******************************************
+
+
+class JobData
+{
+
+public:
+
+ enum QueryType { //type of transaction
+ TDefine=0,
+ TGetDefinitions,
+ TMatch,
+ TShowDatabases,
+ TShowDbInfo,
+ TShowStrategies,
+ TShowInfo,
+ TUpdate
+ };
+
+ enum ErrType { // error codes
+ ErrNoErr=0,
+ ErrCommunication, // display result!
+ ErrTimeout,
+ ErrBadHost,
+ ErrConnect, // display result!
+ ErrRefused,
+ ErrNotAvailable,
+ ErrSyntax,
+ ErrCommandNotImplemented,
+ ErrAccessDenied,
+ ErrAuthFailed,
+ ErrInvalidDbStrat,
+ ErrNoDatabases,
+ ErrNoStrategies,
+ ErrServerError, // display result!
+ ErrMsgTooLong
+ };
+
+ JobData(QueryType Ntype,bool NnewServer,QString const& Nserver,int Nport,
+ int NidleHold, int Ntimeout, int NpipeSize, QString const& Nencoding, bool NAuthEnabled,
+ QString const& Nuser, QString const& Nsecret, unsigned int NheadLayout);
+
+ QueryType type;
+ ErrType error;
+
+ bool canceled;
+ int numFetched;
+ QString result;
+ QStringList matches;
+
+ QString query;
+ QStringList defines;
+
+ bool newServer;
+ QString server;
+ int port, timeout, pipeSize, idleHold;
+ QString encoding;
+ bool authEnabled;
+ QString user, secret;
+ QStringList databases,strategies;
+ QString strategy;
+ unsigned int headLayout;
+};
+
+
+//********* DictAsyncClient ******************************************
+
+
+class DictAsyncClient
+{
+
+public:
+
+ DictAsyncClient(int NfdPipeIn, int NfdPipeOut);
+ ~DictAsyncClient();
+
+ static void* startThread(void* pseudoThis);
+
+ void insertJob(JobData *newJob);
+ void removeJob();
+
+private:
+
+ void waitForWork(); // main loop
+ void define();
+ bool getDefinitions();
+ bool match();
+ void showDatabases();
+ void showDbInfo();
+ void showStrategies();
+ void showInfo();
+ void update();
+
+ void openConnection(); // connect, handshake and authorization
+ void closeSocket();
+ void doQuit(); // send "quit" without timeout, without checks, close connection
+ bool waitForRead(); // used by getNextIntoBuffer()
+ bool waitForWrite(); // used by sendBuffer() & connect()
+ void clearPipe(); // remove start/stop signal
+
+ bool sendBuffer(); // send cmdBuffer to the server
+ bool getNextLine(); // set thisLine to next complete line of input
+ bool nextResponseOk(int code); // reads next line and checks the response code
+ bool getNextResponse(int &code); // reads next line and returns the response code
+ void handleErrors();
+
+ void resultAppend(const char* str);
+ void resultAppend(QString str);
+
+ JobData *job;
+ char *input;
+ QCString cmdBuffer;
+ const unsigned int inputSize;
+ char *thisLine, *nextLine, *inputEnd;
+ int fdPipeIn,fdPipeOut; //IPC-Pipes to/from async thread
+ int tcpSocket,timeout,idleHold;
+ QTextCodec *codec;
+};
+
+
+//********* DictInterface *************************************************
+
+class DictInterface : public QObject
+{
+ Q_OBJECT
+
+public:
+
+ DictInterface();
+ ~DictInterface();
+
+public slots:
+
+ void serverChanged(); // inform the client when server settings get changed
+ void stop(); // cancel all pending jobs
+
+ void define(const QString &query);
+ void getDefinitions(QStringList query);
+ void match(const QString &query);
+ void showDbInfo(const QString &db); // fetch detailed db info
+ void showDatabases(); // fetch misc. info...
+ void showStrategies();
+ void showInfo();
+ void updateServer(); // get info about databases & strategies the server knows
+
+signals:
+
+ void infoReady(); // updateServer done
+ void resultReady(const QString &result, const QString &query); // define done
+ void matchReady(const QStringList &result); // match done
+ void started(const QString &message); // Client is active now, activate indicator
+ void stopped(const QString &message); // Client is now halted, deactivate indicator
+
+private slots:
+
+ void clientDone();
+
+private:
+
+ JobData* generateQuery(JobData::QueryType type, QString query);
+ void insertJob(JobData* job); // insert in job list, if nesscary cancel/remove previous jobs
+ void startClient(); // send start signal
+ void cleanPipes(); // empty the pipes, so that notifier stops firing
+
+ QSocketNotifier *notifier;
+ int fdPipeIn[2],fdPipeOut[2]; //IPC-Pipes to/from async thread
+ pthread_t threadID;
+ DictAsyncClient *client;
+ QPtrList<JobData> jobList;
+ bool newServer,clientDoneInProgress;
+};
+
+extern DictInterface *interface;
+
+#endif
diff --git a/kdict/hi128-app-kdict.png b/kdict/hi128-app-kdict.png
new file mode 100644
index 00000000..fd57f9ab
--- /dev/null
+++ b/kdict/hi128-app-kdict.png
Binary files differ
diff --git a/kdict/hi16-app-kdict.png b/kdict/hi16-app-kdict.png
new file mode 100644
index 00000000..643500fc
--- /dev/null
+++ b/kdict/hi16-app-kdict.png
Binary files differ
diff --git a/kdict/hi32-app-kdict.png b/kdict/hi32-app-kdict.png
new file mode 100644
index 00000000..fa3a0537
--- /dev/null
+++ b/kdict/hi32-app-kdict.png
Binary files differ
diff --git a/kdict/hi48-app-kdict.png b/kdict/hi48-app-kdict.png
new file mode 100644
index 00000000..40efecd0
--- /dev/null
+++ b/kdict/hi48-app-kdict.png
Binary files differ
diff --git a/kdict/hi64-app-kdict.png b/kdict/hi64-app-kdict.png
new file mode 100644
index 00000000..ba6ed634
--- /dev/null
+++ b/kdict/hi64-app-kdict.png
Binary files differ
diff --git a/kdict/hisc-app-kdict.svgz b/kdict/hisc-app-kdict.svgz
new file mode 100644
index 00000000..584e1968
--- /dev/null
+++ b/kdict/hisc-app-kdict.svgz
Binary files differ
diff --git a/kdict/kdict.desktop b/kdict/kdict.desktop
new file mode 100644
index 00000000..d274d233
--- /dev/null
+++ b/kdict/kdict.desktop
@@ -0,0 +1,153 @@
+[Desktop Entry]
+Type=Application
+Exec=kdict -caption "%c" %i %m
+Icon=kdict
+Terminal=false
+Name=Dictionary
+Name[af]=Woordeboek
+Name[ar]=القاموس
+Name[az]=Lüğət
+Name[be]=Слоўнік
+Name[bg]=Речник
+Name[bn]=অভিধান
+Name[br]=Geriadur
+Name[bs]=RjeÄnik
+Name[ca]=Diccionari
+Name[cs]=Slovník
+Name[cy]=Geiriadur
+Name[da]=Ordbog
+Name[de]=Lexikon
+Name[el]=Λεξικό
+Name[eo]=Vortaro
+Name[es]=Diccionario
+Name[et]=Sõnaraamat
+Name[eu]=Hiztegia
+Name[fa]=واژه‌نامه
+Name[fi]=Sanakirja
+Name[fr]=Dictionnaire
+Name[ga]=Foclóir
+Name[gl]=Diccionario
+Name[he]=מילון
+Name[hi]=शबà¥à¤¦à¤•à¥‹à¤¶
+Name[hr]=RjeÄnik
+Name[hu]=Szótár
+Name[is]=Orðabók
+Name[it]=Dizionario
+Name[ja]=辞書
+Name[ka]=ლექსიკáƒáƒœáƒ˜
+Name[kk]=Сөздік
+Name[km]=វចនានុក្រម
+Name[ko]=사전
+Name[lt]=Žodynas
+Name[lv]=VÄrdnÄ«ca
+Name[mk]=Речник
+Name[mn]=Толь бичиг
+Name[ms]=Kamus
+Name[mt]=Dizzjunarju
+Name[nb]=Ordbok
+Name[nds]=Wöörbook
+Name[ne]=शबà¥à¤¦à¤•à¥‹à¤¶
+Name[nl]=Woordenboek
+Name[nn]=Ordbok
+Name[nso]=Pukuntsu
+Name[pa]=ਸ਼ਬਦ-ਕੋਸ਼
+Name[pl]=SÅ‚ownik
+Name[pt]=Dicionário
+Name[pt_BR]=Dicionário
+Name[ro]=Dicţionar
+Name[ru]=Словарь
+Name[se]=Sátnegirji
+Name[sk]=Slovník
+Name[sl]=Slovar
+Name[sr]=Речник
+Name[sr@Latn]=ReÄnik
+Name[sv]=Ordlista
+Name[ta]=அகராதி
+Name[tg]=Луғат
+Name[th]=พจนานุà¸à¸£à¸¡
+Name[tr]=Sözlük
+Name[uk]=Словник
+Name[uz]=Lugʻat
+Name[uz@cyrillic]=Луғат
+Name[ven]=Bugu yau talutshedza maipfi
+Name[wa]=Motî
+Name[xh]=Incwadi eneenkcazelo zamagama
+Name[zh_CN]=å­—å…¸
+Name[zh_HK]=å­—å…¸
+Name[zh_TW]=å­—å…¸
+Name[zu]=Isichazamagama
+GenericName=Online Dictionary
+GenericName[af]=Aan-lyn Woordeboek
+GenericName[ar]=قاموس على الإنترنت
+GenericName[be]=Сеткавы Ñлоўнік
+GenericName[bg]=Мрежови речник
+GenericName[bn]=অনলাইন অভিধান
+GenericName[br]=Geriaoueg enlinenn
+GenericName[bs]=Online rjeÄnik
+GenericName[ca]=Diccionari en línia
+GenericName[cs]=Online slovník
+GenericName[cy]=Geiriadur Ar-lein
+GenericName[da]=Online-ordbog
+GenericName[de]=Online-Lexikon
+GenericName[el]=Διαδικτυακό λεξικό
+GenericName[eo]=Vortaro
+GenericName[es]=Diccionario en la red
+GenericName[et]=Võrgusõnaraamat
+GenericName[eu]=On line hiztegia
+GenericName[fa]=واژه‌نامۀ برخط
+GenericName[fi]=Sanakirja
+GenericName[fr]=Dictionnaire électronique
+GenericName[ga]=Foclóir ar líne
+GenericName[gl]=Diccionario en liña
+GenericName[he]=מילון מקוון
+GenericName[hi]=ऑनलाइन शबà¥à¤¦à¤•à¥‹à¤¶
+GenericName[hr]=Online rjeÄnik
+GenericName[hu]=Szótárkezelő
+GenericName[is]=Orðabók á Netinu
+GenericName[it]=Dizionario in linea
+GenericName[ja]=オンライン辞書
+GenericName[ka]=ხáƒáƒ–ის ლექსიკáƒáƒœáƒ˜
+GenericName[kk]=Онлайн Ñөздік
+GenericName[km]=វចនានុក្រម​លើ​បណ្ដាញ
+GenericName[lt]=Žodynas tinkle
+GenericName[lv]=TieÅ¡saites VÄrdnÄ«ca
+GenericName[mk]=Речник на линија
+GenericName[mn]=Онлайн-Толь бичиг
+GenericName[ms]=Kamus Talian
+GenericName[mt]=Dizzjunarju online
+GenericName[nb]=Ordbok på nettet
+GenericName[nds]=Online-Wöörbook
+GenericName[ne]=अनलाइन शबà¥à¤¦à¤•à¥‹à¤¶
+GenericName[nl]=Online woordenboek
+GenericName[nn]=Internettordbok
+GenericName[nso]=Pukuntsu ya Online
+GenericName[pa]=ਆਨਲਾਇਨ ਡਿਕਸ਼ਨਰੀ
+GenericName[pl]=SÅ‚ownik w sieci
+GenericName[pt]=Dicionário na Rede
+GenericName[pt_BR]= Dicionário On-line
+GenericName[ro]=Dicţionar on-line
+GenericName[ru]=Онлайн-Ñловарь
+GenericName[se]=Fierpmádatsátnegirji
+GenericName[sk]=On-line slovník
+GenericName[sl]=Spletni slovar
+GenericName[sr]=Онлајн речник
+GenericName[sr@Latn]=Onlajn reÄnik
+GenericName[sv]=Online-ordlista
+GenericName[ta]=இணைய அகராதி
+GenericName[tg]=Луғати Шабакавӣ
+GenericName[th]=พจนานุà¸à¸£à¸¡à¹à¸šà¸šà¸­à¸­à¸™à¹„ลน์
+GenericName[tr]=Çevrimiçi Sözlük
+GenericName[uk]=Словник в мережі
+GenericName[uz]=Internet lugʻat
+GenericName[uz@cyrillic]=Интернет луғат
+GenericName[ven]=Bugu talutshedza maipfi ine yavha kha mutevhe wau tshimbila
+GenericName[wa]=Motî so les fyis
+GenericName[xh]=Incwadi eneenkcazelo zamagama Esemgceni
+GenericName[zh_CN]=在线字典
+GenericName[zh_HK]=線上字典
+GenericName[zh_TW]=線上字典
+GenericName[zu]=Isichaza magama esixhumekile
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+DocPath=kdict/index.html
+Categories=Qt;KDE;Network;X-KDE-More;Office;Dictionary;
diff --git a/kdict/kdictui.rc b/kdict/kdictui.rc
new file mode 100644
index 00000000..15b3c414
--- /dev/null
+++ b/kdict/kdictui.rc
@@ -0,0 +1,59 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="Kdict" version="4">
+
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="file_save"/>
+ <Action name="file_print"/>
+ <Separator/>
+ <Action name="start_query"/>
+ <Action name="stop_query"/>
+ <Separator/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu noMerge="1" name="edit"><text>&amp;Edit</text>
+ <Action name="edit_copy"/>
+ <Action name="edit_select_all"/>
+ <Separator/>
+ <Action name="define_clipboard"/>
+ <Action name="match_clipboard"/>
+ <Separator/>
+ <Action name="edit_find"/>
+ </Menu>
+ <Menu name="history"><text>Hist&amp;ory</text>
+ <Action name="browse_back"/>
+ <Action name="browse_forward"/>
+ <Separator />
+ <Action name="clear_history"/>
+ <Separator />
+ <ActionList name="history_items"/>
+ </Menu>
+ <Menu name="server"><text>Ser&amp;ver</text>
+ <Action name="get_capabilities"/>
+ <Action name="edit_sets"/>
+ <Separator />
+ <Menu name="database_sub"><text>Database &amp;Information</text>
+ <Action name="db_summary"/>
+ <Separator />
+ <ActionList name="db_detail"/>
+ </Menu>
+ <Action name="strategy_info"/>
+ <Action name="server_info"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="show_match" append="show_merge"/>
+ </Menu>
+</MenuBar>
+
+<ToolBar fullWidth="true" newline="true" noMerge="1" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="clear_query"/>
+ <Action name="look_label"/>
+ <Action name="query_combo"/>
+ <Separator/>
+ <Action name="define_btn"/>
+ <Action name="match_btn"/>
+</ToolBar>
+
+<StatusBar/>
+
+</kpartgui>
diff --git a/kdict/main.cpp b/kdict/main.cpp
new file mode 100644
index 00000000..235f9048
--- /dev/null
+++ b/kdict/main.cpp
@@ -0,0 +1,56 @@
+/* -------------------------------------------------------------
+
+ main.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias H�zer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ ------------------------------------------------------------- */
+
+#include <config.h>
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kdelibs_export.h>
+
+#include "application.h"
+#include "toplevel.h"
+
+static KCmdLineOptions knoptions[] =
+{
+ { "c", 0, 0 },
+ { "clipboard", I18N_NOOP("Define X11-clipboard content (selected text)"), 0 },
+ { "+[word/phrase]", I18N_NOOP("Lookup the given word/phrase"), 0 },
+ KCmdLineLastOption
+};
+
+
+extern "C" KDE_EXPORT int kdemain(int argc, char* argv[])
+{
+ KAboutData aboutData("kdict",
+ I18N_NOOP("Dictionary"),
+ KDICT_VERSION,
+ I18N_NOOP("The KDE Dictionary Client"),
+ KAboutData:: License_Artistic,
+ "Copyright (c) 1999-2001, Christian Gebauer\nCopyright (c) 1998, Matthias Hoelzer",
+ 0,
+ 0);
+
+ aboutData.addAuthor("Christian Gebauer",I18N_NOOP("Maintainer"),"gebauer@kde.org");
+ aboutData.addAuthor("Matthias Hoelzer",I18N_NOOP("Original Author"),"hoelzer@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( knoptions );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!Application::start())
+ return 0;
+
+ Application app;
+
+ return app.exec();
+}
diff --git a/kdict/matchview.cpp b/kdict/matchview.cpp
new file mode 100644
index 00000000..32cd04dd
--- /dev/null
+++ b/kdict/matchview.cpp
@@ -0,0 +1,473 @@
+/* -------------------------------------------------------------
+
+ matchview.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ MatchView This widget contains the list of matching definitions
+
+ ------------------------------------------------------------- */
+
+#include <qclipboard.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qregexp.h>
+
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+
+#include "dict.h"
+#include "options.h"
+#include "matchview.h"
+
+
+//********* MatchViewItem ********************************************
+
+
+MatchViewItem::MatchViewItem(QListView *view, const QString &text)
+ : QListViewItem(view,text)
+{
+}
+
+
+MatchViewItem::MatchViewItem(QListView *view,QListViewItem *after,const QString &text)
+ : QListViewItem(view,after,text)
+{
+}
+
+
+MatchViewItem::MatchViewItem(QListViewItem *item,const QString &text,const QString &commandStr)
+: QListViewItem(item,text), command(commandStr)
+{
+}
+
+
+MatchViewItem::MatchViewItem(QListViewItem *item,QListViewItem *after,const QString &text,const QString &commandStr)
+: QListViewItem(item,after,text), command(commandStr)
+{
+}
+
+
+MatchViewItem::~MatchViewItem()
+{
+}
+
+
+void MatchViewItem::setOpen(bool o)
+{
+ if (o && !childCount()) {
+ listView()->setUpdatesEnabled(false);
+
+ MatchViewItem *sub=0;
+ QString command, label;
+ QRegExp exp("\"*\"", true, true);
+ QStringList::iterator it;
+ for (it = subEntrys.begin(); it != subEntrys.end(); ++it) {
+ command = "define ";
+ command += (*it);
+ command += "\r\n";
+ exp.search((*it));
+ label = exp.cap();
+ label = label.mid(1, label.length()-2); // remove quotes
+ if (sub)
+ sub = new MatchViewItem(this, sub, label, command);
+ else
+ sub = new MatchViewItem(this, label, command);
+ }
+
+ subEntrys.clear();
+
+ listView()->setUpdatesEnabled(true);
+ }
+
+ if (childCount())
+ QListViewItem::setOpen(o);
+}
+
+
+void MatchViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
+{
+ if(command.isEmpty()) {
+ QFont font=p->font();
+ font.setBold(true);
+ p->setFont(font);
+ }
+ QListViewItem::paintCell(p,cg,column,width,alignment);
+}
+
+
+//********* MatchView ******************************************
+
+
+MatchView::MatchView(QWidget *parent, const char *name)
+ : QWidget(parent,name),getOn(false),getAllOn(false)
+{
+ setCaption(kapp->makeStdCaption(i18n("Match List")));
+
+ QVBoxLayout * boxLayout = new QVBoxLayout(this, 1, 0);
+
+ boxLayout->addSpacing(1);
+ w_strat = new QComboBox(false,this);
+ w_strat->setFixedHeight(w_strat->sizeHint().height());
+ connect(w_strat,SIGNAL(activated(int)),this,SLOT(strategySelected(int)));
+ boxLayout->addWidget(w_strat,0);
+ boxLayout->addSpacing(1);
+
+ w_list = new QListView(this);
+ w_list->setFocusPolicy(QWidget::StrongFocus);
+ w_list->header()->hide();
+ w_list->addColumn("foo");
+ w_list->setColumnWidthMode(0,QListView::Maximum);
+ w_list->setColumnWidth(0,0);
+ w_list->setSelectionMode(QListView::Extended);
+ w_list->setTreeStepSize(18);
+ w_list->setSorting(-1); // disable sorting
+ w_list->setMinimumHeight(w_strat->sizeHint().height());
+ connect(w_list,SIGNAL(selectionChanged()),SLOT(enableGetButton()));
+ connect(w_list,SIGNAL(returnPressed(QListViewItem *)),SLOT(returnPressed(QListViewItem *)));
+ connect(w_list,SIGNAL(doubleClicked(QListViewItem *)),SLOT(getOneItem(QListViewItem *)));
+ connect(w_list,SIGNAL(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)),
+ SLOT(mouseButtonPressed(int, QListViewItem *, const QPoint &, int)));
+ connect(w_list,SIGNAL(rightButtonPressed(QListViewItem *,const QPoint &,int)),SLOT(buildPopupMenu(QListViewItem *,const QPoint &,int)));
+ boxLayout->addWidget(w_list,1);
+
+ boxLayout->addSpacing(1);
+ w_get = new QPushButton(i18n("&Get Selected"),this);
+ w_get->setFixedHeight(w_get->sizeHint().height()-3);
+ w_get->setMinimumWidth(w_get->sizeHint().width()-20);
+ w_get->setEnabled(false);
+ connect(w_get, SIGNAL(clicked()), this, SLOT(getSelected()));
+ boxLayout->addWidget(w_get,0);
+
+ w_getAll = new QPushButton(i18n("Get &All"),this);
+ w_getAll->setFixedHeight(w_getAll->sizeHint().height()-3);
+ w_getAll->setMinimumWidth(w_getAll->sizeHint().width()-20);
+ w_getAll->setEnabled(false);
+ connect(w_getAll, SIGNAL(clicked()), this, SLOT(getAll()));
+ boxLayout->addWidget(w_getAll,0);
+ connect(interface,SIGNAL(matchReady(const QStringList &)),this,SLOT(newList(const QStringList &)));
+ rightBtnMenu = new KPopupMenu();
+}
+
+
+MatchView::~MatchView()
+{
+}
+
+
+void MatchView::updateStrategyCombo()
+{
+ w_strat->clear();
+ w_strat->insertStringList(global->strategies);
+ w_strat->setCurrentItem(global->currentStrategy);
+}
+
+
+bool MatchView::selectStrategy(const QString &strategy) const
+{
+ int newCurrent = global->strategies.findIndex(strategy);
+ if (newCurrent == -1)
+ return false;
+ else {
+ global->currentStrategy = newCurrent;
+ w_strat->setCurrentItem(global->currentStrategy);
+ return true;
+ }
+}
+
+
+void MatchView::match(const QString &query)
+{
+ interface->match(query.utf8());
+}
+
+
+void MatchView::closeEvent ( QCloseEvent * e )
+{
+ e->accept(); // hides the widget
+ emit(windowClosed());
+}
+
+
+void MatchView::strategySelected(int num)
+{
+ global->currentStrategy = num;
+}
+
+
+void MatchView::enableGetButton()
+{
+ if (w_getAll->isEnabled()) {
+ w_get->setEnabled(true);
+ getOn = true;
+ }
+}
+
+
+void MatchView::mouseButtonPressed(int button, QListViewItem *, const QPoint &, int)
+{
+ if (button == MidButton)
+ emit(clipboardRequested());
+}
+
+
+void MatchView::returnPressed(QListViewItem *)
+{
+ getSelected();
+}
+
+
+void MatchView::getOneItem(QListViewItem *i)
+{
+ QStringList defines;
+
+ if ((!i->childCount())&&(i->parent()))
+ defines.append(((MatchViewItem *)(i))->command);
+ else {
+ i = i->firstChild();
+ while (i) {
+ defines.append(((MatchViewItem *)(i))->command);
+ i = i->nextSibling();
+ }
+ }
+
+ doGet(defines);
+}
+
+
+void MatchView::getSelected()
+{
+ QStringList defines;
+ MatchViewItem *top = static_cast<MatchViewItem*>(w_list->firstChild());
+ MatchViewItem *sub;
+
+ while (top) {
+ if (top->isSelected()&&(!top->subEntrys.isEmpty())) {
+ QString command;
+ QStringList::iterator it;
+ for (it = top->subEntrys.begin(); it != top->subEntrys.end(); ++it) {
+ command = "define ";
+ command += (*it);
+ command += "\r\n";
+ defines.append(command);
+ }
+ } else {
+ sub = static_cast<MatchViewItem*>(top->firstChild());
+ while (sub) {
+ if (top->isSelected()||sub->isSelected())
+ defines.append(sub->command);
+ sub = static_cast<MatchViewItem*>(sub->nextSibling());
+ }
+ }
+ top = static_cast<MatchViewItem*>(top->nextSibling());
+ }
+ doGet(defines);
+}
+
+
+void MatchView::getAll()
+{
+ QStringList defines;
+ MatchViewItem *top = static_cast<MatchViewItem*>(w_list->firstChild());
+ MatchViewItem *sub;
+
+ while (top) {
+ if (!top->subEntrys.isEmpty()) {
+ QString command;
+ QStringList::iterator it;
+ for (it = top->subEntrys.begin(); it != top->subEntrys.end(); ++it) {
+ command = "define ";
+ command += (*it);
+ command += "\r\n";
+ defines.append(command);
+ }
+ } else {
+ sub = static_cast<MatchViewItem*>(top->firstChild());
+ while (sub) {
+ defines.append(sub->command);
+ sub = static_cast<MatchViewItem*>(sub->nextSibling());
+ }
+ }
+ top = static_cast<MatchViewItem*>(top->nextSibling());
+ }
+ doGet(defines);
+}
+
+
+void MatchView::doGet(QStringList &defines)
+{
+ if (defines.count() > 0) {
+ if (defines.count() > global->maxDefinitions) {
+ KMessageBox::sorry(global->topLevel,i18n("You have selected %1 definitions,\nbut Kdict will fetch only the first %2 definitions.\nYou can modify this limit in the Preferences Dialog.")
+ .arg(defines.count()).arg(global->maxDefinitions));
+ while (defines.count()>global->maxDefinitions)
+ defines.pop_back();
+ }
+ interface->getDefinitions(defines);
+ }
+}
+
+
+void MatchView::newList(const QStringList &matches)
+{
+ MatchViewItem *top=0;
+ bool initialOpen = (matches.count()<200);
+ int numDb = 0;
+
+ rightBtnMenu->hide();
+ w_list->clear();
+ w_list->setColumnWidth(0,0);
+ w_list->setUpdatesEnabled(false);
+ w_get->setEnabled(false);
+ getOn = false;
+
+ if (matches.isEmpty()) {
+ w_list->setColumnWidth(0,w_get->width()-5);
+ w_list->setRootIsDecorated(false);
+ w_getAll->setEnabled(false);
+ getAllOn = false;
+ top = new MatchViewItem(w_list,top,i18n(" No Hits"));
+ } else {
+ w_list->setRootIsDecorated(true);
+ w_getAll->setEnabled(true);
+ getAllOn = true;
+ QString lastDb, db, match;
+
+ QStringList::const_iterator it;
+ for (it = matches.begin(); it != matches.end(); ++it) {
+ db = (*it).section(' ', 0, 0);
+
+ if (db != lastDb) {
+ numDb++;
+ if (top) {
+ top->setOpen(initialOpen);
+ top = new MatchViewItem(w_list, top, db);
+ } else
+ top = new MatchViewItem(w_list, db);
+ top->setExpandable(true);
+ lastDb = db;
+ }
+
+ if (top)
+ top->subEntrys.append(*it);
+ }
+
+ if ((numDb == 1)||(initialOpen))
+ top->setOpen(true);
+ }
+
+ w_list->setUpdatesEnabled(true);
+ w_list->repaint();
+ w_list->setFocus();
+}
+
+
+// construct the right-mouse-button-popup-menu on demand
+void MatchView::buildPopupMenu(QListViewItem *i, const QPoint &_point, int)
+{
+ rightBtnMenu->clear();
+
+ if ((i!=0L)&&(i->isExpandable()||i->parent())) {
+ popupCurrent = (MatchViewItem *)(i);
+ rightBtnMenu->insertItem(i18n("&Get"),this,SLOT(popupGetCurrent()));
+ if (!i->isExpandable()) { // toplevel item -> only "get"
+ rightBtnMenu->insertItem(i18n("&Match"),this,SLOT(popupMatchCurrent()));
+ rightBtnMenu->insertItem(i18n("&Define"),this,SLOT(popupDefineCurrent()));
+ }
+ rightBtnMenu->insertSeparator();
+ }
+
+ kapp->clipboard()->setSelectionMode(false);
+ QString text = kapp->clipboard()->text();
+ if (text.isEmpty()) {
+ kapp->clipboard()->setSelectionMode(true);
+ text = kapp->clipboard()->text();
+ }
+ if (!text.isEmpty()) {
+ popupClip = kapp->clipboard()->text();
+ rightBtnMenu->insertItem(i18n("Match &Clipboard Content"),this,SLOT(popupMatchClip()));
+ rightBtnMenu->insertItem(SmallIcon("define_clip"),i18n("D&efine Clipboard Content"),this,SLOT(popupDefineClip()));
+ rightBtnMenu->insertSeparator();
+ }
+
+ int ID = rightBtnMenu->insertItem(i18n("Get &Selected"),this,SLOT(getSelected()));
+ rightBtnMenu->setItemEnabled(ID,getOn);
+ ID = rightBtnMenu->insertItem(i18n("Get &All"),this,SLOT(getAll()));
+ rightBtnMenu->setItemEnabled(ID,getAllOn);
+
+ if (w_list->childCount()) {
+ rightBtnMenu->insertSeparator();
+ rightBtnMenu->insertItem(i18n("E&xpand List"),this,SLOT(expandList()));
+ rightBtnMenu->insertItem(i18n("C&ollapse List"),this,SLOT(collapseList()));
+ }
+
+ rightBtnMenu->popup(_point);
+}
+
+
+void MatchView::popupGetCurrent()
+{
+ getOneItem(popupCurrent);
+}
+
+
+void MatchView::popupDefineCurrent()
+{
+ emit(defineRequested(popupCurrent->text(0)));
+}
+
+
+void MatchView::popupMatchCurrent()
+{
+ emit(matchRequested(popupCurrent->text(0)));
+}
+
+
+void MatchView::popupDefineClip()
+{
+ emit(defineRequested(popupClip));
+}
+
+
+void MatchView::popupMatchClip()
+{
+ emit(matchRequested(popupClip));
+}
+
+
+void MatchView::expandList()
+{
+ QListViewItem *top = w_list->firstChild();
+
+ while (top) {
+ w_list->setOpen(top,true);
+ top = top->nextSibling();
+ }
+}
+
+
+void MatchView::collapseList()
+{
+ w_list->setCurrentItem(w_list->firstChild());
+ QListViewItem *top = w_list->firstChild();
+
+ while (top) {
+ w_list->setOpen(top,false);
+ top = top->nextSibling();
+ }
+}
+
+//--------------------------------
+
+#include "matchview.moc"
diff --git a/kdict/matchview.h b/kdict/matchview.h
new file mode 100644
index 00000000..f619820e
--- /dev/null
+++ b/kdict/matchview.h
@@ -0,0 +1,105 @@
+/* -------------------------------------------------------------
+
+ matchview.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ MatchView This widget contains the list of matching definitions
+
+ ------------------------------------------------------------- */
+
+
+#ifndef _MATCHVIEW_H_
+#define _MATCHVIEW_H_
+
+#include <qlistview.h>
+class KPopupMenu;
+
+
+//********* MatchViewItem ********************************************
+
+class MatchViewItem : public QListViewItem
+{
+
+public:
+
+ MatchViewItem(QListView *view,const QString &text);
+ MatchViewItem(QListView *view,QListViewItem *after,const QString &text);
+ MatchViewItem(QListViewItem *item,const QString &text,const QString &commandStr);
+ MatchViewItem(QListViewItem *item,QListViewItem *after,const QString &text,const QString &commandStr);
+ ~MatchViewItem();
+
+ void setOpen(bool o);
+ void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment);
+
+ QString command;
+ QStringList subEntrys;
+};
+
+
+//********* MatchView ******************************************
+
+
+class MatchView : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ MatchView(QWidget *parent=0,const char *name=0);
+ ~MatchView();
+
+ void updateStrategyCombo();
+ bool selectStrategy(const QString &strategy) const;
+ void match(const QString &query);
+
+signals:
+
+ void defineRequested(const QString &query);
+ void matchRequested(const QString &query);
+ void clipboardRequested();
+ void windowClosed();
+
+protected:
+
+ void closeEvent ( QCloseEvent * e );
+
+private slots:
+
+ void strategySelected(int num);
+ void enableGetButton();
+ void mouseButtonPressed(int, QListViewItem *, const QPoint &, int);
+ void returnPressed(QListViewItem *i);
+ void getOneItem(QListViewItem *i);
+ void getSelected();
+ void getAll();
+ void doGet(QStringList &defines);
+ void newList(const QStringList &matches);
+ void buildPopupMenu(QListViewItem *, const QPoint &, int);
+ void popupGetCurrent();
+ void popupDefineCurrent();
+ void popupMatchCurrent();
+ void popupDefineClip();
+ void popupMatchClip();
+ void expandList();
+ void collapseList();
+
+private:
+
+ QComboBox *w_strat;
+ QListView *w_list;
+ QPushButton *w_get,*w_getAll;
+
+ bool getOn, getAllOn;
+
+ KPopupMenu *rightBtnMenu;
+ MatchViewItem *popupCurrent;
+ QString popupClip; // needed for rightbtn-popup menu
+};
+
+#endif
diff --git a/kdict/options.cpp b/kdict/options.cpp
new file mode 100644
index 00000000..a157fbf5
--- /dev/null
+++ b/kdict/options.cpp
@@ -0,0 +1,947 @@
+/* -------------------------------------------------------------
+
+ options.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ GlobalData manages all global data of Kdict
+
+ OptionsDialog the "Preferences" dialog
+
+ DbSetsDialog dialog for editing the user defined database sets
+
+ ------------------------------------------------------------- */
+
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcombobox.h>
+#include <qbuttongroup.h>
+#include <qpainter.h>
+
+#include <kcolordialog.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <knumvalidator.h>
+#include <knuminput.h>
+#include <klineedit.h>
+#include <kcharsets.h>
+#include <kfontdialog.h>
+#include <kiconloader.h>
+
+#include "options.h"
+
+
+//********* DictOptions ******************************************
+
+
+void GlobalData::read()
+{
+ KConfig *config=KGlobal::config();
+
+ // general...
+ config->setGroup("General");
+ defineClipboard = config->readBoolEntry("Lookup_Clipboard",false);
+ headLayout = config->readNumEntry("Heading_Layout",0);
+ if ((headLayout > 2)||(headLayout < 0))
+ headLayout = 0;
+ saveHistory = config->readBoolEntry("Save_History",true);
+ maxHistEntrys = config->readNumEntry("Max_History_Entrys",500);
+ if ((maxHistEntrys < 10)||(maxHistEntrys >5000))
+ maxHistEntrys = 200;
+ maxBrowseListEntrys = config->readNumEntry("Max_Browse_Entrys",15);
+ if ((maxBrowseListEntrys < 1)||(maxBrowseListEntrys > 50))
+ maxBrowseListEntrys = 15;
+ maxDefinitions = config->readNumEntry("Max_Definitions",2000);
+ if ((maxDefinitions < 100)||(maxDefinitions > 10000))
+ maxDefinitions = 2000;
+
+ //colors
+ config->setGroup("Colors");
+ useCustomColors=config->readBoolEntry("customColors", false);
+
+ QColor defCol=KGlobalSettings::textColor();
+ c_olors[Ctext]=config->readColorEntry("textColor",&defCol);
+ c_olorNames[Ctext]=i18n("Text");
+
+ defCol=KGlobalSettings::baseColor();
+ c_olors[Cbackground]=config->readColorEntry("backgroundColor",&defCol);
+ c_olorNames[Cbackground]=i18n("Background");
+
+ defCol=KGlobalSettings::highlightedTextColor();
+ c_olors[CheadingsText]=config->readColorEntry("headingsTextColor",&defCol);
+ c_olorNames[CheadingsText]=i18n("Heading Text");
+
+ defCol=KGlobalSettings::highlightColor();
+ c_olors[CheadingsBackground]=config->readColorEntry("headingsBackgroundColor",&defCol);
+ c_olorNames[CheadingsBackground]=i18n("Heading Background");
+
+ defCol=KGlobalSettings::linkColor();
+ c_olors[Clinks]=config->readColorEntry("linksColor",&defCol);
+ c_olorNames[Clinks]=i18n("Link");
+
+ defCol=KGlobalSettings::visitedLinkColor();
+ c_olors[CvisitedLinks]=config->readColorEntry("linksColor",&defCol);
+ c_olorNames[CvisitedLinks]=i18n("Followed Link");
+
+ //fonts
+ config->setGroup("Fonts");
+ useCustomFonts=config->readBoolEntry("customFonts", false);
+
+ QFont defFont=KGlobalSettings::generalFont();
+ f_onts[Ftext]=config->readFontEntry("textFont",&defFont);
+ f_ontNames[Ftext]=i18n("Text");
+
+ defFont.setBold( true );
+ defFont.setPointSize(defFont.pointSize()+2);
+ f_onts[Fheadings]=config->readFontEntry("headingsFont",&defFont);
+ f_ontNames[Fheadings]=i18n("Headings");
+
+ // geometry...
+ config->setGroup("Geometry");
+ QSize invalid(-1,-1);
+ optSize = config->readSizeEntry("Opt_Size",&invalid);
+ setsSize = config->readSizeEntry("Sets_Size",&invalid);
+ matchSize = config->readSizeEntry("Match_Size",&invalid);
+ showMatchList = config->readBoolEntry("Show_MatchList",false);
+ splitterSizes = config->readIntListEntry("Splitter_Sizes");
+
+ config->setGroup("Query Combo");
+ queryComboCompletionMode = (KGlobalSettings::Completion)config->readNumEntry("Completion_mode",
+ KGlobalSettings::completionMode());
+
+ config->setGroup("Query_History");
+ queryHistory = config->readListEntry("History");
+
+ config->setGroup("DictServer");
+ server = config->readEntry("Server", "dict.org");
+ port = config->readNumEntry("Port", 2628);
+ if (port < 0)
+ port = 2628;
+ timeout = config->readNumEntry("Timeout",60);
+ if (timeout < 0)
+ timeout = 60;
+ pipeSize = config->readNumEntry("Pipe_Size",256);
+ if (pipeSize < 0)
+ pipeSize = 256;
+ idleHold = config->readNumEntry("Idle_Hold",30);
+ if (idleHold < 0)
+ idleHold = 30;
+ encoding=config->readEntry("encoding", "utf8");
+ authEnabled = config->readBoolEntry("Auth_Enabled",false);
+ user = config->readEntry("User");
+ secret = encryptStr(config->readEntry("Secret"));
+ serverDatabases = config->readListEntry("Server_Databases");
+ currentDatabase = config->readNumEntry("Current_Database",0);
+ strategies = config->readListEntry("Strategies");
+ if (strategies.isEmpty()) {
+ strategies.append(i18n("Spell Check"));
+ strategies.append(i18n("Exact"));
+ strategies.append(i18n("Prefix"));
+ } else {
+ strategies.remove(strategies.begin());
+ strategies.prepend(i18n("Spell Check"));
+ }
+
+ currentStrategy = config->readNumEntry("Current_Strategy",0);
+ if (currentStrategy >= strategies.count())
+ currentStrategy = 0;
+
+ config->setGroup("Database_Sets");
+ databaseSets.setAutoDelete(true);
+ int num = config->readNumEntry("Num_Sets",0);
+ QStringList* temp;
+ QString strNum;
+ while (num > 0) {
+ temp = new QStringList();
+ strNum.setNum(num);
+ *temp = config->readListEntry(strNum);
+ if (!temp->isEmpty()) {
+ databaseSets.prepend(temp);
+ num--;
+ } else {
+ delete temp;
+ num = 0; // stop reading...
+ }
+ }
+ databases = serverDatabases;
+ for (int i = databaseSets.count()-1;i>=0;i--)
+ databases.prepend(databaseSets.at(i)->first());
+ databases.prepend(i18n("All Databases"));
+ if (currentDatabase >= databases.count())
+ currentDatabase = 0;
+}
+
+
+void GlobalData::write()
+{
+ KConfig *config=KGlobal::config();
+
+ config->setGroup("General");
+ config->writeEntry("Lookup_Clipboard",defineClipboard);
+ config->writeEntry("Heading_Layout",headLayout);
+ config->writeEntry("Save_History",saveHistory);
+ config->writeEntry("Max_History_Entrys",maxHistEntrys);
+ config->writeEntry("Max_Browse_Entrys",maxBrowseListEntrys);
+ config->writeEntry("Max_Definitions",maxDefinitions);
+
+ config->setGroup("Colors");
+ config->writeEntry("customColors",useCustomColors);
+ config->writeEntry("textColor", c_olors[Ctext]);
+ config->writeEntry("backgroundColor", c_olors[Cbackground]);
+ config->writeEntry("headingsTextColor", c_olors[CheadingsText]);
+ config->writeEntry("headingsBackgroundColor", c_olors[CheadingsBackground]);
+ config->writeEntry("linksColor", c_olors[Clinks]);
+ config->writeEntry("visitedLinksColor", c_olors[CvisitedLinks]);
+
+ config->setGroup("Fonts");
+ config->writeEntry("customFonts", useCustomFonts);
+ config->writeEntry("textFont", f_onts[Ftext]);
+ config->writeEntry("headingsFont", f_onts[Fheadings]);
+
+ config->setGroup("Geometry");
+ config->writeEntry("Opt_Size",optSize);
+ config->writeEntry("Sets_Size",setsSize);
+ config->writeEntry("Match_Size",matchSize);
+ config->writeEntry("Show_MatchList",showMatchList);
+ config->writeEntry("Splitter_Sizes",splitterSizes);
+
+ config->setGroup("Query Combo");
+ config->writeEntry("Completion_mode", (int)queryComboCompletionMode);
+
+ config->setGroup("Query_History");
+ QStringList copy;
+ if (saveHistory)
+ copy = queryHistory;
+ config->writeEntry("History",copy);
+
+ config->setGroup("DictServer");
+ config->writeEntry("Server", server);
+ config->writeEntry("Port", port);
+ config->writeEntry("Timeout",timeout);
+ config->writeEntry("Pipe_Size",pipeSize);
+ config->writeEntry("Idle_Hold",idleHold);
+ config->writeEntry("encoding", encoding);
+ config->writeEntry("Auth_Enabled",authEnabled);
+ config->writeEntry("User", user);
+ config->writeEntry("Secret", encryptStr(secret));
+ config->writeEntry("Server_Databases",serverDatabases);
+ config->writeEntry("Current_Database",currentDatabase);
+ config->writeEntry("Strategies",strategies);
+ config->writeEntry("Current_Strategy",currentStrategy);
+
+ config->setGroup("Database_Sets");
+ config->writeEntry("Num_Sets",databaseSets.count());
+ QString strNum;
+ for (unsigned int i = 0;i<databaseSets.count();i++)
+ config->writeEntry(strNum.setNum(i+1),*databaseSets.at(i));
+}
+
+
+QColor GlobalData::defaultColor(int i)
+{
+ switch(i) {
+ case Ctext:
+ return KGlobalSettings::textColor();
+ break;
+
+ case Cbackground:
+ return KGlobalSettings::baseColor();
+ break;
+
+ case CheadingsText:
+ return KGlobalSettings::highlightedTextColor();
+ break;
+
+ case CheadingsBackground:
+ return KGlobalSettings::highlightColor();
+ break;
+
+ case Clinks:
+ return KGlobalSettings::linkColor();
+ break;
+
+ case CvisitedLinks:
+ return KGlobalSettings::visitedLinkColor();
+ break;
+
+ }
+
+ return KGlobalSettings::baseColor();
+}
+
+
+QColor GlobalData::textColor()
+{
+ if(useCustomColors)
+ return c_olors[Ctext];
+ else
+ return defaultColor(Ctext);
+}
+
+
+QColor GlobalData::backgroundColor()
+{
+ if(useCustomColors)
+ return c_olors[Cbackground];
+ else
+ return defaultColor(Cbackground);
+}
+
+
+QColor GlobalData::headingsTextColor()
+{
+ if(useCustomColors)
+ return c_olors[CheadingsText];
+ else
+ return defaultColor(CheadingsText);
+}
+
+
+QColor GlobalData::headingsBackgroundColor()
+{
+ if(useCustomColors)
+ return c_olors[CheadingsBackground];
+ else
+ return defaultColor(CheadingsBackground);
+}
+
+
+QColor GlobalData::linksColor()
+{
+ if(useCustomColors)
+ return c_olors[Clinks];
+ else
+ return defaultColor(Clinks);
+}
+
+
+QColor GlobalData::visitedLinksColor()
+{
+ if(useCustomColors)
+ return c_olors[CvisitedLinks];
+ else
+ return defaultColor(CvisitedLinks);
+}
+
+
+QFont GlobalData::defaultFont(int i)
+{
+ QFont font = KGlobalSettings::generalFont();
+
+ if (font.pointSize() < 5)
+ font.setPointSize(12);
+
+ if (i==Fheadings)
+ font.setPointSize(font.pointSize()+5);
+
+ return font;
+}
+
+
+QFont GlobalData::textFont()
+{
+ if(useCustomFonts)
+ return f_onts[Ftext];
+ else
+ return defaultFont(Ftext);
+}
+
+
+QFont GlobalData::headingsFont()
+{
+ if(useCustomFonts)
+ return f_onts[Fheadings];
+ else
+ return defaultFont(Fheadings);
+}
+
+
+// simple password scrambling...
+QString GlobalData::encryptStr(const QString& aStr)
+{
+ uint i,val,len = aStr.length();
+ QString result;
+
+ for (i=0; i<len; i++)
+ {
+ val = aStr[i] - ' ';
+ val = (255-' ') - val;
+ result += (char)(val + ' ');
+ }
+
+ return result;
+}
+
+
+//********* OptionsDialog::DialogListBox *****************************
+
+
+OptionsDialog::DialogListBox::DialogListBox(bool alwaysIgnore, QWidget * parent, const char * name)
+ : QListBox(parent, name), a_lwaysIgnore(alwaysIgnore)
+{
+}
+
+
+OptionsDialog::DialogListBox::~DialogListBox()
+{
+}
+
+
+void OptionsDialog::DialogListBox::keyPressEvent(QKeyEvent *e)
+{
+ if ((a_lwaysIgnore || !(hasFocus()&&isVisible()))&&((e->key()==Key_Enter)||(e->key()==Key_Return)))
+ e->ignore();
+ else
+ QListBox::keyPressEvent(e);
+}
+
+
+//********* OptionsDialog::ColorListItem *****************************
+
+
+OptionsDialog::ColorListItem::ColorListItem( const QString &text, const QColor &color )
+ : QListBoxText(text), mColor( color )
+{
+}
+
+
+OptionsDialog::ColorListItem::~ColorListItem()
+{
+}
+
+
+void OptionsDialog::ColorListItem::paint( QPainter *p )
+{
+ QFontMetrics fm = p->fontMetrics();
+ int h = fm.height();
+
+ p->drawText( 30+3*2, fm.ascent() + fm.leading()/2, text() );
+
+ p->setPen( Qt::black );
+ p->drawRect( 3, 1, 30, h-1 );
+ p->fillRect( 4, 2, 28, h-3, mColor );
+}
+
+
+int OptionsDialog::ColorListItem::height(const QListBox *lb ) const
+{
+ return( lb->fontMetrics().lineSpacing()+1 );
+}
+
+
+int OptionsDialog::ColorListItem::width(const QListBox *lb ) const
+{
+ return( 30 + lb->fontMetrics().width( text() ) + 6 );
+}
+
+
+//********* OptionsDialog::FontListItem *****************************
+
+
+OptionsDialog::FontListItem::FontListItem( const QString &name, const QFont &font )
+ : QListBoxText(name), f_ont(font)
+{
+ fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize());
+}
+
+
+OptionsDialog::FontListItem::~FontListItem()
+{
+}
+
+
+void OptionsDialog::FontListItem::setFont(const QFont &font)
+{
+ f_ont = font;
+ fontInfo = QString("[%1 %2]").arg(f_ont.family()).arg(f_ont.pointSize());
+}
+
+
+void OptionsDialog::FontListItem::paint( QPainter *p )
+{
+ QFont fnt = p->font();
+ fnt.setWeight(QFont::Bold);
+ p->setFont(fnt);
+ int fontInfoWidth = p->fontMetrics().width(fontInfo);
+ int h = p->fontMetrics().ascent() + p->fontMetrics().leading()/2;
+ p->drawText(2, h, fontInfo );
+ fnt.setWeight(QFont::Normal);
+ p->setFont(fnt);
+ p->drawText(5 + fontInfoWidth, h, text() );
+}
+
+
+int OptionsDialog::FontListItem::width(const QListBox *lb ) const
+{
+ return( lb->fontMetrics().width(fontInfo) + lb->fontMetrics().width(text()) + 20 );
+}
+
+
+//********* OptionsDialog ******************************************
+
+
+OptionsDialog::OptionsDialog(QWidget *parent, const char *name)
+ : KDialogBase(IconList, i18n("Configure"), Help|Default|Ok|Apply|Cancel, Ok, parent, name, false, true)
+{
+
+ //******** Server ************************************
+ serverTab = addPage(i18n("Server"),i18n("DICT Server Configuration"), BarIcon("network", KIcon::SizeMedium ));
+ QGridLayout* grid = new QGridLayout(serverTab,10,3,0,spacingHint());
+
+ w_server = new KLineEdit(serverTab);
+ w_server->setText(global->server);
+ QLabel *l = new QLabel(w_server, i18n("Host&name:"), serverTab);
+ grid->addWidget(l,0,0);
+ grid->addMultiCellWidget(w_server,0,0,1,2);
+ connect( w_server, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+
+ w_port = new KLineEdit(serverTab);
+ w_port->setValidator(new KIntValidator(0,65536,this));
+ w_port->setText(QString::number(global->port));
+ l = new QLabel(w_port, i18n("&Port:"), serverTab);
+ grid->addWidget(l,1,0);
+ grid->addWidget(w_port,1,1);
+ connect( w_port, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+
+ w_idleHold = new KIntSpinBox(0,300,5,0,10,serverTab);
+ w_idleHold->setSuffix(i18n(" sec"));
+ w_idleHold->setValue(global->idleHold);
+ l = new QLabel(w_idleHold, i18n("Hold conn&ection for:"), serverTab);
+ grid->addWidget(l,2,0);
+ grid->addWidget(w_idleHold,2,1);
+ connect( w_idleHold, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) );
+
+ w_timeout = new KIntSpinBox(5,600,5,5,10,serverTab);
+ w_timeout->setSuffix(i18n(" sec"));
+ w_timeout->setValue(global->timeout);
+ l = new QLabel(w_timeout, i18n("T&imeout:"), serverTab);
+ grid->addWidget(l,3,0);
+ grid->addWidget(w_timeout,3,1);
+ connect( w_timeout, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) );
+
+ w_pipesize = new KIntSpinBox(100,5000,2,2,10,serverTab);
+ w_pipesize->setSuffix(i18n(" bytes"));
+ w_pipesize->setValue(global->pipeSize);
+ l = new QLabel(w_pipesize, i18n("Command &buffer:"), serverTab);
+ grid->addWidget(l,4,0);
+ grid->addWidget(w_pipesize,4,1);
+ connect( w_pipesize, SIGNAL( valueChanged(int) ), this, SLOT( slotChanged() ) );
+
+ QStringList encodingNames = KGlobal::charsets()->descriptiveEncodingNames();
+ int i=0,x=0;
+ for ( QStringList::Iterator it = encodingNames.begin(); it != encodingNames.end(); ++it ) {
+ if (KGlobal::charsets()->encodingForName(*it)==global->encoding) {
+ x = i;
+ break;
+ }
+ i++;
+ }
+ w_encoding = new QComboBox(serverTab);
+ w_encoding->insertStringList(encodingNames);
+ w_encoding->setCurrentItem(x);
+ l = new QLabel(w_encoding, i18n("Encod&ing:"), serverTab);
+ grid->addWidget(l,5,0);
+ grid->addMultiCellWidget(w_encoding,5,5,1,2);
+ connect( w_encoding, SIGNAL( activated(int) ), this, SLOT( slotChanged() ) );
+
+ w_auth = new QCheckBox(i18n("Server requires a&uthentication"),serverTab);
+ w_auth->setChecked(global->authEnabled);
+ grid->addMultiCellWidget(w_auth,6,6,0,2);
+ connect( w_auth, SIGNAL( toggled(bool) ), this, SLOT( slotChanged() ) );
+ connect(w_auth,SIGNAL(toggled(bool)),SLOT(slotAuthRequiredToggled(bool)));
+
+ w_user = new KLineEdit(serverTab);
+ w_user->setText(global->user);
+ l_user = new QLabel(w_user, i18n("U&ser:"),serverTab);
+ grid->addWidget(l_user,7,0);
+ grid->addMultiCellWidget(w_user,7,7,1,2);
+ connect( w_user, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+
+ w_secret = new KLineEdit(serverTab);
+ w_secret->setEchoMode(QLineEdit::Password);
+ w_secret->setText(global->secret);
+ l_secret = new QLabel(w_secret, i18n("Pass&word:"), serverTab);
+ grid->addWidget(l_secret,8,0);
+ grid->addMultiCellWidget(w_secret,8,8,1,2);
+ connect( w_secret, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotChanged() ) );
+
+ slotAuthRequiredToggled( w_auth->isChecked() );
+
+ grid->setColStretch(1,2);
+ grid->setColStretch(2,2);
+
+ //************ Appearance ***************************
+ appTab = addPage(i18n("Appearance"),i18n("Customize Visual Appearance"), BarIcon("appearance", KIcon::SizeMedium ));
+
+ QGridLayout *topL=new QGridLayout(appTab, 8, 3, 0, spacingHint());
+
+ //color-list
+ c_List = new DialogListBox(false, appTab);
+ topL->addMultiCellWidget(c_List,1,3,0,1);
+ connect(c_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotColItemSelected(QListBoxItem*)));
+ connect(c_List, SIGNAL(selectionChanged()), SLOT(slotColSelectionChanged()));
+
+ c_olorCB = new QCheckBox(i18n("&Use custom colors"),appTab);
+ topL->addWidget(c_olorCB,0,0);
+ connect(c_olorCB, SIGNAL(toggled(bool)), this, SLOT(slotColCheckBoxToggled(bool)));
+ connect(c_olorCB, SIGNAL(toggled(bool) ), this, SLOT( slotChanged()));
+
+ c_olChngBtn=new QPushButton(i18n("Cha&nge..."), appTab);
+ connect(c_olChngBtn, SIGNAL(clicked()), SLOT(slotColChangeBtnClicked()));
+ topL->addWidget(c_olChngBtn,1,2);
+
+ c_olDefBtn=new QPushButton(i18n("Default&s"), appTab);
+ connect(c_olDefBtn, SIGNAL(clicked()), SLOT(slotColDefaultBtnClicked()));
+ topL->addWidget(c_olDefBtn,2,2);
+ connect(c_olDefBtn, SIGNAL(clicked()), SLOT(slotChanged()));
+
+ //font-list
+ f_List = new DialogListBox(false, appTab);
+ topL->addMultiCellWidget(f_List,5,7,0,1);
+ connect(f_List, SIGNAL(selected(QListBoxItem*)),SLOT(slotFontItemSelected(QListBoxItem*)));
+ connect(f_List, SIGNAL(selectionChanged()),SLOT(slotFontSelectionChanged()));
+
+ f_ontCB = new QCheckBox(i18n("Use custom &fonts"),appTab);
+ topL->addWidget(f_ontCB,4,0);
+ connect(f_ontCB, SIGNAL(toggled(bool)), SLOT(slotFontCheckBoxToggled(bool)));
+ connect(f_ontCB, SIGNAL(toggled(bool)), SLOT(slotChanged()));
+
+ f_ntChngBtn=new QPushButton(i18n("Chang&e..."), appTab);
+ connect(f_ntChngBtn, SIGNAL(clicked()), SLOT(slotFontChangeBtnClicked()));
+ topL->addWidget(f_ntChngBtn,5,2);
+
+ f_ntDefBtn=new QPushButton(i18n("Defaul&ts"), appTab);
+ connect(f_ntDefBtn, SIGNAL(clicked()), SLOT(slotFontDefaultBtnClicked()));
+ topL->addWidget(f_ntDefBtn,6,2);
+ connect(f_ntDefBtn, SIGNAL(clicked()), SLOT(slotChanged()));
+
+ topL->setColStretch(1,2);
+ topL->setColStretch(2,0);
+ topL->setRowStretch(3,1);
+ topL->setRowStretch(7,1);
+ topL->setResizeMode(QLayout::Minimum);
+
+ //init
+ c_olorCB->setChecked(global->useCustomColors);
+ slotColCheckBoxToggled(global->useCustomColors);
+ for(int i=0; i<global->colorCount(); i++)
+ c_List->insertItem(new ColorListItem(global->colorName(i), global->color(i)));
+
+ f_ontCB->setChecked(global->useCustomFonts);
+ slotFontCheckBoxToggled(global->useCustomFonts);
+ for(int i=0; i<global->fontCount(); i++)
+ f_List->insertItem(new FontListItem(global->fontName(i), global->font(i)));
+
+ //************ Layout ***************************
+ layoutTab = addPage(i18n("Layout"),i18n("Customize Output Format"), BarIcon("text_left", KIcon::SizeMedium ));
+
+ QVBoxLayout *vbox = new QVBoxLayout(layoutTab, 0, spacingHint());
+
+ QButtonGroup *bGroup = new QButtonGroup(i18n("Headings"),layoutTab);
+ QVBoxLayout *bvbox = new QVBoxLayout(bGroup,8,5);
+
+ bvbox->addSpacing(fontMetrics().lineSpacing()-4);
+ w_layout[0] = new QRadioButton(i18n("O&ne heading for each database"),bGroup);
+ w_layout[0]->setChecked(global->headLayout == 0);
+ bvbox->addWidget(w_layout[0],1);
+ w_layout[1] = new QRadioButton(i18n("A&s above, with separators between the definitions"),bGroup);
+ w_layout[1]->setChecked(global->headLayout == 1);
+ bvbox->addWidget(w_layout[1],1);
+ w_layout[2] = new QRadioButton(i18n("A separate heading for &each definition"),bGroup);
+ w_layout[2]->setChecked(global->headLayout == 2);
+ bvbox->addWidget(w_layout[2],1);
+ connect(w_layout[0], SIGNAL(toggled(bool)), SLOT(slotChanged()));
+ connect(w_layout[1], SIGNAL(toggled(bool)), SLOT(slotChanged()));
+ connect(w_layout[2], SIGNAL(toggled(bool)), SLOT(slotChanged()));
+
+ vbox->addWidget(bGroup,0);
+ vbox->addStretch(1);
+
+ //************ Other ***************************
+ otherTab = addPage(i18n("Miscellaneous"),i18n("Various Settings"), BarIcon("misc", KIcon::SizeMedium ));
+
+ vbox = new QVBoxLayout(otherTab, 0, spacingHint());
+
+ QGroupBox *group = new QGroupBox(i18n("Limits"),otherTab);
+
+ grid = new QGridLayout(group,4,2,8,5);
+ grid->addRowSpacing(0, fontMetrics().lineSpacing()-4);
+
+ w_MaxDefinitions = new KIntSpinBox(100,10000,100,100,10,group);
+ w_MaxDefinitions->setValue(global->maxDefinitions);
+ l = new QLabel(w_MaxDefinitions, i18n("De&finitions:"), group);
+ grid->addWidget(l,1,0);
+ grid->addWidget(w_MaxDefinitions,1,1);
+ connect(w_MaxDefinitions, SIGNAL(valueChanged(int)), SLOT(slotChanged()));
+
+ w_Maxbrowse = new KIntSpinBox(1,100,1,1,10,group);
+ w_Maxbrowse->setValue(global->maxBrowseListEntrys);
+ l = new QLabel(w_Maxbrowse, i18n("Cached &results:"), group);
+ grid->addWidget(l,2,0);
+ grid->addWidget(w_Maxbrowse,2,1);
+ connect(w_Maxbrowse, SIGNAL(valueChanged(int)), SLOT(slotChanged()));
+
+ w_Maxhist = new KIntSpinBox(10,5000,10,10,10,group);
+ w_Maxhist->setValue(global->maxHistEntrys);
+ l = new QLabel(w_Maxhist, i18n("Hi&story entries:"), group);
+ grid->addWidget(l,3,0);
+ grid->addWidget(w_Maxhist,3,1);
+ connect(w_Maxhist, SIGNAL(valueChanged(int)), SLOT(slotChanged()));
+
+ grid->setColStretch(1,1);
+
+ vbox->addWidget(group,0);
+
+ group = new QGroupBox(i18n("Other"),otherTab);
+
+ QVBoxLayout *vbox2 = new QVBoxLayout(group, 8, 5);
+
+ vbox2->addSpacing(fontMetrics().lineSpacing()-4);
+
+ w_Savehist = new QCheckBox(i18n("Sa&ve history on exit"),group);
+ w_Savehist->setChecked(global->saveHistory);
+ vbox2->addWidget(w_Savehist,0);
+ connect(w_Savehist, SIGNAL(toggled(bool)), SLOT(slotChanged()));
+
+ w_Clipboard = new QCheckBox(i18n("D&efine selected text on start"),group);
+ w_Clipboard->setChecked(global->defineClipboard);
+ vbox2->addWidget(w_Clipboard,1);
+ connect(w_Clipboard, SIGNAL(toggled(bool)), SLOT(slotChanged()));
+
+ vbox->addWidget(group,0);
+ vbox->addStretch(2);
+
+ setHelp("preferences");
+
+ if (global->optSize.isValid())
+ resize(global->optSize);
+ else
+ resize(300,200);
+ enableButton( Apply, false );
+ configChanged = false;
+}
+
+
+OptionsDialog::~OptionsDialog()
+{
+ global->optSize = size();
+}
+
+
+void OptionsDialog::slotApply()
+{
+ global->server = w_server->text();
+ global->port = w_port->text().toInt();
+ global->timeout = w_timeout->value();
+ global->idleHold = w_idleHold->value();
+ global->pipeSize = w_pipesize->value();
+ global->encoding = KGlobal::charsets()->encodingForName(w_encoding->currentText());
+ global->authEnabled = w_auth->isChecked();
+ global->user = w_user->text();
+ global->secret = w_secret->text();
+ global->useCustomColors=c_olorCB->isChecked();
+ for(int i=0; i<global->colorCount(); i++)
+ global->c_olors[i] = (static_cast<ColorListItem*>(c_List->item(i)))->color();
+
+ global->useCustomFonts=f_ontCB->isChecked();
+ for(int i=0; i<global->fontCount(); i++)
+ global->f_onts[i] = (static_cast<FontListItem*>(f_List->item(i)))->font();
+ if (w_layout[0]->isChecked())
+ global->headLayout = 0;
+ else
+ if (w_layout[1]->isChecked())
+ global->headLayout = 1;
+ else
+ global->headLayout = 2;
+ global->maxDefinitions = w_MaxDefinitions->value();
+ global->maxBrowseListEntrys = w_Maxbrowse->value();
+ global->maxHistEntrys = w_Maxhist->value();
+ global->defineClipboard = w_Clipboard->isChecked();
+ global->saveHistory = w_Savehist->isChecked();
+ emit(optionsChanged());
+ enableButton( Apply, false );
+ configChanged = false;
+}
+
+
+void OptionsDialog::slotOk()
+{
+ if( configChanged )
+ slotApply();
+ KDialogBase::slotOk();
+}
+
+
+void OptionsDialog::slotDefault()
+{
+ QStringList encodingNames;
+ int i=0,x=0;
+
+ switch(activePageIndex()) {
+ case 0:
+ w_server->setText("dict.org");
+ w_port->setText("2628");
+ w_idleHold->setValue(30);
+ w_timeout->setValue(60);
+ w_pipesize->setValue(256);
+ encodingNames = KGlobal::charsets()->descriptiveEncodingNames();
+ for ( QStringList::Iterator it = encodingNames.begin(); it != encodingNames.end(); ++it ) {
+ if (KGlobal::charsets()->encodingForName(*it)=="utf8")
+ x = i;
+ i++;
+ }
+ w_encoding->setCurrentItem(x);
+ w_auth->setChecked(false);
+ w_user->clear();
+ w_user->setEnabled(false);
+ w_secret->clear();
+ w_secret->setEnabled(false);
+ break;
+ case 1:
+ c_olorCB->setChecked(false);
+ slotColCheckBoxToggled(false);
+ slotColDefaultBtnClicked();
+ f_ontCB->setChecked(false);
+ slotFontCheckBoxToggled(false);
+ slotFontDefaultBtnClicked();
+ break;
+ case 2:
+ w_layout[0]->setChecked(true);
+ break;
+ case 3:
+ w_MaxDefinitions->setValue(2000);
+ w_Maxbrowse->setValue(15);
+ w_Maxhist->setValue(500);
+ w_Savehist->setChecked(true);
+ w_Clipboard->setChecked(false);
+ }
+}
+
+
+void OptionsDialog::slotAuthRequiredToggled( bool enable )
+{
+ l_user->setEnabled( enable );
+ l_secret->setEnabled( enable );
+ w_user->setEnabled( enable );
+ w_secret->setEnabled( enable );
+}
+
+
+void OptionsDialog::slotColCheckBoxToggled(bool b)
+{
+ c_List->setEnabled(b);
+ c_olDefBtn->setEnabled(b);
+ c_olChngBtn->setEnabled(b && (c_List->currentItem()!=-1));
+ if (b) c_List->setFocus();
+}
+
+
+// show color dialog for the entry
+void OptionsDialog::slotColItemSelected(QListBoxItem *it)
+{
+ if (it) {
+ ColorListItem *colorItem = static_cast<ColorListItem*>(it);
+ QColor col = colorItem->color();
+ int result = KColorDialog::getColor(col,this);
+
+ if (result == KColorDialog::Accepted) {
+ colorItem->setColor(col);
+ c_List->triggerUpdate(false);
+ slotChanged();
+ }
+ }
+}
+
+
+void OptionsDialog::slotColDefaultBtnClicked()
+{
+ ColorListItem *colorItem;
+ for(int i=0; i < global->colorCount(); i++) {
+ colorItem=static_cast<ColorListItem*>(c_List->item(i));
+ colorItem->setColor(global->defaultColor(i));
+ }
+ c_List->triggerUpdate(true);
+ c_List->repaint(true);
+}
+
+
+void OptionsDialog::slotColChangeBtnClicked()
+{
+ if(c_List->currentItem()!=-1)
+ slotColItemSelected(c_List->item(c_List->currentItem()));
+}
+
+
+void OptionsDialog::slotColSelectionChanged()
+{
+ c_olChngBtn->setEnabled(c_List->currentItem()!=-1);
+}
+
+
+void OptionsDialog::slotFontCheckBoxToggled(bool b)
+{
+ f_List->setEnabled(b);
+ f_ntDefBtn->setEnabled(b);
+ f_ntChngBtn->setEnabled(b && (f_List->currentItem()!=-1));
+ if (b) f_List->setFocus();
+}
+
+
+// show font dialog for the entry
+void OptionsDialog::slotFontItemSelected(QListBoxItem *it)
+{
+ if (it) {
+ FontListItem *fontItem = static_cast<FontListItem*>(it);
+ QFont font = fontItem->font();
+ int result = KFontDialog::getFont(font,false,this);
+
+ if (result == KFontDialog::Accepted) {
+ fontItem->setFont(font);
+ f_List->triggerUpdate(false);
+ slotChanged();
+ }
+ }
+}
+
+
+void OptionsDialog::slotFontDefaultBtnClicked()
+{
+ FontListItem *fontItem;
+ for(int i=0; i < global->fontCount(); i++) {
+ fontItem=static_cast<FontListItem*>(f_List->item(i));
+ fontItem->setFont(global->defaultFont(i));
+ }
+ f_List->triggerUpdate(false);
+}
+
+
+void OptionsDialog::slotFontChangeBtnClicked()
+{
+ if(f_List->currentItem()!=-1)
+ slotFontItemSelected(f_List->item(f_List->currentItem()));
+}
+
+
+void OptionsDialog::slotFontSelectionChanged()
+{
+ f_ntChngBtn->setEnabled(f_List->currentItem()!=-1);
+}
+
+void OptionsDialog::slotChanged()
+{
+ enableButton( Apply, true );
+ configChanged = true;
+}
+
+
+//--------------------------------
+
+#include "options.moc"
diff --git a/kdict/options.h b/kdict/options.h
new file mode 100644
index 00000000..ecef7606
--- /dev/null
+++ b/kdict/options.h
@@ -0,0 +1,229 @@
+/* -------------------------------------------------------------
+
+ options.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ GlobalData manages all global data of Kdict
+ DialgoListBox a list box which ignores Enter, usefull for dialogs
+ OptionsDialog the "Preferences" dialog
+
+ ------------------------------------------------------------- */
+
+#ifndef _KDICT_OPTIONS_H_
+#define _KDICT_OPTIONS_H_
+
+#include <qlistbox.h>
+#include <kdialogbase.h>
+#include <kglobalsettings.h>
+
+class QLineEdit;
+class QCheckBox;
+class QComboBox;
+class QRadioButton;
+
+class KColorButton;
+class KLineEdit;
+class KIntSpinBox;
+
+
+//********* GlobalData ******************************************
+
+#define COL_CNT 6
+#define FNT_CNT 2
+
+class GlobalData
+{
+
+public:
+
+ enum ColorIndex { Ctext=0, Cbackground=1, CheadingsText=2, CheadingsBackground=3, Clinks=4, CvisitedLinks=5 };
+ enum FontIndex { Ftext=0, Fheadings=1 };
+
+ void read();
+ void write();
+
+ // colors...
+ const QColor& color(int i) { return c_olors[i]; }
+ const QString& colorName(int i) { return c_olorNames[i]; }
+ int colorCount() const { return COL_CNT; }
+ QColor defaultColor(int i);
+ bool useCustomColors;
+ QColor textColor();
+ QColor backgroundColor();
+ QColor headingsTextColor();
+ QColor headingsBackgroundColor();
+ QColor linksColor();
+ QColor visitedLinksColor();
+
+ // fonts...
+ const QFont& font(int i) { return f_onts[i]; }
+ const QString& fontName(int i) { return f_ontNames[i]; }
+ int fontCount() const { return FNT_CNT; }
+ QFont defaultFont(int);
+ bool useCustomFonts;
+ QFont textFont();
+ QFont headingsFont();
+
+ QString encryptStr(const QString& aStr);
+
+ bool defineClipboard; // define clipboard content on startup?
+
+ QSize optSize,setsSize,matchSize; // window geometry
+ bool showMatchList;
+ QValueList<int> splitterSizes;
+
+ KGlobalSettings::Completion queryComboCompletionMode;
+
+ QStringList queryHistory;
+ bool saveHistory; // save query history to disk on exit?
+ unsigned int maxHistEntrys, maxBrowseListEntrys, maxDefinitions;
+ int headLayout;
+
+ QString server; // network client...
+ int port,timeout,pipeSize,idleHold;
+ QString encoding;
+ bool authEnabled;
+ QString user, secret;
+ QStringList serverDatabases, databases, strategies;
+ QPtrList<QStringList> databaseSets;
+ unsigned int currentDatabase, currentStrategy;
+
+ QColor c_olors[COL_CNT];
+ QString c_olorNames[COL_CNT];
+ QFont f_onts[FNT_CNT];
+ QString f_ontNames[FNT_CNT];
+
+ QWidget *topLevel;
+};
+
+extern GlobalData *global;
+
+
+//********* OptionsDialog ******************************************
+
+
+class OptionsDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ OptionsDialog(QWidget *parent=0, const char *name=0);
+ ~OptionsDialog();
+
+ //===================================================================================
+
+ class DialogListBox : public QListBox {
+
+ public:
+ // alwaysIgnore==false: enter is ignored when the widget isn't visible/out of focus
+ DialogListBox(bool alwaysIgnore=false, QWidget * parent=0, const char * name=0);
+ ~DialogListBox();
+
+ protected:
+ void keyPressEvent( QKeyEvent *e );
+
+ bool a_lwaysIgnore;
+ };
+
+ //===================================================================================
+
+ class ColorListItem : public QListBoxText {
+
+ public:
+ ColorListItem( const QString &text, const QColor &color=Qt::black );
+ ~ColorListItem();
+ const QColor& color() { return mColor; }
+ void setColor( const QColor &color ) { mColor = color; }
+
+ protected:
+ virtual void paint( QPainter * );
+ virtual int height( const QListBox * ) const;
+ virtual int width( const QListBox * ) const;
+
+ private:
+ QColor mColor;
+ };
+
+ //===================================================================================
+
+ class FontListItem : public QListBoxText {
+
+ public:
+ FontListItem( const QString &name, const QFont & );
+ ~FontListItem();
+ const QFont& font() { return f_ont; }
+ void setFont( const QFont &);
+ protected:
+ virtual void paint( QPainter * );
+ virtual int width( const QListBox * ) const;
+
+ private:
+ QFont f_ont;
+ QString fontInfo;
+ };
+
+ //===================================================================================
+
+signals:
+
+ void optionsChanged();
+
+protected slots:
+ void slotApply();
+ void slotOk();
+ void slotDefault();
+ void slotChanged();
+
+ //server
+ void slotAuthRequiredToggled( bool );
+
+ //colors
+ void slotColCheckBoxToggled(bool b);
+ void slotColItemSelected(QListBoxItem *); // show color dialog for the entry
+ void slotColDefaultBtnClicked();
+ void slotColChangeBtnClicked();
+ void slotColSelectionChanged();
+
+ //fonts
+ void slotFontCheckBoxToggled(bool b);
+ void slotFontItemSelected(QListBoxItem *); // show font dialog for the entry
+ void slotFontDefaultBtnClicked();
+ void slotFontChangeBtnClicked();
+ void slotFontSelectionChanged();
+
+private:
+
+ QFrame *serverTab;
+ QLabel *l_user, *l_secret;
+ KLineEdit *w_server, *w_user, *w_secret, *w_port;
+ QComboBox *w_encoding;
+ QCheckBox *w_auth;
+ KIntSpinBox *w_idleHold,*w_timeout,*w_pipesize;
+
+ QFrame *appTab;
+ DialogListBox *c_List,
+ *f_List;
+ QCheckBox *c_olorCB,
+ *f_ontCB;
+ QPushButton *c_olDefBtn,
+ *c_olChngBtn,
+ *f_ntDefBtn,
+ *f_ntChngBtn;
+
+ QFrame *layoutTab;
+ QRadioButton *w_layout[3];
+
+ QFrame *otherTab;
+ QCheckBox *w_Clipboard, *w_Savehist;
+ KIntSpinBox *w_Maxhist, *w_Maxbrowse, *w_MaxDefinitions;
+ bool configChanged;
+};
+
+#endif
diff --git a/kdict/pics/Makefile.am b/kdict/pics/Makefile.am
new file mode 100644
index 00000000..dfb183ec
--- /dev/null
+++ b/kdict/pics/Makefile.am
@@ -0,0 +1,2 @@
+kdicticondir = $(kde_datadir)/kdict/icons
+kdicticon_ICON = define_clip query_erase
diff --git a/kdict/pics/cr16-action-define_clip.png b/kdict/pics/cr16-action-define_clip.png
new file mode 100644
index 00000000..dd83280b
--- /dev/null
+++ b/kdict/pics/cr16-action-define_clip.png
Binary files differ
diff --git a/kdict/pics/cr16-action-query_erase.png b/kdict/pics/cr16-action-query_erase.png
new file mode 100644
index 00000000..0fb00f91
--- /dev/null
+++ b/kdict/pics/cr16-action-query_erase.png
Binary files differ
diff --git a/kdict/pics/cr22-action-define_clip.png b/kdict/pics/cr22-action-define_clip.png
new file mode 100644
index 00000000..3ab1b0cc
--- /dev/null
+++ b/kdict/pics/cr22-action-define_clip.png
Binary files differ
diff --git a/kdict/pics/cr32-action-define_clip.png b/kdict/pics/cr32-action-define_clip.png
new file mode 100644
index 00000000..f0c56ca4
--- /dev/null
+++ b/kdict/pics/cr32-action-define_clip.png
Binary files differ
diff --git a/kdict/queryview.cpp b/kdict/queryview.cpp
new file mode 100644
index 00000000..a2674d68
--- /dev/null
+++ b/kdict/queryview.cpp
@@ -0,0 +1,618 @@
+/* -------------------------------------------------------------
+
+ queryview.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ BrowseData data structure used for caching definitions
+ DictHTMLPart handling of middle mouse button clicks
+ QueryView widget that displays the definitions
+
+ ------------------------------------------------------------- */
+
+#include <qclipboard.h>
+#include <qtimer.h>
+
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kcursor.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <khtml_events.h>
+#include <khtmlview.h>
+
+#include "actions.h"
+#include "options.h"
+#include "dict.h"
+#include "queryview.h"
+#include <kapplication.h>
+#include <kiconloader.h>
+
+
+//******** SaveHelper *******************************************
+
+QString SaveHelper::lastPath;
+
+SaveHelper::SaveHelper(const QString &saveName, const QString &filter, QWidget *parent)
+ : p_arent(parent), s_aveName(saveName), f_ilter(filter), file(0), tmpFile(0)
+{
+}
+
+
+SaveHelper::~SaveHelper()
+{
+ if (file) { // local filesystem, just close the file
+ delete file;
+ } else
+ if (tmpFile) { // network location, initiate transaction
+ tmpFile->close();
+ if (KIO::NetAccess::upload(tmpFile->name(),url, p_arent) == false)
+ KMessageBox::error(global->topLevel, i18n("Unable to save remote file."));
+ tmpFile->unlink(); // delete temp file
+ delete tmpFile;
+ }
+}
+
+
+QFile* SaveHelper::getFile(const QString &dialogTitle)
+{
+ url = KFileDialog::getSaveURL(lastPath+s_aveName,f_ilter,p_arent,dialogTitle);
+
+ if (url.isEmpty())
+ return 0;
+
+ lastPath = url.url(-1);
+ lastPath.truncate(lastPath.length()-url.fileName().length());
+
+ if (url.isLocalFile()) {
+ if (QFileInfo(url.path()).exists() &&
+ (KMessageBox::warningContinueCancel(global->topLevel,
+ i18n("A file named %1 already exists.\nDo you want to replace it?").arg(url.path()),
+ dialogTitle, i18n("&Replace")) != KMessageBox::Continue)) {
+ return 0;
+ }
+
+ file = new QFile(url.path());
+ if(!file->open(IO_WriteOnly)) {
+ KMessageBox::error(global->topLevel, i18n("Unable to save file."));
+ delete file;
+ file = 0;
+ }
+ return file;
+ } else {
+ tmpFile = new KTempFile();
+ if (tmpFile->status()!=0)
+ KMessageBox::error(global->topLevel, i18n("Unable to create temporary file.")); {
+ delete tmpFile;
+ tmpFile = 0;
+ return 0;
+ }
+ return tmpFile->file();
+ }
+}
+
+
+//**** BrowseData ****************************************************
+
+
+BrowseData::BrowseData(const QString &Nhtml, const QString &NqueryText)
+: html(Nhtml),queryText(NqueryText),xPos(0),yPos(0)
+{}
+
+
+//********* DictHTMLPart ******************************************
+
+DictHTMLPart::DictHTMLPart(QWidget *parentWidget, const char *widgetname)
+ : KHTMLPart(parentWidget,widgetname)
+{}
+
+
+DictHTMLPart::~DictHTMLPart()
+{}
+
+
+void DictHTMLPart::khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *event)
+{
+ if (event->qmouseEvent()->button()==MidButton)
+ emit(middleButtonClicked());
+ else
+ KHTMLPart::khtmlMouseReleaseEvent(event);
+}
+
+
+//********* QueryView ******************************************
+
+
+QueryView::QueryView(QWidget *_parent)
+: QVBox( _parent), actBack(0L), actForward(0L), actQueryCombo(0L), browsePos(0), isRendering(false)
+{
+ browseList.setAutoDelete(true);
+
+ part=new DictHTMLPart(this);
+ part->setDNDEnabled(false);
+ part->setJScriptEnabled(false);
+ part->setJavaEnabled(false);
+ part->setURLCursor(KCursor::handCursor());
+ setFocusPolicy(QWidget::NoFocus);
+ connect(part, SIGNAL(completed()), SLOT(partCompleted()));
+ connect(part, SIGNAL(middleButtonClicked()), SLOT(middleButtonClicked()));
+ rightBtnMenu = new KPopupMenu(this);
+ connect(part,SIGNAL(popupMenu(const QString &, const QPoint &)),this,SLOT(buildPopupMenu(const QString &, const QPoint &)));
+ connect(part->browserExtension(),SIGNAL(openURLRequest(const KURL &,const KParts::URLArgs &)),
+ this,SLOT(slotURLRequest(const KURL &,const KParts::URLArgs &)));
+ connect(part->browserExtension(),SIGNAL(enableAction(const char *,bool)),this,SLOT(enableAction(const char *,bool)));
+ QTimer::singleShot( 0, this, SLOT(optionsChanged()) );
+ connect(interface,SIGNAL(resultReady(const QString &, const QString &)), SLOT(resultReady(const QString &, const QString &)));
+}
+
+
+QueryView::~QueryView()
+{}
+
+
+void QueryView::setActions(KToolBarPopupAction* NactBack, KToolBarPopupAction* NactForward, DictComboAction* NactQueryCombo)
+{
+ actBack = NactBack;
+ connect(actBack->popupMenu(),SIGNAL(activated(int)),SLOT(browseBack(int)));
+ actForward = NactForward;
+ connect(actForward->popupMenu(),SIGNAL(activated(int)),SLOT(browseForward(int)));
+ actQueryCombo = NactQueryCombo;
+}
+
+
+bool QueryView::browseBackPossible() const
+{
+ return (browsePos > 0)? true:false;
+}
+
+
+bool QueryView::browseForwardPossible() const
+{
+ return (browsePos+1 < browseList.count()) ? true:false;
+}
+
+
+void QueryView::optionsChanged()
+{
+ saveCurrentResultPos();
+
+ currentHTMLHeader = QString("<html><head><style type=\"text/css\">\n");
+ currentHTMLHeader += QString("body { background-color:%1; color:%2; }\n").arg(global->backgroundColor().name()).arg(global->textColor().name());
+ currentHTMLHeader += QString("a:link, a:active { color:%1; text-decoration:none; }\n").arg(global->linksColor().name());
+ currentHTMLHeader += QString("a:visited { color:%1; text-decoration:none; }\n").arg(global->visitedLinksColor().name());
+ currentHTMLHeader += QString("a:hover { color:%1; text-decoration:underline; }\n").arg(global->linksColor().name());
+ currentHTMLHeader += QString("p.heading { margin-left:0mm; margin-top:2mm; margin-bottom:2mm; padding:1mm; background-color:%1; color:%2; font-family:%3; font-size:%4pt; ").arg(global->headingsBackgroundColor().name()).arg(global->headingsTextColor().name()).arg(global->headingsFont().family()).arg(global->headingsFont().pointSize());
+ if (global->headingsFont().italic())
+ currentHTMLHeader += QString("font-style:italic; ");
+ if (global->headingsFont().bold())
+ currentHTMLHeader += QString("font-weight:bold; ");
+ currentHTMLHeader += QString("}\n");
+ currentHTMLHeader += QString("p.definition { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
+ currentHTMLHeader += QString("table { margin-left:1.5mm; margin-top:2mm; margin-bottom:2mm;}\n");
+ currentHTMLHeader += QString("pre { font-family:%1; font-size:%2pt; ").arg(global->textFont().family()).arg(global->textFont().pointSize());
+ if (global->textFont().italic())
+ currentHTMLHeader += QString("font-style:italic; ");
+ if (global->textFont().bold())
+ currentHTMLHeader += QString("font-weight:bold; ");
+ currentHTMLHeader += QString("}\n");
+ currentHTMLHeader += QString("</style></head>\n");
+
+ showResult(); // apply changes directly
+}
+
+
+void QueryView::stop()
+{
+ if (isRendering == true) {
+ part->closeURL();
+ isRendering = false;
+ emit(renderingStopped());
+ }
+}
+
+
+// print current result
+void QueryView::printQuery()
+{
+ part->view()->print();
+}
+
+
+// save the current result in an .html file
+void QueryView::saveQuery()
+{
+ if (!browseList.isEmpty()) {
+ BrowseData* brw = browseList.at(browsePos);
+ QString fName = brw->queryText+".html";
+ fName.replace(QRegExp("[\\s/]"),"_");
+ SaveHelper helper(fName,"*.html",global->topLevel);
+ QFile *file = helper.getFile(QString::null);
+
+ if (file) {
+ QTextStream stream(file);
+ stream.setEncoding(QTextStream::Locale);
+ stream << currentHTMLHeader+brw->html;
+ }
+ }
+}
+
+
+void QueryView::browseBack()
+{
+ if (browseBackPossible()) {
+ saveCurrentResultPos();
+ browsePos--;
+ actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
+ showResult();
+ updateBrowseActions();
+ }
+}
+
+
+void QueryView::browseForward()
+{
+ if (browseForwardPossible()) {
+ saveCurrentResultPos();
+ browsePos++;
+ actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
+ showResult();
+ updateBrowseActions();
+ }
+}
+
+
+void QueryView::selectAll()
+{
+ part->selectAll();
+}
+
+
+void QueryView::copySelection()
+{
+ kapp->clipboard()->setText(part->selectedText());
+}
+
+
+void QueryView::showFindDialog()
+{
+ KAction *act = part->actionCollection()->action("find");
+ if (act)
+ act->activate();
+}
+
+
+void QueryView::paletteChange ( const QPalette & )
+{
+
+ optionsChanged();
+}
+
+
+void QueryView::fontChange ( const QFont & )
+{
+ optionsChanged();
+}
+
+
+void QueryView::saveCurrentResultPos()
+{
+ if (!browseList.isEmpty()) {
+ browseList.at(browsePos)->xPos = part->view()->contentsX();
+ browseList.at(browsePos)->yPos = part->view()->contentsY();
+ }
+}
+
+
+void QueryView::showResult()
+{
+ if (!isRendering) {
+ isRendering = true;
+ emit(renderingStarted());
+ }
+
+ part->begin();
+ if (browseList.isEmpty()) {
+ part->write(currentHTMLHeader+"<body></body></html>");
+ part->end();
+ } else {
+ BrowseData* brw = browseList.at(browsePos);
+ emit(newCaption(getShortString(brw->queryText.simplifyWhiteSpace(),70)));
+ part->write(currentHTMLHeader+brw->html);
+ part->end();
+ part->view()->setFocus();
+ }
+}
+
+
+void QueryView::resultReady(const QString &result, const QString &query)
+{
+ BrowseData* brw = new BrowseData(result,query);
+
+ if (browseList.isEmpty()) {
+ browsePos = 0;
+ browseList.append(brw);
+ } else {
+ saveCurrentResultPos();
+ while (browseList.count()>browsePos+1)
+ browseList.removeLast();
+ browseList.append(brw);
+ browsePos++;
+ while (browseList.count()>global->maxBrowseListEntrys) {
+ browseList.removeFirst();
+ browsePos--;
+ }
+ }
+
+ showResult();
+ emit(enablePrintSave());
+ actQueryCombo->selectAll();
+ updateBrowseActions();
+}
+
+
+void QueryView::partCompleted()
+{
+ if (!browseList.isEmpty())
+ part->view()->setContentsPos(browseList.at(browsePos)->xPos,browseList.at(browsePos)->yPos);
+ if (isRendering) {
+ emit(renderingStopped());
+ isRendering = false;
+ }
+}
+
+
+void QueryView::slotURLRequest (const KURL &url, const KParts::URLArgs &)
+{
+ QString type = url.host();
+ QString urlText = url.prettyURL();
+ urlText.remove(0,8+type.length());
+
+ if (type.length()) { // valid url
+ if(type=="define")
+ emit(defineRequested(urlText));
+ if(type=="dbinfo")
+ interface->showDbInfo(urlText.utf8());
+ if(type=="realhttp")
+ kapp->invokeBrowser("http://"+urlText);
+ if(type=="realftp")
+ kapp->invokeBrowser("ftp://"+urlText);
+ }
+}
+
+
+void QueryView::middleButtonClicked()
+{
+ emit(clipboardRequested());
+}
+
+
+// construct the right-mouse-button-popup-menu on demand
+void QueryView::buildPopupMenu(const QString &url, const QPoint &point)
+{
+ rightBtnMenu->clear();
+
+ if (!url.isEmpty()) { // menuitem if mouse is over link
+ KURL u(url);
+ QString type = u.host();
+ popupLink = u.prettyURL();
+ popupLink.remove(0,8+type.length());
+
+ if (type.length()) { // valid url
+ if(type=="define") {
+ rightBtnMenu->insertItem(i18n("Define &Synonym"),
+ this,SLOT(popupDefineLink()));
+ rightBtnMenu->insertItem(i18n("M&atch Synonym"),
+ this,SLOT(popupMatchLink()));
+ rightBtnMenu->insertSeparator();
+ }
+ if(type=="dbinfo") {
+ rightBtnMenu->insertItem(i18n("D&atabase Information"),this,SLOT(popupDbInfo()));
+ rightBtnMenu->insertSeparator();
+ }
+ if(type=="realhttp") {
+ popupLink.prepend("http://");
+ rightBtnMenu->insertItem(SmallIcon("fileopen"),
+ i18n("&Open Link"),
+ this,SLOT(popupOpenLink()));
+ rightBtnMenu->insertSeparator();
+ }
+ if(type=="realftp") {
+ popupLink.prepend("ftp://");
+ rightBtnMenu->insertItem(SmallIcon("fileopen"),
+ i18n("&Open Link"),
+ this,SLOT(popupOpenLink()));
+ rightBtnMenu->insertSeparator();
+ }
+ }
+ }
+
+ if (part->hasSelection()) {
+ popupSelect = part->selectedText();
+ rightBtnMenu->insertItem(i18n("&Define Selection"),
+ this,SLOT(popupDefineSelect()));
+ rightBtnMenu->insertItem(i18n("&Match Selection"),
+ this,SLOT(popupMatchSelect()));
+ rightBtnMenu->insertSeparator();
+ } else {
+ kapp->clipboard()->setSelectionMode(false);
+ QString text = kapp->clipboard()->text();
+ if (text.isEmpty()) {
+ kapp->clipboard()->setSelectionMode(true);
+ text = kapp->clipboard()->text();
+ }
+ if (!text.isEmpty()) {
+ popupSelect = QApplication::clipboard()->text();
+ rightBtnMenu->insertItem(SmallIcon("define_clip"),
+ i18n("&Define Clipboard Content"),
+ this,SLOT(popupDefineSelect()));
+ rightBtnMenu->insertItem(i18n("&Match Clipboard Content"),
+ this,SLOT(popupMatchSelect()));
+ rightBtnMenu->insertSeparator();
+ }
+ }
+
+ int ID;
+
+ if (browseBackPossible()) { // if possible, show string
+ if (browseList.at(browsePos-1)->queryText.isEmpty())
+ rightBtnMenu->insertItem(SmallIcon("back"),
+ i18n("&Back: Information"),
+ this,SLOT(browseBack()));
+ else
+ rightBtnMenu->insertItem(SmallIcon("back"),
+ i18n("&Back: '%1'").arg(getShortString(browseList.at(browsePos-1)->queryText,25)),
+ this,SLOT(browseBack()));
+ } else {
+ ID = rightBtnMenu->insertItem(SmallIcon("back"), i18n("&Back"), this, SLOT(browseBack()));
+ rightBtnMenu->setItemEnabled(ID,false);
+ }
+
+ if (browseForwardPossible()) { // if possible, show string
+ if (browseList.at(browsePos+1)->queryText.isEmpty())
+ rightBtnMenu->insertItem(SmallIcon("forward"),
+ i18n("&Forward: Information"),
+ this,SLOT(browseForward()));
+ else
+ rightBtnMenu->insertItem(SmallIcon("forward"),
+ i18n("&Forward: '%1'").arg(getShortString(browseList.at(browsePos+1)->queryText,25)),
+ this,SLOT(browseForward()));
+ } else {
+ ID = rightBtnMenu->insertItem(SmallIcon("forward"),i18n("&Forward"),this,SLOT(browseForward()));
+ rightBtnMenu->setItemEnabled(ID,false);
+ }
+
+ rightBtnMenu->popup(point);
+}
+
+
+void QueryView::popupDefineLink()
+{
+ emit(defineRequested(popupLink));
+}
+
+
+void QueryView::popupMatchLink()
+{
+ emit(matchRequested(popupLink));
+}
+
+
+void QueryView::popupOpenLink()
+{
+ kapp->invokeBrowser(popupLink);
+}
+
+
+void QueryView::popupDefineSelect()
+{
+ emit(defineRequested(popupSelect));
+}
+
+
+void QueryView::popupMatchSelect()
+{
+ emit(matchRequested(popupSelect));
+}
+
+
+void QueryView::popupDbInfo()
+{
+
+ interface->showDbInfo(popupLink.utf8());
+}
+
+
+void QueryView::enableAction(const char * name, bool enabled)
+{
+ if (!strcmp(name,"copy"))
+ emit(enableCopy(enabled));
+}
+
+
+void QueryView::browseBack(int index)
+{
+ int x = browsePos-index;
+ if (x>=0) {
+ saveCurrentResultPos();
+ browsePos = x;
+ actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
+ showResult();
+ QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
+ }
+}
+
+
+void QueryView::browseForward(int index)
+{
+ int x = browsePos+index;
+ if (x < (int)(browseList.count())) {
+ saveCurrentResultPos();
+ browsePos = x;
+ actQueryCombo->setEditText(browseList.at(browsePos)->queryText);
+ showResult();
+ QTimer::singleShot(0, this, SLOT(updateBrowseActions())); // don't clear the menu in this slot
+ }
+}
+
+
+void QueryView::updateBrowseActions()
+{
+ if (browseBackPossible()) {
+ actBack->setEnabled(true);
+ if (browseList.at(browsePos-1)->queryText.isEmpty())
+ actBack->setText(i18n("&Back: Information"));
+ else
+ actBack->setText(i18n("&Back: '%1'").arg(getShortString(browseList.at(browsePos-1)->queryText,25)));
+
+ actBack->popupMenu()->clear();
+ int i = browsePos-1;
+ int num = 1;
+ QString s;
+ while ((i>=0)&&(num<=10)) {
+ s = browseList.at(i)->queryText;
+ if (s.isEmpty()) s = i18n("Information");
+ actBack->popupMenu()->insertItem(s,num);
+ num++;
+ i--;
+ }
+ } else {
+ actBack->setEnabled(false);
+ actBack->setText(i18n("&Back"));
+ actBack->popupMenu()->clear();
+ }
+
+ if (browseForwardPossible()) {
+ actForward->setEnabled(true);
+ if (browseList.at(browsePos+1)->queryText.isEmpty())
+ actForward->setText(i18n("&Forward: Information"));
+ else
+ actForward->setText(i18n("&Forward: '%1'").arg(getShortString(browseList.at(browsePos+1)->queryText,25)));
+
+ actForward->popupMenu()->clear();
+ int i = browsePos+1;
+ int num = 1;
+ QString s;
+ while ((i<(int)(browseList.count()))&&(num<=10)) {
+ s = browseList.at(i)->queryText;
+ if (s.isEmpty()) s = i18n("Information");
+ actForward->popupMenu()->insertItem(s,num);
+ num++;
+ i++;
+ }
+ } else {
+ actForward->setEnabled(false);
+ actForward->setText(i18n("&Forward"));
+ actForward->popupMenu()->clear();
+ }
+}
+
+//--------------------------------
+
+#include "queryview.moc"
diff --git a/kdict/queryview.h b/kdict/queryview.h
new file mode 100644
index 00000000..e942d297
--- /dev/null
+++ b/kdict/queryview.h
@@ -0,0 +1,178 @@
+/* -------------------------------------------------------------
+
+ queryview.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ SaveHelper network transparent file saving
+ BrowseData data structure used for caching definitions
+ DictHTMLPart handling of middle mouse button clicks
+ QueryView widget that displays the definitions
+
+ ------------------------------------------------------------- */
+
+#ifndef _QUERYVIEW_H_
+#define _QUERYVIEW_H_
+
+#include <qvbox.h>
+#include <khtml_part.h>
+
+class QFile;
+class KTempFile;
+class KPopupMenu;
+class DictComboAction;
+
+
+QString getShortString(QString str,unsigned int length);
+
+
+//******** SaveHelper *******************************************
+
+
+class SaveHelper {
+
+public:
+
+ SaveHelper(const QString &saveName, const QString &filter, QWidget *parent);
+ ~SaveHelper();
+
+ // returns a file open for writing
+ QFile* getFile(const QString &dialogTitle);
+
+private:
+
+ QWidget *p_arent;
+ QString s_aveName, f_ilter;
+ KURL url;
+ QFile* file;
+ KTempFile* tmpFile;
+ static QString lastPath;
+
+};
+
+
+//******** BrowseData ******************************************
+
+
+class BrowseData
+{
+
+public:
+
+ BrowseData(const QString &Nhtml, const QString &NqueryText);
+
+ QString html;
+ QString queryText;
+ int xPos,yPos;
+};
+
+
+//********* DictHTMLPart ***************************************
+
+class DictHTMLPart : public KHTMLPart
+{
+ Q_OBJECT
+
+public:
+
+ DictHTMLPart(QWidget *parentWidget = 0, const char *widgetname = 0);
+ ~DictHTMLPart();
+
+signals:
+ void middleButtonClicked();
+
+protected:
+
+ virtual void khtmlMouseReleaseEvent(khtml::MouseReleaseEvent *event);
+
+};
+
+//********* QueryView ******************************************
+
+
+class QueryView : public QVBox
+{
+ Q_OBJECT
+
+public:
+
+ QueryView(QWidget *_parent = 0L);
+ ~QueryView();
+
+ void setActions(KToolBarPopupAction* NactBack, KToolBarPopupAction* NactForward, DictComboAction* NactQueryCombo);
+
+ bool browseBackPossible() const;
+ bool browseForwardPossible() const;
+
+ void stop();
+
+public slots:
+ void optionsChanged();
+ void printQuery();
+ void saveQuery();
+ void browseBack();
+ void browseForward();
+ void selectAll();
+ void copySelection();
+ void showFindDialog();
+
+signals:
+
+ void defineRequested(const QString &query);
+ void matchRequested(const QString &query);
+ void clipboardRequested();
+ void enableCopy(bool selected); // emited when the user selects/deselects text
+ void enablePrintSave();
+ void renderingStarted();
+ void renderingStopped();
+ void newCaption(const QString&);
+
+protected:
+
+ void paletteChange ( const QPalette & );
+ void fontChange ( const QFont & );
+
+ void saveCurrentResultPos();
+ void showResult();
+
+protected slots:
+
+ void resultReady(const QString &result, const QString &query);
+ void partCompleted();
+ void slotURLRequest (const KURL &url, const KParts::URLArgs &args);
+ void middleButtonClicked();
+ void buildPopupMenu(const QString &url, const QPoint &point);
+ void popupDefineLink();
+ void popupMatchLink();
+ void popupOpenLink();
+ void popupDefineSelect();
+ void popupMatchSelect();
+ void popupDbInfo();
+ void enableAction(const char *, bool);
+ void browseBack(int);
+ void browseForward(int);
+ void updateBrowseActions();
+
+private:
+
+ DictHTMLPart *part; // Widgets
+
+ KToolBarPopupAction *actBack, *actForward;
+ DictComboAction *actQueryCombo;
+
+ KPopupMenu *rightBtnMenu;
+ QString popupLink,popupSelect; // needed for rightbtn-popup menu
+
+ QPtrList<BrowseData> browseList;
+ unsigned int browsePos; // position in browseList
+ QString currentHTMLHeader;
+
+ bool isRendering;
+};
+
+#endif
diff --git a/kdict/sets.cpp b/kdict/sets.cpp
new file mode 100644
index 00000000..0a252cec
--- /dev/null
+++ b/kdict/sets.cpp
@@ -0,0 +1,326 @@
+/* -------------------------------------------------------------
+
+ sets.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ DbSetsDialog dialog for editing the user defined database sets
+
+ ------------------------------------------------------------- */
+
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcombobox.h>
+
+#include <kiconloader.h>
+#include <kseparator.h>
+#include <klocale.h>
+
+#include "options.h"
+#include "sets.h"
+
+
+//********* DbSetsDialog ******************************************
+
+
+DbSetsDialog::DbSetsDialog(QWidget *parent, const char *name)
+ : KDialogBase(Plain, i18n("Database Sets"),Close | Help, Close, parent, name, false, true)
+{
+ QFrame* page=plainPage();
+
+ QStringList sets;
+ for(unsigned int i=1;i<global->databaseSets.count()+1;i++)
+ sets.append(global->databases[i]);
+
+ QVBoxLayout * topLayout = new QVBoxLayout(page, 0, 0);
+
+ QHBoxLayout * subLayout1 = new QHBoxLayout(5);
+ topLayout->addLayout(subLayout1,0);
+
+ w_set = new QComboBox(true,page);
+ w_set->setFixedHeight(w_set->sizeHint().height());
+ w_set->setInsertionPolicy (QComboBox::NoInsertion);
+ w_set->insertStringList(sets);
+ connect(w_set, SIGNAL(activated(int)),this, SLOT(activateSet(int)));
+ QLabel *l = new QLabel(w_set, i18n("&Set:"),page);
+ l->setMinimumSize(l->sizeHint());
+ subLayout1->addWidget(l,0);
+ subLayout1->addWidget(w_set,1);
+
+ subLayout1->addSpacing(8);
+
+ w_save = new QPushButton(i18n("S&ave"),page);
+ connect(w_save,SIGNAL(clicked()),this, SLOT(transferSet()));
+ subLayout1->addWidget(w_save,0);
+
+ QPushButton *btn = new QPushButton(i18n("&New"),page);
+ btn->setMinimumSize(btn->sizeHint());
+ connect(btn, SIGNAL(clicked()),this, SLOT(newPressed()));
+ subLayout1->addWidget(btn,0);
+
+ w_delete = new QPushButton(i18n("&Delete"),page);
+ w_delete->setMinimumSize(w_delete->sizeHint());
+ connect(w_delete, SIGNAL(clicked()),this, SLOT(deletePressed()));
+ subLayout1->addWidget(w_delete,0);
+
+ topLayout->addSpacing(8);
+
+ KSeparator *sep = new KSeparator(page);
+ topLayout->addWidget(sep,0);
+
+ topLayout->addSpacing(8);
+
+ QGridLayout * subLayout2 = new QGridLayout(7,3,6);
+ topLayout->addLayout(subLayout2,1);
+
+ w_leftBox = new QListBox(page);
+ connect(w_leftBox, SIGNAL(selected(int)),this, SLOT(leftSelected(int)));
+ connect(w_leftBox, SIGNAL(highlighted(int)),this, SLOT(leftHighlighted(int)));
+ QLabel *leftLabel = new QLabel(w_leftBox, i18n("S&elected databases:"),page);
+ leftLabel->setMinimumSize(leftLabel->sizeHint());
+ subLayout2->addWidget(leftLabel,0,0);
+ subLayout2->addMultiCellWidget(w_leftBox,1,6,0,0);
+
+ w_allLeft = new QPushButton(page);
+ w_allLeft->setIconSet(BarIconSet("2leftarrow"));
+ connect(w_allLeft, SIGNAL(clicked()),this, SLOT(allLeftPressed()));
+ subLayout2->addWidget(w_allLeft,2,1);
+
+ w_left = new QPushButton(page);
+ w_left->setIconSet(BarIconSet("1leftarrow"));
+ connect(w_left, SIGNAL(clicked()),this, SLOT(leftPressed()));
+ subLayout2->addWidget(w_left,3,1);
+
+ w_right = new QPushButton(page);
+ w_right->setIconSet(BarIconSet("1rightarrow"));
+ connect(w_right, SIGNAL(clicked()),this, SLOT(rightPressed()));
+ subLayout2->addWidget(w_right,4,1);
+
+ w_allRight = new QPushButton(page);
+ w_allRight->setIconSet(BarIconSet("2rightarrow"));
+ connect(w_allRight, SIGNAL(clicked()),this, SLOT(allRightPressed()));
+ subLayout2->addWidget(w_allRight,5,1);
+
+ w_rightBox = new QListBox(page);
+ connect(w_rightBox, SIGNAL(selected(int)),this, SLOT(rightSelected(int)));
+ connect(w_rightBox, SIGNAL(highlighted(int)),this, SLOT(rightHighlighted(int)));
+ QLabel *rightLabel = new QLabel(w_rightBox, i18n("A&vailable databases:"),page);
+ rightLabel->setMinimumSize(rightLabel->sizeHint());
+ subLayout2->addWidget(rightLabel,0,2);
+ subLayout2->addMultiCellWidget(w_rightBox,1,6,2,2);
+
+ subLayout2->setRowStretch(1,1);
+ subLayout2->setRowStretch(6,1);
+ subLayout2->setColStretch(0,1);
+ subLayout2->setColStretch(2,1);
+
+ setHelp("database-sets");
+
+ if (global->setsSize.isValid())
+ resize(global->setsSize);
+ else
+ resize(300,200);
+
+ if ((global->currentDatabase>=1)&&(global->currentDatabase<=global->databaseSets.count()))
+ activateSet(global->currentDatabase-1);
+ else
+ activateSet(0);
+ w_set->setFocus();
+}
+
+
+void DbSetsDialog::hideEvent(QHideEvent *)
+{
+ global->setsSize = size();
+ emit(dialogClosed());
+}
+
+
+void DbSetsDialog::newPressed()
+{
+ QStringList *temp = new QStringList;
+ temp->append(i18n("New Set"));
+ global->databaseSets.append(temp);
+ global->databases.insert(global->databases.at(global->databaseSets.count()),i18n("New Set"));
+ if (global->currentDatabase >= global->databaseSets.count())
+ global->currentDatabase++;
+
+ QStringList sets; // reread sets, because w_sets internal list is not correct in all cases
+ for(unsigned int i=1;i<global->databaseSets.count()+1;i++)
+ sets.append(global->databases[i]);
+ w_set->clear();
+ w_set->insertStringList(sets);
+ emit(setsChanged());
+ activateSet(global->databaseSets.count()-1);
+ w_set->setFocus();
+}
+
+
+void DbSetsDialog::deletePressed()
+{
+ int pos = w_set->currentItem();
+ if (pos>=0) {
+ global->databaseSets.remove(global->databaseSets.at(pos));
+ global->databases.remove(global->databases.at(pos+1));
+ if ((int)global->currentDatabase >= pos+1)
+ global->currentDatabase--;
+ w_set->removeItem(pos);
+ if (pos >= w_set->count())
+ pos--;
+ emit(setsChanged());
+ activateSet(pos);
+ w_set->setFocus();
+ }
+}
+
+
+void DbSetsDialog::allLeftPressed()
+{
+ while (w_rightBox->count()) {
+ w_leftBox->insertItem(w_rightBox->text(0));
+ w_rightBox->removeItem(0);
+ }
+ w_leftBox->sort();
+ checkButtons();
+}
+
+
+void DbSetsDialog::leftPressed()
+{
+ int pos = w_rightBox->currentItem();
+ if (pos>=0) {
+ w_leftBox->insertItem(w_rightBox->text(pos));
+ w_leftBox->sort();
+ w_rightBox->removeItem(pos);
+ if (pos >= (int)w_rightBox->count())
+ pos--;
+ if (pos>=0)
+ w_rightBox->setCurrentItem(pos);
+ checkButtons();
+ }
+}
+
+
+void DbSetsDialog::rightPressed()
+{
+ int pos = w_leftBox->currentItem();
+ if (pos>=0) {
+ w_rightBox->insertItem(w_leftBox->text(pos));
+ w_rightBox->sort();
+ w_leftBox->removeItem(pos);
+ if (pos >= (int)w_leftBox->count())
+ pos--;
+ if (pos>=0)
+ w_leftBox->setCurrentItem(pos);
+ checkButtons();
+ }
+}
+
+
+void DbSetsDialog::allRightPressed()
+{
+ while (w_leftBox->count()) {
+ w_rightBox->insertItem(w_leftBox->text(0));
+ w_leftBox->removeItem(0);
+ }
+ w_rightBox->sort();
+ checkButtons();
+}
+
+
+void DbSetsDialog::closePressed()
+{
+ accept();
+ global->setsSize = size();
+ emit(dialogClosed());
+}
+
+
+void DbSetsDialog::transferSet()
+{
+ global->databaseSets.at(w_set->currentItem())->clear();
+ global->databaseSets.at(w_set->currentItem())->append(w_set->currentText());
+ for (unsigned int i = 0;i<w_leftBox->count();i++)
+ global->databaseSets.at(w_set->currentItem())->append(w_leftBox->text(i));
+ global->databases.remove(global->databases.at(w_set->currentItem()+1));
+ global->databases.insert(global->databases.at(w_set->currentItem()+1),w_set->currentText());
+ w_set->changeItem(w_set->currentText(),w_set->currentItem());
+ emit(setsChanged());
+}
+
+
+void DbSetsDialog::activateSet(int num)
+{
+ w_leftBox->clear();
+ w_rightBox->clear();
+
+ if ((num < 0)||(num>=(int)global->databaseSets.count())) {
+ w_set->clearEdit();
+ w_delete->setEnabled(false);
+ w_save->setEnabled(false);
+ w_rightBox->repaint(true); // Workaround for repaint-bug
+ w_leftBox->repaint(true); // Workaround for repaint-bug
+ } else {
+ w_set->setCurrentItem(num);
+ for (unsigned int i=0;i<global->serverDatabases.count();i++)
+ if (global->databaseSets.at(num)->findIndex(global->serverDatabases[i])>0)
+ w_leftBox->insertItem(global->serverDatabases[i]);
+ else
+ w_rightBox->insertItem(global->serverDatabases[i]);
+ w_leftBox->sort();
+ w_rightBox->sort();
+ w_delete->setEnabled(true);
+ w_save->setEnabled(true);
+ if (w_rightBox->count()==0)
+ w_rightBox->repaint(true); // Workaround for repaint-bug
+ if (w_leftBox->count()==0)
+ w_leftBox->repaint(true); // Workaround for repaint-bug
+ w_leftBox->clearSelection();
+ w_leftBox->centerCurrentItem();
+ w_rightBox->clearSelection();
+ w_rightBox->centerCurrentItem();
+ }
+ checkButtons();
+}
+
+
+void DbSetsDialog::leftSelected(int)
+{
+ rightPressed();
+}
+
+
+void DbSetsDialog::rightSelected(int)
+{
+ leftPressed();
+}
+
+
+void DbSetsDialog::leftHighlighted(int)
+{
+ w_right->setEnabled(true);
+}
+
+
+void DbSetsDialog::rightHighlighted(int)
+{
+ w_left->setEnabled(true);
+}
+
+void DbSetsDialog::checkButtons()
+{
+ w_allLeft->setEnabled((w_rightBox->count()>0));
+ w_allRight->setEnabled((w_leftBox->count()>0));
+ w_right->setEnabled((w_leftBox->currentItem()>=0));
+ w_left->setEnabled((w_rightBox->currentItem()>=0));
+}
+
+//--------------------------------
+
+#include "sets.moc"
diff --git a/kdict/sets.h b/kdict/sets.h
new file mode 100644
index 00000000..3e874a6e
--- /dev/null
+++ b/kdict/sets.h
@@ -0,0 +1,68 @@
+/* -------------------------------------------------------------
+
+ sets.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ dbSetsDialog dialog for editing the user defined database sets
+
+ ------------------------------------------------------------- */
+
+#ifndef _KDICT_SETS_H_
+#define _KDICT_SETS_H_
+
+#include <kdialogbase.h>
+
+class QListBox;
+
+
+//********* DbSetsDialog ******************************************
+
+
+class DbSetsDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ DbSetsDialog(QWidget *parent=0, const char *name=0);
+
+signals:
+
+ void setsChanged();
+ void dialogClosed();
+
+protected:
+ void hideEvent(QHideEvent *);
+
+private slots:
+
+ void newPressed();
+ void deletePressed();
+ void allLeftPressed();
+ void leftPressed();
+ void rightPressed();
+ void allRightPressed();
+ void closePressed();
+ void transferSet();
+ void activateSet(int num);
+ void leftSelected(int index);
+ void rightSelected(int index);
+ void leftHighlighted(int index);
+ void rightHighlighted(int index);
+
+private:
+
+ void checkButtons();
+
+ QComboBox *w_set;
+ QListBox *w_leftBox, *w_rightBox;
+ QPushButton *w_delete,*w_save,*w_allLeft,*w_left,*w_right,*w_allRight;
+};
+
+#endif
diff --git a/kdict/toplevel.cpp b/kdict/toplevel.cpp
new file mode 100644
index 00000000..ee334cbd
--- /dev/null
+++ b/kdict/toplevel.cpp
@@ -0,0 +1,778 @@
+/* -------------------------------------------------------------
+
+ toplevel.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias Hölzer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ TopLevel The toplevel widget of Kdict.
+
+ ------------------------------------------------------------- */
+
+#include <qclipboard.h>
+
+#include <kstdaccel.h>
+#include <kstdaction.h>
+#include <kapplication.h>
+#include <kstatusbar.h>
+#include <klocale.h>
+#include <kwin.h>
+#include <kedittoolbar.h>
+#include <kdebug.h>
+#include <dcopclient.h>
+
+#include "actions.h"
+#include "dict.h"
+#include "options.h"
+#include "queryview.h"
+#include "matchview.h"
+#include "sets.h"
+#include "toplevel.h"
+
+
+// cut a QString and add "..."
+QString getShortString(QString str,unsigned int length)
+{
+ if (str.length()>length) {
+ str.truncate(length-3);
+ str.append("...");
+ }
+ return str;
+}
+
+
+DictInterface *interface;
+GlobalData *global;
+
+
+TopLevel::TopLevel(QWidget* parent, const char* name)
+ : DCOPObject("KDictIface"), KMainWindow(parent, name, WType_TopLevel),
+ optDlg(0L), setsDlg(0L), stopRef(0)
+{
+ kapp->dcopClient()->setDefaultObject(objId());
+ kapp->setMainWidget(this);
+
+ global = new GlobalData();
+ global->topLevel = this;
+ global->read();
+ interface = new DictInterface();
+ connect(interface,SIGNAL(infoReady()),SLOT(stratDbChanged()));
+ connect(interface,SIGNAL(started(const QString&)),SLOT(clientStarted(const QString&)));
+ connect(interface,SIGNAL(stopped(const QString&)),SLOT(clientStopped(const QString&)));
+
+ queryView = new QueryView(this);
+ connect(queryView,SIGNAL(defineRequested(const QString&)),SLOT(define(const QString&)));
+ connect(queryView,SIGNAL(matchRequested(const QString&)),SLOT(match(const QString&)));
+ connect(queryView,SIGNAL(clipboardRequested()),SLOT(defineClipboard()));
+ connect(queryView,SIGNAL(enableCopy(bool)),SLOT(enableCopy(bool)));
+ connect(queryView,SIGNAL(enablePrintSave()),SLOT(enablePrintSave()));
+ connect(queryView,SIGNAL(renderingStarted()),SLOT(renderingStarted()));
+ connect(queryView,SIGNAL(renderingStopped()),SLOT(renderingStopped()));
+ connect(queryView,SIGNAL(newCaption(const QString&)),SLOT(newCaption(const QString&)));
+
+ matchView = new MatchView();
+ connect(matchView,SIGNAL(defineRequested(const QString&)),SLOT(define(const QString&)));
+ connect(matchView,SIGNAL(matchRequested(const QString&)),SLOT(match(const QString&)));
+ connect(matchView,SIGNAL(clipboardRequested()),SLOT(matchClipboard()));
+ connect(matchView,SIGNAL(windowClosed()),SLOT(toggleMatchListShow()));
+ connect(&resetStatusbarTimer,SIGNAL(timeout()),SLOT(resetStatusbar()));
+
+ setupStatusBar();
+ setupActions();
+ recreateGUI();
+ buildHistMenu();
+
+ if (global->showMatchList)
+ { // show splitter, html view & match list
+ splitter = new QSplitter(QSplitter::Horizontal,this);
+ splitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
+ queryView->reparent(splitter,0,queryView->pos(),true);
+ matchView->reparent(splitter,0,matchView->pos(),true);
+ setCentralWidget(splitter);
+ splitter->setResizeMode(matchView,QSplitter::KeepSize);
+ adjustMatchViewSize();
+ }
+ else
+ { // show only html view
+ setCentralWidget(queryView);
+ matchView->hide();
+ }
+
+ //apply settings
+ resize(600,390);
+ applyMainWindowSettings(KGlobal::config(),"toplevel_options");
+ stratDbChanged(); // fill combos, build menus
+
+ actQueryCombo->setFocus(); // place cursor in combobox
+}
+
+
+TopLevel::~TopLevel()
+{
+}
+
+
+void TopLevel::normalStartup()
+{
+ if (global->defineClipboard)
+ defineClipboard();
+}
+
+// ******* DCOP Interface ********************************************************
+
+void TopLevel::quit()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::quit()" << endl;
+ kapp->closeAllWindows();
+}
+
+
+void TopLevel::makeActiveWindow()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::makeActiveWindow()" << endl;
+ raiseWindow();
+}
+
+
+void TopLevel::definePhrase(QString phrase)
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::definePhrase()" << endl;
+ define(phrase);
+ raiseWindow();
+}
+
+
+void TopLevel::matchPhrase(QString phrase)
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::matchPhrase()" << endl;
+ match(phrase);
+ raiseWindow();
+}
+
+
+void TopLevel::defineClipboardContent()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::defineClipboardContent()" << endl;
+ defineClipboard();
+ raiseWindow();
+}
+
+
+void TopLevel::matchClipboardContent()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::matchClipboardContent()" << endl;
+ matchClipboard();
+ raiseWindow();
+}
+
+
+QStringList TopLevel::getDatabases()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::getDatabases()" << endl;
+ return global->databases;
+}
+
+
+QString TopLevel::currentDatabase()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::currentDatabase()" << endl;
+ return global->databases[global->currentDatabase];
+}
+
+
+QStringList TopLevel::getStrategies()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::getStrategies()" << endl;
+ return global->strategies;
+}
+
+
+QString TopLevel::currentStrategy()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::currentStrategy()" << endl;
+ return global->strategies[global->currentStrategy];
+}
+
+
+bool TopLevel::setDatabase(QString db)
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::setDatabase()" << endl;
+
+ int newCurrent = global->databases.findIndex(db);
+ if (newCurrent == -1)
+ return false;
+ else {
+ global->currentDatabase = newCurrent;
+ actDbCombo->setCurrentItem(global->currentDatabase);
+ return true;
+ }
+}
+
+
+bool TopLevel::setStrategy(QString strategy)
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::setStrategy()" << endl;
+
+ return matchView->selectStrategy(strategy);
+}
+
+
+bool TopLevel::historyGoBack()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::historyGoBack()" << endl;
+
+ if (!queryView->browseBackPossible())
+ return false;
+ else {
+ queryView->browseBack();
+ return true;
+ }
+}
+
+
+bool TopLevel::historyGoForward()
+{
+ kdDebug(5004) << "*DCOP call* TopLevel::historyGoForward()" << endl;
+
+ if (!queryView->browseForwardPossible())
+ return false;
+ else {
+ queryView->browseForward();
+ return true;
+ }
+}
+
+// *******************************************************************************
+
+void TopLevel::define(const QString &query)
+{
+ kdDebug(5004) << "TopLevel::define()" << endl;
+ actQueryCombo->setEditText(query);
+ doDefine();
+}
+
+
+void TopLevel::defineClipboard()
+{
+ kdDebug(5004) << "TopLevel::defineClipboard()" << endl;
+ kapp->clipboard()->setSelectionMode(true);
+ QString text = kapp->clipboard()->text();
+ if (text.isEmpty()) {
+ kapp->clipboard()->setSelectionMode(false);
+ text = kapp->clipboard()->text();
+ }
+ define(text);
+}
+
+
+void TopLevel::match(const QString &query)
+{
+ kdDebug(5004) << "TopLevel::match()" << endl;
+ actQueryCombo->setEditText(query);
+ doMatch();
+}
+
+
+void TopLevel::matchClipboard()
+{
+ kdDebug(5004) << "TopLevel::matchClipboard()" << endl;
+ kapp->clipboard()->setSelectionMode(true);
+ QString text = kapp->clipboard()->text();
+ if (text.isEmpty()) {
+ kapp->clipboard()->setSelectionMode(false);
+ text = kapp->clipboard()->text();
+ }
+ match(text);
+}
+
+
+bool TopLevel::queryClose()
+{
+ kdDebug(5004) << "TopLevel::queryClose()" << endl;
+
+ saveMainWindowSettings(KGlobal::config(),"toplevel_options");
+ saveMatchViewSize();
+ global->queryComboCompletionMode = actQueryCombo->completionMode();
+
+ global->write();
+
+ return true;
+}
+
+
+void TopLevel::setupActions()
+{
+ // file menu...
+ actSave = KStdAction::save(queryView, SLOT(saveQuery()), actionCollection());
+ actSave->setText(i18n("&Save As..."));
+ actSave->setEnabled(false);
+ actPrint = KStdAction::print(queryView, SLOT(printQuery()), actionCollection());
+ actPrint->setEnabled(false);
+ actStartQuery = new KAction(i18n("St&art Query"),"reload", 0 , this,
+ SLOT(doDefine()), actionCollection(), "start_query");
+ actStopQuery = new KAction(i18n("St&op Query"),"stop", 0 , this,
+ SLOT(stopClients()), actionCollection(), "stop_query");
+ actStopQuery->setEnabled(false);
+ KStdAction::quit(kapp, SLOT(closeAllWindows()), actionCollection());
+
+ // edit menu...
+ actCopy = KStdAction::copy(queryView, SLOT(copySelection()), actionCollection());
+ actCopy->setEnabled(false);
+ KStdAction::selectAll(queryView, SLOT(selectAll()), actionCollection());
+ new KAction(i18n("&Define Clipboard Content"), "define_clip", 0 , this,
+ SLOT(defineClipboard()), actionCollection(), "define_clipboard");
+ new KAction(i18n("&Match Clipboard Content"), 0 , this,
+ SLOT(matchClipboard()), actionCollection(), "match_clipboard");
+ KStdAction::find(queryView, SLOT(showFindDialog()), actionCollection());
+
+ // history menu...
+ actBack = new KToolBarPopupAction(i18n("&Back"), "back", KStdAccel::shortcut(KStdAccel::Back),
+ queryView, SLOT(browseBack()), actionCollection(),"browse_back");
+ actBack->setDelayed(true);
+ actBack->setStickyMenu(false);
+ actBack->setEnabled(false);
+ actForward = new KToolBarPopupAction(i18n("&Forward"), "forward", KStdAccel::shortcut(KStdAccel::Forward),
+ queryView, SLOT(browseForward()), actionCollection(),"browse_forward");
+ actForward->setDelayed(true);
+ actForward->setStickyMenu(false);
+ actForward->setEnabled(false);
+ new KAction(i18n("&Clear History"), 0 , this,
+ SLOT(clearQueryHistory()), actionCollection(), "clear_history");
+
+ // server menu...
+ new KAction(i18n("&Get Capabilities"), 0 , interface,
+ SLOT(updateServer()), actionCollection(), "get_capabilities");
+ new KAction(i18n("Edit &Database Sets..."), "edit", 0 , this,
+ SLOT(showSetsDialog()), actionCollection(), "edit_sets");
+ new KAction(i18n("&Summary"), 0 , interface,
+ SLOT(showDatabases()), actionCollection(), "db_summary");
+ new KAction(i18n("S&trategy Information"), 0 , interface,
+ SLOT(showStrategies()), actionCollection(), "strategy_info");
+ new KAction(i18n("&Server Information"), 0 , interface,
+ SLOT(showInfo()), actionCollection(), "server_info");
+
+ // settings menu...
+ createStandardStatusBarAction();
+ setStandardToolBarMenuEnabled(true);
+
+ actShowMatchList = new KToggleAction(i18n("Show &Match List"), 0 , this,
+ SLOT(toggleMatchListShow()), actionCollection(), "show_match");
+ actShowMatchList->setCheckedState(i18n("Hide &Match List"));
+ actShowMatchList->setChecked(global->showMatchList);
+ KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()),
+actionCollection());
+ KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), actionCollection());
+ KStdAction::preferences(this, SLOT(showOptionsDialog()), actionCollection());
+
+ // toolbar...
+ new KAction(i18n("Clear Input Field"), "query_erase", 0 , this,
+ SLOT(clearInput()), actionCollection(), "clear_query");
+
+ actQueryLabel = new DictLabelAction(i18n("&Look for:"), actionCollection(), "look_label");
+ actQueryCombo = new DictComboAction(i18n("Query"), actionCollection(), "query_combo",true,true);
+ connect(actQueryCombo,SIGNAL(activated(const QString &)), SLOT(define(const QString&)));
+ actQueryCombo->setCompletionMode(global->queryComboCompletionMode);
+ actDbLabel = new DictLabelAction(i18n("&in"), actionCollection(), "in_label");
+ actDbCombo = new DictComboAction(i18n("Databases"), actionCollection(), "db_combo",false,false);
+ connect(actDbCombo,SIGNAL(activated(int)),SLOT(databaseSelected(int)));
+ actDefineBtn = new DictButtonAction(i18n("&Define"), this, SLOT(doDefine()), actionCollection(), "define_btn");
+ actMatchBtn = new DictButtonAction(i18n("&Match"), this, SLOT(doMatch()), actionCollection(), "match_btn");
+
+ queryView->setActions(actBack,actForward,actQueryCombo);
+}
+
+
+void TopLevel::setupStatusBar()
+{
+ statusBar()->insertItem(i18n(" Ready "),0,2);
+ statusBar()->setItemAlignment(0,AlignLeft | AlignVCenter);
+
+ QString serverInfo;
+ if (global->authEnabled)
+ serverInfo = QString(" %1@%2:%3 ").arg(getShortString(global->user,50))
+ .arg(getShortString(global->server,50))
+ .arg(global->port);
+ else
+ serverInfo = QString(" %1:%3 ").arg(getShortString(global->server,50))
+ .arg(global->port);
+ statusBar()->insertItem(serverInfo, 1,3);
+ statusBar()->setItemAlignment(1,AlignLeft | AlignVCenter);
+}
+
+
+void TopLevel::recreateGUI()
+{
+ kdDebug(5004) << "TopLevel::recreateGUI()" << endl;
+ createGUI("kdictui.rc", false);
+ actQueryCombo->setList(global->queryHistory);
+ actQueryCombo->clearEdit();
+ actQueryLabel->setBuddy(actQueryCombo->widget());
+
+ actDbCombo->setList(global->databases);
+ actDbCombo->setCurrentItem(global->currentDatabase);
+ actDbLabel->setBuddy(actDbCombo->widget());
+ int bwidth;
+ if (actDefineBtn->widthHint() > actMatchBtn->widthHint())
+ bwidth = actDefineBtn->widthHint();
+ else
+ bwidth = actMatchBtn->widthHint();
+ actDefineBtn->setWidth(bwidth);
+ actMatchBtn->setWidth(bwidth);
+}
+
+
+// add text in the query-combobox to the history
+void TopLevel::addCurrentInputToHistory()
+{
+ QString text(actQueryCombo->currentText());
+
+ // maintain queryHistory
+ global->queryHistory.remove(text); // no double entrys
+ global->queryHistory.prepend(text); // prepend new item
+ while (global->queryHistory.count()>global->maxHistEntrys) // shorten list
+ global->queryHistory.remove(global->queryHistory.fromLast());
+
+ actQueryCombo->setList(global->queryHistory);
+ actQueryCombo->setCurrentItem(0);
+ buildHistMenu();
+}
+
+
+
+// erase text in query-combobox
+void TopLevel::clearInput()
+{
+ actQueryCombo->clearEdit();
+ actQueryCombo->setFocus(); // place cursor in combobox
+}
+
+
+// define text in the combobox
+void TopLevel::doDefine()
+{
+ QString text(actQueryCombo->currentText());
+
+ if (!text.isEmpty())
+ {
+ addCurrentInputToHistory();
+ actQueryCombo->selectAll();
+ interface->define(text);
+ }
+}
+
+
+void TopLevel::doMatch()
+{
+ QString text(actQueryCombo->currentText());
+
+ if (!text.isEmpty())
+ {
+ addCurrentInputToHistory();
+ actQueryCombo->selectAll();
+
+ if (!global->showMatchList)
+ {
+ toggleMatchListShow();
+ }
+
+ matchView->match(text);
+ setCaption(getShortString(text.simplifyWhiteSpace(),70));
+ }
+}
+
+
+void TopLevel::stopClients()
+{
+ interface->stop();
+ queryView->stop();
+}
+
+
+// rebuild history menu on demand
+void TopLevel::buildHistMenu()
+{
+ unplugActionList("history_items");
+
+ historyActionList.setAutoDelete(true);
+ historyActionList.clear();
+
+ unsigned int i = 0;
+ while ((i<10)&&(i<global->queryHistory.count())) {
+ historyActionList.append( new KAction(getShortString(global->queryHistory[i],70), 0, this, SLOT(queryHistMenu()),
+ (QObject*)0, global->queryHistory[i].utf8().data()) );
+ i++;
+ }
+
+ plugActionList("history_items", historyActionList);
+}
+
+
+// process a query via the history menu
+void TopLevel::queryHistMenu()
+{
+ QCString name = sender()->name();
+ if (!name.isEmpty())
+ define(QString::fromUtf8(name));
+}
+
+
+void TopLevel::clearQueryHistory()
+{
+ global->queryHistory.clear();
+ actQueryCombo->clear();
+ buildHistMenu();
+}
+
+
+// fill combos, rebuild menus
+void TopLevel::stratDbChanged()
+{
+ actDbCombo->setList(global->databases);
+ actDbCombo->setCurrentItem(global->currentDatabase);
+ matchView->updateStrategyCombo();
+
+ unplugActionList("db_detail");
+
+ dbActionList.setAutoDelete(true);
+ dbActionList.clear();
+
+ for (unsigned int i=0;i<global->serverDatabases.count();i++)
+ dbActionList.append( new KAction(global->serverDatabases[i], 0, this, SLOT(dbInfoMenuClicked()),
+ (QObject*)0, global->serverDatabases[i].utf8().data()) );
+
+ plugActionList("db_detail", dbActionList);
+}
+
+
+void TopLevel::dbInfoMenuClicked()
+{
+ QCString name = sender()->name();
+ if (!name.isEmpty())
+ interface->showDbInfo(name);
+}
+
+
+void TopLevel::databaseSelected(int num)
+{
+ global->currentDatabase = num;
+}
+
+
+void TopLevel::enableCopy(bool selected)
+{
+ actCopy->setEnabled(selected);
+}
+
+
+void TopLevel::enablePrintSave()
+{
+ actSave->setEnabled(true);
+ actPrint->setEnabled(true);
+}
+
+
+void TopLevel::clientStarted(const QString &message)
+{
+ statusBar()->changeItem(message,0);
+ resetStatusbarTimer.stop();
+ stopRef++;
+ actStopQuery->setEnabled(stopRef>0); // enable stop-icon
+ kapp->setOverrideCursor(waitCursor);
+}
+
+
+void TopLevel::clientStopped(const QString &message)
+{
+ statusBar()->changeItem(message,0);
+ resetStatusbarTimer.start(4000);
+ if (stopRef > 0)
+ stopRef--;
+ actStopQuery->setEnabled(stopRef>0); // disable stop-icon
+ kapp->restoreOverrideCursor();
+}
+
+
+void TopLevel::resetStatusbar()
+{
+ resetStatusbarTimer.stop();
+ statusBar()->changeItem(i18n(" Ready "),0);
+}
+
+
+void TopLevel::renderingStarted()
+{
+ stopRef++;
+ actStopQuery->setEnabled(stopRef>0); // disable stop-icon
+ kapp->setOverrideCursor(waitCursor);
+}
+
+
+void TopLevel::renderingStopped()
+{
+ if (stopRef > 0)
+ stopRef--;
+ actStopQuery->setEnabled(stopRef>0); // disable stop-icon
+ kapp->restoreOverrideCursor();
+}
+
+
+void TopLevel::newCaption(const QString &s)
+{
+ setCaption(s);
+}
+
+void TopLevel::toggleMatchListShow()
+{
+ saveMatchViewSize();
+ if (global->showMatchList) // list is visible -> hide it
+ {
+ global->showMatchList = false;
+ queryView->reparent(this,0,queryView->pos(),true);
+ matchView->reparent(this,0,matchView->pos(),true);
+ matchView->hide();
+ delete splitter;
+ setCentralWidget(queryView);
+ }
+ else // list is not visible -> show it
+ {
+ global->showMatchList = true;
+ splitter = new QSplitter(QSplitter::Horizontal,this);
+ splitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
+ setCentralWidget(splitter);
+ splitter->show();
+ queryView->reparent(splitter,0,queryView->pos(),true);
+ matchView->reparent(splitter,0,matchView->pos(),true);
+ splitter->setResizeMode(matchView,QSplitter::KeepSize);
+ adjustMatchViewSize();
+ }
+
+ actShowMatchList->setChecked(global->showMatchList);
+}
+
+
+void TopLevel::saveMatchViewSize()
+{
+ if (global->showMatchList)
+ {
+ global->splitterSizes = splitter->sizes();
+ }
+}
+
+
+void TopLevel::adjustMatchViewSize()
+{
+ if (global->splitterSizes.count()==2)
+ {
+ splitter->setSizes(global->splitterSizes);
+ }
+}
+
+
+void TopLevel::slotConfToolbar()
+{
+ saveMainWindowSettings(KGlobal::config(),"toplevel_options");
+ KEditToolbar dlg(actionCollection(), "kdictui.rc");
+ connect(&dlg,SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() ));
+ dlg.exec();
+}
+
+
+void TopLevel::slotNewToolbarConfig()
+{
+ recreateGUI();
+ applyMainWindowSettings(KGlobal::config(),"toplevel_options");
+ buildHistMenu(); // actionlists must be inserted
+ stratDbChanged();
+}
+
+
+void TopLevel::showSetsDialog()
+{
+ if (!setsDlg) {
+ setsDlg = new DbSetsDialog(this);
+ connect(setsDlg,SIGNAL(setsChanged()),this,SLOT(setsChanged()));
+ connect(setsDlg,SIGNAL(dialogClosed()),this,SLOT(hideSetsDialog()));
+ setsDlg->show();
+ } else {
+ KWin::activateWindow(setsDlg->winId());
+ }
+}
+
+
+void TopLevel::hideSetsDialog()
+{
+ if (setsDlg) {
+ setsDlg->delayedDestruct();
+ setsDlg = 0L;
+ }
+}
+
+
+void TopLevel::setsChanged()
+{
+ actDbCombo->setList(global->databases);
+ actDbCombo->setCurrentItem(global->currentDatabase);
+}
+
+
+void TopLevel::showOptionsDialog()
+{
+ if (!optDlg) {
+ optDlg = new OptionsDialog(this);
+ connect(optDlg,SIGNAL(optionsChanged()),this,SLOT(optionsChanged()));
+ connect(optDlg,SIGNAL(finished()),this,SLOT(hideOptionsDialog()));
+ optDlg->show();
+ } else {
+ KWin::activateWindow(optDlg->winId());
+ }
+}
+
+
+void TopLevel::hideOptionsDialog()
+{
+ if (optDlg) {
+ optDlg->delayedDestruct();
+ optDlg=0;
+ }
+}
+
+
+void TopLevel::optionsChanged()
+{
+ QString serverInfo;
+ if (global->authEnabled)
+ serverInfo = QString(" %1@%2:%3 ").arg(getShortString(global->user,50))
+ .arg(getShortString(global->server,50))
+ .arg(global->port);
+ else
+ serverInfo = QString(" %1:%3 ").arg(getShortString(global->server,50))
+ .arg(global->port);
+ statusBar()->changeItem(serverInfo,1);
+ interface->serverChanged(); // inform client
+ queryView->optionsChanged(); // inform html-view
+}
+
+void TopLevel::raiseWindow()
+{
+ // Bypass focus stealing prevention
+ kapp->updateUserTimestamp();
+
+ KWin::WindowInfo info = KWin::windowInfo( winId() );
+
+ if ( !info.isOnCurrentDesktop() )
+ {
+ KWin::setOnDesktop( winId(), KWin::currentDesktop() );
+ }
+
+ KWin::activateWindow(winId());
+}
+
+
+//--------------------------------
+
+#include "toplevel.moc"
diff --git a/kdict/toplevel.h b/kdict/toplevel.h
new file mode 100644
index 00000000..cd8fa25d
--- /dev/null
+++ b/kdict/toplevel.h
@@ -0,0 +1,149 @@
+/* -------------------------------------------------------------
+
+ toplevel.h (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias Hölzer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ TopLevel The toplevel widget of Kdict.
+
+ ------------------------------------------------------------- */
+
+#ifndef _TOPLEVEL_H_
+#define _TOPLEVEL_H_
+
+#include <qtimer.h>
+#include <kmainwindow.h>
+#include "dcopinterface.h"
+
+class QSplitter;
+
+class KToggleAction;
+class KToolBarPopupAction;
+
+class DictLabelAction;
+class DictComboAction;
+class DictButtonAction;
+class MatchView;
+class QueryView;
+class OptionsDialog;
+class DbSetsDialog;
+
+
+class TopLevel : public KMainWindow, virtual public KDictIface
+{
+ Q_OBJECT
+
+ friend class QueryView;
+
+public:
+
+ TopLevel(QWidget* parent = 0, const char* name = 0);
+ ~TopLevel();
+
+ void normalStartup(); // called when started without commandline parameters
+
+ // DCOP-Interface...
+ void quit();
+ void makeActiveWindow();
+ void definePhrase(QString phrase);
+ void matchPhrase(QString phrase);
+ void defineClipboardContent();
+ void matchClipboardContent();
+ QStringList getDatabases();
+ QString currentDatabase();
+ QStringList getStrategies();
+ QString currentStrategy();
+ bool setDatabase(QString db);
+ bool setStrategy(QString strategy);
+ bool historyGoBack();
+ bool historyGoForward();
+
+public slots:
+
+ void define(const QString &query);
+ void defineClipboard();
+
+ void match(const QString &query);
+ void matchClipboard();
+
+protected:
+ bool queryClose();
+
+private:
+
+ void setupActions();
+ void setupStatusBar();
+ void recreateGUI();
+ void raiseWindow();
+
+ void addCurrentInputToHistory(); // add text in the query-combobox to the history
+
+private slots:
+ void clearInput(); // erase text in query-combobox
+
+ void doDefine(); // define text in the combobox
+ void doMatch(); // match text in the combobox
+
+ void stopClients();
+
+ void buildHistMenu();
+ void queryHistMenu(); // process a query via the history menu
+ void clearQueryHistory();
+
+ void stratDbChanged();
+ void dbInfoMenuClicked();
+ void databaseSelected(int num);
+
+ void enableCopy(bool selected);
+ void enablePrintSave();
+
+ void clientStarted(const QString &message);
+ void clientStopped(const QString &message);
+ void resetStatusbar();
+ void renderingStarted();
+ void renderingStopped();
+
+ void newCaption(const QString&);
+
+ void toggleMatchListShow();
+ void saveMatchViewSize();
+ void adjustMatchViewSize();
+
+ void slotConfToolbar();
+ void slotNewToolbarConfig();
+
+ void showSetsDialog();
+ void hideSetsDialog();
+ void setsChanged();
+
+ void showOptionsDialog();
+ void hideOptionsDialog();
+ void optionsChanged();
+
+private:
+
+ KAction *actSave, *actPrint, *actStartQuery, *actStopQuery, *actCopy;
+ KToggleAction *actShowMatchList;
+ DictLabelAction *actQueryLabel, *actDbLabel;
+ DictComboAction *actQueryCombo, *actDbCombo;
+ DictButtonAction *actDefineBtn, *actMatchBtn;
+ QPtrList<KAction> historyActionList, dbActionList;
+ KToolBarPopupAction *actBack, *actForward;
+
+ QSplitter *splitter; // widgets....
+ QueryView *queryView;
+ MatchView *matchView;
+ OptionsDialog *optDlg;
+ DbSetsDialog *setsDlg;
+
+ QTimer resetStatusbarTimer;
+ int stopRef; // remember how many "clients" are running
+};
+
+#endif
diff --git a/kdnssd/Makefile.am b/kdnssd/Makefile.am
new file mode 100644
index 00000000..cdc3548c
--- /dev/null
+++ b/kdnssd/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = ioslave kdedmodule
diff --git a/kdnssd/ioslave/CONFIG_FORMAT b/kdnssd/ioslave/CONFIG_FORMAT
new file mode 100644
index 00000000..43e34001
--- /dev/null
+++ b/kdnssd/ioslave/CONFIG_FORMAT
@@ -0,0 +1,53 @@
+Every DNS-SD service type used with zeroconf:/ ioslave needs
+configuration file in $DATADIR/zeroconf with name matching service type.
+Used entries:
+
+Type - obligatory, must match file name
+Name - obligarory, specifies user friendly name shown in konqueror/ file dialog
+Name[language] - optional, contains the localized friendly name. E.g. Name[gb]=Colour server
+Exec - optional, specifies executable for helper service - used if service has no
+ corresponding helper protocol
+Protocol - optional, specifies real protocol name, will be taken from service name
+ if not set (for example _ssh._tcp => ssh://)
+Icon - optional, if not set will be taken from protocol
+PathEntry - optional, contains name of attribute carrying path
+UserEntry - optional, contains name of attribute carrying user name
+PasswdEntry - optional, contains name of attribute carrying password
+
+
+Examples:
+
+Name=FTP servers
+Type=_ftp._tcp
+PathEntry=path
+UserEntry=u
+PasswordEntry=p
+
+Protocol is taken from service type (ftp), if service is announced with attributes path, u or p, it
+will be inserted into resolved URL.
+Service published with: Name="Public files", type="_ftp._tcp", port=7773, attributes: path=/home/test/public_files,
+u=test, p=public on hostname "Storage" will be resolved into ftp://test:public@Storage.local/home/test/public_files
+
+-----
+
+
+Name=Remote shell (ssh)
+Type=_ssh._tcp
+UserEntry=u
+PasswordEntry=p
+
+
+Resolved URL will be ssh://user:password@machine:port/ . ssh is helper protocol so ktelnetservice will be launched for
+this URL.
+
+
+
+-----------
+
+Name=Battleship games
+Type=_kbattleship._tcp
+Exec=kbattleship %u
+
+URL will be resolved into kbattleship://host:port, then "kbattleship kbattleship://host:port" will be launched
+
+
diff --git a/kdnssd/ioslave/Makefile.am b/kdnssd/ioslave/Makefile.am
new file mode 100644
index 00000000..7b2cbb9a
--- /dev/null
+++ b/kdnssd/ioslave/Makefile.am
@@ -0,0 +1,21 @@
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = kio_zeroconf.la
+
+kio_zeroconf_la_SOURCES = dnssd.cpp
+kio_zeroconf_la_LIBADD = $(LIB_KDNSSD) $(LIB_KIO)
+kio_zeroconf_la_LDFLAGS = -avoid-version -module $(all_libraries) $(KDE_PLUGIN)
+
+protocol_DATA = zeroconf.protocol invitation.protocol
+protocoldir = $(kde_servicesdir)
+
+services_DATA = _http._tcp _ftp._tcp _ldap._tcp _webdav._tcp _nfs._tcp _ssh._tcp
+servicesdir = $(kde_datadir)/zeroconf
+
+remote_DATA = zeroconf.desktop
+remotedir = $(kde_datadir)/remoteview
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kio_zeroconf.pot
+
+METASOURCES = AUTO
diff --git a/kdnssd/ioslave/_ftp._tcp b/kdnssd/ioslave/_ftp._tcp
new file mode 100644
index 00000000..27655cb4
--- /dev/null
+++ b/kdnssd/ioslave/_ftp._tcp
@@ -0,0 +1,5 @@
+Name=FTP servers
+Type=_ftp._tcp
+PathEntry=path
+UserEntry=u
+PasswordEntry=p
diff --git a/kdnssd/ioslave/_http._tcp b/kdnssd/ioslave/_http._tcp
new file mode 100644
index 00000000..8ce740b7
--- /dev/null
+++ b/kdnssd/ioslave/_http._tcp
@@ -0,0 +1,5 @@
+Name=WWW servers
+Type=_http._tcp
+PathEntry=path
+UserEntry=u
+PasswordEntry=p
diff --git a/kdnssd/ioslave/_ldap._tcp b/kdnssd/ioslave/_ldap._tcp
new file mode 100644
index 00000000..74a3a0cf
--- /dev/null
+++ b/kdnssd/ioslave/_ldap._tcp
@@ -0,0 +1,2 @@
+Name=Lightweight Directory Access Protocol
+Type=_ldap._tcp
diff --git a/kdnssd/ioslave/_nfs._tcp b/kdnssd/ioslave/_nfs._tcp
new file mode 100644
index 00000000..4748fe5b
--- /dev/null
+++ b/kdnssd/ioslave/_nfs._tcp
@@ -0,0 +1,3 @@
+Name=NFS Remote directory
+Type=_nfs._tcp
+PathEntry=path
diff --git a/kdnssd/ioslave/_ssh._tcp b/kdnssd/ioslave/_ssh._tcp
new file mode 100644
index 00000000..9deadb3c
--- /dev/null
+++ b/kdnssd/ioslave/_ssh._tcp
@@ -0,0 +1,4 @@
+Name=Remote shell (ssh)
+Type=_ssh._tcp
+UserEntry=u
+PasswordEntry=p
diff --git a/kdnssd/ioslave/_telnet._tcp b/kdnssd/ioslave/_telnet._tcp
new file mode 100644
index 00000000..4a8c676b
--- /dev/null
+++ b/kdnssd/ioslave/_telnet._tcp
@@ -0,0 +1,4 @@
+Name=Remote shell (telnet)
+Type=_telnet._tcp
+UserEntry=u
+PasswordEntry=p
diff --git a/kdnssd/ioslave/_webdav._tcp b/kdnssd/ioslave/_webdav._tcp
new file mode 100644
index 00000000..3c99b54a
--- /dev/null
+++ b/kdnssd/ioslave/_webdav._tcp
@@ -0,0 +1,5 @@
+Name=WebDav remote directory
+Type=_webdav._tcp
+PathEntry=path
+UserEntry=u
+PasswordEntry=p
diff --git a/kdnssd/ioslave/dnssd.cpp b/kdnssd/ioslave/dnssd.cpp
new file mode 100644
index 00000000..650a37ff
--- /dev/null
+++ b/kdnssd/ioslave/dnssd.cpp
@@ -0,0 +1,369 @@
+/***************************************************************************
+ * Copyright (C) 2004, 2005 by Jakub Stachowski *
+ * qbast@go2.pl *
+ * *
+ * 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. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <qcstring.h>
+#include <qsocket.h>
+#include <qdatetime.h>
+#include <qbitarray.h>
+
+#include <stdlib.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kinstance.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <ksocketaddress.h>
+#include <kprotocolinfo.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <ksock.h>
+#include <qmap.h>
+#include <kapplication.h>
+#include <qeventloop.h>
+#include <dnssd/domainbrowser.h>
+#include <krun.h>
+
+
+#include "dnssd.h"
+
+static const KCmdLineOptions options[] =
+{
+ { "+protocol", I18N_NOOP( "Protocol name" ), 0 },
+ { "+pool", I18N_NOOP( "Socket name" ), 0 },
+ { "+app", I18N_NOOP( "Socket name" ), 0 },
+ KCmdLineLastOption
+};
+
+ZeroConfProtocol::ZeroConfProtocol(const QCString& protocol, const QCString &pool_socket, const QCString &app_socket)
+ : SlaveBase(protocol, pool_socket, app_socket), browser(0),toResolve(0),
+ configData(0)
+{}
+
+ZeroConfProtocol::~ZeroConfProtocol()
+{
+ delete configData;
+}
+
+void ZeroConfProtocol::get(const KURL& url )
+{
+ if (!dnssdOK()) return;
+ UrlType t = checkURL(url);
+ switch (t) {
+ case HelperProtocol:
+ {
+ resolveAndRedirect(url,true);
+ mimeType("text/html");
+ QString reply= "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n";
+ reply+="</head>\n<body>\n<h2>"+i18n("Requested service has been launched in separate window.");
+ reply+="</h2>\n</body></html>";
+ data(reply.utf8());
+ data(QByteArray());
+ finished();
+ break;
+ }
+ case Service:
+ resolveAndRedirect(url);
+ break;
+ default:
+ error(ERR_MALFORMED_URL,i18n("invalid URL"));
+ }
+}
+void ZeroConfProtocol::mimetype(const KURL& url )
+{
+ resolveAndRedirect(url);
+}
+
+UrlType ZeroConfProtocol::checkURL(const KURL& url)
+{
+ if (url.path()=="/") return RootDir;
+ QString service, type, domain;
+ dissect(url,service,type,domain);
+ const QString& proto = type.section('.',1,-1);
+ if (type[0]!='_' || (proto!="_udp" && proto!="_tcp")) return Invalid;
+ if (service.isEmpty()) return ServiceDir;
+ if (!domain.isEmpty()) {
+ if (!setConfig(type)) return Invalid;
+ if (!configData->readEntry("Exec").isNull()) return HelperProtocol;
+ return (KProtocolInfo::isHelperProtocol( configData->readEntry( "Protocol",
+ type.section(".",0,0).mid(1)))) ? HelperProtocol : Service;
+ }
+ return Invalid;
+}
+
+// URL zeroconf://domain/_http._tcp/some%20service
+// URL invitation://host:port/_http._tcp/some%20service?u=username&root=directory
+void ZeroConfProtocol::dissect(const KURL& url,QString& name,QString& type,QString& domain)
+{
+ type = url.path().section("/",1,1);
+ domain = url.host();
+ name = url.path().section("/",2,-1);
+
+}
+
+bool ZeroConfProtocol::dnssdOK()
+{
+ switch(ServiceBrowser::isAvailable()) {
+ case ServiceBrowser::Stopped:
+ error(KIO::ERR_UNSUPPORTED_ACTION,
+ i18n("The Zeroconf daemon (mdnsd) is not running."));
+ return false;
+ case ServiceBrowser::Unsupported:
+ error(KIO::ERR_UNSUPPORTED_ACTION,
+ i18n("KDE has been built without Zeroconf support."));
+ return false;
+ default:
+ return true;
+ }
+}
+
+void ZeroConfProtocol::stat(const KURL& url)
+{
+ UDSEntry entry;
+ if (!dnssdOK()) return;
+ UrlType t = checkURL(url);
+ switch (t) {
+ case RootDir:
+ case ServiceDir:
+ buildDirEntry(entry,"");
+ statEntry(entry);
+ finished();
+ break;
+ case Service:
+ resolveAndRedirect(url);
+ break;
+ case HelperProtocol:
+ {
+ QString name,type,domain;
+ dissect(url,name,type,domain);
+ buildServiceEntry(entry,name,type,domain);
+ statEntry(entry);
+ finished();
+ break;
+ }
+ default:
+ error(ERR_MALFORMED_URL,i18n("invalid URL"));
+ }
+}
+QString ZeroConfProtocol::getAttribute(const QString& name)
+{
+ QString entry = configData->readEntry(name);
+ return (entry.isNull()) ? QString::null : toResolve->textData()[entry];
+}
+
+void ZeroConfProtocol::resolveAndRedirect(const KURL& url, bool useKRun)
+{
+ QString name,type,domain;
+ dissect(url,name,type,domain);
+ if (url.protocol()=="invitation") {
+ delete toResolve;
+ toResolve=0;
+ toResolve= new RemoteService(url);
+ if (!toResolve->isResolved()) error(ERR_MALFORMED_URL,i18n("Invalid URL"));
+ } else {
+ kdDebug() << "Resolve for " << name << ", " << type << ", " << domain << "\n";
+ if (toResolve!=0)
+ if (toResolve->serviceName()==name && toResolve->type()==type &&
+ toResolve->domain()==domain && toResolve->isResolved()) {
+ } else {
+ delete toResolve;
+ toResolve = 0;
+ }
+ if (toResolve==0) {
+ toResolve = new RemoteService(name,type,domain);
+ // or maybe HOST_NOT_FOUND?
+ if (!toResolve->resolve()) error(ERR_SERVICE_NOT_AVAILABLE,i18n("Unable to resolve service"));
+ }
+ }
+ KURL destUrl;
+ kdDebug() << "Resolved: " << toResolve->hostName() << "\n";
+ destUrl.setProtocol(getProtocol(type));
+ destUrl.setUser(getAttribute("UserEntry"));
+ destUrl.setPass(getAttribute("PasswordEntry"));
+ destUrl.setPath(getAttribute("PathEntry"));
+ destUrl.setHost(toResolve->hostName());
+ destUrl.setPort(toResolve->port());
+ // get exec from config or try getting it from helper protocol
+ if (useKRun) KRun::run(configData->readEntry("Exec",KProtocolInfo::exec(getProtocol(type))),destUrl);
+ else {
+ redirection(destUrl);
+ finished();
+ }
+}
+
+bool ZeroConfProtocol::setConfig(const QString& type)
+{
+ kdDebug() << "Setting config for " << type << endl;
+ if (configData)
+ {
+ if (configData->readEntry("Type")!=type)
+ {
+ delete configData;
+ configData=0L;
+ }
+ else
+ return true;
+ }
+ configData = new KConfig("zeroconf/"+type,false,false,"data");
+ return (configData->readEntry("Type")==type);
+}
+
+inline void buildAtom(UDSEntry& entry,UDSAtomTypes type, const QString& data)
+{
+ UDSAtom atom;
+ atom.m_uds=type;
+ atom.m_str=data;
+ entry.append(atom);
+}
+inline void buildAtom(UDSEntry& entry,UDSAtomTypes type, long data)
+{
+ UDSAtom atom;
+ atom.m_uds=type;
+ atom.m_long=data;
+ entry.append(atom);
+}
+
+
+void ZeroConfProtocol::buildDirEntry(UDSEntry& entry,const QString& name,const QString& type, const QString& host)
+{
+ entry.clear();
+ buildAtom(entry,UDS_NAME,name);
+ buildAtom(entry,UDS_ACCESS,0555);
+ buildAtom(entry,UDS_SIZE,0);
+ buildAtom(entry,UDS_FILE_TYPE,S_IFDIR);
+ buildAtom(entry,UDS_MIME_TYPE,"inode/directory");
+ if (!type.isNull()) buildAtom(entry,UDS_URL,"zeroconf:/"+((!host.isNull()) ? "/"+host+"/" : "" )+type+"/");
+}
+QString ZeroConfProtocol::getProtocol(const QString& type)
+{
+ setConfig(type);
+ return configData->readEntry("Protocol",type.section(".",0,0).mid(1));
+}
+
+void ZeroConfProtocol::buildServiceEntry(UDSEntry& entry,const QString& name,const QString& type,const QString& domain)
+{
+ setConfig(type);
+ entry.clear();
+ buildAtom(entry,UDS_NAME,name);
+ buildAtom(entry,UDS_ACCESS,0666);
+ QString icon=configData->readEntry("Icon",KProtocolInfo::icon(getProtocol(type)));
+ if (!icon.isNull()) buildAtom(entry,UDS_ICON_NAME,icon);
+ KURL protourl;
+ protourl.setProtocol(getProtocol(type));
+ QString encname = "zeroconf://" + domain +"/" +type+ "/" + name;
+ if (KProtocolInfo::supportsListing(protourl)) {
+ buildAtom(entry,UDS_FILE_TYPE,S_IFDIR);
+ encname+="/";
+ } else buildAtom(entry,UDS_FILE_TYPE,S_IFREG);
+ buildAtom(entry,UDS_URL,encname);
+}
+
+void ZeroConfProtocol::listDir(const KURL& url )
+{
+
+ if (!dnssdOK()) return;
+ UrlType t = checkURL(url);
+ UDSEntry entry;
+ switch (t) {
+ case RootDir:
+ if (allDomains=url.host().isEmpty())
+ browser = new ServiceBrowser(ServiceBrowser::AllServices);
+ else browser = new ServiceBrowser(ServiceBrowser::AllServices,url.host());
+ connect(browser,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),
+ this,SLOT(newType(DNSSD::RemoteService::Ptr)));
+ break;
+ case ServiceDir:
+ if (url.host().isEmpty())
+ browser = new ServiceBrowser(url.path(-1).section("/",1,-1));
+ else browser = new ServiceBrowser(url.path(-1).section("/",1,-1),url.host());
+ connect(browser,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),
+ this,SLOT(newService(DNSSD::RemoteService::Ptr)));
+ break;
+ case Service:
+ resolveAndRedirect(url);
+ return;
+ default:
+ error(ERR_MALFORMED_URL,i18n("invalid URL"));
+ return;
+ }
+ connect(browser,SIGNAL(finished()),this,SLOT(allReported()));
+ browser->startBrowse();
+ kapp->eventLoop()->enterLoop();
+}
+void ZeroConfProtocol::allReported()
+{
+ UDSEntry entry;
+ listEntry(entry,true);
+ finished();
+ delete browser;
+ browser=0;
+ mergedtypes.clear();
+ kapp->eventLoop()->exitLoop();
+}
+void ZeroConfProtocol::newType(DNSSD::RemoteService::Ptr srv)
+{
+ if (mergedtypes.contains(srv->type())>0) return;
+ mergedtypes << srv->type();
+ UDSEntry entry;
+ kdDebug() << "Got new entry " << srv->type() << endl;
+ if (!setConfig(srv->type())) return;
+ QString name = configData->readEntry("Name");
+ if (!name.isNull()) {
+ buildDirEntry(entry,name,srv->type(), (allDomains) ? QString::null :
+ browser->browsedDomains()->domains()[0]);
+ listEntry(entry,false);
+ }
+}
+void ZeroConfProtocol::newService(DNSSD::RemoteService::Ptr srv)
+{
+ UDSEntry entry;
+ buildServiceEntry(entry,srv->serviceName(),srv->type(),srv->domain());
+ listEntry(entry,false);
+}
+
+
+extern "C"
+{
+ int KDE_EXPORT kdemain( int argc, char **argv )
+ {
+ // KApplication is necessary to use other ioslaves
+ putenv(strdup("SESSION_MANAGER="));
+ KCmdLineArgs::init(argc, argv, "kio_zeroconf", 0, 0, 0, 0);
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication::disableAutoDcopRegistration();
+ KApplication app;
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ ZeroConfProtocol slave( args->arg(0), args->arg(1), args->arg(2) );
+ slave.dispatchLoop();
+ return 0;
+ }
+}
+
+
+#include "dnssd.moc"
+
diff --git a/kdnssd/ioslave/dnssd.h b/kdnssd/ioslave/dnssd.h
new file mode 100644
index 00000000..0f24dbee
--- /dev/null
+++ b/kdnssd/ioslave/dnssd.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2004, 2005 by Jakub Stachowski *
+ * qbast@go2.pl *
+ * *
+ * 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. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _dnssd_H_
+#define _dnssd_H_
+
+#include <qstring.h>
+#include <qcstring.h>
+#include <qobject.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+#include <dnssd/servicebrowser.h>
+#include <dnssd/remoteservice.h>
+#include <qstringlist.h>
+
+
+class QCString;
+using namespace KIO;
+using namespace DNSSD;
+
+enum UrlType { RootDir, ServiceDir, Service, HelperProtocol, Invalid };
+
+class ZeroConfProtocol : public QObject, public KIO::SlaveBase
+{
+ Q_OBJECT
+public:
+ ZeroConfProtocol(const QCString& protocol, const QCString &pool_socket, const QCString &app_socket);
+ ~ZeroConfProtocol();
+ virtual void get(const KURL& url);
+ virtual void mimetype(const KURL& url);
+ virtual void stat(const KURL& url);
+ virtual void listDir(const KURL& url );
+private:
+ // Create UDSEntry for zeroconf:/ or zeroconf:/type/ paths
+ void buildDirEntry(UDSEntry& entry,const QString& name,const QString& type=QString::null,
+ const QString& host=QString::null);
+ // Create UDSEntry for single services: dnssd:/type/service
+ void buildServiceEntry(UDSEntry& entry,const QString& name,const QString& type,
+ const QString& domain);
+ // Returns root dir, service dir, service or invalid
+ UrlType checkURL(const KURL& url);
+ // extract name, type and domain from URL
+ void dissect(const KURL& url,QString& name,QString& type,QString& domain);
+ // resolve given service and redirect() to it or use KRun on it (used for helper protocols)
+ void resolveAndRedirect(const KURL& url, bool useKRun = false);
+ bool dnssdOK();
+ QString getAttribute(const QString& name);
+ QString getProtocol(const QString& type);
+ // try to load config file for given service type (or just return if already loaded)
+ bool setConfig(const QString& type);
+
+ ServiceBrowser* browser;
+ // service types merged from all domains - to avoid duplicates
+ QStringList mergedtypes;
+ // last resolved or still being resolved services - acts as one-entry cache
+ RemoteService *toResolve;
+ // Config file for service - also acts as one-entry cache
+ KConfig *configData;
+ // listDir for all domains (zeroconf:/) or one specified (zeroconf://domain/)
+ bool allDomains;
+private slots:
+ void newType(DNSSD::RemoteService::Ptr);
+ void newService(DNSSD::RemoteService::Ptr);
+ void allReported();
+
+};
+
+#endif
diff --git a/kdnssd/ioslave/invitation.protocol b/kdnssd/ioslave/invitation.protocol
new file mode 100644
index 00000000..ef6b068c
--- /dev/null
+++ b/kdnssd/ioslave/invitation.protocol
@@ -0,0 +1,62 @@
+[Protocol]
+exec=kio_zeroconf
+protocol=invitation
+
+input=none
+output=filesystem
+listing=Name,Link,Type
+reading=true
+writing=false
+makedir=false
+deleting=false
+linking=false
+moving=false
+Icon=network_local
+Description=SD invitations
+Description[be]=ЗапрашÑнні SD
+Description[bg]=Покани SD
+Description[bn]=à¦à¦¸à¦¡à¦¿ আমনà§à¦¤à§à¦°à¦£
+Description[bs]=SD pozivi
+Description[ca]=Invitacions SD
+Description[cs]=SD pozvánky
+Description[da]=SD-invitationer
+Description[de]=SD-Einladungen
+Description[el]=ΠÏοσκλήσεις SD
+Description[es]=Invitaciones SD
+Description[et]=SD kutsed
+Description[eu]=SD gonbidapenak
+Description[fa]=دعوتهای SD
+Description[fi]=SD-kutsut
+Description[fr]=Invitations SD
+Description[gl]=Invitacións SD
+Description[hu]=SD meghívók
+Description[is]=SD boð
+Description[it]=Inviti SD
+Description[ja]=SD 招待
+Description[ka]=SD მáƒáƒ¬áƒ•áƒ”ვáƒ
+Description[kk]=SD шақырулар
+Description[km]=លិážáž·ážâ€‹áž¢áž‰áŸ’ជើញ SD
+Description[lt]=SD pakvietimai
+Description[nb]=SD invitasjoner
+Description[nds]=SD-Inladen
+Description[ne]=SD निमनà¥à¤¤à¥à¤°à¤£à¤¾
+Description[nl]=SD-uitnodigingen
+Description[nn]=SD-innbydingar
+Description[pa]=SD ਸੱਦਾ
+Description[pl]=Zaproszenia SD
+Description[pt]=Convites de SD
+Description[pt_BR]=solicitações SD
+Description[ru]=ÐŸÑ€Ð¸Ð³Ð»Ð°ÑˆÐµÐ½Ð¸Ñ SD
+Description[sk]=SD pozvánky
+Description[sl]=Povabila SD
+Description[sr]=SD позивнице
+Description[sr@Latn]=SD pozivnice
+Description[sv]=SD-inbjudningar
+Description[tr]=SD davetleri
+Description[uk]=Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ SD
+Description[zh_CN]=SD 邀请
+Description[zh_HK]=SD 邀請
+Description[zh_TW]=SD 邀請
+maxInstances=4
+Class=:remote
+
diff --git a/kdnssd/ioslave/zeroconf.desktop b/kdnssd/ioslave/zeroconf.desktop
new file mode 100644
index 00000000..abc41151
--- /dev/null
+++ b/kdnssd/ioslave/zeroconf.desktop
@@ -0,0 +1,56 @@
+[Desktop Entry]
+Icon=network_local
+Name=Network Services
+Name[be]=Ð¡ÐµÑ‚ÐºÐ°Ð²Ñ‹Ñ ÑервіÑÑ‹
+Name[bg]=Мрежови уÑлуги
+Name[bn]=নেটওয়ারà§à¦• সারà§à¦­à¦¿à¦¸
+Name[br]=Servijoù rouedad
+Name[bs]=Mrežni servisi
+Name[ca]=Serveis de xarxa
+Name[cs]=Síťové služby
+Name[da]=Netværkstjenester
+Name[de]=Netzwerkdienste
+Name[el]=ΥπηÏεσίες δικτÏου
+Name[eo]=Retservoj
+Name[es]=Servicios de red
+Name[et]=Võrguteenused
+Name[eu]=Sare zerbitzuak
+Name[fa]=خدمات شبکه
+Name[fi]=Lähiverkkopalvelut
+Name[fr]=Services réseaux
+Name[ga]=Seirbhísí Líonra
+Name[gl]=Servicios de Rede
+Name[he]=שרותי רשת
+Name[hu]=Hálózati szolgáltatások
+Name[is]=Netþjónustur
+Name[it]=Servizi di rete
+Name[ja]=ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚µãƒ¼ãƒ“ス
+Name[ka]=ქსელის სერვისები
+Name[kk]=Желі қызметтері
+Name[km]=សáŸážœáž¶â€‹áž”ណ្ដាញ
+Name[lt]=Tinklo tarnybos
+Name[nb]=Nettverkstjenester
+Name[nds]=Nettwarkdeensten
+Name[ne]=सञà¥à¤œà¤¾à¤² सेवा
+Name[nl]=Netwerkdiensten
+Name[nn]=Nettverkstenester
+Name[pa]=ਨੈੱਟਵਰਕ ਸੇਵਾਵਾਂ
+Name[pl]=Usługi sieciowe
+Name[pt]=Serviços de Rede
+Name[pt_BR]=Serviços de Rede
+Name[ro]=Servicii de reţea
+Name[ru]=Сетевые Ñлужбы
+Name[sk]=Sieťové služby
+Name[sl]=Omrežne storitve
+Name[sr]=Мрежни ÑервиÑи
+Name[sr@Latn]=Mrežni servisi
+Name[sv]=Nätverkstjänster
+Name[tr]=AÄŸ Servisleri
+Name[uk]=Мережеві Ñлужби
+Name[uz]=Tarmoq xizmatlari
+Name[uz@cyrillic]=Тармоқ хизматлари
+Name[zh_CN]=网络æœåŠ¡
+Name[zh_HK]=網絡æœå‹™
+Name[zh_TW]=網路æœå‹™
+Type=Link
+URL=zeroconf:/
diff --git a/kdnssd/ioslave/zeroconf.protocol b/kdnssd/ioslave/zeroconf.protocol
new file mode 100644
index 00000000..5343b282
--- /dev/null
+++ b/kdnssd/ioslave/zeroconf.protocol
@@ -0,0 +1,63 @@
+[Protocol]
+exec=kio_zeroconf
+protocol=zeroconf
+
+input=none
+output=filesystem
+listing=Name,Link,Type
+reading=true
+writing=false
+makedir=false
+deleting=false
+linking=false
+moving=false
+Icon=network_local
+Description=A kioslave for ZeroConf
+Description[be]=Модуль kioslave Ð´Ð»Ñ ZeroConf
+Description[bn]=জিরো-কনà§à¦« à¦à¦° জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ কে-আই-ও সà§à¦²à§‡à¦­
+Description[br]=Ur c'hioslave evit ZeroConf
+Description[bs]=kioslave za ZeroConf
+Description[ca]=Un kioslave per ZeroConf
+Description[cs]=Pomocný protokol pro Zeroconf
+Description[da]=En kioslave for Zeroconf
+Description[de]=Ein Ein-/Ausgabemodul für ZeroConf
+Description[el]=Ένα kioslave για το ZeroConf
+Description[es]=Un «kioslave» para ZeroConf
+Description[et]=ZeroConfi I/O-moodul
+Description[eu]= kioslave bat ZeroConf-erako
+Description[fa]=یک kioslave برای ZeroConf
+Description[fi]=Siirräntätyöskentelijä ZeroConfille
+Description[fr]=Un module d'entrée / sortie pour ZeroConf
+Description[gl]=Un kioslabe para ZeroConf
+Description[he]=kioslave בשביל ZeroConf
+Description[hu]=KDE-protokoll a Zeroconf használatához
+Description[is]=kioslave fyrir ZeroConf
+Description[it]=Un kioslave per Zeroconf
+Description[ja]=ZeroConf ã® kioslave
+Description[ka]= kioslave ZeroConfსთვის
+Description[kk]=ZeroConf-Ñ‚Ñ‹Ò£ kioslave-Ñ–
+Description[km]=kioslave មួយ​សម្រាប់ ZeroConf
+Description[lt]=AntrinÄ— KDE programa skirta ZeroConf
+Description[nb]=En kioslave for ZeroConf
+Description[nds]=In-/Utgaavmoduul för ZeroConf
+Description[ne]=जेरोकनà¥à¤«à¤•à¤¾ लागि कियोसà¥à¤²à¤¾à¤­
+Description[nl]=Een kioslave voor ZeroConf
+Description[nn]=Ein kio-slave for ZeroConf
+Description[pl]=Wtyczka protokołu ZeroConf
+Description[pt]=Um 'kioslave' para o ZeroConf
+Description[pt_BR]=Um IO-Slave para o ZeroConf
+Description[ro]=Un dispozitiv de I/E pentru ZeroConf
+Description[ru]=Kioslave Ð´Ð»Ñ ZeroConf
+Description[sk]=kioslave pre ZeroConf
+Description[sl]=Kioslave za ZeroConf
+Description[sr]=kioslave за ZeroConf
+Description[sr@Latn]=kioslave za ZeroConf
+Description[sv]=En I/O-slav för Zeroconf
+Description[tr]=Zeroconf kioslave'i
+Description[uk]=Kioslave Ð´Ð»Ñ ZeroConf
+Description[zh_CN]=ZeroConf çš„ kioslave
+Description[zh_HK]=用於 ZeroConf 的 kioslave
+Description[zh_TW]=ZeroConf çš„ kioslave
+maxInstances=4
+Class=:local
+
diff --git a/kdnssd/kdedmodule/Makefile.am b/kdnssd/kdedmodule/Makefile.am
new file mode 100644
index 00000000..c01297db
--- /dev/null
+++ b/kdnssd/kdedmodule/Makefile.am
@@ -0,0 +1,13 @@
+kde_module_LTLIBRARIES = kded_dnssdwatcher.la
+
+METASOURCES = AUTO
+INCLUDES = $(all_includes)
+
+kded_dnssdwatcher_la_SOURCES = dnssdwatcher.cpp dnssdwatcher.skel watcher.cpp
+kded_dnssdwatcher_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_dnssdwatcher_la_LIBADD = $(LIB_KDNSSD) $(LIB_KIO)
+
+
+servicesdir = $(kde_servicesdir)/kded
+services_DATA = dnssdwatcher.desktop
+
diff --git a/kdnssd/kdedmodule/dnssdwatcher.cpp b/kdnssd/kdedmodule/dnssdwatcher.cpp
new file mode 100644
index 00000000..5647d29c
--- /dev/null
+++ b/kdnssd/kdedmodule/dnssdwatcher.cpp
@@ -0,0 +1,94 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2004 Jakub Stachowski <qbast@go2.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "dnssdwatcher.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <dnssd/servicebrowser.h>
+#include <kdirnotify_stub.h>
+#include "watcher.h"
+
+
+DNSSDWatcher::DNSSDWatcher(const QCString& obj)
+ : KDEDModule(obj)
+{
+ connectDCOPSignal("","KDirNotify","enteredDirectory(KURL)","enteredDirectory(KURL)",false);
+ connectDCOPSignal("","KDirNotify","leftDirectory(KURL)","leftDirectory(KURL)",false);
+ watchers.setAutoDelete(true);
+}
+
+QStringList DNSSDWatcher::watchedDirectories()
+{
+//TODO
+// return watchers.keys();
+ QStringList keys;
+ for (QDictIterator<Watcher> it(watchers) ; it.current(); ++it ) {
+ keys << it.currentKey();
+ kdDebug() << it.currentKey() << " " << (*it)->refcount << "\n";
+ }
+return keys;
+}
+
+
+// from ioslave
+void DNSSDWatcher::dissect(const KURL& url,QString& name,QString& type,QString& domain)
+{
+ type = url.path().section("/",1,1);
+ domain = url.host();
+ name = url.path().section("/",2,-1);
+}
+
+
+
+void DNSSDWatcher::enteredDirectory(const KURL& dir)
+{
+ if (dir.protocol()!="zeroconf") return;
+ if (watchers[dir.url()]) watchers[dir.url()]->refcount++;
+ else createNotifier(dir);
+}
+
+
+void DNSSDWatcher::leftDirectory(const KURL& dir)
+{
+ if (dir.protocol()!="zeroconf") return;
+ if (!watchers[dir.url()]) return;
+ if ((watchers[dir.url()])->refcount==1) watchers.remove(dir.url());
+ else watchers[dir.url()]->refcount--;
+}
+
+
+void DNSSDWatcher::createNotifier(const KURL& url)
+{
+ QString domain,type,name;
+ dissect(url,name,type,domain);
+ if (type.isEmpty()) type = DNSSD::ServiceBrowser::AllServices;
+ Watcher *w = new Watcher(type,domain);
+ watchers.insert(url.url(),w);
+}
+
+extern "C" {
+ KDE_EXPORT KDEDModule *create_dnssdwatcher(const QCString &obj)
+ {
+ KGlobal::locale()->insertCatalogue("dnssdwatcher");
+ return new DNSSDWatcher(obj);
+ }
+}
+
+#include "dnssdwatcher.moc"
diff --git a/kdnssd/kdedmodule/dnssdwatcher.desktop b/kdnssd/kdedmodule/dnssdwatcher.desktop
new file mode 100644
index 00000000..31e7640e
--- /dev/null
+++ b/kdnssd/kdedmodule/dnssdwatcher.desktop
@@ -0,0 +1,94 @@
+[Desktop Entry]
+Type=Service
+Name=DNS-SD Services Watcher
+Name[be]=ÐглÑдальнік ÑервіÑаў DNS-SD
+Name[bn]=ডিà¦à¦¨à¦à¦¸-à¦à¦¸à¦¡à¦¿ সারà§à¦­à¦¿à¦¸ পরà§à¦¯à¦¬à§‡à¦•à§à¦·à¦•
+Name[bs]=Nadzor DNS-SD servisa
+Name[ca]=Vigilant de serveis DNS-SD
+Name[cs]=Sledování DNS-SD služeb
+Name[da]=DNS-SD overvågning af tjenester
+Name[de]=Ãœberwachung von DNS-SD-Diensten
+Name[el]=ΠαÏατηÏητής υπηÏεσιών DNS-SD
+Name[es]=Observador de servicios DNS-SD
+Name[et]=DNS-SD teenuste jälgija
+Name[eu]=DNS-SD zerbitzu jarraitzailea
+Name[fa]=پایندۀ خدمات DNS-SD
+Name[fi]=DNS-SD-palvelujen tarkkailija
+Name[fr]=Surveillance des services DNS-SD
+Name[gl]=Observador de Servicios DNS-SD
+Name[hu]=DNS-SD szolgáltatásfigyelő
+Name[is]=DNS-SD þjónustuvaktari
+Name[it]=Sentinella dei servizi DNS-SD
+Name[ja]=DNS-SD サービス監視
+Name[ka]=DNS-SD სერვისების მეთვáƒáƒšáƒ§áƒ£áƒ áƒ”
+Name[kk]=DNS-SD қызметтер бақылаушыÑÑ‹
+Name[km]=កម្មវិធី​ឃ្លាំមើល​សáŸážœáž¶â€‹ DNS-SD
+Name[lt]=DNS-SD tarnybos stebÄ—jimas
+Name[nb]=Overvåker for DNS-SD-tjenester
+Name[nds]=DNS-SD-Deenstkieker
+Name[ne]=DNS-SD सेवा दरà¥à¤¶à¤•
+Name[nl]=DNS-SD-diensten observatie
+Name[nn]=DNS-SD-tenesteovervakar
+Name[pa]=DNS-SD ਸੇਵਾਵਾਂ ਵਾਂਚਰ
+Name[pl]=Nadzorca usług DNS-SD
+Name[pt]=Vigia de Serviços DNS-SD
+Name[pt_BR]=Monitor dos Serviços DNS-SD
+Name[ru]=Служба DNS-SD
+Name[sk]=SledovaÄ DNS-SD služieb
+Name[sl]=Opazovalec storitev DNS-SD
+Name[sr]=Пратилац DNS-SD ÑервиÑа
+Name[sr@Latn]=Pratilac DNS-SD servisa
+Name[sv]=DNS-SD tjänstbevakning
+Name[tr]=DNS-SD Servisi Ä°zleyicisi
+Name[uk]=СпоÑÑ‚ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð° Ñлужбами DNS-SD
+Name[zh_CN]=DNS-SD æœåŠ¡ç›‘视器
+Name[zh_HK]=DNS-SD æœå‹™ç›£å¯Ÿå™¨
+Name[zh_TW]=DNS-SD æœå‹™ç›£çœ‹å™¨
+Comment=Keeps track of DNS-SD services and updates directory listings
+Comment[be]=Ðазірае за ÑпіÑам ÑервіÑаў DNS-SD Ñ– абнаўлÑе выглÑд дырÑкторыÑÑž
+Comment[bn]=ডিà¦à¦¨à¦à¦¸-à¦à¦¸à¦¡à¦¿ সারà§à¦­à¦¿à¦¸à§‡à¦° খোà¦à¦œ রাখে à¦à¦¬à¦‚ ডিরেকà§à¦Ÿà¦°à§€ তালিকা আপডেট করে
+Comment[bs]=Prati DNS-SD servise i ažurira spiskove direktorija
+Comment[ca]=Segueix els serveis DNS-SD i actualitza les llistes de directoris
+Comment[cs]=Udržuje přehled o DNS-SD službách a aktualizuje výpisy adresářů
+Comment[da]=Holder styr på DNS-SD-tjenester og opdaterer lister med mapper
+Comment[de]=Überwacht DNS-SD-Dienste und aktualisiert Verzeichniseinträge
+Comment[el]=ΔιατηÏεί το ίχνος των υπηÏεσιών του DNS-SD και ενημεÏώνει τις λίστες του καταλόγου
+Comment[es]=Vigila los servicios DNS-SD y actualiza los listados de directorio
+Comment[et]=Jälgib DNS-SD teenuseid ja uuendab kataloogide nimekirju
+Comment[eu]=DNS-SD zerbitzuak jarraitu eta direktorio zerrendak eguneratzen ditu
+Comment[fa]=رد خدمات DNS-SD را نگهداری می‌کند Ùˆ Ùهرست برنامه‌های Ùهرست راهنما را به‌روزرسانی می‌کند
+Comment[fi]=Pitää kirjaa DNS-SD-palveluista ja päivittää kansiolistaukset
+Comment[fr]=Conserve une trace des services DNS-SD et actualise les listes de dossiers
+Comment[gl]=Deixa constancia dos servicios DNS-SD e anova as listaxes de directorios
+Comment[hu]=Követi a DNS-SD szolgáltatások állapotát és frissíti a hálózati listákat
+Comment[is]=Fylgist með DNS-SD þjónustum og uppfærir möppulista
+Comment[it]=Mantiene traccia dei servizi DNS-SD e aggiorna le liste delle directory
+Comment[ja]=DNS-SD サービスã®ç®¡ç†ã¨ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªä¸€è¦§ã®æ›´æ–°
+Comment[ka]=DNS-SD სერვისების ჩáƒáƒœáƒáƒ¬áƒ”რების შენáƒáƒ®áƒ•áƒ დრდირექტáƒáƒ áƒ˜áƒ˜áƒ¡ ჩáƒáƒ›áƒáƒœáƒáƒ—ვლის გáƒáƒœáƒáƒ®áƒšáƒ”ბáƒ
+Comment[kk]=DNS-SD қызметтердің протоколын жүргізіп, каталогын жаңарту
+Comment[km]=ážáž¶áž˜ážŠáž¶áž“​សáŸážœáž¶ DNS-SD និង​ធ្វើឲ្យ​ការ​រាយ​ážážâ€‹áž‘ាន់​សមáŸáž™
+Comment[lt]=Seka DNS-SD tarnybas ir atnaujina aplankų sąrašus
+Comment[nb]=Holder øye med DNS-SD-tjenester og oppdaterer katalogoppføringer
+Comment[nds]=Kiekt na DNS-SD-Deensten un frischt Orneroplisten op
+Comment[ne]=DNS-SD सेवाको टà¥à¤°à¤¯à¤¾à¤• राखà¥à¤¦à¤› र डाइरेकà¥à¤Ÿà¤°à¥€ सूची अदà¥à¤¯à¤¾à¤µà¤§à¤¿à¤• गरà¥à¤¦à¤›
+Comment[nl]=Houdt de DNS-SD-diensten bij en actualiseert mappenweergaven
+Comment[nn]=Held auge med DNS-SD-tenester og oppdaterer katalogar
+Comment[pl]=Śledzi usługi DNS-SD services i uaktualnia zawartość katalogu
+Comment[pt]=Vigia os serviços DNS-SD e actualiza listas de pastas
+Comment[pt_BR]=Monitora os serviços DNS-SD
+Comment[ru]=Обновление каталогов DNS-SD
+Comment[sk]=Sleduje DNS-SD služby a aktualizuje výpisy prieÄinku
+Comment[sl]=Spremlja storitve DNS-SD in posodablja sezname imenikov
+Comment[sr]=Ðадгледа DNS-SD ÑервиÑе и ажурира лиÑтинге директоријума
+Comment[sr@Latn]=Nadgleda DNS-SD servise i ažurira listinge direktorijuma
+Comment[sv]=Håller ordning på DNS-SD tjänster och uppdaterar kataloglistor
+Comment[tr]=DNS-SD servisi ve klasör listesini güncelleyisi
+Comment[uk]=СпоÑтерігає за Ñлужбами DNS-SD та оновлює ÑпиÑки каталогів
+Comment[zh_CN]=跟踪 DNS-SD æœåŠ¡å¹¶æ›´æ–°ç›®å½•åˆ—表
+Comment[zh_HK]=監察 DNS-SD æœå‹™ä¸¦æ›´æ–°ç›®éŒ„清單
+Comment[zh_TW]=追蹤 DNS-SD æœå‹™ä¸¦æ›´æ–°ç›®éŒ„清單
+ServiceTypes=KDEDModule
+X-KDE-ModuleType=Library
+X-KDE-Library=dnssdwatcher
+X-KDE-FactoryName=dnssdwatcher
+X-KDE-Kded-autoload=true
diff --git a/kdnssd/kdedmodule/dnssdwatcher.h b/kdnssd/kdedmodule/dnssdwatcher.h
new file mode 100644
index 00000000..e624532a
--- /dev/null
+++ b/kdnssd/kdedmodule/dnssdwatcher.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2004 Jakub Stachowski <qbast@go2.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _DNSSDWATCHER_H_
+#define _DNSSDWATCHER_H_
+
+#include <qdict.h>
+#include <kdedmodule.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kurl.h>
+
+class Watcher;
+class DNSSDWatcher : public KDEDModule
+{
+Q_OBJECT
+K_DCOP
+public:
+ DNSSDWatcher(const QCString& obj);
+
+k_dcop:
+ QStringList watchedDirectories();
+ void enteredDirectory(const KURL& dir);
+ void leftDirectory(const KURL& dir);
+
+private:
+ QDict<Watcher> watchers;
+
+ void createNotifier(const KURL& url);
+ void dissect(const KURL& url,QString& name,QString& type,QString& domain);
+
+};
+
+#endif
diff --git a/kdnssd/kdedmodule/watcher.cpp b/kdnssd/kdedmodule/watcher.cpp
new file mode 100644
index 00000000..9fc32b14
--- /dev/null
+++ b/kdnssd/kdedmodule/watcher.cpp
@@ -0,0 +1,72 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2004 Jakub Stachowski <qbast@go2.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "watcher.h"
+
+#include <kdebug.h>
+
+#include <kdirnotify_stub.h>
+#include <qstringlist.h>
+
+
+
+Watcher::Watcher(const QString& type, const QString& domain)
+ : refcount(1), updateNeeded(false), m_type(type), m_domain(domain)
+{
+ if (domain.isEmpty()) browser = new ServiceBrowser(type);
+ else browser = new ServiceBrowser(type,domain);
+ connect(browser,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),
+ SLOT(serviceAdded(DNSSD::RemoteService::Ptr)));
+ connect(browser,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr)),
+ SLOT(serviceRemoved(DNSSD::RemoteService::Ptr)));
+ connect(browser,SIGNAL(finished()),SLOT(finished()));
+ browser->startBrowse();
+}
+
+Watcher::~Watcher()
+{
+ delete browser;
+}
+
+void Watcher::serviceAdded(DNSSD::RemoteService::Ptr)
+{
+ updateNeeded=true;
+}
+
+void Watcher::serviceRemoved(DNSSD::RemoteService::Ptr srv)
+{
+ if (!updateNeeded) removed << srv;
+}
+
+
+void Watcher::finished()
+{
+ KDirNotify_stub st("*","*");
+ kdDebug() << "Finished for " << m_type << "@" << m_domain << "\n";
+ if (updateNeeded || removed.count()) {
+ QString url = "zeroconf:/";
+ if (!m_domain.isEmpty()) url+="/"+m_domain+"/";
+ if (m_type!=ServiceBrowser::AllServices) url+=m_type;
+ kdDebug() << "Sending update: " << url << "\n";
+ st.FilesAdded(url);
+ }
+ removed.clear();
+ updateNeeded=false;
+}
+
+#include "watcher.moc"
diff --git a/kdnssd/kdedmodule/watcher.h b/kdnssd/kdedmodule/watcher.h
new file mode 100644
index 00000000..703680fc
--- /dev/null
+++ b/kdnssd/kdedmodule/watcher.h
@@ -0,0 +1,50 @@
+/* This file is part of the KDE Project
+ Copyright (c) 2004 Jakub Stachowski <qbast@go2.pl>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _WATCHER_H_
+#define _WATCHER_H_
+
+#include <qstring.h>
+#include <dnssd/servicebrowser.h>
+#include <dnssd/remoteservice.h>
+
+using namespace DNSSD;
+
+class Watcher : public QObject
+{
+Q_OBJECT
+public:
+ Watcher(const QString& type, const QString& domain);
+ ~Watcher();
+
+ unsigned int refcount;
+private:
+ ServiceBrowser* browser;
+ bool updateNeeded;
+ QString m_type;
+ QString m_domain;
+ QValueList<DNSSD::RemoteService::Ptr> removed;
+
+private slots:
+ void serviceRemoved(DNSSD::RemoteService::Ptr srv);
+ void serviceAdded(DNSSD::RemoteService::Ptr);
+ void finished();
+
+};
+
+#endif
diff --git a/kfile-plugins/Makefile.am b/kfile-plugins/Makefile.am
new file mode 100644
index 00000000..d092f6a0
--- /dev/null
+++ b/kfile-plugins/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=torrent
diff --git a/kfile-plugins/RETURNED_ITEMS b/kfile-plugins/RETURNED_ITEMS
new file mode 100644
index 00000000..13a579ca
--- /dev/null
+++ b/kfile-plugins/RETURNED_ITEMS
@@ -0,0 +1,15 @@
+If you make a new plugin, please add the list of returned items to this list.
+
+torrent plugin:
+===========
+
+type tag comment
+==============================================================================
+String name Default file/directory name.
+Int length In Bytes, not the size of the .torrent.
+String announce URL of the tracker server.
+Date creation date Date the .torrent was generated.
+Int NumFiles The number of files the .torrent will download.
+Int piece length The block size used for this torrent. Each block
+ is downloaded and hashed separately.
+String comment
diff --git a/kfile-plugins/torrent/Makefile.am b/kfile-plugins/torrent/Makefile.am
new file mode 100644
index 00000000..9727be90
--- /dev/null
+++ b/kfile-plugins/torrent/Makefile.am
@@ -0,0 +1,22 @@
+## Makefile.am for folder file meta info plugin
+
+INCLUDES = $(all_includes)
+
+# these are the headers for your project
+noinst_HEADERS = kfile_torrent.h bbase.h bdict.h bytetape.h \
+ bstring.h bint.h blist.h
+
+kde_module_LTLIBRARIES = kfile_torrent.la
+
+kfile_torrent_la_SOURCES = bytetape.cpp bint.cpp bstring.cpp blist.cpp bdict.cpp kfile_torrent.cpp
+kfile_torrent_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+kfile_torrent_la_LIBADD = $(LIB_KIO)
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+services_DATA = kfile_torrent.desktop
+servicesdir = $(kde_servicesdir)
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kfile_torrent.pot
diff --git a/kfile-plugins/torrent/README b/kfile-plugins/torrent/README
new file mode 100644
index 00000000..c7147c9d
--- /dev/null
+++ b/kfile-plugins/torrent/README
@@ -0,0 +1,7 @@
+This is a meta information plugin for BitTorrent files (*.torrent).
+
+It doesn't depend on BitTorrent or any non-standard library being installed.
+
+The .torrent files used by BitTorrent are coded in what is called a b-encoding
+by the BitTorrent author. Please see the actual kfile plugin source code for
+more information on the encoding.
diff --git a/kfile-plugins/torrent/bbase.h b/kfile-plugins/torrent/bbase.h
new file mode 100644
index 00000000..19455523
--- /dev/null
+++ b/kfile-plugins/torrent/bbase.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BBASE_H
+#define _BBASE_H
+
+#include <ksharedptr.h>
+
+class QIODevice;
+
+/**
+ * Abstract base class for the b-encoded types. Re-implemented
+ * by BInt, BList, BDict, and BString. Derive from this class in
+ * order to make a new type for the b-encoding suite. Classes
+ * derived from this one should not throw exceptions. Instead,
+ * they should implement isValid(), and allow calling modules to
+ * check error status that way.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see BInt, BList, BDict, BString, KSharedPtr
+ */
+
+// Derive from KShared to enable use of KSharedPtr.
+class BBase : public KShared
+{
+ public:
+
+ /**
+ * Identifies the particular class that has been instantiated. All
+ * subclasses of this class should have an identifier here.
+ */
+ enum classID {
+ bBase, /**< This class is BBase. No subclass should return this. */
+ bString, /**< This class is a BString. */
+ bInt, /**< This class is a BInt. */
+ bList, /**< This class is a BList. */
+ bDict /**< This class is a BDict. (Dictionary/Map) */
+ };
+
+ /**
+ * Returns the type identification of the object. It will
+ * be a value in the classID enum. A subclass of this class
+ * must implement this function.
+ *
+ * @return type identifier of the class
+ * @see classID
+ */
+ virtual classID type_id() const = 0;
+
+ /**
+ * Destructor for the class. This function must be reimplemented
+ * in subclasses.
+ */
+ virtual ~BBase () { ; }
+
+ /**
+ * Returns the validity status of the object. Newly constructed
+ * objects are invalid unless the initialization sequence completed
+ * successfully.
+ *
+ * @return the validity status of the object
+ */
+ virtual bool isValid() const = 0;
+
+ /**
+ * Outputs the b-encoded representation of the object to the given
+ * QIODevice.
+ * @param device the QIODevice to write to
+ * @return true on a successful write, false otherwise
+ */
+ virtual bool writeToDevice (QIODevice &device) = 0;
+};
+
+#endif /* _BBASE_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/kfile-plugins/torrent/bdict.cpp b/kfile-plugins/torrent/bdict.cpp
new file mode 100644
index 00000000..47b3bcad
--- /dev/null
+++ b/kfile-plugins/torrent/bdict.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <qstringlist.h>
+#include <qiodevice.h>
+
+#include <kdebug.h>
+
+#include "bbase.h"
+#include "bdict.h"
+#include "bstring.h"
+#include "bint.h"
+#include "blist.h"
+
+BDict::BDict (QByteArray &dict, int start)
+ : m_map(), m_valid(false)
+{
+ ByteTape tape(dict, start);
+
+ init (tape);
+}
+
+BDict::BDict (ByteTape &tape)
+ : m_map(), m_valid (false)
+{
+ init (tape);
+}
+
+void BDict::init (ByteTape &tape)
+{
+ if (*tape != 'd')
+ {
+ kdDebug(7034) << "This isn't a dictionary!" << endl;
+ return; // This isn't a dictionary
+ }
+
+ tape++;
+
+ // We need to loop and read in a string, then read in some data
+ while (*tape != 'e')
+ {
+ BBase *temp_item = 0;
+
+ // Read in string
+ KSharedPtr<BString> str (new BString (tape));
+
+ // Ensure str will be automatically deleted
+ if (!str || !str->isValid())
+ {
+ kdDebug(7034) << (str ? "Invalid string" : "Unable to read String!") << endl;
+ return;
+ }
+
+ // Read in data
+ switch (*tape)
+ {
+ case 'l':
+ temp_item = new BList (tape);
+ break;
+
+ case 'i':
+ temp_item = new BInt (tape);
+ break;
+
+ case 'd':
+ temp_item = new BDict (tape);
+ break;
+
+ default:
+ // Hopefully this is a string
+ temp_item = new BString (tape);
+ }
+
+ if (!temp_item || !temp_item->isValid())
+ {
+ kdDebug(7034) << (temp_item ? "Invalid item!"
+ : "Unable to create keyed data!") << endl;
+ return;
+ }
+
+ m_map.insert(str->get_string(), temp_item);
+ }
+
+ // Move past the 'e'
+ tape++;
+
+ // Let the map delete the items
+ m_map.setAutoDelete (true);
+
+ // Oh yeah, we're valid now, too. :-)
+ m_valid = true;
+}
+
+BDict::~BDict ()
+{
+ // QDict will take care of deleting each entry that
+ // it holds.
+}
+
+BInt *BDict::findInt (const char *key)
+{
+ BBase *base = find(key);
+
+ if (base && base->type_id() == bInt)
+ return dynamic_cast<BInt*>(base);
+
+ return 0;
+}
+
+BList *BDict::findList (const char *key)
+{
+ BBase *base = find(key);
+
+ if (base && base->type_id() == bList)
+ return dynamic_cast<BList*>(base);
+
+ return 0;
+}
+
+BDict *BDict::findDict (const char *key)
+{
+ BBase *base = find(key);
+
+ if (base && base->type_id() == bDict)
+ return dynamic_cast<BDict*>(base);
+
+ return 0;
+}
+
+BString *BDict::findStr (const char *key)
+{
+ BBase *base = find(key);
+
+ if (base && base->type_id() == bString)
+ return dynamic_cast<BString*>(base);
+
+ return 0;
+}
+
+bool BDict::writeToDevice(QIODevice &device)
+{
+ if (!isValid())
+ return false;
+
+ const char *d_str = "d";
+ const char *e_str = "e";
+ Q_LONG written = 0, result = 0;
+
+ written = device.writeBlock (d_str, 1);
+ while (written < 1)
+ {
+ if (written < 0 || result < 0)
+ return false;
+
+ result = device.writeBlock (d_str, 1);
+ written += result;
+ }
+
+ // Strings are supposed to be written in the dictionary such that
+ // the keys are in sorted order. QDictIterator doesn't support an
+ // ordering, so we have to get a list of all the keys, sort it, and
+ // then go by the list.
+
+ BBaseHashIterator iter (m_map);
+ QStringList key_list;
+
+ for ( ; iter.current(); ++iter)
+ key_list.append(iter.currentKey());
+
+ key_list.sort();
+
+ QStringList::Iterator key_iter;
+ for (key_iter = key_list.begin(); key_iter != key_list.end(); ++key_iter)
+ {
+ QCString utfString = (*key_iter).utf8();
+ QString str = QString("%1:").arg(utfString.size() - 1);
+
+ QCString lenString = str.utf8();
+
+ // Write out length of key
+ device.writeBlock(lenString.data(), lenString.size() - 1);
+
+ // Write out actual key
+ device.writeBlock(utfString.data(), utfString.size() - 1);
+
+ // Write out the key's data
+ BBase *base = m_map.find(*key_iter);
+ if (!base->writeToDevice (device))
+ return false;
+ }
+
+ written = device.writeBlock (e_str, 1);
+ while ((uint) written < 1)
+ {
+ if (written < 0 || result < 0)
+ return false;
+
+ result = device.writeBlock (e_str, 1);
+ written += result;
+ }
+
+ return true;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/kfile-plugins/torrent/bdict.h b/kfile-plugins/torrent/bdict.h
new file mode 100644
index 00000000..34c471c6
--- /dev/null
+++ b/kfile-plugins/torrent/bdict.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BDICT_H
+#define _BDICT_H
+
+#include <qdict.h>
+#include <qcstring.h> // QByteArray
+#include "bytetape.h"
+#include "bbase.h"
+
+// Some useful typedefs
+typedef QDict<BBase> BBaseHash;
+typedef QDictIterator<BBase> BBaseHashIterator;
+
+// Forward declarations
+class BInt;
+class BList;
+class BString;
+
+/**
+ * Class to handle the BitTorrent b-encoded dictionary. It is keyed
+ * using const char *strings, and stores pointers to a class descended
+ * from BBase, such as BInt, BString, BList, or even more BDicts.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see BBase, BInt, BString, BList
+ */
+class BDict : public BBase
+{
+ public:
+
+ /**
+ * Construct a dictionary based on the b-encoded data contained in
+ * @p dict. You need to pass a value for @p start if the b-encoded
+ * dictionary doesn't start at the beginning of @p dict.
+ *
+ * @param dict the buffer containing the b-encoded dictionary
+ * @param start the position of the data within the buffer
+ */
+ BDict (QByteArray &dict, int start = 0);
+
+ /**
+ * Construct a dictionary from a ByteTape. The data and current
+ * position of @p tape will be shared with all objects using it.
+ * @p tape should already be positioned to the b-encoded dictionary.
+ * After construction, @p tape will point to the byte after the
+ * dictionary if successful. If not successful, @p tape will have
+ * an undefined position.
+ *
+ * @param tape the ByteTape to read from
+ * @see ByteTape
+ */
+ BDict (ByteTape &tape);
+
+ /**
+ * Destroys the object and frees all memory allocated to it.
+ */
+ virtual ~BDict();
+
+ /**
+ * Returns the type of this class.
+ *
+ * @return bDict. This value is only returned by this class.
+ */
+ virtual classID type_id() const { return bDict; }
+
+ /**
+ * Returns the number of keyed values contained within this
+ * dictionary.
+ *
+ * @return the number of items in this dictionary
+ */
+ virtual int count() const { return m_map.count(); }
+
+ /**
+ * This function should be called to determine whether the
+ * dictionary was successfully created, since no exceptions
+ * are thrown.
+ *
+ * @return true if this is a valid (possibly empty!) dictionary,
+ * false otherwise
+ */
+ virtual bool isValid() const { return m_valid; }
+
+ /**
+ * This function determines whether or not a value with a
+ * certain key exists in the dictionary. The key is case-sensitive.
+ *
+ * @param key the key to search for a value for
+ * @return true, if there is a value for the @p key in the
+ * dictionary, false otherwise
+ */
+ virtual bool contains (const char *key) { return m_map.find(key) != 0; }
+
+ /**
+ * Returns a pointer to the BBase descendant keyed by @p key. You
+ * can use the type_id() method to determine the type of the
+ * object returned.
+ *
+ * @param key the key to search the dictionary for
+ * @return a pointer to the matching object, or 0 if no object
+ * matches the key
+ * @see BBase
+ */
+ virtual BBase *find (const char *key) { return m_map.find(key); }
+
+ /**
+ * Convienience function to find and return a BInt keyed by @p key.
+ *
+ * @param key the key to find a value for
+ * @return 0 if the key doesn't match a value, or if the value isn't
+ * a BInt. Otherwise, a pointer to the matching BInt is
+ * returned.
+ * @see BInt
+ */
+ BInt* findInt (const char *key);
+
+ /**
+ * Convienience function to find and return a BList keyed by @p key.
+ *
+ * @param key the key to find a value for
+ * @return 0 if the key doesn't match a value, or if the value isn't
+ * a BList. Otherwise, a pointer to the matching BList is
+ * returned.
+ * @see BList
+ */
+ BList* findList (const char *key);
+
+ /**
+ * Convienience function to find and return a BDict keyed by @p key.
+ *
+ * @param key the key to find a value for
+ * @return 0 if the key doesn't match a value, or if the value isn't
+ * a BDict. Otherwise, a pointer to the matching BDict is
+ * returned.
+ * @see BDict
+ */
+ BDict* findDict (const char *key);
+
+ /**
+ * Convienience function to find and return a BString keyed by @p key.
+ *
+ * @param key the key to find a value for
+ * @return 0 if the key doesn't match a value, or if the value isn't
+ * a BString. Otherwise, a pointer to the matching BString is
+ * returned.
+ * @see BString
+ */
+ BString* findStr (const char *key);
+
+ /**
+ * Outputs the b-encoded representation of the object to the given
+ * QIODevice.
+ * @param device the QIODevice to write to
+ * @return true on a successful write, false otherwise
+ */
+ virtual bool writeToDevice (QIODevice &device);
+
+ /**
+ * Returns a QDictIterator<BBase> that you can use to iterate through
+ * the items in the dictionary.
+ *
+ * @return QDictIterator<BBase>, which can be used to iterate through
+ * the items in the dictionary.
+ */
+ BBaseHashIterator iterator() const
+ {
+ return BBaseHashIterator(m_map);
+ }
+
+ private:
+
+ /**
+ * This function handles the actual initialization of the object upon
+ * construction, and set the m_valid flag if successful.
+ *
+ * @param tape the ByteTape to read from
+ */
+ void init (ByteTape &tape);
+
+ BBaseHash m_map; /// The QDict that actually store the data
+ bool m_valid; /// Store initialization status
+};
+
+#endif /* _BDICT_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/kfile-plugins/torrent/bint.cpp b/kfile-plugins/torrent/bint.cpp
new file mode 100644
index 00000000..8f273e86
--- /dev/null
+++ b/kfile-plugins/torrent/bint.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <qstring.h>
+#include <qcstring.h>
+#include <qiodevice.h>
+
+#include "bytetape.h"
+#include "bint.h"
+
+// A bencoded int is (approximately) as follows:
+// i(\d)+e
+BInt::BInt (QByteArray &dict, int start)
+ : m_value (0), m_valid(false)
+{
+ ByteTape tape (dict, start);
+ init (tape);
+}
+
+BInt::BInt (ByteTape &tape)
+ : m_value(0), m_valid(false)
+{
+ init(tape);
+}
+
+void BInt::init (ByteTape &tape)
+{
+ if (*tape != 'i')
+ return;
+
+ tape ++; // Move to start of digits
+
+ QByteArray &dict (tape.data());
+ if (dict.find('e', tape.pos()) == -1)
+ return;
+
+ // Copy the part from the start to the e. The values in-between
+ // should be digits, perhaps preceded by a negative sign.
+ int length = dict.find('e', tape.pos()) - tape.pos();
+ char *ptr = dict.data(); // Get start of buffer
+ ptr += tape.pos(); // Advance to current position in tape
+
+ // Allocate temporary data buffer
+ QByteArray buffer(length + 1);
+
+ qmemmove (buffer.data(), ptr, length);
+ buffer[length] = 0; // Null-terminate
+
+ QString numberString (buffer);
+ bool a_isValid; // We want to make sure the string is a valid number
+
+ m_value = numberString.toLongLong(&a_isValid);
+
+ tape += length; // Move to 'e'
+ tape ++; // Move to next char
+
+ m_valid = a_isValid; // Now we're good, if it was a number
+}
+
+BInt::~BInt()
+{
+ /* Nothing yet */
+}
+
+bool BInt::writeToDevice (QIODevice &device)
+{
+ if (!m_valid)
+ return false;
+
+ /* Write out i234e, and such */
+ QString str = QString("i%1e").
+ arg (m_value);
+
+ Q_LONG written = 0, result = 0;
+ written = device.writeBlock (str.latin1(), str.length());
+ while ((uint) written < str.length())
+ {
+ if (written < 0 || result < 0)
+ return false;
+
+ result = device.writeBlock(str.latin1() + written,
+ str.length() - written);
+ written += result;
+ }
+
+ return true;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/bint.h b/kfile-plugins/torrent/bint.h
new file mode 100644
index 00000000..1d4a4614
--- /dev/null
+++ b/kfile-plugins/torrent/bint.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BINT_H
+#define _BINT_H
+
+#include <qcstring.h>
+#include "bbase.h"
+#include "bytetape.h"
+
+/**
+ * Class to represent a b-encoded integer.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see BBase
+ */
+class BInt : public BBase
+{
+ public:
+
+ /**
+ * Constructs a BInt by reading a b-encoded integer from @p dict.
+ * You can start reading from a position other than the beginning
+ * by passing that position to the constructor.
+ *
+ * @param dict the buffer to read from
+ * @param start the position within the buffer of the beginning
+ * of the b-encoded integer.
+ */
+ BInt (QByteArray &dict, int start = 0);
+
+ /**
+ * Constructs a BInt by reading a b-encoded integer from @p tape.
+ *
+ * @param tape the ByteTape to read from. It should already be
+ * positioned at the beginning of the b-encoded integer data.
+ * After construction, @p tape will point to the byte after
+ * the b-encoded integer on success. If construction was
+ * not successful, @p tape will have an undefined position.
+ */
+ BInt (ByteTape &tape);
+
+ /**
+ * Destructor for this class. No special action is taken.
+ */
+ virtual ~BInt ();
+
+ /**
+ * Returns the integer value of the data used to construct this
+ * object.
+ *
+ * @return this object's integer value
+ */
+ Q_LLONG get_value () const { return m_value; }
+
+ /**
+ * Returns the type of this class.
+ *
+ * @return bInt. This value is only returned by this class.
+ */
+ virtual classID type_id() const { return bInt; }
+
+ /**
+ * This function should be called to determine whether the
+ * integer was successfully created, since no exceptions
+ * are thrown.
+ *
+ * @return true if this is a valid integer, false otherwise
+ */
+ virtual bool isValid() const { return m_valid; }
+
+ /**
+ * Outputs the b-encoded representation of the object to the given
+ * QIODevice.
+ * @param device the QIODevice to write to
+ * @return true on a successful write, false otherwise
+ */
+ virtual bool writeToDevice (QIODevice &device);
+
+ private:
+
+ /**
+ * Initialization function for the class, called to handle the
+ * actual work of reading the b-encoded data from @p tape.
+ *
+ * @param tape the ByteTape to read from
+ */
+ void init(ByteTape &tape);
+
+ Q_LLONG m_value;
+ bool m_valid;
+};
+
+#endif /* _BINT_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/blist.cpp b/kfile-plugins/torrent/blist.cpp
new file mode 100644
index 00000000..1aaa8b7c
--- /dev/null
+++ b/kfile-plugins/torrent/blist.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <qiodevice.h>
+
+#include "bytetape.h"
+#include "blist.h"
+#include "bdict.h"
+#include "bstring.h"
+#include "bint.h"
+
+BList::BList (ByteTape &tape)
+ : m_valid(false), m_array()
+{
+ init (tape);
+}
+
+BList::BList (QByteArray &dict, unsigned int start)
+ : m_valid(false), m_array()
+{
+ ByteTape tape (dict, start);
+
+ init (tape);
+}
+
+void BList::init (ByteTape &tape)
+{
+ BBase *temp;
+
+ if (*tape != 'l')
+ return;
+
+ tape++;
+
+ /* Repeat circling over the string until the list is over */
+ while (*tape != 'e')
+ {
+ switch (*tape)
+ {
+ case 'd':
+ temp = new BDict (tape);
+ break;
+
+ case 'l': /* This element is a list */
+ temp = new BList (tape);
+ break;
+
+ case 'i': /* This element is an int */
+ temp = new BInt (tape);
+ break;
+
+ default: /* Maybe a string? */
+ temp = new BString (tape);
+ }
+
+ if (!temp || !temp->isValid())
+ return; // Invalid list element
+
+ m_array.append (temp);
+ }
+
+ m_valid = true;
+
+ // Only way out is to detect 'e', so we need to increment tape past that.
+ tape++;
+}
+
+BList::~BList()
+{
+ BBaseVectorIterator iter;
+
+ for (iter = begin(); iter != end(); ++iter)
+ delete *iter;
+}
+
+BBase* BList::index (unsigned int i)
+{
+ if (i >= count())
+ return 0;
+ else
+ return m_array[i];
+}
+
+BList * BList::indexList (unsigned int i)
+{
+ BBase *base = index(i);
+
+ if (base && base->type_id() == bList)
+ return dynamic_cast<BList*>(base);
+
+ return 0;
+}
+
+BInt * BList::indexInt (unsigned int i)
+{
+ BBase *base = index(i);
+
+ if (base && base->type_id() == bInt)
+ return dynamic_cast<BInt*>(base);
+
+ return 0;
+}
+
+BDict * BList::indexDict (unsigned int i)
+{
+ BBase *base = index(i);
+
+ if (base && base->type_id() == bDict)
+ return dynamic_cast<BDict*>(base);
+
+ return 0;
+}
+
+BString * BList::indexStr (unsigned int i)
+{
+ BBase *base = index(i);
+
+ if (base && base->type_id() == bString)
+ return dynamic_cast<BString*>(base);
+
+ return 0;
+}
+
+bool BList::writeToDevice(QIODevice &device)
+{
+ if (!m_valid)
+ return false;
+
+ const char *l_str = "l";
+ const char *e_str = "e";
+ Q_LONG written = 0, result = 0;
+
+ written = device.writeBlock (l_str, 1);
+ while (written < 1)
+ {
+ if (written < 0 || result < 0)
+ return false;
+
+ result = device.writeBlock (l_str, 1);
+ written += result;
+ }
+
+ BBaseVectorIterator iter;
+ for (iter = begin(); iter != end(); ++iter)
+ {
+ if (!((*iter)->writeToDevice (device)))
+ return false;
+ }
+
+ written = device.writeBlock (e_str, 1);
+ while (written < 1)
+ {
+ if (written < 0 || result < 0)
+ return false;
+
+ result = device.writeBlock (e_str, 1);
+ written += result;
+ }
+
+ return true;
+}
+
+// vim: set et sw=4 ts=4:
diff --git a/kfile-plugins/torrent/blist.h b/kfile-plugins/torrent/blist.h
new file mode 100644
index 00000000..88a52bc4
--- /dev/null
+++ b/kfile-plugins/torrent/blist.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BLIST_H
+#define _BLIST_H
+
+#include <qvaluelist.h>
+#include <qcstring.h>
+#include "bbase.h"
+#include "bytetape.h"
+
+typedef QValueList<BBase *> BBaseVector;
+typedef QValueList<BBase *>::iterator BBaseVectorIterator;
+
+// Predeclare the following classes
+class BDict;
+class BString;
+class BInt;
+
+/**
+ * Class to construct a list of BBase objects from a b-encoded
+ * list.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see BBase
+ */
+class BList : public BBase
+{
+ public:
+
+ /**
+ * Construct a BList from @p dict.
+ *
+ * @param dict the buffer to read from
+ * @param start the position in the buffer to start
+ * reading from
+ */
+ BList (QByteArray &dict, unsigned int start = 0);
+
+ /**
+ * Construct a BList from @p tape. Any changes made to
+ * @p tape, such as its current position and data, will be
+ * shared with the object that called this constructor. @p tape
+ * should already be positioned at the position to read from.
+ * If construction was successful, @p tape will point to the
+ * byte after the list data. If construction was unsuccessful,
+ * the position of the tape is undefined.
+ *
+ * @param tape the ByteTape to read from.
+ */
+ BList (ByteTape &tape);
+
+ /**
+ * Destroys the list, and deletes all of the items that had
+ * been contained within.
+ */
+ virtual ~BList ();
+
+ /**
+ * Returns the type of this class.
+ *
+ * @return bList. This value is only returned by this class.
+ */
+ virtual classID type_id() const { return bList; }
+
+ /**
+ * Returns the number of items contained within the list.
+ *
+ * @return number of items in the list
+ */
+ virtual unsigned int count() const { return m_array.count(); }
+
+ /**
+ * This function should be called to determine whether the
+ * list was successfully created, since no exceptions
+ * are thrown.
+ *
+ * @return true if this is a valid list, false otherwise
+ */
+ virtual bool isValid() const { return m_valid; }
+
+ /**
+ * This function returns a pointer to the appropriate
+ * item in the list.
+ *
+ * @param i index of the item to return
+ * @return pointer to the appropriate BBase, or 0 if
+ * the index is out-of-bounds
+ */
+ inline BBase * index (unsigned int i);
+
+ /**
+ * Convience function to return a pointer to the appropriate
+ * item in the list, already casted.
+ *
+ * @param i index of the item to return
+ * @return pointer to the appropriate BBase, downcasted to
+ * BList. If the element is <b>not</b> a BList, 0
+ * will be returned instead, even if it was a valid
+ * BBase.
+ */
+ BList * indexList (unsigned int i);
+
+ /**
+ * Convience function to return a pointer to the appropriate
+ * item in the list, already casted.
+ *
+ * @param i index of the item to return
+ * @return pointer to the appropriate BBase, downcasted to
+ * BInt. If the element is <b>not</b> a BInt, 0
+ * will be returned instead, even if it was a valid
+ * BBase.
+ */
+ BInt * indexInt (unsigned int i);
+
+ /**
+ * Convience function to return a pointer to the appropriate
+ * item in the list, already casted.
+ *
+ * @param i index of the item to return
+ * @return pointer to the appropriate BBase, downcasted to
+ * BDict. If the element is <b>not</b> a BDict, 0
+ * will be returned instead, even if it was a valid
+ * BBase.
+ */
+ BDict * indexDict (unsigned int i);
+
+ /**
+ * Convience function to return a pointer to the appropriate
+ * item in the list, already casted.
+ *
+ * @param i index of the item to return
+ * @return pointer to the appropriate BBase, downcasted to
+ * BString. If the element is <b>not</b> a BString, 0
+ * will be returned instead, even if it was a valid
+ * BBase.
+ */
+ BString * indexStr (unsigned int i);
+
+ /**
+ * Returns an iterator to the first element in the list.
+ * There is no particular sorting associated with the list
+ * at this time.
+ *
+ * @return iterator pointing to the beginning of the list
+ * @see QValueList
+ */
+ BBaseVectorIterator begin(void) { return m_array.begin(); }
+
+ /**
+ * Returns an iterator pointing one element past the end of
+ * the list. Although this element belongs to the list,
+ * you should never dereference this iterator. Instead, treat
+ * it as a boundary condition to avoid.
+ *
+ * @return iterator pointing one element past the end of the list
+ * @see QValueList
+ */
+ BBaseVectorIterator end(void) { return m_array.end(); }
+
+ /**
+ * Outputs the b-encoded representation of the object to the given
+ * QIODevice.
+ * @param device the QIODevice to write to
+ * @return true on a successful write, false otherwise
+ */
+ virtual bool writeToDevice (QIODevice &device);
+
+ private:
+
+ /**
+ * This function handles the actual initialization of the object upon
+ * construction, and set the m_valid flag if successful.
+ *
+ * @param tape the ByteTape to read from
+ */
+ void init(ByteTape &tape);
+
+ bool m_valid;
+ BBaseVector m_array;
+};
+
+#endif /* _BLIST_H */
+
+// vim: set et sw=4 ts=4:
diff --git a/kfile-plugins/torrent/bstring.cpp b/kfile-plugins/torrent/bstring.cpp
new file mode 100644
index 00000000..b888af72
--- /dev/null
+++ b/kfile-plugins/torrent/bstring.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <qcstring.h>
+#include <qiodevice.h>
+
+#include <kdebug.h>
+
+#include "bstring.h"
+#include "bytetape.h"
+
+BString::BString (QByteArray &dict, int start) :
+ m_data(), m_valid(false)
+{
+ ByteTape tape (dict, start);
+ init (tape);
+}
+
+BString::BString (ByteTape &tape)
+ : m_data(), m_valid(false)
+{
+ init (tape);
+}
+
+// The reason we don't store stuff in a QString is because BitTorrent
+// b-encoded strings may contain zeroes within the string, which makes
+// a BString more of a buffer than a true string.
+void BString::init (ByteTape &tape)
+{
+ QByteArray &dict(tape.data());
+
+ if (dict.find(':', tape.pos()) == -1)
+ {
+ kdDebug(7034) << "Can't find : for string!" << endl;
+ return;
+ }
+
+ // Copy the part from start to :, as it will be a number
+ // That number is the number of characters to read
+ int length = dict.find(':', tape.pos()) - tape.pos();
+ char *ptr = dict.data();
+
+ ptr += tape.pos();
+
+ QByteArray buffer (length + 1);
+ qmemmove (buffer.data(), ptr, length);
+ buffer[length] = 0;
+
+ QString numberString (buffer);
+ bool a_isValid;
+ ulong len = numberString.toULong (&a_isValid);
+
+ if (!a_isValid)
+ {
+ kdDebug(7034) << "Invalid string length!" << endl;
+ return;
+ }
+
+ // Now that we have the length, we need to advance the tape
+ // past the colon
+ tape += length; // Move to colon
+ if (*tape != ':')
+ {
+ // Sanity check
+ kdError(7034) << "SANITY CHECK FAILED. *tape != ':'!" << endl;
+ return;
+ }
+
+ tape++; // Move past colon
+
+ // Time to copy the data
+ char *textBuffer = tape.at(tape.pos());
+ if (!m_data.resize(len + 1))
+ return;
+
+ qmemmove (m_data.data(), textBuffer, len);
+ m_data[len] = 0; // Null terminate for convienience
+
+ tape += len;
+ m_valid = true;
+}
+
+BString::~BString ()
+{
+}
+
+bool BString::writeToDevice(QIODevice &device)
+{
+ if (!m_valid)
+ return false;
+
+ QString str = QString("%1:").
+ arg(get_len());
+
+ QCString utfString = str.utf8();
+
+ /* Don't write null terminator */
+ device.writeBlock (utfString.data(), utfString.size() - 1);
+
+ // Output the actual data
+ device.writeBlock (m_data.data(), m_data.size() - 1);
+
+ // Done
+ return true;
+}
+
+bool BString::setValue (const QString &str)
+{
+ m_data = str.utf8();
+ return true;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/bstring.h b/kfile-plugins/torrent/bstring.h
new file mode 100644
index 00000000..eb98fef8
--- /dev/null
+++ b/kfile-plugins/torrent/bstring.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BSTRING_H
+#define _BSTRING_H
+
+#include <qstring.h>
+#include <qcstring.h>
+
+#include "bytetape.h"
+#include "bbase.h"
+
+/**
+ * A class to handle the b-encoded strings used by BitTorrent.
+ * It implements BBase, and although the class is referred to
+ * as a string, it can hold arbitrary data, since b-encoded strings
+ * are stored with a length, instead of using a terminator character.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see BBase
+ */
+class BString : public BBase
+{
+ public:
+
+ /**
+ * Construct a BString from @p dict.
+ *
+ * @param dict the buffer to read from
+ * @param start the position in the buffer to start
+ * reading from
+ */
+ BString (QByteArray &dict, int start = 0);
+
+ /**
+ * Construct a BString from @p tape. Any changes made to
+ * @p tape, such as its current position and data, will be
+ * shared with the object that called this constructor. @p tape
+ * should already be positioned at the position to read from.
+ * If construction was successful, @p tape will point to the
+ * byte after the string data. If construction was unsuccessful,
+ * the position of the tape is undefined.
+ *
+ * @param tape the ByteTape to read from.
+ */
+ BString (ByteTape &tape);
+
+ /**
+ * Destroys the BString, and deallocates all memory that had
+ * been used.
+ */
+ virtual ~BString ();
+
+ /**
+ * Returns a QString representation of the data in the
+ * BString. It is the responsibility of the caller to ensure
+ * that the data is convertible to a QString. More specifically,
+ * the data should not contain any embedded NULLs.
+ *
+ * @return QString containing the data from this BString.
+ */
+ QString get_string() const { return QString::fromUtf8(m_data.data()); }
+
+ /**
+ * Returns the amount of data held by the string. It would be
+ * perhaps more appropriate to call this size(), since this is
+ * a buffer, not a true text string.
+ *
+ * @return the size of the string, not including the NULL
+ * terminator.
+ */
+ const int get_len() const { return m_data.size() - 1; }
+
+ /**
+ * Returns the type of this class.
+ *
+ * @return bString. This value is only returned by this class.
+ */
+ virtual classID type_id() const { return bString; }
+
+ /**
+ * This function should be called to determine whether the
+ * string was successfully created, since no exceptions
+ * are thrown.
+ *
+ * @return true if this is a valid string, false otherwise
+ */
+ virtual bool isValid() const { return m_valid; }
+
+ /**
+ * Outputs the b-encoded representation of the object to the given
+ * QIODevice.
+ * @param device the QIODevice to write to
+ * @return true on a successful write, false otherwise
+ */
+ virtual bool writeToDevice (QIODevice &device);
+
+ /**
+ * Changes the value of the string to the given QString.
+ *
+ * @param str the QString containing the new value
+ * @return true if the value was successfully changed,
+ * false otherwise.
+ */
+ bool setValue (const QString &str);
+
+ private:
+
+ /**
+ * This function handles the actual initialization of the object upon
+ * construction, and set the m_valid flag if successful.
+ *
+ * @param tape the ByteTape to read from
+ */
+ void init (ByteTape &tape);
+
+ QByteArray m_data;
+ bool m_valid;
+};
+
+#endif /* _BSTRING_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/bytetape.cpp b/kfile-plugins/torrent/bytetape.cpp
new file mode 100644
index 00000000..f2289293
--- /dev/null
+++ b/kfile-plugins/torrent/bytetape.cpp
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kdebug.h>
+
+#include "bytetape.h"
+
+ByteTape::ByteTape (QByteArray &array, int pos)
+ : m_array(array), m_shared(new ByteTapeShared)
+{
+ m_shared->pos = pos;
+}
+
+ByteTape::ByteTape (const ByteTape &tape)
+ : m_array(tape.m_array), m_shared(tape.m_shared)
+{
+}
+
+ByteTape& ByteTape::operator += (const unsigned int i)
+{
+ m_shared->pos += i;
+ if (m_array.size() <= m_shared->pos)
+ {
+ kdDebug(7034) << "Whoops. Advanced too far." << endl;
+ m_shared->pos = m_array.size() - 1;
+ }
+
+ return *this;
+}
+
+ByteTape& ByteTape::operator -= (const unsigned int i)
+{
+ if (i > m_shared->pos)
+ {
+ kdDebug(7034) << "Whoops, tried to back up too far." << endl;
+ m_shared->pos = 0;
+ }
+ else
+ m_shared->pos -= i;
+
+ return *this;
+}
+
+char ByteTape::operator [] (const unsigned int i)
+{
+ if (i < m_array.size())
+ return m_array[i];
+ else
+ {
+ kdWarning() << "Can't dereference tape at " << i
+ << ", size is " << m_array.size() << endl;
+ return 0;
+ }
+}
+
+char &ByteTape::operator * ()
+{
+ return m_array[m_shared->pos];
+}
+
+// Postfix increment
+ByteTape ByteTape::operator ++ (int)
+{
+ // Can't use copy ctor, as we'll be copying shared data, which defeats
+ // the semantics of the postfix operator.
+ ByteTape temp(m_array, m_shared->pos);
+ m_shared->pos ++;
+
+ if (m_shared->pos >= m_array.size())
+ {
+ m_shared->pos = m_array.size() - 1;
+ kdDebug(7034) << "Tape already at end!" << endl;
+ kdDebug(7034) << "Tape size is " << m_array.size() << endl;
+ }
+
+ return temp;
+}
+
+// Prefix increment
+ByteTape & ByteTape::operator ++()
+{
+ m_shared->pos ++;
+ if (m_shared->pos >= m_array.size())
+ {
+ m_shared->pos = m_array.size() - 1;
+ kdDebug(7034) << "Tape already at end!" << endl;
+ kdDebug(7034) << "Tape size is " << m_array.size() << endl;
+ }
+
+ return *this;
+}
+
+// Postfix decrement
+ByteTape ByteTape::operator -- (int)
+{
+ // Can't use copy ctor, as we'll be copying shared data, which defeats
+ // the semantics of the postfix operator.
+ ByteTape temp(m_array, m_shared->pos);
+
+ if (m_shared->pos != 0)
+ m_shared->pos --;
+ else
+ kdDebug(7034) << "Tape already at beginning!" << endl;
+
+ return temp;
+}
+
+// Prefix decrement
+ByteTape & ByteTape::operator -- ()
+{
+ if (m_shared->pos != 0)
+ m_shared->pos --;
+ else
+ kdDebug(7034) << "Tape already at beginning!" << endl;
+
+ return *this;
+}
+
+bool ByteTape::setPos (unsigned int pos)
+{
+ if (pos >= m_array.size())
+ {
+ kdDebug(7034) << "Can't set tape to " << pos << endl;
+ return false;
+ }
+
+ m_shared->pos = pos;
+ return true;
+}
+
+char* ByteTape::at (const unsigned int i)
+{
+ if (i >= m_array.size())
+ {
+ kdDebug(7034) << "Access to tape at " << i << " out-of-range." << endl;
+ return 0;
+ }
+
+ return m_array.data() + i;
+}
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/bytetape.h b/kfile-plugins/torrent/bytetape.h
new file mode 100644
index 00000000..1d8c8db2
--- /dev/null
+++ b/kfile-plugins/torrent/bytetape.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _BYTETAPE_H
+#define _BYTETAPE_H
+
+#include <ksharedptr.h>
+
+#include <qcstring.h>
+
+class ByteTapeShared : public KShared
+{
+ public:
+
+ unsigned int pos;
+};
+
+/**
+ * Class to simulate a seekable byte stream. Very similar to QByteArray,
+ * but the difference is that this class "knows" what a current position
+ * is. Also, the copy constructor will share the byte stream of the
+ * ByteTape being copied. This means that any copies made of an object of
+ * this class will share BOTH the data and the current position. This is
+ * by design.
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @see QByteArray
+ */
+class ByteTape
+{
+ public:
+
+ /**
+ * Constructs a new ByteTape object from @p array. The
+ * current position will be set to @p pos.
+ *
+ * @param array a data buffer to use for reading and writing.
+ * The tape will be fixed to the size of this buffer.
+ * @param pos the initial position of the tape head. It must be
+ * a position within the buffer contained in @p array.
+ */
+ ByteTape (QByteArray &array, int pos = 0);
+
+ /**
+ * Creates a ByteTape as a copy of @p tape. The newly created
+ * object will share both data and the current position with @p tape.
+ * This is done using reference counts.
+ *
+ * @param tape the ByteTape to copy
+ */
+ ByteTape (const ByteTape &tape);
+
+ /**
+ * Increments the current position by @p i. It is the responsibility
+ * of the function caller to ensure that the new position is in bounds.
+ * The position will be capped to remain in bounds regardless.
+ *
+ * @param i the amount to increment the current position by
+ * @return t reference to the object incremented
+ */
+ ByteTape & operator += (const unsigned int i);
+
+ /**
+ * Decrements the current position by @p i. It is the responsibility
+ * of the function caller to ensure that the new position is in bounds.
+ * Unlike a real Turing machine, attempting to decrement past the
+ * start will be capped to the beginning instead of crashing the
+ * computer.
+ *
+ * @param i the amount to decrement the current position by
+ * @return a reference to the object decremented
+ */
+ ByteTape & operator -= (const unsigned int i);
+
+ /**
+ * Increments the current position by 1. This is a postfix
+ * operator (i++). The current position will be capped to the end
+ * of the buffer.
+ *
+ * @return the object before the increment operation
+ */
+ ByteTape operator ++ (int);
+
+ /**
+ * Increments the current position by 1. This is a prefix
+ * operator (++i). The current position will be capped to the end
+ * of the buffer.
+ *
+ * @return a reference to the object incremented
+ */
+ ByteTape & operator ++ ();
+
+ /**
+ * Decrements the current position by 1. This is a postfix
+ * operator (i--). The current position will be capped to the start
+ * of the buffer.
+ *
+ * @return the object before the decrement operation
+ */
+ ByteTape operator -- (int);
+
+ /**
+ * Decrements the current position by 1. This is a prefix
+ * operator (--i). The current position will be capped to the start
+ * of the buffer.
+ *
+ * @return a reference to the object decremented
+ */
+ ByteTape & operator -- ();
+
+ /**
+ * Returns the byte within the array indexed by @p i. It is
+ * the responsibility of the caller to ensure that @p i is in bounds.
+ * Although out-of-range errors will be detected, no exceptions will
+ * be thrown. Since a reference is not returned, you won't be able
+ * to assign to the result. Example: tape[i] = 'a' will not work.
+ *
+ * The reason a reference isn't returned is because no exceptions are
+ * thrown by this class, and we can't return a reference to an out-of-
+ * bounds character.
+ *
+ * @param i the index of the byte to return
+ * @return the byte at the given index. 0 may be returned on error,
+ * but does not necessarily indicate an error.
+ */
+ char operator [] (const unsigned int i);
+
+ /**
+ * Returns the byte at the tape's current position. You can assign
+ * to the reference returned.
+ *
+ * @return a reference to the byte at the tape head's current position
+ */
+ char &operator * ();
+
+ /**
+ * Returns the position in memory of data at the given index, @p i.
+ * Unlike operator [], this function returns a pointer, so it can be
+ * used to access memory.
+ *
+ * @param i index of the byte to lookup.
+ * @return 0 if @p i is out of range, else the address of memory
+ * at that index
+ */
+ char *at(const unsigned int i);
+
+ /**
+ * Returns the current position of the tape head.
+ *
+ * @return the tape head's current position
+ */
+ unsigned int pos() const { return m_shared->pos; }
+
+ /**
+ * Sets the current position of the tape head to @p pos. If the
+ * position given is out-of-bounds, it will be capped to be within
+ * the array.
+ *
+ * @param pos the new position of the tape head
+ * @return whether the set operation was successful
+ */
+ bool setPos(unsigned int pos);
+
+ /**
+ * Returns a reference to the QByteArray used to hold all the data.
+ *
+ * @return the QByteArray used to hold the data
+ * @see QByteArray
+ */
+ QByteArray &data() { return m_array; }
+
+ private:
+ QByteArray &m_array;
+ KSharedPtr<ByteTapeShared> m_shared;
+};
+
+#endif /* _BYTETAPE_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/kfile-plugins/torrent/kfile_torrent.cpp b/kfile-plugins/torrent/kfile_torrent.cpp
new file mode 100644
index 00000000..08007e11
--- /dev/null
+++ b/kfile-plugins/torrent/kfile_torrent.cpp
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2003, 2004 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qdir.h>
+#include <qstringlist.h>
+
+#include <kmessagebox.h>
+#include <kgenericfactory.h>
+
+#include "kfile_torrent.h"
+#include "bdict.h"
+#include "blist.h"
+#include "bint.h"
+#include "bstring.h"
+
+typedef KGenericFactory<KTorrentPlugin> TorrentFactory;
+K_EXPORT_COMPONENT_FACTORY(kfile_torrent, TorrentFactory("kfile_torrent"))
+
+QStringList filesList (BList *list);
+Q_ULLONG filesLength (BList *list);
+
+KTorrentPlugin::KTorrentPlugin (QObject *parent, const char *name,
+ const QStringList &args)
+ : KFilePlugin (parent, name, args), m_failed(true), m_dict(0)
+{
+ KFileMimeTypeInfo *info = addMimeTypeInfo ("application/x-bittorrent");
+ if (!info)
+ {
+ kdError() << "Error creating application/x-bittorrent mime type info!\n";
+ return;
+ }
+
+ KFileMimeTypeInfo::GroupInfo* group =
+ addGroupInfo (info, "TorrentInfo", i18n("Torrent Information"));
+ if (!group)
+ {
+ kdError() << "Error creating TorrentInfo group!\n";
+ return;
+ }
+ setAttributes (group, KFileMimeTypeInfo::Modifiable);
+
+ KFileMimeTypeInfo::ItemInfo *item = 0;
+
+ item = addItemInfo(group, "name", i18n("Name"), QVariant::String);
+ if (!item)
+ {
+ kdError() << "Error adding Name to group!\n";
+ return;
+ }
+ setHint (item, KFileMimeTypeInfo::Name);
+ setAttributes (item, KFileMimeTypeInfo::Modifiable);
+
+ item = addItemInfo(group, "length", i18n("Torrent Length"), QVariant::ULongLong);
+ if (!item)
+ {
+ kdError() << "Error adding Length to group!\n";
+ return;
+ }
+ setHint (item, KFileMimeTypeInfo::Length);
+ setUnit (item, KFileMimeTypeInfo::Bytes);
+
+ item = addItemInfo(group, "announce", i18n("Tracker URL"), QVariant::String);
+ if (!item)
+ {
+ kdError() << "Error adding Announce to group!\n";
+ return;
+ }
+
+ item = addItemInfo(group, "creation date", i18n("Date Created"), QVariant::DateTime);
+ if (!item)
+ {
+ kdError() << "Error adding DateCreated to group!\n";
+ return;
+ }
+
+ item = addItemInfo(group, "NumFiles", i18n("Number of Files"), QVariant::Int);
+ if (!item)
+ {
+ kdError() << "Error adding NumFiles to group!\n";
+ return;
+ }
+
+ item = addItemInfo(group, "piece length", i18n("File Piece Length"), QVariant::Int);
+ if (!item)
+ {
+ kdError() << "Error adding PieceLength to group!\n";
+ return;
+ }
+ setUnit (item, KFileMimeTypeInfo::Bytes);
+
+ item = addItemInfo(group, "comment", i18n("Comment"), QVariant::String);
+ if (!item)
+ {
+ kdError() << "Error adding Comment to group!\n";
+ return;
+ }
+ setAttributes (item, KFileMimeTypeInfo::MultiLine | KFileMimeTypeInfo::Modifiable);
+
+ m_failed = false;
+}
+
+bool KTorrentPlugin::readInfo (KFileMetaInfo &info, unsigned int)
+{
+ /* Since we don't throw during the ctor, check here whether we actually
+ * are valid are not. If not, die.
+ */
+ if (m_failed)
+ {
+ kdError() << "Construction of KTorrentPlugin failed for " << info.path() << endl;
+ kdError() << "Aborting meta-info read.\n";
+ return false;
+ }
+
+ QFile file (info.path());
+ if (!file.open(IO_ReadOnly))
+ {
+ kdError() << "Unable to open given file!\n";
+ return false;
+ }
+
+ // We need to read in the entire file to parse the dictionary structure.
+ QByteArray buf = file.readAll();
+ file.close();
+
+ if (!buf)
+ {
+ kdError() << "Empty file: " << info.path() << endl;
+ return false;
+ }
+
+ m_dict = new BDict(buf);
+
+ if (!m_dict)
+ {
+ kdError() << "Error creating dictionary from open file: " << info.path() << endl;
+ return false;
+ }
+
+ if (!m_dict->isValid())
+ {
+ kdDebug(7034) << "Invalid torrent file: " << info.path() << endl;
+ return false;
+ }
+
+ KFileMetaInfoGroup group = appendGroup(info, "TorrentInfo");
+
+ // The remainder of this function will consist of a lot of redundancy checks.
+ // If a torrent has a key, but it is of the wrong type, then it isn't a valid
+ // torrent, and so we should just die.
+
+ if (m_dict->contains("announce"))
+ {
+ BString *str = m_dict->findStr ("announce");
+ if (!str)
+ return false;
+ appendItem (group, "announce", QString(str->get_string()));
+ }
+
+ if (m_dict->contains("creation date"))
+ {
+ BInt *the_data = m_dict->findInt ("creation date");
+ QDateTime my_date;
+
+ if (!the_data)
+ return false;
+
+ unsigned int the_time = the_data->get_value();
+
+ /* Hopefully the_time is UTC, because that's what QDateTime does. */
+ my_date.setTime_t (the_time);
+ appendItem (group, "creation date", my_date);
+ }
+
+ // A valid torrent must have the info dict, no reason to check twice for
+ // it.
+ BDict *info_dict = m_dict->findDict("info");
+ int num_files = 1;
+ Q_ULLONG length = 0;
+
+ if (!info_dict)
+ return false;
+
+ if (!info_dict->contains("length"))
+ {
+ /* Has more than one file. The list of files is contained in a
+ * list called, appropriately enough, 'files'
+ */
+ BList *info_list = info_dict->findList("files");
+ if (!info_list)
+ return false;
+
+ num_files = info_list->count();
+ length = filesLength (info_list);
+ }
+ else
+ {
+ /* Only one file, let's put its length */
+ BInt *blength = info_dict->findInt("length");
+ if (!blength)
+ return false;
+
+ length = blength->get_value();
+ }
+
+ appendItem (group, "NumFiles", num_files);
+ appendItem (group, "length", length);
+
+ if (info_dict->contains("name"))
+ {
+ BString *str = info_dict->findStr("name");
+ if (!str)
+ return false;
+
+ QString real_str (str->get_string());
+
+ if (num_files > 1 && !real_str.endsWith("/"))
+ real_str.append('/');
+
+ appendItem (group, "name", real_str);
+ }
+
+ // piece length is required as well
+ BInt *piece_length = info_dict->findInt("piece length");
+ if (!piece_length)
+ return false;
+
+ appendItem (group, "piece length", piece_length->get_value());
+
+ if (m_dict->contains("comment"))
+ {
+ BString *comment = m_dict->findStr("comment");
+ if (!comment)
+ return false;
+
+ appendItem (group, "comment", comment->get_string());
+ }
+ else
+ appendItem (group, "comment", QString());
+
+ return true;
+}
+
+/* Returns a QStringList containing file names within the list. The list
+ * should be the one contained within the info dictionary of the torrent,
+ * keyed by 'files'
+ */
+QStringList filesList (BList *list)
+{
+ QStringList str_list, failList;
+
+ for (unsigned int i = 0; i < list->count(); ++i)
+ {
+ /* Each item in this list is a dictionary, composed as follows:
+ * length -> BInt (size of file)
+ * path -> BList (list of strings)
+ * The list of strings is used to construct directory paths. The
+ * last element of the list is the file name.
+ */
+
+ BDict *list_dict = list->indexDict(i);
+ if (!list_dict)
+ return failList;
+
+ BList *list_path = list_dict->findList("path");
+ if (!list_path)
+ return failList;
+
+ QString str;
+ BString *temp_str;
+
+ if (list_path->count() > 0)
+ {
+ temp_str = list_path->indexStr (0);
+ if (!temp_str)
+ return failList;
+
+ str.append (temp_str->get_string());
+ }
+
+ /* Construct QString consisting of path and file name */
+ for (unsigned int j = 1; j < list_path->count(); ++j)
+ {
+ str.append (QDir::separator());
+ temp_str = list_path->indexStr (j);
+ if (!temp_str)
+ return failList;
+
+ str.append (temp_str->get_string());
+ }
+
+ str_list += str;
+ }
+
+ return str_list;
+}
+
+/* This function determines the total length of a torrent stream.
+ * The list provided should be the same one provided for filesList.
+ */
+Q_ULLONG filesLength (BList *list)
+{
+ Q_ULLONG length = 0;
+
+ for (unsigned int i = 0; i < list->count(); ++i)
+ {
+ /* Each item in this list is a dictionary, composed as follows:
+ * length -> BInt (size of file)
+ * path -> BList (list of strings)
+ */
+
+ BDict *list_dict = list->indexDict(i);
+ if (!list_dict)
+ return 0;
+
+ BInt *bfile_len = list_dict->findInt("length");
+ if (!bfile_len)
+ return 0;
+
+ length += bfile_len->get_value();
+ }
+
+ return length;
+}
+
+bool KTorrentPlugin::writeInfo(const KFileMetaInfo &info) const
+{
+ if (m_failed || !m_dict)
+ return false;
+
+ // The m_dict is ready, all we have to do is open a file, and
+ // let 'er go.
+ QStringList list = info.groups();
+ QStringList::Iterator it = list.begin();
+
+ for (; it != list.end(); ++it)
+ {
+ QStringList list2 = info[*it].keys();
+ QStringList::Iterator it2 = list2.begin();
+
+ for (; it2 != list2.end(); ++it2)
+ {
+ QString key = *it2;
+
+ if (info[*it][key].isModified())
+ {
+ // Re-enter the entry in the dictionary.
+ if (key == "comment")
+ {
+ BString *b_str = m_dict->findStr("comment");
+ if (!b_str)
+ return false;
+
+ b_str->setValue (info[*it][key].value().toString());
+ }
+ else if (key == "name")
+ {
+ BDict *info_dict = m_dict->findDict ("info");
+ if (!info_dict)
+ return false;
+
+ BString *name_str = info_dict->findStr ("name");
+ if (!name_str)
+ return false;
+
+ QString the_name = info[*it][key].value().toString();
+
+ // Remove trailing slashes
+ the_name.replace (QRegExp("/*$"), "");
+
+ name_str->setValue (the_name);
+ }
+ }
+ }
+ }
+
+ QFile output (info.path());
+
+ if (!output.open(IO_WriteOnly | IO_Truncate))
+ return false;
+
+ return m_dict->writeToDevice(output);
+}
+
+#include "kfile_torrent.moc"
+
+// vim: set ts=4 sw=4 et:
diff --git a/kfile-plugins/torrent/kfile_torrent.desktop b/kfile-plugins/torrent/kfile_torrent.desktop
new file mode 100644
index 00000000..854985d4
--- /dev/null
+++ b/kfile-plugins/torrent/kfile_torrent.desktop
@@ -0,0 +1,58 @@
+[Desktop Entry]
+Type=Service
+Name=Bit Torrent Info
+Name[be]=ЗвеÑткі Bit Torrent
+Name[bn]=বিট টোরেনà§à¦Ÿ তথà§à¦¯
+Name[br]=Titouriñ diwar-benn Bit Torrent
+Name[bs]=Bit Torrent informacije
+Name[ca]=Informació de Bit Torrent
+Name[cs]=Bit Torrent informace
+Name[da]=Info om bittorrent
+Name[de]=Bit Torrent Informationen
+Name[el]=ΠληÏοφοÏίες Bit Torrent
+Name[es]=Información Bit Torrent
+Name[et]=Bit Torrenti info
+Name[eu]=Bit-torrent informazioa
+Name[fa]=اطلاعات جریان بیتی
+Name[fi]=Bit Torrent
+Name[fr]=Infos Bit Torrent
+Name[gl]=Información de Bit Torrent
+Name[he]=מידע ביטטורנט
+Name[hu]=Bit Torrent-jellemzők
+Name[is]=Bit Torrent upplýsingar
+Name[it]=Informazioni Bittorrent
+Name[ja]=Bit Torrent 情報
+Name[ka]=Bit Torrent ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ
+Name[kk]=Bit Torrent мәліметі
+Name[km]=áž–áŸážáŸŒáž˜áž¶áž“ Bit Torrent
+Name[lt]=Bit Torrent Informacija
+Name[mk]=Информации за Bit Torrent
+Name[nds]=BitTorrent-Info
+Name[ne]=बिट टोरेनà¥à¤Ÿ सूचना
+Name[nl]=Bittorent-informatie
+Name[nn]=Bit Torrent-info
+Name[pa]=Bit Torrent ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o Bit Torrent
+Name[pt]=Informações Bit Torrent
+Name[pt_BR]=Informações do Bit Torrent
+Name[ro]=Informaţii Bit Torrent
+Name[ru]=Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ BitTorrent
+Name[sk]=Bit Torrent informácie
+Name[sl]=Podatki o BitTorrentu
+Name[sr]=Bit Torrent информације
+Name[sr@Latn]=Bit Torrent informacije
+Name[sv]=Information om Bit Torrent
+Name[ta]=பிட௠டோரà¯à®°à¯†à®£à¯à®Ÿà¯ தகவலà¯
+Name[tr]=Bit Torrent Bilgisi
+Name[uk]=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Bit Torrent
+Name[zh_CN]=BitTorrent ä¿¡æ¯
+Name[zh_HK]=Bit Torrent 資訊
+Name[zh_TW]=Bit Torrent 資訊
+ServiceTypes=KFilePlugin
+X-KDE-Library=kfile_torrent
+# change MimeType here! (example: inode/directory)
+MimeType=application/x-bittorrent
+# change PreferredGroups here! (example: FolderInfo)
+PreferredGroups=
+# change PreferredItems here! (example: Items;Size)
+PreferredItems=
diff --git a/kfile-plugins/torrent/kfile_torrent.h b/kfile-plugins/torrent/kfile_torrent.h
new file mode 100644
index 00000000..3e6d3ec4
--- /dev/null
+++ b/kfile-plugins/torrent/kfile_torrent.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2003, 2004 Michael Pyne <michael.pyne@kdemail.net>
+ *
+ * This software 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; see the file COPYING.
+ * If not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef _KFILE_TORRENT_H
+#define _KFILE_TORRENT_H
+
+#include <kfilemetainfo.h>
+
+#include "bdict.h"
+
+class QStringList;
+
+/**
+ * Class to provide meta info for BitTorrent files within Konqueror.
+ * Handles the mime type application/x-bittorrent, files typically
+ * have the extension .torrent
+ *
+ * @author Michael Pyne <mpyne@grammarian.homelinux.net>
+ * @todo Handle editing the meta info as well
+ * @bug The comment meta info should be Multi Line, but that only
+ * works when the comment can also be edited.
+ */
+class KTorrentPlugin : public KFilePlugin
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructs the class, and prepares for reading info on a torrent.
+ *
+ * @param parent the parent of this object
+ * @param name the name of this object (not user-readable)
+ * @param args unused by this class
+ */
+ KTorrentPlugin (QObject *parent, const char *name, const QStringList &args);
+
+ /**
+ * Destructor that closes the dictionary holding the torrent information.
+ */
+ ~KTorrentPlugin () { delete m_dict; }
+
+ /**
+ * Reads information on a torrent file given by @p info.
+ *
+ * @param info information on the file to decode
+ * @return true if the meta info was successfully detected and added,
+ * false otherwise.
+ */
+ virtual bool readInfo (KFileMetaInfo& info, unsigned int);
+
+ /**
+ * Writes information on a torrent file given by @p info.
+ * BitTorrent are practically nothing but meta information.
+ * Therefore, the entire file might be changed.
+ *
+ * @param info information on the file to encode
+ * @return true if the meta info was successfully updated,
+ * false otherwise
+ */
+ virtual bool writeInfo (const KFileMetaInfo& info) const;
+
+ private:
+ bool m_failed;
+ BDict *m_dict;
+};
+
+#endif /* _KFILE_TORRENT_H */
+
+// vim: set et ts=4 sw=4:
diff --git a/kget/AUTHORS b/kget/AUTHORS
new file mode 100644
index 00000000..c12f6a54
--- /dev/null
+++ b/kget/AUTHORS
@@ -0,0 +1,4 @@
+Patrick Charbonnier <pch@freeshell.og>
+Carsten Pfeiffer <pfeiffer@kde.org>
+Matej Koss
+
diff --git a/kget/Makefile.am b/kget/Makefile.am
new file mode 100644
index 00000000..433ee66d
--- /dev/null
+++ b/kget/Makefile.am
@@ -0,0 +1,50 @@
+SUBDIRS = . pics icons sounds kget_plug_in
+INCLUDES = $(all_includes)
+
+
+bin_PROGRAMS = kget
+
+kget_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kget_LDADD = $(LIB_KFILE) $(LIB_KDEUI) $(LIB_KDECORE) $(LIBSOCKET)
+kget_SOURCES = getfilejob.cpp slaveevent.cpp slave.cpp transferlist.cpp \
+ transfer.cpp settings.cpp logwindow.cpp \
+ kmainwidget.cpp kfileio.cpp droptarget.cpp docking.cpp \
+ dlgconnectionbase.ui dlgautomationbase.ui dlglimitsbase.ui \
+ dlgadvancedbase.ui dlgdirectoriesbase.ui dlgsystembase.ui \
+ dlgSystem.cpp dlgPreferences.cpp dlgLimits.cpp \
+ dlgIndividual.cpp dlgDirectories.cpp dlgConnection.cpp \
+ dlgAutomation.cpp dlgAdvanced.cpp \
+ main.cpp dockindividual.cpp kget_iface.skel safedelete.cpp
+
+noinst_HEADERS = common.h \
+ dlgAdvanced.h dlgAutomation.h dlgConnection.h \
+ dlgDirectories.h dlgIndividual.h dlgLimits.h \
+ dlgPreferences.h dlgSystem.h docking.h droptarget.h \
+ kfileio.h kmainwidget.h logwindow.h settings.h \
+ transfer.h transferlist.h version.h slave.h \
+ slaveevent.h http_defaults.h getfilejob.h dockindividual.h
+
+kget_METASOURCES = AUTO
+
+xdg_apps_DATA = kget.desktop
+
+mimedir = $(kde_mimedir)/application
+mime_DATA =x-kgetlist.desktop
+
+servicemenudir = $(kde_datadir)/konqueror/servicemenus
+servicemenu_DATA = kget_download.desktop
+
+EXTRA_DIST = kget.desktop $(mime_DATA)
+
+rcdir = $(kde_datadir)/kget
+rc_DATA = kgetui.rc
+
+events_DATA = eventsrc
+eventsdir = $(kde_datadir)/kget
+
+KDE_ICON = AUTO
+
+messages: rc.cpp
+ $(EXTRACTRC) *.ui > rc.cpp
+ $(EXTRACTRC) *.rc */*.rc >> rc.cpp
+ $(XGETTEXT) *.cpp */*.cpp *.h -o $(podir)/kget.pot
diff --git a/kget/README b/kget/README
new file mode 100644
index 00000000..f121641b
--- /dev/null
+++ b/kget/README
@@ -0,0 +1,30 @@
+ KGet 0.8.0-RC-1
+ ==========
+
+New working release of kget for kde3.
+
+INSTALLATION
+=============
+checkout the admin directory from kde-common then:
+cd kget
+ln -s kde-common/admin .
+make -f Makefile.cvs
+./configure --disable-debug --enable-mt
+make
+make install
+
+BUGS
+=====
+ Please report all bugs to pch@freeshell.org
+
+TODO
+=====
+ o Download segments
+
+
+Happy downloading
+
+____________
+Patrick Charbonnier
+E-mail: pch@freeshell.org
+
diff --git a/kget/TODO b/kget/TODO
new file mode 100644
index 00000000..2fe91449
--- /dev/null
+++ b/kget/TODO
@@ -0,0 +1,10 @@
+Add a plug-in for konqueror
+Add support for mirrors
+Add bandwidth limiting
+Splits download in 2 segments
+Better icons
+Add Help & Guide
+
+
+
+
diff --git a/kget/common.h b/kget/common.h
new file mode 100644
index 00000000..b436902a
--- /dev/null
+++ b/kget/common.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+* common.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+*
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#ifndef _COMMON_H
+#define _COMMON_H
+
+
+#include <kdebug.h>
+
+#define mDebugIn mDebug << ">>>>Entering "
+#define mDebugOut mDebug << ">>>>Leaving "
+
+#define sDebugIn sDebug << ">>>>Entering "
+#define sDebugOut sDebug << ">>>>Leaving "
+
+
+#define mDebug kdDebug(DKGET)<<k_funcinfo << THREAD_ID
+#define sDebug kdDebug(DKGET)<<k_funcinfo << "|--( GUI )--| "
+#define DKGET 0
+#define THREAD_ID "|--TH_ID ( "<< currentThread()<<" )--| "
+
+
+#define TIME_OUT 35
+#endif // common
diff --git a/kget/cr16-app-kget.png b/kget/cr16-app-kget.png
new file mode 100644
index 00000000..2503410c
--- /dev/null
+++ b/kget/cr16-app-kget.png
Binary files differ
diff --git a/kget/cr16-mime-kget_list.png b/kget/cr16-mime-kget_list.png
new file mode 100644
index 00000000..626cddef
--- /dev/null
+++ b/kget/cr16-mime-kget_list.png
Binary files differ
diff --git a/kget/cr22-app-kget.png b/kget/cr22-app-kget.png
new file mode 100644
index 00000000..0e9c982f
--- /dev/null
+++ b/kget/cr22-app-kget.png
Binary files differ
diff --git a/kget/cr22-mime-kget_list.png b/kget/cr22-mime-kget_list.png
new file mode 100644
index 00000000..cada0ffe
--- /dev/null
+++ b/kget/cr22-mime-kget_list.png
Binary files differ
diff --git a/kget/cr32-app-kget.png b/kget/cr32-app-kget.png
new file mode 100644
index 00000000..96ef6794
--- /dev/null
+++ b/kget/cr32-app-kget.png
Binary files differ
diff --git a/kget/cr32-mime-kget_list.png b/kget/cr32-mime-kget_list.png
new file mode 100644
index 00000000..6ead9cfe
--- /dev/null
+++ b/kget/cr32-mime-kget_list.png
Binary files differ
diff --git a/kget/cr48-app-kget.png b/kget/cr48-app-kget.png
new file mode 100644
index 00000000..ef0d08a5
--- /dev/null
+++ b/kget/cr48-app-kget.png
Binary files differ
diff --git a/kget/cr48-mime-kget_list.png b/kget/cr48-mime-kget_list.png
new file mode 100644
index 00000000..5cbf505d
--- /dev/null
+++ b/kget/cr48-mime-kget_list.png
Binary files differ
diff --git a/kget/dlgAdvanced.cpp b/kget/dlgAdvanced.cpp
new file mode 100644
index 00000000..43b0265c
--- /dev/null
+++ b/kget/dlgAdvanced.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+* dlgAdvanced.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+
+#include <kprotocolmanager.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+
+#include "settings.h"
+#include "kmainwidget.h"
+#include "dlgAdvanced.h"
+
+
+
+DlgAdvanced::DlgAdvanced(QWidget * parent)
+ : DlgAdvancedBase(parent)
+{
+ cb_partial->hide();
+}
+
+
+void
+DlgAdvanced::setData()
+{
+ rb_queued->setChecked(ksettings.b_addQueued);
+ rb_delayed->setChecked(!ksettings.b_addQueued);
+ cb_iconify->setEnabled(ksettings.b_showIndividual);
+ cb_individual->setChecked(ksettings.b_showIndividual);
+ cb_iconify->setChecked(ksettings.b_iconifyIndividual);
+ cb_advanced->setChecked(ksettings.b_advancedIndividual);
+ cb_remove->setChecked(ksettings.b_removeOnSuccess);
+ cb_getsizes->setChecked(ksettings.b_getSizes);
+ cb_expertmode->setChecked(ksettings.b_expertMode);
+ cb_partial->setChecked(KProtocolManager::markPartial());
+ cb_konqiIntegration->setChecked(ksettings.b_KonquerorIntegration);
+ cb_ShowMain->setChecked(ksettings.b_showMain);
+}
+
+
+void DlgAdvanced::applyData()
+{
+ ksettings.b_addQueued = rb_queued->isChecked();
+ ksettings.b_showIndividual = cb_individual->isChecked();
+ ksettings.b_iconifyIndividual = cb_iconify->isChecked();
+ ksettings.b_advancedIndividual = cb_advanced->isChecked();
+ ksettings.b_removeOnSuccess = cb_remove->isChecked();
+ ksettings.b_getSizes = cb_getsizes->isChecked();
+ ksettings.b_showMain=cb_ShowMain->isChecked();
+
+ if (ksettings.b_expertMode != cb_expertmode->isChecked()) {
+ kmain->slotToggleExpertMode();
+ }
+
+ bool bIsKonquiEnable=cb_konqiIntegration->isChecked();
+
+ if (ksettings.b_KonquerorIntegration!=bIsKonquiEnable)
+ {
+ ksettings.b_KonquerorIntegration=!ksettings.b_KonquerorIntegration;
+ KConfig cfg("konquerorrc", false, false);
+ cfg.setGroup("HTML Settings");
+ cfg.writePathEntry("DownloadManager",QString((bIsKonquiEnable)?"kget":""));
+ cfg.sync();
+ }
+}
+
+void DlgAdvanced::slotChanged()
+{
+ emit configChanged();
+}
+
+#include "dlgAdvanced.moc"
diff --git a/kget/dlgAdvanced.h b/kget/dlgAdvanced.h
new file mode 100644
index 00000000..5af58c2a
--- /dev/null
+++ b/kget/dlgAdvanced.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+* dlgAdvanced.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+#ifndef _DLGADVANCED_H
+#define _DLGADVANCED_H
+
+#include "dlgadvancedbase.h"
+
+class DlgAdvanced : public DlgAdvancedBase
+{
+
+Q_OBJECT
+
+public:
+
+ DlgAdvanced(QWidget * parent);
+ ~DlgAdvanced() {}
+
+ void applyData();
+ void setData();
+
+protected slots:
+ void slotChanged();
+
+signals:
+ void configChanged();
+};
+
+#endif // _DLGADVANCED_H
diff --git a/kget/dlgAutomation.cpp b/kget/dlgAutomation.cpp
new file mode 100644
index 00000000..98ed4f3e
--- /dev/null
+++ b/kget/dlgAutomation.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+* dlgAutomation.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qgroupbox.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kdatetimewidget.h>
+#include <knuminput.h>
+#include "common.h"
+
+#include <kdialog.h>
+#include <klineedit.h>
+
+#include "settings.h"
+#include "kmainwidget.h"
+#include "dlgAutomation.h"
+
+
+DlgAutomation::DlgAutomation(QWidget * parent)
+ : DlgAutomationBase(parent)
+{
+}
+
+
+void DlgAutomation::disconnectToggled(bool flag)
+{
+ le_autoDisconnect->setEnabled(flag);
+ cb_timedDisconnect->setEnabled(flag);
+ if (cb_timedDisconnect->isChecked()) {
+ spins->setEnabled(flag);
+ }
+}
+
+
+void DlgAutomation::slotTypeChanged(int type)
+{
+ if (type == PERMANENT) {
+ disconnectToggled(false);
+ cb_autoDisconnect->setEnabled(false);
+ cb_autoDisconnect->setChecked(false);
+ cb_timedDisconnect->setChecked(false);
+ } else {
+ cb_autoDisconnect->setEnabled(true);
+ }
+}
+
+
+void DlgAutomation::setData()
+{
+ // auto save
+ le_autoSave->setEnabled(ksettings.b_autoSave);
+ cb_autoSave->setChecked(ksettings.b_autoSave);
+
+ le_autoSave->setValue(ksettings.autoSaveInterval);
+
+ // auto disconnect
+ le_autoDisconnect->setEnabled(ksettings.b_autoDisconnect);
+ cb_timedDisconnect->setEnabled(ksettings.b_autoDisconnect);
+ spins->setEnabled(ksettings.b_autoDisconnect);
+ cb_autoDisconnect->setChecked(ksettings.b_autoDisconnect);
+ le_autoDisconnect->setText(ksettings.disconnectCommand);
+
+ cb_timedDisconnect->setChecked(ksettings.b_timedDisconnect);
+ spins->setEnabled(ksettings.b_timedDisconnect);
+
+ disconnectDateTime.setDate(ksettings.disconnectDate);
+ disconnectDateTime.setTime(ksettings.disconnectTime);
+ spins->setDateTime(disconnectDateTime);
+
+ // auto shutdown
+ cb_autoShutdown->setChecked(ksettings.b_autoShutdown);
+
+ // auto paste
+ cb_autoPaste->setChecked(ksettings.b_autoPaste);
+}
+
+
+void DlgAutomation::applyData()
+{
+ if (cb_autoSave->isChecked() != ksettings.b_autoSave || (uint) le_autoSave->value() != ksettings.autoSaveInterval) {
+ ksettings.b_autoSave = cb_autoSave->isChecked();
+ ksettings.autoSaveInterval = le_autoSave->value();
+ kmain->setAutoSave();
+ }
+
+ if (cb_autoDisconnect->isChecked() != ksettings.b_autoDisconnect) {
+ kmain->slotToggleAutoDisconnect();
+ kmain->setAutoDisconnect();
+ }
+
+ ksettings.disconnectCommand = le_autoDisconnect->text();
+ ksettings.b_timedDisconnect = cb_timedDisconnect->isChecked();
+ ksettings.disconnectDate = spins->dateTime().date();
+ ksettings.disconnectTime = spins->dateTime().time();
+
+ if (cb_autoShutdown->isChecked() != ksettings.b_autoShutdown) {
+ kmain->slotToggleAutoShutdown();
+ }
+
+ if (cb_autoPaste->isChecked() != ksettings.b_autoPaste) {
+ kmain->slotToggleAutoPaste();
+ }
+}
+
+void DlgAutomation::slotChanged()
+{
+ emit configChanged();
+}
+#include "dlgAutomation.moc"
diff --git a/kget/dlgAutomation.h b/kget/dlgAutomation.h
new file mode 100644
index 00000000..e9f629bc
--- /dev/null
+++ b/kget/dlgAutomation.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+* dlgAutomation.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _DLGAUTOMATION_H
+#define _DLGAUTOMATION_H
+
+#include <qstringlist.h>
+#include <qdatetime.h>
+
+#include "dlgautomationbase.h"
+
+class DlgAutomation:public DlgAutomationBase
+{
+
+Q_OBJECT public:
+
+ DlgAutomation(QWidget * parent);
+ ~DlgAutomation() {}
+ void applyData();
+ void setData();
+
+private:
+
+ QDateTime disconnectDateTime;
+
+signals:
+ void configChanged();
+
+public slots:
+ void slotTypeChanged(int);
+
+protected slots:
+ void disconnectToggled(bool);
+ void slotChanged();
+};
+
+#endif // _DLGAUTOMATION_H
diff --git a/kget/dlgConnection.cpp b/kget/dlgConnection.cpp
new file mode 100644
index 00000000..c5ffae56
--- /dev/null
+++ b/kget/dlgConnection.cpp
@@ -0,0 +1,137 @@
+/***************************************************************************
+* dlgConnection.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kprotocolmanager.h>
+#include <kdialog.h>
+#include <knuminput.h>
+#include <kcombobox.h>
+
+
+#include "kmainwidget.h"
+#include "settings.h"
+#include "dlgConnection.h"
+
+
+DlgConnection::DlgConnection(QWidget * parent)
+ :DlgConnectionBase(parent, "", 0)
+{
+ // TODO: these are not supported yet, so hide them
+ gb_timeout->hide();
+ le_nodata->hide();
+ le_noresume->hide();
+}
+
+
+void DlgConnection::comboActivated(int Index)
+{
+ if (Index == 0) {
+ lb_linknum->setEnabled(false);
+ le_linknum->setEnabled(false);
+ cb_offlinemode->setEnabled(true);
+ } else {
+ lb_linknum->setEnabled(true);
+ le_linknum->setEnabled(true);
+ cb_offlinemode->setEnabled(false);
+ cb_offlinemode->setChecked(false);
+ }
+
+ emit typeChanged(Index);
+}
+
+
+void DlgConnection::setData()
+{
+ lb_after->setEnabled(ksettings.b_reconnectOnError);
+ le_after->setEnabled(ksettings.b_reconnectOnError);
+// lb_retries->setEnabled(ksettings.b_reconnectOnError);
+// le_retries->setEnabled(ksettings.b_reconnectOnError);
+ cb_onerror->setChecked(ksettings.b_reconnectOnError);
+
+ le_after->setValue(ksettings.reconnectTime);
+ le_retries->setValue(ksettings.reconnectRetries);
+
+ cb_onbroken->setChecked(ksettings.b_reconnectOnBroken);
+
+// cb_autoresume->setChecked(KProtocolManager::autoResume());
+
+ le_nodata->setValue(ksettings.timeoutData);
+ le_noresume->setValue(ksettings.timeoutDataNoResume);
+
+ cmb_type->setCurrentItem(ksettings.connectionType);
+
+ if (cmb_type->currentItem() == 0) {
+ le_linknum->setValue(0);
+ lb_linknum->setEnabled(false);
+ le_linknum->setEnabled(false);
+ } else {
+ le_linknum->setValue(ksettings.linkNumber);
+ lb_linknum->setEnabled(true);
+ le_linknum->setEnabled(true);
+ }
+
+ cb_offlinemode->setChecked(ksettings.b_offlineMode);
+ if (ksettings.connectionType == 0)
+ cb_offlinemode->setChecked(ksettings.b_offlineMode);
+ else {
+ cb_offlinemode->setEnabled(false);
+ cb_offlinemode->setChecked(false);
+ }
+}
+
+
+void DlgConnection::applyData()
+{
+ ksettings.b_reconnectOnError = cb_onerror->isChecked();
+ ksettings.reconnectTime = le_after->value();
+ ksettings.reconnectRetries = le_retries->value();
+ ksettings.b_reconnectOnBroken = cb_onbroken->isChecked();
+
+ // KProtocolManager::setAutoResume(cb_autoresume->isChecked());
+
+ ksettings.timeoutData = le_nodata->value();
+ ksettings.timeoutDataNoResume = le_noresume->value();
+
+ ksettings.connectionType = cmb_type->currentItem();
+ ksettings.linkNumber = le_linknum->value();
+
+ if (cb_offlinemode->isChecked() != ksettings.b_offlineMode) {
+ kmain->slotToggleOfflineMode();
+ }
+}
+
+void DlgConnection::slotChanged()
+{
+ emit configChanged();
+}
+
+#include "dlgConnection.moc"
diff --git a/kget/dlgConnection.h b/kget/dlgConnection.h
new file mode 100644
index 00000000..0420b4f0
--- /dev/null
+++ b/kget/dlgConnection.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+* dlgConnection.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _DLGCONNECTION_H
+#define _DLGCONNECTION_H
+
+#include <qstringlist.h>
+
+#include "dlgconnectionbase.h"
+
+class DlgConnection : public DlgConnectionBase
+{
+
+Q_OBJECT public:
+
+ DlgConnection(QWidget * parent);
+ ~DlgConnection() {}
+ void applyData();
+ void setData();
+
+ int type() const
+ {
+ return cmb_type->currentItem();
+ }
+
+signals:
+ void typeChanged(int type);
+ void configChanged();
+
+protected slots:
+ void comboActivated(int Index);
+ void slotChanged();
+};
+
+#endif // _DLGCONNECTION_H
diff --git a/kget/dlgDirectories.cpp b/kget/dlgDirectories.cpp
new file mode 100644
index 00000000..6c4c42e3
--- /dev/null
+++ b/kget/dlgDirectories.cpp
@@ -0,0 +1,203 @@
+/***************************************************************************
+* dlgDirectories.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qpushbutton.h>
+#include <qtoolbutton.h>
+#include <qlistview.h>
+
+#include <qdir.h>
+
+#include <kfiledialog.h>
+#include <klineedit.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+
+#include "settings.h"
+#include "dlgDirectories.h"
+#include <kapplication.h>
+
+DlgDirectories::DlgDirectories(QWidget * parent)
+ : DlgDirectoriesBase(parent)
+{
+ connect( le_ext, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotDirectoryChanged( ) ) );
+ connect( le_dir, SIGNAL( textChanged ( const QString & ) ), this, SLOT( slotDirectoryChanged( ) ) );
+
+ le_dir->setMode( KFile::Directory );
+ lv_entries->setSortColumn( -1 );
+
+ slotDirectoryChanged();
+}
+
+void DlgDirectories::slotDirectoryChanged( )
+{
+ pb_add->setEnabled(!le_ext->text().isEmpty() &&!le_dir->url().isEmpty() );
+}
+
+void DlgDirectories::selectEntry(QListViewItem * item)
+{
+ if (item) {
+ le_ext->setText(item->text(0));
+ le_dir->setURL(item->text(1));
+
+ } else {
+ le_ext->clear();
+ le_dir->clear();
+ }
+ updateUpDown();
+}
+
+
+void DlgDirectories::updateUpDown()
+{
+ QListViewItem *item = lv_entries->selectedItem();
+
+ pb_up->setEnabled( item && item->itemAbove() );
+ pb_down->setEnabled( item && item->itemBelow() );
+}
+
+void DlgDirectories::addEntry()
+{
+ QString ext = le_ext->text();
+ QString dir = le_dir->url();
+
+ if (ext.contains(",") || dir.contains(",") || ext.isEmpty() || dir.isEmpty()) {
+ KMessageBox::error(this, i18n("Each row consists of exactly one\nextension type and one folder."), i18n("Error"));
+ return;
+ }
+
+ QDir f(dir);
+
+ if (!f.exists()) {
+ KMessageBox::error(this, i18n("Folder does not exist:\n%1").arg(dir), i18n("Error"));
+ return;
+ }
+
+ new QListViewItem(lv_entries, ext, dir);
+ updateUpDown();
+
+ emit configChanged();
+}
+
+
+void DlgDirectories::deleteEntry()
+{
+ QListViewItem *item = lv_entries->selectedItem();
+ delete item;
+ updateUpDown();
+ emit configChanged();
+}
+
+
+void DlgDirectories::changeEntry()
+{
+ QListViewItem *old_item = lv_entries->selectedItem();
+
+ if (old_item) {
+ QString ext = le_ext->text();
+ QString dir = le_dir->url();
+
+ if (ext.contains(",") || dir.contains(",") || ext.isEmpty() || dir.isEmpty()) {
+ KMessageBox::error(this, i18n("Each row consists of exactly one\nextension type and one folder."), i18n("Error"));
+ return;
+ }
+
+ QDir f(dir);
+
+ if (!f.exists()) {
+ KMessageBox::error(this, i18n("Folder does not exist:\n%1").arg(dir), i18n("Error"));
+ return;
+ }
+
+ new QListViewItem(lv_entries, old_item, ext, dir);
+ delete old_item;
+ emit configChanged();
+ }
+}
+
+
+void DlgDirectories::downEntry()
+{
+ QListViewItem *item = lv_entries->selectedItem();
+
+ if ( !item )
+ return;
+
+ item->moveItem( item->itemBelow() );
+
+ updateUpDown();
+ emit configChanged();
+}
+
+
+void DlgDirectories::upEntry()
+{
+ QListViewItem *item = lv_entries->selectedItem();
+
+ if ( !item || !item->itemAbove() )
+ return;
+
+ item->moveItem( item->itemAbove()->itemAbove() );
+
+ updateUpDown();
+ emit configChanged();
+}
+
+
+void DlgDirectories::setData()
+{
+ DirList::Iterator it;
+
+ if (ksettings.defaultDirList.count() > 0) {
+ // we need to insert items in the reverse order
+ // because "new QListViewItem" puts itself at the beginning
+ for (it = ksettings.defaultDirList.fromLast(); it != ksettings.defaultDirList.begin(); it--) {
+ new QListViewItem(lv_entries, (*it).extRegexp, (*it).defaultDir);
+ }
+ new QListViewItem(lv_entries, (*it).extRegexp, (*it).defaultDir);
+ }
+}
+
+
+void DlgDirectories::applyData()
+{
+ ksettings.defaultDirList.clear();
+ QListViewItemIterator it(lv_entries);
+
+ for (; it.current(); ++it) {
+ QListViewItem *item = it.current();
+
+ DirItem ditem;
+
+ ditem.extRegexp = item->text(0);
+ ditem.defaultDir = item->text(1);
+ ksettings.defaultDirList.append(ditem);
+ }
+}
+
+#include "dlgDirectories.moc"
diff --git a/kget/dlgDirectories.h b/kget/dlgDirectories.h
new file mode 100644
index 00000000..deb403e3
--- /dev/null
+++ b/kget/dlgDirectories.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+* dlgDirectories.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _DLGDIRECTORIES_H
+#define _DLGDIRECTORIES_H
+
+#include "dlgdirectoriesbase.h"
+
+class DlgDirectories : public DlgDirectoriesBase
+{
+
+Q_OBJECT
+
+public:
+
+ DlgDirectories(QWidget * parent);
+ ~DlgDirectories() {}
+ void applyData();
+ void setData();
+
+signals:
+ void configChanged();
+
+protected slots:
+ void selectEntry(QListViewItem * item);
+ void addEntry();
+ void deleteEntry();
+ void changeEntry();
+
+ void upEntry();
+ void downEntry();
+ void slotDirectoryChanged( );
+protected:
+ void updateUpDown();
+
+};
+
+#endif // _DLGDIRECTORIES_H
diff --git a/kget/dlgIndividual.cpp b/kget/dlgIndividual.cpp
new file mode 100644
index 00000000..72fc6c7d
--- /dev/null
+++ b/kget/dlgIndividual.cpp
@@ -0,0 +1,379 @@
+/***************************************************************************
+* dlgIndividual.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qlayout.h>
+
+#include "dockindividual.h"
+
+#include <qlabel.h>
+#include <qtabwidget.h>
+#include <kprogress.h>
+#include <qtextedit.h>
+#include <qcheckbox.h>
+#include <kpushbutton.h>
+#include <ksqueezedtextlabel.h>
+#include <kdatetimewidget.h>
+
+#include <kapplication.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <ktoolbar.h>
+#include <krun.h>
+#include <kio/global.h>
+#include "common.h"
+
+#include "transfer.h"
+
+#include "settings.h"
+#include "dlgIndividual.h"
+
+
+DlgIndividual::DlgIndividual(Transfer * _item)
+ : QWidget(0, "dialog", WDestructiveClose)
+{
+ item = _item;
+
+ //create dock
+ m_pDockIndividual =new DockIndividual(this);
+
+
+ // Actions
+
+ m_paDock = new KToggleAction(i18n("&Dock"),"tool_dock.png", 0, this, SLOT(slotToggleDock()), this, "dockIndividual");
+
+
+ QVBoxLayout *topLayout = new QVBoxLayout( this, KDialog::marginHint(),KDialog::spacingHint() );
+ topLayout->addStrut( 360 ); // makes dlg at least that wide
+
+ QGridLayout *grid = new QGridLayout( 2, 3 );
+ topLayout->addLayout(grid);
+ grid->addColSpacing(1, KDialog::spacingHint());
+
+ grid->addWidget(new QLabel(i18n("Source:"), this), 0, 0);
+
+ sourceLabel = new KSqueezedTextLabel(this);
+ grid->addWidget(sourceLabel, 0, 2);
+ sourceLabel->setText(i18n("Source Label"));
+ grid->addWidget(new QLabel(i18n("Destination:"), this), 1, 0);
+
+ destLabel = new KSqueezedTextLabel(this);
+ grid->addWidget(destLabel, 1, 2);
+ destLabel->setText(i18n("Source Label"));
+
+ m_pProgressBar = new KProgress(this);
+ topLayout->addWidget( m_pProgressBar );
+
+ // processed info
+ QHBoxLayout *hBox = new QHBoxLayout();
+ topLayout->addLayout(hBox);
+
+ sizeLabel = new QLabel(this);
+ hBox->addWidget(sizeLabel);
+ resumeLabel = new QLabel(this);
+ hBox->addWidget(resumeLabel);
+
+ speedLabel = new QLabel(this);
+ speedLabel->setText(i18n("0 B/s"));
+ topLayout->addWidget(speedLabel);
+
+ // setup toolbar
+ KToolBar *toolBar = new KToolBar(this);
+ toolBar->setIconText(KToolBar::IconOnly);
+ toolBar->setBarPos(KToolBar::Bottom);
+ toolBar->setMovingEnabled(false);
+ toolBar->setFlat(true);
+
+ topLayout->addWidget( toolBar );
+
+ // insert toolbar actions
+ item->m_paResume->plug(toolBar);
+ item->m_paPause->plug(toolBar);
+ item->m_paDelete->plug(toolBar);
+
+ toolBar->insertLineSeparator();
+
+ item->m_paQueue->plug(toolBar);
+ item->m_paTimer->plug(toolBar);
+ item->m_paDelay->plug(toolBar);
+
+ toolBar->insertLineSeparator();
+ m_paDock->plug(toolBar);
+
+
+
+ QCheckBox * keepOpen = new QCheckBox( i18n("&Keep this window open after the operation is complete."), this);
+ connect( keepOpen, SIGNAL( toggled(bool) ), SLOT( slotKeepOpenToggled(bool) ) );
+ topLayout->addWidget(keepOpen);
+
+ QFrame *line3 = new QFrame( this );
+ line3->setFrameShape( QFrame::HLine );
+ line3->setFrameShadow( QFrame::Sunken );
+ topLayout->addWidget( line3 );
+
+ hBox = new QHBoxLayout();
+ topLayout->addLayout(hBox);
+
+ openFile = new KPushButton( i18n("Open &File"), this );
+ connect( openFile, SIGNAL( clicked() ), SLOT( slotOpenFile() ) );
+ hBox->addWidget( openFile );
+ openFile->setEnabled(false);
+
+ openLocation = new KPushButton( i18n("Open &Destination"), this );
+ connect( openLocation, SIGNAL( clicked() ), SLOT( slotOpenLocation() ) );
+ hBox->addWidget( openLocation );
+
+ hBox->addStretch(1);
+
+ pbAdvanced = new KPushButton( i18n("Advanced"), this );
+
+ connect(pbAdvanced, SIGNAL(clicked()), SLOT(slotToggleAdvanced()));
+
+ hBox->addWidget( pbAdvanced );
+
+
+ // setup tab dialog
+ panelAdvanced = new QTabWidget(this);
+
+ // if the time was already set somewhere in the future, keep it
+ // otherwise set it to the current time
+ QDateTime dt;
+
+ if (item->getStartTime() < QDateTime::currentDateTime() && item->getMode() != Transfer::MD_SCHEDULED)
+ {
+ dt = QDateTime::currentDateTime();
+ }
+ else
+ {
+ dt = item->getStartTime();
+ }
+
+ spins = new KDateTimeWidget(dt, this, "spins");
+
+ panelAdvanced->addTab(spins, i18n("Timer"));
+ panelAdvanced->hide();
+
+ connect(spins, SIGNAL(valueChanged(const QDateTime &)), item, SLOT(slotStartTime(const QDateTime &)));
+
+ // adding item log
+ ml_log = new QTextEdit(panelAdvanced);
+ ml_log->setTextFormat(LogText);
+ ml_log->setReadOnly(true);
+ // ml_log->setFixedSize(sizeHint());
+ ml_log->setVScrollBarMode(QScrollView::Auto);
+ ml_log->setWordWrap(QTextEdit::NoWrap);
+
+ // ml_log->setSizePolicy(policy);
+
+ panelAdvanced->addTab(ml_log, i18n("Log"));
+ // panelAdvanced->setFixedSize(sizeHint());
+
+
+
+ topLayout->addWidget(panelAdvanced);
+ advanced = ksettings.b_advancedIndividual;
+ slotToggleAdvanced();
+
+ resize( minimumSizeHint() );
+ setMaximumHeight( height() );
+
+ //bool keepOpenChecked = false;
+ //bool noCaptionYet = true;
+ setCaption(i18n("Progress Dialog"));
+
+ bKeepDlgOpen=false;
+}
+
+
+void DlgIndividual::setTotalSize(KIO::filesize_t bytes)
+{
+ m_iTotalSize = bytes;
+}
+
+
+void DlgIndividual::setPercent(unsigned long percent)
+{
+ m_pProgressBar->setValue(percent);
+ m_pDockIndividual->setValue(percent);
+ setCaption(i18n("%1% of %2 - %3").arg(percent).arg(KIO::convertSize(m_iTotalSize)).arg(m_location.fileName()));
+}
+
+
+void DlgIndividual::setProcessedSize(KIO::filesize_t bytes)
+{
+ sizeLabel->setText(i18n("%1 of %2").arg(KIO::convertSize(bytes)).arg(KIO::convertSize(m_iTotalSize)));
+}
+
+
+void DlgIndividual::setSpeed(QString speed)
+{
+ speedLabel->setText(speed);
+ m_pDockIndividual->setTip(speed);
+}
+
+
+void DlgIndividual::setCopying(const KURL & from, const KURL & to)
+{
+ m_location=to;
+ setCaption(m_location.fileName());
+
+ sourceLabel->setText(from.prettyURL());
+ destLabel->setText(to.prettyURL());
+}
+
+
+void DlgIndividual::setCanResume(bool resume)
+{
+ if (resume)
+ resumeLabel->setText(i18n("Resumed"));
+ else
+ resumeLabel->setText(i18n("Not resumed"));
+}
+
+//void DlgIndividual::slotToggleAdvanced(bool advanced)
+void DlgIndividual::slotToggleAdvanced()
+{
+#ifdef _DEBUG
+ sDebugIn<<endl;
+#endif
+
+ if (advanced)
+ panelAdvanced->show();
+ else
+ {
+ panelAdvanced->hide();
+ adjustSize();
+ }
+ advanced = !advanced;
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+void DlgIndividual::slotToggleDock()
+{
+#ifdef _DEBUG
+ sDebugIn<<endl;
+#endif
+
+ if (m_paDock->isChecked())
+ {
+ m_pDockIndividual->show();
+ hide();
+ }
+ else
+ m_pDockIndividual->hide();
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+
+/** Sets the whole log */
+void DlgIndividual::setLog(const QString & _msg)
+{
+ ml_log->setText(_msg);
+}
+
+void DlgIndividual::appendLog(const QString & _msg)
+{
+ ml_log->append(_msg);
+}
+
+
+void DlgIndividual::slotKeepOpenToggled(bool bToggled)
+{
+#ifdef _DEBUG
+ sDebugIn <<"bToggled= "<<bToggled<<endl;
+#endif
+
+
+ bKeepDlgOpen=bToggled;
+
+ if (!bKeepDlgOpen && item->getStatus()==Transfer::ST_FINISHED)
+ {
+ hide();
+ m_pDockIndividual->hide();
+ }
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+
+void DlgIndividual::slotOpenLocation()
+{
+#ifdef _DEBUG
+ sDebugIn<<endl;
+#endif
+
+ KURL location=m_location;
+ location.setFileName("");
+ kapp->invokeBrowser( location.url() );
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+void DlgIndividual::slotOpenFile()
+{
+#ifdef _DEBUG
+ sDebugIn "Starting kfmclient with url "<<m_location.prettyURL()<<endl;
+#endif
+
+ (void) new KRun( m_location );
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+
+void DlgIndividual::enableOpenFile()
+{
+#ifdef _DEBUG
+ sDebugIn<<endl;
+#endif
+
+
+ openFile->setEnabled(true);
+
+ if (!bKeepDlgOpen)
+ {
+ hide();
+ m_pDockIndividual->hide();
+ }
+
+#ifdef _DEBUG
+ sDebugOut<<endl;
+#endif
+}
+
+#include "dlgIndividual.moc"
diff --git a/kget/dlgIndividual.h b/kget/dlgIndividual.h
new file mode 100644
index 00000000..73a233aa
--- /dev/null
+++ b/kget/dlgIndividual.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+* dlgIndividual.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#ifndef __dlgprogress_h__
+#define __dlgprogress_h__
+
+#include <qdatetime.h>
+
+#include <qwidget.h>
+
+#include <kio/global.h>
+
+class Transfer;
+
+class QLabel;
+class QRadioButton;
+class QTabWidget;
+class QTextEdit;
+class QCheckBox;
+class KPushButton;
+class KProgress;
+class KToggleAction;
+class KDateTimeWidget;
+
+class DockIndividual;
+
+class DlgIndividual:public QWidget
+{
+Q_OBJECT
+public:
+ DlgIndividual(Transfer * _item);
+ ~DlgIndividual()
+ {}
+ void setLog(const QString & _msg);
+ void appendLog(const QString & _msg);
+ void enableOpenFile();
+
+public slots:
+ void setTotalSize(KIO::filesize_t bytes);
+
+ void setProcessedSize(KIO::filesize_t bytes);
+
+ void setSpeed(QString speed);
+ void setPercent(unsigned long percent);
+
+ void setCopying(const KURL & src, const KURL & dest);
+ void setCanResume(bool);
+ void slotKeepOpenToggled(bool);
+ void slotOpenLocation();
+ void slotOpenFile();
+
+ bool keepDialogOpen() const { return bKeepDlgOpen; }
+
+protected slots:
+ void slotToggleAdvanced();
+ void slotToggleDock();
+
+protected:
+ QLabel *sourceLabel;
+ QLabel *destLabel;
+ QLabel *speedLabel;
+ QLabel *sizeLabel;
+ QLabel *resumeLabel;
+ QTextEdit *ml_log;
+
+ KProgress *m_pProgressBar;
+ DockIndividual * m_pDockIndividual;
+
+ KPushButton * openFile;
+ KPushButton * openLocation;
+ KPushButton * pbAdvanced ;
+
+ QTabWidget * panelAdvanced;
+ KToggleAction * m_paDock;
+
+ QDateTime qdt;
+ KDateTimeWidget *spins;
+
+ Transfer *item;
+
+ KURL m_location;
+
+ bool bKeepDlgOpen;
+
+ KIO::filesize_t m_iTotalSize;
+
+ bool advanced;
+}
+
+;
+
+#endif // __dlgprogress_h__
diff --git a/kget/dlgLimits.cpp b/kget/dlgLimits.cpp
new file mode 100644
index 00000000..e31326c7
--- /dev/null
+++ b/kget/dlgLimits.cpp
@@ -0,0 +1,72 @@
+/***************************************************************************
+* dlgLimits.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <knuminput.h>
+#include <klocale.h>
+#include <kdialog.h>
+
+#include "settings.h"
+#include "kmainwidget.h"
+#include "dlgLimits.h"
+
+
+DlgLimits::DlgLimits(QWidget * parent)
+ : DlgLimitsBase(parent)
+{
+ // TODO: these are not supported yet, so hide them
+ lb_maxband->hide();
+ le_maxband->hide();
+ lb_minband->hide();
+ le_minband->hide();
+}
+
+
+void DlgLimits::setData()
+{
+ le_maxnum->setValue(ksettings.maxSimultaneousConnections);
+ le_minband->setValue(ksettings.minimumBandwidth);
+ le_maxband->setValue(ksettings.maximumBandwidth);
+}
+
+
+void DlgLimits::applyData()
+{
+ ksettings.maxSimultaneousConnections = le_maxnum->value();
+ ksettings.minimumBandwidth = le_minband->value();
+ ksettings.maximumBandwidth = le_maxband->value();
+ kmain->checkQueue();
+}
+
+void DlgLimits::slotChanged()
+{
+ emit configChanged();
+}
+
+#include "dlgLimits.moc"
diff --git a/kget/dlgLimits.h b/kget/dlgLimits.h
new file mode 100644
index 00000000..a020f221
--- /dev/null
+++ b/kget/dlgLimits.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+* dlgLimits.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _DLGLIMITS_H
+#define _DLGLIMITS_H
+
+#include "dlglimitsbase.h"
+
+class DlgLimits : public DlgLimitsBase
+{
+
+Q_OBJECT public:
+
+ DlgLimits(QWidget * parent);
+ ~DlgLimits() {}
+ void applyData();
+ void setData();
+
+signals:
+ void configChanged();
+
+protected slots:
+ void slotChanged();
+
+};
+
+#endif // _DLGLIMITS_H
diff --git a/kget/dlgPreferences.cpp b/kget/dlgPreferences.cpp
new file mode 100644
index 00000000..f35b8add
--- /dev/null
+++ b/kget/dlgPreferences.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+* dlgPreferences.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qlayout.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kcombobox.h>
+
+#include "dlgConnection.h"
+#include "dlgAutomation.h"
+#include "dlgLimits.h"
+#include "dlgAdvanced.h"
+#include "dlgDirectories.h"
+#include "dlgSystem.h"
+
+#include "settings.h"
+
+#ifdef index
+#undef index
+#endif
+
+#include "kmainwidget.h"
+#include "dlgPreferences.h"
+
+
+DlgPreferences::DlgPreferences(QWidget * parent):
+ KDialogBase(Tabbed, i18n("Configure"), Ok|Apply|Help|Cancel, Ok, parent, "DlgPreferences", true)
+{
+ // add pages
+ QFrame *page = addPage(i18n("Connection"));
+ QVBoxLayout *topLayout = new QVBoxLayout(page, 0, spacingHint());
+
+ conDlg = new DlgConnection(page);
+ topLayout->addWidget(conDlg);
+
+ page = addPage(i18n("Automation"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ autDlg = new DlgAutomation(page);
+ topLayout->addWidget(autDlg);
+ topLayout->addStretch();
+
+ page = addPage(i18n("Limits"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ limDlg = new DlgLimits(page);
+ topLayout->addWidget(limDlg);
+ topLayout->addStretch();
+
+ page = addPage(i18n("Advanced"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ advDlg = new DlgAdvanced(page);
+ topLayout->addWidget(advDlg);
+ topLayout->addStretch();
+
+ // page = addPage(i18n("Search"));
+ // topLayout = new QVBoxLayout(page, 0, spacingHint());
+ // seaDlg = new DlgSearch(page);
+ // topLayout->addWidget(seaDlg);
+
+ page = addPage(i18n("Folders"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ dirDlg = new DlgDirectories(page);
+ topLayout->addWidget(dirDlg);
+ topLayout->addStretch();
+
+ page = addPage(i18n("System"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ sysDlg = new DlgSystem(page);
+ topLayout->addWidget(sysDlg);
+ topLayout->addStretch();
+
+ // type of connection influences autoDisconnect & timedDisconnect features
+ connect(conDlg, SIGNAL(typeChanged(int)), autDlg, SLOT(slotTypeChanged(int)));
+
+ loadAllData();
+
+ connect( conDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+ connect( autDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+ connect( limDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+ connect( advDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+ connect( dirDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+ connect( sysDlg, SIGNAL( configChanged() ), this, SLOT( slotChanged() ) );
+}
+
+
+void DlgPreferences::slotChanged()
+{
+ changed = true;
+ enableButton( Apply, true );
+}
+
+void DlgPreferences::applySettings()
+{
+ conDlg->applyData();
+ autDlg->applyData();
+ limDlg->applyData();
+ advDlg->applyData();
+ // seaDlg->applyData();
+ dirDlg->applyData();
+ sysDlg->applyData();
+
+ ksettings.save();
+ changed = false;
+ enableButton( Apply, false );
+}
+
+void DlgPreferences::slotOk()
+{
+ if ( changed )
+ applySettings();
+ accept();
+}
+
+void DlgPreferences::slotCancel()
+{
+ if ( changed )
+ loadAllData();
+ reject();
+}
+
+void DlgPreferences::slotApply()
+{
+ applySettings();
+}
+
+void DlgPreferences::loadAllData()
+{
+ conDlg->setData();
+ autDlg->setData();
+ limDlg->setData();
+ advDlg->setData();
+ // seaDlg->setData();
+ dirDlg->setData();
+ sysDlg->setData();
+ changed = false;
+ enableButton( Apply, false );
+}
+
+#include "dlgPreferences.moc"
diff --git a/kget/dlgPreferences.h b/kget/dlgPreferences.h
new file mode 100644
index 00000000..8e2e1307
--- /dev/null
+++ b/kget/dlgPreferences.h
@@ -0,0 +1,77 @@
+/***************************************************************************
+* dlgPreferences.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#ifndef _DLGPREFERENCES_H
+#define _DLGPREFERENCES_H
+
+#include <qdialog.h>
+#include <qstringlist.h>
+
+#include <kdialogbase.h>
+
+class DlgConnection;
+class DlgAutomation;
+class DlgLimits;
+class DlgAdvanced;
+class DlgSearch;
+class DlgDirectories;
+class DlgSystem;
+
+
+class DlgPreferences:public KDialogBase
+{
+
+Q_OBJECT public:
+
+ DlgPreferences(QWidget * parent);
+ ~DlgPreferences()
+ {}
+
+private:
+
+ DlgConnection * conDlg;
+ DlgAutomation *autDlg;
+ DlgLimits *limDlg;
+ DlgAdvanced *advDlg;
+
+ // DlgSearch *seaDlg;
+ DlgDirectories *dirDlg;
+ DlgSystem *sysDlg;
+
+ bool changed;
+
+ void loadAllData();
+
+protected slots:
+ void applySettings();
+ void slotChanged();
+ virtual void slotOk();
+ virtual void slotCancel();
+ virtual void slotApply();
+
+};
+
+#endif // _DLGPREFERENCES_H
diff --git a/kget/dlgSystem.cpp b/kget/dlgSystem.cpp
new file mode 100644
index 00000000..d908724a
--- /dev/null
+++ b/kget/dlgSystem.cpp
@@ -0,0 +1,77 @@
+/***************************************************************************
+* dlgSystem.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+
+#ifdef index
+#undef index
+#endif
+
+#include <kfontrequester.h>
+
+#include "settings.h"
+#include "kmainwidget.h"
+#include "dlgSystem.h"
+
+#include <qbuttongroup.h>
+#include <qlabel.h>
+
+
+DlgSystem::DlgSystem(QWidget * parent)
+ : DlgSystemBase(parent)
+{
+ bg_window->hide();
+ textLabel4->hide();
+}
+
+
+void DlgSystem::setData()
+{
+ cb_useAnimation->setChecked(ksettings.b_useAnimation);
+
+ le_font->setFont(ksettings.listViewFont);
+}
+
+
+void DlgSystem::applyData()
+{
+ if (cb_useAnimation->isChecked() != ksettings.b_useAnimation)
+ {
+ kmain->slotToggleAnimation();
+ }
+
+ ksettings.listViewFont = le_font->font();
+ kmain->setListFont();
+}
+
+void DlgSystem::slotChanged()
+{
+ emit configChanged();
+}
+
+#include "dlgSystem.moc"
diff --git a/kget/dlgSystem.h b/kget/dlgSystem.h
new file mode 100644
index 00000000..677481b3
--- /dev/null
+++ b/kget/dlgSystem.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+* dlgSystem.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+
+
+#ifndef _DLGSYSTEM_H
+#define _DLGSYSTEM_H
+
+#include "dlgsystembase.h"
+
+class DlgSystem : public DlgSystemBase
+{
+Q_OBJECT public:
+ DlgSystem(QWidget * parent);
+ ~DlgSystem() {}
+
+ void applyData();
+ void setData();
+
+signals:
+ void configChanged();
+
+protected slots:
+ void slotChanged();
+};
+
+#endif // _DLGSYSTEM_H
diff --git a/kget/dlgadvancedbase.ui b/kget/dlgadvancedbase.ui
new file mode 100644
index 00000000..dd9b5c59
--- /dev/null
+++ b/kget/dlgadvancedbase.ui
@@ -0,0 +1,274 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgAdvancedBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgAdvancedBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>368</width>
+ <height>285</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_advanced</cstring>
+ </property>
+ <property name="title">
+ <string>Advanced Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lb_adding</cstring>
+ </property>
+ <property name="text">
+ <string>Add new transfers as:</string>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_iconify</cstring>
+ </property>
+ <property name="text">
+ <string>Iconified</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_advanced</cstring>
+ </property>
+ <property name="text">
+ <string>Advanced individual windows</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_partial</cstring>
+ </property>
+ <property name="text">
+ <string>Mark partial downloads</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_remove</cstring>
+ </property>
+ <property name="text">
+ <string>Remove files from a list after success</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_getsizes</cstring>
+ </property>
+ <property name="text">
+ <string>Get file sizes</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>cb_expertmode</cstring>
+ </property>
+ <property name="text">
+ <string>Expert mode (do not prompt for Cancel or Delete)</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="8" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_konqiIntegration</cstring>
+ </property>
+ <property name="text">
+ <string>Use KGet as Download Manager for Konqueror</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="9" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_ShowMain</cstring>
+ </property>
+ <property name="text">
+ <string>Show main window at startup</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_individual</cstring>
+ </property>
+ <property name="text">
+ <string>Show individual windows</string>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="2">
+ <property name="name">
+ <cstring>bg_adding</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <property name="exclusive">
+ <bool>false</bool>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_queued</cstring>
+ </property>
+ <property name="text">
+ <string>Queued</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_delayed</cstring>
+ </property>
+ <property name="text">
+ <string>Delayed</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="0" column="3">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>35</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>cb_ShowMain</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_advanced</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_expertmode</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_getsizes</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_iconify</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_individual</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_konqiIntegration</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_partial</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_remove</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>rb_delayed</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>rb_queued</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAdvancedBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_individual</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cb_iconify</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">slotChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/kget/dlgautomationbase.ui b/kget/dlgautomationbase.ui
new file mode 100644
index 00000000..071a089b
--- /dev/null
+++ b/kget/dlgautomationbase.ui
@@ -0,0 +1,256 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgAutomationBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgAutomationBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>401</width>
+ <height>217</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_automation</cstring>
+ </property>
+ <property name="title">
+ <string>Automation Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>le_autoSave</cstring>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>cb_autoDisconnect</cstring>
+ </property>
+ <property name="text">
+ <string>Auto disconnect after completing downloads</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_autoSave</cstring>
+ </property>
+ <property name="text">
+ <string>Autosave file list every:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_timedDisconnect</cstring>
+ </property>
+ <property name="text">
+ <string>Timed disconnect</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lb_autoDisconnect</cstring>
+ </property>
+ <property name="text">
+ <string>Disconnect command:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>le_autoDisconnect</cstring>
+ </property>
+ </widget>
+ <widget class="KDateTimeWidget" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spins</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>cb_autoPaste</cstring>
+ </property>
+ <property name="text">
+ <string>Auto paste from clipboard</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>cb_autoShutdown</cstring>
+ </property>
+ <property name="text">
+ <string>Auto shutdown after completing downloads</string>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>spacer17</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>cb_autoDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lb_autoDisconnect</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>le_autoDisconnect</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_timedDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>spins</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoSave</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>le_autoSave</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoSave</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_autoSave</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_autoDisconnect</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_timedDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>spins</sender>
+ <signal>valueChanged(const QDateTime&amp;)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoShutdown</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoPaste</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_autoDisconnect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgAutomationBase</receiver>
+ <slot>disconnectToggled(bool)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">slotChanged()</slot>
+ <slot access="protected" specifier="pure virtual">disconnectToggled(bool b)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kdatetimewidget.h</includehint>
+ <includehint>kdatewidget.h</includehint>
+ <includehint>ktimewidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kget/dlgconnectionbase.ui b/kget/dlgconnectionbase.ui
new file mode 100644
index 00000000..fa9bc377
--- /dev/null
+++ b/kget/dlgconnectionbase.ui
@@ -0,0 +1,401 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgConnectionBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgConnectionBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>319</width>
+ <height>355</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_reconnect</cstring>
+ </property>
+ <property name="title">
+ <string>Reconnect Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_onerror</cstring>
+ </property>
+ <property name="text">
+ <string>On login or timeout error</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>lb_after</cstring>
+ </property>
+ <property name="text">
+ <string>Reconnect after:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_retries</cstring>
+ </property>
+ <property name="text">
+ <string>Number of retries:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="2" column="2">
+ <property name="name">
+ <cstring>le_after</cstring>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>le_retries</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cb_onbroken</cstring>
+ </property>
+ <property name="text">
+ <string>On broken connection</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="0">
+ <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>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_timeout</cstring>
+ </property>
+ <property name="title">
+ <string>Timeout Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_nodata</cstring>
+ </property>
+ <property name="text">
+ <string>If no data arrives in:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lb_cannot</cstring>
+ </property>
+ <property name="text">
+ <string>If server cannot resume:</string>
+ </property>
+ </widget>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KIntSpinBox" row="2" column="3">
+ <property name="name">
+ <cstring>le_noresume</cstring>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>lb_or</cstring>
+ </property>
+ <property name="text">
+ <string>or</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>le_nodata</cstring>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_type</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Type</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KComboBox" row="0" column="0">
+ <item>
+ <property name="text">
+ <string>Permanent</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ethernet</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>PLIP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>SLIP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>PPP</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>ISDN</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmb_type</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>cb_offlinemode</cstring>
+ </property>
+ <property name="text">
+ <string>Offline mode</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>lb_linknum</cstring>
+ </property>
+ <property name="text">
+ <string>Link number:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="3">
+ <property name="name">
+ <cstring>le_linknum</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>cb_onerror</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lb_after</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_onerror</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>le_after</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_onerror</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_after</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_retries</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_onbroken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_nodata</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_noresume</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cmb_type</sender>
+ <signal>activated(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_linknum</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cb_offlinemode</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>cmb_type</sender>
+ <signal>activated(int)</signal>
+ <receiver>DlgConnectionBase</receiver>
+ <slot>comboActivated(int)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">slotChanged()</slot>
+ <slot access="protected" specifier="pure virtual">comboActivated(int)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/kget/dlgdirectoriesbase.ui b/kget/dlgdirectoriesbase.ui
new file mode 100644
index 00000000..ccad9d15
--- /dev/null
+++ b/kget/dlgdirectoriesbase.ui
@@ -0,0 +1,263 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgDirectoriesBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgDirectoriesBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>437</width>
+ <height>275</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QToolButton" row="2" column="1">
+ <property name="name">
+ <cstring>pb_down</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="iconSet">
+ <iconset>"down"</iconset>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QToolButton" row="0" column="1">
+ <property name="name">
+ <cstring>pb_up</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="iconSet">
+ <iconset>"up"</iconset>
+ </property>
+ </widget>
+ <widget class="QListView" row="0" column="0" rowspan="3" colspan="1">
+ <column>
+ <property name="text">
+ <string>Extension</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Default Folder</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lv_entries</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pb_add</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>5</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pb_delete</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>5</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pb_change</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>5</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Change</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KLineEdit" row="2" column="0">
+ <property name="name">
+ <cstring>le_ext</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lb_ext</cstring>
+ </property>
+ <property name="text">
+ <string>Extension (* for all files):</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="2" column="1">
+ <property name="name">
+ <cstring>le_dir</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>lb_dir</cstring>
+ </property>
+ <property name="text">
+ <string>Default folder:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>pb_down</sender>
+ <signal>clicked()</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>downEntry()</slot>
+ </connection>
+ <connection>
+ <sender>pb_up</sender>
+ <signal>clicked()</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>upEntry()</slot>
+ </connection>
+ <connection>
+ <sender>lv_entries</sender>
+ <signal>selectionChanged(QListViewItem*)</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>selectEntry(QListViewItem*)</slot>
+ </connection>
+ <connection>
+ <sender>pb_add</sender>
+ <signal>clicked()</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>addEntry()</slot>
+ </connection>
+ <connection>
+ <sender>pb_delete</sender>
+ <signal>clicked()</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>deleteEntry()</slot>
+ </connection>
+ <connection>
+ <sender>pb_change</sender>
+ <signal>clicked()</signal>
+ <receiver>DlgDirectoriesBase</receiver>
+ <slot>changeEntry()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="global" impldecl="in implementation">kiconloader.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">selectEntry(QListViewItem *)</slot>
+ <slot specifier="pure virtual">upEntry()</slot>
+ <slot>downEntry()</slot>
+ <slot>addEntry()</slot>
+ <slot>deleteEntry()</slot>
+ <slot>changeEntry()</slot>
+</slots>
+<pixmapfunction>BarIconSet</pixmapfunction>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+</includehints>
+</UI>
diff --git a/kget/dlglimitsbase.ui b/kget/dlglimitsbase.ui
new file mode 100644
index 00000000..faf2309f
--- /dev/null
+++ b/kget/dlglimitsbase.ui
@@ -0,0 +1,189 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgLimitsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgLimitsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>568</width>
+ <height>141</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gb_limits</cstring>
+ </property>
+ <property name="title">
+ <string>Limits Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>lbl_maxnum</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum open connections:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lb_minband</cstring>
+ </property>
+ <property name="text">
+ <string>Minimum network bandwidth:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lb_maxband</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum network bandwidth:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="0" column="2">
+ <property name="name">
+ <cstring>le_maxnum</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>le_minband</cstring>
+ </property>
+ <property name="suffix">
+ <string> bytes/sec</string>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ <property name="minValue">
+ <number>100</number>
+ </property>
+ <property name="lineStep">
+ <number>100</number>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="2">
+ <property name="name">
+ <cstring>le_maxband</cstring>
+ </property>
+ <property name="suffix">
+ <string> bytes/sec</string>
+ </property>
+ <property name="maxValue">
+ <number>100000</number>
+ </property>
+ <property name="minValue">
+ <number>100</number>
+ </property>
+ <property name="lineStep">
+ <number>100</number>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer19</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer20</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>le_maxband</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgLimitsBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_maxnum</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgLimitsBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_minband</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>DlgLimitsBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">slotChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kget/dlgsystembase.ui b/kget/dlgsystembase.ui
new file mode 100644
index 00000000..beee27eb
--- /dev/null
+++ b/kget/dlgsystembase.ui
@@ -0,0 +1,169 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgSystemBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgSystemBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>389</width>
+ <height>137</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_useAnimation</cstring>
+ </property>
+ <property name="text">
+ <string>Use animation</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Window style:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Font:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="2" column="2" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>le_font</cstring>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="2" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>bg_window</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_normal</cstring>
+ </property>
+ <property name="text">
+ <string>Normal</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_docked</cstring>
+ </property>
+ <property name="text">
+ <string>Docked</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_droptarget</cstring>
+ </property>
+ <property name="text">
+ <string>Drop target</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="1" column="6">
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="0">
+ <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>40</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>cb_useAnimation</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgSystemBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>bg_window</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>DlgSystemBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+ <connection>
+ <sender>le_font</sender>
+ <signal>fontSelected(const QFont&amp;)</signal>
+ <receiver>DlgSystemBase</receiver>
+ <slot>slotChanged()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">slotChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kfontrequester.h</includehint>
+</includehints>
+</UI>
diff --git a/kget/dockindividual.cpp b/kget/dockindividual.cpp
new file mode 100644
index 00000000..1ecf61fa
--- /dev/null
+++ b/kget/dockindividual.cpp
@@ -0,0 +1,99 @@
+/***************************************************************************
+* dockindividual.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* :
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qtooltip.h>
+
+#include <kpopupmenu.h>
+
+#include "dockindividual.h"
+DockIndividual::DockIndividual(QWidget *parent, const char *name ) : KSystemTray(parent,name)
+{
+ nPic=0;
+ setPixmap( loadIcon("bar0") );
+}
+
+DockIndividual::~DockIndividual()
+{
+ QToolTip::remove(this);
+}
+
+
+void DockIndividual::setValue(int value){
+#ifdef _DEBUG
+ //sDebugIn<<" value ="<<value<<endl;
+#endif
+ int tmpPic=0;
+ if (value<20)
+ tmpPic=1;
+ else if(value<40)
+ tmpPic=2;
+
+ else if(value<60)
+ tmpPic=3;
+
+ else if(value<80)
+ tmpPic=4;
+
+ else if(value<=95)
+ tmpPic=5;
+
+ else if(value>=96)
+ tmpPic=6;
+
+ if (tmpPic!=nPic)
+ {
+ nPic=tmpPic;
+ QString str = "bar" + QString::number( nPic );
+ setPixmap( loadIcon( str ) );
+ }
+
+#ifdef _DEBUG
+ //sDebugOut<<endl;
+#endif
+}
+
+
+
+void DockIndividual::setTip(const QString & _tip)
+{
+#ifdef _DEBUG
+ //sDebugIn<<"_tip="<<_tip<<endl;
+#endif
+
+
+ QToolTip::add( this, _tip );
+
+#ifdef _DEBUG
+ //sDebugOut<<endl;
+#endif
+}
+
+#include "dockindividual.moc"
+/** No descriptions */
+void DockIndividual::contextMenuAboutToShow ( KPopupMenu* menu )
+{
+ menu->removeItemAt (3);
+}
diff --git a/kget/dockindividual.h b/kget/dockindividual.h
new file mode 100644
index 00000000..39203272
--- /dev/null
+++ b/kget/dockindividual.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+* dockindividual.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* :
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef DOCKINDIVIDUAL_H
+#define DOCKINDIVIDUAL_H
+
+#include <ksystemtray.h>
+#include "common.h"
+
+class KPopupMenu;
+
+class DockIndividual : public KSystemTray {
+ Q_OBJECT
+public:
+ DockIndividual(QWidget *parent=0, const char *name=0);
+ ~DockIndividual();
+ int nPic;
+ void setTip(const QString &);
+ void setValue(int value);
+ /** No descriptions */
+ virtual void contextMenuAboutToShow ( KPopupMenu* menu );
+};
+
+#endif
diff --git a/kget/docking.cpp b/kget/docking.cpp
new file mode 100644
index 00000000..cd3309c3
--- /dev/null
+++ b/kget/docking.cpp
@@ -0,0 +1,131 @@
+/***************************************************************************
+* docking.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qtooltip.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+
+#include "kmainwidget.h"
+#include "settings.h"
+#include "docking.h"
+
+
+DockWidget::DockWidget(KMainWidget * _parent):KSystemTray(_parent)
+{
+ parent = _parent;
+
+ setPixmap( loadIcon( "kget_dock" ));
+
+ // popup menu for right mouse button
+ KPopupMenu *popupMenu = contextMenu();
+ parent->action("paste_transfer")->plug(popupMenu);
+ parent->action("drop_target")->plug(popupMenu);
+ parent->action("konqueror_integration")->plug(popupMenu);
+ popupMenu->insertSeparator();
+ parent->m_paPreferences->plug(popupMenu);
+
+ // Enable dropping
+ setAcceptDrops(true);
+
+ dtip = new DynamicTip( this );
+ dtip->setStatus( kapp->aboutData()->shortDescription() );
+
+}
+
+
+DockWidget::~DockWidget()
+{
+ delete dtip;
+ dtip = 0;
+}
+
+
+void DockWidget::dragEnterEvent(QDragEnterEvent * event)
+{
+ event->accept(KURLDrag::canDecode(event)
+ || QTextDrag::canDecode(event));
+}
+
+
+void DockWidget::dropEvent(QDropEvent * event)
+{
+ KURL::List list;
+ QString str;
+
+ if (KURLDrag::decode(event, list)) {
+ parent->addTransfers(list);
+ } else if (QTextDrag::decode(event, str)) {
+ parent->addTransfer(str);
+ }
+}
+
+
+void DockWidget::mousePressEvent(QMouseEvent * e)
+{
+ if (e->button() == MidButton) {
+ parent->slotPasteTransfer();
+ } else {
+ KSystemTray::mousePressEvent(e);
+ }
+}
+
+
+void DockWidget::updateToolTip( const QString& _status )
+{
+ dtip->setStatus( _status );
+}
+
+
+void DockWidget::changeIcon( const QString& icon )
+{
+ setPixmap( loadIcon( icon ));
+}
+
+
+DynamicTip::DynamicTip( QWidget * parent )
+ : QToolTip( parent )
+{
+ // no explicit initialization needed
+}
+
+
+void DynamicTip::setStatus( const QString & _status )
+{
+ status = _status;
+}
+
+void DynamicTip::maybeTip( const QPoint & _pos )
+{
+ QRect r( parentWidget()->rect() );
+ tip( r, status );
+}
+
+#include "docking.moc"
diff --git a/kget/docking.h b/kget/docking.h
new file mode 100644
index 00000000..df4c75ec
--- /dev/null
+++ b/kget/docking.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+* docking.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+
+#ifndef _DOCKING_H_
+#define _DOCKING_H_
+
+#include <stdio.h>
+
+#include <qdragobject.h>
+
+#include <ksystemtray.h>
+
+class KPopupMenu;
+class KMainWidget;
+
+class DynamicTip : public QToolTip
+{
+ public:
+ DynamicTip( QWidget * parent );
+ virtual ~DynamicTip() {}//TODO workaround for qt-bug, can be removed after 4.0
+ void setStatus( const QString & _status );
+
+ protected:
+ void maybeTip( const QPoint & );
+
+ private:
+ QString status;
+};
+
+class DockWidget:public KSystemTray
+{
+
+Q_OBJECT public:
+ DockWidget(KMainWidget * parent);
+ ~DockWidget();
+ /** No descriptions */
+ void updateToolTip( const QString& );
+ void changeIcon( const QString& );
+
+
+
+private slots:
+ void mousePressEvent(QMouseEvent * e);
+
+protected:
+ // drag and drop
+ void dragEnterEvent(QDragEnterEvent *);
+ void dropEvent(QDropEvent *);
+
+private:
+ KMainWidget *parent;
+ DynamicTip * dtip;
+
+};
+
+#endif
diff --git a/kget/droptarget.cpp b/kget/droptarget.cpp
new file mode 100644
index 00000000..97bb01e2
--- /dev/null
+++ b/kget/droptarget.cpp
@@ -0,0 +1,231 @@
+/***************************************************************************
+* droptarget.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qpainter.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kiconloader.h>
+#include <kglobalsettings.h>
+#include <kmainwindow.h>
+#include <kwin.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+
+#include "kmainwidget.h"
+#include <qcursor.h>
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/extensions/shape.h>
+#undef Bool
+#undef Status
+#endif
+#include "settings.h"
+#include "droptarget.h"
+
+DropTarget::DropTarget(KMainWindow * mainWin):QWidget()
+{
+ int x = ksettings.dropPosition.x();
+ int y = ksettings.dropPosition.y();
+
+ QRect desk = KGlobalSettings::desktopGeometry(this);
+ QPixmap bgnd = UserIcon( "target" );
+
+ if (x != -1 &&
+ x >= desk.left() && y >= desk.top() &&
+ (x + bgnd.width()) <= desk.right() &&
+ (y + bgnd.height()) <= desk.bottom() )
+ {
+ move(ksettings.dropPosition);
+ resize(bgnd.width(), bgnd.height());
+ KWin::setState(winId(), ksettings.dropState);
+ }
+ else
+ {
+ setGeometry(desk.x()+200, desk.y()+200, bgnd.width(), bgnd.height());
+ KWin::setState(winId(), NET::SkipTaskbar | NET::StaysOnTop);
+ }
+
+ b_sticky = ksettings.dropState & NET::Sticky;
+
+ // setup pixmaps
+
+ if (!bgnd.mask())
+ kdError(5001) << "Drop target pixmap has no mask!\n";
+ else
+ mask = *bgnd.mask();
+
+ setBackgroundPixmap( bgnd );
+
+ // popup menu for right mouse button
+ popupMenu = new KPopupMenu();
+ popupMenu->insertTitle(kapp->caption());
+ popupMenu->setCheckable(true);
+
+ pop_Max = popupMenu->insertItem(i18n("Maximize"), this, SLOT(toggleMinimizeRestore()));
+ pop_Min = popupMenu->insertItem(i18n("Minimize"), this, SLOT(toggleMinimizeRestore()));
+
+ pop_sticky = popupMenu->insertItem(i18n("Sticky"), this, SLOT(toggleSticky()));
+ popupMenu->insertSeparator();
+ mainWin->action("drop_target")->plug(popupMenu);
+ popupMenu->insertSeparator();
+
+ popupMenu->setItemChecked(pop_sticky, b_sticky);
+ kmain->m_paPreferences->plug(popupMenu);
+ popupMenu->insertSeparator();
+ kmain->m_paQuit->plug(popupMenu);
+
+ isdragging = false;
+
+ // Enable dropping
+ setAcceptDrops(true);
+}
+
+
+DropTarget::~DropTarget()
+{
+ delete popupMenu;
+}
+
+
+void
+DropTarget::mousePressEvent(QMouseEvent * e)
+{
+ if (e->button() == LeftButton)
+ {
+ // toggleMinimizeRestore ();
+// oldX = 0;
+// oldY = 0;
+ isdragging = true;
+ dx = QCursor::pos().x() - pos().x();
+ dy = QCursor::pos().y() - pos().y();
+ }
+ else if (e->button() == RightButton)
+ {
+ popupMenu->setItemEnabled(pop_Min, kmain->isVisible());
+ popupMenu->setItemEnabled(pop_Max, kmain->isHidden());
+
+ popupMenu->popup(QCursor::pos());
+ }
+ else if (e->button() == MidButton)
+ {
+ kmain->slotPasteTransfer();
+ }
+}
+
+
+void DropTarget::resizeEvent(QResizeEvent *)
+{
+#ifdef Q_WS_X11
+ XShapeCombineMask(x11Display(), winId(), ShapeBounding, 0, 0, mask.handle(), ShapeSet);
+#endif
+}
+
+
+void DropTarget::dragEnterEvent(QDragEnterEvent * event)
+{
+ event->accept(KURLDrag::canDecode(event)
+ || QTextDrag::canDecode(event));
+}
+
+
+void DropTarget::dropEvent(QDropEvent * event)
+{
+ KURL::List list;
+ QString str;
+
+ if (KURLDrag::decode(event, list))
+ {
+ kmain->addTransfers(list);
+ }
+ else if (QTextDrag::decode(event, str))
+ {
+ kmain->addTransfer(str);
+ }
+}
+
+
+void DropTarget::toggleSticky()
+{
+ b_sticky = !b_sticky;
+ popupMenu->setItemChecked(pop_sticky, b_sticky);
+ updateStickyState();
+}
+
+void DropTarget::updateStickyState()
+{
+ if (b_sticky)
+ {
+ KWin::setState(winId(), NET::SkipTaskbar | NET::StaysOnTop | NET::Sticky);
+ }
+ else
+ {
+ KWin::clearState(winId(), NET::Sticky);
+ }
+}
+
+void DropTarget::toggleMinimizeRestore()
+{
+ if (kmain->isVisible())
+ kmain->hide();
+ else
+ kmain->show();
+}
+
+/** No descriptions */
+void DropTarget::mouseMoveEvent(QMouseEvent * e)
+{
+/*
+ if (oldX == 0)
+ {
+ oldX = e->x();
+ oldY = e->y();
+ return;
+ }
++*/
+ if (isdragging)
+ move( QCursor::pos().x() - dx, QCursor::pos().y() - dy );
+
+// move(x() + (e->x() - oldX), y() + (e->y() - oldY)); // <<--
+}
+
+void DropTarget::mouseReleaseEvent(QMouseEvent *)
+{
+ isdragging = false;
+}
+
+/** No descriptions */
+void DropTarget::mouseDoubleClickEvent(QMouseEvent * e)
+{
+ if (e->button() == LeftButton)
+ toggleMinimizeRestore();
+}
+
+#include "droptarget.moc"
diff --git a/kget/droptarget.h b/kget/droptarget.h
new file mode 100644
index 00000000..debce208
--- /dev/null
+++ b/kget/droptarget.h
@@ -0,0 +1,87 @@
+/***************************************************************************
+* droptarget.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#ifndef _DROPTARGET_H
+#define _DROPTARGET_H
+
+#include <qwidget.h>
+#include <qbitmap.h>
+#include <qdragobject.h>
+
+class KPopupMenu;
+class KMainWidget;
+class KMainWindow;
+
+class DropTarget:public QWidget
+{
+Q_OBJECT
+
+public:
+ DropTarget(KMainWindow *);
+ ~DropTarget();
+
+ void updateStickyState();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *);
+
+ // drag and drop
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ /** No descriptions */
+ virtual void mouseDoubleClickEvent(QMouseEvent * e);
+ virtual void dropEvent(QDropEvent *);
+ /** No descriptions */
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void mousePressEvent(QMouseEvent * e);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+
+private slots:
+ void toggleSticky();
+ void toggleMinimizeRestore();
+
+private:
+ KPopupMenu * popupMenu;
+ KMainWidget *parent;
+
+ bool b_sticky;
+
+ int pop_sticky;
+ int pop_Max;
+ int pop_Min;
+
+ int size[4];
+
+ QBitmap mask;
+public: // Public attributes
+ /** */
+// int oldX;
+// int oldY;
+ int dx;
+ int dy;
+ bool isdragging;
+};
+
+#endif // _DROPTARGET_H
diff --git a/kget/eventsrc b/kget/eventsrc
new file mode 100644
index 00000000..98c9ab38
--- /dev/null
+++ b/kget/eventsrc
@@ -0,0 +1,360 @@
+[!Global!]
+IconName=kget
+Comment=KGet
+Comment[bn]=কে-গেট
+Comment[ne]=केडीई गेट
+Comment[pa]=ਕੇ-ਗੈੱਟ
+Comment[sv]=Kget
+
+[added]
+Name=TransferAdded
+Name[bg]=Ðов файл за изтеглÑне
+Name[ca]=Transferència afegida
+Name[cs]=Přidán přenos
+Name[el]=ΠÏοστέθηκε μεταφοÏά
+Name[es]=TransferenciaAñadida
+Name[et]=Ãœlekanne lisatud
+Name[eu]=DeskargaGehituta
+Name[fa]=انتقال اÙزوده
+Name[fi]=SiirtoLisätty
+Name[fr]=Transfert ajouté
+Name[gl]=TransferenciaEngadida
+Name[he]=העברה הוספה
+Name[hu]=ÃtvittHozzáadva
+Name[is]=Færslu bætt við
+Name[it]=Aggiunto trasferimento
+Name[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ”მáƒáƒ¢áƒ
+Name[kk]=Берілім қоÑылды
+Name[km]=បាន​បន្ážáŸ‚ម​ការផ្ទáŸážš
+Name[nds]=ÖverdregenToföögt
+Name[ne]=सà¥à¤¥à¤¾à¤¨à¤¾à¤¨à¥à¤¤à¤°à¤£ थपिà¤à¤•à¥‹
+Name[nl]=OverdrachtToegevoegd
+Name[pl]=Dodany transfer
+Name[pt_BR]=TransferênciaAdicionada
+Name[ru]=ÐовоеЗадание
+Name[sk]=Pridaný transfer
+Name[sl]=PrenosDodan
+Name[sr]=ÐŸÑ€ÐµÐ½Ð¾Ñ Ð´Ð¾Ð´Ð°Ñ‚
+Name[sr@Latn]=Prenos dodat
+Name[sv]=Överföring tillagd
+Name[tr]=Ä°ndirme Eklendi
+Name[uk]=Додано перенеÑеннÑ
+Name[zh_CN]=添加了任务
+Name[zh_HK]=傳輸已新增
+Name[zh_TW]=新增傳輸
+Comment=A new download has been added
+Comment[be]=Ðовы файл Ð´Ð»Ñ ÑцÑÐ³Ð²Ð°Ð½Ð½Ñ Ð´Ð°Ð´Ð°Ð½Ñ‹ Ñž чаргу
+Comment[bg]=Добавен е нов файл за изтеглÑне
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ নতà§à¦¨ ডাউনলোড যোগ করা হয়েছে
+Comment[bs]=Dodan je novi download
+Comment[ca]=S'ha afegit una descàrrega nova
+Comment[cs]=Byl přidán nový přenos
+Comment[da]=En ny download er blevet tilføjet
+Comment[de]=Ein neuer Transfer wurde hinzugefügt
+Comment[el]=ΠÏοστέθηκε μια νέα λήψη αÏχείου
+Comment[es]=Se ha añadido una nueva descarga
+Comment[et]=Lisati uus allalaadimine
+Comment[eu]=Deskarga berri bat gehitu da
+Comment[fa]=بارگیری جدیدی اضاÙÙ‡ شده است
+Comment[fi]=Uusi tiedoston haku lisätty
+Comment[fr]=Un nouveau téléchargement a été ajouté
+Comment[gl]=Engadiuse unha nova descarga
+Comment[he]=הורדה חדשה הוספה
+Comment[hu]=Új letöltés lett megadva
+Comment[is]=Nýju niðurhali hefur verið bætt við
+Comment[it]=È stato aggiunto un nuovo scaricamento
+Comment[ja]=æ–°ã—ã„ダウンロードãŒè¿½åŠ ã•ã‚Œã¾ã—ãŸ
+Comment[ka]=áƒáƒ®áƒáƒšáƒ˜ ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ”მáƒáƒ¢áƒ
+Comment[kk]=Жаңа жүктеп алу тапÑырмаÑÑ‹ қоÑылды
+Comment[km]=បាន​បន្ážáŸ‚ម​ការ​ទាញយកážáŸ’មី​មួយ
+Comment[lt]=Pridėtas naujas atsisiuntimo įrašas
+Comment[nb]=En ny nedlasting er lagt til
+Comment[nds]=En niege Överdregen wöör toföögt
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ नयाठडाउलोड थपिà¤à¤•à¥‹ छ
+Comment[nl]=Er is een nieuwe download toegevoegd
+Comment[nn]=Ei ny nedlasting er lagd til
+Comment[pl]=Nowe zlecenie pobrania pliku zostało dodane
+Comment[pt]=Foi adicionada uma nova transferência
+Comment[pt_BR]=Um novo download foi adicionado
+Comment[ru]=Добавлено новое задание загрузки
+Comment[sk]=Pridané nové sťahovanie
+Comment[sl]=Dodan je bil nov prenos
+Comment[sr]=Ðово преузимање је додато
+Comment[sr@Latn]=Novo preuzimanje je dodato
+Comment[sv]=En ny nerladdning har lagts till
+Comment[tr]=Yeni dosya eklendi
+Comment[uk]=Додано нове звантаженнÑ
+Comment[uz]=Yangi yozib olish qoʻshildi
+Comment[uz@cyrillic]=Янги ёзиб олиш қўшилди
+Comment[zh_CN]=添加了新下载
+Comment[zh_HK]=已新增新的下載
+Comment[zh_TW]=已增加新的下載
+default_sound=KGet_Added.ogg
+default_presentation=17
+
+[started]
+Name=DownloadStarted
+Name[bg]=Ðачало на изтеглÑне
+Name[bn]=ডাউনলোড আরমà§à¦­
+Name[br]=Kroget eo an enkargañ
+Name[ca]=S'ha iniciat la descàrrega
+Name[cs]=Stahování zahájeno
+Name[el]=Εκκίνηση λήψης αÏχείου
+Name[es]=DescargaComenzada
+Name[et]=Allalaadimine alustatud
+Name[eu]=DeskargaHasita
+Name[fa]=آغاز بارگیری
+Name[fi]=HakuAlkoi
+Name[fr]=Téléchargement démarré
+Name[gl]=DescargaComezada
+Name[he]=הורדההתחילה
+Name[hu]=LetöltésKezdődött
+Name[is]=Niðurhal sett í gang
+Name[it]=Scaricamento avviato
+Name[kk]=Жүктеп алу баÑталды
+Name[km]=បាន​ចាប់ផ្ដើម​ការ​ទាញយក
+Name[nb]=NedlastingStartet
+Name[nds]=DaalladenHettStart
+Name[ne]=डाउनलोड सà¥à¤°à¥ गरियो
+Name[nl]=DownloadGestart
+Name[nn]=NedlastingStarta
+Name[pa]=ਡਾਊਨਲੋਡ ਸ਼à©à¨°à©‚
+Name[pl]=Pobieranie rozpoczęte
+Name[pt_BR]=DownloadIniciado
+Name[ru]=ЗапуÑкЗагрузки
+Name[sk]=Spustené sťahovanie
+Name[sl]=PrenosPriÄetek
+Name[sr]=Преузимање покренуто
+Name[sr@Latn]=Preuzimanje pokrenuto
+Name[sv]=Nerladdning startad
+Name[tr]=İndirme Başladı
+Name[uk]=Почато звантаженнÑ
+Name[zh_CN]=开始下载
+Name[zh_HK]=下載已開始
+Name[zh_TW]=開始下載
+Comment=Downloading started
+Comment[be]=СцÑгванне пачалоÑÑ
+Comment[bg]=ИзтеглÑнето започна
+Comment[bn]=ডাউনলোড আরমà§à¦­ করল
+Comment[br]=Kroget eo an enkargañ
+Comment[bs]=Pokrenut download
+Comment[ca]=S'ha iniciat la descàrrega
+Comment[cs]=Bylo zahájeno stahování
+Comment[da]=Download startet
+Comment[de]=Herunterladen gestartet
+Comment[el]=Η λήψη των αÏχείων ξεκίνησε
+Comment[es]=Una descarga comenzó
+Comment[et]=Allalaadimine alustatud
+Comment[eu]=Deskarga hasi da
+Comment[fa]=بارگیری آغاز شد
+Comment[fi]=Tiedoston hakeminen alkoi
+Comment[fr]=Téléchargement démarré
+Comment[gl]=Comezou a descarga
+Comment[he]=ההורדה התחילה
+Comment[hu]=Letöltés kezdődött
+Comment[is]=Byrjað á niðurhali
+Comment[it]=Scaricamento avviato
+Comment[ja]=ダウンロード開始
+Comment[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ˜áƒ¬áƒ§áƒ
+Comment[kk]=Жүктеп алу тапÑырмаÑÑ‹ баÑталды
+Comment[km]=បាន​ចាប់ផ្ដើម​ការ​ទាញយក
+Comment[lt]=Atsisiuntimas pradÄ—tas
+Comment[nb]=Nedlasting startet
+Comment[nds]=Daalladen hett start
+Comment[ne]=डाउनलोडिङ सà¥à¤°à¥ भयो
+Comment[nl]=Downloaden is gestart
+Comment[nn]=Nedlasting starta
+Comment[pl]=Pobieranie pliku zostało rozpoczęte
+Comment[pt]=Transferência iniciada
+Comment[pt_BR]=Download iniciado
+Comment[ru]=Загрузка выполнÑетÑÑ
+Comment[sk]=Spustené sťahovanie
+Comment[sl]=PrenaÅ¡anje se je priÄelo
+Comment[sr]=Покренуто је преузимање
+Comment[sr@Latn]=Pokrenuto je preuzimanje
+Comment[sv]=Nerladdning startad
+Comment[tr]=Dosya indirme başladı
+Comment[uk]=Ð—Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¾
+Comment[uz]=Yozib olish boshlandi
+Comment[uz@cyrillic]=Ðзиб олиш бошланди
+Comment[zh_CN]=下载已开始
+Comment[zh_HK]=下載已開始
+Comment[zh_TW]=開始下載
+default_sound=KGet_Started.ogg
+default_presentation=16
+
+[finished]
+Name=DownloadFinished
+Name[bg]=Край на изтеглÑне
+Name[bn]=ডাউনলোড শেষ
+Name[br]=Echu eo an enkargañ
+Name[ca]=Ha acabat la descàrrega
+Name[cs]=Stahování ukonÄeno
+Name[el]=Η λήψη ολοκληÏώθηκε
+Name[es]=DescargaAcabada
+Name[et]=Allalaadimine lõpetatud
+Name[eu]=DeskargaAmaituta
+Name[fa]=پایان بارگیری
+Name[fi]=HakuValmistui
+Name[fr]=Téléchargement terminé
+Name[gl]=DescargaRematada
+Name[he]=הורדההסתיימה
+Name[hu]=LetöltésBefejezve
+Name[is]=Niðurhali lokið
+Name[it]=Scaricamento completato
+Name[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ¡áƒ áƒ£áƒšáƒ“áƒ
+Name[kk]=Жүктеп алу аÑқталды
+Name[km]=បាន​បញ្ចប់​ការ​ទាញយក
+Name[nb]=NedlastingFerdig
+Name[nds]=DaaladenBeendt
+Name[ne]=डाउनलोड समापà¥à¤¤ भयो
+Name[nl]=DownloadVoltooid
+Name[nn]=Nedlasting ferdig
+Name[pa]=ਡਾਊਨਲੋਡ ਮà©à¨•à©°à¨®à¨² ਹੋਇਆ
+Name[pl]=Pobieranie zakończone
+Name[pt_BR]=DownloadConcluído
+Name[ru]=ЗагрузкаЗавершена
+Name[sk]=SÅ¥ahovanie ukonÄené
+Name[sl]=PrenosKonec
+Name[sr]=Преузимање завршено
+Name[sr@Latn]=Preuzimanje završeno
+Name[sv]=Nerladdning klar
+Name[tr]=Ä°ndirme Bitti
+Name[uk]=Ð—Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾
+Name[zh_CN]=下载完æˆ
+Name[zh_HK]=下載已完æˆ
+Name[zh_TW]=下載完æˆ
+Comment=Downloading finished
+Comment[be]=СцÑгванне ÑкончылаÑÑ
+Comment[bg]=ИзтеглÑнето завърши
+Comment[bn]=ডাউনলোড শেষ করল
+Comment[br]=Echu eo an enkargañ
+Comment[bs]=Završen download
+Comment[ca]=Ha acabat la descàrrega
+Comment[cs]=Stahování bylo dokonÄeno
+Comment[da]=Download afsluttet
+Comment[de]=Herunterladen abgeschlossen
+Comment[el]=Η λήψη των αÏχείων ολοκληÏώθηκε
+Comment[es]=Una descarga acabó
+Comment[et]=Allalaadimine lõpetatud
+Comment[eu]=Deskarga amaitu da
+Comment[fa]=بارگیری پایان یاÙت
+Comment[fi]=Tiedoston haku valmistui
+Comment[fr]=Téléchargement terminé
+Comment[gl]=Rematou a descarga
+Comment[he]=ההורדה הסתיימה
+Comment[hu]=Egy letöltés befejeződött
+Comment[is]=Niðurhali lokið
+Comment[it]=Scaricamento completato
+Comment[ja]=ダウンロード完了
+Comment[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ¡áƒ áƒ£áƒšáƒ“áƒ
+Comment[kk]=Жүктеп алу тапÑырмаÑÑ‹ аÑқталды
+Comment[km]=បាន​បញ្ចប់​ការទាញ​យក
+Comment[lt]=Atsisiuntimas baigtas
+Comment[nb]=Nedlasting ferdig
+Comment[nds]=Daalladen beendt
+Comment[ne]=डाउनलोडिङ समापà¥à¤¤ भयो
+Comment[nl]=Downloaden is voltooid
+Comment[nn]=Nedlasting ferdig
+Comment[pl]=Pobieranie pliku zostało zakończone
+Comment[pt]=Terminou uma transferência
+Comment[pt_BR]=Download concluído
+Comment[ru]=Загрузка завершена
+Comment[sk]=SÅ¥ahovanie ukonÄené
+Comment[sl]=PrenaÅ¡anje se je zakljuÄilo
+Comment[sr]=Завршено је преузимање
+Comment[sr@Latn]=Završeno je preuzimanje
+Comment[sv]=Nerladdning klar
+Comment[tr]=Dosya indirme tamamlandı
+Comment[uk]=Ð—Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾
+Comment[uz]=Yozib olish tugadi
+Comment[uz@cyrillic]=Ðзиб олиш тугади
+Comment[zh_CN]=下载已完æˆ
+Comment[zh_HK]=下載已完æˆ
+Comment[zh_TW]=下載已完æˆ
+default_sound=KGet_Finished.ogg
+default_presentation=16
+
+[finishedall]
+Name=AddDownloadsFinished
+Name[bg]=Ð’Ñички файлове Ñа изтеглени
+Name[ca]=Han acabat totes les descàrregues
+Name[cs]=VÅ¡echna stahování dokonÄena
+Name[el]=ΟλοκληÏώθηκε η Ï€Ïοσθήκη λήψεων
+Name[es]=AñadirDescargasAcabadas
+Name[et]=Kõik allalaadimised lõpetatud
+Name[eu]=GehituDeskargakAmaituta
+Name[fa]=پایان بارگیریهای اÙزوده
+Name[fi]=LisääHautValmistui
+Name[fr]=Téléchargements ajoutés terminés
+Name[hu]=FelvettLetöltésBefejeződött
+Name[is]=Lokið að bæta við niðurhölum
+Name[it]=Aggiunti scaricamenti completati
+Name[ja]=ダウンロード追加完了
+Name[kk]=Барлық жүктеу аÑқталды
+Name[km]=បាន​បញ្ចប់​ការ​បន្ážáŸ‚ម​ការ​ទាញ​យក
+Name[nds]=AllDaalladenBeendt
+Name[ne]=डाउनलोड थपà¥à¤¨à¥‡ समापà¥à¤¤ भयो
+Name[nl]=DownloadsToevoegenVoltooid
+Name[pl]=Wszystkie transfery zakończone
+Name[pt_BR]=AdicionaraosDonwloadsConcluídos
+Name[ru]=Ð’ÑеЗагружено
+Name[sk]=Pridanie sÅ¥ahovania ukonÄené
+Name[sl]=VsiPrenosiKonec
+Name[sr]=Додавање преузимања завршено
+Name[sr@Latn]=Dodavanje preuzimanja završeno
+Name[sv]=Tillagda nerladdningar klara
+Name[tr]=Biten Ä°ndirmelere Ekle
+Name[uk]=Ð’ÑÑ– Ð·Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ñ–
+Name[zh_CN]=添加下载完æˆ
+Name[zh_HK]=新增的下載已完æˆ
+Name[zh_TW]=新增下載完æˆ
+Comment=All downloads finished
+Comment[be]=УÑе файлы ÑцÑгнутыÑ
+Comment[bg]=ИзтеглÑнето на вÑичките файлове завърши
+Comment[bn]=সব ডাউনলোড শেষ করল
+Comment[bs]=Završeni svi downloadi
+Comment[ca]=Han acabat totes les descàrregues
+Comment[cs]=VÅ¡echna stahování byla dokonÄena
+Comment[da]=Alle download afsluttet
+Comment[de]=Alle Datentransfers sind abgeschlossen
+Comment[el]=ΟλοκληÏώθηκαν όλες οι λήψεις
+Comment[es]=Todas las descargas acabaron
+Comment[et]=Kõik allalaadimised lõpetatud
+Comment[eu]=Deskarga guztiak amaituta
+Comment[fa]=همۀ بارگیریها پایان یاÙت
+Comment[fi]=Kaikki tiedostojen haut ovat valmistuneet
+Comment[fr]=Tous les téléchargements sont terminés
+Comment[gl]=Tódalas descargar están rematadas
+Comment[he]=כל ההורדות הסתיימו
+Comment[hu]=Minden letöltés befejeződött
+Comment[is]=Öllum niðurhölum lokið
+Comment[it]=Tutti gli scaricamenti completati
+Comment[ja]=ã™ã¹ã¦ã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ãŒå®Œäº†
+Comment[ka]=ყველრჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ დáƒáƒ¡áƒ áƒ£áƒšáƒ“áƒ
+Comment[kk]=Барлық жүктеу тапÑырмалары аÑқталды
+Comment[km]=បាន​បញ្ចប់​ការ​ទាញយក​ទាំងអស់
+Comment[lt]=Visi atsisiuntimai baigti
+Comment[nb]=Alle nedlastinger ferdig
+Comment[nds]=All Daalladen beendt
+Comment[ne]=सबै डाउनलोड समापà¥à¤¤ भयो
+Comment[nl]=Alle downloads zijn voltooid
+Comment[nn]=Alle nedlastingane er ferdige
+Comment[pl]=Pobieranie wszystkich plików zostało zakończone
+Comment[pt]=Todas as transferências terminaram
+Comment[pt_BR]=Todos os downloads foram concluídos
+Comment[ru]=Ð’Ñе загрузки выполнены.
+Comment[sk]=VÅ¡etky sÅ¥ahovania ukonÄené
+Comment[sl]=Vsi prenosi so se zakljuÄili
+Comment[sr]=Сва преузимања Ñу завршена
+Comment[sr@Latn]=Sva preuzimanja su završena
+Comment[sv]=Alla nerladdningar klara
+Comment[tr]=Tüm dosyaların indirilmesi tamamlandı
+Comment[uk]=Ð’ÑÑ– Ð·Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ñ–
+Comment[uz]=Hamma yozib olishlar tugadi
+Comment[uz@cyrillic]=Ҳамма ёзиб олишлар тугади
+Comment[zh_CN]=所有下载都已完æˆ
+Comment[zh_HK]=所有下載已完æˆ
+Comment[zh_TW]=所有下載皆已完æˆ
+default_sound=KGet_Finished_All.ogg
+default_presentation=16
diff --git a/kget/getfilejob.cpp b/kget/getfilejob.cpp
new file mode 100644
index 00000000..3710dda1
--- /dev/null
+++ b/kget/getfilejob.cpp
@@ -0,0 +1,37 @@
+/***************************************************************************
+ getfilejob.cpp - description
+ -------------------
+ begin : Wed May 8 2002
+ copyright : (C) 2002 by Patrick Charbonnier
+ email : pch@freeshell.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "getfilejob.h"
+
+namespace KIO
+{
+
+
+GetFileJob::GetFileJob(const KURL & m_src, const KURL & m_dest):FileCopyJob(m_src, m_dest,-1, false, false, false, false)
+{}
+
+GetFileJob::~GetFileJob()
+{}
+
+/** Return true if the file has been resumed */
+bool GetFileJob::getCanResume()const
+{
+ return m_canResume;
+}
+
+
+}
diff --git a/kget/getfilejob.h b/kget/getfilejob.h
new file mode 100644
index 00000000..04c44258
--- /dev/null
+++ b/kget/getfilejob.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ getfilejob.h - description
+ -------------------
+ begin : Wed May 8 2002
+ copyright : (C) 2002 by Patrick Charbonnier
+ email : pch@freeshell.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef GETFILEJOB_H
+#define GETFILEJOB_H
+
+#include <kio/jobclasses.h>
+
+
+namespace KIO
+{
+
+class GetFileJob:public FileCopyJob
+{
+public:
+ GetFileJob(const KURL & m_src, const KURL & m_dest);
+ ~GetFileJob();
+ bool getCanResume() const;
+};
+}
+
+#endif
diff --git a/kget/http_defaults.h b/kget/http_defaults.h
new file mode 100644
index 00000000..2937dc5b
--- /dev/null
+++ b/kget/http_defaults.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+* http_defaults.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+*
+* email : pch@freeshell.org
+*
+***************************************************************************/
+
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _KIO_HTTP_SLAVE_DEFAULTS_H
+#define _KIO_HTTP_SLAVE_DEFAULTS_H
+
+// CACHE SETTINGS
+#define DEFAULT_MAX_CACHE_SIZE 5120 // 5 MB
+#define DEFAULT_MAX_CACHE_AGE 60*60*24*14 // 14 DAYS
+#define DEFAULT_CACHE_EXPIRE 10 // 10 SECS
+#define DEFAULT_CLEAN_CACHE_INTERVAL 30*60 // 30 MINS
+#define DEFAULT_CACHE_CONTROL KIO::CC_Verify // Verify with remote
+#define CACHE_REVISION "7\n" // Cache version
+
+// DEFAULT USER AGENT KEY - ENABLES OS NAME
+#define DEFAULT_USER_AGENT_KEYS "o" // Show OS
+
+// MAXIMUM AMOUNT OF DATA THAT CAN BE SAFELY SENT OVER IPC
+#define MAX_IPC_SIZE 1024*8
+
+// AMOUNT OF DATA TO OBTAIN FROM THE SERVER BY DEFAULT
+#define DEFAULT_BUF_SIZE 1024*4
+
+// SOME DEFAULT HEADER VALUES
+#define DEFAULT_LANGUAGE_HEADER "en"
+#define DEFAULT_MIME_TYPE "text/html"
+#define DEFAULT_FULL_CHARSET_HEADER "iso-8859-1, utf-8, *"
+#define DEFAULT_PARIAL_CHARSET_HEADER ", utf-8, *"
+
+#define DEFAULT_ACCEPT_HEADER "text/*, image/jpeg, image/png, image/*, */*"
+
+#endif
diff --git a/kget/icons/Makefile.am b/kget/icons/Makefile.am
new file mode 100644
index 00000000..9b3651ce
--- /dev/null
+++ b/kget/icons/Makefile.am
@@ -0,0 +1,2 @@
+kgeticon_ICON = AUTO
+kgeticondir = $(kde_datadir)/kget/icons
diff --git a/kget/icons/cr22-action-bar0.png b/kget/icons/cr22-action-bar0.png
new file mode 100644
index 00000000..234e8527
--- /dev/null
+++ b/kget/icons/cr22-action-bar0.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar1.png b/kget/icons/cr22-action-bar1.png
new file mode 100644
index 00000000..ac7bd14c
--- /dev/null
+++ b/kget/icons/cr22-action-bar1.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar2.png b/kget/icons/cr22-action-bar2.png
new file mode 100644
index 00000000..ff6c7237
--- /dev/null
+++ b/kget/icons/cr22-action-bar2.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar3.png b/kget/icons/cr22-action-bar3.png
new file mode 100644
index 00000000..c741bd4c
--- /dev/null
+++ b/kget/icons/cr22-action-bar3.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar4.png b/kget/icons/cr22-action-bar4.png
new file mode 100644
index 00000000..7390352c
--- /dev/null
+++ b/kget/icons/cr22-action-bar4.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar5.png b/kget/icons/cr22-action-bar5.png
new file mode 100644
index 00000000..418c1149
--- /dev/null
+++ b/kget/icons/cr22-action-bar5.png
Binary files differ
diff --git a/kget/icons/cr22-action-bar6.png b/kget/icons/cr22-action-bar6.png
new file mode 100644
index 00000000..930bd77a
--- /dev/null
+++ b/kget/icons/cr22-action-bar6.png
Binary files differ
diff --git a/kget/icons/cr22-action-kget_dock.png b/kget/icons/cr22-action-kget_dock.png
new file mode 100644
index 00000000..6b0f14f3
--- /dev/null
+++ b/kget/icons/cr22-action-kget_dock.png
Binary files differ
diff --git a/kget/icons/cr22-action-kget_dock_download.png b/kget/icons/cr22-action-kget_dock_download.png
new file mode 100644
index 00000000..7242c864
--- /dev/null
+++ b/kget/icons/cr22-action-kget_dock_download.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_clipboard.png b/kget/icons/cr22-action-tool_clipboard.png
new file mode 100644
index 00000000..3695bdf0
--- /dev/null
+++ b/kget/icons/cr22-action-tool_clipboard.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_delay.png b/kget/icons/cr22-action-tool_delay.png
new file mode 100644
index 00000000..96919575
--- /dev/null
+++ b/kget/icons/cr22-action-tool_delay.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_disconnect.png b/kget/icons/cr22-action-tool_disconnect.png
new file mode 100644
index 00000000..437b9b6d
--- /dev/null
+++ b/kget/icons/cr22-action-tool_disconnect.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_dock.png b/kget/icons/cr22-action-tool_dock.png
new file mode 100644
index 00000000..6dce81ca
--- /dev/null
+++ b/kget/icons/cr22-action-tool_dock.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_drop_target.png b/kget/icons/cr22-action-tool_drop_target.png
new file mode 100644
index 00000000..6b0f14f3
--- /dev/null
+++ b/kget/icons/cr22-action-tool_drop_target.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_expert.png b/kget/icons/cr22-action-tool_expert.png
new file mode 100644
index 00000000..8e5a3472
--- /dev/null
+++ b/kget/icons/cr22-action-tool_expert.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_logwindow.png b/kget/icons/cr22-action-tool_logwindow.png
new file mode 100644
index 00000000..7596e67a
--- /dev/null
+++ b/kget/icons/cr22-action-tool_logwindow.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_normal.png b/kget/icons/cr22-action-tool_normal.png
new file mode 100644
index 00000000..02511b78
--- /dev/null
+++ b/kget/icons/cr22-action-tool_normal.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_offline_mode_off.png b/kget/icons/cr22-action-tool_offline_mode_off.png
new file mode 100644
index 00000000..66c4e7f5
--- /dev/null
+++ b/kget/icons/cr22-action-tool_offline_mode_off.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_offline_mode_on.png b/kget/icons/cr22-action-tool_offline_mode_on.png
new file mode 100644
index 00000000..b98d1945
--- /dev/null
+++ b/kget/icons/cr22-action-tool_offline_mode_on.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_pause.png b/kget/icons/cr22-action-tool_pause.png
new file mode 100644
index 00000000..d529cee7
--- /dev/null
+++ b/kget/icons/cr22-action-tool_pause.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_queue.png b/kget/icons/cr22-action-tool_queue.png
new file mode 100644
index 00000000..43e2110e
--- /dev/null
+++ b/kget/icons/cr22-action-tool_queue.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_restart.png b/kget/icons/cr22-action-tool_restart.png
new file mode 100644
index 00000000..3fa8db76
--- /dev/null
+++ b/kget/icons/cr22-action-tool_restart.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_resume.png b/kget/icons/cr22-action-tool_resume.png
new file mode 100644
index 00000000..1c71524e
--- /dev/null
+++ b/kget/icons/cr22-action-tool_resume.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_shutdown.png b/kget/icons/cr22-action-tool_shutdown.png
new file mode 100644
index 00000000..119e5ef7
--- /dev/null
+++ b/kget/icons/cr22-action-tool_shutdown.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_timer.png b/kget/icons/cr22-action-tool_timer.png
new file mode 100644
index 00000000..e325e6ef
--- /dev/null
+++ b/kget/icons/cr22-action-tool_timer.png
Binary files differ
diff --git a/kget/icons/cr22-action-tool_uselastdir.png b/kget/icons/cr22-action-tool_uselastdir.png
new file mode 100644
index 00000000..12f0413d
--- /dev/null
+++ b/kget/icons/cr22-action-tool_uselastdir.png
Binary files differ
diff --git a/kget/kfileio.cpp b/kget/kfileio.cpp
new file mode 100644
index 00000000..bfd03864
--- /dev/null
+++ b/kget/kfileio.cpp
@@ -0,0 +1,174 @@
+/***************************************************************************
+* kfileio.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+*
+* email : pch@freeshell.org
+*
+***************************************************************************/
+
+// Author: Stefan Taferner <taferner@kde.org>
+
+#include <qapplication.h>
+#include <qstring.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "kfileio.h"
+
+//-----------------------------------------------------------------------------
+QString kFileToString(const QString & aFileName, bool aEnsureNL, bool aVerbose)
+{
+ QCString result;
+
+ QFileInfo info(aFileName);
+ unsigned int readLen;
+ unsigned int len = info.size();
+ QFile file(aFileName);
+
+ // assert(aFileName!=NULL);
+ if (aFileName == NULL)
+ return "";
+
+ if (!info.exists()) {
+ if (aVerbose)
+ KMessageBox::error(qApp->mainWidget(), i18n("The specified file does not exist:\n%1").arg(aFileName));
+ return QString::null;
+ }
+ if (info.isDir()) {
+ if (aVerbose)
+ KMessageBox::error(qApp->mainWidget(), i18n("This is a folder and not a file:\n%1").arg(aFileName));
+ return QString::null;
+ }
+ if (!info.isReadable()) {
+ if (aVerbose)
+ KMessageBox::error(qApp->mainWidget(), i18n("You do not have read permission for the file:\n%1").arg(aFileName));
+ return QString::null;
+ }
+ if (len <= 0)
+ return QString::null;
+
+ if (!file.open(IO_Raw | IO_ReadOnly)) {
+ if (aVerbose)
+ switch (file.status()) {
+ case IO_ReadError:
+ KMessageBox::error(qApp->mainWidget(), i18n("Could not read file:\n%1").arg(aFileName));
+ break;
+ case IO_OpenError:
+ KMessageBox::error(qApp->mainWidget(), i18n("Could not open file:\n%1").arg(aFileName));
+ break;
+ default:
+ KMessageBox::error(qApp->mainWidget(), i18n("Error while reading file:\n%1").arg(aFileName));
+ }
+ return QString::null;
+ }
+
+ result.resize(len + (int) aEnsureNL + 1);
+ readLen = file.readBlock(result.data(), len);
+ if (aEnsureNL && result[len - 1] != '\n') {
+ result[len++] = '\n';
+ readLen++;
+ }
+ result[len] = '\0';
+
+ if (readLen < len) {
+ QString msg = i18n("Could only read %1 bytes of %2.").arg(KGlobal::locale()->formatNumber(readLen,
+ 0)).arg(KGlobal::locale()->formatNumber(len, 0));
+
+ KMessageBox::error(qApp->mainWidget(), msg);
+ return QString::null;
+ }
+
+ kdDebug() << "kFileToString: " << readLen << " bytes read" << endl;
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+static bool kBytesToFile(const char *aBuffer, int len, const QString & aFileName, bool aAskIfExists, bool aBackup, bool aVerbose)
+{
+ QFile file(aFileName);
+ QFileInfo info(aFileName);
+ int writeLen, rc;
+
+ // assert(aFileName!=NULL);
+ if (aFileName.isNull())
+ return false;
+
+ if (info.exists()) {
+ if (aAskIfExists) {
+ QString str = i18n("File %1 exists.\nDo you want to replace it?").arg(aFileName);
+
+ rc = KMessageBox::questionYesNo(qApp->mainWidget(), str, QString::null, i18n("Replace"),KStdGuiItem::cancel());
+ if (rc != KMessageBox::Yes)
+ return FALSE;
+ }
+ if (aBackup) {
+ // make a backup copy
+ QString bakName = aFileName;
+
+ bakName += '~';
+ QFile::remove(bakName);
+ rc = rename(QFile::encodeName(aFileName), QFile::encodeName(bakName));
+ if (rc) {
+ // failed to rename file
+ if (!aVerbose)
+ return FALSE;
+ rc = KMessageBox::warningContinueCancel(qApp->mainWidget(), i18n("Failed to make a backup copy of %1.\nContinue anyway?").arg(aFileName));
+ if (rc != KMessageBox::Continue)
+ return FALSE;
+ }
+ }
+ }
+
+ if (!file.open(IO_Raw | IO_WriteOnly)) {
+ if (aVerbose)
+ switch (file.status()) {
+ case IO_WriteError:
+ KMessageBox::error(qApp->mainWidget(), i18n("Could not write to file:\n%1").arg(aFileName));
+ break;
+ case IO_OpenError:
+ KMessageBox::error(qApp->mainWidget(), i18n("Could not open file for writing:\n%1").arg(aFileName));
+ break;
+ default:
+ KMessageBox::error(qApp->mainWidget(), i18n("Error while writing file:\n%1").arg(aFileName));
+ }
+ return FALSE;
+ }
+
+ writeLen = file.writeBlock(aBuffer, len);
+
+ if (writeLen < 0) {
+ KMessageBox::error(qApp->mainWidget(), i18n("Could not write to file:\n%1").arg(aFileName));
+ return FALSE;
+ } else if (writeLen < len) {
+ QString msg = i18n("Could only write %1 bytes of %2.").arg(KGlobal::locale()->formatNumber(writeLen,
+ 0)).arg(KGlobal::locale()->formatNumber(len,
+ 0));
+
+ KMessageBox::error(qApp->mainWidget(), msg);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool kCStringToFile(const QCString & aBuffer, const QString & aFileName, bool aAskIfExists, bool aBackup, bool aVerbose)
+{
+ return kBytesToFile(aBuffer, aBuffer.length(), aFileName, aAskIfExists, aBackup, aVerbose);
+}
+
+bool kByteArrayToFile(const QByteArray & aBuffer, const QString & aFileName, bool aAskIfExists, bool aBackup, bool aVerbose)
+{
+ return kBytesToFile(aBuffer, aBuffer.size(), aFileName, aAskIfExists, aBackup, aVerbose);
+}
diff --git a/kget/kfileio.h b/kget/kfileio.h
new file mode 100644
index 00000000..9b2780ad
--- /dev/null
+++ b/kget/kfileio.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+* kfileio.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+*
+* email : pch@freeshell.org
+*
+***************************************************************************/
+
+/* Load / save entire (local) files with nice diagnostics dialog messages.
+ * These functions load/save the whole buffer in one i/o call, so they
+ * should be pretty efficient.
+ *
+ * Author: Stefan Taferner <taferner@kde.org>
+ * This code is under GPL.
+ */
+#ifndef kfileio_h
+#define kfileio_h
+
+/** Load a file. Returns a pointer to the memory-block that contains
+ * the loaded file. Returns a NULL string if the file could not be loaded.
+ * If withDialogs is FALSE no warning dialogs are opened if there are
+ * problems.
+ * The string returned is always zero-terminated and therefore one
+ * byte longer than the file itself.
+ * If ensureNewline is TRUE the string will always have a trailing newline.
+ */
+QString kFileToString(const QString & fileName, bool ensureNewline = TRUE, bool withDialogs = TRUE);
+
+/** Save a file. If withDialogs is FALSE no warning dialogs are opened if
+ * there are problems. Returns TRUE on success and FALSE on failure.
+ * Replaces existing files without warning if askIfExists==FALSE.
+ * Makes a copy if the file exists to filename~ if createBackup==TRUE.
+ */
+bool kCStringToFile(const QCString & buffer, const QString & fileName, bool askIfExists = FALSE, bool createBackup = TRUE, bool withDialogs = TRUE);
+
+// Does not stop at NUL
+bool kByteArrayToFile(const QByteArray & buffer, const QString & fileName, bool askIfExists = FALSE, bool createBackup = TRUE, bool withDialogs = TRUE);
+
+
+#endif /* kfileio_h */
diff --git a/kget/kget.desktop b/kget/kget.desktop
new file mode 100644
index 00000000..7230b416
--- /dev/null
+++ b/kget/kget.desktop
@@ -0,0 +1,77 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Exec=kget -caption "%c" %i %m
+Icon=kget
+Terminal=false
+Name=KGet
+Name[ar]=ك.جيت
+Name[bn]=কে-গেট
+Name[cy]=KNôl
+Name[eo]=Prenilo
+Name[hi]=के-गेट
+Name[ne]=केडीई गेट
+Name[sv]=Kget
+Name[ta]=கேகெடà¯
+Name[th]=ดาวน์โหลด K
+Name[ven]=Wana ha K
+GenericName=Download Manager
+GenericName[ar]=إدارة التحميل
+GenericName[be]=Праграма ÑцÑÐ³Ð²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž
+GenericName[bg]=ИзтеглÑне на файлове
+GenericName[bn]=ডাউনলোড মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦°
+GenericName[br]=Merour enkargañ
+GenericName[bs]=Upravitelj downloadom
+GenericName[ca]=Gestor de descàrregues
+GenericName[cs]=Správce stahování
+GenericName[cy]=Rheolydd Lawrlwytho
+GenericName[da]=HÃ¥ndtering af download
+GenericName[de]=Datentransfer-Verwaltung
+GenericName[el]=ΔιαχειÏιστής λήψεων αÏχείων
+GenericName[eo]=ElÅut-administrilo
+GenericName[es]=Gestor de descargas
+GenericName[et]=Allalaadimiste haldur
+GenericName[eu]=Deskarga kudeatzailea
+GenericName[fa]=مدیر بارگیری
+GenericName[fi]=Tiedostonlataaja
+GenericName[fr]=Gestionnaire de téléchargements
+GenericName[ga]=Bainisteoir Thíosluchtaithe
+GenericName[gl]=Xestor de Descargas
+GenericName[he]=מנהל הורדות
+GenericName[hi]=डाउनलोड पà¥à¤°à¤¬à¤‚धक
+GenericName[hr]=Upravitelj preuzimanja
+GenericName[hu]=Letöltéskezelő
+GenericName[is]=Niðurhalsstjóri
+GenericName[it]=Gestore degli scaricamenti
+GenericName[ja]=ダウンロードマãƒãƒ¼ã‚¸ãƒ£
+GenericName[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒáƒ—რმმáƒáƒ áƒ—ველი
+GenericName[kk]=Жүкеп алу менеджері
+GenericName[km]=កម្មវិធី​គ្រប់គ្រង​ការ​ទាញយក
+GenericName[lt]=Siuntimų valdymas
+GenericName[mk]=Менаџер за Ñимнувања
+GenericName[nb]=Nedlastingsbehandler
+GenericName[nds]=Daallaadpleger
+GenericName[ne]=डाउनलोड पà¥à¤°à¤¬à¤¨à¥à¤§à¤•
+GenericName[nl]=Downloadmanager
+GenericName[nn]=Nedlastingshandsamar
+GenericName[pa]=ਡਾਊਨਲੋਡ ਮੈਨੇਜਰ
+GenericName[pl]=Menedżer pobierania
+GenericName[pt]=Gestor de Transferências
+GenericName[pt_BR]=Gerenciador de Download
+GenericName[ru]=ДиÑпетчер загрузок
+GenericName[sk]=Správca sťahovania
+GenericName[sl]=Upravitelj prenosov
+GenericName[sr]=Менаџер преузимања
+GenericName[sr@Latn]=Menadžer preuzimanja
+GenericName[sv]=Nerladdningshanterare
+GenericName[ta]=பதிவிறகà¯à®• மேலாளரà¯
+GenericName[tg]=Мудири Боркунӣ
+GenericName[tr]=İndirme Yöneticisi
+GenericName[uk]=Менеджер звантажень
+GenericName[uz]=Yozib olish boshqaruvchisi
+GenericName[uz@cyrillic]=Ðзиб олиш бошқарувчиÑи
+GenericName[zh_CN]=下载管ç†å™¨
+GenericName[zh_HK]=下載管ç†å“¡
+GenericName[zh_TW]=下載管ç†å“¡
+Categories=Qt;KDE;Network;FileTransfer;
+X-DCOP-ServiceType=Unique
diff --git a/kget/kget_download.desktop b/kget/kget_download.desktop
new file mode 100644
index 00000000..89654b8e
--- /dev/null
+++ b/kget/kget_download.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Actions=KGetDownload;
+Icon=
+ServiceTypes=all/allfiles
+ExcludeServiceTypes=kdedevice/*
+
+[Desktop Action KGetDownload]
+Exec=kget %U
+Icon=kget
+Name=Download with KGet
+Name[be]=СцÑгнуць праз KGet
+Name[bg]=ИзтеглÑне Ñ KGet
+Name[bn]=কে-গেট দিয়ে ডাউনলোড
+Name[br]=Enkargañ gant KGet
+Name[bs]=Download sa KGet-om
+Name[ca]=Descarrega amb el KGet
+Name[cs]=Stáhnout pomocí KGet
+Name[da]=Download med KGet
+Name[de]=Mit KGet herunterladen
+Name[el]=Λήψη αÏχείου με το KGet
+Name[es]=Descarga con KGet
+Name[et]=Laadi alla KGeti abil
+Name[eu]=Deskargatu KGet-ekin
+Name[fa]=بارگیری با KGet
+Name[fi]=Hae KGet ohjelmalla
+Name[fr]=Télécharger avec KGet
+Name[ga]=Ãosluchtaigh le KGet
+Name[gl]=Descargar con KGet
+Name[he]=הורד בעזרת KGet
+Name[hu]=Letöltés a KGettel
+Name[is]=Sækja með KGet
+Name[it]=Scarica con KGet
+Name[ja]=KGet ã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰
+Name[ka]=KGet-ით ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒ
+Name[kk]=KGet көмегімен жүктеп алу
+Name[km]=ទាញយក​ដោយ​ប្រើ KGet
+Name[lt]=Atsisiųsti su KGet
+Name[nb]=Nedlasting med KGet
+Name[nds]=Dalladen mit KGet
+Name[ne]=केडीई गेटसà¤à¤— डाउनलोड गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Name[nl]=Met KGet downloaden
+Name[nn]=Last ned med KGet
+Name[pa]=KGet ਨਾਲ ਡਾਊਨਲੋਡ
+Name[pl]=Pobierz za pomocÄ… KGet
+Name[pt]=Obter com o KGet
+Name[pt_BR]=Baixar com o KGet
+Name[ru]=Загрузить Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ KGet
+Name[sk]=Stiahnuť pomocou KGet
+Name[sl]=Prenos s KGet
+Name[sr]=Преузми Ñа KGet-ом
+Name[sr@Latn]=Preuzmi sa KGet-om
+Name[sv]=Ladda ner med Kget
+Name[tr]=KGet ile Ä°ndir
+Name[uk]=Звантажити з KGet
+Name[zh_CN]=用 KGet 下载
+Name[zh_HK]=以 KGet 下載
+Name[zh_TW]=用 KGet 下載
diff --git a/kget/kget_iface.h b/kget/kget_iface.h
new file mode 100644
index 00000000..015bef2f
--- /dev/null
+++ b/kget/kget_iface.h
@@ -0,0 +1,37 @@
+/****************************************************************************
+** $Id$
+**
+** Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+**
+****************************************************************************/
+
+#ifndef KGET_IFACE_H
+#define KGET_IFACE_H
+
+#include <dcopobject.h>
+#include <kurl.h>
+
+class KGetIface : public DCOPObject
+{
+ K_DCOP
+
+protected:
+ KGetIface( QCString objId ) : DCOPObject( objId ) {}
+
+k_dcop:
+ /**
+ * @param src The urls to download
+ * @param destDir The destination direction or QString::null if unspecified
+ */
+ virtual ASYNC addTransfers( const KURL::List& src, const QString& destDir = QString::null ) = 0;
+
+ virtual bool isDropTargetVisible() const = 0;
+
+ virtual void setDropTargetVisible( bool setVisible ) = 0;
+
+ virtual void setOfflineMode( bool offline ) = 0;
+
+ virtual bool isOfflineMode() const = 0;
+};
+
+#endif // KGET_IFACE_H
diff --git a/kget/kget_plug_in/Makefile.am b/kget/kget_plug_in/Makefile.am
new file mode 100644
index 00000000..4e2afd67
--- /dev/null
+++ b/kget/kget_plug_in/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = $(all_includes)
+
+kde_module_LTLIBRARIES = khtml_kget.la
+
+khtml_kget_la_METASOURCES = AUTO
+khtml_kget_la_SOURCES = kget_plug_in.cpp kget_linkview.cpp links.cpp
+khtml_kget_la_LIBADD = $(LIB_KHTML)
+khtml_kget_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+
+KDE_ICON = AUTO
+
+part_DATA = kget_plug_in.rc kget_plug_in.desktop
+partdir = $(kde_datadir)/khtml/kpartplugins
diff --git a/kget/kget_plug_in/cr22-action-khtml_kget.png b/kget/kget_plug_in/cr22-action-khtml_kget.png
new file mode 100644
index 00000000..6b0f14f3
--- /dev/null
+++ b/kget/kget_plug_in/cr22-action-khtml_kget.png
Binary files differ
diff --git a/kget/kget_plug_in/kget_linkview.cpp b/kget/kget_plug_in/kget_linkview.cpp
new file mode 100644
index 00000000..179cc81b
--- /dev/null
+++ b/kget/kget_plug_in/kget_linkview.cpp
@@ -0,0 +1,150 @@
+#include "kget_linkview.h"
+
+#include <qlayout.h>
+
+#include <dcopclient.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <klistviewsearchline.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kstdaction.h>
+#include <ktoolbar.h>
+
+#define COL_NAME 0
+#define COL_DESC 1
+#define COL_MIME 2
+#define COL_URL 3
+
+LinkViewItem::LinkViewItem( QListView *parent, const LinkItem *lnk )
+ : QListViewItem( parent ),
+ link( lnk )
+{
+ QString file = link->url.fileName();
+ if ( file.isEmpty() )
+ file = link->url.host();
+
+ setPixmap( COL_NAME, SmallIcon( link->icon ) );
+ setText( COL_NAME, file );
+
+ setText( COL_DESC, link->text );
+ setText( COL_MIME, link->mimeType );
+ setText( COL_URL, link->url.prettyURL() );
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+KGetLinkView::KGetLinkView( QWidget *parent, const char *name )
+ : KMainWindow( parent, name )
+{
+ setPlainCaption( i18n( "KGet" ) );
+
+ KAction* actionDownload = new KAction( i18n("Download Selected Files"),
+ "kget", CTRL+Key_D,
+ this, SLOT( slotStartLeech() ),
+ actionCollection(), "startDownload" );
+
+ KAction* actionSelectAll = KStdAction::selectAll( this, SLOT( slotSelectAll() ),
+ actionCollection() );
+
+ m_links.setAutoDelete( true );
+ actionDownload->plug( toolBar() );
+ toolBar()->insertLineSeparator();
+ actionSelectAll->plug( toolBar() );
+
+ QWidget *mainWidget = new QWidget( this );
+ QVBoxLayout *layout = new QVBoxLayout( mainWidget );
+ setCentralWidget( mainWidget );
+
+ m_view = new KListView( mainWidget, "listview" );
+ m_view->setSelectionMode( QListView::Extended );
+ m_view->addColumn( i18n("File Name") );
+ m_view->addColumn( i18n("Description") );
+ m_view->addColumn( i18n("File Type") );
+ m_view->addColumn( i18n("Location (URL)") );
+ m_view->setShowSortIndicator( true );
+
+ KListViewSearchLineWidget *line = new KListViewSearchLineWidget( m_view, mainWidget, "search line" );
+ layout->addWidget( line );
+ layout->addWidget( m_view );
+
+ // setting a fixed (not floating) toolbar
+ toolBar()->setMovingEnabled( false );
+ // setting Text next to Icons
+ toolBar()->setIconText( KToolBar::IconTextRight );
+}
+
+KGetLinkView::~KGetLinkView()
+{
+}
+
+void KGetLinkView::setLinks( QPtrList<LinkItem>& links )
+{
+ m_links = links; // now we 0wn them
+ showLinks( m_links );
+}
+
+void KGetLinkView::showLinks( const QPtrList<LinkItem>& links )
+{
+ m_view->clear();
+
+ QPtrListIterator<LinkItem> it( links );
+ for ( ; it.current(); ++it )
+ (void) new LinkViewItem( m_view, *it );
+
+ m_view->sort();
+}
+
+void KGetLinkView::slotStartLeech()
+{
+ KURL::List urls;
+ QListViewItemIterator it( m_view->firstChild() );
+ for ( ; it.current(); ++it )
+ {
+ if ( it.current()->isSelected() )
+ urls.append( static_cast<LinkViewItem*>( it.current() )->link->url );
+ }
+
+ if ( urls.isEmpty() )
+ KMessageBox::sorry( this,
+ i18n("You did not select any files to download."),
+ i18n("No Files Selected") );
+ else
+ {
+ DCOPClient* p_dcopServer = new DCOPClient();
+ p_dcopServer->attach();
+
+ if ( !p_dcopServer->isApplicationRegistered( "kget" ) )
+ {
+ KApplication::startServiceByDesktopName( "kget" );
+ }
+ kapp->updateRemoteUserTimestamp( "kget" );
+
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly );
+ stream << urls << QString::null;
+ bool ok = DCOPClient::mainClient()->send( "kget", "KGet-Interface",
+ "addTransfers(KURL::List, QString)",
+ data );
+
+ kdDebug() << "*** startDownload: " << ok << endl;
+
+ p_dcopServer->detach();
+ delete p_dcopServer;
+ }
+}
+
+void KGetLinkView::setPageURL( const QString& url )
+{
+ setPlainCaption( i18n( "Links in: %1 - KGet" ).arg( url ) );
+}
+
+void KGetLinkView::slotSelectAll()
+{
+ m_view->selectAll( true );
+}
+
+#include "kget_linkview.moc"
diff --git a/kget/kget_plug_in/kget_linkview.h b/kget/kget_plug_in/kget_linkview.h
new file mode 100644
index 00000000..a6d7961c
--- /dev/null
+++ b/kget/kget_plug_in/kget_linkview.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+** $Id$
+**
+** Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+**
+****************************************************************************/
+
+#ifndef KGET_LINKVIEW_H
+#define KGET_LINKVIEW_H
+
+#include <qptrlist.h>
+
+#include <klistview.h>
+#include <kmainwindow.h>
+#include <kurl.h>
+
+#include "links.h"
+
+class LinkViewItem : public QListViewItem
+{
+public:
+ LinkViewItem( QListView *parent, const LinkItem * lnk );
+ const LinkItem *link;
+};
+
+
+class KGetLinkView : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ KGetLinkView( QWidget *parent = 0L, const char *name = 0L );
+ ~KGetLinkView();
+
+ void setLinks( QPtrList<LinkItem>& links );
+ void setPageURL( const QString& url );
+
+signals:
+ void leechURLs( const KURL::List& urls );
+
+private slots:
+ void slotStartLeech();
+ void slotSelectAll();
+
+private:
+ void showLinks( const QPtrList<LinkItem>& links );
+
+ QPtrList<LinkItem> m_links;
+
+ KListView *m_view;
+
+};
+
+#endif // KGET_LINKVIEW_H
diff --git a/kget/kget_plug_in/kget_plug_in.cpp b/kget/kget_plug_in/kget_plug_in.cpp
new file mode 100644
index 00000000..dc5f5b9f
--- /dev/null
+++ b/kget/kget_plug_in/kget_plug_in.cpp
@@ -0,0 +1,189 @@
+/***************************************************************************
+ kget_plug_in.cpp - description
+ -------------------
+ begin : Wed Jul 3 22:09:28 CEST 2002
+ copyright : (C) 2002 by Patrick
+ email : pch@valleeurpe.net
+
+ Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "kget_plug_in.h"
+
+#include <dcopref.h>
+#include <kdatastream.h>
+#include <kdebug.h>
+#include <khtml_part.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kaction.h>
+#include <kinstance.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <krun.h>
+
+#include <dom/html_document.h>
+#include <dom/html_misc.h>
+#include <dom/dom_element.h>
+
+#include <kparts/partmanager.h>
+
+#include <set>
+
+#include "links.h"
+#include "kget_linkview.h"
+
+KGet_plug_in::KGet_plug_in( QObject* parent, const char* name )
+ : Plugin( parent, name )
+{
+ QPixmap pix = KGlobal::iconLoader()->loadIcon("kget",
+ KIcon::MainToolbar);
+ KActionMenu *menu = new KActionMenu( i18n("Download Manager"), pix,
+ actionCollection(), "kget_menu" );
+ menu->setDelayed( false );
+ connect( menu->popupMenu(), SIGNAL( aboutToShow() ), SLOT( showPopup() ));
+
+ m_paToggleDropTarget=new KToggleAction(i18n("Show Drop Target"),
+ KShortcut(),
+ this, SLOT(slotShowDrop()),
+ actionCollection(), "show_drop" );
+
+ menu->insert( m_paToggleDropTarget );
+
+ KAction *action = new KAction(i18n("List All Links"), KShortcut(),
+ this, SLOT( slotShowLinks() ),
+ actionCollection(), "show_links");
+ menu->insert( action );
+
+ p_dcopServer= new DCOPClient();
+ p_dcopServer->attach ();
+}
+
+
+KGet_plug_in::~KGet_plug_in()
+{
+ p_dcopServer->detach();
+ delete p_dcopServer;
+}
+
+
+void KGet_plug_in::showPopup()
+{
+ bool hasDropTarget = false;
+
+ if (p_dcopServer->isApplicationRegistered ("kget"))
+ {
+ DCOPRef kget( "kget", "KGet-Interface" );
+ hasDropTarget = kget.call( "isDropTargetVisible" );
+ }
+
+ m_paToggleDropTarget->setChecked( hasDropTarget );
+}
+
+void KGet_plug_in::slotShowDrop()
+{
+ if (!p_dcopServer->isApplicationRegistered ("kget"))
+ KRun::runCommand("kget --showDropTarget");
+ else
+ {
+ DCOPRef kget( "kget", "KGet-Interface" );
+ kget.send( "setDropTargetVisible", m_paToggleDropTarget->isChecked());
+ }
+}
+
+void KGet_plug_in::slotShowLinks()
+{
+ if ( !parent() || !parent()->inherits( "KHTMLPart" ) )
+ return;
+
+ KHTMLPart *htmlPart = static_cast<KHTMLPart*>( parent() );
+ KParts::Part *activePart = 0L;
+ if ( htmlPart->partManager() )
+ {
+ activePart = htmlPart->partManager()->activePart();
+ if ( activePart && activePart->inherits( "KHTMLPart" ) )
+ htmlPart = static_cast<KHTMLPart*>( activePart );
+ }
+
+ DOM::HTMLDocument doc = htmlPart->htmlDocument();
+ if ( doc.isNull() )
+ return;
+
+ DOM::HTMLCollection links = doc.links();
+
+ QPtrList<LinkItem> linkList;
+ std::set<QString> dupeCheck;
+ for ( uint i = 0; i < links.length(); i++ )
+ {
+ DOM::Node link = links.item( i );
+ if ( link.isNull() || link.nodeType() != DOM::Node::ELEMENT_NODE )
+ continue;
+
+ LinkItem *item = new LinkItem( (DOM::Element) link );
+ if ( item->isValid() &&
+ dupeCheck.find( item->url.url() ) == dupeCheck.end() )
+ {
+ linkList.append( item );
+ dupeCheck.insert( item->url.url() );
+ }
+ else
+ delete item;
+ }
+
+ if ( linkList.isEmpty() )
+ {
+ KMessageBox::sorry( htmlPart->widget(),
+ i18n("There are no links in the active frame of the current HTML page."),
+ i18n("No Links") );
+ return;
+ }
+
+ KGetLinkView *view = new KGetLinkView();
+ QString url = doc.URL().string();
+ view->setPageURL( url );
+
+ view->setLinks( linkList );
+ view->show();
+}
+
+KPluginFactory::KPluginFactory( QObject* parent, const char* name )
+ : KLibFactory( parent, name )
+{
+ s_instance = new KInstance("KPluginFactory");
+}
+
+QObject* KPluginFactory::createObject( QObject* parent, const char* name, const char*, const QStringList & )
+{
+ QObject *obj = new KGet_plug_in( parent, name );
+ return obj;
+}
+
+KPluginFactory::~KPluginFactory()
+{
+ delete s_instance;
+}
+
+extern "C"
+{
+ KDE_EXPORT void* init_khtml_kget()
+ {
+ KGlobal::locale()->insertCatalogue("kget");
+ return new KPluginFactory;
+ }
+
+}
+
+KInstance* KPluginFactory::s_instance = 0L;
+
+#include "kget_plug_in.moc"
diff --git a/kget/kget_plug_in/kget_plug_in.desktop b/kget/kget_plug_in/kget_plug_in.desktop
new file mode 100644
index 00000000..8976c2f4
--- /dev/null
+++ b/kget/kget_plug_in/kget_plug_in.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+X-KDE-Library=khtml_kget
+X-KDE-PluginInfo-Author=Patrick Charbonnier, Carsten Pfeiffer
+X-KDE-PluginInfo-Email=pch@valleeurope.net, pfeiffer@kde.org
+X-KDE-PluginInfo-Name=kget
+X-KDE-PluginInfo-Version=3.4
+X-KDE-PluginInfo-Website=http://kget.sourceforge.net
+X-KDE-PluginInfo-Category=Tools
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+Name=KGet
+Name[ar]=ك.جيت
+Name[bn]=কে-গেট
+Name[cy]=KNôl
+Name[eo]=Prenilo
+Name[hi]=के-गेट
+Name[ne]=केडीई गेट
+Name[sv]=Kget
+Name[ta]=கேகெடà¯
+Name[th]=ดาวน์โหลด K
+Name[ven]=Wana ha K
+Comment=Download Manager
+Comment[be]=Праграма ÑцÑÐ³Ð²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð°Ñž
+Comment[bg]=ИзтеглÑне на файлове
+Comment[bn]=ডাউনলোড মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦°
+Comment[br]=Merour enkargañ
+Comment[bs]=Upravitelj downloadom
+Comment[ca]=Gestor de descàrregues
+Comment[cs]=Správce stahování
+Comment[cy]=Trefnydd Lawrlwytho
+Comment[da]=HÃ¥ndtering af download
+Comment[de]=Herunterladen von Dateien
+Comment[el]=ΔιαχειÏιστής λήψεων αÏχείων
+Comment[eo]=ElÅut-administrilo
+Comment[es]=Gestor de descargas
+Comment[et]=Allalaadimiste haldur
+Comment[eu]=Deskarga kudeatzailea
+Comment[fa]=مدیر بارگیری
+Comment[fi]=Tiedostonlataaja
+Comment[fr]=Gestionnaire de téléchargements
+Comment[ga]=Bainisteoir Ãosluchtaithe
+Comment[gl]=Xestor de Descargas
+Comment[he]=מנהל הורדות
+Comment[hu]=Letöltéskezelő
+Comment[is]=Niðurhalsstjóri
+Comment[it]=Gestore degli scaricamenti
+Comment[ja]=ダウンロードマãƒãƒ¼ã‚¸ãƒ£
+Comment[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒáƒ—რმმáƒáƒ áƒ—ველი
+Comment[kk]=Жүктеп алу менеджері
+Comment[km]=កម្មវិធី​គ្រប់គ្រង​ការ​ទាញ​យក
+Comment[lt]=Siuntimų valdymas
+Comment[nb]=Nedlastingsbehandler
+Comment[nds]=Daallaadpleger
+Comment[ne]=डाउनलोड पà¥à¤°à¤¬à¤¨à¥à¤§à¤•
+Comment[nl]=Downloadmanager
+Comment[nn]=Nedlastingshandsamar
+Comment[pa]=ਡਾਊਨਲੋਡ ਮੈਨੇਜਰ
+Comment[pl]=Menedżer pobierania
+Comment[pt]=Gestor de Transferências
+Comment[pt_BR]=Gerenciador de Download
+Comment[ro]=Manager de transferuri FTP
+Comment[ru]=ДиÑпетчер загрузок
+Comment[sk]=Správca sťahovania
+Comment[sl]=Upravitelj prenosov
+Comment[sr]=Менаџер преузимања
+Comment[sr@Latn]=Menadžer preuzimanja
+Comment[sv]=Nerladdningshanterare
+Comment[tr]=İndirme Yöneticisi
+Comment[uk]=Менеджер звантажень
+Comment[uz]=Yozib olish boshqaruvchisi
+Comment[uz@cyrillic]=Ðзиб олиш бошқарувчиÑи
+Comment[zh_CN]=下载管ç†å™¨
+Comment[zh_HK]=下載管ç†å“¡
+Comment[zh_TW]=下載管ç†å“¡
+Icon=khtml_kget
+
diff --git a/kget/kget_plug_in/kget_plug_in.h b/kget/kget_plug_in/kget_plug_in.h
new file mode 100644
index 00000000..75888b10
--- /dev/null
+++ b/kget/kget_plug_in/kget_plug_in.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ kget_plug_in.h - description
+ -------------------
+ begin : Wed Jul 3 22:09:28 CEST 2002
+ copyright : (C) 2002 by Patrick
+ email : pch@valleeurpe.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 __plugin_kget_plug_in_h
+#define __plugin_kget_plug_in_h
+
+#include <kparts/plugin.h>
+#include <klibloader.h>
+#include <dcopclient.h>
+#include <kurl.h>
+
+class KInstance;
+
+class KGet_plug_in : public KParts::Plugin
+{
+ Q_OBJECT
+public:
+ KGet_plug_in( QObject* parent = 0, const char* name = 0 );
+ KToggleAction *m_paToggleDropTarget ;
+ DCOPClient* p_dcopServer;
+ virtual ~KGet_plug_in();
+
+private slots:
+ void slotShowDrop();
+ void slotShowLinks();
+ void showPopup();
+};
+
+
+class KPluginFactory : public KLibFactory
+{
+ Q_OBJECT
+public:
+ KPluginFactory( QObject *parent = 0, const char *name = 0 );
+ ~KPluginFactory() ;
+
+ virtual QObject* createObject( QObject* parent = 0, const char* pname = 0,
+ const char* name = "QObject",
+ const QStringList &args = QStringList() );
+
+private:
+ static KInstance* s_instance;
+};
+
+#endif
diff --git a/kget/kget_plug_in/kget_plug_in.rc b/kget/kget_plug_in/kget_plug_in.rc
new file mode 100644
index 00000000..f40ee8b7
--- /dev/null
+++ b/kget/kget_plug_in/kget_plug_in.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui>
+<kpartgui library="khtml_kget" name="khtml_kget" version="3" >
+<MenuBar>
+ <Menu name="tools"><Text>&amp;Tools</Text>
+ <Action name="kget_menu"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar">
+ <Action name="kget_menu"/>
+</ToolBar>
+</kpartgui>
diff --git a/kget/kget_plug_in/links.cpp b/kget/kget_plug_in/links.cpp
new file mode 100644
index 00000000..a597257d
--- /dev/null
+++ b/kget/kget_plug_in/links.cpp
@@ -0,0 +1,41 @@
+#include "links.h"
+
+#include <kmimetype.h>
+#include <kprotocolinfo.h>
+
+#include <dom/html_misc.h>
+#include <dom/html_document.h>
+
+LinkItem::LinkItem( DOM::Element link )
+ : m_valid( false )
+{
+ DOM::NamedNodeMap attrs = link.attributes();
+ DOM::Node href = attrs.getNamedItem( "href" );
+
+ // qDebug("*** href: %s", href.nodeValue().string().latin1() );
+
+ QString urlString = link.ownerDocument().completeURL( href.nodeValue() ).string();
+ if ( urlString.isEmpty() )
+ return;
+
+ url = KURL::fromPathOrURL( urlString );
+ if ( !KProtocolInfo::supportsReading( url ) )
+ return;
+
+
+ // somehow getElementsByTagName("#text") doesn't work :(
+ DOM::NodeList children = link.childNodes();
+ for ( uint i = 0; i < children.length(); i++ )
+ {
+ DOM::Node node = children.item( i );
+ if ( node.nodeType() == DOM::Node::TEXT_NODE )
+ text.append( node.nodeValue().string() );
+ }
+
+ // force "local file" mimetype determination
+ KMimeType::Ptr mt = KMimeType::findByURL( url, 0, true, true);
+ icon = mt->icon( QString::null, false ); // dummy parameters
+ mimeType = mt->comment();
+
+ m_valid = true;
+}
diff --git a/kget/kget_plug_in/links.h b/kget/kget_plug_in/links.h
new file mode 100644
index 00000000..89ba9ab5
--- /dev/null
+++ b/kget/kget_plug_in/links.h
@@ -0,0 +1,32 @@
+/****************************************************************************
+** $Id$
+**
+** Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+**
+****************************************************************************/
+
+#ifndef LINKS_H
+#define LINKS_H
+
+#include <dom/dom_element.h>
+
+#include <kurl.h>
+
+class LinkItem
+{
+public:
+ LinkItem( DOM::Element link );
+
+ KURL url;
+ QString icon;
+ QString text;
+ QString mimeType;
+
+ bool isValid() const { return m_valid; }
+
+private:
+ bool m_valid : 1;
+};
+
+
+#endif // LINKS_H
diff --git a/kget/kgetui.rc b/kget/kgetui.rc
new file mode 100644
index 00000000..6f746e79
--- /dev/null
+++ b/kget/kgetui.rc
@@ -0,0 +1,71 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kget" version="4">
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="open_transfer"/>
+ <Action name="paste_transfer"/>
+ <Separator/>
+ <Action name="export_transfers"/>
+ <Action name="import_transfers"/>
+ <Action name="import_text"/>
+ <Separator/>
+ <Action name="quit"/>
+ </Menu>
+ <Menu name="transfer"><text>&amp;Transfer</text>
+ <Action name="copy_url"/>
+ <Action name="open_individual"/>
+ <Separator/>
+ <Action name="move_begin"/>
+ <Action name="move_end"/>
+ <Separator/>
+ <Action name="resume"/>
+ <Action name="pause"/>
+ <Action name="delete"/>
+ <Action name="restart"/>
+ <Separator/>
+ <Action name="queue"/>
+ <Action name="timer"/>
+ <Action name="delay"/>
+ </Menu>
+ <Menu noMerge="1" name="view"><text>&amp;View</text>
+ <Action name="toggle_log"/>
+ </Menu>
+ <Menu name="options"><text>&amp;Options</text>
+ <Action name="toggle_animation"/>
+ <Action name="toggle_sound"/>
+ <Separator/>
+ <Action name="expert_mode"/>
+ <Action name="use_last_dir"/>
+ <Action name="offline_mode"/>
+ <Separator/>
+ <Action name="auto_disconnect"/>
+ <Action name="auto_shutdown"/>
+ <Action name="auto_paste"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="drop_target"/>
+ <Action name="show_statusbar"/>
+ <Action name="settings_showmenubar" append="show_merge"/>
+ </Menu>
+ <Menu name="help"><text>&amp;Help</text>
+ </Menu>
+</MenuBar>
+<ToolBar fullWidth="true" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="open_transfer" />
+ <Action name="paste_transfer"/>
+ <Separator/>
+ <Action name="resume"/>
+ <Action name="pause"/>
+ <Action name="delete"/>
+ <Action name="restart"/>
+ <Separator/>
+ <Action name="queue"/>
+ <Action name="delay"/>
+ <Separator/>
+ <Action name="auto_disconnect"/>
+ <Action name="auto_shutdown"/>
+ <Action name="offline_mode"/>
+ <Separator/>
+ <Action name="drop_target"/>
+</ToolBar>
+</kpartgui>
diff --git a/kget/kmainwidget.cpp b/kget/kmainwidget.cpp
new file mode 100644
index 00000000..420d44a1
--- /dev/null
+++ b/kget/kmainwidget.cpp
@@ -0,0 +1,2548 @@
+/***************************************************************************
+* kmainwidget.cpp
+* -------------------
+*
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+* Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __svr4__
+#define map BULLSHIT // on Solaris it conflicts with STL ?
+#include <net/if.h>
+#undef map
+#include <sys/sockio.h> // needed for SIOCGIFFLAGS
+#else
+#include <net/if.h>
+#endif
+
+#include <qclipboard.h>
+#include <qregexp.h>
+#include <qdragobject.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+#include <qtimer.h>
+#include <qdropsite.h>
+#include <qpopupmenu.h>
+#include <qfileinfo.h>
+#include <kinputdialog.h>
+
+#include <kprotocolinfo.h>
+#include <kfiledialog.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <kurl.h>
+#include <kurldrag.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kwin.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+#include <khelpmenu.h>
+#include <kedittoolbar.h>
+#include <kstatusbar.h>
+#include <kconfig.h>
+#include <kio/netaccess.h>
+#include <knotifyclient.h>
+#include <knotifydialog.h>
+#include <kmenubar.h>
+#include <kio/renamedlg.h>
+
+#include "safedelete.h"
+#include "settings.h"
+#include "transfer.h"
+#include "transferlist.h"
+#include "kmainwidget.h"
+#include "kfileio.h"
+#include "dlgPreferences.h"
+#include "logwindow.h"
+#include "docking.h"
+#include "droptarget.h"
+#include <assert.h>
+
+
+
+#include <kio/authinfo.h>
+#include <kio/global.h>
+#include <qiconset.h>
+
+#include "version.h"
+#include "slave.h"
+#include "slaveevent.h"
+
+struct KURLPair
+{
+ KURL dest;
+ KURL src;
+};
+
+KMainWidget *kmain = 0L;
+
+#define LOAD_ICON(X) KGlobal::iconLoader()->loadIcon(X, KIcon::MainToolbar)
+
+DropTarget *kdrop = 0L;
+
+Settings ksettings; // this object contains all settings
+
+static int sockets_open();
+
+
+// socket constants
+int ipx_sock = -1; /* IPX socket */
+int ax25_sock = -1; /* AX.25 socket */
+int inet_sock = -1; /* INET socket */
+int ddp_sock = -1; /* Appletalk DDP socket */
+
+
+
+KMainWidget::KMainWidget(bool bStartDocked)
+ : KGetIface( "KGet-Interface" ),
+ KMainWindow(0, "kget mainwindow",0),
+ prefDlg( 0 ), kdock( 0 )
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ {
+ KConfig cfg( "kioslaverc", false, false);
+ cfg.setGroup(QString::null);
+ cfg.writeEntry("AutoResume", true);
+ cfg.sync();
+ }
+
+ b_connected = TRUE;
+ b_viewLogWindow = FALSE;
+ b_viewPreferences = FALSE;
+
+ myTransferList = 0L;
+ kmain = this;
+
+ // Set log time, needed for the name of log file
+ QDate date = QDateTime::currentDateTime().date();
+ QTime time = QDateTime::currentDateTime().time();
+ QString tmp;
+
+ tmp.sprintf("log%d:%d:%d-%d:%d:%d", date.day(), date.month(), date.year(), time.hour(), time.minute(), time.second());
+
+ logFileName = locateLocal("appdata", "logs/");
+ logFileName += tmp;
+
+ lastClipboard = QApplication::clipboard()->text( QClipboard::Clipboard ).stripWhiteSpace();
+ // Load all settings from KConfig
+ ksettings.load();
+
+ // Setup log window
+ logWindow = new LogWindow();
+
+ m_showDropTarget = false;
+
+ setCaption(KGETVERSION);
+
+ setupGUI();
+ setupWhatsThis();
+
+ log(i18n("Welcome to KGet"));
+
+ setCentralWidget(myTransferList);
+
+ connect(kapp, SIGNAL(saveYourself()), SLOT(slotSaveYourself()));
+
+ // Enable dropping
+ setAcceptDrops(true);
+
+ // Setup connection timer
+ connectionTimer = new QTimer(this);
+ connect(connectionTimer, SIGNAL(timeout()), SLOT(slotCheckConnection()));
+
+ // setup socket for checking connection
+ if ((_sock = sockets_open()) < 0) {
+ log(i18n("Could not create valid socket"), false);
+ } else {
+ connectionTimer->start(5000); // 5 second interval for checking connection
+ }
+
+ checkOnline();
+ ksettings.b_offline=( !b_connected || ksettings.b_offlineMode );
+
+ // Setup animation timer
+ animTimer = new QTimer(this);
+ animCounter = 0;
+ connect(animTimer, SIGNAL(timeout()), SLOT(slotAnimTimeout()));
+
+ if (ksettings.b_useAnimation) {
+ animTimer->start(400);
+ } else {
+ animTimer->start(1000);
+ }
+
+ // Setup transfer timer for scheduled downloads and checkQueue()
+ transferTimer = new QTimer(this);
+ connect(transferTimer, SIGNAL(timeout()), SLOT(slotTransferTimeout()));
+ transferTimer->start(10000); // 10 secs time interval
+
+ // Setup autosave timer
+ autosaveTimer = new QTimer(this);
+ connect(autosaveTimer, SIGNAL(timeout()), SLOT(slotAutosaveTimeout()));
+ setAutoSave();
+
+ // Setup clipboard timer
+ clipboardTimer = new QTimer(this);
+ connect(clipboardTimer, SIGNAL(timeout()), SLOT(slotCheckClipboard()));
+ if (ksettings.b_autoPaste) {
+ clipboardTimer->start(1000);
+ }
+
+ readTransfers();
+
+ // Setup special windows
+ kdrop = new DropTarget(this);
+ kdock = new DockWidget(this);
+ connect(kdock, SIGNAL(quitSelected()), SLOT(slotQuit()));
+
+ // Set geometry
+ if (ksettings.mainPosition.x() != -1) {
+ resize(ksettings.mainSize);
+ move(ksettings.mainPosition);
+ KWin::setState(winId(), ksettings.mainState);
+ } else {
+ resize(650, 180);
+ }
+
+ // update actions
+ m_paUseAnimation->setChecked(ksettings.b_useAnimation);
+ m_paExpertMode->setChecked(ksettings.b_expertMode);
+ m_paUseLastDir->setChecked(ksettings.b_useLastDir);
+
+ if (ksettings.connectionType != PERMANENT) {
+ m_paAutoDisconnect->setChecked(ksettings.b_autoDisconnect);
+ }
+ setAutoDisconnect();
+
+ m_paAutoShutdown->setChecked(ksettings.b_autoShutdown);
+
+
+ m_paOfflineMode->setChecked(ksettings.b_offline);
+ if (!ksettings.b_offlineMode)
+ m_paOfflineMode->setIconSet(LOAD_ICON("tool_offline_mode_on"));
+
+ if (ksettings.b_offline) {
+ setCaption(i18n("Offline"), false);
+ log(i18n("Starting offline"));
+ } else
+ setCaption(QString::null, false);
+
+ m_paAutoPaste->setChecked(ksettings.b_autoPaste);
+ m_paShowLog->setChecked(b_viewLogWindow);
+
+ if (!bStartDocked && ksettings.b_showMain)
+ show();
+
+ kdock->show();
+
+ KNotifyClient::startDaemon();
+
+ setStandardToolBarMenuEnabled(true);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+KMainWidget::~KMainWidget()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ delete prefDlg;
+ delete kdrop;
+ writeTransfers();
+ writeLog();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+ delete logWindow;
+}
+
+
+void KMainWidget::log(const QString & message, bool statusbar)
+{
+#ifdef _DEBUG
+ sDebugIn <<" message= "<< message << endl;
+#endif
+
+ logWindow->logGeneral(message);
+
+ if (statusbar) {
+ statusBar()->message(message, 1000);
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotSaveYourself()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ writeTransfers();
+ ksettings.save();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::setupGUI()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ // setup transfer list
+ myTransferList = new TransferList(this, "transferList");
+ myTransferList->setSorting(-1);
+ setListFont();
+
+ KActionCollection *coll = actionCollection();
+
+ connect(myTransferList, SIGNAL(selectionChanged()), this, SLOT(slotUpdateActions()));
+ connect(myTransferList, SIGNAL(transferSelected(Transfer *)), this, SLOT(slotOpenIndividual()));
+ connect(myTransferList, SIGNAL(popupMenu(Transfer *)), this, SLOT(slotPopupMenu(Transfer *)));
+
+ // file actions
+ m_paOpenTransfer = KStdAction::open(this, SLOT(slotOpenTransfer()), coll, "open_transfer");
+ m_paPasteTransfer = KStdAction::paste(this, SLOT(slotPasteTransfer()), coll, "paste_transfer");
+
+ m_paExportTransfers = new KAction(i18n("&Export Transfer List..."), 0, this, SLOT(slotExportTransfers()), coll, "export_transfers");
+ m_paImportTransfers = new KAction(i18n("&Import Transfer List..."), 0, this, SLOT(slotImportTransfers()), coll, "import_transfers");
+
+ m_paImportText = new KAction(i18n("Import Text &File..."), 0, this, SLOT(slotImportTextFile()), coll, "import_text");
+
+ m_paQuit = KStdAction::quit(this, SLOT(slotQuit()), coll, "quit");
+
+
+ // transfer actions
+ m_paCopy = new KAction(i18n("&Copy URL to Clipboard"), 0, this, SLOT(slotCopyToClipboard()), coll, "copy_url");
+ m_paIndividual = new KAction(i18n("&Open Individual Window"), 0, this, SLOT(slotOpenIndividual()), coll, "open_individual");
+
+ m_paMoveToBegin = new KAction(i18n("Move to &Beginning"), 0, this, SLOT(slotMoveToBegin()), coll, "move_begin");
+
+ m_paMoveToEnd = new KAction(i18n("Move to &End"), 0, this, SLOT(slotMoveToEnd()), coll, "move_end");
+#ifdef _DEBUG
+ sDebug << "Loading pics" << endl;
+#endif
+ m_paResume = new KAction(i18n("&Resume"),"tool_resume", 0, this, SLOT(slotResumeCurrent()), coll, "resume");
+ m_paPause = new KAction(i18n("&Pause"),"tool_pause", 0, this, SLOT(slotPauseCurrent()), coll, "pause");
+ m_paDelete = new KAction(i18n("&Delete"),"editdelete", Qt::Key_Delete, this, SLOT(slotDeleteCurrent()), coll, "delete");
+ m_paRestart = new KAction(i18n("Re&start"),"tool_restart", 0, this, SLOT(slotRestartCurrent()), coll, "restart");
+
+ m_paQueue = new KRadioAction(i18n("&Queue"),"tool_queue", 0, this, SLOT(slotQueueCurrent()), coll, "queue");
+ m_paTimer = new KRadioAction(i18n("&Timer"),"tool_timer", 0, this, SLOT(slotTimerCurrent()), coll, "timer");
+ m_paDelay = new KRadioAction(i18n("De&lay"),"tool_delay", 0, this, SLOT(slotDelayCurrent()), coll, "delay");
+
+ m_paQueue->setExclusiveGroup("TransferMode");
+ m_paTimer->setExclusiveGroup("TransferMode");
+ m_paDelay->setExclusiveGroup("TransferMode");
+
+ // options actions
+ m_paUseAnimation = new KToggleAction(i18n("Use &Animation"), 0, this, SLOT(slotToggleAnimation()), coll, "toggle_animation");
+ m_paExpertMode = new KToggleAction(i18n("&Expert Mode"),"tool_expert", 0, this, SLOT(slotToggleExpertMode()), coll, "expert_mode");
+ m_paUseLastDir = new KToggleAction(i18n("&Use-Last-Folder Mode"),"tool_uselastdir", 0, this, SLOT(slotToggleUseLastDir()), coll, "use_last_dir");
+ m_paAutoDisconnect = new KToggleAction(i18n("Auto-&Disconnect Mode"),"tool_disconnect", 0, this, SLOT(slotToggleAutoDisconnect()), coll, "auto_disconnect");
+ m_paAutoShutdown = new KToggleAction(i18n("Auto-S&hutdown Mode"), "tool_shutdown", 0, this, SLOT(slotToggleAutoShutdown()), coll, "auto_shutdown");
+ m_paOfflineMode = new KToggleAction(i18n("&Offline Mode"),"tool_offline_mode_off", 0, this, SLOT(slotToggleOfflineMode()), coll, "offline_mode");
+ m_paAutoPaste = new KToggleAction(i18n("Auto-Pas&te Mode"),"tool_clipboard", 0, this, SLOT(slotToggleAutoPaste()), coll, "auto_paste");
+
+ m_paPreferences = KStdAction::preferences(this, SLOT(slotPreferences()), coll);
+
+ KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), coll);
+ KStdAction::configureToolbars(this, SLOT(slotConfigureToolbars()), coll);
+ KStdAction::configureNotifications(this, SLOT(slotConfigureNotifications()), coll);
+
+ m_menubarAction = KStdAction::showMenubar(this, SLOT(slotShowMenubar()), coll, "settings_showmenubar" );
+ m_menubarAction->setChecked( !menuBar()->isHidden() );
+
+ // view actions
+ createStandardStatusBarAction();
+
+ m_paShowLog = new KToggleAction(i18n("Show &Log Window"),"tool_logwindow", 0, this, SLOT(slotToggleLogWindow()), coll, "toggle_log");
+ m_paShowLog->setCheckedState(i18n("Hide &Log Window"));
+ m_paDropTarget = new KAction(i18n("Show Drop &Target"),"tool_drop_target", 0, this, SLOT(slotToggleDropTarget()), coll, "drop_target");
+ m_paKonquerorIntegration = new KAction(i18n("Enable &KGet as Konqueror Download Manager"), "konqueror", 0, this, SLOT(slotKonquerorIntegration()), coll, "konqueror_integration");
+ if (ksettings.b_KonquerorIntegration) {
+ m_paKonquerorIntegration->setText(i18n("Disable &KGet as Konqueror Download Manager"));
+ }
+
+ menuHelp = new KHelpMenu(this, KGlobal::instance()->aboutData());
+ KStdAction::whatsThis(menuHelp, SLOT(contextHelpActivated()), coll, "whats_this");
+
+ createGUI("kgetui.rc");
+
+ // setup statusbar
+ statusBar()->insertFixedItem(i18n(" Transfers: %1 ").arg(99), ID_TOTAL_TRANSFERS);
+ statusBar()->insertFixedItem(i18n(" Files: %1 ").arg(555), ID_TOTAL_FILES);
+ statusBar()->insertFixedItem(i18n(" Size: %1 KB ").arg("134.56"), ID_TOTAL_SIZE);
+ statusBar()->insertFixedItem(i18n(" Time: %1 ").arg(KIO::convertSeconds(0)), ID_TOTAL_TIME);
+ statusBar()->insertFixedItem(i18n(" %1 KB/s ").arg("123.34"), ID_TOTAL_SPEED);
+
+ setAutoSaveSettings( "MainWindow", false /*Settings takes care of size & pos & state */ );
+
+ slotUpdateActions();
+
+ updateStatusBar();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::setupWhatsThis()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ QString tmp;
+
+ tmp = i18n("<b>Resume</b> button starts selected transfers\n" "and sets their mode to <i>queued</i>.");
+ m_paResume->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Pause</b> button stops selected transfers\n" "and sets their mode to <i>delayed</i>.");
+ m_paPause->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Delete</b> button removes selected transfers\n" "from the list.");
+ m_paDelete->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Restart</b> button is a convenience button\n" "that simply does Pause and Resume.");
+ m_paRestart->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Queued</b> button sets the mode of selected\n" "transfers to <i>queued</i>.\n" "\n" "It is a radio button -- you can choose between\n" "three modes.");
+ m_paQueue->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Scheduled</b> button sets the mode of selected\n" "transfers to <i>scheduled</i>.\n" "\n" "It is a radio button -- you can choose between\n" "three modes.");
+ m_paTimer->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Delayed</b> button sets the mode of selected\n" "transfers to <i>delayed</i>." "This also causes the selected transfers to stop.\n" "\n" "It is a radio button -- you can choose between\n" "three modes.");
+ m_paDelay->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Preferences</b> button opens a preferences dialog\n" "where you can set various options.\n" "\n" "Some of these options can be more easily set using the toolbar.");
+ m_paPreferences->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Log window</b> button opens a log window.\n" "The log window records all program events that occur\n" "while KGet is running.");
+ m_paShowLog->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Paste transfer</b> button adds a URL from\n" "the clipboard as a new transfer.\n" "\n" "This way you can easily copy&paste URLs between\n" "applications.");
+ m_paPasteTransfer->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Expert mode</b> button toggles the expert mode\n" "on and off.\n" "\n" "Expert mode is recommended for experienced users.\n" "When set, you will not be \"bothered\" by confirmation\n" "messages.\n" "<b>Important!</b>\n" "Turn it on if you are using auto-disconnect or\n" "auto-shutdown features and you want KGet to disconnect \n" "or shut down without asking.");
+ m_paExpertMode->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Use last folder</b> button toggles the\n" "use-last-folder feature on and off.\n" "\n" "When set, KGet will ignore the folder settings\n" "and put all new added transfers into the folder\n" "where the last transfer was put.");
+ m_paUseLastDir->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Auto disconnect</b> button toggles the auto-disconnect\n" "mode on and off.\n" "\n" "When set, KGet will disconnect automatically\n" "after all queued transfers are finished.\n" "\n" "<b>Important!</b>\n" "Also turn on the expert mode when you want KGet\n" "to disconnect without asking.");
+ m_paAutoDisconnect->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Auto shutdown</b> button toggles the auto-shutdown\n" "mode on and off.\n" "\n" "When set, KGet will quit automatically\n" "after all queued transfers are finished.\n" "<b>Important!</b>\n" "Also turn on the expert mode when you want KGet\n" "to quit without asking.");
+ m_paAutoShutdown->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Offline mode</b> button toggles the offline mode\n" "on and off.\n" "\n" "When set, KGet will act as if it was not connected\n" "to the Internet.\n" "\n" "You can browse offline, while still being able to add\n" "new transfers as queued.");
+ m_paOfflineMode->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Auto paste</b> button toggles the auto-paste mode\n" "on and off.\n" "\n" "When set, KGet will periodically scan the clipboard\n" "for URLs and paste them automatically.");
+ m_paAutoPaste->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Drop target</b> button toggles the window style\n" "between a normal window and a drop target.\n" "\n" "When set, the main window will be hidden and\n" "instead a small shaped window will appear.\n" "\n" "You can show/hide a normal window with a simple click\n" "on a shaped window.");
+ m_paDropTarget->setWhatsThis(tmp);
+ /*
+ tmp = i18n("<b>Dock widget</b> button toggles the window style\n" "between a normal window and a docked widget.\n" "\n" "When set, the main window will be hidden and\n" "instead a docked widget will appear on the panel.\n" "\n" "You can show/hide a normal window by simply clicking\n" "on a docked widget.");
+ m_paDockWindow->setWhatsThis(tmp);
+
+ tmp = i18n("<b>Normal window</b> button sets\n" "\n" "the window style to normal window");
+ m_paNormal->setWhatsThis(tmp);
+ */
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotConfigureToolbars()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ saveMainWindowSettings( KGlobal::config(), "MainWindow" );
+ KEditToolbar edit(factory());
+ connect(&edit, SIGNAL( newToolbarConfig() ), this, SLOT( slotNewToolbarConfig() ));
+ edit.exec();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotNewToolbarConfig()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ createGUI("kgetui.rc");
+ applyMainWindowSettings( KGlobal::config(), "MainWindow" );
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotImportTextFile()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ QString tmpFile;
+ QString list;
+ int i, j;
+
+ KURL filename = KFileDialog::getOpenURL(ksettings.lastDirectory);
+ if (!filename.isValid())
+ return;
+
+ if (KIO::NetAccess::download(filename, tmpFile, this)) {
+ list = kFileToString(tmpFile);
+ KIO::NetAccess::removeTempFile(tmpFile);
+ } else
+ list = kFileToString(filename.path()); // file not accessible -> give error message
+
+ i = 0;
+ while ((j = list.find('\n', i)) != -1) {
+ QString newtransfer = list.mid(i, j - i);
+ addTransfer(newtransfer);
+ i = j + 1;
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotImportTransfers()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ readTransfers(true);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::readTransfers(bool ask_for_name)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ KURL url;
+
+ if (ask_for_name)
+ url = KFileDialog::getOpenURL(ksettings.lastDirectory, i18n("*.kgt|*.kgt\n*|All Files"));
+ else
+ url.setPath( locateLocal("appdata", "transfers.kgt") );
+
+ readTransfersEx(url);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::readTransfersEx(const KURL & file)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ if (!file.isValid()) {
+
+#ifdef _DEBUG
+ sDebugOut<< " string empty" << endl;
+#endif
+ return;
+ }
+#ifdef _DEBUG
+ sDebug << "Read from file: " << file << endl;
+#endif
+ myTransferList->readTransfers(file);
+ checkQueue();
+ slotTransferTimeout();
+ myTransferList->clearSelection();
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotExportTransfers()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ writeTransfers(true);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::writeTransfers(bool ask_for_name)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ QString str;
+ QString txt;
+
+ if (ask_for_name)
+ txt = KFileDialog::getSaveFileName(ksettings.lastDirectory, i18n("*.kgt|*.kgt\n*|All Files"));
+ else
+ txt = locateLocal("appdata", "transfers.kgt");
+
+
+
+ if (txt.isEmpty())
+ {
+#ifdef _DEBUG
+ sDebugOut<< " because Destination File name isEmpty"<< endl;
+#endif
+ return;
+ }
+ if (!txt.endsWith(".kgt"))
+ txt += ".kgt";
+
+#ifdef _DEBUG
+ sDebug << "Writing transfers " << txt << endl;
+#endif
+
+ myTransferList->writeTransfers(txt);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::writeLog()
+{
+#ifdef _DEBUG
+ sDebugIn << "Writing log to file : " << logFileName.ascii() << endl;
+#endif
+
+
+ kCStringToFile(logWindow->getText().local8Bit(), logFileName, false, false);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotQuit()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ Transfer *item;
+ TransferIterator it(myTransferList);
+
+ log(i18n("Quitting..."));
+
+ for (; it.current(); ++it) {
+ item = it.current();
+ if (item->getStatus() == Transfer::ST_RUNNING && !ksettings.b_expertMode) {
+ if (KMessageBox::warningContinueCancel(this, i18n("Some transfers are still running.\nAre you sure you want to quit KGet?"), i18n("Warning"), KStdGuiItem::quit()) != KMessageBox::Continue) {
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+ return;
+ }
+ break;
+ }
+ }
+
+ ksettings.save();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+ kapp->quit();
+}
+
+
+void KMainWidget::slotResumeCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it)
+ if (it.current()->isSelected())
+ it.current()->slotResume();
+ slotUpdateActions();
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotPauseCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ TransferIterator it(myTransferList);
+
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ update();
+
+ for (; it.current(); ++it)
+ if (it.current()->isSelected())
+ it.current()->slotRequestPause();
+ slotUpdateActions();
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+
+void KMainWidget::slotRestartCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it)
+ if (it.current()->isSelected())
+ it.current()->slotRequestRestart();
+ slotUpdateActions();
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotDeleteCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ m_paDelete->setEnabled(false);
+ m_paPause->setEnabled(false);
+ update();
+ TransferIterator it(myTransferList);
+ QValueList<QGuardedPtr<Transfer> > selectedItems;
+ QStringList itemNames;
+
+ while (it.current()) {
+ if (it.current()->isSelected()) {
+ selectedItems.append( QGuardedPtr<Transfer>( it.current() ));
+ itemNames.append( it.current()->getSrc().prettyURL() );
+ }
+ ++it;
+ }
+
+ if (!ksettings.b_expertMode)
+ {
+ if ( selectedItems.count() > 1 )
+ {
+ if (KMessageBox::warningContinueCancelList(this, i18n("Are you sure you want to delete these transfers?"),
+ itemNames, i18n("Question"),
+ KStdGuiItem::del(),
+ QString("multiple_delete_transfer"))
+ != KMessageBox::Continue)
+ return; // keep 'em
+ }
+ else
+ {
+ if (KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete this transfer?"),
+ i18n("Question"), KStdGuiItem::del(),
+ QString("delete_transfer"))
+ != KMessageBox::Continue)
+ return;
+ }
+ }
+
+ // If we reach this, we want to delete all selected transfers
+ // Some of them might have finished in the meantime tho. Good that
+ // we used a QGuardedPtr :)
+
+ int transferFinishedMeanwhile = 0;
+ QValueListConstIterator<QGuardedPtr<Transfer> > lit = selectedItems.begin();;
+ while ( lit != selectedItems.end() )
+ {
+ if ( *lit )
+ (*lit)->slotRequestRemove();
+ else
+ ++transferFinishedMeanwhile;
+
+ ++lit;
+ }
+
+ checkQueue(); // needed !
+
+ if ( !ksettings.b_expertMode && transferFinishedMeanwhile > 0 )
+ KMessageBox::information(this, i18n("The transfer you wanted to delete completed before it could be deleted.",
+ "%n transfers you wanted to delete completed before they could be deleted.",
+ transferFinishedMeanwhile ),
+ QString::null, "completedBeforeDeletion" );
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::stopAll()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ log(i18n("Stopping all jobs"), false);
+
+ TransferIterator it(myTransferList);
+ Transfer::TransferStatus Status;
+ for (; it.current(); ++it) {
+ Status = it.current()->getStatus();
+ if (Status == Transfer::ST_TRYING || Status == Transfer::ST_RUNNING)
+ it.current()->slotStop();
+ }
+ slotUpdateActions();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotQueueCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it) {
+ if (it.current()->isSelected()) {
+ it.current()->slotQueue();
+ }
+ }
+
+ // myTransferList->clearSelection();
+ slotUpdateActions();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotTimerCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it)
+ if (it.current()->isSelected())
+ it.current()->slotRequestSchedule();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+}
+
+
+void KMainWidget::slotDelayCurrent()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it)
+ if (it.current()->isSelected())
+ it.current()->slotRequestDelay();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotOpenTransfer()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ QString newtransfer;
+ bool ok = false;
+
+#ifdef _DEBUG
+ // newtransfer = "ftp://localhost/home/pch/test.gz";
+ // newtransfer = "http://www.kernel.org/pub/linux/kernel/v2.4/linux-2.4.18.tar.gz";
+ newtransfer = "ftp://darkmoon/pub/test.gz";
+#endif
+
+ while (!ok) {
+ newtransfer = KInputDialog::getText(i18n("Open Transfer"), i18n("Open transfer:"), newtransfer, &ok, this);
+
+ // user presses cancel
+ if (!ok) {
+ return;
+ }
+
+ KURL url = KURL::fromPathOrURL(newtransfer);
+
+ if (!url.isValid()) {
+ KMessageBox::error(this, i18n("Malformed URL:\n%1").arg(newtransfer), i18n("Error"));
+ ok = false;
+ }
+ }
+
+ addTransfer(newtransfer);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+
+void KMainWidget::slotCheckClipboard()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ QString clipData = QApplication::clipboard()->text( QClipboard::Clipboard ).stripWhiteSpace();
+
+ if (clipData != lastClipboard) {
+ sDebug << "New clipboard event" << endl;
+
+ lastClipboard = clipData;
+ if ( lastClipboard.isEmpty() )
+ return;
+
+ KURL url = KURL::fromPathOrURL( lastClipboard );
+
+ if (url.isValid() && !url.isLocalFile())
+ slotPasteTransfer();
+ }
+
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotPasteTransfer()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ QString newtransfer;
+
+ newtransfer = QApplication::clipboard()->text();
+ newtransfer = newtransfer.stripWhiteSpace();
+
+ if (!ksettings.b_expertMode) {
+ bool ok = false;
+ newtransfer = KInputDialog::getText(i18n("Open Transfer"), i18n("Open transfer:"), newtransfer, &ok, this);
+
+ if (!ok) {
+ // cancelled
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+ return;
+ }
+
+ }
+
+ if (!newtransfer.isEmpty())
+ addTransfer(newtransfer);
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+// destFile must be a filename, not a directory! And it will be deleted, if
+// it exists already, without further notice.
+void KMainWidget::addTransferEx(const KURL& url, const KURL& destFile)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ if ( !sanityChecksSuccessful( url ) )
+ return;
+
+ KURL destURL = destFile;
+
+ // Malformed destination url means one of two things.
+ // 1) The URL is empty.
+ // 2) The URL is only a filename, like a default (suggested) filename.
+ if ( !destURL.isValid() )
+ {
+ // Setup destination
+ QString destDir = getSaveDirectoryFor( url.fileName() );
+ bool b_expertMode = ksettings.b_expertMode;
+ bool bDestisMalformed = true;
+
+ while (bDestisMalformed)
+ {
+ if (!b_expertMode) {
+ // open the filedialog for confirmation
+ KFileDialog dlg( destDir, QString::null,
+ 0L, "save_as", true);
+ dlg.setCaption(i18n("Save As"));
+ dlg.setOperationMode(KFileDialog::Saving);
+
+ // Use the destination name if not empty...
+ if (destURL.isEmpty())
+ dlg.setSelection(url.fileName());
+ else
+ dlg.setSelection(destURL.url());
+
+ if ( dlg.exec() == QDialog::Rejected )
+ {
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+ return;
+ }
+ else
+ {
+ destURL = dlg.selectedURL();
+ ksettings.lastDirectory = destURL.directory();
+ }
+ }
+ else {
+ // in expert mode don't open the filedialog
+ // if destURL is not empty, it's the suggested filename
+ destURL = KURL::fromPathOrURL( destDir + "/" +
+ ( destURL.isEmpty() ?
+ url.fileName() : destURL.url() ));
+ }
+
+ //check if destination already exists
+ if(KIO::NetAccess::exists(destURL, false, this))
+ {
+ if (KMessageBox::warningYesNo(this,i18n("Destination file \n%1\nalready exists.\nDo you want to overwrite it?").arg( destURL.prettyURL()), QString::null, i18n("Overwrite"), i18n("Do Not Overwrite") )
+ == KMessageBox::Yes)
+ {
+ bDestisMalformed=false;
+ SafeDelete::deleteFile( destURL );
+ }
+ else
+ {
+ b_expertMode=false;
+ ksettings.lastDirectory = destURL.directory();
+ }
+ }
+ else
+ bDestisMalformed=false;
+ }
+ }
+
+ else // destURL was given, check whether the file exists already
+ {
+ // simply delete it, the calling process should have asked if you
+ // really want to delete (at least khtml does)
+ if(KIO::NetAccess::exists(destURL, false, this))
+ SafeDelete::deleteFile( destURL );
+ }
+
+ // create a new transfer item
+ Transfer *item = myTransferList->addTransfer(url, destURL);
+ KNotifyClient::event(kdock->winId(), "added", i18n("<i>%1</i> has been added.").arg(url.prettyURL()));
+ item->updateAll(); // update the remaining fields
+
+ if (ksettings.b_showIndividual)
+ item->showIndividual();
+
+ myTransferList->clearSelection();
+
+ checkQueue();
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::addTransfers( const KURL::List& src, const QString& destDir )
+{
+ QValueList<KURLPair> urls_orig;
+
+ for ( KURL::List::ConstIterator it = src.begin(); it != src.end(); ++it )
+ {
+ KURLPair url;
+ url.src = *it;
+ if ( url.src.fileName().endsWith( ".kgt" ) )
+ readTransfersEx(url.src);
+ else
+ urls_orig.append( url );
+ }
+
+ if ( urls_orig.isEmpty() )
+ return;
+
+ if ( urls_orig.count() == 1 ) // just one file -> ask for filename
+ {
+ KURL destFile;
+
+ if ( !destDir.isEmpty() )
+ {
+ // create a proper destination file from destDir
+ KURL destURL = KURL::fromPathOrURL( destDir );
+ QString fileName = urls_orig.first().src.fileName();
+
+ // in case the fileName is empty, we simply ask for a filename in
+ // addTransferEx. Do NOT attempt to use an empty filename, that
+ // would be a directory (and we don't want to overwrite that!)
+ if ( !fileName.isEmpty() )
+ {
+ destURL.adjustPath( +1 );
+ destURL.setFileName( fileName );
+ if(KIO::NetAccess::exists(destURL, false, this))
+ {
+ if (KMessageBox::warningYesNo(this,i18n("Destination file \n%1\nalready exists.\nDo you want to overwrite it?").arg( destURL.prettyURL()), QString::null, i18n("Overwrite"), i18n("Do Not Overwrite") )
+ == KMessageBox::Yes)
+ {
+ SafeDelete::deleteFile( destURL );
+ destFile = destURL;
+ }
+ }
+ else
+ {
+ destFile = destURL;
+ }
+ }
+ }
+
+ addTransferEx( urls_orig.first().src, destFile );
+ return;
+ }
+
+ // multiple files -> ask for directory, not for every single filename
+
+ bool dir_accepted = false;
+ QValueList<KURLPair>::Iterator it;
+ QValueList<KURLPair> urls;
+ KURL::List urlsToDelete;
+ while ( !dir_accepted )
+ {
+ urlsToDelete.clear();
+ urls = urls_orig; // copy the list here, urls might be changed, yet when we return here (Cancel),
+ // we want to start again with the original list
+ dir_accepted = true; //Set to false later when Cancel is pressed
+ KURL dest;
+ if ( destDir.isEmpty() || !QFileInfo( destDir ).isDir() )
+ {
+ if ( !destDir.isEmpty() )
+ dest.setPath( destDir );
+ else
+ dest.setPath( getSaveDirectoryFor( src.first().fileName() ) );
+
+ // ask in any case, when destDir is empty
+ if ( destDir.isEmpty() || !QFileInfo( dest.path() ).isDir() )
+ {
+ QString dir = KFileDialog::getExistingDirectory( dest.path() );
+ if ( dir.isEmpty() ) // aborted
+ return;
+
+ dest.setPath( dir );
+ ksettings.lastDirectory = dir;
+ }
+ }
+
+ // dest is now finally the real destination directory for all the files
+ dest.adjustPath(+1);
+
+ // create new transfer items
+ bool skip_all = false;
+ bool overwrite_all = false;
+ it = urls.begin();
+ KIO::RenameDlg_Result result;
+ while ( it != urls.end() )
+ {
+
+ if ( !sanityChecksSuccessful( (*it).src ) )
+ {
+ it = urls.erase( it );
+ continue; // shouldn't we notify the user??
+ }
+
+ (*it).dest = dest;
+ QString fileName = (*it).src.fileName();
+ if ( fileName.isEmpty() ) // simply use the full url as filename
+ fileName = KURL::encode_string_no_slash( (*it).src.prettyURL() );
+
+ (*it).dest.setFileName( fileName );
+
+ if( KIO::NetAccess::exists((*it).dest, false, this))
+ {
+ QString newdest;
+ if (skip_all)
+ result = KIO::R_SKIP;
+ else if( overwrite_all )
+ result = KIO::R_OVERWRITE;
+ else
+ {
+ QFileInfo finfo( (*it).dest.path() );
+ QString caption = i18n( "File Already exists" ) + " - KGet";
+ result = KIO::open_RenameDlg( caption, (*it).src.url(), (*it).dest.url(), KIO::RenameDlg_Mode(KIO::M_OVERWRITE|KIO::M_SKIP|KIO::M_MULTI), newdest, (KIO::filesize_t) -1, (KIO::filesize_t)finfo.size(), (time_t) -1, (time_t) -1, (time_t) -1, finfo.lastModified().toTime_t());
+ }
+ switch (result)
+ {
+ case KIO::R_RENAME:
+ (*it).dest = KURL::fromPathOrURL( newdest );
+ break;
+ case KIO::R_OVERWRITE_ALL:
+ overwrite_all = true; //fall through
+ case KIO::R_OVERWRITE:
+ urlsToDelete.append( (*it).dest );
+ break;
+ case KIO::R_AUTO_SKIP:
+ skip_all = true;
+ case KIO::R_SKIP: //fall through
+ it = urls.erase( it );
+ continue;
+ break;
+ default: // Cancel, ask again for directory
+ dir_accepted = false;
+ }
+ if ( !dir_accepted )
+ break;
+ } // if(KIO::NetAccess::exists
+ ++it;
+ } // while ( it != urls.end() )
+ } // while ( !dir_accepted )
+
+ KURL::List::Iterator it_1 = urlsToDelete.begin();
+ for ( ; it_1 != urlsToDelete.end(); ++it_1 )
+ {
+ SafeDelete::deleteFile( *it_1 );
+ }
+
+ int numdl = 0;
+ it = urls.begin();
+ for ( ; it != urls.end(); ++it )
+ {
+ Transfer *item = myTransferList->addTransfer((*it).src, (*it).dest);
+ item->updateAll(); // update the remaining fields
+ numdl++;
+ }
+
+ KNotifyClient::event(kdock->winId(), "added", i18n("1 download has been added.", "%n downloads have been added.", numdl));
+
+ myTransferList->clearSelection();
+
+ checkQueue();
+}
+
+void KMainWidget::addTransfer(const QString& src)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ if ( src.isEmpty() )
+ return;
+
+ addTransferEx( KURL::fromPathOrURL( src ) );
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+}
+
+
+void KMainWidget::checkQueue()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ uint numRun = 0;
+ int status;
+ Transfer *item;
+ TransferIterator it(myTransferList);
+
+ if (!ksettings.b_offline) {
+
+ // count running transfers
+ for (; it.current(); ++it) {
+ status = it.current()->getStatus();
+ if (status == Transfer::ST_RUNNING || status == Transfer::ST_TRYING)
+ numRun++;
+ }
+ sDebug << "Found " << numRun << " Running Jobs" << endl;
+ it.reset();
+ bool isRunning;
+ bool isQuequed;
+ for (; it.current() && numRun < ksettings.maxSimultaneousConnections; ++it) {
+ item = it.current();
+ isRunning = (item->getStatus() == Transfer::ST_RUNNING) || ((item->getStatus() == Transfer::ST_TRYING));
+
+ isQuequed = (item->getMode() == Transfer::MD_QUEUED || item->getMode() == Transfer::MD_NEW);
+
+ if (!isRunning && isQuequed && !ksettings.b_offline)
+ {
+ log(i18n("Starting another queued job."));
+ item->slotResume();
+ numRun++;
+ }
+ }
+
+ slotUpdateActions();
+
+ updateStatusBar();
+
+// } else {//TODO this has to be solved different
+// log(i18n("Cannot continue offline status"));
+ }
+
+ it.reset();
+ for (; it.current(); ++it)
+ {
+ item = it.current();
+ if (item->getMode() == Transfer::MD_NEW && item->getStatus() == Transfer::ST_STOPPED)
+ {
+ item->checkCache();
+ }
+ }
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+}
+
+
+void KMainWidget::slotAnimTimeout()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ bool isTransfer;
+
+ animCounter++;
+ if (animCounter == myTransferList->getPhasesNum()) {
+ //updateStatusBar();
+ animCounter = 0;
+ }
+ // update status of all items of transferList
+ isTransfer = myTransferList->updateStatus(animCounter);
+
+ //if (this->isVisible()) {
+ updateStatusBar();
+ //}
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+
+}
+
+
+void KMainWidget::slotTransferTimeout()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ Transfer *item;
+ TransferIterator it(myTransferList);
+
+ bool flag = false;
+
+ for (; it.current(); ++it) {
+ item = it.current();
+ if (item->getMode() == Transfer::MD_SCHEDULED && item->getStartTime() <= QDateTime::currentDateTime()) {
+ item->setMode(Transfer::MD_QUEUED);
+ flag = true;
+ }
+ }
+
+ if (flag) {
+ checkQueue();
+ }
+
+ if (ksettings.b_autoDisconnect && ksettings.b_timedDisconnect && ksettings.disconnectTime <= QTime::currentTime() && ksettings.disconnectDate == QDate::currentDate()) {
+ onlineDisconnect();
+ }
+
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotAutosaveTimeout()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ writeTransfers();
+
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotStatusChanged(Transfer * item, int _operation)
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ switch (_operation) {
+
+ case Transfer::OP_FINISHED:
+ {
+ QString srcurl = item->getSrc().prettyURL();
+ if (ksettings.b_removeOnSuccess && !item->keepDialogOpen() )
+ {
+ delete item;
+ item = 0L;
+ }
+ else
+ item->setMode(Transfer::MD_NONE);
+
+ if (!myTransferList->areTransfersQueuedOrScheduled()) {
+ // no items or only delayed and finished items in the TransferList
+ if (ksettings.b_autoDisconnect)
+ onlineDisconnect();
+
+ if (ksettings.b_autoShutdown) {
+ slotQuit();
+ return;
+ }
+ KNotifyClient::event(kdock->winId(), "finishedall", i18n("All the downloads are finished."));
+ }
+ else
+ {
+ KNotifyClient::event(kdock->winId(), "finished", i18n("<i>%1</i> successfully downloaded.").arg(srcurl));
+ }
+
+ if ( item )
+ item->slotUpdateActions();
+
+ break;
+ }
+ case Transfer::OP_RESUMED:
+ slotUpdateActions();
+ item->slotUpdateActions();
+ // play(ksettings.audioStarted);
+ break;
+ case Transfer::OP_PAUSED:
+ break;
+ case Transfer::OP_REMOVED:
+ delete item;
+ return; // checkQueue() will be called only once after all deletions
+
+ case Transfer::OP_ABORTED:
+ break;
+ case Transfer::OP_DELAYED:
+ case Transfer::OP_QUEUED:
+ slotUpdateActions();
+ item->slotUpdateActions();
+ break;
+ case Transfer::OP_SCHEDULED:
+ slotUpdateActions();
+ item->slotUpdateActions();
+ slotTransferTimeout(); // this will check schedule times
+ return; // checkQueue() is called from slotTransferTimeout()
+ }
+ checkQueue();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::dragEnterEvent(QDragEnterEvent * event)
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ event->accept(KURLDrag::canDecode(event) || QTextDrag::canDecode(event));
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::dropEvent(QDropEvent * event)
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ KURL::List list;
+ QString str;
+
+ if (KURLDrag::decode(event, list)) {
+ addTransfers(list);
+ } else if (QTextDrag::decode(event, str)) {
+ addTransfer(str);
+ }
+ sDebugOut << endl;
+}
+
+
+void KMainWidget::slotCopyToClipboard()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ Transfer *item = (Transfer *) myTransferList->currentItem();
+
+ if (item) {
+ QString url = item->getSrc().url();
+ QClipboard *cb = QApplication::clipboard();
+ cb->setText( url, QClipboard::Selection );
+ cb->setText( url, QClipboard::Clipboard);
+ myTransferList->clearSelection();
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotMoveToBegin()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ myTransferList->moveToBegin((Transfer *) myTransferList->currentItem());
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotMoveToEnd()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ myTransferList->moveToEnd((Transfer *) myTransferList->currentItem());
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotOpenIndividual()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ Transfer *item = (Transfer *) myTransferList->currentItem();
+ if (item)
+ item->showIndividual();
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+bool KMainWidget::queryClose()
+{
+ if( kapp->sessionSaving())
+ return true;
+ hide();
+ return false;
+}
+
+void KMainWidget::setAutoSave()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ autosaveTimer->stop();
+ if (ksettings.b_autoSave) {
+ autosaveTimer->start(ksettings.autoSaveInterval * 60000);
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+
+void KMainWidget::setAutoDisconnect()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ // disable action when we are connected permanently
+ m_paAutoDisconnect->setEnabled(ksettings.connectionType != PERMANENT);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::slotPreferences()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ if ( !prefDlg )
+ prefDlg = new DlgPreferences(this);
+
+ prefDlg->show();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::slotConfigureNotifications()
+{
+ KNotifyDialog::configure(this);
+}
+
+void KMainWidget::slotToggleLogWindow()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ b_viewLogWindow = !b_viewLogWindow;
+ if (b_viewLogWindow)
+ logWindow->show();
+ else
+ logWindow->hide();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleAnimation()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_useAnimation = !ksettings.b_useAnimation;
+
+ if (!ksettings.b_useAnimation && animTimer->isActive()) {
+ animTimer->stop();
+ animTimer->start(1000);
+ animCounter = 0;
+ } else {
+ animTimer->stop();
+ animTimer->start(400);
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::slotToggleOfflineMode()
+{
+#ifdef _DEBUG
+ sDebugIn "ksettings.b_offlineMode = " << ksettings.b_offlineMode << endl;
+#endif
+
+ ksettings.b_offlineMode = !ksettings.b_offlineMode;
+ ksettings.b_offline=(ksettings.b_offlineMode || !b_connected);
+ if (ksettings.b_offline) {
+ log(i18n("Offline mode on."));
+ stopAll();
+ setCaption(i18n("Offline"), false);
+ m_paOfflineMode->setIconSet(LOAD_ICON("tool_offline_mode_off"));
+ } else {
+ log(i18n("Offline mode off."));
+ setCaption(QString::null, false);
+ m_paOfflineMode->setIconSet(LOAD_ICON("tool_offline_mode_on"));
+ }
+
+ m_paOfflineMode->setChecked(ksettings.b_offline);
+
+
+ slotUpdateActions();
+ checkQueue();
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleExpertMode()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_expertMode = !ksettings.b_expertMode;
+
+ if (ksettings.b_expertMode) {
+ log(i18n("Expert mode on."));
+ } else {
+ log(i18n("Expert mode off."));
+ }
+ m_paExpertMode->setChecked(ksettings.b_expertMode);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleUseLastDir()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_useLastDir = !ksettings.b_useLastDir;
+
+ if (ksettings.b_useLastDir) {
+ log(i18n("Use last folder on."));
+ } else {
+ log(i18n("Use last folder off."));
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleAutoDisconnect()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_autoDisconnect = !ksettings.b_autoDisconnect;
+
+ if (ksettings.b_autoDisconnect) {
+ log(i18n("Auto disconnect on."));
+ } else {
+ log(i18n("Auto disconnect off."));
+ }
+ m_paAutoDisconnect->setChecked(ksettings.b_autoDisconnect);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleAutoShutdown()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_autoShutdown = !ksettings.b_autoShutdown;
+
+ if (ksettings.b_autoShutdown) {
+ log(i18n("Auto shutdown on."));
+ } else {
+ log(i18n("Auto shutdown off."));
+ }
+
+ m_paAutoShutdown->setChecked(ksettings.b_autoShutdown);
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleAutoPaste()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ ksettings.b_autoPaste = !ksettings.b_autoPaste;
+
+ if (ksettings.b_autoPaste) {
+ log(i18n("Auto paste on."));
+ clipboardTimer->start(1000);
+ } else {
+ log(i18n("Auto paste off."));
+ clipboardTimer->stop();
+ }
+ m_paAutoPaste->setChecked(ksettings.b_autoPaste);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotToggleDropTarget()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+ m_showDropTarget = !m_showDropTarget;
+
+ if (m_showDropTarget) {
+ kdrop->show();
+ kdrop->updateStickyState();
+ m_paDropTarget->setText(i18n("Hide Drop &Target"));
+ }
+ else {
+ kdrop->hide();
+ m_paDropTarget->setText(i18n("Show Drop &Target"));
+ }
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotKonquerorIntegration()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ bool bIsKonquiEnable=!ksettings.b_KonquerorIntegration;
+ ksettings.b_KonquerorIntegration=!ksettings.b_KonquerorIntegration;
+ KConfig cfg("konquerorrc", false, false);
+ cfg.setGroup("HTML Settings");
+ cfg.writePathEntry("DownloadManager",QString((bIsKonquiEnable)?"kget":""));
+ cfg.sync();
+ if (bIsKonquiEnable)
+ {
+ m_paKonquerorIntegration->setText(i18n("Disable &KGet as Konqueror Download Manager"));
+ }
+ else
+ {
+ m_paKonquerorIntegration->setText(i18n("Enable &KGet as Konqueror Download Manager"));
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotPopupMenu(Transfer * item)
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ // select current item
+ myTransferList->setCurrentItem(item);
+
+ // set action properties
+ slotUpdateActions();
+
+ // popup transfer menu at the position
+ QWidget *menu = guiFactory()->container("transfer",this);
+ ((QPopupMenu *) menu)->popup(QCursor::pos());
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::setListFont()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ myTransferList->setFont(ksettings.listViewFont);
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+void KMainWidget::slotUpdateActions()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+
+ // disable all signals
+ m_paQueue->blockSignals(true);
+ m_paTimer->blockSignals(true);
+ m_paDelay->blockSignals(true);
+
+ // at first turn off all buttons like when nothing is selected
+ m_paQueue->setChecked(false);
+ m_paTimer->setChecked(false);
+ m_paDelay->setChecked(false);
+
+ m_paQueue->setEnabled(false);
+ m_paTimer->setEnabled(false);
+ m_paDelay->setEnabled(false);
+
+ m_paDelete->setEnabled(false);
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+
+ m_paCopy->setEnabled(false);
+ m_paIndividual->setEnabled(false);
+ m_paMoveToBegin->setEnabled(false);
+ m_paMoveToEnd->setEnabled(false);
+
+ Transfer *item;
+ Transfer *first_item = 0L;
+ TransferIterator it(myTransferList);
+ int index = 0;
+ int totals_items = 0;
+ int sel_items = 0;
+
+ for (; it.current(); ++it, ++totals_items) {
+
+ // update action on visibles windows
+ if (it.current()->isVisible())
+ it.current()->slotUpdateActions();
+
+ if (it.current()->isSelected()) {
+ item = it.current();
+ sel_items = totals_items;
+ index++; // counting number of selected items
+ if (index == 1) {
+ first_item = item; // store first selected item
+ if (totals_items > 0)
+ m_paMoveToBegin->setEnabled(true);
+
+ m_paMoveToEnd->setEnabled(true);
+ } else {
+
+ m_paMoveToBegin->setEnabled(false);
+ m_paMoveToEnd->setEnabled(false);
+ }
+ // enable PAUSE, RESUME and RESTART only when we are online and not in offline mode
+#ifdef _DEBUG
+ sDebug << "-->ONLINE= " << ksettings.b_offline << endl;
+#endif
+ if (item == first_item && !ksettings.b_offline) {
+ switch (item->getStatus()) {
+ case Transfer::ST_TRYING:
+ case Transfer::ST_RUNNING:
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(true);
+ m_paRestart->setEnabled(true);
+ break;
+ case Transfer::ST_STOPPED:
+ m_paResume->setEnabled(true);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+#ifdef _DEBUG
+ sDebug << "STATUS IS stopped" << item->getStatus() << endl;
+#endif
+ break;
+ case Transfer::ST_FINISHED:
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ break;
+
+
+ } //end switch
+
+ } else if (item->getStatus() != first_item->getStatus()) {
+ // disable all when all selected items don't have the same status
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ }
+
+
+ if (item == first_item) {
+ m_paDelete->setEnabled(true);
+ m_paCopy->setEnabled(true);
+ m_paIndividual->setEnabled(true);
+ if (item->getStatus() != Transfer::ST_FINISHED) {
+ m_paQueue->setEnabled(true);
+ m_paTimer->setEnabled(true);
+ m_paDelay->setEnabled(true);
+
+ switch (item->getMode()) {
+ case Transfer::MD_QUEUED:
+#ifdef _DEBUG
+ sDebug << "....................THE MODE IS MD_QUEUED " << item->getMode() << endl;
+#endif
+ m_paQueue->setChecked(true);
+ break;
+ case Transfer::MD_SCHEDULED:
+#ifdef _DEBUG
+ sDebug << "....................THE MODE IS MD_SCHEDULED " << item->getMode() << endl;
+#endif
+ m_paTimer->setChecked(true);
+ break;
+ case Transfer::MD_DELAYED:
+#ifdef _DEBUG
+ sDebug << "....................THE MODE IS MD_DELAYED " << item->getMode() << endl;
+#endif
+ m_paDelay->setChecked(true);
+ break;
+ }
+ }
+ } else if (item->getMode() != first_item->getMode()) {
+ // unset all when all selected items don't have the same mode
+ m_paQueue->setChecked(false);
+ m_paTimer->setChecked(false);
+ m_paDelay->setChecked(false);
+ m_paMoveToBegin->setEnabled(false);
+ m_paMoveToEnd->setEnabled(false);
+ m_paQueue->setEnabled(false);
+ m_paTimer->setEnabled(false);
+ m_paDelay->setEnabled(false);
+ }
+
+ } // when item is selected
+ } // loop
+
+
+
+ if (sel_items == totals_items - 1)
+ m_paMoveToEnd->setEnabled(false);
+
+ // enable all signals
+
+
+
+ m_paQueue->blockSignals(false);
+ m_paTimer->blockSignals(false);
+ m_paDelay->blockSignals(false);
+
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::updateStatusBar()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ Transfer *item;
+ QString tmpstr;
+
+ int totalFiles = 0;
+ KIO::filesize_t totalSize = 0;
+ int totalSpeed = 0;
+ unsigned int remTime = 0;
+
+ TransferIterator it(myTransferList);
+
+ for (; it.current(); ++it) {
+ item = it.current();
+ if (item->getTotalSize() != 0) {
+ totalSize += (item->getTotalSize() - item->getProcessedSize());
+ }
+ totalFiles++;
+ totalSpeed += item->getSpeed();
+
+ if (item->getRemainingTime() > remTime) {
+ remTime = item->getRemainingTime();
+ }
+ }
+
+ statusBar()->changeItem(i18n(" Transfers: %1 ").arg(myTransferList->childCount()), ID_TOTAL_TRANSFERS);
+ statusBar()->changeItem(i18n(" Files: %1 ").arg(totalFiles), ID_TOTAL_FILES);
+ statusBar()->changeItem(i18n(" Size: %1 ").arg(KIO::convertSize(totalSize)), ID_TOTAL_SIZE);
+ statusBar()->changeItem(i18n(" Time: %1 ").arg(KIO::convertSeconds(remTime)), ID_TOTAL_TIME);
+ statusBar()->changeItem(i18n(" %1/s ").arg(KIO::convertSize(totalSpeed)), ID_TOTAL_SPEED);
+ //update size for each statusbar field
+ statusBar()->setItemFixed(ID_TOTAL_TRANSFERS, -1);
+ statusBar()->setItemFixed(ID_TOTAL_FILES, -1);
+ statusBar()->setItemFixed(ID_TOTAL_SIZE, -1);
+ statusBar()->setItemFixed(ID_TOTAL_TIME, -1);
+ statusBar()->setItemFixed(ID_TOTAL_SPEED, -1);
+
+ if (kdock) {
+ tmpstr = i18n("<b>Transfers:</b> %1 ").arg(myTransferList->childCount()) +
+ i18n("<br /><b>Files:</b> %1 ").arg(totalFiles) +
+ i18n("<br /><b>Size:</b> %1 ").arg(KIO::convertSize(totalSize)) +
+ i18n("<br /><b>Time:</b> %1 ").arg(KIO::convertSeconds(remTime)) +
+ i18n("<br /><b>Speed:</b> %1/s").arg(KIO::convertSize(totalSpeed));
+ kdock->updateToolTip( tmpstr );
+ //trayicon changes if download is in progress
+ if (totalSpeed == 0)
+ {
+ kdock->changeIcon( "kget_dock" );
+ }
+ else
+ {
+ kdock->changeIcon( "kget_dock_download" );
+ }
+ }
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::onlineDisconnect()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ if (!b_connected) {
+ return;
+ }
+
+ if (!ksettings.b_expertMode) {
+ if (KMessageBox::questionYesNo(this, i18n("Do you really want to disconnect?"),
+ i18n("Question"),
+ i18n("Disconnect"), i18n("Stay Connected"),
+ "kget_AutoOnlineDisconnect")
+ != KMessageBox::Yes) {
+ return;
+ }
+ }
+ log(i18n("Disconnecting..."));
+ system(QFile::encodeName(ksettings.disconnectCommand));
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::slotCheckConnection()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+ bool old = b_connected;
+ checkOnline();
+ if (b_connected != old) {
+ if (b_connected) {
+ log(i18n("We are online."));
+ setCaption(QString::null, false);
+ ksettings.b_offline=ksettings.b_offlineMode;
+ checkQueue();
+ } else {
+ log(i18n("We are offline."));
+ setCaption(i18n("Offline"), false);
+ ksettings.b_offline=true;
+ stopAll();
+ }
+ }
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+void KMainWidget::checkOnline()
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifreq));
+
+ // setup the device name according to the type of connection and link number
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", ConnectionDevices[ksettings.connectionType].ascii(), ksettings.linkNumber);
+
+ bool flag = false;
+
+ if (ksettings.connectionType != PERMANENT) {
+ // get the flags for particular device
+ if (ioctl(_sock, SIOCGIFFLAGS, &ifr) < 0) {
+ flag = true;
+ b_connected = false;
+ } else if (ifr.ifr_flags == 0) {
+#ifdef _DEBUG
+ sDebug << "Can't get flags from interface " << ifr.ifr_name << endl;
+#endif
+ b_connected = false;
+ } else if (ifr.ifr_flags & IFF_UP) { // if (ifr.ifr_flags & IFF_RUNNING)
+ b_connected = true;
+ } else {
+ b_connected = false;
+ }
+ } else {
+ b_connected = true; // PERMANENT connection
+ }
+
+ m_paOfflineMode->setEnabled(b_connected);
+
+ if (flag) {
+#ifdef _DEBUG
+ sDebug << "Unknown interface " << ifr.ifr_name << endl;
+#endif
+ }
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+
+// Helper method for opening device socket
+
+
+
+
+static int sockets_open()
+{
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ inet_sock = socket(AF_INET, SOCK_DGRAM, 0);
+#ifdef AF_IPX
+ ipx_sock = socket(AF_IPX, SOCK_DGRAM, 0);
+#else
+ ipx_sock = -1;
+#endif
+
+#ifdef AF_AX25
+ ax25_sock = socket(AF_AX25, SOCK_DGRAM, 0);
+#else
+ ax25_sock = -1;
+#endif
+
+ ddp_sock = socket(AF_APPLETALK, SOCK_DGRAM, 0);
+ /*
+ * Now pick any (exisiting) useful socket family for generic queries
+ */
+
+ sDebug << "<<<<Leaving -> sockets_open () " << endl;
+ if (inet_sock != -1)
+ return inet_sock;
+ if (ipx_sock != -1)
+ return ipx_sock;
+ if (ax25_sock != -1)
+ return ax25_sock;
+ /*
+ * If this is -1 we have no known network layers and its time to jump.
+ */
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+ return ddp_sock;
+}
+
+
+/** No descriptions */
+void KMainWidget::customEvent(QCustomEvent * _e)
+{
+#ifdef _DEBUG
+ //sDebugIn << endl;
+#endif
+
+
+ SlaveEvent *e = (SlaveEvent *) _e;
+ unsigned int result = e->getEvent();
+
+ switch (result) {
+
+ // running cases..
+ case Slave::SLV_PROGRESS_SIZE:
+ e->getItem()->slotProcessedSize(e->getData());
+ break;
+ case Slave::SLV_PROGRESS_SPEED:
+ e->getItem()->slotSpeed(e->getData());
+ break;
+
+ case Slave::SLV_RESUMED:
+ e->getItem()->slotExecResume();
+ break;
+
+ // stopping cases
+ case Slave::SLV_FINISHED:
+ e->getItem()->slotFinished();
+ break;
+ case Slave::SLV_PAUSED:
+ e->getItem()->slotExecPause();
+ break;
+ case Slave::SLV_SCHEDULED:
+ e->getItem()->slotExecSchedule();
+ break;
+
+ case Slave::SLV_DELAYED:
+ e->getItem()->slotExecDelay();
+ break;
+ case Slave::SLV_CONNECTED:
+ e->getItem()->slotExecConnected();
+ break;
+
+ case Slave::SLV_CAN_RESUME:
+ e->getItem()->slotCanResume((bool) e->getData());
+ break;
+
+ case Slave::SLV_TOTAL_SIZE:
+ e->getItem()->slotTotalSize(e->getData());
+ break;
+
+ case Slave::SLV_ERROR:
+ e->getItem()->slotExecError();
+ break;
+
+ case Slave::SLV_BROKEN:
+ e->getItem()->slotExecBroken();
+ break;
+
+ case Slave::SLV_REMOVED:
+ e->getItem()->slotExecRemove();
+ break;
+
+ case Slave::SLV_INFO:
+ e->getItem()->logMessage(e->getMsg());
+ break;
+
+ case Slave::SLV_NOTINCACHE:
+ e->getItem()->NotInCache();
+ break;
+ default:
+#ifdef _DEBUG
+ sDebug << "Unknown Result..die" << result << endl;
+#endif
+ assert(0);
+ }
+
+
+#ifdef _DEBUG
+ //sDebugOut << endl;
+#endif
+}
+
+QString KMainWidget::getSaveDirectoryFor( const QString& filename ) const
+{
+ // first set destination directory to current directory ( which is also last used )
+ QString destDir = ksettings.lastDirectory;
+
+ if (!ksettings.b_useLastDir) {
+ // check wildcards for default directory
+ DirList::Iterator it;
+ for (it = ksettings.defaultDirList.begin(); it != ksettings.defaultDirList.end(); ++it) {
+ QRegExp rexp((*it).extRegexp);
+
+ rexp.setWildcard(true);
+
+ if ((rexp.search( filename )) != -1) {
+ destDir = (*it).defaultDir;
+ break;
+ }
+ }
+ }
+
+ return destDir;
+}
+
+bool KMainWidget::sanityChecksSuccessful( const KURL& url )
+{
+ if (!url.isValid() || !KProtocolInfo::supportsReading( url ) )
+ {
+ if (!ksettings.b_expertMode)
+ KMessageBox::error(this, i18n("Malformed URL:\n%1").arg(url.prettyURL()), i18n("Error"));
+
+ return false;
+ }
+ // if we find this URL in the list
+ Transfer *transfer = myTransferList->find( url );
+ if ( transfer )
+ {
+ if ( transfer->getStatus() != Transfer::ST_FINISHED )
+ {
+ if ( !ksettings.b_expertMode )
+ {
+ KMessageBox::error(this, i18n("Already saving URL\n%1").arg(url.prettyURL()), i18n("Error"));
+ }
+
+ transfer->showIndividual();
+ return false;
+ }
+
+ else // transfer is finished, ask if we want to download again
+ {
+ if ( ksettings.b_expertMode ||
+ (KMessageBox::questionYesNo(this, i18n("Already saved URL\n%1\nDownload again?").arg(url.prettyURL()),i18n("Question"),i18n("Download Again"),KStdGuiItem::cancel() )
+ == KMessageBox::Yes) )
+ {
+ transfer->slotRequestRemove();
+ checkQueue();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // why restrict this to ftp and http? (pfeiffer)
+// // don't download file URL's TODO : uncomment?
+// if (url.protocol() == "http" && url.protocol() != "ftp") {
+// KMessageBox::error(this, i18n("File protocol not accepted!\n%1").arg(url.prettyURL()), i18n("Error"));
+// #ifdef _DEBUG
+// sDebugOut << endl;
+// #endif
+// return false;
+// }
+
+ return true;
+}
+
+bool KMainWidget::isDropTargetVisible() const
+{
+ return m_showDropTarget;
+}
+
+void KMainWidget::setDropTargetVisible( bool setVisible )
+{
+ if ( setVisible != isDropTargetVisible() )
+ {
+ m_paDropTarget->activate();
+ }
+}
+
+void KMainWidget::setOfflineMode( bool offline )
+{
+ if ( ksettings.b_offlineMode != offline )
+ slotToggleOfflineMode();
+}
+
+bool KMainWidget::isOfflineMode() const
+{
+ return ksettings.b_offlineMode;
+}
+
+void KMainWidget::activateDropTarget()
+{
+ setDropTargetVisible( true );
+}
+
+void KMainWidget::slotShowMenubar()
+{
+ if(m_menubarAction->isChecked())
+ menuBar()->show();
+ else
+ menuBar()->hide();
+}
+
+#include "kmainwidget.moc"
diff --git a/kget/kmainwidget.h b/kget/kmainwidget.h
new file mode 100644
index 00000000..ce7c9ea8
--- /dev/null
+++ b/kget/kmainwidget.h
@@ -0,0 +1,232 @@
+/***************************************************************************
+* kmainwidget.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _KMAINWIDGET_H_
+#define _KMAINWIDGET_H_
+
+#include <kmainwindow.h>
+#include <kaction.h>
+#include <kurl.h>
+#include "common.h"
+
+#include "kget_iface.h"
+
+class KAction;
+//class KToggleAction;
+class KRadioAction;
+
+class DockWidget;
+class DropTarget;
+class LogWindow;
+class DlgPreferences;
+
+class Transfer;
+class TransferList;
+class Settings;
+
+
+class KMainWidget:public KMainWindow, virtual public KGetIface
+{
+
+Q_OBJECT
+
+public:
+ enum StatusbarFields { ID_TOTAL_TRANSFERS = 1, ID_TOTAL_FILES, ID_TOTAL_SIZE,
+ ID_TOTAL_TIME , ID_TOTAL_SPEED };
+
+ KMainWidget(bool bShowMain = false);
+ ~KMainWidget();
+
+ void addTransfer( const QString& src );
+ void addTransferEx( const KURL& url,
+ const KURL& destFile = KURL());
+
+ // dcop interface
+ virtual void addTransfers( const KURL::List& src, const QString& destDir = QString::null );
+ virtual bool isDropTargetVisible() const;
+ virtual void setDropTargetVisible( bool setVisible );
+
+
+ void checkQueue();
+
+ void setListFont();
+ void setAutoSave();
+ void setAutoDisconnect();
+
+ LogWindow *logwin()const { return logWindow;}
+ friend class Settings;
+
+ // Actions
+ KToggleAction *m_paShowLog;
+ KToggleAction *m_menubarAction;
+ KAction *m_paPreferences;
+ KAction *m_paQuit;
+ bool b_viewLogWindow;
+
+ void readTransfersEx(const KURL & url);
+
+ void activateDropTarget();
+
+public slots:
+ void slotPasteTransfer();
+ void slotToggleLogWindow();
+ void slotPreferences();
+ void slotConfigureNotifications();
+ void slotToggleExpertMode();
+ void slotToggleOfflineMode();
+ void slotToggleUseLastDir();
+ void slotToggleAutoDisconnect();
+ void slotToggleAutoShutdown();
+ void slotToggleAutoPaste();
+ void slotToggleDropTarget();
+ void slotToggleAnimation();
+ void slotUpdateActions();
+ void slotKonquerorIntegration();
+
+protected slots:
+ void slotQuit();
+
+ void slotOpenTransfer();
+ void slotExportTransfers();
+ void slotImportTransfers();
+ void slotImportTextFile();
+
+ void slotSaveYourself();
+ void slotCheckConnection();
+
+
+ void slotStatusChanged(Transfer * item, int _operation);
+
+ void slotResumeCurrent();
+ void slotPauseCurrent();
+ void slotDeleteCurrent();
+ void slotRestartCurrent();
+
+ void slotQueueCurrent();
+ void slotTimerCurrent();
+ void slotDelayCurrent();
+
+ void slotOpenIndividual();
+
+ void slotAnimTimeout();
+ void slotTransferTimeout();
+ void slotAutosaveTimeout();
+
+ void slotMoveToBegin();
+ void slotMoveToEnd();
+
+ void slotCopyToClipboard();
+ void slotCheckClipboard();
+
+ void slotConfigureToolbars();
+ void slotNewToolbarConfig();
+ void slotShowMenubar();
+
+ void slotPopupMenu(Transfer * item);
+
+protected:
+ virtual void setOfflineMode( bool online );
+ virtual bool isOfflineMode() const;
+ virtual bool queryClose();
+ void writeLog();
+
+ // drag and drop
+ virtual void dragEnterEvent(QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+
+ void readTransfers(bool ask_for_name = false);
+ void writeTransfers(bool ask_for_name = false);
+
+
+ void setupGUI();
+ void setupWhatsThis();
+
+ void updateStatusBar();
+
+ // some flags
+ bool b_connected;
+ bool b_viewPreferences;
+
+ // utility functions
+ void onlineDisconnect();
+ void checkOnline();
+ void stopAll();
+ void log(const QString & message, bool statusbar = true);
+
+ /** No descriptions */
+ virtual void customEvent(QCustomEvent * e);
+
+ // various timers
+ QTimer *animTimer; // animation timer
+ QTimer *connectionTimer; // timer that checks whether we are online
+ QTimer *transferTimer; // timer for scheduled transfers
+ QTimer *autosaveTimer; // timer for autosaving transfer list
+ QTimer *clipboardTimer; // timer for checking clipboard - autopaste function
+
+ QString logFileName;
+
+
+
+private:
+ QString getSaveDirectoryFor( const QString& filename ) const;
+ bool sanityChecksSuccessful( const KURL& url );
+
+ TransferList * myTransferList;
+ KHelpMenu *menuHelp;
+
+ LogWindow *logWindow;
+ DlgPreferences *prefDlg;
+ DockWidget *kdock;
+
+ QString lastClipboard;
+
+ uint animCounter;
+
+ int _sock;
+
+ // Actions
+ KAction *m_paOpenTransfer, *m_paPasteTransfer, *m_paExportTransfers, *m_paImportTransfers;
+ KAction *m_paImportText;
+
+ KAction *m_paMoveToBegin, *m_paMoveToEnd, *m_paCopy, *m_paIndividual;
+ KAction *m_paResume, *m_paPause, *m_paDelete, *m_paRestart;
+ KRadioAction *m_paQueue, *m_paTimer, *m_paDelay;
+
+ KToggleAction *m_paUseAnimation;
+ KToggleAction *m_paExpertMode, *m_paUseLastDir, *m_paOfflineMode;
+ KToggleAction *m_paAutoDisconnect, *m_paAutoShutdown, *m_paAutoPaste;
+
+ KAction *m_paDropTarget;
+ KAction *m_paKonquerorIntegration;
+ bool m_showDropTarget;
+
+};
+
+extern KMainWidget *kmain;
+extern DropTarget *kdrop;
+
+#endif // _KMAINWIDGET_H_
diff --git a/kget/logwindow.cpp b/kget/logwindow.cpp
new file mode 100644
index 00000000..5d6f77ad
--- /dev/null
+++ b/kget/logwindow.cpp
@@ -0,0 +1,218 @@
+/***************************************************************************
+* logwindow.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/**************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <qlayout.h>
+#include <qlistview.h>
+
+#include <klocale.h>
+#include <kdialog.h>
+#include <kaction.h>
+
+#include "transfer.h"
+#include "kmainwidget.h"
+#include "logwindow.h"
+
+#include <kapplication.h>
+#include <qtextedit.h>
+
+// // Replace regular space with nbsp
+// QString replaceSpaces(const QString &str) {
+// QString res = str;
+// res.simplifyWhiteSpace();
+
+// int pos;
+// while ( (pos = res.find(' ')) != -1) {
+// res.replace(pos, 1, new QChar( 0x00a0 ), 1);
+// }
+
+// return res;
+// }
+
+
+static QString removeHTML(const QString & str)
+{
+ QString res = str;
+ int pos;
+
+ // replace <br/> with a newline
+ while ((pos = res.find("<br/>")) != -1) {
+ res.replace(pos, 4, "\n");
+ }
+
+ // remove all tags
+ while ((pos = res.find('<')) != -1) {
+ int pos2 = res.find('>', pos);
+
+ if (pos2 == -1) {
+ pos2 = res.length() + 1;
+ }
+ res.remove(pos, pos2 - pos + 1);
+ }
+
+ return res;
+}
+
+
+SeparatedLog::SeparatedLog(QWidget * parent):QWidget(parent)
+{
+ idSelected = 0;
+
+ QGridLayout *topGridLayout = new QGridLayout(this, 1, 2, 0, KDialog::spacingHint());
+
+ topGridLayout->setRowStretch(0, 5);
+
+ topGridLayout->setColStretch(0, 3);
+ topGridLayout->setColStretch(1, 10);
+
+ lv_log = new QListView(this);
+ lv_log->setMultiSelection(false);
+ lv_log->setAllColumnsShowFocus(true);
+ lv_log->setSorting(-1);
+
+ lv_log->addColumn(i18n("Id"), 40);
+ lv_log->addColumn(i18n("Name"), 100);
+
+ topGridLayout->addWidget(lv_log, 0, 0);
+
+ connect(lv_log, SIGNAL(selectionChanged(QListViewItem *)), SLOT(transferSelected(QListViewItem *)));
+
+ ml_log = new QTextEdit(this);
+ ml_log->setTextFormat(LogText);
+ ml_log->setMinimumSize(300, 200);
+ ml_log->setVScrollBarMode(QScrollView::Auto);
+ ml_log->setWordWrap(QTextEdit::NoWrap);
+
+ topGridLayout->addWidget(ml_log, 0, 1);
+}
+
+
+void SeparatedLog::addLog(uint id, const QString & filename, const QString & message)
+{
+ if (!trMap.contains(id)) {
+ trMap.insert(id, message);
+ QListViewItem *last=lv_log->lastItem();
+ new QListViewItem(lv_log, last);
+ last=lv_log->lastItem();
+ last->setText(0, QString::number(id));
+ last->setText(1, filename);
+ // if I don't do this, ID#10 gets sorted between ID#1 and ID#2, ugly.
+ } else {
+ trMap[id] += ("\n"+message);
+ }
+
+ if (idSelected == id) {
+ refresh();
+ }
+}
+
+
+void SeparatedLog::transferSelected(QListViewItem * item)
+{
+ if (item) {
+ idSelected = item->text(0).toUInt();
+ // kapp->lock();
+ ml_log->setText(trMap[idSelected]);
+ // kapp->unlock();
+ }
+}
+
+
+void SeparatedLog::refresh()
+{
+ if (idSelected > 0) {
+ // kapp->lock();
+ ml_log->setText(trMap[idSelected]);
+ // kapp->unlock();
+ }
+}
+
+
+////////////////////////
+
+
+
+
+LogWindow::LogWindow():KDialogBase(Tabbed, i18n("Log Window"), Close, Close, 0, "", false)
+{
+
+ // add pages
+ QFrame *page = addPage(i18n("Mixed"));
+ QVBoxLayout *topLayout = new QVBoxLayout(page, 0, spacingHint());
+
+ mixed_log = new QTextEdit(page);
+ mixed_log->setTextFormat(LogText);
+ mixed_log->setVScrollBarMode(QScrollView::Auto);
+ mixed_log->setWordWrap(QTextEdit::NoWrap);
+ topLayout->addWidget(mixed_log);
+
+ page = addPage(i18n("Separated"));
+ topLayout = new QVBoxLayout(page, 0, spacingHint());
+ sep_log = new SeparatedLog(page);
+ topLayout->addWidget(sep_log);
+
+ connect(this, SIGNAL(closeClicked()), this, SLOT(close()));
+
+ // resize( 500, 300 );
+}
+
+
+void LogWindow::closeEvent(QCloseEvent *e)
+{
+ kmain->m_paShowLog->setChecked(false);
+ kmain->b_viewLogWindow = false;
+ KDialogBase::closeEvent( e );
+}
+
+
+void LogWindow::logGeneral(const QString & message)
+{
+ QString tmps = "<font color=\"blue\">" + QTime::currentTime().toString() + "</font> : <b>" + message + "</b>";
+
+ mixed_log->append(tmps);
+}
+
+
+void LogWindow::logTransfer(uint id, const QString & filename, const QString & message)
+{
+ QString mixed_msg, single_msg, job_id;
+
+ job_id = QString("Job[<font color=\"red\">%1</font>] : ").arg(id);
+ mixed_msg = "<font color=\"blue\">" + QTime::currentTime().toString() + "</font> : " + job_id + message;
+
+ single_msg = "<font color=\"blue\">" + QTime::currentTime().toString() + "</font> : " + message;
+
+ mixed_log->append(mixed_msg);
+ sep_log->addLog(id, filename, single_msg);
+}
+
+
+QString LogWindow::getText() const
+{
+ return removeHTML(mixed_log->text());
+}
+
+#include "logwindow.moc"
diff --git a/kget/logwindow.h b/kget/logwindow.h
new file mode 100644
index 00000000..a2aa13d5
--- /dev/null
+++ b/kget/logwindow.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+* logwindow.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _LOGWINDOW_H
+#define _LOGWINDOW_H
+
+#include <qmap.h>
+
+#include <kdialogbase.h>
+
+class QListView;
+class QTextEdit;
+
+
+class SeparatedLog:public QWidget
+{
+
+Q_OBJECT public:
+ SeparatedLog(QWidget * parent);
+ ~SeparatedLog()
+ {}
+ void addLog(uint id, const QString & filename, const QString & message);
+ void refresh();
+
+protected slots:
+ void transferSelected(QListViewItem * item);
+
+private:
+ QListView * lv_log;
+ QTextEdit *ml_log;
+
+ typedef QMap < uint, QString > TransferMap;
+ TransferMap trMap;
+
+ uint idSelected;
+};
+
+
+class LogWindow:public KDialogBase
+{
+
+Q_OBJECT public:
+ LogWindow();
+ ~LogWindow()
+ {}
+ void logGeneral(const QString & message);
+ QString getText() const;
+
+public slots:
+ void logTransfer(uint id, const QString & filename, const QString & message);
+
+protected:
+ void closeEvent(QCloseEvent *);
+
+private:
+ QTextEdit * mixed_log;
+ SeparatedLog *sep_log;
+};
+
+
+#endif // _LOGWINDOW_H
diff --git a/kget/main.cpp b/kget/main.cpp
new file mode 100644
index 00000000..cd3341b5
--- /dev/null
+++ b/kget/main.cpp
@@ -0,0 +1,225 @@
+/***************************************************************************
+* main.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <kwin.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kurl.h>
+#include <kuniqueapplication.h>
+#include <kstartupinfo.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "kmainwidget.h"
+#include "version.h"
+
+
+
+static const char description[] = I18N_NOOP("An advanced download manager for KDE");
+
+static const char version[] = KGETVERSION;
+
+
+static KCmdLineOptions option[] = {
+ { "showDropTarget", I18N_NOOP("Start KGet with drop target"), 0 },
+ {"+[URL(s)]", I18N_NOOP("URL(s) to download"), 0},
+ KCmdLineLastOption
+ };
+
+static void cleanup(void);
+static void setSignalHandler(void (*handler) (int));
+
+//static msg_handler oldMsgHandler = 0L;
+
+//-----------------------------------------------------------------------------
+// Crash recovery signal handler
+static void signalHandler(int sigId)
+{
+ fprintf(stderr, "*** KGet got signal %d\n", sigId);
+
+ if (sigId != SIGSEGV && kmain) {
+ fprintf(stderr, "*** KGet saving data\n");
+ delete kmain;
+ }
+ // If Kget crashes again below this line we consider the data lost :-|
+ // Otherwise Kget will end in an infinite loop.
+ setSignalHandler(SIG_DFL);
+ cleanup();
+ exit(1);
+}
+
+
+//-----------------------------------------------------------------------------
+static void setSignalHandler(void (*handler) (int))
+{
+ signal(SIGSEGV, handler);
+ signal(SIGKILL, handler);
+ signal(SIGTERM, handler);
+ signal(SIGHUP, handler);
+ signal(SIGFPE, handler);
+ signal(SIGABRT, handler);
+
+ // catch also the keyboard interrupt
+ signal(SIGINT, handler);
+}
+
+
+static void cleanup(void)
+{
+ qInstallMsgHandler(0L /*oldMsgHandler*/);
+// QString cmd;
+}
+
+
+class KGetApp : public KUniqueApplication
+{
+private:
+ KMainWidget *kmainwidget;
+
+public:
+ KGetApp() : KUniqueApplication()
+ {
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+
+ kmainwidget=0;
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+ }
+
+ ~KGetApp()
+ {
+#ifdef _DEBUG
+ sDebugIn << endl;
+#endif
+ delete kmainwidget;
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+ }
+
+
+ int newInstance()
+ {
+#ifdef _DEBUG
+ sDebugIn <<"kmainwidget="<<kmainwidget << endl;
+#endif
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+
+ if (kmainwidget==0)
+ {
+ if(args->count()>0)
+ kmainwidget=new KMainWidget(true);
+ else
+ kmainwidget=new KMainWidget();
+ setMainWidget(kmain);
+ }
+
+ else
+ KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
+
+ if (args->isSet("showDropTarget"))
+ kmain->activateDropTarget();
+
+ if (args->count()==1)
+ {
+#ifdef _DEBUG
+ sDebug <<"args(0)= "<<args->arg(0) << endl;
+#endif
+ QString txt(args->arg(0));
+ if ( txt.endsWith( ".kgt" ) )
+ kmain->readTransfersEx(KURL::fromPathOrURL( txt ));
+ else
+ kmain->addTransferEx( KURL::fromPathOrURL( txt ),
+ KURL());
+ }
+ else if(args->count()>=2)
+ {
+ KURL::List urls;
+ for( int i=0; i < args->count(); ++i){
+ urls.append(KURL::fromPathOrURL( args->arg(i)));
+ }
+
+ // Sometimes valid filenames are not recognised by KURL::isLocalFile(), they are marked as invalid then
+ if ( args->count()==2 && ( urls.last().isLocalFile() || !urls.last().isValid()))
+ {
+ kmain->addTransferEx( urls.first(), urls.last() );
+ }
+ else
+ {
+ kmain->addTransfers( urls, QString() );
+ }
+ }
+ args->clear();
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+
+ return 0;
+ }
+};
+
+
+/////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutData("kget", I18N_NOOP("KGet"), version, description, KAboutData::License_GPL, "(C) 2001 - 2002, Patrick Charbonnier \n(C) 2002, Carsten Pfeiffer\n(C) 1998 - 2000, Matej Koss", "kget@kde.org", 0);
+
+ aboutData.addAuthor("Patrick Charbonnier", 0, "pch@freeshell.org");
+ aboutData.addAuthor("Carsten Pfeiffer", 0, "pfeiffer@kde.org");
+ aboutData.addAuthor("Matej Koss", 0);
+
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(option);
+
+ KGetApp::addCmdLineOptions();
+
+ if (!KGetApp::start()) {
+ fprintf(stderr, "kget is already running!\n");
+ return 0;
+ }
+
+ KGetApp kApp;
+
+// disabling he custom signal handler, so at least we have the backtraces for
+// crashes...
+// setSignalHandler(signalHandler);
+ kApp.exec();
+
+ cleanup();
+}
diff --git a/kget/pics/Makefile.am b/kget/pics/Makefile.am
new file mode 100644
index 00000000..6ca36d36
--- /dev/null
+++ b/kget/pics/Makefile.am
@@ -0,0 +1,11 @@
+pics_DATA = connect0.png connect1.png connect2.png connect3.png \
+ connect4.png connect5.png connect6.png connect7.png \
+ try0.png try1.png try2.png try3.png \
+ try4.png try5.png try6.png try7.png \
+ md_delayed.png md_finished.png md_queued.png md_scheduled.png \
+ retrying.png target.png
+
+picsdir = $(kde_datadir)/kget/pics
+
+EXTRA_DIST = $(pics_DATA)
+
diff --git a/kget/pics/connect0.png b/kget/pics/connect0.png
new file mode 100644
index 00000000..658ce773
--- /dev/null
+++ b/kget/pics/connect0.png
Binary files differ
diff --git a/kget/pics/connect1.png b/kget/pics/connect1.png
new file mode 100644
index 00000000..9d2e472a
--- /dev/null
+++ b/kget/pics/connect1.png
Binary files differ
diff --git a/kget/pics/connect2.png b/kget/pics/connect2.png
new file mode 100644
index 00000000..3e948c8d
--- /dev/null
+++ b/kget/pics/connect2.png
Binary files differ
diff --git a/kget/pics/connect3.png b/kget/pics/connect3.png
new file mode 100644
index 00000000..1e700ddf
--- /dev/null
+++ b/kget/pics/connect3.png
Binary files differ
diff --git a/kget/pics/connect4.png b/kget/pics/connect4.png
new file mode 100644
index 00000000..5f651e5f
--- /dev/null
+++ b/kget/pics/connect4.png
Binary files differ
diff --git a/kget/pics/connect5.png b/kget/pics/connect5.png
new file mode 100644
index 00000000..eb951247
--- /dev/null
+++ b/kget/pics/connect5.png
Binary files differ
diff --git a/kget/pics/connect6.png b/kget/pics/connect6.png
new file mode 100644
index 00000000..23b7ef14
--- /dev/null
+++ b/kget/pics/connect6.png
Binary files differ
diff --git a/kget/pics/connect7.png b/kget/pics/connect7.png
new file mode 100644
index 00000000..02ddd04f
--- /dev/null
+++ b/kget/pics/connect7.png
Binary files differ
diff --git a/kget/pics/md_delayed.png b/kget/pics/md_delayed.png
new file mode 100644
index 00000000..17d135a9
--- /dev/null
+++ b/kget/pics/md_delayed.png
Binary files differ
diff --git a/kget/pics/md_finished.png b/kget/pics/md_finished.png
new file mode 100644
index 00000000..5b1dac57
--- /dev/null
+++ b/kget/pics/md_finished.png
Binary files differ
diff --git a/kget/pics/md_queued.png b/kget/pics/md_queued.png
new file mode 100644
index 00000000..93e004b7
--- /dev/null
+++ b/kget/pics/md_queued.png
Binary files differ
diff --git a/kget/pics/md_scheduled.png b/kget/pics/md_scheduled.png
new file mode 100644
index 00000000..79f751df
--- /dev/null
+++ b/kget/pics/md_scheduled.png
Binary files differ
diff --git a/kget/pics/retrying.png b/kget/pics/retrying.png
new file mode 100644
index 00000000..7c107550
--- /dev/null
+++ b/kget/pics/retrying.png
Binary files differ
diff --git a/kget/pics/target.png b/kget/pics/target.png
new file mode 100644
index 00000000..e71e668e
--- /dev/null
+++ b/kget/pics/target.png
Binary files differ
diff --git a/kget/pics/try0.png b/kget/pics/try0.png
new file mode 100644
index 00000000..6d0cf4d1
--- /dev/null
+++ b/kget/pics/try0.png
Binary files differ
diff --git a/kget/pics/try1.png b/kget/pics/try1.png
new file mode 100644
index 00000000..5041101c
--- /dev/null
+++ b/kget/pics/try1.png
Binary files differ
diff --git a/kget/pics/try2.png b/kget/pics/try2.png
new file mode 100644
index 00000000..f6f67818
--- /dev/null
+++ b/kget/pics/try2.png
Binary files differ
diff --git a/kget/pics/try3.png b/kget/pics/try3.png
new file mode 100644
index 00000000..b9af2b38
--- /dev/null
+++ b/kget/pics/try3.png
Binary files differ
diff --git a/kget/pics/try4.png b/kget/pics/try4.png
new file mode 100644
index 00000000..0948b759
--- /dev/null
+++ b/kget/pics/try4.png
Binary files differ
diff --git a/kget/pics/try5.png b/kget/pics/try5.png
new file mode 100644
index 00000000..f6f67818
--- /dev/null
+++ b/kget/pics/try5.png
Binary files differ
diff --git a/kget/pics/try6.png b/kget/pics/try6.png
new file mode 100644
index 00000000..5041101c
--- /dev/null
+++ b/kget/pics/try6.png
Binary files differ
diff --git a/kget/pics/try7.png b/kget/pics/try7.png
new file mode 100644
index 00000000..9acacca1
--- /dev/null
+++ b/kget/pics/try7.png
Binary files differ
diff --git a/kget/safedelete.cpp b/kget/safedelete.cpp
new file mode 100644
index 00000000..c4cb6915
--- /dev/null
+++ b/kget/safedelete.cpp
@@ -0,0 +1,35 @@
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include <qfileinfo.h>
+
+#include "safedelete.h"
+
+bool SafeDelete::deleteFile( const KURL& url )
+{
+ if ( url.isLocalFile() )
+ {
+ QFileInfo info( url.path() );
+ if ( info.isDir() )
+ {
+ KMessageBox::information(0L,
+ i18n("Not deleting\n%1\nas it is a "
+ "directory.").arg( url.prettyURL() ),
+ i18n("Not Deleted"));
+ return false;
+ }
+
+ KIO::NetAccess::del( url, 0L );
+ return true;
+ }
+
+ else
+ KMessageBox::information( 0L,
+ i18n("Not deleting\n%1\nas it is not a local"
+ " file.").arg( url.prettyURL() ),
+ i18n("Not Deleted") );
+
+ return false;
+}
diff --git a/kget/safedelete.h b/kget/safedelete.h
new file mode 100644
index 00000000..c93ebb67
--- /dev/null
+++ b/kget/safedelete.h
@@ -0,0 +1,19 @@
+/****************************************************************************
+** $Id$
+**
+** Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+**
+****************************************************************************/
+
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+class KURL;
+
+class SafeDelete
+{
+public:
+ static bool deleteFile( const KURL& url );
+};
+
+#endif // SAFEDELETE_H
diff --git a/kget/settings.cpp b/kget/settings.cpp
new file mode 100644
index 00000000..da066a6a
--- /dev/null
+++ b/kget/settings.cpp
@@ -0,0 +1,287 @@
+/***************************************************************************
+* settings.cpp
+* -------------------
+*
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+*
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kwin.h>
+#include <kmessagebox.h>
+#include <kglobalsettings.h>
+#include <netwm_def.h>
+
+#include <qdir.h>
+
+#include "kmainwidget.h"
+#include "transferlist.h"
+#include "droptarget.h"
+#include "settings.h"
+#include "common.h"
+#include "version.h"
+
+static const unsigned long DEFAULT_DOCK_STATE = (NET::SkipTaskbar | NET::StaysOnTop);
+
+QString ConnectionDevices[6] = {
+ "",
+ "eth",
+ "plip",
+ "slip",
+ "ppp",
+ "isdn"
+ };
+
+
+void
+Settings::load()
+{
+ sDebug << "Loading settings" << endl;
+
+ KConfig *config = kapp->config();
+
+ // read system options
+ config->setGroup("System");
+
+ // read connection options
+ config->setGroup("Connection");
+
+ b_reconnectOnError = config->readBoolEntry("ReconnectOnError", DEF_ReconnectOnError);
+ reconnectTime = config->readNumEntry("ReconnectTime", DEF_ReconnectTime);
+ reconnectRetries = config->readNumEntry("ReconnectRetries", DEF_ReconnectRetries);
+ b_reconnectOnBroken = config->readBoolEntry("ReconnectOnBroken", DEF_ReconnectOnBroken);
+
+ timeoutData = config->readNumEntry("TimeoutData", DEF_TimeoutData);
+ timeoutDataNoResume = config->readNumEntry("TimeoutDataNoResume", DEF_TimeoutDataNoResume);
+
+ connectionType = config->readNumEntry("ConnectionType", DEF_ConnectionType);
+ linkNumber = config->readNumEntry("LinkNumber", DEF_LinkNumber);
+ b_offlineMode = config->readBoolEntry("OfflineMode", DEF_OfflineMode);
+
+ // read automation options
+ config->setGroup("Automation");
+
+ b_autoSave = config->readBoolEntry("AutoSave", DEF_AutoSave);
+ autoSaveInterval = config->readNumEntry("AutoSaveInterval", DEF_AutoSaveInterval);
+
+ b_autoDisconnect = config->readBoolEntry("AutoDisconnect", DEF_AutoDisconnect);
+ disconnectCommand = config->readPathEntry("DisconnectCommand", DEF_DisconnectCommand);
+
+ b_timedDisconnect = config->readBoolEntry("TimedDisconnect", DEF_TimedDisconnect);
+ disconnectTime.setHMS(config->readNumEntry("DisconnectTimeHour"), config->readNumEntry("DisconnectTimeMinute"), 0);
+
+ disconnectDate = QDate::currentDate(); // doesn't make sense to save it
+
+ b_autoShutdown = config->readBoolEntry("AutoShutdown", DEF_AutoShutdown);
+ b_autoPaste = config->readBoolEntry("AutoPaste", DEF_AutoPaste);
+
+ // read limits options
+ config->setGroup("Limits");
+
+ maxSimultaneousConnections = config->readNumEntry("MaxSimConnections", DEF_MaxSimConnections);
+ minimumBandwidth = config->readNumEntry("MinimumBandwidth", DEF_MinimumBandwidth);
+ maximumBandwidth = config->readNumEntry("MaximumBandwidth", DEF_MaximumBandwidth);
+
+ // read advanced options
+ config->setGroup("Advanced");
+
+ b_addQueued = config->readBoolEntry("AddQueued", DEF_AddQueued);
+ b_showMain = config->readBoolEntry("ShowMain", DEF_ShowMain);
+ b_showIndividual = config->readBoolEntry("ShowIndividual", DEF_ShowIndividual);
+ b_iconifyIndividual = config->readBoolEntry("IconifyIndividual", DEF_IconifyIndividual);
+ b_advancedIndividual = config->readBoolEntry("AdvancedIndividual", DEF_AdvancedIndividual);
+
+ b_removeOnSuccess = config->readBoolEntry("RemoveOnSuccess", DEF_RemoveOnSuccess);
+ b_getSizes = config->readBoolEntry("GetSizes", DEF_GetSizes);
+ b_expertMode = config->readBoolEntry("ExpertMode", DEF_ExpertMode);
+
+ // read if the integration whith konqueror is enabled
+
+ KConfig *cfg = new KConfig("konquerorrc", false, false);
+ cfg->setGroup("HTML Settings");
+ QString downloadManager=cfg->readPathEntry("DownloadManager");
+
+ b_KonquerorIntegration=(downloadManager==KGET_APP_NAME)?true:false;
+
+ // check if we already asked about konqueror integration
+ if(config->readBoolEntry("FirstRun",true))
+ {
+ config->writeEntry("FirstRun",false);
+ bool bAnswerYes = KMessageBox::questionYesNo(0L,i18n("This is the first time that you have run KGet.\nDo you want to use KGet as Download Manager for Konqueror?"), i18n("Konqueror Integration"), i18n("Enable"), i18n("Do Not Enable")) == KMessageBox::Yes;
+ if (bAnswerYes)
+ {
+ cfg->writePathEntry("DownloadManager", QString(KGET_APP_NAME));
+ cfg->sync();
+ b_KonquerorIntegration=true;
+ }
+ }
+ delete cfg;
+
+
+ // read search options
+ config->setGroup("Search");
+ b_searchFastest = config->readBoolEntry("SearchFastest", DEF_SearchFastest);
+ searchItems = config->readNumEntry("SearchItems", DEF_SearchItems);
+ timeoutSearch = config->readNumEntry("TimeoutSearch", DEF_TimeoutSearch);
+ b_switchHosts = config->readBoolEntry("SwitchHosts", DEF_SwitchHosts);
+
+ // read directory options
+ config->setGroup("Directories");
+
+ b_useLastDir = config->readBoolEntry("UseLastDirectory", DEF_UseLastDir);
+ lastDirectory = config->readPathEntry("LastDirectory",
+ QString("file:") + QDir::currentDirPath() );
+
+ QStringList strList;
+
+ strList = config->readPathListEntry("Items");
+
+ defaultDirList.clear();
+ QStringList::Iterator it = strList.begin();
+ for (; it != strList.end(); ++it) {
+ DirItem item;
+
+ item.extRegexp = *it;
+ ++it;
+ item.defaultDir = *it;
+ defaultDirList.append(item);
+ }
+
+ // read misc settings
+ config->setGroup("Misc");
+
+ QFont font = KGlobalSettings::generalFont();
+
+ listViewFont = config->readFontEntry("Font", &font);
+
+ // read main window geometry settings
+ config->setGroup("MainGeometry");
+ const QPoint point(-1,-1);
+ mainPosition = config->readPointEntry("Position", &point);
+ mainSize = config->readSizeEntry("Size");
+ mainState = config->readUnsignedLongNumEntry("State", 0);
+
+ // read drop target geometry settings
+ config->setGroup("DropGeometry");
+ dropPosition = config->readPointEntry("Position", &point);
+ dropState = config->readUnsignedLongNumEntry("State", DEFAULT_DOCK_STATE );
+
+ // flushing pending changes
+ config->sync();
+}
+
+
+void Settings::save()
+{
+ sDebug << "Saving settings" << endl;
+
+ KConfig *config = kapp->config();
+
+ // write connection options
+ config->setGroup("Connection");
+ config->writeEntry("ReconnectOnError", b_reconnectOnError);
+ config->writeEntry("ReconnectTime", reconnectTime);
+ config->writeEntry("ReconnectRetries", reconnectRetries);
+ config->writeEntry("ReconnectOnBroken", b_reconnectOnBroken);
+ config->writeEntry("TimeoutData", timeoutData);
+ config->writeEntry("TimeoutDataNoResume", timeoutDataNoResume);
+ config->writeEntry("ConnectionType", connectionType);
+ config->writeEntry("LinkNumber", linkNumber);
+ config->writeEntry("OfflineMode", b_offlineMode);
+
+ // write automation options
+ config->setGroup("Automation");
+ config->writeEntry("AutoSave", b_autoSave);
+ config->writeEntry("AutoSaveInterval", autoSaveInterval);
+ config->writeEntry("AutoDisconnect", b_autoDisconnect);
+ config->writePathEntry("DisconnectCommand", disconnectCommand);
+ config->writeEntry("TimedDisconnect", b_timedDisconnect);
+ config->writeEntry("DisconnectTimeHour", disconnectTime.hour());
+ config->writeEntry("DisconnectTimeMinute", disconnectTime.minute());
+ config->writeEntry("AutoShutdown", b_autoShutdown);
+ config->writeEntry("AutoPaste", b_autoPaste);
+
+ // write limits options
+ config->setGroup("Limits");
+ config->writeEntry("MaxSimConnections", maxSimultaneousConnections);
+ config->writeEntry("MinimumBandwidth", minimumBandwidth);
+ config->writeEntry("MaximumBandwidth", maximumBandwidth);
+
+ // write advanced options
+ config->setGroup("Advanced");
+ config->writeEntry("ShowMain", b_showMain);
+ config->writeEntry("AddQueued", b_addQueued);
+ config->writeEntry("ShowIndividual", b_showIndividual);
+ config->writeEntry("IconifyIndividual", b_iconifyIndividual);
+ config->writeEntry("AdvancedIndividual", b_advancedIndividual);
+ config->writeEntry("RemoveOnSuccess", b_removeOnSuccess);
+ config->writeEntry("GetSizes", b_getSizes);
+ config->writeEntry("ExpertMode", b_expertMode);
+ config->writeEntry("KonquerorIntegration",b_KonquerorIntegration);
+
+
+ // write search options
+ config->setGroup("Search");
+ config->writeEntry("SearchFastest", b_searchFastest);
+ config->writeEntry("SearchItems", searchItems);
+ config->writeEntry("TimeoutSearch", timeoutSearch);
+ config->writeEntry("SwitchHosts", b_switchHosts);
+
+ // write directory options
+ config->setGroup("Directories");
+ config->writeEntry("UseLastDirectory", b_useLastDir);
+ config->writePathEntry("LastDirectory", lastDirectory );
+ DirList::Iterator it;
+ QStringList lst;
+
+ for (it = defaultDirList.begin(); it != defaultDirList.end(); ++it) {
+ lst.append((*it).extRegexp);
+ lst.append((*it).defaultDir);
+ }
+ config->writePathEntry("Items", lst);
+
+ // write system options
+ config->setGroup("System");
+ config->writeEntry("UseAnimation", b_useAnimation);
+
+ // write misc options
+ config->setGroup("Misc");
+ config->writeEntry("Font", listViewFont);
+
+ // write main window geometry properties
+ config->setGroup("MainGeometry");
+ config->writeEntry("Position", kmain->pos());
+ config->writeEntry("Size", kmain->size());
+ config->writeEntry("State", KWin::windowInfo(kmain->winId()).state());
+
+ // write drop target geometry properties
+ config->setGroup("DropGeometry");
+ config->writeEntry("Position", kdrop->pos());
+ unsigned long state = KWin::windowInfo(kdrop->winId()).state();
+ // state will be 0L if droptarget is hidden. Sigh.
+ config->writeEntry("State", state ? state : DEFAULT_DOCK_STATE );
+
+
+ config->sync();
+}
diff --git a/kget/settings.h b/kget/settings.h
new file mode 100644
index 00000000..c66b00e6
--- /dev/null
+++ b/kget/settings.h
@@ -0,0 +1,186 @@
+/***************************************************************************
+* settings.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _SETTINGS_H
+#define _SETTINGS_H
+
+// common connection types
+enum ConnectionType { PERMANENT = 0, ETHERNET, PLIP, SLIP, PPP, ISDN };
+
+extern QString ConnectionDevices[];
+
+
+// Connection settings
+#define DEF_ReconnectOnError true
+#define DEF_ReconnectTime 1
+#define DEF_ReconnectRetries 10
+#define DEF_ReconnectOnBroken true
+
+#define DEF_TimeoutData 5
+#define DEF_TimeoutDataNoResume 15
+
+#define DEF_ConnectionType PERMANENT
+#define DEF_LinkNumber 0
+#define DEF_OfflineMode false
+
+// Automation settings
+#define DEF_AutoSave true
+#define DEF_AutoSaveInterval 10
+#define DEF_AutoDisconnect false
+#define DEF_DisconnectCommand "kppp -k"
+#define DEF_TimedDisconnect false
+#define DEF_AutoShutdown false
+#define DEF_AutoPaste false
+
+// Limits settings
+#define DEF_MaxSimConnections 2
+#define DEF_MinimumBandwidth 1000
+#define DEF_MaximumBandwidth 10000
+
+// Advanced settings
+#define DEF_AddQueued true
+#define DEF_ShowMain false
+#define DEF_ShowIndividual false
+#define DEF_IconifyIndividual false
+#define DEF_AdvancedIndividual false
+#define DEF_RemoveOnSuccess true
+#define DEF_GetSizes true
+#define DEF_ExpertMode false
+
+// Search settings
+#define DEF_SearchFastest false
+#define DEF_SearchItems 20
+#define DEF_TimeoutSearch 30
+#define DEF_SwitchHosts false
+
+// Directories settings
+#define DEF_UseLastDir false
+
+// System settings
+#define DEF_UseAnimation true
+
+#define DEF_WindowStyle NORMAL
+
+
+#include <qdatetime.h>
+
+#include <kwin.h>
+#include <qvaluelist.h>
+struct DirItem
+{
+ QString extRegexp;
+ QString defaultDir;
+};
+
+typedef QValueList < DirItem > DirList;
+
+class Settings
+{
+
+public:
+
+ Settings()
+ {
+ }
+ ~Settings()
+ {
+ }
+
+ void load();
+ void save();
+
+ // connection options
+ bool b_reconnectOnBroken;
+ bool b_reconnectOnError;
+
+ uint reconnectTime;
+ uint reconnectRetries;
+
+ uint timeoutData;
+ uint timeoutDataNoResume;
+
+ uint connectionType;
+ uint linkNumber;
+ bool b_offlineMode;// If we want to be offline
+ bool b_offline; // If we really are offline
+
+
+ // automation options
+ bool b_autoSave;
+ uint autoSaveInterval;
+ bool b_autoDisconnect;
+ QString disconnectCommand;
+ bool b_timedDisconnect;
+ QDate disconnectDate;
+ QTime disconnectTime;
+ bool b_autoShutdown;
+ bool b_autoPaste;
+
+ // limits options
+ uint maxSimultaneousConnections;
+ uint minimumBandwidth;
+ uint maximumBandwidth;
+
+ // advanced options
+ bool b_addQueued;
+ bool b_showIndividual;
+ bool b_iconifyIndividual;
+ bool b_advancedIndividual;
+ bool b_removeOnSuccess;
+ bool b_getSizes;
+ bool b_expertMode;
+ bool b_KonquerorIntegration;
+ bool b_showMain;
+ // search options
+ bool b_searchFastest;
+ uint searchItems;
+ uint timeoutSearch;
+ bool b_switchHosts;
+
+ // directories options
+ bool b_useLastDir;
+ QString lastDirectory;
+
+ DirList defaultDirList;
+
+ // system options
+ bool b_useAnimation;
+ QFont listViewFont;
+
+
+ // geometry settings
+ QPoint mainPosition;
+ QSize mainSize;
+ unsigned long int mainState;
+
+ QPoint dropPosition;
+ unsigned long int dropState;
+};
+
+extern Settings ksettings;
+
+#endif // _SETTINGS_H
diff --git a/kget/slave.cpp b/kget/slave.cpp
new file mode 100644
index 00000000..944a7f37
--- /dev/null
+++ b/kget/slave.cpp
@@ -0,0 +1,331 @@
+/***************************************************************************
+* slave.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include <kapplication.h>
+#include <kio/scheduler.h>
+
+#include "getfilejob.h"
+#include "slave.h"
+#include "slaveevent.h"
+#include "transfer.h"
+
+#include <assert.h>
+
+Slave::Slave(Transfer * _parent, const KURL & _src, const KURL & _dest)
+ : QObject(),
+ QThread()
+{
+ mDebug << ">>>>Entering" << endl;
+ copyjob = NULL;
+ m_src = _src;
+ m_dest = _dest;
+ m_parent = _parent;
+
+ nPendingCommand = 0;
+
+ mDebug << ">>>>Leaving" << endl;
+}
+
+Slave::~Slave()
+{}
+
+void Slave::Op(SlaveCommand _cmd)
+{
+ mDebugIn << " _cmd = " << _cmd << endl;
+
+ if ( !running() ) // start on demand
+ start();
+
+ mutex.lock();
+ stack.push(_cmd);
+ nPendingCommand++;
+ worker.wakeOne();
+ mutex.unlock();
+
+ mDebugOut << endl;
+}
+
+/** No descriptions */
+void Slave::PostMessage(SlaveResult _event, Q_ULLONG _data)
+{
+ SlaveEvent *e1 = new SlaveEvent(m_parent, _event, _data);
+
+ QApplication::postEvent(kapp->mainWidget(), (QEvent *) e1);
+}
+
+void Slave::PostMessage(SlaveResult _event, const QString & _msg)
+{
+ SlaveEvent *e1 = new SlaveEvent(m_parent, _event, _msg);
+
+ QApplication::postEvent(kapp->mainWidget(), (QEvent *) e1);
+ mDebug << "Msg:" << "_msg = " << _msg << endl;
+}
+
+void Slave::InfoMessage(const QString & _msg)
+{
+ SlaveEvent *e1 = new SlaveEvent(m_parent, SLV_INFO, _msg);
+
+ QApplication::postEvent(kapp->mainWidget(), (QEvent *) e1);
+ mDebug << "Infor Msg:" << "_msg = " << _msg << endl;
+}
+
+
+
+void Slave::run()
+{
+ mDebugIn << endl;
+
+ SlaveCommand cmd;
+ bool running = true;
+
+ while (running)
+ {
+ if (!nPendingCommand)
+ worker.wait();
+ switch (cmd = fetch_cmd())
+ {
+ case RESTART:
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ // fall through
+ case RETR:
+ mDebug << " FETCHED COMMAND RETR" << endl;
+ assert(!copyjob);
+ KIO::Scheduler::checkSlaveOnHold( true );
+ copyjob = new KIO::GetFileJob(m_src, m_dest);
+ Connect();
+ PostMessage(SLV_RESUMED);
+ break;
+
+ case RETR_CACHE:
+ mDebug << " FETCHED COMMAND RETR_CACHE" << endl;
+ assert(!copyjob);
+ KIO::Scheduler::checkSlaveOnHold( true );
+ copyjob = new KIO::GetFileJob(m_src, m_dest);
+ copyjob->addMetaData("cache", "cacheonly");
+ Connect();
+ break;
+
+ case PAUSE:
+ mDebug << " FETCHED COMMAND PAUSE" << endl;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ PostMessage(SLV_PAUSED);
+ break;
+
+ case KILL:
+ mDebug << " FETCHED COMMAND KILL" << endl;
+ running = false;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ // no message posted
+ break;
+
+ case REMOVE:
+ mDebug << " FETCHED COMMAND REMOVE" << endl;
+ running = false;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ PostMessage(SLV_REMOVED);
+ break;
+
+ case SCHEDULE:
+ mDebug << " FETCHED COMMAND SCHEDULE" << endl;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ PostMessage(SLV_SCHEDULED);
+ break;
+
+ case DELAY:
+ mDebug << " FETCHED COMMAND DELAY" << endl;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ PostMessage(SLV_DELAYED);
+ break;
+
+ case NOOP:
+ mDebug << "FETCHED COMMAND NOOP, i.e. empty stack" << endl;
+ if (copyjob) {
+ copyjob->kill(true);
+ copyjob = 0L;
+ }
+ running = false;
+ break;
+
+ default:
+ mDebug << " UNKNOWN COMMAND DIE...." << endl;
+ assert(0);
+ }
+ }
+
+ assert(!copyjob);
+ mDebugOut << endl;
+}
+
+
+Slave::SlaveCommand Slave::fetch_cmd()
+{
+ mutex.lock();
+ SlaveCommand cmd = NOOP;
+ if ( !stack.isEmpty() )
+ {
+ nPendingCommand--;
+ cmd = stack.pop();
+ }
+ mutex.unlock();
+ return cmd;
+}
+
+
+void Slave::Connect()
+{
+ mDebugIn << endl;
+
+
+ connect(copyjob, SIGNAL(canceled(KIO::Job *)), SLOT(slotCanceled(KIO::Job *)));
+ connect(copyjob, SIGNAL(connected(KIO::Job *)), SLOT(slotConnected(KIO::Job *)));
+ connect(copyjob, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *)));
+
+ connect(copyjob, SIGNAL(totalSize(KIO::Job *, KIO::filesize_t)), SLOT(slotTotalSize(KIO::Job *, KIO::filesize_t)));
+
+ connect(copyjob, SIGNAL(processedSize(KIO::Job *, KIO::filesize_t)), SLOT(slotProcessedSize(KIO::Job *, KIO::filesize_t)));
+
+ connect(copyjob, SIGNAL(speed(KIO::Job *, unsigned long)), SLOT(slotSpeed(KIO::Job *, unsigned long)));
+
+ connect(copyjob, SIGNAL(infoMessage(KIO::Job *, const QString &)), SLOT(slotInfoMessage(KIO::Job *, const QString &)));
+
+ mDebugOut << endl;
+}
+
+
+void Slave::slotCanceled(KIO::Job *)
+{
+ mDebugIn << endl;
+
+
+ mDebugOut << endl;
+}
+
+void Slave::slotConnected(KIO::Job *)
+{
+ mDebugIn << endl;
+
+
+ mDebugOut << endl;
+}
+
+void Slave::slotResult(KIO::Job * job)
+{
+ mDebugIn << endl;
+
+ assert(copyjob == job);
+ copyjob=0L;
+ KIO::Error error=KIO::Error(job->error());
+ if (!error) {
+ PostMessage(SLV_FINISHED);
+ }
+ else {
+ if (m_parent->getMode() == Transfer::MD_NEW)
+ {
+ PostMessage(SLV_NOTINCACHE);
+ return;
+ }
+ QString tmsg="<font color=\"red\"> <b>" + job->errorString() + \
+ "</b></font>";
+ InfoMessage(tmsg);
+ if (m_parent->retryOnError() && \
+ ((error==KIO::ERR_COULD_NOT_LOGIN) || (error==KIO::ERR_SERVER_TIMEOUT))) {
+ //Timeout or login error
+ PostMessage(SLV_ERROR);
+ }
+ else if (m_parent->retryOnBroken() && (error==KIO::ERR_CONNECTION_BROKEN)) {
+ // Connection Broken
+ PostMessage(SLV_BROKEN);
+ }
+ else {
+ job->showErrorDialog();
+ PostMessage(SLV_DELAYED);
+ }
+ }
+ mDebugOut << endl;
+}
+
+
+void Slave::slotSpeed(KIO::Job *, unsigned long lSpeed)
+{
+ // mDebugIn<<endl;
+ PostMessage(SLV_PROGRESS_SPEED, lSpeed);
+ // mDebugOut<<endl;
+
+}
+
+void Slave::slotTotalSize(KIO::Job *, KIO::filesize_t _total_size)
+{
+ mDebugIn << "= " << _total_size << endl;
+
+ if ((int)_total_size == 0)//shouldn't happen, but does
+ return;
+
+ if (m_parent->getMode() != Transfer::MD_NEW)
+ {
+ PostMessage(SLV_CAN_RESUME, copyjob->getCanResume());
+ PostMessage(SLV_CONNECTED);
+ }
+ else
+ InfoMessage("checking if file is in cache...yes");
+ PostMessage(SLV_TOTAL_SIZE, _total_size);
+ mDebugOut << endl;
+}
+
+void Slave::slotProcessedSize(KIO::Job *, KIO::filesize_t _processed_size)
+{
+ // mDebugIn<<endl;
+ PostMessage(SLV_PROGRESS_SIZE, _processed_size);
+
+ // mDebugOut<<endl;
+}
+
+void Slave::slotInfoMessage(KIO::Job *, const QString & _msg)
+{
+ mDebugIn << "MSG=" << _msg << endl;
+ InfoMessage(_msg);
+ mDebugOut << endl;
+}
+
+#include "slave.moc"
diff --git a/kget/slave.h b/kget/slave.h
new file mode 100644
index 00000000..670b418b
--- /dev/null
+++ b/kget/slave.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+* slave.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef SLAVE_H
+#define SLAVE_H
+
+#include <qthread.h>
+#include <kurl.h>
+#include <qvaluestack.h>
+#include <qwaitcondition.h>
+#include <qmutex.h>
+#include <qobject.h>
+
+#include "common.h"
+
+namespace KIO
+{
+ class GetFileJob;
+}
+
+class Transfer;
+
+class Slave:public QObject, public QThread
+{
+ Q_OBJECT
+
+public:
+ enum SlaveCommand {
+ RETR, RETR_CACHE, PAUSE, RESTART, ABORT, DELAY,
+ SCHEDULE, REMOVE, KILL, NOOP
+ };
+
+ enum SlaveResult {
+
+ SLV_TOTAL_SIZE, SLV_PROGRESS_SIZE, SLV_PROGRESS_SPEED,
+ SLV_CAN_RESUME, SLV_CONNECTED,
+
+ SLV_RESUMED, SLV_PAUSED, SLV_ERROR, SLV_BROKEN, SLV_SCHEDULED, SLV_DELAYED,
+ SLV_FINISHED, SLV_INFO, SLV_REMOVED, SLV_KILLED, SLV_NOTINCACHE
+ };
+
+public:
+ Slave(Transfer * _parent, const KURL & _src, const KURL & _dest);
+ ~Slave();
+ void Op(SlaveCommand _cmd);
+
+protected:
+ virtual void run();
+
+private slots:
+ void slotCanceled(KIO::Job *);
+ void slotConnected(KIO::Job *);
+ void slotResult(KIO::Job *);
+ void slotTotalSize(KIO::Job *, KIO::filesize_t);
+ void slotProcessedSize(KIO::Job *, KIO::filesize_t);
+ void slotSpeed(KIO::Job *, unsigned long);
+ void slotInfoMessage(KIO::Job *, const QString &);
+
+private:
+ void Connect();
+
+ void PostMessage(SlaveResult _event, Q_ULLONG _data = 0L);
+ void PostMessage(SlaveResult _event, const QString & _msg);
+ void InfoMessage(const QString & _msg);
+
+ Transfer * m_parent;
+
+ KURL m_src;
+ KURL m_dest;
+
+ Slave::SlaveCommand fetch_cmd();
+ int nPendingCommand;
+
+ QValueStack < SlaveCommand > stack;
+ QWaitCondition worker;
+ QMutex mutex;
+ KIO::GetFileJob * copyjob;
+
+};
+
+#endif
diff --git a/kget/slaveevent.cpp b/kget/slaveevent.cpp
new file mode 100644
index 00000000..e0d41661
--- /dev/null
+++ b/kget/slaveevent.cpp
@@ -0,0 +1,72 @@
+/***************************************************************************
+* slaveevent.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* emai : pch@freeshell.org
+*
+***************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#include "slaveevent.h"
+
+#define EVENT_TYPE (QEvent::User + 252)
+
+SlaveEvent::SlaveEvent(Transfer * _item, unsigned int _event, Q_ULLONG _ldata):QCustomEvent(EVENT_TYPE)
+{
+ m_event = _event;
+ m_item = _item;
+ m_ldata = _ldata;
+ m_msg = QString("");
+}
+
+
+SlaveEvent::SlaveEvent(Transfer * _item, unsigned int _event, const QString & _msg):QCustomEvent(EVENT_TYPE)
+{
+ m_event = _event;
+ m_item = _item;
+ m_ldata = 0L;
+ m_msg = _msg;
+}
+
+
+SlaveEvent::~SlaveEvent()
+{
+}
+
+unsigned int
+SlaveEvent::getEvent() const
+{
+ return m_event;
+}
+
+Transfer *SlaveEvent::getItem() const
+{
+ return m_item;
+}
+
+Q_ULLONG SlaveEvent::getData() const
+{
+ return m_ldata;
+}
+
+const QString & SlaveEvent::getMsg() const
+{
+ return m_msg;
+}
diff --git a/kget/slaveevent.h b/kget/slaveevent.h
new file mode 100644
index 00000000..6fb5f96b
--- /dev/null
+++ b/kget/slaveevent.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+* slaveevent.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* email :pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef SLAVEEVENT_H
+#define SLAVEEVENT_H
+
+#include <qevent.h>
+
+class Transfer;
+
+/**
+ *@author Patrick Charbonnier
+ */
+
+class SlaveEvent:public QCustomEvent
+{
+public:
+ SlaveEvent(Transfer * _item, unsigned int _event, Q_ULLONG _ldata = 0L);
+ SlaveEvent(Transfer * _item, unsigned int _event, const QString & _msg);
+ ~SlaveEvent();
+
+ unsigned int getEvent() const;
+ Transfer *getItem() const;
+ Q_ULLONG getData() const;
+ const QString & getMsg() const;
+
+
+private:
+ unsigned int m_event;
+ Transfer *m_item;
+ Q_ULLONG m_ldata;
+ QString m_msg;
+
+};
+
+#endif
diff --git a/kget/sounds/KGet_Added.ogg b/kget/sounds/KGet_Added.ogg
new file mode 100644
index 00000000..8647db9c
--- /dev/null
+++ b/kget/sounds/KGet_Added.ogg
Binary files differ
diff --git a/kget/sounds/KGet_Finished.ogg b/kget/sounds/KGet_Finished.ogg
new file mode 100644
index 00000000..fd06b360
--- /dev/null
+++ b/kget/sounds/KGet_Finished.ogg
Binary files differ
diff --git a/kget/sounds/KGet_Finished_All.ogg b/kget/sounds/KGet_Finished_All.ogg
new file mode 100644
index 00000000..45fbfacd
--- /dev/null
+++ b/kget/sounds/KGet_Finished_All.ogg
Binary files differ
diff --git a/kget/sounds/KGet_Started.ogg b/kget/sounds/KGet_Started.ogg
new file mode 100644
index 00000000..8b139ad8
--- /dev/null
+++ b/kget/sounds/KGet_Started.ogg
Binary files differ
diff --git a/kget/sounds/Makefile.am b/kget/sounds/Makefile.am
new file mode 100644
index 00000000..745b5a70
--- /dev/null
+++ b/kget/sounds/Makefile.am
@@ -0,0 +1,4 @@
+kde_sound_DATA = KGet_Added.ogg KGet_Finished.ogg KGet_Finished_All.ogg \
+ KGet_Started.ogg
+
+EXTRA_DIST = $(kde_sound_DATA)
diff --git a/kget/transfer.cpp b/kget/transfer.cpp
new file mode 100644
index 00000000..44e76fae
--- /dev/null
+++ b/kget/transfer.cpp
@@ -0,0 +1,1025 @@
+/***************************************************************************
+* transfer.cpp
+* -------------------
+*
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002, 2003, 2004, 2005 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public Lkio/global.h:icense as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <qheader.h>
+
+#include <kurl.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kaction.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <kmessagebox.h>
+
+#include <assert.h>
+#include "safedelete.h"
+#include "settings.h"
+#include "logwindow.h"
+#include "kmainwidget.h"
+#include "dlgIndividual.h"
+#include "transferlist.h"
+#include "transfer.h"
+
+#include <kapplication.h>
+#include <kio/passdlg.h>
+#include <kio/global.h>
+#include <kio/netaccess.h>
+
+
+extern Settings ksettings;
+
+
+Transfer::Transfer(TransferList * _view, const KURL & _src, const KURL & _dest, const uint _id)
+ : QObject( _view ),
+ KListViewItem(_view),
+ dlgIndividual( 0 )
+{
+ sDebugIn << endl;
+
+ src = _src;
+ dest = _dest;
+
+ view = _view;
+ init(_id);
+
+ sDebugOut << endl;
+}
+
+
+Transfer::Transfer(TransferList * _view, Transfer * after, const KURL & _src, const KURL & _dest, const uint _id)
+ : QObject( _view ),
+ KListViewItem(_view, (QListViewItem *) after),
+ src(_src), dest(_dest), view(_view),
+ dlgIndividual( 0 )
+{
+ sDebugIn << endl;
+
+ view = _view;
+ src = _src;
+ dest = _dest;
+ init(_id);
+
+ sDebugOut << endl;
+}
+
+
+Transfer::~Transfer()
+{
+ sDebugIn << endl;
+
+ synchronousAbort();
+ delete dlgIndividual;
+
+ sDebugOut << endl;
+}
+
+
+void
+Transfer::init(const uint _id)
+{
+ sDebugIn << endl;
+ remainingTimeSec = 0;
+ totalSize = 0;
+ processedSize = 0;
+ percent = 0;
+ id = _id;
+ m_pSlave = new Slave(this, src, dest);
+ canResume = false;
+ startTime = QDateTime::currentDateTime();
+ speed = 0;
+ // retryCount = ksettings.reconnectRetries-1;
+ retryCount = 0;
+ //first off all we need to know if resume is supported...
+
+ status = ST_STOPPED;
+
+
+ connect(this, SIGNAL(statusChanged(Transfer *, int)), kmain, SLOT(slotStatusChanged(Transfer *, int)));
+ connect(this, SIGNAL(statusChanged(Transfer *, int)), this, SLOT(slotUpdateActions()));
+
+ connect(this, SIGNAL(log(uint, const QString &, const QString &)), kmain->logwin(), SLOT(logTransfer(uint, const QString &, const QString &)));
+
+ // setup actions
+ m_paResume = new KAction(i18n("&Resume"), "tool_resume", 0, this, SLOT(slotResume()), this, "resume");
+
+ m_paPause = new KAction(i18n("&Pause"), "tool_pause", 0, this, SLOT(slotRequestPause()), this, "pause");
+
+ m_paDelete = new KAction(i18n("&Delete"), "editdelete", 0, this, SLOT(slotRequestRemove()), this, "delete");
+
+ m_paRestart = new KAction(i18n("Re&start"), "tool_restart", 0, this, SLOT(slotRequestRestart()), this, "restart");
+
+ m_paQueue = new KRadioAction(i18n("&Queue"), "tool_queue", 0, this, SLOT(slotQueue()), this, "queue");
+
+ m_paTimer = new KRadioAction(i18n("&Timer"), "tool_timer", 0, this, SLOT(slotRequestSchedule()), this, "timer");
+
+ m_paDelay = new KRadioAction(i18n("De&lay"), "tool_delay", 0, this, SLOT(slotRequestDelay()), this, "delay");
+
+ m_paQueue->setExclusiveGroup("TransferMode");
+ m_paTimer->setExclusiveGroup("TransferMode");
+ m_paDelay->setExclusiveGroup("TransferMode");
+
+ // Actions
+
+ // m_paDock = new KAction(i18n("&Dock"),"tool_dock", 0, this,SLOT(slotRequestDelay()), this, "dockIndividual");
+
+ // setup individual transfer dialog
+
+ mode = MD_NEW;
+
+ sDebugOut << endl;
+}
+
+
+void Transfer::synchronousAbort()
+{
+ if ( m_pSlave )
+ {
+ if ( m_pSlave->running() )
+ {
+ m_pSlave->Op(Slave::KILL);
+ m_pSlave->wait();
+ }
+
+ if ( m_pSlave->running() )
+ m_pSlave->terminate();
+
+ delete m_pSlave;
+ m_pSlave = 0L;
+
+ status = ST_STOPPED;
+ slotUpdateActions();
+ }
+
+}
+
+void Transfer::slotUpdateActions()
+{
+ sDebugIn << "the item Status is =" << status << "offline=" << ksettings.b_offline << endl;
+ //if we are offline just disable Resume and Pause and return
+ if (ksettings.b_offline) {
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ if(dlgIndividual)
+ dlgIndividual->update();
+ return;
+ }
+
+ UpdateRetry();
+
+ switch (status) {
+
+ case ST_TRYING://fall-through
+ case ST_RUNNING:
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(true);
+ m_paRestart->setEnabled(true);
+ break;
+
+ case ST_STOPPED:
+ m_paResume->setEnabled(true);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ break;
+ case ST_FINISHED:
+ m_paResume->setEnabled(false);
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ break;
+ }
+
+ // disable all signals
+ m_paQueue->blockSignals(true);
+ m_paTimer->blockSignals(true);
+ m_paDelay->blockSignals(true);
+
+ switch (mode) {
+ case MD_QUEUED:
+ m_paQueue->setChecked(true);
+ break;
+ case MD_SCHEDULED:
+ m_paTimer->setChecked(true);
+ break;
+ case MD_DELAYED:
+ m_paDelay->setChecked(true);
+ break;
+ case MD_NEW: //fall through
+ case MD_NONE:
+ m_paQueue->setChecked(false);
+ m_paTimer->setChecked(false);
+ m_paDelay->setChecked(false);
+
+ m_paQueue->setEnabled(false);
+ m_paTimer->setEnabled(false);
+ m_paDelay->setEnabled(false);
+ break;
+
+ }
+
+
+ // enable all signals
+ m_paQueue->blockSignals(false);
+ m_paTimer->blockSignals(false);
+ m_paDelay->blockSignals(false);
+
+ if (dlgIndividual)
+ dlgIndividual->update();
+
+ sDebugOut << endl;
+}
+
+
+
+void Transfer::setSpeed(unsigned long _speed)
+{
+ // sDebugIn <<endl;
+ speed = _speed;
+
+ remainingTimeSec = KIO::calculateRemainingSeconds(totalSize, processedSize, speed);
+ remainingTime = KIO::convertSeconds(remainingTimeSec);
+ //sDebugOut <<endl;
+}
+
+
+
+void Transfer::updateAll()
+{
+ sDebugIn << endl;
+
+ updateStatus(status); // first phase of animation
+
+ logMessage(i18n("Copy file from: %1").arg(src.prettyURL()));
+ logMessage(i18n("To: %1").arg(dest.prettyURL()));
+
+ // source
+ setText(view->lv_url, src.prettyURL());
+
+ // destination
+ setText(view->lv_filename, dest.fileName());
+
+ if(dlgIndividual)
+ {
+ dlgIndividual->setCopying(src, dest);
+ dlgIndividual->setCanResume(canResume);
+ dlgIndividual->setTotalSize(totalSize);
+ dlgIndividual->setPercent(0);
+ dlgIndividual->setProcessedSize(0);
+ }
+
+ if (totalSize != 0) {
+ //logMessage(i18n("Total size is %1 bytes").arg((double)totalSize));
+ setText(view->lv_total, KIO::convertSize(totalSize));
+ } else {
+ //logMessage(i18n("Total size is unknown"));
+ setText(view->lv_total, i18n("unknown"));
+ }
+
+
+ sDebugOut << endl;
+}
+
+
+bool Transfer::updateStatus(int counter)
+{
+ //sDebug<< ">>>>Entering"<<endl;
+
+ QPixmap *pix = 0L;
+ bool isTransfer = false;
+
+ view->setUpdatesEnabled(false);
+
+ switch(status)
+ {
+ case ST_RUNNING:
+ pix = view->animConn.at(counter);
+ isTransfer = true;
+ break;
+ case ST_TRYING:
+ pix = view->animTry.at(counter);
+ isTransfer = true;
+ break;
+ case ST_STOPPED:
+ if(mode == MD_QUEUED)
+ pix = &view->pixQueued;
+ else if(mode == MD_SCHEDULED)
+ pix = &view->pixScheduled;
+ else
+ pix = &view->pixDelayed;
+ break;
+ case ST_FINISHED:
+ pix = &view->pixFinished;
+ }
+
+ setPixmap(view->lv_pixmap, *pix);
+ view->setUpdatesEnabled(true);
+
+ if(prevStatus!=status || prevMode != mode || status==ST_RUNNING || status==ST_TRYING)
+ {
+ QRect rect = view->header()->sectionRect(view->lv_pixmap);
+
+ int x = rect.x();
+ int y = view->itemRect(this).y();
+ int w = rect.width();
+ int h = rect.height();
+
+ view->QScrollView::updateContents(x,y,w,h);
+
+ prevStatus = status;
+ prevMode = mode;
+ }
+
+ return isTransfer;
+}
+
+
+void Transfer::UpdateRetry()
+{
+ QString retry;
+ QString MaxRetry;
+
+ retry.setNum(retryCount);
+ MaxRetry.setNum(ksettings.reconnectRetries);
+ retry += " / " + MaxRetry;
+
+ setText(view->lv_count, retry);
+}
+
+
+void Transfer::slotResume()
+{
+ sDebugIn << " state =" << status << endl;
+
+ retryCount++;
+ if (retryCount > ksettings.reconnectRetries)
+ retryCount = 1;
+ UpdateRetry();
+ assert(status == ST_STOPPED);
+
+ sDebug << "src: " << src.prettyURL() << endl;
+ sDebug << "dest " << dest.prettyURL() << endl;
+
+ m_paResume->setEnabled(false);
+
+ status = ST_TRYING;
+ mode = MD_QUEUED;
+ logMessage(i18n("Attempt number %1").arg(retryCount));
+
+ sDebug << "sending Resume to slave " << endl;
+ m_pSlave->Op(Slave::RETR);
+
+ sDebugOut << endl;
+}
+
+
+ void Transfer::slotStop()
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Stopping"));
+
+ assert(status <= ST_RUNNING && ksettings.b_offline);
+
+ m_pSlave->Op(Slave::KILL); // KILL doesn't post a Message
+ sDebug << "Killing Slave" << endl;
+
+ slotSpeed(0);
+ mode = MD_QUEUED;
+ status=ST_STOPPED;
+ m_paQueue->setChecked(true);
+
+ slotUpdateActions();
+
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotRequestPause()
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Pausing"));
+
+ assert(status <= ST_RUNNING);
+
+ //stopping the thead
+
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+
+
+ m_pSlave->Op(Slave::PAUSE);
+ sDebug << "Requesting Pause.." << endl;
+
+ sDebugOut << endl;
+}
+
+
+
+
+void Transfer::slotRequestRestart()
+{
+ sDebugIn << endl;
+ m_pSlave->Op(Slave::RESTART);
+ slotSpeed(0);
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotRequestRemove()
+{
+ sDebugIn << endl;
+ if (dlgIndividual && !ksettings.b_expertMode)
+ {
+ if (KMessageBox::warningContinueCancel(0, i18n("Are you sure you want to delete this transfer?"),
+ QString::null, KStdGuiItem::del(),
+ QString("delete_transfer"))
+ != KMessageBox::Continue)
+ return;
+ }
+ m_paDelete->setEnabled(false);
+ m_paPause->setEnabled(false);
+ if (dlgIndividual)
+ dlgIndividual->close();
+
+
+ if ( status != ST_FINISHED )
+ {
+ KURL file = dest;
+ // delete the partly downloaded file, if any
+ file.setFileName( dest.fileName() + ".part" ); // ### get it from the job?
+
+ if ( KIO::NetAccess::exists( file, false, view ) ) // don't pollute user with warnings
+ {
+ SafeDelete::deleteFile( file ); // ### messagebox on failure?
+ }
+ }
+ if (status == ST_RUNNING)
+ m_pSlave->Op(Slave::REMOVE);
+ else
+ emit statusChanged(this, OP_REMOVED);
+
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotQueue()
+{
+ sDebug << ">>>>Entering with mode = " << mode << endl;
+
+ logMessage(i18n("Queueing"));
+
+ assert(!(mode == MD_QUEUED));
+
+ mode = MD_QUEUED;
+ m_paQueue->setChecked(true);
+ emit statusChanged(this, OP_QUEUED);
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotRequestSchedule()
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Scheduling"));
+ assert(!(mode == MD_SCHEDULED));
+
+ // if the time was already set somewhere in the future, keep it
+ // otherwise set it to the current time + 60 seconds
+ if (startTime < QDateTime::currentDateTime()) {
+ QDateTime dt = QDateTime::currentDateTime();
+ startTime = dt.addSecs(60);
+ }
+ if (status == ST_RUNNING) {
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ m_pSlave->Op(Slave::SCHEDULE);
+
+ } else
+ slotExecSchedule();
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotRequestDelay()
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Delaying"));
+
+ assert(!(mode == MD_DELAYED));
+ if (status == ST_RUNNING) {
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(false);
+ m_pSlave->Op(Slave::DELAY);
+ } else
+ slotExecDelay();
+ sDebugOut << endl;
+}
+
+
+
+/*
+void Transfer::slotCanceled(KIO::Job *)
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Canceled by user"));
+ emit statusChanged(this, OP_CANCELED);
+ sDebugOut << endl;
+}
+
+*/
+
+void Transfer::slotFinished()
+{
+ sDebugIn << endl;
+
+ logMessage(i18n("Download finished"));
+ mode = MD_NONE;
+ status = ST_FINISHED;
+ slotProcessedSize(totalSize);
+
+ slotSpeed(0);
+ if(dlgIndividual)
+ dlgIndividual->enableOpenFile();
+ emit statusChanged(this, OP_FINISHED);
+ sDebugOut << endl;
+}
+
+
+
+
+/*
+void Transfer::slotRenaming(KIO::Job *, const KURL &, const KURL & to)
+{
+ sDebugIn << endl;
+
+ dest = to;
+
+ logMessage(i18n("Renaming to %1").arg(dest.prettyURL().ascii()));
+
+ // destination
+ setText (view->lv_filename, dest.fileName ());
+
+ dlgIndividual->setCopying (src, dest);
+
+ sDebugOut << endl;
+}
+ */
+
+
+
+
+void Transfer::slotSpeed(unsigned long bytes_per_second)
+{
+ //sDebugIn <<endl;
+
+ setSpeed(bytes_per_second);
+
+ if (speed == 0 && status == ST_RUNNING) {
+ setText(view->lv_speed, i18n("Stalled"));
+ setText(view->lv_remaining, i18n("Stalled"));
+ if(dlgIndividual)
+ dlgIndividual->setSpeed(i18n("Stalled"));
+ } else if (speed == 0 && status == ST_FINISHED) {
+
+ setText(view->lv_progress, i18n("OK as in 'finished'","OK"));
+ setText(view->lv_speed, i18n("Finished"));
+ setText(view->lv_remaining, i18n("Finished"));
+ if(dlgIndividual)
+ dlgIndividual->setSpeed(i18n("Finished"));
+
+ } else if (speed == 0 && status == ST_STOPPED) {
+
+
+ setText(view->lv_speed, i18n("Stopped"));
+ setText(view->lv_remaining, i18n("Stopped"));
+ if(dlgIndividual)
+ dlgIndividual->setSpeed(i18n("Stopped"));
+
+ } else {
+ QString tmps = i18n("%1/s").arg(KIO::convertSize(speed));
+ setText(view->lv_speed, tmps);
+ setText(view->lv_remaining, remainingTime);
+ if(dlgIndividual)
+ dlgIndividual->setSpeed(tmps + " ( " + remainingTime + " ) ");
+ }
+
+ //sDebugOut<<endl;
+}
+
+
+
+void Transfer::slotTotalSize(KIO::filesize_t bytes)
+{
+#ifdef _DEBUG
+ sDebugIn<<" totalSize is = "<<totalSize << endl;
+#endif
+
+ if (totalSize == 0) {
+ totalSize = bytes;
+ if (totalSize != 0) {
+ logMessage(i18n("Total size is %1 bytes").arg((double)totalSize,0,'f',0));
+ setText(view->lv_total, KIO::convertSize(totalSize));
+ if(dlgIndividual)
+ {
+ dlgIndividual->setTotalSize(totalSize);
+ dlgIndividual->setPercent(0);
+ dlgIndividual->setProcessedSize(0);
+ }
+ }
+ } else {
+
+#ifdef _DEBUG
+ sDebug<<"totalSize="<<totalSize<<" bytes="<<bytes<<endl;
+ assert(totalSize == bytes);
+#endif
+ if (totalSize != bytes)
+ logMessage(i18n("The file size does not match."));
+ else
+ logMessage(i18n("File Size checked"));
+ }
+
+#ifdef _DEBUG
+ sDebugOut << endl;
+#endif
+}
+
+
+
+void Transfer::slotProcessedSize(KIO::filesize_t bytes)
+{
+ //sDebug<< ">>>>Entering"<<endl;
+
+ int old = percent;
+ processedSize = bytes;
+
+ if (totalSize == 0)
+ {
+ percent = 0;
+ }
+ else if ( totalSize < processedSize ) // bogus totalSize value
+ {
+ percent = 99; // what can we say?
+ totalSize = processedSize;
+
+ setText(view->lv_total, KIO::convertSize(totalSize));
+ if(dlgIndividual)
+ dlgIndividual->setTotalSize(totalSize);
+ }
+ else {
+ percent = (int) (((float) processedSize / (float) totalSize) * 100.0);
+ }
+ if(dlgIndividual)
+ dlgIndividual->setProcessedSize(processedSize);
+
+ if (percent != old) {
+ QString tmps;
+ if (percent == 100) {
+ tmps = i18n("OK as in 'finished'","OK");
+ } else {
+ tmps.setNum(percent);
+ }
+
+ setText(view->lv_progress, tmps);
+
+ if(dlgIndividual)
+ dlgIndividual->setPercent(percent);
+ }
+ //sDebug<< "<<<<Leaving"<<endl;
+}
+
+
+
+
+void Transfer::showIndividual()
+{
+ sDebugIn << endl;
+
+ // create a DlgIndividual only if it hasn't been created yet
+ if(!dlgIndividual)
+ {
+ dlgIndividual = new DlgIndividual(this);
+ dlgIndividual->setLog(transferLog);
+ dlgIndividual->setCopying(src, dest);
+ dlgIndividual->setCanResume(canResume);
+ dlgIndividual->setTotalSize(totalSize);
+ dlgIndividual->setPercent(percent);
+ dlgIndividual->setProcessedSize(processedSize);
+ }
+
+ dlgIndividual->raise();
+
+
+ if (ksettings.b_iconifyIndividual) {
+ KWin::iconifyWindow( dlgIndividual->winId() );
+ }
+
+ // update the actions
+ slotUpdateActions();
+ // then show the single dialog
+ KWin::deIconifyWindow( dlgIndividual->winId() );
+ dlgIndividual->show();
+
+ sDebugOut << endl;
+}
+
+
+void Transfer::logMessage(const QString & message)
+{
+ sDebugIn << message << endl;
+
+ emit log(id, src.fileName(), message);
+
+ QString tmps = "<font color=\"blue\">" + QTime::currentTime().toString() + "</font> : " + message;
+
+ transferLog.append(tmps + '\n');
+
+ if(dlgIndividual)
+ dlgIndividual->appendLog(tmps);
+
+ sDebugOut << endl;
+}
+
+
+
+bool Transfer::read(KSimpleConfig * config, int id)
+{
+ sDebugIn << endl;
+
+
+ QString str;
+ str.sprintf("Item%d", id);
+ config->setGroup(str);
+
+ if (src.isEmpty() || dest.isEmpty()) {
+ return false;
+ }
+
+ if (!src.isValid() && !ksettings.b_expertMode) {
+ KMessageBox::error(kmain, i18n("Malformed URL:\n") + src.prettyURL(), i18n("Error"));
+ return false;
+ }
+
+ mode = (TransferMode) config->readNumEntry("Mode", MD_QUEUED);
+ status = (TransferStatus) config->readNumEntry("Status", ST_RUNNING);
+ startTime = config->readDateTimeEntry("ScheduledTime");
+ canResume = config->readBoolEntry("CanResume", true);
+ totalSize = config->readUnsignedNum64Entry("TotalSize", 0);
+ processedSize = config->readUnsignedNum64Entry("ProcessedSize", 0);
+
+ if (status != ST_FINISHED && totalSize != 0) {
+ //TODO insert additional check
+ status = ST_STOPPED;
+ }
+
+ updateAll();
+ sDebugOut << endl;
+ return true;
+}
+
+
+void Transfer::write(KSimpleConfig * config, int id)
+{
+ sDebugIn << endl;
+
+ QString str;
+ str.sprintf("Item%d", id);
+
+ config->setGroup(str);
+ config->writePathEntry("Source", src.url());
+ config->writePathEntry("Dest", dest.url());
+ config->writeEntry("Mode", mode);
+ config->writeEntry("Status", status);
+ config->writeEntry("CanResume", canResume);
+ config->writeEntry("TotalSize", totalSize );
+ config->writeEntry("ProcessedSize", processedSize );
+ config->writeEntry("ScheduledTime", startTime);
+ sDebugOut << endl;
+}
+
+
+
+/** No descriptions */
+void Transfer::slotExecPause()
+{
+ sDebugIn << endl;
+ slotSpeed(0);
+
+ mode = MD_DELAYED;
+ m_paDelay->setChecked(true);
+ status = ST_STOPPED;
+
+ m_paPause->setEnabled(false);
+ m_paRestart->setEnabled(true);
+ m_paResume->setEnabled(true);
+ slotUpdateActions();
+ //TODO WE NEED TO UPDATE ACTIONS..
+ kmain->slotUpdateActions();
+ emit statusChanged(this, OP_PAUSED);
+ sDebugOut << endl;
+}
+
+void Transfer::slotExecError()
+{
+ sDebugIn << endl;
+
+ status = ST_STOPPED;
+ mode = MD_SCHEDULED;
+ startTime=QDateTime::currentDateTime().addSecs(ksettings.reconnectTime * 60);
+ emit statusChanged(this, OP_SCHEDULED);
+
+ sDebugOut << endl;
+}
+
+void Transfer::slotExecBroken()
+{
+ sDebugIn << endl;
+
+ status = ST_STOPPED;
+ mode = MD_QUEUED;
+ emit statusChanged(this, OP_QUEUED);
+
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotExecRemove()
+{
+ sDebugIn << endl;
+
+ m_pSlave->wait();
+ emit statusChanged(this, OP_REMOVED);
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotExecResume()
+{
+ sDebugIn << endl;
+ emit statusChanged(this, OP_RESUMED);
+ sDebugOut << endl;
+}
+
+void Transfer::slotExecConnected()
+{
+ sDebugIn << endl;
+ if (mode == MD_NEW)
+ {
+ if (ksettings.b_offline)// when we're offline and arrive here, then the file is in cache
+ return; // Slave::slotResult will be called immediately, so we do nothing here
+ status = ST_STOPPED;
+ m_pSlave->Op(Slave::KILL);
+ if (ksettings.b_addQueued)
+ {
+ mode = MD_QUEUED;
+ emit statusChanged(this, OP_QUEUED);
+ }
+ else
+ {
+ mode = MD_DELAYED;
+ emit statusChanged(this, OP_DELAYED);
+ }
+ return;
+ }
+
+ status = ST_RUNNING;
+ emit statusChanged(this, OP_CONNECTED);
+ sDebugOut << endl;
+}
+
+
+void Transfer::slotCanResume(bool _bCanResume)
+{
+ sDebugIn << endl;
+
+ canResume = _bCanResume;
+
+ if (canResume) {
+ logMessage(i18n("Download resumed"));
+ setText(view->lv_resume, i18n("Yes"));
+ } else {
+ setText(view->lv_resume, i18n("No"));
+ }
+
+ //dlgIndividual->setCanResume(canResume);
+
+ sDebugOut << endl;
+}
+
+
+/** No descriptions */
+void Transfer::slotExecDelay()
+{
+ sDebugIn << endl;
+
+ mode = MD_DELAYED;
+ status = ST_STOPPED;
+ slotSpeed(0); //need???????
+ m_paDelay->setChecked(true);
+ emit statusChanged(this, OP_DELAYED);
+
+ sDebugOut << endl;
+}
+
+/** No descriptions */
+void Transfer::slotExecSchedule()
+{
+ sDebugIn << endl;
+
+ mode = MD_SCHEDULED;
+ status = ST_STOPPED;
+ m_paTimer->setChecked(true);
+ emit statusChanged(this, OP_SCHEDULED);
+
+ sDebugOut << endl;
+}
+
+/** No descriptions */
+void Transfer::slotStartTime(const QDateTime & _startTime)
+{
+ sDebugIn << endl;
+
+ setStartTime(_startTime);
+ sDebugOut << endl;
+}
+
+/** return true if the dlgIndividual is Visible */
+bool Transfer::isVisible() const
+{
+ return dlgIndividual ? dlgIndividual->isVisible() : false;
+}
+
+bool Transfer::keepDialogOpen() const
+{
+ return dlgIndividual ? dlgIndividual->keepDialogOpen() : false;
+}
+
+void Transfer::maybeShow()
+{
+ if ( ksettings.b_showIndividual && getStatus() != Transfer::ST_FINISHED )
+ {
+ if(dlgIndividual)
+ dlgIndividual->show();
+ }
+}
+
+bool Transfer::retryOnError()
+{
+ return (ksettings.b_reconnectOnError && (retryCount < ksettings.reconnectRetries));
+}
+
+bool Transfer::retryOnBroken()
+{
+ return (ksettings.b_reconnectOnBroken && (retryCount < ksettings.reconnectRetries));
+}
+
+void Transfer::checkCache()
+{
+ assert (mode == MD_NEW);
+
+ if (src.protocol()=="http")
+ {
+ status = ST_TRYING;
+ m_pSlave->Op(Slave::RETR_CACHE);
+ }
+ else
+ NotInCache();
+}
+
+void Transfer::NotInCache()
+{
+ logMessage(i18n("checking if file is in cache...no"));
+ if (ksettings.b_addQueued)
+ mode = MD_QUEUED;
+ else
+ mode = MD_DELAYED;
+ status = ST_STOPPED;
+}
+#include "transfer.moc"
+
diff --git a/kget/transfer.h b/kget/transfer.h
new file mode 100644
index 00000000..845995d8
--- /dev/null
+++ b/kget/transfer.h
@@ -0,0 +1,241 @@
+/***************************************************************************
+* transfer.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+
+#ifndef _Transfer_h
+#define _Transfer_h
+
+#include <klistview.h>
+#include <qdatetime.h>
+#include <qguardedptr.h>
+#include <qmap.h>
+
+#include <kurl.h>
+#include <kio/jobclasses.h>
+
+#include "slave.h"
+
+class QTimer;
+
+class KSimpleConfig;
+class KAction;
+class KRadioAction;
+
+
+
+class DlgIndividual;
+class TransferList;
+
+
+class Transfer:public QObject, public KListViewItem
+{
+ Q_OBJECT
+public:
+ enum TransferMode { MD_QUEUED, MD_DELAYED, MD_SCHEDULED, MD_NONE, MD_NEW };
+
+ enum TransferStatus { ST_TRYING, ST_RUNNING, ST_STOPPED, ST_FINISHED };
+
+ enum TransferOperation {
+ OP_FINISHED, OP_RESUMED, OP_PAUSED, OP_REMOVED, OP_ABORTED,
+ OP_QUEUED, OP_SCHEDULED, OP_DELAYED, OP_CONNECTED
+ };
+
+
+
+ Transfer(TransferList * view, const KURL & _src, const KURL & _dest, const uint _id=0);
+ Transfer(TransferList * view, Transfer * after, const KURL & _src, const KURL & _dest, const uint _id=0);
+ ~Transfer();
+
+ void synchronousAbort();
+
+ bool read(KSimpleConfig * config, int id);
+ void write(KSimpleConfig * config, int id);
+ void logMessage(const QString & message);
+
+ bool keepDialogOpen() const;
+
+
+ QDateTime getStartTime()const
+ {
+ return startTime;
+ }
+ unsigned int getRemainingTime()const
+ {
+ return remainingTimeSec;
+ }
+
+ KIO::filesize_t getTotalSize()const
+ {
+ return totalSize;
+ }
+ KIO::filesize_t getProcessedSize()const
+ {
+ return processedSize;
+ }
+
+ KURL getSrc()const
+ {
+ return src;
+ }
+ KURL getDest()const
+ {
+ return dest;
+ }
+ int getPercent()const
+ {
+ return percent;
+ }
+
+ int getSpeed()const
+ {
+ return speed;
+ }
+ TransferStatus getStatus()const
+ {
+ return status;
+ }
+ int getMode()const
+ {
+ return mode;
+ }
+
+ void setMode(TransferMode _mode)
+ {
+ mode = _mode;
+ }
+ void setStatus(TransferStatus _status)
+ {
+ status = _status;
+ };
+ void setStartTime(QDateTime _startTime)
+ {
+ startTime = _startTime;
+ };
+ void setSpeed(unsigned long _speed);
+
+ // update methods
+ void updateAll();
+ bool updateStatus(int counter);
+
+ void showIndividual();
+ void UpdateRetry();
+
+
+ // actions
+ KAction *m_paResume, *m_paPause, *m_paDelete, *m_paRestart;
+ //KAction *m_paDock;
+ KRadioAction *m_paQueue, *m_paTimer, *m_paDelay;
+
+public:
+ void slotExecPause();
+ void slotExecResume();
+ void slotExecRemove();
+ void slotExecDelay();
+ void slotExecSchedule();
+ void slotExecConnected();
+ void slotExecError();
+ void slotExecBroken();
+ void slotCanResume(bool _bCanResume);
+ void slotSpeed(unsigned long);
+ void checkCache();
+
+ bool isVisible() const;
+ void maybeShow();
+
+ bool retryOnError();
+ bool retryOnBroken();
+
+public slots:
+ // operation methods
+ void slotResume();
+ void slotRequestPause();
+ void slotRequestRemove();
+ void slotRequestSchedule();
+ void slotRequestDelay();
+ void NotInCache();
+ void slotRequestRestart();
+ void slotUpdateActions();
+
+ void slotQueue();
+ void slotFinished();
+
+ void slotTotalSize(KIO::filesize_t bytes);
+ void slotProcessedSize(KIO::filesize_t);
+
+ void slotStartTime(const QDateTime &);
+ void slotStop(); // stop all transfers when going offline
+
+signals:
+ void statusChanged(Transfer *, int _operation);
+ void log(uint, const QString &, const QString &);
+
+private:
+ void init(const uint _id);
+
+ Slave *m_pSlave;
+
+ KURL src;
+ KURL dest;
+
+ /* the tranfer id number */
+ uint id;
+
+ static uint idcount;
+
+ // log
+ QString transferLog;
+
+ // schedule time
+ QDateTime startTime;
+
+ KIO::filesize_t totalSize;
+ KIO::filesize_t processedSize;
+ int percent;
+
+ int speed;
+ unsigned int remainingTimeSec;
+ QString remainingTime;
+
+ TransferStatus status;
+ TransferMode mode;
+
+ TransferStatus prevStatus;
+ TransferMode prevMode;
+
+ // how many times have we retried already
+ unsigned int retryCount;
+
+ bool canResume;
+
+ TransferList *view;
+
+ // individual download window
+ QGuardedPtr<DlgIndividual> dlgIndividual;
+
+};
+
+
+#endif // _Transfer_h
diff --git a/kget/transferlist.cpp b/kget/transferlist.cpp
new file mode 100644
index 00000000..1d9ed902
--- /dev/null
+++ b/kget/transferlist.cpp
@@ -0,0 +1,283 @@
+/***************************************************************************
+* transferlist.cpp
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <ksimpleconfig.h>
+#include <kio/netaccess.h>
+
+#include "transfer.h"
+#include "transferlist.h"
+
+#define NUM_COLS 9
+
+static int defaultColumnWidth[] = {
+ 26, // PIXMAP
+ 160, // LOCAL FILENAME
+ 40, // RESUME
+ 60, // COUNT
+ 30, // PROGRESS
+ 65, // TOTAL
+ 70, // SPEED
+ 70, // REMAINING TIME
+ 450 // URL
+ };
+
+
+TransferList::TransferList(QWidget * parent, const char *name)
+ : KListView(parent, name)
+{
+ // enable selection of more than one item
+ setSelectionMode( QListView::Extended );
+
+ // // disable sorting and clicking on headers
+ // setSorting( -1 );
+
+ setAllColumnsShowFocus(true);
+
+ lv_pixmap = addColumn(i18n("S"));
+ lv_filename = addColumn(i18n("Local File Name"));
+ lv_resume = addColumn(i18n("Resumed"));
+ lv_count = addColumn(i18n("Count"));
+ lv_progress = addColumn(i18n("%"));
+ lv_total = addColumn(i18n("Total"));
+ lv_speed = addColumn(i18n("Speed"));
+ lv_remaining = addColumn(i18n("Rem. Time"));
+ lv_url = addColumn(i18n("Address (URL)"));
+
+ jobid=1;
+
+ // initial layout
+ KConfig *config = KGlobal::config();
+ config->setGroup("ListView");
+ if ( config->readListEntry("ColumnWidths").isEmpty() )
+ {
+ for (int i = 0; i < NUM_COLS; i++)
+ setColumnWidth(i, defaultColumnWidth[i]);
+ }
+ else
+ restoreLayout( KGlobal::config(), "ListView" );
+
+ QString connectPath = "pics/connect%2.png";
+ QString tryPath = "pics/try%2.png";
+
+ // Load animations
+ QPixmap* curPix;
+ if (animConn.count() == 0) {
+ animConn.setAutoDelete(true);
+ animTry.setAutoDelete(true);
+ for (int i = 0; i < 8; i++) {
+ curPix = new QPixmap();
+ curPix->load(locate("appdata", connectPath.arg(i)));
+ animConn.append(curPix);
+
+ curPix = new QPixmap();
+ curPix->load(locate("appdata", tryPath.arg(i)));
+ animTry.append(curPix);
+ }
+ }
+
+ pixQueued = UserIcon("md_queued");
+ pixScheduled = UserIcon("md_scheduled");
+ pixDelayed = UserIcon("md_delayed.png");
+ pixFinished = UserIcon("md_finished");
+ pixRetrying = UserIcon("retrying");
+
+ phasesNum = animConn.count();
+
+ connect(this, SIGNAL(doubleClicked(QListViewItem *)), SLOT(slotTransferSelected(QListViewItem *)));
+ connect(this, SIGNAL(rightButtonPressed(QListViewItem *, const QPoint &, int)), SLOT(slotPopupMenu(QListViewItem *)));
+}
+
+
+TransferList::~TransferList()
+{
+ saveLayout( KGlobal::config(), "ListView" );
+}
+
+
+Transfer *TransferList::addTransfer(const KURL & _source, const KURL & _dest,
+ bool canShow)
+{
+ Transfer *last = static_cast<Transfer*>( lastItem() );
+ Transfer *new_item = new Transfer(this, last, _source, _dest, jobid);
+ jobid++;
+ if ( canShow )
+ new_item->maybeShow();
+
+ return new_item;
+}
+
+
+void TransferList::slotTransferSelected(QListViewItem * item)
+{
+ emit transferSelected((Transfer *) item);
+}
+
+
+void TransferList::slotPopupMenu(QListViewItem * item)
+{
+ if (!item)
+ return;
+ emit popupMenu((Transfer *) item);
+}
+
+
+void TransferList::setSelected(QListViewItem * item, bool selected)
+{
+ bool tmpb = selected;
+
+ if (tmpb && item->isSelected()) {
+ tmpb = false;
+ }
+
+ QListView::setSelected(item, tmpb);
+}
+
+
+void TransferList::moveToBegin(Transfer * item)
+{
+ // ASSERT(item);
+
+ Transfer *oldfirst=static_cast<Transfer*>(firstChild());
+ item->moveItem(oldfirst); //move item after oldfirst
+ oldfirst->moveItem(item); //move oldfirst after item
+}
+
+
+void TransferList::moveToEnd(Transfer * item)
+{
+ // ASSERT(item);
+
+ Transfer *oldlast=static_cast<Transfer*>(lastItem());
+ item->moveItem(oldlast);
+}
+
+
+bool TransferList::updateStatus(int counter)
+{
+ bool isTransfer = false;
+
+ TransferIterator it(this);
+
+ for (; it.current(); ++it) {
+ isTransfer |= it.current()->updateStatus(counter);
+ }
+
+ return isTransfer;
+}
+
+
+bool TransferList::areTransfersQueuedOrScheduled()
+{
+ TransferIterator it(this);
+
+ if (childCount() > 0) {
+ for (; it.current(); ++it) {
+ if ((it.current()->getMode() == Transfer::MD_QUEUED)|| \
+ (it.current()->getMode() == Transfer::MD_SCHEDULED))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Transfer * TransferList::find(const KURL& _src)
+{
+ TransferIterator it(this);
+
+ for (; it.current(); ++it) {
+ if (it.current()->getSrc() == _src) {
+ return it.current();
+ }
+ }
+
+ return 0L;
+}
+
+
+void TransferList::readTransfers(const KURL& file)
+{
+ QString tmpFile;
+
+ if (KIO::NetAccess::download(file, tmpFile, (QWidget*)parent())) {
+ KSimpleConfig config(tmpFile);
+
+ config.setGroup("Common");
+ int num = config.readNumEntry("Count", 0);
+
+ Transfer *item;
+ KURL src, dest;
+
+ for ( int i = 0; i < num; i++ )
+ {
+ QString str;
+
+ str.sprintf("Item%d", i);
+ config.setGroup(str);
+
+ src = KURL::fromPathOrURL( config.readPathEntry("Source") );
+ dest = KURL::fromPathOrURL( config.readPathEntry("Dest") );
+ item = addTransfer( src, dest, false ); // don't show!
+
+ if (!item->read(&config, i))
+ delete item;
+ else
+ {
+ // configuration read, now we know the status to determine
+ // whether to show or not
+ item->maybeShow();
+ }
+ }
+ }
+}
+
+void TransferList::writeTransfers(const QString& file)
+{
+ sDebug << ">>>>Entering with file =" << file << endl;
+
+ KSimpleConfig config(file);
+ int num = childCount();
+
+ config.setGroup("Common");
+ config.writeEntry("Count", num);
+
+ TransferIterator it(this);
+
+ for (int id = 0; it.current(); ++it, ++id)
+ it.current()->write(&config, id);
+ config.sync();
+
+ sDebug << "<<<<Leaving" << endl;
+}
+
+#include "transferlist.moc"
diff --git a/kget/transferlist.h b/kget/transferlist.h
new file mode 100644
index 00000000..0d5929d0
--- /dev/null
+++ b/kget/transferlist.h
@@ -0,0 +1,116 @@
+/***************************************************************************
+* transferlist.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#ifndef _TransferList_h
+#define _TransferList_h
+
+#include <klistview.h>
+#include <qptrlist.h>
+
+#include <kurl.h>
+
+class Transfer;
+
+class TransferIterator:public QListViewItemIterator
+{
+
+public:
+
+ TransferIterator(QListView * view):QListViewItemIterator(view)
+ {
+ }
+ Transfer *current() const
+ {
+ return (Transfer *) QListViewItemIterator::current();
+ }
+ void reset()
+ {
+ curr = listView->firstChild();
+ }
+
+};
+
+
+class TransferList:public KListView
+{
+Q_OBJECT public:
+
+
+ TransferList(QWidget * parent = 0, const char *name = 0);
+ virtual ~ TransferList();
+
+ Transfer *addTransfer(const KURL & _source, const KURL & _dest,
+ bool canShow = true );
+
+ virtual void setSelected(QListViewItem * item, bool selected);
+
+ void moveToBegin(Transfer * item);
+ void moveToEnd(Transfer * item);
+
+ uint getPhasesNum()const
+ {
+ return phasesNum;
+ }
+ bool updateStatus(int counter);
+ Transfer * find(const KURL& _src);
+ bool areTransfersQueuedOrScheduled();
+
+ void readTransfers(const KURL& file);
+ void writeTransfers(const QString& file);
+
+ friend class Transfer;
+
+signals:
+ void transferSelected(Transfer * item);
+ void popupMenu(Transfer * item);
+
+protected slots:
+ void slotTransferSelected(QListViewItem * item);
+ void slotPopupMenu(QListViewItem * item);
+
+protected:
+
+ void readConfig();
+ void writeConfig();
+
+ // ListView IDs
+ int lv_pixmap, lv_filename, lv_resume, lv_count, lv_progress;
+ int lv_total, lv_speed, lv_remaining, lv_url;
+
+ QPtrList < QPixmap > animConn;
+ QPtrList < QPixmap > animTry;
+ QPixmap pixQueued;
+ QPixmap pixScheduled;
+ QPixmap pixDelayed;
+ QPixmap pixFinished;
+ QPixmap pixRetrying;
+
+ uint phasesNum;
+ uint jobid;
+};
+
+
+#endif // _TransferList_h
diff --git a/kget/version.h b/kget/version.h
new file mode 100644
index 00000000..1374e278
--- /dev/null
+++ b/kget/version.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+* version.h
+* -------------------
+*
+* Revision : $Id$
+* begin : Tue Jan 29 2002
+* copyright : (C) 2002 by Patrick Charbonnier
+* : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
+* email : pch@freeshell.org
+*
+****************************************************************************/
+
+/***************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ ***************************************************************************/
+
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+
+#define KGETVERSION "v0.8.5"
+#define KGET_APP_NAME "kget"
+
+
+#endif
diff --git a/kget/x-kgetlist.desktop b/kget/x-kgetlist.desktop
new file mode 100644
index 00000000..e6a79308
--- /dev/null
+++ b/kget/x-kgetlist.desktop
@@ -0,0 +1,74 @@
+[Desktop Entry]
+X-KDE-AutoEmbed=false
+Comment=Kget Download List
+Comment[ar]=قائمة تنزيل ك.جيت
+Comment[be]=Ð¡Ð¿Ñ–Ñ ÑцÑÐ³Ð²Ð°Ð½Ð½Ñ Kget
+Comment[bg]=СпиÑък за изтеглÑне на Kget
+Comment[bn]=কে-গেট ডাউনলোড তালিকা
+Comment[br]=Roll enkargañ Kget
+Comment[bs]=KGet download lista
+Comment[ca]=Llista de descàrrega del Kget
+Comment[cs]=Seznam stahování KGet
+Comment[cy]=Rhestr Lawrlwytho KNôl
+Comment[da]=KGet-download-liste
+Comment[de]=Kget Dateiliste zum Herunterladen
+Comment[el]=Λίστα λήψεων αÏχείων του Kget
+Comment[eo]=Kget DeÅutolisto
+Comment[es]=Lista de descarga de KGet
+Comment[et]=KGeti allalaadimise nimekiri
+Comment[eu]=Kget deskargatzeko zerrenda
+Comment[fa]=Ùهرست بارگیری Kget
+Comment[fi]=Kget-hakulista
+Comment[fr]=Liste de téléchargement de KGet
+Comment[ga]=Liosta Thíosluchtaithe Kget
+Comment[gl]=Lista de Descargas de KGet
+Comment[he]=רשימת הורדה של Kget
+Comment[hi]=के-गेट डाउनलोड सूची
+Comment[hr]=Kget lista za preuzimanje
+Comment[hu]=KGet letöltési lista
+Comment[is]=Kget niðurhalslisti
+Comment[it]=Lista scaricamenti di KGet
+Comment[ja]=Kget ダウンロードリスト
+Comment[ka]=Kget ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ•áƒáƒ—რსიáƒ
+Comment[kk]=Kget жүктеу тізімі
+Comment[km]=បញ្ជី​ការ​ទាញយក​របស់ Kget
+Comment[lt]=Kget atsisiuntimų sąrašas
+Comment[mk]=ЛиÑта на Ñимнувања во Kget
+Comment[mn]=KGet жагÑаалтыг Татаж авах
+Comment[ms]=Senarai Muat turun Kget
+Comment[mt]=Lista ta' downloads KGet
+Comment[nb]=Kget nedlastingsliste
+Comment[nds]=KGet-Daalladenlist
+Comment[ne]=केडीई गेट डाउनलोड सूची
+Comment[nl]=Kget-downloadlijst
+Comment[nn]=Nedlastingsliste for Kget
+Comment[nso]=Palo ya Ngwalollo ya Kget
+Comment[pa]=Kget ਡਾਊਨਲੋਡ ਸੂਚੀ
+Comment[pl]=Lista plików do pobrania przez KGet
+Comment[pt]=Lista de Transferências do KGet
+Comment[pt_BR]=Lista de Download do Kget
+Comment[ro]=Listă de transfer KGet
+Comment[ru]=СпиÑок заданий KGet
+Comment[se]=Kget:a viežžanlisttu
+Comment[sk]=Kget zoznam sťahovania
+Comment[sl]=Seznam datotek za prenos s KGet
+Comment[sr]=Kget-ова лиÑта преузимања
+Comment[sr@Latn]=Kget-ova lista preuzimanja
+Comment[sv]=Nerladdningslista för Kget
+Comment[ta]=கேகெட௠பதிவிறகà¯à®• படà¯à®Ÿà®¿à®¯à®²à¯
+Comment[tg]=Рӯйхати Боркунии Kget
+Comment[th]=รายà¸à¸²à¸£à¸”าวน์โหลด K
+Comment[tr]=KGet Ä°ndirme Listesi
+Comment[uk]=СпиÑок Ð·Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ kget
+Comment[ven]=Mutevhe wau hwesulula wa Kget
+Comment[wa]=Djivêye des aberwetaedjes di Kget
+Comment[xh]=Uluhlu lwe Kget loku Layishela ezantsi
+Comment[zh_CN]=Kget 下载清å•
+Comment[zh_HK]=Kget 下載清單
+Comment[zh_TW]=Kget 下載清單
+Comment[zu]=I-Kget ye-listi Yokugcwalisela phansi
+Icon=kget_list
+Type=MimeType
+MimeType=application/x-kgetlist
+Patterns=*.kgt;
+DefaultApp=KGet
diff --git a/knewsticker/AUTHORS b/knewsticker/AUTHORS
new file mode 100644
index 00000000..5c626223
--- /dev/null
+++ b/knewsticker/AUTHORS
@@ -0,0 +1,2 @@
+Frerich Raabe <raabe@kde.org>
+Malte Starostik <malte@kde.org>
diff --git a/knewsticker/COPYING b/knewsticker/COPYING
new file mode 100644
index 00000000..cca2a5c9
--- /dev/null
+++ b/knewsticker/COPYING
@@ -0,0 +1,20 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/knewsticker/Makefile.am b/knewsticker/Makefile.am
new file mode 100644
index 00000000..eea97be4
--- /dev/null
+++ b/knewsticker/Makefile.am
@@ -0,0 +1,34 @@
+SUBDIRS = common kntsrcfilepropsdlg knewstickerstub
+INCLUDES = -I$(top_srcdir)/knewsticker/common $(all_includes)
+
+kde_module_LTLIBRARIES = knewsticker_panelapplet.la
+
+noinst_HEADERS = newsscroller.h knewsticker.h
+
+METASOURCES = AUTO
+
+knewsticker_panelapplet_la_SOURCES = newsscroller.cpp \
+ knewsticker.cpp knewsticker.skel \
+ knewstickerconfig.cpp knewstickerconfigwidget.ui \
+ newssourcedlgimpl.cpp newssourcedlg.ui
+knewsticker_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+knewsticker_panelapplet_la_LIBADD = common/libknewstickercommon.la \
+ $(LIB_KIO) $(LIB_KDEUI)
+
+appdatadir = $(kde_datadir)/knewsticker
+appdata_DATA = eventsrc
+
+updatedir = $(kde_datadir)/kconf_update
+update_DATA = knewsticker.upd
+update_SCRIPTS = knt-0.1-0.2.pl
+
+lnkdir = $(kde_datadir)/kicker/applets
+lnk_DATA = knewsticker.desktop
+
+xdg_apps_DATA = knewsticker-standalone.desktop
+
+KDE_ICON = AUTO
+
+messages: rc.cpp
+ $(EXTRACTRC) *.ui */*.ui >> rc.cpp
+ $(XGETTEXT) *.cpp *.h */*.cpp */*.h -o $(podir)/knewsticker.pot
diff --git a/knewsticker/TODO b/knewsticker/TODO
new file mode 100644
index 00000000..6d586831
--- /dev/null
+++ b/knewsticker/TODO
@@ -0,0 +1,10 @@
+TODO
+----
+* Make KNewsTicker choose one of the rotated modes as soon as Kicker is moved
+ to one of the sides.
+* Fix the highlighting of the headlines, right now the headlines stay
+ highlighted sometimes even if the mouse is moved away (i.e. with the context
+ menu)
+* Make the "Suggest" function in the news source dialog work for program news
+ sources.
+* Make it possible to pass parameters to external program newssources.
diff --git a/knewsticker/common/Makefile.am b/knewsticker/common/Makefile.am
new file mode 100644
index 00000000..60fddef7
--- /dev/null
+++ b/knewsticker/common/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libknewstickercommon.la
+
+noinst_HEADERS = configiface.h configaccess.h newsiconmgr.h xmlnewsaccess.h \
+ newsengine.h
+
+METASOURCES = AUTO
+
+libknewstickercommon_la_SOURCES = newsiconmgr.cpp xmlnewsaccess.cpp \
+ configaccess.cpp newsiconmgr.skel newsengine.cpp
+libknewstickercommon_la_LIBADD = $(LIB_KIO) $(LIB_KDECORE)
+libknewstickercommon_la_LDFLAGS = $(all_libraries) -no-undefined
diff --git a/knewsticker/common/configaccess.cpp b/knewsticker/common/configaccess.cpp
new file mode 100644
index 00000000..942b3f70
--- /dev/null
+++ b/knewsticker/common/configaccess.cpp
@@ -0,0 +1,674 @@
+/*
+ * configaccess.cpp
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "configaccess.h"
+#include "newsengine.h"
+
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+
+static NewsSourceBase::Data NewsSourceDefault[DEFAULT_NEWSSOURCES] = {
+ // Arts ---------------
+ NewsSourceBase::Data(
+ QString::fromLatin1("Bureau 42"),
+ QString::fromLatin1("http://www.bureau42.com/rdf/"),
+ QString::fromLatin1("http://www.bureau42.com/favicon.ico"),
+ NewsSourceBase::Arts, 5, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("eFilmCritic"),
+ QString::fromLatin1("http://efilmcritic.com/fo.rdf"),
+ QString::fromLatin1("http://efilmcritic.com/favicon.ico"),
+ NewsSourceBase::Arts, 3, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("superhits.ch"),
+ QString::fromLatin1("http://www.superhits.ch/cgi-bin/superhits.cgi?page=rdf"),
+ QString::fromLatin1("http://www.superhits.ch/favicon.ico"),
+ NewsSourceBase::Arts, 10, false, false, QString::fromLatin1("de")),
+ // Business -----------
+ NewsSourceBase::Data(
+ QString::fromLatin1("Internet.com Business"),
+ QString::fromLatin1("http://headlines.internet.com/internetnews/bus-news/news.rss"),
+ QString::null,
+ NewsSourceBase::Business, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("TradeSims"),
+ QString::fromLatin1("http://www.tradesims.com/AEX.rdf"),
+ QString::null,
+ NewsSourceBase::Business, 10, false, false),
+ // Computers ----------
+ NewsSourceBase::Data(
+ QString::fromLatin1( "linuxartist.org" ),
+ QString::fromLatin1( "http://www.linuxartist.org/backend.php"),
+ QString::fromLatin1( "http://www.linuxartist.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false ),
+ NewsSourceBase::Data(
+ QString::fromLatin1("KDE Deutschland"),
+ QString::fromLatin1("http://www.kde.de/nachrichten/nachrichten.rdf"),
+ QString::fromLatin1("http://www.kde.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("KDE France"),
+ QString::fromLatin1("http://www.kde-france.org/backend-breves.php3"),
+ QString::null,
+ NewsSourceBase::Computers, 10, true, false, QString::fromLatin1("fr")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("FreeBSD Project News"),
+ QString::fromLatin1("http://www.freebsd.org/news/news.rdf"),
+ QString::fromLatin1("http://www.freebsd.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("dot.kde.org"),
+ QString::fromLatin1("http://www.kde.org/dotkdeorg.rdf"),
+ QString::fromLatin1("http://www.kde.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("GNOME News"),
+ QString::fromLatin1("http://www.gnomedesktop.org/backend.php"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Slashdot"),
+ QString::fromLatin1("http://slashdot.org/slashdot.rdf"),
+ QString::fromLatin1("http://slashdot.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Ask Slashdot"),
+ QString::fromLatin1("http://slashdot.org/askslashdot.rdf"),
+ QString::fromLatin1("http://slashdot.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Slashdot: Features"),
+ QString::fromLatin1("http://slashdot.org/features.rdf"),
+ QString::fromLatin1("http://slashdot.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Slashdot: Apache"),
+ QString::fromLatin1("http://slashdot.org/apache.rdf"),
+ QString::fromLatin1("http://slashdot.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Slashdot: Books"),
+ QString::fromLatin1("http://slashdot.org/books.rdf"),
+ QString::fromLatin1("http://slashdot.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Jabber News"),
+ QString::fromLatin1("http://www.jabber.org/news/rss.xml"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Freshmeat"),
+ QString::fromLatin1("http://freshmeat.net/backend/fm.rdf"),
+ QString::fromLatin1("http://freshmeat.net/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Linux Weekly News"),
+ QString::fromLatin1("http://www.lwn.net/headlines/rss"),
+ QString::fromLatin1("http://www.lwn.net/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("heise online news"),
+ QString::fromLatin1("http://www.heise.de/newsticker/heise.rdf"),
+ QString::fromLatin1("http://www.heise.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("RUS-CERT Ticker"),
+ QString::fromLatin1("http://cert.uni-stuttgart.de/ticker/rus-cert.rdf"),
+ QString::fromLatin1("http://cert.uni-stuttgart.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("RUS-CERT Elsewhere"),
+ QString::fromLatin1("http://cert.uni-stuttgart.de/ticker/rus-cert-elsewhere.rdf"),
+ QString::fromLatin1("http://cert.uni-stuttgart.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Kuro5hin"),
+ QString::fromLatin1("http://kuro5hin.org/backend.rdf"),
+ QString::fromLatin1("http://kuro5hin.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, true, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Prolinux"),
+ QString::fromLatin1("http://www.pl-forum.de/backend/pro-linux.rdf"),
+ QString::fromLatin1("http://www.prolinux.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Linuxde.org"),
+ QString::fromLatin1("http://www.linuxde.org/backends/news.rdf"),
+ QString::fromLatin1("http://www.linuxde.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("LinuxSecurity.com"),
+ QString::fromLatin1("http://www.linuxsecurity.com/linuxsecurity_hybrid.rdf"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Linux Game Tome"),
+ QString::fromLatin1("http://happypenguin.org/html/news.rdf"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Telefragged"),
+ QString::fromLatin1("http://www.telefragged.com/cgi-bin/rdf.pl"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+/* NewsSourceBase::Data(
+ QString::fromLatin1("Gimp News"),
+ QString::fromLatin1("http://www.xach.com/gimp/news/channel.rdf"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),*/
+ NewsSourceBase::Data(
+ QString::fromLatin1("Mozilla"),
+ QString::fromLatin1("http://www.mozilla.org/news.rdf"),
+ QString::fromLatin1("http://www.mozillazine.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("MozillaZine"),
+ QString::fromLatin1("http://www.mozillazine.org/contents.rdf"),
+ QString::fromLatin1("http://www.mozillazine.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Daemon News"),
+ QString::fromLatin1("http://daily.daemonnews.org/ddn.rdf.php3"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("use Perl;"),
+ QString::fromLatin1("http://use.perl.org/useperl.rdf"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("desktopian.org"),
+ QString::fromLatin1("http://www.desktopian.org/includes/headlines.xml"),
+ QString::fromLatin1("http://www.desktopian.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Root prompt"),
+ QString::fromLatin1("http://www.rootprompt.org/rss/"),
+ QString::fromLatin1("http://www.rootprompt.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("SecurityFocus"),
+ QString::fromLatin1("http://www.securityfocus.com/rss/news.xml"),
+ QString::fromLatin1("http://www.securityfocus.com/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("LinuxNewbie"),
+ QString::fromLatin1("http://www.linuxnewbie.org/news.cdf"),
+ QString::fromLatin1("http://www.linuxnewbie.org/favicon.ico"),
+ NewsSourceBase::Computers, 5, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Arstechnica"),
+ QString::fromLatin1("http://arstechnica.com/etc/rdf/ars.rdf"),
+ QString::fromLatin1("http://arstechnica.com/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("amiga-news.de - deutschsprachige Amiga Nachrichten"),
+ QString::fromLatin1("http://www.amiga-news.de/de/backends/news/index.rss"),
+ QString::fromLatin1("http://www.amiga-news.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("amiga-news.de - english Amiga news"),
+ QString::fromLatin1("http://www.amiga-news.de/en/backends/news/index.rss"),
+ QString::fromLatin1("http://www.amiga-news.de/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("RadioTux)"),
+ QString::fromLatin1("http://blog.radiotux.de/feed/"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("kdenews.unixcode.org"),
+ QString::fromLatin1("http://kdenews.unixcode.org/?node=news&action=rss"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("FreshPorts - the place for ports"),
+ QString::fromLatin1("http://www.freshports.org/news.php"),
+ QString::fromLatin1("http://www.freshports.org/favicon.ico"),
+ NewsSourceBase::Computers, 20, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("NetPhoenix"),
+ QString::fromLatin1("http://www.netphoenix.at/rss/netphoenix.php"),
+ QString::fromLatin1("http://www.netphoenix.at/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("ShortNews - by www.netphoenix.at"),
+ QString::fromLatin1("http://www.netphoenix.at/rss/shortnews.php"),
+ QString::fromLatin1("http://www.netphoenix.at/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("zez.org - about code "),
+ QString::fromLatin1("http://zez.org/article/rssheadlines"),
+ QString::null,
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("BSDatwork.com"),
+ QString::fromLatin1("http://BSDatwork.com/backend.php"),
+ QString::fromLatin1("http://BSDatwork.com/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("FreshSource - the place for source"),
+ QString::fromLatin1("http://www.freshsource.org/news.php"),
+ QString::fromLatin1("http://www.freshsource.org/favicon.ico"),
+ NewsSourceBase::Computers, 20, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("The FreeBSD Diary"),
+ QString::fromLatin1("http://www.freebsddiary.org/news.php"),
+ QString::fromLatin1("http://www.freebsddiary.org/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("MaximumBSD"),
+ QString::fromLatin1("http://www.maximumbsd.com/backend/mb.rdf"),
+ QString::fromLatin1("http://www.maximumbsd.com/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("BR-Linux.org"),
+ QString::fromLatin1("http://br-linux.org/noticias/index.rdf"),
+ QString::fromLatin1("http://br-linux.org/noticias/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("OSNews.com"),
+ QString::fromLatin1("http://www.osnews.com/files/recent.rdf"),
+ QString::fromLatin1("http://www.osnews.com/favicon.ico"),
+ NewsSourceBase::Computers, 10, false, false),
+ // Miscellaneous ------
+ NewsSourceBase::Data(
+ QString::fromLatin1("tagesschau.de"),
+ QString::fromLatin1("http://www.tagesschau.de/newsticker.rdf"),
+ QString::fromLatin1("http://www.tagesschau.de/favicon.ico"),
+ NewsSourceBase::Misc, 10, true, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("N24.de"),
+ QString::fromLatin1("http://www.n24.de/rss/?rubrik=home"),
+ QString::fromLatin1("http://www.n24.de/favicon.ico"),
+ NewsSourceBase::Misc, 10, true, false, QString::fromLatin1("de")),
+/*
+ NewsSourceBase::Data(
+ QString::fromLatin1("CNN"),
+ QString::fromLatin1("http://www.cnn.com/cnn.rss"),
+ QString::fromLatin1("http://www.cnn.com/favicon.ico"),
+ NewsSourceBase::Misc, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("CNN Europe"),
+ QString::fromLatin1("http://europe.cnn.com/cnn.rss"),
+ QString::fromLatin1("http://europe.cnn.com/favicon.ico"),
+ NewsSourceBase::Misc, 10, false, false),
+*/
+ NewsSourceBase::Data(
+ QString::fromLatin1("HotWired"),
+ QString::fromLatin1("http://www.hotwired.com/webmonkey/meta/headlines.rdf"),
+ QString::fromLatin1("http://www.hotwired.com/favicon.ico"),
+ NewsSourceBase::Misc, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1("The Register"),
+ QString::fromLatin1("http://www.theregister.co.uk/tonys/slashdot.rdf"),
+ QString::fromLatin1("http://www.theregister.co.uk/favicon.ico"),
+ NewsSourceBase::Misc, 10, false, false),
+ NewsSourceBase::Data(
+ QString::fromLatin1( "Christian Science Monitor" ),
+ QString::fromLatin1( "http://www.csmonitor.com/rss/csm.rss"),
+ QString::fromLatin1( "http://www.csmonitor.com/favicon.ico"),
+ NewsSourceBase::Misc, 10, false, false ),
+ // Magazines ------
+ NewsSourceBase::Data(
+ QString::fromLatin1("Revista do Linux"),
+ QString::fromLatin1("http://www.revistadolinux.com.br/noticias/arquivo/noticias.rdf"),
+ QString::fromLatin1("http://www.revistadolinux.com.br/favicon.ico"),
+ NewsSourceBase::Magazines, 10, false, false /*, QString::fromLatin1("br")*/ ),
+ NewsSourceBase::Data(
+ QString::fromLatin1("Spiegel.de"),
+ QString::fromLatin1("http://www.spiegel.de/schlagzeilen/rss/0,5291,,00.xml"),
+ QString::fromLatin1("http://www.spiegel.de/favicon.ico"),
+ NewsSourceBase::Magazines, 10, true, false, QString::fromLatin1("de")),
+ NewsSourceBase::Data(
+ QString::fromLatin1("FAZ.de"),
+ QString::fromLatin1("http://www.faz.net/s/Rub/Tpl~Epartner~SRss_.xml"),
+ QString::fromLatin1("http://www.faz.net/favicon.ico"),
+ NewsSourceBase::Magazines, 10, true, false, QString::fromLatin1("de")),
+ // Recreation
+ NewsSourceBase::Data(
+ QString::fromLatin1("Segfault"),
+ QString::fromLatin1("http://segfault.org/stories.xml"),
+ QString::fromLatin1("http://segfault.org/favicon.ico"),
+ NewsSourceBase::Recreation, 10, false, false),
+ // Society
+ NewsSourceBase::Data(
+ QString::fromLatin1("nippon.it"),
+ QString::fromLatin1("http://www.nippon.it/backend.it.php"),
+ QString::fromLatin1("http://www.nippon.it/favicon.ico"),
+ NewsSourceBase::Society, 10, false, false, QString::fromLatin1("it")),
+ NewsSourceBase::Data(
+ QString::fromLatin1( "gflash" ),
+ QString::fromLatin1( "http://www.gflash.de/backend.php"),
+ QString::fromLatin1( "http://www.gflash.de/favicon.ico"),
+ NewsSourceBase::Society, 10, false, false, QString::fromLatin1( "de" ) ),
+ NewsSourceBase::Data(
+ QString::fromLatin1( "Quintessenz" ),
+ QString::fromLatin1( "http://quintessenz.at/cgi-bin/rdf"),
+ QString::fromLatin1( "http://quintessenz.at/favicon.ico"),
+ NewsSourceBase::Society, 9, false, false, QString::fromLatin1( "de" ) )
+};
+
+ArticleFilter::ArticleFilter(const QString &action, const QString &newsSource,
+ const QString &condition, const QString &expression, bool enabled)
+ : m_action(action),
+ m_newsSource(newsSource),
+ m_condition(condition),
+ m_expression(expression),
+ m_enabled(enabled)
+{
+}
+
+bool ArticleFilter::matches(Article::Ptr a) const
+{
+ if (!enabled() ||
+ (a->newsSource()->data().name != newsSource() &&
+ newsSource() != i18n("all news sources")))
+ return false;
+
+ bool matches;
+
+ if (condition() == i18n("contain"))
+ matches = a->headline().contains(expression());
+ else if (condition() == i18n("do not contain"))
+ matches = !a->headline().contains(expression());
+ else if (condition() == i18n("equal"))
+ matches = (a->headline() == expression());
+ else if (condition() == i18n("do not equal"))
+ matches = (a->headline() != expression());
+ else { // condition() == i18n("match")
+ QRegExp regexp = QRegExp(expression());
+ matches = regexp.exactMatch(a->headline());
+ }
+
+ if (action() == i18n("Show"))
+ matches = !matches;
+
+ return matches;
+}
+
+ConfigAccess::ConfigAccess()
+ : m_defaultCfg(new KConfig(QString::null, true, false))
+{
+ m_cfg = m_defaultCfg;
+}
+
+ConfigAccess::ConfigAccess(KConfig *config)
+ : m_cfg(config), m_defaultCfg(0L)
+{
+ m_cfg->setGroup("KNewsTicker");
+}
+
+ConfigAccess::~ConfigAccess()
+{
+ delete m_defaultCfg;
+}
+
+unsigned int ConfigAccess::interval() const
+{
+ return m_cfg->readNumEntry("Update interval", 30);
+}
+
+void ConfigAccess::setInterval(const unsigned int interval)
+{
+ m_cfg->writeEntry("Update interval", interval);
+ m_cfg->sync();
+}
+
+unsigned int ConfigAccess::mouseWheelSpeed() const
+{
+ return m_cfg->readNumEntry("Mouse wheel speed", 5);
+}
+
+void ConfigAccess::setMouseWheelSpeed(const unsigned int mouseWheelSpeed)
+{
+ m_cfg->writeEntry("Mouse wheel speed", mouseWheelSpeed);
+ m_cfg->sync();
+}
+
+QFont ConfigAccess::font() const
+{
+ QFont font = KGlobalSettings::fixedFont();
+ return m_cfg->readFontEntry("Font", &font);
+}
+
+void ConfigAccess::setFont(const QFont &font)
+{
+ m_cfg->writeEntry("Font", font);
+ m_cfg->sync();
+}
+
+bool ConfigAccess::customNames() const
+{
+ return m_cfg->readBoolEntry("Custom names", false);
+}
+
+void ConfigAccess::setCustomNames(bool customNames)
+{
+ m_cfg->writeEntry("Custom names", customNames);
+ m_cfg->sync();
+}
+
+bool ConfigAccess::scrollMostRecentOnly() const
+{
+ return m_cfg->readBoolEntry("Scroll most recent headlines only", false);
+}
+
+void ConfigAccess::setScrollMostRecentOnly(bool scrollMostRecentOnly)
+{
+ m_cfg->writeEntry("Scroll most recent headlines only", scrollMostRecentOnly);
+ m_cfg->sync();
+}
+
+bool ConfigAccess::offlineMode() const
+{
+ return m_cfg->readBoolEntry("Offline mode", false);
+}
+
+void ConfigAccess::setOfflineMode(bool offlineMode)
+{
+ m_cfg->writeEntry("Offline mode", offlineMode);
+ m_cfg->sync();
+}
+
+QStringList ConfigAccess::newsSources() const
+{
+ QStringList tempList = m_cfg->readListEntry("News sources");
+ if (tempList.isEmpty())
+ for (unsigned int i = 0; i < DEFAULT_NEWSSOURCES; i++)
+ tempList << NewsSourceDefault[i].name;
+ return tempList;
+}
+
+ArticleFilter::List ConfigAccess::filters() const
+{
+ return m_cfg->readIntListEntry("Filters");
+}
+
+void ConfigAccess::setNewsSources(const QStringList &newsSources)
+{
+ m_cfg->writeEntry("News sources", newsSources);
+ m_cfg->sync();
+}
+
+void ConfigAccess::setFilters(const ArticleFilter::List &filters)
+{
+ m_cfg->writeEntry("Filters", filters);
+ m_cfg->sync();
+}
+
+unsigned int ConfigAccess::scrollingSpeed() const
+{
+ return m_cfg->readNumEntry("Scrolling speed", 20);
+}
+
+void ConfigAccess::setScrollingSpeed(const unsigned int scrollingSpeed)
+{
+ m_cfg->writeEntry("Scrolling speed", scrollingSpeed);
+ m_cfg->sync();
+}
+
+unsigned int ConfigAccess::scrollingDirection() const
+{
+ return m_cfg->readNumEntry("Scrolling direction", 0);
+}
+
+void ConfigAccess::setScrollingDirection(const unsigned int scrollingDirection)
+{
+ m_cfg->writeEntry("Scrolling direction", scrollingDirection);
+ m_cfg->sync();
+}
+
+QColor ConfigAccess::foregroundColor() const
+{
+ return m_cfg->readColorEntry("Foreground color", &Qt::black);
+}
+
+void ConfigAccess::setForegroundColor(const QColor &foregroundColor)
+{
+ m_cfg->writeEntry("Foreground color", foregroundColor);
+ m_cfg->sync();
+}
+
+QColor ConfigAccess::backgroundColor() const
+{
+ return m_cfg->readColorEntry("Background color", &Qt::white);
+}
+
+void ConfigAccess::setBackgroundColor(const QColor &backgroundColor)
+{
+ m_cfg->writeEntry("Background color", backgroundColor);
+ m_cfg->sync();
+}
+
+QColor ConfigAccess::highlightedColor() const
+{
+ return m_cfg->readColorEntry("Highlighted color", &Qt::red);
+}
+
+void ConfigAccess::setHighlightedColor(const QColor &highlightedColor)
+{
+ m_cfg->writeEntry("Highlighted color", highlightedColor);
+ m_cfg->sync();
+}
+
+bool ConfigAccess::underlineHighlighted() const
+{
+ return m_cfg->readBoolEntry("Underline highlighted headlines", true);
+}
+
+void ConfigAccess::setUnderlineHighlighted(bool underlineHighlighted)
+{
+ m_cfg->writeEntry("Underline highlighted headlines", underlineHighlighted);
+ m_cfg->sync();
+}
+
+NewsSourceBase *ConfigAccess::newsSource(const QString &newsSource)
+{
+ NewsSourceBase::Data nsd;
+
+ if (m_cfg->hasGroup(newsSource)) {
+ m_cfg->setGroup(newsSource);
+ nsd.name = newsSource;
+ nsd.sourceFile = m_cfg->readPathEntry("Source file");
+ nsd.isProgram = m_cfg->readBoolEntry("Is program", false);
+ nsd.subject = static_cast<NewsSourceBase::Subject>
+ (m_cfg->readNumEntry("Subject", NewsSourceBase::Computers));
+ nsd.icon = m_cfg->readEntry("Icon");
+ nsd.maxArticles = m_cfg->readNumEntry("Max articles", 10);
+ nsd.enabled = m_cfg->readBoolEntry("Enabled", true);
+ nsd.language = m_cfg->readEntry("Language", QString::fromLatin1("C"));
+ m_cfg->setGroup("KNewsTicker");
+ } else for (unsigned int i = 0; i < DEFAULT_NEWSSOURCES; i++)
+ if (NewsSourceDefault[i].name == newsSource) {
+ nsd = NewsSourceDefault[i];
+ if (nsd.enabled)
+ nsd.enabled = (nsd.language == QString::fromLatin1("C") ||
+ KGlobal::locale()->languageList().contains(nsd.language));
+ break;
+ }
+
+ if (nsd.isProgram)
+ return new ProgramNewsSource(nsd, this);
+ else
+ return new SourceFileNewsSource(nsd, this);
+
+ return 0L;
+}
+
+ArticleFilter ConfigAccess::filter(const unsigned int filterNo) const
+{
+ ArticleFilter f;
+ f.setId(filterNo);
+
+ if (m_cfg->hasGroup(QString::fromLatin1("Filter #%1").arg(filterNo))) {
+ m_cfg->setGroup(QString::fromLatin1("Filter #%1").arg(filterNo));
+ f.setAction(m_cfg->readEntry("Action", i18n("Show")));
+ f.setNewsSource(m_cfg->readEntry("News source", i18n("all news sources")));
+ f.setCondition(m_cfg->readEntry("Condition", i18n("contain")));
+ f.setExpression(m_cfg->readEntry("Expression"));
+ f.setEnabled(m_cfg->readBoolEntry("Enabled", true));
+ m_cfg->setGroup("KNewsTicker");
+ }
+
+ return f;
+}
+
+void ConfigAccess::setNewsSource(const NewsSourceBase::Data &ns)
+{
+ m_cfg->setGroup(ns.name);
+ m_cfg->writePathEntry("Source file", ns.sourceFile);
+ m_cfg->writeEntry("Is program", ns.isProgram);
+ m_cfg->writeEntry("Max articles", ns.maxArticles);
+ m_cfg->writeEntry("Subject", ns.subject);
+ m_cfg->writeEntry("Icon", ns.icon);
+ m_cfg->writeEntry("Enabled", ns.enabled);
+ m_cfg->writeEntry("Language", ns.language);
+ m_cfg->setGroup("KNewsTicker");
+ m_cfg->sync();
+}
+
+void ConfigAccess::setFilter(const ArticleFilter &f)
+{
+ m_cfg->setGroup(QString::fromLatin1("Filter #%1").arg(f.id()));
+ m_cfg->writeEntry("Action", f.action());
+ m_cfg->writeEntry("News source", f.newsSource());
+ m_cfg->writeEntry("Condition", f.condition());
+ m_cfg->writeEntry("Expression", f.expression());
+ m_cfg->writeEntry("Enabled", f.enabled());
+ m_cfg->setGroup("KNewsTicker");
+ m_cfg->sync();
+}
+
+bool ConfigAccess::showIcons() const
+{
+ return m_cfg->readBoolEntry("Show icons", true);
+}
+
+void ConfigAccess::setShowIcons(bool showIcons)
+{
+ m_cfg->writeEntry("Show icons", showIcons);
+ m_cfg->sync();
+}
+
+bool ConfigAccess::slowedScrolling() const
+{
+ return m_cfg->readBoolEntry("Slowed scrolling", false);
+}
+
+void ConfigAccess::setSlowedScrolling(bool slowedScrolling)
+{
+ m_cfg->writeEntry("Slowed scrolling", slowedScrolling);
+ m_cfg->sync();
+}
+
diff --git a/knewsticker/common/configaccess.h b/knewsticker/common/configaccess.h
new file mode 100644
index 00000000..3cdf80e5
--- /dev/null
+++ b/knewsticker/common/configaccess.h
@@ -0,0 +1,135 @@
+/*
+ * configaccess.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef CONFIGACCESS_H
+#define CONFIGACCESS_H
+
+#include "configiface.h"
+#include "newsengine.h"
+
+#include <kconfig.h>
+#include <kio/job.h>
+#include <klocale.h>
+
+#include <qpixmap.h>
+#include <qvaluelist.h>
+
+#define DEFAULT_NEWSSOURCES 63
+#define DEFAULT_SUBJECTS 13
+
+class QColor;
+class QFont;
+class KURL;
+
+class ArticleFilter {
+ public:
+ typedef QValueList<int> List;
+
+ ArticleFilter(const QString & = I18N_NOOP(QString::fromLatin1("Show")),
+ const QString & = I18N_NOOP(QString::fromLatin1("all newssources")),
+ const QString & = I18N_NOOP(QString::fromLatin1("contain")),
+ const QString & = QString::null,
+ bool = true);
+
+ QString action() const { return m_action; }
+ void setAction(const QString &action) { m_action = action; }
+
+ QString newsSource() const { return m_newsSource; }
+ void setNewsSource(const QString &newsSource) { m_newsSource = newsSource; }
+
+ QString condition() const { return m_condition; }
+ void setCondition(const QString &condition) { m_condition = condition; }
+
+ QString expression() const { return m_expression; }
+ void setExpression(const QString &expression) { m_expression = expression; }
+
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled) { m_enabled = enabled; }
+
+ unsigned int id() const { return m_id; }
+ void setId(const unsigned int id) { m_id = id; }
+
+ bool matches(Article::Ptr) const;
+
+ private:
+ QString m_action;
+ QString m_newsSource;
+ QString m_condition;
+ QString m_expression;
+ bool m_enabled;
+ unsigned int m_id;
+};
+
+class ConfigAccess : public ConfigIface
+{
+ public:
+ ConfigAccess();
+ ConfigAccess(KConfig *);
+ virtual ~ConfigAccess();
+
+ virtual unsigned int interval() const;
+ virtual unsigned int scrollingSpeed() const;
+ virtual unsigned int mouseWheelSpeed() const;
+ virtual unsigned int scrollingDirection() const;
+ virtual bool customNames() const;
+ virtual bool scrollMostRecentOnly() const;
+ virtual bool offlineMode() const;
+ virtual bool underlineHighlighted() const;
+ virtual bool showIcons() const;
+ virtual bool slowedScrolling() const;
+ virtual QColor foregroundColor() const;
+ virtual QColor backgroundColor() const;
+ virtual QColor highlightedColor() const;
+ QFont font() const;
+ virtual QStringList newsSources() const;
+ NewsSourceBase *newsSource(const QString &);
+ ArticleFilter::List filters() const;
+ ArticleFilter filter(const unsigned int) const;
+
+ static bool horizontal(Direction d) { return d == Left || d == Right; }
+ static bool vertical(Direction d) { return d == Up || d == Down; }
+ static bool rotated(Direction d) { return d == UpRotated || d == DownRotated; }
+
+ inline bool horizontalScrolling() const
+ {
+ return horizontal((Direction) scrollingDirection());
+ };
+
+ inline bool verticalScrolling() const
+ {
+ return vertical((Direction)scrollingDirection());
+ };
+
+ virtual void setInterval(const unsigned int);
+ virtual void setScrollingSpeed(const unsigned int);
+ virtual void setMouseWheelSpeed(const unsigned int);
+ virtual void setScrollingDirection(const unsigned int);
+ virtual void setCustomNames(bool);
+ virtual void setScrollMostRecentOnly(bool);
+ virtual void setOfflineMode(bool);
+ virtual void setUnderlineHighlighted(bool);
+ virtual void setShowIcons(bool);
+ virtual void setSlowedScrolling(bool);
+ virtual void setForegroundColor(const QColor &);
+ virtual void setBackgroundColor(const QColor &);
+ virtual void setHighlightedColor(const QColor &);
+ void setFont(const QFont &);
+ virtual void setNewsSources(const QStringList &);
+ void setNewsSource(const NewsSourceBase::Data &);
+ void setFilters(const ArticleFilter::List &);
+ void setFilter(const ArticleFilter &);
+ void reparseConfiguration() { m_cfg->reparseConfiguration(); }
+
+ private:
+ KConfig *m_cfg;
+ KConfig *m_defaultCfg;
+};
+
+#endif // CONFIGACCESS_H
diff --git a/knewsticker/common/configiface.h b/knewsticker/common/configiface.h
new file mode 100644
index 00000000..64bc312d
--- /dev/null
+++ b/knewsticker/common/configiface.h
@@ -0,0 +1,56 @@
+/*
+ * configface.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef CONFIGIFACE_H
+#define CONFIGIFACE_H
+
+class QColor;
+class QStringList;
+class QFont;
+
+class KURL;
+
+class ConfigIface
+{
+ public:
+ enum Direction {Left = 0, Right, Up, Down, UpRotated, DownRotated};
+
+ virtual unsigned int interval() const = 0;
+ virtual unsigned int scrollingSpeed() const = 0;
+ virtual unsigned int mouseWheelSpeed() const = 0;
+ virtual unsigned int scrollingDirection() const = 0;
+ virtual bool customNames() const = 0;
+ virtual bool scrollMostRecentOnly() const = 0;
+ virtual bool offlineMode() const = 0;
+ virtual bool underlineHighlighted() const = 0;
+ virtual bool showIcons() const = 0;
+ virtual bool slowedScrolling() const = 0;
+ virtual QColor foregroundColor() const = 0;
+ virtual QColor backgroundColor() const = 0;
+ virtual QColor highlightedColor() const = 0;
+ virtual QStringList newsSources() const = 0;
+
+ virtual void setInterval(const unsigned int) = 0;
+ virtual void setScrollingSpeed(const unsigned int) = 0;
+ virtual void setMouseWheelSpeed(const unsigned int) = 0;
+ virtual void setScrollingDirection(const unsigned int) = 0;
+ virtual void setCustomNames(bool) = 0;
+ virtual void setScrollMostRecentOnly(bool) = 0;
+ virtual void setOfflineMode(bool) = 0;
+ virtual void setUnderlineHighlighted(bool) = 0;
+ virtual void setShowIcons(bool) = 0;
+ virtual void setSlowedScrolling(bool) = 0;
+ virtual void setForegroundColor(const QColor &) = 0;
+ virtual void setBackgroundColor(const QColor &) = 0;
+ virtual void setHighlightedColor(const QColor &) = 0;
+ virtual void setNewsSources(const QStringList &) = 0;
+};
+
+#endif // CONFIGIFACE_H
diff --git a/knewsticker/common/newsengine.cpp b/knewsticker/common/newsengine.cpp
new file mode 100644
index 00000000..cce7895c
--- /dev/null
+++ b/knewsticker/common/newsengine.cpp
@@ -0,0 +1,324 @@
+/*
+ * newsengine.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "newsengine.h"
+#include "configiface.h"
+#include "configaccess.h"
+#include "xmlnewsaccess.h"
+#include "newsiconmgr.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+
+#include <qbuffer.h>
+
+Article::Article(NewsSourceBase *parent, const QString &headline,
+ const KURL &address)
+ : XMLNewsArticle(headline, address),
+ m_parent(parent),
+ m_read(false)
+{
+}
+
+bool Article::operator==(const Article &other) const
+{
+ return headline() == other.headline() && address() == other.address();
+}
+
+void Article::open()
+{
+ (void) new KRun(address());
+ m_read = true;
+}
+
+NewsSourceBase::NewsSourceBase(const Data &nsd, ConfigIface *config)
+ : XMLNewsSource(),
+ m_data(nsd),
+ m_icon(QPixmap()),
+ m_cfg(dynamic_cast<ConfigAccess *>(config)),
+ m_newsIconMgr(NewsIconMgr::self())
+{
+ connect(this, SIGNAL(loadComplete(XMLNewsSource *, bool)),
+ SLOT(slotProcessArticles(XMLNewsSource *, bool)));
+}
+
+void NewsSourceBase::getIcon()
+{
+ connect(m_newsIconMgr, SIGNAL(gotIcon(const KURL &, const QPixmap &)),
+ this, SLOT(slotGotIcon(const KURL &, const QPixmap &)));
+ m_newsIconMgr->getIcon( KURL( m_data.icon ) );
+}
+
+QString NewsSourceBase::newsSourceName() const
+{
+ if (m_cfg->customNames() || m_name.isEmpty())
+ return m_data.name;
+ else
+ return m_name;
+}
+
+QString NewsSourceBase::subjectText(const Subject subject)
+{
+ switch (subject) {
+ case Arts: return i18n("Arts");
+ case Business: return i18n("Business");
+ case Computers: return i18n("Computers");
+ case Games: return i18n("Games");
+ case Health: return i18n("Health");
+ case Home: return i18n("Home");
+ case Recreation: return i18n("Recreation");
+ case Reference: return i18n("Reference");
+ case Science: return i18n("Science");
+ case Shopping: return i18n("Shopping");
+ case Society: return i18n("Society");
+ case Sports: return i18n("Sports");
+ case Misc: return i18n("Miscellaneous");
+ case Magazines: return i18n("Magazines");
+ default: return i18n("Unknown");
+ }
+}
+
+void NewsSourceBase::slotProcessArticles(XMLNewsSource *, bool gotEm)
+{
+ if (!gotEm) {
+ emit invalidInput(this);
+ return;
+ }
+
+ Article::List oldArticles = m_articles;
+
+ // Truncate the list of articles if necessary.
+ m_articles.clear();
+ XMLNewsArticle::List::ConstIterator it = XMLNewsSource::articles().begin();
+ XMLNewsArticle::List::ConstIterator end = XMLNewsSource::articles().end();
+ for (; it != end; ++it)
+ m_articles.append(new Article(this, (*it).headline(), (*it).address()));
+
+ // Fill the list with old articles until maxArticles is reached.
+ if (m_articles.count() < m_data.maxArticles) {
+ Article::List::ConstIterator oldArtIt = oldArticles.begin();
+ Article::List::ConstIterator oldArtEnd = oldArticles.end();
+ bool isNewArticle;
+ for (; oldArtIt != oldArtEnd; ++oldArtIt) {
+ isNewArticle = true;
+ Article::List::ConstIterator newArtIt = m_articles.begin();
+ Article::List::ConstIterator newArtEnd = m_articles.end();
+ for (; newArtIt != newArtEnd; ++newArtIt) {
+ Article newArt = **newArtIt;
+ Article oldArt = **oldArtIt;
+ if (newArt == oldArt)
+ isNewArticle = false;
+ }
+
+ if (isNewArticle)
+ m_articles.append(*oldArtIt);
+ if (m_articles.count() == m_data.maxArticles)
+ break;
+ }
+ } else
+ while (m_articles.count() > m_data.maxArticles)
+ m_articles.remove(m_articles.fromLast());
+
+ // Copy the read flag of known articles
+ Article::List::ConstIterator oldArtIt = oldArticles.begin();
+ Article::List::ConstIterator oldArtEnd = oldArticles.end();
+ for (; oldArtIt != oldArtEnd; ++oldArtIt) {
+ Article::List::Iterator newArtIt = m_articles.begin();
+ Article::List::Iterator newArtEnd = m_articles.end();
+ for (; newArtIt != newArtEnd; ++newArtIt)
+ if (**oldArtIt == **newArtIt)
+ (*newArtIt)->setRead((*oldArtIt)->read());
+ }
+
+ emit newNewsAvailable(this, oldArticles != m_articles);
+}
+
+Article::Ptr NewsSourceBase::article(const QString &headline)
+{
+ Article::List::ConstIterator it = m_articles.begin();
+ Article::List::ConstIterator end = m_articles.end();
+ for (; it != end; ++it)
+ if ((*it)->headline() == headline)
+ return *it;
+
+ return 0L;
+}
+
+void NewsSourceBase::slotGotIcon(const KURL &url, const QPixmap &pixmap)
+{
+ if (url.url() == m_data.icon) {
+ m_icon = pixmap;
+
+ disconnect(m_newsIconMgr, SIGNAL(gotIcon(const KURL &, const QPixmap &)),
+ this, SLOT(slotGotIcon(const KURL &, const QPixmap &)));
+ }
+}
+
+SourceFileNewsSource::SourceFileNewsSource(const NewsSourceBase::Data &nsd,
+ ConfigIface *config)
+ : NewsSourceBase(nsd, config)
+{
+}
+
+void SourceFileNewsSource::retrieveNews()
+{
+ loadFrom(KURL( m_data.sourceFile ));
+}
+
+ProgramNewsSource::ProgramNewsSource(const NewsSourceBase::Data &nsd,
+ ConfigIface *config) : NewsSourceBase(nsd, config),
+ m_program(new KProcess()),
+ m_programOutput(0)
+{
+ connect(m_program, SIGNAL(processExited(KProcess *)),
+ SLOT(slotProgramExited(KProcess *)));
+ connect(m_program, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ SLOT(slotGotProgramOutput(KProcess *, char *, int)));
+
+ m_data.sourceFile = KURL(m_data.sourceFile).encodedPathAndQuery();
+}
+
+ProgramNewsSource::~ProgramNewsSource()
+{
+ delete m_program;
+ delete m_programOutput;
+}
+
+void ProgramNewsSource::retrieveNews()
+{
+ m_programOutput = new QBuffer;
+ m_programOutput->open(IO_WriteOnly);
+
+ *m_program << m_data.sourceFile;
+ m_program->start(KProcess::NotifyOnExit, KProcess::Stdout);
+}
+
+void ProgramNewsSource::slotGotProgramOutput(KProcess *, char *data, int length)
+{
+ m_programOutput->writeBlock(data, length);
+}
+
+void ProgramNewsSource::slotProgramExited(KProcess *proc)
+{
+ bool okSoFar = true;
+
+ QString errorMsg;
+
+ if (!proc->normalExit()) {
+ errorMsg = i18n("<p>The program '%1' was terminated abnormally.<br>This can"
+ " happen if it receives the SIGKILL signal.</p>");
+ okSoFar = false;
+ } else {
+ ErrorCode error = static_cast<ErrorCode>(proc->exitStatus());
+ if (error != NOERR) {
+ errorMsg = errorMessage(error).arg(m_data.sourceFile);
+ okSoFar = false;
+ }
+ }
+
+ if (!okSoFar) {
+ QString output = QString(m_programOutput->buffer());
+ if (!output.isEmpty()) {
+ output = QString::fromLatin1("\"") + output + QString::fromLatin1("\"");
+ errorMsg += i18n("<p>Program output:<br>%1<br>").arg(output);
+ }
+ KMessageBox::detailedError(0, i18n("An error occurred while updating the"
+ " news source '%1'.").arg(newsSourceName()), errorMsg,
+ i18n("KNewsTicker Error"));
+ }
+
+ processData(m_programOutput->buffer(), okSoFar);
+
+ delete m_programOutput;
+ m_programOutput = 0;
+}
+
+QString ProgramNewsSource::errorMessage(const ErrorCode errorCode)
+{
+ switch (errorCode) {
+ case EPERM: return i18n("The program '%1' could not be started at all.");
+ case ENOENT: return i18n("The program '%1' tried to read or write a file or"
+ " directory which could not be found.");
+ case EIO: return i18n("An error occurred while the program '%1' tried to"
+ " read or write data.");
+ case E2BIG: return i18n("The program '%1' was passed too many arguments."
+ " Please adjust the command line in the configuration dialog.");
+ case ENOEXEC: return i18n("An external system program upon which the"
+ " program '%1' relied could not be executed.");
+ case EACCESS: return i18n("The program '%1' tried to read or write a file or"
+ " directory but lacks the permission to do so.");
+ case ENODEV: return i18n("The program '%1' tried to access a device which"
+ " was not available.");
+ case ENOSPC: return i18n("There is no more space left on the device used by"
+ " the program '%1'.");
+ case EROFS: return i18n("The program '%1' tried to create a temporary file"
+ " on a read only file system.");
+ case ENOSYS: return i18n("The program '%1' tried to call a function which"
+ " is not implemented or attempted to access an external resource which"
+ " does not exist.");
+ case ENODATA: return i18n("The program '%1' was unable to retrieve input data and"
+ " was therefore unable to return any XML data.");
+ case ENONET: return i18n("The program '%1' tried to access a host which is not"
+ " connected to a network.");
+ case EPROTO: return i18n("The program '%1' tried to access a protocol which is not"
+ " implemented.");
+ case EDESTADDRREQ: return i18n("The program '%1' requires you to configure a"
+ " destination address to retrieve data from. Please refer to the"
+ " documentation of the program for information on how to do that.");
+ case ESOCKTNOSUPPORT: return i18n("The program '%1' tried to use a socket"
+ " type which is not supported by this system.");
+ case ENETUNREACH: return i18n("The program '%1' tried to access an unreachable"
+ " network.");
+ case ENETRESET: return i18n("The network the program '%1' was trying to access"
+ " dropped the connection with a reset.");
+ case ECONNRESET: return i18n("The connection of the program '%1' was reset by"
+ " peer.");
+ case ETIMEDOUT: return i18n("The connection the program '%1' was trying to"
+ " establish timed out.");
+ case ECONNREFUSED: return i18n("The connection the program '%1' was trying to"
+ " establish was refused.");
+ case EHOSTDOWN: return i18n("The host the program '%1' was trying to reach is"
+ " down.");
+ case EHOSTUNREACH: return i18n("The host the program '%1' was trying to reach is"
+ " unreachable, no route to host.");
+ case ENOEXECBIT: return i18n("KNewsTicker could not execute the program '%1'"
+ " because its executable bit was not set. You can mark that program as"
+ " executable by executing the following steps:<ul>"
+ "<li>Open a Konqueror window and browse to the program</li>"
+ "<li>Click on the file with the right mouse button, and select 'Properties'</li>"
+ "<li>Open the 'Permissions' tab and make sure that the box in the column"
+ " 'Exec' and the row 'User' is checked to ensure that the current user"
+ " is allowed to execute that file.</li></ul>");
+ case EBADREQ: return i18n("The program '%1' sent a bad request which was not"
+ " understood by the server.");
+ case ENOAUTH: return i18n("The program '%1' failed to issue an authorization for"
+ " an area which needs some form of authorization before it can be"
+ " accessed.");
+ case EMUSTPAY: return i18n("The program '%1' aborted because it could not access"
+ " the data without paying for it.");
+ case EFORBIDDEN: return i18n("The program '%1' tried to access a forbidden"
+ " source.");
+ case ENOTFOUND: return i18n("The program '%1' tried to access data which"
+ " could not be found.");
+ case ETIMEOUT: return i18n("The HTTP request of the program '%1' timed out.");
+ case ESERVERE: return i18n("A server error has been encountered. It is likely"
+ " that you cannot do anything about it.");
+ case EHTTPNOSUP: return i18n("The HTTP protocol version used by the program"
+ " '%1' was not understood by the HTTP server or source.");
+ default: return i18n("KNewsTicker was unable to detect the exact reasons for"
+ " the error.");
+ }
+}
+
+#include "newsengine.moc"
diff --git a/knewsticker/common/newsengine.h b/knewsticker/common/newsengine.h
new file mode 100644
index 00000000..342dd7fe
--- /dev/null
+++ b/knewsticker/common/newsengine.h
@@ -0,0 +1,203 @@
+/*
+ * newsengine.h
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef NEWSENGINE_H
+#define NEWSENGINE_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "configiface.h"
+#include "xmlnewsaccess.h"
+
+#include <qptrlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+
+#include <kio/job.h>
+#include <klocale.h>
+#include <ksharedptr.h>
+
+class ConfigAccess;
+class NewsIconMgr;
+class NewsSourceBase;
+
+class QBuffer;
+class QDomDocument;
+
+class KProcess;
+class KURL;
+
+class KDE_EXPORT Article : public XMLNewsArticle, public KShared
+{
+ public:
+ typedef KSharedPtr<Article> Ptr;
+ typedef QValueList<Ptr> List;
+
+ Article(NewsSourceBase *, const QString & = QString::null,
+ const KURL & = KURL());
+ bool operator==(const Article &other) const;
+
+ bool read() const { return m_read; }
+ void setRead(bool read) { m_read = read; }
+
+ NewsSourceBase *newsSource() const { return m_parent; }
+
+ void open();
+
+ private:
+ NewsSourceBase *m_parent; // don't use KSharedPtr to avoid circular refs!
+ bool m_read;
+};
+
+class KDE_EXPORT NewsSourceBase : public XMLNewsSource, public KShared
+{
+ Q_OBJECT
+
+ public:
+ enum Subject {
+ Arts = 0, Business, Computers, Games, Health, Home, Recreation,
+ Reference, Science, Shopping, Society, Sports, Misc, Magazines
+ };
+ struct Data {
+ Data(const QString &_name = I18N_NOOP("Unknown"),
+ const QString &_sourceFile = QString::null,
+ const QString &_icon = QString::null,
+ const Subject _subject = Computers,
+ unsigned int _maxArticles = 10,
+ bool _enabled = true, bool _isProgram = false,
+ const QString &_language = QString::fromLatin1("C"))
+ {
+ name = _name;
+ sourceFile = _sourceFile;
+ icon = _icon;
+ maxArticles = _maxArticles;
+ subject = _subject;
+ enabled = _enabled;
+ isProgram = _isProgram;
+ language = _language;
+ }
+
+ QString name;
+ QString sourceFile;
+ QString icon;
+ Subject subject;
+ unsigned int maxArticles;
+ bool enabled;
+ bool isProgram;
+ QString language;
+ };
+ typedef KSharedPtr<NewsSourceBase> Ptr;
+ typedef QValueList<Ptr> List;
+
+ NewsSourceBase(const Data &, ConfigIface *);
+
+ virtual QString newsSourceName() const;
+ QString sourceFile() const { return m_data.sourceFile; }
+ unsigned int maxArticles() const { return m_data.maxArticles; }
+ QPixmap icon() const { return m_icon; }
+ void getIcon();
+
+ Data data() const { return m_data; }
+
+ Article::List articles() const { return m_articles; }
+ Article::Ptr article(const QString &);
+
+ static QString subjectText(const Subject);
+
+ signals:
+ void newNewsAvailable(const NewsSourceBase::Ptr &, bool);
+ void invalidInput(const NewsSourceBase::Ptr &);
+
+ public slots:
+ virtual void retrieveNews() = 0;
+
+ protected slots:
+ void slotProcessArticles(XMLNewsSource *, bool);
+ void slotGotIcon(const KURL &, const QPixmap &);
+
+ protected:
+ Data m_data;
+ QPixmap m_icon;
+ ConfigAccess *m_cfg;
+ NewsIconMgr *m_newsIconMgr;
+ Article::List m_articles;
+};
+
+class KDE_EXPORT SourceFileNewsSource : public NewsSourceBase
+{
+ Q_OBJECT
+
+ public:
+ SourceFileNewsSource(const NewsSourceBase::Data &, ConfigIface *);
+
+ public slots:
+ virtual void retrieveNews();
+};
+
+// Make sure compilers don't translate
+// ProgramNewsSource::ErrorCode into
+// enum ErrorCode { 1 = 2, ... }
+#undef NOERR
+#undef EPERM
+#undef ENOENT
+#undef EIO
+#undef E2BIG
+#undef ENOEXEC
+#undef EACCESS
+#undef ENODEV
+#undef ENOSPC
+#undef EROFS
+#undef ENOSYS
+#undef ENODATA
+#undef ENONET
+#undef EPROTO
+#undef EDESTADDRREQ
+#undef ESOCKTNOSUPPORT
+#undef ENETUNREACH
+#undef ENETRESET
+#undef ECONNRESET
+#undef ETIMEDOUT
+#undef ECONNREFUSED
+#undef EHOSTDOWN
+#undef EHOSTUNREACH
+
+class KDE_EXPORT ProgramNewsSource : public NewsSourceBase
+{
+ Q_OBJECT
+
+ public:
+ enum ErrorCode { NOERR = 0, EPERM, ENOENT, EIO = 5, E2BIG = 7,
+ ENOEXEC, EACCESS = 13, ENODEV = 19, ENOSPC = 28, EROFS = 30,
+ ENOSYS = 38, ENODATA = 61, ENONET = 64, EPROTO = 71, EDESTADDRREQ = 89,
+ ESOCKTNOSUPPORT = 94, ENETUNREACH = 101, ENETRESET = 102,
+ ECONNRESET = 104, ETIMEDOUT = 110, ECONNREFUSED, EHOSTDOWN, EHOSTUNREACH,
+ ENOEXECBIT = 126, EBADREQ = 400, ENOAUTH, EMUSTPAY, EFORBIDDEN, ENOTFOUND,
+ ETIMEOUT = 408, ESERVERE = 500, EHTTPNOSUP = 505 };
+
+ ProgramNewsSource(const NewsSourceBase::Data &, ConfigIface *);
+ virtual ~ProgramNewsSource();
+
+ public slots:
+ virtual void retrieveNews();
+
+ protected slots:
+ void slotGotProgramOutput(KProcess *, char *, int);
+ void slotProgramExited(KProcess *);
+
+ private:
+ static QString errorMessage(const ErrorCode);
+
+ KProcess *m_program;
+ QBuffer *m_programOutput;
+};
+
+#endif // NEWSENGINE_H
diff --git a/knewsticker/common/newsiconmgr.cpp b/knewsticker/common/newsiconmgr.cpp
new file mode 100644
index 00000000..69617490
--- /dev/null
+++ b/knewsticker/common/newsiconmgr.cpp
@@ -0,0 +1,159 @@
+/*
+ * newsiconmgr.cpp
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "newsiconmgr.h"
+
+#include <dcopclient.h>
+
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+
+#include <qbuffer.h>
+#include <qfile.h>
+#include <qimage.h>
+
+struct KIODownload
+{
+ KURL url;
+ QByteArray data;
+ QIODevice::Offset dataOffset;
+};
+
+NewsIconMgr *NewsIconMgr::m_instance = 0;
+
+NewsIconMgr *NewsIconMgr::self()
+{
+ if (!m_instance)
+ m_instance = new NewsIconMgr();
+
+ return m_instance;
+}
+
+NewsIconMgr::NewsIconMgr(QObject *parent, const char *name)
+ : QObject(parent, name), DCOPObject("NewsIconMgr"),
+ m_stdIcon(SmallIcon(QString::fromLatin1("news")))
+{
+ connectDCOPSignal("kded",
+ "favicons", "iconChanged(bool, QString, QString)",
+ "slotGotIcon(bool, QString, QString)",
+ false);
+}
+
+NewsIconMgr::~NewsIconMgr()
+{
+ delete m_instance;
+}
+
+void NewsIconMgr::getIcon(const KURL &url)
+{
+ if (url.isEmpty()) {
+ emit gotIcon(url, m_stdIcon);
+ return;
+ }
+
+ if (url.isLocalFile()) {
+ if (QFile::exists(url.encodedPathAndQuery())) {
+ QPixmap icon(url.encodedPathAndQuery());
+ if (!icon.isNull()) {
+ if (icon.size() != QSize(16, 16)) {
+ if (!icon.convertFromImage(icon.convertToImage().smoothScale(16, 16, QImage::ScaleMin))) {
+ emit gotIcon(url, m_stdIcon);
+ return;
+ }
+ }
+ emit gotIcon(url, icon);
+ return;
+ }
+ }
+ emit gotIcon(url, m_stdIcon);
+ return;
+ }
+
+ if (url.encodedPathAndQuery() == "/favicon.ico") {
+ if (favicon(url).isNull()) {
+ QByteArray data;
+ QDataStream ds(data, IO_WriteOnly);
+ ds << url;
+ kapp->dcopClient()->send("kded", "favicons", "downloadHostIcon(KURL)", data);
+ } else {
+ emit gotIcon(url, QPixmap(KGlobal::dirs()->findResource("cache",
+ QString::fromLatin1("favicons/%1.png").arg(url.host()))));
+ }
+ } else {
+ KIO::Job *job = KIO::get(url, true, false);
+ connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *)));
+
+ KIODownload download;
+ download.url = url;
+ download.dataOffset = 0;
+ m_kioDownload.insert(job, download);
+ }
+}
+
+bool NewsIconMgr::isStdIcon(const QPixmap &pixmap) const
+{
+ if (!pixmap.isNull())
+ return pixmap.convertToImage() == m_stdIcon.convertToImage();
+ else
+ return false;
+}
+
+void NewsIconMgr::slotData(KIO::Job *job, const QByteArray &data)
+{
+ QBuffer buf(m_kioDownload[job].data);
+ buf.open(IO_WriteOnly);
+ buf.at(m_kioDownload[job].dataOffset);
+ buf.writeBlock(data);
+ m_kioDownload[job].dataOffset = buf.at();
+}
+
+void NewsIconMgr::slotResult(KIO::Job *job)
+{
+ emit gotIcon(m_kioDownload[job].url, QPixmap(m_kioDownload[job].data));
+ m_kioDownload.remove(job);
+}
+
+void NewsIconMgr::slotGotIcon(bool isHost, QString hostOrURL, QString iconName)
+{
+ KURL url = KURL(hostOrURL);
+ if (!isHost)
+ url.setProtocol(QString::fromLatin1("http"));
+
+ if (iconName.isNull())
+ emit gotIcon(url, m_stdIcon);
+ else
+ emit gotIcon(url, QPixmap(KGlobal::dirs()->findResource("cache",
+ QString::fromLatin1("favicons/%1.png").arg(url.host()))));
+}
+
+QString NewsIconMgr::favicon(const KURL &url) const
+{
+ QByteArray data, reply;
+ QCString replyType;
+ QDataStream ds(data, IO_WriteOnly);
+
+ ds << url;
+
+ kapp->dcopClient()->call("kded", "favicons", "iconForURL(KURL)", data, replyType, reply);
+
+ if (replyType == "QString") {
+ QDataStream replyStream(reply, IO_ReadOnly);
+ QString result;
+ replyStream >> result;
+ return result;
+ }
+
+ return QString::null;
+}
+
+#include "newsiconmgr.moc"
diff --git a/knewsticker/common/newsiconmgr.h b/knewsticker/common/newsiconmgr.h
new file mode 100644
index 00000000..e4c6b657
--- /dev/null
+++ b/knewsticker/common/newsiconmgr.h
@@ -0,0 +1,60 @@
+/*
+ * newsiconmgr.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef NEWSICONMGR_H
+#define NEWSICONMGR_H
+
+#include <qobject.h>
+#include <qpixmap.h>
+
+#include <dcopobject.h>
+#include <kio/job.h>
+#include <kurl.h>
+
+class DCOPClient;
+
+struct KIODownload;
+typedef QMap<KIO::Job *, KIODownload> KIODownloadMap;
+
+class NewsIconMgr : public QObject, public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+ public:
+ static NewsIconMgr *self();
+
+ void getIcon(const KURL &);
+ bool isStdIcon(const QPixmap &) const;
+
+ k_dcop:
+ void slotGotIcon(bool, QString, QString);
+
+ signals:
+ void gotIcon(const KURL &, const QPixmap &);
+
+ protected:
+ NewsIconMgr(QObject * = 0L, const char * = 0L);
+ ~NewsIconMgr();
+
+ private slots:
+ void slotData(KIO::Job *, const QByteArray &);
+ void slotResult(KIO::Job *);
+
+ private:
+ QString favicon(const KURL &) const;
+
+ QPixmap m_stdIcon;
+ KIODownloadMap m_kioDownload;
+
+ static NewsIconMgr *m_instance;
+};
+
+#endif // NEWSICONMGR_H
diff --git a/knewsticker/common/xmlnewsaccess.cpp b/knewsticker/common/xmlnewsaccess.cpp
new file mode 100644
index 00000000..1c892e8e
--- /dev/null
+++ b/knewsticker/common/xmlnewsaccess.cpp
@@ -0,0 +1,132 @@
+/*
+ * xmlnewsaccess.cpp
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "xmlnewsaccess.h"
+
+#include <kcharsets.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include <qbuffer.h>
+#include <qdom.h>
+#include <qregexp.h>
+
+XMLNewsArticle::XMLNewsArticle(const QString &headline, const KURL &address)
+ : m_headline(headline),
+ m_address(address)
+{
+}
+
+XMLNewsArticle &XMLNewsArticle::operator=(const XMLNewsArticle &other)
+{
+ m_headline = other.m_headline;
+ m_address = other.m_address;
+ return *this;
+}
+
+bool XMLNewsArticle::operator==(const XMLNewsArticle &a)
+{
+ return m_headline == a.headline() && m_address == a.address();
+}
+
+XMLNewsSource::XMLNewsSource() : QObject(),
+ m_name(QString::null),
+ m_link(QString::null),
+ m_description(QString::null),
+ m_downloadData(0)
+{
+}
+
+XMLNewsSource::~XMLNewsSource()
+{
+ delete m_downloadData; // Might exist if we are in the middle of a download
+}
+
+void XMLNewsSource::loadFrom(const KURL &url)
+{
+ if ( m_downloadData != 0 ) {
+ kdDebug( 5005 ) << "XMLNewsSource::loadFrom(): Busy, ignoring load "
+ "request for " << url << endl;
+ return;
+ }
+ m_downloadData = new QBuffer;
+ m_downloadData->open(IO_WriteOnly);
+
+ KIO::Job *job = KIO::get(url, false, false);
+ job->addMetaData(QString::fromLatin1("UserAgent"),
+ QString::fromLatin1("KNewsTicker v0.2"));
+ connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *)));
+}
+
+void XMLNewsSource::slotData(KIO::Job *, const QByteArray &data)
+{
+ m_downloadData->writeBlock(data.data(), data.size());
+}
+
+void XMLNewsSource::slotResult(KIO::Job *job)
+{
+ kdDebug(5005) << "XMLNewsSource::slotResult(): Finished downloading data (" << job->error() << ")." << endl;
+ processData(m_downloadData->buffer(), !job->error());
+ delete m_downloadData;
+ m_downloadData = 0;
+}
+
+void XMLNewsSource::processData(const QByteArray &data, bool okSoFar)
+{
+ bool validContent = okSoFar;
+ kdDebug(5005) << "XMLNewsSource::processData(): validContent = " << validContent << endl;
+
+ if (okSoFar) {
+ /*
+ * Some servers prepend whitespace before the <?xml...?> declaration.
+ * Since QDom doesn't like that we strip this first.
+ */
+ QDomDocument domDoc;
+
+ const char *charData = data.data();
+ int len = data.count();
+
+ while (len && (*charData == ' ' || *charData == '\n' || *charData == '\t' || *charData == '\r') ) {
+ len--;
+ charData++;
+ }
+
+ QByteArray tmpData;
+ tmpData.setRawData(charData, len);
+
+ if (validContent = domDoc.setContent(tmpData)) {
+ QDomNode channelNode = domDoc.documentElement().namedItem(QString::fromLatin1("channel"));
+
+ m_name = channelNode.namedItem(QString::fromLatin1("title")).toElement().text().simplifyWhiteSpace();
+ kdDebug(5005) << "XMLNewsSource::processData(): Successfully updated " << m_name << endl;
+ m_link = channelNode.namedItem(QString::fromLatin1("link")).toElement().text().simplifyWhiteSpace();
+ m_description = channelNode.namedItem(QString::fromLatin1("description")).toElement().text().simplifyWhiteSpace();
+
+ QDomNodeList items = domDoc.elementsByTagName(QString::fromLatin1("item"));
+ m_articles.clear();
+ QDomNode itemNode;
+ QString headline, address;
+ for (unsigned int i = 0; i < items.count(); i++) {
+ itemNode = items.item(i);
+ headline = KCharsets::resolveEntities(itemNode.namedItem(QString::fromLatin1("title")).toElement().text().simplifyWhiteSpace());
+ address = KCharsets::resolveEntities(itemNode.namedItem(QString::fromLatin1("link")).toElement().text().simplifyWhiteSpace());
+ m_articles.append(XMLNewsArticle(headline, KURL( address )));
+ }
+ }
+ kdDebug(5005) << "XMLNewsSource::processData(): validContent = " << validContent << endl;
+ tmpData.resetRawData(charData, len);
+ }
+
+ emit loadComplete(this, validContent);
+}
+
+#include "xmlnewsaccess.moc"
diff --git a/knewsticker/common/xmlnewsaccess.h b/knewsticker/common/xmlnewsaccess.h
new file mode 100644
index 00000000..91cbf77f
--- /dev/null
+++ b/knewsticker/common/xmlnewsaccess.h
@@ -0,0 +1,86 @@
+/*
+ * xmlnewsaccess.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef XMLNEWSACCESS_H
+#define XMLNEWSACCESS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qvaluelist.h>
+#include <qobject.h>
+
+#include <kio/job.h>
+#include <kurl.h>
+
+class QBuffer;
+class QDomDocument;
+class XMLNewsSource;
+
+class XMLNewsArticle
+{
+ public:
+ typedef QValueList<XMLNewsArticle> List;
+
+ XMLNewsArticle() {}
+ XMLNewsArticle(const QString &headline, const KURL &addresss);
+ XMLNewsArticle(const XMLNewsArticle &other) { (*this) = other; }
+ XMLNewsArticle &operator=(const XMLNewsArticle &other);
+
+ QString headline() const { return m_headline; }
+ void setHeadline(const QString &headline) { m_headline = headline; }
+
+ KURL address() const { return m_address; }
+ void setAddress(const KURL &address) { m_address = address; }
+
+ bool operator== (const XMLNewsArticle &a);
+ bool operator!= (const XMLNewsArticle &a) { return !operator==(a); }
+
+ private:
+ QString m_headline;
+ KURL m_address;
+};
+
+class XMLNewsSource : public QObject
+{
+ Q_OBJECT
+
+ public:
+ XMLNewsSource();
+ virtual ~XMLNewsSource();
+
+ void loadFrom(const KURL &);
+
+ virtual QString newsSourceName() const { return m_name; }
+ virtual QString link() const { return m_link; }
+ virtual QString description() const { return m_description; }
+ const XMLNewsArticle::List &articles() const { return m_articles; }
+
+ signals:
+ void loadComplete(XMLNewsSource *, bool);
+
+ protected:
+ void processData(const QByteArray &, bool = true);
+
+ QString m_name;
+ QString m_link;
+ QString m_description;
+
+ private slots:
+ void slotData(KIO::Job *, const QByteArray &);
+ void slotResult(KIO::Job *);
+
+ private:
+ XMLNewsArticle::List m_articles;
+ QBuffer *m_downloadData;
+};
+
+#endif // XMLNEWSACCESS_H
diff --git a/knewsticker/eventsrc b/knewsticker/eventsrc
new file mode 100644
index 00000000..e3fdee2f
--- /dev/null
+++ b/knewsticker/eventsrc
@@ -0,0 +1,335 @@
+[!Global!]
+IconName=knewsticker
+Comment=News Ticker
+Comment[af]=Nuus Tikker
+Comment[ar]=تجديد الأخبار
+Comment[az]=Xəbər Gözləyici
+Comment[be]=ÐглÑдальнік навінаў
+Comment[bg]=Четец на новини
+Comment[bn]=সংবাদ টিকার
+Comment[br]=Kliker keleier
+Comment[bs]=Traka s vijestima
+Comment[ca]=Teletip de notícies
+Comment[cs]=Novinky
+Comment[cy]=Ticer Newyddion
+Comment[da]=Nyhedstelegraf
+Comment[de]=Newsticker
+Comment[el]=ΠÏοβολή ειδήσεων
+Comment[eo]=Novaĵprezentilo
+Comment[es]=Teletipo de noticias
+Comment[et]=Uudiste jälgija
+Comment[eu]=Berri markatzailea
+Comment[fa]=تیکر اخبار
+Comment[fi]=Uutisnäyttäjä
+Comment[fr]=Téléscripteur
+Comment[gl]=Teletipo
+Comment[he]=צג חדשות
+Comment[hi]=नà¥à¤¯à¥‚ज टिकर
+Comment[hr]=Ticker sa novostima
+Comment[hu]=Hírmegjelenítő
+Comment[id]=Ticker Berita
+Comment[is]=Fréttastrimill
+Comment[it]=Ticker notizie
+Comment[ja]=ニュースティッカー
+Comment[ka]=სიáƒáƒ®áƒšáƒ”ების მიმღები
+Comment[kk]=Жаңалық таÑпаÑÑ‹
+Comment[km]=កម្មវិធី​ទទួល​ពáŸážáŸŒáž˜áž¶áž“
+Comment[ko]=ìƒˆì†Œì‹ í‘œì‹œê¸°
+Comment[lt]=Naujienų pranešėjas
+Comment[lv]=Ziņu TIkkers
+Comment[mk]=Лента Ñо веÑти
+Comment[mn]=ÐœÑдÑÑний ДÑлгÑц
+Comment[ms]=Detik Berita
+Comment[mt]=News ticker
+Comment[nb]=Nyhetstelegraf
+Comment[nds]=Narichten-Ticker
+Comment[ne]=समाचार टिकर
+Comment[nl]=Nieuwslezer
+Comment[nn]=Nyhendetelegraf
+Comment[nso]=Seswai sa Ditaba
+Comment[pt]=Notícias
+Comment[pt_BR]=Mini-aplicativo de notícias
+Comment[ro]=Åžtiri Internet
+Comment[ru]=Монитор новоÑтей
+Comment[se]=Ođastelegráfa
+Comment[sk]=SledovaÄ správ
+Comment[sl]=Prikazovalnik novic
+Comment[sr]=Пратилац веÑти
+Comment[sr@Latn]=Pratilac vesti
+Comment[sv]=Nyhetsövervakare
+Comment[ta]=செயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à¯
+Comment[tg]=Дидабони Ðхборот
+Comment[th]=ตั๋วข่าว
+Comment[tr]=Haber Gözlemcisi
+Comment[uk]=Стрічка новин
+Comment[ven]=Musengulusi wa Mafhungo
+Comment[zh_CN]=新闻简报
+Comment[zh_HK]=æ–°èžå¿«å ±
+Comment[zh_TW]=æ–°èžç°¡å ±
+Comment[zu]=Umlungiseleli Wezindaba
+
+[NewNews]
+Name=New News Available
+Name[af]=Nuwe Nuus Beskikbaar
+Name[ar]=توجد أخبار جديدة
+Name[az]=Mövcud Yeni Xəbərlər
+Name[be]=ÐÑць навіны
+Name[bg]=ПриÑтигнаха нови новини
+Name[bn]=নতà§à¦¨ সংবাদ পাওয়া যাচà§à¦›à§‡
+Name[br]=Keleier nevez da gaout
+Name[bs]=Nove vijesti su dostupne
+Name[ca]=Noves notícies disponibles
+Name[cs]=Jsou dostupné nové zprávy
+Name[cy]=Newyddion Newydd ar Gael
+Name[da]=Nye nyheder tilgængelige
+Name[de]=Neu eingetroffene Nachrichten
+Name[el]=ΚαινοÏÏιες ειδήσεις διαθέσιμες
+Name[eo]=Novaj diskutmesaÄoj
+Name[es]=Nuevas noticias disponibles
+Name[et]=Uus uudis
+Name[eu]=Azken berriak eskuragarri
+Name[fa]=خبرهای جدید در دسترس
+Name[fi]=Uutisia saatavilla
+Name[fr]=Des nouvelles sont arrivées
+Name[ga]=Nuacht Nua ar Fáil
+Name[gl]=Novas noticias dispoñibles
+Name[he]=ישנן חדשות זמינות
+Name[hi]=नया समाचार उपलबà¥à¤§
+Name[hr]=Novosti su raspoložive
+Name[hu]=Új hír érkezett
+Name[id]=Berita baru tersedia
+Name[is]=Nýjar fréttir komnar
+Name[it]=Nuove notizie disponibili
+Name[ja]=æ–°ç€ãƒ‹ãƒ¥ãƒ¼ã‚¹
+Name[ka]=áƒáƒ®áƒáƒšáƒ˜ სიáƒáƒ®áƒšáƒ”ები ხელმისáƒáƒ¬áƒ•áƒ“áƒáƒ›áƒ˜áƒ
+Name[kk]=Жаңалықтар бар
+Name[km]=មាន​ពáŸážáŸŒáž˜áž¶áž“​ážáŸ’មី
+Name[ko]=새로운 소ì‹ì´ 있습니다
+Name[lt]=Yra šviežių naujienų
+Name[lv]=Jaunas Ziņas Pieejamas
+Name[mk]=Има нови веÑти
+Name[mn]=Ð¨Ð¸Ð½Ñ ÐœÑдÑÑ Ð³Ð°Ñ€Ð»Ð°Ð°
+Name[ms]=Ada Berita Mutakhir
+Name[mt]=Aħbarijiet ġodda
+Name[nb]=Nye artikler tilgjengelige
+Name[nds]=Nieg Narichten verföögbor
+Name[ne]=नयाठसमाचार उपलबà¥à¤§ छ
+Name[nl]=Nieuw nieuws beschikbaar
+Name[nn]=Nyhende tilgjengeleg
+Name[nso]=Ditaba tse Diswa di Gona
+Name[pa]=ਨਵੀਆਂ ਖ਼ਬਰਾਂ ਉਪਲੱਬਧ ਹਨ
+Name[pl]=Dostępne nowe wiadomości
+Name[pt]=Estão novas notícias disponíveis
+Name[pt_BR]=Novas notícias
+Name[ro]=Nu există ştiri noi
+Name[ru]=ЕÑÑ‚ÑŒ новоÑти
+Name[se]=Ođđa ođđasat olamuttus
+Name[sk]=Nové správy
+Name[sl]=Na voljo so sveže novice
+Name[sr]=ДоÑтупне Ñу нове веÑти
+Name[sr@Latn]=Dostupne su nove vesti
+Name[sv]=Det har kommit nya nyheter
+Name[ta]=பà¯à®¤à®¿à®¯ செயà¯à®¤à®¿à®•à®³à¯ உளà¯à®³à®©
+Name[tg]=Ðхборотҳои Ðав ДаÑтраÑанд
+Name[th]=ข่าวใหม่ที่มี
+Name[tr]=Mevcut Yeni Haberler
+Name[uk]=ОÑтанні новини
+Name[ven]=Mafhungo maswa are hone
+Name[xh]=Iindaba Ezintsha Ezikhoyo
+Name[zh_CN]=有新的新闻
+Name[zh_HK]=有新的新èž
+Name[zh_TW]=有新的新èž
+Name[zu]=Izindaba Ezintsha Ziyatholakala manje
+Comment=There is new news available
+Comment[af]=Daar is nuwe nuus beskikbaar
+Comment[ar]=هناك أخبار جديدة
+Comment[az]=Yeni xəbərlər mövcuddur
+Comment[be]=ÐÑць навіны
+Comment[bg]=ПриÑтигнаха нови новини
+Comment[bn]=নতà§à¦¨ সংবাদ পাওয়া যাচà§à¦›à§‡
+Comment[bs]=Sada ima novih vijesti
+Comment[ca]=Hi ha noves notícies disponibles
+Comment[cs]=Jsou dostupné nové zprávy
+Comment[cy]=Mae newyddion newydd ar gael
+Comment[da]=Der er nye nyheder tilgængelige
+Comment[de]=Es sind neue Nachrichten eingetroffen
+Comment[el]=ΥπάÏχουν καινοÏÏιες ειδήσεις διαθέσιμες
+Comment[eo]=Alvenis novaj diskutmesaÄoj
+Comment[es]=Hay nuevas noticias disponibles
+Comment[et]=Uusi uudiseid pole
+Comment[eu]=Azken berriak eskuragarri
+Comment[fa]=اخبار جدید در دسترس است
+Comment[fi]=Uutisia saatavilla
+Comment[fr]=De nouvelles informations sont arrivées.
+Comment[ga]=Tá nuacht nua ar fáil
+Comment[gl]=Hai novas noticias dispoñibles
+Comment[he]=ישנן חדשות זמינות
+Comment[hi]=वहाठनया समाचार उपलबà¥à¤§ है
+Comment[hr]=Postoje nove novosti
+Comment[hu]=Új hír érkezett.
+Comment[id]=Ada berita baru tersedia
+Comment[is]=Nýjar fréttir hafa borist
+Comment[it]=C'è una nuova notizia disponibile
+Comment[ja]=æ–°ã—ã„ニュースãŒå±Šãã¾ã—ãŸ
+Comment[ka]=სიáƒáƒ®áƒšáƒ”ები áƒáƒ  áƒáƒ áƒ˜áƒ¡ ხელმისáƒáƒ¬áƒ•áƒ“áƒáƒ›áƒ˜
+Comment[kk]=Жаңалықтар бар
+Comment[km]=មាន​ពáŸážáŸŒáž˜áž¶áž“​ážáŸ’មី​ហើយ
+Comment[ko]=새로운 소ì‹ì´ 있습니다
+Comment[lt]=Yra šviežių naujienų
+Comment[lv]=Šeit ir jaunas ziņas pieejamas
+Comment[mk]=Има нови веÑти
+Comment[mn]=ÐœÑдÑÑ Ð³Ð°Ñ€Ð»Ð°Ð°
+Comment[ms]=Berita mutakhir hari ini
+Comment[mt]=Hemm aħbarijiet ġodda
+Comment[nb]=Det er nye artikler tilgjengelig
+Comment[nds]=Dat gifft niege Narichten
+Comment[ne]=तà¥à¤¯à¤¹à¤¾à¤ नयाठसमाचार उपलबà¥à¤§ छ
+Comment[nl]=Er is nieuw nieuws beschikbaar
+Comment[nn]=Nyhende tilgjengeleg
+Comment[nso]=Gona le ditaba tse diswa tseo di lego gona
+Comment[pl]=Jest nowa wiadomość
+Comment[pt]=Existem novas notícias disponíveis
+Comment[pt_BR]=Há novas notícias
+Comment[ro]=Nu există ştiri noi
+Comment[ru]=ЕÑÑ‚ÑŒ новоÑти
+Comment[se]=Leat varas ođđasat olamuttus
+Comment[sk]=Žiadne nové správy
+Comment[sl]=Na voljo so sveže novice
+Comment[sr]=Има нових веÑти
+Comment[sr@Latn]=Ima novih vesti
+Comment[sv]=Det har kommit nya nyheter
+Comment[ta]=பà¯à®¤à®¿à®¯ செயà¯à®¤à®¿à®•à®³à¯ உளà¯à®³à®©
+Comment[tg]=Ðхборотҳои нав даÑтраÑанд
+Comment[th]=นี่เป็นข่าวใหม่ที่มี
+Comment[tr]=Yeni haberler var
+Comment[uk]=Ðадійшли Ñвіжі новини
+Comment[ven]=Huna mafhungo maswa are hone
+Comment[xh]=Kukho iindaba ezintsha ezikhoyo
+Comment[zh_CN]=有新的新闻
+Comment[zh_HK]=有新的新èž
+Comment[zh_TW]=有新的新èž
+Comment[zu]=Kukhona izindaba ezintsha ezitholakalayo
+default_sound=
+default_presentation=1
+
+[InvalidRDF]
+Name=Invalid RDF file
+Name[af]=Ongeldige Rdf lêer
+Name[ar]=مل٠RDF غير صالح
+Name[az]=Xətalı RDF faylı
+Name[be]=ÐÑправільны файл RDF
+Name[bg]=Ðевалиден файл RDF
+Name[bn]=অবৈধ আর-ডি-à¦à¦« ফাইল
+Name[br]=N'eo ket mat ar restr RDF
+Name[bs]=Neispravna RDF datoteka
+Name[ca]=Fitxer RDF no vàlid
+Name[cs]=Neplatný RDF soubor
+Name[cy]=Ffeil RDF annilys
+Name[da]=Ugyldig RDF-fil
+Name[de]=Ungültige RDF-Datei
+Name[el]=Μη έγκυÏο αÏχείο RDF
+Name[eo]=Nevalida RDF-dosierojn
+Name[es]=Archivo RDF no válido
+Name[et]=Vigane RDF-fail
+Name[eu]=RDF fitxategia baliogabea da
+Name[fa]=پروندۀ RDF نامعتبر
+Name[fi]=Virheellinen RDF-tiedosto
+Name[fr]=Fichier RDF non valable
+Name[ga]=Comhad RDF Neamhbhailí
+Name[gl]=Fichero RDF inválido
+Name[he]=קובץ RDF ×œ× ×ª×§×£
+Name[hi]=अवैध RDF फ़ाइल
+Name[hr]=Nevažeća RDF datoteka
+Name[hu]=Érvénytelen RDF fájl
+Name[id]=Berkas RDF rusak
+Name[is]=Ógild RDF skrá
+Name[it]=File RDF non valido
+Name[ja]=ä¸æ­£ãª RDF ファイル
+Name[ka]=áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ RDF ფáƒáƒ˜áƒšáƒ˜
+Name[kk]=ЖарамÑыз RDF файл
+Name[km]=ឯកសារ RDF មិនážáŸ’រឹមážáŸ’រូវ​
+Name[ko]=ìž˜ëª»ëœ RDF 파ì¼
+Name[lt]=Neteisinga RDF byla
+Name[lv]=Nepareizs RDF fails
+Name[mk]=Ðевалидна RDF-датотека
+Name[mn]=Буруу RDF файл
+Name[ms]=Fail RDF tidak sah
+Name[mt]=Fajl RDF invalidu
+Name[nb]=Ugyldig RDF-fil
+Name[nds]=Leeg RDF-Datei
+Name[ne]=अवैध आर डी à¤à¤« फाइल
+Name[nl]=Ongeldig RDF-bestand
+Name[nn]=Ugyldig RDF-fil
+Name[nso]=Faele ya RDF yeo esa dumelelwago
+Name[pl]=Nieprawidłowy plik RDF
+Name[pt]=Ficheiro RDF inválido
+Name[pt_BR]=Arquivo RDF inválido
+Name[ro]=FiÅŸier RDF eronat
+Name[ru]=Ðеверный RDF файл
+Name[se]=Gustomeahttun RDF-fiila
+Name[sk]=Neplatný súbor RDF
+Name[sl]=Neveljavna datoteka PDF
+Name[sr]=Погрешан RDF фајл
+Name[sr@Latn]=Pogrešan RDF fajl
+Name[sv]=Ogiltig RDF-fil
+Name[ta]=வலிதறà¯à®± RDF ஆவணமà¯
+Name[tg]=RDF файли нодуруÑÑ‚
+Name[th]=à¹à¸Ÿà¹‰à¸¡ RDF ไม่ถูà¸à¸•à¹‰à¸­à¸‡
+Name[tr]=Hatalı RDF dosyası
+Name[uk]=Ðевірний файл RDF
+Name[ven]=Faela ya RDF isina tshithu
+Name[wa]=Fitchî RDF nén valide
+Name[xh]=Ifayile ye RDF engasebenziyo
+Name[zh_CN]=无效的 RDF 文件
+Name[zh_HK]=無效的 RDF 檔案
+Name[zh_TW]=無效的 RDF 檔案
+Name[zu]=Ifayela Engasebenziyo ye RDF
+Comment=The downloaded RDF file could not be parsed
+Comment[be]=Ðемагчыма апрацаваць ÑцÑгнуты файл RDF
+Comment[bg]=ИзтеглениÑÑ‚ файл RDF не може да бъде анализиран
+Comment[bn]=ডাউনলোডকৃত আর-ডি-à¦à¦« ফাইল পারà§à¦¸ করতে পারল না
+Comment[bs]=Ne mogu protumaÄiti pribavljenu RDF datoteku
+Comment[ca]=El fitxer RDF descarregat no s'ha pogut analitzar
+Comment[cs]=Analýza stáhnutého RDF souboru selhala
+Comment[da]=Den hentede RDF-fil kunne ikke fortolkes
+Comment[de]=Die heruntergeladene RDF-Datei kann nicht eingelesen werden
+Comment[el]=Το αÏχείο που λήφθηκε RDF ήταν αδÏνατο να αναλυθεί
+Comment[eo]=La elÅutita RDF-dosiero ne estis analizebla
+Comment[es]=El archivo RDF descargado no se pudo analizar
+Comment[et]=Allalaaditud RDF-faili pole võimalik parsida
+Comment[eu]=Deskargatutako RDF fitxategia ezin izan da aztertu
+Comment[fa]=پروندۀ بارگیری‌شدۀ RDF نتوانست تجزیه شود
+Comment[fi]=Haettua RDF-tiedostoa ei saatu jäsennettyä
+Comment[fr]=Le fichier RDF téléchargé n'a pas pu être analysé
+Comment[ga]=Ní féidir an comhad RDF íosluchtaithe a pharsáil
+Comment[gl]=O ficheiro RDF descargado non se puido interpretar
+Comment[he]=×ין ×פשרות לנתח ×ת קובץ ×”-RDF שהורד
+Comment[hu]=A letöltött RDF-fájlt nem sikerült feldolgozni
+Comment[is]=Gat ekki þáttað RDF skrána sem var sótt
+Comment[it]=Il file RDF scaricato non può essere interpretato
+Comment[ja]=ダウンロードã•ã‚ŒãŸ RDF ファイルã¯è§£èª­ã§ãã¾ã›ã‚“
+Comment[ka]=ჩáƒáƒ›áƒáƒ¥áƒáƒ©áƒ£áƒšáƒ˜ RDF ფáƒáƒ˜áƒšáƒ˜áƒ¡ გáƒáƒáƒœáƒáƒšáƒ˜áƒ–ებრვერ გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლდáƒ
+Comment[kk]=ТүÑіріп алынған RDF талдауға келмеді
+Comment[km]=មិន​អាច​ញែក​ឯកសារ RDF ដែលបាន​ទាញយក​បាន​ឡើយ
+Comment[lt]=Nepavyko apdoroti atsisiųstos RDF bylos
+Comment[nb]=Kunne ikke tolke RDF-fila som ble lastet ned
+Comment[nds]=De daallaadt RDF-Datei lett sik nich inlesen
+Comment[ne]=डाउनलोड गरिà¤à¤•à¥‹ आर डी à¤à¤« फाइल पद वरà¥à¤£à¤¨ गरà¥à¤¨ सकेन
+Comment[nl]=Het gedownloade RDF-bestand kon niet worden ontleed
+Comment[nn]=Klarte ikkje tolka den nedlasta RDF-fila
+Comment[pl]=Ściągnięty plik RDF ma złą składnię
+Comment[pt]=Não foi possível processar o ficheiro RDF transferido
+Comment[pt_BR]=O arquivo RDF obtido não pôde ser interpretado
+Comment[ro]=FiÅŸierul RDF transferat nu poate fi analizat
+Comment[ru]=Ðе удалоÑÑŒ разобрать загруженный RDF-файл
+Comment[sk]=Stiahnutý RDF súbor nie je možné analyzovať
+Comment[sl]=Prenesene datoteke RDF ni moÄ razÄleniti
+Comment[sr]=Преузети RDF фајл не може да Ñе рашчлани
+Comment[sr@Latn]=Preuzeti RDF fajl ne može da se raÅ¡Älani
+Comment[sv]=Den nerladdade RDF-filen kunde inte tolkas
+Comment[tr]=İndirilen RDF dosyası ayrıştırılamadı
+Comment[uk]=Ðе вдалоÑÑŒ проаналізувати звантажений файл RDF
+Comment[zh_CN]=下载的 RDF 无法分æž
+Comment[zh_HK]=下載的 RDF 檔案無法解æž
+Comment[zh_TW]=下載的 RDF 無法分æž
+default_presentation=0
diff --git a/knewsticker/hi16-app-knewsticker.png b/knewsticker/hi16-app-knewsticker.png
new file mode 100644
index 00000000..a450d096
--- /dev/null
+++ b/knewsticker/hi16-app-knewsticker.png
Binary files differ
diff --git a/knewsticker/hi32-app-knewsticker.png b/knewsticker/hi32-app-knewsticker.png
new file mode 100644
index 00000000..36b0f702
--- /dev/null
+++ b/knewsticker/hi32-app-knewsticker.png
Binary files differ
diff --git a/knewsticker/hi48-app-knewsticker.png b/knewsticker/hi48-app-knewsticker.png
new file mode 100644
index 00000000..447b6813
--- /dev/null
+++ b/knewsticker/hi48-app-knewsticker.png
Binary files differ
diff --git a/knewsticker/knewsticker-standalone.desktop b/knewsticker/knewsticker-standalone.desktop
new file mode 100644
index 00000000..d31a44b1
--- /dev/null
+++ b/knewsticker/knewsticker-standalone.desktop
@@ -0,0 +1,95 @@
+[Desktop Entry]
+Name=KNewsTicker
+Name[af]=K-nuustikker
+Name[ar]=مجدد أخبار كيدي
+Name[bn]=কে-নিউজ-টিকার
+Name[cy]=KTicerNewyddion
+Name[eo]=Novaĵprezentilo
+Name[he]=KNewsTicker - צג חדשות
+Name[hi]=के-नà¥à¤¯à¥‚ज-टिकर
+Name[lv]=KZiņuTikkers
+Name[nb]=Nyhetstelegraf
+Name[ne]=केडीई नà¥à¤¯à¥‚ज टिकर
+Name[nso]=Seswai sa KDitaba
+Name[pt_BR]=Mostrador de Notícias
+Name[sv]=Knewsticker
+Name[ta]=கேசெயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à¯
+Name[tg]=KДидабони Ðхборот
+Name[th]=ตั๋วข่าว - K
+Name[tr]=KDE Haber Gözlemcisi
+Name[ven]=Musengulusi wa mafhungo a K
+Name[zu]=Umlungiseleli Wezindaba ze K
+Type=Application
+Exec=appletproxy knewsticker.desktop
+Icon=knewsticker
+DocPath=knewsticker/index.html
+GenericName=News Ticker
+GenericName[af]=Nuus Tikker
+GenericName[ar]=تجديد الأخبار
+GenericName[az]=Xəbər Gözləyici
+GenericName[be]=ÐглÑдальнік навінаў
+GenericName[bg]=Четец на новини
+GenericName[bn]=সংবাদ টিকার
+GenericName[br]=Kliker keleier
+GenericName[bs]=Traka s vijestima
+GenericName[ca]=Teletip de notícies
+GenericName[cs]=Novinky
+GenericName[cy]=Ticer Newyddion
+GenericName[da]=Nyhedstelegraf
+GenericName[de]=Newsticker
+GenericName[el]=ΠÏοβολή ειδήσεων
+GenericName[eo]=Novaĵprezentilo
+GenericName[es]=Teletipo de noticias
+GenericName[et]=Uudiste jälgija
+GenericName[eu]=Berri markatzailea
+GenericName[fa]=تیکر اخبار
+GenericName[fi]=Uutisnäyttäjä
+GenericName[fr]=Téléscripteur
+GenericName[gl]=Teletipo de noticias
+GenericName[he]=צג חדשות
+GenericName[hi]=नà¥à¤¯à¥‚ज टिकर
+GenericName[hr]=Ticker sa novostima
+GenericName[hu]=RSS hírbejelentő
+GenericName[id]=Ticker Berita
+GenericName[is]=Fréttastrimill
+GenericName[it]=Ticker notizie
+GenericName[ja]=ニュースティッカー
+GenericName[ka]=სიáƒáƒ®áƒšáƒ”ების მიმღები
+GenericName[kk]=Жаңалық таÑпаÑÑ‹
+GenericName[km]=កម្មវិធី​ទទួល​ពáŸážáŸŒáž˜áž¶áž“
+GenericName[ko]=ìƒˆì†Œì‹ í‘œì‹œê¸°
+GenericName[lt]=Naujienų pranešėjas
+GenericName[lv]=Ziņu TIkkers
+GenericName[mk]=Лента Ñо веÑти
+GenericName[mn]=ÐœÑдÑÑний ДÑлгÑц
+GenericName[ms]=Detik Berita
+GenericName[nb]=Nyhetstelegraf
+GenericName[nds]=Narichten-Ticker
+GenericName[ne]=समाचार टिकर
+GenericName[nl]=Nieuws-lichtkrant
+GenericName[nn]=Nyhendetelegraf
+GenericName[nso]=Seswai sa Ditaba
+GenericName[pt]=Notícias
+GenericName[pt_BR]=Mini-aplicativo de notícias
+GenericName[ro]=Åžtiri Internet
+GenericName[ru]=Монитор новоÑтей
+GenericName[se]=Ođastelegráfa
+GenericName[sk]=SledovaÄ správ
+GenericName[sl]=Prikazovalnik novic
+GenericName[sr]=Пратилац веÑти
+GenericName[sr@Latn]=Pratilac vesti
+GenericName[sv]=Nyhetsövervakare
+GenericName[ta]=செயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à¯
+GenericName[tg]=Дидабони Ðхборот
+GenericName[th]=ตั๋วข่าว
+GenericName[tr]=Haber Gözlemcisi
+GenericName[uk]=Стрічка новин
+GenericName[ven]=Musengulusi wa Mafhungo
+GenericName[zh_CN]=新闻播报器
+GenericName[zh_HK]=æ–°èžå¿«å ±
+GenericName[zh_TW]=æ–°èžç°¡å ±
+GenericName[zu]=Umlungiseleli Wezindaba
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Unique
+Categories=Qt;KDE;Network;X-KDE-More;News;
diff --git a/knewsticker/knewsticker.cpp b/knewsticker/knewsticker.cpp
new file mode 100644
index 00000000..6d2f90cb
--- /dev/null
+++ b/knewsticker/knewsticker.cpp
@@ -0,0 +1,546 @@
+/*
+ * knewsticker.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "knewsticker.h"
+#include "newsengine.h"
+#include "newsscroller.h"
+#include "configaccess.h"
+#include "newsiconmgr.h"
+#include "knewstickerconfig.h"
+
+#include <kaboutapplication.h>
+#include <kapplication.h>
+#include <karrowbutton.h>
+#include <kbugreport.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <knotifyclient.h>
+#include <kprocess.h>
+#include <kprotocolmanager.h>
+#include <kstandarddirs.h>
+#include <kstartupinfo.h>
+
+#include <qcursor.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+#include <dcopclient.h>
+
+KNewsTicker::KNewsTicker(const QString &cfgFile, Type t, int actions, QWidget *parent, const char *name)
+ : ConfigIface(), DCOPObject("KNewsTicker"),
+ KPanelApplet(cfgFile, t, actions, parent, name),
+ m_instance(new KInstance("knewsticker")),
+ m_dcopClient(new DCOPClient()),
+ m_cfg(new ConfigAccess(config())),
+ m_newsTimer(new QTimer(this)),
+ m_updateTimer(new QTimer(this)),
+ m_newsIconMgr(NewsIconMgr::self()),
+ m_aboutData(new KAboutData("knewsticker", I18N_NOOP("KNewsTicker"), "v0.2",
+ I18N_NOOP("A news ticker applet."), KAboutData::License_BSD,
+ I18N_NOOP("(c) 2000, 2001 The KNewsTicker developers")))
+{
+ QHBoxLayout *layout = new QHBoxLayout(this);
+
+ m_contextMenu = new KNewsTickerMenu(this);
+ connect(m_contextMenu, SIGNAL(aboutToHide()),
+ SLOT(slotContextMenuAboutToHide()));
+ setCustomMenu(m_contextMenu);
+
+ m_arrowButton = new KArrowButton(this);
+ QToolTip::add(m_arrowButton, i18n("Show menu"));
+ connect(m_arrowButton, SIGNAL(clicked()), SLOT(slotArrowButtonPressed()));
+ m_arrowButton->setFocusPolicy(NoFocus);
+ setupArrowButton();
+ layout->addWidget(m_arrowButton);
+
+ m_scroller = new NewsScroller(this, m_cfg);
+ layout->addWidget(m_scroller);
+
+ m_dcopClient->registerAs("knewsticker", false);
+
+ QToolTip::add(m_scroller, QString::null);
+ connect(m_scroller, SIGNAL(contextMenu()), SLOT(slotOpenContextMenu()));
+
+ connect(m_newsTimer, SIGNAL(timeout()), SLOT(slotUpdateNews()));
+
+ connect(m_updateTimer, SIGNAL(timeout()), SLOT(slotNotifyOfFailures()));
+
+ m_aboutData->addAuthor("Frerich Raabe", I18N_NOOP("Original author"),
+ "raabe@kde.org");
+ m_aboutData->addAuthor("Malte Starostik", I18N_NOOP("Hypertext headlines"
+ " and much more"), "malte@kde.org");
+ m_aboutData->addAuthor("Wilco Greven", I18N_NOOP("Mouse wheel support"),
+ "greven@kde.org");
+ m_aboutData->addAuthor("Adriaan de Groot", I18N_NOOP("Rotated scrolling text"
+ " modes"), "adridg@sci.kun.nl");
+
+ reparseConfig();
+
+ KStartupInfo::appStarted();
+}
+
+KNewsTicker::~KNewsTicker()
+{
+ delete m_cfg;
+ delete m_dcopClient;
+}
+
+int KNewsTicker::heightForWidth(int) const
+{
+ return m_scroller->sizeHint().height() + m_arrowButton->height();
+}
+
+int KNewsTicker::widthForHeight(int) const
+{
+ return m_scroller->sizeHint().width() + m_arrowButton->width();
+}
+
+void KNewsTicker::preferences()
+{
+ KNewsTickerConfig dlg(m_cfg, this);
+ if (dlg.exec() == QDialog::Accepted) {
+ reparseConfig();
+ }
+}
+
+void KNewsTicker::about()
+{
+ KAboutApplication aboutDlg(m_aboutData);
+ aboutDlg.exec();
+}
+
+void KNewsTicker::help()
+{
+ kapp->invokeHelp(QString::null, QString::fromLatin1("knewsticker"));
+}
+
+void KNewsTicker::reportBug()
+{
+ KBugReport bugReport(this, true, m_aboutData);
+ bugReport.exec();
+}
+
+void KNewsTicker::reparseConfig()
+{
+ m_cfg->reparseConfiguration();
+ m_newsSources.clear();
+
+ QStringList newsSources = m_cfg->newsSources();
+ QStringList::ConstIterator it = newsSources.begin();
+ QStringList::ConstIterator end = newsSources.end();
+ for (; it != end; ++it) {
+ NewsSourceBase::Ptr ns = m_cfg->newsSource((*it));
+ if (!ns->data().enabled)
+ continue;
+
+ connect(ns, SIGNAL(newNewsAvailable(const NewsSourceBase::Ptr &, bool)),
+ SLOT(slotNewsSourceUpdated(const NewsSourceBase::Ptr &, bool)));
+ connect(ns, SIGNAL(invalidInput(const NewsSourceBase::Ptr &)),
+ SLOT(slotNewsSourceFailed(const NewsSourceBase::Ptr &)));
+ m_newsSources.append(ns);
+ }
+
+ setOfflineMode(m_cfg->offlineMode());
+ if (!m_cfg->offlineMode())
+ slotUpdateNews();
+}
+
+void KNewsTicker::slotUpdateNews()
+{
+ kdDebug(5005) << "slotUpdateNews()" << endl;
+ m_newNews = false;
+
+ m_updateTimer->start(KProtocolManager::responseTimeout(), true);
+
+ m_failedNewsUpdates.clear();
+ m_pendingNewsUpdates.clear();
+
+ m_scroller->clear();
+
+ NewsSourceBase::List::Iterator it = m_newsSources.begin();
+ NewsSourceBase::List::Iterator end = m_newsSources.end();
+ for (; it != end; ++it) {
+ m_pendingNewsUpdates += (*it)->data().name;
+ (*it)->retrieveNews();
+ (*it)->getIcon();
+ }
+ kdDebug(5005) << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",")
+ << endl;
+}
+
+void KNewsTicker::slotNewsSourceUpdated(const NewsSourceBase::Ptr &ns,
+ bool newNews)
+{
+ kdDebug(5005) << "slotNewsSourceUpdate()" << endl;
+ if (newNews)
+ m_newNews = true;
+
+ if (!ns->articles().isEmpty())
+ if (m_cfg->scrollMostRecentOnly())
+ m_scroller->addHeadline(ns->articles().first());
+ else {
+ Article::List articles = ns->articles();
+ Article::List::ConstIterator artIt = articles.begin();
+ Article::List::ConstIterator artEnd = articles.end();
+ for (; artIt != artEnd; ++artIt)
+ m_scroller->addHeadline(*artIt);
+ }
+
+ m_scroller->reset(true);
+
+ m_pendingNewsUpdates.remove(ns->data().name);
+ kdDebug(5005) << "Updated news source: '" << ns->data().name << "'" << "\n"
+ << "m_pendingNewsUpdates = " << m_pendingNewsUpdates.join(",") << "\n"
+ << "m_failedNewsUpdates = " << m_failedNewsUpdates.join(",")
+ << endl;
+
+ if (!m_pendingNewsUpdates.isEmpty())
+ return;
+
+ m_updateTimer->stop();
+
+ if (!m_failedNewsUpdates.isEmpty())
+ slotNotifyOfFailures();
+
+ if (m_newNews) {
+ KNotifyClient::Instance instance(m_instance);
+ KNotifyClient::event(winId(), QString::fromLatin1("NewNews"));
+ }
+}
+
+void KNewsTicker::slotNewsSourceFailed(const NewsSourceBase::Ptr &ns)
+{
+ m_failedNewsUpdates += ns->newsSourceName();
+ slotNewsSourceUpdated(ns);
+}
+
+void KNewsTicker::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == QMouseEvent::RightButton)
+ slotOpenContextMenu();
+}
+
+void KNewsTicker::slotOpenContextMenu()
+{
+ m_contextMenu->setFullMenu(true);
+ m_contextMenu->exec(QCursor::pos());
+}
+
+void KNewsTicker::slotArrowButtonPressed()
+{
+ QPoint pos(m_arrowButton->mapToGlobal(QPoint(0, 0)));
+ QSize size(m_arrowButton->size());
+
+ if (position() == KPanelApplet::pTop) {
+ pos.setY(pos.y() + size.height() + 2);
+ } else if (position() == KPanelApplet::pBottom) {
+ const int y = pos.y() - m_contextMenu->sizeHint().height() - 2;
+ pos.setY(QMAX(0, y));
+ } else if (position() == KPanelApplet::pLeft ) {
+ pos.setX(pos.x() + size.width() + 2);
+ } else { // position() == KPanelApplet::pRight
+ const int x = pos.x() - m_contextMenu->sizeHint().width() - 2;
+ pos.setX(QMAX(0, x));
+ }
+
+ m_contextMenu->setFullMenu(true);
+ m_contextMenu->exec(pos);
+}
+
+void KNewsTicker::positionChange(Position)
+{
+ delete layout();
+
+ QBoxLayout *layout;
+
+ if (orientation() == Horizontal)
+ layout = new QHBoxLayout(this);
+ else
+ layout = new QVBoxLayout(this);
+
+ if (m_arrowButton) {
+ layout->addWidget(m_arrowButton);
+ setupArrowButton();
+ }
+
+ layout->addWidget(m_scroller);
+}
+
+void KNewsTicker::slotContextMenuAboutToHide()
+{
+ if (m_arrowButton)
+ m_arrowButton->setDown(false);
+}
+
+void KNewsTicker::slotNotifyOfFailures()
+{
+ KNotifyClient::Instance instance(m_instance);
+ QString notification = QString::null;
+
+ if (m_failedNewsUpdates.count() == 1)
+ notification = i18n("<qt>Could not update news site '%1'.<br>"
+ "The supplied resource file is probably invalid or"
+ " broken.</qt>").arg(m_failedNewsUpdates.first());
+ else if (m_failedNewsUpdates.count() > 1 && m_failedNewsUpdates.count() < 8) {
+ notification = i18n("<qt>The following news sites had problems. Their"
+ " resource files are probably invalid or broken.<ul>");
+ QStringList::ConstIterator it = m_failedNewsUpdates.begin();
+ QStringList::ConstIterator end = m_failedNewsUpdates.end();
+ for (; it != end; ++it)
+ notification += QString::fromLatin1("<li>%1</li>").arg(*it);
+ notification += QString::fromLatin1("</ul></qt>");
+ } else
+ notification = i18n("Failed to update several news"
+ " sites. The Internet connection might be cut.");
+
+ KNotifyClient::event(winId(), QString::fromLatin1("InvalidRDF"), notification);
+}
+
+void KNewsTicker::setInterval(const uint interval)
+{
+ m_cfg->setInterval(interval);
+ if ( interval > 4 )
+ m_newsTimer->changeInterval(interval * 60 * 1000);
+}
+
+void KNewsTicker::setScrollingSpeed(const uint scrollingSpeed)
+{
+ m_cfg->setScrollingSpeed(scrollingSpeed);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setMouseWheelSpeed(const uint mouseWheelSpeed)
+{
+ m_cfg->setMouseWheelSpeed(mouseWheelSpeed);
+}
+
+void KNewsTicker::setScrollingDirection(const uint scrollingDirection)
+{
+ m_cfg->setScrollingDirection(scrollingDirection);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setCustomNames(bool customNames)
+{
+ m_cfg->setCustomNames(customNames);
+}
+
+void KNewsTicker::setScrollMostRecentOnly(bool scrollMostRecentOnly)
+{
+ m_cfg->setScrollMostRecentOnly(scrollMostRecentOnly);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setOfflineMode(bool offlineMode)
+{
+ if (offlineMode)
+ m_newsTimer->stop();
+ else
+ if ( m_cfg->interval() > 4 )
+ m_newsTimer->start(m_cfg->interval() * 1000 * 60);
+
+ m_cfg->setOfflineMode(offlineMode);
+}
+
+void KNewsTicker::setUnderlineHighlighted(bool underlineHighlighted)
+{
+ m_cfg->setUnderlineHighlighted(underlineHighlighted);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setShowIcons(bool showIcons)
+{
+ m_cfg->setShowIcons(showIcons);
+ m_scroller->reset(true);
+}
+
+void KNewsTicker::setSlowedScrolling(bool slowedScrolling)
+{
+ m_cfg->setSlowedScrolling(slowedScrolling);
+}
+
+void KNewsTicker::setForegroundColor(const QColor &foregroundColor)
+{
+ m_cfg->setForegroundColor(foregroundColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setBackgroundColor(const QColor &backgroundColor)
+{
+ m_cfg->setBackgroundColor(backgroundColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setHighlightedColor(const QColor &highlightedColor)
+{
+ m_cfg->setHighlightedColor(highlightedColor);
+ m_scroller->reset(false);
+}
+
+void KNewsTicker::setupArrowButton()
+{
+ ArrowType at;
+
+ if (orientation() == Horizontal) {
+ m_arrowButton->setFixedWidth(12);
+ m_arrowButton->setMaximumHeight(128);
+ at = (position() == KPanelApplet::pTop ? DownArrow : UpArrow);
+ } else {
+ m_arrowButton->setMaximumWidth(128);
+ m_arrowButton->setFixedHeight(12);
+ at = (position() == KPanelApplet::pLeft ? RightArrow : LeftArrow);
+ }
+ m_arrowButton->setArrowType(at);
+}
+
+KNewsTickerMenu::KNewsTickerMenu(KNewsTicker *parent, const char *name)
+ : KPopupMenu(parent, name),
+ m_parent(parent),
+ m_fullMenu(false)
+{
+ populateMenu();
+}
+
+void KNewsTickerMenu::populateMenu()
+{
+ clear();
+
+ /*
+ * Perhaps this hardcoded stuff should be replaced by some kind of
+ * themeing functionality?
+ */
+ const QPixmap lookIcon = SmallIcon(QString::fromLatin1("viewmag"));
+ const QPixmap newArticleIcon = SmallIcon(QString::fromLatin1("info"));
+ const QPixmap oldArticleIcon = SmallIcon(QString::fromLatin1("mime_empty"));
+ const QPixmap noArticlesIcon = SmallIcon(QString::fromLatin1("remove"));
+
+ unsigned int articleIdx = 0;
+ const NewsSourceBase::List sources = m_parent->m_newsSources;
+ NewsSourceBase::List::ConstIterator nIt = sources.begin();
+ for (; nIt != sources.end(); ++nIt) {
+ NewsSourceBase::Ptr ns = *nIt;
+
+ KPopupMenu *submenu = new KPopupMenu;
+ int checkNewsId = submenu->insertItem(lookIcon, i18n("Check News"), this, SLOT(slotCheckNews(int)), 0, sources.findIndex(ns) + 1000);
+ setItemParameter(checkNewsId, sources.findIndex(ns));
+
+ submenu->insertSeparator();
+
+ if (m_parent->m_pendingNewsUpdates.contains(ns->newsSourceName())) {
+ submenu->insertItem(noArticlesIcon, i18n("Currently Being Updated, No Articles Available"));
+ } else if (!ns->articles().isEmpty()) {
+ const Article::List articles = ns->articles();
+ Article::List::ConstIterator artIt = articles.begin();
+ for (; artIt != articles.end(); ++artIt) {
+ Article::Ptr a = *artIt;
+ QString headline = a->headline().replace('&', "&&");
+ int id;
+ if ( a->read() )
+ id = submenu->insertItem(oldArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
+ else
+ id = submenu->insertItem(newArticleIcon, headline, this, SLOT(slotOpenArticle(int)), 0, articleIdx+2000);
+ kdDebug( 5005 ) << "Setting articles index for " << a->headline() << " to " << articleIdx << endl;
+ setItemParameter( id, articleIdx++ );
+ }
+ } else {
+ submenu->insertItem(noArticlesIcon, i18n("No Articles Available"));
+ }
+
+ insertItem(ns->icon(), ns->newsSourceName().replace('&', "&&"), submenu);
+ }
+
+ if (!m_parent->m_cfg->newsSources().isEmpty())
+ insertSeparator();
+
+ insertItem(lookIcon, i18n("Check News"), m_parent, SLOT(slotUpdateNews()));
+ int i = insertItem(i18n("Offline Mode"), this, SLOT(slotToggleOfflineMode()), 0, 4711 );
+ setItemChecked(i, m_parent->m_cfg->offlineMode());
+
+ if (m_fullMenu) {
+ insertSeparator();
+
+ const QPixmap logoIcon = SmallIcon(QString::fromLatin1("knewsticker"));
+ const QPixmap helpIcon = SmallIcon(QString::fromLatin1("help"));
+ const QPixmap confIcon = SmallIcon(QString::fromLatin1("configure"));
+
+ insertTitle(logoIcon, i18n("KNewsTicker"), 0, 0);
+
+ insertItem(helpIcon, i18n("Help"), this, SLOT(slotShowHelp()));
+ insertItem(logoIcon, i18n("About KNewsTicker"), this, SLOT(slotShowAbout()));
+ insertSeparator();
+ insertItem(confIcon, i18n("Configure KNewsTicker..."), this, SLOT(slotConfigure()));
+ }
+}
+
+void KNewsTickerMenu::slotShowHelp()
+{
+ m_parent->help();
+}
+
+void KNewsTickerMenu::slotShowAbout()
+{
+ m_parent->about();
+}
+
+void KNewsTickerMenu::slotConfigure()
+{
+ m_parent->preferences();
+}
+
+void KNewsTickerMenu::slotToggleOfflineMode()
+{
+ m_parent->setOfflineMode(!m_parent->m_cfg->offlineMode());
+ setItemChecked( indexOf( 4711 ), !m_parent->m_cfg->offlineMode() );
+}
+
+void KNewsTickerMenu::slotCheckNews(int idx)
+{
+ m_parent->m_newsSources[ idx - 1000 ]->retrieveNews();
+}
+
+void KNewsTickerMenu::slotOpenArticle(int idx)
+{
+ unsigned int i = idx - 2000;
+ const NewsSourceBase::List sources = m_parent->m_newsSources;
+ NewsSourceBase::List::ConstIterator it = sources.begin();
+ while ( it != sources.end() ) {
+ if ( ( *it )->articles().isEmpty() ) {
+ ++it;
+ continue;
+ }
+
+ if ( i <= ( *it )->articles().count() - 1 )
+ break;
+
+ i -= ( *it )->articles().count();
+
+ ++it;
+ }
+
+ if ( it == sources.end() )
+ return;
+
+ ( *it )->articles()[ i ]->open();
+}
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString &configFile)
+ {
+ KGlobal::locale()->insertCatalogue(QString::fromLatin1("knewsticker"));
+ return new KNewsTicker(configFile, KPanelApplet::Stretch,
+ KPanelApplet::Preferences | KPanelApplet::About |
+ KPanelApplet::Help | KPanelApplet::ReportBug,
+ parent, "knewsticker");
+ }
+}
+
+#include "knewsticker.moc"
diff --git a/knewsticker/knewsticker.desktop b/knewsticker/knewsticker.desktop
new file mode 100644
index 00000000..c27cee6b
--- /dev/null
+++ b/knewsticker/knewsticker.desktop
@@ -0,0 +1,104 @@
+[Desktop Entry]
+Name=News Ticker
+Name[af]=Nuus Tikker
+Name[be]=ÐглÑдальнік навінаў
+Name[bn]=সংবাদ টিকার
+Name[br]=Kliker keleier
+Name[bs]=ÄŒitaÄ vijesti
+Name[ca]=Teletip de notícies
+Name[cs]=KNewsTicker
+Name[cy]=Ticer Newyddion
+Name[da]=Nyhedstelegraf
+Name[de]=KNewsTicker
+Name[el]=ΠÏοβολή ειδήσεων
+Name[eo]=Novaĵprezentilo
+Name[et]=Uudiste jälgija
+Name[eu]=Berri-markatzailea
+Name[fa]=تیکر اخبار
+Name[fr]=Téléscripteur de nouvelles
+Name[gl]=Visor de Novas
+Name[he]=צג חדשות
+Name[hu]=KNewsTicker
+Name[id]=Ticker Berita
+Name[is]=Fréttastrimill
+Name[it]=Gestore notizie
+Name[ja]=ニュースティッカー
+Name[ka]=სიáƒáƒ®áƒšáƒ”თრმიმღები
+Name[kk]=Жаңалық таÑпаÑÑ‹
+Name[km]=កម្មវិធី​ទទួល​ពáŸážáŸŒáž˜áž¶áž“
+Name[ko]=ìƒˆì†Œì‹ í‘œì‹œê¸°
+Name[lt]=Naujienų pranešėjas
+Name[lv]=Ziņu Tikkers
+Name[mn]=ÐœÑдÑÑний ДÑлгÑц
+Name[ms]=Detik Berita
+Name[nb]=Nyhetstelegraf
+Name[nds]=Narichten-Ticker
+Name[ne]=समाचार टिकर
+Name[nl]=Nieuwsticker
+Name[nn]=Telegraf
+Name[nso]=Seswai sa Ditaba
+Name[pt]=Extractor de Notícias
+Name[pt_BR]=Mostrador de Notícias
+Name[ru]=Монитор новоÑтей
+Name[sk]=SledovaÄ správ
+Name[sl]=Prikazovalnik novic
+Name[sv]=Nyhetsläsare
+Name[ta]=செயà¯à®¤à®¿à®•à®³à¯ அறிவிபà¯à®ªà®¾à®©à¯
+Name[th]=ตั๋วข่าว
+Name[tr]=Haber Gözlemcisi
+Name[uk]=Стрічка новин
+Name[ven]=Musengulusi wa Mafhungo
+Name[zh_CN]=新闻播报器
+Name[zh_HK]=æ–°èžå¿«å ±
+Name[zh_TW]=æ–°èžç°¡å ±
+Name[zu]=Umlungiseleli Wezindaba
+X-KDE-Library=knewsticker_panelapplet
+X-KDE-UniqueApplet=true
+Icon=knewsticker
+Comment=A scrolling RDF news ticker
+Comment[be]=БÑгучы радок навінаў
+Comment[bg]=Четец на новини RDF
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ অবিরাম আর-ডি-à¦à¦« সংবাদ টিকার
+Comment[bs]=Traka s vijestima iz RDF datoteka
+Comment[ca]=Teletip de notícies RDF amb desplaçament
+Comment[cs]=Rolující RDF novinky
+Comment[da]=En rullende RDF nyhedstelegraf
+Comment[de]=Ein durchlaufender RDF-Newsticker
+Comment[el]=Μια κυλιόμενη Ï€Ïοβολή ειδήσεων RDF
+Comment[eo]=Rulumanta RDF-novaĵprezentilo
+Comment[es]=Teletipo de noticias RDF
+Comment[et]=RDF uudiste jälgija
+Comment[eu]=RDF berri-markatzailea
+Comment[fa]=یک لغزاندن تیکر اخبار RDF
+Comment[fi]=Skrollaava RDF-uutisnäyttäjä
+Comment[fr]=Défilement de nouvelles RDF
+Comment[gl]=Un visor de novas RDF deslizante
+Comment[he]=צג חדשות
+Comment[hu]=Hírbejelentő RSS hírforrásokhoz
+Comment[is]=Skrunandi RDF fréttastrimill
+Comment[it]=Un ticker notizie RDF a scorrimento
+Comment[ja]=スクロールã™ã‚‹ RDFニュースティッカー
+Comment[ka]=მáƒáƒ¡áƒ áƒ˜áƒáƒšáƒ” RDF სიáƒáƒ®áƒšáƒ”თრმიმღები
+Comment[kk]=Ðқтарылатын RDF жаңалық таÑпаÑÑ‹
+Comment[km]=កម្មវិធី​ទទួល​ពáŸážáŸŒáž˜áž¶áž“ RDF រមូរ
+Comment[lt]=Slenkantis RDF naujienų pranešiklis
+Comment[nb]=En rullende RDF nyhetstelegraf
+Comment[nds]=En rullen RDF-Narichtenticker
+Comment[ne]=सà¥à¤•à¥à¤°à¥‹à¤² गरिरहेको आर डी à¤à¤« टिकर
+Comment[nl]=Een verschuivende RDF-nieuwsticker
+Comment[nn]=RDF-nyhendetelegraf
+Comment[pl]=Przewijający się pasek wiadomości RDF
+Comment[pt]=Um extractor de notícias deslizante
+Comment[pt_BR]=Um mini-aplicativo de notícias
+Comment[ru]=Индикатор новоÑтей RDF Ñ Ð¿Ñ€Ð¾ÐºÑ€ÑƒÑ‚ÐºÐ¾Ð¹
+Comment[sk]=Rolujúci sledovaÄ RDF správ
+Comment[sl]=Prikazovalnik novic v obliki RDF
+Comment[sr]=Клизајући приказивач RDF веÑти
+Comment[sr@Latn]=Klizajući prikazivaÄ RDF vesti
+Comment[sv]=En RDF-nyhetsövervakare med rullning
+Comment[tr]=Akan bir RDF haber gözlemcisi
+Comment[uk]=Стрічка новин RDF
+Comment[zh_CN]=滚动的 RDF 新闻播报器
+Comment[zh_HK]=æ²è»¸å¼ RDF æ–°èžç°¡å ±å™¨
+Comment[zh_TW]=æ²è»¸çš„ RDF æ–°èžç°¡å ±
+
diff --git a/knewsticker/knewsticker.h b/knewsticker/knewsticker.h
new file mode 100644
index 00000000..bbb228f6
--- /dev/null
+++ b/knewsticker/knewsticker.h
@@ -0,0 +1,146 @@
+/*
+ * knewsticker.h
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef KNEWSTICKER_H
+#define KNEWSTICKER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dcopobject.h>
+
+#include <qtoolbutton.h>
+#include <kpanelapplet.h>
+#include <kpopupmenu.h>
+
+#include "configiface.h"
+#include "configaccess.h"
+#include "newsengine.h"
+
+class KInstance;
+class NewsSource;
+class KArrowButton;
+class NewsIconMgr;
+class NewsScroller;
+class KNewsTickerMenu;
+class KAboutData;
+class QTimer;
+
+class KNewsTicker : public KPanelApplet, virtual public ConfigIface,
+ virtual public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+ friend class KNewsTickerMenu;
+
+ public:
+ KNewsTicker(const QString &, Type, int, QWidget * = 0, const char * = 0);
+ virtual ~KNewsTicker();
+
+ virtual int widthForHeight(int) const;
+ virtual int heightForWidth(int) const;
+
+ k_dcop:
+ virtual void reparseConfig();
+ virtual void updateNews() { slotUpdateNews(); }
+ virtual uint interval() const { return m_cfg->interval(); }
+ virtual uint scrollingSpeed() const { return m_cfg->scrollingSpeed(); }
+ virtual uint mouseWheelSpeed() const { return m_cfg->mouseWheelSpeed(); }
+ virtual uint scrollingDirection() const { return m_cfg->scrollingDirection(); }
+ virtual bool customNames() const { return m_cfg->customNames(); }
+ virtual bool scrollMostRecentOnly() const { return m_cfg->scrollMostRecentOnly(); }
+ virtual bool offlineMode() const { return m_cfg->offlineMode(); }
+ virtual bool underlineHighlighted() const { return m_cfg->underlineHighlighted(); }
+ virtual bool showIcons() const { return m_cfg->showIcons(); }
+ virtual bool slowedScrolling() const { return m_cfg->slowedScrolling(); }
+ virtual QColor foregroundColor() const { return m_cfg->foregroundColor(); }
+ virtual QColor backgroundColor() const { return m_cfg->backgroundColor(); }
+ virtual QColor highlightedColor() const { return m_cfg->highlightedColor(); }
+ virtual QStringList newsSources() const { return m_cfg->newsSources(); }
+ virtual void setInterval(const uint);
+ virtual void setScrollingSpeed(const uint);
+ virtual void setMouseWheelSpeed(const uint);
+ virtual void setScrollingDirection(const uint);
+ virtual void setCustomNames(bool);
+ virtual void setScrollMostRecentOnly(bool);
+ virtual void setOfflineMode(bool);
+ virtual void setUnderlineHighlighted(bool);
+ virtual void setShowIcons(bool);
+ virtual void setSlowedScrolling(bool);
+ virtual void setForegroundColor(const QColor &);
+ virtual void setBackgroundColor(const QColor &);
+ virtual void setHighlightedColor(const QColor &);
+ virtual void setNewsSources(const QStringList &) {}
+
+ public slots:
+ void slotUpdateNews();
+ void slotOpenContextMenu();
+
+ protected:
+ virtual void preferences();
+ virtual void about();
+ virtual void help();
+ virtual void reportBug();
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void positionChange(Position);
+
+ protected slots:
+ void slotArrowButtonPressed();
+ void slotNewsSourceUpdated(const NewsSourceBase::Ptr &, bool = false);
+ void slotNewsSourceFailed(const NewsSourceBase::Ptr &);
+ void slotContextMenuAboutToHide();
+ void slotNotifyOfFailures();
+
+ private:
+ void setupArrowButton();
+
+ KInstance *m_instance;
+ DCOPClient *m_dcopClient;
+ ConfigAccess *m_cfg;
+ KArrowButton *m_arrowButton;
+ QTimer *m_newsTimer;
+ QTimer *m_updateTimer;
+ NewsIconMgr *m_newsIconMgr;
+ NewsScroller *m_scroller;
+ KAboutData *m_aboutData;
+ KNewsTickerMenu *m_contextMenu;
+ bool m_newNews;
+ NewsSourceBase::List m_newsSources;
+ QStringList m_failedNewsUpdates;
+ QStringList m_pendingNewsUpdates;
+};
+
+class KNewsTickerMenu : public KPopupMenu
+{
+ Q_OBJECT
+
+ public:
+ KNewsTickerMenu(KNewsTicker *, const char * = 0);
+ void setFullMenu(bool full) { m_fullMenu = full; populateMenu(); }
+
+ protected slots:
+ void populateMenu();
+
+ private slots:
+ void slotShowHelp();
+ void slotShowAbout();
+ void slotConfigure();
+ void slotToggleOfflineMode();
+ void slotCheckNews(int idx);
+ void slotOpenArticle(int idx);
+
+ private:
+ KNewsTicker *m_parent;
+ bool m_fullMenu;
+};
+
+#endif // KNEWSTICKER_H
diff --git a/knewsticker/knewsticker.upd b/knewsticker/knewsticker.upd
new file mode 100644
index 00000000..80720bdb
--- /dev/null
+++ b/knewsticker/knewsticker.upd
@@ -0,0 +1,13 @@
+# Updates from version 0.1 to version 0.2
+Id=KNewsTicker-0.2
+File=knewstickerrc,knewstickerappletrc
+Script=knt-0.1-0.2.pl,perl
+# Update for KDE 3.0, Kicker applets use a different naming scheme
+Id=KNewsTicker-0.2-Rename-KDE3
+File=knewstickerappletrc,knewsticker_appletrc
+AllGroups
+# Update for KDE 3.1, seems like they changed the nameing scheme *AGAIN*
+Id=KNewsTicker-0.2-Rename-KDE3.1
+File=knewsticker_appletrc,knewsticker_panelappletrc
+AllGroups
+# End of file
diff --git a/knewsticker/knewstickerconfig.cpp b/knewsticker/knewstickerconfig.cpp
new file mode 100644
index 00000000..528269a0
--- /dev/null
+++ b/knewsticker/knewstickerconfig.cpp
@@ -0,0 +1,582 @@
+/*
+ * knewstickerconfig.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "knewstickerconfig.h"
+#include "configaccess.h"
+#include "newsengine.h"
+#include "newsiconmgr.h"
+#include "newssourcedlgimpl.h"
+
+#include <kapplication.h>
+#include <kcolorbutton.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kfontdialog.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <kmessagebox.h>
+#include <knuminput.h>
+#include <kpopupmenu.h>
+
+#include <qcursor.h>
+#include <qcheckbox.h>
+#include <qdragobject.h>
+#include <qlabel.h>
+#include <qslider.h>
+#include <qregexp.h>
+
+CategoryItem::CategoryItem(QListView *parent, const QString &text)
+ : QListViewItem(parent, text)
+{
+ setOpen(true);
+}
+
+void CategoryItem::setOpen(bool open)
+{
+ if (open)
+ setPixmap(0, SmallIcon(QString::fromLatin1("folder_open")));
+ else
+ setPixmap(0, SmallIcon(QString::fromLatin1("folder")));
+
+ QListViewItem::setOpen(open);
+}
+
+NewsSourceItem::NewsSourceItem(KNewsTickerConfig *kcm, CategoryItem *parent,
+ const NewsSourceBase::Data &nsd)
+ : QCheckListItem(parent, QString::null, QCheckListItem::CheckBox),
+ m_parent(parent),
+ m_kcm(kcm)
+{
+ setData(nsd);
+}
+
+NewsSourceBase::Data NewsSourceItem::data() const
+{
+ NewsSourceBase::Data nsd;
+ nsd.enabled = isOn();
+ nsd.name = text(0);
+ nsd.sourceFile = text(1);
+ nsd.maxArticles = text(2).toUInt();
+ nsd.icon = m_icon;
+ nsd.isProgram = m_isProgram;
+ nsd.subject = m_subject;
+ return nsd;
+}
+
+void NewsSourceItem::setData(const NewsSourceBase::Data &nsd)
+{
+ setOn(nsd.enabled);
+ setText(0, nsd.name);
+ setText(1, nsd.sourceFile);
+ setText(2, QString::number(nsd.maxArticles));
+
+ m_icon = nsd.icon;
+ m_isProgram = nsd.isProgram;
+ m_subject = nsd.subject;
+ m_kcm->getNewsIcon(this, KURL( m_icon ));
+}
+
+void NewsSourceItem::setIcon(const QPixmap &pixmap)
+{
+ setPixmap(0, pixmap);
+}
+
+KNewsTickerConfig::KNewsTickerConfig(ConfigAccess *cfg, QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Configuration"), Ok|Close),
+ m_cfg(cfg),
+ m_child(new KNewsTickerConfigWidget(this)),
+ m_newsIconMgr(NewsIconMgr::self())
+{
+ setMainWidget(m_child);
+
+ // Change various properties of the view which the Qt Designer cannot
+ // set at design time yet.
+ m_child->niInterval->setLabel(i18n("News query interval:"));
+ m_child->niInterval->setRange(4, 180);
+
+ m_child->lvNewsSources->setShowSortIndicator(true);
+ m_child->lvNewsSources->setSelectionModeExt(KListView::Extended);
+ m_child->lvNewsSources->setAcceptDrops(true);
+ m_child->lvNewsSources->viewport()->setAcceptDrops(true);
+ m_child->lvNewsSources->viewport()->installEventFilter(this);
+ m_child->lvNewsSources->installEventFilter(this);
+
+ connect(m_newsIconMgr, SIGNAL(gotIcon(const KURL &, const QPixmap &)), SLOT(slotGotNewsIcon(const KURL &, const QPixmap &)));
+ connect(m_child->bChooseFont, SIGNAL(clicked()), SLOT(slotChooseFont()));
+
+ connect(m_child->lvNewsSources, SIGNAL(contextMenu(KListView *, QListViewItem *, const QPoint &)),
+ SLOT(slotNewsSourceContextMenu(KListView *, QListViewItem *, const QPoint &)));
+ connect(m_child->lvNewsSources, SIGNAL(selectionChanged()),
+ SLOT(slotNewsSourceSelectionChanged()));
+ connect(m_child->lvNewsSources, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
+ SLOT(slotModifyNewsSource(QListViewItem *, const QPoint &, int)));
+ connect(m_child->bAddNewsSource, SIGNAL(clicked()), SLOT(slotAddNewsSource()));
+ connect(m_child->bRemoveNewsSource, SIGNAL(clicked()), SLOT(slotRemoveNewsSource()));
+ connect(m_child->bModifyNewsSource, SIGNAL(clicked()), SLOT(slotModifyNewsSource()));
+
+ connect(m_child->lvFilters, SIGNAL(selectionChanged(QListViewItem *)),
+ SLOT(slotFilterSelectionChanged(QListViewItem *)));
+ connect(m_child->comboFilterAction, SIGNAL(activated(const QString &)),
+ SLOT(slotFilterActionChanged(const QString &)));
+ connect(m_child->comboFilterNewsSource, SIGNAL(activated(const QString &)),
+ SLOT(slotFilterNewsSourceChanged(const QString &)));
+ connect(m_child->comboFilterCondition, SIGNAL(activated(const QString &)),
+ SLOT(slotFilterConditionChanged(const QString &)));
+ connect(m_child->leFilterExpression, SIGNAL(textChanged(const QString &)),
+ SLOT(slotFilterExpressionChanged(const QString &)));
+ connect(m_child->bAddFilter, SIGNAL(clicked()), SLOT(slotAddFilter()));
+ connect(m_child->bRemoveFilter, SIGNAL(clicked()), SLOT(slotRemoveFilter()));
+
+ load();
+
+ m_child->show();
+}
+
+void KNewsTickerConfig::load()
+{
+ m_child->comboFilterNewsSource->clear();
+ m_child->comboFilterNewsSource->insertItem(i18n("All News Sources"));
+
+ m_child->niInterval->setValue(m_cfg->interval());
+ m_child->sliderMouseWheelSpeed->setValue(m_cfg->mouseWheelSpeed());
+ m_child->cbCustomNames->setChecked(m_cfg->customNames());
+ m_child->cbScrollMostRecentOnly->setChecked(m_cfg->scrollMostRecentOnly());
+ m_child->cbSlowedScrolling->setChecked(m_cfg->slowedScrolling());
+
+ m_child->sliderScrollSpeed->setValue(m_cfg->scrollingSpeed());
+ m_child->comboDirection->setCurrentItem(m_cfg->scrollingDirection());
+
+ m_font = m_cfg->font();
+ m_child->colorForeground->setColor(m_cfg->foregroundColor());
+ m_child->colorBackground->setColor(m_cfg->backgroundColor());
+ m_child->colorHighlighted->setColor(m_cfg->highlightedColor());
+ m_child->cbUnderlineHighlighted->setChecked(m_cfg->underlineHighlighted());
+ m_child->cbShowIcons->setChecked(m_cfg->showIcons());
+
+ m_child->lvNewsSources->clear();
+ QStringList nsList = m_cfg->newsSources();
+ for (QStringList::Iterator it = nsList.begin(); it != nsList.end(); ++it)
+ addNewsSource(m_cfg->newsSource((*it))->data());
+
+ ArticleFilter::List filterList = m_cfg->filters();
+ ArticleFilter::List::ConstIterator it = filterList.begin();
+ ArticleFilter::List::ConstIterator end = filterList.end();
+ for (; it != end; ++it)
+ addFilter(m_cfg->filter((*it)));
+
+ slotNewsSourceSelectionChanged();
+}
+
+void KNewsTickerConfig::save()
+{
+ m_cfg->setInterval(m_child->niInterval->value());
+ m_cfg->setMouseWheelSpeed(m_child->sliderMouseWheelSpeed->value());
+ m_cfg->setCustomNames(m_child->cbCustomNames->isChecked());
+ m_cfg->setScrollMostRecentOnly(m_child->cbScrollMostRecentOnly->isChecked());
+ m_cfg->setSlowedScrolling(m_child->cbSlowedScrolling->isChecked());
+
+ m_cfg->setScrollingSpeed(m_child->sliderScrollSpeed->value());
+ m_cfg->setScrollingDirection(static_cast<ConfigAccess::Direction>(m_child->comboDirection->currentItem()));
+
+ m_cfg->setFont(m_font);
+ m_cfg->setForegroundColor(m_child->colorForeground->color());
+ m_cfg->setBackgroundColor(m_child->colorBackground->color());
+ m_cfg->setHighlightedColor(m_child->colorHighlighted->color());
+ m_cfg->setUnderlineHighlighted(m_child->cbUnderlineHighlighted->isChecked());
+ m_cfg->setShowIcons(m_child->cbShowIcons->isChecked());
+
+ QStringList newsSources;
+ for (QListViewItemIterator it(m_child->lvNewsSources); it.current(); it++)
+ if (NewsSourceItem *item = dynamic_cast<NewsSourceItem *>(it.current())) {
+ newsSources += item->data().name;
+ m_cfg->setNewsSource(item->data());
+ }
+ m_cfg->setNewsSources(newsSources);
+
+ ArticleFilter::List filters;
+ ArticleFilter f;
+ unsigned int i = 0;
+ for (QListViewItemIterator it(m_child->lvFilters); it.current(); it++)
+ if (QCheckListItem *item = dynamic_cast<QCheckListItem *>(it.current())) {
+ filters.append(i);
+ f.setAction(item->text(0));
+ f.setNewsSource(item->text(2));
+ f.setCondition(item->text(4));
+ f.setExpression(item->text(5));
+ f.setEnabled(item->isOn());
+ f.setId(i++);
+ m_cfg->setFilter(f);
+ }
+ m_cfg->setFilters(filters);
+}
+
+bool KNewsTickerConfig::eventFilter(QObject *o, QEvent *e)
+{
+ //<HACK>
+ // "if ( e->type() == QEvent::DragEnter ) {" shoult normaly be enough. but there must be a bug somewhere in KListView.
+ if ( e->type() == QEvent::DragMove ) {
+ //</HACK>
+ QDragEnterEvent *d = (QDragEnterEvent*)e;
+ d->accept(QTextDrag::canDecode(d));
+ return true; // eat event
+ }
+ else if ( e->type() == QEvent::Drop) {
+ QDropEvent *d = (QDropEvent*)e;
+ QString newSourceUrl;
+ if ( QTextDrag::decode(d, newSourceUrl) ) {
+ // <HACK>
+ // This is just for http://www.webreference.com/services/news/
+ newSourceUrl = newSourceUrl.replace( QRegExp("^view-source:http%3A//"), "http://" );
+ // </HACK>
+ newSourceUrl = newSourceUrl.stripWhiteSpace();
+
+ //look for a new Name of Source:
+ QString name = i18n("Unknown");
+ bool validName = false;
+ for (QListViewItemIterator it(m_child->lvNewsSources); it.current(); it++) {
+ if (it.current()->text(0) == name) {
+ validName = false;
+ break;
+ } else {
+ validName = true;
+ }
+ }
+ int i = 0;
+ while (validName == false) {
+ name = i18n("Unknown %1").arg(i);
+ for (QListViewItemIterator it(m_child->lvNewsSources); it.current(); it++) {
+ if (it.current()->text(0) == name) {
+ i++;
+ validName = false;
+ break;
+ } else {
+ validName = true;
+ }
+ }
+ }
+
+ NewsSourceBase::Data nsd(name, newSourceUrl, "" , NewsSourceBase::Computers , 10, true, false);
+ NewsSourceDlgImpl nsDlg(this, 0L, true);
+ connect(&nsDlg, SIGNAL(newsSource(const NewsSourceBase::Data &)),
+ SLOT(slotAddNewsSource(const NewsSourceBase::Data &)));
+ nsDlg.setup(nsd, false);
+ nsDlg.exec();
+ }
+ return true; // eat event
+ }
+ return QWidget::eventFilter( o, e );
+}
+
+void KNewsTickerConfig::resizeEvent(QResizeEvent *)
+{
+ m_child->resize(width(), height());
+}
+
+void KNewsTickerConfig::addNewsSource(const NewsSourceBase::Data &nsd,
+ bool select)
+{
+ CategoryItem *catItem = 0L;
+
+ for (QListViewItemIterator it(m_child->lvNewsSources); it.current(); it++) {
+ if (it.current()->text(0) == NewsSourceBase::subjectText(nsd.subject)) {
+ catItem = static_cast<CategoryItem *>(it.current());
+ break;
+ }
+ }
+
+ if (!catItem)
+ catItem = new CategoryItem(m_child->lvNewsSources,
+ NewsSourceBase::subjectText(nsd.subject));
+
+ NewsSourceItem *item = new NewsSourceItem(this, catItem, nsd);
+ if (select)
+ m_child->lvNewsSources->setCurrentItem(item);
+
+ m_child->comboFilterNewsSource->insertItem(item->data().name);
+}
+
+void KNewsTickerConfig::addFilter(const ArticleFilter &fd)
+{
+ QCheckListItem *item = new QCheckListItem(m_child->lvFilters, fd.action(), QCheckListItem::CheckBox);
+ item->setOn(fd.enabled());
+ item->setText(1, m_child->lArticles->text());
+ item->setText(2, fd.newsSource());
+ item->setText(3, m_child->lHeadlines->text());
+ item->setText(4, fd.condition());
+ item->setText(5, fd.expression());
+}
+
+void KNewsTickerConfig::removeNewsSource()
+{
+ if (KMessageBox::warningContinueCancel(this, i18n("<p>Do you really want to remove %n news"
+ " source?</p>",
+ "<p>Do you really want to remove these %n news"
+ " sources?</p>",
+ m_child->lvNewsSources->selectedItems().count()), QString::null, KStdGuiItem::del()) == KMessageBox::Continue) {
+ int itemCount = m_child->lvNewsSources->selectedItems().count();
+ for (int j = 0; j < itemCount; j++) {
+ if (m_child->lvNewsSources->selectedItems().isEmpty()) { break; }
+ QListViewItem *item = m_child->lvNewsSources->selectedItems().take(0);
+ for (int i = 0; i < m_child->comboFilterNewsSource->count(); i++)
+ if (m_child->comboFilterNewsSource->text(i) == item->text(0)) {
+ m_child->comboFilterNewsSource->removeItem(i);
+ break;
+ }
+ if (dynamic_cast<NewsSourceItem *>(item) && item->parent()->childCount() == 1)
+ delete item->parent();
+ else
+ delete item;
+ }
+ m_child->bRemoveNewsSource->setEnabled(false);
+ m_child->bModifyNewsSource->setEnabled(false);
+ }
+}
+
+void KNewsTickerConfig::removeFilter(QListViewItem *item)
+{
+ if (KMessageBox::warningContinueCancel(this, i18n("<p>Do you really want to remove the selected"
+ " filter?</p>"), QString::null, KStdGuiItem::del()) == KMessageBox::Continue) {
+ delete item;
+ m_child->bRemoveFilter->setEnabled(false);
+ }
+}
+
+void KNewsTickerConfig::slotNewsSourceContextMenu(KListView *, QListViewItem *item, const QPoint &)
+{
+ if (!dynamic_cast<NewsSourceItem *>(item))
+ return;
+
+ KPopupMenu *menu = new KPopupMenu();
+
+ QPixmap addIcon = SmallIcon(QString::fromLatin1("news_subscribe"));
+ QPixmap modifyIcon = SmallIcon(QString::fromLatin1("edit"));
+ QPixmap removeIcon = SmallIcon(QString::fromLatin1("news_unsubscribe"));
+ QPixmap logoIcon = SmallIcon(QString::fromLatin1("knewsticker"));
+
+ menu->insertTitle(logoIcon, i18n("Edit News Source"));
+ menu->insertItem(addIcon, i18n("&Add News Source"), 0);
+ if (item) {
+ menu->insertItem(modifyIcon, i18n("&Modify '%1'").arg(item->text(0)), 1);
+ if (m_child->lvNewsSources->selectedItems().count() == 1) {
+ menu->insertItem(removeIcon, i18n("&Remove '%1'").arg(item->text(0)), 2);
+ } else {
+ menu->insertItem(removeIcon, i18n("&Remove News Sources"), 2);
+ }
+ } else {
+ menu->insertItem(modifyIcon, i18n("&Modify News Source"), 1);
+ menu->insertItem(removeIcon, i18n("&Remove News Source"), 2);
+ menu->setItemEnabled(1, false);
+ menu->setItemEnabled(2, false);
+ }
+
+ switch (menu->exec(QCursor::pos())) {
+ case 0: slotAddNewsSource(); break;
+ case 1: modifyNewsSource(item); break;
+ case 2: removeNewsSource(); break;
+ }
+
+ delete menu;
+}
+
+void KNewsTickerConfig::slotChooseFont()
+{
+ KFontDialog fd(this, "Font Dialog", false, true);
+
+ fd.setFont(m_font);
+
+ if (fd.exec() == KFontDialog::Accepted) {
+ if (m_font != fd.font()) {
+ m_font = fd.font();
+ }
+ }
+}
+
+void KNewsTickerConfig::slotAddNewsSource()
+{
+ NewsSourceDlgImpl nsDlg(this, 0L, true);
+ connect(&nsDlg, SIGNAL(newsSource(const NewsSourceBase::Data &)),
+ SLOT(slotAddNewsSource(const NewsSourceBase::Data &)));
+ nsDlg.exec();
+}
+
+void KNewsTickerConfig::slotAddNewsSource(const NewsSourceBase::Data &nsd)
+{
+ addNewsSource(nsd);
+}
+
+void KNewsTickerConfig::slotModifyNewsSource()
+{
+ if ((m_modifyItem = dynamic_cast<NewsSourceItem *>(m_child->lvNewsSources->selectedItems().take(0))))
+ openModifyDialog();
+}
+
+void KNewsTickerConfig::slotModifyNewsSource(const NewsSourceBase::Data &nsd)
+{
+ if (m_modifyItem->data().subject != nsd.subject) {
+ QListViewItem *parentItem = m_modifyItem->parentItem();
+ parentItem->takeItem(m_modifyItem);
+ if (parentItem->childCount() == 0)
+ delete parentItem;
+
+ CategoryItem *catItem = 0L;
+
+ for (QListViewItemIterator it(m_child->lvNewsSources); it.current(); it++) {
+ if (it.current()->text(0) == NewsSourceBase::subjectText(nsd.subject)) {
+ catItem = static_cast<CategoryItem *>(it.current());
+ break;
+ }
+ }
+
+ if (!catItem)
+ catItem = new CategoryItem(m_child->lvNewsSources,
+ NewsSourceBase::subjectText(nsd.subject));
+
+ catItem->insertItem(m_modifyItem);
+ }
+
+ m_modifyItem->setData(nsd);
+}
+
+void KNewsTickerConfig::slotModifyNewsSource(QListViewItem *item, const QPoint &, int)
+{
+ if (dynamic_cast<NewsSourceItem *>(item))
+ modifyNewsSource(item);
+}
+
+void KNewsTickerConfig::modifyNewsSource(QListViewItem *item)
+{
+ if ((m_modifyItem = dynamic_cast<NewsSourceItem *>(item)))
+ openModifyDialog();
+}
+
+void KNewsTickerConfig::openModifyDialog()
+{
+ NewsSourceDlgImpl nsDlg(this, 0L, true);
+ connect(&nsDlg, SIGNAL(newsSource(const NewsSourceBase::Data &)),
+ SLOT(slotModifyNewsSource(const NewsSourceBase::Data &)));
+ nsDlg.setup(m_modifyItem->data(), true);
+ nsDlg.exec();
+}
+
+void KNewsTickerConfig::slotAddFilter()
+{
+ ArticleFilter fd;
+ fd.setAction(m_child->comboFilterAction->currentText());
+ fd.setNewsSource(m_child->comboFilterNewsSource->currentText());
+ fd.setCondition(m_child->comboFilterCondition->currentText());
+ fd.setExpression(m_child->leFilterExpression->text());
+ fd.setEnabled(true);
+ addFilter(fd);
+}
+
+void KNewsTickerConfig::slotRemoveNewsSource()
+{
+ removeNewsSource();
+}
+
+void KNewsTickerConfig::slotRemoveFilter()
+{
+ QListViewItem *item=m_child->lvFilters->selectedItem();
+ if(!item)
+ return;
+ removeFilter(item);
+}
+
+void KNewsTickerConfig::slotNewsSourceSelectionChanged()
+{
+ m_child->bRemoveNewsSource->setEnabled(! m_child->lvNewsSources->selectedItems().isEmpty());
+ m_child->bModifyNewsSource->setEnabled(m_child->lvNewsSources->selectedItems().count() == 1);
+}
+
+void KNewsTickerConfig::slotFilterSelectionChanged(QListViewItem *item)
+{
+ for (int i = 0; i < m_child->comboFilterAction->count(); i++)
+ if (m_child->comboFilterAction->text(i) == item->text(0)) {
+ m_child->comboFilterAction->setCurrentItem(i);
+ break;
+ }
+
+ for (int i = 0; i < m_child->comboFilterNewsSource->count(); i++)
+ if (m_child->comboFilterNewsSource->text(i) == item->text(2)) {
+ m_child->comboFilterNewsSource->setCurrentItem(i);
+ break;
+ }
+
+ for (int i = 0; i < m_child->comboFilterCondition->count(); i++)
+ if (m_child->comboFilterCondition->text(i) == item->text(4)) {
+ m_child->comboFilterCondition->setCurrentItem(i);
+ break;
+ }
+
+ m_child->leFilterExpression->setText(item->text(5));
+
+ m_child->bRemoveFilter->setEnabled(item);
+}
+
+void KNewsTickerConfig::slotFilterActionChanged(const QString &action)
+{
+ QListViewItem *item = m_child->lvFilters->selectedItem();
+
+ if (item) {
+ item->setText(0, action);
+ }
+}
+
+void KNewsTickerConfig::slotFilterNewsSourceChanged(const QString &newsSource)
+{
+ QListViewItem *item = m_child->lvFilters->selectedItem();
+
+ if (item) {
+ item->setText(2, newsSource);
+ }
+}
+
+void KNewsTickerConfig::slotFilterConditionChanged(const QString &condition)
+{
+ QListViewItem *item = m_child->lvFilters->selectedItem();
+
+ if (item) {
+ item->setText(4, condition);
+ }
+}
+
+void KNewsTickerConfig::slotFilterExpressionChanged(const QString &expression)
+{
+ QListViewItem *item = m_child->lvFilters->selectedItem();
+
+ if (item) {
+ item->setText(5, expression);
+ }
+}
+
+void KNewsTickerConfig::getNewsIcon(NewsSourceItem *item, const KURL &url)
+{
+ m_itemIconMap[url.url()] = item;
+ m_newsIconMgr->getIcon(url);
+}
+
+void KNewsTickerConfig::slotGotNewsIcon(const KURL &url, const QPixmap &pixmap)
+{
+ if (m_itemIconMap.find(url.url()) == m_itemIconMap.end()) {
+ kdDebug(5005) << "Got unknown icon for URL " << url << endl;
+ return;
+ }
+ m_itemIconMap[url.url()]->setIcon(pixmap);
+ m_itemIconMap.remove(url.url());
+}
+
+void KNewsTickerConfig::slotOk()
+{
+ save();
+ accept();
+}
+
+#include "knewstickerconfig.moc"
diff --git a/knewsticker/knewstickerconfig.h b/knewsticker/knewstickerconfig.h
new file mode 100644
index 00000000..ec475236
--- /dev/null
+++ b/knewsticker/knewstickerconfig.h
@@ -0,0 +1,110 @@
+/*
+ * knewstickerconfig.h
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef KCMNEWSTICKER_H
+#define KCMNEWSTICKER_H
+
+#include "knewstickerconfigwidget.h"
+#include "configaccess.h"
+#include "newsengine.h"
+
+#include <kdialogbase.h>
+
+#include <qevent.h>
+#include <qfont.h>
+#include <qlistview.h>
+
+class KNewsTickerConfig;
+class KNewsTickerConfigWidget;
+class KConfig;
+class NewsIconMgr;
+class ConfigAccess;
+
+class CategoryItem : public QListViewItem
+{
+ public:
+ CategoryItem(QListView *, const QString &);
+
+ void setOpen(bool);
+};
+
+class NewsSourceItem : public QCheckListItem
+{
+ public:
+ NewsSourceItem(KNewsTickerConfig *, CategoryItem *, const NewsSourceBase::Data &);
+
+ NewsSourceBase::Data data() const;
+ void setData(const NewsSourceBase::Data &);
+ void setIcon(const QPixmap &);
+
+ QListViewItem *parentItem() { return QCheckListItem::parent(); }
+
+ private:
+ QString m_icon;
+ bool m_isProgram;
+ NewsSourceBase::Subject m_subject;
+ CategoryItem *m_parent;
+ KNewsTickerConfig *m_kcm;
+};
+
+class KNewsTickerConfig : public KDialogBase
+{
+ Q_OBJECT
+ friend class NewsSourceItem;
+ public:
+ KNewsTickerConfig(ConfigAccess *, QWidget * = 0, const char * = 0);
+
+ void load();
+ void save();
+ void defaults();
+
+ protected:
+ void addNewsSource(const NewsSourceBase::Data &, bool = false);
+ void modifyNewsSource(QListViewItem *);
+ void removeNewsSource();
+ void addFilter(const ArticleFilter &);
+ void removeFilter(QListViewItem *);
+ void resizeEvent(QResizeEvent *);
+ void openModifyDialog();
+ bool eventFilter(QObject *o, QEvent *e);
+ void getNewsIcon(NewsSourceItem *, const KURL &);
+
+ protected slots:
+ void slotNewsSourceContextMenu(KListView *, QListViewItem *, const QPoint &);
+ void slotChooseFont();
+ void slotAddNewsSource();
+ void slotAddFilter();
+ void slotAddNewsSource(const NewsSourceBase::Data &);
+ void slotRemoveNewsSource();
+ void slotRemoveFilter();
+ void slotModifyNewsSource();
+ void slotModifyNewsSource(const NewsSourceBase::Data &);
+ void slotModifyNewsSource(QListViewItem *, const QPoint &, int);
+ void slotNewsSourceSelectionChanged();
+ void slotFilterSelectionChanged(QListViewItem *);
+ void slotFilterActionChanged(const QString &);
+ void slotFilterNewsSourceChanged(const QString &);
+ void slotFilterConditionChanged(const QString &);
+ void slotFilterExpressionChanged(const QString &);
+ virtual void slotOk();
+
+ private slots:
+ void slotGotNewsIcon(const KURL &, const QPixmap &);
+
+ private:
+ ConfigAccess *m_cfg;
+ KNewsTickerConfigWidget *m_child;
+ QFont m_font;
+ NewsSourceItem *m_modifyItem;
+ NewsIconMgr *m_newsIconMgr;
+ QMap<QString, NewsSourceItem *> m_itemIconMap;
+};
+
+#endif // KCMNEWSTICKER_H
diff --git a/knewsticker/knewstickerconfigwidget.ui b/knewsticker/knewstickerconfigwidget.ui
new file mode 100644
index 00000000..3afa390d
--- /dev/null
+++ b/knewsticker/knewstickerconfigwidget.ui
@@ -0,0 +1,1178 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KNewsTickerConfigWidget</class>
+<comment>The core widget of the KCMNewsTicker KControl module.</comment>
+<author>Frerich Raabe &lt;raabe@kde.org&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KNewsTickerConfigWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>597</width>
+ <height>525</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabScrollerPreferences</cstring>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tabGeneral</cstring>
+ </property>
+ <attribute name="title">
+ <string>General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gbGeneral</cstring>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KIntNumInput" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>niInterval</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ <property name="suffix">
+ <string> min</string>
+ </property>
+ <property name="specialValueText">
+ <string>Never</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Interval of news queries</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define at what interval KNewsTicker queries the configured news sources for new headlines. This depends generally on how fast you would like to hear about news and how much load you want to put on the network:&lt;ul&gt;
+&lt;li&gt;A lower value (lower than &lt;b&gt;15 minutes&lt;/b&gt;) enables you to get notified about news very quickly if you want or need to. Please note that it increases the network traffic significantly, though. Therefore, such low values should not be used if you query popular news sites (such as &lt;a href="http://slashdot.org"&gt;Slashdot&lt;/a&gt; or &lt;a href="http://freshmeat.net"&gt;Freshmeat&lt;/a&gt;) as they have generally already enough work processing the incoming queries.&lt;/li&gt;
+&lt;li&gt;A higher value (higher than &lt;b&gt;45 minutes&lt;/b&gt;) will make you hear about news less quickly. For non-timecritical applications, it should be suitable, though. The positive aspect of longer intervals is that only very little load is put on the network; this saves resources and nerves, for you and the system administrators of the news sites you query.&lt;/li&gt;&lt;/ul&gt;
+The default value (30 minutes) should be appropriate and reasonable in most cases.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lNonSensitive</cstring>
+ </property>
+ <property name="text">
+ <string>Nonsensitive</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mousewheel sensitivity</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This slider allows you to define how quickly/slowly the text should be scrolled when using the mousewheel.</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>sliderMouseWheelSpeed</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickInterval">
+ <number>150</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mousewheel sensitivity</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This slider allows you to define how fast/slow the text should be scrolled when using the mousewheel.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lSensitive</cstring>
+ </property>
+ <property name="text">
+ <string>Sensitive</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mousewheel sensitivity</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This slider allows you to define how fast/slow the text should be scrolled when using the mousewheel.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lMouseWheelSensitivity</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Mousewheel sensitivity:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sliderMouseWheelSpeed</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Mousewheel sensitivity</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This slider allows you to define how fast/slow the text should be scrolled when using the mousewheel.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>cbCustomNames</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use custom names for news sites</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use the names defined in the list of news sources</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to make the news ticker use the names you specified in the list of news sources (available on the tab labeled &lt;i&gt;News sources&lt;/i&gt;) instead of the ones the news sites themselves report.&lt;br&gt;This can be handy for news sites which report a very long or useless name.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tabNewsSources</cstring>
+ </property>
+ <attribute name="title">
+ <string>News Sources</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="4">
+ <column>
+ <property name="text">
+ <string>Name of Site</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Source File</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Max. Articles</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lvNewsSources</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>News sources to be queried</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list lets you manage the list of news sites the news ticker will query for headlines. The news sources are arranged in a tree-like hierarchy and sorted by topic.&lt;br&gt;The column labeled "Max. articles" shows how many articles will be cached for the news sites (read: how many articles will be accessible through the context menu).&lt;ul&gt;
+&lt;li&gt;To add a site, you can either drag the URL of the RDF or RSS file to this list from Konqueror or any other application, or use the &lt;i&gt;Add...&lt;/i&gt; button in the bottom right corner.&lt;/li&gt;
+&lt;li&gt;To modify a site, just double-click on the particular news source you would like to edit and an input field will pop up which lets you edit the respective property.&lt;/li&gt;
+&lt;li&gt;To remove a site, simply select a news source in the list and click on the &lt;i&gt;Remove&lt;/i&gt; button in the lower right corner.&lt;/li&gt;&lt;/ul&gt;
+Note that you can also right-click on the list to open a menu which lets you add and remove news sources. You can also enable or disable certain news sources temporarily by checking or unchecking the box next to it; those news sources whose boxes are checked are considered activated and will be processed by KNewsTicker.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="3">
+ <property name="name">
+ <cstring>bRemoveNewsSource</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>R&amp;emove</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Remove selected site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to remove the currently selected news site from the list.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <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>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>bAddNewsSource</cstring>
+ </property>
+ <property name="text">
+ <string>Add...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add a new site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to add a new site to the list. Note that you can also drag a RDF or RSS file to this list (i.e. from Konqueror) to add it to the list.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>bModifyNewsSource</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Modify...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Modify selected news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to open a dialog which lets you edit the properties (such as the name, the source file, or the icon) of the currently selected news source.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tabFilters</cstring>
+ </property>
+ <attribute name="title">
+ <string>Filters</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="3">
+ <column>
+ <property name="text">
+ <string>Action</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Affects</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Condition</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Expression</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lvFilters</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Currently configured filters</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see the list of currently configured filters and manage them as well as add new filters. Managing them is fairly easy:&lt;ul&gt;
+&lt;li&gt;To &lt;b&gt;add&lt;/b&gt; a new filter, specify its properties in the box below labeled &lt;i&gt;Filter properties&lt;/i&gt; and press the &lt;i&gt;Add&lt;/i&gt; button in the lower right corner.&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;Modifying&lt;/b&gt; an existing filter is done in a similar manner: simply select the filter you would like to edit in the list and change its properties in the box below.&lt;/li&gt;
+&lt;li&gt;Finally, to &lt;b&gt;remove&lt;/b&gt; a filter, select it in the list and press the button labeled &lt;i&gt;Remove&lt;/i&gt; in the lower right corner.&lt;/li&gt;&lt;/ul&gt;
+You can also enable or disable certain filters temporarily by checking or unchecking the box next to them; those filters whose boxes are checked are considered enabled and will be honored by KNewsTicker.&lt;br&gt;
+Note that the filters are processed from the top to the bottom so that of two filters which might nullify each other (like "Show...does not contain KDE" and "Show...contains KDE") only the one which is lower in the list will take effect.</string>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>Spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="2" column="2">
+ <property name="name">
+ <cstring>bRemoveFilter</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>R&amp;emove</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Remove selected filter</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to remove the selected filter from the list.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>bAddFilter</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add configured filter</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to add the configured filter to the list.</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>gbFilterProperties</cstring>
+ </property>
+ <property name="title">
+ <string>Filter Properties</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KComboBox" row="0" column="0" rowspan="1" colspan="3">
+ <item>
+ <property name="text">
+ <string>Hide</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Show</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>comboFilterAction</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Action for this filter</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define what should happen if this filter matches (e.g. whether the matching articles should be shown or hidden).</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>Spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="3">
+ <property name="name">
+ <cstring>lArticles</cstring>
+ </property>
+ <property name="text">
+ <string>articles from</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="0" column="4" rowspan="1" colspan="2">
+ <item>
+ <property name="text">
+ <string>all news sources</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>comboFilterNewsSource</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Affected news sources</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify which news sources (or all of them) are affected. Note that only the news sources which have been activated on the &lt;i&gt;News sources&lt;/i&gt; tab are shown in this combo box.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="6">
+ <property name="name">
+ <cstring>lwhose</cstring>
+ </property>
+ <property name="text">
+ <string>whose</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="5" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>leFilterExpression</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Keyword/Expression</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can type a keyword or expression to be used for this filter which depends on the condition you selected in the combo box at the right:&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;contain&lt;/b&gt;, &lt;b&gt;does not contain&lt;/b&gt; - you should probably enter a keyword here, like "KDE", "Baseball" or "Business". The keyword is not case-sensitive so it does not matter whether you type "kde", "KDE" or "kDe".&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;equals&lt;/b&gt;, &lt;b&gt;does not equal&lt;/b&gt; - enter a phrase or expression here to have the filter match only those articles whose headlines match &lt;b&gt;exactly&lt;/b&gt; the text you typed. The phrase you type will be considered to be case-sensitive, so it makes a difference whether you show articles which contain "Boeing" or "BOEING".&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;matches&lt;/b&gt; - a regular expression is expected. Recommended only if you are familiar with regular expressions, i.e. it should be used by advanced users only.&lt;/li&gt;&lt;/ul&gt;</string>
+ </property>
+ </widget>
+ <spacer row="0" column="7">
+ <property name="name">
+ <cstring>Spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>lHeadlines</cstring>
+ </property>
+ <property name="text">
+ <string>headlines</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="1" column="2" rowspan="1" colspan="3">
+ <item>
+ <property name="text">
+ <string>contain</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>do not contain</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>equal</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>do not equal</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>match</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>comboFilterCondition</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Condition for this filter</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This combo box lets you specify the condition under which the keyword/expression you entered in the input field at the right will match. You can select one of the following values:&lt;ul&gt;
+&lt;li&gt;contain - the filter matches if the headline contains the keyword.&lt;/li&gt;
+&lt;li&gt;does not contain - the filter matches if the headline does not contain the keyword.&lt;/li&gt;
+&lt;li&gt;equals - the filter matches if the headline equals the expression.&lt;/li&gt;
+&lt;li&gt;does not equal - the filter matches if the headline does not equal the expression.&lt;/li&gt;
+&lt;li&gt;matches - the filter matches if the expression matches the headline. The expression you typed at the right will be considered a regular expression in this mode.&lt;/li&gt;</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tabScrollerPreferences</cstring>
+ </property>
+ <attribute name="title">
+ <string>Scroller</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gbScrollerBehaviour</cstring>
+ </property>
+ <property name="title">
+ <string>Behavior</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>Layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lScrollingSpeed</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Scrolling speed:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sliderScrollSpeed</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling speed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define how fast the text should be scrolling. If you only have a little space on your taskbar (and therefore a rather small news ticker), you should probably set this to a slower speed so that you have a chance to read the headlines. For wider news tickers (and better eyes), faster scrolling is probably appropriate so that you do not have to wait too long for the next headline.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout6_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lSlow</cstring>
+ </property>
+ <property name="text">
+ <string>Slow</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling speed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define how fast the text should be scrolling. If you only have a little space on your taskbar (and therefore a rather small news ticker), you should probably set this to a slower speed so that you have a chance to read the headlines. For wider news tickers (and better eyes), faster scrolling is probably appropriate so that you do not have to wait too long for the next headline.</string>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>sliderScrollSpeed</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="tickInterval">
+ <number>10</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling speed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define how fast the text should be scrolling. If you only have a little space on your taskbar (and therefore a rather small news ticker), you should probably set this to a slower speed so that you have a chance to read the headlines. For wider news tickers (and better eyes), faster scrolling is probably appropriate so that you do not have to wait too long for the next headline.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lFast</cstring>
+ </property>
+ <property name="text">
+ <string>Fast</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling speed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can define how fast the text should be scrolling. If you only have a little space on your taskbar (and therefore a rather small news ticker), you should probably set this to a slower speed so that you have a chance to read the headlines. For wider news tickers (and better eyes), faster scrolling is probably appropriate so that you do not have to wait too long for the next headline.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lDirectionOfScrolling_2</cstring>
+ </property>
+ <property name="text">
+ <string>Di&amp;rection of scrolling:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboDirection</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Direction of scrolling</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These options allow you to define in what direction the text should be scrolled, e.g. to the left or to the right, upwards or downwards.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>To the Left</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>To the Right</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Upwards</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Downwards</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Upwards, Rotated</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Downwards, Rotated</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>comboDirection</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Direction of scrolling</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>These options allow you to define in what direction the text should be scrolled, e.g. to the left or to the right, upwards or downwards. Rotated means the text is rotated 90 degrees.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gbScrollerAppearance</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>Appearance</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lHighlightedColor_2</cstring>
+ </property>
+ <property name="text">
+ <string>H&amp;ighlighted color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>colorHighlighted</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Highlighted color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click the button at the right to open a convenient color-selection dialog which lets you choose the color of the headlines when they are highlighted (when you move the mouse over them).</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>colorHighlighted</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Highlighted color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to open a convenient color-selection dialog which lets you choose the color of the headlines when they are highlighted (when you move the mouse over them).</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lBackgroundColor_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>colorBackground</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Background color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click the button at the right to open a convenient color-selection dialog which lets you choose the background color of the scrolling text.</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>colorBackground</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Background color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to open a convenient color-selection dialog which lets you choose the background color of the scrolling text.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lForegroundColor_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Foreground color:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>colorForeground</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Foreground color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click the button at the right to open a convenient color-selection dialog which lets you choose the color of the scrolling text.</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>colorForeground</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Foreground color</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to open a convenient color-selection dialog which lets you choose the color of the scrolling text.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lFont_2</cstring>
+ </property>
+ <property name="text">
+ <string>F&amp;ont:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>bChooseFont</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling text font</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click on the button at the right labeled &lt;i&gt;Choose Font...&lt;/i&gt; to choose the font which will be used for the scrolling text. Please note that certain fonts are harder to read than others, especially when they are used as scrolling text. You should probably choose a font which can be easily read while it is moving.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>bChooseFont</cstring>
+ </property>
+ <property name="text">
+ <string>Choose Font...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Scrolling text font</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click here to choose the font which will be used for the scrolling text. Please note that certain fonts are harder to read than others, especially when they are used as a scrolling text. You should probably choose a font which can be easily read while it is moving.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gbMiscScrollingOptions</cstring>
+ </property>
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>cbScrollMostRecentOnly</cstring>
+ </property>
+ <property name="text">
+ <string>Scroll the most recent headlines onl&amp;y</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show only the most recent headline for each news site in the scroller</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this button to show only the most recent headline for each news site. </string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>cbShowIcons</cstring>
+ </property>
+ <property name="text">
+ <string>Show icons</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show icons in the scrolling text</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to make KNewsTicker show the icons of the news site to which each headline belongs. This makes associating a headline to a news site very easy but takes up some space in the text.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>cbSlowedScrolling</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Temporarily slowed scrolling</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Slow the scrolling down when mouse points at the scroller</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to make KNewsTicker slow the scrolling down when you move the mouse cursor over the scrolling text. This makes clicking on items and dragging the icons (if enabled) away a lot easier.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>cbUnderlineHighlighted</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Underline highlighted headline</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Underline the currently highlighted headline</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to have the currently highlighted headline (e.g. the headline which is currently under the mouse cursor) underlined.</string>
+ </property>
+ </widget>
+ </grid>
+ </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>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>sliderMouseWheelSpeed</tabstop>
+ <tabstop>niInterval</tabstop>
+ <tabstop>cbCustomNames</tabstop>
+ <tabstop>lvNewsSources</tabstop>
+ <tabstop>bAddNewsSource</tabstop>
+ <tabstop>bModifyNewsSource</tabstop>
+ <tabstop>bRemoveNewsSource</tabstop>
+ <tabstop>lvFilters</tabstop>
+ <tabstop>comboFilterAction</tabstop>
+ <tabstop>comboFilterNewsSource</tabstop>
+ <tabstop>comboFilterCondition</tabstop>
+ <tabstop>leFilterExpression</tabstop>
+ <tabstop>bAddFilter</tabstop>
+ <tabstop>bRemoveFilter</tabstop>
+ <tabstop>sliderScrollSpeed</tabstop>
+ <tabstop>comboDirection</tabstop>
+ <tabstop>bChooseFont</tabstop>
+ <tabstop>colorForeground</tabstop>
+ <tabstop>colorBackground</tabstop>
+ <tabstop>colorHighlighted</tabstop>
+ <tabstop>cbScrollMostRecentOnly</tabstop>
+ <tabstop>cbShowIcons</tabstop>
+ <tabstop>cbSlowedScrolling</tabstop>
+ <tabstop>cbUnderlineHighlighted</tabstop>
+ <tabstop>tabScrollerPreferences</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">kcolorbutton.h</include>
+ <include location="global" impldecl="in declaration">kcombobox.h</include>
+ <include location="global" impldecl="in declaration">klineedit.h</include>
+ <include location="global" impldecl="in declaration">klistview.h</include>
+ <include location="global" impldecl="in declaration">knuminput.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/knewsticker/knewstickerstub/Makefile.am b/knewsticker/knewstickerstub/Makefile.am
new file mode 100644
index 00000000..200e2eac
--- /dev/null
+++ b/knewsticker/knewstickerstub/Makefile.am
@@ -0,0 +1,13 @@
+INCLUDES = -I$(top_srcdir)/knewsticker/common $(all_includes)
+
+bin_PROGRAMS = knewstickerstub
+
+servicedir = $(kde_appsdir)/.hidden
+service_DATA = knewstickerstub.desktop
+
+METASOURCES = AUTO
+
+knewstickerstub_SOURCES = knewstickerstub.cpp
+knewstickerstub_LDADD = $(LIB_KIO) ../common/libknewstickercommon.la
+knewstickerstub_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
diff --git a/knewsticker/knewstickerstub/knewstickerstub.cpp b/knewsticker/knewstickerstub/knewstickerstub.cpp
new file mode 100644
index 00000000..7f5960a4
--- /dev/null
+++ b/knewsticker/knewstickerstub/knewstickerstub.cpp
@@ -0,0 +1,80 @@
+/*
+ * knewstickerstub.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include <dcopclient.h>
+
+
+#include "configaccess.h"
+
+static const char name[] = "knewstickerstub";
+static const char verbname[] = I18N_NOOP("KNewsTickerStub");
+static const char version[] = "0.3";
+static const char description[] = I18N_NOOP("A frontend to the KNewsTicker configuration");
+static const char copyright[] = I18N_NOOP("(c)2000, 2001 Frerich Raabe");
+
+static const KCmdLineOptions options[] =
+{
+ {"a", 0, 0},
+ {"addrdf <url>", I18N_NOOP("Add the RDF/RSS file referenced by <url>"), 0},
+ KCmdLineLastOption
+};
+
+int main(int argc, char **argv)
+{
+ KLocale::setMainCatalogue("knewsticker");
+
+ KAboutData aboutData(name, verbname, version, description,
+ KAboutData::License_BSD, copyright);
+ aboutData.addAuthor("Frerich Raabe", I18N_NOOP("Author"), "raabe@kde.org");
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KApplication app(false, false);
+
+ DCOPClient *dcopClient = app.dcopClient();
+ dcopClient->attach();
+
+ KConfig cfg(QString::fromLatin1("knewsticker_panelappletrc"), false, false);
+ ConfigAccess configFrontend(&cfg);
+
+ if (args->isSet("addrdf")) {
+ QStringList newsSources = configFrontend.newsSources();
+
+ // TODO: Use the "Suggest" functionality from addnewssourcedlgimpl.cpp here.
+ QString name = i18n("Unknown");
+ if (newsSources.contains(name))
+ for (unsigned int i = 0; ; i++)
+ if (!newsSources.contains(i18n("Unknown %1").arg(i))) {
+ name = i18n("Unknown %1").arg(i);
+ break;
+ }
+
+ newsSources += name;
+
+ QString sourceFile = QString(args->getOption("addrdf"));
+ configFrontend.setNewsSource(NewsSourceBase::Data(name, sourceFile));
+ configFrontend.setNewsSources(newsSources);
+
+ QByteArray data;
+ dcopClient->send("knewsticker", "KNewsTicker", "reparseConfig()", data);
+ }
+
+ args->clear();
+
+ return 0;
+}
diff --git a/knewsticker/knewstickerstub/knewstickerstub.desktop b/knewsticker/knewstickerstub/knewstickerstub.desktop
new file mode 100644
index 00000000..1ce2238b
--- /dev/null
+++ b/knewsticker/knewstickerstub/knewstickerstub.desktop
@@ -0,0 +1,202 @@
+[Desktop Entry]
+Name=KNewsTicker Config Frontend
+Name[be]=Праграма наÑÑ‚Ð°ÑžÐ»ÐµÐ½Ð½Ñ KNewsTicker
+Name[bg]=ÐаÑтройване четеца на новини
+Name[bn]=কে-নিউজ-টিকার কনফিগ ফà§à¦°à¦¨à§à¦Ÿ-à¦à¦¨à§à¦¡
+Name[bs]=KNewsTicker konfiguracijski frontend
+Name[ca]=Interfície de configuració per a KNewsTicker
+Name[cs]=Nastavení KNewsTickeru
+Name[da]=KNewsTicker config-grænseflade
+Name[de]=Newsticker-Einrichtung
+Name[el]=ΠÏόγÏαμμα ÏÏθμισης KNewsTicker
+Name[eo]=Agordilo por Novaĵprezentilo
+Name[es]=Interfaz de configuración de KNewsTicker
+Name[et]=Uudistejälgija seadistamine
+Name[eu]=KNewsTicker konfigurazio interfazea
+Name[fa]=پایانۀ پیکربندی KNewsTicker
+Name[fi]=KNewsTickerin asetusten käyttöliittymä
+Name[fr]=Interface pour la configuration du téléscripteur
+Name[gl]=Interface de Configuración de KNewsTicker
+Name[he]=ממשק לקביעת התצורה של KNewsTicker
+Name[hu]=Grafikus felületű beállítóprogram a KNewsTickerhez
+Name[is]=Stillingatól KNewsTicker
+Name[it]=Interfaccia per la configurazione di KNewsTicker
+Name[ja]=ニュースティッカー設定フロントエンド
+Name[ka]=KNewsTicker კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ
+Name[kk]=KNewsTicker баптауы
+Name[km]=ផ្នែក​ážáž¶áž„​មុážâ€‹ážŸáž˜áŸ’រាប់​កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ KNewsTicker
+Name[lt]=KNewsTicker derinimo naudotojo sÄ…saja
+Name[mk]=Конфигурација на интерфејÑот на KNewsTicker
+Name[nb]=Oppsettsmodul for KNewsTicker
+Name[nds]=KNewsTicker-Inrichten
+Name[ne]=केडीई नà¥à¤¯à¥‚ज टिकर कनà¥à¤«à¤¿à¤— फà¥à¤°à¤¨à¥à¤Ÿà¤‡à¤¨à¥à¤¡
+Name[nl]=KNewsTicker configuratie-interface
+Name[nn]=Oppsett av KNewsTicker
+Name[pl]=Interfejs konfiguracji KNewsTickera
+Name[pt]=Configuração das Notícias
+Name[pt_BR]=Interface de configuração do KNewsTicker
+Name[ro]=Interfaţă configurare Ştiri Internet
+Name[ru]=Параметры монитора новоÑтей
+Name[se]=Heivet KNewsTicker-lavtta
+Name[sk]=Nastavenie KNewsTicker
+Name[sl]=Nastavitveni vmesnik programa KNewsTicker
+Name[sr]=Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð·Ð° подешавање KNewsTicker-а
+Name[sr@Latn]=Interfejs za podešavanje KNewsTicker-a
+Name[sv]=Gränssnitt för anpassning av nyhetsövervakaren
+Name[ta]=கேசெயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à¯ கடà¯à®Ÿà®®à¯ˆ à®®à¯à®©à¯à®©à®¿à®²à¯ˆ
+Name[tg]=KПешохири Танзими Дидабони Ðхборот
+Name[tr]=KDE Haber Gözlemcisi Yapılandırma Önyüzü
+Name[uk]=Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ KNewsTicker
+Name[zh_CN]=KNewsTicker é…ç½®å‰ç«¯
+Name[zh_HK]=KNewsTicker 設定å‰ç«¯
+Name[zh_TW]=KNewsTicker 設定å‰ç«¯
+Comment=A frontend for the KNewsTicker configuration
+Comment[af]='n voorprogram vir die K-nuustikker opstelling
+Comment[ar]=واجهة لضبط مجدد الأخبار
+Comment[az]=KNewsTicker quraşdırması üçün bir axtar üz
+Comment[be]=Праграма наÑÑ‚Ð°ÑžÐ»ÐµÐ½Ð½Ñ KNewsTicker
+Comment[bg]=Графичен Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð·Ð° наÑтройки на четеца на новини
+Comment[bn]=কে-নিউজ-টিকার কনফিগারেশনের জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ ফà§à¦°à¦¨à§à¦Ÿ-à¦à¦¨à§à¦¡
+Comment[bs]=Frontend za podešavanje KNewsTicker-a
+Comment[ca]=Una interfície per a la configuració del KNewsTicker
+Comment[cs]=Rozhraní pro nastavení KNewsTickeru
+Comment[cy]=Blaen-wyneb i'r ffurweddiad KTicerNewyddion
+Comment[da]=En grænseflade til indstilling af KNewsTicker
+Comment[de]=Eine Oberfläche zur Einrichtung von KNewsTicker
+Comment[el]=Ένα Ï€ÏόγÏαμμα για τη ÏÏθμιση του KNewsTicker
+Comment[eo]=Agordilofasado por Novaĵprezentilo
+Comment[es]=Un interfaz para la configuración de KNewsTicker
+Comment[et]=Uudistejälgija seadistamise kasutajaliides
+Comment[eu]=KNewsTicker konfigurazioaren interfazea
+Comment[fa]=یک پایانه برای پیکربندی KNewsTicker
+Comment[fi]=Käyttöliittymä KNewsTicker-asetuksiin
+Comment[fr]=Interface pour la configuration de KNewsTicker
+Comment[ga]=Comhéadan le haghaidh chumraíocht KNewsTicker
+Comment[gl]=Unha interfaz para a configuración de KNewsTicker
+Comment[he]=ממשק לקביעת התצורה של KNewsTicker
+Comment[hi]=के-नà¥à¤¯à¥‚ज टिकर कॉनà¥à¤«à¤¼à¤¿à¤—रेशन के लिठफà¥à¤°à¤¨à¥à¤Ÿà¤à¤£à¥à¤¡
+Comment[hr]=SuÄelje za podeÅ¡avanje KNewsTicker-a
+Comment[hu]=Előtétprogram a KNewsTicker beállításához
+Comment[is]=Stillingatól fyrir Fréttastrimilinn
+Comment[it]=Un'interfaccia grafica per la configurazione di KNewsTicker
+Comment[ja]=KNewsTicker (ニュースティッカー) ã®è¨­å®šãƒ•ãƒ­ãƒ³ãƒˆã‚¨ãƒ³ãƒ‰
+Comment[ka]=KNewsTicker კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ˜áƒ¡
+Comment[kk]=KNewsTicker баптауының интерфейÑÑ–
+Comment[km]=ផ្នែក​ážáž¶áž„​មុážâ€‹ážŸáž˜áŸ’រាប់​កំណážáŸ‹â€‹ážšáž…នាសម្ពáŸáž“្ធ KNewsTicker
+Comment[ko]=Kìƒˆì†Œì‹ í‘œì‹œê¸° 설정 ë„구
+Comment[lt]=Naudotojo sÄ…saja, skirta KNewsTicker derinimui
+Comment[lv]=KZiņuTikkera konfigurēšanas frontends
+Comment[mk]=Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð·Ð° конфигурацијата на KNewsTicker
+Comment[mn]=KNewsTicker-ийн Тохируулгыг ҮзүүлÑÑ…
+Comment[ms]=Bahagian depan untuk penyelarasan KNewsTicker
+Comment[mt]=Faċċata għall-konfigurazzjoni ta' KNewsTicker
+Comment[nb]=Program for oppsett av KNewsTicker
+Comment[nds]=En Böversiet för de KNewsTicker-Instellen
+Comment[ne]=केडीई नà¥à¤¯à¥‚ज टिकर कनà¥à¤«à¤¿à¤—रेसनका लागि फà¥à¤°à¤¨à¥à¤Ÿà¤‡à¤¨à¥à¤¡
+Comment[nl]=Een grafische schil voor de configuratie van KNewsTicker
+Comment[nn]=Program for oppsett av KNewsTicker
+Comment[nso]=Mafelelo a kapele a peakanyo ya Seswai sa KDitaba
+Comment[pl]=Konfiguracja KNewsTickera
+Comment[pt]=Configuração do extractor de notícias
+Comment[pt_BR]=Uma interface para configuração do KNewsTicker
+Comment[ro]=O interfaţă de configurare pentru Ştiri Internet
+Comment[ru]=ГрафичеÑÐºÐ°Ñ Ð¾Ð±Ð¾Ð»Ð¾Ñ‡ÐºÐ° Ð´Ð»Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð° новоÑтей
+Comment[se]=Prográmma mainna heiveha KNewsTicker
+Comment[sk]=Nastavenie KNewsTicker
+Comment[sl]=Vmesnik za nastavitve programa KNewsTicker
+Comment[sr]=Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÑ˜Ñ Ð·Ð° подешавање KNewsTicker-а
+Comment[sr@Latn]=Interfejs za podešavanje KNewsTicker-a
+Comment[sv]=Gränssnitt för anpassning av nyhetsövervakaren
+Comment[ta]=செயà¯à®¤à®¿ அறிவிபà¯à®ªà®¤à®±à¯à®•à®¾à®© à®®à¯à®©à¯ அமைபà¯à®ªà¯
+Comment[tg]=Муқоваи графикӣ барои танзими KДидабони Ðхборот
+Comment[th]=ฟรอนต์เอนด์สำหรับปรับà¹à¸•à¹ˆà¸‡à¸•à¸±à¹‹à¸§à¸‚่าว - K
+Comment[tr]=KDE Haber Gözlemcisi yapılandırması için önyüz
+Comment[uk]=Зовнішній Ñ–Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ KNewsTicker
+Comment[ven]=U iswa phanda uitela Mavhekanyele a musengulusi wa mafhungo a K
+Comment[xh]=Umphambili wesiphelo soqwalaselo se KNewsTicker
+Comment[zh_CN]=KNewsTicker é…置的å‰ç«¯
+Comment[zh_HK]=設定 KNewsTicker çš„å‰ç«¯ç¨‹å¼
+Comment[zh_TW]=設定 KNewsTicker çš„å‰ç«¯
+Comment[zu]=Isiqalo sesiphelo sezindaba zesiphelo senhlanganiso lomlekeleli wezindaba ze K
+MimeType=text/rdf;text/rss
+Exec=knewstickerstub -a %u
+Icon=knode
+NoDisplay=true
+Type=Application
+Terminal=false
+InitialPreference=8
+Actions=Add;
+X-KDE-StartupNotify=false
+
+[Desktop Action Add]
+Exec=knewstickerstub -a %u
+Name=Use with KNewsTicker
+Name[af]=Gebruik met K-nuustikker
+Name[ar]=استخدمه مع مجدد الأخبار
+Name[az]=KNewsTicker ilə birgə işlədin
+Name[be]=ВыкарыÑтаць з KNewsTicker
+Name[bg]=Използване Ñ Ñ‡ÐµÑ‚ÐµÑ†Ð° на новини
+Name[bn]=কে-নিউজ-টিকারের সঙà§à¦—ে বà§à¦¯à¦¬à¦¹à¦¾à¦°
+Name[br]=Implijit gant KNewsTicker
+Name[bs]=Koristite sa KNewsTicker-om
+Name[ca]=Empra amb el KNewsTicker
+Name[cs]=Použít s KNewsTickerem
+Name[cy]=Defnyddio efo KTicerNewyddion
+Name[da]=Brug med KNewsTicker
+Name[de]=Zur Benutzung mit KNewsticker
+Name[el]=ΧÏήση με το KNewsTicker
+Name[eo]=Uzu kun Novaĵprezentilo
+Name[es]=Usar con KNewsTicker
+Name[et]=Kasutamine KNewsTickeriga
+Name[eu]=Erabili KNewsTicker-ekin
+Name[fa]=استÙاده با KNewsTicker
+Name[fi]=Käytä KNewsTicker-ohjelman kanssa
+Name[fr]=Utiliser avec KNewsTicker
+Name[gl]=Usar con KNewsTicker
+Name[he]=השתמש ב- KNewsTicker
+Name[hi]=के-नà¥à¤¯à¥‚ज टिकर के साथ इसà¥à¤¤à¥‡à¤®à¤¾à¤² करें
+Name[hr]=Koristi sa KNewsTicker-om
+Name[hu]=A KNewsTicker egyik alkotórésze
+Name[id]=Gunakan dengan KNewsTicker
+Name[is]=Notist með 'Fréttastrimli'
+Name[it]=Utilizza con KNewsTicker
+Name[ja]=ニュースティッカーを使用
+Name[ka]=KNewsTicker გáƒáƒ›áƒáƒ§áƒ”ნებáƒ
+Name[kk]=KNewsTicker-мен бірге қолданылады
+Name[km]=ប្រើ​ជា​មួយ KNewsTicker
+Name[ko]=Kìƒˆì†Œì‹ í‘œì‹œê¸°ì™€ ê°™ì´ ì”€
+Name[lt]=Naudojama su KNewsTicker
+Name[lv]=Lieto ar KZiņuTikkeru
+Name[mk]=КориÑти Ñо KNewsTicker
+Name[mn]=KNewsTicker Ñ‚Ñй Ñ…ÑÑ€ÑглÑÑ…
+Name[ms]=Digunakan bersama KNewsTicker
+Name[mt]=Uża ma' KNewsTicker
+Name[nb]=Bruk med KNewsTicker
+Name[nds]=Bruuk dat mit KNewsTicker
+Name[ne]=केडीई नà¥à¤¯à¥‚ज टिकरसà¤à¤— पà¥à¤°à¤¯à¥‹à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Name[nl]=Gebruik met KNewsTicker
+Name[nn]=Bruk med KNewsTicker
+Name[nso]=Somisa le Seswai sa KDitaba
+Name[pl]=Użyj za pomocą KNewsTickera
+Name[pt]=Usar com as Notícias
+Name[pt_BR]=Usar com KNewsTicker
+Name[ro]=Utilizează cu Ştiri Internet
+Name[ru]=ИÑпользуетÑÑ Ñ KNewsTicker
+Name[se]=Geavat ovttas KNewsTicker:in
+Name[sk]=Použiť s KNewsTicker
+Name[sl]=Uporabite s KNewsTicker
+Name[sr]=КориÑти Ñа KNewsTicker-ом
+Name[sr@Latn]=Koristi sa KNewsTicker-om
+Name[sv]=Använd med nyhetsövervakaren
+Name[ta]=கேசெயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à¯
+Name[tg]=Бо KДидабони Ðхборот иÑтифода мегардад
+Name[th]=ใช้à¸à¸±à¸šà¸•à¸±à¹‹à¸§à¸‚่าว - K
+Name[tr]=KDE Haber Gözlemcisi ile birlikte kullanın
+Name[uk]=Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð· KNewsTicker
+Name[ven]=Shumisani na Musengulusi wa mafhungo
+Name[xh]=Yisebenzise ne KNewsTicker
+Name[zh_CN]=和 KNewsTicker 一起使用
+Name[zh_HK]=和 KNewsTicker 一起使用
+Name[zh_TW]=和 KNewsTicker 一起使用
+Name[zu]=Yisebenzise nomlungiseleli wezindaba ze K
+X-KDE-StartupNotify=false
diff --git a/knewsticker/knt-0.1-0.2.pl b/knewsticker/knt-0.1-0.2.pl
new file mode 100755
index 00000000..2ca9e30c
--- /dev/null
+++ b/knewsticker/knt-0.1-0.2.pl
@@ -0,0 +1,104 @@
+#!/usr/bin/perl
+# convert which file to which file?
+# I did nto bother to build in any checks. so that file better exists and it is readable and the data will
+# be written to a writeable directory. Since this is an old config file, it is safe to assume, that is the
+# case.
+
+#$file = $ARGV[0];
+
+# Take your hands off the rest unless you darn well knwo what you are doing
+##################################################################################
+##################################################################################
+
+open(INFO, $file);
+ my ($section, %data);
+
+#read in all the data, split it up into hashes. Thanks again to malte for much input
+while (<>) {
+ if (/\[(.*)\]/) {
+ $sections{$section} = {%data} if $section;
+ $section = $1;
+ undef %data;
+ next;
+ }
+$data{$1} = $2 if /^([^=]*)=(.*)$/;
+}
+
+$sections{$section} = {%data} if $section;
+
+# do the data writing magic
+#first of all be check how many old news souerces we have
+
+$sources = $sections{'General'}->{'News sources'};
+#gather all news sources into a very pretty string before we write the global section
+#also give some feedback to the user
+for my $i (0..($sources-1)) {
+ $all .= "," .$sections{"News source #$i"}->{'Name'};
+ $all =~s/^,//;
+}
+
+# write the main section
+print "[KNewsTicker]\n";
+
+while (($key,$dat) = each(%{$sections{'General'}})) {
+ if ($key ne "News sources") {
+ if ($key eq "Interval") {
+ $key="Update interval";
+ }
+ if ($key eq "Scroll most recent only") {
+ $key="Scroll most recent headlines only";
+ }
+ print "$key=$dat\n";
+ } else {
+ print "News sources=".$all."\n";
+ }
+
+}
+
+# next merge the old scrolling section into the KNewticker Section
+while (($key,$dat) = each(%{$sections{'Scrolling'}})) {
+ if ($key eq "Background") {
+ $key="Background color";
+
+ }
+ if ($key eq "Foreground") {
+ $key="Foreground color";
+ }
+ if ($key eq "Highlighted") {
+ $key="Highlighted color";
+ }
+ if ($key eq "Underline highlighted") {
+ $key="Underline highlighted headlines";
+ }
+ if (($key eq "Direction") && ($dat eq "Left")) {
+ $key ="Scrolling direction";
+ $dat = "0";
+
+}
+if (($key eq "Direction") && ($dat ne "Left")) {
+
+ $key ="Scrolling direction";
+
+}
+if ($key eq "Speed") {
+ $key="Scrolling speed";
+}
+
+
+
+ print "$key=$dat\n";
+}
+
+# next we write the news sources, making sure we have the correct headers
+for my $i (0..($sources-1)) {
+
+print "\n[" .$sections{"News source #$i"}->{'Name'} ."]\n";
+ while (($key,$dat) = each(%{$sections{"News source #$i"}})) {
+ if ($key ne "Address") {
+ print "$key=$dat\n";
+ } else {
+ print "Source file=".$dat."\n";
+ }
+ }
+}
+
diff --git a/knewsticker/kntsrcfilepropsdlg/Makefile.am b/knewsticker/kntsrcfilepropsdlg/Makefile.am
new file mode 100644
index 00000000..9782b90c
--- /dev/null
+++ b/knewsticker/kntsrcfilepropsdlg/Makefile.am
@@ -0,0 +1,12 @@
+INCLUDES = -I$(top_srcdir)/knewsticker/common -I$(top_srcdir)/librss $(all_includes)
+
+kde_module_LTLIBRARIES = libkntsrcfilepropsdlg.la
+
+libkntsrcfilepropsdlg_la_SOURCES = kntsrcfilepropsdlg.cpp \
+ kntsrcfilepropsdlgwidget.ui
+libkntsrcfilepropsdlg_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+libkntsrcfilepropsdlg_la_LIBADD = $(LIB_KIO) ../common/libknewstickercommon.la ../../librss/librss.la
+libkntsrcfilepropsdlg_la_METASOURCES = AUTO
+
+service_DATA = kntsrcfilepropsdlg.desktop
+servicedir = $(kde_servicesdir)
diff --git a/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.cpp b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.cpp
new file mode 100644
index 00000000..93bc71cc
--- /dev/null
+++ b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.cpp
@@ -0,0 +1,130 @@
+/*
+ * kntsrcfilepropsdlg.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "kntsrcfilepropsdlg.h"
+#include "kntsrcfilepropsdlgwidget.h"
+#include "xmlnewsaccess.h"
+#include "newsiconmgr.h"
+
+#include <article.h>
+#include <document.h>
+#include <loader.h>
+
+#include <kapplication.h>
+#include <klistbox.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kurllabel.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+
+#include <qmultilineedit.h>
+#include <qvbox.h>
+
+using namespace RSS;
+
+class ArticleListBoxItem : public QListBoxText
+{
+ public:
+ ArticleListBoxItem( QListBox *listbox, const Article &article );
+
+ const Article &article() const { return m_article; }
+
+ private:
+ Article m_article;
+};
+
+ArticleListBoxItem::ArticleListBoxItem( QListBox *listbox, const Article &article )
+ : QListBoxText( listbox ),
+ m_article( article )
+{
+ setText( article.title() );
+}
+
+KntSrcFilePropsDlg::KntSrcFilePropsDlg(KPropertiesDialog *props)
+ : KPropsDlgPlugin(props)
+{
+ m_child = new KntSrcFilePropsDlgWidget(properties->addVBoxPage(i18n("News Resource")));
+ connect(m_child->urlName, SIGNAL(leftClickedURL(const QString &)),
+ SLOT(slotOpenURL(const QString &)));
+ connect(m_child->lbArticles, SIGNAL(executed(QListBoxItem *)),
+ SLOT(slotClickedArticle(QListBoxItem *)));
+
+ Loader *loader = Loader::create();
+ connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)),
+ SLOT(slotConstructUI(Loader *, Document, Status)));
+ loader->loadFrom(props->item()->url(), new FileRetriever);
+
+ connect(NewsIconMgr::self(), SIGNAL(gotIcon(const KURL &, const QPixmap &)),
+ SLOT(slotGotIcon(const KURL &, const QPixmap &)));
+
+ m_child->show();
+}
+
+void KntSrcFilePropsDlg::slotConstructUI(Loader *, Document doc, Status status)
+{
+ if (status != RSS::Success)
+ return;
+
+ KURL iconURL = doc.link();
+ iconURL.setEncodedPathAndQuery(QString::fromLatin1("/favicon.ico"));
+ NewsIconMgr::self()->getIcon(iconURL);
+
+ m_child->urlName->setText(doc.title());
+ m_child->urlName->setURL(doc.link().url());
+
+ m_child->mleDescription->setText(doc.description());
+
+ Article::List::ConstIterator it = doc.articles().begin();
+ Article::List::ConstIterator end = doc.articles().end();
+ for (; it != end; ++it)
+ new ArticleListBoxItem(m_child->lbArticles, *it);
+}
+
+void KntSrcFilePropsDlg::slotOpenURL(const QString &url)
+{
+ kapp->invokeBrowser(url);
+}
+
+void KntSrcFilePropsDlg::slotGotIcon(const KURL &, const QPixmap &pixmap)
+{
+ m_child->pixmapIcon->setPixmap(pixmap);
+}
+
+void KntSrcFilePropsDlg::slotClickedArticle(QListBoxItem *item)
+{
+ ArticleListBoxItem *articleItem = static_cast<ArticleListBoxItem *>(item);
+ slotOpenURL(articleItem->article().link().url());
+}
+
+QObject *KntSrcFilePropsFactory::createObject(QObject *parent, const char *,
+ const char *classname, const QStringList &)
+{
+ if (QString::fromLatin1(classname) == "KPropsDlgPlugin")
+ {
+ if (!parent->inherits("KPropertiesDialog"))
+ return 0L;
+
+ QObject *obj = new KntSrcFilePropsDlg(static_cast<KPropertiesDialog *>(parent));
+ return obj;
+ }
+ return 0L;
+}
+
+extern "C"
+{
+ KDE_EXPORT void *init_libkntsrcfilepropsdlg()
+ {
+ KGlobal::locale()->insertCatalogue( "knewsticker" );
+ return new KntSrcFilePropsFactory();
+ }
+}
+
+#include "kntsrcfilepropsdlg.moc"
diff --git a/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.desktop b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.desktop
new file mode 100644
index 00000000..6902d9b3
--- /dev/null
+++ b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Type=Service
+Name=KNewsticker Source File Properties Page
+Name[af]=K-nuustikker Bron Lêer Eienskappe Bladsy
+Name[ar]=صÙحة خصائص مل٠مصدر مجدد الأخبار
+Name[az]=KNewsticker Mənbə Faylı Xüsusiyyətləri Səhifəsi
+Name[bg]=ÐаÑтройване на файла източник на KNewsticker
+Name[bn]=কে-নিউজ-টিকার উতà§â€à¦¸ ফাইল বৈশিষà§à¦Ÿà§à¦¯à¦¾à¦¬à¦²à§€ পাতা
+Name[bs]=KNewsticker stranica sa osobinama izvorne datoteke
+Name[ca]=Pàgina de propietats dels fitxers font del KNewsticker
+Name[cs]=Popis vlastností zdrojového souboru programu KNewsticker
+Name[cy]=Tudalen Priodweddau Ffeil Tarddiad KTicerNewyddion
+Name[da]=KNewsTickers side med egenskaber for kildefil
+Name[de]=KNewsticker: Einstellungsseite für Quelldateien
+Name[el]=Σελίδα ιδιοτήτων αÏχείου πηγής KNewsticker
+Name[eo]=EcopaÄo por Novaĵprezentilaj fontdosieroj
+Name[es]=Página de propiedades de archivos fuente de KNewsticker
+Name[et]=KNewstickeri lähtefaili omaduste dialoog
+Name[eu]=KNewsticker iturburu fitxategiaren propietateen orria
+Name[fa]=صÙØ­Û€ ویژگیهای پروندۀ مبدأ KNewsticker
+Name[fi]=KNewsticker lähdetiedostojen asetussivu
+Name[fr]=Page des propriétés du fichier source de KNewsTicker
+Name[gl]=Páxina de propiedades dos arquivos fonte de KNewsTicker
+Name[he]=דף מ×פייני קובץ מקור של KNewsTicker
+Name[hi]=के-नà¥à¤¯à¥‚ज टिकर सà¥à¤°à¥‹à¤¤ फ़ाइल गà¥à¤£ पृषà¥à¤ 
+Name[hr]=Stranica sa postavkama KNewsTicker izvorne datoteke
+Name[hu]=A KNewsTicker forrásfájlok tulajdonságlapja
+Name[is]=Stillisíða fyrir frumskrár Fréttastrimils
+Name[it]=Pagina proprietà file sorgente di notizie per KNewsticker
+Name[ja]=KNewsticker ソースファイルプロパティページ
+Name[ka]=KNewsticker წყáƒáƒ áƒáƒ¡ ფáƒáƒ˜áƒšáƒ˜áƒ¡ თვისებáƒáƒ—რგვერდი
+Name[kk]=KNewsticker файлының қаÑиеттер беті
+Name[km]=ទំពáŸážšâ€‹áž›áž€áŸ’ážážŽáŸˆâ€‹ážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹áž¯áž€ážŸáž¶ážšâ€‹áž”្រភព KNewsticker
+Name[lt]=KNewsticker pirminio teksto bylos parinkÄių puslapis
+Name[lv]=KZiņutikkera Avota Faila Īpašību Lapa
+Name[mk]=Страница за ÑвојÑтвата на изворната датотека на KNewsticker
+Name[mn]=KNewsticker Ñурвалж файлын Тодорхойлолт хуудаÑ
+Name[ms]=Laman Ciri-ciri Fail Sumber KNewsticker
+Name[mt]=Paġna ta' Propjetajiet Fajl ta' Sors KNewsticker
+Name[nb]=Egenskaper for kildefiler til KNewsticker
+Name[nds]=KNewsTicker Borndateien-Egenschappensiet
+Name[ne]=केडीई नà¥à¤¯à¥‚ज टिकर सà¥à¤°à¥‹à¤¤ फाइल विशेषता पृषà¥à¤ 
+Name[nl]=KNewsticker Bronbestand Eigenschappenpagina
+Name[nn]=Eigenskapsside for KNewsticker-kjeldefil
+Name[nso]=Letlakala la Dithoto tsa Faele ya Mothopo wa seswai sa KDitaba
+Name[pl]=Strona właściwości pliku źródłowego KNewsTickera
+Name[pt]=Página de Propriedades do Ficheiro da Fonte do KNewsticker
+Name[pt_BR]=Página de Propriedades do Arquivo Fonte do KNewsTicher
+Name[ro]=Pagină de proprietăţi pentru fişier sursă KNewsticker
+Name[ru]=Страница ÑвойÑтв Ñведений о файле KNewsticker
+Name[se]=KNewsTicker-gáldofiilla iešvuohtasiidu
+Name[sk]=Popis vlastností konfiguraÄného súboru pre KNewsticker
+Name[sl]=Stran z lastnostmi za izvorno datoteko KNewsTicker
+Name[sr]=KNewsticker Ñтрана за подешавање ÑвојÑтава изворних фајлова
+Name[sr@Latn]=KNewsticker strana za podešavanje svojstava izvornih fajlova
+Name[sv]=Nyhetsövervakarens sida för källfilsegenskaper
+Name[ta]=கேசெயà¯à®¤à®¿ அறிவிபà¯à®ªà®¾à®©à®¿à®©à¯ மூல கோபà¯à®ªà®¿à®©à¯ பணà¯à®ªà¯à®•à®³à¯ˆ அறியà¯à®®à¯ பகà¯à®•à®®à¯.
+Name[tg]=Саҳофаи ХуÑуÑиÑтҳои Файли Сарчашмавии KДидабони Ðхборот
+Name[th]=หน้าคุณสมบัติของà¹à¸Ÿà¹‰à¸¡à¸•à¹‰à¸™à¸‰à¸šà¸±à¸šà¸•à¸±à¹‹à¸§à¸‚่าว - K
+Name[tr]=KDE Haber Gözlemcisi Kaynak Dosyası Özellikler Sayfası
+Name[uk]=Сторінка влаÑтивоÑтей вихідного файла KNewsticker
+Name[ven]=Siatari la tshishumiswa tshavhubvo ha faela la tshauthika mafhungo a K
+Name[xh]=Iphepha Leempahla Lemvelaphi Yefayile ye KNewsticker
+Name[zh_CN]=KNewsticker æ¥æºæ–‡ä»¶å±žæ€§é¡µ
+Name[zh_HK]=KNewsticker 來æºæª”案屬性é 
+Name[zh_TW]=KNewsticker 來æºæª”案屬性é 
+Name[zu]=Umlekeleli wezindaba ze K wephepha lefayela Yemvelaphi Yempahla
+X-KDE-Library=libkntsrcfilepropsdlg
+ServiceTypes=KPropsDlg/Plugin,text/rdf,text/rss
diff --git a/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.h b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.h
new file mode 100644
index 00000000..8fe1be58
--- /dev/null
+++ b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlg.h
@@ -0,0 +1,57 @@
+/*
+ * kntsrcfilepropsdlg.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef KNTSRCFILEPROPSDLG_H
+#define KNTSRCFILEPROPSDLG_H
+
+#include <global.h>
+
+#include <klibloader.h>
+#include <kpropertiesdialog.h>
+
+class KntSrcFilePropsDlgWidget;
+class QListBoxItem;
+
+namespace RSS {
+ class Loader;
+ class Document;
+}
+
+using RSS::Loader;
+using RSS::Document;
+using RSS::Status;
+
+class KntSrcFilePropsFactory : public KLibFactory
+{
+ Q_OBJECT
+
+ public:
+ virtual QObject *createObject(QObject * = 0, const char * = 0,
+ const char * = "QObject", const QStringList & = QStringList());
+};
+
+class KntSrcFilePropsDlg : public KPropsDlgPlugin
+{
+ Q_OBJECT
+
+ public:
+ KntSrcFilePropsDlg(KPropertiesDialog *);
+
+ protected slots:
+ void slotOpenURL(const QString &);
+ void slotConstructUI(Loader *loader, Document doc, Status status);
+ void slotGotIcon(const KURL &, const QPixmap &);
+ void slotClickedArticle(QListBoxItem *);
+
+ private:
+ KntSrcFilePropsDlgWidget *m_child;
+};
+
+#endif // KNTSRCFILEPROPSDLG_H
diff --git a/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlgwidget.ui b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlgwidget.ui
new file mode 100644
index 00000000..b8d1827e
--- /dev/null
+++ b/knewsticker/kntsrcfilepropsdlg/kntsrcfilepropsdlgwidget.ui
@@ -0,0 +1,215 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KntSrcFilePropsDlgWidget</class>
+<comment>The widget to be used for the RDF/RSS file properties dialog plugin.</comment>
+<author>Frerich Raabe &lt;raabe@kde.org&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KntSrcFilePropsDlgWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>311</width>
+ <height>274</height>
+ </rect>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <spacer row="0" column="3">
+ <property name="name">
+ <cstring>Spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="4">
+ <property name="name">
+ <cstring>pixmapIcon</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon of this news site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see the icon of this news site.</string>
+ </property>
+ </widget>
+ <widget class="KURLLabel" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>urlName</cstring>
+ </property>
+ <property name="text">
+ <string>heise online news</string>
+ </property>
+ <property name="url" stdset="0">
+ <string>http://www.heise.de/newsticker/</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lDescription</cstring>
+ </property>
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Brief description of the news site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see a brief description about the news site and its contents.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lName</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Name of the news site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the name of the news site.</string>
+ </property>
+ </widget>
+ <widget class="QMultiLineEdit" row="1" column="2" rowspan="2" colspan="3">
+ <property name="name">
+ <cstring>mleDescription</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="wordWrap">
+ <enum>WidgetWidth</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Brief description of the news site</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see a brief description about the news site and its contents.</string>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>Spacer_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>63</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KSeparator" row="3" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>Line1</cstring>
+ </property>
+ <property name="orientation">
+ <number>5</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>lArticles</cstring>
+ </property>
+ <property name="text">
+ <string>Available articles:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Articles contained within this source file</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list shows the headlines and links to the corresponding complete articles which have been stored in the source file whose properties you are watching.</string>
+ </property>
+ </widget>
+ <widget class="KListBox" row="5" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>lbArticles</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Articles contained within this source file</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list shows the headlines and links to the corresponding complete articles which have been stored in the source file whose properties you are watching.&lt;p&gt;You can open the corresponding full article for each headline by, depending on the global KDE settings, clicking or double-clicking on a headline</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="597">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff610000021c49444154388d8592316813511cc67f910c1770b8408704742838248260c63ad943974041533a84d0a1440a9ad6418a83dc22a853b62693c40c25e9503c07e93908ed601b3b354be90916ae43e10e2c5c06e1fed007714873b56daadff28ef7eefbf17dfff762db9d6dfe567db9dee73faa2c5662c3eff8c5c3dcdd1ca56209518216d7a2d5d9dba5b1d2c6b86f505faef787907300f393d91f9f4a621e7c8fccee5761e3dd3afa7503fbcb26e5f97294b4b25889c52f9aad9716e96c1614783f5d8c171318af0c365e6f401c76bbbb1164e1d9423f020ccc36b93b93983326fa988e73e462ab0fb8471ddc4397542a457ba54da3d9e0d0393c3f03e79b873e9e227f2b4f3693257d338d9be9e2adb9c82f8dfd837d961697f08e5ddc1ffb746e7469af5a5c1b02aa93d5583223f8be0f0ad64f5ad83b36c435509020017148eb29b299dbdccbe500ce0000b5871f639eefd149d9585b16aee7e2343d6a73358c0713a04242d543d334e4b77f193094bd639f99e76b4ccfe46935eb84aa87bdd64224403b2d3f12303457e7aa4c3fca8302949038bddae4581a11b91ac0964e32077a62d03f941ea562191414a60aa0403bfdf5d24b04683dad22aa070cccd66a83c2e30228419420be8f9c045727308fdf527eff865069241494662b589f6d44044d0112460946cf602da0dbddc4f75d421510f65c0ac53220880451922b2b548b26cc9a8804387bcea08eea8204034828ff9ec193e74ba3b647ea0f69200af223e8435e0000000049454e44ae426082</data>
+ </image>
+</images>
+<includes>
+ <include location="global" impldecl="in declaration">klistbox.h</include>
+ <include location="global" impldecl="in declaration">kurllabel.h</include>
+ <include location="global" impldecl="in declaration">kseparator.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurllabel.h</includehint>
+ <includehint>kseparator.h</includehint>
+ <includehint>klistbox.h</includehint>
+</includehints>
+</UI>
diff --git a/knewsticker/newsscroller.cpp b/knewsticker/newsscroller.cpp
new file mode 100644
index 00000000..9d11f19e
--- /dev/null
+++ b/knewsticker/newsscroller.cpp
@@ -0,0 +1,596 @@
+/*
+ * newsscroller.cpp
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ * Copyright (c) 2001 Malte Starostik <malte@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include <dcopclient.h>
+
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kcursor.h>
+#include <kstandarddirs.h>
+#include <kurldrag.h>
+#include <kmessagebox.h>
+
+#include "configaccess.h"
+#include "newsscroller.h"
+#include "newsengine.h"
+#include <kapplication.h>
+
+class Headline
+{
+public:
+ Headline(NewsScroller *scroller, const Article::Ptr &article)
+ : m_scroller(scroller),
+ m_article(article),
+ m_normal(0),
+ m_highlighted(0)
+ {
+ };
+
+ virtual ~Headline()
+ {
+ reset();
+ }
+
+ Article::Ptr article() const { return m_article; }
+
+ int width() { return pixmap()->width(); }
+ int height() { return pixmap()->height(); }
+
+ QPixmap *pixmap(bool highlighted = false, bool underlineHighlighted = true)
+ {
+ QPixmap *result = highlighted ? m_highlighted : m_normal;
+ if (!result) {
+ const QFontMetrics &metrics = m_scroller->fontMetrics();
+
+ int w, h;
+ if (m_scroller->m_cfg->showIcons()) {
+ w = m_article->newsSource()->icon().width() + 4 + metrics.width(m_article->headline());
+ h = QMAX(metrics.height(), m_article->newsSource()->icon().height());
+ } else {
+ w = metrics.width(m_article->headline());
+ h = metrics.height();
+ }
+
+ if (ConfigAccess::rotated(static_cast<ConfigAccess::Direction>(m_scroller->m_cfg->scrollingDirection())))
+ result = new QPixmap(h, w);
+ else
+ result = new QPixmap(w, h);
+
+ result->fill(m_scroller->m_cfg->backgroundColor());
+ QPainter p(result);
+ QFont f = m_scroller->font();
+
+ if (highlighted)
+ f.setUnderline(underlineHighlighted);
+
+ p.setFont(f);
+ p.setPen(highlighted ? m_scroller->m_cfg->highlightedColor() : m_scroller->m_cfg->foregroundColor());
+
+ if (ConfigAccess::rotated(static_cast<ConfigAccess::Direction>(m_scroller->m_cfg->scrollingDirection()))) {
+ if (m_scroller->m_cfg->scrollingDirection() == ConfigAccess::UpRotated) {
+ // Note that rotation also
+ // changes the coordinate space
+ //
+ p.rotate(90.0);
+ if (m_scroller->m_cfg->showIcons()) {
+ p.drawPixmap(0, -m_article->newsSource()->icon().height(), m_article->newsSource()->icon());
+ p.drawText(m_article->newsSource()->icon().width() + 4, -metrics.descent(), m_article->headline());
+ } else
+ p.drawText(0, -metrics.descent(), m_article->headline());
+ } else {
+ p.rotate(-90.0);
+ if (m_scroller->m_cfg->showIcons()) {
+ p.drawPixmap(-w, h - m_article->newsSource()->icon().height(), m_article->newsSource()->icon());
+ p.drawText(-w + m_article->newsSource()->icon().width() + 4, h - metrics.descent(), m_article->headline());
+ } else
+ p.drawText(-w, h - metrics.descent(), m_article->headline());
+ }
+ } else {
+ if (m_scroller->m_cfg->showIcons()) {
+ p.drawPixmap(0,
+ (result->height() - m_article->newsSource()->icon().height()) / 2,
+ m_article->newsSource()->icon());
+ p.drawText(m_article->newsSource()->icon().width() + 4, result->height() - metrics.descent(), m_article->headline());
+ } else
+ p.drawText(0, result->height() - metrics.descent(), m_article->headline());
+ }
+
+ if (highlighted)
+ m_highlighted = result;
+ else
+ m_normal = result;
+ }
+ return result;
+ }
+
+ void reset()
+ {
+ delete m_normal;
+ m_normal = 0;
+ delete m_highlighted;
+ m_highlighted = 0;
+ }
+
+private:
+ NewsScroller *m_scroller;
+ Article::Ptr m_article;
+ QPixmap *m_normal;
+ QPixmap *m_highlighted;
+};
+
+NewsScroller::NewsScroller(QWidget *parent, ConfigAccess *cfg, const char *name)
+ : QFrame(parent, name, WNoAutoErase),
+ m_cfg(cfg),
+ m_scrollTimer(new QTimer(this)),
+ m_activeHeadline(0),
+ m_mouseDrag(false),
+ m_totalStepping(0.0)
+{
+ if (!kapp->dcopClient()->isAttached())
+ kapp->dcopClient()->attach();
+
+ setFrameStyle(StyledPanel | Sunken);
+
+ m_headlines.setAutoDelete(true);
+
+ connect(m_scrollTimer, SIGNAL(timeout()), SLOT(slotTimeout()));
+
+ setAcceptDrops(true);
+
+ reset();
+}
+
+QSize NewsScroller::sizeHint() const
+{
+ return QSize(fontMetrics().width(QString::fromLatin1("X")) * 20, fontMetrics().height() * 2);
+}
+
+QSizePolicy NewsScroller::sizePolicy() const
+{
+ return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+}
+
+void NewsScroller::clear()
+{
+ m_headlines.clear();
+ reset();
+}
+
+void NewsScroller::dragEnterEvent(QDragEnterEvent* event)
+{
+ event->accept(QTextDrag::canDecode(event));
+}
+
+void NewsScroller::dropEvent(QDropEvent* event)
+{
+ QString newSourceUrl;
+ if ( QTextDrag::decode(event, newSourceUrl) ) {
+ // <HACK>
+ // This is just for http://www.webreference.com/services/news/
+ newSourceUrl = newSourceUrl.replace(QRegExp(
+ QString::fromLatin1("^view-source:http%3A//")),
+ QString::fromLatin1("http://"));
+ // </HACK>
+ newSourceUrl = newSourceUrl.stripWhiteSpace();
+ if (!isHeadline(newSourceUrl) && KMessageBox::questionYesNo(this, i18n("<p>Do you really want to add '%1' to"
+ " the list of news sources?</p>")
+ .arg(newSourceUrl), QString::null, i18n("Add"), KStdGuiItem::cancel()) == KMessageBox::Yes) {
+ KConfig cfg(QString::fromLatin1("knewsticker_panelappletrc"), false, false);
+ ConfigAccess configFrontend(&cfg);
+ QStringList newsSources = configFrontend.newsSources();
+
+ QString name = i18n("Unknown");
+ if (newsSources.contains(name))
+ for (unsigned int i = 0; ; i++)
+ if (!newsSources.contains(i18n("Unknown %1").arg(i))) {
+ name = i18n("Unknown %1").arg(i);
+ break;
+ }
+
+ newsSources += name;
+ configFrontend.setNewsSource(NewsSourceBase::Data(name, newSourceUrl));
+ configFrontend.setNewsSources(newsSources);
+
+ QByteArray data;
+ kapp->dcopClient()->send("knewsticker", "KNewsTicker", "reparseConfig()", data);
+ }
+ }
+}
+
+bool NewsScroller::isHeadline(const QString &location) const
+{
+ for (Headline *h = m_headlines.first(); h; h = m_headlines.next())
+ if (h->article()->address() == location)
+ return true;
+
+ return false;
+}
+
+void NewsScroller::addHeadline(Article::Ptr article)
+{
+ for (unsigned int i = 0; i < m_cfg->filters().count(); i++)
+ if (m_cfg->filter(i).matches(article))
+ return;
+
+ m_headlines.append(new Headline(this, article));
+}
+
+void NewsScroller::scroll(int distance, bool interpret_directions)
+{
+ unsigned int t_dir;
+ if ( interpret_directions ) t_dir = m_cfg->scrollingDirection();
+ else t_dir = m_cfg->horizontalScrolling() ? ConfigAccess::Left : ConfigAccess::Up;
+ switch (t_dir) {
+ case ConfigAccess::Left:
+ m_offset -= distance;
+ if (m_offset <= - scrollWidth())
+ m_offset = m_offset + scrollWidth() - m_separator.width();
+ break;
+ case ConfigAccess::Right:
+ m_offset += distance;
+ if (m_offset >= contentsRect().width())
+ m_offset = m_offset + m_separator.width() - scrollWidth();
+ break;
+ case ConfigAccess::Up:
+ case ConfigAccess::UpRotated:
+ m_offset -= distance;
+ if (m_offset <= - scrollHeight())
+ m_offset = m_offset + scrollHeight() - m_separator.height();
+ break;
+ case ConfigAccess::Down:
+ case ConfigAccess::DownRotated:
+ m_offset += distance;
+ if (m_offset >= contentsRect().height())
+ m_offset = m_offset + m_separator.height() - scrollHeight();
+ }
+
+ QPoint pt = mapFromGlobal(QCursor::pos());
+
+ if (contentsRect().contains(pt))
+ updateActive(pt);
+
+ update();
+}
+
+void NewsScroller::enterEvent(QEvent *)
+{
+ if (m_cfg->slowedScrolling() && m_cfg->scrollingSpeed() > 1)
+ m_scrollTimer->changeInterval(speedAsInterval(m_cfg->scrollingSpeed() / 2));
+}
+
+void NewsScroller::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == QMouseEvent::LeftButton || e->button() == QMouseEvent::MidButton) {
+ m_dragPos = e->pos();
+
+ if (m_activeHeadline)
+ m_tempHeadline = m_activeHeadline->article()->headline();
+ }
+}
+
+void NewsScroller::mouseReleaseEvent(QMouseEvent *e)
+{
+ if ((e->button() == QMouseEvent::LeftButton || e->button() == QMouseEvent::MidButton)
+ && m_activeHeadline && m_activeHeadline->article()->headline() == m_tempHeadline
+ && !m_mouseDrag) {
+ m_activeHeadline->article()->open();
+ m_tempHeadline = QString::null;
+ }
+
+ if (e->button() == QMouseEvent::RightButton)
+ emit(contextMenu());
+
+ if (m_mouseDrag) {
+ m_mouseDrag = false;
+ if (m_cfg->scrollingSpeed())
+ m_scrollTimer->start(speedAsInterval(m_cfg->scrollingSpeed()));
+ }
+}
+
+void NewsScroller::mouseMoveEvent(QMouseEvent *e)
+{
+ // Are we in a drag phase?
+ if (!m_mouseDrag) {
+ // If not, check whether we need to start a drag.
+ int dragDistance = 0;
+ if (m_cfg->horizontalScrolling())
+ dragDistance = QABS(e->x() - m_dragPos.x());
+ else
+ dragDistance = QABS(e->y() - m_dragPos.y());
+ m_mouseDrag = (e->state() & QMouseEvent::LeftButton != 0) &&
+ dragDistance >= KGlobal::config()->readNumEntry("StartDragDist", KApplication::startDragDistance());
+ if (m_mouseDrag) // Stop the scroller if we just started a drag.
+ m_scrollTimer->stop();
+ } else {
+ // If yes, move the scroller accordingly.
+ bool createDrag;
+ if (m_cfg->horizontalScrolling()) {
+ scroll(m_dragPos.x() - e->x(), false);
+ m_dragPos = e->pos();
+ createDrag = e->y() < 0 || e->y() > height();
+ } else {
+ scroll(m_dragPos.y() - e->y(), false);
+ m_dragPos = e->pos();
+ createDrag = e->x() < 0 || e->x() > width();
+ }
+ m_dragPos = e->pos();
+ if (createDrag && m_activeHeadline) {
+ KURL::List url;
+ url.append(m_activeHeadline->article()->address());
+ QDragObject *drag = new KURLDrag(url, this);
+ drag->setPixmap(m_activeHeadline->article()->newsSource()->icon());
+ drag->drag();
+ m_mouseDrag = false;
+ if (m_cfg->scrollingSpeed())
+ m_scrollTimer->start(speedAsInterval(m_cfg->scrollingSpeed()));
+ }
+ }
+
+ if (updateActive(e->pos()))
+ update();
+}
+
+void NewsScroller::wheelEvent(QWheelEvent *e)
+{
+ // ### This 11 - m_cfg->mouseWheelSpeed() could be eliminated by swapping
+ // the labels of the QSlider. :]
+ int distance = qRound(QABS(e->delta()) / (11 - m_cfg->mouseWheelSpeed()));
+ int direction = e->delta() > 0 ? -1 : 1;
+
+ for (int i = 0; i < distance; i++)
+ scroll(direction);
+
+ QFrame::wheelEvent(e);
+}
+
+void NewsScroller::leaveEvent(QEvent *)
+{
+ if (m_cfg->slowedScrolling() && m_cfg->scrollingSpeed() > 1)
+ m_scrollTimer->changeInterval(speedAsInterval(m_cfg->scrollingSpeed()));
+
+ if (m_activeHeadline) {
+ m_activeHeadline = 0;
+ update();
+ }
+}
+
+void NewsScroller::drawContents(QPainter *p)
+{
+ if (!scrollWidth() || // No news and no "No News Available": uninitialized
+ m_headlines.isEmpty()) // Happens when we're currently fetching new news
+ return;
+
+ QPixmap buffer(contentsRect().width(), contentsRect().height());
+ buffer.fill(m_cfg->backgroundColor());
+ int pos = m_offset;
+
+ // Paste in all the separator bitmaps (" +++ ")
+ if (horizontal()) {
+ while (pos > 0)
+ pos -= scrollWidth() - (m_headlines.isEmpty() ? m_separator.width() : 0);
+ do {
+ bitBlt(&buffer, pos, (contentsRect().height() - m_separator.height()) / 2, &m_separator);
+ pos += m_separator.width();
+ } while (m_headlines.isEmpty() && pos < contentsRect().width());
+ } else {
+ while (pos > 0)
+ pos -= scrollHeight() - (m_headlines.isEmpty() ? 0 : m_separator.height());
+ do {
+ bitBlt(&buffer, (contentsRect().width() - m_separator.width()) / 2, pos, &m_separator);
+ pos += m_separator.height();
+ } while (m_headlines.isEmpty() && pos < contentsRect().height());
+ }
+
+ // Now do the headlines themselves
+ do {
+ QPtrListIterator<Headline> it(m_headlines);
+ for(; *it; ++it) {
+ if (horizontal()) {
+ if (pos + (*it)->width() >= 0)
+ bitBlt(&buffer, pos, (contentsRect().height() - (*it)->height()) / 2, (*it)->pixmap(*it == m_activeHeadline, m_cfg->underlineHighlighted()));
+ pos += (*it)->width();
+
+ if (pos + m_separator.width() >= 0)
+ bitBlt(&buffer, pos, (contentsRect().height() - m_separator.height()) / 2, &m_separator);
+ pos += m_separator.width();
+
+ if (pos >= contentsRect().width())
+ break;
+ } else {
+ if (pos + (*it)->height() >= 0)
+ bitBlt(&buffer, (contentsRect().width() - (*it)->width()) / 2, pos, (*it)->pixmap(*it == m_activeHeadline, m_cfg->underlineHighlighted()));
+ pos += (*it)->height();
+
+ if (pos + m_separator.height() >= 0)
+ bitBlt(&buffer, (contentsRect().width() - m_separator.width()) / 2, pos, &m_separator);
+ pos += m_separator.height();
+
+ if (pos > contentsRect().height())
+ break;
+ }
+ }
+
+ /*
+ * Break out if we reached the bottom of the window before the end of the
+ * list of headlines.
+ */
+ if (*it)
+ break;
+ }
+ while ((m_cfg->horizontalScrolling() && pos < contentsRect().width()) || pos < contentsRect().height());
+
+ p->drawPixmap(0, 0, buffer);
+}
+
+void NewsScroller::reset(bool bSeparatorOnly)
+{
+ setFont(m_cfg->font());
+
+ m_scrollTimer->stop();
+ if (m_cfg->scrollingSpeed())
+ m_scrollTimer->start(speedAsInterval(m_cfg->scrollingSpeed()));
+
+ QString sep = m_headlines.isEmpty() ? i18n(" +++ No News Available +++") : QString::fromLatin1(" +++ ");
+
+ int w = fontMetrics().width(sep);
+ int h = fontMetrics().height();
+
+ if (rotated())
+ m_separator.resize(h, w);
+ else
+ m_separator.resize(w, h);
+
+ m_separator.fill(m_cfg->backgroundColor());
+
+ QPainter p(&m_separator);
+ p.setFont(font());
+ p.setPen(m_cfg->foregroundColor());
+
+ if(rotated()) {
+ if (m_cfg->scrollingDirection() == ConfigAccess::UpRotated) {
+ p.rotate(90.0);
+ p.drawText(0, -fontMetrics().descent(),sep);
+ } else {
+ p.rotate(-90.0);
+ p.drawText(-w, h-fontMetrics().descent(),sep);
+ }
+ } else
+ p.drawText(0, m_separator.height() - fontMetrics().descent(), sep);
+ p.end();
+
+ if (!bSeparatorOnly)
+ for (QPtrListIterator<Headline> it(m_headlines); *it; ++it)
+ (*it)->reset();
+
+ switch (m_cfg->scrollingDirection()) {
+ case ConfigAccess::Left:
+ m_offset = contentsRect().width();
+ break;
+ case ConfigAccess::Right:
+ m_offset = - scrollWidth();
+ break;
+ case ConfigAccess::Up:
+ case ConfigAccess::UpRotated:
+ m_offset = contentsRect().height();
+ break;
+ case ConfigAccess::Down:
+ case ConfigAccess::DownRotated:
+ m_offset = - scrollHeight();
+ }
+
+ update();
+}
+
+int NewsScroller::scrollWidth() const
+{
+ int result = (m_headlines.count() + 1) * m_separator.width();
+
+ for (QPtrListIterator<Headline> it(m_headlines); *it; ++it)
+ result += (*it)->width();
+
+ return result;
+}
+
+int NewsScroller::scrollHeight() const
+{
+ int result = (m_headlines.count() + 1) * m_separator.height();
+
+ for (QPtrListIterator<Headline> it(m_headlines); *it; ++it)
+ result += (*it)->height();
+
+ return result;
+}
+
+bool NewsScroller::updateActive(const QPoint &pt)
+{
+ int pos = m_offset;
+
+ Headline *headline = 0;
+
+ if (!m_headlines.isEmpty()) {
+ while (pos > 0)
+ if (horizontal())
+ pos -= scrollWidth() - m_separator.width();
+ else
+ pos -= scrollHeight() - m_separator.height();
+
+ do {
+ QPtrListIterator<Headline> it(m_headlines);
+ for (; (headline = *it); ++it) {
+ QRect rect;
+ if (horizontal()) {
+ pos += m_separator.width();
+ rect.moveTopLeft(QPoint(pos, (contentsRect().height() - (*it)->height()) / 2));
+ pos += (*it)->width();
+ } else {
+ pos += m_separator.height();
+ rect.moveTopLeft(QPoint((contentsRect().width() - (*it)->width()) / 2, pos));
+ pos += (*it)->height();
+ }
+ rect.setSize(QSize((*it)->width(), (*it)->height()));
+
+ if (m_mouseDrag)
+ if (horizontal()) {
+ rect.setTop(0);
+ rect.setHeight(height());
+ } else {
+ rect.setLeft(0);
+ rect.setWidth(width());
+ }
+
+ if (rect.contains(pt))
+ break;
+ }
+ if (*it)
+ break;
+ }
+ while ((m_cfg->horizontalScrolling() && pos < contentsRect().width()) || pos < contentsRect().height());
+ }
+
+ if (m_activeHeadline == headline)
+ return false;
+
+ if ((m_activeHeadline = headline))
+ setCursor(KCursor::handCursor());
+ else
+ unsetCursor();
+
+ return true;
+}
+
+void NewsScroller::slotTimeout()
+{
+ m_totalStepping += m_stepping;
+ if ( m_totalStepping >= 1.0 ) {
+ const int distance = static_cast<int>( m_totalStepping );
+ m_totalStepping -= distance;
+ scroll( distance );
+ }
+}
+
+int NewsScroller::speedAsInterval( int speed )
+{
+ Q_ASSERT( speed > 0 );
+
+ static const int MaxScreenUpdates = 25;
+
+ if ( speed <= MaxScreenUpdates ) {
+ m_stepping = 1.0;
+ return 1000 / speed;
+ }
+
+ m_stepping = speed / MaxScreenUpdates;
+ return 1000 / MaxScreenUpdates;
+}
+
+#include "newsscroller.moc"
diff --git a/knewsticker/newsscroller.h b/knewsticker/newsscroller.h
new file mode 100644
index 00000000..efe0c3c2
--- /dev/null
+++ b/knewsticker/newsscroller.h
@@ -0,0 +1,102 @@
+/*
+ * newsscroller.h
+ *
+ * Copyright (c) 2000, 2001 Frerich Raabe <raabe@kde.org>
+ * Copyright (c) 2001 Malte Starostik <malte@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef NEWSSCROLLER_H
+#define NEWSSCROLLER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "configaccess.h"
+#include "newsengine.h"
+
+#include <qframe.h>
+#include <qptrlist.h>
+#include <qpixmap.h>
+
+class QTimer;
+class Headline;
+template <class> class QPtrList;
+typedef QPtrList<Headline> HeadlineList;
+
+class NewsScroller : public QFrame
+{
+ Q_OBJECT
+
+ public:
+ NewsScroller(QWidget *, ConfigAccess *, const char * = 0);
+
+ virtual QSize sizeHint() const;
+ virtual QSizePolicy sizePolicy() const;
+
+ // Convenience stuff. Somehow ugly, no?
+ inline bool horizontal() const
+ {
+ return m_cfg->horizontal(static_cast<ConfigAccess::Direction>(m_cfg->scrollingDirection()));
+ }
+
+ inline bool vertical() const
+ {
+ return m_cfg->vertical(static_cast<ConfigAccess::Direction>(m_cfg->scrollingDirection()));
+ }
+
+ inline bool rotated() const
+ {
+ return m_cfg->rotated(static_cast<ConfigAccess::Direction>(m_cfg->scrollingDirection()));
+ }
+
+ public slots:
+ void clear();
+ void addHeadline(Article::Ptr);
+ void reset(bool bSeparatorOnly = false);
+
+ signals:
+ void contextMenu();
+
+ protected:
+ virtual void enterEvent(QEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+ virtual void wheelEvent(QWheelEvent *);
+ virtual void leaveEvent(QEvent *);
+ virtual void drawContents(QPainter *);
+ virtual void dragEnterEvent( QDragEnterEvent *);
+ virtual void dropEvent(QDropEvent *);
+
+ protected slots:
+ void scroll(int = 1, bool = true);
+ void slotTimeout();
+
+ private:
+ int scrollWidth() const;
+ int scrollHeight() const;
+ bool updateActive(const QPoint &);
+ bool isHeadline(const QString &) const;
+ int speedAsInterval( int speed );
+
+ private:
+ friend class Headline;
+ ConfigAccess *m_cfg;
+ QTimer *m_scrollTimer;
+ mutable HeadlineList m_headlines;
+ Headline *m_activeHeadline;
+ QPixmap m_separator;
+ int m_offset;
+ QPoint m_dragPos;
+ bool m_mouseDrag;
+ QString m_tempHeadline;
+ float m_totalStepping;
+ float m_stepping;
+};
+
+#endif // NEWSSCROLLER_H
diff --git a/knewsticker/newssourcedlg.ui b/knewsticker/newssourcedlg.ui
new file mode 100644
index 00000000..bc2690b2
--- /dev/null
+++ b/knewsticker/newssourcedlg.ui
@@ -0,0 +1,408 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>NewsSourceDlg</class>
+<comment>The dialog to be used to edit another news source.</comment>
+<author>Frerich Raabe &lt;raabe@kde.org&gt;</author>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>NewsSourceDlg</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>438</width>
+ <height>201</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Add News Source</string>
+ </property>
+ <property name="icon">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>gbNewsSourceProperties</cstring>
+ </property>
+ <property name="title">
+ <string>News Source Properties</string>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lName</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Name of the news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can enter the name of the news source.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file below.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>leName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Name of the news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can enter the name of the news source.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file below.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lSourceFile</cstring>
+ </property>
+ <property name="text">
+ <string>Source &amp;file:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>urlSourceFile</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The source file for this news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the path to the source file for the news source you want to add here. If you specified a source file here, you can use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill in the remaining values automatically.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>lIcon</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Icon:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leIcon</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Path to the icon for this news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify the path to an icon to be used for this news source. Icons make it easier to distinguish between multiple news sources as the headlines scroll by.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="4" column="1" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>leIcon</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Path to the icon for this news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify the path to an icon to be used for this news source. Icons make it easier to distinguish between multiple news sources as the headlines scroll by.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="5">
+ <property name="name">
+ <cstring>pixmapIcon</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="pixmap">
+ <pixmap>image1</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Icon to be used for this news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is what the currently configured icon for this news source looks like. To change this icon, use the input field at the left.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lCategory</cstring>
+ </property>
+ <property name="text">
+ <string>Ca&amp;tegory:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboCategory</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Into which category does this news source belong?</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify into which category this news source belongs. Arranging the news sources into categories makes it much easier to maintain large lists of news sources.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="3" column="1">
+ <property name="name">
+ <cstring>comboCategory</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Into which category does this news source belong?</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify into which category this news source belongs. Arranging the news sources into categories makes it much easier to maintain large lists of news sources.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>Spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="3" column="3">
+ <property name="name">
+ <cstring>lMaxArticles</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Max. articles:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sbMaxArticles</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Maximum number of articles</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option lets you define how many articles KNewsTicker should cache for this news source. This value will never be exceeded.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="4" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>sbMaxArticles</cstring>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Maximum number of articles</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option lets you define how many articles KNewsTicker should cache for this news source. This value will never be exceeded.&lt;br&gt;Note that you can also use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill this field automatically, after you have entered a source file above.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cbProgram</cstring>
+ </property>
+ <property name="text">
+ <string>The file is a &amp;program</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Is the specified source file a program?</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to tell KNewsTicker that the file you specified in the above input field labeled &lt;i&gt;Source file&lt;/i&gt; is a program and not a RDF or RSS file. KNewsTicker will then process the output (as received on &lt;i&gt;stdout&lt;/i&gt;) of that program.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>urlSourceFile</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The source file for this news source</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the path to the source file for the news source you want to add here. If you specified a source file here, you can use the button at the bottom right labeled &lt;i&gt;Suggest&lt;/i&gt; to let KNewsTicker fill in the remaining values automatically.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QPushButton" row="1" column="3">
+ <property name="name">
+ <cstring>bCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Cancel this configuration</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to close this dialog, discarding all entered information.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>bSuggest</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Suggest</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Suggest suitable values</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to make KNewsTicker guess more or less reasonable values for some of the news properties (such as the name, icon or maximum number of articles).&lt;br&gt;Note that you have to supply a source file in order to use this function.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>bOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Acknowledge these values</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Press this button to apply the values of this dialog and return to the previous configuration dialog.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacerButtons</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="2045">789c55d45b53db381407f0773e4586f3c6ec9cc6777b76f6a177480b85425be8ce3ec8924c4248805cb87467bffb4ae7fc6d688433f3437f1dc992e3577ba3f3e3c3d1deab9df5c66c667664a76635da73dbc5e2e9ef7ffefa7767372947e1af6e46e9ee1f3bbb279b911d1ddd2c7d04670134964f34ddc039bc14d7c1b5e44fa2933ab1899aa2d3713a4e24cfefe13c557f1637c18dd4bb82ad9a1fa2b324b55922fd703656732e6eb23ad3bc872deca2f324b3b9e6e7ea7c0c4fe01c7e2d3639d6c307bd7323fe155da443bfacb73005f6876d74990efe24366551ca789ac38d9a8f61079be82a2b1dd67fa6ae922a13bf13b75551b5e2b770a3260f3bf4c7c3a43aab0aecffd3603d9f37b0aba53e91ba4ed42ce75d87fa38af1676b8ff525d17b5ce770137f00fd8c1e7d14d5ebb26179faa9b04fe0217f0bed8065bf111dcc01f60077f8c3679e38c8e7f549b04eec4613cce7b0d3b78a63649293f0692e7a7cddbb4d5f18dd8198ff5add46da9e743dcbb75d27f0f7b352da26dd138e4efd506e74bb3c17a9e977081f3b1b0d1fda40dece195ba2d71bf696facbf80bd2dc45bb54dd5a476a1beceb756b7a9ee2fddc1dea86f7ba3fe77b53556ef7f1aed0adbefd757b54b9dce2ff59c1ffa0fd5ae74fafe59c21e4ea27d3978d1db97e26bb54fd5a4f6d6e87ed054ed52ac5f9e87ae7406e7d3c2fdfed4ea3083ba828dce4ff27bedbade3c5677a957dfc2fdfdfcecdde97abfc106be84c3475fc67d6362c32ddbe7ffc4a61976ece5eaf892a73ce32bd8f5199ef3352f78c9377ccb77bce2356fd8f236b47b7e40c687c463684f61fc15ffe2d7d2def05b7ec7ef9171a1c6237fe08fbccf073c79717de2cf439d433e0aed4bc84cc2bc4b5cc74127439daf21b11fda6918bb08ebd676c6dff860a8f33df49ef20f3e973acf99b3703f13d4d92271f1a2ce4f1e73c229eb3b2ce722f45e70195a5f27262aaeb941c6136b82087542820cd7d492edeb90a3f8f12113eba046487474f95bc6cbaec53a7d8d8ea634d30c5dd19ce67c2cdfb14e3524ae69a19970a6192de345377c40b774472b5ad386b674ff9c79f1744c38fbbdd1c3ee7f7feefc0f291dd297</data>
+ </image>
+ <image name="image1">
+ <data format="XPM.GZ" length="620">789c7dcfcd6a84301007f0bb4f11f426256bd4b886a58fd0d263a1f43093c48fdd55a1dd1e4ae9bbd749a2220b8d82f9fd33c99843ca5e5f9e587a883e6f70eb35d31d7cb0d47c0dc3f7dbfbe34f148b8ad17b64227e8862ce347b9e464b739ce749e606113c8b5a02315958e5c4c6b3cc2ab7da0796c782683d655623d1044a5512a740a534511373550b2d899740e9d9110bb1f04a2c8512da10cf4499cf2739b6c40a008c2b1e56ba0b8ec41ad1dacadd7d1e09d08334e521036d10d158c4356b5a4380ced2d767fdd9ef828b5db344bb3ade5cb73a0e6ed39c6d759c239d0e83d97af801e3847c9fc130edfe6fedb9cf96debbbd18a25d0f7e9f01dc6549fbef79bfa7e80f2829874e</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>bSuggest</sender>
+ <signal>clicked()</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotSuggestClicked()</slot>
+ </connection>
+ <connection>
+ <sender>bCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotCancelClicked()</slot>
+ </connection>
+ <connection>
+ <sender>leName</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotModified()</slot>
+ </connection>
+ <connection>
+ <sender>comboCategory</sender>
+ <signal>highlighted(const QString&amp;)</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotModified()</slot>
+ </connection>
+ <connection>
+ <sender>sbMaxArticles</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotModified()</slot>
+ </connection>
+ <connection>
+ <sender>leIcon</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotModified()</slot>
+ </connection>
+ <connection>
+ <sender>bOk</sender>
+ <signal>clicked()</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotOkClicked()</slot>
+ </connection>
+ <connection>
+ <sender>urlSourceFile</sender>
+ <signal>textChanged(const QString &amp;)</signal>
+ <receiver>NewsSourceDlg</receiver>
+ <slot>slotSourceFileChanged()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>leName</tabstop>
+ <tabstop>urlSourceFile</tabstop>
+ <tabstop>cbProgram</tabstop>
+ <tabstop>comboCategory</tabstop>
+ <tabstop>sbMaxArticles</tabstop>
+ <tabstop>leIcon</tabstop>
+ <tabstop>bOk</tabstop>
+ <tabstop>bSuggest</tabstop>
+ <tabstop>bCancel</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in declaration">kurlrequester.h</include>
+ <include location="global" impldecl="in declaration">kcombobox.h</include>
+ <include location="global" impldecl="in declaration">klineedit.h</include>
+ <include location="global" impldecl="in declaration">knuminput.h</include>
+</includes>
+<slots>
+ <slot access="protected">slotOkClicked()</slot>
+ <slot access="protected">slotCancelClicked()</slot>
+ <slot access="protected">slotModified()</slot>
+ <slot access="protected">slotSourceFileChanged()</slot>
+ <slot access="protected">slotSuggestClicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/knewsticker/newssourcedlgimpl.cpp b/knewsticker/newssourcedlgimpl.cpp
new file mode 100644
index 00000000..2cf39511
--- /dev/null
+++ b/knewsticker/newssourcedlgimpl.cpp
@@ -0,0 +1,240 @@
+/*
+ * newssourcedlgimpl.cpp
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "newssourcedlgimpl.h"
+#include "xmlnewsaccess.h"
+#include "configaccess.h"
+#include "newsiconmgr.h"
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <knuminput.h>
+#include <kurlrequester.h>
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qprogressbar.h>
+#include <qtimer.h>
+#include <qvbox.h>
+
+SuggestProgressDlg::SuggestProgressDlg(const KURL &url, QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Downloading Data"), Cancel, Cancel),
+ m_gotSourceFile(false),
+ m_gotIcon(false)
+{
+ QVBox *mainWidget = makeVBoxMainWidget();
+
+ new QLabel(i18n("<qt>Please wait while KNewsTicker is downloading some "
+ "data necessary to suggest reasonable values.<br/>"
+ "<br/>"
+ "This will not take longer than one minute.</qt>" ),
+ mainWidget);
+
+ m_progressBar = new QProgressBar(60, mainWidget);
+ m_progressBar->setPercentageVisible(false);
+
+ m_timeoutTimer = new QTimer(this);
+ connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(slotTimeoutTick()));
+ m_timeoutTimer->start(1000);
+
+ m_xmlSrc = new XMLNewsSource;
+ connect(m_xmlSrc, SIGNAL(loadComplete(XMLNewsSource *, bool)),
+ this, SLOT(slotLoadComplete(XMLNewsSource *, bool)));
+ m_xmlSrc->loadFrom(url);
+
+ connect(NewsIconMgr::self(), SIGNAL(gotIcon(const KURL &, const QPixmap &)),
+ this, SLOT(slotGotIcon(const KURL &, const QPixmap &)));
+ KURL u = url;
+ if (url.isLocalFile())
+ u = QString::null;
+ else
+ u.setEncodedPathAndQuery(QString::fromLatin1("/favicon.ico"));
+ NewsIconMgr::self()->getIcon(u);
+}
+
+SuggestProgressDlg::~SuggestProgressDlg()
+{
+ delete m_xmlSrc;
+}
+
+void SuggestProgressDlg::slotTimeoutTick()
+{
+ if (m_progressBar->progress() == m_progressBar->totalSteps()) {
+ m_timeoutTimer->stop();
+ KMessageBox::error(this, i18n("Could not retrieve the specified source file."));
+ reject();
+ return;
+ }
+ m_progressBar->setProgress(m_progressBar->progress() + 1);
+}
+
+void SuggestProgressDlg::slotLoadComplete(XMLNewsSource *, bool succeeded)
+{
+ m_gotSourceFile = true;
+ m_succeeded = succeeded;
+
+ if (m_gotIcon)
+ done(succeeded ? QDialog::Accepted : QDialog::Rejected);
+}
+
+void SuggestProgressDlg::slotGotIcon(const KURL &url, const QPixmap &pixmap)
+{
+ m_gotIcon = true;
+ m_icon = pixmap;
+ m_iconURL = url;
+
+ if (m_gotIcon)
+ done(m_succeeded ? QDialog::Accepted : QDialog::Rejected);
+}
+
+NewsSourceDlgImpl::NewsSourceDlgImpl(QWidget *parent, const char *name, bool modal, WFlags fl)
+ : NewsSourceDlg(parent, name, modal, fl),
+ m_modified(false)
+{
+ connect(NewsIconMgr::self(), SIGNAL(gotIcon(const KURL &, const QPixmap &)),
+ this, SLOT(slotGotIcon(const KURL &, const QPixmap &)));
+
+ for (unsigned int i = 0; i < DEFAULT_SUBJECTS; i++)
+ comboCategory->insertItem(
+ NewsSourceBase::subjectText(static_cast<NewsSourceBase::Subject>(i)));
+
+}
+
+void NewsSourceDlgImpl::slotCancelClicked()
+{
+ close();
+}
+
+void NewsSourceDlgImpl::slotOkClicked()
+{
+ KURL url (polishedURL(KURL(urlSourceFile->url())));
+
+ if (!validateURL(url))
+ return;
+
+ if (leName->text().isEmpty()) {
+ KMessageBox::error(this, i18n("You have to specify a name for this news"
+ " source to be able to use it."), i18n("No Name Specified"));
+ return;
+ }
+
+ // This finds out which subject is selected in the 'Subject' combo box.
+ NewsSourceBase::Subject subject = NewsSourceBase::Computers;
+ for (unsigned int i = 0; i < DEFAULT_SUBJECTS; i++) {
+ NewsSourceBase::Subject thisSubj = static_cast<NewsSourceBase::Subject>(i);
+ if (comboCategory->currentText() == NewsSourceBase::subjectText(thisSubj)) {
+ subject = thisSubj;
+ break;
+ }
+ }
+
+ KURL iconURL ( leIcon->text() );
+ if (iconURL.protocol().isEmpty())
+ if (iconURL.host().startsWith(QString::fromLatin1("ftp.")))
+ iconURL.setProtocol(QString::fromLatin1("ftp"));
+ else if (iconURL.host().startsWith(QString::fromLatin1("www.")))
+ iconURL.setProtocol(QString::fromLatin1("http"));
+ else
+ iconURL.setProtocol(QString::fromLatin1("file"));
+
+ NewsSourceBase::Data nsd(leName->text(), url.url(), iconURL.url(), subject,
+ sbMaxArticles->value(), true, cbProgram->isChecked());
+
+ emit newsSource(nsd);
+
+ close();
+}
+
+void NewsSourceDlgImpl::slotSourceFileChanged()
+{
+ bSuggest->setEnabled(!urlSourceFile->url().isEmpty());
+}
+
+void NewsSourceDlgImpl::slotSuggestClicked()
+{
+ KURL url ( polishedURL(KURL( urlSourceFile->url() )) );
+
+ if (!validateURL(url))
+ return;
+
+ SuggestProgressDlg dlg(url, this);
+ if (dlg.exec() == QDialog::Accepted) {
+ pixmapIcon->setPixmap(dlg.icon());
+ if (NewsIconMgr::self()->isStdIcon(dlg.icon()))
+ leIcon->clear();
+ else
+ leIcon->setText(dlg.iconURL().url());
+ cbProgram->setChecked(false);
+ leName->setText(dlg.xmlSrc()->newsSourceName());
+ sbMaxArticles->setValue(dlg.xmlSrc()->articles().count());
+ }
+}
+
+void NewsSourceDlgImpl::slotModified()
+{
+ m_modified = true;
+}
+
+void NewsSourceDlgImpl::setup(const NewsSourceBase::Data &nsd, bool modify)
+{
+ leName->setText(nsd.name);
+ urlSourceFile->setURL(nsd.sourceFile);
+ cbProgram->setChecked(nsd.isProgram);
+ comboCategory->setCurrentItem(nsd.subject);
+ sbMaxArticles->setValue(nsd.maxArticles);
+ KURL iconURL ( nsd.icon );
+ if (iconURL.protocol() == QString::fromLatin1("file"))
+ iconURL.setProtocol(QString::null);
+ leIcon->setText(iconURL.url());
+ NewsIconMgr::self()->getIcon(iconURL);
+ if (modify == true) {
+ setCaption(i18n("Edit News Source"));
+ }
+}
+
+KURL NewsSourceDlgImpl::polishedURL(const KURL &url) const
+{
+ KURL newURL = url;
+
+ if (url.protocol().isEmpty())
+ if (url.url().startsWith(QString::fromLatin1("ftp")))
+ newURL = QString::fromLatin1("ftp://") + url.url();
+ else
+ newURL = QString::fromLatin1("http://") + url.url();
+
+ return newURL;
+}
+
+bool NewsSourceDlgImpl::validateURL(const KURL &url)
+{
+ if (url.isEmpty()) {
+ KMessageBox::error(this, i18n("You have to specify the source file for this"
+ " news source to be able to use it."), i18n("No Source File"
+ " Specified"));
+ return false;
+ }
+
+ if (!url.isValid() || !url.hasPath() || url.encodedPathAndQuery() == QString::fromLatin1("/")) {
+ KMessageBox::error(this, i18n("KNewsTicker needs a valid RDF or RSS file to"
+ " suggest sensible values. The specified source file is invalid."),
+ i18n("Invalid Source File"));
+ return false;
+ }
+
+ return true;
+}
+
+void NewsSourceDlgImpl::slotGotIcon(const KURL &, const QPixmap &pixmap)
+{
+ pixmapIcon->setPixmap(pixmap);
+}
+
+#include "newssourcedlgimpl.moc"
diff --git a/knewsticker/newssourcedlgimpl.h b/knewsticker/newssourcedlgimpl.h
new file mode 100644
index 00000000..71bb6964
--- /dev/null
+++ b/knewsticker/newssourcedlgimpl.h
@@ -0,0 +1,82 @@
+/*
+ * newssourcedlgimpl.h
+ *
+ * Copyright (c) 2001 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef NEWSSOURCEDLGIMPL_H
+#define NEWSSOURCEDLGIMPL_H
+#include "newssourcedlg.h"
+#include "newsengine.h"
+
+#include <kdialogbase.h>
+#include <kurl.h>
+
+#include <qpixmap.h>
+
+class XMLNewsSource;
+class NewsIconMgr;
+class QProgressBar;
+class QTimer;
+
+class SuggestProgressDlg : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ SuggestProgressDlg(const KURL &url, QWidget *parent, const char *name = 0);
+ virtual ~SuggestProgressDlg();
+
+ XMLNewsSource *xmlSrc() { return m_xmlSrc; }
+ QPixmap icon() const { return m_icon; }
+ const KURL &iconURL() const { return m_iconURL; }
+
+ private slots:
+ void slotTimeoutTick();
+ void slotLoadComplete(XMLNewsSource *, bool);
+ void slotGotIcon(const KURL &, const QPixmap &);
+
+ private:
+ bool m_gotSourceFile;
+ bool m_gotIcon;
+ XMLNewsSource *m_xmlSrc;
+ bool m_succeeded;
+ QPixmap m_icon;
+ KURL m_iconURL;
+ QProgressBar *m_progressBar;
+ QTimer *m_timeoutTimer;
+};
+
+class NewsSourceDlgImpl : public NewsSourceDlg
+{
+ Q_OBJECT
+
+ public:
+ NewsSourceDlgImpl(QWidget * = 0, const char * = 0, bool = FALSE, WFlags = 0);
+
+ void setup(const NewsSourceBase::Data &, bool);
+
+ signals:
+ void newsSource(const NewsSourceBase::Data &);
+
+ protected slots:
+ void slotCancelClicked();
+ void slotOkClicked();
+ void slotSourceFileChanged();
+ void slotSuggestClicked();
+ void slotModified();
+ KURL polishedURL(const KURL &) const;
+ bool validateURL(const KURL &);
+
+ private slots:
+ void slotGotIcon(const KURL &, const QPixmap &);
+
+ private:
+ bool m_modified;
+};
+
+#endif // NEWSSOURCEDLGIMPL_H
diff --git a/kopete/AUTHORS b/kopete/AUTHORS
new file mode 100644
index 00000000..b8d1a69c
--- /dev/null
+++ b/kopete/AUTHORS
@@ -0,0 +1,66 @@
+See the Development team section of http://kopete.kde.org
+for an updated list of Kopete team!
+
+Core Developers
+===============
+
+Duncan Mac-Vicar Prett <duncan@kde.org> (irc: duncanmv)
+- Original author, main app work & design, original MSN Plugin, hacks in all plugins
+
+Martijn Klingens <klingens@kde.org> (irc: spaze)
+- Kopete API and main application core developer, co-maintainer of the MSN plugin,
+ misc changes and fixes in the other plugins
+
+Matt Rogers <matt.rogers@kdemail.net> irc: mattr)
+- Kopete seperate release coordinator, OSCAR/AIM maintainer, general maintainer of stuff
+
+Olivier Goffart <ogoffart@tiscalinet.be> (irc: Gof)
+- Kopete API and main application core developer, primary maintainer of the
+ MSN plugin, misc changes and fixes in the other plugins
+
+Will Stephenson <lists@stevello.free-online.co.uk> (irc: bille)
+- Core developer, KAddressBook integration, nowlistening and webpresence plugin
+ maintainer, Testbed testing plugin
+
+Other developers
+================
+
+Andy Goossens <andygoossens@telenet.be> (irc: gandy)
+- Bugfixes, cleanups and maintainer of our stable branches (backporting patches)
+
+Casey Allen Shobe <cshobe@softhome.net> (irc: sigthor)
+- Testing, Quality Assurance and usability improvements
+
+Ladislav Strojil <ladislav@strojil.cz> (irc: bwian)
+- Bugfixes, testing and keeping Kopete running on KDE 3.1
+
+Plugin Developers
+=================
+
+Gav Wood <gjw102@york.ac.uk> (irc: emmCee)
+- Winpopup and SMS maintainer, several contributions to the Yahoo plugin
+
+Grzegorz Jaskiewicz <gj@pointblue.com.pl> (irc: gregj)
+- Gadu-Gadu maintainer
+
+Michel Hermier <michel.hermier@wanadoo.fr> (irc: slayer)
+- IRC plugin maintainer
+
+Till Gerken <till@tantalo.net> (irc: tigloo)
+- Jabber maintainer
+
+Other contributors. Thanks!
+===========================
+
+This list is too long too mention. If you want to have an idea of all the
+various contributions we've got, please check the CVS commit logs. If we
+would attempt to list the people here other people would surely be forgotten
+and feel left out.
+
+Let's leave it at telling you that several hundreds of people have
+contributed code, helpful information, documentation, translations or
+useful bug reports to Kopete, making Kopete what it is now.
+
+If you contributed to Kopete, you know who you are, and many thanks for
+the work you've done!
+
diff --git a/kopete/COPYING b/kopete/COPYING
new file mode 100644
index 00000000..9e4c44b1
--- /dev/null
+++ b/kopete/COPYING
@@ -0,0 +1,18 @@
+Kopete, The KDE Instant Messenger License
+=========================================
+
+Kopete is licensed under the GNU General Public License version 2
+or later. The text of the GNU General Public License can be viewed at
+http://www.gnu.org/licenses/gpl.html
+
+The Kopete library (libkopete) is licensed under the GNU Lesser
+General Public License so plugin licenses are not restricted to be
+free software. The text of the GNU Lesser General Public License can
+be viewed at http://www.gnu.org/copyleft/lesser.html
+
+As a special exception, you have permission to link this program
+with the following libraries and distribute executables, as long as you
+follow the requirements of the GNU GPL in regard to all of the
+software in the executable aside from the following libraries:
+- OpenSSL (http://www.openssl.org)
+
diff --git a/kopete/ChangeLog b/kopete/ChangeLog
new file mode 100644
index 00000000..53392915
--- /dev/null
+++ b/kopete/ChangeLog
@@ -0,0 +1,174 @@
+2004-09-10 11:10 Will Stephenson <lists@stevello.free-online.org.uk>
+
+ * Make it possible to suppress "XXX has left the chat" messages in
+ group chat. Needed for groupwise.
+
+2004-09-10 11:09 Will Stephenson <lists@stevello.free-online.org.uk>
+
+ * merge Novell GroupWise Messenger support into HEAD.
+
+2004-09-10 02:42 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Fix the popup menu over contacts
+
+2004-09-09 22:50 Matt Rogers <matt.rogers@kdemail.net>
+
+ * Fix the reappearing format toolbar. (#59080)
+
+2004-09-07 12:21 Will Stephenson <lists@stevello.free-online.org.uk>
+
+ * Fix stale chat window member lists on join/leave
+
+2004-09-07 06:24 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Don't request the picture on group chat. this might have verry
+ bad side effect
+
+2004-09-07 06:06 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Allow to drag and drop contact to the chatwindow to invite them
+ to the chat
+
+2004-09-06 04:58 Will Stephenson <lists@stevello.free-online.org.uk>
+
+ * Emoticon scheme for GroupWise
+
+2004-09-06 04:09 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Remove unused file Add tooltip
+
+2004-09-05 16:16 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Allow configuring the application used for the netmeeting plugin.
+ So you can now use Konference insteads of Gnomemeeting
+
+2004-09-04 15:52 Matt Rogers <matt.rogers@kdemail.net>
+
+ * default to enter to send messages
+
+2004-09-03 17:58 Ingo Klöcker <kloecker@kde.org>
+
+ * Fix bug 88759: (Multiple formulas in one paragraph
+ confuse Kopete). Approved by Olivier.
+
+2004-09-03 12:33 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Fix Bug 88751: (right click move contact context menu not
+ updated)
+
+2004-09-03 12:31 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Import the popup to ask the public key from KGPG (it has a quick
+ search line, and show more info) (#88757, #88756)
+
+2004-09-02 08:02 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Use the highlight color for highlight cells. (#88495)
+
+2004-09-02 04:53 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Do not crash if the sending movie can't be found. (88594
+
+2004-09-02 04:26 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Let drag URL in the chatwindow to send files. (#82733)
+
+2004-09-02 02:26 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Ask for confirmation before deleting a contact. (#76224)
+
+2004-09-02 01:55 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Add Undo/Redo functions in the contactlist
+ -Group or metacontacts renames
+ -Move of metacontacts between groups
+ -Move of contact between metacontact
+ -Addition of contacts on the list.
+
+2004-08-30 12:53 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Avoid to remove/destroy protocol managed temporary metacontact
+ while attempting to remove them of the contactlist. (#81823, #86358)
+
+2004-08-29 03:09 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Ask confirmation before overwrite a file for incomming file
+ transfers.
+
+2004-08-28 16:47 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Remove the edit style dialog, and run the default editor instead.
+ I used text/plain as mimetype because text/xml or text/x-xslt
+ don't have default editor by default. I added a tracker to
+ update the preview when the style is saved (#77649, #63825, #77650)
+
+2004-08-28 01:58 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Hide the header of the contactlist view (which only contains
+ "Contacts") (#87974)
+
+2004-08-27 14:20 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * fix bug 73901
+
+2004-08-27 13:41 Olivier Goffart <ogoffart@tiscalinet.be>
+
+ * Add tooltips and whatsthis help
+
+2004-08-26 14:03 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Removed KStringHandler::isUtf8 workaround bug, as now
+ KopeteMessage::decodeString should not trigger the bug anymore.
+
+2004-08-26 13:59 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Make the decodeMessage function more fast for empty CString. As
+ a side effect it fix possible attempt to decode null string using
+ KStringHandler::isUtf8 wich make kopete crash with kdelibs prior
+ to 3.2.92.
+
+2004-08-23 12:54 Gustavo P. Boiko <boiko@conectiva.com.br>
+
+ * Fix encoding of sent OSCAR messages
+
+2004-08-22 22:27 Matt Rogers <matt.rogers@kdemail.net>
+
+ * Fix bug 87727.
+
+ saveOptions() was in the destructor, which meant the window state
+ was always hidden.
+
+2004-08-19 23:27 Matt Rogers <matt.rogers@kdemail.net>
+
+ * Rearrange the yahoo message parsing a bit so we do it all before we
+ create the KopeteMessage object for it.
+
+ Workaround gaim's bugginess when sending URLs so that there
+ aren't parse errors. (#87190)
+
+2004-08-14 14:22 Matt Rogers <matt.rogers@kdemail.net>
+
+ * derive a new class from KActiveLabel so we can control how the
+ links are opened
+
+2004-08-14 14:11 Will Stephenson <lists@stevello.free-online.org.uk>
+
+ * Stupid crash bug, now fixed. Always keep a good MC pointer
+ around to call execute() on. (#87065)
+
+2004-08-13 16:01 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Let's forget about deleting objects, QOject will make it for us.
+ Don't remove automagically added temporary irc account while
+ connection disconnected (was causing a crash). The previous
+ changes remove crashs and invalid reads while quitting.
+
+2004-08-13 13:55 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Removed a possibly usage of a destroyed object, while clossing
+ kopete and attempting to loggin to IRC.
+
+2004-08-13 13:22 Michel Hermier <michel.hermier@wanadoo.fr>
+
+ * Avoided usage of a possible null pointer. (#87083, #86928)
+
diff --git a/kopete/INSTALL b/kopete/INSTALL
new file mode 100644
index 00000000..1eabc857
--- /dev/null
+++ b/kopete/INSTALL
@@ -0,0 +1,239 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ If you know how to build GNU autotools based packages using the
+common 'configure/make/make install' scheme, please read on below in
+the 'Finding Plugins' chapter.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
+Finding Plugins
+===============
+If you don't see any "plugins" listed in the Configure Plugins...
+dialog, or the list of messaging protocols in the Add Account Wizard
+is empty after installing, Kopete you may have installed it in a
+directory where KDE doesn't look for additional resources like plugins.
+
+It's also possible that you've just been too impatient :)
+
+Kopete installs several .desktop files that have to be processed by a
+KDE application called 'kbuildsycoca'. If you run Kopete directly
+after installing it this process might still be running and Kopete
+doesn't see the new plugins yet. Try running 'kbuildsycoca' from a
+console and restart Kopete when it finishes. If that doesn't help,
+please read on.
+
+KDE applications by default look in all directories listed in the
+/etc/kderc file, the $KDEDIRS environment variable, in $KDEDIR if
+this variable doesn't exist, and ultimately in the directory where
+kdelibs is installed. Unless you take special precautions applications
+will _NOT_ look in other directories, even if you install additional
+software (like Kopete) there.
+
+The best solution is to add the following to /etc/kderc (create the file
+if it doesn't exist yet):
+
+ [Directories]
+ prefixes=/the/prefix/I/used/for/kopete
+
+If you don't have write access to /etc/kderc, or if you want to use more
+parallel builds of KDE and/or Kopete you need to rely on the environment
+variables instead.
+
+If $KDEDIRS currently points to /opt/kde3 and you installed Kopete
+to /usr/local then you need to set $KDEDIRS to '/opt/kde3:/usr/local'.
+You need to store this change in a file that is picked up by all shells
+and for all users, like /etc/profile. The name of the preferred version
+varies from system to system.
+
+As a last resort you can simply install Kopete in the same directory as
+where your KDE resides by adding a flag to configure like
+
+ ./configure --prefix=/opt/kde3
+
+if KDE is installed in /opt/kde3. The downside is that you'll probably
+end up installing Kopete into directories used by your distribution,
+instead of a separate path for your own packages like the /usr/local/
+tree. Whether or not your package manager has problems with that and
+whether or not you'll accept those problems should they occur is of
+course up to you to decide, but we strongly recommend to not install
+source-compiled packages like Kopete in directories that are managed
+by a package manager and only use the KDE prefix if you compiled all
+of KDE from source yourself.
+
diff --git a/kopete/KABC_INTEG_NOTES b/kopete/KABC_INTEG_NOTES
new file mode 100644
index 00000000..f249f22c
--- /dev/null
+++ b/kopete/KABC_INTEG_NOTES
@@ -0,0 +1,551 @@
+Address book integration notes 26/08/03
+
+Premise:
+KDE captures the real life relation between IM Contacts and KABC contacts; both represent people with whom we communicate electronically - so there's no need to separate them
+Goals:
+*) Duplicate Information may be removed.
+*) This enables PIM apps like KMail to use a KDE IM service (Kopete) to display IM status or perform IM actions (chatting)
+*) Or games, desktop sharing
+*) Ability to put IM-delivered contact data into the KDE addressbook
+*) send vcards via kopete
+
+What does Kopete get out of the association?
+*) Ability to view KABC information on that metacontact
+*) Use information in the KABC such as GPG key?
+*) Can store/use KABC information in the metacontact like contact photo
+
+
+Spaze's goals
+1. Share data with the other PIM apps. Notably groups, display names, GPG keys, email addresses and other IM UIDs
+2. Allow other apps to retrieve all IM-enabled contacts and their online status for e.g. presence display in kmail or invitations for desktop sharing
+3. Allow other IM apps to share the same data that Kopete uses in a standardized way
+4. Allow other apps to start a chat with selected persons in a standardized way _regardless of client used_
+5. in the longer run i would like to add,say, an ICQ UIN from kaddressbook and Kopete automatically picks up the change, adds it to the server side contact list and does all other kind of syncing of changes made in kaddressbook as well. for the short term one way kopete->kabc is ok, but the API should be prepared for full bidi comms. ideally kopete, libkabc and all server side contact lists are always 100% in sync as much as possible for each.
+
+mETz' goals
+1. well, I just think Kopete should never edit my addressbook without me knowing
+
+Gof's goals
+1. my goal is to have the kopete contactlist a kind of address book (i.e. i want to view and modify kabc entry from kopete, like if i am in the kab interface. i do not want to open KAB
+2. and i want to share fields (gpg key) with other application, like kmail
+3. technicaly, i think that should be transparent.
+Goals (-ve):
+*) don't store information that's not worth sharing in the address book
+
+gregj's goals
+kopete should not replace server side data with kabc derived data nor upload information that wasn't present on the server before.
+E.g. we know someones telephone nr. but we don't want to put this information back on server if it was not present there before. Update IMO is ok.
+
+brunes goals
+the only points I wanted to make were
+1. I think the MSN / OSCAR picture support should somehow integrate with / use the picture in kaddressbook, and
+2. we need to get the KAB guys to add >1 field for IM account in KAB, and all the contacts for a MC should have their accounts there
+
+Syncing policies
+KABC->kopete ok, what about server side metadata
+
+Display Name
+
+Priority: Goals for 3.2
+ONLY the actual linking and one-way storage from kopete in kabc and retrieval of display name and address book data
+Also ability to add kabc contained contacts to kopete ( selected ones only )
+
+Linking
+use KMC UID to store KABC uid, QString::null if no link.
+Establish the link using KMC Props dialog (mETz: Alt+return shortcut to open pls).
+Policy:
+One way kopete->kabc contacts
+, achieve bidi later
+<Bille> what about the sync policy between IM contacts contained in kabc and in kopete
+<spaze> mETz: alt-enter doesn't work? report a bug to klistview/qlistview, that sucks :( </ot>
+<gregj> hyhy
+<spaze> Bille: implied i'd say
+<Bille> for example - in Addressee A I already have 3 IM id's added with another client - if i associate him with a Kopete MC
+<Gof> first, we need to do the link (almost done?) after, we will see
+<spaze> Bille: oh, that
+<Bille> do i want to add those contacts to the MC?
+<spaze> ugh..
+<Gof> and the wizzard iontegration is in the link
+<Gof> now, it has no sense to provide a link if we don't use it at all
+<spaze> Bille: I think Gof is right. first we just ignore what's in libkabc and do it one-way
+<spaze> Bille: once that works we can do it bidi
+<Bille> ok
+<spaze> Bille: we have the luxury to be the first so no need to be compatible :)
+<spaze> Bille: as for bidi, that's still kde 3.2, at least partly
+<spaze> Bille: I think we should should a list of all these contacts and ask what to do
+<spaze> Bille: like
+<Gof> spaze: for kde 3.2, if we provide a link, we need to sync at least some stuff
+<spaze> (x) Add all contacts
+<Bille> spaze: but then, how do you remember what was chosen the next time the props dialog comes up
+<spaze> ( ) Add only these contacts
+<Gof> if not, the link is useless, and it's better no link at all
+<Bille> spaze: agreed
+<spaze> not even conditional, just always, if you say you want to use kabc
+<spaze> that's our first test case
+<spaze> once that works we can do the addressbook fields
+<Bille> gregj: what is your take on syncing display name to addressee name?
+<spaze> first store-and-retrieve, assuming nobody messes with it
+<gregj> hmm
+<spaze> third will be to detect changes made to address book fields outside kopete
+<gregj> yes
+
+***
+
+<Gof> spaze: ( ) add all contact is not good imo (i don't want to add irc channels) and also, it would make a lot of duplicate entities
+<gregj> i want kabc to provide me storage, and give back just what i stored
+<gregj> nothing more
+<Bille> gregj: what if it changes the Kopete displayname (that is not sent to the server)?
+<gregj> if i didn't provide email, i don't want email back
+<gregj> and so on
+<spaze> Gof: eh, we're talking different things now
+<Gof> maybe
+<gregj> Bille: hmm, this is hard to say now
+<gregj> Bille: i am retriving all information on connect from server
+<spaze> Gof: I'm talking about the fields to IMPORT from libkabc when we start kopete and kabc was changed by another app, i.e. someone was added there
+<gregj> Bille: as long as uid matches, this should be ok
+<Bille> gregj: ok
+<gregj> if someone changes uid in kabc, then it is his problem
+<spaze> Gof: but you pointed out a nice flaw in the current addressBookFields API :(
+ samppa Singularity spaze STiAT|off
+<spaze> gregj: i'm talking about adding new entries, not modifying
+<Bille> spaze: pls expand on that
+<Gof> spaze: what one?
+<spaze> Bille: the flaw?
+<gregj> spaze: oh
+<Bille> yup
+<gregj> spaze: than it is ok to me
+<gregj> spaze: i will have to synchronize it with server than
+<spaze> Bille: we now assume addressbook fields are either all stored in kabc or all stored in xml
+
+SEMANTICS OF KABC KEYS
+ ok, one question, it is already dual-key based (app, key)
+<spaze> so that's perfect
+<Bille> yeah, what's that Key about?
+<Bille> i wasn't sure what that's for
+<spaze> Bille: what kabc is "supposed" to be used like is as follows:
+<spaze> addCustomField( "kopete", "myCustomKey", "myValue" )
+<spaze> BUT
+<spaze> we use kabc for data that is NOT app-specific
+<Bille> in our semantics, what's the All mean, all messaging apps incl kopete? :)
+<spaze> so we 'abuse' app as key and are stuck with a key that we don't use
+<-- cdr has quit (Client Quit)
+<gregj> spaze: maybe there should be a category in kabc
+<spaze> Bille: oh... that's another 'abuse' of the sematics
+<spaze> Bille: that's only for contact id's
+<gregj> spaze: category for IM related info, emails, and so on
+<spaze> Bille: "messaging/msn" is the protocol
+<spaze> Bille: but suppose i have 2 msn accounts under a single KABC entry
+<gregj> so we can use category IM
+<Bille> spaze: oic
+<gregj> which is shared with other im apps
+<spaze> how are you going to tell that you want msn@martijn.homeip.net to be contacted by your main account
+<Bille> gregj: that's what the messaging/ part of the key we already use signifies
+<spaze> and msntest@.. with your debug account
+<gregj> Bille: got it :D
+<spaze> Bille: "All" means that all YOUR accounts can access MY account
+
+************************************************
+Use cases
+
+1) Add new metacontact + contacts using Add Contact Wizard
+ Decide whether to associate or not if using mandatory association.
+ a) Matching addressbook entry already exists
+ - Need to choose kabc entry in ACW?
+ - Check if kabc entry already contains IM information (old install of Kopete or from another client)
+ b) No matching entry exists
+ - Need to add new one
+
+2) Someone adds you - protocol notifies and creates (temporary?) MC.
+ (Martijn says this becomes the same as 1) when the MC is made permanent)
+
+3) All new Kopete (first run / no existing configs or contactlist)
+ Add accounts
+ Server Side Contact List (SSCL) fetched, lots of permanent contacts are created.
+*) Consider if 2 accounts are added and there are contacts from different accounts such that they represent the same person. They belong in the same MC. MC merging should take place prior to kabc association.
+
+4) An existing Kopete config is present and we upgrade to a version of Kopete supporting kabc.
+
+5a) After abandoning Kopete, users start using a different KDE IM client, and would like to use the IM address details stored in the KDE address book.
+
+5b) The user used another KDE IM Client. But he finally discovers Kopete,which is the best, decides to use it, and he would like to use information already existing in KABC
+
+6) Protocols may deliver contact data that users may want to aggregate into the kabc entry.
+
+7) A contact adds a new IM account, either the user decides to add the new account to the MC manually, or the contact messages the user first, we get a temporary contact Kopete side, add them to the MC.
+
+8) The user no longer wishes to have a particular contact (or even metacontact) in Kopete, and deletes the object.
+
+9) A contact changes accounts (msn:foo@uncool.org -> foo@cool.org). This is the same as 7) then 8).
+
+Implementation notes
+--------------------
+1) New MC. Need dialogs to ask if association needed. Association dialog allows to select/search kabc contacts. We need to write the kabc data sooner than closing Kopete!
+
+2) Same as 1)
+
+3) Not an issue given optional participation
+*) is orthogonal and can be dealt with separately.
+
+4) Deferred association
+
+5a) We don't want any Kopete specific data in the kabc. Entries should be usable by other KDE IM apps.
+
+5b) The reverse case, therefore we should agree standard entry format with other apps.
+ We should also make using this info really easy in the Add Contact Wizard (see below).
+
+6) Question: how to combine all contacts in an MCs' data before saving this to kabc.
+
+7) First do dupe check or 'im-info-in-kabc-not-in-kopete' check in case this information already exists in KABC. Once added to a MC, if already associated, we can add the new contact information to kabc immediately, otherwise Deferred association accessed via MC properties dialog.
+
+8) Don't remove the kabc fields, other IM clients might be using them. So we just remove them from the Kopete contact list. Dupe contact on the MC add contact thingy will catch if the contact is added again. (and the user might still want to have other data like the phone number) * See the kabc section
+
+Therefore we need an Association Dialog accessed in wizard and from the MC properties dialog. Note, this name sucks, don't write any classes using it.
+
+Side Issues
+-----------
+
+It's not possible to add >1 contact per protocol using the ACW. Martijn: >1 contact per MC is for power users, they can add extra contacts using the MC's context menu. Change to an iterative ACW would fix this.
+
+
+Possible ACW order including kabc
+---------------------------------
+1) Welcome :P
+2) Choose Account
+3) Fill in protocol details
+4) Select Group
+5) Select kabc contact from list | create new | create none
+
+6) - if the user selected to create a new entry in the KAB, show the page showed when you add normaly a new KAB entry (if possible where some fields are
+ - if the user selected to search in the existing KABC database, show a nice widget to search one user.
+ (I hope there is nice KParts in KABC)
+
+( 5) could come after the welcome but this might be too different for users to swallow... Will)
+ agreed - Olivier
+ yup - Matt
+If we delay the KABC assoc question until after contacts are added, we cannot use any IM information
+that is already present in KABC. And if different contacts are in the KABC, this might surprise the user when
+the KMC appears in the contactlist with extra contacts.·
+
+So it would be better if we performed this check before adding any contacts, in the ACW
+
+Olivier: I think a step (the first?) should be to ask a displayname for the metacontact.
+
+wow wow wow... this make a big ACW, but a quick add feature would still be desired by people like this:
+Bug 53062: Adding contacts more quickly
+
+
+--Will's try--
+1) Welcome :P
+2) Do you want to use the KDE Addressbook for this contact, or restrict it to Kopete?
+ ( Could have a 'remember my choice and don't ask again option' ).
+( or else goto A )
+3) Choose addressee or add new addressee.
+ (Check that the addressee is not already associated with an MC)
+ (Mark the contact as 'In KABC' or can we just use valid vs invalid KABC UIDs to show relation?
+ (Chances are next time the user adds a KABC addressee it will coincide with an existing Kopete UID).
+3.5) Select groups and ask for a displayname
+4) (Check to see if IM fields are already present in KABC)
+5a)"The addressbook already contained the following IM information..
+(goto C)
+
+A) How would you like to IM XXX?
+B) Fill in contact details
+ (Add duplicate check after validity check)
+C) Use another protocol?
+
+D) Finish screen
+
+NB) Better to move to a iterative selection of protocols (Select protocol -> fill in details -> Select protocol or done ...).
+This way power users can add +1 contact per protocol without complicating the ACW for simple users.
+
+
+
+--Olivier's 2nd try--
+1) Welcome :P do you want to add this contact to KAB? ( we need to choose a good default, or using the last selected one )
+ (.) Yes / search in KAB fo an existing entry => goto A
+ (.) Yes / and create a new KAB entry => goto B
+ (.) No / Do not add this contact to KAB => goto 3
+( This would require the user to remember if an appropriate KAB entry exists.
+ It would be better to allow the user to select/search KAB entries OR create a new one
+ from the same screen, if they can't find an entry - Will)
+A) use a KPart from KAB to search an user an select one. if one exist => goto 3 the user still can shoose to create one => goto B
+B) use a KPart from KAB to show the widget showed when adding new KAB entried => goto 3
+
+3) select groups, and ask the displayName (let empty to use the serverside one) (show the KAB displayname by default)
+4) select account to use (select by default account where a KAB entry exist)
+5) fill the account data (if a KAB entry exist, show as default)
+6) (.) another account (and select it) => goto 5
+ (.) finish
+7) Finish screen (is that possible to merge this screen with the previous one?
+
+This try to do all in as few step as possible.
+I can't count very well, but AFAICS your suggestion is equal or greater:
+
+Step Count No IM in kabc 1 IM in kabc (new entry) + n new
+Will 7 6 + 3 * n = 9
+Gof 6 5 + 2 * n = 7
+
+
+
+ KAB Widget in Kopete
+=========================
+
+ in the metacontact popup menu:
+A) "Add to KAB" if the metacontact don't use KAB yet
+B) "View information" if the metacontact is already in KAB
+
+
+A) show the step 2 and A/B of my addcontactwizard (Olivier)
+
+B) KParts showing the KAddressBook entries information, and allow to edit it of
+course
+
+
+so kopete becomes in fact a addressbook itself
+
+
+
+Adding fields to kabc
+~~~~~~~~~~~~~~~~~~~~~
+Either - all new addressee, no IM + new IM to add | existing address, no IM + new IM to add
+| existing addressee, no IM + new IM to add | existing addressee, existing IM + new IM to add | existing addressee, existing IM + nothing to add
+
+I think we're going to need a more complex data structure to tell KCL::addMetaContact what it has to do.
+
+ I don't understand this <p> -Olivier
+
+Fields to put in kabc
+~~~~~~~~~~~~~~~~~~~~~
+Relation between kabc entries and kopete metacontacts could be 1:1 or 1:many. 1:1 is neatest.
+
+In Kopete contact list:
+include UID from related kabc entry
+
+In kabc
+Something like X-MESSAGING-MSN:bob@hotmail.com for each protocol account that contact uses.
+
+We should keep the extra stuff in kabc to a minimum. We can store nicknames in contactlist.xml.
+
+We should allow plugins to save some data:
+Some protocol allow to reach phone numbers, or more (see the ICQ info page)
+It would be useful if KABC could remember what application writes an entry. so when ICQ try to modify an e-mail, only the "icq-email" is affected
+
+Or like the language or the PGP public key
+
+It is too complex to store the phone number according to icq as well as the kabc phone number and every other protocol's idea of phone. The fields would be of no use to any app other than the inserting program or else we would have to update the world. We *should* have a facility to insert information obtained from protocols into the common kabc fields but this is a hard problem to solve neatly.
+ - - I think it is not, ICQ phone number ~should~ be the same as all others one. storing it here is just a way to *centralise* all information about someone. Then, when you want to know his phone number, you look only there, and not at every place
+ *problem: how to trust the information? that's why kabc could handle a source of the information
+
+
+KABC Participation modes
+------------------------
+
+Participation in the relation can be optional or mandatory.
+
+Optional
+~~~~~~~~
+Scenario 1:
+1) New Kopete install or account
+2) Ask 'New contacts added, associate with KDE Address book?'
+3) Yes: Pop up association dialog
+ No: Do nothing
+
+Scenario 2:
+1) New Kopete install or account
+2) Do nothing
+3) Deferreed association - user performs associate when they want to
+
+-- For/In Favour
+.) easier to code
+.) easier migration
+.) doesn't disturb people who don't want it
+.) unobtrusive
+
+-- Against
+.) less consistent
+ (can someone explain me this? -Olivier)
+ Yes, I need more explanation on what consistent means. - Matt
+.) may confuse user?
+ how?
+
+Mandatory
+~~~~~~~~~
+Scenario 1
+1) When fetching SSCL, automatically create synthetic kabc entries.
+Scenario 2
+1) Fetch SSCL, ask user to create kabc entry normally.
+Hybrid 1-2
+1) Fetch SSCL
+2) Match SS contacts with kabc entries and associate automatically. Unmatched contacts are associated with synthetic kabc entries.
+
+-- For
+.) Can integrate with kabc contacts without disturbing user
+
+-- Against
+.) User has control of address book, this removes that control.
+.) Synthetic entries may be duplicates of existing user added entries and user will have to reassociate MCs with correct entries and throw out the synthetic entries.
+.) Hybrid approach may be too complex for user to comprehend.
+.) this will be slow to add hundred of contacts when syncing the contactlist for the first time
+
+Conclusion
+~~~~~~~~~~
+I think the Optional is the best solution. all i describe in the other part of this file are using this solution (Olivier)
+
+
+
+KDE PIM Apps' use of Kopete
+--------------------------
+
+Use cases
+
+1) Email client or contact management app displays email sender's IM status if known.
+
+2) Email client or contact management app wants to use Kopete as transport for something (vCard?).
+
+Notes
+
+Client would need to identify addressbook entries that are IM contactable, and obtain status information from an IM app or library.
+
+David Faure says it's possible to do loose lazy binding to the app responsible for providing status information as in 1). Something to do with KTrader and DCOP. We would need some DCOP to return status info to the client from the IM app.
+
+Showing the IM status of a contact in KMail, in KAddressBook....
+A "reply-by-IM" action in KMail
+
+such as things could be done. but how?
+
+The application could know if this user is registered to an IM by looking at messaging fields
+But how to use such as information available only at runtime, like the Status.
+
+1) Saving the status to KABC
+ --the problem is that the status can not be reseted to offline if kopete crashed
+
+2) KMail could contact Kopete via DCop
+ --too bad if i want to use another IM Client?
+
+3) add an interface in kabc (or other) which could contact Kopete, or the IM which is actualy running. This is in effect what dfaure suggested above.
+
+KDE Games/Desktop sharing use of Kopete
+---------------------------------------
+Use cases
+
+1) Program wants to use Kopete as transport for game/desktop sharing invitation.
+
+Notes
+
+As above, client must identify IM contactable addressbook entries, obtain status information and get the IM app to send the invite (inline/file transfer). Some protocols may not support file transfer though. Do we need a capabilities interface on the IM apps, or just use the lowest common denominator?
+
+On receipt of the invitation, IM app needs to recognize the invite and relay it to the recipient (similar lazy loose binding?). In Kopete we could do this in a plugin, quite neatly.
+
+Some problem:
+ Each protocol does that in a different way. The Game/Application need to know some protocol specific stuff (the MSN application ID for example)
+
+ I was thinking about Atlantik, and other games that are protocol independent. "Protocol games" are protocols' problems.
+
+
+
+ 'myself' integration
+=======================
+
+Some protocols allow to send some personal information like real name, phone number, address, ... (see icq user info)
+It would be cool if theses information about the users itself could be obtained from KAB.
+
+see also Bug 63297: "meta-contact" global(local) display nickname for all accounts
+
+Notes: some user might don't want to export their info the the server.
+
+Yes this would be cool. I think we need a general UI to control the flow of contact information between kabc and kopete. The myself issue is the same as the metacontacts issue.
+
+
+What about the API?
+=====================
+
+ In KABC
+---------
+The actual add to kabc could be performed in KopeteContactList::addMetaContact()
+
+ACW::accept() -> KopeteContactList::adddMetaContact()
+
+Here is a suggestion. I don't know if that already works like that in KABC.
+AFAIK by reading the KABC API documentation, it use somme different c++ function to access/modify some stuff.
+With this implementation, it is not possible to make that transparent for plugin. That mean the PGP plugin need to call directly the KABC API (with Addressee::pgpKey()) for example.
+
+My idea is to give for each fields a String, like for MIME-Type
+so fields could be application/pgp-public-key (do you see what i mean?)
+We can even add the possibility to have several entries for one field (StringList) (several emails for example)
+
+Theses key could be stendardized (freedesktop.org ?)
+
+ In Kopete:
+------------
+
+I think we will need to change the xml format of the contactlist, and the whole pluginData thing.
+
+The idea is to have the same fields in Kopete than in KABC.
+Gof: This was not our idea, we intend not to pollute the KABC with any more extra fields than necessary. Kopete's info may not be useful to other IM apps (Will)
+
+so for example, the pgp plugin will request the public key:
+metacontact->data("application/pgp-public-key");
+
+if the contact is in KABC, then libkopete will request info from kabc. Else, it will take the data stored internaly in the contactlist.xml (Gof)
+
+This is a good question. I think since we have the constraint that we have to keep kabc clean, this is the way to do it. (Will)
+
+for contacts same things.
+Anyway? what if we have several contact in one metacontact.
+for example:
+
+messenging/msn => xxx@msn.com ; yyy@msn.com (it's a StringList)
+messenging/nickname => Mr. X ; Mr. Y
+
+that looks fine, but how to make sure than Mr. X is for xxx@msn.com, imagine if we have icq contact, or other? (Gof)
+We should never store nicknames in kabc. The metacontact name should be taken from the kabc formatted name (Will)
+
+Of course every fields shouldn't be stored in KABC (for example: MSN's groups, or others)
+to know what can be saved or not in kabc, i see two ways:
+1) KopeteMetaContact::setData( QString key , QString data , bool saveToKAB);
+2) if the key is or not recognized by KABC. example, if i save "messaging/msn-groups" KAB is not able to store it, so don't store it in KABC
+
+
+Syncing contactlist info currently takes place when Kopete exits. other kabc apps write kabc during their runs. We will have to make changes to do this and to make sure that we don't try to write 200 kabc entries simultaneously with individual tickets after fetching the SSCL for a new account.
+
+I don't think that syncing the KAB on an SSCL fetch is necessary. The only information we get when we fetch the SSCL is the contact name of that person who is on our SSCL, it's not until later, perhaps when we request the user info that we update the KAB. (Matt)
+
+
+
+
+|How to share data when multiple contact in a metacontact?|
++---------------------------------------------------------+
+
+The problem is that there are several sources of the information. example: if i have two msn contact ion the metacontact, i have maybe two pictures.
+
+Currently, two solution has been mentioned:
+1) use a per-contact value + a metacontact one. If there is only one contact, we automaticaly track the child contact one (exepted if the user selected himself the metacontact value). If not, we let the user choose what to use.
+ (like we already does for displayname)
+
+2) Use the account / contact priority to determine the value.
+
+
+
+KABC Association
+----------------
+Association creates a 1:1 relationship between KMC and Addressee. If we assume IM apps incl Kopete put IM addresses in the KABC (Messaging/msn etc) then whenever an association is made or changed, there will have to be a sync between Kopete's idea of IM addresses and those stored in KABC. Changes to KABC data could happen while Kopete is offline so this sync check needs to take place when Kopete starts. Potential problems if another IM client changes the data whilst Kopete is running or vice versa - addressees aren't locked while an apps is running, only while writing.
+
+Association with existing contact: 1) No messaging information 2) Some messaging entries already exist. Possible courses of action - add (only in KABC) accounts to MC - ask (how do we remember what the user wanted the next time we read the KABC)
+
+What if a related kabc entry is deleted while Kopete is running?
+
+Deserialising contact from contactlist.xml - do we try to check the kabc entries and create any accounts that we find there but not in the contactlist.xml?
+
+When to update KABC
+-------------------
+Kopete writes its contactlist.xml at app close. This is sufficient for kopete as the data is held in memory and not shared. Data Kopete puts in KABC is shared, however, and users will expect to be able to use that data immediately. Hence, data that will be put in KABC should be put there immediately.
+This should happen when the KABC-exposed data changes - when an MC's set of contacts changes
+NOT when starting kopete and the MC is filled with contacts!
+*) When adding a new contact (Use case: A new contact messages me, I add them to my contact list, and then I want to play a game with them, and invite them using IM, for example)
+ KopeteContactList::addMetaContact()
+*) When linking an existing contact to KABC (so other apps gain the benefit of the new link)
+ handle this in KMC::setMetaContactId() - remember issues if a link is changed, do we delete the kabc fields?
+*) When adding new contacts to a KMC.
+ KMC::addContact()
+*) When moving contacts between KMCs. KC::setMetaContact()?
+*) When a MC's KABC association is changed. - KsetMetaContact
diff --git a/kopete/Makefile.am b/kopete/Makefile.am
new file mode 100644
index 00000000..9532f16b
--- /dev/null
+++ b/kopete/Makefile.am
@@ -0,0 +1,22 @@
+SUBDIRS = libkopete kopete protocols plugins icons sounds styles
+
+api:
+ $(mkinstalldirs) $(top_builddir)/kopete/apidocs/libkopete
+ if test ! -x $(top_builddir)/kopete/apidocs/common; then \
+ $(LN_S) $(kde_libs_htmldir)/en/common $(top_builddir)/kopete/apidocs/common; \
+ fi
+ doxygen $(top_srcdir)/kopete/kopete.api
+
+
+messages:
+ $(EXTRACTRC) --context="Translators: The %FOO% placeholders are variables that are substituted in the code, please leave them untranslated" --tag-group=none --tag kopete-i18n styles/*.xsl > xml_doc.cpp
+ $(EXTRACTRC) `find . -name \*.ui -o -name \*.rc | egrep -v '(libkopete/compat|protocols/testbed)'` > rc.cpp
+ LIST=`find . -name \*.h -o -name \*.cpp -o -name \*.c | egrep -v '(libkopete/compat|protocols/testbed)'`; \
+ if test -n "$$LIST"; then \
+ $(XGETTEXT) $$LIST -o $(podir)/kopete.pot; \
+ fi
+ -rm xml_doc.cpp
+
+
+DOXYGEN_EMPTY = YES
+include $(top_srcdir)/admin/Doxyfile.am
diff --git a/kopete/README b/kopete/README
new file mode 100644
index 00000000..6da033f1
--- /dev/null
+++ b/kopete/README
@@ -0,0 +1,29 @@
+Kopete, The KDE Messenger
+Duncan Mac-Vicar Prett <duncan@kde.org>, and a cast of thousands.
+http://kopete.kde.org
+----------------------------------------------------------------------
+
+Kopete is an Instant Messaging client designed to be modular and plugin based.
+
+It requires the KDE Desktop, version 3.3 or higher.
+
+Kopete ships with a lot of protocol plugins, supporting nine different messaging
+systems. All plugins have seen numerous enhancements and bugfixes since the
+last release and should bring your Kopete experience to a whole new level.
+
+Additionally a lot of work has gone into cleaning up the core Kopete API and
+GUI with several important usability-, stability- and performance-improvements.
+
+The Kopete team consists now of several active developers, working on the
+various plugins and the core API. However, Kopete wouldn't be in the shape
+in which it is now without the various contributions from KDE developers
+all over the world and the high-quality bug reports from active users.
+Thanks to all of you for your support and your interest in Kopete!
+
+As always, more bug reports and patches are welcome.
+Please use http://bugs.kde.org.
+
+The Kopete main developers can be contacted on our mailing list,
+kopete-devel@kde.org, and on IRC in #kopete, on irc.freenode.net.
+We always welcome new contributors.
+
diff --git a/kopete/TODO b/kopete/TODO
new file mode 100644
index 00000000..e9a7a594
--- /dev/null
+++ b/kopete/TODO
@@ -0,0 +1,90 @@
+These are random snippets that should give developers a clue of what we have
+to fix before the next release.
+
+Beware: It's totally unsorted ;)
+
+================================================================================
+- Once and for all fix the way displayName is used in KopeteContact. With
+ serverside contact lists it's now impossible to rename contacts on the server
+ because some people (e.g. Olivier) want to see the nickname that a user
+ assigned and others (e.g. Martijn) want to ALWAYS see the display name of the
+ meta contact.
+ displayName and nickName should be split and depending on the user's prefs
+ displayName() should return either MC->displayName() or nickName().
+
+- Add a way to detect when a protocol goes offline involuntary. In that case
+ try a reconnect first and only leave the protocol offline if the reconnect
+ fails. Alternatively, retry reconnecting with increasing intervals.
+ -> PARTLY DONE, see KopeteAccount::disconnect() functions
+
+- Prevent attempting to send while offline where appropriate
+
+- First time wizard
+
+- Save the account order in the config file (IMPORTANT)
+
+- Interface for application invitations, and better filetransfers support
+
+- KAB integration
+ -> DONE?
+
+
+================================================================================
+
+
+OSCAR ICQ/AIM TODO ITEMS
+
+ OSCAR in general========================================================
+ - somehow sync server and local list, this is not as trivial as everybody
+ always thinks it is because you cannot sure if local changes or
+ serverside-changes caused the difference (think about two clients being used
+ for the same account, one at home and one at work).
+ Update: Actually it might be possible by saving two timestamp/size values
+ for the account. Serverside and local values.
+ Also I'm much in favor of hindering the user from offline editing the
+ contactlist.
+ X save groupID in KopeteGroups
+ X make renaming serverside contacts possible
+ (should work if kopete groupname == serverside groupname)
+ X Base Buffer class on something like QDataStream (if possible),
+ I don't like all the pointer stuff in here
+ X get rid of AIMContactList, AIMBuddy (almost gone already) and AIMGroup.
+
+ ICQ specific ===========================================================
+ X support simple icq type-2 messages so we can send/receive away
+ messages (yes, I even see this as a point for not releasing 0.7, away
+ messages ARE important or else I could take out away modes altogether)
+ X prevent passwords longer than 8 chars, it upsets the server
+ - general support for SNAC (0x15,y)
+ X read away reasons
+ X own away-reasons
+ - Support RTF
+ X honor encodings for both sides (done but incomplete)
+ - Option: Allow access from contacts on my contact list only
+ - group handling
+ (signals: KopeteMetaContact::movedToGroup, addedToGroup, removedToGroup)
+ I'm not sure what this item means, none of the other protocols listen to
+ these signals.
+ - ignore lists (needs proper group-handling)
+ - invisible list (needs proper group-handling)
+ - support sending all of your own icq userinfo to the server,
+ it's easy to do but a lot of of boring work
+
+ AIM specific ===========================================================
+ - Nothing in here yet, I'd appreciate somebody with more extensive use
+ of AIM to take over just that part of the plugin.
+
+
+================================================================================
+
+
+ MSN TODO (for the Kopete 1.0 release)
+---------------------------------------
+
+ - Handle the MSN PLUS! color codes
+ - Show internals messages in chat window when filetransfers (go with
+ the new interface for invitation in libkopete)
+
+ - Search for an MSN User (not for Kopete 1.0)
+
+================================================================================
diff --git a/kopete/VERSION b/kopete/VERSION
new file mode 100644
index 00000000..c7bfcc62
--- /dev/null
+++ b/kopete/VERSION
@@ -0,0 +1 @@
+Kopete v0.12.4
diff --git a/kopete/configure.in.in b/kopete/configure.in.in
new file mode 100644
index 00000000..802dd84d
--- /dev/null
+++ b/kopete/configure.in.in
@@ -0,0 +1,167 @@
+#MIN_CONFIG(3.3)
+AC_HAVE_GL
+KDE_INIT_DOXYGEN
+AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff,
+ AC_TRY_COMPILE([
+ #include <time.h>
+ ], [
+ struct tm tm;
+ tm.tm_gmtoff = 1;
+ ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no))
+if test $ac_cv_struct_tm_gmtoff = yes; then
+ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm])
+fi
+
+KDE_CHECK_HEADERS(knotifydialog.h)
+
+KOPETE_INCLUDES='-I$(top_srcdir)/kopete/libkopete -I$(top_builddir)/kopete/libkopete -I$(top_srcdir)/kopete/libkopete/avdevice -I$(top_srcdir)/kopete/libkopete/ui -I$(top_builddir)/kopete/libkopete/ui'
+
+AC_MSG_CHECKING([for kdelibs newer than 3.3.x])
+AC_LANG_SAVE
+AC_LANG_CPLUSPLUS
+kcompat_save_CXXFLAGS="$CXXFLAGS"
+kcompat_safe_LIBS="$LIBS"
+LIBS="$LIBS $X_EXTRA_LIBS"
+CXXFLAGS="$CXXFLAGS $all_includes"
+
+AC_TRY_COMPILE([
+#include <kdeversion.h>
+#if !( KDE_IS_VERSION( 3, 3, 90 ) )
+#error Kopete compatibility with KDE 3.3 needs to be enabled
+#endif
+],
+[
+],
+ AC_MSG_RESULT(yes)
+,
+ KOPETE_COMPAT_INCLUDES='-I$(top_srcdir)/kopete/libkopete/compat'
+ KOPETE_INCLUDES=$KOPETE_INCLUDES' $(KOPETE_COMPAT_INCLUDES)'
+ LIB_KOPETECOMPAT='$(top_builddir)/kopete/libkopete/libkopete.la'
+ AC_MSG_RESULT(no)
+)
+CXXFLAGS="$kcompat_save_CXXFLAGS"
+LIBS="$kcompat_safe_LIBS"
+AC_LANG_RESTORE
+AM_CONDITIONAL(compile_LIBKOPETE_COMPAT, test -n "$LIB_KOPETECOMPAT")
+
+
+AC_ARG_ENABLE(smpppd,
+[AC_HELP_STRING([--enable-smpppd], [enable support for the SuSE Meta PPP Daemon (smpppd) (default is NO)])],
+[
+ if test $enableval = yes; then
+ AC_DEFINE(USE_SMPPPD, 1, [enable support for the smpppd])
+ COMPILESMPPPDCS=true
+ else
+ COMPILESMPPPDCS=
+ fi
+],
+[ COMPILESMPPPDCS=
+])
+AM_CONDITIONAL(include_smpppdcs, test -n "$COMPILESMPPPDCS")
+
+KDE_FIND_PATH(xml2-config, XML_CONFIG, [${exec_prefix}/bin ${prefix}/bin], [
+ AC_MSG_WARN([libxml2 not found anywhere, check ftp://xmlsoft.org/ for libxml >= 2.4.8. ])
+])
+
+if test -n "$XML_CONFIG"; then
+ vers=`$XML_CONFIG --version 2>/dev/null | sed -e 's/libxml //' | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'`
+ if test -n "$vers" && test "$vers" -ge 2004008
+ then
+ LIBXML_LIBS="`$XML_CONFIG --libs`"
+ LIBXML_RPATH=
+ for args in $LIBXML_LIBS; do
+ case $args in
+ -L*)
+ LIBXML_RPATH="$LIBXML_RPATH $args"
+ ;;
+ esac
+ done
+ LIBXML_RPATH=`echo $LIBXML_RPATH | sed -e "s/-L/-R/g"`
+ LIBXML_CFLAGS="`$XML_CONFIG --cflags`"
+
+ KDE_FIND_PATH(xmllint, XMLLINT, [${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin /usr/bin], [XMLLINT=""])
+ AC_DEFINE_UNQUOTED(XMLLINT, "$XMLLINT", [Defines the executable of xmllint])
+ else
+ AC_MSG_WARN([You need at least libxml 2.4.8])
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kopete"
+ fi
+fi
+
+
+ KDE_FIND_PATH(xslt-config, XSLT_CONFIG, [${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin /usr/bin], [
+ AC_MSG_WARN([Could not find libxslt anywhere, check ftp://xmlsoft.org/ for libxslt >= 1.0.7.])
+ ])
+
+ if test -n "$XSLT_CONFIG"; then
+ vers=`$XSLT_CONFIG --version 2>/dev/null | awk 'BEGIN { FS = "."; } { printf "%d", ($1 * 1000 + $2) * 1000 + $3;}'`
+ if test -n "$vers" && test "$vers" -ge 1000007; then
+ LIBXSLT_LIBS="`$XSLT_CONFIG --libs`"
+ LIBXSLT_RPATH=
+ for args in $LIBXSLT_LIBS; do
+ case $args in
+ -L*)
+ LIBXSLT_RPATH="$LIBXSLT_RPATH $args"
+ ;;
+ esac
+ done
+ LIBXSLT_RPATH=`echo $LIBXSLT_RPATH | sed -e "s/-L/-R/g"`
+ LIBXSLT_CFLAGS="`$XSLT_CONFIG --cflags`"
+ AC_DEFINE(HAVE_XSLT, 1, [Define if you have xslt libraries and header files])
+ else
+ AC_WARN([You need at least libxslt 1.0.7])
+ fi
+ fi
+
+if test ! "$USE_RPATH" = "yes"; then
+ LIBXSLT_RPATH=
+ LIBXML_RPATH=
+fi
+
+if test -z "$XML_CONFIG"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kopete"
+fi
+
+if test -z "$XSLT_CONFIG"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kopete"
+fi
+AC_SUBST(LIBXSLT_LIBS)
+AC_SUBST(LIBXSLT_CFLAGS)
+AC_SUBST(LIBXSLT_RPATH)
+
+AC_SUBST(LIBXML_LIBS)
+AC_SUBST(LIBXML_CFLAGS)
+AC_SUBST(LIBXML_RPATH)
+
+AC_SUBST(KOPETE_INCLUDES)
+AC_SUBST(KOPETE_COMPAT_INCLUDES)
+AC_SUBST(LIB_KOPETECOMPAT)
+
+# -- Check for installed Valgrind headers --------------------
+
+AC_MSG_CHECKING([for valgrind.h])
+
+AC_TRY_COMPILE([
+#define __VALGRIND_SOMESKIN_H
+#include <valgrind/valgrind.h>
+],
+[
+],
+ ac_have_valgrind_h=yes
+,
+ ac_have_valgrind_h=no
+)
+
+if test $ac_have_valgrind_h = yes; then
+ AC_DEFINE(HAVE_VALGRIND_H, 1, [Define if you have valgrind.h installed])
+fi
+
+AC_MSG_RESULT($ac_have_valgrind_h)
+
+# -- End valgrind ----------------------------------------------
+
+# -- Determine pointer size for sqlite -------------------------
+
+KDE_CHECK_TYPES
+AC_DEFINE(SQLITE_PTR_SZ, SIZEOF_CHAR_P, [Determine pointer size for SQLite])
+
+# -- End sqlite ------------------------------------------------
diff --git a/kopete/icons/Makefile.am b/kopete/icons/Makefile.am
new file mode 100644
index 00000000..a4b97f06
--- /dev/null
+++ b/kopete/icons/Makefile.am
@@ -0,0 +1 @@
+KDE_ICON=AUTO
diff --git a/kopete/icons/cr128-action-voicecall.png b/kopete/icons/cr128-action-voicecall.png
new file mode 100644
index 00000000..7afcf712
--- /dev/null
+++ b/kopete/icons/cr128-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr128-action-webcamreceive.png b/kopete/icons/cr128-action-webcamreceive.png
new file mode 100644
index 00000000..fe1ab8fb
--- /dev/null
+++ b/kopete/icons/cr128-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr128-action-webcamsend.png b/kopete/icons/cr128-action-webcamsend.png
new file mode 100644
index 00000000..7f162e68
--- /dev/null
+++ b/kopete/icons/cr128-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/cr16-action-account_offline_overlay.png b/kopete/icons/cr16-action-account_offline_overlay.png
new file mode 100644
index 00000000..b4ccc595
--- /dev/null
+++ b/kopete/icons/cr16-action-account_offline_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-add_user.png b/kopete/icons/cr16-action-add_user.png
new file mode 100644
index 00000000..cec9f51f
--- /dev/null
+++ b/kopete/icons/cr16-action-add_user.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_away_overlay.png b/kopete/icons/cr16-action-contact_away_overlay.png
new file mode 100644
index 00000000..5a8b8729
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_away_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_busy_overlay.png b/kopete/icons/cr16-action-contact_busy_overlay.png
new file mode 100644
index 00000000..1ddd82b6
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_busy_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_food_overlay.png b/kopete/icons/cr16-action-contact_food_overlay.png
new file mode 100644
index 00000000..c08d5bc8
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_food_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_invisible_overlay.png b/kopete/icons/cr16-action-contact_invisible_overlay.png
new file mode 100644
index 00000000..86ed1a26
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_invisible_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_phone_overlay.png b/kopete/icons/cr16-action-contact_phone_overlay.png
new file mode 100644
index 00000000..42ed83c3
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_phone_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-contact_xa_overlay.png b/kopete/icons/cr16-action-contact_xa_overlay.png
new file mode 100644
index 00000000..43d03896
--- /dev/null
+++ b/kopete/icons/cr16-action-contact_xa_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-delete_user.png b/kopete/icons/cr16-action-delete_user.png
new file mode 100644
index 00000000..568b74ee
--- /dev/null
+++ b/kopete/icons/cr16-action-delete_user.png
Binary files differ
diff --git a/kopete/icons/cr16-action-edit_user.png b/kopete/icons/cr16-action-edit_user.png
new file mode 100644
index 00000000..fa02d83c
--- /dev/null
+++ b/kopete/icons/cr16-action-edit_user.png
Binary files differ
diff --git a/kopete/icons/cr16-action-emoticon.png b/kopete/icons/cr16-action-emoticon.png
new file mode 100644
index 00000000..3567cd09
--- /dev/null
+++ b/kopete/icons/cr16-action-emoticon.png
Binary files differ
diff --git a/kopete/icons/cr16-action-kopeteavailable.png b/kopete/icons/cr16-action-kopeteavailable.png
new file mode 100644
index 00000000..5377f424
--- /dev/null
+++ b/kopete/icons/cr16-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/cr16-action-kopeteaway.png b/kopete/icons/cr16-action-kopeteaway.png
new file mode 100644
index 00000000..cdaa5b29
--- /dev/null
+++ b/kopete/icons/cr16-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/cr16-action-kopeteeditstatusmessage.png b/kopete/icons/cr16-action-kopeteeditstatusmessage.png
new file mode 100644
index 00000000..fc9884bf
--- /dev/null
+++ b/kopete/icons/cr16-action-kopeteeditstatusmessage.png
Binary files differ
diff --git a/kopete/icons/cr16-action-kopetestatusmessage.png b/kopete/icons/cr16-action-kopetestatusmessage.png
new file mode 100644
index 00000000..dff59936
--- /dev/null
+++ b/kopete/icons/cr16-action-kopetestatusmessage.png
Binary files differ
diff --git a/kopete/icons/cr16-action-metacontact_away.png b/kopete/icons/cr16-action-metacontact_away.png
new file mode 100644
index 00000000..40699175
--- /dev/null
+++ b/kopete/icons/cr16-action-metacontact_away.png
Binary files differ
diff --git a/kopete/icons/cr16-action-metacontact_offline.png b/kopete/icons/cr16-action-metacontact_offline.png
new file mode 100644
index 00000000..f0e3e14e
--- /dev/null
+++ b/kopete/icons/cr16-action-metacontact_offline.png
Binary files differ
diff --git a/kopete/icons/cr16-action-metacontact_online.png b/kopete/icons/cr16-action-metacontact_online.png
new file mode 100644
index 00000000..4060007a
--- /dev/null
+++ b/kopete/icons/cr16-action-metacontact_online.png
Binary files differ
diff --git a/kopete/icons/cr16-action-metacontact_unknown.png b/kopete/icons/cr16-action-metacontact_unknown.png
new file mode 100644
index 00000000..290bc852
--- /dev/null
+++ b/kopete/icons/cr16-action-metacontact_unknown.png
Binary files differ
diff --git a/kopete/icons/cr16-action-newmsg.png b/kopete/icons/cr16-action-newmsg.png
new file mode 100644
index 00000000..d42bb0ae
--- /dev/null
+++ b/kopete/icons/cr16-action-newmsg.png
Binary files differ
diff --git a/kopete/icons/cr16-action-search_user.png b/kopete/icons/cr16-action-search_user.png
new file mode 100644
index 00000000..d199579a
--- /dev/null
+++ b/kopete/icons/cr16-action-search_user.png
Binary files differ
diff --git a/kopete/icons/cr16-action-show_offliners.png b/kopete/icons/cr16-action-show_offliners.png
new file mode 100644
index 00000000..93cefe2b
--- /dev/null
+++ b/kopete/icons/cr16-action-show_offliners.png
Binary files differ
diff --git a/kopete/icons/cr16-action-status_unknown.png b/kopete/icons/cr16-action-status_unknown.png
new file mode 100644
index 00000000..fd49f31f
--- /dev/null
+++ b/kopete/icons/cr16-action-status_unknown.png
Binary files differ
diff --git a/kopete/icons/cr16-action-status_unknown_overlay.png b/kopete/icons/cr16-action-status_unknown_overlay.png
new file mode 100644
index 00000000..21cff88d
--- /dev/null
+++ b/kopete/icons/cr16-action-status_unknown_overlay.png
Binary files differ
diff --git a/kopete/icons/cr16-action-voicecall.png b/kopete/icons/cr16-action-voicecall.png
new file mode 100644
index 00000000..42b2f49c
--- /dev/null
+++ b/kopete/icons/cr16-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr16-action-webcamreceive.png b/kopete/icons/cr16-action-webcamreceive.png
new file mode 100644
index 00000000..0d177d9f
--- /dev/null
+++ b/kopete/icons/cr16-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr16-action-webcamsend.png b/kopete/icons/cr16-action-webcamsend.png
new file mode 100644
index 00000000..b1572b73
--- /dev/null
+++ b/kopete/icons/cr16-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/cr22-action-account_offline_overlay.png b/kopete/icons/cr22-action-account_offline_overlay.png
new file mode 100644
index 00000000..89d0f10f
--- /dev/null
+++ b/kopete/icons/cr22-action-account_offline_overlay.png
Binary files differ
diff --git a/kopete/icons/cr22-action-add_user.png b/kopete/icons/cr22-action-add_user.png
new file mode 100644
index 00000000..539debe0
--- /dev/null
+++ b/kopete/icons/cr22-action-add_user.png
Binary files differ
diff --git a/kopete/icons/cr22-action-delete_user.png b/kopete/icons/cr22-action-delete_user.png
new file mode 100644
index 00000000..134f27f5
--- /dev/null
+++ b/kopete/icons/cr22-action-delete_user.png
Binary files differ
diff --git a/kopete/icons/cr22-action-edit_user.png b/kopete/icons/cr22-action-edit_user.png
new file mode 100644
index 00000000..9e53bfa4
--- /dev/null
+++ b/kopete/icons/cr22-action-edit_user.png
Binary files differ
diff --git a/kopete/icons/cr22-action-kopeteavailable.png b/kopete/icons/cr22-action-kopeteavailable.png
new file mode 100644
index 00000000..1449318f
--- /dev/null
+++ b/kopete/icons/cr22-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/cr22-action-kopeteaway.png b/kopete/icons/cr22-action-kopeteaway.png
new file mode 100644
index 00000000..370144e0
--- /dev/null
+++ b/kopete/icons/cr22-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/cr22-action-kopeteeditstatusmessage.png b/kopete/icons/cr22-action-kopeteeditstatusmessage.png
new file mode 100644
index 00000000..ce8b2267
--- /dev/null
+++ b/kopete/icons/cr22-action-kopeteeditstatusmessage.png
Binary files differ
diff --git a/kopete/icons/cr22-action-search_user.png b/kopete/icons/cr22-action-search_user.png
new file mode 100644
index 00000000..5f637d96
--- /dev/null
+++ b/kopete/icons/cr22-action-search_user.png
Binary files differ
diff --git a/kopete/icons/cr22-action-show_offliners.png b/kopete/icons/cr22-action-show_offliners.png
new file mode 100644
index 00000000..473181da
--- /dev/null
+++ b/kopete/icons/cr22-action-show_offliners.png
Binary files differ
diff --git a/kopete/icons/cr22-action-voicecall.png b/kopete/icons/cr22-action-voicecall.png
new file mode 100644
index 00000000..8c5c3c52
--- /dev/null
+++ b/kopete/icons/cr22-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr22-action-webcamreceive.png b/kopete/icons/cr22-action-webcamreceive.png
new file mode 100644
index 00000000..cb5a2a2b
--- /dev/null
+++ b/kopete/icons/cr22-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr22-action-webcamsend.png b/kopete/icons/cr22-action-webcamsend.png
new file mode 100644
index 00000000..55d5ef4d
--- /dev/null
+++ b/kopete/icons/cr22-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/cr32-action-account_offline_overlay.png b/kopete/icons/cr32-action-account_offline_overlay.png
new file mode 100644
index 00000000..07729057
--- /dev/null
+++ b/kopete/icons/cr32-action-account_offline_overlay.png
Binary files differ
diff --git a/kopete/icons/cr32-action-add_user.png b/kopete/icons/cr32-action-add_user.png
new file mode 100644
index 00000000..6b01866e
--- /dev/null
+++ b/kopete/icons/cr32-action-add_user.png
Binary files differ
diff --git a/kopete/icons/cr32-action-delete_user.png b/kopete/icons/cr32-action-delete_user.png
new file mode 100644
index 00000000..e261fa85
--- /dev/null
+++ b/kopete/icons/cr32-action-delete_user.png
Binary files differ
diff --git a/kopete/icons/cr32-action-edit_user.png b/kopete/icons/cr32-action-edit_user.png
new file mode 100644
index 00000000..d7720925
--- /dev/null
+++ b/kopete/icons/cr32-action-edit_user.png
Binary files differ
diff --git a/kopete/icons/cr32-action-kopeteavailable.png b/kopete/icons/cr32-action-kopeteavailable.png
new file mode 100644
index 00000000..24d280b9
--- /dev/null
+++ b/kopete/icons/cr32-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/cr32-action-kopeteaway.png b/kopete/icons/cr32-action-kopeteaway.png
new file mode 100644
index 00000000..c13f5224
--- /dev/null
+++ b/kopete/icons/cr32-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/cr32-action-kopeteeditstatusmessage.png b/kopete/icons/cr32-action-kopeteeditstatusmessage.png
new file mode 100644
index 00000000..1d691451
--- /dev/null
+++ b/kopete/icons/cr32-action-kopeteeditstatusmessage.png
Binary files differ
diff --git a/kopete/icons/cr32-action-metacontact_away.png b/kopete/icons/cr32-action-metacontact_away.png
new file mode 100644
index 00000000..68fe27e4
--- /dev/null
+++ b/kopete/icons/cr32-action-metacontact_away.png
Binary files differ
diff --git a/kopete/icons/cr32-action-metacontact_offline.png b/kopete/icons/cr32-action-metacontact_offline.png
new file mode 100644
index 00000000..27a80af2
--- /dev/null
+++ b/kopete/icons/cr32-action-metacontact_offline.png
Binary files differ
diff --git a/kopete/icons/cr32-action-metacontact_online.png b/kopete/icons/cr32-action-metacontact_online.png
new file mode 100644
index 00000000..083a8eed
--- /dev/null
+++ b/kopete/icons/cr32-action-metacontact_online.png
Binary files differ
diff --git a/kopete/icons/cr32-action-metacontact_unknown.png b/kopete/icons/cr32-action-metacontact_unknown.png
new file mode 100644
index 00000000..52ab79ed
--- /dev/null
+++ b/kopete/icons/cr32-action-metacontact_unknown.png
Binary files differ
diff --git a/kopete/icons/cr32-action-newmessage.mng b/kopete/icons/cr32-action-newmessage.mng
new file mode 100644
index 00000000..a3fbc8f8
--- /dev/null
+++ b/kopete/icons/cr32-action-newmessage.mng
Binary files differ
diff --git a/kopete/icons/cr32-action-newmsg.png b/kopete/icons/cr32-action-newmsg.png
new file mode 100644
index 00000000..b18cb24e
--- /dev/null
+++ b/kopete/icons/cr32-action-newmsg.png
Binary files differ
diff --git a/kopete/icons/cr32-action-search_user.png b/kopete/icons/cr32-action-search_user.png
new file mode 100644
index 00000000..48136240
--- /dev/null
+++ b/kopete/icons/cr32-action-search_user.png
Binary files differ
diff --git a/kopete/icons/cr32-action-show_offliners.png b/kopete/icons/cr32-action-show_offliners.png
new file mode 100644
index 00000000..875d19ad
--- /dev/null
+++ b/kopete/icons/cr32-action-show_offliners.png
Binary files differ
diff --git a/kopete/icons/cr32-action-voicecall.png b/kopete/icons/cr32-action-voicecall.png
new file mode 100644
index 00000000..f8719dc9
--- /dev/null
+++ b/kopete/icons/cr32-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr32-action-webcamreceive.png b/kopete/icons/cr32-action-webcamreceive.png
new file mode 100644
index 00000000..6db6c8f6
--- /dev/null
+++ b/kopete/icons/cr32-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr32-action-webcamsend.png b/kopete/icons/cr32-action-webcamsend.png
new file mode 100644
index 00000000..7c0b1932
--- /dev/null
+++ b/kopete/icons/cr32-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/cr48-action-kopeteavailable.png b/kopete/icons/cr48-action-kopeteavailable.png
new file mode 100644
index 00000000..51678b32
--- /dev/null
+++ b/kopete/icons/cr48-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/cr48-action-kopeteaway.png b/kopete/icons/cr48-action-kopeteaway.png
new file mode 100644
index 00000000..f31bf9b7
--- /dev/null
+++ b/kopete/icons/cr48-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/cr48-action-metacontact_away.png b/kopete/icons/cr48-action-metacontact_away.png
new file mode 100644
index 00000000..1b1e409b
--- /dev/null
+++ b/kopete/icons/cr48-action-metacontact_away.png
Binary files differ
diff --git a/kopete/icons/cr48-action-metacontact_offline.png b/kopete/icons/cr48-action-metacontact_offline.png
new file mode 100644
index 00000000..5a67789d
--- /dev/null
+++ b/kopete/icons/cr48-action-metacontact_offline.png
Binary files differ
diff --git a/kopete/icons/cr48-action-metacontact_online.png b/kopete/icons/cr48-action-metacontact_online.png
new file mode 100644
index 00000000..eeeebaad
--- /dev/null
+++ b/kopete/icons/cr48-action-metacontact_online.png
Binary files differ
diff --git a/kopete/icons/cr48-action-voicecall.png b/kopete/icons/cr48-action-voicecall.png
new file mode 100644
index 00000000..2633f90a
--- /dev/null
+++ b/kopete/icons/cr48-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr48-action-webcamreceive.png b/kopete/icons/cr48-action-webcamreceive.png
new file mode 100644
index 00000000..e5b45d7b
--- /dev/null
+++ b/kopete/icons/cr48-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr48-action-webcamsend.png b/kopete/icons/cr48-action-webcamsend.png
new file mode 100644
index 00000000..d433acf8
--- /dev/null
+++ b/kopete/icons/cr48-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/cr64-action-voicecall.png b/kopete/icons/cr64-action-voicecall.png
new file mode 100644
index 00000000..5c399854
--- /dev/null
+++ b/kopete/icons/cr64-action-voicecall.png
Binary files differ
diff --git a/kopete/icons/cr64-action-webcamreceive.png b/kopete/icons/cr64-action-webcamreceive.png
new file mode 100644
index 00000000..cab7eb6b
--- /dev/null
+++ b/kopete/icons/cr64-action-webcamreceive.png
Binary files differ
diff --git a/kopete/icons/cr64-action-webcamsend.png b/kopete/icons/cr64-action-webcamsend.png
new file mode 100644
index 00000000..97b5ea05
--- /dev/null
+++ b/kopete/icons/cr64-action-webcamsend.png
Binary files differ
diff --git a/kopete/icons/crsc-action-account_offline_overlay.svgz b/kopete/icons/crsc-action-account_offline_overlay.svgz
new file mode 100644
index 00000000..9356668f
--- /dev/null
+++ b/kopete/icons/crsc-action-account_offline_overlay.svgz
Binary files differ
diff --git a/kopete/icons/hi16-action-emoticon.png b/kopete/icons/hi16-action-emoticon.png
new file mode 100644
index 00000000..05c8fb35
--- /dev/null
+++ b/kopete/icons/hi16-action-emoticon.png
Binary files differ
diff --git a/kopete/icons/hi16-action-kopeteavailable.png b/kopete/icons/hi16-action-kopeteavailable.png
new file mode 100644
index 00000000..25d3dc9c
--- /dev/null
+++ b/kopete/icons/hi16-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/hi16-action-kopeteaway.png b/kopete/icons/hi16-action-kopeteaway.png
new file mode 100644
index 00000000..10227908
--- /dev/null
+++ b/kopete/icons/hi16-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/hi16-action-newmsg.png b/kopete/icons/hi16-action-newmsg.png
new file mode 100644
index 00000000..29f597a3
--- /dev/null
+++ b/kopete/icons/hi16-action-newmsg.png
Binary files differ
diff --git a/kopete/icons/hi16-action-status_unknown.png b/kopete/icons/hi16-action-status_unknown.png
new file mode 100644
index 00000000..63e6eb17
--- /dev/null
+++ b/kopete/icons/hi16-action-status_unknown.png
Binary files differ
diff --git a/kopete/icons/hi16-action-status_unknown_overlay.png b/kopete/icons/hi16-action-status_unknown_overlay.png
new file mode 100644
index 00000000..b7e6d778
--- /dev/null
+++ b/kopete/icons/hi16-action-status_unknown_overlay.png
Binary files differ
diff --git a/kopete/icons/hi22-action-kopeteavailable.png b/kopete/icons/hi22-action-kopeteavailable.png
new file mode 100644
index 00000000..16dcc8f1
--- /dev/null
+++ b/kopete/icons/hi22-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/hi22-action-kopeteaway.png b/kopete/icons/hi22-action-kopeteaway.png
new file mode 100644
index 00000000..f49afcc9
--- /dev/null
+++ b/kopete/icons/hi22-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/hi32-action-kopeteavailable.png b/kopete/icons/hi32-action-kopeteavailable.png
new file mode 100644
index 00000000..80969297
--- /dev/null
+++ b/kopete/icons/hi32-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/hi32-action-kopeteaway.png b/kopete/icons/hi32-action-kopeteaway.png
new file mode 100644
index 00000000..ede9cada
--- /dev/null
+++ b/kopete/icons/hi32-action-kopeteaway.png
Binary files differ
diff --git a/kopete/icons/hi32-action-newmessage.mng b/kopete/icons/hi32-action-newmessage.mng
new file mode 100644
index 00000000..a3fbc8f8
--- /dev/null
+++ b/kopete/icons/hi32-action-newmessage.mng
Binary files differ
diff --git a/kopete/icons/hi48-action-kopeteavailable.png b/kopete/icons/hi48-action-kopeteavailable.png
new file mode 100644
index 00000000..52fdad8f
--- /dev/null
+++ b/kopete/icons/hi48-action-kopeteavailable.png
Binary files differ
diff --git a/kopete/icons/hi48-action-kopeteaway.png b/kopete/icons/hi48-action-kopeteaway.png
new file mode 100644
index 00000000..b35a47de
--- /dev/null
+++ b/kopete/icons/hi48-action-kopeteaway.png
Binary files differ
diff --git a/kopete/kopete.api b/kopete/kopete.api
new file mode 100644
index 00000000..95a196aa
--- /dev/null
+++ b/kopete/kopete.api
@@ -0,0 +1,171 @@
+# Doxygen configuration generated by Doxywizard version 0.1
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+PROJECT_NAME = Kopete
+PROJECT_NUMBER = 0.10.90
+OUTPUT_DIRECTORY = apidocs
+OUTPUT_LANGUAGE = English
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ALWAYS_DETAILED_SEC = YES
+FULL_PATH_NAMES = NO
+STRIP_FROM_PATH =
+INTERNAL_DOCS = NO
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = YES
+REFERENCES_RELATION = YES
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+VERBATIM_HEADERS = YES
+SHOW_INCLUDE_FILES = YES
+JAVADOC_AUTOBRIEF = NO
+INHERIT_DOCS = YES
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+DISTRIBUTE_GROUP_DOC = NO
+TAB_SIZE = 8
+ENABLED_SECTIONS =
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = NO
+GENERATE_BUGLIST = NO
+GENERATE_DEPRECATEDLIST= YES
+ALIASES =
+MAX_INITIALIZER_LINES = 30
+OPTIMIZE_OUTPUT_FOR_C = NO
+SHOW_USED_FILES = YES
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_FORMAT =
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = libkopete libkopete/ui
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE_PATTERNS = *.moc.* \
+ moc* \
+ *.all_cpp.* \
+ *unload.* \
+ */test/* \
+ */tests/*
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX = Kopete
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_HEADER = apidocs/common/header.html
+HTML_FOOTER = apidocs/common/footer.html
+HTML_STYLESHEET = apidocs/common/doxygen.css
+HTML_ALIGN_MEMBERS = YES
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = YES
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4wide
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = NO
+USE_PDFLATEX = NO
+LATEX_BATCHMODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE = kopete.tag
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = NO
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+HIDE_UNDOC_RELATIONS = NO
+HAVE_DOT = YES
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = NO
+UML_LOOK = NO
+TEMPLATE_RELATIONS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MAX_DOT_GRAPH_WIDTH = 1024
+MAX_DOT_GRAPH_HEIGHT = 1024
+MAX_DOT_GRAPH_DEPTH = 0
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
diff --git a/kopete/kopete/Makefile.am b/kopete/kopete/Makefile.am
new file mode 100644
index 00000000..eb278998
--- /dev/null
+++ b/kopete/kopete/Makefile.am
@@ -0,0 +1,54 @@
+kopete_srcdir=$(top_srcdir)/kopete
+kopete_builddir=$(top_builddir)/kopete
+
+INCLUDES = -I$(kopete_srcdir)/kopete
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(kopete_srcdir)/kopete/config \
+ -I$(kopete_srcdir)/kopete/addaccountwizard \
+ -I$(kopete_srcdir)/kopete/addcontactwizard \
+ -I$(kopete_srcdir)/kopete/config/accounts \
+ -I$(kopete_builddir)/kopete/addcontactwizard \
+ -I$(kopete_builddir)/libkopete/ \
+ -I$(kopete_srcdir)/libkopete/ui \
+ -I$(kopete_srcdir)/kopete/contactlist \
+ -I$(kopete_builddir)/kopete/contactlist \
+ -I$(kopete_srcdir)/kopete/chatwindow \
+ -I$(kopete_srcdir)/kopete/config/plugins \
+ -I$(kopete_builddir)/kopete/config \
+ -I$(kopete_srcdir)/libkopete/private \
+ -I$(kopete_srcdir)/libkopete/avdevice \
+ -I$(kopete_srcdir) \
+ $(all_includes)
+KDE_ICON = AUTO
+METASOURCES = AUTO
+
+SUBDIRS = addaccountwizard addcontactwizard contactlist chatwindow config . kconf_update
+
+bin_PROGRAMS = kopete
+kopete_SOURCES = main.cpp kopeteapplication.cpp kopeteiface.cpp \
+ kopeteiface.skel systemtray.cpp kopeteballoon.cpp kopetewindow.cpp \
+ kopeteaccountstatusbaricon.cpp kimifaceimpl.cpp kimiface.skel kopeteeditglobalidentitywidget.cpp \
+ groupkabcselectorwidget.ui
+
+kimiface_DIR = $(kde_includes)
+
+kopete_LDFLAGS = -no-undefined $(all_libraries) $(KDE_RPATH) -lktexteditor
+kopete_LDADD = \
+ addcontactwizard/libkopeteaddcontactwizard.la \
+ addaccountwizard/libkopeteaddaccountwizard.la \
+ contactlist/libkopetecontactlist.la \
+ config/plugins/libkopetepluginconfig.la \
+ ../libkopete/libkopete.la \
+ ../libkopete/ui/libkopeteui.la
+
+mydatadir = $(kde_datadir)/kopete
+mydata_DATA = kopeteui.rc eventsrc
+
+mimedir = $(kde_mimedir)/application
+mime_DATA = x-kopete-emoticons.desktop
+
+xdg_apps_DATA = kopete.desktop
+
+# vim: set noet:
+noinst_HEADERS = kimifaceimpl.h
diff --git a/kopete/kopete/addaccountwizard/Makefile.am b/kopete/kopete/addaccountwizard/Makefile.am
new file mode 100644
index 00000000..cb491b46
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteaddaccountwizard.la
+noinst_HEADERS = addaccountwizard.h
+
+libkopeteaddaccountwizard_la_SOURCES = addaccountwizardpage1.ui addaccountwizardpage2.ui addaccountwizardpage3.ui addaccountwizard.cpp
+libkopeteaddaccountwizard_la_LDFLAGS = $(all_libraries) -no-undefined
+libkopeteaddaccountwizard_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KDEUI)
+
+# vim: set noet:
+
diff --git a/kopete/kopete/addaccountwizard/addaccountwizard.cpp b/kopete/kopete/addaccountwizard/addaccountwizard.cpp
new file mode 100644
index 00000000..5518c536
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/addaccountwizard.cpp
@@ -0,0 +1,222 @@
+/*
+ addaccountwizard.cpp - Kopete Add Account Wizard
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "addaccountwizard.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+
+#include <kcolorbutton.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kplugininfo.h>
+
+#include "addaccountwizardpage1.h"
+#include "addaccountwizardpage2.h"
+#include "editaccountwidget.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteprotocol.h"
+#include "kopetepluginmanager.h"
+
+AddAccountWizard::AddAccountWizard( QWidget *parent, const char *name, bool modal, bool firstRun )
+ :
+ KWizard(parent, name, modal, WDestructiveClose),
+ m_accountPage(0),
+ m_proto(0)
+{
+ // setup the select service page
+ m_selectService = new AddAccountWizardPage1(this);
+ if ( firstRun )
+ m_selectService->m_header->setText( i18n( "1st message shown to users on first run of Kopete. Please keep the formatting.", "<h2>Welcome to Kopete</h2><p>Which messaging service do you want to connect to?</p>") );
+ addPage(m_selectService, m_selectService->caption());
+ setNextEnabled(m_selectService, false);
+
+ // setup the final page
+ m_finish = new AddAccountWizardPage2(this);
+ if ( firstRun )
+ m_finish->m_header->setText( i18n( "2nd message shown to users on first run of Kopete. Please keep the formatting.", "<h2>Congratulations</h2><p>You have finished configuring the account. You can add more accounts with <i>Settings->Configure</i>. Please click the \"Finish\" button.</p>") );
+ addPage(m_finish, m_finish->caption());
+ setFinishEnabled(m_finish, true);
+
+ // add the available messanger services to the dialogs list
+ QValueList<KPluginInfo *> protocols = Kopete::PluginManager::self()->availablePlugins("Protocols");
+ for (QValueList<KPluginInfo *>::Iterator it = protocols.begin(); it != protocols.end(); ++it)
+ {
+ QListViewItem *pluginItem = new QListViewItem(m_selectService->protocolListView);
+ pluginItem->setPixmap(0, SmallIcon((*it)->icon()));
+ pluginItem->setText(0, (*it)->name());
+ pluginItem->setText(1, (*it)->comment());
+
+ m_protocolItems.insert(pluginItem, *it);
+ }
+
+ // focus the ListView and select the first item
+ QListView &protocol_list = *m_selectService->protocolListView;
+ protocol_list.setFocus();
+ if (protocol_list.childCount() > 0)
+ {
+ protocol_list.setSelected(protocol_list.firstChild(), true);
+ }
+
+ // hook up the user input
+ connect(m_selectService->protocolListView, SIGNAL(clicked(QListViewItem *)),
+ this, SLOT(slotProtocolListClicked(QListViewItem *)));
+ connect(m_selectService->protocolListView, SIGNAL(selectionChanged(QListViewItem *)),
+ this, SLOT( slotProtocolListClicked(QListViewItem *)));
+ connect(m_selectService->protocolListView, SIGNAL(doubleClicked(QListViewItem *)),
+ this, SLOT(slotProtocolListDoubleClicked(QListViewItem *)));
+}
+
+void AddAccountWizard::slotProtocolListClicked( QListViewItem * )
+{
+ // Make sure a protocol is selected before allowing the user to continue
+ setNextEnabled(m_selectService, m_selectService->protocolListView->selectedItem() != 0);
+}
+
+void AddAccountWizard::slotProtocolListDoubleClicked( QListViewItem *lvi )
+{
+ // proceed to the next wizard page if we double click a protocol
+ next();
+}
+
+void AddAccountWizard::back()
+{
+ if (currentPage() == dynamic_cast<QWidget *>(m_accountPage))
+ {
+ // Deletes the accountPage, KWizard does not like deleting pages
+ // using different pointers, it only seems to watch its own pointer
+ delete currentPage();
+
+ m_accountPage = 0;
+ m_proto = 0;
+
+ // removePage() already goes back to previous page, no back() needed
+ }
+ else
+ {
+ KWizard::back();
+ }
+}
+
+void AddAccountWizard::next()
+{
+ if ( currentPage() == m_selectService &&
+ ( m_selectService->protocolListView->selectedItem() ) )
+ {
+ QListViewItem *lvi = m_selectService->protocolListView->selectedItem();
+
+ m_proto = dynamic_cast<Kopete::Protocol *>(Kopete::PluginManager::self()->loadPlugin(m_protocolItems[lvi]->pluginName()));
+ if (!m_proto)
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Error,
+ i18n("Cannot load the %1 protocol plugin.").arg(m_protocolItems[lvi]->name()),
+ i18n("Error While Adding Account"));
+ return;
+ }
+
+ m_accountPage = m_proto->createEditAccountWidget(0, this);
+ if (!m_accountPage)
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Error,
+ i18n("This protocol does not currently support adding accounts."),
+ i18n("Error While Adding Account"));
+ return;
+ }
+
+ insertPage(dynamic_cast<QWidget *>(m_accountPage), i18n("Step Two: Account Information"), indexOf(m_finish));
+ KWizard::next();
+ }
+ else if (currentPage() == dynamic_cast<QWidget *>(m_accountPage))
+ {
+ // check the data of the page is valid
+ if (!m_accountPage->validateData())
+ {
+ return;
+ }
+
+ QColor col = Kopete::AccountManager::self()->guessColor(m_proto);
+
+ m_finish->mColorButton->setColor(col);
+ m_finish->mUseColor->setChecked(col.isValid());
+ KWizard::next();
+ }
+ else
+ {
+ kdDebug(14100) << k_funcinfo << "Next pressed on misc page" << endl;
+ KWizard::next();
+ }
+
+ // if it's the finish page, focus the finish button
+ if (currentPage() == m_finish)
+ {
+ finishButton()->setFocus();
+ }
+}
+
+void AddAccountWizard::accept()
+{
+ // registeredAccount shouldn't probably be called here. Anyway, if the account is already registered,
+ // it won't be registered twice
+ Kopete::AccountManager *manager = Kopete::AccountManager::self();
+ Kopete::Account *account = manager->registerAccount(m_accountPage->apply());
+
+ // if the account wasn't created correctly then leave
+ if (!account)
+ {
+ return;
+ }
+
+ // Make sure the protocol is correctly enabled. This is not really needed, but still good
+ const QString PROTO_NAME = m_proto->pluginId().remove("Protocol").lower();
+ Kopete::PluginManager::self()->setPluginEnabled(PROTO_NAME , true);
+
+ // setup the custom colour
+ if (m_finish->mUseColor->isChecked())
+ {
+ account->setColor(m_finish->mColorButton->color());
+ }
+
+ // connect if neccessary
+ if (m_finish->mConnectNow->isChecked())
+ {
+ account->connect();
+ }
+
+ KWizard::accept();
+}
+
+void AddAccountWizard::reject()
+{
+ // if we have a protocol plugin loaded and its not being used, unload it
+ if (m_proto && Kopete::AccountManager::self()->accounts(m_proto).isEmpty())
+ {
+ const QString PROTO_NAME = m_proto->pluginId().remove("Protocol").lower();
+ Kopete::PluginManager::self()->unloadPlugin(PROTO_NAME);
+ }
+
+ KWizard::reject();
+}
+
+#include "addaccountwizard.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/addaccountwizard/addaccountwizard.h b/kopete/kopete/addaccountwizard/addaccountwizard.h
new file mode 100644
index 00000000..f4d204ec
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/addaccountwizard.h
@@ -0,0 +1,70 @@
+/*
+ addaccountwizard.h - Kopete Add Account Wizard
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ADDACCOUNTWIZARD_H
+#define ADDACCOUNTWIZARD_H
+
+#include <qmap.h>
+
+#include <kwizard.h>
+
+class QListViewItem;
+
+class KPluginInfo;
+
+namespace Kopete
+{
+class Protocol;
+}
+
+class AddAccountWizardPage1;
+class AddAccountWizardPage2;
+class KopeteEditAccountWidget;
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+class AddAccountWizard : public KWizard
+{
+ Q_OBJECT
+
+public:
+ AddAccountWizard( QWidget *parent = 0, const char *name = 0 , bool modal = false, bool firstRun = false );
+
+private slots:
+ void slotProtocolListClicked( QListViewItem *item );
+ void slotProtocolListDoubleClicked( QListViewItem *lvi );
+
+protected slots:
+ virtual void back();
+ virtual void next();
+ virtual void accept();
+ virtual void reject();
+
+private:
+ QMap<QListViewItem *, KPluginInfo *> m_protocolItems;
+ KopeteEditAccountWidget *m_accountPage;
+ AddAccountWizardPage1 *m_selectService;
+ AddAccountWizardPage2 *m_finish;
+ Kopete::Protocol *m_proto;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/addaccountwizard/addaccountwizardpage1.ui b/kopete/kopete/addaccountwizard/addaccountwizardpage1.ui
new file mode 100644
index 00000000..0e4dae7d
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/addaccountwizardpage1.ui
@@ -0,0 +1,144 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddAccountWizardPage1</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddAccountWizardPage1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>652</width>
+ <height>464</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Step One: Select Messaging Service</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>PixmapLabel1</cstring>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>70</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="KListView" row="1" column="1">
+ <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>Description</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>protocolListView</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>400</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_header</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;h2&gt;Welcome to the Add Account Wizard&lt;/h2&gt;
+&lt;p&gt;Select the messaging service from the list below.&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop|AlignLeft</set>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="95299">789ce57d5973ea3ad7e6fdfb2b4e1dddbdd5e58f84904075f50564820009848c74f5856cc90364200364f8aaff7b2f692d19039e0067effd9e2e4a47fb3922c65e7af4ac257959feaf7fff75d7ebfef5effffad7db3b7f0f9cbf1c9fbffef56f317d7cfcfadfffe77ffdf7bffeded9dbfd6bb756faeba05cfe6bf7effff1afbfadc7bf9cbfd86e696fafc2357ed0b8b65722cc08537bffdde07dfc7b4eb84ad825ec10b6152eef84ed4e88f1f892b0f9be0831b67b882b25c2cf061f94351e11ae50fbd8606c77f5f997e1fc2afaf8fc21c4fafb421076103b55c415ba7eb663309ddf5388f1f77c83e97c3e099bf3090cc676764bb846ed1383e9ef5f426c6b7c4c5850fb7b8875bb4dc7dfdf411cbc2abcb7b357dadbd1d7ff4598ae5f5e1176c81e2cc4fa7c383718af5758888d3d9ca1c1d81edc13ae507bd960b2d75188d1de2383e97ae8fc8c3dd8bbc1d4fe1a62b4c72561638fb710633b9d1fd803db4b21d6edecc6e0aa6eb77dc215b2e7bdc1d82e0cae21f6f1787c6f6f4f9faf7742b88a583c198cf6f5f7083bd8ce760d26fb7f8658b7bb75c4606fb4ff6788f1f83d83d1be927edfd85fdc194cfd771062dd2e25e11af5cf34c4badd367f4ff6e7e6ef05f1b56230b63b7e88917fa788f777a83f1f0dc6efcbd710637f74081b3edc8758b73b2d83d1fed63e61c3976b83f1efc5b3c1d47f5f21d6c79374bdfb02b14fd773b08338e006d7f63476095710fb9a6f95dd72a954d2e7778038e403b59bfef566884d7fbaa721c6f1726d30f687f74db842df9f8518bfff6630f5e70b61eacf910831f6d70761eabfd1a3c164af3162d35fd689c1d41f03c246bfba21c6fe730893fd837188d1befb06637fb007c2f4fb2337c4687fcf60fc7ef014626ca7bf37fde5ef1a4cfd43fd61fa2b102176341e11ae51bb1f626c7f272c10fb5aff2adcf4b7b747780fb1ff45b84af831c4556d5fb467a8170e1d2fd45bfabee187d33498f830214cfdef39846ba4176d83f1fbee116181ed72cf606ce7e310633bf24d8d57d4f3bb10ebf6e0c660ea2ffafd7da3e7cc60eaff9310a31eef10267eb0af10637f3e1a8cfdcd2f09131ffca1c1d44ed77760fc5dc5606c1ff11063bbf97e05f188aef780f420f83298f8108418f9b04f5850fb7388b1fdcd608eed0dc4d51d6a9f194ced17842b887d7d7efbbb217fdc106bfef867841dc20c31d04f63e7de60f4f7de98f01e62e18518fbeb9630f92bd6430c7c42fe9409135fc42b62c30ff725c4c83ffabd7de36ff6438c7a32234cfdef5c8458dbdff3091b7fde0931eacb14b1e96ffe6630e9c34788916f5784e9f759c960f2dfe510231fde0dc6feb5ee08937e066d83a9ffc95e211f5a21c6fea6f30ffbffd060eaff2661eaffe03cc4fafb239b708dda6f438ced166141f8cd605b7f7ff489b8b683d8a969cccb4eb9a631fe1e377cf19f0813df3cfafe9eb383fcfa3298fc19da07f46857f3959f1a8c7c728788437da813267bba5706537c4ee7b36ff4283098f8d0451c8ef74fc2c6ff3c194c7c9810367ae28718bf7f48988eefdf1b4c7ad0475c257f327a0831f667c960ea1fba9e2af997119d8fe9bf911762fcfb03c2d47f41d960ecaf80ec67fa6f340db1d0ed7b842bf4fddd10ebf6d18c700db1a37fffa06cf4c07f475c29eda0de5c8418fbff9e7005dbbdb2c1d4dfb6c1783c59456cfa4b4c111f90bfb00d367c681b4cf1e06e88b5fddd5bc2a40fce8dc1143f1c11267fc0bc1063ffd51157491ff87188b1dd3198c6ef35611affa391c1d47f92b0f10774bda1bdc784c9dea30f839d8ac6743e3541ed5f0663bb83d76f97ab656d4fe788308d571bed6187fa5e415c11d85fac6930b6db6788f74dbce011a6f1e53e2036f616a784c9de828e7740facb3f0c267b8b10a3ded2df1b7b3b7b21c6f1563598ec4de75725fd0d060693fe5e1336f391a1c114df55438ce38d1126ff2b74ff56cbe53de4ab731562d4bf57c2646ffe85b852417bfb1ee2fd1db2ef04f1c10ef947df60b2678930e9993498ec252e111bfb04738cf3911bc2a45f6c1662b4cfb1c178fdd2266cf4ecce606ce7747d55ea6f7b6430b6b3d3106bfbf10ee21a5d8f13184cf6f50d26fbe2f9d865d20be7ce60b2f73e6163ffcf10239f6b84ab88bd27c49512f98f6fc4fb3bc85fff8ab0c0fef2e9f70f76f0efc5036133ff7b435c257fe13d1336f3f9ab1023dffb84c99ed6aec1682f41e75325be3b3d83c99e74fc1af1db7b08b1b61fb3099bf590db10637cd13598e61b3b06937f217bd5c89f08dd3fb5bdd0fe8706a3bded2ae13d6a7f3198fac30d31f6c70de12ab6bb32c4badd3d21ec507fbd22de2b617f786784f710bb1f8449bf3cd760d2af4fc4a6bf5999708d8ed73798f8503198e28f63c4861fee2e62d3ffde25e22af90bef2bc4d8df25c2d47fa28bd8f41f3b0fb1ee0fd1206cf4e93bc4e80fa60663ff5864cf1acdc7b817629cef550dc6bfb7fa06e3dfb337c2147f073b06131f6488912f35c47c07b1a3f5b4e698feb6f70957910ff681c1d82ead10a33e4e089bf84f1a4cdf7f0bb1febe6c198cfd278e10ef113fa56730f183ce2fe4cb28c478bc2fc255e24f8fb0f93d3aff4a09dbdd5783915ffc89708df88be3c5d9df217e71c2a48f1ef6bf63f4dca7f6ea0e5eaf4fbf5fa578c7ad194cf3893661817cf5070693fedc1a8cfc73a93f42bdb00913bfc44b88b17f9b848d1e55438cfa6d194cfc1913a6ebf35f0da6bf1f8418f9776e30febdc3428cfa45f6e634ffe3f706e3f7ed5988917f6784cd7ce6d5609a8f687de07b864f7605b1e18b7d6d30b50f081b3d7b3698f48a1b4cf15383b0e1fb95c1f87db14798f4490e0da6fe75101b3ec90961d3ff3dc2a44fbc4958903fda451cea139d8f89c7fc6fc4b51d8a37ce0d463eb99f84cd7a638b30f1c7ab1a4cfd593698fac343cc8dffec1a4cfa766c30e9c97788757f499730e919932146be1c10e6c807bb6430cd676c8369fe42f6e392fa5ff3853ba6ffec3e61d35f970693bf12848d1e3d194c7f8ffde984f39716619aafc863c2a6bf1cc282da0706a37f61743efb145f48c760ba7f42e7bf4feb6fb24998e26f7e1e626cbf3718edc5d0dece418dfa7f8ab866f46948b842f1ce09613a3f798a98937f72ef0dc6fef276091b7d9221c6f5b40f83893f1d83c99f3408738a972606633b7f3718fb9399df97a4171706633b1f21b67769beca428cf3533d3fb2f7c2febc406cfad3ee194ceddf842bd45e3798da1b846bd82e1e119bfe7477080b1aff6d83e97eca3ee203b3de7519621ccf7b88abe4dffd67c49cfacfef10e6148fdc184ce3fbd160b2974f58d2787d3098faefd0608aff8606937d6b888d7d6537c4dabebc4f789faeaf61307edf3a0cb1febe45d76773c4b6d6175b84f63d274cf6751c83a9fd8930cd4fedaec1d8cef0f8c2ccaff8b7c1f4f75f84a97fbd23c2e6f88784496ff98dc1687fd6426ce607f2c8608a37a684c93fcb3162335f9075c2343f967d83e97ee13b62be43e3fbd060d2eb6bc2d4fffe29627b97d623ba0663ff8b196163ff2661b23f1f1a8ceb05e28cb0a4f61383b13db0103bbbb4bea07f1fe4d0d8ff12b1b1bffd6830e9e13361b23f170663bb27099bfeef1a4c7a6d8718fbc71c8f7edfb6438cfe32c4a47f1f880f0c1f3a0663bbbc234cfa289f428cf39923c2f4fbf2d260f2df17884d3c26de119bf9af7b6330cdcf1961d24f796d30cdc7cddf537ce6d2f9193e78fb8425e9fd0b627b9fe2c303c29cd6333961d203f66930f6276b207676e9fed1bec1a4479f06139fda06d37a5410626c2f13dea7e30f43acdb5decbf707cc90ae2038a5f789d30f1c3b93418db99f9bee1c7a3c1e42f2f08131f1cf37db39ef510628cd7a7884d7cce5b21467def188cdfb7e9fcab153afe89c1e84fed63c234ffb4770da6fbf3738cc733bf47fec3ed1b4ceb6d4dc4c69fbb2d83295e32ed86bf658369bd86ceaf66e6c31706137fcf101bff2f0d36fca4ef1b7fecfb88ed5de21ff597cd697e30232c49affa0653bc80e31ff484be7f6b30e9cf8030f1c51911e688472f060be4974b58623bab198cedb68558ec22167a3c8a8ae92f5e266cc6f7a7c1942f7118621ccf55c2c42fdb3198e6ab2e62d33fce2d61b35e5d236cd6233f0da6f5ed53c2c407716130dddfb80e31e69770c4263e730c36f1c15388b13f7dc2d49f9c8e67ef92deb821c67850188cdf77df0893bf1715c2e43f5c0bb1e94fff9230c577eea1c1d85ffe1d618a47f8d460ec2f3e436cfacfaa13de47ecb4432c353f9e0973c41ced2f0dffb9446ce22b7b4298f445f60c26fbbd13267bda8230e9af1c194cfa4dbf67ec297608efa33d9d6fc21cafd73d3618ed257cc2640fb78bd8e82d7b21bc4ff7ff1f0ca6fef10cc6ef7bf4fb0e8d4fd6262c299f686230e9fd38c4c857e48734f6652784c9becebbc1725ff7cf056149ed2583b19de9f10d07377a3f214cf6f41862a32fde21e17dd2f706614efef9c560d2f7b6c1b4ded63298e69f6784cdef750cc6bf77760cc6efcb1a6287e2617b4c98f8eff40913bf050f31f2db264cc7171f06d37809dbb1bf64d88ee343760c46be7b25c46297d6131e0dc6bf779b06933e8e08ef93deb5438cfee5da60ca0fb93298f4e2dc603a1f61308dc72a614efeefc3606c970f21d67c90645fc30f1e188cfcf0881f7297da4b0613bf7a84f711072f21c6efebf98094862fde3b61b2bf87f680f144b84e98becff609139fdc03c482f4cfde3118edcde718fdd53161b2971f10e6b49ee7184cf9575383d15e3e9d9fa0f129a4c1680f7762305ebf7f83589af8bc6330d99b1b8cdfb71961b21ffb08b1febef74298ce875f194cf66f85587fdf6a1096647fade7eebeb11fab8718fd9d242c69fda48218f88ced6f8449dfd81561f3f7aec1c4b757c2743c16184ceb2fd42ee9f8bc6730b55b84e9f7bc73c2a4175e3bc4baffbd538371bc7b8d10ebfef6ccf1687ee50521467d0f8f4ff6168469bc7b8f06937f1b1b4cf63e33d8d5fdc1d11eaeb10f7f216cec8bdf77c3eb3f3498ae9f11a6ebb76a06637bffdd6271c5e2f05f6e6a28764c6dc337928a43b58022a98e2f3614178a5c2a22520bcbcb55e61f3fa63625c82c2328636daf07b8cec7242bcdad654ad46a11eb71b21627ab2cd7a6c8258bc55bce8d2972a1466bc8dcd68afb284b8dc822e3382badf267e9ea633993c21b27526773862f5df53267e4dabc79da8a3386379a3baad6fcc9c99b04cee0ff4be78ce14d3a67e2782397ea2867d2b893fc79a63aca9958eeccf9a35953acdea473a658bd89e3ccd346bc19476a558264fe6cac3771bc594f6f56392372712699378633997ab330c246649f4ccefc32bd59df4f6de7a3bc25cea83a30dc09fdd7cfe84d3667d6d59bcdfc94b15c3a6762b813f2278e333c436bb6e3cca67a93eca30ad29b857a62f4e71feda3d6d29b65aba1fe6ce0a39cb57cd4b67a931d136fc69915bd519c8916c39f141fc56338537c4c2cac6d63e2f57c541c6f5647dc24d49f5f1d136fab37ebf9a974ce048b9c59e6cf1fa537c5f9a88df486ea891969c89f1c7af30f8a89d3f4264d7ffed131714ebd897267ae3f5be94d968f5a7f0efec331719adee8f212d693507ffe61eb366b702658e64cb40ef9f3bbd76d7e0d6732f54671467f5eb0d0fcf43fd547e5e74d8a8f4aac0d7ff2fba83f2dae491a316bf928d29bc9327722fcc9e3a336e14c3e1f953d8f5a7bdd268133b13e6a65e4cd3f2fda3edbfba8fcbc89e3ccfa7a933d8fcae7a3623993c19f227c543ebdc9f651f9f4664b1f15c39bd072da3eff241f954f6f523963bd52fd16f2e71fb76eb3e0a3d6d09be53ac29f7ff43c2aafde28ce58eff33ac29fcde7517fe0bacda67ab364ad77e2cf7faede64f126afde443813ad893f7f928f8ae74e1667b262e26cce442df86e0af1a7889838596f36bb07bead8fda486f748de525963fff1fc4c4697ab35c883f7f4cbecd567a1359b7595b6f229c89d4ef217ffef3d66d8af251b1bc31968bf0e78fc8b749e0ccf67a93e2a39639132dda3e7f50becdc63e6a6acdac0feb730dbd49e44cb426fe6cefa3a2dcd92c26cebc9799c0992fb0ccb755b71ad6a175b4898fa2fa78993b217ffe43ef2dc0e7c43ab59a56cb3a03eb9c596dab43ff3f8e33510ba57246d7c750bf93fefce7c5c4ca325debdcba20cb60e9597d1861ab7af3665d5a03ebcabab66eac5bebcebab78656295e6f12f5e74f5c278e5bb799c0b5ee58bb5679c132a6ec59951885deb70eacaa5563161466d5ac2ae3cc660e58e96d853bef9191769cc99f641ff5ebe7e0ea0a064c3009e369d532ba3097a95f5abce24be6b3802c538bd42336660fc0a564fe94427dfebdeb36d97a73c91ed9137b06156ec55b864a0bacf7b5a8ce6cc25ee6dc59a85fc1466f60a3e3b9de2cab74027ffe989898bd5b15366533abc93e522d43857db2afd067a9f2c1be597d853b919a35d8a11e690bbc81f216f2e78f8c89991a195fec881d6bdf9d69191a6127ec14349afc127b674dd6626731dc89d66deb9a75623d7c69893fd9317151f936297a0357f5ceba309ecec163276a4da2852ed82372078ed2637d76c906ec2a9e3b218794165d136f42eeb09b707ef1c7c4c4ec169466caeed83df8ee352d43236cc84a70a44fb6c3765999ed817d2a6c9fbda673087cda81e6507484cdf9f367f8a80a8ca7aa75ce6a192a9c5e7adce28c73283677b860975c3d99ee267167ce21ee59f78a33863b0bfeeb77c6c49f30121eb90ffea9c9832d2c43858ff8983ff047fe04dc51fcd9e3cf7cc25f12f527d421fe0a3629adf0e737c6c4ca3ff13776c7dff9747bcb60e133fec13ff917ff56dca152e70d7e98c921ce8f903b544a217fb27d54be98789d75e22ff6c58fad737e02bebb10cb5069b1637eca9bc89d90432db0d0593a87789b7762f8b3998fca5eb749d41b5061f04f569f8fd849a1963123accbcfc12222c21f552e6094a5ea10eff1bed6a09229a17db6d59bbcebc49f309e2ed929937cb0950aa759e78a5ff39b2877223a74cbef527d59438db018fdf90531b18e6aaafc9e0f79e9672c43fab3c37757b813165e563be3257188efb3871cfc293abfef931ff0375ee535dbfa49cba862339bdbf62a77e6853fdb8eda9d8a9f2d73c8766dcff69542db81f264ec46dbe7677d14a8b03de2ef9bc4c29b147b6c3f2471078bfd683f71613fdb13fbc596f6abe18efd66bfdbd3397796f85374be0dc4b0d609f7ed9975082afc435ab3629d0ffbd3fe4ae68e2af6137cbeedbaddb00fed23fbd83eb14feda6dd62dff699f2ea863b7690c99f4d7d945661f04f3556cb37ef2e4a99edb6dd49e30e943dbb6b9f83752eec9eddb72fed01942bfbdabeb16fedbb2877ecfb903f9baddbc4c53523ebcb1ad843bb04e3e9e2d75986b467c7de4de70e58e7097853b6f7c03eca3a15b0ccbe7d6057ed9a632167a2232c813f1bcda3742cec3b8c9ff0d9afd19a25eb30873b7606771eed73c781d1d57384e6ceb5231d69571dd7f11c1f3983056c35e7cff67a032aec04d6a13302dffd8bb466b13863e7c1794ce78ed69e73b0ce85239c277be03c3b136d9d17e7d5795be68eb2d2127f36f1514a858fd9051b3aefbfc732589ca9fd09d14d86df02ed51ca7c01d6e93b33bbe27ca8d1e57c3a5fabdc5175447fd69f477d5a53ab032aacc653eff7594615185bdf10f5e4f15bdff69e53771ace73689d43e788bd2f2b3316e2cfda7a035a73c2eeac433e2d6245a280d175ec9c64f8ad4be7546b8ff15ba0ccce04acf302cadc8c7246d54e4bd509fcc99a474dd99373c66ace2f8af87258a76373a79ba93d0dd09e8b88324f94df02ed39c77827ca9b25fee4d59b23e7c2e9f181d32e784562cbe2f49dcbec9859fbad7a44993f803d03e7cab98e7287dd18ee382db24f1ebdf1f8817363edf1198ca7dfa8c271c5fe706e73faadfa82dfda07ebdc39f78ebadb1cc31d65a5087f92f26d3ead236b669df399f39b3c77867598b3e374d3b547fb2d1d333bf5302a3c50dae3ec72e694adcb2867d447d5217f92f5e6d3fa70f6ac323bf9d915896d0a287365d398d9d9770e9ca1530506c47027a23fab3e0a223eab0b51cd1f6c1955d46a46e67c6b296686d125292aac394361818586ca16cbdc59e10f7187ed08e6f4584d47c2bfdd02a9dc19434098bad69316330b877f0ae10c61840dadaf450f6ffb682b6d9fb9dea8f174c387ce1f6f192c420a37db6f25c4cc87c243ee280e5947a83dcb25e48f076370c73ad719127fa00ac7159b093f07774cccfc043173681d116865d6a38b46d89be2cef247d9478cc498cdacf27f0667a8b4acb278108f5931b3f25b919879606266185d4f863bba2e5b5fcbdc11cf649f0928cdefbfe27c45b1fb42bc8857f126deb3fc564acc5c9b73070bfab0b94263adc7d7efbfea9c9c817e6c8a297c66a03d1f196b3da931b3f88c70c77cfc396fc4d79c3f7fc095e7b00c9f8a6f51170de4bad31187ebc6cce2086366712c4e16b983c53a5ae48eb2d01fcf1fb00c3b1113712a9a913b8a2dd14ae74e749d7929663e748ee6aa1cfd5853ab14e58eaaff60fe34b465ce80356dd1598c529da9ddd93c6656ca1cc71fb0d02572475be7f90fe64f8f9d305774445374996b2d65d7897371b145cc7c2e7a4bcc299b621de951f5352f7f1c7f5a3ce03350e1beb8e4b578bfeaf4950532b4e7dcc4cc641db3ce7c0531730c77421ff636e7ce9fc61ff60196698b81b882729d14a5425478933b66de8bc6cce0b75ec4ad8977568bb883115631dc11aa3cff21fc6959753e10435102b5394b5dcb6e899d3563e667a33d30a3d8e59f49dc218dd63e6caed07f007f2e9c118ca7328ca8093bc9fabe52e6cc98f9313e66167bea1d24ab9c59a80fac4bc31d55ff56fef420a63903dffd2daa3967372d51cba13db131b3b424935cdae26e993391ba4c23cc78f9dfc59f16f8a74f311355d14d52e1b8628f378f99c17d0995fd2b652277ee8c0f43eefc1efd016bb4c54cbaa221caa0c76bfcad90cec9a631b3f4a46f33d5333288e14e19b9837538c27e397f603ccdd83d78a72afb5c3bafa32547e9f7475362e61739960f64e5b2e2493c77a23e4c73e897f10794b7c9eed931ccbc73a8705c918ff2493e6f1633cb0928f3391deb42be44f566ce9db094e5eb2fe44fcf1981e79e88013b8619d4a6a3f2cab975ba9be566c837f92e79e4581fc9dcd1057dd8b39c927d7eca322d71cd6bc09763b0cc1a2a1c5784bfce3af362cc2c67f26321da6c8acf44eee8624d6dff67f9c34bca73c378ea8889ce06daea78ce54e6cecd50ca3c5f490565fe22659e1faf25bf13b983458db02fe0cf4fe84f0bc693b68c74ad762177875afaeefa9a3133adc2bfc83af8f5e5637eabbb82f1b30c9aa96a1f26a7c5f2879d404c53569c010e5f17c343d99087f268d398591e8bcaea71f94c9eac72262c07609fa9755fa8fef4ac3270a62c5d5e734685642fb43828baa8ca53d9e4dfb9e6eb4bd601656e2d2af3bcc82069961af161cf85f0075418bc535569308ca742b217808743754431649ff24cad85e58a997b2b31735b76e27f4376395be10d71076b1861cfdbf247af9757e133b49ac5e490e9e85a1d114628734149cfedddbcf9cc601db110337fc98b25658efc8e8e82523448adb66ec19f169f42ff76d575e89ccced4728f010661d5d760cb61e5034d0923ddbde2c66b66f64dfc4cc71455c6affbec099c55af9b00df803ac81a8a60fcad08078af5ec45d45a5c260eb861aa1e27a3ef3b0c7f2326bbe9e18330f44c51927ffa633925749dc51d651770c37593f84c8660231cd80cf8a51618806aae21b78d8e6b3451e4a0e3173f67c3d3e666ec96b185b29bfcd3ec47e3277d087c9d74cfeb4d4cc493f5b125e91552f2887ec424ce48da82aa5d1cfc92db5cb5b79b771cc7c2fb37aaf659dab3bf08b9c41eb989a3da6f1077a53c5320d5112ddd5b3dfaa80ef8611da8719ab9b3442e5b938dc2c66b60fe450fad93dc84ee4c90a77168a95a43f2de682875596b9541f7903d751906580355559125535429373edf995ad9f15d824660665aecb9d1ce7d293bb7afe558ee38eae0f56f9a3c69318805a5e01f72fc3cfa408eb881958e652c5025923d41e3b95ac9839299f593a7131736c2f9ca879463c77b044f9037e4945ac5d79b3601962d076f36f3e030d86880054fd243b4e723ab29cf31ed76accec4996d7a7f240ee257247d7c41fd05c184f2fa00957cb960947d8e786d6e981bf9b8a2b155be78d9340992b1bc7ccfb6a9d39f7b9a9383a813bd694eca362e044cb849feafa3e0b54f8126cd3d1599eb9e324f9982b37233e663e9017eb9c27ab89cf78eee0a853f6c9b40c7eae60469edf3201a830589d4fd7cdf2e457b29795cf9c12338f613eba4e0f063288e38d29da3e79aca346d87dae5f6d011f4fc51462e18b4d7219654d5e6e9a9b211d77cde7a3d907afb92c9e3baace6f1ff894b2ee39800a77455dbcacb3a3d162713ace6dceb59e9598d9e572833527a72df75679230e84253ed9ee3af691375639f657302770084a83bb116e64196d9db1bcdd629df91ee6a36bff26441aa7216f4ca4d893576c575ef1cff5ec2356ef3fb4f4fd893ed8a60cbfb4b165b0b8362873465e5852ccec3a6e7ebfb5d0bb70fe56c89b3bc51b79a5b8a3eab5c6d725d821ca60608d74455b5c16b397867a8a4bd9609398d995721cb3d6a3f216b39faaa95bb3b965f8a7e18eaad7b30f302872471866f243765fccda0f28db85ebf29bcd6366d75b3a660f7ce8199c7533eb2e365cc137444196e14cb45e933f9730532d368b5c6b179f4ad7f5dd20cb6f25c5ccee48ceb9a3f619bd80313370c7ee835428f5f77909e69a65b63be70cd658d6b64fdf1915671de8e16b8806be45d37d749fdce7cd7233a425efdd89b95a98dbd54413bc6835736fa1160fd4bd28b5fee4be28be2c726703fd519f4e015aa3c6e6b5551665b892173e73c6e2c3de5d3766c66700417b5edd37e527606ef709b3c72acc80db199188e698d5d6592455280d71aa78b3c89d8df80351d0967eaac54ea0d7666afdc744034ec77d5f2f3743f92d8c99e59b3b05ce0cc44496c08b4e3277b500ad13d760c90158e6252c7d775688fe5ceaabdad03abc04a353dd9fb864c791fd0d5b10333f65694f52ccec7eb8c01ab04c29c7c8575a37930db00df2665ebe45ddfd5ae4ce86fcb914e50deee4b4b08f813525880616da9ce9e631b36cb9dfb2241b39d6c2416d980bda545de0cdbc5cbaf542f4e7527cf3e93a5a03f14519faec05a283989db56c2676d2f77b4a8e99417b1aee61f639b00f76a27ba7bac29b79e9bb4785e8cfa55a37ce671d359e2006e908b59e3a8bff0ec4ccf9f3992f96d799dde3ec73003fd6d6b689e78d295d8806567cd846f6b9841968960af67416bc5ac3eeeabd8913bee774642fa7df5a8d994f643fd5976a6f2fced45dfc14decccba57b3ae70e5a6843fbf4ad768a0a072a0333cca54bbd027b2c9f368e99076e8aa7e081ce763ccee4cdbc94c43e2f427fd45ad0716c1cade6aa2a37752006a0c2d9ebcc53b7953f665eca67f6dcb324ee2ab501251ee4e4cdbc348d75b6e4cfa5682ce98952e1a6ce8b1ac01c2dd7ce5ae25c2bf3a631f37eecfd5195d3a79e5428e7e4cc426133b75d88fe008364233a7280371d38a789cab9c8ebd7e4a3dbc9e9b7966266959be176978f07beb1aef3f1ab31dce80ab512decde04fd73d573eac00fe5c8941f48e0f9fb2cff5ee00296596cf39d67a626266fbc65d5c858798583dab00233b8e177dd19655f75c2a2b657108a220e40f968deda366f2f1efa0c8574099377f06d0ed45720a1b6ab62e1be15c6a810fe214ac5277cfd51d266d9d2c0deacf67aa5bf147ad050d379fa90ae9f633e75bc9f9cc46df202686592e7aa9451ea868742aea609d29cccb4ab935e845b4c527ff44fe6c181faad1d565f730dfd9346ba1256f73ecf794f00ca07ba9e329506298a7df83ea2d72e005e652a7a2ae59730a566aace9c32ed54c7563fe5cc168eee83be85bac1bca4727c7fdd1f933803a66bec67c6677a0b2bdc535c47df74b737054616d19f74129c0263ecce9b957ca32c8a135ed53d5d9a927dbad21daccbdde34661681ab72499a5a8997e64f62ea8ec1360f42adb8adc79905feb9b74bfce9e7b04c49e58c59f5ed73c678c9bdcbcc294c8c99dd57f71e2289c5981854d81d826da630aefa397c5456b9927b3caa3fd50c1dd6195e05e48cb5f840deb825772747ccdc885d6796eeee625fc3f1cedd32a8f0299ceb375cdbe6bc89c64ad3287f20124fb24e570cf578da3a739707a0a36df09e8feeeabef0396366d09e8abb1f9d6fbb47ee50dd5f02d6bc6cc999c5d2746773fd81d964699535e033cfacba8ef9b61b512d09f31ab88a32a8fa1446d7815b4d7f8e2b69af39f7c4ad191f235edd32447c256077564cbc49f916afea3979bcffce4ec01651ebbcf01a9fe9ec826db566a62276b0fe0c62018a593ccb7ef318bfc8173347f7cdf0b867abe3798e3b26cb14c9996851198077d6d4c2fcf016ccb93163ac2f5d3e60ea7d145b6a0d2f81f705bd942e44d90b19989e608c1fb27dae12bdd688993dd7f3dc23cf81a32aa5c9130b6f54d8b1546f18a979bebae3aced73a673904185f5782a4085617c5645496534affa3b8fdee5c6aebc112fe78d991dd71b83d65c16e29f92e7efc710716a76f092f7a018a4ed83ebb3257da771ab11c54e2062ab8b36af25fb3bef317c67c0abfdc627bc9e2766f69ebce782fcd38b38f5262a965bf8ffea898fb2b601f532bbb03eace29effd259511083549d110fd2eece792f0bef9b5023ed16f3c562f2992966f65edd1a68cff61cb97287de1bf4e0e582bf2b8ba17eba66416f9d9173b398bfba616968153e52b93f9a85191c04fe2cbfb3e4ca7bf7a6f2393966f6f8d67af32da6de0cd4eb546bd79c8755fd245f5c9e5b8b57a5dc8e3ffa4995ba7805ff779d3712f062de25c9cfec377be27dc43f03e87d7a5f5bf1e6d273bc199c675f5b26fcff4a6dd2f416ae8d6dcc9f16073fe27deb5daf7aebe4197a56c2bb930ebdba7d0cbe2b2e66de5477be45dd7bf31cbdc2b1700c760c114796de5eb0bb4df8036a7e0c31c891bedbb7b6a27b8de4f76fd9afdea177b418337bc79198397fd12a0c5a33d5aabed836d4194139ce951dafc51f95c5d486717c22fa10d36c382abdd3d477b8a9e8b1e9b54c3eb37716c6ccb9fd13ccc986708eaf5a85a36d2f62201bd61af9d1ec33277f5a2a02102f6a146f9e9d40fc69a7bfc34d7dbc8e3c525133684f17d8939f33dfee83f70ddebbbd145b57d588d22be46baa48267f74e69b00bb80d6548bd867d33bcf7c0f20fab40bafe7f5bde53825bee09346afde0c228cfe4a7b5574acb656c9b5cf37953f3af34da8fe38d5bbc86d6919656d6fe05db1abf93b93e26bdef3aebd1befd6bbcdc199ae5a1903ad512abc1c5b574599ddf3e9e6ef004ae00f58c6aa4b57f96e882b8b78b785da1bb4ce6b30bbbcf3eebda15762af89dc09bc1d6f379333ca326aa51958a39e6488f94e59cda4b69b65aff24765be89337105962925e55cac57a0f7d4ee6bdf6e5965027b656f0f3e156f9f5dad72871ff2b677e065e65a0898bfcb2a8cf9573d53586e1fa835f222deaab5c01ff5ac695994d5ea76ae8ca3ecd212d70c021875a7450c350f7b5ecdb7bc3ddff299cf7d5b8fb4703ee63bbef065066fd4cac629447c93d8fba1557517b7b8774745f9c33ee0e8d31c799ff98ad22e95655817dd793460339f29eb680bb9bee7fb7e80dcf147fed87f809e4fe64d573f6954f7263093efc7cfbf798d0ff27b1195ff92be5bd3227f22f3d72d0a66ddc0954e656939f7c71bf88f681d2a4ffe337be53d7fe2bff8af29fe49dd4398ba653d838afb4e55edfcc1a7b9cfbf057da6b2a69a2261078a55fe1450d4086db37b350220de88e1a1dcf1dfe6d681ffbefb53efda9ff91f89bcf906d69c026b1c1d0bc7acf5a91d63f4fe16b99faa54f75cd58c552b6cea77ffde7efe3ee79eca326ca87b2c89b93f0dffd3b742ee7c7915ffdbaffb495aa3c6d3a9acba476a3d35610e3660f7e22cb712f7f45e9403600d3e6994f93785f007e6aa308e8ef51818a62ba37f48da730423ebd83ff14ffd660c271a3aa7fe0174e67525160e79a3f677819ec8a7c40dabce3e613c9588d939af6d4bfee03e8f1d7d5fb79a23f7a7e1b7b475ce80396dbfe3c7dd7b5091f0a98ebceac93917d2950dbd2f4a9ef3bcd0f7e9d59a7b69ddb78c6ece1f959baab303fa6245859347a17f0ef6b9f07b7edfbf649fc0f126a86a0d8e33d4b967df2a075e65a4c4c4c273bd9928b5c91513f7f8148eae987da59f345afb3a37e28fcaf5aee9dd6dfa6a656c8d28ace10ffc2bffdabf991f4b2b97ea617d15ea6eaddec5e5cc6aca06c4bf35b5031e7c3aa2a3f6c163f760d53c7bc600b32927a84fccde8805ebf247ed4ba733534ba2c107ebcf6bfc5bff6e71d719b52392ce3650eb34670b4f23b500f5208e0a20a2bf863e99eabb68d9fb6a7c807f9a687def02d3b67a3e6d0dfeb4748ea1cac9ea027b36da0373b1e89c38ba0ea8d7e161e23942e435247d9f14f196d17cfc6127c0723d57068617b25389de1109679965bd9fdbd6c7d44f0b0e74945db60a7bbf5d267f7aa0050dcd1a75ff30fd59bc9c7d0cec1f90cfce99099c517accd56ad855da55ecbb9ed3f8c367e657751e7c01bfa6df64f1ad2d3329666d8007e0fbf01c3b108515fe66a524fe0057d5ea4a55ef295bc4fa4f4fbfc9a2a4229782d8df82c8eb05cf117cda0fbdeb39893f2aea83f154c8af72b54f785f74554c57d0da401d987da5720974a6c9f6c76b6966c7ec7194c49f62de1da7b338ae74943401df5c04fb7b3ad7a40f0a36b4ca45f0108ed1d46b4a03a1fe4f4efe6c5d940aeb5987de99ae9077ecf2a93e62033cf8d9f67b356816ccf45c55451767f1fbc526f167aba2d8df84b982bbceaa43aa6502b552037ddca115e5edcfb10711cba97a0e56ccc0fb25f2b060fef4d8a7ceb41aa88cb1625418e28b3e5c89ce2c2ee65d9a6abf10f7017838d3f7df53bf5b187f54e48a6b3f35f5bed442fab80e766eeba739db3abadefa3c211a3856f99c6acd285fbc5e047fd43e1c6ac549dd85835f2d648556ccf411cbecb3a87d3af55e1c53a5c26bacc26ebdfea3b97a0ebebba1df74befd2e926ae745b5f2d95723b4987d3a81d92f6a2d8972ffd73ac72df873c18e5586adbe3fb9e5130758f4ceae53508609e58816c043506138a28a72371ba11bf147cdc94edda168d0130705ccbb211ab874c730fffdcc9f69955a80d9e029eaa05e9f69fb74e6e18f93f22cf2728138b32b5ed5eab6be8e62f60a2f83ad4fa1a78bb18cda934edd199faa3c9f6dcf51d987e7dbb1a6a572e9dc23b5076631f7c9e018ea6d9dafe0bba74564a35b180d9cc211bb96daedab8073d4fc61d93b50892bef4ddd3de7997bd8e5e6e1b7bad707ec0f8ad971098ea8ee043dc846319699f3c7bf4f7bdadf6a7b8e5bd6efd8dd3a3f1afb18a28f57cf5177590ad12ee4a1ba7f58b5eacb59ba45f0c71fb2b4797a3d3d9f799dc267b2e497203e730b7a0bb67a8ef0ca9b8976e68e48f94b5ddf97bc9ef3c769f93b3fb57e8257a177f93af6dedc3244d7055946ed7beb1e79dfcbef46ddf8787a5f7d770c3c1ccc67bf9a3fbb7e990f7fc83a2d1d0d4c41bdbe8b9973ab8c3ff0dd257f0f22be42d620e1d3e35379a3f2e4f433c791366d9f8abfcf64e16f37c7dd53badec43d5fd8bf7e2bcea868c0fb76c7b26115a185fa1dbd100dbc92c2ae7c47dbe7404efd6a613ba837f4eada8518ca2afc6ab990b51f9d250ab1e314fab85bd0dbd87b6a9f195982119a922747fcd9f587d66141bee402c6d3b77b242ef58ae1d657a2328b614ea69ef06817c343f6a1ded1aba20159cde221f207ecb3efd7b6bc7ba3f7f7121db56381e6eaf67dac76bd55eff669c3cca32015d6637ee63e00b373652b137f0efc0af78335f74e5efc55aba9e73bdf85acefe9bd58d4ce912a9fb9181eea1dadca383bcabf5f6cc89f037f3fc88ea3578aca4dd5ef4c6eeb8ca342661d6a7723b5efadfbc0b7e534f150efb808dec93d8f57e174fe04dc3f08b89c3ac13a3ba8ab5fe5039d4b3715e5cd9fc758e061e08c60967705472c669db901aaeeca8628a9a784d7d89b68993ff829f39c5907309ea6faedb76dbd1359212aac72d0847ece9f7d16c343da9bed34e7fe88a9fc5123ccba0fec2cf6e97790374547e7784d0a89f85a94f9d657996f85bc958fb244715d7fbbbc9005feec060eaba5f5b1d506f677556ed75a3b4426f330d07b85bda87b5039df509d51d43b4e34b3718fbdad79f837ce2f0e34870e200adab392e201f53e1f75efa690f7c499cc37f5ae16765f0c0f693f3f9525bae13b7a178bf23d863f8150ff15cf814c99a9b60a51e1867ec3cf44cf92abebbda13af14a304b54ed19552d261a50b1b5cae954f609dc70841db0d2f20cadc8a29f3f9ce86c857e51d180f6dcea6d59a0c24544a56af60b637e204aea5e9eb68f40fee8e205b9dfeeb35ed1996f65fdb45621ecc7b717eb0ca5925af32d86873a8ffb4565546034f077549f0fd44c959d16338a22e542e7acab5c9d9762545867139575565ba30815c62c51bdcb9b3ac7e69c87217f7c2a813f64c5ad05b59c91fed5aede13a69079b79ae3d1d371c3627868d5294bf44567892eb4cff9830aad46983d2be24ac4b5d237f5abd22d26e7426789ea5c48fdf4faf63c6ce92cd117fd1459c27eb19a3f41c81d5d336fbb7539f601ecc7bd2acbc5bca11aa2d201f5703977ae7e4601152eeb2342bc94cc43e48f1e612333ca8231df34ffa2a53349750626cc2d0b7937aa7e0e563d6d3028e24de9c8431dad63de69c69c4cf327c21d5d3f780f1b446c1752bfe95254d5ce9185f4b1ba6fa3b244076ad7a6225458c71725fd1459275f9628f1c75f2cfc0de619f97f59c5200dfddcb97a4ead889c0bfdd658b5f7a57adbf0ba3917f145e7d25da92c5175273befdffdbdac3f4130825aad05e5ea2fe06a077ef55be5a6e67ca626ab8fd5bbbbe18898f956d09caca6dfb67aac76e35b8f87f1fc0106c53e1db96019a5c27d985956d533b1fa6d16db5e49cf2a03f7750666face33f979a8560cf5b387f84cf3dae788fc519c015d8ed68f696fdbd0796aa76a4f4db55f57019669a83df4e03a546cfd59c8932aa8c24db55bac38db3c4b34863fca3a7ef064f59d44cfac18cb3ed3f23ed7283dd9d0ab4965bd4b6711abf03dabad9e1056cf77e89cce2dd77f96b8438595524658ab98184467bea9a7886bf3fd11b72be25a54dd07954ba7335c0ad93f218c7b46c41d5dfbfbbc90acb904cb84fb2316b3eb2da9f0a968ab5db3741e47113c6cfebda83ff3f20c71f45df208dba298ccb72ebdf17dfb6336988a20ebea39d8edf6a58e587ba6de94256fb47d46cbdc212b8d0b7a9a24d2c7eaad6e6a7f449ded5a446eaacae9ac8bb67abaa9a82c51cdeca97455b6ebdf73fd21de44ca837356d47378fa89a9a9ba6b09aa5e486eaadea7b3a972e9d475143627d3fbd680ef39411e127f903bfe92a71f05d96f1bcb53602ca99deed43e074565608a867b24d47af3493159a22a5b192ca39e87bd8e3ebb14f2e739ae58f7dbadcbe9b7f78ed5fe884559064a984b579065544e671b8ed88d7b5774c89f450f4f16521a1dbc6cf6cba0994d60cd2545204558a62efade89385dd827753bcb5c507e52539f638cb5b57d56b913989a5faeef0f742e9de38e41858bca4dfd60f79ee339d22d883350202a7df5661095a6be455bdb07b9133bc2c4f31a79412dfd14c8147e15b3380a613f68e6a9ce2d2ee6b92f9567389337c12be5d2651c33863fc89d37f8ef73f00ef524cfbbfdf4f34d2fde4456b77b4375f4986ab764e0e1b0a03b923ae34f4c140f61de98f36f883fb1dcc1c2baa9abe02ddce942bd45a0b09db97afaf9a6a9d2cc62eec7eb3d3edac043f5ecea5ab35fe28fe14c409c79d61f55bfc1cc23e5b972f0b42acb708d8ca3f42bd16f2c7c71cb30873a2be68903bd1f5f55ede6af57aed7fcfb4cfebc07cffc327984e9f7f9169281a933df8610430ec520f26ed4ad2ca3ee78602edda6ab30c49ff788de3c2f177f9f0d0bb0406251efb3554f99aafde58b7a0a5cef54d850fb726d979f94c89ff7b0bc810679c5efdc807dac32dfd4fe88b21af76ed4f58bca03d677e4db52ed68b7755e88b6cf82dec49459302e28e7385a22fb2316b3070266898a6fb5dbe8e63b22a5f027ca9977b218d60f4c1668999ed91f917691dbda323aabadadd77c4ff57decc2ce35e44f3c6fc2c29e8af14f2af34dbfef67aa23d702ae80dee5d9573b7f16f43cc6327fde23dc795ee0ce5b588fb7cd2051fb2302fb1bee83bcd1591c5b9fbd8a05f42e4bea2d71c54403f9f8338b29cfec6ee35fd1fb23820a5f415453880aeb4c36958376259a45a870267f96f5e62de2e9c942fc6d932bd3996f2a97ee54bc14b5231268d7503464497c17140de4e14f1c67b0bc51fd60e57893f8bc28f6832e94d56e3405cdc95aea88948159d03a734efe247066b1b0a39c4735fb237e8b2bbd97671179f07581bb7d5d8a41f12a1c5f548ca9ed93cd9d99ae3fb2cf4cb3bfa332f836db1f318e87105b777007aa9f52e195d26335d9b0fa817ebf43843bc99652e5cb3a4f39aada3903f3d44a7a17b902f640d0fb23aabdee4a996f4a2faaa808a4197c07756bc7fab00631fc8972a6111cd2bfb09e24f79fdea7aa2b3a05e55c04e0b9310373f0f32a4cbfa972342f9c9ed515636b600d5897ec93c59b676d29551f89a477aeebfcfa62f28fadb2569a86dec1e8279fab9e9716abf19af5eddc08669db04765192cc49fb7087796398335161505fde499d6c173eb772cb1fb6276e6ca2e103d35f97d700ce3e9046c0356094ed03aaacee44f231c69ba4ecd5ad8e62c4ba0eb3a9f59baebbe297de3d26243f8b56736152c389d73265a883ff19c59e4ce2c684269153a53d567a9f7e92ce19b117f910aeb1ddfacbe73e35c581f518b18eeb02ff8f738853fb355eea09777f60abb8296de99ab0f9fb27e1b7861bb8aa49696b5679d0767560754f8248e332bfc49e38ce18d2987419b0f0a39cbb27ab31bee8ff88b54f88cb94c3a3df6a454389933ec8b754dbdc09f460267162dd709ba5b9d6543ef58705ae4fe88d9c55131d933cc007616fd532efec471a79950cec192cec6731fb5a7ec25cc2cf5fe88853c9b9ce73787a2ca66309e18faa765bd41aeac7227d49f65ceac72e782ac73a1718f8fd63f53710dfe692abed5eef2bf4a85c53593ec8e4dad9d79c4b75ef9dbe84f92de206716ebc3f5d682d88718ba0fb83fa2cea5fb059651d13c3b776eac8eb2cb2a6792f426567f92f5a619e1cebc3c07fddcbb9994c595da1f91de20ff0b2ca33247ac73501a6599b5b466c98a635542fe6471e61cec644a33e8d8d9abadb43f22bd41fe575806a24cd90d2e61fec496b5261a13a7e98daa559ba9ff8eea4fbcdec4a9f4733048f53b6dbd974343bdbbbba03dc6b24acb1939bde05359863d6ea63551de2cf0278d3b51decccb2cb882282bf96cdb949bfa8b6261760c4ab303fd7d1267993c9c89e34e447fd2f566d97217dacba7ed10504cee4f7669f113eb14bc135b55e1ed78b3c29f34bdc1d25ca80f83eb9fbc6f905d40d3be3d3fb889b7cc3a3e6a9933f33ae44f1a679a21672ec25a8fb0c267aab9b586d7824feb438fa69588af18de2cf0275b6f9a2bf56df0ec3dfc7aeb886beb34b8e307a957bca68f4ae24ef0181c117f92f526ca99c5a218f453bb02c670063ee7ee55706f4653713e6ab5b0afc00b5ac08121e94f5ece44cb79500a767e8d65c419ff10776abd3c8d3345e88db64c1bae6f089f5db24f928f4ae68e2a30c2d8e98fdea5534f51d5d979702725c6c13fe3a342ce8c83a3a01394c1327b689d608ff427993368893967a2f579a09eb3fa21cb381079079ff225596b368d896338330e2ac17e704056a986f5de127fb23883d63205e61981b24fd19cb9604369f3b19418ef6dc79974de04637f08ac698eac3967a275843fd97a33af4d698c5881777b5b4ec96907df3240cb64f9a8edf4465966c4218e3b58e28caaf7147754aded7391536fcc488bd61d8882b6b78c7a32ecda3a74f6e48b359dcf9f7e466f3467bca037b2b50aef2d73265a883f51ee247126ca9df9a7e3ef6cf9ec558f9db021b746ce4858959fe7cca27f5ad69b903b5876893fe97ab3cc19532b4bcd4632e59e6a96e7be7046ec42c5c230a27e94339a371e28cdb356e1610c6756b8a3ac15ea4f3667167913619073b6c9be9bacc64fd89d6010d764de65d9d6478d5c509aaf9117cb9905bd897227f45fe93e6a9533643518bfca3e87237fad7baa2d3e1b05fcdeea089827e0fce9e76262edb9d13f0de993ce19e28d29da3ee93e2a8937e16734caf986089d6b6a1d320191f0347b66b99dde90653a6a96b0648114bd59ae97f813cf9965eb2d7f9ef5dec8192aacde9c6d7d8f1c81b3cb939fd31badc295a0472a9ce9a36278135a2ec29fbc7a839cf1e85f07eaaf462c6d5740a7cd4ff83baa705ece6ca637649936cd12e239335ce24c1c777647632cc49f75f426ce5a0f811d67199571c46bc119a970b85ef31331b19e59827f1a7911adc9f6514b7ab3a4d4467f726b8de10e9cc32df491a95b815cba47d102461d5adf5657cad163be55accdd789f51c617ff444b1f09a3e2a5a1bde2cf0278533b17a13f7b9e2a10f1b3df393d124188831c5c285f9a855de0463e5b96196701b72269fdea470666ebdd138c29f4ccee8cf9c33a83d652a1d18612dab377a09582083aba00de73dcec799cd7c94f14f344b58b640b28f4ad59b58fe24f8a864bdd1add64299f9557b367a1d49f01bcffa1bcd605c14679679a3c7530b7ee16081353963e214ceeca2d5c83a7b217fd23f07099c090bb496c12aed603f3857ffd6f8363882be2e3eaea980653ae15acd0f70665e46667d3e878f4ae0cdb252df8ede420b3de719616b688d074af3acfb6853cec473678933510b25f127c64725f026a5dc065e313e0ae64fef416bf434b246d31c71cd9a3e2a863733c51d552bfb14c0190bad35e70e95aff811b6b67f6ac6f8a7ade29aa88f5ae64c127fa23171b6dea496375d0f83437fb8a9de68ff049e3bc13fad398fcaab3751ee8c3eb2f4398e37c97a13530e5647583eff04acd1fe69f4b9665c93eda3163993c21d65a9d03e0705e88de60c7067a8b953d6ff6ea93192ce9da5b8a6429ebb1c6b8135e751ebea8de24ca44ee04fb6dee4b0148eb0a61a61f9783372613c7516e64f05eb4d3667426bcd22fc498a89f372a66c38b3c41d5d431494a937face5c0bf4ef202316fe111fb5c49db1aea92cf26785376f397c54fc289b974e304ee54d25c63f153e8fcae24e9433cbfc89d79bbc3e2a8e332177cabaad12af3778672e585ddffbe198384d6f42ee7c6141fe6ce5a3e279131d71df268e8ec435153db33cd0568ce7cd363e2ab7de24f1465b2ca23f9be94d126716af1a74651c896b3ced9fca7094698205d6f351ebeb4d126722bc89e74f5c4cbc266f96b9a3cb88d3bd04b55e7ea0db16e39a9fd39bf5798316c3fa6bce9fa2f426f6d3d2eb11079199e5fa7a53584cbcc29918deacf2672b1f15c319b2da10b9b215673658b7599b3773cea8baaead3333fcc9b4c45a7ab3fcd13ab37c67ae807b0b5beb4d026756f953808fb2563913ad1378b3ddba4d413e6a9933510bfdfd7fffe7bffe1f92e4fedb</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>protocolListView</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/addaccountwizard/addaccountwizardpage2.ui b/kopete/kopete/addaccountwizard/addaccountwizardpage2.ui
new file mode 100644
index 00000000..a5711037
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/addaccountwizardpage2.ui
@@ -0,0 +1,156 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddAccountWizardPage2</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddAccountWizardPage2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>356</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Finished</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>m_header</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;h2&gt;Congratulations&lt;/h2&gt;
+&lt;p&gt;You have finished configuring the account. Please click the "Finish" button.&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mUseColor</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;custom color
+for account:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use a custom color for this account</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Account are often differentiated by the protocol icon. But if you have severals accounts of the same protocol, you may apply a color filter to that icon to differentiate accounts from the same protocols.</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>mColorButton</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Account custom color selector</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>101</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>PixmapLabel1_2_2_2</cstring>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <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>58</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>mConnectNow</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;nnect now</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Connect right after Finish is pressed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, the account will be connected right after you clicked on &lt;i&gt;Finished&lt;/i&gt;.</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="95299">789ce57d5973ea3ad7e6fdfb2b4e1dddbdd5e58f84904075f50564820009848c74f5856cc90364200364f8aaff7b2f692d19039e0067effd9e2e4a47fb3922c65e7af4ac257959feaf7fff75d7ebfef5effffad7db3b7f0f9cbf1c9fbffef56f317d7cfcfadfffe77ffdf7bffeded9dbfd6bb756faeba05cfe6bf7effff1afbfadc7bf9cbfd86e696fafc2357ed0b8b65722cc08537bffdde07dfc7b4eb84ad825ec10b6152eef84ed4e88f1f892b0f9be0831b67b882b25c2cf061f94351e11ae50fbd8606c77f5f997e1fc2afaf8fc21c4fafb421076103b55c415ba7eb663309ddf5388f1f77c83e97c3e099bf3090cc676764bb846ed1383e9ef5f426c6b7c4c5850fb7b8875bb4dc7dfdf411cbc2abcb7b357dadbd1d7ff4598ae5f5e1176c81e2cc4fa7c383718af5758888d3d9ca1c1d81edc13ae507bd960b2d75188d1de2383e97ae8fc8c3dd8bbc1d4fe1a62b4c72561638fb710633b9d1fd803db4b21d6edecc6e0aa6eb77dc215b2e7bdc1d82e0cae21f6f1787c6f6f4f9faf7742b88a583c198cf6f5f7083bd8ce760d26fb7f8658b7bb75c4606fb4ff6788f1f83d83d1be927edfd85fdc194cfd771062dd2e25e11af5cf34c4badd367f4ff6e7e6ef05f1b56230b63b7e88917fa788f777a83f1f0dc6efcbd710637f74081b3edc8758b73b2d83d1fed63e61c3976b83f1efc5b3c1d47f5f21d6c79374bdfb02b14fd773b08338e006d7f63476095710fb9a6f95dd72a954d2e7778038e403b59bfef566884d7fbaa721c6f1726d30f687f74db842df9f8518bfff6630f5e70b61eacf910831f6d70761eabfd1a3c164af3162d35fd689c1d41f03c246bfba21c6fe730893fd837188d1befb06637fb007c2f4fb2337c4687fcf60fc7ef014626ca7bf37fde5ef1a4cfd43fd61fa2b102176341e11ae51bb1f626c7f272c10fb5aff2adcf4b7b747780fb1ff45b84af831c4556d5fb467a8170e1d2fd45bfabee187d33498f830214cfdef39846ba4176d83f1fbee116181ed72cf606ce7e310633bf24d8d57d4f3bb10ebf6e0c660ea2ffafd7da3e7cc60eaff9310a31eef10267eb0af10637f3e1a8cfdcd2f09131ffca1c1d44ed77760fc5dc5606c1ff11063bbf97e05f188aef780f420f83298f8108418f9b04f5850fb7388b1fdcd608eed0dc4d51d6a9f194ced17842b887d7d7efbbb217fdc106bfef867841dc20c31d04f63e7de60f4f7de98f01e62e18518fbeb9630f92bd6430c7c42fe9409135fc42b62c30ff725c4c83ffabd7de36ff6438c7a32234cfdef5c8458dbdff3091b7fde0931eacb14b1e96ffe6630e9c34788916f5784e9f759c960f2dfe510231fde0dc6feb5ee08937e066d83a9ffc95e211f5a21c6fea6f30ffbffd060eaff2661eaffe03cc4fafb239b708dda6f438ced166141f8cd605b7f7ff489b8b683d8a969cccb4eb9a631fe1e377cf19f0813df3cfafe9eb383fcfa3298fc19da07f46857f3959f1a8c7c728788437da813267bba5706537c4ee7b36ff4283098f8d0451c8ef74fc2c6ff3c194c7c9810367ae28718bf7f48988eefdf1b4c7ad0475c257f327a0831f667c960ea1fba9e2af997119d8fe9bf911762fcfb03c2d47f41d960ecaf80ec67fa6f340db1d0ed7b842bf4fddd10ebf6d18c700db1a37fffa06cf4c07f475c29eda0de5c8418fbff9e7005dbbdb2c1d4dfb6c1783c59456cfa4b4c111f90bfb00d367c681b4cf1e06e88b5fddd5bc2a40fce8dc1143f1c11267fc0bc1063ffd51157491ff87188b1dd3198c6ef35611affa391c1d47f92b0f10774bda1bdc784c9dea30f839d8ac6743e3541ed5f0663bb83d76f97ab656d4fe788308d571bed6187fa5e415c11d85fac6930b6db6788f74dbce011a6f1e53e2036f616a784c9de828e7740facb3f0c267b8b10a3ded2df1b7b3b7b21c6f1563598ec4de75725fd0d060693fe5e1336f391a1c114df55438ce38d1126ff2b74ff56cbe53de4ab731562d4bf57c2646ffe85b852417bfb1ee2fd1db2ef04f1c10ef947df60b2678930e9993498ec252e111bfb04738cf3911bc2a45f6c1662b4cfb1c178fdd2266cf4ecce606ce7747d55ea6f7b6430b6b3d3106bfbf10ee21a5d8f13184cf6f50d26fbe2f9d865d20be7ce60b2f73e6163ffcf10239f6b84ab88bd27c49512f98f6fc4fb3bc85fff8ab0c0fef2e9f70f76f0efc5036133ff7b435c257fe13d1336f3f9ab1023dffb84c99ed6aec1682f41e75325be3b3d83c99e74fc1af1db7b08b1b61fb3099bf590db10637cd13598e61b3b06937f217bd5c89f08dd3fb5bdd0fe8706a3bded2ae13d6a7f3198fac30d31f6c70de12ab6bb32c4badd3d21ec507fbd22de2b617f786784f710bb1f8449bf3cd760d2af4fc4a6bf5999708d8ed73798f8503198e28f63c4861fee2e62d3ffde25e22af90bef2bc4d8df25c2d47fa28bd8f41f3b0fb1ee0fd1206cf4e93bc4e80fa60663ff5864cf1acdc7b817629cef550dc6bfb7fa06e3dfb337c2147f073b06131f6488912f35c47c07b1a3f5b4e698feb6f70957910ff681c1d82ead10a33e4e089bf84f1a4cdf7f0bb1febe6c198cfd278e10ef113fa56730f183ce2fe4cb28c478bc2fc255e24f8fb0f93d3aff4a09dbdd5783915ffc89708df88be3c5d9df217e71c2a48f1ef6bf63f4dca7f6ea0e5eaf4fbf5fa578c7ad194cf3893661817cf5070693fedc1a8cfc73a93f42bdb00913bfc44b88b17f9b848d1e55438cfa6d194cfc1913a6ebf35f0da6bf1f8418f9776e30febdc3428cfa45f6e634ffe3f706e3f7ed5988917f6784cd7ce6d5609a8f687de07b864f7605b1e18b7d6d30b50f081b3d7b3698f48a1b4cf15383b0e1fb95c1f87db14798f4490e0da6fe75101b3ec90961d3ff3dc2a44fbc4958903fda451cea139d8f89c7fc6fc4b51d8a37ce0d463eb99f84cd7a638b30f1c7ab1a4cfd593698fac343cc8dffec1a4cfa766c30e9c97788757f499730e919932146be1c10e6c807bb6430cd676c8369fe42f6e392fa5ff3853ba6ffec3e61d35f970693bf12848d1e3d194c7f8ffde984f39716619aafc863c2a6bf1cc282da0706a37f61743efb145f48c760ba7f42e7bf4feb6fb24998e26f7e1e626cbf3718edc5d0dece418dfa7f8ab866f46948b842f1ce09613a3f798a98937f72ef0dc6fef276091b7d9221c6f5b40f83893f1d83c99f3408738a972606633b7f3718fb9399df97a4171706633b1f21b67769beca428cf3533d3fb2f7c2febc406cfad3ee194ceddf842bd45e3798da1b846bd82e1e119bfe7477080b1aff6d83e97eca3ee203b3de7519621ccf7b88abe4dffd67c49cfacfef10e6148fdc184ce3fbd160b2974f58d2787d3098faefd0608aff8606937d6b888d7d6537c4dabebc4f789faeaf61307edf3a0cb1febe45d76773c4b6d6175b84f63d274cf6751c83a9fd8930cd4fedaec1d8cef0f8c2ccaff8b7c1f4f75f84a97fbd23c2e6f88784496ff98dc1687fd6426ce607f2c8608a37a684c93fcb3162335f9075c2343f967d83e97ee13b62be43e3fbd060d2eb6bc2d4fffe29627b97d623ba0663ff8b196163ff2661b23f1f1a8ceb05e28cb0a4f61383b13db0103bbbb4bea07f1fe4d0d8ff12b1b1bffd6830e9e13361b23f170663bb27099bfeef1a4c7a6d8718fbc71c8f7edfb6438cfe32c4a47f1f880f0c1f3a0663bbbc234cfa289f428cf39923c2f4fbf2d260f2df17884d3c26de119bf9af7b6330cdcf1961d24f796d30cdc7cddf537ce6d2f9193e78fb8425e9fd0b627b9fe2c303c29cd6333961d203f66930f6276b207676e9fed1bec1a4479f06139fda06d37a5410626c2f13dea7e30f43acdb5decbf707cc90ae2038a5f789d30f1c3b93418db99f9bee1c7a3c1e42f2f08131f1cf37db39ef510628cd7a7884d7cce5b21467def188cdfb7e9fcab153afe89c1e84fed63c234ffb4770da6fbf3738cc733bf47fec3ed1b4ceb6d4dc4c69fbb2d83295e32ed86bf658369bd86ceaf66e6c31706137fcf101bff2f0d36fca4ef1b7fecfb88ed5de21ff597cd697e30232c49affa0653bc80e31ff484be7f6b30e9cf8030f1c51911e688472f060be4974b58623bab198cedb68558ec22167a3c8a8ae92f5e266cc6f7a7c1942f7118621ccf55c2c42fdb3198e6ab2e62d33fce2d61b35e5d236cd6233f0da6f5ed53c2c407716130dddfb80e31e69770c4263e730c36f1c15388b13f7dc2d49f9c8e67ef92deb821c67850188cdf77df0893bf1715c2e43f5c0bb1e94fff9230c577eea1c1d85ffe1d618a47f8d460ec2f3e436cfacfaa13de47ecb4432c353f9e0973c41ced2f0dffb9446ce22b7b4298f445f60c26fbbd13267bda8230e9af1c194cfa4dbf67ec297608efa33d9d6fc21cafd73d3618ed257cc2640fb78bd8e82d7b21bc4ff7ff1f0ca6fef10cc6ef7bf4fb0e8d4fd6262c299f686230e9fd38c4c857e48734f6652784c9becebbc1725ff7cf056149ed2583b19de9f10d07377a3f214cf6f41862a32fde21e17dd2f706614efef9c560d2f7b6c1b4ded63298e69f6784cdef750cc6bf77760cc6efcb1a6287e2617b4c98f8eff40913bf050f31f2db264cc7171f06d37809dbb1bf64d88ee343760c46be7b25c46297d6131e0dc6bf779b06933e8e08ef93deb5438cfee5da60ca0fb93298f4e2dc603a1f61308dc72a614efeefc3606c970f21d67c90645fc30f1e188cfcf0881f7297da4b0613bf7a84f711072f21c6efebf98094862fde3b61b2bf87f680f144b84e98becff609139fdc03c482f4cfde3118edcde718fdd53161b2971f10e6b49ee7184cf9575383d15e3e9d9fa0f129a4c1680f7762305ebf7f83589af8bc6330d99b1b8cdfb71961b21ffb08b1febef74298ce875f194cf66f85587fdf6a1096647fade7eebeb11fab8718fd9d242c69fda48218f88ced6f8449dfd81561f3f7aec1c4b757c2743c16184ceb2fd42ee9f8bc6730b55b84e9f7bc73c2a4175e3bc4baffbd538371bc7b8d10ebfef6ccf1687ee50521467d0f8f4ff6168469bc7b8f06937f1b1b4cf63e33d8d5fdc1d11eaeb10f7f216cec8bdf77c3eb3f3498ae9f11a6ebb76a06637bffdd6271c5e2f05f6e6a28764c6dc337928a43b58022a98e2f3614178a5c2a22520bcbcb55e61f3fa63625c82c2328636daf07b8cec7242bcdad654ad46a11eb71b21627ab2cd7a6c8258bc55bce8d2972a1466bc8dcd68afb284b8dc822e3382badf267e9ea633993c21b27526773862f5df53267e4dabc79da8a3386379a3baad6fcc9c99b04cee0ff4be78ce14d3a67e2782397ea2867d2b893fc79a63aca9958eeccf9a35953acdea473a658bd89e3ccd346bc19476a558264fe6cac3771bc594f6f56392372712699378633997ab330c246649f4ccefc32bd59df4f6de7a3bc25cea83a30dc09fdd7cfe84d3667d6d59bcdfc94b15c3a6762b813f2278e333c436bb6e3cca67a93eca30ad29b857a62f4e71feda3d6d29b65aba1fe6ce0a39cb57cd4b67a931d136fc69915bd519c8916c39f141fc56338537c4c2cac6d63e2f57c541c6f5647dc24d49f5f1d136fab37ebf9a974ce048b9c59e6cf1fa537c5f9a88df486ea891969c89f1c7af30f8a89d3f4264d7ffed131714ebd897267ae3f5be94d968f5a7f0efec331719adee8f212d693507ffe61eb366b702658e64cb40ef9f3bbd76d7e0d6732f54671467f5eb0d0fcf43fd547e5e74d8a8f4aac0d7ff2fba83f2dae491a316bf928d29bc9327722fcc9e3a336e14c3e1f953d8f5a7bdd268133b13e6a65e4cd3f2fda3edbfba8fcbc89e3ccfa7a933d8fcae7a3623993c19f227c543ebdc9f651f9f4664b1f15c39bd072da3eff241f954f6f523963bd52fd16f2e71fb76eb3e0a3d6d09be53ac29f7ff43c2aafde28ce58eff33ac29fcde7517fe0bacda67ab364ad77e2cf7faede64f126afde443813ad893f7f928f8ae74e1667b262e26cce442df86e0af1a7889838596f36bb07bead8fda486f748de525963fff1fc4c4697ab35c883f7f4cbecd567a1359b7595b6f229c89d4ef217ffef3d66d8af251b1bc31968bf0e78fc8b749e0ccf67a93e2a39639132dda3e7f50becdc63e6a6acdac0feb730dbd49e44cb426fe6cefa3a2dcd92c26cebc9799c0992fb0ccb755b71ad6a175b4898fa2fa78993b217ffe43ef2dc0e7c43ab59a56cb3a03eb9c596dab43ff3f8e33510ba57246d7c750bf93fefce7c5c4ca325debdcba20cb60e9597d1861ab7af3665d5a03ebcabab66eac5bebcebab78656295e6f12f5e74f5c278e5bb799c0b5ee58bb5679c132a6ec59951885deb70eacaa5563161466d5ac2ae3cc660e58e96d853bef9191769cc99f641ff5ebe7e0ea0a064c3009e369d532ba3097a95f5abce24be6b3802c538bd42336660fc0a564fe94427dfebdeb36d97a73c91ed9137b06156ec55b864a0bacf7b5a8ce6cc25ee6dc59a85fc1466f60a3e3b9de2cab74027ffe989898bd5b15366533abc93e522d43857db2afd067a9f2c1be597d853b919a35d8a11e690bbc81f216f2e78f8c89991a195fec881d6bdf9d69191a6127ec14349afc127b674dd6626731dc89d66deb9a75623d7c69893fd9317151f936297a0357f5ceba309ecec163276a4da2852ed82372078ed2637d76c906ec2a9e3b218794165d136f42eeb09b707ef1c7c4c4ec169466caeed83df8ee352d43236cc84a70a44fb6c3765999ed817d2a6c9fbda673087cda81e6507484cdf9f367f8a80a8ca7aa75ce6a192a9c5e7adce28c73283677b860975c3d99ee267167ce21ee59f78a33863b0bfeeb77c6c49f30121eb90ffea9c9832d2c43858ff8983ff047fe04dc51fcd9e3cf7cc25f12f527d421fe0a3629adf0e737c6c4ca3ff13776c7dff9747bcb60e133fec13ff917ff56dca152e70d7e98c921ce8f903b544a217fb27d54be98789d75e22ff6c58fad737e02bebb10cb5069b1637eca9bc89d90432db0d0593a87789b7762f8b3998fca5eb749d41b5061f04f569f8fd849a1963123accbcfc12222c21f552e6094a5ea10eff1bed6a09229a17db6d59bbcebc49f309e2ed929937cb0950aa759e78a5ff39b2877223a74cbef527d59438db018fdf90531b18e6aaafc9e0f79e9672c43fab3c37757b813165e563be3257188efb3871cfc293abfef931ff0375ee535dbfa49cba862339bdbf62a77e6853fdb8eda9d8a9f2d73c8766dcff69542db81f264ec46dbe7677d14a8b03de2ef9bc4c29b147b6c3f2471078bfd683f71613fdb13fbc596f6abe18efd66bfdbd3397796f85374be0dc4b0d609f7ed9975082afc435ab3629d0ffbd3fe4ae68e2af6137cbeedbaddb00fed23fbd83eb14feda6dd62dff699f2ea863b7690c99f4d7d945661f04f3556cb37ef2e4a99edb6dd49e30e943dbb6b9f83752eec9eddb72fed01942bfbdabeb16fedbb2877ecfb903f9baddbc4c53523ebcb1ad843bb04e3e9e2d75986b467c7de4de70e58e7097853b6f7c03eca3a15b0ccbe7d6057ed9a632167a2232c813f1bcda3742cec3b8c9ff0d9afd19a25eb30873b7606771eed73c781d1d57384e6ceb5231d69571dd7f11c1f3983056c35e7cff67a032aec04d6a13302dffd8bb466b13863e7c1794ce78ed69e73b0ce85239c277be03c3b136d9d17e7d5795be68eb2d2127f36f1514a858fd9051b3aefbfc732589ca9fd09d14d86df02ed51ca7c01d6e93b33bbe27ca8d1e57c3a5fabdc5175447fd69f477d5a53ab032aacc653eff7594615185bdf10f5e4f15bdff69e53771ace73689d43e788bd2f2b3316e2cfda7a035a73c2eeac433e2d6245a280d175ec9c64f8ad4be7546b8ff15ba0ccce04acf302cadc8c7246d54e4bd509fcc99a474dd99373c66ace2f8af87258a76373a79ba93d0dd09e8b88324f94df02ed39c77827ca9b25fee4d59b23e7c2e9f181d32e784562cbe2f49dcbec9859fbad7a44993f803d03e7cab98e7287dd18ee382db24f1ebdf1f8817363edf1198ca7dfa8c271c5fe706e73faadfa82dfda07ebdc39f78ebadb1cc31d65a5087f92f26d3ead236b669df399f39b3c77867598b3e374d3b547fb2d1d333bf5302a3c50dae3ec72e694adcb2867d447d5217f92f5e6d3fa70f6ac323bf9d915896d0a287365d398d9d9770e9ca1530506c47027a23fab3e0a223eab0b51cd1f6c1955d46a46e67c6b296686d125292aac394361818586ca16cbdc59e10f7187ed08e6f4584d47c2bfdd02a9dc19434098bad69316330b877f0ae10c61840dadaf450f6ffb682b6d9fb9dea8f174c387ce1f6f192c420a37db6f25c4cc87c243ee280e5947a83dcb25e48f076370c73ad719127fa00ac7159b093f07774cccfc043173681d116865d6a38b46d89be2cef247d9478cc498cdacf27f0667a8b4acb278108f5931b3f25b919879606266185d4f863bba2e5b5fcbdc11cf649f0928cdefbfe27c45b1fb42bc8857f126deb3fc564acc5c9b73070bfab0b94263adc7d7efbfea9c9c817e6c8a297c66a03d1f196b3da931b3f88c70c77cfc396fc4d79c3f7fc095e7b00c9f8a6f51170de4bad31187ebc6cce2086366712c4e16b983c53a5ae48eb2d01fcf1fb00c3b1113712a9a913b8a2dd14ae74e749d7929663e748ee6aa1cfd5853ab14e58eaaff60fe34b465ce80356dd1598c529da9ddd93c6656ca1cc71fb0d02572475be7f90fe64f8f9d305774445374996b2d65d7897371b145cc7c2e7a4bcc299b621de951f5352f7f1c7f5a3ce03350e1beb8e4b578bfeaf4950532b4e7dcc4cc641db3ce7c0531730c77421ff636e7ce9fc61ff60196698b81b882729d14a5425478933b66de8bc6cce0b75ec4ad8977568bb883115631dc11aa3cff21fc6959753e10435102b5394b5dcb6e899d3563e667a33d30a3d8e59f49dc218dd63e6caed07f007f2e9c118ca7328ca8093bc9fabe52e6cc98f9313e66167bea1d24ab9c59a80fac4bc31d55ff56fef420a63903dffd2daa3967372d51cba13db131b3b424935cdae26e993391ba4c23cc78f9dfc59f16f8a74f311355d14d52e1b8628f378f99c17d0995fd2b652277ee8c0f43eefc1efd016bb4c54cbaa221caa0c76bfcad90cec9a631b3f4a46f33d5333288e14e19b9837538c27e397f603ccdd83d78a72afb5c3bafa32547e9f7475362e61739960f64e5b2e2493c77a23e4c73e897f10794b7c9eed931ccbc73a8705c918ff2493e6f1633cb0928f3391deb42be44f566ce9db094e5eb2fe44fcf1981e79e88013b8619d4a6a3f2cab975ba9be566c837f92e79e4581fc9dcd1057dd8b39c927d7eca322d71cd6bc09763b0cc1a2a1c5784bfce3af362cc2c67f26321da6c8acf44eee8624d6dff67f9c34bca73c378ea8889ce06daea78ce54e6cecd50ca3c5f490565fe22659e1faf25bf13b983458db02fe0cf4fe84f0bc693b68c74ad762177875afaeefa9a3133adc2bfc83af8f5e5637eabbb82f1b30c9aa96a1f26a7c5f2879d404c53569c010e5f17c343d99087f268d398591e8bcaea71f94c9eac72262c07609fa9755fa8fef4ac3270a62c5d5e734685642fb43828baa8ca53d9e4dfb9e6eb4bd601656e2d2af3bcc82069961af161cf85f0075418bc535569308ca742b217808743754431649ff24cad85e58a997b2b31735b76e27f4376395be10d71076b1861cfdbf247af9757e133b49ac5e490e9e85a1d114628734149cfedddbcf9cc601db110337fc98b25658efc8e8e82523448adb66ec19f169f42ff76d575e89ccced4728f010661d5d760cb61e5034d0923ddbde2c66b66f64dfc4cc71455c6affbec099c55af9b00df803ac81a8a60fcad08078af5ec45d45a5c260eb861aa1e27a3ef3b0c7f2326bbe9e18330f44c51927ffa633925749dc51d651770c37593f84c8660231cd80cf8a51618806aae21b78d8e6b3451e4a0e3173f67c3d3e666ec96b185b29bfcd3ec47e3277d087c9d74cfeb4d4cc493f5b125e91552f2887ec424ce48da82aa5d1cfc92db5cb5b79b771cc7c2fb37aaf659dab3bf08b9c41eb989a3da6f1077a53c5320d5112ddd5b3dfaa80ef8611da8719ab9b3442e5b938dc2c66b60fe450fad93dc84ee4c90a77168a95a43f2de682875596b9541f7903d751906580355559125535429373edf995ad9f15d824660665aecb9d1ce7d293bb7afe558ee38eae0f56f9a3c69318805a5e01f72fc3cfa408eb881958e652c5025923d41e3b95ac9839299f593a7131736c2f9ca879463c77b044f9037e4945ac5d79b3601962d076f36f3e030d86880054fd243b4e723ab29cf31ed76accec4996d7a7f240ee257247d7c41fd05c184f2fa00957cb960947d8e786d6e981bf9b8a2b155be78d9340992b1bc7ccfb6a9d39f7b9a9383a813bd694eca362e044cb849feafa3e0b54f8126cd3d1599eb9e324f9982b37233e663e9017eb9c27ab89cf78eee0a853f6c9b40c7eae60469edf3201a830589d4fd7cdf2e457b29795cf9c12338f613eba4e0f063288e38d29da3e79aca346d87dae5f6d011f4fc51462e18b4d7219654d5e6e9a9b211d77cde7a3d907afb92c9e3baace6f1ff894b2ee39800a77455dbcacb3a3d162713ace6dceb59e9598d9e572833527a72df75679230e84253ed9ee3af691375639f657302770084a83bb116e64196d9db1bcdd629df91ee6a36bff26441aa7216f4ca4d893576c575ef1cff5ec2356ef3fb4f4fd893ed8a60cbfb4b165b0b8362873465e5852ccec3a6e7ebfb5d0bb70fe56c89b3bc51b79a5b8a3eab5c6d725d821ca60608d74455b5c16b397867a8a4bd9609398d995721cb3d6a3f216b39faaa95bb3b965f8a7e18eaad7b30f302872471866f243765fccda0f28db85ebf29bcd6366d75b3a660f7ce8199c7533eb2e365cc137444196e14cb45e933f9730532d368b5c6b179f4ad7f5dd20cb6f25c5ccee48ceb9a3f619bd80313370c7ee835428f5f77909e69a65b63be70cd658d6b64fdf1915671de8e16b8806be45d37d749fdce7cd7233a425efdd89b95a98dbd54413bc6835736fa1160fd4bd28b5fee4be28be2c726703fd519f4e015aa3c6e6b5551665b892173e73c6e2c3de5d3766c66700417b5edd37e527606ef709b3c72acc80db199188e698d5d6592455280d71aa78b3c89d8df80351d0967eaac54ea0d7666afdc744034ec77d5f2f3743f92d8c99e59b3b05ce0cc44496c08b4e3277b500ad13d760c90158e6252c7d775688fe5ceaabdad03abc04a353dd9fb864c791fd0d5b10333f65694f52ccec7eb8c01ab04c29c7c8575a37930db00df2665ebe45ddfd5ae4ce86fcb914e50deee4b4b08f813525880616da9ce9e631b36cb9dfb2241b39d6c2416d980bda545de0cdbc5cbaf542f4e7527cf3e93a5a03f14519faec05a283989db56c2676d2f77b4a8e99417b1aee61f639b00f76a27ba7bac29b79e9bb4785e8cfa55a37ce671d359e2006e908b59e3a8bff0ec4ccf9f3992f96d799dde3ec73003fd6d6b689e78d295d8806567cd846f6b9841968960af67416bc5ac3eeeabd8913bee774642fa7df5a8d994f643fd5976a6f2fced45dfc14decccba57b3ae70e5a6843fbf4ad768a0a072a0333cca54bbd027b2c9f368e99076e8aa7e081ce763ccee4cdbc94c43e2f427fd45ad0716c1cade6aa2a37752006a0c2d9ebcc53b7953f665eca67f6dcb324ee2ab501251ee4e4cdbc348d75b6e4cfa5682ce98952e1a6ce8b1ac01c2dd7ce5ae25c2bf3a631f37eecfd5195d3a79e5428e7e4cc426133b75d88fe008364233a7280371d38a789cab9c8ebd7e4a3dbc9e9b7966266959be176978f07beb1aef3f1ab31dce80ab512decde04fd73d573eac00fe5c8941f48e0f9fb2cff5ee00296596cf39d67a626266fbc65d5c858798583dab00233b8e177dd19655f75c2a2b657108a220e40f968deda366f2f1efa0c8574099377f06d0ed45720a1b6ab62e1be15c6a810fe214ac5277cfd51d266d9d2c0deacf67aa5bf147ad050d379fa90ae9f633e75bc9f9cc46df202686592e7aa9451ea868742aea609d29cccb4ab935e845b4c527ff44fe6c181faad1d565f730dfd9346ba1256f73ecf794f00ca07ba9e329506298a7df83ea2d72e005e652a7a2ae59730a566aace9c32ed54c7563fe5cc168eee83be85bac1bca4727c7fdd1f933803a66bec67c6677a0b2bdc535c47df74b737054616d19f74129c0263ecce9b957ca32c8a135ed53d5d9a927dbad21daccbdde34661681ab72499a5a8997e64f62ea8ec1360f42adb8adc79905feb9b74bfce9e7b04c49e58c59f5ed73c678c9bdcbcc294c8c99dd57f71e2289c5981854d81d826da630aefa397c5456b9927b3caa3fd50c1dd6195e05e48cb5f840deb825772747ccdc885d6796eeee625fc3f1cedd32a8f0299ceb375cdbe6bc89c64ad3287f20124fb24e570cf578da3a739707a0a36df09e8feeeabef0396366d09e8abb1f9d6fbb47ee50dd5f02d6bc6cc999c5d2746773fd81d964699535e033cfacba8ef9b61b512d09f31ab88a32a8fa1446d7815b4d7f8e2b69af39f7c4ad191f235edd32447c256077564cbc49f916afea3979bcffce4ec01651ebbcf01a9fe9ec826db566a62276b0fe0c62018a593ccb7ef318bfc8173347f7cdf0b867abe3798e3b26cb14c9996851198077d6d4c2fcf016ccb93163ac2f5d3e60ea7d145b6a0d2f81f705bd942e44d90b19989e608c1fb27dae12bdd688993dd7f3dc23cf81a32aa5c9130b6f54d8b1546f18a979bebae3aced73a673904185f5782a4085617c5645496534affa3b8fdee5c6aebc112fe78d991dd71b83d65c16e29f92e7efc710716a76f092f7a018a4ed83ebb3257da771ab11c54e2062ab8b36af25fb3bef317c67c0abfdc627bc9e2766f69ebce782fcd38b38f5262a965bf8ffea898fb2b601f532bbb03eace29effd259511083549d110fd2eece792f0bef9b5023ed16f3c562f2992966f65edd1a68cff61cb97287de1bf4e0e582bf2b8ba17eba66416f9d9173b398bfba616968153e52b93f9a85191c04fe2cbfb3e4ca7bf7a6f2393966f6f8d67af32da6de0cd4eb546bd79c8755fd245f5c9e5b8b57a5dc8e3ffa4995ba7805ff779d3712f062de25c9cfec377be27dc43f03e87d7a5f5bf1e6d273bc199c675f5b26fcff4a6dd2f416ae8d6dcc9f16073fe27deb5daf7aebe4197a56c2bb930ebdba7d0cbe2b2e66de5477be45dd7bf31cbdc2b1700c760c114796de5eb0bb4df8036a7e0c31c891bedbb7b6a27b8de4f76fd9afdea177b418337bc79198397fd12a0c5a33d5aabed836d4194139ce951dafc51f95c5d486717c22fa10d36c382abdd3d477b8a9e8b1e9b54c3eb37716c6ccb9fd13ccc986708eaf5a85a36d2f62201bd61af9d1ec33277f5a2a02102f6a146f9e9d40fc69a7bfc34d7dbc8e3c525133684f17d8939f33dfee83f70ddebbbd145b57d588d22be46baa48267f74e69b00bb80d6548bd867d33bcf7c0f20fab40bafe7f5bde53825bee09346afde0c228cfe4a7b5574acb656c9b5cf37953f3af34da8fe38d5bbc86d6919656d6fe05db1abf93b93e26bdef3aebd1befd6bbcdc199ae5a1903ad512abc1c5b574599ddf3e9e6ef004ae00f58c6aa4b57f96e882b8b78b785da1bb4ce6b30bbbcf3eebda15762af89dc09bc1d6f379333ca326aa51958a39e6488f94e59cda4b69b65aff24765be89337105962925e55cac57a0f7d4ee6bdf6e5965027b656f0f3e156f9f5dad72871ff2b677e065e65a0898bfcb2a8cf9573d53586e1fa835f222deaab5c01ff5ac695994d5ea76ae8ca3ecd212d70c021875a7450c350f7b5ecdb7bc3ddff299cf7d5b8fb4703ee63bbef065066fd4cac629447c93d8fba1557517b7b8774745f9c33ee0e8d31c799ff98ad22e95655817dd793460339f29eb680bb9bee7fb7e80dcf147fed87f809e4fe64d573f6954f7263093efc7cfbf798d0ff27b1195ff92be5bd3227f22f3d72d0a66ddc0954e656939f7c71bf88f681d2a4ffe337be53d7fe2bff8af29fe49dd4398ba653d838afb4e55edfcc1a7b9cfbf057da6b2a69a2261078a55fe1450d4086db37b350220de88e1a1dcf1dfe6d681ffbefb53efda9ff91f89bcf906d69c026b1c1d0bc7acf5a91d63f4fe16b99faa54f75cd58c552b6cea77ffde7efe3ee79eca326ca87b2c89b93f0dffd3b742ee7c7915ffdbaffb495aa3c6d3a9acba476a3d35610e3660f7e22cb712f7f45e9403600d3e6994f93785f007e6aa308e8ef51818a62ba37f48da730423ebd83ff14ffd660c271a3aa7fe0174e67525160e79a3f677819ec8a7c40dabce3e613c9588d939af6d4bfee03e8f1d7d5fb79a23f7a7e1b7b475ce80396dbfe3c7dd7b5091f0a98ebceac93917d2950dbd2f4a9ef3bcd0f7e9d59a7b69ddb78c6ece1f959baab303fa6245859347a17f0ef6b9f07b7edfbf649fc0f126a86a0d8e33d4b967df2a075e65a4c4c4c273bd9928b5c91513f7f8148eae987da59f345afb3a37e28fcaf5aee9dd6dfa6a656c8d28ace10ffc2bffdabf991f4b2b97ea617d15ea6eaddec5e5cc6aca06c4bf35b5031e7c3aa2a3f6c163f760d53c7bc600b32927a84fccde8805ebf247ed4ba733534ba2c107ebcf6bfc5bff6e71d719b52392ce3650eb34670b4f23b500f5208e0a20a2bf863e99eabb68d9fb6a7c807f9a687def02d3b67a3e6d0dfeb4748ea1cac9ea027b36da0373b1e89c38ba0ea8d7e161e23942e435247d9f14f196d17cfc6127c0723d57068617b25389de1109679965bd9fdbd6c7d44f0b0e74945db60a7bbf5d267f7aa0050dcd1a75ff30fd59bc9c7d0cec1f90cfce99099c517accd56ad855da55ecbb9ed3f8c367e657751e7c01bfa6df64f1ad2d3329666d8007e0fbf01c3b108515fe66a524fe0057d5ea4a55ef295bc4fa4f4fbfc9a2a4229782d8df82c8eb05cf117cda0fbdeb39893f2aea83f154c8af72b54f785f74554c57d0da401d987da5720974a6c9f6c76b6966c7ec7194c49f62de1da7b338ae74943401df5c04fb7b3ad7a40f0a36b4ca45f0108ed1d46b4a03a1fe4f4efe6c5d940aeb5987de99ae9077ecf2a93e62033cf8d9f67b356816ccf45c55451767f1fbc526f167aba2d8df84b982bbceaa43aa6502b552037ddca115e5edcfb10711cba97a0e56ccc0fb25f2b060fef4d8a7ceb41aa88cb1625418e28b3e5c89ce2c2ee65d9a6abf10f7017838d3f7df53bf5b187f54e48a6b3f35f5bed442fab80e766eeba739db3abadefa3c211a3856f99c6acd285fbc5e047fd43e1c6ac549dd85835f2d648556ccf411cbecb3a87d3af55e1c53a5c26bacc26ebdfea3b97a0ebebba1df74befd2e926ae745b5f2d95723b4987d3a81d92f6a2d8972ffd73ac72df873c18e5586adbe3fb9e5130758f4ceae53508609e58816c043506138a28a72371ba11bf147cdc94edda168d0130705ccbb211ab874c730fffdcc9f69955a80d9e029eaa05e9f69fb74e6e18f93f22cf2728138b32b5ed5eab6be8e62f60a2f83ad4fa1a78bb18cda934edd199faa3c9f6dcf51d987e7dbb1a6a572e9dc23b5076631f7c9e018ea6d9dafe0bba74564a35b180d9cc211bb96daedab8073d4fc61d93b50892bef4ddd3de7997bd8e5e6e1b7bad707ec0f8ad971098ea8ee043dc846319699f3c7bf4f7bdadf6a7b8e5bd6efd8dd3a3f1afb18a28f57cf5177590ad12ee4a1ba7f58b5eacb59ba45f0c71fb2b4797a3d3d9f799dc267b2e497203e730b7a0bb67a8ef0ca9b8976e68e48f94b5ddf97bc9ef3c769f93b3fb57e8257a177f93af6dedc3244d7055946ed7beb1e79dfcbef46ddf8787a5f7d770c3c1ccc67bf9a3fbb7e990f7fc83a2d1d0d4c41bdbe8b9973ab8c3ff0dd257f0f22be42d620e1d3e35379a3f2e4f433c791366d9f8abfcf64e16f37c7dd53badec43d5fd8bf7e2bcea868c0fb76c7b26115a185fa1dbd100dbc92c2ae7c47dbe7404efd6a613ba837f4eada8518ca2afc6ab990b51f9d250ab1e314fab85bd0dbd87b6a9f195982119a922747fcd9f587d66141bee402c6d3b77b242ef58ae1d657a2328b614ea69ef06817c343f6a1ded1aba20159cde221f207ecb3efd7b6bc7ba3f7f7121db56381e6eaf67dac76bd55eff669c3cca32015d6637ee63e00b373652b137f0efc0af78335f74e5efc55aba9e73bdf85acefe9bd58d4ce912a9fb9181eea1dadca383bcabf5f6cc89f037f3fc88ea3578aca4dd5ef4c6eeb8ca342661d6a7723b5efadfbc0b7e534f150efb808dec93d8f57e174fe04dc3f08b89c3ac13a3ba8ab5fe5039d4b3715e5cd9fc758e061e08c60967705472c669db901aaeeca8628a9a784d7d89b68993ff829f39c5907309ea6faedb76dbd1359212aac72d0847ece9f7d16c343da9bed34e7fe88a9fc5123ccba0fec2cf6e97790374547e7784d0a89f85a94f9d657996f85bc958fb244715d7fbbbc9005feec060eaba5f5b1d506f677556ed75a3b4426f330d07b85bda87b5039df509d51d43b4e34b3718fbdad79f837ce2f0e34870e200adab392e201f53e1f75efa690f7c499cc37f5ae16765f0c0f693f3f9525bae13b7a178bf23d863f8150ff15cf814c99a9b60a51e1867ec3cf44cf92abebbda13af14a304b54ed19552d261a50b1b5cae954f609dc70841db0d2f20cadc8a29f3f9ce86c857e51d180f6dcea6d59a0c24544a56af60b637e204aea5e9eb68f40fee8e205b9dfeeb35ed1996f65fdb45621ecc7b717eb0ca5925af32d86873a8ffb4565546034f077549f0fd44c959d16338a22e542e7acab5c9d9762545867139575565ba30815c62c51bdcb9b3ac7e69c87217f7c2a813f64c5ad05b59c91fed5aede13a69079b79ae3d1d371c3627868d5294bf44567892eb4cff9830aad46983d2be24ac4b5d237f5abd22d26e7426789ea5c48fdf4faf63c6ce92cd117fd1459c27eb19a3f41c81d5d336fbb7539f601ecc7bd2acbc5bca11aa2d201f5703977ae7e4601152eeb2342bc94cc43e48f1e612333ca8231df34ffa2a53349750626cc2d0b7937aa7e0e563d6d3028e24de9c8431dad63de69c69c4cf327c21d5d3f780f1b446c1752bfe95254d5ce9185f4b1ba6fa3b244076ad7a6225458c71725fd1459275f9628f1c75f2cfc0de619f97f59c5200dfddcb97a4ead889c0bfdd658b5f7a57adbf0ba3917f145e7d25da92c5175273befdffdbdac3f4130825aad05e5ea2fe06a077ef55be5a6e67ca626ab8fd5bbbbe18898f956d09caca6dfb67aac76e35b8f87f1fc0106c53e1db96019a5c27d985956d533b1fa6d16db5e49cf2a03f7750666face33f979a8560cf5b387f84cf3dae788fc519c015d8ed68f696fdbd0796aa76a4f4db55f57019669a83df4e03a546cfd59c8932aa8c24db55bac38db3c4b34863fca3a7ef064f59d44cfac18cb3ed3f23ed7283dd9d0ab4965bd4b6711abf03dabad9e1056cf77e89cce2dd77f96b8438595524658ab98184467bea9a7886bf3fd11b72be25a54dd07954ba7335c0ad93f218c7b46c41d5dfbfbbc90acb904cb84fb2316b3eb2da9f0a968ab5db3741e47113c6cfebda83ff3f20c71f45df208dba298ccb72ebdf17dfb6336988a20ebea39d8edf6a58e587ba6de94256fb47d46cbdc212b8d0b7a9a24d2c7eaad6e6a7f449ded5a446eaacae9ac8bb67abaa9a82c51cdeca97455b6ebdf73fd21de44ca837356d47378fa89a9a9ba6b09aa5e486eaadea7b3a972e9d475143627d3fbd680ef39411e127f903bfe92a71f05d96f1bcb53602ca99deed43e074565608a867b24d47af3493159a22a5b192ca39e87bd8e3ebb14f2e739ae58f7dbadcbe9b7f78ed5fe884559064a984b579065544e671b8ed88d7b5774c89f450f4f16521a1dbc6cf6cba0994d60cd2545204558a62efade89385dd827753bcb5c507e52539f638cb5b57d56b913989a5faeef0f742e9de38e41858bca4dfd60f79ee339d22d883350202a7df5661095a6be455bdb07b9133bc2c4f31a79412dfd14c8147e15b3380a613f68e6a9ce2d2ee6b92f9567389337c12be5d2651c33863fc89d37f8ef73f00ef524cfbbfdf4f34d2fde4456b77b4375f4986ab764e0e1b0a03b923ae34f4c140f61de98f36f883fb1dcc1c2baa9abe02ddce942bd45a0b09db97afaf9a6a9d2cc62eec7eb3d3edac043f5ecea5ab35fe28fe14c409c79d61f55bfc1cc23e5b972f0b42acb708d8ca3f42bd16f2c7c71cb30873a2be68903bd1f5f55ede6af57aed7fcfb4cfebc07cffc327984e9f7f9169281a933df8610430ec520f26ed4ad2ca3ee78602edda6ab30c49ff788de3c2f177f9f0d0bb0406251efb3554f99aafde58b7a0a5cef54d850fb726d979f94c89ff7b0bc810679c5efdc807dac32dfd4fe88b21af76ed4f58bca03d677e4db52ed68b7755e88b6cf82dec49459302e28e7385a22fb2316b3070266898a6fb5dbe8e63b22a5f027ca9977b218d60f4c1668999ed91f917691dbda323aabadadd77c4ff57decc2ce35e44f3c6fc2c29e8af14f2af34dbfef67aa23d702ae80dee5d9573b7f16f43cc6327fde23dc795ee0ce5b588fb7cd2051fb2302fb1bee83bcd1591c5b9fbd8a05f42e4bea2d71c54403f9f8338b29cfec6ee35fd1fb23820a5f415453880aeb4c36958376259a45a870267f96f5e62de2e9c942fc6d932bd3996f2a97ee54bc14b5231268d7503464497c17140de4e14f1c67b0bc51fd60e57893f8bc28f6832e94d56e3405cdc95aea88948159d03a734efe247066b1b0a39c4735fb237e8b2bbd97671179f07581bb7d5d8a41f12a1c5f548ca9ed93cd9d99ae3fb2cf4cb3bfa332f836db1f318e87105b777007aa9f52e195d26335d9b0fa817ebf43843bc99652e5cb3a4f39aada3903f3d44a7a17b902f640d0fb23aabdee4a996f4a2faaa808a4197c07756bc7fab00631fc8972a6111cd2bfb09e24f79fdea7aa2b3a05e55c04e0b9310373f0f32a4cbfa972342f9c9ed515636b600d5897ec93c59b676d29551f89a477aeebfcfa62f28fadb2569a86dec1e8279fab9e9716abf19af5eddc08669db04765192cc49fb7087796398335161505fde499d6c173eb772cb1fb6276e6ca2e103d35f97d700ce3e9046c0356094ed03aaacee44f231c69ba4ecd5ad8e62c4ba0eb3a9f59baebbe297de3d26243f8b56736152c389d73265a883ff19c59e4ce2c684269153a53d567a9f7e92ce19b117f910aeb1ddfacbe73e35c581f518b18eeb02ff8f738853fb355eea09777f60abb8296de99ab0f9fb27e1b7861bb8aa49696b5679d0767560754f8248e332bfc49e38ce18d2987419b0f0a39cbb27ab31bee8ff88b54f88cb94c3a3df6a454389933ec8b754dbdc09f460267162dd709ba5b9d6543ef58705ae4fe88d9c55131d933cc007616fd532efec471a79950cec192cec6731fb5a7ec25cc2cf5fe88853c9b9ce73787a2ca66309e18faa765bd41aeac7227d49f65ceac72e782ac73a1718f8fd63f53710dfe692abed5eef2bf4a85c53593ec8e4dad9d79c4b75ef9dbe84f92de206716ebc3f5d682d88718ba0fb83fa2cea5fb059651d13c3b776eac8eb2cb2a6792f426567f92f5a619e1cebc3c07fddcbb9994c595da1f91de20ff0b2ca33247ac73501a6599b5b466c98a635542fe6471e61cec644a33e8d8d9abadb43f22bd41fe575806a24cd90d2e61fec496b5261a13a7e98daa559ba9ff8eea4fbcdec4a9f4733048f53b6dbd974343bdbbbba03dc6b24acb1939bde05359863d6ea63551de2cf0278d3b51decccb2cb882282bf96cdb949bfa8b6261760c4ab303fd7d1267993c9c89e34e447fd2f566d97217dacba7ed10504cee4f7669f113eb14bc135b55e1ed78b3c29f34bdc1d25ca80f83eb9fbc6f905d40d3be3d3fb889b7cc3a3e6a9933f33ae44f1a679a21672ec25a8fb0c267aab9b586d7824feb438fa69588af18de2cf0275b6f9a2bf56df0ec3dfc7aeb886beb34b8e307a957bca68f4ae24ef0181c117f92f526ca99c5a218f453bb02c670063ee7ee55706f4653713e6ab5b0afc00b5ac08121e94f5ece44cb79500a767e8d65c419ff10776abd3c8d3345e88db64c1bae6f089f5db24f928f4ae68e2a30c2d8e98fdea5534f51d5d979702725c6c13fe3a342ce8c83a3a01394c1327b689d608ff427993368893967a2f579a09eb3fa21cb381079079ff225596b368d896338330e2ac17e704056a986f5de127fb23883d63205e61981b24fd19cb9604369f3b19418ef6dc79974de04637f08ac698eac3967a275843fd97a33af4d698c5881777b5b4ec96907df3240cb64f9a8edf4465966c4218e3b58e28caaf7147754aded7391536fcc488bd61d8882b6b78c7a32ecda3a74f6e48b359dcf9f7e466f3467bca037b2b50aef2d73265a883f51ee247126ca9df9a7e3ef6cf9ec558f9db021b746ce4858959fe7cca27f5ad69b903b5876893fe97ab3cc19532b4bcd4632e59e6a96e7be7046ec42c5c230a27e94339a371e28cdb356e1610c6756b8a3ac15ea4f3667167913619073b6c9be9bacc64fd89d6010d764de65d9d6478d5c509aaf9117cb9905bd897227f45fe93e6a9533643518bfca3e87237fad7baa2d3e1b05fcdeea089827e0fce9e76262edb9d13f0de993ce19e28d29da3ee93e2a8937e16734caf986089d6b6a1d320191f0347b66b99dde90653a6a96b0648114bd59ae97f813cf9965eb2d7f9ef5dec8192aacde9c6d7d8f1c81b3cb939fd31badc295a0472a9ce9a36278135a2ec29fbc7a839cf1e85f07eaaf462c6d5740a7cd4ff83baa705ece6ca637649936cd12e239335ce24c1c777647632cc49f75f426ce5a0f811d67199571c46bc119a970b85ef31331b19e59827f1a7911adc9f6514b7ab3a4d4467f726b8de10e9cc32df491a95b815cba47d102461d5adf5657cad163be55accdd789f51c617ff444b1f09a3e2a5a1bde2cf0278533b17a13f7b9e2a10f1b3df393d124188831c5c285f9a855de0463e5b96196701b72269fdea470666ebdd138c29f4ccee8cf9c33a83d652a1d18612dab377a09582083aba00de73dcec799cd7c94f14f344b58b640b28f4ad59b58fe24f8a864bdd1add64299f9557b367a1d49f01bcffa1bcd605c14679679a3c7530b7ee16081353963e214ceeca2d5c83a7b217fd23f07099c090bb496c12aed603f3857ffd6f8363882be2e3eaea980653ae15acd0f70665e46667d3e878f4ae0cdb252df8ede420b3de719616b688d074af3acfb6853cec473678933510b25f127c64725f026a5dc065e313e0ae64fef416bf434b246d31c71cd9a3e2a863733c51d552bfb14c0190bad35e70e95aff811b6b67f6ac6f8a7ade29aa88f5ae64c127fa23171b6dea496375d0f83437fb8a9de68ff049e3bc13fad398fcaab3751ee8c3eb2f4398e37c97a13530e5647583eff04acd1fe69f4b9665c93eda3163993c21d65a9d03e0705e88de60c7067a8b953d6ff6ea93192ce9da5b8a6429ebb1c6b8135e751ebea8de24ca44ee04fb6dee4b0148eb0a61a61f9783372613c7516e64f05eb4d3667426bcd22fc498a89f372a66c38b3c41d5d431494a937face5c0bf4ef202316fe111fb5c49db1aea92cf26785376f397c54fc289b974e304ee54d25c63f153e8fcae24e9433cbfc89d79bbc3e2a8e332177cabaad12af3778672e585ddffbe198384d6f42ee7c6141fe6ce5a3e279131d71df268e8ec435153db33cd0568ce7cd363e2ab7de24f1465b2ca23f9be94d126716af1a74651c896b3ced9fca7094698205d6f351ebeb4d126722bc89e74f5c4cbc266f96b9a3cb88d3bd04b55e7ea0db16e39a9fd39bf5798316c3fa6bce9fa2f426f6d3d2eb11079199e5fa7a53584cbcc29918deacf2672b1f15c319b2da10b9b215673658b7599b3773cea8baaead3333fcc9b4c45a7ab3fcd13ab37c67ae807b0b5beb4d026756f953808fb2563913ad1378b3ddba4d413e6a9933510bfdfd7fffe7bffe1f92e4fedb</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/addaccountwizard/addaccountwizardpage3.ui b/kopete/kopete/addaccountwizard/addaccountwizardpage3.ui
new file mode 100644
index 00000000..8d2edc1e
--- /dev/null
+++ b/kopete/kopete/addaccountwizard/addaccountwizardpage3.ui
@@ -0,0 +1,153 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddAccountWizardPage3</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddAccountWizardPage3</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>356</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Finished</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>TextLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;h2&gt;Congratulations&lt;/h2&gt;
+&lt;p&gt;You have finished configuring the account. Please click the "Finish" button.&lt;/p&gt;
+
+</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mUseColor</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;custom color
+for account:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use a custom color for this account</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Account are often differentiated by the protocol icon. But if you have severals accounts of the same protocol, you may apply a color filter to that icon to differentiate accounts from the same protocols.</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>mColorButton</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Account custom color selector</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>101</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>PixmapLabel1_2_2_2</cstring>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <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>58</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>mConnectNow</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;nnect now</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Connect right after Finish is pressed</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, the account will be connected right after you clicked on &lt;i&gt;Finished&lt;/i&gt;.</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="95299">789ce57d5973ea3ad7e6fdfb2b4e1dddbdd5e58f84904075f50564820009848c74f5856cc90364200364f8aaff7b2f692d19039e0067effd9e2e4a47fb3922c65e7af4ac257959feaf7fff75d7ebfef5effffad7db3b7f0f9cbf1c9fbffef56f317d7cfcfadfffe77ffdf7bffeded9dbfd6bb756faeba05cfe6bf7effff1afbfadc7bf9cbfd86e696fafc2357ed0b8b65722cc08537bffdde07dfc7b4eb84ad825ec10b6152eef84ed4e88f1f892b0f9be0831b67b882b25c2cf061f94351e11ae50fbd8606c77f5f997e1fc2afaf8fc21c4fafb421076103b55c415ba7eb663309ddf5388f1f77c83e97c3e099bf3090cc676764bb846ed1383e9ef5f426c6b7c4c5850fb7b8875bb4dc7dfdf411cbc2abcb7b357dadbd1d7ff4598ae5f5e1176c81e2cc4fa7c383718af5758888d3d9ca1c1d81edc13ae507bd960b2d75188d1de2383e97ae8fc8c3dd8bbc1d4fe1a62b4c72561638fb710633b9d1fd803db4b21d6edecc6e0aa6eb77dc215b2e7bdc1d82e0cae21f6f1787c6f6f4f9faf7742b88a583c198cf6f5f7083bd8ce760d26fb7f8658b7bb75c4606fb4ff6788f1f83d83d1be927edfd85fdc194cfd771062dd2e25e11af5cf34c4badd367f4ff6e7e6ef05f1b56230b63b7e88917fa788f777a83f1f0dc6efcbd710637f74081b3edc8758b73b2d83d1fed63e61c3976b83f1efc5b3c1d47f5f21d6c79374bdfb02b14fd773b08338e006d7f63476095710fb9a6f95dd72a954d2e7778038e403b59bfef566884d7fbaa721c6f1726d30f687f74db842df9f8518bfff6630f5e70b61eacf910831f6d70761eabfd1a3c164af3162d35fd689c1d41f03c246bfba21c6fe730893fd837188d1befb06637fb007c2f4fb2337c4687fcf60fc7ef014626ca7bf37fde5ef1a4cfd43fd61fa2b102176341e11ae51bb1f626c7f272c10fb5aff2adcf4b7b747780fb1ff45b84af831c4556d5fb467a8170e1d2fd45bfabee187d33498f830214cfdef39846ba4176d83f1fbee116181ed72cf606ce7e310633bf24d8d57d4f3bb10ebf6e0c660ea2ffafd7da3e7cc60eaff9310a31eef10267eb0af10637f3e1a8cfdcd2f09131ffca1c1d44ed77760fc5dc5606c1ff11063bbf97e05f188aef780f420f83298f8108418f9b04f5850fb7388b1fdcd608eed0dc4d51d6a9f194ced17842b887d7d7efbbb217fdc106bfef867841dc20c31d04f63e7de60f4f7de98f01e62e18518fbeb9630f92bd6430c7c42fe9409135fc42b62c30ff725c4c83ffabd7de36ff6438c7a32234cfdef5c8458dbdff3091b7fde0931eacb14b1e96ffe6630e9c34788916f5784e9f759c960f2dfe510231fde0dc6feb5ee08937e066d83a9ffc95e211f5a21c6fea6f30ffbffd060eaff2661eaffe03cc4fafb239b708dda6f438ced166141f8cd605b7f7ff489b8b683d8a969cccb4eb9a631fe1e377cf19f0813df3cfafe9eb383fcfa3298fc19da07f46857f3959f1a8c7c728788437da813267bba5706537c4ee7b36ff4283098f8d0451c8ef74fc2c6ff3c194c7c9810367ae28718bf7f48988eefdf1b4c7ad0475c257f327a0831f667c960ea1fba9e2af997119d8fe9bf911762fcfb03c2d47f41d960ecaf80ec67fa6f340db1d0ed7b842bf4fddd10ebf6d18c700db1a37fffa06cf4c07f475c29eda0de5c8418fbff9e7005dbbdb2c1d4dfb6c1783c59456cfa4b4c111f90bfb00d367c681b4cf1e06e88b5fddd5bc2a40fce8dc1143f1c11267fc0bc1063ffd51157491ff87188b1dd3198c6ef35611affa391c1d47f92b0f10774bda1bdc784c9dea30f839d8ac6743e3541ed5f0663bb83d76f97ab656d4fe788308d571bed6187fa5e415c11d85fac6930b6db6788f74dbce011a6f1e53e2036f616a784c9de828e7740facb3f0c267b8b10a3ded2df1b7b3b7b21c6f1563598ec4de75725fd0d060693fe5e1336f391a1c114df55438ce38d1126ff2b74ff56cbe53de4ab731562d4bf57c2646ffe85b852417bfb1ee2fd1db2ef04f1c10ef947df60b2678930e9993498ec252e111bfb04738cf3911bc2a45f6c1662b4cfb1c178fdd2266cf4ecce606ce7747d55ea6f7b6430b6b3d3106bfbf10ee21a5d8f13184cf6f50d26fbe2f9d865d20be7ce60b2f73e6163ffcf10239f6b84ab88bd27c49512f98f6fc4fb3bc85fff8ab0c0fef2e9f70f76f0efc5036133ff7b435c257fe13d1336f3f9ab1023dffb84c99ed6aec1682f41e75325be3b3d83c99e74fc1af1db7b08b1b61fb3099bf590db10637cd13598e61b3b06937f217bd5c89f08dd3fb5bdd0fe8706a3bded2ae13d6a7f3198fac30d31f6c70de12ab6bb32c4badd3d21ec507fbd22de2b617f786784f710bb1f8449bf3cd760d2af4fc4a6bf5999708d8ed73798f8503198e28f63c4861fee2e62d3ffde25e22af90bef2bc4d8df25c2d47fa28bd8f41f3b0fb1ee0fd1206cf4e93bc4e80fa60663ff5864cf1acdc7b817629cef550dc6bfb7fa06e3dfb337c2147f073b06131f6488912f35c47c07b1a3f5b4e698feb6f70957910ff681c1d82ead10a33e4e089bf84f1a4cdf7f0bb1febe6c198cfd278e10ef113fa56730f183ce2fe4cb28c478bc2fc255e24f8fb0f93d3aff4a09dbdd5783915ffc89708df88be3c5d9df217e71c2a48f1ef6bf63f4dca7f6ea0e5eaf4fbf5fa578c7ad194cf3893661817cf5070693fedc1a8cfc73a93f42bdb00913bfc44b88b17f9b848d1e55438cfa6d194cfc1913a6ebf35f0da6bf1f8418f9776e30febdc3428cfa45f6e634ffe3f706e3f7ed5988917f6784cd7ce6d5609a8f687de07b864f7605b1e18b7d6d30b50f081b3d7b3698f48a1b4cf15383b0e1fb95c1f87db14798f4490e0da6fe75101b3ec90961d3ff3dc2a44fbc4958903fda451cea139d8f89c7fc6fc4b51d8a37ce0d463eb99f84cd7a638b30f1c7ab1a4cfd593698fac343cc8dffec1a4cfa766c30e9c97788757f499730e919932146be1c10e6c807bb6430cd676c8369fe42f6e392fa5ff3853ba6ffec3e61d35f970693bf12848d1e3d194c7f8ffde984f39716619aafc863c2a6bf1cc282da0706a37f61743efb145f48c760ba7f42e7bf4feb6fb24998e26f7e1e626cbf3718edc5d0dece418dfa7f8ab866f46948b842f1ce09613a3f798a98937f72ef0dc6fef276091b7d9221c6f5b40f83893f1d83c99f3408738a972606633b7f3718fb9399df97a4171706633b1f21b67769beca428cf3533d3fb2f7c2febc406cfad3ee194ceddf842bd45e3798da1b846bd82e1e119bfe7477080b1aff6d83e97eca3ee203b3de7519621ccf7b88abe4dffd67c49cfacfef10e6148fdc184ce3fbd160b2974f58d2787d3098faefd0608aff8606937d6b888d7d6537c4dabebc4f789faeaf61307edf3a0cb1febe45d76773c4b6d6175b84f63d274cf6751c83a9fd8930cd4fedaec1d8cef0f8c2ccaff8b7c1f4f75f84a97fbd23c2e6f88784496ff98dc1687fd6426ce607f2c8608a37a684c93fcb3162335f9075c2343f967d83e97ee13b62be43e3fbd060d2eb6bc2d4fffe29627b97d623ba0663ff8b196163ff2661b23f1f1a8ceb05e28cb0a4f61383b13db0103bbbb4bea07f1fe4d0d8ff12b1b1bffd6830e9e13361b23f170663bb27099bfeef1a4c7a6d8718fbc71c8f7edfb6438cfe32c4a47f1f880f0c1f3a0663bbbc234cfa289f428cf39923c2f4fbf2d260f2df17884d3c26de119bf9af7b6330cdcf1961d24f796d30cdc7cddf537ce6d2f9193e78fb8425e9fd0b627b9fe2c303c29cd6333961d203f66930f6276b207676e9fed1bec1a4479f06139fda06d37a5410626c2f13dea7e30f43acdb5decbf707cc90ae2038a5f789d30f1c3b93418db99f9bee1c7a3c1e42f2f08131f1cf37db39ef510628cd7a7884d7cce5b21467def188cdfb7e9fcab153afe89c1e84fed63c234ffb4770da6fbf3738cc733bf47fec3ed1b4ceb6d4dc4c69fbb2d83295e32ed86bf658369bd86ceaf66e6c31706137fcf101bff2f0d36fca4ef1b7fecfb88ed5de21ff597cd697e30232c49affa0653bc80e31ff484be7f6b30e9cf8030f1c51911e688472f060be4974b58623bab198cedb68558ec22167a3c8a8ae92f5e266cc6f7a7c1942f7118621ccf55c2c42fdb3198e6ab2e62d33fce2d61b35e5d236cd6233f0da6f5ed53c2c407716130dddfb80e31e69770c4263e730c36f1c15388b13f7dc2d49f9c8e67ef92deb821c67850188cdf77df0893bf1715c2e43f5c0bb1e94fff9230c577eea1c1d85ffe1d618a47f8d460ec2f3e436cfacfaa13de47ecb4432c353f9e0973c41ced2f0dffb9446ce22b7b4298f445f60c26fbbd13267bda8230e9af1c194cfa4dbf67ec297608efa33d9d6fc21cafd73d3618ed257cc2640fb78bd8e82d7b21bc4ff7ff1f0ca6fef10cc6ef7bf4fb0e8d4fd6262c299f686230e9fd38c4c857e48734f6652784c9becebbc1725ff7cf056149ed2583b19de9f10d07377a3f214cf6f41862a32fde21e17dd2f706614efef9c560d2f7b6c1b4ded63298e69f6784cdef750cc6bf77760cc6efcb1a6287e2617b4c98f8eff40913bf050f31f2db264cc7171f06d37809dbb1bf64d88ee343760c46be7b25c46297d6131e0dc6bf779b06933e8e08ef93deb5438cfee5da60ca0fb93298f4e2dc603a1f61308dc72a614efeefc3606c970f21d67c90645fc30f1e188cfcf0881f7297da4b0613bf7a84f711072f21c6efebf98094862fde3b61b2bf87f680f144b84e98becff609139fdc03c482f4cfde3118edcde718fdd53161b2971f10e6b49ee7184cf9575383d15e3e9d9fa0f129a4c1680f7762305ebf7f83589af8bc6330d99b1b8cdfb71961b21ffb08b1febef74298ce875f194cf66f85587fdf6a1096647fade7eebeb11fab8718fd9d242c69fda48218f88ced6f8449dfd81561f3f7aec1c4b757c2743c16184ceb2fd42ee9f8bc6730b55b84e9f7bc73c2a4175e3bc4baffbd538371bc7b8d10ebfef6ccf1687ee50521467d0f8f4ff6168469bc7b8f06937f1b1b4cf63e33d8d5fdc1d11eaeb10f7f216cec8bdf77c3eb3f3498ae9f11a6ebb76a06637bffdd6271c5e2f05f6e6a28764c6dc337928a43b58022a98e2f3614178a5c2a22520bcbcb55e61f3fa63625c82c2328636daf07b8cec7242bcdad654ad46a11eb71b21627ab2cd7a6c8258bc55bce8d2972a1466bc8dcd68afb284b8dc822e3382badf267e9ea633993c21b27526773862f5df53267e4dabc79da8a3386379a3baad6fcc9c99b04cee0ff4be78ce14d3a67e2782397ea2867d2b893fc79a63aca9958eeccf9a35953acdea473a658bd89e3ccd346bc19476a558264fe6cac3771bc594f6f56392372712699378633997ab330c246649f4ccefc32bd59df4f6de7a3bc25cea83a30dc09fdd7cfe84d3667d6d59bcdfc94b15c3a6762b813f2278e333c436bb6e3cca67a93eca30ad29b857a62f4e71feda3d6d29b65aba1fe6ce0a39cb57cd4b67a931d136fc69915bd519c8916c39f141fc56338537c4c2cac6d63e2f57c541c6f5647dc24d49f5f1d136fab37ebf9a974ce048b9c59e6cf1fa537c5f9a88df486ea891969c89f1c7af30f8a89d3f4264d7ffed131714ebd897267ae3f5be94d968f5a7f0efec331719adee8f212d693507ffe61eb366b702658e64cb40ef9f3bbd76d7e0d6732f54671467f5eb0d0fcf43fd547e5e74d8a8f4aac0d7ff2fba83f2dae491a316bf928d29bc9327722fcc9e3a336e14c3e1f953d8f5a7bdd268133b13e6a65e4cd3f2fda3edbfba8fcbc89e3ccfa7a933d8fcae7a3623993c19f227c543ebdc9f651f9f4664b1f15c39bd072da3eff241f954f6f523963bd52fd16f2e71fb76eb3e0a3d6d09be53ac29f7ff43c2aafde28ce58eff33ac29fcde7517fe0bacda67ab364ad77e2cf7faede64f126afde443813ad893f7f928f8ae74e1667b262e26cce442df86e0af1a7889838596f36bb07bead8fda486f748de525963fff1fc4c4697ab35c883f7f4cbecd567a1359b7595b6f229c89d4ef217ffef3d66d8af251b1bc31968bf0e78fc8b749e0ccf67a93e2a39639132dda3e7f50becdc63e6a6acdac0feb730dbd49e44cb426fe6cefa3a2dcd92c26cebc9799c0992fb0ccb755b71ad6a175b4898fa2fa78993b217ffe43ef2dc0e7c43ab59a56cb3a03eb9c596dab43ff3f8e33510ba57246d7c750bf93fefce7c5c4ca325debdcba20cb60e9597d1861ab7af3665d5a03ebcabab66eac5bebcebab78656295e6f12f5e74f5c278e5bb799c0b5ee58bb5679c132a6ec59951885deb70eacaa5563161466d5ac2ae3cc660e58e96d853bef9191769cc99f641ff5ebe7e0ea0a064c3009e369d532ba3097a95f5abce24be6b3802c538bd42336660fc0a564fe94427dfebdeb36d97a73c91ed9137b06156ec55b864a0bacf7b5a8ce6cc25ee6dc59a85fc1466f60a3e3b9de2cab74027ffe989898bd5b15366533abc93e522d43857db2afd067a9f2c1be597d853b919a35d8a11e690bbc81f216f2e78f8c89991a195fec881d6bdf9d69191a6127ec14349afc127b674dd6626731dc89d66deb9a75623d7c69893fd9317151f936297a0357f5ceba309ecec163276a4da2852ed82372078ed2637d76c906ec2a9e3b218794165d136f42eeb09b707ef1c7c4c4ec169466caeed83df8ee352d43236cc84a70a44fb6c3765999ed817d2a6c9fbda673087cda81e6507484cdf9f367f8a80a8ca7aa75ce6a192a9c5e7adce28c73283677b860975c3d99ee267167ce21ee59f78a33863b0bfeeb77c6c49f30121eb90ffea9c9832d2c43858ff8983ff047fe04dc51fcd9e3cf7cc25f12f527d421fe0a3629adf0e737c6c4ca3ff13776c7dff9747bcb60e133fec13ff917ff56dca152e70d7e98c921ce8f903b544a217fb27d54be98789d75e22ff6c58fad737e02bebb10cb5069b1637eca9bc89d90432db0d0593a87789b7762f8b3998fca5eb749d41b5061f04f569f8fd849a1963123accbcfc12222c21f552e6094a5ea10eff1bed6a09229a17db6d59bbcebc49f309e2ed929937cb0950aa759e78a5ff39b2877223a74cbef527d59438db018fdf90531b18e6aaafc9e0f79e9672c43fab3c37757b813165e563be3257188efb3871cfc293abfef931ff0375ee535dbfa49cba862339bdbf62a77e6853fdb8eda9d8a9f2d73c8766dcff69542db81f264ec46dbe7677d14a8b03de2ef9bc4c29b147b6c3f2471078bfd683f71613fdb13fbc596f6abe18efd66bfdbd3397796f85374be0dc4b0d609f7ed9975082afc435ab3629d0ffbd3fe4ae68e2af6137cbeedbaddb00fed23fbd83eb14feda6dd62dff699f2ea863b7690c99f4d7d945661f04f3556cb37ef2e4a99edb6dd49e30e943dbb6b9f83752eec9eddb72fed01942bfbdabeb16fedbb2877ecfb903f9baddbc4c53523ebcb1ad843bb04e3e9e2d75986b467c7de4de70e58e7097853b6f7c03eca3a15b0ccbe7d6057ed9a632167a2232c813f1bcda3742cec3b8c9ff0d9afd19a25eb30873b7606771eed73c781d1d57384e6ceb5231d69571dd7f11c1f3983056c35e7cff67a032aec04d6a13302dffd8bb466b13863e7c1794ce78ed69e73b0ce85239c277be03c3b136d9d17e7d5795be68eb2d2127f36f1514a858fd9051b3aefbfc732589ca9fd09d14d86df02ed51ca7c01d6e93b33bbe27ca8d1e57c3a5fabdc5175447fd69f477d5a53ab032aacc653eff7594615185bdf10f5e4f15bdff69e53771ace73689d43e788bd2f2b3316e2cfda7a035a73c2eeac433e2d6245a280d175ec9c64f8ad4be7546b8ff15ba0ccce04acf302cadc8c7246d54e4bd509fcc99a474dd99373c66ace2f8af87258a76373a79ba93d0dd09e8b88324f94df02ed39c77827ca9b25fee4d59b23e7c2e9f181d32e784562cbe2f49dcbec9859fbad7a44993f803d03e7cab98e7287dd18ee382db24f1ebdf1f8817363edf1198ca7dfa8c271c5fe706e73faadfa82dfda07ebdc39f78ebadb1cc31d65a5087f92f26d3ead236b669df399f39b3c77867598b3e374d3b547fb2d1d333bf5302a3c50dae3ec72e694adcb2867d447d5217f92f5e6d3fa70f6ac323bf9d915896d0a287365d398d9d9770e9ca1530506c47027a23fab3e0a223eab0b51cd1f6c1955d46a46e67c6b296686d125292aac394361818586ca16cbdc59e10f7187ed08e6f4584d47c2bfdd02a9dc19434098bad69316330b877f0ae10c61840dadaf450f6ffb682b6d9fb9dea8f174c387ce1f6f192c420a37db6f25c4cc87c243ee280e5947a83dcb25e48f076370c73ad719127fa00ac7159b093f07774cccfc043173681d116865d6a38b46d89be2cef247d9478cc498cdacf27f0667a8b4acb278108f5931b3f25b919879606266185d4f863bba2e5b5fcbdc11cf649f0928cdefbfe27c45b1fb42bc8857f126deb3fc564acc5c9b73070bfab0b94263adc7d7efbfea9c9c817e6c8a297c66a03d1f196b3da931b3f88c70c77cfc396fc4d79c3f7fc095e7b00c9f8a6f51170de4bad31187ebc6cce2086366712c4e16b983c53a5ae48eb2d01fcf1fb00c3b1113712a9a913b8a2dd14ae74e749d7929663e748ee6aa1cfd5853ab14e58eaaff60fe34b465ce80356dd1598c529da9ddd93c6656ca1cc71fb0d02572475be7f90fe64f8f9d305774445374996b2d65d7897371b145cc7c2e7a4bcc299b621de951f5352f7f1c7f5a3ce03350e1beb8e4b578bfeaf4950532b4e7dcc4cc641db3ce7c0531730c77421ff636e7ce9fc61ff60196698b81b882729d14a5425478933b66de8bc6cce0b75ec4ad8977568bb883115631dc11aa3cff21fc6959753e10435102b5394b5dcb6e899d3563e667a33d30a3d8e59f49dc218dd63e6caed07f007f2e9c118ca7328ca8093bc9fabe52e6cc98f9313e66167bea1d24ab9c59a80fac4bc31d55ff56fef420a63903dffd2daa3967372d51cba13db131b3b424935cdae26e993391ba4c23cc78f9dfc59f16f8a74f311355d14d52e1b8628f378f99c17d0995fd2b652277ee8c0f43eefc1efd016bb4c54cbaa221caa0c76bfcad90cec9a631b3f4a46f33d5333288e14e19b9837538c27e397f603ccdd83d78a72afb5c3bafa32547e9f7475362e61739960f64e5b2e2493c77a23e4c73e897f10794b7c9eed931ccbc73a8705c918ff2493e6f1633cb0928f3391deb42be44f566ce9db094e5eb2fe44fcf1981e79e88013b8619d4a6a3f2cab975ba9be566c837f92e79e4581fc9dcd1057dd8b39c927d7eca322d71cd6bc09763b0cc1a2a1c5784bfce3af362cc2c67f26321da6c8acf44eee8624d6dff67f9c34bca73c378ea8889ce06daea78ce54e6cecd50ca3c5f490565fe22659e1faf25bf13b983458db02fe0cf4fe84f0bc693b68c74ad762177875afaeefa9a3133adc2bfc83af8f5e5637eabbb82f1b30c9aa96a1f26a7c5f2879d404c53569c010e5f17c343d99087f268d398591e8bcaea71f94c9eac72262c07609fa9755fa8fef4ac3270a62c5d5e734685642fb43828baa8ca53d9e4dfb9e6eb4bd601656e2d2af3bcc82069961af161cf85f0075418bc535569308ca742b217808743754431649ff24cad85e58a997b2b31735b76e27f4376395be10d71076b1861cfdbf247af9757e133b49ac5e490e9e85a1d114628734149cfedddbcf9cc601db110337fc98b25658efc8e8e82523448adb66ec19f169f42ff76d575e89ccced4728f010661d5d760cb61e5034d0923ddbde2c66b66f64dfc4cc71455c6affbec099c55af9b00df803ac81a8a60fcad08078af5ec45d45a5c260eb861aa1e27a3ef3b0c7f2326bbe9e18330f44c51927ffa633925749dc51d651770c37593f84c8660231cd80cf8a51618806aae21b78d8e6b3451e4a0e3173f67c3d3e666ec96b185b29bfcd3ec47e3277d087c9d74cfeb4d4cc493f5b125e91552f2887ec424ce48da82aa5d1cfc92db5cb5b79b771cc7c2fb37aaf659dab3bf08b9c41eb989a3da6f1077a53c5320d5112ddd5b3dfaa80ef8611da8719ab9b3442e5b938dc2c66b60fe450fad93dc84ee4c90a77168a95a43f2de682875596b9541f7903d751906580355559125535429373edf995ad9f15d824660665aecb9d1ce7d293bb7afe558ee38eae0f56f9a3c69318805a5e01f72fc3cfa408eb881958e652c5025923d41e3b95ac9839299f593a7131736c2f9ca879463c77b044f9037e4945ac5d79b3601962d076f36f3e030d86880054fd243b4e723ab29cf31ed76accec4996d7a7f240ee257247d7c41fd05c184f2fa00957cb960947d8e786d6e981bf9b8a2b155be78d9340992b1bc7ccfb6a9d39f7b9a9383a813bd694eca362e044cb849feafa3e0b54f8126cd3d1599eb9e324f9982b37233e663e9017eb9c27ab89cf78eee0a853f6c9b40c7eae60469edf3201a830589d4fd7cdf2e457b29795cf9c12338f613eba4e0f063288e38d29da3e79aca346d87dae5f6d011f4fc51462e18b4d7219654d5e6e9a9b211d77cde7a3d907afb92c9e3baace6f1ff894b2ee39800a77455dbcacb3a3d162713ace6dceb59e9598d9e572833527a72df75679230e84253ed9ee3af691375639f657302770084a83bb116e64196d9db1bcdd629df91ee6a36bff26441aa7216f4ca4d893576c575ef1cff5ec2356ef3fb4f4fd893ed8a60cbfb4b165b0b8362873465e5852ccec3a6e7ebfb5d0bb70fe56c89b3bc51b79a5b8a3eab5c6d725d821ca60608d74455b5c16b397867a8a4bd9609398d995721cb3d6a3f216b39faaa95bb3b965f8a7e18eaad7b30f302872471866f243765fccda0f28db85ebf29bcd6366d75b3a660f7ce8199c7533eb2e365cc137444196e14cb45e933f9730532d368b5c6b179f4ad7f5dd20cb6f25c5ccee48ceb9a3f619bd80313370c7ee835428f5f77909e69a65b63be70cd658d6b64fdf1915671de8e16b8806be45d37d749fdce7cd7233a425efdd89b95a98dbd54413bc6835736fa1160fd4bd28b5fee4be28be2c726703fd519f4e015aa3c6e6b5551665b892173e73c6e2c3de5d3766c66700417b5edd37e527606ef709b3c72acc80db199188e698d5d6592455280d71aa78b3c89d8df80351d0967eaac54ea0d7666afdc744034ec77d5f2f3743f92d8c99e59b3b05ce0cc44496c08b4e3277b500ad13d760c90158e6252c7d775688fe5ceaabdad03abc04a353dd9fb864c791fd0d5b10333f65694f52ccec7eb8c01ab04c29c7c8575a37930db00df2665ebe45ddfd5ae4ce86fcb914e50deee4b4b08f813525880616da9ce9e631b36cb9dfb2241b39d6c2416d980bda545de0cdbc5cbaf542f4e7527cf3e93a5a03f14519faec05a283989db56c2676d2f77b4a8e99417b1aee61f639b00f76a27ba7bac29b79e9bb4785e8cfa55a37ce671d359e2006e908b59e3a8bff0ec4ccf9f3992f96d799dde3ec73003fd6d6b689e78d295d8806567cd846f6b9841968960af67416bc5ac3eeeabd8913bee774642fa7df5a8d994f643fd5976a6f2fced45dfc14decccba57b3ae70e5a6843fbf4ad768a0a072a0333cca54bbd027b2c9f368e99076e8aa7e081ce763ccee4cdbc94c43e2f427fd45ad0716c1cade6aa2a37752006a0c2d9ebcc53b7953f665eca67f6dcb324ee2ab501251ee4e4cdbc348d75b6e4cfa5682ce98952e1a6ce8b1ac01c2dd7ce5ae25c2bf3a631f37eecfd5195d3a79e5428e7e4cc426133b75d88fe008364233a7280371d38a789cab9c8ebd7e4a3dbc9e9b7966266959be176978f07beb1aef3f1ab31dce80ab512decde04fd73d573eac00fe5c8941f48e0f9fb2cff5ee00296596cf39d67a626266fbc65d5c858798583dab00233b8e177dd19655f75c2a2b657108a220e40f968deda366f2f1efa0c8574099377f06d0ed45720a1b6ab62e1be15c6a810fe214ac5277cfd51d266d9d2c0deacf67aa5bf147ad050d379fa90ae9f633e75bc9f9cc46df202686592e7aa9451ea868742aea609d29cccb4ab935e845b4c527ff44fe6c181faad1d565f730dfd9346ba1256f73ecf794f00ca07ba9e329506298a7df83ea2d72e005e652a7a2ae59730a566aace9c32ed54c7563fe5cc168eee83be85bac1bca4727c7fdd1f933803a66bec67c6677a0b2bdc535c47df74b737054616d19f74129c0263ecce9b957ca32c8a135ed53d5d9a927dbad21daccbdde34661681ab72499a5a8997e64f62ea8ec1360f42adb8adc79905feb9b74bfce9e7b04c49e58c59f5ed73c678c9bdcbcc294c8c99dd57f71e2289c5981854d81d826da630aefa397c5456b9927b3caa3fd50c1dd6195e05e48cb5f840deb825772747ccdc885d6796eeee625fc3f1cedd32a8f0299ceb375cdbe6bc89c64ad3287f20124fb24e570cf578da3a739707a0a36df09e8feeeabef0396366d09e8abb1f9d6fbb47ee50dd5f02d6bc6cc999c5d2746773fd81d964699535e033cfacba8ef9b61b512d09f31ab88a32a8fa1446d7815b4d7f8e2b69af39f7c4ad191f235edd32447c256077564cbc49f916afea3979bcffce4ec01651ebbcf01a9fe9ec826db566a62276b0fe0c62018a593ccb7ef318bfc8173347f7cdf0b867abe3798e3b26cb14c9996851198077d6d4c2fcf016ccb93163ac2f5d3e60ea7d145b6a0d2f81f705bd942e44d90b19989e608c1fb27dae12bdd688993dd7f3dc23cf81a32aa5c9130b6f54d8b1546f18a979bebae3aced73a673904185f5782a4085617c5645496534affa3b8fdee5c6aebc112fe78d991dd71b83d65c16e29f92e7efc710716a76f092f7a018a4ed83ebb3257da771ab11c54e2062ab8b36af25fb3bef317c67c0abfdc627bc9e2766f69ebce782fcd38b38f5262a965bf8ffea898fb2b601f532bbb03eace29effd259511083549d110fd2eece792f0bef9b5023ed16f3c562f2992966f65edd1a68cff61cb97287de1bf4e0e582bf2b8ba17eba66416f9d9173b398bfba616968153e52b93f9a85191c04fe2cbfb3e4ca7bf7a6f2393966f6f8d67af32da6de0cd4eb546bd79c8755fd245f5c9e5b8b57a5dc8e3ffa4995ba7805ff779d3712f062de25c9cfec377be27dc43f03e87d7a5f5bf1e6d273bc199c675f5b26fcff4a6dd2f416ae8d6dcc9f16073fe27deb5daf7aebe4197a56c2bb930ebdba7d0cbe2b2e66de5477be45dd7bf31cbdc2b1700c760c114796de5eb0bb4df8036a7e0c31c891bedbb7b6a27b8de4f76fd9afdea177b418337bc79198397fd12a0c5a33d5aabed836d4194139ce951dafc51f95c5d486717c22fa10d36c382abdd3d477b8a9e8b1e9b54c3eb37716c6ccb9fd13ccc986708eaf5a85a36d2f62201bd61af9d1ec33277f5a2a02102f6a146f9e9d40fc69a7bfc34d7dbc8e3c525133684f17d8939f33dfee83f70ddebbbd145b57d588d22be46baa48267f74e69b00bb80d6548bd867d33bcf7c0f20fab40bafe7f5bde53825bee09346afde0c228cfe4a7b5574acb656c9b5cf37953f3af34da8fe38d5bbc86d6919656d6fe05db1abf93b93e26bdef3aebd1befd6bbcdc199ae5a1903ad512abc1c5b574599ddf3e9e6ef004ae00f58c6aa4b57f96e882b8b78b785da1bb4ce6b30bbbcf3eebda15762af89dc09bc1d6f379333ca326aa51958a39e6488f94e59cda4b69b65aff24765be89337105962925e55cac57a0f7d4ee6bdf6e5965027b656f0f3e156f9f5dad72871ff2b677e065e65a0898bfcb2a8cf9573d53586e1fa835f222deaab5c01ff5ac695994d5ea76ae8ca3ecd212d70c021875a7450c350f7b5ecdb7bc3ddff299cf7d5b8fb4703ee63bbef065066fd4cac629447c93d8fba1557517b7b8774745f9c33ee0e8d31c799ff98ad22e95655817dd793460339f29eb680bb9bee7fb7e80dcf147fed87f809e4fe64d573f6954f7263093efc7cfbf798d0ff27b1195ff92be5bd3227f22f3d72d0a66ddc0954e656939f7c71bf88f681d2a4ffe337be53d7fe2bff8af29fe49dd4398ba653d838afb4e55edfcc1a7b9cfbf057da6b2a69a2261078a55fe1450d4086db37b350220de88e1a1dcf1dfe6d681ffbefb53efda9ff91f89bcf906d69c026b1c1d0bc7acf5a91d63f4fe16b99faa54f75cd58c552b6cea77ffde7efe3ee79eca326ca87b2c89b93f0dffd3b742ee7c7915ffdbaffb495aa3c6d3a9acba476a3d35610e3660f7e22cb712f7f45e9403600d3e6994f93785f007e6aa308e8ef51818a62ba37f48da730423ebd83ff14ffd660c271a3aa7fe0174e67525160e79a3f677819ec8a7c40dabce3e613c9588d939af6d4bfee03e8f1d7d5fb79a23f7a7e1b7b475ce80396dbfe3c7dd7b5091f0a98ebceac93917d2950dbd2f4a9ef3bcd0f7e9d59a7b69ddb78c6ece1f959baab303fa6245859347a17f0ef6b9f07b7edfbf649fc0f126a86a0d8e33d4b967df2a075e65a4c4c4c273bd9928b5c91513f7f8148eae987da59f345afb3a37e28fcaf5aee9dd6dfa6a656c8d28ace10ffc2bffdabf991f4b2b97ea617d15ea6eaddec5e5cc6aca06c4bf35b5031e7c3aa2a3f6c163f760d53c7bc600b32927a84fccde8805ebf247ed4ba733534ba2c107ebcf6bfc5bff6e71d719b52392ce3650eb34670b4f23b500f5208e0a20a2bf863e99eabb68d9fb6a7c807f9a687def02d3b67a3e6d0dfeb4748ea1cac9ea027b36da0373b1e89c38ba0ea8d7e161e23942e435247d9f14f196d17cfc6127c0723d57068617b25389de1109679965bd9fdbd6c7d44f0b0e74945db60a7bbf5d267f7aa0050dcd1a75ff30fd59bc9c7d0cec1f90cfce99099c517accd56ad855da55ecbb9ed3f8c367e657751e7c01bfa6df64f1ad2d3329666d8007e0fbf01c3b108515fe66a524fe0057d5ea4a55ef295bc4fa4f4fbfc9a2a4229782d8df82c8eb05cf117cda0fbdeb39893f2aea83f154c8af72b54f785f74554c57d0da401d987da5720974a6c9f6c76b6966c7ec7194c49f62de1da7b338ae74943401df5c04fb7b3ad7a40f0a36b4ca45f0108ed1d46b4a03a1fe4f4efe6c5d940aeb5987de99ae9077ecf2a93e62033cf8d9f67b356816ccf45c55451767f1fbc526f167aba2d8df84b982bbceaa43aa6502b552037ddca115e5edcfb10711cba97a0e56ccc0fb25f2b060fef4d8a7ceb41aa88cb1625418e28b3e5c89ce2c2ee65d9a6abf10f7017838d3f7df53bf5b187f54e48a6b3f35f5bed442fab80e766eeba739db3abadefa3c211a3856f99c6acd285fbc5e047fd43e1c6ac549dd85835f2d648556ccf411cbecb3a87d3af55e1c53a5c26bacc26ebdfea3b97a0ebebba1df74befd2e926ae745b5f2d95723b4987d3a81d92f6a2d8972ffd73ac72df873c18e5586adbe3fb9e5130758f4ceae53508609e58816c043506138a28a72371ba11bf147cdc94edda168d0130705ccbb211ab874c730fffdcc9f69955a80d9e029eaa05e9f69fb74e6e18f93f22cf2728138b32b5ed5eab6be8e62f60a2f83ad4fa1a78bb18cda934edd199faa3c9f6dcf51d987e7dbb1a6a572e9dc23b5076631f7c9e018ea6d9dafe0bba74564a35b180d9cc211bb96daedab8073d4fc61d93b50892bef4ddd3de7997bd8e5e6e1b7bad707ec0f8ad971098ea8ee043dc846319699f3c7bf4f7bdadf6a7b8e5bd6efd8dd3a3f1afb18a28f57cf5177590ad12ee4a1ba7f58b5eacb59ba45f0c71fb2b4797a3d3d9f799dc267b2e497203e730b7a0bb67a8ef0ca9b8976e68e48f94b5ddf97bc9ef3c769f93b3fb57e8257a177f93af6dedc3244d7055946ed7beb1e79dfcbef46ddf8787a5f7d770c3c1ccc67bf9a3fbb7e990f7fc83a2d1d0d4c41bdbe8b9973ab8c3ff0dd257f0f22be42d620e1d3e35379a3f2e4f433c791366d9f8abfcf64e16f37c7dd53badec43d5fd8bf7e2bcea868c0fb76c7b26115a185fa1dbd100dbc92c2ae7c47dbe7404efd6a613ba837f4eada8518ca2afc6ab990b51f9d250ab1e314fab85bd0dbd87b6a9f195982119a922747fcd9f587d66141bee402c6d3b77b242ef58ae1d657a2328b614ea69ef06817c343f6a1ded1aba20159cde221f207ecb3efd7b6bc7ba3f7f7121db56381e6eaf67dac76bd55eff669c3cca32015d6637ee63e00b373652b137f0efc0af78335f74e5efc55aba9e73bdf85acefe9bd58d4ce912a9fb9181eea1dadca383bcabf5f6cc89f037f3fc88ea3578aca4dd5ef4c6eeb8ca342661d6a7723b5efadfbc0b7e534f150efb808dec93d8f57e174fe04dc3f08b89c3ac13a3ba8ab5fe5039d4b3715e5cd9fc758e061e08c60967705472c669db901aaeeca8628a9a784d7d89b68993ff829f39c5907309ea6faedb76dbd1359212aac72d0847ece9f7d16c343da9bed34e7fe88a9fc5123ccba0fec2cf6e97790374547e7784d0a89f85a94f9d657996f85bc958fb244715d7fbbbc9005feec060eaba5f5b1d506f677556ed75a3b4426f330d07b85bda87b5039df509d51d43b4e34b3718fbdad79f837ce2f0e34870e200adab392e201f53e1f75efa690f7c499cc37f5ae16765f0c0f693f3f9525bae13b7a178bf23d863f8150ff15cf814c99a9b60a51e1867ec3cf44cf92abebbda13af14a304b54ed19552d261a50b1b5cae954f609dc70841db0d2f20cadc8a29f3f9ce86c857e51d180f6dcea6d59a0c24544a56af60b637e204aea5e9eb68f40fee8e205b9dfeeb35ed1996f65fdb45621ecc7b717eb0ca5925af32d86873a8ffb4565546034f077549f0fd44c959d16338a22e542e7acab5c9d9762545867139575565ba30815c62c51bdcb9b3ac7e69c87217f7c2a813f64c5ad05b59c91fed5aede13a69079b79ae3d1d371c3627868d5294bf44567892eb4cff9830aad46983d2be24ac4b5d237f5abd22d26e7426789ea5c48fdf4faf63c6ce92cd117fd1459c27eb19a3f41c81d5d336fbb7539f601ecc7bd2acbc5bca11aa2d201f5703977ae7e4601152eeb2342bc94cc43e48f1e612333ca8231df34ffa2a53349750626cc2d0b7937aa7e0e563d6d3028e24de9c8431dad63de69c69c4cf327c21d5d3f780f1b446c1752bfe95254d5ce9185f4b1ba6fa3b244076ad7a6225458c71725fd1459275f9628f1c75f2cfc0de619f97f59c5200dfddcb97a4ead889c0bfdd658b5f7a57adbf0ba3917f145e7d25da92c5175273befdffdbdac3f4130825aad05e5ea2fe06a077ef55be5a6e67ca626ab8fd5bbbbe18898f956d09caca6dfb67aac76e35b8f87f1fc0106c53e1db96019a5c27d985956d533b1fa6d16db5e49cf2a03f7750666face33f979a8560cf5b387f84cf3dae788fc519c015d8ed68f696fdbd0796aa76a4f4db55f57019669a83df4e03a546cfd59c8932aa8c24db55bac38db3c4b34863fca3a7ef064f59d44cfac18cb3ed3f23ed7283dd9d0ab4965bd4b6711abf03dabad9e1056cf77e89cce2dd77f96b8438595524658ab98184467bea9a7886bf3fd11b72be25a54dd07954ba7335c0ad93f218c7b46c41d5dfbfbbc90acb904cb84fb2316b3eb2da9f0a968ab5db3741e47113c6cfebda83ff3f20c71f45df208dba298ccb72ebdf17dfb6336988a20ebea39d8edf6a58e587ba6de94256fb47d46cbdc212b8d0b7a9a24d2c7eaad6e6a7f449ded5a446eaacae9ac8bb67abaa9a82c51cdeca97455b6ebdf73fd21de44ca837356d47378fa89a9a9ba6b09aa5e486eaadea7b3a972e9d475143627d3fbd680ef39411e127f903bfe92a71f05d96f1bcb53602ca99deed43e074565608a867b24d47af3493159a22a5b192ca39e87bd8e3ebb14f2e739ae58f7dbadcbe9b7f78ed5fe884559064a984b579065544e671b8ed88d7b5774c89f450f4f16521a1dbc6cf6cba0994d60cd2545204558a62efade89385dd827753bcb5c507e52539f638cb5b57d56b913989a5faeef0f742e9de38e41858bca4dfd60f79ee339d22d883350202a7df5661095a6be455bdb07b9133bc2c4f31a79412dfd14c8147e15b3380a613f68e6a9ce2d2ee6b92f9567389337c12be5d2651c33863fc89d37f8ef73f00ef524cfbbfdf4f34d2fde4456b77b4375f4986ab764e0e1b0a03b923ae34f4c140f61de98f36f883fb1dcc1c2baa9abe02ddce942bd45a0b09db97afaf9a6a9d2cc62eec7eb3d3edac043f5ecea5ab35fe28fe14c409c79d61f55bfc1cc23e5b972f0b42acb708d8ca3f42bd16f2c7c71cb30873a2be68903bd1f5f55ede6af57aed7fcfb4cfebc07cffc327984e9f7f9169281a933df8610430ec520f26ed4ad2ca3ee78602edda6ab30c49ff788de3c2f177f9f0d0bb0406251efb3554f99aafde58b7a0a5cef54d850fb726d979f94c89ff7b0bc810679c5efdc807dac32dfd4fe88b21af76ed4f58bca03d677e4db52ed68b7755e88b6cf82dec49459302e28e7385a22fb2316b3070266898a6fb5dbe8e63b22a5f027ca9977b218d60f4c1668999ed91f917691dbda323aabadadd77c4ff57decc2ce35e44f3c6fc2c29e8af14f2af34dbfef67aa23d702ae80dee5d9573b7f16f43cc6327fde23dc795ee0ce5b588fb7cd2051fb2302fb1bee83bcd1591c5b9fbd8a05f42e4bea2d71c54403f9f8338b29cfec6ee35fd1fb23820a5f415453880aeb4c36958376259a45a870267f96f5e62de2e9c942fc6d932bd3996f2a97ee54bc14b5231268d7503464497c17140de4e14f1c67b0bc51fd60e57893f8bc28f6832e94d56e3405cdc95aea88948159d03a734efe247066b1b0a39c4735fb237e8b2bbd97671179f07581bb7d5d8a41f12a1c5f548ca9ed93cd9d99ae3fb2cf4cb3bfa332f836db1f318e87105b777007aa9f52e195d26335d9b0fa817ebf43843bc99652e5cb3a4f39aada3903f3d44a7a17b902f640d0fb23aabdee4a996f4a2faaa808a4197c07756bc7fab00631fc8972a6111cd2bfb09e24f79fdea7aa2b3a05e55c04e0b9310373f0f32a4cbfa972342f9c9ed515636b600d5897ec93c59b676d29551f89a477aeebfcfa62f28fadb2569a86dec1e8279fab9e9716abf19af5eddc08669db04765192cc49fb7087796398335161505fde499d6c173eb772cb1fb6276e6ca2e103d35f97d700ce3e9046c0356094ed03aaacee44f231c69ba4ecd5ad8e62c4ba0eb3a9f59baebbe297de3d26243f8b56736152c389d73265a883ff19c59e4ce2c684269153a53d567a9f7e92ce19b117f910aeb1ddfacbe73e35c581f518b18eeb02ff8f738853fb355eea09777f60abb8296de99ab0f9fb27e1b7861bb8aa49696b5679d0767560754f8248e332bfc49e38ce18d2987419b0f0a39cbb27ab31bee8ff88b54f88cb94c3a3df6a454389933ec8b754dbdc09f460267162dd709ba5b9d6543ef58705ae4fe88d9c55131d933cc007616fd532efec471a79950cec192cec6731fb5a7ec25cc2cf5fe88853c9b9ce73787a2ca66309e18faa765bd41aeac7227d49f65ceac72e782ac73a1718f8fd63f53710dfe692abed5eef2bf4a85c53593ec8e4dad9d79c4b75ef9dbe84f92de206716ebc3f5d682d88718ba0fb83fa2cea5fb059651d13c3b776eac8eb2cb2a6792f426567f92f5a619e1cebc3c07fddcbb9994c595da1f91de20ff0b2ca33247ac73501a6599b5b466c98a635542fe6471e61cec644a33e8d8d9abadb43f22bd41fe575806a24cd90d2e61fec496b5261a13a7e98daa559ba9ff8eea4fbcdec4a9f4733048f53b6dbd974343bdbbbba03dc6b24acb1939bde05359863d6ea63551de2cf0278d3b51decccb2cb882282bf96cdb949bfa8b6261760c4ab303fd7d1267993c9c89e34e447fd2f566d97217dacba7ed10504cee4f7669f113eb14bc135b55e1ed78b3c29f34bdc1d25ca80f83eb9fbc6f905d40d3be3d3fb889b7cc3a3e6a9933f33ae44f1a679a21672ec25a8fb0c267aab9b586d7824feb438fa69588af18de2cf0275b6f9a2bf56df0ec3dfc7aeb886beb34b8e307a957bca68f4ae24ef0181c117f92f526ca99c5a218f453bb02c670063ee7ee55706f4653713e6ab5b0afc00b5ac08121e94f5ece44cb79500a767e8d65c419ff10776abd3c8d3345e88db64c1bae6f089f5db24f928f4ae68e2a30c2d8e98fdea5534f51d5d979702725c6c13fe3a342ce8c83a3a01394c1327b689d608ff427993368893967a2f579a09eb3fa21cb381079079ff225596b368d896338330e2ac17e704056a986f5de127fb23883d63205e61981b24fd19cb9604369f3b19418ef6dc79974de04637f08ac698eac3967a275843fd97a33af4d698c5881777b5b4ec96907df3240cb64f9a8edf4465966c4218e3b58e28caaf7147754aded7391536fcc488bd61d8882b6b78c7a32ecda3a74f6e48b359dcf9f7e466f3467bca037b2b50aef2d73265a883f51ee247126ca9df9a7e3ef6cf9ec558f9db021b746ce4858959fe7cca27f5ad69b903b5876893fe97ab3cc19532b4bcd4632e59e6a96e7be7046ec42c5c230a27e94339a371e28cdb356e1610c6756b8a3ac15ea4f3667167913619073b6c9be9bacc64fd89d6010d764de65d9d6478d5c509aaf9117cb9905bd897227f45fe93e6a9533643518bfca3e87237fad7baa2d3e1b05fcdeea089827e0fce9e76262edb9d13f0de993ce19e28d29da3ee93e2a8937e16734caf986089d6b6a1d320191f0347b66b99dde90653a6a96b0648114bd59ae97f813cf9965eb2d7f9ef5dec8192aacde9c6d7d8f1c81b3cb939fd31badc295a0472a9ce9a36278135a2ec29fbc7a839cf1e85f07eaaf462c6d5740a7cd4ff83baa705ece6ca637649936cd12e239335ce24c1c777647632cc49f75f426ce5a0f811d67199571c46bc119a970b85ef31331b19e59827f1a7911adc9f6514b7ab3a4d4467f726b8de10e9cc32df491a95b815cba47d102461d5adf5657cad163be55accdd789f51c617ff444b1f09a3e2a5a1bde2cf0278533b17a13f7b9e2a10f1b3df393d124188831c5c285f9a855de0463e5b96196701b72269fdea470666ebdd138c29f4ccee8cf9c33a83d652a1d18612dab377a09582083aba00de73dcec799cd7c94f14f344b58b640b28f4ad59b58fe24f8a864bdd1add64299f9557b367a1d49f01bcffa1bcd605c14679679a3c7530b7ee16081353963e214ceeca2d5c83a7b217fd23f07099c090bb496c12aed603f3857ffd6f8363882be2e3eaea980653ae15acd0f70665e46667d3e878f4ae0cdb252df8ede420b3de719616b688d074af3acfb6853cec473678933510b25f127c64725f026a5dc065e313e0ae64fef416bf434b246d31c71cd9a3e2a863733c51d552bfb14c0190bad35e70e95aff811b6b67f6ac6f8a7ade29aa88f5ae64c127fa23171b6dea496375d0f83437fb8a9de68ff049e3bc13fad398fcaab3751ee8c3eb2f4398e37c97a13530e5647583eff04acd1fe69f4b9665c93eda3163993c21d65a9d03e0705e88de60c7067a8b953d6ff6ea93192ce9da5b8a6429ebb1c6b8135e751ebea8de24ca44ee04fb6dee4b0148eb0a61a61f9783372613c7516e64f05eb4d3667426bcd22fc498a89f372a66c38b3c41d5d431494a937face5c0bf4ef202316fe111fb5c49db1aea92cf26785376f397c54fc289b974e304ee54d25c63f153e8fcae24e9433cbfc89d79bbc3e2a8e332177cabaad12af3778672e585ddffbe198384d6f42ee7c6141fe6ce5a3e279131d71df268e8ec435153db33cd0568ce7cd363e2ab7de24f1465b2ca23f9be94d126716af1a74651c896b3ced9fca7094698205d6f351ebeb4d126722bc89e74f5c4cbc266f96b9a3cb88d3bd04b55e7ea0db16e39a9fd39bf5798316c3fa6bce9fa2f426f6d3d2eb11079199e5fa7a53584cbcc29918deacf2672b1f15c319b2da10b9b215673658b7599b3773cea8baaead3333fcc9b4c45a7ab3fcd13ab37c67ae807b0b5beb4d026756f953808fb2563913ad1378b3ddba4d413e6a9933510bfdfd7fffe7bffe1f92e4fedb</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/addcontactwizard/Makefile.am b/kopete/kopete/addcontactwizard/Makefile.am
new file mode 100644
index 00000000..9289b747
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteaddcontactwizard.la
+noinst_HEADERS = addcontactwizard.h
+
+libkopeteaddcontactwizard_la_SOURCES = addcontactwizard_base.ui addcontactwizard.cpp
+libkopeteaddcontactwizard_la_LDFLAGS = $(all_libraries) -no-undefined
+libkopeteaddcontactwizard_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KDEUI) $(LIB_KABC)
+
+# vim: set noet:
diff --git a/kopete/kopete/addcontactwizard/addcontactwizard.cpp b/kopete/kopete/addcontactwizard/addcontactwizard.cpp
new file mode 100644
index 00000000..9b1ca28e
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/addcontactwizard.cpp
@@ -0,0 +1,344 @@
+/*
+ addcontactwizard.h - Kopete's Add Contact Wizard
+
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+// CONDITIONS FOR PROGRESSING:
+// Welcome page
+// true
+// |
+// V
+// Select Address Book Entry
+// ( Addressee is selected AND is not already associated with a contact )
+// OR Do not use address book is checked
+// |
+// V
+// Select Display Name and Group
+// true
+// |
+// V
+// Select Account
+// ( Only 1 account ) OR ( An account is selected )
+// |
+// V
+// (Each AddContactPage)
+// ( Own conditions)
+// |
+// V
+// Finish
+// true
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+#include <kinputdialog.h>
+
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <klistview.h>
+// used for its AddresseeItem class
+#include <kabc/addresseedialog.h>
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+#include <addcontactpage.h>
+#include "addressbookselectorwidget.h"
+#include "addcontactwizard.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+#include "kopetegroup.h"
+
+AddContactWizard::AddContactWizard( QWidget *parent, const char *name )
+: AddContactWizard_Base( parent, name )
+{
+ //QVBox *kabcPageVbox = new QVBox(this->page(1));
+ m_addressbookSelectorWidget = new Kopete::UI::AddressBookSelectorWidget(this->page(1));
+ selectAddresseeLayout->addWidget(m_addressbookSelectorWidget);
+
+ // Populate the groups list
+ Kopete::GroupList groups=Kopete::ContactList::self()->groups();
+ for( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ QString groupname = it->displayName();
+ if ( !groupname.isEmpty() )
+ m_groupItems.insert(new QCheckListItem( groupList, groupname, QCheckListItem::CheckBox) , it ) ;
+ }
+
+ protocolListView->clear();
+ m_accountItems.clear();
+
+ // Populate the accounts list
+ QCheckListItem* accountLVI = 0;
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *i=accounts.first() ; i; i=accounts.next() )
+ {
+ accountLVI= new QCheckListItem( protocolListView, i->accountLabel(), QCheckListItem::CheckBox);
+ accountLVI->setText(1,i->protocol()->displayName() + QString::fromLatin1(" ") );
+ //FIXME - I'm not sure the column 1 is a right place for the colored icon -Olivier
+ accountLVI->setPixmap( 1, i->accountIcon() );
+ m_accountItems.insert(accountLVI,i);
+ }
+ protocolListView->setCurrentItem( protocolListView->firstChild() );
+ groupList->setCurrentItem( groupList->firstChild() );
+
+ if ( accounts.count() == 1 )
+ {
+ accountLVI->setOn( true );
+ setAppropriate( selectService, false );
+ }
+
+ setNextEnabled( selectService, accounts.count() == 1 );
+ setNextEnabled( selectAddressee, false );
+ setFinishEnabled( finis, true );
+
+ // Addressee validation connections
+ connect( chkAddressee, SIGNAL( toggled( bool ) ),
+ SLOT( slotCheckAddresseeChoice( bool ) ) );
+ connect( m_addressbookSelectorWidget, SIGNAL(addresseeListClicked( QListViewItem * )), SLOT(slotAddresseeListClicked( QListViewItem * )) );
+
+ // Group manipulation connection
+ connect( addGroupButton, SIGNAL(clicked()) , SLOT(slotAddGroupClicked()) );
+
+ // Account choice validation connections
+ connect( protocolListView, SIGNAL(clicked(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+ connect( protocolListView, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+ connect( protocolListView, SIGNAL(spacePressed(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+
+ // read sticky settings
+ KConfig *config = kapp->config();
+ config->setGroup("Add Contact Wizard");
+ bool useKABC = config->readBoolEntry( "UseAddressBook", false );
+ chkAddressee->setChecked( useKABC );
+ setAppropriate( selectAddressee, useKABC );
+ // load address book, if using KABC
+ slotCheckAddresseeChoice( useKABC );
+}
+
+
+AddContactWizard::~AddContactWizard()
+{
+}
+
+void AddContactWizard::slotCheckAddresseeChoice( bool on )
+{
+ setAppropriate( selectAddressee, on );
+}
+
+void AddContactWizard::slotAddresseeListClicked( QListViewItem */*addressee*/ )
+{
+ // enable next if a valid addressee is selected
+ bool selected = m_addressbookSelectorWidget->addresseeSelected();
+ setNextEnabled( selectAddressee, selected );
+
+ if ( selected )
+ mDisplayName->setText( m_addressbookSelectorWidget->addressee().realName() );
+}
+
+void AddContactWizard::slotAddGroupClicked()
+{
+ QString groupName = KInputDialog::getText(
+ i18n( "New Group" ),
+ i18n( "Please enter the name for the new group:" )
+ );
+ if ( !groupName.isNull() )
+ ( new QCheckListItem( groupList, groupName, QCheckListItem::CheckBox ) )->setOn( true );
+}
+
+void AddContactWizard::slotProtocolListClicked( QListViewItem *)
+{
+ // Just makes sure a protocol is selected before allowing the user to continue
+ bool oneIsChecked = false;
+
+ for (QListViewItemIterator it(protocolListView); it.current(); ++it)
+ {
+ QCheckListItem *check = dynamic_cast<QCheckListItem *>(it.current());
+ if (check && check->isOn())
+ {
+ oneIsChecked = true;
+ break;
+ }
+ }
+ setNextEnabled(selectService, oneIsChecked);
+}
+
+void AddContactWizard::accept()
+{
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+
+ // set the display name if required
+ if ( !mDisplayName->text().isEmpty() )
+ {
+ metaContact->setDisplayName( mDisplayName->text() );
+ metaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ }
+
+ // set the metacontact's groups
+ bool topLevel = true;
+ for ( QListViewItemIterator it( groupList ); it.current(); ++it )
+ {
+ QCheckListItem *check = dynamic_cast<QCheckListItem *>( it.current() );
+ if ( check && check->isOn() )
+ {
+ if(m_groupItems.contains(check))
+ metaContact->addToGroup(m_groupItems[check]);
+ else //it's a new group
+ metaContact->addToGroup( Kopete::ContactList::self()->findGroup( check->text() ) );
+ topLevel = false;
+ }
+ }
+ if(topLevel)
+ metaContact->addToGroup( Kopete::Group::topLevel() );
+
+ bool ok = protocolPages.isEmpty();
+
+ // get each protocol's contact
+ QMap <Kopete::Account*,AddContactPage*>::Iterator it;
+ for ( it = protocolPages.begin(); it != protocolPages.end(); ++it )
+ ok = ok || it.data()->apply( it.key(), metaContact );
+
+ if ( ok )
+ {
+ if ( chkAddressee->isChecked() && m_addressbookSelectorWidget->addresseeSelected() )
+ {
+ metaContact->setMetaContactId( m_addressbookSelectorWidget->addressee().uid() );
+ // if using kabc link, and the user didn't touch the mc name, set a kabc souce instead of custom
+ if ( chkAddressee->isChecked() && (m_addressbookSelectorWidget->addressee().realName() == mDisplayName->text()))
+ {
+ metaContact->setDisplayNameSource(Kopete::MetaContact::SourceKABC);
+ }
+ }
+ // add it to the contact list
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+ }
+ else
+ delete metaContact;
+
+ // write sticky settings
+ KConfig *config = kapp->config();
+ config->setGroup("Add Contact Wizard");
+ config->writeEntry( "UseAddressBook", chkAddressee->isChecked() );
+ config->sync();
+ deleteLater();
+}
+
+void AddContactWizard::reject()
+{
+ QWizard::reject();
+}
+
+void AddContactWizard::next()
+{
+ // If the we're on the select account page
+ // follow it with the add contact pages for
+ // the chosen protocols
+ if (currentPage() == selectService ||
+ (currentPage() == intro && !appropriate( selectService )))
+ {
+ QStringList usedAccounts;
+ // We don't keep track of this pointer because it gets deleted when the wizard does (which is what we want)
+ for (QListViewItemIterator it(protocolListView); it.current(); ++it)
+ {
+ QCheckListItem *item = dynamic_cast<QCheckListItem *>(it.current());
+ if (item && item->isOn())
+ {
+ Kopete::Account *i=m_accountItems[item];
+ // this shouldn't happen either, but I hate crashes
+ if (!i)
+ continue;
+
+ usedAccounts.append( i->protocol()->pluginId() + i->accountId() );
+
+ if(protocolPages.contains(i))
+ continue;
+
+ AddContactPage *addPage = i->protocol()->createAddContactWidget(this, i );
+ if (!addPage)
+ continue;
+
+ connect(addPage, SIGNAL(dataValid( AddContactPage *, bool )),
+ this, SLOT( slotDataValid( AddContactPage *, bool )));
+ addPage->show();
+
+ insertPage( addPage, i18n( "The user has to select the contact to add to the given account name",
+ "Choose New Contact For %1 Account <b>%2</b>" ).arg( i->protocol()->displayName() ).arg( item->text(0) ), indexOf( finis ) );
+ protocolPages.insert( i , addPage );
+ }
+ }
+
+ //remove pages that were eventualy added previusely, and needs to be removed if the user pressed back.
+ QMap <Kopete::Account*,AddContactPage*>::Iterator it;
+ for ( it = protocolPages.begin(); it != protocolPages.end(); ++it )
+ {
+ Kopete::Account *i=it.key();
+ if( !i || !usedAccounts.contains( i->protocol()->pluginId() + i->accountId() ) )
+ {
+ delete it.data();
+ protocolPages.remove(it);
+ }
+ }
+ QWizard::next();
+ return;
+ }
+
+ // If we're not on any account specific pages,
+ // we must be on an add account page, so make sure it validates
+ if (currentPage() != intro &&
+ currentPage() != selectAddressee &&
+ currentPage() != selectService &&
+ currentPage() != selectGroup &&
+ currentPage() != finis)
+ {
+ AddContactPage *ePage = dynamic_cast<AddContactPage *>(currentPage());
+ if (!ePage || !ePage->validateData())
+ return;
+ }
+
+ QWizard::next();
+}
+
+void AddContactWizard::showPage( QWidget *page )
+{
+ if ( page == intro )
+ setNextEnabled( page, true); // make sure the first page's Next is always enabled
+
+ QWizard::showPage( page );
+
+ if ( page == finis )
+ finishButton()->setFocus();
+}
+
+void AddContactWizard::slotDataValid(AddContactPage *onPage, bool bOn)
+{
+ // some plugins emit dataValid when they are not visible.
+ // so we need to enable the page which is signalling, not just the current page
+ setNextEnabled( onPage, bOn);
+}
+
+
+#include "addcontactwizard.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/addcontactwizard/addcontactwizard.h b/kopete/kopete/addcontactwizard/addcontactwizard.h
new file mode 100644
index 00000000..427bb1e3
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/addcontactwizard.h
@@ -0,0 +1,85 @@
+/*
+ addcontactwizard.h - Kopete's Add Contact Wizard
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ADDCONTACTWIZARD_H
+#define ADDCONTACTWIZARD_H
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include <kdebug.h>
+#include <kabc/addressbook.h>
+#include "addcontactwizard_base.h"
+
+class AddContactPage;
+class QCheckListItem;
+
+namespace Kopete
+{
+class Protocol;
+class Account;
+class Group;
+
+namespace UI
+{
+class AddressBookSelectorWidget;
+}
+
+}
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ */
+class AddContactWizard : public AddContactWizard_Base
+{
+ Q_OBJECT
+
+public:
+ AddContactWizard( QWidget *parent = 0, const char *name = 0 );
+ ~AddContactWizard();
+ virtual void showPage( QWidget *page );
+
+private:
+ //Kopete::Protocol *currentProtocol;
+ //AddContactPage *currentDataWidget;
+ QMap <Kopete::Account*,AddContactPage*> protocolPages;
+ QMap <QCheckListItem*,Kopete::Account*> m_accountItems;
+ QMap <QCheckListItem*,Kopete::Group*> m_groupItems;
+ Kopete::UI::AddressBookSelectorWidget *m_addressbookSelectorWidget;
+
+public slots:
+ virtual void accept();
+ virtual void reject();
+
+ void slotProtocolListClicked( QListViewItem * );
+
+ void slotAddGroupClicked();
+
+protected slots:
+ virtual void next();
+ void slotCheckAddresseeChoice( bool on );
+ void slotAddresseeListClicked( QListViewItem *addressee );
+ void slotDataValid( AddContactPage *onPage, bool bOn);
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/addcontactwizard/addcontactwizard_base.ui b/kopete/kopete/addcontactwizard/addcontactwizard_base.ui
new file mode 100644
index 00000000..d5c31826
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/addcontactwizard_base.ui
@@ -0,0 +1,487 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddContactWizard_Base</class>
+<widget class="QWizard">
+ <property name="name">
+ <cstring>AddContactWizard_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>579</width>
+ <height>417</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Contact Addition Wizard</string>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>intro</cstring>
+ </property>
+ <attribute name="title">
+ <string>Introduction</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;h2&gt;Welcome to the Add Contact Wizard&lt;/h2&gt;
+
+&lt;p&gt;This wizard will guide you through the process of adding a new contact to Kopete.&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop|AlignLeft</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;Kopete shares contact information with the KDE Addressbook. This gives you seamless integration between instant messaging, e-mail and other personal information management applications.&lt;/p&gt;
+&lt;p&gt;If you prefer not to store instant messaging information in the KDE Addressbook, uncheck the box below.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;Press the "Next" button to begin.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="5" colspan="1">
+ <property name="name">
+ <cstring>PixmapLabel1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer14_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="2">
+ <property name="name">
+ <cstring>spacer14_4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>425</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkAddressee</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use the KDE address book for this contact</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this box if you do not want to integrate other KDE applications with Kopete</string>
+ </property>
+ </widget>
+ <spacer row="5" column="1">
+ <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>91</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>selectAddressee</cstring>
+ </property>
+ <attribute name="title">
+ <string>Select Address Book Entry</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>selectGroup</cstring>
+ </property>
+ <attribute name="title">
+ <string>Select Display Name &amp; Group</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;qt&gt;&lt;p&gt;&lt;h2&gt;Select Display Name and Group&lt;/h2&gt;&lt;/p&gt;&lt;/qt&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>AutoText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Enter the contact's displa&amp;y name. This is how the contact will appear in Kopete:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mDisplayName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mDisplayName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Leave this blank to use any display name set by the contact</string>
+ </property>
+ </widget>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer14_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>65</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Line" row="4" column="0" rowspan="1" colspan="2">
+ <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" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Select the contact list &amp;group(s) that this contact should belong to :</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>groupList</cstring>
+ </property>
+ </widget>
+ <widget class="KListView" row="6" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Groups</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>groupList</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A contact may be present in more than one group</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="7" column="0">
+ <property name="name">
+ <cstring>addGroupButton</cstring>
+ </property>
+ <property name="text">
+ <string>Create New G&amp;roup...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Click here to create a new group</string>
+ </property>
+ </widget>
+ <spacer row="7" column="1">
+ <property name="name">
+ <cstring>Spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>407</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>selectService</cstring>
+ </property>
+ <attribute name="title">
+ <string>Select Instant Messaging Accounts</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;h2&gt;Select Instant Messaging Accounts&lt;/h2&gt;&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Select the &amp;account(s) you would like to use for this contact from the list below.</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>protocolListView</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;i&gt;Note&lt;/i&gt;: If a messaging service is missing from the list, please make sure you have created an account for it in Kopete, and that it ready to add new contacts.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Account</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>protocolListView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select the Instant Messaging systems to message the contact. If they use more than one IM system, select them all here</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>How do you want to message the contact? If they use more than one Instant Messaging system, select them all here</string>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>You can always add more ways to message this contact later.</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>How do you want to message the contact? If they use more than one Instant Messaging system, select them all here</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>finis</cstring>
+ </property>
+ <attribute name="title">
+ <string>Finished</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;h2&gt;Congratulations&lt;/h2&gt;&lt;/p&gt;
+
+&lt;p&gt;You have finished configuring a contact. Please click Finish and your contact will be added to your contact list.&lt;/p&gt;
+
+&lt;p&gt;&lt;i&gt;Note&lt;/i&gt;: If adding this contact requires authorization from one or more of the messaging services, Kopete may prompt you for further information after this screen.&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>205</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="130692">789ce4bd4793ebc6b6addb3fbf62c741efc40b5e3a1040e335e8bd2d5f376e0396de9b2279fffc4be61839ab6a1969495ada5bd2db193a479fc0023247ce9c331d12ffeb7ffef53ce8feeb7ffed77fed0ffe611afe2b9cf8bb7ffd4f745c2e2ffffbfffcbffff7bffe3b93f3fe95f50aea9fbcfbafec7fff3ffff5dfc3c3bfc27f59d9423e6bfb374e59c281669f9c23079a1de15038d41c91f3e458b32b9c08479ac7649b3cd1ec298e354fc905f24c38d13cd7ec0b2fc80e7969b890d6bcd21ce45df25a38a37943f6c85bcda1f04e58eb97da937df24138a7f9a83912ae9383425ef3bde658f859d8d6dc2387bc9e15c6f5abe644fd1ef77f21f3ba95122e688ec811792dece8bf2fde389736f7b34ee418d7adb2b0abb9494ec82dcd19e1beb0a7f9196ca7c9dade7259753fe4270d96eb19616d9f3e7f6f67c891b0b6475fdb4b2e27bc2267c93b616d8f3eca9b17ae9073e4aab0b64f5fdb73ce96fb0dc8e67a8d9c270f85b5fdfa19616d9f7e966c93b53de50af27c87cceb4162d8d1f61b8cc905b26e1f3947782baced3778233b64e4d715ae0b6b7b0e7a6497fc200cfbd0ed25e729bd75fd058f645e0f5ec91eb920acedd942f97d555e5ddf418eece3f7612cac7f1fce85b57d87077200b6507f81ba9fb657ff815c80fee199ccdf8717619dffb0430ec95d61b40fe42f54f58ffc0fc879d87b989003e677238ce7d5c8e6fe23617dff304f8e1c7dbf00f61a29bd509f0d615d5fe1956cee7727acef175964de2f1a936347eb1dc1de62e183b06e5f51899c905f8561ff68ff89edc25e8380ecc15e439b1cf3f94761dc6f4e4ef0fcf15218d7d3c2fa79910b2ea4c1f152583f3f76c81970d4be713eadaee37e9e30fe7e40e6efa7b6b06ebfb32e390b8eb5def98cba8ebf7f11c6f35ae42cae4f67c2faefe7beb0b6c779919c23a785d1de9f3467557deaf637ae81cdfdc61639c7fb6784f5df2fcae43cb962d8d5f614213f39a5afb697d9166c7ebf5c1ac6ef972db24d6e0b6b7b8c90dfbcc9cf7245e6f579895cc0dfaf86c2fafa6a24accbbb09c98e8bf8a6e351fee62ff5f535f2afdc1b7f3f12d6bfdf5e84757bdc9aeb2e38d1f6ae1e97837dc42db2cdf23b64de6f13915d3eef24acafaf47c2fafebbadb06eff3b737f0f1ce0feaab7027f17c31e1c939fe49e4cbd1655329fb7cb9279bf5d4e58fb837d8bec830f81b0aeef18fabbea79bafd1dda645e3f86e4007ce80bebf6ec6b7f9af7ec00f1788bf6e1a9e7ebfc1d6b64fefe582787e482b07efedb2339020f604f7e2609757d0cd05efd6c9adc2667c011ecdf57f6abff7e72217ba8af65871c40afe394cce71fccdff3f987acb02edfb1478ec97d61ddde873abee603951fdd9e079e30f2db321ce9fa1caec959febe6898d77be41caf4f0df37a9a9cc7f523ec2d30f93dbd9299df534a58e7f7542227e4b4b0f66703f8af503d5fe7bf6f1be6f3cbc2bafdf6f3e43c794fb6c1c3805c08b57f185585f5fd473ed9e1f5bab0be6ec560155f74fdc52e58d5b7b6a7cd1dd9833f19f3feca9eb53dbea5c92cff214766f9df16c2bafc6f4d619d9f37d45fe8a4c143d44764cad7df910be44058dbdfa84c66797acf8679bd4876519fa39661e8dddf0ae3f76f648f7a5d84f5df0fdbe420d4e5191d85d13f45fc52fd01f45723d873a4da87be7fc2f2a9f6a1fdd9b425acf51fe7c911f8d02327b0b703f48e945e5adf4b4758e7e73c15d6cf3fcf8475fedf987f27c3eb73617dfdfc2a0c3d71ffd8e8d3df08ebfa1f99eb1ef4ec5d8471fd99ece3ef7b0b615cdf9343eadd358cfb8d52e4187a0f137212c29fa37cb18a2fda5ee32399fa1e115f62a3d7db92ccf25e12615dde92e12caf8f85f5f54bc6b0a7f3773d9173e01eea3f51fa687bedcdc92c6f37470ec0bd99b0ce6f374b0ec123871cd13eb764ea31ea93e9cf530f60d59ef5fd13d85762f4d9c4641fed395e90696f47d46fa2f4d0f7bf768475fd9c3264ea53dc08ebe75fdfc8d4a39812d6cf2b56c9797057c74b3b6dcadbad912370ef4958e7af5b25c7e0de949c80476b702e437ba9909d10fdf13bb029efac474e108f573b61fdfbd33358955fdbcf394dced15eca86593e8bccf21547c2bafe4acc8f6393f3c2b00fada79d51e5811e6b61941f7f9fc9a5c981b0ee8f7457e40c78342433de8cb2e43ce38d4b2ea03d8d966427c47c548aec85da7e868fc25a9fb804561d3afdbc31f3a7f4d37c815e19634fa71d394b7b7a21e7604fe5984cfd4a2c8fe8650b6bbdca5b72815c14d6faf575bcb1b3591ffebbeb838d3e9d34394b7e15d6f6d0199073607f0656fe1df684f26455fc83ffce8195bf417fac4b66f94a1d721ef6535e906db497728fccfc5763b283f25576c2fafaa526ac9fdf819e3993dfce959c27f7c936792dacf3db9b900be0d113d9f8fb393982bd0c0be07c86f6d12067437dffe182ac86e0da5e509fb982037f74857de5943eb08f32997a5cb66453de27617dbf8b47763d5d7fd7aab07e7eed48f6c05dddffb6f3aa7e75793a2b32cbdb09c80ed917d6f5dd7e26bbe051861cd23fdf9123f617608ff9bceaf2dc788ef69757f6aff52b3ae41cda43e9956cd39f76c905da4742a63d5cefc92c5f712aacf35f9c917d700ff660ab78a1f3d781bfb24d79da5db2477e22fbe48e30fc059e67e76dd47f00fbb10b39cc0f056732e7e7a232d9c5f8f8c0eba67c9588eca13e8be63af35fcc08ebfc14b3e4003c40fb2ba8fe98f65f1dfc5e6507f1b8fd4866fedb1772403e9343f24c18e311d46f211fc09e43d87741f93f5dffd18aecd21fa2bc0555bf988fb1c9798e771ae402eafb101a66ff624276184fb7c2f08f55b20bff58ec907dd843e98d4c7d4a6d61fdfcd20b3904977d615dde36ead7c945287f7b428ec1ad0239e1f50761f44f783dcffef9d026c7d02f85f23b7684feca15fed8517ae9fc8cb3e43ce26fe98e6cb37fb211d6eda7fcce882719b20bff513a9103d853d92287d0a73c17d6cf2b9bdf47e486b07e7e1bedc955f6a1f56ed96496bf057feee6d3e42e39437d1ec83efb7363720c7f71467b705579f4f31769615dde0befa7caa7ede3f02aacede364096bfbb8b8c288977572c4fa7f12d6f9297be4185c5908ebe757dae404dc823ff354f9b59e1db42fcf94b7fd268cdf97c85972919c030f11ff3c3b0c345bfcfb429aeb317c5e2183f9e22decc353f117f3677532db5b236dd8c77ce3985c80be8d8c611fbf7f25bbbeae8fe384ec311eefc93eecab9135ec6bae74c8d4a7d215d6f659457bf7dc343912c6fc06fa6f7e2e8dfe56c730fb332df81b3f9f677bbc0a43cfa5b0fe7dd326dbe0d6825c008f601fbe9da63d266427d4e5196ec95180f97fc457bfc0f58c31e291aff444fbac915d8e17eec81eecf1cce7abf6a8ebb38afe886fca5fad9233e49a615fdb7b13fde7c0e4bff5cefaf7cd26d92137c82e78887811d861a8f3d384fe41218df25a7532d763aeb09740d913e2ad4bf6519e53911cc03eea0139823faeccc931da5b15f61098f254991f37eb6bfb6d217e84aafef4f35b7332f3dfcc913d72dd700c7d8e649f0c7b0bed3cc60f29b4cf50b5279ddfe1889c09f5efad077216eb3f43f3fb1ceabf88fe7ea8f4d0ed2f59915df8a7715d58ff7ebf26fb6c3f5b32f5a821de86a6fcb5bdb0d6b77620e7c82d617dbf26fc7594f7610f9d9e6196ff400ec85961fdbce69e1c926be4881c9013b617f8a7a89085fdf85d721ef3b743fe7da100bdaeb0ff48955fdfefb82687f01f359b4c7ba9c13f46aafc3abfb5a630f4699359feda909c27e785f5fd3bb0cf3817a37fd4c4782296f2f9e418fab466c2b8be15467bb1c809af6f84711dfdfbd8cee07e0d8fccfef5e8851ca3ff78e70b6b7f17c2fed5680bfe65f3464eb03e3464fe9c7480f9e531d9417febfa40f6399f3321078897d527b2d1ff991ca1ff59853dc7caff687bae337fae0d3deb5b61dd9eead4d72d80fb686f891adee8fa6ca3fd2679ce3f37ab64a317ec21b1d3e021ec332978816edf498eccf5c809c6a7892a1faeefc901b8f86618f65671c821fcd5d1fc7d427b833d246e0efeb6de22b33cf5b6b0d6a79e263b644f58df2fd0e34de55dccfa658f1c607d661491c350e76fb703abfe9fce5fc527bb88df6f4fe418f1e23010467d55c06e16f5d54891999fc69aec922fc2dafe76babd14322a7ee9fb9f77641ff12cbe90693f75733d417ede326015bfb4fd359a64debf31247be41761c477dd7e0a59651f98cf1c810b3ed6a787b87fd6c9a33f34ec939d00f3ef13b20ffb8f5ec821f25b8a85f5fdca4d72041e6784b5fed3023906afee8475792b1572027f7eec8055f9d11f8ec8d4bbb920b3bccd25d927af84757db7581e37407fada3db5321a7c6d3f01fc87fcec9205e8f785de983f69e22bbd0a3312127b097e119eca603f0869c0dd0dfe1fddc5ca0cbdf999299dfd6851c30bf5361f4c7b2c2e8ffe7c82119f597cf7b984f69b6c811e7eb1a60553eb49736398bf26e1232d7874735c3f8fdd52647f007e70e39e678e10dacda8b2e4f354bce717c1392d9ffaddf9169bf6de6d794b7bd2387e4b130ee7712d6f96db33c6e04bed3f1a4a0068818ff9496641beb4da30ab980787b9e905df4c74625b2077f527a26d3fe47013946fb89aee484fe310d76f318efb50e6407f1b41d915df8c35648f6a04fbb4ef661ef1dc301eca5ed9159de4e4a18fd1596d78dc91d615d7f6db4675581b477f8d382633847f6c8577288fe5c19f55d50ed13fadc0b6b3dcb2561f4075fc9b900fd990e5939705dfe67b283fe58d3fcde833e1d87ccf277cfe410ed6538264781ce5fd7229bf2778575f93be67909b8bb348cbf1f20ff8eeadfa03f88f23a36d70312f877a71071be66428e118f2efcbdc3fd0fa30199f71b9dc909f4ac410fc7e81507e40c785c12d6f5317fbf8ef58a9361c4f7da949c477ff85811d6f678dc916df0c8320cffdf32f7f7699f3139a23f7a11d67a749fc946df063981febd9130f436e5a1dec3a530e2d35158977f8078eb7869fe7dd930eb3b238cf13aecd155fd3bec2f417b719d02f68fdc05e404f531467b7595ff829e0732fdff917f6ff41a3c9013b4bf9e2d8cf54ef4375c93dfbe6598e5ad0a637d6141cee07a6f2c8cf5bfb330d63f1fc959700af1d52b14b85ed72647d89f5645fbf694bd60fd3824b3bce39530d67b46e41cfce3e1855c803f1fed0c439fb70ed9417f69e89143f4a7874fe408edbdd120c78c5f1932ed65e082957e586f5918863efd12997a0d9bc2e82f67c9d4a7ef0b6b7dfa7cbe9723e784757dc4c8bf5f08b9bf334d8ed9ff34d713b4ff0beac377b8bfa77c24c7182f1ca664ee5f18f17eae037d471bb28778d2f2c901c7074d728cf6d7ef9013e8d79c0bebbf1f404f5fe987f8d0261bfb3a90b36c6ff7c25abfc1844c3d0657721ed79bb0b740f9439dff37e819a8fe818ee767c4abc0e5fe81768e1cc29f8c56e438c0f80bfe5d8d2e02fdf7dd0999f91bf4c97cfee05118fdadbcb0b6af41966ce37a1bf61fe639df54845ea1cbf5e685e102fa2fa37b7280f83ff2c811b8572727883f23d8a7ea0dc37f0e2c32fb7b439f6c237fc388ccf8379c0863ff16ec3b2a04581ff473e410fb9b4b64155f747d8e8b60957ffdf7a711d9c3f86b649399ff16fc8beaadd35f3d90f3f4b74d61c4ff9930e2c5845c603c9f931de47fb81246fbdf915d7088fe8a1a6fc33f5d3d7201e3f1b24b667b39c1fe62551e5d7f7df41f6393dfd1a330fced3dd9c6f5e19ecce70f4fc2c83fefef79e004e549547bd7cf3b219e27ca5e301f7926733db99612d6f6b65c933dc6db1539407d748ee418eda1077b49547bd5fad5afe42cfbb7fcbd2a1ff62f317faa7cfaf9dda230eaaf26acebef8ef953f581f9ca12d9437d0eeb649fdc124679f5f3556d39181f5c2be400fde94a066cf4580764d6d7214f0e30ffd4de9223cc3ff5c760658f684faf640fed75d825333fc30139200f8575fe126dbf4e46f477c8219effb62247e86f0e5260a5b77e7e67412e303e8c8411af1fc80efd635d18fee9220cff647ecffc8df2c228df3399eb13c317617d3dd6feefb6da8bfedcf82aaccb57299303c4df36ee9f35f9ef35c8267f63619dbffe9aec827bd03ba7fc25fabb36d8c920fec5113987feee640656f9d1bf1fbf9203f000cfcfa9eac0fcb3b6f7dbec37fa1f51811ca03cf1abb0fefbfa06acec19f37f677201eda17d4f76511f2ded3f1cdb66f95b6bb28bbf0fe660551eec7743fdd86e01f13a2a92b97e37f6c911f43db6c909eca7db012bfb457be67565afbabc35d44fc1e57a7cf142e6fada744e8ed15ed66f8671ffc309acfc03c68f0db2c7fe4e91cce77507c2585fd0fe5ab5de10eb590dd8b31a2f205ed74be43cfcdbf842667e0e64553eeca72a9003dcdf87beaabf8df9fd22daa3ab9ea8f99205bb5cafe99155fe90ff3299f7eb0f8575fefa8fe4908ce77b7616feba85f6ee197dc72fe404fe76b6032bfd74fd9fb2e41cc6db0d97ccfc740c87c88fa5fb6f8e5fe0fa7217ed47f597503f47979ce3fc267fafee07ff312147c87face39713387c5faa982647189fd4e14f03551f3a5eadf97ba53ffcd58a5ce07cdf89ccfb77d1fe439bfee98cf6113a3ef65f37d09e4237c7fde468bfa197871e878eb0bedfe1996c633ea2f7464ea07f19fe2952ed15e361fc3ef2b8be10b7c939f0bc4ce6fa42eb488e59bf6fc2f0c74fe404f1bb9b16467f18fe37f2d3e02a387639bf3a467e62f3bc814fe6df77d11e1235fcd5f9f3715df5eeb15e7781bf495cce67b6f97b2f03ff943c906ddc7f5123737ebb3724737efb7411c6feaeaa30fa8fa89fc49467c0e7fb19f8e3b66eef6eda4e70bf520becc488cf170bec86f4375d72ccf99ab530e6375e8475fd2e5db0cabfe65385ec713d7b454ed03eda31d8e7fa492f21b33fdcc2df670a5c7ff54760278ffe6d5220dbe8df0665b203ffbc3993f9be56d107bb9c2f3ee07919a3e7b24be67cf1714966feeb1699f96f75c02abffa7aff899c85de5d6d4f6ed6e67c5213f9c916f2f08f5113acf4d2ed2d1a901d705c10d6f5158764cefff5f260f33c4bc77737a7ec4f97af542273fdea5225f3fdb212ee9f7339bf3ee1df7b9c6fed9fc89c4fdbb7c93ec68f291d6fddbcf247faefad363987f7e9ca33b0cbf7b9da0158d93fd66b2b64ea3d7e22733e735c20733ef37426737eb796032bfb41fb1f9039ff62c13e6d652f3affdb901cc11ece289fedf27da89ae110f12c6a804d7ea29130deb799907dfabf2399f3cdcd0cd8cfc19e7b7764b33e00fb2f287db05e32049bf2266b61bc6fe79003eaf528acafcffac2785f664e0e71fde49339ff9bc07e1dd57eb01f2922f3fdc4e23dd8cba2bd742664ce0ff6f66497fe7a4e66fee2b330e2972d8cfd680d72083eb6c8ccdf316b98f375baffefba853cd76bda64ee4f6bc17e5c558358bfeb8195bd617de981cce7c7537288f636a80aebfa4c26c2c86f931c911d61adc72223aceb7bb517c67c574c8e519e1af3eb9bf9a714d9c578bb89dfabd60b7b4c8e641bf65c46fd7bcac361fcb901abfcc27eaac25affc9901c916d615d9ed94058df6fea9063fc7e5e11c67eb0bab0cedfe1896ce6e3e05f3d539e1efca95fb0f1fb04e5f54d796a884fbe1ba0ff72d9813dbeef50cc9173dcbf9311c678827faff20fffd122c7d0639115d6cf5f98fb2528cf7223acaf1fa197ef73be6388fc06853cfccd15f617a8e110f6cbaec99cdf6fe0fe819b45fbaea03e547f0cf1759227331e1f612f81f2e7186f668531debd92b9be94a0bd86aa3c98efea09e37e0bb0cff9bb6393ccfb771ec91ed613fa3361bc3f0e7f153921e26974224728efa54de67c66e50236e54d90ff48f92fac8f38e404f57138804d7ef65961fdfcc3909c03bf75c979cc5f34c66417feb4f14ce67e85be611ff38dfd0a39000fb6c2583f41ff2156e116fb7bd7c2389fc025737ead85fa8a5dbedf39477c897d8eb7263932fbdf8b8530e26989ccf21dae64ced71c6c618c170e64cedf34995f9ff37f9d3399e5eba23d269e8ff1e700fe5cf50ff1fcf19accfd5ffb0c390f7bdf970da37e0e5d61bcaf3322333fa7aa30e6770232f3d74ec89c1fedebf278e9820f7f912a819d02cfdfa8917df4a7fdba30d6f39fc11ee7633bfcbde7a27fb1dc82fd2cfbcf01d9e437278cf709d764ced7f4ccef23ac67a6747cf56efd4bc4bb7b61fdf7d547b0b208c4bb19d8677f6790368cfc4cb2e42cfa839382618edff7641bf1f0ad4766fede72648ed77a8643d87337458ec8da5f78d902e7cf822558b567edffc22d39c1fb83714a18fbc7f2c2d84fdf01bb695cdfdc9333e83f545a608fef0b1ce760551efdbc7d5118eb65776407dcef9363e83f407de70a21e7670660f3becbf90dec71bf71694ce67ed918f9cf29bdf5ef2b6bb2b1ef4732c7c7cb1ed9e17ce90b99fbc79bda9f7979278df1537b41e6fb46419e9c70bea80d765df88b1af4c87b7c1fa37220733f7ea540f630df35c9807df6bffa5d7090c67e054bf7ef3cdbe17936c50239e0fc0a9e6f9bfa0a8be40ceabbb625c77cbf16f9b33dee5fbef6c8dc6fdccb90395f32407bb14d7ece688f05153fb0bffd851cc19f46d0aba09e8ff73f79dd35f33f68ef05557eb437b41f355ae2f88d7faffc3ffce391ccfd31ed2658e547eb97a0be1c87e7e784b00f478dc874f9abb01f251fe6f36ab02fc7e7fcee74412ec0bef66932c72f2dd8bb639ed7ed9233d87fd7417b779d3cd68bdad04775e8f8be9e47e6fedd4e811c627c7d467bbd4ddf623e1cedc555f7c77cc1919cc3f34a60cfd46f5827b3bcf50e3984fd76517ed5ff43fba8bbe0208bf2f4a764debfa1e395eade15b05e5e46f97c7547c45fd89bef73ffe83820b3bc4de8e7abfbe3fdb71999f76fbb64eeb71abc916dec570ae16f0297e7f5342cb287f7bb4e4f60951fbc9f3126733fe204cf57a313f6c74a8651fe15efef8718df1f6332e7b7fb67b0ca1fdeffd9916d7049c7472f54f686f912e427542d4cdf6f33269bf765cee408ed6b06ffaffa63ccff2b39e2fc087faf9e8ff9b57b32f5e92fc905e42785df474e8cfc5c36c288aff03f91f20f681f0532cf3f3aa07e547f8ae319febd1f217e1d6d7282fe58f702367af45fc87cdfb39f11d6e549d05e62a517f60722fec42ecf676a19e6794a9b0bb980787480feaa778ff9f743816ce6c311ffd4e80bf92bf379aafdc01e2be402ecad85f225663c1c3f90039c47d61919863df5e05f9280efaf0e74fbf2557f05e3096b4b8ee19fafafc2a80f879c60fec47f04abf263be2403f6b9ff7c1d824d79decae40cf8b417d6f96d0cc8b487b6b9eeb03e2ec298df9c935dec5f6ae27ac6f4878b657288f3d70ef8bd1a2d213e1fc760d59e313e89c93edeb74f747bf5b34e88f3e0ac2ad8e5fb00952bd8e3feb839affb3c6fe4b02427f4c713c3e87fafcfe0200b3d560761f8a71a99f969a2be722a5e627c7522337fa50539025f4760955fbcaf31246790ff728dccf9ca4503ec47882fab07b0c9cf9ecf57ed19fbf31361ecff6c92f9be56ef9d71de9ef68f7e5ee507ef0324e42cc6d7ad0639077b9ac0fef27e8cf9af03ea272fcfcf0923bee6c936b89c2617185fa18fadf4c17e148fccfd5457e4c7567ae07d941999f34fad477201ed3d0ac89ccfad406f5b7924cc573f837dce4ff56a64c6d703f455fd119effc1e707dcbf19e3790593dfe48d1c935db0d1b3532267303f61e5c83c3facc9fb79dc0f5ab923c75cff9f8255fef03e72871ce37ca913f42d049cefa8bf9279de47a7450e797ecc89ccfdba09ecdd91fcfbc2faf7494f18bfbf90135c3f7b86d19e9bd0cb71f9be5bf144e6fc72efd530fa7f41482ea03e6b688f8ec7f3b3da5932e7574ba82fc7e7fc4da94bce23feedd1fe55ef9bf38d2db2292faebb26fff19330ae17c1b7150f6d2f0f8651be725b18fb5baac218dff3f7aa078ffea045e6f949653e5ff957d47748f6108f2b1b61f45fccf598e7eff4c809ce4348605f9ec96f52368cfcd64fe40caff70de37ae34e18e3e73c390b2e9f85717edf9eecb23f9e15c6fb39cc8fc7f3aa12f873cfe7f96e07e8eb051c8fecbb6407fea33b25b3bcdd2bd99417f1c837f92fdac2f0bf437216d7939130ceef6c9273285fa3288cfee85218e3b53e39cff8da221778dea1b91ff5d89afb713f6482f6ed073c7fae3923c7688ffd5732cb17c3be0393dfb84ace81ad4418f9af838d5ead3499e7abf450ff4198c1fbe409da6b68ee17d78551ff03721e3c990a233e0564b6d7468becc03f77b686b1fe5fdc81550bc6fa109fef73ffc604fe3f0cb2787fa53e22b33cf50699efffb750fe30e4f95f29d87b64f21b5784b1df3645e67971a99630cae308ebfa6df7c91c4feed0ded5e806edf982f617f9dc4fb2477b8d0217f1b87e2133bff535d8e4b7f56a18f5ddb6c9ac9f2e9f1f66713e8305fd6293df9443e6fedb544518fb5186c23a7f8d1999f5335e905df8abf6ab61b4cf37b2c7f39f7ad02f561109f3092fe080fb71ea7c9e294f9df90b79be59a32a8cfd33077296e70dc5c2789e278cf5f3889ce3794e69619c3f8af225a6fca936d961ff6a2c8cf747ab6417dc89c81ecf735c93399f5bca0863beb8430ec85332e7bbdbb087c4e3f9581d9437f139bf53e5759feb334bd477a2e213ea0bf69d843c2fae51330cbd1a4f64ead7f184d1df782353afeeab30f4dc0b637eec48cee3fa59eba37a3f46af21997a597361cc1f05640f5ccf0a63feb6248cf2ecc901c687cd07b0d20bed7107f6799e443f21f3fc8cfd2b38e0fb92a70399f3978d37b0d1af752253af96b9cef3ea5a6d61ecb7280863ff9343a63e838330e6b342b20d2ee9781f648c3ea90e99fa34b6c2d8ef932773be3a9516c6fcdb4018ef5fcfc85c4f882a64cec7d6efc15e1aefdf5556609ffb95d61770e0603ea23e01877c7fbe5e17c6fc08ef1ff23cc3ce9c4c3dba33619cffd423538ffe4a18eb75babd296f6fca3b12c6f9ba8f64ce3f5e4ec2b0a7a530ce0b7e26737e307085b13fc7037b36f62f5433c2e86f36c0ca9febdf5f2d32e72b4f3570c0f33e9b5370c8f31a7b2c8f296fcf15c6fc57955ce0fbebbabf13e44cf952aeb0ce4ff39d31ff72470e71bde40ba3fc17b2991f5d0bebf26cae649eef57ea83957de0fc53d85bcee7fc61e99d757ed7285f4ec5376d1f0dd44fce94dfd2f124c89bfca59e8431ded9932370bd2b8cfc3e906370b814d6f99f9c85e12f7a64ae2f34af86d1df3aa3bde455f910fff9f75e06e5ad876487fbd3a047de942705fbb04d7ecfaeb0ce9fd521f33c2d6b298cfcf785b13ef2424ec003b41fdbec0f6cd8e42cecb33e2317d0ffd8e7c001f7bbd5793fd51e71de19febe609e6f1dc80938e50963ff2eec577957702320e7f0fc469eccf3349b037201fb5f3aa82f351ec5f923dd3699fb292cd4af639e67416fc73c6fd914d6f6542c91d99f285dc86c9f8d36b980f9e609dabb13301eadefc9dc5fbe41fe5cf33c6b248cf9b40999fb715b2361cc3fdc9339dfddaa1be6fb4d1d7280fd499518ac46b0985f989223beef0d7fef067c7f3c803ff4ccf3adb330c6df77649e476fad8471ff0299e755f58a649e3f5733d7b9ffb289f27abe39afec44e67cf10aedd953f6a4f3bb3a926dc4030bf7f7cdf3ad2399f3e7d64518eb431699e7dd970f86118fb6f7649e47dfda0ae37da62a99e7ad35cdf33dd47fd3fc7d82f16505edcff7793e408cf229f785f1dfb44df639ded1fd2fd59a985feb8eccfc5a2561ec1f9c9379fe7e7d4fe6f95995b330cecfb1c81ef7cbcd84d1fe90ff40d90bf6b3c29e02e571315ee0fd4c7d9c98bf90ef4f5b8847a1c98fbf310cfbb45c32cfff3f66c81efc49fd288cf7a77db28ffc8defc811e25d15fe2054ed5de7e7ba25f3fc80fd181ca6d9fe114f23f37c3f203bccdfa330c6032932cf1fabcfc926bfe6f721f62b37bbe488fbcb77649e87d9280963bf0dca1fa91e91beff60298cfd4f357280f9af3af313f2fdfc408f178358f2df13c678fc9eec826b1b618c9fcdef43f88bd64e18f3432fe408f5517e2027d0bb89fa88d5f801f1e25e18e3b72139cbfd6b863df8671ff59d98fc59b630c6974f64eef76c57c8fcbe42cd2507688fc54772087b29aec811ca7335ccf9e1b64f4ed07e4b287fe2a771de6dfd4118e31ffedee7fb02cdb130ec3520b37c3394578d9ff03d9ae91b99df173821fe2421fb03968e87ca5a595eeb5518df9b68907d5cf717c218efe68591ff323981fee72e38e0fbad9327726cebf28c5d32d7f7f73d70c8f75f2dedbfc38c79dea42f8cf9a71e99f1c41f0be3fde388ccf356da2b72c4f927943fa3ec05fa8ec839b4a7c33d38e4fce192bf0f038ce77ddd3ec2ac795ee74e58d79f7f2287cc6f288cfa75c8fc1e452314d6f5bb7904df56b4747ba991737cdfb54c8e79fe20f29f0d3368bf81ee0f8639f33c7f42e6fedda0228cf765bac2d02b4de6f72a6ab87fcee7fc5db14fce40afb64566fb6b0ec9fcde41790d0ef8be46157ae54d7efcb630d65397e498f9ab09ebfc4da7e484fd35d867decf522fde5f3d1feb351332d7d71b73c3e8efcf7360a51fe24f81ccf5b7fd859c47fbdd570c237eb5513edbe4cfbf90993f7f2b8ceb6f60a527ca9317c67eb498ccef6bd45ec93c5fa733269bef431485e1efebe0db079c6e3cb4c82ed69b931d384cdbd81fc8fb852efb3f297282f16d037a1424ffc86fc1e4dfbf0ae33cd1353903eeb7c859f4b7da5532cf3fa9f50cc35f168b641bfeb15d26f37c9fd20358f917cc871dc939ae0f41df4298c9a33e0764ae17fab00fc7e4cf2f0b633c1991b91fb36aaee7501fe7b130e60b62b287fe65bd42e67961e33999e71d35f8fb90efcffaf0b7ae795e7727acedabfb7e1de7d57b647eafc27f15d6bfbf5484f13ec59d61e8dbb58551bf4732d7df3a55b207fdafae30e6139ec93eeaa3921746ff21478eb11fa49a90f97ef1646c187af458de80e7dd4fe06fdcdb07146ebce0fd437e5f687a32ccfe95ee3faaee1bcbeff7c979f293b0ceff2e45b611af3b3b619cd732207b88b7d30999e749d52332cfa7a9d4c8fcbec404e5f5029ecf521a927318ef96b2c258dfaf901dee772c903dae0f201e78612e8ffe7389eca1bd56a1976fca1ba484d1ffb927dbecafecc93c2fa55621bb882fd31c99e72b1497c2880f1932cfab7a7384f5f3cf6b32fbc76594c70f78bed6e444e6fece6b99ccf3bfca2199fa9cd15efc308dfd8ff51599ef87047a7c1506a67cbe2d8cef873d93f9be7bd511c6fbd531d941fdd742c3885f9d3d997a7412b2cff8027f1304dc1f590ac839f8db5291ccf7d5065db20b3dd6f09f6a7c83fdbeb5ac30deff5a92f9fe4b007f1e9af2848e30f6ab3d901d963f2d8cfd7a27b2cbf2fa647eafa6531646fb37f7677bbfdc93d9ff389adff37caa9ab9cefd25b5ab30e64761cf61c0ef3bd47be41ceaffb426e7b13e7ee6df2bfdd09fb890793edbe095ccf7b57dd85b64ca1bb4c92ed917d67ace36641fede16d248cfe5d85ccfd3313f89b28e0f93ef58330f68bdf91395fbf467d44ca5e71be1efc4b6c9e1f1cc91e3916c6f70a7a641f5c3991b9be527a12c679804b32f74f6c1fc809f7ebc1fee280e7cb5c1fc93ecfefae09633cc9e7a91e04faeb2f6417eb8901f4484c7e832999f90d9b86395ff1480e70fde01bc6f5595b58d7777549e6fac8f54918f5639323724e18e367c4b32470e07faf1772c0fd8373c3d8af5a5e09e3fb23a8ff24e4f9ce2be8a17a47583f99e9f2476953be604166797a2b61ec1f2f92435c9f5f85b1bf3f12c6fce289ccef3374ee85d13f5c836f33baba7c2fc298dfed9259dff537616dafd384ece07ab1218cf1ea86eca1fdd61786b19faede17c6f70fe664ea591a90f97eda3a06877c1fa8512617e0cf8f2c9f7268989fd3fde72863f40a36c2582f3e0ac31e4f64ce8fcc53c2d8cfd822c7b097b790ccf9936e2c8cf32551bf19e5afb0dffc81ccf179272f0cbd3cb2cdf339efc8d47fda243ba89fe998ccf33bce7bb2b1cf2b99efc3f6a0a71a9fe27d8829cb6ff40af0fcac297f5012d6e50f7d72ccf9ad2b99f32d55d47ff6b602aadbc380ccf3cb6b637281e57b23b33cf52599e38d4a8a6ccedf7e328cf6587c20073c0f744fe6fb78e72299e77dd56be090e711255bb293c37a74cf30ec69c2fb2bf7a1af2f599e2887fdee81ee9f29a21e415518f3d5197202eef584111fa1574ee9a5f3f3562767684f7b618c279a64ae7fd7ce640fedad9626f33caa9a43e6794e67e8990b793ed76642e6fb45fb2999f61da0fde74dfee79630becf198055fe51de9130da735f18e799e40d23bfdd1699e59d19cef3fcde57c3e81f7413b2cdfdbf69b283f854750df37c715b18f65e27f3bc926983ccf366b62561cc074ec87cffb2c2df1bfd2acf64beeff7f6208cf5633e3fe4fb753bc339f8b3daa361bc4f541f93591fe336d9b7f5fd43e4c736fa06f7e40ceba3228cf3690fc2bafc338f9c83ffaf1cc99c8fa8b8e402daeb6c4de6f7582aaf649e7f3e9e91038ca72647618ce7127288fe57b5488ed09fbd16c83ccfa0f1404ed03f2b425fdbe8f77611c67eee2199e7778588970553fee085ccfd51d5b130e6b34786d1ff9e9fc939ce876485d1fed264f37d891499eb55d30c99fbdfaf2d618c67a66496ef027b28287f8cf30e9ec819ec5f2f1a66f94f0332cbfbe60b6b3d8b3199e76dd59fc936ece974243bb0bfc3861ce6a19f1ecf468ed12b9c0ae37b3617728efa6685715ebef97b9e0fdff584b11eec08637f6995ccef315487647ecfe66498df33b9e4c809ae4fcdf379be4f19fed2517aa07dbe0943bf3e99fabc79c2ba3e8b4d32df4f3cad84d1be10ef5d53def24818df1f7e24e7713db085e1cf5d61bc0fe7916de8d37d20733db87a26b37f5d49c8d4a37b20f37b2e950199dff72846e418fe6db717c67c7b83ccf3c08f8161b4cf3af31ff2fdd463da30dba345a63e67c37c5fb23216863dc17f78468ff044b6c99130daa3f93de727ab0f64ee2fb9acc93cefa9da22c718ffd53c7282783843ffd20bf93e70ad4acea1bd6d6332cbdb607e42f3fe6a8f6cde073d0ba37d2dc93cbf6b0f7bf3229ee7fcf64ce6fb4325f43f7c53de70466679c3b430cee31b90b97feb1209e3bc8b3599f3d5952699fb514a0e99e78177ef85619fe6f7b49f2af4f78d5eb30599e73155ea649eefb57912d6e5dd94c87c5ff62d258cfec84618fd1fe6470d5fb03fa1288cf87d2027580fdaa33df811e73f43943f30fa5c03619c275f22f37cf0b0258cf9cfa230f69b940df3bce41c99f3b5952c99e7f1770be4107acfdfc8fcbec0cc25c718ff17a1671066a0ffe5228cf3459ac2381f6b45cea27d565fc839c4939ab91fdfef7de3f38c9e9ba930decf36bfe7796f9bb230be37704f8e7338ff0cf61e447c5f68f544e6f79e42f897d0e8d9778431bff446f6707dd71486fd56c8fc7e436f4be6fb5e074b58d7c7fc85ccfd0b17d467a8ec15ebc919720ee381699accfee46c6918d78b0732cf6b5b3f0ac33fbe92795edba6228cf362101f2253bef04918fb994a641fdc8fc901cabb9809a33fdb26f37b155de81985698eb703b279bfba48e6f959b30ab900fbb81ec80ee263b540e6797b6f77649eaf761e0a6bfb583f09233ecfc93cff6d9b08e37c19f8f728cada986f447dc4a6fc615518e75d44e400bcdb08633f7e9fccef17f45d32cfcb3aa3bee23087f654dd92f93d949d4566f936cf64e6773323f33ce44b5e18ef639bdf47b8be2e09a37ca8afc4e4bf3c14c67ac42b99efe38459619c373013c6fe9f3b615dfedd89ccf3c02ad02309393fde2b9133f02f67de5fe901fbc80943cf0d398ff632ab921d8c6f6b0fc2bafcb306d9c5f5b7a330c66b65b20f7f5f0bc83c9faf6c0be37ddc1732f5dc5485f1fd63963fcae4705e06f3739b31d0bc358cf5d637ea11713fd05bde30ee17eafa88d346ef2826f3fdb6f05918edb1248cef774ec89cafdebac2daffec06649e77562d09e3fd18de3fe479dea54818f1d622f3fb25a58330e28143e6fcf5ac46e67969b3ba30f6f3bd92793edfa6268cf9c93732cfd7dbcc85313e66f9a24c16fb4d75fdc419a34f9823f3fceca82b8cf7bf9664be4fba1d0ac35f5fc121cfc73f3f93738c8fe67a9ee33373bd80fe6baf2c8cf1e480ccf3dbe62732cf579be5c93ef429bf33bec7362507e8ff55cdfd22f8bf6b931cd35ed364ea77b913c67e410f1ca539ff7d26dbd86f1141ffacd12b74c99c6f8f3686791e13ea331b727d703125db982f1bf37ea16363ff4b9bccf7094eae619e3f5520f37b25339bccf3d4aa13c3e8ff1e9ae408fdbb92e118fef42d20f3fcc72bec3b1bf17cc4ed5818ef879e84b17fb341ce64f1fdea8c615c9feec9f93cc6373adec639d16b6a187a9db6e030cdeb29c36cbf67619c4fb420f3fcfdf35118f67525b3fdee53c2da3e164532dfcfbbf8649ef75deb9303d8db7c47e6fcf67c2f0c7f6a9e1fa17f5d35bf8f698fe6f73c1ff18af698537aa27fd525f33ccaed4418eb972561e4272267b3388f29318ceb11386ff48bb7e40c389a0923bf4761cc5744e42ce3754518efe3af84f1fec02399efefee86649be3db1e99fa9eefc83c3f70179079fedc2c47e678716ef267f43f08633f1eec2b1ff17b41d50b390bfdb66561bc0fcbfc44b92cbe475f32ccf30166c278ff7c40e6f75cd6d4578d98b1be81fcd846cfe84ca67ed15218fb7777649ed756aa0ba3bd36c8dcdf536e91791efae24518e3abbd30fa3f7d32f71bec3264fa8bf996ccf3d0e64fc2d87f8efab18d3edb8a61e831de90f339d477c130aeaf9fc90ed7d3dec83c4f2cc2f30ba6fc714318d773649e67b35c9059fe7285ecf27cae26d943ff683f24fb3c3f7642e6f9763bf33c9e3738bf23f37cbbf9bdb02ecffcc1307f0f7b2f449c5fbebc08a3ff7a2473bd3f89c9b92cfa1fbe61b4ff6a8accef876d1f85f5f3dfae86711eddd9fcbd8debc7a2615cdf4ec905e8bda90be33c55b40fc7e87b6a0be37c558bccefb9473561d843208cf5821732df9f3cdbc218afe5c93c3fe67c2673ffcbe24a8ed1fe2f0d61acdfa33d3a519afb89eb649e0f792e90f3f09f3b874c3d164b722187f7979f0ce3faf14518e7df98bf776cbcdf60d8b375fe56d427e2fa5b84f2b9469fa847e6f7e24b5361e8650b6b7bde1fc86caf8b6732cf6b3c7584313f0b7fe01afb5b8cc99c7fdebe08c3ded6c238efb54ee6fadaae2f8cf5f17b7201f675691a863ed31dd9c961bc73308ceba7a9b07efe8ae5512370bcdf81e779469fe85118e7650464ae0f2d1bc2d82fba12c67ccc9eccf9e873420ed0ffbc1cc811faeb3bf823efb642acf5e0fd6f2ba69a5fc99cbfdf3ac2785e48e6f77af6cf642787f33e5386a1dfae4776a1c7a92b8cfeca92ecf1fb400d61ccdfbc90f9fdfaf598ccf7eb63f817dfe815d9c2381f794f76c1a509d9c378f43a23733fcb7c048e728c378961fad33732cf875b9c85f5f5edfb75e8ed910bf06fb50b99ef676e9fc93caff25a34ccef4fba64ea5132ec834ff7e480dfdf5b90f97efaba2c8cef2dc07f06468f534b58e7777d22f37bf771228cfa0fc8fcdec5654e8ea90fca17183d7621d99ca7581446fbdf9179dee87945f6d11e2f23c328efae2e0c7fcce79bf87a5d08a3bf32212788c79bac30f4413c0d62bedf7e417f2734e54d9664ee178a0fc2581f9b91b9deb12c9079ded4b92f8cf7c1eb64ae6fece14f43d5fe30fe5a096b7b59cec99cffbf1ae6f99f8b2732e7ff67597281fb4336c2d81f1a19467d2c1e8411bfcfc2183fdc93591fa5bd61e8bb33f9a5debba530e253400ec1db2a39c6fb676bb4ef30e6fbdc9ba561ec3f7a833f8d8cde71931c70fd6d238cf6ec9343702d26f3fbf1e72761cc67d584e1ef613fd16dc555ebf348e67ac0ee228cf30c0f649e5f5a7c1186ff2c08e37c54e64ff41808231e99e747e02df3a7ec15e379e81d197d360d32df7f2fc2be62a347fc4ca61e7b5758d7e76a40e67aca3914c6799c0b61cc173c9063f011f61bab7882f3d827e42ce3f55618fdbd7b720e5c6c91a9efc9267b19ec6731f7e3fcf6b94c0ee02f963b7288fdf0d50399e72d6cfbc2d0f3248cfb8dc8dcffb63d0aebeb6f7c7e9cc67968db1999fbe3364d61bcaf9013c67963d047791bd8db7a484ed07f3fc39e9288ef0baf36c2b87f200cfff74ce6f7538a4761d8d795ccf36d0f3561f8ab1299eb31fb1199e7a76f8ec2f83e90b96ee7707eae6d18fe681a930be82f2e4e6417fe679b16467ff7de30fa3fcb3199e3e1e5cc30dad7722a8cfa7811c67cbe2f8cfd665b7290437fc29437b631df60f2c3fadd197d12f0f6208cf91df46f9398e74fbee9f69ca4239edfbc3e92b3a88fb73d99eb8f2787ccf59bca0b99e7f7ee2a6487eb9343b20bffbcaf0b43af1ad9031fd6643f03ff71350cfbd98d0c43afa5791ed743aa0b32bfe7564bc83c5f77db23538f2dcb779be1d5f6f62c0cfb617e62ee6758e9f697645479719ec7985cc0fce639270c7ff24476781e7b4cf6f8fedb82ece37d80d2a36194e76d4b0e303f342b1bc6fbb4e7bc61d8d3b22a8cf63114c6f9554b61f8c32399df0fdafbc228bfe198fdb717619cbf6bca9ff07bc14d61d427ea3b739b51d7e5dd09e3790d61f8d71239c3fab184b1fe5115d6e5591be6f9d247ed7f926cc4f591758accf364e30cd9cf607db36a18f53b0ec94106f35343c378ff71992373fd649917c67cff011cf37ce0bd47e6f9b8bbaa30e6735e85717e4f288cfb0dc859daa3258cf63f17c67caf4dce81377d61f4c7d07e73aafe311e590be33cba1a39e47aec4118fe2b4be6f715f68161e8bb7b12c6f8ad47e67ac8fe8eccf9fb35ca978b79feeeb64b667e3729721ee3cdad611bfddd25da775e85437099ccfde5ab1e99eb0713733dc1feab63ca30cabfea0a23fe9c8575fdad96c2e86fac84d19ed0bef331cf7b5bbd92b3d86fb39b1a467bdaecc905f4d7b75bb28bf8bb805eb60afff8bef19a9ce03c8f37db30fa2f2bcf30fcf3be208cfdfb15c39c4f801e76ccf3f2778fc2a8afb930eca72a8cf1729e9cc5fb98eb3d3987fadccdc81efb173d61f427985f35a2c3fab0e120c2f804edad104569f4d7eae418fda9e9c630e637f78f8673a8cf0539c17ae36e028e4dbcdb91039c87b3ee9243d8df366f18f959f785114fcddf47c8ffda15d6d7d73d61c473d88f13290175fd1e0de7f5f5dd9d61bcafb13f8163be5fbc7f2367f3d0b345cee3fdce6d8ecc78b376c866bdbd4b0ee13fe668ef6e9430dec686f93e5f048ed3c8dfaa6318e769ac5fc936ce075a6fc98cd79b77c6f861230c7f65eec7f5bff58c1c217f2b5f18fe22278cf23585919fac30ecb126acefbfaa1a467b5b45c288ef8130e6bb0ac2b0d7b930ec2b1446fdd785315f5e11c679db4561ec676808239eadc86a44a07f6f0bebf2adcac2385fb7240cfd968663e477218cfce68591bf9c30e607509f9eb1cfb84b4ed2e85f207e7aca1e30de1c1a467f79db2467f238cfa4268ce79dc93cbf70dd21bbf0d71bc31ed6b7372e99df5759b4c95c5f5a3585a1df4618dfefe908237e3f0bc3bebbc2881f0761d8cbd830fa074b4b18fed915463c6d08a37f7b15c6fce751187a3864c6db85278ceb1961e89f12867f490b438f4458ebb13c09a3fdb485a1c79d30ecab2e0c3d2ec25a8fe59b618c57575961e87316d6fa2c9f84b1fe732f8cfa6f09a3fde48551fe9a30fccb5a18e537fa44e0e58330f48885a1c7a330ce6347fe7c65dfe8cfc586b1ff62cfeb7106f1e61419c6facfdbc030de5739150cc33fbfed84e1ef9be46c16eff72d0d63fd6d6eee97c5f87fe51ac6fe8259869cc7f9b8eb29d9c67941fb19d9c179526b931f9e07b31b93f97ef0a6208cfd1e4d61cc1f3bc2f0c753617dff794b18eb6d5d61f49f5d61ac77b785b11ed731ccfd0159615d9ec58accf71fe69e30e6a7dfafc3bf4d84d19e52c298ff5e0be37b599630fcf35518e799fbc2b09f1761f8978d30fa0b6d615dfecd3b637f534f18df57df1aa67f2b08a3fc3b61c4a3b430cabf14d6e55fecc97c1f69d11246fbb285d19ff785a187238cf767d11e82389dc57c5642cee4303fd61446fb19926dbcff3e5f19c6fee15944e6799db3b130f60bc5c2783f2911867df685311e7e13c67eacb430e687ef85b1dfe62c8cf76d4a64173c5f0a63bdf2228cfdc0af86d19e665761ece72b0a63fefa4118ef732d8411ef1c61e8f1280c3d5e84b51ef344187a3e09c3dfb584a1c7b330ea632c8cf63a17463c0c84e1ffdfcb8bf52fa317df1f9e4f0cf3fdd88630fa533361b4dfa930f42808637cf3fe7bf8c39e30e633d7c2b057cc0785713a83f3ba57e44c5adf7fba368cfeedb2248cf16b288cf1d3dc30e65b8e8fe46c0edf9b7b13c6f7113d722e8bef7108e7c06561cc4f3c90f3f8fdde36ccfebc611bebb1fbc8701ee50985b55e134f18ef4305c2d8bf9f12c67e6c5f18eff358c2181f6c84613fe6f7055c9f5e84f1bda0a130ceb37a15c67cea5518eb3f7b618cdf8ac2babcd3aa61bc7f372d0963bdba22aced695a16863e2fc278ff642a0c7d46c2d0e75918fee6280c7dee8451fe2761ccbf98fa74c0d37b61e8f1280c3dde7f8ffed64518ebd51961f81b539f0ee613661361f89b9330fc4d288cfed64818f38d2d61bcef087dd4703a8df773ebe46c1afdb94818e3f9a2e10cfa3307c3880fab36399745fdf58411cf06c2f007b130fc415e18f61d92f3e065200c7f79278cf3cbdeafe37d8a8330e2992f8cf9204b18e7c1a48411dffbc2f097b630f657ed8561cfefbf47ff6e6718fba9272d61ec4fd90aa3bf9c16c67ed29530f4580ba3fcf7c2e8df885e88cf930dd9e6ef2d61e8d514863fb585313f990843af8630ec7d208cf7b72bc2380fa72a0cbdeac2383fa8268cf929d730c78f9130d67fe5ef317f3779ff3dbe7f87fe63acec15df334884f13d811761fdf7c9b330fc474758eb3d5d09a3ffd310c6f75727c2189f85c2383f6f2c0cffe109633dea3d7f38cf3216d6fa8d2361addff8248cf1c6fbefe12f0e8633f81ed95118fe2245ce65f03ef14118fea228accb3fde0bc35fec8451fead30ceeb1f0a63ffd33ba3fc5d61947f208cf2f785d13f1b0963bf754f18fb174d7dd1df8ce5fef037934858977778f8792965a57c95822f52a8522429664a741a7f912692a69fd24ca7b94e0b49cb2fd24aa7f55769a3d236b5fb2aed753ae87454e9947a4b9d5397d4f5e728433dbe5424fca448fc838a7c4b8f5f5364f5bb14397c5004a9982aa5caa94aaa9aaafd31657ec142fe1c45be6d217f5c11a47aaa916aa65aa976aa93eafe3e659422dfd2e4ef67234611a45eaa9f1a288b19a646a9bbdfa6cc6ff2227f9e8d7cadc71f53e496ee557a108b794c3dfd9832bfc3affe3645e6bf5391cd0f28f24b7a40915b7a4ebda45e6931e9d45d2af3cbcafc0445fecc48f3b5225f469a1f5124cb944be5df2d26657f4b997f4ca4f9769bf95211930aca621c65319d949bcaa4bc8fcafc45bcc8bf5b11955440b12cff663156a0bcafb2184bf79cffd65ee44b45bed766bea748ca8a748aad2455b1c6c662ac8956e69f19697ed9468c22264d532d6b66cd53236b612dadd05a7d50e63f6f233fa2c88ffad51f55e496d6d6c6da5a3b588cb5b70ed6c43a6a65fe8991e6c714b9a5934a6fcac7e81e1e2de66c5db4327fd748f3c71531e94a8be9d2628a56492bf3cf8b34dfd7e3b322b754b62a56f593c5acac8ba547947f212ff2b322cd2f5b8851c4a4baf2be1f7dccca2a590dadccffdf6cc4a4a64e2dab6d469156c7ea2a6594c5583d2af3d78e34bf4791efeb51a61e487d6b600d3126b0f6aa87375216736735ac7badccbfa77ff6ef8b34bfae48ff437aa0c5a81e9e58cca3a5c7daffb848f3638af4ad679d5ede4791dafb6a8bb15eb5327f7d2ff2b5223fee45bea7c8b395b63256d6caa58656fe0b8bb12d7ce1f29fec45bed6038a98e4a8d152708bd76231aed5b33c3fa595f9277b91ef29a2926fa9e4bfcf3bdcc60456cd0f6e16835d957f9a8dfcbcd8fbf36cc428724b911ffbc9c751a458ccd89f8832ff8ef9c4bf828d40915b9aaa34fb38ef601dfdb9f2beb6bff0437fa995f90f449aeff8d59fa1c8f7f4f8a808d2ca5f7f1e13f81b6331fe562bf3cf89343fa2c88a69e7effdc38751a41e13f8476531277fe9bf8932ff442ff22d45761fd2996302ce3bdcc604d693b6988baf576fffb15ee47b8aecfca24ea5ef5accd62f6b65fe4eb137fd59915ff422df53a4e25755aaf9758e2243338aa4c52cfdabdf1065fe3a8afcfc566314319a2035fdd697a3486d31ed9bc5f81dadcc3f25d27cdf463e2a62523795fe7214e9f78cc5f87dadccbf3fd2fc3c4556bfda6abe4c037fa8d3c8ac2c7db098d42d5efb777ec7bfffa0cc9f3557f467b699d5afb699af15417af01fbf1e45de7a78da629efc67adcc3fd18b7c4f11935e52e9af4791b49857ff1e6f7bfc7522cdef8abddff62283ef2832f4337e56a5dcc751e46d4c408bc9df7c8c6fe3bd949f34e7fcd7b611a348d6777472bf1a451a8bf1825460e18dbe7f44a4f935459c8f290882f0f328d2584c1029efab2c06ef2afe61457ede98e6d7fb673faac73715519a242a8d8309c7042bf6f0388a7cb798607a53e60f459a6f29f2eb6de64762efef6b33bfac08d22c98bfaf457e1813188b490771b0d0cafcf3bcc8473d3e2a62d2f26631c1eaf3283258fb8d20e53fdf2c26d87c50e6af1c697e9317f9c242de15d932ed829bc58c308ae49860893141705016730c16c1492bf3d7efaffe1c45b6c19b4ee7e0f27914195cd1c3138b29067ac5ff2f30eefd1936f275abf9521193ca5f5a4c5081c5dc7a78da6236415594f93b479a1f55a416d4556a044d3326085ab751a4b118d5c37b0eda414729530af41ef23f713ef18ffbd51f8934bfac08f440eaa9d4971ede57a3486531839bf70d86c1482bf3378934bfaac8b72ce45d1193eebe9877083f594c0c8b09f458fb4f8c343f2ff6fed636f3a51ebde041a5c7e02978fe302678fd3c8a7cb798e0e58332ff0c2ff23d45905e83f437469157d5c35316731b13688bc904f738a9e5db9afc6d22cd8f2982940bf2b498ef8c098cc5e0cc993fd1467e66ffecb72bf2fa2115747202f7d328528f0974bcb619af3d58cc4765fe3691e6f728c214a66e16135a1f2d0663026331a11f06c14bc873dfffd691e6e1d7150963a6241c7f1a45869f46917a4c1054c349380dec70a695f973c7343f36cafb195ee47b8ac4e15ca745b8fce55164e807dd7075b39870ad95f9fb469a6fb799cf7a98b409b7664cf0be7a7debe1bd8f2283a1b298ddcd62c2bd56e6dfa7c89feb45beadc82d1d543a86cbef8e22dbb751242c26c886a7701dea15ffbf6da4f93145cee145a56b58fc348a4c7d358ad416738bd7378b09f558fb4f8c345f2bf2a51e7f9622672a6252592ce6f1d328f28916a3c70461c5584ca8c7da7faf48f3ab8a5cbe916a615dc604f761e3cb3141d8bcf5f0c4625a61296c6b65fe9e91e64714e9845d9d7a611f16e307d6d387b5c88f6382eead87a72c66a0da52351c8a32ff692ff25b23cd8f2932d2e92ebcffde28327cf83826081f95c5acc3a7b01dead5db3f4d913f33d2fc882226f5c2979bc57c5a59fad262f498207c0dd3ca623261166759fe4523cdd79a7ced577f5991519857c90e0bb0988ffb136931ce278bd13d3c580c4ee1fc6991e6cf8bbddfd6e3971531c9c37b913a5edb5fad2c7d18452a8b99198b89f41ef2bf5da4e9fc902279bd58e447c1d7a348f4f0be1e4586eb288ca2280e5d9c5cfc1322cdd78afc9991e64b45f2df5004691c4df44cd5eab3c57c3def8051e4ad87a72d661a6144f99f8834bf47915fb790774590e6d1e2c35ae4a77907338ad43dbcdb98e0a3c5a422bd87fc3fde83ff3d7ef59715316915adcdfec40fa3c84f6b91eff30e62319b6816e93de47f552fa26a7cf7438a7ca9c72deda343748c4eefef45727fe2e5f328f2a3c5846f51140ea3376531e748bfaffdd78c34aac4d7a8f81b6dc46872884a2a95a3ca3776b4de7d5a8bfc388a54f13aaaaab6a42c26d26f25ff876de43bad26aa478da819b57e838d184590da51e7e3ca9255f86adee1f328528f09a22e2c26ea7d50e62f1569a27e348886d128bafb610bf932dd470fbfb01679fc6a14f9c962a247adcc9f11697e6b0ffe539b899ea267a5cb2dbdfc46455e754a479928abe3f5af8c22693183dbbc4338bcf5f094c5e4a23cbe04f1978b34f3a8a05571542afc0645d291cbe4c5a90ffb13bf9877503dbcc1b74791b098d88a1e635f94f9377b915ff0abfbdbb7664cba7d18f09b5ee4db8a30c591b53716f3ddb548efeb51a4b198388e751ff8dfea457e35f6c6e378a23599c6b378aefeffe23728b2d46915af6131f1e6eb51e4e7b5c8cfa3c8781bef94f77d8cf7f1412bf39fb5914fad253edebe0614cf553ac56ff159a5cb0f2bb28caf71312ec5e5b81257e39afaa7aecf81eba43c3fb8594db0567129fd6e31ba87f7d16266710316133745997fcb98e6d7fa22712b6e6b35ce7127eec63da5cf39eeff80228378188fe2bbf83e7e881f7f213da55a29d75a299dae41574525f4f09e540fef59f5f09268692c26d633573f25d2fc2145e047e297f895baa4e34c9c8d73713eb6e342fcf25d451c95dcd84b5289f58b8a7c9512df6aa8f694fd3c8a54f13a9f04378b8132ffa131cd57912609a14b12dd4e288eb349928c93894ad32ff48022836496cce3fb64f1db34794f29d7bf0451f8a05a96adfa78296d31bd789f2ce3672af36ff322dfedafde7459256b652176b249b6375db4363badccfe831e4a11a5c9211e25c7e4f44d6b784bce2af91fd225b926d7f8092929aa54d2a96ced95f719fb0bd5dfb1838a8a5959d5a6ec9bc57ca5cc4f8f343fd85f7d8dee928a56a19ad492ba28d3d0ffad6914895f9256d24e3a4937e97d554e55d2a4afd38069988c9291f2c448d5b86a25711d4979669d923b7f6c79d6bd8aed35eb12ac540fa81758e11ea383ff8c8d7cf4abc97df2a03598248fc953f29cbca89428655e93749249b2492ec9277652489cc455e5bda591292d52e2bd9756a7f238a5bced7b7274aa30b5250d952650a6641d550f2854f17eafbcb48edadfd4e347faf0bfc98b7c3ff68eadb10f5dc6c13864a726198fc793f1743c1bcf555aa8b27e28a7b5fb5e492d75b3d4f03d59732b50515b92954fa55323a6c75b524ad8aacf6394e95acbd493faef9d2f94f9099166bc5425ffe531cde7383318afa8cb7abc196f75da8df7e3c3f8383e8ddfb432ca6f28456e25fda29caaa4a34f25edaae4ea74774bd642a73d522a836475acdb9b07cb949db2ad8352a5a7c61077aa25dde6463bd642ddcb28f3f322cdd3f8a2d2755c1c97a29764352e8f2be36a723fae7d2ff28eebe3c6b8a9526bdc1e77c65d9d3ae3deb83f1e8c87e39156e64ef9d1be3ec1412555a74f5f9454caa94baa52ca5365ecaa144a1aa934794fdaa7e8a4fbc935a38cbadbbb323fd58b8cefd92739df22cdf841fd5ff5efe3c7f1d3f879fc327e559aa5c719a547769c1be7c7f6b8a07571c6eed893231ead893f0926e1249ac437639a4c265365339d0f251d7d2ea92ae7d95a7d4845d5362e26c51b55ee5b2a31d54c52aadcd62d1fa9cc48699a51b636b2e65a999f1b69dcc9eca6083dea5749ebc034992b7b716e69b2982c27abc95aa58dfa6735d94e7693bdd2e830394e4e93b7c959b59c857e53ebd7cad9784f7ea0cadc93746fddfb47e555909e74f254b21199d4dd436573caff2a9bd1ebda7fa477f62dbf3ab94cae506152fcb622da463ea44969529e5426d5498da9aea831692a855a93f6a433e94e7a2acf13a542ef7349a59cef257dbd257f714b56e19626fd495ff55a6e29a5d349f5626e8b4cd75bf24fdf5026fd51991f54e487c6bd93c158f54426c3c9687237b99f3c4c1e274f93e7c9cbe475929e6426d9496e929fd8a24b61e24cdc89374d4d2d267f1a4cc369a4146a4ce369321d4f6febd21b3dffcf12fb3d9d4c894f925489d5c8fa635a7e4cd3a97f51e36e93defc2563f605315b7931577977adccefb4916f461a78d6e96c3abf59c674315d9a5edb2d4d57e378ba9e6ea6dbe96eba9f1ea6c7e969fa363d4f2fd3ebb4382d4dcb4c956955fd97cb343571a7b5697dda9836f5bcdcaf9733afd3f596d4c87aeddf3195832888fc8624cf7f55a9a3d2e973cc56feb7fb41993facc897639a690b9e75dabef5d8a84b47f5520ed3eeb437394cfb93f574308d946d0c9526e5e9687a37bd9f3e303d4e9fa6cfd317a5cfabb2a4f43433cd4e73d3fc7b39bf28e97b396fa9afd3138e3df6ef9182836fabf46c526005969ff66dd5fe6ecae8c8a494b9c56cd59bb1c65a99cf7afcd87ce27714419ada2a156e2d65ea4c5df6f45fa6de2ca575f127bb9935a94ec399af5479519a3ccc8259388b66b14a89fa67ac68a214ba9b4da7d5d96c369f2d66cbd96ab69e6d3e95d3fe58ced05125452aa831505b259f69101c99629da626f97de5651efdcd7bccbe45a64fcafc607ff59bf3009f15619a6d67bb9b36b3fdad45a961f3647650fd938eb697a6f2209eb296caeca854392925de66e7d9657655a9a8feb9cc4aeabfc4b393ba7a9c95952a955975569bd5678d5973d69ab53f95b3f35ed2b039bbbdb9f5316d3e254fa51252e807a79bff5511ecab989dd2bbd17eb722836f2ad2459ace66bd597f36506998d4554b8ac6fbd9687637d9cfee5514f2543b292b9b0894a5bc29251e668fb3a7d933d3d3ec45fd97d7595a6916cc32b3ec2c37cbcfec5981c999b9336f9e0a4eefe5d4251dea54d529a35217490d1146b7144e9082fbb0724baaad2d541cff2a668b323fc7464491e96cee33053765d4205e0d1a6e2d691eab1eca6a525171e7a6cbd34c757d94753ccc13d5d19dcca7f319d354d17cf6345fcc97aa759da6f7f3d57c3ddfccb7f39d4afbf941a5e3fc347f9b9fdf4b6b52b80a57c18b49e16e7e09b2ef297cbca5c00eecf9d54fa9087fffadde8c3513657e21d2fca88d881e2615e7a579795e51a93aafa9d190f2bcaa7fd298d755d4a9685d12652d8fb3e77943a9d19cb7e66d953aea9faea29efaaf6375f575de57da3ca9bf18ce07f3e17c34bf9bdfcf1f547a54e969fe3c7f09a35b496f293c498ac2687edb2bf49e06e1fa3d05f72ab67106c29f2366abd1c5132253aaa295f9d548f3b522468fef28724b699532f3ecd8d1dae4c683797e6e2bcf5b57d1a6a8bc47a0757999cfe785b93377e7de22a50645babbb248cd3da58f332f2c02d5ae2eea9781f2485515c56b8b70112de245b2187f4893c574719bb77c4f6fe15b14864f3a9574cae8548df4ffe6af2aae877e4ff5056ea3c9f719081d9944991f8934bfae48f193229216f39b328bc562098b513d945715711e6663d5521e952eb3796bb15aac179bc576b15bec55daa97fdba8ffb252563453da3caa5f8ed55f4ca7afaa47d85077392c0e8be3e2b4785b9c255d16d74571719bd3bda5e12d45711487cfe17354bda530871475c32715e3977ea87a8c1f95416f464726a3cc6ff122dfb190af14519a9455aa2caa8bdaa2ae5263d15423a29bc55495e73d29effaa0bc4b4195de5376b25db416ed4567d155a9a3feadb5e82d7c65392df58bf17ca17e7d52d1fd6635f5c97ad15f0c16c3d961315adc2dee170f8b47494f8be7c54bf4e1330551126fa3d987745ebc2acfbbf5db2a2e4199dad7313ba5bf63f7bbbd88d1e3db8abca7f422b3c82e728bfcc2563e66a87a300fcaf3be2a0fdb983bca5efc4561e12845dc85773b4254fd5f6bd15ddefeabafeca6a97e756b77ca6ad45f0ea7d1c29eec97c178304b2dc3245946aa4a93e57839594e759a2de7cb45dcb8a5e8f221d554ca47f9c56ddff8552933512389c5b762b66a4d6d6ba795f9bd5e24fd951e9f14599affad96ebe546a5ed7237f1967be56326b3371577e6cacbb655abd92e9ce561795c9e966fcbb34a17f56f57f55f7c65376be58166ca133fa8d81ea87ef1751aeaf6148cfbcbe2d4430f7b595a9697956555a5dab2be6c60bb616423c57b9dfcd88f6cd58eee55dff9aac6141335da5abc8f26974dad0c6376aaa5bf6df8b36dc468d252a9ad5367d95df696fde5603954a3a3a7e548c5ea1715991dd59236aae574952e77cbfbe5c3f251a587e593a2ebf279b15fbedcac66f9aa62d47916eaf6144c2acbf4c41ff7c6fb719cbc8c27cbcc32bbcc2df3ea1f7b59489626c5079d9a262541f0a2eca5a3c61679e565d42d10b3756492986de553c377657e38d2fc988dbc2b6292b374979e3e68d752fd9847d57fbbb5a5e9bcabe2d176d15e5aca4aee57fe2a58dd3a23d12a5e3ead92d558b5b1de6ab29aae66ba3d456a64559afacad3ac26d6b8a3fa01d16abe5aac96abdbffd6abcd6d35e47b299aadb66a149152bef7ee36c25623f2b1cc8ddf560da00c633694f91d8a7ccf42be54e49656abdd6abf3aac8e4a97d3ea6d755e5d9497b9aa5ecc6cde5e5d5524ea28fbb82c1f942ec55569555e5556d5556d555f35564de56bd6aa77335db594966335d2ac28656a93cd2435edaddaabceaaab55e9adfaabc12fa8d20b4f41ac465337653ca5cc5694e97d3103c1981debd8f49b22cd2fb5992f1519ae462addadee952e0fabc7d5d3ea79f5b27a5dddfa274515717af3cec25fa597cfabcc2aab2c2654aae456f995bd2aa8167559392a4aed1696f23405ad4cac95b16eca2cfa2b77e5ad602ca9b5f50baad8e15e8d97a6c1518d346fca34a0cc87987df739666b9b298b327fc48b7ca907145169edaf8375b87a5847eb789dac5ed6e3f5643dfda8ccea6a9459cfd6f395bd5eac97ebd52a58af57f5e575bda1cd143ed88c5266fabadeae77ebbd52e5b03e7e5f95f54945e8911a5315953283db9c83df57ca5cbf15b3adae991bbff5665203adcc6f8b34df5764f4392d9df5dbfabcbeacafebe2baa45429af2beb2a63365a93abfabb85f56679556da7b6aeadebebc6bab10ad7cd756badbc8fead7f4547fb8b59aa93126fc4c65b95f77d6dd754fa5fe7ab01e7e579766945223a4891a577a6ac43d0dfc0fca5c4cccbecd8d87d6c7de8c35b7c6d68ecafc312ff29522b497d1fa6e7dbf7e50aa3cae9fd6cf4a95cafa65fdaa6afde6812fda03b75613557667355ea795d564d659e57dabebdc3aafda5266d15ddb8b8deaed35e713354628a9d8f4b42eac9db5ab75f136a98db5f137c13775b1d4082154e3c9891a755399d0b945261db3972666ebdecc457a337a34798b4cefcafc0c1b41da849b68136f92cd7833d94c6faa6c669bf966a13a34abcd7ab35171f7490def4aff5f7b67d69cba92b4ebfbf3330ef704b6c1c671ae34829090846c03e64e12b319344f11df7f3f59559a1106afbdbbd7eafeb633ba7774b4bd8d1f65be3954a90a6afeed970089e5f3d4013de99fd650cdbc83e7742167c7c73664261162a905fd930c95cf01ea998e655b0ef116cb052ac43cab1e4f3a74027be82f1fa1e7d68c8911009995211aad4ace6e9e40740899d500dfddfcb7f8081031adc00aadc88aadc4a22cda622c16a870166f0d80cad012acd1ce3cbabb39aaf4706fb04779db42bd808496d22077bf9dc5d3faf07ada9c0450a0cfaf5794b32196284bb2480cc9966c29391764aa354933d37a199b7de81ecf1919d300321690f10db13967e36a26cac948680251257367a66926a2617bb3de5b47eba3e55b536b0654e6d6a7b5b03ad683f508549eacaed5db2dd3ee00e2e96bfdb5b59e213fb5101bc8d063f093feb175da408df7029e649db6a0befbaf2febc5ea9318b25eed76854acb26a6db06f4441cf4921cf4d8988cb93359636cea95ccb4a8e4ecca4a7f31810032f8dddb7b33cd5522a9d968edd06ff1f6d25ed96b42c5ded85b7b673dd97bfbcb3e943a4ae435d409b4155444c29df6f36104fdc00b740aba7d84fa4fb74f5f7dfb0cf5efe73eb62dc4c5b62fa838b66b7bb66f07766847848c19021934b1f934de2fc8e4393b9f4064b3f163361b47393b25f3530fa911c154623bb129f4e603b4cd34c4504685b1599bb3797b600f6d219d4220af99daa37d04ea31c76c44fb74681d3fc177644bb445e824d10c620c0ab385484abe145bc264c617645c5bc664145b05361353341f407b814caabf0f067db2cb39db38e36e32ad66b2d9787502d156577c8dcc3d31532712db1ab6371bed6137ed0f509682cad49ed973fbd35ed81d34b9c25e63eca8dddc7e40131aeb054ff4e4d30e7acb937d3a6dc1535eed473cd57b3e2ea02ba0c0bb4cfbc9ee9efb76afaa2f3532cff68b8d54c63555f099c78ccc45cecefbeccb0944cb407d7689ccaff8484604e56730fb15bd3de4b49d56998aa33b86bd704c67e9acc8b4d359ef967b7d47e3a9de064d81f7b383019eb307cd7901422f5f8283e6365b32eb445c765367ebec9cbdf3758d8c73b055e7e89c9cb36301990e7403487f4b399b64a66f73f65b91b30999fb7d04f388cb3c883910ee8eb3ebea92e396a8788eef044ee8444eec240e4526e4c7ce4e85884293e037f09be5beb35f80e7ccbe360703688039f4d717d4bc9f90bdc87c7cbadf390cea031cd6e12ec8780e4fc8d8cfc066e00c1dc1dc9b8b7aced6e769ce4e2e73369a405473f65aab93f90522b6334276941cd1919c714e45761447752687e971e768ce5bb6aa82238adb99c086c6ab4de67eedbc9f9f212fbf808f7ceea7f0cf0fd096e33e042d329c8fdde7d175a6c065b69a3b73e73323e32c52321d4c46b189ee28ce83d9c1399bc564ac8c0ce86f5acde0ccb4a8cec6b39c8d33d3603dc9c8dc1b33251e2991d41e9d27a7ebf49c67e70553e93baf6e7bd9765b8ee6eaaee19a4713afc4c95b83ac38b94b6043ed77eeca5dbb1b77ebeedcbdf38e0cbc28dcaf8edade803e690ebeb5d8e9ee17ea9150cde21edc63295fa7646cb71c67ee09b25480325356cda47df622cf4cb59c5d9d40acb835ae817f486474412435f7ec5aae8da90c5cc7759747d773fd63f77472836cf5f6805729b7eef6ec866ee4c66ee2522e8d8d71d953b40fc0b6f6c3ee037aa4f98e39baa0495098b81cfc5b7852e39e547700d55d91b365bb92b1dca1d37105c852412567f74a393bc827106f2daabc3b0f6526b437bf4ee676cc341219a526ba923b766557715577b26aad0d5773dfdc77f7c39d162bfeeecc9dbb9feec2edb80f2913da7d4cd93cb95d88b0f94e83bc4e43b51c6f5fa13654dd9efb0c645eb2fadfed1f7bee6b4ae6328f07ced16b7b2d88a6574fc7fa8bc9e83392b30d36efb3fb0d39fb05f43725738f8a5c12c97960f30ccff4964006cc5b796b6fe36dbd9db7f7bebc83775c8d578fdec93b7b9607ceefb91911cff37c2ff0422ff2622fc14679f42e81f889a122443b68b843efd8f1188f3d9d3caedc1d79bc37a852c1171ef3cec1397943c7f3046f043c86ba94e6ecf772ceae4c20b4cb9cbdc67764fc4522c84430c94dcd1b7bb2a780a9e965ef9af7e6bd7b1fde34f711da9b7973efd35b781def01ecd17bf2baf09d3db067efe56be0f5a1ee51b702643265dbf25efd369a5055a70c7ecb7dad642ad7f67d1db2930c19e0d9defa86ce1809e46625cbd9848ce1e95d2093e7eca3504c20b29cbdc693ab5f262212f34db0a5bf72257f0db6f1b7888bbff3f78482ff5510f10ffed13ff967df4a89e4e64382f15d1f9cc8f7fde060e29d68eeb6b57fdbc97ee847a7931fd7670d7e92b18128f2c05b8ea0719e4ff9b4bd857e6def33a024277d5baa6624935b09abb539d1dfb1cfe03ebb3a8140397b8d4f73ba92696e10c13c52030e2ca6828cf3797f0036f48582083019f9a22ff9e33a91948b0ca6f8aa3ff135b037ffddff00a53677f246394cfc299a82fbb3cb398c9fa00ac79f437e5291b7f89fe02d8cbd81ee64ef2ffc8effd03a80baa439db18af56f9cfaecd3da870311b27ab0669ce4ec9fc052299f98fc0e4c9effa3d7f0c549ec15eb2b8f1fbfe2b28c9058d12112568a7d60a74c426300233586ed48d7da48255b046755eb0699a51055ba8f414e42dc1cea7823df6162ef80a0ec1313805677d4372b6de311ff39fb156ee320e6c33d4b7f56a0632d360ade664ae1029a988d8c40333e17d3e700217a874032ff08320082192ac200ae22009285092661e552299d10113b0010766047c30d804bbd76018087814ae3470190562203983609c7b0b8fbcc5da0572a004aab50f262833190f4bb950a8b5b13c2ec7d07bee0d0df2b759ced9a8cf06321426735b459a890c320b34cce52d780f3e5642300d66c13cf80c16d73ce482081d744af6103c064fd8ba416ffdb97a0c9eb1cfbcd4b8c05fb87296e17219f49d17a8bdb7c12bf2168b09db612b5043ddea04fbd0084d73b6f2b29f5a1f9772b834e7660875b26650460baabd8b9cbdc23702ff246aaa4470d48085ab701d6ec2ad350977cbe9320af7e1976fdf41a4c2243ce4760c4f01179ec1acd05e3d860e2213ba152eeb6577659b13d3357aa117fa611086d6fe3cb376611428611c28d0c1b13e1d262155fea9556bd9361f431a7af1f15e02651eea6d88a6944c96b333323ff5918c0831ef31644236e4dcd7905fcacbfd7265bae1201c7eeb23cd440ea1108eb089a1148e818c1c2aa11a4ef0ba5231ef1540276cb3bf5c4287f4091dc02ad4c2377b7d9e86ef612bd4c38fe0e4d3105dac23bb43abe5f1b9ba3c653f05d5f14a7f87cacf842ec1ace56c658def16fc5522e1349ca24c13cec239ca9ce1e7b2bb847c689ee17970e122ec7ce7211522424ea46c0fe163f81476fd28ec619f792ec7c392839e7a616ea1fa17a152b1c397b01f1cc3d7308eda51cb9efa54a447465aebe89149d4c57c3267e6123aaa00aa3f05aabe4d6b0a79fb58cfd984cc4d22cf7522e5cc1b2da315e956a235f8a9689ecd1d7a8ad126dade41e492c728da457bb02fb043748c4ef997859fbbbe8cc9ef01fe1363075987810c336bd1d119790b70b1a0ab85bed6f68a3a30b2e1a9616fc6ea72809af815faa7836e416e72f1bde31d321b6fad3119fc8e7f3dd3dc4724e5e2442eea7d6d37f2223ff56edaf0a17ae846c1cd98b9e44188600b3ea3308a4ee794cb318a571ea858d373975b7ebb67c4511251fe026abd4fa7d2394474c4446c4af30168f6084dfd0be2a81f71ed696902c1a16a26e23332d763a69908d1d568100d233312505d1e8d5016344ff9e79523f10744be2e4c8ac6e021f3c83cb71198488e94254fd4d334e1ef3b60f53cb61650c96e21eb4eda6aa4469360e7c89156e9a6d0a733a237c8d1f8a9e19d45684f7d0cb5af0611a4653bea8b9c1de153fdafa908e671bd5e55a28f680a9f7c46badb688e9f2389fa583fb4fad1e72f11598075a28728afcb4e6af4045cba516f19a6f48bbfefbd1543d733850a8d5fabba04f97b509ed540d720933a307a8e5e324dd2d1fce114f5f1eeaa43b1a31ecfc6814cf41ae353e3eef79172a6895bb18eb2446ca0df1c9bf1328ddfec396af1eaa744e235d826dec6bb785fc9d05efc151fe2235209d3c0bf6501bfe5b935344ed0f1482b764daf78c337c3f854ccfcf26eea84ba297b1a9f4b9f8e78d904bd3b57acf467393bb662bb2093659a3b2af8b6f7143b31aa2ebca51c7be837c73ee282fcdb60911ab6cc3888c31f10d9108b3a7114573b4781e4da388929d3c19adbd3df40257aad37a412ad0d3ce5a4ad1a1353840a671ed368aa45a228eba670efcdc44ccc967faead19b51df5286783fe2a3117139d698c9aeb15bcd78907e89942f604bd8f87c0458847384b649f78dc9ec6622cdd4f24b5712c5f788bb2442b6c3333c0195ac7d1b041d100b9566a39507be86dd17804762a996ac5937426114446ac3932a98fe3378b8bdf89568387b0e029699f5deca827dd64fc114f635ce9fda0a70145f5adb8524dc69fa8638917502c92aa09ebdaf210771a892c1a896ce207b0c7f8a9d21355333487d81bcb34a720d59cac06ab193ac5c03c2f4b7e76b222daf688ba909984f5108671f73c8b7bf1338abed6bee59ba765db786fcdca3bea53322f713f7e2dc8dc57af26eda495e868cf0ef9cc899198ce73b24c56a9dea3272225eb64f3032260c936d925356f4933349d66681555027a1be514f2d421bbeaab696b0b3d74ad6f48be6c35392447ffd39e165de6990a5bc909a98b9e401186bf37568dcfd6a992b3d5e41cbf1232f7747924c72456620300a8b257435c65ef132771ed6de2e949eae1525bdaee12bf41579b89040934364994d4a7526b9ca1277817cc385376c8783caaccc0f355d0dc2970192ed7e1aeae4a499250445d121a7599b86fc05d66c2b4fb0647cec440dfbdec2dd12ce2904f20101936e112bcae7d67977748f86490fddb70fe8467990c13c1ea2423940741cff0674ec444bae9238408b2712227d5f902f9bc7d93ab542010a92b0b32c9b82d42fe006f5947fa16696e61b89fc57e96a897b39a6402fd94ec143b1d51bf10a1bad17804361392b3132d794bde93f712991b3d4df2914cb3d906ae42b12226336b91cc93cf2c0fae06c922e95cf06822826df79c3c0495ddaaa82b5aeecd795ac753fa33aa8f50866ee10cdd1ead9975884e90d125d0baea4f76b39f4c1e9327eb014591c566dd77f861af9d8ec3e6d12ae7fdc2d8d8b5783c81e0932ee29292b9a3a7497ac973fa4c70a6c8ea8ae405fdeea47f14da6f387feac9eb258f3a11aa8dad45e954752f198e05ca5c2ed3eeabd415a17e9864687c960a6d1cc8692899a5caf798a935b5ccd4256a51abf023fca0d6a863a036681752ace2ee37ff6ea80412501b20436d93776a47ed09995b153cf5451d2e2843f5622ca923fadd14d230ece3d499b22a1ed24484984d39f50c8db8e75d91957545ba8133b406195a5d47f84c1dd53cdf8a3fcaa5bc4c5d208a5447ceea3fca0f4669ce53f13482446bbbf5d656a9800aa93d5884c8dcaae0a9984a08e5959d52265dd99b915014fadd140d9942052e0cc536c54c850887cda6f8f2df460d2acffc816468a81a5f51355d6468cce561655f211a1453097d430d832325a0a94470b44bfbd7a85146b1dce5e1fce15022255111324ce6db0a9e1a5332ce157179c69075659402997042a9506fc1d3a426578970a9b5c034ea8d7aa73e563a7a5302bf31b14ed58b7445af69862ebabe344323d3dba5f9ff3c8d8b7d3d8ba10e809a52336a1eb593b93f2faf4a519fd422a348fa8c34e71d366daa03541ec01eab642eeb55ea89eaa615d7ae3a6300ef9bb64ed4093d13aa873e33f54cbd5cf1908c08b23ef54ab7f1bb2453884e19bd590335a30dea557445c3627642b7b20c0deaa21a56595db24f963fb172e730a675da083f7c9a36cb7d14aa00e925d103f3057e9398aa18e8415ba057d4233142a6b982a7d7f486de2255a4773892cef8796633119c2be87dd4a6bfd0a7a60ff4f15b2260f4893ed316a101f50a7aebe869355ca2f3fd8ef0dc5de48da8963612a8a5518f41d42bce4eec2ad645f2ac8beb07dac69f0c3db184d482c4d368c71bd6fb4bda75fab447e29550345b500bc077af06b44f07880a1dd2f8c6996b93003a0285f4527f77d1d3812c8a631872681fcfc226744c273485c9d0349313695589602a2ccdd13c3d203ce02fb281c611321d79536dbd6c073628a297c57c9aa115a2b990a337cb69555da0a342393a8b8be7f493e53fdb7aa38796577029fa4b5ad88bf9940655ed1de8b299b64a8f808948ac4626afce68891e0716d48aebd53a56d1bb4250690ab142cb455442a7b1a6155aa527c045a3df9a88008fccdee98f94474478ac6cf23e1fa821d8726c3e41449de1afcb3274aeb96bcd985473745a233f9a23b406e0e9788a2562bdc69facadea09aa76e86931bd226bdea8bf74c6f4acf07b44b1cda209043d07269fc430998b7a955ed01df824e97b53e54a6c6de8c32c2a51f5423f00994720f374e1219975e91ed833fdd24c03f398432d30c16f868aa6db42befd82721de1d272cd7ebdea49bb13b55803c823700c9de18bb925dfeb2759df0dea825731c99a37fd4a740c4f69466436ceb49956c685c173e0867a55aed6a501d23d01bcc603ef194225dd876ca1a14fce18b4ca98cc9259d588101ed89835b361b6c0638769840d3444f82b39c84c337386f639b7f835959d95a81fcfc185ba1c2fab1eb206d0eac2dff9b0ccb357643a9fd9540ff5dd361306a80264a0e6259e09be22910904b34744982f6225326975c61c12194feb521ec8734854a1371191ea2cdbad5356733147e6c49c19ab8908a6620714e32cc72456c056f87de1320d1abf531de2b7ac47e97be601b0097145b7ad4611e9002a6b0024ebf6d34e73d49a2df9f2f7275f64aa575ddd655cc6433352b0ec8da611e3632601314ca652c13321d525de11ab290fa4bfab729e359fda6fe499321113334913116c1443334cd93bd05be4f88d721a7b0779eb9cd0e8e0f7f097f8fdfc85f1889e69358aa8415a5515357dad7768bf18adbc8f266a14336cb64782f4dd64759712180ebc452cde5c677866905219224364aa152b2330a3320fa031bdccb3e6a4e56232222331e30622c86446615466f2ad77643c80083abd80d1f0d90d3bf8cfa41e45645db2d68107d9ac0c9d766bf46ab5ce93d967de98777b964df548df8d2a63e603783c94c84c3326d8f04caf5aaf7a0b665eaac278a8c21af22cea13a0519b329fcc82e9d488107b601e99a794c6acd13b521a50df99290f74c2c5099d7ab1aceeafbaec00ca35fd11e768b3f233c26a856b1dda5c325d328f607ae02fb8ef469531f35cde9dc7bc307d4284792586c8d4eb33b65da681abb0863c0bbf75c2b6589d3558935d9688c8c8e8777655f18e4e7a6ac512c7ca050d9335dee12fd6a00646e7a23c984eb90748e70b93ac97c5b56b5a3d9095267d5399eb09642600f1ba3034764de611a8f76537a8ef864e4a61b7ec0e7cc6d5e3a3d0dab07bf68b10c95e67c2646af52a7bfca6ea0889924285054aca4cd8137b662dd6263c189975c05cd663fdba765ca38178101aa01c13c8c13454b3017885462a3b5017bdd2b3d5d60ed09e4363470d2afe25179d9429b001d981155911eebb23d47f1fd9908df43656325db7d998adbee645c8d4aa3336011a6b448332afe5d95c49672c0529894144623ee65996e558fe6b518e95f4dc9752ac34d0403c2cc33ad9e94943e8fc211d7a92bc03b8ec35d33580694837d680d9ba77cf38b3836c1ec10e814b1442f71db659219f93baec286722b21218beadbe5e9db17241e3669e7559c59159959db01afbc6beb31fdf7947162be864a1927758068568b0537c229384cf69520ddd108d03ee1c70543475002bb6f5b68c2a5cc0bff0e4abd2fbb23334ed44bd2f3b0fdb672a6c5bfbc8603f73965d76c1760891cc08996a35c23e84fb9a77143c308dc42862857d743cf6a9c93b2ada51f68e203d732af70eb015e6814eeb12c9295e50efcff59ea9a71dc043da011c8b0e60c5eadd9aba9426d4d9da14e2c876d1b413b8f4ce94850ead63d00aaf3b48331eae04d8e7824a46a65e8db02f6cff6a9e554bda8195947de5da5c2ba771bac73b4c21f58e55ea1d3a7843460378e0d3cfdafa4c578c36f820ea00d0bd426f46a2fb2b0b750050b3b2a65e5197ea849acaf681208e9ccea239cdc4622ce63c73fae92efc23ca782b07f71967cec89870260a154ca65a8bd8e820e22b5558aa1de634f50ec7f8e436dc96db5dd18ea0ae1d97de017986d0e8211ac043c167e22dd03979c6526780259abbcd4abb8050670c7f4d2dab5726b1d99a1de9f1b83df7152816eb2fc2d0e1b3ee9b3be08c872a0ba890b82377c207f89c892132199122f37216f68e9c46539ecd94d434389b73be5a25eff83e569abc43c13c16f094d1b9810c3e4d109da3d8d56d744301e900c824ab65b4d5d6705539ef00cff52aaa0b3f6f1635e07ac2b950e37d41df54da3bc2799c8f33d802fe96572ef09d8c4a4ea6568b303217de5775101a5cc4c55c52a271a82b69138d0bef286884f8a44972fea488efed2d77008cd1ae7400f3868923995fa535e05a6bc51c652d9c6365ef88ef9c381ab344d37e91633836a5c2713cc7a7648a5a04e75e6e90c5ca3779f635cbb3dcd06638e14aac7c10250595688c15d08e33e64168c4290f743c303aa12b3da5349dd2a10e70623c56e600a59e019e5b69de8df68f20ff5af1f0618edcc896eb334fa7cf8978ffe30e693b271122d8c61c56e08c47619ccc294dde714549779cca4dae7b07a7a53c5e0befc03cbae939a49806f796f2402cdafa06feba677c50da6bb62a8c3a80faa4a6acba27bb3cef26eb82501d1f74c6dc73ef4514e1d91ede53c3a1bb0e50ffc5182c37254432c3bd76854a3c8b67dc8c9bff24cf729fdc22cbb3d79434f78e0a8ff49c5a33a5b149692cf059bfe8d4dff7960c4f1f7500ac7e5c57d7edb27967befe55ecde211364f899adde416b30ce3c7ffb299dedc5823de53af02c15b432cc3d708f3995276425324004ed0140c62eb96eee1d879277acf23c5bf10eaec73d732f24cf163caa4a5ad18e6bde4178bce37391a7e9b9d016a80574c5067739a9c13ba547d9fa14ea19741fcfbba74875db92fe661aa4e2394ff09b6264e5a09fed35e2faf02921f6b857be9d11c90c93c98914c6b7aee7597c7e6c453b789d3778b3e61ddd8a768825ed287b474a43f7090f28e7108f3e3e3bfb0b9efe1b7e9b645acbd1e5f52f87cc3bb37977eb08cdc168adb424a8131f329afc8a5f175104398ab5b7f686dfa0b53e7ecbef0a22fc9e182653a382f648f05f8d79f66aacf007fe58f28e26252dc7caeb351a60343e631c9daceea3b3d681cb565f94b311de0b5eac32d6d679408fd4353ab9ca2aed36f2f853e491284af7d4e05d12fc99b7789b77ca447897f79095c9000f9fec0348def9a0b9262d94b41a2b7cc8478486c1e6ca71192b443948b49053d41b69e0771b9169f89cfa711b7e633ec7cade3429afa6545477adb6e0ff2bed9358a38a8e8f8b28b2376188a6587cc2533c5de651189e5c9588e46498eb7916f3e856bd836721d9f179ac1434ae7b073a75fecd6c611a14a2b17eabd2803cdd6b1d212b3de0398abd72b30916f4fd613e0726bd14ae7690eaae38bd6bcef3cc9ef545137e504451b67b841ff2429d08b6118fd79bea54d01e095ebae071a1a4b977000f7e6caff82b4a4a788092e63cbef30ec223ea231aed19748e6ff8c68b695bd3033c732513ac4c759962cf24565dd1f0cb51846618a42fe2157b9abe4588f7609da940e1d50b22c0046cc24fca641091d4a270c66bb5cc125fcbb388476bc1bff1ef599e6d5252ec1d31f18e56d4e81d4744a3ce03cff625bcf231d1b7a5099654ec0547931ab487a4fd604eeb5194f545fc47114568461e9cf8293f2b7c242392199e426444a2c2f839ffd9ec1d57b443e6177c07f3e8e7ded1a81d4781f04034100f122b1734521ee9bd311b7c9f0c0bfdf5369b8fe35349f1de1abc177cb4665a43b3d81b9a4711f445e86dff57fea1785f2e8cc21678cc23ff54e781ad8b0c93892a86f748f03dfef9fb3c9b29691e2b53fe85ef976285ca32cb75efc034108f719906f20e4403f3402b422fe91d3c0cf492cff90efed2ea3a6423551f96d6bdd3284af7cb8ea162d7f9d72c8a309753a00cda83d6251164037da097c86022990d8c81598a956f9434ad3a80c760395895bca38146c53bc69558314a34308f9693d180de67806f69e2d75abb5f525d3edb03d77e818ace6b8ea2ac2f1aac49144510ca88cc6033d80e766522884761984c8908d92141877c6fb06fae49afe7d9c1d7e0804f8c2ac50aca2c0db1d2698e95dc3b088d01be3b915f4ff00d5e0abed74bc5e78164aa3b46abc8ed9e295e8f22b4ef04f4521c1cad5d700c5be031389606a7c179605d1219d8c43099828898d9c019b8df28a95f54618577b4cc8137f08b3c8b6f142a7947ab5b8e958246b377601eca20403c563374d31bbef32d02cfadaa6e6ff9d4144578071685ab40b463b6370831916370c43ea30ca2413c482e898051c8f04cafcc24dd0b30a06fe5d94c49b368413406cc806df20e68272e78201a2b96d068ad53efd052ef6080078579c4844771435eeb98ee6844aa0b9d746947702d8a4cc17c81aa94bc19160fb8b0155968d5009119f083c16038102e89808dc0703d532692ee91f81a48551ad795b4ec1d83f140a6f5ab4a9a6a47c6a3ea1d190dcc03df0978716320be25102a96744776bbbc37ad88a2b014450cf46b07b28e3950ac1d5a3508234c461d4c06dae0ad446444886486c9e47b46b23d129e3a782f556124566ee559ec1d838fc134f38ed6fe9206f18e9446a61d65ef082b3ca2ecd6447c8b20fa5e05fe3d6fa80768b9c55e9070578b22ab8822f07834233d0e661919e0321f7c029945e12115eb20c333bd7ccf48b64762f05078c7b74a5af38eb638781c3ce5796554a2c1d594f44aac60efb8a041ee504c6f131ca05df02da354e97e1b4568b71f70e90e7a2532cf8317b07e13116caf43fc4e5c79d708d90f306c55ab8e5c49b54b25cdbd235552d44e34c48a4294f46aac5ca1816f57e4d2bb7d55f03c72b320f8a2fe5d2e2251d487de1be9b4a71f86e6700915de3b64a768b81aae1199e1a64604781486c994768d602edbe1aeee1d84c6b53c4b62059e2478c7703ffc42ef34de112be90da3e98d9be4a64df473151af97d8be48ec5e25e45cd74ebb9e864e3f5975214c113dca25b4a8787f09d90191e091720736a2282ed3c246b07a55d236837c0d08a03c26368ff82922a4367e896bda3cca35052b06fbc037278ee1de091c023e28a5b17c95d8b2dd39c0f3db2bffa4a147db57c7242dcd0476b93617b180c43c205c844174ccea9c5c3382593ef1a416bdec3a4b9eef836cf96bc63480de95ab410254d2ea2a599c64b76fb26f10ef824c45f89b291fb26d14d93ae7e006fc973117e0b3bcd457837c0046a307c46bd7ee0df111978f82c10e1863cd86038ac1189731386f87eedf2ae11e0b21d8e9a3a96eb79f6524987e250baa51de80edaba761434f23b393be91d9c290d741325b98192dc3d69ec4c0823948b38ad16459bd67b71deed700c5ce4a182991053cb1e5210c13619e25ebbb26f441c6a4db17235b3444d7976f8367c076e8576dc8c9506ef9862ef20b790bad97d9c8806be8393dc49897a5e8822bc9a528a22e4eb10436ff88c647c76def003b84c732a33b0f9050f4c64f8498c90192e869de1c3f011ad770f9f521ed769dcaa3a80c6b03bec35d1b854d28658792bbc03ddc35aa7818d54e37e6b66f838171db38a0eadd6b6bac3e7f219f5c397f30ab8cc2ad66f265226337c15da424bd00543308525ba4ca2162bc9cfab8eed4ed8302f8db1c2dd1f2bc00368401fa25568a0da9cdcd4c9eb3e7498952842fb81c183b2f36e8fe88438612becaa5c84bdf0d54c4438801d851322239c054bb0054770054ff0854008854888d358892e68dcccb384869008d4b7de81efed2534b07734c44ae976da38e591dd599add553adbedca51044fc1c7eb3de9d979e854418116989abfcc0456385cf0c04432c36438cc854fc90cc0bd046124888294c64af46d4dda9059887608634186cf2a1579b6493b72ef203ccab7f556bca3e0011d25bec114dfd48aee2ccd72112b28822a4c044d7813de850f612acc84b9f0292c84ce059987ba8fd40d93c93ce611b8f8c21326d3157ac2b3f012bd5678342a69a11dd53c2bf485d77a9ebde91d54c143ff4aefb22dd1c0f7b9e25b5cc95da6f0df5f5914a10733d247c6c81c2dcbca3a5a8dd6552ea3f568738d08b2d176b4c364ecd1bef098d197805ce620f446c7d169741e59d9fde7b7f22cf10e7e5968c7c81e3975edb8a2a48dde91dee7fb9ede6b9bd190f3fb5c4df8df63d4616e77e890aee64c33f24b4c02b070148de26b44322364528f2962a98bb924236a448f98113be22ef36c76e7792956b42c560aed18f1a3c115eff00bed20de518d958c06badd17cf5cb3fb6dcdf26db6ad67a885f7a3e148b89e6946a39c88989a54e351225226031ee3943ca62083b8d0a3f1481e29237534b952934e6bb152c9b3236df4367a2f29a97f45490be528c74a4e23bde557c43cb29b6ebbbbeee863341dcd46f36bb957388c3e478b9c08b1cee8a1ee21157b1c3d8df08a3fe65252df11d2988c0ce2d21b3d8f5e46fdd1ebed3c4bee41873c9bc70a0ab72256caded11c2b290f72d333aae4d8749d2ba58157c0f0ddb7a22e1a682bee259172a61157352ea2b8bece0399b8419693a9a96fc5637ae256dc897bf14b449750ddc8b3f9dde879ac5853f174a1a4d98dd88b26ef001ad83bc4737a0332bef998dc788cd7d417fa42b444bb914845554547746b5c3cd16f224278e486dfe2b9a2be40460c818b3c7ac664223116139112e9b277647976fd764549b16f888cc896a2a54949cb3761c775ef486f445e907b908db6c889bc38f8ae1ac9c90c2f3c4610474d3e9211490d4f3bbf51dfb2c720329238166551a95561fe774a8a680cbaa23ae8dea11de87eecba77e07ba1c97dd0f886e4b93811b58a8f7c937bc5b70b32efdf13c9be5232d73c06c8881fb9c74cc51990998b9fe2a2aea479ac14da51565259ec880fc83bf2fbd21bb523a5f19ade923d4b6fc7ee917ba2c9de72f1517cba518be4ca2a76c55e4ee4597c01ebd7a3a6ca03be5ec557945e3099c70bf5ad7a4c46063c466ac1873624535a5ea949ab4a5a8a156925adebb152f28ed7c23b521abd9cc6737a53b62f6da4adb4bbee21551337d23e27f222a10b753de9708b486a474c8670f95e7d1119ec31c0e5249d254bb21b6bd27aac1c0bef901cc9bd1d2b251e9806be457b67ec6859f224ff1e22998a4881141222b94537881c0b2bc8a0581a758b5892e29afa628fc9c9241225d1a40a2bd56037f2acc4486c7aaf7c7a9b7c2d5672efc86e4e3705b28352e2245e1adc4784983494840a952f6924557834130193a4b18477bd4af245bebea2be104b848b026454692269d25ba3927673257dab7a073d93de1bbc434c6f94cfee91c7344e36de5b1a4061f9e1053789d4b2af34ad7109a5d9773e9211496d8e7de69e7c3dadc452227d4a0ba9233d488fd8432ebca39e670b25959ea46e85468d477eab7c7ac7bad4939ea597db3e52d555a92fbd56c9a057dc6f1299179691f9265fd7d5f7947a4c67ac8f8db1395e6634bed30ef305ef88c4b1325e8dd78d3482fcce792ebd695ee38cf1e63b22cd9966bc1def2a54f6e3aff1e13e22d27c7c4486c95ca82fc9d7792c5d7a0c2173820f7f1e5b637bec7c9367899292589188768cddb137f6531ab977101aa641ee9e374fe3601c8ea39f10215fd2701ca73c324bc65473cc5c1201a3c7cc98216488fa5e764b57d59778cc69cc02196ecc8f07e3e115e5688a15a0910cc7c27894d140de8169b0e609bf05b2337763f895d755a4b93a432a025cc6290f393565acde4f24334ce657d497c4d2196fd4e7c793b1367e4bf3ca85927e258db1a28ddfc71ff83d18f426dd27b7216f93a17787c6d3f10c7bcb8d0afe32f78ee7e3cf1213648b71e75b1e55220f99e129c46df51dd7d5b7e2318f604fe3eeb857ceb37525cd94a3ec1de3e7f1cbb86f3c9a267e976a6106e672fc2ab7e5d63731135cf2c87455d66543363326e8557c7925af7f4684984c7aedfbd417b8c8dbaac7c83b4246decb5ff2413ecaa766ed40348876e05871d2770da7460f5f2f699b4bfcfe5dc754654776c5cd4d1f69ac46644ff67322c40239bc4184a913492daa9169ea96e28b7c5da82f8924f018e012cb894c9595b4ae1d9846c93bc85bff8921d3e8b533d39539999707b77a9a6bd5883c9485121364f067dcef2384476198ccbddd52b3fa661e13cb127a608846d53b080de011e0b7328977903757c95bac3359711e64559edcdbd3d433afacc96f63a5c6e55dfe4879fc88486a5342e6aafaa61e734d7de5594a867099cb9ff202883c96bce392c608d308f1fbceb4f96072cba5dc911fe447f949eecabd7b7a9a6aa6919fe59712913ed8abd2565ad763e63b22f254c12fbc6205feae5b9a5ee66b1c4b258f496309c82886622acb3456080f15d1488c4b1ae8cc00f3d19c282b65ad203f11a5a3b25576ca5ef9ba8b086887729043e528bea43cb02927f95d39df5291eb4432c33ed3ec31bbeff375457d63c5526c20e328aee2d56325e5c1e1d30244069d1d3059aecc397a391a1dbe0b1c720f91352554222556128552e8ebf5ea78ab300a2b8fc6fb12134e3929bc329037573ce42e22a90d2b64eee996aea92ff1184446504666b83453ef7828bc23e3814f9c08cd3ebb504445bad455a0c328e371a2c8f28ba228aa325134e50dec5df950a6ca4c992b9fcaa26002449075940765a03cfe5522ca13314ce6325f37a96fd963aaea1b83cfd8988cab74959ef2acbc601e8f85776434f0d924ed655be92baf6afbbba8515baaae1aaa395ec8cbb12c85f24a857f161e9213e1c60b75a5aed5cd1d2a720711e549dd22cbc9e4b3aa345f5faa6fb55b4ad5f7c263baea4eddab5f198fe5b8a0b16caf6c7436887a508feae94aa6a9458d7a563e544bb555471ea9aeea65f5ad6caabe1aa821fc3f911adfa3abb788101eb9251532b7bba5c5d57c9d7b0c90a15474945c9f32318f757aca6284cf82b25556e5804143eebdd5d3c89ecaab0375a80e95b32ac8f7f8c8dd1e5210c90ce7a65fed96b25842f99aa82f2243a9235554a5328da5bd1aa2f3a1d4b12a4bc37be700f7d7ab7f27116c206e39997bba25b592af27b57ceda6b184c8a8ea44d580c8133e2f8b472789a96feabbfaf14b44fe864c738548724904db54c595de8d5955bd5b62abf93a55dfc26328f018559da973e021d3bbe554fd54176a477db82b667ea95efd9b884cb13d12c364be595bba9aafafa92ff19819d893da5df6d49efaacbea8fdbfc147fe52a6b91e353993c7aa65649af3f5c5acaa9eaf9bd4f7352533c3db890559fb1d44bee5d1e823659be813d21d5c595bfabe5b028fa9a96fee3119196362de333dfb794f73c1e3e7441a78102293253142a6497d6f754b358fa9c71261b3fa2332cd0d2298474ea44ae67abefe7656055c6aea3b4ad53723b39e6cfe8d99e6072a52f790926d892132e94ac12f774b935db3c76036fb7f83aefe828a5c25f29519f699bbbba5da74b3d22d95f27599cc6172fc1355e43a91d44e15324d2bfb37baa50bf59dd5c89c27d69f9069ee27921926f3b3b5a5a67cddade6eb0a1bfb87f5d96ff291dc1c6c6e46e657baa5efd537b3767be2ff3b33cd5f25822d9804844ca5f66dee96aecdaa0a8f698825ec33e1e4df97692e896c6f1071ea4432c3649aa79bdfa96f7c8ffae66ce23f4f452a3c2a442609b11299a6b5a57b6755573d0693a1fe3c15b9e0911301a39161327f497d27cc75f54dc9b013eecff7918c4866b8d2bbab5bba36abaa774b17a64ef8ff041f29d900594ee6ee95fd9fa82ff698e144f8bb7b9abf8548230fb011314ce6672bfbe5b5a5dbea2b4ea43f21d3dc4f6432269692f9957c7d87fa4ee489f267aa484e6454273251c1e0ab44a6b95baaabef4ff2b53ad1fe5415b9e09113c9be3099eb6b4b5756f62f3ca6b9c67b9bbcff362277c6cc2511fcf531f9c064eeccd775f5fd3e5f4fa693d9bf94c8df143575229339d8e7e4b34ae6472bfbdf764ba0bc8b3f29d3dcf611c2a3304ce68e5955cd636ea8efa43379f8b3320de67117911a999fadecdfcad7e2e49e98a913f9f7c6cc55221532558f517ed82d5d4eab9e26dd3f56456e12a990f9955955ef9afa4e7ac0e55fad2277e7de5b31730f995f5ad9bff098c9f3e4e58ff291f9cf9994c85c744bdfeec4bbb2b684b9f427af7f66a6f955328d2bfbf5b5a59bea8b8e9efa37659a7f21919acffc9595fd8c8bae19df78481391dfa0ab3f2393ab6fa55baaaf2d7dafbe938e66fec999e657c9dc58d9bfb6b69493999cb5e59f9d697e8dcc8f56f69bd696546df5b7eaea8febd57f99cffcb56e49d5d6c0e53f44577f894c91af7fb4b2af6db4ed7f13910b32bfb8b2afedb4fd9d449a75b54ee45facab3f24f3cb6b4bda9776f8cfc8347f89cc8f57f6b5a376fa4fc934bf48e6e7fba0818c76d6acff5c15b99b4cc33ee8efd796345b73fe3b8954c8fc78655f7335efbf9548854cad5bbaa5be9aaf05ff6999e697c9fc60655f0bb5e85f3b19f933ac42a6aabe8ddd92166bc97f53ccdc20d3f0de52b3faa2aba63193ff6222353277754b1aa3b1fffd442ec8dc5cd9d7388dffdf40a442e68afa96f3b536d086ffc999e697c99cbe5fd9873812feb7102993b9a5beda48137ff7e7fc6d64bed907ad49ff1bb9a464aeafecbbda58937ff767fc8d64aebe35aa299afabb3fe16f26d3d82d69134dfbdd9fef379369ec96b437edfd777fbadf4e2657dfe2ad51ed439bfeeecff6fbc968b36abed6e6daa7b6f8dd9fec771b26d3d11e0af5d51eb527adfbbb3fd7ef374466f2a9f5b467144bda8bd6d75e7ff767fa338c90997ca28b81d091b5ff784b6619997fac6eff90b966ff90b966ff90b966ff90b966ff90b966ff90b966ff90b966ff90b966fff77ffedffff9ff3e70c4ac</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>chkAddressee</tabstop>
+ <tabstop>mDisplayName</tabstop>
+ <tabstop>groupList</tabstop>
+ <tabstop>addGroupButton</tabstop>
+ <tabstop>protocolListView</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/addcontactwizard/fastaddcontactwizard.cpp b/kopete/kopete/addcontactwizard/fastaddcontactwizard.cpp
new file mode 100644
index 00000000..43c499d6
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/fastaddcontactwizard.cpp
@@ -0,0 +1,135 @@
+/*
+ fastaddcontactwizard.cpp - Kopete's FastAdd Contact Wizard
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Derived from AddContactWizard
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qptrlist.h>
+#include <addcontactpage.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+
+#include "fastaddcontactwizard.h"
+
+FastAddContactWizard::FastAddContactWizard( QWidget *parent, const char *name )
+: FastAddContactWizard_Base( parent, name )
+{
+ m_accountItems.clear();
+
+ // Populate the accounts list
+ QListViewItem* accountLVI = 0L;
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *i=accounts.first() ; i; i=accounts.next() )
+ {
+ accountLVI= new QListViewItem( protocolListView, i->accountLabel() );
+ accountLVI->setText(1,i->protocol()->displayName() + QString::fromLatin1(" ") );
+ accountLVI->setPixmap( 1, SmallIcon( i->protocol()->pluginIcon() ) );
+ m_accountItems.insert(accountLVI,i);
+ }
+
+ if ( accounts.count() == 1 )
+ protocolListView->setCurrentItem( accountLVI );
+
+ // Account choice validation connections
+ connect( protocolListView, SIGNAL(clicked(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+ connect( protocolListView, SIGNAL(selectionChanged(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+ connect( protocolListView, SIGNAL(spacePressed(QListViewItem *)), this, SLOT(slotProtocolListClicked(QListViewItem *)));
+
+ setNextEnabled( selectService, false );
+ setFinishEnabled(finis, true);
+}
+
+FastAddContactWizard::~FastAddContactWizard()
+{
+}
+
+void FastAddContactWizard::slotProtocolListClicked( QListViewItem *account)
+{
+ setNextEnabled( selectService, account? account->isSelected() : false );
+}
+
+void FastAddContactWizard::next()
+{
+ // If we're on the select account page
+ // follow it with the add contact page for
+ // the chosen protocol
+ if ( currentPage() == selectService )
+ {
+ QMap <Kopete::Account*,AddContactPage*>::Iterator it;
+ for ( it = protocolPages.begin(); it != protocolPages.end(); ++it )
+ {
+ delete it.data();
+ }
+ protocolPages.clear();
+
+ QListViewItem* item = protocolListView->selectedItem();
+ AddContactPage *addPage = m_accountItems[item]->protocol()->createAddContactWidget(this, m_accountItems[item] );
+ if (addPage)
+ {
+ QString title = i18n( "The account name is prepended here",
+ "%1 contact information" )
+ .arg( item->text(0) );
+ addPage->show();
+ insertPage( addPage, title, indexOf( finis ) );
+ protocolPages.insert( m_accountItems[item] , addPage );
+ }
+ QWizard::next();
+ return;
+ }
+
+ // If we're not on any account specific pages,
+ // we must be on an add account page, so make sure it validates
+ if ( currentPage() != selectService && currentPage() != finis )
+ {
+ AddContactPage *ePage = dynamic_cast<AddContactPage *>(currentPage());
+ if (!ePage || !ePage->validateData())
+ return;
+ }
+
+ QWizard::next();
+}
+
+void FastAddContactWizard::accept()
+{
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+
+ metaContact->addToGroup( Kopete::Group::topLevel() );
+
+ bool ok = protocolPages.isEmpty();
+
+ // get each protocol's contact
+ QMap <Kopete::Account*,AddContactPage*>::Iterator it;
+ for ( it = protocolPages.begin(); it != protocolPages.end(); ++it )
+ ok |= it.data()->apply( it.key(), metaContact );
+
+ if ( ok )
+ {
+ // add it to the contact list
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+ }
+ else
+ delete metaContact;
+
+ deleteLater();
+}
+
+#include "fastaddcontactwizard.moc"
diff --git a/kopete/kopete/addcontactwizard/fastaddcontactwizard.h b/kopete/kopete/addcontactwizard/fastaddcontactwizard.h
new file mode 100644
index 00000000..ac8db23d
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/fastaddcontactwizard.h
@@ -0,0 +1,64 @@
+/*
+ fastaddcontactwizard.h - Kopete's Fast Add Contact Wizard
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Derived from AddContactWizard
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef FASTADDCONTACTWIZARD_H
+#define FASTADDCONTACTWIZARD_H
+#include <kdebug.h>
+#include <klistview.h>
+
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+
+#include <kdebug.h>
+#include <klistview.h>
+
+#include "fastaddcontactwizard_base.h"
+
+class AddContactPage;
+class QListViewItem;
+
+namespace Kopete
+{
+class Account;
+}
+
+/**
+ * This is a streamlined add contact wizard for users with simple tastes.
+ * @author Will Stephenson
+ */
+
+class FastAddContactWizard : public FastAddContactWizard_Base
+{
+ Q_OBJECT
+public:
+ FastAddContactWizard( QWidget *parent = 0, const char *name = 0 );
+ ~FastAddContactWizard();
+private:
+ QMap <Kopete::Account*,AddContactPage*> protocolPages;
+ QMap <QListViewItem*,Kopete::Account*> m_accountItems;
+public slots:
+ virtual void accept();
+ void slotProtocolListClicked( QListViewItem * );
+protected slots:
+ virtual void next();
+};
+
+#endif
diff --git a/kopete/kopete/addcontactwizard/fastaddcontactwizard_base.ui b/kopete/kopete/addcontactwizard/fastaddcontactwizard_base.ui
new file mode 100644
index 00000000..f53f487d
--- /dev/null
+++ b/kopete/kopete/addcontactwizard/fastaddcontactwizard_base.ui
@@ -0,0 +1,219 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>FastAddContactWizard_Base</class>
+<widget class="QWizard">
+ <property name="name">
+ <cstring>FastAddContactWizard_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>619</width>
+ <height>481</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Fast Contact Addition Wizard</string>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>selectService</cstring>
+ </property>
+ <attribute name="title">
+ <string>Select Instant Messaging Accounts</string>
+ </attribute>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18</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>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;h2&gt;Select IM Accounts&lt;/h2&gt;&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Account</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>protocolListView</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select the Instant Messaging systems to message the contact. If they use more than one IM system, select them all here</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>finis</cstring>
+ </property>
+ <attribute name="title">
+ <string>Done</string>
+ </attribute>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21_2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18_2</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>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout25</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;h2&gt;Contact added.&lt;/h2&gt;&lt;/p&gt;
+&lt;p&gt;That was &lt;i&gt;fast.&lt;/i&gt;&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>211</width>
+ <height>61</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+</widget>
+<customwidgets>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="115372">789cedbd596f233997ae7bdfbfa2d0ba6b1ca8352b021be74292e7799e0ece45ccf3282914a18dfddf3783ef222dd9966c67655665ba5d0b1ff03da918c897e45a1cc3fffd5f7f3d5c9cfef55ffffd1fd39936f38cbf0c57cbfffa2f731e45d5fff7ffffbffffb3ffeb3dd6bfdd5517b7fb55545fdabf39fffcf7ffca7e1ff65fcd568f1ff6a9e0e24b7f9ef23e236d8b425f3fb47b8bf2deeaf0cc9fc7a3b22a6fb67f792f9fd7648dc69756b5e1e72eec8f45c117770bd277eefb67a3507d792fb354767c43df068c1b92b9e373b21a6e7a5f7c45dbc3fb920eeb506356731711f5c159c7be279d13931ddef2c897b48dfac4d3c680d797af0febecc5f97b88ff4ce9e889596c67f879e83da6a2e7ac44a4bad59ef12ab2d9d97d7856483a71f7a0cc5fd79977888f72596649e3e734cacb54c9e5fa45711e975c7c4cc6ab64a62bd65f1f2bc2336c0d989649bdf3f12dc46fe0f894d7081f4a9e2f9339758457efd8098ae0fe7c4561bf50b7a69e2fa45446ce2f7e98cd86af3f29fa5c436382e25f3f21c3738ebb2bc8e8835944f1e12ebd03f154cf767a76056e1797d30a09fc16a3ce7794c2cee8f880d94df0c7a9982239dd844f9cc89db9d362fdfe444322fcf0af5c512e99f3f11ebb8df3b22b6505ec52e675bf0bc416ca3fce65370bbd786befbc4fd36d76336271e800d8f78d8e6e91ff1f6d37ef637636213f5d3be27a6f7a5376071bfd193ccd367d2f56d053ced736ed70e8697cf1d711be59f259c3bb5f1fc78c46a9bbfcfba21d63afc7ea792ccef9ff3fad8eed6c6df7749ac77f8f35c85d8e8c09fe1f9d25fc4785e4fdcef2bc46687d7874823b63abc7dfa2ae73efb17f893063133aeff0db189f7cdc692f9eff390989e377388ed0eaf1f53e467209ee70c8807a84fb329315d9f9860e63e713f2f6f561a74fd628f7888fa9742ff21931bed197a29e2f7ac4fcc8ca717e5a3b0eb79fd9af1f6c14a877ecf6fc19d6e87d787e494b8d7e1f57989f46842ef04efd3d813f8f3529487d6e977787d59407f9d31bf7fb6473cc0ef21eaabd1d6dbbc7d4eaf890dd4c7b90dee285dc4cb25b1da4579203fa6b8deb4c01dbdcbf5b60cc95c3f17e56db13b787af231b185fa6d04e04e07e99d3f10eb5d5e3fa6bcfdb7ed968afa9d77c0e2fe29da8bdd31ba5c7f3b2036bb5cdf8afb5f167e299e2d8660f604de3e66c4b5f1fad227b6ba5cdf25f7279de7fe800e16f7a70f6071ffb447dc46fb2a63621bcf9b9d4ae6fec43b90cccb23b802b3e684f2e0f963ea88e7df10d3f317d7c41db4d788c74b569b04e37d5df63cc42f9fb8035e70ffc86a1b5d3f3589bb687fc939b8dbedf1f797787e5ffcee87c43db4f72c0277fb3df4af78fbec0cda3af5575a6071fdcc23ee53fb1e80bb3dbc6fde241ef478fa2e67e05ea7cff56c3624733d9b1a71175cf0fad2198ae73b23e24107fd8f1de221da7f13e53b14f7370dc9bc7c9a26710f3c47fe1479bf0516bf376dc9bc3c9b747daf0f5e223faaa85f05d2af8ae72536b142fe06f543edaa3d9efef49a58efa1ffd421367a789f0b16ef6b7a92797b6bfac403e24032af1f4bee9f3ad2ff4c8fc1323d3ab10aff565e82bb5a8fa7df3827367b783eea8b269edf8c8887c4b164b44fd4075d3c3fb821d63ae8af44c4cc787d43f9ea5dbbc7ef6f26e09e82e73553c1039e9f6646ac822d94afc13c0e671be9313a149f674b627aff5c23a6f7cf8e890df8f329fc8bc1d2c3d95a807b2c40f1f7e7c4f4fee654327e9f116bc473c918afa07d9bb27ce08f4c999e1931a527837f3259771e7a14c4f4bce682581fc0dfc33f58e27e77426cc25f4f47c416e24b81fa60b1e7c33f77883bc8af7d2b19fe4523ee829b2531bdbfb994ccfd45b32236c029cac716e9c9ef89293d33f82b9b3d1ff91b11d3fdcdb164ee1f9a136293784732c61bbc7fc8865b26957f442cde1710db1daeeff4115c1bd76f0966ed0fcfdf25a6e737f7882de27dc9f02f07c43678ccdb73f7393ecdc1223e460658a467a681457aa605711bf17ef60066e9e3faf83ef1a08ff71f12d3fb9b4792b93f681e83d9f092f308f997e3df2c05cbf73bc4f47ebd0766ed9fb7f77046ac903f3e018be7374f25231e9c11b789cf25a33f8ffc74c5fbbc636216d0b87e2698bd9feb1f2f8855f2e71760f1bce6257187f84a32bf7ec9fb3f5dd95fcedb60f1be7849dca5fe15dedf63efc3f3aec1e279cd1be22ef1ad64f81b3caf2f9e974f887bd49fb289fbe8afa5a8affd9e41fef70e2c9ed7bc27ee0de0bf9f24e3fa07e23e7889f40d447e4be83f10fec3c6f503999e0531a52717bf0fd07fcc90be016b1ff08fa84f03f1be664b32fc599b78009ea3fe0ca57f447b1976a9ff113489e9fdc91531bdbf407b1c327d90ff0e583cbfd9153c44fa7ac443708af42ae279f91ef110fdd5661f2cae6f0e24c3df0f8915624532af3f19ae57c5f3a625b182feef0ce955597d853f5589e9fe469358256e48c67c01da8b8cff21ca4b13cf7786c42afab7860b66e5c1eb6f4323a6e73574628dd890cccba36112ebe008ed4b17cf4fae8935f49f67d04f67f591e77f1649e6d7376c627a5ec392cccbbbe1101bc4ae645e5e11d263b03762bc84f41be2f9d327e23efcd1ac920c3dbac443b4a786474ccf6ff8c4e690a7df437998e27df908dc57707f722c99d747f38e58457b6804c4f4bc462899a7af11115be005ea8f25cad38f880d8c3f1a31585cdf482443df94d826ce24f3f209511f6df1bca0416c7679fa2d94a7cdca1fe32fba9e950fea634e4ccf6b4cc18316f14c32d7ab31276e83475c8f9e9c9fc913304b0fcf8fbb436c613ce48c89ed2e6fdf451fccf4457f674a4ce94d7d624aefac496ca0bd360ab0484f632119fa96c41df088c7af9e8ce7d11558a4677a0b66ef437f62486c52fdaec0e2798da5649efec688b84b3c91ccf33fc1fd325e3b0ab89e00e6fefb91b88df19d8ff77798fea8cf63627a5e6387b847bc2b99e767c4db4f4fce674716583cdf99107730fe9b213d5d56ff30be6d80999ea89f7bc4f4fcc63e719ff840b0c2df37e2edb127e373b52016f389787e4fbcbf710816f7378e8807c4c79279fa7770bf9c1f4f91dfbe785e5811b380c8f3ab12b3ff787e3330d317f1c920ee527d3a211e2a48dfa964f8d73362053ce1f1b227e3b38bf21888f735cec1e2fac68564fc7e49ac8227dcbff7647c4dd1be86e279ce0e71bf87f93ae83564e5c1eb677a2b99ebad9f112bd0af71454cef6b5c4b86bfbe21d6c019d2af88f7b94de201c6cb81463cc4f834d1c14c2fbcef965853509fef8875e27bc9fcfe09ef6ff4e4f87d81fcaae27dde0931bdcfdd23567af0bf317830801e46452cf2fb404cef6b3c121bc44f92319f88e769e2f9be6015f303a90b66f9e3cf374d629df46c119b785ea32d19febd436c8177a0af5c4fc8507ff55e0ff1da6d10d3fbe7a8bf3acb1fc6db3ab1785f97d852e0df7bc436715f32afff63949721df0f7f6488f779a7c45a8fdf9f65c47a0fed670016cfd3517f8c618bde37948cf938e4cf14eb7f2efc8b299e3783bf37999e582f3926b6903fdb970c3de1cf4cf1fc862a99b707ad49dc064fd01ee4fa479e82bb3ae6077db43f4ba427a3ebeb1c733d34c9fc7d0ef4b5d8f3396b0dc9dc5f6b74fdb043ac4be6fe34447cb1c5f3bc33629aef710f882dcc0778d0cb66f7237f067117cfd34cc12acf8f6611f7c0152fcfbe8ce7ae0666efe3efcf1f89e97dda3d58a67f4732de6713d3f33547322f5fcd25ee83f5b664ccbff0f2edcb781ddc82657e75629a8f0a47e07a8194b7ff0330d39bd7375d2316f9f788e97d9a4f3c200e24a3bfcefb437d19bf03bcbf23dee79d13d3fcd31ccfeb30fd79794427c494dfe48c78004eaf24e3fda164fe7e2d221e12c792110f78bc61c35113fd2df7102cd2a36560e68fb93fd212b0b85f1b132bc4a964f8ff9058c5ef13bc4fc6771fd7b3f134e6833c8398e69f74a49ff5eee1efb53b6213e5339b80993efc79fa0931bd5f13bfab2abf5f6f4ae6fe5a6f106be010f9e98bf70706710ff32f85091ef6507f931d620def8b9a92f1be5032de574ae6fe47af8875b0c7e37b7f20dea7a960a63f673d2056116ff427b0683fb6414ce5e1d0f384feeeb5649ede704aac53fa4e25237d2dc9183f39c406788cf621fb1b25ca6b28d6a30294d750e4c7cf8869be4ddb2556d15fd546c416fa5316d23364f9c17e0eba9ea59f978f51121b2ad683cf884de2b664febc05da8b22e6af2df807a54bf3cb19daabc24a803fdf3325a33c97c43afab7c12598bd9fb3d121a6f79906b1455c49e6fa8c79ffa32ffb2b15f454c5fab19683fb6d8c27039398d2138f8887980f34505e6cf489f8e10fc02c7dfc77df233691ded9a364f40752620b6c5e49c6fae235b10db69a92317ea4fb951678cec77f7d395f91a3fcb5ae82f53f0ffa6922fd7e4eacf479fab20b6215f38f560166f511fdd77d624aaf45cf17efb74e2473fdac21719b5891ccdb4fc5fb337dd97fd250de7add83e0e54fbfb3f2e0d747d7c4943e0fe5a90b7ddd01b105fddd4a32bfde86ffd0c5fb1d87b843ec0ad6103fa0271b2d53ff3624a6f99810fece10e9c9778835cc5f9a682f061b40607d78406c23de04c8af21d217f7886db42707f1ca10e971e9fd4a57e3f16284dfe5fa8789f231457af39c58457f4d83ff31d98808eb9da7c403cc8f690f92e1ff746205f3f1da2db181f91d0ff5d514f90dee8975cc17e72d6203eb6f26fcabc9f4e0cff7ba60965fcc6f414f93d51fac0f27c494df592919f12b25ee81fd13c9e80fa0bdc8fe62754c4cfb7d52f43facae067df405b86761bec9b3c0758de3cf9f12537ea6e27713f3a301ea9b25deef9f12f7c1a12999e73f7c201e80c7a8efb648af8dfcdaacfcb0ff614eac5379e27d76af85f96b83eeaf47f49cf17e9bf540509f2d624a6f720466f59fe7277c24a6f44423c9bcfec60ef110bce4ed6520fba3de35982982fd16f7e0611bfd85f0092cde373b23267d92a564fefe989e2fde173f48c6fea811b1423c96ccdbbfc6fdc3a0dd1d52fc9b112bf0e7d913b105ff9876c17589f3f2a3eb9962dc3f98485f9bf90f8c2775627a5f7a42ac129f4ae6fe69c2fde140f64fb512dca5f5b9620216e39770072cde9fd960169ff9f373a4a7a3b4e11fe23e710ffe367789499ffc4a32e6cb15624a6fe648463cd827d6880f24637f08ef3f0fe4fc957e0cae572478fd457eba6cc48af5ea5db0c84f80fc74eb0d913cfd63e236d67be37be20ee6272ca4bfcbea0f7f5e42cf53fa945f8f98d29753fa149d38908cf45c131b1ad77bc2fdf340f697b539b85e71e6ed6901ae571078fe2a6293fa33487fafd7a6fdabc85f6fd081bf4d35e23efcdf14fad6fd63febc694aaca17e4dcf884dc4571bf5ada774a9fd5c131b949f1bc998afcf24c35fe5c4267882fa21e7e734dcdfef68983fd014621a5fea3eb18df19d9580bb16f93fb4977ac0c1cb2b34884df853f708dc3768fe14faf6853e31da5f5f19201ea7743f6befd84f44ef63e947fe0e24237fe792315f7d416c81c7285f39ffa7a13c5983c27c9589f7d71580a7c78c88a93c33f89301eb01717d32e83310e90f7789bb88a731eae380a51feb6f77c4949e694b32f60bb4896d625532fc0fca7f28f47750bf86f50a13d7bf24a6f89bc0df0c7bb4bf7a8afa57f7ff797d4b91ff61ddc2f8fd8e64a47f42dc437f20cb89fbd81fb00cc1a27d2d4f8929fd334b32d6df29bd6a8b3814ac737d2668ef727fb256803b2dccff07f0ff8a787f8af6a1307f85f563f84b453c6f9613b7891792d19f3827ee80753e5e67837d0dfd3bfd112cde6fd8c41ddadf7549dca3f9eb33629a6f8ae9fa2ef58f32d42fb547fece47fe54961fc43f4732fcc70df100fdafa40166f9c57a00eabf2ad23f3b948cdfe97eb54b7c2b19fd3df81fadddc77ab40fffad89f69e237f9a68df09da8fd6a3fde7e603b141eba3681f9a486fe0120fb13e1939c40ad60b2bb44f8df52f305f48d7b3fac4d353c2df6b32fd779251de5de21e714f32bf7f097de5f8c285ffd7dbb4fe9622bfbac8ef14e9d7bb347e9e26c4b41f628ef8aa8bfe5f8efe832ef263a3bdeb8a06ff3543fbd5457ae6a664ecf7b388fb601dfece68d9d84f3347fa0db1dfd93a26a6f574af434cf9c9a1afd111f3ef287f83f528b03e9112537a83476215fdf98aee67fd715e7f6da4d760f114fd67f8174394c73c20a6f4cf33c9d02b271e103f4ac6fc07fa6f668bf66b698fc4b49f29417c35dbb43e9b138bf6664ec1bd21ce17c41ab18afa19a1be9b227f8147aca13f12ef10eb186f5a48af29f26b35887570a8121b545f0f894df4afaaa164cccfdf1393bf9fd3ef22ff738578082e0cc9e82fa2bce5f845bb26a6f914f78a98f63745e80f5a6ddaff12a13d59227fc113b181786ca27e58227f7187d8427fbb807fb05405e9290692715e202156c13aeaabdda2fd2546484cfb4f33c42fbb67e2fc43ae11d3f98d12f5c716f3a74bd4379b8d07f8ef637a3ef37fd89f0efd6df1fec591645ebe8b5b628d58958cf9059e9f7a3a0deb07da9098f617eb1e31ed0fce2c62ca4fde030f68fe3f710543df709fd8c4fa7972436c617e29ccc12c3ee27c530516fa964d624a6f9912ebc4d79231bfc5dbe350cec71b4f82b15f5b3b108cfc2e26c4e2bc481b2cf6e7678fe09e0dff13ef8245fa238fd8c6fcccf21e2ce63fc70698f5d7f9f5a30e58f8ebca22a6f45736b1415c49c67900de5e8672fca41f11b791bfa20116fb9bad2eb847e3fb1cf9673f63bf509a1153fa8316b83e40c5d31f81597bc7fc91466ca1fd4f4692d19f46f97484bfdb3188a9fd4c048bfc9e1353fe2aca9f6a12ab92b1bf8cfb83a11c6f1913e21ece17987b60168fe1cfcfc0fd3ee2ef2804b30885f91fe8db95f9457ded2a2df8ffc9297117f12cb800b30e12c6d7683ff5060e9c5fe913d3f933ed4a30ca638ef4f7c47eb3f91d58ac9fc53ab8dfc6fd11fd2ed2171e10b7b1bf643c148cf5c4b443dcc1fe88c903711feb072394af1cef2cd15efa62bfa7d92336e83c24f2db67e18ce77f82fa5eef47c0fa8a42dcc37e9cc51db185f593f888d8c67c48e48345fad29cb88bfd6013d4d7bee84fdbf0177da637fc273d4fed61bc37c6ef723ca35f10d37eede509b83b407f752e98f6cb8da1f7a047e305ab476cd1f9d09cd8867f98ccc07deabf86d7c41d94970d7fc30658787f02bd06227fd994b807fd4697c40afcbd8ffa537798797bd9417b7b5eef3823a6f39ec609b18af78de7e0ae8ef5ffc91e31ed3f9fa2bd0cfb3aea6b047f3514e9090f89fbd86f3281be4351ff1de4675877e8b95eaa645ede23bcff79fc72486ca27f33823f579803c278f48e98cefb4cae892df477e6687f8ad89f36bf25a6f9e700f5491958585f8d915f45a43fbb241e60bdc7477c52d43eea4f85f27c5e0f1910d3fe1f3d2136e05f76505f54713e7154122bf87d227ed7113f271d621beddf44fb55597dc1f9c87de201cef765c8bf3aeca03d066de23eea4f1410537ed229f110fbfd6ce8c5a20fea8f7f46dc072fa1b75c1f29c6c4b49f61722219fddb1e319dff5d2e24737dc721319d079e2c89a93f3a6a13d3f9567321197a415f8dbd9ee76f4ebf8bfdeb73a45f13fb3de737c4b43f728178a3b106cd7f1f45c4e47f22f83f4de8e3217e6bea00e53f87bf795e7f41fbd5db5df427e7884fbad8bf38bf26a6f14682fceb431a2f8547c4f4bee49658c17cc3645f30da7bda2656319f52e279723fcb1cf5cb10e7cd62dc6f88eb23ba9ee590e7c785ff30d421f2171d13ab88cf23a44fae97cce12f4db1ffb140fd34fb038c0f52b40f533c3ff18975ac973a282f5355f0be05da9becaf1b280fd6bbc5faec7497b84be75fe9fa1ed59f10e56bf535d49fbc416ca3bf96c37f58f50c1cbf1ef9b386b43f337a24a6f4b9d08ff5cf311e092d628df4803f90eb0d73a4c716fb1d97a83f763de3ccf38ff6638be7c707c426f6afd9482feb7f430ffb4432ea2b6fbfcaf379e118ccc239ce935d81c5fec7c91cccea33fc750416ef4be9779663c43b5e3f15d9ff35f1fc765fc379db7c0a1e74e19f12a4a72dee8f9e886dec978cdb60d63f4379f2f187d261fe0ee38721b14a3c27d631fed12ec19d16da6bd4010f69bd3db927a6f715bc3fa9c8fe9dbf0feed379eaf8102cae5ff2fe9bd2eb2b18df4d7c302b2fac0fe8c43db49f087af4ea03ba9cc760d546fe96d047f68f4c05dc3670fe3b6a81950efa8fa9e02ef67b8db9bf54647fc4b0c0cc9f613ef908dc1962ff67f20016f73b5db04af38573e825e3bf3d00f76dfabec32398b558e407e5356439e4fadbbcffa2287d8bbe0f81f263ff80fa54e1f932fe8d915fb53fc47cc198b75745c68b39f4d6c479f46513dc33d1bf8b911e8d3968ccf7f078a0e862fec6bc05b3df319e6a11d3fc507a494cf32913eecf94677f88fa6188fd0a09f2630c34c40ffb18acd1fc5885f27cf677286f53dc9f36c01d1aefe926b185f3390efd3ea0f9631bf935351abfcd797f4ab1c4f9f7f90e319d6f9f1f1153bc0befc01d15e7c5c707e01ecda7d948af35e8a17cc30cac0da0c798fb2be5d95fa17c6c71de3dbf0077a8bf52417f5b9cbf7334f06048f9d903b3e7a3bf714e3cc4f86dc2d3ab4a7f35df038bf3f125ef6fa8d2dfccf7c1e2f718f7b73b1638db25b6e12fb40b7057c7fe972801b3168dfde797604d81deee8d64a4ff4832be2fd02356c1f14032fa8f5c6fb523ceeb5b1a98ddcfcbdb9a4ae6ef1bf1f6a94a7f343f048bf981890aeed1fa83e582eb1516ce6760f67ed4cf67e6e90b4692d13fe9116bd03fd9938cf9bd27c9fc7d13e45faed78d63b0387fe9ed80c5f3c296643ccf27d6f1bcd4918cf86f121bf83df324f3faba407aa5bf2c513efdee10fbbf5dfa9d3d0fe59149863fe4ed55adcf6be3fb2b13f080f66b9821b185781514c436f62f6473b062603fcf88f757d4e7fde82598fd8ef42cc02cfd585f4f894de8919e135b288f11f7c7aa22ceb34f1e892dac6f2cb83f51a5ffb4513f54cdc4f8b5e4fe43d53a26c69b4b1e2f55d9dff451beacff88fc58282f83e50ff5117a1b2c43a86f285fe9cf26a84f66b74bfd371e6f54d9ff1a2dc11d0de3ab25eafff3fe8a3e7848dfeb59f0f2d0eae93e9e1eff1cac77687d98e74793ed3d2fc13d11effb6071fe293904eb7dc4b382fb4f4dce5f4dc6e0ce10e74175e2de80f6a3e1fd1d118f46787f577cafc31e82d90813f35d73b0dec67af292d7674db60f5303f754c4a331ef2f69b2fe1678de407c7f6592111b98ffb42a703d22e7f5a50766cd19e309debe3459ff623c9fc5633c2fb4c13aadefc609f110f349156fcf9a1c3f2fcfc0cc9f623d02fa283d8bc68b1d707f80f48db9ffd2647d9c707fa1c9fa3631c12c5e61fd097acaf8ba8c88e9fb43d31d705fc3f71eb413f080ceb36513b062e13ced88fb434dd6cf1cf935fb341e3671bd39a4f5325707b3888cfda407605dc17ef93025d6a9fef178a0c9fa3d7e20a6ef3399481feb2d438f92f76f3559df43bccfee2b74fd0cacd0f75122a4cfd675ec4f1bf1faabcbf8575d12d3f7acac18dc57f13c9daeef8befa33c81d90883b78f058f77ba6c3fe305318d7f2c3cbfad6bd86f1b9c11d37ec711afcfba6c3f954b4cfd93498f98c6e70beedf7519bfc626317d9f2c6c82eb1e0bd77f1fac1b180f24f4bb6e637da4e0fe4497eda9cc89c5fcc1ad64ac178cc0aa46dff3e0fd535db6b7ca23a6fbcb2931cd6f8dbbc4f4fd9691424cfe6abc434cebb1a30362fafe546680d501fc4bf6406c623f8b97818d16f6fb2ec003f1bee29098e63f165d62f1fe2b627aff62444cdffb1ae7c41ae6a39703624adf127a0fea2f44f1fa82fa311874b03ee4d3fb0c9adf9df0f6a90fc5febd49494cdfd32a5ac4f43dadc93931ad6f8c517f86a2ff3eba27a6ef098da07fbda080f512d4bf217350b89ffb1b5d11cf1fa3be2a22fd930931f5f7973631f54fc7e277ea1f8ee877b15ee1227f8a6e637ce6a460a38df299404f55f4af2787c4d41fab04d3f78dc6284f557c2f6704bd55f1fd81d802b3fe1acec3a0bda8468be6a3f1bb26fa8be30b620de3c1d1102cf6772faf88e97b1023a457ab4f10f3e7d3f3060acadf477968068dffe29898f6fb8fd15e74513ee347627aff644a4cf34315daab5e7f2188bf1fefd3bb36fa071aea072b408cbf4dc1169e37467d31c4f779c663625abf58223f8638cf3eb68987e0d11d31cd972dfbe01eed1f19ed11b7e10f7494af31a4f93613e565a81d3c2f5a100f29ffa8ef66fd4529ee2f505ea6f81ecd08fecd14f327a38c98ce5b2e7bc45d7080fa6c6a5dcc2f8fa19725eacb648798f6232ca08f557fe183ff7e414cf393e39498e62747bb6071be7379494cebf7a38164ac07d0fdf50950ae0f5d3f3029de209e586c44cbdb93790a565bc8bf8d7860193d9c27f426c4033a3f86fa618bef318cd1be6d919ff18858417fb56a13d3fce012e567f706385f39ba958cfa71414cfbc934a4cf56e83c6754805503f575c4db8bd112df679a3860f17d8ee52eb83eb1c9afdf918cf2368855b0f5085628de7a365835d1ff1af1f665b4457d19b7c0f5173878f97ac4627de89498f6434d0a626a4fcb0c2cce1b8e1262da3fb8b48875c49bd18d647cdf12e96d3387cbaf779a6036a2c3fbf8f8c7e888f6345e8245fda872622a8fc90131bd7fd427a6f72d5362da9fb5348969ffcfb22b19fb690ec11a7d9fc6a5df0d3a6fb444fabba23e151558d497c9022ccee3562d620bcfaf0e88693f9f4dd7b3fce37c06fdae52ff2aa3fb35fafe558cfc770d03fda325f4e909ff3e3e018bf6560a16f945fde9f5e97b48d513711bf30fcb4432ea23ef2f1b7d515f26015896c73eb84ff32da36bc9e88faac4b4df39467dea1b74be69c1fd913110d7578fc43dcc8f8c2692f1be3d625abfad3262da7fac3f8087d41f9922fd0385fa43156fefc650d6f763b0288f52018be7951e31ed07ab7625637cdb2156c0ba0b5687a82f25f717f5f10db4a731f450447d5ca0fc14717f3924a6fdc6e53131cdb7947c3c67a8a27d2c4e882daccf140a31b5d7c2960c7f87f25645792cdac4f4fcc519318def0a71bd81f5e2f408ac74315e1d833599be01315d5f1e49467a5d6293be07a713d37e439fae1f5a680f31fc87a629f09763b44f5dd497894d4ccfab52627a5e29aea7f5f8a204b30e2dd677f17e5d35e17f52ba5eb3108fd30bb06161bdc54904633daa407d35c4f3aa84b88df5f2b24fdcc1fe761fed9d6508f1369f81590f18f313683f8641df3389511f0c93ce538cf17c53d48f02cf33c5f38b0762dadf5bc5c43dec3735104f4ca58bfe7b521293ff9d46c4627f19fc9fa9f750bf4ade9f606ad0f3463a31ed672d0ac938cf794f3cc0fe561bf5dd627ae33c820f66fd3ff8878898cee354605bdcbf6c130fc1655732be5f342756b0ffa5e4f5cd6c092eee8855da8ffa40ac81cb8e64c4139e5fb32dce2b2c7788e9f762464ce71d162d6283d8918cf52205ac28e8cf580158a3f149c5f5323be2fa71454ce7e3ca03c9883f5362aa9fc63158d1e8fbd0485f47efa23e2f787b36bbe2fa854d6c83ab3bc9f0876d70bd838df3be64f467b83f357b033a2f56e4e0219ddf2b6e05633fcfa222a6f349654b32f6bb1e811503fd196b0facb5909fb041ac613cb5e4fed0acf7d3a0fe6792117f1ce21eb8b8918cf524e4a76f0eb0be58429f0173ef882f3bc40370914ae6e9314cb0e87f4548ffc05411df4648df7040f5793901d73b4ef8f3ae25633c312656c0e5a364cc8fd0ef2c05c81ff451ea1d11fc798964f8872b628dbe7f644986bf7e20d6c14b94872ab8dc958c7810131b184f1597c426d8c6fb556d80f2720a307b03da2f1f4f989ad07f6112d3fd8b5232daff84d802973b92515f6f896db081f4696a9fe69351ff353600427be1fec4d4d9f518df18e07a4599f34232d65b9f88db600be9d7cd3ef5df2c62fa9ea72d7ea7ef572d911eb97fa1447e0c367cc7fc5248dc21be908cefddeac45d1a4fa0fdb0f121f2e75e122be01cf931588f0bf10dedc130e9fc7709ffc2fc3ffc6d392626ff5b0492d17fb489fbf47d0fd46793f510b1be41d79b438c07538b58857fae50ff2c513e05f4b1143a0fb52888e97c68752319fe7d444cdf4730e66095f6776af0cf96d1a6fd953c9e98b6d0ab3822a6fc14e2f721f6cb1467c40a78a149e6ed693127a6ef0198a8dfb6de43ffcd41f9da269df708e977ab85f35a0bee0fad96c85fb9245631df5b85c41ab8f02423be9c12ebe0c5a3648c8fe97e8dea434c6caae89f876d620dfde992b707ab2dd2b36810d3f3966362035c5692d1de5d62135c5a9231fe8ec12aed27b4f6c19a46ebf70a988df0907feeffac8eb8bf3821a6f3f58b9964f8e79298ce532f479279fbab90feaee0c201ab2de263c9bc7d2c9ac46de2a960f4b7cc21b85ec1e1f17b424cdfc34dfa6083c6ff156f0f564fdc5f2e883bc4267117df1731e97a5dc1f8304079f7ac36dacb82fb67abcfae47fb54897be0b2908cef79d0ef1a9d073607c47d625732e2eb21f180fe5ec883647c8fe14a32f687df83cd0ee261c9fd8d3550fbd8ff5dde110fc08b7bc9989f3f251e82cbb964ec1744fd1eb0fa8cfa63112bc411b1582fed4bc67af38858437b704cc9383f722e98f64ff4c07a1ff34d3abd8f75d0d1dfe1fe9775f6e9fcad658335da2f6c39c426f18164ec6f3d94ccf36718c4349f6776c0fa00eb7be6ae60c4abcc2456b01ee5d0f516edffb3e04f148dce331b578231ff638d8869ff8cb92318e30f0bed45d15ba80fc69160cc875a77c4f4f72eac7bc958cf5f48c6f72c0ac1c88fb3473cc0efb642ac60fe399812dbb45f847e678ae3fb12a88faadea1fe725f32e6677cc9182fdd4ac6fdbbc434def122c9484f4a4c7f2fc6ce24a3bc3bc47ddaefdc928cf937f1fe3ee6e7cd8660cce7a5c8bf6ad8f0bf3e3dcfea207e5a789e269fd726a6f6663f33be3fa149e6f98b4dc1183fd81de221e5af2b19e7996782311eb005d3fa9e3d978cf9f233c198bf303dc9f8de85489f8af945f39098e627747abed9c5fe0a1bf543d7e9efdd388664ac471c48c6fec65230d6579c7d620dd7bb9e64ec47542563fdf95630dd7f484c7fefc639958cf9f863620be9378f24637e10fe4337c4f94ffc6e88fb9d6362fafb38ce0931fdfd1ad7908cfe9b4b4ce7a15c5332f6cb3625a37fbb2b98be6fd626b6e9efd30c04239e184dc9d8af86fa6918e2ef55401f83d5479ede688f7888fe880b7f688ae7bbb6647c6f10fecf345ac4c792e1cfe782313fe89e10b7b1be64a682319f63fa92b1ffe994b883f52ef74c32be9f6210ab88c70efc9f69b5b0ffdeeb130f307e75517e96b8dfbd20ee82bd5032d64b1dc9789e4bdc23f624a3ff732b19dfd3f489fbc481647cbf23211e60fec683bfb58d01d60bbc7be221f6037b0f92717d4b32be8ff248ac103f4986ff6f0ac678dd502563feb92456e93c9f2218f323c65032fcd109b181f9550df5d1362d13f35b681f36eb1162fe8bd75fbb259eef5792b19f6d4732ce2f2c8935e29164a4772c19fbff27c43a380824e3fc444e6c108792f13d9b4c32d6d3457a4cfadef3bd608c5f7d1b6cf5d11e031e5fecb661627d208825a37f974ac6f7a612625a3f0dba9279f9270bc1d037e811db882f415f32d6f70660d6fd407fe04e307d6ff35632fcc38d64d4c76b621aef06bcbced0e7b3ef64b2a60f67cb02a19f333a792b1ffee8ab88df9f6f04c322f9f28958cfa744edc21be908cf1d5a5647c6f6841dca3f330c7c4b4fe92207d5db30b7f1e6592115f72e21ee6cba3a964cc7fce88fb58bf8de692b1dfa32d19e3e12be201e243d4938cf5cd7bc1188fe416f110eb33c6a160b417e38098becf66ec4b46fc417deb5a7d3a1f3e265631ff18a17ef44c5a2f8b9bc4345e89069271fe6e2819e5a3102b585f8b54c9384f7d2919eb7b7b82d1ff8f4f88553aef772119dfc310e9d1305f1ca3fef44d8dcaf34c30e2537c2e19fbf9a792f1f7bc42c1b41f2f22d6b15e94e492513f62c9980fcf88697f6506ffd367e33194f70d31ed37773bc426e61712b4e701bb1fe72752c9484f8fd8c47c41f2446c61fd22e94ac6799b9664ec4f6a13db988f4e3a92515fa682b1bfcec825c37f237f03ab85fd9a462a19e39f03e20eed3f2e884dcc4fe5bc7f620fad16e6fbd34a32f6b32f89db882fe9ae649cbf1d49c6fed31dc9587f191377882792e18f1bc45dcc57e7489fc298eb93f625e3fb494de21e381d08c67c71aa4ac6fef821319d3f4b15c9f81e4a453cc07c6a7e2619e7e1af8987988f3274c9f0b79a64f8db06311b01f1e7233faa35c4f82e2b2523ff0b6205fe3b3f25a6ef0be773c9980f5425a37c6c62da9fa63b60e63130de447cd02c9aefcaee2423bf0ab18ef999fc84d8c0fc892eaeb74cbcbf4f6ce33cbf2e9e6f9b98afb8910cff88fac086ab26f6f7a33c74cb843fc98f25f3fa350d88e9fb7ff98098be9fa8a3beebcc63f2f4f9f03f8665d3f756503f0d9bce17ebfbc4030be723f6888716ca775732e211f4b32c9bfc29eeb7ec3ee6a7749bd8b0b1ffbb4b6cda48df9498fd0be75c32c627c89f6d69187f3b21b18efeb575416ca07f1ced0846ffd0b926a6bf4f683bc4d4bf7063c1e84fd8b664cc9fdc48463cef10dba83f6e2219cf47fcb3ed16e28d732b19e3e74bc9189f5e48863f3c908cf4dd11d3fe04df928cfe645b32e63bbb92f1fee7fb71de20978cf1c25c32bedfe01277e07f9d5c32c67733c1f0bfce5432f4be978c78a64ac6782b25eec2ff853dc9d87fde928cf3768a648c8f1f24e3bcc35432d227f4a0f5afd023a6f516e751324f8fd3928cf5a58964fcbd9327c9f81ee2a564c4ffb9649c37ec4be6e9f34d62fa7b04b1482ffd3d81e04432caf34932e6b36792118f32c988bf73c9f8fee7bd648ce746c403cc57bbb9648cd70692f1fdee9964f4c77724a33e8af40f309feb5f4946fa0dc9f02fa23ed3fca6fb2019f5712619e39747c9e86f1d4ac679d7b664a47f2919f31d223ff4fd31573c4fa1ef670e2543cf9664b49f5832e6f7762523fd4f9231feba968cef2bcf25a3bee892513f9edf8ff1d131b18af956af920c7f2df452715e31f42563fc5b48869e8a64a4a724a6efa9047b92a1df4232eaab2619e76d03c106eaeb11b16e40af4832c6aff79231de0925a33eaa92d1de1bc48681f1d6be608c9ffda6643cff4632beb7752819e521fc9d69a2fe1e48869ec21f5b16cea3913fafffabf972f66d6fd9b7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3d9beb5d96cdfda6cb66f6d36dbb7369bed5b9bcdf6adcd66fbd666b37d6bb3c99a8d6f6d5e5b5363a637f56f6d9e4d6842667c6b53b79e754d9899ccacffc9da6cd084a9c2cc6efe8ffceef5564db82a4da7b6ff49dabcd044dfa40933b7e935bdff09da704db48f6bc2ccafed6b6bf3614d9c554db805cde06b6af3f7346986cda8b6afa5cda668fc8626ee664d98c5b57d0d6dde8bc69b3c8ad4245cd5845bd24cfe6c6d3e1a8d3fa709b3b4b63f539bcf46e3cf69c22d6be67f96363f1a8d4993571ee56d4d9a53d89fa2cdcf893c5b35c98426dc66cdf9efaecdbfa009538559d12c7e576dde8dc62f5bcf8f6892bfad09d9e277d3e6d744e3154dd2f735212b7f176d7e6d347e4393e9464d982adcaa7f5b9b7f261a7f4e1361ff9636ff6c345ed1e4cdd6c36df9ac4a73d41c37c7ffbc36ff4634fe9c26cd09ec9fd3e6df8cc6ef6a52ad6ac26da7b9fbebb5f91da2f1164d46af3569eec17e9d36bf28f2fc48345e7c4e1366fbb5fd7c6d7ec768fc394d9a07cc0e9b873f4f9b9f168d3f1b797e54939d4d9a303baaedef6bf32744e38d9aecbda549f398dbc9dfd1e68f8a3c3b1b5acfaa2650e5a4790afb116dfe284d367994d79a1c0b4d989dd5f6196d3e1d8d5f7a947f2d1a7f4e93e679f3a2b68f68f38f47e3f7bceccfd2e4e46d4d985d32bbdaa6cdd789c62fbdec764d84bda5cddf8c3cbf6134dea8c9f95b9a34af99dd346f57b5f903a3f1cb9af2239a5cbed44418b4f9a322cf0f446352e57c43eb79a109b3bbe67df3bed6e60fd1e427449e7735b9ad3511c6b5f9f2d1f8739a707b683e726dbe7c345ed1e4fa8526772f34796a3e0ae3dafc7ed1f8c723cf9bd1f8739a306b316b736dbe7c34deaac9c34b4d98756ae3da7cf968fc394d987599f5b8365f3e1a6fd4e4f12d4dc8fa5c9b2f1f8d45e4f99026bde680db906bf33b46e38f479e1fd1a4bd4d13615c9b9fe1657f4493837f2a1abff228eb9af4d735692a4db5a9369a5c9bdf33f26c8ec6ef7994ed9a745f683258d7a456851b3f23f4414d5eb69e3f281abff4b2af34515635a1ff34aecdaf8dc62f3dcaaf8dc65b23cfc73469e8dcf819a17f35f2fc23d178ab26cd979a08e3da7cf968fcd2cb6ed78499d9b01afcfcd46f138d5f469e9f168ddfd4a4b14913615c9b3f3d1abf6c3d3fa289bdaa4ac369b80d8fb4f9e2d178adf5bcab8930aecd978fc6af6aca564d98f9b5496dbe74345eb1d79ab82f356116d4c6b5f9f2d1f89526d60b4dbc554d98858da8c1cf4f7dfd68fc394dc8f8f9a9af1f8d377994b7356924ccd2063f23f4f5a3f1268ff2b626c2b836ff44e4f957a3f13b9ac4eb9a34b246de9836f819a12f178d37461e661fd04418d7e6cb47e37735c9563569cc98cd1bfc8cd0d78fc61b3cca064d84716dbe7c347e4393fc8526b3674d1a0b6efc8cd09788c61f8a3c2f3dcadb9a30ab982db9363f698df4378ac6ef78d917ade785266423aecdcf8ac6ef79947f2d1a7f4e13b23169f3c5a3f1e73411c6b5f9bd23cf4f88c66f6a52bdd0642255d961b6dbd85dd1e677d3e4274663a949f9114d98edd5c6b5f9b7a3f10f8c033f178ddf6c3d1b3569ec333b681cac68f385a3f1164d765e6b42c6cf08fdbd71e09f108d5f68f2b2a6bcd0a471d438ae4d6af3a5a3f196d6b3aac9213411c6b5f9f2d1f8739a303ba98d6bf3ef47e3e8d746e38d9a1cbda58930aecdef168d5f469ebf1d8dd73439784f93c669e38cd93969f3c5a3f1e73411c6b5f9f2d1f8739a342e1a97b591365f3c1a6fd0e4846b72f652136657ccaeb9365f3e1abf53535e68c2eca6b65a9b7f2c1aff78e4f9559a5cbcd08454e1c6cf4f7dfd68bc5593ab979a70bb6bdc716dfeac68fce35ef66253eb59d784db7de35e6af3a5a3f1e73461f6c08c9f11fa42d1f8e46768c2eda9f124b5f9d2d1788b26cfaa484dc85a5c9b2f1f8d5754f98026ccdab5496dbe74347eb3f56cd4848c9f2dfbfad1f85d4d5655e930ebd6c6b5f91991e7b78ec62b5ef6039a90f173771f8e3c7f6c347eaff5ac69d26df4990d1a03aecdc7a2f16723cf6f148d3fa1095705c6b5f9b5d1f8a547f917a2f13b9af4d635690c61a4cdef188d5f469ebf118d3fa709338599cab5f9f2d1f8a597ddae89d68491369b34f9f159d9df2a1a7f4e13327e26f18f8fc62f6bcae734515e6aa2d17fa4cd178fc61faa295213fe9faef1bfe9f6f5a3f13b9a34d6356166d4c6b5f9f2d1f8039a68cf9a703335936bf3e5a3f1e734e16669fc6fba7d22f2fca1d1789347795b13664e6d5c9baf168d37469e8f69c2cdd55caecdbf1d797e79347e57137b5513665e6d5c9b2f1f8d573431a426d6264db8f91a3faff90f44e3bb7f371a7f4e132de016726d5e7bd92f168d3fa8095409b9451a3faff92f47e31f8f3c1f8ec62fbdec764db41826b5f9d2d1786beb09a52aa4899670e36712bf7e34fea02650251546dafcfa68fcd2a3fcea689c69b936d566da5c2bb485566a95b6d4464c99cd9a8c9f55e1c6ffe6c96f3d0efc6c34ded176b53d6d5f3bd00eb523ed583bd14eb533ed5cbbd02eb52bed5abbd16eb5bbed9a08236dbe4634bed71eb447a6ca93d6d2da5ae79575b59ed6d706da50533475b326b5e94d1d735b5f201ab30b75ddd00e7453b774fb0d5da4e98eee6a03ddd37d3d785b1366616d5c9bb722cf1f158df5488f992e899eead99b7ae46b36d567fa9c5d5fe88b1555a426dc4a9dff6dae3f3d1aeb4b7dc4723ad627fa8ebebb667b6bb6bf6207faa17ec4ee3ad64f5e6ac2ed54e77fbbe24f8dc6fa997eae05ac2d79fa857ea95fe9d74c9bda6ed6ec76cdeea4ddeb0f7ac2d479d49fb826a5d08459ab36aecdaf8ac6c39f3f36163d14bdad77f4aeded3fbfa401fea8aae1acdd5ad79ccd6429aa1af1a5391996118a661690786cdb45951c570605c9b7f27f2fcd0d818bd36c3353cc3671618a11119b1ae326d122325cbd62c5fb3e98acdd85d73a360ea2c5053842a46591bd7e6f78bc61bc7c64c97ca581a23ae8c6f8c8d89b1532bc372b96becadd9fe8a1dacd921d911bfefd83861ea9c3e6bc2ecac36aecd6f158db78f038d73e38274815d421996c72be39ad9cd9addaed9dd9add1b0f74e7a3f1c4ea5f4b6862b4b9616fd26f148ddf1cf3d0d8d8e81abd355d6aeb0b6d581e07c6904c593175d5cca6b4c6f39da6a61d983aa9d2313aa6511bd7e6b789c69bc781a6695aa6fd4a19df745672e89a9ee9330bccd08cccd84ccc74c5323337a7e6cc9c732b9eefabbd8eb9d00766094d9855b5716d7e381a9bcb9f1779b68d8dcd91397ead0ba93359cda5b963ee9a7be63eb703f3d03c328fb99dd0bfed9ba7e69979be7a0fbfef82d59c4b68625e99d7b5716d7e301a9b37e6edafd5a41ef39877e6fd265db8360f6b797c349fa40afb66cb6c73eb985df96f3db36f0e5e69333415a68e5a6b62d19fab206d7e20f2588dda95b27cffad68fc7ab66d756c6ce996f1564b5a35cb14f9b32ccbb61ccb7dd6c6f22c9f5bb0a2d7be15b2aba297ea586cd46125a44b6a6556c6b5f9012f6be5d6d49a5973abf8f168bc591352666195db7521752a56ee03a6cbd21a59636bb2a2c28eb5cb6d6f4d9b7d76d5c83ab00ed7b4612308ebb8d6c43a8191365b34b14e5f7b14f3c63ab3cead0bebd2bafad168bc7d0ec5bab66e3ea20bd7e6d67ce4aadc59f7d683f5b8a2c293d5b2dacc3a966c53568f69d567d7ddb13bacb576c55a953520658696c2b5d91a792cd56eda0d9671dda676639bd6996dd9b6edd8aeedfd4834de3eaf64fb7660871f55c68eecd84e585efb766a674c85b69d43057b6acfecb95d309bdb0bbbe4ff56d94ba655cbead829d3e8c11e3d6b638fb5037b62299662efd4c6b5d91a8ded5d7bcfdeb70fec43fbc83eb64fec53fbcc3eb72fec4bfbcabeb66f7e74a67ad3cc927d6bdf7d5417a6ccbdfd603fda4f7666b7ecb6ddb1bb4c879eddb707f6d0566cf5d99ca6d36009d51d836935635792428ef4570e1b5b393674711c87aff9bed743713cc777022774222776122775322777a6cecc993b85b3f8d199eab766db9cd2a93eae8bb37446ced899383bceaeb3c7f25b38fbce0153e1d039728e999dbcb0fadf8e9c5376c581b32f15ea48bff3c0b439735c6ee70e3fe7fb5e0fc5b9702e9d2be7dab9716e9d3be7de79701e9d27a7e5b49d8ed3fd7834de3e03e9f49cfec75b125366e00c1dc551dda6dba827feb82ab52627aee1b26ea06bb9acc9bb8e349bfd0b0b62aecb553ab255d7e30a755d9fb461fec60d6a559c733774f9bcdffbe3404b7523377613e7ce4d9d969bb9b93b7567eedc2ddc855bbad5fbd178b32698537297eee8e3bab86377e2ee381377d7dd73f7dd03f7d0852a2e57c4718fdd13f7d43d73cf995d703b6774cafef584fd56ab640a856a7dd0aedc4ba6cd55ad0a8c6bf3ee38d0bd766fdc5bb7ae332df7de7d701fdd27b7e5b6993e1db7ebf6dcbe3bf8b42672b6cd1dbaca273c4ce4aa5ed351bc465d63ea292ecff04ccff2ea5a526b72e65e788ee77a9ee77bc18af95efd9fe3854ca75376a5c314aaeb10d3c7e3bd1d2f86365ec22cf5f8fad4477a2896ea655eee4dddcc9b7973aff0165ee955ded21b392d6fec4dbc1d6fd7db7b4f93d7b3b2debe77e01d7e5c19efc83b7677ec4777d73bf14ebd33efdcbbf02ebd2befdabb714fbd906972cb74b8f3eebd07efd17b5ab147afc5feed8e540a9986275e9beb736cabb5cf314cedc0ebd4aac0b8361f181b7b5dafe7f59d476fe00d3dc553fda6dfa8a7df58724d37655d4fdb77bc9eeffadec734c1acaceffbc1677caf1ffa9133f61ace8e1ffb89a7fba99ff9b93ff567fedc2ffc0557e5c17bf24bbff297fec81ffb136963c63bfe2e53a9c5940b98868ebfe7effb0775fbf299363eebfdf9c7b52afe09333e5ffc91b1b17fe69ffb17fea57fe55fb37fb8f16ffd3bffde7ff01ffd27bfe5b7fd8edff57b7edf1f6cf228a4899c95f587fe275a92e1fb6ad00c1af6a3a3061a6b4b67811e1881e95d055660074ee0b266e37b8f4c95a53f0e82200ca220669690a5ecff474116e44ca5915f31851e8269300be641c1daa11d2c58ab62fe262883ca3f85716ddeeda104cb60148c9d6930097682dd602fd80f0e82c3e02838761f8293e034380bce838be0f25d4d302bdbd2ece02ab8fe8cef0d6ebc66ed7d9d9ddafb06b7ee6170e7e7c17d5d638287e031780a5a413be8305532a64237e805fd60100c032550b90d19f5d9bf76994a5110b25ab413369932f3b0c1dad749a8e96aa86b072c50325542b336aecdbb3d94d00aedd061aedb73efbd65e8874118869157847198846998857938fd802634531dcec2f9677c6f58848bb07494b0aabd6fb80c47e13830c34960853be16eb81778e17e78101e8647e17178c2f23f08d4f0343c0bcfc38bf092ec82d15978159e3295fa4ca19865e19a2973c3da97ef39e12dd3e68e6973cf5479081f6be3dabc33360e9fc256d80e3b6137ec857d771a0ec261e87b15534609ce43356a7e4413cc54478d48fb4c4b8af4c8884c7727b29ee375ed7d837bef3ab28387c889dcc88bfc2888c2288ae228094fa334bc8cb2288fa6d12c9a4b9b31ce984ae7e115ab47fda8a86b4db4f04be6a182a88c2afd285ad69a442318d7e69db171348e26d18e73635f846eb41bed45fbd14174e8cdfcabe8283a7edbcbbed484eb72129d46679fd0e53cba882efd28baaae3b51f8b781d5dfbd370c79f4737d16db81fdd45f7d143f4183d45ada81d75a22e53651ef5a27e3488869112a9cc146683b8c9fe75ce15ba881bb116cc633d3658ebdaf55ab119b311436cd79ac4acfb1fb3ee1cd7666baf2df6629f39b5308ee2384ee234cee23c9ec633ff299ec74570f1be2698bb8f17f187661c84c555bc8cccdafbaec6eb7854c7eb781c4f0237de8977233fde8bf7e383f890e9d262fa3cc647f131d3448d4fe2d3f88cd939b7334617f165a4c4cdf82a9ac5d775ad896f58eb8a9877aee2db98b5a8f8bed6247e80716db6f6dae2c7f8296ec56ddf8d3b7137eec5fd78100f63c56fc56ad24c1a2b9a9cb0dbf5979ad4b3f78991989fd125b1123bba643d3c8ad7eefe7abc4e9ce031718356e2450f899f04bcc6c4ac4d85ac6df949c874384fa2244e9224254b922cc99329d3e824529359c294498af02c18260b76cb3829b583a44a96b526c9881bff9ee8b6b171384d26c9ceba974d7693bd64df6f2707c9617224ea49729c9cb05cadaef3f0158de42c394f2e3eae8b1d2597c955721d34c468693d5e87bb75bc4e6ec283782fb94dee92fbe481d97df4c87cf121f3c9fbecd747a6c653d24ada4987accda89bf4984251d24f064c9961a2242ad4499b69433b48356892ea30aecd07c73ccf5e36355233b55236c047db49ddd413794b7d5ae5e12b1a69907e62fcc8ee8ed2385c205ea7c9cb785d7bdf340d9ed22ccdd3693a4be7519c1651ccba2fb3749a782c8e3fb178fe902ed232add2653a4ac7e984d938dd4977d38a69d44af7d2fd609e1ea487cc23cda04eca7a7de9b1d084d90933f46f3eacc9b34749cfd2f3f422bd64ba5ca5d7abbdfef486d51bbeca93dea69f98896177dea70fe96372cdfabe4afa84d1521daf3d19afa31bc4ebe83e3c8a1e93fbb4953cc487c95dda8ef7d24ec27e6575ca617d412beda6bdb49f0ed261aa301ba66ad6cc1ae938d332bdf63499119f321fcdd4092f33d328322bb38526b08caf876fd564a397cdd8d02a6e1b65f62a8f59601a59984559fc1965b2244bc3451dafe17db3cc3b5b8dd759be1eafe17d5997d78f1eb269d0626de93171d8d861c67c52ee67d92c9b6745b6202bb32a5b66a36ccc9599b03617317594a897ed64bbda41b6f7ac09b3fdec203b78d6667d6cfc91c8931d6647c65976fcd29b6427f1223bfd942e67ce28b9aae375ed7db3738c96ea789d5dacc7eb6c5ac76b78dff890c5a7fdec92a9e5c53b2c6e4de2311b735eb1b1e7058b6967d955769ddd64b7d91dfbdf7df6903d725d9eb2166b5d2da6ce34bec8da598729d35dd54458adcd673591ebc6676655b79eecf54aec27cc8eb27ebccc06cff13a1bd6def775bc4e3b75bc4eee582b7a603586795fa6d33dbc6f7493294cc36974cd5ae098e9cac65c6e93e9ace6cdbc916bb52ab5e57a6e64cd749c9bcc3367b995dbda41eee4eeaa2ab99733f796f3bfb1f4794dd6d78da3e6e76ac9aae5611ed53546c4ebfc55bcae474b88d7b5f76535267e8ed7cc2367c153e4b0feb1ed5db3b67719dcb987acefbccfbcf8ae337177bc669ee4699e3155f27c9acff27956a64a5ea4bb49275fe42c72e755be94aa704d84716d5eed58929a38db3411ebc6e655fe8939bb674beff3713ec906abf13a5c067a3d5aca77d6e3753d5a7a8ed7c91de235bc6fb89bef325f3d6163ac31f3ddcbbac638aaa3048de43adfcb27f97e7e901fe647f9717e92dde5a7f959aaa693fc3cbf60ca5ce6572f3561769ddfe4fc1b763fac498575e37a2530bffd748db9cbeff387f5789d3fd6f1baf6be88d7f913e235ab21b37ab4f456bcaebd6fde6299bbf35316eff7f3d8a9c7eb63561387c955dece3b769477f35edecf07f9305772352ba7cd69c35c64d6547b4b13615c9bd7ade7c39a8835d2a9fe195da6c6d4cc27225e3fcf6e225e4fdf8cd7d15abc8e7785f70d2cd62734bd0be6a1ce98a73a7154fb316844663698da5367eaca777a537f1ae427d3701a4d63dd9b26d37445959b559b6653d6006b6ddef028a48954a55add61b0a249136ba4d3e974f60965e6d362ba98966fc5ebdafbaec6ebec32de7f8ed7b5f7cda6d3aa8ed7d18d3fafbdaf9f7ba69f321f95f87176ce7cd6988d4e59cf7aba9c8ea2b5129b8ea793e9ce7497e9b237dddfac09b383dab836eb9ab43769b2a6cacaba717a3bfdf08ceff428b1a7cbd7f19ae5ceacbd2fe275f018ee4fabf578cd14a278cd5473987ab688d74cd533a6ee89bbcb94de89cc69395d78c7d3e3c47ae3fd27d3d3e9d9cbd6c334c99e35999e73bbe0dabc6e3d9b35499f35c1bab1164d2f3fa8cbd5f49ad5725bc46b477d8ed7182dadc7ebb4bd1aaf59cb92f13adc0d7758cb9bacc76b47f1a3e43a7d64fe3d9dde4caf364480707abb4113a872c1ed6ecabf0bf956e4795f13ac1bb3f6b47577ccb3d9f7d387bc9d5c39c33a5ed7b39b75bc0e6e990765a325e64d5766379fe375ed7da38879628ad7cc3bb378edcf44bc66de9bc5eb3441bc0e17397bc7f431d95e5aa3e9d3364da62d6efc3b6d3fa289b069e74335a63bedb17033c9f7d6e3f5f3ec66befbd1785d8f96588467358645fbb5789d0df2c9d47127d3fed6b40ca6c3a9f24a938b674dc8f8f76fde8e3caf3419ae6a52afa53beeacf99131d3ac91c6ac3417225e3fcf6eaec66bd683a3784db39bedd578cd7a7f14af315aaabd2fe275ed7dbd66ed7de3e54c9be933635b5a82eb99c9dad2564d661637fe5d81579a64ef692256d367cebbbab8338fb5fee5a6785d7bdf4c798ed7dcfbca781defafc7ebdafbb291848cd7b5f715f17ac61adad47496db52a377596bdaa4495b6842c6d77c5f479ef734c15afa2c7a57997816b0825ed4def7edd548e17d45bc16b39b225e47776c84792be27576e1996c04fa46bc9e69693c4becad299aa5b36cc5a36cd4848cf76fb67b94754da00a6c36dd961267195dccb4d578fdd66a643dbbc9624fab8ed7627613f1ba9edd5c8dd7b5f77d1dafc3451daf5909cc7cf59d729acf8a4dad675d136e8bd9826bf3494deab5f45939dbba53260f67cb992f464bdb5623939bd5789dbe19af038ad7e108f13a7d5a8dd7f93df332ef8ce866e3d9e41d4d769e5581716d94375bcf2b4d6a5566a55849df363b3e75678188d7efad46aec7eb745e7b5fc4eb3415f1ba1e2db17b6fd7e3753d5a9a3ece76fd70bad5ff3265f666fb1fd76476c0ec70c6ff56cee734c15afaec688b32d775bcaebdefc756236bef2be275721befadc7eb7ab4c4ea1af3beafe335ab31c7796776f28e32a7b3b3373cca6b4dcea189b05a9bd75e769326b52a584b9f6d5c39982588d7db572383a7c85b8bd734bb29e2f5eaec26e2b5ff2a5ebb93e066f66e9f7c7635bbfe9c26b39bd9edec6ec6fbc56f6982dd39af358185e6f3bac24b65ea78fdd9d548cc6ec67b91ff3a5ed7b39b2cb6adc6eb451daf672c2cd9eff4c967f7b30716873eaec94dad89b05a9bcf68c2ed61f6387bdaa0cc627576f3e3ab91f5ece67abcae6737eb78ed697e5c8f96a2ab3a5ea78f88d779346bcddc779469cf3aeb91e7639accbab35e6dd00691e77d4db0bf207c9cbdd92bafd7dc7e7c35f2ad782d663759dba4789db7eb1a331bcc86db7b79866f9533e50d4d0e5e6872fb5a93990aabb559d7a4deb1b44913ec2f889d79f3755ae68df5d1d2e75723c33d11af576737c38af9f3f2395ecfb5c44e2eb7f7f2586af4b9f13af2bcf428d2d634999b73ab36a14dadc9ab9af24a13da61f0307f75fe22b1ec874df17ad36a24e2f5faece67abcae6737d7e375703377e6efb425a68cc71ef5694d982a96b460cee7d2df683d6b9ac85d170fc2e62fd671ed6816fc8cd5c8d5781d6898dd44bc664d329a1d4f8ffd70feeee8769e30d1b76bd2dda849ad4a36cf6babb5f9b826d86190eaf3e97c365ffdaf982fe6e5bc9a2fe7a3f9783e99efcc77e77bf3fdf9c1fc707e343f9e9fcc4fe767f3f3f9c5fc727ec5fe7731bf9edfcc6fe7772f5723c5ec26e2f5f368a98ed7acc6dcbfd7cb63ca3cb0decc764d5eb51ea909a9c28defdb7a6e3def69226cfe346fcddb1fb6cebcbbc17af3fecbd54811af6beffb1caf67ba330a8bf4dd79b4f9607ab039f27c48937c3e64a6ccf93998579ab8eb9a3cefbac05aba97ced54f28d32e9a1bb5e9168df578bd3ebb395d66691daf0b6de6cd1aefe962f885cee2d2dfd60456f0b3ac1fd544aca5176661b11cdb8553b88557f84550844554c44552a44556e4c5b49815f3a22816455954ecca6531daa8cdb898bcb51ab91aaf8b9da959ecbea78b7758ec7d5093e085268feb9a70db2ff66b6d3eaa095f4bdfcf0e8a0396dfc3e2a8382e4e8ad3e2ac382f2e983697c555715ddc14b75c9bbbe2be78281e8ba782b53ea6e5e69ad37e7b3552c4eba2933e14dd776b4cafe86f8ec69fd384dba0e0df92dfa0c9c94b4d841543965ba55017cde264d15868c5f9425f180b7361419b85bd7084360b77e1cddb0b7f116c566711be5e8d9c6988d76c8c7dfd5e2fcff017d122de168d3fa10957a5182c9245526bb3a289be4d13ac1b2f5296d76c91336d98a364dacc1705d366b128a1cda27aa1cd723162778cb7683359ecacae468a78ed8c16bb8bbdf77a794c99fdc5c1468ff2039a303b5c1c2d8e6a6dded0e4c54e94dc7b5e1d5d1cb39c9e2c4eeb7ac3b5d1b836c6466dce16e7ec8e8bc5e51675aed66737f368eecc5a8bebfc03fbbe1637fee0bd68fcd2cbbe6c3deb9a70bb5df0bfe9f67ac7d25b9a903277a8358bfbc5036f538f759b2a2e16467129dbd4d3a2b5a24d7bd15974d95dbd2ddaf41703ac468a783d3517c3fc033b0517caecece391e7b526a4cab3265c95855a364b7e3efc4d4ddedc7591df948d79bbd44abd344ab3704aab8e53a5fd1ca74a07beb874599c7a28ca5a9bd22b7d765750869bd52923785fc4ebe8828de8dfede5197e1997c94fd5e4b6d6848c9f11fa8826a44c56e6e5b49c95f39265bb2ccbaa5c96a3725c4eca9d72b7dcab6378b95f1e88185e1e9647e531d3e6a49c32754eb76873569ecf76eb785d5e4c6fb2fea615db35652ecbabcf459e154d0ed735414d8126ccae6b8336ef6922d68dcb1b96c35b566bee58ad39468b629d98fbda13970fe563f954b6ca76795076ca6ed92bfb4c9b41392c9552ad9af376d5d8accdbc5bf1781d5d54fafb6349c3af8ccafc559a541637be76b75913b9eb82af90564ee5565ee52fee0bb50aea5a53852b3dbfabd5085e45555c2555ca3cf1b2ca983679c5627935dda2cdac9a4f6ff24ef4819d3c55512daaf21391e7539a30e3ff496db66822d68dab25cbdd68715fb7a76acc7ccd69ed87eb5a534d588b7aac76d65b14bc4db55bed55fbd54175c8ee3eda5a738ec3c2ddf8bd9215654e2ad63aabb317aa6c89c69fd384ff775ef133d0ef6902ab2eaa4b566bae0ab55c144e75cddad3bcbaa97d4d75cbfd308fdfd55d755f3dacb528af7aac9eaa56c5c756d5e67167adcebbfd5fa64cafeaf36b07d5f0435ef62d4d565579a549a5546a6d429bf51d4b6fada52f99c75836160f759d61ca346a65d09eea9e4ded6b961a6a0d8b513c7e2f75dea2f64a75692ccda5b5b4d9139c6dda2cdff5344b6fe9cbab83cf469e5735e54d4d84d5da6cd46465dd78192ea3ca5bc68b6659b211e629297351f7f9eaf684e80d5fb3526b8eeb5ab34caa8365bacc96793dee5c6ef1392cbf5bf7c62de7cb62e5dac5b2fc159aac69237771bddc9df3bc46ba64e3e9e572392a4e96e3c25b684299dad314c97252dc94adeaae3c58ee705f237ac4dc0f2f8daac56acdee726fb9cf9e72b0559b62b9717fdcf26879fce2ea934f7ad9754dcedfd6644d9b2d9a603d7079ba3c63b93ae7bacc596c3aaffd4cdd9a6a656a4fc362f74a7baa2394f035b2d65c2c2f9757bce65c6f5567c3b75d96b7d5ecd5b577cbfb9fafc99a361b3511ab81cb07a6cc23745914758d593e3165cc65abdc439d29f73133b16ca33dd5116ad9215fd3a55ad35bf69703f6a4e1566d94ea8d35dca5faf6d5a3e66722cfa6d6b3459bb7355959231db191c2482b476ce4142cf4bac6549322ae3d30eb09af285376473ac56ed69e789f8f45a8653a32ea5a333247d688f9e3913372b7a9337ab1663af2475b663846e1cfd6645d9b4d9a60ed6b14b1fcc44550de1717b52ee54e112f5b755baa7b34ac13d6e67d1aa10c8f4f2bedc91c25cbdd513aca96fd513e9ad6917cf4aa75ace5763e2a5694592c6f47f32d5797a3eabd68fc63566bf37ac7d2fa7ae088f5f946a3e513f3bcb7757da97dcc685cd798a556dcd61e18ad4928533cd5ca8c26d53e62f76867b937da1ded8df6470798411e1d8eb6f60247c7529993d1e9e86c74bef5ea8b9fabc99a361b34c11ae9e8b2588eaeca9d4559abb2b0a04bf95464c56ddd96160e1b3db95554f6ead62494a9f646d7757c1add8c8cd12d6b4f77a3fbd5f9f5d1c3b6dc32af33470f78f4c894391b3d8d5a5bb4698f3a3f5393356d765e68b2b61e38eab29cf4ca875a15e65fea7674c35bd2fee249b4a5a2a862f2c0676bcaf46b4f331a8c8623657ded61b4c1b70a1b370d7fdc98cfb832ccc6dad6abf59fa9c99a365bd68dc7c6d81c5baca65cf3bac254295b757d812e65a79857116b4b2c6abf5606917b6c8f9db75666c65bfd31cbaf37f685325c9d2dfe781c8ea35fa6cd86dd39b3bb71cc7291d49ab0110157a5ba2ba6d57dad4be9d635a67828fb4559f76716ed3a36b1d1d33e9419a7e36c9c6f5ab51a4fdfd12658558669b3d57f8fe7bf489b8debc6e362bc189745cefa755c93f2605c1533d6f7bd2be6cb76dd926aef2b6a0c1b71674b63bc1c8fc6e3f164bcf3de9ade78f71d75f6d6d5591d2fbcb4623cdeff45da6c58371eb3b8323eac1561e3a4196f434c15565beeeb7654f6c747e3e3f1c9f8747c363e1f5f8c2fc7579f59ef1c5fbfa3cdcdc85bab39b7cbbb2d57dffd126d36ac918eefc70fe347aec71d1b0b304daaa82846faf869dc1ab7c79d71f7334abca9ce96f9759edffe8b7635d87af5f09768f3e61ae998c596b1cac647f7ccab1465af8ac74793e6b8f3771579b6c9d659d27a2e70a2ad6a33d15e8fa99e6da24f8c9faecd9b6ba45539312716f327ccca7e954cec89531cfe3c5d6adbb6524ef9755ff89cadd16de2fd746dde5c239df8ac5c83f2b03cacd22a9d8493e8e7ea423527deaecdf26e7cbba68e37bed9a24d32497fae366faf1b4fb249ce468ccc4a6532fd15bad4b67da59cab53acd79cf996b1155367f693b579638d743267655a2c3bd5ee6431297f95325c9d2d2be5b0d1f19acfa95ece70ad69b39c8c7eaa366fac1b4fc69309ebc53d4e76eab5935faacdc5f671e7eb5ee0646bbf68b2f793b579b1463ad967116a343998fc64dffbb64dded186a9a3bd50675bcd39999cfe446d5ead7d4dce26e7938b7a6fc83fa2cde5b69572deaa5a6c24beaacdd5d69a73fdf3b479b9ca33b999dcbe3d3efc65ea6ce9ef52cd69bea839f75bd579f869dabc580f9c3cfe93ba706d9eded366349fb4d6b4696fed05763e3a5ffeae366bebc693eea4f74f6bc3d4e9bfa7ce64f0a2e66c9d919ffc8459ae5a9bf5b5afc9a7f6c7fe34db32af075bdeedacb5ab9dc6ce96f9ae1d7de76f8f1ffef3fffcaffff8bf491de25c</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/chatwindow/Makefile.am b/kopete/kopete/chatwindow/Makefile.am
new file mode 100644
index 00000000..bcec2bed
--- /dev/null
+++ b/kopete/kopete/chatwindow/Makefile.am
@@ -0,0 +1,33 @@
+kde_module_LTLIBRARIES = libkrichtexteditpart.la kopete_chatwindow.la kopete_emailwindow.la
+noinst_LTLIBRARIES = libkopetechatwindow.la
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(KOPETE_COMPAT_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private -I$(top_srcdir)/kopete/kopete $(all_includes)
+
+kopete_chatwindow_la_SOURCES = chatview.cpp kopetechatwindow.cpp chatmemberslistwidget.cpp
+kopete_chatwindow_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_chatwindow_la_LIBADD = ../../libkopete/libkopete.la ./libkopetechatwindow.la $(LIB_KOPETECOMPAT) $(LIB_KDEUI)
+
+kopete_emailwindow_la_SOURCES = kopeteemailwindow.cpp
+kopete_emailwindow_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_emailwindow_la_LIBADD = ../../libkopete/libkopete.la ./libkopetechatwindow.la $(LIB_KOPETECOMPAT) $(LIB_KDEUI)
+
+libkrichtexteditpart_la_SOURCES = krichtexteditpart.cpp
+libkrichtexteditpart_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) $(KDE_RPATH)
+libkrichtexteditpart_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KPARTS) $(LIB_KSPELL) $(LIB_KDEPRINT)
+
+libkopetechatwindow_la_SOURCES = chatmessagepart.cpp emoticonselector.cpp kopeteemoticonaction.cpp \
+ chattexteditpart.cpp krichtexteditpart.cpp kopetechatwindowstylemanager.cpp \
+ kopetechatwindowstyle.cpp
+libkopetechatwindow_la_LDFLAGS = $(all_libraries) -no-undefined
+libkopetechatwindow_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KOPETECOMPAT) $(LIB_KDEUI)
+
+partdir = $(kde_datadir)/kopeterichtexteditpart
+part_DATA = kopeterichtexteditpartfull.rc
+
+rcdir = $(kde_datadir)/kopete
+rc_DATA = kopetechatwindow.rc kopeteemailwindow.rc
+
+service_DATA = chatwindow.desktop emailwindow.desktop
+servicedir = $(kde_servicesdir)
+
diff --git a/kopete/kopete/chatwindow/chatmemberslistwidget.cpp b/kopete/kopete/chatwindow/chatmemberslistwidget.cpp
new file mode 100644
index 00000000..16e37991
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatmemberslistwidget.cpp
@@ -0,0 +1,263 @@
+/*
+ chatmemberslistwidget.cpp - Chat Members List Widget
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatmemberslistwidget.h"
+
+#include "kopetechatsession.h"
+#include "kopetecontact.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteglobal.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetemetacontact.h"
+
+#include <kabc/stdaddressbook.h>
+#include <kabc/addressee.h>
+#include <kabc/vcardconverter.h>
+#include <kdebug.h>
+#include <kmultipledrag.h>
+#include <kpopupmenu.h>
+
+#include <qheader.h>
+#include <qtooltip.h>
+
+//BEGIN ChatMembersListWidget::ToolTip
+
+class ChatMembersListWidget::ToolTip : public QToolTip
+{
+public:
+ ToolTip( KListView *parent )
+ : QToolTip( parent->viewport() ), m_listView ( parent )
+ {
+ }
+
+ virtual ~ToolTip()
+ {
+ remove( m_listView->viewport() );
+ }
+
+ void maybeTip( const QPoint &pos )
+ {
+ if( QListViewItem *item = m_listView->itemAt( pos ) )
+ {
+ QRect itemRect = m_listView->itemRect( item );
+ if( itemRect.contains( pos ) )
+ tip( itemRect, static_cast<ContactItem*>( item )->contact()->toolTip() );
+ }
+ }
+
+private:
+ KListView *m_listView;
+};
+
+//END ChatMembersListWidget::ToolTip
+
+
+//BEGIN ChatMembersListWidget::ContactItem
+
+ChatMembersListWidget::ContactItem::ContactItem( ChatMembersListWidget *parent, Kopete::Contact *contact )
+ : KListViewItem( parent ), m_contact( contact )
+{
+ QString nick = m_contact->property(Kopete::Global::Properties::self()->nickName().key()).value().toString();
+ if ( nick.isEmpty() )
+ nick = m_contact->contactId();
+ setText( 0, nick );
+ setDragEnabled(true);
+
+ connect( m_contact, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+
+ setStatus( parent->session()->contactOnlineStatus(m_contact) );
+ reposition();
+}
+
+void ChatMembersListWidget::ContactItem::slotPropertyChanged( Kopete::Contact*,
+ const QString &key, const QVariant&, const QVariant &newValue )
+{
+ if ( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ setText( 0, newValue.toString() );
+ reposition();
+ }
+}
+
+void ChatMembersListWidget::ContactItem::setStatus( const Kopete::OnlineStatus &status )
+{
+ setPixmap( 0, status.iconFor( m_contact ) );
+ reposition();
+}
+
+void ChatMembersListWidget::ContactItem::reposition()
+{
+ // Qt's listview sorting is pathetic - it's impossible to reposition a single item
+ // when its key changes, without re-sorting the whole list. Plus, the whole list gets
+ // re-sorted whenever an item is added/removed. So, we do manual sorting.
+ // In particular, this makes adding N items O(N^2) not O(N^2 log N).
+ Kopete::ChatSession *session = static_cast<ChatMembersListWidget*>( listView() )->session();
+ int ourWeight = session->contactOnlineStatus(m_contact).weight();
+ QListViewItem *after = 0;
+
+ for ( QListViewItem *it = KListViewItem::listView()->firstChild(); it; it = it->nextSibling() )
+ {
+ ChatMembersListWidget::ContactItem *item = static_cast<ChatMembersListWidget::ContactItem*>(it);
+ int theirWeight = session->contactOnlineStatus(item->m_contact).weight();
+
+ if( theirWeight < ourWeight ||
+ (theirWeight == ourWeight && item->text(0).localeAwareCompare( text(0) ) > 0 ) )
+ {
+ break;
+ }
+
+ after = it;
+ }
+
+ moveItem( after );
+}
+
+//END ChatMembersListWidget::ContactItem
+
+
+//BEGIN ChatMembersListWidget
+
+ChatMembersListWidget::ChatMembersListWidget( Kopete::ChatSession *session, QWidget *parent, const char *name )
+ : KListView( parent, name ), m_session( session )
+{
+ // use our own custom tooltips
+ setShowToolTips( false );
+ m_toolTip = new ToolTip( this );
+
+ // set up display: no header
+ setAllColumnsShowFocus( true );
+ addColumn( QString::null, -1 );
+ header()->setStretchEnabled( true, 0 );
+ header()->hide();
+
+ // list is sorted by us, not by Qt
+ setSorting( -1 );
+
+ // add chat members
+ slotContactAdded( session->myself() );
+ for ( QPtrListIterator<Kopete::Contact> it( session->members() ); it.current(); ++it )
+ slotContactAdded( *it );
+
+ connect( this, SIGNAL( contextMenu( KListView*, QListViewItem *, const QPoint &) ),
+ SLOT( slotContextMenu(KListView*, QListViewItem *, const QPoint & ) ) );
+ connect( this, SIGNAL( executed( QListViewItem* ) ),
+ SLOT( slotExecute( QListViewItem * ) ) );
+
+ connect( session, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
+ this, SLOT( slotContactAdded(const Kopete::Contact*) ) );
+ connect( session, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Kopete::Message::MessageFormat, bool) ),
+ this, SLOT( slotContactRemoved(const Kopete::Contact*) ) );
+ connect( session, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
+ this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & ) ) );
+}
+
+ChatMembersListWidget::~ChatMembersListWidget()
+{
+}
+
+void ChatMembersListWidget::slotContextMenu( KListView*, QListViewItem *item, const QPoint &point )
+{
+ if ( ContactItem *contactItem = dynamic_cast<ContactItem*>(item) )
+ {
+ KPopupMenu *p = contactItem->contact()->popupMenu( session() );
+ connect( p, SIGNAL( aboutToHide() ), p, SLOT( deleteLater() ) );
+ p->popup( point );
+ }
+}
+
+void ChatMembersListWidget::slotContactAdded( const Kopete::Contact *contact )
+{
+ if ( !m_members.contains( contact ) )
+ m_members.insert( contact, new ContactItem( this, const_cast<Kopete::Contact*>( contact ) ) );
+}
+
+void ChatMembersListWidget::slotContactRemoved( const Kopete::Contact *contact )
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ if ( m_members.contains( contact ) && contact != session()->myself() )
+ {
+ delete m_members[ contact ];
+ m_members.remove( contact );
+ }
+}
+
+void ChatMembersListWidget::slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &status )
+{
+ if ( m_members.contains( contact ) )
+ m_members[contact]->setStatus( status );
+}
+
+void ChatMembersListWidget::slotExecute( QListViewItem *item )
+{
+ if ( ContactItem *contactItem = dynamic_cast<ContactItem*>(item ) )
+ {
+ Kopete::Contact *contact=contactItem->contact();
+
+ if(!contact || contact == contact->account()->myself())
+ return;
+
+ contact->execute();
+ }
+}
+
+QDragObject *ChatMembersListWidget::dragObject()
+{
+ QListViewItem *currentLVI = currentItem();
+ if( !currentLVI )
+ return 0L;
+
+ ContactItem *lvi = dynamic_cast<ContactItem*>( currentLVI );
+ if( !lvi )
+ return 0L;
+
+ Kopete::Contact *c = lvi->contact();
+ KMultipleDrag *drag = new KMultipleDrag( this );
+ drag->addDragObject( new QStoredDrag("application/x-qlistviewitem", 0L ) );
+
+ QStoredDrag *d = new QStoredDrag("kopete/x-contact", 0L );
+ d->setEncodedData( QString( c->protocol()->pluginId()+QChar( 0xE000 )+c->account()->accountId()+QChar( 0xE000 )+ c->contactId() ).utf8() );
+ drag->addDragObject( d );
+
+ KABC::Addressee address = KABC::StdAddressBook::self()->findByUid(c->metaContact()->metaContactId());
+
+ if( !address.isEmpty() )
+ {
+ drag->addDragObject( new QTextDrag( address.fullEmail(), 0L ) );
+ KABC::VCardConverter converter;
+ QString vcard = converter.createVCard( address );
+ if( !vcard.isNull() )
+ {
+ QStoredDrag *vcardDrag = new QStoredDrag("text/x-vcard", 0L );
+ vcardDrag->setEncodedData( vcard.utf8() );
+ drag->addDragObject( vcardDrag );
+ }
+ }
+
+ drag->setPixmap( c->onlineStatus().iconFor(c, 12) );
+
+ return drag;
+}
+
+
+//END ChatMembersListWidget
+
+#include "chatmemberslistwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chatmemberslistwidget.h b/kopete/kopete/chatwindow/chatmemberslistwidget.h
new file mode 100644
index 00000000..71084554
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatmemberslistwidget.h
@@ -0,0 +1,121 @@
+/*
+ chatmemberslistwidget.h - Chat Members List Widget
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATMEMBERSLISTWIDGET_H
+#define CHATMEMBERSLISTWIDGET_H
+
+#include <klistview.h>
+
+#include <qmap.h>
+
+namespace Kopete
+{
+class ChatSession;
+class Contact;
+class OnlineStatus;
+}
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class ChatMembersListWidget : public KListView
+{
+ Q_OBJECT
+public:
+ ChatMembersListWidget( Kopete::ChatSession *session, QWidget *parent, const char *name = 0 );
+ virtual ~ChatMembersListWidget();
+
+ Kopete::ChatSession *session() { return m_session; }
+
+ class ToolTip;
+ class ContactItem;
+
+protected:
+
+ /**
+ * Start a drag operation
+ * @return a KMultipleDrag containing:
+ * 1) A QStoredDrag of type "application/x-qlistviewitem",
+ * 2) If the contact is associated with a KABC entry,
+ * i) a QTextDrag containing their email address, and
+ * ii) their vCard representation.
+ */
+ virtual QDragObject *dragObject();
+
+private slots:
+ /**
+ * Show the context menu for @p item at @p point
+ */
+ void slotContextMenu( KListView*, QListViewItem *item, const QPoint &point );
+
+ /**
+ * Called when a contact is added to the chat session.
+ * Adds this contact to the contact list view.
+ * @param c The contact that joined the chat
+ */
+ void slotContactAdded( const Kopete::Contact *c );
+
+ /**
+ * Called when a contact is removed from the chat session.
+ * Removes this contact from the contact list view.
+ * @param c The contact that left the chat
+ */
+ void slotContactRemoved( const Kopete::Contact *c );
+
+ /**
+ * Called when a contact changes status.
+ * @param contact The contact who changed status
+ * @param status The new status of the contact
+ */
+ void slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &status );
+
+ /**
+ * Called when a contact is clicked.
+ * @param item The list view item representing the clicked contact
+ */
+ void slotExecute( QListViewItem *contact );
+
+private:
+ Kopete::ChatSession *m_session;
+ QMap<const Kopete::Contact*, ContactItem*> m_members;
+ ToolTip *m_toolTip;
+};
+
+class ChatMembersListWidget::ContactItem : public QObject, public KListViewItem
+{
+ Q_OBJECT
+public:
+ ContactItem( ChatMembersListWidget *list, Kopete::Contact *contact );
+ Kopete::Contact *contact() const { return m_contact; }
+
+private slots:
+ void slotPropertyChanged( Kopete::Contact *contact, const QString &key, const QVariant &oldValue, const QVariant &newValue );
+
+private:
+ friend class ChatMembersListWidget;
+
+ void reposition();
+ void setStatus( const Kopete::OnlineStatus &status );
+
+ Kopete::Contact *m_contact;
+};
+
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chatmessagepart.cpp b/kopete/kopete/chatwindow/chatmessagepart.cpp
new file mode 100644
index 00000000..36523dac
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatmessagepart.cpp
@@ -0,0 +1,1328 @@
+/*
+ chatmessagepart.cpp - Chat Message KPart
+
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatmessagepart.h"
+
+// STYLE_TIMETEST is for time staticstic gathering.
+//#define STYLE_TIMETEST
+
+#include <ctime>
+
+// Qt includes
+#include <qclipboard.h>
+#include <qtooltip.h>
+#include <qrect.h>
+#include <qcursor.h>
+#include <qptrlist.h>
+#include <qregexp.h>
+#include <qvaluelist.h>
+#include <qtimer.h>
+#include <qstylesheet.h>
+
+// KHTML::DOM includes
+#include <dom/dom_doc.h>
+#include <dom/dom_text.h>
+#include <dom/dom_element.h>
+#include <dom/html_base.h>
+#include <dom/html_document.h>
+#include <dom/html_inline.h>
+
+
+// KDE includes
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kfiledialog.h>
+#include <khtmlview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmultipledrag.h>
+#include <kpopupmenu.h>
+#include <krun.h>
+#include <kstringhandler.h>
+#include <ktempfile.h>
+#include <kurldrag.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+
+// Kopete includes
+#include "chatmemberslistwidget.h"
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatwindow.h"
+#include "kopetechatsession.h"
+#include "kopetemetacontact.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopeteglobal.h"
+#include "kopeteemoticons.h"
+#include "kopeteview.h"
+#include "kopetepicture.h"
+
+#include "kopetechatwindowstyle.h"
+#include "kopetechatwindowstylemanager.h"
+
+#if !(KDE_IS_VERSION(3,3,90))
+//From kdelibs/khtml/misc/htmltags.h
+// used in ChatMessagePart::copy()
+#define ID_BLOCKQUOTE 12
+#define ID_BR 14
+#define ID_DD 22
+#define ID_DIV 26
+#define ID_DL 27
+#define ID_DT 28
+#define ID_H1 36
+#define ID_H2 37
+#define ID_H3 38
+#define ID_H4 39
+#define ID_H5 40
+#define ID_H6 41
+#define ID_HR 43
+#define ID_IMG 48
+#define ID_LI 57
+#define ID_OL 69
+#define ID_P 72
+#define ID_PRE 75
+#define ID_TD 90
+#define ID_TH 93
+#define ID_TR 96
+#define ID_TT 97
+#define ID_UL 99
+#endif
+
+class ToolTip;
+
+class ChatMessagePart::Private
+{
+public:
+ Private()
+ : tt(0L), manager(0L), scrollPressed(false),
+ copyAction(0L), saveAction(0L), printAction(0L),
+ closeAction(0L),copyURLAction(0L), currentChatStyle(0L), latestContact(0L),
+ latestDirection(Kopete::Message::Inbound), latestType(Kopete::Message::TypeNormal)
+ {}
+
+ ~Private()
+ {
+ // Don't delete manager and latestContact, because they could be still used.
+ // Don't delete currentChatStyle, it is handled by ChatWindowStyleManager.
+ }
+
+ bool bgOverride;
+ bool fgOverride;
+ bool rtfOverride;
+
+ ToolTip *tt;
+
+ Kopete::ChatSession *manager;
+ bool scrollPressed;
+
+ DOM::HTMLElement activeElement;
+
+ KAction *copyAction;
+ KAction *saveAction;
+ KAction *printAction;
+ KAction *closeAction;
+ KAction *copyURLAction;
+
+ ChatWindowStyle *currentChatStyle;
+ Kopete::Contact *latestContact;
+ Kopete::Message::MessageDirection latestDirection;
+ Kopete::Message::MessageType latestType;
+ // Yep I know it will take memory, but I don't have choice
+ // to enable on-the-fly style changing.
+ QValueList<Kopete::Message> allMessages;
+};
+
+class ChatMessagePart::ToolTip : public QToolTip
+{
+public:
+ ToolTip( ChatMessagePart *c ) : QToolTip( c->view()->viewport() )
+ {
+ m_chat = c;
+ }
+
+ void maybeTip( const QPoint &/*p*/ )
+ {
+ // FIXME: it's wrong to look for the node under the mouse - this makes too many
+ // assumptions about how tooltips work. but there is no nodeAtPoint.
+ DOM::Node node = m_chat->nodeUnderMouse();
+ Kopete::Contact *contact = m_chat->contactFromNode( node );
+ QString toolTipText;
+
+ if(node.isNull())
+ return;
+
+ // this tooltip is attached to the viewport widget, so translate the node's rect
+ // into its coordinates.
+ QRect rect = node.getRect();
+ rect = QRect( m_chat->view()->contentsToViewport( rect.topLeft() ),
+ m_chat->view()->contentsToViewport( rect.bottomRight() ) );
+
+ if( contact )
+ {
+ toolTipText = contact->toolTip();
+ }
+ else
+ {
+ m_chat->emitTooltipEvent( m_chat->textUnderMouse(), toolTipText );
+
+ if( toolTipText.isEmpty() )
+ {
+ //Fall back to the title attribute
+ for( DOM::HTMLElement element = node; !element.isNull(); element = element.parentNode() )
+ {
+ if( element.hasAttribute( "title" ) )
+ {
+ toolTipText = element.getAttribute( "title" ).string();
+ break;
+ }
+ }
+ }
+ }
+
+ if( !toolTipText.isEmpty() )
+ tip( rect, toolTipText );
+ }
+
+private:
+ ChatMessagePart *m_chat;
+};
+
+ChatMessagePart::ChatMessagePart( Kopete::ChatSession *mgr, QWidget *parent, const char *name)
+ : KHTMLPart( parent, name ), d( new Private )
+{
+ d->manager = mgr;
+
+ KopetePrefs *kopetePrefs = KopetePrefs::prefs();
+ d->currentChatStyle = ChatWindowStyleManager::self()->getStyleFromPool( kopetePrefs->stylePath() );
+
+ //Security settings, we don't need this stuff
+ setJScriptEnabled( false ) ;
+ setJavaEnabled( false );
+ setPluginsEnabled( false );
+ setMetaRefreshEnabled( false );
+ setOnlyLocalReferences( true );
+
+ // Write the template to KHTMLPart
+ writeTemplate();
+
+ view()->setFocusPolicy( QWidget::NoFocus );
+
+ d->tt=new ToolTip( this );
+
+ // It is not possible to drag and drop on our widget
+ view()->setAcceptDrops(false);
+
+ connect( KopetePrefs::prefs(), SIGNAL(messageAppearanceChanged()),
+ this, SLOT( slotAppearanceChanged() ) );
+ connect( KopetePrefs::prefs(), SIGNAL(windowAppearanceChanged()),
+ this, SLOT( slotRefreshView() ) );
+ connect( KopetePrefs::prefs(), SIGNAL(styleChanged(const QString &)),
+ this, SLOT( setStyle(const QString &) ) );
+ connect( KopetePrefs::prefs(), SIGNAL(styleVariantChanged(const QString &)),
+ this, SLOT( setStyleVariant(const QString &) ) );
+ // Refresh the style if the display name change.
+ connect( d->manager, SIGNAL(displayNameChanged()), this, SLOT(slotUpdateHeaderDisplayName()) );
+ connect( d->manager, SIGNAL(photoChanged()), this, SLOT(slotUpdateHeaderPhoto()) );
+
+ connect ( browserExtension(), SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
+ this, SLOT( slotOpenURLRequest( const KURL &, const KParts::URLArgs & ) ) );
+
+ connect( this, SIGNAL(popupMenu(const QString &, const QPoint &)),
+ this, SLOT(slotRightClick(const QString &, const QPoint &)) );
+ connect( view(), SIGNAL(contentsMoving(int,int)),
+ this, SLOT(slotScrollingTo(int,int)) );
+
+ //initActions
+ d->copyAction = KStdAction::copy( this, SLOT(copy()), actionCollection() );
+ d->saveAction = KStdAction::saveAs( this, SLOT(save()), actionCollection() );
+ d->printAction = KStdAction::print( this, SLOT(print()),actionCollection() );
+ d->closeAction = KStdAction::close( this, SLOT(slotCloseView()),actionCollection() );
+ d->copyURLAction = new KAction( i18n( "Copy Link Address" ), QString::fromLatin1( "editcopy" ), 0, this, SLOT( slotCopyURL() ), actionCollection() );
+
+ // read formatting override flags
+ readOverrides();
+}
+
+ChatMessagePart::~ChatMessagePart()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ delete d->tt;
+ delete d;
+}
+
+void ChatMessagePart::slotScrollingTo( int /*x*/, int y )
+{
+ int scrolledTo = y + view()->visibleHeight();
+ if ( scrolledTo >= ( view()->contentsHeight() - 10 ) )
+ d->scrollPressed = false;
+ else
+ d->scrollPressed = true;
+}
+
+void ChatMessagePart::save()
+{
+ KFileDialog dlg( QString::null, QString::fromLatin1( "text/html text/plain" ), view(), "fileSaveDialog", false );
+ dlg.setCaption( i18n( "Save Conversation" ) );
+ dlg.setOperationMode( KFileDialog::Saving );
+
+ if ( dlg.exec() != QDialog::Accepted )
+ return;
+
+ KURL saveURL = dlg.selectedURL();
+ KTempFile tempFile;
+ tempFile.setAutoDelete( true );
+ QFile* file = tempFile.file();
+
+ QTextStream stream ( file );
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+
+ if ( dlg.currentFilter() == QString::fromLatin1( "text/plain" ) )
+ {
+ QValueList<Kopete::Message>::ConstIterator it, itEnd = d->allMessages.constEnd();
+ for(it = d->allMessages.constBegin(); it != itEnd; ++it)
+ {
+ Kopete::Message tempMessage = *it;
+ stream << "[" << KGlobal::locale()->formatDateTime(tempMessage.timestamp()) << "] ";
+ if( tempMessage.from() && tempMessage.from()->metaContact() )
+ {
+ stream << formatName(tempMessage.from()->metaContact()->displayName());
+ }
+ stream << ": " << tempMessage.plainBody() << "\n";
+ }
+ }
+ else
+ {
+ stream << htmlDocument().toHTML() << '\n';
+ }
+
+ tempFile.close();
+
+ if ( !KIO::NetAccess::move( KURL( tempFile.name() ), saveURL ) )
+ {
+ KMessageBox::queuedMessageBox( view(), KMessageBox::Error,
+ i18n("<qt>Could not open <b>%1</b> for writing.</qt>").arg( saveURL.prettyURL() ), // Message
+ i18n("Error While Saving") ); //Caption
+ }
+}
+
+void ChatMessagePart::pageUp()
+{
+ view()->scrollBy( 0, -view()->visibleHeight() );
+}
+
+void ChatMessagePart::pageDown()
+{
+ view()->scrollBy( 0, view()->visibleHeight() );
+}
+
+void ChatMessagePart::slotOpenURLRequest(const KURL &url, const KParts::URLArgs &/*args*/)
+{
+ kdDebug(14000) << k_funcinfo << "url=" << url.url() << endl;
+ if ( url.protocol() == QString::fromLatin1("kopetemessage") )
+ {
+ Kopete::Contact *contact = d->manager->account()->contacts()[ url.host() ];
+ if ( contact )
+ contact->execute();
+ }
+ else
+ {
+ KRun *runner = new KRun( url, 0, false ); // false = non-local files
+ runner->setRunExecutables( false ); //security
+ //KRun autodeletes itself by default when finished.
+ }
+}
+
+void ChatMessagePart::readOverrides()
+{
+ d->bgOverride = KopetePrefs::prefs()->bgOverride();
+ d->fgOverride = KopetePrefs::prefs()->fgOverride();
+ d->rtfOverride = KopetePrefs::prefs()->rtfOverride();
+}
+
+void ChatMessagePart::setStyle( const QString &stylePath )
+{
+ // Create a new ChatWindowStyle
+ d->currentChatStyle = ChatWindowStyleManager::self()->getStyleFromPool(stylePath);
+
+ // Do the actual style switch
+ // Wait for the event loop before switching the style
+ QTimer::singleShot( 0, this, SLOT(changeStyle()) );
+}
+
+void ChatMessagePart::setStyle( ChatWindowStyle *style )
+{
+ // Change the current style
+ d->currentChatStyle = style;
+
+ // Do the actual style switch
+ // Wait for the event loop before switching the style
+ QTimer::singleShot( 0, this, SLOT(changeStyle()) );
+}
+
+void ChatMessagePart::setStyleVariant( const QString &variantPath )
+{
+ DOM::HTMLElement variantNode = document().getElementById( QString::fromUtf8("mainStyle") );
+ if( !variantNode.isNull() )
+ variantNode.setInnerText( QString("@import url(\"%1\");").arg(variantPath) );
+}
+
+void ChatMessagePart::slotAppearanceChanged()
+{
+ readOverrides();
+
+ changeStyle();
+}
+
+void ChatMessagePart::appendMessage( Kopete::Message &message, bool restoring )
+{
+ message.setBgOverride( d->bgOverride );
+ message.setFgOverride( d->fgOverride );
+ message.setRtfOverride( d->rtfOverride );
+
+ // parse emoticons and URL now.
+ // Do not reparse emoticons on restoring, because it cause very intensive CPU usage on long chats.
+ if( !restoring )
+ message.setBody( message.parsedBody() , Kopete::Message::ParsedHTML );
+
+#ifdef STYLE_TIMETEST
+ QTime beforeMessage = QTime::currentTime();
+#endif
+
+ QString formattedMessageHtml;
+ bool isConsecutiveMessage = false;
+ uint bufferLen = (uint)KopetePrefs::prefs()->chatViewBufferSize();
+
+ // Find the "Chat" div element.
+ // If the "Chat" div element is not found, do nothing. It's the central part of Adium format.
+ DOM::HTMLElement chatNode = htmlDocument().getElementById( "Chat" );
+
+ if( chatNode.isNull() )
+ {
+ kdDebug(14000) << k_funcinfo << "WARNING: Chat Node was null !" << endl;
+ return;
+ }
+
+ // Check if it's a consecutive Message
+ // Consecutive messages are only for normal messages, status messages do not have a <div id="insert" />
+ // We check if the from() is the latestContact, because consecutive incoming/outgoing message can come from differents peopole(in groupchat and IRC)
+ // Group only if the user want it.
+ if( KopetePrefs::prefs()->groupConsecutiveMessages() )
+ {
+ isConsecutiveMessage = (message.direction() == d->latestDirection && d->latestContact && d->latestContact == message.from() && message.type() == d->latestType);
+ }
+
+ // Don't test it in the switch to don't break consecutive messages.
+ if(message.type() == Kopete::Message::TypeAction)
+ {
+ // Check if chat style support Action template (Kopete extension)
+ if( d->currentChatStyle->hasActionTemplate() )
+ {
+ switch(message.direction())
+ {
+ case Kopete::Message::Inbound:
+ formattedMessageHtml = d->currentChatStyle->getActionIncomingHtml();
+ break;
+ case Kopete::Message::Outbound:
+ formattedMessageHtml = d->currentChatStyle->getActionOutgoingHtml();
+ break;
+ default:
+ break;
+ }
+ }
+ // Use status template if no Action template.
+ else
+ {
+ formattedMessageHtml = d->currentChatStyle->getStatusHtml();
+ }
+ }
+ else
+ {
+ switch(message.direction())
+ {
+ case Kopete::Message::Inbound:
+ {
+ if(isConsecutiveMessage)
+ {
+ formattedMessageHtml = d->currentChatStyle->getNextIncomingHtml();
+ }
+ else
+ {
+ formattedMessageHtml = d->currentChatStyle->getIncomingHtml();
+ }
+ break;
+ }
+ case Kopete::Message::Outbound:
+ {
+ if(isConsecutiveMessage)
+ {
+ formattedMessageHtml = d->currentChatStyle->getNextOutgoingHtml();
+ }
+ else
+ {
+ formattedMessageHtml = d->currentChatStyle->getOutgoingHtml();
+ }
+ break;
+ }
+ case Kopete::Message::Internal:
+ {
+ formattedMessageHtml = d->currentChatStyle->getStatusHtml();
+ break;
+ }
+ }
+ }
+
+ formattedMessageHtml = formatStyleKeywords( formattedMessageHtml, message );
+
+ // newMessageNode is common to both code path
+ // FIXME: Find a better than to create a dummy span.
+ DOM::HTMLElement newMessageNode = document().createElement( QString::fromUtf8("span") );
+ newMessageNode.setInnerHTML( formattedMessageHtml );
+
+ // Find the insert Node
+ DOM::HTMLElement insertNode = document().getElementById( QString::fromUtf8("insert") );
+
+ if( isConsecutiveMessage && !insertNode.isNull() )
+ {
+ // Replace the insert block, because it's a consecutive message.
+ insertNode.parentNode().replaceChild(newMessageNode, insertNode);
+ }
+ else
+ {
+ // Remove the insert block, because it's a new message.
+ if( !insertNode.isNull() )
+ insertNode.parentNode().removeChild(insertNode);
+ // Append to the chat.
+ chatNode.appendChild(newMessageNode);
+ }
+
+ // Keep the direction to see on next message
+ // if it's a consecutive message
+ // Keep also the from() contact.
+ d->latestDirection = message.direction();
+ d->latestType = message.type();
+ d->latestContact = const_cast<Kopete::Contact*>(message.from());
+
+ // Add the message to the list for futher restoring if needed
+ if(!restoring)
+ d->allMessages.append(message);
+
+ while ( bufferLen>0 && d->allMessages.count() >= bufferLen )
+ {
+ d->allMessages.pop_front();
+
+ // FIXME: Find a way to make work Chat View Buffer efficiently with consecutives messages.
+ // Before it was calling changeStyle() but it's damn too slow.
+ if( !KopetePrefs::prefs()->groupConsecutiveMessages() )
+ {
+ chatNode.removeChild( chatNode.firstChild() );
+ }
+ }
+
+ if ( !d->scrollPressed )
+ QTimer::singleShot( 1, this, SLOT( slotScrollView() ) );
+
+#ifdef STYLE_TIMETEST
+ kdDebug(14000) << "Message time: " << beforeMessage.msecsTo( QTime::currentTime()) << endl;
+#endif
+}
+
+void ChatMessagePart::slotRefreshView()
+{
+ DOM::HTMLElement kopeteNode = document().getElementById( QString::fromUtf8("KopeteStyle") );
+ if( !kopeteNode.isNull() )
+ kopeteNode.setInnerText( styleHTML() );
+
+ DOM::HTMLBodyElement bodyElement = htmlDocument().body();
+ bodyElement.setBgColor( KopetePrefs::prefs()->bgColor().name() );
+}
+
+void ChatMessagePart::keepScrolledDown()
+{
+ if ( !d->scrollPressed )
+ QTimer::singleShot( 1, this, SLOT( slotScrollView() ) );
+}
+
+const QString ChatMessagePart::styleHTML() const
+{
+ KopetePrefs *p = KopetePrefs::prefs();
+
+ int fontSize = 0;
+ QString fontSizeCss;
+ // Use correct font size unit, depending of how the QFont was build.
+ if( p->fontFace().pointSize() != -1 )
+ {
+ fontSize = p->fontFace().pointSize();
+ fontSizeCss = QString::fromUtf8("%1pt;").arg(fontSize);
+ }
+ else if( p->fontFace().pixelSize() != -1 )
+ {
+ fontSize = p->fontFace().pixelSize();
+ fontSizeCss = QString::fromUtf8("%1px;").arg(fontSize);
+ }
+
+ QString style = QString::fromLatin1(
+ "body{background-color:%1;font-family:%2;font-size:%3;color:%4}"
+ "td{font-family:%5;font-size:%6;color:%7}"
+ "a{color:%8}a.visited{color:%9}"
+ "a.KopeteDisplayName{text-decoration:none;color:inherit;}"
+ "a.KopeteDisplayName:hover{text-decoration:underline;color:inherit}"
+ ".KopeteLink{cursor:pointer;}.KopeteLink:hover{text-decoration:underline}"
+ ".KopeteMessageBody > p:first-child{margin:0;padding:0;display:inline;}" /* some html messages are encapsuled into a <p> */ )
+ .arg( p->bgColor().name() )
+ .arg( p->fontFace().family() )
+ .arg( fontSizeCss )
+ .arg( p->textColor().name() )
+ .arg( p->fontFace().family() )
+ .arg( fontSizeCss )
+ .arg( p->textColor().name() )
+ .arg( p->linkColor().name() )
+ .arg( p->linkColor().name() );
+
+ return style;
+}
+
+void ChatMessagePart::clear()
+{
+ // writeTemplate actually reset the HTML chat session from the beginning.
+ writeTemplate();
+
+ // Reset consecutive messages
+ d->latestContact = 0;
+ // Remove all stored messages.
+ d->allMessages.clear();
+}
+
+Kopete::Contact *ChatMessagePart::contactFromNode( const DOM::Node &n ) const
+{
+ DOM::Node node = n;
+
+ if ( node.isNull() )
+ return 0;
+
+ while ( !node.isNull() && ( node.nodeType() == DOM::Node::TEXT_NODE || ((DOM::HTMLElement)node).className() != "KopeteDisplayName" ) )
+ node = node.parentNode();
+
+ DOM::HTMLElement element = node;
+ if ( element.className() != "KopeteDisplayName" )
+ return 0;
+
+ if ( element.hasAttribute( "contactid" ) )
+ {
+ QString contactId = element.getAttribute( "contactid" ).string();
+ for ( QPtrListIterator<Kopete::Contact> it ( d->manager->members() ); it.current(); ++it )
+ if ( (*it)->contactId() == contactId )
+ return *it;
+ }
+ else
+ {
+ QString nick = element.innerText().string().stripWhiteSpace();
+ for ( QPtrListIterator<Kopete::Contact> it ( d->manager->members() ); it.current(); ++it )
+ if ( (*it)->property( Kopete::Global::Properties::self()->nickName().key() ).value().toString() == nick )
+ return *it;
+ }
+
+ return 0;
+}
+
+void ChatMessagePart::slotRightClick( const QString &, const QPoint &point )
+{
+ // look through parents until we find an Element
+ DOM::Node activeNode = nodeUnderMouse();
+ while ( !activeNode.isNull() && activeNode.nodeType() != DOM::Node::ELEMENT_NODE )
+ activeNode = activeNode.parentNode();
+
+ // make sure it's valid
+ d->activeElement = activeNode;
+ if ( d->activeElement.isNull() )
+ return;
+
+ KPopupMenu *chatWindowPopup = 0L;
+
+ if ( Kopete::Contact *contact = contactFromNode( d->activeElement ) )
+ {
+ chatWindowPopup = contact->popupMenu( d->manager );
+ connect( chatWindowPopup, SIGNAL( aboutToHide() ), chatWindowPopup , SLOT( deleteLater() ) );
+ }
+ else
+ {
+ chatWindowPopup = new KPopupMenu();
+
+ if ( d->activeElement.className() == "KopeteDisplayName" )
+ {
+ chatWindowPopup->insertItem( i18n( "User Has Left" ), 1 );
+ chatWindowPopup->setItemEnabled( 1, false );
+ chatWindowPopup->insertSeparator();
+ }
+ else if ( d->activeElement.tagName().lower() == QString::fromLatin1( "a" ) )
+ {
+ d->copyURLAction->plug( chatWindowPopup );
+ chatWindowPopup->insertSeparator();
+ }
+
+ d->copyAction->setEnabled( hasSelection() );
+ d->copyAction->plug( chatWindowPopup );
+ d->saveAction->plug( chatWindowPopup );
+ d->printAction->plug( chatWindowPopup );
+ chatWindowPopup->insertSeparator();
+ d->closeAction->plug( chatWindowPopup );
+
+ connect( chatWindowPopup, SIGNAL( aboutToHide() ), chatWindowPopup, SLOT( deleteLater() ) );
+ chatWindowPopup->popup( point );
+ }
+
+ //Emit for plugin hooks
+ emit contextMenuEvent( textUnderMouse(), chatWindowPopup );
+
+ chatWindowPopup->popup( point );
+}
+
+QString ChatMessagePart::textUnderMouse()
+{
+ DOM::Node activeNode = nodeUnderMouse();
+ if( activeNode.nodeType() != DOM::Node::TEXT_NODE )
+ return QString::null;
+
+ DOM::Text textNode = activeNode;
+ QString data = textNode.data().string();
+
+ //Ok, we have the whole node. Now, find the text under the mouse.
+ int mouseLeft = view()->mapFromGlobal( QCursor::pos() ).x(),
+ nodeLeft = activeNode.getRect().x(),
+ cPos = 0,
+ dataLen = data.length();
+
+ QFontMetrics metrics( KopetePrefs::prefs()->fontFace() );
+ QString buffer;
+ while( cPos < dataLen && nodeLeft < mouseLeft )
+ {
+ QChar c = data[cPos++];
+ if( c.isSpace() )
+ buffer.truncate(0);
+ else
+ buffer += c;
+
+ nodeLeft += metrics.width(c);
+ }
+
+ if( cPos < dataLen )
+ {
+ QChar c = data[cPos++];
+ while( cPos < dataLen && !c.isSpace() )
+ {
+ buffer += c;
+ c = data[cPos++];
+ }
+ }
+
+ return buffer;
+}
+
+void ChatMessagePart::slotCopyURL()
+{
+ DOM::HTMLAnchorElement a = d->activeElement;
+ if ( !a.isNull() )
+ {
+ QApplication::clipboard()->setText( a.href().string(), QClipboard::Clipboard );
+ QApplication::clipboard()->setText( a.href().string(), QClipboard::Selection );
+ }
+}
+
+void ChatMessagePart::slotScrollView()
+{
+ // NB: view()->contentsHeight() is incorrect before the view has been shown in its window.
+ // Until this happens, the geometry has not been correctly calculated, so this scrollBy call
+ // will usually scroll to the top of the view.
+ view()->scrollBy( 0, view()->contentsHeight() );
+}
+
+void ChatMessagePart::copy(bool justselection /* default false */)
+{
+ /*
+ * The objective of this function is to keep the text of emoticons (or of latex image) when copying.
+ * see Bug 61676
+ * This also copies the text as type text/html
+ * RangeImpl::toHTML was not implemented before KDE 3.4
+ */
+ QString text;
+ QString htmltext;
+
+#if KDE_IS_VERSION(3,3,90)
+ htmltext = selectedTextAsHTML();
+ text = selectedText();
+ //selectedText is now sufficent
+// text=Kopete::Message::unescape( htmltext ).stripWhiteSpace();
+ // Message::unsescape will replace image by his title attribute
+ // stripWhiteSpace is for removing the newline added by the <!DOCTYPE> and other xml things of RangeImpl::toHTML
+#else
+
+ DOM::Node startNode, endNode;
+ long startOffset, endOffset;
+ selection( startNode, startOffset, endNode, endOffset );
+
+ //BEGIN: copied from KHTMLPart::selectedText
+
+ bool hasNewLine = true;
+ DOM::Node n = startNode;
+ while(!n.isNull())
+ {
+ if(n.nodeType() == DOM::Node::TEXT_NODE /*&& n.handle()->renderer()*/)
+ {
+ QString str = n.nodeValue().string();
+ hasNewLine = false;
+ if(n == startNode && n == endNode)
+ text = str.mid(startOffset, endOffset - startOffset);
+ else if(n == startNode)
+ text = str.mid(startOffset);
+ else if(n == endNode)
+ text += str.left(endOffset);
+ else
+ text += str;
+ }
+ else
+ { // This is our simple HTML -> ASCII transformation:
+ unsigned short id = n.elementId();
+ switch(id)
+ {
+ case ID_IMG: //here is the main difference with KHTMLView::selectedText
+ {
+ DOM::HTMLElement e = n;
+ if( !e.isNull() && e.hasAttribute( "title" ) )
+ text+=e.getAttribute( "title" ).string();
+ break;
+ }
+ case ID_BR:
+ text += "\n";
+ hasNewLine = true;
+ break;
+ case ID_TD: case ID_TH: case ID_HR:
+ case ID_OL: case ID_UL: case ID_LI:
+ case ID_DD: case ID_DL: case ID_DT:
+ case ID_PRE: case ID_BLOCKQUOTE: case ID_DIV:
+ if (!hasNewLine)
+ text += "\n";
+ hasNewLine = true;
+ break;
+ case ID_P: case ID_TR:
+ case ID_H1: case ID_H2: case ID_H3:
+ case ID_H4: case ID_H5: case ID_H6:
+ if (!hasNewLine)
+ text += "\n";
+ text += "\n";
+ hasNewLine = true;
+ break;
+ }
+ }
+ if(n == endNode)
+ break;
+ DOM::Node next = n.firstChild();
+ if(next.isNull())
+ next = n.nextSibling();
+ while( next.isNull() && !n.parentNode().isNull() )
+ {
+ n = n.parentNode();
+ next = n.nextSibling();
+ unsigned short id = n.elementId();
+ switch(id)
+ {
+ case ID_TD: case ID_TH: case ID_HR:
+ case ID_OL: case ID_UL: case ID_LI:
+ case ID_DD: case ID_DL: case ID_DT:
+ case ID_PRE: case ID_BLOCKQUOTE: case ID_DIV:
+ if (!hasNewLine)
+ text += "\n";
+ hasNewLine = true;
+ break;
+ case ID_P: case ID_TR:
+ case ID_H1: case ID_H2: case ID_H3:
+ case ID_H4: case ID_H5: case ID_H6:
+ if (!hasNewLine)
+ text += "\n";
+ text += "\n";
+ hasNewLine = true;
+ break;
+ }
+ }
+ n = next;
+ }
+
+ if(text.isEmpty())
+ return;
+
+ int start = 0;
+ int end = text.length();
+
+ // Strip leading LFs
+ while ((start < end) && (text[start] == '\n'))
+ start++;
+
+ // Strip excessive trailing LFs
+ while ((start < (end-1)) && (text[end-1] == '\n') && (text[end-2] == '\n'))
+ end--;
+
+ text=text.mid(start, end-start);
+
+ //END: copied from KHTMLPart::selectedText
+#endif
+
+ if(text.isEmpty()) return;
+
+ disconnect( kapp->clipboard(), SIGNAL( selectionChanged()), this, SLOT( slotClearSelection()));
+
+#ifndef QT_NO_MIMECLIPBOARD
+ if(!justselection)
+ {
+ QTextDrag *textdrag = new QTextDrag(text, 0L);
+ KMultipleDrag *drag = new KMultipleDrag( );
+ drag->addDragObject( textdrag );
+ if(!htmltext.isEmpty()) {
+ htmltext.replace( QChar( 0xa0 ), ' ' );
+ QTextDrag *htmltextdrag = new QTextDrag(htmltext, 0L);
+ htmltextdrag->setSubtype("html");
+ drag->addDragObject( htmltextdrag );
+ }
+ QApplication::clipboard()->setData( drag, QClipboard::Clipboard );
+ }
+ QApplication::clipboard()->setText( text, QClipboard::Selection );
+#else
+ if(!justselection)
+ QApplication::clipboard()->setText( text, QClipboard::Clipboard );
+ QApplication::clipboard()->setText( text, QClipboard::Selection );
+#endif
+ connect( kapp->clipboard(), SIGNAL( selectionChanged()), SLOT( slotClearSelection()));
+
+}
+
+void ChatMessagePart::print()
+{
+ view()->print();
+}
+
+void ChatMessagePart::khtmlDrawContentsEvent( khtml::DrawContentsEvent * event) //virtual
+{
+ KHTMLPart::khtmlDrawContentsEvent(event);
+ //copy(true /*selection only*/); not needed anymore.
+}
+void ChatMessagePart::slotCloseView( bool force )
+{
+ d->manager->view()->closeView( force );
+}
+
+void ChatMessagePart::emitTooltipEvent( const QString &textUnderMouse, QString &toolTip )
+{
+ emit tooltipEvent( textUnderMouse, toolTip );
+}
+
+// Style formatting for messages(incoming, outgoing, status)
+QString ChatMessagePart::formatStyleKeywords( const QString &sourceHTML, const Kopete::Message &_message )
+{
+ Kopete::Message message=_message; //we will eventually need to modify it before showing it.
+ QString resultHTML = sourceHTML;
+ QString nick, contactId, service, protocolIcon, nickLink;
+
+ if( message.from() )
+ {
+ // Use metacontact display name if the metacontact exists and if its not the myself metacontact.
+ if( message.from()->metaContact() && message.from()->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ nick = message.from()->metaContact()->displayName();
+ }
+ // Use contact nickname for no metacontact or myself.
+ else
+ {
+ nick = message.from()->nickName();
+ }
+ nick = formatName(nick);
+ contactId = message.from()->contactId();
+ // protocol() returns NULL here in the style preview in appearance config.
+ // this isn't the right place to work around it, since contacts should never have
+ // no protocol, but it works for now.
+ //
+ // Use default if protocol() and protocol()->displayName() is NULL.
+ // For preview and unit tests.
+ QString iconName = QString::fromUtf8("kopete");
+ service = QString::fromUtf8("Kopete");
+ if(message.from()->protocol() && !message.from()->protocol()->displayName().isNull())
+ {
+ service = message.from()->protocol()->displayName();
+ iconName = message.from()->protocol()->pluginIcon();
+ }
+
+ protocolIcon = KGlobal::iconLoader()->iconPath( iconName, KIcon::Small );
+
+ nickLink=QString::fromLatin1("<a href=\"kopetemessage://%1/?protocolId=%2&amp;accountId=%3\" class=\"KopeteDisplayName\">")
+ .arg( QStyleSheet::escape(message.from()->contactId()).replace('"',"&quot;"),
+ QStyleSheet::escape(message.from()->protocol()->pluginId()).replace('"',"&quot;"),
+ QStyleSheet::escape(message.from()->account()->accountId() ).replace('"',"&quot;"));
+ }
+ else
+ {
+ nickLink="<a>";
+ }
+
+
+ // Replace sender (contact nick)
+ resultHTML = resultHTML.replace( QString::fromUtf8("%sender%"), nickLink+nick+"</a>" );
+ // Replace time, by default display only time and display seconds(that was true means).
+ resultHTML = resultHTML.replace( QString::fromUtf8("%time%"), KGlobal::locale()->formatTime(message.timestamp().time(), true) );
+ // Replace %screenName% (contact ID)
+ resultHTML = resultHTML.replace( QString::fromUtf8("%senderScreenName%"), nickLink+QStyleSheet::escape(contactId)+"</a>" );
+ // Replace service name (protocol name)
+ resultHTML = resultHTML.replace( QString::fromUtf8("%service%"), QStyleSheet::escape(service) );
+ // Replace protocolIcon (sender statusIcon)
+ resultHTML = resultHTML.replace( QString::fromUtf8("%senderStatusIcon%"), QStyleSheet::escape(protocolIcon).replace('"',"&quot;") );
+
+ // Look for %time{X}%
+ QRegExp timeRegExp("%time\\{([^}]*)\\}%");
+ int pos=0;
+ while( (pos=timeRegExp.search(resultHTML , pos) ) != -1 )
+ {
+ QString timeKeyword = formatTime( timeRegExp.cap(1), message.timestamp() );
+ resultHTML = resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword );
+ }
+
+ // Look for %textbackgroundcolor{X}%
+ // TODO: use the X value.
+ // Replace with user-selected highlight color if to be highlighted or
+ // with "inherit" otherwise to keep CSS clean
+ QString bgColor = QString::fromUtf8("inherit");
+ if( message.importance() == Kopete::Message::Highlight && KopetePrefs::prefs()->highlightEnabled() )
+ {
+ bgColor = KopetePrefs::prefs()->highlightBackground().name();
+ }
+
+ QRegExp textBackgroundRegExp("%textbackgroundcolor\\{([^}]*)\\}%");
+ int textPos=0;
+ while( (textPos=textBackgroundRegExp.search(resultHTML, textPos) ) != -1 )
+ {
+ resultHTML = resultHTML.replace( textPos , textBackgroundRegExp.cap(0).length() , bgColor );
+ }
+
+ // Replace userIconPath
+ if( message.from() )
+ {
+ QString photoPath;
+#if 0
+ photoPath = message.from()->property(Kopete::Global::Properties::self()->photo().key()).value().toString();
+ // If the photo path is empty, set the default buddy icon for the theme
+ if( photoPath.isEmpty() )
+ {
+ if(message.direction() == Kopete::Message::Inbound)
+ photoPath = QString::fromUtf8("Incoming/buddy_icon.png");
+ else if(message.direction() == Kopete::Message::Outbound)
+ photoPath = QString::fromUtf8("Outgoing/buddy_icon.png");
+ }
+#endif
+ if( !message.from()->metaContact()->picture().isNull() )
+ {
+ photoPath = QString( "data:image/png;base64," ) + message.from()->metaContact()->picture().base64();
+ }
+ else
+ {
+ if(message.direction() == Kopete::Message::Inbound)
+ photoPath = QString::fromUtf8("Incoming/buddy_icon.png");
+ else if(message.direction() == Kopete::Message::Outbound)
+ photoPath = QString::fromUtf8("Outgoing/buddy_icon.png");
+ }
+ resultHTML = resultHTML.replace(QString::fromUtf8("%userIconPath%"), photoPath);
+ }
+
+ // Replace messages.
+ // Build the action message if the currentChatStyle do not have Action template.
+ if( message.type() == Kopete::Message::TypeAction && !d->currentChatStyle->hasActionTemplate() )
+ {
+ kdDebug(14000) << k_funcinfo << "Map Action message to Status template. " << endl;
+
+ QString boldNick = QString::fromUtf8("%1<b>%2</b></a> ").arg(nickLink,nick);
+ QString newBody = boldNick + message.parsedBody();
+ message.setBody(newBody, Kopete::Message::ParsedHTML );
+ }
+
+ // Set message direction("rtl"(Right-To-Left) or "ltr"(Left-to-right))
+ resultHTML = resultHTML.replace( QString::fromUtf8("%messageDirection%"), message.isRightToLeft() ? "rtl" : "ltr" );
+
+ // These colors are used for coloring nicknames. I tried to use
+ // colors both visible on light and dark background.
+ static const char* const nameColors[] =
+ {
+ "red", "blue" , "gray", "magenta", "violet", /*"olive"*/ "#808000", "yellowgreen",
+ "darkred", "darkgreen", "darksalmon", "darkcyan", /*"darkyellow"*/ "#B07D2B",
+ "mediumpurple", "peru", "olivedrab", /*"royalred"*/ "#B01712", "darkorange", "slateblue",
+ "slategray", "goldenrod", "orangered", "tomato", /*"dogderblue"*/ "#1E90FF", "steelblue",
+ "deeppink", "saddlebrown", "coral", "royalblue"
+ };
+
+ static const int nameColorsLen = sizeof(nameColors) / sizeof(nameColors[0]) - 1;
+ // hash contactId to deterministically pick a color for the contact
+ int hash = 0;
+ for( uint f = 0; f < contactId.length(); ++f )
+ hash += contactId[f].unicode() * f;
+ const QString colorName = nameColors[ hash % nameColorsLen ];
+ QString lightColorName; // Do not initialize, QColor::name() is expensive!
+ kdDebug(14000) << k_funcinfo << "Hash " << hash << " has color " << colorName << endl;
+ QRegExp senderColorRegExp("%senderColor(?:\\{([^}]*)\\})?%");
+ textPos=0;
+ while( (textPos=senderColorRegExp.search(resultHTML, textPos) ) != -1 )
+ {
+ int light=100;
+ bool doLight=false;
+ if(senderColorRegExp.numCaptures()>=1)
+ {
+ light=senderColorRegExp.cap(1).toUInt(&doLight);
+ }
+
+ // Lazily init light color
+ if ( doLight && lightColorName.isNull() )
+ lightColorName = QColor( colorName ).light( light ).name();
+
+ resultHTML = resultHTML.replace( textPos , senderColorRegExp.cap(0).length(),
+ doLight ? lightColorName : colorName );
+ }
+
+ // Replace message at the end, maybe someone could put a Adium keyword in his message :P
+ resultHTML = resultHTML.replace( QString::fromUtf8("%message%"), formatMessageBody(message) );
+
+ // TODO: %status
+// resultHTML = addNickLinks( resultHTML );
+ return resultHTML;
+}
+
+// Style formatting for header and footer.
+QString ChatMessagePart::formatStyleKeywords( const QString &sourceHTML )
+{
+ QString resultHTML = sourceHTML;
+
+ Kopete::Contact *remoteContact = d->manager->members().getFirst();
+
+ // Verify that all contacts are not null before doing anything
+ if( remoteContact && d->manager->myself() )
+ {
+ QString sourceName, destinationName;
+ // Use contact nickname for ourselfs, Myself metacontact display name isn't a reliable source.
+ sourceName = d->manager->myself()->nickName();
+ if( remoteContact->metaContact() )
+ destinationName = remoteContact->metaContact()->displayName();
+ else
+ destinationName = remoteContact->nickName();
+
+ // Replace %chatName%, create a internal span to update it by DOM when asked.
+ resultHTML = resultHTML.replace( QString::fromUtf8("%chatName%"), QString("<span id=\"KopeteHeaderChatNameInternal\">%1</span>").arg( formatName(d->manager->displayName()) ) );
+ // Replace %sourceName%
+ resultHTML = resultHTML.replace( QString::fromUtf8("%sourceName%"), formatName(sourceName) );
+ // Replace %destinationName%
+ resultHTML = resultHTML.replace( QString::fromUtf8("%destinationName%"), formatName(destinationName) );
+ // For %timeOpened%, display the date and time (also the seconds).
+ resultHTML = resultHTML.replace( QString::fromUtf8("%timeOpened%"), KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), true, true ) );
+
+ // Look for %timeOpened{X}%
+ QRegExp timeRegExp("%timeOpened\\{([^}]*)\\}%");
+ int pos=0;
+ while( (pos=timeRegExp.search(resultHTML, pos) ) != -1 )
+ {
+ QString timeKeyword = formatTime( timeRegExp.cap(1), QDateTime::currentDateTime() );
+ resultHTML = resultHTML.replace( pos , timeRegExp.cap(0).length() , timeKeyword );
+ }
+ // Get contact image paths
+#if 0
+ QString photoIncomingPath, photoOutgoingPath;
+ photoIncomingPath = remoteContact->property( Kopete::Global::Properties::self()->photo().key()).value().toString();
+ photoOutgoingPath = d->manager->myself()->property(Kopete::Global::Properties::self()->photo().key()).value().toString();
+
+ if( photoIncomingPath.isEmpty() )
+ photoIncomingPath = QString::fromUtf8("Incoming/buddy_icon.png");
+ if( photoOutgoingPath.isEmpty() )
+ photoOutgoingPath = QString::fromUtf8("Outgoing/buddy_icon.png");
+
+ resultHTML = resultHTML.replace( QString::fromUtf8("%incomingIconPath%"), photoIncomingPath);
+ resultHTML = resultHTML.replace( QString::fromUtf8("%outgoingIconPath%"), photoOutgoingPath);
+#endif
+ QString photoIncoming, photoOutgoing;
+ if( remoteContact->metaContact() && !remoteContact->metaContact()->picture().isNull() )
+ {
+ photoIncoming = QString("data:image/png;base64,%1").arg( remoteContact->metaContact()->picture().base64() );
+ }
+ else
+ {
+ photoIncoming = QString::fromUtf8("Incoming/buddy_icon.png");
+ }
+
+ if( d->manager->myself()->metaContact() && !d->manager->myself()->metaContact()->picture().isNull() )
+ {
+ photoOutgoing = QString("data:image/png;base64,%1").arg( d->manager->myself()->metaContact()->picture().base64() );
+ }
+ else
+ {
+ photoOutgoing = QString::fromUtf8("Outgoing/buddy_icon.png");
+ }
+
+
+ resultHTML = resultHTML.replace( QString::fromUtf8("%incomingIconPath%"), photoIncoming);
+ resultHTML = resultHTML.replace( QString::fromUtf8("%outgoingIconPath%"), photoOutgoing );
+ }
+
+ return resultHTML;
+}
+
+QString ChatMessagePart::formatTime(const QString &timeFormat, const QDateTime &dateTime)
+{
+ char buffer[256];
+
+ time_t timeT;
+ struct tm *loctime;
+ // Get current time
+ timeT = dateTime.toTime_t();
+ // Convert it to local time representation.
+ loctime = localtime (&timeT);
+ strftime (buffer, 256, timeFormat.ascii(), loctime);
+
+ return QString(buffer);
+}
+
+QString ChatMessagePart::formatName(const QString &sourceName)
+{
+ QString formattedName = sourceName;
+ // Escape the name.
+ formattedName = Kopete::Message::escape(formattedName);
+
+ // Squeeze the nickname if the user want it
+ if( KopetePrefs::prefs()->truncateContactNames() )
+ {
+ formattedName = KStringHandler::csqueeze( sourceName, KopetePrefs::prefs()->maxConactNameLength() );
+ }
+
+ return formattedName;
+}
+
+QString ChatMessagePart::formatMessageBody(const Kopete::Message &message)
+{
+ QString formattedBody("<span ");
+
+ formattedBody += message.getHtmlStyleAttribute();
+
+ // Affect the parsed body.
+ formattedBody += QString::fromUtf8("class=\"KopeteMessageBody\">%1</span>").arg(message.parsedBody());
+
+ return formattedBody;
+}
+
+void ChatMessagePart::slotUpdateHeaderDisplayName()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ DOM::HTMLElement kopeteChatNameNode = document().getElementById( QString::fromUtf8("KopeteHeaderChatNameInternal") );
+ if( !kopeteChatNameNode.isNull() )
+ kopeteChatNameNode.setInnerText( formatName(d->manager->displayName()) );
+}
+
+void ChatMessagePart::slotUpdateHeaderPhoto()
+{
+ // Do the actual style switch
+ // Wait for the event loop before switching the style
+ QTimer::singleShot( 0, this, SLOT(changeStyle()) );
+}
+
+void ChatMessagePart::changeStyle()
+{
+#ifdef STYLE_TIMETEST
+ QTime beforeChange = QTime::currentTime();
+#endif
+ // Make latestContact null to reset consecutives messages.
+ d->latestContact = 0;
+
+ // Rewrite the header and footer.
+ writeTemplate();
+
+ // Readd all current messages.
+ QValueList<Kopete::Message>::ConstIterator it, itEnd = d->allMessages.constEnd();
+ for(it = d->allMessages.constBegin(); it != itEnd; ++it)
+ {
+ Kopete::Message tempMessage = *it;
+ appendMessage(tempMessage, true); // true means that we are restoring.
+ }
+ kdDebug(14000) << k_funcinfo << "Finish changing style." << endl;
+#ifdef STYLE_TIMETEST
+ kdDebug(14000) << "Change time: " << beforeChange.msecsTo( QTime::currentTime()) << endl;
+#endif
+}
+
+void ChatMessagePart::writeTemplate()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+
+#ifdef STYLE_TIMETEST
+ QTime beforeHeader = QTime::currentTime();
+#endif
+ // Clear all the page, and begin a new page.
+ begin();
+
+ // NOTE: About styles
+ // Order of style tag in the template is important.
+ // mainStyle take over all other style definition (which is what we want).
+ //
+ // KopeteStyle: Kopete appearance configuration into a style. It loaded first because
+ // we don't want Kopete settings to override CSS Chat Window Style.
+ // baseStyle: Import the main.css from the Chat Window Style
+ // mainStyle: Currrent variant CSS url.
+
+ // FIXME: Maybe this string should be load from a file, then parsed for args.
+ QString xhtmlBase;
+ xhtmlBase += QString("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n"
+ "\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ "<head>\n"
+ "<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\n\" />\n"
+ "<base href=\"%1\">\n"
+ "<style id=\"KopeteStyle\" type=\"text/css\" media=\"screen,print\">\n"
+ " %5\n"
+ "</style>\n"
+ "<style id=\"baseStyle\" type=\"text/css\" media=\"screen,print\">\n"
+ " @import url(\"main.css\");\n"
+ " *{ word-wrap:break-word; }\n"
+ "</style>\n"
+ "<style id=\"mainStyle\" type=\"text/css\" media=\"screen,print\">\n"
+ " @import url(\"%4\");\n"
+ "</style>\n"
+ "</head>\n"
+ "<body>\n"
+ "%2\n"
+ "<div id=\"Chat\">\n</div>\n"
+ "%3\n"
+ "</body>"
+ "</html>"
+ ).arg( d->currentChatStyle->getStyleBaseHref() )
+ .arg( formatStyleKeywords(d->currentChatStyle->getHeaderHtml()) )
+ .arg( formatStyleKeywords(d->currentChatStyle->getFooterHtml()) )
+ .arg( KopetePrefs::prefs()->styleVariant() )
+ .arg( styleHTML() );
+ write(xhtmlBase);
+ end();
+#ifdef STYLE_TIMETEST
+ kdDebug(14000) << "Header time: " << beforeHeader.msecsTo( QTime::currentTime()) << endl;
+#endif
+}
+
+#include "chatmessagepart.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chatmessagepart.h b/kopete/kopete/chatwindow/chatmessagepart.h
new file mode 100644
index 00000000..ba92b95f
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatmessagepart.h
@@ -0,0 +1,248 @@
+/*
+ chatmessagepart.h - Chat Message KPart
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATMESSAGEPART_H
+#define CHATMESSAGEPART_H
+
+#include <khtml_part.h>
+#include <dom/html_element.h>
+
+#include <qvaluelist.h>
+
+namespace Kopete
+{
+ class Message;
+ class ChatSession;
+ class Contact;
+}
+class KPopupMenu;
+class ChatWindowStyle;
+
+/**
+ * @author Richard Smith
+ */
+class ChatMessagePart : public KHTMLPart
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new ChatMessage Part.
+ */
+ ChatMessagePart( Kopete::ChatSession *manager, QWidget *parent, const char *name = 0);
+ ~ChatMessagePart();
+
+ /**
+ * Clear the message window
+ */
+ void clear();
+
+ /**
+ * Immediately scroll the chat to the bottom, as long as it has not been intentionally scrolled away from the bottom
+ * use
+ */
+ void keepScrolledDown();
+
+public slots:
+ /**
+ * Initiates a copy action
+ * If there is text selected in the HTML view, that text is copied
+ * Otherwise if @p justselection is false, the entire edit area is copied.
+ *
+ * @param justselection If this is true, then the text will be only copied to the selection buffer only.
+ * In this mode, if nothing is selected, then nothing is copied.
+ */
+ void copy(bool justselection = false);
+
+ /**
+ * Print out the contents of the chatwindow
+ */
+ void print();
+
+ /**
+ * Save the contents of the chat to a file
+ */
+ void save();
+
+ /**
+ * Scroll the view up a page
+ */
+ void pageUp();
+
+ /**
+ * Scroll the view down a page
+ */
+ void pageDown();
+
+ /**
+ * Appends a message to the messave view
+ * @param message The message to be appended
+ * @param restoring This flag is used to not re-append message when changing style. By default false.
+ */
+ void appendMessage( Kopete::Message &message, bool restoring = false);
+
+ /**
+ * Change the current style.
+ * This method override is used when preferences change.
+ * This method create a new ChatWindowStyle object.
+ *
+ * Need to rebuild all the XHTML content.
+ *
+ * @param stylePath absolute path to the style.
+ */
+ void setStyle( const QString &stylePath );
+
+ /**
+ * Change the current style
+ * This method override is used on preview and unit tests.
+ * Use a already existing ChatWindowStyle object.
+ *
+ * Need to rebuild all the XHTML content.
+ *
+ * @param chatWindowStyle ChatWindowStyle object.
+ */
+ void setStyle( ChatWindowStyle *style );
+
+ /**
+ * Change the current variant for the current style
+ * @param variantPath relative path to the style variant.
+ */
+ void setStyleVariant( const QString &variantPath );
+
+signals:
+ /**
+ * Emits before the context menu is about to show
+ */
+ void contextMenuEvent( const QString &textUnderMouse, KPopupMenu *popupMenu );
+
+ /**
+ * Emits before the tooltip is about to show
+ */
+ void tooltipEvent( const QString &textUnderMouse, QString &toolTip );
+
+private slots:
+ void slotOpenURLRequest( const KURL &url, const KParts::URLArgs &args );
+ void slotScrollView();
+ void slotAppearanceChanged();
+
+ void slotScrollingTo( int x, int y );
+
+ void slotRefreshView();
+
+ void slotRightClick( const QString &, const QPoint &point );
+
+ void slotCopyURL();
+
+ void slotCloseView( bool force = false );
+
+ /**
+ * Do the actual style change.
+ */
+ void changeStyle();
+
+ /**
+ * Update the display in the header template if any.
+ */
+ void slotUpdateHeaderDisplayName();
+ /**
+ * Upda the photo in the header.
+ */
+ void slotUpdateHeaderPhoto();
+
+protected:
+ virtual void khtmlDrawContentsEvent( khtml::DrawContentsEvent * );
+
+private:
+ void readOverrides();
+
+ const QString styleHTML() const;
+
+ Kopete::Contact *contactFromNode( const DOM::Node &n ) const;
+
+ /**
+ * Emits before the tooltip is about to show
+ */
+ void emitTooltipEvent( const QString &textUnderMouse, QString &toolTipString );
+
+ /**
+ * Returns the text currently under the mouse
+ */
+ QString textUnderMouse();
+
+ /**
+ * Format(replace) style keywords for messages (incoming, outgoing, internal)
+ * Use formatStyleKeywords(const QString &sourceHTML) for header and footer.
+ *
+ * @param sourceHTML the source html which contains the keywords
+ * @param message the current Message.
+ *
+ * @return the resulting HTML with replaced keywords.
+ */
+ QString formatStyleKeywords( const QString &sourceHTML, const Kopete::Message &message );
+ /**
+ * Format(replace) style keywords for header and footers.
+ * For messages, use formatStyleKeywords(const QString &sourceHTML, Kopete::Message &message) instead.
+ *
+ * @param sourceHTML HTML source needed to be replaced.
+ *
+ * @return the resulting HTML with replaced keywords.
+ */
+ QString formatStyleKeywords( const QString &sourceHTML );
+
+ /**
+ * Helper function to parse time in correct format.
+ * Use glibc strftime function.
+ *
+ * @param timeFormat the time format to parse.
+ * @param dateTime the QDateTime which contains the datetime to format.
+ * @return the formatted time string.
+ */
+ QString formatTime(const QString &timeFormat, const QDateTime &dateTime);
+
+ /**
+ * Format a nickname/displayname according to preferences.
+ *
+ * @param sourceName Source name to format.
+ * @return the formatted name.
+ */
+ QString formatName( const QString &sourceName );
+
+ /**
+ * Format a message body according to the style included
+ * in the message.
+ *
+ * @param message Kopete::Message to format.
+ * @return a span tag with a style attribute.
+ */
+ QString formatMessageBody( const Kopete::Message &message );
+
+ /**
+ * Write the template file to KHTMLPart
+ */
+ void writeTemplate();
+
+ class ToolTip;
+ friend class ToolTip;
+
+ class Private;
+ Private *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chattexteditpart.cpp b/kopete/kopete/chatwindow/chattexteditpart.cpp
new file mode 100644
index 00000000..bac31bcf
--- /dev/null
+++ b/kopete/kopete/chatwindow/chattexteditpart.cpp
@@ -0,0 +1,423 @@
+/*
+ chattexteditpart.cpp - Chat Text Edit Part
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chattexteditpart.h"
+
+#include "kopetechatsession.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteprotocol.h"
+#include "kopeteglobal.h"
+#include "kopeteprefs.h"
+
+#include <kcompletion.h>
+#include <kdebug.h>
+#include <ktextedit.h>
+#include <ksyntaxhighlighter.h>
+
+#include <qtimer.h>
+#include <qregexp.h>
+
+ChatTextEditPart::ChatTextEditPart( Kopete::ChatSession *session, QWidget *parent, const char *name )
+ : KopeteRichTextEditPart( parent, name, session->protocol()->capabilities() ), m_session(session)
+{
+ historyPos = -1;
+
+ toggleAutoSpellCheck(KopetePrefs::prefs()->spellCheck());
+
+ mComplete = new KCompletion();
+ mComplete->setIgnoreCase( true );
+ mComplete->setOrder( KCompletion::Weighted );
+
+ // set params on the edit widget
+ edit()->setMinimumSize( QSize( 75, 20 ) );
+ edit()->setWordWrap( QTextEdit::WidgetWidth );
+ edit()->setWrapPolicy( QTextEdit::AtWhiteSpace );
+ edit()->setAutoFormatting( QTextEdit::AutoNone );
+
+ // some signals and slots connections
+ connect( edit(), SIGNAL( textChanged()), this, SLOT( slotTextChanged() ) );
+
+ // timers for typing notifications
+ m_typingRepeatTimer = new QTimer(this, "m_typingRepeatTimer");
+ m_typingStopTimer = new QTimer(this, "m_typingStopTimer");
+
+ connect( m_typingRepeatTimer, SIGNAL( timeout() ), this, SLOT( slotRepeatTypingTimer() ) );
+ connect( m_typingStopTimer, SIGNAL( timeout() ), this, SLOT( slotStoppedTypingTimer() ) );
+
+ connect( session, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
+ this, SLOT( slotContactAdded(const Kopete::Contact*) ) );
+ connect( session, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Kopete::Message::MessageFormat, bool) ),
+ this, SLOT( slotContactRemoved(const Kopete::Contact*) ) );
+ connect( session, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
+ this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+
+ slotContactAdded( session->myself() );
+ for ( QPtrListIterator<Kopete::Contact> it( session->members() ); it.current(); ++it )
+ slotContactAdded( *it );
+}
+
+ChatTextEditPart::~ChatTextEditPart()
+{
+ delete mComplete;
+}
+
+KTextEdit *ChatTextEditPart::edit()
+{
+ return static_cast<KTextEdit*>(widget());
+}
+
+void ChatTextEditPart::toggleAutoSpellCheck( bool enabled )
+{
+ if ( richTextEnabled() )
+ enabled = false;
+
+ m_autoSpellCheckEnabled = enabled;
+ if ( spellHighlighter() )
+ {
+ spellHighlighter()->setAutomatic( enabled );
+ spellHighlighter()->setActive( enabled );
+ }
+ edit()->setCheckSpellingEnabled( enabled );
+}
+
+bool ChatTextEditPart::autoSpellCheckEnabled() const
+{
+ return m_autoSpellCheckEnabled;
+}
+
+KDictSpellingHighlighter* ChatTextEditPart::spellHighlighter()
+{
+ QSyntaxHighlighter *qsh = edit()->syntaxHighlighter();
+ KDictSpellingHighlighter* kdsh = dynamic_cast<KDictSpellingHighlighter*>( qsh );
+ return kdsh;
+}
+
+// NAUGHTY, BAD AND WRONG! (but needed to fix nick complete bugs)
+#include <private/qrichtext_p.h>
+class EvilTextEdit : public KTextEdit
+{
+public:
+ // grab the paragraph as plain text - very very evil.
+ QString plainText( int para )
+ {
+ QString str = document()->paragAt( para )->string()->toString();
+ // str includes an extra space on the end (from the newline character?) - remove it
+ return str.left( str.length() - 1 );
+ }
+};
+
+void ChatTextEditPart::complete()
+{
+ int para = 1, parIdx = 1;
+ edit()->getCursorPosition( &para, &parIdx);
+
+ // FIXME: strips out all formatting
+ QString txt = static_cast<EvilTextEdit*>(edit())->plainText( para );
+
+ if ( parIdx > 0 )
+ {
+ int firstSpace = txt.findRev( QRegExp( QString::fromLatin1("\\s\\S+") ), parIdx - 1 ) + 1;
+ int lastSpace = txt.find( QRegExp( QString::fromLatin1("[\\s\\:]") ), firstSpace );
+ if( lastSpace == -1 )
+ lastSpace = txt.length();
+
+ QString word = txt.mid( firstSpace, lastSpace - firstSpace );
+ QString match;
+
+ kdDebug(14000) << k_funcinfo << word << " from '" << txt << "'" << endl;
+
+ if ( word != m_lastMatch )
+ {
+ match = mComplete->makeCompletion( word );
+ m_lastMatch = QString::null;
+ parIdx -= word.length();
+ }
+ else
+ {
+ match = mComplete->nextMatch();
+ parIdx -= m_lastMatch.length();
+ }
+
+ if ( !match.isNull() && !match.isEmpty() )
+ {
+ QString rightText = txt.right( txt.length() - lastSpace );
+
+ if ( para == 0 && firstSpace == 0 && rightText[0] != QChar(':') )
+ {
+ rightText = match + QString::fromLatin1(": ") + rightText;
+ parIdx += 2;
+ }
+ else
+ rightText = match + rightText;
+
+ // insert *before* remove. this is becase Qt adds an extra blank line
+ // if the rich text control becomes empty (if you remove the only para).
+ // disable updates while we change the contents to eliminate flicker.
+ edit()->setUpdatesEnabled( false );
+ edit()->insertParagraph( txt.left(firstSpace) + rightText, para );
+ edit()->removeParagraph( para + 1 );
+ edit()->setCursorPosition( para, parIdx + match.length() );
+ edit()->setUpdatesEnabled( true );
+ // must call this rather than update because QTextEdit is broken :(
+ edit()->updateContents();
+ m_lastMatch = match;
+ }
+ else
+ {
+ kdDebug(14000) << k_funcinfo << "No completions! Tried " << mComplete->items() << endl;
+ }
+ }
+}
+
+void ChatTextEditPart::slotPropertyChanged( Kopete::Contact*, const QString &key,
+ const QVariant& oldValue, const QVariant &newValue )
+{
+ if ( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ mComplete->removeItem( oldValue.toString() );
+ mComplete->addItem( newValue.toString() );
+ }
+}
+
+void ChatTextEditPart::slotContactAdded( const Kopete::Contact *contact )
+{
+ connect( contact, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+
+ QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
+ mComplete->addItem( contactName );
+}
+
+void ChatTextEditPart::slotContactRemoved( const Kopete::Contact *contact )
+{
+ disconnect( contact, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+
+ QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
+ mComplete->removeItem( contactName );
+}
+
+bool ChatTextEditPart::canSend()
+{
+ if ( !m_session ) return false;
+
+ // can't send if there's nothing *to* send...
+ if ( edit()->text().isEmpty() )
+ return false;
+
+ Kopete::ContactPtrList members = m_session->members();
+
+ // if we can't send offline, make sure we have a reachable contact...
+ if ( !( m_session->protocol()->capabilities() & Kopete::Protocol::CanSendOffline ) )
+ {
+ bool reachableContactFound = false;
+
+ //TODO: does this perform badly in large / busy IRC channels? - no, doesn't seem to
+ QPtrListIterator<Kopete::Contact> it ( members );
+ for( ; it.current(); ++it )
+ {
+ if ( (*it)->isReachable() )
+ {
+ reachableContactFound = true;
+ break;
+ }
+ }
+
+ // no online contact found and can't send offline? can't send.
+ if ( !reachableContactFound )
+ return false;
+ }
+
+ return true;
+}
+
+void ChatTextEditPart::slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
+{
+ //FIXME: should use signal contact->isReachableChanged, but it doesn't exist ;(
+ if ( ( oldStatus.status() == Kopete::OnlineStatus::Offline )
+ != ( newStatus.status() == Kopete::OnlineStatus::Offline ) )
+ {
+ emit canSendChanged( canSend() );
+ }
+}
+
+void ChatTextEditPart::sendMessage()
+{
+ QString txt = text( Qt::PlainText );
+ // avoid sending emtpy messages or enter keys (see bug 100334)
+ if ( txt.isEmpty() || txt == "\n" )
+ return;
+
+ if ( m_lastMatch.isNull() && ( txt.find( QRegExp( QString::fromLatin1("^\\w+:\\s") ) ) > -1 ) )
+ { //no last match and it finds something of the form of "word:" at the start of a line
+ QString search = txt.left( txt.find(':') );
+ if( !search.isEmpty() )
+ {
+ QString match = mComplete->makeCompletion( search );
+ if( !match.isNull() )
+ edit()->setText( txt.replace(0,search.length(),match) );
+ }
+ }
+
+ if ( !m_lastMatch.isNull() )
+ {
+ //FIXME: what is the next line for?
+ mComplete->addItem( m_lastMatch );
+ m_lastMatch = QString::null;
+ }
+
+ slotStoppedTypingTimer();
+ Kopete::Message sentMessage = contents();
+ emit messageSent( sentMessage );
+ historyList.prepend( edit()->text() );
+ historyPos = -1;
+ clear();
+ emit canSendChanged( false );
+}
+
+bool ChatTextEditPart::isTyping()
+{
+ QString txt = text( Qt::PlainText );
+
+ //Make sure the message is empty. QString::isEmpty()
+ //returns false if a message contains just whitespace
+ //which is the reason why we strip the whitespace
+ return !txt.stripWhiteSpace().isEmpty();
+}
+
+void ChatTextEditPart::slotTextChanged()
+{
+ if ( isTyping() )
+ {
+ // And they were previously typing
+ if( !m_typingRepeatTimer->isActive() )
+ {
+ m_typingRepeatTimer->start( 4000, false );
+ slotRepeatTypingTimer();
+ }
+
+ // Reset the stop timer again, regardless of status
+ m_typingStopTimer->start( 4500, true );
+ }
+
+ emit canSendChanged( canSend() );
+}
+
+void ChatTextEditPart::historyUp()
+{
+ if ( historyList.empty() || historyPos == historyList.count() - 1 )
+ return;
+
+ QString text = edit()->text();
+ bool empty = text.stripWhiteSpace().isEmpty();
+
+ // got text? save it
+ if ( !empty )
+ {
+ if ( historyPos == -1 )
+ {
+ historyList.prepend( text );
+ historyPos = 0;
+ }
+ else
+ {
+ historyList[historyPos] = text;
+ }
+ }
+
+ historyPos++;
+
+ QString newText = historyList[historyPos];
+ TextFormat format=edit()->textFormat();
+ edit()->setTextFormat(AutoText); //workaround bug 115690
+ edit()->setText( newText );
+ edit()->setTextFormat(format);
+ edit()->moveCursor( QTextEdit::MoveEnd, false );
+}
+
+void ChatTextEditPart::historyDown()
+{
+ if ( historyList.empty() || historyPos == -1 )
+ return;
+
+ QString text = edit()->text();
+ bool empty = text.stripWhiteSpace().isEmpty();
+
+ // got text? save it
+ if ( !empty )
+ {
+ historyList[historyPos] = text;
+ }
+
+ historyPos--;
+
+ QString newText = ( historyPos >= 0 ? historyList[historyPos] : QString::null );
+
+
+ TextFormat format=edit()->textFormat();
+ edit()->setTextFormat(AutoText); //workaround bug 115690
+ edit()->setText( newText );
+ edit()->setTextFormat(format);
+
+
+
+ edit()->moveCursor( QTextEdit::MoveEnd, false );
+}
+
+void ChatTextEditPart::addText( const QString &text )
+{
+ edit()->insert( text );
+}
+
+void ChatTextEditPart::setContents( const Kopete::Message &message )
+{
+ edit()->setText( richTextEnabled() ? message.escapedBody() : message.plainBody() );
+
+ setFont( message.font() );
+ setFgColor( message.fg() );
+ setBgColor( message.bg() );
+}
+
+Kopete::Message ChatTextEditPart::contents()
+{
+ Kopete::Message currentMsg( m_session->myself(), m_session->members(), edit()->text(),
+ Kopete::Message::Outbound, richTextEnabled() ?
+ Kopete::Message::RichText : Kopete::Message::PlainText );
+
+ currentMsg.setBg( bgColor() );
+ currentMsg.setFg( fgColor() );
+ currentMsg.setFont( font() );
+
+ return currentMsg;
+}
+
+void ChatTextEditPart::slotRepeatTypingTimer()
+{
+ emit typing( true );
+}
+
+void ChatTextEditPart::slotStoppedTypingTimer()
+{
+ m_typingRepeatTimer->stop();
+ m_typingStopTimer->stop();
+ emit typing( false );
+}
+
+#include "chattexteditpart.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chattexteditpart.h b/kopete/kopete/chatwindow/chattexteditpart.h
new file mode 100644
index 00000000..3391b8a7
--- /dev/null
+++ b/kopete/kopete/chatwindow/chattexteditpart.h
@@ -0,0 +1,209 @@
+/*
+ chattexteditpart.h - Chat Text Edit Part
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATTEXTEDITPART_H
+#define CHATTEXTEDITPART_H
+
+#include "krichtexteditpart.h"
+
+#include <qstringlist.h>
+
+class QTimer;
+
+class KCompletion;
+class KDictSpellingHighlighter;
+
+namespace Kopete
+{
+class Message;
+class Contact;
+class OnlineStatus;
+class ChatSession;
+}
+
+/**
+ * @brief An instant message composition part
+ *
+ * This class provides an input part suitable for the composition of instant messages.
+ * It provides command history, nickname completion and typing notifications. It is
+ * also able to determine whether the send button should be enabled.
+ *
+ * @author Richard Smith
+ */
+class ChatTextEditPart : public KopeteRichTextEditPart
+{
+ Q_OBJECT
+public:
+ ChatTextEditPart( Kopete::ChatSession *session, QWidget *parent, const char *name = 0 );
+ ~ChatTextEditPart();
+
+ /**
+ * Returns the message currently in the edit area
+ * @return The @ref Kopete::Message object for the message
+ */
+ Kopete::Message contents();
+
+ /**
+ * Sets the message in the edit field
+ * @param message The message to display
+ */
+ void setContents( const Kopete::Message &message );
+
+ /**
+ * Adds text into the edit area. Used when an emoticon is selected.
+ * @param text The text to be inserted
+ */
+ void addText( const QString &text );
+
+ /**
+ * Can we send messages now?
+ */
+ bool canSend();
+
+ /**
+ * Is the user typing right now?
+ */
+ bool isTyping();
+
+ /**
+ * @return This part's main widget
+ */
+ KTextEdit *edit();
+
+ /**
+ * Enable or Disable the automatic spell checking
+ * @param enabled the state that auto spell checking should beee
+ */
+ void toggleAutoSpellCheck( bool enabled );
+
+ /**
+ * Get the state of auto spell checking
+ * @return true if auto spell checking is turned on, false otherwise
+ */
+ bool autoSpellCheckEnabled() const;
+
+public slots:
+ /**
+ * Go up an entry in the message history.
+ */
+ void historyUp();
+
+ /**
+ * Go down an entry in the message history.
+ */
+ void historyDown();
+
+ /**
+ * Try to complete the word under the cursor.
+ */
+ void complete();
+
+ /**
+ * Sends the text currently entered into the edit area.
+ */
+ void sendMessage();
+
+signals:
+ /**
+ * Emitted when a message is sent.
+ * @param message The message sent
+ */
+ void messageSent( Kopete::Message &message );
+
+ /**
+ * Emitted every 4 seconds while the user is typing.
+ * @param typing @c true if the user is typing, @c false otherwise
+ */
+ void typing( bool typing );
+
+ /**
+ * Our send-button-enabled flag might have changed
+ * @param canSend The return value of @ref canSend().
+ */
+ void canSendChanged( bool canSend );
+
+private slots:
+ /**
+ * Called when a contact is added to the chat session.
+ * Adds this contact to the nickname completion list.
+ * @param c The contact that joined the chat
+ */
+ void slotContactAdded( const Kopete::Contact *c );
+
+ /**
+ * Called when a contact is removed from the chat session.
+ * Removes this contact from the nickname completion list.
+ * @param c The contact left the chat
+ */
+ void slotContactRemoved( const Kopete::Contact *c );
+
+ /**
+ * Called when a contact changes status, may emit @ref canSendChanged.
+ * @param contact The contact who changed status
+ * @param status The new status of the contact
+ */
+ void slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldstatus );
+
+ /**
+ * Called when text is changed in the edit area
+ */
+ void slotTextChanged();
+
+ /**
+ * User is typing, so emit a @ref typing( @c true ) signal every 4 seconds.
+ * This is stupid. Why not just emit it once?
+ */
+ void slotRepeatTypingTimer();
+
+ /**
+ * Emits a @ref typing( @c false ) signal 4.5 seconds after the user stops typing.
+ */
+ void slotStoppedTypingTimer();
+
+ /**
+ * Update completion to follow changes in users' nicknames
+ */
+ void slotPropertyChanged( Kopete::Contact *, const QString &key, const QVariant &oldValue, const QVariant &newValue );
+
+private:
+ KDictSpellingHighlighter* spellHighlighter();
+
+private:
+ Kopete::ChatSession *m_session;
+
+ /**
+ * The history buffer conceptually works like this:
+ * We have a list of messages (historyList), with indices from -1 to n.
+ * historyPos is our current position in this list; historyList[historyPos]
+ * is conceptually the message we are editing. The exception to this is that
+ * index -1 is treated specially; when it is modified, changes are saved to
+ * a new message placed at index 0.
+ */
+ QStringList historyList;
+ int historyPos;
+
+ KCompletion *mComplete;
+ QString m_lastMatch;
+
+ QTimer *m_typingRepeatTimer;
+ QTimer *m_typingStopTimer;
+ bool m_autoSpellCheckEnabled;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/chatwindow/chatview.cpp b/kopete/kopete/chatwindow/chatview.cpp
new file mode 100644
index 00000000..62c1206d
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatview.cpp
@@ -0,0 +1,1087 @@
+/*
+ chatview.cpp - Chat View
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatview.h"
+
+#include "chatmemberslistwidget.h"
+#include "chatmessagepart.h"
+#include "chattexteditpart.h"
+#include "kopetechatwindow.h"
+#include "kopetechatsession.h"
+#include "kopetemetacontact.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopeteglobal.h"
+#include "kopetecontactlist.h"
+#include "kopeteviewmanager.h"
+
+#include <kconfig.h>
+#include <ktabwidget.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kstringhandler.h>
+#include <kwin.h>
+#include <kurldrag.h>
+#include <kglobalsettings.h>
+#include <kgenericfactory.h>
+#include <khtmlview.h>
+#include <ksyntaxhighlighter.h>
+#include <qscrollview.h>
+#include <qtimer.h>
+
+typedef KGenericFactory<ChatWindowPlugin> ChatWindowPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_chatwindow, ChatWindowPluginFactory( "kopete_chatwindow" ) )
+
+ChatWindowPlugin::ChatWindowPlugin(QObject *parent, const char *name, const QStringList &) :
+ Kopete::ViewPlugin( ChatWindowPluginFactory::instance(), parent, name )
+{}
+
+KopeteView* ChatWindowPlugin::createView( Kopete::ChatSession *manager )
+{
+ return (KopeteView*)new ChatView(manager,this);
+}
+
+class KopeteChatViewPrivate
+{
+public:
+ QString captionText;
+ QString statusText;
+ bool isActive;
+ bool sendInProgress;
+ bool visibleMembers;
+};
+
+ChatView::ChatView( Kopete::ChatSession *mgr, ChatWindowPlugin *parent, const char *name )
+ : KDockMainWindow( 0L, name, 0L ), KopeteView( mgr, parent )
+{
+ d = new KopeteChatViewPrivate;
+ d->isActive = false;
+ d->visibleMembers = false;
+ d->sendInProgress = false;
+
+
+ m_mainWindow = 0L;
+ membersDock = 0L;
+ membersStatus = Smart;
+ m_tabState = Normal;
+
+
+ //FIXME: don't widgets start off hidden anyway?
+ hide();
+
+ //Create the view dock widget (KHTML Part), and set it to no docking (lock it in place)
+ viewDock = createDockWidget(QString::fromLatin1( "viewDock" ), QPixmap(),
+ 0L,QString::fromLatin1("viewDock"), QString::fromLatin1(" "));
+ m_messagePart = new ChatMessagePart( mgr, viewDock, "m_messagePart" );
+
+ viewDock->setWidget(messagePart()->widget());
+ viewDock->setDockSite(KDockWidget::DockBottom);
+ viewDock->setEnableDocking(KDockWidget::DockNone);
+
+ //Create the bottom dock widget, with the edit area, statusbar and send button
+ editDock = createDockWidget( QString::fromLatin1( "editDock" ), QPixmap(),
+ 0L, QString::fromLatin1("editDock"), QString::fromLatin1(" ") );
+ m_editPart = new ChatTextEditPart( mgr, editDock, "kopeterichtexteditpart" );
+
+ // FIXME: is this used these days? it seems totally unnecessary
+ connect( editPart(), SIGNAL( toggleToolbar(bool)), this, SLOT(slotToggleRtfToolbar(bool)) );
+
+ connect( editPart(), SIGNAL( messageSent( Kopete::Message & ) ),
+ this, SIGNAL( messageSent( Kopete::Message & ) ) );
+ connect( editPart(), SIGNAL( canSendChanged( bool ) ),
+ this, SIGNAL( canSendChanged(bool) ) );
+ connect( editPart(), SIGNAL( typing(bool) ),
+ mgr, SLOT( typing(bool) ) );
+
+ //Make the edit area dockable for now
+ editDock->setWidget( editPart()->widget() );
+ editDock->setDockSite( KDockWidget::DockNone );
+ editDock->setEnableDocking(KDockWidget::DockBottom);
+
+ //Set the view as the main widget
+ setMainDockWidget( viewDock );
+ setView(viewDock);
+
+ //It is possible to drag and drop on this widget.
+ // I had to disable the acceptDrop in the khtml widget to be able to intercept theses events.
+ setAcceptDrops(true);
+ viewDock->setAcceptDrops(false);
+
+ m_remoteTypingMap.setAutoDelete( true );
+
+ //Manager signals
+ connect( mgr, SIGNAL( displayNameChanged() ),
+ this, SLOT( slotChatDisplayNameChanged() ) );
+ connect( mgr, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
+ this, SLOT( slotContactAdded(const Kopete::Contact*, bool) ) );
+ connect( mgr, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Kopete::Message::MessageFormat, bool) ),
+ this, SLOT( slotContactRemoved(const Kopete::Contact*, const QString&, Kopete::Message::MessageFormat, bool) ) );
+ connect( mgr, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
+ this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+ connect( mgr, SIGNAL( remoteTyping( const Kopete::Contact *, bool) ),
+ this, SLOT( remoteTyping(const Kopete::Contact *, bool) ) );
+ connect( mgr, SIGNAL( eventNotification( const QString& ) ),
+ this, SLOT( setStatusText( const QString& ) ) );
+
+ //Connections to the manager and the ViewManager that every view should have
+ connect( this, SIGNAL( closing( KopeteView * ) ),
+ KopeteViewManager::viewManager(), SLOT( slotViewDestroyed( KopeteView * ) ) );
+ connect( this, SIGNAL( activated( KopeteView * ) ),
+ KopeteViewManager::viewManager(), SLOT( slotViewActivated( KopeteView * ) ) );
+ connect( this, SIGNAL( messageSent(Kopete::Message &) ),
+ mgr, SLOT( sendMessage(Kopete::Message &) ) );
+ connect( mgr, SIGNAL( messageSuccess() ),
+ this, SLOT( messageSentSuccessfully() ));
+
+ // add contacts
+ slotContactAdded( mgr->myself(), true );
+ for ( QPtrListIterator<Kopete::Contact> it( mgr->members() ); it.current(); ++it )
+ slotContactAdded( *it, true );
+
+ setFocusProxy( editPart()->widget() );
+ editPart()->widget()->setFocus();
+
+ // init actions
+ KStdAction::copy( this, SLOT(copy()), actionCollection() );
+ KStdAction::close( this, SLOT(closeView()),actionCollection() );
+
+ setCaption( m_manager->displayName(), false );
+
+ // restore docking positions
+ readOptions();
+
+ // maybe show chat members
+ createMembersList();
+}
+
+ChatView::~ChatView()
+{
+ emit( closing( static_cast<KopeteView*>(this) ) );
+
+ saveOptions();
+
+ delete d;
+}
+
+KTextEdit *ChatView::editWidget()
+{
+ return editPart()->widget();
+}
+
+QWidget *ChatView::mainWidget()
+{
+ return this;
+}
+
+bool ChatView::canSend()
+{
+ return editPart()->canSend();
+}
+
+Kopete::Message ChatView::currentMessage()
+{
+ return editPart()->contents();
+}
+
+void ChatView::setCurrentMessage( const Kopete::Message &message )
+{
+ editPart()->setContents( message );
+}
+
+void ChatView::cut()
+{
+ editPart()->edit()->cut();
+}
+
+void ChatView::copy()
+{
+ if ( messagePart()->hasSelection() )
+ messagePart()->copy();
+ else
+ editPart()->edit()->copy();
+}
+
+void ChatView::paste()
+{
+ editPart()->edit()->paste();
+}
+
+void ChatView::nickComplete()
+{
+ return editPart()->complete();
+}
+
+void ChatView::addText( const QString &text )
+{
+ editPart()->addText( text );
+}
+
+void ChatView::clear()
+{
+ messagePart()->clear();
+}
+
+void ChatView::setBgColor( const QColor &newColor )
+{
+ editPart()->setBgColor( newColor );
+}
+
+void ChatView::setFont()
+{
+ editPart()->setFont();
+}
+
+QFont ChatView::font()
+{
+ return editPart()->font();
+}
+
+void ChatView::setFont( const QFont &font )
+{
+ editPart()->setFont( font );
+}
+
+void ChatView::setFgColor( const QColor &newColor )
+{
+ editPart()->setFgColor( newColor );
+}
+
+void ChatView::raise( bool activate )
+{
+ // this shouldn't change the focus. When the window is raised when a new message arrives
+ // if i am coding, or talking to someone else, i want to end my sentence before switching to
+ // the other chat. i just want to KNOW and SEE the other chat to switch to it later
+ // (except if activate==true)
+
+ if ( !m_mainWindow || !m_mainWindow->isActiveWindow() || activate )
+ makeVisible();
+
+ if ( !KWin::windowInfo( m_mainWindow->winId(), NET::WMDesktop ).onAllDesktops() )
+ if( KopetePrefs::prefs()->trayflashNotifySetCurrentDesktopToChatView() && activate )
+ KWin::setCurrentDesktop( KWin::windowInfo( m_mainWindow->winId(), NET::WMDesktop ).desktop() );
+ else
+ KWin::setOnDesktop( m_mainWindow->winId(), KWin::currentDesktop() );
+
+ if(m_mainWindow->isMinimized())
+ {
+ m_mainWindow->showNormal();
+ }
+
+
+ m_mainWindow->raise();
+
+ /* Removed Nov 2003
+ According to Zack, the user double-clicking a contact is not valid reason for a non-pager
+ to grab window focus. While I don't agree with this, and it runs contradictory to every other
+ IM out there, commenting this code out to agree with KWin policy.
+
+ Redirect any bugs relating to the widnow now not grabbing focus on clicking a contact to KWin.
+ - Jason K
+ */
+
+ //Will not activate window if user was typing
+ if ( activate )
+ KWin::activateWindow( m_mainWindow->winId() );
+
+}
+
+void ChatView::makeVisible()
+{
+ if ( !m_mainWindow )
+ {
+ m_mainWindow = KopeteChatWindow::window( m_manager );
+// if ( root )
+// root->repaint( true );
+ emit windowCreated();
+ }
+
+ if ( !m_mainWindow->isVisible() )
+ {
+ m_mainWindow->show();
+ // scroll down post show and layout, otherwise the geometry is wrong to scroll to the bottom.
+ m_messagePart->keepScrolledDown();
+ }
+
+
+
+ m_mainWindow->setActiveView( this );
+}
+
+bool ChatView::isVisible()
+{
+ return ( m_mainWindow && m_mainWindow->isVisible() );
+}
+
+bool ChatView::visibleMembersList()
+{
+ return d->visibleMembers;
+}
+
+bool ChatView::sendInProgress()
+{
+ return d->sendInProgress;
+}
+
+bool ChatView::closeView( bool force )
+{
+ int response = KMessageBox::Continue;
+
+ if ( !force )
+ {
+ if ( m_manager->members().count() > 1 )
+ {
+ QString shortCaption = d->captionText;
+ shortCaption = KStringHandler::rsqueeze( shortCaption );
+
+ response = KMessageBox::warningContinueCancel( this, i18n("<qt>You are about to leave the group chat session <b>%1</b>.<br>"
+ "You will not receive future messages from this conversation.</qt>").arg( shortCaption ), i18n( "Closing Group Chat" ),
+ i18n( "Cl&ose Chat" ), QString::fromLatin1( "AskCloseGroupChat" ) );
+ }
+
+ if ( !unreadMessageFrom.isNull() && ( response == KMessageBox::Continue ) )
+ {
+ response = KMessageBox::warningContinueCancel( this, i18n("<qt>You have received a message from <b>%1</b> in the last "
+ "second. Are you sure you want to close this chat?</qt>").arg( unreadMessageFrom ), i18n( "Unread Message" ),
+ i18n( "Cl&ose Chat" ), QString::fromLatin1("AskCloseChatRecentMessage" ) );
+ }
+
+ if ( d->sendInProgress && ( response == KMessageBox::Continue ) )
+ {
+ response = KMessageBox::warningContinueCancel( this, i18n( "You have a message send in progress, which will be "
+ "aborted if this chat is closed. Are you sure you want to close this chat?" ), i18n( "Message in Transit" ),
+ i18n( "Cl&ose Chat" ), QString::fromLatin1( "AskCloseChatMessageInProgress" ) );
+ }
+ }
+
+ if( response == KMessageBox::Continue )
+ {
+ // Remove the widget from the window it's attached to
+ // and schedule it for deletion
+ if( m_mainWindow )
+ m_mainWindow->detachChatView( this );
+ deleteLater();
+
+ return true;
+ }
+
+ return false;
+}
+
+void ChatView::updateChatState( KopeteTabState newState )
+{
+ if ( newState == Undefined )
+ newState = m_tabState;
+ else if ( newState != Typing && ( newState != Changed || ( m_tabState != Message && m_tabState != Highlighted ) )
+ && ( newState != Message || m_tabState != Highlighted ) )
+ { //if the new state is not a typing state and we don't already have a message or a highlighted message
+ //change the tab state
+ m_tabState = newState;
+ }
+
+ newState = m_remoteTypingMap.isEmpty() ? m_tabState : Typing ;
+
+ emit updateChatState( this, newState );
+
+ if( newState != Typing )
+ {
+ setStatusText( i18n( "One other person in the chat",
+ "%n other people in the chat", m_manager->members().count() ) );
+ }
+}
+
+void ChatView::setMainWindow( KopeteChatWindow* parent )
+{
+ m_mainWindow = parent;
+}
+
+void ChatView::createMembersList()
+{
+ if ( !membersDock )
+ {
+ //Create the chat members list
+ membersDock = createDockWidget( QString::fromLatin1( "membersDock" ), QPixmap(), 0L,
+ QString::fromLatin1( "membersDock" ), QString::fromLatin1( " " ) );
+ m_membersList = new ChatMembersListWidget( m_manager, this, "m_membersList" );
+
+ membersDock->setWidget( m_membersList );
+
+ Kopete::ContactPtrList members = m_manager->members();
+
+ if ( members.first() && members.first()->metaContact() != 0 )
+ {
+ membersStatus = static_cast<MembersListPolicy>
+ (
+ members.first()->metaContact()->pluginData
+ ( m_manager->protocol(), QString::fromLatin1( "MembersListPolicy" ) ).toInt()
+ );
+ }
+ else
+ {
+ membersStatus = Smart;
+ }
+
+ if( membersStatus == Smart )
+ d->visibleMembers = ( m_manager->members().count() > 1 );
+ else
+ d->visibleMembers = ( membersStatus == Visible );
+
+ placeMembersList( membersDockPosition );
+ }
+}
+
+void ChatView::toggleMembersVisibility()
+{
+ if( membersDock )
+ {
+ d->visibleMembers = !d->visibleMembers;
+ membersStatus = d->visibleMembers ? Visible : Hidden;
+ placeMembersList( membersDockPosition );
+ Kopete::ContactPtrList members = m_manager->members();
+ if ( members.first()->metaContact() )
+ {
+ members.first()->metaContact()->setPluginData( m_manager->protocol(),
+ QString::fromLatin1( "MembersListPolicy" ), QString::number(membersStatus) );
+ }
+ //refreshView();
+ }
+}
+
+void ChatView::placeMembersList( KDockWidget::DockPosition dp )
+{
+// kdDebug(14000) << k_funcinfo << "Members list policy " << membersStatus <<
+// ", visible " << d->visibleMembers << endl;
+
+ if ( d->visibleMembers )
+ {
+ membersDockPosition = dp;
+
+ // look up the dock width
+ int dockWidth;
+ KGlobal::config()->setGroup( QString::fromLatin1( "ChatViewDock" ) );
+
+ if( membersDockPosition == KDockWidget::DockLeft )
+ {
+ dockWidth = KGlobal::config()->readNumEntry(
+ QString::fromLatin1( "membersDock,viewDock:sepPos" ), 30);
+ }
+ else
+ {
+ dockWidth = KGlobal::config()->readNumEntry(
+ QString::fromLatin1( "viewDock,membersDock:sepPos" ), 70);
+ }
+
+ // Make sure it is shown then place it wherever
+ membersDock->setEnableDocking( KDockWidget::DockLeft | KDockWidget::DockRight );
+ membersDock->manualDock( viewDock, membersDockPosition, dockWidth );
+ membersDock->show();
+ membersDock->setEnableDocking( KDockWidget::DockNone );
+ }
+ else
+ {
+ // Dock it to the desktop then hide it
+ membersDock->undock();
+ membersDock->hide();
+ }
+
+ if( d->isActive )
+ m_mainWindow->updateMembersActions();
+
+ //refreshView();
+}
+
+void ChatView::remoteTyping( const Kopete::Contact *contact, bool isTyping )
+{
+ // Make sure we (re-)add the timer at the end, because the slot will
+ // remove the first timer
+ // And yes, the const_cast is a bit ugly, but it's only used as key
+ // value in this dictionary (no indirections) so it's basically
+ // harmless. Unfortunately there's no QConstPtrDictionary in Qt...
+ void *key = const_cast<Kopete::Contact *>( contact );
+ m_remoteTypingMap.remove( key );
+ if( isTyping )
+ {
+ m_remoteTypingMap.insert( key, new QTimer(this) );
+ connect( m_remoteTypingMap[ key ], SIGNAL( timeout() ), SLOT( slotRemoteTypingTimeout() ) );
+ m_remoteTypingMap[ key ]->start( 6000, true );
+ }
+
+ // Loop through the map, constructing a string of people typing
+ QStringList typingList;
+ QPtrDictIterator<QTimer> it( m_remoteTypingMap );
+
+ for( ; it.current(); ++it )
+ {
+ Kopete::Contact *c = static_cast<Kopete::Contact*>( it.currentKey() );
+ QString nick;
+ if( c->metaContact() && c->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ nick = c->metaContact()->displayName();
+ }
+ else
+ {
+ nick = c->nickName();
+ }
+ typingList.append( nick );
+ }
+
+ // Update the status area
+ if( !typingList.isEmpty() )
+ {
+ if ( typingList.count() == 1 )
+ setStatusText( i18n( "%1 is typing a message" ).arg( typingList.first() ) );
+ else
+ {
+ QString statusTyping = typingList.join( QString::fromLatin1( ", " ) );
+ setStatusText( i18n( "%1 is a list of names", "%1 are typing a message" ).arg( statusTyping ) );
+ }
+ updateChatState( Typing );
+ }
+ else
+ {
+ updateChatState();
+ }
+}
+
+void ChatView::setStatusText( const QString &status )
+{
+ d->statusText = status;
+ if ( d->isActive )
+ m_mainWindow->setStatus( status );
+}
+
+const QString& ChatView::statusText()
+{
+ return d->statusText;
+}
+
+void ChatView::slotChatDisplayNameChanged()
+{
+ // This fires whenever a contact or MC changes displayName, so only
+ // update the caption if it changed to avoid unneeded updates that
+ // could cause flickering
+ QString chatName = m_manager->displayName();
+ if ( chatName != d->captionText )
+ setCaption( chatName, true );
+}
+
+void ChatView::slotPropertyChanged( Kopete::Contact*, const QString &key,
+ const QVariant& oldValue, const QVariant &newValue )
+{
+ if ( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ QString newName=newValue.toString();
+ QString oldName=oldValue.toString();
+
+ if(KopetePrefs::prefs()->showEvents())
+ if ( oldName != newName && !oldName.isEmpty())
+ sendInternalMessage( i18n( "%1 is now known as %2" ). arg( oldName, newName ) );
+ }
+}
+
+void ChatView::slotDisplayNameChanged( const QString &oldValue, const QString &newValue )
+{
+ if( KopetePrefs::prefs()->showEvents() )
+ {
+ if( oldValue != newValue )
+ sendInternalMessage( i18n( "%1 is now known as %2" ). arg( oldValue, newValue ) );
+ }
+}
+
+void ChatView::slotContactAdded(const Kopete::Contact *contact, bool suppress)
+{
+ QString contactName;
+ // Myself metacontact is not a reliable source.
+ if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ contactName = contact->metaContact()->displayName();
+ }
+ else
+ {
+ contactName = contact->nickName();
+ }
+
+ if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ connect( contact->metaContact(), SIGNAL( displayNameChanged(const QString&, const QString&) ),
+ this, SLOT( slotDisplayNameChanged(const QString &, const QString &) ) );
+ }
+ else
+ {
+ connect( contact, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+ }
+
+ if( !suppress && m_manager->members().count() > 1 )
+ sendInternalMessage( i18n("%1 has joined the chat.").arg(contactName) );
+
+ if( membersStatus == Smart && membersDock )
+ {
+ bool shouldShowMembers = ( m_manager->members().count() > 1);
+ if( shouldShowMembers != d->visibleMembers )
+ {
+ d->visibleMembers = shouldShowMembers;
+ placeMembersList( membersDockPosition );
+ }
+ }
+
+ updateChatState();
+ emit updateStatusIcon( this );
+}
+
+void ChatView::slotContactRemoved( const Kopete::Contact *contact, const QString &reason, Kopete::Message::MessageFormat format, bool suppressNotification )
+{
+// kdDebug(14000) << k_funcinfo << endl;
+ if ( contact != m_manager->myself() )
+ {
+ m_remoteTypingMap.remove( const_cast<Kopete::Contact *>( contact ) );
+
+ QString contactName;
+ if( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ contactName = contact->metaContact()->displayName();
+ }
+ else
+ {
+ contactName = contact->nickName();
+ }
+
+ // When the last person leaves, don't disconnect the signals, since we're in a one-to-one chat
+ if ( m_manager->members().count() > 0 )
+ {
+ if( contact->metaContact() )
+ {
+ disconnect( contact->metaContact(), SIGNAL( displayNameChanged(const QString&, const QString&) ),
+ this, SLOT( slotDisplayNameChanged(const QString&, const QString&) ) );
+ }
+ else
+ {
+ disconnect(contact,SIGNAL(propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & )),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+ }
+ }
+
+ if ( !suppressNotification )
+ {
+ if ( reason.isEmpty() )
+ sendInternalMessage( i18n( "%1 has left the chat." ).arg( contactName ), format ) ;
+ else
+ sendInternalMessage( i18n( "%1 has left the chat (%2)." ).arg( contactName, reason ), format);
+ }
+ }
+
+ updateChatState();
+ emit updateStatusIcon( this );
+}
+
+QString& ChatView::caption() const
+{
+ return d->captionText;
+}
+
+void ChatView::setCaption( const QString &text, bool modified )
+{
+// kdDebug(14000) << k_funcinfo << endl;
+ QString newCaption = text;
+
+ //Save this caption
+ d->captionText = text;
+
+ //Turncate if needed
+ newCaption = KStringHandler::rsqueeze( d->captionText, 20 );
+
+ //Call the original set caption
+ KDockMainWindow::setCaption( newCaption, false );
+
+ emit updateChatTooltip( this, QString::fromLatin1("<qt>%1</qt>").arg( d->captionText ) );
+ emit updateChatLabel( this, newCaption );
+ //Blink icon if modified and not active
+ if( !d->isActive && modified )
+ updateChatState( Changed );
+ else
+ updateChatState();
+
+ //Tell the parent we changed our caption
+ emit( captionChanged( d->isActive ) );
+}
+
+void ChatView::appendMessage(Kopete::Message &message)
+{
+ remoteTyping( message.from(), false );
+
+ messagePart()->appendMessage(message);
+
+ if( !d->isActive )
+ {
+ switch ( message.importance() )
+ {
+ case Kopete::Message::Highlight:
+ updateChatState( Highlighted );
+ break;
+ case Kopete::Message::Normal:
+ if ( message.direction() == Kopete::Message::Inbound )
+ {
+ updateChatState( Message );
+ break;
+ } // if it's an enternal message or a outgoing, fall thought
+ default:
+ updateChatState( Changed );
+ }
+ }
+
+ if( message.direction() == Kopete::Message::Inbound )
+ {
+ if( message.from()->metaContact() && message.from()->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ unreadMessageFrom = message.from()->metaContact()->displayName();
+ }
+ else
+ {
+ unreadMessageFrom = message.from()->nickName();
+ }
+ QTimer::singleShot( 1000, this, SLOT( slotMarkMessageRead() ) );
+ }
+ else
+ unreadMessageFrom = QString::null;
+}
+
+void ChatView::slotMarkMessageRead()
+{
+ unreadMessageFrom = QString::null;
+}
+
+void ChatView::slotToggleRtfToolbar( bool enabled )
+{
+ emit rtfEnabled( this, enabled );
+}
+
+void ChatView::slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
+{
+ kdDebug(14000) << k_funcinfo << contact << endl;
+ bool inhibitNotification = ( newStatus.status() == Kopete::OnlineStatus::Unknown ||
+ oldStatus.status() == Kopete::OnlineStatus::Unknown );
+ if ( contact && KopetePrefs::prefs()->showEvents() && !inhibitNotification )
+ {
+ if ( contact->account() && contact == contact->account()->myself() )
+ {
+ // Separate notification for the 'self' contact
+ if ( newStatus.status() != Kopete::OnlineStatus::Connecting )
+ sendInternalMessage( i18n( "You are now marked as %1." ).arg( newStatus.description() ) );
+ }
+ else if ( !contact->account() || !contact->account()->suppressStatusNotification() )
+ {
+ // Don't send notifications when we just connected ourselves, i.e. when suppressions are still active
+ if ( contact->metaContact() && contact->metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ sendInternalMessage( i18n( "%2 is now %1." )
+ .arg( newStatus.description(), contact->metaContact()->displayName() ) );
+ }
+ else
+ {
+ QString nick=contact->nickName();
+ sendInternalMessage( i18n( "%2 is now %1." )
+ .arg( newStatus.description(), nick ) );
+ }
+ }
+ }
+
+ // update the windows caption
+ slotChatDisplayNameChanged();
+ emit updateStatusIcon( this );
+}
+
+void ChatView::sendInternalMessage(const QString &msg, Kopete::Message::MessageFormat format )
+{
+ // When closing kopete, some internal message may be sent because some contact are deleted
+ // these contacts can already be deleted
+ Kopete::Message message = Kopete::Message( 0L /*m_manager->myself()*/ , 0L /*m_manager->members()*/, msg, Kopete::Message::Internal, format );
+ // (in many case, this is useless to set myself as contact)
+ // TODO: set the contact which initiate the internal message,
+ // so we can later show a icon of it (for example, when he join a chat)
+ messagePart()->appendMessage( message );
+}
+
+void ChatView::sendMessage()
+{
+ d->sendInProgress = true;
+ editPart()->sendMessage();
+}
+
+void ChatView::messageSentSuccessfully()
+{
+ d->sendInProgress = false;
+ emit messageSuccess( this );
+}
+
+void ChatView::saveOptions()
+{
+ KConfig *config = KGlobal::config();
+
+ writeDockConfig ( config, QString::fromLatin1( "ChatViewDock" ) );
+ config->setGroup( QString::fromLatin1( "ChatViewDock" ) );
+ config->writeEntry( QString::fromLatin1( "membersDockPosition" ), membersDockPosition );
+ saveChatSettings();
+ config->sync();
+}
+
+void ChatView::saveChatSettings()
+{
+ Kopete::ContactPtrList contacts = msgManager()->members();
+
+ if ( contacts.count() == 0 )
+ return;
+
+ Kopete::MetaContact* mc = contacts.first()->metaContact();
+
+ if ( contacts.count() > 1 )
+ return; //can't save with more than one person in chatview
+
+ if ( !mc )
+ return;
+
+ KConfig* config = KGlobal::config();
+
+ QString contactListGroup = QString::fromLatin1("chatwindow_") +
+ mc->metaContactId();
+
+ config->setGroup( contactListGroup );
+ config->writeEntry( "EnableRichText", editPart()->richTextEnabled() );
+ config->writeEntry( "EnableAutoSpellCheck", editPart()->autoSpellCheckEnabled() );
+ config->sync();
+}
+
+void ChatView::loadChatSettings()
+{
+ Kopete::ContactPtrList contacts = msgManager()->members();
+ if ( contacts.count() > 1 )
+ return; //can't load with more than one other person in the chat
+
+ //read settings for metacontact
+ QString contactListGroup = QString::fromLatin1("chatwindow_") +
+ contacts.first()->metaContact()->metaContactId();
+ KConfig* config = KGlobal::config();
+ config->setGroup( contactListGroup );
+ bool enableRichText = config->readBoolEntry( "EnableRichText", true );
+ editPart()->slotSetRichTextEnabled( enableRichText );
+ emit rtfEnabled( this, editPart()->richTextEnabled() );
+ bool enableAutoSpell = config->readBoolEntry( "EnableAutoSpellCheck", false );
+ emit autoSpellCheckEnabled( this, enableAutoSpell );
+}
+
+void ChatView::readOptions()
+{
+ KConfig *config = KGlobal::config();
+
+ /** THIS IS BROKEN !!! */
+ //dockManager->readConfig ( config, QString::fromLatin1("ChatViewDock") );
+
+ //Work-around to restore dock widget positions
+ config->setGroup( QString::fromLatin1( "ChatViewDock" ) );
+
+ membersDockPosition = static_cast<KDockWidget::DockPosition>(
+ config->readNumEntry( QString::fromLatin1( "membersDockPosition" ), KDockWidget::DockRight ) );
+
+ QString dockKey = QString::fromLatin1( "viewDock" );
+ if ( d->visibleMembers )
+ {
+ if( membersDockPosition == KDockWidget::DockLeft )
+ dockKey.prepend( QString::fromLatin1( "membersDock," ) );
+ else if( membersDockPosition == KDockWidget::DockRight )
+ dockKey.append( QString::fromLatin1( ",membersDock" ) );
+ }
+
+ dockKey.append( QString::fromLatin1( ",editDock:sepPos" ) );
+ //kdDebug(14000) << k_funcinfo << "reading splitterpos from key: " << dockKey << endl;
+ int splitterPos = config->readNumEntry( dockKey, 70 );
+ editDock->manualDock( viewDock, KDockWidget::DockBottom, splitterPos );
+ viewDock->setDockSite( KDockWidget::DockLeft | KDockWidget::DockRight );
+ editDock->setEnableDocking( KDockWidget::DockNone );
+}
+
+void ChatView::setActive( bool value )
+{
+ d->isActive = value;
+ if ( d->isActive )
+ {
+ updateChatState( Normal );
+ emit( activated( static_cast<KopeteView*>(this) ) );
+ }
+}
+
+void ChatView::slotRemoteTypingTimeout()
+{
+ // Remove the topmost timer from the list. Why does QPtrDict use void* keys and not typed keys? *sigh*
+ if ( !m_remoteTypingMap.isEmpty() )
+ remoteTyping( reinterpret_cast<const Kopete::Contact *>( QPtrDictIterator<QTimer>(m_remoteTypingMap).currentKey() ), false );
+}
+
+void ChatView::dragEnterEvent ( QDragEnterEvent * event )
+{
+ if( event->provides( "kopete/x-contact" ) )
+ {
+ QStringList lst=QStringList::split( QChar( 0xE000 ) , QString::fromUtf8(event->encodedData ( "kopete/x-contact" )) );
+ if(m_manager->mayInvite() && m_manager->protocol()->pluginId() == lst[0] && m_manager->account()->accountId() == lst[1])
+ {
+ QString contact=lst[2];
+
+ bool found =false;
+ QPtrList<Kopete::Contact> cts=m_manager->members();
+ for ( QPtrListIterator<Kopete::Contact> it( cts ); it.current(); ++it )
+ {
+ if(it.current()->contactId() == contact)
+ {
+ found=true;
+ break;
+ }
+ }
+
+ if(!found && contact != m_manager->myself()->contactId())
+ event->accept();
+ }
+ }
+ else if( event->provides( "kopete/x-metacontact" ) )
+ {
+ QString metacontactID=QString::fromUtf8(event->encodedData ( "kopete/x-metacontact" ));
+ Kopete::MetaContact *m=Kopete::ContactList::self()->metaContact(metacontactID);
+
+ if( m && m_manager->mayInvite())
+ {
+ QPtrList<Kopete::Contact> cts=m->contacts();
+ for ( QPtrListIterator<Kopete::Contact> it( cts ); it.current(); ++it )
+ {
+ Kopete::Contact *c=it.current();
+ if(c && c->account() == m_manager->account())
+ {
+ if( c != m_manager->myself() && !m_manager->members().contains(c) && c->isOnline())
+ event->accept();
+ }
+ }
+ }
+ }
+ // make sure it doesn't come from the current chat view - then it's an emoticon
+ else if ( event->provides( "text/uri-list" ) && m_manager->members().count() == 1 &&
+ ( event->source() != (QWidget*)m_messagePart->view()->viewport() ) )
+ {
+ Kopete::ContactPtrList members = m_manager->members();
+ Kopete::Contact *contact = members.first();
+ if ( contact && contact->canAcceptFiles() )
+ event->accept();
+ }
+ else
+ KDockMainWindow::dragEnterEvent(event);
+}
+
+void ChatView::dropEvent ( QDropEvent * event )
+{
+ if( event->provides( "kopete/x-contact" ) )
+ {
+ QStringList lst=QStringList::split( QChar( 0xE000 ) , QString::fromUtf8(event->encodedData ( "kopete/x-contact" )) );
+ if(m_manager->mayInvite() && m_manager->protocol()->pluginId() == lst[0] && m_manager->account()->accountId() == lst[1])
+ {
+ QString contact=lst[2];
+
+ bool found =false;
+ QPtrList<Kopete::Contact> cts=m_manager->members();
+ for ( QPtrListIterator<Kopete::Contact> it( cts ); it.current(); ++it )
+ {
+ if(it.current()->contactId() == contact)
+ {
+ found=true;
+ break;
+ }
+ }
+ if(!found && contact != m_manager->myself()->contactId())
+ m_manager->inviteContact(contact);
+ }
+ }
+ else if( event->provides( "kopete/x-metacontact" ) )
+ {
+ QString metacontactID=QString::fromUtf8(event->encodedData ( "kopete/x-metacontact" ));
+ Kopete::MetaContact *m=Kopete::ContactList::self()->metaContact(metacontactID);
+ if(m && m_manager->mayInvite())
+ {
+ QPtrList<Kopete::Contact> cts=m->contacts();
+ for ( QPtrListIterator<Kopete::Contact> it( cts ); it.current(); ++it )
+ {
+ Kopete::Contact *c=it.current();
+ if(c && c->account() == m_manager->account() && c->isOnline())
+ {
+ if( c != m_manager->myself() && !m_manager->members().contains(c) )
+ m_manager->inviteContact(c->contactId());
+ }
+ }
+ }
+ }
+ else if ( event->provides( "text/uri-list" ) && m_manager->members().count() == 1 )
+ {
+ Kopete::ContactPtrList members = m_manager->members();
+ Kopete::Contact *contact = members.first();
+
+ if ( !contact || !contact->canAcceptFiles() || !QUriDrag::canDecode( event ) )
+ {
+ event->ignore();
+ return;
+ }
+
+ KURL::List urlList;
+ KURLDrag::decode( event, urlList );
+
+ for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it )
+ {
+ if ( (*it).isLocalFile() )
+ { //send a file
+ contact->sendFile( *it );
+ }
+ else
+ { //this is a URL, send the URL in a message
+ addText( (*it).url() );
+ }
+ }
+ event->acceptAction();
+ return;
+ }
+ else
+ KDockMainWindow::dropEvent(event);
+
+}
+
+void ChatView::registerContextMenuHandler( QObject *target, const char* slot )
+{
+ connect( m_messagePart,
+ SIGNAL( contextMenuEvent( Kopete::Message &, const QString &, KPopupMenu * ) ),
+ target,
+ slot
+ );
+}
+
+void ChatView::registerTooltipHandler( QObject *target, const char* slot )
+{
+ connect( m_messagePart,
+ SIGNAL( tooltipEvent( Kopete::Message &, const QString &, QString & ) ),
+ target,
+ slot
+ );
+}
+
+#include "chatview.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chatview.h b/kopete/kopete/chatwindow/chatview.h
new file mode 100644
index 00000000..ef10320b
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatview.h
@@ -0,0 +1,421 @@
+/*
+ chatview.h - Chat View
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATVIEW_H
+#define CHATVIEW_H
+
+#include "kopeteview.h"
+#include "kopeteviewplugin.h"
+#include <kdockwidget.h>
+#include <ktextedit.h> // for covariant return type of editWidget
+#include <qptrdict.h>
+
+class QTimer;
+
+class ChatTextEditPart;
+class ChatMembersListWidget;
+class ChatMessagePart;
+
+class KopeteChatWindow;
+
+class KTabWidget;
+
+class KopeteChatViewPrivate;
+class ChatWindowPlugin;
+
+namespace KParts
+{
+ class Part;
+}
+
+namespace Kopete
+{
+ class Contact;
+ class ChatSession;
+}
+
+/**
+ * @author Olivier Goffart
+ */
+class ChatView : public KDockMainWindow, public KopeteView
+{
+ Q_OBJECT
+public:
+ ChatView( Kopete::ChatSession *manager, ChatWindowPlugin *parent, const char *name = 0 );
+ ~ChatView();
+
+ /** the state of our chat */
+ enum KopeteTabState { Normal, Highlighted, Changed, Typing, Message, Undefined };
+
+ ChatMembersListWidget *membersList() const { return m_membersList; }
+ ChatMessagePart *messagePart() const { return m_messagePart; }
+ ChatTextEditPart *editPart() const { return m_editPart; }
+
+ /**
+ * Adds text into the edit area. Used when you select an emoticon
+ * @param text The text to be inserted
+ */
+ void addText( const QString &text );
+
+ /**
+ * Saves window settings such as splitter positions
+ */
+ void saveOptions();
+
+ /**
+ * Tells this view it is the active view
+ */
+ void setActive( bool value );
+
+ /**
+ * save the chat settings (rich text, auto spelling)
+ */
+ void saveChatSettings();
+
+ /**
+ * read the chat settings (rich text, auto spelling)
+ */
+ void loadChatSettings();
+
+ /**
+ * Clears the chat buffer
+ *
+ * Reimplemented from KopeteView
+ */
+ virtual void clear();
+
+ /**
+ * Sets the text to be displayed on tab label and window caption
+ */
+ void setCaption( const QString &text, bool modified );
+
+ /**
+ * Changes the pointer to the chat window. Used to re-parent the view
+ * @param parent The new chat window
+ */
+ void setMainWindow( KopeteChatWindow* parent );
+
+ /**
+ * Returns the message currently in the edit area
+ * @return The Kopete::Message object for the message
+ *
+ * Reimplemented from KopeteView
+ */
+ virtual Kopete::Message currentMessage();
+
+ /**
+ * Sets the current message in the chat window
+ * @param parent The new chat window
+ *
+ * Reimplemented from KopeteView
+ */
+ virtual void setCurrentMessage( const Kopete::Message &newMessage );
+
+ /**
+ * Sets the placement of the chat members list.
+ * DockLeft, DockRight, or DockNone.
+ * @param dp The dock position of the list
+ */
+ void placeMembersList( KDockWidget::DockPosition dp = KDockWidget::DockRight );
+
+ /**
+ * Shows or hides the chat members list
+ */
+ void toggleMembersVisibility();
+
+ /**
+ * Returns the chat window this view is in
+ * @return The chat window
+ */
+ KopeteChatWindow *mainWindow() const { return m_mainWindow; }
+
+ /**
+ * Returns the current position of the chat member slist
+ * @return The position of the chat members list
+ */
+ const KDockWidget::DockPosition membersListPosition() { return membersDockPosition; }
+
+ /**
+ * Returns whether or not the chat member list is visible
+ * @return Is the chat member list visible?
+ */
+ bool visibleMembersList();
+
+ const QString &statusText();
+
+ QString &caption() const;
+
+ bool sendInProgress();
+
+ /** Reimplemented from KopeteView **/
+ virtual void raise( bool activate=false );
+
+ /** Reimplemented from KopeteView **/
+ virtual void makeVisible();
+
+ /** Reimplemented from KopeteView **/
+ virtual bool isVisible();
+
+ /** Reimplemented from KopeteView **/
+ virtual QWidget *mainWidget();
+
+ KTextEdit *editWidget();
+
+ bool canSend();
+
+ /** Reimplemented from KopeteView **/
+ virtual void registerContextMenuHandler( QObject *target, const char* slot );
+
+ /** Reimplemented from KopeteView **/
+ virtual void registerTooltipHandler( QObject *target, const char* slot );
+
+public slots:
+ /**
+ * Initiates a cut action on the edit area of the chat view
+ */
+ void cut();
+
+ /**
+ * Initiates a copy action
+ * If there is text selected in the HTML view, that text is copied
+ * Otherwise, the entire edit area is copied.
+ */
+ void copy();
+
+ /**
+ * Initiates a paste action into the edit area of the chat view
+ */
+ void paste();
+
+ void nickComplete();
+
+ /**
+ * Sets the foreground color of the entry area, and outgoing messages
+ * @param newColor The new foreground color. If this is QColor(), then
+ * a color chooser dialog is opened
+ */
+ void setFgColor( const QColor &newColor = QColor() );
+
+ /**
+ * Sets the font of the edit area and outgoing messages to the specified value.
+ * @param newFont The new font to use.
+ */
+ void setFont( const QFont &newFont );
+
+ /**
+ * show a Font dialog and set the font selected by the user
+ */
+ void setFont();
+
+ /**
+ * Get the font used in the format toolbar for Rich Text formatting
+ */
+ QFont font();
+
+ /**
+ * Sets the background color of the entry area, and outgoing messages
+ * @param newColor The new background color. If this is QColor(), then
+ * a color chooser dialog is opened
+ */
+ void setBgColor( const QColor &newColor = QColor() );
+
+ /**
+ * Sends the text currently entered into the edit area
+ */
+ virtual void sendMessage();
+
+ /**
+ * Called when a message is received from someone
+ * @param message The message received
+ */
+ virtual void appendMessage( Kopete::Message &message );
+
+ /**
+ * Called when a typing event is received from a contact
+ * Updates the typing map and outputs the typing message into the status area
+ * @param contact The contact who is / isn't typing
+ * @param typing If the contact is typing now
+ */
+ void remoteTyping( const Kopete::Contact *contact, bool typing );
+
+ /**
+ * Sets the text to be displayed on the status label
+ * @param text The text to be displayed
+ */
+ void setStatusText( const QString &text );
+
+ /** Reimplemented from KopeteView **/
+ virtual void messageSentSuccessfully();
+
+ virtual bool closeView( bool force = false );
+
+signals:
+ /**
+ * Emitted when a message is sent
+ * @param message The message sent
+ */
+ void messageSent( Kopete::Message & );
+
+ void messageSuccess( ChatView* );
+
+ /**
+ * Emits when the chat view is shown
+ */
+ void shown();
+
+ void closing( KopeteView* );
+
+ void activated( KopeteView* );
+
+ void captionChanged( bool active );
+
+ void updateStatusIcon( ChatView* );
+
+ /** Emitted when a possible tab tooltip needs updating */
+ void updateChatTooltip( ChatView*, const QString& );
+
+ /** Emitted when the state of the chat changes */
+ void updateChatState( ChatView*, int );
+
+ /** Emitted when a possible tab label needs updating */
+ void updateChatLabel( ChatView*, const QString& );
+
+ /**
+ * Our send-button-enabled flag has changed
+ */
+ void canSendChanged(bool);
+
+ /**
+ * Emitted when we re-parent ourselves with a new window
+ */
+ void windowCreated();
+
+ /**
+ * Emitted when the state of RTF has changed
+ */
+ void rtfEnabled( ChatView*, bool );
+
+ void autoSpellCheckEnabled( ChatView*, bool );
+
+private slots:
+ void slotRemoteTypingTimeout();
+ /**
+ * Show that a contact changed his nickname when a metacontact is not avaiable.
+ */
+ void slotPropertyChanged( Kopete::Contact *contact, const QString &key, const QVariant &oldValue, const QVariant &newValue );
+
+ /**
+ * Called when a contact is added to the chat session.
+ * Adds this contact to the typingMap and the contact list view
+ * @param c The contact that joined the chat
+ * @param suppress mean that no notifications are showed
+ */
+ void slotContactAdded( const Kopete::Contact *c, bool suppress );
+
+ /**
+ * Called when a contact is removed from the chat session. Updates the tab state and status icon,
+ * displays a notification message and performs some cleanup.
+ * @param c The contact left the chat
+ * @param reason is the reason the contact left
+ * @param format The format of the reason message
+ * @param suppressNotification mean that no notifications are showed
+ */
+ void slotContactRemoved( const Kopete::Contact *c, const QString& reason, Kopete::Message::MessageFormat format, bool suppressNotification=false );
+
+ /**
+ * Called when a contact changes status, updates the display name, status icon and tab bar state.
+ * If the user isn't changing to/from an Unknown status, will also display a message in the chatwindow.
+ * @param contact The contact who changed status
+ * @param status The new status of the contact
+ * @param oldstatus The former status of the contact
+ */
+ void slotContactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldstatus );
+
+ /**
+ * Called when the chat's display name is changed
+ */
+ void slotChatDisplayNameChanged();
+
+ void slotMarkMessageRead();
+
+ void slotToggleRtfToolbar( bool enabled );
+
+ /**
+ * Show that a (meta)contact change his display name.
+ */
+ void slotDisplayNameChanged(const QString &oldValue, const QString &newValue);
+
+protected:
+ virtual void dragEnterEvent ( QDragEnterEvent * );
+ virtual void dropEvent ( QDropEvent * );
+
+private:
+ // widget stuff
+ KopeteChatWindow *m_mainWindow;
+
+ KDockWidget *viewDock;
+ ChatMessagePart *m_messagePart;
+
+ KDockWidget *membersDock;
+ ChatMembersListWidget *m_membersList;
+
+ KDockWidget *editDock;
+ ChatTextEditPart *m_editPart;
+
+ KopeteTabState m_tabState;
+
+ // position and visibility of the chat member list
+ KDockWidget::DockPosition membersDockPosition;
+ enum MembersListPolicy { Smart = 0, Visible = 1, Hidden = 2 };
+ MembersListPolicy membersStatus;
+
+ // miscellany
+ QPtrDict<QTimer> m_remoteTypingMap;
+ QString unreadMessageFrom;
+ QString m_status;
+
+ void updateChatState( KopeteTabState state = Undefined );
+
+ /**
+ * Creates the members list widget
+ */
+ void createMembersList();
+
+ /**
+ * Read in saved options, such as splitter positions
+ */
+ void readOptions();
+
+ void sendInternalMessage( const QString &msg, Kopete::Message::MessageFormat format = Kopete::Message::PlainText );
+
+ KopeteChatViewPrivate *d;
+};
+
+/**
+ * This is the class that makes the chatwindow a plugin
+ */
+class ChatWindowPlugin : public Kopete::ViewPlugin
+{
+ public:
+ ChatWindowPlugin(QObject *parent, const char *name, const QStringList &args);
+ KopeteView* createView( Kopete::ChatSession *manager );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/chatwindow.desktop b/kopete/kopete/chatwindow/chatwindow.desktop
new file mode 100644
index 00000000..fa1b4f92
--- /dev/null
+++ b/kopete/kopete/chatwindow/chatwindow.desktop
@@ -0,0 +1,126 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kopete/Plugin
+X-Kopete-Version=1000900
+X-KDE-Library=kopete_chatwindow
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_chatwindow
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Views
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+Name=Kopete Chat Window
+Name[ar]=ناÙذة محادثة Kopete
+Name[be]=Вакно гутаркі Kopete
+Name[bg]=Прозорец за разговори
+Name[bn]=কপেট চà§à¦¯à¦¾à¦Ÿ উইনà§à¦¡à§‹
+Name[br]=Prenestr flapañ Kopete
+Name[bs]=Kopete chat prozor
+Name[ca]=Finestra de xat del Kopete
+Name[cs]=Okno rozhovoru Kopete
+Name[cy]=Ffenestr Sgwrs Kopete
+Name[da]=Kopete Chat-vindue
+Name[de]=Kopete-Chat-Fenster
+Name[el]=ΠαÏάθυÏο συνομιλίας Kopete
+Name[es]=Ventana de charla de Kopete
+Name[et]=Kopete vestlusaken
+Name[eu]=Kopete elkarrizketa leihoa
+Name[fa]=پنجرۀ گپ Kopete
+Name[fi]=Kopeten keskusteluikkuna
+Name[fr]=Fenêtre de discussion de Kopete
+Name[ga]=Fuinneog Chomhrá Kopete
+Name[gl]=Fiestra de conversa de Kopete
+Name[he]=חלון שיחה של Kopete
+Name[hi]=के-ऑपà¥à¤Ÿà¥€ गपशप विंडो
+Name[hr]=Kopeteov prozor za razgovor
+Name[hu]=Kopete csevegési ablak
+Name[is]=Spjallgluggi Kopete
+Name[it]=Finestra di chat di Kopete
+Name[ja]=Kopete ãƒãƒ£ãƒƒãƒˆã‚¦ã‚£ãƒ³ãƒ‰ã‚¦
+Name[ka]=Kopete სáƒáƒ£áƒ‘რის ფáƒáƒœáƒ¯áƒáƒ áƒ
+Name[kk]=Kopete әңгіме-дүкен терезеÑÑ–
+Name[km]=បង្អួច​ជជែក​កំសាន្ហKopete
+Name[lt]=Kopete pokalbių langas
+Name[mk]=Прозорец за муабет од Kopete
+Name[nb]=Kopete pratevindu
+Name[nds]=Kopete-Klöönfinster
+Name[ne]=कोपेट कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ सञà¥à¤à¥à¤¯à¤¾à¤²
+Name[nl]=Kopete gespreksvenster
+Name[nn]=Kopete pratevindauge
+Name[pa]=ਕੋਪੀਟੀ ਗੱਲਾਂਬਾਤਾਂ
+Name[pl]=Okno rozmowy Kopete
+Name[pt]=Janela de Conversão do Kopete
+Name[pt_BR]=Janela de Bate-papo do Kopete
+Name[ro]=Fereastră de discuţii Kopete
+Name[ru]=Разговор
+Name[se]=Kopete Äáttenláse
+Name[sk]=Okno rozhovoru Kopete
+Name[sl]=Okno za klepet v Kopete
+Name[sr]=Kopete-ов прозор за ћаÑкање
+Name[sr@Latn]=Kopete-ov prozor za ćaskanje
+Name[sv]=Kopete-chattfönster
+Name[ta]=Kopete அரடà¯à®Ÿà¯ˆ சாளரமà¯
+Name[tg]=Тирезаи Чати Kopete
+Name[tr]=Kopete Sohbet Penceresi
+Name[uk]=Вікно розмови Kopete
+Name[zh_CN]=Kopete èŠå¤©çª—å£
+Name[zh_HK]=Kopete èŠå¤©è¦–窗
+Name[zh_TW]=Kopete èŠå¤©è¦–窗
+Comment=The default Kopete chat window
+Comment[ar]=ناÙذة محادثة Kopete ألاÙتراضية
+Comment[be]=Прадвызначанае вакно гутаркі Kopete
+Comment[bg]=Стандартен прозорец за разговори
+Comment[bn]=ডিফলà§à¦Ÿ কপেট চà§à¦¯à¦¾à¦Ÿ উইনà§à¦¡à§‹
+Comment[bs]=Osnovni Kopete chat prozor
+Comment[ca]=La finestra de xat per omissió del Kopete
+Comment[cs]=Výchozí okno Kopete pro rozhovor
+Comment[cy]=Y ffenestr sgwrs Kopete rhagosod
+Comment[da]=Kopete's standard-chatvindue
+Comment[de]=Das übliche Chat-Fenster für Kopete
+Comment[el]=Το Ï€ÏοκαθοÏισμένο παÏάθυÏο συνομιλίας του Kopete
+Comment[es]=La ventana de charla predeterminada de Kopete
+Comment[et]=Kopete vaikimisi vestlusaken
+Comment[eu]=Kopete elkarrizketa leiho lehenetsia
+Comment[fa]=پنجرۀ پیش‌Ùرض Ú¯Ù¾ Kopete
+Comment[fi]=Kopeten oletuskeskusteluikkuna
+Comment[fr]=La fenêtre de discussion par défaut de Kopete
+Comment[ga]=Fuinneog chomhrá réamhshocruithe Kopete
+Comment[gl]=A fiestra de conversa por defecto de Kopete
+Comment[he]=ברירת מחדל עבור חלון השיחה של Kopete
+Comment[hi]=डिफ़ॉलà¥à¤Ÿ के-ऑपà¥à¤Ÿà¥€ गपशप विंडो
+Comment[hr]=UobiÄajeni Kopeteov prozor za razgovor
+Comment[hu]=Az alapértelmezett Kopete csevegési ablak
+Comment[is]=Sjálfgefni spjallgluggi Kopete
+Comment[it]=La finestra di chat predefinita di Kopete
+Comment[ja]=Kopete ã®æ¨™æº–ãƒãƒ£ãƒƒãƒˆã‚¦ã‚£ãƒ³ãƒ‰ã‚¦
+Comment[ka]=Kopeteს ნáƒáƒ’ულისხმები სáƒáƒ£áƒ‘რის ფáƒáƒœáƒ¯áƒáƒ áƒ
+Comment[kk]=Әдетті Kopete әңгіме-дүкен терезеÑÑ–
+Comment[km]=បង្អួច​ជជែក​កំសាន្ážâ€‹áž›áŸ†áž“ាំ​ដើម​របស់ Kopete
+Comment[lt]=Numatytas Kopete pokalbių langas
+Comment[mk]=Почетниот прозорец за муабет од Kopete
+Comment[nb]=Standardvinduet for Kopete nettprat
+Comment[nds]=Dat Standardklöönfinster vun Kopete
+Comment[ne]=पूरà¥à¤µà¤¾à¤¨à¤¿à¤°à¥à¤§à¤¾à¤°à¤¿à¤¤ कोपेट कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ सञà¥à¤à¥à¤¯à¤¾à¤²
+Comment[nl]=Het standaard Kopete gespreksvenster
+Comment[nn]=Standardvindauget for nettprat i Kopete
+Comment[pl]=Domyślne okno rozmowy Kopete
+Comment[pt]=A janela de conversação por omissão do Kopete
+Comment[pt_BR]=A janela de bate-papo padrão do Kopete
+Comment[ro]=Fereastra de discuţii implicită Kopete
+Comment[ru]=Разговор
+Comment[se]=Standárda Kopete-Äáttenláse
+Comment[sk]=Štandardné okno rozhovoru pre Kopete
+Comment[sl]=Privzeto okno za klepet v Kopete
+Comment[sr]=Подразумевани Kopete-ов прозор за ћаÑкање
+Comment[sr@Latn]=Podrazumevani Kopete-ov prozor za ćaskanje
+Comment[sv]=Kopetes vanliga chattfönster
+Comment[ta]=à®®à¯à®©à¯à®©à®¿à®°à¯à®ªà¯à®ªà¯ செயறà¯à®ªà®Ÿà¯à®Ÿà¯ˆà®ªà¯ பலகக௠கà¯à®±à¯à®¨à®¿à®°à®²à¯
+Comment[tg]=Тирезаи Чат бо нобаёнии Kopete
+Comment[tr]=Varsayılan Kopete Sohbet Penceresi
+Comment[uk]=Типове вікно розмови Kopete
+Comment[zh_CN]=默认的 Kopete èŠå¤©çª—å£
+Comment[zh_HK]=Kopete é è¨­çš„èŠå¤©è¦–窗
+Comment[zh_TW]=é è¨­ Kopete èŠå¤©è¦–窗
diff --git a/kopete/kopete/chatwindow/emailwindow.desktop b/kopete/kopete/chatwindow/emailwindow.desktop
new file mode 100644
index 00000000..ee71aea9
--- /dev/null
+++ b/kopete/kopete/chatwindow/emailwindow.desktop
@@ -0,0 +1,111 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Kopete/Plugin
+X-Kopete-Version=1000900
+X-KDE-Library=kopete_emailwindow
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_emailwindow
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Views
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+Name=Kopete Email Window
+Name[be]=Вакно Ñлектроннай пошты Kopete
+Name[bg]=Прозорец за е-поща
+Name[bn]=কপেট ই-মেইল উইনà§à¦¡à§‹
+Name[br]=Prenestr postel Kopete
+Name[bs]=Kopete e-mail prozor
+Name[ca]=Finestra de correu-e del Kopete
+Name[cs]=Emailové okno Kopete
+Name[da]=Kopete's e-mail-vindue
+Name[de]=Kopete-E-Mail-Fenster
+Name[el]=ΠαÏάθυÏο Email του Kopete
+Name[es]=Ventana de correo de Kopete
+Name[et]=Kopete e-posti aken
+Name[eu]=Kopete elkarrizketa leihoa
+Name[fa]=پنجرۀ رایانامۀ Kopete
+Name[fi]=Kopeten sähköposti-ikkuna
+Name[fr]=Fenêtre de courrier électronique Kopete
+Name[ga]=Fuinneog R-Phoist Kopete
+Name[gl]=Fiestra de Correo-e De Kopete
+Name[he]=חלון דו×"ל של Kopete
+Name[hu]=Kopete levelezési ablak
+Name[is]=Kopete póstgluggi
+Name[it]=Finestra dei messaggi di Kopete
+Name[ja]=Kopete Eメールウィンドウ
+Name[ka]=Kopeteს ელფáƒáƒ¡áƒ¢áƒ ფáƒáƒœáƒ¯áƒáƒ áƒ
+Name[kk]=Kopete Ñл.пошта терезеÑÑ–
+Name[km]=បង្អួច​អ៊ីមែល Kopete
+Name[lt]=Kopete el. pašto langas
+Name[nb]=Kopete e-postvindu
+Name[nds]=Kopete-Nettpostfinster
+Name[ne]=कोपेट इमेल सञà¥à¤à¥à¤¯à¤¾à¤²
+Name[nl]=Kopete e-mailvenster
+Name[nn]=Kopete e-postvindauge
+Name[pa]=ਕੋਪੀਟੀ ਈ-ਮੇਲ à¨à¨°à©‹à¨–ਾ
+Name[pl]=Okno e-mailowe Kopete
+Name[pt]=Janela de E-mail do Kopete
+Name[pt_BR]=Janela de Mensagens do Kopete
+Name[ro]=Fereastră de e-mail Kopete
+Name[ru]=Отдельные ÑообщениÑ
+Name[sk]=Kopete okno pošty
+Name[sl]=Okno za e-pošto v Kopete
+Name[sr]=Kopete-ов прозор за е-пошту
+Name[sr@Latn]=Kopete-ov prozor za e-poštu
+Name[sv]=Kopete e-postfönster
+Name[tr]=Kopete e-posta Penceresi
+Name[uk]=Вікно ел. пошти Kopete
+Name[zh_CN]=Kopete 电å­é‚®ä»¶çª—å£
+Name[zh_HK]=Kopete 電郵視窗
+Name[zh_TW]=Kopete é›»å­éƒµä»¶è¦–窗
+Comment=The Kopete email window
+Comment[be]=Вакно Ñлектроннай пошты Kopete
+Comment[bg]=Прозорец за изпращане на е-поща
+Comment[bn]=কপেট ই-মেইল উইনà§à¦¡à§‹
+Comment[br]=Prenestr postel Kopete
+Comment[bs]=Kopete e-mail prozor
+Comment[ca]=La finestra de correu-e del Kopete
+Comment[cs]=Emailové okno Kopete
+Comment[da]=Kopete's e-mail-vindue
+Comment[de]=Das E-Mail-Fenster für Kopete
+Comment[el]=Το παÏάθυÏο email του Kopete
+Comment[es]=La ventana de correo de Kopete
+Comment[et]=Kopete e-posti aken
+Comment[eu]=Kopete elkarrizketa leiho lehenetsia
+Comment[fa]=پنجرۀ رایانامۀ Kopete
+Comment[fi]=Kopeten sähköposti-ikkuna
+Comment[fr]=La fenêtre de courrier électronique de Kopete
+Comment[ga]=An fhuinneog r-phoist Kopete
+Comment[gl]=A fiestra de correo-e de Kopete
+Comment[he]=ברירת מחדל עבור חלון הדו×"ל של Kopete
+Comment[hu]=A Kopete levelezési ablaka
+Comment[is]=Póstgluggi Kopete
+Comment[it]=La finestra dei messaggi di Kopete
+Comment[ja]=Kopete Eメールウィンドウ
+Comment[ka]=Kopeteს ელფáƒáƒ¡áƒ¢áƒ˜áƒ¡ ფáƒáƒœáƒ¯áƒáƒ áƒ
+Comment[kk]=Kopete Ñл.пошта терезеÑÑ–
+Comment[km]=បង្អួច​អ៊ីមែល​របស់ Kopete
+Comment[lt]=Kopete el. pašto langas
+Comment[nb]=Kopetes e-postvindu
+Comment[nds]=Dat Kopete-Nettpostfinster
+Comment[ne]=कोपेट इमेल सञà¥à¤à¥à¤¯à¤¾à¤²
+Comment[nl]=Het standaard Kopete gespreksvenster
+Comment[nn]=E-postvindauget i Kopete
+Comment[pl]=Okno e-maila w Kopete
+Comment[pt]=A janela de e-mail do Kopete
+Comment[pt_BR]=A janela de mensagens do Kopete
+Comment[ro]=Fereastra de e-mail Kopete
+Comment[ru]=Отдельные ÑообщениÑ
+Comment[sk]=Okno pošty pre Kopete
+Comment[sl]=Okno za e-pošto v Kopete
+Comment[sr]=Подразумевани Kopete-ов прозор за е-пошту
+Comment[sr@Latn]=Podrazumevani Kopete-ov prozor za e-poštu
+Comment[sv]=Kopetes e-postfönster
+Comment[tr]=Kopete e-posta Penceresi
+Comment[uk]=Вікно ел. пошти Kopete
+Comment[zh_CN]=Kopete 电å­é‚®ä»¶çª—å£
+Comment[zh_HK]=Kopete 電郵視窗
+Comment[zh_TW]=Kopete é›»å­éƒµä»¶è¦–窗
diff --git a/kopete/kopete/chatwindow/emoticonselector.cpp b/kopete/kopete/chatwindow/emoticonselector.cpp
new file mode 100644
index 00000000..e6802b45
--- /dev/null
+++ b/kopete/kopete/chatwindow/emoticonselector.cpp
@@ -0,0 +1,141 @@
+/*
+ emoticonselector.cpp
+
+ a button that pops up a list of all emoticons and returns
+ the emoticon-string if one is selected in the list
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "emoticonselector.h"
+#include "kopeteemoticons.h"
+
+#include <math.h>
+
+#include <qmovie.h>
+#include <qlayout.h>
+#include <qobjectlist.h>
+#include <qtooltip.h>
+#include <qobjectlist.h>
+
+#include <kdebug.h>
+
+EmoticonLabel::EmoticonLabel(const QString &emoticonText, const QString &pixmapPath, QWidget *parent, const char *name)
+ : QLabel(parent,name)
+{
+ mText = emoticonText;
+ setMovie( QMovie(pixmapPath) );
+ setAlignment(Qt::AlignCenter);
+ QToolTip::add(this,emoticonText);
+ // Somehow QLabel doesn't tell a reasonable size when you use setMovie
+ // although it does it correctly for setPixmap. Therefore here is a little workaround
+ // to tell our minimum size.
+ QPixmap p(pixmapPath);
+ //
+ // Some of the custom icons are rather large
+ // so lets limit them to a maximum size for this display panel
+ //
+ if (p.width() > 32 || p.height() > 32)
+ p.resize(32, 32);
+ setMinimumSize(p.size());
+}
+
+void EmoticonLabel::mouseReleaseEvent(QMouseEvent*)
+{
+ emit clicked(mText);
+}
+
+EmoticonSelector::EmoticonSelector(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+// kdDebug(14000) << k_funcinfo << "called." << endl;
+ lay = 0L;
+}
+
+void EmoticonSelector::prepareList(void)
+{
+// kdDebug(14000) << k_funcinfo << "called." << endl;
+ int row = 0;
+ int col = 0;
+ QMap<QString, QStringList> list = Kopete::Emoticons::self()->emoticonAndPicList();
+ int emoticonsPerRow = static_cast<int>(sqrt(list.count()));
+ //kdDebug(14000) << "emoticonsPerRow=" << emoticonsPerRow << endl;
+
+ if ( lay )
+ {
+ QObjectList *objList = queryList( "EmoticonLabel" );
+ //kdDebug(14000) << k_funcinfo << "There are " << objList->count() << " EmoticonLabels to delete." << endl;
+ objList->setAutoDelete(true);
+ objList->clear();
+ delete objList;
+ delete lay;
+ }
+
+ lay = new QGridLayout(this, 0, 0, 4, 4, "emoticonLayout");
+ movieList.clear();
+ for (QMap<QString, QStringList>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
+ {
+ QWidget *w = new EmoticonLabel(it.data().first(), it.key(), this);
+ movieList.push_back( ((QLabel*)w)->movie() );
+ connect(w, SIGNAL(clicked(const QString&)), this, SLOT(emoticonClicked(const QString&)));
+// kdDebug(14000) << "adding Emoticon to row=" << row << ", col=" << col << "." << endl;
+ lay->addWidget(w, row, col);
+ if ( col == emoticonsPerRow )
+ {
+ col = 0;
+ row++;
+ }
+ else
+ col++;
+ }
+ resize(minimumSizeHint());
+}
+
+void EmoticonSelector::emoticonClicked(const QString &str)
+{
+// kdDebug(14000) << "selected emoticon '" << str << "'" << endl;
+ // KDE4/Qt TODO: use qobject_cast instead.
+ emit ItemSelected ( str );
+ if ( isVisible() && parentWidget() &&
+ parentWidget()->inherits("QPopupMenu") )
+ {
+ parentWidget()->close();
+ }
+}
+
+void EmoticonSelector::hideEvent( QHideEvent* )
+{
+ kdDebug( 14000 ) << k_funcinfo << endl;
+ MovieList::iterator it;
+ for( it = movieList.begin(); it != movieList.end(); ++it )
+ {
+ (*it)->pause();
+ }
+}
+
+void EmoticonSelector::showEvent( QShowEvent* )
+{
+ kdDebug( 14000 ) << k_funcinfo << endl;
+ MovieList::iterator it;
+ for( it = movieList.begin(); it != movieList.end(); ++it )
+ {
+ (*it)->unpause();
+ }
+}
+
+#include "emoticonselector.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/emoticonselector.h b/kopete/kopete/chatwindow/emoticonselector.h
new file mode 100644
index 00000000..7d7b4842
--- /dev/null
+++ b/kopete/kopete/chatwindow/emoticonselector.h
@@ -0,0 +1,76 @@
+/*
+ emoticonselector.h
+
+ a button that pops up a list of all emoticons and returns
+ the emoticon-string if one is selected in the list
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __emoticonselector_h__
+#define __emoticonselector_h__
+
+#include <qlabel.h>
+#include <qwidget.h>
+class QGridLayout;
+class QHideEvent;
+class QShowEvent;
+
+class EmoticonLabel : public QLabel
+{
+ Q_OBJECT
+
+public:
+ EmoticonLabel(const QString &emoticonText, const QString &pixmapPath, QWidget *parent=0, const char *name=0);
+// ~EmoticonLabel();
+
+signals:
+ void clicked(const QString &text);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent*);
+ QString mText;
+};
+
+class EmoticonSelector : public QWidget
+{
+ Q_OBJECT
+
+public:
+
+ EmoticonSelector ( QWidget *parent = 0, const char *name = 0 );
+// ~EmoticonSelector();
+
+ typedef QValueList<QMovie*> MovieList;
+signals:
+ /**
+ * gets emitted when an emoticon has been selected from the list
+ * the QString holds the emoticon as a string or is 0L if nothing was selected
+ **/
+ void ItemSelected(const QString &);
+
+public slots:
+ void prepareList();
+
+protected:
+ virtual void hideEvent( QHideEvent* );
+ virtual void showEvent( QShowEvent* );
+ MovieList movieList;
+ QGridLayout *lay;
+
+protected slots:
+ void emoticonClicked(const QString &);
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/chatwindow/kopetechatwindow.cpp b/kopete/kopete/chatwindow/kopetechatwindow.cpp
new file mode 100644
index 00000000..2af1426d
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindow.cpp
@@ -0,0 +1,1280 @@
+/*
+ kopetechatwindow.cpp - Chat Window
+
+ Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2003-2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (C) 2002 by James Grant
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2004 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qfileinfo.h>
+
+#include <kapplication.h>
+#include <kcursor.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kconfig.h>
+#include <kpopupmenu.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kwin.h>
+#include <ktempfile.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kstatusbar.h>
+#include <kpushbutton.h>
+#include <ktabwidget.h>
+#include <kstandarddirs.h>
+#include <kdialog.h>
+#include <kstringhandler.h>
+#include <ksqueezedtextlabel.h>
+#include <kstdaccel.h>
+#include <kglobalsettings.h>
+
+#include "chatmessagepart.h"
+#include "chattexteditpart.h"
+#include "chatview.h"
+#include "kopeteapplication.h"
+#include "kopetechatwindow.h"
+#include "kopeteemoticonaction.h"
+#include "kopetegroup.h"
+#include "kopetechatsession.h"
+#include "kopetemetacontact.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopeteprotocol.h"
+#include "kopetestdaction.h"
+#include "kopeteviewmanager.h"
+
+#include <qtoolbutton.h>
+#include <kactionclasses.h>
+
+typedef QMap<Kopete::Account*,KopeteChatWindow*> AccountMap;
+typedef QMap<Kopete::Group*,KopeteChatWindow*> GroupMap;
+typedef QMap<Kopete::MetaContact*,KopeteChatWindow*> MetaContactMap;
+typedef QPtrList<KopeteChatWindow> WindowList;
+
+namespace
+{
+ AccountMap accountMap;
+ GroupMap groupMap;
+ MetaContactMap mcMap;
+ WindowList windows;
+}
+
+KopeteChatWindow *KopeteChatWindow::window( Kopete::ChatSession *manager )
+{
+ bool windowCreated = false;
+ KopeteChatWindow *myWindow;
+
+ //Take the first and the first? What else?
+ Kopete::Group *group = 0L;
+ Kopete::ContactPtrList members = manager->members();
+ Kopete::MetaContact *metaContact = members.first()->metaContact();
+
+ if ( metaContact )
+ {
+ Kopete::GroupList gList = metaContact->groups();
+ group = gList.first();
+ }
+
+ switch( KopetePrefs::prefs()->chatWindowPolicy() )
+ {
+ case GROUP_BY_ACCOUNT: //Open chats from the same protocol in the same window
+ if( accountMap.contains( manager->account() ) )
+ myWindow = accountMap[ manager->account() ];
+ else
+ windowCreated = true;
+ break;
+
+ case GROUP_BY_GROUP: //Open chats from the same group in the same window
+ if( group && groupMap.contains( group ) )
+ myWindow = groupMap[ group ];
+ else
+ windowCreated = true;
+ break;
+
+ case GROUP_BY_METACONTACT: //Open chats from the same metacontact in the same window
+ if( mcMap.contains( metaContact ) )
+ myWindow = mcMap[ metaContact ];
+ else
+ windowCreated = true;
+ break;
+
+ case GROUP_ALL: //Open all chats in the same window
+ if( windows.isEmpty() )
+ windowCreated = true;
+ else
+ {
+ //Here we are finding the window with the most tabs and
+ //putting it there. Need this for the cases where config changes
+ //midstream
+
+ int viewCount = -1;
+ for ( KopeteChatWindow *thisWindow = windows.first(); thisWindow; thisWindow = windows.next() )
+ {
+ if( thisWindow->chatViewCount() > viewCount )
+ {
+ myWindow = thisWindow;
+ viewCount = thisWindow->chatViewCount();
+ }
+ }
+ }
+ break;
+
+ case NEW_WINDOW: //Open every chat in a new window
+ default:
+ windowCreated = true;
+ break;
+ }
+
+ if ( windowCreated )
+ {
+ myWindow = new KopeteChatWindow();
+
+ if ( !accountMap.contains( manager->account() ) )
+ accountMap.insert( manager->account(), myWindow );
+
+ if ( !mcMap.contains( metaContact ) )
+ mcMap.insert( metaContact, myWindow );
+
+ if ( group && !groupMap.contains( group ) )
+ groupMap.insert( group, myWindow );
+ }
+
+// kdDebug( 14010 ) << k_funcinfo << "Open Windows: " << windows.count() << endl;
+
+ return myWindow;
+}
+
+KopeteChatWindow::KopeteChatWindow( QWidget *parent, const char* name )
+ : KParts::MainWindow( parent, name )
+{
+ m_activeView = 0L;
+ m_popupView = 0L;
+ backgroundFile = 0L;
+ updateBg = true;
+ m_tabBar = 0L;
+
+ initActions();
+
+ QVBox *vBox = new QVBox( this );
+ vBox->setLineWidth( 0 );
+ vBox->setSpacing( 0 );
+ vBox->setFrameStyle( QFrame::NoFrame );
+ // set default window size. This could be removed by fixing the size hints of the contents
+ resize( 500, 500 );
+ setCentralWidget( vBox );
+
+ mainArea = new QFrame( vBox );
+ mainArea->setLineWidth( 0 );
+ mainArea->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
+ mainLayout = new QVBoxLayout( mainArea );
+
+ if ( KopetePrefs::prefs()->chatWShowSend() )
+ {
+ //Send Button
+ m_button_send = new KPushButton( i18n("Send"), statusBar() );
+ m_button_send->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) );
+ m_button_send->setEnabled( false );
+ m_button_send->setFont( statusBar()->font() );
+ m_button_send->setFixedHeight( statusBar()->sizeHint().height() );
+ connect( m_button_send, SIGNAL( clicked() ), this, SLOT( slotSendMessage() ) );
+ statusBar()->addWidget( m_button_send, 0, true );
+ }
+ else
+ m_button_send = 0L;
+
+ m_status_text = new KSqueezedTextLabel( i18n("Ready."), statusBar(), "m_status_text" );
+ m_status_text->setAlignment( AlignLeft | AlignVCenter );
+ m_status_text->setFont( statusBar()->font() );
+ m_status_text->setFixedHeight( statusBar()->sizeHint().height() );
+ statusBar()->addWidget( m_status_text, 1 );
+
+ readOptions();
+ setWFlags( Qt::WDestructiveClose );
+
+ windows.append( this );
+ windowListChanged();
+
+ KGlobal::config()->setGroup( QString::fromLatin1("ChatWindowSettings") );
+ m_alwaysShowTabs = KGlobal::config()->readBoolEntry( QString::fromLatin1("AlwaysShowTabs"), false );
+ m_showFormatToolbar = KGlobal::config()->readBoolEntry( QString::fromLatin1("Show Format Toolbar"), true );
+ adjustingFormatToolbar = false;
+// kdDebug( 14010 ) << k_funcinfo << "Open Windows: " << windows.count() << endl;
+ kapp->ref();
+}
+
+KopeteChatWindow::~KopeteChatWindow()
+{
+ kdDebug( 14010 ) << k_funcinfo << endl;
+
+ emit( closing( this ) );
+
+ for( AccountMap::Iterator it = accountMap.begin(); it != accountMap.end(); )
+ {
+ AccountMap::Iterator mayDeleteIt = it;
+ ++it;
+ if( mayDeleteIt.data() == this )
+ accountMap.remove( mayDeleteIt.key() );
+ }
+
+ for( GroupMap::Iterator it = groupMap.begin(); it != groupMap.end(); )
+ {
+ GroupMap::Iterator mayDeleteIt = it;
+ ++it;
+ if( mayDeleteIt.data() == this )
+ groupMap.remove( mayDeleteIt.key() );
+ }
+
+ for( MetaContactMap::Iterator it = mcMap.begin(); it != mcMap.end(); )
+ {
+ MetaContactMap::Iterator mayDeleteIt = it;
+ ++it;
+ if( mayDeleteIt.data() == this )
+ mcMap.remove( mayDeleteIt.key() );
+ }
+
+ windows.remove( this );
+ windowListChanged();
+
+// kdDebug( 14010 ) << "Open Windows: " << windows.count() << endl;
+
+ saveOptions();
+
+ if( backgroundFile )
+ {
+ backgroundFile->close();
+ backgroundFile->unlink();
+ delete backgroundFile;
+ }
+
+ delete anim;
+ kapp->deref();
+}
+
+void KopeteChatWindow::windowListChanged()
+{
+ // update all windows' Move Tab to Window action
+ for ( QPtrListIterator<KopeteChatWindow> it( windows ); *it; ++it )
+ (*it)->checkDetachEnable();
+}
+
+void KopeteChatWindow::slotNickComplete()
+{
+ if( m_activeView )
+ m_activeView->nickComplete();
+}
+
+void KopeteChatWindow::slotTabContextMenu( QWidget *tab, const QPoint &pos )
+{
+ m_popupView = static_cast<ChatView*>( tab );
+
+ KPopupMenu *popup = new KPopupMenu;
+ popup->insertTitle( KStringHandler::rsqueeze( m_popupView->caption() ) );
+
+ actionContactMenu->plug( popup );
+ popup->insertSeparator();
+ actionTabPlacementMenu->plug( popup );
+ tabDetach->plug( popup );
+ actionDetachMenu->plug( popup );
+ tabClose->plug( popup );
+ popup->exec( pos );
+
+ delete popup;
+ m_popupView = 0;
+}
+
+ChatView *KopeteChatWindow::activeView()
+{
+ return m_activeView;
+}
+
+void KopeteChatWindow::initActions(void)
+{
+ KActionCollection *coll = actionCollection();
+
+ createStandardStatusBarAction();
+
+ chatSend = new KAction( i18n( "&Send Message" ), QString::fromLatin1( "mail_send" ), QKeySequence(Key_Return) ,
+ this, SLOT( slotSendMessage() ), coll, "chat_send" );
+ chatSend->setEnabled( false );
+
+ KStdAction::save ( this, SLOT(slotChatSave()), coll );
+ KStdAction::print ( this, SLOT(slotChatPrint()), coll );
+ KAction* quitAction = KStdAction::quit ( this, SLOT(close()), coll );
+ quitAction->setText( i18n("Close All Chats") );
+
+ tabClose = KStdAction::close ( this, SLOT(slotChatClosed()), coll, "tabs_close" );
+
+ tabRight=new KAction( i18n( "&Activate Next Tab" ), 0, KStdAccel::tabNext(),
+ this, SLOT( slotNextTab() ), coll, "tabs_right" );
+ tabLeft=new KAction( i18n( "&Activate Previous Tab" ), 0, KStdAccel::tabPrev(),
+ this, SLOT( slotPreviousTab() ), coll, "tabs_left" );
+ tabLeft->setEnabled( false );
+ tabRight->setEnabled( false );
+
+ nickComplete = new KAction( i18n( "Nic&k Completion" ), QString::null, 0, this, SLOT( slotNickComplete() ), coll , "nick_compete");
+ nickComplete->setShortcut( QKeySequence( Key_Tab ) );
+
+ tabDetach = new KAction( i18n( "&Detach Chat" ), QString::fromLatin1( "tab_breakoff" ), 0,
+ this, SLOT( slotDetachChat() ), coll, "tabs_detach" );
+ tabDetach->setEnabled( false );
+
+ actionDetachMenu = new KActionMenu( i18n( "&Move Tab to Window" ), QString::fromLatin1( "tab_breakoff" ), coll, "tabs_detachmove" );
+ actionDetachMenu->setDelayed( false );
+
+ connect ( actionDetachMenu->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotPrepareDetachMenu()) );
+ connect ( actionDetachMenu->popupMenu(), SIGNAL(activated(int)), this, SLOT(slotDetachChat(int)) );
+
+ actionTabPlacementMenu = new KActionMenu( i18n( "&Tab Placement" ), coll, "tabs_placement" );
+ connect ( actionTabPlacementMenu->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotPreparePlacementMenu()) );
+ connect ( actionTabPlacementMenu->popupMenu(), SIGNAL(activated(int)), this, SLOT(slotPlaceTabs(int)) );
+
+ tabDetach->setShortcut( QKeySequence(CTRL + SHIFT + Key_B) );
+
+ KStdAction::cut( this, SLOT(slotCut()), coll);
+ KStdAction::copy( this, SLOT(slotCopy()), coll);
+ KStdAction::paste( this, SLOT(slotPaste()), coll);
+
+ new KAction( i18n( "Set Default &Font..." ), QString::fromLatin1( "charset" ), 0, this, SLOT( slotSetFont() ), coll, "format_font" );
+ new KAction( i18n( "Set Default Text &Color..." ), QString::fromLatin1( "pencil" ), 0, this, SLOT( slotSetFgColor() ), coll, "format_fgcolor" );
+ new KAction( i18n( "Set &Background Color..." ), QString::fromLatin1( "fill" ), 0, this, SLOT( slotSetBgColor() ), coll, "format_bgcolor" );
+
+ historyUp = new KAction( i18n( "Previous History" ), QString::null, 0,
+ this, SLOT( slotHistoryUp() ), coll, "history_up" );
+ historyUp->setShortcut( QKeySequence(CTRL + Key_Up) );
+
+ historyDown = new KAction( i18n( "Next History" ), QString::null, 0,
+ this, SLOT( slotHistoryDown() ), coll, "history_down" );
+ historyDown->setShortcut( QKeySequence(CTRL + Key_Down) );
+
+ KStdAction::prior( this, SLOT( slotPageUp() ), coll, "scroll_up" );
+ KStdAction::next( this, SLOT( slotPageDown() ), coll, "scroll_down" );
+
+ KStdAction::showMenubar( this, SLOT(slotViewMenuBar()), coll );
+
+ membersLeft = new KToggleAction( i18n( "Place to Left of Chat Area" ), QString::null, 0,
+ this, SLOT( slotViewMembersLeft() ), coll, "options_membersleft" );
+ membersRight = new KToggleAction( i18n( "Place to Right of Chat Area" ), QString::null, 0,
+ this, SLOT( slotViewMembersRight() ), coll, "options_membersright" );
+ toggleMembers = new KToggleAction( i18n( "Show" ), QString::null, 0,
+ this, SLOT( slotToggleViewMembers() ), coll, "options_togglemembers" );
+ toggleMembers->setCheckedState(i18n("Hide"));
+ toggleAutoSpellCheck = new KToggleAction( i18n( "Automatic Spell Checking" ), QString::null, 0,
+ this, SLOT( toggleAutoSpellChecking() ), coll, "enable_auto_spell_check" );
+ toggleAutoSpellCheck->setChecked( true );
+
+ actionSmileyMenu = new KopeteEmoticonAction( coll, "format_smiley" );
+ actionSmileyMenu->setDelayed( false );
+ connect(actionSmileyMenu, SIGNAL(activated(const QString &)), this, SLOT(slotSmileyActivated(const QString &)));
+
+ actionContactMenu = new KActionMenu(i18n("Co&ntacts"), coll, "contacts_menu" );
+ actionContactMenu->setDelayed( false );
+ connect ( actionContactMenu->popupMenu(), SIGNAL(aboutToShow()), this, SLOT(slotPrepareContactMenu()) );
+
+ // add configure key bindings menu item
+ KStdAction::keyBindings( guiFactory(), SLOT( configureShortcuts() ), coll );
+
+ KStdAction::configureToolbars(this, SLOT(slotConfToolbar()), coll);
+ KopeteStdAction::preferences( coll , "settings_prefs" );
+
+ //The Sending movie
+ normalIcon = QPixmap( BarIcon( QString::fromLatin1( "kopete" ) ) );
+ animIcon = KGlobal::iconLoader()->loadMovie( QString::fromLatin1( "newmessage" ), KIcon::Toolbar);
+
+ // Pause the animation because otherwise it's running even when we're not
+ // showing it. This eats resources, and also triggers a pixmap leak in
+ // QMovie in at least Qt 3.1, Qt 3.2 and the current Qt 3.3 beta
+ if( !animIcon.isNull() ) //and another QT bug: it crash if we pause a null movie
+ animIcon.pause();
+
+ // we can't set the tool bar as parent, if we do, it will be deleted when we configure toolbars
+ anim = new QLabel( QString::null, 0L ,"kde toolbar widget" );
+ anim->setMargin(5);
+ anim->setPixmap( normalIcon );
+
+
+ new KWidgetAction( anim , i18n("Toolbar Animation") , 0, 0 , 0 , coll , "toolbar_animation");
+
+ //toolBar()->insertWidget( 99, anim->width(), anim );
+ //toolBar()->alignItemRight( 99 );
+ setStandardToolBarMenuEnabled( true );
+
+ setXMLFile( QString::fromLatin1( "kopetechatwindow.rc" ) );
+ createGUI( 0L );
+
+ // Special handling for remembering whether the format toolbar is visible or not
+ connect ( toolBar("formatToolBar"), SIGNAL(visibilityChanged(bool)), this, SLOT(slotToggleFormatToolbar(bool)) );
+}
+
+const QString KopeteChatWindow::fileContents( const QString &path ) const
+{
+ QString contents;
+ QFile file( path );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ QTextStream stream( &file );
+ contents = stream.read();
+ file.close();
+ }
+
+ return contents;
+}
+
+void KopeteChatWindow::slotStopAnimation( ChatView* view )
+{
+ if( view == m_activeView )
+ anim->setPixmap( normalIcon );
+}
+
+void KopeteChatWindow::slotUpdateSendEnabled()
+{
+ if ( !m_activeView ) return;
+
+ bool enabled = m_activeView->canSend();
+ chatSend->setEnabled( enabled );
+ if(m_button_send)
+ m_button_send->setEnabled( enabled );
+}
+
+void KopeteChatWindow::updateMembersActions()
+{
+ if( m_activeView )
+ {
+ const KDockWidget::DockPosition pos = m_activeView->membersListPosition();
+ bool visibleMembers = m_activeView->visibleMembersList();
+ membersLeft->setChecked( pos == KDockWidget::DockLeft );
+ membersLeft->setEnabled( visibleMembers );
+ membersRight->setChecked( pos == KDockWidget::DockRight );
+ membersRight->setEnabled( visibleMembers );
+ toggleMembers->setChecked( visibleMembers );
+ }
+}
+
+void KopeteChatWindow::slotViewMembersLeft()
+{
+ m_activeView->placeMembersList( KDockWidget::DockLeft );
+ updateMembersActions();
+}
+
+void KopeteChatWindow::slotViewMembersRight()
+{
+ m_activeView->placeMembersList( KDockWidget::DockRight );
+ updateMembersActions();
+}
+
+void KopeteChatWindow::slotToggleViewMembers()
+{
+ m_activeView->toggleMembersVisibility();
+ updateMembersActions();
+}
+
+void KopeteChatWindow::toggleAutoSpellChecking()
+{
+ if ( !m_activeView )
+ return;
+
+ bool currentSetting = m_activeView->editPart()->autoSpellCheckEnabled();
+ m_activeView->editPart()->toggleAutoSpellCheck( !currentSetting );
+ updateSpellCheckAction();
+}
+
+void KopeteChatWindow::updateSpellCheckAction()
+{
+ if ( !m_activeView )
+ return;
+
+ if ( m_activeView->editPart()->richTextEnabled() )
+ {
+ toggleAutoSpellCheck->setEnabled( false );
+ toggleAutoSpellCheck->setChecked( false );
+ m_activeView->editPart()->toggleAutoSpellCheck( false );
+ }
+ else
+ {
+ toggleAutoSpellCheck->setEnabled( true );
+ if ( KopetePrefs::prefs()->spellCheck() )
+ {
+ kdDebug(14000) << k_funcinfo << "spell check enabled" << endl;
+ toggleAutoSpellCheck->setChecked( true );
+ m_activeView->editPart()->toggleAutoSpellCheck(true);
+ }
+ else
+ {
+ kdDebug(14000) << k_funcinfo << "spell check disabled" << endl;
+ toggleAutoSpellCheck->setChecked( false );
+ m_activeView->editPart()->toggleAutoSpellCheck(false);
+ }
+ }
+}
+
+void KopeteChatWindow::slotHistoryUp()
+{
+ if( m_activeView )
+ m_activeView->editPart()->historyUp();
+}
+
+void KopeteChatWindow::slotHistoryDown()
+{
+ if( m_activeView )
+ m_activeView->editPart()->historyDown();
+}
+
+void KopeteChatWindow::slotPageUp()
+{
+ if( m_activeView )
+ m_activeView->messagePart()->pageUp();
+}
+
+void KopeteChatWindow::slotPageDown()
+{
+ if( m_activeView )
+ m_activeView->messagePart()->pageDown();
+}
+
+void KopeteChatWindow::slotCut()
+{
+ m_activeView->cut();
+}
+
+void KopeteChatWindow::slotCopy()
+{
+ m_activeView->copy();
+}
+
+void KopeteChatWindow::slotPaste()
+{
+ m_activeView->paste();
+}
+
+
+void KopeteChatWindow::slotSetFont()
+{
+ m_activeView->setFont();
+}
+
+void KopeteChatWindow::slotSetFgColor()
+{
+ m_activeView->setFgColor();
+}
+
+void KopeteChatWindow::slotSetBgColor()
+{
+ m_activeView->setBgColor();
+}
+
+void KopeteChatWindow::setStatus(const QString &text)
+{
+ m_status_text->setText(text);
+}
+
+void KopeteChatWindow::createTabBar()
+{
+ if( !m_tabBar )
+ {
+ KGlobal::config()->setGroup( QString::fromLatin1("ChatWindowSettings") );
+
+ m_tabBar = new KTabWidget( mainArea );
+ m_tabBar->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
+ m_tabBar->setHoverCloseButton(KGlobal::config()->readBoolEntry( QString::fromLatin1("HoverClose"), false ));
+ m_tabBar->setTabReorderingEnabled(true);
+#if KDE_IS_VERSION(3,4,0)
+ m_tabBar->setAutomaticResizeTabs(true);
+#endif
+ connect( m_tabBar, SIGNAL( closeRequest( QWidget* )), this, SLOT( slotCloseChat( QWidget* ) ) );
+
+ QToolButton* m_rightWidget = new QToolButton( m_tabBar );
+ connect( m_rightWidget, SIGNAL( clicked() ), this, SLOT( slotChatClosed() ) );
+ m_rightWidget->setIconSet( SmallIcon( "tab_remove" ) );
+ m_rightWidget->adjustSize();
+ QToolTip::add( m_rightWidget, i18n("Close the current tab"));
+ m_tabBar->setCornerWidget( m_rightWidget, QWidget::TopRight );
+
+ mainLayout->addWidget( m_tabBar );
+ m_tabBar->show();
+ connect ( m_tabBar, SIGNAL(currentChanged(QWidget *)), this, SLOT(setActiveView(QWidget *)) );
+ connect ( m_tabBar, SIGNAL(contextMenu(QWidget *, const QPoint & )), this, SLOT(slotTabContextMenu( QWidget *, const QPoint & )) );
+
+ for( ChatView *view = chatViewList.first(); view; view = chatViewList.next() )
+ addTab( view );
+
+ if( m_activeView )
+ m_tabBar->showPage( m_activeView );
+ else
+ setActiveView( chatViewList.first() );
+
+ int tabPosition = KGlobal::config()->readNumEntry( QString::fromLatin1("Tab Placement") , 0 );
+ slotPlaceTabs( tabPosition );
+ }
+}
+
+void KopeteChatWindow::slotCloseChat( QWidget *chatView )
+{
+ static_cast<ChatView*>( chatView )->closeView();
+}
+
+void KopeteChatWindow::addTab( ChatView *view )
+{
+ QPtrList<Kopete::Contact> chatMembers=view->msgManager()->members();
+ Kopete::Contact *c=0L;
+ for ( Kopete::Contact *contact = chatMembers.first(); contact; contact = chatMembers.next() )
+ {
+ if(!c || c->onlineStatus() < contact->onlineStatus())
+ c=contact;
+ }
+ QPixmap pluginIcon = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c) : SmallIcon( view->msgManager()->protocol()->pluginIcon() );
+
+ view->reparent( m_tabBar, 0, QPoint(), true );
+ m_tabBar->addTab( view, pluginIcon, view->caption() );
+ if( view == m_activeView )
+ view->show();
+ else
+ view->hide();
+ connect( view, SIGNAL( captionChanged( bool ) ), this, SLOT( updateChatLabel() ) );
+ connect( view, SIGNAL( updateStatusIcon( ChatView* ) ), this, SLOT( slotUpdateCaptionIcons( ChatView* ) ) );
+ view->setCaption( view->caption(), false );
+}
+
+void KopeteChatWindow::setPrimaryChatView( ChatView *view )
+{
+ //TODO figure out what else we have to save here besides the font
+ //reparent clears a lot of stuff out
+ QFont savedFont = view->font();
+ view->reparent( mainArea, 0, QPoint(), true );
+ view->setFont( savedFont );
+ view->show();
+
+ mainLayout->addWidget( view );
+ setActiveView( view );
+}
+
+void KopeteChatWindow::deleteTabBar()
+{
+ if( m_tabBar )
+ {
+ disconnect ( m_tabBar, SIGNAL(currentChanged(QWidget *)), this, SLOT(setActiveView(QWidget *)) );
+ disconnect ( m_tabBar, SIGNAL(contextMenu(QWidget *, const QPoint & )), this, SLOT(slotTabContextMenu( QWidget *, const QPoint & )) );
+
+ if( !chatViewList.isEmpty() )
+ setPrimaryChatView( chatViewList.first() );
+
+ m_tabBar->deleteLater();
+ m_tabBar = 0L;
+ }
+}
+
+void KopeteChatWindow::attachChatView( ChatView* newView )
+{
+ chatViewList.append( newView );
+
+ if ( !m_alwaysShowTabs && chatViewList.count() == 1 )
+ setPrimaryChatView( newView );
+ else
+ {
+ if ( !m_tabBar )
+ createTabBar();
+ else
+ addTab( newView );
+ newView->setActive( false );
+ }
+
+ newView->setMainWindow( this );
+ newView->editWidget()->installEventFilter( this );
+
+ KCursor::setAutoHideCursor( newView->editWidget(), true, true );
+ connect( newView, SIGNAL(captionChanged( bool)), this, SLOT(slotSetCaption(bool)) );
+ connect( newView, SIGNAL(messageSuccess( ChatView* )), this, SLOT(slotStopAnimation( ChatView* )) );
+ connect( newView, SIGNAL(rtfEnabled( ChatView*, bool ) ), this, SLOT( slotRTFEnabled( ChatView*, bool ) ) );
+ connect( newView, SIGNAL(updateStatusIcon( ChatView* ) ), this, SLOT(slotUpdateCaptionIcons( ChatView* ) ) );
+ connect( newView, SIGNAL(updateChatState( ChatView*, int ) ), this, SLOT( updateChatState( ChatView*, int ) ) );
+
+ updateSpellCheckAction();
+ checkDetachEnable();
+ newView->loadChatSettings();
+ connect( newView, SIGNAL(autoSpellCheckEnabled( ChatView*, bool ) ),
+ this, SLOT( slotAutoSpellCheckEnabled( ChatView*, bool ) ) );
+}
+
+void KopeteChatWindow::checkDetachEnable()
+{
+ bool haveTabs = (chatViewList.count() > 1);
+ tabDetach->setEnabled( haveTabs );
+ tabLeft->setEnabled( haveTabs );
+ tabRight->setEnabled( haveTabs );
+ actionTabPlacementMenu->setEnabled( m_tabBar != 0 );
+
+ bool otherWindows = (windows.count() > 1);
+ actionDetachMenu->setEnabled( otherWindows );
+}
+
+void KopeteChatWindow::detachChatView( ChatView *view )
+{
+ if( !chatViewList.removeRef( view ) )
+ return;
+
+ disconnect( view, SIGNAL(captionChanged( bool)), this, SLOT(slotSetCaption(bool)) );
+ disconnect( view, SIGNAL( updateStatusIcon( ChatView* ) ), this, SLOT( slotUpdateCaptionIcons( ChatView* ) ) );
+ disconnect( view, SIGNAL( updateChatState( ChatView*, int ) ), this, SLOT( updateChatState( ChatView*, int ) ) );
+ view->editWidget()->removeEventFilter( this );
+
+ if( m_tabBar )
+ {
+ int curPage = m_tabBar->currentPageIndex();
+ QWidget *page = m_tabBar->page( curPage );
+
+ // if the current view is to be detached, switch to a different one
+ if( page == view )
+ {
+ if( curPage > 0 )
+ m_tabBar->setCurrentPage( curPage - 1 );
+ else
+ m_tabBar->setCurrentPage( curPage + 1 );
+ }
+
+ m_tabBar->removePage( view );
+
+ if( m_tabBar->currentPage() )
+ setActiveView( static_cast<ChatView*>(m_tabBar->currentPage()) );
+ }
+
+ if( chatViewList.isEmpty() )
+ close();
+ else if( !m_alwaysShowTabs && chatViewList.count() == 1)
+ deleteTabBar();
+
+ checkDetachEnable();
+}
+
+void KopeteChatWindow::slotDetachChat( int newWindowIndex )
+{
+ KopeteChatWindow *newWindow = 0L;
+ ChatView *detachedView;
+
+ if( m_popupView )
+ detachedView = m_popupView;
+ else
+ detachedView = m_activeView;
+
+ if( !detachedView )
+ return;
+
+ //if we don't do this, we might crash
+ createGUI(0L);
+ guiFactory()->removeClient(detachedView->msgManager());
+
+ if( newWindowIndex == -1 )
+ newWindow = new KopeteChatWindow();
+ else
+ newWindow = windows.at( newWindowIndex );
+
+ newWindow->show();
+ newWindow->raise();
+
+ detachChatView( detachedView );
+ newWindow->attachChatView( detachedView );
+}
+
+void KopeteChatWindow::slotPreviousTab()
+{
+ int curPage = m_tabBar->currentPageIndex();
+ if( curPage > 0 )
+ m_tabBar->setCurrentPage( curPage - 1 );
+ else
+ m_tabBar->setCurrentPage( m_tabBar->count() - 1 );
+}
+
+void KopeteChatWindow::slotNextTab()
+{
+ int curPage = m_tabBar->currentPageIndex();
+ if( curPage == ( m_tabBar->count() - 1 ) )
+ m_tabBar->setCurrentPage( 0 );
+ else
+ m_tabBar->setCurrentPage( curPage + 1 );
+}
+
+void KopeteChatWindow::slotSetCaption( bool active )
+{
+ if( active && m_activeView )
+ {
+ setCaption( m_activeView->caption(), false );
+ }
+}
+
+void KopeteChatWindow::updateBackground( const QPixmap &pm )
+{
+ if( updateBg )
+ {
+ updateBg = false;
+ if( backgroundFile != 0L )
+ {
+ backgroundFile->close();
+ backgroundFile->unlink();
+ }
+
+ backgroundFile = new KTempFile( QString::null, QString::fromLatin1( ".bmp" ) );
+ pm.save( backgroundFile->name(), "BMP" );
+ QTimer::singleShot( 100, this, SLOT( slotEnableUpdateBg() ) );
+ }
+}
+
+void KopeteChatWindow::setActiveView( QWidget *widget )
+{
+ ChatView *view = static_cast<ChatView*>(widget);
+
+ if( m_activeView == view )
+ return;
+
+ if(m_activeView)
+ {
+ disconnect( m_activeView, SIGNAL( canSendChanged(bool) ), this, SLOT( slotUpdateSendEnabled() ) );
+ guiFactory()->removeClient(m_activeView->msgManager());
+ m_activeView->saveChatSettings();
+ }
+
+ guiFactory()->addClient(view->msgManager());
+ createGUI( view->editPart() );
+
+ if( m_activeView )
+ m_activeView->setActive( false );
+
+ m_activeView = view;
+
+ if( !chatViewList.contains( view ) )
+ attachChatView( view );
+
+ connect( m_activeView, SIGNAL( canSendChanged(bool) ), this, SLOT( slotUpdateSendEnabled() ) );
+
+ //Tell it it is active
+ m_activeView->setActive( true );
+
+ //Update icons to match
+ slotUpdateCaptionIcons( m_activeView );
+
+ //Update chat members actions
+ updateMembersActions();
+
+ if ( m_activeView->sendInProgress() && !animIcon.isNull() )
+ {
+ anim->setMovie( animIcon );
+ animIcon.unpause();
+ }
+ else
+ {
+ anim->setPixmap( normalIcon );
+ if( !animIcon.isNull() )
+ animIcon.pause();
+ }
+
+ if ( m_alwaysShowTabs || chatViewList.count() > 1 )
+ {
+ if( !m_tabBar )
+ createTabBar();
+
+ m_tabBar->showPage( m_activeView );
+ }
+
+ setCaption( m_activeView->caption() );
+ setStatus( m_activeView->statusText() );
+ m_activeView->setFocus();
+ updateSpellCheckAction();
+ slotUpdateSendEnabled();
+ m_activeView->editPart()->reloadConfig();
+ m_activeView->loadChatSettings();
+}
+
+void KopeteChatWindow::slotUpdateCaptionIcons( ChatView *view )
+{
+ if ( !view )
+ return; //(pas de charité)
+
+ QPtrList<Kopete::Contact> chatMembers=view->msgManager()->members();
+ Kopete::Contact *c=0L;
+ for ( Kopete::Contact *contact = chatMembers.first(); contact; contact = chatMembers.next() )
+ {
+ if(!c || c->onlineStatus() < contact->onlineStatus())
+ c=contact;
+ }
+
+ if ( view == m_activeView )
+ {
+ QPixmap icon16 = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c , 16) :
+ SmallIcon( view->msgManager()->protocol()->pluginIcon() );
+ QPixmap icon32 = c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c , 32) :
+ SmallIcon( view->msgManager()->protocol()->pluginIcon() );
+ KWin::setIcons( winId(), icon32, icon16 );
+ }
+
+ if ( m_tabBar )
+ m_tabBar->setTabIconSet( view, c ? view->msgManager()->contactOnlineStatus( c ).iconFor( c ) :
+ SmallIcon( view->msgManager()->protocol()->pluginIcon() ) );
+}
+
+void KopeteChatWindow::slotChatClosed()
+{
+ if( m_popupView )
+ m_popupView->closeView();
+ else
+ m_activeView->closeView();
+}
+
+void KopeteChatWindow::slotPrepareDetachMenu(void)
+{
+ QPopupMenu *detachMenu = actionDetachMenu->popupMenu();
+ detachMenu->clear();
+
+ for ( unsigned id=0; id < windows.count(); id++ )
+ {
+ KopeteChatWindow *win = windows.at( id );
+ if( win != this )
+ detachMenu->insertItem( win->caption(), id );
+ }
+}
+
+void KopeteChatWindow::slotSendMessage()
+{
+ if ( m_activeView && m_activeView->canSend() )
+ {
+ if( !animIcon.isNull() )
+ {
+ anim->setMovie( animIcon );
+ animIcon.unpause();
+ }
+ m_activeView->sendMessage();
+ }
+}
+
+void KopeteChatWindow::slotPrepareContactMenu(void)
+{
+ QPopupMenu *contactsMenu = actionContactMenu->popupMenu();
+ contactsMenu->clear();
+
+ Kopete::Contact *contact;
+ Kopete::ContactPtrList m_them;
+
+ if( m_popupView )
+ m_them = m_popupView->msgManager()->members();
+ else
+ m_them = m_activeView->msgManager()->members();
+
+ //TODO: don't display a menu with one contact in it, display that
+ // contact's menu instead. Will require changing text and icon of
+ // 'Contacts' action, or something cleverer.
+ uint contactCount = 0;
+
+ for ( contact = m_them.first(); contact; contact = m_them.next() )
+ {
+ KPopupMenu *p = contact->popupMenu();
+ connect ( actionContactMenu->popupMenu(), SIGNAL(aboutToHide()),
+ p, SLOT(deleteLater() ) );
+
+ if( contact->metaContact() )
+ contactsMenu->insertItem( contact->onlineStatus().iconFor( contact ) , contact->metaContact()->displayName(), p );
+ else
+ contactsMenu->insertItem( contact->onlineStatus().iconFor( contact ) , contact->contactId(), p );
+
+ //FIXME: This number should be a config option
+ if( ++contactCount == 15 && contact != m_them.getLast() )
+ {
+ KActionMenu *moreMenu = new KActionMenu( i18n("More..."),
+ QString::fromLatin1("folder_open"), contactsMenu );
+ connect ( actionContactMenu->popupMenu(), SIGNAL(aboutToHide()),
+ moreMenu, SLOT(deleteLater() ) );
+ moreMenu->plug( contactsMenu );
+ contactsMenu = moreMenu->popupMenu();
+ contactCount = 0;
+ }
+ }
+}
+
+void KopeteChatWindow::slotPreparePlacementMenu()
+{
+ QPopupMenu *placementMenu = actionTabPlacementMenu->popupMenu();
+ placementMenu->clear();
+
+ placementMenu->insertItem( i18n("Top"), 0 );
+ placementMenu->insertItem( i18n("Bottom"), 1 );
+}
+
+void KopeteChatWindow::slotPlaceTabs( int placement )
+{
+ if( m_tabBar )
+ {
+
+ if( placement == 0 )
+ m_tabBar->setTabPosition( QTabWidget::Top );
+ else
+ m_tabBar->setTabPosition( QTabWidget::Bottom );
+
+ saveOptions();
+ }
+}
+
+void KopeteChatWindow::readOptions()
+{
+ // load and apply config file settings affecting the appearance of the UI
+// kdDebug(14010) << k_funcinfo << endl;
+ KConfig *config = KGlobal::config();
+ applyMainWindowSettings( config, QString::fromLatin1( "KopeteChatWindow" ) );
+ config->setGroup( QString::fromLatin1("ChatWindowSettings") );
+ m_showFormatToolbar = config->readBoolEntry( QString::fromLatin1("Show Format Toolbar"), true );
+}
+
+void KopeteChatWindow::saveOptions()
+{
+// kdDebug(14010) << k_funcinfo << endl;
+
+ KConfig *config = KGlobal::config();
+
+ // saves menubar,toolbar and statusbar setting
+ saveMainWindowSettings( config, QString::fromLatin1( "KopeteChatWindow" ) );
+ config->setGroup( QString::fromLatin1("ChatWindowSettings") );
+ if( m_tabBar )
+ config->writeEntry ( QString::fromLatin1("Tab Placement"), m_tabBar->tabPosition() );
+
+ config->writeEntry( QString::fromLatin1("Show Format Toolbar"), m_showFormatToolbar );
+ config->sync();
+}
+
+void KopeteChatWindow::slotChatSave()
+{
+// kdDebug(14010) << "KopeteChatWindow::slotChatSave()" << endl;
+ if( isActiveWindow() && m_activeView )
+ m_activeView->messagePart()->save();
+}
+
+void KopeteChatWindow::windowActivationChange( bool )
+{
+ if( isActiveWindow() && m_activeView )
+ m_activeView->setActive( true );
+}
+
+void KopeteChatWindow::slotChatPrint()
+{
+ m_activeView->messagePart()->print();
+}
+
+void KopeteChatWindow::slotToggleStatusBar()
+{
+ if (statusBar()->isVisible())
+ statusBar()->hide();
+ else
+ statusBar()->show();
+}
+
+void KopeteChatWindow::slotToggleFormatToolbar(bool visible)
+{
+ if ( adjustingFormatToolbar )
+ return;
+ m_showFormatToolbar = visible;
+}
+
+void KopeteChatWindow::slotViewMenuBar()
+{
+ if( !menuBar()->isHidden() )
+ menuBar()->hide();
+ else
+ menuBar()->show();
+}
+
+void KopeteChatWindow::slotSmileyActivated(const QString &sm)
+{
+ if ( !sm.isNull() )
+ m_activeView->addText( " " + sm + " " );
+ //we are adding space around the emoticon becasue our parser only display emoticons not in a word.
+}
+
+void KopeteChatWindow::slotRTFEnabled( ChatView* cv, bool enabled)
+{
+ if ( cv != m_activeView )
+ return;
+
+ adjustingFormatToolbar = true;
+ if ( enabled && m_showFormatToolbar )
+ toolBar( "formatToolBar" )->show();
+ else
+ toolBar( "formatToolBar" )->hide();
+ adjustingFormatToolbar = false;
+ updateSpellCheckAction();
+}
+
+void KopeteChatWindow::slotAutoSpellCheckEnabled( ChatView* view, bool isEnabled )
+{
+ if ( view != m_activeView )
+ return;
+
+ toggleAutoSpellCheck->setEnabled( isEnabled );
+ toggleAutoSpellCheck->setChecked( isEnabled );
+ m_activeView->editPart()->toggleAutoSpellCheck( isEnabled );
+}
+
+bool KopeteChatWindow::queryClose()
+{
+ bool canClose = true;
+
+// kdDebug( 14010 ) << " Windows left open:" << endl;
+// for( QPtrListIterator<ChatView> it( chatViewList ); it; ++it)
+// kdDebug( 14010 ) << " " << *it << " (" << (*it)->caption() << ")" << endl;
+
+ for( QPtrListIterator<ChatView> it( chatViewList ); it; )
+ {
+ ChatView *view = *it;
+ // move out of the way before view is removed
+ ++it;
+
+ // FIXME: This should only check if it *can* close
+ // and not start closing if the close can be aborted halfway, it would
+ // leave us with half the chats open and half of them closed. - Martijn
+
+ // if the view is closed, it is removed from chatViewList for us
+ if ( !view->closeView() )
+ {
+ kdDebug() << k_funcinfo << "Closing view failed!" << endl;
+ canClose = false;
+ }
+ }
+ return canClose;
+}
+
+bool KopeteChatWindow::queryExit()
+{
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ if ( app->sessionSaving()
+ || app->isShuttingDown() /* only set if KopeteApplication::quitKopete() or
+ KopeteApplication::commitData() called */
+ || !KopetePrefs::prefs()->showTray() /* also close if our tray icon is hidden! */
+ || !isShown() )
+ {
+ Kopete::PluginManager::self()->shutdown();
+ return true;
+ }
+ else
+ return false;
+}
+
+void KopeteChatWindow::closeEvent( QCloseEvent * e )
+{
+ // if there's a system tray applet and we are not shutting down then just do what needs to be done if a
+ // window is closed.
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ if ( KopetePrefs::prefs()->showTray() && !app->isShuttingDown() && !app->sessionSaving() ) {
+// hide();
+ // BEGIN of code borrowed from KMainWindow::closeEvent
+ // Save settings if auto-save is enabled, and settings have changed
+ if ( settingsDirty() && autoSaveSettings() )
+ saveAutoSaveSettings();
+
+ if ( queryClose() ) {
+ e->accept();
+ }
+ // END of code borrowed from KMainWindow::closeEvent
+ }
+ else
+ KMainWindow::closeEvent( e );
+}
+
+void KopeteChatWindow::slotConfKeys()
+{
+ KKeyDialog dlg( false, this );
+ dlg.insert( actionCollection() );
+ if( m_activeView )
+ {
+ dlg.insert(m_activeView->msgManager()->actionCollection() , i18n("Plugin Actions") );
+ QPtrListIterator<KXMLGUIClient> it( *m_activeView->msgManager()->childClients() );
+ KXMLGUIClient *c = 0;
+ while( (c = it.current()) != 0 )
+ {
+ dlg.insert( c->actionCollection() /*, i18n("Plugin Actions")*/ );
+ ++it;
+ }
+
+ if( m_activeView->editPart() )
+ dlg.insert( m_activeView->editPart()->actionCollection(), m_activeView->editPart()->name() );
+ }
+
+ dlg.configure();
+}
+
+void KopeteChatWindow::slotConfToolbar()
+{
+ saveMainWindowSettings(KGlobal::config(), QString::fromLatin1( "KopeteChatWindow" ));
+ KEditToolbar *dlg = new KEditToolbar(factory(), this );
+ if (dlg->exec())
+ {
+ if( m_activeView )
+ createGUI( m_activeView->editPart() );
+ else
+ createGUI( 0L );
+ applyMainWindowSettings(KGlobal::config(), QString::fromLatin1( "KopeteChatWindow" ));
+ }
+ delete dlg;
+}
+
+void KopeteChatWindow::updateChatState( ChatView* cv, int newState )
+{
+ if ( m_tabBar )
+ {
+ switch( newState )
+ {
+ case ChatView::Highlighted:
+ m_tabBar->setTabColor( cv, Qt::blue );
+ break;
+ case ChatView::Message:
+ m_tabBar->setTabColor( cv, Qt::red );
+ break;
+ case ChatView::Changed:
+ m_tabBar->setTabColor( cv, Qt::darkRed );
+ break;
+ case ChatView::Typing:
+ m_tabBar->setTabColor( cv, Qt::darkGreen );
+ break;
+ case ChatView::Normal:
+ default:
+ m_tabBar->setTabColor( cv, KGlobalSettings::textColor() );
+ break;
+ }
+ }
+}
+
+void KopeteChatWindow::updateChatTooltip( ChatView* cv )
+{
+ if ( m_tabBar )
+ m_tabBar->setTabToolTip( cv, QString::fromLatin1("<qt>%1</qt>").arg( cv->caption() ) );
+}
+
+void KopeteChatWindow::updateChatLabel()
+{
+ const ChatView* cv = dynamic_cast<const ChatView*>( sender() );
+ if ( !cv || !m_tabBar )
+ return;
+
+ ChatView* chat = const_cast<ChatView*>( cv );
+ if ( m_tabBar )
+ {
+ m_tabBar->setTabLabel( chat, chat->caption() );
+ if ( m_tabBar->count() < 2 || m_tabBar->currentPage() == static_cast<const QWidget *>(cv) )
+ setCaption( chat->caption() );
+ }
+}
+
+#include "kopetechatwindow.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopetechatwindow.h b/kopete/kopete/chatwindow/kopetechatwindow.h
new file mode 100644
index 00000000..39277d86
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindow.h
@@ -0,0 +1,243 @@
+/*
+ kopetechatwindow.h - Chat Window
+
+ Copyright (c) 2002 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2004 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETECHATWINDOW_H
+#define KOPETECHATWINDOW_H
+
+#include <kparts/mainwindow.h>
+#include <qmovie.h>
+#include "kopetecontact.h"
+#include "kdeversion.h"
+
+class KAction;
+class KToggleAction;
+class KActionMenu;
+class KTempFile;
+class QPixmap;
+class QTabWidget;
+class KSqueezedTextLabel;
+class KPushButton;
+class QVBox;
+class QVBoxLayout;
+class QFrame;
+class KTabWidget;
+class QLabel;
+class KopeteEmoticonAction;
+class KopeteView;
+class KSelectAction;
+class ChatView;
+
+namespace Kopete
+{
+class Message;
+class ChatSession;
+class Contact;
+class Protocol;
+typedef QPtrList<Contact> ContactPtrList;
+}
+
+class KopeteChatWindow : public KParts::MainWindow
+{
+ Q_OBJECT
+
+ enum {NEW_WINDOW, GROUP_BY_ACCOUNT, GROUP_ALL, GROUP_BY_GROUP, GROUP_BY_METACONTACT};
+
+public:
+ /**
+ * Find the appropriate window for a ChatView of the given protocol to
+ * dock into. If no such window exists, create one.
+ * @param protocol The protocol we are creating a view for
+ * @return A KopeteChatWindow suitable for docking a ChatView into. Guaranteed
+ * to be a valid pointer.
+ */
+ static KopeteChatWindow *window( Kopete::ChatSession *manager );
+ ~KopeteChatWindow();
+
+ /**
+ * Attach an unattached chatview to this window
+ * @param chat The chat view to attach
+ */
+ void attachChatView( ChatView *chat );
+
+ /**
+ * Detach a chat view from this window
+ * @param chat The chat view to detach
+ */
+ void detachChatView( ChatView *chat );
+
+ /**
+ * Returns the number of chat views attached to this window
+ */
+ int chatViewCount() { return chatViewList.count(); }
+
+ /**
+ * Returns the chatview in the currently active tab, or the only chat view
+ * if chatViewCount() == 1
+ */
+ ChatView *activeView();
+
+ void updateMembersActions();
+ void setStatus( const QString & );
+
+ /**
+ * Reimplemented from KMainWindow - asks each ChatView in the window if it is ok to close the window
+ * @return true if no ChatView objects to closing.
+ */
+ virtual bool queryClose();
+ virtual bool queryExit();
+
+ KTempFile *backgroundFile;
+ QPtrList<ChatView> chatViewList;
+
+private:
+ // All KopeteChatWindows are created by the window function
+ KopeteChatWindow( QWidget *parent = 0, const char* name = "KopeteChatWindow" );
+
+ /**
+ * The window list has changed:
+ * For each chat window, update it's Move Tab to Window action
+ */
+ static void windowListChanged();
+
+ void initActions(void);
+ void saveOptions(void);
+ void readOptions(void);
+ void checkDetachEnable();
+ void createTabBar();
+ void deleteTabBar();
+ void addTab( ChatView* );
+ void setPrimaryChatView( ChatView* );
+ const QString fileContents( const QString &file ) const;
+
+ ChatView *m_activeView;
+ ChatView *m_popupView;
+ bool m_alwaysShowTabs;
+ bool m_showFormatToolbar;
+ bool adjustingFormatToolbar;
+ bool updateBg;
+ KTabWidget *m_tabBar;
+ KPushButton *m_button_send;
+ KSqueezedTextLabel *m_status_text;
+ QVBoxLayout *mainLayout;
+ QFrame *mainArea;
+ QLabel *anim;
+ QMovie animIcon;
+ QPixmap normalIcon;
+
+ KAction *chatSend;
+ KAction *historyUp;
+ KAction *historyDown;
+ KAction *nickComplete;
+
+ KToggleAction *mStatusbarAction;
+
+ KAction *tabLeft;
+ KAction *tabRight;
+ KAction *tabDetach;
+ KAction* tabClose;
+
+ KToggleAction* membersLeft;
+ KToggleAction* membersRight;
+ KToggleAction* toggleMembers;
+ KToggleAction* toggleAutoSpellCheck;
+
+ KopeteEmoticonAction *actionSmileyMenu;
+ KActionMenu *actionActionMenu;
+ KActionMenu *actionContactMenu;
+ KActionMenu *actionDetachMenu;
+ KActionMenu *actionTabPlacementMenu;
+ QString statusMsg;
+
+signals:
+ void closing( KopeteChatWindow* );
+
+public slots:
+ void slotSmileyActivated( const QString & );
+ void setActiveView( QWidget *active );
+ void updateBackground( const QPixmap &pm );
+
+private slots:
+// void slotPrepareSmileyMenu();
+ void slotPrepareContactMenu();
+ void slotPrepareDetachMenu();
+ void slotPreparePlacementMenu();
+ void slotUpdateSendEnabled();
+
+ void slotCut();
+ void slotCopy();
+ void slotPaste();
+
+ void slotSetBgColor();
+ void slotSetFgColor();
+ void slotSetFont();
+
+ void slotHistoryUp();
+ void slotHistoryDown();
+ void slotPageUp();
+ void slotPageDown();
+
+ void slotSendMessage();
+ void slotChatSave();
+ void slotChatPrint();
+
+ void slotPreviousTab();
+ void slotNextTab();
+ void slotDetachChat( int newWindowIndex = -1 );
+ void slotPlaceTabs( int tabPlacement );
+
+ void slotViewMenuBar();
+ void slotToggleStatusBar();
+ void slotToggleFormatToolbar( bool );
+
+ void slotConfKeys();
+ void slotConfToolbar();
+
+ void slotViewMembersLeft();
+ void slotViewMembersRight();
+ void slotToggleViewMembers();
+ void slotEnableUpdateBg() { updateBg = true; }
+
+ void toggleAutoSpellChecking();
+ void slotRTFEnabled( ChatView*, bool );
+ void slotAutoSpellCheckEnabled( ChatView*, bool );
+
+ void slotSetCaption( bool );
+ void slotUpdateCaptionIcons( ChatView * );
+ void slotChatClosed();
+ void slotTabContextMenu( QWidget*, const QPoint & );
+ void slotStopAnimation( ChatView* );
+ void slotNickComplete();
+ void slotCloseChat( QWidget* );
+
+ //slots for tabs from the chatview widget
+ void updateChatState( ChatView* cv, int state );
+ void updateChatTooltip( ChatView* cv );
+ void updateChatLabel();
+
+private:
+ void updateSpellCheckAction();
+
+protected:
+ virtual void closeEvent( QCloseEvent *e );
+ virtual void windowActivationChange( bool );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopetechatwindow.rc b/kopete/kopete/chatwindow/kopetechatwindow.rc
new file mode 100644
index 00000000..89f19c39
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindow.rc
@@ -0,0 +1,64 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="33" name="kopetechatwindow">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="chat_send" />
+ <Action name="file_save" />
+ <Action name="file_print" />
+ <Separator lineSeparator="true"/>
+ <Action name="contacts_menu"/>
+ <Merge/>
+ <Separator lineSeparator="true"/>
+ <Action name="tabs_close"/>
+ <Action name="file_quit" />
+ </Menu>
+
+ <Menu name="format" >
+ <text>&amp;Format</text>
+ <Action name="format_smiley"/>
+ </Menu>
+
+ <Menu name="tabs" >
+ <text>&amp;Tabs</text>
+ <Action name="tabs_placement"/>
+ <Action name="tabs_detach"/>
+ <Action name="tabs_detachmove"/>
+ <Action name="tabs_left"/>
+ <Action name="tabs_right"/>
+ </Menu>
+
+ <Menu name="settings">
+ <text>&amp;Settings</text>
+ <Merge name="StandardToolBarMenuHandler" />
+ <Menu append="show_merge" name="options_chatmembers">
+ <text>&amp;Chat Members List</text>
+ <Action name="options_membersleft" />
+ <Action name="options_membersright" />
+ <Action name="options_togglemembers" />
+ </Menu>
+ <Action name="enable_auto_spell_check" />
+ <Action append="show_merge" name="options_styles"/>
+ <Action append="configure_merge" name="settings_prefs" />
+ </Menu>
+ <Merge/>
+ </MenuBar>
+
+ <ToolBar name="mainToolBar" fullWidth="true">
+ <text>Main Toolbar</text>
+ <Action name="format_smiley"/>
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Merge />
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Action name="toolbar_animation"/>
+ </ToolBar>
+
+ <ToolBar name="statusToolBar">
+ <text>Status</text>
+ </ToolBar>
+ <ToolBar name="formatToolBar">
+ <text>Format Toolbar</text>
+ </ToolBar>
+
+
+</kpartgui>
diff --git a/kopete/kopete/chatwindow/kopetechatwindowstyle.cpp b/kopete/kopete/chatwindow/kopetechatwindowstyle.cpp
new file mode 100644
index 00000000..3e15281f
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindowstyle.cpp
@@ -0,0 +1,287 @@
+ /*
+ kopetechatwindowstyle.cpp - A Chat Window Style.
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetechatwindowstyle.h"
+
+// Qt includes
+#include <qfile.h>
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+// KDE includes
+#include <kdebug.h>
+
+
+class ChatWindowStyle::Private
+{
+public:
+ QString stylePath;
+ StyleVariants variantsList;
+ QString baseHref;
+ QString currentVariantPath;
+
+ QString headerHtml;
+ QString footerHtml;
+ QString incomingHtml;
+ QString nextIncomingHtml;
+ QString outgoingHtml;
+ QString nextOutgoingHtml;
+ QString statusHtml;
+ QString actionIncomingHtml;
+ QString actionOutgoingHtml;
+};
+
+ChatWindowStyle::ChatWindowStyle(const QString &stylePath, int styleBuildMode)
+ : d(new Private)
+{
+ init(stylePath, styleBuildMode);
+}
+
+ChatWindowStyle::ChatWindowStyle(const QString &stylePath, const QString &variantPath, int styleBuildMode)
+ : d(new Private)
+{
+ d->currentVariantPath = variantPath;
+ init(stylePath, styleBuildMode);
+}
+
+void ChatWindowStyle::init(const QString &stylePath, int styleBuildMode)
+{
+ d->stylePath = stylePath;
+ d->baseHref = stylePath + QString::fromUtf8("/Contents/Resources/");
+ readStyleFiles();
+ if(styleBuildMode & StyleBuildNormal)
+ {
+ listVariants();
+ }
+}
+
+ChatWindowStyle::~ChatWindowStyle()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ delete d;
+}
+
+ChatWindowStyle::StyleVariants ChatWindowStyle::getVariants()
+{
+ // If the variantList is empty, list available variants.
+ if( d->variantsList.isEmpty() )
+ {
+ listVariants();
+ }
+ return d->variantsList;
+}
+
+QString ChatWindowStyle::getStylePath() const
+{
+ return d->stylePath;
+}
+
+QString ChatWindowStyle::getStyleBaseHref() const
+{
+ return d->baseHref;
+}
+
+QString ChatWindowStyle::getHeaderHtml() const
+{
+ return d->headerHtml;
+}
+
+QString ChatWindowStyle::getFooterHtml() const
+{
+ return d->footerHtml;
+}
+
+QString ChatWindowStyle::getIncomingHtml() const
+{
+ return d->incomingHtml;
+}
+
+QString ChatWindowStyle::getNextIncomingHtml() const
+{
+ return d->nextIncomingHtml;
+}
+
+QString ChatWindowStyle::getOutgoingHtml() const
+{
+ return d->outgoingHtml;
+}
+
+QString ChatWindowStyle::getNextOutgoingHtml() const
+{
+ return d->nextOutgoingHtml;
+}
+
+QString ChatWindowStyle::getStatusHtml() const
+{
+ return d->statusHtml;
+}
+
+QString ChatWindowStyle::getActionIncomingHtml() const
+{
+ return d->actionIncomingHtml;
+}
+
+QString ChatWindowStyle::getActionOutgoingHtml() const
+{
+ return d->actionOutgoingHtml;
+}
+
+bool ChatWindowStyle::hasActionTemplate() const
+{
+ return ( !d->actionIncomingHtml.isEmpty() && !d->actionOutgoingHtml.isEmpty() );
+}
+
+void ChatWindowStyle::listVariants()
+{
+ QString variantDirPath = d->baseHref + QString::fromUtf8("Variants/");
+ QDir variantDir(variantDirPath);
+
+ QStringList variantList = variantDir.entryList("*.css");
+ QStringList::ConstIterator it, itEnd = variantList.constEnd();
+ for(it = variantList.constBegin(); it != itEnd; ++it)
+ {
+ QString variantName = *it, variantPath;
+ // Retrieve only the file name.
+ variantName = variantName.left(variantName.findRev("."));
+ // variantPath is relative to baseHref.
+ variantPath = QString("Variants/%1").arg(*it);
+ d->variantsList.insert(variantName, variantPath);
+ }
+}
+
+void ChatWindowStyle::readStyleFiles()
+{
+ QString headerFile = d->baseHref + QString("Header.html");
+ QString footerFile = d->baseHref + QString("Footer.html");
+ QString incomingFile = d->baseHref + QString("Incoming/Content.html");
+ QString nextIncomingFile = d->baseHref + QString("Incoming/NextContent.html");
+ QString outgoingFile = d->baseHref + QString("Outgoing/Content.html");
+ QString nextOutgoingFile = d->baseHref + QString("Outgoing/NextContent.html");
+ QString statusFile = d->baseHref + QString("Status.html");
+ QString actionIncomingFile = d->baseHref + QString("Incoming/Action.html");
+ QString actionOutgoingFile = d->baseHref + QString("Outgoing/Action.html");
+
+ QFile fileAccess;
+ // First load header file.
+ if( QFile::exists(headerFile) )
+ {
+ fileAccess.setName(headerFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->headerHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "Header HTML: " << d->headerHtml << endl;
+ fileAccess.close();
+ }
+ // Load Footer file
+ if( QFile::exists(footerFile) )
+ {
+ fileAccess.setName(footerFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->footerHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "Footer HTML: " << d->footerHtml << endl;
+ fileAccess.close();
+ }
+ // Load incoming file
+ if( QFile::exists(incomingFile) )
+ {
+ fileAccess.setName(incomingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->incomingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "Incoming HTML: " << d->incomingHtml << endl;
+ fileAccess.close();
+ }
+ // Load next Incoming file
+ if( QFile::exists(nextIncomingFile) )
+ {
+ fileAccess.setName(nextIncomingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->nextIncomingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "NextIncoming HTML: " << d->nextIncomingHtml << endl;
+ fileAccess.close();
+ }
+ // Load outgoing file
+ if( QFile::exists(outgoingFile) )
+ {
+ fileAccess.setName(outgoingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->outgoingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "Outgoing HTML: " << d->outgoingHtml << endl;
+ fileAccess.close();
+ }
+ // Load next outgoing file
+ if( QFile::exists(nextOutgoingFile) )
+ {
+ fileAccess.setName(nextOutgoingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->nextOutgoingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "NextOutgoing HTML: " << d->nextOutgoingHtml << endl;
+ fileAccess.close();
+ }
+ // Load status file
+ if( QFile::exists(statusFile) )
+ {
+ fileAccess.setName(statusFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->statusHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "Status HTML: " << d->statusHtml << endl;
+ fileAccess.close();
+ }
+
+ // Load Action Incoming file
+ if( QFile::exists(actionIncomingFile) )
+ {
+ fileAccess.setName(actionIncomingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->actionIncomingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "ActionIncoming HTML: " << d->actionIncomingHtml << endl;
+ fileAccess.close();
+ }
+ // Load Action Outgoing file
+ if( QFile::exists(actionOutgoingFile) )
+ {
+ fileAccess.setName(actionOutgoingFile);
+ fileAccess.open(IO_ReadOnly);
+ QTextStream headerStream(&fileAccess);
+ headerStream.setEncoding(QTextStream::UnicodeUTF8);
+ d->actionOutgoingHtml = headerStream.read();
+ kdDebug(14000) << k_funcinfo << "ActionOutgoing HTML: " << d->actionOutgoingHtml << endl;
+ fileAccess.close();
+ }
+}
+
+void ChatWindowStyle::reload()
+{
+ d->variantsList.clear();
+ readStyleFiles();
+ listVariants();
+}
diff --git a/kopete/kopete/chatwindow/kopetechatwindowstyle.h b/kopete/kopete/chatwindow/kopetechatwindowstyle.h
new file mode 100644
index 00000000..ca5a0863
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindowstyle.h
@@ -0,0 +1,129 @@
+ /*
+ kopetechatwindowstyle.h - A Chat Window Style.
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef KOPETECHATWINDOWSTYLE_H
+#define KOPETECHATWINDOWSTYLE_H
+
+#include <qstring.h>
+#include <qmap.h>
+
+
+/**
+ * This class represent a single chat window style.
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class ChatWindowStyle
+{
+public:
+ /**
+ * StyleVariants is a typedef to a QMap
+ * key = Variant Name
+ * value = Path to variant CSS file.
+ * Path is relative to Ressources directory.
+ */
+ typedef QMap<QString,QString> StyleVariants;
+
+ /**
+ * This enum specifies the mode of the constructor
+ * - StyleBuildFast : Build the style the fatest possible
+ * - StyleBuildNormal : List all variants of this style. Require a async dir list.
+ */
+ enum StyleBuildMode { StyleBuildFast, StyleBuildNormal};
+
+ /**
+ * @brief Build a single chat window style.
+ *
+ */
+ ChatWindowStyle(const QString &stylePath, int styleBuildMode = StyleBuildNormal);
+ ChatWindowStyle(const QString &stylePath, const QString &variantPath, int styleBuildMode = StyleBuildFast);
+ ~ChatWindowStyle();
+
+ /**
+ * Get the list of all variants for this theme.
+ * If the variant aren't listed, it call the lister
+ * before returning the list of the Variants.
+ * If the variant are listed, it just return the cached
+ * variant list.
+ * @return the StyleVariants QMap.
+ */
+ StyleVariants getVariants();
+
+ /**
+ * Get the style path.
+ * The style path points to the directory where the style is located.
+ * ex: ~/.kde/share/apps/kopete/styles/StyleName/
+ *
+ * @return the style path based.
+ */
+ QString getStylePath() const;
+
+ /**
+ * Get the style ressource directory.
+ * Ressources directory is the base where all CSS, HTML and images are located.
+ *
+ * Adium(and now Kopete too) style directories are disposed like this:
+ * StyleName/
+ * Contents/
+ * Resources/
+ *
+ * @return the path to the the ressource directory.
+ */
+ QString getStyleBaseHref() const;
+
+ QString getHeaderHtml() const;
+ QString getFooterHtml() const;
+ QString getIncomingHtml() const;
+ QString getNextIncomingHtml() const;
+ QString getOutgoingHtml() const;
+ QString getNextOutgoingHtml() const;
+ QString getStatusHtml() const;
+
+ QString getActionIncomingHtml() const;
+ QString getActionOutgoingHtml() const;
+
+ /**
+ * Check if the style has the support for Kopete Action template (Kopete extension)
+ * @return true if the style has Action template.
+ */
+ bool hasActionTemplate() const;
+
+ /**
+ * Reload style from disk.
+ */
+ void reload();
+private:
+ /**
+ * Read style HTML files from disk
+ */
+ void readStyleFiles();
+
+ /**
+ * Init this class
+ */
+ void init(const QString &stylePath, int styleBuildMode);
+
+ /**
+ * List available variants for the current style.
+ */
+ void listVariants();
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/kopete/chatwindow/kopetechatwindowstylemanager.cpp b/kopete/kopete/chatwindow/kopetechatwindowstylemanager.cpp
new file mode 100644
index 00000000..71032ea3
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindowstylemanager.cpp
@@ -0,0 +1,390 @@
+ /*
+ kopetechatwindowstylemanager.cpp - Manager all chat window styles
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetechatwindowstylemanager.h"
+
+// Qt includes
+#include <qvaluestack.h>
+
+// KDE includes
+#include <kstandarddirs.h>
+#include <kdirlister.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kglobal.h>
+#include <karchive.h>
+#include <kzip.h>
+#include <ktar.h>
+#include <kmimetype.h>
+#include <kio/netaccess.h>
+#include <kstaticdeleter.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include "kopetechatwindowstyle.h"
+
+class ChatWindowStyleManager::Private
+{
+public:
+ Private()
+ : styleDirLister(0)
+ {}
+
+ ~Private()
+ {
+ if(styleDirLister)
+ {
+ styleDirLister->deleteLater();
+ }
+
+ QMap<QString, ChatWindowStyle*>::Iterator styleIt, styleItEnd = stylePool.end();
+ for(styleIt = stylePool.begin(); styleIt != styleItEnd; ++styleIt)
+ {
+ delete styleIt.data();
+ }
+ }
+
+ KDirLister *styleDirLister;
+ StyleList availableStyles;
+
+ // key = style path, value = ChatWindowStyle instance
+ QMap<QString, ChatWindowStyle*> stylePool;
+
+ QValueStack<KURL> styleDirs;
+};
+
+static KStaticDeleter<ChatWindowStyleManager> ChatWindowStyleManagerstaticDeleter;
+
+ChatWindowStyleManager *ChatWindowStyleManager::s_self = 0;
+
+ChatWindowStyleManager *ChatWindowStyleManager::self()
+{
+ if( !s_self )
+ {
+ ChatWindowStyleManagerstaticDeleter.setObject( s_self, new ChatWindowStyleManager() );
+ }
+
+ return s_self;
+}
+
+ChatWindowStyleManager::ChatWindowStyleManager(QObject *parent, const char *name)
+ : QObject(parent, name), d(new Private())
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ loadStyles();
+}
+
+ChatWindowStyleManager::~ChatWindowStyleManager()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ delete d;
+}
+
+void ChatWindowStyleManager::loadStyles()
+{
+ QStringList chatStyles = KGlobal::dirs()->findDirs( "appdata", QString::fromUtf8( "styles" ) );
+ QString localStyleDir( locateLocal( "appdata", QString::fromUtf8("styles/"),true) );
+ if( !chatStyles.contains(localStyleDir))
+ chatStyles<<localStyleDir;
+
+ QStringList::const_iterator it;
+ for(it = chatStyles.constBegin(); it != chatStyles.constEnd(); ++it)
+ {
+ kdDebug(14000) << k_funcinfo << *it << endl;
+ d->styleDirs.push( KURL(*it) );
+ }
+
+ d->styleDirLister = new KDirLister(this);
+ d->styleDirLister->setDirOnlyMode(true);
+
+ connect(d->styleDirLister, SIGNAL(newItems(const KFileItemList &)), this, SLOT(slotNewStyles(const KFileItemList &)));
+ connect(d->styleDirLister, SIGNAL(completed()), this, SLOT(slotDirectoryFinished()));
+
+ if( !d->styleDirs.isEmpty() )
+ d->styleDirLister->openURL(d->styleDirs.pop(), true);
+}
+
+ChatWindowStyleManager::StyleList ChatWindowStyleManager::getAvailableStyles()
+{
+ return d->availableStyles;
+}
+
+int ChatWindowStyleManager::installStyle(const QString &styleBundlePath)
+{
+ QString localStyleDir( locateLocal( "appdata", QString::fromUtf8("styles/") ) );
+
+ KArchiveEntry *currentEntry = 0L;
+ KArchiveDirectory* currentDir = 0L;
+ KArchive *archive = 0L;
+
+ if( localStyleDir.isEmpty() )
+ {
+ return StyleNoDirectoryValid;
+ }
+
+ // Find mimetype for current bundle. ZIP and KTar need separate constructor
+ QString currentBundleMimeType = KMimeType::findByPath(styleBundlePath, 0, false)->name();
+ if(currentBundleMimeType == "application/x-zip")
+ {
+ archive = new KZip(styleBundlePath);
+ }
+ else if( currentBundleMimeType == "application/x-tgz" || currentBundleMimeType == "application/x-tbz" || currentBundleMimeType == "application/x-gzip" || currentBundleMimeType == "application/x-bzip2" )
+ {
+ archive = new KTar(styleBundlePath);
+ }
+ else
+ {
+ return StyleCannotOpen;
+ }
+
+ if ( !archive->open(IO_ReadOnly) )
+ {
+ delete archive;
+
+ return StyleCannotOpen;
+ }
+
+ const KArchiveDirectory* rootDir = archive->directory();
+
+ // Ok where we go to check if the archive is valid.
+ // Each time we found a correspondance to a theme bundle, we add a point to validResult.
+ // A valid style bundle must have:
+ // -a Contents, Contents/Resources, Co/Res/Incoming, Co/Res/Outgoing dirs
+ // main.css, Footer.html, Header.html, Status.html files in Contents/Ressources.
+ // So for a style bundle to be valid, it must have a result greather than 8, because we test for 8 required entry.
+ int validResult = 0;
+ QStringList entries = rootDir->entries();
+ // Will be reused later.
+ QStringList::Iterator entriesIt, entriesItEnd = entries.end();
+ for(entriesIt = entries.begin(); entriesIt != entries.end(); ++entriesIt)
+ {
+ currentEntry = const_cast<KArchiveEntry*>(rootDir->entry(*entriesIt));
+// kdDebug() << k_funcinfo << "Current entry name: " << currentEntry->name() << endl;
+ if (currentEntry->isDirectory())
+ {
+ currentDir = dynamic_cast<KArchiveDirectory*>( currentEntry );
+ if (currentDir)
+ {
+ if( currentDir->entry(QString::fromUtf8("Contents")) )
+ {
+// kdDebug() << k_funcinfo << "Contents found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Incoming")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Incoming found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Outgoing")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Outgoing found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/main.css")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/main.css found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Footer.html")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Footer.html found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Status.html")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Status.html found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Header.html")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Header.html found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Incoming/Content.html")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Incoming/Content.html found" << endl;
+ validResult += 1;
+ }
+ if( currentDir->entry(QString::fromUtf8("Contents/Resources/Outgoing/Content.html")) )
+ {
+// kdDebug() << k_funcinfo << "Contents/Resources/Outgoing/Content.html found" << endl;
+ validResult += 1;
+ }
+ }
+ }
+ }
+// kdDebug() << k_funcinfo << "Valid result: " << QString::number(validResult) << endl;
+ // The archive is a valid style bundle.
+ if(validResult >= 8)
+ {
+ bool installOk = false;
+ for(entriesIt = entries.begin(); entriesIt != entries.end(); ++entriesIt)
+ {
+ currentEntry = const_cast<KArchiveEntry*>(rootDir->entry(*entriesIt));
+ if(currentEntry && currentEntry->isDirectory())
+ {
+ // Ignore this MacOS X "garbage" directory in zip.
+ if(currentEntry->name() == QString::fromUtf8("__MACOSX"))
+ {
+ continue;
+ }
+ else
+ {
+ currentDir = dynamic_cast<KArchiveDirectory*>(currentEntry);
+ if(currentDir)
+ {
+ currentDir->copyTo(localStyleDir + currentDir->name());
+ installOk = true;
+ }
+ }
+ }
+ }
+
+ archive->close();
+ delete archive;
+
+ if(installOk)
+ return StyleInstallOk;
+ else
+ return StyleUnknow;
+ }
+ else
+ {
+ archive->close();
+ delete archive;
+
+ return StyleNotValid;
+ }
+
+ if(archive)
+ {
+ archive->close();
+ delete archive;
+ }
+
+ return StyleUnknow;
+}
+
+bool ChatWindowStyleManager::removeStyle(const QString &stylePath)
+{
+ // Find for the current style in avaiableStyles map.
+ KURL urlStyle(stylePath);
+ QString styleName=urlStyle.fileName();
+ StyleList::Iterator foundStyle = d->availableStyles.find(styleName);
+ // QMap iterator return end() if it found no item.
+ if(foundStyle != d->availableStyles.end())
+ {
+ d->availableStyles.remove(foundStyle);
+
+ // Remove and delete style from pool if needed.
+ if( d->stylePool.contains(stylePath) )
+ {
+ ChatWindowStyle *deletedStyle = d->stylePool[stylePath];
+ d->stylePool.remove(stylePath);
+ delete deletedStyle;
+ }
+
+ // Do the actual deletion of the directory style.
+ return KIO::NetAccess::del( urlStyle, 0 );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+ChatWindowStyle *ChatWindowStyleManager::getStyleFromPool(const QString &stylePath)
+{
+ if( d->stylePool.contains(stylePath) )
+ {
+ // NOTE: This is a hidden config switch for style developers
+ // Check in the config if the cache is disabled.
+ // if the cache is disabled, reload the style everytime it's getted.
+ KConfig *config = KGlobal::config();
+ config->setGroup("KopeteStyleDebug");
+ bool disableCache = config->readBoolEntry("disableStyleCache", false);
+ if(disableCache)
+ {
+ d->stylePool[stylePath]->reload();
+ }
+
+ return d->stylePool[stylePath];
+ }
+ else
+ {
+ // Build a chat window style and list its variants, then add it to the pool.
+ ChatWindowStyle *style = new ChatWindowStyle(stylePath, ChatWindowStyle::StyleBuildNormal);
+ d->stylePool.insert(stylePath, style);
+
+ return style;
+ }
+
+ return 0;
+}
+
+void ChatWindowStyleManager::slotNewStyles(const KFileItemList &dirList)
+{
+ KFileItem *item;
+ QPtrListIterator<KFileItem> it( dirList );
+ while( (item = it.current()) != 0 )
+ {
+ // Ignore data dir(from deprecated XSLT themes)
+ if( !item->url().fileName().contains(QString::fromUtf8("data")) )
+ {
+ kdDebug(14000) << k_funcinfo << "Listing: " << item->url().fileName() << endl;
+ // If the style path is already in the pool, that's mean the style was updated on disk
+ // Reload the style
+ if( d->stylePool.contains(item->url().path()) )
+ {
+ kdDebug(14000) << k_funcinfo << "Updating style: " << item->url().path() << endl;
+
+ d->stylePool[item->url().path()]->reload();
+
+ // Add to avaialble if required.
+ if( !d->availableStyles.contains(item->url().fileName()) )
+ d->availableStyles.insert(item->url().fileName(), item->url().path());
+ }
+ else
+ {
+ // TODO: Use name from Info.plist
+ d->availableStyles.insert(item->url().fileName(), item->url().path());
+ }
+ }
+ ++it;
+ }
+}
+
+void ChatWindowStyleManager::slotDirectoryFinished()
+{
+ // Start another scanning if the directories stack is not empty
+ if( !d->styleDirs.isEmpty() )
+ {
+ kdDebug(14000) << k_funcinfo << "Starting another directory." << endl;
+ d->styleDirLister->openURL(d->styleDirs.pop(), true);
+ }
+ else
+ {
+ emit loadStylesFinished();
+ }
+}
+
+#include "kopetechatwindowstylemanager.moc"
diff --git a/kopete/kopete/chatwindow/kopetechatwindowstylemanager.h b/kopete/kopete/chatwindow/kopetechatwindowstylemanager.h
new file mode 100644
index 00000000..4b21c79b
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopetechatwindowstylemanager.h
@@ -0,0 +1,147 @@
+ /*
+ kopetechatwindowstylemanager.h - Manager all chat window styles
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETECHATWINDOWSTYLEMANAGER_H
+#define KOPETECHATWINDOWSTYLEMANAGER_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <kfileitem.h>
+#include <kopete_export.h>
+
+class ChatWindowStyle;
+/**
+ * Sigleton class that handle Chat Window styles.
+ * It use style absolute path to avoid unexpected behavior that could happen when using style name.
+ *
+ * It can install, delete styles. The styles are managed in a pool, they are only retrieved on demand.
+ *
+ * Use getStyleFromPool to retrieve a ChatWindowStyle instance. Do not delete the returned instance, it
+ * is handled by this class.
+ *
+ * When called the first time, it list all the available styles in $KDEDATADIR/kopete/styles and
+ * KDirWatch (via KDirLister) watch for new styles.
+ *
+ * If you want to keep a trace of avaiable styles, connect to loadStylesFinished() signal.
+ * It is called when KDirLister finish a job(ex: on new directory).
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class KOPETE_EXPORT ChatWindowStyleManager : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * StyleList typedef (a QMap)
+ * key = Name of the style (currently the directory name)
+ * value = Path to the style
+ */
+ typedef QMap<QString, QString> StyleList;
+
+ /**
+ * The StyleInstallStatus enum. It gives better return value for installStyle().
+ * - StyleInstallOk : The install went fine.
+ * - StyleNotValid : The archive didn't contain a valid Chat Window style.
+ * - StyleNoDirectoryValid : It didn't find a suitable directory to install the theme.
+ * - StyleCannotOpen : The archive couldn't be openned.
+ * - StyleUnknow : Unknow error.
+ */
+ enum StyleInstallStatus { StyleInstallOk = 0, StyleNotValid, StyleNoDirectoryValid, StyleCannotOpen, StyleUnknow };
+
+ /**
+ * Destructor.
+ */
+ ~ChatWindowStyleManager();
+
+ /**
+ * Singleton access to this class.
+ * @return the single instance of this class.
+ */
+ static ChatWindowStyleManager *self();
+
+ /**
+ * List all availables styles.
+ * Init KDirLister and thus KDirWatch that watch for new styles.
+ */
+ void loadStyles();
+
+ /**
+ * Get all available styles.
+ */
+ StyleList getAvailableStyles();
+
+public slots:
+ /**
+ * Install a new style into user style directory
+ * Note that you must pass a path to a archive.
+ *
+ * @param styleBundlePath Path to the container file to install.
+ * @return A status code from StyleInstallStatus enum.
+ */
+ int installStyle(const QString &styleBundlePath);
+
+ /**
+ * Remove a style from user style directory
+ *
+ * @param stylePath the path of the style to remove.
+ * @return true if the deletion went without problems.
+ */
+ bool removeStyle(const QString &stylePath);
+
+ /**
+ * Get a instance of a ChatWindowStyle from the pool.
+ * If they are no instance for the specified style, it gets created.
+ * DO NOT DELETE the resulting pointer, it is handled by this class.
+ *
+ * @param stylePath Path for the specified style. Name can be ambigous.
+ * @return the instance of ChatWindow for the specified style. DO NOT DELETE IT.
+ */
+ ChatWindowStyle *getStyleFromPool(const QString &stylePath);
+
+signals:
+ /**
+ * This signal is emitted when all styles finished to list.
+ * Used to inform and/or update GUI.
+ */
+ void loadStylesFinished();
+
+private slots:
+ /**
+ * KDirLister found new files.
+ * @param dirList new files found.
+ */
+ void slotNewStyles(const KFileItemList &dirList);
+ /**
+ * KDirLister finished a job.
+ * Emit loadStylesFinished() if they are no directory left in the stack.
+ */
+ void slotDirectoryFinished();
+
+private:
+ /**
+ * Private constructor(it's a singleton class)
+ * Call loadStyles() to list all avaiable styles.
+ */
+ ChatWindowStyleManager(QObject *parent = 0, const char *name = 0);
+
+ static ChatWindowStyleManager *s_self;
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/kopete/chatwindow/kopeteemailwindow.cpp b/kopete/kopete/chatwindow/kopeteemailwindow.cpp
new file mode 100644
index 00000000..84b71b16
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeteemailwindow.cpp
@@ -0,0 +1,565 @@
+/*
+ kopeteemailwindow.cpp - Kopete "email window" for single-shot messages
+
+ Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteemailwindow.h"
+
+#include "chatmessagepart.h"
+#include "chattexteditpart.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopeteemoticonaction.h"
+#include "kopetechatsession.h"
+#include "kopeteplugin.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopetestdaction.h"
+#include "kopeteviewmanager.h"
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kcolordialog.h>
+#include <kconfig.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kedittoolbar.h>
+#include <kfontdialog.h>
+#include <kglobalsettings.h>
+#include <khtmlview.h>
+#include <kiconloader.h>
+#include <kkeydialog.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kpushbutton.h>
+#include <ktextedit.h>
+#include <kwin.h>
+#include <kgenericfactory.h>
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qvbox.h>
+
+typedef KGenericFactory<EmailWindowPlugin> EmailWindowPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_emailwindow, EmailWindowPluginFactory( "kopete_emailwindow" ) )
+
+EmailWindowPlugin::EmailWindowPlugin(QObject *parent, const char *name, const QStringList &) :
+ Kopete::ViewPlugin( EmailWindowPluginFactory::instance(), parent, name )
+{}
+
+KopeteView* EmailWindowPlugin::createView( Kopete::ChatSession *manager )
+{
+ //TODO: foreignMessage, how will we do this cleanly?
+ return (KopeteView*)new KopeteEmailWindow(manager,this, false);
+}
+
+class KopeteEmailWindow::Private
+{
+public:
+ QValueList<Kopete::Message> messageQueue;
+ bool showingMessage;
+ bool sendInProgress;
+ bool visible;
+ uint queuePosition;
+ KPushButton *btnReplySend;
+ KPushButton *btnReadNext;
+ KPushButton *btnReadPrev;
+ QSplitter *split;
+ ChatMessagePart *messagePart;
+ KopeteEmailWindow::WindowMode mode;
+ KAction *chatSend;
+ QLabel *anim;
+ QMovie animIcon;
+ QPixmap normalIcon;
+ QString unreadMessageFrom;
+ ChatTextEditPart *editPart;
+
+ KActionMenu *actionActionMenu;
+ KopeteEmoticonAction *actionSmileyMenu;
+};
+
+KopeteEmailWindow::KopeteEmailWindow( Kopete::ChatSession *manager, EmailWindowPlugin *parent, bool foreignMessage )
+ : KParts::MainWindow( ), KopeteView( manager, parent ), d( new Private )
+{
+ QVBox *v = new QVBox( this );
+ setCentralWidget( v );
+
+ setMinimumSize( QSize( 75, 20 ) );
+
+ d->split = new QSplitter( v );
+ d->split->setOrientation( QSplitter::Vertical );
+
+ d->messagePart = new ChatMessagePart( manager, d->split, "messagePart" );
+
+ // FIXME: should this be in ChatView too? maybe move to ChatMessagePart?
+ d->messagePart->view()->setMarginWidth( 4 );
+ d->messagePart->view()->setMarginHeight( 4 );
+ d->messagePart->view()->setMinimumSize( QSize( 75, 20 ) );
+
+ d->editPart = new ChatTextEditPart( manager, d->split, "editPart" );
+
+ /*
+ FIXME: dude, wtf?
+ QDomDocument doc = d->editPart->domDocument();
+ QDomNode menu = doc.documentElement().firstChild();
+ menu.removeChild( menu.firstChild() ); // Remove File
+ menu.removeChild( menu.firstChild() ); // Remove Edit
+ menu.removeChild( menu.firstChild() ); // Remove View
+ menu.removeChild( menu.lastChild() ); //Remove Help
+
+ doc.documentElement().removeChild( doc.documentElement().childNodes().item(1) ); //Remove MainToolbar
+ doc.documentElement().removeChild( doc.documentElement().lastChild() ); // Remove Edit popup
+ */
+ connect( d->editPart, SIGNAL( messageSent( Kopete::Message & ) ),
+ this, SIGNAL( messageSent( Kopete::Message & ) ) );
+ connect( d->editPart, SIGNAL( canSendChanged( bool ) ),
+ this, SLOT( slotUpdateReplySend() ) );
+ connect( d->editPart, SIGNAL( typing(bool) ),
+ manager, SIGNAL( typing(bool) ) );
+
+ //Connections to the manager and the ViewManager that every view should have
+ connect( this, SIGNAL( closing( KopeteView * ) ),
+ KopeteViewManager::viewManager(), SLOT( slotViewDestroyed( KopeteView * ) ) );
+ connect( this, SIGNAL( activated( KopeteView * ) ),
+ KopeteViewManager::viewManager(), SLOT( slotViewActivated( KopeteView * ) ) );
+ connect( this, SIGNAL( messageSent(Kopete::Message &) ),
+ manager, SLOT( sendMessage(Kopete::Message &) ) );
+ connect( manager, SIGNAL( messageSuccess() ),
+ this, SLOT( messageSentSuccessfully() ));
+
+ QWidget *containerWidget = new QWidget( v );
+ containerWidget->setSizePolicy( QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum) );
+
+ QHBoxLayout *h = new QHBoxLayout( containerWidget, 4, 4 );
+ h->addStretch();
+
+ d->btnReadPrev = new KPushButton( i18n( "<< Prev" ), containerWidget );
+ connect( d->btnReadPrev, SIGNAL( pressed() ), this, SLOT( slotReadPrev() ) );
+ h->addWidget( d->btnReadPrev, 0, Qt::AlignRight | Qt::AlignVCenter );
+ d->btnReadPrev->setEnabled( false );
+
+ d->btnReadNext = new KPushButton( i18n( "(0) Next >>" ), containerWidget );
+ connect( d->btnReadNext, SIGNAL( pressed() ), this, SLOT( slotReadNext() ) );
+ h->addWidget( d->btnReadNext, 0, Qt::AlignRight | Qt::AlignVCenter );
+
+ d->btnReplySend = new KPushButton( containerWidget );
+ connect( d->btnReplySend, SIGNAL( pressed() ), this, SLOT( slotReplySend() ) );
+ h->addWidget( d->btnReplySend, 0, Qt::AlignRight | Qt::AlignVCenter );
+
+ initActions();
+ setWFlags(Qt::WDestructiveClose);
+
+ d->showingMessage = false;
+
+ if( foreignMessage )
+ toggleMode( Read );
+ else
+ toggleMode( Send );
+
+ KConfig *config = KGlobal::config();
+ applyMainWindowSettings( config, QString::fromLatin1( "KopeteEmailWindow" ) );
+
+ d->sendInProgress = false;
+
+ toolBar()->alignItemRight( 99 );
+
+ d->visible = false;
+ d->queuePosition = 0;
+
+ setCaption( manager->displayName() );
+
+ slotUpdateReplySend();
+}
+
+KopeteEmailWindow::~KopeteEmailWindow()
+{
+ emit( closing( this ) );
+
+ // saves menubar, toolbar and statusbar setting
+ KConfig *config = KGlobal::config();
+ saveMainWindowSettings( config, QString::fromLatin1( "KopeteEmailWindow" ) );
+ config->sync();
+
+ delete d;
+}
+
+void KopeteEmailWindow::initActions(void)
+{
+ KActionCollection *coll = actionCollection();
+
+ d->chatSend = new KAction( i18n( "&Send Message" ), QString::fromLatin1( "mail_send" ), 0,
+ this, SLOT( slotReplySend() ), coll, "chat_send" );
+ //Default to 'Return' for sending messages
+ d->chatSend->setShortcut( QKeySequence( Key_Return ) );
+
+ KStdAction::quit ( this, SLOT( slotCloseView() ), coll );
+
+ KStdAction::cut( d->editPart->widget(), SLOT( cut() ), coll );
+ KStdAction::copy( this, SLOT(slotCopy()), coll);
+ KStdAction::paste( d->editPart->widget(), SLOT( paste() ), coll );
+
+ new KAction( i18n( "&Set Font..." ), QString::fromLatin1( "charset" ), 0,
+ d->editPart, SLOT( setFont() ), coll, "format_font" );
+ new KAction( i18n( "Set Text &Color..." ), QString::fromLatin1( "pencil" ), 0,
+ d->editPart, SLOT( setFgColor() ), coll, "format_color" );
+ new KAction( i18n( "Set &Background Color..." ), QString::fromLatin1( "fill" ), 0,
+ d->editPart, SLOT( setBgColor() ), coll, "format_bgcolor" );
+
+ KStdAction::showMenubar( this, SLOT( slotViewMenuBar() ), coll );
+ setStandardToolBarMenuEnabled( true );
+
+ d->actionSmileyMenu = new KopeteEmoticonAction( coll, "format_smiley" );
+ d->actionSmileyMenu->setDelayed( false );
+ connect(d->actionSmileyMenu, SIGNAL(activated(const QString &)), this, SLOT(slotSmileyActivated(const QString &)));
+
+ // add configure key bindings menu item
+ KStdAction::keyBindings( guiFactory(), SLOT( configureShortcuts() ), coll );
+ KStdAction::configureToolbars(this, SLOT( slotConfToolbar() ), coll);
+ //FIXME: no longer works?
+ KopeteStdAction::preferences( coll , "settings_prefs" );
+
+ // The animated toolbarbutton
+ d->normalIcon = QPixmap( BarIcon( QString::fromLatin1( "kopete" ) ) );
+ d->animIcon = KGlobal::iconLoader()->loadMovie( QString::fromLatin1( "newmessage" ), KIcon::Toolbar);
+ d->animIcon.pause();
+
+ d->anim = new QLabel( this, "kde toolbar widget" );
+ d->anim->setMargin( 5 );
+ d->anim->setPixmap( d->normalIcon );
+ new KWidgetAction( d->anim, i18n("Toolbar Animation"), 0, 0, 0, coll, "toolbar_animation" );
+
+ setXMLFile( QString::fromLatin1( "kopeteemailwindow.rc" ) );
+ createGUI( d->editPart );
+ //createGUI( QString::fromLatin1( "kopeteemailwindow.rc" ) );
+ guiFactory()->addClient(m_manager);
+}
+
+void KopeteEmailWindow::closeEvent( QCloseEvent *e )
+{
+ // DO NOT call base class's closeEvent - see comment in KopeteApplication constructor for reason
+
+ // Save settings if auto-save is enabled, and settings have changed
+ if ( settingsDirty() && autoSaveSettings() )
+ saveAutoSaveSettings();
+
+ e->accept();
+}
+
+void KopeteEmailWindow::slotViewMenuBar()
+{
+ if( !menuBar()->isHidden() )
+ menuBar()->hide();
+ else
+ menuBar()->show();
+}
+
+void KopeteEmailWindow::slotSmileyActivated(const QString &sm )
+{
+ if ( !sm.isNull() )
+ d->editPart->addText( sm );
+}
+
+void KopeteEmailWindow::slotConfToolbar()
+{
+ saveMainWindowSettings(KGlobal::config(), QString::fromLatin1( "KopeteEmailWindow" ));
+ KEditToolbar *dlg = new KEditToolbar(actionCollection(), QString::fromLatin1("kopeteemailwindow.rc") );
+ if (dlg->exec())
+ {
+ createGUI( d->editPart );
+ applyMainWindowSettings(KGlobal::config(), QString::fromLatin1( "KopeteEmailWindow" ));
+ }
+ delete dlg;
+}
+
+void KopeteEmailWindow::slotCopy()
+{
+// kdDebug(14010) << k_funcinfo << endl;
+
+ if ( d->messagePart->hasSelection() )
+ d->messagePart->copy();
+ else
+ d->editPart->widget()->copy();
+}
+
+void KopeteEmailWindow::appendMessage(Kopete::Message &message)
+{
+ if( message.from() != m_manager->myself() )
+ {
+ if( d->mode == Send )
+ toggleMode( Reply );
+
+ d->messageQueue.append( message );
+
+ if( !d->showingMessage )
+ slotReadNext();
+ else
+ {
+ d->btnReadNext->setPaletteForegroundColor( QColor("red") );
+ updateNextButton();
+ }
+
+ d->unreadMessageFrom = message.from()->metaContact() ?
+ message.from()->metaContact()->displayName() : message.from()->contactId();
+ QTimer::singleShot( 1000, this, SLOT(slotMarkMessageRead()) );
+ }
+}
+
+void KopeteEmailWindow::slotMarkMessageRead()
+{
+ d->unreadMessageFrom = QString::null;
+}
+
+void KopeteEmailWindow::updateNextButton()
+{
+ if( d->queuePosition == d->messageQueue.count() )
+ {
+ d->btnReadNext->setEnabled( false );
+
+ d->btnReadNext->setPaletteForegroundColor( KGlobalSettings::textColor() );
+ }
+ else
+ d->btnReadNext->setEnabled( true );
+
+ if( d->queuePosition == 1 )
+ d->btnReadPrev->setEnabled( false );
+ else
+ d->btnReadPrev->setEnabled( true );
+
+ d->btnReadNext->setText( i18n( "(%1) Next >>" ).arg( d->messageQueue.count() - d->queuePosition ) );
+}
+
+void KopeteEmailWindow::slotUpdateReplySend()
+{
+ bool canSend;
+ if( d->mode == Read )
+ canSend = true;
+ else
+ canSend = d->editPart->canSend();
+
+ d->btnReplySend->setEnabled( canSend );
+ d->chatSend->setEnabled( canSend );
+}
+
+void KopeteEmailWindow::slotReadNext()
+{
+// kdDebug(14010) << k_funcinfo << endl;
+
+ d->showingMessage = true;
+
+ d->queuePosition++;
+
+ writeMessage( (*d->messageQueue.at( d->queuePosition - 1 )) );
+
+ updateNextButton();
+}
+
+void KopeteEmailWindow::slotReadPrev()
+{
+// kdDebug(14010) << k_funcinfo << endl;
+
+ d->showingMessage = true;
+
+ d->queuePosition--;
+
+ writeMessage( (*d->messageQueue.at( d->queuePosition - 1 )) );
+
+ updateNextButton();
+}
+
+void KopeteEmailWindow::writeMessage( Kopete::Message &msg )
+{
+ d->messagePart->clear();
+ d->messagePart->appendMessage( msg );
+}
+
+void KopeteEmailWindow::sendMessage()
+{
+ if ( !d->editPart->canSend() )
+ return;
+ d->sendInProgress = true;
+ d->anim->setMovie( d->animIcon );
+ d->animIcon.unpause();
+ d->editPart->widget()->setEnabled( false );
+ d->editPart->sendMessage();
+}
+
+void KopeteEmailWindow::messageSentSuccessfully()
+{
+ d->sendInProgress = false;
+ d->anim->setPixmap( d->normalIcon );
+ d->animIcon.pause();
+ closeView();
+}
+
+bool KopeteEmailWindow::closeView( bool force )
+{
+ int response = KMessageBox::Continue;
+
+ if( !force )
+ {
+ if( m_manager->members().count() > 1 )
+ {
+ QString shortCaption = caption();
+ if( shortCaption.length() > 40 )
+ shortCaption = shortCaption.left( 40 ) + QString::fromLatin1("...");
+
+ response = KMessageBox::warningContinueCancel(this, i18n("<qt>You are about to leave the group chat session <b>%1</b>.<br>"
+ "You will not receive future messages from this conversation.</qt>").arg(shortCaption), i18n("Closing Group Chat"),
+ i18n("Cl&ose Chat"), QString::fromLatin1("AskCloseGroupChat"));
+ }
+
+ if( !d->unreadMessageFrom.isNull() && ( response == KMessageBox::Continue ) )
+ {
+ response = KMessageBox::warningContinueCancel(this, i18n("<qt>You have received a message from <b>%1</b> in the last "
+ "second. Are you sure you want to close this chat?</qt>").arg(d->unreadMessageFrom), i18n("Unread Message"),
+ i18n("Cl&ose Chat"), QString::fromLatin1("AskCloseChatRecentMessage"));
+ }
+
+ if( d->sendInProgress && ( response == KMessageBox::Continue ) )
+ {
+ response = KMessageBox::warningContinueCancel(this, i18n("You have a message send in progress, which will be "
+ "aborted if this chat is closed. Are you sure you want to close this chat?"), i18n("Message in Transit"),
+ i18n("Cl&ose Chat"), QString::fromLatin1("AskCloseChatMessageInProgress") );
+ }
+ }
+
+ if( response == KMessageBox::Continue )
+ {
+ d->visible = false;
+ deleteLater();
+ return true;
+ }
+ else
+ {
+ d->editPart->widget()->setEnabled( true );
+ }
+
+ return false;
+}
+
+void KopeteEmailWindow::toggleMode( WindowMode newMode )
+{
+ d->mode = newMode;
+
+ switch( d->mode )
+ {
+ case Send:
+ d->btnReplySend->setText( i18n( "Send" ) );
+ d->editPart->widget()->show();
+ d->messagePart->view()->hide();
+ d->btnReadNext->hide();
+ d->btnReadPrev->hide();
+ break;
+ case Read:
+ d->btnReplySend->setText( i18n( "Reply" ) );
+ d->editPart->widget()->hide();
+ d->messagePart->view()->show();
+ d->btnReadNext->show();
+ d->btnReadPrev->show();
+ break;
+ case Reply:
+ QValueList<int> splitPercent;
+ // FIXME: should be saved and restored
+ splitPercent.append(50);
+ splitPercent.append(50);
+ d->btnReplySend->setText( i18n( "Send" ) );
+ d->editPart->widget()->show();
+ d->messagePart->view()->show();
+ d->btnReadNext->show();
+ d->btnReadPrev->show();
+ d->split->setSizes( splitPercent );
+ d->editPart->widget()->setFocus();
+ break;
+ }
+ slotUpdateReplySend();
+}
+
+void KopeteEmailWindow::slotReplySend()
+{
+ if( d->mode == Read )
+ toggleMode( Reply );
+ else
+ sendMessage();
+}
+
+//FIXME: Activate bool no longer needed due to setActiveWindow not being allowed
+void KopeteEmailWindow::raise(bool activate)
+{
+ makeVisible();
+
+ if ( !KWin::windowInfo( winId(), NET::WMDesktop ).onAllDesktops() )
+ KWin::setOnDesktop( winId(), KWin::currentDesktop() );
+
+ KMainWindow::raise();
+
+ /* Removed Nov 2003
+ According to Zack, the user double-clicking a contact is not valid reason for a non-pager
+ to grab window focus. While I don't agree with this, and it runs contradictory to every other
+ IM out there, commenting this code out to agree with KWin policy.
+
+ Redirect any bugs relating to the widnow now not grabbing focus on clicking a contact to KWin.
+ - Jason K
+ */
+
+ //Will not activate window if user was typing
+ if(activate)
+ KWin::activateWindow( winId() );
+}
+
+void KopeteEmailWindow::windowActivationChange( bool )
+{
+ if( isActiveWindow() )
+ emit( activated( static_cast<KopeteView*>(this) ) );
+}
+
+void KopeteEmailWindow::makeVisible()
+{
+// kdDebug(14010) << k_funcinfo << endl;
+ d->visible = true;
+ show();
+}
+
+bool KopeteEmailWindow::isVisible()
+{
+ return d->visible;
+}
+
+Kopete::Message KopeteEmailWindow::currentMessage()
+{
+ return d->editPart->contents();
+}
+
+void KopeteEmailWindow::setCurrentMessage( const Kopete::Message &newMessage )
+{
+ d->editPart->setContents( newMessage );
+}
+
+void KopeteEmailWindow::slotCloseView()
+{
+ closeView();
+}
+
+
+#include "kopeteemailwindow.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopeteemailwindow.h b/kopete/kopete/chatwindow/kopeteemailwindow.h
new file mode 100644
index 00000000..43908645
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeteemailwindow.h
@@ -0,0 +1,104 @@
+/*
+ kopeteemailwindow.h - Kopete "email window" for single-shot messages
+
+ Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEEMAILWINDOW_H
+#define KOPETEEMAILWINDOW_H
+
+#include "kopeteview.h"
+#include "kopeteviewplugin.h"
+#include <kmainwindow.h>
+#include <kparts/mainwindow.h>
+
+namespace KParts { struct URLArgs; }
+class EmailWindowPlugin;
+
+class KopeteEmailWindow : KParts::MainWindow, public KopeteView
+{
+ Q_OBJECT
+
+public:
+ enum WindowMode { Send, Read, Reply };
+
+ KopeteEmailWindow( Kopete::ChatSession *, EmailWindowPlugin *parent, bool foreignMessage );
+ ~KopeteEmailWindow();
+
+ virtual Kopete::Message currentMessage();
+ virtual void setCurrentMessage( const Kopete::Message &newMessage );
+ virtual void raise(bool activate=false);
+ virtual void makeVisible();
+ virtual bool closeView( bool force = false );
+ virtual bool isVisible();
+ virtual QWidget *mainWidget() { return this; }
+
+public slots:
+ virtual void sendMessage();
+ virtual void appendMessage( Kopete::Message &message );
+ virtual void messageSentSuccessfully();
+
+signals:
+ virtual void shown();
+ virtual void messageSent( Kopete::Message &message );
+ virtual void closing( KopeteView *view );
+ virtual void activated( KopeteView *view );
+
+protected:
+ virtual void closeEvent( QCloseEvent *e );
+ virtual void windowActivationChange( bool activated );
+
+private slots:
+ void slotReplySend();
+ void slotUpdateReplySend();
+ void slotReadNext();
+ void slotReadPrev();
+ void slotCloseView();
+
+ void slotSmileyActivated( const QString & );
+ void slotCopy();
+
+ void slotViewMenuBar();
+
+ void slotConfToolbar();
+
+ void slotMarkMessageRead();
+
+private:
+ class Private;
+ Private *d;
+
+ void toggleMode( WindowMode );
+ void updateNextButton();
+ void initActions();
+ void writeMessage( Kopete::Message & );
+};
+
+
+/**
+ * This is the class that makes the emailwindow a plugin
+ */
+class EmailWindowPlugin : public Kopete::ViewPlugin
+{
+ public:
+ EmailWindowPlugin(QObject *parent, const char *name, const QStringList &args);
+ KopeteView* createView( Kopete::ChatSession *manager );
+};
+
+#endif // __KOPETEEMAILWINDOW_H__
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopeteemailwindow.rc b/kopete/kopete/chatwindow/kopeteemailwindow.rc
new file mode 100644
index 00000000..23bc6a7f
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeteemailwindow.rc
@@ -0,0 +1,43 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="16" name="kopeteemailwindow">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="chat_send" />
+ <Separator lineSeparator="true"/>
+ <Merge />
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Action name="file_quit" />
+ </Menu>
+
+ <Menu name="format" >
+ <text>&amp;Format</text>
+ <Action name="format_font" />
+ <Action name="format_color" />
+ <Action name="format_bgcolor" />
+ <Action name="format_smiley"/>
+ </Menu>
+
+ <Menu name="settings">
+ <text>&amp;Settings</text>
+ </Menu>
+
+ <Merge/>
+ </MenuBar>
+
+ <ToolBar name="mainToolBar" fullWidth="true" >
+ <text>Main Toolbar</text>
+ <Action name="format_font" />
+ <Action name="format_color" />
+ <Action name="format_bgcolor" />
+ <Action name="format_smiley"/>
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Merge/>
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Action name="toolbar_animation"/>
+ </ToolBar>
+
+ <ToolBar name="statusToolBar">
+ <text>Status</text>
+ </ToolBar>
+</kpartgui>
diff --git a/kopete/kopete/chatwindow/kopeteemoticonaction.cpp b/kopete/kopete/chatwindow/kopeteemoticonaction.cpp
new file mode 100644
index 00000000..3e14e66d
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeteemoticonaction.cpp
@@ -0,0 +1,228 @@
+/*
+ kopeteemoticonaction.cpp
+
+ KAction to show the emoticon selector
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteemoticonaction.h"
+
+#include <math.h>
+
+#include <qwhatsthis.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmenubar.h>
+#include <kpopupmenu.h>
+#include <ktoolbar.h>
+#include <ktoolbarbutton.h>
+
+#include "emoticonselector.h"
+#include "kopeteemoticons.h"
+
+class KopeteEmoticonAction::KopeteEmoticonActionPrivate
+{
+public:
+ KopeteEmoticonActionPrivate()
+ {
+ m_delayed = true;
+ m_stickyMenu = true;
+ m_popup = new KPopupMenu(0L,"KopeteEmoticonActionPrivate::m_popup");
+ emoticonSelector = new EmoticonSelector( m_popup, "KopeteEmoticonActionPrivate::emoticonSelector");
+ m_popup->insertItem( emoticonSelector );
+ // TODO: Maybe connect to kopeteprefs and redo list only on config changes
+ connect( m_popup, SIGNAL( aboutToShow() ), emoticonSelector, SLOT( prepareList() ) );
+ }
+
+ ~KopeteEmoticonActionPrivate()
+ {
+ delete m_popup;
+ m_popup = 0;
+ }
+
+ KPopupMenu *m_popup;
+ EmoticonSelector *emoticonSelector;
+ bool m_delayed;
+ bool m_stickyMenu;
+};
+
+KopeteEmoticonAction::KopeteEmoticonAction( QObject* parent, const char* name )
+ : KAction( i18n( "Add Smiley" ), 0, parent, name )
+{
+ d = new KopeteEmoticonActionPrivate;
+
+ // Try to load the icon for our current emoticon theme, when it fails
+ // fall back to our own default
+ QString icon;
+ QMap<QString, QStringList> emoticonsMap = Kopete::Emoticons::self()->emoticonAndPicList();
+ for( QMap<QString, QStringList>::const_iterator it = emoticonsMap.constBegin();
+ it != emoticonsMap.constEnd(); ++it )
+ {
+ if( ( *it ).contains( ":)" ) || ( *it ).contains( ":-)" ) )
+ {
+ icon = it.key();
+ break;
+ }
+ }
+
+ if ( icon.isNull() )
+ setIcon( "emoticon" );
+ else
+ setIconSet( QIconSet( icon ) );
+
+ setShortcutConfigurable( false );
+ connect( d->emoticonSelector, SIGNAL( ItemSelected( const QString & ) ),
+ this, SIGNAL( activated( const QString & ) ) );
+}
+
+KopeteEmoticonAction::~KopeteEmoticonAction()
+{
+ unplugAll();
+// kdDebug(14010) << "KopeteEmoticonAction::~KopeteEmoticonAction()" << endl;
+ delete d;
+ d = 0;
+}
+
+void KopeteEmoticonAction::popup( const QPoint& global )
+{
+ popupMenu()->popup( global );
+}
+
+KPopupMenu* KopeteEmoticonAction::popupMenu() const
+{
+ return d->m_popup;
+}
+
+bool KopeteEmoticonAction::delayed() const
+{
+ return d->m_delayed;
+}
+
+void KopeteEmoticonAction::setDelayed(bool _delayed)
+{
+ d->m_delayed = _delayed;
+}
+
+bool KopeteEmoticonAction::stickyMenu() const
+{
+ return d->m_stickyMenu;
+}
+
+void KopeteEmoticonAction::setStickyMenu(bool sticky)
+{
+ d->m_stickyMenu = sticky;
+}
+
+int KopeteEmoticonAction::plug( QWidget* widget, int index )
+{
+ if (kapp && !kapp->authorizeKAction(name()))
+ return -1;
+
+// kdDebug(14010) << "KopeteEmoticonAction::plug( " << widget << ", " << index << " )" << endl;
+
+ // KDE4/Qt TODO: Use qobject_cast instead.
+ if ( widget->inherits("QPopupMenu") )
+ {
+ QPopupMenu* menu = static_cast<QPopupMenu*>( widget );
+ int id;
+ if ( hasIcon() )
+ id = menu->insertItem( iconSet(KIcon::Small), text(), d->m_popup, -1, index );
+ else
+ id = menu->insertItem( text(), d->m_popup, -1, index );
+
+ if ( !isEnabled() )
+ menu->setItemEnabled( id, false );
+
+ addContainer( menu, id );
+ connect( menu, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+
+ if ( m_parentCollection )
+ m_parentCollection->connectHighlight( menu, this );
+
+ return containerCount() - 1;
+ }
+ // KDE4/Qt TODO: Use qobject_cast instead.
+ else if ( widget->inherits( "KToolBar" ) )
+ {
+ KToolBar *bar = static_cast<KToolBar *>( widget );
+
+ int id_ = KAction::getToolButtonID();
+
+ if ( icon().isEmpty() && !iconSet(KIcon::Small).isNull() )
+ {
+ bar->insertButton(
+ iconSet(KIcon::Small).pixmap(), id_, SIGNAL(clicked()), this,
+ SLOT(slotActivated()), isEnabled(), plainText(),
+ index );
+ }
+ else
+ {
+ KInstance *instance;
+
+ if ( m_parentCollection )
+ instance = m_parentCollection->instance();
+ else
+ instance = KGlobal::instance();
+
+ bar->insertButton( icon(), id_, SIGNAL( clicked() ), this,
+ SLOT( slotActivated() ), isEnabled(), plainText(),
+ index, instance );
+ }
+
+ addContainer( bar, id_ );
+
+ if (!whatsThis().isEmpty())
+ QWhatsThis::add( bar->getButton(id_), whatsThis() );
+
+ connect( bar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+
+ if (delayed())
+ bar->setDelayedPopup(id_, popupMenu(), stickyMenu());
+ else
+ bar->getButton(id_)->setPopup(popupMenu(), stickyMenu());
+
+ if ( m_parentCollection )
+ m_parentCollection->connectHighlight(bar, this);
+
+ return containerCount() - 1;
+ }
+ // KDE4/Qt TODO: Use qobject_cast instead.
+ else if ( widget->inherits( "QMenuBar" ) )
+ {
+ QMenuBar *bar = static_cast<QMenuBar *>( widget );
+
+ int id;
+
+ id = bar->insertItem( text(), popupMenu(), -1, index );
+
+ if ( !isEnabled() )
+ bar->setItemEnabled( id, false );
+
+ addContainer( bar, id );
+ connect( bar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+
+ return containerCount() - 1;
+ }
+
+ return -1;
+}
+
+#include "kopeteemoticonaction.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopeteemoticonaction.h b/kopete/kopete/chatwindow/kopeteemoticonaction.h
new file mode 100644
index 00000000..d420518a
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeteemoticonaction.h
@@ -0,0 +1,90 @@
+/*
+ kopeteemoticonaction.h
+
+ KAction to show the emoticon selector
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETEEMOTICONACTION_H_
+#define _KOPETEEMOTICONACTION_H_
+
+#include <kaction.h>
+
+class KopeteEmoticonAction : public KAction
+{
+ Q_OBJECT
+
+ Q_PROPERTY( bool delayed READ delayed WRITE setDelayed )
+ Q_PROPERTY( bool stickyMenu READ stickyMenu WRITE setStickyMenu )
+
+public:
+ KopeteEmoticonAction( QObject *parent = 0, const char *name = 0 );
+ virtual ~KopeteEmoticonAction();
+
+ KPopupMenu * popupMenu() const;
+ void popup( const QPoint &global );
+
+ /**
+ * Returns true if this action creates a delayed popup menu
+ * when plugged in a KToolbar.
+ */
+ bool delayed() const;
+
+ /**
+ * If set to true, this action will create a delayed popup menu
+ * when plugged in a KToolbar. Otherwise it creates a normal popup.
+ * Default: delayed
+ *
+ * Remember that if the "main" action (the toolbar button itself)
+ * cannot be clicked, then you should call setDelayed(false).
+ *
+ * On the opposite, if the main action can be clicked, it can only happen
+ * in a toolbar: in a menu, the parent of a submenu can't be activated.
+ * To get a "normal" menu item when plugged a menu (and no submenu)
+ * use KToolBarPopupAction.
+ */
+ void setDelayed( bool delayed );
+
+ /**
+ * Returns true if this action creates a sticky popup menu.
+ * See @ref setStickyMenu.
+ */
+ bool stickyMenu() const;
+
+ /**
+ * If set to true, this action will create a sticky popup menu
+ * when plugged in a KToolbar.
+ * "Sticky", means it's visible until a selection is made or the mouse is
+ * clicked elsewhere. This feature allows you to make a selection without
+ * having to press and hold down the mouse while making a selection.
+ * Default: sticky.
+ */
+ void setStickyMenu( bool sticky );
+
+ virtual int plug( QWidget* widget, int index = -1 );
+
+signals:
+ void activated( const QString &item );
+
+private:
+ class KopeteEmoticonActionPrivate;
+ KopeteEmoticonActionPrivate *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/chatwindow/kopeterichtexteditpartfull.rc b/kopete/kopete/chatwindow/kopeterichtexteditpartfull.rc
new file mode 100644
index 00000000..4031e4d7
--- /dev/null
+++ b/kopete/kopete/chatwindow/kopeterichtexteditpartfull.rc
@@ -0,0 +1,49 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="krichtexteditpart" version="7">
+<MenuBar>
+ <Menu name="format"><text>F&amp;ormat</text>
+ <Action name="format_bold"/>
+ <Action name="format_italic"/>
+ <Action name="format_underline"/>
+ <Action name="format_color"/>
+ <Action name="format_bgcolor"/>
+ <Separator/>
+ <Menu name="alignment"><text>&amp;Alignment</text>
+ <Action name="format_align_left"/>
+ <Action name="format_align_center"/>
+ <Action name="format_align_right"/>
+ <Action name="format_align_justify"/>
+ </Menu>
+ <Separator/>
+ <Action name="format_font_size"/>
+ <Action name="format_font"/>
+ </Menu>
+ <Merge />
+</MenuBar>
+
+<ToolBar name="mainToolBar"><text>Main Toolbar</text>
+ <Separator weakSeparator="true" lineSeparator="true"/>
+ <Action name="enableRichText"/>
+ <Separator/>
+ <Action name="check_spelling"/>
+ <Separator weakSeparator="true" lineSeparator="true"/>
+</ToolBar>
+
+<ToolBar name="formatToolBar"><text>Format Toolbar</text>
+ <Action name="format_bold"/>
+ <Action name="format_italic"/>
+ <Action name="format_underline"/>
+ <Action name="format_color"/>
+ <Action name="format_bgcolor"/>
+ <Separator lineSeparator="true"/>
+ <Action name="format_font"/>
+ <Action name="format_font_size"/>
+ <Separator lineSeparator="true"/>
+ <Action name="format_align_left"/>
+ <Action name="format_align_center"/>
+ <Action name="format_align_right"/>
+ <Action name="format_align_justify"/>
+</ToolBar>
+</kpartgui>
+
+
diff --git a/kopete/kopete/chatwindow/krichtexteditpart.cpp b/kopete/kopete/chatwindow/krichtexteditpart.cpp
new file mode 100644
index 00000000..001096b9
--- /dev/null
+++ b/kopete/kopete/chatwindow/krichtexteditpart.cpp
@@ -0,0 +1,550 @@
+#include <ktextedit.h>
+#include <kaction.h>
+#include <kcolordialog.h>
+#include <kglobalsettings.h>
+#include <kfontdialog.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qevent.h>
+#include <kparts/genericfactory.h>
+#include <private/qrichtext_p.h>
+
+#include "krichtexteditpart.h"
+#include "krichtexteditpart.moc"
+#include "kopeteprotocol.h"
+#include "kopeteprefs.h"
+
+typedef KParts::GenericFactory<KopeteRichTextEditPart> KopeteRichTextEditPartFactory;
+K_EXPORT_COMPONENT_FACTORY( libkopeterichtexteditpart, KopeteRichTextEditPartFactory )
+
+class KopeteTextEdit : public KTextEdit
+{
+public:
+ KopeteTextEdit( QWidget *parent ) : KTextEdit( parent ) {}
+ const QTextCursor * cursor() { return textCursor(); }
+ bool event(QEvent *event)
+ {
+ // don't allow QTextEdit to override accels
+ if ( event->type() == QEvent::AccelOverride )
+ return QWidget::event(event);
+ else
+ return KTextEdit::event(event);
+ }
+};
+
+KopeteRichTextEditPart::KopeteRichTextEditPart( QWidget *wparent, const char *wname, QObject*, const char*, const QStringList& )
+ : KParts::ReadOnlyPart( wparent, wname ? wname : "rich_text_part" )
+{
+ KopeteRichTextEditPart::KopeteRichTextEditPart( wparent, wname, false );
+}
+
+KopeteRichTextEditPart::KopeteRichTextEditPart( QWidget *parent, const char *name, int capabilities )
+ : KParts::ReadOnlyPart( parent, name ? name : "rich_text_part" ),
+ m_capabilities( capabilities ),
+ m_richTextEnabled( true )
+{
+ // we need an instance
+ setInstance( KopeteRichTextEditPartFactory::instance() );
+
+ editor = new KopeteTextEdit( parent );
+ editor->setReadOnly( false );
+
+ setWidget( editor );
+
+ m_richTextAvailable = (
+ m_capabilities & Kopete::Protocol::RichFormatting ||
+ m_capabilities & Kopete::Protocol::Alignment ||
+ m_capabilities & Kopete::Protocol::RichFont ||
+ m_capabilities & Kopete::Protocol::RichColor
+ );
+
+ createActions();
+
+ setXMLFile( "kopeterichtexteditpartfull.rc" );
+ enableRichText->setEnabled( m_richTextAvailable );
+ enableRichText->setChecked( m_richTextAvailable );
+ slotSetRichTextEnabled( m_richTextAvailable );
+
+ //Set colors, font
+ readConfig();
+}
+
+void KopeteRichTextEditPart::slotSetRichTextEnabled( bool enable )
+{
+ m_richTextEnabled = enable && m_richTextAvailable;
+
+ if( m_richTextEnabled )
+ {
+ editor->setTextFormat( Qt::RichText );
+ }
+ else
+ {
+ editor->setTextFormat( Qt::PlainText );
+ }
+
+ emit toggleToolbar( buttonsEnabled() );
+
+ // Spellchecking disabled when using rich text because the
+ // text we were getting from widget was coloured HTML!
+ editor->setCheckSpellingEnabled( !m_richTextEnabled );
+ checkSpelling->setEnabled( !m_richTextEnabled );
+
+ //Enable / disable buttons
+ updateActions();
+ enableRichText->setChecked( m_richTextEnabled );
+}
+
+void KopeteRichTextEditPart::checkToolbarEnabled()
+{
+ emit toggleToolbar( buttonsEnabled() );
+}
+
+void KopeteRichTextEditPart::reloadConfig()
+{
+ readConfig();
+}
+
+void KopeteRichTextEditPart::createActions()
+{
+ createActions( actionCollection() );
+}
+
+KAboutData *KopeteRichTextEditPart::createAboutData()
+{
+ KAboutData *aboutData = new KAboutData("kopeterichtexteditpart", I18N_NOOP("KopeteRichTextEditPart"), "0.1",
+ I18N_NOOP("A simple rich text editor part for Kopete"),
+ KAboutData::License_LGPL );
+ aboutData->addAuthor("Richard J. Moore", 0, "rich@kde.org", "http://xmelegance.org/" );
+ aboutData->addAuthor("Jason Keirstead", 0, "jason@keirstead.org", "http://www.keirstead.org/" );
+ return aboutData;
+}
+
+void KopeteRichTextEditPart::createActions( KActionCollection *ac )
+{
+ enableRichText = new KToggleAction(i18n("Enable &Rich Text"), "pencil", 0,
+ ac, "enableRichText" );
+ enableRichText->setCheckedState(i18n("Disable &Rich Text"));
+ connect( enableRichText, SIGNAL( toggled(bool) ),
+ this, SLOT( slotSetRichTextEnabled(bool) ) );
+
+ checkSpelling = new KAction( i18n("Check &Spelling"), "spellcheck", 0,
+ editor, SLOT( checkSpelling() ), ac, "check_spelling" );
+
+ //Fg Color
+ actionFgColor = new KAction( i18n("Text &Color..."), "color_line", 0,
+ this, SLOT( setFgColor() ),
+ ac, "format_color" );
+
+ //BG Color
+ actionBgColor = new KAction( i18n("Background Co&lor..."), "color_fill", 0,
+ this, SLOT( setBgColor() ),
+ ac, "format_bgcolor" );
+
+ //Font Family
+ action_font = new KFontAction( i18n("&Font"), 0,
+ ac, "format_font" );
+ connect( action_font, SIGNAL( activated( const QString & ) ),
+ this, SLOT( setFont( const QString & ) ) );
+
+ //Font Size
+ action_font_size = new KFontSizeAction( i18n("Font &Size"), 0,
+ ac, "format_font_size" );
+ connect( action_font_size, SIGNAL( fontSizeChanged(int) ),
+ this, SLOT( setFontSize(int) ) );
+
+ //Formatting
+ action_bold = new KToggleAction( i18n("&Bold"), "text_bold", CTRL+Key_B,
+ ac, "format_bold" );
+ connect( action_bold, SIGNAL( toggled(bool) ),
+ this, SLOT( setBold(bool) ) );
+
+ action_italic = new KToggleAction( i18n("&Italic"), "text_italic", CTRL+Key_I,
+ ac, "format_italic" );
+ connect( action_italic, SIGNAL( toggled(bool) ),
+ this, SLOT( setItalic(bool) ) );
+
+ action_underline = new KToggleAction( i18n("&Underline"), "text_under", CTRL+Key_U,
+ ac, "format_underline" );
+ connect( action_underline, SIGNAL( toggled(bool) ),
+ this, SLOT( setUnderline(bool) ) );
+
+ connect( editor, SIGNAL( currentFontChanged( const QFont & ) ),
+ this, SLOT( updateCharFmt() ) );
+ updateCharFmt();
+
+ connect( editor, SIGNAL( currentFontChanged( const QFont & ) ),
+ this, SLOT( updateFont() ) );
+ updateFont();
+
+ //Alignment
+ action_align_left = new KToggleAction( i18n("Align &Left"), "text_left", 0,
+ ac, "format_align_left" );
+ connect( action_align_left, SIGNAL( toggled(bool) ),
+ this, SLOT( setAlignLeft(bool) ) );
+
+ action_align_center = new KToggleAction( i18n("Align &Center"), "text_center", 0,
+ ac, "format_align_center" );
+ connect( action_align_center, SIGNAL( toggled(bool) ),
+ this, SLOT( setAlignCenter(bool) ) );
+
+ action_align_right = new KToggleAction( i18n("Align &Right"), "text_right", 0,
+ ac, "format_align_right" );
+ connect( action_align_right, SIGNAL( toggled(bool) ),
+ this, SLOT( setAlignRight(bool) ) );
+
+ action_align_justify = new KToggleAction( i18n("&Justify"), "text_block", 0,
+ ac, "format_align_justify" );
+ connect( action_align_justify, SIGNAL( toggled(bool) ),
+ this, SLOT( setAlignJustify(bool) ) );
+
+ action_align_left->setExclusiveGroup( "alignment" );
+ action_align_center->setExclusiveGroup( "alignment" );
+ action_align_right->setExclusiveGroup( "alignment" );
+ action_align_justify->setExclusiveGroup( "alignment" );
+
+ connect( editor, SIGNAL( cursorPositionChanged( int,int ) ),
+ this, SLOT( updateAligment() ) );
+
+ updateAligment();
+}
+
+void KopeteRichTextEditPart::updateActions()
+{
+ bool buttonsEnabled = this->buttonsEnabled();
+ bool enableFgColor = m_capabilities & Kopete::Protocol::BaseFgColor || m_capabilities & Kopete::Protocol::RichFgColor;
+ bool enableBGColor = m_capabilities & Kopete::Protocol::BaseBgColor || m_capabilities & Kopete::Protocol::RichBgColor;
+ bool activateAlignment = buttonsEnabled && ( m_capabilities & Kopete::Protocol::Alignment );
+ bool activateFont = m_capabilities & Kopete::Protocol::BaseFont || m_capabilities & Kopete::Protocol::RichFont;
+
+ bool activateBFormat = m_capabilities & Kopete::Protocol::BaseBFormatting || m_capabilities & Kopete::Protocol::RichBFormatting;
+ bool activateUFormat = m_capabilities & Kopete::Protocol::BaseUFormatting || m_capabilities & Kopete::Protocol::RichUFormatting;
+ bool activateIFormat = m_capabilities & Kopete::Protocol::BaseIFormatting || m_capabilities & Kopete::Protocol::RichIFormatting;
+
+ actionFgColor->setEnabled( buttonsEnabled && enableFgColor );
+ actionBgColor->setEnabled( buttonsEnabled && enableBGColor );
+
+ action_font->setEnabled( buttonsEnabled && activateFont );
+ action_font_size->setEnabled( buttonsEnabled && activateFont );
+
+ action_bold->setEnabled( buttonsEnabled && activateBFormat );
+ action_italic->setEnabled( buttonsEnabled && activateIFormat );
+ action_underline->setEnabled( buttonsEnabled && activateUFormat );
+
+ action_align_left->setEnabled( activateAlignment );
+ action_align_center->setEnabled( activateAlignment );
+ action_align_right->setEnabled( activateAlignment );
+ action_align_justify->setEnabled( activateAlignment );
+}
+
+void KopeteRichTextEditPart::updateCharFmt()
+{
+ action_bold->setChecked( editor->bold() );
+ action_italic->setChecked( editor->italic() );
+ action_underline->setChecked( editor->underline() );
+}
+
+void KopeteRichTextEditPart::clear()
+{
+ editor->setText( QString::null );
+ setFont( mFont );
+ setFgColor( mFgColor );
+
+ if( m_capabilities & Kopete::Protocol::BaseBFormatting || m_capabilities & Kopete::Protocol::RichBFormatting )
+ {
+ editor->setBold( action_bold->isChecked() );
+ }
+ if( m_capabilities & Kopete::Protocol::BaseIFormatting || m_capabilities & Kopete::Protocol::RichIFormatting )
+ {
+ editor->setItalic( action_italic->isChecked() );
+ }
+ if( m_capabilities & Kopete::Protocol::BaseUFormatting || m_capabilities & Kopete::Protocol::RichUFormatting )
+ {
+ editor->setUnderline( action_underline->isChecked() );
+ }
+}
+
+void KopeteRichTextEditPart::updateAligment()
+{
+ int align = editor->alignment();
+
+ switch ( align )
+ {
+ case AlignRight:
+ action_align_right->setChecked( true );
+ break;
+ case AlignCenter:
+ action_align_center->setChecked( true );
+ break;
+ case AlignLeft:
+ action_align_left->setChecked( true );
+ break;
+ case AlignJustify:
+ action_align_justify->setChecked( true );
+ break;
+ default:
+ break;
+ }
+}
+
+void KopeteRichTextEditPart::updateFont()
+{
+ if ( editor->pointSize() > 0 )
+ action_font_size->setFontSize( editor->pointSize() );
+ action_font->setFont( editor->family() );
+}
+
+void KopeteRichTextEditPart::readConfig()
+{
+ // Don't update config untill we read whole config first
+ m_configWriteLock = true;
+ KConfig *config = KGlobal::config();
+ config->setGroup("RichTextEditor");
+
+ QColor tmpColor = KGlobalSettings::textColor();
+ setFgColor( config->readColorEntry("FgColor", &tmpColor ) );
+
+ tmpColor = KGlobalSettings::baseColor();
+ setBgColor( config->readColorEntry("BgColor", &tmpColor ) );
+
+ QFont tmpFont = KopetePrefs::prefs()->fontFace();
+ setFont( config->readFontEntry("Font", &tmpFont ) );
+
+ int tmp = KGlobalSettings::generalFont().pixelSize();
+ setFontSize( config->readNumEntry( "FontSize", tmp ) );
+
+ action_bold->setChecked( config->readBoolEntry( "FontBold" ) );
+ action_italic->setChecked( config->readBoolEntry( "FontItalic" ) );
+ action_underline->setChecked( config->readBoolEntry( "FontUnderline" ) );
+
+ switch( config->readNumEntry( "EditAlignment", AlignLeft ) )
+ {
+ case AlignLeft:
+ action_align_left->activate();
+ break;
+ case AlignCenter:
+ action_align_center->activate();
+ break;
+ case AlignRight:
+ action_align_right->activate();
+ break;
+ case AlignJustify:
+ action_align_justify->activate();
+ break;
+ }
+ m_configWriteLock = false;
+}
+
+void KopeteRichTextEditPart::writeConfig()
+{
+ // If true we're still reading the conf write now, so don't write.
+ if( m_configWriteLock ) return;
+
+ KConfig *config = KGlobal::config();
+ config->setGroup("RichTextEditor");
+ config->writeEntry("Font", mFont );
+ config->writeEntry("FontSize", mFont.pointSize() );
+ config->writeEntry("FontBold", mFont.bold() );
+ config->writeEntry("FontItalic", mFont.italic() );
+ config->writeEntry("FontUnderline", mFont.underline() );
+ config->writeEntry("BgColor", mBgColor );
+ config->writeEntry("FgColor", mFgColor );
+ config->writeEntry("EditAlignment", editor->alignment() );
+ config->sync();
+}
+
+void KopeteRichTextEditPart::setFgColor()
+{
+ QColor col=editor->color();
+
+ int s = KColorDialog::getColor( col, KGlobalSettings::textColor() , editor );
+ if(!col.isValid())
+ col= KGlobalSettings::textColor() ;
+ if ( s != QDialog::Accepted )
+ return;
+
+ setFgColor( col );
+
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setFgColor( const QColor &newColor )
+{
+ mFgColor = newColor;
+
+ if( !(m_capabilities & Kopete::Protocol::RichColor) )
+ {
+ QPalette pal = editor->palette();
+ pal.setColor(QPalette::Active, QColorGroup::Text, mFgColor );
+ pal.setColor(QPalette::Inactive, QColorGroup::Text, mFgColor );
+
+ if ( pal == QApplication::palette( editor ) )
+ editor->unsetPalette();
+ else
+ editor->setPalette(pal);
+ }
+
+ editor->setColor( mFgColor );
+}
+
+QColor KopeteRichTextEditPart::fgColor()
+{
+ if( mFgColor == KGlobalSettings::textColor())
+ return QColor();
+ return mFgColor;
+}
+
+void KopeteRichTextEditPart::setBgColor()
+{
+ QColor col=mBgColor;
+
+ int s = KColorDialog::getColor( col, KGlobalSettings::baseColor(), editor );
+ if(!col.isValid())
+ {
+ col=KGlobalSettings::baseColor();
+ }
+
+ if ( s != QDialog::Accepted )
+ return;
+
+ setBgColor( col );
+
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setBgColor( const QColor &newColor )
+{
+ mBgColor = newColor;
+
+ QPalette pal = editor->palette();
+ pal.setColor(QPalette::Active, QColorGroup::Base, mBgColor );
+ pal.setColor(QPalette::Inactive, QColorGroup::Base, mBgColor );
+ pal.setColor(QPalette::Disabled, QColorGroup::Base, mBgColor );
+
+ if ( pal == QApplication::palette( editor ) )
+ editor->unsetPalette();
+ else
+ editor->setPalette(pal);
+}
+
+QColor KopeteRichTextEditPart::bgColor()
+{
+ if( mBgColor == KGlobalSettings::baseColor())
+ return QColor();
+ return mBgColor;
+}
+
+void KopeteRichTextEditPart::setFontSize( int size )
+{
+ mFont.setPointSize( size );
+ if( m_capabilities & Kopete::Protocol::RichFont )
+ editor->setPointSize( size );
+ else if( m_capabilities & Kopete::Protocol::BaseFont)
+ editor->setFont( mFont );
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setFont()
+{
+ KFontDialog::getFont(mFont, false, editor);
+ setFont(mFont);
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setFont( const QFont &newFont )
+{
+ mFont = newFont;
+ editor->setFont(mFont);
+ updateFont();
+}
+
+void KopeteRichTextEditPart::setFont( const QString &newFont )
+{
+ mFont.setFamily( newFont );
+ if( m_capabilities & Kopete::Protocol::RichFont)
+ editor->setFamily( newFont );
+ else if( m_capabilities & Kopete::Protocol::BaseFont)
+ editor->setFont( mFont );
+ updateFont();
+ writeConfig();
+}
+
+
+void KopeteRichTextEditPart::setBold( bool b )
+{
+ mFont.setBold(b);
+ if( m_capabilities & Kopete::Protocol::RichBFormatting || m_capabilities & Kopete::Protocol::BaseBFormatting )
+ {
+ if( m_richTextEnabled )
+ editor->setBold(b);
+ else
+ editor->setFont(mFont);
+ }
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setItalic( bool b )
+{
+ mFont.setItalic( b );
+ if( m_capabilities & Kopete::Protocol::RichIFormatting || m_capabilities & Kopete::Protocol::BaseIFormatting )
+ {
+ if(m_richTextEnabled)
+ editor->setItalic(b);
+ else
+ editor->setFont(mFont);
+ }
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setUnderline( bool b )
+{
+ mFont.setUnderline( b );
+ if( m_capabilities & Kopete::Protocol::RichUFormatting || m_capabilities & Kopete::Protocol::BaseUFormatting )
+ {
+ if(m_richTextEnabled)
+ editor->setUnderline(b);
+ else
+ editor->setFont(mFont);
+ }
+ writeConfig();
+}
+
+
+void KopeteRichTextEditPart::setAlignLeft( bool yes )
+{
+ if ( yes )
+ editor->setAlignment( AlignLeft );
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setAlignRight( bool yes )
+{
+ if ( yes )
+ editor->setAlignment( AlignRight );
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setAlignCenter( bool yes )
+{
+ if ( yes )
+ editor->setAlignment( AlignCenter );
+ writeConfig();
+}
+
+void KopeteRichTextEditPart::setAlignJustify( bool yes )
+{
+ if ( yes )
+ editor->setAlignment( AlignJustify );
+ writeConfig();
+}
+
+QString KopeteRichTextEditPart::text( Qt::TextFormat fmt ) const
+{
+ if( fmt == editor->textFormat() || fmt != Qt::PlainText )
+ return editor->text();
+ else
+ return editor->cursor()->document()->plainText();
+}
+
diff --git a/kopete/kopete/chatwindow/krichtexteditpart.h b/kopete/kopete/chatwindow/krichtexteditpart.h
new file mode 100644
index 00000000..cd32993c
--- /dev/null
+++ b/kopete/kopete/chatwindow/krichtexteditpart.h
@@ -0,0 +1,139 @@
+// -*- c++ -*-
+
+#ifndef KRICHTEXTEDITPART_H
+#define KRICHTEXTEDITPART_H
+
+#include <kparts/part.h>
+
+#include <qfont.h>
+#include <qcolor.h>
+
+class KAboutData;
+class KTextEdit;
+class KFontAction;
+class KFontSizeAction;
+class KToggleAction;
+class KopeteTextEdit;
+
+/**
+ * KParts wrapper for QTextEdit.
+ *
+ * Originally by Richard Moore, rich@kde.org
+ * forked by Jason Keirstead
+ */
+class KopeteRichTextEditPart : public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+
+ public:
+ KopeteRichTextEditPart( QWidget *wparent, const char *wname, QObject*, const char*, const QStringList& );
+ KopeteRichTextEditPart( QWidget *wparent, const char *wname, int capabilities );
+
+ /**
+ * Returns the current editor widget.
+ */
+ KTextEdit *widget() const { return (KTextEdit*)editor; }
+
+ QString text( Qt::TextFormat = Qt::AutoText ) const;
+
+ QFont font() { return mFont; }
+
+ QColor fgColor();
+
+ QColor bgColor();
+
+ void clear();
+
+ int capabilities() { return m_capabilities; }
+
+ bool richTextEnabled() { return m_richTextAvailable && m_richTextEnabled; }
+
+ bool buttonsEnabled() { return !m_richTextAvailable || m_richTextEnabled; }
+
+ static KAboutData *createAboutData();
+
+ virtual bool openFile() { return false; };
+
+ public slots:
+
+ void setFgColor();
+ void setFgColor( const QColor & );
+
+ void setBgColor();
+ void setBgColor( const QColor & );
+
+ void setFont();
+ void setFont( const QFont & );
+ void setFont( const QString & );
+
+ void setFontSize( int );
+
+ void setUnderline( bool );
+ void setBold( bool );
+ void setItalic( bool );
+
+ void setAlignLeft( bool yes );
+ void setAlignRight( bool yes );
+ void setAlignCenter( bool yes );
+ void setAlignJustify( bool yes );
+
+ void checkToolbarEnabled();
+ void reloadConfig();
+ void slotSetRichTextEnabled( bool enable );
+
+ signals:
+ void toggleToolbar( bool enabled );
+
+ protected:
+ /**
+ * Creates the part's actions in the specified action collection.
+ */
+ virtual void createActions( KActionCollection *ac );
+
+ protected slots:
+
+ /**
+ * Creates the part's actions in the part's action collection.
+ */
+ void createActions();
+ void updateActions();
+
+ void updateFont();
+ void updateCharFmt();
+ void updateAligment();
+
+ private:
+ void readConfig();
+ void writeConfig();
+
+ KopeteTextEdit *editor;
+ KAction *checkSpelling;
+ KToggleAction *enableRichText;
+
+ KAction *actionFgColor;
+ KAction *actionBgColor;
+
+ KToggleAction *action_bold;
+ KToggleAction *action_italic;
+ KToggleAction *action_underline;
+
+ KFontAction *action_font;
+ KFontSizeAction *action_font_size;
+
+ KToggleAction *action_align_left;
+ KToggleAction *action_align_right;
+ KToggleAction *action_align_center;
+ KToggleAction *action_align_justify;
+
+ int m_capabilities;
+ bool m_richTextAvailable;
+ bool m_richTextEnabled;
+
+ bool m_configWriteLock;
+
+ QFont mFont;
+ QColor mBgColor;
+ QColor mFgColor;
+};
+
+#endif // KRICHTEXTEDITPART_H
diff --git a/kopete/kopete/chatwindow/tests/Makefile.am b/kopete/kopete/chatwindow/tests/Makefile.am
new file mode 100644
index 00000000..40a864d6
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/Makefile.am
@@ -0,0 +1,17 @@
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_ASCII_CAST -DQT_NO_COMPAT \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/kopete/chatwindow -I$(top_srcdir)/kopete/libkopete $(all_includes) -DSRCDIR=\"$(top_srcdir)/kopete/kopete/chatwindow/tests\"
+METASOURCES = AUTO
+check_LTLIBRARIES = kunittest_chatwindowstyle_test.la kunittest_chatwindowstylerendering_test.la
+
+kunittest_chatwindowstyle_test_la_SOURCES = chatwindowstyle_test.cpp
+kunittest_chatwindowstyle_test_la_LIBADD = -lkunittest ../libkopetechatwindow.la
+kunittest_chatwindowstyle_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+kunittest_chatwindowstylerendering_test_la_SOURCES = chatwindowstylerendering_test.cpp
+kunittest_chatwindowstylerendering_test_la_LIBADD = -lkunittest ../../../libkopete/libkopete.la ../libkopetechatwindow.la
+kunittest_chatwindowstylerendering_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+check-local:
+ kunittestmodrunner
+guicheck:
+ kunittestmod $(PWD)
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Info.plist b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Info.plist
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Info.plist
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Header.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Header.html
new file mode 100644
index 00000000..e743064c
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Header.html
@@ -0,0 +1,7 @@
+<div>%chatName%</div>
+<div>%sourceName%</div>
+<div>%destinationName%</div>
+<div>%incomingIconPath%</div>
+<div>%outgoingIconPath%</div>
+<div>%timeOpened%</div>
+<div>%timeOpened{%H:%M}%</div> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/Content.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..5a6ae0c0
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,9 @@
+Incoming:
+<div>%userIconPath%</div>
+<div>%senderScreenName%</div>
+<div>%sender%</div>
+<div>%service%</div>
+<div>%message%</div>
+<div>%time%</div>
+<div>%time{%H:%M}%</div>
+<div id="insert"> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/NextContent.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..017a2cad
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,3 @@
+Incoming:
+<div>%message%</div>
+<div id="insert"> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/Content.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..06bbd826
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,9 @@
+Outgoing:
+<div>%userIconPath%</div>
+<div>%senderScreenName%</div>
+<div>%sender%</div>
+<div>%service%</div>
+<div>%message%</div>
+<div>%time%</div>
+<div>%time{%H:%M}%</div>
+<div id="insert"> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/NextContent.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..8d3fa8d0
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,3 @@
+Outgoing:
+<div>%message%</div>
+<div id="insert"> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Status.html b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Status.html
new file mode 100644
index 00000000..71a77cd1
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Status.html
@@ -0,0 +1,3 @@
+<div>%message%</div>
+<div>%time%</div>
+<div>%time{%H:%M}%</div> \ No newline at end of file
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant1.css b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant1.css
new file mode 100644
index 00000000..8d1c8b69
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant1.css
@@ -0,0 +1 @@
+
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant2.css b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant2.css
new file mode 100644
index 00000000..8d1c8b69
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/Variants/Variant2.css
@@ -0,0 +1 @@
+
diff --git a/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/main.css b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/main.css
new file mode 100644
index 00000000..8d1c8b69
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/TestStyle/Contents/Resources/main.css
@@ -0,0 +1 @@
+
diff --git a/kopete/kopete/chatwindow/tests/chatwindowstyle_test.cpp b/kopete/kopete/chatwindow/tests/chatwindowstyle_test.cpp
new file mode 100644
index 00000000..26dba26b
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/chatwindowstyle_test.cpp
@@ -0,0 +1,132 @@
+/*
+ ChatWindowStyle test suite
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kunittest/module.h>
+#include "chatwindowstyle_test.h"
+
+#include <stdlib.h>
+
+#include <qdir.h>
+#include <qfile.h>
+
+#include <kopetechatwindowstyle.h>
+
+KUNITTEST_MODULE( kunittest_chatwindowstyle_test, "KopeteChatWindowTestSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( ChatWindowStyle_Test );
+
+void ChatWindowStyle_Test::allTests()
+{
+ testStyle = new ChatWindowStyle(QString(SRCDIR)+QString("/TestStyle"));
+
+ // change user data dir to avoid messing with user's .kde dir
+ setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kopete-unittest" ), true );
+
+ testPaths();
+ testHtml();
+ testVariants();
+ testAction();
+}
+
+void ChatWindowStyle_Test::testPaths()
+{
+ QString expectedStylePath = SRCDIR + QString::fromUtf8("/TestStyle");
+ QString expectedBaseHref = expectedStylePath + QString::fromUtf8("/Contents/Resources/");
+
+ CHECK(testStyle->getStylePath(), expectedStylePath);
+ CHECK(testStyle->getStyleBaseHref(), expectedBaseHref);
+}
+
+void ChatWindowStyle_Test::testHtml()
+{
+ QString exceptedHeader = QString::fromUtf8(
+"<div>%chatName%</div>\n"
+"<div>%sourceName%</div>\n"
+"<div>%destinationName%</div>\n"
+"<div>%incomingIconPath%</div>\n"
+"<div>%outgoingIconPath%</div>\n"
+"<div>%timeOpened%</div>\n"
+"<div>%timeOpened{%H:%M}%</div>");
+ // Footer is empty on purpose, this is to test if the file doesn't exist.
+ QString exceptedFooter;
+ QString exceptedIncoming = QString::fromUtf8(
+"Incoming:\n"
+"<div>%userIconPath%</div>\n"
+"<div>%senderScreenName%</div>\n"
+"<div>%sender%</div>\n"
+"<div>%service%</div>\n"
+"<div>%message%</div>\n"
+"<div>%time%</div>\n"
+"<div>%time{%H:%M}%</div>\n"
+"<div id=\"insert\">");
+ QString exceptedNextIncoming = QString::fromUtf8(
+"Incoming:\n"
+"<div>%message%</div>\n"
+"<div id=\"insert\">"
+);
+ QString exceptedOutgoing = QString::fromUtf8(
+"Outgoing:\n"
+"<div>%userIconPath%</div>\n"
+"<div>%senderScreenName%</div>\n"
+"<div>%sender%</div>\n"
+"<div>%service%</div>\n"
+"<div>%message%</div>\n"
+"<div>%time%</div>\n"
+"<div>%time{%H:%M}%</div>\n"
+"<div id=\"insert\">");
+ QString exceptedNextOutgoing = QString::fromUtf8(
+"Outgoing:\n"
+"<div>%message%</div>\n"
+"<div id=\"insert\">"
+);
+ QString exceptedStatus = QString::fromUtf8(
+"<div>%message%</div>\n"
+"<div>%time%</div>\n"
+"<div>%time{%H:%M}%</div>");
+
+ CHECK(testStyle->getHeaderHtml(), exceptedHeader);
+ CHECK(testStyle->getFooterHtml(), exceptedFooter);
+ CHECK(testStyle->getIncomingHtml(), exceptedIncoming);
+ CHECK(testStyle->getNextIncomingHtml(), exceptedNextIncoming);
+ CHECK(testStyle->getOutgoingHtml(), exceptedOutgoing);
+ CHECK(testStyle->getNextOutgoingHtml(), exceptedNextOutgoing);
+ CHECK(testStyle->getStatusHtml(), exceptedStatus);
+}
+
+void ChatWindowStyle_Test::testAction()
+{
+ CHECK(testStyle->hasActionTemplate(), false);
+}
+
+void ChatWindowStyle_Test::testVariants()
+{
+ QString expectedNameResult("Variant1;Variant2");
+ QString expectedPathResult("Variants/Variant1.css;Variants/Variant2.css");
+ QStringList variantNameList;
+ QStringList variantPathList;
+ ChatWindowStyle::StyleVariants variantList;
+ ChatWindowStyle::StyleVariants::ConstIterator it;
+ variantList = testStyle->getVariants();
+
+ for(it = variantList.constBegin(); it != variantList.constEnd(); ++it)
+ {
+ variantNameList.append(it.key());
+ variantPathList.append(it.data());
+ }
+
+ CHECK(variantNameList.join(";"), expectedNameResult);
+ CHECK(variantPathList.join(";"), expectedPathResult);
+}
diff --git a/kopete/kopete/chatwindow/tests/chatwindowstyle_test.h b/kopete/kopete/chatwindow/tests/chatwindowstyle_test.h
new file mode 100644
index 00000000..7cf2b912
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/chatwindowstyle_test.h
@@ -0,0 +1,39 @@
+/*
+ ChatWindowStyle test suite
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef CHATWINDOWSTYLE_TEST_H
+#define CHATWINDOWSTYLE_TEST_H
+
+#include <kunittest/tester.h>
+
+class ChatWindowStyle;
+
+class ChatWindowStyle_Test : public KUnitTest::Tester
+{
+public:
+ void allTests();
+
+public slots:
+ void testPaths();
+ void testHtml();
+ void testVariants();
+ void testAction();
+
+private:
+ ChatWindowStyle *testStyle;
+};
+
+#endif
diff --git a/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.cpp b/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.cpp
new file mode 100644
index 00000000..af0f6b81
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.cpp
@@ -0,0 +1,326 @@
+/*
+ Adium(and Kopete) ChatWindowStyle format rendering test suite
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include "chatwindowstylerendering_test.h"
+
+#include <stdlib.h>
+
+// Qt includes
+#include <qdir.h>
+#include <qfile.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+
+// KDE includes
+#include <kapplication.h>
+#include <kunittest/module.h>
+#include <kinstance.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+#include <kopetechatwindowstyle.h>
+
+// Libkopete includes
+#include <kopeteprotocol.h>
+#include <kopetemetacontact.h>
+#include <kopeteaccount.h>
+#include <kopetecontact.h>
+#include <kopetechatsession.h>
+#include <kopetechatsessionmanager.h>
+
+using namespace Kopete;
+
+KUNITTEST_MODULE( kunittest_chatwindowstylerendering_test, "KopeteChatWindowTestSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( ChatWindowStyleRendering_Test );
+
+// Reimplement Kopete::Contact and its abstract method
+class FakeContact : public Kopete::Contact
+{
+public:
+ FakeContact (Kopete::Account *account, const QString &id, Kopete::MetaContact *mc ) : Kopete::Contact( account, id, mc ) {}
+ virtual Kopete::ChatSession *manager(Kopete::Contact::CanCreateFlags /*c*/) { return 0L; }
+ virtual void slotUserInfo() {};
+};
+
+class FakeProtocol : public Kopete::Protocol
+{
+public:
+FakeProtocol( KInstance *instance, QObject *parent, const char *name ) : Kopete::Protocol(instance, parent, name)
+{
+
+}
+
+Account* createNewAccount( const QString &/*accountId*/ )
+{
+ return 0L;
+}
+
+AddContactPage* createAddContactWidget( QWidget */*parent*/, Kopete::Account */*account*/)
+{
+ return 0L;
+}
+
+KopeteEditAccountWidget* createEditAccountWidget( Kopete::Account */*account*/, QWidget */*parent */)
+{
+ return 0L;
+}
+
+};
+
+class FakeAccount : public Kopete::Account
+{
+public:
+FakeAccount(Kopete::Protocol *parent, const QString &accountID, const char *name) : Kopete::Account(parent, accountID, name)
+{
+
+}
+
+~FakeAccount()
+{
+
+}
+
+bool createContact( const QString &/*contactId*/, Kopete::MetaContact */*parentContact*/ )
+{
+ return true;
+}
+
+void connect( const Kopete::OnlineStatus& /*initialStatus*/)
+{
+ // do nothing
+}
+
+void disconnect()
+{
+ // do nothing
+}
+
+void setOnlineStatus( const Kopete::OnlineStatus& /*status*/ , const QString &/*reason*/)
+{
+ // do nothing
+}
+};
+
+class ChatWindowStyleRendering_Test::Private
+{
+public:
+ Private()
+ {
+ protocol = new FakeProtocol( new KInstance(QCString("test-kopete-message")), 0L, "test-kopete-message");
+ account = new FakeAccount(protocol, QString("testaccount"), 0);
+
+ // Create fake meta/contacts
+ myselfMetaContact = new Kopete::MetaContact();
+ myself = new FakeContact(account, "bob@localhost", myselfMetaContact);
+ myself->setNickName("Bob");
+ otherMetaContact = new Kopete::MetaContact();
+ other = new FakeContact(account, "audrey@localhost", otherMetaContact);
+ other->setNickName("Audrey");
+ myselfMetaContact->setDisplayName("Bob");
+ myselfMetaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ otherMetaContact->setDisplayName("Audrey");
+ otherMetaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+
+ Kopete::ContactPtrList contactList;
+ contactList.append(other);
+ // Create fakeChatSession
+ fakeChatSession = Kopete::ChatSessionManager::self()->create(myself, contactList, 0);
+ fakeChatSession->setDisplayName("Test Session");
+
+ // Create testStyle
+ testStyle = new ChatWindowStyle(QString(SRCDIR)+QString("/TestStyle"));
+ }
+ ~Private()
+ {
+ delete myselfMetaContact;
+ delete otherMetaContact;
+ delete myself;
+ delete other;
+ delete fakeChatSession;
+ }
+ FakeProtocol *protocol;
+ FakeAccount *account;
+ Kopete::MetaContact *myselfMetaContact;
+ Kopete::MetaContact *otherMetaContact;
+ FakeContact *myself;
+ FakeContact *other;
+ Kopete::ChatSession *fakeChatSession;
+ ChatWindowStyle *testStyle;
+
+ QString resultHTML;
+};
+
+
+
+ChatWindowStyleRendering_Test::ChatWindowStyleRendering_Test()
+{
+ d = new Private;
+}
+
+ChatWindowStyleRendering_Test::~ChatWindowStyleRendering_Test()
+{
+ delete d;
+}
+
+void ChatWindowStyleRendering_Test::allTests()
+{
+ // change user data dir to avoid messing with user's .kde dir
+ setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kopete-unittest" ), true );
+
+ KApplication::disableAutoDcopRegistration();
+ //KCmdLineArgs::init(argc,argv,"testkopetemessage", 0, 0, 0, 0);
+ KApplication app;
+
+ chatPart = new ChatMessagePart(d->fakeChatSession, 0, 0);
+
+ testHeaderRendering();
+ testMessageRendering();
+ testStatusRendering();
+ //testFullRendering();
+}
+
+void ChatWindowStyleRendering_Test::testHeaderRendering()
+{
+ QString expectedHtml = QString(
+"<div><span id=\"KopeteHeaderChatNameInternal\">Test Session</span></div>\n"
+"<div>Bob</div>\n"
+"<div>Audrey</div>\n"
+"<div>Incoming/buddy_icon.png</div>\n"
+"<div>Outgoing/buddy_icon.png</div>\n"
+"<div>%1</div>\n"
+"<div>%2</div>"
+ ).arg(KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), true, true ) )
+ .arg(QDateTime::currentDateTime().toString("hh:mm"));
+
+ QString headerHtml = d->testStyle->getHeaderHtml();
+ QString resultHtml;
+
+ resultHtml = chatPart->formatStyleKeywords(headerHtml);
+
+ kdDebug(14000) << "Result HTML: " << resultHtml << endl;
+
+ CHECK(resultHtml, expectedHtml);
+}
+
+void ChatWindowStyleRendering_Test::testMessageRendering()
+{
+ QString expectedIncomingHtml = QString(
+"Incoming:\n"
+"<div>Incoming/buddy_icon.png</div>\n"
+"<div><a href=\"kopetemessage://audrey@localhost/?protocolId=Kopete::Protocol&amp;accountId=testaccount\" class=\"KopeteDisplayName\">audrey@localhost</a></div>\n"
+"<div><a href=\"kopetemessage://audrey@localhost/?protocolId=Kopete::Protocol&amp;accountId=testaccount\" class=\"KopeteDisplayName\">Audrey</a></div>\n"
+"<div>Kopete</div>\n"
+"<div><span style=\"\"class=\"KopeteMessageBody\">Test</span></div>\n"
+"<div>%1</div>\n"
+"<div>%2</div>\n"
+"<div id=\"insert\">"
+ ).arg( QDateTime::currentDateTime().toString( "hh:mm:ss" ), QDateTime::currentDateTime().toString( "hh:mm" ) );
+
+ QString expectedOutgoingHtml = QString(
+"Outgoing:\n"
+"<div>Outgoing/buddy_icon.png</div>\n"
+"<div><a href=\"kopetemessage://bob@localhost/?protocolId=Kopete::Protocol&amp;accountId=testaccount\" class=\"KopeteDisplayName\">bob@localhost</a></div>\n"
+"<div><a href=\"kopetemessage://bob@localhost/?protocolId=Kopete::Protocol&amp;accountId=testaccount\" class=\"KopeteDisplayName\">Bob</a></div>\n"
+"<div>Kopete</div>\n"
+"<div><span style=\"\"class=\"KopeteMessageBody\">Hello there</span></div>\n"
+"<div>%1</div>\n"
+"<div>%2</div>\n"
+"<div id=\"insert\">"
+ ).arg( QDateTime::currentDateTime().toString( "hh:mm:ss" ), QDateTime::currentDateTime().toString( "hh:mm" ) );
+
+
+ QString tempHtml;
+ QString resultHtml;
+
+ Kopete::Message msgIn(d->other, d->myself, QString::fromUtf8("Test"), Kopete::Message::Inbound );
+ Kopete::Message msgOut(d->myself, d->other, QString::fromUtf8("Hello there"), Kopete::Message::Outbound);
+
+ tempHtml = d->testStyle->getIncomingHtml();
+ resultHtml = chatPart->formatStyleKeywords(tempHtml, msgIn);
+
+ kdDebug(14000) << "Message incoming HTML: " << resultHtml << endl;
+
+ CHECK(resultHtml, expectedIncomingHtml);
+
+ tempHtml = d->testStyle->getOutgoingHtml();
+ resultHtml = chatPart->formatStyleKeywords(tempHtml, msgOut);
+
+ kdDebug(14000) << "Message outgoing HTML: " << resultHtml << endl;
+
+ CHECK(resultHtml, expectedOutgoingHtml);
+}
+
+void ChatWindowStyleRendering_Test::testStatusRendering()
+{
+ QString expectedStatusHtml = QString(
+"<div><span style=\"\"class=\"KopeteMessageBody\">A contact went offline.</span></div>\n"
+"<div>%1</div>\n"
+"<div>%2</div>"
+ ).arg( QDateTime::currentDateTime().toString( "hh:mm:ss" ), QDateTime::currentDateTime().toString( "hh:mm" ) );
+
+ QString statusHtml = d->testStyle->getStatusHtml();
+ QString resultHtml;
+
+ Kopete::Message msgStatus(0,0, QString::fromUtf8("A contact went offline."), Kopete::Message::Internal);
+ resultHtml = chatPart->formatStyleKeywords(statusHtml, msgStatus);
+
+ CHECK(resultHtml, expectedStatusHtml);
+}
+
+void ChatWindowStyleRendering_Test::testFullRendering()
+{
+ QString expectedFullHtml;
+ QString resultHtml;
+
+ Kopete::Message msgIn1(d->myself, d->other, QString("Hello there !"), Kopete::Message::Inbound);
+ Kopete::Message msgIn2(d->myself, d->other, QString("How are you doing ?"), Kopete::Message::Inbound);
+ Kopete::Message msgOut1(d->other, d->myself, QString("Fine and you ?"), Kopete::Message::Outbound);
+ Kopete::Message msgStatus1(d->myself,d->other, QString("You are now marked as away."), Kopete::Message::Internal);
+ Kopete::Message msgStatus2(d->myself,d->other, QString("You are now marked as online."), Kopete::Message::Internal);
+ Kopete::Message msgIn3(d->myself, d->other, QString("Well, doing some tests."), Kopete::Message::Internal);
+ Kopete::Message msgOut2(d->other, d->myself, QString("All your bases are belong to us."), Kopete::Message::Outbound);
+ Kopete::Message msgOut3(d->other, d->myself, QString("You are on the way to destruction"), Kopete::Message::Outbound);
+
+ // Change style on the fly in ChatMessagePart so this test would run
+ chatPart->setStyle(d->testStyle);
+
+ // Simulate a consersation
+ chatPart->appendMessage(msgIn1);
+ chatPart->appendMessage(msgIn2);
+ chatPart->appendMessage(msgOut1);
+ chatPart->appendMessage(msgStatus1);
+ chatPart->appendMessage(msgStatus2);
+ chatPart->appendMessage(msgIn3);
+ chatPart->appendMessage(msgOut2);
+ chatPart->appendMessage(msgOut3);
+
+ resultHtml = chatPart->htmlDocument().toHTML();
+
+ // Read the expected(sample) HTML from file.
+ QFile sampleHtml(QString(SRCDIR)+"sample.html");
+ if(sampleHtml.open(IO_ReadOnly))
+ {
+ QTextStream stream(&sampleHtml);
+ stream.setEncoding(QTextStream::UnicodeUTF8);
+ expectedFullHtml = stream.read();
+ sampleHtml.close();
+ }
+
+ CHECK(resultHtml, expectedFullHtml);
+}
diff --git a/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.h b/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.h
new file mode 100644
index 00000000..992d00a4
--- /dev/null
+++ b/kopete/kopete/chatwindow/tests/chatwindowstylerendering_test.h
@@ -0,0 +1,45 @@
+/*
+ Adium(and Kopete) ChatWindowStyle format rendering test suite
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef CHATWINDOWSTYLERENDERING_TEST_H
+#define CHATWINDOWSTYLERENDERING_TEST_H
+
+#include <kunittest/tester.h>
+
+// HACK: Needed to access private methods of ChatMessagePart.
+#define private public
+#include <chatmessagepart.h>
+#undef private
+
+class ChatWindowStyleRendering_Test : public KUnitTest::Tester
+{
+public:
+ ChatWindowStyleRendering_Test();
+ ~ChatWindowStyleRendering_Test();
+
+ void allTests();
+public slots:
+ void testHeaderRendering();
+ void testMessageRendering();
+ void testStatusRendering();
+ void testFullRendering();
+private:
+ class Private;
+ Private *d;
+
+ ChatMessagePart *chatPart;
+};
+#endif
diff --git a/kopete/kopete/config/Makefile.am b/kopete/kopete/config/Makefile.am
new file mode 100644
index 00000000..2e866531
--- /dev/null
+++ b/kopete/kopete/config/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = plugins accounts behavior appearance identity avdevice
+
+
diff --git a/kopete/kopete/config/accounts/Makefile.am b/kopete/kopete/config/accounts/Makefile.am
new file mode 100644
index 00000000..9f32e5af
--- /dev/null
+++ b/kopete/kopete/config/accounts/Makefile.am
@@ -0,0 +1,18 @@
+kopete_srcdir=$(top_srcdir)/kopete
+kopete_builddir=$(top_builddir)/kopete
+
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(KOPETE_COMPAT_INCLUDES) $(all_includes) -I$(kopete_srcdir)/kopete/addaccountwizard
+
+
+kde_module_LTLIBRARIES = kcm_kopete_accountconfig.la
+
+kcm_kopete_accountconfig_la_SOURCES = kopeteaccountconfigbase.ui kopeteaccountconfig.cpp
+kcm_kopete_accountconfig_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_accountconfig_la_LIBADD = ../../../libkopete/libkopete.la ../../addaccountwizard/libkopeteaddaccountwizard.la $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_accountconfig.desktop
+servicedir = $(kde_servicesdir)
+
+# vim: set noet:
+
diff --git a/kopete/kopete/config/accounts/kopete_accountconfig.desktop b/kopete/kopete/config/accounts/kopete_accountconfig.desktop
new file mode 100644
index 00000000..78ce4efe
--- /dev/null
+++ b/kopete/kopete/config/accounts/kopete_accountconfig.desktop
@@ -0,0 +1,131 @@
+[Desktop Entry]
+Icon=personal
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_accountconfig
+X-KDE-FactoryName=KopeteAccountConfigFactory
+X-KDE-ParentApp=kopete
+X-KDE-ParentComponents=kopete
+
+Name=Accounts
+Name[ar]=حسابات
+Name[be]=Рахункі
+Name[bg]=Сметки
+Name[bn]=অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ
+Name[br]=Kontoù
+Name[bs]=RaÄuni
+Name[ca]=Comptes
+Name[cs]=ÚÄty
+Name[cy]=Cyfrifon
+Name[da]=Konti
+Name[de]=Zugänge
+Name[el]=ΛογαÏιασμοί
+Name[eo]=Kontoj
+Name[es]=Cuentas
+Name[et]=Kontod
+Name[eu]=Kontuak
+Name[fa]=حسابها
+Name[fi]=Tilit
+Name[fr]=Comptes
+Name[ga]=Cuntais
+Name[gl]=Contas
+Name[he]=חשבונות
+Name[hi]=अकाउनà¥à¤Ÿà¥à¤¸
+Name[hr]=KorisniÄki raÄuni
+Name[hu]=Azonosítók
+Name[is]=Notandanöfn
+Name[it]=Account
+Name[ja]=アカウント
+Name[ka]=áƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ”ბი
+Name[kk]=Тіркелгілер
+Name[km]=គណនី
+Name[lt]=Paskyros
+Name[mk]=Сметки
+Name[nb]=Kontoer
+Name[nds]=Kontos
+Name[ne]=खाता
+Name[nn]=Kontoar
+Name[pa]=ਖਾਤੇ
+Name[pl]=Konta
+Name[pt]=Contas
+Name[pt_BR]=Contas
+Name[ro]=Conturi
+Name[ru]=Учётные запиÑи
+Name[se]=Kontut
+Name[sk]=ÚÄty
+Name[sl]=RaÄuni
+Name[sr]=Ðалози
+Name[sr@Latn]=Nalozi
+Name[sv]=Konton
+Name[ta]=கணகà¯à®•à¯à®•à®³à¯
+Name[tg]=ҲиÑоботҳо
+Name[tr]=Hesaplar
+Name[uk]=Рахунки
+Name[uz]=Hisoblar
+Name[uz@cyrillic]=ҲиÑоблар
+Name[wa]=Contes
+Name[zh_CN]=账户
+Name[zh_HK]=帳號
+Name[zh_TW]=帳號
+Comment=Here You Can Manage All Your Accounts
+Comment[ar]=من هنا تستطيع إدارة جميع حساباتك
+Comment[be]=Кіраванне вашымі рахункамі
+Comment[bg]=ÐаÑтройване на Ñметките
+Comment[bn]=আপনি à¦à¦–ানে আপনার অà§à¦¯à¦¾à¦•à¦¾à¦‰à¦¨à§à¦Ÿ পরিচালনা করতে পারেন
+Comment[bs]=Ovdje možete upravljati svim svojim raÄunima
+Comment[ca]=Aquí podreu gestionar tots els vostres comptes
+Comment[cs]=Zde můžete spravovat veÅ¡keré své úÄty
+Comment[cy]=Yma Gallwch Reoli Eich Cyfrifon i Gyd
+Comment[da]=Her kan du håndtere alle dine konti
+Comment[de]=Hier können Sie Ihre Zugänge verwalten
+Comment[el]=Εδώ μποÏείτε να Ïυθμίσετε όλους τους λογαÏιασμοÏÏ‚ σας
+Comment[es]=Gestiona todas sus cuentas
+Comment[et]=Kõigi oma kohtode haldamine
+Comment[eu]=Hemen zure kontu guztiak kudeatu ditzakezu
+Comment[fa]=اینجا می‌توانید همۀ حسابهای خود را مدیریت کنید
+Comment[fi]=Täältä voit hallita kaikkia tilejäsi
+Comment[fr]=Vous pouvez gérer vos comptes ici
+Comment[ga]=Tig leat do chuntais uile a láimhseáil anseo
+Comment[gl]=Aquí podes xestionar todas as túas contas
+Comment[he]=×›×ן ב×פשרותך לנהל ×ת כל חשבונותיך
+Comment[hi]=यहाठआप अपने सभी अकाउनà¥à¤Ÿà¥à¤¸ का पà¥à¤°à¤¬à¤‚धन कर सकते हैं
+Comment[hr]=Ovde možete upravljati svim vaÅ¡im korisniÄkim raÄunima
+Comment[hu]=Itt lehet kezelni az azonosítókat
+Comment[is]=Hér geturðu haldið utan um öll þín notandanöfn
+Comment[it]=Qui puoi gestire tutti i tuoi account
+Comment[ja]=ã“ã“ã§ã™ã¹ã¦ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’管ç†ã—ã¾ã™
+Comment[ka]=áƒáƒ¥ თქვენ შეგიძლიáƒáƒ— ყველრáƒáƒœáƒ’áƒáƒ áƒ˜áƒ¨áƒ˜áƒ¡ მáƒáƒ áƒ—ვáƒ
+Comment[kk]=Мұнда барлық тіркелгілеріңізді баÑқара алаÑыз
+Comment[km]=ទីនáŸáŸ‡ អ្នក​អាច​គ្រប់គ្រង​គណនី​ទាំងអស់​របស់​អ្នក
+Comment[lt]=ÄŒia galite tvarkyti visas savo paskyras
+Comment[mk]=Тука можете да управувате Ñо Ñите ваши Ñметки
+Comment[nb]=Rediger kontoene du har satt opp
+Comment[nds]=Hier kannst Du all Dien Kontos plegen
+Comment[ne]=यहाठतपाईठतपाईà¤à¤•à¥‹ सबै खाता पà¥à¤°à¤¬à¤¨à¥à¤§ गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Hier kunt u al uw accounts beheren
+Comment[nn]=Rediger kontoane du har sett opp
+Comment[pl]=Tutaj można zarządzać wszystkimi Twoimi kontami
+Comment[pt]=Aqui Você Pode Gerir Todas as Suas Contas
+Comment[pt_BR]=Aqui você pode gerenciar todas as suas contas
+Comment[ro]=Aici vă puteţi administra toate conturile
+Comment[ru]=ÐаÑтройка учётных запиÑей
+Comment[se]=Dáppe sáhtát gieđahallat buot iežat kontuid
+Comment[sk]=Tu môžete spravovaÅ¥ vÅ¡etky svoje úÄty
+Comment[sl]=Tukaj lahko upravljate z vsemi svojimi raÄuni
+Comment[sr]=Овде можете управљати Ñвим вашим налозима
+Comment[sr@Latn]=Ovde možete upravljati svim vašim nalozima
+Comment[sv]=Här kan du hantera alla dina konton
+Comment[ta]=இஙà¯à®•à¯ நீஙà¯à®•à®³à¯ அனைதà¯à®¤à¯ கணகà¯à®•à¯à®•à®³à¯ˆà®¯à¯à®®à¯ உளà¯à®³à®®à¯ˆà®•à¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯
+Comment[tg]=Дар ин ҷо Шумо Ҳамаи ҲиÑоботҳои худро Идора карда метавонед
+Comment[tr]=Bütün Hesaplarınız Buradan Yönetilebilir
+Comment[uk]=Тут можна керувати вашими рахунками
+Comment[uz]=Bu yerda hamma hisoblarni moslash mumkin
+Comment[uz@cyrillic]=Бу ерда ҳамма ҳиÑобларни моÑлаш мумкин
+Comment[zh_CN]=您å¯åœ¨æ­¤ç®¡ç†æ‚¨å…¨éƒ¨çš„账户
+Comment[zh_HK]=在此您å¯ç®¡ç†æ‚¨çš„帳號
+Comment[zh_TW]=您å¯ä»¥åœ¨æ­¤ç®¡ç†æ‚¨çš„所有帳號
+DocPath=kopete/configure-dialog.html#configuring-accounts
+
+
diff --git a/kopete/kopete/config/accounts/kopeteaccountconfig.cpp b/kopete/kopete/config/accounts/kopeteaccountconfig.cpp
new file mode 100644
index 00000000..3d86fb8d
--- /dev/null
+++ b/kopete/kopete/config/accounts/kopeteaccountconfig.cpp
@@ -0,0 +1,285 @@
+/*
+ accountconfig.cpp - Kopete account config page
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteaccountconfig.h"
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qguardedptr.h>
+
+#include <kcolorbutton.h>
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <kgenericfactory.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "addaccountwizard.h"
+#include "editaccountwidget.h"
+#include "kopeteaccountconfigbase.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+
+class KopeteAccountLVI : public KListViewItem
+{
+ public:
+ KopeteAccountLVI( Kopete::Account *a, KListView *p ) : KListViewItem( p ){ m_account = a; }
+ Kopete::Account *account() { return m_account; }
+
+ private:
+ //need to be guarded because some accounts may be linked (that's the case of jabber transports)
+ QGuardedPtr<Kopete::Account> m_account;
+};
+
+typedef KGenericFactory<KopeteAccountConfig, QWidget> KopeteAccountConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_accountconfig, KopeteAccountConfigFactory( "kcm_kopete_accountconfig" ) )
+
+KopeteAccountConfig::KopeteAccountConfig( QWidget *parent, const char * /* name */, const QStringList &args )
+: KCModule( KopeteAccountConfigFactory::instance(), parent, args )
+{
+
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ m_view = new KopeteAccountConfigBase( this, "KopeteAccountConfig::m_view" );
+
+ m_view->mButtonUp->setIconSet( SmallIconSet( "up" ) );
+ m_view->mButtonDown->setIconSet( SmallIconSet( "down" ) );
+
+ connect( m_view->mButtonNew, SIGNAL( clicked() ), this, SLOT( slotAddAccount() ) );
+ connect( m_view->mButtonEdit, SIGNAL( clicked() ), this, SLOT( slotEditAccount() ) );
+ connect( m_view->mButtonRemove, SIGNAL( clicked() ), this, SLOT( slotRemoveAccount() ) );
+ connect( m_view->mButtonUp, SIGNAL( clicked() ), this, SLOT( slotAccountUp() ) );
+ connect( m_view->mButtonDown, SIGNAL( clicked() ), this, SLOT( slotAccountDown() ) );
+ connect( m_view->mAccountList, SIGNAL( selectionChanged() ), this, SLOT( slotItemSelected() ) );
+ connect( m_view->mAccountList, SIGNAL( doubleClicked( QListViewItem * ) ), this, SLOT( slotEditAccount() ) );
+ connect( m_view->mUseColor, SIGNAL( toggled( bool ) ), this, SLOT( slotColorChanged() ) );
+ connect( m_view->mColorButton, SIGNAL( changed( const QColor & ) ), this, SLOT( slotColorChanged() ) );
+
+ m_view->mAccountList->setSorting(-1);
+
+ setButtons( Help );
+ load();
+}
+
+void KopeteAccountConfig::save()
+{
+ uint priority = m_view->mAccountList->childCount();
+
+ KopeteAccountLVI *i = static_cast<KopeteAccountLVI*>( m_view->mAccountList->firstChild() );
+ while( i )
+ {
+ if(!i->account())
+ continue;
+ i->account()->setPriority( priority-- );
+ i = static_cast<KopeteAccountLVI*>( i->nextSibling() );
+ }
+
+ QMap<Kopete::Account *, QColor>::Iterator it;
+ for(it=m_newColors.begin() ; it != m_newColors.end() ; ++it)
+ it.key()->setColor(it.data());
+ m_newColors.clear();
+
+ Kopete::AccountManager::self()->save();
+
+ load(); //refresh the colred accounts (in case of apply)
+}
+
+void KopeteAccountConfig::load()
+{
+ KopeteAccountLVI *lvi = 0L;
+
+ m_view->mAccountList->clear();
+
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for ( Kopete::Account *i = accounts.first() ; i; i = accounts.next() )
+ {
+ // Insert the item after the previous one
+ lvi = new KopeteAccountLVI( i, m_view->mAccountList );
+ lvi->setText( 0, i->protocol()->displayName() );
+ lvi->setPixmap( 0, i->accountIcon() );
+ lvi->setText( 1, i->accountLabel() );
+ }
+
+ m_newColors.clear();
+ slotItemSelected();
+}
+
+void KopeteAccountConfig::slotItemSelected()
+{
+ m_protected=true;
+ KopeteAccountLVI *itemSelected = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+
+ m_view->mButtonEdit->setEnabled( itemSelected );
+ m_view->mButtonRemove->setEnabled( itemSelected );
+
+ if ( itemSelected && itemSelected->account() )
+ {
+ m_view->mButtonUp->setEnabled( itemSelected->itemAbove() );
+ m_view->mButtonDown->setEnabled( itemSelected->itemBelow() );
+
+ Kopete::Account *account = itemSelected->account();
+ QColor color= m_newColors.contains(account) ? m_newColors[account] : account->color();
+ m_view->mUseColor->setEnabled( true );
+ m_view->mUseColor->setChecked( color.isValid() );
+ m_view->mColorButton->setColor( color );
+ m_view->mColorButton->setEnabled( m_view->mUseColor->isChecked() );
+
+ }
+ else
+ {
+ m_view->mButtonUp->setEnabled( false );
+ m_view->mButtonDown->setEnabled( false);
+ m_view->mUseColor->setEnabled( false );
+ m_view->mColorButton->setEnabled( false );
+ }
+ m_protected=false;
+}
+
+void KopeteAccountConfig::slotAccountUp()
+{
+ KopeteAccountLVI *itemSelected = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+ if ( !itemSelected )
+ return;
+
+ if ( itemSelected->itemAbove() )
+ itemSelected->itemAbove()->moveItem( itemSelected );
+
+ slotItemSelected();
+ emit changed( true );
+}
+
+void KopeteAccountConfig::slotAccountDown()
+{
+ KopeteAccountLVI *itemSelected = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+ if ( !itemSelected )
+ return;
+
+ itemSelected->moveItem( itemSelected->itemBelow() );
+
+ slotItemSelected();
+ emit changed( true );
+}
+
+void KopeteAccountConfig::slotAddAccount()
+{
+ AddAccountWizard *m_addwizard = new AddAccountWizard( this, "addAccountWizard", true );
+ connect( m_addwizard, SIGNAL( destroyed( QObject * ) ), this, SLOT( slotAddWizardDone() ) );
+ m_addwizard->show();
+}
+
+void KopeteAccountConfig::slotEditAccount()
+{
+ KopeteAccountLVI *lvi = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+ if ( !lvi || !lvi->account() )
+ return;
+
+ Kopete::Account *ident = lvi->account();
+ Kopete::Protocol *proto = ident->protocol();
+
+ KDialogBase *editDialog = new KDialogBase( this, "KopeteAccountConfig::editDialog", true,
+ i18n( "Edit Account" ), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );
+
+ KopeteEditAccountWidget *m_accountWidget = proto->createEditAccountWidget( ident, editDialog );
+ if ( !m_accountWidget )
+ return;
+
+ // FIXME: Why the #### is EditAccountWidget not a QWidget?!? This sideways casting
+ // is braindead and error-prone. Looking at MSN the only reason I can see is
+ // because it allows direct subclassing of designer widgets. But what is
+ // wrong with embedding the designer widget in an empty QWidget instead?
+ // Also, if this REALLY has to be a pure class and not a widget, then the
+ // class should at least be renamed to EditAccountIface instead - Martijn
+ QWidget *w = dynamic_cast<QWidget *>( m_accountWidget );
+ if ( !w )
+ return;
+
+ editDialog->setMainWidget( w );
+ if ( editDialog->exec() == QDialog::Accepted )
+ {
+ if( m_accountWidget->validateData() )
+ m_accountWidget->apply();
+ }
+
+ // FIXME: Why deleteLater? It shouldn't be in use anymore at this point - Martijn
+ editDialog->deleteLater();
+ load();
+ Kopete::AccountManager::self()->save();
+}
+
+void KopeteAccountConfig::slotRemoveAccount()
+{
+ KopeteAccountLVI *lvi = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+ if ( !lvi || !lvi->account() )
+ return;
+
+ Kopete::Account *i = lvi->account();
+ if ( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to remove the account \"%1\"?" ).arg( i->accountLabel() ),
+ i18n( "Remove Account" ), KGuiItem(i18n( "Remove Account" ), "editdelete"),
+ "askRemoveAccount", KMessageBox::Notify | KMessageBox::Dangerous ) == KMessageBox::Continue )
+ {
+ Kopete::AccountManager::self()->removeAccount( i );
+ delete lvi;
+ }
+}
+
+void KopeteAccountConfig::slotAddWizardDone()
+{
+ save();
+ load();
+}
+
+void KopeteAccountConfig::slotColorChanged()
+{
+ if(m_protected) //this slot is called because we changed the button
+ return; // color because another account has been selected
+
+ KopeteAccountLVI *lvi = static_cast<KopeteAccountLVI*>( m_view->mAccountList->selectedItem() );
+ if ( !lvi || !lvi->account() )
+ return;
+ Kopete::Account *account = lvi->account();
+
+ if(!account->color().isValid() && !m_view->mUseColor->isChecked() )
+ { //we don't use color for that account and nothing changed.
+ m_newColors.remove(account);
+ return;
+ }
+ else if(!m_view->mUseColor->isChecked())
+ { //the user disabled account coloring, but it was activated before
+ m_newColors[account]=QColor();
+ emit changed(true);
+ return;
+ }
+ else if(account->color() == m_view->mColorButton->color() )
+ { //The color has not changed.
+ m_newColors.remove(account);
+ return;
+ }
+ else
+ {
+ m_newColors[account]=m_view->mColorButton->color();
+ emit changed(true);
+ }
+}
+
+#include "kopeteaccountconfig.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/config/accounts/kopeteaccountconfig.h b/kopete/kopete/config/accounts/kopeteaccountconfig.h
new file mode 100644
index 00000000..9aec2a60
--- /dev/null
+++ b/kopete/kopete/config/accounts/kopeteaccountconfig.h
@@ -0,0 +1,61 @@
+/*
+ accountconfig.h - Kopete account config page
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __ACCOUNTCONFIG_H
+#define __ACCOUNTCONFIG_H
+
+#include <kcmodule.h>
+#include <qmap.h>
+#include <qcolor.h>
+
+namespace Kopete
+{
+class Account;
+}
+
+class KopeteAccountConfigBase;
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+class KopeteAccountConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KopeteAccountConfig(QWidget *parent, const char *name, const QStringList &args );
+
+public slots:
+ virtual void save();
+ virtual void load();
+
+private:
+ KopeteAccountConfigBase *m_view;
+ QMap<Kopete::Account* , QColor> m_newColors;
+ bool m_protected;
+
+private slots:
+ void slotRemoveAccount();
+ void slotEditAccount();
+ void slotAddAccount();
+ void slotAddWizardDone();
+ void slotItemSelected();
+ void slotAccountUp();
+ void slotAccountDown();
+ void slotColorChanged();
+};
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/accounts/kopeteaccountconfigbase.ui b/kopete/kopete/config/accounts/kopeteaccountconfigbase.ui
new file mode 100644
index 00000000..eea90c35
--- /dev/null
+++ b/kopete/kopete/config/accounts/kopeteaccountconfigbase.ui
@@ -0,0 +1,268 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KopeteAccountConfigBase</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteAccountConfigBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>477</width>
+ <height>316</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Manage Accounts</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QPushButton" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mButtonNew</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add new account</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mButtonEdit</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Modify...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Modify selected account</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Let you edit the account's properties.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mButtonRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Remove selected account</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Remove selected account</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="0" column="0" rowspan="8" colspan="1">
+ <column>
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Account</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>mAccountList</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mUseColor</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;custom color</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use custom color for account</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Allows you to set a custom color for this account</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="5" column="2">
+ <property name="name">
+ <cstring>mColorButton</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Account custom color selector</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Allows you to set a custom color for this account.
+The icon of every contact of this account will be coloured with this color. Useful if you have several accounts of the same protocol</string>
+ </property>
+ </widget>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="7" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout27</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>mButtonUp</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Increase the priority</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Uses these buttons to increase or decrease the priority.
+The priority is used to determine which contact to use when you click on a metacontact: Kopete will use the contact of the account with the greatest priority (if all contacts have the same online status.)</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer41</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>mButtonDown</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>60</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Decrease the priority</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Uses these buttons to increase or decrease the priority.
+The priority is used to determine which contact to use when you click on a metacontact: Kopete will use the contact of the account with the greatest priority (if all contacts have the same online status.)</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="6" column="1" rowspan="1" colspan="2">
+ <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>70</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>mUseColor</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mColorButton</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>mAccountList</tabstop>
+ <tabstop>mButtonNew</tabstop>
+ <tabstop>mButtonEdit</tabstop>
+ <tabstop>mButtonRemove</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/appearance/Makefile.am b/kopete/kopete/config/appearance/Makefile.am
new file mode 100644
index 00000000..7e7fc8ca
--- /dev/null
+++ b/kopete/kopete/config/appearance/Makefile.am
@@ -0,0 +1,20 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private \
+ -I$(top_srcdir)/kopete/kopete/chatwindow $(KOPETE_COMPAT_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_kopete_appearanceconfig.la
+
+kcm_kopete_appearanceconfig_la_SOURCES = appearanceconfig_emoticons.ui \
+ appearanceconfig_colors.ui appearanceconfig_chatwindow.ui appearanceconfig_contactlist.ui \
+ appearanceconfig.cpp tooltipeditwidget.ui tooltipeditdialog.cpp
+
+kcm_kopete_appearanceconfig_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) \
+ $(all_libraries)
+kcm_kopete_appearanceconfig_la_LIBADD = ../../../libkopete/libkopete.la \
+ ../../../kopete/chatwindow/libkopetechatwindow.la \
+ -lktexteditor $(LIB_KOPETECOMPAT) $(LIB_KUTILS) $(LIB_KNEWSTUFF)
+
+service_DATA = kopete_appearanceconfig.desktop
+servicedir = $(kde_servicesdir)
+
+# vim: set noet:
diff --git a/kopete/kopete/config/appearance/appearanceconfig.cpp b/kopete/kopete/config/appearance/appearanceconfig.cpp
new file mode 100644
index 00000000..e3867d41
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig.cpp
@@ -0,0 +1,870 @@
+/*
+ appearanceconfig.cpp - Kopete Look Feel Config
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "appearanceconfig.h"
+#include "appearanceconfig_emoticons.h"
+#include "appearanceconfig_chatwindow.h"
+#include "appearanceconfig_colors.h"
+#include "appearanceconfig_contactlist.h"
+
+#include "tooltipeditdialog.h"
+
+#include <qcheckbox.h>
+#include <qdir.h>
+#include <qlayout.h>
+#include <qhbuttongroup.h>
+#include <qspinbox.h>
+#include <qslider.h>
+#include <qlabel.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+
+#include <kapplication.h>
+#include <kcolorcombo.h>
+#include <kcolorbutton.h>
+#include <kconfig.h> // for KNewStuff emoticon fetching
+#include <kdebug.h>
+#include <kfontrequester.h>
+#include <kgenericfactory.h>
+#include <kio/netaccess.h>
+#include <khtmlview.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+#include <kstandarddirs.h>
+#include <ktextedit.h>
+#include <kurl.h> // KNewStuff
+#include <kurlrequesterdlg.h>
+#include <krun.h>
+#include <kfiledialog.h>
+
+#include <knewstuff/downloaddialog.h> // knewstuff emoticon and chatwindow fetching
+#include <knewstuff/engine.h> // "
+#include <knewstuff/entry.h> // "
+#include <knewstuff/knewstuff.h> // "
+#include <knewstuff/provider.h> // "
+
+// For Kopete Chat Window Style configuration and preview.
+#include <kopetechatwindowstylemanager.h>
+#include <kopetechatwindowstyle.h>
+#include <chatmessagepart.h>
+
+// Things we fake to get the message preview to work
+#include <kopeteprotocol.h>
+#include <kopetemetacontact.h>
+#include <kopeteaccount.h>
+#include <kopetecontact.h>
+#include <kopetemessage.h>
+#include <kopetechatsession.h>
+#include <kopetechatsessionmanager.h>
+
+#include "kopeteprefs.h"
+#include "kopeteemoticons.h"
+#include "kopeteglobal.h"
+
+#include <qtabwidget.h>
+
+typedef KGenericFactory<AppearanceConfig, QWidget> KopeteAppearanceConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_appearanceconfig, KopeteAppearanceConfigFactory( "kcm_kopete_appearanceconfig" ) )
+
+class FakeProtocol;
+class FakeAccount;
+class FakeContact;
+
+class AppearanceConfig::Private
+{
+public:
+ Private()
+ : mAppearanceTabCtl(0L), preview(0L), mPrfsEmoticons(0L),mPrfsChatWindow(0L),
+ mPrfsColors(0L), mPrfsContactList(0L), currentStyle(0L), loading(false),
+ styleChanged(false)
+ {}
+
+ QTabWidget *mAppearanceTabCtl;
+
+ ChatMessagePart *preview;
+ AppearanceConfig_Emoticons *mPrfsEmoticons;
+ AppearanceConfig_ChatWindow *mPrfsChatWindow;
+ AppearanceConfig_Colors *mPrfsColors;
+ AppearanceConfig_ContactList *mPrfsContactList;
+
+ // value is the style path
+ QMap<QListBoxItem*,QString> styleItemMap;
+ ChatWindowStyle::StyleVariants currentVariantMap;
+ ChatWindowStyle *currentStyle;
+ bool loading;
+ bool styleChanged;
+
+ // For style preview
+ FakeProtocol *previewProtocol;
+ FakeAccount *previewAccount;
+ Kopete::MetaContact *myselfMetaContact;
+ Kopete::MetaContact *jackMetaContact;
+ FakeContact *myself;
+ FakeContact *jack;
+ Kopete::ChatSession *previewChatSession;
+};
+
+class KopeteStyleNewStuff : public KNewStuff
+{
+public:
+ KopeteStyleNewStuff(const QString &type, QWidget *parentWidget = 0)
+ : KNewStuff( type, parentWidget)
+ {}
+
+ bool createUploadFile(const QString &)
+ {
+ return false;
+ }
+
+ bool install(const QString &styleFilename)
+ {
+ int styleInstallReturn = 0;
+ styleInstallReturn = ChatWindowStyleManager::self()->installStyle( styleFilename );
+
+ switch(styleInstallReturn)
+ {
+ case ChatWindowStyleManager::StyleInstallOk:
+ {
+ KMessageBox::queuedMessageBox( this->parentWidget(), KMessageBox::Information, i18n("The Chat Window style was successfully installed."), i18n("Install successful") );
+ return true;
+ }
+ case ChatWindowStyleManager::StyleCannotOpen:
+ {
+ KMessageBox::queuedMessageBox( this->parentWidget(), KMessageBox::Error, i18n("The specified archive cannot be opened.\nMake sure that the archive is valid ZIP or TAR archive."), i18n("Cannot open archive") );
+ break;
+ }
+ case ChatWindowStyleManager::StyleNoDirectoryValid:
+ {
+ KMessageBox::queuedMessageBox( this->parentWidget(), KMessageBox::Error, i18n("Could not find a suitable place to install the Chat Window style in user directory."), i18n("Cannot find styles directory") );
+ break;
+ }
+ case ChatWindowStyleManager::StyleNotValid:
+ {
+ KMessageBox::queuedMessageBox( this->parentWidget(), KMessageBox::Error, i18n("The specified archive does not contain a valid Chat Window style."), i18n("Invalid Style") );
+ break;
+ }
+
+ case ChatWindowStyleManager::StyleUnknow:
+ default:
+ {
+ KMessageBox::queuedMessageBox( this->parentWidget(), KMessageBox::Error, i18n("An unknow error occurred while trying to install the Chat Window style."), i18n("Unknow error") );
+ break;
+ }
+ }
+ return false;
+ }
+};
+
+// TODO: Someday, this configuration dialog must(not should) use KConfigXT
+AppearanceConfig::AppearanceConfig(QWidget *parent, const char* /*name*/, const QStringList &args )
+: KCModule( KopeteAppearanceConfigFactory::instance(), parent, args )
+{
+ d = new Private;
+
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ d->mAppearanceTabCtl = new QTabWidget(this, "mAppearanceTabCtl");
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "ChatWindowSettings" );
+
+ // "Emoticons" TAB ==========================================================
+ d->mPrfsEmoticons = new AppearanceConfig_Emoticons(d->mAppearanceTabCtl);
+ connect(d->mPrfsEmoticons->chkUseEmoticons, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsEmoticons->chkRequireSpaces, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsEmoticons->icon_theme_list, SIGNAL(selectionChanged()),
+ this, SLOT(slotSelectedEmoticonsThemeChanged()));
+ connect(d->mPrfsEmoticons->btnInstallTheme, SIGNAL(clicked()),
+ this, SLOT(installEmoticonTheme()));
+
+ connect(d->mPrfsEmoticons->btnGetThemes, SIGNAL(clicked()),
+ this, SLOT(slotGetEmoticonThemes()));
+ connect(d->mPrfsEmoticons->btnRemoveTheme, SIGNAL(clicked()),
+ this, SLOT(removeSelectedEmoticonTheme()));
+
+ d->mAppearanceTabCtl->addTab(d->mPrfsEmoticons, i18n("&Emoticons"));
+
+ // "Chat Window" TAB ========================================================
+ d->mPrfsChatWindow = new AppearanceConfig_ChatWindow(d->mAppearanceTabCtl);
+
+ connect(d->mPrfsChatWindow->styleList, SIGNAL(selectionChanged(QListBoxItem *)),
+ this, SLOT(slotChatStyleSelected()));
+ connect(d->mPrfsChatWindow->variantList, SIGNAL(activated(const QString&)),
+ this, SLOT(slotChatStyleVariantSelected(const QString &)));
+ connect(d->mPrfsChatWindow->deleteButton, SIGNAL(clicked()),
+ this, SLOT(slotDeleteChatStyle()));
+ connect(d->mPrfsChatWindow->installButton, SIGNAL(clicked()),
+ this, SLOT(slotInstallChatStyle()));
+ connect(d->mPrfsChatWindow->btnGetStyles, SIGNAL(clicked()),
+ this, SLOT(slotGetChatStyles()));
+ connect(d->mPrfsChatWindow->groupConsecutiveMessages, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ // Show the available styles when the Manager has finish to load the styles.
+ connect(ChatWindowStyleManager::self(), SIGNAL(loadStylesFinished()), this, SLOT(slotLoadChatStyles()));
+
+ d->mPrfsChatWindow->htmlFrame->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ // Create the fake Chat Session
+ createPreviewChatSession();
+ QVBoxLayout *l = new QVBoxLayout(d->mPrfsChatWindow->htmlFrame);
+ d->preview = new ChatMessagePart(d->previewChatSession, d->mPrfsChatWindow->htmlFrame, "preview");
+ d->preview->setJScriptEnabled(false);
+ d->preview->setJavaEnabled(false);
+ d->preview->setPluginsEnabled(false);
+ d->preview->setMetaRefreshEnabled(false);
+ KHTMLView *htmlWidget = d->preview->view();
+ htmlWidget->setMarginWidth(4);
+ htmlWidget->setMarginHeight(4);
+ htmlWidget->setFocusPolicy(NoFocus);
+ htmlWidget->setSizePolicy(
+ QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+ l->addWidget(htmlWidget);
+ // Add the preview message to the ChatMessagePart
+ createPreviewMessages();
+
+ d->mAppearanceTabCtl->addTab( d->mPrfsChatWindow, i18n("Chat Window") );
+
+ // "Contact List" TAB =======================================================
+ d->mPrfsContactList = new AppearanceConfig_ContactList(d->mAppearanceTabCtl);
+ connect(d->mPrfsContactList->mTreeContactList, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mSortByGroup, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mEditTooltips, SIGNAL(clicked()),
+ this, SLOT(slotEditTooltips()));
+ connect(d->mPrfsContactList->mIndentContacts, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mDisplayMode, SIGNAL(clicked(int)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mIconMode, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mAnimateChanges, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mFadeVisibility, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mFoldVisibility, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mAutoHide, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsContactList->mAutoHideTimeout, SIGNAL(valueChanged(int)),
+ this, SLOT(emitChanged()));
+
+ // don't enable the checkbox if XRender is not available
+ #ifdef HAVE_XRENDER
+ d->mPrfsContactList->mFadeVisibility->setEnabled(true);
+ #else
+ d->mPrfsContactList->mFadeVisibility->setEnabled(false);
+ #endif
+
+ d->mAppearanceTabCtl->addTab(d->mPrfsContactList, i18n("Contact List"));
+
+ // "Colors and Fonts" TAB ===================================================
+ d->mPrfsColors = new AppearanceConfig_Colors(d->mAppearanceTabCtl);
+ connect(d->mPrfsColors->foregroundColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(slotHighlightChanged()));
+ connect(d->mPrfsColors->backgroundColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(slotHighlightChanged()));
+ connect(d->mPrfsColors->fontFace, SIGNAL(fontSelected(const QFont &)),
+ this, SLOT(slotChangeFont()));
+ connect(d->mPrfsColors->textColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(slotUpdateChatPreview()));
+ connect(d->mPrfsColors->bgColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(slotUpdateChatPreview()));
+ connect(d->mPrfsColors->linkColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(slotUpdateChatPreview()));
+ connect(d->mPrfsColors->mGreyIdleMetaContacts, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->idleContactColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mUseCustomFonts, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mSmallFont, SIGNAL(fontSelected(const QFont &)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mNormalFont, SIGNAL(fontSelected(const QFont &)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mGroupNameColor, SIGNAL(changed(const QColor &)),
+ this, SLOT(emitChanged()));
+
+ connect(d->mPrfsColors->mBgOverride, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mFgOverride, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+ connect(d->mPrfsColors->mRtfOverride, SIGNAL(toggled(bool)),
+ this, SLOT(emitChanged()));
+
+ d->mAppearanceTabCtl->addTab(d->mPrfsColors, i18n("Colors && Fonts"));
+
+ // ==========================================================================
+
+ load();
+}
+
+AppearanceConfig::~AppearanceConfig()
+{
+ delete d;
+}
+
+void AppearanceConfig::updateEmoticonsButton(bool _b)
+{
+ QString themeName = d->mPrfsEmoticons->icon_theme_list->currentText();
+ QFileInfo fileInf(KGlobal::dirs()->findResource("emoticons", themeName+"/"));
+ d->mPrfsEmoticons->btnRemoveTheme->setEnabled( _b && fileInf.isWritable());
+ d->mPrfsEmoticons->btnGetThemes->setEnabled( false );
+}
+
+void AppearanceConfig::save()
+{
+// kdDebug(14000) << k_funcinfo << "called." << endl;
+ KopetePrefs *p = KopetePrefs::prefs();
+
+ // "Emoticons" TAB ==========================================================
+ p->setIconTheme( d->mPrfsEmoticons->icon_theme_list->currentText() );
+ p->setUseEmoticons ( d->mPrfsEmoticons->chkUseEmoticons->isChecked() );
+ p->setEmoticonsRequireSpaces( d->mPrfsEmoticons->chkRequireSpaces->isChecked() );
+
+ // "Chat Window" TAB ========================================================
+ p->setGroupConsecutiveMessages( d->mPrfsChatWindow->groupConsecutiveMessages->isChecked() );
+
+ // Get the stylePath
+ if(d->currentStyle)
+ {
+ kdDebug(14000) << k_funcinfo << d->currentStyle->getStylePath() << endl;
+ p->setStylePath(d->currentStyle->getStylePath());
+ }
+ // Get and save the styleVariant
+ if( !d->currentVariantMap.empty() )
+ {
+ kdDebug(14000) << k_funcinfo << d->currentVariantMap[ d->mPrfsChatWindow->variantList->currentText()] << endl;
+ p->setStyleVariant(d->currentVariantMap[ d->mPrfsChatWindow->variantList->currentText()]);
+ }
+
+ // "Contact List" TAB =======================================================
+ p->setTreeView(d->mPrfsContactList->mTreeContactList->isChecked());
+ p->setSortByGroup(d->mPrfsContactList->mSortByGroup->isChecked());
+ p->setContactListIndentContacts(d->mPrfsContactList->mIndentContacts->isChecked());
+ p->setContactListDisplayMode(KopetePrefs::ContactDisplayMode(d->mPrfsContactList->mDisplayMode->selectedId()));
+ p->setContactListIconMode(KopetePrefs::IconDisplayMode((d->mPrfsContactList->mIconMode->isChecked()) ? KopetePrefs::PhotoPic : KopetePrefs::IconPic));
+ p->setContactListAnimation(d->mPrfsContactList->mAnimateChanges->isChecked());
+ p->setContactListFading(d->mPrfsContactList->mFadeVisibility->isChecked());
+ p->setContactListFolding(d->mPrfsContactList->mFoldVisibility->isChecked());
+
+ // "Colors & Fonts" TAB =====================================================
+ p->setHighlightBackground(d->mPrfsColors->backgroundColor->color());
+ p->setHighlightForeground(d->mPrfsColors->foregroundColor->color());
+ p->setBgColor(d->mPrfsColors->bgColor->color());
+ p->setTextColor(d->mPrfsColors->textColor->color());
+ p->setLinkColor(d->mPrfsColors->linkColor->color());
+ p->setFontFace(d->mPrfsColors->fontFace->font());
+ p->setIdleContactColor(d->mPrfsColors->idleContactColor->color());
+ p->setGreyIdleMetaContacts(d->mPrfsColors->mGreyIdleMetaContacts->isChecked());
+ p->setContactListUseCustomFonts(d->mPrfsColors->mUseCustomFonts->isChecked());
+ p->setContactListCustomSmallFont(d->mPrfsColors->mSmallFont->font());
+ p->setContactListCustomNormalFont(d->mPrfsColors->mNormalFont->font());
+ p->setContactListGroupNameColor(d->mPrfsColors->mGroupNameColor->color());
+ p->setContactListAutoHide(d->mPrfsContactList->mAutoHide->isChecked());
+ p->setContactListAutoHideTimeout(d->mPrfsContactList->mAutoHideTimeout->value());
+
+ p->setBgOverride( d->mPrfsColors->mBgOverride->isChecked() );
+ p->setFgOverride( d->mPrfsColors->mFgOverride->isChecked() );
+ p->setRtfOverride( d->mPrfsColors->mRtfOverride->isChecked() );
+
+ p->save();
+ d->styleChanged = false;
+}
+
+void AppearanceConfig::load()
+{
+ //we will change the state of somme controls, which will call some signals.
+ //so to don't refresh everything several times, we memorize we are loading.
+ d->loading=true;
+
+// kdDebug(14000) << k_funcinfo << "called" << endl;
+ KopetePrefs *p = KopetePrefs::prefs();
+
+ // "Emoticons" TAB ==========================================================
+ updateEmoticonlist();
+ d->mPrfsEmoticons->chkUseEmoticons->setChecked( p->useEmoticons() );
+ d->mPrfsEmoticons->chkRequireSpaces->setChecked( p->emoticonsRequireSpaces() );
+
+ // "Chat Window" TAB ========================================================
+ d->mPrfsChatWindow->groupConsecutiveMessages->setChecked( p->groupConsecutiveMessages() );
+ // Look for avaiable chat window styles.
+ slotLoadChatStyles();
+
+ // "Contact List" TAB =======================================================
+ d->mPrfsContactList->mTreeContactList->setChecked( p->treeView() );
+ d->mPrfsContactList->mSortByGroup->setChecked( p->sortByGroup() );
+ d->mPrfsContactList->mIndentContacts->setChecked( p->contactListIndentContacts() );
+
+ // convert old single value display mode to dual display/icon modes
+ if (p->contactListDisplayMode() == KopetePrefs::Yagami) {
+ p->setContactListDisplayMode( KopetePrefs::Detailed);
+ p->setContactListIconMode( KopetePrefs::PhotoPic );
+ }
+
+ d->mPrfsContactList->mDisplayMode->setButton( p->contactListDisplayMode() );
+ d->mPrfsContactList->mIconMode->setChecked( p->contactListIconMode() == KopetePrefs::PhotoPic);
+
+
+ d->mPrfsContactList->mAnimateChanges->setChecked( p->contactListAnimation() );
+#ifdef HAVE_XRENDER
+ d->mPrfsContactList->mFadeVisibility->setChecked( p->contactListFading() );
+#else
+ d->mPrfsContactList->mFadeVisibility->setChecked( false );
+#endif
+ d->mPrfsContactList->mFoldVisibility->setChecked( p->contactListFolding() );
+ d->mPrfsContactList->mAutoHide->setChecked( p->contactListAutoHide() );
+ d->mPrfsContactList->mAutoHideTimeout->setValue( p->contactListAutoHideTimeout() );
+
+ // "Colors & Fonts" TAB =====================================================
+ d->mPrfsColors->foregroundColor->setColor(p->highlightForeground());
+ d->mPrfsColors->backgroundColor->setColor(p->highlightBackground());
+ d->mPrfsColors->textColor->setColor(p->textColor());
+ d->mPrfsColors->linkColor->setColor(p->linkColor());
+ d->mPrfsColors->bgColor->setColor(p->bgColor());
+ d->mPrfsColors->fontFace->setFont(p->fontFace());
+ d->mPrfsColors->mGreyIdleMetaContacts->setChecked(p->greyIdleMetaContacts());
+ d->mPrfsColors->idleContactColor->setColor(p->idleContactColor());
+ d->mPrfsColors->mUseCustomFonts->setChecked(p->contactListUseCustomFonts());
+ d->mPrfsColors->mSmallFont->setFont(p->contactListCustomSmallFont());
+ d->mPrfsColors->mNormalFont->setFont(p->contactListCustomNormalFont());
+ d->mPrfsColors->mGroupNameColor->setColor(p->contactListGroupNameColor());
+
+ d->mPrfsColors->mBgOverride->setChecked( p->bgOverride() );
+ d->mPrfsColors->mFgOverride->setChecked( p->fgOverride() );
+ d->mPrfsColors->mRtfOverride->setChecked( p->rtfOverride() );
+
+ d->loading=false;
+ slotUpdateChatPreview();
+}
+
+void AppearanceConfig::slotLoadChatStyles()
+{
+ d->mPrfsChatWindow->styleList->clear();
+ d->styleItemMap.clear();
+
+ ChatWindowStyleManager::StyleList availableStyles;
+ availableStyles = ChatWindowStyleManager::self()->getAvailableStyles();
+ if( availableStyles.empty() )
+ kdDebug(14000) << k_funcinfo << "Warning, available styles is empty !" << endl;
+
+ ChatWindowStyleManager::StyleList::ConstIterator it, itEnd = availableStyles.constEnd();
+ for(it = availableStyles.constBegin(); it != itEnd; ++it)
+ {
+ // Insert style name into the listbox
+ d->mPrfsChatWindow->styleList->insertItem( it.key(), 0 );
+ // Insert the style class into the internal map for futher acces.
+ d->styleItemMap.insert( d->mPrfsChatWindow->styleList->firstItem(), it.data() );
+
+ if( it.data() == KopetePrefs::prefs()->stylePath() )
+ {
+ kdDebug(14000) << k_funcinfo << "Restoring saved style: " << it.key() << endl;
+
+ d->mPrfsChatWindow->styleList->setSelected( d->mPrfsChatWindow->styleList->firstItem(), true );
+ }
+ }
+
+ d->mPrfsChatWindow->styleList->sort();
+}
+
+void AppearanceConfig::updateEmoticonlist()
+{
+ KopetePrefs *p = KopetePrefs::prefs();
+ KStandardDirs dir;
+
+ d->mPrfsEmoticons->icon_theme_list->clear(); // Wipe out old list
+ // Get a list of directories in our icon theme dir
+ QStringList themeDirs = KGlobal::dirs()->findDirs("emoticons", "");
+ // loop adding themes from all dirs into theme-list
+ for(unsigned int x = 0;x < themeDirs.count();x++)
+ {
+ QDir themeQDir(themeDirs[x]);
+ themeQDir.setFilter( QDir::Dirs ); // only scan for subdirs
+ themeQDir.setSorting( QDir::Name ); // I guess name is as good as any
+ for(unsigned int y = 0; y < themeQDir.count(); y++)
+ {
+ QStringList themes = themeQDir.entryList(QDir::Dirs, QDir::Name);
+ // We don't care for '.' and '..'
+ if ( themeQDir[y] != "." && themeQDir[y] != ".." )
+ {
+ // Add ourselves to the list, using our directory name FIXME: use the first emoticon of the theme.
+ QPixmap previewPixmap = QPixmap(locate("emoticons", themeQDir[y]+"/smile.png"));
+ d->mPrfsEmoticons->icon_theme_list->insertItem(previewPixmap,themeQDir[y]);
+ }
+ }
+ }
+
+ // Where is that theme in our big-list-o-themes?
+ QListBoxItem *item = d->mPrfsEmoticons->icon_theme_list->findItem( p->iconTheme() );
+
+ if (item) // found it... make it the currently selected theme
+ d->mPrfsEmoticons->icon_theme_list->setCurrentItem( item );
+ else // Er, it's not there... select the current item
+ d->mPrfsEmoticons->icon_theme_list->setCurrentItem( 0 );
+}
+
+void AppearanceConfig::slotSelectedEmoticonsThemeChanged()
+{
+ QString themeName = d->mPrfsEmoticons->icon_theme_list->currentText();
+ QFileInfo fileInf(KGlobal::dirs()->findResource("emoticons", themeName+"/"));
+ d->mPrfsEmoticons->btnRemoveTheme->setEnabled( fileInf.isWritable() );
+
+ Kopete::Emoticons emoticons( themeName );
+ QStringList smileys = emoticons.emoticonAndPicList().keys();
+ QString newContentText = "<qt>";
+
+ for(QStringList::Iterator it = smileys.begin(); it != smileys.end(); ++it )
+ newContentText += QString::fromLatin1("<img src=\"%1\"> ").arg(*it);
+
+ newContentText += QString::fromLatin1("</qt>");
+ d->mPrfsEmoticons->icon_theme_preview->setText(newContentText);
+ emitChanged();
+}
+
+void AppearanceConfig::slotHighlightChanged()
+{
+// bool value = mPrfsChatWindow->highlightEnabled->isChecked();
+// mPrfsChatWindow->foregroundColor->setEnabled ( value );
+// mPrfsChatWindow->backgroundColor->setEnabled ( value );
+ slotUpdateChatPreview();
+}
+
+void AppearanceConfig::slotChangeFont()
+{
+ slotUpdateChatPreview();
+ emitChanged();
+}
+
+void AppearanceConfig::slotChatStyleSelected()
+{
+ // Retrieve variant list.
+ QString stylePath = d->styleItemMap[d->mPrfsChatWindow->styleList->selectedItem()];
+ d->currentStyle = ChatWindowStyleManager::self()->getStyleFromPool( stylePath );
+
+ if(d->currentStyle)
+ {
+ d->currentVariantMap = d->currentStyle->getVariants();
+ kdDebug(14000) << k_funcinfo << "Loading style: " << d->currentStyle->getStylePath() << endl;
+
+ // Update the variant list based on current style.
+ d->mPrfsChatWindow->variantList->clear();
+
+ // Add the no variant item to the list
+ // TODO: Use default name variant from Info.plist
+ // TODO: Select default variant from Info.plist
+ d->mPrfsChatWindow->variantList->insertItem( i18n("(No Variant)") );
+
+ ChatWindowStyle::StyleVariants::ConstIterator it, itEnd = d->currentVariantMap.constEnd();
+ int currentIndex = 0;
+ for(it = d->currentVariantMap.constBegin(); it != itEnd; ++it)
+ {
+ // Insert variant name into the combobox.
+ d->mPrfsChatWindow->variantList->insertItem( it.key() );
+
+ if( it.data() == KopetePrefs::prefs()->styleVariant() )
+ d->mPrfsChatWindow->variantList->setCurrentItem(currentIndex+1);
+
+ currentIndex++;
+ }
+
+ // Update the preview
+ slotUpdateChatPreview();
+ // Get the first variant to preview
+ // Check if the current style has variants.
+ if( !d->currentVariantMap.empty() )
+ d->preview->setStyleVariant(d->currentVariantMap[0]);
+
+ emitChanged();
+ }
+}
+
+void AppearanceConfig::slotChatStyleVariantSelected(const QString &variantName)
+{
+// kdDebug(14000) << k_funcinfo << variantName << endl;
+// kdDebug(14000) << k_funcinfo << d->currentVariantMap[variantName] << endl;
+
+ // Update the preview
+ d->preview->setStyleVariant(d->currentVariantMap[variantName]);
+ emitChanged();
+}
+
+void AppearanceConfig::slotInstallChatStyle()
+{
+ KURL styleToInstall = KFileDialog::getOpenURL( QString::null, QString::fromUtf8("application/x-zip application/x-tgz application/x-tbz"), this, i18n("Choose Chat Window style to install.") );
+
+ if( !styleToInstall.isEmpty() )
+ {
+ QString stylePath;
+ if( KIO::NetAccess::download( styleToInstall, stylePath, this ) )
+ {
+ int styleInstallReturn = 0;
+ styleInstallReturn = ChatWindowStyleManager::self()->installStyle( stylePath );
+ switch(styleInstallReturn)
+ {
+ case ChatWindowStyleManager::StyleCannotOpen:
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Error, i18n("The specified archive cannot be opened.\nMake sure that the archive is valid ZIP or TAR archive."), i18n("Can't open archive") );
+ break;
+ }
+ case ChatWindowStyleManager::StyleNoDirectoryValid:
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Error, i18n("Could not find a suitable place to install the Chat Window style in user directory."), i18n("Can't find styles directory") );
+ break;
+ }
+ case ChatWindowStyleManager::StyleNotValid:
+ KMessageBox::queuedMessageBox( this, KMessageBox::Error, i18n("The specified archive does not contain a valid Chat Window style."), i18n("Invalid Style") );
+ break;
+ case ChatWindowStyleManager::StyleInstallOk:
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Information, i18n("The Chat Window style was successfully installed."), i18n("Install successful") );
+ break;
+ }
+ case ChatWindowStyleManager::StyleUnknow:
+ default:
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Error, i18n("An unknow error occurred while trying to install the Chat Window style."), i18n("Unknow error") );
+ break;
+ }
+ }
+
+ // removeTempFile check if the file is a temp file, so it's ok for local files.
+ KIO::NetAccess::removeTempFile( stylePath );
+ }
+ }
+}
+
+void AppearanceConfig::slotDeleteChatStyle()
+{
+ QString styleName = d->mPrfsChatWindow->styleList->selectedItem()->text();
+ QString stylePathToDelete = d->styleItemMap[d->mPrfsChatWindow->styleList->selectedItem()];
+ if( ChatWindowStyleManager::self()->removeStyle(stylePathToDelete) )
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Information, i18n("It's the deleted style name", "The style %1 was successfully deleted.").arg(styleName));
+
+ // Get the first item in the stye List.
+ QString stylePath = (*d->styleItemMap.begin());
+ d->currentStyle = ChatWindowStyleManager::self()->getStyleFromPool(stylePath);
+ emitChanged();
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Information, i18n("It's the deleted style name", "An error occured while trying to delete %1 style.").arg(styleName));
+ }
+}
+
+void AppearanceConfig::slotGetChatStyles()
+{
+ // we need this because KNewStuffGeneric's install function isn't clever enough
+ KopeteStyleNewStuff *kopeteNewStuff = new KopeteStyleNewStuff( "kopete/chatstyle", this );
+ KNS::Engine *engine = new KNS::Engine( kopeteNewStuff, "kopete/chatstyle", this );
+ KNS::DownloadDialog *downloadDialog = new KNS::DownloadDialog( engine, this );
+ downloadDialog->setType( "kopete/chatstyle" );
+ // you have to do this by hand when providing your own Engine
+ KNS::ProviderLoader *provider = new KNS::ProviderLoader( this );
+ QObject::connect( provider, SIGNAL( providersLoaded(Provider::List*) ), downloadDialog, SLOT( slotProviders (Provider::List *) ) );
+ provider->load( "kopete/chatstyle", "http://download.kde.org/khotnewstuff/kopetestyles12-providers.xml" );
+ downloadDialog->exec();
+}
+
+// Reimplement Kopete::Contact and its abstract method
+// This is for style preview.
+class FakeContact : public Kopete::Contact
+{
+public:
+ FakeContact (Kopete::Account *account, const QString &id, Kopete::MetaContact *mc ) : Kopete::Contact( account, id, mc ) {}
+ virtual Kopete::ChatSession *manager(Kopete::Contact::CanCreateFlags /*c*/) { return 0L; }
+ virtual void slotUserInfo() {};
+};
+
+// This is for style preview.
+class FakeProtocol : public Kopete::Protocol
+{
+public:
+FakeProtocol( KInstance *instance, QObject *parent, const char *name ) : Kopete::Protocol(instance, parent, name){}
+Kopete::Account* createNewAccount( const QString &/*accountId*/ ){return 0L;}
+AddContactPage* createAddContactWidget( QWidget */*parent*/, Kopete::Account */*account*/){return 0L;}
+KopeteEditAccountWidget* createEditAccountWidget( Kopete::Account */*account*/, QWidget */*parent */){return 0L;}
+};
+
+// This is for style preview.
+class FakeAccount : public Kopete::Account
+{
+public:
+FakeAccount(Kopete::Protocol *parent, const QString &accountID, const char *name) : Kopete::Account(parent, accountID, name){}
+~FakeAccount()
+{}
+bool createContact( const QString &/*contactId*/, Kopete::MetaContact */*parentContact*/ ){return true;}
+void connect( const Kopete::OnlineStatus& /*initialStatus*/){}
+void disconnect(){}
+void setOnlineStatus( const Kopete::OnlineStatus& /*status*/ , const QString &/*reason*/){}
+};
+
+void AppearanceConfig::createPreviewChatSession()
+{
+ d->previewProtocol = new FakeProtocol( new KInstance(QCString("kopete-preview-chatwindowstyle")), 0L, "kopete-preview-chatwindowstyle");
+ d->previewAccount = new FakeAccount(d->previewProtocol, QString("previewaccount"), 0);
+
+ // Create fake meta/contacts
+ d->myselfMetaContact = new Kopete::MetaContact();
+ d->myself = new FakeContact(d->previewAccount, i18n("This is the myself preview contact id", "myself@preview"), d->myselfMetaContact);
+ d->myself->setNickName(i18n("This is the myself preview contact nickname", "Myself"));
+ d->jackMetaContact = new Kopete::MetaContact();
+ d->jack = new FakeContact(d->previewAccount, i18n("This is the other preview contact id", "jack@preview"), d->jackMetaContact);
+ d->jack->setNickName(i18n("This is the other preview contact nickname", "Jack"));
+ d->myselfMetaContact->setDisplayName(i18n("Myself"));
+ d->myselfMetaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ d->jackMetaContact->setDisplayName(i18n("Jack"));
+ d->jackMetaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+
+ Kopete::ContactPtrList contactList;
+ contactList.append(d->jack);
+ // Create fakeChatSession
+ d->previewChatSession = Kopete::ChatSessionManager::self()->create(d->myself, contactList, 0);
+ d->previewChatSession->setDisplayName("Preview Session");
+}
+
+void AppearanceConfig::createPreviewMessages()
+{
+ Kopete::Message msgIn( d->jack,d->myself, i18n( "Hello, this is an incoming message :-)" ), Kopete::Message::Inbound );
+ Kopete::Message msgIn2( d->jack, d->myself, i18n( "Hello, this is an incoming consecutive message." ), Kopete::Message::Inbound );
+
+ Kopete::Message msgOut( d->myself, d->jack, i18n( "Ok, this is an outgoing message" ), Kopete::Message::Outbound );
+ Kopete::Message msgOut2( d->myself, d->jack, i18n( "Ok, a outgoing consecutive message." ), Kopete::Message::Outbound );
+
+ Kopete::Message msgCol( d->jack, d->myself, i18n( "Here is an incoming colored message" ), Kopete::Message::Inbound );
+ msgCol.setFg( QColor( "DodgerBlue" ) );
+ msgCol.setBg( QColor( "LightSteelBlue" ) );
+ Kopete::Message msgInt( d->jack, d->myself, i18n( "This is an internal message" ), Kopete::Message::Internal );
+ Kopete::Message msgAct( d->jack, d->myself, i18n( "performed an action" ), Kopete::Message::Inbound,
+ Kopete::Message::PlainText, QString::null, Kopete::Message::TypeAction );
+ Kopete::Message msgHigh( d->jack, d->myself, i18n( "This is a highlighted message" ), Kopete::Message::Inbound );
+ msgHigh.setImportance( Kopete::Message::Highlight );
+ // This is a UTF-8 string btw.
+ Kopete::Message msgRightToLeft(d->myself, d->jack, i18n("This special UTF-8 string is to test if the style support Right-to-Left language display.", "הודעות טקסט"), Kopete::Message::Outbound);
+ Kopete::Message msgExplanation( d->myself, d->jack, i18n( "That message was in a Right-to-Left language, which Kopete also supports." ), Kopete::Message::Outbound );
+ Kopete::Message msgBye ( d->myself, d->jack, i18n( "Bye" ), Kopete::Message::Outbound );
+
+ // Add the messages to ChatMessagePart
+ d->preview->appendMessage(msgIn);
+ d->preview->appendMessage(msgIn2);
+ d->preview->appendMessage(msgOut);
+ d->preview->appendMessage(msgOut2);
+ d->preview->appendMessage(msgCol);
+ d->preview->appendMessage(msgInt);
+ d->preview->appendMessage(msgAct);
+ d->preview->appendMessage(msgHigh);
+ d->preview->appendMessage(msgRightToLeft);
+ d->preview->appendMessage(msgExplanation);
+ d->preview->appendMessage(msgBye);
+}
+
+void AppearanceConfig::slotUpdateChatPreview()
+{
+ if(d->loading || !d->currentStyle)
+ return;
+
+ // Update the preview
+ d->preview->setStyle(d->currentStyle);
+
+ emitChanged();
+}
+
+void AppearanceConfig::emitChanged()
+{
+ emit changed( true );
+}
+
+void AppearanceConfig::installEmoticonTheme()
+{
+ KURL themeURL = KURLRequesterDlg::getURL(QString::null, this,
+ i18n("Drag or Type Emoticon Theme URL"));
+ if ( themeURL.isEmpty() )
+ return;
+
+ //TODO: support remote theme files!
+ if ( !themeURL.isLocalFile() )
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Error, i18n("Sorry, emoticon themes must be installed from local files."),
+ i18n("Could Not Install Emoticon Theme") );
+ return;
+ }
+
+ Kopete::Global::installEmoticonTheme( themeURL.path() );
+ updateEmoticonlist();
+}
+
+void AppearanceConfig::removeSelectedEmoticonTheme()
+{
+ QListBoxItem *selected = d->mPrfsEmoticons->icon_theme_list->selectedItem();
+ if(selected==0)
+ return;
+
+ QString themeName = selected->text();
+
+ QString question=i18n("<qt>Are you sure you want to remove the "
+ "<strong>%1</strong> emoticon theme?<br>"
+ "<br>"
+ "This will delete the files installed by this theme.</qt>").
+ arg(themeName);
+
+ int res = KMessageBox::warningContinueCancel(this, question, i18n("Confirmation"),KStdGuiItem::del());
+ if (res!=KMessageBox::Continue)
+ return;
+
+ KURL themeUrl(KGlobal::dirs()->findResource("emoticons", themeName+"/"));
+ KIO::NetAccess::del(themeUrl, this);
+
+ updateEmoticonlist();
+}
+
+void AppearanceConfig::slotGetEmoticonThemes()
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup( "KNewStuff" );
+ config->writeEntry( "ProvidersUrl",
+ "http://download.kde.org/khotnewstuff/emoticons-providers.xml" );
+ config->writeEntry( "StandardResource", "emoticons" );
+ config->writeEntry( "Uncompress", "application/x-gzip" );
+ config->sync();
+
+#if ( KDE_IS_VERSION(3,3,90) )
+ KNS::DownloadDialog::open( "emoticons", i18n( "Get New Emoticons") );
+#else
+ KNS::DownloadDialog::open( i18n( "Get New Emoticons" ) );
+#endif
+
+ updateEmoticonlist();
+}
+
+void AppearanceConfig::slotEditTooltips()
+{
+ TooltipEditDialog *dlg = new TooltipEditDialog(this);
+ connect(dlg, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ dlg->exec();
+ delete dlg;
+}
+
+#include "appearanceconfig.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/appearance/appearanceconfig.h b/kopete/kopete/config/appearance/appearanceconfig.h
new file mode 100644
index 00000000..22a23024
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig.h
@@ -0,0 +1,70 @@
+/*
+ appearanceconfig.h - Kopete Look Feel Config
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __APPEARANCE_H
+#define __APPEARANCE_H
+
+#include "kcmodule.h"
+#include <qptrlist.h>
+#include <qmap.h>
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class AppearanceConfig : public KCModule
+{
+ Q_OBJECT
+
+friend class KopeteStyleNewStuff;
+
+public:
+ AppearanceConfig( QWidget *parent, const char *name, const QStringList &args );
+ ~AppearanceConfig();
+
+ virtual void save();
+ virtual void load();
+
+private slots:
+ void slotSelectedEmoticonsThemeChanged();
+ void slotUpdateChatPreview();
+ void slotHighlightChanged();
+ void slotChangeFont();
+ void slotInstallChatStyle();
+ void slotDeleteChatStyle();
+ void slotChatStyleSelected();
+ void slotChatStyleVariantSelected(const QString &variantName);
+ void slotEditTooltips();
+ void emitChanged();
+ void installEmoticonTheme();
+ void removeSelectedEmoticonTheme();
+ void slotGetEmoticonThemes();
+ void slotGetChatStyles();
+ void slotLoadChatStyles();
+ void updateEmoticonsButton(bool);
+private:
+ void updateEmoticonlist();
+ void createPreviewChatSession();
+ void createPreviewMessages();
+
+private:
+ class Private;
+ Private *d;
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/appearance/appearanceconfig_chatwindow.ui b/kopete/kopete/config/appearance/appearanceconfig_chatwindow.ui
new file mode 100644
index 00000000..129abdd5
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig_chatwindow.ui
@@ -0,0 +1,195 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AppearanceConfig_ChatWindow</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AppearanceConfig_ChatWindow</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>462</width>
+ <height>454</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Chat Window Appearance</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>stylesGroupBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Styles</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSplitter" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>splitter1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="KListBox">
+ <property name="name">
+ <cstring>styleList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>htmlFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>4</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnGetStyles</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Get New...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Get new Chat Window styles over the Internet</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>installButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Install...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>deleteButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Style Variant:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>variantList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Display</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupConsecutiveMessages</cstring>
+ </property>
+ <property name="text">
+ <string>Group consecuti&amp;ve messages</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>styleList</tabstop>
+ <tabstop>installButton</tabstop>
+ <tabstop>deleteButton</tabstop>
+</tabstops>
+<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/kopete/config/appearance/appearanceconfig_colors.ui b/kopete/kopete/config/appearance/appearanceconfig_colors.ui
new file mode 100644
index 00000000..6300b844
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig_colors.ui
@@ -0,0 +1,397 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AppearanceConfig_Colors</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AppearanceConfig_Colors</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>595</width>
+ <height>606</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Colors</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Chat Window</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Base font:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>foregroundColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Highlight foreground:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>linkColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="color">
+ <color>
+ <red>0</red>
+ <green>85</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="3">
+ <property name="name">
+ <cstring>backgroundColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Base font color:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>textColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="3">
+ <property name="name">
+ <cstring>bgColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="color">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Highlight background:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>Link color:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Background color:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>fontFace</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Formatting Overrides</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mBgOverride</cstring>
+ </property>
+ <property name="text">
+ <string>Do not show user specified &amp;background color</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mFgOverride</cstring>
+ </property>
+ <property name="text">
+ <string>Do not show user specified &amp;foreground color</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mRtfOverride</cstring>
+ </property>
+ <property name="text">
+ <string>Do not show user specified &amp;rich text</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3_2</cstring>
+ </property>
+ <property name="title">
+ <string>Contact List</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mUseCustomFonts</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom fonts for contact list items</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>mSmallFontLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Small font:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="0" column="1">
+ <property name="name">
+ <cstring>mNormalFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>mNormalFontLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Normal font:</string>
+ </property>
+ </widget>
+ <widget class="KFontRequester" row="1" column="1">
+ <property name="name">
+ <cstring>mSmallFont</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>mGroupNameColor</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>idleContactColor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>mGreyIdleMetaContacts</cstring>
+ </property>
+ <property name="text">
+ <string>Recolor contacts marked as idle:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Group name color:</string>
+ </property>
+ </widget>
+ </grid>
+ </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>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>mUseCustomFonts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mNormalFontLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseCustomFonts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mNormalFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseCustomFonts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mSmallFontLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseCustomFonts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mSmallFont</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mGreyIdleMetaContacts</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>idleContactColor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>bgColor</tabstop>
+ <tabstop>textColor</tabstop>
+ <tabstop>linkColor</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kfontrequester.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/appearance/appearanceconfig_contactlist.ui b/kopete/kopete/config/appearance/appearanceconfig_contactlist.ui
new file mode 100644
index 00000000..4c9c7934
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig_contactlist.ui
@@ -0,0 +1,349 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AppearanceConfig_ContactList</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>AppearanceConfig_ContactList</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>707</width>
+ <height>445</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Contact List Appearance</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Layout</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mSortByGroup</cstring>
+ </property>
+ <property name="text">
+ <string>Arrange metacontacts by &amp;group</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mTreeContactList</cstring>
+ </property>
+ <property name="text">
+ <string>Show tree &amp;branch lines</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mIndentContacts</cstring>
+ </property>
+ <property name="text">
+ <string>In&amp;dent contacts</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>groupBox10</cstring>
+ </property>
+ <property name="title">
+ <string>Contact Display Mode</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>mDisplayMode</cstring>
+ </property>
+ <property name="title">
+ <string>List Style</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioButton8</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Classic, left-aligned status icons</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioButton9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Right-aligned status icons</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioButton10</cstring>
+ </property>
+ <property name="text">
+ <string>Detailed &amp;view</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mIconMode</cstring>
+ </property>
+ <property name="text">
+ <string>Use contact photos when available</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Contact List Auto-Hide</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When enabled, the contact list will automatically be hidden a fixed amount of time after the mouse cursor leaves the window. You can set the amount of time in the 'Time until autohide' box below.</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mAutoHide</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;uto-hide contact list</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mAutoHideTimeout</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="suffix">
+ <string> Sec</string>
+ </property>
+ <property name="maxValue">
+ <number>300</number>
+ </property>
+ <property name="minValue">
+ <number>3</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The timeout value for both contact list and scrollbar auto-hiding.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>after the cursor left the window</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>81</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Contact List Animations</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mAnimateChanges</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Animate changes to contact list items</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mFadeVisibility</cstring>
+ </property>
+ <property name="text">
+ <string>Fade in / out contacts as the&amp;y appear / disappear</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mFoldVisibility</cstring>
+ </property>
+ <property name="text">
+ <string>Fo&amp;ld in / out contacts as they appear / disappear</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>mEditTooltips</cstring>
+ </property>
+ <property name="text">
+ <string>Change &amp;Tooltip Contents...</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>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>mTreeContactList</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mIndentContacts</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/config/appearance/appearanceconfig_emoticons.ui b/kopete/kopete/config/appearance/appearanceconfig_emoticons.ui
new file mode 100644
index 00000000..8649e4c2
--- /dev/null
+++ b/kopete/kopete/config/appearance/appearanceconfig_emoticons.ui
@@ -0,0 +1,214 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AppearanceConfig_Emoticons</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AppearanceConfig_Emoticons</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>541</width>
+ <height>395</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkUseEmoticons</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use emoticons</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, the text representation of emoticons in messages will be replaced by an image</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkRequireSpaces</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Require separators (spaces) around emoticons</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, only emoticons that are separated from the text by spaces will be shown as images.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>Select emoticon theme:</string>
+ </property>
+ </widget>
+ <widget class="KListBox">
+ <property name="name">
+ <cstring>icon_theme_list</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblPreview</cstring>
+ </property>
+ <property name="text">
+ <string>Preview:</string>
+ </property>
+ </widget>
+ <widget class="KTextEdit">
+ <property name="name">
+ <cstring>icon_theme_preview</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnGetThemes</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Get New Themes...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Download emoticon theme from the Internet</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnInstallTheme</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Install Theme File...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRemoveTheme</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Remove Theme</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>chkRequireSpaces</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblPreview</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>icon_theme_list</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblPreview</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>btnGetThemes</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>btnInstallTheme</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkUseEmoticons</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>btnRemoveTheme</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+ <includehint>ktextedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/appearance/kopete_appearanceconfig.desktop b/kopete/kopete/config/appearance/kopete_appearanceconfig.desktop
new file mode 100644
index 00000000..e9b941b7
--- /dev/null
+++ b/kopete/kopete/config/appearance/kopete_appearanceconfig.desktop
@@ -0,0 +1,130 @@
+[Desktop Entry]
+Icon=looknfeel
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_appearanceconfig
+X-KDE-FactoryName=KopeteAppearanceConfigFactory
+X-KDE-ParentApp=kopete
+X-KDE-ParentComponents=kopete
+
+Name=Appearance
+Name[ar]=المظهر
+Name[be]=Вонкавы выглÑд
+Name[bg]=Външен вид
+Name[bn]=চেহারা
+Name[br]=Neuziadur
+Name[bs]=Izgled
+Name[ca]=Aparença
+Name[cs]=Vzhled
+Name[cy]=Golwg
+Name[da]=Udseende
+Name[de]=Erscheinungsbild
+Name[el]=Εμφάνιση
+Name[eo]=Apero
+Name[es]=Apariencia
+Name[et]=Välimus
+Name[eu]=Itxura
+Name[fa]=ظاهر
+Name[fi]=Ulkonäkö
+Name[fr]=Apparence
+Name[ga]=Cuma
+Name[gl]=Apariencia
+Name[he]=מר××”
+Name[hi]=शकà¥à¤²-सूरत
+Name[hr]=Izgled
+Name[hu]=Megjelenés
+Name[is]=Útlit
+Name[it]=Aspetto
+Name[ja]=外観
+Name[ka]=იერსáƒáƒ®áƒ”
+Name[kk]=Сыртқы көрініÑÑ–
+Name[km]=រូបរាង
+Name[lt]=IÅ¡vaizda
+Name[mk]=Изглед
+Name[nb]=Utseende
+Name[nds]=Utsehn
+Name[ne]=दृशà¥à¤¯
+Name[nl]=Uiterlijk
+Name[nn]=Utsjånad
+Name[pa]=ਦਿੱਖ
+Name[pl]=WyglÄ…d
+Name[pt]=Aparência
+Name[pt_BR]=Aparência
+Name[ro]=Aspect
+Name[ru]=Внешний вид
+Name[rw]=Imigaragarire
+Name[se]=Fárda
+Name[sk]=Vzhľad
+Name[sl]=Videz
+Name[sr]=Изглед
+Name[sr@Latn]=Izgled
+Name[sv]=Utseende
+Name[tg]=Ðамуди зоҳирӣ
+Name[tr]=Görünüm
+Name[uk]=ВиглÑд
+Name[uz]=Koʻrinishi
+Name[uz@cyrillic]=Кўриниши
+Name[wa]=Rivnance
+Name[zh_CN]=外观
+Name[zh_HK]=外觀
+Name[zh_TW]=外觀
+Comment=Here You Can Alter Kopete's Look And Feel
+Comment[ar]=يمكنك تغيير مظهر Kopete
+Comment[be]=Вонкавы выглÑд Kopete
+Comment[bg]=ÐаÑтройване Ð²ÑŠÐ½ÑˆÐ½Ð¸Ñ Ð²Ð¸Ð´ на програмата
+Comment[bn]=আপনি à¦à¦–ানে কপেটের চেহারা ও কারà§à¦¯à¦•à¦¾à¦°à¦¿à¦¤à¦¾ পরিবরà§à¦¤à¦¨ করতে পারেন
+Comment[bs]=Ovdje možete izmijeniti izgled Kopete-a
+Comment[ca]=Aquí podreu modificar l'aparença i comportament de Kopete
+Comment[cs]=Zde je možné přizpůsobit si vzhled a chování Kopete
+Comment[cy]=Yma Gallwch Addasu Golwg a Theimlad Kopete
+Comment[da]=Her kan du ændre Kopete's udseende
+Comment[de]=Hier können Sie Kopetes Erscheinungsbild verändern
+Comment[el]=Εδώ μποÏείτε να Ï„Ïοποποιήσετε την όψη και αίσθηση του Kopete
+Comment[es]=Aquí­ puede modificar el aspecto de Kopete
+Comment[et]=Siin saab muuta Kopete väljanägemist
+Comment[eu]=Hemen Kopete-ren itxura eta izaera alda dezakezu
+Comment[fa]=اینجا می‌توانید ویژگیهای ظاهری Kopete را تغییر دهید
+Comment[fi]=Täältä voit muuttaa Kopeten ulkonäköä ja tuntumaa
+Comment[fr]=Vous pouvez modifier l'apparence et l'ergonomie de Kopete ici
+Comment[gl]=Aquí podes modificar o aspecto de Kopete
+Comment[he]=×›×ן תוכל לשנות ×ת המר××” של Kopete
+Comment[hi]=यहाठआप के-ऑपà¥à¤Ÿà¥€ के रूप और विनà¥à¤¯à¤¾à¤¸ को बदल सकते हैं
+Comment[hr]=Ovde možete promijeniti Kopeteov izgled i naÄin rada
+Comment[hu]=Itt lehet megváltoztatni a Kopete grafikai megjelenését
+Comment[is]=Hér er hægt að breyta útliti og viðmóti Kopete
+Comment[it]=Qui puoi modificare l'aspetto di Kopete
+Comment[ja]=ã“ã“㧠Kopete ã®å¤–観をカスタマイズã—ã¾ã™
+Comment[ka]=áƒáƒ¥ თქვენ შეგიძლიáƒáƒ— Kopeteს იერსáƒáƒ®áƒ˜áƒ¡ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ
+Comment[kk]=Мұнда Kopete-Ñ‚Ñ–Ò£ Ñырқы көрініÑін өзгерте алаÑыз
+Comment[km]=ទី​នáŸáŸ‡ អ្នក​អាច​ប្ដូរ​រូបរាង និង​មុážáž„ារ​របស់ Kopete​
+Comment[lt]=Čia galite keisti Kopete išvaizdą ir pojūtį
+Comment[mk]=Тука можете да го менувате изгледот на Kopete
+Comment[nb]=Endre utseendet på Kopete
+Comment[nds]=Hier kannst Du dat Utsehn vun Kopete ännern
+Comment[ne]=यहाठतपाईठकोपेटको हेराइ र महसà¥à¤¸ अलà¥à¤Ÿà¤° गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Hier kunt u uw het uiterlijk van Kopete aanpassen
+Comment[nn]=Endra utsjånaden til Kopete
+Comment[pl]=Tutaj można zmienić wygląd i zachowanie Kopete
+Comment[pt]=Aqui Você Pode Alterar a Aparência e Comportamento do Kopete
+Comment[pt_BR]=Aqui você pode alterar a aparência do Kopete
+Comment[ro]=Aici puteţi modifica aspectul Kopete
+Comment[ru]=ÐаÑтройка внешнего вида Kopete
+Comment[se]=Dáppe sáhtát rievdadit Kopete:a fárdda
+Comment[sk]=Tu môžete upraviť vzhľad Kopete
+Comment[sl]=Tukaj lahko spreminjate izgled in obÄutek za Kopete
+Comment[sr]=Овде можете променити Kopete-ов изглед и оÑећај
+Comment[sr@Latn]=Ovde možete promeniti Kopete-ov izgled i osećaj
+Comment[sv]=Här kan du ändra Kopetes utseende och känsla
+Comment[tg]=Дар Ин Ҷо Шумо Ðамуди Зоҳирии Kopete-и Худро Тағир Дода Метавонед
+Comment[tr]=Kopete'nin Görünüm ve Dokusunu Buradan Değiştirebilirsiniz
+Comment[uk]=Тут можна наладнати зовнішній виглÑд Kopete
+Comment[uz]=Bu yerda Kopete koʻrinishini moslash mumkin
+Comment[uz@cyrillic]=Бу ерда Kopete кўринишини моÑлаш мумкин
+Comment[zh_CN]=您å¯åœ¨æ­¤æ›´æ”¹ Kopete 的观感
+Comment[zh_HK]=在此您å¯æ”¹å‹• Kopete 的外觀
+Comment[zh_TW]=您å¯ä»¥åœ¨æ­¤æ”¹è®Š Kopete 的外觀與感覺
+DocPath=kopete/configure-dialog.html#configuring-appearance
+
+
diff --git a/kopete/kopete/config/appearance/tooltipeditdialog.cpp b/kopete/kopete/config/appearance/tooltipeditdialog.cpp
new file mode 100644
index 00000000..c8ed8a5d
--- /dev/null
+++ b/kopete/kopete/config/appearance/tooltipeditdialog.cpp
@@ -0,0 +1,226 @@
+/*
+ tooltipeditdialog.cpp - Kopete Tooltip Editor
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "tooltipeditdialog.h"
+#include "tooltipeditwidget.h"
+
+#include "kopetecontactproperty.h"
+#include "kopeteglobal.h"
+#include "kopeteprefs.h"
+
+#include <qapplication.h>
+#include <qtoolbutton.h>
+#include <qheader.h>
+#include <qstringlist.h>
+
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+
+class TooltipItem : public KListViewItem
+{
+ public:
+ TooltipItem(KListView *parent, const QString& label, const QString& propertyName)
+ : KListViewItem(parent, label),
+ mPropName(propertyName)
+ {
+ }
+
+ TooltipItem(KListView *parent, QListViewItem *item, const QString& label, const QString& propertyName)
+ : KListViewItem(parent, item, label),
+ mPropName(propertyName)
+ {
+ }
+
+ QString propertyName() const { return mPropName; }
+ private:
+ QString mPropName;
+};
+
+
+
+TooltipEditDialog::TooltipEditDialog(QWidget *parent, const char* name)
+ : KDialogBase(parent, name, true, i18n("Tooltip Editor"), Ok|Cancel, Ok, true)
+{
+ mMainWidget = new TooltipEditWidget(this, "TooltipEditDialog::mMainWidget");
+ setMainWidget(mMainWidget);
+ mMainWidget->lstUsedItems->header()->hide();
+ mMainWidget->lstUnusedItems->header()->hide();
+ mMainWidget->lstUsedItems->setSorting( -1 );
+ mMainWidget->lstUnusedItems->setSorting( 0 );
+
+ const Kopete::ContactPropertyTmpl::Map propmap(
+ Kopete::Global::Properties::self()->templateMap());
+ QStringList usedKeys = KopetePrefs::prefs()->toolTipContents();
+
+ connect(mMainWidget->lstUnusedItems, SIGNAL(doubleClicked ( QListViewItem *, const QPoint &, int )), this, SLOT(slotAddButton()));
+ connect(mMainWidget->lstUsedItems, SIGNAL(doubleClicked ( QListViewItem *, const QPoint &, int )), this, SLOT(slotRemoveButton()));
+
+ // first fill the "used" list
+ QStringList::Iterator usedIt=usedKeys.end();
+ do
+ {
+ usedIt--;
+ // only add if that property key is really known
+ if(propmap.contains(*usedIt) && !propmap[*usedIt].isPrivate())
+ {
+ new TooltipItem(mMainWidget->lstUsedItems,
+ propmap[*usedIt].label(), *usedIt);
+ }
+ } while(usedIt != usedKeys.begin());
+
+ // then iterate over all known properties and insert the remaining ones
+ // into the "unused" list
+ Kopete::ContactPropertyTmpl::Map::ConstIterator it;
+ for(it = propmap.begin(); it != propmap.end(); ++it)
+ {
+ if((usedKeys.contains(it.key())==0) && (!it.data().isPrivate()))
+ new TooltipItem(mMainWidget->lstUnusedItems, it.data().label(), it.key());
+ }
+
+ connect(mMainWidget->lstUnusedItems, SIGNAL(selectionChanged(QListViewItem *)),
+ this, SLOT(slotUnusedSelected(QListViewItem *)));
+ connect(mMainWidget->lstUsedItems, SIGNAL(selectionChanged(QListViewItem *)),
+ this, SLOT(slotUsedSelected(QListViewItem *)));
+
+ QIconSet iconSet;
+ iconSet = SmallIconSet("up");
+ mMainWidget->tbUp->setIconSet(iconSet);
+ mMainWidget->tbUp->setEnabled(false);
+ mMainWidget->tbUp->setAutoRepeat(true);
+ connect(mMainWidget->tbUp, SIGNAL(clicked()), SLOT(slotUpButton()));
+
+ iconSet = SmallIconSet("down");
+ mMainWidget->tbDown->setIconSet(iconSet);
+ mMainWidget->tbDown->setEnabled(false);
+ mMainWidget->tbDown->setAutoRepeat(true);
+ connect(mMainWidget->tbDown, SIGNAL(clicked()), SLOT(slotDownButton()));
+
+ iconSet = QApplication::reverseLayout() ? SmallIconSet("back") : SmallIconSet("forward");
+ mMainWidget->tbAdd->setIconSet(iconSet);
+ mMainWidget->tbAdd->setEnabled(false);
+ connect(mMainWidget->tbAdd, SIGNAL(clicked()), SLOT(slotAddButton()));
+
+ iconSet = QApplication::reverseLayout() ? SmallIconSet("forward") : SmallIconSet("back");
+ mMainWidget->tbRemove->setIconSet(iconSet);
+ mMainWidget->tbRemove->setEnabled(false);
+ connect(mMainWidget->tbRemove, SIGNAL(clicked()), SLOT(slotRemoveButton()));
+
+ connect(this, SIGNAL(okClicked()), this, SLOT(slotOkClicked()));
+
+ resize(QSize(450, 450));
+}
+
+void TooltipEditDialog::slotOkClicked()
+{
+ QStringList oldList = KopetePrefs::prefs()->toolTipContents();
+ QStringList newList;
+ QListViewItemIterator it(mMainWidget->lstUsedItems);
+ QString keyname;
+
+ while(it.current())
+ {
+ keyname = static_cast<TooltipItem *>(it.current())->propertyName();
+ newList += keyname;
+ kdDebug(14000) << k_funcinfo <<
+ "Adding key '" << keyname << "' to tooltip list" << endl;
+ ++it;
+ }
+
+ if(oldList != newList)
+ {
+ KopetePrefs::prefs()->setToolTipContents(newList);
+ emit changed(true);
+ kdDebug(14000) << k_funcinfo << "tooltip fields changed, emitting changed()" << endl;
+ }
+}
+
+
+void TooltipEditDialog::slotUnusedSelected(QListViewItem *item)
+{
+ //mMainWidget->tbRemove->setEnabled(false);
+ mMainWidget->tbAdd->setEnabled(item!=0);
+}
+
+void TooltipEditDialog::slotUsedSelected(QListViewItem *item)
+{
+ mMainWidget->tbRemove->setEnabled(item!=0);
+ //mMainWidget->tbAdd->setEnabled(false);
+ if (item)
+ {
+ mMainWidget->tbUp->setEnabled(item->itemAbove() != 0);
+ mMainWidget->tbDown->setEnabled(item->itemBelow() != 0);
+ }
+ else
+ {
+ mMainWidget->tbUp->setEnabled(false);
+ mMainWidget->tbDown->setEnabled(false);
+ }
+}
+
+void TooltipEditDialog::slotUpButton()
+{
+ QListViewItem *item = mMainWidget->lstUsedItems->currentItem();
+ QListViewItem *prev = item->itemAbove();
+ if(prev == 0) // we are first item already
+ return;
+
+ prev->moveItem(item);
+ slotUsedSelected(item);
+}
+
+void TooltipEditDialog::slotDownButton()
+{
+ QListViewItem *item = mMainWidget->lstUsedItems->currentItem();
+ QListViewItem *next = item->itemBelow();
+ if(next == 0) // we are last item already
+ return;
+
+ item->moveItem(next);
+ slotUsedSelected(item);
+}
+
+void TooltipEditDialog::slotAddButton()
+{
+ TooltipItem *item = static_cast<TooltipItem *>(mMainWidget->lstUnusedItems->currentItem());
+ if(!item)
+ return;
+ //kdDebug(14000) << k_funcinfo << endl;
+
+ // build a new one in the "used" list
+ new TooltipItem(mMainWidget->lstUsedItems, item->text(0), item->propertyName());
+
+ // remove the old one from "unused" list
+ mMainWidget->lstUnusedItems->takeItem(item);
+ delete item;
+}
+
+void TooltipEditDialog::slotRemoveButton()
+{
+ TooltipItem *item = static_cast<TooltipItem *>(mMainWidget->lstUsedItems->currentItem());
+ if(!item)
+ return;
+ //kdDebug(14000) << k_funcinfo << endl;
+
+ // build a new one in the "unused" list
+ new TooltipItem(mMainWidget->lstUnusedItems, item->text(0), item->propertyName());
+
+ // remove the old one from "used" list
+ mMainWidget->lstUsedItems->takeItem(item);
+ delete item;
+}
+
+#include "tooltipeditdialog.moc"
diff --git a/kopete/kopete/config/appearance/tooltipeditdialog.h b/kopete/kopete/config/appearance/tooltipeditdialog.h
new file mode 100644
index 00000000..92d75aa9
--- /dev/null
+++ b/kopete/kopete/config/appearance/tooltipeditdialog.h
@@ -0,0 +1,49 @@
+/*
+ tooltipeditdialog.cpp - Kopete Tooltip Editor
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TOOLTIPEDITDIALOG_H
+#define TOOLTIPEDITDIALOG_H
+
+#include <kdebug.h>
+#include <qhbox.h>
+#include <kdialogbase.h>
+class TooltipEditWidget;
+
+class TooltipEditDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ TooltipEditDialog(QWidget *parent=0, const char* name="ToolTipEditDialog");
+
+ private slots:
+ void slotUnusedSelected(QListViewItem *);
+ void slotUsedSelected(QListViewItem *);
+ void slotUpButton();
+ void slotDownButton();
+ void slotAddButton();
+ void slotRemoveButton();
+ void slotOkClicked();
+
+ signals:
+ void changed(bool);
+
+ private:
+ TooltipEditWidget *mMainWidget;
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/appearance/tooltipeditwidget.ui b/kopete/kopete/config/appearance/tooltipeditwidget.ui
new file mode 100644
index 00000000..f00b8f26
--- /dev/null
+++ b/kopete/kopete/config/appearance/tooltipeditwidget.ui
@@ -0,0 +1,215 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>TooltipEditWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TooltipEditWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>489</width>
+ <height>418</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="Line" row="1" column="0">
+ <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" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Using the arrow buttons, put on the right the items you want to see in the contact tooltips. You can then sort them.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Here you can customize the contact tooltips&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lstUnusedItems</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list contains elements which are currently &lt;b&gt;not present&lt;/b&gt; in the contact tooltip.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <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>60</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QToolButton" row="2" column="1">
+ <property name="name">
+ <cstring>tbDown</cstring>
+ </property>
+ <property name="text">
+ <string>v</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this arrow to reorder the items in the list.</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="0" column="1">
+ <property name="name">
+ <cstring>tbUp</cstring>
+ </property>
+ <property name="text">
+ <string>^</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="0">
+ <property name="name">
+ <cstring>tbRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;</string>
+ </property>
+ </widget>
+ <widget class="QToolButton" row="1" column="2">
+ <property name="name">
+ <cstring>tbAdd</cstring>
+ </property>
+ <property name="text">
+ <string>&gt;</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this arrows to add or remove items to your contact tooltips.</string>
+ </property>
+ </widget>
+ </grid>
+ </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>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lstUsedItems</cstring>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list contains elements which are currently &lt;b&gt;present&lt;/b&gt; in the contact tooltips.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/avdevice/Makefile.am b/kopete/kopete/config/avdevice/Makefile.am
new file mode 100644
index 00000000..f1c9303a
--- /dev/null
+++ b/kopete/kopete/config/avdevice/Makefile.am
@@ -0,0 +1,24 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(top_srcdir)/kopete/libkopete/avdevice \
+ -I$(top_srcdir)/kopete/libkopete/private $(KOPETE_COMPAT_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_kopete_avdeviceconfig.la
+
+kcm_kopete_avdeviceconfig_la_SOURCES = avdeviceconfig.cpp \
+ avdeviceconfig_videoconfig.ui
+
+kcm_kopete_avdeviceconfig_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+
+kcm_kopete_avdeviceconfig_la_LIBADD = ../../../libkopete/libkopete.la \
+ ../../../libkopete/avdevice/libkopete_videodevice.la \
+ $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_avdeviceconfig.desktop
+servicedir = $(kde_servicesdir)
+
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
+# vim: set noet:
+noinst_HEADERS = avdeviceconfig_videoconfig.h
diff --git a/kopete/kopete/config/avdevice/avdeviceconfig.cpp b/kopete/kopete/config/avdevice/avdeviceconfig.cpp
new file mode 100644
index 00000000..a2c474e0
--- /dev/null
+++ b/kopete/kopete/config/avdevice/avdeviceconfig.cpp
@@ -0,0 +1,229 @@
+/*
+ avdeviceconfig.cpp - Kopete Video Device Configuration Panel
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "avdeviceconfig.h"
+#include "avdeviceconfig_videoconfig.h"
+#include "videodevice.h"
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qhbuttongroup.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qslider.h>
+
+#include <kplugininfo.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kgenericfactory.h>
+#include <ktrader.h>
+#include <kconfig.h>
+#include <kcombobox.h>
+#include <qimage.h>
+#include <qpixmap.h>
+
+#include <qtabwidget.h>
+
+//#include "videodevice.h"
+typedef KGenericFactory<AVDeviceConfig, QWidget> KopeteAVDeviceConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_avdeviceconfig, KopeteAVDeviceConfigFactory( "kcm_kopete_avdeviceconfig" ) )
+
+AVDeviceConfig::AVDeviceConfig(QWidget *parent, const char * name , const QStringList &args)
+ : KCModule( KopeteAVDeviceConfigFactory::instance(), parent, args )
+{
+ kdDebug() << "kopete:config (avdevice): KopeteAVDeviceConfigFactory::instance() called. " << endl;
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ mAVDeviceTabCtl = new QTabWidget(this, "mAVDeviceTabCtl");
+
+// "Video" TAB ============================================================
+ mPrfsVideoDevice = new AVDeviceConfig_VideoDevice(mAVDeviceTabCtl);
+ connect(mPrfsVideoDevice->mDeviceKComboBox, SIGNAL(activated(int)), this, SLOT(slotDeviceKComboBoxChanged(int)));
+ connect(mPrfsVideoDevice->mInputKComboBox, SIGNAL(activated(int)), this, SLOT(slotInputKComboBoxChanged(int)));
+ connect(mPrfsVideoDevice->mStandardKComboBox, SIGNAL(activated(int)), this, SLOT(slotStandardKComboBoxChanged(int)));
+ connect(mPrfsVideoDevice->mBrightnessSlider, SIGNAL(valueChanged(int)), this, SLOT(slotBrightnessSliderChanged(int)));
+ connect(mPrfsVideoDevice->mContrastSlider, SIGNAL(valueChanged(int)), this, SLOT(slotContrastSliderChanged(int)));
+ connect(mPrfsVideoDevice->mSaturationSlider, SIGNAL(valueChanged(int)), this, SLOT(slotSaturationSliderChanged(int)));
+ connect(mPrfsVideoDevice->mWhitenessSlider, SIGNAL(valueChanged(int)), this, SLOT(slotWhitenessSliderChanged(int)));
+ connect(mPrfsVideoDevice->mHueSlider, SIGNAL(valueChanged(int)), this, SLOT(slotHueSliderChanged(int)));
+ connect(mPrfsVideoDevice->mImageAutoBrightnessContrast, SIGNAL(toggled(bool)), this, SLOT(slotImageAutoBrightnessContrastChanged(bool)));
+ connect(mPrfsVideoDevice->mImageAutoColorCorrection, SIGNAL(toggled(bool)), this, SLOT(slotImageAutoColorCorrectionChanged(bool)));
+ connect(mPrfsVideoDevice->mImageAsMirror, SIGNAL(toggled(bool)), this, SLOT(slotImageAsMirrorChanged(bool)));
+
+ // why is this here?
+ // mPrfsVideoDevice->mVideoImageLabel->setPixmap(qpixmap);
+ mAVDeviceTabCtl->addTab(mPrfsVideoDevice, i18n("&Video"));
+ mVideoDevicePool = Kopete::AV::VideoDevicePool::self();
+ mVideoDevicePool->open();
+ mVideoDevicePool->setSize(320, 240);
+
+ mVideoDevicePool->fillDeviceKComboBox(mPrfsVideoDevice->mDeviceKComboBox);
+ mVideoDevicePool->fillInputKComboBox(mPrfsVideoDevice->mInputKComboBox);
+ mVideoDevicePool->fillStandardKComboBox(mPrfsVideoDevice->mStandardKComboBox);
+ setVideoInputParameters();
+
+ mVideoDevicePool->startCapturing();
+ mVideoDevicePool->getFrame();
+ mVideoDevicePool->getImage(&qimage);
+ if (qpixmap.convertFromImage(qimage,0) == true)
+ mPrfsVideoDevice->mVideoImageLabel->setPixmap(qpixmap);
+ connect(&qtimer, SIGNAL(timeout()), this, SLOT(slotUpdateImage()) );
+ qtimer.start(0,FALSE);
+}
+
+
+AVDeviceConfig::~AVDeviceConfig()
+{
+ mVideoDevicePool->close();
+}
+
+
+
+
+/*!
+ \fn VideoDeviceConfig::save()
+ */
+void AVDeviceConfig::save()
+{
+ /// @todo implement me
+ kdDebug() << "kopete:config (avdevice): save() called. " << endl;
+ mVideoDevicePool->saveConfig();
+}
+
+
+/*!
+ \fn VideoDeviceConfig::load()
+ */
+void AVDeviceConfig::load()
+{
+ /// @todo implement me
+}
+
+void AVDeviceConfig::slotSettingsChanged(bool){
+ emit changed(true);
+}
+
+void AVDeviceConfig::slotValueChanged(int){
+ emit changed( true );
+}
+
+void AVDeviceConfig::setVideoInputParameters()
+{
+ if(mVideoDevicePool->size())
+ {
+ mPrfsVideoDevice->mBrightnessSlider->setValue((int)(mVideoDevicePool->getBrightness()*65535));
+ mPrfsVideoDevice->mContrastSlider->setValue((int)(mVideoDevicePool->getContrast()*65535));
+ mPrfsVideoDevice->mSaturationSlider->setValue((int)(mVideoDevicePool->getSaturation()*65535));
+ mPrfsVideoDevice->mWhitenessSlider->setValue((int)(mVideoDevicePool->getWhiteness()*65535));
+ mPrfsVideoDevice->mHueSlider->setValue((int)(mVideoDevicePool->getHue()*65535));
+ mPrfsVideoDevice->mImageAutoBrightnessContrast->setChecked(mVideoDevicePool->getAutoBrightnessContrast());
+ mPrfsVideoDevice->mImageAutoColorCorrection->setChecked(mVideoDevicePool->getAutoColorCorrection());
+ mPrfsVideoDevice->mImageAsMirror->setChecked(mVideoDevicePool->getImageAsMirror());
+ }
+}
+
+void AVDeviceConfig::slotDeviceKComboBoxChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotDeviceKComboBoxChanged(int) called. " << endl;
+ unsigned int newdevice = mPrfsVideoDevice->mDeviceKComboBox->currentItem();
+ kdDebug() << "kopete:config (avdevice): slotDeviceKComboBoxChanged(int) Current device: " << mVideoDevicePool->currentDevice() << "New device: " << newdevice << endl;
+ if ((newdevice < mVideoDevicePool->m_videodevice.size())&&(newdevice!=mVideoDevicePool->currentDevice()))
+ {
+ kdDebug() << "kopete:config (avdevice): slotDeviceKComboBoxChanged(int) should change device. " << endl;
+ mVideoDevicePool->open(newdevice);
+ mVideoDevicePool->setSize(320, 240);
+ mVideoDevicePool->fillInputKComboBox(mPrfsVideoDevice->mInputKComboBox);
+ mVideoDevicePool->startCapturing();
+ setVideoInputParameters();
+ kdDebug() << "kopete:config (avdevice): slotDeviceKComboBoxChanged(int) called. " << endl;
+ emit changed( true );
+ }
+
+}
+
+void AVDeviceConfig::slotInputKComboBoxChanged(int){
+ unsigned int newinput = mPrfsVideoDevice->mInputKComboBox->currentItem();
+ if((newinput < mVideoDevicePool->inputs()) && ( newinput !=mVideoDevicePool->currentInput()))
+ {
+ mVideoDevicePool->selectInput(mPrfsVideoDevice->mInputKComboBox->currentItem());
+ mVideoDevicePool->fillStandardKComboBox(mPrfsVideoDevice->mStandardKComboBox);
+ setVideoInputParameters();
+ emit changed( true );
+ }
+}
+
+// ATTENTION: The 65535.0 value must be used instead of 65535 because the trailing ".0" converts the resulting value to floating point number.
+// Otherwise the resulting division operation would return 0 or 1 exclusively.
+
+void AVDeviceConfig::slotStandardKComboBoxChanged(int){
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotBrightnessSliderChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotBrightnessSliderChanged(int) called. " << mPrfsVideoDevice->mBrightnessSlider->value() / 65535.0 << endl;
+ mVideoDevicePool->setBrightness( mPrfsVideoDevice->mBrightnessSlider->value() / 65535.0 );
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotContrastSliderChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotContrastSliderChanged(int) called. " << mPrfsVideoDevice->mContrastSlider->value() / 65535.0 << endl;
+ mVideoDevicePool->setContrast( mPrfsVideoDevice->mContrastSlider->value() / 65535.0 );
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotSaturationSliderChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotSaturationSliderChanged(int) called. " << mPrfsVideoDevice->mSaturationSlider->value() / 65535.0 << endl;
+ mVideoDevicePool->setSaturation( mPrfsVideoDevice->mSaturationSlider->value() / 65535.0);
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotWhitenessSliderChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotWhitenessSliderChanged(int) called. " << mPrfsVideoDevice->mWhitenessSlider->value() / 65535.0 << endl;
+ mVideoDevicePool->setWhiteness( mPrfsVideoDevice->mWhitenessSlider->value() / 65535.0);
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotHueSliderChanged(int){
+ kdDebug() << "kopete:config (avdevice): slotHueSliderChanged(int) called. " << mPrfsVideoDevice->mHueSlider->value() / 65535.0 << endl;
+ mVideoDevicePool->setHue( mPrfsVideoDevice->mHueSlider->value() / 65535.0 );
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotImageAutoBrightnessContrastChanged(bool){
+ kdDebug() << "kopete:config (avdevice): slotImageAutoBrightnessContrastChanged(" << mPrfsVideoDevice->mImageAutoBrightnessContrast->isChecked() << ") called. " << endl;
+ mVideoDevicePool->setAutoBrightnessContrast(mPrfsVideoDevice->mImageAutoBrightnessContrast->isChecked());
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotImageAutoColorCorrectionChanged(bool){
+ kdDebug() << "kopete:config (avdevice): slotImageAutoColorCorrectionChanged(" << mPrfsVideoDevice->mImageAutoColorCorrection->isChecked() << ") called. " << endl;
+ mVideoDevicePool->setAutoColorCorrection(mPrfsVideoDevice->mImageAutoColorCorrection->isChecked());
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotImageAsMirrorChanged(bool){
+ kdDebug() << "kopete:config (avdevice): slotImageAsMirrorChanged(" << mPrfsVideoDevice->mImageAsMirror->isChecked() << ") called. " << endl;
+ mVideoDevicePool->setImageAsMirror(mPrfsVideoDevice->mImageAsMirror->isChecked());
+ emit changed( true );
+}
+
+void AVDeviceConfig::slotUpdateImage()
+{
+ mVideoDevicePool->getFrame();
+ mVideoDevicePool->getImage(&qimage);
+ bitBlt(mPrfsVideoDevice->mVideoImageLabel, 0, 0, &qimage, 0, Qt::CopyROP);
+// kdDebug() << "kopete (avdeviceconfig_videoconfig): Image updated." << endl;
+}
diff --git a/kopete/kopete/config/avdevice/avdeviceconfig.h b/kopete/kopete/config/avdevice/avdeviceconfig.h
new file mode 100644
index 00000000..d732b1a7
--- /dev/null
+++ b/kopete/kopete/config/avdevice/avdeviceconfig.h
@@ -0,0 +1,79 @@
+/*
+ avdeviceconfig.h - Kopete Video Device Configuration Panel
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef AVDEVICECONFIG_H
+#define AVDEVICECONFIG_H
+
+#include "kcmodule.h"
+#include "videodevicepool.h"
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+
+#ifdef HAVE_GL
+# include <qgl.h>
+#endif
+
+class QFrame;
+class QTabWidget;
+
+class AVDeviceConfig_VideoDevice;
+class AVDeviceConfig_AudioDevice;
+
+/**
+@author Cl�dio da Silveira Pinheiro
+*/
+class AVDeviceConfig : public KCModule
+{
+Q_OBJECT
+public:
+ AVDeviceConfig(QWidget *parent, const char * name , const QStringList &args);
+
+ ~AVDeviceConfig();
+ virtual void save();
+ virtual void load();
+
+private slots:
+ void slotSettingsChanged(bool);
+ void slotValueChanged(int);
+ void slotDeviceKComboBoxChanged(int);
+ void slotInputKComboBoxChanged(int);
+ void slotStandardKComboBoxChanged(int);
+ void slotBrightnessSliderChanged(int);
+ void slotContrastSliderChanged(int);
+ void slotSaturationSliderChanged(int);
+ void slotWhitenessSliderChanged(int);
+ void slotHueSliderChanged(int);
+ void slotImageAutoBrightnessContrastChanged(bool);
+ void slotImageAutoColorCorrectionChanged(bool);
+ void slotImageAsMirrorChanged(bool);
+ void slotUpdateImage();
+private:
+ QTabWidget* mAVDeviceTabCtl;
+ AVDeviceConfig_VideoDevice *mPrfsVideoDevice;
+ AVDeviceConfig_AudioDevice *mPrfsAudioDevice;
+ Kopete::AV::VideoDevicePool *mVideoDevicePool ;
+ QImage qimage;
+ QPixmap qpixmap;
+ QTimer qtimer;
+ void setVideoInputParameters();
+#ifdef HAVE_GL
+ QGLWidget m_video_gl;
+#endif
+};
+
+#endif
diff --git a/kopete/kopete/config/avdevice/avdeviceconfig_videoconfig.ui b/kopete/kopete/config/avdevice/avdeviceconfig_videoconfig.ui
new file mode 100644
index 00000000..90464a8b
--- /dev/null
+++ b/kopete/kopete/config/avdevice/avdeviceconfig_videoconfig.ui
@@ -0,0 +1,624 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AVDeviceConfig_VideoDevice</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AVDeviceConfig_VideoDevice</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>358</width>
+ <height>510</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Video</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="1" column="0">
+ <property name="name">
+ <cstring>VideoTabWidget</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Device</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Video Device Configuration</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>videodevice_selection_layout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>videodevice_selection_labels</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>deviceLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Device:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>inputLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Input:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>standardLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Standard:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>videodevice_selection_combos</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>mDeviceKComboBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>mInputKComboBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>mStandardKComboBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Con&amp;trols</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Image Adjustment</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <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>brightnessLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Brightness:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>contrastLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Contrast:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>saturationLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Saturation:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>whitenessLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Whiteness:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>hueLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Hue:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>mBrightnessSlider</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="lineStep">
+ <number>256</number>
+ </property>
+ <property name="pageStep">
+ <number>4096</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>mContrastSlider</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="lineStep">
+ <number>256</number>
+ </property>
+ <property name="pageStep">
+ <number>4096</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>mSaturationSlider</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="lineStep">
+ <number>256</number>
+ </property>
+ <property name="pageStep">
+ <number>4096</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>mWhitenessSlider</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="lineStep">
+ <number>256</number>
+ </property>
+ <property name="pageStep">
+ <number>4096</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>mHueSlider</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="lineStep">
+ <number>256</number>
+ </property>
+ <property name="pageStep">
+ <number>4096</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Optio&amp;ns</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>ImageOptions</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Image options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mImageAutoBrightnessContrast</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Au&amp;tomatic brightness/contrast adjustment</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mImageAutoColorCorrection</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Automatic color correction</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mImageAsMirror</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>See preview mirrored</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>imageLeftSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>mVideoImageLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>imageRightSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="4833">789c8597596f23470e80dfe75718c3b7c182e9eabb11ec834fc9873cbeaf601fc86ec9966df994cfc5fef794483633934d10c836fcb9582cde55fee5dbd2d9de68e9db2f5f9ee7349fb64bed153d2d7deb5e66b38fdffef3efff7ef99aa64b8baf2c5b4abffeebcb57dc5c6a9720499290640b8613e110ff22eb941b07657e7256f90be742e4a7c6a9ed076791c7a1732efbd784d3f817615a71167db0e55c0a17ce95c88f8ced3c7e74d6f306cea29fd159f583b3e8a75367d18f5bce8df0b63389be67e3ccfcd97716fd78e22cfaf1d459edefedcbedbc63e75af4cf85b33edeb8616cfef1b5b39eb7e72cf6c28b7166fb53678df7b5b3fadf388b3d503a8b3d40c6b9eaa35767d9cf37c6a5e53f73d6f3769d55bedf5f2789ac6bfcf2de1fda33ce4cdfbb716eeb6fce1acf1de3c2f2bbe7acf573605cdafe2b67f5efd359f20bb97165febe38b3c453fd2bfa7cd0a67165fea97c9db416ff1de3b1e9d77a6d12567bf8c159ed9d1ab7969fc2b8d378e2ba304579c93769bd719457fd524f2184cae221f51bd250a8fd3c35ae4cfec1b8b6fa06e3c6eaffc859d651f215b2d0c7efdab8327b969d459e9e8d7bfd57c68df587c43be4c1e20b685c1b9f0b17c1e283cfce927f96f911ca40d63fcbce7afebd31eb3a91b3fa37376ead9e64dea5759606a94f546eb23499883d9bc69932bef4acf22cf594725c1f8bfca9b3c8d3aa711e82c82bb73de3817111a4fe2171d67e1a1b97ba8e87ce5a8f12bfb48b2cfae8ceb85206894f9666135bff2e5c45d6fae894a3393a3f5be13aaeb7729eca4ff2d6ce3f5b701ef2b1c95f0aa7715dfb4de657519675d0fc8d8c9ba0f527f12faab234fd8fc695f1ab716dfa3f84ebb2089dec5f779678d381711d74beae398b7f540b3751bfd6e39ab1c9033b6bbd07e326687dcb3c2da8ecf47cfa301e9bbe37e389c94b3c0a2edba0f5f361dc194b3c8b36eed77acf9c453fab7f6d95587e6e8d83c957c25dc9668fdc5fc538b2c6e3a6e754e7adcc836252b6a9d69bccffb2ad535d67e98fb2ab538befaab3e8439947e5b84e82f6ffa17165f1bd7096fcc1ccb8b67a981b375a0f20f92d2775b078493dd54c75aaef0de9dfba75167fea2eb2f683dc5771388d753fbe396b7fca7c6b52ea2c5e57ce621fcbfdd26464f1812767ed8f63e3ced665de34c464fe9e1bb3d5b3f45bc3dca67a7fc9fba7697b46a9bf66cc13cb8fcc8b66c2960f3832b6f3988dad1e2828b799c53371d6f9766b9c5b7f4afe286983f5c3aeb3be177ace6c1ecc9cb51f6a678def8a7169f351fca710f5a93d9db3d64febacf9981aa76a2f5e3b6b7d5d1af7f6df3aebfb67e4acf93f72d6f972675cd83c7d77d67cac3bebfdfce9acf7eb87b3c66b665cdabc5feed9f44bfd51caade59395dbc4fc3f33cecc9f9b9e35bf786fdccfffc459e7ffc059eb7fe2acefcfd459df13dbceda5f4367bddffed8affdf0665c243a6f769c351f6367f51f7ad6fcefcf9dd57e72567fd959e3dd3a6bbc3b677daf34ce6affadb3dadbdb57263a5f46ce7adf8e9dd5dededfbe5e2f9cb5de5b678df7bb71a5fa59f767dccf77cd57d6f7136c185b7e79d359f3159cf53df7e8acf19e19f7f57ee5acef9b0d67f5b733b6fa844b63f38f07ceea9fcc3fca5b9bef58199b3dbce5acefa53567bd6fee8c73d54f95b3fa3b34b6fcc3b3b3f6dba1b3f6afe6a788fb6bad9f1f3f08f19b90b18ddff0f3dafefc2fe4bb284948f1b7f1e2e73fca4ff012af708ad77883b77f2f8f33bc8b9aeff1011ff1099f718e2ff88a6ff88e1ff8196da33fc92fe30aaee21aaee3060e70889bb885dbb88323dc8d7a407df941be8dd2dfa3ec5e94dac7033cc4233cc6133cc5333cc78bffb327c18029669863812556d1ef1a9ba8168080a1850ec608daaf30814bb882296ec035dc486426700b33b8837b78c0213cc2133ce367af3f7a93c01c5e700b5ee10d6fe11d093ee01396610556f104d6601d36a2d77abf0da2f41036610bb6b1841d18c12e7c873dd887033884233886133885b318297d3fc558e114cee1021208d1e01432c8a180122aa8e3c5190f2322c63bb507995aea62bc37698c9f34a14bba822d9ad235ddd02dcde80e87744f0f7fc823d1233de1363d634d737aa1577aa3ebf8047ba70ffaa4655aa1555aa375b37f0c298e6923ca0fb0a1212cd3266dd136edd08876e93bedd13e1dd0211d997e88f6031dd3099d624e67744e17f15f8b40296594cb273ef61632aa5ff34515d5d43032707c19449fd6e993db283b8217ee62fc0630fa31bf1cdf037c493bb1724ef98a162d30e56bcae185467cc3b731dfa0f935f919dff13daef1033ff2133fc77d039e73d41da55ff90d268b8afba17ea27d30a18edff9833f7999577895d7a88421aff31b6fc0e04ff5c6314acc031ef2266cc4c7ce036ff12ca66a10ff75d95eacfd2ccf3b0bfd38e651ac93f7d83b77f84e47d1e6f1222e0bf9c5ef3ff7a3f690766f4ffd0490aa85affffbf5cbef985d44a8</data>
+ </image>
+</images>
+<slots>
+ <slot>mHueSlider_valueChanged(int)</slot>
+ <slot>mHueSlider_sliderPressed()</slot>
+ <slot>mHueSlider_destroyed(QObject*)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/avdevice/cr128-app-kopete_avdevice.png b/kopete/kopete/config/avdevice/cr128-app-kopete_avdevice.png
new file mode 100644
index 00000000..a4e4fa58
--- /dev/null
+++ b/kopete/kopete/config/avdevice/cr128-app-kopete_avdevice.png
Binary files differ
diff --git a/kopete/kopete/config/avdevice/cr22-app-kopete_avdevice.png b/kopete/kopete/config/avdevice/cr22-app-kopete_avdevice.png
new file mode 100644
index 00000000..4b0eaa77
--- /dev/null
+++ b/kopete/kopete/config/avdevice/cr22-app-kopete_avdevice.png
Binary files differ
diff --git a/kopete/kopete/config/avdevice/cr32-app-kopete_avdevice.png b/kopete/kopete/config/avdevice/cr32-app-kopete_avdevice.png
new file mode 100644
index 00000000..df6476e9
--- /dev/null
+++ b/kopete/kopete/config/avdevice/cr32-app-kopete_avdevice.png
Binary files differ
diff --git a/kopete/kopete/config/avdevice/cr64-app-kopete_avdevice.png b/kopete/kopete/config/avdevice/cr64-app-kopete_avdevice.png
new file mode 100644
index 00000000..3d382289
--- /dev/null
+++ b/kopete/kopete/config/avdevice/cr64-app-kopete_avdevice.png
Binary files differ
diff --git a/kopete/kopete/config/avdevice/kopete_avdeviceconfig.desktop b/kopete/kopete/config/avdevice/kopete_avdeviceconfig.desktop
new file mode 100644
index 00000000..49fa3e94
--- /dev/null
+++ b/kopete/kopete/config/avdevice/kopete_avdeviceconfig.desktop
@@ -0,0 +1,118 @@
+[Desktop Entry]
+Icon=kopete_avdevice
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_avdeviceconfig
+X-KDE-FactoryName=KopeteAVDeviceConfigFactory
+X-KDE-ParentApp=kopete
+X-KDE-ParentComponents=kopete
+
+Name=Devices
+Name[be]=Прылады
+Name[bg]=УÑтройÑтва
+Name[bn]=ডিভাইস
+Name[br]=Trobarzhelloù
+Name[bs]=Uređaji
+Name[ca]=Dispositius
+Name[cs]=Zařízení
+Name[cy]=Dyfeisiau
+Name[da]=Enheder
+Name[de]=Geräte
+Name[el]=Συσκευές
+Name[eo]=Aparatoj
+Name[es]=Dispositivos
+Name[et]=Seadmed
+Name[eu]=Dispositiboak
+Name[fa]=دستگاهها
+Name[fi]=Laitteet
+Name[fr]=Périphériques
+Name[ga]=Gléasanna
+Name[gl]=Dispositivos
+Name[he]=התקני×
+Name[hu]=Eszközök
+Name[id]=Divais
+Name[is]=Tæki
+Name[it]=Periferiche
+Name[ja]=デãƒã‚¤ã‚¹
+Name[ka]=მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ები
+Name[kk]=Құрылығылар
+Name[km]=ឧបករណáŸ
+Name[lt]=Įrenginiai
+Name[lv]=IekÄrtas
+Name[nb]=Enheter
+Name[nds]=Reedschappen
+Name[ne]=यनà¥à¤¤à¥à¤°
+Name[nl]=Apparaten
+Name[nn]=Einingar
+Name[pa]=ਜੰਤਰ
+Name[pl]=UrzÄ…dzenia
+Name[pt]=Dispositivos
+Name[pt_BR]=Dispositivos
+Name[ru]=УÑтройÑтва
+Name[rw]=Apareye
+Name[sk]=Zariadenia
+Name[sl]=Naprave
+Name[sr]=Уређаји
+Name[sr@Latn]=Uređaji
+Name[sv]=Enheter
+Name[th]=อุปà¸à¸£à¸“์
+Name[tr]=Aygıtlar
+Name[uk]=ПриÑтрої
+Name[uz]=Uskunalar
+Name[uz@cyrillic]=УÑкуналар
+Name[ven]=Maano
+Name[wa]=Éndjins
+Name[xh]=Amacebo
+Name[zh_CN]=设备
+Name[zh_HK]=è£ç½®
+Name[zh_TW]=è£ç½®
+Name[zu]=Amacebo
+Comment=Here You Can Alter Kopete's Video And Audio Devices' Settings
+Comment[be]=ÐаÑтаўленні відÑа- Ñ– аўдыёпрыладаў Ð´Ð»Ñ Ð¿Ñ€Ð°Ñ†Ñ‹ з Kopete
+Comment[bg]=ÐаÑтройване на видео и аудио уÑтройÑтвата на програмата
+Comment[bn]=আপনি à¦à¦–ানে কপেটের ভিডিও à¦à¦¬à¦‚ অডিও ডিভাইসের মানসমূহ পরিবরà§à¦¤à¦¨ করতে পারেন
+Comment[bs]=Ovdje možete izmijeniti postavke Kopete-a za video i audio uređaje
+Comment[ca]=Aquí podreu modificar els paràmetres dels dispositius d'àudio i vídeo del Kopete
+Comment[cs]=Zde je možné přizpůsobit si nastavení audio a video zařízení
+Comment[da]=Her kan du ændre Kopete's video- og lydenheds opsætning
+Comment[de]=Hier können Sie Kopetes Einstellungen zu Audio- und Videogeräten verändern
+Comment[el]=Εδώ μποÏείτε να Ï„Ïοποποιήσετε τις Ïυθμίσεις των συσκευών βίντεο και ήχου
+Comment[es]=Aquí­ puede configurar los dispositivos de sonido y audio de Kopete
+Comment[et]=Siin saab muuta Kopete video- ja heliseadmete seadistusi
+Comment[eu]=Hemen Kopete-ren bideo eta audio ezarpenak ezar ditzakezu
+Comment[fa]=اینجا می‌توانید تنظیمات دستگاههای ویدیویی و صوتیKopete را تغییر دهید
+Comment[fi]=Täältä voit muuttaa Kopeten video- ja äänilaitteiden asetuksia
+Comment[fr]=Vous pouvez modifier ici la configuration des périphériques audio et vidéo de Kopete
+Comment[gl]=Aquí podes modificar as opcións dos dispositivos de video e audio de Kopete
+Comment[hu]=Itt lehet módosítani a Kopete video- és hangbeállításait
+Comment[is]=Hér getur þú breytt vídeó- og hljóðtæki stillingum Kopete
+Comment[it]=Qui puoi modificare le impostazioni audio e video di Kopete
+Comment[ja]=ã“ã“㧠Kopete ã® A/V デãƒã‚¤ã‚¹è¨­å®šã‚’変更ã—ã¾ã™
+Comment[ka]=áƒáƒ¥ თქვენ შეგიძლიáƒáƒ— Kopeteს ვიდერდრáƒáƒ£áƒ“ირმáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ების პáƒáƒ áƒáƒ›áƒ”ტრების გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=Мұнда Kopete-Ñ‚Ñ–Ò£ бейне мен Ð´Ñ‹Ð±Ñ‹Ñ Ò›Ò±Ñ€Ñ‹Ð»Ñ‹Ò“Ñ‹Ð»Ð°Ñ€Ð´Ñ‹Ò£ параметрлерін өзгерте алаÑыз
+Comment[km]=ទីនáŸáŸ‡ អ្នក​អាច​ប្ដូរ​ការ​កំណážáŸ‹â€‹áž“ៃ​ឧបករណáŸâ€‹áž¢áž¼ážŒáž¸áž™áŸ‰áž¼ និង​វីដáŸáž¢áž¼â€‹ážšáž”ស់ Kopete
+Comment[lt]=Čia galite keisti Kopete video ir audio įrenginių nustatymus
+Comment[nb]=Her kan du endre innstillinger for lyd og bilde i Kopete
+Comment[nds]=Hier kannst Du de Instellen för de Video- un Audio-Reedschappen vun Kopete ännern
+Comment[ne]=यहाठतपाईठकोपेटको भिडियो र अडियो यनà¥à¤¤à¥à¤° सेटिङ अलà¥à¤Ÿà¤° गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Hier kunt u de video- en audioapparaten voor Kopete instellen
+Comment[nn]=Her kan du endra på innstillingane for lyd og bilete i Kopete
+Comment[pl]=Tutaj można zmienić ustawienia urządzeń audio i wideo Kopete
+Comment[pt]=Aqui Você Pode Alterar a Configuração do Ãudio e Vídeo do Kopete
+Comment[pt_BR]=Aqui você pode alterar as configurações dos dispositivos de audio e vídeo do Kopete
+Comment[ru]=Параметры видео- и аудиоуÑтройÑтв Ð´Ð»Ñ Kopete
+Comment[sk]=Tu môžete upraviť nastavenia video a audio zariadení pre Kopete
+Comment[sl]=Tukaj lahko spreminjate nastavitve video in zvoÄnih naprav za Kopete
+Comment[sr]=Овде можете променити поÑтавке Kopete-ових аудио и видео уређаја
+Comment[sr@Latn]=Ovde možete promeniti postavke Kopete-ovih audio i video uređaja
+Comment[sv]=Här kan du ändra Kopetes video- och ljudenhetsinställningar
+Comment[tr]=Kopete'nin Video ve Ses Ayarlarını Buradan Değiştirebilirsiniz
+Comment[uk]=Тут можна змінити відео і аудіо параметри Kopete
+Comment[uz]=Bu yerda video va audio uskunalarini moslash mumkin
+Comment[uz@cyrillic]=Бу ерда видео ва аудио уÑкуналарини моÑлаш мумкин
+Comment[zh_CN]=您å¯åœ¨æ­¤æ›´æ”¹ Kopete 的影音设备设置
+Comment[zh_HK]=在此您å¯æ”¹å‹• Kopete 視åƒå’ŒéŸ³è¨Šè£ç½®çš„設定
+Comment[zh_TW]=您å¯ä»¥åœ¨æ­¤æ”¹è®Š Kopete çš„å½±åƒèˆ‡è²éŸ³è£ç½®è¨­å®š
+DocPath=kopete/configure-dialog.html#configuring-avdevice
diff --git a/kopete/kopete/config/behavior/Makefile.am b/kopete/kopete/config/behavior/Makefile.am
new file mode 100644
index 00000000..cc7a6196
--- /dev/null
+++ b/kopete/kopete/config/behavior/Makefile.am
@@ -0,0 +1,18 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private $(KOPETE_COMPAT_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_kopete_behaviorconfig.la
+
+kcm_kopete_behaviorconfig_la_SOURCES = \
+ kopeteawayconfigbase.ui \
+ behaviorconfig_chat.ui behaviorconfig_general.ui behaviorconfig_events.ui behaviorconfig.cpp
+
+kcm_kopete_behaviorconfig_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+
+kcm_kopete_behaviorconfig_la_LIBADD = ../../../libkopete/libkopete.la \
+ $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_behaviorconfig.desktop
+servicedir = $(kde_servicesdir)
+
+# vim: set noet:
diff --git a/kopete/kopete/config/behavior/behaviorconfig.cpp b/kopete/kopete/config/behavior/behaviorconfig.cpp
new file mode 100644
index 00000000..379e762a
--- /dev/null
+++ b/kopete/kopete/config/behavior/behaviorconfig.cpp
@@ -0,0 +1,305 @@
+/*
+ behaviorconfig.cpp - Kopete Look Feel Config
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "behaviorconfig.h"
+#include "behaviorconfig_general.h"
+#include "behaviorconfig_events.h"
+#include "behaviorconfig_chat.h"
+
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qhbuttongroup.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qradiobutton.h>
+
+#include <kdebug.h>
+#include <kplugininfo.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kgenericfactory.h>
+#include <ktrader.h>
+#include <kconfig.h>
+#include <klineedit.h>
+
+#include "kopeteprefs.h"
+#include "kopeteaway.h"
+#include "kopeteawayconfigbase.h"
+#include "kopetepluginmanager.h"
+#include "kopeteaway.h"
+
+#include <qtabwidget.h>
+
+typedef KGenericFactory<BehaviorConfig, QWidget> KopeteBehaviorConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_behaviorconfig, KopeteBehaviorConfigFactory( "kcm_kopete_behaviorconfig" ) )
+
+
+BehaviorConfig::BehaviorConfig(QWidget *parent, const char * /* name */, const QStringList &args) :
+ KCModule( KopeteBehaviorConfigFactory::instance(), parent, args )
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ mBehaviorTabCtl = new QTabWidget(this, "mBehaviorTabCtl");
+
+ // "General" TAB ============================================================
+ mPrfsGeneral = new BehaviorConfig_General(mBehaviorTabCtl);
+ mBehaviorTabCtl->addTab(mPrfsGeneral, i18n("&General"));
+
+ // "Events" TAB ============================================================
+ mPrfsEvents = new BehaviorConfig_Events(mBehaviorTabCtl);
+ mBehaviorTabCtl->addTab(mPrfsEvents, i18n("&Events"));
+
+ // "Away" TAB ===============================================================
+ mAwayConfigUI = new KopeteAwayConfigBaseUI(mBehaviorTabCtl);
+ mBehaviorTabCtl->addTab(mAwayConfigUI, i18n("A&way Settings"));
+
+ // "Chat" TAB ===============================================================
+ mPrfsChat = new BehaviorConfig_Chat(mBehaviorTabCtl);
+ mBehaviorTabCtl->addTab(mPrfsChat, i18n("Cha&t"));
+
+ Kopete::PluginManager *pluginManager = Kopete::PluginManager::self();
+ viewPlugins = pluginManager->availablePlugins("Views");
+
+ load();
+
+
+ // "General" TAB ============================================================
+ connect(mPrfsGeneral->mShowTrayChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsGeneral->mStartDockedChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsGeneral->mUseQueueChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsGeneral->mUseStackChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsGeneral->mQueueUnreadMessagesChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsGeneral->mAutoConnect, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+
+ // "Events" TAB ============================================================
+ connect(mPrfsEvents->mQueueOnlyHighlightedMessagesInGroupChatsChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mQueueOnlyMessagesOnAnotherDesktopChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mBalloonNotifyChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mBalloonNotifyIgnoreClosesChatViewChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mCloseBalloonChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mBalloonCloseDelay, SIGNAL(valueChanged(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect(mPrfsEvents->mTrayflashNotifyChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mTrayflashNotifyLeftClickOpensMessageChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mTrayflashNotifySetCurrentDesktopToChatViewChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mSoundIfAwayChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mEventIfActive, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect(mPrfsEvents->mRaiseMsgWindowChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+
+
+ // "Chat" TAB ===============================================================
+ connect( mPrfsChat->cb_ShowEventsChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mPrfsChat->highlightEnabled, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mPrfsChat->cb_SpellCheckChk, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mPrfsChat->cmbChatGroupingPolicy, SIGNAL(activated(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mPrfsChat->mChatViewBufferSize, SIGNAL(valueChanged(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mPrfsChat->truncateContactNameEnabled, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mPrfsChat->mMaxContactNameLength, SIGNAL(valueChanged(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mPrfsChat->viewPlugin, SIGNAL(activated(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mPrfsChat->viewPlugin, SIGNAL(activated(int)),
+ this, SLOT(slotUpdatePluginLabel(int)));
+
+ // "Away" TAB ===============================================================
+ connect( mAwayConfigUI->rememberedMessages, SIGNAL(valueChanged(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mAwayConfigUI->mAutoAwayTimeout, SIGNAL(valueChanged(int)),
+ this, SLOT(slotValueChanged(int)));
+ connect( mAwayConfigUI->mGoAvailable, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mAwayConfigUI->mUseAutoAway, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mAwayConfigUI->mDisplayLastAwayMessage, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mAwayConfigUI->mDisplayCustomAwayMessage, SIGNAL(toggled(bool)),
+ this, SLOT(slotSettingsChanged(bool)));
+ connect( mAwayConfigUI->mAutoAwayMessageEdit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotTextChanged(const QString&)));
+}
+
+void BehaviorConfig::save()
+{
+// kdDebug(14000) << k_funcinfo << "called." << endl;
+
+ KopetePrefs *p = KopetePrefs::prefs();
+ KConfig *config = KGlobal::config();
+
+ // "General" TAB ============================================================
+ p->setShowTray(mPrfsGeneral->mShowTrayChk->isChecked());
+ p->setStartDocked(mPrfsGeneral->mStartDockedChk->isChecked());
+ p->setUseQueue(mPrfsGeneral->mUseQueueChk->isChecked());
+ p->setUseStack(mPrfsGeneral->mUseStackChk->isChecked());
+ p->setQueueUnreadMessages(mPrfsGeneral->mQueueUnreadMessagesChk->isChecked());
+ p->setAutoConnect(mPrfsGeneral->mAutoConnect->isChecked());
+
+ // "Events" TAB ============================================================
+ p->setQueueOnlyHighlightedMessagesInGroupChats(mPrfsEvents->mQueueOnlyHighlightedMessagesInGroupChatsChk->isChecked());
+ p->setQueueOnlyMessagesOnAnotherDesktop(mPrfsEvents->mQueueOnlyMessagesOnAnotherDesktopChk->isChecked());
+ p->setBalloonNotify(mPrfsEvents->mBalloonNotifyChk->isChecked());
+ p->setBalloonNotifyIgnoreClosesChatView(mPrfsEvents->mBalloonNotifyIgnoreClosesChatViewChk->isChecked());
+ p->setBalloonClose(mPrfsEvents->mCloseBalloonChk->isChecked());
+ p->setBalloonDelay(mPrfsEvents->mBalloonCloseDelay->value());
+ p->setTrayflashNotify(mPrfsEvents->mTrayflashNotifyChk->isChecked());
+ p->setTrayflashNotifyLeftClickOpensMessage(mPrfsEvents->mTrayflashNotifyLeftClickOpensMessageChk->isChecked());
+ p->setTrayflashNotifySetCurrentDesktopToChatView(mPrfsEvents->mTrayflashNotifySetCurrentDesktopToChatViewChk->isChecked());
+ p->setSoundIfAway(mPrfsEvents->mSoundIfAwayChk->isChecked());
+ p->setRaiseMsgWindow(mPrfsEvents->mRaiseMsgWindowChk->isChecked());
+ config->setGroup("General");
+ config->writeEntry("EventIfActive", mPrfsEvents->mEventIfActive->isChecked());
+
+ // "Away" TAB ===============================================================
+ p->setRememberedMessages( mAwayConfigUI->rememberedMessages->value() );
+
+ config->setGroup("AutoAway");
+ config->writeEntry("Timeout", mAwayConfigUI->mAutoAwayTimeout->value() * 60);
+ config->writeEntry("GoAvailable", mAwayConfigUI->mGoAvailable->isChecked());
+ config->writeEntry("UseAutoAway", mAwayConfigUI->mUseAutoAway->isChecked() );
+ config->writeEntry("UseAutoAwayMessage", mAwayConfigUI->mDisplayCustomAwayMessage->isChecked() );
+ config->sync();
+
+ // Save the auto away message, if defined
+ if( mAwayConfigUI->mDisplayCustomAwayMessage->isChecked() )
+ {
+ awayInstance->setAutoAwayMessage( mAwayConfigUI->mAutoAwayMessageEdit->text() );
+ }
+
+ // "Chat" TAB ===============================================================
+ p->setShowEvents(mPrfsChat->cb_ShowEventsChk->isChecked());
+ p->setHighlightEnabled(mPrfsChat->highlightEnabled->isChecked());
+ p->setSpellCheck(mPrfsChat->cb_SpellCheckChk->isChecked());
+ p->setInterfacePreference( viewPlugins[mPrfsChat->viewPlugin->currentItem()]->pluginName() );
+ p->setChatWindowPolicy(mPrfsChat->cmbChatGroupingPolicy->currentItem());
+
+ p->setChatViewBufferSize(mPrfsChat->mChatViewBufferSize->value());
+ p->setTruncateContactNames(mPrfsChat->truncateContactNameEnabled->isChecked());
+ p->setMaxContactNameLength(mPrfsChat->mMaxContactNameLength->value());
+
+ p->save();
+ emit changed(false);
+}
+
+void BehaviorConfig::load()
+{
+// kdDebug(14000) << k_funcinfo << "called" << endl;
+ KopetePrefs *p = KopetePrefs::prefs();
+ KConfig *config = KGlobal::config();
+ awayInstance = Kopete::Away::getInstance();
+
+ // "General" TAB ============================================================
+ mPrfsGeneral->mShowTrayChk->setChecked( p->showTray() );
+ mPrfsGeneral->mStartDockedChk->setChecked( p->startDocked() );
+ mPrfsGeneral->mInstantMessageOpeningChk->setChecked( !p->useQueue() && !p->useStack());
+ mPrfsGeneral->mUseQueueChk->setChecked( p->useQueue() );
+ mPrfsGeneral->mUseStackChk->setChecked( p->useStack() );
+ mPrfsGeneral->mQueueUnreadMessagesChk->setChecked ( p->queueUnreadMessages() );
+ mPrfsGeneral->mAutoConnect->setChecked( p->autoConnect() );
+
+ // "Events" TAB ============================================================
+ mPrfsEvents->mQueueOnlyHighlightedMessagesInGroupChatsChk->setChecked ( p->queueOnlyHighlightedMessagesInGroupChats() );
+ mPrfsEvents->mQueueOnlyMessagesOnAnotherDesktopChk->setChecked ( p->queueOnlyMessagesOnAnotherDesktop() );
+ mPrfsEvents->mBalloonNotifyChk->setChecked ( p->balloonNotify() );
+ mPrfsEvents->mBalloonNotifyIgnoreClosesChatViewChk->setChecked ( p->balloonNotifyIgnoreClosesChatView() );
+ mPrfsEvents->mCloseBalloonChk->setChecked( p->balloonClose() );
+ mPrfsEvents->mBalloonCloseDelay->setValue( p->balloonCloseDelay() );
+ mPrfsEvents->mTrayflashNotifyChk->setChecked ( p->trayflashNotify() );
+ mPrfsEvents->mTrayflashNotifyLeftClickOpensMessageChk->setChecked ( p->trayflashNotifyLeftClickOpensMessage() );
+ mPrfsEvents->mTrayflashNotifySetCurrentDesktopToChatViewChk->setChecked ( p->trayflashNotifySetCurrentDesktopToChatView() );
+ mPrfsEvents->mSoundIfAwayChk->setChecked( p->soundIfAway() );
+ mPrfsEvents->mRaiseMsgWindowChk->setChecked(p->raiseMsgWindow());
+ config->setGroup("General");
+ mPrfsEvents->mEventIfActive->setChecked(config->readBoolEntry("EventIfActive", true));
+
+ // "Away" TAB ===============================================================
+ config->setGroup("AutoAway");
+ mAwayConfigUI->mAutoAwayTimeout->setValue(config->readNumEntry("Timeout", 600)/60);
+ mAwayConfigUI->mGoAvailable->setChecked(config->readBoolEntry("GoAvailable", true));
+ mAwayConfigUI->mUseAutoAway->setChecked(config->readBoolEntry("UseAutoAway", true));
+ mAwayConfigUI->rememberedMessages->setValue( p->rememberedMessages() );
+ mAwayConfigUI->mAutoAwayMessageEdit->setText( awayInstance->autoAwayMessage() );
+
+ // Always display the last away message by default
+ mAwayConfigUI->mDisplayCustomAwayMessage->setChecked(config->readBoolEntry("UseAutoAwayMessage", false));
+
+ // "Chat" TAB ===============================================================
+ mPrfsChat->cb_ShowEventsChk->setChecked(p->showEvents());
+ mPrfsChat->highlightEnabled->setChecked(p->highlightEnabled());
+ mPrfsChat->cb_SpellCheckChk->setChecked(p->spellCheck());
+ mPrfsChat->cmbChatGroupingPolicy->setCurrentItem(p->chatWindowPolicy());
+
+ mPrfsChat->mChatViewBufferSize->setValue(p->chatViewBufferSize());
+ mPrfsChat->truncateContactNameEnabled->setChecked(p->truncateContactNames());
+ mPrfsChat->mMaxContactNameLength->setValue(p->maxConactNameLength());
+
+
+ mPrfsChat->viewPlugin->clear();
+ int selectedIdx = 0, i = 0;
+ for( QValueList<KPluginInfo*>::iterator it = viewPlugins.begin(); it != viewPlugins.end(); ++it )
+ {
+ if( (*it)->pluginName() == p->interfacePreference() )
+ selectedIdx = i;
+ mPrfsChat->viewPlugin->insertItem( (*it)->name(), i++ );
+ }
+
+ mPrfsChat->viewPlugin->setCurrentItem(selectedIdx);
+ slotUpdatePluginLabel(selectedIdx);
+}
+
+void BehaviorConfig::slotUpdatePluginLabel(int)
+{
+ mPrfsChat->viewPluginLabel->setText( viewPlugins[ mPrfsChat->viewPlugin->currentItem() ]->comment() );
+}
+
+void BehaviorConfig::slotSettingsChanged(bool)
+{
+ emit changed(true);
+}
+
+void BehaviorConfig::slotValueChanged(int)
+{
+ emit changed( true );
+}
+
+void BehaviorConfig::slotTextChanged(const QString&)
+{
+ emit changed( true );
+}
+
+#include "behaviorconfig.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/behavior/behaviorconfig.h b/kopete/kopete/config/behavior/behaviorconfig.h
new file mode 100644
index 00000000..5a981784
--- /dev/null
+++ b/kopete/kopete/config/behavior/behaviorconfig.h
@@ -0,0 +1,61 @@
+/*
+ behaviorconfig.h - Kopete Look Feel Config
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __BEHAVIOR_H
+#define __BEHAVIOR_H
+
+#include "kcmodule.h"
+
+namespace Kopete
+{
+class Away;
+}
+
+class QFrame;
+class QTabWidget;
+
+class BehaviorConfig_General;
+class BehaviorConfig_Events;
+class BehaviorConfig_Chat;
+class KopeteAwayConfigBaseUI;
+class KPluginInfo;
+
+class BehaviorConfig : public KCModule
+{
+ Q_OBJECT
+
+ public:
+ BehaviorConfig(QWidget *parent, const char * name , const QStringList &args) ;
+
+ virtual void save();
+ virtual void load();
+
+ private slots:
+ void slotSettingsChanged(bool);
+ void slotValueChanged(int);
+ void slotUpdatePluginLabel(int);
+ void slotTextChanged(const QString&);
+
+ private:
+ QTabWidget* mBehaviorTabCtl;
+ BehaviorConfig_General *mPrfsGeneral;
+ BehaviorConfig_Events *mPrfsEvents;
+ BehaviorConfig_Chat *mPrfsChat;
+ KopeteAwayConfigBaseUI *mAwayConfigUI;
+ QValueList<KPluginInfo*> viewPlugins;
+ Kopete::Away* awayInstance;
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/behavior/behaviorconfig_chat.ui b/kopete/kopete/config/behavior/behaviorconfig_chat.ui
new file mode 100644
index 00000000..26bfae46
--- /dev/null
+++ b/kopete/kopete/config/behavior/behaviorconfig_chat.ui
@@ -0,0 +1,308 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>BehaviorConfig_Chat</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>BehaviorConfig_Chat</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>564</width>
+ <height>537</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Chat</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="4" column="0">
+ <property name="name">
+ <cstring>interfaceGroup</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Interface Preference</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>viewPlugin</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>viewPluginLabel</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>viewPlugin</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer row="8" column="0">
+ <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>240</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup" row="5" column="0">
+ <property name="name">
+ <cstring>chatWindowGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Chat Window Grouping &amp;Policy</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="7" column="0">
+ <item>
+ <property name="text">
+ <string>Open All Messages in New Chat Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Group Messages From Same Account in Same Chat Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Group All Messages in Same Chat Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Group Messages From Contacts in Same Group in Same Chat Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Group Messages From Same Metacontact in Same Chat Window</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmbChatGroupingPolicy</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;dl&gt;
+ &lt;dt&gt;&lt;tt&gt;Open all messages in a new chat window&lt;/tt&gt;
+ &lt;dd&gt;Every chat will have its own window.
+ &lt;dt&gt;&lt;tt&gt;Group messages from the same account in the same chat window&lt;/tt&gt;
+ &lt;dd&gt;All chats for one account get grouped in to one window by using tabs.
+ &lt;dt&gt;&lt;tt&gt;Group all messages in the same chat window&lt;/tt&gt;
+ &lt;dd&gt;All chats get grouped in to one window by using tabs.
+ &lt;dt&gt;&lt;tt&gt;Group messages from contacts in the same group in the same chat window&lt;/tt&gt;
+ &lt;dd&gt;All chats from one group get grouped in to one window by using tabs.
+ &lt;dt&gt;&lt;tt&gt;Group messages from the same metacontact in the same chat window&lt;/tt&gt;
+ &lt;dd&gt;All chats from one metacontact get grouped in to one window by using tabs.
+ &lt;/dl&gt;
+ </string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>highlightEnabled</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>High&amp;light messages containing your nickname</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>cb_SpellCheckChk</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>E&amp;nable automatic spell checking</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>cb_ShowEventsChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show events in chat window</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="6" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>truncateContactNameEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>T&amp;runcate contact name with more characters than:</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>120</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mMaxContactNameLength</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minValue">
+ <number>5</number>
+ </property>
+ <property name="value">
+ <number>20</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="7" column="0">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>txtChatViewBufferSize</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Maximum number of chat window lines:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mChatViewBufferSize</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Limit the maximum number of lines visible in a chat window to improve speed for complex layouts.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>212</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mChatViewBufferSize</cstring>
+ </property>
+ <property name="maxValue">
+ <number>9000</number>
+ </property>
+ <property name="minValue">
+ <number>2</number>
+ </property>
+ <property name="value">
+ <number>250</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>truncateContactNameEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mMaxContactNameLength</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>cb_ShowEventsChk</tabstop>
+ <tabstop>highlightEnabled</tabstop>
+ <tabstop>cb_SpellCheckChk</tabstop>
+ <tabstop>viewPlugin</tabstop>
+ <tabstop>cmbChatGroupingPolicy</tabstop>
+ <tabstop>truncateContactNameEnabled</tabstop>
+ <tabstop>mMaxContactNameLength</tabstop>
+ <tabstop>mChatViewBufferSize</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/config/behavior/behaviorconfig_events.ui b/kopete/kopete/config/behavior/behaviorconfig_events.ui
new file mode 100644
index 00000000..ea2d1ea3
--- /dev/null
+++ b/kopete/kopete/config/behavior/behaviorconfig_events.ui
@@ -0,0 +1,388 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>BehaviorConfig_Events</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>BehaviorConfig_Events</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>427</width>
+ <height>386</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Events</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>notifyGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Tray Flash &amp;&amp; Bubble</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mTrayflashNotifyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Flash s&amp;ystem tray</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Flash the system tray icon on an incoming message</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Flash the system tray icon whenever a message comes in.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mTrayflashNotifyLeftClickOpensMessageChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Left mouse click opens message</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Left mouse click on flashing system tray opens message instead of restoring/minimizing contact list</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A left mouse click on the flashing system tray icon opens the incoming message instead of restoring/minimizing the contact list (e.g. to check who is sending messages). A middle click always opens this message.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mBalloonNotifyChk</cstring>
+ </property>
+ <property name="text">
+ <string>Sho&amp;w bubble</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show a bubble on an incoming message</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Show a bubble whenever a message comes in.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mBalloonNotifyIgnoreClosesChatViewChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Button "&amp;Ignore" closes chat</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The "Ignore" button of the bubble closes the chat window for the sender</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If there is already a chat window opened for the sender of the message displayed in the bubble the "Ignore" button will close this chat window.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mCloseBalloonChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Close &amp;bubble automatically after</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Automatically close bubble after fixed amount of time</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Bubbles will automatically be closed after a fixed amount of time. A closed one will be replaced by a new one if another message is waiting.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mBalloonCloseDelay</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="suffix">
+ <string> Sec</string>
+ </property>
+ <property name="maxValue">
+ <number>120</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>30</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mQueueOnlyHighlightedMessagesInGroupChatsChk</cstring>
+ </property>
+ <property name="text">
+ <string>Exclude non-highlighted messages in grou&amp;p chats</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Notify only highlighted messages in group chats</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>In very active group chats important messages can be singled out by excluding non-highlighted messages from notification.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mQueueOnlyMessagesOnAnotherDesktopChk</cstring>
+ </property>
+ <property name="text">
+ <string>Exclude messages in chats on current des&amp;ktop</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Do not display notification for messages in chat windows on current desktop</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option allows you to turn off the notification of events for chat windows that are on the current desktop. If this option is turned on, then only chat windows on different desktops than the current one will notify you that an event has occured. Otherwise, all chat windows will notify you that an event has occured.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mSoundIfAwayChk</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;nable events while away</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enable events if your account status is "Away"</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable notification events even if your account status is "Away" or less available, e.g. "Not Available" or "Do not Disturb". Note: This does not affect the flashing of the system tray icon.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mEventIfActive</cstring>
+ </property>
+ <property name="text">
+ <string>Enable events for acti&amp;ve chat windows</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enable events for incoming messages if the chat window is active</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable notification events for incoming messages even if the receiving chat window is active. Note: Neither the system tray icon flashes nor the bubble is shown.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mTrayflashNotifySetCurrentDesktopToChatViewChk</cstring>
+ </property>
+ <property name="text">
+ <string>Switch &amp;to desktop containing chat on opening message</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Switch to the desktop which contains the chat window for the sender when opening his/her message</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If there is already a chat window open for the sender of the message, opening his/her message will cause a switch to the desktop which contains this chat window.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mRaiseMsgWindowChk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Raise window on incoming message</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Raise the chat window/tab on an incoming message</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If there is already a chat window opened for the sender of an incoming message this window will be put on the current desktop and in front of all other windows.</string>
+ </property>
+ </widget>
+ </vbox>
+ </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>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>mTrayflashNotifyChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mTrayflashNotifyLeftClickOpensMessageChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mBalloonNotifyChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mBalloonNotifyIgnoreClosesChatViewChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mBalloonNotifyChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mCloseBalloonChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mBalloonNotifyChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mBalloonCloseDelay</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>mTrayflashNotifyChk</tabstop>
+ <tabstop>mBalloonNotifyChk</tabstop>
+ <tabstop>mQueueOnlyHighlightedMessagesInGroupChatsChk</tabstop>
+ <tabstop>mQueueOnlyMessagesOnAnotherDesktopChk</tabstop>
+ <tabstop>mSoundIfAwayChk</tabstop>
+ <tabstop>mEventIfActive</tabstop>
+ <tabstop>mTrayflashNotifySetCurrentDesktopToChatViewChk</tabstop>
+ <tabstop>mRaiseMsgWindowChk</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/config/behavior/behaviorconfig_general.ui b/kopete/kopete/config/behavior/behaviorconfig_general.ui
new file mode 100644
index 00000000..d15815ad
--- /dev/null
+++ b/kopete/kopete/config/behavior/behaviorconfig_general.ui
@@ -0,0 +1,211 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>BehaviorConfig_General</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>BehaviorConfig_General</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>348</width>
+ <height>302</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>General</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>System Tray</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mShowTrayChk</cstring>
+ </property>
+ <property name="text">
+ <string>Show system tray &amp;icon</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show the icon in the system tray</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>By default, the system tray icon indicates new incoming messages by flashing and showing a bubble. A left or middle mouse click on the icon will open the message in a new chat window. Pressing the "View" button in the bubble has the same effect.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mStartDockedChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Start with hidden &amp;main window</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Start with the main window minimized to the system tray</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Start with the main window hidden. The only visible item is the system tray icon.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Message Handling</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mInstantMessageOpeningChk</cstring>
+ </property>
+ <property name="text">
+ <string>Open messages instantl&amp;y</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Instantly open incoming messages</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If there is no existing chat window a new window will be opened when a new message comes in. If there is already a chat window opened for the sender of the message it will be displayed there instantly.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mUseQueueChk</cstring>
+ </property>
+ <property name="text">
+ <string>Use message &amp;queue</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use a message queue to store incoming messages</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Store new incoming messages in a message queue. New messages are messages that cannot be displayed in an already open chat window. Only queued or stacked messages trigger notification via bubble, a flashing tray icon, or both..</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mUseStackChk</cstring>
+ </property>
+ <property name="text">
+ <string>Use message stac&amp;k</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use a message stack to store incoming messages</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Store new incoming messages in a message stack. New messages are messages that cannot be displayed in an already open chat window. Only queued or stacked messages trigger notification via bubble and flashing tray.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mQueueUnreadMessagesChk</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Queue/stack &amp;unread messages</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Also add unread messages to queue/stack</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Unread messages are messages that will be displayed in an already opened but inactive chat window. Only incoming queued messages trigger notification via the bubble, the flashing tray icon, or both. With this option disabled only new incoming messages are queued, i.e. messages that cannot be displayed in an already open chat window.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Miscellaneous</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mAutoConnect</cstring>
+ </property>
+ <property name="text">
+ <string>Connect automatically at &amp;startup</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Connect all your accounts automatically when starting Kopete</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When starting Kopete all your accounts will be connected automatically. Note: You can exclude accounts individually in their properties.</string>
+ </property>
+ </widget>
+ </vbox>
+ </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>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>mShowTrayChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mStartDockedChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseQueueChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mQueueUnreadMessagesChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseStackChk</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mQueueUnreadMessagesChk</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>mShowTrayChk</tabstop>
+ <tabstop>mStartDockedChk</tabstop>
+ <tabstop>mUseQueueChk</tabstop>
+ <tabstop>mAutoConnect</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/config/behavior/kopete_behaviorconfig.desktop b/kopete/kopete/config/behavior/kopete_behaviorconfig.desktop
new file mode 100644
index 00000000..a9531a60
--- /dev/null
+++ b/kopete/kopete/config/behavior/kopete_behaviorconfig.desktop
@@ -0,0 +1,135 @@
+[Desktop Entry]
+Icon=configure
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_behaviorconfig
+X-KDE-FactoryName=KopeteBehaviorConfigFactory
+X-KDE-ParentApp=kopete
+X-KDE-ParentComponents=kopete
+
+Name=Behavior
+Name[ar]=السلوك
+Name[be]=Паводзіны
+Name[bg]=Поведение
+Name[bn]=আচরণ
+Name[br]=Emzalc'h
+Name[bs]=Ponašanje
+Name[ca]=Comportament
+Name[cs]=Chování
+Name[cy]=Ymddygiad
+Name[da]=Opførsel
+Name[de]=Verhalten
+Name[el]=ΣυμπεÏιφοÏά
+Name[en_GB]=Behaviour
+Name[eo]=Fenestrokonduto
+Name[es]=Comportamiento
+Name[et]=Käitumine
+Name[eu]=Portaera
+Name[fa]=رÙتار
+Name[fi]=Käytös
+Name[fr]=Comportement
+Name[ga]=Oibriú
+Name[gl]=Comportamento
+Name[he]=×ופן פעולה
+Name[hi]=बरà¥à¤¤à¤¾à¤µ
+Name[hr]=Ponašanje
+Name[hu]=Működés
+Name[is]=Hegðun
+Name[it]=Comportamento
+Name[ja]=挙動
+Name[ka]=ქცევáƒ
+Name[kk]=ҚаÑиеттер
+Name[km]=ឥរិយាបáž
+Name[lt]=Elgsena
+Name[mk]=ОднеÑување
+Name[nb]=Oppførsel
+Name[nds]=Bedregen
+Name[ne]=वà¥à¤¯à¤µà¤¹à¤¾à¤°
+Name[nl]=Gedrag
+Name[nn]=Ã…tferd
+Name[pa]=ਰਵੱਈਆ
+Name[pl]=Zachowanie
+Name[pt]=Comportamento
+Name[pt_BR]=Comportamento
+Name[ro]=Comportament
+Name[ru]=Поведение
+Name[rw]=Imyitwarire
+Name[se]=Láhtten
+Name[sk]=Chovanie
+Name[sl]=Obnašanje
+Name[sr]=Понашање
+Name[sr@Latn]=Ponašanje
+Name[sv]=Beteende
+Name[tg]=Рафтор
+Name[tr]=Davranış
+Name[uk]=Поведінка
+Name[uz]=Xususiyatlar
+Name[uz@cyrillic]=ХуÑуÑиÑтлар
+Name[wa]=Dujhance
+Name[zh_CN]=行为
+Name[zh_HK]=行為
+Name[zh_TW]=行為
+Comment=Here You Can Personalize Kopete
+Comment[ar]=هنا يمكنك تخصيص Kopete
+Comment[be]=ПерÑÐ°Ð½Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ‹Ñ Kopete
+Comment[bg]=ÐаÑтройване поведението на програмата
+Comment[bn]=আপনি à¦à¦–ানে কপেট বà§à¦¯à¦•à§à¦¤à¦¿à¦—তকরণ করতে পারেন
+Comment[bs]=Ovdje možete prilagoditi sebi Kopete
+Comment[ca]=Aquí podreu personalitzar el Kopete
+Comment[cs]=Zde je možné přizpůsobit si Kopete
+Comment[cy]=Yma Gallwch Bersonoleiddio Kopete
+Comment[da]=Her kan du personliggøre Kopete
+Comment[de]=Hier können Sie Kopete an Ihre persönlichen Vorstellungen anpassen
+Comment[el]=Εδώ μποÏείτε να Ï€ÏοσαÏμόσετε το Kopete
+Comment[en_GB]=Here You Can Personalise Kopete
+Comment[es]=Aquí­ puede personalizar Kopete
+Comment[et]=Siin saab muuta Kopete just selliseks, nagu sulle meeldib
+Comment[eu]=Hemen Kopete pertsonalizatu dezakezu
+Comment[fa]=اینجا می‌توانید Kopete را شخصی کنید
+Comment[fi]=Täällä voit yksilöllistää Kopetea
+Comment[fr]=Vous pouvez personnaliser Kopete ici
+Comment[ga]=Tig Leat Kopete a Oiriúnú Duit Féin Anseo
+Comment[gl]=Aquí podes personaliza-lo comportamento de Kopete
+Comment[he]=×›×ן ניתן להת××™× ×ישית ×ת Kopete
+Comment[hi]=यहाठआप के-ऑपà¥à¤Ÿà¥€ को परà¥à¤¸à¤¨à¤²à¤¾à¤‡à¤œ कर सकते हैं
+Comment[hr]=Ovde možete prilagoditi Kopete
+Comment[hu]=Itt lehet megváltoztatni a Kopete működési jellemzőit
+Comment[is]=Hér geturðu breytt Kopete
+Comment[it]=Qui puoi personalizzare Kopete
+Comment[ja]=ã“ã“㧠Kopete ã®æŒ™å‹•ã‚’カスタマイズã—ã¾ã™
+Comment[ka]=თქვენ áƒáƒ¥ შეგიძლიáƒáƒ— Kopete-ს მáƒáƒ áƒ’ებáƒ
+Comment[kk]=Мұнда Kopete-ті ыңғайлап алуға болады
+Comment[km]=ទីនáŸáŸ‡ អ្នក​អាច​ážáž˜áŸ’រូវ Kopete ážáž¶áž˜â€‹áž”ុគ្គល
+Comment[lt]=ÄŒia galite Kopete pritaikyti sau
+Comment[mk]=Тука можете да го прилагодите Kopete на вашите желби
+Comment[nb]=Sett dine egne innstillinger i Kopete
+Comment[nds]=Hier kannst Du Kopete Dien Wennsten topassen
+Comment[ne]=तपाईठयहाठकोपेट निजीकरण गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Hier kunt u Kopete naar wens instellen
+Comment[nn]=Vel dine eigne innstillingar i Kopete
+Comment[pl]=Tutaj możesz dostosować Kopete do osobistych preferencji
+Comment[pt]=Aqui você pode personalizar o Kopete
+Comment[pt_BR]=Agora você pode personalizar o Kopete
+Comment[ro]=Aici puteţi personaliza Kopete
+Comment[ru]=ПерÑÐ¾Ð½Ð°Ð»ÑŒÐ½Ð°Ñ Ð½Ð°Ñтройка Kopete
+Comment[se]=Dáppe sáhtát heivehit Kopete:a iežat hápmái
+Comment[sk]=Tu môžete Kopete prispôsobiť
+Comment[sl]=Tukaj lahko nastavite Kopete
+Comment[sr]=Овде можете прилагодити Kopete Ñеби
+Comment[sr@Latn]=Ovde možete prilagoditi Kopete sebi
+Comment[sv]=Här kan du göra personlig anpassning av Kopete
+Comment[tg]=Дар Ин Ҷо Шумо Kopete-и Худро ШахÑÓ£ Карда Метавонед
+Comment[tr]=Burada Kopete'yi KiÅŸiselleÅŸtirebilirsiniz
+Comment[uk]=Тут можна наладнати Kopete під Ñебе
+Comment[uz]=Bu yerda turli xususiyatlarni moslash mumkin
+Comment[uz@cyrillic]=Бу ерда турли хуÑуÑиÑтларни моÑлаш мумкин
+Comment[wa]=Chal vos ploz apontyî Kopete a vosse mode
+Comment[zh_CN]=您å¯åœ¨æ­¤ä¸ªæ€§åŒ– Kopete
+Comment[zh_HK]=在此您å¯è¨­å®š Kopete 的個人化資料
+Comment[zh_TW]=您已經在此將 Kopete 個人化
+DocPath=kopete/configure-dialog.html#configuring-behavior
+
+
+
diff --git a/kopete/kopete/config/behavior/kopeteawayconfigbase.ui b/kopete/kopete/config/behavior/kopeteawayconfigbase.ui
new file mode 100644
index 00000000..8d0b8441
--- /dev/null
+++ b/kopete/kopete/config/behavior/kopeteawayconfigbase.ui
@@ -0,0 +1,356 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KopeteAwayConfigBaseUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteAwayConfigBaseUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>471</width>
+ <height>266</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Away Configuration</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Number of away messages to remember:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Kopete will remember this many away messages for use at a later date; if this limit is exceeded, the least-used message will be removed.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Kopete will remember this many away messages for use at a later date; if this limit is exceeded, the least-used message will be removed.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>rememberedMessages</cstring>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Kopete will remember this many away messages for use at a later date; if this limit is exceeded, the least-used message will be removed.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Kopete will remember this many away messages for use at a later date; if this limit is exceeded, the least-used message will be removed.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Auto Away</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;p&gt;If you check the &lt;i&gt;Use auto away&lt;/i&gt; checkbox, Kopete will automaticaly set you globaly away when the KDE screen saver start, or after the selected minutes of user inactivity (i.e no mouse move, or key pressed)&lt;/p&gt;
+&lt;p&gt;Kopete will set you available again when you come back if you checked &lt;i&gt;Become available when detecting activity again&lt;/i&gt;&lt;/p&gt;</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mUseAutoAway</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use auto away</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Become away after</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mAutoAwayTimeout</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>minutes of user inactivity</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>81</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mGoAvailable</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Become available when detecting activity again</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>autoAwayMessageGroup</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Auto Away Message</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mDisplayLastAwayMessage</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Display the last away message used</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>mDisplayCustomAwayMessage</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Display the following away message:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>mAutoAwayMessageEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>TextLabel2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mAutoAwayTimeout</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>TextLabel3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mGoAvailable</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mDisplayLastAwayMessage</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mDisplayCustomAwayMessage</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mDisplayCustomAwayMessage</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mAutoAwayMessageEdit</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mUseAutoAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>autoAwayMessageGroup</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>mUseAutoAway</tabstop>
+ <tabstop>mAutoAwayTimeout</tabstop>
+ <tabstop>mGoAvailable</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/identity/Makefile.am b/kopete/kopete/config/identity/Makefile.am
new file mode 100644
index 00000000..3f0fa409
--- /dev/null
+++ b/kopete/kopete/config/identity/Makefile.am
@@ -0,0 +1,15 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(KOPETE_COMPAT_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_kopete_identityconfig.la
+
+kcm_kopete_identityconfig_la_SOURCES = kopeteidentityconfigbase.ui \
+ kopeteidentityconfig.cpp globalidentitiesmanager.cpp kopeteidentityconfigpreferences.kcfgc
+kcm_kopete_identityconfig_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_identityconfig_la_LIBADD = -lkabc ../../../libkopete/libkopete.la $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_identityconfig.desktop
+servicedir = $(kde_servicesdir)
+
+# vim: set noet:
+kde_kcfg_DATA = kopeteidentityconfigpreferences.kcfg
diff --git a/kopete/kopete/config/identity/globalidentitiesmanager.cpp b/kopete/kopete/config/identity/globalidentitiesmanager.cpp
new file mode 100644
index 00000000..192849e4
--- /dev/null
+++ b/kopete/kopete/config/identity/globalidentitiesmanager.cpp
@@ -0,0 +1,260 @@
+/*
+ globalidentitiesmanager.h - Kopete Global identities manager.
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "globalidentitiesmanager.h"
+
+// Qt includes
+#include <qdom.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+// KDE includes
+#include <kdebug.h>
+#include <ksavefile.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kstandarddirs.h>
+
+// Kopete includes
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetepluginmanager.h"
+
+class GlobalIdentitiesManager::Private
+{
+public:
+ QMap<QString, Kopete::MetaContact*> identitiesList;
+};
+
+GlobalIdentitiesManager *GlobalIdentitiesManager::s_self = 0L;
+GlobalIdentitiesManager *GlobalIdentitiesManager::self()
+{
+ if ( !s_self )
+ s_self = new GlobalIdentitiesManager;
+
+ return s_self;
+}
+
+GlobalIdentitiesManager::GlobalIdentitiesManager(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+ d = new Private;
+}
+
+GlobalIdentitiesManager::~GlobalIdentitiesManager()
+{
+ s_self = 0L;
+
+ delete d;
+}
+
+void GlobalIdentitiesManager::createNewIdentity(const QString &identityName)
+{
+ // Create new identity metacontact based on myself to get the sub-contacts.
+ Kopete::MetaContact *newIdentity = createNewMetaContact();
+
+ // Add to internal list.
+ d->identitiesList.insert(identityName, newIdentity);
+}
+
+void GlobalIdentitiesManager::copyIdentity(const QString &copyIdentityName, const QString &sourceIdentity)
+{
+ Kopete::MetaContact *copyIdentity = createCopyMetaContact(d->identitiesList[sourceIdentity]);
+
+ d->identitiesList.insert(copyIdentityName, copyIdentity);
+}
+
+void GlobalIdentitiesManager::renameIdentity(const QString &oldName, const QString &newName)
+{
+ Kopete::MetaContact *renamedIdentity = d->identitiesList[oldName];
+ d->identitiesList.remove(oldName);
+ d->identitiesList.insert(newName, renamedIdentity);
+}
+
+void GlobalIdentitiesManager::removeIdentity(const QString &removedIdentity)
+{
+ // Clear from memory the identity metacontact.
+ delete d->identitiesList[removedIdentity];
+ // Remove from the list.
+ d->identitiesList.remove(removedIdentity);
+}
+
+void GlobalIdentitiesManager::updateIdentity(const QString &updatedIdentity, Kopete::MetaContact *sourceMetaContact)
+{
+ copyMetaContact(d->identitiesList[updatedIdentity], sourceMetaContact);
+}
+
+bool GlobalIdentitiesManager::isIdentityPresent(const QString &identityName)
+{
+ QMapIterator<QString, Kopete::MetaContact*> it;
+ QMapIterator<QString, Kopete::MetaContact*> end = d->identitiesList.end();
+
+ for(it = d->identitiesList.begin(); it != end; ++it)
+ {
+ if(it.key() == identityName)
+ {
+ // A entry with the same name was found.
+ return true;
+ }
+ }
+ return false;
+}
+
+Kopete::MetaContact *GlobalIdentitiesManager::getIdentity(const QString &identityName)
+{
+ // Check if the identity is present.
+ return isIdentityPresent(identityName) ? d->identitiesList[identityName] : 0;
+}
+
+void GlobalIdentitiesManager::loadXML()
+{
+ kdDebug() << k_funcinfo << "Loading global identities list from XML." << endl;
+
+ QString filename = locateLocal( "appdata", QString::fromUtf8("global-identities.xml") );
+ if( filename.isEmpty() )
+ {
+ return;
+ }
+
+ QDomDocument globalIdentitiesList( QString::fromUtf8( "kopete-global-identities-list" ) );
+
+ QFile globalIdentitiesListFile( filename );
+ globalIdentitiesListFile.open( IO_ReadOnly );
+ globalIdentitiesList.setContent( &globalIdentitiesListFile );
+
+ QDomElement list = globalIdentitiesList.documentElement();
+ QDomElement element = list.firstChild().toElement();
+ while( !element.isNull() )
+ {
+ if( element.tagName() == QString::fromUtf8("identity") )
+ {
+ Kopete::MetaContact *metaContact = createNewMetaContact();
+ QString identityName = element.attribute(QString::fromUtf8("name"));
+
+ if(!metaContact->fromXML(element))
+ {
+ delete metaContact;
+ metaContact = 0L;
+ }
+ else
+ {
+ d->identitiesList.insert(identityName, metaContact);
+ }
+ }
+ element = element.nextSibling().toElement();
+ }
+
+ // If no identity are loaded, create a default identity MetaContact.
+ if(d->identitiesList.empty())
+ {
+ createNewIdentity(i18n("Default Identity"));
+ }
+}
+
+void GlobalIdentitiesManager::saveXML()
+{
+ kdDebug() << k_funcinfo << "Saving global identities list to XML." << endl;
+
+ QString globalIdentitiesListFileName = locateLocal( "appdata", QString::fromUtf8("global-identities.xml") );
+ KSaveFile globalIdentitiesListFile(globalIdentitiesListFileName);
+ if( globalIdentitiesListFile.status() == 0 )
+ {
+ QTextStream *stream = globalIdentitiesListFile.textStream();
+ stream->setEncoding( QTextStream::UnicodeUTF8 );
+ toXML().save( *stream, 4 );
+
+ if ( globalIdentitiesListFile.close() )
+ {
+ return;
+ }
+ else
+ {
+ kdDebug(14000) << k_funcinfo << "Failed to write global identities list, error code is: " << globalIdentitiesListFile.status() << endl;
+ }
+ }
+ else
+ {
+ kdWarning(14000) << k_funcinfo << "Couldn't open global identities list file " << globalIdentitiesListFileName
+ << ". Global Identities list not saved." << endl;
+ }
+}
+
+const QDomDocument GlobalIdentitiesManager::toXML()
+{
+ QDomDocument doc;
+
+ doc.appendChild(doc.createElement(QString::fromUtf8("kopete-global-identities-list")));
+
+ QMapIterator<QString, Kopete::MetaContact*> it;
+ QMapIterator<QString, Kopete::MetaContact*> end = d->identitiesList.end();
+ for(it = d->identitiesList.begin(); it != end; ++it)
+ {
+ kdDebug(14000) << k_funcinfo << "Saving " << it.key() << endl;
+ QDomElement identityMetaContactElement = it.data()->toXML(true); // Save minimal information.
+ identityMetaContactElement.setTagName(QString::fromUtf8("identity"));
+ identityMetaContactElement.setAttribute(QString::fromUtf8("name"), it.key());
+ doc.documentElement().appendChild(doc.importNode(identityMetaContactElement, true));
+ }
+
+ return doc;
+}
+
+Kopete::MetaContact *GlobalIdentitiesManager::createNewMetaContact()
+{
+ Kopete::MetaContact *newMetaContact = new Kopete::MetaContact();
+ QPtrList<Kopete::Contact> contactList = Kopete::ContactList::self()->myself()->contacts();
+
+ // Copy the contacts list to the new metacontact, so Kopete::Contact for SourceContact
+ // will not be null.
+ QPtrListIterator<Kopete::Contact> it( contactList);
+ for ( ; it.current(); ++it )
+ {
+ newMetaContact->addContact(it.current());
+ }
+
+ newMetaContact->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ newMetaContact->setPhotoSource(Kopete::MetaContact::SourceCustom);
+
+ return newMetaContact;
+}
+
+Kopete::MetaContact *GlobalIdentitiesManager::createCopyMetaContact(Kopete::MetaContact *source)
+{
+ Kopete::MetaContact *copyMetaContactObject = createNewMetaContact();
+
+ copyMetaContact(copyMetaContactObject, source);
+
+ return copyMetaContactObject;
+}
+
+void GlobalIdentitiesManager::copyMetaContact(Kopete::MetaContact *destination, Kopete::MetaContact *source)
+{
+ destination->setDisplayName(source->customDisplayName());
+ destination->setDisplayNameSource(source->displayNameSource());
+ destination->setDisplayNameSourceContact(source->displayNameSourceContact());
+
+ destination->setPhoto(source->customPhoto());
+ destination->setPhotoSource(source->photoSource());
+ destination->setPhotoSourceContact(source->photoSourceContact());
+}
+
+QMap<QString, Kopete::MetaContact*> GlobalIdentitiesManager::getGlobalIdentitiesList()
+{
+ return d->identitiesList;
+}
+
+#include "globalidentitiesmanager.moc"
diff --git a/kopete/kopete/config/identity/globalidentitiesmanager.h b/kopete/kopete/config/identity/globalidentitiesmanager.h
new file mode 100644
index 00000000..c188d497
--- /dev/null
+++ b/kopete/kopete/config/identity/globalidentitiesmanager.h
@@ -0,0 +1,143 @@
+/*
+ globalidentitiesmanager.h - Kopete Global identities manager.
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GLOBALIDENTITIESMANAGER_H
+#define GLOBALIDENTITIESMANAGER_H
+
+#include <qobject.h>
+#include <qmap.h>
+
+namespace Kopete
+{
+ class MetaContact;
+}
+class QDomDocument;
+
+/**
+ * This singleton class handle the loading, saving and manipulating of all the global identities from a XML file.
+ * It also hold the pointer list of metacontacts.
+ * Use this class with GlobalIdentitiesManager::self()
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+*/
+class GlobalIdentitiesManager : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * @brief Return the single instance of GlobalIdentitiesManager class
+ *
+ * The global identities manager is a singleton class of which only
+ * a single instance will exist. If no manager exists yet, this method
+ * create one for you.
+ *
+ * @return The single instance of GlobalIdentitiesManager class.
+ * FIXME: Should I remove the singleton pattern ?
+ */
+ static GlobalIdentitiesManager* self();
+ ~GlobalIdentitiesManager();
+
+ /**
+ * @brief Create a new identity and add it to the internal list.
+ */
+ void createNewIdentity(const QString &identityName);
+
+ /**
+ * @brief Copy a identity
+ *
+ * @param copyIdentityName Name for the copy identity.
+ * @param sourceIdentity Name of the source identity.
+ */
+ void copyIdentity(const QString &copyIdentityName, const QString &sourceIdentity);
+
+ /**
+ * @brief Rename a identity
+ *
+ * @param oldName Identity to rename.
+ * @param newName New identity name.
+ */
+ void renameIdentity(const QString &oldName, const QString &newName);
+
+ /**
+ * @brief Delete identity
+ *
+ * @param removedIdentity Identity name to remove.
+ */
+ void removeIdentity(const QString &removedIdentity);
+
+ /**
+ * @brief Update the specified identity using the source MetaContact
+ *
+ * @param updatedIdentity Identity to update.
+ * @param sourceMetaContact Source of data.
+ */
+ void updateIdentity(const QString &updatedIdentity, Kopete::MetaContact *sourceMetaContact);
+
+ /**
+ * @brief Check if the specified identityName exists.
+ *
+ * This is a helper method to avoid duplicated entries.
+ * @return if the identityName is in the internal list.
+ */
+ bool isIdentityPresent(const QString &identityName);
+
+ /**
+ * @brief Return the specified identity.
+ *
+ * @param identityName Identity to retrive.
+ * @return Identity data as Kopete::MetaContact.
+ */
+ Kopete::MetaContact *getIdentity(const QString &identityName);
+
+ /**
+ * @brief Load the XML file where global identities metacontacts are stored.
+ */
+ void loadXML();
+
+ /**
+ * @brief Save the global identities metacontacts to XML file.
+ */
+ void saveXML();
+
+ /**
+ * @brief Return the list of global identities metacontact.
+ * @return The pointer list of metacontact as QValueList
+ */
+ QMap<QString, Kopete::MetaContact*> getGlobalIdentitiesList();
+
+private:
+ GlobalIdentitiesManager(QObject *parent = 0, const char *name = 0);
+
+ /**
+ * @brief Return a XML representation of the global identities list.
+ *
+ * @return the XML represention as QDomDocument.
+ */
+ const QDomDocument toXML();
+
+ Kopete::MetaContact *createNewMetaContact();
+ Kopete::MetaContact *createCopyMetaContact(Kopete::MetaContact *source);
+ void copyMetaContact(Kopete::MetaContact *destination, Kopete::MetaContact *source);
+
+private:
+ static GlobalIdentitiesManager *s_self;
+ class Private;
+ Private *d;
+
+};
+
+#endif
diff --git a/kopete/kopete/config/identity/kopete_identityconfig.desktop b/kopete/kopete/config/identity/kopete_identityconfig.desktop
new file mode 100644
index 00000000..6b655550
--- /dev/null
+++ b/kopete/kopete/config/identity/kopete_identityconfig.desktop
@@ -0,0 +1,112 @@
+[Desktop Entry]
+Icon=identity
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_identityconfig
+X-KDE-FactoryName=KopeteIdentityConfigFactory
+X-KDE-ParentApp=kopete
+X-KDE-ParentComponents=kopete
+
+Name=Identity
+Name[be]=ÐŸÑ€Ñ‹Ð²Ð°Ñ‚Ð½Ñ‹Ñ Ð·Ð²ÐµÑткі
+Name[bg]=ИдентификациÑ
+Name[bn]=পরিচয়
+Name[br]=Anvelezh
+Name[bs]=Identitet
+Name[ca]=Identitat
+Name[cs]=Identita
+Name[cy]=Dynodiad
+Name[da]=Identitet
+Name[de]=Identität
+Name[el]=Ταυτότητα
+Name[eo]=Idento
+Name[es]=Identidad
+Name[et]=Identiteet
+Name[eu]=Identitatea
+Name[fa]=هویت
+Name[fi]=Identiteetti
+Name[fr]=Identité
+Name[ga]=Aitheantas
+Name[gl]=Indentidade
+Name[he]=חשבון
+Name[hu]=Azonosító
+Name[is]=Auðkenni
+Name[it]=Identità
+Name[ja]=分身
+Name[ka]=პრáƒáƒ¤áƒ˜áƒšáƒ˜
+Name[kk]=Профилі
+Name[km]=អážáŸ’ážážŸáž‰áŸ’ញាណ
+Name[lt]=TapatybÄ—
+Name[nb]=Identitet
+Name[nds]=Identiteet
+Name[ne]=परिचय
+Name[nl]=Identiteit
+Name[nn]=Identitet
+Name[pl]=Tożsamość
+Name[pt]=Identidade
+Name[pt_BR]=Identidade
+Name[ru]=Профиль
+Name[sk]=Identita
+Name[sl]=Identiteta
+Name[sr]=Идентитет
+Name[sr@Latn]=Identitet
+Name[sv]=Identitet
+Name[tr]=Kimlik
+Name[uk]=Профіль
+Name[uz]=Shaxsiyat
+Name[uz@cyrillic]=ШахÑиÑÑ‚
+Name[zh_CN]=身份
+Name[zh_HK]=識別å稱
+Name[zh_TW]=身份
+Comment=Here You Can Manage Your Global Identity
+Comment[be]=Ð“Ð»Ð°Ð±Ð°Ð»ÑŒÐ½Ñ‹Ñ Ð¿Ñ€Ñ‹Ð²Ð°Ñ‚Ð½Ñ‹Ñ Ð·Ð²ÐµÑткі
+Comment[bg]=ÐаÑтройване на идентификациÑта
+Comment[bn]=আপনি à¦à¦–ানে আপনার বিশà§à¦¬ পরিচয় পরিচালনা করতে পারেন
+Comment[bs]=Ovdje možete upravljati svojim globalnim identiteom
+Comment[ca]=Aquí podreu gestionar globalment la vostra identitat
+Comment[cs]=Zde můžete spravovat svou globální identitu
+Comment[da]=Her kan du håndtere din globale identitet
+Comment[de]=Hier können Sie Ihre globale Identität verwalten
+Comment[el]=Εδώ μποÏείτε να χειÏιστείτε την καθολική σας ταυτότητα
+Comment[es]=Aquí puede gestionar su identidad global
+Comment[et]=Siin saad hallata oma globaalset identiteeti
+Comment[eu]=Hemen zure identitate globala kudea dezakezu
+Comment[fa]=اینجا می‌توانید هویت سراسری خود را مدیریت کنید
+Comment[fi]=Täältä voit hallita globaalia identiteettiäsi
+Comment[fr]=Vous pouvez gérer ici votre identité principale
+Comment[ga]=Tig Leat d'Aitheantas a Láimhseáil Anseo
+Comment[gl]=Aquí podes xestionar a súa identidade global
+Comment[he]=×›×ן ב×פשרותך לנהל ×ת כל חשבונותיך
+Comment[hu]=Itt lehet kezelni a globális azonosítókat
+Comment[is]=Hér geturðu haldið utan um víðværa auðkennið þitt
+Comment[it]=Qui puoi gestire la tua identità globale
+Comment[ja]=ã“ã“ã§ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå…±é€šã®ã‚ãªãŸã®åˆ†èº«ã‚’設定ã—ã¾ã™
+Comment[ka]=áƒáƒ¥ თქვენ თქვენი ზáƒáƒ’áƒáƒ“ი იდენტიფიკáƒáƒªáƒ˜áƒ˜áƒ¡ გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=Мұнда жалпы профилін баÑқара алаÑыз
+Comment[km]=ទីនáŸáŸ‡ អ្នក​អាច​គ្រប់គ្រង​អážáŸ’ážážŸáž‰áŸ’ញាណ​សកល​របស់​អ្នក
+Comment[lt]=ÄŒia galite tvarkyti globaliÄ…jÄ… paskyrÄ…
+Comment[nb]=Her kan du behandle din globale identitet
+Comment[nds]=Hier kannst Du Dien globale Identiteet plegen
+Comment[ne]=यहाठतपाईठतपाईà¤à¤•à¥‹ विशà¥à¤µà¤µà¥à¤¯à¤¾à¤ªà¥€ परिचय पà¥à¤°à¤¬à¤¨à¥à¤§ गरà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Hier kunt u uw globale identiteit beheren
+Comment[nn]=Her kan du handtera den globale identiteten din
+Comment[pl]=Tutaj można zarządzać Twoją tożsamością
+Comment[pt]=Aqui Você Pode Gerir a Sua Identidade Global
+Comment[pt_BR]=Aqui Você Pode Gerenciar Sua Identidade Global
+Comment[ru]=ÐаÑтройка глобального Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ
+Comment[sk]=Tu môžete spravovať Vašu globálnu identifikáciu
+Comment[sl]=Tukaj lahko upravljate s svojo globalno identiteto
+Comment[sr]=Овде можете управљати вашим општим идентитетом
+Comment[sr@Latn]=Ovde možete upravljati vašim opštim identitetom
+Comment[sv]=Här kan du hantera din generella identitet
+Comment[tr]=Bütün Genel Kimliklerinizi Buradan Yönetebilirsiniz
+Comment[uk]=Тут можна керувати вашим глобальним профілем
+Comment[uz]=Bu yerda umumiy shaxsiyatni moslash mumkin
+Comment[uz@cyrillic]=Бу ерда умумий шахÑиÑтни моÑлаш мумкин
+Comment[zh_CN]=您å¯åœ¨æ­¤ç®¡ç†æ‚¨çš„全局身份
+Comment[zh_HK]=在此您å¯ç®¡ç† Kopete 的全域識別å稱
+Comment[zh_TW]=您å¯ä»¥åœ¨æ­¤ç®¡ç†æ‚¨çš„全域身份
+
+DocPath=kopete/configure-dialog.html#configuring-identity
diff --git a/kopete/kopete/config/identity/kopeteidentityconfig.cpp b/kopete/kopete/config/identity/kopeteidentityconfig.cpp
new file mode 100644
index 00000000..26613b87
--- /dev/null
+++ b/kopete/kopete/config/identity/kopeteidentityconfig.cpp
@@ -0,0 +1,636 @@
+/*
+ kopeteidentityconfig.cpp - Kopete Identity config page
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteidentityconfig.h"
+
+// Qt includes
+#include <qlayout.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qapplication.h>
+#include <qbuffer.h>
+
+// KDE includes
+#include <kcombobox.h>
+#include <kfiledialog.h>
+#include <kpushbutton.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kglobal.h>
+#include <kurlrequester.h>
+#include <kinputdialog.h>
+#include <kpixmapregionselectordialog.h>
+#include <kmdcodec.h>
+
+// KDE KIO includes
+#include <kio/netaccess.h>
+
+// KDE KABC(AddressBook) includes
+#include <kabc/addresseedialog.h>
+#include <kabc/stdaddressbook.h>
+#include <kabc/addressee.h>
+
+// Kopete include
+#include "kabcpersistence.h"
+#include "kopeteglobal.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "addressbookselectordialog.h"
+#include "kopeteconfig.h"
+
+// Local includes
+#include "kopeteidentityconfigbase.h"
+#include "globalidentitiesmanager.h"
+#include "kopeteidentityconfigpreferences.h"
+
+class KopeteIdentityConfig::Private
+{
+public:
+ Private() : m_view(0L), myself(0L), currentIdentity(0L), selectedIdentity("")
+ {}
+
+ KopeteIdentityConfigBase *m_view;
+ Kopete::MetaContact *myself;
+ Kopete::MetaContact *currentIdentity;
+
+ QMap<int, Kopete::Contact*> contactPhotoSourceList;
+ QString selectedIdentity;
+};
+
+typedef KGenericFactory<KopeteIdentityConfig, QWidget> KopeteIdentityConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_identityconfig, KopeteIdentityConfigFactory( "kcm_kopete_identityconfig" ) )
+
+KopeteIdentityConfig::KopeteIdentityConfig(QWidget *parent, const char */*name*/, const QStringList &args) : KCModule( KopeteIdentityConfigFactory::instance(), parent, args)
+{
+ d = new Private;
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ d->m_view = new KopeteIdentityConfigBase( this, "KopeteIdentityConfig::m_view" );
+
+ // Setup KConfigXT link with GUI.
+ addConfig( Kopete::Config::self(), d->m_view );
+
+ // Load config
+ KopeteIdentityConfigPreferences::self()->readConfig();
+
+ // Load from XML the identities.
+ GlobalIdentitiesManager::self()->loadXML();
+
+ d->myself = Kopete::ContactList::self()->myself();
+
+ // Set the latest selected Identity.
+ d->selectedIdentity = KopeteIdentityConfigPreferences::self()->selectedIdentity();
+ kdDebug() << k_funcinfo << "Latest loaded identity: " << d->selectedIdentity << endl;
+
+ // If the latest selected Identity is not present anymore, use a fallback identity.
+ if( !GlobalIdentitiesManager::self()->isIdentityPresent(d->selectedIdentity) )
+ {
+ QMapIterator<QString, Kopete::MetaContact*> it = GlobalIdentitiesManager::self()->getGlobalIdentitiesList().begin();
+ d->selectedIdentity = it.key();
+ }
+ else
+ {
+ // Update the latest identity with myself Metacontact.
+ GlobalIdentitiesManager::self()->updateIdentity(d->selectedIdentity, d->myself);
+ }
+ d->currentIdentity = GlobalIdentitiesManager::self()->getIdentity(d->selectedIdentity);
+
+ // Set icon for KPushButton
+ d->m_view->buttonNewIdentity->setIconSet(SmallIconSet("new"));
+ d->m_view->buttonCopyIdentity->setIconSet(SmallIconSet("editcopy"));
+ d->m_view->buttonRenameIdentity->setIconSet(SmallIconSet("edit"));
+ d->m_view->buttonRemoveIdentity->setIconSet(SmallIconSet("delete_user"));
+ d->m_view->buttonClearPhoto->setIconSet( SmallIconSet( QApplication::reverseLayout() ? "locationbar_erase" : "clear_left" ) );
+
+ load(); // Load Configuration
+
+ // Action signal/slots
+ connect(d->m_view->buttonChangeAddressee, SIGNAL(clicked()), this, SLOT(slotChangeAddressee()));
+ connect(d->m_view->comboSelectIdentity, SIGNAL(activated(const QString &)), this, SLOT(slotUpdateCurrentIdentity(const QString& )));
+ connect(d->m_view->buttonNewIdentity, SIGNAL(clicked()), this, SLOT(slotNewIdentity()));
+ connect(d->m_view->buttonCopyIdentity, SIGNAL(clicked()), this, SLOT(slotCopyIdentity()));
+ connect(d->m_view->buttonRenameIdentity, SIGNAL(clicked()), this, SLOT(slotRenameIdentity()));
+ connect(d->m_view->buttonRemoveIdentity, SIGNAL(clicked()), this, SLOT(slotRemoveIdentity()));
+ connect(d->m_view->comboPhotoURL, SIGNAL(urlSelected(const QString& )), this, SLOT(slotChangePhoto(const QString& )));
+ connect(d->m_view->buttonClearPhoto, SIGNAL(clicked()), this, SLOT(slotClearPhoto()));
+
+ // Settings signal/slots
+ connect(d->m_view->radioNicknameContact, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+ connect(d->m_view->radioNicknameCustom, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+ connect(d->m_view->radioNicknameKABC, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+
+ connect(d->m_view->radioPhotoContact, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+ connect(d->m_view->radioPhotoCustom, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+ connect(d->m_view->radioPhotoKABC, SIGNAL(toggled(bool )), this, SLOT(slotEnableAndDisableWidgets()));
+
+ connect(d->m_view->checkSyncPhotoKABC, SIGNAL(toggled(bool )), this, SLOT(slotSettingsChanged()));
+ connect(d->m_view->lineNickname, SIGNAL(textChanged(const QString& )), this, SLOT(slotSettingsChanged()));
+ connect(d->m_view->comboNameContact, SIGNAL(activated(int )), this, SLOT(slotSettingsChanged()));
+ connect(d->m_view->comboPhotoContact, SIGNAL(activated(int )), this, SLOT(slotEnableAndDisableWidgets()));
+}
+
+KopeteIdentityConfig::~KopeteIdentityConfig()
+{
+ delete d;
+}
+
+void KopeteIdentityConfig::load()
+{
+ KCModule::load();
+
+ // Populate the select Identity combo box.
+ loadIdentities();
+ // Populate the name contact ComboBox
+ slotLoadNameSources();
+ // Populate the photo contact ComboBOx
+ slotLoadPhotoSources();
+
+ KABC::Addressee a = KABC::StdAddressBook::self()->whoAmI();
+ // Load the address book link
+ if (!a.isEmpty())
+ {
+ d->m_view->lineAddressee->setText(a.realName());
+ }
+
+ slotEnableAndDisableWidgets();
+}
+
+void KopeteIdentityConfig::save()
+{
+ KCModule::save();
+
+ saveCurrentIdentity();
+
+ // Don't save the new global identity if it's not activated.
+ if(d->m_view->kcfg_EnableGlobalIdentity->isChecked())
+ {
+ // Save the myself metacontact settings.
+ // Nickname settings.
+ if(d->m_view->lineNickname->text() != d->myself->customDisplayName())
+ d->myself->setDisplayName(d->m_view->lineNickname->text());
+
+ d->myself->setDisplayNameSource(selectedNameSource());
+ d->myself->setDisplayNameSourceContact(selectedNameSourceContact());
+
+ // Photo settings
+ d->myself->setPhotoSource(selectedPhotoSource());
+ d->myself->setPhotoSourceContact(selectedPhotoSourceContact());
+ if(!d->m_view->comboPhotoURL->url().isEmpty())
+ d->myself->setPhoto(d->m_view->comboPhotoURL->url());
+ else
+ d->myself->setPhoto( KURL() );
+ d->myself->setPhotoSyncedWithKABC(d->m_view->checkSyncPhotoKABC->isChecked());
+ }
+
+ // Save global identities list.
+ KopeteIdentityConfigPreferences::self()->setSelectedIdentity(d->selectedIdentity);
+ GlobalIdentitiesManager::self()->saveXML();
+
+ // (Re)made slot connections to apply Global Identity in protocols
+ Kopete::ContactList::self()->loadGlobalIdentity();
+
+ load();
+}
+
+void KopeteIdentityConfig::loadIdentities()
+{
+ d->m_view->comboSelectIdentity->clear();
+
+ QMap<QString, Kopete::MetaContact*> identitiesList = GlobalIdentitiesManager::self()->getGlobalIdentitiesList();
+ QMapIterator<QString, Kopete::MetaContact*> it;
+ QMapIterator<QString, Kopete::MetaContact*> end = identitiesList.end();
+
+ int count=0, selectedIndex=0;
+ for(it = identitiesList.begin(); it != end; ++it)
+ {
+ d->m_view->comboSelectIdentity->insertItem(it.key());
+ if(it.key() == d->selectedIdentity)
+ {
+ selectedIndex = count;
+ }
+ count++;
+ }
+
+ d->m_view->comboSelectIdentity->setCurrentItem(selectedIndex);
+ d->m_view->buttonRemoveIdentity->setEnabled(count == 1 ? false : true);
+}
+
+void KopeteIdentityConfig::saveCurrentIdentity()
+{
+ kdDebug() << k_funcinfo << "Saving data of current identity." << endl;
+ // Ignore saving when removing a identity
+ if(!d->currentIdentity)
+ return;
+
+ if(d->m_view->lineNickname->text() != d->currentIdentity->customDisplayName())
+ d->currentIdentity->setDisplayName(d->m_view->lineNickname->text());
+
+ d->currentIdentity->setDisplayNameSource(selectedNameSource());
+ d->currentIdentity->setDisplayNameSourceContact(selectedNameSourceContact());
+
+ // Photo settings
+ d->currentIdentity->setPhotoSource(selectedPhotoSource());
+ d->currentIdentity->setPhotoSourceContact(selectedPhotoSourceContact());
+ if(!d->m_view->comboPhotoURL->url().isEmpty())
+ d->currentIdentity->setPhoto(d->m_view->comboPhotoURL->url());
+ else
+ d->currentIdentity->setPhoto( KURL() );
+ d->currentIdentity->setPhotoSyncedWithKABC(d->m_view->checkSyncPhotoKABC->isChecked());
+}
+
+void KopeteIdentityConfig::slotLoadNameSources()
+{
+ Kopete::Contact *nameSourceContact = d->currentIdentity->displayNameSourceContact();
+
+ QPtrList<Kopete::Contact> contactList = d->myself->contacts(); // Use myself contact PtrList. Safer.
+ QPtrListIterator<Kopete::Contact> it(contactList);
+
+ d->m_view->comboNameContact->clear();
+
+ for(; it.current(); ++it)
+ {
+ QString account = it.current()->property(Kopete::Global::Properties::self()->nickName()).value().toString() + " <" + it.current()->contactId() + ">";
+ QPixmap accountIcon = it.current()->account()->accountIcon();
+ d->m_view->comboNameContact->insertItem(accountIcon, account);
+
+ // Select this item if it's the one we're tracking.
+ if(it.current() == nameSourceContact)
+ {
+ d->m_view->comboNameContact->setCurrentItem(d->m_view->comboNameContact->count() - 1);
+ }
+ }
+
+ d->m_view->lineNickname->setText(d->currentIdentity->customDisplayName());
+
+ Kopete::MetaContact::PropertySource nameSource = d->currentIdentity->displayNameSource();
+
+ d->m_view->radioNicknameCustom->setChecked(nameSource == Kopete::MetaContact::SourceCustom);
+ d->m_view->radioNicknameKABC->setChecked(nameSource == Kopete::MetaContact::SourceKABC);
+ d->m_view->radioNicknameContact->setChecked(nameSource == Kopete::MetaContact::SourceContact);
+}
+
+void KopeteIdentityConfig::slotLoadPhotoSources()
+{
+ Kopete::Contact *photoSourceContact = d->currentIdentity->photoSourceContact();
+
+ QPtrList<Kopete::Contact> contactList = d->myself->contacts(); // Use myself contact PtrList. Safer.
+ QPtrListIterator<Kopete::Contact> it(contactList);
+
+ d->m_view->comboPhotoContact->clear();
+ d->m_view->comboPhotoURL->clear();
+ d->contactPhotoSourceList.clear();
+
+ for(; it.current(); ++it)
+ {
+ Kopete::Contact *currentContact = it.current();
+ if(currentContact->hasProperty(Kopete::Global::Properties::self()->photo().key()))
+ {
+ QString account = currentContact->property(Kopete::Global::Properties::self()->nickName()).value().toString() + " <" + currentContact->contactId() + ">";
+ QPixmap accountIcon = currentContact->account()->accountIcon();
+
+ d->m_view->comboPhotoContact->insertItem(accountIcon, account);
+ d->contactPhotoSourceList.insert(d->m_view->comboPhotoContact->count() - 1, currentContact);
+
+ // Select this item if it's the one we're tracking.
+ if(currentContact == photoSourceContact)
+ {
+ d->m_view->comboPhotoContact->setCurrentItem(d->m_view->comboPhotoContact->count() - 1);
+ }
+ }
+ }
+
+ d->m_view->comboPhotoURL->setURL(d->currentIdentity->customPhoto().pathOrURL());
+ Kopete::MetaContact::PropertySource photoSource = d->currentIdentity->photoSource();
+
+ d->m_view->radioPhotoCustom->setChecked(photoSource == Kopete::MetaContact::SourceCustom);
+ d->m_view->radioPhotoContact->setChecked(photoSource == Kopete::MetaContact::SourceContact);
+ d->m_view->radioPhotoKABC->setChecked(photoSource == Kopete::MetaContact::SourceKABC);
+
+ d->m_view->checkSyncPhotoKABC->setChecked(d->currentIdentity->isPhotoSyncedWithKABC());
+}
+
+void KopeteIdentityConfig::slotEnableAndDisableWidgets()
+{
+ KABC::Addressee a = KABC::StdAddressBook::self()->whoAmI();
+ bool hasKABCLink = !a.isEmpty();
+
+ d->m_view->radioNicknameKABC->setEnabled(hasKABCLink);
+ d->m_view->radioPhotoKABC->setEnabled(hasKABCLink);
+
+ // Don't sync global photo with KABC if KABC is the source
+ // or if they are no KABC link. (would create a break in timeline)
+ if( selectedPhotoSource() == Kopete::MetaContact::SourceKABC || !hasKABCLink )
+ {
+ d->m_view->checkSyncPhotoKABC->setEnabled(false);
+ }
+ else
+ {
+ d->m_view->checkSyncPhotoKABC->setEnabled(true);
+ }
+
+ d->m_view->radioNicknameContact->setEnabled(d->currentIdentity->contacts().count());
+ d->m_view->radioPhotoContact->setEnabled(!d->contactPhotoSourceList.isEmpty());
+
+ d->m_view->comboNameContact->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceContact);
+ d->m_view->lineNickname->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceCustom);
+
+ d->m_view->comboPhotoContact->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceContact);
+ d->m_view->comboPhotoURL->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceCustom);
+
+ if(d->contactPhotoSourceList.isEmpty() )
+ {
+ d->m_view->comboPhotoContact->clear();
+ d->m_view->comboPhotoContact->insertItem(i18n("No Contacts with Photo Support"));
+ d->m_view->comboPhotoContact->setEnabled(false);
+ }
+
+ QImage photo;
+ switch ( selectedPhotoSource() )
+ {
+ case Kopete::MetaContact::SourceKABC:
+ photo = Kopete::photoFromKABC(a.uid());
+ break;
+ case Kopete::MetaContact::SourceContact:
+ photo = Kopete::photoFromContact(selectedNameSourceContact());
+ break;
+ case Kopete::MetaContact::SourceCustom:
+ photo = QImage(d->m_view->comboPhotoURL->url());
+ break;
+ }
+
+ if(!photo.isNull())
+ d->m_view->labelPhoto->setPixmap(QPixmap(photo.smoothScale(64, 92, QImage::ScaleMin)));
+ else
+ d->m_view->labelPhoto->setPixmap(QPixmap());
+
+ emit changed(true);
+}
+
+void KopeteIdentityConfig::slotUpdateCurrentIdentity(const QString &selectedIdentity)
+{
+ kdDebug() << k_funcinfo << "Updating current identity." << endl;
+
+ // Save the current identity detail, so we don't loose information.
+ saveCurrentIdentity();
+
+ // Change the current identity reflecting the combo box.
+ d->selectedIdentity = selectedIdentity;
+ d->currentIdentity = GlobalIdentitiesManager::self()->getIdentity(d->selectedIdentity);
+ KopeteIdentityConfigPreferences::self()->setSelectedIdentity(d->selectedIdentity);
+ KopeteIdentityConfigPreferences::self()->writeConfig();
+ // Save global identities list.
+ GlobalIdentitiesManager::self()->saveXML();
+
+ // Reload the details.
+ slotLoadNameSources();
+ slotLoadPhotoSources();
+}
+
+void KopeteIdentityConfig::slotNewIdentity()
+{
+ bool ok;
+ QString newIdentityName = KInputDialog::getText(i18n("New Identity"), i18n("Identity name:") , QString::null , &ok);
+
+ if(newIdentityName.isEmpty() || !ok)
+ return;
+
+
+ GlobalIdentitiesManager::self()->createNewIdentity(newIdentityName);
+
+ slotUpdateCurrentIdentity(newIdentityName);
+ loadIdentities();
+}
+
+void KopeteIdentityConfig::slotCopyIdentity()
+{
+ bool ok;
+ QString copyName = KInputDialog::getText(i18n("Copy Identity"), i18n("Identity name:") , QString::null, &ok);
+
+ if(copyName.isEmpty() || !ok)
+ return;
+
+
+ if(!GlobalIdentitiesManager::self()->isIdentityPresent(copyName))
+ {
+ GlobalIdentitiesManager::self()->copyIdentity(copyName, d->selectedIdentity);
+
+ slotUpdateCurrentIdentity(copyName);
+ loadIdentities();
+ }
+ else
+ {
+ KMessageBox::error(this, i18n("An identity with the same name was found."), i18n("Identity Configuration"));
+ }
+}
+
+void KopeteIdentityConfig::slotRenameIdentity()
+{
+ if(d->selectedIdentity.isNull())
+ return;
+
+ bool ok;
+ QString renamedName = KInputDialog::getText(i18n("Rename Identity"), i18n("Identity name:") , d->selectedIdentity, &ok);
+
+ if(renamedName.isEmpty() || !ok)
+ return;
+
+
+ if(renamedName.isEmpty())
+ return;
+
+ if(!GlobalIdentitiesManager::self()->isIdentityPresent(renamedName))
+ {
+ GlobalIdentitiesManager::self()->renameIdentity(d->selectedIdentity, renamedName);
+
+ slotUpdateCurrentIdentity(renamedName);
+ loadIdentities();
+ }
+ else
+ {
+ KMessageBox::error(this, i18n("An identity with the same name was found."), i18n("Identity Configuration"));
+ }
+}
+
+void KopeteIdentityConfig::slotRemoveIdentity()
+{
+ kdDebug() << k_funcinfo << "Removing current identity." << endl;
+ GlobalIdentitiesManager::self()->removeIdentity(d->selectedIdentity);
+ // Reset the currentIdentity pointer. The currentIdentity object was deleted in GlobalIdentitiesManager.
+ d->currentIdentity = 0;
+
+ // Select the entry before(or after) the removed identity.
+ int currentItem = d->m_view->comboSelectIdentity->currentItem();
+ // Use the next item if the removed identity is the first in the comboBox.
+ if(currentItem - 1 < 0)
+ {
+ currentItem++;
+ }
+ else
+ {
+ currentItem--;
+ }
+ d->m_view->comboSelectIdentity->setCurrentItem(currentItem);
+
+ slotUpdateCurrentIdentity(d->m_view->comboSelectIdentity->currentText());
+ loadIdentities();
+}
+
+
+
+void KopeteIdentityConfig::slotChangeAddressee()
+{
+ KABC::Addressee a = Kopete::UI::AddressBookSelectorDialog::getAddressee(i18n("Addressbook Association"), i18n("Choose the person who is yourself."), d->myself->metaContactId(), this);
+
+ if ( !a.isEmpty() )
+ {
+ d->m_view->lineAddressee->setText(a.realName());
+ KABC::StdAddressBook::self()->setWhoAmI(a);
+ d->myself->setMetaContactId(a.uid());
+ }
+
+ emit changed(true);
+}
+
+void KopeteIdentityConfig::slotChangePhoto(const QString &photoUrl)
+{
+ QString saveLocation;
+
+ QImage photo(photoUrl);
+ // use KABC photo size 100x140
+ photo = KPixmapRegionSelectorDialog::getSelectedImage( QPixmap(photo), 96, 96, this );
+
+ if(!photo.isNull())
+ {
+ if(photo.width() > 96 || photo.height() > 96)
+ {
+ // Scale and crop the picture.
+ photo = photo.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, 96, 96);
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, 96, 96);
+
+ }
+ else if (photo.width() < 32 || photo.height() < 32)
+ {
+ // Scale and crop the picture.
+ photo = photo.smoothScale( 32, 32, QImage::ScaleMin );
+ // crop image if not square
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, 32, 32);
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, 32, 32);
+
+ }
+ else if (photo.width() != photo.height())
+ {
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, photo.height(), photo.height());
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, photo.height(), photo.height());
+ }
+
+ // Use MD5 hash to save the filename, so no problems will occur with the filename because of non-ASCII characters.
+ // Bug 124175: My personnal picture doesn't appear cause of l10n
+ QByteArray tempArray;
+ QBuffer tempBuffer(tempArray);
+ tempBuffer.open( IO_WriteOnly );
+ photo.save(&tempBuffer, "PNG");
+ KMD5 context(tempArray);
+ // Save the image to a file.
+ saveLocation = context.hexDigest() + ".png";
+ saveLocation = locateLocal( "appdata", QString::fromUtf8("globalidentitiespictures/%1").arg( saveLocation ) );
+
+ if(!photo.save(saveLocation, "PNG"))
+ {
+ KMessageBox::sorry(this,
+ i18n("An error occurred when trying to save the custom photo for %1 identity.").arg(d->selectedIdentity),
+ i18n("Identity Configuration"));
+ }
+ d->m_view->comboPhotoURL->setURL(saveLocation);
+ slotEnableAndDisableWidgets();
+ }
+ else
+ {
+ KMessageBox::sorry(this,
+ i18n("An error occurred when trying to save the custom photo for %1 identity.").arg(d->selectedIdentity),
+ i18n("Identity Configuration"));
+ }
+}
+
+void KopeteIdentityConfig::slotClearPhoto()
+{
+ d->m_view->comboPhotoURL->setURL( QString::null );
+ slotEnableAndDisableWidgets();
+}
+
+void KopeteIdentityConfig::slotSettingsChanged()
+{
+ emit changed(true);
+}
+
+Kopete::MetaContact::PropertySource KopeteIdentityConfig::selectedNameSource() const
+{
+ if (d->m_view->radioNicknameKABC->isChecked())
+ return Kopete::MetaContact::SourceKABC;
+ if (d->m_view->radioNicknameContact->isChecked())
+ return Kopete::MetaContact::SourceContact;
+ if (d->m_view->radioNicknameCustom->isChecked())
+ return Kopete::MetaContact::SourceCustom;
+ else
+ return Kopete::MetaContact::SourceCustom;
+}
+
+Kopete::MetaContact::PropertySource KopeteIdentityConfig::selectedPhotoSource() const
+{
+ if (d->m_view->radioPhotoKABC->isChecked())
+ return Kopete::MetaContact::SourceKABC;
+ if (d->m_view->radioPhotoContact->isChecked())
+ return Kopete::MetaContact::SourceContact;
+ if (d->m_view->radioPhotoCustom->isChecked())
+ return Kopete::MetaContact::SourceCustom;
+ else
+ return Kopete::MetaContact::SourceCustom;
+}
+
+Kopete::Contact* KopeteIdentityConfig::selectedNameSourceContact() const
+{
+ Kopete::Contact *c = d->myself->contacts().at(d->m_view->comboNameContact->currentItem());
+ return c ? c : 0L;
+}
+
+Kopete::Contact* KopeteIdentityConfig::selectedPhotoSourceContact() const
+{
+ if (d->contactPhotoSourceList.isEmpty())
+ return 0L;
+
+ Kopete::Contact *c = d->contactPhotoSourceList[d->m_view->comboPhotoContact->currentItem()];
+ return c ? c : 0L;
+}
+
+#include "kopeteidentityconfig.moc"
diff --git a/kopete/kopete/config/identity/kopeteidentityconfig.h b/kopete/kopete/config/identity/kopeteidentityconfig.h
new file mode 100644
index 00000000..f18c5a8c
--- /dev/null
+++ b/kopete/kopete/config/identity/kopeteidentityconfig.h
@@ -0,0 +1,80 @@
+/*
+ kopeteidentityconfig.h - Kopete identity config page
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _IDENTITYCONFIG_H
+#define _IDENTITYCONFIG_H
+
+#include <kcmodule.h>
+#include <kconfigdialog.h>
+
+#include "kopetemetacontact.h"
+
+namespace Kopete
+{
+class Contact;
+}
+
+class KopeteIdentityConfigBase;
+
+/**
+ * @author Michaël Larouche <shock@shockdev.ca.tc>
+ */
+class KopeteIdentityConfig : public KCModule
+{
+ Q_OBJECT
+public:
+ KopeteIdentityConfig(QWidget *parent, const char *name, const QStringList &args );
+ ~KopeteIdentityConfig();
+
+public slots:
+ virtual void save();
+ virtual void load();
+
+private:
+ void loadIdentities();
+ void saveCurrentIdentity();
+
+ Kopete::MetaContact::PropertySource selectedNameSource() const;
+ Kopete::MetaContact::PropertySource selectedPhotoSource() const;
+ Kopete::Contact* selectedNameSourceContact() const;
+ Kopete::Contact* selectedPhotoSourceContact() const;
+
+private slots:
+ void slotLoadNameSources();
+ void slotLoadPhotoSources();
+ void slotEnableAndDisableWidgets();
+
+ void slotUpdateCurrentIdentity(const QString &selectedIdentity);
+ void slotNewIdentity();
+ void slotCopyIdentity();
+ void slotRenameIdentity();
+ void slotRemoveIdentity();
+
+ void slotChangeAddressee();
+ void slotChangePhoto(const QString &photoUrl);
+ void slotClearPhoto();
+
+ void slotSettingsChanged();
+
+private:
+ class Private;
+ Private *d;
+
+};
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/config/identity/kopeteidentityconfigbase.ui b/kopete/kopete/config/identity/kopeteidentityconfigbase.ui
new file mode 100644
index 00000000..4e31a29f
--- /dev/null
+++ b/kopete/kopete/config/identity/kopeteidentityconfigbase.ui
@@ -0,0 +1,541 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KopeteIdentityConfigBase</class>
+<author>Michaël Larouche</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteIdentityConfigBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>633</width>
+ <height>447</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_EnableGlobalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Enable &amp;global identity</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Identity:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>comboSelectIdentity</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonNewIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Ne&amp;w Identity...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonCopyIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Cop&amp;y Identity...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonRenameIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Rename I&amp;dentity...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonRemoveIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Remo&amp;ve Identity</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Nickname</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>nicknameButtonGroup</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNicknameCustom</cstring>
+ </property>
+ <property name="text">
+ <string>Cu&amp;stom:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>lineNickname</cstring>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNicknameKABC</cstring>
+ </property>
+ <property name="text">
+ <string>Use address boo&amp;k name (need address book link)</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNicknameContact</cstring>
+ </property>
+ <property name="text">
+ <string>Use nickname from con&amp;tact for global nickname:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>comboNameContact</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Contact to synchronize the displayname with.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>P&amp;hoto</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>photoButtonGroup</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLComboRequester" row="1" column="0">
+ <property name="name">
+ <cstring>comboPhotoURL</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>buttonClearPhoto</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32</width>
+ <height>3232</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>checkSyncPhotoKABC</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;ync address book photo with global photo</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>comboPhotoContact</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>radioPhotoCustom</cstring>
+ </property>
+ <property name="text">
+ <string>Cus&amp;tom:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>radioPhotoContact</cstring>
+ </property>
+ <property name="text">
+ <string>U&amp;se photo from contact for global photo:</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>radioPhotoKABC</cstring>
+ </property>
+ <property name="text">
+ <string>Use a&amp;ddress book photo (needs address book link)</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <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>36</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelPhoto</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>92</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>64</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="text">
+ <string>&lt;center&gt;Photo&lt;/center&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </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>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Address &amp;Book Link</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>lineAddressee</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonChangeAddressee</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>C&amp;hange...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Note:&lt;/b&gt; The address book link uses KAddressBook's
+current user contact.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_2</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>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_EnableGlobalIdentity</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nicknameButtonGroup</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_EnableGlobalIdentity</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>photoButtonGroup</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_EnableGlobalIdentity</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>comboSelectIdentity</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_EnableGlobalIdentity</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>buttonChangeAddressee</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfg b/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfg
new file mode 100644
index 00000000..3d004495
--- /dev/null
+++ b/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfg
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author: Michaël Larouche-->
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kopeterc"/>
+
+ <group name="Kopete Identity Config">
+ <entry name="SelectedIdentity" type="String">
+ <label>Latest selected global identity.</label>
+ <default code="true">i18n("Default Identity")</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfgc b/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfgc
new file mode 100644
index 00000000..6f93d40f
--- /dev/null
+++ b/kopete/kopete/config/identity/kopeteidentityconfigpreferences.kcfgc
@@ -0,0 +1,8 @@
+# Code generation options for kconfig_compiler
+File=kopeteidentityconfigpreferences.kcfg
+ClassName=KopeteIdentityConfigPreferences
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
+IncludeFiles=klocale.h
diff --git a/kopete/kopete/config/plugins/Makefile.am b/kopete/kopete/config/plugins/Makefile.am
new file mode 100644
index 00000000..3ed239a1
--- /dev/null
+++ b/kopete/kopete/config/plugins/Makefile.am
@@ -0,0 +1,11 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_LTLIBRARIES = libkopetepluginconfig.la
+
+libkopetepluginconfig_la_SOURCES = kopetepluginconfig.cpp
+libkopetepluginconfig_la_LDFLAGS = $(all_libraries)
+libkopetepluginconfig_la_LIBADD = $(LIB_KUTILS)
+
+# vim: set noet:
+
diff --git a/kopete/kopete/config/plugins/kopetepluginconfig.cpp b/kopete/kopete/config/plugins/kopetepluginconfig.cpp
new file mode 100644
index 00000000..9949f70e
--- /dev/null
+++ b/kopete/kopete/config/plugins/kopetepluginconfig.cpp
@@ -0,0 +1,120 @@
+/*
+ kopetepluginconfig.cpp - Configure the Kopete plugins
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetepluginconfig.h"
+
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpluginselector.h>
+#include <ksettings/dispatcher.h>
+
+#include "kopetepluginmanager.h"
+
+class KopetePluginConfigPrivate
+{
+public:
+ KPluginSelector *pluginSelector;
+ bool isChanged;
+};
+
+KopetePluginConfig::~KopetePluginConfig()
+{
+ delete d;
+}
+
+KopetePluginConfig::KopetePluginConfig( QWidget *parent, const char *name )
+: KDialogBase( Plain, i18n( "Configure Plugins" ), /*Help |*/ Cancel | Apply | Ok | User1,
+ Ok, parent, name, false, true, KGuiItem( i18n( "&Reset" ), "undo" ) )
+{
+ d = new KopetePluginConfigPrivate;
+ showButton( User1, false );
+ setChanged( false );
+
+ // FIXME: Implement this - Martijn
+ enableButton( KDialogBase::Help, false );
+
+ setInitialSize( QSize( 640, 480 ) );
+
+ ( new QVBoxLayout( plainPage(), 0, 0 ) )->setAutoAdd( true );
+ d->pluginSelector = new KPluginSelector( plainPage() );
+ setMainWidget( d->pluginSelector );
+ connect( d->pluginSelector, SIGNAL( changed( bool ) ), this, SLOT( setChanged( bool ) ) );
+ connect( d->pluginSelector, SIGNAL( configCommitted( const QCString & ) ),
+ KSettings::Dispatcher::self(), SLOT( reparseConfiguration( const QCString & ) ) );
+
+ d->pluginSelector->addPlugins( Kopete::PluginManager::self()->availablePlugins( "Plugins" ), i18n( "General Plugins" ), "Plugins" );
+}
+
+void KopetePluginConfig::setChanged( bool c )
+{
+ d->isChanged = c;
+ enableButton( Apply, c );
+}
+
+void KopetePluginConfig::slotDefault()
+{
+ d->pluginSelector->defaults();
+ setChanged( false );
+}
+
+void KopetePluginConfig::slotUser1()
+{
+ d->pluginSelector->load();
+ setChanged( false );
+}
+
+void KopetePluginConfig::apply()
+{
+ if( d->isChanged )
+ {
+ d->pluginSelector->save();
+ Kopete::PluginManager::self()->loadAllPlugins();
+ setChanged( false );
+ }
+}
+
+void KopetePluginConfig::slotApply()
+{
+ apply();
+}
+
+void KopetePluginConfig::slotOk()
+{
+ emit okClicked();
+ apply();
+ accept();
+}
+
+void KopetePluginConfig::slotHelp()
+{
+ kdWarning() << k_funcinfo << "FIXME: Implement!" << endl;
+}
+
+void KopetePluginConfig::show()
+{
+ d->pluginSelector->load();
+
+ KDialogBase::show();
+}
+
+#include "kopetepluginconfig.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/config/plugins/kopetepluginconfig.h b/kopete/kopete/config/plugins/kopetepluginconfig.h
new file mode 100644
index 00000000..e8853a1c
--- /dev/null
+++ b/kopete/kopete/config/plugins/kopetepluginconfig.h
@@ -0,0 +1,56 @@
+/*
+ kopetepluginconfig.h - Configure the Kopete plugins
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPLUGINCONFIG_H
+#define KOPETEPLUGINCONFIG_H
+
+#include <kdialogbase.h>
+
+class KopetePluginConfigPrivate;
+
+/**
+ * Plugin selector. See KPluginSelector in kdelibs for documentation.
+ *
+ * @author Martijn Klingens <klingens@kde.org>
+ */
+class KopetePluginConfig : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ KopetePluginConfig( QWidget *parent, const char *name = 0L );
+ ~KopetePluginConfig();
+ void apply();
+
+public slots:
+ void setChanged( bool c );
+
+ virtual void slotDefault();
+ virtual void slotUser1();
+ virtual void slotApply();
+ virtual void slotOk();
+ virtual void slotHelp();
+ virtual void show();
+
+private:
+ KopetePluginConfigPrivate *d;
+};
+
+#endif // KOPETEPLUGINCONFIG_H
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/contactlist/Makefile.am b/kopete/kopete/contactlist/Makefile.am
new file mode 100644
index 00000000..d9544d5d
--- /dev/null
+++ b/kopete/kopete/contactlist/Makefile.am
@@ -0,0 +1,28 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(top_srcdir)/kopete/libkopete/private \
+ -I$(top_srcdir)/kopete/libkopete/ui \
+ -I$(top_srcdir)/kopete/kopete \
+ -I$(top_srcdir)/kopete/kopete/chatwindow \
+ -I$(top_srcdir)/kopete/kopete/addcontactwizard \
+ -I$(top_builddir)/kopete/kopete/addcontactwizard \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopetecontactlist.la
+
+libkopetecontactlist_la_SOURCES = kopetemetacontactlvi.cpp \
+ kopetestatusgroupviewitem.cpp kopetegroupviewitem.cpp kopetecontactlistview.cpp \
+ kopetegvipropswidget.ui kopetemetalvipropswidget.ui kopetelviprops.cpp \
+ kopeteaddrbookexport.cpp kopeteaddrbookexportui.ui customnotifications.ui \
+ customnotificationprops.cpp kopetegrouplistaction.cpp kabcexport.cpp \
+ kabcexport_base.ui
+
+libkopetecontactlist_la_LDFLAGS = $(all_libraries)
+libkopetecontactlist_la_LIBADD = -lkabc ../../libkopete/libkopete.la ../addcontactwizard/libkopeteaddcontactwizard.la $(LIB_KDEUI) $(LIB_XRENDER)
+
+noinst_HEADERS = kopeteaddrbookexport.h customnotificationprops.h kabcexport.h
+
+KDE_OPTIONS = nofinal
+
+# vim: set noet:
diff --git a/kopete/kopete/contactlist/configure.in.in b/kopete/kopete/contactlist/configure.in.in
new file mode 100644
index 00000000..900224ea
--- /dev/null
+++ b/kopete/kopete/contactlist/configure.in.in
@@ -0,0 +1,15 @@
+
+dnl -----------------------------------------------------
+dnl XRender check - stolen from kdelibs/kdefx
+dnl -----------------------------------------------------
+LIB_XRENDER=
+if test "$kde_use_qt_emb" = "no" && test "$kde_use_qt_mac" = "no"; then
+ KDE_CHECK_HEADER(X11/extensions/Xrender.h, [xrender_h=yes], [xrender_h=no])
+ if test "$xrender_h" = yes; then
+ KDE_CHECK_LIB(Xrender, XRenderComposite, [
+ LIB_XRENDER=-lXrender
+ AC_DEFINE_UNQUOTED(HAVE_XRENDER, 1, [Defined if your system has XRender support])
+ ], [], -lXext -lX11 $X_EXTRA_LIBS)
+ fi
+fi
+AC_SUBST(LIB_XRENDER)
diff --git a/kopete/kopete/contactlist/customnotificationprops.cpp b/kopete/kopete/contactlist/customnotificationprops.cpp
new file mode 100644
index 00000000..87833fa7
--- /dev/null
+++ b/kopete/kopete/contactlist/customnotificationprops.cpp
@@ -0,0 +1,168 @@
+/*
+ customnotificationprops.cpp
+
+ Kopete Contactlist Custom Notifications GUI for Groups and MetaContacts
+
+ Contains UI controller logic for managing custom notifications
+
+ Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kurlrequester.h>
+
+#include "customnotifications.h"
+#include "kopeteeventpresentation.h"
+#include "kopetenotifyevent.h"
+#include "kopetenotifydataobject.h"
+
+#include "customnotificationprops.h"
+
+CustomNotificationProps::CustomNotificationProps( QWidget *parent, Kopete::NotifyDataObject* item, const char * name )
+: QObject( parent, name )
+{
+ m_notifyWidget = new CustomNotificationWidget( parent, "notificationWidget" );
+
+ m_item = item;
+ QString path = "kopete/eventsrc";
+ KConfig eventsfile( path, true, false, "data" );
+ m_eventList = eventsfile.groupList();
+ QStringList contactSpecificEvents; // we are only interested in events that relate to contacts
+ QStringList::Iterator it = m_eventList.begin();
+ QStringList::Iterator end = m_eventList.end();
+ for ( ; it != end; ++it )
+ {
+ if ( !(*it).startsWith( QString::fromLatin1( "kopete_contact_" ) ) )
+ continue;
+ contactSpecificEvents.append( *it );
+ QMap<QString, QString> entries = eventsfile.entryMap( *it );
+ eventsfile.setGroup( *it );
+ QString comment = eventsfile.readEntry( "Comment", QString::fromLatin1( "Found nothing!" ) );
+ m_notifyWidget->cmbEvents->insertItem( comment );
+ }
+ m_eventList = contactSpecificEvents;
+ slotEventsComboChanged( m_notifyWidget->cmbEvents->currentItem() );
+ // we have to do this after adding items
+ connect( m_notifyWidget->cmbEvents, SIGNAL( activated( int ) ), this, SLOT( slotEventsComboChanged( int ) ) );
+}
+
+void CustomNotificationProps::slotEventsComboChanged( int itemNo )
+{
+ // if the combo has changed, store the previous state of the widgets
+ // record the selected item so we can save it when the widget changes next
+ storeCurrentCustoms();
+ m_event = m_eventList[ itemNo ];
+ // update the widgets for the selected item
+ // get the corresponding Kopete::NotifyEvent
+ Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event );
+ // set the widgets accordingly
+ resetEventWidgets();
+ if ( evt )
+ {
+ // sound presentation
+ Kopete::EventPresentation *pres = evt->presentation( Kopete::EventPresentation::Sound );
+ if ( pres )
+ {
+ m_notifyWidget->chkCustomSound->setChecked( pres->enabled() );
+ m_notifyWidget->customSound->setURL( pres->content() );
+ m_notifyWidget->chkSoundSS->setChecked( pres->singleShot() );
+ }
+ // message presentation
+ pres = evt->presentation( Kopete::EventPresentation::Message );
+ if ( pres )
+ {
+ m_notifyWidget->chkCustomMsg->setChecked( pres->enabled() );
+ m_notifyWidget->customMsg->setText( pres->content() );
+ m_notifyWidget->chkMsgSS->setChecked( pres->singleShot() );
+ }
+ // chat presentation
+ pres = evt->presentation( Kopete::EventPresentation::Chat );
+ if ( pres )
+ {
+ m_notifyWidget->chkCustomChat->setChecked( pres->enabled() );
+ m_notifyWidget->chkChatSS->setChecked( pres->singleShot() );
+ }
+ m_notifyWidget->chkSuppressCommon->setChecked( evt->suppressCommon() );
+ }
+ //dumpData();
+}
+
+
+void CustomNotificationProps::dumpData()
+{
+ Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event );
+ if ( evt )
+ kdDebug( 14000 ) << k_funcinfo << evt->toString() << endl;
+ else
+ kdDebug( 14000 ) << k_funcinfo << " no event exists." << endl;
+}
+
+void CustomNotificationProps::resetEventWidgets()
+{
+ m_notifyWidget->chkCustomSound->setChecked( false );
+ m_notifyWidget->customSound->clear();
+ m_notifyWidget->chkSoundSS->setChecked( true );
+ m_notifyWidget->chkCustomMsg->setChecked( false );
+ m_notifyWidget->customMsg->clear();
+ m_notifyWidget->chkMsgSS->setChecked( true );
+ m_notifyWidget->chkCustomChat->setChecked( false );
+ m_notifyWidget->chkChatSS->setChecked( true );
+ m_notifyWidget->chkSuppressCommon->setChecked( false );
+}
+
+void CustomNotificationProps::storeCurrentCustoms()
+{
+ if ( !m_event.isNull() )
+ {
+ Kopete::NotifyEvent *evt = m_item->notifyEvent( m_event );
+ if ( !evt )
+ {
+ evt = new Kopete::NotifyEvent( );
+ // store the changed event
+ m_item->setNotifyEvent( m_event, evt );
+ }
+ evt->setSuppressCommon( m_notifyWidget->chkSuppressCommon->isChecked() );
+ // set different presentations
+ Kopete::EventPresentation *eventNotify = 0;
+ eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Sound,
+ m_notifyWidget->customSound->url(),
+ m_notifyWidget->chkSoundSS->isChecked(),
+ m_notifyWidget->chkCustomSound->isChecked() );
+ evt->setPresentation( Kopete::EventPresentation::Sound, eventNotify );
+ // set message attributes
+ eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Message,
+ m_notifyWidget->customMsg->text(),
+ m_notifyWidget->chkMsgSS->isChecked(),
+ m_notifyWidget->chkCustomMsg->isChecked() );
+ evt->setPresentation( Kopete::EventPresentation::Message, eventNotify );
+ // set chat attributes
+ eventNotify = new Kopete::EventPresentation( Kopete::EventPresentation::Chat,
+ QString::null,
+ m_notifyWidget->chkChatSS->isChecked(),
+ m_notifyWidget->chkCustomChat->isChecked() );
+ evt->setPresentation( Kopete::EventPresentation::Chat, eventNotify );
+ evt->setSuppressCommon( m_notifyWidget->chkSuppressCommon->isChecked() );
+ }
+}
+
+CustomNotificationWidget* CustomNotificationProps::widget()
+{
+ return m_notifyWidget;
+}
+
+#include "customnotificationprops.moc"
diff --git a/kopete/kopete/contactlist/customnotificationprops.h b/kopete/kopete/contactlist/customnotificationprops.h
new file mode 100644
index 00000000..5d6c1dea
--- /dev/null
+++ b/kopete/kopete/contactlist/customnotificationprops.h
@@ -0,0 +1,51 @@
+/*
+ customnotificationprops.h
+
+ Kopete Contactlist Custom Notifications GUI for Groups and MetaContacts
+
+ Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_CUSTOM_NOTIFICATION_PROPS_H
+#define KOPETE_CUSTOM_NOTIFICATION_PROPS_H
+
+class CustomNotificationWidget;
+class QBoxLayout;
+
+namespace Kopete
+{
+class NotifyDataObject;
+}
+
+class CustomNotificationProps : public QObject
+{
+ Q_OBJECT
+public:
+ CustomNotificationProps( QWidget *parent, Kopete::NotifyDataObject* item, const char * name = 0 );
+ ~CustomNotificationProps() {}
+ void dumpData();
+ void resetEventWidgets();
+ void storeCurrentCustoms();
+ CustomNotificationWidget* widget();
+
+protected slots:
+ void slotEventsComboChanged( int itemNo );
+
+private:
+ CustomNotificationWidget* m_notifyWidget;
+ Kopete::NotifyDataObject * m_item;
+ QStringList m_eventList;
+ QString m_event;
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/customnotifications.ui b/kopete/kopete/contactlist/customnotifications.ui
new file mode 100644
index 00000000..86224af2
--- /dev/null
+++ b/kopete/kopete/contactlist/customnotifications.ui
@@ -0,0 +1,238 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>CustomNotificationWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CustomNotificationWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>389</width>
+ <height>220</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>On &amp;event:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbEvents</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>cmbEvents</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Choose the event that should have a custom notification</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>customSound</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select the sound to play</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>chkCustomSound</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Play a sound:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Play a sound when this event occurs for this contact</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>chkCustomChat</cstring>
+ </property>
+ <property name="text">
+ <string>Start a cha&amp;t</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Open a chat window with this contact when this event occurs for this contact</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>chkCustomMsg</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Display a message:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Display a message on your screen when this event occurs for this contact</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>customMsg</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter the message to display</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="2">
+ <property name="name">
+ <cstring>chkMsgSS</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>D&amp;isplay once</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only display a message the next time the event occurs</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="2">
+ <property name="name">
+ <cstring>chkSoundSS</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>P&amp;lay once</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only play a sound the next time the event occurs</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="2">
+ <property name="name">
+ <cstring>chkChatSS</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>T&amp;rigger once</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only start a chat the next time the event occurs</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkSuppressCommon</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;uppress standard notifications</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check to prevent notifications common to all contacts from happening for this contact</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>chkCustomSound</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>customSound</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomSound</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>chkSoundSS</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomMsg</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>customMsg</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomMsg</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>chkMsgSS</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomChat</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>chkChatSS</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>cmbEvents</tabstop>
+ <tabstop>chkCustomSound</tabstop>
+ <tabstop>customSound</tabstop>
+ <tabstop>chkSoundSS</tabstop>
+ <tabstop>chkCustomMsg</tabstop>
+ <tabstop>customMsg</tabstop>
+ <tabstop>chkMsgSS</tabstop>
+ <tabstop>chkCustomChat</tabstop>
+ <tabstop>chkChatSS</tabstop>
+ <tabstop>chkSuppressCommon</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/contactlist/kabcexport.cpp b/kopete/kopete/contactlist/kabcexport.cpp
new file mode 100644
index 00000000..73f67344
--- /dev/null
+++ b/kopete/kopete/contactlist/kabcexport.cpp
@@ -0,0 +1,249 @@
+/*
+ kabcexport.cpp - Export Contacts to Address Book Wizard for Kopete
+
+ Copyright (c) 2005 by Will Stephenson <will@stevello.free-online.co.uk>
+ Resource selector taken from KRES::SelectDialog
+ Copyright (c) 2002 Tobias Koenig <tokoe@kde.org>
+ Copyright (c) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+ Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qmap.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kabc/addressee.h>
+#include <kabc/addressbook.h>
+#include <kabc/phonenumber.h>
+#include <kabc/picture.h>
+#include <kabc/resource.h>
+#include <kabc/stdaddressbook.h>
+
+#include <kabcpersistence.h>
+#include <kopetecontact.h>
+#include <kopetecontactlist.h>
+#include <kopetecontactproperty.h>
+#include <kopeteglobal.h>
+#include <kopetemetacontact.h>
+
+#include "kabcpersistence.h"
+
+#include "kabcexport.h"
+
+class ContactLVI : public QCheckListItem
+{
+ public:
+ ContactLVI ( Kopete::MetaContact * mc, QListView * parent, const QString & text, Type tt = RadioButtonController ) : QCheckListItem( parent, text, tt ), mc( mc )
+ { }
+ Kopete::MetaContact * mc;
+ QString uid;
+};
+
+// ctor populates the resource list and contact list, and enables the next button on the first page
+KabcExportWizard::KabcExportWizard( QWidget *parent, const char *name )
+ : KabcExportWizard_Base( parent, name )
+{
+ connect( m_addrBooks, SIGNAL( selectionChanged( QListBoxItem * ) ), SLOT( slotResourceSelectionChanged( QListBoxItem * ) ) );
+
+ connect( m_btnSelectAll, SIGNAL( clicked() ), SLOT( slotSelectAll() ) );
+ connect( m_btnDeselectAll, SIGNAL( clicked() ), SLOT( slotDeselectAll() ) );
+
+ // fill resource selector
+ m_addressBook = Kopete::KABCPersistence::self()->addressBook();
+
+ QPtrList<KABC::Resource> kabcResources = m_addressBook->resources();
+
+ QPtrListIterator<KABC::Resource> resIt( kabcResources );
+ KABC::Resource *resource;
+
+ uint counter = 0;
+ while ( ( resource = resIt.current() ) != 0 )
+ {
+ ++resIt;
+ if ( !resource->readOnly() )
+ {
+ m_resourceMap.insert( counter, resource );
+ m_addrBooks->insertItem( resource->resourceName() );
+ counter++;
+ }
+ }
+ setNextEnabled( QWizard::page( 0 ), false );
+ setFinishEnabled( QWizard::page( 1 ), true );
+ // if there were no writable address books, tell the user
+ if ( counter == 0 )
+ {
+ m_addrBooks->insertItem( i18n( "No writeable addressbook resource found." ) );
+ m_addrBooks->insertItem( i18n( "Add or enable one using the KDE Control Center." ) );
+ m_addrBooks->setEnabled( false );
+ }
+
+ if ( m_addrBooks->count() == 1 )
+ m_addrBooks->setSelected( 0, true );
+
+ // fill contact list
+ QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( contacts );
+ counter = 0;
+ QString alreadyIn = i18n( " (already in address book)" );
+ for (; it.current(); ++it)
+ {
+ m_contactMap.insert( counter, it.current() );
+ QCheckListItem * lvi = new ContactLVI( it.current(), m_contactList,
+ it.current()->displayName(), QCheckListItem::CheckBox );
+ lvi->setOn( false );
+ if ( it.current()->metaContactId().contains(':') )
+ {
+ lvi->setOn( true );
+ lvi->setEnabled( true );
+ }
+ else
+ lvi->setText( 0, lvi->text( 0 ) + alreadyIn );
+ }
+}
+
+KabcExportWizard::~KabcExportWizard()
+{
+
+}
+
+void KabcExportWizard::slotDeselectAll()
+{
+ QListViewItemIterator it( m_contactList );
+ while ( it.current() )
+ {
+ ContactLVI *item = static_cast<ContactLVI *>( it.current() );
+ item->setOn( false );
+ ++it;
+ }
+}
+
+void KabcExportWizard::slotSelectAll()
+{
+ QListViewItemIterator it( m_contactList );
+ while ( it.current() )
+ {
+ ContactLVI *item = static_cast<ContactLVI *>( it.current() );
+ ++it;
+ if ( !item->isEnabled() )
+ continue;
+ item->setOn( true );
+ }
+}
+
+void KabcExportWizard::slotResourceSelectionChanged( QListBoxItem * lbi )
+{
+ setNextEnabled( QWizard::page( 0 ), lbi->isSelected() );
+}
+
+// accept runs the export algorithm
+void KabcExportWizard::accept()
+{
+ // first add an addressee to the selected resource
+ // then set the metacontactId of each MC to that of the new addressee
+ KABC::Resource * selectedResource =
+ m_resourceMap[ ( m_addrBooks->index( m_addrBooks->selectedItem() ) ) ];
+ // for each item checked
+ {
+ QListViewItemIterator it( m_contactList );
+ while ( it.current() )
+ {
+ ContactLVI *item = static_cast<ContactLVI *>( it.current() );
+ // if it is checked and enabled
+ if ( item->isEnabled() && item->isOn() )
+ {
+ KABC::Addressee addr;
+ addr = m_addressBook->findByUid( item->mc->metaContactId() );
+ if ( addr.isEmpty() ) // unassociated contact
+ {
+ kdDebug( 14000 ) << "creating addressee " << item->mc->displayName() << " in address book " << selectedResource->resourceName() << endl;
+ // create a new addressee in the selected resource
+ addr.setResource( selectedResource );
+
+ // set name
+ QPtrList<Kopete::Contact> contacts = item->mc->contacts();
+ if ( contacts.count() == 1 )
+ {
+ Kopete::ContactProperty prop;
+ prop = contacts.first()->property(
+ Kopete::Global::Properties::self()->fullName() );
+ if ( prop.isNull() )
+ addr.setNameFromString( item->mc->displayName() );
+ else
+ addr.setNameFromString( prop.value().toString() );
+ }
+ else
+ addr.setNameFromString( item->mc->displayName() );
+
+ // set details
+ exportDetails( item->mc, addr );
+ m_addressBook->insertAddressee( addr );
+ // set the metacontact's id to that of the new addressee
+ // - this causes the addressbook to be written by libkopete
+ item->mc->setMetaContactId( addr.uid() );
+ }
+ else
+ {
+ exportDetails( item->mc, addr );
+ m_addressBook->insertAddressee( addr );
+ }
+ }
+ ++it;
+ }
+ }
+ // request a write in case we only changed details on existing linked addressee
+ Kopete::KABCPersistence::self()->writeAddressBook( selectedResource );
+ QDialog::accept();
+}
+
+void KabcExportWizard::exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr )
+{
+ // for each contact
+ QPtrList<Kopete::Contact> contacts = mc->contacts();
+ QPtrListIterator<Kopete::Contact> cit( contacts );
+ for( ; cit.current(); ++cit )
+ {
+ Kopete::ContactProperty prop;
+ prop = (*cit)->property( Kopete::Global::Properties::self()->emailAddress() );
+ if ( !prop.isNull() )
+ {
+ addr.insertEmail( prop.value().toString() );
+ }
+ prop = (*cit)->property( Kopete::Global::Properties::self()->privatePhone() );
+ if ( !prop.isNull() )
+ {
+ addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Home ) );
+ }
+ prop = (*cit)->property( Kopete::Global::Properties::self()->workPhone() );
+ if ( !prop.isNull() )
+ {
+ addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Work ) );
+ }
+ prop = (*cit)->property( Kopete::Global::Properties::self()->privateMobilePhone() );
+ if ( !prop.isNull() )
+ {
+ addr.insertPhoneNumber( KABC::PhoneNumber( prop.value().toString(), KABC::PhoneNumber::Cell ) );
+ }
+
+ }
+ // metacontact photo
+ QImage photo = mc->photo();
+ if ( !photo.isNull() )
+ addr.setPhoto( KABC::Picture( photo ) );
+}
+
+#include "kabcexport.moc"
diff --git a/kopete/kopete/contactlist/kabcexport.h b/kopete/kopete/contactlist/kabcexport.h
new file mode 100644
index 00000000..bad1d8e6
--- /dev/null
+++ b/kopete/kopete/contactlist/kabcexport.h
@@ -0,0 +1,56 @@
+/*
+ kabcexport.h - Export Contacts to Address Book Wizard for Kopete
+
+ Copyright (c) 2005 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KABCEXPORTWIZARD_H
+#define KABCEXPORTWIZARD_H
+
+#include "kabcexport_base.h"
+
+namespace KABC {
+ class AddressBook;
+ class Addressee;
+}
+
+namespace Kopete {
+ class MetaContact;
+}
+
+namespace KRES {
+ class Resource;
+}
+
+class KabcExportWizard : public KabcExportWizard_Base
+{
+Q_OBJECT
+ public:
+ KabcExportWizard( QWidget *parent = 0, const char *name = 0 );
+ ~KabcExportWizard();
+ public slots:
+ void accept();
+ protected slots:
+ void slotDeselectAll();
+ void slotSelectAll();
+ void slotResourceSelectionChanged( QListBoxItem * lbi );
+ protected:
+ void exportDetails( Kopete::MetaContact * mc, KABC::Addressee & addr );
+ private:
+ KABC::AddressBook* m_addressBook;
+ QMap<int, KABC::Resource*> m_resourceMap;
+ QMap<int, Kopete::MetaContact*> m_contactMap;
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/kabcexport_base.ui b/kopete/kopete/contactlist/kabcexport_base.ui
new file mode 100644
index 00000000..80ace5c6
--- /dev/null
+++ b/kopete/kopete/contactlist/kabcexport_base.ui
@@ -0,0 +1,183 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KabcExportWizard_Base</class>
+<widget class="QWizard">
+ <property name="name">
+ <cstring>KabcExportWizard_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>423</width>
+ <height>398</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Export Contacts</string>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="title">
+ <string>Export Contacts to Address Book</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>This wizard helps you export instant messaging contacts to the KDE address book.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Select Address Book</string>
+ </property>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>m_addrBooks</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>11</x>
+ <y>31</y>
+ <width>230</width>
+ <height>140</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>WizardPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Select Contacts to Export</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Selected contacts will be added to the KDE address book.</string>
+ </property>
+ </widget>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Contact</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_contactList</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnSelectAll</cstring>
+ </property>
+ <property name="text">
+ <string>Select &amp;All</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_btnDeselectAll</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Deselect All</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/contactlist/kopeteaddrbookexport.cpp b/kopete/kopete/contactlist/kopeteaddrbookexport.cpp
new file mode 100644
index 00000000..d752f71e
--- /dev/null
+++ b/kopete/kopete/contactlist/kopeteaddrbookexport.cpp
@@ -0,0 +1,298 @@
+/*
+ kopeteaddrbookexport.cpp - Kopete Online Status
+
+ Logic for exporting data acquired from messaging systems to the
+ KDE address book
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kabc/phonenumber.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+
+#include <kdialogbase.h>
+#include <kiconloader.h>
+#include <klistbox.h>
+#include <klocale.h>
+
+#include "kopeteaccount.h"
+#include "kopeteglobal.h"
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+
+#include "kopeteaddrbookexport.h"
+#include "kopeteaddrbookexportui.h"
+
+KopeteAddressBookExport::KopeteAddressBookExport( QWidget *parent, Kopete::MetaContact *mc ) : QObject( parent )
+{
+ // instantiate dialog and populate widgets
+ mParent = parent;
+ mAddressBook = KABC::StdAddressBook::self();
+ mMetaContact = mc;
+}
+
+KopeteAddressBookExport::~KopeteAddressBookExport()
+{
+
+}
+
+void KopeteAddressBookExport::initLabels()
+{
+ if ( !mAddressee.isEmpty() )
+ {
+ mUI->mLblFirstName->setText( mAddressee.givenNameLabel() );
+ mUI->mLblLastName->setText( mAddressee.familyNameLabel() );
+ mUI->mLblEmail->setText( mAddressee.emailLabel() );
+ mUI->mLblUrl->setText( mAddressee.urlLabel() );
+ mUI->mLblHomePhone->setText( mAddressee.homePhoneLabel() );
+ mUI->mLblWorkPhone->setText( mAddressee.businessPhoneLabel() );
+ mUI->mLblMobilePhone->setText( mAddressee.mobilePhoneLabel() );
+ }
+}
+
+void KopeteAddressBookExport::fetchKABCData()
+{
+ if ( !mAddressee.isEmpty() )
+ {
+ mAddrBookIcon = SmallIcon( "kaddressbook" );
+
+ // given name
+ QString given = mAddressee.givenName();
+ if ( !given.isEmpty() )
+ mUI->mFirstName->insertItem( mAddrBookIcon, given );
+ else
+ mUI->mFirstName->insertItem( mAddrBookIcon, i18n("<Not Set>") );
+
+ // family name
+ QString family = mAddressee.familyName();
+ if ( !family.isEmpty() )
+ mUI->mLastName->insertItem( mAddrBookIcon, family );
+ else
+ mUI->mLastName->insertItem( mAddrBookIcon, i18n("<Not Set>") );
+
+ // url
+ QString url = mAddressee.url().url();
+ if ( !url.isEmpty() )
+ mUI->mUrl->insertItem( mAddrBookIcon, url );
+ else
+ mUI->mUrl->insertItem( mAddrBookIcon, i18n("<Not Set>") );
+
+ // emails
+ QStringList emails = mAddressee.emails();
+ numEmails = emails.count();
+ for ( QStringList::Iterator it = emails.begin(); it != emails.end(); ++it )
+ mUI->mEmails->insertItem( mAddrBookIcon, *it );
+ if ( numEmails == 0 )
+ {
+ mUI->mEmails->insertItem( mAddrBookIcon, i18n("<Not Set>") );
+ numEmails = 1;
+ }
+
+ // phone numbers
+ fetchPhoneNumbers( mUI->mHomePhones, KABC::PhoneNumber::Home, numHomePhones );
+ fetchPhoneNumbers( mUI->mWorkPhones, KABC::PhoneNumber::Work, numWorkPhones );
+ fetchPhoneNumbers( mUI->mMobilePhones, KABC::PhoneNumber::Cell, numMobilePhones );
+ }
+}
+
+void KopeteAddressBookExport::fetchPhoneNumbers( KListBox * listBox, int type, uint& counter )
+{
+ KABC::PhoneNumber::List phones = mAddressee.phoneNumbers( type );
+ counter = phones.count();
+ KABC::PhoneNumber::List::Iterator it;
+ for ( it = phones.begin(); it != phones.end(); ++it )
+ listBox->insertItem( mAddrBookIcon, (*it).number() );
+ if ( counter == 0 )
+ {
+ listBox->insertItem( mAddrBookIcon, i18n("<Not Set>") );
+ counter = 1;
+ }
+}
+
+void KopeteAddressBookExport::fetchIMData()
+{
+ QPtrList<Kopete::Contact> contacts = mMetaContact->contacts();
+ QPtrListIterator<Kopete::Contact> cit( contacts );
+ for( ; cit.current(); ++cit )
+ {
+ // for each contact, get the property content
+ Kopete::Contact* c = cit.current();
+ QPixmap contactIcon = c->account()->accountIcon( 16 );
+ // given name
+ populateIM( c, contactIcon, mUI->mFirstName, Kopete::Global::Properties::self()->firstName() );
+ // family name
+ populateIM( c, contactIcon, mUI->mLastName, Kopete::Global::Properties::self()->lastName() );
+ // url
+ // TODO: make URL/homepage a global template, currently only in IRC channel contact
+ // emails
+ populateIM( c, contactIcon, mUI->mEmails, Kopete::Global::Properties::self()->emailAddress() );
+ // home phone
+ populateIM( c, contactIcon, mUI->mHomePhones, Kopete::Global::Properties::self()->privatePhone() );
+ // work phone
+ populateIM( c, contactIcon, mUI->mWorkPhones, Kopete::Global::Properties::self()->workPhone() );
+ // mobile phone
+ populateIM( c, contactIcon, mUI->mMobilePhones, Kopete::Global::Properties::self()->privateMobilePhone() );
+ }
+}
+
+void KopeteAddressBookExport::populateIM( const Kopete::Contact *contact, const QPixmap &icon, QComboBox *combo, const Kopete::ContactPropertyTmpl &property )
+{
+ Kopete::ContactProperty prop = contact->property( property );
+ if ( !prop.isNull() )
+ {
+ combo->insertItem( icon, prop.value().toString() );
+ }
+}
+
+void KopeteAddressBookExport::populateIM( const Kopete::Contact *contact, const QPixmap &icon, KListBox *listBox, const Kopete::ContactPropertyTmpl &property )
+{
+ Kopete::ContactProperty prop = contact->property( property );
+ if ( !prop.isNull() )
+ {
+ listBox->insertItem( icon, prop.value().toString() );
+ }
+}
+
+int KopeteAddressBookExport::showDialog()
+{
+ mAddressee = mAddressBook->findByUid( mMetaContact->metaContactId() );
+ if ( !mAddressee.isEmpty() )
+ {
+ numEmails = 0;
+ numHomePhones = 0;
+ numWorkPhones = 0;
+ numMobilePhones = 0;
+ mDialog = new KDialogBase( mParent, "addressbookexportdialog", true, i18n("Export to Address Book"), KDialogBase::Ok|KDialogBase::Cancel );
+ mUI = new AddressBookExportUI( mDialog );
+ mDialog->setMainWidget( mUI );
+ mDialog->setButtonOK( KGuiItem( i18n( "Export" ),
+ QString::null, i18n( "Set address book fields using the selected data from Kopete" ) ) );
+
+ initLabels();
+ // fetch existing data from kabc
+ fetchKABCData();
+ // fetch data from contacts
+ fetchIMData();
+
+ return mDialog->exec();
+ }
+ else
+ return QDialog::Rejected;
+}
+
+void KopeteAddressBookExport::exportData()
+{
+ // write the data from the widget to KABC
+ // update the Addressee
+ // first name
+ bool dirty = false;
+ if ( newValue( mUI->mFirstName ) )
+ {
+ dirty = true;
+ mAddressee.setGivenName( mUI->mFirstName->currentText() );
+ }
+ // last name
+ if ( newValue( mUI->mLastName ) )
+ {
+ dirty = true;
+ mAddressee.setFamilyName( mUI->mLastName->currentText() );
+ }
+ // url
+ if ( newValue( mUI->mUrl ) )
+ {
+ dirty = true;
+ mAddressee.setUrl( KURL( mUI->mUrl->currentText() ) );
+ }
+
+ QStringList newVals;
+ // email
+ newVals = newValues( mUI->mEmails, numEmails );
+ for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it )
+ {
+ dirty = true;
+ mAddressee.insertEmail( *it );
+ }
+ // home phone
+ newVals = newValues( mUI->mHomePhones, numHomePhones );
+ for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it )
+ {
+ dirty = true;
+ mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Home ) );
+ }
+ // work phone
+ newVals = newValues( mUI->mWorkPhones, numWorkPhones );
+ for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it )
+ {
+ dirty = true;
+ mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Work ) );
+ }
+ // mobile
+ newVals = newValues( mUI->mMobilePhones, numMobilePhones );
+ for ( QStringList::Iterator it = newVals.begin(); it != newVals.end(); ++it )
+ {
+ dirty = true;
+ mAddressee.insertPhoneNumber( KABC::PhoneNumber( *it, KABC::PhoneNumber::Cell ) );
+ }
+
+ if ( dirty )
+ {
+ // write the changed addressbook
+ mAddressBook->insertAddressee( mAddressee );
+
+ KABC::Ticket *ticket = mAddressBook->requestSaveTicket();
+ if ( !ticket )
+ kdWarning( 14000 ) << k_funcinfo << "WARNING: Resource is locked by other application!" << endl;
+ else
+ {
+ if ( !mAddressBook->save( ticket ) )
+ {
+ kdWarning( 14000 ) << k_funcinfo << "ERROR: Saving failed!" << endl;
+ mAddressBook->releaseSaveTicket( ticket );
+ }
+ }
+ kdDebug( 14000 ) << k_funcinfo << "Finished writing KABC" << endl;
+ }
+}
+
+bool KopeteAddressBookExport::newValue( QComboBox *combo )
+{
+ // all data in position 0 is from KABC, so if position 0 is selected,
+ // or if the selection is the same as the data at 0, return false
+ return !( combo->currentItem() == 0 ||
+ ( combo->text( combo->currentItem() ) == combo->text( 0 ) ) );
+}
+
+QStringList KopeteAddressBookExport::newValues( KListBox *listBox, uint counter )
+{
+ QStringList newValues;
+ // need to iterate all items except those from KABC and check if selected and not same as the first
+ // counter is the number of KABC items, and hence the index of the first non KABC item
+ for ( uint i = counter; i < listBox->count(); ++i )
+ {
+ if ( listBox->isSelected( i ) )
+ {
+ // check whether it matches any KABC item
+ bool duplicate = false;
+ for ( uint j = 0; j < counter; ++j )
+ {
+ if ( listBox->text( i ) == listBox->text( j ) )
+ duplicate = true;
+ }
+ if ( !duplicate )
+ newValues.append( listBox->text( i ) );
+ }
+ }
+ return newValues;
+}
diff --git a/kopete/kopete/contactlist/kopeteaddrbookexport.h b/kopete/kopete/contactlist/kopeteaddrbookexport.h
new file mode 100644
index 00000000..b4437c4e
--- /dev/null
+++ b/kopete/kopete/contactlist/kopeteaddrbookexport.h
@@ -0,0 +1,102 @@
+/*
+ kopeteaddrbookexport.h - Kopete Online Status
+
+ Logic for exporting data acquired from messaging systems to the
+ KDE address book
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEADDRBOOKEXPORT_H
+#define KOPETEADDRBOOKEXPORT_H
+
+#include <kabc/stdaddressbook.h>
+#include <kabc/addressee.h>
+
+#include "kopetecontactproperty.h"
+
+class AddressBookExportUI;
+class KDialogBase;
+class KListBox;
+class KComboBox;
+
+namespace Kopete
+{
+class Contact;
+class MetaContact;
+}
+
+class KopeteAddressBookExport : public QObject
+{
+public:
+ KopeteAddressBookExport( QWidget *parent, Kopete::MetaContact *mc );
+ ~KopeteAddressBookExport();
+
+ /**
+ * Display the dialog
+ * @return a QDialog return code
+ */
+ int showDialog();
+ /**
+ * Export the data to KABC if changed, omitting any duplicates
+ */
+ void exportData();
+
+protected:
+ /**
+ * Initialise the GUI labels with labels from KABC
+ */
+ void initLabels();
+ /**
+ * Populate the GUI with data from KABC
+ */
+ void fetchKABCData();
+ /**
+ * Populate a listbox with a given type of phone number
+ */
+ void fetchPhoneNumbers( KListBox * listBox, int type, uint& counter );
+ /**
+ * Populate the GUI with data from IM systems
+ */
+ void fetchIMData();
+ /**
+ * Populate a combobox with a contact's IM data
+ */
+ void populateIM( const Kopete::Contact *contact, const QPixmap &icon,
+ QComboBox *combo, const Kopete::ContactPropertyTmpl &property );
+ /**
+ * Populate a listbox with a contact's IM data
+ */
+ void populateIM( const Kopete::Contact *contact, const QPixmap &icon,
+ KListBox *combo, const Kopete::ContactPropertyTmpl &property );
+
+ /** Check the selected item is not the first (existing KABC) item, or the same as it */
+ bool newValue( QComboBox *combo );
+ QStringList newValues( KListBox *listBox, uint counter );
+
+ // the GUI
+ QWidget *mParent;
+ KDialogBase * mDialog;
+ QPixmap mAddrBookIcon;
+ AddressBookExportUI *mUI;
+ Kopete::MetaContact *mMetaContact;
+ KABC::AddressBook *mAddressBook;
+ KABC::Addressee mAddressee;
+
+ // counters tracking the number of KABC values where multiple values are possible in a single key
+ uint numEmails, numHomePhones, numWorkPhones, numMobilePhones;
+
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/kopeteaddrbookexportui.ui b/kopete/kopete/contactlist/kopeteaddrbookexportui.ui
new file mode 100644
index 00000000..9613d44f
--- /dev/null
+++ b/kopete/kopete/contactlist/kopeteaddrbookexportui.ui
@@ -0,0 +1,149 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>AddressBookExportUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddressBookExportUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>598</width>
+ <height>651</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Merge with Address Book</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>mLblFirstName</cstring>
+ </property>
+ <property name="text">
+ <string>First name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox1</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>mLblHomePhone</cstring>
+ </property>
+ <property name="text">
+ <string>Home phone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mPhones</cstring>
+ </property>
+ </widget>
+ <widget class="KListBox" row="5" column="1">
+ <property name="name">
+ <cstring>mWorkPhones</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ <widget class="KListBox" row="6" column="1">
+ <property name="name">
+ <cstring>mMobilePhones</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ <widget class="KListBox" row="4" column="1">
+ <property name="name">
+ <cstring>mHomePhones</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>mLblWorkPhone</cstring>
+ </property>
+ <property name="text">
+ <string>Work phone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mPhones_2</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>mLblMobilePhone</cstring>
+ </property>
+ <property name="text">
+ <string>Mobile phone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mPhones_3</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>mLblUrl</cstring>
+ </property>
+ <property name="text">
+ <string>URL:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox4</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <property name="name">
+ <cstring>mUrl</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>mFirstName</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>mLastName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>mLblLastName</cstring>
+ </property>
+ <property name="text">
+ <string>Last name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>comboBox2</cstring>
+ </property>
+ </widget>
+ <widget class="KListBox" row="2" column="1">
+ <property name="name">
+ <cstring>mEmails</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Extended</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>mLblEmail</cstring>
+ </property>
+ <property name="text">
+ <string>Email:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mEmails</cstring>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/kopete/contactlist/kopetecontactlistview.cpp b/kopete/kopete/contactlist/kopetecontactlistview.cpp
new file mode 100644
index 00000000..b6b01a2f
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetecontactlistview.cpp
@@ -0,0 +1,2134 @@
+/*
+ kopetecontactlistview.cpp
+
+ Kopete Contactlist GUI
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@usinternet.com>
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontactlistview.h"
+#include "kopeteuiglobal.h"
+
+#include <qcursor.h>
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qstylesheet.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kmultipledrag.h>
+#include <kabc/stdaddressbook.h>
+#include <kabc/vcardconverter.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+
+#include "addcontactwizard.h"
+#include "addcontactpage.h"
+#include "chatmessagepart.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontactlist.h"
+#include "kopetemessageevent.h"
+#include "kopetegroup.h"
+#include "kopetegroupviewitem.h"
+#include "kopetemetacontact.h"
+#include "kopetemetacontactlvi.h"
+#include "kopeteprefs.h"
+#include "kopeteprotocol.h"
+#include "kopetestatusgroupviewitem.h"
+#include "kopetestdaction.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetecontact.h"
+#include "kopetechatsession.h" //needed to send the URL
+#include "kopetemessage.h" //needed to send the URL
+#include "kopeteglobal.h"
+#include "kopetelviprops.h"
+#include "kopetegrouplistaction.h"
+
+#include <memory>
+
+class ContactListViewStrategy;
+
+class KopeteContactListViewPrivate
+{
+public:
+ std::auto_ptr<ContactListViewStrategy> viewStrategy;
+
+ void updateViewStrategy( KListView *view );
+};
+
+class ContactListViewStrategy
+{
+public:
+ ContactListViewStrategy( KListView *view )
+ : _listView( view )
+ {
+ view->clear();
+ }
+ virtual ~ContactListViewStrategy() {}
+ KListView *listView() { return _listView; }
+ void addCurrentItems()
+ {
+ // Add the already existing groups now
+ QPtrList<Kopete::Group> grps = Kopete::ContactList::self()->groups();
+ for ( QPtrListIterator<Kopete::Group> groupIt( grps ); groupIt.current(); ++groupIt )
+ addGroup( groupIt.current() );
+
+ // Add the already existing meta contacts now
+ QPtrList<Kopete::MetaContact> metaContacts = Kopete::ContactList::self()->metaContacts();
+ for ( QPtrListIterator<Kopete::MetaContact> it( metaContacts ); it.current(); ++it )
+ addMetaContact( it.current() );
+ }
+
+ virtual void addMetaContact( Kopete::MetaContact *mc ) = 0;
+ virtual void removeMetaContact( Kopete::MetaContact *mc ) = 0;
+
+ virtual void addGroup( Kopete::Group * ) {}
+
+ virtual void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) = 0;
+ virtual void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group *gp ) = 0;
+ virtual void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to ) = 0;
+
+ virtual void metaContactStatusChanged( Kopete::MetaContact *mc ) = 0;
+
+protected:
+ // work around QListView design stupidity.
+ // GroupViewItem will be QListView-derived, or QListViewItem-derived.
+ template<typename GroupViewItem>
+ void addMetaContactToGroupInner( Kopete::MetaContact *mc, GroupViewItem *gpi )
+ {
+ // check if the contact isn't already in the group
+ for( QListViewItem *item = gpi->firstChild(); item; item = item->nextSibling() )
+ if ( KopeteMetaContactLVI *mci = dynamic_cast<KopeteMetaContactLVI*>(item) )
+ if ( mci->metaContact() == mc )
+ return;
+ (void) new KopeteMetaContactLVI( mc, gpi );
+ }
+
+ template<typename GroupViewItem>
+ void removeMetaContactFromGroupInner( Kopete::MetaContact *mc, GroupViewItem *gpi )
+ {
+ KopeteMetaContactLVI* mci;
+ QListViewItem* item = gpi->firstChild();
+ while(item) {
+ mci = dynamic_cast<KopeteMetaContactLVI*>(item);
+ item = item->nextSibling();
+
+ if ( mci && mci->metaContact() == mc )
+ delete mci;
+ }
+ }
+
+private:
+ KListView *_listView;
+};
+
+class ArrangeByGroupsViewStrategy : public ContactListViewStrategy
+{
+public:
+ ArrangeByGroupsViewStrategy( KListView *view )
+ : ContactListViewStrategy( view )
+ {
+ addCurrentItems();
+ }
+
+ void addMetaContact( Kopete::MetaContact *mc )
+ {
+ // create group items
+ Kopete::GroupList list = mc->groups();
+ for ( Kopete::Group *gp = list.first(); gp; gp = list.next() )
+ // will check to see if the contact is already in the group.
+ // this is inefficient but makes this function idempotent.
+ addMetaContactToGroup( mc, gp );
+ }
+ void removeMetaContact( Kopete::MetaContact *mc )
+ {
+ // usually, the list item will be deleted when the KMC is. however, we still
+ // need to make sure that the item count of the groups is correct.
+ // as a bonus, this allows us to remove a MC from the contact list without deleting it.
+ Kopete::GroupList list = mc->groups();
+ for ( Kopete::Group *gp = list.first(); gp; gp = list.next() )
+ removeMetaContactFromGroup( mc, gp );
+ }
+
+ void addGroup( Kopete::Group *group )
+ {
+ (void) findOrCreateGroupItem( group );
+ }
+
+ void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group *gp )
+ {
+ if ( KopeteGroupViewItem *gpi = findOrCreateGroupItem( gp ) )
+ addMetaContactToGroupInner( mc, gpi );
+ else
+ addMetaContactToGroupInner( mc, listView() );
+ }
+ void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group *gp )
+ {
+ if ( gp->type() == Kopete::Group::TopLevel )
+ removeMetaContactFromGroupInner( mc, listView() );
+ else if ( KopeteGroupViewItem *gpi = findGroupItem( gp ) )
+ {
+ removeMetaContactFromGroupInner( mc, gpi );
+
+ // update the group's display of its number of children.
+ // TODO: make the KopeteGroupViewItem not need this, by overriding insertItem and takeItem
+ gpi->refreshDisplayName();
+
+ // remove the temporary group if it's empty
+ if ( gpi->childCount() == 0 )
+ if ( gp->type() == Kopete::Group::Temporary )
+ delete gpi;
+ }
+ }
+ void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to )
+ {
+ // TODO: use takeItem and insertItem, and mci->movedGroup
+ addMetaContactToGroup( mc, to );
+ removeMetaContactFromGroup( mc, from );
+ }
+ void metaContactStatusChanged( Kopete::MetaContact * ) {}
+
+private:
+ KopeteGroupViewItem *findGroupItem( Kopete::Group *gp )
+ {
+ if ( gp->type() == Kopete::Group::TopLevel ) return 0;
+ for( QListViewItem *item = listView()->firstChild(); item; item = item->nextSibling() )
+ if ( KopeteGroupViewItem *gvi = dynamic_cast<KopeteGroupViewItem*>(item) )
+ if ( gvi->group() == gp )
+ return gvi;
+ return 0;
+ }
+ KopeteGroupViewItem *findOrCreateGroupItem( Kopete::Group *gp )
+ {
+ if ( gp->type() == Kopete::Group::TopLevel ) return 0;
+ if ( KopeteGroupViewItem *item = findGroupItem(gp) )
+ return item;
+ KopeteGroupViewItem *gpi = new KopeteGroupViewItem( gp, listView() );
+ // TODO: store as plugin data the expandedness of a group
+ // currently this requires a 'plugin' for the main UI.
+ gpi->setOpen( gp->isExpanded() );
+ return gpi;
+ }
+};
+
+class ArrangeByPresenceViewStrategy : public ContactListViewStrategy
+{
+public:
+ ArrangeByPresenceViewStrategy( KListView *view )
+ : ContactListViewStrategy( view )
+ , m_onlineItem( new KopeteStatusGroupViewItem( Kopete::OnlineStatus::Online, listView() ) )
+ , m_offlineItem( new KopeteStatusGroupViewItem( Kopete::OnlineStatus::Offline, listView() ) )
+ {
+ m_onlineItem->setOpen( true );
+ m_offlineItem->setOpen( true );
+ addCurrentItems();
+ }
+
+ void removeMetaContact( Kopete::MetaContact *mc )
+ {
+ // there's only three places we put metacontacts: online, offline and temporary.
+ removeMetaContactFromGroupInner( mc, m_onlineItem );
+ removeMetaContactFromGroupInner( mc, m_offlineItem );
+ if ( m_temporaryItem )
+ removeMetaContactFromGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem );
+ }
+
+ void addMetaContact( Kopete::MetaContact *mc )
+ {
+ updateMetaContact( mc );
+ }
+ void addMetaContactToGroup( Kopete::MetaContact *mc, Kopete::Group * )
+ {
+ updateMetaContact( mc );
+ }
+ void removeMetaContactFromGroup( Kopete::MetaContact *mc, Kopete::Group * )
+ {
+ updateMetaContact( mc );
+ }
+ void moveMetaContactBetweenGroups( Kopete::MetaContact *mc, Kopete::Group *, Kopete::Group * )
+ {
+ updateMetaContact( mc );
+ }
+ void metaContactStatusChanged( Kopete::MetaContact *mc )
+ {
+ updateMetaContact( mc );
+ }
+private:
+ void updateMetaContact( Kopete::MetaContact *mc )
+ {
+ // split into a ...Inner function and this one to make the short-circuiting logic easier
+ updateMetaContactInner( mc );
+
+ // FIXME: these items should do this for themselves...
+ m_onlineItem->setText(0,i18n("Online contacts (%1)").arg(m_onlineItem->childCount()));
+ m_offlineItem->setText(0,i18n("Offline contacts (%1)").arg(m_offlineItem->childCount()));
+ }
+ void updateMetaContactInner( Kopete::MetaContact *mc )
+ {
+ // this function basically *is* the arrange-by-presence strategy.
+ // given a metacontact, it removes it from any existing incorrect place and adds
+ // it to the correct place. usually it does this with takeItem and insertItem.
+
+ // if the metacontact is temporary, it should be only in the temporary group
+ if ( mc->isTemporary() )
+ {
+ removeMetaContactFromGroupInner( mc, m_onlineItem );
+ removeMetaContactFromGroupInner( mc, m_offlineItem );
+
+ // create temporary item on demand
+ if ( !m_temporaryItem )
+ {
+ m_temporaryItem = new KopeteGroupViewItem( Kopete::Group::temporary(), listView() );
+ m_temporaryItem->setOpen( true );
+ }
+
+ addMetaContactToGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem );
+ return;
+ }
+
+ // if it's not temporary, it should not be in the temporary group
+ if ( m_temporaryItem )
+ {
+ removeMetaContactFromGroupInner( mc, (KopeteGroupViewItem*)m_temporaryItem );
+
+ // remove temporary item if empty
+ if ( m_temporaryItem && m_temporaryItem->childCount() == 0 )
+ {
+ delete (KopeteGroupViewItem*)m_temporaryItem;
+ }
+ }
+
+ // check if the contact is already in the correct "group"
+ QListViewItem *currentGroup = mc->isOnline() ? m_onlineItem : m_offlineItem;
+ for( QListViewItem *lvi = currentGroup->firstChild(); lvi; lvi = lvi->nextSibling() )
+ if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) )
+ if ( kc->metaContact() == mc )
+ return;
+
+ // item not found in the right group; look for it in the other group
+ QListViewItem *oppositeGroup = mc->isOnline() ? m_offlineItem : m_onlineItem;
+ for( QListViewItem *lvi = oppositeGroup->firstChild(); lvi; lvi = lvi->nextSibling() )
+ {
+ if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) )
+ {
+ if ( kc->metaContact() == mc )
+ {
+ // found: move it over to the right group
+ oppositeGroup->takeItem(kc);
+ currentGroup->insertItem(kc);
+ return;
+ }
+ }
+ }
+
+ // item not found in either online neither offline groups: add it
+ (void) new KopeteMetaContactLVI( mc, currentGroup );
+ }
+
+ KopeteStatusGroupViewItem *m_onlineItem, *m_offlineItem;
+ QGuardedPtr<KopeteGroupViewItem> m_temporaryItem;
+};
+
+void KopeteContactListViewPrivate::updateViewStrategy( KListView *view )
+{
+ // this is a bit nasty, but this function needs changing if we add
+ // more view strategies anyway, so it should be fine.
+ bool bSortByGroup = (bool)dynamic_cast<ArrangeByGroupsViewStrategy*>(viewStrategy.get());
+ if ( !viewStrategy.get() || KopetePrefs::prefs()->sortByGroup() != bSortByGroup )
+ {
+ // delete old strategy first...
+ viewStrategy.reset( 0 );
+ // then create and store a new one
+ if ( KopetePrefs::prefs()->sortByGroup() )
+ viewStrategy.reset( new ArrangeByGroupsViewStrategy(view) );
+ else
+ viewStrategy.reset( new ArrangeByPresenceViewStrategy(view) );
+ }
+}
+
+// returns the next item in a depth-first descent of the list view.
+// much like QLVI::itemBelow but does not depend on visibility of items, etc.
+static QListViewItem *nextItem( QListViewItem *item )
+{
+ if ( QListViewItem *it = item->firstChild() )
+ return it;
+ while ( item && !item->nextSibling() )
+ item = item->parent();
+ if ( !item )
+ return 0;
+ return item->nextSibling();
+}
+
+
+
+KopeteContactListView::KopeteContactListView( QWidget *parent, const char *name )
+ : Kopete::UI::ListView::ListView( parent, name )
+{
+ d = new KopeteContactListViewPrivate;
+ m_undo=0L;
+ m_redo=0L;
+
+ mShowAsTree = KopetePrefs::prefs()->treeView();
+ if ( mShowAsTree )
+ {
+ setRootIsDecorated( true );
+ }
+ else
+ {
+ setRootIsDecorated( false );
+ setTreeStepSize( 0 );
+ }
+
+ d->updateViewStrategy( this );
+
+ setFullWidth( true );
+
+ connect( this, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ),
+ SLOT( slotContextMenu( KListView *, QListViewItem *, const QPoint & ) ) );
+ connect( this, SIGNAL( expanded( QListViewItem * ) ),
+ SLOT( slotExpanded( QListViewItem * ) ) );
+ connect( this, SIGNAL( collapsed( QListViewItem * ) ),
+ SLOT( slotCollapsed( QListViewItem * ) ) );
+ connect( this, SIGNAL( executed( QListViewItem *, const QPoint &, int ) ),
+ SLOT( slotExecuted( QListViewItem *, const QPoint &, int ) ) );
+ connect( this, SIGNAL( selectionChanged() ), SLOT( slotViewSelectionChanged() ) );
+ connect( this, SIGNAL( itemRenamed( QListViewItem * ) ),
+ SLOT( slotItemRenamed( QListViewItem * ) ) );
+
+ connect( KopetePrefs::prefs(), SIGNAL( saved() ), SLOT( slotSettingsChanged() ) );
+
+ connect( Kopete::ContactList::self(), SIGNAL( selectionChanged() ),
+ SLOT( slotListSelectionChanged() ) );
+ connect( Kopete::ContactList::self(),
+ SIGNAL( metaContactAdded( Kopete::MetaContact * ) ),
+ SLOT( slotMetaContactAdded( Kopete::MetaContact * ) ) );
+ connect( Kopete::ContactList::self(),
+ SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ),
+ SLOT( slotMetaContactDeleted( Kopete::MetaContact * ) ) );
+ connect( Kopete::ContactList::self(), SIGNAL( groupAdded( Kopete::Group * ) ),
+ SLOT( slotGroupAdded( Kopete::Group * ) ) );
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( newEvent( Kopete::MessageEvent * ) ),
+ this, SLOT( slotNewMessageEvent( Kopete::MessageEvent * ) ) );
+
+ connect( this, SIGNAL( dropped( QDropEvent *, QListViewItem *, QListViewItem * ) ),
+ this, SLOT( slotDropped( QDropEvent *, QListViewItem *, QListViewItem * ) ) );
+
+ connect( &undoTimer, SIGNAL(timeout()) , this, SLOT (slotTimeout() ) );
+
+ addColumn( i18n( "Contacts" ), 0 ); //add an unique colums to add every contact
+ header()->hide(); // and hide the ugly header which show the single word "Contacts"
+
+ setAutoOpen( true );
+ setDragEnabled( true );
+ setAcceptDrops( true );
+ setItemsMovable( false );
+ setDropVisualizer( false );
+ setDropHighlighter( true );
+ setSelectionMode( QListView::Extended );
+
+ // Load in the user's initial settings
+ slotSettingsChanged();
+}
+
+void KopeteContactListView::initActions( KActionCollection *ac )
+{
+ actionUndo = KStdAction::undo( this , SLOT( slotUndo() ) , ac );
+ actionRedo = KStdAction::redo( this , SLOT( slotRedo() ) , ac );
+ actionUndo->setEnabled(false);
+ actionRedo->setEnabled(false);
+
+
+ new KAction( i18n( "Create New Group..." ), 0, 0, this, SLOT( addGroup() ),
+ ac, "AddGroup" );
+
+ actionSendMessage = KopeteStdAction::sendMessage(
+ this, SLOT( slotSendMessage() ), ac, "contactSendMessage" );
+ actionStartChat = KopeteStdAction::chat( this, SLOT( slotStartChat() ),
+ ac, "contactStartChat" );
+
+ actionMove = new KopeteGroupListAction( i18n( "&Move To" ), QString::fromLatin1( "editcut" ),
+ 0, this, SLOT( slotMoveToGroup() ), ac, "contactMove" );
+ actionCopy = new KopeteGroupListAction( i18n( "&Copy To" ), QString::fromLatin1( "editcopy" ), 0,
+ this, SLOT( slotCopyToGroup() ), ac, "contactCopy" );
+
+ actionRemove = KopeteStdAction::deleteContact( this, SLOT( slotRemove() ),
+ ac, "contactRemove" );
+ actionSendEmail = new KAction( i18n( "Send Email..." ), QString::fromLatin1( "mail_generic" ),
+ 0, this, SLOT( slotSendEmail() ), ac, "contactSendEmail" );
+ /* this actionRename is buggy, and useless with properties, removed in kopeteui.rc*/
+ actionRename = new KAction( i18n( "Rename" ), "filesaveas", 0,
+ this, SLOT( slotRename() ), ac, "contactRename" );
+ actionSendFile = KopeteStdAction::sendFile( this, SLOT( slotSendFile() ),
+ ac, "contactSendFile" );
+
+ actionAddContact = new KActionMenu( i18n( "&Add Contact" ),
+ QString::fromLatin1( "add_user" ), ac , "contactAddContact" );
+ actionAddContact->popupMenu()->insertTitle( i18n("Select Account") );
+
+ actionAddTemporaryContact = new KAction( i18n( "Add to Your Contact List" ), "add_user", 0,
+ this, SLOT( slotAddTemporaryContact() ), ac, "contactAddTemporaryContact" );
+
+ connect( Kopete::ContactList::self(), SIGNAL( metaContactSelected( bool ) ), this, SLOT( slotMetaContactSelected( bool ) ) );
+
+ connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered( Kopete::Account* )), SLOT(slotAddSubContactActionNewAccount(Kopete::Account*)));
+ connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered( const Kopete::Account* )), SLOT(slotAddSubContactActionAccountDeleted(const Kopete::Account *)));
+
+ actionProperties = new KAction( i18n( "&Properties" ), "edit_user", Qt::Key_Alt + Qt::Key_Return,
+ this, SLOT( slotProperties() ), ac, "contactProperties" );
+
+ // Update enabled/disabled actions
+ slotViewSelectionChanged();
+}
+
+KopeteContactListView::~KopeteContactListView()
+{
+ delete d;
+}
+
+void KopeteContactListView::slotAddSubContactActionNewAccount(Kopete::Account* account)
+{
+ KAction *action = new KAction( account->accountLabel(), account->accountIcon(), 0 , this, SLOT(slotAddContact()), account);
+ m_accountAddContactMap.insert( account, action);
+ actionAddContact->insert( action );
+}
+
+void KopeteContactListView::slotAddSubContactActionAccountDeleted(const Kopete::Account *account)
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ if ( m_accountAddContactMap.contains( account ) )
+ {
+ KAction *action = m_accountAddContactMap[account];
+ m_accountAddContactMap.remove( account );
+ actionAddContact->remove( action );
+ }
+}
+
+void KopeteContactListView::slotMetaContactAdded( Kopete::MetaContact *mc )
+{
+ d->viewStrategy->addMetaContact( mc );
+
+ connect( mc, SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ),
+ SLOT( slotAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
+ connect( mc, SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ),
+ SLOT( slotRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
+ connect( mc, SIGNAL( movedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ),
+ SLOT( slotMovedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ) );
+ connect( mc, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ),
+ SLOT( slotContactStatusChanged( Kopete::MetaContact * ) ) );
+}
+
+void KopeteContactListView::slotMetaContactDeleted( Kopete::MetaContact *mc )
+{
+ d->viewStrategy->removeMetaContact( mc );
+}
+
+void KopeteContactListView::slotMetaContactSelected( bool sel )
+{
+ bool set = sel;
+
+ if( sel )
+ {
+ Kopete::MetaContact *kmc = Kopete::ContactList::self()->selectedMetaContacts().first();
+ set = sel && kmc->isReachable();
+ actionAddTemporaryContact->setEnabled( sel && kmc->isTemporary() );
+ }
+ else
+ {
+ actionAddTemporaryContact->setEnabled(false);
+ }
+
+ actionSendMessage->setEnabled( set );
+ actionStartChat->setEnabled( set );
+ actionMove->setEnabled( sel ); // TODO: make available for several contacts
+ actionCopy->setEnabled( sel ); // TODO: make available for several contacts
+}
+
+void KopeteContactListView::slotAddedToGroup( Kopete::MetaContact *mc, Kopete::Group *to )
+{
+ d->viewStrategy->addMetaContactToGroup( mc, to );
+}
+
+void KopeteContactListView::slotRemovedFromGroup( Kopete::MetaContact *mc, Kopete::Group *from )
+{
+ d->viewStrategy->removeMetaContactFromGroup( mc, from );
+}
+
+void KopeteContactListView::slotMovedToGroup( Kopete::MetaContact *mc,
+ Kopete::Group *from, Kopete::Group *to )
+{
+ d->viewStrategy->moveMetaContactBetweenGroups( mc, from, to );
+}
+
+void KopeteContactListView::removeContact( Kopete::MetaContact *c )
+{
+ d->viewStrategy->removeMetaContact( c );
+}
+
+void KopeteContactListView::addGroup()
+{
+ QString groupName =
+ KInputDialog::getText( i18n( "New Group" ),
+ i18n( "Please enter the name for the new group:" ) );
+
+ if ( !groupName.isEmpty() )
+ addGroup( groupName );
+}
+
+void KopeteContactListView::addGroup( const QString &groupName )
+{
+ d->viewStrategy->addGroup( Kopete::ContactList::self()->findGroup(groupName) );
+}
+
+void KopeteContactListView::slotGroupAdded( Kopete::Group *group )
+{
+ d->viewStrategy->addGroup( group );
+}
+
+void KopeteContactListView::slotExpanded( QListViewItem *item )
+{
+ KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem *>( item );
+ if ( groupLVI )
+ {
+ groupLVI->group()->setExpanded( true );
+ groupLVI->updateIcon();
+ }
+
+ //workaround a bug in qt which make the items of a closed item not sorted. (qt 3.3.4 here)
+ delayedSort();
+}
+
+void KopeteContactListView::slotCollapsed( QListViewItem *item )
+{
+ KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem*>( item );
+ if ( groupLVI )
+ {
+ groupLVI->group()->setExpanded( false );
+ groupLVI->updateIcon();
+ }
+}
+
+void KopeteContactListView::slotContextMenu( KListView * /*listview*/,
+ QListViewItem *item, const QPoint &point )
+{
+ // FIXME: this code should be moved to the various list view item classes.
+ KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( item );
+ KopeteGroupViewItem *groupvi = dynamic_cast<KopeteGroupViewItem *>( item );
+
+ if ( item && !item->isSelected() )
+ {
+ clearSelection();
+ item->setSelected( true );
+ }
+
+ if ( !item )
+ {
+ clearSelection();
+ //Clear selection doesn't update lists of selected contact if the item is onlt heilighted (see bug 106090)
+ Kopete::ContactList::self()->setSelectedItems( QPtrList<Kopete::MetaContact>() , QPtrList<Kopete::Group>() );
+ }
+
+ int nb = Kopete::ContactList::self()->selectedMetaContacts().count() +
+ Kopete::ContactList::self()->selectedGroups().count();
+
+ KMainWindow *window = dynamic_cast<KMainWindow *>(topLevelWidget());
+ if ( !window )
+ {
+ kdError( 14000 ) << k_funcinfo << "Main window not found, unable to display context-menu; "
+ << "Kopete::UI::Global::mainWidget() = " << Kopete::UI::Global::mainWidget() << endl;
+ return;
+ }
+
+ if ( metaLVI && nb == 1 )
+ {
+ int px = mapFromGlobal( point ).x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) +
+ treeStepSize() * ( item->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() );
+ int py = mapFromGlobal( point ).y() - itemRect( item ).y() - (header()->isVisible() ? header()->height() : 0) ;
+
+ //kdDebug( 14000 ) << k_funcinfo << "x: " << px << ", y: " << py << endl;
+ Kopete::Contact *c = metaLVI->contactForPoint( QPoint( px, py ) ) ;
+ if ( c )
+ {
+ KPopupMenu *p = c->popupMenu();
+ connect( p, SIGNAL( aboutToHide() ), p, SLOT( deleteLater() ) );
+ p->popup( point );
+ }
+ else
+ {
+ KPopupMenu *popup = dynamic_cast<KPopupMenu *>(
+ window->factory()->container( "contact_popup", window ) );
+ if ( popup )
+ {
+ QString title = i18n( "Translators: format: '<nickname> (<online status>)'", "%1 (%2)" ).
+ arg( metaLVI->metaContact()->displayName(), metaLVI->metaContact()->statusString() );
+
+ if ( title.length() > 43 )
+ title = title.left( 40 ) + QString::fromLatin1( "..." );
+
+ if ( popup->title( 0 ).isNull() )
+ popup->insertTitle ( title, 0, 0 );
+ else
+ popup->changeTitle ( 0, title );
+
+ // Submenus for separate contact actions
+ bool sep = false; //FIXME: find if there is already a separator in the end - Olivier
+ QPtrList<Kopete::Contact> it = metaLVI->metaContact()->contacts();
+ for( Kopete::Contact *c = it.first(); c; c = it.next() )
+ {
+ if( sep )
+ {
+ popup->insertSeparator();
+ sep = false;
+ }
+
+ KPopupMenu *contactMenu = it.current()->popupMenu();
+ connect( popup, SIGNAL( aboutToHide() ), contactMenu, SLOT( deleteLater() ) );
+ QString nick=c->property(Kopete::Global::Properties::self()->nickName()).value().toString();
+ QString text= nick.isEmpty() ? c->contactId() : i18n( "Translators: format: '<displayName> (<id>)'", "%2 <%1>" ). arg( c->contactId(), nick );
+ text=text.replace("&","&&"); // cf BUG 115449
+
+ if ( text.length() > 41 )
+ text = text.left( 38 ) + QString::fromLatin1( "..." );
+
+ popup->insertItem( c->onlineStatus().iconFor( c, 16 ), text , contactMenu );
+ }
+
+ popup->popup( point );
+ }
+ }
+ }
+ else if ( groupvi && nb == 1 )
+ {
+ KPopupMenu *popup = dynamic_cast<KPopupMenu *>(
+ window->factory()->container( "group_popup", window ) );
+ if ( popup )
+ {
+ QString title = groupvi->group()->displayName();
+ if ( title.length() > 32 )
+ title = title.left( 30 ) + QString::fromLatin1( "..." );
+
+ if( popup->title( 0 ).isNull() )
+ popup->insertTitle( title, 0, 0 );
+ else
+ popup->changeTitle( 0, title );
+
+ popup->popup( point );
+ }
+ }
+ else if ( nb >= 1 )
+ {
+ KPopupMenu *popup = dynamic_cast<KPopupMenu *>(
+ window->factory()->container( "contactlistitems_popup", window ) );
+ if ( popup )
+ popup->popup( point );
+ }
+ else
+ {
+ KPopupMenu *popup = dynamic_cast<KPopupMenu *>(
+ window->factory()->container( "contactlist_popup", window ) );
+ if ( popup )
+ {
+ if ( popup->title( 0 ).isNull() )
+ popup->insertTitle( i18n( "Kopete" ), 0, 0 );
+
+ popup->popup( point );
+ }
+ }
+}
+
+void KopeteContactListView::slotShowAddContactDialog()
+{
+ ( new AddContactWizard( Kopete::UI::Global::mainWidget() ) )->show();
+}
+
+void KopeteContactListView::slotSettingsChanged( void )
+{
+ mShowAsTree = KopetePrefs::prefs()->treeView();
+ if ( mShowAsTree )
+ {
+ setRootIsDecorated( true );
+ setTreeStepSize( 20 );
+ }
+ else
+ {
+ setRootIsDecorated( false );
+ setTreeStepSize( 0 );
+ }
+
+ // maybe setEffects should read these from KopetePrefs itself?
+ Kopete::UI::ListView::Item::setEffects( KopetePrefs::prefs()->contactListAnimation(),
+ KopetePrefs::prefs()->contactListFading(),
+ KopetePrefs::prefs()->contactListFolding() );
+
+ d->updateViewStrategy( this );
+
+ slotUpdateAllGroupIcons();
+ update();
+}
+
+void KopeteContactListView::slotUpdateAllGroupIcons()
+{
+ // FIXME: groups can (should?) do this for themselves
+ // HACK: assume all groups are top-level. works for now, until the fixme above is dealt with
+ for ( QListViewItem *it = firstChild(); it; it = it->nextSibling() )
+ if ( KopeteGroupViewItem *gpi = dynamic_cast<KopeteGroupViewItem*>( it ) )
+ gpi->updateIcon();
+}
+
+void KopeteContactListView::slotExecuted( QListViewItem *item, const QPoint &p, int /* col */ )
+{
+ item->setSelected( false );
+ KopeteMetaContactLVI *metaContactLVI = dynamic_cast<KopeteMetaContactLVI *>( item );
+
+ QPoint pos = viewport()->mapFromGlobal( p );
+ Kopete::Contact *c = 0L;
+ if ( metaContactLVI )
+ {
+ // Try if we are clicking a protocol icon. If so, open a direct
+ // connection for that protocol
+ QRect r = itemRect( item );
+ QPoint relativePos( pos.x() - r.left() - ( treeStepSize() *
+ ( item->depth() + ( rootIsDecorated() ? 1 : 0 ) ) +
+ itemMargin() ), pos.y() - r.top() );
+ c = metaContactLVI->contactForPoint( relativePos );
+ if( c )
+ c->execute();
+ else
+ metaContactLVI->execute();
+ }
+}
+
+void KopeteContactListView::slotContactStatusChanged( Kopete::MetaContact *mc )
+{
+ d->viewStrategy->metaContactStatusChanged( mc );
+}
+
+void KopeteContactListView::slotDropped(QDropEvent *e, QListViewItem *, QListViewItem *after)
+{
+ if(!acceptDrag(e))
+ return;
+
+ KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(after);
+ KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(after);
+
+ if( const_cast<const QWidget *>( e->source() ) == this )
+ {
+ QPtrListIterator<KopeteMetaContactLVI> it( m_selectedContacts );
+
+ while ( it.current() )
+ {
+ Kopete::Contact *source_contact=0L;
+ KopeteMetaContactLVI *source_metaLVI = it.current();
+ ++it;
+
+ if(source_metaLVI)
+ source_contact = source_metaLVI->contactForPoint( m_startDragPos );
+
+ if(source_metaLVI && dest_groupLVI)
+ {
+ if(source_metaLVI->group() == dest_groupLVI->group())
+ return;
+
+ if(source_metaLVI->metaContact()->isTemporary())
+ {
+ addDraggedContactToGroup(source_metaLVI->metaContact(),dest_groupLVI->group());
+ }
+ else
+ {
+ moveDraggedContactToGroup( source_metaLVI->metaContact(),
+ source_metaLVI->group(), dest_groupLVI->group() );
+ }
+ }
+ else if(source_metaLVI && !dest_metaLVI && !dest_groupLVI)
+ {
+ if ( source_metaLVI->group()->type() == Kopete::Group::TopLevel )
+ return;
+
+ if(source_metaLVI->metaContact()->isTemporary())
+ {
+ addDraggedContactToGroup(source_metaLVI->metaContact() , Kopete::Group::topLevel() );
+ }
+ else
+ {
+ moveDraggedContactToGroup( source_metaLVI->metaContact(),
+ source_metaLVI->group(), Kopete::Group::topLevel() );
+ }
+ }
+ else if(source_contact && dest_metaLVI) //we are moving a contact to another metacontact
+ {
+ if(source_metaLVI->metaContact()->isTemporary())
+ {
+ addDraggedContactToMetaContact( source_contact,dest_metaLVI->metaContact() );
+ }
+ else
+ {
+ UndoItem *u=new UndoItem;
+ u->type=UndoItem::MetaContactChange;
+ u->metacontact=source_metaLVI->metaContact();
+ u->group=source_metaLVI->group();
+ u->args << source_contact->protocol()->pluginId() << source_contact->account()->accountId() << source_contact->contactId();
+ u->args << source_metaLVI->metaContact()->displayName();
+ insertUndoItem(u);
+
+ source_contact->setMetaContact(dest_metaLVI->metaContact());
+ }
+ }
+ }
+ }
+ else if( e->provides("kopete/x-contact") )
+ {
+ QString contactInfo = QString::fromUtf8( e->encodedData("kopete/x-contact") );
+ QString protocolId = contactInfo.section( QChar( 0xE000 ), 0, 0 );
+ QString accountId = contactInfo.section( QChar( 0xE000 ), 1, 1 );
+ QString contactId = contactInfo.section( QChar( 0xE000 ), 2 );
+
+ addDraggedContactByInfo( protocolId, accountId, contactId, after );
+
+ }
+ else if( e->provides("text/uri-list") )
+ {
+ if ( !QUriDrag::canDecode( e ) )
+ {
+ e->ignore();
+ return;
+ }
+
+ KURL::List urlList;
+ KURLDrag::decode( e, urlList );
+
+ for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it )
+ {
+ KURL url = (*it);
+ if( url.protocol() == QString::fromLatin1("kopetemessage") )
+ {
+ //Add a contact
+ addDraggedContactByInfo( url.queryItem("protocolId"),
+ url.queryItem("accountId"), url.host(), after );
+ }
+ else if( dest_metaLVI )
+ {
+ QPoint p = contentsToViewport(e->pos());
+ int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) +
+ treeStepSize() * ( dest_metaLVI->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() );
+ int py = p.y() - itemRect( dest_metaLVI ).y();
+
+ Kopete::Contact *c = dest_metaLVI->contactForPoint( QPoint( px, py ) );
+
+ if( url.isLocalFile() )
+ {
+ //send a file
+ if(c)
+ c->sendFile( url );
+ else
+ dest_metaLVI->metaContact()->sendFile( url );
+ }
+ else
+ {
+ //this is a URL, send the URL in a message
+ if(!c)
+ {
+ // We need to know which contact was chosen as the preferred
+ // in order to message it
+ c = dest_metaLVI->metaContact()->execute();
+ }
+
+ if (!c)
+ return;
+
+ Kopete::Message msg(c->account()->myself(), c, url.url(),
+ Kopete::Message::Outbound);
+ c->manager(Kopete::Contact::CanCreate)->sendMessage(msg);
+ }
+ }
+ }
+ e->acceptAction();
+ }
+}
+
+void KopeteContactListView::moveDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *from, Kopete::Group *to )
+{
+ contact->moveToGroup( from, to );
+
+ insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , contact, to ) );
+ UndoItem *u=new UndoItem( UndoItem::MetaContactRemove, contact, to );
+ u->isStep=false;
+ insertUndoItem(u);
+}
+
+void KopeteContactListView::addDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *group )
+{
+ int r=KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Would you like to add <b>%1</b> to your contact list as a member of <b>%2</b>?</qt>" )
+ .arg( contact->displayName(), group->displayName() ),
+ i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"),
+ "addTemporaryWhenMoving" );
+
+ if( r == KMessageBox::Yes )
+ {
+ contact->setTemporary( false, group );
+ Kopete::ContactList::self()->addMetaContact( contact );
+ insertUndoItem( new UndoItem( UndoItem::MetaContactAdd, contact, group ) );
+ }
+}
+
+void KopeteContactListView::addDraggedContactToMetaContact( Kopete::Contact *contact, Kopete::MetaContact *parent )
+{
+ int r = KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Would you like to add <b>%1</b> to your contact list as a child contact of <b>%2</b>?</qt>" )
+ .arg( contact->contactId(), parent->displayName() ),
+ i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"),
+ "addTemporaryWhenMoving" );
+
+ if( r == KMessageBox::Yes )
+ {
+ contact->setMetaContact(parent);
+
+ UndoItem *u=new UndoItem;
+ u->type=UndoItem::ContactAdd;
+ u->args << contact->protocol()->pluginId() << contact->account()->accountId() << contact->contactId();
+ insertUndoItem(u);
+ }
+}
+
+void KopeteContactListView::addDraggedContactByInfo( const QString &protocolId, const QString &accountId,
+ const QString &contactId, QListViewItem *after )
+{
+ kdDebug(14000) << k_funcinfo << "protocolId=" << protocolId <<
+ ", accountId=" << accountId << ", contactId=" << contactId << endl;
+
+ Kopete::Account *account = Kopete::AccountManager::self()->findAccount( protocolId,accountId );
+ if( account )
+ {
+ QDict<Kopete::Contact> contacts = account->contacts();
+ Kopete::Contact *source_contact = contacts[ contactId ];
+
+ if( source_contact )
+ {
+ if( source_contact->metaContact()->isTemporary() )
+ {
+ KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(after);
+ KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(after);
+
+ if( dest_metaLVI )
+ {
+ addDraggedContactToMetaContact( source_contact, dest_metaLVI->metaContact() );
+ }
+ else if( dest_groupLVI )
+ {
+ addDraggedContactToGroup( source_contact->metaContact(),dest_groupLVI->group() );
+ }
+ else
+ {
+ addDraggedContactToGroup( source_contact->metaContact(), Kopete::Group::topLevel() );
+ }
+ }
+ else
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n("<qt>This contact is already on your contact list. It is a child contact of <b>%1</b></qt>")
+ .arg( source_contact->metaContact()->displayName() )
+ );
+ }
+ }
+ }
+}
+
+bool KopeteContactListView::acceptDrag(QDropEvent *e) const
+{
+ QListViewItem *source=currentItem();
+ QListViewItem *parent;
+ QListViewItem *afterme;
+ // Due to a little design problem in KListView::findDrop() we can't
+ // call it directly from a const method until KDE 4.0, but as the
+ // method is in fact const we can of course get away with a
+ // const_cast...
+ const_cast<KopeteContactListView *>( this )->findDrop( e->pos(), parent, afterme );
+
+ KopeteMetaContactLVI *dest_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(afterme);
+
+ if( const_cast<const QWidget *>( e->source() ) == this )
+ {
+ KopeteMetaContactLVI *source_metaLVI=dynamic_cast<KopeteMetaContactLVI*>(source);
+ KopeteGroupViewItem *dest_groupLVI=dynamic_cast<KopeteGroupViewItem*>(afterme);
+ Kopete::Contact *source_contact=0L;
+
+ if(source_metaLVI)
+ source_contact = source_metaLVI->contactForPoint( m_startDragPos );
+
+ if( source_metaLVI && dest_groupLVI && !source_contact)
+ { //we are moving a metacontact to another group
+ if(source_metaLVI->group() == dest_groupLVI->group())
+ return false;
+ if ( dest_groupLVI->group()->type() == Kopete::Group::Temporary )
+ return false;
+ // if(source_metaLVI->metaContact()->isTemporary())
+ // return false;
+ return true;
+ }
+ else if(source_metaLVI && !dest_metaLVI && !dest_groupLVI && !source_contact)
+ { //we are moving a metacontact to toplevel
+ if ( source_metaLVI->group()->type() == Kopete::Group::TopLevel )
+ return false;
+ // if(source_metaLVI->metaContact()->isTemporary())
+ // return false;
+
+ return true;
+ }
+ else if(source_contact && dest_metaLVI) //we are moving a contact to another metacontact
+ {
+ if(source_contact->metaContact() == dest_metaLVI->metaContact() )
+ return false;
+ if(dest_metaLVI->metaContact()->isTemporary())
+ return false;
+ return true;
+ }
+/* else if(source_groupLVI && dest_groupLVI) //we are moving a group to another group
+ {
+ if(dest_groupLVI->group() == Kopete::Group::temporary)
+ return false;
+ if(source_groupLVI->group() == Kopete::Group::temporary)
+ return false;
+ if(source_groupLVI->group()->parentGroup() == dest_groupLVI->group() )
+ return false;
+ Kopete::Group *g=dest_groupLVI->group()->parentGroup();
+ while(g && g != Kopete::Group::toplevel)
+ {
+ if(g==source_groupLVI->group())
+ return false;
+ g=g->parentGroup();
+ }
+ return true;
+ }
+ else if(source_groupLVI && !dest_groupLVI && dest_metaLVI) //we are moving a group to toplevel
+ {
+ if(source_groupLVI->group() == Kopete::Group::temporary)
+ return false;
+ if(source_groupLVI->group()->parentGroup() == Kopete::Group::toplevel)
+ return false;
+ return true;
+ }*/
+ }
+ else
+ {
+ if( e->provides( "text/uri-list" ) )
+ {
+ //we are sending a file (or dragging from the chat view)
+ if( dest_metaLVI )
+ {
+ QPoint p=contentsToViewport(e->pos());
+ int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) +
+ treeStepSize() * ( dest_metaLVI->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() );
+ int py = p.y() - itemRect( dest_metaLVI ).y();
+ Kopete::Contact *c = dest_metaLVI->contactForPoint( QPoint( px, py ) ) ;
+
+ if( c ? !c->isReachable() : !dest_metaLVI->metaContact()->isReachable() )
+ return false; //If the pointed contact is not reachable, abort
+
+ if( c ? c->canAcceptFiles() : dest_metaLVI->metaContact()->canAcceptFiles() )
+ {
+ // If the pointed contact, or the metacontact if no contact are
+ // pointed can accept file, return true in everycase
+ return true;
+ }
+ }
+
+ if ( !QUriDrag::canDecode(e) )
+ return false;
+
+ KURL::List urlList;
+ KURLDrag::decode( e, urlList );
+
+ for ( KURL::List::Iterator it = urlList.begin(); it != urlList.end(); ++it )
+ {
+ if( (*it).protocol() != QString::fromLatin1("kopetemessage") && (*it).isLocalFile() )
+ return false; //we can't send links if a locale file is in link
+ }
+
+ return true; //we will send a link
+ }
+ else if( e->provides( "application/x-qlistviewitem" ) )
+ {
+ //Coming from chat members
+ return true;
+ }
+ else
+ {
+ QString text;
+ QTextDrag::decode(e, text);
+ kdDebug(14000) << k_funcinfo << "drop with mimetype:" << e->format() << " data as text:" << text << endl;
+ }
+
+ }
+
+ return false;
+}
+
+void KopeteContactListView::findDrop(const QPoint &pos, QListViewItem *&parent,
+ QListViewItem *&after)
+{
+ //Since KDE 3.1.1 , the original find Drop return 0L for afterme if the group is open.
+ //This woraround allow us to keep the highlight of the item, and give always a correct position
+ parent=0L;
+ QPoint p (contentsToViewport(pos));
+ after=itemAt(p);
+}
+
+
+void KopeteContactListView::contentsMousePressEvent( QMouseEvent *e )
+{
+ KListView::contentsMousePressEvent( e );
+ if (e->button() == LeftButton )
+ {
+ QPoint p=contentsToViewport(e->pos());
+ QListViewItem *i=itemAt( p );
+ if( i )
+ {
+ //Maybe we are starting a drag?
+ //memorize the position to know later if the user move a small contacticon
+
+ int px = p.x() - ( header()->sectionPos( header()->mapToIndex( 0 ) ) +
+ treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() );
+ int py = p.y() - itemRect( i ).y();
+
+ m_startDragPos = QPoint( px , py );
+ }
+ }
+}
+
+void KopeteContactListView::slotNewMessageEvent(Kopete::MessageEvent *event)
+{
+ Kopete::Message msg=event->message();
+ //only for single chat
+ if(msg.from() && msg.to().count()==1)
+ {
+ Kopete::MetaContact *m=msg.from()->metaContact();
+ if(!m)
+ return;
+
+ for ( QListViewItem *item = firstChild(); item; item = nextItem(item) )
+ if ( KopeteMetaContactLVI *li = dynamic_cast<KopeteMetaContactLVI*>(item) )
+ if ( li->metaContact() == m )
+ li->catchEvent(event);
+ }
+}
+
+QDragObject *KopeteContactListView::dragObject()
+{
+ // Discover what the drag started on.
+ // If it's a MetaContactLVI, it was either on the MCLVI itself
+ // or on one of the child contacts
+ // Once we know this,
+ // we set the pixmap for the drag to the MC's pixmap
+ // or the child contact's small icon
+
+ QListViewItem *currentLVI = currentItem();
+ if( !currentLVI )
+ return 0L;
+
+ KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI*>( currentLVI );
+ if( !metaLVI )
+ return 0L;
+
+ QPixmap pm;
+ Kopete::Contact *c = metaLVI->contactForPoint( m_startDragPos );
+ KMultipleDrag *drag = new KMultipleDrag( this );
+ drag->addDragObject( new QStoredDrag("application/x-qlistviewitem", 0L ) );
+
+ QStoredDrag *d = new QStoredDrag("kopete/x-metacontact", 0L );
+ d->setEncodedData( metaLVI->metaContact()->metaContactId().utf8() );
+ drag->addDragObject( d );
+
+ if ( c ) // dragging a contact
+ {
+ QStoredDrag *d = new QStoredDrag("kopete/x-contact", 0L );
+ d->setEncodedData( QString( c->protocol()->pluginId() +QChar( 0xE000 )+ c->account()->accountId() +QChar( 0xE000 )+ c->contactId() ).utf8() );
+ drag->addDragObject( d );
+
+ pm = c->onlineStatus().iconFor( c, 12 ); // FIXME: fixed icon scaling
+ }
+ else // dragging a metacontact
+ {
+ // FIXME: first start at rendering the whole MC incl small icons
+ // into a pixmap to drag - anyone know how to complete this?
+ //QPainter p( pm );
+ //source_metaLVI->paintCell( p, cg?, width(), 0, 0 );
+ pm = SmallIcon( metaLVI->metaContact()->statusIcon() );
+ }
+
+ KABC::Addressee address = KABC::StdAddressBook::self()->findByUid(
+ metaLVI->metaContact()->metaContactId()
+ );
+
+ if( !address.isEmpty() )
+ {
+ drag->addDragObject( new QTextDrag( address.fullEmail(), 0L ) );
+ KABC::VCardConverter converter;
+ QString vcard = converter.createVCard( address );
+ if( !vcard.isNull() )
+ {
+ QStoredDrag *vcardDrag = new QStoredDrag("text/x-vcard", 0L );
+ vcardDrag->setEncodedData( vcard.utf8() );
+ drag->addDragObject( vcardDrag );
+ }
+ }
+
+ //QSize s = pm.size();
+ drag->setPixmap( pm /*, QPoint( s.width() , s.height() )*/ );
+
+ return drag;
+}
+
+void KopeteContactListView::slotViewSelectionChanged()
+{
+ QPtrList<Kopete::MetaContact> contacts;
+ QPtrList<Kopete::Group> groups;
+
+ m_selectedContacts.clear();
+ m_selectedGroups.clear();
+
+ QListViewItemIterator it( this );
+ while ( it.current() )
+ {
+ QListViewItem *item = it.current();
+ ++it;
+
+ if ( item->isSelected() )
+ {
+ KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(item);
+ if(metaLVI)
+ {
+ m_selectedContacts.append( metaLVI );
+ if(!contacts.contains(metaLVI->metaContact()))
+ contacts.append( metaLVI->metaContact() );
+ }
+ KopeteGroupViewItem *groupLVI=dynamic_cast<KopeteGroupViewItem*>(item);
+ if(groupLVI)
+ {
+ m_selectedGroups.append( groupLVI );
+ if(!groups.contains(groupLVI->group()))
+ groups.append( groupLVI->group() );
+
+ }
+ }
+ }
+
+ // will cause slotListSelectionChanged to be called to update our actions.
+ Kopete::ContactList::self()->setSelectedItems(contacts , groups);
+}
+
+void KopeteContactListView::slotListSelectionChanged()
+{
+ QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts();
+ QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->selectedGroups();
+
+ //TODO: update the list to select the items that should be selected.
+ // make sure slotViewSelectionChanged is *not* called.
+ updateActionsForSelection( contacts, groups );
+}
+
+void KopeteContactListView::updateActionsForSelection(
+ QPtrList<Kopete::MetaContact> contacts, QPtrList<Kopete::Group> groups )
+{
+ bool singleContactSelected = groups.isEmpty() && contacts.count() == 1;
+ bool inkabc=false;
+ if(singleContactSelected)
+ {
+ QString kabcid=contacts.first()->metaContactId();
+ inkabc= !kabcid.isEmpty() && !kabcid.contains(":");
+ }
+
+ actionSendFile->setEnabled( singleContactSelected && contacts.first()->canAcceptFiles());
+ actionAddContact->setEnabled( singleContactSelected && !contacts.first()->isTemporary());
+ actionSendEmail->setEnabled( inkabc );
+
+ if( singleContactSelected )
+ {
+ actionRename->setText(i18n("Rename Contact"));
+ actionRemove->setText(i18n("Remove Contact"));
+ actionSendMessage->setText(i18n("Send Single Message..."));
+ actionRename->setEnabled(true);
+ actionRemove->setEnabled(true);
+ actionAddContact->setText(i18n("&Add Subcontact"));
+ actionAddContact->setEnabled(!contacts.first()->isTemporary());
+ }
+ else if( groups.count() == 1 && contacts.isEmpty() )
+ {
+ actionRename->setText(i18n("Rename Group"));
+ actionRemove->setText(i18n("Remove Group"));
+ actionSendMessage->setText(i18n("Send Message to Group"));
+ actionRename->setEnabled(true);
+ actionRemove->setEnabled(true);
+ actionSendMessage->setEnabled(true);
+ actionAddContact->setText(i18n("&Add Contact to Group"));
+ actionAddContact->setEnabled(groups.first()->type()==Kopete::Group::Normal);
+ }
+ else
+ {
+ actionRename->setText(i18n("Rename"));
+ actionRemove->setText(i18n("Remove"));
+ actionRename->setEnabled(false);
+ actionRemove->setEnabled(contacts.count()+groups.count());
+ actionAddContact->setEnabled(false);
+ }
+
+ actionMove->setCurrentItem( -1 );
+ actionCopy->setCurrentItem( -1 );
+
+ actionProperties->setEnabled( ( groups.count() + contacts.count() ) == 1 );
+}
+
+void KopeteContactListView::slotSendMessage()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ Kopete::Group *group = Kopete::ContactList::self()->selectedGroups().first();
+ if(m)
+ m->sendMessage();
+ else
+ if(group)
+ group->sendMessage();
+}
+
+void KopeteContactListView::slotStartChat()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if(m)
+ m->startChat();
+}
+
+void KopeteContactListView::slotSendFile()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if(m)
+ m->sendFile(KURL());
+}
+
+ void KopeteContactListView::slotSendEmail()
+{
+ //I borrowed this from slotSendMessage
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if ( !m->metaContactId().isEmpty( ) ) // check if in kabc
+ {
+ KABC::Addressee addressee = KABC::StdAddressBook::self()->findByUid( m->metaContactId() );
+ if ( !addressee.isEmpty() )
+ {
+ QString emailAddr = addressee.fullEmail();
+
+ kdDebug( 14000 ) << "Email: " << emailAddr << "!" << endl;
+ if ( !emailAddr.isEmpty() )
+ kapp->invokeMailer( emailAddr, QString::null );
+ else
+ KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "There is no email address set for this contact in the KDE address book." ), i18n( "No Email Address in Address Book" ) );
+ }
+ else
+ KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "This contact was not found in the KDE address book. Check that a contact is selected in the properties dialog." ), i18n( "Not Found in Address Book" ) );
+ }
+ else
+ KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "This contact is not associated with a KDE address book entry, where the email address is stored. Check that a contact is selected in the properties dialog." ), i18n( "Not Found in Address Book" ) );
+}
+
+void KopeteContactListView::slotMoveToGroup()
+{
+ KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(currentItem());
+ if(!metaLVI)
+ return;
+ Kopete::MetaContact *m=metaLVI->metaContact();
+ Kopete::Group *g=metaLVI->group();
+
+ //FIXME What if two groups have the same name?
+ Kopete::Group *to = actionMove->currentItem() ?
+ Kopete::ContactList::self()->findGroup( actionMove->currentText() ) :
+ Kopete::Group::topLevel();
+
+ if( !to || to->type() == Kopete::Group::Temporary )
+ return;
+
+ if(m->isTemporary())
+ {
+ if( KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Would you like to add this contact to your contact list?</qt>" ),
+ i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"),
+ "addTemporaryWhenMoving" ) == KMessageBox::Yes )
+ {
+ m->setTemporary(false,to);
+
+ insertUndoItem( new UndoItem( UndoItem::MetaContactAdd , m ) );
+ }
+ }
+ else if( !m->groups().contains( to ) )
+ {
+ m->moveToGroup( g, to );
+
+ insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , m , to ) );
+
+ UndoItem *u=new UndoItem( UndoItem::MetaContactRemove, m, g );
+ u->isStep=false;
+ insertUndoItem(u);
+ }
+
+ actionMove->setCurrentItem( -1 );
+}
+
+void KopeteContactListView::slotCopyToGroup()
+{
+ Kopete::MetaContact *m =
+ Kopete::ContactList::self()->selectedMetaContacts().first();
+
+ if(!m)
+ return;
+
+ //FIXME! what if two groups have the same name?
+ Kopete::Group *to = actionCopy->currentItem() ?
+ Kopete::ContactList::self()->findGroup( actionCopy->currentText() ) :
+ Kopete::Group::topLevel();
+
+ if( !to || to->type() == Kopete::Group::Temporary )
+ return;
+
+ if( m->isTemporary() )
+ return;
+
+ if( !m->groups().contains( to ) )
+ {
+ m->addToGroup( to );
+
+ insertUndoItem( new UndoItem( UndoItem::MetaContactCopy , m , to ) );
+ }
+
+ actionCopy->setCurrentItem( -1 );
+}
+
+
+
+void KopeteContactListView::slotRemove()
+{
+ QPtrList<Kopete::MetaContact> contacts = Kopete::ContactList::self()->selectedMetaContacts();
+ QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->selectedGroups();
+
+ if(groups.count() + contacts.count() == 0)
+ return;
+
+ QStringList items;
+ for( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ if(!it->displayName().isEmpty())
+ items.append( it->displayName() );
+ }
+ for( Kopete::MetaContact *it = contacts.first(); it; it = contacts.next() )
+ {
+ if(!it->displayName().isEmpty() )
+ items.append( it->displayName() );
+ }
+
+ if( items.count() <= 1 )
+ {
+ // we are deleting an empty contact
+ QString msg;
+ if( !contacts.isEmpty() )
+ {
+ msg = i18n( "<qt>Are you sure you want to remove the contact <b>%1</b>" \
+ " from your contact list?</qt>" )
+ .arg( contacts.first()->displayName() ) ;
+ }
+ else if( !groups.isEmpty() )
+ {
+ msg = i18n( "<qt>Are you sure you want to remove the group <b>%1</b> " \
+ "and all contacts that are contained within it?</qt>" )
+ .arg( groups.first()->displayName() );
+ }
+ else
+ return; // this should never happen
+
+ if( KMessageBox::warningContinueCancel( this, msg, i18n( "Remove" ), KGuiItem(i18n("Remove"),"editdelete") ,
+ "askRemovingContactOrGroup" , KMessageBox::Notify | KMessageBox::Dangerous ) !=
+ KMessageBox::Continue )
+ {
+ return;
+ }
+ }
+ else
+ {
+ QString msg = groups.isEmpty() ?
+ i18n( "Are you sure you want to remove these contacts " \
+ "from your contact list?" ) :
+ i18n( "Are you sure you want to remove these groups and " \
+ "contacts from your contact list?" );
+
+ if( KMessageBox::warningContinueCancelList( this, msg, items, i18n("Remove"),
+ KGuiItem(i18n("Remove"),"editdelete"), "askRemovingContactOrGroup",
+ KMessageBox::Notify | KMessageBox::Dangerous ) != KMessageBox::Continue )
+ {
+ return;
+ }
+ }
+
+ bool undo_step=true; //only the first undo item we will add will be a step
+
+ for( Kopete::MetaContact *mc = contacts.first(); mc; mc = contacts.next() )
+ {
+ if(mc->groups().count()==1 || mc->isTemporary() )
+ Kopete::ContactList::self()->removeMetaContact( mc );
+ else
+ {
+ //try to guess from what group we are removing that contact.
+ QListViewItemIterator lvi_it( this );
+ while ( lvi_it.current() )
+ {
+ QListViewItem *item = lvi_it.current();
+ ++lvi_it;
+
+ if ( item->isSelected() )
+ {
+ KopeteMetaContactLVI *metaLVI=dynamic_cast<KopeteMetaContactLVI*>(item);
+ if(metaLVI && metaLVI->metaContact() == mc )
+ {
+ if(mc->groups().count()==1)
+ {
+ Kopete::ContactList::self()->removeMetaContact( mc );
+ break;
+ }
+ else
+ {
+ mc->removeFromGroup(metaLVI->group());
+ insertUndoItem( new UndoItem( UndoItem::MetaContactRemove , mc , metaLVI->group() ) );
+ m_undo->isStep=undo_step; //if there is several selected contacts.
+ undo_step=false;
+ }
+ //let's continue, it's possible this contact is selected several times
+ }
+ }
+ }
+ }
+ }
+
+ for( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ QPtrList<Kopete::MetaContact> list = it->members();
+ for( list.first(); list.current(); list.next() )
+ {
+ Kopete::MetaContact *mc = list.current();
+ if(mc->groups().count()==1)
+ Kopete::ContactList::self()->removeMetaContact(mc);
+ else
+ mc->removeFromGroup(it);
+ }
+
+ if( !it->members().isEmpty() )
+ {
+ kdDebug(14000) << "KopeteContactListView::slotRemove(): "
+ << "all subMetaContacts are not removed... Aborting" << endl;
+ continue;
+ }
+
+ Kopete::ContactList::self()->removeGroup( it );
+ }
+}
+
+void KopeteContactListView::slotRename()
+{
+ if ( KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( currentItem() ) )
+ {
+ metaLVI->setRenameEnabled( 0, true);
+ metaLVI->startRename( 0 );
+ }
+ else if ( KopeteGroupViewItem *groupLVI = dynamic_cast<KopeteGroupViewItem *>( currentItem() ) )
+ {
+ if ( !KopetePrefs::prefs()->sortByGroup() )
+ return;
+ groupLVI->setRenameEnabled( 0, true);
+ groupLVI->startRename( 0 );
+ }
+}
+
+void KopeteContactListView::slotAddContact()
+{
+ if( !sender() )
+ return;
+
+ Kopete::MetaContact *metacontact =
+ Kopete::ContactList::self()->selectedMetaContacts().first();
+ Kopete::Group *group =
+ Kopete::ContactList::self()->selectedGroups().first();
+ Kopete::Account *account = dynamic_cast<Kopete::Account*>( sender()->parent() );
+
+ if ( ( metacontact && metacontact->isTemporary() ) ||
+ (group && group->type()!=Kopete::Group::Normal ) )
+ return;
+
+
+ if( account && ( metacontact || group) )
+ {
+ KDialogBase *addDialog = new KDialogBase( this, "addDialog", true,
+ i18n( "Add Contact" ), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, true );
+
+ AddContactPage *addContactPage =
+ account->protocol()->createAddContactWidget( addDialog, account );
+
+ if (!addContactPage)
+ {
+ kdDebug(14000) << k_funcinfo <<
+ "Error while creating addcontactpage" << endl;
+ }
+ else
+ {
+ addDialog->setMainWidget( addContactPage );
+ if( addDialog->exec() == QDialog::Accepted )
+ {
+ if( addContactPage->validateData() )
+ {
+ if(!metacontact)
+ {
+ metacontact = new Kopete::MetaContact();
+ metacontact->addToGroup( group );
+ if (addContactPage->apply( account, metacontact ))
+ {
+ Kopete::ContactList::self()->addMetaContact( metacontact );
+ }
+ else
+ {
+ delete metacontact;
+ }
+ }
+ else
+ {
+ addContactPage->apply( account, metacontact );
+ }
+ }
+ }
+ }
+ addDialog->deleteLater();
+ }
+}
+
+void KopeteContactListView::slotAddTemporaryContact()
+{
+ Kopete::MetaContact *metacontact =
+ Kopete::ContactList::self()->selectedMetaContacts().first();
+ if( metacontact )
+ {
+/* int r=KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Would you like to add this contact to your contact list?</qt>" ),
+ i18n( "Kopete" ), i18n("Add"), i18n("Do Not Add"),
+ "addTemporaryWhenMoving" );
+
+ if(r==KMessageBox::Yes)*/
+ if(metacontact->isTemporary() )
+ metacontact->setTemporary( false );
+ }
+}
+
+void KopeteContactListView::slotProperties()
+{
+// kdDebug(14000) << k_funcinfo << "Called" << endl;
+
+ KopeteMetaContactLVI *metaLVI =
+ dynamic_cast<KopeteMetaContactLVI *>( currentItem() );
+ KopeteGroupViewItem *groupLVI =
+ dynamic_cast<KopeteGroupViewItem *>( currentItem() );
+
+ if(metaLVI)
+ {
+
+ KopeteMetaLVIProps *propsDialog =
+ new KopeteMetaLVIProps( metaLVI, 0L, "propsDialog" );
+
+ propsDialog->exec(); // modal
+ delete propsDialog;
+
+ /*
+ if( metaLVI->group()->useCustomIcon() )
+ {
+ metaLVI->updateCustomIcons( mShowAsTree );
+ }
+ else
+ {
+ }
+ */
+ metaLVI->repaint();
+ }
+ else if(groupLVI)
+ {
+ KopeteGVIProps *propsDialog =
+ new KopeteGVIProps( groupLVI, 0L, "propsDialog");
+
+ propsDialog->exec(); // modal
+ delete propsDialog;
+
+ groupLVI->updateIcon();
+ }
+}
+
+void KopeteContactListView::slotItemRenamed( QListViewItem */*item*/ )
+{
+ //everithing is now done in KopeteMetaContactLVI::rename
+
+/* KopeteMetaContactLVI *metaLVI = dynamic_cast<KopeteMetaContactLVI *>( item );
+ Kopete::MetaContact *m= metaLVI ? metaLVI->metaContact() : 0L ;
+ if ( m )
+ {
+ m->setDisplayName( metaLVI->text( 0 ) );
+ }
+ else
+ {
+ //group are handled differently in KopeteGroupViewItem
+ // kdWarning( 14000 ) << k_funcinfo << "Unknown list view item '" << item
+ // << "' renamed, ignoring item" << endl;
+ }
+ */
+}
+
+void KopeteContactListView::insertUndoItem( KopeteContactListView::UndoItem *u)
+{
+ u->next=m_undo;
+ m_undo=u;
+ actionUndo->setEnabled(true);
+ while(m_redo)
+ {
+ UndoItem *i=m_redo->next;
+ delete m_redo;
+ m_redo=i;
+ }
+ actionRedo->setEnabled(false);
+ undoTimer.start(10*60*1000);
+}
+
+
+void KopeteContactListView::slotUndo()
+{
+ bool step = false;
+ while(m_undo && !step)
+ {
+ bool success=false;
+ switch (m_undo->type)
+ {
+ case UndoItem::MetaContactAdd:
+ {
+ Kopete::MetaContact *m=m_undo->metacontact;
+ if(m)
+ {
+ m->setTemporary(true);
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactCopy:
+ {
+ Kopete::MetaContact *m=m_undo->metacontact;
+ Kopete::Group *to=m_undo->group;
+ if( m && to )
+ {
+ m->removeFromGroup( to );
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactRemove:
+ {
+ Kopete::MetaContact *m=m_undo->metacontact;
+ Kopete::Group *g=m_undo->group;
+ if( m && g )
+ {
+ m->addToGroup( g );
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactRename:
+ {
+ Kopete::MetaContact *m=m_undo->metacontact;
+ if( m )
+ {
+ // make a copy
+ QStringList undoArgs = m_undo->args;
+ Kopete::MetaContact::PropertySource undoSource = m_undo->nameSource;
+ // set undo undo
+ // set the source first
+ m_undo->nameSource = m->displayNameSource();
+ if ( m->displayNameSource() == Kopete::MetaContact::SourceCustom )
+ {
+ m_undo->args[0] = m->customDisplayName();
+ }
+ else if ( m->displayNameSource() == Kopete::MetaContact::SourceContact )
+ {
+ Kopete::Contact* c = m->displayNameSourceContact();
+ m_undo->args[0] = c->contactId();
+ m_undo->args[1] = c->protocol()->pluginId();
+ m_undo->args[2] = c->account()->accountId();
+ }
+ // source kabc requires no arguments
+
+ // do the undo
+ if ( undoSource == Kopete::MetaContact::SourceContact )
+ { // do undo
+ Kopete::Contact *c = Kopete::ContactList::self()->findContact( undoArgs[1], undoArgs[2], undoArgs[0]);
+ if (!c)
+ {
+ success=false;
+ break;
+ }
+ // do undo
+ m->setDisplayNameSourceContact(c);
+ m->setDisplayNameSource(Kopete::MetaContact::SourceContact);
+ }
+ else if ( undoSource == Kopete::MetaContact::SourceCustom )
+ {
+ m->setDisplayName(undoArgs[0]);
+ m->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ }
+ else if ( undoSource == Kopete::MetaContact::SourceKABC )
+ {
+ m->setDisplayNameSource(Kopete::MetaContact::SourceKABC);
+ }
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::GroupRename:
+ {
+ if( m_undo->group )
+ {
+ const QString old=m_undo->group->displayName();
+ m_undo->group->setDisplayName( m_undo->args[0] );
+ m_undo->args[0]=old;
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactChange:
+ {
+ Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_undo->args[0] , m_undo->args[1], m_undo->args[2] ) ;
+ if(c)
+ {
+ success=true;
+ if(m_undo->metacontact)
+ c->setMetaContact(m_undo->metacontact);
+ else
+ {
+ Kopete::MetaContact *m=new Kopete::MetaContact;
+ m->addToGroup(m_undo->group);
+ m->setDisplayName(m_undo->args[3]);
+ c->setMetaContact(m);
+ Kopete::ContactList::self()->addMetaContact(m);
+ }
+ m_undo->metacontact=c->metaContact(); //for the redo
+ }
+ break;
+ }
+ case UndoItem::ContactAdd:
+ {
+ Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_undo->args[0] , m_undo->args[1], m_undo->args[2] ) ;
+ if(c)
+ {
+ success=true;
+ Kopete::MetaContact *m=new Kopete::MetaContact;
+ m->setTemporary(true);
+ c->setMetaContact(m);
+ Kopete::ContactList::self()->addMetaContact(m);
+ m_undo->metacontact=c->metaContact();
+ }
+ break;
+ }
+ }
+
+ if(success) //the undo item has been correctly performed
+ {
+ step=m_undo->isStep;
+ UndoItem *u=m_undo->next;
+ m_undo->next=m_redo;
+ m_redo=m_undo;
+ m_undo=u;
+ }
+ else //something has been corrupted, clear all undo items
+ {
+ while(m_undo)
+ {
+ UndoItem *u=m_undo->next;
+ delete m_undo;
+ m_undo=u;
+ }
+ }
+ }
+ actionUndo->setEnabled(m_undo);
+ actionRedo->setEnabled(m_redo);
+ undoTimer.start(10*60*1000);
+}
+
+void KopeteContactListView::slotRedo()
+{
+ bool step = false;
+ while(m_redo && (!step || !m_redo->isStep ))
+ {
+ bool success=false;
+ switch (m_redo->type)
+ {
+ case UndoItem::MetaContactAdd:
+ {
+ Kopete::MetaContact *m=m_redo->metacontact;
+ if(m && m_redo->group)
+ {
+ m->setTemporary(false,m_redo->group);
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactCopy:
+ {
+ Kopete::MetaContact *m=m_redo->metacontact;
+ Kopete::Group *to=m_redo->group;
+ if( m && to )
+ {
+ m->addToGroup( to );
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactRemove:
+ {
+ Kopete::MetaContact *m=m_redo->metacontact;
+ Kopete::Group *g=m_redo->group;
+ if( m && g )
+ {
+ m->removeFromGroup( g );
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactRename:
+ {
+ /*
+ Kopete::MetaContact *m=m_redo->metacontact;
+ if( m )
+ {
+ const QString old=m->displayName();
+ if( m_redo->args[1].isEmpty() )
+ {
+ const QString name = m_redo->args[0];
+ m_redo->args[0] = m->displayNameSource()->contactId();
+ m_redo->args[1] = m->displayNameSource()->protocol()->pluginId();
+ m_redo->args[2] = m->displayNameSource()->account()->accountId();
+ m->setDisplayName( name );
+ }
+ else
+ {
+ const QString oldName = m->displayName();
+ QPtrList< Kopete::Contact > cList = m->contacts();
+ QPtrListIterator< Kopete::Contact > it (cList);
+ Kopete::Contact *c = Kopete::ContactList::self()->findContact( args[0], args[2], args[1]);
+ if ( !c)
+ return;
+ m->setNameSourceContact(c);
+ break;
+
+ m_redo->args[0] = oldName;
+ m_redo->args[1] = "";
+ m_redo->args[2] = "";
+ }
+ success=true;
+ }
+ */ //Why is this code commented ? - Olivier 2006-04
+ break;
+ }
+ case UndoItem::GroupRename:
+ {
+ if( m_redo->group )
+ {
+ const QString old=m_redo->group->displayName();
+ m_redo->group->setDisplayName( m_redo->args[0] );
+ m_redo->args[0]=old;
+ success=true;
+ }
+ break;
+ }
+ case UndoItem::MetaContactChange:
+ case UndoItem::ContactAdd:
+ {
+ Kopete::Contact *c=Kopete::ContactList::self()->findContact(m_redo->args[0] , m_redo->args[1], m_redo->args[2] ) ;
+ if(c && m_redo->metacontact)
+ {
+ success=true;
+ c->setMetaContact(m_redo->metacontact);
+ m_redo->metacontact=c->metaContact();
+ }
+ break;
+ }
+ }
+
+ if(success) //the undo item has been correctly performed
+ {
+ step=true;
+ UndoItem *u=m_redo->next;
+ m_redo->next=m_undo;
+ m_undo=m_redo;
+ m_redo=u;
+ }
+ else //something has been corrupted, clear all undo items
+ {
+ while(m_redo)
+ {
+ UndoItem *u=m_redo->next;
+ delete m_redo;
+ m_redo=u;
+ }
+ }
+ }
+ actionUndo->setEnabled(m_undo);
+ actionRedo->setEnabled(m_redo);
+ undoTimer.start(10*60*1000);
+}
+
+void KopeteContactListView::slotTimeout()
+{
+ undoTimer.stop();
+
+ //we will keep one (complete) undo action
+ UndoItem *Sdel=m_undo;
+ while(Sdel && !Sdel->isStep)
+ Sdel=Sdel->next;
+
+ if(Sdel) while( Sdel->next )
+ {
+ UndoItem *u=Sdel->next->next;
+ delete Sdel->next;
+ Sdel->next=u;
+ }
+ actionUndo->setEnabled(m_undo);
+ while(m_redo)
+ {
+ UndoItem *i=m_redo->next;
+ delete m_redo;
+ m_redo=i;
+ }
+ actionRedo->setEnabled(false);
+}
+
+#include "kopetecontactlistview.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/contactlist/kopetecontactlistview.h b/kopete/kopete/contactlist/kopetecontactlistview.h
new file mode 100644
index 00000000..43c60ebe
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetecontactlistview.h
@@ -0,0 +1,252 @@
+/*
+ kopetecontactlistview.h
+
+ Kopete Contactlist GUI
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@usinternet.com>
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_CONTACTLISTVIEW_H
+#define KOPETE_CONTACTLISTVIEW_H
+
+#include "kopetelistview.h"
+#include "kopetemetacontact.h"
+
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qstringlist.h>
+#include <qrect.h>
+#include <qtimer.h>
+#include <qguardedptr.h>
+
+class KopeteMetaContactLVI;
+class KopeteGroupViewItem;
+class KopeteStatusGroupViewItem;
+class KRootPixmap;
+class KActionCollection;
+class KAction;
+class KListAction;
+class KActionMenu;
+
+class KopeteContactListViewPrivate;
+
+namespace Kopete
+{
+class Contact;
+class MetaContact;
+class Group;
+class MessageEvent;
+}
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ */
+class KopeteContactListView : public Kopete::UI::ListView::ListView
+{
+ Q_OBJECT
+
+public:
+ KopeteContactListView( QWidget *parent = 0, const char *name = 0 );
+ ~KopeteContactListView();
+
+ /**
+ * Init MetaContact related actions
+ */
+ void initActions(KActionCollection*);
+
+ /**
+ * Add a given group name and return it
+ */
+ void addGroup( const QString &groupName );
+
+ /**
+ * Are we displaying as a tree view (true), or in a flat list (false)?
+ * @todo make this an enum
+ */
+ bool showAsTree() { return mShowAsTree; }
+
+public slots:
+ /**
+ * Remove all KopeteMetaContactLVI of a metaContact
+ */
+ void removeContact( Kopete::MetaContact *contact );
+
+ /**
+ * Prompt the user for the group name (slot)
+ */
+ void addGroup();
+
+protected:
+ virtual void contentsMousePressEvent( QMouseEvent *e );
+
+ virtual bool acceptDrag(QDropEvent *e) const;
+
+ /**
+ * Start a drag operation
+ * @return a KMultipleDrag containing: 1) A QStoredDrag of type "application/x-qlistviewitem", 2) If the MC is associated with a KABC entry, i) a QTextDrag containing their email address, and ii) their vCard representation.
+ */
+ virtual QDragObject *dragObject();
+
+ /**
+ * Since KDE 3.1.1 , the original find Drop return 0L for afterme if the group is open.
+ * This woraround allow us to keep the highlight of the item, and give always a correct position
+ */
+ virtual void findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after);
+
+ /**
+ * The selected items have changed; update our actions to show what's possible.
+ */
+ void updateActionsForSelection( QPtrList<Kopete::MetaContact> contacts, QPtrList<Kopete::Group> groups );
+
+private slots:
+ /**
+ * When an account is added, so we add it to the menu action
+ */
+ void slotAddSubContactActionNewAccount(Kopete::Account*);
+ /**
+ * When an account is destroyed, the child add subcontact action is deleted
+ * so we remove it from the menu action
+ */
+ void slotAddSubContactActionAccountDeleted(const Kopete::Account *);
+
+ void slotViewSelectionChanged();
+ void slotListSelectionChanged();
+ void slotContextMenu(KListView*,QListViewItem *item, const QPoint &point );
+ void slotExpanded( QListViewItem *item );
+ void slotCollapsed( QListViewItem *item );
+
+ void slotSettingsChanged( void );
+ void slotUpdateAllGroupIcons();
+ void slotExecuted( QListViewItem *item, const QPoint &pos, int c );
+
+ void slotAddedToGroup( Kopete::MetaContact *mc, Kopete::Group *to );
+ void slotRemovedFromGroup( Kopete::MetaContact *mc, Kopete::Group *from );
+ void slotMovedToGroup( Kopete::MetaContact *mc, Kopete::Group *from, Kopete::Group *to );
+
+ /**
+ * A meta contact was added to the contact list - update the view
+ */
+ void slotMetaContactAdded( Kopete::MetaContact *mc );
+ void slotMetaContactDeleted( Kopete::MetaContact *mc );
+ void slotMetaContactSelected( bool sel );
+
+ void slotGroupAdded(Kopete::Group *);
+
+ void slotContactStatusChanged( Kopete::MetaContact *mc );
+
+ void slotDropped(QDropEvent *e, QListViewItem *parent, QListViewItem*);
+
+ void slotShowAddContactDialog();
+ void slotNewMessageEvent(Kopete::MessageEvent *);
+
+ /**
+ * Handle renamed items by renaming the meta contact
+ */
+ void slotItemRenamed( QListViewItem *item );
+
+ /** Actions related slots **/
+ void slotSendMessage();
+ void slotStartChat();
+ void slotSendFile();
+ void slotSendEmail();
+ void slotMoveToGroup();
+ void slotCopyToGroup();
+ void slotRemove();
+ void slotRename();
+ void slotAddContact();
+ void slotAddTemporaryContact();
+ void slotProperties();
+ void slotUndo();
+ void slotRedo();
+
+ void slotTimeout();
+
+private:
+ bool mShowAsTree;
+
+ // TODO: do we really need to store these?
+ QPtrList<KopeteMetaContactLVI> m_selectedContacts;
+ QPtrList<KopeteGroupViewItem> m_selectedGroups;
+
+ bool mSortByGroup;
+ KRootPixmap *root;
+
+ QRect m_onItem;
+
+ QPoint m_startDragPos;
+
+ /* ACTIONS */
+ KAction *actionSendMessage;
+ KAction *actionStartChat;
+ KAction *actionSendFile;
+ KAction *actionSendEmail;
+ KListAction *actionMove;
+ KListAction *actionCopy;
+ KAction *actionRename;
+ KAction *actionRemove;
+ KAction *actionAddTemporaryContact;
+ KAction *actionProperties;
+ KAction *actionUndo;
+ KAction *actionRedo;
+
+ KopeteContactListViewPrivate *d;
+
+ void moveDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *from, Kopete::Group *to );
+ void addDraggedContactToGroup( Kopete::MetaContact *contact, Kopete::Group *group );
+ void addDraggedContactToMetaContact( Kopete::Contact *contact, Kopete::MetaContact *parent );
+ void addDraggedContactByInfo( const QString &protocolId, const QString &accountId,
+ const QString &contactId, QListViewItem *after );
+
+public:
+ struct UndoItem;
+ UndoItem *m_undo;
+ UndoItem *m_redo;
+ void insertUndoItem(UndoItem *u);
+ QTimer undoTimer;
+
+public:
+ // This is public so the chatwinodw can handle sub actions
+ // FIXME: do we not believe in accessor functions any more?
+ KActionMenu *actionAddContact;
+ QMap<const Kopete::Account *, KAction *> m_accountAddContactMap;
+};
+
+struct KopeteContactListView::UndoItem
+{
+ enum Type { MetaContactAdd, MetaContactRemove , MetaContactCopy , MetaContactRename, MetaContactChange, ContactAdd, GroupRename } type;
+ QStringList args;
+ QGuardedPtr<Kopete::MetaContact> metacontact;
+ QGuardedPtr<Kopete::Group> group;
+ UndoItem *next;
+ bool isStep;
+ Kopete::MetaContact::PropertySource nameSource;
+
+ UndoItem() : isStep(true) {}
+ UndoItem(Type t, Kopete::MetaContact *m=0L ,Kopete::Group *g=0L)
+ {
+ isStep=true;
+ type=t;
+ metacontact=m;
+ group=g;
+ next=0L;
+ }
+};
+
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/contactlist/kopetegrouplistaction.cpp b/kopete/kopete/contactlist/kopetegrouplistaction.cpp
new file mode 100644
index 00000000..1556f9b6
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetegrouplistaction.cpp
@@ -0,0 +1,66 @@
+/*
+ kopetegrouplistcation.cpp - the action used for Move To and copy To
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2001-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+/* This code was previously in libkopete/ui/kopetestdactions.cpp */
+
+#include "kopetegrouplistaction.h"
+
+#include <kdebug.h>
+#include <kguiitem.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kwin.h>
+#include <kcmultidialog.h>
+
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+
+KopeteGroupListAction::KopeteGroupListAction( const QString &text, const QString &pix, const KShortcut &cut, const QObject *receiver,
+ const char *slot, QObject *parent, const char *name )
+: KListAction( text, pix, cut, parent, name )
+{
+ connect( this, SIGNAL( activated() ), receiver, slot );
+
+ connect( Kopete::ContactList::self(), SIGNAL( groupAdded( Kopete::Group * ) ), this, SLOT( slotUpdateList() ) );
+ connect( Kopete::ContactList::self(), SIGNAL( groupRemoved( Kopete::Group * ) ), this, SLOT( slotUpdateList() ) );
+ connect( Kopete::ContactList::self(), SIGNAL( groupRenamed(Kopete::Group*, const QString& ) ), this, SLOT( slotUpdateList() ) );
+ slotUpdateList();
+}
+
+KopeteGroupListAction::~KopeteGroupListAction()
+{
+}
+
+void KopeteGroupListAction::slotUpdateList()
+{
+ QStringList groupList;
+
+ // Add groups to our list
+ QPtrList<Kopete::Group> groups = Kopete::ContactList::self()->groups();
+ for ( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ if(it->type() == Kopete::Group::Normal)
+ groupList.append( it->displayName() );
+ }
+
+ groupList.sort();
+ groupList.prepend(QString::null); //add a separator;
+ groupList.prepend( i18n("Top Level") ); //the top-level group, with the id 0
+ setItems( groupList );
+}
+
+#include "kopetegrouplistaction.moc"
diff --git a/kopete/kopete/contactlist/kopetegrouplistaction.h b/kopete/kopete/contactlist/kopetegrouplistaction.h
new file mode 100644
index 00000000..3576f3ed
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetegrouplistaction.h
@@ -0,0 +1,44 @@
+/*
+ kopetegrouplistaction.h
+
+ Copyright (c) 2005 Olivier Goffart <ogoffart@ kde.org>
+
+
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEGRLISTACT_H
+#define KOPETEGRLISTACT_H
+
+
+#include <kaction.h>
+
+
+/**
+ * Action used for Copy To and Move To
+ */
+class KopeteGroupListAction : public KListAction
+{
+ Q_OBJECT
+
+public:
+ KopeteGroupListAction( const QString &, const QString &, const KShortcut &,
+ const QObject *, const char *, QObject *, const char * );
+ ~KopeteGroupListAction();
+
+protected slots:
+ void slotUpdateList();
+private:
+ QStringList m_groupList;
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/kopetegroupviewitem.cpp b/kopete/kopete/contactlist/kopetegroupviewitem.cpp
new file mode 100644
index 00000000..21b1cf79
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetegroupviewitem.cpp
@@ -0,0 +1,286 @@
+/*
+ kopeteonlinestatus.cpp - Kopete Online Status
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qpainter.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kapplication.h>
+
+#include "kopetecontactlistview.h"
+#include "kopetegroupviewitem.h"
+#include "kopetegroup.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteprefs.h"
+#include "kopetemetacontactlvi.h"
+#include "kopetemetacontact.h"
+
+#include <memory>
+
+//using namespace Kopete::UI;
+
+class KopeteGroupViewItem::Private
+{
+public:
+ Kopete::UI::ListView::ImageComponent *image;
+ Kopete::UI::ListView::DisplayNameComponent *name;
+ Kopete::UI::ListView::TextComponent *count;
+ std::auto_ptr<Kopete::UI::ListView::ToolTipSource> toolTipSource;
+};
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+class GroupToolTipSource : public ToolTipSource
+{
+public:
+ GroupToolTipSource( KopeteGroupViewItem *gp )
+ : group( gp )
+ {
+ }
+ QString operator()( ComponentBase *, const QPoint &, QRect & )
+ {
+ return group->toolTip();
+ }
+private:
+ KopeteGroupViewItem *group;
+};
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+KopeteGroupViewItem::KopeteGroupViewItem( Kopete::Group *group_, QListView *parent, const char *name )
+: Kopete::UI::ListView::Item( parent, group_, name )
+{
+ m_group = group_;
+ initLVI();
+}
+
+KopeteGroupViewItem::KopeteGroupViewItem( Kopete::Group *group_, QListViewItem *parent, const char *name )
+ : Kopete::UI::ListView::Item( parent, group_, name )
+{
+ m_group = group_;
+ initLVI();
+}
+
+KopeteGroupViewItem::~KopeteGroupViewItem()
+{
+ delete d;
+}
+
+void KopeteGroupViewItem::initLVI()
+{
+ d = new Private;
+
+ d->toolTipSource.reset( new Kopete::UI::ListView::GroupToolTipSource( this ) );
+
+ using namespace Kopete::UI::ListView;
+ Component *hbox = new BoxComponent( this, BoxComponent::Horizontal );
+ d->image = new ImageComponent( hbox );
+ d->name = new DisplayNameComponent( hbox );
+ d->count = new TextComponent( hbox );
+
+ d->image->setToolTipSource( d->toolTipSource.get() );
+ d->name->setToolTipSource( d->toolTipSource.get() );
+ d->count->setToolTipSource( d->toolTipSource.get() );
+
+ connect( m_group, SIGNAL( displayNameChanged( Kopete::Group*, const QString& ) ),
+ this, SLOT( refreshDisplayName() ) );
+
+ connect( KopetePrefs::prefs(), SIGNAL( contactListAppearanceChanged() ),
+ SLOT( slotConfigChanged() ) );
+ connect( kapp, SIGNAL( appearanceChanged() ), SLOT( slotConfigChanged() ) );
+
+ connect( m_group, SIGNAL( iconAppearanceChanged() ), SLOT( updateIcon() ) );
+
+ refreshDisplayName();
+ slotConfigChanged();
+}
+
+Kopete::Group* KopeteGroupViewItem::group() const
+{
+ return m_group;
+}
+
+QString KopeteGroupViewItem::toolTip() const
+{
+ // TODO: add icon, and some more information than that which
+ // is already displayed in the list view item
+ // FIXME: post-KDE-3.3, make this better i18n-able
+ // currently it can't cause more problems than the contact list itself, at least.
+ return "<b>" + d->name->text() + "</b> " + d->count->text();
+}
+
+void KopeteGroupViewItem::slotConfigChanged()
+{
+ updateIcon();
+ updateVisibility();
+
+ d->name->setColor( KopetePrefs::prefs()->contactListGroupNameColor() );
+
+ QFont font = listView()->font();
+ if ( KopetePrefs::prefs()->contactListUseCustomFonts() )
+ font = KopetePrefs::prefs()->contactListCustomNormalFont();
+ d->name->setFont( font );
+
+ d->count->setFont( KopetePrefs::prefs()->contactListSmallFont() );
+}
+
+void KopeteGroupViewItem::refreshDisplayName()
+{
+ totalMemberCount = 0;
+ onlineMemberCount = 0;
+
+ for ( QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling() )
+ {
+ if ( KopeteMetaContactLVI *kc = dynamic_cast<KopeteMetaContactLVI*>( lvi ) )
+ {
+ totalMemberCount++;
+ if ( kc->metaContact()->isOnline() )
+ onlineMemberCount++;
+ }
+ }
+
+ d->name->setText( m_group->displayName() );
+ d->count->setText( i18n( "(NUMBER OF ONLINE CONTACTS/NUMBER OF CONTACTS IN GROUP)", "(%1/%2)" )
+ .arg( QString::number( onlineMemberCount ), QString::number( totalMemberCount ) ) );
+
+ updateVisibility();
+
+ // Sorting in this slot is extremely expensive as it's called dozens of times and
+ // the sorting itself is rather slow. Therefore we call delayedSort, which tries
+ // to group multiple sort requests into one.
+ using namespace Kopete::UI::ListView;
+ if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) )
+ lv->delayedSort();
+ else
+ listView()->sort();
+}
+
+QString KopeteGroupViewItem::key( int, bool ) const
+{
+ //Groups are placed after topLevel contact.
+ //Exepted Temporary group which is the first group
+ if ( group()->type() != Kopete::Group::Normal )
+ return "0" + d->name->text();
+ return "M" + d->name->text();
+}
+
+void KopeteGroupViewItem::startRename( int /*col*/ )
+{
+ //kdDebug(14000) << k_funcinfo << endl;
+ KListViewItem::startRename( 0 );
+}
+
+void KopeteGroupViewItem::okRename( int col )
+{
+ //kdDebug(14000) << k_funcinfo << endl;
+ KListViewItem::okRename(col);
+ setRenameEnabled( 0, false );
+}
+
+void KopeteGroupViewItem::cancelRename( int col )
+{
+ //kdDebug(14000) << k_funcinfo << endl;
+ KListViewItem::cancelRename(col);
+ setRenameEnabled( 0, false );
+}
+
+void KopeteGroupViewItem::updateVisibility()
+{
+ //FIXME: A contact can ve visible if he has a unknwon status (it's not online)
+ // or if he has an event (blinking icon). If such as contact is not with
+ // others inline contact in the group. the group will stay hidden.
+ int visibleUsers = onlineMemberCount;
+ if ( KopetePrefs::prefs()->showOffline() )
+ visibleUsers = totalMemberCount;
+
+ bool visible = KopetePrefs::prefs()->showEmptyGroups() || ( visibleUsers > 0 );
+
+ if ( isVisible() != visible )
+ {
+ setTargetVisibility( visible );
+ if ( visible )
+ {
+ // When calling setVisible(true) EVERY child item will be shown,
+ // even if they should be hidden.
+ // We just re-update the visibility of all child items
+ for ( QListViewItem *lvi = firstChild(); lvi; lvi = lvi->nextSibling() )
+ {
+ if ( KopeteMetaContactLVI *kmc = dynamic_cast<KopeteMetaContactLVI *>( lvi ) )
+ kmc->updateVisibility();
+ }
+ }
+ }
+}
+
+void KopeteGroupViewItem::updateIcon()
+{
+ // TODO: clever caching
+ if ( isOpen() )
+ {
+ if ( group()->useCustomIcon() && !group()->icon( Kopete::ContactListElement::Open ).isEmpty() )
+ open = SmallIcon( group()->icon( Kopete::ContactListElement::Open ) );
+ else
+ open = SmallIcon( KOPETE_GROUP_DEFAULT_OPEN_ICON );
+
+ d->image->setPixmap( open );
+ }
+ else
+ {
+ if ( group()->useCustomIcon() && !group()->icon( Kopete::ContactListElement::Closed ).isEmpty() )
+ closed = SmallIcon( group()->icon( Kopete::ContactListElement::Closed ) );
+ else
+ closed = SmallIcon( KOPETE_GROUP_DEFAULT_CLOSED_ICON );
+
+ d->image->setPixmap( closed );
+ }
+}
+
+QString KopeteGroupViewItem::text( int column ) const
+{
+ if ( column == 0 )
+ return d->name->text();
+ else
+ return KListViewItem::text( column );
+}
+
+void KopeteGroupViewItem::setText( int column, const QString &text )
+{
+ if ( column == 0 )
+ {
+ KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() );
+ if ( lv )
+ {
+ KopeteContactListView::UndoItem *u=new KopeteContactListView::UndoItem(KopeteContactListView::UndoItem::GroupRename, 0L, m_group);
+ u->args << m_group->displayName();
+ lv->insertUndoItem(u);
+ }
+ group()->setDisplayName( text );
+ }
+ else
+ KListViewItem::setText( column, text );
+}
+
+#include "kopetegroupviewitem.moc"
+// vim: set noet ts=4 sts=4 sw=4:
+
+
diff --git a/kopete/kopete/contactlist/kopetegroupviewitem.h b/kopete/kopete/contactlist/kopetegroupviewitem.h
new file mode 100644
index 00000000..777ea2e8
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetegroupviewitem.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+ kopetegroupviewitem.h - description
+ -------------------
+ begin : lun oct 28 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef KOPETEGROUPVIEWITEM_H
+#define KOPETEGROUPVIEWITEM_H
+
+#include "kopetelistviewitem.h"
+#include <qpixmap.h>
+
+#define KOPETE_GROUP_DEFAULT_OPEN_ICON "folder_open"
+#define KOPETE_GROUP_DEFAULT_CLOSED_ICON "folder"
+
+namespace Kopete
+{
+class Group;
+}
+
+/**
+ * @author Olivier Goffart
+ */
+class KopeteGroupViewItem : public Kopete::UI::ListView::Item
+{
+ Q_OBJECT
+public:
+ KopeteGroupViewItem( Kopete::Group *group , QListView *parent, const char *name = 0 );
+ KopeteGroupViewItem( Kopete::Group *group , QListViewItem *parent, const char *name = 0 );
+ ~KopeteGroupViewItem();
+
+ Kopete::Group * group() const;
+
+ virtual void startRename( int col );
+
+ /**
+ * reimplemented from KListViewItem to take into account our alternate text storage
+ */
+ virtual QString text( int column ) const;
+ virtual void setText( int column, const QString &text );
+
+ QString toolTip() const;
+
+public slots:
+ void refreshDisplayName();
+ void updateIcon();
+ void updateVisibility();
+
+protected:
+ virtual void okRename( int col );
+ virtual void cancelRename( int col );
+
+private:
+ void initLVI();
+
+ Kopete::Group *m_group;
+ QPixmap open, closed;
+
+ QString key( int column, bool ascending ) const;
+
+ unsigned int onlineMemberCount;
+ unsigned int totalMemberCount;
+
+ class Private;
+ Private *d;
+
+private slots:
+ void slotConfigChanged();
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/kopetegvipropswidget.ui b/kopete/kopete/contactlist/kopetegvipropswidget.ui
new file mode 100644
index 00000000..4dbb1599
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetegvipropswidget.ui
@@ -0,0 +1,156 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KopeteGVIPropsWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteGVIPropsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>220</width>
+ <height>223</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblDisplayName</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtDisplayName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>edtDisplayName</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>grpIcons</cstring>
+ </property>
+ <property name="title">
+ <string>Icons</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIconButton" row="1" column="1">
+ <property name="name">
+ <cstring>icnbOpen</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblOpen</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;pen:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbOpen</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblClosed</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;losed:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbClosed</cstring>
+ </property>
+ </widget>
+ <widget class="KIconButton" row="2" column="1">
+ <property name="name">
+ <cstring>icnbClosed</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>chkUseCustomIcons</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom &amp;icons</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacerHorizontal1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>edtDisplayName</tabstop>
+ <tabstop>chkUseCustomIcons</tabstop>
+ <tabstop>icnbOpen</tabstop>
+ <tabstop>icnbClosed</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kicondialog.h</includehint>
+ <includehint>kicondialog.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/contactlist/kopetelviprops.cpp b/kopete/kopete/contactlist/kopetelviprops.cpp
new file mode 100644
index 00000000..bf5431bf
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetelviprops.cpp
@@ -0,0 +1,570 @@
+/*
+ kopetelviprops.cpp
+
+ Kopete Contactlist Properties GUI for Groups and MetaContacts
+
+ Copyright (c) 2002-2003 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+ Copyright (c) 2004-2005 by Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetelviprops.h"
+
+#include <kdebug.h>
+
+#include <qapplication.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qtabwidget.h>
+#include <qcombobox.h>
+
+#include <kdialogbase.h>
+#include <kfiledialog.h>
+#include <kicondialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+#include <kabc/addresseedialog.h>
+#include <kabc/stdaddressbook.h>
+#include <kabc/addressee.h>
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+
+#include "kabcpersistence.h"
+#include "kopeteaddrbookexport.h"
+#include "kopetecontact.h"
+#include "kopetegroup.h"
+#include "kopetegroupviewitem.h"
+#include "kopetemetacontactlvi.h"
+#include "kopeteaccount.h"
+#include "kopeteprotocol.h"
+#include "addressbooklinkwidget.h"
+#include "addressbookselectordialog.h"
+
+#include "customnotificationprops.h"
+#include "customnotifications.h"
+
+const char MC_OFF[] = "metacontact_offline";
+const char MC_ON[] = "metacontact_online";
+const char MC_AW[] = "metacontact_away";
+const char MC_UNK[] = "metacontact_unknown";
+
+
+KopeteGVIProps::KopeteGVIProps(KopeteGroupViewItem *gvi, QWidget *parent, const char *name)
+: KDialogBase(parent, name, true, i18n("Properties of Group %1").arg(gvi->group()->displayName()), Ok|Cancel, Ok, false)
+{
+ mainWidget = new KopeteGVIPropsWidget(this, "mainWidget");
+ mainWidget->icnbOpen->setIconSize(KIcon::SizeSmall);
+ mainWidget->icnbClosed->setIconSize(KIcon::SizeSmall);
+
+ mNotificationProps = new CustomNotificationProps( this, gvi->group() );
+ mainWidget->tabWidget->addTab( mNotificationProps->widget(), i18n( "Custom &Notifications" ) );
+
+ setMainWidget(mainWidget);
+ item = gvi;
+ m_dirty = false;
+
+ mainWidget->edtDisplayName->setText( item->group()->displayName() );
+
+ mainWidget->chkUseCustomIcons->setChecked( item->group()->useCustomIcon() );
+
+ QString openName = item->group()->icon( Kopete::ContactListElement::Open );
+ if(openName.isEmpty())
+ openName = KOPETE_GROUP_DEFAULT_OPEN_ICON;
+ QString closeName = item->group()->icon( Kopete::ContactListElement::Closed );
+ if(closeName.isEmpty())
+ closeName = KOPETE_GROUP_DEFAULT_CLOSED_ICON;
+ mainWidget->icnbOpen->setIcon( openName );
+ mainWidget->icnbClosed->setIcon( closeName );
+
+ connect( this, SIGNAL(okClicked()), this, SLOT( slotOkClicked() ) );
+ connect( mainWidget->chkUseCustomIcons, SIGNAL( toggled( bool ) ),
+ this, SLOT( slotUseCustomIconsToggled( bool ) ) );
+ connect( mainWidget->icnbOpen, SIGNAL( iconChanged( QString ) ),
+ SLOT( slotIconChanged() ) );
+ connect( mainWidget->icnbClosed, SIGNAL( iconChanged( QString ) ),
+ SLOT( slotIconChanged() ) );
+ slotUseCustomIconsToggled( mainWidget->chkUseCustomIcons->isChecked() );
+}
+
+KopeteGVIProps::~KopeteGVIProps()
+{
+}
+
+void KopeteGVIProps::slotOkClicked()
+{
+ if( mainWidget->edtDisplayName->text() != item->group()->displayName() )
+ {
+ item->group()->setDisplayName( mainWidget->edtDisplayName->text() );
+ item->refreshDisplayName();
+ }
+
+ item->group()->setUseCustomIcon( mainWidget->chkUseCustomIcons->isChecked() );
+
+ // only call setIcon if the icon was changed
+ if( m_dirty )
+ {
+ item->group()->setIcon( mainWidget->icnbOpen->icon(),
+ Kopete::ContactListElement::Open );
+
+ item->group()->setIcon( mainWidget->icnbClosed->icon(),
+ Kopete::ContactListElement::Closed );
+ }
+
+ mNotificationProps->storeCurrentCustoms();
+}
+
+void KopeteGVIProps::slotUseCustomIconsToggled(bool on)
+{
+ mainWidget->lblOpen->setEnabled( on );
+ mainWidget->icnbOpen->setEnabled( on );
+ mainWidget->lblClosed->setEnabled( on );
+ mainWidget->icnbClosed->setEnabled( on );
+}
+
+void KopeteGVIProps::slotIconChanged()
+{
+ m_dirty = true;
+}
+
+// =============================================================================
+
+
+KopeteMetaLVIProps::KopeteMetaLVIProps(KopeteMetaContactLVI *lvi, QWidget *parent, const char *name)
+: KDialogBase(parent, name, true, i18n("Properties of Meta Contact %1").arg(lvi->metaContact()->displayName()), Ok|Cancel, Ok, false)
+{
+ m_countPhotoCapable = 0;
+ mainWidget = new KopeteMetaLVIPropsWidget( this, "mainWidget" );
+ mainWidget->icnbOffline->setIconSize( KIcon::SizeSmall );
+ mainWidget->icnbOnline->setIconSize( KIcon::SizeSmall );
+ mainWidget->icnbAway->setIconSize( KIcon::SizeSmall );
+ mainWidget->icnbUnknown->setIconSize( KIcon::SizeSmall );
+
+ mNotificationProps = new CustomNotificationProps( this, lvi->metaContact() );
+ // add a button to the notification props to get the sound from KABC
+ // the widget's vert box layout, horiz box layout containing button, spacer, followed by a spacer
+ QBoxLayout * vb = static_cast<QVBoxLayout*>( mNotificationProps->widget()->layout() );
+
+ QHBoxLayout* hb = new QHBoxLayout( vb, -1, "soundFromKABClayout" );
+ mFromKABC = new QPushButton( i18n( "Sync KABC..." ), mNotificationProps->widget(), "getSoundFromKABC" );
+ hb->addWidget( mFromKABC ); // [ [Button] <-xxxxx-> ]
+ hb->addStretch();
+ vb->addStretch(); // vert spacer keeps the rest snug
+
+ mainWidget->tabWidget->addTab( mNotificationProps->widget(), i18n( "Custom &Notifications" ) );
+ setMainWidget( mainWidget );
+ item = lvi;
+
+ connect( mainWidget->radioNameKABC, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->radioNameContact, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->radioNameCustom, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->radioPhotoKABC, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->radioPhotoContact, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->radioPhotoCustom, SIGNAL(toggled(bool)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->cmbPhotoUrl, SIGNAL(urlSelected(const QString &)), SLOT(slotEnableAndDisableWidgets()));
+ connect( mainWidget->cmbAccountPhoto, SIGNAL(activated ( int )), SLOT(slotEnableAndDisableWidgets()));
+
+
+ mainWidget->btnClearPhoto->setIconSet( SmallIconSet( QApplication::reverseLayout() ? "locationbar_erase" : "clear_left" ) );
+ connect( mainWidget->btnClearPhoto, SIGNAL( clicked() ), this, SLOT( slotClearPhotoClicked() ) );
+ connect( mainWidget->widAddresseeLink, SIGNAL( addresseeChanged( const KABC::Addressee & ) ), SLOT( slotAddresseeChanged( const KABC::Addressee & ) ) );
+ mainWidget->chkUseCustomIcons->setChecked( item->metaContact()->useCustomIcon() );
+
+ QString offlineName = item->metaContact()->icon( Kopete::ContactListElement::Offline );
+ if(offlineName.isEmpty())
+ offlineName = QString::fromLatin1(MC_OFF); // Default
+
+ QString onlineName = item->metaContact()->icon( Kopete::ContactListElement::Online );
+ if(onlineName.isEmpty())
+ onlineName = QString::fromLatin1(MC_ON); // Default
+
+ QString awayName = item->metaContact()->icon( Kopete::ContactListElement::Away );
+ if(awayName.isEmpty())
+ awayName = QString::fromLatin1(MC_AW); // Default
+
+ QString unknownName = item->metaContact()->icon( Kopete::ContactListElement::Unknown );
+ if(unknownName.isEmpty())
+ unknownName = QString::fromLatin1(MC_UNK); // Default
+
+ mainWidget->icnbOffline->setIcon( offlineName );
+ mainWidget->icnbOnline->setIcon( onlineName );
+ mainWidget->icnbAway->setIcon( awayName );
+ mainWidget->icnbUnknown->setIcon( unknownName );
+
+ mainWidget->widAddresseeLink->setMetaContact( lvi->metaContact() );
+
+ mAddressBookUid = item->metaContact()->metaContactId();
+
+ mExport = 0L;
+
+ if ( !mAddressBookUid.isEmpty() )
+ {
+ KABC::AddressBook *ab = Kopete::KABCPersistence::self()->addressBook();
+ KABC::Addressee a = ab->findByUid( mAddressBookUid );
+ mainWidget->widAddresseeLink->setAddressee( a );
+
+ if ( !a.isEmpty() )
+ {
+ mainWidget->btnImportKABC->setEnabled( true );
+ mainWidget->btnExportKABC->setEnabled( true );
+ mExport = new KopeteAddressBookExport( this, item->metaContact() );
+
+ mSound = a.sound();
+ mFromKABC->setEnabled( !( mSound.isIntern() || mSound.url().isEmpty() ) );
+ }
+ }
+
+ slotLoadNameSources();
+ slotLoadPhotoSources();
+
+ connect( this, SIGNAL(okClicked()), this, SLOT( slotOkClicked() ) );
+ connect( mainWidget->chkUseCustomIcons, SIGNAL( toggled( bool ) ),
+ this, SLOT( slotUseCustomIconsToggled( bool ) ) );
+ connect( mainWidget->btnImportKABC, SIGNAL( clicked() ),
+ this, SLOT( slotImportClicked() ) );
+ connect( mainWidget->btnExportKABC, SIGNAL( clicked() ),
+ this, SLOT( slotExportClicked() ) );
+ connect( mFromKABC, SIGNAL( clicked() ),
+ this, SLOT( slotFromKABCClicked() ) );
+ connect( mNotificationProps->widget()->customSound, SIGNAL( openFileDialog( KURLRequester * )),
+ SLOT( slotOpenSoundDialog( KURLRequester * )));
+
+ slotUseCustomIconsToggled( mainWidget->chkUseCustomIcons->isChecked() );
+ slotEnableAndDisableWidgets();
+}
+
+KopeteMetaLVIProps::~KopeteMetaLVIProps()
+{
+}
+
+
+void KopeteMetaLVIProps::slotLoadNameSources()
+{
+ Kopete::Contact* trackingName = item->metaContact()->displayNameSourceContact();
+ QPtrList< Kopete::Contact > cList = item->metaContact()->contacts();
+ QPtrListIterator<Kopete::Contact> it( cList );
+ mainWidget->cmbAccountName->clear();
+ for( ; it.current(); ++it )
+ {
+ QString acct = it.current()->property( Kopete::Global::Properties::self()->nickName() ).value().toString() + " <" + it.current()->contactId() + ">";
+ QPixmap acctIcon = it.current()->account()->accountIcon();
+ mainWidget->cmbAccountName->insertItem( acctIcon, acct );
+
+ // Select this item if it's the one we're tracking.
+ if( it.current() == trackingName )
+ {
+ mainWidget->cmbAccountName->setCurrentItem( mainWidget->cmbAccountName->count() - 1 );
+ }
+ }
+
+ mainWidget->edtDisplayName->setText( item->metaContact()->customDisplayName() );
+
+ Kopete::MetaContact::PropertySource nameSource = item->metaContact()->displayNameSource();
+
+ mainWidget->radioNameContact->setChecked(nameSource == Kopete::MetaContact::SourceContact);
+ mainWidget->radioNameKABC->setChecked(nameSource == Kopete::MetaContact::SourceKABC);
+ mainWidget->radioNameCustom->setChecked(nameSource == Kopete::MetaContact::SourceCustom);
+
+}
+
+void KopeteMetaLVIProps::slotLoadPhotoSources()
+{
+ // fill photo contact sources
+ QPtrList< Kopete::Contact > cList = item->metaContact()->contacts();
+ m_withPhotoContacts.clear();
+ Kopete::Contact* trackingPhoto = item->metaContact()->photoSourceContact();
+ mainWidget->cmbAccountPhoto->clear();
+ QPtrListIterator<Kopete::Contact> itp( cList );
+ for( ; itp.current(); ++itp )
+ {
+ Kopete::Contact *citem = itp.current();
+ if ( citem->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
+ {
+ QString acct = citem->property( Kopete::Global::Properties::self()->nickName() ).value().toString() + " <" + citem->contactId() + ">";
+ QPixmap acctIcon = citem->account()->accountIcon();
+ mainWidget->cmbAccountPhoto->insertItem( acctIcon, acct );
+
+ // Select this item if it's the one we're tracking.
+ if( citem == trackingPhoto )
+ {
+ mainWidget->cmbAccountPhoto->setCurrentItem( mainWidget->cmbAccountPhoto->count() - 1 );
+ }
+ m_withPhotoContacts.insert(mainWidget->cmbAccountPhoto->count() - 1 , citem );
+ }
+ }
+#if KDE_IS_VERSION(3,4,0)
+ mainWidget->cmbPhotoUrl->setKURL(item->metaContact()->customPhoto().url());
+#else
+ mainWidget->cmbPhotoUrl->setURL(item->metaContact()->customPhoto().url());
+#endif
+
+ Kopete::MetaContact::PropertySource photoSource = item->metaContact()->photoSource();
+
+ mainWidget->radioPhotoContact->setChecked(photoSource == Kopete::MetaContact::SourceContact);
+ mainWidget->radioPhotoKABC->setChecked(photoSource == Kopete::MetaContact::SourceKABC);
+ mainWidget->radioPhotoCustom->setChecked(photoSource == Kopete::MetaContact::SourceCustom);
+
+ mainWidget->chkSyncPhoto->setChecked(item->metaContact()->isPhotoSyncedWithKABC());
+}
+
+void KopeteMetaLVIProps::slotEnableAndDisableWidgets()
+{
+ KABC::AddressBook *ab = Kopete::KABCPersistence::self()->addressBook();
+ KABC::Addressee a = ab->findByUid( mAddressBookUid );
+ bool validLink = ! a.isEmpty();
+ // kabc source requires a kabc link
+ mainWidget->radioNameKABC->setEnabled(validLink);
+ // kabc source requires a kabc link
+ mainWidget->radioPhotoKABC->setEnabled(validLink);
+ // sync with kabc has no sense if we use kabc as source (sync kabc with kabc? uh?)
+ // it has also no sense if they are no kabc link
+ if( selectedPhotoSource() == Kopete::MetaContact::SourceKABC || !validLink )
+ {
+ mainWidget->chkSyncPhoto->setEnabled(false);
+ }
+ else
+ {
+ mainWidget->chkSyncPhoto->setEnabled(true);
+ }
+
+ mainWidget->radioNameContact->setEnabled(item->metaContact()->contacts().count());
+ mainWidget->radioPhotoContact->setEnabled(!m_withPhotoContacts.isEmpty());
+
+ mainWidget->cmbAccountName->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceContact);
+ mainWidget->edtDisplayName->setEnabled(selectedNameSource() == Kopete::MetaContact::SourceCustom);
+
+ mainWidget->cmbAccountPhoto->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceContact);
+ mainWidget->cmbPhotoUrl->setEnabled(selectedPhotoSource() == Kopete::MetaContact::SourceCustom);
+
+ if ( m_withPhotoContacts.isEmpty() )
+ {
+ mainWidget->cmbAccountPhoto->clear();
+ mainWidget->cmbAccountPhoto->insertItem(i18n("No Contacts with Photo Support"));
+ mainWidget->cmbAccountPhoto->setEnabled(false);
+ }
+
+ QImage photo;
+ switch ( selectedPhotoSource() )
+ {
+ case Kopete::MetaContact::SourceKABC:
+ photo = Kopete::photoFromKABC(mAddressBookUid);
+ break;
+ case Kopete::MetaContact::SourceContact:
+ photo = Kopete::photoFromContact(selectedPhotoSourceContact());
+ break;
+ case Kopete::MetaContact::SourceCustom:
+ photo = QImage(KURL::decode_string(mainWidget->cmbPhotoUrl->url()));
+ break;
+ }
+ if( !photo.isNull() )
+ mainWidget->photoLabel->setPixmap(QPixmap(photo.smoothScale( 64, 92, QImage::ScaleMin )));
+ else
+ mainWidget->photoLabel->setPixmap( QPixmap() );
+}
+
+Kopete::MetaContact::PropertySource KopeteMetaLVIProps::selectedNameSource() const
+{
+ if ( mainWidget->radioNameKABC->isChecked() )
+ return Kopete::MetaContact::SourceKABC;
+ if ( mainWidget->radioNameContact->isChecked() )
+ return Kopete::MetaContact::SourceContact;
+ if ( mainWidget->radioNameCustom->isChecked() )
+ return Kopete::MetaContact::SourceCustom;
+ else
+ return Kopete::MetaContact::SourceCustom;
+}
+
+Kopete::MetaContact::PropertySource KopeteMetaLVIProps::selectedPhotoSource() const
+{
+ if ( mainWidget->radioPhotoKABC->isChecked() )
+ return Kopete::MetaContact::SourceKABC;
+ if ( mainWidget->radioPhotoContact->isChecked() )
+ return Kopete::MetaContact::SourceContact;
+ if ( mainWidget->radioPhotoCustom->isChecked() )
+ return Kopete::MetaContact::SourceCustom;
+ else
+ return Kopete::MetaContact::SourceCustom;
+}
+
+Kopete::Contact* KopeteMetaLVIProps::selectedNameSourceContact() const
+{
+ Kopete::Contact *c= item->metaContact()->contacts().at( mainWidget->cmbAccountName->currentItem() );
+ return c ? c : 0L;
+}
+
+Kopete::Contact* KopeteMetaLVIProps::selectedPhotoSourceContact() const
+{
+ if (m_withPhotoContacts.isEmpty())
+ return 0L;
+ Kopete::Contact *c = m_withPhotoContacts[mainWidget->cmbAccountPhoto->currentItem() ];
+ return c ? c : 0L;
+}
+
+void KopeteMetaLVIProps::slotOkClicked()
+{
+ // update meta contact's UID
+ item->metaContact()->setMetaContactId( mAddressBookUid );
+ //this has to be done first, in the case something is synced with KABC (see bug 109494)
+
+ // set custom display name
+ if( mainWidget->edtDisplayName->text() != item->metaContact()->customDisplayName() )
+ item->metaContact()->setDisplayName( mainWidget->edtDisplayName->text() );
+
+ item->metaContact()->setDisplayNameSource(selectedNameSource());
+ item->metaContact()->setDisplayNameSourceContact( selectedNameSourceContact() );
+
+ // set photo source
+ item->metaContact()->setPhotoSource(selectedPhotoSource());
+ item->metaContact()->setPhotoSourceContact( selectedPhotoSourceContact() );
+ if ( !mainWidget->cmbPhotoUrl->url().isEmpty())
+ item->metaContact()->setPhoto(KURL::fromPathOrURL((mainWidget->cmbPhotoUrl->url())));
+ item->metaContact()->setPhotoSyncedWithKABC( mainWidget->chkSyncPhoto->isChecked() );
+
+ item->metaContact()->setUseCustomIcon(
+ mainWidget->chkUseCustomIcons->isChecked() );
+
+ // only call setIcon if any of the icons is not set to default icon
+ if(
+ mainWidget->icnbOffline->icon() != MC_OFF ||
+ mainWidget->icnbOnline->icon() != MC_ON ||
+ mainWidget->icnbAway->icon() != MC_AW ||
+ mainWidget->icnbUnknown->icon() != MC_UNK )
+ {
+ item->metaContact()->setIcon( mainWidget->icnbOffline->icon(),
+ Kopete::ContactListElement::Offline );
+
+ item->metaContact()->setIcon( mainWidget->icnbOnline->icon(),
+ Kopete::ContactListElement::Online );
+
+ item->metaContact()->setIcon( mainWidget->icnbAway->icon(),
+ Kopete::ContactListElement::Away );
+
+ item->metaContact()->setIcon( mainWidget->icnbUnknown->icon(),
+ Kopete::ContactListElement::Unknown );
+ }
+
+ mNotificationProps->storeCurrentCustoms();
+}
+
+void KopeteMetaLVIProps::slotUseCustomIconsToggled(bool on)
+{
+ mainWidget->lblOffline->setEnabled( on );
+ mainWidget->lblOnline->setEnabled( on );
+ mainWidget->lblAway->setEnabled( on );
+ mainWidget->lblUnknown->setEnabled( on );
+
+ mainWidget->icnbOffline->setEnabled( on );
+ mainWidget->icnbOnline->setEnabled( on );
+ mainWidget->icnbAway->setEnabled( on );
+ mainWidget->icnbUnknown->setEnabled( on );
+}
+
+void KopeteMetaLVIProps::slotAddresseeChanged( const KABC::Addressee & a )
+{
+ if ( !a.isEmpty() )
+ {
+ mSound = a.sound();
+ mFromKABC->setEnabled( !( mSound.isIntern() || mSound.url().isEmpty() ) );
+ mainWidget->btnExportKABC->setEnabled( true );
+ mainWidget->btnImportKABC->setEnabled( true );
+ // set/update the MC's addressee uin field
+ mAddressBookUid = a.uid();
+ }
+ else
+ {
+ mainWidget->btnExportKABC->setEnabled( false );
+ mainWidget->btnImportKABC->setEnabled( false );
+ mAddressBookUid = QString::null;
+ mainWidget->radioNameContact->setChecked( true );
+ mainWidget->radioPhotoContact->setChecked( true );
+ }
+ slotEnableAndDisableWidgets();
+}
+
+void KopeteMetaLVIProps::slotExportClicked()
+{
+ item->metaContact()->setMetaContactId( mAddressBookUid );
+ delete mExport;
+ mExport = new KopeteAddressBookExport( this, item->metaContact() );
+ if ( mExport->showDialog() == QDialog::Accepted )
+ mExport->exportData();
+}
+
+void KopeteMetaLVIProps::slotImportClicked()
+{
+ item->metaContact()->setMetaContactId( mAddressBookUid );
+ if ( Kopete::KABCPersistence::self()->syncWithKABC( item->metaContact() ) )
+ KMessageBox::queuedMessageBox( this, KMessageBox::Information,
+ i18n( "No contacts were imported from the address book." ),
+ i18n( "No Change" ) );
+}
+
+void KopeteMetaLVIProps::slotFromKABCClicked()
+{
+ mNotificationProps->widget()->customSound->setURL( mSound.url() );
+}
+
+void KopeteMetaLVIProps::slotOpenSoundDialog( KURLRequester *requester )
+{
+ // taken from kdelibs/kio/kfile/knotifydialog.cpp
+ // only need to init this once
+ requester->disconnect( SIGNAL( openFileDialog( KURLRequester * )),
+ this, SLOT( slotOpenSoundDialog( KURLRequester * )));
+
+ KFileDialog *fileDialog = requester->fileDialog();
+ //fileDialog->setCaption( i18n("Select Sound File") );
+ QStringList filters;
+ filters << "audio/x-wav" << "audio/x-mp3" << "application/ogg"
+ << "audio/x-adpcm";
+ fileDialog->setMimeFilter( filters );
+
+ // find the first "sound"-resource that contains files
+ QStringList soundDirs =
+ KGlobal::dirs()->findDirs("data", "kopete/sounds");
+ soundDirs += KGlobal::dirs()->resourceDirs( "sound" );
+
+ if ( !soundDirs.isEmpty() ) {
+ KURL soundURL;
+ QDir dir;
+ dir.setFilter( QDir::Files | QDir::Readable );
+ QStringList::ConstIterator it = soundDirs.begin();
+ while ( it != soundDirs.end() ) {
+ dir = *it;
+ if ( dir.isReadable() && dir.count() > 2 ) {
+ soundURL.setPath( *it );
+ fileDialog->setURL( soundURL );
+ break;
+ }
+ ++it;
+ }
+ }
+}
+
+void KopeteMetaLVIProps::slotClearPhotoClicked()
+{
+#if KDE_IS_VERSION(3,4,0)
+ mainWidget->cmbPhotoUrl->setKURL( KURL() );
+#else
+ mainWidget->cmbPhotoUrl->setURL( QString::null );
+#endif
+ item->metaContact()->setPhoto( KURL() );
+
+ slotEnableAndDisableWidgets();
+}
+
+#include "kopetelviprops.moc"
diff --git a/kopete/kopete/contactlist/kopetelviprops.h b/kopete/kopete/contactlist/kopetelviprops.h
new file mode 100644
index 00000000..9a2ebf34
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetelviprops.h
@@ -0,0 +1,102 @@
+/*
+ kopetelviprops.h
+
+ Kopete Contactlist Properties GUI for Groups and MetaContacts
+
+ Copyright (c) 2002-2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETELVIPROPS_H
+#define KOPETELVIPROPS_H
+
+#include <kdialogbase.h>
+#include <kabc/sound.h>
+
+#include "kopetemetacontact.h"
+
+#include "kopetegvipropswidget.h"
+#include "kopetemetalvipropswidget.h"
+
+class QButtonGroup;
+
+class AddressBookLinkWidget;
+class CustomNotificationProps;
+class KPushButton;
+class KopeteGroupViewItem;
+class KopeteMetaContactLVI;
+class KopeteAddressBookExport;
+class KURLRequester;
+
+namespace KABC { class Addressee; }
+namespace Kopete { class Contact; }
+
+class KopeteGVIProps: public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KopeteGVIProps(KopeteGroupViewItem *gvi, QWidget *parent, const char *name=0L);
+ ~KopeteGVIProps();
+
+ private:
+ CustomNotificationProps * mNotificationProps;
+ KopeteGVIPropsWidget *mainWidget;
+ KopeteGroupViewItem *item;
+ bool m_dirty;
+
+ private slots:
+ void slotOkClicked();
+ void slotUseCustomIconsToggled(bool on);
+ void slotIconChanged();
+};
+
+
+class KopeteMetaLVIProps: public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ KopeteMetaLVIProps(KopeteMetaContactLVI *gvi, QWidget *parent, const char *name=0L);
+ ~KopeteMetaLVIProps();
+
+ private:
+ CustomNotificationProps * mNotificationProps;
+ QPushButton *mFromKABC;
+ KopeteMetaLVIPropsWidget *mainWidget;
+ AddressBookLinkWidget *linkWidget;
+ KopeteMetaContactLVI *item;
+ KopeteAddressBookExport *mExport;
+ KABC::Sound mSound;
+ int m_countPhotoCapable;
+ QMap<int, Kopete::Contact *> m_withPhotoContacts;
+ QString mAddressBookUid; // the currently selected addressbook UID
+
+ Kopete::MetaContact::PropertySource selectedNameSource() const;
+ Kopete::MetaContact::PropertySource selectedPhotoSource() const;
+ Kopete::Contact* selectedNameSourceContact() const;
+ Kopete::Contact* selectedPhotoSourceContact() const;
+ private slots:
+ void slotOkClicked();
+ void slotUseCustomIconsToggled( bool on );
+ void slotClearPhotoClicked();
+ void slotAddresseeChanged( const KABC::Addressee & );
+ void slotExportClicked();
+ void slotImportClicked();
+ void slotFromKABCClicked();
+ void slotOpenSoundDialog( KURLRequester *requester );
+ void slotLoadNameSources();
+ void slotLoadPhotoSources();
+ void slotEnableAndDisableWidgets();
+};
+
+#endif
diff --git a/kopete/kopete/contactlist/kopetemetacontactlvi.cpp b/kopete/kopete/contactlist/kopetemetacontactlvi.cpp
new file mode 100644
index 00000000..86dc4b40
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetemetacontactlvi.cpp
@@ -0,0 +1,1102 @@
+/*
+ kopetemetacontactlvi.cpp - Kopete Meta Contact KListViewItem
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2002-2004 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar P <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qvariant.h>
+#include <qmime.h>
+#include <qstylesheet.h>
+
+#include "knotification.h"
+#include <kdebug.h>
+#include <kiconeffect.h>
+#include <kimageeffect.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassivepopup.h>
+#include <kpopupmenu.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+#include <kabc/addressbook.h>
+#include <kabc/addressee.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+
+
+#include "addcontactpage.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontactlist.h"
+#include "kopetecontactlistview.h"
+#include "kopeteemoticons.h"
+#include "kopeteuiglobal.h"
+#include "kopetegroup.h"
+#include "kopetegroupviewitem.h"
+#include "kopetemetacontact.h"
+#include "kopetemetacontactlvi.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopetestdaction.h"
+#include "systemtray.h"
+#include "kopeteglobal.h"
+#include "kopetecontact.h"
+#include "kabcpersistence.h"
+
+#include <memory>
+
+using namespace Kopete::UI;
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+class MetaContactToolTipSource : public ToolTipSource
+{
+public:
+ MetaContactToolTipSource( MetaContact *mc )
+ : metaContact( mc )
+ {
+ }
+ QString operator()( ComponentBase *, const QPoint &, QRect & )
+ {
+
+ // We begin with the meta contact display name at the top of the tooltip
+ QString toolTip = QString::fromLatin1("<qt><table cellpadding=\"0\" cellspacing=\"1\">");
+
+ toolTip += QString::fromLatin1("<tr><td>");
+
+ if ( ! metaContact->photo().isNull() )
+ {
+ QString photoName = QString::fromLatin1("kopete-metacontact-photo:%1").arg( KURL::encode_string( metaContact->metaContactId() ));
+ //QMimeSourceFactory::defaultFactory()->setImage( "contactimg", metaContact->photo() );
+ toolTip += QString::fromLatin1("<img src=\"%1\">").arg( photoName );
+ }
+
+ toolTip += QString::fromLatin1("</td><td>");
+
+ QString displayName;
+ Kopete::Emoticons *e = Kopete::Emoticons::self();
+ QValueList<Emoticons::Token> t = e->tokenize( metaContact->displayName());
+ QValueList<Emoticons::Token>::iterator it;
+ for( it = t.begin(); it != t.end(); ++it )
+ {
+ if( (*it).type == Kopete::Emoticons::Image )
+ {
+ displayName += (*it).picHTMLCode;
+ } else if( (*it).type == Kopete::Emoticons::Text )
+ {
+ displayName += QStyleSheet::escape( (*it).text );
+ }
+ }
+
+ toolTip += QString::fromLatin1("<b><font size=\"+1\">%1</font></b><br><br>").arg( displayName );
+
+ QPtrList<Contact> contacts = metaContact->contacts();
+ if ( contacts.count() == 1 )
+ {
+ return toolTip + contacts.first()->toolTip() + QString::fromLatin1("</td></tr></table></qt>");
+ }
+
+ toolTip += QString::fromLatin1("<table>");
+
+ // We are over a metacontact with > 1 child contacts, and not over a specific contact
+ // Iterate through children and display a summary tooltip
+ for(Contact *c = contacts.first(); c; c = contacts.next())
+ {
+ QString iconName = QString::fromLatin1("kopete-contact-icon:%1:%2:%3")
+ .arg( KURL::encode_string( c->protocol()->pluginId() ),
+ KURL::encode_string( c->account()->accountId() ),
+ KURL::encode_string( c->contactId() )
+ );
+
+ toolTip += i18n("<tr><td>STATUS ICON <b>PROTOCOL NAME</b> (ACCOUNT NAME)</td><td>STATUS DESCRIPTION</td></tr>",
+ "<tr><td><img src=\"%1\">&nbsp;<nobr><b>%2</b></nobr>&nbsp;<nobr>(%3)</nobr></td><td align=\"right\"><nobr>%4</nobr></td></tr>")
+ .arg( iconName, Kopete::Emoticons::parseEmoticons(c->property(Kopete::Global::Properties::self()->nickName()).value().toString()) , c->contactId(), c->onlineStatus().description() );
+ }
+
+ return toolTip + QString::fromLatin1("</table></td></tr></table></qt>");
+ }
+private:
+ MetaContact *metaContact;
+};
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+class KopeteMetaContactLVI::Private
+{
+public:
+ Private() : metaContactIcon( 0L ), nameText( 0L ), extraText( 0L ), contactIconBox( 0L ),
+ currentMode( -1 ), currentIconMode( -1 ) {}
+ ListView::ImageComponent *metaContactIcon;
+ ListView::DisplayNameComponent *nameText;
+ ListView::DisplayNameComponent *extraText;
+ ListView::BoxComponent *contactIconBox;
+ ListView::BoxComponent *spacerBox;
+ std::auto_ptr<ListView::ToolTipSource> toolTipSource;
+ // metacontact icon size
+ int iconSize;
+ // protocol icon size
+ int contactIconSize;
+ int currentMode;
+ int currentIconMode;
+
+ QPtrList<Kopete::MessageEvent> events;
+};
+
+KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, KopeteGroupViewItem *parent )
+: ListView::Item( parent, contact, "MetaContactLVI" )
+//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent )
+{
+ m_metaContact = contact;
+ m_isTopLevel = false;
+ m_parentGroup = parent;
+ m_parentView = 0L;
+
+ initLVI();
+ parent->refreshDisplayName();
+}
+
+KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, QListViewItem *parent )
+: ListView::Item( parent, contact, "MetaContactLVI" )
+//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent )
+{
+ m_metaContact = contact;
+
+ m_isTopLevel = true;
+ m_parentGroup = 0L;
+ m_parentView = 0L;
+
+ initLVI();
+}
+
+KopeteMetaContactLVI::KopeteMetaContactLVI( Kopete::MetaContact *contact, QListView *parent )
+: ListView::Item( parent, contact, "MetaContactLVI" )
+//: QObject( contact, "MetaContactLVI" ), KListViewItem( parent )
+{
+ m_metaContact = contact;
+
+ m_isTopLevel = true;
+ m_parentGroup = 0L;
+ m_parentView = parent;
+
+ initLVI();
+}
+
+void KopeteMetaContactLVI::initLVI()
+{
+ d = new Private;
+
+ d->toolTipSource.reset( new ListView::MetaContactToolTipSource( m_metaContact ) );
+
+ m_oldStatus = m_metaContact->status();
+
+ connect( m_metaContact, SIGNAL( displayNameChanged( const QString &, const QString & ) ),
+ SLOT( slotDisplayNameChanged() ) );
+
+ connect( m_metaContact, SIGNAL( photoChanged() ),
+ SLOT( slotPhotoChanged() ) );
+
+ connect( m_metaContact, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ),
+ SLOT( slotPhotoChanged() ) );
+
+ connect( m_metaContact, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ),
+ this, SLOT(slotIdleStateChanged( ) ) );
+
+ connect( m_metaContact, SIGNAL( contactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & ) ),
+ SLOT( slotContactStatusChanged( Kopete::Contact * ) ) );
+
+ connect( m_metaContact, SIGNAL( contactAdded( Kopete::Contact * ) ),
+ SLOT( slotContactAdded( Kopete::Contact * ) ) );
+
+ connect( m_metaContact, SIGNAL( contactRemoved( Kopete::Contact * ) ),
+ SLOT( slotContactRemoved( Kopete::Contact * ) ) );
+
+ connect( m_metaContact, SIGNAL( iconAppearanceChanged() ),
+ SLOT( slotUpdateMetaContact() ) );
+
+ connect( m_metaContact, SIGNAL( useCustomIconChanged( bool ) ),
+ SLOT( slotUpdateMetaContact() ) );
+
+ connect( m_metaContact, SIGNAL( contactIdleStateChanged( Kopete::Contact * ) ),
+ SLOT( slotIdleStateChanged( Kopete::Contact * ) ) );
+
+ connect( KopetePrefs::prefs(), SIGNAL( contactListAppearanceChanged() ),
+ SLOT( slotConfigChanged() ) );
+
+ connect( kapp, SIGNAL( appearanceChanged() ), SLOT( slotConfigChanged() ) );
+
+ mBlinkTimer = new QTimer( this, "mBlinkTimer" );
+ connect( mBlinkTimer, SIGNAL( timeout() ), SLOT( slotBlink() ) );
+ mIsBlinkIcon = false;
+
+ //if ( !mBlinkIcon )
+ // mBlinkIcon = new QPixmap( KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "newmsg" ), KIcon::Small ) );
+
+ slotConfigChanged(); // this calls slotIdleStateChanged(), which sets up the constituent components, spacing, fonts and indirectly, the contact icon
+ slotDisplayNameChanged();
+ updateContactIcons();
+}
+
+KopeteMetaContactLVI::~KopeteMetaContactLVI()
+{
+ delete d;
+ //if ( m_parentGroup )
+ // m_parentGroup->refreshDisplayName();
+}
+
+void KopeteMetaContactLVI::movedToDifferentGroup()
+{
+ KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() );
+ if ( !lv )
+ return;
+
+ if ( m_parentGroup )
+ m_parentGroup->refreshDisplayName();
+
+ // create a spacer if wanted
+ // I assume that the safety property that allows the delete in slotConfigChanged holds here - Will
+ delete d->spacerBox->component( 0 );
+ if ( KListViewItem::parent() && KopetePrefs::prefs()->contactListIndentContacts() &&
+ !KopetePrefs::prefs()->treeView() )
+ {
+ new ListView::SpacerComponent( d->spacerBox, 20, 0 );
+ }
+
+ KopeteGroupViewItem *group_item = dynamic_cast<KopeteGroupViewItem*>(KListViewItem::parent());
+ if ( group_item )
+ {
+ m_isTopLevel = false;
+ m_parentGroup = group_item;
+ m_parentView = 0L;
+ group_item->refreshDisplayName();
+ }
+ else
+ {
+ m_isTopLevel = true;
+ m_parentGroup = 0L;
+ m_parentView = lv;
+ }
+}
+
+void KopeteMetaContactLVI::rename( const QString& newName )
+{
+ QString oldName = m_metaContact->displayName();
+ KopeteContactListView *lv = dynamic_cast<KopeteContactListView *>( listView() );
+ if ( lv )
+ {
+ KopeteContactListView::UndoItem *u=new KopeteContactListView::UndoItem(KopeteContactListView::UndoItem::MetaContactRename, m_metaContact);
+ // HACK but args are strings not ints
+ u->nameSource = m_metaContact->displayNameSource();
+ // additional args
+ if ( m_metaContact->displayNameSource() == Kopete::MetaContact::SourceCustom )
+ {
+ u->args << m_metaContact->customDisplayName();
+ }
+ else if ( m_metaContact->displayNameSource() == Kopete::MetaContact::SourceContact )
+ {
+ Kopete::Contact* c = m_metaContact->displayNameSourceContact();
+ if(c)
+ u->args << c->contactId() << c->protocol()->pluginId() << c->account()->accountId();
+ }
+ // source kabc requires no arguments
+
+ lv->insertUndoItem(u);
+ }
+
+ if ( newName.isEmpty() )
+ {
+ // fallback to KABC
+ if ( !m_metaContact->metaContactId().isEmpty() )
+ {
+ m_metaContact->setDisplayNameSource(Kopete::MetaContact::SourceKABC);
+ if ( ! m_metaContact->displayName().isEmpty() )
+ {
+ slotDisplayNameChanged();
+ return;
+ }
+ }
+ // bad luck with KABC
+ m_metaContact->setDisplayNameSource(Kopete::MetaContact::SourceContact);
+ // TODO iterate though all subcontacts to check non empty nick
+ m_metaContact->setDisplayNameSourceContact( m_metaContact->contacts().first() );
+ slotDisplayNameChanged();
+ }
+ else // user changed name manually, set source to custom
+ {
+ m_metaContact->setDisplayNameSource( Kopete::MetaContact::SourceCustom );
+ m_metaContact->setDisplayName( newName );
+ slotDisplayNameChanged();
+ }
+
+ kdDebug( 14000 ) << k_funcinfo << "newName=" << newName << endl;
+}
+
+void KopeteMetaContactLVI::slotContactStatusChanged( Kopete::Contact *c )
+{
+ updateContactIcon( c );
+
+ // FIXME: All this code should be in kopetemetacontact.cpp.. having it in the LVI makes it all fire
+ // multiple times if the user is in multiple groups - Jason
+
+ // comparing the status of the previous and new preferred contact is the determining factor in deciding to notify
+ Kopete::OnlineStatus newStatus;
+ if ( m_metaContact->preferredContact() )
+ newStatus = m_metaContact->preferredContact()->onlineStatus();
+ else
+ {
+ // the last child contact has gone offline or otherwise unreachable, so take the changed contact's online status
+ newStatus = c->onlineStatus();
+ }
+
+ // ensure we are not suppressing notifications, because connecting or disconnected
+ if ( !(c->account()->suppressStatusNotification()
+ || ( c->account()->myself()->onlineStatus().status() == Kopete::OnlineStatus::Connecting )
+ || !c->account()->isConnected() ) )
+ {
+ if ( !c->account()->isAway() || KopetePrefs::prefs()->soundIfAway() )
+ {
+ //int winId = KopeteSystemTray::systemTray() ? KopeteSystemTray::systemTray()->winId() : 0;
+
+ QString text = i18n( "<qt><i>%1</i> is now %2.</qt>" )
+ .arg( Kopete::Emoticons::parseEmoticons( QStyleSheet::escape(m_metaContact->displayName()) ) ,
+ QStyleSheet::escape(c->onlineStatus().description()));
+
+ // figure out what's happened
+ enum ChangeType { noChange, noEvent, signedIn, changedStatus, signedOut };
+ ChangeType t = noChange;
+ //kdDebug( 14000 ) << k_funcinfo << m_metaContact->displayName() <<
+ //" - Old MC Status: " << m_oldStatus.status() << ", New MC Status: " << newStatus.status() << endl;
+ // first, exclude changes due to blocking or subscription changes at the protocol level
+ if ( ( m_oldStatus.status() == Kopete::OnlineStatus::Unknown
+ || newStatus.status() == Kopete::OnlineStatus::Unknown ) )
+ t = noEvent; // This means the contact's changed from or to unknown - due to a protocol state change, not a contact state change
+ else // we're dealing with a genuine contact state change
+ {
+ if ( m_oldStatus.status() == Kopete::OnlineStatus::Offline )
+ {
+ if ( newStatus.status() != Kopete::OnlineStatus::Offline )
+ {
+ //kdDebug( 14000 ) << "signed in" << endl;
+ t = signedIn; // contact has gone from offline to something else, it's a sign-in
+ }
+ }
+ else if ( m_oldStatus.status() == Kopete::OnlineStatus::Online
+ || m_oldStatus.status() == Kopete::OnlineStatus::Away
+ || m_oldStatus.status() == Kopete::OnlineStatus::Invisible)
+ {
+ if ( newStatus.status() == Kopete::OnlineStatus::Offline )
+ {
+ //kdDebug( 14000 ) << "signed OUT" << endl;
+ t = signedOut; // contact has gone from an online state to an offline state, it's a sign out
+ }
+ else if ( m_oldStatus > newStatus || m_oldStatus < newStatus ) // operator!= is useless because it's an identity operator, not an equivalence operator
+ {
+ // contact has changed online states, it's a status change,
+ // and the preferredContact changed status, or there is a new preferredContacat
+ // so it's worth notifying
+ //kdDebug( 14000 ) << "changed status" << endl;
+ t = changedStatus;
+ }
+ }
+ else if ( m_oldStatus != newStatus )
+ {
+ //kdDebug( 14000 ) << "non-event" << endl;
+ // catch-all for any other status change we don't know about
+ t = noEvent;
+ }
+ // if none of the above were true, t will still be noChange
+ }
+
+ // now issue the appropriate notification
+ switch ( t )
+ {
+ case noEvent:
+ case noChange:
+ break;
+ case signedIn:
+ connect(KNotification::event(m_metaContact, "kopete_contact_online", text, m_metaContact->photo(), KopeteSystemTray::systemTray(), i18n( "Chat" )) ,
+ SIGNAL(activated(unsigned int )) , this, SLOT( execute() ) );
+ break;
+ case changedStatus:
+ connect(KNotification::event(m_metaContact, "kopete_contact_status_change", text, m_metaContact->photo(), KopeteSystemTray::systemTray(), i18n( "Chat" )) ,
+ SIGNAL(activated(unsigned int )) , this, SLOT( execute() ));
+ break;
+ case signedOut:
+ KNotification::event(m_metaContact, "kopete_contact_offline", text, m_metaContact->photo(), KopeteSystemTray::systemTray());
+ break;
+ }
+ }
+ //blink if the metacontact icon has changed.
+ if ( !mBlinkTimer->isActive() && d->metaContactIcon /*&& d->metaContactIcon->pixmap() != m_oldStatusIcon */)
+ {
+ mIsBlinkIcon = false;
+ m_blinkLeft = 9;
+ mBlinkTimer->start( 400, false );
+ }
+ }
+ else
+ {
+ //the status icon probably changed, but we didn't blink.
+ //So the olfStatusIcon will not be set to the real after the blink.
+ //we set it now.
+ if( !mBlinkTimer->isActive() )
+ m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap();
+ }
+
+ // make a note of the current status for the next time we get a status change
+ m_oldStatus = newStatus;
+
+ if ( m_parentGroup )
+ m_parentGroup->refreshDisplayName();
+ updateVisibility();
+}
+
+void KopeteMetaContactLVI::slotUpdateMetaContact()
+{
+ slotIdleStateChanged( 0 );
+ updateVisibility();
+
+ if ( m_parentGroup )
+ m_parentGroup->refreshDisplayName();
+}
+
+void KopeteMetaContactLVI::execute() const
+{
+ if ( d->events.first() )
+ d->events.first()->apply();
+ else
+ m_metaContact->execute();
+
+ //The selection is removed, but the contact still hihjlihted, remove the selection in the contactlist (see bug 106090)
+ Kopete::ContactList::self()->setSelectedItems( QPtrList<Kopete::MetaContact>() , QPtrList<Kopete::Group>() );
+}
+
+void KopeteMetaContactLVI::slotDisplayNameChanged()
+{
+ if ( d->nameText )
+ {
+ d->nameText->setText( m_metaContact->displayName() );
+
+ // delay the sort if we can
+ if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) )
+ lv->delayedSort();
+ else
+ listView()->sort();
+ }
+}
+
+void KopeteMetaContactLVI::slotPhotoChanged()
+{
+ if ( d->metaContactIcon && d->currentIconMode == KopetePrefs::PhotoPic )
+ {
+ m_oldStatusIcon= d->metaContactIcon->pixmap();
+ QPixmap photoPixmap;
+ //QPixmap defaultIcon( KGlobal::iconLoader()->loadIcon( "vcard", KIcon::Desktop ) );
+ QImage photoImg = m_metaContact->photo();
+ if ( !photoImg.isNull() && (photoImg.width() > 0) && (photoImg.height() > 0) )
+ {
+ int photoSize = d->iconSize;
+
+ photoImg = photoImg.smoothScale( photoSize, photoSize, QImage::ScaleMin );
+
+ KImageEffect *effect = 0L;
+ switch ( m_metaContact->status() )
+ {
+ case Kopete::OnlineStatus::Online:
+ break;
+ case Kopete::OnlineStatus::Away:
+ effect = new KImageEffect();
+ effect->fade(photoImg, 0.5, Qt::white);
+ break;
+ case Kopete::OnlineStatus::Offline:
+ effect = new KImageEffect();
+ effect->fade(photoImg, 0.4, Qt::white);
+ effect->toGray(photoImg);
+ break;
+ case Kopete::OnlineStatus::Unknown:
+ default:
+ effect = new KImageEffect();
+ effect->fade(photoImg, 0.8, Qt::white);
+ }
+ delete effect;
+
+ photoPixmap = photoImg;
+ }
+ else
+ {
+ photoPixmap=SmallIcon(m_metaContact->statusIcon(), d->iconSize);
+ }
+ d->metaContactIcon->setPixmap( photoPixmap, false);
+ if(mBlinkTimer->isActive())
+ m_originalBlinkIcon=photoPixmap;
+ }
+}
+
+/*
+void KopeteMetaContactLVI::slotRemoveThisUser()
+{
+ kdDebug( 14000 ) << k_funcinfo << " Removing user" << endl;
+ //m_metaContact->removeThisUser();
+
+ if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(),
+ i18n( "Are you sure you want to remove %1 from your contact list?" ).
+ arg( m_metaContact->displayName() ), i18n( "Remove Contact" ), KGuiItem(i18n("Remove"),"delete_user") )
+ == KMessageBox::Continue )
+ {
+ Kopete::ContactList::self()->removeMetaContact( m_metaContact );
+ }
+}
+
+void KopeteMetaContactLVI::slotRemoveFromGroup()
+{
+ if ( m_metaContact->isTemporary() )
+ return;
+
+ m_metaContact->removeFromGroup( group() );
+}
+*/
+
+void KopeteMetaContactLVI::startRename( int /*col*/ )
+{
+ KListViewItem::startRename( 0 );
+}
+
+void KopeteMetaContactLVI::okRename( int col )
+{
+ KListViewItem::okRename( col );
+ setRenameEnabled( 0, false );
+}
+
+void KopeteMetaContactLVI::cancelRename( int col )
+{
+ KListViewItem::cancelRename( col );
+ setRenameEnabled( 0, false );
+}
+
+/*
+void KopeteMetaContactLVI::slotMoveToGroup()
+{
+ if ( m_actionMove && !m_metaContact->isTemporary() )
+ {
+ if ( m_actionMove->currentItem() == 0 )
+ {
+ // we are moving to top-level
+ if ( group() != Kopete::Group::toplevel )
+ m_metaContact->moveToGroup( group(), Kopete::Group::toplevel );
+ }
+ else
+ {
+ Kopete::Group *to = Kopete::ContactList::self()->getGroup( m_actionMove->currentText() );
+ if ( !m_metaContact->groups().contains( to ) )
+ m_metaContact->moveToGroup( group(), to );
+ }
+ }
+}
+
+void KopeteMetaContactLVI::slotAddToGroup()
+{
+ if ( m_actionCopy )
+ {
+ kdDebug( 14000 ) << "KopeteMetaContactLVI::slotAddToGroup " << endl;
+ if ( m_actionCopy->currentItem() == 0 )
+ {
+ // we are adding to top-level
+ m_metaContact->addToGroup( Kopete::Group::toplevel );
+ }
+ else
+ {
+ m_metaContact->addToGroup( Kopete::ContactList::self()->getGroup( m_actionCopy->currentText() ) );
+ }
+ }
+}
+*/
+
+//FIXME: this is not used... remove?
+void KopeteMetaContactLVI::slotAddToNewGroup()
+{
+ if ( m_metaContact->isTemporary() )
+ return;
+
+ QString groupName = KInputDialog::getText(
+ i18n( "New Group" ), i18n( "Please enter the name for the new group:" ) );
+
+ if ( !groupName.isEmpty() )
+ m_metaContact->addToGroup( Kopete::ContactList::self()->findGroup( groupName ) );
+}
+
+void KopeteMetaContactLVI::slotConfigChanged()
+{
+ setDisplayMode( KopetePrefs::prefs()->contactListDisplayMode(),
+ KopetePrefs::prefs()->contactListIconMode() );
+
+ // create a spacer if wanted
+ delete d->spacerBox->component( 0 );
+ if ( KListViewItem::parent() && KopetePrefs::prefs()->contactListIndentContacts() &&
+ !KopetePrefs::prefs()->treeView() )
+ {
+ new ListView::SpacerComponent( d->spacerBox, 20, 0 );
+ }
+
+ if ( KopetePrefs::prefs()->contactListUseCustomFonts() )
+ {
+ d->nameText->setFont( KopetePrefs::prefs()->contactListCustomNormalFont() );
+ if ( d->extraText )
+ d->extraText->setFont( KopetePrefs::prefs()->contactListSmallFont() );
+ }
+ else
+ {
+ QFont font=listView()->font();
+ d->nameText->setFont( font );
+ if(d->extraText)
+ {
+ if ( font.pixelSize() != -1 )
+ font.setPixelSize( (font.pixelSize() * 3) / 4 );
+ else
+ font.setPointSizeFloat( font.pointSizeFloat() * 0.75 );
+ d->extraText->setFont( font );
+ }
+ }
+
+ updateVisibility();
+ updateContactIcons();
+ slotIdleStateChanged( 0 );
+ if(d->nameText)
+ d->nameText->redraw();
+ if(d->extraText)
+ d->extraText->redraw();
+}
+
+void KopeteMetaContactLVI::setMetaContactToolTipSourceForComponent( ListView::Component *comp )
+{
+ if ( comp )
+ comp->setToolTipSource( d->toolTipSource.get() );
+}
+
+void KopeteMetaContactLVI::setDisplayMode( int mode, int iconmode )
+{
+ if ( mode == d->currentMode && iconmode == d->currentIconMode )
+ return;
+
+ d->currentMode = mode;
+ d->currentIconMode = iconmode;
+
+ // empty...
+ while ( component( 0 ) )
+ delete component( 0 );
+
+ d->nameText = 0L;
+ d->extraText = 0L;
+ d->metaContactIcon = 0L;
+ d->contactIconSize = 12;
+ if (mode == KopetePrefs::Detailed) {
+ d->iconSize = iconmode == KopetePrefs::IconPic ? KIcon::SizeMedium : KIcon::SizeLarge;
+ } else {
+ d->iconSize = iconmode == KopetePrefs::IconPic ? IconSize( KIcon::Small ) : KIcon::SizeMedium;
+ }
+ disconnect( Kopete::KABCPersistence::self()->addressBook() , 0 , this , 0);
+
+ // generate our contents
+ using namespace ListView;
+ Component *hbox = new BoxComponent( this, BoxComponent::Horizontal );
+ d->spacerBox = new BoxComponent( hbox, BoxComponent::Horizontal );
+
+ if (iconmode == KopetePrefs::PhotoPic) {
+ Component *imageBox = new BoxComponent( hbox, BoxComponent::Vertical );
+ new VSpacerComponent( imageBox );
+ d->metaContactIcon = new ImageComponent( imageBox, d->iconSize + 2 , d->iconSize + 2 );
+ new VSpacerComponent( imageBox );
+ if(!metaContact()->photoSource() && !Kopete::KABCPersistence::self()->addressBook()->findByUid( metaContact()->metaContactId() ).isEmpty() )
+ { //if the photo is the one of the kaddressbook, track every change in the adressbook, it might be the photo of our contact.
+ connect( Kopete::KABCPersistence::self()->addressBook() , SIGNAL(addressBookChanged (AddressBook *) ) ,
+ this , SLOT(slotPhotoChanged()));
+ }
+ } else {
+ d->metaContactIcon = new ImageComponent( hbox );
+ }
+
+ if( mode == KopetePrefs::Detailed )
+ {
+ d->contactIconSize = IconSize( KIcon::Small );
+ Component *vbox = new BoxComponent( hbox, BoxComponent::Vertical );
+ d->nameText = new DisplayNameComponent( vbox );
+ d->extraText = new DisplayNameComponent( vbox );
+
+ Component *box = new BoxComponent( vbox, BoxComponent::Horizontal );
+ d->contactIconBox = new BoxComponent( box, BoxComponent::Horizontal );
+ }
+ else if( mode == KopetePrefs::RightAligned ) // old right-aligned contact
+ {
+ d->nameText = new DisplayNameComponent( hbox );
+ new HSpacerComponent( hbox );
+ d->contactIconBox = new BoxComponent( hbox, BoxComponent::Horizontal );
+ }
+ else // older left-aligned contact
+ {
+ d->nameText = new DisplayNameComponent( hbox );
+ d->contactIconBox = new BoxComponent( hbox, BoxComponent::Horizontal );
+ }
+
+ // set some components to have the metacontact tooltip
+ setMetaContactToolTipSourceForComponent( d->metaContactIcon );
+ setMetaContactToolTipSourceForComponent( d->nameText );
+ setMetaContactToolTipSourceForComponent( d->extraText );
+
+ // update the display name
+ slotDisplayNameChanged();
+ slotPhotoChanged();
+ slotIdleStateChanged( 0 );
+
+ // finally, re-add all contacts so their icons appear. remove them first for consistency.
+ QPtrList<Kopete::Contact> contacts = m_metaContact->contacts();
+ for ( QPtrListIterator<Kopete::Contact> it( contacts ); it.current(); ++it )
+ {
+ slotContactRemoved( *it );
+ slotContactAdded( *it );
+ }
+ m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap();
+ if( mBlinkTimer->isActive() )
+ m_originalBlinkIcon=m_oldStatusIcon;
+}
+
+void KopeteMetaContactLVI::updateVisibility()
+{
+ if ( KopetePrefs::prefs()->showOffline() || !d->events.isEmpty() )
+ setTargetVisibility( true );
+ else if ( !m_metaContact->isOnline() && !mBlinkTimer->isActive() )
+ setTargetVisibility( false );
+ else
+ setTargetVisibility( true );
+}
+
+void KopeteMetaContactLVI::slotContactPropertyChanged( Kopete::Contact *contact,
+ const QString &key, const QVariant &old, const QVariant &newVal )
+{
+// if ( key == QString::fromLatin1("awayMessage") )
+// kdDebug( 14000 ) << k_funcinfo << "contact=" << contact->contactId() << ", isonline=" << contact->isOnline() << ", alloffline=" << !m_metaContact->isOnline() << ", oldvalue=" << old.toString() << ", newvalue=" << newVal.toString() << endl;
+ if ( key == QString::fromLatin1("awayMessage") && d->extraText && old != newVal )
+ {
+ bool allOffline = !m_metaContact->isOnline();
+ if ( newVal.toString().isEmpty() || ( !contact->isOnline() && !allOffline ) )
+ {
+ // try to find a more suitable away message to be displayed when:
+ // -new away message is empty or
+ // -contact who set it is offline and there are contacts online in the metacontact
+ bool allAwayMessagesEmpty = true;
+ QPtrList<Kopete::Contact> contacts = m_metaContact->contacts();
+ for ( Kopete::Contact *c = contacts.first(); c; c = contacts.next() )
+ {
+// kdDebug( 14000 ) << k_funcinfo << "ccontact=" << c->contactId() << ", isonline=" << c->isOnline() << ", awaymsg=" << c->property( key ).value().toString() << endl;
+ QString awayMessage( c->property( key ).value().toString() );
+ if ( ( allOffline || c->isOnline() ) && !awayMessage.isEmpty() )
+ {
+ // display this contact's away message when:
+ // -this contact's away message is not empty and
+ // -this contact is online or there are no contacts online at all
+ allAwayMessagesEmpty = false;
+ d->extraText->setText( awayMessage );
+ break;
+ }
+ }
+ if ( allAwayMessagesEmpty )
+ d->extraText->setText( QString::null );
+ }
+ else
+ {
+ // just use new away message when:
+ // -new away message is not empty and
+ // -contact who set it is online or there are no contacts online at all
+ d->extraText->setText( newVal.toString() );
+ }
+ } // wtf? KopeteMetaContact also connects this signals and emits photoChanged! why no connect photoChanged to slotPhotoChanged?
+ /*else if ( key == QString::fromLatin1("photo") && (m_metaContact->photoSourceContact() == contact) && (m_metaContact->photoSource() == Kopete::MetaContact::SourceContact))
+ {
+ slotPhotoChanged();
+ }*/
+}
+
+void KopeteMetaContactLVI::slotContactAdded( Kopete::Contact *c )
+{
+ connect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &,
+ const QVariant &, const QVariant & ) ),
+ this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &,
+ const QVariant &, const QVariant & ) ) );
+ connect( c->account() , SIGNAL( colorChanged(const QColor& ) ) , this, SLOT( updateContactIcons() ) );
+
+ updateContactIcon( c );
+
+ slotContactPropertyChanged( c, QString::fromLatin1("awayMessage"),
+ QVariant(), c->property( QString::fromLatin1("awayMessage") ).value() );
+}
+
+void KopeteMetaContactLVI::slotContactRemoved( Kopete::Contact *c )
+{
+ disconnect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &,
+ const QVariant &, const QVariant & ) ),
+ this, SLOT( slotContactPropertyChanged( Kopete::Contact *,
+ const QString &, const QVariant &, const QVariant & ) ) );
+ disconnect( c->account() , SIGNAL( colorChanged(const QColor& ) ) , this, SLOT( updateContactIcons() ) );
+
+ if ( ListView::Component *comp = contactComponent( c ) )
+ delete comp;
+
+ slotContactPropertyChanged( c, QString::fromLatin1("awayMessage"),
+ c->property( QString::fromLatin1("awayMessage") ).value(), QVariant() );
+}
+
+void KopeteMetaContactLVI::updateContactIcons()
+{
+ // show offline contacts setting may have changed
+ QPtrList<Kopete::Contact> contacts = m_metaContact->contacts();
+ for ( QPtrListIterator<Kopete::Contact> it( contacts ); it.current(); ++it )
+ updateContactIcon( *it );
+}
+
+void KopeteMetaContactLVI::updateContactIcon( Kopete::Contact *c )
+{
+ KGlobal::config()->setGroup( QString::fromLatin1("ContactList") );
+ bool bHideOffline = KGlobal::config()->readBoolEntry(
+ QString::fromLatin1("HideOfflineContacts"), false );
+ if ( KopetePrefs::prefs()->showOffline() )
+ bHideOffline = false;
+
+ ListView::ContactComponent *comp = contactComponent( c );
+ bool bShow = !bHideOffline || c->isOnline();
+ if ( bShow && !comp )
+ (void)new ListView::ContactComponent( d->contactIconBox, c, d->contactIconSize );
+ else if ( !bShow && comp )
+ delete comp;
+ else if ( comp )
+ comp->updatePixmap();
+}
+
+Kopete::Contact *KopeteMetaContactLVI::contactForPoint( const QPoint &p ) const
+{
+ if ( ListView::ContactComponent *comp = dynamic_cast<ListView::ContactComponent*>( d->contactIconBox->componentAt( p ) ) )
+ return comp->contact();
+ return 0L;
+}
+
+ListView::ContactComponent *KopeteMetaContactLVI::contactComponent( const Kopete::Contact *c ) const
+{
+ for ( uint n = 0; n < d->contactIconBox->components(); ++n )
+ {
+ if ( ListView::ContactComponent *comp = dynamic_cast<ListView::ContactComponent*>( d->contactIconBox->component( n ) ) )
+ {
+ if ( comp->contact() == c )
+ return comp;
+ }
+ }
+ return 0;
+}
+
+QRect KopeteMetaContactLVI::contactRect( const Kopete::Contact *c ) const
+{
+ if ( ListView::Component *comp = contactComponent( c ) )
+ return comp->rect();
+ return QRect();
+}
+
+Kopete::Group *KopeteMetaContactLVI::group()
+{
+ if ( m_parentGroup && m_parentGroup->group() != Kopete::Group::topLevel() )
+ return m_parentGroup->group();
+ else
+ return Kopete::Group::topLevel();
+}
+
+QString KopeteMetaContactLVI::key( int, bool ) const
+{
+ char importanceChar;
+ switch ( m_metaContact->status() )
+ {
+ case Kopete::OnlineStatus::Online:
+ importanceChar = 'A';
+ break;
+ case Kopete::OnlineStatus::Away:
+ importanceChar = 'B';
+ break;
+ case Kopete::OnlineStatus::Offline:
+ importanceChar = 'C';
+ break;
+ case Kopete::OnlineStatus::Unknown:
+ default:
+ importanceChar = 'D';
+ }
+
+ return importanceChar + d->nameText->text().lower();
+}
+
+bool KopeteMetaContactLVI::isTopLevel() const
+{
+ return m_isTopLevel;
+}
+
+bool KopeteMetaContactLVI::isGrouped() const
+{
+ if ( m_parentView )
+ return true;
+
+ if ( !m_parentGroup || !m_parentGroup->group() )
+ return false;
+
+ if ( m_parentGroup->group() == Kopete::Group::temporary() && !KopetePrefs::prefs()->sortByGroup() )
+ return false;
+
+ return true;
+}
+
+void KopeteMetaContactLVI::slotIdleStateChanged( Kopete::Contact *c )
+{
+ bool doWeHaveToGrayThatContact = KopetePrefs::prefs()->greyIdleMetaContacts() && ( m_metaContact->idleTime() >= 10 * 60 );
+ if ( doWeHaveToGrayThatContact )
+ {
+ d->nameText->setColor( KopetePrefs::prefs()->idleContactColor() );
+ if ( d->extraText )
+ d->extraText->setColor( KopetePrefs::prefs()->idleContactColor() );
+ }
+ else
+ {
+ d->nameText->setDefaultColor();
+ if ( d->extraText )
+ d->extraText->setDefaultColor();
+ }
+
+ if(d->metaContactIcon && d->currentIconMode==KopetePrefs::IconPic)
+ {
+ m_oldStatusIcon=d->metaContactIcon->pixmap();
+
+ QPixmap icon = SmallIcon( m_metaContact->statusIcon(), d->iconSize );
+ if ( doWeHaveToGrayThatContact )
+ {
+ // TODO: QPixmapCache this result
+ KIconEffect::semiTransparent( icon );
+ }
+
+ d->metaContactIcon->setPixmap( icon );
+ if(mBlinkTimer->isActive())
+ m_originalBlinkIcon=icon;
+ }
+ // we only need to update the contact icon if one was supplied;
+ // if none was supplied, we only need to update the MC appearance
+ if ( c )
+ updateContactIcon( c );
+ else
+ return;
+}
+
+void KopeteMetaContactLVI::catchEvent( Kopete::MessageEvent *event )
+{
+ d->events.append( event );
+
+ connect( event, SIGNAL( done( Kopete::MessageEvent* ) ),
+ this, SLOT( slotEventDone( Kopete::MessageEvent * ) ) );
+
+ if ( mBlinkTimer->isActive() )
+ mBlinkTimer->stop();
+
+ m_oldStatusIcon= d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap();
+
+ mBlinkTimer->start( 400, false );
+
+ //show the contact if it was hidden because offline.
+ updateVisibility();
+ }
+
+void KopeteMetaContactLVI::slotBlink()
+{
+ bool haveEvent = !d->events.isEmpty();
+ if ( mIsBlinkIcon )
+ {
+ if(d->metaContactIcon)
+ d->metaContactIcon->setPixmap( m_originalBlinkIcon );
+ if ( !haveEvent && m_blinkLeft <= 0 )
+ {
+ mBlinkTimer->stop();
+ m_oldStatusIcon=d->metaContactIcon ? d->metaContactIcon->pixmap() : QPixmap();
+ updateVisibility();
+ m_originalBlinkIcon=QPixmap(); //i hope this help to reduce memory consuption
+ }
+ }
+ else
+ {
+ if(d->metaContactIcon)
+ m_originalBlinkIcon=d->metaContactIcon->pixmap();
+ if ( haveEvent )
+ {
+ if(d->metaContactIcon)
+ d->metaContactIcon->setPixmap( SmallIcon( "newmsg", d->iconSize ) );
+ }
+ else
+ {
+ if(d->metaContactIcon)
+ d->metaContactIcon->setPixmap( m_oldStatusIcon );
+ m_blinkLeft--;
+ }
+ }
+
+ mIsBlinkIcon = !mIsBlinkIcon;
+}
+
+void KopeteMetaContactLVI::slotEventDone( Kopete::MessageEvent *event )
+{
+ d->events.remove( event );
+
+ if ( d->events.isEmpty() )
+ {
+ if ( mBlinkTimer->isActive() )
+ {
+ mBlinkTimer->stop();
+ //If the contact gone offline while the timer was actif,
+ //the visibility has not been correctly updated. so do it now
+ updateVisibility();
+ }
+
+ if(d->metaContactIcon)
+ d->metaContactIcon->setPixmap( m_originalBlinkIcon );
+ m_originalBlinkIcon=QPixmap(); //i hope this help to reduce memory consuption
+ mIsBlinkIcon = false;
+ }
+}
+
+QString KopeteMetaContactLVI::text( int column ) const
+{
+ if ( column == 0 )
+ return d->nameText->text();
+ else
+ return KListViewItem::text( column );
+}
+
+void KopeteMetaContactLVI::setText( int column, const QString &text )
+{
+ if ( column == 0 )
+ rename( text );
+ else
+ KListViewItem::setText( column, text );
+}
+
+#include "kopetemetacontactlvi.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/contactlist/kopetemetacontactlvi.h b/kopete/kopete/contactlist/kopetemetacontactlvi.h
new file mode 100644
index 00000000..767330ba
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetemetacontactlvi.h
@@ -0,0 +1,191 @@
+/*
+ kopetemetacontactlvi.h - Kopete Meta Contact KListViewItem
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar P <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __kopetemetacontactlvi_h__
+#define __kopetemetacontactlvi_h__
+
+#include "kopetelistviewitem.h"
+
+#include <qobject.h>
+#include <qpixmap.h>
+#include <qptrdict.h>
+
+#include <klistview.h>
+
+class QVariant;
+
+class KAction;
+class KListAction;
+
+namespace Kopete
+{
+class Account;
+class MetaContact;
+class Contact;
+class Group;
+class MessageEvent;
+}
+
+class AddContactPage;
+class KopeteGroupViewItem;
+
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ */
+class KopeteMetaContactLVI : public Kopete::UI::ListView::Item
+{
+ Q_OBJECT
+
+public:
+ KopeteMetaContactLVI( Kopete::MetaContact *contact, KopeteGroupViewItem *parent );
+ KopeteMetaContactLVI( Kopete::MetaContact *contact, QListViewItem *parent );
+ KopeteMetaContactLVI( Kopete::MetaContact *contact, QListView *parent );
+ ~KopeteMetaContactLVI();
+
+ /**
+ * metacontact this visual item represents
+ */
+ Kopete::MetaContact *metaContact() const
+ { return m_metaContact; };
+
+ /**
+ * true if the item is at top level and not under a group
+ */
+ bool isTopLevel() const;
+
+ /**
+ * parent when top-level
+ */
+ QListView *parentView() const { return m_parentView; };
+
+ /**
+ * parent when not top-level
+ */
+ KopeteGroupViewItem *parentGroup() const { return m_parentGroup; };
+
+ /**
+ * call this when the item has been moved to a different group
+ */
+ void movedToDifferentGroup();
+ void rename( const QString& name );
+ void startRename( int );
+
+ Kopete::Group *group();
+
+ /**
+ * Returns the Kopete::Contact of the small little icon at the point p
+ * @param p must be in the list view item's coordinate system.
+ * Returns a null pointer if p is not on a small icon.
+ * (This is used for e.g. the context-menu of a contact when
+ * right-clicking an icon, or the tooltips)
+ */
+ Kopete::Contact *contactForPoint( const QPoint &p ) const;
+
+ /**
+ * Returns the QRect small little icon used for the Kopete::Contact.
+ * The behavior is undefined if @param c doesn't point to a valid
+ * Kopete::Contact for this list view item.
+ * The returned QRect is using the list view item's coordinate
+ * system and should probably be transformed into the list view's
+ * coordinates before being of any practical use.
+ * Note that the returned Rect is always vertically stretched to fill
+ * the full list view item's height, only the width is relative to
+ * the actual icon width.
+ */
+ QRect contactRect( const Kopete::Contact *c ) const;
+
+ bool isGrouped() const;
+
+ /**
+ * reimplemented from KListViewItem to take into account our alternate text storage
+ */
+ virtual QString text( int column ) const;
+ virtual void setText( int column, const QString &text );
+
+public slots:
+ /**
+ * Call the meta contact's execute as I don't want to expose m_contact
+ * directly.
+ */
+ void execute() const;
+
+ void catchEvent( Kopete::MessageEvent * );
+
+ void updateVisibility();
+
+private slots:
+ void slotUpdateMetaContact();
+ void slotContactStatusChanged( Kopete::Contact * );
+ void slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & );
+ void slotContactAdded( Kopete::Contact * );
+ void slotContactRemoved( Kopete::Contact * );
+
+ void slotDisplayNameChanged();
+ void slotPhotoChanged();
+
+ void slotAddToNewGroup();
+ void slotIdleStateChanged( Kopete::Contact * =0L);
+
+ void slotConfigChanged();
+
+ void slotEventDone( Kopete::MessageEvent* );
+ void slotBlink();
+
+ void updateContactIcons();
+
+protected:
+ void okRename(int col);
+ void cancelRename(int col);
+
+private:
+ void initLVI();
+ void setDisplayMode( int mode, int iconMode );
+ void setMetaContactToolTipSourceForComponent( Kopete::UI::ListView::Component *comp );
+ QString key( int column, bool ascending ) const;
+ void updateContactIcon( Kopete::Contact * );
+ Kopete::UI::ListView::ContactComponent *contactComponent( const Kopete::Contact *c ) const;
+
+ Kopete::MetaContact *m_metaContact;
+ KopeteGroupViewItem *m_parentGroup;
+ QListView *m_parentView;
+ bool m_isTopLevel;
+
+ int m_pixelWide;
+
+ Kopete::OnlineStatus m_oldStatus;
+ QPixmap m_oldStatusIcon;
+ QPixmap m_originalBlinkIcon;
+
+ QTimer *mBlinkTimer;
+
+ QPtrDict<Kopete::Account> m_addContactActions;
+
+ bool mIsBlinkIcon;
+ int m_blinkLeft;
+
+ class Private;
+ Private *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/contactlist/kopetemetalvipropswidget.ui b/kopete/kopete/contactlist/kopetemetalvipropswidget.ui
new file mode 100644
index 00000000..e191d4b9
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetemetalvipropswidget.ui
@@ -0,0 +1,587 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>KopeteMetaLVIPropsWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteMetaLVIPropsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>530</width>
+ <height>457</height>
+ </rect>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>grpAddressbook</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Address Book Link</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="Kopete::UI::AddressBookLinkWidget">
+ <property name="name">
+ <cstring>widAddresseeLink</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnExportKABC</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>E&amp;xport Details...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Export contact's details to the KDE Address Book</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>107</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnImportKABC</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Import Contacts</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Import contacts from the KDE Address Book</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Display Name Source</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNameKABC</cstring>
+ </property>
+ <property name="text">
+ <string>Use addressbook &amp;name (needs addressbook link)</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNameContact</cstring>
+ </property>
+ <property name="text">
+ <string>From contact:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>cmbAccountName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Contact to synchronize the displayname with.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioNameCustom</cstring>
+ </property>
+ <property name="text">
+ <string>Cus&amp;tom:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>edtDisplayName</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="2" column="0">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Photo Source</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="1" rowspan="3" colspan="1">
+ <property name="name">
+ <cstring>photoLabel</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>92</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>64</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="text">
+ <string>Photo</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>radioPhotoKABC</cstring>
+ </property>
+ <property name="text">
+ <string>U&amp;se addressbook photo (needs addressbook link)</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioPhotoContact</cstring>
+ </property>
+ <property name="text">
+ <string>From contact:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>cmbAccountPhoto</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Contact to synchronize the displayname with.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioPhotoCustom</cstring>
+ </property>
+ <property name="text">
+ <string>Custom:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KURLComboRequester">
+ <property name="name">
+ <cstring>cmbPhotoUrl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnClearPhoto</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>chkSyncPhoto</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;ync photo to addressbook</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Ad&amp;vanced</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>grpIcons</cstring>
+ </property>
+ <property name="title">
+ <string>Icons</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblAway</cstring>
+ </property>
+ <property name="text">
+ <string>Awa&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbAway</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblOnline</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Online:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbOnline</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>chkUseCustomIcons</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom status &amp;icons</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check to set custom icons for this contact</string>
+ </property>
+ </widget>
+ <widget class="KIconButton" row="2" column="1">
+ <property name="name">
+ <cstring>icnbAway</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KIconButton" row="1" column="1">
+ <property name="name">
+ <cstring>icnbOnline</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KIconButton" row="1" column="3">
+ <property name="name">
+ <cstring>icnbOffline</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KIconButton" row="2" column="3">
+ <property name="name">
+ <cstring>icnbUnknown</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>lblOffline</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;ffline:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbOffline</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>lblUnknown</cstring>
+ </property>
+ <property name="text">
+ <string>Un&amp;known:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>icnbUnknown</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="4">
+ <property name="name">
+ <cstring>spacerHorizontal1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer12</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>170</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>btnExportKABC</tabstop>
+ <tabstop>btnImportKABC</tabstop>
+ <tabstop>edtDisplayName</tabstop>
+ <tabstop>cmbAccountName</tabstop>
+ <tabstop>cmbAccountPhoto</tabstop>
+ <tabstop>chkSyncPhoto</tabstop>
+ <tabstop>chkUseCustomIcons</tabstop>
+ <tabstop>icnbOnline</tabstop>
+ <tabstop>icnbAway</tabstop>
+ <tabstop>icnbOffline</tabstop>
+ <tabstop>icnbUnknown</tabstop>
+</tabstops>
+<customwidgets>
+ <customwidget>
+ <class>Kopete::UI::AddressBookLinkWidget</class>
+ <header>addressbooklinkwidget.h</header>
+ </customwidget>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>addressbooklinkwidget.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kicondialog.h</includehint>
+ <includehint>kicondialog.h</includehint>
+ <includehint>kicondialog.h</includehint>
+ <includehint>kicondialog.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp b/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp
new file mode 100644
index 00000000..9dc910dd
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetestatusgroupviewitem.cpp
@@ -0,0 +1,51 @@
+/*
+ kopetestatusgroupviewitem.cpp
+
+ Class to show a status folder
+
+ Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include "kopetestatusgroupviewitem.h"
+
+KopeteStatusGroupViewItem::KopeteStatusGroupViewItem( Kopete::OnlineStatus::StatusType status_ , QListView *parent, const char *name )
+ : QListViewItem(parent,name)
+{
+ m_status = status_;
+}
+
+KopeteStatusGroupViewItem::~KopeteStatusGroupViewItem()
+{
+}
+
+QString KopeteStatusGroupViewItem::key( int, bool ) const
+{
+ switch (m_status)
+ {
+ case Kopete::OnlineStatus::Online :
+ return "A";
+ break;
+ case Kopete::OnlineStatus::Away :
+ return "B";
+ break;
+ case Kopete::OnlineStatus::Offline :
+ return "C";
+ break;
+ case Kopete::OnlineStatus::Unknown :
+ default:
+ return "D";
+ }
+}
+
diff --git a/kopete/kopete/contactlist/kopetestatusgroupviewitem.h b/kopete/kopete/contactlist/kopetestatusgroupviewitem.h
new file mode 100644
index 00000000..8b1a930f
--- /dev/null
+++ b/kopete/kopete/contactlist/kopetestatusgroupviewitem.h
@@ -0,0 +1,43 @@
+/*
+ kopetestatusgroupviewitem.h
+
+ Class to show a status folder
+
+ Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETESTATUSGROUPVIEWITEM_H
+#define KOPETESTATUSGROUPVIEWITEM_H
+
+#include <klistview.h>
+#include "kopetemetacontact.h"
+
+/**
+ *@author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+
+ class KopeteStatusGroupViewItem : public QListViewItem
+{
+public:
+ KopeteStatusGroupViewItem( Kopete::OnlineStatus::StatusType status_ , QListView *parent, const char *name=0);
+ ~KopeteStatusGroupViewItem();
+
+private:
+
+ Kopete::OnlineStatus::StatusType m_status;
+ QString key( int column, bool ascending ) const;
+
+};
+
+#endif
diff --git a/kopete/kopete/cr16-mime-kopete_emoticons.png b/kopete/kopete/cr16-mime-kopete_emoticons.png
new file mode 100644
index 00000000..2f596e17
--- /dev/null
+++ b/kopete/kopete/cr16-mime-kopete_emoticons.png
Binary files differ
diff --git a/kopete/kopete/cr22-app-kopete_all_away.png b/kopete/kopete/cr22-app-kopete_all_away.png
new file mode 100644
index 00000000..b82d9abd
--- /dev/null
+++ b/kopete/kopete/cr22-app-kopete_all_away.png
Binary files differ
diff --git a/kopete/kopete/cr22-app-kopete_offline.png b/kopete/kopete/cr22-app-kopete_offline.png
new file mode 100644
index 00000000..da80a3c9
--- /dev/null
+++ b/kopete/kopete/cr22-app-kopete_offline.png
Binary files differ
diff --git a/kopete/kopete/cr22-app-kopete_some_away.png b/kopete/kopete/cr22-app-kopete_some_away.png
new file mode 100644
index 00000000..d3587a19
--- /dev/null
+++ b/kopete/kopete/cr22-app-kopete_some_away.png
Binary files differ
diff --git a/kopete/kopete/cr22-app-kopete_some_online.png b/kopete/kopete/cr22-app-kopete_some_online.png
new file mode 100644
index 00000000..cb018a88
--- /dev/null
+++ b/kopete/kopete/cr22-app-kopete_some_online.png
Binary files differ
diff --git a/kopete/kopete/cr22-mime-kopete_emoticons.png b/kopete/kopete/cr22-mime-kopete_emoticons.png
new file mode 100644
index 00000000..5aa7437d
--- /dev/null
+++ b/kopete/kopete/cr22-mime-kopete_emoticons.png
Binary files differ
diff --git a/kopete/kopete/eventsrc b/kopete/kopete/eventsrc
new file mode 100644
index 00000000..9d3cf2f7
--- /dev/null
+++ b/kopete/kopete/eventsrc
@@ -0,0 +1,1950 @@
+[!Global!]
+IconName=kopete
+Comment=Kopete Messenger
+Comment[ar]=مرسال Kopete
+Comment[be]=Праграма імгненных паведамленнÑÑž Kopete
+Comment[bg]=МеÑинджър
+Comment[bn]=কপেট বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•
+Comment[ca]=Missatger Kopete
+Comment[cs]=Kopete komunikátor
+Comment[cy]=Negesydd Kopete
+Comment[de]=Kopete-Nachrichtendienst
+Comment[el]=Αποστολέας μηνυμάτων Kopete
+Comment[eo]=Kopete-mesaÄilo
+Comment[es]=Mensajería de Kopete
+Comment[fa]=پیام‌رسان Kopete
+Comment[fi]=Kopete-viestin
+Comment[fr]=Messager Kopete
+Comment[gl]=Mensaxería instantánea con Kopete
+Comment[he]=תוכנת ×”×ž×¡×¨×™× ×”×ž×™×™×“×™× Kopete
+Comment[hi]=के-ऑपà¥à¤Ÿà¥€ मैसेंजर
+Comment[hu]=Kopete üzenetküldő
+Comment[is]=Kopete samtalsforrit
+Comment[it]=Messaggistica Kopete
+Comment[ja]=Kopete メッセンジャー
+Comment[ka]=Kopete მესინჯერი
+Comment[kk]=Kopete хабарлаÑу бағдарламаÑÑ‹
+Comment[km]=កម្មវិធី​ផ្ញើ​សំបុážáŸ’ážš Kopete
+Comment[lt]=Kopete žinuÄių klientas
+Comment[mk]=Kopete глаÑник
+Comment[nb]=Kopete meldingsprogram
+Comment[nds]=Kopete-Kortnarichtendeenst
+Comment[ne]=कोपेट मेसेनà¥à¤œà¤°
+Comment[nn]=Lynmeldingsprogrammet Kopete
+Comment[pl]=Komunikator Kopete
+Comment[pt]=Mensageiro Kopete
+Comment[pt_BR]=Mensageiro Kopete
+Comment[ro]=Mesaje instantanee Kopete
+Comment[ru]=Программа обмена ÑообщениÑми Kopete
+Comment[se]=Kopete-šleađgadieđáhusprográmma
+Comment[sl]=SporoÄilnik Kopete
+Comment[sr]=ГлаÑник Kopete
+Comment[sr@Latn]=Glasnik Kopete
+Comment[sv]=Kopete meddelandeklient
+Comment[ta]=உடனடி தூதரà¯
+Comment[tg]=Пайёмбари Kopete
+Comment[uk]=Програма обміну повідомленнÑми Kopete
+Comment[uz]=Kopete xabar almashish vositasi
+Comment[uz@cyrillic]=Kopete хабар алмашиш воÑитаÑи
+Comment[ven]=Murumiwa wa Kopete
+Comment[zh_CN]=Kopete 信使
+
+[kopete_contact_incoming]
+Name=Incoming
+Name[ar]=وارد
+Name[be]=Уваходнае
+Name[bg]=ПриÑтигна ново Ñъобщение
+Name[bn]=অনà§à¦¤à¦°à§à¦®à§à¦–ী
+Name[br]=Nevez
+Name[bs]=Ulazni
+Name[ca]=Entrant
+Name[cs]=Příchozí
+Name[cy]=Cyrraedd
+Name[da]=Indkommende
+Name[de]=Eingang
+Name[el]=ΕισεÏχόμενο
+Name[eo]=Alvenantaj
+Name[es]=Entrante
+Name[et]=Sisenev
+Name[eu]=Sarrerako
+Name[fa]=واردشونده
+Name[fi]=Saapuva
+Name[fr]=Entrant
+Name[ga]=Ag Teacht
+Name[gl]=Entrante
+Name[he]=נכנס
+Name[hi]=आवक
+Name[hr]=Dolazni
+Name[hu]=Bejövő
+Name[is]=à leið inn
+Name[it]=In entrata
+Name[ja]=å—ä¿¡
+Name[ka]=შემáƒáƒ›áƒáƒ•áƒáƒšáƒ˜
+Name[kk]=КіріÑ
+Name[km]=ចូល
+Name[lt]=Gaunami
+Name[mk]=Дојдовни
+Name[nb]=Innkommende
+Name[nds]=Rinkamen
+Name[ne]=आगमन
+Name[nl]=Inkomend
+Name[nn]=Innkomande
+Name[pa]=ਆ ਰਹੇ
+Name[pl]=NadchodzÄ…ce
+Name[pt]=Recebido
+Name[pt_BR]=Entrada
+Name[ro]=Primire
+Name[ru]=ВходÑщее
+Name[se]=Boahtti
+Name[sk]=Prichádzajúca
+Name[sl]=PrihajajoÄe
+Name[sr]=Долазећи
+Name[sr@Latn]=Dolazeći
+Name[sv]=Inkommande
+Name[ta]=உளà¯à®µà®°à®µà¯
+Name[tg]=Воридшаванда
+Name[tr]=Gelen
+Name[uk]=Вхідна
+Name[ven]=Zwine zwa khoutou da
+Name[wa]=En intrêye
+Name[xh]=Engenayo
+Name[zh_CN]=收到
+Name[zh_HK]=å…§é€
+Name[zh_TW]=新訊æ¯
+Comment=An incoming message has been received
+Comment[be]=Ðтрыманае ўваходнае паведамленне
+Comment[bg]=ПриÑтигна ново Ñъобщение
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ অনà§à¦¤à¦°à§à¦®à§à¦–ী বারà§à¦¤à¦¾ গà§à¦°à¦¹à¦£ করা হয়েছে
+Comment[bs]=Primljena je poruka
+Comment[ca]=S'ha rebut un missatge entrant
+Comment[cs]=Přišla zpráva
+Comment[da]=En indkommende besked er blevet modtaget
+Comment[de]=Eingehende Nachricht
+Comment[el]=Ένα εισεÏχόμενο μήνυμα παÏαλήφθηκε
+Comment[eo]=Alvenanta mesaÄo estis ricevita
+Comment[es]=Se ha recibido un mensaje entrante
+Comment[et]=Saabus sõnum
+Comment[eu]=Sarrerako mezu bat jaso da
+Comment[fa]=یک پیام واردشده دریاÙت شده است
+Comment[fi]=Viesti on saapunut
+Comment[fr]=Un message entrant a été reçu
+Comment[gl]=Recibiuse unha nova mensaxe
+Comment[he]=התקבל מסר נכנס
+Comment[hr]=Primljena je dolazna poruka
+Comment[hu]=Bejövő üzenet érkezett
+Comment[is]=Skeyti móttekið
+Comment[it]=Ricevuto un nuovo messaggio
+Comment[ja]=å—信メッセージãŒå±Šãã¾ã—ãŸ
+Comment[ka]=შემáƒáƒ›áƒáƒ•áƒáƒšáƒ˜ შეტყáƒáƒ‘ინებრმიღებულ იქნáƒ
+Comment[kk]=Хабарлама келді
+Comment[km]=បាន​ទទួល​សំបុážáŸ’រ​មួយ​ចូល
+Comment[lt]=Gauta nauja žinutė
+Comment[mk]=Примена е дојдовна порака
+Comment[nb]=Ny melding ankommet
+Comment[nds]=En Naricht keem rin
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ आगमन सनà¥à¤¦à¥‡à¤¶ पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो
+Comment[nl]=Inkomend bericht binnengekomen
+Comment[nn]=Ny melding er komen
+Comment[pl]=Nadeszła nowa wiadomość
+Comment[pt]=Foi recebida uma mensagem
+Comment[pt_BR]=chegou nova mensagem
+Comment[ro]=A fost primit un mesaj
+Comment[ru]=Получено новое Ñообщение
+Comment[se]=Boahtti dieđáhus vuostáiváldon
+Comment[sk]=Príchod správy
+Comment[sl]=Prejeto je bilo prihajajoÄe sporoÄilo
+Comment[sr]=Примљена је долазећа порука
+Comment[sr@Latn]=Primljena je dolazeća poruka
+Comment[sv]=Ett inkommande meddelande har anlänt
+Comment[ta]=உளà¯à®µà®°à¯à®®à¯ தகவல௠பெறபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Пайёми воридшаванда қабул гардид
+Comment[tr]=Bir gelen mesaj alındı
+Comment[uk]=Отримано нове повідомленнÑ
+Comment[uz]=Xabar qabul qilindi
+Comment[uz@cyrillic]=Хабар қабул қилинди
+Comment[zh_CN]=收到了消æ¯
+Comment[zh_HK]=收到新訊æ¯
+Comment[zh_TW]=已收到新訊æ¯
+default_sound=Kopete_Received.ogg
+default_presentation=64
+
+[kopete_outgoing]
+Name=Outgoing
+Name[ar]=خارج
+Name[be]=Зыходнае
+Name[bg]=Изпратено е Ñъобщение
+Name[bn]=বহিরà§à¦®à§à¦–ী
+Name[bs]=Izlazni
+Name[ca]=Sortint
+Name[cs]=Odchozí
+Name[cy]=Allfynd
+Name[da]=Udgående
+Name[de]=Ausgang
+Name[el]=ΕξεÏχόμενο
+Name[eo]=Elirantaj
+Name[es]=Saliente
+Name[et]=Väljuv
+Name[eu]=Irteerakoa
+Name[fa]=خروج
+Name[fi]=Lähtevä
+Name[fr]=Sortant
+Name[ga]=Ag Imeacht
+Name[gl]=Saliente
+Name[he]=יוצ×
+Name[hi]=जावक
+Name[hr]=Izlazno
+Name[hu]=Kimenő
+Name[is]=à leið út
+Name[it]=In uscita
+Name[ja]=é€ä¿¡
+Name[ka]=გáƒáƒ›áƒáƒ•áƒáƒšáƒ˜
+Name[kk]=ШығыÑ
+Name[km]=áž…áŸáž‰
+Name[lt]=IÅ¡siunÄiamos
+Name[mk]=Појдовни
+Name[nb]=Utgående
+Name[nds]=Rutgahn
+Name[ne]=निरà¥à¤—मन
+Name[nl]=Uitgaand
+Name[nn]=Utgåande
+Name[pa]=ਜਾ ਰਹੇ
+Name[pl]=WychodzÄ…ce
+Name[pt]=A Enviar
+Name[pt_BR]=Saída
+Name[ro]=Trimitere
+Name[ru]=ИÑходÑщее
+Name[se]=Manni
+Name[sk]=Odchádzajúca
+Name[sl]=OdhajajoÄe
+Name[sr]=Одлазећи
+Name[sr@Latn]=Odlazeći
+Name[sv]=Utgående
+Name[ta]=வெளிசெலà¯à®²à¯à®¤à®²à¯
+Name[tg]=Хориҷшаванда
+Name[tr]=Giden
+Name[uk]=Вихідна
+Name[ven]=Zwine zwa khou tou bva
+Name[wa]=E rexhowe
+Name[xh]=Ephumayo
+Name[zh_CN]=å‘出
+Name[zh_HK]=外發
+Name[zh_TW]=é€å‡ºè¨Šæ¯
+Comment=An outgoing message has been sent
+Comment[be]=Ðтрыманае зыходнае паведамленне
+Comment[bg]=Изпратено е Ñъобщение
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ বহিরà§à¦®à§à¦–ী বারà§à¦¤à¦¾ পাঠান হয়েছে
+Comment[bs]=Poslana je poruka
+Comment[ca]=S'ha enviat un missatge sortint
+Comment[cs]=Byla odeslána zpráva
+Comment[da]=En udgående besked er blevet sendt
+Comment[de]=Ausgehende Nachricht wurde versendet
+Comment[el]=Ένα εξεÏχόμενο μήνυμα στάλθηκε
+Comment[eo]=Eliranta mesaÄo estis sendita
+Comment[es]=Se ha enviado un mensaje entrante
+Comment[et]=Saadeti sõnum
+Comment[eu]=Irteerako mezu bat bidali da
+Comment[fa]=یک پیام خروجی ارسال شده است
+Comment[fi]=Viesti on lähetetty
+Comment[fr]=Un message sortant a été envoyé
+Comment[gl]=Enviouse unha nova mensaxe
+Comment[he]=מסר ×™×•×¦× × ×©×œ×—
+Comment[hr]=Primljena je odlazna poruka
+Comment[hu]=Kimenő üzenet továbbítódott
+Comment[is]=Skeyti sent
+Comment[it]=Inviato un messaggio
+Comment[ja]=é€ä¿¡ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’é€ã‚Šã¾ã—ãŸ
+Comment[ka]=გáƒáƒ›áƒáƒ•áƒáƒšáƒ˜ შეტყáƒáƒ‘ინებრმიღებულ იქნáƒ
+Comment[kk]=Хабарлама жіберілді
+Comment[km]=បាន​ផ្ញើ​សំបុážáŸ’រ​មួយ​ចáŸáž‰
+Comment[lt]=Žinutė išsiųsta
+Comment[mk]=ИÑпратена е појдовна порака
+Comment[nb]=Utgående melding er sendt
+Comment[nds]=En Naricht wöör afschickt
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ निरà¥à¤—ामन सनà¥à¤¦à¥‡à¤¶ पठाइयो
+Comment[nl]=Uitgaand bericht is verzonden
+Comment[nn]=Utgåande melding er send
+Comment[pl]=Wiadomość została wysłana
+Comment[pt]=Foi enviada uma mensagem
+Comment[pt_BR]=mensagem enviada
+Comment[ro]=A fost trimis un mesaj
+Comment[ru]=ИÑходÑщее Ñообщение отправлено
+Comment[se]=Manni dieđáhus sáddejuvvon
+Comment[sk]=Odoslanie správy
+Comment[sl]=Poslano je bilo odhajajoÄe poroÄilo
+Comment[sr]=Одлазећа порука је поÑлата
+Comment[sr@Latn]=Odlazeća poruka je poslata
+Comment[sv]=Ett utgående meddelande har skickats
+Comment[ta]=வெளிசà¯à®šà¯†à®²à¯à®² வேணà¯à®Ÿà®¿à®¯ தகவல௠அனà¯à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Пайёми хориҷшаванда фириÑтода шуд
+Comment[tr]=Bir giden mesaj gönderildi
+Comment[uk]=ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ відіÑлано
+Comment[uz]=Xabar joʻnatildi
+Comment[uz@cyrillic]=Хабар жўнатилди
+Comment[zh_CN]=é€å‡ºäº†æ¶ˆæ¯
+Comment[zh_HK]=訊æ¯å·²ç™¼å‡º
+Comment[zh_TW]=å·²é€å‡ºè¨Šæ¯
+default_sound=Kopete_Sent.ogg
+default_presentation=0
+
+
+[kopete_contact_online]
+Name=Online
+Name[ar]=متصل
+Name[be]=ЗлучÑнне з Ñеткай
+Name[bg]=Включи Ñе приÑтел
+Name[bn]=অনলাইন
+Name[br]=Enlinenn
+Name[ca]=Connectat
+Name[cy]=Ar-lein
+Name[el]=Σε σÏνδεση
+Name[eo]=Konektata
+Name[es]=Conectado
+Name[et]=Võrgus
+Name[fa]=برخط
+Name[fi]=Yhteys auki
+Name[fr]=Se connecter
+Name[ga]=Ar Líne
+Name[gl]=En liña
+Name[he]=מקוון
+Name[hi]=आनलाइन
+Name[is]=Tengdur
+Name[it]=In linea
+Name[ja]=オンライン
+Name[ka]=ხáƒáƒ–ზე
+Name[kk]=Желіде
+Name[km]=លើ​បណ្ážáž¶áž‰
+Name[lt]=PrisijungÄ™
+Name[mk]=Ðа линија
+Name[nb]=Koble til
+Name[nds]=Tokoppelt
+Name[ne]=अनलाइन
+Name[nl]=Online gaan
+Name[nn]=Tilkopla
+Name[pa]=ਆਨਲਾਇਨ
+Name[pl]=Dostępny
+Name[pt]=Ligado
+Name[pt_BR]=Ficar On-line
+Name[ro]=Conectat
+Name[ru]=Ð’ Ñети
+Name[sk]=On-line
+Name[sl]=Na zvezi
+Name[sr]=Ðа вези
+Name[sr@Latn]=Na vezi
+Name[sv]=Uppkopplad
+Name[ta]=இணையதà¯à®¤à®¿à®²à¯
+Name[tg]=Дар шабака
+Name[tr]=Çevirimiçi
+Name[uk]=В мережі
+Name[uz]=Onlayn
+Name[uz@cyrillic]=Онлайн
+Name[wa]=So les fyis
+Name[zh_CN]=上线
+Name[zh_HK]=上線
+Name[zh_TW]=上線
+Comment=A contact has come online
+Comment[be]=Чалавек злучыўÑÑ Ð· Ñеткай
+Comment[bg]=Включи Ñе приÑтел
+Comment[bn]=যোগাযোগ তালিকাভà§à¦•à§à¦¤ à¦à¦•à¦œà¦¨ অনলাইন হয়েছে
+Comment[bs]=Kontakt je došao online
+Comment[ca]=Un contacte s'ha connectat
+Comment[cs]=Osoba je online
+Comment[da]=En kontakt er gået på nettet
+Comment[de]=Ein Benutzer wechselt auf Online
+Comment[el]=Μια επαφή μόλις συνδέθηκε
+Comment[es]=Se ha conectado un contacto
+Comment[et]=Kontakt tuli võrku
+Comment[eu]=Kontaktu bat on line jarri da
+Comment[fa]=یک تماس برخط شد
+Comment[fi]=Yhteyshenkilö on saapunut verkkoon
+Comment[fr]=Un contact s'est connecté
+Comment[gl]=Acaba de conectarse un contact
+Comment[he]=×יש קשר התחבר
+Comment[hu]=Egy partner online állapotúvá vált
+Comment[is]=Notandi tengist
+Comment[it]=Un utente è tornato in linea
+Comment[ja]=コンタクトãŒã‚ªãƒ³ãƒ©ã‚¤ãƒ³ã«ãªã‚Šã¾ã—ãŸ
+Comment[ka]=მეგáƒáƒ‘áƒáƒ áƒ˜ ხáƒáƒ–ზეáƒ
+Comment[kk]=ҚатынаÑушы желіге кірді
+Comment[km]=ទំនាក់​ទំនង​មួយបាន​ក្លាយ​ទៅ​ជា "លើ​បណ្ដាញ"
+Comment[lt]=PrisijungÄ— kontaktinis asmuo
+Comment[mk]=Контактот е на линија
+Comment[nb]=En bruker kobler til på nettet
+Comment[nds]=En Kontakt hett sik tokoppelt
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ समà¥à¤ªà¤°à¥à¤• अनलाइन आयो
+Comment[nl]=Gebruiker gaat online
+Comment[nn]=Kontakt koplar seg til nettet
+Comment[pl]=Użytkownik stał się dostępny
+Comment[pt]=Um contacto ligou-se
+Comment[pt_BR]=O usuário ficou on-line
+Comment[ru]=Пользователь вошёл в Ñеть
+Comment[se]=Oktavuohta lea «online»
+Comment[sk]=Užívateľ je on-line
+Comment[sl]=Uporabnik je vzpostavil povezavo
+Comment[sr]=КориÑник Ñе повезао
+Comment[sr@Latn]=Korisnik se povezao
+Comment[sv]=En användare har kopplat upp
+Comment[ta]=பயனர௠உரையாட வநà¯à®¤à¯à®³à¯à®³à®¾à®°à¯
+Comment[tg]=Корванде ба шабака ворид шуд
+Comment[tr]=Bir bağlantı bağlı durumda
+Comment[uk]=КориÑтувач увійшов у мережу
+Comment[zh_CN]=è”系人上线
+Comment[zh_HK]=有個è¯çµ¡äººå·²ä¸Šç·š
+Comment[zh_TW]=è¯çµ¡äººå·²ä¸Šç·š
+default_sound=Kopete_User_is_Online.ogg
+default_presentation=16
+
+[kopete_contact_offline]
+Name=Offline
+Name[ar]=غير متصل
+Name[be]=Па-за Ñеткай
+Name[bg]=Изключи Ñе приÑтел
+Name[bn]=অফলাইন
+Name[br]=N'eo ket enlinenn
+Name[ca]=Desconnectat
+Name[cy]=All-lein
+Name[da]=GÃ¥ offline
+Name[el]=ΧωÏίς σÏνδεση
+Name[eo]=Nekonektata
+Name[es]=Desconectar
+Name[et]=Pole võrgus
+Name[eu]=Deskonektatuta
+Name[fa]=برون‌خط
+Name[fi]=Yhteys katkaistu
+Name[fr]=Se déconnecter
+Name[ga]=As Líne
+Name[gl]=Desconectar
+Name[he]=מנותק
+Name[hi]=ऑफ़लाइन
+Name[hr]=Neumrežen
+Name[is]=Aftengdur
+Name[it]=Non in linea
+Name[ja]=オフライン
+Name[ka]=ხáƒáƒ–იდáƒáƒœ გáƒáƒ¡áƒ£áƒšáƒ˜
+Name[kk]=Желіден Ñ‚Ñ‹Ñ
+Name[km]=ក្រៅ​បណ្ដាញ
+Name[lt]=AtsijungÄ™
+Name[mk]=Ðе на линија
+Name[nb]=Koble fra
+Name[nds]=Afkoppelt
+Name[ne]=अफलाइन
+Name[nl]=Offline gaan
+Name[nn]=Fråkopla
+Name[pa]=ਆਫਲਾਇਨ
+Name[pl]=Niedostępny
+Name[pt]=Desligado
+Name[pt_BR]=Desconectado
+Name[ro]=Deconectat
+Name[ru]=Ðвтономный режим
+Name[sk]=Off-line
+Name[sl]=Brez zveze
+Name[sr]=Ðије на вези
+Name[sr@Latn]=Nije na vezi
+Name[sv]=Nerkopplad
+Name[ta]=இணையதà¯à®¤à®¿à®²à¯ இலà¯à®²à¯ˆ
+Name[tg]=УÑули Худмухтор
+Name[tr]=Bağlı değil
+Name[uk]=Вимкнений
+Name[uz]=Oflayn
+Name[uz@cyrillic]=Офлайн
+Name[zh_CN]=离线
+Name[zh_HK]=離線
+Name[zh_TW]=離線
+Comment=A contact has gone offline
+Comment[be]=Чалавек адлучыўÑÑ Ð°Ð´ Ñеткі
+Comment[bg]=Изключи Ñе приÑтел
+Comment[bn]=যোগাযোগ তালিকাভà§à¦•à§à¦¤ à¦à¦•à¦œà¦¨ অফলাইন হয়ে গিয়েছে
+Comment[bs]=Kontakt je otišao offline
+Comment[ca]=Un contacte s'ha desconnectat
+Comment[cs]=Osoba je offline
+Comment[da]=En kontakt er gået af nettet
+Comment[de]=Ein Benutzer wechselt auf Offline
+Comment[el]=Μια επαφή μόλις αποσυνδέθηκε
+Comment[es]=Se ha desconectado un contacto
+Comment[et]=Kontakt läks võrgust ära
+Comment[eu]=Kontaktu bat deskonektatu da
+Comment[fa]=یک تماس برون‌خط شد
+Comment[fi]=Yhteyshenkilö on lähtenyt verkosta
+Comment[fr]=Un contact s'est déconnecté
+Comment[gl]=Acaba de desconctarse un contacto
+Comment[he]=משתמש התנתק
+Comment[hu]=Egy partner offline állapotúvá vált
+Comment[is]=Notandi aftengist
+Comment[it]=Un utente è andato non in linea
+Comment[ja]=コンタクトãŒã‚ªãƒ•ãƒ©ã‚¤ãƒ³ã«ãªã‚Šã¾ã—ãŸ
+Comment[ka]=მეგáƒáƒ‘áƒáƒ áƒ˜ ხáƒáƒ–იდáƒáƒœ გáƒáƒ•áƒ˜áƒ“áƒ
+Comment[kk]=ҚатынаÑушы желіден шықты
+Comment[km]=ទំនាក់​ទំនង​មួយបាន​ក្លាយ​ទៅ​ជា "ក្រៅ​បណ្ដាញ"
+Comment[lt]=Kontaktinis asmuo atsijungÄ—
+Comment[mk]=Контактот Ñе иÑклучи
+Comment[nb]=En bruker kobler fra nettet
+Comment[nds]=En Kontakt hett sik afkoppelt
+Comment[ne]=समà¥à¤ªà¤°à¥à¤• अफलाइन भयो
+Comment[nl]=Gebruiker gaat offline
+Comment[nn]=Kontakt koplar seg frå nettet
+Comment[pl]=Użytkownik stał się niedostępny
+Comment[pt]=Um contacto desligou-se
+Comment[pt_BR]=O usuário se desconectou
+Comment[ru]=Пользователь вышел из Ñети
+Comment[se]=Oktavuohta lea «offline»
+Comment[sk]=Užívateľ je off-line
+Comment[sl]=Uporabnik je prekinil povezavo
+Comment[sr]=КориÑник је отишао Ñа везе
+Comment[sr@Latn]=Korisnik je otišao sa veze
+Comment[sv]=En användare har kopplat ner
+Comment[ta]=பயனர௠வெளியேறிவிடà¯à®Ÿà®¾à®°à¯
+Comment[tg]=Корванде аз шабака хориҷ шуд
+Comment[tr]=Bir bağlantı çevrim dışı
+Comment[uk]=КориÑтувач вийшов з мережі
+Comment[zh_CN]=è”系人离线
+Comment[zh_HK]=有個è¯çµ¡äººå·²é›¢ç·š
+Comment[zh_TW]=è¯çµ¡äººå·²é›¢ç·š
+default_sound=Kopete_Event.ogg
+default_presentation=0
+
+
+[kopete_contact_status_change]
+Name=Status Change
+Name[ar]=تغير حالة
+Name[be]=Змена Ñтану
+Name[bg]=Променено ÑÑŠÑтоÑние на приÑтел
+Name[bn]=অবসà§à¦¥à¦¾ পরিবরà§à¦¤à¦¨
+Name[bs]=Izmjena statusa
+Name[ca]=Canvia estatus
+Name[cs]=Změna stavu
+Name[cy]=Newid Cyflwr
+Name[da]=Statusændring
+Name[de]=Statuswechsel
+Name[el]=Αλλαγή κατάστασης
+Name[eo]=Status-ÅanÄo
+Name[es]=Cambiar estado
+Name[et]=Staatuse muutus
+Name[eu]=Egoera aldaketa
+Name[fa]=تغییر وضعیت
+Name[fi]=Tilan muuttuminen
+Name[fr]=Changement d'état
+Name[ga]=Athrú Stádais
+Name[gl]=Cambio de estado
+Name[he]=שינוי מצב
+Name[hi]=सà¥à¤¥à¤¿à¤¤à¤¿ परिवरà¥à¤¤à¤¨
+Name[hr]=Promjena statusa
+Name[hu]=Ãllapotváltozás
+Name[is]=Breyta stöðu
+Name[it]=Cambio di stato
+Name[ja]=状態ã®å¤‰åŒ–
+Name[ka]=მდგáƒáƒ›áƒáƒ áƒ”áƒáƒ‘ის შეცვლáƒ
+Name[kk]=Күй-жайы өзгерді
+Name[km]=ការផ្លាស់ប្ដូរ​ស្ážáž¶áž“ភាព
+Name[lt]=BÅ«senos pakitimas
+Name[mk]=Промена на ÑтатуÑот
+Name[nb]=Endre status
+Name[nds]=Statusännern
+Name[ne]=वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ परिवरà¥à¤¤à¤¨
+Name[nl]=Statusverandering
+Name[nn]=Statusendring
+Name[pa]=ਹਾਲਤ ਬਦਲੀ
+Name[pl]=Zmiana statusu
+Name[pt]=Mudança de Estado
+Name[pt_BR]=Mudança de Status
+Name[ro]=Modificare stare
+Name[ru]=Изменение ÑтатуÑа
+Name[se]=Stáhtusrievdadus
+Name[sk]=Zmena stavu
+Name[sl]=Sprememba stanja
+Name[sr]=Промена ÑтатуÑа
+Name[sr@Latn]=Promena statusa
+Name[sv]=Statusändring
+Name[ta]=நிலை மாறà¯à®±à®®à¯
+Name[tg]=Ивази Ҳолат
+Name[tr]=Durum DeÄŸiÅŸimi
+Name[uk]=Зміна Ñтану
+Name[uz]=Holati oʻzgardi
+Name[uz@cyrillic]=Ҳолати ўзгарди
+Name[wa]=Candjmint di statut
+Name[zh_CN]=状æ€æ›´æ”¹
+Name[zh_HK]=狀態改變
+Name[zh_TW]=狀態改變
+Comment=A contact's online status has changed
+Comment[be]=Сеткавы Ñтан чалавека змÑніўÑÑ
+Comment[bg]=Променено ÑÑŠÑтоÑние на приÑтел
+Comment[bn]=যোগাযোগ তালিকাভà§à¦•à§à¦¤ à¦à¦•à¦œà¦¨à§‡à¦° অনলাইন অবসà§à¦¥à¦¾à¦° পরিবরà§à¦¤à¦¨ হয়েছে
+Comment[bs]=Kontakt je promijenio svoj online status
+Comment[ca]=L'estat d'un contacte ha canviat
+Comment[cs]=Osoba změnila online stav
+Comment[da]=En kontakts status på nettet er ændret
+Comment[de]=Ein Benutzer wechselt seinen Status.
+Comment[el]=Μια επαφή άλλαξε αυτή τη στιγμή κατάσταση
+Comment[es]=El estado de conexión de un contacto ha cambiado
+Comment[et]=Kontakt muutis oma võrgusoleku staatust
+Comment[eu]=Kontaktu baten on line egoera aldatu da
+Comment[fa]=وضعیت تماس برخط تغییر یاÙت
+Comment[fi]=Yhteyshenkilön online-tila on muuttunut
+Comment[fr]=Un contact a changé son état de connexion
+Comment[gl]=O estado en liña dun contacto acaba de mudar
+Comment[he]=×יש הקשר שינה ×ת מצב ההתחברות שלו
+Comment[hu]=Egy partner állapota megváltozott
+Comment[is]=Notandi breytir um stöðu
+Comment[it]=Un utente ha modificato il suo stato in linea
+Comment[ja]=コンタクトã®æŽ¥ç¶šçŠ¶æ…‹ãŒå¤‰ã‚ã‚Šã¾ã—ãŸ
+Comment[ka]=მეგáƒáƒ‘რის ხáƒáƒ–ზე ყáƒáƒ¤áƒœáƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ˜ შეიცვáƒáƒšáƒ
+Comment[kk]=ҚатынаÑушының желідегі күйі өзгерді
+Comment[km]=បាន​ផ្លាស់ប្ដូរ​ស្ážáž¶áž“ភាព​លើ​បណ្ដាញ​របស់​ទំនាក់​ទំនង​មួយ
+Comment[lt]=Kontaktinio asmens būsena pakito
+Comment[mk]=СтатуÑот на контактот на линија Ñе измени
+Comment[nb]=En bruker endret tilkobling til nettet
+Comment[nds]=De Tokoppel-Status vun en Kontakt hett sik ännert
+Comment[ne]=समà¥à¤ªà¤°à¥à¤•à¤•à¥‹ अनलाइन वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ परिवरà¥à¤¤à¤¨ भयो
+Comment[nl]=Gebruiker wijzigde online status
+Comment[nn]=Kontakt endrar status
+Comment[pl]=Użytkownik zmienił swój stan
+Comment[pt]=O contacto mudou o seu estado de ligação.
+Comment[pt_BR]=O usuário mudou o seu status de on-line
+Comment[ru]=Пользователь изменил Ñвоё ÑоÑтоÑние в Ñети
+Comment[se]=Oktavuohta rievdada stáhtusa
+Comment[sk]=Zmena stavu pripojenia užívateľa
+Comment[sl]=Uporabnik je spremenil svoje stanje
+Comment[sr]=КориÑник је променио Ñвој ÑÑ‚Ð°Ñ‚ÑƒÑ Ð½Ð° вези
+Comment[sr@Latn]=Korisnik je promenio svoj status na vezi
+Comment[sv]=Uppkopplingsstatus för en användare har ändrats
+Comment[ta]= இணைய உரையாடலில௠பயனரின௠நிலை மாறà¯à®±à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Ҳолати алоқаҳои пайваÑта тағир дода шуданд
+Comment[tr]=Bağlantının durumu bağlı olarak değişti
+Comment[uk]=КориÑтувач змінив Ñвій Ñтан в мережі
+Comment[zh_CN]=è”系人更改了他的在线状æ€
+Comment[zh_HK]=有個è¯çµ¡äººçš„上線狀態改變了
+Comment[zh_TW]=è¯çµ¡äººçš„上線狀態已改變
+default_sound=Kopete_Event.ogg
+default_presentation=0
+
+[kopete_contact_highlight]
+Name=Highlight
+Name[ar]=تمييز
+Name[bg]=ОткроÑване
+Name[bn]=গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£
+Name[br]=Splannadur
+Name[bs]=Isticanje
+Name[ca]=Ressaltat
+Name[cs]=Zvýraznění
+Name[cy]=Amlygu
+Name[da]=Fremhæv
+Name[de]=Hervorhebung
+Name[el]=Τονισμός
+Name[eo]=Lumaĵo
+Name[es]=Resaltar
+Name[et]=Esiletõstmine
+Name[eu]=Nabarmendu
+Name[fa]=مشخص
+Name[fi]=Korostus
+Name[fr]=Surlignement
+Name[ga]=Aibhsiú
+Name[gl]=Resaltar
+Name[he]=מודגש
+Name[hi]=उभारें
+Name[hr]=Isticanje
+Name[hu]=Kiemelés
+Name[is]=Merkja
+Name[it]=Evidenziazione
+Name[ja]=強調
+Name[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული
+Name[kk]=Ерекше
+Name[km]=សំážáž¶áž“់
+Name[lt]=Paryškinti
+Name[mk]=ОÑветлување
+Name[nb]=Marker
+Name[nds]=Rutheven
+Name[ne]=हाइलाइट
+Name[nl]=Aanwijzen
+Name[nn]=Marker
+Name[pa]=ਉਘਾੜਨ
+Name[pl]=Podświetlenie
+Name[pt]=Realce
+Name[pt_BR]=Destaque
+Name[ro]=Evidenţiat
+Name[ru]=Выделение
+Name[se]=Merke
+Name[sk]=Zvýrazniť
+Name[sl]=Poudarjeno sporoÄilo
+Name[sr]=ИÑтицање
+Name[sr@Latn]=Isticanje
+Name[sv]=Markera
+Name[ta]=à®®à¯à®©à¯ˆà®ªà¯à®ªà¯à®±à¯à®¤à¯à®¤à®²à¯
+Name[tg]=Равшаннамоӣ
+Name[tr]=Vurgu
+Name[uk]=ПідÑвічуваннÑ
+Name[wa]=E sorbiyance
+Name[zh_CN]=çªå‡ºæ˜¾ç¤º
+Name[zh_HK]=加強顯示
+Name[zh_TW]=高亮度
+Comment=A highlighted message has been received
+Comment[bg]=ПриÑтигна Ñпециално Ñъобщение
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£ বারà§à¦¤à¦¾ গà§à¦°à¦¹à¦£ করা হয়েছে
+Comment[bs]=Primljena je naglašena poruka
+Comment[ca]=S'ha rebut un missatge ressaltat
+Comment[cs]=Byla obdržena zvýrazněná zpráva
+Comment[da]=En fremhævet besked er blevet modtaget
+Comment[de]=Eine hervorgehobene Nachricht ist eingegangen
+Comment[el]=Ένα τονισμένο μήνυμα μόλις παÏαλήφθηκε
+Comment[es]=Se ha recibido un mensaje resaltado
+Comment[et]=Saabus esiletõstetud sõnum
+Comment[eu]=Nabarmendutako mezu bat jaso da
+Comment[fa]=یک پیام مشخص‌شده دریاÙت شده است
+Comment[fi]=Korostettu viesti on saapunut
+Comment[fr]=Un message surligné a été reçu
+Comment[gl]=Recibiuse unha mensaxe subliñada
+Comment[he]=התקבל מסר מודגש
+Comment[hr]=Osvijetljena poruka je primljena
+Comment[hu]=Kiemelt üzenet érkezett
+Comment[is]=Merkt skeyti móttekið
+Comment[it]=Un messaggio evidenziato è stato ricevuto
+Comment[ja]=強調ã•ã‚ŒãŸãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ãŒå±Šãã¾ã—ãŸ
+Comment[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული შეტყáƒáƒ‘ინებრმიღებულ იქნáƒ
+Comment[kk]=Ерекше хабарлама келді
+Comment[km]=បាន​ទទួល​សារ​សំážáž¶áž“់​មួយ
+Comment[lt]=Gauta paryškinta žinutė
+Comment[mk]=Примена е оÑветлена порака
+Comment[nb]=En fremhevet melding er mottatt
+Comment[nds]=En rutheevt Naricht keem rin
+Comment[ne]=हाइलाइट गरिà¤à¤•à¥‹ सनà¥à¤¦à¥‡à¤¶ पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो
+Comment[nl]=Een geaccentueerd bericht is binnengekomen
+Comment[nn]=Ei markert melding er motteken
+Comment[pl]=Podświetlona wiadomość została odebrana
+Comment[pt]=Foi recebida uma mensagem realçada
+Comment[pt_BR]=Uma mensagem de destaque foi recebida
+Comment[ru]=Получено выделенное Ñообщение
+Comment[se]=Merkejuvvon dieđáhus lea vuostáiváldon
+Comment[sk]=Prijatá zvýraznená správa
+Comment[sl]=Prejeto je bilo poudarjeno sporoÄilo
+Comment[sr]=Примљена је иÑтакнута порука
+Comment[sr@Latn]=Primljena je istaknuta poruka
+Comment[sv]=Ett markerat meddelande har tagits emot
+Comment[ta]=தனிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿ செயà¯à®¤à®¿ பெறபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Пайёмҳои равшаншаванда қабул гардиданд
+Comment[tr]=Vurgulanmış bir mesaj alındı
+Comment[uk]=Отримано виділене повідомленнÑ
+Comment[zh_CN]=收到了çªå‡ºæ˜¾ç¤ºçš„消æ¯
+Comment[zh_HK]=收到一個加強顯示的訊æ¯
+Comment[zh_TW]=接收到一個高亮度訊æ¯
+default_sound=Kopete_Received.ogg
+default_presentation=65
+
+
+[kopete_contact_lowpriority]
+Name=Low priority messages
+Name[be]=Паведамленне нізкай важнаÑці
+Name[bg]=ПриÑтигна Ñъобщение Ñ Ð½Ð¸Ñък приоритет
+Name[bn]=কম গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£ বারà§à¦¤à¦¾
+Name[bs]=Poruke niskog prioriteta
+Name[ca]=Missatges de baixa prioritat
+Name[cs]=Zprávy s nízkou prioritou
+Name[da]=Breve med lav prioritet
+Name[de]=Nachrichten mit niedriger Priorität
+Name[el]=ΜηνÏματα χαμηλής Ï€ÏοτεÏαιότητας
+Name[es]=Mensajes de prioridad baja
+Name[et]=Madala prioriteediga sõnumid
+Name[eu]=Lehentasun gutxiko mezuak
+Name[fa]=پیامهای کم اولویت
+Name[fi]=Matalan prioriteetin viestit
+Name[fr]=Messages de basse priorité
+Name[gl]=Mensaxes con baixa prioridade
+Name[he]=הודעות בעדיפות נמוכה
+Name[hu]=Alacsony prioritású üzenetek
+Name[is]=Skeyti með lágum forgangi
+Name[it]=Messaggi a bassa priorità
+Name[ja]=優先度ã®ä½Žã„メッセージ
+Name[ka]=დáƒáƒ‘áƒáƒšáƒ˜ პრიáƒáƒ áƒ˜áƒ¢áƒ”ტის შეტყáƒáƒ‘ინებáƒ
+Name[kk]=Ðртықшылығы төмен хабарлама
+Name[km]=សារ​អាទិភាព​ទាប
+Name[lt]=Žemo prioriteto žinutės
+Name[mk]=Пораки Ñо низок приоритет
+Name[nb]=Meldinger med lav prioritet
+Name[nds]=Narichten mit siete Prioriteet
+Name[ne]=कम पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤•à¤¤à¤¾ सनà¥à¤¦à¥‡à¤¶
+Name[nl]=Berichten met lage prioriteit
+Name[nn]=Meldingar med låg prioritet
+Name[pa]=ਘੱਟ ਤਰਜੀਹ ਸà©à¨¨à©‡à¨¹à©‡
+Name[pl]=Wiadomości o niskim priorytecie
+Name[pt]=Mensagens de baixa prioridade
+Name[pt_BR]=Mensagens com baixa prioridade
+Name[ro]=Mesaje de prioritate mică
+Name[ru]=Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ Ð½Ð¸Ð·ÐºÐ¸Ð¼ приоритетом
+Name[se]=Dieđahusat mas lea unna ovdavuorra
+Name[sk]=Správy s nízkou prioritou
+Name[sl]=Manj pomembno sporoÄilo
+Name[sr]=Поруке ниÑког приоритета
+Name[sr@Latn]=Poruke niskog prioriteta
+Name[sv]=LÃ¥gprioriterat meddelande
+Name[ta]=கà¯à®±à¯ˆà®¨à¯à®¤ à®®à¯à®©à¯à®©à¯à®°à®¿à®®à¯ˆ உளà¯à®³ செயà¯à®¤à®¿à®•à®³à¯
+Name[tg]=Пайёмҳои Имтиёзашон Кам
+Name[tr]=Düşük öncelikli mesajlar
+Name[uk]=ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· низьким пріоритетом
+Name[zh_CN]=低优先级消æ¯
+Name[zh_HK]=ä¸å¤ªé‡è¦çš„訊æ¯
+Name[zh_TW]=低優先權訊æ¯
+Comment=A message marked with a low priority has been received
+Comment[be]=Ðтрыманае паведамленне нізкай важнаÑці
+Comment[bg]=ПриÑтигна Ñъобщение Ñ Ð½Ð¸Ñък приоритет
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ কম গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£ বারà§à¦¤à¦¾ গà§à¦°à¦¹à¦£ করা হয়েছে
+Comment[bs]=Primljena je poruka oznaÄena kao poruka niskog prioriteta
+Comment[ca]=S'ha rebut un missatge marcat amb baixa prioritat
+Comment[cs]=Byla obdržena zvýrazněná zpráva s nízkou prioritou
+Comment[da]=En indkommende besked med lav prioritet er blevet modtaget
+Comment[de]=Eine Nachricht mit niedriger Priorität ist eingegangen
+Comment[el]=Ένα μήνυμα, σημειωμένο με χαμηλή Ï€ÏοτεÏαιότητα μόλις παÏαλήφθηκε
+Comment[es]=Se ha recibido un mensaje marcado con poca prioridad
+Comment[et]=Saabus madalaprioriteediline sõnum
+Comment[eu]=Lehentasun gutxiko bezala markatutako mezu bat jaso da
+Comment[fa]=یک پیام با نشان اولویت Ú©Ù… دریاÙت شده است
+Comment[fi]=Matalaprioriteettinen viesti on saapunut
+Comment[fr]=Un message marqué avec une basse priorité a été reçu
+Comment[gl]=Unha mensaxe marcada coma de baixa prioridade foi recibida
+Comment[he]=הודעה שמסומנת כבעלת עדיפות נמוכה התקבלה
+Comment[hu]=Alacsony prioritású üzenet érkezett
+Comment[is]=Skeyti með lágum forgangi móttekið
+Comment[it]=Un messaggio segnato con bassa priorità è stato ricevuto
+Comment[ja]=優先度ã®ä½Žã„メッセージãŒå±Šãã¾ã—ãŸ
+Comment[ka]=დáƒáƒ‘áƒáƒšáƒ˜ პრიáƒáƒ áƒ˜áƒ¢áƒ”ტით შეტყáƒáƒ‘ინებრმიღებულ იქნáƒ
+Comment[kk]=Ðртықшылығы төмен деп белгіленген хабарлама келді
+Comment[km]=បាន​ទទួល​សារ ដែល​បាន​សម្គាល់​​ážáž¶â€‹áž‡áž¶â€‹áž¢áž¶áž‘ិភាព​ទាប
+Comment[lt]=Gauta žemo prioriteto žinutė
+Comment[mk]=Примена е порака означена Ñо низок приоритет
+Comment[nb]=En melding med lav prioritet er mottatt
+Comment[nds]=En Naricht mit siete Prioriteet keem rin
+Comment[ne]=कम पà¥à¤°à¤¾à¤¥à¤®à¤¿à¤•à¤¤à¤¾à¤•à¥‹ रूपमा चिनà¥à¤¹ लगाइà¤à¤•à¥‹ सनà¥à¤¦à¥‡à¤¶ पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो
+Comment[nl]=Er is een bericht met een lage prioriteit binnengekomen
+Comment[nn]=Ei melding markert med låg prioritet er motteken
+Comment[pl]=Otrzymana została wiadomość o niskim priorytecie
+Comment[pt]=Foi recebida uma mensagem marcada como de baixo prioridade
+Comment[pt_BR]=Chegou uma mensagem marcada com baixa prioridade
+Comment[ru]=Получены ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ Ð½Ð¸Ð·ÐºÐ¸Ð¼ приоритетом
+Comment[se]=Dieđáhus mas lea unna ovdavuorru lea vuostáiváldon
+Comment[sk]=Prijatá správa s nízkou prioritou
+Comment[sl]=Prejeto je bilo manj pomembno sporoÄilo
+Comment[sr]=Примљена је порука означена ниÑким приоритетом
+Comment[sr@Latn]=Primljena je poruka oznaÄena niskim prioritetom
+Comment[sv]=Ett inkommande meddelande har anlänt
+Comment[ta]=எளிய à®®à¯à®©à¯à®©à¯à®°à®¿à®®à¯ˆ à®à®±à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿ செயà¯à®¤à®¿ சà¯à®´à®±à¯à®šà®¿
+Comment[tg]=Пайёми бо имтиёзи паÑÑ‚ нишона карда шуда қабул гардид
+Comment[tr]=Önceliği düşük olarak işaretlenmiş bir rmesaj alındı
+Comment[uk]=Отримано Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· низьким пріоритетом
+Comment[zh_CN]=收到了标为低优先级的消æ¯
+Comment[zh_HK]=收到一個標示為ä¸å¤ªé‡è¦çš„訊æ¯
+Comment[zh_TW]=接收到一個標記為低優先權的訊æ¯
+default_presentation=0
+
+[kopete_authorization]
+Name=Authorization
+Name[bg]=ОторизациÑ
+Name[ca]=Autorització
+Name[cs]=Autorizace
+Name[da]=Godkendelse
+Name[de]=Autorisierung
+Name[el]=Πιστοποίηση
+Name[eo]=Rajtigo
+Name[es]=Autorización
+Name[et]=Autoriseerimine
+Name[fa]=اجازه
+Name[fi]=Hyväksyminen
+Name[fr]=Autorisation
+Name[he]=הזדהות
+Name[hu]=Felhasználóazonosítás
+Name[is]=Auðkenning
+Name[it]=Autorizzazione
+Name[ja]=許å¯
+Name[km]=សáŸáž…ក្ដី​អនុញ្ញាáž
+Name[lt]=Autorizavimas
+Name[nb]=Autorisering
+Name[nds]=Identifikatschoon
+Name[ne]=आधिकीकरण
+Name[nl]=Autorisatie
+Name[pa]=ਪਰਮਾਣਕਿਤਾ
+Name[pl]=Autoryzacja
+Name[pt]=Autorização
+Name[pt_BR]=Autorização
+Name[ru]=ÐвторизациÑ
+Name[sk]=Autorizácia
+Name[sl]=Odobritev
+Name[sr]=Ðуторизација
+Name[sr@Latn]=Autorizacija
+Name[sv]=Behörighetskontroll
+Name[tr]=Yetkilendirme
+Name[uk]=ÐвторизаціÑ
+Name[zh_CN]=身份验è¯
+Name[zh_TW]=èªè­‰
+Comment=An user has accepted/declined your authorization request
+Comment[bg]=Потребител е приел/отхвърлил вашата заÑвка за оторизациÑ
+Comment[ca]=Un usuari ha autoritzat/declinat la vostra petició d'autorització
+Comment[cs]=Uživatel přijmul/odmítnul váš požadavek na autorizaci
+Comment[da]=En bruger har godkendt/afslået din godkendelsesforespørgsel
+Comment[de]=Ein Benutzer hat Ihre Autorisierung erlaubt/abgelehnt
+Comment[el]=Ένας χÏήστης έχει πιστοποιήσει/αποÏÏίψει την αίτησή σας για πιστοποίηση
+Comment[es]=Un usuario ha aceptado/declinado su petición de autorización
+Comment[et]=Kasutaja autoriseeris/lükkas tagasi sinu autoriseerimissoovi
+Comment[fa]=کاربری درخواست اجازۀ شما را پذیرÙته/نپذیرÙته است
+Comment[fi]=Käyttäjä on hyväksynyt/hylännyt hyväksymispyyntösi
+Comment[fr]=Un utilisateur a accepté ou refusé votre demande d'autorisation
+Comment[he]=משתמש ×ישר/דחה ×ת בקשתך לזיהוי
+Comment[hu]=Egy felhasználó elfogadta vagy elutasította az Ön felhasználóazonosítási kérését
+Comment[is]=Notandi hefur heimilað eða hafnað beiðni þinni um auðkenningu auðkenningabeiðni þinni
+Comment[it]=Un utente ha autorizzato/declinato la tua richiesta
+Comment[ja]=ユーザã¯ã‚ãªãŸã®è¨±å¯è¦æ±‚を承諾/æ‹’å¦ã—ã¾ã—ãŸ
+Comment[km]=អ្នក​ប្រើ​បានទទួល​យក/បដិសáŸáž’ នូវ​សំណើ​សáŸáž…ក្ដី​អនុញ្ញាážâ€‹ážšáž”ស់​អ្នក
+Comment[lt]=Naudotojas priėmė/atmetė Jūsų autorizacijos prašymą
+Comment[nb]=En bruker har autorisert eller avvist din forespørsel om autorisering
+Comment[nds]=En Bruker hett Dien Identifikatschoonanfraag tolaten/afwiest
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ तपाईà¤à¤•à¥‹ आधिकीकरण अनà¥à¤°à¥‹à¤§ सà¥à¤µà¥€à¤•à¤¾à¤°/असà¥à¤µà¥€à¤•à¤¾à¤° गरेको छ
+Comment[nl]=Een gebruiker heeft uw autorisatieverzoek geaccepteerd/afgewezen
+Comment[pl]=Użytkownik zaakceptował/odrzucił Twoje żądanie autoryzacji
+Comment[pt]=Um utilizador autorizou/rejeitou o seu pedido de autorização
+Comment[pt_BR]=Um usuário aceitou/rejeitou seu pedido de autorização
+Comment[ru]=Пользователь авторизовал Ð²Ð°Ñ Ð¸Ð»Ð¸ отклонил ваш запроÑ
+Comment[sk]=Užívateľ prijal/odmietol vašu požiadavku o autorizáciu
+Comment[sl]=Uporabnik je spejel/zavrnil vaš zahtevek za odobritev
+Comment[sr]=КориÑник је прихватио/одбио ваш захтев за ауторизацију
+Comment[sr@Latn]=Korisnik je prihvatio/odbio vaš zahtev za autorizaciju
+Comment[sv]=En användare har tillåtit eller nekat till din begäran om behörighetskontroll
+Comment[tr]=Bir kullanıcı izin isteğinizi onayladı/onaylamadı
+Comment[uk]=КориÑтувач прийнÑв/відхилив ваш запит на авторизацію
+Comment[zh_CN]=用户åŒæ„/æ‹’ç»äº†æ‚¨çš„身份验è¯è¯·æ±‚
+Comment[zh_TW]=已有一å使用者接å—/拒絕了您的èªè­‰è¦æ±‚
+default_presentation=16
+
+[yahoo_mail]
+Name=Yahoo Mail
+Name[be]=Пошта Yahoo
+Name[bg]=ПриÑтигна нова поща в Yahoo
+Name[bn]=ইয়à§à¦¯à¦¾à¦¹à§ মেইল
+Name[br]=Postel Yahoo
+Name[bs]=Yahoo mail
+Name[ca]=Correu Yahoo
+Name[es]=Correo de Yahoo
+Name[fa]=نامۀ یاهو
+Name[fi]=Yahoo-sähköposti
+Name[fr]=Courriel Yahoo
+Name[he]=דו×"ל של Yahoo
+Name[is]=Yahoo póstur
+Name[ja]=Yahoo メール
+Name[ka]=Yahoo ფáƒáƒ¡áƒ¢áƒ
+Name[kk]=Yahoo поштаÑÑ‹
+Name[km]=សំបុážáŸ’រ​យ៉ាហ៊ូ
+Name[lt]=Yahoo paštas
+Name[mk]=Yahoo-пошта
+Name[nb]=Yahoo e-post
+Name[nds]=Yahoo-Nettpost
+Name[ne]=याहू मेल
+Name[pa]=ਯਾਹੂ ਮੇਲ
+Name[pl]=Poczta Yahoo
+Name[pt]=E-mail Yahoo
+Name[ru]=Почта Yahoo
+Name[sk]=Pošta Yahoo
+Name[sl]=E-pošta Yahoo
+Name[sr]=Yahoo пошта
+Name[sr@Latn]=Yahoo pošta
+Name[sv]=Yahoo e-post
+Name[ta]=யாஹூ அஞà¯à®šà®²à¯
+Name[tg]=ПоÑти Yahoo
+Name[tr]=Yahoo Posta
+Name[uk]=Пошта Yahoo
+Name[zh_CN]=Yahoo 邮件
+Comment=New email has arrived in your Yahoo inbox
+Comment[be]=У вашай Ñлектроннай Ñкрыні Yahoo Ð½Ð¾Ð²Ð°Ñ Ð¿Ð¾ÑˆÑ‚Ð°
+Comment[bg]=ПриÑтигна нова поща в Yahoo
+Comment[bn]=আপনার ইয়à§à¦¯à¦¾à¦¹à§ ইনবকà§à¦¸à§‡ নতà§à¦¨ ই-মেইল উপসà§à¦¥à¦¿à¦¤ হয়েছে
+Comment[br]=Deuet eo ur postel nevez d'em voest degemer Yahoo
+Comment[bs]=Stigla je nova poÅ¡ta u vaÅ¡ Yahoo sanduÄić
+Comment[ca]=Ha arribat un nou correu a la vostra bústia de Yahoo
+Comment[cs]=Přišla nová pošta do vaší Yahoo schránky
+Comment[da]=Ny e-mail ankom til din Yahoo-indbakke
+Comment[de]=Eine neue Nachricht befindet sich im Yahoo-Eingangsordner
+Comment[el]=Μόλις έφτασε νέο email στα εισεÏχόμενα του Yahoo σας
+Comment[eo]=Nova poÅto ricevita
+Comment[es]=Tiene correo nuevo en la cuenta de Yahoo
+Comment[et]=Saabus uus kiri sinu Yahoo Inboxi
+Comment[eu]=E-posta berri bat jaso da zure Yahoo-ko sarrerako ontzian
+Comment[fa]=یک رایانامۀ جدید در دریاÙتی یاهوی شما رسیده است
+Comment[fi]=Uutta postia saapunut Yahoo-sähköpostilaatikkoon
+Comment[fr]=Un nouveau message est arrivé dans votre boîte aux lettres Yahoo
+Comment[gl]=Unha nova mensaxe chegou ao teu cartafol de entrada de Yahoo
+Comment[he]=התקבל עבורך דו×ר חדש בתיבת הדו×"ל של Yahoo
+Comment[hu]=Új levél érkezett a Yahoo postaládába
+Comment[is]=Nýr póstur kominn í Yahoo innhólfið
+Comment[it]=È arrivata nuova posta nella tua casella Yahoo
+Comment[ja]=Yahoo ã®å—ä¿¡ç®±ã«æ–°ã—ã„メールãŒå±Šãã¾ã—ãŸ
+Comment[ka]=áƒáƒ®áƒáƒšáƒ˜ ელფáƒáƒ¡áƒ¢áƒ მáƒáƒ•áƒ˜áƒ“რYahoo-ს სáƒáƒ¤áƒáƒ¡áƒ¢áƒ ყუთში
+Comment[kk]=Yahoo пошта жәшігне хабарлама келі
+Comment[km]=អ៊ីមែល​ážáŸ’មី​បាន​មក​ដល់​ក្នុង​ប្រអប់​ទទួល​យ៉ាហ៊ូ​របស់​អ្នក​ហើយ
+Comment[lt]=Į Yahoo pašto dėžutę gautas naujas laiškas
+Comment[mk]=ПриÑтигна нова пошта во вашето Yahoo-Ñандаче
+Comment[nb]=Ny e-post er ankommet i Yahoo-innboksen
+Comment[nds]=Du hest niege Nettpost in Dien Yahoo-Postingang
+Comment[ne]=नयाठइमेल तपाईà¤à¤•à¥‹ याहू पतà¥à¤°à¤®à¤žà¥à¤œà¥‚षामा आयो
+Comment[nl]=Er is een nieuwe e-mail aangekomen in uw Yahoo-inbox
+Comment[nn]=Ny e-post er komen til Yahoo-innboksen
+Comment[pl]=Nadeszła nowa wiadomość do Twojej skrzynki odbiorczej w Yahoo
+Comment[pt]=Chegou uma mensagem nova à sua caixa de correio do Yahoo
+Comment[pt_BR]=Chegou um novo e-mail em sua caixa de entrada do Yahoo
+Comment[ru]=Пришли новые пиÑьма в Yahoo
+Comment[se]=Ođđa e-boasta lea boahtán du Yahoo-boastaboksii
+Comment[sk]=Do vašej schránky Yahoo prišla nová správa
+Comment[sl]=Nova e-pošta je prispela v vaš nabiralnik Yahoo
+Comment[sr]=Ðова порука је Ñтигла у ваше Yahoo Ñандуче
+Comment[sr@Latn]=Nova poruka je stigla u vaÅ¡e Yahoo sanduÄe
+Comment[sv]=Ett nytt brev har anlänt i din Yahoo-inkorg
+Comment[ta]=உஙà¯à®•à®³à¯ மின௠அஞà¯à®šà®²à¯ பெடà¯à®Ÿà®¿à®•à¯à®•à¯à®³à¯ பà¯à®¤à®¿à®¯ மினà¯à®©à®žà¯à®šà®²à¯ ஒனà¯à®±à¯ வநà¯à®¤à¯à®³à¯à®³à®¤à¯
+Comment[tg]=Ба қутии поÑти Yahoo-и шумо пайёми Ñлектронии нав омад
+Comment[tr]=Yahoo gelen kutunuza yeni bir e-posta geldi
+Comment[uk]=Прийшли нові лиÑти у вашу Ñкриньку Yahoo
+Comment[zh_CN]=您的 Yahoo 收件箱中有新邮件到达
+Comment[zh_HK]=您的 Yahoo 收件匣有新郵件
+Comment[zh_TW]=新郵件é€é”您的 Yahoo 收件匣
+default_presentation=16
+
+[msn_alert]
+Name=MSN Alert
+Name[bg]=MSN Ñъобщение
+Name[ca]=Alerta del MSN
+Name[cs]=MSN upozornění
+Name[da]=MSN-alarm
+Name[de]=MSN-Warnung
+Name[el]=Ειδοποίηση του MSN
+Name[es]=Alerta MSN
+Name[et]=MSN teade
+Name[fa]=هشدار ام‌اس‌ان
+Name[fi]=MSN-varoitus
+Name[fr]=Alerte MSN
+Name[he]=×זהרה של MSN
+Name[hu]=MSN értesítő
+Name[is]=MSN skeyti
+Name[it]=Avviso MSN
+Name[km]=សáŸáž…ក្ដី​ជូនដំណឹង MSN
+Name[lt]=MSN: „dėmesio!“
+Name[nb]=MSN-varsling
+Name[nds]=MSN-Alarm
+Name[ne]=à¤à¤®à¤à¤¸à¤à¤¨ सावधानी
+Name[nl]=MSN-melding
+Name[pa]=MSN ਚੇਤਾਵਨੀ
+Name[pl]=Alarm MSN
+Name[pt]=Alerta MSN
+Name[pt_BR]=Alerta do MSN
+Name[ru]=Предупреждение MSN
+Name[sk]=MSN Upozornenie
+Name[sl]=Alarm MSN
+Name[sr]=MSN аларм
+Name[sr@Latn]=MSN alarm
+Name[sv]=MSN-larm
+Name[tr]=MSN Uyarısı
+Name[uk]=Сигнал MSN
+Name[zh_CN]=MSN æ醒
+Name[zh_TW]=MSN 警告
+Comment=A new alert has been sent to you
+Comment[bg]=Изпратено ви е ново Ñъобщение
+Comment[ca]=Se us ha enviat una nova alerta
+Comment[cs]=Bylo vám doruÄeno nové upozornÄ›ní
+Comment[da]=En ny alarm er sendt til dig
+Comment[de]=Sie haben eine neue Warnung erhalten
+Comment[el]=Σας στάλθηκε μια νέα ειδοποίηση
+Comment[es]=Se le ha enviado una nueva alerta
+Comment[et]=Sulle saadeti uus teade
+Comment[fa]=هشدار جدیدی برای شما ارسال شده است
+Comment[fi]=Sinulle on lähetetty uusi varoitus
+Comment[fr]=Une nouvelle alerte vous a été envoyée
+Comment[he]=×זהרה חדשה נשלחה ×ליך
+Comment[hu]=Új értesítőt küldtek Önnek
+Comment[is]=Þér hefur verið sent nýtt skeyti
+Comment[it]=Ti è stato inviato un avviso
+Comment[ja]=æ–°ã—ã„アラートをå—ä¿¡ã—ã¾ã—ãŸ
+Comment[km]=បានផ្ញើ​សáŸáž…ក្ដី​ជូន​ដំណឹង​ទៅឲ្យអ្នក
+Comment[lt]=Jums pasiųstas naujas „dėmesio!“ signalas
+Comment[nb]=En ny varsling er sendt til deg
+Comment[nds]=Een hett Di en niegen Alarm sendt
+Comment[ne]=तपाईà¤à¤²à¤¾à¤ˆ à¤à¤‰à¤Ÿà¤¾ नयाठसावधानी सनà¥à¤¦à¥‡à¤¶ पठाà¤à¤•à¥‹ छ
+Comment[nl]=U hebt een nieuwe melding ontvangen
+Comment[pl]=Nowy alarm został wysłany do Ciebie
+Comment[pt]=Foi enviada um novo alerta
+Comment[pt_BR]=Um novo alerta foi enviado para você
+Comment[ru]=Вам отправлено предупреждение
+Comment[sk]=Bolo vám poslané nové upozornenie
+Comment[sl]=Poslan vam je bil alarm
+Comment[sr]=ПоÑлат вам је нови аларм
+Comment[sr@Latn]=Poslat vam je novi alarm
+Comment[sv]=Ett nytt larm har skickats till dig
+Comment[tr]=Size yeni bir uyarı gönderildi
+Comment[uk]=Вам було відіÑлано новий Ñигнал
+Comment[zh_CN]=您收到了新æ醒
+Comment[zh_TW]=一個新的警告已é€é”給您
+default_presentation=16
+
+[msn_mail]
+Name=MSN Mail
+Name[ar]=بريد MSN
+Name[be]=Пошта MSN
+Name[bg]=ПриÑтигна нова поща в MSN
+Name[bn]=à¦à¦®à¦à¦¸à¦à¦¨ মেইল
+Name[br]=Postel MSN
+Name[bs]=MSN mail
+Name[ca]=Correu de MSN
+Name[cs]=MSN pošta
+Name[da]=MSN-Mail
+Name[de]=MSN-Mail
+Name[es]=Correo MSN
+Name[fa]=نامۀ ام‌اس‌ان
+Name[fi]=MSN-sähköposti
+Name[fr]=Courriel MSN
+Name[gl]=Correo MSN
+Name[he]=דו×"ל של MSN
+Name[hi]=à¤à¤®à¤à¤¸à¤à¤¨ मेल
+Name[hr]=MSN pošta
+Name[hu]=MSN e-mail
+Name[is]=MSN póstur
+Name[it]=Posta MSN
+Name[ja]=MSN メール
+Name[ka]=MSN ფáƒáƒ¡áƒ¢áƒ
+Name[kk]=MSN поштаÑÑ‹
+Name[km]=សំបុážáŸ’ážš MSN
+Name[lt]=MSN Paštas
+Name[mk]=MSN-пошта
+Name[nb]=MSN e-post
+Name[nds]=MSN-Nettpost
+Name[ne]=à¤à¤®à¤à¤¸à¤à¤¨ मेल
+Name[nn]=MSN-e-post
+Name[pa]=MSN ਮੇਲ
+Name[pl]=Poczta MSN
+Name[pt]=E-mail MSN
+Name[ru]=Почта MSN
+Name[sl]=E-pošta MSN
+Name[sr]=MSN пошта
+Name[sr@Latn]=MSN pošta
+Name[sv]=MSN e-post
+Name[ta]=MSN மினà¯à®©à®žà¯à®šà®²à¯
+Name[tg]=ПоÑти MSN
+Name[tr]=MSN Posta
+Name[uk]=Пошта MSN
+Name[zh_CN]=MSN 邮件
+Comment=New email has arrived in your MSN inbox
+Comment[be]=У вашай Ñлектроннай Ñкрыні MSN Ð½Ð¾Ð²Ð°Ñ Ð¿Ð¾ÑˆÑ‚Ð°
+Comment[bg]=ПриÑтигна нова поща в MSN
+Comment[bn]=আপনার à¦à¦®à¦à¦¸à¦à¦¨ ইনবকà§à¦¸à§‡ নতà§à¦¨ ই-মেইল উপসà§à¦¥à¦¿à¦¤ হয়েছে
+Comment[br]=Deuet eo ur postel nevez d'em voest degemer MSN
+Comment[bs]=Stigla je nova poÅ¡ta u vaÅ¡ MSN sanduÄić
+Comment[ca]=Ha arribat un nou correu a la vostra bústia de MSN
+Comment[cs]=Přišla nová pošta do vaší MSN schránky
+Comment[da]=Ny e-mail ankom til din MSN-indbakke
+Comment[de]=Eine neue Nachricht befindet sich im MSN-Eingangsordner
+Comment[el]=Μόλις έφτασε νέο e-mail στα εισεÏχόμενα του MSN σας
+Comment[eo]=Nova poÅto ricevita
+Comment[es]=Tiene correo nuevo en la cuenta de MSN
+Comment[et]=Saabus uus kiri sinu MSN Inboxi
+Comment[eu]=E-posta berri bat jaso da zure MSN-ko sarrerako ontzian
+Comment[fa]=یک رایانامۀ جدید در دریاÙتی ام‌اس‌ان شما رسیده است
+Comment[fi]=Uutta postia saapunut MSN-sähköpostilaatikkoon
+Comment[fr]=Un nouveau message est arrivé dans votre boîte aux lettres MSN
+Comment[gl]=Unha nova mensaxe chegou ao teu cartafol de entrada de MSN
+Comment[he]=התקבל עבורך דו×ר חדש בתיבת הדו×"ל של MSN
+Comment[hr]=Nova poÅ¡ta je stigla u vaÅ¡ MSN sanduÄić
+Comment[hu]=Új levél érkezett az MSN postaládába
+Comment[is]=Það er nýr póstur í MSN innhólfinu þínu
+Comment[it]=È arrivata nuova posta nella tua casella MSN
+Comment[ja]=MSN ã®å—ä¿¡ç®±ã«æ–°ã—ã„メールãŒå±Šãã¾ã—ãŸ
+Comment[ka]=áƒáƒ®áƒáƒšáƒ˜ ელფáƒáƒ¡áƒ¢áƒ მáƒáƒ•áƒ˜áƒ“რMSN სáƒáƒ¤áƒáƒ¡áƒ¢áƒ ყუთში
+Comment[kk]=MSN пошта жәшігне хабарлама келді
+Comment[km]=អ៊ីមែល​ážáŸ’មី​បាន​មក​ដល់​ក្នុង​ប្រអប់​ទទួល MSN របស់​អ្នក​​ហើយ
+Comment[lt]=Į MSN pašto dėžutę gautas naujas laiškas
+Comment[mk]=ПриÑтигна нова пошта во вашето MSN-Ñандаче
+Comment[nb]=Ny e-post er ankommet i MSN-innboksen
+Comment[nds]=Du hest niege Nettpost in Dien MSN-Postingang
+Comment[ne]=तपाईà¤à¤•à¥‹ à¤à¤®à¤à¤¸à¤à¤¨ पतà¥à¤°à¤®à¤žà¥à¤œà¥‚षामा नयाठइमेल छ
+Comment[nl]=Er is een nieuwe e-mail aangekomen in uw MSN-inbox
+Comment[nn]=Ny e-post er komen til MSN-innboksen
+Comment[pl]=Nadeszła nowa wiadomość do Twojej skrzynki odbiorczej w MSN
+Comment[pt]=Chegou uma mensagem nova à sua caixa de correio do MSN
+Comment[pt_BR]=chegou um novo e-mail em sua caixa de entrada MSN
+Comment[ru]=Пришли новые пиÑьма в MSN
+Comment[se]=Ođđa e-boasta lea boahtán du MSN-boastaboksii
+Comment[sk]=Do vašej schránky MSN prišla nová správa
+Comment[sl]=Nova e-pošta je prispela v vaš nabiralnik MSN
+Comment[sr]=Ðова порука је Ñтигла у ваше MSN Ñандуче
+Comment[sr@Latn]=Nova poruka je stigla u vaÅ¡e MSN sanduÄe
+Comment[sv]=Ett nytt brev har anlänt i din MSN-inkorg
+Comment[ta]=உஙà¯à®•à®³à¯ எமà¯à®Žà®¸à¯à®Žà®©à¯ அஞà¯à®šà®²à¯ பெடà¯à®Ÿà®¿à®•à¯à®•à¯ பà¯à®¤à®¿à®¯ மினà¯à®©à®žà¯à®šà®²à¯ வநà¯à®¤à¯à®³à¯à®³à®¤à¯
+Comment[tg]=Ба қутии поÑти MSN-и шумо пайёми Ñлектронии нав омад
+Comment[tr]=MSN gelen kutunuza yeni bir e-posta geldi
+Comment[uk]=Прийшли нові лиÑти у вашу Ñкриньку MSN
+Comment[zh_CN]=您的 MSN 收件箱中有新邮件到达
+Comment[zh_HK]=您的 MSN 收件匣有新郵件
+Comment[zh_TW]=新郵件é€é”您的 MSN 收件匣
+default_presentation=16
+
+[icq_authorization]
+Name=ICQ Authorization
+Name[be]=Спраўджванне аÑобы ICQ
+Name[bg]=ÐžÑ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð½Ð° ICQ
+Name[bn]=আই-সি-কিউ পà§à¦°à¦¾à¦ªà§à¦¤à¦¾à¦§à¦¿à¦•à¦¾à¦°
+Name[bs]=ICQ autorizacija
+Name[ca]=Autorització ICQ
+Name[cs]=ICQ autorizace
+Name[da]=ICQ-godkendelse
+Name[de]=ICQ-Autorisierung
+Name[el]=Πιστοποίηση ICQ
+Name[en_GB]=ICQ Authorisation
+Name[eo]=ICQ-Rajtigo
+Name[es]=Autorización ICQ
+Name[et]=ICQ autoriseerimine
+Name[eu]=ICQ baimena
+Name[fa]=اجازۀ ICQ
+Name[fi]=ICQ-todennus
+Name[fr]=Autorisation ICQ
+Name[ga]=Údárú ICQ
+Name[gl]=Autorización ICQ
+Name[he]=הרשמה ל-ICQ
+Name[hu]=ICQ felhasználóazonosítás
+Name[is]=ICQ auðkenning
+Name[it]=Autorizzazione ICQ
+Name[ja]=ICQ 承諾
+Name[ka]=ICQ áƒáƒ•áƒ¢áƒáƒ áƒ˜áƒ–áƒáƒªáƒ˜áƒ
+Name[kk]=ICQ авторизациÑÑÑ‹
+Name[km]=សáŸáž…ក្ដី​អនុញ្ញាហICQ
+Name[lt]=ICQ prieiga
+Name[nb]=ICQ-autorisering
+Name[nds]=ICQ-Identifikatschoon
+Name[ne]=आईसीकà¥à¤¯à¥‚ आधिकीकरण
+Name[nl]=ICQ-autorisatie
+Name[nn]=ICQ-autorisering
+Name[pa]=ICQ ਪਰਮਾਣਕਿਤਾ
+Name[pl]=Autoryzacja ICQ
+Name[pt]=Autorização ICQ
+Name[pt_BR]=Autorização do ICQ
+Name[ro]=Autorizare ICQ
+Name[ru]=ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ ICQ
+Name[sk]=ICQ overenie
+Name[sl]=Odobritev za ICQ
+Name[sr]=ICQ ауторизација
+Name[sr@Latn]=ICQ autorizacija
+Name[sv]=ICQ-behörighetskontroll
+Name[tr]=ICQ Ä°zni
+Name[uk]=ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ ICQ
+Name[zh_CN]=ICQ 身份验è¯
+Name[zh_HK]=ICQ 授權
+Name[zh_TW]=ICQ èªè­‰
+Comment=An ICQ user has authorized/declined your authorization request
+Comment[be]=КарыÑтальнік ICQ адказаў на запыт атарызацыі
+Comment[bg]=Потребител на ICQ е приел/отхвърлил вашата заÑвка за оторизациÑ
+Comment[bn]=à¦à¦•à¦œà¦¨ আই-সি-কিউ বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ আপনার পà§à¦°à¦¾à¦ªà§à¦¤à¦¾à¦§à¦¿à¦•à¦¾à¦° অনà§à¦°à§‹à¦§ অনà§à¦®à§‹à¦¦à¦¨/পà§à¦°à¦¤à§à¦¯à¦¾à¦–à§à¦¯à¦¾à¦¨ করেছে
+Comment[bs]=Jedan ICQ korisnik je odobrio ili odbio vaš zahtjev za autorizaciju
+Comment[ca]=Un usuari d'ICQ ha autoritzat/declinat la vostra petició d'autorització
+Comment[cs]=ICQ uživatel vám poskytl/odmítnul požadavek na autorizaci
+Comment[da]=En ICQ-bruger har godkendt/afslået din godkendelsesforespørgsel
+Comment[de]=Ein ICQ-Benutzer hat Ihre Autorisierung erlaubt/abgelehnt
+Comment[el]=Ένας χÏήστης ICQ έχει πιστοποιήσει/αποÏÏίψει την αίτησή σας για πιστοποίηση
+Comment[en_GB]=An ICQ user has authorised/declined your authorisation request
+Comment[es]=Un usuario ICQ ha autorizado/declinado su petición de autorización
+Comment[et]=ICQ kasutaja autoriseeris/lükkas tagasi sinu autoriseerimissoovi
+Comment[eu]=ICQ erabiltzailea batek zure baimen eskaera onartu/ukatu du.
+Comment[fa]=یک کاربر ICQ درخواست اجازۀ شما را پذیرÙته/نپذیرÙته است
+Comment[fi]=ICQ-käyttäjä on hyväksynyt/hylännyt hyväksymispyyntösi
+Comment[fr]=Un utilisateur ICQ a accepté ou décliné votre demande d'autorisation
+Comment[gl]=Un usuario ICQ autorizou/declinou a súa solicitude de autorización
+Comment[he]=משתמש ICQ ×ישר/דחה ×ת בקשתך להרשמה
+Comment[hu]=Egy ICQ-felhasználó elfogadta vagy elutasította az Ön bejelentkezési kérését
+Comment[is]=ICQ notandi hefur heimilað/hafnað auðkenningabeiðni þinni
+Comment[it]=Un utente ICQ ha autorizzato/declinato la tua richiesta
+Comment[ja]=ICQ ユーザãŒã‚ãªãŸã®èªå¯è¦æ±‚ã‚’èªå¯/æ‹’å¦ã—ã¾ã—ãŸ
+Comment[ka]=ICQ მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბელმრმáƒáƒ’ცáƒáƒ— áƒáƒ•áƒ¢áƒáƒ áƒ˜áƒ–áƒáƒªáƒ˜áƒ
+Comment[kk]=ICQ пайдаланушыÑÑ‹ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ Ñұрауыңызды құптады не құптаған жоқ
+Comment[km]=អ្នក​ប្រើ ICQ ម្នាក់​បាន​អនុញ្ញាហឬ បដិសáŸáž’​សំណើសុំ​សáŸáž…ក្ដី​អនុញ្ញាážâ€‹ážšáž”ស់​អ្នក​ហើយ
+Comment[lt]=ICQ naudotojas priėmė/atmetė jūsų prieigos prašymą
+Comment[nb]=En ICQ-bruker har autorisert eller avvist din forespørsel om autorisering
+Comment[nds]=En ICQ-Bruker hett Dien Identifikatschoonanfraag tolaten/afwiest
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ आईसीकà¥à¤¯à¥‚ पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ तपाईà¤à¤•à¥‹ आधिकीकरण अनà¥à¤°à¥‹à¤§à¤®à¤¾ आधिकरण गरà¥à¤¯à¥‹/घटायो
+Comment[nl]=Een ICQ-gebruiker heeft uw autorisatieverzoek geaccepteerd/afgewezen
+Comment[nn]=Ein ICQ-brukar har godtatt eller nekta førespurnaden din om autorisering
+Comment[pl]=Użytkownik ICQ zaakceptował/odrzucił Twoje żądanie autoryzacji
+Comment[pt]=Um utilizador ICQ autorizou/rejeitou o seu pedido de autorização
+Comment[pt_BR]=Um usuário do ICQ aceitou/declinou seu pedido de autorização
+Comment[ru]=Пользователь ICQ авторизовал Ð²Ð°Ñ Ð¸Ð»Ð¸ отклонил ваш запроÑ
+Comment[sk]=ICQ užívateľ overil/odmietol Vašu požiadavku na overenie
+Comment[sl]=Uporabnik ICQ-ja je spejel/zavrnil vaš zahtevek za odobritev
+Comment[sr]=ICQ кориÑник је ауторизовао/одбио ваш захтев за ауторизацију
+Comment[sr@Latn]=ICQ korisnik je autorizovao/odbio vaš zahtev za autorizaciju
+Comment[sv]=En ICQ-användare har tillåtit eller nekat till din begäran om behörighetskontroll
+Comment[tr]=ICQ kullanısı izin isteğinizi onayladı/onaylamadı
+Comment[uk]=КориÑтувач ICQ затвердив/відхилив ваш запит на авторизацію
+Comment[zh_CN]=ICQ 用户åŒæ„/æ‹’ç»äº†æ‚¨çš„身份验è¯è¯·æ±‚
+Comment[zh_HK]=æœ‰ä½ ICQ 用戶批準/拒絕了您的授權è¦æ±‚
+Comment[zh_TW]=ICQ 使用者已èªè­‰/拒絕您的èªè­‰è¦æ±‚
+default_presentation=16
+
+[irc_event]
+Name=IRC Event
+Name[be]=ÐŸÐ°Ð´Ð·ÐµÑ IRC
+Name[bg]=Събитие в IRC
+Name[bn]=আই-আর-সি ঘটনা
+Name[bs]=IRC događaj
+Name[ca]=Esdeveniment IRC
+Name[cs]=IRC událost
+Name[da]=IRC-begivenhed
+Name[de]=IRC-Ereignis
+Name[el]=Γεγονός IRC
+Name[eo]=IRC-Evento
+Name[es]=Evento IRC
+Name[et]=IRC sündmus
+Name[eu]=IRC gertaera
+Name[fa]=رویداد IRC
+Name[fi]=IRC-tapahtuma
+Name[fr]=Évènement IRC
+Name[ga]=Teagmhas IRC
+Name[gl]=Evento IRC
+Name[he]=×רוע IRC
+Name[hu]=IRC-esemény
+Name[is]=IRC atburður
+Name[it]=Evento IRC
+Name[ja]=IRC イベント
+Name[ka]=IRC მáƒáƒ•áƒšáƒ”ნáƒ
+Name[kk]=IRC оқиғаÑÑ‹
+Name[km]=ព្រឹážáŸ’ážáž·áž€áž¶ážšážŽáŸ IRC
+Name[lt]=IRC Įvykis
+Name[nb]=IRC-hendelse
+Name[nds]=Klöön-Begeefnis
+Name[ne]=आइआरसी घटना
+Name[nl]=IRC-gebeurtenis
+Name[nn]=IRC-hending
+Name[pa]=IRC ਘਟਨਾ
+Name[pl]=Zdarzenie IRC
+Name[pt]=Evento de IRC
+Name[pt_BR]=Evento IRC
+Name[ro]=Eveniment IRC
+Name[ru]=Событие IRC
+Name[sk]=IRC udalosť
+Name[sl]=Dogodek IRC
+Name[sr]=IRC догађај
+Name[sr@Latn]=IRC događaj
+Name[sv]=IRC-händelse
+Name[tr]=IRC Olayı
+Name[uk]=ÐŸÐ¾Ð´Ñ–Ñ IRC
+Name[uz]=IRC hodisasi
+Name[uz@cyrillic]=IRC ҳодиÑаÑи
+Name[zh_CN]=IRC 事件
+Name[zh_HK]=IRC 事件
+Name[zh_TW]=IRC 事件
+Comment=An IRC event has occurred
+Comment[be]=ÐŸÐ°Ð´Ð·ÐµÑ IRC
+Comment[bg]=Събитие в клиента за IRC
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ আই-আর-সি ঘটনা ঘটেছে
+Comment[bs]=Desio se IRC događaj
+Comment[ca]=Ha ocorregut un esdeveniment d'IRC
+Comment[cs]=Nastala IRC událost
+Comment[da]=En IRC=begivenhed er opstået
+Comment[de]=Ein IRC-Ereignis ist aufgetreten
+Comment[el]=Ένα γεγονός του IRC συνέβη
+Comment[eo]=Okazis IRC-evento
+Comment[es]=Ocurrió un evento IRC
+Comment[et]=Midagi toimus IRCus
+Comment[eu]=IRC gertaera bat gertatu da
+Comment[fa]=یک رویداد IRC رخ داده است
+Comment[fi]=IRC-tapahtuma
+Comment[fr]=Un évènement IRC s'est produit
+Comment[gl]=Ocorreu un evento IRC
+Comment[he]=×רוע IRC התרחש
+Comment[hu]=IRC-esemény következett be
+Comment[is]=IRC atburður hefur átt sér stað
+Comment[it]=Evento IRC
+Comment[ja]=IRC イベントãŒç™ºç”Ÿã—ã¾ã—ãŸ
+Comment[ka]=IRC მáƒáƒ•áƒšáƒ”ნრმáƒáƒ®áƒ“áƒ
+Comment[kk]=IRC оқиғаÑÑ‹ орын алды
+Comment[km]=ព្រឹážáŸ’ážáž·áž€áž¶ážšážŽáŸ IRC មួយ​បាន​កើážáž¡áž¾áž„
+Comment[lt]=Įvyko IRC įvykis
+Comment[nb]=Det har skjedd en IRC-hendelse
+Comment[nds]=Dat hett en IRC-Begeefnis geven
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ आइआरसी घटना देखापरà¥à¤¯à¥‹
+Comment[nl]=Er heeft zich een IRC-gebeurtenis voorgedaan
+Comment[nn]=Det har skjedd ei IRC-hending
+Comment[pl]=Wystąpiło zdarzenie IRC-a
+Comment[pt]=Ocorreu um evento de IRC
+Comment[pt_BR]=Um evento do IRC ocorreu
+Comment[ru]=Произошло Ñобытие IRC
+Comment[sk]=Nastala IRC udalosť
+Comment[sl]=Zgodil se je dogodek na IRC-u
+Comment[sr]=Дошло јо до догађаја на IRC-у
+Comment[sr@Latn]=Došlo jo do događaja na IRC-u
+Comment[sv]=En IRC-händelse har inträffat
+Comment[tr]=IRC olayı meydana geldi
+Comment[uk]=СталаÑÑŒ Ð¿Ð¾Ð´Ñ–Ñ IRC
+Comment[zh_CN]=å‘生了 IRC 事件
+Comment[zh_HK]=發生了 IRC 事件
+Comment[zh_TW]=發生 IRC 事件
+default_presentation=16
+
+[connection_error]
+Name=Connection Error
+Name[be]=Памылка злучÑннÑ
+Name[bg]=Грешка при връзка
+Name[bn]=সংযোগ তà§à¦°à§à¦Ÿà¦¿
+Name[br]=Fazi ar gevreadenn
+Name[bs]=Greška u vezi
+Name[ca]=Error de connexió
+Name[cs]=Chyba ve spojení
+Name[da]=Forbindelsesfejl
+Name[de]=Verbindungsfehler
+Name[el]=Σφάλμα σÏνδεσης
+Name[eo]=Konekto-eraro
+Name[es]=Error en la conexión
+Name[et]=Ãœhenduse viga
+Name[eu]=Konexio-errorea
+Name[fa]=خطای اتصال
+Name[fi]=Yhteysvirhe
+Name[fr]=Erreur de connexion
+Name[ga]=Earráid Naisc
+Name[gl]=Erro de Conexión
+Name[he]=שגי××” בחיבור
+Name[hu]=Csatlakozási hiba
+Name[is]=Villa í tengingu
+Name[it]=Errore di connessione
+Name[ja]=接続エラー
+Name[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ შეცდáƒáƒ›áƒ
+Name[kk]=ҚоÑылым қатеÑÑ–
+Name[km]=កំហុស​ការ​ážâ€‹áž—្ជាប់
+Name[lt]=Ryšio klaida
+Name[nb]=Koblingsfeil
+Name[nds]=Verbinnen-Fehler
+Name[ne]=जडान तà¥à¤°à¥à¤Ÿà¤¿
+Name[nl]=Verbindingsfout
+Name[nn]=Sambandsfeil
+Name[pa]=ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਗਲਤੀ
+Name[pl]=BÅ‚Ä…d Å‚Ä…czenia
+Name[pt]=Erro de Ligação
+Name[pt_BR]=Erro na Conexão
+Name[ru]=Редактор Ñоединений
+Name[sk]=Chyba spojenia
+Name[sl]=Napaka povezave
+Name[sr]=Грешка везе
+Name[sr@Latn]=Greška veze
+Name[sv]=Anslutningsfel
+Name[tr]=Bağlantı Hatası
+Name[uk]=Помилка з'єднаннÑ
+Name[uz]=Ulanish xatosi
+Name[uz@cyrillic]=Уланиш хатоÑи
+Name[zh_CN]=连接错误
+Name[zh_HK]=連線錯誤
+Name[zh_TW]=連線錯誤
+Comment=An error on connection has occurred
+Comment[be]=ÐдбылаÑÑ Ð¿Ð°Ð¼Ñ‹Ð»ÐºÐ° злучÑннÑ
+Comment[bg]=Грешка по време на уÑтановÑване на връзка
+Comment[bn]=সংযোগে à¦à¦•à¦Ÿà¦¿ তà§à¦°à§à¦Ÿà¦¿ ঘটেছে
+Comment[bs]=Došlo je do greške na vezi sa mrežom (Internetom)
+Comment[ca]=Hi ha hagut un error al connectar
+Comment[cs]=Nastala chyba ve spojení
+Comment[da]=En fejl i forbindelsen er opstået
+Comment[de]=Ein Verbindungsfehler ist aufgetreten
+Comment[el]=ΠαÏουσιάστηκε σφάλμα κατά τη σÏνδεση
+Comment[es]=Ocurrió un error en la conexión
+Comment[et]=Ãœhendusega tekkis viga
+Comment[eu]=Errore bat gertatu da konexioan
+Comment[fa]=هنگام اتصال خطایی رخ داده است
+Comment[fi]=Yhteydessä tapahtui virhe
+Comment[fr]=Une erreur de connexion est apparue
+Comment[gl]=Ocorreu un erro na conexión
+Comment[he]=התרחשה שגי××” בחיבור
+Comment[hu]=Hiba történt csatlakozás közben
+Comment[is]=Villa kom upp þegar reynt var að tengjast
+Comment[it]=Si è verificato un errore di connessione
+Comment[ja]=接続ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ
+Comment[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡áƒáƒ¡ შეცდáƒáƒ›áƒ მáƒáƒ®áƒ“áƒ
+Comment[kk]=ҚоÑылым қатеÑÑ– орын алды
+Comment[km]=កំហុស​ការ​ážâ€‹áž—្ជាប់​មួយ​បាន​កើážâ€‹áž¡áž¾áž„
+Comment[lt]=Įvyko ryšio klaida
+Comment[nb]=Det har oppstått en feil ved tilkobling
+Comment[nds]=Dat hett en Verbinnenfehler geven
+Comment[ne]=जडनामा à¤à¤‰à¤Ÿà¤¾ तà¥à¤°à¥à¤Ÿà¤¿ देखापरà¥à¤¯à¥‹
+Comment[nl]=Er deed zich een verbindingsfout voor
+Comment[nn]=Det har oppstått eit problem med sambandet
+Comment[pl]=Wystąpił błąd przy łączeniu
+Comment[pt]=Ocorreu um erro na ligação
+Comment[pt_BR]=Ocorreu um erro na conexão
+Comment[ru]=Возникла ошибка ÑоединениÑ
+Comment[sk]=Nastala chyba spojenia
+Comment[sl]=Prišlo je do napake pri povezavi
+Comment[sr]=Дошло јо до грешке на вези
+Comment[sr@Latn]=Došlo jo do greške na vezi
+Comment[sv]=En fel vid anslutning har inträffat
+Comment[tr]=Bağlantıda hata meydana geldi
+Comment[uk]=ТрапилаÑÑŒ помила під Ñ‡Ð°Ñ Ð·'єднаннÑ
+Comment[uz]=Aloqa oʻrnatishda xato roʻy berdi
+Comment[uz@cyrillic]=Ðлоқа ўрнатишда хато рўй берди
+Comment[zh_CN]=å‘生了连接错误
+Comment[zh_HK]=連線發生錯誤
+Comment[zh_TW]=發生了連線的錯誤
+default_presentation=2
+
+[connection_lost]
+Name=Connection Lost
+Name[be]=ЗлучÑнне згубленае
+Name[bg]=Връзката е прекъÑната
+Name[bn]=সংযোগ বিচà§à¦›à¦¿à¦¨à§à¦¨
+Name[br]=Kollet eo ar gevreadenn
+Name[bs]=Veza je pukla
+Name[ca]=Connexió perduda
+Name[cs]=Spojení ztraceno
+Name[cy]=Collwyd Cysylltiad
+Name[da]=Forbindelse tabt
+Name[de]=Verbindung unterbrochen
+Name[el]=Η σÏνδεση έκλεισε
+Name[eo]=Konekto Perdita
+Name[es]=Conexión perdida
+Name[et]=Ãœhendus kadus
+Name[eu]=Konexioa galdu da
+Name[fa]=اتصال Ù…Ùقود شد
+Name[fi]=Yhteys hävisi
+Name[fr]=Connexion perdue
+Name[ga]=Cailleadh an Nasc
+Name[gl]=Conexión Perdida
+Name[he]=חיבור נסגר
+Name[hu]=A kapcsolat megszakadt
+Name[is]=Tengingu tapað
+Name[it]=Connessione chiusa
+Name[ja]=接続切断
+Name[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ გáƒáƒ¬áƒ§áƒ“áƒ
+Name[kk]=ҚоÑылым үзілді
+Name[km]=បាážáŸ‹â€‹áž”ង់​ការ​ážâ€‹áž—្ជាប់
+Name[lt]=Ryšys prarastas
+Name[nb]=Tilkobling mistet
+Name[nds]=Verbinnen afreten
+Name[ne]=जडान हरायो
+Name[nl]=Verbinding verbroken
+Name[nn]=Samband stengt
+Name[pa]=ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਟà©à©±à¨Ÿà¨¿à¨†
+Name[pl]=Połączenie utracone
+Name[pt]=Perdeu-se a Ligação
+Name[pt_BR]=Conexão Perdida
+Name[ru]=Соединение утерÑно
+Name[sk]=Spojenie stratené
+Name[sl]=Povezava prekinjena
+Name[sr]=Веза изгубљена
+Name[sr@Latn]=Veza izgubljena
+Name[sv]=Anslutning förlorad
+Name[tr]=Bağlantı Kesildi
+Name[uk]=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ñ‚Ñ€Ð°Ñ‡ÐµÐ½Ð¾
+Name[uz]=Ulanish uzildi
+Name[uz@cyrillic]=Уланиш узилди
+Name[zh_CN]=连接已丢失
+Name[zh_HK]=連線已中斷
+Name[zh_TW]=連線已中斷
+Comment=The connection has been lost
+Comment[be]=ЗлучÑнне згубленае
+Comment[bg]=Връзката е прекъÑната
+Comment[bn]=সংযোগ বিচà§à¦›à¦¿à¦¨à§à¦¨ হয়েছে
+Comment[bs]=Veza na mrežu (Internet) je prekinuta
+Comment[ca]=S'ha perdut la connexió
+Comment[cs]=Spojení bylo ztraceno
+Comment[da]=Forbindelsen er gået tabt
+Comment[de]=Die Verbindung wurde unterbrochen
+Comment[el]=Η σÏνδεση έκλεισε
+Comment[eo]=La konekto estis perdita
+Comment[es]=Se perdió la conexión
+Comment[et]=Ãœhendus kadus
+Comment[eu]=Konexioa galdu da
+Comment[fa]=اتصال Ù…Ùقود شده است
+Comment[fi]=Yhteys on hävinnyt
+Comment[fr]=La connexion a été perdue
+Comment[ga]=Cailleadh an nasc
+Comment[gl]=Perdeuse a conexión
+Comment[he]=החיבור נסגר
+Comment[hu]=A kapcsolat megszakadt
+Comment[is]=Tengingin tapaðist
+Comment[it]=La connessione è stata chiusa
+Comment[ja]=接続ãŒåˆ‡æ–­ã•ã‚Œã¾ã—ãŸ
+Comment[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ გáƒáƒ¬áƒ§áƒ“áƒ
+Comment[kk]=ҚоÑылым үзіліÑÑ– орын алды
+Comment[km]=បាន​បាážáŸ‹áž”ង់​ការ​ážáž—្ជាប់
+Comment[lt]=Ryšys prarastas
+Comment[nb]=Tilkoblingen er tapt
+Comment[nds]=De Verbinnen is afreten
+Comment[ne]=जडान हराà¤à¤•à¥‹ छ
+Comment[nl]=De verbinding is verbroken
+Comment[nn]=Sambandet er stengt
+Comment[pl]=Połączenie zostało przerwane
+Comment[pt]=A ligação foi-se abaixo
+Comment[pt_BR]=A conexão foi perdida
+Comment[ru]=Разрыв ÑоединениÑ
+Comment[sk]=Spojenie sa stratilo
+Comment[sl]=Povezava je bila prekinjena
+Comment[sr]=Веза је изгубљена
+Comment[sr@Latn]=Veza je izgubljena
+Comment[sv]=Anslutningen har förlorats
+Comment[tr]=Bağlantı kayboldu
+Comment[uk]=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ втрачено
+Comment[uz]=Oʻrnatilgan aloqa uzildi
+Comment[uz@cyrillic]=Ўрнатилган алоқа узилди
+Comment[zh_CN]=连接已丢失
+Comment[zh_HK]=連線已中斷
+Comment[zh_TW]=連線已中斷
+default_presentation=16
+
+[cannot_connect]
+Name=Cannot Connect
+Name[be]=Ðемагчыма злучыцца
+Name[bg]=Ðевъзможна връзка
+Name[bn]=সংযোগ করা গেল না
+Name[br]=N'hellan ket kevreañ
+Name[bs]=Ne mogu se spojiti
+Name[ca]=No es pot connectar
+Name[cs]=Nelze se připojit
+Name[da]=Kan ikke forbinde
+Name[de]=Verbindung nicht möglich
+Name[el]=ΑδÏνατη η σÏνδεση
+Name[eo]=Ne eblas konekti
+Name[es]=No se pudo conectar
+Name[et]=Ühendumine ebaõnnestus
+Name[eu]=Ezin da konektatu
+Name[fa]=نمی‌تواند متصل شود
+Name[fi]=Ei voitu yhdistää
+Name[fr]=Impossible de se connecter
+Name[ga]=Ní Féidir Nasc a Dhéanamh
+Name[gl]=Non se pode conectar
+Name[he]=×ין ×פשרות להתחבר
+Name[hu]=Nem sikerült csatlakozni
+Name[is]=Get ekki tengst
+Name[it]=Impossibile connettersi
+Name[ja]=接続ã§ãã¾ã›ã‚“
+Name[ka]=დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბრვერ ხáƒáƒ áƒªáƒ˜áƒ”ლდებáƒ
+Name[kk]=ҚоÑылым болмады
+Name[km]=មិន​អាច​ážâ€‹áž—្ជាប់
+Name[lt]=Nepavyksta prisijungti
+Name[nb]=Kan ikke koble til
+Name[nds]=Tokoppeln nich mööglich
+Name[ne]=जडान गरà¥à¤¨ सकिà¤à¤¦à¥ˆà¤¨
+Name[nl]=Verbinding niet mogelijk
+Name[nn]=Klarar ikkje kopla til
+Name[pa]=ਜà©à©œ ਨਹੀਂ ਸਕਦਾ
+Name[pl]=Nie można się połączyć
+Name[pt]=Não É Possível Ligar
+Name[pt_BR]=Não foi possível efetuar a conexão
+Name[ru]=Ðе удаётÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒÑÑ
+Name[sk]=Nedá sa pripojiť
+Name[sl]=Povezava ni mogoÄa
+Name[sr]=Повезивање немогуће
+Name[sr@Latn]=Povezivanje nemoguće
+Name[sv]=Kan inte ansluta
+Name[tr]=Bağlanılamıyor
+Name[uk]=Ðе вдаєтьÑÑ Ð·'єднатиÑÑŒ
+Name[uz]=Ulanib boʻlmadi
+Name[uz@cyrillic]=Уланиб бўлмади
+Name[zh_CN]=无法连接
+Name[zh_HK]=無法連接
+Name[zh_TW]=無法連線
+Comment=Kopete can't connect to the service
+Comment[be]=Kopete не можа злучыцца з ÑервіÑам
+Comment[bg]=УÑтановÑването на връзка е невъзможно
+Comment[bn]=কপেট সারà§à¦­à¦¿à¦¸à§‡ সংযোগ করতে পারেনি
+Comment[bs]=Kopete se ne može spojiti na servis
+Comment[ca]=El Kopete no pot connectar al servei
+Comment[cs]=Kopete se nedokáže připojit ke službě
+Comment[da]=Kopete kan ikke forbinde til tjenesten
+Comment[de]=Kopete kann zu diesem Dienst keine Verbindung herstellen
+Comment[el]=Το Kopete δεν μποÏεί να συνδεθεί με την υπηÏεσία
+Comment[es]=Kopete no se puede conectar al servicio
+Comment[et]=Kopetel ebaõnnestus ühendumine teenusega
+Comment[eu]=Kopete-k ezin du zerbitzuarekin konektatu
+Comment[fa]=Kopete نمی‌تواند به خدمت متصل شود
+Comment[fi]=Kopete ei voi yhdistää palveluun
+Comment[fr]=Kopete ne peut pas se connecter à ce service
+Comment[gl]=Kopete non pode conectar co servicio
+Comment[he]=Kopete ×œ× ×™×›×•×œ להתחבר לשירות
+Comment[hu]=A Kopete nem tudott csatlakozni a szolgáltatóhoz
+Comment[is]=Kopete gat ekki tengst þjónustunni
+Comment[it]=Kopete non è in grado di connettersi al servizio
+Comment[ja]=Kopete ã¯ã‚µãƒ¼ãƒ“スã«æŽ¥ç¶šã§ãã¾ã›ã‚“
+Comment[ka]=Kopeteს áƒáƒ  შეუძლირსერვერთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბáƒ
+Comment[kk]=Kopete қызметке қоÑыла алмады
+Comment[km]=Kopete មិន​អាច​ážâ€‹áž—្ជាប់​ទៅ​សáŸážœáž¶â€‹áž”ាន​ឡើយ
+Comment[lt]=Kopete nepavyksta prisijungti prie serviso
+Comment[nb]=Kopete kan ikke koble til tjenesten
+Comment[nds]=Kopete kann sik nich na den Deenst tokoppeln
+Comment[ne]=कोपेटले सेवामा जडान गरà¥à¤¨ सकà¥à¤¦à¥ˆà¤¨
+Comment[nl]=Kopete kan geen verbinding met de dienst maken
+Comment[nn]=Kopete klarar ikkje kopla til tenesta
+Comment[pl]=Kopete nie może się połączyć z usługą
+Comment[pt]=O Kopete não se conseguiu ligar ao serviço
+Comment[pt_BR]=O Kopete não pode conectar-se ao serviço
+Comment[ru]=Kopete не удаётÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ÑŒÑÑ Ðº Ñлужбе
+Comment[sk]=Kopete sa nevie pripojiť na službu
+Comment[sl]=Kopete se ne more povezati s storitvijo
+Comment[sr]=Kopete Ñе не може повезати Ñа ÑервиÑом
+Comment[sr@Latn]=Kopete se ne može povezati sa servisom
+Comment[sv]=Kopete kan inte ansluta till tjänsten
+Comment[tr]=Kopete servise bağlanamıyor
+Comment[uk]=Kopete не може з'єднатиÑÑŒ зі Ñлужбою
+Comment[uz]=Kopete xizmat bilan aloqa oʻrnataolmadi
+Comment[uz@cyrillic]=Kopete хизмат билан алоқа ўрнатаолмади
+Comment[zh_CN]=Kopete 无法连接到æœåŠ¡
+Comment[zh_HK]=Kopete 無法連接至æœå‹™
+Comment[zh_TW]=Kopete 無法連線到æœå‹™
+default_presentation=16
+
+[network_problems]
+Name=Network Problems
+Name[be]=Ð¡ÐµÑ‚ÐºÐ°Ð²Ñ‹Ñ Ð¿Ñ€Ð°Ð±Ð»ÐµÐ¼Ñ‹
+Name[bg]=Мрежови проблеми
+Name[bn]=নেটওয়ারà§à¦• সমসà§à¦¯à¦¾
+Name[br]=Kudennoù rouedad
+Name[bs]=Mrežni problemi
+Name[ca]=Problemes de xarxa
+Name[cs]=Problémy se sítí
+Name[da]=Netværksproblemer
+Name[de]=Netzwerkprobleme
+Name[el]=ΠÏοβλήματα δικτÏου
+Name[eo]=Retproblemoj
+Name[es]=Problemas de red
+Name[et]=Võrguprobleemid
+Name[eu]=Sare-arazoak
+Name[fa]=مسئله‌های شبکه
+Name[fi]=Verkko-ongelma
+Name[fr]=Problèmes réseaux
+Name[ga]=Fadhbanna Líonra
+Name[gl]=Problemas na rede
+Name[he]=בעיות רשת
+Name[hu]=Hálózati hibák
+Name[is]=Netvandamál
+Name[it]=Problemi di rete
+Name[ja]=ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã®å•é¡Œ
+Name[ka]=ქსელის პრáƒáƒ‘ლემები
+Name[kk]=Желідегі мәÑелелер
+Name[km]=បញ្ហា​បណ្ដាញ
+Name[lt]=Tinklo problemos
+Name[nb]=Nettverksproblemer
+Name[nds]=Nettwarkproblemen
+Name[ne]=सञà¥à¤œà¤¾à¤² समसà¥à¤¯à¤¾
+Name[nl]=Netwerkproblemen
+Name[nn]=Nettverksproblem
+Name[pa]=ਨੈੱਟਵਰਕ ਸਮੱਸਿਆ
+Name[pl]=Problemy sieci
+Name[pt]=Problemas na Rede
+Name[pt_BR]=Problemas na Rede
+Name[ru]=Сбой Ñети
+Name[sk]=Sieťové problémy
+Name[sl]=Omrežne težave
+Name[sr]=Проблеми мреже
+Name[sr@Latn]=Problemi mreže
+Name[sv]=Nätverksproblem
+Name[tr]=AÄŸ Problemleri
+Name[uk]=Проблеми в мережі
+Name[uz]=Tarmoq muammolari
+Name[uz@cyrillic]=Тармоқ муаммолари
+Name[zh_CN]=网络故障
+Name[zh_HK]=網絡å•é¡Œ
+Name[zh_TW]=網路å•é¡Œ
+Comment=The network is experiencing problems
+Comment[be]=Сетка не можа Ñправіцца з праблемамі
+Comment[bg]=Мрежови проблеми
+Comment[bn]=নেটওয়ারà§à¦• সমসà§à¦¯à¦¾ অনà§à¦­à¦¬ করছে
+Comment[bs]=Došlo je do problema na mreži
+Comment[ca]=La xarxa està tenint problemes
+Comment[cs]=Síť má problémy
+Comment[da]=Netværket har for øjeblikket problemer
+Comment[de]=Das Netzwerk scheint derzeit gestört zu sein
+Comment[el]=ΠαÏουσιάστηκαν Ï€Ïοβλήματα στο δίκτυο
+Comment[es]=La red sufre problemas
+Comment[et]=Võrguga on mingeid probleeme
+Comment[eu]=Sareak arazoak ditu
+Comment[fa]=شبکه، مسائل را آزمایش می‌کند
+Comment[fi]=Verkossa on ongelmia
+Comment[fr]=Le réseau rencontre des problèmes
+Comment[gl]=A rede está experimentando problemas
+Comment[he]=הרשת חווה בעיות
+Comment[hu]=Hiba lépett fel a hálózaton
+Comment[is]=Það er vandamál með netið
+Comment[it]=Ci sono dei problemi di rete
+Comment[ja]=ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã«å•é¡ŒãŒç™ºç”Ÿã—ã¦ã„ã¾ã™
+Comment[ka]=ქსელს პრáƒáƒ‘ლემები áƒáƒ¥áƒ•áƒ¡
+Comment[kk]=Желіде бір мәÑелелер орын алды
+Comment[km]=បណ្ដាញ​កំពុង​ជួបប្រទះ​បញ្ហា
+Comment[lt]=Tinkle yra problemų
+Comment[nb]=Nettverket har problemer nå
+Comment[nds]=As dat lett gifft dat Nettwarkproblemen
+Comment[ne]=सञà¥à¤œà¤¾à¤²à¤²à¥‡ समसà¥à¤¯à¤¾ अनà¥à¤­à¤µ गरिरहेको छ
+Comment[nl]=Het netwerk ondervindt problemen
+Comment[nn]=Det er problem i nettverket
+Comment[pl]=W sieci występują problemy
+Comment[pt]=A rede está com problemas
+Comment[pt_BR]=A rede está com problemas
+Comment[ru]=Ð’ Ñети возникли неполадки
+Comment[sk]=Sieť zakúša problémy
+Comment[sl]=V omrežju se pojavljajo težave
+Comment[sr]=Мрежа има проблема
+Comment[sr@Latn]=Mreža ima problema
+Comment[sv]=Nätverket har för närvarande problem
+Comment[tr]=AÄŸ problemleri var
+Comment[uk]=В мережі виникли проблеми
+Comment[zh_CN]=网络é‡åˆ°é—®é¢˜
+Comment[zh_HK]=網絡發生å•é¡Œ
+Comment[zh_TW]=網路有å•é¡Œ
+default_presentation=16
+
+[server_error]
+Name=Server Internal Error
+Name[be]=Ð£Ð½ÑƒÑ‚Ñ€Ð°Ð½Ð°Ñ Ð¿Ð°Ð¼Ñ‹Ð»ÐºÐ° Ñервера
+Name[bg]=Вътрешна грешка на Ñървъра
+Name[bn]=সারà§à¦­à¦¾à¦° অভà§à¦¯à¦¨à§à¦¤à¦°à§€à¦¨ তà§à¦°à§à¦Ÿà¦¿
+Name[bs]=Interna greška servera
+Name[ca]=Error intern del servidor
+Name[cs]=Interní chyba serveru
+Name[da]=Intern fejl i serveren
+Name[de]=Interner Serverfehler
+Name[el]=ΕσωτεÏικό σφάλμα του εξυπηÏετητή
+Name[eo]=Servil-interna eraro
+Name[es]=Error interno del servidor
+Name[et]=Serveri sisemine viga
+Name[eu]=Zerbitzariaren barne-errorea
+Name[fa]=خطای درونی کارساز
+Name[fi]=Palvelimen sisäinen virhe
+Name[fr]=Erreur interne du serveur
+Name[gl]=Erro Interno do Servidor
+Name[hu]=Belső kiszolgálóhiba
+Name[is]=Innri villa í þjóni
+Name[it]=Errore interno del server
+Name[ja]=サーãƒã®å†…部エラー
+Name[ka]=სერვერის შიდრშეცდáƒáƒ›áƒ
+Name[kk]=Сервердегі ішкі қате
+Name[km]=កំហុស​ážáž¶áž„​ក្នុង​ម៉ាស៊ីន​បម្រើ
+Name[lt]=VidinÄ— serverio klaida
+Name[nb]=Intern tjenerfeil
+Name[nds]=Serverintern Fehler
+Name[ne]=सरà¥à¤­à¤°à¤•à¥‹ आनà¥à¤¤à¤°à¥€à¤• तà¥à¤°à¥à¤Ÿà¤¿
+Name[nl]=Interne serverfout
+Name[nn]=Intern tenarfeil
+Name[pa]=ਸਰਵਰ ਅੰਦਰੂਨੀ ਗਲਤੀ
+Name[pl]=Wewnętrzny błąd serwera
+Name[pt]=Erro Interno do Servidor
+Name[pt_BR]=Erro interno do Servidor
+Name[ru]=ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° Ñервера
+Name[sk]=Interná chyba servera
+Name[sl]=Notranja napaka strežnika
+Name[sr]=Интерна грешка Ñервера
+Name[sr@Latn]=Interna greška servera
+Name[sv]=Internt fel i servern
+Name[tr]=Sunucu İç Hatası
+Name[uk]=Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° Ñервера
+Name[zh_CN]=æœåŠ¡å™¨å†…部错误
+Name[zh_HK]=伺æœå™¨å…§éƒ¨éŒ¯èª¤
+Name[zh_TW]=伺æœå™¨å…§éƒ¨éŒ¯èª¤
+Comment=A service internal error has occurred
+Comment[be]=ÐдбылаÑÑ ÑžÐ½ÑƒÑ‚Ñ€Ð°Ð½Ð°Ñ Ð¿Ð°Ð¼Ñ‹Ð»ÐºÐ° Ñервера
+Comment[bg]=Възникна вътрешна грешка на Ñървъра
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ সারà§à¦­à¦¿à¦¸ অভà§à¦¯à¦¨à§à¦¤à¦°à§€à¦¨ তà§à¦°à§à¦Ÿà¦¿ ঘটেছে
+Comment[bs]=Desila se interna greška servisa
+Comment[ca]=Hi ha hagut un error intern del servei
+Comment[cs]=Nastala interní chyba služby
+Comment[da]=En intern fejl for tjenesten er opstået
+Comment[de]=Bei diesem Dienst ist ein interner Serverfehler aufgetreten.
+Comment[el]=ΠαÏουσιάστηκε ένα εσωτεÏικό σφάλμα της υπηÏεσίας
+Comment[es]=Ocurrió un error interno en el servicio
+Comment[et]=Tekkis teenuse sisemine viga
+Comment[eu]=Zerbitzuaren barne-errore bat gertatu da
+Comment[fa]=یک خطای درونی رخ داده است
+Comment[fi]=Tapahtui palvelun sisäinen virhe
+Comment[fr]=Une erreur interne au service s'est produite
+Comment[gl]=Ocorreu un erro interno do servicio
+Comment[hu]=Belső hiba történt a szolgáltatásban
+Comment[is]=Innri villa í þjónustu hefur átt sér stað
+Comment[it]=Si è verificato un errore interno del servizio
+Comment[ja]=サービス内部エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ
+Comment[ka]=სერვერის შიდრშეცდáƒáƒ›áƒ
+Comment[kk]=Қызметте бір ішкі қате пайда болды
+Comment[km]=កំហុស​ážáž¶áž„​ក្នុង​សáŸážœáž¶â€‹áž”ាន​កើážâ€‹áž¡áž¾áž„
+Comment[lt]=Įvyko vidine serverio klaida
+Comment[nb]=Det har oppstått en intern feil ved tjenesten
+Comment[nds]=Dat hett binnen den Deenst en Fehler geven
+Comment[ne]=सरà¥à¤­à¤°à¤•à¥‹ आनà¥à¤¤à¤°à¥€à¤• तà¥à¤°à¥à¤Ÿà¤¿ देखापरà¥à¤¯à¥‹
+Comment[nl]=Er deed zich een interne fout op de server voor
+Comment[nn]=Det har skjedd ein feil internt i tenesta
+Comment[pl]=Wystąpił błąd wewnętrzny usługi
+Comment[pt]=Ocorreu um erro interno do serviço
+Comment[pt_BR]=Ocorreu um erro interno no serviço
+Comment[ru]=ВнутреннÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° Ñлужбы
+Comment[sk]=Nastala interná chyba servera
+Comment[sl]=Prišlo je do notranje napake pri storitvi
+Comment[sr]=Дошло јо до интерне грешке ÑервиÑа
+Comment[sr@Latn]=Došlo jo do interne greške servisa
+Comment[sv]=Ett internt fel har inträffat i tjänsten
+Comment[tr]=Serviste iç hata oluştu
+Comment[uk]=ТрапилаÑÑŒ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° Ñлужби
+Comment[uz]=Xizmatda ichki xato roʻy berdi
+Comment[uz@cyrillic]=Хизматда ички хато рўй берди
+Comment[zh_CN]=å‘生了æœåŠ¡å†…部错误
+Comment[zh_HK]=æœå‹™ç™¼ç”Ÿå…§éƒ¨éŒ¯èª¤
+Comment[zh_TW]=æœå‹™ç™¼ç”Ÿå…§éƒ¨éŒ¯èª¤
+default_presentation=16
+
+[buzz_nudge]
+Name=Buzz/Nudge
+Name[bg]=Сбутване
+Name[bn]=গà§à¦žà§à¦œà¦¨/গà§à¦à¦¤à§‹
+Name[ca]=Truca/Avisa
+Name[cs]=Å touchanec
+Name[da]=Opringning/Puf
+Name[el]=Βομβητής/ειδοποίηση
+Name[es]=Zumbido
+Name[et]=Mõmin/müks
+Name[eu]=Burrumbada/Ukondokada
+Name[fr]=Vibration
+Name[hu]=Figyelemfelhívó
+Name[is]=Ãta við
+Name[it]=Buzz/Trillo
+Name[ja]=ブザー/注æ„å–šèµ·
+Name[kk]=Қоңырау
+Name[km]=សន្ទនា​រក ឬ កáŸáŸ‡áž€áŸ€ážœ
+Name[lt]=Skambutis/Kumštelėjimas
+Name[nds]=Anstoot
+Name[ne]=बज/नज
+Name[nn]=Pirk
+Name[pl]=Pobudka/kuksaniec
+Name[pt]=Apitar/Tocar
+Name[pt_BR]=Pedir Atenção (Buzinar/Tremer)
+Name[sl]=Brnenje/dregnenje
+Name[sr]=Зврц/Гуркање
+Name[sr@Latn]=Zvrc/Gurkanje
+Name[sv]=PÃ¥ringning/knuff
+Name[tr]=Sesli Uyarı/Uyarı
+Name[uk]=Гудок/поштовх
+Name[zh_CN]=é—ªå±æŒ¯åŠ¨
+Name[zh_HK]=響è²/æ示
+Name[zh_TW]=呼å«/來電震動
+Comment=A contact has sent you a buzz/nudge.
+Comment[bg]=Контакт ви изпрати Ñбутване
+Comment[bn]=যোগাযোগ তালিকাভà§à¦•à§à¦¤ à¦à¦•à¦œà¦¨ আপনাকে à¦à¦•à¦Ÿà¦¿ গà§à¦žà§à¦œà¦¨/গà§à¦à¦¤à§‹ পাঠিয়েছে।
+Comment[bs]=Kontakt vam je poslao buzz/nudge.
+Comment[ca]=Un contacte us ha enviat una trucada/avís.
+Comment[cs]=Osoba vám poslala šťouchanec :-)
+Comment[da]=En kontakt har sendt dig en buzz/nudge.
+Comment[de]=Ein Benutzer macht auf sich aufmerksam.
+Comment[el]=Μια επαφή μόλις σας έστειλα έναν βομβητή/ειδοποίηση.
+Comment[es]=Un contacto le ha mandado un zumbido
+Comment[et]=Kontakt müksas sind.
+Comment[eu]=Kontaktu batek burrumbada/ukondokada bat bidali dizu.
+Comment[fa]=تماسی برای شما یک buzz/nudge ارسال کرده است.
+Comment[fr]=Un contact vous a envoyé une vibration.
+Comment[hu]=Egy partner figyelemfelhívó üzenetet küldött.
+Comment[is]=Notandi hefur ýtt við þér.
+Comment[it]=Un contatto ti ha inviato un buzz/trillo.
+Comment[ja]=コンタクトãŒã¤ã¤ã„ã¦ã„ã¾ã™
+Comment[ka]=მეგáƒáƒ‘áƒáƒ áƒ›áƒ გáƒáƒ›áƒáƒ’იგზáƒáƒ•áƒœáƒáƒ— buzz/nudge.
+Comment[kk]=Сізге қонырау жіберілді.
+Comment[km]=ទំនាក់​ទំនង​មួយ​បាន​កáŸáŸ‡áž€áŸ€ážœ ឬ សន្ទនា​រក​អ្នក ។
+Comment[lt]=Kontaktas siunÄia jums skambutį/kumÅ¡telÄ—jimÄ….
+Comment[nb]=En kontakt har sendt deg en buzz/nudge
+Comment[nds]=En Bruker will wat vun Di.
+Comment[ne]=समà¥à¤ªà¤°à¥à¤•à¤²à¥‡ तपाईà¤à¤²à¤¾à¤ˆ बज/नज पठायो ।
+Comment[nl]=Een contact heeft u een buzz/nudge gestuurd.
+Comment[nn]=Ein kontakt har pirka borti deg.
+Comment[pl]=Użytkownik wysłał ci pobudkę/kuksańca.
+Comment[pt]=Um contacto enviou-lhe um toque/apito.
+Comment[pt_BR]=Um contato lhe enviou um pedido de atenção.
+Comment[ru]=Вам отправили buzz/nudge.
+Comment[sk]=Kontakt Vám poslal buzz/nudge.
+Comment[sl]=Uporabnik vam je poslal brnenje/dregnenje.
+Comment[sr]=Контакт вам је поÑлао зврц/гуркање.
+Comment[sr@Latn]=Kontakt vam je poslao zvrc/gurkanje.
+Comment[sv]=En kontakt har ringt eller knuffat dig.
+Comment[tr]=Birisi size bir titreşim gönderdi.
+Comment[uk]=Контакт надіÑлав вам гудок/поштовх.
+Comment[zh_CN]=è”系人å‘您å‘é€äº†é—ªå±æŒ¯åŠ¨ã€‚
+Comment[zh_HK]=有è¯çµ¡äººå‚³äº†éŸ¿è²æˆ–æ示給您。
+Comment[zh_TW]=è¯çµ¡äººå°æ‚¨é€å‡ºå‘¼å«/來電震動
+default_sound=Kopete_Received.ogg
+default_presentation=17
diff --git a/kopete/kopete/groupkabcselectorwidget.ui b/kopete/kopete/groupkabcselectorwidget.ui
new file mode 100644
index 00000000..fe0223ba
--- /dev/null
+++ b/kopete/kopete/groupkabcselectorwidget.ui
@@ -0,0 +1,91 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GroupKABCSelectorWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GroupKABCSelectorWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>487</width>
+ <height>80</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <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>layout13</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>kabcLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Addressbook entry:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kabcCombo</cstring>
+ </property>
+ </widget>
+ <widget class="Kopete::UI::AddressBookLinkWidget" row="1" column="1">
+ <property name="name">
+ <cstring>widAddresseeLink</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>groupLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Group</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>groupCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>groupCombo</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<customwidgets>
+ <customwidget>
+ <class>Kopete::UI::AddressBookLinkWidget</class>
+ <header>addressbooklinkwidget.h</header>
+ </customwidget>
+</customwidgets>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/kopete/hi128-app-kopete.png b/kopete/kopete/hi128-app-kopete.png
new file mode 100644
index 00000000..c42fe9f0
--- /dev/null
+++ b/kopete/kopete/hi128-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hi16-app-kopete.png b/kopete/kopete/hi16-app-kopete.png
new file mode 100644
index 00000000..7889b585
--- /dev/null
+++ b/kopete/kopete/hi16-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hi22-app-kopete.png b/kopete/kopete/hi22-app-kopete.png
new file mode 100644
index 00000000..6bd90e32
--- /dev/null
+++ b/kopete/kopete/hi22-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hi32-app-kopete.png b/kopete/kopete/hi32-app-kopete.png
new file mode 100644
index 00000000..cfd03ba7
--- /dev/null
+++ b/kopete/kopete/hi32-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hi48-app-kopete.png b/kopete/kopete/hi48-app-kopete.png
new file mode 100644
index 00000000..0118a598
--- /dev/null
+++ b/kopete/kopete/hi48-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hi64-app-kopete.png b/kopete/kopete/hi64-app-kopete.png
new file mode 100644
index 00000000..7b49bb96
--- /dev/null
+++ b/kopete/kopete/hi64-app-kopete.png
Binary files differ
diff --git a/kopete/kopete/hisc-app-kopete2.svgz b/kopete/kopete/hisc-app-kopete2.svgz
new file mode 100644
index 00000000..7a154365
--- /dev/null
+++ b/kopete/kopete/hisc-app-kopete2.svgz
Binary files differ
diff --git a/kopete/kopete/kconf_update/Makefile.am b/kopete/kopete/kconf_update/Makefile.am
new file mode 100644
index 00000000..dc808c00
--- /dev/null
+++ b/kopete/kopete/kconf_update/Makefile.am
@@ -0,0 +1,32 @@
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT $(all_includes)
+
+update_DATA = kopete-pluginloader.upd kopete-account-kconf_update.upd \
+ kopete-pluginloader2.upd kopete-jabberproxytype-kconf_update.upd \
+ kopete-jabberpriorityaddition-kconf_update.upd kopete-nameTracking.upd
+update_SCRIPTS = kopete-pluginloader.pl kopete-account-kconf_update.sh \
+ kopete-pluginloader2.sh kopete-jabberproxytype-kconf_update.sh \
+ kopete-jabberpriorityaddition-kconf_update.sh kopete-account-0.10.pl
+updatedir = $(kde_datadir)/kconf_update
+
+# The Qt app cannot go into kde_datadir, that is not portable.
+# install to kde_bindir/kconf_update_bin instead.
+# KDE 3.2 will allow kconf_update scripts to run directly from there,
+# but for us that's too late. Use the .sh script as a workaround.
+kconf_PROGRAMS = kopete-account-kconf_update kopete-pluginloader2-kconf_update \
+ kopete-nameTracking-kconf_update
+kconfdir = $(libdir)/kconf_update_bin
+
+kopete_account_kconf_update_SOURCES = kopete-account-kconf_update.cpp
+kopete_account_kconf_update_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kopete_account_kconf_update_LDADD = $(LIB_QT)
+
+kopete_pluginloader2_kconf_update_SOURCES = kopete-pluginloader2.cpp
+kopete_pluginloader2_kconf_update_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kopete_pluginloader2_kconf_update_LDADD = $(LIB_QT)
+
+kopete_nameTracking_kconf_update_SOURCES = kopete-nameTracking.cpp
+kopete_nameTracking_kconf_update_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kopete_nameTracking_kconf_update_LDADD = $(LIB_QT) $(LIB_KDECORE)
+
+# vim: set noet:
+
diff --git a/kopete/kopete/kconf_update/kopete-account-0.10.pl b/kopete/kopete/kconf_update/kopete-account-0.10.pl
new file mode 100755
index 00000000..3925a52f
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-account-0.10.pl
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+# Olivier Goffart <ogoffart @ tiscalinet.be>
+# License: GPL
+
+use strict;
+
+# This script rename old plugin datas key.
+# It remove the PlguinData_PLUGINID_ prefix from keys.
+
+# read the whole config file
+my $currentGroup = "";
+my %configFile;
+while ( <> ) {
+ chomp; # eat the trailing '\n'
+ next if ( /^$/ ); # skip empty lines
+ next if ( /^\#/ ); # skip comments
+ if ( /^\[/ ) { # group begin
+ $currentGroup = $_;
+ next;
+ } elsif ( $currentGroup =~ /^\[Account_/ and /^PluginData\_.+_(.+)=(.+)$/ )
+ {
+ print "$currentGroup\n$1=$2\n";
+ my ($key,$value) = split /=/;
+ print "# DELETE $currentGroup$key\n";
+ }
+}
diff --git a/kopete/kopete/kconf_update/kopete-account-kconf_update.cpp b/kopete/kopete/kconf_update/kopete-account-kconf_update.cpp
new file mode 100644
index 00000000..fe1c3351
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-account-kconf_update.cpp
@@ -0,0 +1,251 @@
+/*
+ kconf_update app for migrating kopete 0.6.x accounts to 0.7. Code is
+ not up to my normal standards, but it does the job, and since it's
+ supposed to run exactly once on each system that's good enough for me :)
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+static QTextStream qcin ( stdin, IO_ReadOnly );
+static QTextStream qcout( stdout, IO_WriteOnly );
+static QTextStream qcerr( stderr, IO_WriteOnly );
+
+// Group cache. Yes, I know global vars are ugly :)
+bool needFlush = false;
+QString accountId;
+QString password;
+QString autoConnect;
+QString protocol;
+QMap<QString, QString> pluginData;
+
+// Global vars to hold separate IRC vars until we have read all of them
+QString ircNick;
+QString ircServer;
+QString ircPort;
+
+/*
+ * Function for (en/de)crypting strings for config file, taken from KMail
+ * Author: Stefan Taferner <taferner@alpin.or.at>
+ */
+QString cryptStr(const QString &aStr)
+{
+ QString result;
+ for (unsigned int i = 0; i < aStr.length(); i++)
+ result += (aStr[i].unicode() < 0x20) ? aStr[i] :
+ QChar(0x1001F - aStr[i].unicode());
+ return result;
+}
+
+void parseGroup( const QString &group, const QString &rawLine )
+{
+ // Groups that are converted can almost certainly be removed entirely
+
+ if ( group == "MSN" || group == "ICQ" || group == "Oscar" || group == "Gadu" || group == "Jabber" || group == "IRC" )
+ {
+ accountId = "EMPTY";
+ autoConnect = "true";
+
+ if ( group == "Oscar" )
+ protocol = "AIMProtocol";
+ else
+ protocol = group + "Protocol";
+
+ password = QString::null;
+ pluginData.clear();
+
+ needFlush = true;
+
+ qcout << "# DELETEGROUP [" << group << "]" << endl;
+ }
+ else
+ {
+ // Groups we don't convert. Output the raw line instead.
+ qcout << rawLine << endl;
+ }
+}
+
+void parseKey( const QString &group, const QString &key, const QString &value, const QString &rawLine )
+{
+ //qcerr << "*** group='" << group << "'" << endl;
+ if ( group == "MSN" )
+ {
+ if ( key == "UserID" )
+ accountId = value;
+ else if ( key == "Password" )
+ password = value;
+ else if ( key == "AutoConnect" )
+ autoConnect = value;
+ else if ( key == "Nick" )
+ pluginData[ "displayName" ] = value;
+
+ // All other keys are ignored for MSN, as these apply to stuff that's
+ // now in libkopete (and the main app) instead.
+ }
+ else if ( group == "ICQ" )
+ {
+ if ( key == "UIN" )
+ accountId = value;
+ else if ( key == "Password" )
+ password = value;
+ else if ( key == "AutoConnect" )
+ autoConnect = value;
+ else if ( key == "Nick" )
+ pluginData[ "NickName" ] = value;
+ else if ( key == "Server" )
+ pluginData[ key ] = value;
+ else if ( key == "Port" )
+ pluginData[ key ] = value;
+ }
+ else if ( group == "Oscar" )
+ {
+ if ( key == "ScreenName" )
+ accountId = value;
+ else if ( key == "Password" )
+ password = value;
+ else if ( key == "Server" )
+ pluginData[ key ] = value;
+ else if ( key == "Port" )
+ pluginData[ key ] = value;
+ }
+ else if ( group == "Jabber" )
+ {
+ if ( key == "UserID" )
+ accountId = value;
+ else if ( key == "Password" )
+ password = value;
+ if ( key == "Server" ||
+ key == "Port" || key == "UseSSL" || key == "Resource" )
+ pluginData[ key ] = value;
+ }
+ else if ( group == "Gadu" )
+ {
+ if ( key == "UIN" )
+ accountId = value;
+ else if ( key == "Password" )
+ password = value;
+ else if ( key == "Nick" )
+ pluginData[ "displayName" ] = value;
+ }
+ else if ( group == "IRC" )
+ {
+ if ( key == "Nickname" )
+ ircNick = value;
+ if ( key == "Server" )
+ ircServer = value;
+ if ( key == "Port" )
+ ircPort = value;
+ if ( accountId == "EMPTY" &&
+ !ircNick.isEmpty( ) && !ircServer.isEmpty() &&
+ !ircPort.isEmpty() )
+ {
+#if QT_VERSION < 0x030200
+ accountId = QString::fromLatin1( "%1@%2:%3" ).arg( ircNick ).arg( ircServer ).arg( ircPort );
+#else
+ accountId = QString::fromLatin1( "%1@%2:%3" ).arg( ircNick, ircServer, ircPort );
+#endif
+ }
+ }
+ /*
+ fixme: insert all other plugins here - martijn
+ */
+ else if ( key == "Modules" )
+ {
+ QString newValue = value;
+ newValue.replace ( ".plugin", ".desktop" );
+ qcout << "Plugins=" << newValue;
+ }
+ else
+ {
+ // groups we don't convert. output the raw line instead.
+ qcout << rawLine << endl;
+ }
+}
+
+void flushData( const QString &group )
+{
+
+ qcout << "[Account_" << protocol << "_" << accountId << "]" << endl;
+ qcout << "Protocol=" << protocol << endl;
+
+ if( group == "Jabber" )
+ qcout << "AccountId=" << accountId << "@" << pluginData["Server"] << endl;
+ else
+ qcout << "AccountId=" << accountId << endl;
+
+ qcout << "Password=" << cryptStr( password ) << endl;
+ qcout << "AutoConnect=" << autoConnect << endl;
+
+ QMap<QString, QString>::ConstIterator it;
+ for ( it = pluginData.begin(); it != pluginData.end(); ++it )
+ qcout << "PluginData_" << protocol << "_" << it.key() << "=" << it.data() << endl;
+
+}
+
+int main()
+{
+ qcin.setEncoding( QTextStream::UnicodeUTF8 );
+ qcout.setEncoding( QTextStream::UnicodeUTF8 );
+
+ QString curGroup;
+
+ QRegExp groupRegExp( "^\\[(.*)\\]" );
+ QRegExp keyRegExp( "^([a-zA-Z0-9:, _-]*)\\s*=\\s*(.*)\\s*" );
+ QRegExp commentRegExp( "^(#.*)?$" );
+
+ while ( !qcin.atEnd() )
+ {
+ QString line = qcin.readLine();
+
+ if ( commentRegExp.exactMatch( line ) )
+ {
+ // We found a comment, leave unchanged
+ qcout << line << endl;
+ }
+ else if ( groupRegExp.exactMatch( line ) )
+ {
+ // We found the start of a group, parse it
+ if ( needFlush )
+ {
+ // ... but we were already working on a group, so finish what
+ // we were doing - flush existing group first
+ flushData ( curGroup );
+ needFlush = false;
+ }
+
+ curGroup = groupRegExp.capturedTexts()[ 1 ];
+ parseGroup( curGroup, line );
+ }
+ else if ( keyRegExp.exactMatch( line ) )
+ {
+ // We found the a key line
+ parseKey( curGroup, keyRegExp.capturedTexts()[ 1 ], keyRegExp.capturedTexts()[ 2 ], line );
+ }
+ else
+ {
+ qcerr << "** Unknown input line: " << line << endl;
+ }
+ }
+
+ if ( needFlush )
+ flushData ( curGroup );
+
+ return 0;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kconf_update/kopete-account-kconf_update.sh b/kopete/kopete/kconf_update/kopete-account-kconf_update.sh
new file mode 100644
index 00000000..4b2e086e
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-account-kconf_update.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+IFS=:
+SUFF=kconf_update_bin/kopete-account-kconf_update
+for path in `kde-config --path lib`; do
+ if test -x "$path/$SUFF"; then
+ exec "$path/$SUFF"
+ fi
+done
+
diff --git a/kopete/kopete/kconf_update/kopete-account-kconf_update.upd b/kopete/kopete/kconf_update/kopete-account-kconf_update.upd
new file mode 100644
index 00000000..ed1f3ac7
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-account-kconf_update.upd
@@ -0,0 +1,9 @@
+Id=kopete0.7/r1
+File=kopeterc
+Script=kopete-account-kconf_update.sh,sh
+
+#remove the PluginData_ProtocolID_ prefix from config keys
+Id=kopete0.10/r1
+File=kopeterc
+Script=kopete-account-0.10.pl,perl
+
diff --git a/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.sh b/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.sh
new file mode 100644
index 00000000..2ba47416
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+sed -e 's/^\(PluginData_JabberProtocol_Resource=.*\)$/\1\nPluginData_JabberProtocol_Priority=5/'
diff --git a/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.upd b/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.upd
new file mode 100644
index 00000000..8e761e6f
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-jabberpriorityaddition-kconf_update.upd
@@ -0,0 +1,4 @@
+Id=kopete0.9/r1
+File=kopeterc
+Script=kopete-jabberpriorityaddition-kconf_update.sh,sh
+
diff --git a/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.sh b/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.sh
new file mode 100644
index 00000000..3cbf8a55
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+sed "s/PluginData_JabberProtocol_ProxyType=SOCKS4/PluginData_JabberProtocol_ProxyType=SOCKS/" | sed "s/PluginData_JabberProtocol_ProxyType=SOCKS5/PluginData_JabberProtocol_ProxyType=SOCKS/"
diff --git a/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.upd b/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.upd
new file mode 100644
index 00000000..c39ce69b
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-jabberproxytype-kconf_update.upd
@@ -0,0 +1,4 @@
+Id=kopete0.9/r1
+File=kopeterc
+Script=kopete-jabberproxytype-kconf_update.sh,sh
+
diff --git a/kopete/kopete/kconf_update/kopete-nameTracking.cpp b/kopete/kopete/kconf_update/kopete-nameTracking.cpp
new file mode 100644
index 00000000..391e5132
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-nameTracking.cpp
@@ -0,0 +1,135 @@
+/*
+ kconf_update app for updating the contactlist format ( <= 0.9.0) for MetaContacts to
+ track the name of a subcontact.
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdir.h>
+#include <qtextstream.h>
+#include <qdom.h>
+
+#include <kstandarddirs.h>
+
+static QTextStream qcerr( stderr, IO_WriteOnly );
+
+int main()
+{
+ KInstance* inst = new KInstance( "Update script" );
+ QString filename = locateLocal( "data", QString::fromLatin1( "kopete/contactlist.xml" ) );
+
+ // Load contact list & save backup.
+ QFile contactListFile( filename );
+ contactListFile.open( IO_ReadOnly );
+ QDomDocument contactList;
+ contactList.setContent( &contactListFile );
+ contactListFile.close();
+ QDir().rename( filename, filename + QString::fromLatin1( ".bak" ) );
+
+ // parse the XML file
+ QDomElement list = contactList.documentElement();
+ QDomElement mcElement = list.firstChild().toElement();
+
+ while( !mcElement.isNull() )
+ {
+
+ // update all the MetaContacts
+ if( mcElement.tagName() == QString::fromLatin1("meta-contact") )
+ {
+ QDomElement displayName;
+ QDomElement subcontact;
+
+ QDomElement elem = mcElement.firstChild().toElement();
+ while( !elem.isNull() )
+ {
+ if( elem.tagName() == QString::fromLatin1( "display-name" ) )
+ displayName = elem;
+ if( elem.tagName() == QString::fromLatin1( "plugin-data" ) )
+ {
+ // check if it's a contact by checking for "protocol" substring in the tag,
+ // and the presence of a contactId child element.
+ QString pluginId = elem.attribute( QString::fromLatin1( "plugin-id" ) );
+ bool isProtocol = ( pluginId.contains( "protocol", false ) > 0 ); // case-insensitive search
+ bool hasContactId = false;
+ QDomNode field = elem.firstChild();
+ while( !field.isNull() )
+ {
+ QDomElement fieldElem = field.toElement();
+
+ if( !fieldElem.isNull() &&
+ fieldElem.tagName() == QString::fromLatin1( "plugin-data-field" ) &&
+ fieldElem.attribute( QString::fromLatin1( "key" ) ) == QString::fromLatin1( "contactId" ) )
+ {
+ hasContactId = true;
+ break;
+ }
+ field = field.nextSibling();
+ }
+
+ if( isProtocol && hasContactId )
+ subcontact = elem;
+ }
+
+ elem = elem.nextSibling().toElement();
+ } // end while
+
+ // check if we're even tracking the subcontact's name
+ // if displayName.isNull(), it simply won't find the attribute; no harm done
+ bool tracking =
+ ( displayName.attribute( QString::fromLatin1( "trackChildNameChanges" ),
+ QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ if( !displayName.isNull() && !subcontact.isNull() && tracking )
+ {
+ // collect info
+ QString nsCID;
+ QString nsPID;
+ QString nsAID;
+
+ nsPID = subcontact.attribute( QString::fromLatin1( "plugin-id" ) );
+ QDomNode field = subcontact.firstChild();
+ while( !field.isNull() )
+ {
+ QDomElement fieldElem = field.toElement();
+
+ if( !fieldElem.isNull() && fieldElem.tagName() == QString::fromLatin1( "plugin-data-field" ) )
+ {
+ if( fieldElem.attribute( QString::fromLatin1( "key" ) ) == QString::fromLatin1( "contactId" ) )
+ nsCID = fieldElem.text();
+ if( fieldElem.attribute( QString::fromLatin1( "key" ) ) == QString::fromLatin1( "accountId" ) )
+ nsAID = fieldElem.text();
+ }
+ field = field.nextSibling();
+ }
+
+ // create the tracking info
+ displayName.setAttribute( QString::fromLatin1( "nameSourceContactId" ), nsCID );
+ displayName.setAttribute( QString::fromLatin1( "nameSourcePluginId" ), nsPID );
+ displayName.setAttribute( QString::fromLatin1( "nameSourceAccountId" ), nsAID );
+ }
+ }
+
+ mcElement = mcElement.nextSibling().toElement();
+ }
+
+ // Save converted contactlist
+ contactListFile.open( IO_WriteOnly );
+ QTextStream stream( &contactListFile );
+ stream.setEncoding( QTextStream::UnicodeUTF8 );
+ stream << contactList.toString( 4 );
+ contactListFile.flush();
+ contactListFile.close();
+
+ return 0;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kconf_update/kopete-nameTracking.upd b/kopete/kopete/kconf_update/kopete-nameTracking.upd
new file mode 100644
index 00000000..da0a0e2d
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-nameTracking.upd
@@ -0,0 +1,3 @@
+Id=kopete0.9/r1
+File=kopeterc
+Script=kopete-nameTracking-kconf_update
diff --git a/kopete/kopete/kconf_update/kopete-pluginloader.pl b/kopete/kopete/kconf_update/kopete-pluginloader.pl
new file mode 100755
index 00000000..19709d3a
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-pluginloader.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+my $logging = "false";
+my $moduleLine;
+
+while( my $line = <> )
+{
+ if( $line =~ /LogAll/ )
+ {
+ $logging = "true";
+ }
+ if( $line =~ /^Modules=/ )
+ {
+ $moduleLine = $line;
+ }
+}
+
+$moduleLine =~ s/^Modules/Plugins/;
+$moduleLine =~ s/\.plugin/\.desktop/g;
+$moduleLine =~ s/oscar/aim/;
+if ( $logging == "true" )
+{
+ chomp $moduleLine;
+ $moduleLine = $moduleLine . ",history.desktop\n";
+}
+print $moduleLine;
+
+print "# DELETE Modules\n";
+
diff --git a/kopete/kopete/kconf_update/kopete-pluginloader.upd b/kopete/kopete/kconf_update/kopete-pluginloader.upd
new file mode 100644
index 00000000..cc739414
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-pluginloader.upd
@@ -0,0 +1,4 @@
+Id=kopete0.7/r1
+File=kopeterc
+Script=kopete-pluginloader.pl,perl
+
diff --git a/kopete/kopete/kconf_update/kopete-pluginloader2.cpp b/kopete/kopete/kconf_update/kopete-pluginloader2.cpp
new file mode 100644
index 00000000..af8daa69
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-pluginloader2.cpp
@@ -0,0 +1,89 @@
+/*
+ kconf_update app for migrating the list of loaded plugins in
+ kopete 0.7.x to the new KPluginSelector format.
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtextstream.h>
+#include <qregexp.h>
+
+static QTextStream qcin ( stdin, IO_ReadOnly );
+static QTextStream qcout( stdout, IO_WriteOnly );
+static QTextStream qcerr( stderr, IO_WriteOnly );
+
+void parseKey( const QString &group, const QString &key, const QString &value, const QString &rawLine )
+{
+ //qcerr << "*** group='" << group << "'" << endl;
+ if ( group.isEmpty() && key == "Plugins" )
+ {
+ QStringList plugins = QStringList::split( ',', value );
+ if ( !plugins.isEmpty() )
+ {
+ qcout << "[Plugins]" << endl;
+ for ( QStringList::Iterator it = plugins.begin(); it != plugins.end(); ++it )
+ qcout << "kopete_" << ( *it ).remove( ".desktop" ) << "Enabled=true" << endl;
+ }
+ qcout << "# DELETE []Plugins" << endl;
+ }
+ else
+ {
+ // groups we don't convert. output the raw line instead.
+ qcout << rawLine << endl;
+ }
+}
+
+int main()
+{
+ qcin.setEncoding( QTextStream::UnicodeUTF8 );
+ qcout.setEncoding( QTextStream::UnicodeUTF8 );
+
+ QString curGroup;
+
+ QRegExp groupRegExp( "^\\[(.*)\\]" );
+ QRegExp keyRegExp( "^([a-zA-Z0-9:, _-]*)\\s*=\\s*(.*)\\s*" );
+ QRegExp commentRegExp( "^(#.*)?$" );
+
+ while ( !qcin.atEnd() )
+ {
+ QString line = qcin.readLine();
+
+ if ( commentRegExp.exactMatch( line ) )
+ {
+ // We found a comment, leave unchanged
+ qcout << line << endl;
+ }
+ else if ( groupRegExp.exactMatch( line ) )
+ {
+ // We found the start of a group, leave unchanged
+ qcout << line << endl;
+
+ curGroup = groupRegExp.capturedTexts()[ 1 ];
+ }
+ else if ( keyRegExp.exactMatch( line ) )
+ {
+ // We found the a key line
+ parseKey( curGroup, keyRegExp.capturedTexts()[ 1 ], keyRegExp.capturedTexts()[ 2 ], line );
+ }
+ else
+ {
+ qcerr << "** Unknown input line: " << line << endl;
+ }
+ }
+
+ return 0;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kconf_update/kopete-pluginloader2.sh b/kopete/kopete/kconf_update/kopete-pluginloader2.sh
new file mode 100644
index 00000000..e058dcf1
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-pluginloader2.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+IFS=:
+SUFF=kconf_update_bin/kopete-pluginloader2-kconf_update
+for path in `kde-config --path lib`; do
+ if test -x "$path/$SUFF"; then
+ exec "$path/$SUFF"
+ fi
+done
+
diff --git a/kopete/kopete/kconf_update/kopete-pluginloader2.upd b/kopete/kopete/kconf_update/kopete-pluginloader2.upd
new file mode 100644
index 00000000..3461c7a7
--- /dev/null
+++ b/kopete/kopete/kconf_update/kopete-pluginloader2.upd
@@ -0,0 +1,4 @@
+Id=kopete0.8/r1
+File=kopeterc
+Script=kopete-pluginloader2.sh,sh
+
diff --git a/kopete/kopete/kimiface.h b/kopete/kopete/kimiface.h
new file mode 100644
index 00000000..3d599013
--- /dev/null
+++ b/kopete/kopete/kimiface.h
@@ -0,0 +1,197 @@
+/*
+ kimiface.h - KDE Instant Messenger DCOP Interface
+
+ Copyright (c) 2004 Will Stephenson <lists@stevello.free-online.co.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KIMIFACE_H
+#define KIMIFACE_H
+
+#include <qpixmap.h>
+#include <dcopobject.h>
+#include <qstringlist.h>
+#include <kurl.h>
+
+/**
+ * Generic DCOP interface for KDE instant messenger applications
+ * Note one omission of this interface is the lack of control over the range of values used for protocols' names.
+ * @since 3.3
+ * @author Will Stephenson <lists@stevello.free-online.co.uk>
+ */
+class KIMIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+// ACCESSORS
+// contact list
+ /**
+ * Obtain a list of IM-contactable entries in the KDE
+ * address book.
+ * @return a list of KABC uids.
+ */
+ virtual QStringList allContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who are
+ * currently reachable.
+ * @return a list of KABC uids who can receive a message, even if online.
+ */
+ virtual QStringList reachableContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who are
+ * currently online.
+ * @return a list of KABC uids who are online with unspecified presence.
+ */
+ virtual QStringList onlineContacts() = 0;
+ /**
+ * Obtain a list of KDE address book entries who may
+ * receive file transfers.
+ * @return a list of KABC uids capable of file transfer.
+ */
+ virtual QStringList fileTransferContacts() = 0;
+
+// individual
+ /**
+ * Confirm if a given KABC uid is known to KIMProxy
+ * @param uid the KABC uid you are interested in.
+ * @return whether one of the chat programs KIMProxy talks to knows of this KABC uid.
+ */
+ virtual bool isPresent( const QString & uid ) = 0;
+ /**
+ * Obtain the IM app's idea of the contact's display name
+ * Useful if KABC lookups may be too slow
+ * @param KABC uid.
+ * @return The corresponding display name.
+ */
+ virtual QString displayName( const QString & uid ) = 0;
+ /**
+ * Obtain the IM presence as a i18ned string for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return the i18ned string describing presence.
+ */
+ virtual QString presenceString( const QString & uid ) = 0;
+ /**
+ * Obtain the IM presence as a number (see KIMIface) for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return a numeric representation of presence - currently one of 0 (Unknown), 1 (Offline), 2 (Connecting), 3 (Away), 4 (Online)
+ */
+ virtual int presenceStatus( const QString & uid ) = 0;
+ /**
+ * Indicate if a given uid can receive files
+ * @param uid the KABC uid you are interested in.
+ * @return Whether the specified addressee can receive files.
+ */
+ virtual bool canReceiveFiles( const QString & uid ) = 0;
+ /**
+ * Some media are unidirectional (eg, sending SMS via a web interface).
+ * @param uid the KABC uid you are interested in.
+ * @return Whether the specified addressee can respond.
+ */
+ virtual bool canRespond( const QString & uid ) = 0;
+ /**
+ * Get the KABC uid corresponding to the supplied IM address
+ * Protocols should be
+ * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC.
+ * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol",
+ * @return a KABC uid or null if none found/
+ */
+ virtual QString locate( const QString & contactId, const QString & protocol ) = 0;
+// metadata
+ /**
+ * Obtain the icon representing IM presence for the specified addressee
+ * @param uid the KABC uid you want the presence for.
+ * @return a pixmap representing the uid's presence.
+ */
+ virtual QPixmap icon( const QString & uid ) = 0;
+ /**
+ * Get the supplied addressee's current context (home, work, or any).
+ * @param uid the KABC uid you want the context for.
+ * @return A QString describing the context, or null if not supported.
+ */
+ virtual QString context( const QString & uid ) = 0;
+// App capabilities
+ /**
+ * Discover what protocols the application supports
+ * @return the set of protocols that the application supports
+ */
+ virtual QStringList protocols() = 0;
+
+// ACTORS
+ /**
+ * Send a single message to the specified addressee
+ * Any response will be handled by the IM client as a normal
+ * conversation.
+ * @param uid the KABC uid you want to chat with.
+ * @param message the message to send them.
+ */
+ virtual void messageContact( const QString &uid, const QString& message ) = 0;
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ virtual void messageNewContact( const QString &contactId, const QString &protocol ) = 0;
+
+ /**
+ * Start a chat session with the specified addressee
+ * @param uid the KABC uid you want to chat with.
+ */
+ virtual void chatWithContact( const QString &uid ) = 0;
+
+ /**
+ * Send the file to the contact
+ * @param uid the KABC uid you are sending to.
+ * @param sourceURL a @ref KURL to send.
+ * @param altFileName an alternate filename describing the file
+ * @param fileSize file size in bytes
+ */
+ virtual void sendFile(const QString &uid, const KURL &sourceURL,
+ const QString &altFileName = QString::null, uint fileSize = 0) = 0;
+
+// MUTATORS
+// Contact list
+ /**
+ * Add a contact to the contact list
+ * @param contactId the protocol specific identifier for the contact, eg UIN for ICQ, screenname for AIM, nick for IRC.
+ * @param protocol the protocol, eg one of "AIMProtocol", "MSNProtocol", "ICQProtocol", ...
+ * @return whether the add succeeded. False may signal already present, protocol not supported, or add operation not supported.
+ */
+ virtual bool addContact( const QString &contactId, const QString &protocol ) = 0;
+// SIGNALS
+k_dcop_signals:
+ /**
+ * Indicates that a contact's presence has changed
+ * @param uid the contact whose presence changed.
+ * @param appId the dcop application id of the program the signal originates from.
+ * @param presence the new numeric presence @ref presenceStatus
+ */
+ void contactPresenceChanged( QString uid, QCString appId, int presence );
+};
+
+#endif
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kimifaceimpl.cpp b/kopete/kopete/kimifaceimpl.cpp
new file mode 100644
index 00000000..dd1bd962
--- /dev/null
+++ b/kopete/kopete/kimifaceimpl.cpp
@@ -0,0 +1,395 @@
+/*
+ kimifaceimpl.cpp - Kopete DCOP Interface
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstringlist.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kplugininfo.h>
+#include <kabc/addressbook.h>
+#include <kabc/stdaddressbook.h>
+
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsession.h"
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopetepluginmanager.h"
+#include "kopeteuiglobal.h"
+#include "kopetecontact.h"
+
+#include <kdebug.h>
+
+#include "kimifaceimpl.h"
+
+KIMIfaceImpl::KIMIfaceImpl() : DCOPObject( "KIMIface" ), QObject()
+{
+ connect( Kopete::ContactList::self(),
+ SIGNAL( metaContactAdded( Kopete::MetaContact * ) ),
+ SLOT( slotMetaContactAdded( Kopete::MetaContact * ) ) );
+}
+
+KIMIfaceImpl::~KIMIfaceImpl()
+{
+}
+
+QStringList KIMIfaceImpl::allContacts()
+{
+ QStringList result;
+ QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( list );
+ for( ; it.current(); ++it )
+ {
+ if ( !it.current()->metaContactId().contains(':') )
+ result.append( it.current()->metaContactId() );
+ }
+
+ return result;
+}
+
+QStringList KIMIfaceImpl::reachableContacts()
+{
+ QStringList result;
+ QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( list );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isReachable() && !it.current()->metaContactId().contains(':') )
+ result.append( it.current()->metaContactId() );
+ }
+
+ return result;
+}
+
+QStringList KIMIfaceImpl::onlineContacts()
+{
+ QStringList result;
+ QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( list );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isOnline() && !it.current()->metaContactId().contains(':') )
+ result.append( it.current()->metaContactId() );
+ }
+
+ return result;
+}
+
+QStringList KIMIfaceImpl::fileTransferContacts()
+{
+ QStringList result;
+ QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( list );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->canAcceptFiles() && !it.current()->metaContactId().contains(':') )
+ result.append( it.current()->metaContactId() );
+ }
+
+ return result;
+}
+
+bool KIMIfaceImpl::isPresent( const QString & uid )
+{
+ Kopete::MetaContact *mc;
+ mc = Kopete::ContactList::self()->metaContact( uid );
+
+ return ( mc != 0 );
+}
+
+
+QString KIMIfaceImpl::displayName( const QString & uid )
+{
+ Kopete::MetaContact *mc;
+ mc = Kopete::ContactList::self()->metaContact( uid );
+ QString name;
+ if ( mc )
+ name = mc->displayName();
+
+ return name;
+}
+
+int KIMIfaceImpl::presenceStatus( const QString & uid )
+{
+ //kdDebug( 14000 ) << k_funcinfo << endl;
+ int p = -1;
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ if ( m )
+ {
+ Kopete::OnlineStatus status = m->status();
+ switch ( status.status() )
+ {
+ case Kopete::OnlineStatus::Unknown:
+ p = 0;
+ break;
+ case Kopete::OnlineStatus::Offline:
+ case Kopete::OnlineStatus::Invisible:
+ p = 1;
+ break;
+ case Kopete::OnlineStatus::Connecting:
+ p = 2;
+ break;
+ case Kopete::OnlineStatus::Away:
+ p = 3;
+ break;
+ case Kopete::OnlineStatus::Online:
+ p = 4;
+ break;
+ }
+ }
+ return p;
+}
+
+QString KIMIfaceImpl::presenceString( const QString & uid )
+{
+ //kdDebug( 14000 ) << "KIMIfaceImpl::presenceString" << endl;
+ QString p;
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ if ( m )
+ {
+ Kopete::OnlineStatus status = m->status();
+ p = status.description();
+ kdDebug( 14000 ) << "Got presence for " << uid << " : " << p.ascii() << endl;
+ }
+ else
+ {
+ kdDebug( 14000 ) << "Couldn't find MC: " << uid << endl;;
+ p = QString();
+ }
+ return p;
+}
+
+bool KIMIfaceImpl::canReceiveFiles( const QString & uid )
+{
+ Kopete::MetaContact *mc;
+ mc = Kopete::ContactList::self()->metaContact( uid );
+
+ if ( mc )
+ return mc->canAcceptFiles();
+ else
+ return false;
+}
+
+bool KIMIfaceImpl::canRespond( const QString & uid )
+{
+ Kopete::MetaContact *mc;
+ mc = Kopete::ContactList::self()->metaContact( uid );
+
+ if ( mc )
+ {
+ QPtrList<Kopete::Contact> list = mc->contacts();
+ QPtrListIterator<Kopete::Contact> it( list );
+ Kopete::Contact *contact;
+ while ( ( contact = it.current() ) != 0 )
+ {
+ ++it;
+ if ( contact->isOnline() && contact->protocol()->pluginId() != "SMSProtocol" )
+ return true;
+ }
+ }
+ return false;
+}
+
+QString KIMIfaceImpl::locate( const QString & contactId, const QString & protocolId )
+{
+ Kopete::MetaContact *mc = locateProtocolContact( contactId, protocolId );
+ if ( mc )
+ return mc->metaContactId();
+ else
+ return QString::null;
+}
+
+Kopete::MetaContact * KIMIfaceImpl::locateProtocolContact( const QString & contactId, const QString & protocolId )
+{
+ Kopete::MetaContact *mc = 0;
+ // find a matching protocol
+ Kopete::Protocol *protocol = dynamic_cast<Kopete::Protocol*>( Kopete::PluginManager::self()->plugin( protocolId ) );
+
+ if ( protocol )
+ {
+ // find its accounts
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( protocol );
+ QDictIterator<Kopete::Account> it( accounts );
+ for( ; it.current(); ++it )
+ {
+ Kopete::Contact *c = Kopete::ContactList::self()->findContact( protocolId, it.currentKey(), contactId );
+ if (c)
+ {
+ mc=c->metaContact();
+ break;
+ }
+ }
+ }
+ return mc;
+}
+
+QPixmap KIMIfaceImpl::icon( const QString & uid )
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ QPixmap p;
+ if ( m )
+ p = SmallIcon( m->statusIcon() );
+ return p;
+}
+
+QString KIMIfaceImpl::context( const QString & uid )
+{
+ // TODO: support context
+ // shush warning
+ QString myUid = uid;
+
+ return QString::null;
+}
+
+QStringList KIMIfaceImpl::protocols()
+{
+ QValueList<KPluginInfo *> protocols = Kopete::PluginManager::self()->availablePlugins( "Protocols" );
+ QStringList protocolList;
+ for ( QValueList<KPluginInfo *>::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ protocolList.append( (*it)->name() );
+
+ return protocolList;
+}
+
+void KIMIfaceImpl::messageContact( const QString &uid, const QString& messageText )
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ if ( m )
+ {
+ Kopete::Contact * c = m->preferredContact();
+ Kopete::ChatSession * manager = c->manager(Kopete::Contact::CanCreate);
+ c->manager( Kopete::Contact::CanCreate )->view( true );
+ Kopete::Message msg = Kopete::Message( manager->myself(), manager->members(), messageText,
+ Kopete::Message::Outbound, Kopete::Message::PlainText);
+ manager->sendMessage( msg );
+ }
+ else
+ unknown( uid );
+}
+
+void KIMIfaceImpl::messageNewContact( const QString &contactId, const QString &protocol )
+{
+ Kopete::MetaContact *mc = locateProtocolContact( contactId, protocol );
+ if ( mc )
+ mc->sendMessage();
+}
+
+void KIMIfaceImpl::chatWithContact( const QString &uid )
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ if ( m )
+ m->execute();
+ else
+ unknown( uid );
+}
+
+void KIMIfaceImpl::sendFile(const QString &uid, const KURL &sourceURL,
+ const QString &altFileName, uint fileSize)
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( uid );
+ if ( m )
+ m->sendFile( sourceURL, altFileName, fileSize );
+ // else, prompt to create a new MC associated with UID
+}
+
+bool KIMIfaceImpl::addContact( const QString &contactId, const QString &protocolId )
+{
+ // find a matching protocol
+ Kopete::Protocol *protocol = dynamic_cast<Kopete::Protocol*>( Kopete::PluginManager::self()->plugin( protocolId ) );
+
+ if ( protocol )
+ {
+ // find its accounts
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( protocol );
+ QDictIterator<Kopete::Account> it( accounts );
+ Kopete::Account *ac = it.toFirst();
+ if ( ac )
+ {
+ ac->addContact( contactId );
+ return true;
+ }
+ }
+ return false;
+}
+
+void KIMIfaceImpl::slotMetaContactAdded( Kopete::MetaContact *mc )
+{
+ connect( mc, SIGNAL( onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType ) ),
+ SLOT( slotContactStatusChanged( Kopete::MetaContact * ) ) );
+}
+
+void KIMIfaceImpl::slotContactStatusChanged( Kopete::MetaContact *mc )
+{
+ if ( !mc->metaContactId().contains( ':' ) )
+ {
+ int p = -1;
+ Kopete::OnlineStatus status = mc->status();
+ switch ( status.status() )
+ {
+ case Kopete::OnlineStatus::Unknown:
+ p = 0;
+ break;
+ case Kopete::OnlineStatus::Offline:
+ case Kopete::OnlineStatus::Invisible:
+ p = 1;
+ break;
+ case Kopete::OnlineStatus::Connecting:
+ p = 2;
+ break;
+ case Kopete::OnlineStatus::Away:
+ p = 3;
+ break;
+ case Kopete::OnlineStatus::Online:
+ p = 4;
+ break;
+ }
+ // tell anyone who's listening over DCOP
+ contactPresenceChanged( mc->metaContactId(), kapp->name(), p );
+/* QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << mc->metaContactId();
+ stream << kapp->name();
+ stream << p;
+ kapp->dcopClient()->emitDCOPSignal( "contactPresenceChanged( QString, QCString, int )", params );*/
+ }
+}
+
+void KIMIfaceImpl::unknown( const QString &uid )
+{
+ // warn the user that the KABC contact associated with this UID isn't known to kopete,
+ // either associate an existing contact with KABC or add a new one using the ACW.
+ KABC::AddressBook *bk = KABC::StdAddressBook::self( false );
+ KABC::Addressee addr = bk->findByUid( uid );
+ if ( addr.isEmpty() )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, i18n("Another KDE application tried to use Kopete for instant messaging, but Kopete could not find the specified contact in the KDE address book."), i18n( "Not Found in Address Book" ) );
+ }
+ else
+ {
+ QString apology = i18n( "Translators: %1 is the name of a person taken from the KDE address book, who Kopete doesn't know about. Kopete must either be told that an existing contact in Kopete is this person, or add a new contact for them",
+ "<qt><p>The KDE Address Book has no instant messaging information for</p><p><b>%1</b>.</p><p>If he/she is already present in the Kopete contact list, indicate the correct addressbook entry in their properties.</p><p>Otherwise, add a new contact using the Add Contact wizard.</p></qt>" );
+ apology = apology.arg( addr.realName() );
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, apology, i18n( "No Instant Messaging Address" ) );
+ }
+}
+
+#include "kimifaceimpl.moc"
+
diff --git a/kopete/kopete/kimifaceimpl.h b/kopete/kopete/kimifaceimpl.h
new file mode 100644
index 00000000..ff8c3611
--- /dev/null
+++ b/kopete/kopete/kimifaceimpl.h
@@ -0,0 +1,108 @@
+/*
+ kimifaceimpl.cpp - Kopete DCOP Interface
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef KIMIFACEIMPL_H
+#define KIMIFACEIMPL_H
+
+#include <qobject.h>
+#include "kimiface.h"
+
+namespace Kopete
+{
+class MetaContact;
+}
+
+class KIMIfaceImpl : public QObject, public KIMIface
+{
+ Q_OBJECT
+public:
+ KIMIfaceImpl();
+ ~KIMIfaceImpl();
+
+ QStringList allContacts();
+ QStringList reachableContacts();
+ QStringList onlineContacts();
+ QStringList fileTransferContacts();
+
+// individual
+ bool isPresent( const QString &uid );
+ QString displayName( const QString &uid );
+ QString presenceString( const QString &uid );
+ int presenceStatus( const QString &uid );
+ bool canReceiveFiles( const QString &uid );
+ bool canRespond( const QString &uid );
+ QString locate( const QString &contactId, const QString &protocol );
+// metadata
+ QPixmap icon( const QString &uid );
+ QString context( const QString &uid );
+// App capabilities
+ QStringList protocols();
+
+// ACTORS
+ /**
+ * Message a contact by their metaContactId, aka their uid in KABC.
+ */
+ void messageContact( const QString &uid, const QString& message );
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ void messageNewContact( const QString &contactId, const QString &protocolId );
+
+ /**
+ * Message a contact by their metaContactId, aka their uid in KABC.
+ */
+ void chatWithContact( const QString &uid );
+
+ /**
+ * Send the file to the contact
+ */
+ void sendFile(const QString &uid, const KURL &sourceURL,
+ const QString &altFileName = QString::null, uint fileSize = 0);
+
+// MUTATORS
+// Contact list
+ bool addContact( const QString &contactId, const QString &protocolId );
+// SIGNALS
+ /**
+ * DCOP Signal used to notify
+ * external apps of status changes.
+ */
+ void contactStatusChanged( const QString &uid);
+
+protected:
+ void unknown( const QString &uid );
+protected slots:
+ void slotMetaContactAdded( Kopete::MetaContact *mc );
+ void slotContactStatusChanged( Kopete::MetaContact *mc );
+
+private:
+ Kopete::MetaContact *locateProtocolContact( const QString & contactId, const QString & protocolId );
+};
+
+#endif
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopete.desktop b/kopete/kopete/kopete.desktop
new file mode 100644
index 00000000..eabdae57
--- /dev/null
+++ b/kopete/kopete/kopete.desktop
@@ -0,0 +1,126 @@
+[Desktop Entry]
+MimeType=application/x-kopete-emoticons;application/x-icq;
+Name=Kopete
+Name[bn]=কপেট
+Name[hi]=के-ऑपà¥à¤Ÿà¥€
+Name[ne]=कोपेट
+Name[pa]=ਕੋਪੀਟੀ
+GenericName=Instant Messenger
+GenericName[ar]=المرسال الÙوري
+GenericName[be]=Праграма імгненных паведамленнÑÑž
+GenericName[bg]=Ð¡ÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² реално време
+GenericName[bn]=তাতà§à¦•à§à¦·à¦£à¦¿à¦• বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•
+GenericName[br]=Posteler a-benn-kaer
+GenericName[bs]=Instant poruke
+GenericName[ca]=Missatger a l'instant
+GenericName[cs]=Komunikátor
+GenericName[cy]=Negesydd Chwim
+GenericName[el]=Στιγμιαίος αποστολέας μηνυμάτων
+GenericName[eo]=RapidmesaÄilo
+GenericName[es]=Mensajería instantánea
+GenericName[et]=Kiirsuhtlemisrakendus
+GenericName[eu]=Berehalako mezularitza
+GenericName[fa]=پیام‌رسان Ùوری
+GenericName[fi]=Pikaviestinohjelma
+GenericName[fr]=Messagerie instantanée
+GenericName[ga]=Clár teachtaireachtaí meandaracha
+GenericName[gl]=Mensaxería Instantánea
+GenericName[he]=תוכנת ×ž×¡×¨×™× ×ž×™×“×™×™×
+GenericName[hi]=इंसटैंट मैसेंजर
+GenericName[hr]=Instant poruke
+GenericName[hu]=Internetes csevegő
+GenericName[is]=Spjallforrit
+GenericName[it]=Messaggistica istantanea
+GenericName[ja]=インスタントメッセンジャー
+GenericName[kk]=Жедел хабарлаÑу
+GenericName[km]=កម្មវិធី​ផ្ញើ​សារ​បន្ទាន់
+GenericName[lt]=Momentinių žinuÄių klientas
+GenericName[mk]=ИнÑтант глаÑник
+GenericName[nb]=Hurtigmelding
+GenericName[nds]=Kortnarichtenprogramm
+GenericName[ne]=ततà¥à¤•à¤¾à¤² मेसेनà¥à¤œà¤°
+GenericName[nl]=Instant messenger
+GenericName[nn]=Lynmeldingsprogram
+GenericName[pa]=ਮੌਕਾ ਸà©à¨¨à©‡à¨¹à¨¾à¨•à¨¾à¨°
+GenericName[pl]=Komunikator internetowy
+GenericName[pt]=Mensageiro Instantâneo
+GenericName[pt_BR]=Mensageiro Instantâneo
+GenericName[ru]=Программа обмена ÑообщениÑми
+GenericName[rw]=Intumwa y'Akokanya
+GenericName[se]=Šleađgadiehtoprográmma
+GenericName[sl]=TakojÅ¡ni sporoÄilnik
+GenericName[sr]=Брзи глаÑник
+GenericName[sr@Latn]=Brzi glasnik
+GenericName[sv]=Direktmeddelandeklient
+GenericName[ta]=உடனடி தூதரà¯
+GenericName[tg]=Пайёмбари Фаврӣ
+GenericName[tr]=Anında Haberleşme Hizmeti
+GenericName[uk]=Програма Ð´Ð»Ñ Ð¼Ð¸Ñ‚Ñ‚Ñ”Ð²Ð¾Ð³Ð¾ зв'Ñзку
+GenericName[uz]=Xabar almashish vositasi
+GenericName[uz@cyrillic]=Хабар алмашиш воÑитаÑи
+GenericName[zh_CN]=å³æ—¶é€šè®¯å®¢æˆ·ç¨‹åº
+GenericName[zh_HK]=å³æ™‚通訊程å¼
+GenericName[zh_TW]=å³æ™‚訊æ¯å®¢æˆ¶ç«¯ç¨‹å¼
+Comment=Instant Messenger
+Comment[ar]=المرسال الÙوري
+Comment[be]=Праграма імгненных паведамленнÑÑž
+Comment[bg]=Ð¡ÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð² реално време
+Comment[bn]=তাতà§à¦•à§à¦·à¦£à¦¿à¦• বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•
+Comment[br]=Posteler a-benn-kaer
+Comment[bs]=Instant poruke
+Comment[ca]=Missatger a l'instant
+Comment[cs]=Komunikátor
+Comment[cy]=Negesydd Chwim
+Comment[el]=Στιγμιαίος αποστολέας μηνυμάτων
+Comment[eo]=RapidmesaÄilo
+Comment[es]=Mensajería instantánea
+Comment[et]=Kiirsuhtlusrakendus
+Comment[eu]=Berehalako mezularitza
+Comment[fa]=پیام‌رسان Ùوری
+Comment[fi]=Pikaviestinohjelma
+Comment[fr]=Messagerie instantanée
+Comment[gl]=Mensaxería Instantánea
+Comment[he]=תוכנת ×ž×¡×¨×™× ×ž×™×“×™×™×
+Comment[hi]=इंसà¥à¤Ÿà¥ˆà¤‚ट मैसेंजर
+Comment[hr]=Instant poruke
+Comment[hu]=Azonnali üzenetküldő
+Comment[is]=Spjallforrit
+Comment[it]=Messaggistica istantanea
+Comment[ja]=インスタントメッセンジャー
+Comment[kk]=Жедел хабарлаÑу бағдарламаÑÑ‹
+Comment[km]=កម្មវិធី​ផ្ញើ​សារ​បន្ទាន់
+Comment[lt]=Momentinių žinuÄių klientas
+Comment[mk]=ИнÑтант глаÑник
+Comment[nb]=hurtigmeldingssystem
+Comment[nds]=Kortnarichtenprogramm
+Comment[ne]=ततà¥à¤•à¤¾à¤² मेसेनà¥à¤œà¤°
+Comment[nl]=Instant messenger
+Comment[nn]=Lynmeldingsprogram
+Comment[pl]=Komunikator
+Comment[pt]=Mensageiro Instantâneo
+Comment[pt_BR]=Mensageiro Instantâneo
+Comment[ro]=Mesaje instantanee
+Comment[ru]=Программа обмена ÑообщениÑми
+Comment[se]=Instant Messenger-klienta
+Comment[sl]=TakojÅ¡ni sporoÄilnik
+Comment[sr]=Брзи глаÑник
+Comment[sr@Latn]=Brzi glasnik
+Comment[sv]=Direktmeddelandeklient
+Comment[ta]=உடனடி தூதரà¯
+Comment[tg]=Пайёмбари Фаврӣ
+Comment[tr]=Anında Haberleşme Hizmeti
+Comment[uk]=Програма Ð´Ð»Ñ Ð¼Ð¸Ñ‚Ñ‚Ñ”Ð²Ð¾Ð³Ð¾ зв'Ñзку
+Comment[uz]=Xabar almashish vositasi
+Comment[uz@cyrillic]=Хабар алмашиш воÑитаÑи
+Comment[zh_CN]=å³æ—¶é€šè®¯å®¢æˆ·ç¨‹åº
+Comment[zh_HK]=å³æ™‚通訊程å¼
+Comment[zh_TW]=å³æ™‚訊æ¯å®¢æˆ¶ç«¯ç¨‹å¼
+Exec=kopete -caption "%c" %i %m %u
+Type=Application
+DocPath=kopete/index.html
+Terminal=false
+Icon=kopete
+Categories=Qt;KDE;Network;InstantMessaging;
+X-DCOP-ServiceType=Unique
+X-DCOP-ServiceName=kopete
+ServiceTypes=DCOP/InstantMessenger
diff --git a/kopete/kopete/kopeteaccountstatusbaricon.cpp b/kopete/kopete/kopeteaccountstatusbaricon.cpp
new file mode 100644
index 00000000..05855b63
--- /dev/null
+++ b/kopete/kopete/kopeteaccountstatusbaricon.cpp
@@ -0,0 +1,60 @@
+/*
+ kopeteaccountstatusbaricon.cpp - Kopete Account StatusBar Dock Icon
+
+ Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteaccountstatusbaricon.h"
+#include "qcursor.h"
+
+#include <kdebug.h>
+
+//#include <kdebug.h>
+
+KopeteAccountStatusBarIcon::KopeteAccountStatusBarIcon( Kopete::Account *acc, QWidget *parent,
+ const char *name )
+: QLabel( parent, name )
+{
+// kdDebug(14000) << "[KopeteAccountStatusBarIcon] Setting Initial Protocol Icon" << endl;
+ //setMask(initialPixmap->mask());
+ //setPixmap( Kopete::OnlineStatus( Kopete::OnlineStatus::Unknown, 0, proto, 0, "status_unknown", QString::null, QString::null ).protocolIcon() );
+ //setPixmap( proto->status().protocolIcon() );
+
+ setFixedSize ( 16, 16 );
+ setCursor(QCursor(Qt::PointingHandCursor));
+ show();
+
+ m_account = acc;
+}
+
+KopeteAccountStatusBarIcon::~KopeteAccountStatusBarIcon()
+{
+}
+
+void KopeteAccountStatusBarIcon::mousePressEvent( QMouseEvent *me )
+{
+ if( me->button() == QEvent::RightButton )
+ {
+ emit rightClicked( m_account, QPoint( me->globalX(), me->globalY() ) );
+ }
+ else if( me->button() == QEvent::LeftButton )
+ {
+ emit leftClicked( m_account, QPoint( me->globalX(), me->globalY() ) );
+ }
+}
+
+#include "kopeteaccountstatusbaricon.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteaccountstatusbaricon.h b/kopete/kopete/kopeteaccountstatusbaricon.h
new file mode 100644
index 00000000..7c3034c9
--- /dev/null
+++ b/kopete/kopete/kopeteaccountstatusbaricon.h
@@ -0,0 +1,60 @@
+/*
+ kopeteaccounrstatusbaricon.h - Kopete Account StatusBar Dock Icon
+
+ Copyright (c) 2001-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEACCOUNTSTATUSBARICON_H
+#define KOPETEACCOUNTSTATUSBARICON_H
+
+#include <qevent.h>
+#include <qlabel.h>
+#include <qpoint.h>
+
+namespace Kopete
+{
+class Account;
+}
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ */
+class KopeteAccountStatusBarIcon : public QLabel
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a statusbar icon.
+ */
+ KopeteAccountStatusBarIcon( Kopete::Account *acc, QWidget *parent,
+ const char *name = 0 );
+
+ ~KopeteAccountStatusBarIcon();
+
+signals:
+ void rightClicked( Kopete::Account *acc, const QPoint &p );
+ void leftClicked( Kopete::Account *acc, const QPoint &p );
+
+protected:
+ virtual void mousePressEvent( QMouseEvent *me );
+
+private:
+ Kopete::Account *m_account;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteapplication.cpp b/kopete/kopete/kopeteapplication.cpp
new file mode 100644
index 00000000..3a481d3f
--- /dev/null
+++ b/kopete/kopete/kopeteapplication.cpp
@@ -0,0 +1,338 @@
+/*
+ kopete.cpp
+
+ Kopete Instant Messenger Main Class
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteapplication.h"
+
+#include <qtimer.h>
+#include <qregexp.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kmessagebox.h>
+
+#include "addaccountwizard.h"
+#include "kabcpersistence.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecommandhandler.h"
+#include "kopetecontactlist.h"
+#include "kopeteglobal.h"
+#include "kopetemimesourcefactory.h"
+#include "kopetemimetypehandler.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprotocol.h"
+#include "kopetestdaction.h"
+#include "kopeteuiglobal.h"
+#include "kopetewindow.h"
+#include "kopeteprefs.h"
+#include "kopeteviewmanager.h"
+#include "videodevice.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+KopeteApplication::KopeteApplication()
+: KUniqueApplication( true, true, true )
+{
+ m_isShuttingDown = false;
+ m_mainWindow = new KopeteWindow( 0, "mainWindow" );
+
+ Kopete::PluginManager::self();
+
+ Kopete::UI::Global::setMainWidget( m_mainWindow );
+
+ /*
+ * FIXME: This is a workaround for a quite odd problem:
+ * When starting up kopete and the msn plugin gets loaded it can bring up
+ * a messagebox, in case the msg configuration is missing. This messagebox
+ * will result in a QApplication::enter_loop() call, an event loop is
+ * created. At this point however the loop_level is 0, because this is all
+ * still inside the KopeteApplication constructor, before the exec() call from main.
+ * When the messagebox is finished the loop_level will drop down to zero and
+ * QApplication thinks the application shuts down (this is usually the case
+ * when the loop_level goes down to zero) . So it emits aboutToQuit(), to
+ * which KApplication is connected and re-emits shutdown() , to which again
+ * KMainWindow (a KopeteWindow instance exists already) is connected. KMainWindow's
+ * shuttingDown() slot calls queryExit() which results in KopeteWindow::queryExit()
+ * calling unloadPlugins() . This of course is wrong and just shouldn't happen.
+ * The workaround is to simply delay the initialization of all this to a point
+ * where the loop_level is already > 0 . That is why I moved all the code from
+ * the constructor to the initialize() method and added this single-shot-timer
+ * setup. (Simon)
+ *
+ * Additionally, it makes the GUI appear less 'blocking' during startup, so
+ * there is a secondary benefit as well here. (Martijn)
+ */
+ QTimer::singleShot( 0, this, SLOT( slotLoadPlugins() ) );
+
+ m_mimeFactory = new Kopete::MimeSourceFactory;
+ QMimeSourceFactory::addFactory( m_mimeFactory );
+
+ //Create the emoticon installer
+ m_emoticonHandler = new Kopete::EmoticonMimeTypeHandler;
+}
+
+KopeteApplication::~KopeteApplication()
+{
+ kdDebug( 14000 ) << k_funcinfo << endl;
+
+ delete m_mainWindow;
+ delete m_emoticonHandler;
+ delete m_mimeFactory;
+ //kdDebug( 14000 ) << k_funcinfo << "Done" << endl;
+}
+
+void KopeteApplication::slotLoadPlugins()
+{
+ // we have to load the address book early, because calling this enters the Qt event loop when there are remote resources.
+ // The plugin manager is written with the assumption that Kopete will not reenter the event loop during plugin load,
+ // otherwise lots of things break as plugins are loaded, then contacts are added to incompletely initialised MCLVIs
+ Kopete::KABCPersistence::self()->addressBook();
+
+ //Create the command handler (looks silly)
+ Kopete::CommandHandler::commandHandler();
+
+ //Create the view manager
+ KopeteViewManager::viewManager();
+
+ Kopete::AccountManager::self()->load();
+ Kopete::ContactList::self()->load();
+
+ KConfig *config = KGlobal::config();
+
+ // Parse command-line arguments
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ bool showConfigDialog = false;
+
+ config->setGroup( "Plugins" );
+
+ /* FIXME: This is crap, if something purged that groups but your accounts
+ * are still working kopete will load the necessary plugins but still show the
+ * stupid accounts dialog (of course empty at that time because account data
+ * gets loaded later on). [mETz - 29.05.2004]
+ */
+ if ( !config->hasGroup( "Plugins" ) )
+ showConfigDialog = true;
+
+ // Listen to arguments
+ /*
+ // TODO: conflicts with emoticon installer and the general meaning
+ // of %U in kopete.desktop
+ if ( args->count() > 0 )
+ {
+ showConfigDialog = false;
+ for ( int i = 0; i < args->count(); i++ )
+ Kopete::PluginManager::self()->setPluginEnabled( args->arg( i ), true );
+ }
+ */
+
+ // Prevent plugins from loading? (--disable=foo,bar)
+ QStringList disableArgs = QStringList::split( ',', args->getOption( "disable" ) );
+ for ( QStringList::ConstIterator it = disableArgs.begin(); it != disableArgs.end(); ++it )
+ {
+ showConfigDialog = false;
+ Kopete::PluginManager::self()->setPluginEnabled( *it, false );
+ }
+
+ // Load some plugins exclusively? (--load-plugins=foo,bar)
+ if ( args->isSet( "load-plugins" ) )
+ {
+ config->deleteGroup( "Plugins", true );
+ showConfigDialog = false;
+ QStringList plugins = QStringList::split( ',', args->getOption( "load-plugins" ) );
+ for ( QStringList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it )
+ Kopete::PluginManager::self()->setPluginEnabled( *it, true );
+ }
+
+ config->sync();
+
+ // Disable plugins altogether? (--noplugins)
+ if ( !args->isSet( "plugins" ) )
+ {
+ // If anybody reenables this I'll get a sword and make a nice chop-suy out
+ // of your body :P [mETz - 29.05.2004]
+ // This screws up kopeterc because there is no way to get the Plugins group back!
+ //config->deleteGroup( "Plugins", true );
+
+ showConfigDialog = false;
+ // pretend all plugins were loaded :)
+ QTimer::singleShot(0, this, SLOT( slotAllPluginsLoaded() ));
+ }
+ else
+ {
+ Kopete::PluginManager::self()->loadAllPlugins();
+ }
+
+ connect( Kopete::PluginManager::self(), SIGNAL( allPluginsLoaded() ),
+ this, SLOT( slotAllPluginsLoaded() ));
+
+ if( showConfigDialog )
+ {
+ // No plugins specified. Show the config dialog.
+ // FIXME: Although it's a bit stupid it is theoretically possible that a user
+ // explicitly configured Kopete to not load plugins on startup. In this
+ // case we don't want this dialog. We need some other config setting
+ // like a bool hasRunKopeteBefore or so to trigger the loading of the
+ // wizard. Maybe using the last run version number is more useful even
+ // as it also allows for other features. - Martijn
+ // FIXME: Possibly we need to influence the showConfigDialog bool based on the
+ // command line arguments processed below. But how exactly? - Martijn
+ // NB: the command line args are completely broken atm.
+ // I don't want to fix them for 3.5 as plugin loading will change for KDE4. - Will
+ AddAccountWizard *m_addwizard = new AddAccountWizard( Kopete::UI::Global::mainWidget(), "addAccountWizard", true, true );
+ m_addwizard->exec();
+ Kopete::AccountManager::self()->save();
+ }
+}
+
+void KopeteApplication::slotAllPluginsLoaded()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ // --noconnect not specified?
+ if ( args->isSet( "connect" ) && KopetePrefs::prefs()->autoConnect() )
+ Kopete::AccountManager::self()->connectAll();
+
+
+ // Handle things like '--autoconnect foo,bar --autoconnect foobar'
+ QCStringList connectArgsC = args->getOptionList( "autoconnect" );
+ QStringList connectArgs;
+
+ for ( QCStringList::ConstIterator it = connectArgsC.begin(); it != connectArgsC.end(); ++it )
+ {
+ QStringList split = QStringList::split( ',', QString::fromLatin1( *it ) );
+
+ for ( QStringList::ConstIterator it2 = split.begin(); it2 != split.end(); ++it2 )
+ {
+ connectArgs.append( *it2 );
+ }
+ }
+
+ for ( QStringList::ConstIterator i = connectArgs.begin(); i != connectArgs.end(); ++i )
+ {
+ QRegExp rx( QString::fromLatin1( "([^\\|]*)\\|\\|(.*)" ) );
+ rx.search( *i );
+ QString protocolId = rx.cap( 1 );
+ QString accountId = rx.cap( 2 );
+
+ if ( accountId.isEmpty() )
+ {
+ if ( protocolId.isEmpty() )
+ accountId = *i;
+ else
+ continue;
+ }
+
+ QPtrListIterator<Kopete::Account> it( Kopete::AccountManager::self()->accounts() );
+ Kopete::Account *account;
+ while ( ( account = it.current() ) != 0 )
+ {
+ ++it;
+
+ if ( ( account->accountId() == accountId ) )
+ {
+ if ( protocolId.isEmpty() || account->protocol()->pluginId() == protocolId )
+ {
+ account->connect();
+ break;
+ }
+ }
+ }
+ }
+
+ // Parse any passed URLs/files
+ handleURLArgs();
+}
+
+int KopeteApplication::newInstance()
+{
+// kdDebug(14000) << k_funcinfo << endl;
+ handleURLArgs();
+
+ /**
+ * The following three lines work around a problem that
+ * Kopete has since it has multiple main windows so
+ * qapp->mainWidget() returns 0, which breaks the normal
+ * KUniqueApplication::newInstance() behavior that raises
+ * the window when we run a new instance.
+ *
+ * 1. Set the main widget to the contact list window
+ * 2. Call KUniqueApplication::newInstance()
+ * 3. Set the main widget back to 0
+ *
+ * This little workaround fixes the problem. -Matt
+ */
+
+ setMainWidget( m_mainWindow );
+ int kUniqAppReturnCode = KUniqueApplication::newInstance();
+ setMainWidget( 0L );
+
+ return kUniqAppReturnCode;
+}
+
+void KopeteApplication::handleURLArgs()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+// kdDebug(14000) << k_funcinfo << "called with " << args->count() << " arguments to handle." << endl;
+
+ if ( args->count() > 0 )
+ {
+ for ( int i = 0; i < args->count(); i++ )
+ {
+ KURL u( args->url( i ) );
+ if ( !u.isValid() )
+ continue;
+
+ Kopete::MimeTypeHandler::dispatchURL( u );
+ } // END for()
+ } // END args->count() > 0
+}
+
+void KopeteApplication::quitKopete()
+{
+ kdDebug( 14000 ) << k_funcinfo << endl;
+
+ m_isShuttingDown = true;
+
+ // close all windows
+ QPtrListIterator<KMainWindow> it(*KMainWindow::memberList);
+ for (it.toFirst(); it.current(); ++it)
+ {
+ if ( !it.current()->close() )
+ {
+ m_isShuttingDown = false;
+ break;
+ }
+ }
+}
+
+
+void KopeteApplication::commitData( QSessionManager &sm )
+{
+ m_isShuttingDown = true;
+ KUniqueApplication::commitData( sm );
+}
+
+#include "kopeteapplication.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/kopeteapplication.h b/kopete/kopete/kopeteapplication.h
new file mode 100644
index 00000000..9634adca
--- /dev/null
+++ b/kopete/kopete/kopeteapplication.h
@@ -0,0 +1,94 @@
+/*
+ kopete.h
+
+ Kopete Instant Messenger Main Class
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEAPPLICATION_H
+#define KOPETEAPPLICATION_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <qguardedptr.h>
+
+#include <kuniqueapplication.h>
+
+class KopeteWindow;
+class QSessionManager;
+class QMimeSourceFactory;
+
+namespace Kopete
+{
+ class MimeTypeHandler;
+}
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ */
+class KopeteApplication : public KUniqueApplication
+{
+ Q_OBJECT
+
+public:
+ KopeteApplication();
+ ~KopeteApplication();
+
+ /**
+ * Method to return whether or not we're shutting down
+ * or not at this point.
+ */
+ bool isShuttingDown() const { return m_isShuttingDown; }
+
+ virtual int newInstance();
+
+public slots:
+ /**
+ * Quit Kopete, closing all the windows, which causes application shutdown
+ * This method marks Kopete as 'shutting down' to avoid
+ * showing the message box that Kopete will be left running in the
+ * system tray before calling qApp->quit().
+ */
+ void quitKopete();
+
+ virtual void commitData( QSessionManager &sm );
+ /**
+ * Load all plugins
+ */
+ void slotLoadPlugins();
+
+private slots:
+ /**
+ * auto-connect
+ */
+ void slotAllPluginsLoaded();
+private:
+ // The main window might get deleted behind our back (W_DestructiveClose),
+ // so use a guarded pointer
+ QGuardedPtr<KopeteWindow> m_mainWindow;
+ bool m_isShuttingDown;
+ Kopete::MimeTypeHandler *m_emoticonHandler;
+ QMimeSourceFactory *m_mimeFactory;
+
+private:
+ void handleURLArgs();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteballoon.cpp b/kopete/kopete/kopeteballoon.cpp
new file mode 100644
index 00000000..cee42240
--- /dev/null
+++ b/kopete/kopete/kopeteballoon.cpp
@@ -0,0 +1,186 @@
+/*
+ kopeteballoon.cpp - Nice Balloon
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code based on Kim Applet code
+ Copyright (c) 2000-2002 by Malte Starostik <malte@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qpointarray.h>
+#include <qpushbutton.h>
+#include <qtooltip.h>
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <kdeversion.h>
+#include <kglobalsettings.h>
+
+#include <kapplication.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kprotocolinfo.h>
+#include <kurl.h>
+#include <krun.h>
+
+#include "kopeteballoon.h"
+#include "systemtray.h"
+#include "kopeteprefs.h"
+
+KopeteActiveLabel::KopeteActiveLabel( QWidget *parent, const char *name )
+ : KActiveLabel( parent, name )
+{
+}
+
+KopeteActiveLabel::KopeteActiveLabel( const QString& text, QWidget *parent,
+ const char *name ) : KActiveLabel( text, parent, name )
+{
+}
+
+void KopeteActiveLabel::openLink( const QString& link )
+{
+ KURL url( link );
+ QString protocol = url.protocol();
+
+ if ( protocol == "mailto" )
+ kapp->invokeMailer(url);
+ else
+ {
+ if ( KProtocolInfo::protocolClass( protocol ) == ":internet" ) // http, ftp, etc.
+ new KRun( url, this );
+ }
+}
+
+KopeteBalloon::KopeteBalloon(const QString &text, const QString &pix)
+: QWidget(0L, "KopeteBalloon", WStyle_StaysOnTop | WStyle_Customize |
+ WStyle_NoBorder | WStyle_Tool | WX11BypassWM)
+{
+ setCaption("");
+
+ QVBoxLayout *BalloonLayout = new QVBoxLayout(this, 22,
+ KDialog::spacingHint(), "BalloonLayout");
+
+ // BEGIN Layout1
+ QHBoxLayout *Layout1 = new QHBoxLayout(BalloonLayout,
+ KDialog::spacingHint(), "Layout1");
+ //QLabel *mCaption = new QLabel(text, this, "mCaption");
+ KopeteActiveLabel *mCaption = new KopeteActiveLabel(text, this, "mCaption");
+ mCaption->setPalette(QToolTip::palette());
+ mCaption->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
+
+ if (!pix.isEmpty())
+ {
+ QLabel *mImage = new QLabel(this, "mImage");
+ mImage->setScaledContents(FALSE);
+ mImage->setPixmap(locate("data", pix));
+
+ Layout1->addWidget(mImage);
+ }
+ Layout1->addWidget(mCaption);
+ // END Layout1
+
+
+ // BEGIN Layout2
+ QHBoxLayout *Layout2 = new QHBoxLayout(BalloonLayout,
+ KDialog::spacingHint(), "Layout2");
+ QPushButton *mViewButton = new QPushButton(i18n("to view", "View"), this,
+ "mViewButton");
+ QPushButton *mIgnoreButton = new QPushButton(i18n("Ignore"), this,
+ "mIgnoreButton");
+
+ Layout2->addStretch();
+ Layout2->addWidget(mViewButton);
+ Layout2->addWidget(mIgnoreButton);
+ Layout2->addStretch();
+ // END Layout2
+
+ setPalette(QToolTip::palette());
+ setAutoMask(TRUE);
+
+ connect(mViewButton, SIGNAL(clicked()),
+ this, SIGNAL(signalButtonClicked()));
+ connect(mViewButton, SIGNAL(clicked()),
+ this, SLOT(deleteLater()));
+ connect(mIgnoreButton, SIGNAL(clicked()),
+ this, SIGNAL(signalIgnoreButtonClicked()));
+ connect(mIgnoreButton, SIGNAL(clicked()),
+ this, SLOT(deleteLater()));
+ connect(mCaption, SIGNAL(linkClicked(const QString &)),
+ this, SIGNAL(signalIgnoreButtonClicked()));
+ connect(mCaption, SIGNAL(linkClicked(const QString &)),
+ this, SLOT(deleteLater()));
+
+ KopetePrefs *p = KopetePrefs::prefs();
+ // Autoclose balloon
+ if (p->balloonClose())
+ QTimer::singleShot( p->balloonCloseDelay() * 1000, this, SIGNAL( signalTimeout( ) ) );
+}
+
+void KopeteBalloon::setAnchor(const QPoint &anchor)
+{
+ mAnchor = anchor;
+ updateMask();
+}
+
+void KopeteBalloon::updateMask()
+{
+ QRegion mask(10, 10, width() - 20, height() - 20);
+
+ QPoint corners[8] = {
+ QPoint(width() - 50, 10),
+ QPoint(10, 10),
+ QPoint(10, height() - 50),
+ QPoint(width() - 50, height() - 50),
+ QPoint(width() - 10, 10),
+ QPoint(10, 10),
+ QPoint(10, height() - 10),
+ QPoint(width() - 10, height() - 10)
+ };
+
+ for (int i = 0; i < 4; ++i)
+ {
+ QPointArray corner;
+ corner.makeArc(corners[i].x(), corners[i].y(), 40, 40,
+ i * 16 * 90, 16 * 90);
+ corner.resize(corner.size() + 1);
+ corner.setPoint(corner.size() - 1, corners[i + 4]);
+ mask -= corner;
+ }
+
+ // get screen-geometry for screen our anchor is on
+ // (geometry can differ from screen to screen!
+ QRect deskRect = KGlobalSettings::desktopGeometry(mAnchor);
+
+ bool bottom = (mAnchor.y() + height()) > ((deskRect.y() + deskRect.height()-48));
+ bool right = (mAnchor.x() + width()) > ((deskRect.x() + deskRect.width()-48));
+
+ QPointArray arrow(4);
+ arrow.setPoint(0, QPoint(right ? width() : 0, bottom ? height() : 0));
+ arrow.setPoint(1, QPoint(right ? width() - 10 : 10,
+ bottom ? height() - 30 : 30));
+ arrow.setPoint(2, QPoint(right ? width() - 30 : 30,
+ bottom ? height() - 10 : 10));
+ arrow.setPoint(3, arrow[0]);
+ mask += arrow;
+ setMask(mask);
+
+ move( right ? mAnchor.x() - width() : ( mAnchor.x() < 0 ? 0 : mAnchor.x() ),
+ bottom ? mAnchor.y() - height() : ( mAnchor.y() < 0 ? 0 : mAnchor.y() ) );
+
+ //kdDebug(14000) << k_funcinfo << "finalpos: x=" << x() << ", y=" << y() << endl;
+}
+
+#include "kopeteballoon.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/kopeteballoon.h b/kopete/kopete/kopeteballoon.h
new file mode 100644
index 00000000..1c30a8e4
--- /dev/null
+++ b/kopete/kopete/kopeteballoon.h
@@ -0,0 +1,77 @@
+/*
+ kopeteballoon.h - Nice Balloon
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code based on Kim Applet code
+ Copyright (c) 2000-2002 by Malte Starostik <malte@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEBALLOON_H
+#define KOPETEBALLOON_H
+
+#include <qwidget.h>
+#include <kactivelabel.h>
+
+/**
+ * A class derived from KActiveLabel so we can handle how
+ * links are opened.
+ */
+class KopeteActiveLabel : public KActiveLabel
+{
+ Q_OBJECT
+
+public:
+ KopeteActiveLabel( QWidget *parent = 0, const char* name = 0 );
+ KopeteActiveLabel( const QString& text, QWidget *parent = 0, const char* name = 0 );
+
+public slots:
+ virtual void openLink( const QString &link );
+};
+
+
+
+/**
+ * A little balloon for notifications
+ *
+ * @author Malte Starostik <malte@kde.org>
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+class KopeteBalloon : public QWidget
+{
+ Q_OBJECT
+
+public:
+ KopeteBalloon(const QString &text, const QString &pic);
+// KopeteBalloon();
+
+ void setAnchor(const QPoint &anchor);
+
+signals:
+ void signalButtonClicked();
+ void signalIgnoreButtonClicked();
+ void signalBalloonClicked();
+ void signalTimeout();
+
+protected:
+ virtual void updateMask();
+
+private:
+ QPoint mAnchor;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteeditglobalidentitywidget.cpp b/kopete/kopete/kopeteeditglobalidentitywidget.cpp
new file mode 100644
index 00000000..63357068
--- /dev/null
+++ b/kopete/kopete/kopeteeditglobalidentitywidget.cpp
@@ -0,0 +1,255 @@
+/*
+ kopeteeditglobalidentitywidget.cpp - Kopete Edit Global Identity widget
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteeditglobalidentitywidget.h"
+
+// Qt include
+#include <qlayout.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qtooltip.h>
+#include <qcursor.h>
+
+// KDE include
+#include <klineedit.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <ktoolbar.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+#include <kpixmapregionselectordialog.h>
+
+// Kopete include
+#include "kopeteglobal.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+
+
+ClickableLabel::ClickableLabel(QWidget *parent, const char *name)
+ : QLabel(parent, name)
+{
+ setCursor(QCursor(Qt::PointingHandCursor));
+}
+
+void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
+{
+ if(event->button() == Qt::LeftButton)
+ {
+ emit clicked();
+ event->accept();
+ }
+}
+
+class KopeteEditGlobalIdentityWidget::Private
+{
+public:
+ Private() : myself(0L), labelPicture(0L), lineNickname(0L), lineStatusMessage(0L), mainLayout(0L), iconSize(22),
+ lastNickname("")
+ {}
+
+ Kopete::MetaContact *myself;
+ ClickableLabel *labelPicture;
+ KLineEdit *lineNickname;
+ KLineEdit *lineStatusMessage;
+ QHBoxLayout *mainLayout;
+ int iconSize;
+ QString lastNickname;
+};
+
+KopeteEditGlobalIdentityWidget::KopeteEditGlobalIdentityWidget(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ d = new Private;
+
+ d->myself = Kopete::ContactList::self()->myself();
+
+ createGUI();
+
+ // Update the GUI when a global identity key change.
+ connect(Kopete::ContactList::self(), SIGNAL(globalIdentityChanged(const QString&, const QVariant& )), this, SLOT(updateGUI(const QString&, const QVariant&)));
+}
+
+KopeteEditGlobalIdentityWidget::~KopeteEditGlobalIdentityWidget()
+{
+ delete d;
+}
+
+void KopeteEditGlobalIdentityWidget::setIconSize(int size)
+{
+ kdDebug(14000) << k_funcinfo << "Manually changing the icon size." << endl;
+
+ // Update the picture (change the size of it)
+ d->iconSize = size;
+ d->labelPicture->setMinimumSize(QSize(d->iconSize, d->iconSize));
+ d->labelPicture->setMaximumSize(QSize(d->iconSize, d->iconSize));
+ if( !d->myself->photo().isNull() )
+ d->labelPicture->setPixmap(QPixmap(d->myself->photo().smoothScale(d->iconSize, d->iconSize, QImage::ScaleMin)));
+}
+
+void KopeteEditGlobalIdentityWidget::iconSizeChanged()
+{
+ kdDebug(14000) << k_funcinfo << "Changing icon size (i.e the picture size)" << endl;
+
+ KToolBar *tb = (KToolBar*)sender();
+ if(tb)
+ {
+ // Update the picture (change the size of it)
+ d->iconSize = tb->iconSize();
+ d->labelPicture->setMinimumSize(QSize(d->iconSize, d->iconSize));
+ d->labelPicture->setMaximumSize(QSize(d->iconSize, d->iconSize));
+ if( !d->myself->photo().isNull() )
+ d->labelPicture->setPixmap(QPixmap(d->myself->photo().smoothScale(d->iconSize, d->iconSize, QImage::ScaleMin)));
+ }
+}
+
+void KopeteEditGlobalIdentityWidget::createGUI()
+{
+ d->mainLayout = new QHBoxLayout(this);
+
+ // The picture label
+ d->labelPicture = new ClickableLabel(this);
+ d->labelPicture->setMinimumSize(QSize(d->iconSize, d->iconSize));
+ d->labelPicture->setMaximumSize(QSize(d->iconSize, d->iconSize));
+ d->labelPicture->setFrameShape(QFrame::Box);
+ d->mainLayout->addWidget(d->labelPicture);
+ connect(d->labelPicture, SIGNAL(clicked()), this, SLOT(photoClicked()));
+
+ // The nickname lineEdit
+ d->lineNickname = new KLineEdit(this);
+ d->mainLayout->addWidget(d->lineNickname);
+ // Update the nickname when the user press return.
+ connect(d->lineNickname, SIGNAL(returnPressed()), this, SLOT(changeNickname()));
+ // Show the nickname text in red when they are change.
+ connect(d->lineNickname, SIGNAL(textChanged(const QString&)), this, SLOT(lineNicknameTextChanged(const QString& )));
+}
+
+void KopeteEditGlobalIdentityWidget::updateGUI(const QString &key, const QVariant &value)
+{
+ kdDebug(14000) << k_funcinfo << "Updating the GUI reflecting the global identity change." << endl;
+
+ if(key == Kopete::Global::Properties::self()->photo().key())
+ {
+ // Update the picture and the tooltip
+ if( !d->myself->photo().isNull() )
+ {
+ d->labelPicture->setPixmap(QPixmap(d->myself->photo().smoothScale(d->iconSize, d->iconSize, QImage::ScaleMin)));
+ QToolTip::add(d->labelPicture, "<qt><img src=\""+ value.toString() +"\"></qt>");
+ }
+ }
+ else if(key == Kopete::Global::Properties::self()->nickName().key())
+ {
+ // Update the nickname
+ d->lastNickname = value.toString();
+ d->lineNickname->setText(value.toString());
+ }
+}
+
+void KopeteEditGlobalIdentityWidget::photoClicked()
+{
+ KURL photoURL = KFileDialog::getImageOpenURL(QString::null, this, i18n("Global Photo"));
+ if(photoURL.isEmpty())
+ return;
+
+ // Only accept local file.
+ if(!photoURL.isLocalFile())
+ {
+ KMessageBox::sorry(this, i18n("Remote photos are not allowed."), i18n("Global Photo"));
+ return;
+ }
+
+ QString saveLocation(locateLocal("appdata", "global-photo.png"));
+ QImage photo(photoURL.path());
+ photo = KPixmapRegionSelectorDialog::getSelectedImage( QPixmap(photo), 96, 96, this );
+
+ if(!photo.isNull())
+ {
+ if(photo.width() > 96 || photo.height() > 96)
+ {
+ // Scale and crop the picture.
+ photo = photo.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, 96, 96);
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, 96, 96);
+
+ }
+ else if (photo.width() < 32 || photo.height() < 32)
+ {
+ // Scale and crop the picture.
+ photo = photo.smoothScale( 32, 32, QImage::ScaleMin );
+ // crop image if not square
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, 32, 32);
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, 32, 32);
+
+ }
+ else if (photo.width() != photo.height())
+ {
+ if(photo.width() < photo.height())
+ photo = photo.copy((photo.width()-photo.height())/2, 0, photo.height(), photo.height());
+ else if (photo.width() > photo.height())
+ photo = photo.copy(0, (photo.height()-photo.width())/2, photo.height(), photo.height());
+ }
+
+ if(!photo.save(saveLocation, "PNG"))
+ {
+ KMessageBox::sorry(this,
+ i18n("An error occurred when trying to save the global photo."),
+ i18n("Global Photo"));
+ }
+ }
+
+ d->myself->setPhotoSource(Kopete::MetaContact::SourceCustom);
+ d->myself->setPhoto(KURL(saveLocation));
+}
+
+void KopeteEditGlobalIdentityWidget::lineNicknameTextChanged(const QString &text)
+{
+ // Display the nickname in red if they are any change.
+ if(text != d->lastNickname)
+ {
+ d->lineNickname->setPaletteForegroundColor(Qt::red);
+ }
+ // The nickname re-become like it was before, reset the palette.
+ else
+ {
+ d->lineNickname->unsetPalette();
+ }
+}
+
+void KopeteEditGlobalIdentityWidget::changeNickname()
+{
+ if( !d->lineNickname->text().isEmpty() && d->lineNickname->text() != d->myself->displayName() )
+ {
+ kdDebug(14000) << k_funcinfo << "Updating global nickname..." << endl;
+
+ // Reset the text color since the nickname is now updated.
+ d->lineNickname->unsetPalette();
+
+ // Set the new nickname and set the DisplayName source Custom.
+ d->lastNickname = d->lineNickname->text();
+ d->myself->setDisplayName(d->lineNickname->text());
+ d->myself->setDisplayNameSource(Kopete::MetaContact::SourceCustom);
+ }
+}
+
+#include "kopeteeditglobalidentitywidget.moc"
diff --git a/kopete/kopete/kopeteeditglobalidentitywidget.h b/kopete/kopete/kopeteeditglobalidentitywidget.h
new file mode 100644
index 00000000..731a2cc5
--- /dev/null
+++ b/kopete/kopete/kopeteeditglobalidentitywidget.h
@@ -0,0 +1,99 @@
+/*
+ kopeteeditglobalidentitywidget.h - Kopete Edit Global Identity widget
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEEDITGLOBALIDENTITYWIDGET_H
+#define KOPETEEDITGLOBALIDENTITYWIDGET_H
+
+#include <qwidget.h>
+#include <qlabel.h>
+
+/**
+ * This is a simple widget added to a toolbar in KopeteWindow.
+ *
+ * It can edit the global photo and the global nickname.
+ * When either the photo or the nickname change, it's set the source to Custom.
+ * When well connected(signal/slot), it react to the toolbar icon size change.
+ *
+ * @author Michaël Larouche
+ */
+class KopeteEditGlobalIdentityWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ KopeteEditGlobalIdentityWidget(QWidget *parent = 0, const char *name = 0);
+ virtual ~KopeteEditGlobalIdentityWidget();
+
+public slots:
+ /**
+ * This slot is called when the "parent" toolbar change its icon size.
+ */
+ void iconSizeChanged();
+ /**
+ * This slot is called to first set the icon size.
+ */
+ void setIconSize(int size);
+
+private:
+ /**
+ * Create the internal widgets and signal/slots connections
+ */
+ void createGUI();
+
+private slots:
+ /**
+ * When a global identity key is changed, update the GUI.
+ */
+ void updateGUI(const QString &key, const QVariant &value);
+ /**
+ * The photo label was clicked, show a ImageFileDialog.
+ */
+ void photoClicked();
+ /**
+ * The nickname was changed, display the text in red to display the change.
+ */
+ void lineNicknameTextChanged(const QString &text);
+ /**
+ * User press Return/Enter in the KLineEdit, commit the new nickname.
+ */
+ void changeNickname();
+
+private:
+ class Private;
+ Private *d;
+};
+
+class QMouseEvent;
+/**
+ * This is a special label that react to click.
+ * Also display a "hand" when hovered.
+ *
+ * @author Michaël Larouche
+ */
+class ClickableLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ ClickableLabel(QWidget *parent = 0, const char *name = 0);
+
+signals:
+ void clicked();
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *event);
+};
+
+#endif
diff --git a/kopete/kopete/kopeteiface.cpp b/kopete/kopete/kopeteiface.cpp
new file mode 100644
index 00000000..3895e271
--- /dev/null
+++ b/kopete/kopete/kopeteiface.cpp
@@ -0,0 +1,344 @@
+/*
+ kopeteiface.cpp - Kopete DCOP Interface
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hennevl@hennevl.de>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include "kopeteiface.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteuiglobal.h"
+#include "kopeteaway.h"
+#include "kopetegroup.h"
+#include "kopetecontact.h"
+#include "kopeteconfig.h"
+
+KopeteIface::KopeteIface() : DCOPObject( "KopeteIface" )
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("AutoAway");
+
+ if (config->readBoolEntry("UseAutoAway", true))
+ {
+ connectDCOPSignal("kdesktop", "KScreensaverIface",
+ "KDE_start_screensaver()", "setAutoAway()", false);
+ }
+ else
+ {
+ disconnectDCOPSignal("kdesktop", "KScreensaverIface",
+ "KDE_start_screensaver()", "setAutoAway()");
+ }
+ // FIXME: AFAICT, this never seems to fire.
+ connectDCOPSignal("kdesktop", "KScreensaverIface",
+ "KDE_stop_screensaver()", "setActive()", false);
+}
+
+QStringList KopeteIface::contacts()
+{
+ return Kopete::ContactList::self()->contacts();
+}
+
+QStringList KopeteIface::reachableContacts()
+{
+ return Kopete::ContactList::self()->reachableContacts();
+}
+
+QStringList KopeteIface::onlineContacts()
+{
+ QStringList result;
+ QPtrList<Kopete::Contact> list = Kopete::ContactList::self()->onlineContacts();
+ QPtrListIterator<Kopete::Contact> it( list );
+ for( ; it.current(); ++it )
+ result.append( it.current()->contactId() );
+
+ return result;
+}
+
+QStringList KopeteIface::contactsStatus()
+{
+ return Kopete::ContactList::self()->contactStatuses();
+}
+
+QStringList KopeteIface::fileTransferContacts()
+{
+ return Kopete::ContactList::self()->fileTransferContacts();
+}
+
+QStringList KopeteIface::contactFileProtocols(const QString &displayName)
+{
+ return Kopete::ContactList::self()->contactFileProtocols(displayName);
+}
+
+QString KopeteIface::messageContact( const QString &contactId, const QString &messageText )
+{
+ Kopete::MetaContact *mc = Kopete::ContactList::self()->findMetaContactByContactId( contactId );
+ if ( !mc )
+ {
+ return "No such contact.";
+ }
+
+ if ( mc->isReachable() )
+ Kopete::ContactList::self()->messageContact( contactId, messageText );
+ else
+ return "The contact is not reachable";
+
+ //Default return value
+ return QString::null;
+}
+/*
+void KopeteIface::sendFile(const QString &displayName, const KURL &sourceURL,
+ const QString &altFileName, uint fileSize)
+{
+ return Kopete::ContactList::self()->sendFile(displayName, sourceURL, altFileName, fileSize);
+}
+
+*/
+
+QString KopeteIface::onlineStatus( const QString &metaContactId )
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( metaContactId );
+ if( m )
+ {
+ Kopete::OnlineStatus status = m->status();
+ return status.description();
+ }
+
+ return "Unknown Contact";
+}
+
+void KopeteIface::messageContactById( const QString &metaContactId )
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->metaContact( metaContactId );
+ if( m )
+ {
+ m->execute();
+ }
+}
+
+bool KopeteIface::addContact( const QString &protocolName, const QString &accountId, const QString &contactId,
+ const QString &displayName, const QString &groupName )
+{
+ //Get the protocol instance
+ Kopete::Account *myAccount = Kopete::AccountManager::self()->findAccount( protocolName, accountId );
+
+ if( myAccount )
+ {
+ QString contactName;
+ Kopete::Group *realGroup=0L;
+ //If the nickName isn't specified we need to display the userId in the prompt
+ if( displayName.isEmpty() || displayName.isNull() )
+ contactName = contactId;
+ else
+ contactName = displayName;
+
+ if ( !groupName.isEmpty() )
+ realGroup=Kopete::ContactList::self()->findGroup( groupName );
+
+ // Confirm with the user before we add the contact
+ // FIXME: This is completely bogus since the user may not
+ // even be at the computer. We just need to add the contact --Matt
+ if( KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), i18n( "An external application is attempting to add the "
+ " '%1' contact '%2' to your contact list. Do you want to allow this?" )
+ .arg( protocolName ).arg( contactName ), i18n( "Allow Contact?" ), i18n("Allow"), i18n("Reject") ) == 3 ) // Yes == 3
+ {
+ //User said Yes
+ myAccount->addContact( contactId, contactName, realGroup, Kopete::Account::DontChangeKABC);
+ return true;
+ } else {
+ //User said No
+ return false;
+ }
+
+ } else {
+ //This protocol is not loaded
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("An external application has attempted to add a contact using "
+ " the %1 protocol, which either does not exist or is not loaded.").arg( protocolName ),
+ i18n("Missing Protocol"));
+
+ return false;
+ }
+}
+
+QStringList KopeteIface::accounts()
+{
+ QStringList list;
+ QPtrList<Kopete::Account> m_accounts=Kopete::AccountManager::self()->accounts();
+ QPtrListIterator<Kopete::Account> it( m_accounts );
+ Kopete::Account *account;
+ while ( ( account = it.current() ) != 0 )
+ {
+ ++it;
+
+ list += ( account->protocol()->pluginId() +"||" + account->accountId() );
+ }
+
+ return list;
+
+}
+
+void KopeteIface::connect(const QString &protocolId, const QString &accountId )
+{
+ QPtrListIterator<Kopete::Account> it( Kopete::AccountManager::self()->accounts() );
+ Kopete::Account *account;
+ while ( ( account = it.current() ) != 0 )
+ {
+ ++it;
+
+ if( ( account->accountId() == accountId) )
+ {
+ if( protocolId.isEmpty() || account->protocol()->pluginId() == protocolId )
+ {
+ account->connect();
+ break;
+ }
+ }
+ }
+}
+
+void KopeteIface::disconnect(const QString &protocolId, const QString &accountId )
+{
+ QPtrListIterator<Kopete::Account> it( Kopete::AccountManager::self()->accounts() );
+ Kopete::Account *account;
+ while ( ( account = it.current() ) != 0 )
+ {
+ ++it;
+
+ if( ( account->accountId() == accountId) )
+ {
+ if( protocolId.isEmpty() || account->protocol()->pluginId() == protocolId )
+ {
+ account->disconnect();
+ break;
+ }
+ }
+ }
+}
+
+void KopeteIface::connectAll()
+{
+ Kopete::AccountManager::self()->connectAll();
+}
+
+void KopeteIface::disconnectAll()
+{
+ Kopete::AccountManager::self()->disconnectAll();
+}
+
+bool KopeteIface::loadPlugin( const QString &name )
+{
+ if ( Kopete::PluginManager::self()->setPluginEnabled( name ) )
+ {
+ QString argument = name;
+ if ( !argument.startsWith( "kopete_" ) )
+ argument.prepend( "kopete_" );
+ return Kopete::PluginManager::self()->loadPlugin( argument );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool KopeteIface::unloadPlugin( const QString &name )
+{
+ if ( Kopete::PluginManager::self()->setPluginEnabled( name, false ) )
+ {
+ QString argument = name;
+ if ( !argument.startsWith( "kopete_" ) )
+ argument.prepend( "kopete_" );
+ return Kopete::PluginManager::self()->unloadPlugin( argument );
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void KopeteIface::setAway()
+{
+ Kopete::AccountManager::self()->setAwayAll();
+}
+
+void KopeteIface::setAway(const QString &msg, bool away)
+{
+ Kopete::AccountManager::self()->setAwayAll(msg, away);
+}
+
+void KopeteIface::setAvailable()
+{
+ Kopete::AccountManager::self()->setAvailableAll();
+}
+
+void KopeteIface::setAutoAway()
+{
+ Kopete::Away::getInstance()->setAutoAway();
+}
+
+void KopeteIface::setGlobalNickname( const QString &nickname )
+{
+ if( Kopete::Config::enableGlobalIdentity() )
+ {
+ Kopete::MetaContact *myselfMetaContact = Kopete::ContactList::self()->myself();
+ myselfMetaContact->setDisplayNameSource( Kopete::MetaContact::SourceCustom );
+ myselfMetaContact->setDisplayName( nickname );
+ }
+}
+
+void KopeteIface::setGlobalPhoto( const KURL &photoUrl )
+{
+ if( Kopete::Config::enableGlobalIdentity() )
+ {
+ Kopete::MetaContact *myselfMetaContact = Kopete::ContactList::self()->myself();
+ myselfMetaContact->setPhoto( photoUrl );
+ if( myselfMetaContact->photoSource() != Kopete::MetaContact::SourceCustom )
+ myselfMetaContact->setPhotoSource( Kopete::MetaContact::SourceCustom );
+ }
+}
+
+QStringList KopeteIface::contactsForDisplayName( const QString & displayName )
+{
+ Kopete::MetaContact * mc = Kopete::ContactList::self()->findMetaContactByDisplayName( displayName );
+ QStringList contactIds;
+ if ( mc )
+ {
+ QPtrList<Kopete::Contact> contacts = mc->contacts();
+ QPtrListIterator<Kopete::Contact> it( contacts );
+ for( ; it.current(); ++it )
+ {
+ contactIds.append( (*it)->contactId() );
+ }
+ }
+ return contactIds;
+}
+
+QStringList KopeteIface::metacontactsForContactId( const QString & contactId )
+{
+ Kopete::MetaContact * mc = Kopete::ContactList::self()->findMetaContactByContactId( contactId );
+ if ( mc )
+ return QStringList( mc->displayName() );
+ else
+ return QStringList();
+}
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteiface.h b/kopete/kopete/kopeteiface.h
new file mode 100644
index 00000000..4ca4c4d1
--- /dev/null
+++ b/kopete/kopete/kopeteiface.h
@@ -0,0 +1,184 @@
+/*
+ kopeteiface.h - Kopete DCOP Interface
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hennevl@hennevl.de>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KopeteIface_h
+#define KopeteIface_h
+
+#include <dcopobject.h>
+#include <qstringlist.h>
+#include <kurl.h>
+
+#include "kopeteonlinestatus.h"
+
+/**
+ * DCOP interface for kopete
+ */
+class KopeteIface : virtual public DCOPObject
+{
+ K_DCOP
+
+public:
+ KopeteIface();
+
+k_dcop:
+ QStringList contacts();
+ QStringList reachableContacts();
+ QStringList onlineContacts();
+ QStringList fileTransferContacts();
+ QStringList contactFileProtocols(const QString &displayName);
+
+ /*void sendFile(const QString &displayName, const KURL &sourceURL,
+ const QString &altFileName = QString::null, uint fileSize = 0);*/
+
+ // FIXME: Do we *need* this one? Sounds error prone to me, because
+ // nicknames can contain parentheses too.
+ // Better add a contactStatus( const QString id ) I'd say - Martijn
+ QStringList contactsStatus();
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ QString messageContact( const QString &contactId, const QString &messageText = QString::null );
+
+ /**
+ * Describe the status of a contact by their metaContactId,
+ * aka their uid in KABC.
+ */
+ QString onlineStatus( const QString &metaContactId );
+
+ /**
+ * Message a contact by their metaContactId, aka their uid in KABC.
+ */
+ void messageContactById( const QString &metaContactId );
+
+ /**
+ * Adds a contact with the specified params.
+ *
+ * @param protocolName The name of the protocol this contact is for ("ICQ", etc)
+ * @param accountId The account ID to add the contact to
+ * @param contactId The unique ID for this protocol
+ * @param displayName The displayName of the contact (may equal userId for some protocols
+ * @param groupName The name of the group to add the contact to
+ * @return Weather or not the contact was added successfully
+ */
+ bool addContact( const QString &protocolName, const QString &accountId, const QString &contactId,
+ const QString &displayName, const QString &groupName = QString::null );
+
+ /**
+ * return a list of alls accounts.
+ * form: XXXProtocol||AccountId
+ */
+ QStringList accounts();
+
+ /**
+ * connect a given account in the given protocol
+ */
+ void connect(const QString &protocolName, const QString &accountId);
+ /**
+ * disconnect a given account in the given protocol
+ */
+ void disconnect(const QString &protocolName, const QString &accountId);
+
+ /**
+ * Ask all accounts to connect
+ */
+ void connectAll();
+
+ /**
+ * Ask all accounts to disconnect
+ */
+ void disconnectAll();
+
+ /**
+ * load a plugin
+ * the name is the name of the library: example: kopete_msn
+ * but you can ommit the kopete_ prefix
+ */
+ bool loadPlugin( const QString& name );
+ /**
+ * unload a plugin
+ * the name is the name of the library: example: kopete_msn
+ * but you can ommit the kopete_ prefix
+ */
+ bool unloadPlugin( const QString& name );
+
+ /**
+ * set all account away using the global away function
+ */
+ void setAway();
+
+ /**
+ * set all account away using the global away function
+ * and set an away message
+ */
+ void setAway( const QString &msg ) { setAway( msg, true ); }
+
+ /**
+ * set all account away using the global away function
+ * and set an away message.
+ * @param away decides if the message is away/non-away
+ */
+ void setAway( const QString &msg, bool away );
+
+ /**
+ * set Available all accountes
+ */
+ void setAvailable();
+ /**
+ * set all account away using the auto away funciton.
+ * accounts will return online if activity is detected again
+ */
+ void setAutoAway();
+
+ /**
+ * set the global nickname if global identity is enabled.
+ * @param nickname the new global nickname
+ */
+ void setGlobalNickname( const QString &nickname );
+
+ /**
+ * set the global photo if global identity is enabled.
+ * @param photoUrl URL to the photo
+ */
+ void setGlobalPhoto( const KURL &photoUrl );
+
+ /**
+ * get the contactIds for a given display name
+ * @param displayName
+ */
+ QStringList contactsForDisplayName( const QString & displayName );
+
+ /**
+ * get the metacontactIds that have the given contactId
+ * @param contactId
+ */
+ QStringList metacontactsForContactId( const QString & contactId );
+};
+
+#endif
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/kopeteui.rc b/kopete/kopete/kopeteui.rc
new file mode 100644
index 00000000..6bdfb89c
--- /dev/null
+++ b/kopete/kopete/kopeteui.rc
@@ -0,0 +1,107 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete" version="25">
+ <MenuBar>
+ <Menu name="file" noMerge="1">
+ <text>&amp;File</text>
+ <!-- <Action name="Connection"/> -->
+ <Action name="Status"/>
+ <Action name="SetStatusMessage"/>
+ <Separator lineSeparator="true"/>
+ <Action name="AddContact"/>
+ <Action name="AddGroup"/>
+ <Action name="ExportContacts"/>
+ <Separator lineSeparator="true"/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="contactSendMessage" />
+ <Action name="contactStartChat" />
+ <Action name="contactSendFile" />
+ <Separator lineSeparator="true" />
+ <Action name="contactMove" />
+ <Action name="contactCopy" />
+ <Action name="contactAddContact" />
+ <Action name="contactAddTemporaryContact" />
+ <Action name="contactRemove" />
+ <Merge />
+ <Action name="contactProperties" />
+
+ </Menu>
+ <Menu name="settings">
+ <text>&amp;Settings</text>
+ <Action name="settings_showmenubar" append="show_merge"/>
+ <Merge name="StandardToolBarMenuHandler" append="show_merge"/>
+ <Action name="settings_showstatusbar" append="show_merge"/>
+ <Action name="settings_show_offliners" append="show_merge"/>
+ <Action name="settings_show_empty_groups" append="show_merge"/>
+ <Action name="settings_plugins" append="configure_merge"/>
+ <Action name="settings_notifications" append="configure_merge"/>
+ <Action name="settings_global" append="configure_merge"/>
+ <Action name="settings_keys" append="configure_merge"/>
+ <Action name="settings_toolbars" append="configure_merge"/>
+ <Action name="settings_prefs" append="configure_merge"/>
+ </Menu>
+ </MenuBar>
+
+ <ToolBar fullWidth="true" name="mainToolBar" noMerge="1"><Text>Main Toolbar</Text>
+ <Action name="Status"/>
+ <Action name="SetStatusMessage"/>
+ <Action name="AddContact"/>
+ <Action name="settings_show_offliners"/>
+ <Action name="settings_show_empty_groups"/>
+ </ToolBar>
+
+ <ToolBar fullWidth="true" name="quickSearchBar" noMerge="1"><Text>Quick Search Bar</Text>
+ <Action name="quicksearch_reset"/>
+ <Action name="quicksearch_label"/>
+ <Action name="quicksearch_bar"/>
+ </ToolBar>
+
+ <ToolBar fullWidth="true" name="editGlobalIdentityBar" noMerge="1" hidden="true" newline="true"><Text>Global Identity Bar</Text>
+ <Action name="editglobal_widget"/>
+ </ToolBar>
+
+ <Menu name="contact_popup">
+ <Action name="contactSendMessage" />
+ <Action name="contactStartChat" />
+
+ <Menu name="contact_popup_actions">
+ <text>&amp;Other Actions</text>
+ <Action name="contactSendFile" />
+ <Action name="contactSendEmail" />
+ <Action name="contactSyncKABC"/>
+ </Menu>
+ <Separator lineSeparator="true" />
+ <Menu name="contact_popup_groups">
+ <text>&amp;Groups</text>
+ <Action name="contactMove" />
+ <Action name="contactCopy" />
+ </Menu>
+ <Action name="contactAddContact" />
+ <Action name="contactAddTemporaryContact" />
+ <Action name="contactRemove" />
+ <Merge />
+ <Action name="contactProperties" />
+ <Separator lineSeparator="true" />
+ </Menu>
+ <Menu name="group_popup">
+ <Action name="contactRemove" />
+ <Action name="contactRename" />
+ <Action name="contactSendMessage" />
+ <Action name="contactAddContact" />
+ <Separator lineSeparator="true" />
+ <Action name="contactProperties" />
+ </Menu>
+ <Menu name="contactlistitems_popup">
+ <Action name="contactRemove" />
+ </Menu>
+ <Menu name="contactlist_popup">
+ <Action name="AddContact"/>
+ <Action name="AddGroup"/>
+ <Separator lineSeparator="true"/>
+ <Action name="settings_showmenubar" />
+ <Action name="settings_show_offliners" />
+ <Action name="settings_show_empty_groups" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/kopete/kopetewindow.cpp b/kopete/kopete/kopetewindow.cpp
new file mode 100644
index 00000000..f3ec502e
--- /dev/null
+++ b/kopete/kopete/kopetewindow.cpp
@@ -0,0 +1,1112 @@
+/*
+ kopetewindow.cpp - Kopete Main Window
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2001-2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005-2006 by Will Stephenson <wstephenson@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetewindow.h"
+
+#include <qcursor.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qtooltip.h>
+#include <qtimer.h>
+#include <qevent.h>
+#include <qsignalmapper.h>
+
+#include <kaction.h>
+#include <kactionclasses.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <knotifydialog.h>
+#include <kpopupmenu.h>
+#include <kaccel.h>
+#include <kkeydialog.h>
+#include <kedittoolbar.h>
+#include <kmenubar.h>
+#include <kstatusbar.h>
+#include <kglobalaccel.h>
+#include <kwin.h>
+#include <kdeversion.h>
+#include <kinputdialog.h>
+#include <kplugininfo.h>
+#include <ksqueezedtextlabel.h>
+#include <kstringhandler.h>
+#include <kurl.h>
+
+#include "addcontactpage.h"
+#include "addcontactwizard.h"
+#include "addressbooklinkwidget.h"
+#include "groupkabcselectorwidget.h"
+#include "kabcexport.h"
+#include "kopeteapplication.h"
+#include "kopeteaccount.h"
+#include "kopeteaway.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccountstatusbaricon.h"
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "kopetecontactlistview.h"
+#include "kopetegroup.h"
+#include <kdialogbase.h>
+#include "kopetelistviewsearchline.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetepluginconfig.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprefs.h"
+#include "kopeteprotocol.h"
+#include "kopetestdaction.h"
+#include "kopeteawayaction.h"
+#include "kopeteuiglobal.h"
+#include "systemtray.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteeditglobalidentitywidget.h"
+
+//BEGIN GlobalStatusMessageIconLabel
+GlobalStatusMessageIconLabel::GlobalStatusMessageIconLabel(QWidget *parent, const char *name)
+ : QLabel(parent, name)
+{}
+
+void GlobalStatusMessageIconLabel::mouseReleaseEvent( QMouseEvent *event )
+{
+ if( event->button() == Qt::LeftButton || event->button() == Qt::RightButton )
+ {
+ emit iconClicked( event->globalPos() );
+ event->accept();
+ }
+}
+//END GlobalStatusMessageIconLabel
+
+/* KMainWindow is very broken from our point of view - it deref()'s the app
+ * when the last visible KMainWindow is destroyed. But when our main window is
+ * hidden when it's in the tray,closing the last chatwindow would cause the app
+ * to quit. - Richard
+ *
+ * Fortunately KMainWindow checks queryExit before deref()ing the Kapplication.
+ * KopeteWindow reimplements queryExit() and only returns true if it is shutting down
+ * (either because the user quit Kopete, or the session manager did).
+ *
+ * KopeteWindow and ChatWindows are closed by session management.
+ * App shutdown is not performed by the KopeteWindow but by KopeteApplication:
+ * 1) user quit - KopeteWindow::slotQuit() was called, calls KopeteApplication::quitKopete(),
+ * which closes all chatwindows and the KopeteWindow. The last window to close
+ * shuts down the PluginManager in queryExit(). When the PluginManager has completed its
+ * shutdown, the app is finally deref()ed, and the contactlist and accountmanager
+ * are saved.
+ * and calling KApplication::quit()
+ * 2) session - KopeteWindow and all chatwindows are closed by KApplication session management.
+ * quit Then the shutdown proceeds as above.
+ *
+ * queryClose() is honoured so group chats and chats receiving recent messages can interrupt
+ * (session) quit.
+ */
+
+KopeteWindow::KopeteWindow( QWidget *parent, const char *name )
+: KMainWindow( parent, name, WType_TopLevel )
+{
+ // Applications should ensure that their StatusBar exists before calling createGUI()
+ // so that the StatusBar is always correctly positioned when KDE is configured to use
+ // a MacOS-style MenuBar.
+ // This fixes a "statusbar drawn over the top of the toolbar" bug
+ // e.g. it can happen when you switch desktops on Kopete startup
+
+ m_statusBarWidget = new QHBox(statusBar(), "m_statusBarWidget");
+ m_statusBarWidget->setMargin( 2 );
+ m_statusBarWidget->setSpacing( 1 );
+ statusBar()->addWidget(m_statusBarWidget, 0, true );
+ QHBox *statusBarMessage = new QHBox(statusBar(), "m_statusBarWidget");
+ m_statusBarWidget->setMargin( 2 );
+ m_statusBarWidget->setSpacing( 1 );
+
+ GlobalStatusMessageIconLabel *label = new GlobalStatusMessageIconLabel( statusBarMessage, "statusmsglabel" );
+ label->setFixedSize( 16, 16 );
+ label->setPixmap( SmallIcon( "kopetestatusmessage" ) );
+ connect(label, SIGNAL(iconClicked( const QPoint& )),
+ this, SLOT(slotGlobalStatusMessageIconClicked( const QPoint& )));
+ QToolTip::add( label, i18n( "Global status message" ) );
+ m_globalStatusMessage = new KSqueezedTextLabel( statusBarMessage );
+ statusBar()->addWidget(statusBarMessage, 1, false );
+
+ m_pluginConfig = 0L;
+ m_autoHideTimer = new QTimer( this );
+
+ // --------------------------------------------------------------------------------
+ initView();
+ initActions();
+ contactlist->initActions(actionCollection());
+ initSystray();
+ // --------------------------------------------------------------------------------
+
+ // Trap all loaded plugins, so we can add their status bar icons accordingly , also used to add XMLGUIClient
+ connect( Kopete::PluginManager::self(), SIGNAL( pluginLoaded( Kopete::Plugin * ) ),
+ this, SLOT( slotPluginLoaded( Kopete::Plugin * ) ) );
+ connect( Kopete::PluginManager::self(), SIGNAL( allPluginsLoaded() ),
+ this, SLOT( slotAllPluginsLoaded() ));
+ //Connect the appropriate account signals
+ /* Please note that I tried to put this in the slotAllPluginsLoaded() function
+ * but it seemed to break the account icons in the statusbar --Matt */
+
+ connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
+ this, SLOT(slotAccountRegistered(Kopete::Account*)));
+ connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered(const Kopete::Account*)),
+ this, SLOT(slotAccountUnregistered(const Kopete::Account*)));
+
+ connect( m_autoHideTimer, SIGNAL( timeout() ), this, SLOT( slotAutoHide() ) );
+ connect( KopetePrefs::prefs(), SIGNAL( contactListAppearanceChanged() ),
+ this, SLOT( slotContactListAppearanceChanged() ) );
+
+ createGUI ( "kopeteui.rc", false );
+
+ // call this _after_ createGUI(), otherwise menubar is not set up correctly
+ loadOptions();
+
+ // If some plugins are already loaded, merge the GUI
+ Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins();
+ Kopete::PluginList::ConstIterator it;
+ for ( it = plugins.begin(); it != plugins.end(); ++it )
+ slotPluginLoaded( *it );
+
+ // If some account alrady loaded, build the status icon
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *a=accounts.first() ; a; a=accounts.next() )
+ slotAccountRegistered(a);
+
+ //install an event filter for the quick search toolbar so we can
+ //catch the hide events
+ toolBar( "quickSearchBar" )->installEventFilter( this );
+
+}
+
+void KopeteWindow::initView()
+{
+ contactlist = new KopeteContactListView(this);
+ setCentralWidget(contactlist);
+}
+
+void KopeteWindow::initActions()
+{
+ // this action menu contains one action per account and is updated when accounts are registered/unregistered
+ actionAddContact = new KActionMenu( i18n( "&Add Contact" ), "add_user",
+ actionCollection(), "AddContact" );
+ actionAddContact->setDelayed( false );
+ // this signal mapper is needed to call slotAddContact with the correct arguments
+ addContactMapper = new QSignalMapper( this );
+ connect( addContactMapper, SIGNAL( mapped( const QString & ) ),
+ this, SLOT( slotAddContactDialogInternal( const QString & ) ) );
+
+ /* ConnectAll is now obsolete. "Go online" has replaced it.
+ actionConnect = new KAction( i18n( "&Connect Accounts" ), "connect_creating",
+ 0, Kopete::AccountManager::self(), SLOT( connectAll() ),
+ actionCollection(), "ConnectAll" );
+ */
+
+ actionDisconnect = new KAction( i18n( "O&ffline" ), "connect_no",
+ 0, this, SLOT( slotDisconnectAll() ),
+ actionCollection(), "DisconnectAll" );
+
+ actionExportContacts = new KAction( i18n( "&Export Contacts..." ), "", 0, this,
+ SLOT( showExportDialog() ),actionCollection(), "ExportContacts" );
+
+ /* the connection menu has been replaced by the set status menu
+ actionConnectionMenu = new KActionMenu( i18n("Connection"),"connect_established",
+ actionCollection(), "Connection" );
+
+ actionConnectionMenu->setDelayed( false );
+ actionConnectionMenu->insert(actionConnect);
+ actionConnectionMenu->insert(actionDisconnect);
+ actionConnect->setEnabled(false);
+ */
+ actionDisconnect->setEnabled(false);
+
+ selectAway = new KAction( i18n("&Away"), SmallIcon("kopeteaway"), 0,
+ this, SLOT( slotGlobalAway() ), actionCollection(),
+ "SetAwayAll" );
+
+ selectBusy = new KAction( i18n("&Busy"), SmallIcon("kopeteaway"), 0,
+ this, SLOT( slotGlobalBusy() ), actionCollection(),
+ "SetBusyAll" );
+
+
+ actionSetInvisible = new KAction( i18n( "&Invisible" ), "kopeteavailable", 0 ,
+ this, SLOT( slotSetInvisibleAll() ), actionCollection(),
+ "SetInvisibleAll" );
+
+
+
+ /*actionSetAvailable = new KAction( i18n( "&Online" ),
+ "kopeteavailable", 0 , Kopete::AccountManager::self(),
+ SLOT( setAvailableAll() ), actionCollection(),
+ "SetAvailableAll" );*/
+
+ actionSetAvailable = new KAction( i18n("&Online"),
+ SmallIcon("kopeteavailable"), 0, this,
+ SLOT( slotGlobalAvailable() ), actionCollection(),
+ "SetAvailableAll" );
+
+ actionAwayMenu = new KActionMenu( i18n("&Set Status"), "kopeteavailable",
+ actionCollection(), "Status" );
+ actionAwayMenu->setDelayed( false );
+ actionAwayMenu->insert(actionSetAvailable);
+ actionAwayMenu->insert(selectAway);
+ actionAwayMenu->insert(selectBusy);
+ actionAwayMenu->insert(actionSetInvisible);
+ actionAwayMenu->insert(actionDisconnect);
+
+ actionPrefs = KopeteStdAction::preferences( actionCollection(), "settings_prefs" );
+
+ KStdAction::quit(this, SLOT(slotQuit()), actionCollection());
+
+ setStandardToolBarMenuEnabled(true);
+ menubarAction = KStdAction::showMenubar(this, SLOT(showMenubar()), actionCollection(), "settings_showmenubar" );
+ statusbarAction = KStdAction::showStatusbar(this, SLOT(showStatusbar()), actionCollection(), "settings_showstatusbar");
+
+ KStdAction::keyBindings( guiFactory(), SLOT( configureShortcuts() ), actionCollection(), "settings_keys" );
+ new KAction( i18n( "Configure Plugins..." ), "input_devices_settings", 0, this,
+ SLOT( slotConfigurePlugins() ), actionCollection(), "settings_plugins" );
+ new KAction( i18n( "Configure &Global Shortcuts..." ), "configure_shortcuts", 0, this,
+ SLOT( slotConfGlobalKeys() ), actionCollection(), "settings_global" );
+
+ KStdAction::configureToolbars( this, SLOT(slotConfToolbar()), actionCollection() );
+ KStdAction::configureNotifications(this, SLOT(slotConfNotifications()), actionCollection(), "settings_notifications" );
+
+ actionShowOffliners = new KToggleAction( i18n( "Show Offline &Users" ), "show_offliners", CTRL + Key_U,
+ this, SLOT( slotToggleShowOffliners() ), actionCollection(), "settings_show_offliners" );
+ actionShowEmptyGroups = new KToggleAction( i18n( "Show Empty &Groups" ), "folder", CTRL + Key_G,
+ this, SLOT( slotToggleShowEmptyGroups() ), actionCollection(), "settings_show_empty_groups" );
+
+ actionShowOffliners->setCheckedState(i18n("Hide Offline &Users"));
+ actionShowEmptyGroups->setCheckedState(i18n("Hide Empty &Groups"));
+
+ // quick search bar
+ QLabel *searchLabel = new QLabel( i18n("Se&arch:"), 0, "kde toolbar widget" );
+ QWidget *searchBar = new Kopete::UI::ListView::SearchLine( 0, contactlist, "quicksearch_bar" );
+ searchLabel->setBuddy( searchBar );
+ KWidgetAction *quickSearch = new KWidgetAction( searchBar, i18n( "Quick Search Bar" ), 0, 0, 0, actionCollection(), "quicksearch_bar" );
+ new KWidgetAction( searchLabel, i18n( "Search:" ), 0, 0, 0, actionCollection(), "quicksearch_label" );
+ quickSearch->setAutoSized( true );
+ // quick search bar - clear button
+ KAction *resetQuickSearch = new KAction( i18n( "Reset Quick Search" ),
+ QApplication::reverseLayout() ? "clear_left" : "locationbar_erase",
+ 0, searchBar, SLOT( clear() ), actionCollection(), "quicksearch_reset" );
+ resetQuickSearch->setWhatsThis( i18n( "Reset Quick Search\n"
+ "Resets the quick search so that all contacts and groups are shown again." ) );
+
+ // Edit global identity widget/bar
+ editGlobalIdentityWidget = new KopeteEditGlobalIdentityWidget(this, "editglobalBar");
+ editGlobalIdentityWidget->hide();
+ KWidgetAction *editGlobalAction = new KWidgetAction( editGlobalIdentityWidget, i18n("Edit Global Identity Widget"), 0, 0, 0, actionCollection(), "editglobal_widget");
+ editGlobalAction->setAutoSized( true );
+
+ // KActionMenu for selecting the global status message(kopeteonlinestatus_0)
+ KActionMenu * setStatusMenu = new KActionMenu( i18n( "Set Status Message" ), "kopeteeditstatusmessage", actionCollection(), "SetStatusMessage" );
+ setStatusMenu->setDelayed( false );
+ connect( setStatusMenu->popupMenu(), SIGNAL( aboutToShow() ), SLOT(slotBuildStatusMessageMenu() ) );
+ connect( setStatusMenu->popupMenu(), SIGNAL( activated( int ) ), SLOT(slotStatusMessageSelected( int ) ) );
+
+ // sync actions, config and prefs-dialog
+ connect ( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()) );
+ slotConfigChanged();
+
+ globalAccel = new KGlobalAccel( this );
+ globalAccel->insert( QString::fromLatin1("Read Message"), i18n("Read Message"), i18n("Read the next pending message"),
+ CTRL+SHIFT+Key_I, KKey::QtWIN+CTRL+Key_I, Kopete::ChatSessionManager::self(), SLOT(slotReadMessage()) );
+
+ globalAccel->insert( QString::fromLatin1("Show/Hide Contact List"), i18n("Show/Hide Contact List"), i18n("Show or hide the contact list"),
+ CTRL+SHIFT+Key_S, KKey::QtWIN+CTRL+Key_S, this, SLOT(slotShowHide()) );
+
+ globalAccel->insert( QString::fromLatin1("Set Away/Back"), i18n("Set Away/Back"), i18n("Sets away from keyboard or sets back"),
+ CTRL+SHIFT+Key_W, KKey::QtWIN+CTRL+SHIFT+Key_W, this, SLOT(slotToggleAway()) );
+
+ globalAccel->readSettings();
+ globalAccel->updateConnections();
+}
+
+void KopeteWindow::slotShowHide()
+{
+ if(isActiveWindow())
+ {
+ m_autoHideTimer->stop(); //no timeouts if active
+ hide();
+ }
+ else
+ {
+ show();
+ //raise() and show() should normaly deIconify the window. but it doesn't do here due
+ // to a bug in QT or in KDE (qt3.1.x or KDE 3.1.x) then, i have to call KWin's method
+ if(isMinimized())
+ KWin::deIconifyWindow(winId());
+
+ if(!KWin::windowInfo(winId(),NET::WMDesktop).onAllDesktops())
+ KWin::setOnDesktop(winId(), KWin::currentDesktop());
+ raise();
+ setActiveWindow();
+ }
+}
+
+void KopeteWindow::slotToggleAway()
+{
+ Kopete::Away *mAway = Kopete::Away::getInstance();
+ if ( mAway->globalAway() )
+ {
+ Kopete::AccountManager::self()->setAvailableAll();
+ }
+ else
+ {
+ QString awayReason = mAway->getMessage( 0 );
+ slotGlobalAway();
+ }
+}
+
+void KopeteWindow::initSystray()
+{
+ m_tray = KopeteSystemTray::systemTray( this, "KopeteSystemTray" );
+ Kopete::UI::Global::setSysTrayWId( m_tray->winId() );
+ KPopupMenu *tm = m_tray->contextMenu();
+
+ // NOTE: This is in reverse order because we insert
+ // at the top of the menu, not at bottom!
+ actionAddContact->plug( tm, 1 );
+ actionPrefs->plug( tm, 1 );
+ tm->insertSeparator( 1 );
+ actionAwayMenu->plug( tm, 1 );
+ //actionConnectionMenu->plug ( tm, 1 );
+ tm->insertSeparator( 1 );
+
+ QObject::connect( m_tray, SIGNAL( aboutToShowMenu( KPopupMenu * ) ),
+ this, SLOT( slotTrayAboutToShowMenu( KPopupMenu * ) ) );
+ QObject::connect( m_tray, SIGNAL( quitSelected() ), this, SLOT( slotQuit() ) );
+}
+
+KopeteWindow::~KopeteWindow()
+{
+ delete m_pluginConfig;
+}
+
+bool KopeteWindow::eventFilter( QObject* target, QEvent* event )
+{
+ KToolBar* toolBar = dynamic_cast<KToolBar*>( target );
+ KAction* resetAction = actionCollection()->action( "quicksearch_reset" );
+
+ if ( toolBar && resetAction && resetAction->isPlugged( toolBar ) )
+ {
+
+ if ( event->type() == QEvent::Hide )
+ {
+ resetAction->activate();
+ return true;
+ }
+ return KMainWindow::eventFilter( target, event );
+ }
+
+ return KMainWindow::eventFilter( target, event );
+}
+
+void KopeteWindow::loadOptions()
+{
+ KConfig *config = KGlobal::config();
+
+ toolBar("mainToolBar")->applySettings( config, "ToolBar Settings" );
+ toolBar("quickSearchBar")->applySettings( config, "QuickSearchBar Settings" );
+ toolBar("editGlobalIdentityBar")->applySettings( config, "EditGlobalIdentityBar Settings" );
+
+ // FIXME: HACK: Is there a way to do that automatic ?
+ editGlobalIdentityWidget->setIconSize(toolBar("editGlobalIdentityBar")->iconSize());
+ connect(toolBar("editGlobalIdentityBar"), SIGNAL(modechange()), editGlobalIdentityWidget, SLOT(iconSizeChanged()));
+
+ applyMainWindowSettings( config, "General Options" );
+ config->setGroup("General Options");
+ QPoint pos = config->readPointEntry("Position");
+ move(pos);
+
+ QSize size = config->readSizeEntry("Geometry");
+ if(size.isEmpty()) // Default size
+ resize( QSize(220, 350) );
+ else
+ resize(size);
+
+ KopetePrefs *p = KopetePrefs::prefs();
+
+ m_autoHide = p->contactListAutoHide();
+ m_autoHideTimeout = p->contactListAutoHideTimeout();
+
+
+ QString tmp = config->readEntry("State", "Shown");
+ if ( tmp == "Minimized" && p->showTray())
+ {
+ showMinimized();
+ }
+ else if ( tmp == "Hidden" && p->showTray())
+ {
+ hide();
+ }
+ else if ( !p->startDocked() || !p->showTray() )
+ show();
+
+ menubarAction->setChecked( !menuBar()->isHidden() );
+ statusbarAction->setChecked( !statusBar()->isHidden() );
+ m_autoHide = p->contactListAutoHide();
+ m_autoHideTimeout = p->contactListAutoHideTimeout();
+}
+
+void KopeteWindow::saveOptions()
+{
+ KConfig *config = KGlobal::config();
+
+ toolBar("mainToolBar")->saveSettings ( config, "ToolBar Settings" );
+ toolBar("quickSearchBar")->saveSettings( config, "QuickSearchBar Settings" );
+ toolBar("editGlobalIdentityBar")->saveSettings( config, "EditGlobalIdentityBar Settings" );
+
+ saveMainWindowSettings( config, "General Options" );
+
+ config->setGroup("General Options");
+ config->writeEntry("Position", pos());
+ config->writeEntry("Geometry", size());
+
+ if(isMinimized())
+ {
+ config->writeEntry("State", "Minimized");
+ }
+ else if(isHidden())
+ {
+ config->writeEntry("State", "Hidden");
+ }
+ else
+ {
+ config->writeEntry("State", "Shown");
+ }
+
+ config->sync();
+}
+
+void KopeteWindow::showMenubar()
+{
+ if(menubarAction->isChecked())
+ menuBar()->show();
+ else
+ menuBar()->hide();
+}
+
+void KopeteWindow::showStatusbar()
+{
+ if( statusbarAction->isChecked() )
+ statusBar()->show();
+ else
+ statusBar()->hide();
+}
+
+void KopeteWindow::slotToggleShowOffliners()
+{
+ KopetePrefs *p = KopetePrefs::prefs();
+ p->setShowOffline ( actionShowOffliners->isChecked() );
+
+ disconnect ( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()) );
+ p->save();
+ connect ( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()) );
+}
+
+void KopeteWindow::slotToggleShowEmptyGroups()
+{
+ KopetePrefs *p = KopetePrefs::prefs();
+ p->setShowEmptyGroups ( actionShowEmptyGroups->isChecked() );
+
+ disconnect ( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()) );
+ p->save();
+ connect ( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()) );
+}
+
+void KopeteWindow::slotConfigChanged()
+{
+ KopetePrefs *pref = KopetePrefs::prefs();
+
+ if( isHidden() && !pref->showTray()) // user disabled systray while kopete is hidden, show it!
+ show();
+
+ actionShowOffliners->setChecked( pref->showOffline() );
+ actionShowEmptyGroups->setChecked( pref->showEmptyGroups() );
+}
+
+void KopeteWindow::slotContactListAppearanceChanged()
+{
+ KopetePrefs* p = KopetePrefs::prefs();
+ m_autoHide = p->contactListAutoHide();
+ m_autoHideTimeout = p->contactListAutoHideTimeout();
+
+ startAutoHideTimer();
+}
+
+void KopeteWindow::slotConfNotifications()
+{
+ KNotifyDialog::configure( this );
+}
+
+void KopeteWindow::slotConfigurePlugins()
+{
+ if ( !m_pluginConfig )
+ m_pluginConfig = new KopetePluginConfig( this );
+ m_pluginConfig->show();
+
+ m_pluginConfig->raise();
+
+ KWin::activateWindow( m_pluginConfig->winId() );
+}
+
+void KopeteWindow::slotConfGlobalKeys()
+{
+ KKeyDialog::configure( globalAccel, this ) ;
+}
+
+void KopeteWindow::slotConfToolbar()
+{
+ saveMainWindowSettings(KGlobal::config(), "General Options");
+ KEditToolbar *dlg = new KEditToolbar(factory());
+ connect( dlg, SIGNAL(newToolbarConfig()), this, SLOT(slotUpdateToolbar()) );
+ connect( dlg, SIGNAL(finished()) , dlg, SLOT(deleteLater()));
+ dlg->show();
+}
+
+void KopeteWindow::slotUpdateToolbar()
+{
+ applyMainWindowSettings(KGlobal::config(), "General Options");
+}
+
+void KopeteWindow::slotGlobalAway()
+{
+ Kopete::AccountManager::self()->setAwayAll( m_globalStatusMessageStored );
+}
+
+void KopeteWindow::slotGlobalBusy()
+{
+ Kopete::AccountManager::self()->setOnlineStatus(
+ Kopete::OnlineStatusManager::Busy, m_globalStatusMessageStored );
+}
+
+void KopeteWindow::slotGlobalAvailable()
+{
+ Kopete::AccountManager::self()->setAvailableAll( m_globalStatusMessageStored );
+}
+
+void KopeteWindow::slotSetInvisibleAll()
+{
+ Kopete::AccountManager::self()->setOnlineStatus( Kopete::OnlineStatusManager::Invisible );
+}
+
+void KopeteWindow::slotDisconnectAll()
+{
+ m_globalStatusMessage->setText( "" );
+ m_globalStatusMessageStored = QString();
+ Kopete::AccountManager::self()->disconnectAll();
+}
+
+bool KopeteWindow::queryClose()
+{
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ if ( !app->sessionSaving() // if we are just closing but not shutting down
+ && !app->isShuttingDown()
+ && KopetePrefs::prefs()->showTray()
+ && isShown() )
+ // I would make this a KMessageBox::queuedMessageBox but there doesn't seem to be don'tShowAgain support for those
+ KMessageBox::information( this,
+ i18n( "<qt>Closing the main window will keep Kopete running in the "
+ "system tray. Use 'Quit' from the 'File' menu to quit the application.</qt>" ),
+ i18n( "Docking in System Tray" ), "hideOnCloseInfo" );
+// else // we are shutting down either user initiated or session management
+// Kopete::PluginManager::self()->shutdown();
+
+ return true;
+}
+
+bool KopeteWindow::queryExit()
+{
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ if ( app->sessionSaving()
+ || app->isShuttingDown() /* only set if KopeteApplication::quitKopete() or
+ KopeteApplication::commitData() called */
+ || !KopetePrefs::prefs()->showTray() /* also close if our tray icon is hidden! */
+ || !isShown() )
+ {
+ kdDebug( 14000 ) << k_funcinfo << " shutting down plugin manager" << endl;
+ Kopete::PluginManager::self()->shutdown();
+ return true;
+ }
+ else
+ return false;
+}
+
+void KopeteWindow::closeEvent( QCloseEvent *e )
+{
+ // if there's a system tray applet and we are not shutting down then just do what needs to be done if a
+ // window is closed.
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ if ( KopetePrefs::prefs()->showTray() && !app->isShuttingDown() && !app->sessionSaving() ) {
+ // BEGIN of code borrowed from KMainWindow::closeEvent
+ // Save settings if auto-save is enabled, and settings have changed
+ if ( settingsDirty() && autoSaveSettings() )
+ saveAutoSaveSettings();
+
+ if ( queryClose() ) {
+ e->accept();
+ }
+ // END of code borrowed from KMainWindow::closeEvent
+ kdDebug( 14000 ) << k_funcinfo << "just closing because we have a system tray icon" << endl;
+ }
+ else
+ {
+ kdDebug( 14000 ) << k_funcinfo << "delegating to KMainWindow::closeEvent()" << endl;
+ KMainWindow::closeEvent( e );
+ }
+}
+
+void KopeteWindow::slotQuit()
+{
+ saveOptions();
+ KopeteApplication *app = static_cast<KopeteApplication *>( kapp );
+ app->quitKopete();
+}
+
+void KopeteWindow::slotPluginLoaded( Kopete::Plugin * p )
+{
+ guiFactory()->addClient(p);
+}
+
+void KopeteWindow::slotAllPluginsLoaded()
+{
+// actionConnect->setEnabled(true);
+ actionDisconnect->setEnabled(true);
+}
+
+void KopeteWindow::slotAccountRegistered( Kopete::Account *account )
+{
+// kdDebug(14000) << k_funcinfo << "Called." << endl;
+ if ( !account )
+ return;
+
+ //enable the connect all toolbar button
+// actionConnect->setEnabled(true);
+ actionDisconnect->setEnabled(true);
+
+ connect( account->myself(),
+ SIGNAL(onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &) ),
+ this, SLOT( slotAccountStatusIconChanged( Kopete::Contact * ) ) );
+
+// connect( account, SIGNAL( iconAppearanceChanged() ), SLOT( slotAccountStatusIconChanged() ) );
+ connect( account, SIGNAL( colorChanged(const QColor& ) ), SLOT( slotAccountStatusIconChanged() ) );
+
+ connect( account->myself(),
+ SIGNAL(propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotAccountStatusIconChanged( Kopete::Contact* ) ) );
+
+ KopeteAccountStatusBarIcon *sbIcon = new KopeteAccountStatusBarIcon( account, m_statusBarWidget );
+ connect( sbIcon, SIGNAL( rightClicked( Kopete::Account *, const QPoint & ) ),
+ SLOT( slotAccountStatusIconRightClicked( Kopete::Account *,
+ const QPoint & ) ) );
+ connect( sbIcon, SIGNAL( leftClicked( Kopete::Account *, const QPoint & ) ),
+ SLOT( slotAccountStatusIconRightClicked( Kopete::Account *,
+ const QPoint & ) ) );
+
+ m_accountStatusBarIcons.insert( account, sbIcon );
+ slotAccountStatusIconChanged( account->myself() );
+
+ // add an item for this account to the add contact actionmenu
+ QString s = "actionAdd%1Contact";
+ s.arg( account->accountId() );
+ KAction *action = new KAction( account->accountLabel(), account->accountIcon(), 0 , addContactMapper, SLOT( map() ), account, s.latin1() );
+ addContactMapper->setMapping( action, account->protocol()->pluginId() + QChar(0xE000) + account->accountId() );
+ actionAddContact->insert( action );
+}
+
+void KopeteWindow::slotAccountUnregistered( const Kopete::Account *account)
+{
+// kdDebug(14000) << k_funcinfo << "Called." << endl;
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ if (accounts.isEmpty())
+ {
+// actionConnect->setEnabled(false);
+ actionDisconnect->setEnabled(false);
+ }
+
+ // the (void*) is to remove the const. i don't know why QPtrList doesn't accept const ptr as key.
+ KopeteAccountStatusBarIcon *sbIcon = static_cast<KopeteAccountStatusBarIcon *>( m_accountStatusBarIcons[ (void*)account ] );
+
+ if( !sbIcon )
+ return;
+
+ m_accountStatusBarIcons.remove( (void*)account );
+ delete sbIcon;
+
+ makeTrayToolTip();
+
+ // update add contact actionmenu
+ QString s = "actionAdd%1Contact";
+ s.arg( account->accountId() );
+// KAction * action = actionCollection()->action( account->accountId() );
+ Kopete::Account * myAccount = const_cast< Kopete::Account * > ( account );
+ KAction * action = static_cast< KAction *>( myAccount->child( s.latin1() ) );
+ if ( action )
+ {
+ kdDebug(14000) << " found KAction " << action << " with name: " << action->name() << endl;
+ addContactMapper->removeMappings( action );
+ actionAddContact->remove( action );
+ }
+}
+
+void KopeteWindow::slotAccountStatusIconChanged()
+{
+ if ( const Kopete::Account *from = dynamic_cast<const Kopete::Account*>(sender()) )
+ slotAccountStatusIconChanged( from->myself() );
+}
+
+void KopeteWindow::slotAccountStatusIconChanged( Kopete::Contact *contact )
+{
+ kdDebug( 14000 ) << k_funcinfo << contact->property( Kopete::Global::Properties::self()->awayMessage() ).value() << endl;
+ // update the global status label if the change doesn't
+// QString newAwayMessage = contact->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
+ Kopete::OnlineStatus status = contact->onlineStatus();
+/* if ( status.status() != Kopete::OnlineStatus::Connecting )
+ {
+ QString globalMessage = m_globalStatusMessage->text();
+ if ( newAwayMessage != globalMessage )
+ m_globalStatusMessage->setText( "" /* i18n("status message to show when different accounts have different status messages", "(multiple)" )*/ /*);
+ }*/
+// kdDebug(14000) << k_funcinfo << "Icons: '" <<
+// status.overlayIcons() << "'" << endl;
+
+ if ( status != Kopete::OnlineStatus::Connecting )
+ {
+ if(contact->hasProperty(Kopete::Global::Properties::self()->awayMessage().key()))
+ {
+ m_globalStatusMessageStored = contact->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
+ m_globalStatusMessage->setText( m_globalStatusMessageStored );
+ }
+ else //If the account has not status message, it may be because the protocol doesn't support it (Bug 132609)
+ { // or because the user just set an empty status to this account.
+ // We will check if another account has still a status message, if yes, we will use it, if not, we will clear it.
+ QString statusMessageToUse;
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *a = accounts.first(); a; a = accounts.next())
+ {
+ Kopete::Contact *self = a->myself();
+ if(self->hasProperty(Kopete::Global::Properties::self()->awayMessage().key()))
+ {
+ statusMessageToUse = self->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
+ if(statusMessageToUse == m_globalStatusMessageStored )
+ break; //keep this one
+ }
+ }
+ m_globalStatusMessageStored = statusMessageToUse;
+ m_globalStatusMessage->setText( m_globalStatusMessageStored );
+ }
+ }
+
+ KopeteAccountStatusBarIcon *i = static_cast<KopeteAccountStatusBarIcon *>( m_accountStatusBarIcons[ contact->account() ] );
+ if( !i )
+ return;
+
+ // Adds tooltip for each status icon,
+ // useful in case you have many accounts
+ // over one protocol
+ QToolTip::remove( i );
+ QToolTip::add( i, contact->toolTip() );
+
+ // Because we want null pixmaps to detect the need for a loadMovie
+ // we can't use the SmallIcon() method directly
+ KIconLoader *loader = KGlobal::instance()->iconLoader();
+
+ QMovie mv = loader->loadMovie( status.overlayIcons().first(), KIcon::Small );
+
+ if ( mv.isNull() )
+ {
+ // No movie found, fallback to pixmap
+ // Get the icon for our status
+
+ //QPixmap pm = SmallIcon( icon );
+ QPixmap pm = status.iconFor( contact->account() );
+
+ // No Pixmap found, fallback to Unknown
+ if( pm.isNull() )
+ i->setPixmap( KIconLoader::unknown() );
+ else
+ i->setPixmap( pm );
+ }
+ else
+ {
+ //kdDebug( 14000 ) << k_funcinfo << "Using movie." << endl;
+ i->setMovie( mv );
+ }
+ makeTrayToolTip();
+}
+
+void KopeteWindow::makeTrayToolTip()
+{
+ //the tool-tip of the systemtray.
+ if(m_tray)
+ {
+ QToolTip::remove(m_tray);
+
+ QString tt = QString::fromLatin1("<qt>");
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *a = accounts.first(); a; a = accounts.next())
+ {
+ Kopete::Contact *self = a->myself();
+ tt += i18n( "Account tooltip information: <nobr>ICON <b>PROTOCOL:</b> NAME (<i>STATUS</i>)<br/>",
+ "<nobr><img src=\"kopete-account-icon:%3:%4\"> <b>%1:</b> %2 (<i>%5</i>)<br/>" )
+ .arg( a->protocol()->displayName() ).arg( a->accountLabel(), KURL::encode_string( a->protocol()->pluginId() ),
+ KURL::encode_string( a->accountId() ), self->onlineStatus().description() );
+ }
+ tt += QString::fromLatin1("</qt>");
+ QToolTip::add(m_tray, tt);
+ }
+}
+
+void KopeteWindow::slotAccountStatusIconRightClicked( Kopete::Account *account, const QPoint &p )
+{
+ KActionMenu *actionMenu = account->actionMenu();
+ if ( !actionMenu )
+ return;
+
+ connect( actionMenu->popupMenu(), SIGNAL( aboutToHide() ), actionMenu, SLOT( deleteLater() ) );
+ actionMenu->popupMenu()->popup( p );
+}
+
+void KopeteWindow::slotTrayAboutToShowMenu( KPopupMenu * popup )
+{
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *a=accounts.first() ; a; a=accounts.next() )
+ {
+ KActionMenu *menu = a->actionMenu();
+ if( menu )
+ menu->plug(popup, 1 );
+
+ connect(popup , SIGNAL(aboutToHide()) , menu , SLOT(deleteLater()));
+ }
+
+}
+
+
+
+/*void KopeteWindow::slotProtocolStatusIconRightClicked( Kopete::Protocol *proto, const QPoint &p )
+{
+ //kdDebug( 14000 ) << k_funcinfo << endl;
+ if ( Kopete::AccountManager::self()->accounts( proto ).count() > 0 )
+ {
+ KActionMenu *menu = proto->protocolActions();
+
+ connect( menu->popupMenu(), SIGNAL( aboutToHide() ), menu, SLOT( deleteLater() ) );
+ menu->popupMenu()->popup( p );
+ }
+}*/
+
+void KopeteWindow::showExportDialog()
+{
+ ( new KabcExportWizard( this, "export_contact_dialog" ) )->show();
+}
+
+void KopeteWindow::leaveEvent( QEvent * )
+{
+ startAutoHideTimer();
+}
+
+void KopeteWindow::showEvent( QShowEvent * )
+{
+ startAutoHideTimer();
+}
+
+void KopeteWindow::slotAutoHide()
+{
+ if ( this->geometry().contains( QCursor::pos() ) == false )
+ {
+ /* The autohide-timer doesn't need to emit
+ * timeouts when the window is hidden already. */
+ m_autoHideTimer->stop();
+ hide();
+ }
+}
+
+void KopeteWindow::startAutoHideTimer()
+{
+ if ( m_autoHideTimeout > 0 && m_autoHide == true && isVisible() && KopetePrefs::prefs()->showTray())
+ m_autoHideTimer->start( m_autoHideTimeout * 1000 );
+}
+
+// Iterate each connected account, updating its status message bug keeping the
+// same onlinestatus. Then update Kopete::Away and the UI.
+void KopeteWindow::setStatusMessage( const QString & message )
+{
+ bool changed = false;
+ for ( QPtrListIterator<Kopete::Account> it( Kopete::AccountManager::self()->accounts() ); it.current(); ++it )
+ {
+ Kopete::Contact *self = it.current()->myself();
+ bool isInvisible = self && self->onlineStatus().status() == Kopete::OnlineStatus::Invisible;
+ if ( it.current()->isConnected() && !isInvisible )
+ {
+ changed = true;
+ it.current()->setOnlineStatus( self->onlineStatus(), message );
+ }
+ }
+ Kopete::Away::getInstance()->setGlobalAwayMessage( message );
+ m_globalStatusMessageStored = message;
+ m_globalStatusMessage->setText( message );
+}
+
+void KopeteWindow::slotBuildStatusMessageMenu()
+{
+ QObject * senderObj = const_cast<QObject *>( sender() );
+ m_globalStatusMessageMenu = static_cast<KPopupMenu *>( senderObj );
+ m_globalStatusMessageMenu->clear();
+// pop up a menu containing the away messages, and a lineedit
+// see kopeteaway
+ //messageMenu = new KPopupMenu( this );
+// messageMenu->insertTitle( i18n( "Status Message" ) );
+ QHBox * newMessageBox = new QHBox( 0 );
+ newMessageBox->setMargin( 1 );
+ QLabel * newMessagePix = new QLabel( newMessageBox );
+ newMessagePix->setPixmap( SmallIcon( "edit" ) );
+/* QLabel * newMessageLabel = new QLabel( i18n( "Add " ), newMessageBox );*/
+ m_newMessageEdit = new QLineEdit( newMessageBox, "newmessage" );
+
+ newMessageBox->setFocusProxy( m_newMessageEdit );
+ newMessageBox->setFocusPolicy( QWidget::ClickFocus );
+/* newMessageLabel->setFocusProxy( newMessageEdit );
+ newMessageLabel->setBuddy( newMessageEdit );
+ newMessageLabel->setFocusPolicy( QWidget::ClickFocus );*/
+ newMessagePix->setFocusProxy( m_newMessageEdit );
+ newMessagePix->setFocusPolicy( QWidget::ClickFocus );
+ connect( m_newMessageEdit, SIGNAL( returnPressed() ), SLOT( slotNewStatusMessageEntered() ) );
+
+ m_globalStatusMessageMenu->insertItem( newMessageBox );
+
+ int i = 0;
+
+ m_globalStatusMessageMenu->insertItem( SmallIcon( "remove" ), i18n( "No Message" ), i++ );
+ m_globalStatusMessageMenu->insertSeparator();
+
+ QStringList awayMessages = Kopete::Away::getInstance()->getMessages();
+ for( QStringList::iterator it = awayMessages.begin(); it != awayMessages.end(); ++it, ++i )
+ {
+ m_globalStatusMessageMenu->insertItem( KStringHandler::rsqueeze( *it ), i );
+ }
+// connect( m_globalStatusMessageMenu, SIGNAL( activated( int ) ), SLOT( slotStatusMessageSelected( int ) ) );
+// connect( messageMenu, SIGNAL( aboutToHide() ), messageMenu, SLOT( deleteLater() ) );
+
+ m_newMessageEdit->setFocus();
+
+ //messageMenu->popup( e->globalPos(), 1 );
+}
+
+void KopeteWindow::slotStatusMessageSelected( int i )
+{
+ Kopete::Away *away = Kopete::Away::getInstance();
+ if ( 0 == i )
+ setStatusMessage( "" );
+ else
+ setStatusMessage( away->getMessage( i - 1 ) );
+}
+
+void KopeteWindow::slotNewStatusMessageEntered()
+{
+ m_globalStatusMessageMenu->close();
+ QString newMessage = m_newMessageEdit->text();
+ if ( !newMessage.isEmpty() )
+ Kopete::Away::getInstance()->addMessage( newMessage );
+ setStatusMessage( m_newMessageEdit->text() );
+}
+
+void KopeteWindow::slotGlobalStatusMessageIconClicked( const QPoint &position )
+{
+ KPopupMenu *statusMessageIconMenu = new KPopupMenu(this, "statusMessageIconMenu");
+ connect(statusMessageIconMenu, SIGNAL( aboutToShow() ),
+ this, SLOT(slotBuildStatusMessageMenu()));
+ connect( statusMessageIconMenu, SIGNAL( activated( int ) ),
+ SLOT( slotStatusMessageSelected( int ) ) );
+
+ statusMessageIconMenu->popup(position);
+}
+
+void KopeteWindow::slotAddContactDialogInternal( const QString & accountIdentifier )
+{
+ QString protocolId = accountIdentifier.section( QChar(0xE000), 0, 0 );
+ QString accountId = accountIdentifier.section( QChar(0xE000), 1, 1 );
+ Kopete::Account *account = Kopete::AccountManager::self()->findAccount( protocolId, accountId );
+ showAddContactDialog( account );
+}
+
+void KopeteWindow::showAddContactDialog( Kopete::Account * account )
+{
+ if ( !account ) {
+ kdDebug( 14000 ) << k_funcinfo << "no account given" << endl;
+ return;
+ }
+
+ KDialogBase *addDialog = new KDialogBase( this, "addDialog", true,
+ i18n( "Add Contact" ), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, true );
+
+ QVBox * mainWid = new QVBox( addDialog );
+
+ AddContactPage *addContactPage =
+ account->protocol()->createAddContactWidget( mainWid, account );
+
+ GroupKABCSelectorWidget * groupKABC = new GroupKABCSelectorWidget( mainWid, "groupkabcwidget" );
+
+ // Populate the groups list
+ Kopete::GroupList groups=Kopete::ContactList::self()->groups();
+ QDict<Kopete::Group> groupItems;
+ for( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ QString groupname = it->displayName();
+ if ( !groupname.isEmpty() )
+ {
+ groupItems.insert( groupname, it );
+ groupKABC->groupCombo->insertItem( groupname );
+ }
+ }
+
+ if (!addContactPage)
+ {
+ kdDebug(14000) << k_funcinfo <<
+ "Error while creating addcontactpage" << endl;
+ }
+ else
+ {
+ addDialog->setMainWidget( mainWid );
+ if( addDialog->exec() == QDialog::Accepted )
+ {
+ if( addContactPage->validateData() )
+ {
+ Kopete::MetaContact * metacontact = new Kopete::MetaContact();
+ metacontact->addToGroup( groupItems[ groupKABC->groupCombo->currentText() ] );
+ metacontact->setMetaContactId( groupKABC->widAddresseeLink->uid() );
+ if (addContactPage->apply( account, metacontact ))
+ {
+ Kopete::ContactList::self()->addMetaContact( metacontact );
+ }
+ else
+ {
+ delete metacontact;
+ }
+ }
+ }
+ }
+ addDialog->deleteLater();
+}
+
+#include "kopetewindow.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/kopetewindow.h b/kopete/kopete/kopetewindow.h
new file mode 100644
index 00000000..cc466883
--- /dev/null
+++ b/kopete/kopete/kopetewindow.h
@@ -0,0 +1,280 @@
+/*
+ kopetewindow.h - Kopete Main Window
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2001-2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEWINDOW_H
+#define KOPETEWINDOW_H
+
+#include <qptrdict.h>
+
+#include <kmainwindow.h>
+#include <qlabel.h>
+
+class QHBox;
+class QTimer;
+class QSignalMapper;
+
+class QMouseEvent;
+class QPoint;
+
+class KAction;
+class KActionMenu;
+
+class KGlobalAccel;
+class KSelectAction;
+class KSqueezedTextLabel;
+class KToggleAction;
+
+class KopeteAccountStatusBarIcon;
+class KopeteContactListView;
+class KopetePluginConfig;
+class KopeteSystemTray;
+class KopeteEditGlobalIdentityWidget;
+
+namespace Kopete
+{
+class AwayAction;
+class Account;
+class Contact;
+class Plugin;
+class Protocol;
+}
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ */
+class KopeteWindow : public KMainWindow
+{
+ Q_OBJECT
+
+public:
+ KopeteWindow ( QWidget *parent = 0, const char *name = 0 );
+ ~KopeteWindow();
+
+ virtual bool eventFilter( QObject* o, QEvent* e );
+
+protected:
+ virtual void closeEvent( QCloseEvent *ev );
+ virtual void leaveEvent( QEvent* ev );
+ virtual void showEvent( QShowEvent* ev );
+
+private slots:
+ void showMenubar();
+ void showStatusbar();
+ void slotToggleShowOffliners();
+ void slotToggleShowEmptyGroups();
+ void slotConfigChanged();
+ void slotConfNotifications();
+ void slotConfToolbar();
+ void slotUpdateToolbar();
+ void slotConfigurePlugins();
+ void slotConfGlobalKeys();
+ void slotShowHide();
+ void slotToggleAway();
+
+ /* show the global status message selector menu
+ */
+ void setStatusMessage( const QString & );
+
+ /**
+ * Checks if the mousecursor is in the contact list.
+ * If not, the window will be hidden.
+ */
+ void slotAutoHide();
+
+ /**
+ * This slot will apply settings that change the
+ * contactlist's appearance. Only autohiding is
+ * handled here at the moment
+ */
+ void slotContactListAppearanceChanged();
+
+ /**
+ * This slot will set all the protocols to away
+ */
+ void slotGlobalAway();
+ void slotGlobalBusy();
+ void slotGlobalAvailable();
+ void slotSetInvisibleAll();
+ void slotDisconnectAll();
+
+ void slotQuit();
+
+ /**
+ * Get a notification when a plugin is loaded, so we can merge
+ * XMLGUI cruft
+ */
+ void slotPluginLoaded( Kopete::Plugin *p );
+
+ /**
+ * Get a notification when an account is created, so we can add a status bar
+ * icon
+ */
+ void slotAccountRegistered( Kopete::Account *a );
+
+ /**
+ * Cleanup the status bar icon when the account is destroyed
+ */
+ void slotAccountUnregistered( const Kopete::Account *a);
+
+ /**
+ * The status icon got changed, update it.
+ * @param contact The account's contact that changed.
+ */
+ void slotAccountStatusIconChanged( Kopete::Contact * contact);
+
+ /**
+ * The status icon of some account changed. Must be sent by the account in question.
+ */
+ void slotAccountStatusIconChanged();
+
+ /**
+ * Show a context menu for a protocol
+ */
+// void slotProtocolStatusIconRightClicked( Kopete::Protocol *proto, const QPoint &p );
+
+ /**
+ * Show a context menu for an account
+ */
+ void slotAccountStatusIconRightClicked( Kopete::Account *a,
+ const QPoint &p );
+
+ void slotTrayAboutToShowMenu(KPopupMenu *);
+
+ /**
+ * Show the Add Contact wizard
+ */
+ void showAddContactDialog( Kopete::Account * );
+
+ /**
+ * Show the Export Contacts wizards
+ */
+ void showExportDialog();
+
+ /**
+ * Enable the Connect All and Disconnect All buttons here
+ * along with connecting the accountRegistered and accountUnregistered
+ * signals.
+ */
+ void slotAllPluginsLoaded();
+
+ /**
+ * Protected slot to setup the Set Global Status Message menu.
+ */
+ void slotBuildStatusMessageMenu();
+ void slotStatusMessageSelected( int i );
+ void slotNewStatusMessageEntered();
+
+ /**
+ * Show the set global status message menu when clicking on the icon in the status bar.
+ */
+ void slotGlobalStatusMessageIconClicked( const QPoint &position );
+
+ /**
+ * Extracts protocolId and accountId from the single QString argument signalled by a QSignalMapper,
+ * get the account, and call showAddContactDialog.
+ * @param accountIdentifer QString of protocolId and accountId, concatenated with QChar( 0xE000 )
+ * We need both to uniquely identify an account, but QSignalMapper only emits one QString.
+ */
+ void slotAddContactDialogInternal( const QString & accountIdentifier );
+
+public:
+ KopeteContactListView *contactlist;
+
+ // Some Actions
+ KActionMenu* actionAddContact;
+
+ //KActionMenu* actionConnectionMenu;
+ //KAction* actionConnect;
+ KAction* actionDisconnect;
+ KAction* actionExportContacts;
+
+ KActionMenu* actionAwayMenu;
+ KActionMenu* actionDockMenu;
+ KAction* selectAway;
+ KAction* selectBusy;
+ KAction* actionSetAvailable;
+ KAction* actionSetInvisible;
+
+
+ KAction* actionPrefs;
+ KAction* actionQuit;
+ KAction* actionSave;
+ KToggleAction *menubarAction;
+ KToggleAction *statusbarAction;
+ KToggleAction *actionShowOffliners;
+ KToggleAction *actionShowEmptyGroups;
+ KGlobalAccel *globalAccel;
+
+ KopeteEditGlobalIdentityWidget *editGlobalIdentityWidget;
+private:
+ void initView();
+ void initActions();
+ void initSystray();
+ void loadOptions();
+ void saveOptions();
+
+ void makeTrayToolTip();
+ void startAutoHideTimer();
+
+ virtual bool queryClose();
+ virtual bool queryExit();
+private:
+ int docked;
+ bool hidden;
+ int deskRight;
+ QPoint position;
+ QHBox *m_statusBarWidget;
+ KopeteSystemTray *m_tray;
+ bool m_autoHide;
+ unsigned int m_autoHideTimeout;
+ QTimer* m_autoHideTimer;
+ QSignalMapper* addContactMapper;
+
+ KopetePluginConfig *m_pluginConfig;
+
+ /**
+ * This is really a dictionary of KopeteAccountStatusBarIcon objects, but
+ * QPtrDict requires a full class definition to be known to make
+ * that work. And since I don't want to include that whole file here,
+ * use QObject instead.
+ */
+ QPtrDict<QObject> m_accountStatusBarIcons;
+ KSqueezedTextLabel * m_globalStatusMessage;
+ KPopupMenu * m_globalStatusMessageMenu;
+ QLineEdit * m_newMessageEdit;
+ QString m_globalStatusMessageStored;
+};
+
+
+class GlobalStatusMessageIconLabel : public QLabel
+{
+ Q_OBJECT
+public:
+ GlobalStatusMessageIconLabel(QWidget *parent = 0, const char *name = 0);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *event);
+
+signals:
+ void iconClicked(const QPoint &position);
+
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/main.cpp b/kopete/kopete/main.cpp
new file mode 100644
index 00000000..d428c1bc
--- /dev/null
+++ b/kopete/kopete/main.cpp
@@ -0,0 +1,109 @@
+/*
+ Kopete , The KDE Instant Messenger
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Viva Chile Mierda!
+ Started at Wed Dec 26 03:12:10 CLST 2001, Santiago de Chile
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include "kopeteapplication.h"
+
+#include <dcopclient.h>
+#include "kopeteiface.h"
+#include "kimifaceimpl.h"
+#include "kopeteversion.h"
+
+static const char description[] =
+ I18N_NOOP( "Kopete, the KDE Instant Messenger" );
+
+static KCmdLineOptions options[] =
+{
+ { "noplugins", I18N_NOOP( "Do not load plugins. This option overrides all other options." ), 0 },
+ { "noconnect", I18N_NOOP( "Disable auto-connection" ), 0 },
+ { "autoconnect <accounts>", I18N_NOOP( "Auto-connect the specified accounts. Use a comma-separated list\n"
+ "to auto-connect multiple accounts." ), 0 },
+ { "disable <plugins>", I18N_NOOP( "Do not load the specified plugin. Use a comma-separated list\n"
+ "to disable multiple plugins." ), 0 },
+ { "load-plugins <plugins>", I18N_NOOP( "Load only the specified plugins. Use a comma-separated list\n"
+ "to load multiple plugins. This option has no effect when\n"
+ "--noplugins is set and overrides all other plugin related\n"
+ "command line options." ), 0 },
+// { "url <url>", I18N_NOOP( "Load the given Kopete URL" ), 0 },
+// { "!+[plugin]", I18N_NOOP( "Load specified plugins" ), 0 },
+ { "!+[URL]", I18N_NOOP("URLs to pass to kopete / emoticon themes to install"), 0},
+ KCmdLineLastOption
+};
+
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData( "kopete", I18N_NOOP("Kopete"),
+ KOPETE_VERSION_STRING, description, KAboutData::License_GPL,
+ I18N_NOOP("(c) 2001-2004, Duncan Mac-Vicar Prett\n(c) 2002-2005, Kopete Development Team"), "kopete-devel@kde.org", "http://kopete.kde.org");
+
+ aboutData.addAuthor ( "Duncan Mac-Vicar Prett", I18N_NOOP("Developer and Project founder"), "duncan@kde.org", "http://www.mac-vicar.org/~duncan" );
+ aboutData.addAuthor ( "Andre Duffeck", I18N_NOOP("Developer, Yahoo plugin maintainer"), "andre@duffeck.de" );
+ aboutData.addAuthor ( "Andy Goossens", I18N_NOOP("Developer"), "andygoossens@telenet.be" );
+ aboutData.addAuthor ( "Chetan Reddy", I18N_NOOP("Developer, Yahoo"), "chetan13@gmail.com" );
+ aboutData.addAuthor ( "Chris Howells", I18N_NOOP("Developer, Connection status plugin author"), "howells@kde.org", "http://chrishowells.co.uk");
+ aboutData.addAuthor ( "Cláudio da Silveira Pinheiro", I18N_NOOP("Developer, Video device support"), "taupter@gmail.com", "http://taupter.homelinux.org" );
+ aboutData.addAuthor ( "Gregg Edghill", I18N_NOOP("Developer, MSN"), "gregg.edghill@gmail.com");
+ aboutData.addAuthor ( "Grzegorz Jaskiewicz", I18N_NOOP("Developer, Gadu plugin maintainer"), "gj@pointblue.com.pl" );
+ aboutData.addAuthor ( "Jason Keirstead", I18N_NOOP("Developer"), "jason@keirstead.org", "http://www.keirstead.org");
+ aboutData.addAuthor ( "Matt Rogers", I18N_NOOP("Lead Developer, AIM and ICQ plugin maintainer"), "mattr@kde.org" );
+ aboutData.addAuthor ( "Michel Hermier", I18N_NOOP("IRC plugin maintainer"), "michel.hermier@wanadoo.fr" );
+ aboutData.addAuthor ( "Michaël Larouche", I18N_NOOP("Lead Developer"), "larouche@kde.org", "http://www.tehbisnatch.org/" );
+ aboutData.addAuthor ( "Olivier Goffart", I18N_NOOP("Lead Developer, MSN plugin maintainer"), "ogoffart @ kde.org");
+ aboutData.addAuthor ( "Ollivier Lapeyre Johann", I18N_NOOP("Artist / Developer, Artwork maintainer"), "johann.ollivierlapeyre@gmail.com" );
+ aboutData.addAuthor ( "Richard Smith", I18N_NOOP("Developer, UI maintainer"), "kde@metafoo.co.uk" );
+ aboutData.addAuthor ( "Till Gerken", I18N_NOOP("Developer, Jabber plugin maintainer"), "till@tantalo.net");
+ aboutData.addAuthor ( "Will Stephenson", I18N_NOOP("Lead Developer, GroupWise maintainer"), "lists@stevello.free-online.co.uk" );
+
+ aboutData.addCredit ( "Vally8", I18N_NOOP("Konki style author"), "vally8@gmail.com", "http://vally8.free.fr/" );
+ aboutData.addCredit ( "Tm_T", I18N_NOOP("Hacker style author"), "jussi.kekkonen@gmail.com");
+ aboutData.addCredit ( "Luciash d' Being", I18N_NOOP("Kopete's icon author") );
+ aboutData.addCredit ( "Steve Cable", I18N_NOOP("Sounds") );
+ aboutData.addCredit ( "Jessica Hall", I18N_NOOP("Kopete Docugoddess, Bug and Patch Testing.") );
+ aboutData.addCredit ( "Justin Karneges", I18N_NOOP("Iris Jabber Backend Library") );
+ aboutData.addCredit ( "Tom Linsky", I18N_NOOP("OscarSocket author"), "twl6@po.cwru.edu" );
+ aboutData.addCredit ( "Olaf Lueg", I18N_NOOP("Kmerlin MSN code") );
+ aboutData.addCredit ( "Nick Betcher", I18N_NOOP("Former developer, project co-founder"), "nbetcher@kde.org");
+ aboutData.addCredit ( "Ryan Cumming", I18N_NOOP("Former developer"), "ryan@kde.org" );
+ aboutData.addCredit ( "Stefan Gehn", I18N_NOOP("Former developer"), "metz@gehn.net", "http://metz.gehn.net" );
+ aboutData.addCredit ( "Martijn Klingens", I18N_NOOP("Former developer"), "klingens@kde.org" );
+ aboutData.addCredit ( "Andres Krapf", I18N_NOOP("Former developer"), "dae@chez.com" );
+ aboutData.addCredit ( "Carsten Pfeiffer", I18N_NOOP("Misc bugfixes and enhancements"), "pfeiffer@kde.org" );
+ aboutData.addCredit ( "Zack Rusin", I18N_NOOP("Former developer, original Gadu plugin author"), "zack@kde.org" );
+ aboutData.addCredit ( "Richard Stellingwerff", I18N_NOOP("Former developer"), "remenic@linuxfromscratch.org");
+ aboutData.addCredit ( "Daniel Stone", I18N_NOOP("Former developer, Jabber plugin author"), "daniel@fooishbar.org", "http://fooishbar.org");
+ aboutData.addCredit ( "Chris TenHarmsel", I18N_NOOP("Former developer, Oscar plugin"), "tenharmsel@users.sourceforge.net");
+ aboutData.addCredit ( "Hendrik vom Lehn", I18N_NOOP("Former developer"), "hennevl@hennevl.de", "http://www.hennevl.de");
+ aboutData.addCredit ( "Gav Wood", I18N_NOOP("Former developer and WinPopup maintainer"), "gav@indigoarchive.net" );
+
+ aboutData.setTranslator( I18N_NOOP("_: NAME OF TRANSLATORS\nYour names"),
+ I18N_NOOP("_: EMAIL OF TRANSLATORS\nYour emails") );
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options ); // Add our own options.
+ KUniqueApplication::addCmdLineOptions();
+
+ KopeteApplication kopete;
+ new KIMIfaceImpl();
+ kapp->dcopClient()->registerAs( "kopete", false );
+ kapp->dcopClient()->setDefaultObject( (new KopeteIface())->objId() ); // Has to be called before exec
+
+ kopete.exec();
+}
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/systemtray.cpp b/kopete/kopete/systemtray.cpp
new file mode 100644
index 00000000..5ed018c5
--- /dev/null
+++ b/kopete/kopete/systemtray.cpp
@@ -0,0 +1,435 @@
+/*
+ systemtray.cpp - Kopete Tray Dock Icon
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "systemtray.h"
+
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qregexp.h>
+
+#include <kwin.h>
+#include <kaboutdata.h>
+#include <kactioncollection.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include "kopeteuiglobal.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteballoon.h"
+#include "kopeteprefs.h"
+#include "kopetemetacontact.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontact.h"
+#include "kopetewindow.h"
+
+KopeteSystemTray* KopeteSystemTray::s_systemTray = 0L;
+
+KopeteSystemTray* KopeteSystemTray::systemTray( QWidget *parent, const char* name )
+{
+ if( !s_systemTray )
+ s_systemTray = new KopeteSystemTray( parent, name );
+
+ return s_systemTray;
+}
+
+KopeteSystemTray::KopeteSystemTray(QWidget* parent, const char* name)
+ : KSystemTray(parent,name)
+{
+// kdDebug(14010) << "Creating KopeteSystemTray" << endl;
+ QToolTip::add( this, kapp->aboutData()->shortDescription() );
+
+ mIsBlinkIcon = false;
+ mIsBlinking = false;
+ mBlinkTimer = new QTimer(this, "mBlinkTimer");
+
+ mKopeteIcon = loadIcon("kopete");
+
+ connect(mBlinkTimer, SIGNAL(timeout()), this, SLOT(slotBlink()));
+ connect(Kopete::ChatSessionManager::self() , SIGNAL(newEvent(Kopete::MessageEvent*)),
+ this, SLOT(slotNewEvent(Kopete::MessageEvent*)));
+ connect(KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(slotConfigChanged()));
+
+ connect(Kopete::AccountManager::self(),
+ SIGNAL(accountOnlineStatusChanged(Kopete::Account *,
+ const Kopete::OnlineStatus &, const Kopete::OnlineStatus &)),
+ this, SLOT(slotReevaluateAccountStates()));
+
+ // the slot called by default by the quit action, KSystemTray::maybeQuit(),
+ // just closes the parent window, which is hard to distinguish in that window's closeEvent()
+ // from a click on the window's close widget
+ // in the quit case, we want to quit the application
+ // in the close widget click case, we only want to hide the parent window
+ // so instead, we make it call our general purpose quit slot on the window, which causes a window close and everything else we need
+ // KDE4 - app will have to listen for quitSelected instead
+ KAction *quit = actionCollection()->action( "file_quit" );
+ quit->disconnect();
+ KopeteWindow *myParent = static_cast<KopeteWindow *>( parent );
+ connect( quit, SIGNAL( activated() ), myParent, SLOT( slotQuit() ) );
+
+ //setPixmap(mKopeteIcon);
+ slotReevaluateAccountStates();
+ slotConfigChanged();
+
+ m_balloon=0l;
+}
+
+KopeteSystemTray::~KopeteSystemTray()
+{
+// kdDebug(14010) << "[KopeteSystemTray] ~KopeteSystemTray" << endl;
+// delete mBlinkTimer;
+ Kopete::UI::Global::setSysTrayWId( 0 );
+}
+
+void KopeteSystemTray::mousePressEvent( QMouseEvent *me )
+{
+ if (
+ (me->button() == QEvent::MidButton ||
+ (me->button() == QEvent::LeftButton && KopetePrefs::prefs()->trayflashNotifyLeftClickOpensMessage())) &&
+ mIsBlinking )
+ {
+ mouseDoubleClickEvent( me );
+ return;
+ }
+
+ KSystemTray::mousePressEvent( me );
+}
+
+void KopeteSystemTray::mouseDoubleClickEvent( QMouseEvent *me )
+{
+ if ( !mIsBlinking )
+ {
+ KSystemTray::mousePressEvent( me );
+ }
+ else
+ {
+ if(!mEventList.isEmpty())
+ mEventList.first()->apply();
+ }
+}
+
+void KopeteSystemTray::contextMenuAboutToShow( KPopupMenu *me )
+{
+ //kdDebug(14010) << k_funcinfo << "Called." << endl;
+ emit aboutToShowMenu( me );
+}
+
+void KopeteSystemTray::startBlink( const QString &icon )
+{
+ startBlink( KGlobal::iconLoader()->loadIcon( icon , KIcon::Panel ) );
+}
+
+void KopeteSystemTray::startBlink( const QPixmap &icon )
+{
+ mBlinkIcon = icon;
+ if ( mBlinkTimer->isActive() == false )
+ {
+ mIsBlinkIcon = true;
+ mIsBlinking = true;
+ mBlinkTimer->start( 1000, false );
+ }
+ else
+ {
+ mBlinkTimer->stop();
+ mIsBlinkIcon = true;
+ mIsBlinking = true;
+ mBlinkTimer->start( 1000, false );
+ }
+}
+
+void KopeteSystemTray::startBlink( const QMovie &movie )
+{
+ //kdDebug( 14010 ) << k_funcinfo << "starting movie." << endl;
+ const_cast<QMovie &>( movie ).unpause();
+ setMovie( movie );
+ mIsBlinking = true;
+}
+
+void KopeteSystemTray::startBlink()
+{
+ if ( mMovie.isNull() )
+ mMovie = KGlobal::iconLoader()->loadMovie( QString::fromLatin1( "newmessage" ), KIcon::Panel );
+
+ startBlink( mMovie );
+}
+
+void KopeteSystemTray::stopBlink()
+{
+ if ( movie() )
+ kdDebug( 14010 ) << k_funcinfo << "stopping movie." << endl;
+ else if ( mBlinkTimer->isActive() )
+ mBlinkTimer->stop();
+
+ if ( !mMovie.isNull() )
+ mMovie.pause();
+
+ mIsBlinkIcon = false;
+ mIsBlinking = false;
+ //setPixmap( mKopeteIcon );
+ slotReevaluateAccountStates();
+}
+
+void KopeteSystemTray::slotBlink()
+{
+ setPixmap( mIsBlinkIcon ? mKopeteIcon : mBlinkIcon );
+
+ mIsBlinkIcon = !mIsBlinkIcon;
+}
+
+void KopeteSystemTray::slotNewEvent( Kopete::MessageEvent *event )
+{
+ if( KopetePrefs::prefs()->useStack() )
+ {
+ mEventList.prepend( event );
+ mBalloonEventList.prepend( event );
+ }
+ else
+ {
+ mEventList.append( event );
+ mBalloonEventList.append( event );
+ }
+
+ connect(event, SIGNAL(done(Kopete::MessageEvent*)),
+ this, SLOT(slotEventDone(Kopete::MessageEvent*)));
+
+ if( event->message().manager() != 0 )
+ {
+ if( event->message().manager()->account() )
+ {
+ if( !event->message().manager()->account()->isAway() ||
+ KopetePrefs::prefs()->soundIfAway() )
+ {
+ addBalloon();
+ }
+ else
+ {
+ kdDebug(14000) << k_funcinfo << "Supressing balloon, account is away" << endl;
+ }
+ }
+ }
+ else
+ kdDebug(14000) << k_funcinfo << "NULL message().manager()!" << endl;
+
+ // tray animation
+ if ( KopetePrefs::prefs()->trayflashNotify() )
+ if( mBalloonEventList.count() == mEventList.count() )
+ startBlink();
+ else
+ stopBlink();
+}
+
+void KopeteSystemTray::slotEventDone(Kopete::MessageEvent *event)
+{
+ mEventList.remove(event);
+
+ removeBalloonEvent(event);
+
+ if(mEventList.isEmpty())
+ stopBlink();
+}
+
+void KopeteSystemTray::slotRemoveBalloon()
+{
+ removeBalloonEvent(mBalloonEventList.first());
+}
+
+void KopeteSystemTray::removeBalloonEvent(Kopete::MessageEvent *event)
+{
+ bool current= event==mBalloonEventList.first();
+ mBalloonEventList.remove(event);
+
+ if(current && m_balloon)
+ {
+ m_balloon->deleteLater();
+ m_balloon=0l;
+ if(!mBalloonEventList.isEmpty())
+ {
+ //delay the addBalloon to let the time to event be deleted
+ //in case a contact has been deleted cf Bug 100196
+ QTimer::singleShot(0, this, SLOT(addBalloon()));
+ }
+ else
+ {
+ if(KopetePrefs::prefs()->trayflashNotify() && !mEventList.isEmpty())
+ startBlink();
+ }
+ }
+}
+
+void KopeteSystemTray::addBalloon()
+{
+ /*kdDebug(14010) << k_funcinfo <<
+ m_balloon << ":" << KopetePrefs::prefs()->showTray() <<
+ ":" << KopetePrefs::prefs()->balloonNotify()
+ << ":" << !mBalloonEventList.isEmpty() << endl;*/
+
+ if( m_balloon && KopetePrefs::prefs()->useStack() )
+ {
+ m_balloon->deleteLater();
+ m_balloon=0l;
+ }
+
+ if( !m_balloon && KopetePrefs::prefs()->showTray() && KopetePrefs::prefs()->balloonNotify() && !mBalloonEventList.isEmpty() )
+ {
+ Kopete::Message msg = mBalloonEventList.first()->message();
+
+ if ( msg.from() )
+ {
+ QString msgText = squashMessage( msg );
+ kdDebug(14010) << k_funcinfo << "msg text=" << msgText << endl;
+
+ QString msgFrom;
+ if( msg.from()->metaContact() )
+ msgFrom = msg.from()->metaContact()->displayName();
+ else
+ msgFrom = msg.from()->contactId();
+
+ m_balloon = new KopeteBalloon(
+ i18n( "<qt><nobr><b>New Message from %1:</b></nobr><br><nobr>\"%2\"</nobr></qt>" )
+ .arg( QStyleSheet::escape( msgFrom ), msgText ), QString::null );
+ connect(m_balloon, SIGNAL(signalBalloonClicked()), mBalloonEventList.first() , SLOT(apply()));
+ connect(m_balloon, SIGNAL(signalButtonClicked()), mBalloonEventList.first() , SLOT(apply()));
+ connect(m_balloon, SIGNAL(signalIgnoreButtonClicked()), mBalloonEventList.first() , SLOT(ignore()));
+ connect(m_balloon, SIGNAL(signalTimeout()), this , SLOT(slotRemoveBalloon()));
+ m_balloon->setAnchor(mapToGlobal(pos()));
+ m_balloon->show();
+ KWin::setOnAllDesktops(m_balloon->winId(), true);
+ }
+ }
+}
+
+void KopeteSystemTray::slotConfigChanged()
+{
+// kdDebug(14010) << k_funcinfo << "called." << endl;
+ if ( KopetePrefs::prefs()->showTray() )
+ show();
+ else
+ hide(); // for users without kicker or a similar docking app
+}
+
+void KopeteSystemTray::slotReevaluateAccountStates()
+{
+ // If there is a pending message, we don't need to refresh the system tray now.
+ // This function will even be called when the animation will stop.
+ if ( mIsBlinking )
+ return;
+
+
+ //kdDebug(14010) << k_funcinfo << endl;
+ bool bOnline = false;
+ bool bAway = false;
+ bool bOffline = false;
+ Kopete::Contact *c = 0;
+
+ for (QPtrListIterator<Kopete::Account> it(Kopete::AccountManager::self()->accounts()); it.current(); ++it)
+ {
+ c = it.current()->myself();
+ if (!c)
+ continue;
+
+ if (c->onlineStatus().status() == Kopete::OnlineStatus::Online)
+ {
+ bOnline = true; // at least one contact is online
+ }
+ else if (c->onlineStatus().status() == Kopete::OnlineStatus::Away
+ || c->onlineStatus().status() == Kopete::OnlineStatus::Invisible)
+ {
+ bAway = true; // at least one contact is away or invisible
+ }
+ else // this account must be offline (or unknown, which I don't know how to handle)
+ {
+ bOffline = true;
+ }
+ }
+
+ if (!bOnline && !bAway && !bOffline) // special case, no accounts defined (yet)
+ bOffline = true;
+
+ if (bAway)
+ {
+ if (!bOnline && !bOffline) // none online and none offline -> all away
+ setPixmap(loadIcon("kopete_all_away"));
+ else
+ setPixmap(loadIcon("kopete_some_away"));
+ }
+ else if(bOnline)
+ {
+ /*if(bOffline) // at least one offline and at least one online -> some accounts online
+ setPixmap(loadIcon("kopete_some_online"));
+ else*/ // none offline and none away -> all online
+ setPixmap(mKopeteIcon);
+ }
+ else // none away and none online -> all offline
+ {
+ //kdDebug(14010) << k_funcinfo << "All Accounts offline!" << endl;
+ setPixmap(loadIcon("kopete_offline"));
+ }
+}
+
+
+QString KopeteSystemTray::squashMessage( const Kopete::Message& msg )
+{
+ QString msgText = msg.parsedBody();
+
+ QRegExp rx( "(<a.*>((http://)?(.+))</a>)" );
+ rx.setMinimal( true );
+ if ( rx.search( msgText ) == -1 )
+ {
+ // no URLs in text, just pick the first 30 chars of
+ // the parsed text if necessary. We used parsed text
+ // so that things like "<knuff>" show correctly
+ // Escape it after snipping it to not snip entities
+ msgText =msg.plainBody() ;
+ if( msgText.length() > 30 )
+ msgText = msgText.left( 30 ) + QString::fromLatin1( " ..." );
+ msgText=Kopete::Message::escape(msgText);
+ }
+ else
+ {
+ QString plainText = msg.plainBody();
+ if ( plainText.length() > 30 )
+ {
+ QString fullUrl = rx.cap( 2 );
+ QString shorterUrl;
+ if ( fullUrl.length() > 30 )
+ {
+ QString urlWithoutProtocol = rx.cap( 4 );
+ shorterUrl = urlWithoutProtocol.left( 27 )
+ + QString::fromLatin1( "... " );
+ }
+ else
+ {
+ shorterUrl = fullUrl.left( 27 )
+ + QString::fromLatin1( "... " );
+ }
+ // remove message text
+ msgText = QString::fromLatin1( "... " ) +
+ rx.cap( 1 ) +
+ QString::fromLatin1( " ..." );
+ // find last occurrence of URL (the one inside the <a> tag)
+ int revUrlOffset = msgText.findRev( fullUrl );
+ msgText.replace( revUrlOffset,
+ fullUrl.length(), shorterUrl );
+ }
+ }
+ return msgText;
+}
+
+#include "systemtray.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/kopete/systemtray.h b/kopete/kopete/systemtray.h
new file mode 100644
index 00000000..223cb173
--- /dev/null
+++ b/kopete/kopete/systemtray.h
@@ -0,0 +1,104 @@
+/*
+ systemtray.h - Kopete Tray Dock Icon
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SYSTEMTRAY_H
+#define SYSTEMTRAY_H
+
+#include <qpixmap.h>
+#include <qmovie.h>
+
+#include <ksystemtray.h>
+
+#include "kopetemessageevent.h"
+
+class QTimer;
+class QPoint;
+class KPopupMenu;
+class KActionMenu;
+class KopeteBalloon;
+
+/**
+ * @author Nick Betcher <nbetcher@kde.org>
+ *
+ * NOTE: This class is for use ONLY in libkopete! It is not public API, and
+ * is NOT supposed to remain binary compatible in the future!
+ */
+class KopeteSystemTray : public KSystemTray
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Retrieve the system tray instance
+ */
+ static KopeteSystemTray* systemTray( QWidget* parent = 0, const char* name = 0 );
+
+ ~KopeteSystemTray();
+
+ // One method, multiple interfaces :-)
+ void startBlink( const QString &icon );
+ void startBlink( const QPixmap &icon );
+ void startBlink( const QMovie &movie );
+ void startBlink();
+
+ void stopBlink();
+ bool isBlinking() const { return mIsBlinking; };
+ KPopupMenu *contextMenu() const { return KSystemTray::contextMenu(); };
+
+protected:
+ virtual void mousePressEvent( QMouseEvent *e );
+ virtual void mouseDoubleClickEvent( QMouseEvent *me );
+ virtual void contextMenuAboutToShow( KPopupMenu * );
+
+signals:
+ void aboutToShowMenu(KPopupMenu *am);
+
+private slots:
+ void slotBlink();
+ void slotNewEvent(Kopete::MessageEvent*);
+ void slotEventDone(Kopete::MessageEvent *);
+ void slotConfigChanged();
+ void slotReevaluateAccountStates();
+ void slotRemoveBalloon();
+ void addBalloon();
+
+private:
+ KopeteSystemTray( QWidget* parent, const char* name );
+ QString squashMessage( const Kopete::Message& msgText );
+ void removeBalloonEvent(Kopete::MessageEvent *);
+
+ QTimer *mBlinkTimer;
+ QPixmap mKopeteIcon;
+ QPixmap mBlinkIcon;
+ QMovie mMovie;
+
+ bool mIsBlinkIcon;
+ bool mIsBlinking;
+
+ static KopeteSystemTray* s_systemTray;
+
+ QPtrList<Kopete::MessageEvent> mEventList;
+ QPtrList<Kopete::MessageEvent> mBalloonEventList;
+ KopeteBalloon *m_balloon;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/kopete/x-kopete-emoticons.desktop b/kopete/kopete/x-kopete-emoticons.desktop
new file mode 100644
index 00000000..45f0ee79
--- /dev/null
+++ b/kopete/kopete/x-kopete-emoticons.desktop
@@ -0,0 +1,57 @@
+[Desktop Entry]
+Type=MimeType
+MimeType=application/x-kopete-emoticons
+DefaultApp=kopete
+Icon=kopete_emoticons
+Patterns=*.kopete-emoticons
+X-KDE-AutoEmbed=false
+Comment=Kopete Emoticon Archive
+Comment[be]=Ðрхіў Ñмацыйных значак Kopete
+Comment[bg]=Ðрхив на икони за Kopete
+Comment[bn]=কপেট অভিবà§à¦¯à¦•à§à¦¤à¦¿ পà§à¦°à¦¤à§€à¦• আরà§à¦•à¦¾à¦‡à¦­
+Comment[bs]=Kopete arhiva smajlija
+Comment[ca]=Arxiu d'emoticones Kopete
+Comment[cs]=Archív Kopete emotikonů
+Comment[da]=Kopete emotikon-arkiv
+Comment[de]=Kopete Emoticon-Archiv
+Comment[el]=ΑÏχειοθήκη emoticon του Kopete
+Comment[es]=Archivo de emoticonos de Kopete
+Comment[et]=Kopete emotikoniarhiiv
+Comment[eu]=Kopete emoticon artxiboa
+Comment[fa]=بایگانی تصاویر متحرک Kopete
+Comment[fi]=Kopeten hymiöarkisto
+Comment[fr]=Archive d'émoticônes pour Kopete
+Comment[gl]=Arquivo de Emoticonas de Kopete
+Comment[he]=×רכיון ערכת רגשות של Kopete
+Comment[hr]=Kopete Emoticon arhiva
+Comment[hu]=Kopete emotikon-archívum
+Comment[is]=Tilfynningatákn fyrir Kopete
+Comment[it]=Archivio emoticon di Kopete
+Comment[ja]=Kopete ã®æ„Ÿæƒ…アイコン集
+Comment[ka]=Kopeteს ემáƒáƒªáƒ˜áƒáƒ—რáƒáƒ áƒ¥áƒ˜áƒ•áƒ˜
+Comment[kk]=Kopete белгілер архиві
+Comment[km]=áž”áŸážŽáŸ’ណសារ​សញ្ញា​អារម្មណ០Kopete
+Comment[lt]=Kopete šypsniukų archyvas
+Comment[mk]=Ðрхива Ñо емотикони за Kopete
+Comment[nb]=Kopete fjesingarkiv
+Comment[nds]=Kopete-Snutenarchiv
+Comment[ne]=कोपेट इमोटिकन सङà¥à¤—à¥à¤°à¤¹
+Comment[nl]=Kopete Emoticon-archief
+Comment[nn]=Fjesingarkiv for Kopete
+Comment[pl]=Archiwum emotikon Kopete
+Comment[pt]=Arquivo de 'Emoticons' do Kopete
+Comment[pt_BR]=Pacote de Emoticon do Kopete
+Comment[ru]=Ðрхив значков Ð´Ð»Ñ Kopete
+Comment[se]=Kopete:a «emoticon»-vuorká
+Comment[sk]=Archív smajlíkov Kopete
+Comment[sl]=Arhiv z ikonami Äustev za Kopete
+Comment[sr]=Kopete-ова архива емотикона
+Comment[sr@Latn]=Kopete-ova arhiva emotikona
+Comment[sv]=Kopetes smilisarkiv
+Comment[ta]=Kopete எமோடிகà¯à®•à®¾à®©à¯ வளைவà¯
+Comment[tg]=Бойгонии Kopete Emoticon
+Comment[tr]=Kopete His Simgeleri ArÅŸivi
+Comment[uk]=Ðрхів емоційок Ð´Ð»Ñ Kopete
+Comment[zh_CN]=Kopete 表情存档
+Comment[zh_HK]=Kopete 表情符號集
+Comment[zh_TW]=Kopete 表情圖示檔
diff --git a/kopete/libkopete/API-TODO b/kopete/libkopete/API-TODO
new file mode 100644
index 00000000..ea63082b
--- /dev/null
+++ b/kopete/libkopete/API-TODO
@@ -0,0 +1,248 @@
+This file is a listing of (proposed) changes to be made to libkopete's API.
+
+
+ Buddy Icons:
+==============
+
+Some support for buddy icons is needed in libkopete. Maybe just a simple contact property and a
+metacontact property computed from it will do.
+
+
+ Properties:
+=============
+
+The current properties system is a bit of a mess. With the PluginDataObjects, the ContactProperties,
+KopeteContact's serializeProperties, and the various properties which are stored specially but
+shouldn't be (such as KopeteGroup::expanded) it's hard to know where you are. I (Richard) would like
+to replace this whole set of different property systems with a unified one. The way I see this
+working is as follows:
+- An object is created for each property. That object represents the property, and knows how to read
+ set store and manipulate that property.
+- Properties on objects supporting them (KopeteContact, KopeteMetaContact, KopeteGroup, and so on)
+ will be accessed in a uniform manner, eg:
+ contact(myProperty) = 42;
+ metaContact(displayName) = i18n("Ford Prefect");
+ The exact notation is not finalised yet. Maybe X[...] or X.property(...) would be clearer?
+- New types of properties with different serialization requirements and so on can easily be created
+- MetaContact properties can be computed from Contact properties (eg, for buddy icon, away message,
+ idle time and so on) by writing an appropriate property class.
+- This system would be extended to plugin configuration, so plugin authors can easily deal with
+ configuration variables without having to use strings as keys.
+
+
+ KopeteMessageManager:
+=======================
+
+KopeteMessageManager should allow any number of views to be attached, from 0
+to infinity.
+
+A lot of code should be moved from the KopeteViewManager to the KopeteMessageManager.
+Allowing the creation of chatwindow plugin more easy
+
+The chat window should be restructured so each ChatView object has its own
+send button. (-that's not a part of the libkopete api-)
+
+
+ KopeteMessage:
+================
+
+KopeteMessage should be reorganised to use QDomDocument or something similar
+to store its contents - the purpose of this is so libkopete can provide a
+uniform way of dealing with messages; the emoticon and link-adding filters
+don't then need to grok HTML in order to be able to mangle a message
+
+
+ KopeteContactList
+===================
+
+Add to KopeteMetaContact a statusPixmap() function to return the pixmap
+associated with its name. Take implementation from KopeteMetaContactLVI.
+Use this function in the Kopete::MimeSourceFactory so we get MCs being
+grayed if they're idle in tooltips too.
+
+KopeteContactList::removeGroup should remove the group from all
+metacontacts. Move code from KopeteContactListView to KopeteContactList for
+this.
+
+KopeteContactList::findContact and KopeteMetaContact::findContact should maybe be removed,
+or at least contains ptr to accounts insteads of id.
+
+KopeteContact::slotDeleteContact should be renamed, maybe return a bool to cancel the deletion of the contact.
+
+Add an iconName() function to contacts, metacontacts and accounts returning a string that
+can be put in an <img src="%1"> in any rich text to give the appropriate icon. See the
+MimeSourceFactory.
+
+KCL::selectedMetaContacts really doesn't belong here. A contact list shouldn't
+have a concept of selected items (this is action- and UI-specific knowledge).
+The only place this is used is when plugins' actions need to be enabled and
+disabled, or applied. Find a better way to do this UI-specific task.
+KCL::selectedGroups can be removed outright.
+
+
+ KopeteEmoticon
+================
+
+Allow emoticons and emoticon sets to be flagged as being for only a specific protocol.
+Allow the user to have more than one emoticon set enabled at once, and to set priorities.
+This way, the user will be able to have a base theme, a set of MSN-specific emoticons, a
+set of Gadu-Gadu-specific emoticons and so on.
+
+Possibly move emoticon support into a plugin?
+
+
+ KopeteOnlineStatus
+====================
+
+Add an Unknown status to KopeteOnlineStatus for when the status of a
+contact is unknown (in the case that they've not authorised you, or the
+protocol does not provide presence information) and a status for Unreachable
+(in the case that your account is offline, etc). The crucial difference is
+that a contact with Unknown status may be reachable (though that contact
+should probably be avoided for messaging unless nothing else is available).
+
+More granular away settings: see Bug 57297.
+The number of different global statuses (away / busy / be right back) should
+be extended to a configurable list where each element contains the name of the
+status, the default away message, and the KOS for every account)... though
+perhaps this would be better placed in an 'advanced status' plugin?
+
+Add a way to register automatically KOS. The code for the right-click menu in
+the Kopete account tray is duplicated all over the place; this should be
+done automatically by the account tray code.
+
+
+ KopeteAccount
+===============
+
+KopeteAccount::password should be split in two method: KopeteAccount::password which return the
+remembered password if any, but which does not try to ask it to the user. and getPassword which
+acts like the acutal function.
+
+<lilachaze> KopeteAccount will soon have no ::password. instead, use Kopete::PasswordedAccount,
+ and acct.password().request(...) or acct.password().cachedValue()
+
+
+ DCOP
+======
+The DCOP interface needs to be totally re-done so as to be useful, and to hopefully not rely on
+display names. Obsolete functions previously used only for DCOP should be removed from KopeteContactList
+where applicable.
+
+
+ KopeteTransferManager
+=======================
+
+The file transfer mechanisms should be available to plugins... ie, a plugin should be able to
+both initiate file transfers, and intercept and possibly cancel file transfer requests, the exact
+same as plugins can ( will ) be able to filter KopeteMessages ( see below ).
+
+
+ Message Processing
+====================
+
+Some sort of async message processing API needs to be designed and implemented
+Richard's proposal: (email questions to the list or to kde@metafoo.co.uk)
+- how do we order the various message filters available properly?
+ they give us a processing stage, and an offset within that stage. the
+ stages will be something like:
+ for an incoming message:
+ - Start - message was just received (History)
+ - ToSent - convert from received format to sent format (GPG)
+ - ToDesired - convert to how the user wants the message (Translator, AutoReplace)
+ ToDesired+Before - Highlight
+ - Format - decorate the message (without changing the content) (Links, Emoticons, TextEffect)
+ - Finished - message is now ready for display (ChatWindow / MessageQueue)
+ for an outgoing message:
+ - Start - user just hit Send
+ - Parse - process commands (CommandHandler, Alias, Now Listening)
+ Parse+After - History
+ - ToDesired - convert to how the user wanted to send (Translator, AutoReplace)
+ - Format - decorate the message (without changing the content) (TextEffect)
+ - ToSent - convert to the format to send in (GPG)
+ - Finished - message is now ready for sending (Protocols)
+ There should be a number of offsets defined for when to do the
+ processing, within a stage, such as:
+ - Before - before any other processing in this stage
+ - VeryEarly
+ - Early
+ - Normal
+ - Late
+ - VeryLate
+ - After - after any other processing in this stage
+- how do we construct a set of message filters for a particular message
+ manager?
+ - message filters register themselves with the filter manager, with a
+ message direction, a stage and an offset within that stage.
+ - each registered message filter factory gets queried (in stage/offset
+ order) by the object creating the filter chain. it either returns a
+ new filter for the chain, or returns NULL (meaning this filter is not
+ needed in this chain).
+ - the signals in one filter are connected to the slots in the next. any
+ sent/received message is handed to the first filter in the appropriate
+ chain.
+- how long does a filter chain live for?
+ - it's created when it's first needed (when a message is sent / received
+ and no chain already exists to process it, or when a chatwindow is
+ opened)
+ - it's reference counted
+ - the MessageQueue / ChatWindow holds a reference to its chains
+ - the chain knows how many messages are in it (the messages unregister
+ themselves when they're destroyed)
+ - this makes it trivial to implement 65803 - stay in chatwindows when no
+ window is open - just make the Kopete::Contact hold a reference to the
+ receive chain
+- interactions with the chat manager
+ - the chat manager (or possibly just 'chat') is an abstraction for a
+ conversation between our client/user and some other computer/user. it's
+ a bit like the message manager we have now, but more sophisticated.
+ - the send and receive chains are fundamentally linked - they are owned
+ by the same chat manager (which has a chainFor(MessageDirection)
+ function)
+ - when a chain's reference count drops to 0, it stays alive until
+ all the messages in it have been processed, but calls to
+ chainFor(Outgoing) will create a new chain. if we want, we can
+ guarantee messages from the old chain get sent over the wire before
+ ones from the new chain, but it's probably not essential.
+- interactions with a chat view
+ - the ChatWindow component above is actually the ChatWindowFilter. it's
+ owned by the filter chain, and so should not be a QWidget.
+ - when a chat view is closed, it drops its reference to the various
+ message chains. but the receive chain will still exist if there's an
+ incoming message that's still being processed. therefore:
+ - the chatwindow prompts you if you ask it to be closed and there are
+ messages left in its receive chain
+ - the chatwindow filter will *drop* messages that reach it if there's no
+ chatview available to send them to. but that's ok since the user will
+ already have been prompted about this.
+- problems with this design
+ - when the receive chain is closed (refcount drops to 0), it's not
+ necessarily the case that messages in it still need to be processed.
+ for instance, if you don't use the History plugin, or all the messages
+ are already past it, it probably doesn't matter if they're dropped. we
+ should somehow allow the filters to prevent destruction of the part of
+ the chain before them, and if none of them does, destroy it.
+
+
+
+ Invitation Handling Proposal:
+===============================
+
+Invitations is framework that allow others network applications (Games, Desktop
+Sharing, Video conference, [file transfer?], ...) to initiate the communication
+with kopete. (like Windows Messenger does)
+
+The user has two ways to initiate such as thing:
+
+- in the application itself, they could (with the help of KABC) select a
+ contactable contact; the invitaiton is transported to Kopete.
+- in Kopete, in the chat window, an "tools" menu, with all possible actions.
+
+
+
+ Blacklist support:
+====================
+
+<roie> BlackList API is added. Protocols maintainers, please check if a contact
+ is blocked before passing messages to MessageManager.
+ I will also attach the GUI to block() and unblock() in the near future. \ No newline at end of file
diff --git a/kopete/libkopete/Makefile.am b/kopete/libkopete/Makefile.am
new file mode 100644
index 00000000..47e6b9cf
--- /dev/null
+++ b/kopete/libkopete/Makefile.am
@@ -0,0 +1,71 @@
+if compile_LIBKOPETE_COMPAT
+COMPAT_DIR = compat
+COMPAT_LIBS = compat/libkopetecompat.la
+endif
+
+
+include ../../admin/Doxyfile.am
+DOXYGEN_REFERENCES = kio kdecore kdeui
+DOXYGEN_EXCLUDE = compat
+DOXYGEN_SET_PROJECT_NAME = libkopete
+
+SUBDIRS = $(COMPAT_DIR) private ui . avdevice
+
+METASOURCES = AUTO
+
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private \
+ -I$(top_srcdir)/kopete/libkopete/ui $(all_includes)
+
+lib_LTLIBRARIES = libkopete.la
+
+libkopete_la_SOURCES = knotification.cpp connectionmanager.cpp kopeteonlinestatus.cpp kopeteonlinestatusmanager.cpp \
+ kopeteprotocol.cpp kopetecontact.cpp kopetepluginmanager.cpp kopeteplugin.cpp \
+ kopetemessage.cpp kopetechatsession.cpp kopetechatsessionmanager.cpp \
+ kopetecontactlist.cpp kopetemetacontact.cpp kopeteawaydialog.cpp kopetetransfermanager.cpp \
+ kopetegroup.cpp kcautoconfigmodule.cpp kopeteaccountmanager.cpp kopeteaccount.cpp \
+ kopetecontactlistelement.cpp kopetecommandhandler.cpp kopeteaway.cpp \
+ kopeteawayaction.cpp kautoconfig.cpp kopetewalletmanager.cpp kopetecontactproperty.cpp \
+ kopetepassword.cpp kopeteglobal.cpp kopeteuiglobal.cpp kopetepasswordedaccount.cpp \
+ kopetemimetypehandler.cpp kopetetask.cpp kopetemimesourcefactory.cpp \
+ kopeteeventpresentation.cpp kopetenotifyevent.cpp kopetenotifydataobject.cpp kopeteblacklister.cpp \
+ kopetemessageevent.cpp kopetemessagehandler.cpp kopetemessagehandlerchain.cpp \
+ kopetesimplemessagehandler.cpp kopeteproperties.cpp kabcpersistence.cpp connectionmanager.skel \
+ clientiface.stub managedconnectionaccount.cpp networkstatuscommon.h kopeteconfig.kcfgc kopeteutils.cpp \
+ kopeteprefs.cpp kopetepicture.cpp webcamwidget.cpp
+
+libkopete_la_LDFLAGS = -no-undefined -version-info 1:0:0 $(all_libraries)
+libkopete_la_LIBADD = -lkabc ui/libkopeteui.la $(COMPAT_LIBS) $(LIB_KIO) $(LIB_XSS) $(LIB_XRENDER)
+
+kde_kcfg_DATA = kopete.kcfg
+
+#AM_CXXFLAGS = -DQT_PLUGIN
+#kde_widget_LTLIBRARIES = libkopetewidgets.la
+#libkopetewidgets_la_LDFLAGS = $(KDE_PLUGIN) -module $(all_libraries)
+#libkopetewidgets_la_LIBADD = $(LIB_KIO) libkopete.la ui/libkopeteui.la
+#libkopetewidgets_la_SOURCES = ui/kopetewidgets.cpp
+
+kopetewidgets.cpp: $(srcdir)/kopete.widgets
+ $(MAKEKDEWIDGETS) -o kopetewidgets.cpp $(srcdir)/kopete.widgets
+
+rcdir = $(kde_datadir)/kopete
+rc_DATA = kopetecommandui.rc
+
+servicetype_DATA = kopeteplugin.desktop kopeteprotocol.desktop kopeteui.desktop
+servicetypedir = $(kde_servicetypesdir)
+
+kopeteincludedir = $(includedir)/kopete
+kopeteinclude_HEADERS = kopeteaccount.h kopeteaccountmanager.h kopeteawayaction.h kopeteawaydialog.h kopeteaway.h \
+ kopeteblacklister.h kopetecommandhandler.h kopetecontact.h kopetecontactlistelement.h kopetecontactlist.h \
+ kopetecontactproperty.h kopeteeventpresentation.h kopete_export.h kopeteglobal.h kopetegroup.h \
+ kopetemessageevent.h kopetemessage.h kopetemessagehandlerchain.h kopetemessagehandler.h \
+ kopetechatsession.h kopetechatsessionmanager.h kopetemetacontact.h kopetemimetypehandler.h \
+ kopeteonlinestatus.h kopeteonlinestatusmanager.h kopetepasswordedaccount.h \
+ kopetepassword.h kopeteplugin.h kopeteprotocol.h kopetesimplemessagehandler.h kopetetask.h \
+ kopetetransfermanager.h kopeteuiglobal.h kabcpersistence.h managedconnectionaccount.h \
+ kopetenotifydataobject.h kopeteversion.h kopeteprefs.h kopetepicture.h webcamwidget.h \
+ kopetepluginmanager.h
+
+# vim: set noet:
+
+noinst_HEADERS = kopeteblacklister.h kopeteconfig.h
diff --git a/kopete/libkopete/PORTING b/kopete/libkopete/PORTING
new file mode 100644
index 00000000..b1d8e8a6
--- /dev/null
+++ b/kopete/libkopete/PORTING
@@ -0,0 +1,93 @@
+Porting from Kopete 0.9 to Kopete 0.10
+
+
+*) KopetePluginManager has been renamed Kopete::PluginManager
+ .) QMap<KPluginInfo *, KopetePlugin *> loadedPlugins( const QString &category = QString::null ) const;
+ the QMap has been replaced by a QPtrList<KopetePlugin> the KPluginInfo is not interesting here.
+ .) addressBookFields( KopetePlugin *p ) has been removed
+ .) pluginName, pluginId, pluginIcon has been removed (they are acessible from pluginInfo)
+ .) KPluginInfo *pluginInfo( KopetePlugin* ) has been added
+
+
+*) KopetePlugin has been renamed Kopete::Plugin
+ .) addressBookFields, addressBookIndexField and addAddressBookField have been removed (they were useless)
+ .) customChatWindowPopupActions( const KopeteMessage &, DOM::Node &node ) has been removed.
+ It was used to show the channel's menu when you right click on a IRC channel. Better to show the contact menu in the chatwindow for every contact.
+ .) KPluginInfo *pluginInfo()
+
+
+*) KopeteProtocol has been renamed Kopete::Protocol
+ .) protocolAction() has been removed
+ .) the broken canSendOffline() has been removed, and we can uses capabilities insteads
+ .) (set)RichTextCapabilities has been renamed to (set)Capabilities
+
+
+*) KopeteAccountManager has been renamed to Kopete::AccountManager
+ .) manager() has been renamed to self()
+ .) registerAccount is now public, and MUST be called manualy on account creation
+ .) autoConnect has been merged with connectAll which only connect accounts with the flag autoConnect
+ .) accountReady has been renamed again to accountRegistered
+ .) accountUnregistered signal takes now a const Account.
+
+
+*) KopeteAccount has been renamed to Kopete::Account
+ .) Account no longer inerits from PluginDataObject. you can access directly to the config with configGroup()
+ The config now uses the KConfig cache dirrectly and not internal cache.
+ .) loaded() has been removed since properties are available dirrectly in the constructor.
+ .) setAccountId and accountIdChanged has been removed
+ .) password() setPassword() rememberPassword() has been removed. if the account has a pssword, uses PasswordedAccount
+ .) addContactToMetaContact has been renamed createContact. it has loose his displayName param, uses the metacontact one if you want.
+ .) addContact has been splitted into addMetaContact and addContact
+ .) connect now takes the initial status as parameter
+ .) autoLogin has been renamed autoConnect for consistency
+
+
+*) KopeteContactList has been renamed to Kopete::ContactList
+ .) contactList() has been renamed to self()
+ .) getGroup has been renamed to group() or findGroup()
+ .) findContact now return a Contact
+ .) findContactByDisplayName has been renamed findMetaContactByDisplayName
+ .) metaContactDeleted signal has been renamed metaContactRemoved for consistency
+
+
+*) KopeteMetaContact has been renamed to Kopete::MetaContact
+ .) persistentDataChanged take no more argument
+ .) isTopLevel has been removed
+ .) groupSyncMode has been removed because it is broken
+
+
+*) KopeteContact has been renamed to Kopete::Contact
+ .) displayName has finaly been totaly removed
+ .) slotDeleteContact has been renamed to deleteContact
+ .) slotUserInfo has been renamed userInfo and is now pure virtual
+
+
+*) KopeteGroup has been renamed to Kopete::Group
+ .) renamed() signal has been renamed into displayNameChanged() for consistancy
+ .) internalName has been removed
+
+
+*) KopetePluginDataObject has been replaced Kopete::ContactListElement
+
+
+*) KopeteOnlineStatus has been renamed Kopete::OnlineStatus
+ .) caption has been removed (moved to OnlineStatusManager
+
+
+*) OnlineStatusIconCache has been replaced by OnlineStatusManager
+
+
+*) KopeteMessage => Kopete::Message
+
+
+*) KopeteMessageManager => Kopete::ChatSession
+ .) there are not anymore mmId()
+ .) user() has been renamed myself()
+ .) closed() has been renamed chatSessionDestroyed();
+ .) typingMsg has been renamed userTyping
+
+
+*) KopeteMessageManagerFactory => Kopete::ChatSessionManager
+ .) every function with messageManager has been renamed with ChatSession
+ .) addKopeteMessageManager => registerChatSession
+ \ No newline at end of file
diff --git a/kopete/libkopete/avdevice/Makefile.am b/kopete/libkopete/avdevice/Makefile.am
new file mode 100644
index 00000000..a234f797
--- /dev/null
+++ b/kopete/libkopete/avdevice/Makefile.am
@@ -0,0 +1,18 @@
+INCLUDES =$(GLINC) $(all_includes)
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private \
+ -I$(top_srcdir)/kopete/libkopete/ui $(all_includes)
+METASOURCES = AUTO
+lib_LTLIBRARIES = libkopete_videodevice.la
+noinst_LTLIBRARIES = libkvideoio.la
+libkopete_videodevice_la_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+
+noinst_HEADERS = kxv.h qvideo.h qvideostream.h videocontrol.h videodevice.h \
+ videodevicemodelpool.h videodevicepool.h videoinput.h \
+ sonix_compress.h bayer.h
+libkopete_videodevice_la_SOURCES = videocontrol.cpp videodevice.cpp \
+ videodevicemodelpool.cpp videodevicepool.cpp videoinput.cpp \
+ sonix_compress.cpp bayer.cpp
+libkvideoio_la_LDFLAGS = -no-undefined $(all_libraries) -version-info 1:0:0
+libkvideoio_la_SOURCES = kxv.cpp qvideo.cpp qvideostream.cpp
+libkvideoio_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(GLLIB)
diff --git a/kopete/libkopete/avdevice/bayer.cpp b/kopete/libkopete/avdevice/bayer.cpp
new file mode 100644
index 00000000..69189bae
--- /dev/null
+++ b/kopete/libkopete/avdevice/bayer.cpp
@@ -0,0 +1,118 @@
+/*
+ * BAYER2RGB24 ROUTINE TAKEN FROM:
+ *
+ * Sonix SN9C101 based webcam basic I/F routines
+ * Copyright (C) 2004 Takafumi Mizuno <taka-qce@ls-a.jp>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+void bayer2rgb24(unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT)
+{
+ long int i;
+ unsigned char *rawpt, *scanpt;
+ long int size;
+
+ rawpt = src;
+ scanpt = dst;
+ size = WIDTH*HEIGHT;
+
+ for ( i = 0; i < size; i++ )
+ {
+ if ( (i/WIDTH) % 2 == 0 )
+ {
+ if ( (i % 2) == 0 )
+ {
+ // B
+ if ( (i > WIDTH) && ((i % WIDTH) > 0) )
+ {
+ *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; // R
+ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+*(rawpt+WIDTH)+*(rawpt-WIDTH))/4; // G
+ *scanpt++ = *rawpt; // B
+ }
+ else
+ {
+ // first line or left column
+ *scanpt++ = *(rawpt+WIDTH+1); // R
+ *scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; // G
+ *scanpt++ = *rawpt; // B
+ }
+ }
+ else
+ {
+ // (B)G
+ if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) )
+ {
+ *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; // R
+ *scanpt++ = *rawpt; // G
+ *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; // B
+ }
+ else
+ {
+ // first line or right column
+ *scanpt++ = *(rawpt+WIDTH); // R
+ *scanpt++ = *rawpt; // G
+ *scanpt++ = *(rawpt-1); // B
+ }
+ }
+ }
+ else
+ {
+ if ( (i % 2) == 0 )
+ {
+ // G(R)
+ if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) )
+ {
+ *scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; // R
+ *scanpt++ = *rawpt; // G
+ *scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; // B
+ }
+ else
+ {
+ // bottom line or left column
+ *scanpt++ = *(rawpt+1); /* R */
+ *scanpt++ = *rawpt; /* G */
+ *scanpt++ = *(rawpt-WIDTH); /* B */
+ }
+ }
+ else
+ {
+ // R
+ if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) )
+ {
+ *scanpt++ = *rawpt; // R
+ *scanpt++ = (*(rawpt-1)+*(rawpt+1)+*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; // G
+ *scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; // B
+ }
+ else
+ {
+ // bottom line or right column
+ *scanpt++ = *rawpt; /* R */
+ *scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */
+ *scanpt++ = *(rawpt-WIDTH-1); /* B */
+ }
+ }
+ }
+ rawpt++;
+ }
+}
+
diff --git a/kopete/libkopete/avdevice/bayer.h b/kopete/libkopete/avdevice/bayer.h
new file mode 100644
index 00000000..af6d8baf
--- /dev/null
+++ b/kopete/libkopete/avdevice/bayer.h
@@ -0,0 +1,30 @@
+/*
+ * BAYER2RGB24 ROUTINE TAKEN FROM:
+ *
+ * Sonix SN9C101 based webcam basic I/F routines
+ * Copyright (C) 2004 Takafumi Mizuno <taka-qce@ls-a.jp>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+void bayer2rgb24 (unsigned char *dst, unsigned char *src, long int WIDTH, long int HEIGHT);
diff --git a/kopete/libkopete/avdevice/kxv.cpp b/kopete/libkopete/avdevice/kxv.cpp
new file mode 100644
index 00000000..661bdfad
--- /dev/null
+++ b/kopete/libkopete/avdevice/kxv.cpp
@@ -0,0 +1,711 @@
+/*
+ * KDE Xv interface
+ *
+ * Copyright (C) 2001 George Staikos (staikos@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+
+#include <qwindowdefs.h>
+#include <qwidget.h>
+
+#include <kdebug.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kxv.h"
+
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/StringDefs.h>
+#include <X11/Xatom.h>
+#ifdef HAVE_XSHM
+extern "C" {
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+}
+#endif
+
+#ifdef HAVE_LIBXV
+#include <X11/extensions/Xv.h>
+#include <X11/extensions/Xvlib.h>
+#endif
+
+#ifdef HAVE_LIBXVMC
+#include <X11/extensions/XvMC.h>
+#include <X11/extensions/XvMClib.h>
+#endif
+
+
+KXv::KXv()
+{
+ xv_adaptors = 0;
+ _devs.setAutoDelete(true);
+}
+
+
+KXv::~KXv()
+{
+ kdDebug() << "KXv::~KXv: Close Xv connection." << endl;
+ _devs.clear();
+
+#ifdef HAVE_LIBXV
+ if (xv_adaptors > 0)
+ XvFreeAdaptorInfo((XvAdaptorInfo *)xv_adaptor_info);
+#endif
+}
+
+
+KXvDeviceList& KXv::devices()
+{
+ return _devs;
+}
+
+
+bool KXv::haveXv()
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ unsigned int tmp;
+ if (Success != XvQueryExtension(qt_xdisplay(),
+ &tmp,
+ &tmp,
+ &tmp,
+ &tmp,
+ &tmp))
+ return false;
+
+ return true;
+#endif
+}
+
+
+KXv* KXv::connect(Drawable d)
+{
+ KXv *xvptr;
+
+ xvptr = new KXv;
+ if (!xvptr->init(d)) {
+ kdDebug() << "KXv::connect: Xv init failed." << endl;
+ delete xvptr;
+ return NULL;
+ }
+
+ kdDebug() << "KXv::connect: Xv init completed." << endl;
+ return xvptr;
+}
+
+
+bool KXv::init(Drawable d)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ if (Success != XvQueryExtension(qt_xdisplay(),
+ &xv_version,
+ &xv_release,
+ &xv_request,
+ &xv_event,
+ &xv_error)) {
+ kdWarning() << "KXv::init: Xv extension not available." << endl;
+ return false;
+ }
+
+#ifdef HAVE_LIBXVMC
+ // Causes crashes for some people.
+ // if (Success == XvMCQueryExtension(qt_xdisplay(),0,0)) {
+ // kdDebug() << "Found XvMC!" << endl;
+ // }
+#endif
+
+ if (Success != XvQueryAdaptors(qt_xdisplay(),
+ d,
+ &xv_adaptors,
+ (XvAdaptorInfo **)&xv_adaptor_info)) {
+ // Note technically fatal... what to do?
+ kdWarning() << "KXv::init: XvQueryAdaptors failed." << endl;
+ }
+
+ XvAdaptorInfo *ai = (XvAdaptorInfo *)xv_adaptor_info;
+
+ for (unsigned int i = 0; i < xv_adaptors; i++) {
+ KXvDevice *xvd = new KXvDevice;
+ xvd->xv_type = ai[i].type;
+ xvd->xv_port = ai[i].base_id;
+ xvd->xv_name = ai[i].name;
+ xvd->xv_adaptor = i;
+ xvd->xv_nvisualformats = ai[i].num_formats;
+ xvd->xv_visualformats = ai[i].formats;
+ if (ai[i].type & XvInputMask &&
+ ai[i].type & XvVideoMask ) {
+ kdDebug() << "KXv::init: Xv VideoMask port " << ai[i].base_id << " was found."
+ << " Device is: " << ai[i].name << "." << endl;
+ }
+ if (ai[i].type & XvInputMask &&
+ ai[i].type & XvImageMask ) {
+ kdDebug() << "KXv::init: Xv ImageMask port " << ai[i].base_id << " was found."
+ << " Device is: " << ai[i].name << "." << endl;
+ }
+
+ if (xvd->init()) {
+ _devs.append(xvd);
+ } else {
+ delete xvd;
+ }
+ }
+
+ return true;
+#endif
+}
+
+bool KXvDevice::grabStill(QImage* /*pix*/, int /*dw*/, int /*dh*/)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ return false;
+#endif
+}
+
+int KXvDevice::displayImage(QWidget *widget, const unsigned char *const data, int w, int h, int dw, int dh)
+{
+ if (!widget)
+ return -1;
+ return displayImage(widget->winId(), data, w, h, 0, 0, w, h, dw, dh);
+}
+
+int KXvDevice::displayImage(QWidget *widget, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh)
+{
+ if (!widget)
+ return -1;
+ return displayImage(widget->winId(), data, w, h, x, y, sw, sh, dw, dh);
+}
+
+int KXvDevice::displayImage(Window win, const unsigned char *const data, int w, int h, int dw, int dh)
+{
+ return displayImage(win, data, w, h, 0, 0, w, h, dw, dh);
+}
+
+int KXvDevice::displayImage(Window win, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh)
+{
+#ifndef HAVE_LIBXV
+ return -1;
+#else
+ Q_ASSERT(xv_port != -1);
+
+ // Must be a video capable device!
+ if (!(xv_type & XvImageMask) || !(xv_type & XvInputMask)) {
+ kdWarning() << "KXvDevice::displayImage: This is not a video capable device." << endl;
+ return -1;
+ }
+
+ if (xv_image_w != w || xv_image_h != h || !xv_image)
+ rebuildImage(w, h, _shm);
+
+ if (!xv_image)
+ return -1;
+
+ if (win != xv_last_win && xv_gc) {
+ XFreeGC(qt_xdisplay(), xv_gc);
+ xv_gc = 0;
+ }
+
+ if (!xv_gc) {
+ xv_last_win = win;
+ xv_gc = XCreateGC(qt_xdisplay(), win, 0, NULL);
+ }
+
+ int rc = 0;
+ Q_ASSERT(xv_image);
+ if (!_shm) {
+ static_cast<XvImage*>(xv_image)->data =
+ (char *)const_cast<unsigned char*>(data);
+ rc = XvPutImage(qt_xdisplay(), xv_port, win, xv_gc,
+ static_cast<XvImage*>(xv_image), x, y, sw, sh, 0, 0, dw, dh);
+ } else {
+#ifdef HAVE_XSHM
+ memcpy(static_cast<XvImage*>(xv_image)->data, data, static_cast<XvImage*>(xv_image)->data_size);
+ rc = XvShmPutImage(qt_xdisplay(), xv_port, win, xv_gc,
+ static_cast<XvImage*>(xv_image), x, y, sw, sh, 0, 0, dw, dh, 0);
+#endif
+ }
+
+ XSync(qt_xdisplay(), False);
+ return rc;
+#endif
+}
+
+
+bool KXvDevice::startVideo(QWidget *w, int dw, int dh)
+{
+ if (!w) return false;
+ return startVideo(w->winId(), dw, dh);
+}
+
+
+bool KXvDevice::startVideo(Window w, int dw, int dh)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ int sx = 0, sy = 0, dx = 0, dy = 0, sw = dw, sh = dh;
+
+ // Must be a video capable device!
+ if (!(xv_type & XvVideoMask) || !(xv_type & XvInputMask)) {
+ kdWarning() << "KXvDevice::startVideo: This is not a video capable device." << endl;
+ return false;
+ }
+
+ if (videoStarted) stopVideo();
+
+ if (xv_port == -1) {
+ kdWarning() << "KXvDevice::startVideo: No xv_port." << endl;
+ return false;
+ }
+
+ if (w != xv_last_win && xv_gc) {
+ XFreeGC(qt_xdisplay(), xv_gc);
+ xv_gc = 0;
+ }
+
+ if (!xv_gc) {
+ xv_last_win = w;
+ xv_gc = XCreateGC(qt_xdisplay(), w, 0, NULL);
+ }
+
+ if (-1 != xv_encoding) {
+ sw = ((XvEncodingInfo *)xv_encoding_info)[xv_encoding].width;
+ sh = ((XvEncodingInfo *)xv_encoding_info)[xv_encoding].height;
+ }
+
+ // xawtv does this here:
+ // ng_ratio_fixup(&dw, &dh, &dx, &dy);
+
+ kdDebug() << "XvPutVideo: " << qt_xdisplay()
+ << " " << xv_port << " " << w << " " << xv_gc
+ << " " << sx << " " << sy << " " << sw << " " << sh
+ << " " << dx << " " << dy << " " << dw << " " << dh << endl;
+ XvPutVideo(qt_xdisplay(), xv_port, w, xv_gc, sx, sy, sw, sh, dx, dy, dw, dh);
+
+ videoStarted = true;
+ videoWindow = w;
+ return true;
+#endif
+}
+
+bool KXvDevice::stopVideo()
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ if (!videoStarted)
+ return true;
+ if (xv_port == -1) {
+ kdWarning() << "KXvDevice::stopVideo: No xv_port." << endl;
+ return false;
+ }
+
+ XvStopVideo(qt_xdisplay(), xv_port, videoWindow);
+ videoStarted = false;
+ return true;
+#endif
+}
+
+
+KXvDevice::KXvDevice()
+{
+ xv_encoding_info = NULL;
+ xv_formatvalues = NULL;
+ xv_attr = NULL;
+ xv_port = -1;
+ xv_encoding = -1;
+ xv_name = QString::null;
+ xv_type = -1;
+ xv_adaptor = -1;
+ _shm = false;
+#ifdef HAVE_LIBXV
+ xv_imageformat = 0x32595559; // FIXME (YUY2)
+#ifdef HAVE_XSHM
+ if (!XShmQueryExtension(qt_xdisplay())) {
+ _haveShm = false;
+ } else {
+ _shm = true;
+ _haveShm = true;
+ }
+ xv_shminfo = new XShmSegmentInfo;
+#else
+ xv_shminfo = 0;
+#endif
+#endif
+ xv_gc = 0;
+ xv_last_win = 0;
+ videoStarted = false;
+ _attrs.setAutoDelete(true);
+ xv_image = 0;
+ xv_image_w = 320;
+ xv_image_h = 200;
+}
+
+
+KXvDevice::~KXvDevice()
+{
+#ifdef HAVE_LIBXV
+ _attrs.clear();
+ if (videoStarted) stopVideo();
+ if (xv_encoding_info)
+ XvFreeEncodingInfo((XvEncodingInfo *)xv_encoding_info);
+ XFree(xv_formatvalues);
+ XFree(xv_attr);
+#ifdef HAVE_XSHM
+ delete (XShmSegmentInfo*)xv_shminfo;
+#endif
+ destroyImage();
+#endif
+ if (xv_gc)
+ XFreeGC(qt_xdisplay(), xv_gc);
+
+#ifdef HAVE_LIBXV
+ if (xv_port != -1)
+ XvUngrabPort(qt_xdisplay(), xv_port, CurrentTime);
+#endif
+}
+
+
+bool KXvDevice::init()
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ assert(xv_port != -1); // make sure we were prepped by KXv already.
+
+ if (XvGrabPort(qt_xdisplay(), xv_port, CurrentTime)) {
+ kdWarning() << "KXvDevice::init(): Unable to grab Xv port." << endl;
+ return false;
+ }
+
+ if (Success != XvQueryEncodings(qt_xdisplay(),
+ xv_port,
+ &xv_encodings,
+ (XvEncodingInfo **)&xv_encoding_info)) {
+ kdWarning() << "KXvDevice::init: Xv QueryEncodings failed. Dropping Xv support for this device." << endl;
+ return false;
+ }
+
+ // Package the encodings up nicely
+ for (unsigned int i = 0; i < xv_encodings; i++) {
+ //kdDebug() << "Added encoding: " << ((XvEncodingInfo *)xv_encoding_info)[i].name << endl;
+ _encodingList << ((XvEncodingInfo *)xv_encoding_info)[i].name;
+ }
+
+ xv_attr = XvQueryPortAttributes(qt_xdisplay(),
+ xv_port,
+ &xv_encoding_attributes);
+ XvAttribute *xvattr = (XvAttribute *)xv_attr;
+ kdDebug() << "Attributes for port " << xv_port << endl;
+ for (int i = 0; i < xv_encoding_attributes; i++) {
+ assert(xvattr);
+ kdDebug() << " -> " << xvattr[i].name
+ << ((xvattr[i].flags & XvGettable) ? " get" : "")
+ << ((xvattr[i].flags & XvSettable) ? " set" : "")
+ << " Range: " << xvattr[i].min_value
+ << " -> " << xvattr[i].max_value << endl;
+
+ KXvDeviceAttribute *xvda = new KXvDeviceAttribute;
+ xvda->name = xvattr[i].name;
+ xvda->min = xvattr[i].min_value;
+ xvda->max = xvattr[i].max_value;
+ xvda->flags = xvattr[i].flags;
+ _attrs.append(xvda);
+ }
+
+ XvImageFormatValues *fo;
+ fo = XvListImageFormats(qt_xdisplay(), xv_port, &xv_formats);
+ xv_formatvalues = (void *)fo;
+ kdDebug() << "Image formats for port " << xv_port << endl;
+ for (int i = 0; i < xv_formats; i++) {
+ assert(fo);
+ QString imout;
+ imout.sprintf(" 0x%x (%c%c%c%c) %s",
+ fo[i].id,
+ fo[i].id & 0xff,
+ (fo[i].id >> 8) & 0xff,
+ (fo[i].id >> 16) & 0xff,
+ (fo[i].id >> 24) & 0xff,
+ ((fo[i].format == XvPacked) ?
+ "Packed" : "Planar"));
+ kdDebug() << imout << endl;
+ }
+
+ kdDebug() << "Disabling double buffering." << endl;
+ setAttribute("XV_DOUBLE_BUFFER", 0);
+
+ return true;
+#endif
+}
+
+
+bool KXvDevice::supportsWidget(QWidget *w)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ for (int i = 0; i < xv_nvisualformats; i++) {
+ if (static_cast<XvFormat*>(xv_visualformats)[i].visual_id
+ == static_cast<Visual*>(w->x11Visual())->visualid) {
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+
+bool KXvDevice::isVideoSource()
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ if (xv_type & XvVideoMask && xv_type & XvInputMask)
+ return true;
+ return false;
+#endif
+}
+
+
+bool KXvDevice::isImageBackend()
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ if (xv_type & XvImageMask && xv_type & XvInputMask)
+ return true;
+ return false;
+#endif
+}
+
+
+const KXvDeviceAttributes& KXvDevice::attributes()
+{
+ return _attrs;
+}
+
+
+bool KXvDevice::getAttributeRange(const QString& attribute, int *min, int *max)
+{
+ for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) {
+ if (at->name == attribute) {
+ if (min) *min = at->min;
+ if (max) *max = at->max;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool KXvDevice::getAttribute(const QString& attribute, int *val)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) {
+ if (at->name == attribute) {
+ if (val)
+ XvGetPortAttribute(qt_xdisplay(), xv_port, at->atom(), val);
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+
+bool KXvDevice::setAttribute(const QString& attribute, int val)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ for (KXvDeviceAttribute *at = _attrs.first(); at != NULL; at = _attrs.next()) {
+ if (at->name == attribute) {
+ XvSetPortAttribute(qt_xdisplay(), xv_port, at->atom(), val);
+ XSync(qt_xdisplay(), False);
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+
+const QString& KXvDevice::name() const
+{
+ return xv_name;
+}
+
+
+int KXvDevice::port() const
+{
+ return xv_port;
+}
+
+const QStringList& KXvDevice::encodings() const
+{
+ return _encodingList;
+}
+
+bool KXvDevice::encoding(QString& encoding)
+{
+#ifndef HAVE_LIBXV
+ return false;
+#else
+ XvEncodingID enc;
+
+ for (KXvDeviceAttribute *at = _attrs.first(); at != 0L; at = _attrs.next()) {
+ if (at->name == "XV_ENCODING") {
+ XvGetPortAttribute(qt_xdisplay(), xv_port, at->atom(), (int*)&enc);
+ kdDebug() << "KXvDevice: encoding: " << enc << endl;
+ encoding = enc;
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+bool KXvDevice::setEncoding(const QString& e)
+{
+#ifdef HAVE_LIBXV
+ for (unsigned int i = 0; i < xv_encodings; i++) {
+ if (e == ((XvEncodingInfo *)xv_encoding_info)[i].name) {
+ xv_encoding = i;
+ return setAttribute("XV_ENCODING",
+ ((XvEncodingInfo *)xv_encoding_info)[i].encoding_id);
+ }
+ }
+#endif
+ return false;
+}
+
+bool KXvDevice::videoPlaying() const
+{
+ return videoStarted;
+}
+
+
+bool KXvDevice::useShm(bool on)
+{
+#ifndef HAVE_XSHM
+ if (on) {
+ return false;
+ }
+#endif
+ if (!_haveShm) {
+ return false;
+ }
+ if (_shm != on)
+ rebuildImage(xv_image_w, xv_image_h, on);
+ if (_haveShm) // This can change in rebuildImage()
+ _shm = on;
+ return _shm;
+}
+
+
+bool KXvDevice::usingShm() const
+{
+ return _shm;
+}
+
+
+#include <unistd.h>
+void KXvDevice::rebuildImage(int w, int h, bool shm)
+{
+ if (xv_image) {
+ destroyImage();
+ }
+#ifdef HAVE_LIBXV
+ if (!shm) {
+ xv_image = (void*)XvCreateImage(qt_xdisplay(), xv_port, xv_imageformat,
+ 0, w, h);
+ if (!xv_image) {
+ kdWarning() << "KXvDevice::rebuildImage: XvCreateImage failed." << endl;
+ }
+ } else {
+#ifdef HAVE_XSHM
+ memset(xv_shminfo, 0, sizeof(XShmSegmentInfo));
+ xv_image = (void*)XvShmCreateImage(qt_xdisplay(), xv_port, xv_imageformat,
+ 0, w, h, static_cast<XShmSegmentInfo*>(xv_shminfo));
+ if (!xv_image) {
+ kdWarning() << "KXvDevice::rebuildImage: Error using SHM with Xv! Disabling SHM..." << endl;
+ _haveShm = false;
+ _shm = false;
+ xv_image = (void*)XvCreateImage(qt_xdisplay(), xv_port, xv_imageformat,
+ 0, w, h);
+ if (!xv_image) {
+ kdWarning() << "KXvDevice::rebuildImage: XvCreateImage failed." << endl;
+ }
+ } else {
+ static_cast<XShmSegmentInfo*>(xv_shminfo)->shmid =
+ shmget(IPC_PRIVATE,
+ static_cast<XvImage*>(xv_image)->data_size,
+ IPC_CREAT | 0600);
+ static_cast<XShmSegmentInfo*>(xv_shminfo)->shmaddr =
+ (char*)shmat(static_cast<XShmSegmentInfo*>(xv_shminfo)->shmid, 0, 0);
+ static_cast<XShmSegmentInfo*>(xv_shminfo)->readOnly = True;
+ static_cast<XvImage*>(xv_image)->data =
+ static_cast<XShmSegmentInfo*>(xv_shminfo)->shmaddr;
+ XShmAttach(qt_xdisplay(), static_cast<XShmSegmentInfo*>(xv_shminfo));
+ XSync(qt_xdisplay(), False);
+ shmctl(static_cast<XShmSegmentInfo*>(xv_shminfo)->shmid, IPC_RMID, 0);
+ }
+#endif
+ }
+ Q_ASSERT(xv_image != 0);
+ xv_image_w = w;
+ xv_image_h = h;
+#endif
+}
+
+
+void KXvDevice::destroyImage()
+{
+#ifdef HAVE_LIBXV
+ if (!_shm) {
+ if (xv_image) {
+ static_cast<XvImage*>(xv_image)->data = 0;
+ }
+ } else {
+ if (xv_image) {
+#ifdef HAVE_XSHM
+ shmdt(static_cast<XShmSegmentInfo*>(xv_shminfo)->shmaddr);
+#endif
+ }
+ }
+ XFree(xv_image);
+ xv_image = 0;
+#endif
+}
+
+
+Atom KXvDeviceAttribute::atom()
+{
+ return XInternAtom(qt_xdisplay(), name.latin1(), False);
+}
diff --git a/kopete/libkopete/avdevice/kxv.h b/kopete/libkopete/avdevice/kxv.h
new file mode 100644
index 00000000..d386cda9
--- /dev/null
+++ b/kopete/libkopete/avdevice/kxv.h
@@ -0,0 +1,260 @@
+/*
+ * KDE Xv interface
+ *
+ * Copyright (C) 2001 George Staikos (staikos@kde.org)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KXV_H
+#define __KXV_H
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+
+class QWidget;
+class QImage;
+
+class KXvPrivate;
+class KXvDevice;
+class KXvDevicePrivate;
+
+typedef QPtrList<KXvDevice> KXvDeviceList;
+
+
+class KXv
+{
+public:
+ ~KXv();
+
+ /*
+ * To get access to the Xv extension, you call this method. It will return
+ * a KXv* object on success, or NULL if it can't connect.
+ *
+ * d is typically the Window ID
+ */
+ static KXv *connect(Drawable d);
+
+ /*
+ * True if we can connect to the Xv extension.
+ */
+ static bool haveXv();
+
+ /*
+ * Return the list of Xv devices
+ */
+ KXvDeviceList& devices();
+
+protected:
+ KXv();
+ bool init(Drawable d);
+
+ /*** XV info ***/
+ unsigned int xv_version, xv_release, xv_request, xv_event, xv_error;
+ unsigned int xv_adaptors;
+ void *xv_adaptor_info;
+
+ KXvDeviceList _devs;
+
+private:
+ KXvPrivate *d;
+};
+
+
+
+class KXvDeviceAttribute
+{
+public:
+ QString name;
+ int min;
+ int max;
+ int flags;
+
+ Atom atom();
+};
+
+typedef QPtrList<KXvDeviceAttribute> KXvDeviceAttributes;
+
+
+class KXvDevice
+{
+ friend class KXv;
+public:
+
+ KXvDevice();
+ ~KXvDevice();
+
+ /*
+ * return the list of known attributes
+ */
+ const KXvDeviceAttributes& attributes();
+
+ /*
+ * return the range for a given attribute
+ */
+ bool getAttributeRange(const QString& attribute, int *min, int *max);
+
+ /*
+ * get the current value of a given attribute
+ */
+ bool getAttribute(const QString& attribute, int *val);
+
+ /*
+ * set the current value of a given attribute
+ */
+ bool setAttribute(const QString& attribute, int val);
+
+ bool grabStill(QImage *pix, int dw, int dh);
+
+ /*
+ * True if this device can operate on the given widget
+ */
+ bool supportsWidget(QWidget *w);
+
+ /*
+ * Display the given image with Xv.
+ */
+ int displayImage(QWidget *widget, const unsigned char *const data, int w, int h, int dw, int dh);
+ int displayImage(Window win, const unsigned char *const data, int w, int h, int dw, int dh);
+
+ /*
+ * Display a portion of the given image with Xv.
+ */
+ int displayImage(QWidget *widget, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh);
+ int displayImage(Window win, const unsigned char *const data, int w, int h, int x, int y, int sw, int sh, int dw, int dh);
+
+ /*
+ * Start a video stream in widget w, width dw, height dh
+ */
+ bool startVideo(QWidget *w, int dw, int dh);
+ bool startVideo(Window w, int dw, int dh);
+
+ /*
+ * Is the video playing
+ */
+ bool videoPlaying() const;
+
+ /*
+ * Stop video stream
+ */
+ bool stopVideo();
+
+ /*
+ * True if this is an image output backend (video card)
+ */
+ bool isImageBackend();
+
+ /*
+ * True if this is a video source
+ */
+ bool isVideoSource();
+
+ /*
+ * Name of the device
+ */
+ const QString& name() const;
+
+ /*
+ * The Xv port for this device
+ */
+ int port() const;
+
+ /*
+ * The list of encodings/norms available
+ */
+ const QStringList& encodings() const;
+
+ /*
+ * get encoding
+ */
+ bool encoding(QString& encoding);
+
+ /*
+ * Set the encoding to the given one. This should be taken from the list.
+ */
+ bool setEncoding(const QString& e);
+
+ /*
+ * Set the image format. (ex YUV)
+ */
+ int setImageFormat(int format);
+
+ /*
+ * Get the current image format
+ */
+ int imageFormat() const;
+
+ /*
+ * Use SHM for PutImage if available
+ */
+ bool useShm(bool on);
+
+ /*
+ * Is SHM being used?
+ */
+ bool usingShm() const;
+
+
+protected:
+ bool init();
+
+ bool _shm;
+ KXvDeviceAttributes _attrs;
+
+ int xv_type, xv_adaptor;
+ QString xv_name;
+ int xv_port;
+ unsigned int xv_encodings;
+ int xv_encoding;
+ void *xv_encoding_info;
+ int xv_encoding_attributes;
+ void *xv_attr;
+ GC xv_gc;
+ Window xv_last_win;
+
+ QStringList _encodingList;
+
+ int xv_formats;
+ void *xv_formatvalues;
+
+ int xv_nvisualformats;
+ void *xv_visualformats; // XvFormat*
+
+ bool videoStarted;
+ Window videoWindow;
+
+ long xv_imageformat;
+
+ void *xv_shminfo;
+ void *xv_image;
+ int xv_image_w;
+ int xv_image_h;
+ bool _haveShm;
+
+
+private:
+ KXvDevicePrivate *d;
+
+ void rebuildImage(int w, int h, bool shm);
+ void destroyImage();
+};
+
+
+#endif
+
diff --git a/kopete/libkopete/avdevice/qvideo.cpp b/kopete/libkopete/avdevice/qvideo.cpp
new file mode 100644
index 00000000..ad6fb762
--- /dev/null
+++ b/kopete/libkopete/avdevice/qvideo.cpp
@@ -0,0 +1,154 @@
+/***************************************************************************
+ qvideo.cpp
+ ----------
+ begin : Sat Jun 12 2004
+ copyright : (C) 2004 by Dirk Ziegelmeier
+ (C) 2002 by George Staikos
+ email : dziegel@gmx.de
+ ***************************************************************************/
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "qvideo.h"
+
+#include <kdebug.h>
+#include <qpaintdevice.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+unsigned int QVideo::bytesppForFormat(ImageFormat fmt)
+{
+ switch (fmt) {
+ case FORMAT_RGB32:
+ case FORMAT_RGB24:
+ case FORMAT_BGR32:
+ case FORMAT_BGR24:
+ return 4;
+
+ case FORMAT_RGB15_LE:
+ case FORMAT_RGB16_LE:
+ case FORMAT_RGB15_BE:
+ case FORMAT_RGB16_BE:
+ case FORMAT_YUYV:
+ case FORMAT_UYVY:
+ case FORMAT_YUV422P:
+ case FORMAT_YUV420P:
+ return 2;
+
+ case FORMAT_GREY:
+ case FORMAT_HI240:
+ return 1;
+
+ default:
+ // unknown format
+ return 0;
+ }
+}
+
+bool QVideo::findDisplayProperties(ImageFormat& fmt, int& depth, unsigned int& bitsperpixel, int& bytesperpixel)
+{
+ XVisualInfo *vi_in, vi_out;
+ long mask = VisualScreenMask;
+ int nvis = 0;
+
+ ImageFormat p = FORMAT_NONE;
+ int bpp = 0;
+ int d = 0;
+
+ vi_out.screen = QPaintDevice::x11AppScreen();
+ vi_in = XGetVisualInfo(qt_xdisplay(), mask, &vi_out, &nvis);
+
+ if (vi_in) {
+ for (int i = 0; i < nvis; i++) {
+ bpp = 0;
+ int n;
+ XPixmapFormatValues *pf = XListPixmapFormats(qt_xdisplay(),&n);
+ d = vi_in[i].depth;
+ for (int j = 0; j < n; j++) {
+ if (pf[j].depth == d) {
+ bpp = pf[j].bits_per_pixel;
+ break;
+ }
+ }
+ XFree(pf);
+
+ // FIXME: Endianess detection
+
+ p = FORMAT_NONE;
+ switch (bpp) {
+ case 32:
+ if (vi_in[i].red_mask == 0xff0000 &&
+ vi_in[i].green_mask == 0x00ff00 &&
+ vi_in[i].blue_mask == 0x0000ff) {
+ p = FORMAT_BGR32;
+ kdDebug() << "QVideo: Found BGR32 display." << endl;
+ }
+ break;
+ case 24:
+ if (vi_in[i].red_mask == 0xff0000 &&
+ vi_in[i].green_mask == 0x00ff00 &&
+ vi_in[i].blue_mask == 0x0000ff) {
+ p = FORMAT_BGR24;
+ kdDebug() << "QVideo: Found BGR24 display." << endl;
+ }
+ break;
+ case 16:
+ if (vi_in[i].red_mask == 0x00f800 &&
+ vi_in[i].green_mask == 0x0007e0 &&
+ vi_in[i].blue_mask == 0x00001f) {
+ p = FORMAT_RGB15_LE;
+ kdDebug() << "QVideo: Found RGB16_LE display." << endl;
+ } else
+ if (vi_in[i].red_mask == 0x007c00 &&
+ vi_in[i].green_mask == 0x0003e0 &&
+ vi_in[i].blue_mask == 0x00001f) {
+ p = FORMAT_RGB15_LE;
+ kdDebug() << "QVideo: Found RGB15_LE display." << endl;
+ }
+ break;
+ case 8:
+ default:
+ continue;
+ }
+
+ if (p != FORMAT_NONE)
+ break;
+ }
+ XFree(vi_in);
+ }
+
+ if (p != FORMAT_NONE) {
+ int bytespp = bytesppForFormat(p);
+ kdDebug() << "QVideo: Display properties: depth: " << d
+ << ", bits/pixel: " << bpp
+ << ", bytes/pixel: " << bytespp << endl;
+ fmt = p;
+ bitsperpixel = bpp;
+ bytesperpixel = bytespp;
+ depth = d;
+ return true;
+ } else {
+ kdWarning() << "QVideo: Unable to find out palette. What display do you have????" << endl;
+ fmt = FORMAT_NONE;
+ bitsperpixel = 0;
+ bytesperpixel = 0;
+ depth = 0;
+ return false;
+ }
+}
diff --git a/kopete/libkopete/avdevice/qvideo.h b/kopete/libkopete/avdevice/qvideo.h
new file mode 100644
index 00000000..20e999fc
--- /dev/null
+++ b/kopete/libkopete/avdevice/qvideo.h
@@ -0,0 +1,68 @@
+// -*- c++ -*-
+/***************************************************************************
+ qvideo.h
+ --------
+ begin : Sat Jun 12 2004
+ copyright : (C) 2004 by Dirk Ziegelmeier
+ email : dziegel@gmx.de
+ ***************************************************************************/
+
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef QVIDEO_H
+#define QVIDEO_H
+
+class QVideo
+{
+public:
+ typedef enum {
+ FORMAT_NONE = 0,
+ FORMAT_GREY = (1<<0),
+ FORMAT_HI240 = (1<<1),
+ FORMAT_RGB15_LE = (1<<2),
+ FORMAT_RGB15_BE = (1<<3),
+ FORMAT_RGB16_LE = (1<<4),
+ FORMAT_RGB16_BE = (1<<5),
+ FORMAT_RGB32 = (1<<6),
+ FORMAT_BGR32 = (1<<7),
+ FORMAT_RGB24 = (1<<8),
+ FORMAT_BGR24 = (1<<9),
+ FORMAT_YUYV = (1<<10),
+ FORMAT_UYVY = (1<<11),
+ FORMAT_YUV422P = (1<<12),
+ FORMAT_YUV420P = (1<<13),
+ FORMAT_ALL = 0x00003FFF
+ } ImageFormat;
+
+ typedef enum {
+ METHOD_NONE = 0,
+ METHOD_XSHM = 1,
+ METHOD_XV = 2,
+ METHOD_XVSHM = 4,
+ METHOD_X11 = 8,
+ METHOD_DGA = 16, /* unimplemented */
+ METHOD_GL = 32,
+ METHOD_SDL = 64 /* unimplemented */
+ } VideoMethod;
+
+ static unsigned int bytesppForFormat(ImageFormat fmt);
+ static bool findDisplayProperties(ImageFormat& fmt, int& depth, unsigned int& bitsperpixel, int& bytesperpixel);
+};
+
+#endif //QVIDEO_H
+
diff --git a/kopete/libkopete/avdevice/qvideostream.cpp b/kopete/libkopete/avdevice/qvideostream.cpp
new file mode 100644
index 00000000..cd7aafc2
--- /dev/null
+++ b/kopete/libkopete/avdevice/qvideostream.cpp
@@ -0,0 +1,731 @@
+/*
+ *
+ * Copyright (C) 2002 George Staikos <staikos@kde.org>
+ * 2004 Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "qvideostream.h"
+#include <qevent.h>
+#include <qimage.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include "kxv.h"
+
+#include <sys/types.h>
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSHM
+extern "C" {
+#include <sys/shm.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+}
+#endif
+
+#ifdef HAVE_GL
+class QVideoStreamGLWidget : public QGLWidget
+{
+public:
+ QVideoStreamGLWidget(QWidget* parent = 0, const char* name = 0);
+ virtual ~QVideoStreamGLWidget();
+
+ void setInputSize(const QSize& sz);
+ void display(const unsigned char *const img, int x, int y, int sw, int sh);
+
+private:
+ virtual void resizeGL(int w, int h);
+ void initializeGL();
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+ void calc(QPoint& p, QPoint& v);
+
+
+ QSize _inputSize;
+ GLuint _tex;
+ int _tw, _th;
+ QWidget* _w;
+ int _maxGL;
+ QSize _sz;
+ bool _glfun;
+ QPoint _ul, _ur, _ll, _lr;
+ QPoint _vul, _vur, _vll, _vlr;
+ QTimer* _glfunTimer;
+};
+#endif
+
+class QVideoStreamPrivate
+{
+public:
+ QVideoStreamPrivate();
+ ~QVideoStreamPrivate();
+ KXv *xvHandle;
+ KXvDevice *xvdev;
+ XImage *xim;
+ GC gc;
+#ifdef HAVE_GL
+ QVideoStreamGLWidget* glwidget;
+#endif
+#ifdef HAVE_XSHM
+ XShmSegmentInfo shmh;
+#endif
+};
+
+QVideoStreamPrivate::QVideoStreamPrivate()
+{
+ xvHandle = 0;
+ xim = 0;
+}
+
+QVideoStreamPrivate::~QVideoStreamPrivate()
+{
+ delete xvHandle;
+}
+
+QVideoStream::QVideoStream(QWidget *widget, const char* name)
+ : QObject(widget, name),
+ d(new QVideoStreamPrivate),
+ _w(widget),
+ _methods(METHOD_NONE),
+ _method(METHOD_NONE),
+ _format(FORMAT_NONE),
+ _init(false)
+{
+ int dummy;
+ unsigned int dummy2;
+ findDisplayProperties(_xFormat, dummy, dummy2, dummy);
+
+ _methods = (VideoMethod)(_methods | METHOD_X11);
+
+#ifdef HAVE_XSHM
+ if (XShmQueryExtension(_w->x11Display())) {
+ _methods = (VideoMethod)(_methods | METHOD_XSHM);
+ }
+#endif
+
+ if (KXv::haveXv()) {
+ _methods = (VideoMethod)(_methods | METHOD_XV);
+#ifdef HAVE_XSHM
+ _methods = (VideoMethod)(_methods | METHOD_XVSHM);
+#endif
+ }
+
+#ifdef HAVE_GL
+ if (QGLFormat::hasOpenGL()) {
+ _methods = (VideoMethod)(_methods | METHOD_GL);
+ }
+#endif
+
+ d->gc = XCreateGC(_w->x11Display(), _w->winId(), 0, NULL);
+}
+
+QVideoStream::~QVideoStream()
+{
+ deInit();
+ XFreeGC(_w->x11Display(), d->gc);
+ delete d;
+}
+
+void QVideoStream::deInit()
+{
+ if (!_init)
+ return;
+
+ _init = false;
+ _format = FORMAT_NONE;
+
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return;
+
+ switch (_method) {
+ case METHOD_XSHM:
+#ifdef HAVE_XSHM
+ XShmDetach(_w->x11Display(), &(d->shmh));
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ shmdt(d->shmh.shmaddr);
+#endif
+ break;
+ case METHOD_X11:
+ delete[] d->xim->data;
+ d->xim->data = 0;
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ break;
+ case METHOD_XVSHM:
+ case METHOD_XV:
+ delete d->xvHandle;
+ d->xvHandle = 0;
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ delete d->glwidget;
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return;
+ }
+}
+
+void QVideoStream::init()
+{
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return;
+
+ switch (_method) {
+ case METHOD_XSHM:
+ {
+#ifdef HAVE_XSHM
+ if ( !_inputSize.isValid() ) {
+ kdWarning() << "QVideoStream::init() (XSHM): Unable to initialize due to invalid input size." << endl;
+ return;
+ }
+
+ memset(&(d->shmh), 0, sizeof(XShmSegmentInfo));
+ d->xim = XShmCreateImage(_w->x11Display(),
+ (Visual*)_w->x11Visual(),
+ _w->x11Depth(),
+ ZPixmap, 0, &(d->shmh),
+ _inputSize.width(),
+ _inputSize.height());
+ d->shmh.shmid = shmget(IPC_PRIVATE,
+ d->xim->bytes_per_line*d->xim->height,
+ IPC_CREAT|0600);
+ d->shmh.shmaddr = (char *)shmat(d->shmh.shmid, 0, 0);
+ d->xim->data = (char*)d->shmh.shmaddr;
+ d->shmh.readOnly = False;
+ Status s = XShmAttach(_w->x11Display(), &(d->shmh));
+ if (s) {
+ XSync(_w->x11Display(), False);
+ shmctl(d->shmh.shmid, IPC_RMID, 0);
+ _format = _xFormat;
+ _init = true;
+ } else {
+ kdWarning() << "XShmAttach failed!" << endl;
+ XDestroyImage(d->xim);
+ d->xim = 0;
+ shmdt(d->shmh.shmaddr);
+ }
+#endif
+ }
+ break;
+ case METHOD_X11:
+ if ( !_inputSize.isValid() ) {
+ kdWarning() << "QVideoStream::init() (X11): Unable to initialize due to invalid input size." << endl;
+ return;
+ }
+
+ d->xim = XCreateImage(_w->x11Display(),
+ (Visual*)_w->x11Visual(),
+ _w->x11Depth(),
+ ZPixmap, 0, 0,
+ _inputSize.width(),
+ _inputSize.height(),
+ 32, 0);
+
+ d->xim->data = new char[d->xim->bytes_per_line*_inputSize.height()];
+ _format = _xFormat;
+ _init = true;
+ break;
+ case METHOD_XVSHM:
+ case METHOD_XV:
+ {
+ if (d->xvHandle)
+ delete d->xvHandle;
+
+ d->xvHandle = KXv::connect(_w->winId());
+ KXvDeviceList& xvdl(d->xvHandle->devices());
+ KXvDevice *xvdev = NULL;
+
+ for (xvdev = xvdl.first(); xvdev; xvdev = xvdl.next()) {
+ if (xvdev->isImageBackend() &&
+ xvdev->supportsWidget(_w)) {
+ d->xvdev = xvdev;
+ d->xvdev->useShm(_method == METHOD_XVSHM);
+ _format = FORMAT_YUYV;
+ _init = true;
+ break;
+ }
+ }
+
+ if (!_init) {
+ delete d->xvHandle;
+ d->xvHandle = 0;
+ }
+ }
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ d->glwidget = new QVideoStreamGLWidget(_w, "QVideoStreamGLWidget");
+ d->glwidget->resize(_w->width(), _w->height());
+ d->glwidget->show();
+ _format = FORMAT_BGR24;
+ _init = true;
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return;
+ }
+}
+
+bool QVideoStream::haveMethod(VideoMethod method) const
+{
+ return _methods & method;
+}
+
+QVideo::VideoMethod QVideoStream::method() const
+{
+ return _method;
+}
+
+QVideo::VideoMethod QVideoStream::setMethod(VideoMethod method)
+{
+ if (_methods & method) {
+ deInit();
+ _method = method;
+ init();
+ }
+
+ return _method;
+}
+
+QSize QVideoStream::maxSize() const
+{
+ return _size;
+}
+
+int QVideoStream::maxWidth() const
+{
+ return _size.width();
+}
+
+int QVideoStream::maxHeight() const
+{
+ return _size.height();
+}
+
+QSize QVideoStream::size() const
+{
+ return _size;
+}
+
+int QVideoStream::width() const
+{
+ return _size.width();
+}
+
+int QVideoStream::height() const
+{
+ return _size.height();
+}
+
+QSize QVideoStream::setSize(const QSize& sz)
+{
+ _size = sz;
+ return _size;
+}
+
+int QVideoStream::setWidth(int width)
+{
+ if (width < 0)
+ width = 0;
+ if (width > maxWidth())
+ width = maxWidth();
+ _size.setWidth(width);
+ return _size.width();
+}
+
+int QVideoStream::setHeight(int height)
+{
+ if (height < 0)
+ height = 0;
+ if (height > maxHeight())
+ height = maxHeight();
+ _size.setHeight(height);
+ return _size.height();
+}
+
+QSize QVideoStream::inputSize() const
+{
+ return _inputSize;
+}
+
+int QVideoStream::inputWidth() const
+{
+ return _inputSize.width();
+}
+
+int QVideoStream::inputHeight() const
+{
+ return _inputSize.height();
+}
+
+QSize QVideoStream::setInputSize(const QSize& sz)
+{
+ if (sz == _inputSize)
+ return _inputSize;
+ _inputSize = sz;
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize;
+}
+
+int QVideoStream::setInputWidth(int width)
+{
+ if (width == _inputSize.width())
+ return _inputSize.width();
+ _inputSize.setWidth(width);
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize.width();
+}
+
+int QVideoStream::setInputHeight(int height)
+{
+ if (height == _inputSize.height())
+ return _inputSize.height();
+ _inputSize.setHeight(height);
+ if (_method & (METHOD_XSHM | METHOD_X11)) {
+ deInit();
+ init();
+ }
+#ifdef HAVE_GL
+ if (_method & METHOD_GL) {
+ d->glwidget->setInputSize(_inputSize);
+ }
+#endif
+ return _inputSize.height();
+}
+
+bool QVideoStream::supportsFormat(VideoMethod method, ImageFormat format)
+{
+ return (bool)(formatsForMethod(method) & format);
+}
+
+QVideo::ImageFormat QVideoStream::formatsForMethod(VideoMethod method)
+{
+ switch(method) {
+ case METHOD_XSHM:
+ case METHOD_X11:
+ return _xFormat;
+ case METHOD_XV:
+ case METHOD_XVSHM:
+ return FORMAT_YUYV;
+ case METHOD_GL:
+ return FORMAT_BGR24;
+ default:
+ return FORMAT_NONE;
+ }
+}
+
+QVideo::ImageFormat QVideoStream::format() const
+{
+ return _format;
+}
+
+bool QVideoStream::setFormat(ImageFormat format)
+{
+ if(supportsFormat(_method, format)) {
+ _format = format;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+int QVideoStream::displayFrame(const unsigned char *const img)
+{
+ return displayFrame(img, 0, 0, _inputSize.width(), _inputSize.height());
+}
+
+int QVideoStream::displayFrame(const unsigned char *const img, int x, int y, int sw, int sh)
+{
+ Q_ASSERT(_init);
+ if (!_init)
+ return -1;
+
+ Q_ASSERT(_methods & _method);
+ if (!(_methods & _method))
+ return -1;
+
+ switch (_method) {
+ case METHOD_XV:
+ case METHOD_XVSHM:
+ return d->xvdev->displayImage(_w, img,
+ _inputSize.width(), _inputSize.height(), x, y, sw, sh,
+ _size.width(), _size.height());
+ break;
+ case METHOD_XSHM:
+#ifdef HAVE_XSHM
+ memcpy(d->xim->data,img,d->xim->bytes_per_line*d->xim->height);
+ XShmPutImage(_w->x11Display(), _w->winId(), d->gc, d->xim,
+ x, y,
+ 0, 0,
+ sw, sh,
+ 0);
+ XSync(_w->x11Display(), False);
+ break;
+#else
+ return -1;
+#endif
+ case METHOD_X11:
+ memcpy(d->xim->data, img, d->xim->bytes_per_line*d->xim->height);
+ XPutImage(_w->x11Display(), _w->winId(), d->gc, d->xim,
+ x, y,
+ 0, 0,
+ sw, sh);
+ XSync(_w->x11Display(), False);
+ break;
+ case METHOD_GL:
+#ifdef HAVE_GL
+ d->glwidget->display(img, x, y, sw, sh);
+#endif
+ break;
+ default:
+ Q_ASSERT(0);
+ return -1;
+ }
+
+ return 0;
+}
+
+QVideoStream& QVideoStream::operator<<(const unsigned char *const img)
+{
+ displayFrame(img);
+ return *this;
+}
+
+// ---------------------------------------------------------------------------------------
+#ifdef HAVE_GL
+
+QVideoStreamGLWidget::QVideoStreamGLWidget(QWidget* parent, const char* name)
+ : QGLWidget(QGLFormat(QGL::DoubleBuffer | QGL::Rgba | QGL::DirectRendering), parent, name),
+ _tex(0),
+ _w(parent),
+ _glfun(false)
+{
+ kdDebug() << "QVideoStreamGLWidget::QVideoStreamGLWidget()" << endl;
+
+ connect(_w, SIGNAL(resized(int, int)),
+ this, SLOT(resize(int, int)));
+
+ topLevelWidget()->installEventFilter(this);
+ _glfunTimer = new QTimer();
+}
+
+QVideoStreamGLWidget::~QVideoStreamGLWidget()
+{
+ kdDebug() << "QVideoStreamGLWidget::~QVideoStreamGLWidget()" << endl;
+ delete _glfunTimer;
+
+ makeCurrent();
+ if(_tex != 0) {
+ glDeleteTextures(1, &_tex);
+ }
+}
+
+bool QVideoStreamGLWidget::eventFilter(QObject*, QEvent* e)
+{
+ // For some reason, KeyPress does not work (yields 2), QEvent::KeyPress is unknown... What the f...????
+ // I am too lazy to scan the header files for the reason.
+ if(e->type() == 6) {
+ QKeyEvent* ke = static_cast<QKeyEvent*>(e);
+ if(ke->key() == Qt::Key_Pause) {
+ _glfunTimer->start(500, true);
+ } else if (_glfunTimer->isActive() && (ke->key() == Qt::Key_Escape)) {
+ _glfun = !_glfun;
+ }
+ }
+ return false;
+}
+
+void QVideoStreamGLWidget::initializeGL()
+{
+ kdDebug() << "QVideoStreamGLWidget::initializeGL()" << endl;
+ setAutoBufferSwap(false);
+
+ QGLFormat f = format();
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxGL);
+ kdDebug() << "OpenGL capabilities (* = required):" << endl;
+ kdDebug() << " Valid context*: " << isValid() << endl;
+ kdDebug() << " DoubleBuffer*: " << f.doubleBuffer() << endl;
+ kdDebug() << " Depth: " << f.depth() << endl;
+ kdDebug() << " RGBA*: " << f.rgba() << endl;
+ kdDebug() << " Alpha: " << f.alpha() << endl;
+ kdDebug() << " Accum: " << f.accum() << endl;
+ kdDebug() << " Stencil: " << f.stencil() << endl;
+ kdDebug() << " Stereo: " << f.stereo() << endl;
+ kdDebug() << " DirectRendering*: " << f.directRendering() << endl;
+ kdDebug() << " Overlay: " << f.hasOverlay() << endl;
+ kdDebug() << " Plane: " << f.plane() << endl;
+ kdDebug() << " MAX_TEXTURE_SIZE: " << _maxGL << endl;
+
+ qglClearColor(Qt::black);
+ glShadeModel(GL_FLAT);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ _vul = QPoint( 4, 10);
+ _vur = QPoint(-8, 4);
+ _vll = QPoint(10, -4);
+ _vlr = QPoint(-8, -10);
+}
+
+void QVideoStreamGLWidget::resizeGL(int w, int h)
+{
+ _sz = QSize(w, h);
+
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0.0, w, 0.0, h, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ _ul = QPoint(0, 0);
+ _ur = QPoint(w, 0);
+ _ll = QPoint(0, h);
+ _lr = QPoint(w, h);
+}
+
+void QVideoStreamGLWidget::setInputSize(const QSize& sz)
+{
+ makeCurrent();
+
+ _inputSize = sz;
+ int iw = _inputSize.width();
+ int ih = _inputSize.height();
+
+ if ( (iw > _maxGL) || (ih > _maxGL) ) {
+ kdWarning() << "QVideoStreamGLWidget::setInputSize(): Texture too large! maxGL: " << _maxGL << endl;
+ return;
+ }
+
+ // textures have power-of-two x,y dimensions
+ int i;
+ for (i = 0; iw >= (1 << i); i++)
+ ;
+ _tw = (1 << i);
+ for (i = 0; ih >= (1 << i); i++)
+ ;
+ _th = (1 << i);
+
+ // Generate texture
+ if(_tex != 0) {
+ glDeleteTextures(1, &_tex);
+ }
+ glGenTextures(1, &_tex);
+ glBindTexture(GL_TEXTURE_2D, _tex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // Blank texture
+ char* dummy = new char[_tw*_th*4];
+ memset(dummy, 128, _tw*_th*4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _tw, _th, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, dummy);
+ delete[] dummy;
+}
+
+void QVideoStreamGLWidget::display(const unsigned char *const img, int x, int y, int sw, int sh)
+{
+ makeCurrent();
+
+ // FIXME: Endianess - also support GL_RGB
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _inputSize.width(), _inputSize.height(),
+ GL_BGR, GL_UNSIGNED_BYTE, img);
+
+ // upper right coords
+ float ur_x = (float)(x + sw) / _tw;
+ float ur_y = (float)(y + sh) / _th;
+
+ // lower left coords
+ float ll_x = (float)(x) / _tw;
+ float ll_y = (float)(y) / _th;
+
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ glBindTexture(GL_TEXTURE_2D, _tex);
+ if (!_glfun) {
+ glBegin(GL_QUADS);
+ glTexCoord2f(ll_x, ur_y); glVertex2i(0, 0 );
+ glTexCoord2f(ll_x, ll_y); glVertex2i(0, _sz.height());
+ glTexCoord2f(ur_x, ll_y); glVertex2i(_sz.width(), _sz.height());
+ glTexCoord2f(ur_x, ur_y); glVertex2i(_sz.width(), 0 );
+ glEnd();
+ } else {
+ calc(_ul, _vul);
+ calc(_ur, _vur);
+ calc(_ll, _vll);
+ calc(_lr, _vlr);
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, y); glVertex2i(_ul.x(), _ul.y());
+ glTexCoord2f(0, 0); glVertex2i(_ll.x(), _ll.y());
+ glTexCoord2f(x, 0); glVertex2i(_lr.x(), _lr.y());
+ glTexCoord2f(x, y); glVertex2i(_ur.x(), _ur.y());
+ glEnd();
+ }
+ swapBuffers();
+ glDisable(GL_TEXTURE_2D);
+}
+
+void QVideoStreamGLWidget::calc(QPoint& p, QPoint& v)
+{
+ p += v;
+
+ if(p.x() < 0) {
+ p.setX(-p.x());
+ v.setX(-v.x());
+ }
+ if(p.y() < 0) {
+ p.setY(-p.y());
+ v.setY(-v.y());
+ }
+ if(p.x() > _sz.width()) {
+ p.setX(_sz.width() - (p.x() - _sz.width()));
+ v.setX(-v.x());
+ }
+ if(p.y() > _sz.height()) {
+ p.setY(_sz.height() - (p.y() - _sz.height()));
+ v.setY(-v.y());
+ }
+}
+#endif
+
+#include "qvideostream.moc"
diff --git a/kopete/libkopete/avdevice/qvideostream.h b/kopete/libkopete/avdevice/qvideostream.h
new file mode 100644
index 00000000..801fa829
--- /dev/null
+++ b/kopete/libkopete/avdevice/qvideostream.h
@@ -0,0 +1,112 @@
+// -*- c++ -*-
+
+/*
+ *
+ * Copyright (C) 2002 George Staikos <staikos@kde.org>
+ * 2004 Dirk Ziegelmeier <dziegel@gmx.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _QVIDEOSTREAM_H
+#define _QVIDEOSTREAM_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_GL
+#include <qgl.h>
+#include <GL/gl.h>
+#endif
+
+#include <qwidget.h>
+#include "qvideo.h"
+
+class QVideoStreamPrivate;
+
+/**
+ * QT-style video stream driver.
+ */
+class QVideoStream : public QObject, public QVideo
+{
+ Q_OBJECT
+
+public:
+ QVideoStream(QWidget *widget, const char* name = 0);
+ ~QVideoStream();
+
+ /* output method */
+ bool haveMethod(VideoMethod method) const;
+ VideoMethod method() const;
+ VideoMethod setMethod(VideoMethod method);
+
+ /* max output sizes */
+ QSize maxSize() const;
+ int maxWidth() const;
+ int maxHeight() const;
+
+ /* output sizes */
+ QSize size() const;
+ int width() const;
+ int height() const;
+
+ QSize setSize(const QSize& sz);
+ int setWidth(int width);
+ int setHeight(int height);
+
+ /* input sizes */
+ QSize inputSize() const;
+ int inputWidth() const;
+ int inputHeight() const;
+
+ QSize setInputSize(const QSize& sz);
+ int setInputWidth(int width);
+ int setInputHeight(int height);
+
+ /* input format */
+ ImageFormat format() const;
+ bool setFormat(ImageFormat format);
+
+ /* functions to find out about formats */
+ ImageFormat formatsForMethod(VideoMethod method);
+ bool supportsFormat(VideoMethod method, ImageFormat format);
+
+ /* Display image */
+ QVideoStream& operator<<(const unsigned char *const img);
+
+public slots:
+ int displayFrame(const unsigned char *const img);
+ int displayFrame(const unsigned char *const img, int x, int y, int sw, int sh);
+
+private:
+ QVideoStreamPrivate* d;
+
+ QWidget* _w;
+ VideoMethod _methods; // list of methods
+ VideoMethod _method; // the current method
+ ImageFormat _format;
+ QSize _size;
+ QSize _inputSize;
+ bool _init;
+ ImageFormat _xFormat;
+
+ void deInit();
+ void init();
+};
+
+#endif
+
diff --git a/kopete/libkopete/avdevice/sonix_compress.cpp b/kopete/libkopete/avdevice/sonix_compress.cpp
new file mode 100644
index 00000000..400635c4
--- /dev/null
+++ b/kopete/libkopete/avdevice/sonix_compress.cpp
@@ -0,0 +1,180 @@
+#include "sonix_compress.h"
+
+#define CLAMP(x) ((x)<0?0:((x)>255)?255:(x))
+
+typedef struct {
+ int is_abs;
+ int len;
+ int val;
+ int unk;
+} code_table_t;
+
+
+/* local storage */
+static code_table_t table[256];
+static int init_done = 0;
+
+/* global variable */
+int sonix_unknown = 0;
+
+/*
+ sonix_decompress_init
+ =====================
+ pre-calculates a locally stored table for efficient huffman-decoding.
+
+ Each entry at index x in the table represents the codeword
+ present at the MSB of byte x.
+
+*/
+void sonix_decompress_init(void)
+{
+ int i;
+ int is_abs, val, len, unk;
+
+ for (i = 0; i < 256; i++) {
+ is_abs = 0;
+ val = 0;
+ len = 0;
+ unk = 0;
+ if ((i & 0x80) == 0) {
+ /* code 0 */
+ val = 0;
+ len = 1;
+ }
+ else if ((i & 0xE0) == 0x80) {
+ /* code 100 */
+ val = +4;
+ len = 3;
+ }
+ else if ((i & 0xE0) == 0xA0) {
+ /* code 101 */
+ val = -4;
+ len = 3;
+ }
+ else if ((i & 0xF0) == 0xD0) {
+ /* code 1101 */
+ val = +11;
+ len = 4;
+ }
+ else if ((i & 0xF0) == 0xF0) {
+ /* code 1111 */
+ val = -11;
+ len = 4;
+ }
+ else if ((i & 0xF8) == 0xC8) {
+ /* code 11001 */
+ val = +20;
+ len = 5;
+ }
+ else if ((i & 0xFC) == 0xC0) {
+ /* code 110000 */
+ val = -20;
+ len = 6;
+ }
+ else if ((i & 0xFC) == 0xC4) {
+ /* code 110001xx: unknown */
+ val = 0;
+ len = 8;
+ unk = 1;
+ }
+ else if ((i & 0xF0) == 0xE0) {
+ /* code 1110xxxx */
+ is_abs = 1;
+ val = (i & 0x0F) << 4;
+ len = 8;
+ }
+ table[i].is_abs = is_abs;
+ table[i].val = val;
+ table[i].len = len;
+ table[i].unk = unk;
+ }
+
+ sonix_unknown = 0;
+ init_done = 1;
+}
+
+
+/*
+ sonix_decompress
+ ================
+ decompresses an image encoded by a SN9C101 camera controller chip.
+
+ IN width
+ height
+ inp pointer to compressed frame (with header already stripped)
+ OUT outp pointer to decompressed frame
+
+ Returns 0 if the operation was successful.
+ Returns <0 if operation failed.
+
+*/
+int sonix_decompress(int width, int height, unsigned char *inp, unsigned char *outp)
+{
+ int row, col;
+ int val;
+ int bitpos;
+ unsigned char code;
+ unsigned char *addr;
+
+ if (!init_done) {
+ /* do sonix_decompress_init first! */
+ return -1;
+ }
+
+ bitpos = 0;
+ for (row = 0; row < height; row++) {
+
+ col = 0;
+
+ /* first two pixels in first two rows are stored as raw 8-bit */
+ if (row < 2) {
+ addr = inp + (bitpos >> 3);
+ code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+ bitpos += 8;
+ *outp++ = code;
+
+ addr = inp + (bitpos >> 3);
+ code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+ bitpos += 8;
+ *outp++ = code;
+
+ col += 2;
+ }
+
+ while (col < width) {
+ /* get bitcode from bitstream */
+ addr = inp + (bitpos >> 3);
+ code = (addr[0] << (bitpos & 7)) | (addr[1] >> (8 - (bitpos & 7)));
+
+ /* update bit position */
+ bitpos += table[code].len;
+
+ /* update code statistics */
+ sonix_unknown += table[code].unk;
+
+ /* calculate pixel value */
+ val = table[code].val;
+ if (!table[code].is_abs) {
+ /* value is relative to top and left pixel */
+ if (col < 2) {
+ /* left column: relative to top pixel */
+ val += outp[-2*width];
+ }
+ else if (row < 2) {
+ /* top row: relative to left pixel */
+ val += outp[-2];
+ }
+ else {
+ /* main area: average of left pixel and top pixel */
+ val += (outp[-2] + outp[-2*width]) / 2;
+ }
+ }
+
+ /* store pixel */
+ *outp++ = CLAMP(val);
+ col++;
+ }
+ }
+
+ return 0;
+}
diff --git a/kopete/libkopete/avdevice/sonix_compress.h b/kopete/libkopete/avdevice/sonix_compress.h
new file mode 100644
index 00000000..509bcb07
--- /dev/null
+++ b/kopete/libkopete/avdevice/sonix_compress.h
@@ -0,0 +1,8 @@
+// Call this function first (just once is needed), before calling sonix_decompress
+void sonix_decompress_init(void);
+
+// decompresses data at inp until a full image of widthxheight has been written to outp
+int sonix_decompress(int width, int height, unsigned char *inp, unsigned char *outp);
+
+// counter to detect presence of currently unknown huffman codes
+extern int sonix_unknown;
diff --git a/kopete/libkopete/avdevice/videocontrol.cpp b/kopete/libkopete/avdevice/videocontrol.cpp
new file mode 100644
index 00000000..f4807c1c
--- /dev/null
+++ b/kopete/libkopete/avdevice/videocontrol.cpp
@@ -0,0 +1,36 @@
+/*
+ videoinput.cpp - Kopete Video Input Class
+
+ Copyright (c) 2007 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "videocontrol.h"
+
+namespace Kopete {
+
+namespace AV {
+
+VideoControl::VideoControl()
+{
+}
+
+
+VideoControl::~VideoControl()
+{
+}
+
+
+}
+
+}
diff --git a/kopete/libkopete/avdevice/videocontrol.h b/kopete/libkopete/avdevice/videocontrol.h
new file mode 100644
index 00000000..2675be6e
--- /dev/null
+++ b/kopete/libkopete/avdevice/videocontrol.h
@@ -0,0 +1,82 @@
+/*
+ videoinput.cpp - Kopete Video Input Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#define ENABLE_AV
+
+#ifndef KOPETE_AVVIDEOCONTROL_H
+#define KOPETE_AVVIDEOCONTROL_H
+
+#include <asm/types.h>
+#undef __STRICT_ANSI__
+#ifndef __u64 //required by videodev.h
+#define __u64 unsigned long long
+#endif // __u64
+
+#ifndef __s64 //required by videodev.h
+#define __s64 long long
+#endif // __s64
+
+#include <qstring.h>
+#include <kdebug.h>
+#include <qvaluevector.h>
+#include "kopete_export.h"
+
+namespace Kopete {
+
+namespace AV {
+
+typedef enum
+{
+ CONTROLTYPE_INTEGER = 0,
+ CONTROLTYPE_BOOLEAN = 1,
+ CONTROLTYPE_MENU = 2,
+ CONTROLTYPE_BUTTON = 3
+} control_type;
+
+typedef enum
+{
+ CONTROLFLAG_DISABLED = (1 << 0), // This control is permanently disabled and should be ignored by the application.
+ CONTROLFLAG_GRABBED = (1 << 1), // This control is temporarily unchangeable,
+ CONTROLFLAG_READONLY = (1 << 2), // This control is permanently readable only.
+ CONTROLFLAG__UPDATE = (1 << 3), // Changing this control may affect the value of other controls within the same control class.
+ CONTROLFLAG_INACTIVE = (1 << 4), // This control is not applicable to the current configuration.
+ CONTROLFLAG_SLIDER = (1 << 5) // This control is best represented as a slider.
+} control_flag;
+/**
+ @author Kopete Developers <kopete-devel@kde.org>
+*/
+class VideoControl{
+public:
+ VideoControl();
+ ~VideoControl();
+
+protected:
+ __u32 m_id;
+ control_type m_type;
+ QString m_name;
+ __s32 m_minimum;
+ __s32 m_maximum;
+ __s32 m_step;
+ __s32 m_default;
+ __u32 m_flags;
+};
+
+}
+
+}
+
+#endif
diff --git a/kopete/libkopete/avdevice/videodevice.cpp b/kopete/libkopete/avdevice/videodevice.cpp
new file mode 100644
index 00000000..ada02ae5
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevice.cpp
@@ -0,0 +1,2752 @@
+/*
+ videodevice.cpp - Kopete Video Device Low-level Support
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#define ENABLE_AV
+
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+
+#include <kdebug.h>
+
+#include "videoinput.h"
+#include "videodevice.h"
+
+#include "bayer.h"
+#include "sonix_compress.h"
+
+#define CLEAR(x) memset (&(x), 0, sizeof (x))
+
+namespace Kopete {
+
+namespace AV {
+
+VideoDevice::VideoDevice()
+{
+// kdDebug(14010) << "libkopete (avdevice): VideoDevice() called" << endl;
+ descriptor = -1;
+ m_streambuffers = 0;
+ m_current_input = 0;
+// kdDebug(14010) << "libkopete (avdevice): VideoDevice() exited successfuly" << endl;
+ maxwidth = 32767;
+ maxheight = 32767;
+ minwidth = 1;
+ minheight = 1;
+}
+
+
+VideoDevice::~VideoDevice()
+{
+}
+
+
+
+
+
+
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+
+void VideoDevice::enumerateMenu (void)
+{
+ kdDebug(14010) << k_funcinfo << " Menu items:" << endl;
+
+ memset (&querymenu, 0, sizeof (querymenu));
+ querymenu.id = queryctrl.id;
+
+ for (querymenu.index = queryctrl.minimum; querymenu.index <= queryctrl.maximum; querymenu.index++)
+ {
+ if (0 == xioctl (VIDIOC_QUERYMENU, &querymenu))
+ {
+ kdDebug(14010) << k_funcinfo << " " << QString::fromLocal8Bit((const char*)querymenu.name) << endl;
+ }
+ else
+ {
+ perror ("VIDIOC_QUERYMENU");
+ exit (EXIT_FAILURE);
+ }
+ }
+}
+
+
+#endif
+
+
+
+
+
+/*!
+ \fn VideoDevice::xioctl(int fd, int request, void *arg)
+ */
+int VideoDevice::xioctl(int request, void *arg)
+{
+ int r;
+
+ do r = ioctl (descriptor, request, arg);
+ while (-1 == r && EINTR == errno);
+ return r;
+}
+
+/*!
+ \fn VideoDevice::errnoReturn(const char* s)
+ */
+int VideoDevice::errnoReturn(const char* s)
+{
+ /// @todo implement me
+ fprintf (stderr, "%s error %d, %s\n",s, errno, strerror (errno));
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevice::setFileName(QString name)
+ */
+int VideoDevice::setFileName(QString filename)
+{
+ /// @todo implement me
+ full_filename=filename;
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn VideoDevice::open()
+ */
+int VideoDevice::open()
+{
+ /// @todo implement me
+
+ kdDebug(14010) << k_funcinfo << "called" << endl;
+ if(-1 != descriptor)
+ {
+ kdDebug(14010) << k_funcinfo << "Device is already open" << endl;
+ return EXIT_SUCCESS;
+ }
+ descriptor = ::open (QFile::encodeName(full_filename), O_RDWR, 0);
+ if(isOpen())
+ {
+ kdDebug(14010) << k_funcinfo << "File " << full_filename << " was opened successfuly" << endl;
+ if(EXIT_FAILURE==checkDevice())
+ {
+ kdDebug(14010) << k_funcinfo << "File " << full_filename << " could not be opened" << endl;
+ close();
+ return EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "Unable to open file " << full_filename << "Err: "<< errno << endl;
+ return EXIT_FAILURE;
+ }
+
+ initDevice();
+ selectInput(m_current_input);
+ kdDebug(14010) << k_funcinfo << "exited successfuly" << endl;
+ return EXIT_SUCCESS;
+}
+
+bool VideoDevice::isOpen()
+{
+ if(-1 == descriptor)
+ {
+// kdDebug(14010) << k_funcinfo << "VideoDevice::isOpen() File is not open" << endl;
+ return false;
+ }
+// kdDebug(14010) << k_funcinfo << "VideoDevice::isOpen() File is open" << endl;
+ return true;
+}
+
+int VideoDevice::checkDevice()
+{
+ kdDebug(14010) << k_funcinfo << "checkDevice() called." << endl;
+ if(isOpen())
+ {
+ m_videocapture=false;
+ m_videochromakey=false;
+ m_videoscale=false;
+ m_videooverlay=false;
+ m_videoread=false;
+ m_videoasyncio=false;
+ m_videostream=false;
+
+ m_driver=VIDEODEV_DRIVER_NONE;
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+
+//if(!getWorkaroundBrokenDriver())
+{
+ kdDebug(14010) << k_funcinfo << "checkDevice(): " << full_filename << " Trying V4L2 API." << endl;
+ CLEAR(V4L2_capabilities);
+
+ if (-1 != xioctl (VIDIOC_QUERYCAP, &V4L2_capabilities))
+ {
+ if (!(V4L2_capabilities.capabilities & V4L2_CAP_VIDEO_CAPTURE))
+ {
+ kdDebug(14010) << k_funcinfo << "checkDevice(): " << full_filename << " is not a video capture device." << endl;
+ m_driver = VIDEODEV_DRIVER_NONE;
+ return EXIT_FAILURE;
+ }
+ m_videocapture=true;
+ kdDebug(14010) << k_funcinfo << "checkDevice(): " << full_filename << " is a V4L2 device." << endl;
+ m_driver = VIDEODEV_DRIVER_V4L2;
+ m_model=QString::fromLocal8Bit((const char*)V4L2_capabilities.card);
+
+
+// Detect maximum and minimum resolution supported by the V4L2 device
+ CLEAR (fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl (VIDIOC_G_FMT, &fmt))
+ kdDebug(14010) << k_funcinfo << "VIDIOC_G_FMT failed (" << errno << ")." << endl;
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = 32767;
+ fmt.fmt.pix.height = 32767;
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (-1 == xioctl (VIDIOC_S_FMT, &fmt))
+ {
+ kdDebug(14010) << k_funcinfo << "Detecting maximum size with VIDIOC_S_FMT failed (" << errno << ").Returned maxwidth: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ // Note VIDIOC_S_FMT may change width and height.
+ }
+ else
+ {
+ maxwidth = fmt.fmt.pix.width;
+ maxheight = fmt.fmt.pix.height;
+ }
+ if (-1 == xioctl (VIDIOC_G_FMT, &fmt))
+ kdDebug(14010) << k_funcinfo << "VIDIOC_G_FMT failed (" << errno << ")." << endl;
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = 1;
+ fmt.fmt.pix.height = 1;
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (-1 == xioctl (VIDIOC_S_FMT, &fmt))
+ {
+ kdDebug(14010) << k_funcinfo << "Detecting minimum size with VIDIOC_S_FMT failed (" << errno << ").Returned maxwidth: " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ // Note VIDIOC_S_FMT may change width and height.
+ }
+ else
+ {
+ minwidth = fmt.fmt.pix.width;
+ minheight = fmt.fmt.pix.height;
+ }
+
+// Buggy driver paranoia
+/* min = fmt.fmt.pix.width * 2;
+ if (fmt.fmt.pix.bytesperline < min)
+ fmt.fmt.pix.bytesperline = min;
+ min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
+ if (fmt.fmt.pix.sizeimage < min)
+ fmt.fmt.pix.sizeimage = min;
+ m_buffer_size=fmt.fmt.pix.sizeimage ;*/
+
+ int inputisok=EXIT_SUCCESS;
+ m_input.clear();
+ for(unsigned int loop=0; inputisok==EXIT_SUCCESS; loop++)
+ {
+ struct v4l2_input videoinput;
+ CLEAR(videoinput);
+ videoinput.index = loop;
+ inputisok=xioctl(VIDIOC_ENUMINPUT, &videoinput);
+ if(inputisok==EXIT_SUCCESS)
+ {
+ VideoInput tempinput;
+ tempinput.name = QString::fromLocal8Bit((const char*)videoinput.name);
+ tempinput.hastuner = videoinput.type & V4L2_INPUT_TYPE_TUNER;
+ tempinput.m_standards = videoinput.std;
+ m_input.push_back(tempinput);
+ kdDebug(14010) << k_funcinfo << "Input " << loop << ": " << tempinput.name << " (tuner: " << ((videoinput.type & V4L2_INPUT_TYPE_TUNER) != 0) << ")" << endl;
+ if((videoinput.type & V4L2_INPUT_TYPE_TUNER) != 0)
+ {
+// _tunerForInput[name] = desc.tuner;
+// _isTuner = true;
+ }
+ else
+ {
+// _tunerForInput[name] = -1;
+ }
+ }
+ }
+
+
+
+
+// -----------------------------------------------------------------------------------------------------------------
+// This must turn up to be a proper method to check for controls' existence.
+CLEAR (queryctrl);
+// v4l2_queryctrl may zero the .id in some cases, even if the IOCTL returns EXIT_SUCCESS (tested with a bttv card, when testing for V4L2_CID_AUDIO_VOLUME).
+// As of 6th Aug 2007, according to the V4L2 specification version 0.21, this behavior is undocumented, and the example 1-8 code found at
+// http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/x519.htm fails because of this behavior with a bttv card.
+
+int currentid = V4L2_CID_BASE;
+
+kdDebug(14010) << k_funcinfo << "Checking CID controls" << endl;
+
+for (currentid = V4L2_CID_BASE; currentid < V4L2_CID_LASTP1; currentid++)
+//for (queryctrl.id = 9963776; queryctrl.id < 9963800; queryctrl.id++)
+{
+ queryctrl.id = currentid;
+//kdDebug(14010) << k_funcinfo << "Checking CID controls from " << V4L2_CID_BASE << " to " << V4L2_CID_LASTP1 << ". Current: " << queryctrl.id << ". IOCTL returns: " << resultado << endl;
+ if (0 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ continue;
+
+//kdDebug(14010) << k_funcinfo << " Control: " << QString::fromLocal8Bit((const char*)queryctrl.name) << endl;
+kdDebug(14010) << k_funcinfo << " Control: " << QString::fromLocal8Bit((const char*)queryctrl.name) << " Values from " << queryctrl.minimum << " to " << queryctrl.maximum << " with steps of " << queryctrl.step << ". Default: " << queryctrl.default_value << endl;
+
+/* switch (queryctrl.type)
+ {
+ case V4L2_CTRL_TYPE_INTEGER :
+ }*/
+ if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
+ enumerateMenu ();
+ }
+ else
+ {
+ if (errno == EINVAL)
+ continue;
+
+ perror ("VIDIOC_QUERYCTRL");
+// exit (EXIT_FAILURE);
+ }
+}
+
+kdDebug(14010) << k_funcinfo << "Checking CID private controls" << endl;
+
+for (currentid = V4L2_CID_PRIVATE_BASE;; currentid++)
+//for (queryctrl.id = 9963776; queryctrl.id < 9963800; queryctrl.id++)
+{
+ queryctrl.id = currentid;
+//kdDebug(14010) << k_funcinfo << "Checking CID private controls from " << V4L2_CID_PRIVATE_BASE << ". Current: " << queryctrl.id << ". IOCTL returns: " << resultado << endl;
+ if ( 0 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ continue;
+
+kdDebug(14010) << k_funcinfo << " Control: " << QString::fromLocal8Bit((const char*)queryctrl.name) << " Values from " << queryctrl.minimum << " to " << queryctrl.maximum << " with steps of " << queryctrl.step << ". Default: " << queryctrl.default_value << endl;
+
+ if (queryctrl.type == V4L2_CTRL_TYPE_MENU)
+ enumerateMenu ();
+ }
+ else
+ {
+ if (errno == EINVAL)
+ break;
+
+ perror ("VIDIOC_QUERYCTRL");
+// exit (EXIT_FAILURE);
+ }
+}
+
+
+
+
+ }
+ else
+ {
+// V4L-only drivers should return an EINVAL in errno to indicate they cannot handle V4L2 calls. Not every driver is compliant, so
+// it will try the V4L api even if the error code is different than expected.
+ kdDebug(14010) << k_funcinfo << "checkDevice(): " << full_filename << " is not a V4L2 device." << endl;
+ }
+
+}
+#endif
+
+ CLEAR(V4L_capabilities);
+
+ if(m_driver==VIDEODEV_DRIVER_NONE)
+ {
+ kdDebug(14010) << k_funcinfo << "checkDevice(): " << full_filename << " Trying V4L API." << endl;
+ if (-1 == xioctl (VIDIOCGCAP, &V4L_capabilities))
+ {
+ perror ("ioctl (VIDIOCGCAP)");
+ m_driver = VIDEODEV_DRIVER_NONE;
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " is a V4L device." << endl;
+ m_driver = VIDEODEV_DRIVER_V4L;
+ m_model=QString::fromLocal8Bit((const char*)V4L_capabilities.name);
+ if(V4L_capabilities.type & VID_TYPE_CAPTURE)
+ m_videocapture=true;
+ if(V4L_capabilities.type & VID_TYPE_CHROMAKEY)
+ m_videochromakey=true;
+ if(V4L_capabilities.type & VID_TYPE_SCALES)
+ m_videoscale=true;
+ if(V4L_capabilities.type & VID_TYPE_OVERLAY)
+ m_videooverlay=true;
+// kdDebug(14010) << "libkopete (avdevice): Inputs : " << V4L_capabilities.channels << endl;
+// kdDebug(14010) << "libkopete (avdevice): Audios : " << V4L_capabilities.audios << endl;
+ minwidth = V4L_capabilities.minwidth;
+ maxwidth = V4L_capabilities.maxwidth;
+ minheight = V4L_capabilities.minheight;
+ maxheight = V4L_capabilities.maxheight;
+
+
+ int inputisok=EXIT_SUCCESS;
+ m_input.clear();
+ for(int loop=0; loop < V4L_capabilities.channels; loop++)
+ {
+ struct video_channel videoinput;
+ CLEAR(videoinput);
+ videoinput.channel = loop;
+ videoinput.norm = 1;
+ inputisok=xioctl(VIDIOCGCHAN, &videoinput);
+ if(inputisok==EXIT_SUCCESS)
+ {
+ VideoInput tempinput;
+ tempinput.name = QString::fromLocal8Bit((const char*)videoinput.name);
+ tempinput.hastuner=videoinput.flags & VIDEO_VC_TUNER;
+// TODO: The routine to detect the appropriate video standards for V4L must be placed here
+ m_input.push_back(tempinput);
+// kdDebug(14010) << "libkopete (avdevice): Input " << loop << ": " << tempinput.name << " (tuner: " << ((videoinput.flags & VIDEO_VC_TUNER) != 0) << ")" << endl;
+/* if((input.type & V4L2_INPUT_TYPE_TUNER) != 0)
+ {
+// _tunerForInput[name] = desc.tuner;
+// _isTuner = true;
+ }
+ else
+ {
+// _tunerForInput[name] = -1;
+ }
+*/ }
+ }
+
+ }
+ }
+#endif
+ m_name=m_model; // Take care about changing the name to be different from the model itself...
+
+ detectPixelFormats();
+
+// TODO: Now we must execute the proper initialization according to the type of the driver.
+ kdDebug(14010) << k_funcinfo << "checkDevice() exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+
+/*!
+ \fn VideoDevice::showDeviceCapabilities()
+ */
+int VideoDevice::showDeviceCapabilities()
+{
+ kdDebug(14010) << k_funcinfo << "showDeviceCapabilities() called." << endl;
+ if(isOpen())
+ {
+/* kdDebug(14010) << "libkopete (avdevice): Driver: " << (const char*)V4L2_capabilities.driver << " "
+ << ((V4L2_capabilities.version>>16) & 0xFF) << "."
+ << ((V4L2_capabilities.version>> 8) & 0xFF) << "."
+ << ((V4L2_capabilities.version ) & 0xFF) << endl;
+ kdDebug(14010) << "libkopete (avdevice): Card: " << name << endl;
+ kdDebug(14010) << "libkopete (avdevice): Capabilities:" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_VIDEO_CAPTURE)
+ kdDebug(14010) << "libkopete (avdevice): Video capture" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_VIDEO_OUTPUT)
+ kdDebug(14010) << "libkopete (avdevice): Video output" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_VIDEO_OVERLAY)
+ kdDebug(14010) << "libkopete (avdevice): Video overlay" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_VBI_CAPTURE)
+ kdDebug(14010) << "libkopete (avdevice): VBI capture" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_VBI_OUTPUT)
+ kdDebug(14010) << "libkopete (avdevice): VBI output" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_RDS_CAPTURE)
+ kdDebug(14010) << "libkopete (avdevice): RDS capture" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_TUNER)
+ kdDebug(14010) << "libkopete (avdevice): Tuner IO" << endl;
+ if(V4L2_capabilities.capabilities & V4L2_CAP_AUDIO)
+ kdDebug(14010) << "libkopete (avdevice): Audio IO" << endl;
+;*/
+ kdDebug(14010) << k_funcinfo << "Card model: " << m_model << endl;
+ kdDebug(14010) << k_funcinfo << "Card name : " << m_name << endl;
+ kdDebug(14010) << k_funcinfo << "Capabilities:" << endl;
+ if(canCapture())
+ kdDebug(14010) << k_funcinfo << " Video capture" << endl;
+ if(canRead())
+ kdDebug(14010) << k_funcinfo << " Read" << endl;
+ if(canAsyncIO())
+ kdDebug(14010) << k_funcinfo << " Asynchronous input/output" << endl;
+ if(canStream())
+ kdDebug(14010) << k_funcinfo << " Streaming" << endl;
+ if(canChromakey())
+ kdDebug(14010) << k_funcinfo << " Video chromakey" << endl;
+ if(canScale())
+ kdDebug(14010) << k_funcinfo << " Video scales" << endl;
+ if(canOverlay())
+ kdDebug(14010) << k_funcinfo << " Video overlay" << endl;
+// kdDebug(14010) << "libkopete (avdevice): Audios : " << V4L_capabilities.audios << endl;
+ kdDebug(14010) << k_funcinfo << " Max res: " << maxWidth() << " x " << maxHeight() << endl;
+ kdDebug(14010) << k_funcinfo << " Min res: " << minWidth() << " x " << minHeight() << endl;
+ kdDebug(14010) << k_funcinfo << " Inputs : " << inputs() << endl;
+ for (unsigned int loop=0; loop < inputs(); loop++)
+ kdDebug(14010) << k_funcinfo << "Input " << loop << ": " << m_input[loop].name << " (tuner: " << m_input[loop].hastuner << ")" << endl;
+ kdDebug(14010) << k_funcinfo << "showDeviceCapabilities() exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevicePool::initDevice()
+ */
+int VideoDevice::initDevice()
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "initDevice() started" << endl;
+ if(-1 == descriptor)
+ {
+ kdDebug(14010) << k_funcinfo << "initDevice() Device is not open" << endl;
+ return EXIT_FAILURE;
+ }
+ m_io_method = IO_METHOD_NONE;
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ if(V4L2_capabilities.capabilities & V4L2_CAP_READWRITE)
+ {
+ m_videoread=true;
+ m_io_method = IO_METHOD_READ;
+ kdDebug(14010) << k_funcinfo << " Read/Write interface" << endl;
+ }
+ if(V4L2_capabilities.capabilities & V4L2_CAP_ASYNCIO)
+ {
+ m_videoasyncio=true;
+ kdDebug(14010) << k_funcinfo << " Async IO interface" << endl;
+ }
+ if(V4L2_capabilities.capabilities & V4L2_CAP_STREAMING)
+ {
+ m_videostream=true;
+ m_io_method = IO_METHOD_MMAP;
+// m_io_method = IO_METHOD_USERPTR;
+ kdDebug(14010) << k_funcinfo << " Streaming interface" << endl;
+ }
+ if(m_io_method==IO_METHOD_NONE)
+ {
+ kdDebug(14010) << k_funcinfo << "initDevice() Found no suitable input/output method for " << full_filename << endl;
+ return EXIT_FAILURE;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ m_videoread=true;
+ m_io_method=IO_METHOD_READ;
+ if(-1 != xioctl(VIDIOCGFBUF,&V4L_videobuffer))
+ {
+// m_videostream=true;
+// m_io_method = IO_METHOD_MMAP;
+ kdDebug(14010) << k_funcinfo << " Streaming interface" << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+
+ break;
+ }
+
+// Select video input, video standard and tune here.
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl (VIDIOC_CROPCAP, &cropcap))
+ { // Errors ignored.
+ }
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ crop.c = cropcap.defrect; // reset to default
+ if (-1 == xioctl (VIDIOC_S_CROP, &crop))
+ {
+ switch (errno)
+ {
+ case EINVAL: break; // Cropping not supported.
+ default: break; // Errors ignored.
+ }
+ }
+#endif
+#endif
+
+ showDeviceCapabilities();
+ kdDebug(14010) << k_funcinfo << "initDevice() exited successfuly" << endl;
+ return EXIT_SUCCESS;
+}
+
+unsigned int VideoDevice::inputs()
+{
+ return m_input.size();
+}
+
+
+int VideoDevice::width()
+{
+ return currentwidth;
+}
+
+int VideoDevice::minWidth()
+{
+ return minwidth;
+}
+
+int VideoDevice::maxWidth()
+{
+ return maxwidth;
+}
+
+int VideoDevice::height()
+{
+ return currentheight;
+}
+
+int VideoDevice::minHeight()
+{
+ return minheight;
+}
+
+int VideoDevice::maxHeight()
+{
+ return maxheight;
+}
+
+int VideoDevice::setSize( int newwidth, int newheight)
+{
+kdDebug(14010) << k_funcinfo << "setSize(" << newwidth << ", " << newheight << ") called." << endl;
+ if(isOpen())
+ {
+// It should not be there. It must remain in a completely distict place, cause this method should not change the pixelformat.
+ kdDebug(14010) << k_funcinfo << "Trying YUY422P" << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_YUV422P))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support YUV422P format. Trying YUYV." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_YUYV))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support YUYV format. Trying UYVY." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_UYVY))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support UYVY format. Trying YUV420P." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_YUV420P))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support YUV420P format. Trying RGB24." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_RGB24))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support RGB24 format. Trying BGR24." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_BGR24))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support RGB24 format. Trying RGB32." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_RGB32))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support RGB32 format. Trying BGR32." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_BGR32))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support BGR32 format. Trying SN9C10X." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_SN9C10X))
+ {
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support SN9C10X format. Trying Bayer RGB." << endl;
+ if(PIXELFORMAT_NONE == setPixelFormat(PIXELFORMAT_SBGGR8))
+ kdDebug(14010) << k_funcinfo << "Card doesn't seem to support SBGGR8 format. Fallback from it is not yet implemented." << endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(newwidth > maxwidth ) newwidth = maxwidth;
+ if(newheight > maxheight) newheight = maxheight;
+ if(newwidth < minwidth ) newwidth = minwidth;
+ if(newheight < minheight) newheight = minheight;
+
+ currentwidth = newwidth;
+ currentheight = newheight;
+
+//kdDebug(14010) << k_funcinfo << "width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << width() << "x" << height() << endl;
+// Change resolution for the video device
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+// CLEAR (fmt);
+ if (-1 == xioctl (VIDIOC_G_FMT, &fmt))
+ kdDebug(14010) << k_funcinfo << "VIDIOC_G_FMT failed (" << errno << ").Returned width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = width();
+ fmt.fmt.pix.height = height();
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
+ if (-1 == xioctl (VIDIOC_S_FMT, &fmt))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_FMT failed (" << errno << ").Returned width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ // Note VIDIOC_S_FMT may change width and height.
+ }
+ else
+ {
+// Buggy driver paranoia.
+kdDebug(14010) << k_funcinfo << "VIDIOC_S_FMT worked (" << errno << ").Returned width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ unsigned int min = fmt.fmt.pix.width * 2;
+ if (fmt.fmt.pix.bytesperline < min)
+ fmt.fmt.pix.bytesperline = min;
+ min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
+ if (fmt.fmt.pix.sizeimage < min)
+ fmt.fmt.pix.sizeimage = min;
+ m_buffer_size=fmt.fmt.pix.sizeimage ;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_window V4L_videowindow;
+
+kdDebug(14010) << "------------- width: " << V4L_videowindow.width << " Height: " << V4L_videowindow.height << " Clipcount: " << V4L_videowindow.clipcount << " -----------------" << endl;
+
+ if (xioctl (VIDIOCGWIN, &V4L_videowindow)== -1)
+ {
+ perror ("ioctl VIDIOCGWIN");
+// return (NULL);
+ }
+ V4L_videowindow.width = width();
+ V4L_videowindow.height = height();
+ V4L_videowindow.clipcount=0;
+ if (xioctl (VIDIOCSWIN, &V4L_videowindow)== -1)
+ {
+ perror ("ioctl VIDIOCSWIN");
+// return (NULL);
+ }
+kdDebug(14010) << "------------- width: " << V4L_videowindow.width << " Height: " << V4L_videowindow.height << " Clipcount: " << V4L_videowindow.clipcount << " -----------------" << endl;
+
+// kdDebug(14010) << "libkopete (avdevice): V4L_picture.palette: " << V4L_picture.palette << " Depth: " << V4L_picture.depth << endl;
+
+/* if(-1 == xioctl(VIDIOCGFBUF,&V4L_videobuffer))
+ kdDebug(14010) << "libkopete (avdevice): VIDIOCGFBUF failed (" << errno << "): Card cannot stream" << endl;*/
+
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ m_buffer_size = width() * height() * pixelFormatDepth(m_pixelformat) / 8;
+kdDebug(14010) << "------------------------- ------- -- m_buffer_size: " << m_buffer_size << " !!! -- ------- -----------------------------------------" << endl;
+
+ m_currentbuffer.pixelformat=m_pixelformat;
+ m_currentbuffer.data.resize(m_buffer_size);
+
+ switch (m_io_method)
+ {
+ case IO_METHOD_NONE: break;
+ case IO_METHOD_READ: initRead (); break;
+ case IO_METHOD_MMAP: initMmap (); break;
+ case IO_METHOD_USERPTR: initUserptr (); break;
+ }
+
+kdDebug(14010) << k_funcinfo << "setSize(" << newwidth << ", " << newheight << ") exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+kdDebug(14010) << k_funcinfo << "setSize(" << newwidth << ", " << newheight << ") Device is not open." << endl;
+ return EXIT_FAILURE;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+pixel_format VideoDevice::setPixelFormat(pixel_format newformat)
+{
+ pixel_format ret = PIXELFORMAT_NONE;
+//kdDebug(14010) << k_funcinfo << "called." << endl;
+// Change the pixel format for the video device
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+// CLEAR (fmt);
+ if (-1 == xioctl (VIDIOC_G_FMT, &fmt))
+ {
+// return errnoReturn ("VIDIOC_S_FMT");
+// kdDebug(14010) << k_funcinfo << "VIDIOC_G_FMT failed (" << errno << ").Returned width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ }
+ else
+ m_pixelformat = pixelFormatForPalette(fmt.fmt.pix.pixelformat);
+
+ fmt.fmt.pix.pixelformat = pixelFormatCode(newformat);
+ if (-1 == xioctl (VIDIOC_S_FMT, &fmt))
+ {
+// kdDebug(14010) << k_funcinfo << "VIDIOC_S_FMT failed (" << errno << ").Returned width: " << pixelFormatName(fmt.fmt.pix.pixelformat) << " " << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << endl;
+ }
+ else
+ {
+ if (fmt.fmt.pix.pixelformat == pixelFormatCode(newformat))
+ {
+ m_pixelformat = newformat;
+ ret = m_pixelformat;
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+// kdDebug(14010) << k_funcinfo << "V4L_picture.palette: " << V4L_picture.palette << " Depth: " << V4L_picture.depth << endl;
+ V4L_picture.palette = pixelFormatCode(newformat);
+ V4L_picture.depth = pixelFormatDepth(newformat);
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ {
+// kdDebug(14010) << k_funcinfo << "Card seems to not support " << pixelFormatName(newformat) << " format. Fallback to it is not yet implemented." << endl;
+ }
+
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+
+// kdDebug(14010) << k_funcinfo << "V4L_picture.palette: " << V4L_picture.palette << " Depth: " << V4L_picture.depth << endl;
+ m_pixelformat=pixelFormatForPalette(V4L_picture.palette);
+ if (m_pixelformat == newformat)
+ ret = newformat;
+
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+
+
+
+
+/*!
+ \fn Kopete::AV::VideoDevice::currentInput()
+ */
+int VideoDevice::currentInput()
+{
+ /// @todo implement me
+ if(isOpen())
+ {
+ return m_current_input;
+ }
+ return 0;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevice::selectInput(int input)
+ */
+int VideoDevice::selectInput(int newinput)
+{
+ /// @todo implement me
+ if(m_current_input >= inputs())
+ return EXIT_FAILURE;
+
+ if(isOpen())
+ {
+ switch (m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ if (-1 == ioctl (descriptor, VIDIOC_S_INPUT, &newinput))
+ {
+ perror ("VIDIOC_S_INPUT");
+ return EXIT_FAILURE;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ struct video_channel V4L_input;
+ V4L_input.channel=newinput;
+ V4L_input.norm=4; // Hey, it's plain wrong! It should be input's signal standard!
+ if (-1 == ioctl (descriptor, VIDIOCSCHAN, &V4L_input))
+ {
+ perror ("ioctl (VIDIOCSCHAN)");
+ return EXIT_FAILURE;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ kdDebug(14010) << k_funcinfo << "Selected input " << newinput << " (" << m_input[newinput].name << ")" << endl;
+ m_current_input = newinput;
+ setInputParameters();
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevice::setInputParameters()
+ */
+int VideoDevice::setInputParameters()
+{
+ /// @todo implement me
+ if( (isOpen()) && (m_current_input < inputs() ) )
+ {
+ setBrightness( getBrightness() );
+ setContrast( getContrast() );
+ setSaturation( getSaturation() );
+ setWhiteness( getWhiteness() );
+ setHue( getHue() );
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevice::startCapturing()
+ */
+int VideoDevice::startCapturing()
+{
+
+ kdDebug(14010) << k_funcinfo << "called." << endl;
+ if(isOpen())
+ {
+ switch (m_io_method)
+ {
+ case IO_METHOD_NONE: // Card cannot capture frames
+ return EXIT_FAILURE;
+ break;
+ case IO_METHOD_READ: // Nothing to do
+ break;
+ case IO_METHOD_MMAP:
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ {
+ unsigned int loop;
+ for (loop = 0; loop < m_streambuffers; ++loop)
+ {
+ struct v4l2_buffer buf;
+ CLEAR (buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = loop;
+ if (-1 == xioctl (VIDIOC_QBUF, &buf))
+ return errnoReturn ("VIDIOC_QBUF");
+ }
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl (VIDIOC_STREAMON, &type))
+ return errnoReturn ("VIDIOC_STREAMON");
+ }
+#endif
+#endif
+ break;
+ case IO_METHOD_USERPTR:
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ {
+ unsigned int loop;
+ for (loop = 0; loop < m_streambuffers; ++loop)
+ {
+ struct v4l2_buffer buf;
+ CLEAR (buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.m.userptr = (unsigned long) m_rawbuffers[loop].start;
+ buf.length = m_rawbuffers[loop].length;
+ if (-1 == xioctl (VIDIOC_QBUF, &buf))
+ return errnoReturn ("VIDIOC_QBUF");
+ }
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl (VIDIOC_STREAMON, &type))
+ return errnoReturn ("VIDIOC_STREAMON");
+ }
+#endif
+#endif
+ break;
+ }
+
+ kdDebug(14010) << k_funcinfo << "exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevice::getFrame()
+ */
+int VideoDevice::getFrame()
+{
+ /// @todo implement me
+ ssize_t bytesread;
+
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ struct v4l2_buffer v4l2buffer;
+#endif
+#endif
+// kdDebug(14010) << k_funcinfo << "getFrame() called." << endl;
+ if(isOpen())
+ {
+ switch (m_io_method)
+ {
+ case IO_METHOD_NONE: // Card cannot capture frames
+ return EXIT_FAILURE;
+ break;
+ case IO_METHOD_READ:
+// kdDebug(14010) << k_funcinfo << "Using IO_METHOD_READ.File descriptor: " << descriptor << " Buffer address: " << &m_currentbuffer.data[0] << " Size: " << m_currentbuffer.data.size() << endl;
+ bytesread = read (descriptor, &m_currentbuffer.data[0], m_currentbuffer.data.size());
+ if (-1 == bytesread) // must verify this point with ov511 driver.
+ {
+ kdDebug(14010) << k_funcinfo << "IO_METHOD_READ failed." << endl;
+ switch (errno)
+ {
+ case EAGAIN:
+ return EXIT_FAILURE;
+ case EIO: /* Could ignore EIO, see spec. fall through */
+ default:
+ return errnoReturn ("read");
+ }
+ }
+ if((int)m_currentbuffer.data.size() < bytesread)
+ {
+ kdDebug(14010) << k_funcinfo << "IO_METHOD_READ returned less bytes (" << bytesread << ") than it was asked for (" << m_currentbuffer.data.size() <<")." << endl;
+ }
+ break;
+ case IO_METHOD_MMAP:
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ CLEAR (v4l2buffer);
+ v4l2buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2buffer.memory = V4L2_MEMORY_MMAP;
+ if (-1 == xioctl (VIDIOC_DQBUF, &v4l2buffer))
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " MMAPed getFrame failed." << endl;
+ switch (errno)
+ {
+ case EAGAIN:
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " MMAPed getFrame failed: EAGAIN. Pointer: " << endl;
+ return EXIT_FAILURE;
+ }
+ case EIO: /* Could ignore EIO, see spec. fall through */
+ default:
+ return errnoReturn ("VIDIOC_DQBUF");
+ }
+ }
+/* if (v4l2buffer.index < m_streambuffers)
+ return EXIT_FAILURE;*/ //it was an assert()
+//kdDebug(14010) << k_funcinfo << "m_rawbuffers[" << v4l2buffer.index << "].start: " << (void *)m_rawbuffers[v4l2buffer.index].start << " Size: " << m_currentbuffer.data.size() << endl;
+
+
+
+/*{
+ unsigned long long result=0;
+ unsigned long long R=0, G=0, B=0, A=0;
+ int Rmax=0, Gmax=0, Bmax=0, Amax=0;
+ int Rmin=255, Gmin=255, Bmin=255, Amin=0;
+
+ for(unsigned int loop=0;loop < m_currentbuffer.data.size();loop+=4)
+ {
+ R+=m_rawbuffers[v4l2buffer.index].start[loop];
+ G+=m_rawbuffers[v4l2buffer.index].start[loop+1];
+ B+=m_rawbuffers[v4l2buffer.index].start[loop+2];
+// A+=currentbuffer.data[loop+3];
+ if (m_currentbuffer.data[loop] < Rmin) Rmin = m_currentbuffer.data[loop];
+ if (m_currentbuffer.data[loop+1] < Gmin) Gmin = m_currentbuffer.data[loop+1];
+ if (m_currentbuffer.data[loop+2] < Bmin) Bmin = m_currentbuffer.data[loop+2];
+// if (m_currentbuffer.data[loop+3] < Amin) Amin = m_currentbuffer.data[loop+3];
+ if (m_currentbuffer.data[loop] > Rmax) Rmax = m_currentbuffer.data[loop];
+ if (m_currentbuffer.data[loop+1] > Gmax) Gmax = m_currentbuffer.data[loop+1];
+ if (m_currentbuffer.data[loop+2] > Bmax) Bmax = m_currentbuffer.data[loop+2];
+// if (m_currentbuffer.data[loop+3] > Amax) Amax = m_currentbuffer.data[loop+3];
+ }
+ kdDebug(14010) << " R: " << R << " G: " << G << " B: " << B << " A: " << A <<
+ " Rmin: " << Rmin << " Gmin: " << Gmin << " Bmin: " << Bmin << " Amin: " << Amin <<
+ " Rmax: " << Rmax << " Gmax: " << Gmax << " Bmax: " << Bmax << " Amax: " << Amax << endl;
+}*/
+
+
+memcpy(&m_currentbuffer.data[0], m_rawbuffers[v4l2buffer.index].start, m_currentbuffer.data.size());
+ if (-1 == xioctl (VIDIOC_QBUF, &v4l2buffer))
+ return errnoReturn ("VIDIOC_QBUF");
+#endif
+#endif
+ break;
+ case IO_METHOD_USERPTR:
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ {
+ unsigned int i;
+ CLEAR (v4l2buffer);
+ v4l2buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2buffer.memory = V4L2_MEMORY_USERPTR;
+ if (-1 == xioctl (VIDIOC_DQBUF, &v4l2buffer))
+ {
+ switch (errno)
+ {
+ case EAGAIN:
+ return EXIT_FAILURE;
+ case EIO: /* Could ignore EIO, see spec. fall through */
+ default:
+ return errnoReturn ("VIDIOC_DQBUF");
+ }
+ }
+ for (i = 0; i < m_streambuffers; ++i)
+ if (v4l2buffer.m.userptr == (unsigned long) m_rawbuffers[i].start && v4l2buffer.length == m_rawbuffers[i].length)
+ break;
+ if (i < m_streambuffers)
+ return EXIT_FAILURE;
+ if (-1 == xioctl (VIDIOC_QBUF, &v4l2buffer))
+ return errnoReturn ("VIDIOC_QBUF");
+ }
+#endif
+#endif
+ break;
+ }
+
+/* Automatic color correction. Now it just swaps R and B channels in RGB24/BGR24 modes.
+ if(m_input[m_current_input].getAutoColorCorrection())
+ {
+ switch(m_currentbuffer.pixelformat)
+ {
+ case PIXELFORMAT_NONE : break;
+ case PIXELFORMAT_GREY : break;
+ case PIXELFORMAT_RGB332 : break;
+ case PIXELFORMAT_RGB555 : break;
+ case PIXELFORMAT_RGB555X: break;
+ case PIXELFORMAT_RGB565 : break;
+ case PIXELFORMAT_RGB565X: break;
+ case PIXELFORMAT_RGB24 :
+ case PIXELFORMAT_BGR24 :
+ {
+ unsigned char temp;
+ for(unsigned int loop=0;loop < m_currentbuffer.data.size();loop+=3)
+ {
+ temp = m_currentbuffer.data[loop];
+ m_currentbuffer.data[loop] = m_currentbuffer.data[loop+2];
+ m_currentbuffer.data[loop+2] = temp;
+ }
+ }
+ break;
+ case PIXELFORMAT_RGB32 :
+ case PIXELFORMAT_BGR32 :
+ {
+ unsigned char temp;
+ for(unsigned int loop=0;loop < m_currentbuffer.data.size();loop+=4)
+ {
+ temp = m_currentbuffer.data[loop];
+ m_currentbuffer.data[loop] = m_currentbuffer.data[loop+2];
+ m_currentbuffer.data[loop+2] = temp;
+ }
+ }
+ break;
+ case PIXELFORMAT_YUYV : break;
+ case PIXELFORMAT_UYVY : break;
+ case PIXELFORMAT_YUV420P: break;
+ case PIXELFORMAT_YUV422P: break;
+ }
+ }*/
+//kdDebug(14010) << k_funcinfo << "10 Using IO_METHOD_READ.File descriptor: " << descriptor << " Buffer address: " << &m_currentbuffer.data[0] << " Size: " << m_currentbuffer.data.size() << endl;
+
+
+// put frame copy operation here
+// kdDebug(14010) << k_funcinfo << "exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevice::getFrame(imagebuffer *imgbuffer)
+ */
+int VideoDevice::getFrame(imagebuffer *imgbuffer)
+{
+ if(imgbuffer)
+ {
+ getFrame();
+ imgbuffer->height = m_currentbuffer.height;
+ imgbuffer->width = m_currentbuffer.width;
+ imgbuffer->pixelformat = m_currentbuffer.pixelformat;
+ imgbuffer->data = m_currentbuffer.data;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevice::getImage(const QImage *qimage)
+ */
+int VideoDevice::getImage(QImage *qimage)
+{
+ /// @todo implement me
+
+ // do NOT delete qimage here, as it is received as a parameter
+ if (qimage->width() != width() || qimage->height() != height())
+ qimage->create(width(), height(),32, QImage::IgnoreEndian);
+
+ uchar *bits=qimage->bits();
+// kDebug() << "Capturing in " << pixelFormatName(m_currentbuffer.pixelformat);
+ switch(m_currentbuffer.pixelformat)
+ {
+ case PIXELFORMAT_NONE : break;
+
+// Packed RGB formats
+ case PIXELFORMAT_RGB332 : break;
+ case PIXELFORMAT_RGB444 : break;
+ case PIXELFORMAT_RGB555 : break;
+ case PIXELFORMAT_RGB565 :
+ {
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = (m_currentbuffer.data[step]<<3)+(m_currentbuffer.data[step]<<3>>5);
+ bits[loop+1] = ((m_currentbuffer.data[step+1])<<5)|m_currentbuffer.data[step]>>5;
+ bits[loop+2] = ((m_currentbuffer.data[step+1])&248)+((m_currentbuffer.data[step+1])>>5);
+ bits[loop+3] = 255;
+ step+=2;
+ }
+ }
+ break;
+ case PIXELFORMAT_RGB555X: break;
+ case PIXELFORMAT_RGB565X: break;
+ case PIXELFORMAT_BGR24 :
+ {
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = m_currentbuffer.data[step+2];
+ bits[loop+1] = m_currentbuffer.data[step+1];
+ bits[loop+2] = m_currentbuffer.data[step];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ }
+ break;
+ case PIXELFORMAT_RGB24 :
+ {
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = m_currentbuffer.data[step];
+ bits[loop+1] = m_currentbuffer.data[step+1];
+ bits[loop+2] = m_currentbuffer.data[step+2];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ }
+ break;
+ case PIXELFORMAT_BGR32 : break;
+ case PIXELFORMAT_RGB32 : memcpy(bits,&m_currentbuffer.data[0], m_currentbuffer.data.size());
+ break;
+
+// Bayer RGB format
+ case PIXELFORMAT_SBGGR8 :
+ {
+ unsigned char *d = (unsigned char *) malloc (width() * height() * 3);
+ bayer2rgb24(d, &m_currentbuffer.data.first(), width(), height());
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = d[step+2];
+ bits[loop+1] = d[step+1];
+ bits[loop+2] = d[step];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ free(d);
+ }
+ break;
+
+// YUV formats
+ case PIXELFORMAT_GREY : break;
+ case PIXELFORMAT_YUYV:
+ case PIXELFORMAT_UYVY:
+ case PIXELFORMAT_YUV420P:
+ case PIXELFORMAT_YUV422P:
+ {
+ uchar *yptr, *cbptr, *crptr;
+ bool halfheight=false;
+ bool packed=false;
+// Adjust algorythm to specific YUV data arrangements.
+ if (m_currentbuffer.pixelformat == PIXELFORMAT_YUV420P)
+ halfheight=true;
+ if (m_currentbuffer.pixelformat == PIXELFORMAT_YUYV)
+ {
+ yptr = &m_currentbuffer.data[0];
+ cbptr = yptr + 1;
+ crptr = yptr + 3;
+ packed=true;
+ }
+ else if (m_currentbuffer.pixelformat == PIXELFORMAT_UYVY)
+ {
+ cbptr = &m_currentbuffer.data[0];
+ yptr = cbptr + 1;
+ crptr = cbptr + 2;
+ packed=true;
+ }
+ else
+ {
+ yptr = &m_currentbuffer.data[0];
+ cbptr = yptr + (width()*height());
+ crptr = cbptr + (width()*height()/(halfheight ? 4:2));
+ }
+
+ for(int y=0; y<height(); y++)
+ {
+// Decode scanline
+ for(int x=0; x<width(); x++)
+ {
+ int c,d,e;
+
+ if (packed)
+ {
+ c = (yptr[x<<1])-16;
+ d = (cbptr[x>>1<<2])-128;
+ e = (crptr[x>>1<<2])-128;
+ }
+ else
+ {
+ c = (yptr[x])-16;
+ d = (cbptr[x>>1])-128;
+ e = (crptr[x>>1])-128;
+ }
+
+ int r = (298 * c + 409 * e + 128)>>8;
+ int g = (298 * c - 100 * d - 208 * e + 128)>>8;
+ int b = (298 * c + 516 * d + 128)>>8;
+
+ if (r<0) r=0; if (r>255) r=255;
+ if (g<0) g=0; if (g>255) g=255;
+ if (b<0) b=0; if (b>255) b=255;
+
+ uint *p = (uint*)qimage->scanLine(y)+x;
+ *p = qRgba(r,g,b,255);
+
+ }
+// Jump to next line
+ if (packed)
+ {
+ yptr+=width()*2;
+ cbptr+=width()*2;
+ crptr+=width()*2;
+ }
+ else
+ {
+ yptr+=width();
+ if (!halfheight || y&1)
+ {
+ cbptr+=width()/2;
+ crptr+=width()/2;
+ }
+ }
+ }
+ }
+ break;
+
+// Compressed formats
+ case PIXELFORMAT_JPEG : break;
+ case PIXELFORMAT_MPEG : break;
+
+// Reserved formats
+ case PIXELFORMAT_DV : break;
+ case PIXELFORMAT_ET61X251:break;
+ case PIXELFORMAT_HI240 : break;
+ case PIXELFORMAT_HM12 : break;
+ case PIXELFORMAT_MJPEG : break;
+ case PIXELFORMAT_PWC1 : break;
+ case PIXELFORMAT_PWC2 : break;
+ case PIXELFORMAT_SN9C10X:
+ {
+ unsigned char *s = new unsigned char [width() * height()];
+ unsigned char *d = new unsigned char [width() * height() * 3];
+ sonix_decompress_init();
+ sonix_decompress(width(), height(), &m_currentbuffer.data.first(), s);
+ bayer2rgb24(d, s, width(), height());
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = d[step+2];
+ bits[loop+1] = d[step+1];
+ bits[loop+2] = d[step];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ delete[] s;
+ delete[] d;
+ }
+ break;
+ case PIXELFORMAT_WNVA : break;
+ case PIXELFORMAT_YYUV : break;
+ }
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn VideoDevice::stopCapturing()
+ */
+int VideoDevice::stopCapturing()
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "called." << endl;
+ if(isOpen())
+ {
+ switch (m_io_method)
+ {
+ case IO_METHOD_NONE: // Card cannot capture frames
+ return EXIT_FAILURE;
+ break;
+ case IO_METHOD_READ: // Nothing to do
+ break;
+ case IO_METHOD_MMAP:
+ case IO_METHOD_USERPTR:
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ {
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ if (-1 == xioctl (VIDIOC_STREAMOFF, &type))
+ return errnoReturn ("VIDIOC_STREAMOFF");
+
+ if (m_io_method == IO_METHOD_MMAP)
+ {
+ unsigned int loop;
+ for (loop = 0; loop < m_streambuffers; ++loop)
+ {
+ if (munmap(m_rawbuffers[loop].start,m_rawbuffers[loop].length) != 0)
+ {
+ kdDebug(14010) << k_funcinfo << "unable to munmap." << endl;
+ }
+ }
+ }
+ }
+#endif
+ break;
+ }
+ kdDebug(14010) << k_funcinfo << "exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+
+/*!
+ \fn VideoDevice::close()
+ */
+int VideoDevice::close()
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ if(isOpen())
+ {
+ kdDebug(14010) << k_funcinfo << " Device is open. Trying to properly shutdown the device." << endl;
+ stopCapturing();
+ kdDebug(14010) << k_funcinfo << "::close() returns " << ::close(descriptor) << endl;
+ }
+ descriptor = -1;
+ return EXIT_SUCCESS;
+}
+
+float VideoDevice::getBrightness()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getBrightness();
+ else
+ return 0;
+}
+
+float VideoDevice::setBrightness(float brightness)
+{
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ m_input[m_current_input].setBrightness(brightness); // Just to check bounds
+
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ {
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+
+ CLEAR (queryctrl);
+ queryctrl.id = V4L2_CID_BRIGHTNESS;
+
+ if (-1 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (errno != EINVAL)
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_QUERYCTRL failed (" << errno << ")." << endl;
+ } else
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Brightness control." << endl;
+ }
+ } else
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Brightness control." << endl;
+ } else
+ {
+ CLEAR (control);
+ control.id = V4L2_CID_BRIGHTNESS;
+ control.value = (__s32)((queryctrl.maximum - queryctrl.minimum)*getBrightness());
+
+ if (-1 == xioctl (VIDIOC_S_CTRL, &control))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_CTRL failed (" << errno << ")." << endl;
+ }
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+ V4L_picture.brightness = uint(65535*getBrightness());
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ kdDebug(14010) << k_funcinfo << "Card seems to not support adjusting image brightness. Fallback to it is not yet implemented." << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return getBrightness();
+}
+
+float VideoDevice::getContrast()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getContrast();
+ else
+ return 0;
+}
+
+float VideoDevice::setContrast(float contrast)
+{
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ m_input[m_current_input].setContrast(contrast); // Just to check bounds
+
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ {
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+
+ CLEAR (queryctrl);
+ queryctrl.id = V4L2_CID_CONTRAST;
+
+ if (-1 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (errno != EINVAL)
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_QUERYCTRL failed (" << errno << ")." << endl;
+ } else
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Contrast control." << endl;
+ }
+ } else
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Contrast control." << endl;
+ } else
+ {
+ CLEAR (control);
+ control.id = V4L2_CID_CONTRAST;
+ control.value = (__s32)((queryctrl.maximum - queryctrl.minimum)*getContrast());
+
+ if (-1 == xioctl (VIDIOC_S_CTRL, &control))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_CTRL failed (" << errno << ")." << endl;
+ }
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+ V4L_picture.contrast = uint(65535*getContrast());
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ kdDebug(14010) << k_funcinfo << "Card seems to not support adjusting image contrast. Fallback to it is not yet implemented." << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return getContrast();
+}
+
+float VideoDevice::getSaturation()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getSaturation();
+ else
+ return 0;
+}
+
+float VideoDevice::setSaturation(float saturation)
+{
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ m_input[m_current_input].setSaturation(saturation); // Just to check bounds
+
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ {
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+
+ CLEAR (queryctrl);
+ queryctrl.id = V4L2_CID_SATURATION;
+
+ if (-1 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (errno != EINVAL)
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_QUERYCTRL failed (" << errno << ")." << endl;
+ } else
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Saturation control." << endl;
+ }
+ } else
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Saturation control." << endl;
+ } else
+ {
+ CLEAR (control);
+ control.id = V4L2_CID_SATURATION;
+ control.value = (__s32)((queryctrl.maximum - queryctrl.minimum)*getSaturation());
+
+ if (-1 == xioctl (VIDIOC_S_CTRL, &control))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_CTRL failed (" << errno << ")." << endl;
+ }
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+ V4L_picture.colour = uint(65535*getSaturation());
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ kdDebug(14010) << k_funcinfo << "Card seems to not support adjusting image saturation. Fallback to it is not yet implemented." << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return getSaturation();
+}
+
+float VideoDevice::getWhiteness()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getWhiteness();
+ else
+ return 0;
+}
+
+float VideoDevice::setWhiteness(float whiteness)
+{
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ m_input[m_current_input].setWhiteness(whiteness); // Just to check bounds
+
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ {
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+
+ CLEAR (queryctrl);
+ queryctrl.id = V4L2_CID_WHITENESS;
+
+ if (-1 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (errno != EINVAL)
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_QUERYCTRL failed (" << errno << ")." << endl;
+ } else
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Whiteness control." << endl;
+ }
+ } else
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Whiteness control." << endl;
+ } else
+ {
+ CLEAR (control);
+ control.id = V4L2_CID_WHITENESS;
+ control.value = (__s32)((queryctrl.maximum - queryctrl.minimum)*getWhiteness());
+
+ if (-1 == xioctl (VIDIOC_S_CTRL, &control))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_CTRL failed (" << errno << ")." << endl;
+ }
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+ V4L_picture.whiteness = uint(65535*getWhiteness());
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ kdDebug(14010) << k_funcinfo << "Card seems to not support adjusting white level. Fallback to it is not yet implemented." << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return getWhiteness();
+}
+
+float VideoDevice::getHue()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getHue();
+ else
+ return 0;
+}
+
+float VideoDevice::setHue(float hue)
+{
+ kdDebug(14010) << k_funcinfo << " called." << endl;
+ m_input[m_current_input].setHue(hue); // Just to check bounds
+
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ {
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_control control;
+
+ CLEAR (queryctrl);
+ queryctrl.id = V4L2_CID_HUE;
+
+ if (-1 == xioctl (VIDIOC_QUERYCTRL, &queryctrl))
+ {
+ if (errno != EINVAL)
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_QUERYCTRL failed (" << errno << ")." << endl;
+ } else
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Hue control." << endl;
+ }
+ } else
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ {
+ kdDebug(14010) << k_funcinfo << "Device doesn't support the Hue control." << endl;
+ } else
+ {
+ CLEAR (control);
+ control.id = V4L2_CID_HUE;
+ control.value = (__s32)((queryctrl.maximum - queryctrl.minimum)*getHue());
+
+ if (-1 == xioctl (VIDIOC_S_CTRL, &control))
+ {
+ kdDebug(14010) << k_funcinfo << "VIDIOC_S_CTRL failed (" << errno << ")." << endl;
+ }
+ }
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ {
+ struct video_picture V4L_picture;
+ if(-1 == xioctl(VIDIOCGPICT, &V4L_picture))
+ kdDebug(14010) << k_funcinfo << "VIDIOCGPICT failed (" << errno << ")." << endl;
+ V4L_picture.hue = uint(65535*getHue());
+ if(-1 == xioctl(VIDIOCSPICT,&V4L_picture))
+ kdDebug(14010) << k_funcinfo << "Card seems to not support adjusting image hue. Fallback to it is not yet implemented." << endl;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return getHue();
+}
+
+
+bool VideoDevice::getAutoBrightnessContrast()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getAutoBrightnessContrast();
+ else
+ return false;
+}
+
+bool VideoDevice::setAutoBrightnessContrast(bool brightnesscontrast)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevice::setAutoBrightnessContrast(" << brightnesscontrast << ") called." << endl;
+ if (m_current_input < m_input.size() )
+ {
+ m_input[m_current_input].setAutoBrightnessContrast(brightnesscontrast);
+ return m_input[m_current_input].getAutoBrightnessContrast();
+ }
+ else
+ return false;
+
+}
+
+bool VideoDevice::getAutoColorCorrection()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getAutoColorCorrection();
+ else
+ return false;
+}
+
+bool VideoDevice::setAutoColorCorrection(bool colorcorrection)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevice::setAutoColorCorrection(" << colorcorrection << ") called." << endl;
+ if (m_current_input < m_input.size() )
+ {
+ m_input[m_current_input].setAutoColorCorrection(colorcorrection);
+ return m_input[m_current_input].getAutoColorCorrection();
+ }
+ else
+ return false;
+}
+
+bool VideoDevice::getImageAsMirror()
+{
+ if (m_current_input < m_input.size() )
+ return m_input[m_current_input].getImageAsMirror();
+ else
+ return false;
+}
+
+bool VideoDevice::setImageAsMirror(bool imageasmirror)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevice::setImageAsMirror(" << imageasmirror << ") called." << endl;
+ if (m_current_input < m_input.size() )
+ {
+ m_input[m_current_input].setImageAsMirror(imageasmirror);
+ return m_input[m_current_input].getImageAsMirror();
+ }
+ else
+ return false;
+}
+
+pixel_format VideoDevice::pixelFormatForPalette( int palette )
+{
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ switch(palette)
+ {
+ case 0 : return PIXELFORMAT_NONE; break;
+
+// Packed RGB formats
+ case V4L2_PIX_FMT_RGB332 : return PIXELFORMAT_RGB332; break;
+#if defined( V4L2_PIX_FMT_RGB444 )
+ case V4L2_PIX_FMT_RGB444 : return PIXELFORMAT_RGB444; break;
+#endif
+ case V4L2_PIX_FMT_RGB555 : return PIXELFORMAT_RGB555; break;
+ case V4L2_PIX_FMT_RGB565 : return PIXELFORMAT_RGB565; break;
+ case V4L2_PIX_FMT_RGB555X : return PIXELFORMAT_RGB555X; break;
+ case V4L2_PIX_FMT_RGB565X : return PIXELFORMAT_RGB565X; break;
+ case V4L2_PIX_FMT_BGR24 : return PIXELFORMAT_BGR24; break;
+ case V4L2_PIX_FMT_RGB24 : return PIXELFORMAT_RGB24; break;
+ case V4L2_PIX_FMT_BGR32 : return PIXELFORMAT_BGR32; break;
+ case V4L2_PIX_FMT_RGB32 : return PIXELFORMAT_RGB32; break;
+
+// Bayer RGB format
+ case V4L2_PIX_FMT_SBGGR8 : return PIXELFORMAT_SBGGR8; break;
+
+// YUV formats
+ case V4L2_PIX_FMT_GREY : return PIXELFORMAT_GREY; break;
+ case V4L2_PIX_FMT_YUYV : return PIXELFORMAT_YUYV; break;
+ case V4L2_PIX_FMT_UYVY : return PIXELFORMAT_UYVY; break;
+ case V4L2_PIX_FMT_YUV420 : return PIXELFORMAT_YUV420P; break;
+ case V4L2_PIX_FMT_YUV422P : return PIXELFORMAT_YUV422P; break;
+
+// Compressed formats
+ case V4L2_PIX_FMT_JPEG : return PIXELFORMAT_JPEG; break;
+ case V4L2_PIX_FMT_MPEG : return PIXELFORMAT_MPEG; break;
+
+// Reserved formats
+ case V4L2_PIX_FMT_DV : return PIXELFORMAT_DV; break;
+ case V4L2_PIX_FMT_ET61X251 : return PIXELFORMAT_ET61X251; break;
+ case V4L2_PIX_FMT_HI240 : return PIXELFORMAT_HI240; break;
+#if defined( V4L2_PIX_FMT_HM12 )
+ case V4L2_PIX_FMT_HM12 : return PIXELFORMAT_HM12; break;
+#endif
+ case V4L2_PIX_FMT_MJPEG : return PIXELFORMAT_MJPEG; break;
+ case V4L2_PIX_FMT_PWC1 : return PIXELFORMAT_PWC1; break;
+ case V4L2_PIX_FMT_PWC2 : return PIXELFORMAT_PWC2; break;
+ case V4L2_PIX_FMT_SN9C10X : return PIXELFORMAT_SN9C10X; break;
+ case V4L2_PIX_FMT_WNVA : return PIXELFORMAT_WNVA; break;
+ case V4L2_PIX_FMT_YYUV : return PIXELFORMAT_YYUV; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ switch(palette)
+ {
+ case 0 : return PIXELFORMAT_NONE; break;
+ case VIDEO_PALETTE_GREY : return PIXELFORMAT_GREY; break;
+ case VIDEO_PALETTE_HI240 : return PIXELFORMAT_RGB332; break;
+ case VIDEO_PALETTE_RGB555 : return PIXELFORMAT_RGB555; break;
+ case VIDEO_PALETTE_RGB565 : return PIXELFORMAT_RGB565; break;
+ case VIDEO_PALETTE_RGB24 : return PIXELFORMAT_RGB24; break;
+ case VIDEO_PALETTE_RGB32 : return PIXELFORMAT_RGB32; break;
+ case VIDEO_PALETTE_YUYV : return PIXELFORMAT_YUYV; break;
+ case VIDEO_PALETTE_UYVY : return PIXELFORMAT_UYVY; break;
+ case VIDEO_PALETTE_YUV420 :
+ case VIDEO_PALETTE_YUV420P : return PIXELFORMAT_YUV420P; break;
+ case VIDEO_PALETTE_YUV422P : return PIXELFORMAT_YUV422P; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ return PIXELFORMAT_NONE; break;
+ }
+ return PIXELFORMAT_NONE;
+}
+
+int VideoDevice::pixelFormatCode(pixel_format pixelformat)
+{
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ switch(pixelformat)
+ {
+ case PIXELFORMAT_NONE : return 0; break;
+
+// Packed RGB formats
+ case PIXELFORMAT_RGB332 : return V4L2_PIX_FMT_RGB332; break;
+#if defined( V4L2_PIX_FMT_RGB444 )
+ case PIXELFORMAT_RGB444 : return V4L2_PIX_FMT_RGB444; break;
+#endif
+ case PIXELFORMAT_RGB555 : return V4L2_PIX_FMT_RGB555; break;
+ case PIXELFORMAT_RGB565 : return V4L2_PIX_FMT_RGB565; break;
+ case PIXELFORMAT_RGB555X: return V4L2_PIX_FMT_RGB555X; break;
+ case PIXELFORMAT_RGB565X: return V4L2_PIX_FMT_RGB565X; break;
+ case PIXELFORMAT_BGR24 : return V4L2_PIX_FMT_BGR24; break;
+ case PIXELFORMAT_RGB24 : return V4L2_PIX_FMT_RGB24; break;
+ case PIXELFORMAT_BGR32 : return V4L2_PIX_FMT_BGR32; break;
+ case PIXELFORMAT_RGB32 : return V4L2_PIX_FMT_RGB32; break;
+
+// Bayer RGB format
+ case PIXELFORMAT_SBGGR8 : return V4L2_PIX_FMT_SBGGR8; break;
+
+// YUV formats
+ case PIXELFORMAT_GREY : return V4L2_PIX_FMT_GREY; break;
+ case PIXELFORMAT_YUYV : return V4L2_PIX_FMT_YUYV; break;
+ case PIXELFORMAT_UYVY : return V4L2_PIX_FMT_UYVY; break;
+ case PIXELFORMAT_YUV420P: return V4L2_PIX_FMT_YUV420; break;
+ case PIXELFORMAT_YUV422P: return V4L2_PIX_FMT_YUV422P; break;
+
+// Compressed formats
+ case PIXELFORMAT_JPEG : return V4L2_PIX_FMT_JPEG; break;
+ case PIXELFORMAT_MPEG : return V4L2_PIX_FMT_MPEG; break;
+
+// Reserved formats
+ case PIXELFORMAT_DV : return V4L2_PIX_FMT_DV; break;
+ case PIXELFORMAT_ET61X251:return V4L2_PIX_FMT_ET61X251;break;
+ case PIXELFORMAT_HI240 : return V4L2_PIX_FMT_HI240; break;
+#if defined( V4L2_PIX_FMT_HM12 )
+ case PIXELFORMAT_HM12 : return V4L2_PIX_FMT_HM12; break;
+#endif
+ case PIXELFORMAT_MJPEG : return V4L2_PIX_FMT_MJPEG; break;
+ case PIXELFORMAT_PWC1 : return V4L2_PIX_FMT_PWC1; break;
+ case PIXELFORMAT_PWC2 : return V4L2_PIX_FMT_PWC2; break;
+ case PIXELFORMAT_SN9C10X: return V4L2_PIX_FMT_SN9C10X; break;
+ case PIXELFORMAT_WNVA : return V4L2_PIX_FMT_WNVA; break;
+ case PIXELFORMAT_YYUV : return V4L2_PIX_FMT_YYUV; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ switch(pixelformat)
+ {
+ case PIXELFORMAT_NONE : return 0; break;
+
+// Packed RGB formats
+ case PIXELFORMAT_RGB332 : return VIDEO_PALETTE_HI240; break;
+ case PIXELFORMAT_RGB444 : return 0; break;
+ case PIXELFORMAT_RGB555 : return VIDEO_PALETTE_RGB555; break;
+ case PIXELFORMAT_RGB565 : return VIDEO_PALETTE_RGB565; break;
+ case PIXELFORMAT_RGB555X: return 0; break;
+ case PIXELFORMAT_RGB565X: return 0; break;
+ case PIXELFORMAT_BGR24 : return 0; break;
+ case PIXELFORMAT_RGB24 : return VIDEO_PALETTE_RGB24; break;
+ case PIXELFORMAT_BGR32 : return 0; break;
+ case PIXELFORMAT_RGB32 : return VIDEO_PALETTE_RGB32; break;
+
+// Bayer RGB format
+ case PIXELFORMAT_SBGGR8 : return 0; break;
+
+// YUV formats
+ case PIXELFORMAT_GREY : return VIDEO_PALETTE_GREY; break;
+ case PIXELFORMAT_YUYV : return VIDEO_PALETTE_YUYV; break;
+ case PIXELFORMAT_UYVY : return VIDEO_PALETTE_UYVY; break;
+ case PIXELFORMAT_YUV420P: return VIDEO_PALETTE_YUV420; break;
+ case PIXELFORMAT_YUV422P: return VIDEO_PALETTE_YUV422P; break;
+
+// Compressed formats
+ case PIXELFORMAT_JPEG : return 0; break;
+ case PIXELFORMAT_MPEG : return 0; break;
+
+// Reserved formats
+ case PIXELFORMAT_DV : return 0; break;
+ case PIXELFORMAT_ET61X251:return 0; break;
+ case PIXELFORMAT_HI240 : return VIDEO_PALETTE_HI240; break;
+ case PIXELFORMAT_HM12 : return 0; break;
+ case PIXELFORMAT_MJPEG : return 0; break;
+ case PIXELFORMAT_PWC1 : return 0; break;
+ case PIXELFORMAT_PWC2 : return 0; break;
+ case PIXELFORMAT_SN9C10X: return 0; break;
+ case PIXELFORMAT_WNVA : return 0; break;
+ case PIXELFORMAT_YYUV : return 0; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ return PIXELFORMAT_NONE; break;
+ }
+ return PIXELFORMAT_NONE;
+}
+
+int VideoDevice::pixelFormatDepth(pixel_format pixelformat)
+{
+ switch(pixelformat)
+ {
+ case PIXELFORMAT_NONE : return 0; break;
+
+// Packed RGB formats
+ case PIXELFORMAT_RGB332 : return 8; break;
+ case PIXELFORMAT_RGB444 : return 16; break;
+ case PIXELFORMAT_RGB555 : return 16; break;
+ case PIXELFORMAT_RGB565 : return 16; break;
+ case PIXELFORMAT_RGB555X: return 16; break;
+ case PIXELFORMAT_RGB565X: return 16; break;
+ case PIXELFORMAT_BGR24 : return 24; break;
+ case PIXELFORMAT_RGB24 : return 24; break;
+ case PIXELFORMAT_BGR32 : return 32; break;
+ case PIXELFORMAT_RGB32 : return 32; break;
+
+// Bayer RGB format
+ case PIXELFORMAT_SBGGR8 : return 0; break;
+
+// YUV formats
+ case PIXELFORMAT_GREY : return 8; break;
+ case PIXELFORMAT_YUYV : return 16; break;
+ case PIXELFORMAT_UYVY : return 16; break;
+ case PIXELFORMAT_YUV420P: return 16; break;
+ case PIXELFORMAT_YUV422P: return 16; break;
+
+// Compressed formats
+ case PIXELFORMAT_JPEG : return 0; break;
+ case PIXELFORMAT_MPEG : return 0; break;
+
+// Reserved formats
+ case PIXELFORMAT_DV : return 0; break;
+ case PIXELFORMAT_ET61X251:return 0; break;
+ case PIXELFORMAT_HI240 : return 8; break;
+ case PIXELFORMAT_HM12 : return 0; break;
+ case PIXELFORMAT_MJPEG : return 0; break;
+ case PIXELFORMAT_PWC1 : return 0; break;
+ case PIXELFORMAT_PWC2 : return 0; break;
+ case PIXELFORMAT_SN9C10X: return 0; break;
+ case PIXELFORMAT_WNVA : return 0; break;
+ case PIXELFORMAT_YYUV : return 0; break;
+ }
+ return 0;
+}
+
+QString VideoDevice::pixelFormatName(pixel_format pixelformat)
+{
+ QString returnvalue;
+ returnvalue = "None";
+ switch(pixelformat)
+ {
+ case PIXELFORMAT_NONE : returnvalue = "None"; break;
+
+// Packed RGB formats
+ case PIXELFORMAT_RGB332 : returnvalue = "8-bit RGB332"; break;
+ case PIXELFORMAT_RGB444 : returnvalue = "8-bit RGB444"; break;
+ case PIXELFORMAT_RGB555 : returnvalue = "16-bit RGB555"; break;
+ case PIXELFORMAT_RGB565 : returnvalue = "16-bit RGB565"; break;
+ case PIXELFORMAT_RGB555X: returnvalue = "16-bit RGB555X"; break;
+ case PIXELFORMAT_RGB565X: returnvalue = "16-bit RGB565X"; break;
+ case PIXELFORMAT_BGR24 : returnvalue = "24-bit BGR24"; break;
+ case PIXELFORMAT_RGB24 : returnvalue = "24-bit RGB24"; break;
+ case PIXELFORMAT_BGR32 : returnvalue = "32-bit BGR32"; break;
+ case PIXELFORMAT_RGB32 : returnvalue = "32-bit RGB32"; break;
+
+// Bayer RGB format
+ case PIXELFORMAT_SBGGR8 : returnvalue = "Bayer RGB format"; break;
+
+// YUV formats
+ case PIXELFORMAT_GREY : returnvalue = "8-bit Grayscale"; break;
+ case PIXELFORMAT_YUYV : returnvalue = "Packed YUV 4:2:2"; break;
+ case PIXELFORMAT_UYVY : returnvalue = "Packed YVU 4:2:2"; break;
+ case PIXELFORMAT_YUV420P: returnvalue = "Planar YUV 4:2:0"; break;
+ case PIXELFORMAT_YUV422P: returnvalue = "Planar YUV 4:2:2"; break;
+
+
+// Compressed formats
+ case PIXELFORMAT_JPEG : returnvalue = "JPEG image"; break;
+ case PIXELFORMAT_MPEG : returnvalue = "MPEG stream"; break;
+
+// Reserved formats
+ case PIXELFORMAT_DV : returnvalue = "DV (unknown)"; break;
+ case PIXELFORMAT_ET61X251:returnvalue = "ET61X251"; break;
+ case PIXELFORMAT_HI240 : returnvalue = "8-bit HI240 (RGB332)"; break;
+ case PIXELFORMAT_HM12 : returnvalue = "Packed YUV 4:2:2"; break;
+ case PIXELFORMAT_MJPEG : returnvalue = "8-bit Grayscale"; break;
+ case PIXELFORMAT_PWC1 : returnvalue = "PWC1"; break;
+ case PIXELFORMAT_PWC2 : returnvalue = "PWC2"; break;
+ case PIXELFORMAT_SN9C10X: returnvalue = "SN9C102"; break;
+ case PIXELFORMAT_WNVA : returnvalue = "Winnov Videum"; break;
+ case PIXELFORMAT_YYUV : returnvalue = "YYUV (unknown)"; break;
+ }
+ return returnvalue;
+}
+
+QString VideoDevice::pixelFormatName(int pixelformat)
+{
+ QString returnvalue;
+ returnvalue = "None";
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ switch(pixelformat)
+ {
+ case 0 : returnvalue = pixelFormatName(PIXELFORMAT_NONE); break;
+
+// Packed RGB formats
+ case V4L2_PIX_FMT_RGB332 : returnvalue = pixelFormatName(PIXELFORMAT_RGB332); break;
+#if defined( V4L2_PIX_FMT_RGB444 )
+ case V4L2_PIX_FMT_RGB444 : returnvalue = pixelFormatName(PIXELFORMAT_RGB444); break;
+#endif
+ case V4L2_PIX_FMT_RGB555 : returnvalue = pixelFormatName(PIXELFORMAT_RGB555); break;
+ case V4L2_PIX_FMT_RGB565 : returnvalue = pixelFormatName(PIXELFORMAT_RGB565); break;
+ case V4L2_PIX_FMT_RGB555X : returnvalue = pixelFormatName(PIXELFORMAT_RGB555X); break;
+ case V4L2_PIX_FMT_RGB565X : returnvalue = pixelFormatName(PIXELFORMAT_RGB565X); break;
+ case V4L2_PIX_FMT_BGR24 : returnvalue = pixelFormatName(PIXELFORMAT_BGR24); break;
+ case V4L2_PIX_FMT_RGB24 : returnvalue = pixelFormatName(PIXELFORMAT_RGB24); break;
+ case V4L2_PIX_FMT_BGR32 : returnvalue = pixelFormatName(PIXELFORMAT_BGR32); break;
+ case V4L2_PIX_FMT_RGB32 : returnvalue = pixelFormatName(PIXELFORMAT_RGB32); break;
+
+// Bayer RGB format
+ case V4L2_PIX_FMT_SBGGR8 : returnvalue = pixelFormatName(PIXELFORMAT_SBGGR8); break;
+
+// YUV formats
+ case V4L2_PIX_FMT_GREY : returnvalue = pixelFormatName(PIXELFORMAT_GREY); break;
+ case V4L2_PIX_FMT_YUYV : returnvalue = pixelFormatName(PIXELFORMAT_YUYV); break;
+ case V4L2_PIX_FMT_UYVY : returnvalue = pixelFormatName(PIXELFORMAT_UYVY); break;
+ case V4L2_PIX_FMT_YUV420 : returnvalue = pixelFormatName(PIXELFORMAT_YUV420P); break;
+ case V4L2_PIX_FMT_YUV422P : returnvalue = pixelFormatName(PIXELFORMAT_YUV422P); break;
+
+// Compressed formats
+ case V4L2_PIX_FMT_JPEG : returnvalue = pixelFormatName(PIXELFORMAT_JPEG); break;
+ case V4L2_PIX_FMT_MPEG : returnvalue = pixelFormatName(PIXELFORMAT_MPEG); break;
+
+// Reserved formats
+ case V4L2_PIX_FMT_DV : returnvalue = pixelFormatName(PIXELFORMAT_DV); break;
+ case V4L2_PIX_FMT_ET61X251 : returnvalue = pixelFormatName(PIXELFORMAT_ET61X251); break;
+ case V4L2_PIX_FMT_HI240 : returnvalue = pixelFormatName(PIXELFORMAT_HI240); break;
+#if defined( V4L2_PIX_FMT_HM12 )
+ case V4L2_PIX_FMT_HM12 : returnvalue = pixelFormatName(PIXELFORMAT_HM12); break;
+#endif
+ case V4L2_PIX_FMT_MJPEG : returnvalue = pixelFormatName(PIXELFORMAT_MJPEG); break;
+ case V4L2_PIX_FMT_PWC1 : returnvalue = pixelFormatName(PIXELFORMAT_PWC1); break;
+ case V4L2_PIX_FMT_PWC2 : returnvalue = pixelFormatName(PIXELFORMAT_PWC2); break;
+ case V4L2_PIX_FMT_SN9C10X : returnvalue = pixelFormatName(PIXELFORMAT_SN9C10X); break;
+ case V4L2_PIX_FMT_WNVA : returnvalue = pixelFormatName(PIXELFORMAT_WNVA); break;
+ case V4L2_PIX_FMT_YYUV : returnvalue = pixelFormatName(PIXELFORMAT_YYUV); break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ switch(pixelformat)
+ {
+ case VIDEO_PALETTE_GREY : returnvalue = pixelFormatName(PIXELFORMAT_GREY); break;
+ case VIDEO_PALETTE_HI240 : returnvalue = pixelFormatName(PIXELFORMAT_RGB332); break;
+ case VIDEO_PALETTE_RGB555 : returnvalue = pixelFormatName(PIXELFORMAT_RGB555); break;
+ case VIDEO_PALETTE_RGB565 : returnvalue = pixelFormatName(PIXELFORMAT_RGB565); break;
+ case VIDEO_PALETTE_RGB24 : returnvalue = pixelFormatName(PIXELFORMAT_RGB24); break;
+ case VIDEO_PALETTE_RGB32 : returnvalue = pixelFormatName(PIXELFORMAT_RGB32); break;
+ case VIDEO_PALETTE_YUYV : returnvalue = pixelFormatName(PIXELFORMAT_YUYV); break;
+ case VIDEO_PALETTE_UYVY : returnvalue = pixelFormatName(PIXELFORMAT_UYVY); break;
+ case VIDEO_PALETTE_YUV420 :
+ case VIDEO_PALETTE_YUV420P : returnvalue = pixelFormatName(PIXELFORMAT_YUV420P); break;
+ case VIDEO_PALETTE_YUV422P : returnvalue = pixelFormatName(PIXELFORMAT_YUV422P); break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return returnvalue;
+}
+
+int VideoDevice::detectPixelFormats()
+{
+ int err = 0;
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ fmtdesc.index = 0;
+ fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ while ( err == 0 )
+ {
+ if (-1 == xioctl (VIDIOC_ENUM_FMT, &fmtdesc))
+// if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0 )
+ {
+ perror("VIDIOC_ENUM_FMT");
+ err = errno;
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << fmtdesc.pixelformat << " " << pixelFormatName(fmtdesc.pixelformat) << endl; // Need a cleanup. PixelFormatForPalette is a really bad name
+ fmtdesc.index++;
+ }
+ }
+// break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+// TODO: THis thing can be used to detec what pixel formats are supported in a API-independent way, but V4L2 has VIDIOC_ENUM_PIXFMT.
+// The correct thing to do is to isolate these calls and do a proper implementation for V4L and another for V4L2 when this thing will be migrated to a plugin architecture.
+
+// Packed RGB formats
+ kdDebug(14010) << k_funcinfo << "Supported pixel formats:" << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB332)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB332) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB444)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB444) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB555)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB555) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB565)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB565) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB555X)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB555X) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB565X)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB565X) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_BGR24)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_BGR24) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB24)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB24) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_BGR32)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_BGR32) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_RGB32)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_RGB32) << endl;
+
+// Bayer RGB format
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_SBGGR8)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_SBGGR8) << endl;
+
+// YUV formats
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_GREY)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_GREY) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_YUYV)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_YUYV) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_UYVY)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_UYVY) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_YUV420P)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_YUV420P) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_YUV422P)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_YUV422P) << endl;
+
+// Compressed formats
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_JPEG)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_JPEG) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_MPEG)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_MPEG) << endl;
+
+// Reserved formats
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_DV)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_DV) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_ET61X251)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_ET61X251) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_HI240)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_HI240) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_HM12)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_HM12) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_MJPEG)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_MJPEG) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_PWC1)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_PWC1) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_PWC2)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_PWC2) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_SN9C10X)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_SN9C10X) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_WNVA)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_WNVA) << endl;
+ if(PIXELFORMAT_NONE != setPixelFormat(PIXELFORMAT_YYUV)) kdDebug(14010) << k_funcinfo << pixelFormatName(PIXELFORMAT_YYUV) << endl;
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ return PIXELFORMAT_NONE; break;
+ }
+ return PIXELFORMAT_NONE;
+}
+
+__u64 VideoDevice::signalStandardCode(signal_standard standard)
+{
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ switch(standard)
+ {
+ case STANDARD_NONE : return V4L2_STD_UNKNOWN; break;
+ case STANDARD_PAL_B : return V4L2_STD_PAL_B; break;
+ case STANDARD_PAL_B1 : return V4L2_STD_PAL_B1; break;
+ case STANDARD_PAL_G : return V4L2_STD_PAL_G; break;
+ case STANDARD_PAL_H : return V4L2_STD_PAL_H; break;
+ case STANDARD_PAL_I : return V4L2_STD_PAL_I; break;
+ case STANDARD_PAL_D : return V4L2_STD_PAL_D; break;
+ case STANDARD_PAL_D1 : return V4L2_STD_PAL_D1; break;
+ case STANDARD_PAL_K : return V4L2_STD_PAL_K; break;
+ case STANDARD_PAL_M : return V4L2_STD_PAL_M; break;
+ case STANDARD_PAL_N : return V4L2_STD_PAL_N; break;
+ case STANDARD_PAL_Nc : return V4L2_STD_PAL_Nc; break;
+ case STANDARD_PAL_60 : return V4L2_STD_PAL_60; break;
+ case STANDARD_NTSC_M : return V4L2_STD_NTSC_M; break;
+ case STANDARD_NTSC_M_JP : return V4L2_STD_NTSC_M_JP; break;
+ case STANDARD_NTSC_443 : return V4L2_STD_NTSC; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_SECAM_B : return V4L2_STD_SECAM_B; break;
+ case STANDARD_SECAM_D : return V4L2_STD_SECAM_D; break;
+ case STANDARD_SECAM_G : return V4L2_STD_SECAM_G; break;
+ case STANDARD_SECAM_H : return V4L2_STD_SECAM_H; break;
+ case STANDARD_SECAM_K : return V4L2_STD_SECAM_K; break;
+ case STANDARD_SECAM_K1 : return V4L2_STD_SECAM_K1; break;
+ case STANDARD_SECAM_L : return V4L2_STD_SECAM_L; break;
+ case STANDARD_SECAM_LC : return V4L2_STD_SECAM; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_ATSC_8_VSB : return V4L2_STD_ATSC_8_VSB; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_ATSC_16_VSB : return V4L2_STD_ATSC_16_VSB; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_PAL_BG : return V4L2_STD_PAL_BG; break;
+ case STANDARD_PAL_DK : return V4L2_STD_PAL_DK; break;
+ case STANDARD_PAL : return V4L2_STD_PAL; break;
+ case STANDARD_NTSC : return V4L2_STD_NTSC; break;
+ case STANDARD_SECAM_DK : return V4L2_STD_SECAM_DK; break;
+ case STANDARD_SECAM : return V4L2_STD_SECAM; break;
+ case STANDARD_525_60 : return V4L2_STD_525_60; break;
+ case STANDARD_625_50 : return V4L2_STD_625_50; break;
+ case STANDARD_ALL : return V4L2_STD_ALL; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ switch(standard)
+ {
+ case STANDARD_NONE : return VIDEO_MODE_AUTO; break;
+ case STANDARD_PAL_B : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_B1 : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_G : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_H : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_I : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_D : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_D1 : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_K : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_M : return 5; break; // Undocumented value found to be compatible with V4L bttv driver
+ case STANDARD_PAL_N : return 6; break; // Undocumented value found to be compatible with V4L bttv driver
+ case STANDARD_PAL_Nc : return 4; break; // Undocumented value found to be compatible with V4L bttv driver
+ case STANDARD_PAL_60 : return VIDEO_MODE_PAL; break;
+ case STANDARD_NTSC_M : return VIDEO_MODE_NTSC; break;
+ case STANDARD_NTSC_M_JP : return 7; break; // Undocumented value found to be compatible with V4L bttv driver
+ case STANDARD_NTSC_443 : return VIDEO_MODE_NTSC; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_SECAM_B : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_D : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_G : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_H : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_K : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_K1 : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_L : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM_LC : return VIDEO_MODE_SECAM; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_ATSC_8_VSB : return VIDEO_MODE_AUTO; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_ATSC_16_VSB : return VIDEO_MODE_AUTO; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_PAL_BG : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL_DK : return VIDEO_MODE_PAL; break;
+ case STANDARD_PAL : return VIDEO_MODE_PAL; break;
+ case STANDARD_NTSC : return VIDEO_MODE_NTSC; break;
+ case STANDARD_SECAM_DK : return VIDEO_MODE_SECAM; break;
+ case STANDARD_SECAM : return VIDEO_MODE_SECAM; break;
+ case STANDARD_525_60 : return VIDEO_MODE_PAL; break;
+ case STANDARD_625_50 : return VIDEO_MODE_SECAM; break;
+ case STANDARD_ALL : return VIDEO_MODE_AUTO; break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ return STANDARD_NONE; break;
+ }
+ return STANDARD_NONE;
+}
+
+QString VideoDevice::signalStandardName(signal_standard standard)
+{
+ QString returnvalue;
+ returnvalue = "None";
+ switch(standard)
+ {
+ case STANDARD_NONE : returnvalue = "None"; break;
+ case STANDARD_PAL_B : returnvalue = "PAL-B"; break;
+ case STANDARD_PAL_B1 : returnvalue = "PAL-B1"; break;
+ case STANDARD_PAL_G : returnvalue = "PAL-G"; break;
+ case STANDARD_PAL_H : returnvalue = "PAL-H"; break;
+ case STANDARD_PAL_I : returnvalue = "PAL-I"; break;
+ case STANDARD_PAL_D : returnvalue = "PAL-D"; break;
+ case STANDARD_PAL_D1 : returnvalue = "PAL-D1"; break;
+ case STANDARD_PAL_K : returnvalue = "PAL-K"; break;
+ case STANDARD_PAL_M : returnvalue = "PAL-M"; break;
+ case STANDARD_PAL_N : returnvalue = "PAL-N"; break;
+ case STANDARD_PAL_Nc : returnvalue = "PAL-Nc"; break;
+ case STANDARD_PAL_60 : returnvalue = "PAL-60"; break;
+ case STANDARD_NTSC_M : returnvalue = "NTSC-M"; break;
+ case STANDARD_NTSC_M_JP : returnvalue = "NTSC-M(JP)"; break;
+ case STANDARD_NTSC_443 : returnvalue = "NTSC-443"; break;
+ case STANDARD_SECAM_B : returnvalue = "SECAM-B"; break;
+ case STANDARD_SECAM_D : returnvalue = "SECAM-D"; break;
+ case STANDARD_SECAM_G : returnvalue = "SECAM-G"; break;
+ case STANDARD_SECAM_H : returnvalue = "SECAM-H"; break;
+ case STANDARD_SECAM_K : returnvalue = "SECAM-K"; break;
+ case STANDARD_SECAM_K1 : returnvalue = "SECAM-K1"; break;
+ case STANDARD_SECAM_L : returnvalue = "SECAM-L"; break;
+ case STANDARD_SECAM_LC : returnvalue = "SECAM-LC"; break;
+ case STANDARD_ATSC_8_VSB : returnvalue = "ATSC-8-VSB"; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_ATSC_16_VSB : returnvalue = "ATSC-16-VSB"; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_PAL_BG : returnvalue = "PAL-BG"; break;
+ case STANDARD_PAL_DK : returnvalue = "PAL-DK"; break;
+ case STANDARD_PAL : returnvalue = "PAL"; break;
+ case STANDARD_NTSC : returnvalue = "NTSC"; break;
+ case STANDARD_SECAM_DK : returnvalue = "SECAM-DK"; break;
+ case STANDARD_SECAM : returnvalue = "SECAM"; break;
+ case STANDARD_525_60 : returnvalue = "525 lines 60Hz"; break;
+ case STANDARD_625_50 : returnvalue = "625 lines 50Hz"; break;
+ case STANDARD_ALL : returnvalue = "All"; break;
+ }
+ return returnvalue;
+}
+
+QString VideoDevice::signalStandardName(int standard)
+{
+ QString returnvalue;
+ returnvalue = "None";
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ switch(standard)
+ {
+ case V4L2_STD_PAL_B : returnvalue = signalStandardName(STANDARD_PAL_B); break;
+ case V4L2_STD_PAL_B1 : returnvalue = signalStandardName(STANDARD_PAL_B1); break;
+ case V4L2_STD_PAL_G : returnvalue = signalStandardName(STANDARD_PAL_G); break;
+ case V4L2_STD_PAL_H : returnvalue = signalStandardName(STANDARD_PAL_H); break;
+ case V4L2_STD_PAL_I : returnvalue = signalStandardName(STANDARD_PAL_I); break;
+ case V4L2_STD_PAL_D : returnvalue = signalStandardName(STANDARD_PAL_D); break;
+ case V4L2_STD_PAL_D1 : returnvalue = signalStandardName(STANDARD_PAL_D1); break;
+ case V4L2_STD_PAL_K : returnvalue = signalStandardName(STANDARD_PAL_K); break;
+ case V4L2_STD_PAL_M : returnvalue = signalStandardName(STANDARD_PAL_M); break;
+ case V4L2_STD_PAL_N : returnvalue = signalStandardName(STANDARD_PAL_N); break;
+ case V4L2_STD_PAL_Nc : returnvalue = signalStandardName(STANDARD_PAL_Nc); break;
+ case V4L2_STD_PAL_60 : returnvalue = signalStandardName(STANDARD_PAL_60); break;
+ case V4L2_STD_NTSC_M : returnvalue = signalStandardName(STANDARD_NTSC_M); break;
+ case V4L2_STD_NTSC_M_JP : returnvalue = signalStandardName(STANDARD_NTSC_M_JP); break;
+// case V4L2_STD_NTSC_443 : returnvalue = signalStandardName(STANDARD_NTSC_443); break; // Commented out because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case V4L2_STD_SECAM_B : returnvalue = signalStandardName(STANDARD_SECAM_B); break;
+ case V4L2_STD_SECAM_D : returnvalue = signalStandardName(STANDARD_SECAM_D); break;
+ case V4L2_STD_SECAM_G : returnvalue = signalStandardName(STANDARD_SECAM_G); break;
+ case V4L2_STD_SECAM_H : returnvalue = signalStandardName(STANDARD_SECAM_H); break;
+ case V4L2_STD_SECAM_K : returnvalue = signalStandardName(STANDARD_SECAM_K); break;
+ case V4L2_STD_SECAM_K1 : returnvalue = signalStandardName(STANDARD_SECAM_K1); break;
+ case V4L2_STD_SECAM_L : returnvalue = signalStandardName(STANDARD_SECAM_L); break;
+// case V4L2_STD_SECAM_LC : returnvalue = signalStandardName(STANDARD_SECAM_LC); break; // Commented out because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case V4L2_STD_ATSC_8_VSB : returnvalue = signalStandardName(STANDARD_ATSC_8_VSB); break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case V4L2_STD_ATSC_16_VSB : returnvalue = signalStandardName(STANDARD_ATSC_16_VSB); break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case V4L2_STD_PAL_BG : returnvalue = signalStandardName(STANDARD_PAL_BG); break;
+ case V4L2_STD_PAL_DK : returnvalue = signalStandardName(STANDARD_PAL_DK); break;
+ case V4L2_STD_PAL : returnvalue = signalStandardName(STANDARD_PAL); break;
+ case V4L2_STD_NTSC : returnvalue = signalStandardName(STANDARD_NTSC); break;
+ case V4L2_STD_SECAM_DK : returnvalue = signalStandardName(STANDARD_SECAM_DK); break;
+ case V4L2_STD_SECAM : returnvalue = signalStandardName(STANDARD_SECAM); break;
+ case V4L2_STD_525_60 : returnvalue = signalStandardName(STANDARD_525_60); break;
+ case V4L2_STD_625_50 : returnvalue = signalStandardName(STANDARD_625_50); break;
+ case V4L2_STD_ALL : returnvalue = signalStandardName(STANDARD_ALL); break;
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ switch(standard)
+ {
+ case VIDEO_MODE_PAL : returnvalue = signalStandardName(STANDARD_PAL); break;
+ case VIDEO_MODE_NTSC : returnvalue = signalStandardName(STANDARD_NTSC); break;
+ case VIDEO_MODE_SECAM : returnvalue = signalStandardName(STANDARD_SECAM); break;
+ case VIDEO_MODE_AUTO : returnvalue = signalStandardName(STANDARD_ALL); break; // It must be disabled until I find a correct way to handle those non-standard bttv modes
+// case VIDEO_MODE_PAL_Nc : returnvalue = signalStandardName(STANDARD_PAL_Nc); break; // Undocumented value found to be compatible with V4L bttv driver
+ case VIDEO_MODE_PAL_M : returnvalue = signalStandardName(STANDARD_PAL_M); break; // Undocumented value found to be compatible with V4L bttv driver
+ case VIDEO_MODE_PAL_N : returnvalue = signalStandardName(STANDARD_PAL_N); break; // Undocumented value found to be compatible with V4L bttv driver
+ case VIDEO_MODE_NTSC_JP : returnvalue = signalStandardName(STANDARD_NTSC_M_JP); break; // Undocumented value found to be compatible with V4L bttv driver
+ }
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ return returnvalue;
+}
+
+/*!
+ \fn VideoDevice::detectSignalStandards()
+ */
+int VideoDevice::detectSignalStandards()
+{
+ switch(m_driver)
+ {
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ case VIDEODEV_DRIVER_V4L2:
+ break;
+#endif
+ case VIDEODEV_DRIVER_V4L:
+ break;
+#endif
+ case VIDEODEV_DRIVER_NONE:
+ default:
+ break;
+ }
+ //FIXME: return a real value
+ return 0;
+}
+
+/*!
+ \fn VideoDevice::initRead()
+ */
+int VideoDevice::initRead()
+{
+ /// @todo implement me
+
+ kdDebug(14010) << k_funcinfo << "called." << endl;
+ if(isOpen())
+ {
+ m_rawbuffers.resize(1);
+ if (m_rawbuffers.size()==0)
+ {
+ fprintf (stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+ kdDebug(14010) << k_funcinfo << "m_buffer_size: " << m_buffer_size << endl;
+
+// m_rawbuffers[0].pixelformat=m_pixelformat;
+ m_rawbuffers[0].length = m_buffer_size;
+ m_rawbuffers[0].start = (uchar *)malloc (m_buffer_size);
+
+ if (!m_rawbuffers[0].start)
+ {
+ fprintf (stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+ kdDebug(14010) << k_funcinfo << "exited successfuly." << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+
+/*!
+ \fn VideoDevice::initMmap()
+ */
+int VideoDevice::initMmap()
+{
+ /// @todo implement me
+#define BUFFERS 2
+ if(isOpen())
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " Trying to MMAP" << endl;
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ struct v4l2_requestbuffers req;
+
+ CLEAR (req);
+
+ req.count = BUFFERS;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl (VIDIOC_REQBUFS, &req))
+ {
+ if (EINVAL == errno)
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " does not support memory mapping" << endl;
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ return errnoReturn ("VIDIOC_REQBUFS");
+ }
+ }
+
+ if (req.count < BUFFERS)
+ {
+ kdDebug(14010) << k_funcinfo << "Insufficient buffer memory on " << full_filename << endl;
+ return EXIT_FAILURE;
+ }
+
+ m_rawbuffers.resize(req.count);
+
+ if (m_rawbuffers.size()==0)
+ {
+ kdDebug(14010) << k_funcinfo << "Out of memory" << endl;
+ return EXIT_FAILURE;
+ }
+
+ for (m_streambuffers = 0; m_streambuffers < req.count; ++m_streambuffers)
+ {
+ struct v4l2_buffer v4l2buffer;
+
+ CLEAR (v4l2buffer);
+
+ v4l2buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2buffer.index = m_streambuffers;
+
+ if (-1 == xioctl (VIDIOC_QUERYBUF, &v4l2buffer))
+ return errnoReturn ("VIDIOC_QUERYBUF");
+
+ m_rawbuffers[m_streambuffers].length = v4l2buffer.length;
+ m_rawbuffers[m_streambuffers].start = (uchar *) mmap (NULL /* start anywhere */, v4l2buffer.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, descriptor, v4l2buffer.m.offset);
+
+ if (MAP_FAILED == m_rawbuffers[m_streambuffers].start)
+ return errnoReturn ("mmap");
+ }
+#endif
+ m_currentbuffer.data.resize(m_rawbuffers[0].length); // Makes the imagesize.data buffer size equal to the rawbuffer size
+ kdDebug(14010) << k_funcinfo << full_filename << " m_currentbuffer.data.size(): " << m_currentbuffer.data.size() << endl;
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+
+/*!
+ \fn VideoDevice::initUserptr()
+ */
+int VideoDevice::initUserptr()
+{
+ /// @todo implement me
+ if(isOpen())
+ {
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ struct v4l2_requestbuffers req;
+
+ CLEAR (req);
+
+ req.count = 2;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl (VIDIOC_REQBUFS, &req))
+ {
+ if (EINVAL == errno)
+ {
+ kdDebug(14010) << k_funcinfo << full_filename << " does not support memory mapping" << endl;
+ return EXIT_FAILURE;
+ }
+ else
+ {
+ return errnoReturn ("VIDIOC_REQBUFS");
+ }
+ }
+
+ m_rawbuffers.resize(4);
+
+ if (m_rawbuffers.size()==0)
+ {
+ fprintf (stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+
+ for (m_streambuffers = 0; m_streambuffers < 4; ++m_streambuffers)
+ {
+ m_rawbuffers[m_streambuffers].length = m_buffer_size;
+ m_rawbuffers[m_streambuffers].start = (uchar *) malloc (m_buffer_size);
+
+ if (!m_rawbuffers[m_streambuffers].start)
+ {
+ kdDebug(14010) << k_funcinfo << "Out of memory" << endl;
+ return EXIT_FAILURE;
+ }
+ }
+#endif
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+bool VideoDevice::canCapture()
+{
+ return m_videocapture;
+}
+
+bool VideoDevice::canChromakey()
+{
+ return m_videochromakey;
+}
+
+bool VideoDevice::canScale()
+{
+ return m_videoscale;
+}
+
+bool VideoDevice::canOverlay()
+{
+ return m_videooverlay;
+}
+
+bool VideoDevice::canRead()
+{
+ return m_videoread;
+}
+
+bool VideoDevice::canAsyncIO()
+{
+ return m_videoasyncio;
+}
+
+bool VideoDevice::canStream()
+{
+ return m_videostream;
+}
+
+
+
+}
+
+}
diff --git a/kopete/libkopete/avdevice/videodevice.h b/kopete/libkopete/avdevice/videodevice.h
new file mode 100644
index 00000000..982ab5f3
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevice.h
@@ -0,0 +1,333 @@
+/*
+ videodevice.cpp - Kopete Video Device Low-level Support
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#define ENABLE_AV
+
+#ifndef KOPETE_AVVIDEODEVICELISTITEM_H
+#define KOPETE_AVVIDEODEVICELISTITEM_H
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+
+#if defined(__linux__) && defined(ENABLE_AV)
+
+#include <asm/types.h>
+#undef __STRICT_ANSI__
+#ifndef __u64 //required by videodev.h
+#define __u64 unsigned long long
+#endif // __u64
+
+#ifndef __s64 //required by videodev.h
+#define __s64 long long
+#endif // __s64
+
+
+#ifndef pgoff_t
+#define pgoff_t unsigned long
+#endif
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/videodev.h>
+#define VIDEO_MODE_PAL_Nc 3
+#define VIDEO_MODE_PAL_M 4
+#define VIDEO_MODE_PAL_N 5
+#define VIDEO_MODE_NTSC_JP 6
+#define __STRICT_ANSI__
+
+#endif // __linux__
+
+#include <qstring.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qvaluevector.h>
+#include <kcombobox.h>
+
+#include "videoinput.h"
+#include "videocontrol.h"
+
+namespace Kopete {
+
+namespace AV {
+
+/**
+@author Kopete Developers
+*/
+typedef enum
+{
+ VIDEODEV_DRIVER_NONE
+#if defined( __linux__) && defined(ENABLE_AV)
+ ,
+ VIDEODEV_DRIVER_V4L
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ ,
+ VIDEODEV_DRIVER_V4L2
+#endif
+#endif
+} videodev_driver;
+
+typedef enum
+{
+// Packed RGB formats
+ PIXELFORMAT_NONE = 0,
+ PIXELFORMAT_GREY = (1 << 0),
+ PIXELFORMAT_RGB332 = (1 << 1),
+ PIXELFORMAT_RGB444 = (1 << 2),
+ PIXELFORMAT_RGB555 = (1 << 3),
+ PIXELFORMAT_RGB565 = (1 << 4),
+ PIXELFORMAT_RGB555X = (1 << 5),
+ PIXELFORMAT_RGB565X = (1 << 6),
+ PIXELFORMAT_BGR24 = (1 << 7),
+ PIXELFORMAT_RGB24 = (1 << 8),
+ PIXELFORMAT_BGR32 = (1 << 9),
+ PIXELFORMAT_RGB32 = (1 << 10),
+
+// Bayer RGB format
+ PIXELFORMAT_SBGGR8 = (1 << 11),
+
+// YUV formats
+ PIXELFORMAT_YUYV = (1 << 12),
+ PIXELFORMAT_UYVY = (1 << 13),
+ PIXELFORMAT_YUV420P = (1 << 14),
+ PIXELFORMAT_YUV422P = (1 << 15),
+
+// Compressed formats
+ PIXELFORMAT_JPEG = (1 << 16),
+ PIXELFORMAT_MPEG = (1 << 17),
+
+// Reserved formats
+ PIXELFORMAT_DV = (1 << 18),
+ PIXELFORMAT_ET61X251 = (1 << 19),
+ PIXELFORMAT_HI240 = (1 << 20),
+ PIXELFORMAT_HM12 = (1 << 21),
+ PIXELFORMAT_MJPEG = (1 << 22),
+ PIXELFORMAT_PWC1 = (1 << 23),
+ PIXELFORMAT_PWC2 = (1 << 24),
+ PIXELFORMAT_SN9C10X = (1 << 25),
+ PIXELFORMAT_WNVA = (1 << 26),
+ PIXELFORMAT_YYUV = (1 << 27)
+
+// PIXELFORMAT_ALL = 0x00003FFF
+} pixel_format;
+
+typedef enum
+{
+ STANDARD_NONE = 0,
+ STANDARD_PAL_B = (1 << 0),
+ STANDARD_PAL_B1 = (1 << 1),
+ STANDARD_PAL_G = (1 << 2),
+ STANDARD_PAL_H = (1 << 3),
+ STANDARD_PAL_I = (1 << 4),
+ STANDARD_PAL_D = (1 << 5),
+ STANDARD_PAL_D1 = (1 << 6),
+ STANDARD_PAL_K = (1 << 7),
+ STANDARD_PAL_M = (1 << 8),
+ STANDARD_PAL_N = (1 << 9),
+ STANDARD_PAL_Nc = (1 << 10),
+ STANDARD_PAL_60 = (1 << 11),
+// STANDARD_PAL_60 is a hybrid standard with 525 lines, 60 Hz refresh rate, and PAL color modulation with a 4.43 MHz color subcarrier. Some PAL video recorders can play back NTSC tapes in this mode for display on a 50/60 Hz agnostic PAL TV.
+ STANDARD_NTSC_M = (1 << 12),
+ STANDARD_NTSC_M_JP = (1 << 13),
+ STANDARD_NTSC_443 = (1 << 14),
+// STANDARD_NTSC_443 is a hybrid standard with 525 lines, 60 Hz refresh rate, and NTSC color modulation with a 4.43 MHz color subcarrier.
+ STANDARD_SECAM_B = (1 << 16),
+ STANDARD_SECAM_D = (1 << 17),
+ STANDARD_SECAM_G = (1 << 18),
+ STANDARD_SECAM_H = (1 << 19),
+ STANDARD_SECAM_K = (1 << 20),
+ STANDARD_SECAM_K1 = (1 << 21),
+ STANDARD_SECAM_L = (1 << 22),
+ STANDARD_SECAM_LC = (1 << 23),
+// ATSC/HDTV
+ STANDARD_ATSC_8_VSB = (1 << 24),
+ STANDARD_ATSC_16_VSB = (1 << 25),
+
+ STANDARD_PAL_BG = ( STANDARD_PAL_B | STANDARD_PAL_B1 | STANDARD_PAL_G ),
+ STANDARD_PAL_DK = ( STANDARD_PAL_D | STANDARD_PAL_D1 | STANDARD_PAL_K ),
+ STANDARD_PAL = ( STANDARD_PAL_BG | STANDARD_PAL_DK | STANDARD_PAL_H | STANDARD_PAL_I ),
+ STANDARD_NTSC = ( STANDARD_NTSC_M | STANDARD_NTSC_M_JP ),
+ STANDARD_SECAM_DK = ( STANDARD_SECAM_D | STANDARD_SECAM_K | STANDARD_SECAM_K1 ),
+ STANDARD_SECAM = ( STANDARD_SECAM_B | STANDARD_SECAM_G | STANDARD_SECAM_H | STANDARD_SECAM_DK | STANDARD_SECAM_L),
+ STANDARD_525_60 = ( STANDARD_PAL_M | STANDARD_PAL_60 | STANDARD_NTSC | STANDARD_NTSC_443),
+ STANDARD_625_50 = ( STANDARD_PAL | STANDARD_PAL_N | STANDARD_PAL_Nc | STANDARD_SECAM),
+ STANDARD_ALL = ( STANDARD_525_60 | STANDARD_625_50)
+} signal_standard;
+
+
+typedef enum
+{
+ IO_METHOD_NONE,
+ IO_METHOD_READ,
+ IO_METHOD_MMAP,
+ IO_METHOD_USERPTR
+} io_method;
+
+struct imagebuffer
+{
+ int height;
+ int width;
+ pixel_format pixelformat;
+ QValueVector <uchar> data; // maybe it should be a rawbuffer instead of it? It could make us avoid a memory copy
+};
+struct rawbuffer // raw buffer
+{
+ uchar * start;
+ size_t length;
+};
+
+
+class VideoDevice{
+public:
+ VideoDevice();
+ ~VideoDevice();
+ int setFileName(QString filename);
+ int open();
+ bool isOpen();
+ int checkDevice();
+ int showDeviceCapabilities();
+ int initDevice();
+ unsigned int inputs();
+ int width();
+ int minWidth();
+ int maxWidth();
+ int height();
+ int minHeight();
+ int maxHeight();
+ int setSize( int newwidth, int newheight);
+
+ pixel_format setPixelFormat(pixel_format newformat);
+ int pixelFormatCode(pixel_format pixelformat);
+ pixel_format pixelFormatForPalette( int palette );
+ int pixelFormatDepth(pixel_format pixelformat);
+ QString pixelFormatName(pixel_format pixelformat);
+ QString pixelFormatName(int pixelformat);
+ int detectPixelFormats();
+
+ __u64 signalStandardCode(signal_standard standard);
+ QString signalStandardName(signal_standard standard);
+ QString signalStandardName(int standard);
+ int detectSignalStandards();
+
+ int currentInput();
+ int selectInput(int input);
+ int setInputParameters();
+ int startCapturing();
+ int getFrame();
+ int getFrame(imagebuffer *imgbuffer);
+ int getImage(QImage *qimage);
+ int stopCapturing();
+ int close();
+
+ float getBrightness();
+ float setBrightness(float brightness);
+ float getContrast();
+ float setContrast(float contrast);
+ float getSaturation();
+ float setSaturation(float saturation);
+ float getWhiteness();
+ float setWhiteness(float whiteness);
+ float getHue();
+ float setHue(float Hue);
+
+ bool getAutoBrightnessContrast();
+ bool setAutoBrightnessContrast(bool brightnesscontrast);
+ bool getAutoColorCorrection();
+ bool setAutoColorCorrection(bool colorcorrection);
+ bool getImageAsMirror();
+ bool setImageAsMirror(bool imageasmirror);
+
+ bool canCapture();
+ bool canChromakey();
+ bool canScale();
+ bool canOverlay();
+ bool canRead();
+ bool canAsyncIO();
+ bool canStream();
+
+ QString m_model;
+ QString m_name;
+ size_t m_modelindex; // Defines what's the number of a device when more than 1 device of a given model is present;
+ QString full_filename;
+ videodev_driver m_driver;
+ int descriptor;
+
+//protected:
+#if defined(__linux__) && defined(ENABLE_AV)
+#ifdef V4L2_CAP_VIDEO_CAPTURE
+ struct v4l2_capability V4L2_capabilities;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+ struct v4l2_format fmt;
+ struct v4l2_fmtdesc fmtdesc; // Not sure if it must be here or inside detectPixelFormats(). Should inve
+// struct v4l2_input m_input;
+ struct v4l2_queryctrl queryctrl;
+ struct v4l2_querymenu querymenu;
+void enumerateMenu (void);
+
+#endif
+ struct video_capability V4L_capabilities;
+ struct video_buffer V4L_videobuffer;
+#endif
+ QValueVector<Kopete::AV::VideoInput> m_input;
+ QValueVector<Kopete::AV::VideoControl> m_control;
+// QFile file;
+protected:
+ int currentwidth, minwidth, maxwidth, currentheight, minheight, maxheight;
+
+ bool m_disablemmap;
+ bool m_workaroundbrokendriver;
+
+ QValueVector<rawbuffer> m_rawbuffers;
+ unsigned int m_streambuffers;
+ imagebuffer m_currentbuffer;
+ int m_buffer_size;
+
+ int m_current_input;
+ pixel_format m_pixelformat;
+
+ io_method m_io_method;
+ bool m_videocapture;
+ bool m_videochromakey;
+ bool m_videoscale;
+ bool m_videooverlay;
+ bool m_videoread;
+ bool m_videoasyncio;
+ bool m_videostream;
+
+ int xioctl(int request, void *arg);
+ int errnoReturn(const char* s);
+ int initRead();
+ int initMmap();
+ int initUserptr();
+
+};
+
+}
+
+}
+
+#endif
diff --git a/kopete/libkopete/avdevice/videodevicemodelpool.cpp b/kopete/libkopete/avdevice/videodevicemodelpool.cpp
new file mode 100644
index 00000000..c6fc533e
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevicemodelpool.cpp
@@ -0,0 +1,68 @@
+/*
+ videodevicepool.h - Kopete Multiple Video Device handler Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "videodevicemodelpool.h"
+
+namespace Kopete {
+
+namespace AV {
+
+VideoDeviceModelPool::VideoDeviceModelPool()
+{
+}
+
+
+VideoDeviceModelPool::~VideoDeviceModelPool()
+{
+}
+
+void VideoDeviceModelPool::clear()
+{
+ m_devicemodel.clear();
+}
+
+size_t VideoDeviceModelPool::size()
+{
+ return m_devicemodel.size();
+}
+
+size_t VideoDeviceModelPool::addModel( QString newmodel )
+{
+ VideoDeviceModel newdevicemodel;
+ newdevicemodel.model=newmodel;
+ newdevicemodel.count=0;
+
+ if(m_devicemodel.size())
+ {
+ for ( size_t loop = 0 ; loop < m_devicemodel.size(); loop++)
+ if (newmodel == m_devicemodel[loop].model)
+ {
+ kdDebug() << k_funcinfo << "Model " << newmodel << " already exists." << endl;
+ m_devicemodel[loop].count++;
+ return m_devicemodel[loop].count;
+ }
+ }
+ m_devicemodel.push_back(newdevicemodel);
+ m_devicemodel[m_devicemodel.size()-1].model = newmodel;
+ m_devicemodel[m_devicemodel.size()-1].count = 0;
+ return 0;
+}
+
+
+}
+
+}
diff --git a/kopete/libkopete/avdevice/videodevicemodelpool.h b/kopete/libkopete/avdevice/videodevicemodelpool.h
new file mode 100644
index 00000000..54d801c4
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevicemodelpool.h
@@ -0,0 +1,53 @@
+/*
+ videodevicepool.h - Kopete Multiple Video Device handler Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_AVVIDEODEVICEMODELPOOL_H
+#define KOPETE_AVVIDEODEVICEMODELPOOL_H
+
+#include <qstring.h>
+#include <qvaluevector.h>
+#include <kdebug.h>
+#include "kopete_export.h"
+
+namespace Kopete {
+
+namespace AV {
+
+/**
+ @author Kopete Developers <kopete-devel@kde.org>
+*/
+class VideoDeviceModelPool{
+
+ struct VideoDeviceModel
+ {
+ QString model;
+ size_t count;
+ };
+ QValueVector<VideoDeviceModel> m_devicemodel;
+public:
+ VideoDeviceModelPool();
+ ~VideoDeviceModelPool();
+ void clear();
+ size_t size();
+ size_t addModel(QString newmodel);
+};
+
+}
+
+}
+
+#endif
diff --git a/kopete/libkopete/avdevice/videodevicepool.cpp b/kopete/libkopete/avdevice/videodevicepool.cpp
new file mode 100644
index 00000000..2651addb
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevicepool.cpp
@@ -0,0 +1,889 @@
+/*
+ videodevice.cpp - Kopete Video Device Low-level Support
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#define ENABLE_AV
+
+#include <assert.h>
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qdir.h>
+
+#include "videodevice.h"
+#include "videodevicepool.h"
+
+#define CLEAR(x) memset (&(x), 0, sizeof (x))
+
+namespace Kopete {
+
+namespace AV {
+
+VideoDevicePool *VideoDevicePool::s_self = NULL;
+__u64 VideoDevicePool::m_clients = 0;
+
+VideoDevicePool* VideoDevicePool::self()
+{
+ kdDebug(14010) << "libkopete (avdevice): self() called" << endl;
+ if (s_self == NULL)
+ {
+ s_self = new VideoDevicePool;
+ if (s_self)
+ m_clients = 0;
+ }
+ kdDebug(14010) << "libkopete (avdevice): self() exited successfuly. m_clients = " << m_clients << endl;
+ return s_self;
+}
+
+VideoDevicePool::VideoDevicePool()
+{
+}
+
+
+VideoDevicePool::~VideoDevicePool()
+{
+}
+
+
+
+
+/*!
+ \fn VideoDevicePool::open()
+ */
+int VideoDevicePool::open()
+{
+ /// @todo implement me
+
+ m_ready.lock();
+ if(!m_videodevice.size())
+ {
+ kdDebug(14010) << k_funcinfo << "open(): No devices found. Must scan for available devices." << m_current_device << endl;
+ scanDevices();
+ }
+ if(!m_videodevice.size())
+ {
+ kdDebug(14010) << k_funcinfo << "open(): No devices found. bailing out." << m_current_device << endl;
+ m_ready.unlock();
+ return EXIT_FAILURE;
+ }
+ if(m_current_device >= m_videodevice.size())
+ {
+ kdDebug(14010) << k_funcinfo << "open(): Device out of scope (" << m_current_device << "). Defaulting to the first one." << endl;
+ m_current_device = 0;
+ }
+ int isopen = m_videodevice[currentDevice()].open();
+ if ( isopen == EXIT_SUCCESS)
+ {
+ loadConfig(); // Temporary hack. The open() seems to clean the input parameters. Need to find a way to fix it.
+
+ }
+ m_clients++;
+ kdDebug(14010) << k_funcinfo << "Number of clients: " << m_clients << endl;
+ m_ready.unlock();
+ return isopen;
+}
+
+/*!
+ \fn VideoDevicePool::open(int device)
+ */
+int VideoDevicePool::open(unsigned int device)
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "open(" << device << ") called." << endl;
+ if(device >= m_videodevice.size())
+ {
+ kdDebug(14010) << k_funcinfo << "open(" << device <<"): Device does not exist." << endl;
+ return EXIT_FAILURE;
+ }
+ close();
+ kdDebug(14010) << k_funcinfo << "open(" << device << ") Setting m_current_Device to " << device << endl;
+ m_current_device = device;
+ saveConfig();
+ kdDebug(14010) << k_funcinfo << "open(" << device << ") Calling open()." << endl;
+ return open();
+}
+
+bool VideoDevicePool::isOpen()
+{
+ return m_videodevice[currentDevice()].isOpen();
+}
+
+/*!
+ \fn VideoDevicePool::showDeviceCapabilities(int device)
+ */
+int VideoDevicePool::showDeviceCapabilities(unsigned int device)
+{
+ return m_videodevice[device].showDeviceCapabilities();
+}
+
+int VideoDevicePool::width()
+{
+ return m_videodevice[currentDevice()].width();
+}
+
+int VideoDevicePool::minWidth()
+{
+ return m_videodevice[currentDevice()].minWidth();
+}
+
+int VideoDevicePool::maxWidth()
+{
+ return m_videodevice[currentDevice()].maxWidth();
+}
+
+int VideoDevicePool::height()
+{
+ return m_videodevice[currentDevice()].height();
+}
+
+int VideoDevicePool::minHeight()
+{
+ return m_videodevice[currentDevice()].minHeight();
+}
+
+int VideoDevicePool::maxHeight()
+{
+ return m_videodevice[currentDevice()].maxHeight();
+}
+
+int VideoDevicePool::setSize( int newwidth, int newheight)
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].setSize(newwidth, newheight);
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::setSize() fallback for no device." << endl;
+ m_buffer.width=newwidth;
+ m_buffer.height=newheight;
+ m_buffer.pixelformat= PIXELFORMAT_RGB24;
+ m_buffer.data.resize(m_buffer.width*m_buffer.height*3);
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::setSize() buffer size: "<< m_buffer.data.size() << endl;
+ }
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn VideoDevicePool::close()
+ */
+int VideoDevicePool::close()
+{
+ /// @todo implement me
+ if(m_clients)
+ m_clients--;
+ if((currentDevice() < m_videodevice.size())&&(!m_clients))
+ return m_videodevice[currentDevice()].close();
+ if(m_clients)
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::close() The video device is still in use." << endl;
+ if(currentDevice() >= m_videodevice.size())
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::close() Current device out of range." << endl;
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn VideoDevicePool::startCapturing()
+ */
+int VideoDevicePool::startCapturing()
+{
+ kdDebug(14010) << k_funcinfo << "startCapturing() called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].startCapturing();
+ return EXIT_FAILURE;
+}
+
+
+/*!
+ \fn VideoDevicePool::stopCapturing()
+ */
+int VideoDevicePool::stopCapturing()
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].stopCapturing();
+ return EXIT_FAILURE;
+}
+
+// Implementation of the methods that get / set input's adjustment parameters
+/*!
+ \fn VideoDevicePool::getBrightness()
+ */
+float VideoDevicePool::getBrightness()
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].getBrightness();
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::setBrightness(float brightness)
+ */
+float VideoDevicePool::setBrightness(float brightness)
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].setBrightness(brightness);
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::getContrast()
+ */
+float VideoDevicePool::getContrast()
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].getContrast();
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::setContrast(float contrast)
+ */
+float VideoDevicePool::setContrast(float contrast)
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].setContrast(contrast);
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::getSaturation()
+ */
+float VideoDevicePool::getSaturation()
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].getSaturation();
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::setSaturation(float saturation)
+ */
+float VideoDevicePool::setSaturation(float saturation)
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].setSaturation(saturation);
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::getWhiteness()
+ */
+float VideoDevicePool::getWhiteness()
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].getWhiteness();
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::setWhiteness(float whiteness)
+ */
+float VideoDevicePool::setWhiteness(float whiteness)
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].setWhiteness(whiteness);
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::getHue()
+ */
+float VideoDevicePool::getHue()
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].getHue();
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::setHue(float hue)
+ */
+float VideoDevicePool::setHue(float hue)
+{
+ if (currentDevice() < m_videodevice.size() )
+ return m_videodevice[currentDevice()].setHue(hue);
+ else
+ return 0;
+}
+
+/*!
+ \fn VideoDevicePool::getAutoBrightnessContrast()
+ */
+bool VideoDevicePool::getAutoBrightnessContrast()
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].getAutoBrightnessContrast();
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::setAutoBrightnessContrast(bool brightnesscontrast)
+ */
+bool VideoDevicePool::setAutoBrightnessContrast(bool brightnesscontrast)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::setAutoBrightnessContrast(" << brightnesscontrast << ") called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].setAutoBrightnessContrast(brightnesscontrast);
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::getAutoColorCorrection()
+ */
+bool VideoDevicePool::getAutoColorCorrection()
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].getAutoColorCorrection();
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::setAutoColorCorrection(bool colorcorrection)
+ */
+bool VideoDevicePool::setAutoColorCorrection(bool colorcorrection)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::setAutoColorCorrection(" << colorcorrection << ") called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].setAutoColorCorrection(colorcorrection);
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::getIMageAsMirror()
+ */
+bool VideoDevicePool::getImageAsMirror()
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].getImageAsMirror();
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::setImageAsMirror(bool imageasmirror)
+ */
+bool VideoDevicePool::setImageAsMirror(bool imageasmirror)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::setImageAsMirror(" << imageasmirror << ") called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].setImageAsMirror(imageasmirror);
+ return false;
+}
+
+/*!
+ \fn VideoDevicePool::getFrame()
+ */
+int VideoDevicePool::getFrame()
+{
+// kdDebug(14010) << k_funcinfo << "VideoDevicePool::getFrame() called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].getFrame();
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::getFrame() fallback for no device." << endl;
+ for(unsigned int loop=0; loop < m_buffer.data.size(); loop+=3)
+ {
+ m_buffer.data[loop] = 255;
+ m_buffer.data[loop+1] = 0;
+ m_buffer.data[loop+2] = 0;
+ }
+ }
+// kdDebug(14010) << k_funcinfo << "VideoDevicePool::getFrame() exited successfuly." << endl;
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn VideoDevicePool::getQImage(QImage *qimage)
+ */
+int VideoDevicePool::getImage(QImage *qimage)
+{
+// kdDebug(14010) << k_funcinfo << "VideoDevicePool::getImage() called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].getImage(qimage);
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::getImage() fallback for no device." << endl;
+ qimage->create(m_buffer.width, m_buffer.height,32, QImage::IgnoreEndian);
+ uchar *bits=qimage->bits();
+ switch(m_buffer.pixelformat)
+ {
+ case PIXELFORMAT_NONE : break;
+ case PIXELFORMAT_GREY : break;
+ case PIXELFORMAT_RGB332 : break;
+ case PIXELFORMAT_RGB555 : break;
+ case PIXELFORMAT_RGB555X: break;
+ case PIXELFORMAT_RGB565 : break;
+ case PIXELFORMAT_RGB565X: break;
+ case PIXELFORMAT_RGB24 :
+ {
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::getImage() fallback for no device - RGB24." << endl;
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = m_buffer.data[step];
+ bits[loop+1] = m_buffer.data[step+1];
+ bits[loop+2] = m_buffer.data[step+2];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ }
+ break;
+ case PIXELFORMAT_BGR24 : break;
+ {
+ int step=0;
+ for(int loop=0;loop < qimage->numBytes();loop+=4)
+ {
+ bits[loop] = m_buffer.data[step+2];
+ bits[loop+1] = m_buffer.data[step+1];
+ bits[loop+2] = m_buffer.data[step];
+ bits[loop+3] = 255;
+ step+=3;
+ }
+ }
+ break;
+ case PIXELFORMAT_RGB32 : memcpy(bits,&m_buffer.data[0], m_buffer.data.size());
+ break;
+ case PIXELFORMAT_BGR32 : break;
+ }
+ }
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::getImage() exited successfuly." << endl;
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::selectInput(int input)
+ */
+int VideoDevicePool::selectInput(int newinput)
+{
+ kdDebug(14010) << k_funcinfo << "VideoDevicePool::selectInput(" << newinput << ") called." << endl;
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].selectInput(newinput);
+ else
+ return 0;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::setInputParameters()
+ */
+int VideoDevicePool::setInputParameters()
+{
+ if(m_videodevice.size())
+ return m_videodevice[currentDevice()].setInputParameters();
+ else
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::fillDeviceKComboBox(KComboBox *combobox)
+ */
+int VideoDevicePool::fillDeviceKComboBox(KComboBox *combobox)
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "fillInputKComboBox: Called." << endl;
+ combobox->clear();
+ if(m_videodevice.size())
+ {
+ for (unsigned int loop=0; loop < m_videodevice.size(); loop++)
+ {
+ combobox->insertItem(m_videodevice[loop].m_name);
+ kdDebug(14010) << k_funcinfo << "DeviceKCombobox: Added device " << loop << ": " << m_videodevice[loop].m_name << endl;
+ }
+ combobox->setCurrentItem(currentDevice());
+ return EXIT_SUCCESS;
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::fillInputKComboBox(KComboBox *combobox)
+ */
+int VideoDevicePool::fillInputKComboBox(KComboBox *combobox)
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "fillInputKComboBox: Called." << endl;
+ combobox->clear();
+ if(m_videodevice.size())
+ {
+ if(m_videodevice[currentDevice()].inputs()>0)
+ {
+ for (unsigned int loop=0; loop < m_videodevice[currentDevice()].inputs(); loop++)
+ {
+ combobox->insertItem(m_videodevice[currentDevice()].m_input[loop].name);
+ kdDebug(14010) << k_funcinfo << "InputKCombobox: Added input " << loop << ": " << m_videodevice[currentDevice()].m_input[loop].name << " (tuner: " << m_videodevice[currentDevice()].m_input[loop].hastuner << ")" << endl;
+ }
+ combobox->setCurrentItem(currentInput());
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::fillStandardKComboBox(KComboBox *combobox)
+ */
+int VideoDevicePool::fillStandardKComboBox(KComboBox *combobox)
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "fillInputKComboBox: Called." << endl;
+ combobox->clear();
+ if(m_videodevice.size())
+ {
+ if(m_videodevice[currentDevice()].inputs()>0)
+ {
+ for (unsigned int loop=0; loop < 25; loop++)
+ {
+ if ( (m_videodevice[currentDevice()].m_input[currentInput()].m_standards) & (1 << loop) )
+ combobox->insertItem(m_videodevice[currentDevice()].signalStandardName( 1 << loop));
+/*
+ case STANDARD_PAL_B1 : return V4L2_STD_PAL_B1; break;
+ case STANDARD_PAL_G : return V4L2_STD_PAL_G; break;
+ case STANDARD_PAL_H : return V4L2_STD_PAL_H; break;
+ case STANDARD_PAL_I : return V4L2_STD_PAL_I; break;
+ case STANDARD_PAL_D : return V4L2_STD_PAL_D; break;
+ case STANDARD_PAL_D1 : return V4L2_STD_PAL_D1; break;
+ case STANDARD_PAL_K : return V4L2_STD_PAL_K; break;
+ case STANDARD_PAL_M : return V4L2_STD_PAL_M; break;
+ case STANDARD_PAL_N : return V4L2_STD_PAL_N; break;
+ case STANDARD_PAL_Nc : return V4L2_STD_PAL_Nc; break;
+ case STANDARD_PAL_60 : return V4L2_STD_PAL_60; break;
+ case STANDARD_NTSC_M : return V4L2_STD_NTSC_M; break;
+ case STANDARD_NTSC_M_JP : return V4L2_STD_NTSC_M_JP; break;
+ case STANDARD_NTSC_443 : return V4L2_STD_NTSC; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_SECAM_B : return V4L2_STD_SECAM_B; break;
+ case STANDARD_SECAM_D : return V4L2_STD_SECAM_D; break;
+ case STANDARD_SECAM_G : return V4L2_STD_SECAM_G; break;
+ case STANDARD_SECAM_H : return V4L2_STD_SECAM_H; break;
+ case STANDARD_SECAM_K : return V4L2_STD_SECAM_K; break;
+ case STANDARD_SECAM_K1 : return V4L2_STD_SECAM_K1; break;
+ case STANDARD_SECAM_L : return V4L2_STD_SECAM_L; break;
+ case STANDARD_SECAM_LC : return V4L2_STD_SECAM; break; // Using workaround value because my videodev2.h header seems to not include this standard in struct __u64 v4l2_std_id
+ case STANDARD_ATSC_8_VSB : return V4L2_STD_ATSC_8_VSB; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_ATSC_16_VSB : return V4L2_STD_ATSC_16_VSB; break; // ATSC/HDTV Standard officially not supported by V4L2 but exists in videodev2.h
+ case STANDARD_PAL_BG : return V4L2_STD_PAL_BG; break;
+ case STANDARD_PAL_DK : return V4L2_STD_PAL_DK; break;
+ case STANDARD_PAL : return V4L2_STD_PAL; break;
+ case STANDARD_NTSC : return V4L2_STD_NTSC; break;
+ case STANDARD_SECAM_DK : return V4L2_STD_SECAM_DK; break;
+ case STANDARD_SECAM : return V4L2_STD_SECAM; break;
+ case STANDARD_525_60 : return V4L2_STD_525_60; break;
+ case STANDARD_625_50 : return V4L2_STD_625_50; break;
+ case STANDARD_ALL : return V4L2_STD_ALL; break;
+
+ combobox->insertItem(m_videodevice[currentDevice()].m_input[loop].name);
+ kdDebug(14010) << k_funcinfo << "StandardKCombobox: Added input " << loop << ": " << m_videodevice[currentDevice()].m_input[loop].name << " (tuner: " << m_videodevice[currentDevice()].m_input[loop].hastuner << ")" << endl;*/
+ }
+ combobox->setCurrentItem(currentInput());
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::scanDevices()
+ */
+int VideoDevicePool::scanDevices()
+{
+ /// @todo implement me
+
+ kdDebug(14010) << k_funcinfo << "called" << endl;
+#if defined(__linux__) && defined(ENABLE_AV)
+ QDir videodevice_dir;
+ const QString videodevice_dir_path=QString::fromLocal8Bit("/dev/v4l/");
+ const QString videodevice_dir_filter=QString::fromLocal8Bit("video*");
+ VideoDevice videodevice;
+
+ m_videodevice.clear();
+ m_modelvector.clear();
+
+ videodevice_dir.setPath(videodevice_dir_path);
+ videodevice_dir.setNameFilter(videodevice_dir_filter);
+ videodevice_dir.setFilter( QDir::System | QDir::NoSymLinks | QDir::Readable | QDir::Writable );
+ videodevice_dir.setSorting( QDir::Name );
+
+ kdDebug(14010) << k_funcinfo << "Looking for devices in " << videodevice_dir_path << endl;
+ const QFileInfoList *list = videodevice_dir.entryInfoList();
+
+ if (!list)
+ {
+ kdDebug(14010) << k_funcinfo << "Found no suitable devices in " << videodevice_dir_path << endl;
+ QDir videodevice_dir;
+ const QString videodevice_dir_path=QString::fromLocal8Bit("/dev/");
+ const QString videodevice_dir_filter=QString::fromLocal8Bit("video*");
+ VideoDevice videodevice;
+
+ videodevice_dir.setPath(videodevice_dir_path);
+ videodevice_dir.setNameFilter(videodevice_dir_filter);
+ videodevice_dir.setFilter( QDir::System | QDir::NoSymLinks | QDir::Readable | QDir::Writable );
+ videodevice_dir.setSorting( QDir::Name );
+
+ kdDebug(14010) << k_funcinfo << "Looking for devices in " << videodevice_dir_path << endl;
+ const QFileInfoList *list = videodevice_dir.entryInfoList();
+
+ if (!list)
+ {
+ kdDebug(14010) << k_funcinfo << "Found no suitable devices in " << videodevice_dir_path << endl;
+ return EXIT_FAILURE;
+ }
+
+ QFileInfoListIterator fileiterator ( *list );
+ QFileInfo *fileinfo;
+
+ kdDebug(14010) << k_funcinfo << "scanning devices in " << videodevice_dir_path << "..." << endl;
+ while ( (fileinfo = fileiterator.current()) != 0 )
+ {
+ videodevice.setFileName(fileinfo->absFilePath());
+ kdDebug(14010) << k_funcinfo << "Found device " << videodevice.full_filename << endl;
+ videodevice.open(); // It should be opened with O_NONBLOCK (it's a FIFO) but I dunno how to do it using QFile
+ if(videodevice.isOpen())
+ {
+ kdDebug(14010) << k_funcinfo << "File " << videodevice.full_filename << " was opened successfuly" << endl;
+
+// This must be changed to proper code to handle multiple devices of the same model. It currently simply add models without proper checking
+ videodevice.close();
+ videodevice.m_modelindex=m_modelvector.addModel (videodevice.m_model); // Adds device to the device list and sets model number
+ m_videodevice.push_back(videodevice);
+ }
+ ++fileiterator;
+ }
+
+
+ m_current_device = 0;
+ loadConfig();
+ kdDebug(14010) << k_funcinfo << "exited successfuly" << endl;
+ return EXIT_SUCCESS;
+
+ }
+ QFileInfoListIterator fileiterator ( *list );
+ QFileInfo *fileinfo;
+
+ kdDebug(14010) << k_funcinfo << "scanning devices in " << videodevice_dir_path << "..." << endl;
+ while ( (fileinfo = fileiterator.current()) != 0 )
+ {
+ videodevice.setFileName(fileinfo->absFilePath());
+ kdDebug(14010) << k_funcinfo << "Found device " << videodevice.full_filename << endl;
+ videodevice.open(); // It should be opened with O_NONBLOCK (it's a FIFO) but I dunno how to do it using QFile
+ if(videodevice.isOpen())
+ {
+ kdDebug(14010) << k_funcinfo << "File " << videodevice.full_filename << " was opened successfuly" << endl;
+
+// This must be changed to proper code to handle multiple devices of the same model. It currently simply add models without proper checking
+ videodevice.close();
+ videodevice.m_modelindex=m_modelvector.addModel (videodevice.m_model); // Adds device to the device list and sets model number
+ m_videodevice.push_back(videodevice);
+ }
+ ++fileiterator;
+ }
+ m_current_device = 0;
+ loadConfig();
+#endif
+ kdDebug(14010) << k_funcinfo << "exited successfuly" << endl;
+ return EXIT_SUCCESS;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::hasDevices()
+ */
+bool VideoDevicePool::hasDevices()
+{
+ /// @todo implement me
+ if(m_videodevice.size())
+ return true;
+ return false;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::size()
+ */
+size_t VideoDevicePool::size()
+{
+ /// @todo implement me
+ return m_videodevice.size();
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::currentDevice()
+ */
+unsigned int VideoDevicePool::currentDevice()
+{
+ /// @todo implement me
+ return m_current_device;
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::currentInput()
+ */
+int VideoDevicePool::currentInput()
+{
+ /// @todo implement me
+ return m_videodevice[currentDevice()].currentInput();
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::currentInput()
+ */
+unsigned int VideoDevicePool::inputs()
+{
+ /// @todo implement me
+ return m_videodevice[currentDevice()].inputs();
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::loadConfig()
+ */
+void VideoDevicePool::loadConfig()
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "called" << endl;
+ if((hasDevices())&&(m_clients==0))
+ {
+ KConfig *config = KGlobal::config();
+ config->setGroup("Video Device Settings");
+ const QString currentdevice = config->readEntry("Current Device", QString::null);
+ kdDebug(14010) << k_funcinfo << "Current device: " << currentdevice << endl;
+
+// m_current_device = 0; // Must check this thing because of the fact that multiple loadConfig in other methodas can do bad things. Watch out!
+
+ VideoDeviceVector::iterator vditerator;
+ for( vditerator = m_videodevice.begin(); vditerator != m_videodevice.end(); ++vditerator )
+ {
+ const QString modelindex = QString::fromLocal8Bit ( "Model %1 Device %2") .arg ((*vditerator).m_name ) .arg ((*vditerator).m_modelindex);
+ if(modelindex == currentdevice)
+ {
+ m_current_device = vditerator - m_videodevice.begin();
+// kdDebug(14010) << k_funcinfo << "This place will be used to set " << modelindex << " as the current device ( " << (vditerator - m_videodevice.begin()) << " )." << endl;
+ }
+ const QString name = config->readEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Name") .arg ((*vditerator).m_name ) .arg ((*vditerator).m_modelindex)), (*vditerator).m_model);
+ const int currentinput = config->readNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Current input") .arg ((*vditerator).m_name ) .arg ((*vditerator).m_modelindex)), 0);
+ kdDebug(14010) << k_funcinfo << "Device name: " << name << endl;
+ kdDebug(14010) << k_funcinfo << "Device current input: " << currentinput << endl;
+ (*vditerator).selectInput(currentinput);
+
+ for (size_t input = 0 ; input < (*vditerator).m_input.size(); input++)
+ {
+ const float brightness = config->readDoubleNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Brightness").arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , 0.5 );
+ const float contrast = config->readDoubleNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Contrast") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , 0.5 );
+ const float saturation = config->readDoubleNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Saturation").arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , 0.5 );
+ const float whiteness = config->readDoubleNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Whiteness") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , 0.5 );
+ const float hue = config->readDoubleNumEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Hue") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , 0.5 );
+ const bool autobrightnesscontrast = config->readBoolEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 AutoBrightnessContrast") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , false );
+ const bool autocolorcorrection = config->readBoolEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 AutoColorCorrection") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , false );
+ const bool imageasmirror = config->readBoolEntry((QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 mageAsMirror") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input)) , false );
+ (*vditerator).setBrightness(brightness);
+ (*vditerator).setContrast(contrast);
+ (*vditerator).setSaturation(saturation);
+ (*vditerator).setHue(hue);
+ (*vditerator).setAutoBrightnessContrast(autobrightnesscontrast);
+ (*vditerator).setAutoColorCorrection(autocolorcorrection);
+ (*vditerator).setImageAsMirror(imageasmirror);
+ kdDebug(14010) << k_funcinfo << "Brightness:" << brightness << endl;
+ kdDebug(14010) << k_funcinfo << "Contrast :" << contrast << endl;
+ kdDebug(14010) << k_funcinfo << "Saturation:" << saturation << endl;
+ kdDebug(14010) << k_funcinfo << "Whiteness :" << whiteness << endl;
+ kdDebug(14010) << k_funcinfo << "Hue :" << hue << endl;
+ kdDebug(14010) << k_funcinfo << "AutoBrightnessContrast:" << autobrightnesscontrast << endl;
+ kdDebug(14010) << k_funcinfo << "AutoColorCorrection :" << autocolorcorrection << endl;
+ kdDebug(14010) << k_funcinfo << "ImageAsMirror :" << imageasmirror << endl;
+ }
+ }
+ }
+}
+
+/*!
+ \fn Kopete::AV::VideoDevicePool::saveConfig()
+ */
+void VideoDevicePool::saveConfig()
+{
+ /// @todo implement me
+ kdDebug(14010) << k_funcinfo << "called" << endl;
+ if(hasDevices())
+ {
+ KConfig *config = KGlobal::config();
+ config->setGroup("Video Device Settings");
+
+/* if(m_modelvector.size())
+ {
+ VideoDeviceModelPool::m_devicemodel::iterator vmiterator;
+ for( vmiterator = m_modelvector.begin(); vmiterator != m_modelvector.end(); ++vmiterator )
+ {
+ kdDebug(14010) << "Device Model: " << (*vmiterator).model << endl;
+ kdDebug(14010) << "Device Count: " << (*vmiterator).count << endl;
+ }
+ }
+*/
+// Stores what is the current video device in use
+ const QString currentdevice = QString::fromLocal8Bit ( "Model %1 Device %2" ) .arg(m_videodevice[m_current_device].m_model) .arg(m_videodevice[m_current_device].m_modelindex);
+ config->writeEntry( "Current Device", currentdevice);
+
+ VideoDeviceVector::iterator vditerator;
+ for( vditerator = m_videodevice.begin(); vditerator != m_videodevice.end(); ++vditerator )
+ {
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Name:" << (*vditerator).m_name << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Current input:" << (*vditerator).currentInput() << endl;
+
+// Stores current input for the given video device
+ const QString name = QString::fromLocal8Bit ( "Model %1 Device %2 Name") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex);
+ const QString currentinput = QString::fromLocal8Bit ( "Model %1 Device %2 Current input") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex);
+ config->writeEntry( name, (*vditerator).m_name);
+ config->writeEntry( currentinput, (*vditerator).currentInput());
+
+ for (size_t input = 0 ; input < (*vditerator).m_input.size(); input++)
+ {
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Brightness: " << (*vditerator).m_input[input].getBrightness() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Contrast : " << (*vditerator).m_input[input].getContrast() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Saturation: " << (*vditerator).m_input[input].getSaturation() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Whiteness : " << (*vditerator).m_input[input].getWhiteness() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Hue : " << (*vditerator).m_input[input].getHue() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Automatic brightness / contrast: " << (*vditerator).m_input[input].getAutoBrightnessContrast() << endl;
+ kdDebug(14010) << "Model:" << (*vditerator).m_model << ":Index:" << (*vditerator).m_modelindex << ":Input:" << input << ":Automatic color correction : " << (*vditerator).m_input[input].getAutoColorCorrection() << endl;
+
+// Stores configuration about each channel
+ const QString brightness = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Brightness") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString contrast = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Contrast") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString saturation = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Saturation") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString whiteness = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Whiteness") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString hue = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 Hue") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString autobrightnesscontrast = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 AutoBrightnessContrast") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString autocolorcorrection = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 AutoColorCorrection") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ const QString imageasmirror = QString::fromLocal8Bit ( "Model %1 Device %2 Input %3 ImageAsMirror") .arg ((*vditerator).m_model ) .arg ((*vditerator).m_modelindex) .arg (input);
+ config->writeEntry( brightness, (*vditerator).m_input[input].getBrightness());
+ config->writeEntry( contrast, (*vditerator).m_input[input].getContrast());
+ config->writeEntry( saturation, (*vditerator).m_input[input].getSaturation());
+ config->writeEntry( whiteness, (*vditerator).m_input[input].getWhiteness());
+ config->writeEntry( hue, (*vditerator).m_input[input].getHue());
+ config->writeEntry( autobrightnesscontrast, (*vditerator).m_input[input].getAutoBrightnessContrast());
+ config->writeEntry( autocolorcorrection, (*vditerator).m_input[input].getAutoColorCorrection());
+ config->writeEntry( imageasmirror, (*vditerator).m_input[input].getImageAsMirror());
+ }
+ }
+ config->sync();
+ kdDebug(14010) << endl;
+ }
+}
+
+
+
+}
+
+}
diff --git a/kopete/libkopete/avdevice/videodevicepool.h b/kopete/libkopete/avdevice/videodevicepool.h
new file mode 100644
index 00000000..1fbdb3e1
--- /dev/null
+++ b/kopete/libkopete/avdevice/videodevicepool.h
@@ -0,0 +1,127 @@
+/*
+ videodevicepool.h - Kopete Multiple Video Device handler Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_AVVIDEODEVICE_H
+#define KOPETE_AVVIDEODEVICE_H
+
+#include <qvaluevector.h>
+#include <iostream>
+
+
+#include "videoinput.h"
+#include "videodevicemodelpool.h"
+#include <qstring.h>
+#include <qimage.h>
+#include <qvaluevector.h>
+#include <qmutex.h>
+#include <kcombobox.h>
+#include "videodevice.h"
+#include "kopete_export.h"
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+namespace Kopete {
+
+namespace AV {
+
+/**
+This class allows kopete to check for the existence, open, configure, test, set parameters, grab frames from and close a given video capture card using the Video4Linux API.
+
+@author Cláudio da Silveira Pinheiro
+*/
+
+typedef QValueVector<Kopete::AV::VideoDevice> VideoDeviceVector;
+
+class VideoDevicePoolPrivate;
+
+class KOPETE_EXPORT VideoDevicePool
+{
+public:
+ static VideoDevicePool* self();
+ int open();
+ int open(unsigned int device);
+ bool isOpen();
+ int getFrame();
+ int width();
+ int minWidth();
+ int maxWidth();
+ int height();
+ int minHeight();
+ int maxHeight();
+ int setSize( int newwidth, int newheight);
+ int close();
+ int startCapturing();
+ int stopCapturing();
+ int readFrame();
+ int getImage(QImage *qimage);
+ int selectInput(int newinput);
+ int setInputParameters();
+ int scanDevices();
+ bool hasDevices();
+ size_t size();
+ ~VideoDevicePool();
+ VideoDeviceVector m_videodevice; // Vector to be filled with found devices
+ VideoDeviceModelPool m_modelvector; // Vector to be filled with unique device models
+ int fillDeviceKComboBox(KComboBox *combobox);
+ int fillInputKComboBox(KComboBox *combobox);
+ int fillStandardKComboBox(KComboBox *combobox);
+ unsigned int currentDevice();
+ int currentInput();
+ unsigned int inputs();
+
+ float getBrightness();
+ float setBrightness(float brightness);
+ float getContrast();
+ float setContrast(float contrast);
+ float getSaturation();
+ float setSaturation(float saturation);
+ float getWhiteness();
+ float setWhiteness(float whiteness);
+ float getHue();
+ float setHue(float hue);
+
+ bool getAutoBrightnessContrast();
+ bool setAutoBrightnessContrast(bool brightnesscontrast);
+ bool getAutoColorCorrection();
+ bool setAutoColorCorrection(bool colorcorrection);
+ bool getImageAsMirror();
+ bool setImageAsMirror(bool imageasmirror);
+
+ void loadConfig(); // Load configuration parameters;
+ void saveConfig(); // Save configuretion parameters;
+
+protected:
+ int xioctl(int request, void *arg);
+ int errnoReturn(const char* s);
+ int showDeviceCapabilities(unsigned int device);
+ void guessDriver();
+ unsigned int m_current_device;
+ struct imagebuffer m_buffer; // only used when no devices were found
+
+ QMutex m_ready;
+private:
+ VideoDevicePool();
+ static VideoDevicePool* s_self;
+ static __u64 m_clients; // Number of instances
+};
+
+}
+
+}
+
+#endif
diff --git a/kopete/libkopete/avdevice/videoinput.cpp b/kopete/libkopete/avdevice/videoinput.cpp
new file mode 100644
index 00000000..5f0f8e58
--- /dev/null
+++ b/kopete/libkopete/avdevice/videoinput.cpp
@@ -0,0 +1,172 @@
+/*
+ videoinput.cpp - Kopete Video Input Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "videoinput.h"
+
+namespace Kopete {
+
+namespace AV {
+
+VideoInput::VideoInput()
+{
+ kdDebug() << k_funcinfo << "Executing Video Input's constructor!!!" << endl;
+ m_brightness = 0.5;
+ m_contrast = 0.5;
+ m_saturation = 0.5;
+ m_hue = 0.5;
+ m_autobrightnesscontrast = false;
+ m_autocolorcorrection = false;
+}
+
+
+VideoInput::~VideoInput()
+{
+}
+
+float VideoInput::getBrightness()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_brightness;
+}
+
+float VideoInput::setBrightness(float brightness)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ if ( brightness > 1 )
+ brightness = 1;
+ else
+ if ( brightness < 0 )
+ brightness = 0;
+ m_brightness = brightness;
+ return getBrightness();
+}
+
+float VideoInput::getContrast()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_contrast;
+}
+
+float VideoInput::setContrast(float contrast)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ if ( contrast > 1 )
+ contrast = 1;
+ else
+ if ( contrast < 0 )
+ contrast = 0;
+ m_contrast = contrast;
+ return getContrast();
+}
+
+float VideoInput::getSaturation()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_saturation;
+}
+
+float VideoInput::setSaturation(float saturation)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ if ( saturation > 1 )
+ saturation = 1;
+ else
+ if ( saturation < 0 )
+ saturation = 0;
+ m_saturation = saturation;
+ return getSaturation();
+}
+
+float VideoInput::getWhiteness()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_whiteness;
+}
+
+float VideoInput::setWhiteness(float whiteness)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ if ( whiteness > 1 )
+ whiteness = 1;
+ else
+ if ( whiteness < 0 )
+ whiteness = 0;
+ m_whiteness = whiteness;
+ return getWhiteness();
+}
+
+float VideoInput::getHue()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_hue;
+}
+
+float VideoInput::setHue(float hue)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ if ( hue > 1 )
+ hue = 1;
+ else
+ if ( hue < 0 )
+ hue = 0;
+ m_hue = hue;
+ return getHue();
+}
+
+
+bool VideoInput::getAutoBrightnessContrast()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_autobrightnesscontrast;
+}
+
+bool VideoInput::setAutoBrightnessContrast(bool brightnesscontrast)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ m_autobrightnesscontrast = brightnesscontrast;
+ return getAutoBrightnessContrast();
+}
+
+bool VideoInput::getAutoColorCorrection()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_autocolorcorrection;
+}
+
+bool VideoInput::setAutoColorCorrection(bool colorcorrection)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ m_autocolorcorrection = colorcorrection;
+ return getAutoColorCorrection();
+}
+
+bool VideoInput::getImageAsMirror()
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ return m_imageasmirror;
+}
+
+bool VideoInput::setImageAsMirror(bool imageasmirror)
+{
+// kdDebug() << k_funcinfo << " called." << endl;
+ m_imageasmirror = imageasmirror;
+ return getImageAsMirror();
+}
+
+}
+
+}
diff --git a/kopete/libkopete/avdevice/videoinput.h b/kopete/libkopete/avdevice/videoinput.h
new file mode 100644
index 00000000..3381663e
--- /dev/null
+++ b/kopete/libkopete/avdevice/videoinput.h
@@ -0,0 +1,89 @@
+/*
+ videodevice.h - Kopete Video Input Class
+
+ Copyright (c) 2005-2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#define ENABLE_AV
+
+#ifndef KOPETE_AVVIDEOINPUT_H
+#define KOPETE_AVVIDEOINPUT_H
+
+#ifdef __linux__
+#include <asm/types.h>
+#undef __STRICT_ANSI__
+#endif // __linux__
+#ifndef __u64 //required by videodev.h
+#define __u64 unsigned long long
+#endif // __u64*/
+
+#include <qstring.h>
+#include <kdebug.h>
+#include <qvaluevector.h>
+#include "kopete_export.h"
+
+#include "videocontrol.h"
+
+namespace Kopete {
+
+namespace AV {
+
+/**
+@author Kopete Developers
+*/
+class KOPETE_EXPORT VideoInput{
+public:
+ VideoInput();
+ ~VideoInput();
+ QString name;
+ int hastuner;
+ __u64 m_standards;
+
+
+ float getBrightness();
+ float setBrightness(float brightness);
+ float getContrast();
+ float setContrast(float contrast);
+ float getSaturation();
+ float setSaturation(float saturation);
+ float getWhiteness();
+ float setWhiteness(float whiteness);
+ float getHue();
+ float setHue(float Hue);
+ bool getAutoBrightnessContrast();
+ bool setAutoBrightnessContrast(bool brightnesscontrast);
+ bool getAutoColorCorrection();
+ bool setAutoColorCorrection(bool colorcorrection);
+ bool getImageAsMirror();
+ bool setImageAsMirror(bool imageasmirror);
+
+protected:
+ QValueVector<VideoControl> m_control;
+ float m_brightness;
+ float m_contrast;
+ float m_saturation;
+ float m_whiteness;
+ float m_hue;
+ bool m_autobrightnesscontrast;
+ bool m_autocolorcorrection;
+ bool m_imageasmirror;
+
+
+};
+
+}
+
+}
+
+#endif
diff --git a/kopete/libkopete/clientiface.h b/kopete/libkopete/clientiface.h
new file mode 100644
index 00000000..02162189
--- /dev/null
+++ b/kopete/libkopete/clientiface.h
@@ -0,0 +1,56 @@
+#ifndef KDED_NETWORKSTATUS_CLIENTIFACE_H
+#define KDED_NETWORKSTATUS_CLIENTIFACE_H
+
+#include "networkstatuscommon.h"
+
+#include <dcopobject.h>
+
+class ClientIface : virtual public DCOPObject
+{
+K_DCOP
+k_dcop:
+ /** Get the set of networks that the daemon is aware of. Mostly for debug */
+ virtual QStringList networks() = 0;
+ /**
+ * Get the status of the connection to the given host.
+ * @param host
+ * @return a NetworkStatus::EnumStatus representing the state of the connection to the given host
+ */
+ virtual int status( const QString & host = QString::null ) = 0;
+ /**
+ * Request a connection to the named host, registering the application's usage of this connection
+ * @param host The hostname the client wants to connect to.
+ * @param userInitiated Indicates whether the connection is a direct result of a user action or is a background task. Used by the daemon to decide whether to create an on-demand connection.
+ * @return An NetworkStatus::EnumRequestResult indicating whether the request was accepted
+ */
+ virtual int request( const QString & host, bool userInitiated ) = 0;
+ /**
+ * Indicate that a previously registered connection to the given host is no longer needed by this client
+ * @param host The hostname being relinquished.
+ */
+ virtual void relinquish( const QString & host ) = 0;
+ /**
+ * Indicate that a communication failure has occured for a given host
+ * @param host The hostname for which the failure occurred.
+ * @return True indicates the caller should try again to lookup the host, as the daemon has another IP address available.
+ */
+ virtual bool reportFailure( const QString & host ) = 0;
+ /**
+ * Utility method to check the daemon's status
+ */
+k_dcop_signals:
+ /**
+ * A status change occurred for the network(s) used to connect to the given host.
+ * @param host The host which the application has indicated it is using
+ * @param status The new status of the network used to reach host.
+ */
+ void statusChange( QString host, int status );
+ /**
+ * The network would like to shut down - any clients using this host are to finish using it immediately and call
+ * relinquish() when done.
+ * @param host The host, registered as in use by applications, which is about to be disconnected.
+ */
+ void shutdownRequested( QString host );
+};
+
+#endif
diff --git a/kopete/libkopete/compat/Makefile.am b/kopete/libkopete/compat/Makefile.am
new file mode 100644
index 00000000..6723bcf5
--- /dev/null
+++ b/kopete/libkopete/compat/Makefile.am
@@ -0,0 +1,8 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+noinst_LTLIBRARIES = libkopetecompat.la
+
+libkopetecompat_la_SOURCES = kpixmapregionselectordialog.cpp kpixmapregionselectorwidget.cpp
+libkopetecompat_la_LDFLAGS = -no-undefined $(all_libraries)
+libkopetecompat_la_LIBADD = $(LIB_KDEUI) $(LIB_KDECORE)
+
diff --git a/kopete/libkopete/compat/kpixmapregionselectordialog.cpp b/kopete/libkopete/compat/kpixmapregionselectordialog.cpp
new file mode 100644
index 00000000..ee9d185e
--- /dev/null
+++ b/kopete/libkopete/compat/kpixmapregionselectordialog.cpp
@@ -0,0 +1,127 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kpixmapregionselectordialog.h"
+#include <kdialogbase.h>
+#include <qdialog.h>
+#include <qdesktopwidget.h>
+#include <klocale.h>
+#include <kdialog.h>
+
+KPixmapRegionSelectorDialog::KPixmapRegionSelectorDialog(QWidget *parent,
+ const char *name, bool modal ) : KDialogBase(parent, name, modal, i18n("Select Region of Image"), Help|Ok|Cancel, Ok, true )
+{
+ QVBox *vbox=new QVBox(this);
+ new QLabel(i18n("Please click and drag on the image to select the region of interest:"), vbox);
+ m_pixmapSelectorWidget= new KPixmapRegionSelectorWidget(vbox);
+
+ vbox->setSpacing( KDialog::spacingHint() );
+
+ setMainWidget(vbox);
+}
+
+KPixmapRegionSelectorDialog::~KPixmapRegionSelectorDialog()
+{
+}
+
+QRect KPixmapRegionSelectorDialog::getSelectedRegion(const QPixmap &pixmap, QWidget *parent )
+{
+ KPixmapRegionSelectorDialog dialog(parent);
+
+ dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap);
+
+ QDesktopWidget desktopWidget;
+ QRect screen=desktopWidget.availableGeometry();
+ dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize(
+ (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5));
+
+ int result = dialog.exec();
+
+ QRect rect;
+
+ if ( result == QDialog::Accepted )
+ rect = dialog.pixmapRegionSelectorWidget()->unzoomedSelectedRegion();
+
+ return rect;
+}
+
+QRect KPixmapRegionSelectorDialog::getSelectedRegion(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent )
+{
+ KPixmapRegionSelectorDialog dialog(parent);
+
+ dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap);
+ dialog.pixmapRegionSelectorWidget()->setSelectionAspectRatio(aspectRatioWidth,aspectRatioHeight);
+
+ QDesktopWidget desktopWidget;
+ QRect screen=desktopWidget.availableGeometry();
+ dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize(
+ (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5));
+
+ int result = dialog.exec();
+
+ QRect rect;
+
+ if ( result == QDialog::Accepted )
+ rect = dialog.pixmapRegionSelectorWidget()->unzoomedSelectedRegion();
+
+ return rect;
+}
+
+QImage KPixmapRegionSelectorDialog::getSelectedImage(const QPixmap &pixmap, QWidget *parent )
+{
+ KPixmapRegionSelectorDialog dialog(parent);
+
+ dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap);
+
+ QDesktopWidget desktopWidget;
+ QRect screen=desktopWidget.availableGeometry();
+ dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize(
+ (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5));
+ int result = dialog.exec();
+
+ QImage image;
+
+ if ( result == QDialog::Accepted )
+ image = dialog.pixmapRegionSelectorWidget()->selectedImage();
+
+ return image;
+}
+
+QImage KPixmapRegionSelectorDialog::getSelectedImage(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent )
+{
+ KPixmapRegionSelectorDialog dialog(parent);
+
+ dialog.pixmapRegionSelectorWidget()->setPixmap(pixmap);
+ dialog.pixmapRegionSelectorWidget()->setSelectionAspectRatio(aspectRatioWidth,aspectRatioHeight);
+
+ QDesktopWidget desktopWidget;
+ QRect screen=desktopWidget.availableGeometry();
+ dialog.pixmapRegionSelectorWidget()->setMaximumWidgetSize(
+ (int)(screen.width()*4.0/5), (int)(screen.height()*4.0/5));
+
+ int result = dialog.exec();
+
+ QImage image;
+
+ if ( result == QDialog::Accepted )
+ image = dialog.pixmapRegionSelectorWidget()->selectedImage();
+
+ return image;
+}
+
diff --git a/kopete/libkopete/compat/kpixmapregionselectordialog.h b/kopete/libkopete/compat/kpixmapregionselectordialog.h
new file mode 100644
index 00000000..1c15067e
--- /dev/null
+++ b/kopete/libkopete/compat/kpixmapregionselectordialog.h
@@ -0,0 +1,107 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KPIXMAPREGIONSELECTORDIALOG_H__
+#define __KPIXMAPREGIONSELECTORDIALOG_H__
+
+#include <qimage.h>
+
+#include <kdialogbase.h>
+#include <kpixmapregionselectorwidget.h>
+
+/**
+ * A dialog that uses a KPixmapRegionSelectorWidget to allow the user
+ * to select a region of an image. If you want to use special features
+ * like forcing the selected area to have a fixed aspect ratio, you can use
+ * @see pixmapRegionSelectorWidget() to get the pointer to the
+ * KPixmapRegionSelectorWidget object and set the desired options there.
+ *
+ * There are some convenience methods that allow to easily show a dialog
+ * for the user to select a region of an image, and just care about the selected
+ * image.
+ *
+ * @author Antonio Larrosa <larrosa@kde.org>
+ * @since 3.4
+ */
+class KOPETE_EXPORT KPixmapRegionSelectorDialog : public KDialogBase
+{
+public:
+ /**
+ * The constructor of an empty KPixmapRegionSelectorDialog, you have to call
+ * later the setPixmap method of the KPixmapRegionSelectorWidget widget of
+ * the new object.
+ */
+ KPixmapRegionSelectorDialog(QWidget *parent=0L, const char *name=0L,
+ bool modal = false );
+ /**
+ * The destructor of the dialog
+ */
+ ~KPixmapRegionSelectorDialog();
+
+ /**
+ * @returns the KPixmapRegionSelectorWidget widget so that additional
+ * parameters can be set by using it.
+ */
+ KPixmapRegionSelectorWidget *pixmapRegionSelectorWidget() const
+ { return m_pixmapSelectorWidget; };
+
+ /**
+ * Creates a modal dialog, lets the user to select a region of the @p pixmap
+ * and returns when the dialog is closed.
+ *
+ * @returns the selected rectangle, or an invalid rectangle if the user
+ * pressed the Cancel button.
+ */
+ static QRect getSelectedRegion(const QPixmap &pixmap, QWidget *parent = 0L );
+
+ /**
+ * Creates a modal dialog, lets the user to select a region of the @p pixmap
+ * with the same aspect ratio than @p aspectRatioWidth x @p aspectRatioHeight
+ * and returns when the dialog is closed.
+ *
+ * @returns the selected rectangle, or an invalid rectangle if the user
+ * pressed the Cancel button.
+ */
+ static QRect getSelectedRegion(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent = 0L );
+
+ /**
+ * Creates a modal dialog, lets the user to select a region of the @p pixmap
+ * and returns when the dialog is closed.
+ *
+ * @returns the selected image, or an invalid image if the user
+ * pressed the Cancel button.
+ */
+ static QImage getSelectedImage(const QPixmap &pixmap, QWidget *parent = 0L );
+
+ /**
+ * Creates a modal dialog, lets the user to select a region of the @p pixmap
+ * with the same aspect ratio than @p aspectRatioWidth x @p aspectRatioHeight
+ * and returns when the dialog is closed.
+ *
+ * @returns the selected image, or an invalid image if the user
+ * pressed the Cancel button.
+ */
+ static QImage getSelectedImage(const QPixmap &pixmap, int aspectRatioWidth, int aspectRatioHeight, QWidget *parent = 0L );
+
+protected:
+ KPixmapRegionSelectorWidget *m_pixmapSelectorWidget;
+};
+
+
+#endif
diff --git a/kopete/libkopete/compat/kpixmapregionselectorwidget.cpp b/kopete/libkopete/compat/kpixmapregionselectorwidget.cpp
new file mode 100644
index 00000000..da2be5f9
--- /dev/null
+++ b/kopete/libkopete/compat/kpixmapregionselectorwidget.cpp
@@ -0,0 +1,450 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/* NOTE: There are two copies of this .h and the .cpp file, with subtle differences.
+ * One copy is in kdelibs/kdeui, and the other copy is in kdepim/libkdepim
+ * This is because kdepim has to remain backwards compatible. Any changes
+ * to either file should be made to the other.
+ */
+
+#include "kpixmapregionselectorwidget.h"
+#include <qpainter.h>
+#include <qcolor.h>
+#include <qimage.h>
+#include <qlayout.h>
+#include <kimageeffect.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <stdlib.h>
+#include <qcursor.h>
+#include <qapplication.h>
+
+KPixmapRegionSelectorWidget::KPixmapRegionSelectorWidget( QWidget *parent,
+ const char *name) : QWidget( parent, name)
+{
+ QHBoxLayout * hboxLayout=new QHBoxLayout( this );
+
+ hboxLayout->addStretch();
+ QVBoxLayout * vboxLayout=new QVBoxLayout( hboxLayout );
+
+ vboxLayout->addStretch();
+ m_label = new QLabel(this, "pixmapHolder");
+ m_label->setBackgroundMode( Qt::NoBackground );
+ m_label->installEventFilter( this );
+
+ vboxLayout->addWidget(m_label);
+ vboxLayout->addStretch();
+
+ hboxLayout->addStretch();
+
+ m_forcedAspectRatio=0;
+
+ m_zoomFactor=1.0;
+}
+
+KPixmapRegionSelectorWidget::~KPixmapRegionSelectorWidget()
+{
+}
+
+void KPixmapRegionSelectorWidget::setPixmap( const QPixmap &pixmap )
+{
+ Q_ASSERT(!pixmap.isNull()); //This class isn't designed to deal with null pixmaps.
+ m_originalPixmap = pixmap;
+ m_unzoomedPixmap = pixmap;
+ m_label->setPixmap( pixmap );
+ resetSelection();
+}
+
+void KPixmapRegionSelectorWidget::resetSelection()
+{
+ m_selectedRegion = m_originalPixmap.rect();
+ updatePixmap();
+}
+
+QRect KPixmapRegionSelectorWidget::selectedRegion() const
+{
+ return m_selectedRegion;
+}
+
+void KPixmapRegionSelectorWidget::setSelectedRegion(const QRect &rect)
+{
+ if (!rect.isValid()) resetSelection();
+ else
+ {
+ m_selectedRegion=rect;
+ updatePixmap();
+
+ QRect r=unzoomedSelectedRegion();
+ }
+}
+
+void KPixmapRegionSelectorWidget::updatePixmap()
+{
+ Q_ASSERT(!m_originalPixmap.isNull()); if(m_originalPixmap.isNull()) { m_label->setPixmap(m_originalPixmap); return; }
+ if (m_selectedRegion.width()>m_originalPixmap.width()) m_selectedRegion.setWidth( m_originalPixmap.width() );
+ if (m_selectedRegion.height()>m_originalPixmap.height()) m_selectedRegion.setHeight( m_originalPixmap.height() );
+
+ QPainter painter;
+ if (m_linedPixmap.isNull())
+ {
+ m_linedPixmap = m_originalPixmap;
+
+ painter.begin(&m_linedPixmap);
+ painter.setRasterOp( Qt::XorROP );
+ painter.fillRect(0,0,m_linedPixmap.width(), m_linedPixmap.height(),
+ QBrush( QColor(255,255,255), Qt::BDiagPattern) );
+ painter.end();
+
+ QImage image=m_linedPixmap.convertToImage();
+ image=KImageEffect::fade(image, (float)0.4, QColor(0,0,0));
+ m_linedPixmap.convertFromImage(image);
+ }
+
+ QPixmap pixmap = m_linedPixmap;
+
+ painter.begin(&pixmap);
+ painter.drawPixmap( m_selectedRegion.topLeft(),
+ m_originalPixmap, m_selectedRegion );
+
+ painter.setPen( QColor(255,255,255) );
+ painter.setRasterOp( Qt::XorROP );
+
+ painter.drawRect( m_selectedRegion );
+
+ painter.end();
+
+ m_label->setPixmap(pixmap);
+}
+
+
+KPopupMenu *KPixmapRegionSelectorWidget::createPopupMenu()
+{
+ KPopupMenu *popup=new KPopupMenu(this, "PixmapRegionSelectorPopup");
+ popup->insertTitle(i18n("Image Operations"));
+
+ KAction *action = new KAction(i18n("&Rotate Clockwise"), "rotate_cw",
+ 0, this, SLOT(rotateClockwise()),
+ popup, "rotateclockwise");
+ action->plug(popup);
+
+ action = new KAction(i18n("Rotate &Counterclockwise"), "rotate_ccw",
+ 0, this, SLOT(rotateCounterclockwise()),
+ popup, "rotatecounterclockwise");
+ action->plug(popup);
+
+/*
+ I wonder if it would be appropiate to have here an "Open with..." option to
+ edit the image (antlarr)
+*/
+ return popup;
+}
+
+void KPixmapRegionSelectorWidget::rotate(KImageEffect::RotateDirection direction)
+{
+ int w=m_originalPixmap.width();
+ int h=m_originalPixmap.height();
+ QImage img=m_unzoomedPixmap.convertToImage();
+ img= KImageEffect::rotate(img, direction);
+ m_unzoomedPixmap.convertFromImage(img);
+
+ img=m_originalPixmap.convertToImage();
+ img= KImageEffect::rotate(img, direction);
+ m_originalPixmap.convertFromImage(img);
+
+ m_linedPixmap=QPixmap();
+
+ if (m_forcedAspectRatio>0 && m_forcedAspectRatio!=1)
+ resetSelection();
+ else
+ {
+ switch (direction)
+ {
+ case ( KImageEffect::Rotate90 ):
+ {
+ int x=h-m_selectedRegion.y()-m_selectedRegion.height();
+ int y=m_selectedRegion.x();
+ m_selectedRegion.setRect(x, y, m_selectedRegion.height(), m_selectedRegion.width() );
+ updatePixmap();
+ } break;
+ case ( KImageEffect::Rotate270 ):
+ {
+ int x=m_selectedRegion.y();
+ int y=w-m_selectedRegion.x()-m_selectedRegion.width();
+ m_selectedRegion.setRect(x, y, m_selectedRegion.height(), m_selectedRegion.width() );
+ updatePixmap();
+ } break;
+ default: resetSelection();
+ }
+ }
+}
+
+void KPixmapRegionSelectorWidget::rotateClockwise()
+{
+ rotate(KImageEffect::Rotate90);
+}
+
+void KPixmapRegionSelectorWidget::rotateCounterclockwise()
+{
+ rotate(KImageEffect::Rotate270);
+}
+
+bool KPixmapRegionSelectorWidget::eventFilter(QObject *obj, QEvent *ev)
+{
+ if ( ev->type() == QEvent::MouseButtonPress )
+ {
+ QMouseEvent *mev= (QMouseEvent *)(ev);
+ //kdDebug() << QString("click at %1,%2").arg( mev->x() ).arg( mev->y() ) << endl;
+
+ if ( mev->button() == RightButton )
+ {
+ KPopupMenu *popup = createPopupMenu( );
+ popup->exec( mev->globalPos() );
+ delete popup;
+ return TRUE;
+ };
+
+ QCursor cursor;
+
+ if ( m_selectedRegion.contains( mev->pos() )
+ && m_selectedRegion!=m_originalPixmap.rect() )
+ {
+ m_state=Moving;
+ cursor.setShape( Qt::SizeAllCursor );
+ }
+ else
+ {
+ m_state=Resizing;
+ cursor.setShape( Qt::CrossCursor );
+ }
+ QApplication::setOverrideCursor(cursor);
+
+ m_tempFirstClick=mev->pos();
+
+
+ return TRUE;
+ }
+
+ if ( ev->type() == QEvent::MouseMove )
+ {
+ QMouseEvent *mev= (QMouseEvent *)(ev);
+
+ //kdDebug() << QString("move to %1,%2").arg( mev->x() ).arg( mev->y() ) << endl;
+
+ if ( m_state == Resizing )
+ {
+ setSelectedRegion (
+ calcSelectionRectangle( m_tempFirstClick, mev->pos() ) );
+ }
+ else if (m_state == Moving )
+ {
+ int mevx = mev->x();
+ int mevy = mev->y();
+ bool mouseOutside=false;
+ if ( mevx < 0 )
+ {
+ m_selectedRegion.moveBy(-m_selectedRegion.x(),0);
+ mouseOutside=true;
+ }
+ else if ( mevx > m_originalPixmap.width() )
+ {
+ m_selectedRegion.moveBy(m_originalPixmap.width()-m_selectedRegion.width()-m_selectedRegion.x(),0);
+ mouseOutside=true;
+ }
+ if ( mevy < 0 )
+ {
+ m_selectedRegion.moveBy(0,-m_selectedRegion.y());
+ mouseOutside=true;
+ }
+ else if ( mevy > m_originalPixmap.height() )
+ {
+ m_selectedRegion.moveBy(0,m_originalPixmap.height()-m_selectedRegion.height()-m_selectedRegion.y());
+ mouseOutside=true;
+ }
+ if (mouseOutside) { updatePixmap(); return TRUE; };
+
+ m_selectedRegion.moveBy( mev->x()-m_tempFirstClick.x(),
+ mev->y()-m_tempFirstClick.y() );
+
+ // Check that the region has not fallen outside the image
+ if (m_selectedRegion.x() < 0)
+ m_selectedRegion.moveBy(-m_selectedRegion.x(),0);
+ else if (m_selectedRegion.right() > m_originalPixmap.width())
+ m_selectedRegion.moveBy(-(m_selectedRegion.right()-m_originalPixmap.width()),0);
+
+ if (m_selectedRegion.y() < 0)
+ m_selectedRegion.moveBy(0,-m_selectedRegion.y());
+ else if (m_selectedRegion.bottom() > m_originalPixmap.height())
+ m_selectedRegion.moveBy(0,-(m_selectedRegion.bottom()-m_originalPixmap.height()));
+
+ m_tempFirstClick=mev->pos();
+ updatePixmap();
+ }
+ return TRUE;
+ }
+
+ if ( ev->type() == QEvent::MouseButtonRelease )
+ {
+ QMouseEvent *mev= (QMouseEvent *)(ev);
+
+ if ( m_state == Resizing && mev->pos() == m_tempFirstClick)
+ resetSelection();
+
+ m_state=None;
+ QApplication::restoreOverrideCursor();
+
+ return TRUE;
+ }
+
+ QWidget::eventFilter(obj, ev);
+ return FALSE;
+}
+
+QRect KPixmapRegionSelectorWidget::calcSelectionRectangle( const QPoint & startPoint, const QPoint & _endPoint )
+{
+ QPoint endPoint = _endPoint;
+ if ( endPoint.x() < 0 ) endPoint.setX(0);
+ else if ( endPoint.x() > m_originalPixmap.width() ) endPoint.setX(m_originalPixmap.width());
+ if ( endPoint.y() < 0 ) endPoint.setY(0);
+ else if ( endPoint.y() > m_originalPixmap.height() ) endPoint.setY(m_originalPixmap.height());
+ int w=abs(startPoint.x()-endPoint.x());
+ int h=abs(startPoint.y()-endPoint.y());
+
+ if (m_forcedAspectRatio>0)
+ {
+ double aspectRatio=w/double(h);
+
+ if (aspectRatio>m_forcedAspectRatio)
+ h=(int)(w/m_forcedAspectRatio);
+ else
+ w=(int)(h*m_forcedAspectRatio);
+ }
+
+ int x,y;
+ if ( startPoint.x() < endPoint.x() )
+ x=startPoint.x();
+ else
+ x=startPoint.x()-w;
+ if ( startPoint.y() < endPoint.y() )
+ y=startPoint.y();
+ else
+ y=startPoint.y()-h;
+
+ if (x<0)
+ {
+ w+=x;
+ x=0;
+ h=(int)(w/m_forcedAspectRatio);
+
+ if ( startPoint.y() > endPoint.y() )
+ y=startPoint.y()-h;
+ }
+ else if (x+w>m_originalPixmap.width())
+ {
+ w=m_originalPixmap.width()-x;
+ h=(int)(w/m_forcedAspectRatio);
+
+ if ( startPoint.y() > endPoint.y() )
+ y=startPoint.y()-h;
+ }
+ if (y<0)
+ {
+ h+=y;
+ y=0;
+ w=(int)(h*m_forcedAspectRatio);
+
+ if ( startPoint.x() > endPoint.x() )
+ x=startPoint.x()-w;
+ }
+ else if (y+h>m_originalPixmap.height())
+ {
+ h=m_originalPixmap.height()-y;
+ w=(int)(h*m_forcedAspectRatio);
+
+ if ( startPoint.x() > endPoint.x() )
+ x=startPoint.x()-w;
+ }
+
+ return QRect(x,y,w,h);
+}
+
+QRect KPixmapRegionSelectorWidget::unzoomedSelectedRegion() const
+{
+ return QRect((int)(m_selectedRegion.x()/m_zoomFactor),
+ (int)(m_selectedRegion.y()/m_zoomFactor),
+ (int)(m_selectedRegion.width()/m_zoomFactor),
+ (int)(m_selectedRegion.height()/m_zoomFactor));
+}
+
+QImage KPixmapRegionSelectorWidget::selectedImage() const
+{
+ QImage origImage=m_unzoomedPixmap.convertToImage();
+ return origImage.copy(unzoomedSelectedRegion());
+}
+
+void KPixmapRegionSelectorWidget::setSelectionAspectRatio(int width, int height)
+{
+ m_forcedAspectRatio=width/double(height);
+}
+
+void KPixmapRegionSelectorWidget::setFreeSelectionAspectRatio()
+{
+ m_forcedAspectRatio=0;
+}
+
+void KPixmapRegionSelectorWidget::setMaximumWidgetSize(int width, int height)
+{
+ m_maxWidth=width;
+ m_maxHeight=height;
+
+ m_originalPixmap=m_unzoomedPixmap;
+ if (m_selectedRegion == m_originalPixmap.rect()) m_selectedRegion=QRect();
+
+// kdDebug() << QString(" original Pixmap :") << m_originalPixmap.rect() << endl;
+// kdDebug() << QString(" unzoomed Pixmap : %1 x %2 ").arg(m_unzoomedPixmap.width()).arg(m_unzoomedPixmap.height()) << endl;
+
+ if ( !m_originalPixmap.isNull() &&
+ ( m_originalPixmap.width() > m_maxWidth ||
+ m_originalPixmap.height() > m_maxHeight ) )
+ {
+ /* We have to resize the pixmap to get it complete on the screen */
+ QImage image=m_originalPixmap.convertToImage();
+ m_originalPixmap.convertFromImage( image.smoothScale( width, height, QImage::ScaleMin ) );
+ double oldZoomFactor = m_zoomFactor;
+ m_zoomFactor=m_originalPixmap.width()/(double)m_unzoomedPixmap.width();
+
+ if (m_selectedRegion.isValid())
+ {
+ m_selectedRegion=
+ QRect((int)(m_selectedRegion.x()*m_zoomFactor/oldZoomFactor),
+ (int)(m_selectedRegion.y()*m_zoomFactor/oldZoomFactor),
+ (int)(m_selectedRegion.width()*m_zoomFactor/oldZoomFactor),
+ (int)(m_selectedRegion.height()*m_zoomFactor/oldZoomFactor) );
+ }
+ }
+
+ if (!m_selectedRegion.isValid()) m_selectedRegion = m_originalPixmap.rect();
+
+ m_linedPixmap=QPixmap();
+ updatePixmap();
+ resize(m_label->width(), m_label->height());
+}
+
+#include "kpixmapregionselectorwidget.moc"
diff --git a/kopete/libkopete/compat/kpixmapregionselectorwidget.h b/kopete/libkopete/compat/kpixmapregionselectorwidget.h
new file mode 100644
index 00000000..a4a9cfcf
--- /dev/null
+++ b/kopete/libkopete/compat/kpixmapregionselectorwidget.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Antonio Larrosa <larrosa@kde.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KPIXMAPREGIONSELECTORWIDGET_H__
+#define __KPIXMAPREGIONSELECTORWIDGET_H__
+#include <qvbox.h>
+#include <qpixmap.h>
+#include <qrect.h>
+#include <qlabel.h>
+#include <kimageeffect.h>
+
+class KPopupMenu;
+
+#include "kopete_export.h"
+
+/**
+ * KPixmapRegionSelectorWidget is a widget that shows a picture and provides the
+ * user with a friendly way to select a rectangular subregion of the pixmap.
+ *
+ * NOTE: There are two copies of this .h and the .cpp file, with subtle differences.
+ * One copy is in kdelibs/kdeui, and the other copy is in kdepim/libkdepim
+ * This is because kdepim has to remain backwards compatible. Any changes
+ * to either file should be made to the other.
+ *
+ * @author Antonio Larrosa <larrosa@kde.org>
+ * @since 3.4
+ */
+class KOPETE_EXPORT KPixmapRegionSelectorWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor for a KPixmapRegionSelectorWidget.
+ */
+ KPixmapRegionSelectorWidget( QWidget *parent = 0L, const char *name=0L);
+
+ /**
+ * Destructor for a KPixmapRegionSelectorWidget
+ */
+ ~KPixmapRegionSelectorWidget();
+
+ /**
+ * Sets the pixmap which will be shown for the user to select a region from.
+ * @param pixmap The pixmap. Must be non-null.
+ *
+ */
+ void setPixmap( const QPixmap &pixmap );
+
+ /**
+ * @return the original whole pixmap that we're using in this widget as the
+ * pixmap the user is selecting a region from.
+ */
+ QPixmap pixmap() const { return m_unzoomedPixmap; };
+
+ /**
+ * Sets the selected region to be @p rect (in zoomed pixmap coordinates)
+ */
+ void setSelectedRegion(const QRect &rect);
+
+ /**
+ * Returns the selected region ( in zoomed pixmap coordinates )
+ */
+ QRect selectedRegion() const;
+
+ /**
+ * Returns the selected region ( in unzoomed, original pixmap coordinates )
+ */
+ QRect unzoomedSelectedRegion() const;
+
+ /**
+ * Resets the selection to use the whole image
+ */
+ void resetSelection();
+
+ /**
+ * @returns a QImage object with just the region the user selected from the
+ * image
+ */
+ QImage selectedImage() const;
+
+ /**
+ * Sets the aspect ration that the selected subimage should have. The way to
+ * select it, is specifying an example valid @p width and @p height.
+ * @see setFreeSelectionAspectRatio()
+ */
+ void setSelectionAspectRatio(int width, int height);
+
+ /**
+ * Allows the user to do a selection which has any aspect ratio. This is
+ * the default.
+ * @see setSelectionAspectRatio()
+ */
+ void setFreeSelectionAspectRatio();
+
+ /**
+ * Sets the maximum size for the widget. If the image is larger than this
+ * (either horizontally or vertically), it's scaled to adjust to the maximum
+ * size (preserving the aspect ratio)
+ */
+ void setMaximumWidgetSize( int width, int height );
+
+ /**
+ * Rotates the image as specified by the @p direction parameter, also tries
+ * to rotate the selected region so that it doesn't change, as long as the
+ * forced aspect ratio setting is respected, in other case, the selected region
+ * is resetted.
+ */
+ void rotate(KImageEffect::RotateDirection direction);
+
+public slots:
+ /**
+ * Rotates the current image 90º clockwise
+ */
+ void rotateClockwise();
+ /**
+ * Rotates the current image 90º counterclockwise
+ */
+ void rotateCounterclockwise();
+
+protected:
+ /**
+ * Creates a KPopupMenu with the menu that appears when clicking with the right button on the label
+ */
+ virtual KPopupMenu *createPopupMenu();
+
+private:
+ bool eventFilter(QObject *obj, QEvent *ev);
+
+ /**
+ * Recalculates the pixmap that is shown based on the current selected area,
+ * the original image, etc.
+ */
+ void updatePixmap();
+
+ QRect calcSelectionRectangle( const QPoint &startPoint, const QPoint & endPoint );
+
+ enum CursorState { None=0, Resizing, Moving };
+ CursorState m_state;
+
+ QPixmap m_unzoomedPixmap;
+ QPixmap m_originalPixmap;
+ QPixmap m_linedPixmap;
+ QRect m_selectedRegion;
+ QLabel *m_label;
+
+ QPoint m_tempFirstClick;
+ double m_forcedAspectRatio;
+
+ int m_maxWidth, m_maxHeight;
+ double m_zoomFactor;
+};
+
+#endif
+
diff --git a/kopete/libkopete/configure.in.in b/kopete/libkopete/configure.in.in
new file mode 100644
index 00000000..f9d1fb76
--- /dev/null
+++ b/kopete/libkopete/configure.in.in
@@ -0,0 +1,32 @@
+# -- Check for XScreenSaver -----------------------------------------
+AC_CHECK_HEADERS(tgmath.h)xss_save_ldflags="$LDFLAGS"
+LDFLAGS="$X_LDFLAGS"
+
+LIB_XSS=
+
+KDE_CHECK_HEADER(X11/extensions/scrnsaver.h,
+ [
+ AC_CHECK_LIB(Xext,XScreenSaverQueryInfo,
+ [
+ AC_DEFINE(HAVE_XSCREENSAVER, 1, [Define if you have the XScreenSaver extension])
+ LIB_XSS="-lXext"
+ ],
+ [
+ ld_shared_flag=
+ KDE_CHECK_COMPILER_FLAG(shared, [ld_shared_flag="-shared"])
+ AC_CHECK_LIB(Xss,XScreenSaverQueryInfo,
+ [
+ AC_DEFINE(HAVE_XSCREENSAVER, 1, [Define if you have the XScreenSaver extension])
+ LIB_XSS="-lXss"
+ ],
+ [],
+ [ $ld_shared_flag $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS ])
+ ],
+ [ $X_PRE_LIBS -lX11 $X_EXTRA_LIBS ])
+ ], [],
+ [
+ #include <X11/Xlib.h>
+ ] )
+
+AC_SUBST(LIB_XSS)
+LDFLAGS="$xss_save_ldflags"
diff --git a/kopete/libkopete/connectionmanager.cpp b/kopete/libkopete/connectionmanager.cpp
new file mode 100644
index 00000000..b2dd7825
--- /dev/null
+++ b/kopete/libkopete/connectionmanager.cpp
@@ -0,0 +1,153 @@
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstaticdeleter.h>
+
+#include "clientiface_stub.h"
+#include "networkstatuscommon.h"
+
+#include "connectionmanager.h"
+
+// ConnectionManager's private parts
+class ConnectionManagerPrivate
+{
+ public:
+ // this holds the currently active state
+ ConnectionManager::State m_state;
+ ClientIface_stub * m_stub;
+ bool m_userInitiatedOnly;
+};
+
+// Connection manager itself
+ConnectionManager::ConnectionManager( QObject * parent, const char * name ) : DCOPObject( "ConnectionManager" ),QObject( parent, name )
+{
+ d = new ConnectionManagerPrivate;
+
+ d->m_stub = new ClientIface_stub( kapp->dcopClient(), "kded", "networkstatus" );
+
+ connectDCOPSignal( "kded", "networkstatus", "statusChange(QString,int)", "slotStatusChanged(QString,int)", false );
+ d->m_userInitiatedOnly = false;
+ initialise();
+}
+
+ConnectionManager *ConnectionManager::s_self = 0L;
+
+ConnectionManager *ConnectionManager::self()
+{
+ static KStaticDeleter<ConnectionManager> deleter;
+ if(!s_self)
+ deleter.setObject( s_self, new ConnectionManager( 0, "connection_manager" ) );
+ return s_self;
+}
+
+void ConnectionManager::initialise()
+{
+ // determine initial state and set the state object accordingly.
+ d->m_state = Inactive;
+ updateStatus();
+}
+
+void ConnectionManager::updateStatus()
+{
+ NetworkStatus::EnumStatus daemonStatus = (NetworkStatus::EnumStatus)d->m_stub->status( QString::null );
+ kdDebug() << k_funcinfo << endl;
+ switch ( daemonStatus )
+ {
+ case NetworkStatus::Offline:
+ case NetworkStatus::OfflineFailed:
+ case NetworkStatus::OfflineDisconnected:
+ case NetworkStatus::ShuttingDown:
+ if ( d->m_state == Online )
+ {
+ kdDebug() << "STATE IS PENDING" << endl;
+ d->m_state = Pending;
+ }
+ else
+ {
+ kdDebug() << "STATE IS OFFLINE" << endl;
+ d->m_state = Offline;
+ }
+ break;
+ case NetworkStatus::Establishing:
+ case NetworkStatus::Online:
+ kdDebug() << "STATE IS ONLINE" << endl;
+ d->m_state = Online;
+ break;
+ case NetworkStatus::NoNetworks:
+ case NetworkStatus::Unreachable:
+ kdDebug() << "STATE IS INACTIVE" << endl;
+ d->m_state = Inactive;
+ break;
+ }
+}
+
+ConnectionManager::~ConnectionManager()
+{
+ delete d;
+}
+
+NetworkStatus::EnumStatus ConnectionManager::status( const QString & host )
+{
+ // need also to check that the daemon hasn't died
+ updateStatus();
+ if ( d->m_state == Pending )
+ return NetworkStatus::Offline;
+ if ( d->m_state == Online )
+ return NetworkStatus::Online;
+ if ( d->m_state == Offline )
+ return NetworkStatus::Offline;
+ return NetworkStatus::NoNetworks;
+}
+
+NetworkStatus::EnumRequestResult ConnectionManager::requestConnection( QWidget * mainWidget, const QString & host, bool userInitiated )
+{
+ kdDebug() << k_funcinfo << endl;
+ NetworkStatus::EnumRequestResult result;
+ // if offline and the user has previously indicated they didn't want any new connections, suppress it
+ if ( d->m_state == Offline && !userInitiated && d->m_userInitiatedOnly )
+ result = NetworkStatus::UserRefused;
+ // if offline, ask the user whether this connection should be allowed
+ if ( d->m_state == Offline )
+ {
+ if ( askToConnect( mainWidget ) )
+ //result = NetworkStatus::Connected;
+ result = (NetworkStatus::EnumRequestResult)d->m_stub->request( host, userInitiated );
+ else
+ result = NetworkStatus::UserRefused;
+ }
+ // otherwise, just ask for the connection
+ else
+ result = (NetworkStatus::EnumRequestResult)d->m_stub->request( host, userInitiated );
+
+ return result;
+}
+
+void ConnectionManager::relinquishConnection( const QString & host )
+{
+ d->m_stub->relinquish( host );
+}
+
+void ConnectionManager::slotStatusChanged( QString host, int status )
+{
+ kdDebug() << k_funcinfo << endl;
+ updateStatus();
+ // reset user initiated only flag if we are now online
+ if ( d->m_state == Online )
+ d->m_userInitiatedOnly = false;
+
+ emit statusChanged( host, (NetworkStatus::EnumStatus)status );
+}
+
+bool ConnectionManager::askToConnect( QWidget * mainWidget )
+{
+ i18n( "A network connection was disconnected. The application is now in offline mode. Do you want the application to resume network operations when the network is available again?" );
+ i18n( "This application is currently in offline mode. Do you want to connect?" );
+ return ( KMessageBox::questionYesNo( mainWidget,
+ i18n("This application is currently in offline mode. Do you want to connect in order to carry out this operation?"),
+ i18n("Leave Offline Mode?"),
+ i18n("Connect"), i18n("Stay Offline"),
+ QString::fromLatin1("OfflineModeAlwaysGoOnline") ) == KMessageBox::Yes );
+}
+
+#include "connectionmanager.moc"
diff --git a/kopete/libkopete/connectionmanager.h b/kopete/libkopete/connectionmanager.h
new file mode 100644
index 00000000..b78df8d4
--- /dev/null
+++ b/kopete/libkopete/connectionmanager.h
@@ -0,0 +1,58 @@
+/*
+ connectionmanager.h - Provides the client side interface to the kde networkstatus daemon
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ *************************************************************************
+ * *
+ * 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 KDE_CONNECTION_MANAGER_H
+#define KDE_CONNECTION_MANAGER_H
+
+#include <dcopobject.h>
+
+#include "kopete_export.h"
+#include "networkstatuscommon.h"
+
+class ConnectionManagerPrivate;
+
+class KOPETE_EXPORT ConnectionManager : public QObject, virtual public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+ public:
+ static ConnectionManager* self();
+ enum State { Inactive, Online, Offline, Pending };
+ virtual ~ConnectionManager();
+ NetworkStatus::EnumStatus status( const QString & host );
+ // check if a hostname is available. Ask user if offline. Request host
+ NetworkStatus::EnumRequestResult requestConnection( QWidget* mainWidget, const QString & host, bool userInitiated );
+ // method to relinquish a connection
+ void relinquishConnection( const QString & host );
+ signals:
+ // signal that the network for a hostname is up/down
+ void statusChanged( const QString & host, NetworkStatus::EnumStatus status );
+ protected:
+ // sets up internal state
+ void initialise();
+ // reread the desktop status from the daemon and update internal state
+ void updateStatus();
+ // ask if the user would like to reconnect
+ bool askToConnect( QWidget * mainWidget );
+ k_dcop:
+ void slotStatusChanged( QString host, int status );
+ private:
+ ConnectionManager( QObject *parent, const char * name );
+ ConnectionManagerPrivate *d;
+ static ConnectionManager * s_self;
+};
+
+#endif
+
diff --git a/kopete/libkopete/kabcpersistence.cpp b/kopete/libkopete/kabcpersistence.cpp
new file mode 100644
index 00000000..527a99a4
--- /dev/null
+++ b/kopete/libkopete/kabcpersistence.cpp
@@ -0,0 +1,452 @@
+/*
+ addressbooklink.cpp - Manages operations involving the KDE Address Book
+
+ Copyright (c) 2005 Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstring.h>
+#include <qtimer.h>
+
+#include <kabc/addressbook.h>
+#include <kabc/addressee.h>
+#include <kabc/resource.h>
+#include <kabc/stdaddressbook.h>
+
+// UI related includes used for importing from KABC
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include "accountselector.h"
+#include "kopeteuiglobal.h"
+
+#include <kstaticdeleter.h>
+
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprotocol.h"
+
+#include "kabcpersistence.h"
+
+namespace Kopete
+{
+
+/**
+ * utility function to merge two QStrings containing individual elements separated by 0xE000
+ */
+static QString unionContents( QString arg1, QString arg2 )
+{
+ QChar separator( 0xE000 );
+ QStringList outList = QStringList::split( separator, arg1 );
+ QStringList arg2List = QStringList::split( separator, arg2 );
+ for ( QStringList::iterator it = arg2List.begin(); it != arg2List.end(); ++it )
+ if ( !outList.contains( *it ) )
+ outList.append( *it );
+ QString out = outList.join( separator );
+ return out;
+}
+
+KABCPersistence::KABCPersistence( QObject * parent, const char * name ) : QObject( parent, name )
+{
+ s_pendingResources.setAutoDelete( false );
+}
+
+KABCPersistence::~KABCPersistence()
+{
+}
+
+KABCPersistence *KABCPersistence::s_self = 0L;
+
+bool KABCPersistence::s_addrBookWritePending = false;
+
+QPtrList<KABC::Resource> KABCPersistence::s_pendingResources;
+
+KABC::AddressBook* KABCPersistence::s_addressBook = 0;
+
+KABCPersistence *KABCPersistence::self()
+{
+ static KStaticDeleter<KABCPersistence> deleter;
+ if(!s_self)
+ deleter.setObject( s_self, new KABCPersistence() );
+ return s_self;
+}
+
+KABC::AddressBook* KABCPersistence::addressBook()
+{
+ if ( s_addressBook == 0L )
+ {
+ s_addressBook = KABC::StdAddressBook::self();
+ KABC::StdAddressBook::setAutomaticSave( false );
+ }
+ return s_addressBook;
+}
+
+void KABCPersistence::write( MetaContact * mc )
+{
+ // Save any changes in each contact's addressBookFields to KABC
+ KABC::AddressBook* ab = addressBook();
+
+ kdDebug( 14010 ) << k_funcinfo << "looking up Addressee for " << mc->displayName() << "..." << endl;
+ // Look up the address book entry
+ KABC::Addressee theAddressee = ab->findByUid( mc->metaContactId() );
+ // Check that if addressee is not deleted or if the link is spurious
+ // (inherited from Kopete < 0.8, where all metacontacts had random ids)
+ if ( theAddressee.isEmpty() )
+ {
+ // not found in currently enabled addressbooks - may be in a disabled resource...
+ return;
+ }
+ else
+ {
+ // collate the instant messaging data to be inserted into the address book
+ QMap<QString, QStringList> addressMap;
+ QPtrList<Contact> contacts = mc->contacts();
+ QPtrListIterator<Contact> cIt( contacts );
+ while ( Contact * c = cIt.current() )
+ {
+ QStringList addresses = addressMap[ c->protocol()->addressBookIndexField() ];
+ addresses.append( c->contactId() );
+ addressMap.insert( c->protocol()->addressBookIndexField(), addresses );
+ ++cIt;
+ }
+
+ // insert a custom field for each protocol
+ QMap<QString, QStringList>::ConstIterator it = addressMap.begin();
+ for ( ; it != addressMap.end(); ++it )
+ {
+ // read existing data for this key
+ QString currentCustomForProtocol = theAddressee.custom( it.key(), QString::fromLatin1( "All" ) );
+ // merge without duplicating
+ QString toWrite = unionContents( currentCustomForProtocol, it.data().join( QChar( 0xE000 ) ) );
+ // Note if nothing ends up in the KABC data, this is because insertCustom does nothing if any param is empty.
+ kdDebug( 14010 ) << k_funcinfo << "Writing: " << it.key() << ", " << "All" << ", " << toWrite << endl;
+ theAddressee.insertCustom( it.key(), QString::fromLatin1( "All" ), toWrite );
+ QString check = theAddressee.custom( it.key(), QString::fromLatin1( "All" ) );
+ }
+ ab->insertAddressee( theAddressee );
+ //kdDebug( 14010 ) << k_funcinfo << "dumping addressbook before write " << endl;
+ //dumpAB();
+ writeAddressBook( theAddressee.resource() );
+ //theAddressee.dump();
+ }
+
+/* // Wipe out the existing addressBook entries
+ d->addressBook.clear();
+ // This causes each Kopete::Protocol subclass to serialise its contacts' data into the metacontact's plugin data and address book data
+ emit aboutToSave(this);
+
+ kdDebug( 14010 ) << k_funcinfo << "...FOUND ONE!" << endl;
+ // Store address book fields
+ QMap<QString, QMap<QString, QString> >::ConstIterator appIt = d->addressBook.begin();
+ for( ; appIt != d->addressBook.end(); ++appIt )
+ {
+ QMap<QString, QString>::ConstIterator addrIt = appIt.data().begin();
+ for( ; addrIt != appIt.data().end(); ++addrIt )
+ {
+ // read existing data for this key
+ QString currentCustom = theAddressee.custom( appIt.key(), addrIt.key() );
+ // merge without duplicating
+ QString toWrite = unionContents( currentCustom, addrIt.data() );
+ // write the result
+ // Note if nothing ends up in the KABC data, this is because insertCustom does nothing if any param is empty.
+ kdDebug( 14010 ) << k_funcinfo << "Writing: " << appIt.key() << ", " << addrIt.key() << ", " << toWrite << endl;
+ theAddressee.insertCustom( appIt.key(), addrIt.key(), toWrite );
+ }
+ }
+ ab->insertAddressee( theAddressee );
+ writeAddressBook();
+ }*/
+}
+
+void KABCPersistence::writeAddressBook( const KABC::Resource * res)
+{
+ if ( !s_pendingResources.containsRef( res ) )
+ s_pendingResources.append( res );
+ if ( !s_addrBookWritePending )
+ {
+ s_addrBookWritePending = true;
+ QTimer::singleShot( 2000, this, SLOT( slotWriteAddressBook() ) );
+ }
+}
+
+void KABCPersistence::slotWriteAddressBook()
+{
+ //kdDebug( 14010 ) << k_funcinfo << endl;
+ KABC::AddressBook* ab = addressBook();
+ QPtrListIterator<KABC::Resource> it( s_pendingResources );
+ for ( ; it.current(); ++it )
+ {
+ //kdDebug( 14010 ) << "Writing resource " << it.current()->resourceName() << endl;
+ KABC::Ticket *ticket = ab->requestSaveTicket( it.current() );
+ if ( !ticket )
+ kdWarning( 14010 ) << "WARNING: Resource is locked by other application!" << endl;
+ else
+ {
+ if ( !ab->save( ticket ) )
+ {
+ kdWarning( 14010 ) << "ERROR: Saving failed!" << endl;
+ ab->releaseSaveTicket( ticket );
+ }
+ }
+ //kdDebug( 14010 ) << "Finished writing KABC" << endl;
+ }
+ s_pendingResources.clear();
+ s_addrBookWritePending = false;
+}
+
+void KABCPersistence::removeKABC( MetaContact *)
+{
+/* // remove any data this KMC has written to the KDE address book
+ // Save any changes in each contact's addressBookFields to KABC
+ KABC::AddressBook* ab = addressBook();
+
+ // Wipe out the existing addressBook entries
+ d->addressBook.clear();
+ // This causes each Kopete::Protocol subclass to serialise its contacts' data into the metacontact's plugin data and address book data
+ emit aboutToSave(this);
+
+ // If the metacontact is linked to a kabc entry
+ if ( !d->metaContactId.isEmpty() )
+ {
+ //kdDebug( 14010 ) << k_funcinfo << "looking up Addressee for " << displayName() << "..." << endl;
+ // Look up the address book entry
+ KABC::Addressee theAddressee = ab->findByUid( metaContactId() );
+
+ if ( theAddressee.isEmpty() )
+ {
+ // remove the link
+ //kdDebug( 14010 ) << k_funcinfo << "...not found." << endl;
+ d->metaContactId=QString::null;
+ }
+ else
+ {
+ //kdDebug( 14010 ) << k_funcinfo << "...FOUND ONE!" << endl;
+ // Remove address book fields
+ QMap<QString, QMap<QString, QString> >::ConstIterator appIt = d->addressBook.begin();
+ for( ; appIt != d->addressBook.end(); ++appIt )
+ {
+ QMap<QString, QString>::ConstIterator addrIt = appIt.data().begin();
+ for( ; addrIt != appIt.data().end(); ++addrIt )
+ {
+ // FIXME: This assumes Kopete is the only app writing these fields
+ kdDebug( 14010 ) << k_funcinfo << "Removing: " << appIt.key() << ", " << addrIt.key() << endl;
+ theAddressee.removeCustom( appIt.key(), addrIt.key() );
+ }
+ }
+ ab->insertAddressee( theAddressee );
+
+ writeAddressBook();
+ }
+ }
+// kdDebug(14010) << k_funcinfo << kdBacktrace() <<endl;*/
+}
+
+bool KABCPersistence::syncWithKABC( MetaContact * mc )
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ bool contactAdded = false;
+ // check whether the dontShowAgain was checked
+ KABC::AddressBook* ab = addressBook();
+ KABC::Addressee addr = ab->findByUid( mc->metaContactId() );
+
+ if ( !addr.isEmpty() ) // if we are associated with KABC
+ {
+// load the set of addresses from KABC
+ QStringList customs = addr.customs();
+
+ QStringList::ConstIterator it;
+ for ( it = customs.begin(); it != customs.end(); ++it )
+ {
+ QString app, name, value;
+ splitField( *it, app, name, value );
+ kdDebug( 14010 ) << "app=" << app << " name=" << name << " value=" << value << endl;
+
+ if ( app.startsWith( QString::fromLatin1( "messaging/" ) ) )
+ {
+ if ( name == QString::fromLatin1( "All" ) )
+ {
+ kdDebug( 14010 ) << " syncing \"" << app << ":" << name << " with contactlist " << endl;
+ // Get the protocol name from the custom field
+ // by chopping the 'messaging/' prefix from the custom field app name
+ QString protocolName = app.right( app.length() - 10 );
+ // munge Jabber hack
+ if ( protocolName == QString::fromLatin1( "xmpp" ) )
+ protocolName = QString::fromLatin1( "jabber" );
+
+ // Check Kopete supports it
+ Protocol * proto = dynamic_cast<Protocol*>( PluginManager::self()->loadPlugin( QString::fromLatin1( "kopete_" ) + protocolName ) );
+ if ( !proto )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>\"%1\" is not supported by Kopete.</qt>" ).arg( protocolName ),
+ i18n( "Could Not Sync with KDE Address Book" ) );
+ continue;
+ }
+
+ // See if we need to add each contact in this protocol
+ QStringList addresses = QStringList::split( QChar( 0xE000 ), value );
+ QStringList::iterator end = addresses.end();
+ for ( QStringList::iterator it = addresses.begin(); it != end; ++it )
+ {
+ // check whether each one is present in Kopete
+ // Is it in the contact list?
+ // First discard anything after an 0xE120, this is used by IRC to separate nick and server group name, but
+ // IRC doesn't support this properly yet, so the user will have to select an appropriate account manually
+ int separatorPos = (*it).find( QChar( 0xE120 ) );
+ if ( separatorPos != -1 )
+ *it = (*it).left( separatorPos );
+
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( proto );
+ QDictIterator<Kopete::Account> acs(accounts);
+ Kopete::MetaContact *otherMc = 0;
+ for ( acs.toFirst(); acs.current(); ++acs )
+ {
+ Kopete::Contact *c= acs.current()->contacts()[*it];
+ if(c)
+ {
+ otherMc=c->metaContact();
+ break;
+ }
+ }
+
+ if ( otherMc ) // Is it in another metacontact?
+ {
+ // Is it already in this metacontact? If so, we needn't do anything
+ if ( otherMc == mc )
+ {
+ kdDebug( 14010 ) << *it << " already a child of this metacontact." << endl;
+ continue;
+ }
+ kdDebug( 14010 ) << *it << " already exists in OTHER metacontact, move here?" << endl;
+ // find the Kopete::Contact and attempt to move it to this metacontact.
+ otherMc->findContact( proto->pluginId(), QString::null, *it )->setMetaContact( mc );
+ }
+ else
+ {
+ // if not, prompt to add it
+ kdDebug( 14010 ) << proto->pluginId() << "://" << *it << " was not found in the contact list. Prompting to add..." << endl;
+ if ( KMessageBox::Yes == KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>An address was added to this contact by another application.<br>Would you like to use it in Kopete?<br><b>Protocol:</b> %1<br><b>Address:</b> %2</qt>" ).arg( proto->displayName() ).arg( *it ), i18n( "Import Address From Address Book" ), i18n("Use"), i18n("Do Not Use"), QString::fromLatin1( "ImportFromKABC" ) ) )
+ {
+ // Check the accounts for this protocol are all connected
+ // Most protocols do not allow you to add contacts while offline
+ // Would be better to have a virtual bool Kopete::Account::readyToAddContact()
+ bool allAccountsConnected = true;
+ for ( acs.toFirst(); acs.current(); ++acs )
+ if ( !acs.current()->isConnected() )
+ { allAccountsConnected = false;
+ break;
+ }
+ if ( !allAccountsConnected )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>One or more of your accounts using %1 are offline. Most systems have to be connected to add contacts. Please connect these accounts and try again.</qt>" ).arg( protocolName ),
+ i18n( "Not Connected" ) );
+ continue;
+ }
+
+ // we have got a contact to add, our accounts are connected, so add it.
+ // Do we need to choose an account
+ Kopete::Account *chosen = 0;
+ if ( accounts.count() > 1 )
+ { // if we have >1 account in this protocol, prompt for the protocol.
+ KDialogBase *chooser = new KDialogBase(0, "chooser", true,
+ i18n("Choose Account"), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, false);
+ AccountSelector *accSelector = new AccountSelector(proto, chooser,
+ "accSelector");
+ chooser->setMainWidget(accSelector);
+ if ( chooser->exec() == QDialog::Rejected )
+ continue;
+ chosen = accSelector->selectedItem();
+
+ delete chooser;
+ }
+ else if ( accounts.isEmpty() )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>You do not have an account configured for <b>%1</b> yet. Please create an account, connect it, and try again.</qt>" ).arg( protocolName ),
+ i18n( "No Account Found" ) );
+ continue;
+ }
+ else // if we have 1 account in this protocol, choose it
+ {
+ chosen = acs.toFirst();
+ }
+
+ // add the contact to the chosen account
+ if ( chosen )
+ {
+ kdDebug( 14010 ) << "Adding " << *it << " to " << chosen->accountId() << endl;
+ if ( chosen->addContact( *it, mc ) )
+ contactAdded = true;
+ else
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>It was not possible to add the contact.</qt>" ),
+ i18n( "Could Not Add Contact") ) ;
+ }
+ }
+ else
+ kdDebug( 14010 ) << " user declined to add " << *it << " to contactlist " << endl;
+ }
+ }
+ kdDebug( 14010 ) << " all " << addresses.count() << " contacts in " << proto->pluginId() << " checked " << endl;
+ }
+ else
+ kdDebug( 14010 ) << "not interested in name=" << name << endl;
+
+ }
+ else
+ kdDebug( 14010 ) << "not interested in app=" << app << endl;
+ }
+ }
+ return contactAdded;
+ return false;
+}
+
+// FIXME: Remove when IM address API is in KABC (KDE 4)
+void KABCPersistence::splitField( const QString &str, QString &app, QString &name, QString &value )
+{
+ int colon = str.find( ':' );
+ if ( colon != -1 ) {
+ QString tmp = str.left( colon );
+ value = str.mid( colon + 1 );
+
+ int dash = tmp.find( '-' );
+ if ( dash != -1 ) {
+ app = tmp.left( dash );
+ name = tmp.mid( dash + 1 );
+ }
+ }
+}
+
+void KABCPersistence::dumpAB()
+{
+ KABC::AddressBook * ab = addressBook();
+ kdDebug( 14010 ) << k_funcinfo << " DUMPING ADDRESSBOOK" << endl;
+ KABC::AddressBook::ConstIterator dumpit = ab->begin();
+ for ( ; dumpit != ab->end(); ++dumpit )
+ {
+ (*dumpit).dump();
+ }
+}
+
+
+} // end namespace Kopete
+
+ // dump addressbook contents
+
+#include "kabcpersistence.moc"
diff --git a/kopete/libkopete/kabcpersistence.h b/kopete/libkopete/kabcpersistence.h
new file mode 100644
index 00000000..fa02fb64
--- /dev/null
+++ b/kopete/libkopete/kabcpersistence.h
@@ -0,0 +1,107 @@
+/*
+ addressbooklink.h - Manages operations involving the KDE Address Book
+
+ Copyright (c) 2005 Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEADDRESSBOOKLINK_H
+#define KOPETEADDRESSBOOKLINK_H
+
+#include "kopete_export.h"
+
+// Goal is to have all the address book modifying code in one place
+// Currently in
+// *) Add Contact Wizard
+// *) KopeteMetaContact
+// *) KopeteAddrBookExport
+// *) KABC Export Wizard - TODO - think about sequence of events when adding addressees AND writing their IM data. - Extra save should be unnecessary because we are sharing a kabc instance
+// *) Select addressbook entry
+
+namespace KABC
+{
+ class AddressBook;
+ class Resource;
+}
+
+namespace Kopete
+{
+
+ class MetaContact;
+
+class KOPETE_EXPORT KABCPersistence : public QObject
+{
+ Q_OBJECT
+ public:
+ /**
+ * \brief Retrieve the instance of AccountManager.
+ *
+ * The account manager is a singleton class of which only a single
+ * instance will exist. If no manager exists yet this function will
+ * create one for you.
+ *
+ * \return the instance of the AccountManager
+ */
+ static KABCPersistence* self();
+
+ KABCPersistence( QObject * parent = 0, const char * name = 0 );
+ ~KABCPersistence();
+ /**
+ * @brief Access Kopete's KDE address book instance
+ */
+ static KABC::AddressBook* addressBook();
+ /**
+ * @brief Change the KABC data associated with this metacontact
+ *
+ * The KABC exposed data changed, so change it in KABC.
+ * Replaces Kopete::MetaContact::updateKABC()
+ */
+ void write( MetaContact * mc );
+
+ /**
+ * @brief Remove any KABC data for this meta contact
+ */
+ void removeKABC( MetaContact * mc );
+
+ /**
+ * Check for any new addresses added to this contact's KABC entry
+ * and prompt if they should be added in Kopete too.
+ * @return whether any contacts were added from KABC.
+ */
+ bool syncWithKABC( MetaContact * mc );
+
+ /**
+ * Request an address book write, will be delayed to bundle any others happening around the same time
+ */
+ void writeAddressBook( const KABC::Resource * res );
+ protected:
+
+ static void splitField( const QString &str, QString &app, QString &name, QString &value );
+
+ void dumpAB();
+ protected slots:
+ /**
+ * Perform a delayed address book write
+ */
+ void slotWriteAddressBook();
+ private:
+ static KABCPersistence * s_self;
+ static KABC::AddressBook* s_addressBook;
+ static bool s_addrBookWritePending;
+ static QPtrList<KABC::Resource> s_pendingResources;
+};
+
+} // end namespace Kopete
+
+#endif
+
diff --git a/kopete/libkopete/kautoconfig.cpp b/kopete/libkopete/kautoconfig.cpp
new file mode 100644
index 00000000..497b6cd5
--- /dev/null
+++ b/kopete/libkopete/kautoconfig.cpp
@@ -0,0 +1,450 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kautoconfig.h"
+
+#include <kglobal.h>
+#include <qsqlpropertymap.h>
+#include <qobjectlist.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kdeversion.h>
+
+/**
+ * Macro function to warn developers when they are making calls
+ * that can never return anything of value
+ */
+#ifndef NDEBUG
+#include "kdebug.h"
+#define functionCallPreOrderCheck(functionName, returnValue) \
+ if(!d->retrievedSettings){ \
+ kdDebug(180) << "KAutoConfig::"functionName"() was called before " \
+ "KAutoConfig::retrieveSettings(). This should NEVER happen because " \
+ "it will do nothing. Please Fix." << endl; \
+ return returnValue; \
+ }
+
+#define functionCallPostOrderCheck(functionName, returnValue) \
+ if(d->retrievedSettings){ \
+ kdDebug(180) << "KAutoConfig::"functionName"() was called after " \
+ "KAutoConfig::retrieveSettings(). This should NEVER happen because " \
+ "it will do nothing. Please Fix." << endl; \
+ return returnValue; \
+ }
+#else
+#define functionCallPostOrderCheck(functionName, returnValue)
+#define functionCallPreOrderCheck(functionName, returnValue)
+#endif
+
+class KAutoConfig::KAutoConfigPrivate {
+
+public:
+ KAutoConfigPrivate() : changed(false)
+#ifndef NDEBUG
+ , retrievedSettings(false)
+#endif
+ { init(); }
+
+ // Widgets to parse
+ QPtrList<QWidget> widgets;
+ // Name of the group that KConfig should be set to for each widget.
+ QMap<QWidget*, QString> groups;
+
+ // Child widgets of widgets to ignore
+ QPtrList<QWidget> ignore;
+
+ // Reset to false after saveSettings returns true.
+ bool changed;
+
+#ifndef NDEBUG
+ // Many functions require this to be true to be of any value.
+ bool retrievedSettings;
+#endif
+
+ // Known widgets that can be configured
+ QMap<QWidget*, QPtrList<QWidget> > autoWidgets;
+ // Default values for the widgets.
+ QMap<QWidget*, QVariant> defaultValues;
+ // Widgets to not get properties on (QLabel etc)
+ QAsciiDict<int> ignoreTheseWidgets;
+
+ void init(){
+ ignoreTheseWidgets.insert("QLabel", new int(1));
+ ignoreTheseWidgets.insert("QFrame", new int(2));
+ ignoreTheseWidgets.insert("QGroupBox", new int(3));
+ ignoreTheseWidgets.insert("QButtonGroup", new int(4));
+ ignoreTheseWidgets.insert("QWidget", new int(5));
+ ignoreTheseWidgets.setAutoDelete(true);
+
+ static bool defaultKDEPropertyMapInstalled = false;
+ if ( !defaultKDEPropertyMapInstalled && kapp ) {
+ kapp->installKDEPropertyMap();
+ defaultKDEPropertyMapInstalled = true;
+ }
+ }
+};
+
+KAutoConfig::KAutoConfig(KConfig *kconfig, QObject *parent,
+ const char *name) : QObject(parent, name), config(kconfig) {
+ d = new KAutoConfigPrivate();
+}
+
+KAutoConfig::KAutoConfig(QObject *parent, const char *name) :
+ QObject(parent, name), config(KGlobal::config()) {
+ d = new KAutoConfigPrivate();
+}
+
+KAutoConfig::~KAutoConfig(){
+ delete d;
+}
+
+void KAutoConfig::addWidget(QWidget *widget, const QString &group){
+ functionCallPostOrderCheck("addWidget",);
+ d->groups.insert(widget, group);
+ d->widgets.append(widget);
+ QPtrList<QWidget> newAutoConfigWidget;
+ d->autoWidgets.insert(widget, newAutoConfigWidget );
+}
+
+void KAutoConfig::ignoreSubWidget(QWidget *widget){
+ functionCallPostOrderCheck("ignoreSubWidget",);
+ d->ignore.append(widget);
+}
+
+bool KAutoConfig::retrieveSettings(bool trackChanges){
+#ifndef NDEBUG
+ if(d->retrievedSettings){
+ kdDebug(180) << "This should not happen. Function "
+ "KAutoConfig::retrieveSettings() was called more then once, returning "
+ "false. Please fix." << endl;
+ return false;
+ }
+ d->retrievedSettings = true;
+#endif
+
+ if(trackChanges){
+ // QT
+ changedMap.insert(QString::fromLatin1("QButton"), SIGNAL(stateChanged(int)));
+ changedMap.insert(QString::fromLatin1("QCheckBox"), SIGNAL(stateChanged(int)));
+ changedMap.insert(QString::fromLatin1("QPushButton"), SIGNAL(stateChanged(int)));
+ changedMap.insert(QString::fromLatin1("QRadioButton"), SIGNAL(stateChanged(int)));
+ changedMap.insert(QString::fromLatin1("QComboBox"), SIGNAL(activated (int)));
+ //qsqlproperty map doesn't store the text, but the value!
+ //changedMap.insert(QString::fromLatin1("QComboBox"), SIGNAL(textChanged(const QString &)));
+ changedMap.insert(QString::fromLatin1("QDateEdit"), SIGNAL(valueChanged(const QDate &)));
+ changedMap.insert(QString::fromLatin1("QDateTimeEdit"), SIGNAL(valueChanged(const QDateTime &)));
+ changedMap.insert(QString::fromLatin1("QDial"), SIGNAL(valueChanged (int)));
+ changedMap.insert(QString::fromLatin1("QLineEdit"), SIGNAL(textChanged(const QString &)));
+ changedMap.insert(QString::fromLatin1("QSlider"), SIGNAL(valueChanged(int)));
+ changedMap.insert(QString::fromLatin1("QSpinBox"), SIGNAL(valueChanged(int)));
+ changedMap.insert(QString::fromLatin1("QTimeEdit"), SIGNAL(valueChanged(const QTime &)));
+ changedMap.insert(QString::fromLatin1("QTextEdit"), SIGNAL(textChanged()));
+ changedMap.insert(QString::fromLatin1("QTextBrowser"), SIGNAL(sourceChanged(const QString &)));
+ changedMap.insert(QString::fromLatin1("QMultiLineEdit"), SIGNAL(textChanged()));
+ changedMap.insert(QString::fromLatin1("QListBox"), SIGNAL(selectionChanged()));
+ changedMap.insert(QString::fromLatin1("QTabWidget"), SIGNAL(currentChanged(QWidget *)));
+
+ // KDE
+ changedMap.insert( QString::fromLatin1("KComboBox"), SIGNAL(activated (int)));
+ changedMap.insert( QString::fromLatin1("KFontCombo"), SIGNAL(activated (int)));
+ changedMap.insert( QString::fromLatin1("KFontRequester"), SIGNAL(fontSelected(const QFont &)));
+ changedMap.insert( QString::fromLatin1("KFontChooser"), SIGNAL(fontSelected(const QFont &)));
+ changedMap.insert( QString::fromLatin1("KHistoryCombo"), SIGNAL(activated (int)));
+
+ changedMap.insert( QString::fromLatin1("KColorButton"), SIGNAL(changed(const QColor &)));
+ changedMap.insert( QString::fromLatin1("KDatePicker"), SIGNAL(dateSelected (QDate)));
+ changedMap.insert( QString::fromLatin1("KEditListBox"), SIGNAL(changed()));
+ changedMap.insert( QString::fromLatin1("KListBox"), SIGNAL(selectionChanged()));
+ changedMap.insert( QString::fromLatin1("KLineEdit"), SIGNAL(textChanged(const QString &)));
+ changedMap.insert( QString::fromLatin1("KPasswordEdit"), SIGNAL(textChanged(const QString &)));
+ changedMap.insert( QString::fromLatin1("KRestrictedLine"), SIGNAL(textChanged(const QString &)));
+ changedMap.insert( QString::fromLatin1("KTextBrowser"), SIGNAL(sourceChanged(const QString &)));
+ changedMap.insert( QString::fromLatin1("KTextEdit"), SIGNAL(textChanged()));
+ changedMap.insert( QString::fromLatin1("KURLRequester"), SIGNAL(textChanged (const QString& )));
+ changedMap.insert( QString::fromLatin1("KIntNumInput"), SIGNAL(valueChanged (int)));
+ changedMap.insert( QString::fromLatin1("KIntSpinBox"), SIGNAL(valueChanged (int)));
+ changedMap.insert( QString::fromLatin1("KDoubleNumInput"), SIGNAL(valueChanged (double)));
+ }
+
+ // Go through all of the children of the widgets and find all known widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *widget;
+ bool usingDefaultValues = false;
+ while ( (widget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[widget]);
+ usingDefaultValues |= parseChildren(widget, d->autoWidgets[widget], trackChanges);
+ }
+ return usingDefaultValues;
+}
+
+bool KAutoConfig::saveSettings() {
+ functionCallPreOrderCheck("saveSettings", false);
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ // Go through all of the widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *widget;
+ while ( (widget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[widget]);
+
+ // Go through the known autowidgets of this widget and save
+ QPtrListIterator<QWidget> it( d->autoWidgets[widget] );
+ QWidget *groupWidget;
+ bool widgetChanged = false;
+ while ( (groupWidget = it.current()) != 0 ){
+ ++it;
+ QVariant defaultValue = d->defaultValues[groupWidget];
+ QVariant currentValue = propertyMap->property(groupWidget);
+#if KDE_IS_VERSION( 3, 1, 90 )
+ if(!config->hasDefault(QString::fromLatin1(groupWidget->name())) && currentValue == defaultValue){
+ config->revertToDefault(QString::fromLatin1(groupWidget->name()));
+ widgetChanged = true;
+ }
+ else{
+#endif
+ QVariant savedValue = config->readPropertyEntry(groupWidget->name(),
+ defaultValue);
+ if(savedValue != currentValue){
+ config->writeEntry(groupWidget->name(), currentValue);
+ widgetChanged = true;
+ }
+#if KDE_IS_VERSION( 3, 1, 90 )
+ }
+#endif
+ }
+ d->changed |= widgetChanged;
+ if(widgetChanged)
+ emit( settingsChanged(widget) );
+ }
+
+ if(d->changed){
+ emit( settingsChanged() );
+ d->changed = false;
+ config->sync();
+ return true;
+ }
+ return false;
+}
+
+bool KAutoConfig::hasChanged() const {
+ functionCallPreOrderCheck("hasChanged", false);
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ // Go through all of the widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *widget;
+ while ( (widget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[widget]);
+ // Go through the known autowidgets of this widget and save
+ QPtrListIterator<QWidget> it( d->autoWidgets[widget] );
+ QWidget *groupWidget;
+ while ( (groupWidget = it.current()) != 0 ){
+ ++it;
+ QVariant defaultValue = d->defaultValues[groupWidget];
+ QVariant currentValue = propertyMap->property(groupWidget);
+ QVariant savedValue = config->readPropertyEntry(groupWidget->name(),
+ defaultValue);
+
+ // Return once just one item is found to have changed.
+ if((currentValue == defaultValue && savedValue != currentValue) ||
+ (savedValue != currentValue))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool KAutoConfig::isDefault() const {
+ functionCallPreOrderCheck("isDefault", false);
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ // Go through all of the widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *widget;
+ while ( (widget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[widget]);
+ // Go through the known autowidgets of this widget and save
+ QPtrListIterator<QWidget> it( d->autoWidgets[widget] );
+ QWidget *groupWidget;
+ while ( (groupWidget = it.current()) != 0 ){
+ ++it;
+ QVariant defaultValue = d->defaultValues[groupWidget];
+ QVariant currentValue = propertyMap->property(groupWidget);
+ if(currentValue != defaultValue){
+ //qDebug("groupWidget %s, has changed: default: %s new: %s", groupWidget->name(), defaultValue.toString().latin1(), currentValue.toString().latin1());
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void KAutoConfig::resetSettings() const {
+ functionCallPreOrderCheck("resetSettings",);
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ // Go through all of the widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *widget;
+ while ( (widget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[widget]);
+
+ // Go through the known autowidgets of this widget and save
+ QPtrListIterator<QWidget> it( d->autoWidgets[widget] );
+ QWidget *groupWidget;
+ while ( (groupWidget = it.current()) != 0 ){
+ ++it;
+ QVariant defaultValue = d->defaultValues[groupWidget];
+ if(defaultValue != propertyMap->property(groupWidget)){
+ propertyMap->setProperty(groupWidget, defaultValue);
+ d->changed = true;
+ }
+ }
+ }
+}
+
+void KAutoConfig::reloadSettings() const {
+ functionCallPreOrderCheck("reloadSettings", );
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ // Go through all of the widgets
+ QPtrListIterator<QWidget> it( d->widgets );
+ QWidget *pageWidget;
+ while ( (pageWidget = it.current()) != 0 ) {
+ ++it;
+ config->setGroup(d->groups[pageWidget]);
+
+ // Go through the known widgets of this page and reload
+ QPtrListIterator<QWidget> it( d->autoWidgets[pageWidget] );
+ QWidget *widget;
+ while ( (widget = it.current()) != 0 ){
+ ++it;
+ QVariant defaultSetting = d->defaultValues[widget];
+ QVariant setting =
+ config->readPropertyEntry(widget->name(), defaultSetting);
+ propertyMap->setProperty(widget, setting);
+ }
+ }
+ d->changed = false;
+}
+
+bool KAutoConfig::parseChildren(const QWidget *widget,
+ QPtrList<QWidget>& currentGroup, bool trackChanges){
+ bool valueChanged = false;
+ const QPtrList<QObject> *listOfChildren = widget->children();
+ if(!listOfChildren)
+ return valueChanged;
+
+ QSqlPropertyMap *propertyMap = QSqlPropertyMap::defaultMap();
+ QPtrListIterator<QObject> it( *listOfChildren );
+ QObject *object;
+ while ( (object = it.current()) != 0 )
+ {
+ ++it;
+ if(!object->isWidgetType()){
+ continue;
+ }
+ QWidget *childWidget = (QWidget *)object;
+ if(d->ignore.containsRef(childWidget)){
+ continue;
+ }
+
+ bool parseTheChildren = true;
+#ifndef NDEBUG
+ if(d->ignoreTheseWidgets[childWidget->className()] == 0 &&
+ childWidget->name(0) == NULL){
+ // Without a name the widget is just skipped over.
+ kdDebug(180) << "KAutoConfig::retrieveSettings, widget with "
+ "NULL name. className: " << childWidget->className() << endl;
+ }
+#endif
+
+
+ if( d->ignoreTheseWidgets[childWidget->className()] == 0 &&
+ childWidget->name(0) != NULL )
+ {
+ QVariant defaultSetting = propertyMap->property(childWidget);
+ if(defaultSetting.isValid())
+ {
+ parseTheChildren = false;
+ // Disable the widget if it is immutable?
+ if(config->entryIsImmutable( QString::fromLatin1(childWidget->name())))
+ childWidget->setEnabled(false);
+ else
+ {
+ // FOR THOSE WHO ARE LOOKING
+ // Here is the code were the widget is actually marked to watch.
+ //qDebug("KAutoConfig: Adding widget(%s)",childWidget->name());
+ currentGroup.append(childWidget);
+ d->defaultValues.insert(childWidget, defaultSetting);
+ }
+ // Get/Set settings and connect up the changed signal
+ QVariant setting =
+ config->readPropertyEntry(childWidget->name(), defaultSetting);
+ if(setting != defaultSetting)
+ {
+ propertyMap->setProperty(childWidget, setting);
+ valueChanged = true;
+ }
+ if(trackChanges && changedMap.find(QString::fromLatin1(childWidget->className())) !=
+ changedMap.end())
+ {
+ connect(childWidget, changedMap[QString::fromLatin1(childWidget->className())],
+ this, SIGNAL(widgetModified()));
+ }
+#ifndef NDEBUG
+ else if(trackChanges &&
+ changedMap.find(QString::fromLatin1(childWidget->className())) == changedMap.end())
+ {
+ // Without a signal kautoconfigdialog could incorectly
+ // enable/disable the buttons
+ kdDebug(180) << "KAutoConfig::retrieveSettings, Unknown changed "
+ "signal for widget:" << childWidget->className() << endl;
+ }
+#endif
+
+ }
+#ifndef NDEBUG
+ else
+ {
+ // If kautoconfig doesn't know how to get/set the widget's value
+ // nothing can be done to it and it is skipped.
+ kdDebug(180) << "KAutoConfig::retrieveSettings, Unknown widget:"
+ << childWidget->className() << endl;
+ }
+#endif
+ }
+ if(parseTheChildren)
+ {
+ // this widget is not known as something we can store.
+ // Maybe we can store one of its children.
+ valueChanged |= parseChildren(childWidget, currentGroup, trackChanges);
+ }
+ }
+ return valueChanged;
+}
+
+#include "kautoconfig.moc"
+
diff --git a/kopete/libkopete/kautoconfig.h b/kopete/libkopete/kautoconfig.h
new file mode 100644
index 00000000..de0df143
--- /dev/null
+++ b/kopete/libkopete/kautoconfig.h
@@ -0,0 +1,278 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2003 Benjamin C Meyer (ben+kdelibs at meyerhome dot net)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef KAUTOCONFIG_H
+#define KAUTOCONFIG_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "kopete_export.h"
+
+class KConfig;
+class QWidget;
+
+/**
+ * @short Provides a means of automatically retrieving, saving and resetting basic settings
+ *
+ * The KAutoConfig class provides a means of automatically retrieving,
+ * saving and resetting basic settings. It also can emit signals when
+ * settings have been changed (settings were saved) or modified (the
+ * user changes a checkbox from on to off).
+ *
+ * When told to retrieve settings ( retrieveSettings()) KAutoConfig
+ * will traverse the specified widgets building a list of all known widgets
+ * that have a name and havn't been marked to be ignored.
+ * If a setting is marked immutable the value is loaded and the widget is
+ * disabled.
+ *
+ * The name of the widget determines the name of the setting. The initial
+ * value of the widget also is the default value when the widget is reset.
+ * If the widget does not have a name then it is ignored.
+ *
+ * When saveSettings() or resetSettings() is called KAutoConfig
+ * goes through the list of known widgets and performs the operation on each
+ * of them.
+ *
+ * If one of the widgets needs special treatment it can be specified to be
+ * ignored using the ignoreSubWidget() function.
+ *
+ * KAutoConfig uses the QSqlPropertyMap class to determine if it can do
+ * anything to a widget. Note that KAutoConfig doesn't require a database,
+ * it simply uses the functionality that is built into the QSqlPropertyMap
+ * class. New widgets can be added to the map using
+ * QSqlPropertyMap::installDefaultMap(). Note that you can't just add any
+ * class. The class must have a matching Q_PROPERTY(...) macro defined.
+ *
+ * For example (note that KColorButton is already added and it doesn't need to
+ * manually added):
+ *
+ * kcolorbutton.h defines the following property:
+ * \code
+ * Q_PROPERTY( QColor color READ color WRITE setColor )
+ * \endcode
+ *
+ * To add KColorButton the following code would be inserted in the main.
+ *
+ * \code
+ * QSqlPropertyMap *map = QSqlPropertyMap::defaultMap();
+ * map.insert("KColorButton", "color");
+ * QSqlPropertyMap::installDefaultMap(map);
+ * \endcode
+ *
+ * If you add a new widget to the QSqlPropertyMap and wish to be notified when
+ * it is modified you should add its signal using addWidgetChangedSignal().
+ * If the Apply and Default buttons and enabled/disabled by KAutoConfigDialog
+ * automatically then this must be done.
+ *
+ * @see KAutoConfigDialog
+ * @since 3.2
+ * @author Benjamin C Meyer <ben+kdelibs at meyerhome dot net>
+ */
+class KOPETE_EXPORT KAutoConfig : public QObject {
+
+Q_OBJECT
+
+signals:
+ /**
+ * One or more of the settings have been saved (such as when the user
+ * clicks on the Apply button). This is only emitted by saveSettings()
+ * whenever one or more setting were changed and consequently saved.
+ */
+ void settingsChanged();
+
+ /**
+ * One or more of the settings have been changed.
+ * @param widget - The widget group (pass in via addWidget()) that
+ * contains the one or more modified setting.
+ * @see settingsChanged()
+ */
+ void settingsChanged( QWidget *widget );
+
+ /**
+ * If retrieveSettings() was told to track changes then if
+ * any known setting was changed this signal will be emitted. Note
+ * that a settings can be modified several times and might go back to the
+ * original saved state. hasChanged() will tell you if anything has
+ * actually changed from the saved values.
+ */
+ void widgetModified();
+
+
+public:
+ /**
+ * Constructor.
+ * @param kconfig - KConfig to use when retrieving/saving the widgets
+ * that KAutoConfig knows about.
+ * @param parent - Parent object.
+ * @param name - Object name.
+ */
+ KAutoConfig( KConfig *kconfig, QObject *parent=0, const char *name=0 );
+
+ /**
+ * Constructor.
+ * Uses KGlobal::config() when retrieving/saving the widgets that
+ * KAutoConfig knows about.
+ * @param parent - Parent object.
+ * @param name - Object name.
+ */
+ KAutoConfig( QObject *parent=0, const char *name=0 );
+
+ /**
+ * Destructor. Deletes private class.
+ */
+ ~KAutoConfig();
+
+ /**
+ * Adds a widget to the list of widgets that should be parsed for any
+ * children that KAutoConfig might know when retrieveSettings() is
+ * called. All calls to this function should be made before calling
+ * retrieveSettings().
+ * @param widget - Pointer to the widget to add.
+ * @param group - Name of the group from which all of the settings for this
+ * widget will be located. If a child of 'widget' needs to be in a separate
+ * group it should be added separately and also ignored.
+ * @see ignoreSubWidget()
+ */
+ void addWidget( QWidget *widget, const QString &group );
+
+ /**
+ * Ignore the specified child widget when performing an action. Doesn't
+ * effect widgets that were added with addWidget() only their children. All
+ * calls to this function should be made before calling retrieveSettings().
+ * @param widget - Pointer to the widget that should be ignored.
+ * Note: Widgets that don't have a name are ignored automatically.
+ **/
+ void ignoreSubWidget( QWidget *widget );
+
+ /**
+ * Traverse the specified widgets to see if anything is different then the
+ * current settings. retrieveSettings() must be called before this
+ * function to build the list of known widgets and default values.
+ * @return bool - True if any settings are different then the stored values.
+ */
+ bool hasChanged() const;
+
+ /**
+ * Traverse the specified widgets to see if anything is different then the
+ * default. retrieveSettings() must be called before this function to
+ * build the list of known widgets and default values.
+ * @return bool - True if all of the settings are their default values.
+ */
+ bool isDefault() const;
+
+ /**
+ * Adds a widget and its signal to the internal list so that when
+ * KAutoConfig finds widgetName in retrieveSettings() it will know
+ * how to connect its signal that it has changed to KAutoConfig's signal
+ * widgetModified(). This function should be called before
+ *
+ * Example:
+ * \code
+ * addWidgetChangedSignal( "QCheckbox", SIGNAL(stateChanged(int)) );
+ * \endcode
+ *
+ * This is generally used in conjunction with the addition of a class
+ * to QSqlPropertyMap so KAutoConfig can get/set its values.
+ *
+ * @param widgetName - The class name of the widget (className()).
+ * @param signal - The signal (with "SIGNAL()" wrapper) that should be called.
+ */
+ inline void addWidgetChangedSignal( const QString &widgetName,
+ const QCString &signal ){
+ changedMap.insert( widgetName, signal );
+ }
+
+ /**
+ * Traverse the specified widgets, retrieving the settings for all known
+ * widgets that aren't being ignored and storing the default values.
+ * @param trackChanges - If any changes by the widgets should be tracked
+ * set true. This causes the emitting the modified() signal when
+ * something changes.
+ * @return bool - True if any setting was changed from the default.
+ */
+ bool retrieveSettings( bool trackChanges=false );
+
+public slots:
+ /**
+ * Traverse the specified widgets, saving the settings for all known
+ * widgets that aren't being ignored. retrieveSettings() must be called
+ * before this function to build the list of known widgets and default values.
+ * @return bool - True if any settings were changed.
+ *
+ * Example use: User clicks Ok or Apply button in a configure dialog.
+ */
+ bool saveSettings();
+
+ /**
+ * Traverse the specified widgets, reseting the widgets to their default
+ * values for all known widgets that aren't being ignored.
+ * retrieveSettings() must be called before this function to build
+ * the list of known widgets and default values.
+ *
+ * Example use: User clicks Default button in a configure dialog.
+ */
+ void resetSettings() const;
+
+ /**
+ * Traverse the specified widgets, reloading the settings for all known
+ * widgets that aren't being ignored.
+ * retrieveSettings() must be called before this function to build
+ * the list of known widgets and default values.
+ *
+ * Example use: User clicks Reset button in a configure dialog.
+ */
+ void reloadSettings() const;
+
+protected:
+ /**
+ * KConfigBase object used to get/save values.
+ */
+ KConfig *config;
+ /**
+ * Map of the classes and the signals that they emit when changed.
+ */
+ QMap<QString, QCString> changedMap;
+
+ /**
+ * Recursive function that finds all known children.
+ * Goes through the children of widget and if any are known and not being
+ * ignored, stores them in currentGroup. Also checks if the widget
+ * should be disabled because it is set immutable.
+ * @param widget - Parent of the children to look at.
+ * @param currentGroup - Place to store known children of widget.
+ * @param trackChanges - If true then tracks any changes to the children of
+ * widget that are known.
+ * @return bool - If a widget was set to something other then its default.
+ * @see retrieveSettings()
+ */
+ bool parseChildren( const QWidget *widget,
+ QPtrList<QWidget>&currentGroup, bool trackChanges );
+
+private:
+ class KAutoConfigPrivate;
+ /**
+ * KAutoConfig Private class.
+ */
+ KAutoConfigPrivate *d;
+
+};
+
+#endif // KAUTOCONFIG_H
+
diff --git a/kopete/libkopete/kcautoconfigmodule.cpp b/kopete/libkopete/kcautoconfigmodule.cpp
new file mode 100644
index 00000000..8bbe87c0
--- /dev/null
+++ b/kopete/libkopete/kcautoconfigmodule.cpp
@@ -0,0 +1,114 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Olivier Goffart <ogoffart @ kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#include "kcautoconfigmodule.h"
+
+#include <qlayout.h>
+
+#include <kautoconfig.h>
+
+class KCAutoConfigModule::KCAutoConfigModulePrivate
+{
+ public:
+ KAutoConfig *kautoconfig;
+};
+
+
+KCAutoConfigModule::KCAutoConfigModule( QWidget * parent, const char * name, const QStringList & args )
+ : KCModule( parent, name, args )
+ , d( new KCAutoConfigModulePrivate )
+{
+ d->kautoconfig = new KAutoConfig( this );
+ connect(d->kautoconfig, SIGNAL(widgetModified()), SLOT(slotWidgetModified()));
+ connect(d->kautoconfig, SIGNAL(settingsChanged()), SLOT(widgetModified()));
+}
+
+KCAutoConfigModule::KCAutoConfigModule( KInstance * instance, QWidget * parent, const QStringList & args )
+ : KCModule( instance, parent, args )
+ , d( new KCAutoConfigModulePrivate )
+{
+ d->kautoconfig = new KAutoConfig( this );
+ connect(d->kautoconfig, SIGNAL(widgetModified()), SLOT(slotWidgetModified()));
+ connect(d->kautoconfig, SIGNAL(settingsChanged()), SLOT(slotWidgetModified()));
+}
+
+
+
+KCAutoConfigModule::KCAutoConfigModule( KConfig *config,QWidget * parent, const char * name, const QStringList & args )
+ : KCModule( parent, name, args ) , d( new KCAutoConfigModulePrivate )
+{
+ d->kautoconfig = new KAutoConfig( config, this );
+ connect(d->kautoconfig, SIGNAL(widgetModified()), SLOT(slotWidgetModified()));
+ connect(d->kautoconfig, SIGNAL(settingsChanged()), SLOT(slotWidgetModified()));
+}
+
+KCAutoConfigModule::KCAutoConfigModule( KConfig *config , KInstance * instance, QWidget * parent, const QStringList & args )
+ : KCModule( instance, parent, args )
+ , d( new KCAutoConfigModulePrivate )
+{
+ d->kautoconfig = new KAutoConfig( config, this );
+ connect(d->kautoconfig, SIGNAL(widgetModified()), SLOT(slotWidgetModified()));
+ connect(d->kautoconfig, SIGNAL(settingsChanged()), SLOT(slotWidgetModified()));
+}
+
+
+KCAutoConfigModule::~KCAutoConfigModule()
+{
+ delete d;
+}
+
+
+void KCAutoConfigModule::load()
+{
+ d->kautoconfig->reloadSettings();
+}
+
+void KCAutoConfigModule::save()
+{
+ d->kautoconfig->saveSettings();
+}
+
+void KCAutoConfigModule::defaults()
+{
+ d->kautoconfig->resetSettings();
+}
+
+void KCAutoConfigModule::slotWidgetModified()
+{
+ emit changed(d->kautoconfig->hasChanged());
+}
+
+KAutoConfig *KCAutoConfigModule::autoConfig()
+{
+ return d->kautoconfig;
+}
+
+void KCAutoConfigModule::setMainWidget(QWidget *widget, const QString& group )
+{
+ QBoxLayout * l = new QVBoxLayout( this );
+ l->addWidget( widget );
+
+ d->kautoconfig->addWidget(widget,group);
+ d->kautoconfig->retrieveSettings(true);
+}
+
+#include "kcautoconfigmodule.moc"
+
+// vim: sw=4 sts=4 et
+
diff --git a/kopete/libkopete/kcautoconfigmodule.h b/kopete/libkopete/kcautoconfigmodule.h
new file mode 100644
index 00000000..e3737ca5
--- /dev/null
+++ b/kopete/libkopete/kcautoconfigmodule.h
@@ -0,0 +1,150 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Olivier Goffart <ogoffart @ kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+*/
+
+#ifndef KCAUTOCONFIGMODULE_H
+#define KCAUTOCONFIGMODULE_H
+
+#include <kcmodule.h>
+
+#include "kopete_export.h"
+
+class KAutoConfig;
+class KConfig;
+
+
+/**
+ * @short Convenience KCModule for creating config page handled with KAutoConfig
+ *
+ * This class makes it very easy to create a configuration page using KAutoConfig.
+ * All you need to do is create a class that is derived from KCAutoConfigModule, create your
+ * config page with QDesigner, and add it to the module
+ * This can be done using the setMainWidget() method:
+ * \code
+ * typedef KGenericFactory<MyPageConfig, QWidget> MyPageConfigFactory;
+ * K_EXPORT_COMPONENT_FACTORY( kcm_mypageconfig, MyPageConfigFactory( "kcm_mypageconfig" ) )
+ *
+ * MyPageConfig( QWidget * parent, const char *, const QStringList & args )
+ * : KCAutoConfigModule( MyPageConfigFactory::instance(), parent, args )
+ * {
+ * setMainWidget( new MyPageConfigBase(this) , "MyGroup" );
+ * }
+ * \endcode
+ *
+ *
+ * @author Olivier Goffart <ogoffart(@)tisclinet.be>
+ * @since 3.2
+ */
+class KOPETE_EXPORT KCAutoConfigModule : public KCModule
+{
+ Q_OBJECT
+ public:
+ /**
+ * Standard KCModule constructor. Use KGlobal::config()
+ */
+ KCAutoConfigModule( QWidget * parent = 0, const char * name = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Standard KCModule constructor. Use KGlobal::config()
+ */
+ KCAutoConfigModule( KInstance * instance, QWidget * parent = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Constructor.
+ * @param config the KConfig to use
+ * @param instance KInstance object for this KCM
+ * @param parent parent widget
+ * @param args special arguments for this KCM
+ *
+ * @todo document what the args mean (inherited from KCModule?)
+ */
+ KCAutoConfigModule(KConfig* config, KInstance * instance, QWidget * parent = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Constructor, much like the one above, except with
+ * no instance and with a name.
+ * @param config the KConfig to use
+ * @param parent parent widget
+ * @param name name of the object
+ * @param args special arguments for this KCM
+ */
+ KCAutoConfigModule(KConfig* config, QWidget * parent = 0, const char * name=0 , const QStringList & args = QStringList() );
+
+
+ ~KCAutoConfigModule();
+
+ /**
+ * Set the main widget. @p widget will be lay out to take all available place in the module.
+ * @p widget must have this module as parent.
+ *
+ * This method automatically call KAutoConfig::addWidget() and KAutoConfig::retrieveSettings()
+ *
+ * @param widget the widget to place on the page and to add in the KAutoConfig
+ * @param group the name of the group where settings are stored in the config file
+ */
+ void setMainWidget(QWidget *widget, const QString& group);
+
+ /**
+ * @brief a reference to the KAutoConfig
+ *
+ * You can add or remove manually some widget from the KAutoWidget.
+ * If you choose to don't add the main widget with setMainWidget() , you need
+ * to call KAutoConfig::retrieveSettings(true) yourself
+ *
+ * @return a reference to the KAutoConfig
+ */
+ KAutoConfig *autoConfig();
+
+ /**
+ * Reload the config from the configfile.
+ *
+ * You can also reimplement this method, but you should always call the parent KCModule::load()
+ * be sure you know what you are doing
+ */
+ virtual void load();
+
+ /**
+ * Save the config to the configfile.
+ *
+ * You can also reimplement this method, but you should always call the parent KCModule::save()
+ * be sure you know what you are doing
+ */
+ virtual void save();
+
+ /**
+ * Reload the default config
+ *
+ * You can also reimplement this method, but you should always call the parent KCModule::defaults()
+ * be sure you know what you are doing
+ */
+ virtual void defaults();
+
+
+ protected slots:
+ /**
+ * Some setting was modified, updates buttons
+ */
+ virtual void slotWidgetModified();
+
+ private:
+ class KCAutoConfigModulePrivate;
+ KCAutoConfigModulePrivate * d;
+};
+
+
+#endif
diff --git a/kopete/libkopete/knotification.cpp b/kopete/libkopete/knotification.cpp
new file mode 100644
index 00000000..3749c21c
--- /dev/null
+++ b/kopete/libkopete/knotification.cpp
@@ -0,0 +1,533 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Olivier Goffart <ogoffart @ kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "knotification.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <knotifyclient.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kpassivepopup.h>
+#include <kactivelabel.h>
+#include <kprocess.h>
+#include <kdialog.h>
+#include <kmacroexpander.h>
+#include <kwin.h>
+
+
+#include <qvbox.h>
+#include <dcopclient.h>
+#include <qcstring.h>
+#include <qguardedptr.h>
+#include <qstylesheet.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <qtabwidget.h>
+
+
+
+//TODO, make the KNotification aware of the systemtray.
+#include "kopeteuiglobal.h"
+static WId checkWinId( const QString &/*appName*/, WId senderWinId )
+{
+ if(senderWinId==0)
+ senderWinId=Kopete::UI::Global::sysTrayWId();
+
+ return senderWinId;
+}
+
+
+struct KNotification::Private
+{
+ QWidget *widget;
+ QString text;
+ QStringList actions;
+ int level;
+};
+
+KNotification::KNotification(QObject *parent) :
+ QObject(parent) , d(new Private)
+{
+ m_linkClicked = false;
+}
+
+KNotification::~KNotification()
+{
+ delete d;
+}
+
+
+void KNotification::notifyByExecute(const QString &command, const QString& event,
+ const QString& fromApp, const QString& text,
+ int winId, int eventId)
+{
+ if (!command.isEmpty())
+ {
+ // kdDebug() << "executing command '" << command << "'" << endl;
+ QMap<QChar,QString> subst;
+ subst.insert( 'e', event );
+ subst.insert( 'a', fromApp );
+ subst.insert( 's', text );
+ subst.insert( 'w', QString::number( winId ));
+ subst.insert( 'i', QString::number( eventId ));
+ QString execLine = KMacroExpander::expandMacrosShellQuote( command, subst );
+ if ( execLine.isEmpty() )
+ execLine = command; // fallback
+
+ KProcess p;
+ p.setUseShell(true);
+ p << execLine;
+ p.start(KProcess::DontCare);
+// return true;
+ }
+ //return false;
+}
+
+
+void KNotification::notifyByMessagebox()
+{
+ // ignore empty messages
+ if ( d->text.isEmpty() )
+ return;
+
+ QString action=d->actions[0];
+ WId winId=d->widget ? d->widget->topLevelWidget()->winId() : 0;
+
+ if( action.isEmpty())
+ {
+ // display message box for specified event level
+ switch( d->level )
+ {
+ default:
+ case KNotifyClient::Notification:
+ KMessageBox::informationWId( winId, d->text, i18n( "Notification" ) );
+ break;
+ case KNotifyClient::Warning:
+ KMessageBox::sorryWId( winId, d->text, i18n( "Warning" ) );
+ break;
+ case KNotifyClient::Error:
+ KMessageBox::errorWId( winId, d->text, i18n( "Error" ) );
+ break;
+ case KNotifyClient::Catastrophe:
+ KMessageBox::errorWId( winId, d->text, i18n( "Fatal" ) );
+ break;
+ }
+ }
+ else
+ { //we may show the specific action button
+ int result=0;
+ QGuardedPtr<KNotification> _this=this; //this can be deleted
+ switch( d->level )
+ {
+ default:
+ case KNotifyClient::Notification:
+ result = KMessageBox::questionYesNo(d->widget, d->text, i18n( "Notification" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Warning:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Warning" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Error:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Error" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ case KNotifyClient::Catastrophe:
+ result = KMessageBox::warningYesNo( d->widget, d->text, i18n( "Fatal" ), action, KStdGuiItem::cancel(), QString::null, false );
+ break;
+ }
+ if(result==KMessageBox::Yes && _this)
+ {
+ activate(0);
+ }
+ }
+}
+
+
+
+void KNotification::notifyByPassivePopup(const QPixmap &pix )
+{
+ QString appName = QString::fromAscii( KNotifyClient::instance()->instanceName() );
+ KIconLoader iconLoader( appName );
+ KConfig eventsFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
+ KConfigGroup config( &eventsFile, "!Global!" );
+ QString iconName = config.readEntry( "IconName", appName );
+ QPixmap icon = iconLoader.loadIcon( iconName, KIcon::Small );
+ QString title = config.readEntry( "Comment", appName );
+ //KPassivePopup::message(title, text, icon, senderWinId);
+
+ WId winId=d->widget ? d->widget->topLevelWidget()->winId() : 0;
+
+ KPassivePopup *pop = new KPassivePopup( checkWinId(appName, winId) );
+ QObject::connect(this, SIGNAL(closed()), pop, SLOT(deleteLater()));
+
+ QVBox *vb = pop->standardView( title, pix.isNull() ? d->text: QString::null , icon );
+ QVBox *vb2=vb;
+
+ if(!pix.isNull())
+ {
+ QHBox *hb = new QHBox(vb);
+ hb->setSpacing(KDialog::spacingHint());
+ QLabel *pil=new QLabel(hb);
+ pil->setPixmap(pix);
+ pil->setScaledContents(true);
+ if(pix.height() > 80 && pix.height() > pix.width() )
+ {
+ pil->setMaximumHeight(80);
+ pil->setMaximumWidth(80*pix.width()/pix.height());
+ }
+ else if(pix.width() > 80 && pix.height() <= pix.width())
+ {
+ pil->setMaximumWidth(80);
+ pil->setMaximumHeight(80*pix.height()/pix.width());
+ }
+ vb=new QVBox(hb);
+ QLabel *msg = new QLabel( d->text, vb, "msg_label" );
+ msg->setAlignment( AlignLeft );
+ }
+
+
+ if ( !d->actions.isEmpty() )
+ {
+ QString linkCode=QString::fromLatin1("<p align=\"right\">");
+ int i=0;
+ for ( QStringList::ConstIterator it = d->actions.begin() ; it != d->actions.end(); ++it )
+ {
+ i++;
+ linkCode+=QString::fromLatin1("&nbsp;<a href=\"%1\">%2</a> ").arg( QString::number(i) , QStyleSheet::escape(*it) );
+ }
+ linkCode+=QString::fromLatin1("</p>");
+ KActiveLabel *link = new KActiveLabel(linkCode , vb );
+ //link->setAlignment( AlignRight );
+ QObject::disconnect(link, SIGNAL(linkClicked(const QString &)), link, SLOT(openLink(const QString &)));
+ QObject::connect(link, SIGNAL(linkClicked(const QString &)), this, SLOT(slotPopupLinkClicked(const QString &)));
+ QObject::connect(link, SIGNAL(linkClicked(const QString &)), pop, SLOT(hide()));
+ }
+
+ pop->setAutoDelete( true );
+ //pop->setTimeout(-1);
+
+ pop->setView( vb2 );
+ pop->show();
+
+}
+
+void KNotification::slotPopupLinkClicked(const QString &adr)
+{
+ m_linkClicked = true;
+ unsigned int action=adr.toUInt();
+ if(action==0)
+ return;
+
+ activate(action);
+
+ // since we've hidden the message (KNotification::notifyByPassivePopup(const QPixmap &pix ))
+ // we must now schedule overselves for deletion
+ close();
+}
+
+void KNotification::activate(unsigned int action)
+{
+ if(action==0)
+ emit activated();
+
+ emit activated(action);
+ deleteLater();
+}
+
+
+void KNotification::close()
+{
+ // if the user hasn't clicked the link, and if we got here, it means the dialog closed
+ // and we were ignored
+ if (!m_linkClicked)
+ {
+ emit ignored();
+ }
+
+ emit closed();
+ deleteLater();
+}
+
+
+void KNotification::raiseWidget()
+{
+ if(!d->widget)
+ return;
+
+ raiseWidget(d->widget);
+}
+
+
+void KNotification::raiseWidget(QWidget *w)
+{
+ //TODO this funciton is far from finished.
+ if(w->isTopLevel())
+ {
+ w->raise();
+ KWin::activateWindow( w->winId() );
+ }
+ else
+ {
+ QWidget *pw=w->parentWidget();
+ raiseWidget(pw);
+
+ if( QTabWidget *tab_widget=dynamic_cast<QTabWidget*>(pw))
+ {
+ tab_widget->showPage(w);
+ }
+ }
+}
+
+
+
+
+
+KNotification *KNotification::event( const QString& message , const QString& text,
+ const QPixmap& pixmap, QWidget *widget,
+ const QStringList &actions, unsigned int flags)
+{
+ /* NOTE: this function still use the KNotifyClient,
+ * in the future (KDE4) all the function of the knotifyclient will be moved there.
+ * Some code here is derived from the old KNotify deamon
+ */
+
+ int level=KNotifyClient::Default;
+ QString sound;
+ QString file;
+ QString commandline;
+
+ // get config file
+ KConfig eventsFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+"/eventsrc" ), true, false, "data");
+ eventsFile.setGroup(message);
+
+ KConfig configFile( QString::fromAscii( KNotifyClient::instance()->instanceName()+".eventsrc" ), true, false);
+ configFile.setGroup(message);
+
+ int present=KNotifyClient::getPresentation(message);
+ if(present==-1)
+ present=KNotifyClient::getDefaultPresentation(message);
+ if(present==-1)
+ present=0;
+
+ // get sound file name
+ if( present & KNotifyClient::Sound ) {
+ QString theSound = configFile.readPathEntry( "soundfile" );
+ if ( theSound.isEmpty() )
+ theSound = eventsFile.readPathEntry( "default_sound" );
+ if ( !theSound.isEmpty() )
+ sound = theSound;
+ }
+
+ // get log file name
+ if( present & KNotifyClient::Logfile ) {
+ QString theFile = configFile.readPathEntry( "logfile" );
+ if ( theFile.isEmpty() )
+ theFile = eventsFile.readPathEntry( "default_logfile" );
+ if ( !theFile.isEmpty() )
+ file = theFile;
+ }
+
+ // get default event level
+ if( present & KNotifyClient::Messagebox )
+ level = eventsFile.readNumEntry( "level", 0 );
+
+ // get command line
+ if (present & KNotifyClient::Execute ) {
+ commandline = configFile.readPathEntry( "commandline" );
+ if ( commandline.isEmpty() )
+ commandline = eventsFile.readPathEntry( "default_commandline" );
+ }
+
+ return userEvent( text, pixmap, widget, actions, present , level, sound, file, commandline, flags );
+}
+
+KNotification *KNotification::userEvent( const QString& text, const QPixmap& pixmap, QWidget *widget,
+ QStringList actions,int present, int level, const QString &sound, const QString &file,
+ const QString &commandline, unsigned int flags)
+{
+
+ /* NOTE: this function still use the KNotifyClient,
+ * in the futur (KDE4) all the function of the knotifyclient will be moved there.
+ * Some code of this function fome from the old KNotify deamon
+ */
+
+
+ KNotification *notify=new KNotification(widget);
+ notify->d->widget=widget;
+ notify->d->text=text;
+ notify->d->actions=actions;
+ notify->d->level=level;
+ WId winId=widget ? widget->topLevelWidget()->winId() : 0;
+
+
+ //we will catch some event that will not be fired by the old deamon
+
+
+ //we remove presentation that has been already be played, and we fire the event in the old way
+
+
+ KNotifyClient::userEvent(winId,text,present & ~( KNotifyClient::PassivePopup|KNotifyClient::Messagebox|KNotifyClient::Execute),level,sound,file);
+
+
+ if ( present & KNotifyClient::PassivePopup )
+ {
+ notify->notifyByPassivePopup( pixmap );
+ }
+ if ( present & KNotifyClient::Messagebox )
+ {
+ QTimer::singleShot(0,notify,SLOT(notifyByMessagebox()));
+ }
+ else //not a message box (because closing the event when a message box is there is suicide)
+ if(flags & CloseOnTimeout)
+ {
+ QTimer::singleShot(6*1000, notify, SLOT(close()));
+ }
+ if ( present & KNotifyClient::Execute )
+ {
+ QString appname = QString::fromAscii( KNotifyClient::instance()->instanceName() );
+ notify->notifyByExecute(commandline, QString::null,appname,text, winId, 0 );
+ }
+
+ return notify;
+
+}
+
+
+
+/* This code is there before i find a great way to perform context-dependent notifications
+ * in a way independent of kopete.
+ * i'm in fact still using the Will's old code.
+ */
+
+
+#include "kopeteeventpresentation.h"
+#include "kopetegroup.h"
+#include "kopetenotifydataobject.h"
+#include "kopetenotifyevent.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+#include <qimage.h>
+
+
+static KNotification *performCustomNotifications( QWidget *widget, Kopete::MetaContact * mc, const QString &message, bool& suppress)
+{
+ KNotification *n=0L;
+ //kdDebug( 14010 ) << k_funcinfo << endl;
+ if ( suppress )
+ return n;
+
+ // Anything, including the MC itself, may set suppress and prevent further notifications
+
+ /* This is a really ugly piece of logic now. The idea is to check for notifications
+ * first on the metacontact, then on each of its groups, until something suppresses
+ * any further notifications.
+ * So on the first run round this loop, dataObj points to the metacontact, and on subsequent
+ * iterations it points to one of the contact's groups. The metacontact pointer is maintained
+ * so that if a group has a chat notification set for this event, we can call execute() on the MC.
+ */
+
+ bool checkingMetaContact = true;
+ Kopete::NotifyDataObject * dataObj = mc;
+ do {
+ QString sound;
+ QString text;
+
+ if ( dataObj )
+ {
+ Kopete::NotifyEvent *evt = dataObj->notifyEvent( message );
+ if ( evt )
+ {
+ suppress = evt->suppressCommon();
+ int present = 0;
+ // sound
+ Kopete::EventPresentation *pres = evt->presentation( Kopete::EventPresentation::Sound );
+ if ( pres && pres->enabled() )
+ {
+ present = present | KNotifyClient::Sound;
+ sound = pres->content();
+ evt->firePresentation( Kopete::EventPresentation::Sound );
+ }
+ // message
+ if ( ( pres = evt->presentation( Kopete::EventPresentation::Message ) )
+ && pres->enabled() )
+ {
+ present = present | KNotifyClient::PassivePopup;
+ text = pres->content();
+ evt->firePresentation( Kopete::EventPresentation::Message );
+ }
+ // chat
+ if ( ( pres = evt->presentation( Kopete::EventPresentation::Chat ) )
+ && pres->enabled() )
+ {
+ mc->execute();
+ evt->firePresentation( Kopete::EventPresentation::Chat );
+ }
+ // fire the event
+ n=KNotification::userEvent( text, mc->photo(), widget, QStringList() , present, 0, sound, QString::null, QString::null , KNotification::CloseOnTimeout);
+ }
+ }
+
+ if ( mc )
+ {
+ if ( checkingMetaContact )
+ {
+ // only executed on first iteration
+ checkingMetaContact = false;
+ dataObj = mc->groups().first();
+ }
+ else
+ dataObj = mc->groups().next();
+ }
+ }
+ while ( dataObj && !suppress );
+ return n;
+}
+
+
+
+
+KNotification *KNotification::event( Kopete::MetaContact *mc, const QString& message ,
+ const QString& text, const QPixmap& pixmap, QWidget *widget,
+ const QStringList &actions, unsigned int flags)
+{
+ if (message.isEmpty()) return 0;
+
+ bool suppress = false;
+ KNotification *n=performCustomNotifications( widget, mc, message, suppress);
+
+ if ( suppress )
+ {
+ //kdDebug( 14000 ) << "suppressing common notifications" << endl;
+ return n; // custom notifications don't create a single unique id
+ }
+ else
+ {
+ //kdDebug( 14000 ) << "carrying out common notifications" << endl;
+ return event( message, text, pixmap, widget , actions, flags);
+ }
+}
+
+
+
+
+
+#include "knotification.moc"
+
+
+
diff --git a/kopete/libkopete/knotification.h b/kopete/libkopete/knotification.h
new file mode 100644
index 00000000..b017b7c0
--- /dev/null
+++ b/kopete/libkopete/knotification.h
@@ -0,0 +1,217 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 Olivier Goffart <ogoffart @ kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#ifndef KNOTIFICATION_H
+#define KNOTIFICATION_H
+
+
+#include <qpixmap.h>
+#include <qobject.h>
+#include <qstringlist.h>
+#include "kopete_export.h"
+
+class QWidget;
+namespace Kopete { class MetaContact; }
+
+/**
+ * KNotification is used to notify some event to the user.
+ *
+ * It covers severals kind of notifications
+ *
+ * @li Interface feedback events:
+ * For notifying the user that he/she just performed an operation, like maximizing a
+ * window. This allows us to play sounds when a dialog appears.
+ * This is an instant notification. It ends automatically after a small timeout
+ *
+ * @li complex notifications:
+ * Notify when one received a new message, or when something important happened
+ * the user has to know. This notification has a start and a end. It start when
+ * the event actually occurs, and finish when the message is acknowledged.
+ *
+ *
+ * use the static function event() to fire an event
+ *
+ * the returned KNotification pointer may be used to connect signals or slots
+ *
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+class KOPETE_EXPORT KNotification : public QObject
+{
+ Q_OBJECT
+public:
+
+ enum NotificationFlags
+ {
+ /**
+ * When the notification is activated, raise the notification's widget.
+ *
+ * This will change the desktop, raise the window, and switch to the tab.
+ */
+ RaiseWidgetOnActivation=0x01,
+
+ /**
+ * The notification will be automatically closed after a timeout.
+ */
+ CloseOnTimeout=0x02,
+ /**
+ * The notification will be automatically closed if the widget() becomes
+ * activated.
+ *
+ * If the widget is already activated when the notification occurs, the
+ * notification will be closed after a small timeout.
+ */
+ CloseWhenWidgetActivated=0x03
+ };
+
+
+ ~KNotification();
+
+ /**
+ * @brief the widget associated to the notification
+ *
+ * If the widget is destroyed, the notification will be automatically canceled.
+ * If the widget is activated, the notificaiton will be automatically closed if the flags said that
+ *
+ * When the notification is activated, the widget might be raised.
+ * Depending of the configuration, the taskbar entry of the window containing the widget may blink.
+ */
+ QWidget *widget();
+
+ signals:
+ /**
+ * Emit only when the default activation has occured
+ */
+ void activated();
+ /**
+ * Emit when an action has been activated.
+ * @param action will be 0 is the default aciton was activated, or any actiton id
+ */
+ void activated(unsigned int action);
+
+ /**
+ * Emit when the notification is closed. Both if it's activated or just ignored
+ */
+ void closed();
+
+ /**
+ * The notification has been ignored
+ */
+ void ignored();
+
+public slots:
+ /**
+ * @brief Active the action specified action
+ * If the action is zero, then the default action is activated
+ */
+ void activate(unsigned int action=0);
+
+ /**
+ * close the notification without activate it.
+ *
+ * This will delete the notification
+ */
+ void close();
+
+ /**
+ * @brief Raise the widget.
+ * This will change the desktop, activate the window, and the tab if needed.
+ */
+ void raiseWidget();
+
+
+
+private:
+ struct Private;
+ Private *d;
+ KNotification(QObject *parent=0L);
+ /**
+ * recursive function that raise the widget. @p w
+ *
+ * @see raiseWidget()
+ */
+ static void raiseWidget(QWidget *w);
+
+ bool m_linkClicked;
+
+private slots:
+ void notifyByMessagebox();
+ void notifyByPassivePopup(const QPixmap &pix);
+ void notifyByExecute(const QString &command, const QString& event,const QString& fromApp, const QString& text, int winId, int eventId);
+ void slotPopupLinkClicked(const QString &);
+
+
+public:
+ /**
+ * @brief emit an event
+ *
+ * A popup may be showed, a sound may be played, depending the config.
+ *
+ * return a KNotification . You may use that pointer to connect some signals or slot.
+ * the pointer is automatically deleted when the event is closed.
+ *
+ * Make sure you use one of the CloseOnTimeOut or CloseWhenWidgetActivated, if not,
+ * you have to close yourself the notification.
+ *
+ * @note the text is shown in a QLabel, you should make sure to escape the html is needed.
+ *
+ * @param eventId is the name of the event
+ * @param text is the text of the notification to show in the popup.
+ * @param pixmap is a picture which may be shown in the popup.
+ * @param widget is a widget where the notification reports to
+ * @param actions is a list of action texts.
+ * @param flags is a bitmask of NotificationsFlags
+ */
+ static KNotification *event( const QString& eventId , const QString& text=QString::null,
+ const QPixmap& pixmap=QPixmap(), QWidget *widget=0L,
+ const QStringList &actions=QStringList(), unsigned int flags=CloseOnTimeout);
+
+
+ /**
+ * @brief emit a custom event
+ *
+ * @param text is the text of the notification to show in the popup.
+ * @param pixmap is a picture which may be shown in the popup
+ * @param widget is a widget where the notification raports to
+ * @param actions is a list of actions text.
+ * @param present The presentation method of the event
+ * @param level The error message level
+ * @param sound The sound to play if selected with @p present
+ * @param file The log file to append the message to if selected with @p parent
+ * @param commandLine the command line to run if selected with @p parent
+ * @param flags Indicates the way in which the notification should be handled
+ */
+ static KNotification *userEvent( const QString& text, const QPixmap& pixmap,
+ QWidget *widget, QStringList actions,int present, int level,
+ const QString &sound, const QString &file,
+ const QString &commandLine, unsigned int flags);
+
+
+
+ /**
+ * @todo find a proper way to do context-dependent notifications
+ */
+ static KNotification *event( Kopete::MetaContact *mc, const QString& eventId , const QString& text=QString::null,
+ const QPixmap& pixmap=QPixmap(), QWidget *widget=0L,
+ const QStringList &actions=QStringList(),unsigned int flags=CloseOnTimeout);
+
+};
+
+
+
+#endif
diff --git a/kopete/libkopete/kopete.kcfg b/kopete/libkopete/kopete.kcfg
new file mode 100644
index 00000000..59573689
--- /dev/null
+++ b/kopete/libkopete/kopete.kcfg
@@ -0,0 +1,12 @@
+<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
+<kcfg>
+ <kcfgfile name="kopete" />
+ <group name="GlobalIdentity" >
+ <entry key="enableGlobalIdentity" type="Bool" name="EnableGlobalIdentity" >
+ <label>Enable the global identity feature</label>
+ <whatsthis>When enabled, this allows you to set your data in a central place. All your IM accounts will use this global data.
+</whatsthis>
+ <default>false</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kopete/libkopete/kopete_export.h b/kopete/libkopete/kopete_export.h
new file mode 100644
index 00000000..df184b57
--- /dev/null
+++ b/kopete/libkopete/kopete_export.h
@@ -0,0 +1,30 @@
+/*
+ Kopete Export macors
+
+ Copyright (c) 2004 by Dirk Mueller <mueller@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_EXPORT_H
+#define KOPETE_EXPORT_H
+
+#include <kdemacros.h>
+#include <kdeversion.h>
+
+#if KDE_IS_VERSION(3,3,2)
+#define KOPETE_EXPORT KDE_EXPORT
+#else
+#define KOPETE_EXPORT
+#endif
+
+#endif
diff --git a/kopete/libkopete/kopeteaccount.cpp b/kopete/libkopete/kopeteaccount.cpp
new file mode 100644
index 00000000..52bb26bc
--- /dev/null
+++ b/kopete/libkopete/kopeteaccount.cpp
@@ -0,0 +1,581 @@
+/*
+ kopeteaccount.cpp - Kopete Account
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2003-2004 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+
+#include "kabcpersistence.h"
+#include "kopetecontactlist.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopetepluginmanager.h"
+#include "kopetegroup.h"
+#include "kopeteprefs.h"
+#include "kopeteutils.h"
+#include "kopeteuiglobal.h"
+#include "kopeteblacklister.h"
+#include "kopeteonlinestatusmanager.h"
+#include "editaccountwidget.h"
+
+namespace Kopete
+{
+
+
+class Account::Private
+{
+public:
+ Private( Protocol *protocol, const QString &accountId )
+ : protocol( protocol ), id( accountId )
+ , excludeconnect( true ), priority( 0 ), myself( 0 )
+ , suppressStatusTimer( 0 ), suppressStatusNotification( false )
+ , blackList( new Kopete::BlackLister( protocol->pluginId(), accountId ) )
+ , connectionTry(0)
+ { }
+
+
+ ~Private() { delete blackList; }
+
+ Protocol *protocol;
+ QString id;
+ QString accountLabel;
+ bool excludeconnect;
+ uint priority;
+ QDict<Contact> contacts;
+ QColor color;
+ Contact *myself;
+ QTimer suppressStatusTimer;
+ bool suppressStatusNotification;
+ Kopete::BlackLister *blackList;
+ KConfigGroup *configGroup;
+ uint connectionTry;
+ QString customIcon;
+ Kopete::OnlineStatus restoreStatus;
+ QString restoreMessage;
+};
+
+Account::Account( Protocol *parent, const QString &accountId, const char *name )
+ : QObject( parent, name ), d( new Private( parent, accountId ) )
+{
+ d->configGroup=new KConfigGroup(KGlobal::config(), QString::fromLatin1( "Account_%1_%2" ).arg( d->protocol->pluginId(), d->id ));
+
+ d->excludeconnect = d->configGroup->readBoolEntry( "ExcludeConnect", false );
+ d->color = d->configGroup->readColorEntry( "Color", &d->color );
+ d->customIcon = d->configGroup->readEntry( "Icon", QString() );
+ d->priority = d->configGroup->readNumEntry( "Priority", 0 );
+
+ d->restoreStatus = Kopete::OnlineStatus::Online;
+ d->restoreMessage = "";
+
+ QObject::connect( &d->suppressStatusTimer, SIGNAL( timeout() ),
+ this, SLOT( slotStopSuppression() ) );
+}
+
+Account::~Account()
+{
+ d->contacts.remove( d->myself->contactId() );
+
+ // Delete all registered child contacts first
+ while ( !d->contacts.isEmpty() )
+ delete *QDictIterator<Contact>( d->contacts );
+
+ kdDebug( 14010 ) << k_funcinfo << " account '" << d->id << "' about to emit accountDestroyed " << endl;
+ emit accountDestroyed(this);
+
+ delete d->myself;
+ delete d->configGroup;
+ delete d;
+}
+
+void Account::reconnect()
+{
+ kdDebug( 14010 ) << k_funcinfo << "account " << d->id << " restoreStatus " << d->restoreStatus.status() << " restoreMessage " << d->restoreMessage << endl;
+ setOnlineStatus( d->restoreStatus, d->restoreMessage );
+}
+
+void Account::disconnected( DisconnectReason reason )
+{
+ kdDebug( 14010 ) << k_funcinfo << reason << endl;
+ //reconnect if needed
+ if(reason == BadPassword )
+ {
+ QTimer::singleShot(0, this, SLOT(reconnect()));
+ }
+ else if ( KopetePrefs::prefs()->reconnectOnDisconnect() == true && reason > Manual )
+ {
+ d->connectionTry++;
+ //use a timer to allow the plugins to clean up after return
+ if(d->connectionTry < 3)
+ QTimer::singleShot(10000, this, SLOT(reconnect())); // wait 10 seconds before reconnect
+ }
+ if(reason== OtherClient)
+ {
+ Kopete::Utils::notifyConnectionLost(this, i18n("You have been disconnected"), i18n( "You have connected from another client or computer to the account '%1'" ).arg(d->id), i18n("Most proprietary Instant Messaging services do not allow you to connect from more than one location. Check that nobody is using your account without your permission. If you need a service that supports connection from various locations at the same time, use the Jabber protocol."));
+ }
+}
+
+Protocol *Account::protocol() const
+{
+ return d->protocol;
+}
+
+QString Account::accountId() const
+{
+ return d->id;
+}
+
+const QColor Account::color() const
+{
+ return d->color;
+}
+
+void Account::setColor( const QColor &color )
+{
+ d->color = color;
+ if ( d->color.isValid() )
+ d->configGroup->writeEntry( "Color", d->color );
+ else
+ d->configGroup->deleteEntry( "Color" );
+ emit colorChanged( color );
+}
+
+void Account::setPriority( uint priority )
+{
+ d->priority = priority;
+ d->configGroup->writeEntry( "Priority", d->priority );
+}
+
+uint Account::priority() const
+{
+ return d->priority;
+}
+
+
+QPixmap Account::accountIcon(const int size) const
+{
+ QString icon= d->customIcon.isEmpty() ? d->protocol->pluginIcon() : d->customIcon;
+
+ // FIXME: this code is duplicated with OnlineStatus, can we merge it somehow?
+ QPixmap base = KGlobal::instance()->iconLoader()->loadIcon(
+ icon, KIcon::Small, size );
+
+ if ( d->color.isValid() )
+ {
+ KIconEffect effect;
+ base = effect.apply( base, KIconEffect::Colorize, 1, d->color, 0);
+ }
+
+ if ( size > 0 && base.width() != size )
+ {
+ base = QPixmap( base.convertToImage().smoothScale( size, size ) );
+ }
+
+ return base;
+}
+
+KConfigGroup* Kopete::Account::configGroup() const
+{
+ return d->configGroup;
+}
+
+void Account::setAccountLabel( const QString &label )
+{
+ d->accountLabel = label;
+}
+
+QString Account::accountLabel() const
+{
+ if( d->accountLabel.isNull() )
+ return d->id;
+ return d->accountLabel;
+}
+
+void Account::setExcludeConnect( bool b )
+{
+ d->excludeconnect = b;
+ d->configGroup->writeEntry( "ExcludeConnect", d->excludeconnect );
+}
+
+bool Account::excludeConnect() const
+{
+ return d->excludeconnect;
+}
+
+void Account::registerContact( Contact *c )
+{
+ d->contacts.insert( c->contactId(), c );
+ QObject::connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
+ SLOT( contactDestroyed( Kopete::Contact * ) ) );
+}
+
+void Account::contactDestroyed( Contact *c )
+{
+ d->contacts.remove( c->contactId() );
+}
+
+
+const QDict<Contact>& Account::contacts()
+{
+ return d->contacts;
+}
+
+
+Kopete::MetaContact* Account::addContact( const QString &contactId, const QString &displayName , Group *group, AddMode mode )
+{
+
+ if ( contactId == d->myself->contactId() )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("You are not allowed to add yourself to the contact list. The addition of \"%1\" to account \"%2\" will not take place.").arg(contactId,accountId()), i18n("Error Creating Contact")
+ );
+ return false;
+ }
+
+ bool isTemporary = mode == Temporary;
+
+ Contact *c = d->contacts[ contactId ];
+
+ if(!group)
+ group=Group::topLevel();
+
+ if ( c && c->metaContact() )
+ {
+ if ( c->metaContact()->isTemporary() && !isTemporary )
+ {
+ kdDebug( 14010 ) << k_funcinfo << " You are trying to add an existing temporary contact. Just add it on the list" << endl;
+
+ c->metaContact()->setTemporary(false, group );
+ ContactList::self()->addMetaContact(c->metaContact());
+ }
+ else
+ {
+ // should we here add the contact to the parentContact if any?
+ kdDebug( 14010 ) << k_funcinfo << "Contact already exists" << endl;
+ }
+ return c->metaContact();
+ }
+
+ MetaContact *parentContact = new MetaContact();
+ if(!displayName.isEmpty())
+ parentContact->setDisplayName( displayName );
+
+ //Set it as a temporary contact if requested
+ if ( isTemporary )
+ parentContact->setTemporary( true );
+ else
+ parentContact->addToGroup( group );
+
+ if ( c )
+ {
+ c->setMetaContact( parentContact );
+ if ( mode == ChangeKABC )
+ {
+ kdDebug( 14010 ) << k_funcinfo << " changing KABC" << endl;
+ KABCPersistence::self()->write( parentContact );
+ }
+ }
+ else
+ {
+ if ( !createContact( contactId, parentContact ) )
+ {
+ delete parentContact;
+ return 0L;
+ }
+ }
+
+ ContactList::self()->addMetaContact( parentContact );
+ return parentContact;
+}
+
+bool Account::addContact(const QString &contactId , MetaContact *parent, AddMode mode )
+{
+ if ( contactId == myself()->contactId() )
+ {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("You are not allowed to add yourself to the contact list. The addition of \"%1\" to account \"%2\" will not take place.").arg(contactId,accountId()), i18n("Error Creating Contact")
+ );
+ return 0L;
+ }
+
+ bool isTemporary= parent->isTemporary();
+ Contact *c = d->contacts[ contactId ];
+ if ( c && c->metaContact() )
+ {
+ if ( c->metaContact()->isTemporary() && !isTemporary )
+ {
+ kdDebug( 14010 ) <<
+ "Account::addContact: You are trying to add an existing temporary contact. Just add it on the list" << endl;
+
+ //setMetaContact ill take care about the deletion of the old contact
+ c->setMetaContact(parent);
+ return true;
+ }
+ else
+ {
+ // should we here add the contact to the parentContact if any?
+ kdDebug( 14010 ) << "Account::addContact: Contact already exists" << endl;
+ }
+ return false; //(the contact is not in the correct metacontact, so false)
+ }
+
+ bool success = createContact(contactId, parent);
+
+ if ( success && mode == ChangeKABC )
+ {
+ kdDebug( 14010 ) << k_funcinfo << " changing KABC" << endl;
+ KABCPersistence::self()->write( parent );
+ }
+
+ return success;
+}
+
+KActionMenu * Account::actionMenu()
+{
+ //default implementation
+ KActionMenu *menu = new KActionMenu( accountId(), myself()->onlineStatus().iconFor( this ), this );
+ QString nick = myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString();
+
+ menu->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ),
+ nick.isNull() ? accountLabel() : i18n( "%2 <%1>" ).arg( accountLabel(), nick )
+ );
+
+ OnlineStatusManager::self()->createAccountStatusActions(this, menu);
+ menu->popupMenu()->insertSeparator();
+ menu->insert( new KAction ( i18n( "Properties" ), 0, this, SLOT( editAccount() ), menu, "actionAccountProperties" ) );
+
+ return menu;
+}
+
+
+bool Account::isConnected() const
+{
+ return myself() && myself()->isOnline();
+}
+
+bool Account::isAway() const
+{
+ return d->myself && ( d->myself->onlineStatus().status() == Kopete::OnlineStatus::Away );
+}
+
+Contact * Account::myself() const
+{
+ return d->myself;
+}
+
+void Account::setMyself( Contact *myself )
+{
+ bool wasConnected = isConnected();
+
+ if ( d->myself )
+ {
+ QObject::disconnect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+ QObject::disconnect( d->myself, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) );
+ }
+
+ d->myself = myself;
+
+// d->contacts.remove( myself->contactId() );
+
+ QObject::connect( d->myself, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+ QObject::connect( d->myself, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) );
+
+ if ( isConnected() != wasConnected )
+ emit isConnectedChanged();
+}
+
+void Account::slotOnlineStatusChanged( Contact * /* contact */,
+ const OnlineStatus &newStatus, const OnlineStatus &oldStatus )
+{
+ bool wasOffline = !oldStatus.isDefinitelyOnline();
+ bool isOffline = !newStatus.isDefinitelyOnline();
+
+ if ( wasOffline || newStatus.status() == OnlineStatus::Offline )
+ {
+ // Wait for five seconds until we treat status notifications for contacts
+ // as unrelated to our own status change.
+ // Five seconds may seem like a long time, but just after your own
+ // connection it's basically neglectible, and depending on your own
+ // contact list's size, the protocol you are using, your internet
+ // connection's speed and your computer's speed you *will* need it.
+ d->suppressStatusNotification = true;
+ d->suppressStatusTimer.start( 5000, true );
+ //the timer is also used to reset the d->connectionTry
+ }
+
+ if ( !isOffline )
+ {
+ d->restoreStatus = newStatus;
+ d->restoreMessage = myself()->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
+// kdDebug( 14010 ) << k_funcinfo << "account " << d->id << " restoreStatus " << d->restoreStatus.status() << " restoreMessage " << d->restoreMessage << endl;
+ }
+
+/* kdDebug(14010) << k_funcinfo << "account " << d->id << " changed status. was "
+ << Kopete::OnlineStatus::statusTypeToString(oldStatus.status()) << ", is "
+ << Kopete::OnlineStatus::statusTypeToString(newStatus.status()) << endl;*/
+ if ( wasOffline != isOffline )
+ emit isConnectedChanged();
+}
+
+void Account::setAllContactsStatus( const Kopete::OnlineStatus &status )
+{
+ d->suppressStatusNotification = true;
+ d->suppressStatusTimer.start( 5000, true );
+
+ for ( QDictIterator<Contact> it( d->contacts ); it.current(); ++it )
+ if ( it.current() != d->myself )
+ it.current()->setOnlineStatus( status );
+}
+
+void Account::slotContactPropertyChanged( Contact * /* contact */,
+ const QString &key, const QVariant &old, const QVariant &newVal )
+{
+ if ( key == QString::fromLatin1("awayMessage") && old != newVal && isConnected() )
+ {
+ d->restoreMessage = newVal.toString();
+// kdDebug( 14010 ) << k_funcinfo << "account " << d->id << " restoreMessage " << d->restoreMessage << endl;
+ }
+}
+
+void Account::slotStopSuppression()
+{
+ d->suppressStatusNotification = false;
+ if(isConnected())
+ d->connectionTry=0;
+}
+
+bool Account::suppressStatusNotification() const
+{
+ return d->suppressStatusNotification;
+}
+
+bool Account::removeAccount()
+{
+ //default implementation
+ return true;
+}
+
+
+BlackLister* Account::blackLister()
+{
+ return d->blackList;
+}
+
+void Account::block( const QString &contactId )
+{
+ d->blackList->addContact( contactId );
+}
+
+void Account::unblock( const QString &contactId )
+{
+ d->blackList->removeContact( contactId );
+}
+
+bool Account::isBlocked( const QString &contactId )
+{
+ return d->blackList->isBlocked( contactId );
+}
+
+void Account::editAccount(QWidget *parent)
+{
+ KDialogBase *editDialog = new KDialogBase( parent, "KopeteAccountConfig::editDialog", true,
+ i18n( "Edit Account" ), KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true );
+
+ KopeteEditAccountWidget *m_accountWidget = protocol()->createEditAccountWidget( this, editDialog );
+ if ( !m_accountWidget )
+ return;
+
+ // FIXME: Why the #### is EditAccountWidget not a QWidget?!? This sideways casting
+ // is braindead and error-prone. Looking at MSN the only reason I can see is
+ // because it allows direct subclassing of designer widgets. But what is
+ // wrong with embedding the designer widget in an empty QWidget instead?
+ // Also, if this REALLY has to be a pure class and not a widget, then the
+ // class should at least be renamed to EditAccountIface instead - Martijn
+ QWidget *w = dynamic_cast<QWidget *>( m_accountWidget );
+ if ( !w )
+ return;
+
+ editDialog->setMainWidget( w );
+ if ( editDialog->exec() == QDialog::Accepted )
+ {
+ if( m_accountWidget->validateData() )
+ m_accountWidget->apply();
+ }
+
+ editDialog->deleteLater();
+}
+
+void Account::setPluginData( Plugin* /*plugin*/, const QString &key, const QString &value )
+{
+ configGroup()->writeEntry(key,value);
+}
+
+QString Account::pluginData( Plugin* /*plugin*/, const QString &key ) const
+{
+ return configGroup()->readEntry(key);
+}
+
+void Account::setAway(bool away, const QString& reason)
+{
+ setOnlineStatus( OnlineStatusManager::self()->onlineStatus(protocol() , away ? OnlineStatusManager::Away : OnlineStatusManager::Online) , reason );
+}
+
+void Account::setCustomIcon( const QString & i)
+{
+ d->customIcon = i;
+ if(!i.isEmpty())
+ d->configGroup->writeEntry( "Icon", i );
+ else
+ d->configGroup->deleteEntry( "Icon" );
+ emit colorChanged( color() );
+}
+
+QString Account::customIcon() const
+{
+ return d->customIcon;
+}
+
+void Account::virtual_hook( uint /*id*/, void* /*data*/)
+{
+}
+
+
+
+}
+
+ //END namespace Kopete
+
+#include "kopeteaccount.moc"
diff --git a/kopete/libkopete/kopeteaccount.h b/kopete/libkopete/kopeteaccount.h
new file mode 100644
index 00000000..f3c2d338
--- /dev/null
+++ b/kopete/libkopete/kopeteaccount.h
@@ -0,0 +1,554 @@
+/*
+ kopeteaccount.h - Kopete Account
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+ Copyright (c) 2003-2004 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEACCOUNT_H
+#define KOPETEACCOUNT_H
+
+#include "kopeteonlinestatus.h"
+
+#include "kopete_export.h"
+
+#include <qobject.h>
+#include <qdict.h>
+
+class QDomNode;
+class KActionMenu;
+class KConfigGroup;
+
+namespace Kopete
+{
+class Contact;
+class Plugin;
+class Protocol;
+class MetaContact;
+class Group;
+class OnlineStatus;
+class BlackLister;
+
+/**
+ * The Kopete::Account class handles one account.
+ * Each protocol should subclass this class in its own custom accounts class.
+ * There are few pure virtual method that the protocol must implement. Examples are:
+ * \li \ref connect()
+ * \li \ref disconnect()
+ * \li \ref createContact()
+ *
+ * If your account requires a password, derive from @ref PasswordedAccount instead of this class.
+ *
+ * The accountId is an @em constant unique id, which represents the login.
+ * The @ref myself() contact is one of the most important contacts, which represents
+ * the user tied to this account. You must create this contact in the contructor of your
+ * account and pass it to @ref setMyself().
+ *
+ * All account data is saved to @ref KConfig. This includes the accountId, the autoconnect flag and
+ * the color. You can save more data using @ref configGroup()
+ *
+ * When you create a new account, you have to register it with the account manager by calling
+ * @ref AccountManager::registerAccount.
+ *
+ * @author Olivier Goffart <ogoffart@tiscalinet.be>
+ */
+class KOPETE_EXPORT Account : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS( AddMode )
+ Q_PROPERTY( QString accountId READ accountId )
+ Q_PROPERTY( bool excludeConnect READ excludeConnect WRITE setExcludeConnect )
+ Q_PROPERTY( QColor color READ color WRITE setColor )
+ Q_PROPERTY( QPixmap accountIcon READ accountIcon )
+ Q_PROPERTY( bool isConnected READ isConnected )
+ Q_PROPERTY( bool isAway READ isAway )
+ Q_PROPERTY( bool suppressStatusNotification READ suppressStatusNotification )
+ Q_PROPERTY( uint priority READ priority WRITE setPriority )
+
+public:
+ /**
+ * \brief Describes how the account was disconnected
+ *
+ * Manual means that the disconnection was done by the user and no reconnection
+ * will take place. Any other value will reconnect the account on disconnection.
+ * The case where the password is wrong will be handled differently.
+ * @see @ref disconnected
+ */
+ enum DisconnectReason {
+ OtherClient = -4, ///< connection went down because another client connected the same account
+ BadPassword = -3, ///< connection failed because password was incorrect
+ BadUserName = -2, ///< connection failed because user name was invalid / unknown
+ InvalidHost = -1, ///< connection failed because host is unreachable
+ Manual = 0, ///< the user disconnected normally
+ ConnectionReset = 1, ///< the connection was lost
+ Unknown = 99 ///< the reason for disconnection is unknown
+ };
+
+ /**
+ * @param parent the protocol for this account. The account is a child object of the
+ * protocol, so it will be automatically deleted when the protocol is.
+ * @param accountID the unique ID of this account.
+ * @param name the name of this QObject.
+ */
+ Account(Protocol *parent, const QString &accountID, const char *name=0L);
+ ~Account();
+
+ /**
+ * \return the Protocol for this account
+ */
+ Protocol *protocol() const ;
+
+ /**
+ * \return the unique ID of this account used as the login
+ */
+ QString accountId() const;
+
+ /**
+ * \return The label of this account, for the GUI
+ */
+ QString accountLabel() const;
+
+ /**
+ * \brief Get the priority of this account.
+ *
+ * Used for sorting and determining the preferred account to message a contact.
+ */
+ uint priority() const;
+
+ /**
+ * \brief Set the priority of this account.
+ *
+ * @note This method is called by the UI, and should not be called elsewhere.
+ */
+ void setPriority( uint priority );
+
+ /**
+ * \brief Set if the account should not log in automatically.
+ *
+ * This function can be used by the EditAccountPage. Kopete handles connection automatically.
+ * @sa @ref excludeConnect
+ */
+ void setExcludeConnect(bool);
+
+ /**
+ * \brief Get if the account should not log in.
+ *
+ * @return @c true if the account should not be connected when connectAll at startup, @c false otherwise.
+ */
+ bool excludeConnect() const;
+
+ /**
+ * \brief Get the color for this account.
+ *
+ * The color will be used to visually differentiate this account from other accounts on the
+ * same protocol.
+ *
+ * \return the user color for this account
+ */
+ const QColor color() const;
+
+ /**
+ * \brief Set the color for this account.
+ *
+ * This is called by Kopete's account config page; you don't have to set the color yourself.
+ *
+ * @sa @ref color()
+ */
+ void setColor( const QColor &color);
+
+ /**
+ * \brief Get the icon for this account.
+ *
+ * Generates an image of size @p size representing this account. The result is not cached.
+ *
+ * @param size the size of the icon. If the size is 0, the default size is used.
+ * @return the icon for this account, colored if needed
+ */
+ QPixmap accountIcon( const int size = 0 ) const;
+
+ /**
+ * \brief change the account icon.
+ * by default the icon of an account is the protocol one, but it may be overide it.
+ * Set QString::null to go back to the default (the protocol icon)
+ *
+ * this call will emit colorChanged()
+ */
+ void setCustomIcon( const QString& );
+
+ /**
+ * \brief return the icon base
+ * This is the custom account icon set with setIcon. if this icon is null, then the protocol icon is used
+ * don't use this funciton to get the icon that need to be displayed, use accountIcon
+ */
+ QString customIcon() const;
+
+
+
+
+ /**
+ * \brief Retrieve the 'myself' contact.
+ *
+ * \return a pointer to the Contact object for this account
+ *
+ * \see setMyself().
+ */
+ Contact * myself() const;
+
+ /**
+ * @brief Return the menu for this account
+ *
+ * You have to reimplement this method to return the custom action menu which will
+ * be shown in the statusbar. It is the caller's responsibility to ensure the menu is deleted.
+ *
+ * The default implementation provides a generic menu, with actions generated from the protocol's
+ * registered statuses, and an action to show the account's settings dialog.
+ *
+ * You should call the default implementation from your reimplementation, and add more actions
+ * you require to the resulting action menu.
+ *
+ * @see OnlineStatusManager::registerOnlineStatus
+ */
+ virtual KActionMenu* actionMenu() ;
+
+ /**
+ * @brief Retrieve the list of contacts for this account
+ *
+ * The list is guaranteed to contain only contacts for this account,
+ * so you can safely use static_cast to your own derived contact class
+ * if needed.
+ */
+ const QDict<Contact>& contacts();
+
+ /**
+ * Indicates whether or not we should suppress status notifications
+ * for contacts belonging to this account.
+ *
+ * This is used when we just connected or disconnected, and every contact has their initial
+ * status set.
+ *
+ * @return @c true if notifications should not be used, @c false otherwise
+ */
+ bool suppressStatusNotification() const;
+
+ /**
+ * \brief Describes what should be done when the contact is added to a metacontact
+ * @sa @ref addContact()
+ */
+ enum AddMode {
+ ChangeKABC = 0, ///< The KDE Address book may be updated
+ DontChangeKABC = 1, ///< The KDE Address book will not be changed
+ Temporary = 2 ///< The contact will not be added on the contactlist
+ };
+
+ /**
+ * \brief Create a contact (creating a new metacontact if necessary)
+ *
+ * If a contact for this account with ID @p contactId is not already on the contact list,
+ * a new contact with that ID is created, and added to a new metacontact.
+ *
+ * If @p mode is @c ChangeKABC, MetaContact::updateKABC will be called on the resulting metacontact.
+ * If @p mode is @c Temporary, MetaContact::setTemporary will be called on the resulting metacontact,
+ * and the metacontact will not be added to @p group.
+ * If @p mode is @c DontChangeKABC, no additional action is carried out.
+ *
+ * @param contactId the @ref Contact::contactId of the contact to create
+ * @param displayName the displayname (alias) of the new metacontact. Leave as QString::null if
+ * no alias is known, then by default, the nick will be taken as alias and tracked if changed.
+ * @param group the group to add the created metacontact to, or 0 for the top-level group.
+ * @param mode the mode used to add the contact. Use DontChangeKABC when deserializing.
+ * @return the new created metacontact or 0L if the operation failed
+ */
+ MetaContact* addContact( const QString &contactId, const QString &displayName = QString::null, Group *group = 0, AddMode mode = DontChangeKABC ) ;
+
+ /**
+ * @brief Create a new contact, adding it to an existing metacontact
+ *
+ * If a contact for this account with ID @p contactId is not already on the contact list,
+ * a new contact with that ID is created, and added to the metacontact @p parent.
+ *
+ * @param contactId the @ref Contact::contactId of the contact to create
+ * @param parent the parent metacontact (must not be 0)
+ * @param mode the mode used to add the contact. See addContact(const QString&,const QString&,Group*,AddMode) for details.
+ *
+ * @return @c true if creation of the contact succeeded or the contact was already in the list,
+ * @c false otherwise.
+ */
+ bool addContact( const QString &contactId, MetaContact *parent, AddMode mode = DontChangeKABC );
+
+ /**
+ * @brief Indicate whether the account is connected at all.
+ *
+ * This is a convenience method that calls @ref Contact::isOnline() on @ref myself().
+ * This function is safe to call if @ref setMyself() has not been called yet.
+ *
+ * @see @ref isConnectedChanged()
+ */
+ bool isConnected() const;
+
+ /**
+ * @brief Indicate whether the account is away.
+ *
+ * This is a convenience method that queries @ref Contact::onlineStatus() on @ref myself().
+ * This function is safe to call if @ref setMyself() has not been called yet.
+ */
+ bool isAway() const;
+
+ /**
+ * Return the @ref KConfigGroup used to write and read special properties
+ *
+ * "Protocol", "AccountId" , "Color", "AutoConnect", "Priority", "Enabled" , "Icon" are reserved keyword
+ * already in use in that group.
+ *
+ * for compatibility, try to not use key that start with a uppercase
+ */
+ KConfigGroup *configGroup() const;
+
+ /**
+ * @brief Remove the account from the server.
+ *
+ * Reimplement this if your protocol supports removing the accounts from the server.
+ * This function is called by @ref AccountManager::removeAccount typically when you remove the
+ * account on the account config page.
+ *
+ * You should add a confirmation message box before removing the account. The default
+ * implementation does nothing.
+ *
+ * @return @c false only if the user requested for the account to be deleted, and deleting the
+ * account failed. Returns @c true in all other cases.
+ */
+ virtual bool removeAccount();
+
+ /**
+ * \return a pointer to the blacklist of the account
+ */
+ BlackLister* blackLister();
+
+ /**
+ * \return @c true if the contact with ID @p contactId is in the blacklist, @c false otherwise.
+ */
+ virtual bool isBlocked( const QString &contactId );
+
+protected:
+ /**
+ * \brief Set the 'myself' contact.
+ *
+ * This contact must be defined for every account, because it holds the online status
+ * of the account. You must call this function in the constructor of your account.
+ *
+ * The myself contact can't be deleted as long as the account still exists. The myself
+ * contact is used as a member of every ChatSession involving this account. myself's
+ * contactId should be the accountID. The online status of the myself contact represents
+ * the account's status.
+ *
+ * The myself should have the @ref ContactList::myself() as parent metacontact
+ *
+ */
+ void setMyself( Contact *myself );
+
+ /**
+ * \brief Create a new contact in the specified metacontact
+ *
+ * You shouldn't ever call this method yourself. To add contacts, use @ref addContact().
+ *
+ * This method is called by @ref addContact(). In this method, you should create the
+ * new custom @ref Contact, using @p parentContact as the parent.
+ *
+ * If the metacontact is not temporary and the protocol supports it, you can add the
+ * contact to the server.
+ *
+ * @param contactId the ID of the contact to create
+ * @param parentContact the metacontact to add this contact to
+ * @return @c true if creating the contact succeeded, @c false on failure.
+ */
+ virtual bool createContact( const QString &contactId, MetaContact *parentContact ) =0;
+
+
+ /**
+ * \brief Sets the account label
+ *
+ * @param label The label to set
+ */
+ void setAccountLabel( const QString &label );
+
+protected slots:
+ /**
+ * \brief The service has been disconnected
+ *
+ * You have to call this method when you are disconnected. Depending on the value of
+ * @p reason, this function may attempt to reconnect to the server.
+ *
+ * - BadPassword will ask again for the password
+ * - OtherClient will show a message box
+ *
+ * @param reason the reason for the disconnection.
+ */
+ virtual void disconnected( Kopete::Account::DisconnectReason reason );
+
+ /**
+ * @brief Sets the online status of all contacts in this account to the same value
+ *
+ * Some protocols do not provide status-changed events for all contacts when an account
+ * becomes connected or disconnected. For such protocols, this function may be useful
+ * to set all contacts offline.
+ *
+ * Calls @ref Kopete::Contact::setOnlineStatus on all contacts of this account (except the
+ * @ref myself() contact), passing @p status as the status.
+ *
+ * @param status the status to set all contacts of this account except @ref myself() to.
+ */
+ void setAllContactsStatus( const Kopete::OnlineStatus &status );
+
+signals:
+ /**
+ * The color of the account has been changed
+ *
+ * also emited when the icon change
+ * @todo probably rename to accountIconChanged
+ */
+ void colorChanged( const QColor & );
+
+ /**
+ * Emitted when the account is deleted.
+ * @warning emitted in the Account destructor. It is not safe to call any functions on @p account.
+ */
+ void accountDestroyed( const Kopete::Account *account );
+
+ /**
+ * Emitted whenever @ref isConnected() changes.
+ */
+ void isConnectedChanged();
+
+private:
+ /**
+ * @internal
+ * Reads the configuration information of the account from KConfig.
+ */
+ void readConfig();
+
+public:
+ /**
+ * @internal
+ * Register a new Contact with the account. This should be called @em only from the
+ * @ref Contact constructor, not from anywhere else (not even a derived class).
+ */
+ void registerContact( Contact *c );
+
+public slots:
+ /**
+ * @brief Go online for this service.
+ *
+ * @param initialStatus is the status to connect with. If it is an invalid status for this
+ * account, the default online for the account should be used.
+ */
+ virtual void connect( const Kopete::OnlineStatus& initialStatus = OnlineStatus() ) = 0;
+
+ /**
+ * @brief Go offline for this service.
+ *
+ * If the service is connecting, you should abort the connection.
+ *
+ * You should call the @ref disconnected function from this function.
+ */
+ virtual void disconnect( ) = 0 ;
+
+public slots:
+ /**
+ * If @p away is @c true, set the account away with away message @p reason. Otherwise,
+ * set the account to not be away.
+ *
+ * @todo change ; make use of setOnlineStatus
+ */
+ virtual void setAway( bool away, const QString &reason = QString::null );
+
+ /**
+ * Reimplement this function to set the online status
+ * @param status is the new status
+ * @param reason is the away message to set.
+ * @note If needed, you need to connect. if the offline status is given, you should disconnect
+ */
+ virtual void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null ) = 0;
+
+ /**
+ * Display the edit account widget for the account
+ */
+ void editAccount( QWidget* parent = 0L );
+
+ /**
+ * Add a user to the blacklist. The default implementation calls
+ * blackList()->addContact( contactId )
+ *
+ * @param contactId the contact to be added to the blacklist
+ */
+ virtual void block( const QString &contactId );
+
+ /**
+ * Remove a user from the blacklist. The default implementation calls
+ * blackList()->removeContact( contactId )
+ *
+ * @param contactId the contact to be removed from the blacklist
+ */
+ virtual void unblock( const QString &contactId );
+
+private slots:
+ /**
+ * Restore online status and status message on reconnect.
+ */
+ virtual void reconnect();
+
+ /**
+ * Track the deletion of a Contact and clean up
+ */
+ void contactDestroyed( Kopete::Contact * );
+
+ /**
+ * The @ref myself() contact's online status changed.
+ */
+ void slotOnlineStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus );
+
+ /**
+ * The @ref myself() contact's property changed.
+ */
+ void slotContactPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & );
+
+ /**
+ * Stop the suppression of status notification (connected to a timer)
+ */
+ void slotStopSuppression();
+
+private:
+ class Private;
+ Private *d;
+
+protected:
+ virtual void virtual_hook( uint id, void* data);
+
+public:
+ /**
+ * @todo remove
+ * @deprecated use configGroup
+ */
+ void setPluginData( Plugin* /*plugin*/, const QString &key, const QString &value ) KDE_DEPRECATED;
+
+ /**
+ * @todo remove
+ * @deprecated use configGroup
+ */
+ QString pluginData( Plugin* /*plugin*/, const QString &key ) const KDE_DEPRECATED;
+};
+
+} //END namespace Kopete
+
+#endif
+
diff --git a/kopete/libkopete/kopeteaccountmanager.cpp b/kopete/libkopete/kopeteaccountmanager.cpp
new file mode 100644
index 00000000..b00f080e
--- /dev/null
+++ b/kopete/libkopete/kopeteaccountmanager.cpp
@@ -0,0 +1,440 @@
+/*
+ kopeteaccountmanager.cpp - Kopete Account Manager
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteaccountmanager.h"
+
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kplugininfo.h>
+
+#include "kopeteaccount.h"
+#include "kopeteaway.h"
+#include "kopeteprotocol.h"
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "kopetepluginmanager.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopetemetacontact.h"
+#include "kopetegroup.h"
+
+namespace Kopete {
+
+class AccountManager::Private
+{
+public:
+
+ class AccountPtrList : public QPtrList<Account>
+ {
+ protected:
+ int compareItems( AccountPtrList::Item a, AccountPtrList::Item b )
+ {
+ uint priority1 = static_cast<Account*>(a)->priority();
+ uint priority2 = static_cast<Account*>(b)->priority();
+
+ if( a==b ) //two account are equal only if they are equal :-)
+ return 0; // remember than an account can be only once on the list, but two account may have the same priority when loading
+ else if( priority1 > priority2 )
+ return 1;
+ else
+ return -1;
+ }
+ } accounts;
+
+};
+
+AccountManager * AccountManager::s_self = 0L;
+
+AccountManager * AccountManager::self()
+{
+ if ( !s_self )
+ s_self = new AccountManager;
+
+ return s_self;
+}
+
+
+AccountManager::AccountManager()
+: QObject( qApp, "KopeteAccountManager" )
+{
+ d = new Private;
+}
+
+
+AccountManager::~AccountManager()
+{
+ s_self = 0L;
+
+ delete d;
+}
+
+bool AccountManager::isAnyAccountConnected()
+{
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if(it.current()->isConnected())
+ return true;
+ }
+ return false;
+}
+
+void AccountManager::connectAll()
+{
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ if(!it.current()->excludeConnect())
+ it.current()->connect();
+}
+
+void AccountManager::setAvailableAll( const QString &awayReason )
+{
+ Away::setGlobalAway( false );
+ bool anyConnected = isAnyAccountConnected();
+
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if ( anyConnected )
+ {
+ if ( it.current()->isConnected() )
+ it.current()->setAway( false, awayReason );
+ }
+ else
+ if(!it.current()->excludeConnect())
+ it.current()->connect();
+ }
+}
+
+void AccountManager::disconnectAll()
+{
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ it.current()->disconnect();
+}
+
+void AccountManager::setAwayAll( const QString &awayReason, bool away )
+{
+ Away::setGlobalAway( true );
+ bool anyConnected = isAnyAccountConnected();
+
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ // FIXME: ICQ's invisible online should be set to invisible away
+ Contact *self = it.current()->myself();
+ bool isInvisible = self && self->onlineStatus().status() == OnlineStatus::Invisible;
+ if ( anyConnected )
+ {
+ if ( it.current()->isConnected() && !isInvisible )
+ it.current()->setAway( away, awayReason );
+ }
+ else
+ {
+ if ( !it.current()->excludeConnect() && !isInvisible )
+ it.current()->setAway( away, awayReason );
+ }
+ }
+}
+
+void AccountManager::setOnlineStatus( uint category , const QString& awayMessage, uint flags )
+{
+ OnlineStatusManager::Categories katgor=(OnlineStatusManager::Categories)category;
+ bool anyConnected = isAnyAccountConnected();
+
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ Account *account = it.current();
+ Kopete::OnlineStatus status = OnlineStatusManager::self()->onlineStatus(account->protocol() , katgor);
+ if ( anyConnected )
+ {
+ if ( account->isConnected() || ( (flags & ConnectIfOffline) && !account->excludeConnect() ) )
+ account->setOnlineStatus( status , awayMessage );
+ }
+ else
+ {
+ if ( !account->excludeConnect() )
+ account->setOnlineStatus( status , awayMessage );
+ }
+ }
+}
+
+
+QColor AccountManager::guessColor( Protocol *protocol ) const
+{
+ // In a perfect wold, we should check if the color is actually not used by the account.
+ // Anyway, this is not really required, It would be a difficult job for about nothing more.
+ // -- Olivier
+ int protocolCount = 0;
+
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if ( it.current()->protocol()->pluginId() == protocol->pluginId() )
+ protocolCount++;
+ }
+
+ // let's figure a color
+ QColor color;
+ switch ( protocolCount % 7 )
+ {
+ case 0:
+ color = QColor();
+ break;
+ case 1:
+ color = Qt::red;
+ break;
+ case 2:
+ color = Qt::green;
+ break;
+ case 3:
+ color = Qt::blue;
+ break;
+ case 4:
+ color = Qt::yellow;
+ break;
+ case 5:
+ color = Qt::magenta;
+ break;
+ case 6:
+ color = Qt::cyan;
+ break;
+ }
+
+ return color;
+}
+
+Account* AccountManager::registerAccount( Account *account )
+{
+ if( !account || d->accounts.contains( account ) )
+ return account;
+
+ if( account->accountId().isEmpty() )
+ {
+ account->deleteLater();
+ return 0L;
+ }
+
+ // If this account already exists, do nothing
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if ( ( account->protocol() == it.current()->protocol() ) && ( account->accountId() == it.current()->accountId() ) )
+ {
+ account->deleteLater();
+ return 0L;
+ }
+ }
+
+ d->accounts.append( account );
+ d->accounts.sort();
+
+ // Connect to the account's status changed signal
+ connect(account->myself(), SIGNAL(onlineStatusChanged(Kopete::Contact *,
+ const Kopete::OnlineStatus &, const Kopete::OnlineStatus &)),
+ this, SLOT(slotAccountOnlineStatusChanged(Kopete::Contact *,
+ const Kopete::OnlineStatus &, const Kopete::OnlineStatus &)));
+
+ connect(account, SIGNAL(accountDestroyed(const Kopete::Account *)) , this, SLOT( unregisterAccount(const Kopete::Account *) ));
+
+ emit accountRegistered( account );
+ return account;
+}
+
+void AccountManager::unregisterAccount( const Account *account )
+{
+ kdDebug( 14010 ) << k_funcinfo << "Unregistering account " << account->accountId() << endl;
+ d->accounts.remove( account );
+ emit accountUnregistered( account );
+}
+
+const QPtrList<Account>& AccountManager::accounts() const
+{
+ return d->accounts;
+}
+
+QDict<Account> AccountManager::accounts( const Protocol *protocol ) const
+{
+ QDict<Account> dict;
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if ( it.current()->protocol() == protocol && !it.current()->accountId().isNull() )
+ dict.insert( it.current()->accountId(), it.current() );
+ }
+
+ return dict;
+}
+
+Account * AccountManager::findAccount( const QString &protocolId, const QString &accountId )
+{
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ if ( it.current()->protocol()->pluginId() == protocolId && it.current()->accountId() == accountId )
+ return it.current();
+ }
+ return 0L;
+}
+
+void AccountManager::removeAccount( Account *account )
+{
+ if(!account->removeAccount())
+ return;
+
+ Protocol *protocol = account->protocol();
+
+
+ KConfigGroup *configgroup = account->configGroup();
+
+ // Clean up the contact list
+ QDictIterator<Kopete::Contact> it( account->contacts() );
+ for ( ; it.current(); ++it )
+ {
+ Contact* c = it.current();
+ MetaContact* mc = c->metaContact();
+ if ( mc == ContactList::self()->myself() )
+ continue;
+ mc->removeContact( c );
+ c->deleteLater();
+ if ( mc->contacts().count() == 0 ) //we can delete the metacontact
+ {
+ //get the first group and it's members
+ Group* group = mc->groups().first();
+ QPtrList<MetaContact> groupMembers = group->members();
+ ContactList::self()->removeMetaContact( mc );
+ if ( groupMembers.count() == 1 && groupMembers.findRef( mc ) != -1 )
+ ContactList::self()->removeGroup( group );
+ }
+ }
+
+ // Clean up the account list
+ d->accounts.remove( account );
+
+ // Clean up configuration
+ configgroup->deleteGroup();
+ configgroup->sync();
+
+ delete account;
+
+ if ( accounts( protocol ).isEmpty() )
+ {
+ // FIXME: pluginId() should return the internal name and not the class name, so
+ // we can get rid of this hack - Olivier/Martijn
+ QString protocolName = protocol->pluginId().remove( QString::fromLatin1( "Protocol" ) ).lower();
+
+ PluginManager::self()->setPluginEnabled( protocolName, false );
+ PluginManager::self()->unloadPlugin( protocolName );
+ }
+}
+
+void AccountManager::save()
+{
+ //kdDebug( 14010 ) << k_funcinfo << endl;
+ d->accounts.sort();
+
+ for ( QPtrListIterator<Account> it( d->accounts ); it.current(); ++it )
+ {
+ KConfigBase *config = it.current()->configGroup();
+
+ config->writeEntry( "Protocol", it.current()->protocol()->pluginId() );
+ config->writeEntry( "AccountId", it.current()->accountId() );
+ }
+
+ KGlobal::config()->sync();
+}
+
+void AccountManager::load()
+{
+ connect( PluginManager::self(), SIGNAL( pluginLoaded( Kopete::Plugin * ) ),
+ this, SLOT( slotPluginLoaded( Kopete::Plugin * ) ) );
+
+ // Iterate over all groups that start with "Account_" as those are accounts
+ // and load the required protocols if the account is enabled.
+ // Don't try to optimize duplicate calls out, the plugin queue is smart enough
+ // (and fast enough) to handle that without adding complexity here
+ KConfig *config = KGlobal::config();
+ QStringList accountGroups = config->groupList().grep( QRegExp( QString::fromLatin1( "^Account_" ) ) );
+ for ( QStringList::Iterator it = accountGroups.begin(); it != accountGroups.end(); ++it )
+ {
+ config->setGroup( *it );
+
+ QString protocol = config->readEntry( "Protocol" );
+ if ( protocol.endsWith( QString::fromLatin1( "Protocol" ) ) )
+ protocol = QString::fromLatin1( "kopete_" ) + protocol.lower().remove( QString::fromLatin1( "protocol" ) );
+
+ if ( config->readBoolEntry( "Enabled", true ) )
+ PluginManager::self()->loadPlugin( protocol, PluginManager::LoadAsync );
+ }
+}
+
+void AccountManager::slotPluginLoaded( Plugin *plugin )
+{
+ Protocol* protocol = dynamic_cast<Protocol*>( plugin );
+ if ( !protocol )
+ return;
+
+ // Iterate over all groups that start with "Account_" as those are accounts
+ // and parse them if they are from this protocol
+ KConfig *config = KGlobal::config();
+ QStringList accountGroups = config->groupList().grep( QRegExp( QString::fromLatin1( "^Account_" ) ) );
+ for ( QStringList::Iterator it = accountGroups.begin(); it != accountGroups.end(); ++it )
+ {
+ config->setGroup( *it );
+
+ if ( config->readEntry( "Protocol" ) != protocol->pluginId() )
+ continue;
+
+ // There's no GUI for this, but developers may want to disable an account.
+ if ( !config->readBoolEntry( "Enabled", true ) )
+ continue;
+
+ QString accountId = config->readEntry( "AccountId" );
+ if ( accountId.isEmpty() )
+ {
+ kdWarning( 14010 ) << k_funcinfo <<
+ "Not creating account for empty accountId." << endl;
+ continue;
+ }
+
+ kdDebug( 14010 ) << k_funcinfo <<
+ "Creating account for '" << accountId << "'" << endl;
+
+ Account *account = 0L;
+ account = registerAccount( protocol->createNewAccount( accountId ) );
+ if ( !account )
+ {
+ kdWarning( 14010 ) << k_funcinfo <<
+ "Failed to create account for '" << accountId << "'" << endl;
+ continue;
+ }
+ }
+}
+
+void AccountManager::slotAccountOnlineStatusChanged(Contact *c,
+ const OnlineStatus &oldStatus, const OnlineStatus &newStatus)
+{
+ Account *account = c->account();
+ if (!account)
+ return;
+
+ //kdDebug(14010) << k_funcinfo << endl;
+ emit accountOnlineStatusChanged(account, oldStatus, newStatus);
+}
+
+} //END namespace Kopete
+
+#include "kopeteaccountmanager.moc"
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/libkopete/kopeteaccountmanager.h b/kopete/libkopete/kopeteaccountmanager.h
new file mode 100644
index 00000000..ed0c939a
--- /dev/null
+++ b/kopete/libkopete/kopeteaccountmanager.h
@@ -0,0 +1,233 @@
+/*
+ kopeteaccountmanager.h - Kopete Account Manager
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __kopeteaccountmanager_h__
+#define __kopeteaccountmanager_h__
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qdict.h>
+
+#include "kopete_export.h"
+
+
+namespace Kopete {
+
+class Account;
+class Plugin;
+class Protocol;
+class Contact;
+class OnlineStatus;
+
+/**
+ * AccountManager manages all defined accounts in Kopete. You can
+ * query them and globally set them all online or offline from here.
+ *
+ * AccountManager is a singleton, you may uses it with @ref AccountManager::self()
+ *
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart@ tiscalinet.be>
+ */
+class KOPETE_EXPORT AccountManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * \brief Retrieve the instance of AccountManager.
+ *
+ * The account manager is a singleton class of which only a single
+ * instance will exist. If no manager exists yet this function will
+ * create one for you.
+ *
+ * \return the instance of the AccountManager
+ */
+ static AccountManager* self();
+
+ ~AccountManager();
+
+ /**
+ * \brief Retrieve the list of accounts
+ * \return a list of all the accounts
+ */
+ const QPtrList<Account> & accounts() const;
+
+ /**
+ * \brief Retrieve a QDict of accounts for the given protocol
+ *
+ * The list is guaranteed to contain only accounts for the specified
+ * protocol
+ * \param p is the Protocol object you want accounts for
+ */
+ QDict<Account> accounts( const Protocol *p ) const;
+
+ /**
+ * \brief Return the account asked
+ * \param protocolId is the ID for the protocol
+ * \param accountId is the ID for the account you want
+ * \return the Account object found or NULL if no account was found
+ */
+ Account* findAccount( const QString &protocolId, const QString &accountId );
+
+ /**
+ * \brief Delete the account and clean the config data
+ *
+ * This is praticaly called by the account config page when you remove the account.
+ */
+ void removeAccount( Account *account );
+
+ /**
+ * \brief Guess the color for a new account
+ *
+ * Guesses a color for the next account of a given protocol based on the already registered colors
+ * \return the color guessed for the account
+ */
+ QColor guessColor( Protocol *protocol ) const ;
+
+ /**
+ * @brief Register the account.
+ *
+ * This adds the account in the manager's account list.
+ * It will check no accounts already exist with the same ID, if any, the account is deleted. and not added
+ *
+ * @return @p account, or 0L if the account was deleted because id collision
+ */
+ Account *registerAccount( Account *account );
+
+
+ /**
+ * Flag to be used in setOnlineStatus
+ *
+ * @c ConnectIfOffline : if set, this will connect offlines account with the status.
+ */
+ enum SetOnlineStatusFlag { ConnectIfOffline=0x01 };
+
+
+public slots:
+ /**
+ * \brief Connect all accounts at once.
+ *
+ * Connect every account if the flag excludeConnect is false
+ * @see @ref Account::excludeConnect()
+ */
+ void connectAll();
+
+ /**
+ * \brief Disconnect all accounts at once.
+ */
+ void disconnectAll();
+
+ /**
+ * @brief Set all accounts a status in the specified category
+ *
+ * Account that are offline will not be connected, unless the ConnectIfOffline flag is set.
+ *
+ * @param category is one of the Kopete::OnlineStatusManager::Categories
+ * @param awayMessage is the new away message
+ * @param flags is a bitmask of SetOnlineStatusFlag
+ */
+ void setOnlineStatus( /*Kopete::OnlineStatusManager::Categories*/ uint category,
+ const QString& awayMessage = QString::null, uint flags=0);
+
+ /**
+ * \brief Set all accounts to away at once.
+ *
+ * All account that are connected, but not invisible will be set to away
+ * @see Account::setAway
+ * @param awayReason is the away message that will be set.
+ * @param away decides whether the message is away/non-away
+ */
+ void setAwayAll( const QString &awayReason = QString::null, bool away=true );
+
+ /**
+ * \brief Connect or make available every account.
+ * Make all accounts Available, by setting status, and connecting if necessary.
+ * Accounts are connected based on their excludeConnect() setting.
+ * Accounts which are already connected are controlled regardless of their excludeConnect() setting.
+ * This is a slot, so you can connect directly to it from e.g. a KAction.
+ * @param awayReason is the away(status) message that will be set.
+ */
+ void setAvailableAll( const QString &awayReason = QString::null );
+
+ /**
+ * \internal
+ * Save the account data to KConfig
+ */
+ void save();
+
+ /**
+ * \internal
+ * Load the account data from KConfig
+ */
+ void load();
+
+
+
+signals:
+ /**
+ * \brief Signals when an account is ready for use
+ */
+ void accountRegistered( Kopete::Account *account );
+
+ /**
+ * \brief Signals when an account has been unregistered
+ *
+ * At this state, we are already in the Account destructor.
+ */
+ void accountUnregistered( const Kopete::Account *account );
+
+ /**
+ * \brief An account has changed its onlinestatus
+ * Technically this monitors Account::myself() onlinestatus changes
+ * \param account Account which changed its onlinestatus
+ * \param oldStatus The online status before the change
+ * \param newStatus The new online status
+ */
+ void accountOnlineStatusChanged(Kopete::Account *account,
+ const Kopete::OnlineStatus &oldStatus, const Kopete::OnlineStatus &newStatus);
+
+private:
+ /**
+ * Private constructor, because we're a singleton
+ */
+ AccountManager();
+
+private slots:
+ void slotPluginLoaded( Kopete::Plugin *plugin );
+ void slotAccountOnlineStatusChanged(Kopete::Contact *c,
+ const Kopete::OnlineStatus &oldStatus, const Kopete::OnlineStatus &newStatus);
+
+ /**
+ * \internal
+ * Unregister the account.
+ */
+ void unregisterAccount( const Kopete::Account *account );
+
+private:
+ bool isAnyAccountConnected();
+ static AccountManager *s_self;
+ class Private;
+ Private *d;
+};
+
+} //END namespace Kopete
+
+
+#endif
+
+
diff --git a/kopete/libkopete/kopeteaway.cpp b/kopete/libkopete/kopeteaway.cpp
new file mode 100644
index 00000000..fa500e0c
--- /dev/null
+++ b/kopete/libkopete/kopeteaway.cpp
@@ -0,0 +1,525 @@
+/*
+ kopeteaway.cpp - Kopete Away
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2003 Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kopeteaway.h"
+
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopetecontact.h"
+#include "kopeteprefs.h"
+
+#include <kconfig.h>
+#include <qtimer.h>
+#include <kapplication.h>
+#include <dcopref.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <ksettings/dispatcher.h>
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xresource.h>
+// The following include is to make --enable-final work
+#include <X11/Xutil.h>
+
+#ifdef HAVE_XSCREENSAVER
+#define HasScreenSaver
+#include <X11/extensions/scrnsaver.h>
+#endif
+#endif // Q_WS_X11
+
+// As this is an untested X extension we better leave it off
+#undef HAVE_XIDLE
+#undef HasXidle
+
+
+struct KopeteAwayPrivate
+{
+ QString awayMessage;
+ QString autoAwayMessage;
+ bool useAutoAwayMessage;
+ bool globalAway;
+ QStringList awayMessageList;
+ QTime idleTime;
+ QTimer *timer;
+ bool autoaway;
+ bool goAvailable;
+ int awayTimeout;
+ bool useAutoAway;
+ QPtrList<Kopete::Account> autoAwayAccounts;
+
+ int mouse_x;
+ int mouse_y;
+ unsigned int mouse_mask;
+#ifdef Q_WS_X11
+ Window root; /* root window the pointer is on */
+ Screen* screen; /* screen the pointer is on */
+
+ Time xIdleTime;
+#endif
+ bool useXidle;
+ bool useMit;
+};
+
+Kopete::Away *Kopete::Away::instance = 0L;
+
+Kopete::Away::Away() : QObject( kapp , "Kopete::Away")
+{
+ int dummy = 0;
+ dummy = dummy; // shut up
+
+ d = new KopeteAwayPrivate;
+
+ // Set up the away messages
+ d->awayMessage = QString::null;
+ d->autoAwayMessage = QString::null;
+ d->useAutoAwayMessage = false;
+ d->globalAway = false;
+ d->autoaway = false;
+ d->useAutoAway = true;
+
+ // Empty the list
+ d->awayMessageList.clear();
+
+ // set the XAutoLock info
+#ifdef Q_WS_X11
+ Display *dsp = qt_xdisplay();
+#endif
+ d->mouse_x = d->mouse_y=0;
+ d->mouse_mask = 0;
+#ifdef Q_WS_X11
+ d->root = DefaultRootWindow (dsp);
+ d->screen = ScreenOfDisplay (dsp, DefaultScreen (dsp));
+#endif
+ d->useXidle = false;
+ d->useMit = false;
+#ifdef HasXidle
+ d->useXidle = XidleQueryExtension(qt_xdisplay(), &dummy, &dummy);
+#endif
+#ifdef HasScreenSaver
+ if(!d->useXidle)
+ d->useMit = XScreenSaverQueryExtension(qt_xdisplay(), &dummy, &dummy);
+#endif
+#ifdef Q_WS_X11
+ d->xIdleTime = 0;
+#endif
+ kdDebug(14010) << k_funcinfo << "Idle detection methods:" << endl;
+ kdDebug(14010) << k_funcinfo << "\tKScreensaverIface::isBlanked()" << endl;
+#ifdef Q_WS_X11
+ kdDebug(14010) << k_funcinfo << "\tX11 XQueryPointer()" << endl;
+#endif
+ if (d->useXidle)
+ {
+ kdDebug(14010) << k_funcinfo << "\tX11 Xidle extension" << endl;
+ }
+ if (d->useMit)
+ {
+ kdDebug(14010) << k_funcinfo << "\tX11 MIT Screensaver extension" << endl;
+ }
+
+
+ load();
+ KSettings::Dispatcher::self()->registerInstance( KGlobal::instance(), this, SLOT( load() ) );
+ // Set up the config object
+ KConfig *config = KGlobal::config();
+ /* Load the saved away messages */
+ config->setGroup("Away Messages");
+
+ // Away Messages
+ if(config->hasKey("Messages"))
+ {
+ d->awayMessageList = config->readListEntry("Messages");
+ }
+ else if(config->hasKey("Titles")) // Old config format
+ {
+ QStringList titles = config->readListEntry("Titles"); // Get the titles
+ for(QStringList::iterator i = titles.begin(); i != titles.end(); ++i)
+ {
+ d->awayMessageList.append( config->readEntry(*i) ); // And add it to the list
+ }
+
+ /* Save this list to disk */
+ save();
+ }
+ else
+ {
+ d->awayMessageList.append( i18n( "Sorry, I am busy right now" ) );
+ d->awayMessageList.append( i18n( "I am gone right now, but I will be back later" ) );
+
+ /* Save this list to disk */
+ save();
+ }
+
+ // Auto away message
+ if(config->hasKey("AutoAwayMessage"))
+ {
+ d->autoAwayMessage = config->readEntry("AutoAwayMessage");
+ }
+ else
+ {
+ d->autoAwayMessage = i18n( "I am gone right now, but I will be back later" );
+
+ // Save the default auto away message to disk
+ save();
+ }
+
+ // init the timer
+ d->timer = new QTimer(this, "AwayTimer");
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(slotTimerTimeout()));
+ d->timer->start(4000);
+
+ //init the time and other
+ setActive();
+}
+
+Kopete::Away::~Away()
+{
+ if(this == instance)
+ instance = 0L;
+ delete d;
+}
+
+QString Kopete::Away::message()
+{
+ return getInstance()->d->awayMessage;
+}
+
+QString Kopete::Away::autoAwayMessage()
+{
+ return getInstance()->d->autoAwayMessage;
+}
+
+void Kopete::Away::setGlobalAwayMessage(const QString &message)
+{
+ if( !message.isEmpty() )
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "Setting global away message: " << message << endl;
+ d->awayMessage = message;
+ }
+}
+
+void Kopete::Away::setAutoAwayMessage(const QString &message)
+{
+ if( !message.isEmpty() )
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "Setting auto away message: " << message << endl;
+ d->autoAwayMessage = message;
+
+ // Save the new auto away message to disk
+ save();
+ }
+}
+
+Kopete::Away *Kopete::Away::getInstance()
+{
+ if (!instance)
+ instance = new Kopete::Away;
+ return instance;
+}
+
+bool Kopete::Away::globalAway()
+{
+ return getInstance()->d->globalAway;
+}
+
+void Kopete::Away::setGlobalAway(bool status)
+{
+ getInstance()->d->globalAway = status;
+}
+
+void Kopete::Away::save()
+{
+ KConfig *config = KGlobal::config();
+ /* Set the away message settings in the Away Messages config group */
+ config->setGroup("Away Messages");
+ config->writeEntry("Messages", d->awayMessageList);
+ config->writeEntry("AutoAwayMessage", d->autoAwayMessage);
+ config->sync();
+
+ emit( messagesChanged() );
+}
+
+void Kopete::Away::load()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("AutoAway");
+ d->awayTimeout=config->readNumEntry("Timeout", 600);
+ d->goAvailable=config->readBoolEntry("GoAvailable", true);
+ d->useAutoAway=config->readBoolEntry("UseAutoAway", true);
+ d->useAutoAwayMessage=config->readBoolEntry("UseAutoAwayMessage", false);
+}
+
+QStringList Kopete::Away::getMessages()
+{
+ return d->awayMessageList;
+}
+
+QString Kopete::Away::getMessage( uint messageNumber )
+{
+ QStringList::iterator it = d->awayMessageList.at( messageNumber );
+ if( it != d->awayMessageList.end() )
+ {
+ QString str = *it;
+ d->awayMessageList.prepend( str );
+ d->awayMessageList.remove( it );
+ save();
+ return str;
+ }
+ else
+ {
+ return QString::null;
+ }
+}
+
+void Kopete::Away::addMessage(const QString &message)
+{
+ d->awayMessageList.prepend( message );
+ if( (int)d->awayMessageList.count() > KopetePrefs::prefs()->rememberedMessages() )
+ d->awayMessageList.pop_back();
+ save();
+}
+
+long int Kopete::Away::idleTime()
+{
+ //FIXME: the time is reset to zero if more than 24 hours are elapsed
+ // we can imagine someone who leave his PC for several weeks
+ return (d->idleTime.elapsed() / 1000);
+}
+
+void Kopete::Away::slotTimerTimeout()
+{
+ // Time to check whether we're active or autoaway. We basically have two
+ // bits of info to go on - KDE's screensaver status
+ // (KScreenSaverIface::isBlanked()) and the X11 activity detection.
+ //
+ // Note that isBlanked() is a slight of a misnomer. It returns true if we're:
+ // - using a non-locking screensaver, which is running, or
+ // - using a locking screensaver which is still locked, regardless of
+ // whether the user is trying to unlock it right now
+ // Either way, it's only worth checking for activity if the screensaver
+ // isn't blanked/locked, because activity while blanked is impossible and
+ // activity while locked never matters (if there is any, it's probably just
+ // the cleaner wiping the keyboard :).
+
+
+ /* we should be able to respond to KDesktop queries to avoid a deadlock, so we allow the event loop to be called */
+ static bool rentrency_protection=false;
+ if(rentrency_protection)
+ return;
+ rentrency_protection=true;
+ DCOPRef screenSaver("kdesktop", "KScreensaverIface");
+ DCOPReply isBlanked = screenSaver.callExt("isBlanked" , DCOPRef::UseEventLoop, 10);
+ rentrency_protection=false;
+ if(!instance) //this may have been deleted in the event loop
+ return;
+ if (!(isBlanked.isValid() && isBlanked.type == "bool" && ((bool)isBlanked)))
+ {
+ // DCOP failed, or returned something odd, or the screensaver is
+ // inactive, so check for activity the X11 way. It's only worth
+ // checking for autoaway if there's no activity, and because
+ // Screensaver blanking/locking implies autoAway activation (see
+ // KopeteIface::KopeteIface()), only worth checking autoAway when the
+ // screensaver isn't running.
+ if (isActivity())
+ {
+ setActive();
+ }
+ else if (!d->autoaway && d->useAutoAway && idleTime() > d->awayTimeout)
+ {
+ setAutoAway();
+ }
+ }
+}
+
+bool Kopete::Away::isActivity()
+{
+ // Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
+ //
+ // KDE screensaver engine
+ //
+ // This module is a heavily modified xautolock.
+ // In fact as of KDE 2.0 this code is practically unrecognisable as xautolock.
+
+ bool activity = false;
+
+#ifdef Q_WS_X11
+ Display *dsp = qt_xdisplay();
+ Window dummy_w;
+ int dummy_c;
+ unsigned int mask; /* modifier mask */
+ int root_x;
+ int root_y;
+
+ /*
+ * Find out whether the pointer has moved. Using XQueryPointer for this
+ * is gross, but it also is the only way never to mess up propagation
+ * of pointer events.
+ *
+ * Remark : Unlike XNextEvent(), XPending () doesn't notice if the
+ * connection to the server is lost. For this reason, earlier
+ * versions of xautolock periodically called XNoOp (). But
+ * why not let XQueryPointer () do the job for us, since
+ * we now call that periodically anyway?
+ */
+ if (!XQueryPointer (dsp, d->root, &(d->root), &dummy_w, &root_x, &root_y,
+ &dummy_c, &dummy_c, &mask))
+ {
+ /*
+ * Pointer has moved to another screen, so let's find out which one.
+ */
+ for (int i = 0; i < ScreenCount(dsp); i++)
+ {
+ if (d->root == RootWindow(dsp, i))
+ {
+ d->screen = ScreenOfDisplay (dsp, i);
+ break;
+ }
+ }
+ }
+
+ // =================================================================================
+
+ Time xIdleTime = 0; // millisecs since last input event
+
+ #ifdef HasXidle
+ if (d->useXidle)
+ {
+ XGetIdleTime(dsp, &xIdleTime);
+ }
+ else
+ #endif /* HasXIdle */
+
+ {
+ #ifdef HasScreenSaver
+ if(d->useMit)
+ {
+ static XScreenSaverInfo* mitInfo = 0;
+ if (!mitInfo) mitInfo = XScreenSaverAllocInfo();
+ XScreenSaverQueryInfo (dsp, d->root, mitInfo);
+ xIdleTime = mitInfo->idle;
+ }
+ #endif /* HasScreenSaver */
+ }
+
+ // =================================================================================
+
+ // Only check idle time if we have some way of measuring it, otherwise if
+ // we've neither Mit nor Xidle it'll still be zero and we'll always appear active.
+ // FIXME: what problem does the 2000ms fudge solve?
+ if (root_x != d->mouse_x || root_y != d->mouse_y || mask != d->mouse_mask
+ || ((d->useXidle || d->useMit) && xIdleTime < d->xIdleTime + 2000))
+ {
+ // -1 => just gone autoaway, ignore apparent activity this time round
+ // anything else => genuine activity
+ // See setAutoAway().
+ if (d->mouse_x != -1)
+ {
+ activity = true;
+ }
+ d->mouse_x = root_x;
+ d->mouse_y = root_y;
+ d->mouse_mask = mask;
+ d->xIdleTime = xIdleTime;
+ }
+#endif // Q_WS_X11
+ // =================================================================================
+
+ return activity;
+}
+
+void Kopete::Away::setActive()
+{
+// kdDebug(14010) << k_funcinfo << "Found activity on desktop, resetting away timer" << endl;
+ d->idleTime.start();
+
+ if(d->autoaway)
+ {
+ d->autoaway = false;
+ emit activity();
+ if (d->goAvailable)
+ {
+ d->autoAwayAccounts.setAutoDelete(false);
+ for(Kopete::Account *i=d->autoAwayAccounts.first() ; i; i=d->autoAwayAccounts.current() )
+ {
+ if(i->isConnected() && i->isAway())
+ {
+ i->setOnlineStatus( Kopete::OnlineStatusManager::self()->onlineStatus( i->protocol() ,
+ Kopete::OnlineStatusManager::Online ) );
+ }
+
+ // remove() makes the next entry in the list the current one,
+ // that's why we use current() above
+ d->autoAwayAccounts.remove();
+ }
+ }
+ }
+}
+
+void Kopete::Away::setAutoAway()
+{
+ // A value of -1 in mouse_x indicates to checkActivity() that next time it
+ // fires it should ignore any apparent idle/mouse/keyboard changes.
+ // I think the point of this is that if you manually start the screensaver
+ // then there'll unavoidably be some residual mouse/keyboard activity
+ // that should be ignored.
+ d->mouse_x = -1;
+
+// kdDebug(14010) << k_funcinfo << "Going AutoAway!" << endl;
+ d->autoaway = true;
+
+ // Set all accounts that are not away already to away.
+ // We remember them so later we only set the accounts to
+ // available that we set to away (and not the user).
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *i=accounts.first() ; i; i=accounts.next() )
+ {
+ if(i->myself()->onlineStatus().status() == Kopete::OnlineStatus::Online)
+ {
+ d->autoAwayAccounts.append(i);
+
+ if(d->useAutoAwayMessage)
+ {
+ // Display a specific away message
+ i->setOnlineStatus( Kopete::OnlineStatusManager::self()->onlineStatus( i->protocol() ,
+ Kopete::OnlineStatusManager::Idle ) ,
+ getInstance()->d->autoAwayMessage);
+ }
+ else
+ {
+ // Display the last away message used
+ i->setOnlineStatus( Kopete::OnlineStatusManager::self()->onlineStatus( i->protocol() ,
+ Kopete::OnlineStatusManager::Idle ) ,
+ getInstance()->d->awayMessage);
+ }
+ }
+ }
+}
+
+#include "kopeteaway.moc"
+// vim: set et ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteaway.h b/kopete/libkopete/kopeteaway.h
new file mode 100644
index 00000000..544dff75
--- /dev/null
+++ b/kopete/libkopete/kopeteaway.h
@@ -0,0 +1,217 @@
+/*
+ kopeteaway.h - Kopete Away
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2003 Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEAWAY_HI
+#define KOPETEAWAY_HI
+
+#include <qstring.h>
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include "kopeteawaydialog.h"
+#include "kopete_export.h"
+
+class QStringList;
+
+struct KopeteAwayPrivate;
+
+class KopeteGlobalAwayDialog;
+class KopeteAwayDialog;
+
+namespace Kopete
+{
+
+/**
+ * @class Kopete::Away kopeteaway.h
+ *
+ * Kopete::Away is a singleton class that manages away messages
+ * for Kopete. It stores a global away message, as well as
+ * a list of user defined away messages.
+ * This class is used by KopeteAwayDialog, which gets it's
+ * list of user-defined away messages from this. Protocol
+ * plugins' individual away dialogs should also get away
+ * messages from this object.
+ *
+ * It also handle global Idle Time, and all auto away stuff
+ *
+ * @author Hendrik vom Lehn <hvl@linux-4-ever.de>
+ * @author Chris TenHarmsel <tenharmsel@users.sourceforge.net>
+ * @author Olivier Goffart <ogoffart @ kde.org>
+
+ */
+class KOPETE_EXPORT Away : public QObject
+{
+Q_OBJECT
+
+friend class ::KopeteAwayDialog;
+
+public:
+
+ /**
+ * @brief Method to get the single instance of Kopete::Away
+ * @return Kopete::Away instance pointer
+ */
+ static Away *getInstance();
+
+ /**
+ * @brief Gets the current global away message
+ * @return The global away message
+ */
+ static QString message();
+
+ /**
+ * @brief Gets the current global auto away message
+ * @return The global auto away message
+ */
+ static QString autoAwayMessage();
+
+ /**
+ * This method sets the global away message,
+ * it does not set you away, just sets the message.
+ * @brief Sets the global away message
+ * @param message The message you want to set
+ */
+ void setGlobalAwayMessage(const QString &message);
+
+ /**
+ * This method sets the global auto away message,
+ * it does not set you away, just sets the message.
+ * @brief Sets the global auto away message
+ * @param message The message you want to set
+ */
+ void setAutoAwayMessage(const QString &message);
+
+ /**
+ * @brief Sets global away for all protocols
+ */
+ static void setGlobalAway(bool status);
+
+ /**
+ * @brief Indicates global away status
+ * @return Bool indicating global away status
+ */
+ static bool globalAway();
+
+ /**
+ * @brief Function to get the titles of user defined away messages
+ * @return List of away message titles
+ *
+ * This function can be used to retrieve a QStringList of the away message titles,
+ * these titles can be passed to getMessage(QString title) to retrieve the
+ * corresponding message.
+ */
+ QStringList getMessages();
+
+ /**
+ * @brief Function to get an away message
+ * @return The away message corresponding to the title
+ * @param messageNumber Number of the away message to retrieve
+ *
+ * This function retrieves the away message that corresponds to the ringbuffer index
+ * passed in.
+ */
+ QString getMessage( uint messageNumber );
+
+ /**
+ * @brief Adds an away message to the ringbuffer
+ * @param message The away message
+ *
+ * This function will add an away message to the ringbuffer of user defined
+ * away messages.
+ */
+ void addMessage(const QString &message);
+
+ /**
+ * time in seconds the user has been idle
+ */
+ long int idleTime();
+
+private:
+ Away();
+ ~Away();
+
+ /**
+ * @brief Saves the away messages to disk
+ *
+ * This function will save the current list of away messages to the disk
+ * using KConfig. It is called automatically.
+ */
+ void save();
+
+ /**
+ * @brief Check for activity using X11 methods
+ * @return true if activity was detected, otherwise false
+ *
+ * Attempt to detect activity using a variety of X11 methods.
+ */
+ bool isActivity();
+
+ //Away( const Away &rhs );
+ //Away &operator=( const Away &rhs );
+ static Away *instance;
+ KopeteAwayPrivate *d;
+
+private slots:
+ void slotTimerTimeout();
+ void load();
+
+public slots:
+ /**
+ * @brief Mark the user active
+ *
+ * Plugins can mark the user active if they discover activity by another way than the mouse or the keyboard
+ * (example, the motion auto away plugin)
+ * this will reset the @ref idleTime to 0, and set all protocols to available (online) if the state was
+ * set automatically to away because of idleness, and if they was previously online
+ */
+ void setActive();
+
+ /**
+ * Use this method if you want to go in the autoaway mode.
+ * This will go autoaway even if the idle time is not yet reached. (and even if the user
+ * did not selected to go autoaway automaticaly)
+ * But that will go unaway again when activity will be detected
+ */
+ void setAutoAway();
+
+signals:
+ /**
+ * @brief Activity was detected
+ *
+ * this signal is emit when activity has been discover after being autoAway.
+ */
+ void activity();
+
+ /**
+ * @brief Default messages were changed
+ */
+ void messagesChanged();
+};
+
+}
+
+#endif
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteawayaction.cpp b/kopete/libkopete/kopeteawayaction.cpp
new file mode 100644
index 00000000..84622c7e
--- /dev/null
+++ b/kopete/libkopete/kopeteawayaction.cpp
@@ -0,0 +1,134 @@
+/*
+ kopeteaway.cpp - Kopete Away Action
+
+ Copyright (c) 2003 Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <klocale.h>
+#include <kdeversion.h>
+#include <kinputdialog.h>
+#include <kstringhandler.h>
+
+#include "kopeteawayaction.h"
+#include "kopeteaway.h"
+#include "kopeteonlinestatus.h"
+
+
+namespace Kopete {
+
+class AwayAction::Private
+{
+public:
+ Private(const OnlineStatus& s) : reasonCount(0) , status(s) {};
+ int reasonCount;
+ OnlineStatus status;
+};
+
+
+AwayAction::AwayAction(const QString &text, const QIconSet &pix, const KShortcut &cut,
+ const QObject *receiver, const char *slot, QObject *parent, const char *name )
+ : KSelectAction(text, pix, cut, parent, name ) , d(new Private( OnlineStatus() ) )
+{
+ QObject::connect( Kopete::Away::getInstance(), SIGNAL( messagesChanged() ),
+ this, SLOT( slotAwayChanged() ) );
+
+ QObject::connect( this, SIGNAL( awayMessageSelected( const QString & ) ),
+ receiver, slot );
+
+ QObject::connect( this, SIGNAL( activated( int ) ),
+ this, SLOT( slotSelectAway( int ) ) );
+
+ slotAwayChanged();
+}
+
+AwayAction::AwayAction( const OnlineStatus& status, const QString &text, const QIconSet &pix, const KShortcut &cut,
+ const QObject *receiver, const char *slot, QObject *parent, const char *name )
+ : KSelectAction(text, pix, cut, parent, name ) , d(new Private( status ) )
+{
+ QObject::connect( Kopete::Away::getInstance(), SIGNAL( messagesChanged() ),
+ this, SLOT( slotAwayChanged() ) );
+
+ QObject::connect( this, SIGNAL( awayMessageSelected( const Kopete::OnlineStatus &, const QString & ) ),
+ receiver, slot );
+
+ QObject::connect( this, SIGNAL( activated( int ) ),
+ this, SLOT( slotSelectAway( int ) ) );
+
+ slotAwayChanged();
+}
+
+AwayAction::~AwayAction()
+{
+ delete d;
+}
+
+void AwayAction::slotAwayChanged()
+{
+ QStringList awayMessages = Kopete::Away::getInstance()->getMessages();
+ for( QStringList::iterator it = awayMessages.begin(); it != awayMessages.end(); ++it )
+ {
+ (*it) = KStringHandler::rsqueeze( *it );
+ }
+ d->reasonCount = awayMessages.count();
+ QStringList menu;
+ menu << i18n( "No Message" );
+ menu << i18n( "New Message..." );
+ menu << QString::null ; //separator
+ menu += awayMessages ;
+ setItems( menu );
+ setCurrentItem( -1 );
+}
+
+void AwayAction::slotSelectAway( int index )
+{
+ //remove that crappy check mark cf bug 119862
+ setCurrentItem( -1 );
+
+ Kopete::Away *mAway = Kopete::Away::getInstance();
+ QString awayReason;
+
+ // Index == -1 means this is a result of Global Away all.
+ // Use the last entered message (0)
+ if( index == -1 )
+ index = 0;
+
+ switch(index)
+ {
+ case 0:
+ awayReason = QString::null;
+ break;
+ case 1:
+ bool ok;
+ awayReason = KInputDialog::getText( i18n( "New Away Message" ), i18n( "Please enter your away reason:" ) , QString::null , &ok );
+ if(!ok) //the user canceled
+ return;
+ if( !awayReason.isEmpty() )
+ Kopete::Away::getInstance()->addMessage( awayReason );
+ break;
+ case 2:
+ //not possible case, that's a separator
+ break;
+ default:
+ if( index-3 < d->reasonCount )
+ awayReason = mAway->getMessage( index-3 );
+ }
+
+ emit awayMessageSelected( awayReason ) ;
+ emit awayMessageSelected( d->status, awayReason );
+}
+
+} //END namespace Kopete
+
+#include "kopeteawayaction.moc"
+
diff --git a/kopete/libkopete/kopeteawayaction.h b/kopete/libkopete/kopeteawayaction.h
new file mode 100644
index 00000000..f8ab9d64
--- /dev/null
+++ b/kopete/libkopete/kopeteawayaction.h
@@ -0,0 +1,91 @@
+/*
+ kopetehistorydialog.h - Kopete Away Action
+
+ Copyright (c) 2003 Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEAWAYACTION_H
+#define KOPETEAWAYACTION_H
+
+#include <kdeversion.h>
+#include <kactionclasses.h>
+#include <kaction.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+class OnlineStatus;
+
+/**
+ * @class Kopete::AwayAction
+ *
+ * Kopete::AwayAction is a KAction that lets you select an away message
+ * from the list of predefined away messages, or enter a custom one.
+ *
+ * @author Jason Keirstead <jason@keirstead.org>
+ */
+class KOPETE_EXPORT AwayAction : public KSelectAction
+{
+ Q_OBJECT
+ public:
+ /**
+ * Constructor
+ * @p text, @p pix, @p cut, @p receiver, @p slot, @p parent and
+ * @p name are all handled by KSelectAction.
+ **/
+ AwayAction(const QString &text, const QIconSet &pix,
+ const KShortcut &cut, const QObject *receiver, const char *slot,
+ QObject *parent, const char *name = 0);
+
+ /**
+ * Constructor
+ * @param status the OnlineStatus that appears in the signal
+ * @param slot must have the following signature: ( const OnlineStatus &, const QString & )
+ * @p text, @p pix, @p cut, @p receiver, @p slot, @p parent and
+ * @p name are all handled by KSelectAction.
+ **/
+ AwayAction(const OnlineStatus &status, const QString &text, const QIconSet &pix,
+ const KShortcut &cut, const QObject *receiver, const char *slot,
+ QObject *parent, const char *name = 0);
+
+ /**
+ * Destructor.
+ */
+ ~AwayAction();
+
+ signals:
+ /**
+ * @brief Emits when the user selects an away message
+ */
+ void awayMessageSelected( const QString & );
+
+ /**
+ * same as above, but with the saved status
+ */
+ void awayMessageSelected( const Kopete::OnlineStatus& , const QString & );
+
+ private slots:
+ void slotAwayChanged();
+ void slotSelectAway( int index );
+
+ private:
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopeteawaydialog.cpp b/kopete/libkopete/kopeteawaydialog.cpp
new file mode 100644
index 00000000..0dbb7023
--- /dev/null
+++ b/kopete/libkopete/kopeteawaydialog.cpp
@@ -0,0 +1,143 @@
+/*
+ kopeteawaydialog.cpp - Kopete Away Dialog
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteawaydialog.h"
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstringhandler.h>
+
+#include "kopeteaway.h"
+#include "kopeteawaydialogbase.h"
+
+class KopeteAwayDialogPrivate
+{
+public:
+ KopeteAwayDialog_Base *base;
+};
+
+KopeteAwayDialog::KopeteAwayDialog( QWidget *parent, const char *name )
+: KDialogBase( parent, name, true, i18n( "Global Away Message" ),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true )
+{
+ //kdDebug( 14010 ) << k_funcinfo << "Building KopeteAwayDialog..." << endl;
+
+ d = new KopeteAwayDialogPrivate;
+
+ d->base = new KopeteAwayDialog_Base( this );
+ setMainWidget( d->base );
+
+ QObject::connect( d->base->cmbHistory, SIGNAL( activated( int ) ), this, SLOT( slotComboBoxSelection( int ) ) );
+
+ awayInstance = Kopete::Away::getInstance();
+ mExtendedAwayType = 0;
+ init();
+
+ //kdDebug( 14010 ) << k_funcinfo << "KopeteAwayDialog created." << endl;
+}
+
+KopeteAwayDialog::~KopeteAwayDialog()
+{
+ delete d;
+}
+
+void KopeteAwayDialog::slotComboBoxSelection( int index )
+{
+ // If they selected something out of the combo box
+ // They probably want to use it
+ d->base->txtOneShot->setText( awayInstance->getMessage(index) );
+ d->base->txtOneShot->setCursorPosition( 0 );
+}
+
+void KopeteAwayDialog::show()
+{
+ // When this show is called, set the
+ // mExtendedAwayType to the empty string
+ mExtendedAwayType = 0;
+
+ // Reinit the GUI
+ init();
+
+ //kdDebug( 14010 ) << k_funcinfo << "Showing Dialog with no extended away type" << endl;
+
+ KDialogBase::show();
+}
+
+void KopeteAwayDialog::show( int awayType )
+{
+ mExtendedAwayType = awayType;
+
+ // Reinit the GUI to set it up correctly
+ init();
+
+ kdDebug( 14010 ) << k_funcinfo << "Showing Dialog with extended away type " << awayType << endl;
+
+ KDialogBase::show();
+}
+
+void KopeteAwayDialog::cancelAway( int /* awayType */ )
+{
+ /* Empty default implementation */
+}
+
+void KopeteAwayDialog::init()
+{
+ QStringList awayMessages = awayInstance->getMessages();
+ for( QStringList::iterator it = awayMessages.begin(); it != awayMessages.end(); ++it )
+ {
+ *it = KStringHandler::rsqueeze( *it );
+ }
+
+ d->base->cmbHistory->clear();
+ d->base->cmbHistory->insertStringList( awayMessages );
+ d->base->txtOneShot->setText( awayMessages[0] );
+
+ d->base->txtOneShot->setFocus();
+ d->base->txtOneShot->setCursorPosition( 0 );
+}
+
+QString KopeteAwayDialog::getSelectedAwayMessage()
+{
+ mLastUserAwayMessage = d->base->txtOneShot->text();
+ return mLastUserAwayMessage;
+}
+
+void KopeteAwayDialog::slotOk()
+{
+ // Save the text the user typed
+ mLastUserTypedMessage = d->base->txtOneShot->text();
+
+ setAway( mExtendedAwayType );
+
+ KDialogBase::slotOk();
+}
+
+void KopeteAwayDialog::slotCancel()
+{
+ // Call the virtual function with the type of away
+ cancelAway( mExtendedAwayType );
+
+ KDialogBase::slotCancel();
+}
+
+#include "kopeteawaydialog.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteawaydialog.h b/kopete/libkopete/kopeteawaydialog.h
new file mode 100644
index 00000000..313cafe2
--- /dev/null
+++ b/kopete/libkopete/kopeteawaydialog.h
@@ -0,0 +1,201 @@
+/*
+ kopeteawaydialog.h - Kopete Away Dialog
+
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEAWAYDIALOG_H
+#define KOPETEAWAYDIALOG_H
+
+#include <kdialogbase.h>
+#include "kopete_export.h"
+
+namespace Kopete
+{
+class Away;
+}
+
+class KopeteAwayDialogPrivate;
+
+/**
+ * KopeteAwayDialog is a base class used for implementing
+ * Away Message selection dialogs in Kopete. It presents
+ * the user with a list of pre-written away messages and
+ * a line edit for them to type a "single shot" away message,
+ * one that is not saved and will be lost the next time
+ * they restart the application.
+ *
+ * Individual protocols should subclass this class for protocol
+ * specific Away Message choosers (in the case that the user
+ * wants to set only one protocol away). There are methods for
+ * getting the message that the user selected, as well as a
+ * virtual method that should be implemented that is called
+ * when the user selects "OK", and should be used to do
+ * protocol specific actions needed to set the user as
+ * "Away" (or whatever the protocol calls it).
+ *
+ * @author Hendrik vom Lehn <hvl@linux-4-ever.de>
+ * @author Christopher TenHarmsel <tenharmsel@users.sourceforge.net>
+ */
+
+class KOPETE_EXPORT KopeteAwayDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor for the Away Dialog
+ * @param parent The object that owns this
+ * @param name Name for this object
+ */
+ KopeteAwayDialog( QWidget *parent = 0, const char *name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~KopeteAwayDialog();
+
+protected:
+ /**
+ * Do not delete this, this instance will
+ * deleted when the application closes
+ */
+ Kopete::Away *awayInstance;
+
+ /**
+ * \brief Gets the last selected away message
+ * @return An away message
+ */
+ QString getSelectedAwayMessage();
+
+ /**
+ * \brief Sets the user away
+ *
+ * This method is called when the user clicks
+ * OK in the GUI, signalling that they wish
+ * to set the away message that they have chosen.
+ * Please reimplement this method to do protocol
+ * specific things, and use getSelectedAwayMessage()
+ * to get the text of the message that the user
+ * selected.
+ *
+ * @param awayType This is the away type specified
+ * if show was called with a parameter. If show() was called
+ * instead, this parameter will be the empty string. You
+ * will need to compare it to an enum that you declare
+ * in your subclass.
+ */
+ virtual void setAway( int awayType ) = 0;
+
+ /**
+ * \brief Called when "Cancel" is clicked
+ *
+ * This method is called when the user clicks
+ * Cancel in the GUI, signalling that they
+ * canceled their request to mark themselves as
+ * away. If your implementation finds this
+ * information useful, implement this method
+ * to handle this info. By default it does nothing
+ *
+ * @param awayType This is the away type specified
+ * if show was called with a parameter, if show() was called
+ * instead, this parameter will be the empty string.
+ */
+ virtual void cancelAway( int awayType );
+
+public slots:
+ /**
+ * \brief Shows the dialog
+ */
+ virtual void show();
+
+ /**
+ * \brief Shows the dialog
+ *
+ * Shows the away dialog, but maintains a "state"
+ * so you can specify if you're setting away,
+ * do not disturb, gone, etc for protocols that
+ * support this like ICQ and MSN.
+ *
+ * This string does not have any special internal
+ * meaning, but rather will get passed to setAway()
+ * when it is called so that you can decide what
+ * kind of "away" you really want to do.
+ *
+ * @param awayType The type of "away" you want to set.
+ */
+ void show( int awayType );
+
+protected slots:
+ /**
+ * This slot is called when the user click on "OK"
+ * it will call setAway(), which is pure virtual and
+ * should be implemented for specific needs
+ */
+ virtual void slotOk();
+
+ /**
+ * This slot is called when the user clicks on
+ * "Cancel". It calls cancelAway(), which is
+ * pure virtual and should be implemented to
+ * fit your specific needs if the user selects
+ * "Cancel". This method will close the
+ * dialog, but if you require any specific actions
+ * please implement them in cancelAway().
+ */
+ virtual void slotCancel();
+
+private slots:
+ /**
+ * \brief An entry was selected from the combo box
+ */
+ void slotComboBoxSelection( int index );
+
+private:
+ /**
+ * Initializes the GUI elements every time the
+ * dialog is show. Basically used for remembering
+ * the singleshot message that the user may have
+ * typed in.
+ */
+ void init();
+
+ /**
+ * The last user-entered away text
+ * or the title of the last selected
+ * saved away message, whichever was
+ * last chosen
+ */
+ QString mLastUserAwayMessage;
+
+ /**
+ * The last message that the user typed in the
+ * line edit
+ */
+ QString mLastUserTypedMessage;
+
+ /**
+ * This is used to store the type of away that we're
+ * going to go.
+ */
+ int mExtendedAwayType;
+
+ KopeteAwayDialogPrivate *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteblacklister.cpp b/kopete/libkopete/kopeteblacklister.cpp
new file mode 100644
index 00000000..8ec5c54b
--- /dev/null
+++ b/kopete/libkopete/kopeteblacklister.cpp
@@ -0,0 +1,109 @@
+/*
+ kopeteblacklister.cpp - Kopete BlackLister
+
+ Copyright (c) 2004 by Roie Kerstein <sf_kersteinroie@bezeqint.net>
+
+ *************************************************************************
+ * *
+ * 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 "kopeteblacklister.h"
+
+#include "kopetecontact.h"
+
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include <qstringlist.h>
+
+namespace Kopete
+{
+
+class BlackLister::Private
+{
+public:
+ QStringList blacklist;
+ QString owner;
+ QString protocol;
+};
+
+
+BlackLister::BlackLister(const QString &protocolId, const QString &accountId, QObject *parent, const char *name)
+ : QObject(parent, name), d( new Private )
+{
+ KConfig *config = KGlobal::config();
+
+ d->owner = accountId;
+ d->protocol = protocolId;
+ config->setGroup("BlackLister");
+ d->blacklist = config->readListEntry( d->protocol + QString::fromLatin1("_") + d->owner );
+}
+
+BlackLister::~BlackLister()
+{
+ delete d;
+}
+
+
+bool BlackLister::isBlocked(const QString &contactId)
+{
+ return (d->blacklist.find( contactId ) != d->blacklist.end() );
+}
+
+bool BlackLister::isBlocked(Contact *contact)
+{
+ return isBlocked(contact->contactId());
+}
+
+void BlackLister::addContact(const QString &contactId)
+{
+ if( !isBlocked(contactId) )
+ {
+ d->blacklist += contactId;
+ saveToDisk();
+ emit contactAdded( contactId );
+ }
+}
+
+void BlackLister::addContact(Contact *contact)
+{
+ QString temp = contact->contactId();
+
+ addContact( temp );
+}
+
+void BlackLister::removeContact(Contact *contact)
+{
+ QString temp = contact->contactId();
+
+ removeContact( temp );
+}
+
+void BlackLister::saveToDisk()
+{
+ KConfig *config = KGlobal::config();
+
+ config->setGroup("BlackLister");
+ config->writeEntry( d->protocol + QString::fromLatin1("_") + d->owner, d->blacklist );
+ config->sync();
+}
+
+void BlackLister::removeContact(const QString &contactId)
+{
+ if( isBlocked(contactId) )
+ {
+ d->blacklist.remove( contactId );
+ saveToDisk();
+ emit contactRemoved( contactId );
+ }
+}
+
+}
+
+#include "kopeteblacklister.moc"
diff --git a/kopete/libkopete/kopeteblacklister.h b/kopete/libkopete/kopeteblacklister.h
new file mode 100644
index 00000000..ed3e5566
--- /dev/null
+++ b/kopete/libkopete/kopeteblacklister.h
@@ -0,0 +1,124 @@
+/*
+ kopeteblacklister.h - Kopete BlackLister
+
+ Copyright (c) 2004 by Roie Kerstein <sf_kersteinroie@bezeqint.net>
+
+ *************************************************************************
+ * *
+ * 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 KOPETEBLACKLISTER_H
+#define KOPETEBLACKLISTER_H
+
+#include <qobject.h>
+
+namespace Kopete
+{
+
+class Contact;
+
+/**
+ * @brief Manages the list of blacklisted contacts for an account
+ *
+ * This class manages the list of contacts the user wishes
+ * to ignore permanently. In order to use the this class, there is no need to
+ * create an instance. Use the @ref Kopete::Account::blackLister() instead.
+ *
+ * Keep in mind that this class does not discard messages from blocked
+ * users - It only manages the list. It is the up to the protocol to
+ * check whether a user is blocked, and act accordingly. A protocol may
+ * re-implement @ref Kopete::Account::block() and @ref Kopete::Account::unblock()
+ * and use @ref Kopete::Account::blackLister() as a persistent list manager
+ * only, or connect the signals @ref contactAdded() and @ref contactRemoved()
+ * to its slots.
+ *
+ * @sa Kopete::Account::block() Kopete::Account::unblock()
+ *
+ * @author Roie Kerstein <sf_kersteinroie@bezeqint.net>
+ */
+class BlackLister : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create an instance, and read the blacklist from disk if it exists.
+ * @param protocolId is the ID of the protocol owning accountId
+ * @param accountId is the ID of the owning Account.
+ * @param parent The QObject parent for this class.
+ * @param name The QObject name for this class.
+ */
+ BlackLister( const QString &protocolId, const QString &accountId, QObject *parent = 0, const char *name = 0 );
+ ~BlackLister();
+
+ /**
+ * \return @c true if @p contact is blocked, @c false otherwise.
+ */
+ bool isBlocked( Contact *contact );
+
+ /**
+ * \return @c true if the contact with ID @p contactId is blocked, @c false otherwise.
+ */
+ bool isBlocked( const QString &contactId );
+
+public slots:
+ /**
+ * Add a contact to the blacklist.
+ *
+ * This function emits the @ref contactAdded() signal.
+ * @param contactId is the ID of the contact to be added to the list.
+ */
+ void addContact( const QString &contactId );
+
+ /**
+ * @overload
+ */
+ void addContact( Contact *contact );
+
+ /**
+ * \brief Remove a contact from the blacklist.
+ *
+ * Removes the contact from the blacklist.
+ * This function emits the @ref contactRemoved() signal.
+ * @param contact is the contact to be removed from the list.
+ */
+ void removeContact( Contact *contact );
+
+ /**
+ * @overload
+ */
+ void removeContact( const QString &contactId );
+
+signals:
+ /**
+ * \brief A new contact has been added to the list
+ *
+ * Connect to this signal if you want to perform additional actions,
+ * and you prefer not to derive from this class.
+ */
+ void contactAdded( const QString &contactId );
+
+ /**
+ * \brief A contact has been removed from the list
+ *
+ * Connect to this signal if you want to perform additional actions,
+ * and you prefer not to derive from this class.
+ */
+ void contactRemoved( const QString &contactId );
+
+private:
+ void saveToDisk();
+
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopetechatsession.cpp b/kopete/libkopete/kopetechatsession.cpp
new file mode 100644
index 00000000..9ebf1d07
--- /dev/null
+++ b/kopete/libkopete/kopetechatsession.cpp
@@ -0,0 +1,515 @@
+/*
+ kopetechatsession.cpp - Manages all chats
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetechatsession.h"
+
+#include <qapplication.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <knotification.h>
+
+#include "kopeteaccount.h"
+#include "kopetecommandhandler.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemessagehandlerchain.h"
+#include "kopetemetacontact.h"
+#include "knotification.h"
+#include "kopeteprefs.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+#include "kopeteview.h"
+#include "kopetecontact.h"
+
+class KMMPrivate
+{
+public:
+ Kopete::ContactPtrList mContactList;
+ const Kopete::Contact *mUser;
+ QMap<const Kopete::Contact *, Kopete::OnlineStatus> contactStatus;
+ Kopete::Protocol *mProtocol;
+ bool isEmpty;
+ bool mCanBeDeleted;
+ unsigned int refcount;
+ bool customDisplayName;
+ QDateTime awayTime;
+ QString displayName;
+ KopeteView *view;
+ bool mayInvite;
+ Kopete::MessageHandlerChain::Ptr chains[3];
+};
+
+Kopete::ChatSession::ChatSession( const Kopete::Contact *user,
+ Kopete::ContactPtrList others, Kopete::Protocol *protocol, const char *name )
+: QObject( user->account(), name )
+{
+ d = new KMMPrivate;
+ d->mUser = user;
+ d->mProtocol = protocol;
+ d->isEmpty = others.isEmpty();
+ d->mCanBeDeleted = true;
+ d->refcount = 0;
+ d->view = 0L;
+ d->customDisplayName = false;
+ d->mayInvite = false;
+
+ for ( Kopete::Contact *c = others.first(); c; c = others.next() )
+ addContact( c, true );
+
+ connect( user, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ), this,
+ SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+
+ if( user->metaContact() )
+ connect( user->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
+
+ slotUpdateDisplayName();
+}
+
+Kopete::ChatSession::~ChatSession()
+{
+ //for ( Kopete::Contact *c = d->mContactList.first(); c; c = d->mContactList.next() )
+ // c->setConversations( c->conversations() - 1 );
+
+ if ( !d )
+ return;
+ d->mCanBeDeleted = false; //prevent double deletion
+ Kopete::ChatSessionManager::self()->removeSession( this );
+ emit closing( this );
+ delete d;
+}
+
+void Kopete::ChatSession::slotOnlineStatusChanged( Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus )
+{
+ slotUpdateDisplayName();
+ emit onlineStatusChanged((Kopete::Contact*)c, status, oldStatus);
+}
+
+void Kopete::ChatSession::setContactOnlineStatus( const Kopete::Contact *contact, const Kopete::OnlineStatus &status )
+{
+ Kopete::OnlineStatus oldStatus = d->contactStatus[ contact ];
+ d->contactStatus[ contact ] = status;
+ disconnect( contact, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &) ) );
+ emit onlineStatusChanged( (Kopete::Contact*)contact, status, oldStatus );
+}
+
+const Kopete::OnlineStatus Kopete::ChatSession::contactOnlineStatus( const Kopete::Contact *contact ) const
+{
+ if ( d->contactStatus.contains( contact ) )
+ return d->contactStatus[ contact ];
+
+ return contact->onlineStatus();
+}
+
+const QString Kopete::ChatSession::displayName()
+{
+ if ( d->displayName.isNull() )
+ {
+ slotUpdateDisplayName();
+ }
+
+ return d->displayName;
+}
+
+void Kopete::ChatSession::setDisplayName( const QString &newName )
+{
+ d->displayName = newName;
+ d->customDisplayName = true;
+ emit displayNameChanged();
+}
+
+void Kopete::ChatSession::slotUpdateDisplayName()
+{
+ if( d->customDisplayName )
+ return;
+
+ Kopete::Contact *c = d->mContactList.first();
+
+ //If there is no member yet, don't try to update the display name
+ if ( !c )
+ return;
+
+ d->displayName=QString::null;
+ do
+ {
+ if(! d->displayName.isNull() )
+ d->displayName.append( QString::fromLatin1( ", " ) ) ;
+
+ if ( c->metaContact() )
+ d->displayName.append( c->metaContact()->displayName() );
+ else
+ {
+ QString nick=c->property(Kopete::Global::Properties::self()->nickName()).value().toString();
+ d->displayName.append( nick.isEmpty() ? c->contactId() : nick );
+ }
+ c=d->mContactList.next();
+ } while (c);
+
+ //If we have only 1 contact, add the status of him
+ if ( d->mContactList.count() == 1 )
+ {
+ d->displayName.append( QString::fromLatin1( " (%1)" ).arg( d->mContactList.first()->onlineStatus().description() ) );
+ }
+
+ emit displayNameChanged();
+}
+
+const Kopete::ContactPtrList& Kopete::ChatSession::members() const
+{
+ return d->mContactList;
+}
+
+const Kopete::Contact* Kopete::ChatSession::myself() const
+{
+ return d->mUser;
+}
+
+Kopete::Protocol* Kopete::ChatSession::protocol() const
+{
+ return d->mProtocol;
+}
+
+
+#include "kopetemessagehandler.h"
+#include "kopetemessageevent.h"
+
+// FIXME: remove this and the friend decl in KMM
+class Kopete::TemporaryKMMCallbackAppendMessageHandler : public Kopete::MessageHandler
+{
+ Kopete::ChatSession *manager;
+public:
+ TemporaryKMMCallbackAppendMessageHandler( Kopete::ChatSession *manager )
+ : manager(manager)
+ {
+ }
+ void handleMessage( Kopete::MessageEvent *event )
+ {
+ Kopete::Message message = event->message();
+ emit manager->messageAppended( message, manager );
+ delete event;
+ }
+};
+
+class TempFactory : public Kopete::MessageHandlerFactory
+{
+public:
+ Kopete::MessageHandler *create( Kopete::ChatSession *manager, Kopete::Message::MessageDirection )
+ {
+ return new Kopete::TemporaryKMMCallbackAppendMessageHandler( manager );
+ }
+ int filterPosition( Kopete::ChatSession *, Kopete::Message::MessageDirection )
+ {
+ // FIXME: somewhere after everyone else.
+ return 100000;
+ }
+};
+
+Kopete::MessageHandlerChain::Ptr Kopete::ChatSession::chainForDirection( Kopete::Message::MessageDirection dir )
+{
+ if( dir < 0 || dir > 2)
+ kdFatal(14000) << k_funcinfo << "invalid message direction " << dir << endl;
+ if( !d->chains[dir] )
+ {
+ TempFactory theTempFactory;
+ d->chains[dir] = Kopete::MessageHandlerChain::create( this, dir );
+ }
+ return d->chains[dir];
+}
+
+void Kopete::ChatSession::sendMessage( Kopete::Message &message )
+{
+ message.setManager( this );
+ Kopete::Message sentMessage = message;
+ if ( !Kopete::CommandHandler::commandHandler()->processMessage( message, this ) )
+ {
+ emit messageSent( sentMessage, this );
+ if ( !account()->isAway() || KopetePrefs::prefs()->soundIfAway() )
+ {
+ KNotification::event(QString::fromLatin1( "kopete_outgoing" ), i18n( "Outgoing Message Sent" ) );
+ }
+ }
+ else
+ {
+ messageSucceeded();
+ }
+}
+
+void Kopete::ChatSession::messageSucceeded()
+{
+ emit messageSuccess();
+}
+
+void Kopete::ChatSession::emitNudgeNotification()
+{
+ KNotification::event( QString::fromLatin1("buzz_nudge"), i18n("A contact sent you a buzz/nudge.") );
+}
+
+void Kopete::ChatSession::appendMessage( Kopete::Message &msg )
+{
+ msg.setManager( this );
+
+ if ( msg.direction() == Kopete::Message::Inbound )
+ {
+ QString nick=myself()->property(Kopete::Global::Properties::self()->nickName()).value().toString();
+ if ( KopetePrefs::prefs()->highlightEnabled() && !nick.isEmpty() &&
+ msg.plainBody().contains( QRegExp( QString::fromLatin1( "\\b(%1)\\b" ).arg( nick ), false ) ) )
+ {
+ msg.setImportance( Kopete::Message::Highlight );
+ }
+
+ emit messageReceived( msg, this );
+ }
+
+ // outbound messages here are ones the user has sent that are now
+ // getting reflected back to the chatwindow. they should go down
+ // the incoming chain.
+ Kopete::Message::MessageDirection chainDirection = msg.direction();
+ if( chainDirection == Kopete::Message::Outbound )
+ chainDirection = Kopete::Message::Inbound;
+
+ chainForDirection( chainDirection )->processMessage( msg );
+// emit messageAppended( msg, this );
+}
+
+void Kopete::ChatSession::addContact( const Kopete::Contact *c, const Kopete::OnlineStatus &initialStatus, bool suppress )
+{
+ if( !d->contactStatus.contains(c) )
+ d->contactStatus[ c ] = initialStatus;
+ addContact( c, suppress );
+}
+
+void Kopete::ChatSession::addContact( const Kopete::Contact *c, bool suppress )
+{
+ //kdDebug( 14010 ) << k_funcinfo << endl;
+ if ( d->mContactList.contains( c ) )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Contact already exists" <<endl;
+ emit contactAdded( c, suppress );
+ }
+ else
+ {
+ if ( d->mContactList.count() == 1 && d->isEmpty )
+ {
+ kdDebug( 14010 ) << k_funcinfo << " FUCKER ZONE " << endl;
+ /* We have only 1 contact before, so the status of the
+ message manager was given from that contact status */
+ Kopete::Contact *old = d->mContactList.first();
+ d->mContactList.remove( old );
+ d->mContactList.append( c );
+
+ disconnect( old, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &) ) );
+
+ if ( old->metaContact() )
+ {
+ disconnect( old->metaContact(), SIGNAL( displayNameChanged( const QString &, const QString & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ disconnect( old->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
+ }
+ else
+ disconnect( old, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ emit contactAdded( c, suppress );
+ emit contactRemoved( old, QString::null );
+ }
+ else
+ {
+ d->mContactList.append( c );
+ emit contactAdded( c, suppress );
+ }
+
+ connect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &) ) );
+;
+ if ( c->metaContact() )
+ {
+ connect( c->metaContact(), SIGNAL( displayNameChanged( const QString &, const QString & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ connect( c->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
+ }
+ else
+ connect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ), this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
+
+ slotUpdateDisplayName();
+ }
+ d->isEmpty = false;
+}
+
+void Kopete::ChatSession::removeContact( const Kopete::Contact *c, const QString& reason, Kopete::Message::MessageFormat format, bool suppressNotification )
+{
+ kdDebug( 14010 ) << k_funcinfo << endl;
+ if ( !c || !d->mContactList.contains( c ) )
+ return;
+
+ if ( d->mContactList.count() == 1 )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Contact not removed. Keep always one contact" << endl;
+ d->isEmpty = true;
+ }
+ else
+ {
+ d->mContactList.remove( c );
+
+ disconnect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotOnlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus &) ) );
+
+ if ( c->metaContact() )
+ {
+ disconnect( c->metaContact(), SIGNAL( displayNameChanged( const QString &, const QString & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ disconnect( c->metaContact(), SIGNAL( photoChanged() ), this, SIGNAL( photoChanged() ) );
+ }
+ else
+ disconnect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ), this, SLOT( slotUpdateDisplayName() ) );
+ disconnect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ), this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
+
+ slotUpdateDisplayName();
+ }
+
+ d->contactStatus.remove( c );
+
+ emit contactRemoved( c, reason, format, suppressNotification );
+}
+
+void Kopete::ChatSession::receivedTypingMsg( const Kopete::Contact *c, bool t )
+{
+ emit remoteTyping( c, t );
+}
+
+void Kopete::ChatSession::receivedTypingMsg( const QString &contactId, bool t )
+{
+ for ( Kopete::Contact *it = d->mContactList.first(); it; it = d->mContactList.next() )
+ {
+ if ( it->contactId() == contactId )
+ {
+ receivedTypingMsg( it, t );
+ return;
+ }
+ }
+}
+
+void Kopete::ChatSession::typing( bool t )
+{
+ emit myselfTyping( t );
+}
+
+void Kopete::ChatSession::receivedEventNotification( const QString& notificationText)
+{
+ emit eventNotification( notificationText );
+}
+
+void Kopete::ChatSession::setCanBeDeleted ( bool b )
+{
+ d->mCanBeDeleted = b;
+ if (d->refcount < (b?1:0) && !d->view )
+ deleteLater();
+}
+
+void Kopete::ChatSession::ref ()
+{
+ d->refcount++;
+}
+void Kopete::ChatSession::deref ()
+{
+ d->refcount--;
+ if ( d->refcount < 1 && d->mCanBeDeleted && !d->view )
+ deleteLater();
+}
+
+KopeteView* Kopete::ChatSession::view( bool canCreate, const QString &requestedPlugin )
+{
+ if ( !d->view && canCreate )
+ {
+ d->view = Kopete::ChatSessionManager::self()->createView( this, requestedPlugin );
+ if ( d->view )
+ {
+ connect( d->view->mainWidget(), SIGNAL( closing( KopeteView * ) ), this, SLOT( slotViewDestroyed( ) ) );
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n( "<qt>An error has occurred while creating a new chat window. The chat window has not been created.</qt>" ),
+ i18n( "Error While Creating Chat Window" ) );
+ }
+ }
+ return d->view;
+}
+
+void Kopete::ChatSession::slotViewDestroyed()
+{
+ d->view = 0L;
+ if ( d->mCanBeDeleted && d->refcount < 1)
+ deleteLater();
+}
+
+Kopete::Account *Kopete::ChatSession::account() const
+{
+ return myself()->account();
+}
+
+void Kopete::ChatSession::slotContactDestroyed( Kopete::Contact *contact )
+{
+ if(contact == myself())
+ deleteLater();
+
+ if( !contact || !d->mContactList.contains( contact ) )
+ return;
+
+ //This is a workaround to prevent crash if the contact get deleted.
+ // in the best case, we should ask the protocol to recreate a temporary contact.
+ // (remember: the contact may be deleted when the users removes it from the contactlist, or when closing kopete )
+ d->mContactList.remove( contact );
+ emit contactRemoved( contact, QString::null );
+
+ if ( d->mContactList.isEmpty() )
+ deleteLater();
+}
+
+bool Kopete::ChatSession::mayInvite() const
+{
+ return d->mayInvite;
+}
+
+void Kopete::ChatSession::inviteContact(const QString& )
+{
+ //default implementation do nothing
+}
+
+void Kopete::ChatSession::setMayInvite( bool b )
+{
+ d->mayInvite=b;
+}
+
+void Kopete::ChatSession::raiseView()
+{
+ KopeteView *v=view(true, KopetePrefs::prefs()->interfacePreference() );
+ if(v)
+ v->raise(true);
+}
+
+#include "kopetechatsession.moc"
+
+
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetechatsession.h b/kopete/libkopete/kopetechatsession.h
new file mode 100644
index 00000000..86d5fa64
--- /dev/null
+++ b/kopete/libkopete/kopetechatsession.h
@@ -0,0 +1,405 @@
+/*
+ kopetechatsession.h - Manages all chats
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __KOPETECHATSESSION_H__
+#define __KOPETECHATSESSION_H__
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qvaluelist.h>
+
+#include <kxmlguiclient.h>
+
+#include "kopete_export.h"
+
+// FIXME: get rid of these includes
+#include "kopetemessage.h"
+#include "kopetemessagehandlerchain.h"
+
+class KMMPrivate;
+
+class KopeteView;
+
+namespace Kopete
+{
+
+class Contact;
+class Message;
+class Protocol;
+class OnlineStatus;
+class Account;
+class ChatSessionManager;
+class MessageHandlerChain;
+class TemporaryKMMCallbackAppendMessageHandler;
+
+typedef QPtrList<Contact> ContactPtrList;
+typedef QValueList<Message> MessageList;
+
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ * @author Daniel Stone <dstone@kde.org>
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ * @author Jason Keirstead <jason@keirstead.org>
+ *
+ * The Kopete::ChatSession manages a single chat.
+ * It is an interface between the protocol, and the chatwindow.
+ * The protocol can connect to @ref messageSent() signals to send the message, and can
+ * append received message with @ref messageReceived()
+ *
+ * The KMM inherits from KXMLGUIClient, this client is merged with the chatwindow's ui
+ * so plugins can add childClients of this client to add their own actions in the
+ * chatwindow.
+ */
+class KOPETE_EXPORT ChatSession : public QObject , public KXMLGUIClient
+{
+ // friend class so the object factory can access the protected constructor
+ friend class ChatSessionManager;
+
+ Q_OBJECT
+
+public:
+ /**
+ * Delete a chat manager instance
+ * You shouldn't delete the KMM yourself. it will be deleted when the chatwindow is closed
+ * see also @ref setCanBeDeleted() , @ref deref()
+ */
+ ~ChatSession();
+
+ /**
+ * @brief Get a list of all contacts in the session
+ */
+ const ContactPtrList& members() const;
+
+ /**
+ * @brief Get the local user in the session
+ * @return the local user in the session, same as account()->myself()
+ */
+ const Contact* myself() const;
+
+ /**
+ * @brief Get the protocol being used.
+ * @return the protocol
+ */
+ Protocol* protocol() const;
+
+ /**
+ * @brief get the account
+ * @return the account
+ */
+ Account *account() const ;
+
+ /**
+ * @brief The caption of the chat
+ *
+ * Used for named chats
+ */
+ const QString displayName();
+
+ /**
+ * @brief change the displayname
+ *
+ * change the display name of the chat
+ */
+ void setDisplayName( const QString &displayName );
+
+ /**
+ * @brief set a specified KOS for specified contact in this KMM
+ *
+ * Set a special icon for a contact in this kmm only.
+ * by default, all contact have their own status
+ */
+ void setContactOnlineStatus( const Contact *contact, const OnlineStatus &newStatus );
+
+ /**
+ * @brief get the status of a contact.
+ *
+ * see @ref setContactOnlineStatus()
+ */
+ const OnlineStatus contactOnlineStatus( const Contact *contact ) const;
+
+ /**
+ * @brief the manager's view
+ *
+ * Return the view for the supplied Kopete::ChatSession. If it already
+ * exists, it will be returned, otherwise, 0L will be returned or a new one
+ * if canCreate=true
+ * @param canCreate create a new one if it does not exist
+ * @param requestedPlugin Specifies the view plugin to use if we have to create one.
+ */
+ // FIXME: canCreate should definitely be an enum and not a bool - Martijn
+ KopeteView* view( bool canCreate = false, const QString &requestedPlugin = QString::null );
+
+ /**
+ * says if you may invite contact from the same account to this chat with @ref inviteContact
+ * @see setMayInvite
+ * @return true if it is possible to invite contact to this chat.
+ */
+ bool mayInvite() const ;
+
+ /**
+ * this method is called when a contact is dragged to the contactlist.
+ * @p contactId is the id of the contact. the contact is supposed to be of the same account as
+ * the @ref account() but we can't be sure the Kopete::Contact is realy on the contactlist
+ *
+ * It is possible to drag contact only if @ref mayInvite return true
+ *
+ * the default implementaiton do nothing
+ */
+ virtual void inviteContact(const QString &contactId);
+
+ /**
+ * Returns the message handler chain for the message direction @p dir.
+ */
+ MessageHandlerChain::Ptr chainForDirection( Message::MessageDirection dir );
+
+signals:
+ /**
+ * @brief the KMM will be deleted
+ * Used by a Kopete::ChatSession to signal that it is closing.
+ */
+ void closing( Kopete::ChatSession *kmm );
+
+ /**
+ * a message will be soon shown in the chatwindow.
+ * See @ref Kopete::ChatSessionManager::aboutToDisplay() signal
+ */
+ void messageAppended( Kopete::Message &msg, Kopete::ChatSession *kmm = 0L );
+
+ /**
+ * a message will be soon received
+ * See @ref Kopete::ChatSessionManager::aboutToReceive() signal
+ */
+ void messageReceived( Kopete::Message &msg, Kopete::ChatSession *kmm = 0L );
+
+ /**
+ * @brief a message is going to be sent
+ *
+ * The message is going to be sent.
+ * protocols can connect to this signal to send the message ro the network.
+ * the protocol have also to call @ref appendMessage() and @ref messageSucceeded()
+ * See also @ref Kopete::ChatSessionManager::aboutToSend() signal
+ */
+ void messageSent( Kopete::Message &msg, Kopete::ChatSession *kmm = 0L );
+
+ /**
+ * The last message has finaly successfully been sent
+ */
+ void messageSuccess();
+
+ /**
+ * @brief a new contact is now in the chat
+ */
+ // FIXME: What's 'suppress'? Shouldn't this be an enum? - Martijn
+ void contactAdded( const Kopete::Contact *contact, bool suppress );
+
+ /**
+ * @brief a contact is no longer in this chat
+ */
+ void contactRemoved( const Kopete::Contact *contact, const QString &reason, Kopete::Message::MessageFormat format = Message::PlainText, bool contactRemoved = false );
+
+ /**
+ * @brief a contact in this chat has changed his status
+ */
+ void onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & );
+
+ /**
+ * @brief The name of the chat is changed
+ */
+ void displayNameChanged();
+
+ /**
+ * @brief emitting a typing notification
+ *
+ * The user is typing a message, or just stopped typing
+ * the protocol should connect to this signal to signal to others
+ * that the user is typing if the protocol supports this
+ * @param isTyping say if the user is typing or not
+ */
+ void myselfTyping( bool isTyping );
+
+ /**
+ * Signals that a remote user is typing a message.
+ * the chatwindow connects to this signal to update the statusbar
+ */
+ void remoteTyping( const Kopete::Contact *contact, bool isTyping );
+
+ /**
+ * Signals that a an event has to be displayed in the statusbar.
+ * The chatwindow connects to this signal to update the statusbar.
+ */
+ void eventNotification( const QString& notificationText);
+
+ /**
+ * @brief A contact within the chat session changed his photo.
+ * Used to update the contacts photo in chat window.
+ */
+ void photoChanged();
+
+public slots:
+ /**
+ * @brief Got a typing notification from a user
+ */
+ void receivedTypingMsg( const Kopete::Contact *contact , bool isTyping = true );
+
+ /**
+ * Got a typing notification from a user. This is a convenience version
+ * of the above method that takes a QString contactId instead of a full
+ * Kopete::Contact
+ */
+ void receivedTypingMsg( const QString &contactId, bool isTyping = true );
+
+ /**
+ * @brief Got an event notification from a user.
+ * It will emit the signal eventNotification(). Use this slot in your protocols
+ * and plugins to change chatwindow statusBar text.
+ */
+ void receivedEventNotification( const QString& notificationText );
+
+ /**
+ * Show a message to the chatwindow, or append it to the queue.
+ * This is the function protocols HAVE TO call for both incoming and outgoing messages
+ * if the message must be showed in the chatwindow
+ */
+ void appendMessage( Kopete::Message &msg );
+
+ /**
+ * Add a contact to the session
+ * @param c is the contact
+ * @param suppress mean the there will be no automatic notifications in the chatwindow.
+ * (note that i don't like the param suppress at all. it is used in irc to show a different notification (with an info text)
+ * a QStringinfo would be more interesting, but it is also used to don't show the notification when entering in a channel)
+ */
+ void addContact( const Kopete::Contact *c, bool suppress = false );
+
+ /**
+ * Add a contact to the session with a pre-set initial status
+ * @param c is the contact
+ * @param initialStatus The initial contactOnlineStatus of the contact
+ * @param suppress mean the there will be no automatic notifications in the chatwindow.
+ * (note that i don't like the param suppress at all. it is used in irc to show a different notification (with an info text)
+ * a QStringinfo would be more interesting, but it is also used to don't show the notification when entering in a channel)
+ * @see contactOnlineStatus
+ */
+ void addContact( const Kopete::Contact *c, const Kopete::OnlineStatus &initialStatus, bool suppress = false );
+
+ /**
+ * Remove a contact from the session
+ * @param contact is the contact
+ * @param reason is the optional raison message showed in the chatwindow
+ * @param format The format of the message
+ * @param suppressNotification prevents a notification of the removal in the chat view. See note in @ref addContact
+ */
+ void removeContact( const Kopete::Contact *contact, const QString& reason = QString::null, Kopete::Message::MessageFormat format = Message::PlainText, bool suppressNotification = false );
+
+ /**
+ * Set if the KMM will be deleted when the chatwindow is deleted. It is useful if you want
+ * to keep the KMM alive even if the chatwindow is closed.
+ * Warning: if you set it to false, please keep in mind that you have to reset it to true
+ * later to delete it. In many case, you should never delete yourself the KMM, just call this
+ * this method.
+ * default is true.
+ * If there are no chatwindow when setting it to true, the kmm will be deleted.
+ *
+ * @deprecated use ref and deref
+ */
+ void setCanBeDeleted ( bool canBeDeleted );
+
+ /**
+ * reference count the chat session.
+ * the chat session may be deleted only if the count reach 0
+ * if you ref, don't forget to deref
+ * @see deref()
+ */
+ void ref();
+ /**
+ * dereference count the chat session
+ * if the reference counter reach 0 and there is no chat window open, the chat session will be deleted.
+ */
+ void deref();
+
+
+ /**
+ * Send a message to the user
+ */
+ void sendMessage( Kopete::Message &message );
+
+ /**
+ * Tell the KMM that the user is typing
+ * This method should be called only by a chatwindow. It emits @ref myselfTyping signal
+ */
+ void typing( bool t );
+
+ /**
+ * Protocols have to call this method when the last message sent has been correctly sent
+ * This will emit @ref messageSuccess signal. and allow the email window to get closed
+ */
+ void messageSucceeded();
+
+ /**
+ * Protcols have to call this method if they want to emit a notification when a nudge/buzz is received.
+ */
+ void emitNudgeNotification();
+
+ /**
+ * Raise the chat window and give him the focus
+ * It's used when the user wanted to activated (by clicking on the "view" button of a popup)
+ */
+ void raiseView();
+
+private slots:
+ void slotUpdateDisplayName();
+ void slotViewDestroyed();
+ void slotOnlineStatusChanged( Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus );
+ void slotContactDestroyed( Kopete::Contact *contact );
+
+protected:
+ /**
+ * Create a message manager. This constructor is private, because the
+ * static factory method createSession() creates the object. You may
+ * not create instances yourself directly!
+ */
+ ChatSession( const Contact *user, ContactPtrList others,
+ Protocol *protocol, const char *name = 0 );
+
+ /**
+ * Set wether or not contact from this account may be invited in this chat.
+ * By default, it is set to false
+ * @see inviteContact()
+ * @see mayInvite()
+ */
+ void setMayInvite(bool);
+
+private:
+ KMMPrivate *d;
+
+ // FIXME: remove
+ friend class TemporaryKMMCallbackAppendMessageHandler;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetechatsessionmanager.cpp b/kopete/libkopete/kopetechatsessionmanager.cpp
new file mode 100644
index 00000000..9b7dd489
--- /dev/null
+++ b/kopete/libkopete/kopetechatsessionmanager.cpp
@@ -0,0 +1,197 @@
+/*
+ kopetechatsessionmanager.cpp - Creates chat sessions
+
+ Copyright (c) 2002-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteviewmanager.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include "ui/kopeteview.h"
+#include "kopetecontact.h"
+
+namespace Kopete {
+
+class ChatSessionManager::Private
+{
+ public:
+ QValueList <ChatSession*> sessions;
+// UI::ChatView *activeView;
+};
+
+ChatSessionManager* ChatSessionManager::s_self = 0L;
+
+ChatSessionManager* ChatSessionManager::self()
+{
+ if( !s_self )
+ s_self = new ChatSessionManager( kapp );
+
+ return s_self;
+}
+
+ChatSessionManager::ChatSessionManager( QObject* parent,
+ const char* name )
+ : QObject( parent, name )
+{
+ d=new Private;
+ s_self = this;
+}
+
+ChatSessionManager::~ChatSessionManager()
+{
+ s_self = 0L;
+ QValueListIterator<ChatSession*> it;
+ for ( it=d->sessions.begin() ; it!=d->sessions.end() ; ++it )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Unloading KMM: Why this KMM isn't yet unloaded?" << endl;
+ (*it)->deleteLater();
+ }
+ delete d;
+}
+
+ChatSession* ChatSessionManager::findChatSession(const Contact *user,
+ ContactPtrList chatContacts, Protocol *protocol)
+{
+ ChatSession *result = 0L;
+ QValueList<ChatSession*>::Iterator it;
+ for ( it= d->sessions.begin(); it!=d->sessions.end() && !result ; ++it )
+ {
+ ChatSession* cs=(*it);
+ if ( cs->protocol() == protocol && user == cs->myself() )
+ {
+ QPtrList<Contact> contactlist = cs->members();
+
+ // set this to false if chatContacts doesn't contain current cs's contactlist
+ bool halfMatch = true;
+
+ Contact *tmpContact;
+ for (tmpContact = contactlist.first(); tmpContact && halfMatch; tmpContact = contactlist.next())
+ {
+ if ( !chatContacts.containsRef( tmpContact ) )
+ halfMatch = false;
+ }
+
+ // If chatContacts contains current cs's contactlist, try the other way around
+ if (halfMatch)
+ {
+ bool fullMatch = true;
+ for (tmpContact = chatContacts.first(); tmpContact && fullMatch; tmpContact = chatContacts.next())
+ {
+ if ( !contactlist.containsRef( tmpContact ) )
+ fullMatch = false;
+ }
+ // We have a winner
+ if (fullMatch)
+ result = cs;
+ }
+ }
+ }
+ return result;
+}
+
+ChatSession *ChatSessionManager::create(
+ const Contact *user, ContactPtrList chatContacts, Protocol *protocol)
+{
+ ChatSession *result=findChatSession( user, chatContacts, protocol);
+ if (!result)
+ {
+ result = new ChatSession(user, chatContacts, protocol );
+ registerChatSession(result);
+ }
+ return (result);
+}
+
+void ChatSessionManager::slotReadMessage()
+{
+ emit readMessage();
+}
+
+void ChatSessionManager::registerChatSession(ChatSession * result)
+{
+ d->sessions.append( result );
+
+ /*
+ * There's no need for a slot here... just add a public remove()
+ * method and call from KMM's destructor
+ */
+ connect( result, SIGNAL( messageAppended( Kopete::Message &, Kopete::ChatSession * ) ),
+ SIGNAL( aboutToDisplay( Kopete::Message & ) ) );
+ connect( result, SIGNAL( messageSent( Kopete::Message &, Kopete::ChatSession * ) ),
+ SIGNAL( aboutToSend(Kopete::Message & ) ) );
+ connect( result, SIGNAL( messageReceived( Kopete::Message &, Kopete::ChatSession * ) ),
+ SIGNAL( aboutToReceive(Kopete::Message & ) ) );
+
+ connect( result, SIGNAL(messageAppended( Kopete::Message &, Kopete::ChatSession *) ),
+ SIGNAL( display( Kopete::Message &, Kopete::ChatSession *) ) );
+
+ emit chatSessionCreated(result);
+}
+
+
+void ChatSessionManager::removeSession( ChatSession *session)
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ d->sessions.remove( session );
+}
+
+QValueList<ChatSession*> ChatSessionManager::sessions( )
+{
+ return d->sessions;
+}
+
+KopeteView * ChatSessionManager::createView( ChatSession *kmm , const QString &requestedPlugin )
+{
+ KopeteView *newView = KopeteViewManager::viewManager()->view(kmm,requestedPlugin);
+ if(!newView)
+ {
+ kdDebug(14010) << k_funcinfo << "View not successfuly created" << endl;
+ return 0L;
+ }
+
+ QObject *viewObject = dynamic_cast<QObject *>(newView);
+ if(viewObject)
+ {
+ connect(viewObject, SIGNAL(activated(KopeteView *)),
+ this, SIGNAL(viewActivated(KopeteView *)));
+ connect(viewObject, SIGNAL(closing(KopeteView *)),
+ this, SIGNAL(viewClosing(KopeteView *)));
+ }
+ else
+ {
+ kdWarning(14010) << "Failed to cast view to QObject *" << endl;
+ }
+
+ emit viewCreated( newView ) ;
+ return newView;
+}
+
+void ChatSessionManager::postNewEvent(MessageEvent *e)
+{
+ emit newEvent(e);
+}
+
+KopeteView *ChatSessionManager::activeView()
+{
+ return KopeteViewManager::viewManager()->activeView();
+}
+
+} //END namespace Kopete
+
+#include "kopetechatsessionmanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetechatsessionmanager.h b/kopete/libkopete/kopetechatsessionmanager.h
new file mode 100644
index 00000000..e41eb14d
--- /dev/null
+++ b/kopete/libkopete/kopetechatsessionmanager.h
@@ -0,0 +1,192 @@
+/*
+ kopetechatsessionmanager.h - Creates chat sessions
+
+ Copyright (c) 2002-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMESSAGEMANAGERFACTORY_H
+#define KOPETEMESSAGEMANAGERFACTORY_H
+
+#include <qobject.h>
+#include <qptrlist.h>
+#include <qintdict.h>
+#include <qvaluelist.h>
+
+#include "kopetechatsession.h"
+#include "kopetemessage.h"
+
+#include "kopete_export.h"
+
+class KopeteView;
+
+namespace Kopete
+{
+
+class Contact;
+class Protocol;
+class MessageEvent;
+
+typedef QPtrList<Contact> ContactPtrList;
+typedef QValueList<Message> MessageList;
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ *
+ * Kopete::ChatSessionManager is responsible for creating and tracking Kopete::ChatSession
+ * instances for each chat.
+ */
+class KOPETE_EXPORT ChatSessionManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ static ChatSessionManager* self();
+
+ ~ChatSessionManager();
+
+ /**
+ * Create a new chat session. Provided is the initial list of contacts in
+ * the session. If a session with exactly these contacts already exists,
+ * it will be reused. Otherwise a new session is created.
+ * @param user The local user in the session.
+ * @param chatContacts The list of contacts taking part in the chat.
+ * @param protocol The protocol that the chat is using.
+ * @return A pointer to a new or reused Kopete::ChatSession.
+ */
+ Kopete::ChatSession* create( const Kopete::Contact *user,
+ Kopete::ContactPtrList chatContacts, Kopete::Protocol *protocol);
+
+ /**
+ * Find a chat session, if one exists, that matches the given list of contacts.
+ * @param user The local user in the session.
+ * @param chatContacts The list of contacts taking part in the chat.
+ * @param protocol The protocol that the chat is using.
+ * @return A pointer to an existing Kopete::ChatSession, or 0L if none was found.
+ */
+ Kopete::ChatSession* findChatSession( const Kopete::Contact *user,
+ Kopete::ContactPtrList chatContacts, Kopete::Protocol *protocol);
+
+ /**
+ * Registers a Kopete::ChatSession (or subclass thereof) with the Kopete::ChatSessionManager
+ */
+ void registerChatSession(Kopete::ChatSession *);
+
+ /**
+ * Get a list of all open sessions.
+ */
+ QValueList<ChatSession*> sessions();
+
+ /**
+ * @internal
+ * called by the kmm itself when it gets deleted
+ */
+ void removeSession( Kopete::ChatSession *session );
+
+ /**
+ * create a new view for the manager.
+ * only the manager should call this function
+ */
+ KopeteView *createView( Kopete::ChatSession * , const QString &requestedPlugin = QString::null );
+
+ /**
+ * Post a new event. this will emit the @ref newEvent signal
+ */
+ void postNewEvent(Kopete::MessageEvent*);
+
+ /**
+ * Returns the current active Kopete view
+ */
+ KopeteView *activeView();
+
+signals:
+ /**
+ * This signal is emitted whenever a message
+ * is about to be displayed by the KopeteChatWindow.
+ * Please remember that both messages sent and
+ * messages received will emit this signal!
+ * Plugins may connect to this signal to change
+ * the message contents before it's going to be displayed.
+ */
+ void aboutToDisplay( Kopete::Message& message );
+
+ /**
+ * Plugins may connect to this signal
+ * to manipulate the contents of the
+ * message that is being sent.
+ */
+ void aboutToSend( Kopete::Message& message );
+
+ /**
+ * Plugins may connect to this signal
+ * to manipulate the contents of the
+ * message that is being received.
+ *
+ * This signal is emitted before @ref aboutToDisplay()
+ */
+ void aboutToReceive( Kopete::Message& message );
+
+ /**
+ * A new view has been created
+ */
+ void viewCreated( KopeteView * );
+
+ /**
+ * A view as been activated(manually only?).
+ */
+ void viewActivated( KopeteView *view );
+
+ /*
+ * A view is about to close.
+ */
+ void viewClosing( KopeteView *view );
+
+ /**
+ * a new KMM has been created
+ */
+ void chatSessionCreated( Kopete::ChatSession *);
+
+ /**
+ * the message is ready to be displayed
+ */
+ void display( Kopete::Message& message, Kopete::ChatSession * );
+
+ /**
+ * A new event has been posted.
+ */
+ void newEvent(Kopete::MessageEvent *);
+
+ /**
+ * The global shortcut for sending message has been used
+ */
+ void readMessage();
+
+public slots:
+ void slotReadMessage();
+
+private:
+ ChatSessionManager( QObject* parent = 0, const char* name = 0 );
+
+ class Private;
+ Private *d;
+
+ static ChatSessionManager *s_self;
+
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetecommandhandler.cpp b/kopete/libkopete/kopetecommandhandler.cpp
new file mode 100644
index 00000000..b761ec08
--- /dev/null
+++ b/kopete/libkopete/kopetecommandhandler.cpp
@@ -0,0 +1,490 @@
+/*
+ kopetecommandhandler.cpp - Command Handler
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kapplication.h>
+#include <qregexp.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kdeversion.h>
+#include <kxmlguiclient.h>
+#include <kaction.h>
+#include <qdom.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteprotocol.h"
+#include "kopetepluginmanager.h"
+#include "kopeteview.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+#include "kopetecommandhandler.h"
+#include "kopetecontact.h"
+#include "kopetecommand.h"
+
+using Kopete::CommandList;
+
+typedef QMap<QObject*, CommandList> PluginCommandMap;
+typedef QMap<QString,QString> CommandMap;
+typedef QPair<Kopete::ChatSession*, Kopete::Message::MessageDirection> ManagerPair;
+
+class KopeteCommandGUIClient : public QObject, public KXMLGUIClient
+{
+ public:
+ KopeteCommandGUIClient( Kopete::ChatSession *manager ) : QObject(manager), KXMLGUIClient(manager)
+ {
+ setXMLFile( QString::fromLatin1("kopetecommandui.rc") );
+
+ QDomDocument doc = domDocument();
+ QDomNode menu = doc.documentElement().firstChild().firstChild().firstChild();
+ CommandList mCommands = Kopete::CommandHandler::commandHandler()->commands(
+ manager->protocol()
+ );
+
+ for( QDictIterator<Kopete::Command> it( mCommands ); it.current(); ++it )
+ {
+ KAction *a = static_cast<KAction*>( it.current() );
+ actionCollection()->insert( a );
+ QDomElement newNode = doc.createElement( QString::fromLatin1("Action") );
+ newNode.setAttribute( QString::fromLatin1("name"),
+ QString::fromLatin1( a->name() ) );
+
+ bool added = false;
+ for( QDomElement n = menu.firstChild().toElement();
+ !n.isNull(); n = n.nextSibling().toElement() )
+ {
+ if( QString::fromLatin1(a->name()) < n.attribute(QString::fromLatin1("name")))
+ {
+ menu.insertBefore( newNode, n );
+ added = true;
+ break;
+ }
+ }
+
+ if( !added )
+ {
+ menu.appendChild( newNode );
+ }
+ }
+
+ setDOMDocument( doc );
+ }
+};
+
+struct CommandHandlerPrivate
+{
+ PluginCommandMap pluginCommands;
+ Kopete::CommandHandler *s_handler;
+ QMap<KProcess*,ManagerPair> processMap;
+ bool inCommand;
+ QPtrList<KAction> m_commands;
+};
+
+CommandHandlerPrivate *Kopete::CommandHandler::p = 0L;
+
+Kopete::CommandHandler::CommandHandler() : QObject( qApp )
+{
+ p->s_handler = this;
+ p->inCommand = false;
+
+ CommandList mCommands(31, false);
+ mCommands.setAutoDelete( true );
+ p->pluginCommands.insert( this, mCommands );
+
+ registerCommand( this, QString::fromLatin1("help"), SLOT( slotHelpCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /help [<command>] - Used to list available commands, or show help for a specified command." ), 0, 1 );
+
+ registerCommand( this, QString::fromLatin1("close"), SLOT( slotCloseCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /close - Closes the current view." ) );
+
+ // FIXME: What's the difference with /close? The help doesn't explain it - Martijn
+ registerCommand( this, QString::fromLatin1("part"), SLOT( slotPartCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /part - Closes the current view." ) );
+
+ registerCommand( this, QString::fromLatin1("clear"), SLOT( slotClearCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /clear - Clears the active view's chat buffer." ) );
+
+ //registerCommand( this, QString::fromLatin1("me"), SLOT( slotMeCommand( const QString &, Kopete::ChatSession * ) ),
+ // i18n( "USAGE: /me <text> - Formats message as in '<nickname> went to the store'." ) );
+
+ registerCommand( this, QString::fromLatin1("away"), SLOT( slotAwayCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /away [<reason>] - Marks you as away/back for the current account only." ) );
+
+ registerCommand( this, QString::fromLatin1("awayall"), SLOT( slotAwayAllCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /awayall [<reason>] - Marks you as away/back for all accounts." ) );
+
+ registerCommand( this, QString::fromLatin1("say"), SLOT( slotSayCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /say <text> - Say text in this chat. This is the same as just typing a message, but is very "
+ "useful for scripts." ), 1 );
+
+ registerCommand( this, QString::fromLatin1("exec"), SLOT( slotExecCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n( "USAGE: /exec [-o] <command> - Executes the specified command and displays the output in the chat buffer. "
+ "If -o is specified, the output is sent to all members of the chat."), 1 );
+
+ connect( Kopete::PluginManager::self(), SIGNAL( pluginLoaded( Kopete::Plugin*) ),
+ this, SLOT(slotPluginLoaded(Kopete::Plugin*) ) );
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( viewCreated( KopeteView * ) ),
+ this, SLOT( slotViewCreated( KopeteView* ) ) );
+}
+
+Kopete::CommandHandler::~CommandHandler()
+{
+ delete p;
+}
+
+Kopete::CommandHandler *Kopete::CommandHandler::commandHandler()
+{
+ if( !p )
+ {
+ p = new CommandHandlerPrivate;
+ p->s_handler = new Kopete::CommandHandler();
+ }
+
+ return p->s_handler;
+}
+
+void Kopete::CommandHandler::registerCommand( QObject *parent, const QString &command, const char* handlerSlot,
+ const QString &help, uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix )
+{
+ QString lowerCommand = command.lower();
+
+ Kopete::Command *mCommand = new Kopete::Command( parent, lowerCommand, handlerSlot, help,
+ Normal, QString::null, minArgs, maxArgs, cut, pix);
+ p->pluginCommands[ parent ].insert( lowerCommand, mCommand );
+}
+
+void Kopete::CommandHandler::unregisterCommand( QObject *parent, const QString &command )
+{
+ if( p->pluginCommands[ parent ].find(command) )
+ p->pluginCommands[ parent ].remove( command );
+}
+
+void Kopete::CommandHandler::registerAlias( QObject *parent, const QString &alias, const QString &formatString,
+ const QString &help, CommandType type, uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix )
+{
+ QString lowerAlias = alias.lower();
+
+ Kopete::Command *mCommand = new Kopete::Command( parent, lowerAlias, 0L, help, type,
+ formatString, minArgs, maxArgs, cut, pix );
+ p->pluginCommands[ parent ].insert( lowerAlias, mCommand );
+}
+
+void Kopete::CommandHandler::unregisterAlias( QObject *parent, const QString &alias )
+{
+ if( p->pluginCommands[ parent ].find(alias) )
+ p->pluginCommands[ parent ].remove( alias );
+}
+
+bool Kopete::CommandHandler::processMessage( const QString &msg, Kopete::ChatSession *manager )
+{
+ if( p->inCommand )
+ return false;
+ QRegExp splitRx( QString::fromLatin1("^/([\\S]+)(.*)") );
+ QString command;
+ QString args;
+ if(splitRx.search(msg) != -1)
+ {
+ command = splitRx.cap(1);
+ args = splitRx.cap(2).mid(1);
+ }
+ else
+ return false;
+
+ CommandList mCommands = commands( manager->protocol() );
+ Kopete::Command *c = mCommands[ command ];
+ if(c)
+ {
+ kdDebug(14010) << k_funcinfo << "Handled Command" << endl;
+ if( c->type() != SystemAlias && c->type() != UserAlias )
+ p->inCommand = true;
+
+ c->processCommand( args, manager );
+ p->inCommand = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool Kopete::CommandHandler::processMessage( Kopete::Message &msg, Kopete::ChatSession *manager )
+{
+ QString messageBody = msg.plainBody();
+
+ return processMessage( messageBody, manager );
+}
+
+void Kopete::CommandHandler::slotHelpCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QString output;
+ if( args.isEmpty() )
+ {
+ int commandCount = 0;
+ output = i18n( "Available Commands:\n" );
+
+ CommandList mCommands = commands( manager->myself()->protocol() );
+ QDictIterator<Kopete::Command> it( mCommands );
+ for( ; it.current(); ++it )
+ {
+ output.append( it.current()->command().upper() + '\t' );
+ if( commandCount++ == 5 )
+ {
+ commandCount = 0;
+ output.append( '\n' );
+ }
+ }
+ output.append( i18n( "\nType /help <command> for more information." ) );
+ }
+ else
+ {
+ QString command = parseArguments( args ).front().lower();
+ Kopete::Command *c = commands( manager->myself()->protocol() )[ command ];
+ if( c && !c->help().isNull() )
+ output = c->help();
+ else
+ output = i18n("There is no help available for '%1'.").arg( command );
+ }
+
+ Kopete::Message msg(manager->myself(), manager->members(), output, Kopete::Message::Internal, Kopete::Message::PlainText);
+ manager->appendMessage(msg);
+}
+
+void Kopete::CommandHandler::slotSayCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ //Just say whatever is passed
+ Kopete::Message msg(manager->myself(), manager->members(), args,
+ Kopete::Message::Outbound, Kopete::Message::PlainText);
+ manager->sendMessage(msg);
+}
+
+void Kopete::CommandHandler::slotExecCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ if( !args.isEmpty() )
+ {
+ KProcess *proc = 0L;
+ if ( kapp->authorize( QString::fromLatin1( "shell_access" ) ) )
+ proc = new KProcess(manager);
+
+ if( proc )
+ {
+ *proc << QString::fromLatin1("sh") << QString::fromLatin1("-c");
+
+ QStringList argsList = parseArguments( args );
+ if( argsList.front() == QString::fromLatin1("-o") )
+ {
+ p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Outbound) );
+ *proc << args.section(QRegExp(QString::fromLatin1("\\s+")), 1);
+ }
+ else
+ {
+ p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Internal) );
+ *proc << args;
+ }
+
+ connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(slotExecReturnedData(KProcess *, char *, int)));
+ connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(slotExecReturnedData(KProcess *, char *, int)));
+ proc->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+ else
+ {
+ Kopete::Message msg(manager->myself(), manager->members(),
+ i18n( "ERROR: Shell access has been restricted on your system. The /exec command will not function." ),
+ Kopete::Message::Internal, Kopete::Message::PlainText );
+ manager->sendMessage( msg );
+ }
+ }
+}
+
+void Kopete::CommandHandler::slotClearCommand( const QString &, Kopete::ChatSession *manager )
+{
+ if( manager->view() )
+ manager->view()->clear();
+}
+
+void Kopete::CommandHandler::slotPartCommand( const QString &, Kopete::ChatSession *manager )
+{
+ if( manager->view() )
+ manager->view()->closeView();
+}
+
+void Kopete::CommandHandler::slotAwayCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ bool goAway = !manager->account()->isAway();
+
+ if( args.isEmpty() )
+ manager->account()->setAway( goAway );
+ else
+ manager->account()->setAway( goAway, args );
+}
+
+void Kopete::CommandHandler::slotAwayAllCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ if( manager->account()->isAway() )
+ Kopete::AccountManager::self()->setAvailableAll();
+
+ else
+ {
+ if( args.isEmpty() )
+ Kopete::AccountManager::self()->setAwayAll();
+ else
+ Kopete::AccountManager::self()->setAwayAll( args );
+ }
+}
+
+void Kopete::CommandHandler::slotCloseCommand( const QString &, Kopete::ChatSession *manager )
+{
+ if( manager->view() )
+ manager->view()->closeView();
+}
+
+void Kopete::CommandHandler::slotExecReturnedData(KProcess *proc, char *buff, int bufflen )
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ QString buffer = QString::fromLocal8Bit( buff, bufflen );
+ ManagerPair mgrPair = p->processMap[ proc ];
+ Kopete::Message msg( mgrPair.first->myself(), mgrPair.first->members(), buffer, mgrPair.second, Kopete::Message::PlainText );
+ if( mgrPair.second == Kopete::Message::Outbound )
+ mgrPair.first->sendMessage( msg );
+ else
+ mgrPair.first->appendMessage( msg );
+}
+
+void Kopete::CommandHandler::slotExecFinished(KProcess *proc)
+{
+ delete proc;
+ p->processMap.remove( proc );
+}
+
+QStringList Kopete::CommandHandler::parseArguments( const QString &args )
+{
+ QStringList arguments;
+ QRegExp quotedArgs( QString::fromLatin1("\"(.*)\"") );
+ quotedArgs.setMinimal( true );
+
+ if ( quotedArgs.search( args ) != -1 )
+ {
+ for( int i = 0; i< quotedArgs.numCaptures(); i++ )
+ arguments.append( quotedArgs.cap(i) );
+ }
+
+ QStringList otherArgs = QStringList::split( QRegExp(QString::fromLatin1("\\s+")), args.section( quotedArgs, 0 ) );
+ for( QStringList::Iterator it = otherArgs.begin(); it != otherArgs.end(); ++it )
+ arguments.append( *it );
+
+ return arguments;
+}
+
+bool Kopete::CommandHandler::commandHandled( const QString &command )
+{
+ for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
+ {
+ if( it.data()[ command ] )
+ return true;
+ }
+
+ return false;
+}
+
+bool Kopete::CommandHandler::commandHandledByProtocol( const QString &command, Kopete::Protocol *protocol )
+{
+ // Make sure the protocol is not NULL
+ if(!protocol)
+ return false;
+
+ // Fetch the commands for the protocol
+ CommandList commandList = commands( protocol );
+ QDictIterator<Kopete::Command> it ( commandList );
+
+ // Loop through commands and check if they match the supplied command
+ for( ; it.current(); ++it )
+ {
+ if( it.current()->command().lower() == command )
+ return true;
+ }
+
+ // No commands found
+ return false;
+}
+
+CommandList Kopete::CommandHandler::commands( Kopete::Protocol *protocol )
+{
+ CommandList commandList(63, false);
+
+ //Add plugin user aliases first
+ addCommands( p->pluginCommands[protocol], commandList, UserAlias );
+
+ //Add plugin system aliases next
+ addCommands( p->pluginCommands[protocol], commandList, SystemAlias );
+
+ //Add the commands for this protocol next
+ addCommands( p->pluginCommands[protocol], commandList );
+
+ //Add plugin commands
+ for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
+ {
+ if( !it.key()->inherits("Kopete::Protocol") && it.key()->inherits("Kopete::Plugin") )
+ addCommands( it.data(), commandList );
+ }
+
+ //Add global user aliases first
+ addCommands( p->pluginCommands[this], commandList, UserAlias );
+
+ //Add global system aliases next
+ addCommands( p->pluginCommands[this], commandList, SystemAlias );
+
+ //Add the internal commands *last*
+ addCommands( p->pluginCommands[this], commandList );
+
+ return commandList;
+}
+
+void Kopete::CommandHandler::addCommands( CommandList &from, CommandList &to, CommandType type )
+{
+ QDictIterator<Kopete::Command> itDict( from );
+ for( ; itDict.current(); ++itDict )
+ {
+ if( !to[ itDict.currentKey() ] &&
+ ( type == Undefined || itDict.current()->type() == type ) )
+ to.insert( itDict.currentKey(), itDict.current() );
+ }
+}
+
+void Kopete::CommandHandler::slotViewCreated( KopeteView *view )
+{
+ new KopeteCommandGUIClient( view->msgManager() );
+}
+
+void Kopete::CommandHandler::slotPluginLoaded( Kopete::Plugin *plugin )
+{
+ connect( plugin, SIGNAL( destroyed( QObject * ) ), this, SLOT( slotPluginDestroyed( QObject * ) ) );
+ if( !p->pluginCommands.contains( plugin ) )
+ {
+ //Create a QDict optomized for a larger # of commands, and case insensitive
+ CommandList mCommands(31, false);
+ mCommands.setAutoDelete( true );
+ p->pluginCommands.insert( plugin, mCommands );
+ }
+}
+
+void Kopete::CommandHandler::slotPluginDestroyed( QObject *plugin )
+{
+ p->pluginCommands.remove( static_cast<Kopete::Plugin*>(plugin) );
+}
+
+#include "kopetecommandhandler.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetecommandhandler.h b/kopete/libkopete/kopetecommandhandler.h
new file mode 100644
index 00000000..f763ace6
--- /dev/null
+++ b/kopete/libkopete/kopetecommandhandler.h
@@ -0,0 +1,219 @@
+/*
+ kopetecommandhandler.h - Command Handler
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETECOMMANDHANDLER_H_
+#define _KOPETECOMMANDHANDLER_H_
+
+#include <qdict.h>
+#include <kshortcut.h>
+#include "kopetemessage.h"
+
+#include "kopete_export.h"
+
+class KProcess;
+
+struct CommandHandlerPrivate;
+
+class KopeteView;
+class KopeteCommandGUIClient;
+
+namespace Kopete
+{
+
+class ChatSession;
+class Plugin;
+class Protocol;
+class Command;
+
+typedef QDict<Command> CommandList;
+
+/**
+ * @author Jason Keirstead <jason@keirstead.org>
+ *
+ * The Kopete::CommandHandler can handle /action like messages
+ */
+class KOPETE_EXPORT CommandHandler : public QObject
+{
+ friend class ::KopeteCommandGUIClient;
+
+ Q_OBJECT
+
+ public:
+ /**
+ * an enum defining the type of a command
+ */
+ enum CommandType { Normal, SystemAlias, UserAlias, Undefined };
+
+ /**
+ * Returns a pointer to the command handler
+ */
+ static CommandHandler *commandHandler();
+
+ /**
+ * \brief Register a command with the command handler.
+ *
+ * Command matching is case insensitive. All commands are registered,
+ * regardless of whether or not they are already handled by another
+ * handler. This is so that if the first plugin is unloaded, the next
+ * handler in the sequence will handle the command. However, there are
+ * certain commands which are reserved (internally handled by the
+ * Kopete::CommandHandler). These commands can also be overridden by
+ * registering a new duplicate command.
+ *
+ * @param parent The plugin who owns this command
+ * @param command The command we want to handle, not including the '/'
+ * @param handlerSlot The slot used to handle the command. This slot must
+ * accept two parameters, a QString of arguments, and a Kopete::ChatSession
+ * pointer to the manager under which the command was sent.
+ * @param help An optional help string to be shown when the user uses
+ * /help \<command\>
+ * @param minArgs the minimum number of arguments for this command
+ * @param maxArgs the maximum number of arguments this command takes
+ * @param cut a default keyboard shortcut
+ * @param pix icon name, the icon will be shown in menus
+ */
+ void registerCommand( QObject *parent, const QString &command, const char* handlerSlot,
+ const QString &help = QString::null, uint minArgs = 0, int maxArgs = -1,
+ const KShortcut &cut = 0, const QString &pix = QString::null );
+
+ /**
+ * \brief Register a command alias.
+ *
+ * @param parent The plugin who owns this alias
+ * @param alias The command for the alias
+ * @param formatString This is the string that will be transformed into another
+ * command. The formatString should begin with an already existing command,
+ * followed by any other arguments. The variables %1, %2... %9 will be substituted
+ * with the arguments passed into the alias. The variable %s will be substituted with
+ * the entire argument string
+ * @param help An optional help string to be shown when the user uses
+ * /help \<command\>
+ * @param minArgs the minimum number of arguments for this command
+ * @param maxArgs the maximum number of arguments this command takes
+ * @param cut a default keyboard shortcut
+ * @param pix icon name, the icon will be shown in menus
+ */
+ void registerAlias( QObject *parent,
+ const QString &alias,
+ const QString &formatString,
+ const QString &help = QString::null,
+ CommandType = SystemAlias,
+ uint minArgs = 0,
+ int maxArgs = -1,
+ const KShortcut &cut = 0,
+ const QString &pix = QString::null );
+
+ /**
+ * \brief Unregister a command.
+ *
+ * When a plugin unloads, all commands are automaticlly unregistered and deleted.
+ * This function should only be called in the case of a plugin which loads and
+ * unloads commands dynamically.
+ *
+ * @param parent The plugin who owns this command
+ * @param command The command to unload
+ */
+ void unregisterCommand( QObject *parent, const QString &command );
+
+ /**
+ * \brief Unregister an alias.
+ *
+ * \see unregisterCommand( QObject *parent, const QString &command )
+ * @param parent The plugin who owns this alias
+ * @param alias The alais to unload
+ */
+ void unregisterAlias( QObject *parent, const QString &alias );
+
+ /**
+ * \brief Process a message to see if any commands should be handled
+ *
+ * @param msg The message to process
+ * @param manager The manager who owns this message
+ * @return True if the command was handled, false if not
+ */
+ bool processMessage( Message &msg, ChatSession *manager );
+
+ /**
+ * \brief Process a message to see if any commands should be handled
+ *
+ * \see processMessage( Kopete::Message &msg, Kopete::ChatSession *manager)
+ * \param msg A QString contain the message
+ * \param manager the Kopete::ChatSession who will own the message
+ * \return true if the command was handled, false if the command was not handled.
+ */
+ bool processMessage( const QString &msg, ChatSession *manager );
+
+ /**
+ * Parses a string of command arguments into a QStringList. Quoted
+ * blocks within the arguments string are treated as one argument.
+ */
+ static QStringList parseArguments( const QString &args );
+
+ /**
+ * \brief Check if a command is already handled
+ *
+ * @param command The command to check
+ * @return True if the command is already being handled, False if not
+ */
+ bool commandHandled( const QString &command );
+
+ /**
+ * \brief Check if a command is already handled by a spesific protocol
+ *
+ * @param command The command to check
+ * @param protocol The protocol to check
+ * @return True if the command is already being handled, False if not
+ */
+ bool commandHandledByProtocol( const QString &command, Protocol *protocol);
+
+ private slots:
+ void slotPluginLoaded( Kopete::Plugin * );
+ void slotPluginDestroyed( QObject * );
+ void slotExecReturnedData(KProcess *proc, char *buff, int bufflen );
+ void slotExecFinished(KProcess *proc);
+ void slotViewCreated( KopeteView *view );
+
+ void slotHelpCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotClearCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotPartCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotCloseCommand( const QString & args, Kopete::ChatSession *manager );
+ //void slotMeCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotExecCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotAwayCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotAwayAllCommand( const QString & args, Kopete::ChatSession *manager );
+ void slotSayCommand( const QString & args, Kopete::ChatSession *manager );
+
+ private:
+ /**
+ * Helper function. Returns all the commands that can be used by a KMM of this protocol
+ * (all non-protocol commands, plus this protocols commands)
+ */
+ CommandList commands( Protocol * );
+
+ /**
+ * Helper function for commands()
+ */
+ void addCommands( CommandList &from, CommandList &to, CommandType type = Undefined );
+
+ CommandHandler();
+ ~CommandHandler();
+
+ static CommandHandlerPrivate *p;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopetecommandui.rc b/kopete/libkopete/kopetecommandui.rc
new file mode 100644
index 00000000..8cd10680
--- /dev/null
+++ b/kopete/libkopete/kopetecommandui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="2" name="kopetechatwindow">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <Menu name="commandmenu">
+ <text>Commands</text>
+ <ActionList name="commandactionlist" />
+ </Menu>
+ </Menu>
+ </MenuBar>
+
+</kpartgui>
diff --git a/kopete/libkopete/kopeteconfig.kcfgc b/kopete/libkopete/kopeteconfig.kcfgc
new file mode 100644
index 00000000..86dbae8e
--- /dev/null
+++ b/kopete/libkopete/kopeteconfig.kcfgc
@@ -0,0 +1,13 @@
+ClassName=Config
+File=kopete.kcfg
+GlobalEnums=false
+Inherits=KConfigSkeleton
+ItemAccessors=true
+MemberVariables=private
+Mutators=true
+NameSpace=Kopete
+SetUserTexts=false
+Singleton=true
+Visibility=KOPETE_EXPORT
+IncludeFiles=kopete_export.h
+
diff --git a/kopete/libkopete/kopetecontact.cpp b/kopete/libkopete/kopetecontact.cpp
new file mode 100644
index 00000000..15cb27df
--- /dev/null
+++ b/kopete/libkopete/kopetecontact.cpp
@@ -0,0 +1,863 @@
+/*
+ kopetecontact.cpp - Kopete Contact
+
+ Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontact.h"
+
+#include <qapplication.h>
+
+#include <kdebug.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+
+#include <kabcpersistence.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <klistviewsearchline.h>
+
+#include "kopetecontactlist.h"
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetestdaction.h"
+#include "kopetechatsession.h"
+#include "kopeteview.h"
+#include "kopetemetacontact.h"
+#include "kopeteprefs.h"
+#include "metacontactselectorwidget.h"
+#include "kopeteemoticons.h"
+
+//For the moving to another metacontact dialog
+#include <qlabel.h>
+#include <qimage.h>
+#include <qmime.h>
+#include <qvbox.h>
+#include <klistview.h>
+#include <qcheckbox.h>
+#include <qwhatsthis.h>
+#include <qstylesheet.h>
+
+namespace Kopete {
+
+struct Contact::Private
+{
+public:
+ bool fileCapable;
+
+ OnlineStatus onlineStatus;
+ Account *account;
+
+ MetaContact *metaContact;
+
+ QString contactId;
+ QString icon;
+
+ QTime idleTimer;
+ unsigned long int idleTime;
+
+ Kopete::ContactProperty::Map properties;
+
+};
+
+Contact::Contact( Account *account, const QString &contactId,
+ MetaContact *parent, const QString &icon )
+ : QObject( parent )
+{
+ d = new Private;
+
+ //kdDebug( 14010 ) << k_funcinfo << "Creating contact with id " << contactId << endl;
+
+ d->contactId = contactId;
+ d->metaContact = parent;
+ d->fileCapable = false;
+ d->account = account;
+ d->idleTime = 0;
+ d->icon = icon;
+
+ // If can happend that a MetaContact may be used without a account
+ // (ex: for unit tests or chat window style preview)
+ if ( account )
+ {
+ account->registerContact( this );
+ connect( account, SIGNAL( isConnectedChanged() ), SLOT( slotAccountIsConnectedChanged() ) );
+ }
+
+ // Need to check this because myself() may have no parent
+ // Maybe too the metaContact doesn't have a valid protocol()
+ // (ex: for unit tests or chat window style preview)
+ if( parent && protocol() )
+ {
+ connect( parent, SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
+ protocol(), SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );
+
+ parent->addContact( this );
+ }
+
+
+}
+
+Contact::~Contact()
+{
+ //kdDebug(14010) << k_funcinfo << endl;
+ emit( contactDestroyed( this ) );
+ delete d;
+}
+
+
+
+OnlineStatus Contact::onlineStatus() const
+{
+ if ( this == account()->myself() || account()->isConnected() )
+ return d->onlineStatus;
+ else
+ return protocol()->accountOfflineStatus();
+}
+
+void Contact::setOnlineStatus( const OnlineStatus &status )
+{
+ if( status == d->onlineStatus )
+ return;
+
+ OnlineStatus oldStatus = d->onlineStatus;
+ d->onlineStatus = status;
+
+ Kopete::Global::Properties *globalProps = Kopete::Global::Properties::self();
+
+ // Contact changed from Offline to another status
+ if( oldStatus.status() == OnlineStatus::Offline &&
+ status.status() != OnlineStatus::Offline )
+ {
+ setProperty( globalProps->onlineSince(), QDateTime::currentDateTime() );
+ /*kdDebug(14010) << k_funcinfo << "REMOVING lastSeen property for " <<
+ d->displayName << endl;*/
+ removeProperty( globalProps->lastSeen() );
+ }
+ else if( oldStatus.status() != OnlineStatus::Offline &&
+ oldStatus.status() != OnlineStatus::Unknown &&
+ status.status() == OnlineStatus::Offline ) // Contact went back offline
+ {
+ removeProperty( globalProps->onlineSince() );
+ /*kdDebug(14010) << k_funcinfo << "SETTING lastSeen property for " <<
+ d->displayName << endl;*/
+ setProperty( globalProps->lastSeen(), QDateTime::currentDateTime() );
+ }
+
+ if ( this == account()->myself() || account()->isConnected() )
+ emit onlineStatusChanged( this, status, oldStatus );
+}
+
+void Contact::slotAccountIsConnectedChanged()
+{
+ if ( this == account()->myself() )
+ return;
+
+ if ( account()->isConnected() )
+ emit onlineStatusChanged( this, d->onlineStatus, protocol()->accountOfflineStatus() );
+ else
+ emit onlineStatusChanged( this, protocol()->accountOfflineStatus(), d->onlineStatus );
+}
+
+
+void Contact::sendFile( const KURL &, const QString &, uint )
+{
+ kdWarning( 14010 ) << k_funcinfo << "Plugin "
+ << protocol()->pluginId() << " has enabled file sending, "
+ << "but didn't implement it!" << endl;
+}
+
+void Contact::slotAddContact()
+{
+ if( metaContact() )
+ {
+ metaContact()->setTemporary( false );
+ ContactList::self()->addMetaContact( metaContact() );
+ }
+}
+
+KPopupMenu* Contact::popupMenu( ChatSession *manager )
+{
+ // Build the menu
+ KPopupMenu *menu = new KPopupMenu();
+
+ // insert title
+ QString titleText;
+ QString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if( nick.isEmpty() )
+ titleText = QString::fromLatin1( "%1 (%2)" ).arg( contactId(), onlineStatus().description() );
+ else
+ titleText = QString::fromLatin1( "%1 <%2> (%3)" ).arg( nick, contactId(), onlineStatus().description() );
+ menu->insertTitle( titleText );
+
+ if( metaContact() && metaContact()->isTemporary() && contactId() != account()->myself()->contactId() )
+ {
+ KAction *actionAddContact = new KAction( i18n( "&Add to Your Contact List" ), QString::fromLatin1( "add_user" ),
+ 0, this, SLOT( slotAddContact() ), menu, "actionAddContact" );
+ actionAddContact->plug( menu );
+ menu->insertSeparator();
+ }
+
+ // FIXME: After KDE 3.2 we should make isReachable do the isConnected call so it can be removed here - Martijn
+ bool reach = account()->isConnected() && isReachable();
+ bool myself = (this == account()->myself());
+
+ KAction *actionSendMessage = KopeteStdAction::sendMessage( this, SLOT( sendMessage() ), menu, "actionSendMessage" );
+ actionSendMessage->setEnabled( reach && !myself );
+ actionSendMessage->plug( menu );
+
+ KAction *actionChat = KopeteStdAction::chat( this, SLOT( startChat() ), menu, "actionChat" );
+ actionChat->setEnabled( reach && !myself );
+ actionChat->plug( menu );
+
+ KAction *actionSendFile = KopeteStdAction::sendFile( this, SLOT( sendFile() ), menu, "actionSendFile" );
+ actionSendFile->setEnabled( reach && d->fileCapable && !myself );
+ actionSendFile->plug( menu );
+
+ // Protocol specific options will go below this separator
+ // through the use of the customContextMenuActions() function
+
+ // Get the custom actions from the protocols ( pure virtual function )
+ QPtrList<KAction> *customActions = customContextMenuActions( manager );
+ if( customActions && !customActions->isEmpty() )
+ {
+ menu->insertSeparator();
+
+ for( KAction *a = customActions->first(); a; a = customActions->next() )
+ a->plug( menu );
+ }
+ delete customActions;
+
+ menu->insertSeparator();
+
+ if( metaContact() && !metaContact()->isTemporary() )
+ KopeteStdAction::changeMetaContact( this, SLOT( changeMetaContact() ), menu, "actionChangeMetaContact" )->plug( menu );
+
+ KopeteStdAction::contactInfo( this, SLOT( slotUserInfo() ), menu, "actionUserInfo" )->plug( menu );
+
+#if 0 //this is not fully implemented yet (and doesn't work). disable for now - Olivier 2005-01-11
+ if ( account()->isBlocked( d->contactId ) )
+ KopeteStdAction::unblockContact( this, SLOT( slotUnblock() ), menu, "actionUnblockContact" )->plug( menu );
+ else
+ KopeteStdAction::blockContact( this, SLOT( slotBlock() ), menu, "actionBlockContact" )->plug( menu );
+#endif
+
+ if( metaContact() && !metaContact()->isTemporary() )
+ KopeteStdAction::deleteContact( this, SLOT( slotDelete() ), menu, "actionDeleteContact" )->plug( menu );
+
+ return menu;
+}
+
+void Contact::changeMetaContact()
+{
+ KDialogBase *moveDialog = new KDialogBase( Kopete::UI::Global::mainWidget(), "moveDialog", true, i18n( "Move Contact" ),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );
+
+ QVBox *w = new QVBox( moveDialog );
+ w->setSpacing( KDialog::spacingHint() );
+ Kopete::UI::MetaContactSelectorWidget *selector = new Kopete::UI::MetaContactSelectorWidget(w);
+ selector->setLabelMessage(i18n( "Select the meta contact to which you want to move this contact:" ));
+ // exclude this metacontact as a target metacontact for the move
+ selector->excludeMetaContact( metaContact() );
+ QCheckBox *chkCreateNew = new QCheckBox( i18n( "Create a new metacontact for this contact" ), w );
+ QWhatsThis::add( chkCreateNew , i18n( "If you select this option, a new metacontact will be created in the top-level group "
+ "with the name of this contact and the contact will be moved to it." ) );
+ QObject::connect( chkCreateNew , SIGNAL( toggled(bool) ) , selector , SLOT ( setDisabled(bool) ) ) ;
+
+ moveDialog->setMainWidget(w);
+ if( moveDialog->exec() == QDialog::Accepted )
+ {
+ Kopete::MetaContact *mc = selector->metaContact();
+ if(chkCreateNew->isChecked())
+ {
+ mc=new Kopete::MetaContact();
+ Kopete::ContactList::self()->addMetaContact(mc);
+ }
+ if( mc )
+ {
+ setMetaContact( mc );
+ }
+ }
+
+ moveDialog->deleteLater();
+}
+
+void Contact::setMetaContact( MetaContact *m )
+{
+ MetaContact *old = d->metaContact;
+ if(old==m) //that make no sens
+ return;
+
+ if( old )
+ {
+ int result=KMessageBox::No;
+ if( old->isTemporary() )
+ result=KMessageBox::Yes;
+ else if( old->contacts().count()==1 )
+ { //only one contact, including this one, that mean the contact will be empty efter the move
+ result = KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget(), i18n( "You are moving the contact `%1' to the meta contact `%2'.\n"
+ "`%3' will be empty afterwards. Do you want to delete this contact?" )
+ .arg(contactId(), m ? m->displayName() : QString::null, old->displayName())
+ , i18n( "Move Contact" ), KStdGuiItem::del(), i18n( "&Keep" ) , QString::fromLatin1("delete_old_contact_when_move") );
+ if(result==KMessageBox::Cancel)
+ return;
+ }
+ old->removeContact( this );
+ disconnect( old, SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
+ protocol(), SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );
+
+ if(result==KMessageBox::Yes)
+ {
+ //remove the old metacontact. (this delete the MC)
+ ContactList::self()->removeMetaContact(old);
+ }
+ else
+ {
+ d->metaContact = m; //i am forced to do that now if i want the next line works
+ //remove cached data for this protocol which will not be removed since we disconnected
+ protocol()->slotMetaContactAboutToSave( old );
+ }
+ }
+
+ d->metaContact = m;
+
+ if( m )
+ {
+ m->addContact( this );
+ m->insertChild( this );
+ // it is necessary to call this write here, because MetaContact::addContact() does not differentiate
+ // between adding completely new contacts (which should be written to kabc) and restoring upon restart
+ // (where no write is needed).
+ KABCPersistence::self()->write( m );
+ connect( d->metaContact, SIGNAL( aboutToSave( Kopete::MetaContact * ) ),
+ protocol(), SLOT( slotMetaContactAboutToSave( Kopete::MetaContact * ) ) );
+ }
+ sync();
+}
+
+void Contact::serialize( QMap<QString, QString> &/*serializedData*/,
+ QMap<QString, QString> & /* addressBookData */ )
+{
+}
+
+
+void Contact::serializeProperties(QMap<QString, QString> &serializedData)
+{
+
+ Kopete::ContactProperty::Map::ConstIterator it;// = d->properties.ConstIterator;
+ for (it=d->properties.begin(); it != d->properties.end(); ++it)
+ {
+ if (!it.data().tmpl().persistent())
+ continue;
+
+ QVariant val = it.data().value();
+ QString key = QString::fromLatin1("prop_%1_%2").arg(QString::fromLatin1(val.typeName()), it.key());
+
+ serializedData[key] = val.toString();
+
+ } // end for()
+} // end serializeProperties()
+
+void Contact::deserializeProperties(
+ QMap<QString, QString> &serializedData )
+{
+ QMap<QString, QString>::ConstIterator it;
+ for ( it=serializedData.begin(); it != serializedData.end(); ++it )
+ {
+ QString key = it.key();
+
+ if ( !key.startsWith( QString::fromLatin1("prop_") ) ) // avoid parsing other serialized data
+ continue;
+
+ QStringList keyList = QStringList::split( QChar('_'), key, false );
+ if( keyList.count() < 3 ) // invalid key, not enough parts in string "prop_X_Y"
+ continue;
+
+ key = keyList[2]; // overwrite key var with the real key name this property has
+ QString type( keyList[1] ); // needed for QVariant casting
+
+ QVariant variant( it.data() );
+ if( !variant.cast(QVariant::nameToType(type.latin1())) )
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "Casting QVariant to needed type FAILED" <<
+ "key=" << key << ", type=" << type << endl;
+ continue;
+ }
+
+ Kopete::ContactPropertyTmpl tmpl = Kopete::Global::Properties::self()->tmpl(key);
+ if( tmpl.isNull() )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "no ContactPropertyTmpl defined for" \
+ " key " << key << ", cannot restore persistent property" << endl;
+ continue;
+ }
+
+ setProperty(tmpl, variant);
+ } // end for()
+}
+
+
+bool Contact::isReachable()
+{
+ // The default implementation returns false when offline and true
+ // otherwise. Subclass if you need more control over the process.
+ return onlineStatus().status() != OnlineStatus::Offline;
+}
+
+
+void Contact::startChat()
+{
+ KopeteView *v=manager( CanCreate )->view(true, QString::fromLatin1("kopete_chatwindow") );
+ if(v)
+ v->raise(true);
+}
+
+void Contact::sendMessage()
+{
+ KopeteView *v=manager( CanCreate )->view(true, QString::fromLatin1("kopete_emailwindow") );
+ if(v)
+ v->raise(true);
+}
+
+void Contact::execute()
+{
+ // FIXME: After KDE 3.2 remove the isConnected check and move it to isReachable - Martijn
+ if ( account()->isConnected() && isReachable() )
+ {
+ KopeteView *v=manager( CanCreate )->view(true, KopetePrefs::prefs()->interfacePreference() );
+ if(v)
+ v->raise(true);
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "This user is not reachable at the moment. Please try a protocol that supports offline sending, or wait "
+ "until this user comes online." ), i18n( "User is Not Reachable" ) );
+ }
+}
+
+void Contact::slotDelete()
+{
+ if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(),
+ i18n( "Are you sure you want to remove the contact '%1' from your contact list?" ).
+ arg( d->contactId ), i18n( "Remove Contact" ), KGuiItem(i18n("Remove"), QString::fromLatin1("delete_user") ),
+ QString::fromLatin1("askRemoveContact"), KMessageBox::Notify | KMessageBox::Dangerous )
+ == KMessageBox::Continue )
+ {
+ deleteContact();
+ }
+}
+
+void Contact::deleteContact()
+{
+ // Default implementation simply deletes the contact
+ deleteLater();
+}
+
+
+MetaContact * Contact::metaContact() const
+{
+ return d->metaContact;
+}
+
+QString Contact::contactId() const
+{
+ return d->contactId;
+}
+
+Protocol * Contact::protocol() const
+{
+ return d->account ? d->account->protocol() : 0L;
+}
+
+Account * Contact::account() const
+{
+ return d->account;
+}
+
+
+
+void Contact::sync(unsigned int)
+{
+ /* Default implementation does nothing */
+}
+
+QString& Contact::icon() const
+{
+ return d->icon;
+}
+
+void Contact::setIcon( const QString& icon )
+{
+ d->icon = icon;
+ return;
+}
+
+QPtrList<KAction> *Contact::customContextMenuActions()
+{
+ return 0L;
+}
+
+QPtrList<KAction> *Contact::customContextMenuActions( ChatSession * /* manager */ )
+{
+ return customContextMenuActions();
+}
+
+
+bool Contact::isOnline() const
+{
+ return onlineStatus().isDefinitelyOnline();
+}
+
+
+bool Contact::isFileCapable() const
+{
+ return d->fileCapable;
+}
+
+void Contact::setFileCapable( bool filecap )
+{
+ d->fileCapable = filecap;
+}
+
+
+bool Contact::canAcceptFiles() const
+{
+ return isOnline() && d->fileCapable;
+}
+
+unsigned long int Contact::idleTime() const
+{
+ if(d->idleTime==0)
+ return 0;
+
+ return d->idleTime+(d->idleTimer.elapsed()/1000);
+}
+
+void Contact::setIdleTime( unsigned long int t )
+{
+ bool idleChanged = false;
+ if(d->idleTime != t)
+ idleChanged = true;
+ d->idleTime=t;
+ if(t > 0)
+ d->idleTimer.start();
+//FIXME: if t == 0, idleTime() will now return garbage
+// else
+// d->idleTimer.stop();
+ if(idleChanged)
+ emit idleStateChanged(this);
+}
+
+
+QStringList Contact::properties() const
+{
+ return d->properties.keys();
+}
+
+bool Contact::hasProperty(const QString &key) const
+{
+ return d->properties.contains(key);
+}
+
+const ContactProperty &Contact::property(const QString &key) const
+{
+ if(hasProperty(key))
+ return d->properties[key];
+ else
+ return Kopete::ContactProperty::null;
+}
+
+const Kopete::ContactProperty &Contact::property(
+ const Kopete::ContactPropertyTmpl &tmpl) const
+{
+ if(hasProperty(tmpl.key()))
+ return d->properties[tmpl.key()];
+ else
+ return Kopete::ContactProperty::null;
+}
+
+
+void Contact::setProperty(const Kopete::ContactPropertyTmpl &tmpl,
+ const QVariant &value)
+{
+ if(tmpl.isNull() || tmpl.key().isEmpty())
+ {
+ kdDebug(14000) << k_funcinfo <<
+ "No valid template for property passed!" << endl;
+ return;
+ }
+
+ if(value.isNull() || value.canCast(QVariant::String) && value.toString().isEmpty())
+ {
+ removeProperty(tmpl);
+ }
+ else
+ {
+ QVariant oldValue = property(tmpl.key()).value();
+
+ if(oldValue != value)
+ {
+ Kopete::ContactProperty prop(tmpl, value);
+ d->properties.insert(tmpl.key(), prop, true);
+
+ emit propertyChanged(this, tmpl.key(), oldValue, value);
+ }
+ }
+}
+
+void Contact::removeProperty(const Kopete::ContactPropertyTmpl &tmpl)
+{
+ if(!tmpl.isNull() && !tmpl.key().isEmpty())
+ {
+
+ QVariant oldValue = property(tmpl.key()).value();
+ d->properties.remove(tmpl.key());
+ emit propertyChanged(this, tmpl.key(), oldValue, QVariant());
+ }
+}
+
+
+QString Contact::toolTip() const
+{
+ Kopete::ContactProperty p;
+ QString tip;
+ QStringList shownProps = KopetePrefs::prefs()->toolTipContents();
+
+ // --------------------------------------------------------------------------
+ // Fixed part of tooltip
+
+ QString iconName = QString::fromLatin1("kopete-contact-icon:%1:%2:%3")
+ .arg( KURL::encode_string( protocol()->pluginId() ),
+ KURL::encode_string( account()->accountId() ),
+ KURL::encode_string( contactId() ) );
+
+ // TODO: the nickname should be a configurable properties, like others. -Olivier
+ QString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if ( nick.isEmpty() )
+ {
+ tip = i18n( "<b>DISPLAY NAME</b><br><img src=\"%2\">&nbsp;CONTACT STATUS",
+ "<b><nobr>%3</nobr></b><br><img src=\"%2\">&nbsp;%1" ).
+ arg( Kopete::Message::escape( onlineStatus().description() ), iconName,
+ Kopete::Message::escape( d->contactId ) );
+ }
+ else
+ {
+ tip = i18n( "<b>DISPLAY NAME</b> (CONTACT ID)<br><img src=\"%2\">&nbsp;CONTACT STATUS",
+ "<nobr><b>%4</b> (%3)</nobr><br><img src=\"%2\">&nbsp;%1" ).
+ arg( Kopete::Message::escape( onlineStatus().description() ), iconName,
+ Kopete::Message::escape( contactId() ),
+ Kopete::Emoticons::parseEmoticons( Kopete::Message::escape( nick ) ) );
+ }
+
+ // --------------------------------------------------------------------------
+ // Configurable part of tooltip
+
+ for(QStringList::Iterator it=shownProps.begin(); it!=shownProps.end(); ++it)
+ {
+ if((*it) == QString::fromLatin1("FormattedName"))
+ {
+ QString name = formattedName();
+ if(!name.isEmpty())
+ {
+ tip += i18n("<br><b>Full Name:</b>&nbsp;FORMATTED NAME",
+ "<br><b>Full Name:</b>&nbsp;<nobr>%1</nobr>").arg(QStyleSheet::escape(name));
+ }
+ }
+ else if ((*it) == QString::fromLatin1("FormattedIdleTime"))
+ {
+ QString time = formattedIdleTime();
+ if(!time.isEmpty())
+ {
+ tip += i18n("<br><b>Idle:</b>&nbsp;FORMATTED IDLE TIME",
+ "<br><b>Idle:</b>&nbsp;<nobr>%1</nobr>").arg(time);
+ }
+ }
+ else if ((*it) == QString::fromLatin1("homePage"))
+ {
+ QString url = property(*it).value().toString();
+ if(!url.isEmpty())
+ {
+ tip += i18n("<br><b>Home Page:</b>&nbsp;FORMATTED URL",
+ "<br><b>Home Page:</b>&nbsp;<a href=\"%1\"><nobr>%2</nobr></a>").
+ arg( KURL::encode_string( url ), Kopete::Message::escape( QStyleSheet::escape(url) ) );
+ }
+ }
+ else if ((*it) == QString::fromLatin1("awayMessage"))
+ {
+ QString awaymsg = property(*it).value().toString();
+ if(!awaymsg.isEmpty())
+ {
+ tip += i18n("<br><b>Away Message:</b>&nbsp;FORMATTED AWAY MESSAGE",
+ "<br><b>Away&nbsp;Message:</b>&nbsp;%1").arg ( Kopete::Emoticons::parseEmoticons( Kopete::Message::escape(awaymsg) ) );
+ }
+ }
+ else
+ {
+ p = property(*it);
+ if(!p.isNull())
+ {
+ QVariant val = p.value();
+ QString valueText;
+
+ switch(val.type())
+ {
+ case QVariant::DateTime:
+ valueText = KGlobal::locale()->formatDateTime(val.toDateTime());
+ valueText = Kopete::Message::escape( valueText );
+ break;
+ case QVariant::Date:
+ valueText = KGlobal::locale()->formatDate(val.toDate());
+ valueText = Kopete::Message::escape( valueText );
+ break;
+ case QVariant::Time:
+ valueText = KGlobal::locale()->formatTime(val.toTime());
+ valueText = Kopete::Message::escape( valueText );
+ break;
+ default:
+ if( p.isRichText() )
+ {
+ valueText = val.toString();
+ }
+ else
+ {
+ valueText = Kopete::Message::escape( val.toString() );
+ }
+ }
+
+ tip += i18n("<br><b>PROPERTY LABEL:</b>&nbsp;PROPERTY VALUE",
+ "<br><nobr><b>%2:</b></nobr>&nbsp;%1").
+ arg( valueText, QStyleSheet::escape(p.tmpl().label()) );
+ }
+ }
+ }
+
+ return tip;
+}
+
+QString Kopete::Contact::formattedName() const
+{
+ if( hasProperty(QString::fromLatin1("FormattedName")) )
+ return property(QString::fromLatin1("FormattedName")).value().toString();
+
+ QString ret;
+ Kopete::ContactProperty first, last;
+
+ first = property(QString::fromLatin1("firstName"));
+ last = property(QString::fromLatin1("lastName"));
+ if(!first.isNull())
+ {
+ if(!last.isNull()) // contact has both first and last name
+ {
+ ret = i18n("firstName lastName", "%2 %1")
+ .arg(last.value().toString())
+ .arg(first.value().toString());
+ }
+ else // only first name set
+ {
+ ret = first.value().toString();
+ }
+ }
+ else if(!last.isNull()) // only last name set
+ {
+ ret = last.value().toString();
+ }
+
+ return ret;
+}
+
+QString Kopete::Contact::formattedIdleTime() const
+{
+ QString ret;
+ unsigned long int leftTime = idleTime();
+
+ if ( leftTime > 0 )
+ { // FIXME: duplicated from code in kopetecontactlistview.cpp
+ unsigned long int days, hours, mins, secs;
+
+ days = leftTime / ( 60*60*24 );
+ leftTime = leftTime % ( 60*60*24 );
+ hours = leftTime / ( 60*60 );
+ leftTime = leftTime % ( 60*60 );
+ mins = leftTime / 60;
+ secs = leftTime % 60;
+
+ if ( days != 0 )
+ {
+ ret = i18n( "<days>d <hours>h <minutes>m <seconds>s",
+ "%4d %3h %2m %1s" )
+ .arg( secs )
+ .arg( mins )
+ .arg( hours )
+ .arg( days );
+ }
+ else if ( hours != 0 )
+ {
+ ret = i18n( "<hours>h <minutes>m <seconds>s", "%3h %2m %1s" )
+ .arg( secs )
+ .arg( mins )
+ .arg( hours );
+ }
+ else
+ {
+ ret = i18n( "<minutes>m <seconds>s", "%2m %1s" )
+ .arg( secs )
+ .arg( mins );
+ }
+ }
+ return ret;
+}
+
+
+void Kopete::Contact::slotBlock()
+{
+ account()->block( d->contactId );
+}
+
+void Kopete::Contact::slotUnblock()
+{
+ account()->unblock( d->contactId );
+}
+
+void Kopete::Contact::setNickName( const QString &name )
+{
+ setProperty( Kopete::Global::Properties::self()->nickName(), name );
+}
+
+QString Kopete::Contact::nickName() const
+{
+ QString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if( !nick.isEmpty() )
+ return nick;
+
+ return contactId();
+}
+
+void Contact::virtual_hook( uint , void * )
+{ }
+
+
+} //END namespace Kopete
+
+
+#include "kopetecontact.moc"
+
+
diff --git a/kopete/libkopete/kopetecontact.h b/kopete/libkopete/kopetecontact.h
new file mode 100644
index 00000000..8f02bfc2
--- /dev/null
+++ b/kopete/libkopete/kopetecontact.h
@@ -0,0 +1,557 @@
+/*
+ kopetecontact.h - Kopete Contact
+
+ Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __KOPETECONTACT_H__
+#define __KOPETECONTACT_H__
+
+#include <qobject.h>
+#include <kurl.h>
+#include <kdemacros.h>
+#include "kopeteglobal.h"
+
+#include "kopete_export.h"
+
+class QImage;
+class KPopupMenu;
+class KAction;
+
+namespace Kopete
+{
+
+class Group;
+class MetaContact;
+class ChatSession;
+class OnlineStatus;
+class Plugin;
+class Protocol;
+class Account;
+typedef QPtrList<Group> GroupList;
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart@ tiscalinet.be>
+ *
+ * This class abstracts a generic contact
+ * Use it for inserting contacts in the contact list for example.
+ */
+class KOPETE_EXPORT Contact : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS( CanCreateFlags )
+ Q_PROPERTY( QString formattedName READ formattedName )
+ Q_PROPERTY( QString formattedIdleTime READ formattedIdleTime )
+ Q_PROPERTY( bool isOnline READ isOnline )
+ Q_PROPERTY( bool fileCapable READ isFileCapable WRITE setFileCapable )
+ Q_PROPERTY( bool canAcceptFiles READ canAcceptFiles )
+ //Q_PROPERTY( bool isReachable READ isReachable )
+ Q_PROPERTY( QString contactId READ contactId )
+ Q_PROPERTY( QString icon READ icon WRITE setIcon )
+ Q_PROPERTY( QString toolTip READ toolTip )
+ Q_PROPERTY( QString nickName READ nickName WRITE setNickName )
+ //Q_PROPERTY( unsigned long idleTime READ idleTime WRITE setIdleTime )
+
+public:
+ /**
+ * \brief Create new contact.
+ *
+ * <b>The parent MetaContact must not be NULL</b>
+ *
+ * \note id is required to be unique per protocol and per account.
+ * Across those boundaries ids may occur multiple times.
+ * The id is solely for comparing items safely (using pointers is
+ * more crash-prone). DO NOT assume anything regarding the id's
+ * value! Even if it may look like an ICQ UIN or an MSN passport,
+ * this is undefined and may change at any time!
+ *
+ * @param account is the parent account. this constructor automatically register the contact to the account
+ * @param id is the Contact's unique Id (mostly the user's login)
+ * @param parent is the parent @ref MetaContact this Contact is part of
+ * @param icon is an optional icon
+ */
+ Contact( Account *account, const QString &id, MetaContact *parent,
+ const QString &icon = QString::null );
+
+ ~Contact();
+
+ /**
+ * \brief Get the metacontact for this contact
+ * @return The MetaContact object for this contact
+ */
+ MetaContact *metaContact() const;
+
+
+ /**
+ * \brief Get the unique id that identifies a contact.
+ *
+ * \note Id is required to be unique per protocol and per account.
+ * Across those boundaries ids may occur multiple times.
+ * The id is solely for comparing items safely (using pointers is
+ * more crash-prone). DO NOT assume anything regarding the id's
+ * value! Even if it may look like an ICQ UIN or an MSN passport,
+ * this is undefined and may change at any time!
+ *
+ * @return The unique id of the contact
+ */
+ QString contactId() const;
+
+ /**
+ * \brief Get the protocol that the contact belongs to.
+ *
+ * simply return account()->protocol()
+ *
+ * @return the contact's protocol
+ */
+ Protocol* protocol() const;
+
+ /**
+ * \brief Get the account that this contact belongs to
+ *
+ * @return the Account object for this contact
+ */
+ Account* account() const;
+
+ /**
+ * \brief Move this contact to a new MetaContact.
+ * This basically reparents the contact and updates the internal
+ * data structures.
+ * If the old contact is going to be empty, a question may ask to the user if it wants to delete the old contact.
+ *
+ * @param m The new MetaContact to move this contact to
+ */
+ void setMetaContact(MetaContact *m);
+
+
+ /**
+ * @brief Get whether this contact is online.
+ * @return @c true if the contact is online, @c false otherwise.
+ */
+ bool isOnline() const;
+
+ /**
+ * \brief Get whether this contact can receive messages
+ *
+ * Used in determining if the contact is able to
+ * receive messages. This function must be defined by child classes
+ *
+ * @return true if the contact can be reached
+ * @return false if the contact can not be reached
+ */
+ // FIXME: After KDE 3.2 we should split this into a public, NON-virtual
+ // isReachable() accessor that checks for account->isConnected()
+ // and then calls a new virtual method that does the
+ // protocol-specific work, like 'doIsUnreachable' or so - Martijn
+ //
+ //FIXME: Can this be made const please? - JK
+ virtual bool isReachable();
+
+ /**
+ * @brief Serialize the contact for storage in the contact list.
+ *
+ * The provided serializedData contain the contact id in the field
+ * "contactId". If you don't like this, or don't want to
+ * store these fields at all,
+ * you are free to remove them from the list.
+ *
+ * Most plugins don't need more than these fields, so they only need
+ * to set the address book fields themselves. If you have nothing to
+ * save at all you can clear the QMap, an empty map is treated as
+ * 'nothing to save'.
+ *
+ * The provided addressBookFields QMap contains the index field as
+ * marked with @ref Plugin::addAddressBookField() with the
+ * contact id as value. If no index field is available the QMap is
+ * simply passed as an empty map.
+ *
+ * @sa Protocol::deserializeContact
+ */
+ virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData );
+
+ /**
+ * @brief Serialize the contacts persistent properties for storage in the contact list.
+ *
+ * Does the same as @ref serialize() does but for KopeteContactProperties
+ * set in this contact with their persistency flag turned on.
+ * In contrary to @ref serialize() this does not need to be reimplemented.
+ *
+ */
+ void serializeProperties(QMap<QString, QString> &serializedData);
+
+ /**
+ * @brief Deserialize the contacts persistent properties
+ */
+ void deserializeProperties(QMap<QString, QString> &serializedData);
+
+ /**
+ * @brief Get the online status of the contact
+ * @return the online status of the contact
+ */
+ OnlineStatus onlineStatus() const;
+
+ /**
+ * \brief Set the contact's online status
+ */
+ void setOnlineStatus(const OnlineStatus &status);
+
+ /**
+ * \brief Get the set of custom menu items for this contact
+ *
+ * Returns a set of custom menu items for the context menu
+ * which is displayed in showContextMenu (private). Protocols
+ * should use this to add protocol-specific actions to the
+ * popup menu. Kopete take care of the deletion of the action collection.
+ * Actions should have the collection as parent.
+ *
+ * @return Collection of menu items to be show on the context menu
+ * @todo if possible, try to use KXMLGUI
+ */
+ virtual QPtrList<KAction> *customContextMenuActions();
+
+ /**
+ * @todo What is this function for ?
+ */
+ virtual QPtrList<KAction> *customContextMenuActions( ChatSession *manager );
+
+ /**
+ * @brief Get the Context Menu for this contact
+ *
+ * This menu includes generic actions common to each protocol, and action defined in
+ * @ref customContextMenuActions()
+ */
+ KPopupMenu *popupMenu( ChatSession *manager = 0L );
+
+ /**
+ * \brief Get whether or not this contact is capable of file transfers
+ *
+ *
+ * \see setFileCapable()
+ * \return true if the protocol for this contact is capable of file transfers
+ * \return false if the protocol for this contact is not capable of file transfers
+ *
+ * @todo have a capabilioties. or move to protocol capabilities
+ */
+ bool isFileCapable() const;
+
+ /**
+ * \brief Set the file transfer capability of this contact
+ *
+ * \param filecap The new file transfer capability setting
+ * @todo have a capabilioties. or move to protocol capabilities
+ */
+ void setFileCapable( bool filecap );
+
+ /**
+ * \brief Get whether or not this contact can accept file transfers
+ *
+ * This function checks to make sure that the contact is online as well as
+ * capable of sending files.
+ * \see isReachable()
+ * @return true if this contact is online and is capable of receiving files
+ * @todo have a capabilioties. or move to protocol capabilities
+ */
+ bool canAcceptFiles() const;
+
+ enum CanCreateFlags { CannotCreate=false , CanCreate=true };
+
+ /**
+ * Returns the primary message manager affiliated with this contact
+ * Although a contact can have more than one active message manager
+ * (as is the case with MSN at least), only one message manager will
+ * ever be the contacts "primary" message manager.. aka the 1 on 1 chat.
+ * This function should always return that instance.
+ *
+ * @param canCreate If a new message manager can be created in addition
+ * to any existing managers. Currently, this is only set to true when
+ * a chat is initiated by the user by clicking the contact list.
+ */
+ virtual ChatSession * manager( CanCreateFlags canCreate = CannotCreate ) =0;
+
+ /**
+ * Returns the name of the icon to use for this contact
+ * If null, the protocol icon need to be used.
+ * The icon is not colored, nor has the status icon overloaded
+ */
+ QString& icon() const;
+
+ /**
+ * @brief Change the icon to use for this contact
+ * If you don't want to have the protocol icon as icon for this contact, you may set
+ * another icon. The icon doesn't need to be colored with the account icon as this operation
+ * will be performed later.
+ *
+ * if you want to go back to the protocol icon, set a null string.
+ */
+ void setIcon( const QString& icon );
+
+ /**
+ * \brief Get the time (in seconds) this contact has been idle
+ * It will return the time set in @ref setIdleTime() with an addition of the time
+ * since you set this last time
+ * @return time this contact has been idle for, in seconds
+ //
+ // FIXME: Can we make this just 'unsigned long' ? QT Properties can't handle
+ // 'unsigned long int'
+ */
+ virtual unsigned long int idleTime() const;
+
+ /**
+ * \brief Set the current idle time in seconds.
+ * Kopete will automatically calculate the time in @ref idleTime
+ * except if you set 0.
+ //
+ // FIXME: Can we make this just 'unsigned long' ? QT Properties can't handle
+ // 'unsigned long int'
+ */
+ void setIdleTime(unsigned long int);
+
+ /**
+ * @return A QStringList containing all property keys
+ **/
+ QStringList properties() const;
+
+ /**
+ * Check for existance of a certain property stored
+ * using "key".
+ * \param key the property to check for
+ **/
+ bool hasProperty(const QString &key) const;
+
+ /**
+ * \brief Get the value of a property with key "key".
+ *
+ * If you don't know the type of the returned QVariant, you will need
+ * to check for it.
+ * \return the value of the property
+ **/
+ const Kopete::ContactProperty &property(const QString &key) const;
+ const Kopete::ContactProperty &property(const Kopete::ContactPropertyTmpl &tmpl) const;
+
+ /**
+ * \brief Add or Set a property for this contact.
+ *
+ * @param tmpl The template this property is based on, key, label etc. are
+ * taken from this one
+ * @param value The value to store
+ *
+ * \note Setting a NULL value or an empty QString castable value
+ * removes the property if it already existed.
+ * <b>Don't</b> abuse this for property-removal, instead use
+ * @ref removeProperty() if you want to remove on purpose.
+ * The Removal is done to clean up the list of properties and to purge them
+ * from UI.
+ **/
+ void setProperty(const Kopete::ContactPropertyTmpl &tmpl, const QVariant &value);
+
+ /**
+ * \brief Convenience method to set the nickName property to the specified value
+ * @param name The nickname to set
+ */
+ void setNickName( const QString &name );
+
+ /**
+ * \brief Convenience method to retrieve the nickName property.
+ *
+ * This method will return the contactId if there has been no nickName property set
+ */
+ QString nickName() const;
+
+ /**
+ * \brief Remove a property if it exists
+ *
+ * @param tmpl the template this property is based on
+ **/
+ void removeProperty(const Kopete::ContactPropertyTmpl &tmpl);
+
+ /**
+ * \brief Get the tooltip for this contact
+ * Makes use of formattedName() and formattedIdleTime().
+ * \return an RTF tooltip depending on KopetePrefs settings
+ **/
+ QString toolTip() const;
+
+ /**
+ * Returns a formatted string of "firstName" and/or "lastName" properties
+ * if present.
+ * Suitable for GUI display
+ **/
+ QString formattedName() const;
+
+ /**
+ * Returns a formatted string of idleTime().
+ * Suitable for GUI display
+ **/
+ QString formattedIdleTime() const;
+
+ /**
+ * used in @ref sync()
+ */
+ enum Changed{ MovedBetweenGroup = 0x01, ///< the contact has been moved between groups
+ DisplayNameChanged = 0x02 ///< the displayname of the contact changed
+ };
+
+
+public slots:
+ /**
+ * This should typically pop up a KopeteChatWindow
+ */
+ void startChat();
+
+ /**
+ * Pops up an email type window
+ */
+ void sendMessage();
+
+ /**
+ * The user clicked on the contact, do the default action
+ */
+ void execute();
+
+ /**
+ * Changes the MetaContact that this contact is a part of. This function
+ * is called by the KAction changeMetaContact that is part of the context
+ * menu.
+ */
+ void changeMetaContact();
+
+ /**
+ * Method to retrieve user information. Should be implemented by
+ * the protocols, and popup some sort of dialog box
+ *
+ * reimplement it to show the informlation
+ * @todo rename and make it pure virtual
+ */
+ virtual void slotUserInfo() {};
+
+ /**
+ * @brief Syncronise the server and the metacontact.
+ * Protocols with server-side contact lists can implement this to
+ * sync the server groups with the metaContact groups. Or the server alias if any.
+ *
+ * This method is called every time the metacontact has been moved or renamed.
+ *
+ * default implementation does nothing
+ *
+ * @param changed is a bitmask of the @ref Changed enum which say why the call to this funtion is done.
+ */
+ virtual void sync(unsigned int changed = 0xFF);
+
+ /**
+ * Method to delete a contact from the contact list,
+ * should be implemented by protocol plugin to handle
+ * protocol-specific actions required to delete a contact
+ * (ie. messages to the server, etc)
+ * the default implementation simply call deleteLater()
+ */
+ virtual void deleteContact();
+
+ /**
+ * This is the Contact level slot for sending files. It should be
+ * implemented by all contacts which have the setFileCapable() flag set to
+ * true. If the function is called through the GUI, no parameters are sent
+ * and they take on default values (the file is chosen with a file open dialog)
+ *
+ * @param sourceURL The actual KURL of the file you are sending
+ * @param fileName (Optional) An alternate name for the file - what the
+ * receiver will see
+ * @param fileSize (Optional) Size of the file being sent. Used when sending
+ * a nondeterminate
+ * file size (such as over asocket
+ */
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+private slots:
+
+ /**
+ * This add the contact totally in the list if it was a temporary contact
+ */
+ void slotAddContact();
+
+ /**
+ * slot called when the action "delete" is called.
+ */
+ void slotDelete();
+
+ /**
+ * slot called when the action "block" is called.
+ */
+ void slotBlock();
+
+ /**
+ * slot called when the action "unblock" is called.
+ */
+ void slotUnblock();
+
+ /**
+ * The account's isConnected has changed.
+ */
+ void slotAccountIsConnectedChanged();
+
+signals:
+ /**
+ * The contact's online status changed
+ */
+ void onlineStatusChanged( Kopete::Contact *contact,
+ const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus );
+
+ /**
+ * The contact is about to be destroyed.
+ * Called when entering the destructor. Useful for cleanup, since
+ * metaContact() is still accessible at this point.
+ *
+ * @warning this signal is emit in the Contact destructor, so all
+ * virtual method are not available
+ */
+ void contactDestroyed( Kopete::Contact *contact );
+
+ /**
+ * The contact's idle state changed.
+ * You need to emit this signal to update the view.
+ * That mean when activity has been noticed
+ */
+ void idleStateChanged( Kopete::Contact *contact );
+
+ /**
+ * One of the contact's properties has changed.
+ * @param contact this contact, useful for listening to signals from more than one contact
+ * @param key the key whose value has changed
+ * @param oldValue the value before the change, or an invalid QVariant if the property is new
+ * @param newValue the value after the change, or an invalid QVariant if the property was removed
+ */
+ void propertyChanged( Kopete::Contact *contact, const QString &key,
+ const QVariant &oldValue, const QVariant &newValue );
+
+protected:
+ virtual void virtual_hook(uint id, void *data);
+
+private:
+ class Private;
+ Private *d;
+
+
+};
+
+
+} //END namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetecontactlist.cpp b/kopete/libkopete/kopetecontactlist.cpp
new file mode 100644
index 00000000..9aab9f2f
--- /dev/null
+++ b/kopete/libkopete/kopetecontactlist.cpp
@@ -0,0 +1,1112 @@
+/*
+ kopetecontactlist.cpp - Kopete's Contact List backend
+
+ Copyright (c) 2005 by Michael Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontactlist.h"
+
+#include <qdir.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kapplication.h>
+#include <kabc/stdaddressbook.h>
+#include <kdebug.h>
+#include <ksavefile.h>
+#include <kstandarddirs.h>
+#include <kopeteconfig.h>
+#include <kglobal.h>
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopetechatsession.h"
+//#include "kopetemessage.h"
+#include "kopetepluginmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetegroup.h"
+#include "kopetepicture.h"
+
+
+namespace Kopete
+{
+
+class ContactList::Private
+{public:
+ /** Flag: do not save the contactlist until she is completely loaded */
+ bool loaded ;
+
+ QPtrList<MetaContact> contacts;
+ QPtrList<Group> groups;
+ QPtrList<MetaContact> selectedMetaContacts;
+ QPtrList<Group> selectedGroups;
+
+ QTimer *saveTimer;
+
+ MetaContact *myself;
+
+ /** Flag: does the user uses the global identity */
+ bool useGlobalIdentity;
+
+ /**
+ * Current contact list version * 10 ( i.e. '10' is version '1.0' )
+ */
+ static const uint ContactListVersion = 10;
+};
+
+ContactList *ContactList::s_self = 0L;
+
+ContactList *ContactList::self()
+{
+ if( !s_self )
+ s_self = new ContactList;
+
+ return s_self;
+}
+
+ContactList::ContactList()
+ : QObject( kapp, "KopeteContactList" )
+{
+ d=new Private;
+
+ //the myself metacontact can't be created now, because it will use
+ //ContactList::self() as parent which will call this constructor -> infinite loop
+ d->myself=0L;
+
+ //no contactlist loaded yet, don't save them
+ d->loaded=false;
+
+ // automatically save on changes to the list
+ d->saveTimer = new QTimer( this, "saveTimer" );
+ connect( d->saveTimer, SIGNAL( timeout() ), SLOT ( save() ) );
+
+ connect( this, SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), SLOT( slotSaveLater() ) );
+ connect( this, SIGNAL( metaContactRemoved( Kopete::MetaContact * ) ), SLOT( slotSaveLater() ) );
+ connect( this, SIGNAL( groupAdded( Kopete::Group * ) ), SLOT( slotSaveLater() ) );
+ connect( this, SIGNAL( groupRemoved( Kopete::Group * ) ), SLOT( slotSaveLater() ) );
+ connect( this, SIGNAL( groupRenamed( Kopete::Group *, const QString & ) ), SLOT( slotSaveLater() ) );
+}
+
+ContactList::~ContactList()
+{
+ delete d->myself;
+ delete d;
+}
+
+QPtrList<MetaContact> ContactList::metaContacts() const
+{
+ return d->contacts;
+}
+
+
+QPtrList<Group> ContactList::groups() const
+{
+ return d->groups;
+}
+
+
+MetaContact *ContactList::metaContact( const QString &metaContactId ) const
+{
+ QPtrListIterator<MetaContact> it( d->contacts );
+
+ for( ; it.current(); ++it )
+ {
+ if( it.current()->metaContactId() == metaContactId )
+ return it.current();
+ }
+
+ return 0L;
+}
+
+
+Group * ContactList::group(unsigned int groupId) const
+{
+ Group *groupIterator;
+ for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() )
+ {
+ if( groupIterator->groupId()==groupId )
+ return groupIterator;
+ }
+ return 0L;
+}
+
+
+Contact *ContactList::findContact( const QString &protocolId,
+ const QString &accountId, const QString &contactId ) const
+{
+ //Browsing metacontacts is too slow, better to uses the Dict of the account.
+ Account *i=AccountManager::self()->findAccount(protocolId,accountId);
+ if(!i)
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Account not found" << endl;
+ return 0L;
+ }
+ return i->contacts()[contactId];
+}
+
+
+MetaContact *ContactList::findMetaContactByDisplayName( const QString &displayName ) const
+{
+ QPtrListIterator<MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+// kdDebug(14010) << "Display Name: " << it.current()->displayName() << "\n";
+ if( it.current()->displayName() == displayName ) {
+ return it.current();
+ }
+ }
+
+ return 0L;
+}
+
+MetaContact* ContactList::findMetaContactByContactId( const QString &contactId ) const
+{
+ QPtrList<Account> acts=AccountManager::self()->accounts();
+ QPtrListIterator<Account> it( acts );
+ for ( ; it.current(); ++it )
+ {
+ Contact *c=(*it)->contacts()[contactId];
+ if(c && c->metaContact())
+ return c->metaContact();
+ }
+ return 0L;
+}
+
+Group * ContactList::findGroup(const QString& displayName, int type)
+{
+ if( type == Group::Temporary )
+ return Group::temporary();
+
+ Group *groupIterator;
+ for ( groupIterator = d->groups.first(); groupIterator; groupIterator = d->groups.next() )
+ {
+ if( groupIterator->type() == type && groupIterator->displayName() == displayName )
+ return groupIterator;
+ }
+
+ Group *newGroup = new Group( displayName, (Group::GroupType)type );
+ addGroup( newGroup );
+ return newGroup;
+}
+
+
+QPtrList<MetaContact> ContactList::selectedMetaContacts() const
+{
+ return d->selectedMetaContacts;
+}
+
+QPtrList<Group> ContactList::selectedGroups() const
+{
+ return d->selectedGroups;
+}
+
+
+void ContactList::addMetaContact( MetaContact *mc )
+{
+ if ( d->contacts.contains( mc ) )
+ return;
+
+ d->contacts.append( mc );
+
+ emit metaContactAdded( mc );
+ connect( mc, SIGNAL( persistentDataChanged( ) ), SLOT( slotSaveLater() ) );
+ connect( mc, SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( metaContactAddedToGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
+ connect( mc, SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( metaContactRemovedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ) );
+}
+
+
+void ContactList::removeMetaContact(MetaContact *m)
+{
+ if ( !d->contacts.contains(m) )
+ {
+ kdDebug(14010) << k_funcinfo << "Trying to remove a not listed MetaContact." << endl;
+ return;
+ }
+
+ if ( d->selectedMetaContacts.contains( m ) )
+ {
+ d->selectedMetaContacts.remove( m );
+ setSelectedItems( d->selectedMetaContacts, d->selectedGroups );
+ }
+
+ //removes subcontact from server here and now.
+ QPtrList<Contact> cts=m->contacts();
+ for( Contact *c = cts.first(); c; c = cts.next() )
+ {
+ c->deleteContact();
+ }
+
+ d->contacts.remove( m );
+ emit metaContactRemoved( m );
+ m->deleteLater();
+}
+
+
+void ContactList::addGroup( Group * g )
+{
+ if(!d->groups.contains(g) )
+ {
+ d->groups.append( g );
+ emit groupAdded( g );
+ connect( g , SIGNAL ( displayNameChanged(Kopete::Group* , const QString & )) , this , SIGNAL ( groupRenamed(Kopete::Group* , const QString & )) ) ;
+ }
+}
+
+void ContactList::removeGroup( Group *g )
+{
+ if ( d->selectedGroups.contains( g ) )
+ {
+ d->selectedGroups.remove( g );
+ setSelectedItems( d->selectedMetaContacts, d->selectedGroups );
+ }
+
+ d->groups.remove( g );
+ emit groupRemoved( g );
+ g->deleteLater();
+}
+
+
+void ContactList::setSelectedItems(QPtrList<MetaContact> metaContacts , QPtrList<Group> groups)
+{
+ kdDebug( 14010 ) << k_funcinfo << metaContacts.count() << " metacontacts, " << groups.count() << " groups selected" << endl;
+ d->selectedMetaContacts=metaContacts;
+ d->selectedGroups=groups;
+
+ emit metaContactSelected( groups.isEmpty() && metaContacts.count()==1 );
+ emit selectionChanged();
+}
+
+MetaContact* ContactList::myself()
+{
+ if(!d->myself)
+ d->myself=new MetaContact();
+ return d->myself;
+}
+
+void ContactList::loadGlobalIdentity()
+{
+ // Apply the global identity
+ if(Kopete::Config::enableGlobalIdentity())
+ {
+ // Disconnect to make sure it will not cause duplicate calls.
+ disconnect(myself(), SIGNAL(displayNameChanged(const QString&, const QString&)), this, SLOT(slotDisplayNameChanged()));
+ disconnect(myself(), SIGNAL(photoChanged()), this, SLOT(slotPhotoChanged()));
+
+ connect(myself(), SIGNAL(displayNameChanged(const QString&, const QString&)), this, SLOT(slotDisplayNameChanged()));
+ connect(myself(), SIGNAL(photoChanged()), this, SLOT(slotPhotoChanged()));
+
+ // Ensure that the myself metaContactId is always the KABC whoAmI
+ KABC::Addressee a = KABC::StdAddressBook::self()->whoAmI();
+ if(!a.isEmpty() && a.uid() != myself()->metaContactId())
+ {
+ myself()->setMetaContactId(a.uid());
+ }
+
+ // Apply the global identity
+ // Maybe one of the myself contact from a account has a different displayName/photo at startup.
+ slotDisplayNameChanged();
+ slotPhotoChanged();
+ }
+ else
+ {
+ disconnect(myself(), SIGNAL(displayNameChanged(const QString&, const QString&)), this, SLOT(slotDisplayNameChanged()));
+ disconnect(myself(), SIGNAL(photoChanged()), this, SLOT(slotPhotoChanged()));
+ }
+}
+
+void ContactList::slotDisplayNameChanged()
+{
+ static bool mutex=false;
+ if(mutex)
+ {
+ kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ;
+ return;
+ }
+ mutex=true;
+
+ kdDebug( 14010 ) << k_funcinfo << myself()->displayName() << endl;
+
+ emit globalIdentityChanged(Kopete::Global::Properties::self()->nickName().key(), myself()->displayName());
+ mutex=false;
+}
+
+void ContactList::slotPhotoChanged()
+{
+ static bool mutex=false;
+ if(mutex)
+ {
+ kdDebug (14010) << k_funcinfo << " mutex blocked" << endl ;
+ return;
+ }
+ mutex=true;
+ kdDebug( 14010 ) << k_funcinfo << myself()->picture().path() << endl;
+
+ emit globalIdentityChanged(Kopete::Global::Properties::self()->photo().key(), myself()->picture().path());
+ mutex=false;
+ /* The mutex is usefull to don't have such as stack overflow
+ Kopete::ContactList::slotPhotoChanged -> Kopete::ContactList::globalIdentityChanged
+ MSNAccount::slotGlobalIdentityChanged -> Kopete::Contact::propertyChanged
+ Kopete::MetaContact::slotPropertyChanged -> Kopete::MetaContact::photoChanged -> Kopete::ContactList::slotPhotoChanged
+ */
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+void ContactList::load()
+{
+ loadXML();
+ // Apply the global identity when all the protocols plugins are loaded.
+ connect(PluginManager::self(), SIGNAL(allPluginsLoaded()), this, SLOT(loadGlobalIdentity()));
+}
+
+void ContactList::loadXML()
+{
+ // don't save when we're in the middle of this...
+ d->loaded = false;
+
+ QString filename = locateLocal( "appdata", QString::fromLatin1( "contactlist.xml" ) );
+ if( filename.isEmpty() )
+ {
+ d->loaded=true;
+ return ;
+ }
+
+ QDomDocument contactList( QString::fromLatin1( "kopete-contact-list" ) );
+
+ QFile contactListFile( filename );
+ contactListFile.open( IO_ReadOnly );
+ contactList.setContent( &contactListFile );
+
+ QDomElement list = contactList.documentElement();
+
+ QString versionString = list.attribute( QString::fromLatin1( "version" ), QString::null );
+ uint version = 0;
+ if( QRegExp( QString::fromLatin1( "[0-9]+\\.[0-9]" ) ).exactMatch( versionString ) )
+ version = versionString.replace( QString::fromLatin1( "." ), QString::null ).toUInt();
+
+ if( version < Private::ContactListVersion )
+ {
+ // The version string is invalid, or we're using an older version.
+ // Convert first and reparse the file afterwards
+ kdDebug( 14010 ) << k_funcinfo << "Contact list version " << version
+ << " is older than current version " << Private::ContactListVersion
+ << ". Converting first." << endl;
+
+ contactListFile.close();
+
+ convertContactList( filename, version, Private::ContactListVersion );
+
+ contactList = QDomDocument ( QString::fromLatin1( "kopete-contact-list" ) );
+
+ contactListFile.open( IO_ReadOnly );
+ contactList.setContent( &contactListFile );
+
+ list = contactList.documentElement();
+ }
+
+ addGroup( Kopete::Group::topLevel() );
+
+ QDomElement element = list.firstChild().toElement();
+ while( !element.isNull() )
+ {
+ if( element.tagName() == QString::fromLatin1("meta-contact") )
+ {
+ //TODO: id isn't used
+ //QString id = element.attribute( "id", QString::null );
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+ if ( !metaContact->fromXML( element ) )
+ {
+ delete metaContact;
+ metaContact = 0;
+ }
+ else
+ {
+ Kopete::ContactList::self()->addMetaContact(
+ metaContact );
+ }
+ }
+ else if( element.tagName() == QString::fromLatin1("kopete-group") )
+ {
+ Kopete::Group *group = new Kopete::Group();
+ if( !group->fromXML( element ) )
+ {
+ delete group;
+ group = 0;
+ }
+ else
+ {
+ Kopete::ContactList::self()->addGroup( group );
+ }
+ }
+ // Only load myself metacontact information when Global Identity is enabled.
+ else if( element.tagName() == QString::fromLatin1("myself-meta-contact") && Kopete::Config::enableGlobalIdentity() )
+ {
+ if( !myself()->fromXML( element ) )
+ {
+ delete d->myself;
+ d->myself = 0;
+ }
+ }
+ else
+ {
+ kdWarning(14010) << "Kopete::ContactList::loadXML: "
+ << "Unknown element '" << element.tagName()
+ << "' in contact list!" << endl;
+ }
+ element = element.nextSibling().toElement();
+ }
+ contactListFile.close();
+ d->loaded=true;
+}
+
+void ContactList::convertContactList( const QString &fileName, uint /* fromVersion */, uint /* toVersion */ )
+{
+ // For now, ignore fromVersion and toVersion. These are meant for future
+ // changes to allow incremental (multi-pass) conversion so we don't have
+ // to rewrite the whole conversion code for each change.
+
+ QDomDocument contactList( QString::fromLatin1( "messaging-contact-list" ) );
+ QFile contactListFile( fileName );
+ contactListFile.open( IO_ReadOnly );
+ contactList.setContent( &contactListFile );
+
+ QDomElement oldList = contactList.documentElement();
+
+ QDomDocument newList( QString::fromLatin1( "kopete-contact-list" ) );
+ newList.appendChild( newList.createProcessingInstruction( QString::fromLatin1( "xml" ), QString::fromLatin1( "version=\"1.0\"" ) ) );
+
+ QDomElement newRoot = newList.createElement( QString::fromLatin1( "kopete-contact-list" ) );
+ newList.appendChild( newRoot );
+ newRoot.setAttribute( QString::fromLatin1( "version" ), QString::fromLatin1( "1.0" ) );
+
+ QDomNode oldNode = oldList.firstChild();
+ while( !oldNode.isNull() )
+ {
+ QDomElement oldElement = oldNode.toElement();
+ if( !oldElement.isNull() )
+ {
+ if( oldElement.tagName() == QString::fromLatin1("meta-contact") )
+ {
+ // Ignore ID, it is not used in the current list anyway
+ QDomElement newMetaContact = newList.createElement( QString::fromLatin1( "meta-contact" ) );
+ newRoot.appendChild( newMetaContact );
+
+ // Plugin data is stored completely different, and requires
+ // some bookkeeping to convert properly
+ QMap<QString, QDomElement> pluginData;
+ QStringList icqData;
+ QStringList gaduData;
+
+ // ICQ and Gadu can only be converted properly if the address book fields
+ // are already parsed. Therefore, scan for those first and add the rest
+ // afterwards
+ QDomNode oldContactNode = oldNode.firstChild();
+ while( !oldContactNode.isNull() )
+ {
+ QDomElement oldContactElement = oldContactNode.toElement();
+ if( !oldContactElement.isNull() && oldContactElement.tagName() == QString::fromLatin1("address-book-field") )
+ {
+ // Convert address book fields.
+ // Jabber will be called "xmpp", Aim/Toc and Aim/Oscar both will
+ // be called "aim". MSN, AIM, IRC, Oscar and SMS don't use address
+ // book fields yet; Gadu and ICQ can be converted as-is.
+ // As Yahoo is unfinished we won't try to convert at all.
+ QString id = oldContactElement.attribute( QString::fromLatin1( "id" ), QString::null );
+ QString data = oldContactElement.text();
+
+ QString app, key, val;
+ QString separator = QString::fromLatin1( "," );
+ if( id == QString::fromLatin1( "messaging/gadu" ) )
+ separator = QString::fromLatin1( "\n" );
+ else if( id == QString::fromLatin1( "messaging/icq" ) )
+ separator = QString::fromLatin1( ";" );
+ else if( id == QString::fromLatin1( "messaging/jabber" ) )
+ id = QString::fromLatin1( "messaging/xmpp" );
+
+ if( id == QString::fromLatin1( "messaging/gadu" ) || id == QString::fromLatin1( "messaging/icq" ) ||
+ id == QString::fromLatin1( "messaging/winpopup" ) || id == QString::fromLatin1( "messaging/xmpp" ) )
+ {
+ app = id;
+ key = QString::fromLatin1( "All" );
+ val = data.replace( separator, QChar( 0xE000 ) );
+ }
+
+ if( !app.isEmpty() )
+ {
+ QDomElement addressBookField = newList.createElement( QString::fromLatin1( "address-book-field" ) );
+ newMetaContact.appendChild( addressBookField );
+
+ addressBookField.setAttribute( QString::fromLatin1( "app" ), app );
+ addressBookField.setAttribute( QString::fromLatin1( "key" ), key );
+
+ addressBookField.appendChild( newList.createTextNode( val ) );
+
+ // ICQ didn't store the contactId locally, only in the address
+ // book fields, so we need to be able to access it later
+ if( id == QString::fromLatin1( "messaging/icq" ) )
+ icqData = QStringList::split( QChar( 0xE000 ), val );
+ else if( id == QString::fromLatin1("messaging/gadu") )
+ gaduData = QStringList::split( QChar( 0xE000 ), val );
+ }
+ }
+ oldContactNode = oldContactNode.nextSibling();
+ }
+
+ // Now, convert the other elements
+ oldContactNode = oldNode.firstChild();
+ while( !oldContactNode.isNull() )
+ {
+ QDomElement oldContactElement = oldContactNode.toElement();
+ if( !oldContactElement.isNull() )
+ {
+ if( oldContactElement.tagName() == QString::fromLatin1("display-name") )
+ {
+ QDomElement displayName = newList.createElement( QString::fromLatin1( "display-name" ) );
+ displayName.appendChild( newList.createTextNode( oldContactElement.text() ) );
+ newMetaContact.appendChild( displayName );
+ }
+ else if( oldContactElement.tagName() == QString::fromLatin1("groups") )
+ {
+ QDomElement groups = newList.createElement( QString::fromLatin1( "groups" ) );
+ newMetaContact.appendChild( groups );
+
+ QDomNode oldGroup = oldContactElement.firstChild();
+ while( !oldGroup.isNull() )
+ {
+ QDomElement oldGroupElement = oldGroup.toElement();
+ if ( oldGroupElement.tagName() == QString::fromLatin1("group") )
+ {
+ QDomElement group = newList.createElement( QString::fromLatin1( "group" ) );
+ group.appendChild( newList.createTextNode( oldGroupElement.text() ) );
+ groups.appendChild( group );
+ }
+ else if ( oldGroupElement.tagName() == QString::fromLatin1("top-level") )
+ {
+ QDomElement group = newList.createElement( QString::fromLatin1( "top-level" ) );
+ groups.appendChild( group );
+ }
+
+ oldGroup = oldGroup.nextSibling();
+ }
+ }
+ else if( oldContactElement.tagName() == QString::fromLatin1( "plugin-data" ) )
+ {
+ // Convert the plugin data
+ QString id = oldContactElement.attribute( QString::fromLatin1( "plugin-id" ), QString::null );
+ QString data = oldContactElement.text();
+
+ bool convertOldAim = false;
+ uint fieldCount = 1;
+ QString addressBookLabel;
+ if( id == QString::fromLatin1("MSNProtocol") )
+ {
+ fieldCount = 3;
+ addressBookLabel = QString::fromLatin1("msn");
+ }
+ else if( id == QString::fromLatin1("IRCProtocol") )
+ {
+ fieldCount = 3;
+ addressBookLabel = QString::fromLatin1("irc");
+ }
+ else if( id == QString::fromLatin1("OscarProtocol") )
+ {
+ fieldCount = 2;
+ addressBookLabel = QString::fromLatin1("aim");
+ }
+ else if( id == QString::fromLatin1("AIMProtocol") )
+ {
+ id = QString::fromLatin1("OscarProtocol");
+ convertOldAim = true;
+ addressBookLabel = QString::fromLatin1("aim");
+ }
+ else if( id == QString::fromLatin1("ICQProtocol") || id == QString::fromLatin1("WPProtocol") || id == QString::fromLatin1("GaduProtocol") )
+ {
+ fieldCount = 1;
+ }
+ else if( id == QString::fromLatin1("JabberProtocol") )
+ {
+ fieldCount = 4;
+ }
+ else if( id == QString::fromLatin1("SMSProtocol") )
+ {
+ // SMS used a variable serializing using a dot as delimiter.
+ // The minimal count is three though (id, name, delimiter).
+ fieldCount = 2;
+ addressBookLabel = QString::fromLatin1("sms");
+ }
+
+ if( pluginData[ id ].isNull() )
+ {
+ pluginData[ id ] = newList.createElement( QString::fromLatin1( "plugin-data" ) );
+ pluginData[ id ].setAttribute( QString::fromLatin1( "plugin-id" ), id );
+ newMetaContact.appendChild( pluginData[ id ] );
+ }
+
+ // Do the actual conversion
+ if( id == QString::fromLatin1( "MSNProtocol" ) || id == QString::fromLatin1( "OscarProtocol" ) ||
+ id == QString::fromLatin1( "AIMProtocol" ) || id == QString::fromLatin1( "IRCProtocol" ) ||
+ id == QString::fromLatin1( "ICQProtocol" ) || id == QString::fromLatin1( "JabberProtocol" ) ||
+ id == QString::fromLatin1( "SMSProtocol" ) || id == QString::fromLatin1( "WPProtocol" ) ||
+ id == QString::fromLatin1( "GaduProtocol" ) )
+ {
+ QStringList strList = QStringList::split( QString::fromLatin1( "||" ), data );
+
+ // Unescape '||'
+ for( QStringList::iterator it = strList.begin(); it != strList.end(); ++it )
+ {
+ ( *it ).replace( QString::fromLatin1( "\\|;" ), QString::fromLatin1( "|" ) ).
+ replace( QString::fromLatin1( "\\\\" ), QString::fromLatin1( "\\" ) );
+ }
+
+ uint idx = 0;
+ while( idx < strList.size() )
+ {
+ QDomElement dataField;
+
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "contactId" ) );
+ if( id == QString::fromLatin1("ICQProtocol") )
+ dataField.appendChild( newList.createTextNode( icqData[ idx ] ) );
+ else if( id == QString::fromLatin1("GaduProtocol") )
+ dataField.appendChild( newList.createTextNode( gaduData[ idx ] ) );
+ else if( id == QString::fromLatin1("JabberProtocol") )
+ dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) );
+ else
+ dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
+
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "displayName" ) );
+ if( convertOldAim || id == QString::fromLatin1("ICQProtocol") || id == QString::fromLatin1("WPProtocol") || id == QString::fromLatin1("GaduProtocol") )
+ dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
+ else if( id == QString::fromLatin1("JabberProtocol") )
+ dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
+ else
+ dataField.appendChild( newList.createTextNode( strList[ idx + 1 ] ) );
+
+ if( id == QString::fromLatin1("MSNProtocol") )
+ {
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "groups" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
+ }
+ else if( id == QString::fromLatin1("IRCProtocol") )
+ {
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "serverName" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
+ }
+ else if( id == QString::fromLatin1("JabberProtocol") )
+ {
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "accountId" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx ] ) );
+
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "groups" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) );
+ }
+ else if( id == QString::fromLatin1( "SMSProtocol" ) &&
+ ( idx + 2 < strList.size() ) && strList[ idx + 2 ] != QString::fromLatin1( "." ) )
+ {
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "serviceName" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx + 2 ] ) );
+
+ dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "servicePrefs" ) );
+ dataField.appendChild( newList.createTextNode( strList[ idx + 3 ] ) );
+
+ // Add extra fields
+ idx += 2;
+ }
+
+ // MSN, AIM, IRC, Oscar and SMS didn't store address book fields up
+ // to now, so create one
+ if( id != QString::fromLatin1("ICQProtocol") && id != QString::fromLatin1("JabberProtocol") && id != QString::fromLatin1("WPProtocol") && id != QString::fromLatin1("GaduProtocol") )
+ {
+ QDomElement addressBookField = newList.createElement( QString::fromLatin1( "address-book-field" ) );
+ newMetaContact.appendChild( addressBookField );
+
+ addressBookField.setAttribute( QString::fromLatin1( "app" ),
+ QString::fromLatin1( "messaging/" ) + addressBookLabel );
+ addressBookField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "All" ) );
+ addressBookField.appendChild( newList.createTextNode( strList[ idx ] ) );
+ }
+
+ idx += fieldCount;
+ }
+ }
+ else if( id == QString::fromLatin1("ContactNotesPlugin") || id == QString::fromLatin1("CryptographyPlugin") || id == QString::fromLatin1("TranslatorPlugin") )
+ {
+ QDomElement dataField = newList.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginData[ id ].appendChild( dataField );
+ if( id == QString::fromLatin1("ContactNotesPlugin") )
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "notes" ) );
+ else if( id == QString::fromLatin1("CryptographyPlugin") )
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "gpgKey" ) );
+ else if( id == QString::fromLatin1("TranslatorPlugin") )
+ dataField.setAttribute( QString::fromLatin1( "key" ), QString::fromLatin1( "languageKey" ) );
+
+ dataField.appendChild( newList.createTextNode( data ) );
+ }
+ }
+ }
+ oldContactNode = oldContactNode.nextSibling();
+ }
+ }
+ else if( oldElement.tagName() == QString::fromLatin1("kopete-group") )
+ {
+ QDomElement newGroup = newList.createElement( QString::fromLatin1( "kopete-group" ) );
+ newRoot.appendChild( newGroup );
+
+ QDomNode oldGroupNode = oldNode.firstChild();
+ while( !oldGroupNode.isNull() )
+ {
+ QDomElement oldGroupElement = oldGroupNode.toElement();
+
+ if( oldGroupElement.tagName() == QString::fromLatin1("display-name") )
+ {
+ QDomElement displayName = newList.createElement( QString::fromLatin1( "display-name" ) );
+ displayName.appendChild( newList.createTextNode( oldGroupElement.text() ) );
+ newGroup.appendChild( displayName );
+ }
+ if( oldGroupElement.tagName() == QString::fromLatin1("type") )
+ {
+ if( oldGroupElement.text() == QString::fromLatin1("Temporary") )
+ newGroup.setAttribute( QString::fromLatin1( "type" ), QString::fromLatin1( "temporary" ) );
+ else if( oldGroupElement.text() == QString::fromLatin1( "TopLevel" ) )
+ newGroup.setAttribute( QString::fromLatin1( "type" ), QString::fromLatin1( "top-level" ) );
+ else
+ newGroup.setAttribute( QString::fromLatin1( "type" ), QString::fromLatin1( "standard" ) );
+ }
+ if( oldGroupElement.tagName() == QString::fromLatin1("view") )
+ {
+ if( oldGroupElement.text() == QString::fromLatin1("collapsed") )
+ newGroup.setAttribute( QString::fromLatin1( "view" ), QString::fromLatin1( "collapsed" ) );
+ else
+ newGroup.setAttribute( QString::fromLatin1( "view" ), QString::fromLatin1( "expanded" ) );
+ }
+ else if( oldGroupElement.tagName() == QString::fromLatin1("plugin-data") )
+ {
+ // Per-group plugin data
+ // FIXME: This needs updating too, ideally, convert this in a later
+ // contactlist.xml version
+ QDomElement groupPluginData = newList.createElement( QString::fromLatin1( "plugin-data" ) );
+ newGroup.appendChild( groupPluginData );
+
+ groupPluginData.setAttribute( QString::fromLatin1( "plugin-id" ),
+ oldGroupElement.attribute( QString::fromLatin1( "plugin-id" ), QString::null ) );
+ groupPluginData.appendChild( newList.createTextNode( oldGroupElement.text() ) );
+ }
+
+ oldGroupNode = oldGroupNode.nextSibling();
+ }
+ }
+ else
+ {
+ kdWarning( 14010 ) << k_funcinfo << "Unknown element '" << oldElement.tagName()
+ << "' in contact list!" << endl;
+ }
+ }
+ oldNode = oldNode.nextSibling();
+ }
+
+ // Close the file, and save the new file
+ contactListFile.close();
+
+ QDir().rename( fileName, fileName + QString::fromLatin1( ".bak" ) );
+
+ // kdDebug( 14010 ) << k_funcinfo << "XML output:\n" << newList.toString( 2 ) << endl;
+
+ contactListFile.open( IO_WriteOnly );
+ QTextStream stream( &contactListFile );
+ stream.setEncoding( QTextStream::UnicodeUTF8 );
+ stream << newList.toString( 2 );
+
+ contactListFile.flush();
+ contactListFile.close();
+}
+
+void Kopete::ContactList::save()
+{
+ saveXML();
+}
+
+void Kopete::ContactList::saveXML()
+{
+ if(!d->loaded)
+ {
+ kdDebug(14010) << "Kopete::ContactList::saveXML: contactlist not loaded, abort saving" << endl;
+ return;
+ }
+
+ QString contactListFileName = locateLocal( "appdata", QString::fromLatin1( "contactlist.xml" ) );
+ KSaveFile contactListFile( contactListFileName );
+ if( contactListFile.status() == 0 )
+ {
+ QTextStream *stream = contactListFile.textStream();
+ stream->setEncoding( QTextStream::UnicodeUTF8 );
+ toXML().save( *stream, 4 );
+
+ if ( contactListFile.close() )
+ {
+ // cancel any scheduled saves
+ d->saveTimer->stop();
+ return;
+ }
+ else
+ {
+ kdDebug(14010) << "Kopete::ContactList::saveXML: failed to write contactlist, error code is: " << contactListFile.status() << endl;
+ }
+ }
+ else
+ {
+ kdWarning(14010) << "Kopete::ContactList::saveXML: Couldn't open contact list file "
+ << contactListFileName << ". Contact list not saved." << endl;
+ }
+
+ // if we got here, saving the contact list failed. retry every minute until it works.
+ d->saveTimer->start( 60000, true /* single-shot: will get restarted by us next time if it's still failing */ );
+}
+
+const QDomDocument ContactList::toXML()
+{
+ QDomDocument doc;
+ doc.appendChild( doc.createElement( QString::fromLatin1("kopete-contact-list") ) );
+ doc.documentElement().setAttribute( QString::fromLatin1("version"), QString::fromLatin1("1.0"));
+
+ // Save group information. ie: Open/Closed, pehaps later icons? Who knows.
+ for( Kopete::Group *g = d->groups.first(); g; g = d->groups.next() )
+ doc.documentElement().appendChild( doc.importNode( g->toXML(), true ) );
+
+ // Save metacontact information.
+ for( Kopete::MetaContact *m = d->contacts.first(); m; m = d->contacts.next() )
+ if( !m->isTemporary() )
+ doc.documentElement().appendChild( doc.importNode( m->toXML(), true ) );
+
+ // Save myself metacontact information
+ if( Kopete::Config::enableGlobalIdentity() )
+ {
+ QDomElement myselfElement = myself()->toXML(true); // Save minimal information.
+ myselfElement.setTagName( QString::fromLatin1("myself-meta-contact") );
+ doc.documentElement().appendChild( doc.importNode( myselfElement, true ) );
+ }
+
+ return doc;
+}
+
+QStringList ContactList::contacts() const
+{
+ QStringList contacts;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ contacts.append( it.current()->displayName() );
+ }
+ return contacts;
+}
+
+QStringList ContactList::contactStatuses() const
+{
+ QStringList meta_contacts;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ meta_contacts.append( QString::fromLatin1( "%1 (%2)" ).
+ arg( it.current()->displayName(), it.current()->statusString() ));
+ }
+ return meta_contacts;
+}
+
+QStringList ContactList::reachableContacts() const
+{
+ QStringList contacts;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isReachable() )
+ contacts.append( it.current()->displayName() );
+ }
+ return contacts;
+}
+
+QPtrList<Contact> ContactList::onlineContacts() const
+{
+ QPtrList<Kopete::Contact> result;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isOnline() )
+ {
+ QPtrList<Kopete::Contact> contacts = it.current()->contacts();
+ QPtrListIterator<Kopete::Contact> cit( contacts );
+ for( ; cit.current(); ++cit )
+ {
+ if ( cit.current()->isOnline() )
+ result.append( cit.current() );
+ }
+ }
+ }
+ return result;
+}
+
+QPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts() const
+{
+ QPtrList<Kopete::MetaContact> result;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isOnline() )
+ result.append( it.current() );
+ }
+ return result;
+}
+
+QPtrList<Kopete::MetaContact> Kopete::ContactList::onlineMetaContacts( const QString &protocolId ) const
+{
+ QPtrList<Kopete::MetaContact> result;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ // FIXME: This loop is not very efficient :(
+ if ( it.current()->isOnline() )
+ {
+ QPtrList<Kopete::Contact> contacts = it.current()->contacts();
+ QPtrListIterator<Kopete::Contact> cit( contacts );
+ for( ; cit.current(); ++cit )
+ {
+ if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId )
+ result.append( it.current() );
+ }
+ }
+ }
+ return result;
+}
+
+QPtrList<Kopete::Contact> Kopete::ContactList::onlineContacts( const QString &protocolId ) const
+{
+ QPtrList<Kopete::Contact> result;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ // FIXME: This loop is not very efficient :(
+ if ( it.current()->isOnline() )
+ {
+ QPtrList<Kopete::Contact> contacts = it.current()->contacts();
+ QPtrListIterator<Kopete::Contact> cit( contacts );
+ for( ; cit.current(); ++cit )
+ {
+ if( cit.current()->isOnline() && cit.current()->protocol()->pluginId() == protocolId )
+ result.append( cit.current() );
+ }
+ }
+ }
+ return result;
+}
+
+QStringList Kopete::ContactList::fileTransferContacts() const
+{
+ QStringList contacts;
+ QPtrListIterator<Kopete::MetaContact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->canAcceptFiles() )
+ contacts.append( it.current()->displayName() );
+ }
+ return contacts;
+}
+
+void Kopete::ContactList::sendFile( const QString &displayName, const KURL &sourceURL,
+ const QString &altFileName, const long unsigned int fileSize)
+{
+// kdDebug(14010) << "Send To Display Name: " << displayName << "\n";
+
+ Kopete::MetaContact *c = findMetaContactByDisplayName( displayName );
+ if( c )
+ c->sendFile( sourceURL, altFileName, fileSize );
+}
+
+void Kopete::ContactList::messageContact( const QString &contactId, const QString &messageText )
+{
+ Kopete::MetaContact *mc = findMetaContactByContactId( contactId );
+ if (!mc) return;
+
+ Kopete::Contact *c = mc->execute(); //We need to know which contact was chosen as the preferred in order to message it
+ if (!c) return;
+
+ Kopete::Message msg(c->account()->myself(), c, messageText, Kopete::Message::Outbound);
+ c->manager(Contact::CanCreate)->sendMessage(msg);
+
+}
+
+
+QStringList Kopete::ContactList::contactFileProtocols(const QString &displayName)
+{
+// kdDebug(14010) << "Get contacts for: " << displayName << "\n";
+ QStringList protocols;
+
+ Kopete::MetaContact *c = findMetaContactByDisplayName( displayName );
+ if( c )
+ {
+ QPtrList<Kopete::Contact> mContacts = c->contacts();
+ kdDebug(14010) << mContacts.count() << endl;
+ QPtrListIterator<Kopete::Contact> jt( mContacts );
+ for ( ; jt.current(); ++jt )
+ {
+ kdDebug(14010) << "1" << jt.current()->protocol()->pluginId() << endl;
+ if( jt.current()->canAcceptFiles() ) {
+ kdDebug(14010) << jt.current()->protocol()->pluginId() << endl;
+ protocols.append ( jt.current()->protocol()->pluginId() );
+ }
+ }
+ return protocols;
+ }
+ return QStringList();
+}
+
+
+void ContactList::slotSaveLater()
+{
+ // if we already have a save scheduled, it will be cancelled. either way,
+ // start a timer to save the contact list a bit later.
+ d->saveTimer->start( 17100 /* 17,1 seconds */, true /* single-shot */ );
+}
+
+void ContactList::slotKABCChanged()
+{
+ // TODO: react to changes in KABC, replacing this function, post 3.4 (Will)
+ // call syncWithKABC on each metacontact to check if its associated kabc entry has changed.
+/* for ( MetaContact * mc = d->contacts.first(); mc; mc = d->contacts.next() )
+
+ mc->syncWithKABC();*/
+}
+
+
+} //END namespace Kopete
+
+#include "kopetecontactlist.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetecontactlist.h b/kopete/libkopete/kopetecontactlist.h
new file mode 100644
index 00000000..fc6dd5f9
--- /dev/null
+++ b/kopete/libkopete/kopetecontactlist.h
@@ -0,0 +1,405 @@
+/*
+ kopetecontactlist.h - Kopete's Contact List backend
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETECONTACTLIST_H__
+#define KOPETECONTACTLIST_H__
+
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "kopete_export.h"
+
+class KURL;
+class QDomDocument;
+
+
+namespace Kopete
+{
+
+class MetaContact;
+class Group;
+class Contact;
+
+
+/**
+ * @brief manage contacts and metacontact
+ *
+ * The contactList is a singleton you can uses with @ref ContactList::self()
+ *
+ * it let you get a list of metacontact with metaContacts()
+ * Only metacontact which are on the contactlist are returned.
+ *
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart@tiscalinet.be>
+ */
+class KOPETE_EXPORT ContactList : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * The contact list is a singleton object. Use this method to retrieve
+ * the instance.
+ */
+ static ContactList *self();
+ ~ContactList();
+
+ /**
+ * @brief return a list of all metacontact of the contactlist
+ * Retrieve the list of all available meta contacts.
+ * The returned QPtrList is not the internally used variable, so changes
+ * to it won't propagate into the actual contact list. This can be
+ * useful if you need a subset of the contact list, because you can
+ * simply filter the result set as you wish without worrying about
+ * side effects.
+ * The contained MetaContacts are obviously _not_ duplicates, so
+ * changing those *will* have the expected result :-)
+ */
+ QPtrList<MetaContact> metaContacts() const;
+
+ /**
+ * @return all groups
+ */
+ QPtrList<Group> groups() const;
+
+ /**
+ * Return the metacontact referenced by the given id. is none is found, return 0L
+ * @sa MetaContact::metaContactId()
+ */
+ MetaContact *metaContact( const QString &metaContactId ) const;
+
+ /**
+ * return the group with the given unique id. if none is found return 0L
+ */
+ Group * group(unsigned int groupId) const;
+
+
+ /**
+ * @brief find a contact in the contactlist.
+ * Browse in each metacontact of the list to find the contact with the given ID.
+ * @param protocolId the @ref Plugin::pluginId() of the protocol ("MSNProtocol")
+ * @param accountId the @ref Account::accountId()
+ * @param contactId the @ref Contact::contactId()
+ * @return the contact with the parameters, or 0L if not found.
+ */
+ Contact *findContact( const QString &protocolId, const QString &accountId, const QString &contactId ) const;
+
+ /**
+ * Find a contact by display name. Returns the first match.
+ */
+ MetaContact *findMetaContactByDisplayName( const QString &displayName ) const;
+
+ /**
+ * Find a meta contact by its contact id. Returns the first match.
+ */
+ MetaContact *findMetaContactByContactId( const QString &contactId ) const;
+
+ /**
+ * @brief find a group with his displayName
+ * If a group already exists with the given name and the given type, the existing group will be returned.
+ * Otherwise, a new group will be created.
+ * @param displayName is the display name to search
+ * @param type is the Group::GroupType to search, the default value is group::Normal
+ * @return always a valid Group
+ */
+ Group * findGroup( const QString &displayName, int type = 0/*Group::Normal*/ );
+
+ /**
+ * return the list of metacontact actually selected in the contactlist UI
+ */
+ QPtrList<MetaContact> selectedMetaContacts() const;
+
+ /**
+ * return the list of groups actualy selected in the contactlist UI
+ */
+ QPtrList<Group> selectedGroups() const ;
+
+ /**
+ * return the metacontact that represent the user itself.
+ * This metacontact should be the parent of every Kopete::Account::myself() contacts.
+ *
+ * This metacontact is not in the contactlist.
+ */
+ MetaContact* myself();
+
+
+public slots:
+
+ /**
+ * Add the metacontact into the contact list
+ * When calling this method, the contact has to be already placed in the correct group.
+ * If the contact is not in a group, it will be added to the top-level group.
+ * It is also better if the MetaContact could also be completely created, i.e: all contacts already in it
+ */
+ void addMetaContact( Kopete::MetaContact *c );
+
+ /**
+ * Remove a metacontact from the contactlist.
+ * This method delete itself the metacontact.
+ */
+ void removeMetaContact( Kopete::MetaContact *contact );
+
+ /**
+ * Add a group
+ * each group must be added on the list after his creation.
+ */
+ void addGroup(Kopete::Group *);
+
+ /**
+ * Remove a group
+ * this method delete the group
+ */
+ void removeGroup(Kopete::Group *);
+
+ /**
+ * Set which items are selected in the ContactList GUI.
+ * This method has to be called by the contactlist UI side.
+ * it stores the selected items, and emits signals
+ */
+ void setSelectedItems(QPtrList<MetaContact> metaContacts , QPtrList<Group> groups);
+
+ /**
+ * Apply the global identity.
+ */
+ void loadGlobalIdentity();
+
+signals:
+ /**
+ * A meta contact was added to the contact list. Interested classes
+ * ( like the listview widgets ) can connect to this signal to receive
+ * the newly added contacts.
+ */
+ void metaContactAdded( Kopete::MetaContact *mc );
+
+ /**
+ * A metacontact has just been removed. and will be soon deleted
+ */
+ void metaContactRemoved( Kopete::MetaContact *mc );
+
+ /**
+ * A group has just been added
+ */
+ void groupAdded( Kopete::Group * );
+
+ /**
+ * A group has just been removed
+ */
+ void groupRemoved( Kopete::Group * );
+
+ /**
+ * A group has just been renamed
+ */
+ void groupRenamed(Kopete::Group *, const QString & oldname);
+
+ /**
+ * A contact has been added to a group
+ */
+ void metaContactAddedToGroup( Kopete::MetaContact *mc, Kopete::Group *to );
+ /**
+ * A contact has been removed from a group
+ */
+ void metaContactRemovedFromGroup( Kopete::MetaContact *mc, Kopete::Group *from );
+
+ /**
+ * This signal is emit when the selection has changed, it is emitted after the following slot
+ * Warning: Do not delete any contacts in slots connected to this signal. (it is the warning in the QListView::selectionChanged() doc)
+ */
+ void selectionChanged();
+ /**
+ * This signal is emitted each time the selection has changed. the bool is set to true if only one meta contact has been selected,
+ * and set to false if none, or several contacts are selected
+ * you can connect this signal to KAction::setEnabled if you have an action which is applied to only one contact
+ */
+ void metaContactSelected(bool);
+
+ /**
+ * This signal is emitted each time a global identity field change.
+ * HOWTO use:
+ *
+ * - Connect signal globalIdentityChanged(const QString &key, const QVariant
+ * &value) to a slot in your derivate Account class (the best
+ * place to put it).
+ * - In the slot:
+ * - Check the key you want to be sync with global identity.
+ * - Update the myself contact and/or update on server.
+ *
+ * For now, when photo is changed, it always send the photo file path.
+ *
+ * Connect signal in your Account constructor:
+ * @code
+ * connect(Kopete::ContactList::self(), SIGNAL(globalIdentityChanged(const QString&, const QVariant&)), SLOT(slotglobalIdentityChanged(const QString&, const QVariant&)));
+ * @endcode
+ *
+ * Example of a typical implemented slot:
+ * @code
+ * void slotGlobalIdentityChanged(const QString &key, const QVariant &value)
+ * {
+ * if(key == Kopete::Global::Properties::self()->nickName().key())
+ * {
+ * myself()->setProperty(protocol()->propNickname, value.toString());
+ * this->slotUpdateUserInfo();
+ * }
+ * else if(key == Kopete::Global::Properties::self()->photo().key())
+ * {
+ * myself()->setProperty(protocol()->propPhotoUrl, value.toString());
+ * this->slotUpdateDisplayPicture();
+ * }
+ * }
+ * @endcode
+ */
+ void globalIdentityChanged( const QString &key, const QVariant &value );
+
+private slots:
+ /**
+ * Called when the contact list changes. Flags the list dirty and schedules a save for a little while later.
+ */
+ void slotSaveLater();
+ /**
+ * Called on contactlist load or when KABC has changed, to check if we need to update our contactlist from there.
+ */
+ void slotKABCChanged();
+
+ /**
+ * Called when the myself displayName changed.
+ */
+ void slotDisplayNameChanged();
+
+ /**
+ * Called when the myself photo changed.
+ */
+ void slotPhotoChanged();
+
+private:
+
+ /**
+ * Convert the contact list from an older version
+ */
+ void convertContactList( const QString &fileName, uint fromVersion, uint toVersion );
+
+
+ /**
+ * Private constructor: we are a singleton
+ */
+ ContactList();
+
+ static ContactList *s_self;
+ class Private;
+ Private *d;
+
+public: //TODO I think all theses method should be moved to the decop interface.
+ /**
+ * Return all meta contacts
+ */
+ QStringList contacts() const;
+
+ /**
+ * Return all meta contacts that are reachable
+ */
+ QStringList reachableContacts() const;
+
+ /**
+ * Return all contacts that are online
+ */
+ QPtrList<Contact> onlineContacts() const;
+
+ /**
+ * Overloaded method of @ref onlineContacts() that only returns
+ * the online contacts for a single protocol
+ */
+ QPtrList<Contact> onlineContacts( const QString &protocolId ) const;
+
+ /**
+ * Return all meta contacts that are online
+ */
+ QPtrList<MetaContact> onlineMetaContacts() const;
+
+ /**
+ * Overloaded method of @ref onlineMetaContacts() that only returns
+ * the online meta contacts for a single protocol
+ */
+ QPtrList<MetaContact> onlineMetaContacts( const QString &protocolId ) const;
+
+ /**
+ * Returns all contacts which can accept file transfers
+ */
+ QStringList fileTransferContacts() const;
+
+ QStringList contactFileProtocols( const QString &displayName);
+
+ /**
+ * Return all meta contacts with their current status
+ *
+ * FIXME: Do we *need* this one? Sounds error prone to me, because
+ * nicknames can contain parentheses too. - Martijn
+ */
+ QStringList contactStatuses() const;
+
+
+ /**
+ * Exposed via DCOP in kopeteiface
+ * Used to send a file to a MetaContact using the highest ranked protocol
+ *
+ * FIXME: We need to change this to use a unique ID instead of the displayName
+ *
+ * @param displayName Metacontact to send file to
+ * @param sourceURL The file we are sending
+ * @param altFileName (Optional) An alternate filename for the file we are sending
+ * @param fileSize (Optional) The size of the file
+ */
+ void sendFile(const QString &displayName, const KURL &sourceURL,
+ const QString &altFileName = QString::null, const long unsigned int fileSize = 0L);
+
+ /**
+ * Open a chat to a contact, and optionally set some initial text
+ */
+ void messageContact( const QString &displayName, const QString &messageText = QString::null );
+
+public slots:
+ /**
+ * @internal
+ * Load the contact list
+ *
+ * FIXME: Use a better way, without exposing the XML backend, though.
+ */
+ void load();
+
+ void save();
+
+private:
+ /**
+ * Return a XML representation of the contact list
+ */
+ const QDomDocument toXML();
+
+ /**
+ * Load the contact list from XML file
+ */
+ void loadXML();
+
+ /**
+ * Save the contact list to XML file
+ */
+ void saveXML();
+};
+
+} //END namespace Kopete
+
+
+#endif
+
+
diff --git a/kopete/libkopete/kopetecontactlistelement.cpp b/kopete/libkopete/kopetecontactlistelement.cpp
new file mode 100644
index 00000000..2474d1af
--- /dev/null
+++ b/kopete/libkopete/kopetecontactlistelement.cpp
@@ -0,0 +1,261 @@
+/*
+ kopeteplugindataobject.cpp - Kopete Plugin Data Object
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontactlistelement.h"
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "kopeteplugin.h"
+
+namespace Kopete {
+
+class ContactListElement::Private
+{
+public:
+ QMap<QString, QMap<QString, QString> > pluginData;
+ QMap<ContactListElement::IconState, QString> icons;
+ bool useCustomIcon;
+};
+
+ContactListElement::ContactListElement( QObject *parent, const char *name )
+: QObject( parent, name )
+{
+ d = new Private;
+
+ d->useCustomIcon = false;
+#if 0 //TODO
+ connect( Kopete::Global::onlineStatusIconCache(), SIGNAL( iconsChanged() ), SIGNAL( iconAppearanceChanged() ) );
+#endif
+}
+
+ContactListElement::~ContactListElement()
+{
+ delete d;
+}
+
+void ContactListElement::setPluginData( Plugin *plugin, const QMap<QString, QString> &pluginData )
+{
+ if ( pluginData.isEmpty() )
+ {
+ d->pluginData.remove( plugin->pluginId() );
+ return;
+ }
+
+ d->pluginData[ plugin->pluginId() ] = pluginData;
+
+ emit pluginDataChanged();
+}
+
+void ContactListElement::setPluginData( Plugin *p, const QString &key, const QString &value )
+{
+ d->pluginData[ p->pluginId() ][ key ] = value;
+
+ emit pluginDataChanged();
+}
+
+QMap<QString, QString> ContactListElement::pluginData( Plugin *plugin ) const
+{
+ if ( !d->pluginData.contains( plugin->pluginId() ) )
+ return QMap<QString, QString>();
+
+ return d->pluginData[ plugin->pluginId() ];
+}
+
+QString ContactListElement::pluginData( Plugin *plugin, const QString &key ) const
+{
+ if ( !d->pluginData.contains( plugin->pluginId() ) || !d->pluginData[ plugin->pluginId() ].contains( key ) )
+ return QString::null;
+
+ return d->pluginData[ plugin->pluginId() ][ key ];
+}
+
+const QValueList<QDomElement> ContactListElement::toXML()
+{
+ QDomDocument pluginData;
+ QValueList<QDomElement> pluginNodes;
+ pluginData.appendChild( pluginData.createElement( QString::fromLatin1( "plugin-data" ) ) );
+
+ if ( !d->pluginData.isEmpty() )
+ {
+ QMap<QString, QMap<QString, QString> >::ConstIterator pluginIt;
+ for ( pluginIt = d->pluginData.begin(); pluginIt != d->pluginData.end(); ++pluginIt )
+ {
+ QDomElement pluginElement = pluginData.createElement( QString::fromLatin1( "plugin-data" ) );
+ pluginElement.setAttribute( QString::fromLatin1( "plugin-id" ), pluginIt.key() );
+
+ QMap<QString, QString>::ConstIterator it;
+ for ( it = pluginIt.data().begin(); it != pluginIt.data().end(); ++it )
+ {
+ QDomElement pluginDataField = pluginData.createElement( QString::fromLatin1( "plugin-data-field" ) );
+ pluginDataField.setAttribute( QString::fromLatin1( "key" ), it.key() );
+ pluginDataField.appendChild( pluginData.createTextNode( it.data() ) );
+ pluginElement.appendChild( pluginDataField );
+ }
+
+ pluginData.documentElement().appendChild( pluginElement );
+ pluginNodes.append( pluginElement );
+ }
+ }
+ if ( !d->icons.isEmpty() )
+ {
+ QDomElement iconsElement = pluginData.createElement( QString::fromLatin1( "custom-icons" ) );
+ iconsElement.setAttribute( QString::fromLatin1( "use" ), d->useCustomIcon ? QString::fromLatin1( "1" ) : QString::fromLatin1( "0" ) );
+
+ for ( QMap<IconState, QString >::ConstIterator it = d->icons.begin(); it != d->icons.end(); ++it )
+ {
+ QDomElement iconElement = pluginData.createElement( QString::fromLatin1( "icon" ) );
+ QString stateStr;
+ switch ( it.key() )
+ {
+ case Open:
+ stateStr = QString::fromLatin1( "open" );
+ break;
+ case Closed:
+ stateStr = QString::fromLatin1( "closed" );
+ break;
+ case Online:
+ stateStr = QString::fromLatin1( "online" );
+ break;
+ case Away:
+ stateStr = QString::fromLatin1( "away" );
+ break;
+ case Offline:
+ stateStr = QString::fromLatin1( "offline" );
+ break;
+ case Unknown:
+ stateStr = QString::fromLatin1( "unknown" );
+ break;
+ case None:
+ default:
+ stateStr = QString::fromLatin1( "none" );
+ break;
+ }
+ iconElement.setAttribute( QString::fromLatin1( "state" ), stateStr );
+ iconElement.appendChild( pluginData.createTextNode( it.data() ) );
+ iconsElement.appendChild( iconElement );
+ }
+ pluginData.documentElement().appendChild( iconsElement );
+ pluginNodes.append( iconsElement );
+ }
+ return pluginNodes;
+}
+
+bool ContactListElement::fromXML( const QDomElement& element )
+{
+ if ( element.tagName() == QString::fromLatin1( "plugin-data" ) )
+ {
+ QMap<QString, QString> pluginData;
+ QString pluginId = element.attribute( QString::fromLatin1( "plugin-id" ), QString::null );
+
+ //in kopete 0.6 the AIM protocol was called OSCAR
+ if ( pluginId == QString::fromLatin1( "OscarProtocol" ) )
+ pluginId = QString::fromLatin1( "AIMProtocol" );
+
+ QDomNode field = element.firstChild();
+ while( !field.isNull() )
+ {
+ QDomElement fieldElement = field.toElement();
+ if ( fieldElement.tagName() == QString::fromLatin1( "plugin-data-field" ) )
+ {
+ pluginData.insert( fieldElement.attribute( QString::fromLatin1( "key" ),
+ QString::fromLatin1( "undefined-key" ) ), fieldElement.text() );
+ }
+ field = field.nextSibling();
+ }
+ d->pluginData.insert( pluginId, pluginData );
+ }
+ else if ( element.tagName() == QString::fromLatin1( "custom-icons" ) )
+ {
+ d->useCustomIcon= element.attribute( QString::fromLatin1( "use" ), QString::fromLatin1( "1" ) ) == QString::fromLatin1( "1" );
+ QDomNode ic = element.firstChild();
+ while( !ic.isNull() )
+ {
+ QDomElement iconElement = ic.toElement();
+ if ( iconElement.tagName() == QString::fromLatin1( "icon" ) )
+ {
+ QString stateStr = iconElement.attribute( QString::fromLatin1( "state" ), QString::null );
+ QString icon = iconElement.text();
+ IconState state = None;
+
+ if ( stateStr == QString::fromLatin1( "open" ) )
+ state = Open;
+ if ( stateStr == QString::fromLatin1( "closed" ) )
+ state = Closed;
+ if ( stateStr == QString::fromLatin1( "online" ) )
+ state = Online;
+ if ( stateStr == QString::fromLatin1( "offline" ) )
+ state = Offline;
+ if ( stateStr == QString::fromLatin1( "away" ) )
+ state = Away;
+ if ( stateStr == QString::fromLatin1( "unknown" ) )
+ state = Unknown;
+
+ d->icons[ state ] = icon;
+ }
+ ic = ic.nextSibling();
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+QString ContactListElement::icon( ContactListElement::IconState state ) const
+{
+ if ( d->icons.contains( state ) )
+ return d->icons[state];
+
+ return d->icons[ None ];
+}
+
+void ContactListElement::setIcon( const QString& icon , ContactListElement::IconState state )
+{
+ if ( icon.isNull() )
+ d->icons.remove( state );
+ else
+ d->icons[ state ] = icon;
+
+ emit iconChanged( state, icon );
+ emit iconAppearanceChanged();
+}
+
+bool ContactListElement::useCustomIcon() const
+{
+ return d->useCustomIcon;
+}
+
+void ContactListElement::setUseCustomIcon( bool useCustomIcon )
+{
+ if ( d->useCustomIcon != useCustomIcon )
+ {
+ d->useCustomIcon = useCustomIcon;
+ emit useCustomIconChanged( useCustomIcon );
+ }
+}
+
+} //END namespace Kopete
+
+#include "kopetecontactlistelement.moc"
+
+
+
diff --git a/kopete/libkopete/kopetecontactlistelement.h b/kopete/libkopete/kopetecontactlistelement.h
new file mode 100644
index 00000000..b0f2eb69
--- /dev/null
+++ b/kopete/libkopete/kopetecontactlistelement.h
@@ -0,0 +1,172 @@
+/*
+ kopeteplugindataobject.h - Kopete Plugin Data Object
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPLUGINDATAOBJECT_H
+#define KOPETEPLUGINDATAOBJECT_H
+
+#include <qobject.h>
+#include <qdom.h>
+
+#include "kopete_export.h"
+
+namespace Kopete {
+
+class Plugin;
+
+
+/**
+ * @author Olivier Goffart <ogoffart@ tiscalinet.be>
+ *
+ * This is the base class for base elements of the contactlist.
+ * His purpose is to share the code between @ref Group and @ref MetaContact
+ *
+ * It handle the saving and loading of plugin data from the contactlist.
+ * Plugins may set custom datas to metaocntacts or groups by calling @ref setPluginData
+ * and may retreive them with @ref pluginData
+ *
+ * It also allow to store an icon for this element.
+ */
+class KOPETE_EXPORT ContactListElement : public QObject /* public KopeteNotifyDataObject */
+{
+ Q_OBJECT
+
+protected:
+ ContactListElement( QObject *parent = 0L, const char *name = 0L );
+ ~ContactListElement();
+
+
+public:
+
+ /**
+ * Set the plugin-specific data.
+ * The data in the provided QMap is a set of key/value pairs.
+ * Note that protocol plugins usually shouldn't use this method, but
+ * reimplement @ref Contact::serialize() instead. This method
+ * is called by @ref Protocol for those classes.
+ *
+ * WARNING: This erases all old data stored for this object!
+ * You may want to consider the @ref setPluginData() overload
+ * that takes a single field as parameter.
+ */
+ void setPluginData( Plugin *plugin, const QMap<QString, QString> &value );
+
+ /**
+ * Convenience method to store or change only a single field of the
+ * plugin data. As with the other @ref setPluginData() method, protocols
+ * are advised not to use this method and reimplement
+ * @ref Contact::serialize() instead.
+ *
+ * Note that you should save the file after adding data or it will get lost.
+ */
+ void setPluginData( Plugin *plugin, const QString &key, const QString &value );
+
+ /**
+ * Get the settings as stored previously by calls to @ref setPluginData()
+ *
+ * Note that calling this method for protocol plugins that use the
+ * @ref Contact::serialize() API may yield unexpected results.
+ */
+ QMap<QString, QString> pluginData( Plugin *plugin ) const;
+
+ /**
+ * Convenience method to retrieve only a single field from the plugin
+ * data. See @ref setPluginData().
+ *
+ * Note that plugin data is accessible only after it has been loaded
+ * from the XML file. Don't call this method before then (e.g. in
+ * constructors).
+ */
+ QString pluginData( Plugin *plugin, const QString &key ) const;
+
+ /**
+ * The various icon states. Some state are reserved for Groups,
+ * other for metacontact.
+ * 'None' is the default icon.
+ */
+ enum IconState { None, Open, Closed, Online, Away, Offline, Unknown };
+
+ /**
+ * return the icon for this object, in the given state.
+ * if there is no icon registered for this state, the None icon is used
+ * if available
+ */
+ QString icon( IconState state = None ) const;
+
+ /**
+ * Set the icon in the given state
+ * To clear an entry, set a QString::null
+ */
+ void setIcon( const QString &icon, IconState = None );
+
+ /**
+ * return if yes or no the user wants to display some custom icon.
+ * you can use @ref icon() to know the icons to uses
+ */
+ bool useCustomIcon() const;
+
+ /**
+ * set if the user want to show custom icon he set with @ref setIcon
+ * this does not clear icons string if you set false
+ */
+ void setUseCustomIcon( bool useCustomIcon );
+
+signals:
+ /**
+ * The plugin data was changed (by a plugin)
+ */
+ void pluginDataChanged();
+
+ /**
+ * The icon to use for some state has changed
+ */
+ void iconChanged( Kopete::ContactListElement::IconState, const QString & );
+
+ /**
+ * The visual appearance of some of our icons has changed
+ */
+ void iconAppearanceChanged();
+
+ /**
+ * The useCustomIcon property has changed
+ */
+ void useCustomIconChanged( bool useCustomIcon );
+
+protected:
+ /**
+ * Return a XML representation of plugin data
+ */
+ const QValueList<QDomElement> toXML();
+
+ /**
+ * Load plugin data from one Dom Element:
+ * It should be a <plugin-data> element or a <custom-icons> element. if not, nothing will happen
+ * @return true if something has ben loaded. false if the element was not a fine
+ */
+ bool fromXML( const QDomElement &element );
+
+private:
+ class Private;
+ Private *d;
+};
+
+} //END namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetecontactproperty.cpp b/kopete/libkopete/kopetecontactproperty.cpp
new file mode 100644
index 00000000..87e176af
--- /dev/null
+++ b/kopete/libkopete/kopetecontactproperty.cpp
@@ -0,0 +1,204 @@
+/*
+ kopetecontactproperty.cpp
+
+ Kopete::Contact Property class
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontactproperty.h"
+#include <kdebug.h>
+#include "kopeteglobal.h"
+
+namespace Kopete
+{
+
+struct ContactPropertyTmplPrivate
+{
+ QString key;
+ QString label;
+ QString icon;
+ bool persistent;
+ bool richText;
+ bool privateProp;
+ unsigned int refCount;
+};
+
+ContactPropertyTmpl ContactPropertyTmpl::null;
+
+
+ContactPropertyTmpl::ContactPropertyTmpl()
+{
+ d = new ContactPropertyTmplPrivate;
+ d->refCount = 1;
+ d->persistent = false;
+ // Don't register empty template
+}
+
+ContactPropertyTmpl::ContactPropertyTmpl(const QString &key,
+ const QString &label, const QString &icon, bool persistent, bool richText, bool privateProp)
+{
+ ContactPropertyTmpl other = Kopete::Global::Properties::self()->tmpl(key);
+ if(other.isNull())
+ {
+// kdDebug(14000) << k_funcinfo << "Creating new template for key = '" << key << "'" << endl;
+
+ d = new ContactPropertyTmplPrivate;
+ d->refCount = 1;
+ d->key = key;
+ d->label = label;
+ d->icon = icon;
+ d->persistent = persistent;
+ d->richText = richText;
+ d->privateProp = privateProp;
+ Kopete::Global::Properties::self()->registerTemplate(key, (*this));
+ }
+ else
+ {
+// kdDebug(14000) << k_funcinfo << "Using existing template for key = '" << key << "'" << endl;
+ d = other.d;
+ d->refCount++;
+ }
+}
+
+ContactPropertyTmpl::ContactPropertyTmpl(const ContactPropertyTmpl &other)
+{
+ d = other.d;
+ d->refCount++;
+}
+
+ContactPropertyTmpl &ContactPropertyTmpl::operator=(
+ const ContactPropertyTmpl &other)
+{
+ d->refCount--;
+ if(d->refCount == 0)
+ {
+ if (!d->key.isEmpty()) // null property
+ Kopete::Global::Properties::self()->unregisterTemplate(d->key);
+ delete d;
+ }
+
+ d = other.d;
+ d->refCount++;
+
+ return *this;
+}
+
+ContactPropertyTmpl::~ContactPropertyTmpl()
+{
+ d->refCount--;
+ if(d->refCount == 0)
+ {
+ if (!d->key.isEmpty()) // null property
+ Kopete::Global::Properties::self()->unregisterTemplate(d->key);
+ delete d;
+ }
+}
+
+bool ContactPropertyTmpl::operator==(const ContactPropertyTmpl &other) const
+{
+ return (d && other.d &&
+ d->key == other.d->key &&
+ d->label == other.d->label &&
+ d->icon == other.d->key &&
+ d->persistent == other.d->persistent);
+}
+
+bool ContactPropertyTmpl::operator!=(const ContactPropertyTmpl &other) const
+{
+ return (!d || !other.d ||
+ d->key != other.d->key ||
+ d->label != other.d->label ||
+ d->icon != other.d->key ||
+ d->persistent != other.d->persistent);
+}
+
+
+const QString &ContactPropertyTmpl::key() const
+{
+ return d->key;
+}
+
+const QString &ContactPropertyTmpl::label() const
+{
+ return d->label;
+}
+
+const QString &ContactPropertyTmpl::icon() const
+{
+ return d->icon;
+}
+
+bool ContactPropertyTmpl::persistent() const
+{
+ return d->persistent;
+}
+
+bool ContactPropertyTmpl::isRichText() const
+{
+ return d->richText;
+}
+
+bool ContactPropertyTmpl::isPrivate() const
+{
+ return d->privateProp;
+}
+
+bool ContactPropertyTmpl::isNull() const
+{
+ return (!d || d->key.isNull());
+}
+
+
+// -----------------------------------------------------------------------------
+
+
+ContactProperty ContactProperty::null;
+
+ContactProperty::ContactProperty()
+{
+}
+
+ContactProperty::ContactProperty(const ContactPropertyTmpl &tmpl,
+ const QVariant &val)
+{
+ mTemplate = tmpl;
+ mValue = val;
+}
+
+ContactProperty::~ContactProperty()
+{
+ //kdDebug(14000) << k_funcinfo << "this = " << (void *)this << endl;
+}
+
+const QVariant &ContactProperty::value() const
+{
+ return mValue;
+}
+
+const ContactPropertyTmpl &ContactProperty::tmpl() const
+{
+ return mTemplate;
+}
+
+bool ContactProperty::isNull() const
+{
+ return mValue.isNull();
+}
+
+bool ContactProperty::isRichText() const
+{
+ return mTemplate.isRichText();
+}
+
+} // END namespace Kopete
diff --git a/kopete/libkopete/kopetecontactproperty.h b/kopete/libkopete/kopetecontactproperty.h
new file mode 100644
index 00000000..b5c8f060
--- /dev/null
+++ b/kopete/libkopete/kopetecontactproperty.h
@@ -0,0 +1,195 @@
+/*
+ kopetecontactproperty.h
+
+ Kopete::Contact Property class
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETECONTACTPROPERTY_H_
+#define _KOPETECONTACTPROPERTY_H_
+
+#include <qvariant.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+struct ContactPropertyTmplPrivate;
+
+/**
+ * @author Stefan Gehn <metz AT gehn.net>
+ *
+ * The template class for registering properties in Kopete
+ * You need to use this if you want to set properties for a
+ * Kopete::Contact
+ **/
+class KOPETE_EXPORT ContactPropertyTmpl
+{
+ public:
+ /**
+ * Constructor only used for empty ContactPropertyTmpl objects
+ *
+ * Note: Only useful for the null object
+ **/
+ ContactPropertyTmpl();
+
+ /**
+ * Constructor
+ * @param key internal unique key for this template
+ * @param label a label to show for properties based on this template
+ * @param icon name of the icon to show for properties based on this template
+ * @param persistent if true, properties based on this template will be
+ * saved to the contactlist.
+ * @param richText Indicate that this property should be able to handle rich text
+ * @param privateProp if true, properties based on this template won't be
+ * visible to the user
+ **/
+ ContactPropertyTmpl( const QString &key,
+ const QString &label,
+ const QString &icon = QString::null,
+ bool persistent = false,
+ bool richText = false,
+ bool privateProp = false );
+
+ /**
+ * Copy constructor
+ **/
+ ContactPropertyTmpl(const ContactPropertyTmpl &other);
+
+ /** Destructor */
+ ~ContactPropertyTmpl();
+
+ ContactPropertyTmpl &operator=(const ContactPropertyTmpl &other);
+
+ bool operator==(const ContactPropertyTmpl &other) const;
+ bool operator!=(const ContactPropertyTmpl &other) const;
+
+ /**
+ * Getter for the unique key. Properties based on this template will be
+ * stored with this key
+ **/
+ const QString &key() const;
+
+ /**
+ * Getter for i18ned label
+ **/
+ const QString &label() const;
+
+ /**
+ * Getter for icon to show aside or instead of @p label()
+ **/
+ const QString &icon() const;
+
+ /**
+ * Returns true if properties based on this template should
+ * be saved across Kopete sessions, false otherwise.
+ **/
+ bool persistent() const;
+
+ /**
+ * Returns true if properties based on this template are HTML formatted
+ **/
+ bool isRichText() const;
+
+ /**
+ * Returns true if properties based on this template are invisible to the user
+ **/
+ bool isPrivate() const;
+
+ /**
+ * An empty template, check for it using isNull()
+ */
+ static ContactPropertyTmpl null;
+
+ /**
+ * Returns true if this object is an empty template
+ **/
+ bool isNull() const;
+
+ /**
+ * A Map of QString and ContactPropertyTmpl objects, based on QMap
+ **/
+ typedef QMap<QString, ContactPropertyTmpl> Map;
+
+ private:
+ ContactPropertyTmplPrivate *d;
+};
+
+
+/**
+ * @author Stefan Gehn <metz AT gehn.net>
+ *
+ * A data container for whatever information Kopete or any of its
+ * plugins want to store for a Kopete::Contact
+ **/
+class KOPETE_EXPORT ContactProperty
+{
+ // TODO: Add d-pointer !
+ public:
+ /**
+ * Constructor only used for empty ContactProperty objects
+ *
+ * Note: you cannot set a label or value later on!
+ **/
+ ContactProperty();
+
+ /**
+ * @param tmpl The contact property template this property is based on
+ * @param value The value this Property holds
+ **/
+ ContactProperty(const ContactPropertyTmpl &tmpl, const QVariant &value);
+
+ /** Destructor **/
+ ~ContactProperty();
+
+ /**
+ * Getter for this properties template
+ **/
+ const ContactPropertyTmpl &tmpl() const;
+
+ /**
+ * Getter for this properties value
+ **/
+ const QVariant &value() const;
+
+ /**
+ * The null, i.e. empty, ContactProperty
+ */
+ static ContactProperty null;
+
+ /**
+ * Returns true if this object is an empty Property (i.e. it holds no
+ * value), false otherwise.
+ **/
+ bool isNull() const;
+
+ /**
+ * Returns true if this property is HTML formatted
+ **/
+ bool isRichText() const;
+
+ /**
+ * A map of key,ContactProperty items
+ **/
+ typedef QMap<QString, ContactProperty> Map;
+
+ private:
+ QVariant mValue;
+ ContactPropertyTmpl mTemplate;
+};
+
+} // END namespace Kopete
+
+#endif //_KOPETECONTACTPROPERTY_H_
diff --git a/kopete/libkopete/kopeteeventpresentation.cpp b/kopete/libkopete/kopeteeventpresentation.cpp
new file mode 100644
index 00000000..f90a19e5
--- /dev/null
+++ b/kopete/libkopete/kopeteeventpresentation.cpp
@@ -0,0 +1,90 @@
+/*
+ kopeteeventpresentation.cpp - Kopete Custom Notify Data Object
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteeventpresentation.h"
+
+Kopete::EventPresentation::EventPresentation( const PresentationType type )
+{
+ m_type = type;
+}
+
+Kopete::EventPresentation::EventPresentation( const PresentationType type,
+ const QString &content, const bool singleShot, const bool enabled )
+{
+ m_type = type;
+ m_content = content;
+ m_singleShot = singleShot;
+ m_enabled = enabled;
+}
+
+Kopete::EventPresentation::~EventPresentation()
+{
+}
+
+Kopete::EventPresentation::PresentationType Kopete::EventPresentation::type()
+{
+ return m_type;
+}
+
+QString Kopete::EventPresentation::content()
+{
+ return m_content;
+}
+
+bool Kopete::EventPresentation::enabled()
+{
+ return m_enabled;
+}
+
+bool Kopete::EventPresentation::singleShot()
+{
+ return m_singleShot;
+}
+
+void Kopete::EventPresentation::setContent( const QString &content )
+{
+ m_content = content;
+}
+
+void Kopete::EventPresentation::setEnabled( const bool enabled )
+{
+ m_enabled = enabled;
+}
+
+void Kopete::EventPresentation::setSingleShot( const bool singleShot )
+{
+ m_singleShot = singleShot;
+}
+
+QString Kopete::EventPresentation::toString()
+{
+ QString type;
+ switch ( m_type )
+ {
+ case Sound:
+ type= QString::fromLatin1("sound");
+ break;
+ case Message:
+ type= QString::fromLatin1("message");
+ break;
+ case Chat:
+ type= QString::fromLatin1("chat");
+ break;
+ }
+ QString stringRep = QString::fromLatin1( "Presentation; type=%1; content=%2; enabled=%3; single shot=%4\n" ).arg(type).arg(m_content).arg(m_enabled).arg(m_singleShot);
+ return stringRep;
+}
diff --git a/kopete/libkopete/kopeteeventpresentation.h b/kopete/libkopete/kopeteeventpresentation.h
new file mode 100644
index 00000000..ea30cb5d
--- /dev/null
+++ b/kopete/libkopete/kopeteeventpresentation.h
@@ -0,0 +1,56 @@
+/*
+ kopeteeventpresentation.h - Kopete Custom Notify Data Object
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEEVENTPRESENTATION_H
+#define KOPETEEVENTPRESENTATION_H
+
+#include <qstring.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+class KOPETE_EXPORT EventPresentation
+{
+public:
+ enum PresentationType { Sound, Message, Chat };
+ EventPresentation( const PresentationType type );
+ EventPresentation( const PresentationType type,
+ const QString &content = QString::null,
+ const bool singleShot = false, const bool enabled = false );
+ ~EventPresentation();
+
+ PresentationType type();
+ QString content();
+ bool enabled();
+ bool singleShot();
+
+ void setContent( const QString &content );
+ void setEnabled( const bool enabled );
+ void setSingleShot( const bool singleShot );
+ QString toString();
+private:
+ PresentationType m_type;
+ QString m_content;
+ bool m_enabled;
+ bool m_singleShot;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopeteglobal.cpp b/kopete/libkopete/kopeteglobal.cpp
new file mode 100644
index 00000000..a11dafdd
--- /dev/null
+++ b/kopete/libkopete/kopeteglobal.cpp
@@ -0,0 +1,346 @@
+/*
+ kopeteglobal.cpp - Kopete Globals
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+#include <kmimetype.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <kstandarddirs.h>
+#include <ktar.h>
+#include <kzip.h>
+#include <kmimetype.h>
+
+
+namespace Kopete
+{
+
+namespace Global
+{
+
+class PropertiesPrivate
+{
+ public:
+ ContactPropertyTmpl::Map mTemplates;
+};
+
+Properties *Properties::mSelf = 0L;
+
+Properties *Properties::self()
+{
+ if(!mSelf)
+ {
+ //kdDebug(14000) << k_funcinfo << endl;
+ mSelf = new Properties();
+ }
+ return mSelf;
+}
+
+Properties::Properties()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ d = new PropertiesPrivate();
+}
+
+Properties::~Properties()
+{
+ kdDebug(14000) << k_funcinfo << endl;
+ delete d;
+}
+
+const ContactPropertyTmpl &Properties::tmpl(const QString &key) const
+{
+ if(d->mTemplates.contains(key))
+ {
+ /*kdDebug(14000) << k_funcinfo <<
+ "Found template for key = '" << key << "'" << endl;*/
+ return d->mTemplates[key];
+ }
+ else
+ return ContactPropertyTmpl::null;
+}
+
+bool Properties::registerTemplate(const QString &key,
+ const ContactPropertyTmpl &tmpl)
+{
+ if(d->mTemplates.contains(key))
+ {
+ kdDebug(14000) << k_funcinfo <<
+ "Called for EXISTING key = '" << key << "'" << endl;
+ return false;
+ }
+ else
+ {
+ d->mTemplates.insert(key, tmpl);
+ return true;
+ }
+}
+
+void Properties::unregisterTemplate(const QString &key)
+{
+ kdDebug(14000) << k_funcinfo << "called for key: '" << key << "'" << endl;
+ d->mTemplates.remove(key);
+}
+
+bool Properties::isRegistered(const QString &key)
+{
+ return d->mTemplates.contains(key);
+}
+
+const ContactPropertyTmpl &Properties::fullName() const
+{
+ return createProp(QString::fromLatin1("FormattedName"),
+ i18n("Full Name"));
+}
+
+const ContactPropertyTmpl &Properties::idleTime() const
+{
+ return createProp(QString::fromLatin1("idleTime"),
+ i18n("Idle Time"));
+}
+
+const ContactPropertyTmpl &Properties::onlineSince() const
+{
+ return createProp(QString::fromLatin1("onlineSince"),
+ i18n("Online Since"));
+}
+
+const ContactPropertyTmpl &Properties::lastSeen() const
+{
+ return createProp(QString::fromLatin1("lastSeen"),
+ i18n("Last Seen"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::awayMessage() const
+{
+ return createProp(QString::fromLatin1("awayMessage"),
+ i18n("Away Message"));
+}
+
+const ContactPropertyTmpl &Properties::firstName() const
+{
+ return createProp(QString::fromLatin1("firstName"),
+ i18n("First Name"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::lastName() const
+{
+ return createProp(QString::fromLatin1("lastName"),
+ i18n("Last Name"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::privatePhone() const
+{
+ return createProp(QString::fromLatin1("privatePhoneNumber"),
+ i18n("Private Phone"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::privateMobilePhone() const
+{
+ return createProp(QString::fromLatin1("privateMobilePhoneNumber"),
+ i18n("Private Mobile Phone"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::workPhone() const
+{
+ return createProp(QString::fromLatin1("workPhoneNumber"),
+ i18n("Work Phone"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::workMobilePhone() const
+{
+ return createProp(QString::fromLatin1("workMobilePhoneNumber"),
+ i18n("Work Mobile Phone"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::emailAddress() const
+{
+ return createProp(QString::fromLatin1("emailAddress"),
+ i18n("Email Address"), QString::fromLatin1("mail_generic"), true);
+}
+
+const ContactPropertyTmpl &Properties::nickName() const
+{
+ return createProp(QString::fromLatin1("nickName"),
+ i18n("Nick Name"), QString::null, true);
+}
+
+const ContactPropertyTmpl &Properties::photo() const
+{
+ return createProp(QString::fromLatin1("photo"),
+ i18n("Photo"), QString::null, true);
+}
+
+
+const ContactPropertyTmpl &Properties::createProp(const QString &key,
+ const QString &label, const QString &icon, bool persistent) const
+{
+ /*kdDebug(14000) << k_funcinfo <<
+ "key = " << key << ", label = " << label << endl;*/
+
+ if(!d->mTemplates.contains(key))
+ {
+/* kdDebug(14000) << k_funcinfo <<
+ "CREATING NEW ContactPropertyTmpl WITH key = " << key <<
+ ", label = " << label << ", persisten = " << persistent << endl;*/
+ d->mTemplates.insert(key, ContactPropertyTmpl(key, label, icon, persistent));
+ }
+ return tmpl(key);
+}
+
+const ContactPropertyTmpl::Map &Properties::templateMap() const
+{
+ return d->mTemplates;
+}
+
+
+// -----------------------------------------------------------------------------
+
+
+void installEmoticonTheme(const QString &archiveName)
+{
+ QStringList foundThemes;
+ KArchiveEntry *currentEntry = 0L;
+ KArchiveDirectory* currentDir = 0L;
+ KProgressDialog *progressDlg = 0L;
+ KArchive *archive = 0L;
+
+ QString localThemesDir(locateLocal("emoticons", QString::null) );
+
+ if(localThemesDir.isEmpty())
+ {
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(),
+ KMessageBox::Error, i18n("Could not find suitable place " \
+ "to install emoticon themes into."));
+ return;
+ }
+
+ progressDlg = new KProgressDialog(0 , "emoticonInstProgress",
+ i18n("Installing Emoticon Themes..."), QString::null, true);
+ progressDlg->progressBar()->setTotalSteps(foundThemes.count());
+ progressDlg->show();
+ kapp->processEvents();
+
+ QString currentBundleMimeType = KMimeType::findByPath(archiveName, 0, false)->name();
+ if( currentBundleMimeType == QString::fromLatin1("application/x-zip") )
+ archive = new KZip(archiveName);
+ else if( currentBundleMimeType == QString::fromLatin1("application/x-tgz") ||
+ currentBundleMimeType == QString::fromLatin1("application/x-tbz") ||
+ currentBundleMimeType == QString::fromLatin1("application/x-gzip") ||
+ currentBundleMimeType == QString::fromLatin1("application/x-bzip2") )
+ archive = new KTar(archiveName);
+ else if(archiveName.endsWith(QString::fromLatin1("jisp")) || archiveName.endsWith(QString::fromLatin1("zip")) )
+ archive = new KZip(archiveName);
+ else
+ archive = new KTar(archiveName);
+
+ if ( !archive || !archive->open(IO_ReadOnly) )
+ {
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(),
+ KMessageBox::Error,
+ i18n("Could not open \"%1\" for unpacking.").arg(archiveName));
+ delete archive;
+ delete progressDlg;
+ return;
+ }
+
+ const KArchiveDirectory* rootDir = archive->directory();
+
+ // iterate all the dirs looking for an emoticons.xml file
+ QStringList entries = rootDir->entries();
+ for (QStringList::Iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ currentEntry = const_cast<KArchiveEntry*>(rootDir->entry(*it));
+ if (currentEntry->isDirectory())
+ {
+ currentDir = dynamic_cast<KArchiveDirectory*>( currentEntry );
+ if (currentDir && ( currentDir->entry(QString::fromLatin1("emoticons.xml")) != NULL ||
+ currentDir->entry(QString::fromLatin1("icondef.xml")) != NULL ) )
+ foundThemes.append(currentDir->name());
+ }
+ }
+
+ if (foundThemes.isEmpty())
+ {
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(),
+ KMessageBox::Error, i18n("<qt>The file \"%1\" is not a valid" \
+ " emoticon theme archive.</qt>").arg(archiveName));
+ archive->close();
+ delete archive;
+ delete progressDlg;
+ return;
+ }
+
+ for (QStringList::ConstIterator it = foundThemes.begin(); it != foundThemes.end(); ++it)
+ {
+ progressDlg->setLabel(
+ i18n("<qt>Installing <strong>%1</strong> emoticon theme</qt>")
+ .arg(*it));
+ progressDlg->resize(progressDlg->sizeHint());
+ kapp->processEvents();
+
+ if (progressDlg->wasCancelled())
+ break;
+
+ currentEntry = const_cast<KArchiveEntry *>(rootDir->entry(*it));
+ if (currentEntry == 0)
+ {
+ kdDebug(14010) << k_funcinfo << "couldn't get next archive entry" << endl;
+ continue;
+ }
+
+ if(currentEntry->isDirectory())
+ {
+ currentDir = dynamic_cast<KArchiveDirectory*>(currentEntry);
+ if (currentDir == 0)
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "couldn't cast archive entry to KArchiveDirectory" << endl;
+ continue;
+ }
+ currentDir->copyTo(localThemesDir + *it);
+ progressDlg->progressBar()->advance(1);
+ }
+ }
+
+ archive->close();
+ delete archive;
+
+ // check if all steps were done, if there are skipped ones then we didn't
+ // succeed copying all dirs from the tarball
+ if (progressDlg->progressBar()->totalSteps() > progressDlg->progressBar()->progress())
+ {
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(),
+ KMessageBox::Error,
+ i18n("<qt>A problem occurred during the installation process. "
+ "However, some of the emoticon themes in the archive may have been "
+ "installed.</qt>"));
+ }
+
+ delete progressDlg;
+}
+
+} // END namespace Global
+
+} // END namespace Kopete
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteglobal.h b/kopete/libkopete/kopeteglobal.h
new file mode 100644
index 00000000..aa6456f4
--- /dev/null
+++ b/kopete/libkopete/kopeteglobal.h
@@ -0,0 +1,176 @@
+/*
+ kopeteglobal.h - Kopete Globals
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEGLOBAL_H
+#define KOPETEGLOBAL_H
+
+#include "kopetecontactproperty.h"
+
+#include "kopete_export.h"
+
+/**
+ * This namespace contains all of Kopete's core classes and functions.
+ */
+namespace Kopete
+{
+
+/**
+ * This namespace contains Kopete's global settings and functions
+ */
+namespace Global
+{
+ class PropertiesPrivate;
+
+ /**
+ * \brief Installs one or more kopete emoticon themes from a tarball
+ * (either .kopete-emoticons or .tar.gz or .tar.bz2)
+ *
+ * @p localPath Full path to a local emoticon archive, use KIO to download
+ * files in case their are non-local.
+ *
+ * @return true in case install was successful, false otherwise. Errors are
+ * displayed by either KIO or by using KMessagebox directly.
+ *
+ * TODO: If possible, port it to KIO instead of using ugly blocking KTar
+ **/
+ KOPETE_EXPORT void installEmoticonTheme(const QString &localPath);
+
+ /**
+ * \brief Global facility to query/store templates that are needed by KopeteContactProperty
+ *
+ * Basically all a plugin author needs to worry about is creating ContactPropertyTmpl
+ * objects for all the properties he wants to set for a Kopete::Contact,
+ * everything else is handled behind the scenes.
+ **/
+ class KOPETE_EXPORT Properties
+ {
+ friend class Kopete::ContactPropertyTmpl;
+ public:
+ /**
+ * \brief Singleton accessor for this class.
+ *
+ * Use it to access the global list of property-templates or to get
+ * a reference to one of the common ContactPropertyTmpl objects
+ */
+ static Properties *self();
+
+ /**
+ * Return a template with defined by @p key, if no such template has
+ * been registered ContactPropertyTmpl::null will be returned
+ */
+ const ContactPropertyTmpl &tmpl(const QString &key) const;
+
+ /**
+ * @return a ready-to-use template for a contact's full name.
+ *
+ * This is actually no real property, it makes use of
+ * firstName() and lastName() to assemble an name that consists of
+ * both name parts
+ */
+ const ContactPropertyTmpl &fullName() const;
+
+ /**
+ * Return default template for a contact's idle-time
+ */
+ const ContactPropertyTmpl &idleTime() const;
+ /**
+ * Return default template for a contact's online-since time
+ * (i.e. time since he went from offline to online)
+ */
+ const ContactPropertyTmpl &onlineSince() const;
+ /**
+ * @return default template for a contact's last-seen time
+ */
+ const ContactPropertyTmpl &lastSeen() const;
+ /**
+ * @return default template for a contact's away-message
+ */
+ const ContactPropertyTmpl &awayMessage() const;
+ /**
+ * @return default template for a contact's first name
+ */
+ const ContactPropertyTmpl &firstName() const;
+ /**
+ * @return default template for a contact's last name
+ */
+ const ContactPropertyTmpl &lastName() const;
+ /**
+ * @return default template for a contact's email-address
+ */
+ const ContactPropertyTmpl &emailAddress() const;
+ /**
+ * @return default template for a contact's private phone number
+ */
+ const ContactPropertyTmpl &privatePhone() const;
+ /**
+ * @return default template for a contact's private mobile number
+ */
+ const ContactPropertyTmpl &privateMobilePhone() const;
+ /**
+ * @return default template for a contact's work phone number
+ */
+ const ContactPropertyTmpl &workPhone() const;
+ /**
+ * @return default template for a contact's work mobile number
+ */
+ const ContactPropertyTmpl &workMobilePhone() const;
+ /**
+ * @return default template for a contact's nickname (set by the contact)
+ */
+ const ContactPropertyTmpl &nickName() const;
+ /**
+ * default template for a contact's photo.
+ *
+ * It could be either a QString or a QImage.
+ * If it's a QString, it should points to the path the image is stored.
+ */
+ const ContactPropertyTmpl &photo() const;
+
+ /**
+ * @return a map of all registered ContactPropertyTmpl object
+ */
+ const ContactPropertyTmpl::Map &templateMap() const;
+
+ /**
+ * return true if a template with key @p key is already registered,
+ * false otherwise
+ */
+ bool isRegistered(const QString &key);
+
+ private:
+ Properties();
+ ~Properties();
+
+ bool registerTemplate(const QString &key,
+ const ContactPropertyTmpl &tmpl);
+ void unregisterTemplate(const QString &key);
+
+ const ContactPropertyTmpl &createProp(const QString &key,
+ const QString &label, const QString &icon=QString::null,
+ bool persistent = false) const;
+
+ private:
+ static Properties *mSelf;
+ PropertiesPrivate *d;
+ }; // end class Properties
+
+} // Global
+
+} // Kopete
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetegroup.cpp b/kopete/libkopete/kopetegroup.cpp
new file mode 100644
index 00000000..f50eb08b
--- /dev/null
+++ b/kopete/libkopete/kopetegroup.cpp
@@ -0,0 +1,335 @@
+/*
+ kopetegroup.cpp - Kopete (Meta)Contact Group
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetegroup.h"
+
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopetechatsession.h"
+
+#include <klocale.h>
+
+namespace Kopete {
+
+class Group::Private
+{
+public:
+ QString displayName;
+ Group::GroupType type;
+ bool expanded;
+ uint groupId;
+
+ //Unique contact id per metacontact
+ static uint uniqueGroupId;
+};
+
+Group *Group::s_topLevel = 0L;
+Group *Group::s_temporary = 0L;
+Group * Group::topLevel()
+{
+ if ( !s_topLevel )
+ s_topLevel = new Group( i18n( "Top Level" ), Group::TopLevel );
+
+ return s_topLevel;
+}
+
+Group * Group::temporary()
+{
+ if ( !s_temporary )
+ s_temporary = new Group( i18n( "Not in your contact list" ), Group::Temporary );
+
+ return s_temporary;
+}
+
+uint Group::Private::uniqueGroupId = 0;
+
+Group::Group( const QString &_name, GroupType _type )
+ : ContactListElement( ContactList::self() )
+{
+ d = new Private;
+ d->displayName = _name;
+ d->type = _type;
+ d->expanded = true;
+ d->groupId = 0;
+}
+
+Group::Group()
+ : ContactListElement( ContactList::self() )
+{
+ d = new Private;
+ d->expanded = true;
+ d->type = Normal;
+ d->groupId = 0;
+}
+
+Group::~Group()
+{
+ if(d->type == TopLevel)
+ s_topLevel=0L;
+ if(d->type == Temporary)
+ s_temporary=0L;
+ delete d;
+}
+
+QPtrList<MetaContact> Group::members() const
+{
+ QPtrList<MetaContact> members = ContactList::self()->metaContacts();
+ // members is a *copy* of the meta contacts, so using first(), next() and remove() is fine.
+ for( members.first(); members.current(); )
+ {
+ if ( members.current()->groups().contains( this ) )
+ members.next();
+ else
+ members.remove();
+ }
+ return members;
+}
+
+const QDomElement Group::toXML()
+{
+ QDomDocument group;
+ group.appendChild( group.createElement( QString::fromLatin1( "kopete-group" ) ) );
+ group.documentElement().setAttribute( QString::fromLatin1( "groupId" ), QString::number( groupId() ) );
+
+ QString type;
+ switch ( d->type )
+ {
+ case Temporary:
+ type = QString::fromLatin1( "temporary" );
+ break;
+ case TopLevel:
+ type = QString::fromLatin1( "top-level" );
+ break;
+ default:
+ type = QString::fromLatin1( "standard" ); // == Normal
+ break;
+ }
+
+ group.documentElement().setAttribute( QString::fromLatin1( "type" ), type );
+ group.documentElement().setAttribute( QString::fromLatin1( "view" ), QString::fromLatin1( d->expanded ? "expanded" : "collapsed" ) );
+
+ QDomElement displayName = group.createElement( QString::fromLatin1( "display-name" ) );
+ displayName.appendChild( group.createTextNode( d->displayName ) );
+ group.documentElement().appendChild( displayName );
+
+ // Store other plugin data
+ QValueList<QDomElement> pluginData = ContactListElement::toXML();
+ for ( QValueList<QDomElement>::Iterator it = pluginData.begin(); it != pluginData.end(); ++it )
+ group.documentElement().appendChild( group.importNode( *it, true ) );
+
+ // Store custom notification data
+ QDomElement notifyData = Kopete::NotifyDataObject::notifyDataToXML();
+ if ( notifyData.hasChildNodes() )
+ group.documentElement().appendChild( group.importNode( notifyData, true ) );
+
+ return group.documentElement();
+}
+
+bool Group::fromXML( const QDomElement &data )
+{
+ QString strGroupId = data.attribute( QString::fromLatin1( "groupId" ) );
+ if ( !strGroupId.isEmpty() )
+ {
+ d->groupId = strGroupId.toUInt();
+ if ( d->groupId > d->uniqueGroupId )
+ d->uniqueGroupId = d->groupId;
+ }
+
+ // Don't overwrite type for Temporary and TopLevel groups
+ if ( d->type != Temporary && d->type != TopLevel )
+ {
+ QString type = data.attribute( QString::fromLatin1( "type" ), QString::fromLatin1( "standard" ) );
+ if ( type == QString::fromLatin1( "temporary" ) )
+ {
+ if ( d->type != Temporary )
+ {
+ s_temporary->fromXML( data );
+ return false;
+ }
+ }
+ else if ( type == QString::fromLatin1( "top-level" ) )
+ {
+ if ( d->type != TopLevel )
+ {
+ s_topLevel->fromXML( data );
+ return false;
+ }
+ }
+ else
+ {
+ d->type = Normal;
+ }
+ }
+
+ QString view = data.attribute( QString::fromLatin1( "view" ), QString::fromLatin1( "expanded" ) );
+ d->expanded = ( view != QString::fromLatin1( "collapsed" ) );
+
+ QDomNode groupData = data.firstChild();
+ while ( !groupData.isNull() )
+ {
+ QDomElement groupElement = groupData.toElement();
+ if ( groupElement.tagName() == QString::fromLatin1( "display-name" ) )
+ {
+ // Don't set display name for temporary or top-level items
+ if ( d->type == Normal )
+ d->displayName = groupElement.text();
+ }
+ else if( groupElement.tagName() == QString::fromLatin1( "custom-notifications" ) )
+ {
+ Kopete::NotifyDataObject::notifyDataFromXML( groupElement );
+ }
+ else
+ {
+ Kopete::ContactListElement::fromXML( groupElement );
+ }
+
+ groupData = groupData.nextSibling();
+ }
+
+ // Sanity checks. We must not have groups without a displayname.
+ if ( d->displayName.isEmpty() )
+ {
+ switch ( d->type )
+ {
+ case Temporary:
+ d->displayName = QString::fromLatin1( "Temporary" );
+ break;
+ case TopLevel:
+ d->displayName = QString::fromLatin1( "Top-Level" );
+ break;
+ default:
+ d->displayName = i18n( "(Unnamed Group)" );
+ break;
+ }
+ }
+
+ //this allows to save data for the top-level group in the top-level group
+ return ( d->type == Normal );
+}
+
+void Group::setDisplayName( const QString &s )
+{
+ if ( d->displayName != s )
+ {
+ QString oldname = d->displayName;
+ d->displayName = s;
+ emit displayNameChanged( this, oldname );
+ }
+}
+
+QString Group::displayName() const
+{
+ return d->displayName;
+}
+
+Group::GroupType Group::type() const
+{
+ return d->type;
+}
+
+void Group::setType( GroupType t )
+{
+ d->type = t;
+}
+
+void Group::setExpanded( bool isExpanded )
+{
+ d->expanded = isExpanded;
+}
+
+bool Group::isExpanded() const
+{
+ return d->expanded;
+}
+
+uint Group::groupId() const
+{
+ if ( d->groupId == 0 )
+ d->groupId = ++d->uniqueGroupId;
+
+ return d->groupId;
+}
+
+
+void Group::sendMessage()
+{
+ QPtrList<Kopete::MetaContact> list = onlineMembers();
+ Kopete::MetaContact *mc = list.first();
+ Kopete::Contact *c;
+
+ if(!mc)
+ return;
+ c = mc->preferredContact();
+ c->sendMessage();
+ if( c->manager( Contact::CanCreate ) )
+ {
+ connect( c->manager(), SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession* ) ), this, SLOT( sendMessage( Kopete::Message& ) ));
+ }
+}
+
+void Group::sendMessage( Message& msg )
+{
+ QPtrList<MetaContact> list = onlineMembers();
+ Kopete::MetaContact *mc = list.first();
+ ChatSession *cs=msg.manager();
+ if( cs )
+ {
+ disconnect( cs, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession* ) ), this, SLOT( sendMessage( Kopete::Message& ) ) );
+ }
+ else
+ return;
+
+ if(!mc)
+ return;
+ list.remove( msg.to().first()->metaContact() );
+ for( mc = list.first(); mc; mc = list.next() )
+ {
+ if(mc->isReachable())
+ {
+ Contact *kcontact=mc->preferredContact();
+ if( kcontact->manager( Contact::CanCreate ) )
+ {
+ //This is hack and stupid. send message to group should never exist anyway - Olivier 2005-09-11
+ // changing the "to" is require, because jabber use it to send the messgae. Cf BUG 111514
+ Message msg2(cs->myself() , kcontact , msg.plainBody() , msg.direction() , Message::PlainText , msg.requestedPlugin() );
+ kcontact->manager( Contact::CanCreate )->sendMessage( msg2 );
+ }
+ }
+ }
+}
+
+QPtrList<MetaContact> Group::onlineMembers() const
+{
+ QPtrList<MetaContact> list = members();
+
+ for( list.first(); list.current(); )
+ if( list.current()->isReachable() && list.current()->isOnline() )
+ list.next();
+ else
+ list.remove();
+ return list;
+}
+
+} //END namespace Kopete
+
+
+#include "kopetegroup.moc"
+
+
+
diff --git a/kopete/libkopete/kopetegroup.h b/kopete/libkopete/kopetegroup.h
new file mode 100644
index 00000000..37b8572d
--- /dev/null
+++ b/kopete/libkopete/kopetegroup.h
@@ -0,0 +1,187 @@
+/*
+ kopetegroup.h - Kopete (Meta)Contact Group
+
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEGROUP_H
+#define KOPETEGROUP_H
+
+#include "kopetenotifydataobject.h"
+#include "kopetecontactlistelement.h"
+
+#include "kopete_export.h"
+
+#include <qptrlist.h>
+
+class QDomElement;
+
+
+namespace Kopete {
+
+
+class MetaContact;
+class Message;
+
+/**
+ * Class which represents the Group.
+ *
+ * A Group is a ConstacListElement which means plugin can save datas.
+ *
+ * some static group are availavle from this class: topLevel and temporary
+ *
+ * @author Olivier Goffart
+ */
+class KOPETE_EXPORT Group : public ContactListElement, public NotifyDataObject
+{
+ Q_PROPERTY( QString displayName READ displayName WRITE setDisplayName )
+ Q_PROPERTY( uint groupId READ groupId )
+ Q_PROPERTY( bool expanded READ isExpanded WRITE setExpanded )
+
+ Q_OBJECT
+
+public:
+ /** Kinds of groups. */
+ enum GroupType { Normal=0, Temporary, TopLevel };
+
+ /**
+ * \brief Create an empty group
+ *
+ * Note that the constructor will not add the group automatically to the contact list.
+ * Use @ref ContactList::addGroup() to add it
+ */
+ Group();
+
+ /**
+ * \brief Create a group of the specified type
+ *
+ * Overloaded constructor to create a group with a display name of the specified type.
+ */
+ Group( const QString &displayName, GroupType type = Normal );
+
+ ~Group();
+
+ /**
+ * \brief Return the group's display name
+ *
+ * \return the display name of the group
+ */
+ QString displayName() const;
+
+ /**
+ * \brief Rename the group
+ */
+ void setDisplayName( const QString &newName );
+
+ /**
+ * \return the group type
+ */
+ GroupType type() const;
+
+ /**
+ * \brief Set the group type
+ */
+ void setType( GroupType newType );
+
+ /**
+ * \return the unique id for this group
+ */
+ uint groupId() const;
+
+ /**
+ * @brief child metacontact
+ * This function is not very efficient - it searches through all the metacontacts in the contact list
+ * \return the members of this group
+ */
+ QPtrList<MetaContact> members() const;
+
+ /**
+ * \brief Set if the group is expanded.
+ *
+ * This is saved to the xml contactlist file
+ */
+ void setExpanded( bool expanded );
+
+ /**
+ *
+ * \return true if the group is expanded.
+ * \return false otherwise
+ */
+ bool isExpanded() const;
+
+ /**
+ * \return a Group pointer to the toplevel group
+ */
+ static Group *topLevel();
+
+ /**
+ * \return a Group pointer to the temporary group
+ */
+ static Group *temporary();
+
+
+
+ /**
+ * @internal
+ * Outputs the group data in XML
+ */
+ const QDomElement toXML();
+
+
+ /**
+ * @internal
+ * Loads the group data from XML
+ */
+ bool fromXML( const QDomElement &data );
+
+
+
+
+public slots:
+ /**
+ * Send a message to all contacts in the group
+ */
+ void sendMessage();
+
+
+signals:
+ /**
+ * \brief Emitted when the group has been renamed
+ */
+ void displayNameChanged( Kopete::Group *group , const QString &oldName );
+
+
+private slots:
+ void sendMessage( Kopete::Message& );
+
+private:
+ static Group *s_topLevel;
+ static Group *s_temporary;
+
+ class Private;
+ Private *d;
+
+
+ /**
+ * @internal used to get reachabe contact to send message to thom.
+ */
+ QPtrList<MetaContact> onlineMembers() const;
+};
+
+} //END namespace Kopete
+
+#endif
+
+
diff --git a/kopete/libkopete/kopetemessage.cpp b/kopete/libkopete/kopetemessage.cpp
new file mode 100644
index 00000000..54761799
--- /dev/null
+++ b/kopete/libkopete/kopetemessage.cpp
@@ -0,0 +1,641 @@
+/*
+ kopetemessage.cpp - Base class for Kopete messages
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2006 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <stdlib.h>
+
+#include <qcolor.h>
+#include <qbuffer.h>
+#include <qimage.h>
+#include <qstylesheet.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstringhandler.h>
+#include <kmdcodec.h>
+#include <qguardedptr.h>
+
+#include "kopetemessage.h"
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopetechatsession.h"
+#include "kopeteprefs.h"
+#include "kopetecontact.h"
+#include "kopeteemoticons.h"
+
+
+using namespace Kopete;
+
+class Message::Private
+ : public KShared
+{
+public:
+ Private( const QDateTime &timeStamp, const Contact *from, const ContactPtrList &to,
+ const QString &subject, MessageDirection direction,
+ const QString &requestedPlugin, MessageType type );
+
+ QGuardedPtr<const Contact> from;
+ ContactPtrList to;
+ ChatSession *manager;
+
+ MessageDirection direction;
+ MessageFormat format;
+ MessageType type;
+ QString requestedPlugin;
+ MessageImportance importance;
+ bool bgOverride;
+ bool fgOverride;
+ bool rtfOverride;
+ bool isRightToLeft;
+ QDateTime timeStamp;
+ QFont font;
+
+ QColor fgColor;
+ QColor bgColor;
+ QString body;
+ QString subject;
+};
+
+Message::Private::Private( const QDateTime &timeStamp, const Contact *from,
+ const ContactPtrList &to, const QString &subject,
+ MessageDirection direction, const QString &requestedPlugin, MessageType type )
+: from( from ), to( to ), manager( 0 ), direction( direction ), format( PlainText ), type( type ),
+ requestedPlugin( requestedPlugin ), importance( (to.count() <= 1) ? Normal : Low ),
+ bgOverride( false ), fgOverride( false ), rtfOverride( false ), isRightToLeft( false ),
+ timeStamp( timeStamp ), body( QString::null ), subject( subject )
+{
+}
+
+Message::Message()
+: d( new Private( QDateTime::currentDateTime(), 0L, QPtrList<Contact>(), QString::null, Internal,
+ QString::null, TypeNormal ) )
+{
+}
+
+Message::Message( const Contact *fromKC, const QPtrList<Contact> &toKC, const QString &body,
+ MessageDirection direction, MessageFormat f, const QString &requestedPlugin, MessageType type )
+: d( new Private( QDateTime::currentDateTime(), fromKC, toKC, QString::null, direction, requestedPlugin, type ) )
+{
+ doSetBody( body, f );
+}
+
+Message::Message( const Contact *fromKC, const Contact *toKC, const QString &body,
+ MessageDirection direction, MessageFormat f, const QString &requestedPlugin, MessageType type )
+{
+ QPtrList<Contact> to;
+ to.append(toKC);
+ d = new Private( QDateTime::currentDateTime(), fromKC, to, QString::null, direction, requestedPlugin, type );
+ doSetBody( body, f );
+}
+
+Message::Message( const Contact *fromKC, const QPtrList<Contact> &toKC, const QString &body,
+ const QString &subject, MessageDirection direction, MessageFormat f, const QString &requestedPlugin, MessageType type )
+ : d( new Private( QDateTime::currentDateTime(), fromKC, toKC, subject, direction, requestedPlugin, type ) )
+{
+ doSetBody( body, f );
+}
+
+Message::Message( const QDateTime &timeStamp, const Contact *fromKC, const QPtrList<Contact> &toKC,
+ const QString &body, MessageDirection direction, MessageFormat f, const QString &requestedPlugin, MessageType type )
+ : d( new Private( timeStamp, fromKC, toKC, QString::null, direction, requestedPlugin, type ) )
+{
+ doSetBody( body, f );
+}
+
+
+Message::Message( const QDateTime &timeStamp, const Contact *fromKC, const QPtrList<Contact> &toKC,
+ const QString &body, const QString &subject, MessageDirection direction, MessageFormat f, const QString &requestedPlugin, MessageType type )
+ : d( new Private( timeStamp, fromKC, toKC, subject, direction, requestedPlugin, type ) )
+{
+ doSetBody( body, f );
+}
+
+Kopete::Message::Message( const Message &other )
+ : d(other.d)
+{
+}
+
+
+
+Message& Message::operator=( const Message &other )
+{
+ d = other.d;
+ return *this;
+}
+
+Message::~Message()
+{
+}
+
+void Message::detach()
+{
+ // there is no detach in KSharedPtr :(
+ if( d.count() == 1 )
+ return;
+
+ // Warning: this only works as long as the private object doesn't contain pointers to allocated objects.
+ // The from contact for example is fine, but it's a shallow copy this way.
+ d = new Private(*d);
+}
+
+void Message::setBgOverride( bool enabled )
+{
+ detach();
+ d->bgOverride = enabled;
+}
+
+void Message::setFgOverride( bool enabled )
+{
+ detach();
+ d->fgOverride = enabled;
+}
+
+void Message::setRtfOverride( bool enabled )
+{
+ detach();
+ d->rtfOverride = enabled;
+}
+
+void Message::setFg( const QColor &color )
+{
+ detach();
+ d->fgColor=color;
+}
+
+void Message::setBg( const QColor &color )
+{
+ detach();
+ d->bgColor=color;
+}
+
+void Message::setFont( const QFont &font )
+{
+ detach();
+ d->font = font;
+}
+
+void Message::doSetBody( const QString &_body, Message::MessageFormat f )
+{
+ QString body = _body;
+
+ //TODO: move that in ChatTextEditPart::contents
+ if( f == RichText )
+ {
+ //This is coming from the RichTextEditor component.
+ //Strip off the containing HTML document
+ body.replace( QRegExp( QString::fromLatin1(".*<body[^>]*>(.*)</body>.*") ), QString::fromLatin1("\\1") );
+
+ //Strip <p> tags
+ body.replace( QString::fromLatin1("<p>"), QString::null );
+
+ //Replace </p> with a <br/>
+ body.replace( QString::fromLatin1("</p>"), QString::fromLatin1("<br/>") );
+
+ //Remove trailing </br>
+ if ( body.endsWith( QString::fromLatin1("<br/>") ) )
+ body.truncate( body.length() - 5 );
+
+ body.remove( QString::fromLatin1("\n") );
+ body.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( " &nbsp;" ) );
+ }
+ /*
+ else if( f == ParsedHTML )
+ {
+ kdWarning( 14000 ) << k_funcinfo << "using ParsedHTML which is internal! Message: '" <<
+ body << "', Backtrace: " << kdBacktrace() << endl;
+ }
+ */
+
+ d->body = body;
+ d->format = f;
+
+ // unescaping is very expensive, do it only once and cache the result
+ d->isRightToLeft = ( f & RichText ? unescape( d->body ).isRightToLeft() : d->body.isRightToLeft() );
+}
+
+void Message::setBody( const QString &body, MessageFormat f )
+{
+ detach();
+
+ doSetBody( body, f );
+}
+
+bool Message::isRightToLeft() const
+{
+ return d->isRightToLeft;
+}
+
+void Message::setImportance(Message::MessageImportance i)
+{
+ detach();
+ d->importance = i;
+}
+
+QString Message::unescape( const QString &xml )
+{
+ QString data = xml;
+
+ // Remove linebreak and multiple spaces. First return nbsp's to normal spaces :)
+ data.simplifyWhiteSpace();
+
+ int pos;
+ while ( ( pos = data.find( '<' ) ) != -1 )
+ {
+ int endPos = data.find( '>', pos + 1 );
+ if( endPos == -1 )
+ break; // No more complete elements left
+
+ // Take the part between < and >, and extract the element name from that
+ int matchWidth = endPos - pos + 1;
+ QString match = data.mid( pos + 1, matchWidth - 2 ).simplifyWhiteSpace();
+ int elemEndPos = match.find( ' ' );
+ QString elem = ( elemEndPos == -1 ? match.lower() : match.left( elemEndPos ).lower() );
+ if ( elem == QString::fromLatin1( "img" ) )
+ {
+ // Replace smileys with their original text'
+ const QString attrTitle = QString::fromLatin1( "title=\"" );
+ int titlePos = match.find( attrTitle, elemEndPos );
+ int titleEndPos = match.find( '"', titlePos + attrTitle.length() );
+ if( titlePos == -1 || titleEndPos == -1 )
+ {
+ // Not a smiley but a normal <img>
+ // Don't update pos, we restart at this position :)
+ data.remove( pos, matchWidth );
+ }
+ else
+ {
+ QString orig = match.mid( titlePos + attrTitle.length(),
+ titleEndPos - titlePos - attrTitle.length() );
+ data.replace( pos, matchWidth, orig );
+ pos += orig.length();
+ }
+ }
+ else if ( elem == QString::fromLatin1( "/p" ) || elem == QString::fromLatin1( "/div" ) ||
+ elem == QString::fromLatin1( "br" ) )
+ {
+ // Replace paragraph, div and line breaks with a newline
+ data.replace( pos, matchWidth, '\n' );
+ pos++;
+ }
+ else
+ {
+ // Remove all other elements entirely
+ // Don't update pos, we restart at this position :)
+ data.remove( pos, matchWidth );
+ }
+ }
+
+ // Replace stuff starting with '&'
+ data.replace( QString::fromLatin1( "&gt;" ), QString::fromLatin1( ">" ) );
+ data.replace( QString::fromLatin1( "&lt;" ), QString::fromLatin1( "<" ) );
+ data.replace( QString::fromLatin1( "&quot;" ), QString::fromLatin1( "\"" ) );
+ data.replace( QString::fromLatin1( "&nbsp;" ), QString::fromLatin1( " " ) );
+ data.replace( QString::fromLatin1( "&amp;" ), QString::fromLatin1( "&" ) );
+ data.replace( QString::fromLatin1( "&#160;" ), QString::fromLatin1( " " ) ); //this one is used in jabber: note, we should escape all &#xx;
+
+ return data;
+}
+
+QString Message::escape( const QString &text )
+{
+ QString html = QStyleSheet::escape( text );
+ //Replace carriage returns inside the text
+ html.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "<br />" ) );
+ //Replace a tab with 4 spaces
+ html.replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "&nbsp;&nbsp;&nbsp;&nbsp;" ) );
+
+ //Replace multiple spaces with &nbsp;
+ //do not replace every space so we break the linebreak
+ html.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "&nbsp; " ) );
+
+ return html;
+}
+
+
+
+QString Message::plainBody() const
+{
+ QString body=d->body;
+ if( d->format & RichText )
+ {
+ body = unescape( body );
+ }
+ return body;
+}
+
+QString Message::escapedBody() const
+{
+ QString escapedBody=d->body;
+// kdDebug(14000) << k_funcinfo << escapedBody << " " << d->rtfOverride << endl;
+
+ if( d->format & PlainText )
+ {
+ escapedBody=escape( escapedBody );
+ }
+ else if( d->format & RichText && d->rtfOverride)
+ {
+ //remove the rich text
+ escapedBody = escape (unescape( escapedBody ) );
+ }
+
+ return escapedBody;
+}
+
+QString Message::parsedBody() const
+{
+ //kdDebug(14000) << k_funcinfo << "messageformat: " << d->format << endl;
+
+ if( d->format == ParsedHTML )
+ {
+ return d->body;
+ }
+ else
+ {
+ return Kopete::Emoticons::parseEmoticons(parseLinks(escapedBody(), RichText));
+ }
+}
+
+static QString makeRegExp( const char *pattern )
+{
+ const QString urlChar = QString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
+ const QString boundaryStart = QString::fromLatin1( "(^|[^%1])(" ).arg( urlChar );
+ const QString boundaryEnd = QString::fromLatin1( ")([^%1]|$)" ).arg( urlChar );
+
+ return boundaryStart + QString::fromLatin1(pattern) + boundaryEnd;
+}
+
+QString Message::parseLinks( const QString &message, MessageFormat format )
+{
+ if ( format == ParsedHTML )
+ return message;
+
+ if ( format & RichText )
+ {
+ // < in HTML *always* means start-of-tag
+ QStringList entries = QStringList::split( QChar('<'), message, true );
+
+ QStringList::Iterator it = entries.begin();
+
+ // first one is different: it doesn't start with an HTML tag.
+ if ( it != entries.end() )
+ {
+ *it = parseLinks( *it, PlainText );
+ ++it;
+ }
+
+ for ( ; it != entries.end(); ++it )
+ {
+ QString curr = *it;
+ // > in HTML means start-of-tag if and only if it's the first one after a <
+ int tagclose = curr.find( QChar('>') );
+ // no >: the HTML is broken, but we can cope
+ if ( tagclose == -1 )
+ continue;
+ QString tag = curr.left( tagclose + 1 );
+ QString body = curr.mid( tagclose + 1 );
+ *it = tag + parseLinks( body, PlainText );
+ }
+ return entries.join(QString::fromLatin1("<"));
+ }
+
+ QString result = message;
+
+ // common subpatterns - may not contain matching parens!
+ const QString name = QString::fromLatin1( "[\\w\\+\\-=_\\.]+" );
+ const QString userAndPassword = QString::fromLatin1( "(?:%1(?::%1)?\\@)" ).arg( name );
+ const QString urlChar = QString::fromLatin1( "\\+\\-\\w\\./#@&;:=\\?~%_,\\!\\$\\*\\(\\)" );
+ const QString urlSection = QString::fromLatin1( "[%1]+" ).arg( urlChar );
+ const QString domain = QString::fromLatin1( "[\\-\\w_]+(?:\\.[\\-\\w_]+)+" );
+
+ //Replace http/https/ftp links:
+ // Replace (stuff)://[user:password@](linkstuff) with a link
+ result.replace(
+ QRegExp( makeRegExp("\\w+://%1?\\w%2").arg( userAndPassword, urlSection ) ),
+ QString::fromLatin1("\\1<a href=\"\\2\" title=\"\\2\">\\2</a>\\3" ) );
+
+ // Replace www.X.Y(linkstuff) with a http: link
+ result.replace(
+ QRegExp( makeRegExp("%1?www\\.%2%3").arg( userAndPassword, domain, urlSection ) ),
+ QString::fromLatin1("\\1<a href=\"http://\\2\" title=\"http://\\2\">\\2</a>\\3" ) );
+
+ //Replace Email Links
+ // Replace user@domain with a mailto: link
+ result.replace(
+ QRegExp( makeRegExp("%1@%2").arg( name, domain ) ),
+ QString::fromLatin1("\\1<a href=\"mailto:\\2\" title=\"mailto:\\2\">\\2</a>\\3") );
+
+ //Workaround for Bug 85061: Highlighted URLs adds a ' ' after the URL itself
+ // the trailing &nbsp; is included in the url.
+ result.replace( QRegExp( QString::fromLatin1("(<a href=\"[^\"]+)(&nbsp;)(\")") ) , QString::fromLatin1("\\1\\3") );
+
+ return result;
+}
+
+
+
+QDateTime Message::timestamp() const
+{
+ return d->timeStamp;
+}
+
+const Contact *Message::from() const
+{
+ return d->from;
+}
+
+QPtrList<Contact> Message::to() const
+{
+ return d->to;
+}
+
+Message::MessageType Message::type() const
+{
+ return d->type;
+}
+
+QString Message::requestedPlugin() const
+{
+ return d->requestedPlugin;
+}
+
+QColor Message::fg() const
+{
+ return d->fgColor;
+}
+
+QColor Message::bg() const
+{
+ return d->bgColor;
+}
+
+QFont Message::font() const
+{
+ //QDomElement bodyNode = d->xmlDoc.elementsByTagName( QString::fromLatin1("body") ).item(0).toElement();
+ //return QFont( bodyNode.attribute( QString::fromLatin1("font") ), bodyNode.attribute( QString::fromLatin1("fontsize") ).toInt() );
+ return d->font;
+}
+
+QString Message::subject() const
+{
+ return d->subject;
+}
+
+Message::MessageFormat Message::format() const
+{
+ return d->format;
+}
+
+Message::MessageDirection Message::direction() const
+{
+ return d->direction;
+}
+
+Message::MessageImportance Message::importance() const
+{
+ return d->importance;
+}
+
+ChatSession *Message::manager() const
+{
+ return d->manager;
+}
+
+void Message::setManager(ChatSession *kmm)
+{
+ detach();
+ d->manager=kmm;
+}
+
+QString Message::getHtmlStyleAttribute() const
+{
+ QString styleAttribute;
+
+ styleAttribute = QString::fromUtf8("style=\"");
+
+ // Affect foreground(color) and background color to message.
+ if( !d->fgOverride && d->fgColor.isValid() )
+ {
+ styleAttribute += QString::fromUtf8("color: %1; ").arg(d->fgColor.name());
+ }
+ if( !d->bgOverride && d->bgColor.isValid() )
+ {
+ styleAttribute += QString::fromUtf8("background-color: %1; ").arg(d->bgColor.name());
+ }
+
+ // Affect font parameters.
+ if( !d->rtfOverride && d->font!=QFont() )
+ {
+ QString fontstr;
+ if(!d->font.family().isNull())
+ fontstr+=QString::fromLatin1("font-family: ")+d->font.family()+QString::fromLatin1("; ");
+ if(d->font.italic())
+ fontstr+=QString::fromLatin1("font-style: italic; ");
+ if(d->font.strikeOut())
+ fontstr+=QString::fromLatin1("text-decoration: line-through; ");
+ if(d->font.underline())
+ fontstr+=QString::fromLatin1("text-decoration: underline; ");
+ if(d->font.bold())
+ fontstr+=QString::fromLatin1("font-weight: bold;");
+
+ styleAttribute += fontstr;
+ }
+
+ styleAttribute += QString::fromUtf8("\"");
+
+ return styleAttribute;
+}
+
+// KDE4: Move that to a utils class/namespace
+QString Message::decodeString( const QCString &message, const QTextCodec *providedCodec, bool *success )
+{
+ /*
+ Note to everyone. This function is not the most efficient, that is for sure.
+ However, it *is* the only way we can be guarenteed that a given string is
+ decoded properly.
+ */
+
+ if( success )
+ *success = true;
+
+ // Avoid heavy codec tests on empty message.
+ if( message.isEmpty() )
+ return QString::fromAscii( message );
+
+ //Check first 128 chars
+ int charsToCheck = message.length();
+ charsToCheck = 128 > charsToCheck ? charsToCheck : 128;
+
+ //They are providing a possible codec. Check if it is valid
+ if( providedCodec && providedCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
+ {
+ //All chars decodable.
+ return providedCodec->toUnicode( message );
+ }
+
+ //Check if it is UTF
+ if( KStringHandler::isUtf8(message) )
+ {
+ //We have a UTF string almost for sure. At least we know it will be decoded.
+ return QString::fromUtf8( message );
+ }
+
+ //Try codecForContent - exact match
+ QTextCodec *testCodec = QTextCodec::codecForContent(message, charsToCheck);
+ if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
+ {
+ //All chars decodable.
+ return testCodec->toUnicode( message );
+ }
+
+ kdWarning(14000) << k_funcinfo << "Unable to decode string using provided codec(s), taking best guesses!" << endl;
+ if( success )
+ *success = false;
+
+ //We don't have any clues here.
+
+ //Try local codec
+ testCodec = QTextCodec::codecForLocale();
+ if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
+ {
+ //All chars decodable.
+ kdDebug(14000) << k_funcinfo << "Using locale's codec" << endl;
+ return testCodec->toUnicode( message );
+ }
+
+ //Try latin1 codec
+ testCodec = QTextCodec::codecForMib(4);
+ if( testCodec && testCodec->heuristicContentMatch( message, charsToCheck ) >= 0 )
+ {
+ //All chars decodable.
+ kdDebug(14000) << k_funcinfo << "Using latin1" << endl;
+ return testCodec->toUnicode( message );
+ }
+
+ kdDebug(14000) << k_funcinfo << "Using latin1 and cleaning string" << endl;
+ //No codec decoded. Just decode latin1, and clean out any junk.
+ QString result = QString::fromLatin1( message );
+ const uint length = message.length();
+ for( uint i = 0; i < length; ++i )
+ {
+ if( !result[i].isPrint() )
+ result[i] = '?';
+ }
+
+ return result;
+}
diff --git a/kopete/libkopete/kopetemessage.h b/kopete/libkopete/kopetemessage.h
new file mode 100644
index 00000000..0737d2ae
--- /dev/null
+++ b/kopete/libkopete/kopetemessage.h
@@ -0,0 +1,424 @@
+/*
+ kopetemessage.h - Base class for Kopete messages
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __KOPETE_MESSAGE_H__
+#define __KOPETE_MESSAGE_H__
+
+#include "kopetecontact.h"
+
+#include <ksharedptr.h>
+
+#include <qptrlist.h>
+#include <qstring.h>
+#include <qdom.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qdatetime.h>
+#include <qvaluelist.h>
+
+#include "kopete_export.h"
+
+
+class QDateTime;
+
+namespace Kopete {
+
+
+ class ChatSession;
+class Contact;
+
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ *
+ * Message represents any kind of messages shown on a chat view.
+ *
+ * The message may be a simple plaintext string, or a Richtext HTML like message,
+ * this is indicated by the @ref format() flag.
+ * PlainText message can however have a color, or specific fonts with the flag
+ * @ref bg(), @ref fg(), @ref font()
+ * It is recommended to use these flags, even for RichText messages, so the user can disable
+ * custom colors in the chat window style.
+ */
+class KOPETE_EXPORT Message
+{
+public:
+ /**
+ * Direction of a message.
+ * - Inbound: Message is from the chat partner
+ * - Outbound: Message sent by the user.
+ * - Internal: Messages which are not sent via the network. This is just a notification a plugin can show in a chat view
+ * - Action: For the /me command , like on irc
+ */
+ enum MessageDirection { Inbound = 0, Outbound = 1, Internal= 2 };
+
+ /**
+ * Format of body
+ * - PlainText: Just a simple text, without any formatting. If it contains HTML tags then they will be simply shown in the chatview.
+ * - RichText: Text already HTML escaped and which can contains some tags. the string
+ * should be a valid (X)HTML string.
+ * Any HTML specific characters (\<, \>, \&, ...) are escaped to the equivalent HTML
+ * entity (\&gt;, \&lt;, ...) newlines are \<br /\> and any other HTML tags will be interpreted.
+ * - ParsedHTML: only used by the chatview, this text is parsed and ready to
+ * show into the chatview, with Emoticons, and URLs
+ * - Crypted is used only by Jabber and the Cryptography plugin
+ */
+ enum MessageFormat{ PlainText = 0x01 , RichText =0x02 , ParsedHTML = 0x04|RichText , Crypted = 0x08|PlainText};
+
+ /**
+ * Specifies the type of the message.
+ * Currently supported types are:
+ * - Normal: a message
+ * - Action: an IRC-style DESCRIBE action.
+ */
+ enum MessageType { TypeNormal, TypeAction };
+
+ /**
+ * Specifies the type of notification that will be sent with this message
+ * - Low: almost no notifications. automatically used in groupChat
+ * - Normal: Default notification, for normal message
+ * - Highlight: Highlight notification, for most important messages, which require particular attentions.
+ */
+ enum MessageImportance { Low = 0, Normal = 1, Highlight = 2 };
+
+ /**
+ * Constructs a new empty message
+ */
+ Message();
+
+ /**
+ * Deref and clean private object if refcount == 0
+ */
+ ~Message();
+
+ /**
+ * Constructs a new message. See @ref setBody() to more information about the format
+ * @param fromKC The Contact that the message is coming from
+ * @param toKC List of Contacts the message is going to
+ * @param body Message body
+ * @param direction The direction of the message, Message::Inbound, Message::Outbound, Message::Internal
+ * @param format Format of the message
+ * @param requestedPlugin Requested view plugin for the message
+ * @param type Type of the message, see @ref MessageType
+ */
+ Message( const Contact *fromKC, const QPtrList<Contact> &toKC, const QString &body,
+ MessageDirection direction, MessageFormat format = PlainText,
+ const QString &requestedPlugin = QString::null, MessageType type = TypeNormal );
+
+ /**
+ * Constructs a new message. See @ref setBody() to more information about the format
+ * @param fromKC The Contact that the message is coming from
+ * @param toKC List of Contacts the message is going to
+ * @param body Message body
+ * @param direction The direction of the message, Message::Inbound, Message::Outbound, Message::Internal
+ * @param format Format of the message
+ * @param requestedPlugin Requested view plugin for the message
+ * @param type Type of the message, see @ref MessageType
+ */
+ Message( const Contact *fromKC, const Contact *toKC, const QString &body,
+ MessageDirection direction, MessageFormat format = PlainText,
+ const QString &requestedPlugin = QString::null, MessageType type = TypeNormal );
+
+ /**
+ * Constructs a new message. See @ref setBody() to more information about the format
+ * @param fromKC The Contact that the message is coming from
+ * @param toKC List of Contacts the message is going to
+ * @param body Message body
+ * @param subject The subject of the message
+ * @param direction The direction of the message, Message::Inbound, Message::Outbound, Message::Internal
+ * @param format Format of the message
+ * @param requestedPlugin Requested view plugin for the message
+ * @param type Type of the message, see @ref MessageType
+ */
+ Message( const Contact *fromKC, const QPtrList<Contact> &toKC, const QString &body,
+ const QString &subject, MessageDirection direction, MessageFormat format = PlainText,
+ const QString &requestedPlugin = QString::null, MessageType type = TypeNormal );
+
+ /**
+ * Constructs a new message. See @ref setBody() to more information about the format
+ * @param timeStamp Timestamp for the message
+ * @param fromKC The Contact that the message is coming from
+ * @param toKC List of Contacts the message is going to
+ * @param body Message body
+ * @param direction The direction of the message, Message::Inbound, Message::Outbound, Message::Internal
+ * @param format Format of the message
+ * @param requestedPlugin Requested view plugin for the message
+ * @param type Type of the message, see @ref MessageType
+ */
+ Message( const QDateTime &timeStamp, const Contact *fromKC, const QPtrList<Contact> &toKC,
+ const QString &body, MessageDirection direction, MessageFormat format = PlainText,
+ const QString &requestedPlugin = QString::null, MessageType type = TypeNormal );
+
+ /**
+ * Constructs a new message. See @ref setBody() to more information about the format
+ * @param timeStamp Timestamp for the message
+ * @param fromKC The Contact that the message is coming from
+ * @param toKC List of Contacts the message is going to
+ * @param body Message body
+ * @param subject The subject of the message
+ * @param direction The direction of the message, Message::Inbound, Message::Outbound, Message::Internal
+ * @param format Format of the message
+ * @param requestedPlugin Requested view plugin for the message
+ * @param type Type of the message, see @ref MessageType
+ */
+ Message( const QDateTime &timeStamp, const Contact *fromKC, const QPtrList<Contact> &toKC,
+ const QString &body, const QString &subject, MessageDirection direction,
+ MessageFormat format = PlainText, const QString &requestedPlugin = QString::null,
+ MessageType type = TypeNormal );
+
+ /**
+ * Copy constructor.
+ * Just adds a reference, doesn't actually copy.
+ */
+ Message( const Message &other );
+
+ /**
+ * Assignment operator
+ * Just like the copy constructor it just refs and doesn't copy.
+ */
+ Message & operator=( const Message &other );
+
+ /**
+ * Accessor method for the timestamp of the message
+ * @return The message's timestamp
+ */
+ QDateTime timestamp() const;
+
+ /**
+ * Accessor method for the Contact that sent this message
+ * @return The Contact who sent this message
+ */
+ const Contact * from() const;
+
+ /**
+ * Accessor method for the Contacts that this message was sent to
+ * @return Pointer list of the Contacts this message was sent to
+ */
+ QPtrList<Contact> to() const;
+
+ /**
+ * @return the @ref MessageType of this message
+ */
+ MessageType type() const;
+
+ /**
+ * @return the view plugin you would prefer to use to read this message. If
+ * null, Kopete will use the user's preferred plugin.
+ */
+ QString requestedPlugin() const;
+
+ /**
+ * Accessor method for the foreground color
+ * @return The message's foreground color
+ */
+ QColor fg() const;
+
+ /**
+ * Accessor method for the background color of the message
+ * @return The message's background color
+ */
+ QColor bg() const;
+
+ /**
+ * Accessor method for the font of the message
+ * @return The message's font
+ */
+ QFont font() const;
+
+ /**
+ * Accessor method for the subject of the message
+ * @return The message subject
+ */
+ QString subject() const;
+
+ /**
+ * Accessor method for the format of the message
+ * @return The message format
+ */
+ MessageFormat format() const;
+
+ /**
+ * Accessor method for the direction of the message
+ * @return The message direction
+ */
+ MessageDirection direction() const;
+
+ /**
+ * @brief Accessor method for the importance
+ * @return The message importance (low/normal/highlight)
+ */
+ MessageImportance importance() const;
+
+ /**
+ * @brief Set the importance.
+ * @see importance
+ * @param importance The message importance to set
+ */
+ void setImportance(MessageImportance importance);
+
+ /**
+ * Sets the foreground color for the message
+ * @param color The color
+ */
+ void setFg( const QColor &color );
+
+ /**
+ * Sets the background color for the message
+ * @param color The color
+ */
+ void setBg( const QColor &color );
+
+ /**
+ * Sets the font for the message
+ * @param font The font
+ */
+ void setFont( const QFont &font );
+
+ /**
+ * @brief Sets the body of the message
+ *
+ * @param body The body
+ * @param format The format of the message, @see MessageFormat
+ */
+ void setBody( const QString &body, MessageFormat format = PlainText );
+
+ /**
+ * Get the message body back as plain text
+ * @return The message body as plain text
+ */
+ QString plainBody() const;
+
+ /**
+ * Get the message body as escaped (X)HTML format.
+ * That means every HTML special char (\>, \<, \&, ...) is escaped to the HTML entity (\&lt;, \&gt;, ...)
+ * and newlines (\\n) are converted to \<br /\>
+ * @return The message body as escaped text
+ */
+ QString escapedBody() const;
+
+ /**
+ * Get the message body as parsed HTML with Emoticons, and URL parsed
+ * this should be ready to be shown in the chatwindow.
+ * @return The HTML and Emoticon parsed message body
+ */
+ QString parsedBody() const;
+
+ /**
+ * Get the related message manager.
+ * If it is not set, returns 0L.
+ *
+ * The @ref ChatSession is only set if the message is already passed by the manager.
+ * We should trust this only in aboutToSend/aboutToReceive signals
+ */
+ ChatSession *manager() const ;
+
+ /**
+ * set the messagemanager for this message.
+ * should be only used by the manager itself
+ */
+ void setManager(ChatSession *);
+
+ /**
+ * Enables the use of a background for a message
+ * @param enable A flag to indicate if the background should be enabled or disabled.
+ */
+ void setBgOverride( bool enable );
+
+ /**
+ * Enables the use of a foreground for a message
+ * @param enable A flag to indicate if the foreground should be enabled or disabled.
+ */
+ void setFgOverride( bool enable );
+
+ /**
+ * Enables the use of a RTF formatting for a message
+ * @param enable A flag to indicate if the RTF formatting should be enabled or disabled.
+ */
+ void setRtfOverride( bool enable );
+
+ /**
+ * Return HTML style attribute for this message.
+ * @return A string formatted like this: "style=attr"
+ */
+ QString getHtmlStyleAttribute() const;
+
+public: /* static helpers */
+
+ /**
+ * Unescapes a string, removing XML entity references and returns a plain text.
+ *
+ * Note that this method is *VERY* expensive when called on rich text bodies,
+ * use with care!
+ *
+ * @param xml The string you want to unescape
+ */
+ static QString unescape( const QString &xml );
+
+ /**
+ * Indicate whether the string is right-to-left (Arabic or Hebrew are bidi locales)
+ * or "normal" left-to-right. Calculating RTL on rich text is expensive, and
+ * isRightToLeft() therefore uses a cached value.
+ */
+ bool isRightToLeft() const;
+
+ /**
+ * @brief Transform a pleintext message to an html.
+ * it escape main entity like &gt; &lt; add some &lt;br /&gt; or &amp;nbsp;
+ */
+ static QString escape( const QString & );
+
+
+ /**
+ * Helper function to decode a string. Whatever returned here is *nearly guarenteed* to
+ * be parseable by the XML engine.
+ *
+ * @param message The string you are trying to decode
+ * @param providedCodec A codec you want to try to decode with
+ * @param success Optional pointer to a bool you want updated on success. "Success"
+ * is defined as a successfull decoding using either UTF8 or the codec you
+ * provided. If a guess has to be taken, success will be false.
+ */
+ static QString decodeString( const QCString &message,
+ const QTextCodec *providedCodec = 0L, bool *success = 0L );
+
+private:
+ /**
+ * Message is implicitly shared.
+ * Detach the instance when modifying data.
+ */
+ void detach();
+
+ /**
+ * Called internally by @ref setBody() and the constructor
+ * Basically @ref setBody() without detach
+ */
+ void doSetBody( const QString &body, MessageFormat format = PlainText );
+
+ class Private;
+ KSharedPtr<Private> d;
+
+ static QString parseLinks( const QString &message, MessageFormat format );
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetemessageevent.cpp b/kopete/libkopete/kopetemessageevent.cpp
new file mode 100644
index 00000000..fb129837
--- /dev/null
+++ b/kopete/libkopete/kopetemessageevent.cpp
@@ -0,0 +1,105 @@
+/*
+ kopetemessageevent.cpp - Kopete Message Event
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004 by Richard Smith <richard@metafoo.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include "kopetemessageevent.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetecontact.h"
+#include "kopeteprefs.h"
+
+namespace Kopete
+{
+
+class MessageEvent::Private
+{
+public:
+ Kopete::Message message;
+ EventState state;
+};
+
+MessageEvent::MessageEvent( const Message& m, QObject *parent, const char *name )
+ : QObject(parent,name), d( new Private )
+{
+ d->message = m;
+ d->state = Nothing;
+ const Contact *c=m.from();
+ if(c)
+ connect(c,SIGNAL(contactDestroyed( Kopete::Contact* )),this,SLOT(discard()));
+}
+
+MessageEvent::~MessageEvent()
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ emit done(this);
+ delete d;
+}
+
+Kopete::Message MessageEvent::message()
+{
+ return d->message;
+}
+
+void MessageEvent::setMessage( const Kopete::Message &message )
+{
+ d->message = message;
+}
+
+MessageEvent::EventState MessageEvent::state()
+{
+ return d->state;
+}
+
+void MessageEvent::apply()
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ d->state = Applied;
+ deleteLater();
+}
+
+void MessageEvent::ignore()
+{
+ // FIXME: this should be done by the contact list for itself.
+ if( d->message.from()->metaContact() && d->message.from()->metaContact()->isTemporary() &&
+ KopetePrefs::prefs()->balloonNotifyIgnoreClosesChatView() )
+ ContactList::self()->removeMetaContact( d->message.from()->metaContact() );
+ d->state = Ignored;
+ deleteLater();
+}
+
+void MessageEvent::accept()
+{
+ emit accepted(this);
+}
+
+void MessageEvent::discard()
+{
+ emit discarded(this);
+ delete this;
+}
+
+}
+
+#include "kopetemessageevent.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetemessageevent.h b/kopete/libkopete/kopetemessageevent.h
new file mode 100644
index 00000000..7beb1aa2
--- /dev/null
+++ b/kopete/libkopete/kopetemessageevent.h
@@ -0,0 +1,129 @@
+/*
+ kopetemessageevent.h - Kopete Message Event
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Hendrik vom Lehn <hvl@linux-4-ever.de>
+ Copyright (c) 2004 by Richard Smith <richard@metafoo.co.uk>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMESSAGEEVENT_H
+#define KOPETEMESSAGEEVENT_H
+
+#include <qobject.h>
+
+#include "kopetemessage.h"
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ * @author Richard Smith <richard@metafoo.co.uk>
+ *
+ * Kopete::MessageEvent is used when a new messages arrives, it is
+ * caught by the UI. It contains just informations about
+ * the message, and a signal when it is terminated (i.e.
+ * the message is read
+ **/
+class KOPETE_EXPORT MessageEvent : public QObject
+{
+ Q_OBJECT
+
+public:
+ MessageEvent(const Kopete::Message& , QObject* parent=0L, const char *name=0L);
+ ~MessageEvent();
+
+ /**
+ * @return A copy of the message
+ */
+ Kopete::Message message();
+
+ /**
+ * Sets the message contained in this event.
+ * @param message The new value for the message
+ */
+ void setMessage( const Kopete::Message &message );
+
+ /**
+ * The state of the event.
+ * - @c Nothing means that the event has not been accepted or ignored
+ * - @c Applied if the event has been applied
+ * - @c Ignored if the event has been ignored
+ */
+ enum EventState { Nothing , Applied , Ignored };
+
+ EventState state();
+
+public slots:
+ /**
+ * @deprecated Use accept() instead to continue the processing of this event once the caller has moved to using MessageHandlers
+ *
+ * execute the event
+ */
+ void apply();
+
+ /**
+ * @deprecated Use discard() instead to destroy this event once the caller has moved to using MessageHandlers
+ *
+ * ignore the event
+ */
+ void ignore();
+
+ /**
+ * @brief Passes the event to the next handler
+ *
+ * Call this when you've finished processing this event
+ */
+ void accept();
+
+ /**
+ * @brief Discards the event
+ *
+ * If this event should not be processed any further, this function
+ * should be called to discard it.
+ */
+ void discard();
+
+signals:
+ /**
+ * The event has been processed
+ */
+ void done(Kopete::MessageEvent *);
+
+ /**
+ * The event has been discarded.
+ * @param event The event sending the signal.
+ */
+ void discarded(Kopete::MessageEvent *event);
+
+ /**
+ * The event has been accepted by its current handler.
+ * @param event The event sending the signal.
+ */
+ void accepted(Kopete::MessageEvent *event);
+
+private:
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetemessagehandler.cpp b/kopete/libkopete/kopetemessagehandler.cpp
new file mode 100644
index 00000000..89628d4f
--- /dev/null
+++ b/kopete/libkopete/kopetemessagehandler.cpp
@@ -0,0 +1,111 @@
+/*
+ kopetemessagefilter.cpp - Kopete Message Filtering
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemessagehandler.h"
+#include "kopetemessageevent.h"
+
+#include <kstaticdeleter.h>
+
+namespace Kopete
+{
+
+class MessageHandler::Private
+{
+public:
+ Private() : next(0) {}
+ MessageHandler *next;
+};
+
+MessageHandler::MessageHandler()
+ : QObject( 0 ), d( new Private )
+{
+}
+
+MessageHandler::~MessageHandler()
+{
+ delete d;
+}
+
+MessageHandler *MessageHandler::next()
+{
+ return d->next;
+}
+
+void MessageHandler::setNext( MessageHandler *next )
+{
+ d->next = next;
+}
+
+int MessageHandler::capabilities()
+{
+ return d->next->capabilities();
+}
+
+void MessageHandler::handleMessageInternal( MessageEvent *event )
+{
+ connect( event, SIGNAL( accepted(Kopete::MessageEvent*) ), this, SLOT( messageAccepted(Kopete::MessageEvent*) ) );
+ handleMessage( event );
+}
+
+void MessageHandler::handleMessage( MessageEvent *event )
+{
+ messageAccepted( event );
+}
+
+void MessageHandler::messageAccepted( MessageEvent *event )
+{
+ disconnect( event, SIGNAL( accepted(Kopete::MessageEvent*) ), this, SLOT( messageAccepted(Kopete::MessageEvent*) ) );
+ d->next->handleMessageInternal( event );
+}
+
+
+class MessageHandlerFactory::Private
+{
+public:
+ static FactoryList &factories();
+};
+
+MessageHandlerFactory::FactoryList &MessageHandlerFactory::Private::factories()
+{
+ static KStaticDeleter<FactoryList> deleter;
+ static FactoryList *list = 0;
+ if( !list )
+ deleter.setObject( list, new FactoryList );
+ return *list;
+}
+
+MessageHandlerFactory::MessageHandlerFactory()
+ : d( new Private )
+{
+ Private::factories().append(this);
+}
+
+MessageHandlerFactory::~MessageHandlerFactory()
+{
+ Private::factories().remove( this );
+ delete d;
+}
+
+MessageHandlerFactory::FactoryList MessageHandlerFactory::messageHandlerFactories()
+{
+ return Private::factories();
+}
+
+}
+
+#include "kopetemessagehandler.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemessagehandler.h b/kopete/libkopete/kopetemessagehandler.h
new file mode 100644
index 00000000..ba16184c
--- /dev/null
+++ b/kopete/libkopete/kopetemessagehandler.h
@@ -0,0 +1,225 @@
+/*
+ kopetemessagehandler.h - Kopete Message Filtering
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMESSAGEHANDLER_H
+#define KOPETEMESSAGEHANDLER_H
+
+#include <qobject.h>
+//#include <kdemacros.h>
+#include "kopete_export.h"
+
+//FIXME: Message::MessageDirection could be moved into namespace Kopete
+// to avoid this being included everywhere
+#include "kopetemessage.h"
+
+#include <qvaluelist.h>
+
+namespace Kopete
+{
+
+class MessageEvent;
+class ChatSession;
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ *
+ * An object which sits between the protocol and the chat window which
+ * intercepts and processes messages on their way through.
+ *
+ * This class implements Handler role in the Chain of Responsibility pattern.
+ * The Client role will be filled by the Kopete::MessageHandlerChain class.
+ */
+class KOPETE_EXPORT MessageHandler : public QObject
+{
+ Q_OBJECT
+public:
+ MessageHandler();
+ virtual ~MessageHandler() = 0;
+
+ /**
+ * @return the next handler in the chain
+ */
+ MessageHandler *next();
+ // FIXME: remove?
+ void setNext( MessageHandler *next );
+
+ /**
+ * @brief Gets the rich-text capabilities of this message handling object
+ *
+ * The default implementation returns next()->capabilities().
+ */
+ virtual int capabilities();
+
+ /**
+ * @brief Performs any processing necessary on the message
+ *
+ * @param event The message event to process. Should not be null.
+ *
+ * Overriders of this handler @em must cause (possibly asynchronously)
+ * one of the following to happen:
+ * - @p event->discard() to be called
+ * - @p event->continue() to be called
+ * - this base class implementation to be called (equivalent to event->continue() but faster)
+ *
+ * The base class implementation passes the event on to the next
+ * handler in the chain.
+ *
+ * @note If you store @p event, be aware that it could be deleted at any time, and either
+ * connect to the its discarded(Kopete::MessageEvent*) signal or store it in a QGuardedPtr.
+ */
+ virtual void handleMessage( MessageEvent *event );
+
+ /** @internal */
+ void handleMessageInternal( MessageEvent *event );
+private slots:
+ /**
+ * @internal The message has been accepted. Pass it on to the next handler.
+ */
+ void messageAccepted( Kopete::MessageEvent *event );
+
+private:
+ class Private;
+ Private *d;
+};
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ *
+ * A factory for creating MessageHandlers. Instantiate a class derived from MessageHandlerFactory
+ * in order to make your MessageHandler be automatically added to the list of handlers used
+ * when constructing handler chains.
+ *
+ * @note If you construct a handler for an Inbound chain, it may still be asked to process Outbound
+ * messages. This is because when a message is being sent it first passes through the Outbound
+ * chain to the protocol, then (when it has been delivered) it passes back through the Inbound
+ * chain to the chat window to be displayed.
+ */
+class KOPETE_EXPORT MessageHandlerFactory
+{
+public:
+ /**
+ * Constructs a MessageHandlerFactory, and adds it to the list of factories considered when
+ * creating a MessageHandlerChain for a ChatSession.
+ *
+ * @note Since the factory is added to the list of possible factories before the object is
+ * finished being constructed, it is not safe to call any function from a derived class's
+ * constructor which may cause a MessageHandlerChain to be created.
+ */
+ MessageHandlerFactory();
+ /**
+ * Destroys the MessageHandlerFactory and removes it from the list of factories.
+ */
+ virtual ~MessageHandlerFactory();
+
+ typedef QValueList<MessageHandlerFactory*> FactoryList;
+ /**
+ * @return the list of registered message handler factories
+ */
+ static FactoryList messageHandlerFactories();
+
+ /**
+ * @brief Creates a message handler for a given manager in a given direction.
+ * @param manager The manager whose message handler chain the message handler is for
+ * @param direction The direction of the chain that is being created.
+ * @return the @ref MessageHandler object to put in the chain, or 0 if none is needed.
+ */
+ virtual MessageHandler *create( ChatSession *manager, Message::MessageDirection direction ) = 0;
+
+ /**
+ * Special stages usable with any message direction
+ */
+ enum SpecialStage
+ {
+ StageDoNotCreate = -10000, ///< do not create a filter for this stage
+ StageStart = 0, ///< start of processing
+ StageEnd = 10000 ///< end of processing
+ };
+
+ /**
+ * Processing stages for handlers in inbound message handler chains
+ */
+ enum InboundStage
+ {
+ InStageStart = 0, ///< message was just received
+ InStageToSent = 2000, ///< convert from received format to sent format
+ InStageToDesired = 5000, ///< convert to how the user wants the message
+ InStageFormat = 7000, ///< decorate the message without changing the content
+ InStageEnd = 10000 ///< message ready for display
+ };
+
+ /**
+ * Processing stages for handlers in outbound message handler chains
+ */
+ enum OutboundStage
+ {
+ OutStageStart = 0, ///< user just hit Send
+ OutStageParse = 2000, ///< process commands
+ OutStageToDesired = 4000, ///< convert to how the user wanted to send
+ OutStageFormat = 6000, ///< decorate the message without changing the content
+ OutStageToSent = 8000, ///< convert to the format to send in
+ OutStageEnd = 10000 ///< message ready for sending
+ };
+
+ /**
+ * Processing stages for handlers in internal message handler chains
+ */
+ enum InternalStage
+ {
+ IntStageStart = 0, ///< some component just created the message
+ IntStageEnd = 10000 ///< message ready for display
+ };
+
+ /**
+ * Offsets within a processing stage. Using these values allows finer
+ * control over where in a chain a message handler will be added. Add
+ * one of these values to values from the various Stage enumerations
+ * to form a filter position.
+ */
+ enum Offset
+ {
+ OffsetBefore = -90,
+ OffsetVeryEarly = -60,
+ OffsetEarly = -30,
+ OffsetNormal = 0,
+ OffsetLate = 30,
+ OffsetVeryLate = 60,
+ OffsetAfter = 90
+ };
+
+ /**
+ * @brief Returns the position in the message handler chain to put this factory's handlers
+ * @param manager The manager whose message handler chain the message handler is for
+ * @param direction The direction of the chain that is being created.
+ * @return a member of the InboundStage, OutboundStage or InternalStage enumeration, as
+ * appropriate, optionally combined with a member of the Offset enumeration.
+ * @retval StageDoNotCreate No filter should be created for this chain.
+ */
+ virtual int filterPosition( ChatSession *manager, Message::MessageDirection direction ) = 0;
+
+private:
+ // noncopyable
+ MessageHandlerFactory(const MessageHandlerFactory &);
+ void operator=(const MessageHandlerFactory &);
+
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemessagehandlerchain.cpp b/kopete/libkopete/kopetemessagehandlerchain.cpp
new file mode 100644
index 00000000..fe1e96ab
--- /dev/null
+++ b/kopete/libkopete/kopetemessagehandlerchain.cpp
@@ -0,0 +1,186 @@
+/*
+ kopetemessagehandlerchain.h - Kopete Message Handler Chain
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemessagehandlerchain.h"
+#include "kopetemessagehandler.h"
+#include "kopetemessageevent.h"
+#include "kopetechatsession.h"
+
+#include <kdebug.h>
+
+#include <qmap.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+namespace Kopete
+{
+
+class MessageHandlerChainTerminator : public MessageHandler
+{
+public:
+ void handleMessage( MessageEvent *event )
+ {
+ kdError( 14010 ) << k_funcinfo << "message got to end of chain!" << endl;
+ event->discard();
+ }
+ int capabilities()
+ {
+ kdError( 14010 ) << k_funcinfo << "request got to end of chain!" << endl;
+ return 0;
+ }
+};
+
+// BEGIN MessageHandlerChain
+
+class MessageHandlerChain::Private
+{
+public:
+ Private() : first(0) {}
+ MessageHandler *first;
+};
+
+MessageHandlerChain::Ptr MessageHandlerChain::create( ChatSession *manager, Message::MessageDirection direction )
+{
+ // create the handler chain
+ MessageHandlerChain *chain = new MessageHandlerChain;
+
+ // grab the list of handler factories
+ typedef MessageHandlerFactory::FactoryList FactoryList;
+ FactoryList factories = MessageHandlerFactory::messageHandlerFactories();
+
+ // create a sorted list of handlers
+ typedef QValueList<MessageHandler*> HandlerList;
+ typedef QMap<int,HandlerList> HandlerMap;
+ HandlerMap handlers;
+ uint count = 0;
+ for( FactoryList::Iterator it = factories.begin(); it != factories.end(); ++it )
+ {
+ int position = (*it)->filterPosition( manager, direction );
+ if ( position == MessageHandlerFactory::StageDoNotCreate )
+ continue;
+ MessageHandler *handler = (*it)->create( manager, direction );
+ if ( handler )
+ {
+ ++count;
+ handlers[ position ].append( handler );
+ }
+ }
+
+ kdDebug(14010) << k_funcinfo << "got " << count << " handlers for chain" << endl;
+
+ // add the handlers to the chain
+ MessageHandler *curr = 0;
+ for( HandlerMap::Iterator it = handlers.begin(); it != handlers.end(); ++it )
+ {
+ for ( HandlerList::Iterator handlerIt = (*it).begin(); handlerIt != (*it).end(); ++handlerIt )
+ {
+ if ( curr )
+ curr->setNext( *handlerIt );
+ else
+ chain->d->first = *handlerIt;
+ curr = *handlerIt;
+ }
+ }
+
+ // add a terminator to avoid crashes if the message somehow manages to get to the
+ // end of the chain. maybe we should use a MessageHandlerFactory for this too?
+ MessageHandler *terminator = new MessageHandlerChainTerminator;
+ if ( curr )
+ curr->setNext( terminator );
+ else // empty chain: might happen for dir == Internal
+ chain->d->first = terminator;
+
+ return chain;
+}
+
+MessageHandlerChain::MessageHandlerChain()
+ : QObject( 0 ), d( new Private )
+{
+}
+
+MessageHandlerChain::~MessageHandlerChain()
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ MessageHandler *handler = d->first;
+ while( handler )
+ {
+ MessageHandler *next = handler->next();
+ delete handler;
+ handler = next;
+ }
+ delete d;
+}
+
+
+ProcessMessageTask *MessageHandlerChain::processMessage( const Message &message )
+{
+ MessageEvent *event = new MessageEvent( message );
+ return new ProcessMessageTask( this, event );
+}
+
+int MessageHandlerChain::capabilities()
+{
+ return d->first->capabilities();
+}
+
+// END MessageHandlerChain
+
+// BEGIN ProcessMessageTask
+
+class ProcessMessageTask::Private
+{
+public:
+ Private( MessageHandlerChain::Ptr chain, MessageEvent *event ) : chain(chain), event(event) {}
+ MessageHandlerChain::Ptr chain;
+ MessageEvent *event;
+};
+
+ProcessMessageTask::ProcessMessageTask( MessageHandlerChain::Ptr chain, MessageEvent *event )
+ : d( new Private(chain, event) )
+{
+ QTimer::singleShot( 0, this, SLOT( slotStart() ) );
+ connect( event, SIGNAL( done( Kopete::MessageEvent* ) ), this, SLOT( slotDone() ) );
+ event->message().manager()->ref();
+}
+
+ProcessMessageTask::~ProcessMessageTask()
+{
+ delete d;
+}
+
+void ProcessMessageTask::slotStart()
+{
+ d->chain->d->first->handleMessageInternal( d->event );
+}
+
+void ProcessMessageTask::slotDone()
+{
+ d->event->message().manager()->deref();
+ emitResult();
+}
+
+MessageEvent *ProcessMessageTask::event()
+{
+ return d->event;
+}
+
+//END ProcessMessageTask
+
+}
+
+#include "kopetemessagehandlerchain.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemessagehandlerchain.h b/kopete/libkopete/kopetemessagehandlerchain.h
new file mode 100644
index 00000000..5852c2da
--- /dev/null
+++ b/kopete/libkopete/kopetemessagehandlerchain.h
@@ -0,0 +1,96 @@
+/*
+ kopetefilterchain.h - Kopete Message Filter Chain
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEFILTERCHAIN_H
+#define KOPETEFILTERCHAIN_H
+
+#include <qobject.h>
+#include <kdemacros.h>
+#include <ksharedptr.h>
+#include "kopetemessage.h"
+#include "kopetetask.h"
+
+namespace Kopete
+{
+
+class MessageEvent;
+class MessageHandler;
+class ProcessMessageTask;
+
+/**
+ * @brief A chain of message handlers; the processing layer between protocol and chat view
+ *
+ * This class represents a chain of connected message handlers.
+ *
+ * This class is the client of the chain of responsibility formed by the
+ * MessageHandlers, and acts as a facade for that chain, presenting a
+ * more convenient interface.
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class MessageHandlerChain : public QObject, private KShared
+{
+ Q_OBJECT
+public:
+ friend class KSharedPtr<MessageHandlerChain>;
+ typedef KSharedPtr<MessageHandlerChain> Ptr;
+
+ /**
+ * Create a new MessageHandlerChain object with the appropriate handlers for
+ * processing messages entering @p manager in direction @p direction.
+ */
+ static Ptr create( ChatSession *manager, Message::MessageDirection direction );
+
+ ProcessMessageTask *processMessage( const Message &message );
+ int capabilities();
+
+private:
+ MessageHandlerChain();
+ ~MessageHandlerChain();
+
+ friend class ProcessMessageTask;
+ class Private;
+ Private *d;
+};
+
+/**
+ * @brief A task for processing a message
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class ProcessMessageTask : public Task
+{
+ Q_OBJECT
+public:
+ MessageEvent *event();
+
+private slots:
+ void slotStart();
+ void slotDone();
+
+private:
+ ProcessMessageTask(MessageHandlerChain::Ptr, MessageEvent *event);
+ ~ProcessMessageTask();
+
+ friend class MessageHandlerChain;
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemessagemanager.h b/kopete/libkopete/kopetemessagemanager.h
new file mode 100644
index 00000000..a07fb6f9
--- /dev/null
+++ b/kopete/libkopete/kopetemessagemanager.h
@@ -0,0 +1,3 @@
+#warning kopetemessagemanager.h has been renamed to kopetechatsession.h
+#include "kopetechatsession.h"
+
diff --git a/kopete/libkopete/kopetemessagemanagerfactory.h b/kopete/libkopete/kopetemessagemanagerfactory.h
new file mode 100644
index 00000000..9ebdaa98
--- /dev/null
+++ b/kopete/libkopete/kopetemessagemanagerfactory.h
@@ -0,0 +1,3 @@
+#warning kopetemessagemanagerfactory.h has been renamed to kopetechatsessionmanager.h
+#include "kopetechatsessionmanager.h"
+
diff --git a/kopete/libkopete/kopetemetacontact.cpp b/kopete/libkopete/kopetemetacontact.cpp
new file mode 100644
index 00000000..e181f52e
--- /dev/null
+++ b/kopete/libkopete/kopetemetacontact.cpp
@@ -0,0 +1,1442 @@
+/*
+ kopetemetacontact.cpp - Kopete Meta Contact
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemetacontact.h"
+
+#include <kapplication.h>
+
+#include <kabc/addressbook.h>
+#include <kabc/addressee.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdeversion.h>
+
+#include "kabcpersistence.h"
+#include "kopetecontactlist.h"
+#include "kopetecontact.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetepluginmanager.h"
+#include "kopetegroup.h"
+#include "kopeteglobal.h"
+#include "kopeteprefs.h"
+#include "kopeteuiglobal.h"
+#include "kopetepicture.h"
+
+namespace Kopete {
+
+// this is just to save typing
+const QString NSCID_ELEM = QString::fromUtf8("nameSourceContactId" );
+const QString NSPID_ELEM = QString::fromUtf8( "nameSourcePluginId" );
+const QString NSAID_ELEM = QString::fromUtf8( "nameSourceAccountId" );
+const QString PSCID_ELEM = QString::fromUtf8( "photoSourceContactId" );
+const QString PSPID_ELEM = QString::fromUtf8( "photoSourcePluginId" );
+const QString PSAID_ELEM = QString::fromUtf8( "photoSourceAccountId" );
+
+class MetaContact::Private
+{ public:
+ Private() :
+ photoSource(MetaContact::SourceCustom), displayNameSource(MetaContact::SourceCustom),
+ displayNameSourceContact(0L), photoSourceContact(0L), temporary(false),
+ onlineStatus(Kopete::OnlineStatus::Offline), photoSyncedWithKABC(false)
+ {}
+
+ ~Private()
+ {}
+
+ QPtrList<Contact> contacts;
+
+ // property sources
+ PropertySource photoSource;
+ PropertySource displayNameSource;
+
+ // when source is contact
+ Contact *displayNameSourceContact;
+ Contact *photoSourceContact;
+
+ // used when source is kabc
+ QString metaContactId;
+
+ // used when source is custom
+ QString displayName;
+ KURL photoUrl;
+
+ QPtrList<Group> groups;
+ QMap<QString, QMap<QString, QString> > addressBook;
+ bool temporary;
+
+ OnlineStatus::StatusType onlineStatus;
+ bool photoSyncedWithKABC;
+
+ // Used to set contact source at load.
+ QString nameSourcePID, nameSourceAID, nameSourceCID;
+ QString photoSourcePID, photoSourceAID, photoSourceCID;
+
+ // The photo cache. Reduce disk access and CPU usage.
+ Picture customPicture, contactPicture, kabcPicture;
+};
+
+MetaContact::MetaContact()
+ : ContactListElement( ContactList::self() )
+{
+ d = new Private;
+
+ connect( this, SIGNAL( pluginDataChanged() ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( iconChanged( Kopete::ContactListElement::IconState, const QString & ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( useCustomIconChanged( bool ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( displayNameChanged( const QString &, const QString & ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( movedToGroup( Kopete::MetaContact *, Kopete::Group *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( removedFromGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( addedToGroup( Kopete::MetaContact *, Kopete::Group * ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( contactAdded( Kopete::Contact * ) ), SIGNAL( persistentDataChanged() ) );
+ connect( this, SIGNAL( contactRemoved( Kopete::Contact * ) ), SIGNAL( persistentDataChanged() ) );
+
+ // Update the KABC picture when the KDE Address book change.
+ connect(KABCPersistence::self()->addressBook(), SIGNAL(addressBookChanged(AddressBook *)), this, SLOT(slotUpdateAddressBookPicture()));
+
+ // make sure MetaContact is at least in one group
+ addToGroup( Group::topLevel() );
+ //i'm not sure this is correct -Olivier
+ // we probably should do the check in groups() instead
+}
+
+MetaContact::~MetaContact()
+{
+ delete d;
+}
+
+void MetaContact::addContact( Contact *c )
+{
+ if( d->contacts.contains( c ) )
+ {
+ kdWarning(14010) << "Ignoring attempt to add duplicate contact " << c->contactId() << "!" << endl;
+ }
+ else
+ {
+ d->contacts.append( c );
+
+ connect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+
+ connect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+
+ connect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
+ this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
+
+ connect( c, SIGNAL( idleStateChanged( Kopete::Contact * ) ),
+ this, SIGNAL( contactIdleStateChanged( Kopete::Contact * ) ) );
+
+ emit contactAdded(c);
+
+ updateOnlineStatus();
+
+ // if this is the first contact, probbaly was created by a protocol
+ // so it has empty custom properties, then set sources to the contact
+ if ( d->contacts.count() == 1 )
+ {
+ if ( displayName().isEmpty() )
+ {
+ setDisplayNameSourceContact(c);
+ setDisplayNameSource(SourceContact);
+ }
+ if ( photo().isNull() )
+ {
+ setPhotoSourceContact(c);
+ setPhotoSource(SourceContact);
+ }
+ }
+ }
+}
+
+void MetaContact::updateOnlineStatus()
+{
+ Kopete::OnlineStatus::StatusType newStatus = Kopete::OnlineStatus::Unknown;
+ Kopete::OnlineStatus mostSignificantStatus;
+
+ for ( QPtrListIterator<Contact> it( d->contacts ); it.current(); ++it )
+ {
+ // find most significant status
+ if ( it.current()->onlineStatus() > mostSignificantStatus )
+ mostSignificantStatus = it.current()->onlineStatus();
+ }
+
+ newStatus = mostSignificantStatus.status();
+
+ if( newStatus != d->onlineStatus )
+ {
+ d->onlineStatus = newStatus;
+ emit onlineStatusChanged( this, d->onlineStatus );
+ }
+}
+
+
+void MetaContact::removeContact(Contact *c, bool deleted)
+{
+ if( !d->contacts.contains( c ) )
+ {
+ kdDebug(14010) << k_funcinfo << " Contact is not in this metaContact " << endl;
+ }
+ else
+ {
+ // must check before removing, or will always be false
+ bool wasTrackingName = ( !displayNameSourceContact() && (displayNameSource() == SourceContact) );
+ bool wasTrackingPhoto = ( !photoSourceContact() && (photoSource() == SourceContact) );
+ // save for later use
+ QString currDisplayName = displayName();
+
+ d->contacts.remove( c );
+
+ // if the contact was a source of property data, clean
+ if (displayNameSourceContact() == c)
+ setDisplayNameSourceContact(0L);
+ if (photoSourceContact() == c)
+ setPhotoSourceContact(0L);
+
+
+ if ( wasTrackingName )
+ {
+ // Oh! this contact was the source for the metacontact's name
+ // lets do something
+ // is this the only contact?
+ if ( d->contacts.isEmpty() )
+ {
+ // fallback to a custom name as we don't have
+ // more contacts to chose as source.
+ setDisplayNameSource(SourceCustom);
+ // perhaps the custom display name was empty
+ // no problems baby, I saved the old one.
+ setDisplayName(currDisplayName);
+ }
+ else
+ {
+ // we didn't fallback to SourceCustom above so lets use the next
+ // contact as source
+ setDisplayNameSourceContact( d->contacts.first() );
+ }
+ }
+
+ if ( wasTrackingPhoto )
+ {
+ // Oh! this contact was the source for the metacontact's photo
+ // lets do something
+ // is this the only contact?
+ if ( d->contacts.isEmpty() )
+ {
+ // fallback to a custom photo as we don't have
+ // more contacts to chose as source.
+ setPhotoSource(SourceCustom);
+ // FIXME set the custom photo
+ }
+ else
+ {
+ // we didn't fallback to SourceCustom above so lets use the next
+ // contact as source
+ setPhotoSourceContact( d->contacts.first() );
+ }
+ }
+
+ if(!deleted)
+ { //If this function is tell by slotContactRemoved, c is maybe just a QObject
+ disconnect( c, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+ disconnect( c, SIGNAL( propertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ),
+ this, SLOT( slotPropertyChanged( Kopete::Contact *, const QString &, const QVariant &, const QVariant & ) ) ) ;
+ disconnect( c, SIGNAL( contactDestroyed( Kopete::Contact * ) ),
+ this, SLOT( slotContactDestroyed( Kopete::Contact * ) ) );
+ disconnect( c, SIGNAL( idleStateChanged( Kopete::Contact * ) ),
+ this, SIGNAL( contactIdleStateChanged( Kopete::Contact *) ) );
+
+ kdDebug( 14010 ) << k_funcinfo << "Contact disconnected" << endl;
+
+ KABCPersistence::self()->write( this );
+ }
+
+ // Reparent the contact
+ removeChild( c );
+
+ emit contactRemoved( c );
+ }
+ updateOnlineStatus();
+}
+
+Contact *MetaContact::findContact( const QString &protocolId, const QString &accountId, const QString &contactId )
+{
+ //kdDebug( 14010 ) << k_funcinfo << "Num contacts: " << d->contacts.count() << endl;
+ QPtrListIterator<Contact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ //kdDebug( 14010 ) << k_funcinfo << "Trying " << it.current()->contactId() << ", proto "
+ //<< it.current()->protocol()->pluginId() << ", account " << it.current()->accountId() << endl;
+ if( ( it.current()->contactId() == contactId ) && ( it.current()->protocol()->pluginId() == protocolId || protocolId.isNull() ) )
+ {
+ if ( accountId.isNull() )
+ return it.current();
+
+ if(it.current()->account())
+ {
+ if(it.current()->account()->accountId() == accountId)
+ return it.current();
+ }
+ }
+ }
+
+ // Contact not found
+ return 0L;
+}
+
+void MetaContact::setDisplayNameSource(PropertySource source)
+{
+ QString oldName = displayName();
+ d->displayNameSource = source;
+ QString newName = displayName();
+ if ( oldName != newName)
+ emit displayNameChanged( oldName, newName );
+}
+
+MetaContact::PropertySource MetaContact::displayNameSource() const
+{
+ return d->displayNameSource;
+}
+
+void MetaContact::setPhotoSource(PropertySource source)
+{
+ PropertySource oldSource = photoSource();
+ d->photoSource = source;
+ if ( source != oldSource )
+ {
+ emit photoChanged();
+ }
+}
+
+MetaContact::PropertySource MetaContact::photoSource() const
+{
+ return d->photoSource;
+}
+
+
+Contact *MetaContact::sendMessage()
+{
+ Contact *c = preferredContact();
+
+ if( !c )
+ {
+ KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
+ "until this user comes online." ), i18n( "User is Not Reachable" ) );
+ }
+ else
+ {
+ c->sendMessage();
+ return c;
+ }
+ return 0L;
+}
+
+Contact *MetaContact::startChat()
+{
+ Contact *c = preferredContact();
+
+ if( !c )
+ {
+ KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
+ "until this user comes online." ), i18n( "User is Not Reachable" ) );
+ }
+ else
+ {
+ c->startChat();
+ return c;
+ }
+ return 0L;
+}
+
+Contact *MetaContact::preferredContact()
+{
+ /*
+ This function will determine what contact will be used to reach the contact.
+
+ The prefered contact is choose with the following criterias: (in that order)
+ 1) If a contact was an open chatwindow already, we will use that one.
+ 2) The contact with the better online status is used. But if that
+ contact is not reachable, we prefer return no contact.
+ 3) If all the criterias aboxe still gives ex-eaquo, we use the preffered
+ account as selected in the account preferances (with the arrows)
+ */
+
+ Contact *contact = 0;
+ bool hasOpenView=false; //has the selected contact already an open chatwindow
+ for ( QPtrListIterator<Contact> it( d->contacts ); it.current(); ++it )
+ {
+ Contact *c=it.current();
+
+ //Does the contact an open chatwindow?
+ if( c->manager( Contact::CannotCreate ) )
+ { //no need to check the view. having a manager is enough
+ if( !hasOpenView )
+ {
+ contact=c;
+ hasOpenView=true;
+ if( c->isReachable() )
+ continue;
+ } //else, several contact might have an open view, uses following criterias
+ }
+ else if( hasOpenView && contact->isReachable() )
+ continue; //This contact has not open view, but the selected contact has, and is reachable
+
+ // FIXME: The isConnected call should be handled in Contact::isReachable
+ // after KDE 3.2 - Martijn
+ if ( !c->account() || !c->account()->isConnected() || !c->isReachable() )
+ continue; //if this contact is not reachable, we ignore it.
+
+ if ( !contact )
+ { //this is the first contact.
+ contact= c;
+ continue;
+ }
+
+ if( c->onlineStatus().status() > contact->onlineStatus().status() )
+ contact=c; //this contact has a better status
+ else if ( c->onlineStatus().status() == contact->onlineStatus().status() )
+ {
+ if( c->account()->priority() > contact->account()->priority() )
+ contact=c;
+ else if( c->account()->priority() == contact->account()->priority()
+ && c->onlineStatus().weight() > contact->onlineStatus().weight() )
+ contact = c; //the weight is not supposed to follow the same scale for each protocol
+ }
+ }
+ return contact;
+}
+
+Contact *MetaContact::execute()
+{
+ Contact *c = preferredContact();
+
+ if( !c )
+ {
+ KMessageBox::queuedMessageBox( UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "This user is not reachable at the moment. Please make sure you are connected and using a protocol that supports offline sending, or wait "
+ "until this user comes online." ), i18n( "User is Not Reachable" ) );
+ }
+ else
+ {
+ c->execute();
+ return c;
+ }
+
+ return 0L;
+}
+
+unsigned long int MetaContact::idleTime() const
+{
+ unsigned long int time = 0;
+ QPtrListIterator<Contact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ unsigned long int i = it.current()->idleTime();
+ if( it.current()->isOnline() && i < time || time == 0 )
+ {
+ time = i;
+ }
+ }
+ return time;
+}
+
+QString MetaContact::statusIcon() const
+{
+ switch( status() )
+ {
+ case OnlineStatus::Online:
+ if( useCustomIcon() )
+ return icon( ContactListElement::Online );
+ else
+ return QString::fromUtf8( "metacontact_online" );
+ case OnlineStatus::Away:
+ if( useCustomIcon() )
+ return icon( ContactListElement::Away );
+ else
+ return QString::fromUtf8( "metacontact_away" );
+
+ case OnlineStatus::Unknown:
+ if( useCustomIcon() )
+ return icon( ContactListElement::Unknown );
+ if ( d->contacts.isEmpty() )
+ return QString::fromUtf8( "metacontact_unknown" );
+ else
+ return QString::fromUtf8( "metacontact_offline" );
+
+ case OnlineStatus::Offline:
+ default:
+ if( useCustomIcon() )
+ return icon( ContactListElement::Offline );
+ else
+ return QString::fromUtf8( "metacontact_offline" );
+ }
+}
+
+QString MetaContact::statusString() const
+{
+ switch( status() )
+ {
+ case OnlineStatus::Online:
+ return i18n( "Online" );
+ case OnlineStatus::Away:
+ return i18n( "Away" );
+ case OnlineStatus::Offline:
+ return i18n( "Offline" );
+ case OnlineStatus::Unknown:
+ default:
+ return i18n( "Status not available" );
+ }
+}
+
+OnlineStatus::StatusType MetaContact::status() const
+{
+ return d->onlineStatus;
+}
+
+bool MetaContact::isOnline() const
+{
+ QPtrListIterator<Contact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if( it.current()->isOnline() )
+ return true;
+ }
+ return false;
+}
+
+bool MetaContact::isReachable() const
+{
+ if ( isOnline() )
+ return true;
+
+ for ( QPtrListIterator<Contact> it( d->contacts ); it.current(); ++it )
+ {
+ if ( it.current()->account()->isConnected() && it.current()->isReachable() )
+ return true;
+ }
+ return false;
+}
+
+//Determine if we are capable of accepting file transfers
+bool MetaContact::canAcceptFiles() const
+{
+ if( !isOnline() )
+ return false;
+
+ QPtrListIterator<Contact> it( d->contacts );
+ for( ; it.current(); ++it )
+ {
+ if( it.current()->canAcceptFiles() )
+ return true;
+ }
+ return false;
+}
+
+//Slot for sending files
+void MetaContact::sendFile( const KURL &sourceURL, const QString &altFileName, unsigned long fileSize )
+{
+ //If we can't send any files then exit
+ if( d->contacts.isEmpty() || !canAcceptFiles() )
+ return;
+
+ //Find the highest ranked protocol that can accept files
+ Contact *contact = d->contacts.first();
+ for( QPtrListIterator<Contact> it( d->contacts ) ; it.current(); ++it )
+ {
+ if( ( *it )->onlineStatus() > contact->onlineStatus() && ( *it )->canAcceptFiles() )
+ contact = *it;
+ }
+
+ //Call the sendFile slot of this protocol
+ contact->sendFile( sourceURL, altFileName, fileSize );
+}
+
+
+void MetaContact::slotContactStatusChanged( Contact * c, const OnlineStatus &status, const OnlineStatus &/*oldstatus*/ )
+{
+ updateOnlineStatus();
+ emit contactStatusChanged( c, status );
+}
+
+void MetaContact::setDisplayName( const QString &name )
+{
+ /*kdDebug( 14010 ) << k_funcinfo << "Change displayName from " << d->displayName <<
+ " to " << name << ", d->trackChildNameChanges=" << d->trackChildNameChanges << endl;
+ kdDebug(14010) << kdBacktrace(6) << endl;*/
+
+ if( name == d->displayName )
+ return;
+
+ const QString old = d->displayName;
+ d->displayName = name;
+
+ emit displayNameChanged( old , name );
+
+ for( QPtrListIterator<Kopete::Contact> it( d->contacts ) ; it.current(); ++it )
+ ( *it )->sync(Contact::DisplayNameChanged);
+
+}
+
+QString MetaContact::customDisplayName() const
+{
+ return d->displayName;
+}
+
+QString MetaContact::displayName() const
+{
+ PropertySource source = displayNameSource();
+ if ( source == SourceKABC )
+ {
+ // kabc source, try to get from addressbook
+ // if the metacontact has a kabc association
+ if ( !metaContactId().isEmpty() )
+ return nameFromKABC(metaContactId());
+ }
+ else if ( source == SourceContact )
+ {
+ if ( d->displayNameSourceContact==0 )
+ {
+ if( d->contacts.count() >= 1 )
+ {// don't call setDisplayNameSource , or there will probably be an infinite loop
+ d->displayNameSourceContact=d->contacts.first();
+// kdDebug( 14010 ) << k_funcinfo << " setting displayname source for " << metaContactId() << endl;
+ }
+ }
+ if ( displayNameSourceContact() != 0L )
+ {
+ return nameFromContact(displayNameSourceContact());
+ }
+ else
+ {
+// kdDebug( 14010 ) << k_funcinfo << " source == SourceContact , but there is no displayNameSourceContact for contact " << metaContactId() << endl;
+ }
+ }
+ return d->displayName;
+}
+
+QString nameFromKABC( const QString &id ) /*const*/
+{
+ KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
+ if ( ! id.isEmpty() && !id.contains(':') )
+ {
+ KABC::Addressee theAddressee = ab->findByUid(id);
+ if ( theAddressee.isEmpty() )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "no KABC::Addressee found for ( " << id << " ) " << " in current address book" << endl;
+ }
+ else
+ {
+ return theAddressee.formattedName();
+ }
+ }
+ // no kabc association, return null image
+ return QString::null;
+}
+
+QString nameFromContact( Kopete::Contact *c) /*const*/
+{
+ if ( !c )
+ return QString::null;
+
+ QString contactName;
+ if ( c->hasProperty( Kopete::Global::Properties::self()->nickName().key() ) )
+ contactName = c->property( Global::Properties::self()->nickName()).value().toString();
+
+ //the replace is there to workaround the Bug 95444
+ return contactName.isEmpty() ? c->contactId() : contactName.replace('\n',QString::fromUtf8(""));
+}
+
+KURL MetaContact::customPhoto() const
+{
+ return d->photoUrl;
+}
+
+void MetaContact::setPhoto( const KURL &url )
+{
+ d->photoUrl = url;
+ d->customPicture.setPicture(url.path());
+
+ if ( photoSource() == SourceCustom )
+ {
+ emit photoChanged();
+ }
+}
+
+QImage MetaContact::photo() const
+{
+ if( picture().image().width() > 96 && picture().image().height() > 96 )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Resizing image from " << picture().image().width() << " x " << picture().image().height() << endl;
+ return picture().image().smoothScale(96,96,QImage::ScaleMin);
+ }
+ else
+ return picture().image();
+}
+
+Picture &MetaContact::picture() const
+{
+ if ( photoSource() == SourceKABC )
+ {
+ return d->kabcPicture;
+ }
+ else if ( photoSource() == SourceContact )
+ {
+ return d->contactPicture;
+ }
+
+ return d->customPicture;
+}
+
+QImage MetaContact::photoFromCustom() const
+{
+ return d->customPicture.image();
+}
+
+QImage photoFromContact( Kopete::Contact *contact) /*const*/
+{
+ if ( contact == 0L )
+ return QImage();
+
+ QVariant photoProp;
+ if ( contact->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
+ photoProp = contact->property( Kopete::Global::Properties::self()->photo().key() ).value();
+
+ QImage img;
+ if(photoProp.canCast( QVariant::Image ))
+ img=photoProp.toImage();
+ else if(photoProp.canCast( QVariant::Pixmap ))
+ img=photoProp.toPixmap().convertToImage();
+ else if(!photoProp.asString().isEmpty())
+ {
+ img=QPixmap( photoProp.toString() ).convertToImage();
+ }
+ return img;
+}
+
+QImage photoFromKABC( const QString &id ) /*const*/
+{
+ KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
+ if ( ! id.isEmpty() && !id.contains(':') )
+ {
+ KABC::Addressee theAddressee = ab->findByUid(id);
+ if ( theAddressee.isEmpty() )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "no KABC::Addressee found for ( " << id << " ) " << " in current address book" << endl;
+ }
+ else
+ {
+ KABC::Picture pic = theAddressee.photo();
+ if ( pic.data().isNull() && pic.url().isEmpty() )
+ pic = theAddressee.logo();
+
+ if ( pic.isIntern())
+ {
+ return pic.data();
+ }
+ else
+ {
+ return QPixmap( pic.url() ).convertToImage();
+ }
+ }
+ }
+ // no kabc association, return null image
+ return QImage();
+}
+
+Contact *MetaContact::displayNameSourceContact() const
+{
+ return d->displayNameSourceContact;
+}
+
+Contact *MetaContact::photoSourceContact() const
+{
+ return d->photoSourceContact;
+}
+
+void MetaContact::setDisplayNameSourceContact( Contact *contact )
+{
+ Contact *old = d->displayNameSourceContact;
+ d->displayNameSourceContact = contact;
+ if ( displayNameSource() == SourceContact )
+ {
+ emit displayNameChanged( nameFromContact(old), nameFromContact(contact));
+ }
+}
+
+void MetaContact::setPhotoSourceContact( Contact *contact )
+{
+ d->photoSourceContact = contact;
+
+ // Create a cache for the contact photo.
+ if(d->photoSourceContact != 0L)
+ {
+ QVariant photoProp;
+ if ( contact->hasProperty( Kopete::Global::Properties::self()->photo().key() ) )
+ photoProp = contact->property( Kopete::Global::Properties::self()->photo().key() ).value();
+
+ if(photoProp.canCast( QVariant::Image ))
+ d->contactPicture.setPicture(photoProp.toImage());
+ else if(photoProp.canCast( QVariant::Pixmap ))
+ d->contactPicture.setPicture(photoProp.toPixmap().convertToImage());
+ else if(!photoProp.asString().isEmpty())
+ {
+ d->contactPicture.setPicture(photoProp.toString());
+ }
+ }
+
+ if ( photoSource() == SourceContact )
+ {
+ emit photoChanged();
+ }
+}
+
+void MetaContact::slotPropertyChanged( Contact* subcontact, const QString &key,
+ const QVariant &oldValue, const QVariant &newValue )
+{
+ if ( displayNameSource() == SourceContact )
+ {
+ if( key == Global::Properties::self()->nickName().key() )
+ {
+ if (displayNameSourceContact() == subcontact)
+ {
+ emit displayNameChanged( oldValue.toString(), newValue.toString());
+ }
+ else
+ {
+ // HACK the displayName that changed is not from the contact we are tracking, but
+ // as the current one is null, lets use this new one
+ if (displayName().isEmpty())
+ setDisplayNameSourceContact(subcontact);
+ }
+ }
+ }
+
+ if (photoSource() == SourceContact)
+ {
+ if ( key == Global::Properties::self()->photo().key() )
+ {
+ if (photoSourceContact() != subcontact)
+ {
+ // HACK the displayName that changed is not from the contact we are tracking, but
+ // as the current one is null, lets use this new one
+ if (photo().isNull())
+ setPhotoSourceContact(subcontact);
+
+ }
+ else if(photoSourceContact() == subcontact)
+ {
+ if(d->photoSyncedWithKABC)
+ setPhotoSyncedWithKABC(true);
+
+ setPhotoSourceContact(subcontact);
+ }
+ }
+ }
+}
+
+void MetaContact::moveToGroup( Group *from, Group *to )
+{
+ if ( !from || !groups().contains( from ) )
+ {
+ // We're adding, not moving, because 'from' is illegal
+ addToGroup( to );
+ return;
+ }
+
+ if ( !to || groups().contains( to ) )
+ {
+ // We're removing, not moving, because 'to' is illegal
+ removeFromGroup( from );
+ return;
+ }
+
+ if ( isTemporary() && to->type() != Group::Temporary )
+ return;
+
+
+ //kdDebug( 14010 ) << k_funcinfo << from->displayName() << " => " << to->displayName() << endl;
+
+ d->groups.remove( from );
+ d->groups.append( to );
+
+ for( Contact *c = d->contacts.first(); c ; c = d->contacts.next() )
+ c->sync(Contact::MovedBetweenGroup);
+
+ emit movedToGroup( this, from, to );
+}
+
+void MetaContact::removeFromGroup( Group *group )
+{
+ if ( !group || !groups().contains( group ) || ( isTemporary() && group->type() == Group::Temporary ) )
+ {
+ return;
+ }
+
+ d->groups.remove( group );
+
+ // make sure MetaContact is at least in one group
+ if ( d->groups.isEmpty() )
+ {
+ d->groups.append( Group::topLevel() );
+ emit addedToGroup( this, Group::topLevel() );
+ }
+
+ for( Contact *c = d->contacts.first(); c ; c = d->contacts.next() )
+ c->sync(Contact::MovedBetweenGroup);
+
+ emit removedFromGroup( this, group );
+}
+
+void MetaContact::addToGroup( Group *to )
+{
+ if ( !to || groups().contains( to ) )
+ return;
+
+ if ( d->temporary && to->type() != Group::Temporary )
+ return;
+
+ if ( d->groups.contains( Group::topLevel() ) )
+ {
+ d->groups.remove( Group::topLevel() );
+ emit removedFromGroup( this, Group::topLevel() );
+ }
+
+ d->groups.append( to );
+
+ for( Contact *c = d->contacts.first(); c ; c = d->contacts.next() )
+ c->sync(Contact::MovedBetweenGroup);
+
+ emit addedToGroup( this, to );
+}
+
+QPtrList<Group> MetaContact::groups() const
+{
+ return d->groups;
+}
+
+void MetaContact::slotContactDestroyed( Contact *contact )
+{
+ removeContact(contact,true);
+}
+
+const QDomElement MetaContact::toXML(bool minimal)
+{
+ // This causes each Kopete::Protocol subclass to serialise its contacts' data into the metacontact's plugin data and address book data
+ emit aboutToSave(this);
+
+ QDomDocument metaContact;
+ metaContact.appendChild( metaContact.createElement( QString::fromUtf8( "meta-contact" ) ) );
+ metaContact.documentElement().setAttribute( QString::fromUtf8( "contactId" ), metaContactId() );
+
+ // the custom display name, used for the custom name source
+ QDomElement displayName = metaContact.createElement( QString::fromUtf8("display-name" ) );
+ displayName.appendChild( metaContact.createTextNode( d->displayName ) );
+ metaContact.documentElement().appendChild( displayName );
+ QDomElement photo = metaContact.createElement( QString::fromUtf8("photo" ) );
+ KURL photoUrl = d->photoUrl;
+ photo.appendChild( metaContact.createTextNode( photoUrl.url() ) );
+ metaContact.documentElement().appendChild( photo );
+
+ // Property sources
+ QDomElement propertySources = metaContact.createElement( QString::fromUtf8("property-sources" ) );
+ QDomElement _nameSource = metaContact.createElement( QString::fromUtf8("name") );
+ QDomElement _photoSource = metaContact.createElement( QString::fromUtf8("photo") );
+
+ // set the contact source for display name
+ _nameSource.setAttribute(QString::fromUtf8("source"), sourceToString(displayNameSource()));
+
+ // set contact source metadata
+ if (displayNameSourceContact())
+ {
+ QDomElement contactNameSource = metaContact.createElement( QString::fromUtf8("contact-source") );
+ contactNameSource.setAttribute( NSCID_ELEM, displayNameSourceContact()->contactId() );
+ contactNameSource.setAttribute( NSPID_ELEM, displayNameSourceContact()->protocol()->pluginId() );
+ contactNameSource.setAttribute( NSAID_ELEM, displayNameSourceContact()->account()->accountId() );
+ _nameSource.appendChild( contactNameSource );
+ }
+
+ // set the contact source for photo
+ _photoSource.setAttribute(QString::fromUtf8("source"), sourceToString(photoSource()));
+
+ if( !d->metaContactId.isEmpty() )
+ photo.setAttribute( QString::fromUtf8("syncWithKABC") , QString::fromUtf8( d->photoSyncedWithKABC ? "true" : "false" ) );
+
+ if (photoSourceContact())
+ {
+ //kdDebug(14010) << k_funcinfo << "serializing photo source " << nameFromContact(photoSourceContact()) << endl;
+ // set contact source metadata for photo
+ QDomElement contactPhotoSource = metaContact.createElement( QString::fromUtf8("contact-source") );
+ contactPhotoSource.setAttribute( NSCID_ELEM, photoSourceContact()->contactId() );
+ contactPhotoSource.setAttribute( NSPID_ELEM, photoSourceContact()->protocol()->pluginId() );
+ contactPhotoSource.setAttribute( NSAID_ELEM, photoSourceContact()->account()->accountId() );
+ _photoSource.appendChild( contactPhotoSource );
+ }
+ // apend name and photo sources to property sources
+ propertySources.appendChild(_nameSource);
+ propertySources.appendChild(_photoSource);
+
+ metaContact.documentElement().appendChild(propertySources);
+
+ // Don't store these information in minimal mode.
+ if(!minimal)
+ {
+ // Store groups
+ if ( !d->groups.isEmpty() )
+ {
+ QDomElement groups = metaContact.createElement( QString::fromUtf8("groups") );
+ Group *g;
+ for ( g = d->groups.first(); g; g = d->groups.next() )
+ {
+ QDomElement group = metaContact.createElement( QString::fromUtf8("group") );
+ group.setAttribute( QString::fromUtf8("id"), g->groupId() );
+ groups.appendChild( group );
+ }
+ metaContact.documentElement().appendChild( groups );
+ }
+
+ // Store other plugin data
+ QValueList<QDomElement> pluginData = Kopete::ContactListElement::toXML();
+ for( QValueList<QDomElement>::Iterator it = pluginData.begin(); it != pluginData.end(); ++it )
+ metaContact.documentElement().appendChild( metaContact.importNode( *it, true ) );
+
+ // Store custom notification data
+ QDomElement notifyData = NotifyDataObject::notifyDataToXML();
+ if ( notifyData.hasChildNodes() )
+ metaContact.documentElement().appendChild( metaContact.importNode( notifyData, true ) );
+ }
+ return metaContact.documentElement();
+}
+
+bool MetaContact::fromXML( const QDomElement& element )
+{
+ if( !element.hasChildNodes() )
+ return false;
+
+ bool oldPhotoTracking = false;
+ bool oldNameTracking = false;
+
+ QString strContactId = element.attribute( QString::fromUtf8("contactId") );
+ if( !strContactId.isEmpty() )
+ {
+ d->metaContactId = strContactId;
+ // Set the KABC Picture
+ slotUpdateAddressBookPicture();
+ }
+
+ QDomElement contactElement = element.firstChild().toElement();
+ while( !contactElement.isNull() )
+ {
+
+ if( contactElement.tagName() == QString::fromUtf8( "display-name" ) )
+ { // custom display name, used for the custom name source
+
+ // WTF, why were we not loading the metacontact if nickname was empty.
+ //if ( contactElement.text().isEmpty() )
+ // return false;
+
+ //the replace is there to workaround the Bug 95444
+ d->displayName = contactElement.text().replace('\n',QString::fromUtf8(""));
+
+ if ( contactElement.hasAttribute(NSCID_ELEM) && contactElement.hasAttribute(NSPID_ELEM) && contactElement.hasAttribute(NSAID_ELEM))
+ {
+ oldNameTracking = true;
+ //kdDebug(14010) << k_funcinfo << "old name tracking" << endl;
+ // retrieve deprecated data (now stored in property-sources)
+ // save temporarely, we will find a Contact* with this later
+ d->nameSourceCID = contactElement.attribute( NSCID_ELEM );
+ d->nameSourcePID = contactElement.attribute( NSPID_ELEM );
+ d->nameSourceAID = contactElement.attribute( NSAID_ELEM );
+ }
+// else
+// kdDebug(14010) << k_funcinfo << "no old name tracking" << endl;
+ }
+ else if( contactElement.tagName() == QString::fromUtf8( "photo" ) )
+ {
+ // custom photo, used for custom photo source
+ setPhoto( KURL(contactElement.text()) );
+
+ d->photoSyncedWithKABC = (contactElement.attribute(QString::fromUtf8("syncWithKABC")) == QString::fromUtf8("1")) || (contactElement.attribute(QString::fromUtf8("syncWithKABC")) == QString::fromUtf8("true"));
+
+ // retrieve deprecated data (now stored in property-sources)
+ // save temporarely, we will find a Contact* with this later
+ if ( contactElement.hasAttribute(PSCID_ELEM) && contactElement.hasAttribute(PSPID_ELEM) && contactElement.hasAttribute(PSAID_ELEM))
+ {
+ oldPhotoTracking = true;
+// kdDebug(14010) << k_funcinfo << "old photo tracking" << endl;
+ d->photoSourceCID = contactElement.attribute( PSCID_ELEM );
+ d->photoSourcePID = contactElement.attribute( PSPID_ELEM );
+ d->photoSourceAID = contactElement.attribute( PSAID_ELEM );
+ }
+// else
+// kdDebug(14010) << k_funcinfo << "no old photo tracking" << endl;
+ }
+ else if( contactElement.tagName() == QString::fromUtf8( "property-sources" ) )
+ {
+ QDomNode property = contactElement.firstChild();
+ while( !property.isNull() )
+ {
+ QDomElement propertyElement = property.toElement();
+
+ if( propertyElement.tagName() == QString::fromUtf8( "name" ) )
+ {
+ QString source = propertyElement.attribute( QString::fromUtf8("source") );
+ setDisplayNameSource(stringToSource(source));
+ // find contact sources now.
+ QDomNode propertyParam = propertyElement.firstChild();
+ while( !propertyParam.isNull() )
+ {
+ QDomElement propertyParamElement = propertyParam.toElement();
+ if( propertyParamElement.tagName() == QString::fromUtf8( "contact-source" ) )
+ {
+ d->nameSourceCID = propertyParamElement.attribute( NSCID_ELEM );
+ d->nameSourcePID = propertyParamElement.attribute( NSPID_ELEM );
+ d->nameSourceAID = propertyParamElement.attribute( NSAID_ELEM );
+ }
+ propertyParam = propertyParam.nextSibling();
+ }
+ }
+ if( propertyElement.tagName() == QString::fromUtf8( "photo" ) )
+ {
+ QString source = propertyElement.attribute( QString::fromUtf8("source") );
+ setPhotoSource(stringToSource(source));
+ // find contact sources now.
+ QDomNode propertyParam = propertyElement.firstChild();
+ while( !propertyParam.isNull() )
+ {
+ QDomElement propertyParamElement = propertyParam.toElement();
+ if( propertyParamElement.tagName() == QString::fromUtf8( "contact-source" ) )
+ {
+ d->photoSourceCID = propertyParamElement.attribute( NSCID_ELEM );
+ d->photoSourcePID = propertyParamElement.attribute( NSPID_ELEM );
+ d->photoSourceAID = propertyParamElement.attribute( NSAID_ELEM );
+ }
+ propertyParam = propertyParam.nextSibling();
+ }
+ }
+ property = property.nextSibling();
+ }
+ }
+ else if( contactElement.tagName() == QString::fromUtf8( "groups" ) )
+ {
+ QDomNode group = contactElement.firstChild();
+ while( !group.isNull() )
+ {
+ QDomElement groupElement = group.toElement();
+
+ if( groupElement.tagName() == QString::fromUtf8( "group" ) )
+ {
+ QString strGroupId = groupElement.attribute( QString::fromUtf8("id") );
+ if( !strGroupId.isEmpty() )
+ addToGroup( Kopete::ContactList::self()->group( strGroupId.toUInt() ) );
+ else //kopete 0.6 contactlist
+ addToGroup( Kopete::ContactList::self()->findGroup( groupElement.text() ) );
+ }
+ else if( groupElement.tagName() == QString::fromUtf8( "top-level" ) ) //kopete 0.6 contactlist
+ addToGroup( Kopete::Group::topLevel() );
+
+ group = group.nextSibling();
+ }
+ }
+ else if( contactElement.tagName() == QString::fromUtf8( "address-book-field" ) )
+ {
+ QString app = contactElement.attribute( QString::fromUtf8( "app" ), QString::null );
+ QString key = contactElement.attribute( QString::fromUtf8( "key" ), QString::null );
+ QString val = contactElement.text();
+ d->addressBook[ app ][ key ] = val;
+ }
+ else if( contactElement.tagName() == QString::fromUtf8( "custom-notifications" ) )
+ {
+ Kopete::NotifyDataObject::notifyDataFromXML( contactElement );
+ }
+ else //if( groupElement.tagName() == QString::fromUtf8( "plugin-data" ) || groupElement.tagName() == QString::fromUtf8("custom-icons" ))
+ {
+ Kopete::ContactListElement::fromXML(contactElement);
+ }
+ contactElement = contactElement.nextSibling().toElement();
+ }
+
+ if( oldNameTracking )
+ {
+ /* if (displayNameSourceContact() ) <- doesn't work because the contact is only set up when all plugin are loaded (BUG 111956) */
+ if ( !d->nameSourceCID.isEmpty() )
+ {
+// kdDebug(14010) << k_funcinfo << "Converting old name source" << endl;
+ // even if the old tracking attributes exists, they could have been null, that means custom
+ setDisplayNameSource(SourceContact);
+ }
+ else
+ {
+ // lets do the best conversion for the old name tracking
+ // if the custom display name is the same as kabc name, set the source to kabc
+ if ( !d->metaContactId.isEmpty() && ( d->displayName == nameFromKABC(d->metaContactId)) )
+ setDisplayNameSource(SourceKABC);
+ else
+ setDisplayNameSource(SourceCustom);
+ }
+ }
+
+ if ( oldPhotoTracking )
+ {
+// kdDebug(14010) << k_funcinfo << "Converting old photo source" << endl;
+ if ( !d->photoSourceCID.isEmpty() )
+ {
+ setPhotoSource(SourceContact);
+ }
+ else
+ {
+ if ( !d->metaContactId.isEmpty() && !photoFromKABC(d->metaContactId).isNull())
+ setPhotoSource(SourceKABC);
+ else
+ setPhotoSource(SourceCustom);
+ }
+ }
+
+ // If a plugin is loaded, load data cached
+ connect( Kopete::PluginManager::self(), SIGNAL( pluginLoaded(Kopete::Plugin*) ),
+ this, SLOT( slotPluginLoaded(Kopete::Plugin*) ) );
+
+ // All plugins are already loaded, call manually the contact setting slot.
+ if( Kopete::PluginManager::self()->isAllPluginsLoaded() )
+ slotAllPluginsLoaded();
+ else
+ // When all plugins are loaded, set the source contact.
+ connect( Kopete::PluginManager::self(), SIGNAL( allPluginsLoaded() ),
+ this, SLOT( slotAllPluginsLoaded() ) );
+
+ // track changes only works if ONE Contact is inside the MetaContact
+// if (d->contacts.count() > 1) // Does NOT work as intended
+// d->trackChildNameChanges=false;
+
+// kdDebug(14010) << k_funcinfo << "END" << endl;
+ return true;
+}
+
+QString MetaContact::sourceToString(PropertySource source) const
+{
+ if ( source == SourceCustom )
+ return QString::fromUtf8("custom");
+ else if ( source == SourceKABC )
+ return QString::fromUtf8("addressbook");
+ else if ( source == SourceContact )
+ return QString::fromUtf8("contact");
+ else // recovery
+ return sourceToString(SourceCustom);
+}
+
+MetaContact::PropertySource MetaContact::stringToSource(const QString &name) const
+{
+ if ( name == QString::fromUtf8("custom") )
+ return SourceCustom;
+ else if ( name == QString::fromUtf8("addressbook") )
+ return SourceKABC;
+ else if ( name == QString::fromUtf8("contact") )
+ return SourceContact;
+ else // recovery
+ return SourceCustom;
+}
+
+QString MetaContact::addressBookField( Kopete::Plugin * /* p */, const QString &app, const QString & key ) const
+{
+ return d->addressBook[ app ][ key ];
+}
+
+void Kopete::MetaContact::setAddressBookField( Kopete::Plugin * /* p */, const QString &app, const QString &key, const QString &value )
+{
+ d->addressBook[ app ][ key ] = value;
+}
+
+void MetaContact::slotPluginLoaded( Plugin *p )
+{
+ if( !p )
+ return;
+
+ QMap<QString, QString> map= pluginData( p );
+ if(!map.isEmpty())
+ {
+ p->deserialize(this,map);
+ }
+}
+
+void MetaContact::slotAllPluginsLoaded()
+{
+ // Now that the plugins and subcontacts are loaded, set the source contact.
+ setDisplayNameSourceContact( findContact( d->nameSourcePID, d->nameSourceAID, d->nameSourceCID) );
+ setPhotoSourceContact( findContact( d->photoSourcePID, d->photoSourceAID, d->photoSourceCID) );
+}
+
+void MetaContact::slotUpdateAddressBookPicture()
+{
+ KABC::AddressBook* ab = KABCPersistence::self()->addressBook();
+ QString id = metaContactId();
+ if ( !id.isEmpty() && !id.contains(':') )
+ {
+ KABC::Addressee theAddressee = ab->findByUid(id);
+ if ( theAddressee.isEmpty() )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "no KABC::Addressee found for ( " << id << " ) " << " in current address book" << endl;
+ }
+ else
+ {
+ KABC::Picture pic = theAddressee.photo();
+ if ( pic.data().isNull() && pic.url().isEmpty() )
+ pic = theAddressee.logo();
+
+ d->kabcPicture.setPicture(pic);
+ }
+ }
+}
+
+bool MetaContact::isTemporary() const
+{
+ return d->temporary;
+}
+
+void MetaContact::setTemporary( bool isTemporary, Group *group )
+{
+ d->temporary = isTemporary;
+ Group *temporaryGroup = Group::temporary();
+ if ( d->temporary )
+ {
+ addToGroup (temporaryGroup);
+ Group *g;
+ for( g = d->groups.first(); g; g = d->groups.next() )
+ {
+ if(g != temporaryGroup)
+ removeFromGroup(g);
+ }
+ }
+ else
+ moveToGroup(temporaryGroup, group ? group : Group::topLevel());
+}
+
+QString MetaContact::metaContactId() const
+{
+ if(d->metaContactId.isEmpty())
+ {
+ Contact *c=d->contacts.first();
+ if(!c)
+ return QString::null;
+ return c->protocol()->pluginId()+QString::fromUtf8(":")+c->account()->accountId()+QString::fromUtf8(":") + c->contactId() ;
+ }
+ return d->metaContactId;
+}
+
+void MetaContact::setMetaContactId( const QString& newMetaContactId )
+{
+ if(newMetaContactId == d->metaContactId)
+ return;
+
+ // 1) Check the Id is not already used by another contact
+ // 2) cause a kabc write ( only in response to metacontactLVIProps calling this, or will
+ // write be called twice when creating a brand new MC? )
+ // 3) What about changing from one valid kabc to another, are kabc fields removed?
+ // 4) May be called with Null to remove an invalid kabc uid by KMC::toKABC()
+ // 5) Is called when reading the saved contact list
+
+ // Don't remove IM addresses from kabc if we are changing contacts;
+ // other programs may have written that data and depend on it
+ d->metaContactId = newMetaContactId;
+ KABCPersistence::self()->write( this );
+ emit onlineStatusChanged( this, d->onlineStatus );
+ emit persistentDataChanged();
+}
+
+bool MetaContact::isPhotoSyncedWithKABC() const
+{
+ return d->photoSyncedWithKABC;
+}
+
+void MetaContact::setPhotoSyncedWithKABC(bool b)
+{
+ d->photoSyncedWithKABC=b;
+ if(b)
+ {
+ QVariant newValue;
+
+ switch( photoSource() )
+ {
+ case SourceContact:
+ {
+ Contact *source = photoSourceContact();
+ if(source != 0L)
+ newValue = source->property( Kopete::Global::Properties::self()->photo() ).value();
+ break;
+ }
+ case SourceCustom:
+ {
+ if( !d->customPicture.isNull() )
+ newValue = d->customPicture.path();
+ break;
+ }
+ // Don't sync the photo with KABC if the source is KABC !
+ default:
+ return;
+ }
+
+ if ( !d->metaContactId.isEmpty() && !newValue.isNull())
+ {
+ KABC::Addressee theAddressee = KABCPersistence::self()->addressBook()->findByUid( metaContactId() );
+
+ if ( !theAddressee.isEmpty() )
+ {
+ QImage img;
+ if(newValue.canCast( QVariant::Image ))
+ img=newValue.toImage();
+ else if(newValue.canCast( QVariant::Pixmap ))
+ img=newValue.toPixmap().convertToImage();
+
+ if(img.isNull())
+ {
+ // Some protocols like MSN save the photo as a url in
+ // contact properties, we should not use this url
+ // to sync with kabc but try first to embed the
+ // photo data in the kabc addressee, because it could
+ // be remote resource and the local url makes no sense
+ QImage fallBackImage = QImage(newValue.toString());
+ if(fallBackImage.isNull())
+ theAddressee.setPhoto(newValue.toString());
+ else
+ theAddressee.setPhoto(fallBackImage);
+ }
+ else
+ theAddressee.setPhoto(img);
+
+ KABCPersistence::self()->addressBook()->insertAddressee(theAddressee);
+ KABCPersistence::self()->writeAddressBook( theAddressee.resource() );
+ }
+ }
+ }
+}
+
+QPtrList<Contact> MetaContact::contacts() const
+{
+ return d->contacts;
+}
+} //END namespace Kopete
+
+#include "kopetemetacontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemetacontact.h b/kopete/libkopete/kopetemetacontact.h
new file mode 100644
index 00000000..3bdaa33a
--- /dev/null
+++ b/kopete/libkopete/kopetemetacontact.h
@@ -0,0 +1,615 @@
+/*
+ kopetemetacontact.h - Kopete Meta Contact
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef kopetemetacontact_h__
+#define kopetemetacontact_h__
+
+#include "kopetecontactlistelement.h"
+#include <qptrlist.h>
+#include <qstring.h>
+
+#include <kdemacros.h>
+#include "kopete_export.h"
+
+#include "kopetenotifydataobject.h"
+#include "kopetecontactlistelement.h"
+#include "kopeteonlinestatus.h"
+
+class QDomNode;
+
+class KURL;
+
+namespace Kopete {
+
+
+class Plugin;
+class Group;
+class Picture;
+
+/**
+ * @author Will Stephenson <will@stevello.free-online.co.uk>
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ * @author Olivier Goffart <ogoffart@tiscalinet.be>
+ *
+ * A metacontact represent a person. This is a kind of entry to
+ * the contactlist. All information of a contact is contained in
+ * the metacontact. Plugins can store data in it with all
+ * @ref ContactListElement methods
+ */
+class KOPETE_EXPORT MetaContact : public ContactListElement, public NotifyDataObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY( QString displayName READ displayName WRITE setDisplayName )
+ Q_PROPERTY( QString statusString READ statusString )
+ Q_PROPERTY( QString statusIcon READ statusIcon )
+ Q_PROPERTY( bool isOnline READ isOnline )
+ Q_PROPERTY( bool isReachable READ isReachable )
+ Q_PROPERTY( bool isTemporary READ isTemporary )
+ Q_PROPERTY( bool canAcceptFiles READ canAcceptFiles )
+ //Q_PROPERTY( ulong idleTime READ idleTime )
+ Q_PROPERTY( QString metaContactId READ metaContactId WRITE setMetaContactId )
+ Q_PROPERTY( bool photoSyncedWithKABC READ isPhotoSyncedWithKABC WRITE setPhotoSyncedWithKABC )
+
+public:
+ /**
+ * Enumeration of possible sources for a property (which may be
+ * photos, see setPhotoSource() for instance).
+ */
+ enum PropertySource {
+ SourceContact /**< Data comes from the contact itself. */,
+ SourceKABC /**< Data comes from KABC (addressbook). */,
+ SourceCustom /**< Data comes from somewhere else. */
+ };
+
+ /**
+ * constructor
+ */
+ MetaContact();
+ /**
+ * destructor
+ */
+ ~MetaContact();
+
+ /**
+ * @brief Returns this metacontact's ID.
+ *
+ * Every metacontact has a unique id, set by when creating the contact, or reading the contactlist
+ * TODO: make it real
+ */
+ QString metaContactId() const;
+
+ /**
+ * @brief Add or change the link to a KDE addressbook (KABC) Addressee.
+ * FIXME: Use with care. You could create 1 to many relationships with the current implementation
+ */
+ void setMetaContactId( const QString& newMetaContactId );
+
+ /**
+ * @brief Retrieve the list of contacts that are part of the meta contact
+ */
+ QPtrList<Contact> contacts() const;
+
+ /**
+ * @brief The groups the contact is stored in
+ */
+ QPtrList<Group> groups() const;
+
+ /**
+ * Find the Contact to a given contact. If contact
+ * is not found, a null pointer is returned.
+ * if @p protocolId or @p accountId are null, it is searched over all protocols/accounts
+ */
+ Contact *findContact( const QString &protocolId, const QString &accountId, const QString &contactId );
+
+ /**
+ * @brief Set the source of metacontact displayName
+ *
+ * This method selects the display name source for one
+ * of the sources defined in @ref PropertySource
+ *
+ * @see PropertySource
+ */
+ void setDisplayNameSource(PropertySource source);
+
+ /**
+ * @brief get the source of metacontact display name
+ *
+ * This method obtains the current name source for one
+ * of the sources defined in @ref PropertySource
+ *
+ * @see PropertySource
+ */
+ PropertySource displayNameSource() const;
+
+ /**
+ * @brief Set the source of metacontact photo
+ *
+ * This method selects the photo source for one
+ * of the sources defined in @ref PropertySource
+ *
+ * @see PropertySource
+ */
+ void setPhotoSource(PropertySource source);
+
+ /**
+ * @brief get the source of metacontact photo
+ *
+ * This method obtains the current photo source for one
+ * of the sources defined in @ref PropertySource
+ *
+ * @see PropertySource
+ */
+ PropertySource photoSource() const;
+
+ /**
+ * @brief the display name showed in the contactlist window
+ *
+ * The displayname is the name which should be shown almost everywere to
+ * represent the metacontact. (in the contactlist, in the chatwindow, ....)
+ *
+ * This is a kind of alias, set by the kopete user, as opposed to a nickname
+ * set by the contact itself.
+ *
+ * If the protocol support alias serverside, the metacontact displayname
+ * should probably be syncronized with the alias on the server.
+ *
+ * This displayName is obtained from the source set with @ref setDisplayNameSource
+ */
+ QString displayName() const;
+
+ /**
+ * @brief the photo showed in the contactlist window
+ *
+ * Returns a image for the metacontact. If the metacontact photo source is
+ * the KDE addressbook. it will return the picture stored in the addressbook
+ * It can also use a subcontact as the photo source.
+ *
+ * This photo is obtained from the source set with @ref setPhotoSource
+ */
+ QImage photo() const;
+
+ /**
+ * Return the correct Kopete::Picture object depending of the metacontact photo source.
+ *
+ * This photo is obtained from the source set with @ref setPhotoSource
+ *
+ * KDE4 TODO: Rename this to photo() and use the new object.
+ */
+ Picture &picture() const;
+
+ /**
+ * @brief Set the custom displayName.
+ *
+ * This display name is used when name source is Custom
+ * this metohd may emit @ref displayNameChanged signal.
+ * And will call @ref Kopete::Contact::sync
+ *
+ * @see displayName()
+ * @see displayNameSource()
+ */
+ void setDisplayName( const QString &name );
+
+ /**
+ * @brief Returns the custom display name
+ *
+ * @see displayName()
+ * @see displayNameSource()
+ */
+ QString customDisplayName() const;
+
+ /**
+ * @brief Returns the custom display photo
+ *
+ * @see photo()
+ * @see photoSource()
+ */
+ KURL customPhoto() const;
+
+
+ /**
+ * @brief Set the custom photo.
+ *
+ * This photo is used when photo source is set toCustom
+ * this metohd may emit @ref photoChanged signal.
+ *
+ * @see photo()
+ * @see photoSource()
+ */
+ void setPhoto( const KURL &url );
+
+ /**
+ * @brief get the subcontact being tracked for its displayname (null if not set)
+ *
+ * The MetaContact will adjust its displayName() every time the
+ * "nameSource" changes its nickname property.
+ */
+ Contact *displayNameSourceContact() const;
+
+ /**
+ * @brief set the subcontact whose name is to be tracked (set to null to disable tracking)
+ * @see nameSource
+ */
+ void setDisplayNameSourceContact( Contact* contact );
+
+ /**
+ * @brief get the subcontact being tracked for its photo
+ */
+ Contact *photoSourceContact() const;
+
+ /**
+ * @brief set the subcontact to use for SourceContact source
+ */
+ void setPhotoSourceContact( Contact* contact );
+
+ /**
+ * @return true if when a subcontact change his photo, the photo will be set to the kabc contact.
+ */
+ bool isPhotoSyncedWithKABC() const;
+
+ /**
+ * Set if the photo should be synced with the adressbook when the photosource change his photo
+ *
+ * If \p b is true, the photo will be synced immediatly if possible
+ */
+ void setPhotoSyncedWithKABC(bool b);
+
+
+ /**
+ * Temporary contacts will not be serialized.
+ * If they are added to the contactlist, they appears in a special "Not in your contactlist" group.
+ * (the @ref Group::temporary group)
+ */
+ bool isTemporary() const;
+
+ /**
+ * @brief Add a contact which has just been deserialised to the meta contact
+ * @param c The Contact being added
+ */
+ void addContact( Contact *c );
+
+ /**
+ * @brief remove the contact from this metacontact
+ *
+ * set 'deleted' to true if the Contact is already deleted
+ *
+ * @param c is the contact to remove
+ * @param deleted : if it is false, it will disconnect the old contact, and call some method.
+ */
+ void removeContact( Contact *c , bool deleted = false );
+
+ /**
+ * @return the preferred child Contact for communication, or 0 if none is suitable (all unreachable).
+ */
+ Contact *preferredContact();
+
+ /**
+ * @brief The name of the icon associated with the contact's status
+ * @todo improve with OnlineStatus
+ */
+ QString statusIcon() const;
+
+ /**
+ * @brief The status string of the contact
+ *
+ * @see @ref status()
+ * @todo improve with OnlineStatus
+ */
+ QString statusString() const;
+
+ /**
+ * Returns whether this contact can be reached online for at least one
+ * FIXME: Make that an enum, because status can be unknown for certain
+ * protocols
+ */
+ bool isOnline() const;
+
+ /**
+ * Returns whether this contact can accept files
+ * @return True if the user is online with a file capable protocol, false otherwise
+ */
+ bool canAcceptFiles() const;
+
+ /**
+ * Return a more fine-grained status.
+ * Online means at least one sub-contact is online, away means at least
+ * one is away, but nobody is online and offline speaks for itself
+ */
+ OnlineStatus::StatusType status() const;
+
+ /**
+ * Like isOnline, but returns true even if the contact is not online, but
+ * can be reached trough offline-messages.
+ * it it return false, you are unable to open a chatwindow
+ * @todo : Here too, use preference order, not append order!
+ * @todo : Here too an enum.
+ */
+ bool isReachable() const;
+
+ /**
+ * return the time in second the contact is idle.
+ */
+ unsigned long int idleTime() const;
+
+ /**
+ * Return a XML representation of the metacontact
+ * @internal
+ * @param minimal When true, it doesn't save the
+ * plugins, groups and notification data. False by default.
+ */
+ const QDomElement toXML(bool minimal = false);
+
+ /**
+ * Creates a metacontact from XML
+ * Return value of false indicated that
+ * creation failed and this contact should be
+ * discarded.
+ * @internal
+ */
+ bool fromXML( const QDomElement& cnode );
+
+ /**
+ * Get or set a field for the KDE address book backend. Fields not
+ * registered during the call to Plugin::addressBookFields()
+ * cannot be altered!
+ *
+ * @param p The Plugin by which uses this field
+ * @param app refers to the application id in the libkabc database.
+ * This should be a standardized format to make sense in the address
+ * book in the first place - if you could use "" as application
+ * then probably you should use the plugin data API instead of the
+ * address book fields.
+ * @param key The name of the address book field to get or set
+ *
+ * @todo: In the code the requirement that fields are registered first
+ * is already lifted, but the API needs some review before we
+ * can remove it here too.
+ * Probably it requires once more some rewrites to get it working
+ * properly :( - Martijn
+ */
+ QString addressBookField( Plugin *p, const QString &app, const QString &key ) const;
+
+ /**
+ * @brief set an address book field
+ *
+ * @see also @ref addressBookField()
+ * @param p The Plugin by which uses this field
+ * @param app The application ID in the KABC database
+ * @param key The name of the address book field to set
+ * @param value The value of the address book field to set
+ */
+ void setAddressBookField( Plugin *p, const QString &app, const QString &key, const QString &value );
+
+public slots:
+
+ /**
+ * @brief Send a file to this metacontact
+ *
+ * This is the MetaContact level slot for sending files. It may be called through the
+ * "Send File" entry in the GUI, or over DCOP. If the function is called through the GUI,
+ * no parameters are sent and they assume default values. This slot calls the slotSendFile
+ * with identical params of the highest ranked contact capable of sending files (if any)
+ *
+ * @param sourceURL The actual KURL of the file you are sending
+ * @param altFileName (Optional) An alternate name for the file - what the receiver will see
+ * @param fileSize (Optional) Size of the file being sent. Used when sending a nondeterminate
+ * file size (such as over a socket)
+ *
+ */
+ void sendFile( const KURL &sourceURL, const QString &altFileName = QString::null,
+ unsigned long fileSize = 0L );
+signals:
+ /**
+ * This metaContact is going to be saved to the contactlist. Plugins should
+ * connect to this signal to update data with setPluginData()
+ */
+ void aboutToSave( Kopete::MetaContact *metaContact );
+
+ /**
+ * One of the subcontacts' idle status has changed. As with online status,
+ * this can occur without the metacontact changing idle state
+ */
+ void contactIdleStateChanged( Kopete::Contact *contact );
+
+
+public slots:
+
+ /**
+ * @brief Move a contact from one group to another.
+ */
+ void moveToGroup( Kopete::Group *from, Kopete::Group *to );
+
+ /**
+ * @brief Remove a contact from one group
+ */
+ void removeFromGroup( Kopete::Group *from );
+
+ /**
+ * @brief Add a contact to another group.
+ */
+ void addToGroup( Kopete::Group *to );
+
+ /**
+ * @brief Set if this is a temporary contact. (see @ref isTemporary)
+ *
+ * @param b if the contact is or not temporary
+ * @param group if the contact was temporary and b is false, then the contact will be moved to this group.
+ * if group is null, it will be moved to top-level
+ */
+ void setTemporary( bool b = true, Kopete::Group *group = 0L );
+
+ /**
+ * @brief Contact another user.
+ *
+ * Depending on the config settings, call sendMessage() or
+ * startChat()
+ *
+ * returns the Contact that was chosen as the preferred
+ */
+ Contact *execute();
+
+ /**
+ * @brief Send a single message, classic ICQ style.
+ *
+ * The actual sending is done by the Contact, but the meta contact
+ * does the GUI side of things.
+ * This is a slot to allow being called easily from e.g. a GUI.
+ *
+ * returns the Contact that was chosen as the preferred
+ */
+ Contact *sendMessage();
+
+ /**
+ * @brief Start a chat in a persistent chat window
+ *
+ * Like sendMessage, but this time a full-blown chat will be opened.
+ * Most protocols can't distinguish between the two and are either
+ * completely session based like MSN or completely message based like
+ * ICQ the only true difference is the GUI shown to the user.
+ *
+ * returns the Contact that was chosen as the preferred
+ */
+ Contact *startChat();
+
+signals:
+ /**
+ * @brief The MetaContact online status changed
+ */
+ void onlineStatusChanged( Kopete::MetaContact *contact, Kopete::OnlineStatus::StatusType status );
+
+ /**
+ * @brief A contact's online status changed
+ *
+ * this signal differs from @ref onlineStatusChanged because a contact can
+ * change his status without changing MetaContact status. It is mainly used to update the small icons
+ * in the contactlist
+ */
+ void contactStatusChanged( Kopete::Contact *contact, const Kopete::OnlineStatus &status );
+
+ /**
+ * @brief The meta contact's display name changed
+ */
+ void displayNameChanged( const QString &oldName, const QString &newName );
+
+ /**
+ * @brief The meta contact's photo changed
+ */
+ void photoChanged();
+
+ /**
+ * @brief The contact was moved
+ */
+ void movedToGroup( Kopete::MetaContact *contact, Kopete::Group *from, Kopete::Group *to );
+
+ /**
+ * @brief The contact was removed from group
+ */
+ void removedFromGroup( Kopete::MetaContact *contact, Kopete::Group *group );
+
+ /**
+ * @brief The contact was added to another group
+ */
+ void addedToGroup( Kopete::MetaContact *contact, Kopete::Group *to );
+
+ /**
+ * @brief a contact has been added into this metacontact
+ *
+ * This signal is emitted when a contact is added to this metacontact
+ */
+ void contactAdded( Kopete::Contact *c );
+
+ /**
+ * @brief a contact has been removed from this metacontact
+ *
+ * This signal is emitted when a contact is removed from this metacontact
+ */
+ void contactRemoved( Kopete::Contact *c );
+
+ /**
+ * Some part of this object's persistent data (as returned by toXML) has changed.
+ */
+ void persistentDataChanged( );
+
+private slots:
+ /**
+ * Update the contact's online status and emit onlineStatusChanged
+ * when appropriate
+ */
+ void updateOnlineStatus();
+
+ /**
+ * One of the child contact's online status changed
+ */
+ void slotContactStatusChanged( Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus );
+
+ /**
+ * One of the child contact's property changed
+ */
+ void slotPropertyChanged( Kopete::Contact *contact, const QString &key, const QVariant &oldValue, const QVariant &newValue );
+
+ /**
+ * A child contact was deleted, remove it from the list, if it's still
+ * there
+ */
+ void slotContactDestroyed( Kopete::Contact* );
+
+ /**
+ * If a plugin is loaded, maybe data about this plugin are already cached in the metacontact
+ */
+ void slotPluginLoaded( Kopete::Plugin *plugin );
+
+ /**
+ * When all the plugins are loaded, set the Contact Source.
+ */
+ void slotAllPluginsLoaded();
+
+ /**
+ * Update the KABC Picture when the addressbook is changed.
+ */
+ void slotUpdateAddressBookPicture();
+
+protected:
+ //QImage photoFromContact( Kopete::Contact *c) const;
+ //QImage photoFromKABC( const QString &id ) const;
+ QImage photoFromCustom() const;
+ //QString nameFromContact( Kopete::Contact *c) const;
+ //QString nameFromKABC( const QString &id ) const;
+
+ QString sourceToString(PropertySource source) const;
+ PropertySource stringToSource(const QString &name) const;
+private:
+ class Private;
+ Private *d;
+};
+
+// util functions shared with metacontact property dialog
+KOPETE_EXPORT QImage photoFromContact( Kopete::Contact *c) /*const*/;
+KOPETE_EXPORT QImage photoFromKABC( const QString &id ) /*const*/;
+KOPETE_EXPORT QString nameFromContact( Kopete::Contact *c) /*const*/;
+KOPETE_EXPORT QString nameFromKABC( const QString &id ) /*const*/;
+
+} //END namespace Kopete
+
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetemimesourcefactory.cpp b/kopete/libkopete/kopetemimesourcefactory.cpp
new file mode 100644
index 00000000..a34d8aee
--- /dev/null
+++ b/kopete/libkopete/kopetemimesourcefactory.cpp
@@ -0,0 +1,175 @@
+/*
+ kopetemimesourcefactory.cpp - Kopete mime source factory
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemimesourcefactory.h"
+
+#include "kopeteaccountmanager.h"
+#include "kopetecontactlist.h"
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+
+#include <qdragobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+namespace Kopete
+{
+
+class MimeSourceFactory::Private
+{
+public:
+ Private() : lastMimeSource( 0 ) {}
+ ~Private() { delete lastMimeSource; }
+ mutable QMimeSource *lastMimeSource;
+};
+
+MimeSourceFactory::MimeSourceFactory()
+ : d( new Private )
+{
+}
+
+MimeSourceFactory::~MimeSourceFactory()
+{
+ delete d;
+}
+
+const QMimeSource *MimeSourceFactory::data( const QString &abs_name ) const
+{
+ // flag used to signal something went wrong when creating a mimesource
+ bool completed = false;
+ // extract and decode arguments
+ QStringList parts = QStringList::split( QChar(':'), abs_name );
+ for ( QStringList::Iterator it = parts.begin(); it != parts.end(); ++it )
+ *it = KURL::decode_string( *it );
+
+ QPixmap img;
+ if ( parts[0] == QString::fromLatin1("kopete-contact-icon") )
+ {
+ if ( parts.size() >= 4 )
+ {
+ Account *account = AccountManager::self()->findAccount( parts[1], parts[2] );
+ if ( account )
+ {
+ Contact *contact = account->contacts()[ parts[3] ];
+ if ( contact )
+ {
+ img = contact->onlineStatus().iconFor( contact );
+ completed = true;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-contact-icon: contact not found" << endl;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-contact-icon: account not found" << endl;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-contact-icon: insufficient information in abs_name: " << parts << endl;
+ }
+
+ if ( parts[0] == QString::fromLatin1("kopete-account-icon") )
+ {
+ if ( parts.size() >= 3 )
+ {
+ Account *account = AccountManager::self()->findAccount( parts[1], parts[2] );
+ if ( account )
+ {
+ img = account->myself()->onlineStatus().iconFor( account->myself() );
+ completed = true;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-account-icon: account not found" << endl;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-account-icon: insufficient information in abs_name: " << parts << endl;
+ }
+
+ if ( parts[0] == QString::fromLatin1("kopete-metacontact-icon") )
+ {
+ if ( parts.size() >= 2 )
+ {
+ MetaContact *mc = ContactList::self()->metaContact( parts[1] );
+ if ( mc )
+ {
+ img = SmallIcon( mc->statusIcon() );
+ completed = true;
+ }
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-metacontact-icon: insufficient information in abs_name: " << parts << endl;
+ }
+
+ if ( parts[0] == QString::fromLatin1("kopete-metacontact-photo") )
+ {
+ if ( parts.size() >= 2 )
+ {
+ MetaContact *mc = ContactList::self()->metaContact( parts[1] );
+ if ( mc )
+ {
+ QImage photo = mc->photo();
+ delete d->lastMimeSource;
+ d->lastMimeSource = new QImageDrag( photo );
+ return d->lastMimeSource;
+ }
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-metacontact-photo: insufficient information in abs_name: " << parts << endl;
+ }
+
+ if ( parts[0] == QString::fromLatin1("kopete-onlinestatus-icon") )
+ {
+ if ( parts.size() >= 2 )
+ {
+ /*
+ * We are using a dirty trick here: this mime source is supposed to return the
+ * icon for an arbitrary KOS instance. To do this, the caller needs to ask
+ * the KOS for the mime source key first, which also ensures the icon is
+ * currently in the cache. The cache is global, so we just need to find any
+ * existing KOS instance to return us the rendered icon from the cache.
+ * To find a valid KOS, we ask Kopete's account manager to locate an existing
+ * account. We'll use the myself() instance of that account to reference its
+ * current KOS object, which in turn has access to the global KOS icon cache.
+ * Note that if the cache has been invalidated in the meantime, we'll just
+ * get an empty pixmap back.
+ */
+ Account *account = AccountManager::self()->accounts().getFirst();
+ if ( account )
+ {
+ img = account->myself()->onlineStatus().iconFor( parts[1] );
+ completed = true;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-onlinestatus-icon: no active account found" << endl;
+ }
+ else
+ kdDebug( 14010 ) << k_funcinfo << "kopete-onlinestatus-icon: insufficient information in abs_name: " << parts << endl;
+ }
+
+ delete d->lastMimeSource;
+ if ( completed )
+ d->lastMimeSource = new QImageDrag( img.convertToImage() );
+ else
+ d->lastMimeSource = 0;
+ return d->lastMimeSource;
+}
+
+} // END namespace Kopete
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemimesourcefactory.h b/kopete/libkopete/kopetemimesourcefactory.h
new file mode 100644
index 00000000..76c2f188
--- /dev/null
+++ b/kopete/libkopete/kopetemimesourcefactory.h
@@ -0,0 +1,55 @@
+/*
+ kopetemimesourcefactory.h - Kopete mime source factory
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMIMESOURCEFACTORY_H
+#define KOPETEMIMESOURCEFACTORY_H
+
+#include <qmime.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+/**
+ * @brief A mime source factory for providing kopete's various icons for labels and tooltips
+ *
+ * The following 'protocols' are supported, and provide appropriate icons for
+ * various situations:
+ * kopete-contact-icon:\<protocolId\>:\<accountId\>:\<contactId\>
+ * kopete-account-icon:\<protocolId\>:\<accountId\>
+ * kopete-metacontact-icon:\<metaContactId\>
+ * Note that the various id strings should be URL-encoded (with, for instance,
+ * KURL::encode_string) if they might contain colons.
+ */
+class KOPETE_EXPORT MimeSourceFactory : public QMimeSourceFactory
+{
+public:
+ MimeSourceFactory();
+ ~MimeSourceFactory();
+
+ const QMimeSource *data( const QString &abs_name ) const;
+
+private:
+ class Private;
+ Private *d;
+};
+
+} // Kopete
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemimetypehandler.cpp b/kopete/libkopete/kopetemimetypehandler.cpp
new file mode 100644
index 00000000..04c4939b
--- /dev/null
+++ b/kopete/libkopete/kopetemimetypehandler.cpp
@@ -0,0 +1,215 @@
+/*
+ kopetemimetypehandler.cpp - Kopete mime type handlers
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemimetypehandler.h"
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+
+#include <qwidget.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/netaccess.h>
+#include <kmimetype.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <kstandarddirs.h>
+#include <ktar.h>
+
+namespace Kopete
+{
+
+namespace
+{
+ static QDict<Kopete::MimeTypeHandler> g_mimeHandlers;
+ static QDict<Kopete::MimeTypeHandler> g_protocolHandlers;
+}
+
+class MimeTypeHandler::Private
+{
+public:
+ Private( bool carf ) : canAcceptRemoteFiles( carf ) {}
+ bool canAcceptRemoteFiles;
+ QStringList mimeTypes;
+ QStringList protocols;
+};
+
+MimeTypeHandler::MimeTypeHandler( bool canAcceptRemoteFiles )
+ : d( new Private( canAcceptRemoteFiles ) )
+{
+}
+
+MimeTypeHandler::~MimeTypeHandler()
+{
+ for( QStringList::iterator it = d->mimeTypes.begin(); it != d->mimeTypes.end(); ++it )
+ g_mimeHandlers.remove( *it );
+
+ for( QStringList::iterator it = d->protocols.begin(); it != d->protocols.end(); ++it )
+ g_protocolHandlers.remove( *it );
+
+ delete d;
+}
+
+bool MimeTypeHandler::registerAsMimeHandler( const QString &mimeType )
+{
+ if( g_mimeHandlers[ mimeType ] )
+ {
+ kdWarning(14010) << k_funcinfo << "Warning: Two mime type handlers attempting"
+ " to handle " << mimeType << endl;
+ return false;
+ }
+
+ g_mimeHandlers.insert( mimeType, this );
+ d->mimeTypes.append( mimeType );
+// kdDebug(14010) << k_funcinfo << "Mime type " << mimeType << " registered" << endl;
+ return true;
+}
+
+bool MimeTypeHandler::registerAsProtocolHandler( const QString &protocol )
+{
+ if( g_protocolHandlers[ protocol ] )
+ {
+ kdWarning(14010) << k_funcinfo << "Warning: Two protocol handlers attempting"
+ " to handle " << protocol << endl;
+ return false;
+ }
+
+ g_protocolHandlers.insert( protocol, this );
+ d->protocols.append( protocol );
+ kdDebug(14010) << k_funcinfo << "Mime type " << protocol << " registered" << endl;
+ return true;
+}
+
+const QStringList MimeTypeHandler::mimeTypes() const
+{
+ return d->mimeTypes;
+}
+
+const QStringList MimeTypeHandler::protocols() const
+{
+ return d->protocols;
+}
+
+bool MimeTypeHandler::canAcceptRemoteFiles() const
+{
+ return d->canAcceptRemoteFiles;
+}
+
+bool MimeTypeHandler::dispatchURL( const KURL &url )
+{
+ if( url.isEmpty() )
+ return false;
+
+ QString type = KMimeType::findByURL( url )->name();
+
+ MimeTypeHandler *mimeHandler = g_mimeHandlers[ type ];
+
+ if( mimeHandler )
+ {
+ return dispatchToHandler( url, type, mimeHandler );
+ }
+ else
+ {
+ mimeHandler = g_protocolHandlers[ url.protocol() ];
+
+ if( mimeHandler )
+ {
+ mimeHandler->handleURL( url );
+ return true;
+ }
+ else
+ {
+ kdDebug(14010) << "No mime type handler can handle this URL: " << url.prettyURL() << endl;
+ return false;
+ }
+ }
+}
+
+bool MimeTypeHandler::dispatchToHandler( const KURL &url, const QString &mimeType, MimeTypeHandler *handler )
+{
+ if( !handler->canAcceptRemoteFiles() )
+ {
+ QString file;
+ if( !KIO::NetAccess::download( url, file, Kopete::UI::Global::mainWidget() ) )
+ {
+ QString sorryText;
+ if ( url.isLocalFile() )
+ {
+ sorryText = i18n( "Unable to find the file %1." );
+ }
+ else
+ {
+ sorryText = i18n( "<qt>Unable to download the requested file;<br>"
+ "please check that address %1 is correct.</qt>" );
+ }
+
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ sorryText.arg( url.prettyURL() ) );
+ return false;
+ }
+
+ KURL dest;
+ dest.setPath( file );
+
+ if( !mimeType.isNull() )
+ handler->handleURL( mimeType, dest );
+ else
+ handler->handleURL( dest );
+
+ // for now, local-only handlers have to be synchronous
+ KIO::NetAccess::removeTempFile( file );
+ }
+ else
+ {
+ if( !mimeType.isNull() )
+ handler->handleURL( mimeType, url );
+ else
+ handler->handleURL( url );
+ }
+
+ return true;
+}
+
+void MimeTypeHandler::handleURL( const KURL &url ) const
+{
+ Q_UNUSED( url );
+}
+
+void MimeTypeHandler::handleURL( const QString &mimeType, const KURL &url ) const
+{
+ Q_UNUSED( mimeType );
+ Q_UNUSED( url );
+}
+
+
+EmoticonMimeTypeHandler::EmoticonMimeTypeHandler()
+ : MimeTypeHandler( false )
+{
+ registerAsMimeHandler( QString::fromLatin1("application/x-kopete-emoticons") );
+ registerAsMimeHandler( QString::fromLatin1("application/x-tgz") );
+ registerAsMimeHandler( QString::fromLatin1("application/x-tbz") );
+}
+
+void EmoticonMimeTypeHandler::handleURL( const QString &, const KURL &url ) const
+{
+ Global::installEmoticonTheme( url.path() );
+}
+
+} // END namespace Kopete
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetemimetypehandler.h b/kopete/libkopete/kopetemimetypehandler.h
new file mode 100644
index 00000000..8f3235f0
--- /dev/null
+++ b/kopete/libkopete/kopetemimetypehandler.h
@@ -0,0 +1,133 @@
+/*
+ kopetemimetypehandler.h - Kopete Mime-type Handlers
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMIMETYPEHANDLER_H
+#define KOPETEMIMETYPEHANDLER_H
+
+class KURL;
+class QString;
+class QStringList;
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+/**
+ * @brief A handler for some set of mime-types
+ * A mime type handler is responsible for handling requests to open files of
+ * certain mime types presented to the main application.
+ */
+class KOPETE_EXPORT MimeTypeHandler
+{
+protected:
+ MimeTypeHandler( bool canAcceptRemoteFiles = false );
+public:
+ virtual ~MimeTypeHandler();
+
+ /**
+ * Finds a MimeTypeHandler for a given URL, and tells that handler to handle it
+ *
+ * @param url the url to dispatch
+ *
+ * @return true if a handler was registered for the mime type, false otherwise
+ */
+ static bool dispatchURL( const KURL &url );
+
+ /**
+ * Returns a list of mime types this object is registered to handle
+ */
+ const QStringList mimeTypes() const;
+
+ /**
+ * Returns a list of protocols this object is registered to handle
+ */
+ const QStringList protocols() const;
+
+ /**
+ * Returns true if this handler can accept remote files direcltly;
+ * If false, remote files are downloaded via KIO::NetAccess before
+ * being passed to handleURL
+ */
+ bool canAcceptRemoteFiles() const;
+
+ /**
+ * Handles the URL @p url
+ *
+ * @param url The url to handle
+ */
+ virtual void handleURL( const KURL &url ) const;
+
+ /**
+ * Handles the URL @p url, which has the mime type @p mimeType
+ *
+ * @param mimeType The mime type of the URL
+ * @param url The url to handle
+ */
+ virtual void handleURL( const QString &mimeType, const KURL &url ) const;
+
+protected:
+ /**
+ * Register this object as the handler of type @p mimeType.
+ * @param mimeType the mime type to handle
+ * @return true if registration succeeded, false if another handler is
+ * already set for this mime type.
+ */
+ bool registerAsMimeHandler( const QString &mimeType );
+
+ /**
+ * Register this object as the handler of type @p protocol.
+ * @param protocol the protocol to handle
+ * @return true if registration succeeded, false if another handler is
+ * already set for this protocol.
+ */
+ bool registerAsProtocolHandler( const QString &protocol );
+
+private:
+ /**
+ * Helper function.
+ * Attempts to dispatch a given URL to a given handler
+ *
+ * @param url The url to dispatch
+ * @param mimeType The mime type of the url
+ * @param handler The handler to attempt
+ *
+ * @return true if a handler was able to process the URL, false otherwise
+ */
+ static bool dispatchToHandler( const KURL &url, const QString &mimeType, MimeTypeHandler *handler );
+
+ class Private;
+ Private *d;
+};
+
+/**
+ * Mime-type handler class for Kopete emoticon files
+ */
+class KOPETE_EXPORT EmoticonMimeTypeHandler : public MimeTypeHandler
+{
+public:
+ EmoticonMimeTypeHandler();
+
+ const QStringList mimeTypes() const;
+
+ void handleURL( const QString &mimeType, const KURL &url ) const;
+};
+
+} // Kopete
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetenotifydataobject.cpp b/kopete/libkopete/kopetenotifydataobject.cpp
new file mode 100644
index 00000000..9a0de544
--- /dev/null
+++ b/kopete/libkopete/kopetenotifydataobject.cpp
@@ -0,0 +1,152 @@
+/*
+ kopetenotifydataobject.cpp - Container for notification events
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdom.h>
+#include <kdebug.h>
+#include "kopetenotifydataobject.h"
+#include "kopetenotifyevent.h"
+
+class Kopete::NotifyDataObject::Private
+{
+public:
+ QDict<Kopete::NotifyEvent> events;
+};
+
+Kopete::NotifyDataObject::NotifyDataObject()
+{
+ d = new Private();
+ d->events.setAutoDelete( true );
+}
+
+Kopete::NotifyDataObject::~NotifyDataObject()
+{
+ delete d;
+}
+
+Kopete::NotifyEvent * Kopete::NotifyDataObject::notifyEvent( const QString &event ) const
+{
+ Kopete::NotifyEvent *evt = d->events.find( event );
+ return evt;
+}
+
+void Kopete::NotifyDataObject::setNotifyEvent( const QString& event, Kopete::NotifyEvent *notifyEvent )
+{
+ d->events.replace( event, notifyEvent );
+}
+
+bool Kopete::NotifyDataObject::removeNotifyEvent( const QString &event )
+{
+ return d->events.remove( event );
+}
+
+QDomElement Kopete::NotifyDataObject::notifyDataToXML()
+{
+ QDomDocument notify;
+ QDomElement notifications;
+ if ( !d->events.isEmpty() )
+ {
+ //<custom-notifications>
+ notifications = notify.createElement( QString::fromLatin1( "custom-notifications" ) );
+ QDictIterator<Kopete::NotifyEvent> it( d->events );
+ for ( ; it.current(); ++it )
+ {
+ //<event name="..." suppress-common="true|false">
+ QDomElement event = notify.createElement( QString::fromLatin1( "event" ) );
+ event.setAttribute( QString::fromLatin1( "name" ), it.currentKey() );
+ event.setAttribute( QString::fromLatin1( "suppress-common" ), QString::fromLatin1( it.current()->suppressCommon() ? "true" : "false" ) );
+ QValueList<QDomElement> presentations = it.current()->toXML();
+ //<sound-notification enabled="true|false" src="..." single-shot="">
+ for ( QValueList<QDomElement>::Iterator it = presentations.begin(); it != presentations.end(); ++it )
+ event.appendChild( notify.importNode( *it, true ) );
+ notifications.appendChild( event );
+ }
+ }
+ return notifications;
+}
+
+bool Kopete::NotifyDataObject::notifyDataFromXML( const QDomElement& element )
+{
+ if ( element.tagName() == QString::fromLatin1( "custom-notifications" ) )
+ {
+ QDomNode field = element.firstChild();
+ while( !field.isNull() )
+ {
+ //read an event
+ QDomElement fieldElement = field.toElement();
+ if ( fieldElement.tagName() == QString::fromLatin1( "event" ) )
+ {
+ // get its attributes
+ QString name = fieldElement.attribute( QString::fromLatin1( "name" ), QString::null );
+ QString suppress = fieldElement.attribute( QString::fromLatin1( "suppress-common" ), QString::null );
+ Kopete::NotifyEvent *evt = new Kopete::NotifyEvent( suppress == QString::fromLatin1( "true" ) );
+
+ // get its children
+ QDomNode child = fieldElement.firstChild();
+ while( !child.isNull() )
+ {
+ QDomElement childElement = child.toElement();
+ if ( childElement.tagName() == QString::fromLatin1( "sound-presentation" ) )
+ {
+// kdDebug(14010) << k_funcinfo << "read: sound" << endl;
+ QString src = childElement.attribute( QString::fromLatin1( "src" ) );
+ QString enabled = childElement.attribute( QString::fromLatin1( "enabled" ) );
+ QString singleShot = childElement.attribute( QString::fromLatin1( "single-shot" ) );
+ Kopete::EventPresentation *pres = new Kopete::EventPresentation( Kopete::EventPresentation::Sound, src,
+ ( singleShot == QString::fromLatin1( "true" ) ),
+ ( enabled == QString::fromLatin1( "true" ) ) );
+ evt->setPresentation( Kopete::EventPresentation::Sound, pres );
+// kdDebug(14010) << k_funcinfo << "after sound: " << evt->toString() << endl;
+ }
+ if ( childElement.tagName() == QString::fromLatin1( "message-presentation" ) )
+ {
+// kdDebug(14010) << k_funcinfo << "read: msg" << endl;
+ QString src = childElement.attribute( QString::fromLatin1( "src" ) );
+ QString enabled = childElement.attribute( QString::fromLatin1( "enabled" ) );
+ QString singleShot = childElement.attribute( QString::fromLatin1( "single-shot" ) );
+ Kopete::EventPresentation *pres = new Kopete::EventPresentation( Kopete::EventPresentation::Message, src,
+ ( singleShot == QString::fromLatin1( "true" ) ),
+ ( enabled == QString::fromLatin1( "true" ) ) );
+ evt->setPresentation( Kopete::EventPresentation::Message, pres );
+// kdDebug(14010) << k_funcinfo << "after message: " << evt->toString() << endl;
+ }
+ if ( childElement.tagName() == QString::fromLatin1( "chat-presentation" ) )
+ {
+// kdDebug(14010) << k_funcinfo << "read: chat" << endl;
+ QString enabled = childElement.attribute( QString::fromLatin1( "enabled" ) );
+ QString singleShot = childElement.attribute( QString::fromLatin1( "single-shot" ) );
+ Kopete::EventPresentation *pres = new Kopete::EventPresentation( Kopete::EventPresentation::Chat, QString::null,
+ ( singleShot == QString::fromLatin1( "true" ) ),
+ ( enabled == QString::fromLatin1( "true" ) ) );
+ evt->setPresentation( Kopete::EventPresentation::Chat, pres );
+// kdDebug(14010) << k_funcinfo << "after chat: " << evt->toString() << endl;
+ }
+ child = child.nextSibling();
+ }
+// kdDebug(14010) << k_funcinfo << "read: " << evt->toString() << endl;
+ setNotifyEvent( name, evt );
+ }
+ field = field.nextSibling();
+ }
+ return true;
+ }
+ else
+ {
+ kdDebug( 14010 ) << "element wasn't custom-notifications" << endl;
+ return false;
+ }
+}
+
diff --git a/kopete/libkopete/kopetenotifydataobject.h b/kopete/libkopete/kopetenotifydataobject.h
new file mode 100644
index 00000000..db253c60
--- /dev/null
+++ b/kopete/libkopete/kopetenotifydataobject.h
@@ -0,0 +1,58 @@
+/*
+ kopetenotifydataobject.h - Kopete Custom Notify Data Object
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef KOPETENOTIFYDATAOBJECT_H
+#define KOPETENOTIFYDATAOBJECT_H
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+#include "kopete_export.h"
+
+class QDomElement;
+
+namespace Kopete
+{
+
+class NotifyEvent;
+
+/**
+ * Contains custom notification control and storage functionality
+ */
+
+class KOPETE_EXPORT NotifyDataObject
+{
+ public:
+ NotifyDataObject();
+ ~NotifyDataObject();
+ // Notify events
+ NotifyEvent *notifyEvent( const QString &event ) const;
+ void setNotifyEvent( const QString &event,
+ NotifyEvent *notifyEvent );
+ bool removeNotifyEvent( const QString &event );
+ // Serialization
+ protected:
+ QDomElement notifyDataToXML();
+ bool notifyDataFromXML( const QDomElement& element );
+ private:
+ class Private;
+ NotifyDataObject::Private* d;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopetenotifyevent.cpp b/kopete/libkopete/kopetenotifyevent.cpp
new file mode 100644
index 00000000..28c4ab15
--- /dev/null
+++ b/kopete/libkopete/kopetenotifyevent.cpp
@@ -0,0 +1,181 @@
+/*
+ kopetenotifyevent.h - Kopete Notifications for a given event
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdom.h>
+#include <kdebug.h>
+#include "kopetenotifyevent.h"
+#include "kopeteeventpresentation.h"
+
+Kopete::NotifyEvent::NotifyEvent( const bool suppressCommon )
+{
+ m_suppressCommon = suppressCommon;
+ m_message = 0;
+ m_chat = 0;
+ m_sound = 0;
+}
+
+Kopete::NotifyEvent::~NotifyEvent()
+{
+ delete m_sound;
+ delete m_message;
+ delete m_chat;
+}
+
+bool Kopete::NotifyEvent::suppressCommon() const
+{
+ return m_suppressCommon;
+}
+
+Kopete::EventPresentation *Kopete::NotifyEvent::presentation( const Kopete::EventPresentation::PresentationType type ) const
+{
+ switch ( type )
+ {
+ case Kopete::EventPresentation::Sound:
+ return m_sound;
+ case Kopete::EventPresentation::Message:
+ return m_message;
+ case Kopete::EventPresentation::Chat:
+ return m_chat;
+ default:
+ return 0;
+ }
+}
+
+void Kopete::NotifyEvent::removePresentation( const Kopete::EventPresentation::PresentationType type )
+{
+ Kopete::EventPresentation **presToChange;
+ switch ( type )
+ {
+ case Kopete::EventPresentation::Sound:
+ presToChange = &m_sound;
+ break;
+ case Kopete::EventPresentation::Message:
+ presToChange = &m_message;
+ break;
+ case Kopete::EventPresentation::Chat:
+ presToChange = &m_chat;
+ break;
+ default:
+ kdDebug( 14010 ) << k_funcinfo << " Someone tried to set an unrecognised type of presentation!" << endl;
+ return;
+ }
+ if ( *presToChange )
+ {
+ delete *presToChange;
+ *presToChange = 0;
+ }
+}
+
+void Kopete::NotifyEvent::setPresentation( const Kopete::EventPresentation::PresentationType type, Kopete::EventPresentation * notification )
+{
+ Kopete::EventPresentation **presToChange;
+ switch ( type )
+ {
+ case Kopete::EventPresentation::Sound:
+ presToChange = &m_sound;
+ break;
+ case Kopete::EventPresentation::Message:
+ presToChange = &m_message;
+ break;
+ case Kopete::EventPresentation::Chat:
+ presToChange = &m_chat;
+ break;
+ default:
+ kdDebug( 14010 ) << k_funcinfo << " Someone tried to set an unrecognised type of presentation!" << endl;
+ return;
+ }
+ if ( *presToChange )
+ delete *presToChange;
+ *presToChange = notification;
+}
+
+bool Kopete::NotifyEvent::firePresentation( const Kopete::EventPresentation::PresentationType type )
+{
+ kdDebug( 14010 ) << k_funcinfo << endl;
+ Kopete::EventPresentation **presToChange;
+ switch ( type )
+ {
+ case Kopete::EventPresentation::Sound:
+ presToChange = &m_sound;
+ break;
+ case Kopete::EventPresentation::Message:
+ presToChange = &m_message;
+ break;
+ case Kopete::EventPresentation::Chat:
+ presToChange = &m_chat;
+ break;
+ default:
+ return false;
+ }
+ kdDebug( 14010 ) << toString() << endl;
+ if ( *presToChange && (*presToChange)->singleShot() )
+ {
+ kdDebug( 14010 ) << " removing singleshot!" << endl;
+ delete *presToChange;
+ *presToChange = 0;
+ kdDebug( 14010 ) << toString() << endl;
+ return true;
+ }
+ return false;
+}
+
+void Kopete::NotifyEvent::setSuppressCommon( const bool suppress )
+{
+ m_suppressCommon = suppress;
+}
+
+const QValueList<QDomElement> Kopete::NotifyEvent::toXML() const
+{
+ QDomDocument eventData;
+ QValueList<QDomElement> eventNodes;
+ if ( m_sound && !m_sound->content().isEmpty() )
+ {
+ QDomElement soundElmt = eventData.createElement( QString::fromLatin1( "sound-presentation" ) );
+ soundElmt.setAttribute( QString::fromLatin1( "enabled" ), QString::fromLatin1( m_sound->enabled() ? "true" : "false" ) );
+ soundElmt.setAttribute( QString::fromLatin1( "single-shot" ), QString::fromLatin1( m_sound->singleShot() ? "true" : "false" ) );
+ soundElmt.setAttribute( QString::fromLatin1( "src" ), m_sound->content() );
+ eventNodes.append( soundElmt );
+ }
+ if ( m_message && !m_message->content().isEmpty() )
+ {
+ QDomElement msgElmt = eventData.createElement( QString::fromLatin1( "message-presentation" ) );
+ msgElmt.setAttribute( QString::fromLatin1( "enabled" ), QString::fromLatin1( m_message->enabled() ? "true" : "false" ) );
+ msgElmt.setAttribute( QString::fromLatin1( "single-shot" ), QString::fromLatin1( m_message->singleShot() ? "true" : "false" ) );
+ msgElmt.setAttribute( QString::fromLatin1( "src" ), m_message->content() );
+ eventNodes.append( msgElmt );
+ }
+ if ( m_chat && m_chat->enabled() )
+ {
+ QDomElement chatElmt = eventData.createElement( QString::fromLatin1( "chat-presentation" ) );
+ chatElmt.setAttribute( QString::fromLatin1( "enabled" ), QString::fromLatin1( "true" ) );
+ chatElmt.setAttribute( QString::fromLatin1( "single-shot" ), QString::fromLatin1( m_chat->singleShot() ? "true" : "false" ) );
+ eventNodes.append( chatElmt );
+ }
+ return eventNodes;
+}
+
+QString Kopete::NotifyEvent::toString()
+{
+ QString stringRep = QString::fromLatin1("Event; Suppress common=%1").arg( QString::fromLatin1( suppressCommon() ? "true" : "false" ) );
+ if ( m_sound)
+ stringRep += m_sound->toString();
+ if ( m_message)
+ stringRep += m_message->toString();
+ if ( m_chat)
+ stringRep += m_chat->toString();
+ return stringRep;
+}
diff --git a/kopete/libkopete/kopetenotifyevent.h b/kopete/libkopete/kopetenotifyevent.h
new file mode 100644
index 00000000..b7acd3c1
--- /dev/null
+++ b/kopete/libkopete/kopetenotifyevent.h
@@ -0,0 +1,60 @@
+/*
+ kopetenotifyevent.h - Container for presentations of an event
+
+ Copyright (c) 2004 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETENOTIFYEVENT_H
+#define KOPETENOTIFYEVENT_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include "kopeteeventpresentation.h"
+
+#include "kopete_export.h"
+
+class QDomElement;
+
+namespace Kopete
+{
+
+class KOPETE_EXPORT NotifyEvent
+{
+public:
+ NotifyEvent( const bool suppressCommon = false );
+ ~NotifyEvent();
+
+ bool suppressCommon() const;
+ EventPresentation *presentation( const EventPresentation::PresentationType type ) const;
+ void setPresentation( const EventPresentation::PresentationType type, EventPresentation * );
+ void removePresentation( const EventPresentation::PresentationType type );
+ /**
+ * @return true if the presentation was single shot
+ */
+ bool firePresentation( const EventPresentation::PresentationType type );
+
+ void setSuppressCommon( bool suppress );
+ const QValueList<QDomElement> toXML() const;
+ QString toString();
+private:
+ QString m_event;
+ EventPresentation *m_sound;
+ EventPresentation *m_message;
+ EventPresentation *m_chat;
+ bool m_suppressCommon;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/kopeteonlinestatus.cpp b/kopete/libkopete/kopeteonlinestatus.cpp
new file mode 100644
index 00000000..8872f28b
--- /dev/null
+++ b/kopete/libkopete/kopeteonlinestatus.cpp
@@ -0,0 +1,302 @@
+/*
+ kopeteonlinestatus.cpp - Kopete Online Status
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2003 by Will Stephenson <lists@stevello.free-online.co.uk>
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteonlinestatus.h"
+#include "kopeteonlinestatusmanager.h"
+
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <kapplication.h>
+
+
+
+using namespace Kopete;
+
+class OnlineStatus::Private
+ : public KShared
+{
+public:
+ StatusType status;
+ unsigned weight;
+ Protocol *protocol;
+ unsigned internalStatus;
+ QStringList overlayIcons;
+ QString description;
+ unsigned refCount;
+
+ QString protocolIcon() const
+ {
+ return protocol ? protocol->pluginIcon() : QString::fromLatin1( "unknown" );
+ }
+
+};
+
+/**
+ * This is required by some plugins, when a status need to be stored on
+ * the disk, to avoid problems.
+ */
+static struct
+{
+ OnlineStatus::StatusType status;
+ const char *name;
+} statusNames[] = {
+ { OnlineStatus::Unknown, "Unknown" },
+ { OnlineStatus::Offline, "Offline" },
+ { OnlineStatus::Connecting, "Connecting" },
+ { OnlineStatus::Invisible, "Invisible" },
+ { OnlineStatus::Online, "Online"},
+ { OnlineStatus::Away, "Away" } };
+
+OnlineStatus::OnlineStatus( StatusType status, unsigned weight, Protocol *protocol,
+ unsigned internalStatus, const QStringList &overlayIcons, const QString &description )
+ : d( new Private )
+{
+ d->status = status;
+ d->internalStatus = internalStatus;
+ d->weight = weight;
+ d->overlayIcons = overlayIcons;
+ d->protocol = protocol;
+ d->description = description;
+}
+
+OnlineStatus::OnlineStatus( StatusType status, unsigned weight, Protocol *protocol, unsigned internalStatus,
+ const QStringList &overlayIcons, const QString &description, const QString &caption, unsigned int categories , unsigned int options )
+ : d( new Private )
+{
+ d->status = status;
+ d->internalStatus = internalStatus;
+ d->weight = weight;
+ d->overlayIcons = overlayIcons;
+ d->protocol = protocol;
+ d->description = description;
+
+ OnlineStatusManager::self()->registerOnlineStatus(*this, caption, categories, options );
+}
+
+OnlineStatus::OnlineStatus( StatusType status )
+ : d( new Private )
+{
+ d->status = status;
+ d->internalStatus = 0;
+ d->weight = 0;
+ d->protocol = 0L;
+
+ switch( status )
+ {
+ case Online:
+ d->description = i18n( "Online" );
+ break;
+ case Away:
+ d->description = i18n( "Away" );
+ break;
+ case Connecting:
+ d->description = i18n( "Connecting" );
+ break;
+ case Invisible:
+ d->description = i18n( "Invisible" );
+ break;
+ case Offline:
+ d->description = i18n( "Offline" );
+ break;
+ case Unknown:
+ default:
+ d->description = i18n( "Unknown" );
+ d->overlayIcons = QString::fromLatin1("status_unknown");
+ break;
+
+ }
+}
+
+OnlineStatus::OnlineStatus()
+ : d( new Private )
+{
+ d->status = Unknown;
+ d->internalStatus = 0;
+ d->weight = 0;
+ d->protocol = 0L;
+ d->overlayIcons = QString::fromLatin1( "status_unknown" );
+}
+
+OnlineStatus::OnlineStatus( const OnlineStatus &other )
+ : d( other.d )
+{
+}
+
+bool OnlineStatus::operator==( const OnlineStatus &other ) const
+{
+ if ( d->internalStatus == other.d->internalStatus && d->protocol == other.d->protocol &&
+ d->weight == other.d->weight && d->overlayIcons == other.d->overlayIcons &&
+ d->description == other.d->description )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool OnlineStatus::operator!=( const OnlineStatus &other ) const
+{
+ return !(*this == other);
+}
+
+
+bool OnlineStatus::operator>( const OnlineStatus &other ) const
+{
+ if( d->status == other.d->status )
+ return d->weight > other.d->weight;
+ else
+ return d->status > other.d->status;
+}
+
+bool OnlineStatus::operator<( const OnlineStatus &other ) const
+{
+ if( d->status == other.d->status )
+ return d->weight < other.d->weight;
+ else
+ return d->status < other.d->status;
+}
+
+OnlineStatus & OnlineStatus::operator=( const OnlineStatus &other )
+{
+ d = other.d;
+ return *this;
+}
+
+OnlineStatus::~OnlineStatus()
+{
+}
+
+OnlineStatus::StatusType OnlineStatus::status() const
+{
+ return d->status;
+}
+
+unsigned OnlineStatus::internalStatus() const
+{
+ return d->internalStatus;
+}
+
+unsigned OnlineStatus::weight() const
+{
+ return d->weight;
+}
+
+QStringList OnlineStatus::overlayIcons() const
+{
+ return d->overlayIcons;
+}
+
+QString OnlineStatus::description() const
+{
+ return d->description;
+}
+
+Protocol* OnlineStatus::protocol() const
+{
+ return d->protocol;
+}
+
+bool OnlineStatus::isDefinitelyOnline() const
+{
+ if ( status() == Offline || status() == Connecting || status() == Unknown )
+ return false;
+ return true;
+}
+
+QPixmap OnlineStatus::iconFor( const Contact *contact, int size ) const
+{
+ return OnlineStatusManager::self()->cacheLookupByMimeSource( mimeSourceFor( contact, size ) );
+}
+
+
+QString OnlineStatus::mimeSourceFor( const Contact *contact, int size ) const
+{
+ // figure out what icon we should use for this contact
+ QString iconName = contact->icon();
+ if ( iconName.isNull() )
+ iconName = contact->account()->customIcon();
+ if ( iconName.isNull() )
+ iconName = d->protocolIcon();
+
+
+ return mimeSource( iconName, size, contact->account()->color(),contact->idleTime() >= 10*60 );
+}
+
+QPixmap OnlineStatus::iconFor( const Account *account, int size ) const
+{
+ return OnlineStatusManager::self()->cacheLookupByMimeSource( mimeSourceFor( account, size ) );
+}
+
+QString OnlineStatus::mimeSourceFor( const Account *account, int size ) const
+{
+ QString iconName = account->customIcon();
+ if ( iconName.isNull() )
+ iconName = d->protocolIcon();
+
+ return mimeSource( iconName, size, account->color(), false );
+}
+
+QPixmap OnlineStatus::iconFor( const QString &mimeSource ) const
+{
+ return OnlineStatusManager::self()->cacheLookupByMimeSource( mimeSource );
+}
+
+QPixmap OnlineStatus::protocolIcon() const
+{
+ return OnlineStatusManager::self()->cacheLookupByObject( *this, d->protocolIcon() , 16, QColor() );
+}
+
+QString OnlineStatus::mimeSource( const QString& icon, int size, QColor color, bool idle) const
+{
+ // make sure the item is in the cache
+ OnlineStatusManager::self()->cacheLookupByObject( *this, icon, size, color, idle );
+ // now return the fingerprint instead
+ return OnlineStatusManager::self()->fingerprint( *this, icon, size, color, idle );
+}
+
+QString OnlineStatus::statusTypeToString(OnlineStatus::StatusType statusType)
+{
+ const int size = sizeof(statusNames) / sizeof(statusNames[0]);
+
+ for (int i=0; i< size; i++)
+ if (statusNames[i].status == statusType)
+ return QString::fromLatin1(statusNames[i].name);
+
+ return QString::fromLatin1(statusNames[0].name); // Unknown
+}
+
+OnlineStatus::StatusType OnlineStatus::statusStringToType(QString& string)
+{
+ int size = sizeof(statusNames) / sizeof(statusNames[0]);
+
+ for (int i=0; i< size; i++)
+ if (QString::fromLatin1(statusNames[i].name) == string)
+ return statusNames[i].status;
+
+ return OnlineStatus::Unknown;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteonlinestatus.h b/kopete/libkopete/kopeteonlinestatus.h
new file mode 100644
index 00000000..2eed5164
--- /dev/null
+++ b/kopete/libkopete/kopeteonlinestatus.h
@@ -0,0 +1,415 @@
+/*
+ kopeteonlinestatus.h - Kopete Online Status
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2003 by Will Stephenson <lists@stevello.free-online.co.uk>
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef kopeteonlinestatus_h
+#define kopeteonlinestatus_h
+
+#include "kopete_export.h"
+
+#include <kdemacros.h>
+#include <ksharedptr.h>
+
+#include <qobject.h>
+
+class QString;
+class QPixmap;
+class QColor;
+
+namespace Kopete
+{
+
+ class OnlineStatusManager;
+ class Protocol;
+ class Account;
+ class Contact;
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Will Stephenson (icon generating code)
+ *
+ * OnlineStatus is a class that encapsulates all information about the
+ * various online states that a protocol can be in in a single class. The
+ * online status consists of both a 'global' status as it's known to libkopete
+ * and used for going online or away, which non-protocol plugins can use,
+ * and the 'private' status, which is simply an unsigned int and is only
+ * useful for the actual protocol plugin that uses the status.
+ *
+ * This class is passed around by value, but is refcounted to cut down on the
+ * amount of overhead. All in all it should be more than fast enough for
+ * general use.
+ *
+ * Note that ONLY the constructor can set the data, the object is considered
+ * to be const after creation as there really shouldn't be a need to change
+ * a status' characteristics during runtime!
+ */
+class KOPETE_EXPORT OnlineStatus
+{
+public:
+ /**
+ * The available global states. It is possible that multiple internal
+ * states map to the same global states. For example ICQ's 'Do not disturb'
+ * is handled just like 'Away' by libkopete. Only ICQ itself makes (and
+ * should make) a distinction.
+ * The order is important and is used in the < or > operator
+ */
+ enum StatusType
+ {
+ /**
+ * Refers to protocols where state cannot be determined. This
+ * applies to SMS contacts (text messages via mobile phones),
+ * since there's no presence information over SMS, but also
+ * to e.g. MSN contacts that are not on your contact list,
+ * since MSN only allows a user to query online state for
+ * users that are formally on the contact list. Lastly, libkopete
+ * itself uses the Unknown state in @ref MetaContact for
+ * meta contacts that have no child contacts at all.
+ */
+ Unknown=0,
+ /**
+ * State where you really cannot be contacted. Although
+ * Kopete doesn't oppose any technical limitations it really
+ * doesn't make sense to have more than one status per protocol
+ * that maps to 'Offline', since you're supposed to be
+ * disconnected from the network in this state.
+ */
+ Offline=10,
+ /**
+ * State where the user is not available on the network yet
+ * but trying to get onto. Most useful to yourself contact, because
+ * this state means not visible but with network access
+ */
+ Connecting=20,
+ /**
+ * State where you are online but none of your contacts can
+ * see that you're online. Useful for all the protocols that support
+ * being invisible.
+ */
+ Invisible=30,
+ /**
+ * Refers to a state where you can be technically reached, but
+ * for one reason or another it is often not useful to do so.
+ * This can be because you really aren't behind the computer
+ * ('Away' or 'Idle') or because you have other things to do
+ * and don't want to get involved in messaging ('Busy' or 'Do
+ * not Disturb' for example).
+ */
+ Away=40,
+ /**
+ * Refers to a true online state, i.e. you can be contacted by
+ * others both technically and practically. This also applies
+ * to e.g. ICQ's 'Free for Chat' status.
+ */
+ Online=50
+ };
+ // note than Unknown is first, because the metacontact algorithm to detect
+ // the metacontact status from the contact status starts from Unknown, and
+ // takes a contact only if its status is greater
+
+ /**
+ * Reserved internal status values
+ *
+ * Any internal status value > 0x80000000 is reserved for internal
+ * libkopete use. This enumeration lists the currently known values.
+ */
+ enum ReservedInternalStatus
+ {
+ /**
+ * The account this contact belongs to is offline. Used with
+ * the Unknown StatusType.
+ */
+ AccountOffline = 0x80000001
+ };
+
+
+ /**
+ * Constructor.
+ *
+ * Creates an empty OnlineStatus object. Since you cannot change
+ * OnlineStatus objects that are already created other than by their
+ * assignment operator, this constructor is only a convenience method
+ * for use in e.g. class members and local variables.
+ */
+ OnlineStatus();
+
+
+ /**
+ * Constructor.
+ *
+ * Creates a new OnlineStatus object. All fields are mandatory; there
+ * are no default values. Also, you cannot change the object after creation.
+ *
+ * @param status is the global online status as used by libkopete
+ * @param weight is the 'weight' of this status. The contact list is
+ * sorted by status, and by weight within a status. It's not possible to
+ * 'promote' an Away item to a level above Online, since the status field
+ * always takes precedence. Weight is used when the same status is used
+ * more than once. Weight is also used for picking the most important
+ * 'Away' status for a protocol when going Away.
+ * @param protocol is a pointer to the protocol used. This is used when
+ * comparing two online status objects.
+ * @param internalStatus is the status as used internally by the protocol.
+ * This status is usually a lot more fine-grained than the status as used
+ * by libkopete and should be unique per protocol.
+ * @param overlayIcons is a list of QStrings which are the name of status
+ * icons to be used by the KDE icon loader. (Statuses which don't have icons
+ * to overlay like Online and Offline should use QString::null as icon
+ * name ). NOTE if the string is a movie ( *.mng ) it must be the first string in the list.
+ * TODO: KDE4 sort out movies and overlay icons.
+ * @param description is a description in e.g. tooltips.
+ */
+ OnlineStatus( StatusType status, unsigned weight, Protocol *protocol,
+ unsigned internalStatus, const QStringList &overlayIcons, const QString &description );
+
+ /**
+ * Constructor.
+ *
+ * @p Creates a new OnlineStatus object and registers it with the @ref Kopete::OnlineStatusManager.
+ * Registration allows you to generate a KActionMenu filled with KActions for changing to this OnlineStatus,
+ * using Kopete::Account::accountMenu().
+ *
+ * @p Note that weight has an additional significance for registered protocols when used for menu generation.
+ *
+ * All fields are mandatory; there
+ * are no default values. Also, you cannot change the object after creation.
+ *
+ * @param status is the global online status as used by libkopete
+ * @param weight is the 'weight' of this status. The contact list is
+ * sorted by status, and by weight within a status. It's not possible to
+ * 'promote' an Away item to a level above Online, since the status field
+ * always takes precedence. Weight is used when the same status is used
+ * more than once. Weight is also used for picking the most important
+ * 'Away' status for a protocol when going Away. Additionally, Weight determinesis also
+ * @param protocol is a pointer to the protocol used. This is used when
+ * comparing two online status objects.
+ * @param internalStatus is the status as used internally by the protocol.
+ * This status is usually a lot more fine-grained than the status as used
+ * by libkopete and should be unique per protocol.
+ * @param overlayIcon is a string returning the name of the status icon to be
+ * used by the KDE icon loader. (Status whiwh doesn't have icon to overlay like
+ * Online and Offline should use QString::null as icon string)
+ * @param description is a description in e.g. tooltips.
+ * @param caption is the text of the action in the menu
+ * @param categories the categories this online status is in
+ * @param options the options of this online status
+ * @see Kopete::OnlineStatusManager::registerOnlineStatus for more info about the categories and options parameters
+ */
+ OnlineStatus( StatusType status, unsigned weight, Protocol *protocol, unsigned internalStatus, const QStringList &overlayIcon,
+ const QString &description, const QString& caption, unsigned int categories=0x0 , unsigned int options=0x0 );
+
+
+ /**
+ * Constructor.
+ *
+ * Creates a libkopete builtin status object. Weight, protocol and internal
+ * status are set to zero, the strings and icons are set to the meta contact
+ * strings.
+ */
+ OnlineStatus( StatusType status );
+
+ /**
+ * Copy constructor.
+ *
+ * Just adds a reference to the refcount. Used to copy around the status
+ * objects with very little overhead.
+ */
+ OnlineStatus( const OnlineStatus &other );
+
+ /**
+ * Destructor.
+ */
+ ~OnlineStatus();
+
+ /**
+ * \brief Return the status
+ */
+ StatusType status() const;
+
+ /**
+ * \brief Return the internal status
+ */
+ unsigned internalStatus() const;
+
+ /**
+ * \brief Return the weight
+ */
+ unsigned weight() const;
+
+ /**
+ * \brief Return the list of overlay icons
+ */
+ QStringList overlayIcons() const;
+
+ /**
+ * \brief Return the description
+ */
+ QString description() const;
+
+ /**
+ * \brief Return the protocol this applies to
+ */
+ Protocol* protocol() const;
+
+ /**
+ * @return @c true if this a contact with this status is definitely online,
+ * @c false if the contact is Offline, Connecting or Unknown.
+ */
+ bool isDefinitelyOnline() const;
+
+
+ /**
+ * \brief Return a status icon generated for the given Contact
+ *
+ * This will draw an overlay representing the online status
+ * of the contact the OnlineStatus applies to
+ * over the base icon.
+ * A cache is employed to reduce CPU and memory usage.
+ * @param contact is the contact the icon should apply to.
+ * @param size is the size we the icon should be scaled to - 16 is default and so costs nothing
+ */
+ QPixmap iconFor( const Contact *contact, int size = 16 ) const;
+
+ /**
+ * \brief Return the mime source for a status icon generated for the given Contact
+ *
+ * This behaves essentially like the method above, except for that
+ * it returns a mime source string that can be used to render the
+ * image in richtext components and the like. The returned key
+ * is only valid until the cache is cleared for the next time,
+ * so no assumptions should be made about long-time availability
+ * of the referenced data.
+ * @param contact is the contact the icon should apply to.
+ * @param size is the size we the icon should be scaled to - 16 is default and so costs nothing
+ */
+ QString mimeSourceFor( const Contact *contact, int size = 16 ) const;
+
+ /**
+ * \brief Return a status icon generated for the given Account
+ *
+ * This will draw an overlay representing the online status
+ * of the account the OnlineStatus applies to
+ * over the base icon.
+ * A cache is employed to reduce CPU and memory usage.
+ * @param account is the account the icon should apply to.
+ * The account's color causes tinting, if it's plain QColor(), no tinting takes place.
+ * @param size is the size we the icon should be scaled to - 16 is default and so costs nothing
+ */
+ QPixmap iconFor( const Account *account, int size = 16 ) const;
+
+ /**
+ * \brief Return the mime source for a status icon generated for the given Account
+ *
+ * This behaves essentially like the method above, except for that
+ * it returns a mime source string that can be used to render the
+ * image in richtext components and the like. The returned key
+ * is only valid until the cache is cleared for the next time,
+ * so no assumptions should be made about long-time availability
+ * of the referenced data.
+ * @param account is the account the icon should apply to.
+ * The account's color causes tinting, if it's plain QColor(), no tinting takes place.
+ * @param size is the size we the icon should be scaled to - 16 is default and so costs nothing
+ */
+ QString mimeSourceFor( const Account *account, int size = 16 ) const;
+
+ /**
+ * \brief Return a previously rendered status icon for a mime source key
+ *
+ * You can access icons with this method that have previously been rendered
+ * using mimeSourceFor(). Note that only a cache lookup will be done, so
+ * if the cache has been invalidated due to a change of icon sets between
+ * requesting the key (thus rendering the icon) and trying to access the
+ * icon by key, an invalid pixmap will be returned.
+ */
+ QPixmap iconFor( const QString &mimeSource ) const;
+
+ /**
+ * \brief Returns the status icon for the protocol.
+ *
+ * A cache is employed to reduce CPU and memory usage.
+ */
+ QPixmap protocolIcon() const;
+
+ /**
+ * Assignment operator
+ */
+ OnlineStatus & operator=( const OnlineStatus &other );
+
+ /**
+ * Comparison operator
+ *
+ * Returns true if both the protocol and the internal status are
+ * identical.
+ */
+ bool operator==( const OnlineStatus &other ) const;
+
+ /**
+ * Comparison operator
+ *
+ * This operator works exactly opposite of @ref operator==()
+ */
+ bool operator!=( const OnlineStatus &other ) const;
+
+ /**
+ * Comparison operator
+ *
+ * Returns true if the status() of this contact is of higher value than the other
+ * contact or if both statuses are equal and weight() is higher for this contact.
+ */
+ bool operator>( const OnlineStatus &other ) const;
+
+ /**
+ * Comparison operator
+ *
+ * This operator works exactly opposite of @ref operator>()
+ */
+ bool operator<( const OnlineStatus &other ) const;
+
+ /**
+ * \brief returns a QString from a StatusType
+ *
+ * Static method to convert a Kopete::OnlineStatus::StatusType to a string to avoid
+ * many issues when saving StatusType to disk
+ */
+ static QString statusTypeToString(OnlineStatus::StatusType status);
+
+ /**
+ * \brief returns a StatusType from a QString
+ *
+ * Static method to convert a QString representing a StatusType to a StatusType to avoid
+ * many issues when saving StatusType to disk
+ */
+ static OnlineStatus::StatusType statusStringToType(QString& string);
+
+
+
+private:
+
+ class Private;
+ KSharedPtr<Private> d;
+
+ QString mimeSource( const QString& icon, int size, QColor color, bool idle) const;
+
+
+};
+
+} //END namespace Kopete
+
+#endif
+
+
diff --git a/kopete/libkopete/kopeteonlinestatusmanager.cpp b/kopete/libkopete/kopeteonlinestatusmanager.cpp
new file mode 100644
index 00000000..61c41b83
--- /dev/null
+++ b/kopete/libkopete/kopeteonlinestatusmanager.cpp
@@ -0,0 +1,436 @@
+/*
+ kopeteonlinestatusmanager.cpp
+
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ tiscalinet . be>
+ Copyright (c) 2003 by Will Stephenson <lists@stevello.free-online.co.uk>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteonlinestatusmanager.h"
+
+#include "kopeteawayaction.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <kapplication.h>
+#include <kcpuinfo.h> // for WORDS_BIGENDIAN
+
+#include <algorithm> // for min
+
+namespace Kopete {
+
+
+class OnlineStatusManager::Private
+{public:
+
+ struct RegisteredStatusStruct
+ {
+ QString caption;
+ unsigned int categories;
+ unsigned int options;
+ };
+
+ typedef QMap< OnlineStatus , RegisteredStatusStruct > ProtocolMap ;
+
+ QPixmap *nullPixmap;
+ QMap<Protocol* , ProtocolMap > registeredStatus;
+ QDict< QPixmap > iconCache;
+};
+
+OnlineStatusManager *OnlineStatusManager::s_self=0L;
+
+OnlineStatusManager *OnlineStatusManager::self()
+{
+ static KStaticDeleter<OnlineStatusManager> deleter;
+ if(!s_self)
+ deleter.setObject( s_self, new OnlineStatusManager() );
+ return s_self;
+}
+
+OnlineStatusManager::OnlineStatusManager()
+ : d( new Private )
+{
+ d->iconCache.setAutoDelete( true );
+ d->nullPixmap = new QPixmap;
+ connect( kapp, SIGNAL( iconChanged(int) ), this, SLOT( slotIconsChanged() ) );
+}
+
+OnlineStatusManager::~OnlineStatusManager()
+{
+ delete d->nullPixmap;
+ delete d;
+}
+
+void OnlineStatusManager::slotIconsChanged()
+{
+ d->iconCache.clear();
+ emit iconsChanged();
+}
+
+void OnlineStatusManager::registerOnlineStatus( const OnlineStatus &status, const QString & caption, unsigned int categories, unsigned int options)
+{
+ Private::RegisteredStatusStruct s;
+ s.caption=caption;
+ s.categories=categories;
+ s.options=options;
+ d->registeredStatus[status.protocol()].insert(status, s );
+}
+
+OnlineStatus OnlineStatusManager::onlineStatus(Protocol * protocol, Categories category) const
+{
+ /* Each category has a number which is a power of two, so it is possible to have several categories per online status
+ * the logaritm in base two if this number, which represent the bit which is equal to 1 in the number is chosen to be in a tree
+ * 1 (0 is reserved for Offline)
+ * / \
+ * 2 3
+ * / \ / \
+ * 4 5 6 7
+ * /\ / \ / \ / \
+ * 8 9 10 11 12 13 14 15
+ * To get the parent of a key, one just divide per two the number
+ */
+
+ Private::ProtocolMap protocolMap=d->registeredStatus[protocol];
+
+ int categ_nb=-1; //the logaritm of category
+ uint category_=category;
+ while(category_)
+ {
+ category_ >>= 1;
+ categ_nb++;
+ } //that code will give the log +1
+
+ do
+ {
+ Private::ProtocolMap::Iterator it;
+ for ( it = protocolMap.begin(); it != protocolMap.end(); it++ )
+ {
+ unsigned int catgs=it.data().categories;
+ if(catgs & (1<<(categ_nb)))
+ return it.key();
+ }
+ //no status found in this category, try the previous one.
+ categ_nb=(int)(categ_nb/2);
+ } while (categ_nb > 0);
+
+ kdWarning() << "No status in the category " << category << " for the protocol " << protocol->displayName() <<endl;
+ return OnlineStatus();
+}
+
+QString OnlineStatusManager::fingerprint( const OnlineStatus &statusFor, const QString& icon, int size, QColor color, bool idle)
+{
+ // create a 'fingerprint' to use as a hash key
+ // fingerprint consists of description/icon name/color/overlay name/size/idle state
+ return QString::fromLatin1("%1/%2/%3/%4/%5/%6")
+ .arg( statusFor.description() )
+ .arg( icon )
+ .arg( color.name() )
+ .arg( statusFor.overlayIcons().join( QString::fromLatin1( "," ) ) )
+ .arg( size )
+ .arg( idle ? 'i' : 'a' );
+}
+
+QPixmap OnlineStatusManager::cacheLookupByObject( const OnlineStatus &statusFor, const QString& icon, int size, QColor color, bool idle)
+{
+ QString fp = fingerprint( statusFor, icon, size, color, idle );
+
+ // look it up in the cache
+ QPixmap *theIcon= d->iconCache.find( fp );
+ if ( !theIcon )
+ {
+ // cache miss
+// kdDebug(14010) << k_funcinfo << "Missed " << fingerprint << " in icon cache!" << endl;
+ theIcon = renderIcon( statusFor, icon, size, color, idle);
+ d->iconCache.insert( fp, theIcon );
+ }
+ return *theIcon;
+}
+
+QPixmap OnlineStatusManager::cacheLookupByMimeSource( const QString &mimeSource )
+{
+ // look it up in the cache
+ const QPixmap *theIcon= d->iconCache.find( mimeSource );
+ if ( !theIcon )
+ {
+ // need to return an invalid pixmap
+ theIcon = d->nullPixmap;
+ }
+ return *theIcon;
+}
+
+// This code was forked from the broken KImageEffect::blendOnLower, but it's
+// been so heavily fixed and rearranged it's hard to recognise that now.
+static void blendOnLower( const QImage &upper_, QImage &lower, const QPoint &offset )
+{
+ if ( upper_.width() <= 0 || upper_.height() <= 0 )
+ return;
+ if ( lower.width() <= 0 || lower.height() <= 0 )
+ return;
+ if ( offset.x() < 0 || offset.x() >= lower.width() )
+ return;
+ if ( offset.y() < 0 || offset.y() >= lower.height() )
+ return;
+
+ QImage upper = upper_;
+ if ( upper.depth() != 32 )
+ upper = upper.convertDepth( 32 );
+ if ( lower.depth() != 32 )
+ lower = lower.convertDepth( 32 );
+
+ const int cx = offset.x();
+ const int cy = offset.y();
+ const int cw = std::min( upper.width() + cx, lower.width() );
+ const int ch = std::min( upper.height() + cy, lower.height() );
+ const int m = 255;
+
+ for ( int j = cy; j < ch; ++j )
+ {
+ QRgb *u = (QRgb*)upper.scanLine(j - cy);
+ QRgb *l = (QRgb*)lower.scanLine(j) + cx;
+
+ for( int k = cx; k < cw; ++u, ++l, ++k )
+ {
+ int ua = qAlpha(*u);
+ if ( !ua )
+ continue;
+
+ int la = qAlpha(*l);
+
+ int d = ua * m + la * (m - ua);
+ uchar r = uchar( ( qRed(*u) * ua * m + qRed(*l) * la * (m - ua) ) / d );
+ uchar g = uchar( ( qGreen(*u) * ua * m + qGreen(*l) * la * (m - ua) ) / d );
+ uchar b = uchar( ( qBlue(*u) * ua * m + qBlue(*l) * la * (m - ua) ) / d );
+ uchar a = uchar( ( ua * ua * m + la * la * (m - ua) ) / d );
+ *l = qRgba( r, g, b, a );
+ }
+ }
+}
+
+// Get bounding box of image via alpha channel
+static QRect getBoundingBox( const QImage& image )
+{
+ const int width = image.width();
+ const int height = image.height();
+ if ( width <= 0 || height <= 0 )
+ return QRect();
+
+ // scan image from left to right and top to bottom
+ // to get upper left corner of bounding box
+ int x1 = width - 1;
+ int y1 = height - 1;
+ for ( int j = 0; j < height; ++j )
+ {
+ QRgb *i = (QRgb*)image.scanLine(j);
+
+ for( int k = 0; k < width; ++i, ++k )
+ {
+ if ( qAlpha(*i) )
+ {
+ x1 = std::min( x1, k );
+ y1 = std::min( y1, j );
+ break;
+ }
+ }
+ }
+
+ // scan image from right to left and bottom to top
+ // to get lower right corner of bounding box
+ int x2 = 0;
+ int y2 = 0;
+ for ( int j = height-1; j >= 0; --j )
+ {
+ QRgb *i = (QRgb*)image.scanLine(j) + width-1;
+
+ for( int k = width-1; k >= 0; --i, --k )
+ {
+ if ( qAlpha(*i) )
+ {
+ x2 = std::max( x2, k );
+ y2 = std::max( y2, j );
+ break;
+ }
+ }
+ }
+ return QRect( x1, y1, std::max( 0, x2-x1+1 ), std::max( 0, y2-y1+1 ) );
+}
+
+// Get offset for upperImage to blend it in the i%4-th corner of lowerImage:
+// bottom right, bottom left, top left, top right
+static QPoint getOffsetForCorner( const QImage& upperImage, const QImage& lowerImage, const int i )
+{
+ const int dX = lowerImage.width() - upperImage.width();
+ const int dY = lowerImage.height() - upperImage.height();
+ const int corner = i % 4;
+ QPoint offset;
+ switch( corner ) {
+ case 0:
+ // bottom right
+ offset = QPoint( dX, dY );
+ break;
+ case 1:
+ // bottom left
+ offset = QPoint( 0, dY );
+ break;
+ case 2:
+ // top left
+ offset = QPoint( 0, 0 );
+ break;
+ case 3:
+ // top right
+ offset = QPoint( dX, 0 );
+ break;
+ }
+ return offset;
+}
+
+QPixmap* OnlineStatusManager::renderIcon( const OnlineStatus &statusFor, const QString& baseIcon, int size, QColor color, bool idle) const
+{
+ // create an icon suiting the status from the base icon
+ // use reasonable defaults if not provided or protocol not set
+
+ if ( baseIcon == statusFor.overlayIcons().first() )
+ kdWarning( 14010 ) << "Base and overlay icons are the same - icon effects will not be visible." << endl;
+
+ QPixmap* basis = new QPixmap( SmallIcon( baseIcon ) );
+
+ // Colorize
+ if ( color.isValid() )
+ *basis = KIconEffect().apply( *basis, KIconEffect::Colorize, 1, color, 0);
+
+ // Note that we do this before compositing the overlay, since we want
+ // that to be colored in this case.
+ if ( statusFor.internalStatus() == Kopete::OnlineStatus::AccountOffline || statusFor.status() == Kopete::OnlineStatus::Offline )
+ {
+ *basis = KIconEffect().apply( *basis, KIconEffect::ToGray , 0.85, QColor() , false );
+ }
+
+ //composite the iconOverlay for this status and the supplied baseIcon
+ QStringList overlays = statusFor.overlayIcons();
+ if ( !( overlays.isEmpty() ) ) // otherwise leave the basis as-is
+ {
+ KIconLoader *loader = KGlobal::instance()->iconLoader();
+
+ int i = 0;
+ for( QStringList::iterator it = overlays.begin(), end = overlays.end(); it != end; ++it )
+ {
+ QPixmap overlay = loader->loadIcon(*it, KIcon::Small, 0 ,
+ KIcon::DefaultState, 0L, /*canReturnNull=*/ true );
+
+ if ( !overlay.isNull() )
+ {
+ // we want to preserve the alpha channels of both basis and overlay.
+ // there's no way to do this in Qt. In fact, there's no way to do this
+ // in KDE since KImageEffect is so badly broken.
+ QImage basisImage = basis->convertToImage();
+ QImage overlayImage = overlay.convertToImage();
+ QPoint offset;
+ if ( (*it).endsWith( QString::fromLatin1( "_overlay" ) ) )
+ {
+ // it is possible to have more than one overlay icon
+ // to avoid overlapping we place them in different corners
+ overlayImage = overlayImage.copy( getBoundingBox( overlayImage ) );
+ offset = getOffsetForCorner( overlayImage, basisImage, i );
+ ++i;
+ }
+ blendOnLower( overlayImage, basisImage, offset );
+ basis->convertFromImage( basisImage );
+ }
+ }
+ }
+
+ // no need to scale if the icon is already of the required size (assuming height == width!)
+ if ( basis->width() != size )
+ {
+ QImage scaledImg = basis->convertToImage().smoothScale( size, size );
+ *basis = QPixmap( scaledImg );
+ }
+
+ // if idle, apply effects
+ if ( idle )
+ KIconEffect::semiTransparent( *basis );
+
+ return basis;
+}
+
+void OnlineStatusManager::createAccountStatusActions( Account *account , KActionMenu *parent)
+{
+ Private::ProtocolMap protocolMap=d->registeredStatus[account->protocol()];
+ Private::ProtocolMap::Iterator it;
+ for ( it = --protocolMap.end(); it != protocolMap.end(); --it )
+ {
+ unsigned int options=it.data().options;
+ if(options & OnlineStatusManager::HideFromMenu)
+ continue;
+
+ OnlineStatus status=it.key();
+ QString caption=it.data().caption;
+ KAction *action;
+
+ // Any existing actions owned by the account are reused by recovering them
+ // from the parent's child list.
+ // The description of the onlinestatus is used as the qobject name
+ // This is safe as long as OnlineStatus are immutable
+ QCString actionName = status.description().ascii();
+ if ( !( action = static_cast<KAction*>( account->child( actionName ) ) ) )
+ {
+ if(options & OnlineStatusManager::HasAwayMessage)
+ {
+ action = new AwayAction( status, caption, status.iconFor(account), 0, account,
+ SLOT( setOnlineStatus( const Kopete::OnlineStatus&, const QString& ) ),
+ account, actionName );
+ }
+ else
+ {
+ action=new OnlineStatusAction( status, caption, status.iconFor(account) , account, actionName );
+ connect(action,SIGNAL(activated(const Kopete::OnlineStatus&)) ,
+ account, SLOT(setOnlineStatus(const Kopete::OnlineStatus&)));
+ }
+ }
+
+#if 0
+ //disabled because since action are reused, they are not enabled back if the account is online.
+ if(options & OnlineStatusManager::DisabledIfOffline && !account->isConnected())
+ action->setEnabled(false);
+#endif
+
+ if(parent)
+ parent->insert(action);
+
+ }
+}
+
+
+OnlineStatusAction::OnlineStatusAction( const OnlineStatus& status, const QString &text, const QIconSet &pix, QObject *parent, const char *name)
+ : KAction( text, pix, KShortcut() , parent, name) , m_status(status)
+{
+ connect(this,SIGNAL(activated()),this,SLOT(slotActivated()));
+}
+
+void OnlineStatusAction::slotActivated()
+{
+ emit activated(m_status);
+}
+
+
+} //END namespace Kopete
+
+#include "kopeteonlinestatusmanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteonlinestatusmanager.h b/kopete/libkopete/kopeteonlinestatusmanager.h
new file mode 100644
index 00000000..d3369403
--- /dev/null
+++ b/kopete/libkopete/kopeteonlinestatusmanager.h
@@ -0,0 +1,169 @@
+/*
+ kopeteonlinestatusmanager.h
+
+ Copyright (c) 2004-2005 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2004-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef kopeteonlinestatusmanager_h__
+#define kopeteonlinestatusmanager_h__
+
+#include <qobject.h>
+
+#include "kopeteonlinestatus.h"
+#include "kaction.h"
+
+class QString;
+class QPixmap;
+class QColor;
+class KActionMenu;
+
+namespace Kopete
+{
+ class OnlineStatus;
+ class Account;
+
+
+/**
+ * OnlineStatusManager is a singleton which manage OnlineStatus
+ *
+ * @author Olivier Goffart
+ */
+class KOPETE_EXPORT OnlineStatusManager : public QObject
+{
+ Q_OBJECT
+public:
+ static OnlineStatusManager* self();
+ ~OnlineStatusManager();
+
+ /**
+ * Kopete will uses categories to have a more general system than siply globaly away.
+ *
+ * Idealy, in each protocol, there should be one status per categories (status may be in several or in none categories
+ *
+ * Idle is the status used for auto-away
+ *
+ * Status number are organised so that make a tree.
+ */
+ //please be carrefull when modifying values of status. read comment in onlineStatus()
+ enum Categories
+ {
+ Idle=1<<8, ExtendedAway=1<<9 , Invisible=1<<10,
+ // \ / __________/
+ /*1<<4*/ Busy=1<<5, FreeForChat=1<<6, /* 1<<7*/
+ // \ / /
+ Away=1<<2, /* 1<<3 */
+ // \ /
+ Online=1<<1,
+ Offline=1
+ };
+
+
+ /**
+ * @see registerOnlineStatus
+ */
+ enum Options
+ {
+ /// The user may set away messages for this online status
+ HasAwayMessage = 0x01,
+ /// The action of the status will be disabled if the account is offline.
+ /// use it if your protocol doesn't support connecting with the status as initial status.
+ /// You praticaly shouldn't abuse of that, and automaticaly set status after connecting if possible
+ DisabledIfOffline = 0x02,
+ /// The status will not appears in the action menu. Used if you want to register the status for e.g. autoaway,
+ /// without letting the user set itself that status
+ HideFromMenu = 0x04
+ };
+
+ /**
+ * You need to register each status an account can be.
+ * Registered statuses will appear in the account menu.
+ *
+ * The Protocol constructor is a good place to call this function.
+ * But if you want, you may use a special OnlineStatus constructor that call this function automaticaly
+ *
+ * You can set the status to be in the predefined categories.
+ * Ideally, each category should own one status.
+ * A status may be in several categories, or in none.
+ * There shouldn't be more than one status per protocol per categories.
+ *
+ * @param status The status to register
+ * @param caption The caption that will appear in menus (e.g. "Set &Away")
+ * @param categories A bitflag of @ref Categories
+ * @param options is a bitflag of @ref Options
+ */
+ void registerOnlineStatus(const OnlineStatus& status, const QString &caption, unsigned int categories=0x00 , unsigned int options=0x0);
+
+ /**
+ * insert "setStatus" actions from the given account to the specified actionMenu.
+ * (actions have that menu as parent QObject)
+ * they are connected to the Account::setOnlineStatus signal
+ *
+ * Items are stored by status height.
+ *
+ * @param account the account
+ * @param parent the ActionMenu where action are inserted
+ */
+ void createAccountStatusActions( Account *account , KActionMenu *parent);
+
+ /**
+ * return the status of the @p protocol which is in the category @p category
+ *
+ * If no status has been registered in this category, return the one in the category which is the most similair
+ */
+ OnlineStatus onlineStatus(Protocol *protocol, Categories category) const;
+
+private:
+ friend class OnlineStatus;
+ QPixmap cacheLookupByObject( const OnlineStatus &statusFor, const QString& icon, int size, QColor color, bool idle = false);
+ QPixmap cacheLookupByMimeSource( const QString &mimeSource );
+ QString fingerprint( const OnlineStatus &statusFor, const QString& icon, int size, QColor color, bool idle = false);
+ QPixmap* renderIcon( const OnlineStatus &statusFor, const QString& baseicon, int size, QColor color, bool idle = false) const;
+
+signals:
+ void iconsChanged();
+
+private slots:
+ void slotIconsChanged();
+
+private:
+
+ static OnlineStatusManager *s_self;
+ OnlineStatusManager();
+ class Private;
+ Private *d;
+};
+
+
+/**
+ * @internal
+ */
+class OnlineStatusAction : public KAction
+{
+ Q_OBJECT
+ public:
+ OnlineStatusAction ( const OnlineStatus& status, const QString &text, const QIconSet &pix, QObject *parent=0, const char *name=0);
+ signals:
+ void activated( const Kopete::OnlineStatus& status );
+ private slots:
+ void slotActivated();
+ private:
+ OnlineStatus m_status;
+};
+
+} //END namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetepassword.cpp b/kopete/libkopete/kopetepassword.cpp
new file mode 100644
index 00000000..f0b788a9
--- /dev/null
+++ b/kopete/libkopete/kopetepassword.cpp
@@ -0,0 +1,502 @@
+/*
+ kopetepassword.cpp - Kopete Password
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteuiglobal.h"
+#include "kopetepassword.h"
+#include "kopetepassworddialog.h"
+#include "kopetewalletmanager.h"
+
+#include <kwallet.h>
+
+#include <qapplication.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+
+#include <kactivelabel.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kpassdlg.h>
+#include <kstringhandler.h>
+
+class Kopete::Password::Private
+{
+public:
+ Private( const QString &group, uint maxLen, bool blanksAllowed )
+ : refCount( 1 ), configGroup( group ), remembered( false ), maximumLength( maxLen ),
+ isWrong( false ), allowBlankPassword( blanksAllowed )
+ {
+ }
+ Private *incRef()
+ {
+ ++refCount;
+ return this;
+ }
+ void decRef()
+ {
+ if( --refCount == 0 )
+ delete this;
+ }
+ /** Reference count */
+ int refCount;
+ /** Group to use for KConfig and KWallet */
+ const QString configGroup;
+ /** Is the password being remembered? */
+ bool remembered;
+ /** The current password in the KConfig file, or QString::null if no password there */
+ QString passwordFromKConfig;
+ /** The maximum length allowed for this password, or -1 if there is no limit */
+ uint maximumLength;
+ /** Is the current password known to be wrong? */
+ bool isWrong;
+ /** Are we allowed to have blank passwords? */
+ bool allowBlankPassword;
+ /** The cached password */
+ QString cachedValue;
+};
+
+/**
+ * Implementation detail of Kopete::Password: manages a single password request
+ * @internal
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class KopetePasswordRequest : public KopetePasswordRequestBase
+{
+public:
+ KopetePasswordRequest( QObject *owner, Kopete::Password &pass )
+ : QObject( owner ), mPassword( pass ), mWallet( 0 )
+ {
+ }
+
+ /**
+ * Start the request - ask for the wallet
+ */
+ void begin()
+ {
+ kdDebug( 14010 ) << k_funcinfo << endl;
+ Kopete::WalletManager::self()->openWallet( this, SLOT( walletReceived( KWallet::Wallet* ) ) );
+ }
+
+ void walletReceived( KWallet::Wallet *wallet )
+ {
+ kdDebug( 14010 ) << k_funcinfo << endl;
+ mWallet = wallet;
+ processRequest();
+ }
+
+ /**
+ * Got wallet; now carry out whatever action this request represents
+ */
+ virtual void processRequest() = 0;
+
+ void slotOkPressed() {}
+ void slotCancelPressed() {}
+
+protected:
+ Kopete::Password mPassword;
+ KWallet::Wallet *mWallet;
+};
+
+/**
+ * Implementation detail of Kopete::Password: manages a single password retrieval request
+ * @internal
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class KopetePasswordGetRequest : public KopetePasswordRequest
+{
+public:
+ KopetePasswordGetRequest( QObject *owner, Kopete::Password &pass )
+ : KopetePasswordRequest( owner, pass )
+ {
+ }
+
+ QString grabPassword()
+ {
+ // Before trying to read from the wallet, check if the config file holds a password.
+ // If so, remove it from the config and set it through KWallet instead.
+ QString pwd;
+ if ( mPassword.d->remembered && !mPassword.d->passwordFromKConfig.isNull() )
+ {
+ pwd = mPassword.d->passwordFromKConfig;
+ mPassword.set( pwd );
+ return pwd;
+ }
+
+ if ( mWallet && mWallet->readPassword( mPassword.d->configGroup, pwd ) == 0 && !pwd.isNull() )
+ return pwd;
+
+ if ( mPassword.d->remembered && !mPassword.d->passwordFromKConfig.isNull() )
+ return mPassword.d->passwordFromKConfig;
+
+ return QString::null;
+ }
+
+ void finished( const QString &result )
+ {
+ mPassword.d->cachedValue = result;
+ emit requestFinished( result );
+ delete this;
+ }
+};
+
+class KopetePasswordGetRequestPrompt : public KopetePasswordGetRequest
+{
+public:
+ KopetePasswordGetRequestPrompt( QObject *owner, Kopete::Password &pass, const QPixmap &image, const QString &prompt, Kopete::Password::PasswordSource source )
+ : KopetePasswordGetRequest( owner, pass ), mImage( image ), mPrompt( prompt ), mSource( source ), mView( 0 )
+ {
+ }
+
+ void processRequest()
+ {
+ QString result = grabPassword();
+ if ( mSource == Kopete::Password::FromUser || result.isNull() )
+ doPasswordDialog();
+ else
+ finished( result );
+ }
+
+ void doPasswordDialog()
+ {
+ kdDebug( 14010 ) << k_funcinfo << endl;
+
+ KDialogBase *passwdDialog = new KDialogBase( Kopete::UI::Global::mainWidget(), "passwdDialog", true, i18n( "Password Required" ),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );
+
+ mView = new KopetePasswordDialog( passwdDialog );
+ passwdDialog->setMainWidget( mView );
+
+ mView->m_text->setText( mPrompt );
+ mView->m_image->setPixmap( mImage );
+ /* Do not put the default password, or it will confuse those which doesn't echo anything for the password
+ mView->m_password->insert( password );
+ */
+ int maxLength = mPassword.maximumLength();
+ if ( maxLength != 0 )
+ mView->m_password->setMaxLength( maxLength );
+ mView->m_password->setFocus();
+
+ // FIXME: either document what these are for or remove them - lilac
+ mView->adjustSize();
+ passwdDialog->adjustSize();
+
+ connect( passwdDialog, SIGNAL( okClicked() ), SLOT( slotOkPressed() ) );
+ connect( passwdDialog, SIGNAL( cancelClicked() ), SLOT( slotCancelPressed() ) );
+ connect( this, SIGNAL( destroyed() ), passwdDialog, SLOT( deleteLater() ) );
+ passwdDialog->show();
+ }
+
+ void slotOkPressed()
+ {
+ QString result = QString::fromLocal8Bit( mView->m_password->password() );
+ if ( mView->m_save_passwd->isChecked() )
+ mPassword.set( result );
+
+ finished( result );
+ }
+
+ void slotCancelPressed()
+ {
+ finished( QString::null );
+ }
+
+private:
+ QPixmap mImage;
+ QString mPrompt;
+ Kopete::Password::PasswordSource mSource;
+ unsigned int mMaxLength;
+ KopetePasswordDialog *mView;
+};
+
+class KopetePasswordGetRequestNoPrompt : public KopetePasswordGetRequest
+{
+public:
+ KopetePasswordGetRequestNoPrompt( QObject *owner, Kopete::Password &pass )
+ : KopetePasswordGetRequest( owner, pass )
+ {
+ }
+
+ void processRequest()
+ {
+ finished( grabPassword() );
+ }
+};
+
+/**
+ * Implementation detail of Kopete::Password: manages a single password change request
+ * @internal
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class KopetePasswordSetRequest : public KopetePasswordRequest
+{
+public:
+ KopetePasswordSetRequest( Kopete::Password &pass, const QString &newPass )
+ : KopetePasswordRequest( 0, pass ), mNewPass( newPass )
+ {
+ if ( KApplication *app = KApplication::kApplication() )
+ app->ref();
+ }
+ ~KopetePasswordSetRequest()
+ {
+ if ( KApplication *app = KApplication::kApplication() )
+ app->deref();
+ kdDebug( 14010 ) << k_funcinfo << "job complete" << endl;
+ }
+ void processRequest()
+ {
+ if ( setPassword() )
+ {
+ mPassword.setWrong( false );
+ mPassword.d->cachedValue = mNewPass;
+ }
+ delete this;
+ }
+ bool setPassword()
+ {
+ kdDebug( 14010 ) << k_funcinfo << " setting password for " << mPassword.d->configGroup << endl;
+
+ if ( mWallet && mWallet->writePassword( mPassword.d->configGroup, mNewPass ) == 0 )
+ {
+ mPassword.d->remembered = true;
+ mPassword.d->passwordFromKConfig = QString::null;
+ mPassword.writeConfig();
+ return true;
+ }
+
+ if ( KWallet::Wallet::isEnabled() )
+ {
+ // If we end up here, the wallet is enabled, but failed somehow.
+ // Ask the user what to do now.
+
+ //NOTE: This will start a nested event loop. However, this is fine; the only code we
+ // call after this point is in Kopete::Password, so as long as we've not been deleted
+ // everything should work out OK. We have no parent QObject, so we should survive.
+ if ( KMessageBox::warningContinueCancel( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Kopete is unable to save your password securely in your wallet;<br>"
+ "do you want to save the password in the <b>unsafe</b> configuration file instead?</qt>" ),
+ i18n( "Unable to Store Secure Password" ),
+ KGuiItem( i18n( "Store &Unsafe" ), QString::fromLatin1( "unlock" ) ),
+ QString::fromLatin1( "KWalletFallbackToKConfig" ) ) != KMessageBox::Continue )
+ {
+ return false;
+ }
+ }
+ mPassword.d->remembered = true;
+ mPassword.d->passwordFromKConfig = mNewPass;
+ mPassword.writeConfig();
+ return true;
+ }
+
+private:
+ QString mNewPass;
+};
+
+class KopetePasswordClearRequest : public KopetePasswordRequest
+{
+public:
+ KopetePasswordClearRequest( Kopete::Password &pass )
+ : KopetePasswordRequest( 0, pass )
+ {
+ if ( KApplication *app = KApplication::kApplication() )
+ app->ref();
+ }
+ ~KopetePasswordClearRequest()
+ {
+ if ( KApplication *app = KApplication::kApplication() )
+ app->deref();
+ kdDebug( 14010 ) << k_funcinfo << "job complete" << endl;
+ }
+ void processRequest()
+ {
+ if ( clearPassword() )
+ {
+ mPassword.setWrong( true );
+ mPassword.d->cachedValue = QString::null;
+ }
+
+ delete this;
+ }
+ bool clearPassword()
+ {
+ kdDebug( 14010 ) << k_funcinfo << " clearing password" << endl;
+
+ mPassword.d->remembered = false;
+ mPassword.d->passwordFromKConfig = QString::null;
+ mPassword.writeConfig();
+ if ( mWallet )
+ mWallet->removeEntry( mPassword.d->configGroup );
+ return true;
+ }
+};
+
+Kopete::Password::Password( const QString &configGroup, uint maximumLength, const char *name )
+ : QObject( 0, name ), d( new Private( configGroup, maximumLength, false ) )
+{
+ readConfig();
+}
+
+Kopete::Password::Password( const QString &configGroup, uint maximumLength,
+ bool allowBlankPassword, const char *name )
+ : QObject( 0, name ), d( new Private( configGroup, maximumLength, allowBlankPassword ) )
+{
+ readConfig();
+}
+
+Kopete::Password::Password( Password &other, const char *name )
+ : QObject( 0, name ), d( other.d->incRef() )
+{
+}
+
+Kopete::Password::~Password()
+{
+ d->decRef();
+}
+
+Kopete::Password &Kopete::Password::operator=( Password &other )
+{
+ if ( d == other.d ) return *this;
+ d->decRef();
+ d = other.d->incRef();
+ return *this;
+}
+
+void Kopete::Password::readConfig()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup( d->configGroup );
+
+ QString passwordCrypted = config->readEntry( "Password" );
+ if ( passwordCrypted.isNull() )
+ d->passwordFromKConfig = QString::null;
+ else
+ d->passwordFromKConfig = KStringHandler::obscure( passwordCrypted );
+
+ d->remembered = config->readBoolEntry( "RememberPassword", false );
+ d->isWrong = config->readBoolEntry( "PasswordIsWrong", false );
+}
+
+void Kopete::Password::writeConfig()
+{
+ KConfig *config = KGlobal::config();
+ if(!config->hasGroup(d->configGroup))
+ {
+ //### (KOPETE)
+ // if the kopete account has been removed, we have no way to know it.
+ // but we don't want in any case to recreate the group.
+ // see Bug 106460
+ // (the problem is that when we remove the account, we remove the password
+ // also, which cause a call to this function )
+ return;
+ }
+
+ config->setGroup( d->configGroup );
+
+ if ( d->remembered && !d->passwordFromKConfig.isNull() )
+ config->writeEntry( "Password", KStringHandler::obscure( d->passwordFromKConfig ) );
+ else
+ config->deleteEntry( "Password" );
+
+ config->writeEntry( "RememberPassword", d->remembered );
+ config->writeEntry( "PasswordIsWrong", d->isWrong );
+}
+
+int Kopete::Password::preferredImageSize()
+{
+ return IconSize(KIcon::Toolbar);
+}
+
+bool Kopete::Password::allowBlankPassword()
+{
+ return d->allowBlankPassword;
+}
+
+uint Kopete::Password::maximumLength()
+{
+ return d->maximumLength;
+}
+
+void Kopete::Password::setMaximumLength( uint max )
+{
+ d->maximumLength = max;
+}
+
+bool Kopete::Password::isWrong()
+{
+ return d->isWrong;
+}
+
+void Kopete::Password::setWrong( bool bWrong )
+{
+ d->isWrong = bWrong;
+ writeConfig();
+
+ if ( bWrong ) d->cachedValue = QString::null;
+}
+
+void Kopete::Password::requestWithoutPrompt( QObject *returnObj, const char *slot )
+{
+ KopetePasswordRequest *request = new KopetePasswordGetRequestNoPrompt( returnObj, *this );
+ // call connect on returnObj so we can still connect if 'slot' is protected/private
+ returnObj->connect( request, SIGNAL( requestFinished( const QString & ) ), slot );
+ request->begin();
+}
+
+void Kopete::Password::request( QObject *returnObj, const char *slot, const QPixmap &image, const QString &prompt, Kopete::Password::PasswordSource source )
+{
+ KopetePasswordRequest *request = new KopetePasswordGetRequestPrompt( returnObj, *this, image, prompt, source );
+ returnObj->connect( request, SIGNAL( requestFinished( const QString & ) ), slot );
+ request->begin();
+}
+
+QString Kopete::Password::cachedValue()
+{
+ return d->cachedValue;
+}
+
+void Kopete::Password::set( const QString &pass )
+{
+ // if we're being told to forget the password, and we aren't remembering one,
+ // don't try to open the wallet. fixes bug #71804.
+ if( pass.isNull() && !d->allowBlankPassword )
+ {
+ if( remembered() )
+ clear();
+ return;
+ }
+
+ KopetePasswordRequest *request = new KopetePasswordSetRequest( *this, pass );
+ request->begin();
+}
+
+void Kopete::Password::clear()
+{
+ KopetePasswordClearRequest *request = new KopetePasswordClearRequest( *this );
+ request->begin();
+}
+
+bool Kopete::Password::remembered()
+{
+ return d->remembered;
+}
+
+#include "kopetepassword.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetepassword.h b/kopete/libkopete/kopetepassword.h
new file mode 100644
index 00000000..149db6f6
--- /dev/null
+++ b/kopete/libkopete/kopetepassword.h
@@ -0,0 +1,220 @@
+/*
+ kopetepassword.h - Kopete Password
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPASSWORD_H
+#define KOPETEPASSWORD_H
+
+#include <qobject.h>
+#include "kopete_export.h"
+
+namespace KWallet { class Wallet; }
+
+class QPixmap;
+
+/** @internal */
+class KopetePasswordGetRequest;
+/** @internal */
+class KopetePasswordSetRequest;
+/** @internal */
+class KopetePasswordClearRequest;
+
+namespace Kopete
+{
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ *
+ * The Kopete::Password object is responsible for storing and retrieving a
+ * password for a plugin or account object.
+ *
+ * If the KWallet is active, passwords will be stored in it, otherwise, they
+ * will be stored in the KConfig, in a slightly mangled form.
+ */
+class KOPETE_EXPORT Password : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a new Kopete::Password object.
+ *
+ * @param configGroup The configuration group to save passwords in.
+ * @param maxLength The maximum length of the password, or 0 if no maximum exists.
+ * @param name The name for this object
+ *
+ * @deprecated Use the constructor that specifies if a blank password is allowed
+ */
+ explicit Password( const QString &configGroup, uint maxLength = 0, const char *name = 0 );
+
+ /**
+ * Create a new Kopete::Password object.
+ *
+ * @param configGroup The configuration group to save passwords in.
+ * @param maxLength The maximum length of the password, or 0 if no maximum exists.
+ * @param allowBlankPassword If this password is allowed to be blank
+ * @param name The name for this object
+ */
+ explicit Password( const QString &configGroup, uint maxLength = 0,
+ bool allowBlankPassword = false, const char *name = 0 );
+
+ /**
+ * Create a shallow copy of this object
+ */
+ Password( Password &other, const char *name = 0 );
+ ~Password();
+
+ /**
+ * Assignment operator for passwords: make this object represent a different password
+ */
+ Password &operator=( Password &other );
+
+ /**
+ * Returns the preferred size for images passed to the retrieve and request functions.
+ */
+ static int preferredImageSize();
+
+ /**
+ * @brief Returns the maximum allowed length of the password, or 0 if there is no maximum.
+ */
+ uint maximumLength();
+ /**
+ * Sets the maximum allowed length of the password.
+ * @param max The new maximum allowed length, or 0 if there is no maximum.
+ */
+ void setMaximumLength( uint max );
+
+ /**
+ * @brief Returns whether the password currently stored by this object is known to be incorrect.
+ * This flag gets reset whenever the user enters a new password, and is
+ * expected to be set by the user of this class if it is detected that the
+ * password the user entered is wrong.
+ */
+ bool isWrong();
+ /**
+ * Flag the password as being incorrect.
+ * @see isWrong
+ */
+ void setWrong( bool bWrong = true );
+
+ /**
+ * Type of password request to perform:
+ * FromConfigOrUser : get the password from the config file, or from the user
+ * if no password in config.
+ * FromUser : always ask the user for a password (ie, if last password was
+ * wrong or you know the password has changed).
+ */
+ enum PasswordSource { FromConfigOrUser, FromUser };
+
+ /**
+ * @brief Start an asynchronous call to get the password.
+ * Causes a password entry dialog to appear if the password is not set. Triggers
+ * a provided slot when done, but not until after this function has returned (you
+ * don't need to worry about reentrancy or nested event loops).
+ *
+ * @param receiver The object to notify when the password request finishes
+ * @param slot The slot on receiver to call at the end of the request. The signature
+ * of this function should be slot( const QString &password ). password will
+ * be the password if successful, or QString::null if failed.
+ * @param image The icon to display in the dialog when asking for the password
+ * @param prompt The message to display to the user, asking for a
+ * password. Can be any Qt RichText string.
+ * @param source The source the password is taken from if a wrong or
+ * invalid password is entered or the password could not be found in the wallet
+ */
+ void request( QObject *receiver, const char *slot, const QPixmap &image,
+ const QString &prompt, PasswordSource source = FromConfigOrUser );
+
+ /**
+ * @brief Start an asynchronous password request without a prompt
+ *
+ * Starts an asynchronous password request. Does not pop up a password entry dialog
+ * if there is no password.
+ * @see request(QObject*,const char*,const QPixmap&,const QString&,bool,unsigned int)
+ * The password given to the provided slot will be NULL if no password could be retrieved for
+ * some reason, such as the user declining to open the wallet, or no password being found.
+ */
+ void requestWithoutPrompt( QObject *receiver, const char *slot );
+
+ /**
+ * @return true if the password is remembered, false otherwise.
+ *
+ * If it returns false, calling @ref request() will
+ * pop up an Enter Password window.
+ */
+ bool remembered();
+
+ /**
+ * @return true if you are allowed to have a blank password
+ */
+ bool allowBlankPassword();
+
+ /**
+ * When a password request succeeds, the password is cached. This function
+ * returns the cached password, if there is one, or QString::null if there
+ * is not.
+ */
+ QString cachedValue();
+
+public slots:
+ /**
+ * Set the password for this account.
+ * @param pass If set to QString::null, the password is forgotten unless you
+ * specified to allow blank passwords. Otherwise, sets the password to
+ * this value.
+ *
+ * Note: this function is asynchronous; changes will not be instant.
+ */
+ void set( const QString &pass = QString::null );
+
+ /**
+ * Unconditionally clears the stored password
+ */
+ void clear();
+
+private:
+ void readConfig();
+ void writeConfig();
+
+ class Private;
+ Private *d;
+
+ //TODO: can we rearrange things so these aren't friends?
+ friend class ::KopetePasswordGetRequest;
+ friend class ::KopetePasswordSetRequest;
+ friend class ::KopetePasswordClearRequest;
+};
+
+}
+
+/**
+ * This class is an implementation detail of KopetePassword.
+ * @internal
+ * @see KopetePassword
+ */
+class KopetePasswordRequestBase : public virtual QObject
+{
+ Q_OBJECT
+signals:
+ void requestFinished( const QString &password );
+public slots:
+ virtual void walletReceived( KWallet::Wallet *wallet ) = 0;
+ virtual void slotOkPressed() = 0;
+ virtual void slotCancelPressed() = 0;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetepasswordedaccount.cpp b/kopete/libkopete/kopetepasswordedaccount.cpp
new file mode 100644
index 00000000..9fea5c66
--- /dev/null
+++ b/kopete/libkopete/kopetepasswordedaccount.cpp
@@ -0,0 +1,111 @@
+/*
+ kopetepasswordedaccount.cpp - Kopete Account with a password
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetepasswordedaccount.h"
+#include "kopetepassword.h"
+#include "kopeteprotocol.h"
+#include "kopeteonlinestatus.h"
+
+#include <klocale.h>
+
+#include <qpixmap.h>
+
+struct Kopete::PasswordedAccount::Private
+{
+ Private( const QString &group, uint maxLen, bool allowBlankPassword ) :
+ password( group, maxLen, allowBlankPassword, "mPassword" ) {}
+ Kopete::Password password;
+ Kopete::OnlineStatus initialStatus;
+};
+
+Kopete::PasswordedAccount::PasswordedAccount( Kopete::Protocol *parent, const QString &acctId, uint maxLen, const char *name )
+ : Kopete::Account( parent, acctId, name ), d( new Private( QString::fromLatin1("Account_")+ parent->pluginId() + QString::fromLatin1("_") + acctId , maxLen, false ) )
+{
+}
+
+Kopete::PasswordedAccount::PasswordedAccount( Kopete::Protocol *parent, const QString &acctId, uint maxLen,
+ bool allowBlankPassword, const char *name )
+ : Kopete::Account( parent, acctId, name ), d( new Private( QString::fromLatin1("Account_")+ parent->pluginId() + QString::fromLatin1("_") + acctId , maxLen, allowBlankPassword ) )
+{
+}
+
+Kopete::PasswordedAccount::~PasswordedAccount()
+{
+ delete d;
+}
+
+Kopete::Password &Kopete::PasswordedAccount::password()
+{
+ return d->password;
+}
+
+void Kopete::PasswordedAccount::connect( )
+{
+ Kopete::OnlineStatus s(Kopete::OnlineStatus::Online);
+ connect( s );
+}
+
+void Kopete::PasswordedAccount::connect( const Kopete::OnlineStatus& initialStatus )
+{
+ // check that the networkstatus is up
+
+ // warn user somewhere
+ d->initialStatus = initialStatus;
+ QString cached = password().cachedValue();
+ if( !cached.isNull() || d->password.allowBlankPassword() )
+ {
+ connectWithPassword( cached );
+ return;
+ }
+
+ QString prompt = passwordPrompt();
+ Kopete::Password::PasswordSource src = password().isWrong() ? Kopete::Password::FromUser : Kopete::Password::FromConfigOrUser;
+
+ password().request( this, SLOT( connectWithPassword( const QString & ) ), accountIcon( Kopete::Password::preferredImageSize() ), prompt, src );
+}
+
+QString Kopete::PasswordedAccount::passwordPrompt()
+{
+ if ( password().isWrong() )
+ return i18n( "<b>The password was wrong;</b> please re-enter your password for %1 account <b>%2</b>" ).arg( protocol()->displayName(), accountId() );
+ else
+ return i18n( "Please enter your password for %1 account <b>%2</b>" ).arg( protocol()->displayName(), accountId() );
+}
+
+Kopete::OnlineStatus Kopete::PasswordedAccount::initialStatus()
+{
+ return d->initialStatus;
+}
+
+bool Kopete::PasswordedAccount::removeAccount()
+{
+ password().set(QString::null);
+ return Kopete::Account::removeAccount();
+}
+
+void Kopete::PasswordedAccount::disconnected( Kopete::Account::DisconnectReason reason )
+{
+ if(reason==Kopete::Account::BadPassword || reason==Kopete::Account::BadUserName)
+ {
+ password().setWrong(true);
+ }
+ Kopete::Account::disconnected(reason);
+}
+
+
+#include "kopetepasswordedaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetepasswordedaccount.h b/kopete/libkopete/kopetepasswordedaccount.h
new file mode 100644
index 00000000..1534025d
--- /dev/null
+++ b/kopete/libkopete/kopetepasswordedaccount.h
@@ -0,0 +1,132 @@
+/*
+ kopetepasswordedaccount.h - Kopete Account with a password
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPASSWORDEDACCOUNT_H
+#define KOPETEPASSWORDEDACCOUNT_H
+
+#include "kopeteaccount.h"
+
+#include "kopete_export.h"
+
+class Kopete::OnlineStatus;
+
+namespace Kopete
+{
+
+class Password;
+
+/**
+ * An account requiring a password to connect. Instead of reimplementing connect()
+ * in your subclass, reimplement connectWithPassword.
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class KOPETE_EXPORT PasswordedAccount : public Account
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * KopetePasswordedAccount constructor
+ * @param parent The protocol this account connects via
+ * @param acctId The ID of this account - should be unique within this protocol
+ * @param maxPasswordLength The maximum length for passwords for this account, or 0 for no limit
+ * @param name The name for this QObject
+ *
+ * @deprecated Use the constructor that specifies if a blank password is allowed
+ */
+ PasswordedAccount( Protocol *parent, const QString &acctId, uint maxPasswordLength = 0, const char *name = 0 );
+
+ /**
+ * KopetePasswordedAccount constructor
+ * @param parent The protocol this account connects via
+ * @param acctId The ID of this account - should be unique within this protocol
+ * @param maxPasswordLength The maximum length for passwords for this account, or 0 for no limit
+ * @param allowBlankPassword If this protocol allows blank passwords. Note that this will mean that
+ *
+ * @param name The name for this QObject
+ */
+ PasswordedAccount( Protocol *parent, const QString &acctId, uint maxPasswordLength = 0,
+ bool allowBlankPassword = false, const char *name = 0 );
+
+ virtual ~PasswordedAccount();
+
+ /**
+ * Returns a reference to the password object stored in this account.
+ */
+ Password &password();
+
+ void connect();
+
+ /**
+ * @brief Go online for this service.
+ *
+ * @param initialStatus is the status to connect with. If it is an invalid status for this
+ * account, the default online for the account should be used.
+ */
+ void connect( const OnlineStatus& initialStatus );
+
+ /**
+ * \brief Get the initial status
+ */
+ OnlineStatus initialStatus();
+
+ /**
+ * @brief Remove the account from the server.
+ *
+ * Reimplementation of Account::removeAccount() to remove the password from the wallet.
+ * if your protocol reimplements this function, this function should still be called.
+ *
+ * @return Always true
+ */
+ virtual bool removeAccount();
+
+
+public slots:
+ /**
+ * Called when your account should attempt to connect.
+ * @param password The password to connect with, or QString::null
+ * if the user wished to cancel the connection attempt.
+ */
+ virtual void connectWithPassword( const QString &password ) = 0;
+
+protected:
+ /**
+ * Returns the prompt shown to the user when requesting their password.
+ * The default implementation should be adequate in most cases; override
+ * if you have a custom message to show the user.
+ */
+ virtual QString passwordPrompt();
+
+protected slots:
+ /**
+ * @internal
+ * Reimplemented to set the password wrong if the reason is BadPassword
+ */
+ virtual void disconnected( Kopete::Account::DisconnectReason reason );
+
+
+private:
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetepicture.cpp b/kopete/libkopete/kopetepicture.cpp
new file mode 100644
index 00000000..1c586b40
--- /dev/null
+++ b/kopete/libkopete/kopetepicture.cpp
@@ -0,0 +1,197 @@
+/*
+ kopetepicture.cpp - Kopete Picture
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "kopetepicture.h"
+
+#include <qbuffer.h>
+
+#include <kabc/picture.h>
+
+#include <kmdcodec.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+namespace Kopete
+{
+
+class Picture::Private : public KShared
+{
+public:
+ Private()
+ {}
+
+ QString pictureBase64;
+ QImage pictureImage;
+ QString picturePath;
+};
+
+Picture::Picture()
+ : d(new Private)
+{
+}
+
+Picture::Picture(const QString &path)
+ : d(new Private)
+{
+ setPicture(path);
+}
+
+Picture::Picture(const QImage &image)
+ : d(new Private)
+{
+ setPicture(image);
+}
+
+Picture::Picture(const KABC::Picture &picture)
+ : d(new Private)
+{
+ setPicture(picture);
+}
+
+Picture::Picture(const Picture &other)
+ : d(other.d)
+{}
+
+Picture::~Picture()
+{}
+
+Picture &Picture::operator=(const Picture &other)
+{
+ d = other.d;
+ return *this;
+}
+
+QImage Picture::image()
+{
+ // Do the conversion if only needed.
+ // If the image is null, the path is not empty then.
+ if( d->pictureImage.isNull() )
+ {
+ d->pictureImage = QImage(d->picturePath);
+ }
+
+ return d->pictureImage;
+}
+
+QString Picture::base64()
+{
+ if( d->pictureBase64.isEmpty() )
+ {
+ // Generate base64 cache for the picture.
+ QByteArray tempArray;
+ QBuffer tempBuffer( tempArray );
+ tempBuffer.open( IO_WriteOnly );
+ // Make sure it create a image cache.
+ if( image().save( &tempBuffer, "PNG" ) )
+ {
+ d->pictureBase64 = KCodecs::base64Encode(tempArray);
+ }
+ }
+
+ return d->pictureBase64;
+}
+
+QString Picture::path()
+{
+ if( d->picturePath.isEmpty() )
+ {
+ // For a image source, finding a filename is tricky.
+ // I decided to use MD5 Hash as the filename.
+ QString localPhotoPath;
+
+ // Generate MD5 Hash for the image.
+ QByteArray tempArray;
+ QBuffer tempBuffer(tempArray);
+ tempBuffer.open( IO_WriteOnly );
+ image().save(&tempBuffer, "PNG");
+ KMD5 context(tempArray);
+ // Save the image to a file.
+ localPhotoPath = context.hexDigest() + ".png";
+ localPhotoPath = locateLocal( "appdata", QString::fromUtf8("metacontactpicturecache/%1").arg( localPhotoPath) );
+ if( image().save(localPhotoPath, "PNG") )
+ {
+ d->picturePath = localPhotoPath;
+ }
+ }
+
+ return d->picturePath;
+}
+
+bool Picture::isNull()
+{
+ if( d->pictureBase64.isEmpty() && d->picturePath.isEmpty() && d->pictureImage.isNull() )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void Picture::clear()
+{
+ detach();
+ d->pictureBase64 = QString::null;
+ d->picturePath = QString::null;
+ d->pictureImage = QImage();
+}
+
+void Picture::setPicture(const QImage &image)
+{
+ detach();
+
+ d->pictureImage = image;
+
+ // Clear the path and base64, it will call the update of then when "getted"
+ d->picturePath= QString::null;
+ d->pictureBase64 = QString::null;
+}
+
+void Picture::setPicture(const QString &path)
+{
+ detach();
+ d->picturePath = path;
+
+ // Clear the image and base64, it will call the update of then when "getted"
+ d->pictureImage = QImage();
+ d->pictureBase64 = QString::null;
+}
+
+void Picture::setPicture(const KABC::Picture &picture)
+{
+ // No need to call detach() here because setPicture will do it.
+ if ( picture.isIntern())
+ {
+ setPicture( picture.data() );
+ }
+ else
+ {
+ setPicture( picture.url() );
+ }
+}
+
+void Picture::detach()
+{
+ // there is no detach in KSharedPtr.
+ if( d.count() == 1 )
+ return;
+
+ // Warning: this only works as long as the private object doesn't contain pointers to allocated objects.
+ d = new Private(*d);
+}
+
+} // END namespace Kopete
diff --git a/kopete/libkopete/kopetepicture.h b/kopete/libkopete/kopetepicture.h
new file mode 100644
index 00000000..5631afc1
--- /dev/null
+++ b/kopete/libkopete/kopetepicture.h
@@ -0,0 +1,149 @@
+/*
+ kopetepicture.h - Kopete Picture
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef KOPETEPICTURE_H
+#define KOPETEPICTURE_H
+
+#include <kdemacros.h>
+#include <ksharedptr.h>
+#include "kopete_export.h"
+
+#include <qimage.h>
+
+namespace KABC
+{
+ class Picture;
+}
+
+namespace Kopete
+{
+/**
+ * @brief Represent a picture in Kopete context
+ *
+ * It kept a cache of a QImage object, a base64 string and
+ * a path to a image file. It ensure that all source are synced.
+ * Interally, the image is stored in PNG format when possible.
+ * It can happen that the image path do not return a PNG file.
+ *
+ * You can only use an QImage and a image path to create/update
+ * the picture.
+ * If the picture doesn't exist as a file, it generate a local
+ * copy into ~/.kde/share/apps/kopete/metacontactpicturecache
+ *
+ * This class is implicitly shared, so don't use it as a pointer.
+ *
+ * How to use this class:
+ * @code
+ * Kopete::Picture picture;
+ * picture.setPicture(QImage());
+ * picture.setPicture(QString("/tmp/image.png"));
+ *
+ * QString base64 = picture.base64();
+ * QString path = picture.path();
+ * QImage image = picture.image();
+ * @endcode
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class KOPETE_EXPORT Picture
+{
+public:
+ /**
+ * Create a empty Kopete::Picture
+ */
+ Picture();
+ /**
+ * Create a picture from a local path.
+ */
+ Picture(const QString &path);
+ /**
+ * Create a picture from a QImage.
+ */
+ Picture(const QImage &image);
+ /**
+ * Create a picture from a KABC::Picture.
+ */
+ Picture(const KABC::Picture &picture);
+ /**
+ * Copy a picture. It doesn't create a full copy, it just make a reference.
+ */
+ Picture(const Picture &other);
+ /**
+ * Delete the Kopete::Picture
+ */
+ ~Picture();
+ /**
+ * Assignment operator.
+ * Like the copy constructor, it just make a reference.
+ */
+ Picture &operator=(const Picture &other);
+
+ /**
+ * Return the current picture as QImage.
+ * QImage can used to draw the image on a context.
+ *
+ * @return the QImage cache of current picture.
+ */
+ QImage image();
+ /**
+ * Return the current picture as a base64 string.
+ * The base64 is used to include the picture into a XML/XHTML context.
+ */
+ QString base64();
+ /**
+ * Return the local path of the current picture.
+ */
+ QString path();
+
+ /**
+ * Check if the picture is null.
+ */
+ bool isNull();
+ /**
+ * Reset the picture.
+ */
+ void clear();
+
+ /**
+ * Set the picture content.
+ * @param image the picture as a QImage.
+ */
+ void setPicture(const QImage &image);
+ /**
+ * Set the picture content.
+ * @param path the path to the picture.
+ */
+ void setPicture(const QString &path);
+ /**
+ * Set the picture content.
+ * @param picture a KABC Picture.
+ */
+ void setPicture(const KABC::Picture &picture);
+
+private:
+ /**
+ * Kopete::Picture is implicitly shared.
+ * Detach the instance when modifying data.
+ */
+ void detach();
+
+ class Private;
+ KSharedPtr<Private> d;
+};
+
+}//END namespace Kopete
+
+#endif
diff --git a/kopete/libkopete/kopeteplugin.cpp b/kopete/libkopete/kopeteplugin.cpp
new file mode 100644
index 00000000..cec99179
--- /dev/null
+++ b/kopete/libkopete/kopeteplugin.cpp
@@ -0,0 +1,110 @@
+/*
+ kopeteplugin.cpp - Kopete Plugin API
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar P. <duncan@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
+
+ Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteplugin.h"
+#include "kopetepluginmanager.h"
+
+#include <kplugininfo.h>
+#include <ksettings/dispatcher.h>
+#include <kplugininfo.h>
+
+namespace Kopete {
+
+class Plugin::Private
+{
+public:
+ QStringList addressBookFields;
+ QString indexField;
+};
+
+Plugin::Plugin( KInstance *instance, QObject *parent, const char *name )
+: QObject( parent, name ), KXMLGUIClient(), d(new Private)
+{
+ setInstance( instance );
+ KSettings::Dispatcher::self()->registerInstance( instance, this, SIGNAL( settingsChanged() ) );
+}
+
+Plugin::~Plugin()
+{
+ delete d;
+}
+
+QString Plugin::pluginId() const
+{
+ return QString::fromLatin1( className() );
+}
+
+
+QString Plugin::displayName() const
+{
+ return pluginInfo() ? pluginInfo()->name() : QString::null;
+}
+
+QString Plugin::pluginIcon() const
+{
+ return pluginInfo() ? pluginInfo()->icon() : QString::null;
+}
+
+
+KPluginInfo *Plugin::pluginInfo() const
+{
+ return PluginManager::self()->pluginInfo( this );
+}
+
+void Plugin::aboutToUnload()
+{
+ // Just make the unload synchronous by default
+ emit readyForUnload();
+}
+
+
+void Plugin::deserialize( MetaContact * /* metaContact */,
+ const QMap<QString, QString> & /* stream */ )
+{
+ // Do nothing in default implementation
+}
+
+
+
+void Kopete::Plugin::addAddressBookField( const QString &field, AddressBookFieldAddMode mode )
+{
+ d->addressBookFields.append( field );
+ if( mode == MakeIndexField )
+ d->indexField = field;
+}
+
+QStringList Kopete::Plugin::addressBookFields() const
+{
+ return d->addressBookFields;
+}
+
+QString Kopete::Plugin::addressBookIndexField() const
+{
+ return d->indexField;
+
+}
+
+
+void Plugin::virtual_hook( uint, void * ) { }
+
+} //END namespace Kopete
+
+
+#include "kopeteplugin.moc"
+
+
diff --git a/kopete/libkopete/kopeteplugin.desktop b/kopete/libkopete/kopeteplugin.desktop
new file mode 100644
index 00000000..f5c29fd4
--- /dev/null
+++ b/kopete/libkopete/kopeteplugin.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=Kopete/Plugin
+X-KDE-Derived=KPluginInfo
+Comment=Kopete Plugin
+Comment[ar]=توصيلة Kopete
+Comment[be]=Модуль Kopete
+Comment[bg]=ПриÑтавки на Kopete
+Comment[bn]=কপেট পà§à¦²à¦¾à¦—িন
+Comment[br]=Lugent Kopete
+Comment[bs]=Kopete dodatak
+Comment[ca]=Connector de Kopete
+Comment[cs]=Modul aplikace Kopete
+Comment[cy]=Ategyn Kopete
+Comment[da]=Kopete-plugin
+Comment[de]=Kopete-Modul
+Comment[el]=ΠÏόσθετο Kopete
+Comment[eo]=Kopete-kromaĵo
+Comment[es]=Complemento de Kopete
+Comment[et]=Kopete plugin
+Comment[eu]=Kopete plugin-a
+Comment[fa]=Kopete وصلۀ
+Comment[fi]=Kopete-liitännäinen
+Comment[fr]=Module de Kopete
+Comment[ga]=Breiseán Kopete
+Comment[gl]=Plugin de Kopete
+Comment[he]=תוסף Kopete
+Comment[hi]=के-ऑपà¥à¤Ÿà¥€ पà¥à¤²à¤—इन
+Comment[hr]=Umetak za Kopete
+Comment[hu]=Kopete bővítőmodul
+Comment[is]=Kopete íforrit
+Comment[it]=Plugin di Kopete
+Comment[ja]=Kopete プラグイン
+Comment[ka]=Kopeteს მáƒáƒ“ული
+Comment[kk]=Kopete плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ Kopete
+Comment[lt]=Kopete įskiepis
+Comment[mk]=Приклучок за Kopete
+Comment[nb]=Programtillegg for Kopete
+Comment[nds]=Kopete-Moduul
+Comment[ne]=कोपेट पà¥à¤²à¤—इन
+Comment[nl]=Kopete-plugin
+Comment[nn]=Kopete-programtillegg
+Comment[pl]=Wtyczka Kopete
+Comment[pt]='Plugin' do Kopete
+Comment[pt_BR]=Plug-in do Kopete
+Comment[ro]=Modul Kopete
+Comment[ru]=Модуль Kopete
+Comment[se]=Kopete lassemoduvla
+Comment[sk]=Modul Kopete
+Comment[sl]=Vstavek za Kopete
+Comment[sr]=Прикључак за Kopete
+Comment[sr@Latn]=PrikljuÄak za Kopete
+Comment[sv]=Insticksprogram för Kopete
+Comment[ta]=Kopete செரà¯à®•à®²à¯
+Comment[tg]=Модули Kopete
+Comment[tr]=Kopete Eklentisi
+Comment[uk]=Втулок Kopete
+Comment[uz]=Kopete plagini
+Comment[uz@cyrillic]=Kopete плагини
+Comment[wa]=Tchôke-divins po Kopete
+Comment[zh_CN]=Kopete æ’件
+Comment[zh_HK]=Kopete æ’件
+Comment[zh_TW]=Kopete 外掛程å¼
+
+# The Kopete version for which the plugin is written
+[PropertyDef::X-Kopete-Version]
+Type=int
+
diff --git a/kopete/libkopete/kopeteplugin.h b/kopete/libkopete/kopeteplugin.h
new file mode 100644
index 00000000..43a80849
--- /dev/null
+++ b/kopete/libkopete/kopeteplugin.h
@@ -0,0 +1,217 @@
+/*
+ kopeteplugin.h - Kopete Plugin API
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+
+ Copyright (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPLUGIN_H
+#define KOPETEPLUGIN_H
+
+#include <kxmlguiclient.h>
+#include <qobject.h>
+#include <kdemacros.h>
+
+#include "kopete_export.h"
+
+#include <kopetemessage.h> //TODO: remove
+namespace DOM { class Node; } //TODO: remove
+class KAction; //TODO: remove
+
+
+class KPluginInfo;
+
+
+namespace Kopete
+{
+
+class MetaContact;
+
+/**
+ * @brief Base class for all plugins or protocols.
+ *
+ * To create a plugin, you need to create a .desktop file which looks like that:
+ * \verbatim
+[Desktop Entry]
+Encoding=UTF-8
+Type=Service
+X-Kopete-Version=1000900
+Icon=icon
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_myplugin
+X-KDE-PluginInfo-Author=Your Name
+X-KDE-PluginInfo-Email=your@mail.com
+X-KDE-PluginInfo-Name=kopete_myplugin
+X-KDE-PluginInfo-Version=0.0.1
+X-KDE-PluginInfo-Website=http://yoursite.com
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=MyPlugin
+Comment=Plugin that do some nice stuff
+ \endverbatim
+ *
+ * The constructor of your plugin should looks like this:
+ *
+ * \code
+ typedef KGenericFactory<MyPlugin> MyPluginFactory;
+ static const KAboutData aboutdata("kopete_myplugin", I18N_NOOP("MyPlugin") , "1.0" );
+ K_EXPORT_COMPONENT_FACTORY( kopete_myplugin, MyPluginFactory( &aboutdata ) )
+
+ MyPlugin::MyPlugin( QObject *parent, const char *name, const QStringList & args )
+ : Kopete::Plugin( MyPluginFactory::instance(), parent, name )
+ {
+ //...
+ }
+ \endcode
+ *
+ * Kopete::Plugin inherits from KXMLGUIClient. That client is added
+ * to the Kopete's mainwindow KXMLGUIFactory. So you may add actions
+ * on the main window (for hinstance in the meta contact popup menu).
+ * Please note the the client is added right after the plugin is created.
+ * so you have to create every actions in the constructor
+ *
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ * @author Olivier Goffart <ogoffart @ tiscalinet.be>
+ */
+class KOPETE_EXPORT Plugin : public QObject, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ Plugin( KInstance *instance, QObject *parent, const char *name );
+ virtual ~Plugin();
+
+ /**
+ * Returns the KPluginInfo object associated with this plugin
+ */
+ KPluginInfo *pluginInfo() const;
+
+ /**
+ * Get the name of the icon for this plugin. The icon name is taken from the
+ * .desktop file.
+ *
+ * May return an empty string if the .desktop file for this plugin specifies
+ * no icon name to use.
+ *
+ * This is a convenience method that simply calls @ref pluginInfo()->icon().
+ */
+ QString pluginIcon() const;
+
+ /**
+ * Returns the display name of this plugin.
+ *
+ * This is a convenience method that simply calls @ref pluginInfo()->name().
+ */
+ QString displayName() const;
+
+ /**
+ * @brief Get the plugin id
+ * @return the plugin's id which is gotten by calling QObject::className().
+ */
+ QString pluginId() const;
+
+ /**
+ * Return the list of all keys from the address book in which the plugin
+ * is interested. Those keys are monitored for changes upon load and
+ * during runtime. When the key actually changes, the plugin's
+ * addressBookKeyChanged( Kopete::MetaContact *mc, const QString &key )
+ * is called.
+ * You can add fields to the list using @ref addAddressBookField()
+ */
+ QStringList addressBookFields() const;
+
+ /**
+ * Return the index field as set by @ref addAddressBookField()
+ */
+ QString addressBookIndexField() const;
+
+ /**
+ * Mode for an address book field as used by @ref addAddressBookField()
+ */
+ enum AddressBookFieldAddMode { AddOnly, MakeIndexField };
+
+ /**
+ * Add a field to the list of address book fields. See also @ref addressBookFields()
+ * for a description of the fields.
+ *
+ * Set mode to MakeIndexField to make this the index field. Index fields
+ * are currently used by Kopete::Contact::serialize to autoset the index
+ * when possible.
+ *
+ * Only one field can be index field. Calling this method multiple times
+ * as index field will reset the value of index field!
+ */
+ void addAddressBookField( const QString &field, AddressBookFieldAddMode mode = AddOnly );
+
+ /**
+ * @brief Prepare for unloading a plugin
+ *
+ * When unloading a plugin the plugin manager first calls aboutToUnload()
+ * to indicate the pending unload. Some plugins need time to shutdown
+ * asynchronously and thus can't be simply deleted in the destructor.
+ *
+ * The default implementation immediately emits the @ref readyForUnload() signal,
+ * which basically makes the shutdown immediate and synchronous. If you need
+ * more time you can reimplement this method and fire the signal whenever
+ * you're ready. (you have 3 seconds)
+ *
+ * @ref Kopete::Protocol reimplement it.
+ */
+ virtual void aboutToUnload();
+
+signals:
+ /**
+ * Notify that the settings of a plugin were changed.
+ * These changes are passed on from the new KCDialog code in kdelibs/kutils.
+ */
+ void settingsChanged();
+
+ /**
+ * Indicate when we're ready for unload.
+ * @see aboutToUnload()
+ */
+ void readyForUnload();
+
+public slots:
+
+ /**
+ * deserialize() and tell the plugin
+ * to apply the previously stored data again.
+ * This method is also responsible for retrieving the settings from the
+ * address book. Settings that were registered can be retrieved with
+ * @ref Kopete::MetaContact::addressBookField().
+ *
+ * The default implementation does nothing.
+ *
+ * @todo we probably should think to another way to save the contacltist.
+ */
+ virtual void deserialize( MetaContact *metaContact, const QMap<QString, QString> &data );
+
+
+protected:
+ virtual void virtual_hook( uint id, void *data );
+
+private:
+ class Private;
+ Private *d;
+};
+
+
+} //END namespace Kopete
+
+
+#endif
diff --git a/kopete/libkopete/kopetepluginmanager.cpp b/kopete/libkopete/kopetepluginmanager.cpp
new file mode 100644
index 00000000..8f613a86
--- /dev/null
+++ b/kopete/libkopete/kopetepluginmanager.cpp
@@ -0,0 +1,534 @@
+/*
+ kopetepluginmanager.cpp - Kopete Plugin Loader
+
+ Copyright (c) 2002-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "config.h"
+
+#include "kopetepluginmanager.h"
+
+#if defined(HAVE_VALGRIND_H) && !defined(NDEBUG) && defined(__i386__)
+// We don't want the per-skin includes, so pretend we have a skin header already
+#define __VALGRIND_SOMESKIN_H
+#include <valgrind/valgrind.h>
+#endif
+
+#include <qapplication.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtimer.h>
+#include <qvaluestack.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kparts/componentfactory.h>
+#include <kplugininfo.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kurl.h>
+
+#include "kopeteplugin.h"
+#include "kopetecontactlist.h"
+#include "kopeteaccountmanager.h"
+
+namespace Kopete
+{
+
+
+class PluginManager::Private
+{
+public:
+ Private() : shutdownMode( StartingUp ), isAllPluginsLoaded(false) {}
+
+ // All available plugins, regardless of category, and loaded or not
+ QValueList<KPluginInfo *> plugins;
+
+ // Dict of all currently loaded plugins, mapping the KPluginInfo to
+ // a plugin
+ typedef QMap<KPluginInfo *, Plugin *> InfoToPluginMap;
+ InfoToPluginMap loadedPlugins;
+
+ // The plugin manager's mode. The mode is StartingUp until loadAllPlugins()
+ // has finished loading the plugins, after which it is set to Running.
+ // ShuttingDown and DoneShutdown are used during Kopete shutdown by the
+ // async unloading of plugins.
+ enum ShutdownMode { StartingUp, Running, ShuttingDown, DoneShutdown };
+ ShutdownMode shutdownMode;
+
+ // Plugins pending for loading
+ QValueStack<QString> pluginsToLoad;
+
+ static KStaticDeleter<PluginManager> deleter;
+
+ bool isAllPluginsLoaded;
+};
+
+KStaticDeleter<PluginManager> PluginManager::Private::deleter;
+PluginManager* PluginManager::s_self = 0L;
+
+PluginManager* PluginManager::self()
+{
+ if ( !s_self )
+ Private::deleter.setObject( s_self, new PluginManager() );
+
+ return s_self;
+}
+
+PluginManager::PluginManager() : QObject( qApp ), d( new Private )
+{
+ d->plugins = KPluginInfo::fromServices( KTrader::self()->query( QString::fromLatin1( "Kopete/Plugin" ),
+ QString::fromLatin1( "[X-Kopete-Version] == 1000900" ) ) );
+
+ // We want to add a reference to the application's event loop so we
+ // can remain in control when all windows are removed.
+ // This way we can unload plugins asynchronously, which is more
+ // robust if they are still doing processing.
+ kapp->ref();
+}
+
+PluginManager::~PluginManager()
+{
+ if ( d->shutdownMode != Private::DoneShutdown )
+ kdWarning( 14010 ) << k_funcinfo << "Destructing plugin manager without going through the shutdown process! Backtrace is: " << endl << kdBacktrace() << endl;
+
+ // Quick cleanup of the remaining plugins, hope it helps
+ // Note that deleting it.data() causes slotPluginDestroyed to be called, which
+ // removes the plugin from the list of loaded plugins.
+ while ( !d->loadedPlugins.empty() )
+ {
+ Private::InfoToPluginMap::ConstIterator it = d->loadedPlugins.begin();
+ kdWarning( 14010 ) << k_funcinfo << "Deleting stale plugin '" << it.data()->name() << "'" << endl;
+ delete it.data();
+ }
+
+ delete d;
+}
+
+QValueList<KPluginInfo *> PluginManager::availablePlugins( const QString &category ) const
+{
+ if ( category.isEmpty() )
+ return d->plugins;
+
+ QValueList<KPluginInfo *> result;
+ QValueList<KPluginInfo *>::ConstIterator it;
+ for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
+ {
+ if ( ( *it )->category() == category )
+ result.append( *it );
+ }
+
+ return result;
+}
+
+PluginList PluginManager::loadedPlugins( const QString &category ) const
+{
+ PluginList result;
+
+ for ( Private::InfoToPluginMap::ConstIterator it = d->loadedPlugins.begin();
+ it != d->loadedPlugins.end(); ++it )
+ {
+ if ( category.isEmpty() || it.key()->category() == category )
+ result.append( it.data() );
+ }
+
+ return result;
+}
+
+
+KPluginInfo *PluginManager::pluginInfo( const Plugin *plugin ) const
+{
+ for ( Private::InfoToPluginMap::ConstIterator it = d->loadedPlugins.begin();
+ it != d->loadedPlugins.end(); ++it )
+ {
+ if ( it.data() == plugin )
+ return it.key();
+ }
+ return 0;
+}
+
+void PluginManager::shutdown()
+{
+ if(d->shutdownMode != Private::Running)
+ {
+ kdDebug( 14010 ) << k_funcinfo << "called when not running. / state = " << d->shutdownMode << endl;
+ return;
+ }
+
+ d->shutdownMode = Private::ShuttingDown;
+
+
+ /* save the contact list now, just in case a change was made very recently
+ and it hasn't autosaved yet
+ from a OO point of view, theses lines should not be there, but i don't
+ see better place -Olivier
+ */
+ Kopete::ContactList::self()->save();
+ Kopete::AccountManager::self()->save();
+
+ // Remove any pending plugins to load, we're shutting down now :)
+ d->pluginsToLoad.clear();
+
+ // Ask all plugins to unload
+ for ( Private::InfoToPluginMap::ConstIterator it = d->loadedPlugins.begin();
+ it != d->loadedPlugins.end(); /* EMPTY */ )
+ {
+ // Plugins could emit their ready for unload signal directly in response to this,
+ // which would invalidate the current iterator. Therefore, we copy the iterator
+ // and increment it beforehand.
+ Private::InfoToPluginMap::ConstIterator current( it );
+ ++it;
+ // FIXME: a much cleaner approach would be to just delete the plugin now. if it needs
+ // to do some async processing, it can grab a reference to the app itself and create
+ // another object to do it.
+ current.data()->aboutToUnload();
+ }
+
+ // When running under valgrind, don't enable the timer because it will almost
+ // certainly fire due to valgrind's much slower processing
+#if defined(HAVE_VALGRIND_H) && !defined(NDEBUG) && defined(__i386__)
+ if ( RUNNING_ON_VALGRIND )
+ kdDebug(14010) << k_funcinfo << "Running under valgrind, disabling plugin unload timeout guard" << endl;
+ else
+#endif
+ QTimer::singleShot( 3000, this, SLOT( slotShutdownTimeout() ) );
+}
+
+void PluginManager::slotPluginReadyForUnload()
+{
+ // Using QObject::sender() is on purpose here, because otherwise all
+ // plugins would have to pass 'this' as parameter, which makes the API
+ // less clean for plugin authors
+ // FIXME: I don't buy the above argument. Add a Kopete::Plugin::emitReadyForUnload(void),
+ // and make readyForUnload be passed a plugin. - Richard
+ Plugin *plugin = dynamic_cast<Plugin *>( const_cast<QObject *>( sender() ) );
+ kdDebug( 14010 ) << k_funcinfo << plugin->pluginId() << "ready for unload" << endl;
+ if ( !plugin )
+ {
+ kdWarning( 14010 ) << k_funcinfo << "Calling object is not a plugin!" << endl;
+ return;
+ }
+
+ plugin->deleteLater();
+}
+
+
+void PluginManager::slotShutdownTimeout()
+{
+ // When we were already done the timer might still fire.
+ // Do nothing in that case.
+ if ( d->shutdownMode == Private::DoneShutdown )
+ return;
+
+ QStringList remaining;
+ for ( Private::InfoToPluginMap::ConstIterator it = d->loadedPlugins.begin(); it != d->loadedPlugins.end(); ++it )
+ remaining.append( it.data()->pluginId() );
+
+ kdWarning( 14010 ) << k_funcinfo << "Some plugins didn't shutdown in time!" << endl
+ << "Remaining plugins: " << remaining.join( QString::fromLatin1( ", " ) ) << endl
+ << "Forcing Kopete shutdown now." << endl;
+
+ slotShutdownDone();
+}
+
+void PluginManager::slotShutdownDone()
+{
+ kdDebug( 14010 ) << k_funcinfo << endl;
+
+ d->shutdownMode = Private::DoneShutdown;
+
+ kapp->deref();
+}
+
+void PluginManager::loadAllPlugins()
+{
+ // FIXME: We need session management here - Martijn
+
+ KConfig *config = KGlobal::config();
+ if ( config->hasGroup( QString::fromLatin1( "Plugins" ) ) )
+ {
+ QMap<QString, bool> pluginsMap;
+
+ QMap<QString, QString> entries = config->entryMap( QString::fromLatin1( "Plugins" ) );
+ QMap<QString, QString>::Iterator it;
+ for ( it = entries.begin(); it != entries.end(); ++it )
+ {
+ QString key = it.key();
+ if ( key.endsWith( QString::fromLatin1( "Enabled" ) ) )
+ pluginsMap.insert( key.left( key.length() - 7 ), (it.data() == QString::fromLatin1( "true" )) );
+ }
+
+ QValueList<KPluginInfo *> plugins = availablePlugins( QString::null );
+ QValueList<KPluginInfo *>::ConstIterator it2 = plugins.begin();
+ QValueList<KPluginInfo *>::ConstIterator end = plugins.end();
+ for ( ; it2 != end; ++it2 )
+ {
+ // Protocols are loaded automatically so they aren't always in Plugins group. (fixes bug 167113)
+ if ( (*it2)->category() == QString::fromLatin1( "Protocols" ) )
+ continue;
+
+ QString pluginName = (*it2)->pluginName();
+ bool inMap = pluginsMap.contains( pluginName );
+ if ( (inMap && pluginsMap[pluginName]) || (!inMap && (*it2)->isPluginEnabledByDefault()) )
+ {
+ if ( !plugin( pluginName ) )
+ d->pluginsToLoad.push( pluginName );
+ }
+ else
+ {
+ //This happens if the user unloaded plugins with the config plugin page.
+ // No real need to be assync because the user usualy unload few plugins
+ // compared tto the number of plugin to load in a cold start. - Olivier
+ if ( plugin( pluginName ) )
+ unloadPlugin( pluginName );
+ }
+ }
+ }
+ else
+ {
+ // we had no config, so we load any plugins that should be loaded by default.
+ QValueList<KPluginInfo *> plugins = availablePlugins( QString::null );
+ QValueList<KPluginInfo *>::ConstIterator it = plugins.begin();
+ QValueList<KPluginInfo *>::ConstIterator end = plugins.end();
+ for ( ; it != end; ++it )
+ {
+ if ( (*it)->isPluginEnabledByDefault() )
+ d->pluginsToLoad.push( (*it)->pluginName() );
+ }
+ }
+ // Schedule the plugins to load
+ QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
+}
+
+void PluginManager::slotLoadNextPlugin()
+{
+ if ( d->pluginsToLoad.isEmpty() )
+ {
+ if ( d->shutdownMode == Private::StartingUp )
+ {
+ d->shutdownMode = Private::Running;
+ d->isAllPluginsLoaded = true;
+ emit allPluginsLoaded();
+ }
+ return;
+ }
+
+ QString key = d->pluginsToLoad.pop();
+ loadPluginInternal( key );
+
+ // Schedule the next run unconditionally to avoid code duplication on the
+ // allPluginsLoaded() signal's handling. This has the added benefit that
+ // the signal is delayed one event loop, so the accounts are more likely
+ // to be instantiated.
+ QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
+}
+
+Plugin * PluginManager::loadPlugin( const QString &_pluginId, PluginLoadMode mode /* = LoadSync */ )
+{
+ QString pluginId = _pluginId;
+
+ // Try to find legacy code
+ // FIXME: Find any cases causing this, remove them, and remove this too - Richard
+ if ( pluginId.endsWith( QString::fromLatin1( ".desktop" ) ) )
+ {
+ kdWarning( 14010 ) << "Trying to use old-style API!" << endl << kdBacktrace() << endl;
+ pluginId = pluginId.remove( QRegExp( QString::fromLatin1( ".desktop$" ) ) );
+ }
+
+ if ( mode == LoadSync )
+ {
+ return loadPluginInternal( pluginId );
+ }
+ else
+ {
+ d->pluginsToLoad.push( pluginId );
+ QTimer::singleShot( 0, this, SLOT( slotLoadNextPlugin() ) );
+ return 0L;
+ }
+}
+
+Plugin *PluginManager::loadPluginInternal( const QString &pluginId )
+{
+ //kdDebug( 14010 ) << k_funcinfo << pluginId << endl;
+
+ KPluginInfo *info = infoForPluginId( pluginId );
+ if ( !info )
+ {
+ kdWarning( 14010 ) << k_funcinfo << "Unable to find a plugin named '" << pluginId << "'!" << endl;
+ return 0L;
+ }
+
+ if ( d->loadedPlugins.contains( info ) )
+ return d->loadedPlugins[ info ];
+
+ int error = 0;
+ Plugin *plugin = KParts::ComponentFactory::createInstanceFromQuery<Plugin>( QString::fromLatin1( "Kopete/Plugin" ),
+ QString::fromLatin1( "[X-KDE-PluginInfo-Name]=='%1'" ).arg( pluginId ), this, 0, QStringList(), &error );
+
+ if ( plugin )
+ {
+ d->loadedPlugins.insert( info, plugin );
+ info->setPluginEnabled( true );
+
+ connect( plugin, SIGNAL( destroyed( QObject * ) ), this, SLOT( slotPluginDestroyed( QObject * ) ) );
+ connect( plugin, SIGNAL( readyForUnload() ), this, SLOT( slotPluginReadyForUnload() ) );
+
+ kdDebug( 14010 ) << k_funcinfo << "Successfully loaded plugin '" << pluginId << "'" << endl;
+
+ emit pluginLoaded( plugin );
+ }
+ else
+ {
+ switch( error )
+ {
+ case KParts::ComponentFactory::ErrNoServiceFound:
+ kdDebug( 14010 ) << k_funcinfo << "No service implementing the given mimetype "
+ << "and fullfilling the given constraint expression can be found." << endl;
+ break;
+
+ case KParts::ComponentFactory::ErrServiceProvidesNoLibrary:
+ kdDebug( 14010 ) << "the specified service provides no shared library." << endl;
+ break;
+
+ case KParts::ComponentFactory::ErrNoLibrary:
+ kdDebug( 14010 ) << "the specified library could not be loaded." << endl;
+ break;
+
+ case KParts::ComponentFactory::ErrNoFactory:
+ kdDebug( 14010 ) << "the library does not export a factory for creating components." << endl;
+ break;
+
+ case KParts::ComponentFactory::ErrNoComponent:
+ kdDebug( 14010 ) << "the factory does not support creating components of the specified type." << endl;
+ break;
+ }
+
+ kdDebug( 14010 ) << k_funcinfo << "Loading plugin '" << pluginId << "' failed, KLibLoader reported error: '" << endl <<
+ KLibLoader::self()->lastErrorMessage() << "'" << endl;
+ }
+
+ return plugin;
+}
+
+bool PluginManager::unloadPlugin( const QString &spec )
+{
+ //kdDebug(14010) << k_funcinfo << spec << endl;
+ if( Plugin *thePlugin = plugin( spec ) )
+ {
+ thePlugin->aboutToUnload();
+ return true;
+ }
+ else
+ return false;
+}
+
+
+
+void PluginManager::slotPluginDestroyed( QObject *plugin )
+{
+ for ( Private::InfoToPluginMap::Iterator it = d->loadedPlugins.begin();
+ it != d->loadedPlugins.end(); ++it )
+ {
+ if ( it.data() == plugin )
+ {
+ d->loadedPlugins.erase( it );
+ break;
+ }
+ }
+
+ if ( d->shutdownMode == Private::ShuttingDown && d->loadedPlugins.isEmpty() )
+ {
+ // Use a timer to make sure any pending deleteLater() calls have
+ // been handled first
+ QTimer::singleShot( 0, this, SLOT( slotShutdownDone() ) );
+ }
+}
+
+
+
+
+Plugin* PluginManager::plugin( const QString &_pluginId ) const
+{
+ // Hack for compatibility with Plugin::pluginId(), which returns
+ // classname() instead of the internal name. Changing that is not easy
+ // as it invalidates the config file, the contact list, and most likely
+ // other code as well.
+ // For now, just transform FooProtocol to kopete_foo.
+ // FIXME: In the future we'll need to change this nevertheless to unify
+ // the handling - Martijn
+ QString pluginId = _pluginId;
+ if ( pluginId.endsWith( QString::fromLatin1( "Protocol" ) ) )
+ pluginId = QString::fromLatin1( "kopete_" ) + _pluginId.lower().remove( QString::fromLatin1( "protocol" ) );
+ // End hack
+
+ KPluginInfo *info = infoForPluginId( pluginId );
+ if ( !info )
+ return 0L;
+
+ if ( d->loadedPlugins.contains( info ) )
+ return d->loadedPlugins[ info ];
+ else
+ return 0L;
+}
+
+KPluginInfo * PluginManager::infoForPluginId( const QString &pluginId ) const
+{
+ QValueList<KPluginInfo *>::ConstIterator it;
+ for ( it = d->plugins.begin(); it != d->plugins.end(); ++it )
+ {
+ if ( ( *it )->pluginName() == pluginId )
+ return *it;
+ }
+
+ return 0L;
+}
+
+
+bool PluginManager::setPluginEnabled( const QString &_pluginId, bool enabled /* = true */ )
+{
+ QString pluginId = _pluginId;
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "Plugins" );
+
+ // FIXME: What is this for? This sort of thing is kconf_update's job - Richard
+ if ( !pluginId.startsWith( QString::fromLatin1( "kopete_" ) ) )
+ pluginId.prepend( QString::fromLatin1( "kopete_" ) );
+
+ if ( !infoForPluginId( pluginId ) )
+ return false;
+
+ config->writeEntry( pluginId + QString::fromLatin1( "Enabled" ), enabled );
+ config->sync();
+
+ return true;
+}
+
+bool PluginManager::isAllPluginsLoaded() const
+{
+ return d->isAllPluginsLoaded;
+}
+
+} //END namespace Kopete
+
+
+#include "kopetepluginmanager.moc"
+
+
+
+
+
diff --git a/kopete/libkopete/kopetepluginmanager.h b/kopete/libkopete/kopetepluginmanager.h
new file mode 100644
index 00000000..815cf422
--- /dev/null
+++ b/kopete/libkopete/kopetepluginmanager.h
@@ -0,0 +1,246 @@
+/*
+ kopetepluginmanager.h - Kopete Plugin Loader
+
+ Copyright (c) 2002-2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPLUGINMANAGER_H
+#define KOPETEPLUGINMANAGER_H
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+#include "kopete_export.h"
+
+class KPluginInfo;
+
+namespace Kopete
+{
+
+class Plugin;
+
+typedef QValueList<Plugin*> PluginList;
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ * @author Martijn Klingens <klingens@kde.org>
+ */
+class KOPETE_EXPORT PluginManager : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS( PluginLoadMode )
+
+public:
+ /**
+ * Retrieve the plugin loader instance.
+ */
+ static PluginManager* self();
+
+ ~PluginManager();
+
+ /**
+ * Returns a list of all available plugins for the given category.
+ * Currently there are two categories, "Plugins" and "Protocols", but
+ * you can add your own categories if you want.
+ *
+ * If you pass an empty string you get the complete list of ALL plugins.
+ *
+ * You can query all information on the plugins through the KPluginInfo
+ * interface.
+ */
+ QValueList<KPluginInfo *> availablePlugins( const QString &category = QString::null ) const;
+
+ /**
+ * Returns a list of all plugins that are actually loaded.
+ * If you omit the category you get all, otherwise it's a filtered list.
+ * See also @ref availablePlugins().
+ */
+ PluginList loadedPlugins( const QString &category = QString::null ) const;
+
+ /**
+ * @brief Search by plugin name. This is the key used as X-KDE-PluginInfo-Name in
+ * the .desktop file, e.g. "kopete_jabber"
+ *
+ * @return The @ref Kopete::Plugin object found by the search, or a null
+ * pointer if the plugin is not loaded.
+ *
+ * If you want to also load the plugin you can better use @ref loadPlugin, which returns
+ * the pointer to the plugin if it's already loaded.
+ */
+ Plugin *plugin( const QString &pluginName ) const;
+
+ /**
+ * @return the KPluginInfo for the specified plugin
+ */
+ KPluginInfo *pluginInfo( const Kopete::Plugin *plugin ) const;
+
+
+ /**
+ * Shuts down the plugin manager on Kopete shutdown, but first
+ * unloads all plugins asynchronously.
+ *
+ * After 3 seconds all plugins should be removed; what's still left
+ * by then is unloaded through a hard delete instead.
+ *
+ * Note that this call also derefs the plugin manager from the event
+ * loop, so do NOT call this method when not terminating Kopete!
+ */
+ void shutdown();
+
+ /**
+ * Enable a plugin.
+ *
+ * This marks a plugin as enabled in the config file, so loadAll()
+ * can pick it up later.
+ *
+ * This method does not actually load a plugin, it only edits the
+ * config file.
+ *
+ * @param name is the name of the plugin as it is listed in the .desktop
+ * file in the X-KDE-Library field.
+ * @param enabled sets whether or not the plugin is enabled
+ *
+ * Returns false when no appropriate plugin can be found.
+ */
+ bool setPluginEnabled( const QString &name, bool enabled = true );
+
+ /**
+ * This method check if all the plugins are loaded.
+ * @return true if all the plugins are loaded.
+ */
+ bool isAllPluginsLoaded() const;
+
+ /**
+ * Plugin loading mode. Used by @ref loadPlugin(). Code that doesn't want to block
+ * the GUI and/or lot a lot of plugins at once should use asynchronous loading (@c LoadAsync).
+ * The default is synchronous loading (@c LoadSync).
+ */
+ enum PluginLoadMode { LoadSync, LoadAsync };
+
+public slots:
+ /**
+ * @brief Load a single plugin by plugin name. Returns an existing plugin
+ * if one is already loaded in memory.
+ *
+ * If mode is set to Async, the plugin will be queued and loaded in
+ * the background. This method will return a null pointer. To get
+ * the loaded plugin you can track the @ref pluginLoaded() signal.
+ *
+ * See also @ref plugin().
+ */
+ Plugin *loadPlugin( const QString &pluginId, PluginLoadMode mode = LoadSync );
+
+ /**
+ * @brief Unload the plugin specified by @p pluginName
+ */
+ bool unloadPlugin( const QString &pluginName );
+
+ /**
+ * @brief Loads all the enabled plugins. Also used to reread the
+ * config file when the configuration has changed.
+ */
+ void loadAllPlugins();
+
+signals:
+ /**
+ * @brief Signals a new plugin has just been loaded.
+ */
+ void pluginLoaded( Kopete::Plugin *plugin );
+
+ /**
+ * @brief All plugins have been loaded by the plugin manager.
+ *
+ * This signal is emitted exactly ONCE, when the plugin manager has emptied
+ * its plugin queue for the first time. This means that if you call an async
+ * loadPlugin() before loadAllPlugins() this signal is probably emitted after
+ * the initial call completes, unless you are quick enough to fill the queue
+ * before it completes, which is a dangerous race you shouldn't count upon :)
+ *
+ * The signal is delayed one event loop iteration through a singleShot timer,
+ * but that is not guaranteed to be enough for account instantiation. You may
+ * need an additional timer for it in the code if you want to programmatically
+ * act on it.
+ *
+ * If you use the signal for enabling/disabling GUI objects there is little
+ * chance a user is able to activate them in the short while that's remaining,
+ * the slow part of the code is over now and the remaining processing time
+ * is neglectable for the user.
+ */
+ void allPluginsLoaded();
+
+private slots:
+ /**
+ * @brief Cleans up some references if the plugin is destroyed
+ */
+ void slotPluginDestroyed( QObject *plugin );
+
+ /**
+ * shutdown() starts a timer, when it fires we force all plugins
+ * to be unloaded here by deref()-ing the event loop to trigger the plugin
+ * manager's destruction
+ */
+ void slotShutdownTimeout();
+
+ /**
+ * Common entry point to deref() the KApplication. Used both by the clean
+ * shutdown and the timeout condition of slotShutdownTimeout()
+ */
+ void slotShutdownDone();
+
+ /**
+ * Emitted by a Kopete::Plugin when it's ready for unload
+ */
+ void slotPluginReadyForUnload();
+
+ /**
+ * Load a plugin from our queue. Does nothing if the queue is empty.
+ * Schedules itself again if more plugins are pending.
+ */
+ void slotLoadNextPlugin();
+
+private:
+ /**
+ * @internal
+ *
+ * The internal method for loading plugins.
+ * Called by @ref loadPlugin directly or through the queue for async plugin
+ * loading.
+ */
+ Plugin * loadPluginInternal( const QString &pluginId );
+
+ /**
+ * @internal
+ *
+ * Find the KPluginInfo structure by key. Reduces some code duplication.
+ *
+ * Returns a null pointer when no plugin info is found.
+ */
+ KPluginInfo * infoForPluginId( const QString &pluginId ) const;
+
+ PluginManager();
+
+ class Private;
+ Private *d;
+ static PluginManager *s_self;
+};
+
+}
+
+#endif // KOPETEPLUGINMANAGER_H
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteprefs.cpp b/kopete/libkopete/kopeteprefs.cpp
new file mode 100644
index 00000000..e1148260
--- /dev/null
+++ b/kopete/libkopete/kopeteprefs.cpp
@@ -0,0 +1,672 @@
+/*
+ kopeteprefs.cpp - Kopete Preferences Container-Class
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteprefs.h"
+
+#include <qfile.h>
+#include <qfont.h>
+#include <qmetaobject.h>
+
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#define KOPETE_DEFAULT_CHATSTYLE "Kopete"
+
+KopetePrefs *KopetePrefs::s_prefs = 0L;
+
+KopetePrefs *KopetePrefs::prefs()
+{
+ if( !s_prefs )
+ s_prefs = new KopetePrefs;
+ return s_prefs;
+}
+
+KopetePrefs::KopetePrefs() : QObject( kapp, "KopetePrefs" )
+{
+ config = KGlobal::config();
+ load();
+}
+
+void KopetePrefs::load()
+{
+// kdDebug( 14010 ) << k_funcinfo << endl;
+ config->setGroup("Appearance");
+
+ mIconTheme = config->readEntry("EmoticonTheme", defaultTheme());
+ mUseEmoticons = config->readBoolEntry("Use Emoticons", true);
+ mEmoticonsRequireSpaces = config->readBoolEntry("EmoticonsRequireSpaces" , true );
+ mShowOffline = config->readBoolEntry("ShowOfflineUsers", true);
+ mShowEmptyGroups = config->readBoolEntry("ShowEmptyGroups", true);
+ mGreyIdle = config->readBoolEntry("GreyIdleMetaContacts", true);
+ mSortByGroup = config->readBoolEntry("SortByGroup" , true);
+ mTreeView = config->readBoolEntry("TreeView", true);
+ mStartDocked = config->readBoolEntry("StartDocked", false);
+ mUseQueue = config->readBoolEntry("Use Queue", true);
+ mUseStack = config->readBoolEntry("Use Stack", false);
+ mRaiseMsgWindow = config->readBoolEntry("Raise Msg Window", false);
+ mShowEvents = config->readBoolEntry("Show Events in Chat Window", true);
+ mSpellCheck = config->readBoolEntry("SpellCheck", true);
+ mQueueUnreadMessages = config->readBoolEntry("Queue Unread Messages", false);
+ mQueueOnlyHighlightedMessagesInGroupChats = config->readBoolEntry("Queue Only Highlighted Messages In Group Chats", false);
+ mQueueOnlyMessagesOnAnotherDesktop = config->readBoolEntry("Queue Only Messages On Another Desktop", false);
+ mBalloonNotify = config->readBoolEntry("Balloon Notification", true);
+ mBalloonNotifyIgnoreClosesChatView = config->readBoolEntry("Balloon Notification Ignore Closes Chat View", false);
+ mBalloonCloseDelay = config->readNumEntry("Balloon Autoclose Delay", 30);
+ mBalloonClose = config->readBoolEntry("Balloon Autoclose", false);
+ mTrayflashNotify = config->readBoolEntry("Trayflash Notification", true);
+ mTrayflashNotifyLeftClickOpensMessage = config->readBoolEntry("Trayflash Notification Left Click Opens Message", true);
+ mTrayflashNotifySetCurrentDesktopToChatView = config->readBoolEntry("Trayflash Notification Set Current Desktop To Chat View", false);
+ mSoundIfAway = config->readBoolEntry("Sound Notification If Away", true);
+ mChatWindowPolicy = config->readNumEntry("Chatwindow Policy", 0);
+ mRichText = config->readBoolEntry("RichText editor", false);
+ mChatWShowSend = config->readBoolEntry("Show Chatwindow Send Button", true);
+ mRememberedMessages = config->readNumEntry("Remembered Messages", 5);
+ mTruncateContactNames = config->readBoolEntry("TruncateContactNames", false);
+ mMaxContactNameLength = config->readNumEntry("MaxContactNameLength", 20);
+
+ mChatViewBufferSize = config->readNumEntry("ChatView BufferSize", 250);
+
+ QColor tmpColor = KGlobalSettings::highlightColor();
+ mHighlightBackground = config->readColorEntry("Highlight Background Color", &tmpColor);
+ tmpColor = KGlobalSettings::highlightedTextColor();
+ mHighlightForeground = config->readColorEntry("Highlight Foreground Color", &tmpColor);
+ mHighlightEnabled = config->readBoolEntry("Highlighting Enabled", true);
+ mBgOverride = config->readBoolEntry("ChatView Override Background", false);
+ mFgOverride = config->readBoolEntry("ChatView Override Foreground", false);
+ mRtfOverride = config->readBoolEntry("ChatView Override RTF", false);
+ mInterfacePreference = config->readEntry("View Plugin", QString::fromLatin1("kopete_chatwindow") );
+ tmpColor = KGlobalSettings::textColor();
+ mTextColor = config->readColorEntry("Text Color", &tmpColor );
+ tmpColor = KGlobalSettings::baseColor();
+ mBgColor = config->readColorEntry("Bg Color", &tmpColor );
+ tmpColor = KGlobalSettings::linkColor();
+ mLinkColor = config->readColorEntry("Link Color", &tmpColor );
+ mFontFace = config->readFontEntry("Font Face");
+ tmpColor = darkGray;
+ mIdleContactColor = config->readColorEntry("Idle Contact Color", &tmpColor);
+
+ mShowTray = config->readBoolEntry("Show Systemtray", true);
+
+ _setStylePath(config->readEntry("StylePath"));
+ mStyleVariant = config->readEntry("StyleVariant");
+ // Read Chat Window Style display
+ mGroupConsecutiveMessages = config->readBoolEntry("GroupConsecutiveMessages", true);
+
+ mToolTipContents = config->readListEntry("ToolTipContents");
+ if(mToolTipContents.empty())
+ {
+ mToolTipContents
+ << QString::fromLatin1("FormattedName")
+ << QString::fromLatin1("userInfo")
+ << QString::fromLatin1("server")
+ << QString::fromLatin1("channels")
+ << QString::fromLatin1("FormattedIdleTime")
+ << QString::fromLatin1("channelMembers")
+ << QString::fromLatin1("channelTopic")
+ << QString::fromLatin1("emailAddress")
+ << QString::fromLatin1("homePage")
+ << QString::fromLatin1("onlineSince")
+ << QString::fromLatin1("lastOnline")
+ << QString::fromLatin1("awayMessage");
+ }
+
+ config->setGroup("ContactList");
+ int n = metaObject()->findProperty( "contactListDisplayMode" );
+ QString value = config->readEntry("DisplayMode",QString::fromLatin1("Default"));
+ mContactListDisplayMode = (ContactDisplayMode)metaObject()->property( n )->keyToValue( value.latin1() );
+ n = metaObject()->findProperty( "contactListIconMode" );
+ value = config->readEntry("IconMode",
+ QString::fromLatin1("IconDefault"));
+ mContactListIconMode = (IconDisplayMode) metaObject()->property( n )->keyToValue( value.latin1() );
+ mContactListIndentContacts = config->readBoolEntry("IndentContacts", false);
+ mContactListUseCustomFonts = config->readBoolEntry("UseCustomFonts", false);
+ QFont font = KGlobalSettings::generalFont();
+ mContactListNormalFont = config->readFontEntry("NormalFont", &font);
+ if ( font.pixelSize() != -1 )
+ font.setPixelSize( (font.pixelSize() * 3) / 4 );
+ else
+ font.setPointSizeFloat( font.pointSizeFloat() * 0.75 );
+ mContactListSmallFont = config->readFontEntry("SmallFont", &font);
+ mContactListGroupNameColor = config->readColorEntry("GroupNameColor", &darkRed);
+ mContactListAnimation = config->readBoolEntry("AnimateChanges", true);
+ mContactListFading = config->readBoolEntry("FadeItems", true);
+ mContactListFolding = config->readBoolEntry("FoldItems", true);
+ mContactListAutoHide = config->readBoolEntry("AutoHide", false);
+ mContactListAutoHideTimeout = config->readUnsignedNumEntry("AutoHideTimeout", 30);
+
+ // Load the reconnection setting
+ config->setGroup("General");
+ mReconnectOnDisconnect = config->readBoolEntry("ReconnectOnDisconnect", true);
+ mAutoConnect = config->readBoolEntry("AutoConnect", false);
+
+ // Nothing has changed yet
+ mWindowAppearanceChanged = false;
+ mContactListAppearanceChanged = false;
+ mMessageAppearanceChanged = false;
+ mStylePathChanged = false;
+ mStyleVariantChanged = false;
+}
+
+void KopetePrefs::save()
+{
+// kdDebug(14010) << "KopetePrefs::save()" << endl;
+ config->setGroup("Appearance");
+
+ config->writeEntry("EmoticonTheme", mIconTheme);
+ config->writeEntry("Use Emoticons", mUseEmoticons);
+ config->writeEntry("EmoticonsRequireSpaces", mEmoticonsRequireSpaces);
+ config->writeEntry("ShowOfflineUsers", mShowOffline);
+ config->writeEntry("ShowEmptyGroups", mShowEmptyGroups);
+ config->writeEntry("GreyIdleMetaContacts", mGreyIdle);
+ config->writeEntry("TreeView", mTreeView);
+ config->writeEntry("SortByGroup", mSortByGroup);
+ config->writeEntry("StartDocked", mStartDocked);
+ config->writeEntry("Use Queue", mUseQueue);
+ config->writeEntry("Use Stack", mUseStack);
+ config->writeEntry("Raise Msg Window", mRaiseMsgWindow);
+ config->writeEntry("Show Events in Chat Window", mShowEvents);
+ config->writeEntry("SpellCheck", mSpellCheck);
+ config->writeEntry("Queue Unread Messages", mQueueUnreadMessages);
+ config->writeEntry("Queue Only Highlighted Messages In Group Chats", mQueueOnlyHighlightedMessagesInGroupChats);
+ config->writeEntry("Queue Only Messages On Another Desktop", mQueueOnlyMessagesOnAnotherDesktop);
+ config->writeEntry("Balloon Notification", mBalloonNotify);
+ config->writeEntry("Balloon Notification Ignore Closes Chat View", mBalloonNotifyIgnoreClosesChatView);
+ config->writeEntry("Balloon Autoclose Delay", mBalloonCloseDelay);
+ config->writeEntry("Balloon Autoclose", mBalloonClose);
+ config->writeEntry("Trayflash Notification", mTrayflashNotify);
+ config->writeEntry("Trayflash Notification Left Click Opens Message", mTrayflashNotifyLeftClickOpensMessage);
+ config->writeEntry("Trayflash Notification Set Current Desktop To Chat View", mTrayflashNotifySetCurrentDesktopToChatView);
+ config->writeEntry("Sound Notification If Away", mSoundIfAway);
+ config->writeEntry("Chatwindow Policy", mChatWindowPolicy);
+ config->writeEntry("ChatView Override Background", mBgOverride);
+ config->writeEntry("ChatView Override Foreground", mFgOverride);
+ config->writeEntry("ChatView Override RTF", mRtfOverride);
+ config->writeEntry("ChatView BufferSize", mChatViewBufferSize);
+ config->writeEntry("Highlight Background Color", mHighlightBackground);
+ config->writeEntry("Highlight Foreground Color", mHighlightForeground);
+ config->writeEntry("Highlighting Enabled", mHighlightEnabled );
+ config->writeEntry("Font Face", mFontFace);
+ config->writeEntry("Text Color",mTextColor);
+ config->writeEntry("Remembered Messages",mRememberedMessages);
+ config->writeEntry("Bg Color", mBgColor);
+ config->writeEntry("Link Color", mLinkColor);
+ config->writeEntry("Idle Contact Color", mIdleContactColor);
+ config->writeEntry("RichText editor", mRichText);
+ config->writeEntry("Show Chatwindow Send Button", mChatWShowSend);
+ config->writeEntry("TruncateContactNames", mTruncateContactNames);
+ config->writeEntry("MaxContactNameLength", mMaxContactNameLength);
+
+ config->writeEntry("View Plugin", mInterfacePreference);
+
+ config->writeEntry("Show Systemtray", mShowTray);
+
+ //Style
+ //for xhtml+css
+ config->writeEntry("StylePath", mStylePath);
+ config->writeEntry("StyleVariant", mStyleVariant);
+ // Chat Window Display
+ config->writeEntry("GroupConsecutiveMessages", mGroupConsecutiveMessages);
+
+ config->writeEntry("ToolTipContents", mToolTipContents);
+
+ config->setGroup("ContactList");
+ int n = metaObject()->findProperty( "contactListDisplayMode" );
+ config->writeEntry("DisplayMode", metaObject()->property( n )->valueToKey( mContactListDisplayMode ));
+ n = metaObject()->findProperty( "contactListIconMode" );
+ config->writeEntry("IconMode", metaObject()->property( n )->valueToKey( mContactListIconMode ));
+ config->writeEntry("IndentContacts", mContactListIndentContacts);
+ config->writeEntry("UseCustomFonts", mContactListUseCustomFonts);
+ config->writeEntry("NormalFont", mContactListNormalFont);
+ config->writeEntry("SmallFont", mContactListSmallFont);
+ config->writeEntry("GroupNameColor", mContactListGroupNameColor);
+ config->writeEntry("AnimateChanges", mContactListAnimation);
+ config->writeEntry("FadeItems", mContactListFading);
+ config->writeEntry("FoldItems", mContactListFolding);
+ config->writeEntry("AutoHide", mContactListAutoHide);
+ config->writeEntry("AutoHideTimeout", mContactListAutoHideTimeout);
+
+ //Save the reconnection setting
+ config->setGroup("General");
+ config->writeEntry("ReconnectOnDisconnect", mReconnectOnDisconnect);
+ config->writeEntry("AutoConnect", mAutoConnect);
+
+ config->sync();
+ emit saved();
+
+ if(mWindowAppearanceChanged)
+ emit windowAppearanceChanged();
+
+ if(mContactListAppearanceChanged)
+ emit contactListAppearanceChanged();
+
+ if(mMessageAppearanceChanged)
+ emit messageAppearanceChanged();
+
+ if(mStylePathChanged)
+ emit styleChanged(mStylePath);
+
+ if(mStyleVariantChanged)
+ emit styleVariantChanged(mStyleVariant);
+
+ // Clear all *Changed flags. This will cause breakage if someone makes some
+ // changes but doesn't save them in a slot connected to a *Changed signal.
+ mWindowAppearanceChanged = false;
+ mContactListAppearanceChanged = false;
+ mMessageAppearanceChanged = false;
+ mStylePathChanged = false;
+ mStyleVariantChanged = false;
+}
+
+void KopetePrefs::setIconTheme(const QString &value)
+{
+ if( mIconTheme != value )
+ {
+ mMessageAppearanceChanged = true;
+ mContactListAppearanceChanged = true;
+ }
+ mIconTheme = value;
+}
+
+void KopetePrefs::setUseEmoticons(bool value)
+{
+ if( mUseEmoticons != value )
+ {
+ mMessageAppearanceChanged = true;
+ mContactListAppearanceChanged = true;
+ }
+ mUseEmoticons = value;
+}
+
+void KopetePrefs::setShowOffline(bool value)
+{
+ if( value != mShowOffline ) mContactListAppearanceChanged = true;
+ mShowOffline = value;
+}
+
+void KopetePrefs::setShowEmptyGroups(bool value)
+{
+ if( value != mShowEmptyGroups ) mContactListAppearanceChanged = true;
+ mShowEmptyGroups = value;
+}
+
+void KopetePrefs::setTreeView(bool value)
+{
+ if( value != mTreeView ) mContactListAppearanceChanged = true;
+ mTreeView = value;
+}
+
+void KopetePrefs::setSortByGroup(bool value)
+{
+ if( value != mSortByGroup ) mContactListAppearanceChanged = true;
+ mSortByGroup = value;
+}
+
+void KopetePrefs::setGreyIdleMetaContacts(bool value)
+{
+ if( value != mGreyIdle ) mContactListAppearanceChanged = true;
+ mGreyIdle = value;
+}
+
+void KopetePrefs::setStartDocked(bool value)
+{
+ mStartDocked = value;
+}
+
+void KopetePrefs::setUseQueue(bool value)
+{
+ mUseQueue = value;
+}
+
+void KopetePrefs::setUseStack(bool value)
+{
+ mUseStack = value;
+}
+
+
+void KopetePrefs::setRaiseMsgWindow(bool value)
+{
+ mRaiseMsgWindow = value;
+}
+
+void KopetePrefs::setRememberedMessages(int value)
+{
+ mRememberedMessages = value;
+}
+
+void KopetePrefs::setShowEvents(bool value)
+{
+ mShowEvents = value;
+}
+
+void KopetePrefs::setTrayflashNotify(bool value)
+{
+ mTrayflashNotify = value;
+}
+
+void KopetePrefs::setSpellCheck(bool value)
+{
+ mSpellCheck = value;
+}
+
+void KopetePrefs::setQueueUnreadMessages(bool value)
+{
+ mQueueUnreadMessages = value;
+}
+
+void KopetePrefs::setQueueOnlyHighlightedMessagesInGroupChats(bool value)
+{
+ mQueueOnlyHighlightedMessagesInGroupChats = value;
+}
+
+void KopetePrefs::setQueueOnlyMessagesOnAnotherDesktop(bool value)
+{
+ mQueueOnlyMessagesOnAnotherDesktop = value;
+}
+
+void KopetePrefs::setTrayflashNotifyLeftClickOpensMessage(bool value)
+{
+ mTrayflashNotifyLeftClickOpensMessage = value;
+}
+
+void KopetePrefs::setTrayflashNotifySetCurrentDesktopToChatView(bool value)
+{
+ mTrayflashNotifySetCurrentDesktopToChatView = value;
+}
+
+void KopetePrefs::setBalloonNotify(bool value)
+{
+ mBalloonNotify = value;
+}
+
+void KopetePrefs::setBalloonNotifyIgnoreClosesChatView(bool value)
+{
+ mBalloonNotifyIgnoreClosesChatView = value;
+}
+
+void KopetePrefs::setBalloonClose( bool value )
+{
+ mBalloonClose = value;
+}
+
+void KopetePrefs::setBalloonDelay( int value )
+{
+ mBalloonCloseDelay = value;
+}
+
+void KopetePrefs::setSoundIfAway(bool value)
+{
+ mSoundIfAway = value;
+}
+
+void KopetePrefs::setStylePath(const QString &stylePath)
+{
+ if(mStylePath != stylePath) mStylePathChanged = true;
+ _setStylePath(stylePath);
+}
+
+void KopetePrefs::_setStylePath(const QString &stylePath)
+{
+ mStylePath = stylePath;
+
+ // Fallback to default style if the directory doesn't exist
+ // or the value is empty.
+ if( !QFile::exists(stylePath) || stylePath.isEmpty() )
+ {
+ QString fallback;
+ fallback = QString(QString::fromLatin1("styles/%1/")).arg(QString::fromLatin1(KOPETE_DEFAULT_CHATSTYLE));
+ mStylePath = locate("appdata", fallback);
+ }
+}
+
+void KopetePrefs::setStyleVariant(const QString &variantPath)
+{
+ if(mStyleVariant != variantPath) mStyleVariantChanged = true;
+ mStyleVariant = variantPath;
+}
+
+void KopetePrefs::setFontFace( const QFont &value )
+{
+ if( value != mFontFace ) mWindowAppearanceChanged = true;
+ mFontFace = value;
+}
+
+void KopetePrefs::setTextColor( const QColor &value )
+{
+ if( value != mTextColor ) mWindowAppearanceChanged = true;
+ mTextColor = value;
+}
+
+void KopetePrefs::setBgColor( const QColor &value )
+{
+ if( value != mBgColor ) mWindowAppearanceChanged = true;
+ mBgColor = value;
+}
+
+void KopetePrefs::setLinkColor( const QColor &value )
+{
+ if( value != mLinkColor ) mWindowAppearanceChanged = true;
+ mLinkColor = value;
+}
+
+void KopetePrefs::setChatWindowPolicy(int value)
+{
+ mChatWindowPolicy = value;
+}
+
+void KopetePrefs::setTruncateContactNames( bool value )
+{
+ mTruncateContactNames = value;
+}
+
+void KopetePrefs::setMaxContactNameLength( int value )
+{
+ mMaxContactNameLength = value;
+}
+
+void KopetePrefs::setInterfacePreference(const QString &value)
+{
+ mInterfacePreference = value;
+}
+
+void KopetePrefs::setChatViewBufferSize( int value )
+{
+ mChatViewBufferSize = value;
+}
+
+void KopetePrefs::setHighlightBackground(const QColor &value)
+{
+ if( value != mHighlightBackground ) mWindowAppearanceChanged = true;
+ mHighlightBackground = value;
+}
+
+void KopetePrefs::setHighlightForeground(const QColor &value)
+{
+ if( value != mHighlightForeground ) mWindowAppearanceChanged = true;
+ mHighlightForeground = value;
+}
+
+void KopetePrefs::setHighlightEnabled(bool value)
+{
+ if( value != mHighlightEnabled ) mWindowAppearanceChanged = true;
+ mHighlightEnabled = value;
+}
+
+void KopetePrefs::setBgOverride(bool value)
+{
+ if( value != mBgOverride ) mMessageAppearanceChanged = true;
+ mBgOverride = value;
+}
+
+void KopetePrefs::setFgOverride(bool value)
+{
+ if( value != mFgOverride ) mMessageAppearanceChanged = true;
+ mFgOverride = value;
+}
+
+void KopetePrefs::setRtfOverride(bool value)
+{
+ if( value != mRtfOverride ) mMessageAppearanceChanged = true;
+ mRtfOverride = value;
+}
+
+void KopetePrefs::setShowTray(bool value)
+{
+ mShowTray = value;
+}
+
+
+QString KopetePrefs::fileContents(const QString &path)
+{
+ QString contents;
+ QFile file( path );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ QTextStream stream( &file );
+ contents = stream.read();
+ file.close();
+ }
+ return contents;
+}
+
+void KopetePrefs::setIdleContactColor(const QColor &value)
+{
+ if( value != mIdleContactColor ) mContactListAppearanceChanged = true;
+ mIdleContactColor = value;
+}
+
+void KopetePrefs::setRichText(bool value)
+{
+ mRichText=value;
+}
+
+void KopetePrefs::setToolTipContents(const QStringList &value)
+{
+ mToolTipContents=value;
+}
+
+void KopetePrefs::setContactListIndentContacts( bool v )
+{
+ if( v != mContactListIndentContacts ) mContactListAppearanceChanged = true;
+ mContactListIndentContacts = v;
+}
+
+void KopetePrefs::setContactListDisplayMode( ContactDisplayMode v )
+{
+ if( v != mContactListDisplayMode ) mContactListAppearanceChanged = true;
+ mContactListDisplayMode = v;
+}
+
+void KopetePrefs::setContactListIconMode( IconDisplayMode v )
+{
+ if( v != mContactListIconMode ) mContactListAppearanceChanged = true;
+ mContactListIconMode = v;
+}
+
+void KopetePrefs::setContactListUseCustomFonts( bool v )
+{
+ if( v != mContactListUseCustomFonts ) mContactListAppearanceChanged = true;
+ mContactListUseCustomFonts = v;
+}
+
+void KopetePrefs::setContactListCustomNormalFont( const QFont & v )
+{
+ if( v != mContactListNormalFont ) mContactListAppearanceChanged = true;
+ mContactListNormalFont = v;
+}
+
+void KopetePrefs::setContactListCustomSmallFont( const QFont & v )
+{
+ if( v != mContactListSmallFont ) mContactListAppearanceChanged = true;
+ mContactListSmallFont = v;
+}
+
+QFont KopetePrefs::contactListSmallFont() const
+{
+ if ( mContactListUseCustomFonts )
+ return contactListCustomSmallFont();
+ QFont smallFont = KGlobalSettings::generalFont();
+ if ( smallFont.pixelSize() != -1 )
+ smallFont.setPixelSize( (smallFont.pixelSize() * 3) / 4 );
+ else
+ smallFont.setPointSizeFloat( smallFont.pointSizeFloat() * 0.75 );
+ return smallFont;
+}
+
+void KopetePrefs::setContactListGroupNameColor( const QColor & v )
+{
+ if( v != mContactListGroupNameColor ) mContactListAppearanceChanged = true;
+ mContactListGroupNameColor = v;
+}
+
+void KopetePrefs::setContactListAnimation( bool n )
+{
+ if( n != mContactListAnimation ) mContactListAppearanceChanged = true;
+ mContactListAnimation = n;
+}
+
+void KopetePrefs::setContactListFading( bool n )
+{
+ if( n != mContactListFading ) mContactListAppearanceChanged = true;
+ mContactListFading = n;
+}
+
+void KopetePrefs::setContactListFolding( bool n )
+{
+ if( n != mContactListFolding ) mContactListAppearanceChanged = true;
+ mContactListFolding = n;
+}
+
+void KopetePrefs::setContactListAutoHide( bool n )
+{
+ if( n != mContactListAutoHide ) mContactListAppearanceChanged = true;
+ mContactListAutoHide = n;
+}
+
+void KopetePrefs::setContactListAutoHideTimeout( unsigned int n )
+{
+ if( n != mContactListAutoHideTimeout ) mContactListAppearanceChanged = true;
+ mContactListAutoHideTimeout = n;
+}
+
+void KopetePrefs::setReconnectOnDisconnect( bool newSetting )
+{
+ mReconnectOnDisconnect = newSetting;
+}
+
+void KopetePrefs::setAutoConnect(bool b)
+{
+ mAutoConnect=b;
+}
+
+void KopetePrefs::setEmoticonsRequireSpaces( bool b )
+{
+ if( mEmoticonsRequireSpaces != b )
+ {
+ mMessageAppearanceChanged = true;
+ mContactListAppearanceChanged = true;
+ }
+ mEmoticonsRequireSpaces=b;
+}
+
+void KopetePrefs::setGroupConsecutiveMessages( bool value )
+{
+ mGroupConsecutiveMessages = value;
+}
+#include "kopeteprefs.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteprefs.h b/kopete/libkopete/kopeteprefs.h
new file mode 100644
index 00000000..4a5162ff
--- /dev/null
+++ b/kopete/libkopete/kopeteprefs.h
@@ -0,0 +1,318 @@
+/*
+ kopeteprefs.cpp - Kopete Preferences Container-Class
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __KOPETEPREFS_H__
+#define __KOPETEPREFS_H__
+
+#include <qobject.h>
+#include <kdeversion.h>
+#include <qcolor.h>
+#include <qfont.h>
+
+#include "kopete_export.h"
+
+class KConfig;
+
+class KOPETE_EXPORT KopetePrefs : public QObject
+{
+ Q_OBJECT
+ // here so we can use Qt to translate enums<-->strings
+ Q_PROPERTY( ContactDisplayMode contactListDisplayMode READ contactListDisplayMode WRITE setContactListDisplayMode )
+ Q_PROPERTY( IconDisplayMode contactListIconMode READ contactListIconMode WRITE setContactListIconMode )
+ Q_ENUMS( ContactDisplayMode IconDisplayMode )
+
+public:
+ /**
+ * The prefs container-class is a singleton object. Use this method to retrieve
+ * the instance.
+ */
+ static KopetePrefs *prefs();
+
+ /**
+ * Reads all pref-variables from KConfig
+ * usually you don't need this as KopetePrefs loads settings
+ * when an instance is created
+ */
+ void load();
+
+ /**
+ * Stores all pref-variables into KConfig
+ */
+ void save();
+
+ QString iconTheme() const { return mIconTheme; }
+ bool useEmoticons() const { return mUseEmoticons; }
+ bool showOffline() const { return mShowOffline; }
+ bool showEmptyGroups() const { return mShowEmptyGroups; }
+ bool treeView() const { return mTreeView; }
+ bool sortByGroup() const { return mSortByGroup; }
+ bool greyIdleMetaContacts() const { return mGreyIdle; }
+ bool startDocked() const { return mStartDocked; }
+ bool useQueue() const { return mUseQueue; }
+ bool useStack() const { return mUseStack; }
+ bool raiseMsgWindow() const{ return mRaiseMsgWindow; }
+ bool showEvents() const{ return mShowEvents; }
+ bool trayflashNotify() const { return mTrayflashNotify; }
+ bool spellCheck() const { return mSpellCheck; }
+ bool queueUnreadMessages() const { return mQueueUnreadMessages; }
+ bool queueOnlyHighlightedMessagesInGroupChats() const { return mQueueOnlyHighlightedMessagesInGroupChats; }
+ bool queueOnlyMessagesOnAnotherDesktop() const { return mQueueOnlyMessagesOnAnotherDesktop; }
+ bool trayflashNotifyLeftClickOpensMessage() const { return mTrayflashNotifyLeftClickOpensMessage; }
+ bool trayflashNotifySetCurrentDesktopToChatView() const { return mTrayflashNotifySetCurrentDesktopToChatView; }
+ bool balloonNotify() const { return mBalloonNotify; }
+ bool balloonNotifyIgnoreClosesChatView() const { return mBalloonNotifyIgnoreClosesChatView; }
+ bool balloonClose() const { return mBalloonClose; }
+ int balloonCloseDelay() const { return mBalloonCloseDelay; }
+ bool soundIfAway() const { return mSoundIfAway; }
+ int chatViewBufferSize() const { return mChatViewBufferSize; }
+ int rememberedMessages() const { return mRememberedMessages; }
+ const QColor &highlightBackground() const { return mHighlightBackground; }
+ const QColor &highlightForeground() const { return mHighlightForeground; }
+ const QColor &textColor() const { return mTextColor; }
+ const QColor &bgColor() const { return mBgColor; }
+ const QColor &linkColor() const { return mLinkColor; }
+ const QFont &fontFace() const { return mFontFace; }
+ const QColor &idleContactColor() const { return mIdleContactColor; }
+ bool highlightEnabled() const { return mHighlightEnabled; }
+ bool bgOverride() const { return mBgOverride; }
+ bool fgOverride() const { return mFgOverride; }
+ bool rtfOverride() const { return mRtfOverride; }
+
+ QString interfacePreference() const { return mInterfacePreference; }
+ bool showTray() const { return mShowTray; }
+ bool richText() const { return mRichText; }
+ bool chatWShowSend() const { return mChatWShowSend; }
+ bool autoConnect() const { return mAutoConnect; }
+
+ int chatWindowPolicy() const { return mChatWindowPolicy; }
+
+ //Styles
+ QString defaultTheme() const { return QString::fromLatin1("Default"); }
+ //for Adium (xhtml+css)
+ QString stylePath() const { return mStylePath; }
+ QString styleVariant() const { return mStyleVariant; }
+
+ QStringList toolTipContents() const { return mToolTipContents; }
+
+ ///
+ enum ContactDisplayMode { Classic, RightAligned, Detailed, Yagami, Default = Classic };
+ ///
+ enum IconDisplayMode { IconPic, PhotoPic, IconDefault = IconPic };
+ bool contactListIndentContacts() const { return mContactListIndentContacts; }
+ ContactDisplayMode contactListDisplayMode() const { return mContactListDisplayMode; }
+ IconDisplayMode contactListIconMode() const { return mContactListIconMode; }
+ bool contactListUseCustomFonts() const { return mContactListUseCustomFonts; }
+ QFont contactListCustomNormalFont() const { return mContactListNormalFont; }
+ QFont contactListCustomSmallFont() const { return mContactListSmallFont; }
+ QFont contactListSmallFont() const;
+ QColor contactListGroupNameColor() const { return mContactListGroupNameColor; }
+ bool contactListAnimation() const { return mContactListAnimation; }
+ bool contactListFading() const { return mContactListFading; }
+ bool contactListFolding() const { return mContactListFolding; }
+ bool contactListAutoHide() const { return mContactListAutoHide; }
+ unsigned int contactListAutoHideTimeout() const { return mContactListAutoHideTimeout; }
+
+ bool reconnectOnDisconnect() const { return mReconnectOnDisconnect; }
+
+ bool truncateContactNames() const { return mTruncateContactNames; }
+ int maxConactNameLength() const { return mMaxContactNameLength; }
+ bool emoticonsRequireSpaces() const { return mEmoticonsRequireSpaces; }
+ bool groupConsecutiveMessages() const { return mGroupConsecutiveMessages; }
+
+ void setIconTheme(const QString &value);
+ void setUseEmoticons(bool value);
+ void setShowOffline(bool value);
+ void setShowEmptyGroups(bool value);
+ void setTreeView(bool);
+ void setSortByGroup(bool);
+ void setGreyIdleMetaContacts(bool);
+ void setStartDocked(bool);
+ void setUseQueue(bool);
+ void setUseStack(bool);
+ void setRaiseMsgWindow(bool);
+ void setShowEvents(bool);
+ void setTrayflashNotify(bool);
+ void setSpellCheck(bool);
+ void setQueueUnreadMessages(bool);
+ void setQueueOnlyHighlightedMessagesInGroupChats(bool);
+ void setQueueOnlyMessagesOnAnotherDesktop(bool);
+ void setTrayflashNotifyLeftClickOpensMessage(bool);
+ void setTrayflashNotifySetCurrentDesktopToChatView(bool);
+ void setBalloonNotify(bool);
+ void setBalloonNotifyIgnoreClosesChatView(bool);
+ void setSoundIfAway(bool);
+ void setBeepNotify(bool);
+ void setChatWindowPolicy(int);
+ void setStylePath(const QString &);
+ void setStyleVariant(const QString &);
+ void setChatViewBufferSize(int);
+ void setHighlightBackground(const QColor &);
+ void setHighlightForeground(const QColor &);
+ void setHighlightEnabled(bool);
+ void setBgOverride(bool);
+ void setFgOverride(bool);
+ void setRtfOverride(bool);
+ void setInterfacePreference(const QString &viewPlugin);
+ void setTextColor(const QColor &);
+ void setBgColor(const QColor &);
+ void setLinkColor(const QColor &);
+ void setFontFace(const QFont &);
+ void setIdleContactColor(const QColor &);
+ void setShowTray(bool);
+ void setRichText(bool);
+ void setRememberedMessages(int);
+ void setToolTipContents(const QStringList &);
+ void setContactListIndentContacts( bool v );
+ void setContactListDisplayMode( ContactDisplayMode v );
+ void setContactListIconMode( IconDisplayMode v );
+ void setContactListUseCustomFonts( bool v );
+ void setContactListCustomNormalFont( const QFont & v );
+ void setContactListCustomSmallFont( const QFont & v );
+ void setContactListGroupNameColor( const QColor & v );
+ void setContactListAnimation( bool );
+ void setContactListFading( bool );
+ void setContactListFolding( bool );
+ void setContactListAutoHide( bool );
+ void setContactListAutoHideTimeout( unsigned int );
+ void setReconnectOnDisconnect( bool newSetting );
+ void setTruncateContactNames( bool );
+ void setMaxContactNameLength( int );
+ void setAutoConnect( bool );
+ void setEmoticonsRequireSpaces( bool );
+ void setBalloonClose( bool );
+ void setBalloonDelay( int );
+ void setGroupConsecutiveMessages( bool );
+
+signals:
+ /**
+ * Emitted when config gets saved by save()
+ */
+ void saved();
+ /**
+ * Emitted when config gets saved by save() and a certain
+ * setting has changed.
+ * Naming scheme is the same as with the config vars.
+ */
+ void windowAppearanceChanged();
+ void messageAppearanceChanged();
+ void contactListAppearanceChanged();
+ /**
+ * Emitted when chat Window Style changed.
+ * @param stylePath New stylePath
+ */
+ void styleChanged(const QString &stylePath);
+ /**
+ * Emitted when ChatWindowStyle variant changed.
+ * @param variantPath New variant Path.
+ */
+ void styleVariantChanged(const QString &variantPath);
+
+private:
+ /**
+ * Private constructor: we are a singleton
+ */
+ KopetePrefs();
+
+ /**
+ * Our instance
+ */
+ static KopetePrefs *s_prefs;
+
+ KConfig *config;
+
+ QString mIconTheme;
+ bool mUseEmoticons;
+ bool mShowOffline;
+ bool mShowEmptyGroups;
+ bool mGreyIdle;
+ bool mTreeView;
+ bool mSortByGroup;
+ bool mStartDocked;
+ bool mUseQueue;
+ bool mUseStack;
+ bool mRaiseMsgWindow;
+ bool mShowEvents;
+ bool mTrayflashNotify;
+ bool mSpellCheck;
+ bool mQueueUnreadMessages;
+ bool mQueueOnlyHighlightedMessagesInGroupChats;
+ bool mQueueOnlyMessagesOnAnotherDesktop;
+ bool mTrayflashNotifyLeftClickOpensMessage;
+ bool mTrayflashNotifySetCurrentDesktopToChatView;
+ bool mBalloonNotify;
+ bool mBalloonNotifyIgnoreClosesChatView;
+ bool mBalloonClose;
+ int mBalloonCloseDelay;
+ bool mSoundIfAway;
+ int mRememberedMessages;
+ QString mInterfacePreference;
+ int mChatViewBufferSize;
+ QColor mHighlightBackground;
+ QColor mHighlightForeground;
+ QColor mTextColor;
+ QColor mBgColor;
+ QColor mLinkColor;
+ QFont mFontFace;
+ QColor mIdleContactColor;
+ bool mHighlightEnabled;
+ bool mBgOverride;
+ bool mFgOverride;
+ bool mRtfOverride;
+ bool mShowTray;
+ bool mWindowAppearanceChanged;
+ bool mMessageAppearanceChanged;
+ bool mContactListAppearanceChanged;
+ bool mChatWShowSend;
+ bool mAutoConnect;
+
+ int mChatWindowPolicy;
+
+ bool mTruncateContactNames;
+ int mMaxContactNameLength;
+
+ bool mRichText;
+
+ // xhtml+css
+ //for Adium (xhtml+css)
+ QString mStylePath;
+ QString mStyleVariant;
+ bool mStylePathChanged;
+ bool mStyleVariantChanged;
+
+ QStringList mToolTipContents;
+
+ bool mContactListIndentContacts;
+ ContactDisplayMode mContactListDisplayMode;
+ IconDisplayMode mContactListIconMode;
+ bool mContactListUseCustomFonts;
+ QFont mContactListNormalFont;
+ QFont mContactListSmallFont;
+ QColor mContactListGroupNameColor;
+ bool mContactListAnimation;
+ bool mContactListFading;
+ bool mContactListFolding;
+ bool mContactListAutoHide;
+ unsigned int mContactListAutoHideTimeout;
+
+ bool mReconnectOnDisconnect;
+ bool mEmoticonsRequireSpaces;
+ bool mGroupConsecutiveMessages;
+
+ QString fileContents(const QString &path);
+ void _setStylePath (const QString &);
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteproperties.cpp b/kopete/libkopete/kopeteproperties.cpp
new file mode 100644
index 00000000..9009cd07
--- /dev/null
+++ b/kopete/libkopete/kopeteproperties.cpp
@@ -0,0 +1,50 @@
+/*
+ kopetetproperties.cpp - Kopete Properties
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteproperties.h"
+
+#include <kdebug.h>
+
+#include <qdom.h>
+#include <qvariant.h>
+#include <typeinfo>
+
+namespace Kopete {
+namespace Properties {
+
+// Keep as much type-independent code out of the templated stuff as we can
+// FIXME: shouldn't be inline
+void customPropertyDataIncorrectType( const char *name, const std::type_info &found, const std::type_info &expected )
+{
+ kdWarning(14010) << "data time mismatch for property data name " << name
+ << ". found: " << found.name() << ", expected: " << expected.name() << endl;
+}
+
+template<>
+int variantTo<int>(QVariant) { return 0; }
+//...
+
+QVariant variantFromXML(const QDomElement&)
+{
+ return QVariant();
+}
+
+void variantToXML(QVariant v, QDomElement &)
+{
+}
+
+} // namespace Properties
+} // namespace Kopete
diff --git a/kopete/libkopete/kopeteproperties.h b/kopete/libkopete/kopeteproperties.h
new file mode 100644
index 00000000..bfeaedea
--- /dev/null
+++ b/kopete/libkopete/kopeteproperties.h
@@ -0,0 +1,350 @@
+/*
+ kopeteproperties.h - Kopete Properties
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPROPERTIES_H
+#define KOPETEPROPERTIES_H
+
+#include <qasciidict.h>
+
+#include <typeinfo>
+
+class QString;
+class QVariant;
+class QDomElement;
+
+namespace Kopete
+{
+
+/**
+ * Contains the classes forming Kopete's Properties system.
+ *
+ * @todo Explain more, give examples.
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+namespace Properties
+{
+
+//BEGIN core functionality
+
+/**
+ * @brief Property-type-independent base class for properties
+ *
+ * The base class for all properties of any type which can be set or got for @p Parent
+ * objects. It is rare to need to use this class directly. Usually you will want to use
+ * the @ref Property derived class, or dynamic_cast the PropertyBase object to another interface.
+ *
+ * @see Property UserVisible XMLSerializable StringSerializable
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+template<class Parent>
+class PropertyBase
+{
+public:
+ /**
+ * Returns the name of the property. This name should uniquely identify this property
+ * within the type Parent, and will be used for persistently identifying this property.
+ *
+ * For core properties, the chosen name should not contain any slash characters. For
+ * properties defined in plugins kept in Kopete's CVS, the name should be of the form
+ * pluginName/propertyName. For third-party plugins, please use a URL with a host which
+ * you own, such as "http://my-host.com/kopete/properties/groupId".
+ *
+ * @return the name of this property.
+ */
+ virtual const char *name() const = 0;
+};
+
+/**
+ * @brief Property-type-dependent base class for properties
+ *
+ * This class represents a property of type @p Type applicable to @p Parent objects. Usage
+ * of this class is usually as simple as:
+ *
+ * \code
+ * SomeParent *propertyContainer = ...
+ * Property<SomeParent,QString> &myProperty = ...
+ * QString value = propertyContainer->property(myProperty);
+ * propertyContainer->setProperty(myProperty, "hello");
+ * \endcode
+ *
+ * You should never need to call functions in this class directly.
+ */
+template<class Parent, typename Type>
+class Property : public PropertyBase<Parent>
+{
+public:
+ /**
+ * Returns the value of this property in the object @p parent.
+ */
+ virtual Type get( const Parent *parent ) const = 0;
+ /**
+ * Sets the value of this property in the object @p parent.
+ */
+ virtual void set( Parent *, const Type & ) const = 0;
+};
+
+/**
+ * @brief Base class for property data objects
+ *
+ * Some property objects want to store property-specific data in their parent objects.
+ * To support that, subclasses of this class are permitted to be stored. Once passed
+ * to the @ref PropertyStorage object via @ref PropertyStorage::setCustomPropertyData,
+ * the @ref PropertyStorage object owns the PropertyData, and will delete it when it
+ * is no longer needed.
+ */
+struct PropertyData
+{
+ virtual ~PropertyData() {}
+};
+
+/**
+ * @brief Storage object for PropertyData objects
+ *
+ * This class is responsible for storing PropertyData-derived data objects for properties.
+ * This is the non-templated part of the @ref WithProperties class, split out into its own
+ * class to eliminate the template bloat.
+ */
+class PropertyStorage
+{
+ typedef QAsciiDict<PropertyData> PropertyDict;
+ // setCustomPropertyData can be called on a const object, allowing the
+ // guarantee that DataProperty::data() never returns 0.
+ mutable PropertyDict _storage;
+
+public:
+ PropertyStorage() { _storage.setAutoDelete( true ); }
+
+ /**
+ * Sets the stored property data with name @p name to be @p data.
+ *
+ * @note The @p name argument should usually be the name of the property which the data
+ * is being stored for. However, if properties wish to share data, they may choose to
+ * name their custom data differently. Names are bound by the same rules as are laid out
+ * for naming properties in PropertyBase<Parent>::name.
+ */
+ void setCustomPropertyData( const char *name, PropertyData *data ) const { _storage.replace( name, data ); }
+
+ /**
+ * Gets the stored property data with name @p name. Returns a null
+ * pointer if no data has been stored for that property.
+ */
+ PropertyData *getCustomPropertyData( const char *name ) const { return _storage[name]; }
+};
+
+/**
+ * @brief Base class for classes to which properties can be applied
+ *
+ * This class provides support for properties to another class. If you want your class
+ * to support properties, derive from this passing your class as the Parent parameter:
+ *
+ * \code
+ * class YourClass : public WithProperties<YourClass> { ... };
+ * \endcode
+ *
+ * You will also need to explicitly specialise the propertyCreated() member function to
+ * load property data upon creation of a new property object.
+ */
+template<class Parent>
+class WithProperties : public PropertyStorage
+{
+public:
+ /**
+ * Get the value of property @p prop in this object.
+ * @param prop the Property object representing the property to get
+ */
+ template<typename T>
+ T property( Property<Parent,T> const &prop ) { return prop.get( static_cast<Parent*>(this) ); }
+ /**
+ * Set the value of property @p prop in this object.
+ * @param prop the Property object representing the property to get
+ * @param value the value to set the property to
+ */
+ template<typename T>
+ void setProperty( Property<Parent,T> const &prop, const T &value ) { prop.set( static_cast<Parent*>(this), value ); }
+
+ /**
+ * Called when a property is created; loads the Parent object's data into the property.
+ *
+ * @note Derived classes must explicitly specialize this to load the property's data into
+ * every object of this type.
+ */
+ static void propertyCreated( const PropertyBase<Parent> &property );
+};
+
+//END core functionality
+
+//BEGIN interfaces
+
+/**
+ * @brief An interface for user-visible properties
+ * @todo document
+ */
+template<class Parent>
+struct UserVisible
+{
+ virtual QString userText( Parent * ) = 0;
+ virtual QString label() = 0;
+ virtual QString icon() = 0;
+};
+
+/**
+ * @brief An interface for properties which can be serialized as XML
+ * @todo document
+ */
+template<class Parent>
+struct XMLSerializable
+{
+ virtual void fromXML( Parent *, const QDomElement & ) = 0;
+ virtual void toXML( const Parent *, QDomElement & ) = 0;
+};
+
+/**
+ * @brief An interface for properties which can be serialized as strings
+ * @todo document
+ */
+template<class Parent>
+struct StringSerializable
+{
+ virtual void fromString( Parent *, const QString & ) = 0;
+ virtual QString toString( const Parent * ) = 0;
+};
+
+//END interfaces
+
+//BEGIN convenience classes
+
+/**
+ * @internal Display a warning message when the wrong type of property data is found
+ */
+void customPropertyDataIncorrectType( const char *name, const std::type_info &found, const std::type_info &expected );
+
+/**
+ * @brief Convenience implementation of a Property that stores PropertyData
+ *
+ * A property for objects of type @p Parent, that stores data in the class @p Data.
+ * @p Data must be derived from @ref PropertyBase, or your code will not compile.
+ */
+template<class Parent, typename Type, class Data>
+class DataProperty : public Property<Parent,Type>
+{
+public:
+ Data *data( const Parent *c ) const
+ {
+ PropertyData *pd = c->getCustomPropertyData( this->name() );
+ Data *data = dynamic_cast<Data*>(pd);
+ if ( !data )
+ {
+ if ( pd )
+ customPropertyDataIncorrectType( this->name(), typeid(*pd), typeid(Data) );
+ data = new Data;
+ c->setCustomPropertyData( this->name(), data );
+ }
+ return data;
+ }
+};
+
+/**
+ * @brief Convenience implementation of a PropertyData subclass which stores a single datum
+ *
+ * If a @ref Property needs to store only a single value in an object, using this
+ * class is simpler than deriving from @ref PropertyData yourself. The value will
+ * be default-constructed (which means for numeric types and pointers it will be
+ * set to 0).
+ */
+template<typename T>
+struct SimplePropertyData : public PropertyData
+{
+ SimplePropertyData() : value() {}
+ T value;
+};
+
+/**
+ * @brief Convenience implementation of a Property which stores a single datum as PropertyData
+ *
+ * This convenience class implements the @ref Property interface by simply storing and
+ * retrieving the datum from PropertyData. This class does not provide any serialization
+ * of the data.
+ *
+ * @note You will need to derive from this class to use it; the @ref name function is
+ * still pure virtual.
+ */
+template<class Parent, typename Type>
+class SimpleDataProperty : public DataProperty<Parent,Type,SimplePropertyData<Type> >
+{
+public:
+ Type get( const Parent *p ) const { return data(p)->value; }
+ void set( Parent *p, const Type &v ) const { data(p)->value = v; }
+};
+
+/**
+ * Move somewhere else
+ * @{
+ */
+
+/**
+ * Explicitly specialised for all types QVariant supports
+ */
+template<class T> T variantTo(QVariant);
+
+QVariant variantFromXML(const QDomElement&);
+void variantToXML(QVariant v, QDomElement &);
+
+/**
+ * @}
+ */
+
+/**
+ * @brief Convenience implementation of XMLSerializable in terms of QVariants
+ *
+ * This class provides XML serialization for data that can be stored in a QVariant. You
+ * will need to multiply-inherit from this class and (usually indirectly) from @ref Property.
+ *
+ * You can combine this class with other convenience classes such as SimpleDataProperty
+ * like this:
+ *
+ * \code
+ * class ContactNickNameProperty
+ * : public SimpleDataProperty<Contact,QString>
+ * , XMLProperty<ContactNickNameProperty,Contact,QString>
+ * {
+ * public:
+ * const char *name() const { return "nickName"; }
+ * };
+ * \endcode
+ */
+template<class Derived, class Parent, typename Type>
+class XMLProperty : public XMLSerializable<Parent>
+{
+public:
+ void fromXML( Parent *t, const QDomElement &e )
+ {
+ static_cast<Derived*>(this)->set(t, variantTo<Type>(variantFromXML(e)));
+ }
+ void toXML( const Parent *t, QDomElement &e )
+ {
+ variantToXML(QVariant(static_cast<Derived*>(this)->get(t)),e);
+ }
+};
+
+//END convenience classes
+
+} // namespace Properties
+
+} // namespace Kopete
+
+#endif
diff --git a/kopete/libkopete/kopeteprotocol.cpp b/kopete/libkopete/kopeteprotocol.cpp
new file mode 100644
index 00000000..7854a1a3
--- /dev/null
+++ b/kopete/libkopete/kopeteprotocol.cpp
@@ -0,0 +1,340 @@
+/*
+ kopeteprotocol.cpp - Kopete Protocol
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteprotocol.h"
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include <qdict.h>
+
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+#include "kopeteglobal.h"
+#include "kopetecontactproperty.h"
+#include "kopetemetacontact.h"
+
+namespace Kopete
+{
+
+class Protocol::Private
+{
+public:
+ bool unloading;
+ int capabilities;
+ /*
+ * Make sure we always have a lastSeen and a fullname property as long as
+ * a protocol is loaded
+ */
+ ContactPropertyTmpl mStickLastSeen;
+ ContactPropertyTmpl mStickFullName;
+
+ Kopete::OnlineStatus accountNotConnectedStatus;
+};
+
+Protocol::Protocol( KInstance *instance, QObject *parent, const char *name )
+: Plugin( instance, parent, name )
+{
+ d = new Private;
+ d->mStickLastSeen = Global::Properties::self()->lastSeen();
+ d->mStickFullName = Global::Properties::self()->fullName();
+ d->unloading = false;
+ d->capabilities = 0;
+ d->accountNotConnectedStatus = Kopete::OnlineStatus( Kopete::OnlineStatus::Unknown, 0, this, Kopete::OnlineStatus::AccountOffline, QString::fromLatin1( "account_offline_overlay" ), i18n( "Account Offline" ) );
+}
+
+Protocol::~Protocol()
+{
+ // Remove all active accounts
+ QDict<Account> accounts = AccountManager::self()->accounts( this );
+ if ( !accounts.isEmpty() )
+ {
+ kdWarning( 14010 ) << k_funcinfo << "Deleting protocol with existing accounts! Did the account unloading go wrong?" << endl;
+
+ for( QDictIterator<Account> it( accounts ); it.current() ; ++it )
+ delete *it;
+ }
+
+ delete d;
+}
+
+unsigned int Protocol::capabilities() const
+{
+ return d->capabilities;
+}
+
+void Protocol::setCapabilities( unsigned int capabilities )
+{
+ d->capabilities = capabilities;
+}
+
+
+Kopete::OnlineStatus Protocol::accountOfflineStatus() const
+{
+ return d->accountNotConnectedStatus;
+}
+
+void Protocol::slotAccountOnlineStatusChanged( Contact *self )
+{//slot connected in aboutToUnload
+ if ( !self || !self->account() || self->account()->isConnected())
+ return;
+ // some protocols change status several times during shutdown. We should only call deleteLater() once
+ disconnect( self, 0, this, 0 );
+
+ connect( self->account(), SIGNAL(accountDestroyed(const Kopete::Account* )),
+ this, SLOT( slotAccountDestroyed( ) ) );
+
+ self->account()->deleteLater();
+}
+
+void Protocol::slotAccountDestroyed( )
+{
+ QDict<Account> dict = AccountManager::self()->accounts( this );
+ if ( dict.isEmpty() )
+ {
+ // While at this point we are still in a stack trace from the destroyed
+ // account it's safe to emit readyForUnload already, because it uses a
+ // deleteLater rather than a delete for exactly this reason, to keep the
+ // API managable
+ emit( readyForUnload() );
+ }
+}
+
+void Protocol::aboutToUnload()
+{
+
+ d->unloading = true;
+
+ // Disconnect all accounts
+ QDict<Account> accounts = AccountManager::self()->accounts( this );
+
+ if ( accounts.isEmpty() )
+ emit readyForUnload();
+ else for ( QDictIterator<Account> it( accounts ); it.current() ; ++it )
+ {
+ if ( it.current()->myself() && it.current()->myself()->isOnline() )
+ {
+ kdDebug( 14010 ) << k_funcinfo << it.current()->accountId() <<
+ " is still connected, disconnecting..." << endl;
+
+ QObject::connect( it.current()->myself(),
+ SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT( slotAccountOnlineStatusChanged( Kopete::Contact * ) ) );
+ it.current()->disconnect();
+ }
+ else
+ {
+ // Remove account, it's already disconnected
+ kdDebug( 14010 ) << k_funcinfo << it.current()->accountId() <<
+ " is already disconnected, deleting..." << endl;
+
+ QObject::connect( it.current(), SIGNAL( accountDestroyed( const Kopete::Account* ) ),
+ this, SLOT( slotAccountDestroyed( ) ) );
+ it.current()->deleteLater();
+ }
+ }
+}
+
+
+
+void Protocol::slotMetaContactAboutToSave( MetaContact *metaContact )
+{
+ QMap<QString, QString> serializedData, sd;
+ QMap<QString, QString> addressBookData, ad;
+ QMap<QString, QString>::Iterator it;
+
+ //kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: protocol " << pluginId() << ": serializing " << metaContact->displayName() << endl;
+
+ QPtrList<Contact> contacts=metaContact->contacts();
+ for (Contact *c=contacts.first() ; c ; c=contacts.next() )
+ {
+ if( c->protocol()->pluginId() != pluginId() )
+ continue;
+
+ sd.clear();
+ ad.clear();
+
+ // Preset the contactId and displayName, if the plugin doesn't want to save
+ // them, or use its own format, it can call clear() on the provided list
+ sd[ QString::fromLatin1( "contactId" ) ] = c->contactId();
+ //TODO(nick) remove
+ sd[ QString::fromLatin1( "displayName" ) ] = c->property(Global::Properties::self()->nickName()).value().toString();
+ if(c->account())
+ sd[ QString::fromLatin1( "accountId" ) ] = c->account()->accountId();
+
+ // If there's an index field preset it too
+ QString index = c->protocol()->addressBookIndexField();
+ if( !index.isEmpty() )
+ ad[ index ] = c->contactId();
+
+ c->serializeProperties( sd );
+ c->serialize( sd, ad );
+
+ // Merge the returned fields with what we already (may) have
+ for( it = sd.begin(); it != sd.end(); ++it )
+ {
+ // The Unicode chars E000-F800 are non-printable and reserved for
+ // private use in applications. For more details, see also
+ // http://www.unicode.org/charts/PDF/UE000.pdf.
+ // Inside libkabc the use of QChar( 0xE000 ) has been standardized
+ // as separator for the string lists, use this also for the 'normal'
+ // serialized data.
+ if( serializedData.contains( it.key() ) )
+ serializedData[ it.key() ] = serializedData[ it.key() ] + QChar( 0xE000 ) + it.data();
+ else
+ serializedData[ it.key() ] = it.data();
+ }
+
+ for( it = ad.begin(); it != ad.end(); ++it )
+ {
+ if( addressBookData.contains( it.key() ) )
+ addressBookData[ it.key() ] = addressBookData[ it.key() ] + QChar( 0xE000 ) + it.data();
+ else
+ addressBookData[ it.key() ] = it.data();
+ }
+ }
+
+ // Pass all returned fields to the contact list
+ //if( !serializedData.isEmpty() ) //even if we are empty, that mean there are no contact, so remove old value
+ metaContact->setPluginData( this, serializedData );
+
+ for( it = addressBookData.begin(); it != addressBookData.end(); ++it )
+ {
+ //kdDebug( 14010 ) << "Protocol::metaContactAboutToSave: addressBookData: key: " << it.key() << ", data: " << it.data() << endl;
+ // FIXME: This is a terrible hack to check the key name for the phrase "messaging/"
+ // to indicate what app name to use, but for now it's by far the easiest
+ // way to get this working.
+ // Once all this is in CVS and the actual storage in libkabc is working
+ // we can devise a better API, but with the constantly changing
+ // requirements every time I learn more about kabc I'd better no touch
+ // the API yet - Martijn
+ if( it.key().startsWith( QString::fromLatin1( "messaging/" ) ) )
+ {
+ metaContact->setAddressBookField( this, it.key(), QString::fromLatin1( "All" ), it.data() );
+// kdDebug(14010) << k_funcinfo << "metaContact->setAddressBookField( " << this << ", " << it.key() << ", \"All\", " << it.data() << " );" << endl;
+ }
+ else
+ metaContact->setAddressBookField( this, QString::fromLatin1( "kopete" ), it.key(), it.data() );
+ }
+}
+
+void Protocol::deserialize( MetaContact *metaContact, const QMap<QString, QString> &data )
+{
+ /*kdDebug( 14010 ) << "Protocol::deserialize: protocol " <<
+ pluginId() << ": deserializing " << metaContact->displayName() << endl;*/
+
+ QMap<QString, QStringList> serializedData;
+ QMap<QString, QStringList::Iterator> serializedDataIterators;
+ QMap<QString, QString>::ConstIterator it;
+ for( it = data.begin(); it != data.end(); ++it )
+ {
+ serializedData[ it.key() ] = QStringList::split( QChar( 0xE000 ), it.data(), true );
+ serializedDataIterators[ it.key() ] = serializedData[ it.key() ].begin();
+ }
+
+ uint count = serializedData[QString::fromLatin1("contactId")].count();
+
+ // Prepare the independent entries to pass to the plugin's implementation
+ for( uint i = 0; i < count ; i++ )
+ {
+ QMap<QString, QString> sd;
+ QMap<QString, QStringList::Iterator>::Iterator serializedDataIt;
+ for( serializedDataIt = serializedDataIterators.begin(); serializedDataIt != serializedDataIterators.end(); ++serializedDataIt )
+ {
+ sd[ serializedDataIt.key() ] = *( serializedDataIt.data() );
+ ++( serializedDataIt.data() );
+ }
+
+ const QString& accountId=sd[ QString::fromLatin1( "accountId" ) ];
+ // myself was allowed in the contactlist in old version of kopete.
+ // But if one keep it on the contactlist now, it may conflict witht he myself metacontact.
+ // So ignore it
+ if(accountId == sd[ QString::fromLatin1( "contactId" ) ] )
+ {
+ kdDebug( 14010 ) << k_funcinfo << "Myself contact was on the contactlist.xml for account " << accountId << ". Ignore it" << endl;
+ continue;
+ }
+
+ // FIXME: This code almost certainly breaks when having more than
+ // one contact in a meta contact. There are solutions, but
+ // they are all hacky and the API needs revision anyway (see
+ // FIXME a few lines below :), so I'm not going to add that
+ // for now.
+ // Note that even though it breaks, the current code will
+ // never notice, since none of the plugins use the address
+ // book data in the deserializer yet, only when serializing.
+ // - Martijn
+ QMap<QString, QString> ad;
+ QStringList kabcFields = addressBookFields();
+ for( QStringList::Iterator fieldIt = kabcFields.begin(); fieldIt != kabcFields.end(); ++fieldIt )
+ {
+ // FIXME: This hack is even more ugly, and has the same reasons as the similar
+ // hack in the serialize code.
+ // Once this code is actually capable of talking to kabc this hack
+ // should be removed ASAP! - Martijn
+ if( ( *fieldIt ).startsWith( QString::fromLatin1( "messaging/" ) ) )
+ ad[ *fieldIt ] = metaContact->addressBookField( this, *fieldIt, QString::fromLatin1( "All" ) );
+ else
+ ad[ *fieldIt ] = metaContact->addressBookField( this, QString::fromLatin1( "kopete" ), *fieldIt );
+ }
+
+ // Check if we have an account id. If not we're deserializing a Kopete 0.6 contact
+ // (our our config is corrupted). Pick the first available account there. This
+ // might not be what you want for corrupted accounts, but it's correct for people
+ // who migrate from 0.6, as there's only one account in that case
+ if( accountId.isNull() )
+ {
+ QDict<Account> accounts = AccountManager::self()->accounts( this );
+ if ( accounts.count() > 0 )
+ {
+ sd[ QString::fromLatin1( "accountId" ) ] = QDictIterator<Account>( accounts ).currentKey();
+ }
+ else
+ {
+ kdWarning( 14010 ) << k_funcinfo <<
+ "No account available and account not set in " \
+ "contactlist.xml either!" << endl
+ << "Not deserializing this contact." << endl;
+ return;
+ }
+ }
+
+
+ Contact *c = deserializeContact( metaContact, sd, ad );
+ if (c) // should never be null but I do not like crashes
+ c->deserializeProperties( sd );
+ }
+}
+
+Contact *Protocol::deserializeContact(
+ MetaContact */*metaContact */,
+ const QMap<QString, QString> & /* serializedData */,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ /* Default implementation does nothing */
+ return 0;
+}
+
+
+} //END namespace Kopete
+
+#include "kopeteprotocol.moc"
+
diff --git a/kopete/libkopete/kopeteprotocol.desktop b/kopete/libkopete/kopeteprotocol.desktop
new file mode 100644
index 00000000..3b3762a4
--- /dev/null
+++ b/kopete/libkopete/kopeteprotocol.desktop
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=Kopete/Protocol
+X-KDE-Derived=Kopete/Plugin
+Comment=Kopete Protocol Plugin
+Comment[ar]=توصيلة بروتوكول KDE
+Comment[be]=Пратакол Kopete
+Comment[bg]=ПриÑтавка за протоколите на Kopete
+Comment[bn]=কপেট পà§à¦°à§‹à¦Ÿà§‹à¦•à¦² পà§à¦²à¦¾à¦—িন
+Comment[br]=Lugent komenad Kopete
+Comment[bs]=Kopete dodatak za protokol
+Comment[ca]=Connector de protocol per a Kopete
+Comment[cs]=Modul protokolu aplikace Kopete
+Comment[cy]=Ategyn Protocol Kopete
+Comment[da]=Kopete-protokol-plugin
+Comment[de]=Kopete Protokoll-Modul
+Comment[el]=ΠÏόσθετο Ï€Ïωτοκόλλου Kopete
+Comment[eo]=Kopete-Protokolkromaĵo
+Comment[es]=Complemento de protocolo de Kopete
+Comment[et]=Kopete protokolliplugin
+Comment[eu]=Kopete protocolo plugin-a
+Comment[fa]=وصلۀ قرارداد Kopete
+Comment[fi]=Kopeten yhteyskäytäntöliitännäinen
+Comment[fr]=Module de protocole pour Kopete
+Comment[ga]=Breiseán Phrótacal Kopete
+Comment[gl]=Plugin de protocolo para Kopete
+Comment[he]=תוסף פרוטוקול של Kopete
+Comment[hi]=के-ऑपà¥à¤Ÿà¥€ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤² पà¥à¤²à¤—इन
+Comment[hr]=Umetak za Kopete protokol
+Comment[hu]=Kopete protokollkezelő bővítőmodul
+Comment[is]=Ãforrit fyrir Kopete samskiptamátann
+Comment[it]=Plugin del protocollo di Kopete
+Comment[ja]=Kopete プロトコルプラグイン
+Comment[ka]=Kopete áƒáƒ¥áƒ›áƒ˜áƒ¡ მáƒáƒ“ული
+Comment[kk]=Kopete протоколының плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ពិធីការ Kopete
+Comment[lt]=Kopete protokolo įskiepis
+Comment[mk]=Приклучок за протокол во Kopete
+Comment[nb]=Programtillegg for Kopete-protokoll
+Comment[nds]=Kopete-Protokollmoduul
+Comment[ne]=कोपेट पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤² पà¥à¤²à¤—इन
+Comment[nl]=Kopete protocol-plugin
+Comment[nn]=Kopete-programtillegg for protokoll
+Comment[pl]=Wtyczka protokołu Kopete
+Comment[pt]='Plugin' de Protocolo do Kopete
+Comment[pt_BR]=Plugin do Protocolo do Kopete
+Comment[ro]=Modul de protocol Kopete
+Comment[ru]=Модуль протокола Kopete
+Comment[se]=Kopete protokollalassemoduvla
+Comment[sk]=Modul protokolu Kopete
+Comment[sl]=Vstavek za protokol za Kopete
+Comment[sr]=Kopete-ов прикључак за протокол
+Comment[sr@Latn]=Kopete-ov prikljuÄak za protokol
+Comment[sv]=Protokollinsticksprogram för Kopete
+Comment[ta]=Kopete விதிமà¯à®±à¯ˆ செரà¯à®•à®²à¯
+Comment[tg]=Модули Қарордоди Kopete
+Comment[tr]=Kopete İletişim Kuralı Eklentisi
+Comment[uk]=Втулок протоколу Ð´Ð»Ñ Kopete
+Comment[wa]=Tchôke-divins di protocole po Kopete
+Comment[zh_CN]=Kopete åè®®æ’件
+Comment[zh_HK]=Kopete 通訊å”定æ’件
+Comment[zh_TW]=Kopete å”定外掛程å¼
+
+[PropertyDef::X-Kopete-Messaging-Protocol]
+Type=QString
+
diff --git a/kopete/libkopete/kopeteprotocol.h b/kopete/libkopete/kopeteprotocol.h
new file mode 100644
index 00000000..805e00c2
--- /dev/null
+++ b/kopete/libkopete/kopeteprotocol.h
@@ -0,0 +1,269 @@
+/*
+ kopeteprotocol.h - Kopete Protocol
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart@ tiscalinet.be>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPROTOCOL_H
+#define KOPETEPROTOCOL_H
+
+#include "kopeteplugin.h"
+#include "kopeteonlinestatus.h"
+
+class KopeteEditAccountWidget;
+class AddContactPage;
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+class Contact;
+class MetaContact;
+class Account;
+
+/*namespace UI
+{
+ class EditAccountWidget;
+ class AddContactPage;
+}*/
+
+
+/**
+ * @brief base class of every protocols.
+ *
+ * A protocol is just a particular case of Plugin
+ *
+ * Protocol is an abstract class, you need to reimplement createNewAccount,
+ * createAddContactPage, createEditAccountWidget
+ *
+ *
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart @ tiscalinet.be>
+ */
+class KOPETE_EXPORT Protocol : public Plugin
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * @todo Ideally, the destructor should be protected. but we need it public to allow QPtrList<Protocol>
+ */
+ virtual ~Protocol();
+
+ /**
+ * @brief Create an empty Account
+ *
+ * This method is called during the loading of the config file.
+ * @param accountId - the account ID to create the account with. This is usually
+ * the login name of the account
+ *
+ * you don't need to register the account to the AccountManager in this function.
+ * But if you want to use this function don't forget to call @ref AccountManager::registerAccount
+ *
+ * @return The new @ref Account object created by this function
+ */
+ virtual Account *createNewAccount( const QString &accountId ) = 0;
+
+ /**
+ * @brief Create a new AddContactPage widget to be shown in the Add Contact Wizard.
+ *
+ * @return A new AddContactPage to be shown in the Add Contact Wizard
+ */
+ virtual AddContactPage *createAddContactWidget( QWidget *parent, Account *account ) = 0;
+
+ /**
+ * @brief Create a new KopeteEditAccountWidget
+ *
+ * @return A new KopeteEditAccountWidget to be shown in the account part of the configurations.
+ *
+ * @param account is the KopeteAccount to edit. If it's 0L, then we create a new account
+ * @param parent The parent of the 'to be returned' widget
+ */
+ virtual KopeteEditAccountWidget * createEditAccountWidget( Account *account, QWidget *parent ) = 0;
+
+
+ /**
+ * @brief a bitmask of the capabilities of this protocol
+ * @sa @ref setCapabilities
+ */
+ unsigned int capabilities() const ;
+
+
+ /**
+ * @brief Available capabilities
+ *
+ * @ref capabilities() returns an ORed list of these, which
+ * the edit widget interperts to determine what buttons to show
+ */
+ enum Capabilities
+ {
+ BaseFgColor = 0x1, ///< Setting the bg color of the whole edit widget / message
+ BaseBgColor = 0x2, ///< Setting the fg color of the whole edit widget / message
+ RichFgColor = 0x4, ///< Setting the fg/bg color of text portions individually
+ RichBgColor = 0x8, ///< Setting the fg/bg color of text portions individually
+
+ BaseFont = 0x10, ///< Setting the font of the whole edit widget / message
+ RichFont = 0x20, ///< Setting the font of text portions individually
+
+ /// Setting the formatting of the whole edit widget / message
+ BaseUFormatting = 0x40,
+ BaseIFormatting = 0x80,
+ BaseBFormatting = 0x100,
+
+ /// Setting the formatting of text portions individually
+ RichUFormatting = 0x200,
+ RichIFormatting = 0x400,
+ RichBFormatting = 0x800,
+
+ Alignment = 0x1000, ///< Setting the alignment of text portions
+
+ /// Setting the formatting of the whole edit widget / message
+ BaseFormatting = BaseIFormatting | BaseUFormatting | BaseBFormatting,
+
+ /// Setting the formatting of text portions individually
+ RichFormatting = RichIFormatting | RichUFormatting | RichBFormatting,
+
+ RichColor = RichBgColor | RichFgColor,
+ BaseColor = BaseBgColor | BaseFgColor,
+
+ //Shortcut for All of the above - full HTML
+ FullRTF = RichFormatting | Alignment | RichFont | RichFgColor | RichBgColor ,
+
+
+ CanSendOffline = 0x10000 ///< If it's possible to send offline messages
+ };
+
+ /**
+ * @brief Returns the status used for contacts when accounts of this protocol are offline
+ */
+ Kopete::OnlineStatus accountOfflineStatus() const;
+
+
+protected:
+ /**
+ * @brief Constructor for Protocol
+ *
+ * @param instance The protocol's instance, every plugin needs to have a KInstance of its own
+ * @param parent The protocol's parent object
+ * @param name The protocol's name
+ */
+ Protocol( KInstance *instance, QObject *parent, const char *name );
+
+ /**
+ * @brief Sets the capabilities of this protcol.
+ *
+ * The subclass contructor is a good place for calling it.
+ * @sa @ref capabilities()
+ */
+ void setCapabilities( unsigned int );
+
+public:
+
+ /**
+ * Reimplemented from Kopete::Plugin.
+ *
+ * This method disconnects all accounts and deletes them, after which it
+ * will emit readyForUnload.
+ *
+ * Note that this is an asynchronous operation that may take some time
+ * with active chats. It's no longer immediate as it used to be in
+ * Kopete 0.7.x and before. This also means that you can do a clean
+ * shutdown.
+ * @note The method is not private to allow subclasses to reimplement
+ * it even more, but if you need to do this please explain why
+ * on the list first. It might make more sense to add another
+ * virtual for protocols that's called instead, but for now I
+ * actually think protocols don't need their own implementation
+ * at all, so I left out the necessary hooks on purpose.
+ * - Martijn
+ */
+ virtual void aboutToUnload();
+
+private slots:
+ /**
+ * @internal
+ * The account changed online status. Used while unloading the protocol.
+ */
+ void slotAccountOnlineStatusChanged( Kopete::Contact *self );
+
+ /**
+ * @internal
+ * The account is destroyed. When it's the last account we emit the
+ * readyForUnload signal. Used while unloading the protocol.
+ */
+ void slotAccountDestroyed( );
+
+
+public:
+
+ /**
+ * @brief Deserialize the plugin data for a meta contact.
+ *
+ * This method splits up the data into the independent Kopete::Contact objects
+ * and calls @ref deserializeContact() for each contact.
+ *
+ * Note that you can still reimplement this method if you prefer, but you are
+ * strongly recommended to use this version of the method instead, unless you
+ * want to do _VERY_ special things with the data...
+ *
+ * @todo we probably should think to another way to save the contacltist.
+ */
+ virtual void deserialize( MetaContact *metaContact, const QMap<QString, QString> &serializedData );
+
+ /**
+ * @brief Deserialize a single contact.
+ *
+ * This method is called by @ref deserialize() for each separate contact,
+ * so you don't need to add your own hooks for multiple contacts in a single
+ * meta contact yourself. @p serializedData and @p addressBookData will be
+ * the data the contact provided in Kopete::Contact::serialize.
+ *
+ * The default implementation does nothing.
+ *
+ * @return The contact created from the data
+ * @sa Contact::serialize
+ *
+ * @todo we probably should think to another way to save the contacltist.
+ */
+ virtual Contact *deserializeContact( MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &addressBookData );
+
+
+
+public slots:
+ /**
+ * A meta contact is about to save.
+ * Call serialize() for all contained contacts for this protocol.
+ * @internal
+ * it's public because for example, Contact::setMetaContact uses it.
+ * @todo we probably should think to another way to save the contacltist.
+ */
+ void slotMetaContactAboutToSave( Kopete::MetaContact *metaContact );
+
+
+private:
+ class Private;
+ Private *d;
+};
+
+} //END namespace kopete
+
+#endif
+
diff --git a/kopete/libkopete/kopetesimplemessagehandler.cpp b/kopete/libkopete/kopetesimplemessagehandler.cpp
new file mode 100644
index 00000000..3e44520c
--- /dev/null
+++ b/kopete/libkopete/kopetesimplemessagehandler.cpp
@@ -0,0 +1,101 @@
+/*
+ kopetemessagefilter.cpp - Kopete Message Filtering
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetesimplemessagehandler.h"
+#include "kopetemessageevent.h"
+
+#include <kstaticdeleter.h>
+
+#include <qguardedptr.h>
+
+namespace Kopete
+{
+
+//BEGIN SimpleMessageHandlerFactory
+
+class SimpleMessageHandlerFactory::Private
+{
+public:
+ Message::MessageDirection direction;
+ int position;
+ QGuardedPtr<QObject> target;
+ const char *slot;
+};
+
+SimpleMessageHandlerFactory::SimpleMessageHandlerFactory( Message::MessageDirection direction,
+ int position, QObject *target, const char *slot )
+ : d( new Private )
+{
+ d->direction = direction;
+ d->position = position;
+ d->target = target;
+ d->slot = slot;
+}
+
+SimpleMessageHandlerFactory::~SimpleMessageHandlerFactory()
+{
+ delete d;
+}
+
+MessageHandler *SimpleMessageHandlerFactory::create( ChatSession */*manager*/, Message::MessageDirection direction )
+{
+ if ( direction != d->direction )
+ return 0;
+ MessageHandler *handler = new SimpleMessageHandler;
+ QObject::connect( handler, SIGNAL( handle( Kopete::Message & ) ), d->target, d->slot );
+ return handler;
+}
+
+int SimpleMessageHandlerFactory::filterPosition( ChatSession */*manager*/, Message::MessageDirection direction )
+{
+ if ( direction != d->direction )
+ return StageDoNotCreate;
+ return d->position;
+}
+
+//END SimpleMessageHandlerFactory
+
+//BEGIN SimpleMessageHandler
+
+class SimpleMessageHandler::Private
+{
+};
+
+SimpleMessageHandler::SimpleMessageHandler()
+ : d(0)
+{
+}
+
+SimpleMessageHandler::~SimpleMessageHandler()
+{
+ delete d;
+}
+
+void SimpleMessageHandler::handleMessage( MessageEvent *event )
+{
+ Message message = event->message();
+ emit handle( message );
+ event->setMessage( message );
+ MessageHandler::handleMessage( event );
+}
+
+//END SimpleMessageHandler
+
+}
+
+#include "kopetesimplemessagehandler.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetesimplemessagehandler.h b/kopete/libkopete/kopetesimplemessagehandler.h
new file mode 100644
index 00000000..af6de4ab
--- /dev/null
+++ b/kopete/libkopete/kopetesimplemessagehandler.h
@@ -0,0 +1,90 @@
+/*
+ kopetesimplemessagehandler.h - Kopete Message Filtering - simple interface
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETESIMPLEMESSAGEHANDLER_H
+#define KOPETESIMPLEMESSAGEHANDLER_H
+
+#include "kopete_export.h"
+#include "kopetemessagehandler.h"
+
+namespace Kopete
+{
+
+/**
+ * @brief A MessageHandlerFactory that creates synchronous MessageHandlers that just call a slot
+ *
+ * A concrete MessageHandlerFactory. This class is intended to make writing MessageHandlers simpler;
+ * all that is required is to implement a message processing function and place an instance of this
+ * class in your Plugin-derived class.
+ *
+ * Whenever a message passes through a handler created by this factory, the slot passed to the
+ * constructor will be called. The slot should take a single argument of type (non-@p const)
+ * <tt>Message &</tt>.
+ */
+class KOPETE_EXPORT SimpleMessageHandlerFactory : public MessageHandlerFactory
+{
+public:
+ /**
+ * @param direction The direction this factory should create message handlers for
+ * @param position Where in the chain the handler should be installed
+ * @param target The object to call back to when handling a message
+ * @param slot The slot on @p target to call when handling a message
+ * @see Kopete::MessageHandlerFactory::filterPosition
+ */
+ SimpleMessageHandlerFactory( Message::MessageDirection direction, int position,
+ QObject *target, const char *slot );
+ ~SimpleMessageHandlerFactory();
+
+ /**
+ * Creates and returns a SimpleMessageHandler object.
+ */
+ MessageHandler *create( ChatSession *manager, Message::MessageDirection direction );
+ /**
+ * Returns the filter position passed to the constructor if @p direction matches the
+ * direction passed to the constructor, otherwise returns @c StageDoNotCreate.
+ */
+ int filterPosition( ChatSession *manager, Message::MessageDirection direction );
+
+private:
+ class Private;
+ Private *d;
+};
+
+/**
+ * @internal This class is used to implement SimpleMessageHandlerFactory.
+ */
+class SimpleMessageHandler : public MessageHandler
+{
+ Q_OBJECT
+public:
+ SimpleMessageHandler();
+ ~SimpleMessageHandler();
+
+ void handleMessage( MessageEvent *event );
+
+signals:
+ void handle( Kopete::Message &message );
+
+private:
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetetask.cpp b/kopete/libkopete/kopetetask.cpp
new file mode 100644
index 00000000..b7484116
--- /dev/null
+++ b/kopete/libkopete/kopetetask.cpp
@@ -0,0 +1,108 @@
+/*
+ kopetetask.cpp - Kopete Task
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetetask.h"
+
+#include <klocale.h>
+
+#include <qptrlist.h>
+
+namespace Kopete
+{
+
+class Task::Private
+{
+public:
+ Private()
+ : result( ResultFailed )
+ {
+ errorMessage = i18n( "The operation has not finished yet" );
+ }
+
+ Task::Result result;
+ QString errorMessage;
+ QPtrList<Task> subtasks;
+};
+
+Task::Task()
+ : d( new Private )
+{
+}
+
+Task::~Task()
+{
+ delete d;
+}
+
+bool Task::succeeded() const
+{
+ return d->result == ResultSucceeded;
+}
+
+const QString &Task::errorString() const
+{
+ return d->errorMessage;
+}
+
+void Task::abort( int flags )
+{
+ int childFlags = flags & ~AbortEmitResult;
+ for ( Task *task = d->subtasks.first(); task; task = d->subtasks.next() )
+ task->abort( childFlags );
+
+ if ( flags & AbortEmitResult )
+ emitResult( ResultFailed, i18n( "Aborted" ) );
+ else
+ delete this;
+}
+
+void Task::addSubtask( Task *task )
+{
+ d->subtasks.append( task );
+ connect( task, SIGNAL( result( Kopete::Task* ) ),
+ this, SLOT( slotResult( Kopete::Task* ) ) );
+ connect( task, SIGNAL( statusMessage( Kopete::Task*, const QString & ) ),
+ this, SIGNAL( statusMessage( Kopete::Task*, const QString & ) ) );
+}
+
+void Task::removeSubtask( Task *task, RemoveSubtaskIfLast actionIfLast )
+{
+ disconnect( task, SIGNAL( result( Kopete::Task* ) ),
+ this, SLOT( slotResult( Kopete::Task* ) ) );
+ disconnect( task, SIGNAL( statusMessage( Kopete::Task*, const QString & ) ),
+ this, SIGNAL( statusMessage( Kopete::Task*, const QString & ) ) );
+ d->subtasks.remove( task );
+ if ( d->subtasks.isEmpty() && actionIfLast == IfLastEmitResult )
+ emitResult( task->succeeded() ? ResultSucceeded : ResultFailed, task->errorString() );
+}
+
+void Task::emitResult( Result res, const QString &errorMessage )
+{
+ d->result = res;
+ d->errorMessage = errorMessage;
+ emit result( this );
+ delete this;
+}
+
+void Task::slotResult( Kopete::Task *task )
+{
+ removeSubtask( task );
+}
+
+}
+
+#include "kopetetask.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetetask.h b/kopete/libkopete/kopetetask.h
new file mode 100644
index 00000000..115e1ebe
--- /dev/null
+++ b/kopete/libkopete/kopetetask.h
@@ -0,0 +1,162 @@
+/*
+ kopetetask.h - Kopete Task
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETETASK_H
+#define KOPETETASK_H
+
+#include <qobject.h>
+#include <kdemacros.h>
+
+namespace Kopete
+{
+
+/**
+ * The base class for all tasks.
+ * For most tasks created in Kopete, the code looks like
+ *
+ * \code
+ * Kopete::Task *task = someobject->someoperation( some parameters );
+ * connect( task, SIGNAL( result( Kopete::Task * ) ),
+ * this, SLOT( slotResult( Kopete::Task * ) ) );
+ * \endcode
+ * (other connects, specific to the job)
+ *
+ * And slotResult is usually at least:
+ *
+ * \code
+ * if ( !task->succeeded() )
+ * Kopete::UI::Global::showTaskError( task );
+ * \endcode
+ *
+ * Much of the ideas (and some of the documentation and function names) for this
+ * class come from KIO::Job.
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class Task : public QObject
+{
+ Q_OBJECT
+
+protected:
+ Task();
+public:
+ ~Task();
+
+ /**
+ * Returns whether the task completed successfully.
+ * Only call this method from the slot connected to result().
+ * @return if the task succeeded, returns true, otherwise returns false.
+ */
+ bool succeeded() const;
+ /**
+ * Converts an error code and a non-i18n error message into an
+ * error message in the current language. The low level (non-i18n)
+ * error message (usually a url) is put into the translated error
+ * message using %%1.
+ *
+ * Use this to display the error yourself, but for a dialog box
+ * use Kopete::UI::Global::showTaskError. Do not call it if succeeded()
+ * returns true.
+ * @return the error message and if there is no error, a message
+ * telling the user that the app is broken, so check with
+ * succeeded() whether there is an error.
+ */
+ const QString &errorString() const;
+
+ /** Flags for the abort() function */
+ enum AbortFlags { AbortNormal = 0, AbortEmitResult = 1 };
+public slots:
+ /**
+ * Abort this task.
+ * This aborts all subtasks and deletes the task.
+ *
+ * @param flags a combination of flags from AbortFlags. If AbortEmitResult is
+ * set, Job will emit the result signal. AbortEmitResult is removed
+ * from the flags passed to the abort function of subtasks.
+ */
+ virtual void abort( int flags = AbortNormal );
+
+signals:
+ /**
+ * Emitted when the task is finished, in any case (completed, canceled,
+ * failed...). Use error() to find the result.
+ * @param task the task that emitted this signal
+ */
+ void result( Kopete::Task *task );
+ /**
+ * Emitted to display status information about this task.
+ * Examples of messages are:
+ * "Removing ICQ contact Joe from server-side list",
+ * "Loading account plugin", etc.
+ * @param task the task that emitted this signal
+ * @param message the info message
+ */
+ void statusMessage( Kopete::Task *task, const QString &message );
+
+protected:
+ /**
+ * Add a task that has to be completed before a result is emitted. This
+ * obviously should not be called after the finish signal is emitted by
+ * the subtask.
+ *
+ * @param task the subtask to add
+ */
+ virtual void addSubtask( Task *task );
+
+ enum RemoveSubtaskIfLast { IfLastDoNothing, IfLastEmitResult };
+ /**
+ * Mark a sub job as being done. If it's the last to
+ * wait on the job will emit a result - jobs with
+ * two steps might want to override slotResult
+ * in order to avoid calling this method.
+ *
+ * @param task the subjob to add
+ * @param actionIfLast the action to take if this is the last subtask.
+ * If set to IfLastEmitResult, the error information from @p task
+ * will be copied to this object, and emitResult() will be called.
+ */
+ virtual void removeSubtask( Task *task, RemoveSubtaskIfLast actionIfLast = IfLastEmitResult );
+
+ enum Result { ResultFailed = 0, ResultSucceeded = 1 };
+ /**
+ * Utility function to emit the result signal, and suicide this job.
+ * Sets the stored result and error message to @p result and @p errorMessage.
+ * You should call this instead of emitting the result() signal yourself.
+ */
+ void emitResult( Result result = ResultSucceeded, const QString &errorMessage = QString::null );
+
+protected slots:
+ /**
+ * Called whenever a subtask finishes.
+ * The default implementation checks for errors and propagates
+ * them to this task, then calls removeSubtask().
+ * Override if you want to provide a different @p actionIfLast to
+ * removeSubtask, or want to perform some other processing in response
+ * to a subtask finishing
+ * @param task the subtask that finished
+ * @see result()
+ */
+ virtual void slotResult( Kopete::Task *task );
+
+private:
+ class Private;
+ Private *d;
+};
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopetetransfermanager.cpp b/kopete/libkopete/kopetetransfermanager.cpp
new file mode 100644
index 00000000..1131cd90
--- /dev/null
+++ b/kopete/libkopete/kopetetransfermanager.cpp
@@ -0,0 +1,271 @@
+/*
+ kopetetransfermanager.cpp
+
+ Copyright (c) 2002-2003 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002-2003 by Richard Smith <kopete@metafoo.co.uk>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <kfiledialog.h>
+#include <kfileitem.h>
+#include <kmessagebox.h>
+#include <kio/observer.h>
+
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopeteuiglobal.h"
+
+#include "kopetetransfermanager.h"
+#include "kopetefileconfirmdialog.h"
+
+/***************************
+ * Kopete::FileTransferInfo *
+ ***************************/
+
+Kopete::FileTransferInfo::FileTransferInfo( Kopete::Contact *contact, const QString& file, const unsigned long size, const QString &recipient, KopeteTransferDirection di, const unsigned int id, QString internalId)
+{
+ mContact = contact;
+ mFile = file;
+ mId = id;
+ mSize = size;
+ mRecipient = recipient;
+ m_intId= internalId;
+ mDirection= di;
+}
+
+/***************************
+ * Kopete::Transfer *
+ ***************************/
+
+
+Kopete::Transfer::Transfer( const Kopete::FileTransferInfo &kfti, const QString &localFile, bool showProgressInfo)
+ : KIO::Job(showProgressInfo), mInfo(kfti)
+{
+ KURL targ; targ.setPath( localFile );
+ init( targ, showProgressInfo );
+}
+
+Kopete::Transfer::Transfer( const Kopete::FileTransferInfo &kfti, const Kopete::Contact *contact, bool showProgressInfo)
+ : KIO::Job(showProgressInfo), mInfo(kfti)
+{
+ // TODO: use mInfo.url().fileName() after move to protocol-aware filetransfers
+ KURL targ; targ.setPath( mInfo.file() );
+ init( displayURL( contact, targ.fileName() ), showProgressInfo );
+}
+
+void Kopete::Transfer::init( const KURL &target, bool showProgressInfo )
+{
+ mTarget = target;
+
+ if( showProgressInfo )
+ Observer::self()->slotCopying( this, sourceURL(), destinationURL() );
+
+ connect( this, SIGNAL( result( KIO::Job* ) ), SLOT( slotResultEmitted() ) );
+
+ setAutoErrorHandlingEnabled( true, 0 );
+}
+
+Kopete::Transfer::~Transfer()
+{
+}
+
+KURL Kopete::Transfer::displayURL( const Kopete::Contact *contact, const QString &file )
+{
+ KURL url;
+ url.setProtocol( QString::fromLatin1("kopete") );
+
+ QString host;
+ if( !contact )
+ host = QString::fromLatin1("unknown origin");
+ else if( contact->metaContact() )
+ host = contact->metaContact()->displayName();
+ else
+ host = contact->contactId();
+ url.setHost(host);
+
+ // url.setPath( contact->protocol()->displayName() );
+
+ url.setFileName( file );
+ return url;
+}
+
+// TODO: add possibility of network file transfers;
+// call mInfo->url() not file()
+KURL Kopete::Transfer::sourceURL()
+{
+ if( mInfo.direction() == Kopete::FileTransferInfo::Incoming )
+ return displayURL( mInfo.contact(), mInfo.file() );
+ else
+ {
+ KURL url; url.setPath( mInfo.file() );
+ return url;
+ }
+}
+
+KURL Kopete::Transfer::destinationURL()
+{
+ return mTarget;
+}
+
+void Kopete::Transfer::slotProcessed(unsigned int bytes)
+{
+ emitPercent( bytes, mInfo.size() );
+}
+
+void Kopete::Transfer::slotComplete()
+{
+ emitResult();
+}
+
+void Kopete::Transfer::slotError( int error, const QString &errorText )
+{
+ m_error = error;
+ m_errorText = errorText;
+
+ emitResult();
+}
+
+void Kopete::Transfer::slotResultEmitted()
+{
+ if( error() == KIO::ERR_USER_CANCELED )
+ emit transferCanceled();
+}
+
+/***************************
+ * Kopete::TransferManager *
+ ***************************/
+
+static KStaticDeleter<Kopete::TransferManager> deleteManager;
+Kopete::TransferManager *Kopete::TransferManager::s_transferManager = 0;
+
+Kopete::TransferManager* Kopete::TransferManager::transferManager()
+{
+ if(!s_transferManager)
+ deleteManager.setObject(s_transferManager, new Kopete::TransferManager(0));
+
+ return s_transferManager;
+}
+
+Kopete::TransferManager::TransferManager( QObject *parent ) : QObject( parent )
+{
+ nextID = 0;
+}
+
+Kopete::Transfer* Kopete::TransferManager::addTransfer( Kopete::Contact *contact, const QString& file, const unsigned long size, const QString &recipient , Kopete::FileTransferInfo::KopeteTransferDirection di)
+{
+// if (nextID != 0)
+ nextID++;
+ Kopete::FileTransferInfo info(contact, file, size, recipient,di, nextID);
+ Kopete::Transfer *trans = new Kopete::Transfer(info, contact);
+ connect(trans, SIGNAL(result(KIO::Job *)), this, SLOT(slotComplete(KIO::Job *)));
+ mTransfersMap.insert(nextID, trans);
+ return trans;
+}
+
+void Kopete::TransferManager::slotAccepted(const Kopete::FileTransferInfo& info, const QString& filename)
+{
+ Kopete::Transfer *trans = new Kopete::Transfer(info, filename);
+ connect(trans, SIGNAL(result(KIO::Job *)), this, SLOT(slotComplete(KIO::Job *)));
+ mTransfersMap.insert(info.transferId(), trans);
+ emit accepted(trans,filename);
+}
+
+int Kopete::TransferManager::askIncomingTransfer( Kopete::Contact *contact, const QString& file, const unsigned long size, const QString& description, QString internalId)
+{
+// if (nextID != 0)
+ nextID++;
+
+ QString dn= contact ? (contact->metaContact() ? contact->metaContact()->displayName() : contact->contactId()) : i18n("<unknown>");
+
+ Kopete::FileTransferInfo info(contact, file, size, dn, Kopete::FileTransferInfo::Incoming , nextID , internalId);
+
+ //FIXME!!! this will not be deleted if it's still open when kopete exits
+ KopeteFileConfirmDialog *diag= new KopeteFileConfirmDialog(info, description , 0 ) ;
+
+ connect( diag, SIGNAL( accepted(const Kopete::FileTransferInfo&, const QString&)) , this, SLOT( slotAccepted(const Kopete::FileTransferInfo&, const QString&) ) );
+ connect( diag, SIGNAL( refused(const Kopete::FileTransferInfo&)) , this, SIGNAL( refused(const Kopete::FileTransferInfo&) ) );
+ diag->show();
+ return nextID;
+}
+
+void Kopete::TransferManager::removeTransfer( unsigned int id )
+{
+ mTransfersMap.remove(id);
+ //we don't need to delete the job, the job get deleted itself
+}
+
+void Kopete::TransferManager::slotComplete(KIO::Job *job)
+{
+ Kopete::Transfer *transfer=dynamic_cast<Kopete::Transfer*>(job);
+ if(!transfer)
+ return;
+
+ emit done(transfer);
+
+ for( QMap<unsigned, Kopete::Transfer*>::Iterator it = mTransfersMap.begin();
+ it != mTransfersMap.end(); ++it )
+ {
+ if( it.data() == transfer )
+ {
+ removeTransfer(it.key());
+ break;
+ }
+ }
+}
+
+void Kopete::TransferManager::sendFile( const KURL &file, const QString &fname, unsigned long sz,
+ bool mustBeLocal, QObject *sendTo, const char *slot )
+{
+ KURL url(file);
+ QString filename;
+ unsigned int size = 0;
+
+ //If the file location is null, then get it from a file open dialog
+ if( !url.isValid() )
+ url = KFileDialog::getOpenURL( QString::null, QString::fromLatin1("*"), 0l, i18n( "Kopete File Transfer" ));
+ else
+ {
+ filename = fname;
+ size = sz;
+ }
+
+ if( filename.isEmpty() )
+ filename = url.fileName();
+
+ if( size == 0 )
+ {
+ KFileItem finfo(KFileItem::Unknown, KFileItem::Unknown, url);
+ size = (unsigned long)finfo.size();
+ }
+
+ if( !url.isEmpty() )
+ {
+ if( mustBeLocal && !url.isLocalFile() )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "Sorry, sending files which are not stored locally is not yet supported by this protocol.\n"
+ "Please copy this file to your computer and try again." ) );
+ }
+ else
+ {
+ connect( this, SIGNAL(sendFile(const KURL&, const QString&, unsigned int)), sendTo, slot );
+ emit sendFile( url, filename, size );
+ disconnect( this, SIGNAL(sendFile(const KURL&, const QString&, unsigned int)), sendTo, slot );
+ }
+ }
+}
+
+#include "kopetetransfermanager.moc"
+
diff --git a/kopete/libkopete/kopetetransfermanager.h b/kopete/libkopete/kopetetransfermanager.h
new file mode 100644
index 00000000..f4e7416f
--- /dev/null
+++ b/kopete/libkopete/kopetetransfermanager.h
@@ -0,0 +1,212 @@
+/*
+ kopetetransfermanager.h
+
+ Copyright (c) 2002-2003 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002-2003 by Richard Smith <kopete@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEFILETRANSFER_H
+#define KOPETEFILETRANSFER_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qmap.h>
+#include "kopete_export.h"
+
+#include <kio/job.h>
+
+namespace Kopete
+{
+
+class Transfer;
+class Contact;
+
+/**
+ * @author Nick Betcher. <nbetcher@kde.org>
+ */
+class KOPETE_EXPORT FileTransferInfo
+{
+public:
+ enum KopeteTransferDirection { Incoming, Outgoing };
+
+ FileTransferInfo( Contact *, const QString&, const unsigned long size, const QString &, KopeteTransferDirection di, const unsigned int id, QString internalId=QString::null);
+ ~FileTransferInfo() {}
+ unsigned int transferId() const { return mId; }
+ const Contact* contact() const { return mContact; }
+ QString file() const { return mFile; }
+ QString recipient() const { return mRecipient; }
+ unsigned long size() const { return mSize; }
+ QString internalId() const { return m_intId; }
+ KopeteTransferDirection direction() const { return mDirection; }
+
+private:
+ unsigned long mSize;
+ QString mRecipient;
+ unsigned int mId;
+ Contact *mContact;
+ QString mFile;
+ QString m_intId;
+ KopeteTransferDirection mDirection;
+};
+
+/**
+ * Creates and manages kopete file transfers
+ */
+class KOPETE_EXPORT TransferManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Retrieve the transfer manager instance
+ */
+ static TransferManager* transferManager();
+ virtual ~TransferManager() {};
+
+ /**
+ * @brief Adds a file transfer to the Kopete::TransferManager
+ */
+ Transfer *addTransfer( Contact *contact, const QString& file, const unsigned long size, const QString &recipient , FileTransferInfo::KopeteTransferDirection di);
+ int askIncomingTransfer( Contact *contact, const QString& file, const unsigned long size, const QString& description=QString::null, QString internalId=QString::null);
+ void removeTransfer( unsigned int id );
+
+ /**
+ * @brief Ask the user which file to send when they click Send File.
+ *
+ * Possibly ask the user which file to send when they click Send File. Sends a signal indicating KURL to
+ * send when the local user accepts the transfer.
+ * @param file If valid, the user will not be prompted for a URL, and this one will be used instead.
+ * If it refers to a remote file and mustBeLocal is true, the file will be transferred to the local
+ * filesystem.
+ * @param localFile file name to display if file is a valid URL
+ * @param fileSize file size to send if file is a valid URL
+ * @param mustBeLocal If the protocol can only send files on the local filesystem, this flag
+ * allows you to ensure the filename will be local.
+ * @param sendTo The object to send the signal to
+ * @param slot The slot to send the signal to. Signature: sendFile(const KURL &file)
+ */
+ void sendFile( const KURL &file, const QString &localFile, unsigned long fileSize,
+ bool mustBeLocal, QObject *sendTo, const char *slot );
+
+signals:
+ /** @brief Signals the transfer is done. */
+ void done( Kopete::Transfer* );
+
+ /** @brief Signals the transfer has been canceled. */
+ void canceled( Kopete::Transfer* );
+
+ /** @brief Signals the transfer has been accepted */
+ void accepted(Kopete::Transfer*, const QString &fileName);
+
+ /** @brief Signals the transfer has been rejected */
+ void refused(const Kopete::FileTransferInfo& );
+
+ /** @brief Send a file */
+ void sendFile(const KURL &file, const QString &localFile, unsigned int fileSize);
+
+private slots:
+ void slotAccepted(const Kopete::FileTransferInfo&, const QString&);
+ void slotComplete(KIO::Job*);
+
+private:
+ TransferManager( QObject *parent );
+ static TransferManager *s_transferManager;
+
+ int nextID;
+ QMap<unsigned int, Transfer *> mTransfersMap;
+};
+
+/**
+ * A KIO job for a kopete file transfer.
+ * @author Richard Smith <kopete@metafoo.co.uk>
+ */
+class KOPETE_EXPORT Transfer : public KIO::Job
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor
+ */
+ Transfer( const FileTransferInfo &, const QString &localFile, bool showProgressInfo = true);
+
+ /**
+ * Constructor
+ */
+ Transfer( const FileTransferInfo &, const Contact *toUser, bool showProgressInfo = true);
+
+ /**
+ * Destructor
+ */
+ ~Transfer();
+
+ /** @brief Get the info for this file transfer */
+ const FileTransferInfo &info() const { return mInfo; }
+
+ /**
+ * Retrieve a URL indicating where the file is being copied from.
+ * For display purposes only! There's no guarantee that this URL
+ * refers to a real file being transferred.
+ */
+ KURL sourceURL();
+
+ /**
+ * Retrieve a URL indicating where the file is being copied to.
+ * See @ref sourceURL
+ */
+ KURL destinationURL();
+
+public slots:
+
+ /**
+ * @brief Set the file size processed so far
+ */
+ void slotProcessed(unsigned int);
+
+ /**
+ * @brief Indicate that the transfer is complete
+ */
+ void slotComplete();
+
+ /**
+ * @brief Inform the job that an error has occurred while transferring the file.
+ *
+ * @param error A member of the KIO::Error enumeration indicating what error occurred.
+ * @param errorText A string to aid understanding of the error, often the offending URL.
+ */
+ void slotError( int error, const QString &errorText );
+
+signals:
+ /**
+ * @deprecated Use result() and check error() for ERR_USER_CANCELED
+ */
+ void transferCanceled();
+
+private:
+ void init( const KURL &, bool );
+
+ static KURL displayURL( const Contact *contact, const QString &file );
+
+ FileTransferInfo mInfo;
+ KURL mTarget;
+ int mPercent;
+
+private slots:
+ void slotResultEmitted();
+};
+
+}
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/kopeteui.desktop b/kopete/libkopete/kopeteui.desktop
new file mode 100644
index 00000000..6818fc35
--- /dev/null
+++ b/kopete/libkopete/kopeteui.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=Kopete/UI
+X-KDE-Derived=Kopete/Plugin
+Comment=A Kopete UI Plugin
+Comment[ar]=توصيلة واجهة استخدام Kopete
+Comment[be]=Модуль інтÑрфÑйÑу Kopete
+Comment[bg]=ПриÑтавка за Ð³Ñ€Ð°Ñ„Ð¸Ñ‡Ð½Ð¸Ñ Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð½Ð° Kopete
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ কপেট ইউ-আই পà§à¦²à¦¾à¦—িন
+Comment[bs]=Kopete dodatak za UI
+Comment[ca]=Un connector de IU per a Kopete
+Comment[cs]=Modul rozhraní aplikace Kopete
+Comment[cy]=Ategyn UI Kopete
+Comment[da]=En Kopete UI-plugin
+Comment[de]=Ein Kopete Benutzeroberflächenmodul
+Comment[el]=Ένα Ï€Ïόσθετο γÏÎ±Ï†Î¹ÎºÎ¿Ï Ï€ÎµÏιβάλλοντος του Kopete
+Comment[es]=Complemento de UI de Kopete
+Comment[et]=Kopete kasutajaliidese plugin
+Comment[eu]=Kopete UI plugin bat
+Comment[fa]=یک وصلۀ شناسۀ کاربر Kopete
+Comment[fi]=Kopeten käyttöliittymäliitännäinen
+Comment[fr]=Un module d'interface utilisateur pour Kopete
+Comment[ga]=Breiseán Chomhéadan Úsáideora Kopete
+Comment[gl]=Un protocolo de interfaz gráfica para Kopete
+Comment[he]=תוסף ממשק משתמש של Kopete
+Comment[hi]=à¤à¤• के-ऑपà¥à¤Ÿà¥€ यूआई पà¥à¤²à¤—इन
+Comment[hr]=Umetak za Kopeteovo korisniÄko suÄelje
+Comment[hu]=Kopete bővítőmodul a grafikus felülethez
+Comment[is]=Viðmótsíforrit fyrir Kopete
+Comment[it]=Plugin per UI di Kopete
+Comment[ja]=Kopete UI プラグイン
+Comment[ka]=Kopete UI მáƒáƒ“ული
+Comment[kk]=Kopete интерфейÑінің плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ចំណុច​ប្រទាក់​ក្រាហ្វិក​របស់ Kopete
+Comment[lt]=Kopete sąsajos įskiepis
+Comment[mk]=UI-приклучок за Kopete
+Comment[nb]=Et programtillegg for Kopete brukergrensesnitt
+Comment[nds]=Kopete-Böversietmoduul
+Comment[ne]=कोपेट यू आई पà¥à¤²à¤—इन
+Comment[nl]=Een Kopete gebruikersinterface-plugin
+Comment[nn]=Kopete-programtillegg for brukargrensesnitt
+Comment[pl]=Wtyczka interfejsu użytkownika Kopete
+Comment[pt]=Um 'Plugin' de Interface do Kopete
+Comment[pt_BR]=Um plug-in de UI do Kopete
+Comment[ro]=Un modul interfaţă grafică Kopete
+Comment[ru]=Модуль интерфейÑа Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Kopete
+Comment[se]=Kopete geavaheaddjelaktalassemoduvla
+Comment[sk]=Modul rozhrania Kopete
+Comment[sl]=Vstavek za uporabniški vmesnik za Kopete
+Comment[sr]=Прикључак за Kopete-ов кориÑнички интерфејÑ
+Comment[sr@Latn]=PrikljuÄak za Kopete-ov korisniÄki interfejs
+Comment[sv]=Gränssnittsinsticksprogram för Kopete
+Comment[ta]=ஒர௠Kopete UI செரà¯à®•à®²à¯
+Comment[tg]=Модули ИнтерфейÑи Корвандии Kopete
+Comment[tr]=Bir Kopete UI Eklentisi
+Comment[uk]=Втулок інтерфейÑу Ð´Ð»Ñ Kopete
+Comment[wa]=On tchôke-divins d' eterface grafike po Kopete
+Comment[zh_CN]=Kopete ç•Œé¢æ’件
+Comment[zh_HK]=Kopete 用戶界é¢æ’件
+Comment[zh_TW]=Kopete 使用者介é¢å¤–掛程å¼
diff --git a/kopete/libkopete/kopeteuiglobal.cpp b/kopete/libkopete/kopeteuiglobal.cpp
new file mode 100644
index 00000000..06c0dfa3
--- /dev/null
+++ b/kopete/libkopete/kopeteuiglobal.cpp
@@ -0,0 +1,60 @@
+/*
+ kopeteuiglobal.cpp - Kopete UI Globals
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteuiglobal.h"
+
+#include <qguardedptr.h>
+
+
+namespace Kopete
+{
+
+
+namespace
+{
+ QGuardedPtr<QWidget> g_mainWidget;
+ int g_sysTrayWId;
+}
+
+void UI::Global::setMainWidget( QWidget *widget )
+{
+ g_mainWidget = widget;
+}
+
+QWidget *UI::Global::mainWidget()
+{
+ return g_mainWidget;
+}
+
+void UI::Global::setSysTrayWId( int newWinId )
+{
+ g_sysTrayWId = newWinId;
+}
+
+int UI::Global::sysTrayWId()
+{
+ if ( g_sysTrayWId == 0 )
+ return g_mainWidget->winId();
+ else
+ return g_sysTrayWId;
+}
+
+
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteuiglobal.h b/kopete/libkopete/kopeteuiglobal.h
new file mode 100644
index 00000000..4a79eb87
--- /dev/null
+++ b/kopete/libkopete/kopeteuiglobal.h
@@ -0,0 +1,72 @@
+/*
+ kopeteuiglobal.h - Kopete UI Globals
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEUIGLOBAL_H
+#define KOPETEUIGLOBAL_H
+
+#include <qwidget.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+namespace UI
+{
+
+/**
+ * This namespace contains the Kopete user interface's global settings
+ */
+namespace Global
+{
+ /**
+ * Set the main widget to widget
+ */
+ KOPETE_EXPORT void setMainWidget( QWidget *widget );
+ /**
+ * Returns the main widget - this is the widget that message boxes
+ * and KNotify stuff should use as a parent.
+ */
+ KOPETE_EXPORT QWidget *mainWidget();
+
+ /**
+ * \brief Returns the WId of the system tray.
+ *
+ * Allows developers easy access to the WId of the system tray so
+ * that it can be used for passive popups in the protocols
+ * \return the WId of the system tray. Returns the WId of the main
+ * widget if there's no system tray.
+ */
+ KOPETE_EXPORT int sysTrayWId();
+
+ /**
+ * \brief Set the WId of the system tray.
+ *
+ * Called by the KopeteSystemTray constructor and destructor to
+ * set the WId for the system tray appropriately
+ */
+ KOPETE_EXPORT void setSysTrayWId( int newWinId );
+} //Global::UI
+
+} //UI
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopeteutils.cpp b/kopete/libkopete/kopeteutils.cpp
new file mode 100644
index 00000000..d7d8eb0f
--- /dev/null
+++ b/kopete/libkopete/kopeteutils.cpp
@@ -0,0 +1,124 @@
+/*
+ Kopete Utils.
+ Copyright (c) 2005 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ isHostReachable function code derived from KDE's HTTP kioslave
+ Copyright (c) 2005 Waldo Bastian <bastian@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <dcopclient.h>
+#include <kdatastream.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+
+#include "kopeteaccount.h"
+#include "knotification.h"
+#include "kopeteutils_private.h"
+#include "kopeteutils.h"
+#include "kopeteuiglobal.h"
+
+static const QString notifyConnectionLost_DefaultMessage = i18n("You have been disconnected.");
+static const QString notifyConnectionLost_DefaultCaption = i18n("Connection Lost.");
+static const QString notifyConnectionLost_DefaultExplanation = i18n("Kopete lost the channel used to talk to the instant messaging system.\nThis can be because either your internet access went down, the service is experiencing problems, or the service disconnected you because you tried to connect with the same account from another location. Try connecting again later.");
+
+static const QString notifyCannotConnect_DefaultMessage = i18n("Can't connect with the instant messaging server or peers.");
+static const QString notifyCannotConnect_DefaultCaption = i18n("Can't connect.");
+static const QString notifyCannotConnect_DefaultExplanation = i18n("This means Kopete can't reach the instant messaging server or peers.\nThis can be because either your internet access is down or the server is experiencing problems. Try connecting again later.");
+
+namespace Kopete
+{
+namespace Utils
+{
+
+void notify( QPixmap pic, const QString &eventid, const QString &caption, const QString &message, const QString explanation, const QString debugInfo)
+{
+ QString action;
+ if ( !explanation.isEmpty() )
+ action = i18n( "More Information..." );
+ kdDebug( 14010 ) << k_funcinfo << endl;
+ KNotification *n = KNotification::event( eventid, message, pic , 0L , action );
+ ErrorNotificationInfo info;
+ info.explanation = explanation;
+ info.debugInfo = debugInfo;
+
+ NotifyHelper::self()->registerNotification(n, info);
+ QObject::connect( n, SIGNAL(activated(unsigned int )) , NotifyHelper::self() , SLOT( slotEventActivated(unsigned int) ) );
+ QObject::connect( n, SIGNAL(closed()) , NotifyHelper::self() , SLOT( slotEventClosed() ) );
+}
+
+void notifyConnectionLost( const Account *account, const QString &caption, const QString &message, const QString &explanation, const QString &debugInfo )
+{
+ if (!account)
+ return;
+
+ notify( account->accountIcon(32), QString::fromLatin1("connection_lost"), caption.isEmpty() ? notifyConnectionLost_DefaultCaption : caption, message.isEmpty() ? notifyConnectionLost_DefaultMessage : message, explanation.isEmpty() ? notifyConnectionLost_DefaultExplanation : explanation, debugInfo);
+}
+
+bool isHostReachable(const QString &host)
+{
+ const int NetWorkStatusUnknown = 1;
+ const int NetWorkStatusOnline = 8;
+ QCString replyType;
+ QByteArray params;
+ QByteArray reply;
+
+ QDataStream stream(params, IO_WriteOnly);
+ stream << host;
+
+ if ( KApplication::kApplication()->dcopClient()->call( "kded", "networkstatus", "status(QString)", params, replyType, reply ) && (replyType == "int") )
+ {
+ int result;
+ QDataStream stream2( reply, IO_ReadOnly );
+ stream2 >> result;
+ return (result != NetWorkStatusUnknown) && (result != NetWorkStatusOnline);
+ }
+ return false; // On error, assume we are online
+}
+
+void notifyCannotConnect( const Account *account, const QString &explanation, const QString &debugInfo)
+{
+ if (!account)
+ return;
+
+ notify( account->accountIcon(), QString::fromLatin1("cannot_connect"), notifyCannotConnect_DefaultCaption, notifyCannotConnect_DefaultMessage, notifyCannotConnect_DefaultExplanation, debugInfo);
+}
+
+void notifyConnectionError( const Account *account, const QString &caption, const QString &message, const QString &explanation, const QString &debugInfo )
+{
+ if (!account)
+ return;
+
+ // TODO: Display a specific default connection error message, I don't want to introducte too many new strings
+ notify( account->accountIcon(32), QString::fromLatin1("connection_error"), caption, message, explanation, debugInfo);
+}
+
+void notifyServerError( const Account *account, const QString &caption, const QString &message, const QString &explanation, const QString &debugInfo )
+{
+ if (!account)
+ return;
+
+ // TODO: Display a specific default server error message, I don't want to introducte too many new strings
+ notify( account->accountIcon(32), QString::fromLatin1("server_error"), caption, message, explanation, debugInfo);
+}
+
+} // end ns ErrorNotifier
+} // end ns Kopete
+
diff --git a/kopete/libkopete/kopeteutils.h b/kopete/libkopete/kopeteutils.h
new file mode 100644
index 00000000..1cbcb4c3
--- /dev/null
+++ b/kopete/libkopete/kopeteutils.h
@@ -0,0 +1,114 @@
+/*
+ Kopete Utils.
+
+ Copyright (c) 2005 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_UTILS_H
+#define KOPETE_UTILS_H
+
+#include "qobject.h"
+#include "qstring.h"
+#include "qpixmap.h"
+#include "kopete_export.h"
+
+class KNotification;
+
+namespace Kopete
+{
+
+class Account;
+
+namespace Utils
+{
+
+/**
+ * Checks if host is accesible. Useful for plugins to check for disconnected events.
+ *
+ * @param host The host to be cheked
+ */
+bool isHostReachable( const QString &host );
+
+/**
+ * Notifies the user connection has been lost without coupling plugins with GUI code.
+ *
+ * @param account The account that lost the connection and wants to notify the user.
+ * @param caption A brief subject line, used where possible if the presentation allows it.
+ * @param message A short description of the error.
+ * @param explanation A long description on how the error occured and what the user can do about it.
+ * @param debugInfo Debug info that can be sent to the developers or to the network service owners.
+ *
+ * You can not provide debugInfo without an user explanation. If you don't provide a caption, message, or
+ * explanation, Kopete will use a default explanation.
+ */
+void KOPETE_EXPORT notifyConnectionLost( const Account *account,
+ const QString &caption = QString::null,
+ const QString &message = QString::null,
+ const QString &explanation = QString::null,
+ const QString &debugInfo = QString::null );
+
+
+/**
+ * Notifies the user the server is not reachable without coupling plugins with GUI code.
+ *
+ * @param account The account that cannot establish a connection and want to notify the user about that.
+ * @param explanation A long description on how the error occured and what the user can do about it.
+ * @param debugInfo Debug info that can be sent to the developers or to the network service owners.
+ *
+ * You can not provide debugInfo without an user explanation. If you don't provide a caption, message, or
+ * explanation, Kopete will use a default explanation.
+ */
+void KOPETE_EXPORT notifyCannotConnect( const Account *account,
+ const QString &explanation = QString::null,
+ const QString &debugInfo = QString::null);
+
+/**
+ * Notifies the user that an error on a connection occcured without coupling plugins with GUI code.
+ *
+ * @param account The account where the connection error occured and wants to notify the user.
+ * @param caption A brief subject line, used where possible if the presentation allows it.
+ * @param message A short description of the error.
+ * @param explanation A long description on how the error occured and what the user can do about it.
+ * @param debugInfo Debug info that can be sent to the developers or to the network service owners.
+ *
+ * You can not provide debugInfo without an user explanation. If you don't provide a caption, message, or
+ * explanation, Kopete will use a default explanation.
+ */
+void KOPETE_EXPORT notifyConnectionError( const Account *account,
+ const QString &caption = QString::null,
+ const QString &message = QString::null,
+ const QString &explanation = QString::null,
+ const QString &debugInfo = QString::null );
+
+/**
+ * Notifies the user that an error on the server occcured without coupling plugins with GUI code.
+ *
+ * @param account The account where the server error occured and wants to notify the user.
+ * @param caption A brief subject line, used where possible if the presentation allows it.
+ * @param message A short description of the error.
+ * @param explanation A long description on how the error occured and what the user can do about it.
+ * @param debugInfo Debug info that can be sent to the developers or to the network service owners.
+ *
+ * You can not provide debugInfo without an user explanation. If you don't provide a caption, message, or
+ * explanation, Kopete will use a default explanation.
+ */
+void KOPETE_EXPORT notifyServerError( const Account *account,
+ const QString &caption = QString::null,
+ const QString &message = QString::null,
+ const QString &explanation = QString::null,
+ const QString &debugInfo = QString::null );
+} // end ns Utils
+} // end ns Kopete
+
+#endif
diff --git a/kopete/libkopete/kopeteversion.h b/kopete/libkopete/kopeteversion.h
new file mode 100644
index 00000000..9775347c
--- /dev/null
+++ b/kopete/libkopete/kopeteversion.h
@@ -0,0 +1,29 @@
+/*
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETE_VERSION_H_
+#define _KOPETE_VERSION_H_
+
+#define KOPETE_VERSION_STRING "0.12.7"
+#define KOPETE_VERSION_MAJOR 0
+#define KOPETE_VERSION_MINOR 12
+#define KOPETE_VERSION_RELEASE 7
+#define KOPETE_MAKE_VERSION( a,b,c ) (((a) << 16) | ((b) << 8) | (c))
+
+#define KOPETE_VERSION \
+ KOPETE_MAKE_VERSION(KOPETE_VERSION_MAJOR,KOPETE_VERSION_MINOR,KOPETE_VERSION_RELEASE)
+
+#define KOPETE_IS_VERSION(a,b,c) ( KOPETE_VERSION >= KOPETE_MAKE_VERSION(a,b,c) )
+
+
+#endif // _KOPETE_VERSION_H_
diff --git a/kopete/libkopete/kopetewalletmanager.cpp b/kopete/libkopete/kopetewalletmanager.cpp
new file mode 100644
index 00000000..e1d198fc
--- /dev/null
+++ b/kopete/libkopete/kopetewalletmanager.cpp
@@ -0,0 +1,190 @@
+/*
+ kopetewalletmanager.cpp - Kopete Wallet Manager
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetewalletmanager.h"
+
+#include "kopeteuiglobal.h"
+
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+#include <kwallet.h>
+
+#include <qtimer.h>
+#include <qwidget.h>
+#include <qapplication.h>
+
+static WId mainWindowID()
+{
+ if ( QWidget *w = Kopete::UI::Global::mainWidget() )
+ return w->winId();
+ return 0;
+}
+
+class Kopete::WalletManager::Private
+{
+public:
+ Private() : wallet(0), signal(0) {}
+ ~Private() { delete wallet; delete signal; }
+
+ KWallet::Wallet *wallet;
+
+ // we can't just connect every slot that wants the wallet to the
+ // walletOpened signal - since we disconnect all the slots immediately
+ // after emitting the signal, this would result in everyone who asked
+ // for the wallet again in response to a walletOpened signal to fail
+ // to receive it.
+ // instead, we store a KopeteWalletSignal which we connect to, and create
+ // a new one for each set of requests.
+ KopeteWalletSignal *signal;
+};
+
+Kopete::WalletManager::WalletManager()
+ : d( new Private )
+{
+}
+
+Kopete::WalletManager::~WalletManager()
+{
+ closeWallet();
+ delete d;
+}
+
+Kopete::WalletManager *Kopete::WalletManager::self()
+{
+ static KStaticDeleter<Kopete::WalletManager> s_deleter;
+ static Kopete::WalletManager *s_self = 0;
+
+ if ( !s_self )
+ s_deleter.setObject( s_self, new Kopete::WalletManager() );
+ return s_self;
+}
+
+void Kopete::WalletManager::openWallet( QObject *object, const char *slot )
+{
+ if ( !d->signal )
+ d->signal = new KopeteWalletSignal;
+ // allow connecting to protected slots by calling object->connect
+ connect( d->signal, SIGNAL( walletOpened( KWallet::Wallet* ) ), object, slot );
+ //object->connect( d->signal, SIGNAL( walletOpened( KWallet::Wallet* ) ), slot );
+ openWalletInner();
+}
+
+void Kopete::WalletManager::openWalletInner()
+{
+ // do we already have a wallet?
+ if ( d->wallet )
+ {
+ // if the wallet isn't open yet, we're pending a slotWalletChangedStatus
+ // anyway, so we don't set up a single shot.
+ if ( d->wallet->isOpen() )
+ {
+ kdDebug(14010) << k_funcinfo << " wallet already open" << endl;
+ QTimer::singleShot( 0, this, SLOT( slotGiveExistingWallet() ) );
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << " still waiting for earlier request" << endl;
+ }
+ return;
+ }
+
+ kdDebug(14010) << k_funcinfo << " about to open wallet async" << endl;
+
+ // we have no wallet: ask for one.
+ d->wallet = KWallet::Wallet::openWallet( KWallet::Wallet::NetworkWallet(),
+ mainWindowID(), KWallet::Wallet::Asynchronous );
+
+ connect( d->wallet, SIGNAL( walletOpened(bool) ), SLOT( slotWalletChangedStatus() ) );
+}
+
+void Kopete::WalletManager::slotWalletChangedStatus()
+{
+ kdDebug(14010) << k_funcinfo << " isOpen: " << d->wallet->isOpen() << endl;
+
+ if( d->wallet->isOpen() )
+ {
+ if ( !d->wallet->hasFolder( QString::fromLatin1( "Kopete" ) ) )
+ d->wallet->createFolder( QString::fromLatin1( "Kopete" ) );
+
+ if ( d->wallet->setFolder( QString::fromLatin1( "Kopete" ) ) )
+ {
+ // success!
+ QObject::connect( d->wallet, SIGNAL( walletClosed() ), this, SLOT( closeWallet() ) );
+ }
+ else
+ {
+ // opened OK, but we can't use it
+ delete d->wallet;
+ d->wallet = 0;
+ }
+ }
+ else
+ {
+ // failed to open
+ delete d->wallet;
+ d->wallet = 0;
+ }
+
+ emitWalletOpened( d->wallet );
+}
+
+void Kopete::WalletManager::slotGiveExistingWallet()
+{
+ kdDebug(14010) << k_funcinfo << " with d->wallet " << d->wallet << endl;
+
+ if ( d->wallet )
+ {
+ // the wallet was already open
+ if ( d->wallet->isOpen() )
+ emitWalletOpened( d->wallet );
+ // if the wallet was not open, but d->wallet is not 0,
+ // then we're waiting for it to open, and will be told
+ // when it's done: do nothing.
+ else
+ kdDebug(14010) << k_funcinfo << " wallet gone, waiting for another wallet" << endl;
+ }
+ else
+ {
+ // the wallet was lost between us trying to open it and
+ // getting called back. try to reopen it.
+ openWalletInner();
+ }
+}
+
+void Kopete::WalletManager::closeWallet()
+{
+ if ( !d->wallet ) return;
+
+ delete d->wallet;
+ d->wallet = 0L;
+
+ emit walletLost();
+}
+
+void Kopete::WalletManager::emitWalletOpened( KWallet::Wallet *wallet )
+{
+ KopeteWalletSignal *signal = d->signal;
+ d->signal = 0;
+ if ( signal )
+ emit signal->walletOpened( wallet );
+ delete signal;
+}
+
+
+#include "kopetewalletmanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/kopetewalletmanager.h b/kopete/libkopete/kopetewalletmanager.h
new file mode 100644
index 00000000..fdd3a154
--- /dev/null
+++ b/kopete/libkopete/kopetewalletmanager.h
@@ -0,0 +1,116 @@
+/*
+ kopetewalletmanager.h - Kopete Wallet Manager
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEWALLETMANAGER_H
+#define KOPETEWALLETMANAGER_H
+
+#include <qobject.h>
+
+#include <kdemacros.h>
+
+#include "kopete_export.h"
+
+namespace KWallet { class Wallet; }
+
+namespace Kopete
+{
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ *
+ * The Kopete::WalletManager class is a singleton, which looks after Kopete's
+ * KWallet connection.
+ */
+class KOPETE_EXPORT WalletManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Retrieve the wallet manager instance
+ */
+ static WalletManager *self();
+ ~WalletManager();
+
+ /**
+ * @brief Attempt to open the KWallet asyncronously, then signal an
+ * object to indicate the task is complete.
+ *
+ * @param object The object to call back to
+ * @param slot The slot on object to call; must have signature slot( KWallet::Wallet* )
+ * The parameter to the slot will be the wallet that was opened if the call
+ * succeeded, or NULL if the wallet failed to open or the Kopete folder was
+ * inaccessible.
+ *
+ * For simplicity of client code, it is guaranteed that your slot
+ * will not be called during a call to this function.
+ */
+ void openWallet( QObject *object, const char *slot );
+
+public slots:
+ /**
+ * Close the connection to the wallet. Will cause walletLost() to be emitted.
+ */
+ void closeWallet();
+
+signals:
+ /**
+ * Emitted when the connection to the wallet is lost.
+ */
+ void walletLost();
+
+private slots:
+ /**
+ * Called by the stored wallet pointer when it is successfully opened or
+ * when it fails.
+ *
+ * Causes walletOpened to be emitted.
+ */
+ void slotWalletChangedStatus();
+
+ /**
+ * Called by a singleShot timer in the event that we are asked for a
+ * wallet when we already have one open and ready.
+ */
+ void slotGiveExistingWallet();
+
+private:
+ void openWalletInner();
+ void emitWalletOpened( KWallet::Wallet *wallet );
+
+ class Private;
+ Private *d;
+
+ WalletManager();
+};
+
+}
+
+/**
+ * @internal
+ */
+class KopeteWalletSignal : public QObject
+{
+ Q_OBJECT
+ friend class Kopete::WalletManager;
+signals:
+ void walletOpened( KWallet::Wallet *wallet );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/managedconnectionaccount.cpp b/kopete/libkopete/managedconnectionaccount.cpp
new file mode 100644
index 00000000..0f1625b2
--- /dev/null
+++ b/kopete/libkopete/managedconnectionaccount.cpp
@@ -0,0 +1,73 @@
+/*
+ managedconnectionaccount.h - Kopete Account that uses a manager to
+ control its connection and respond to connection events
+
+ Copyright (c) 2005 by Will Stephenson <lists@stevello.free-online.co.uk>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connectionmanager.h"
+#include "kopeteuiglobal.h"
+
+#include "managedconnectionaccount.h"
+
+
+namespace Kopete
+{
+
+ManagedConnectionAccount::ManagedConnectionAccount( Protocol *parent, const QString &acctId, uint maxPasswordLength, const char *name )
+ : PasswordedAccount( parent, acctId, maxPasswordLength, name ), m_waitingForConnection( false )
+{
+ QObject::connect( ConnectionManager::self(), SIGNAL(statusChanged(const QString&, NetworkStatus::EnumStatus ) ),
+ SLOT(slotConnectionStatusChanged(const QString&, NetworkStatus::EnumStatus ) ) );
+}
+
+void ManagedConnectionAccount::connectWithPassword( const QString &password )
+{
+ m_password = password;
+ NetworkStatus::EnumStatus status = ConnectionManager::self()->status( QString::null );
+ if ( status == NetworkStatus::NoNetworks )
+ performConnectWithPassword( password );
+ else
+ {
+ m_waitingForConnection = true;
+ // need to adapt libkopete so we know the hostname in this class and whether the connection was user initiated
+ // for now, these are the default parameters to always bring up a connection to "the internet".
+ NetworkStatus::EnumRequestResult response = ConnectionManager::self()->requestConnection( Kopete::UI::Global::mainWidget(), QString::null, true );
+ if ( response == NetworkStatus::Connected )
+ {
+ m_waitingForConnection = false;
+ performConnectWithPassword( password );
+ }
+ else if ( response == NetworkStatus::UserRefused || response == NetworkStatus::Unavailable )
+ disconnect();
+ }
+}
+
+void ManagedConnectionAccount::slotConnectionStatusChanged( const QString & host, NetworkStatus::EnumStatus status )
+{
+ Q_UNUSED(host); // as above, we didn't register a hostname, so treat any connection as our own.
+
+ if ( m_waitingForConnection && ( status == NetworkStatus::Online || status == NetworkStatus::NoNetworks ) )
+ {
+ m_waitingForConnection = false;
+ performConnectWithPassword( m_password );
+ }
+ else if ( isConnected() && ( status == NetworkStatus::Offline
+ || status == NetworkStatus::ShuttingDown
+ || status == NetworkStatus::OfflineDisconnected
+ || status == NetworkStatus::OfflineFailed ) )
+ disconnect();
+}
+
+} // end namespace Kopete
+#include "managedconnectionaccount.moc"
diff --git a/kopete/libkopete/managedconnectionaccount.h b/kopete/libkopete/managedconnectionaccount.h
new file mode 100644
index 00000000..ad29feed
--- /dev/null
+++ b/kopete/libkopete/managedconnectionaccount.h
@@ -0,0 +1,79 @@
+/*
+ managedconnectionaccount.h - Kopete Account that uses a manager to
+ control its connection and respond to connection events
+
+ Copyright (c) 2005 by Will Stephenson <lists@stevello.free-online.co.uk>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MANAGEDCONNECTIONACCOUNT_H
+#define MANAGEDCONNECTIONACCOUNT_H
+
+#include "networkstatuscommon.h"
+
+#include "kopetepasswordedaccount.h"
+
+namespace Kopete
+{
+class Protocol;
+
+/**
+ * A ManagedConnectionAccount queries the NetworkStatus KDED Module before trying to connect using
+ * connectwithPassword, starting a network connection if needed. If the network is not available,
+ * it delays calling performConnectWithPassword until it receives notification from the daemon
+ * that the network is up. The account receiveds notifications from the daemon of network failures
+ * and calls disconnect to set the account offline in a timely manner.
+ */
+class KOPETE_EXPORT ManagedConnectionAccount : public PasswordedAccount
+{
+ Q_OBJECT
+ public:
+ /**
+ * @brief ManagedConnectionAccount constructor.
+ * @param parent The protocol this account connects via
+ * @param acctId The ID of this account - should be unique within this protocol
+ * @param maxPasswordLength The maximum length for passwords for this account, or 0 for no limit
+ * @param name The name for this QObject
+ */
+ ManagedConnectionAccount( Protocol *parent, const QString &acctId, uint maxPasswordLength = 0, const char *name = 0 );
+ public slots:
+ /**
+ * @brief Begin the connection process, by checking if the connection is available with the ConnectionManager.
+ * This method is called by PasswordedAccount::connect()
+ * @param password the password to connect with.
+ */
+ void connectWithPassword( const QString &password );
+ protected:
+ /**
+ * @brief Connect to the server, once the network is available.
+ * This method is called by the ManagedConnectionAccount once the network is available. In this method you should set up your
+ * network connection and connect to the server.
+ */
+ virtual void performConnectWithPassword( const QString & password ) = 0;
+ protected slots:
+ /**
+ * @brief Handle a change in the network connection
+ * Called by the ConnectionManager when the network comes up or fails.
+ * The default implementation calls performConnectWithPassword when the network goes online and connectWithPassword() was
+ * previously called, and calls disconnect() when the connection goes down.
+ * @param host For future expansion.
+ * @param status the new status of the network
+ */
+ virtual void slotConnectionStatusChanged( const QString & host, NetworkStatus::EnumStatus status );
+ private:
+ QString m_password;
+ bool m_waitingForConnection;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/networkstatuscommon.cpp b/kopete/libkopete/networkstatuscommon.cpp
new file mode 100644
index 00000000..216752bd
--- /dev/null
+++ b/kopete/libkopete/networkstatuscommon.cpp
@@ -0,0 +1,32 @@
+#include "networkstatuscommon.h"
+#include <kdebug.h>
+
+QDataStream & operator<< ( QDataStream & s, const NetworkStatus::Properties p )
+{
+ kdDebug() << k_funcinfo << "status is: " << (int)p.status << endl;
+ s << (int)p.status;
+ s << (int)p.onDemandPolicy;
+ s << p.service;
+ s << ( p.internet ? 1 : 0 );
+ s << p.netmasks;
+ return s;
+}
+
+QDataStream & operator>> ( QDataStream & s, NetworkStatus::Properties &p )
+{
+ int status, onDemandPolicy, internet;
+ s >> status;
+ kdDebug() << k_funcinfo << "status is: " << status << endl;
+ p.status = ( NetworkStatus::EnumStatus )status;
+ s >> onDemandPolicy;
+ p.onDemandPolicy = ( NetworkStatus::EnumOnDemandPolicy )onDemandPolicy;
+ s >> p.service;
+ s >> internet;
+ if ( internet )
+ p.internet = true;
+ else
+ p.internet = false;
+ s >> p.netmasks;
+ kdDebug() << k_funcinfo << "enum converted status is: " << p.status << endl;
+ return s;
+}
diff --git a/kopete/libkopete/networkstatuscommon.h b/kopete/libkopete/networkstatuscommon.h
new file mode 100644
index 00000000..e6906445
--- /dev/null
+++ b/kopete/libkopete/networkstatuscommon.h
@@ -0,0 +1,33 @@
+#ifndef NETWORKSTATUS_COMMON_H
+#define NETWORKSTATUS_COMMON_H
+
+#include <qstringlist.h>
+
+namespace NetworkStatus
+{
+ enum EnumStatus { NoNetworks = 1, Unreachable, OfflineDisconnected, OfflineFailed, ShuttingDown, Offline, Establishing, Online };
+ enum EnumRequestResult { RequestAccepted = 1, Connected, UserRefused, Unavailable };
+ enum EnumOnDemandPolicy { All, User, None, Permanent };
+ struct Properties
+ {
+ QString name;
+ // status of the network
+ EnumStatus status;
+ // policy for on-demand usage as defined by the service
+ EnumOnDemandPolicy onDemandPolicy;
+ // identifier for the service
+ QCString service;
+ // indicate that the connection is to 'the internet' - similar to default gateway in routing
+ bool internet;
+ // list of netmasks that the network connects to - overridden by above internet
+ QStringList netmasks;
+ // for future expansion consider
+ // EnumChargingModel - FlatRate, TimeCharge, VolumeCharged
+ // EnumLinkStatus - for WLANs - VPOOR, POOR, AVERAGE, GOOD, EXCELLENT
+ };
+}
+
+QDataStream & operator>> ( QDataStream & s, NetworkStatus::Properties &p );
+QDataStream & operator<< ( QDataStream & s, const NetworkStatus::Properties p );
+
+#endif
diff --git a/kopete/libkopete/private/Makefile.am b/kopete/libkopete/private/Makefile.am
new file mode 100644
index 00000000..15e930df
--- /dev/null
+++ b/kopete/libkopete/private/Makefile.am
@@ -0,0 +1,13 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \
+ $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteprivate.la
+
+libkopeteprivate_la_SOURCES = kopeteemoticons.cpp \
+ kopetecommand.cpp kopeteviewmanager.cpp kopeteutils_private.cpp
+libkopeteprivate_la_LDFLAGS = $(all_libraries)
+libkopeteprivate_la_LIBADD = $(LIB_KDEUI)
+# vim: set noet:
+
diff --git a/kopete/libkopete/private/kopetecommand.cpp b/kopete/libkopete/private/kopetecommand.cpp
new file mode 100644
index 00000000..52588f2e
--- /dev/null
+++ b/kopete/libkopete/private/kopetecommand.cpp
@@ -0,0 +1,142 @@
+/*
+ kopetecommand.cpp - Command
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstringlist.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+#include "kopetecommand.h"
+#include "kopeteuiglobal.h"
+
+Kopete::Command::Command( QObject *parent, const QString &command, const char* handlerSlot,
+ const QString &help, Kopete::CommandHandler::CommandType type, const QString &formatString,
+ uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix )
+ : KAction( command[0].upper() + command.right( command.length() - 1).lower(), pix, cut, parent,
+ ( command.lower() + QString::fromLatin1("_command") ).latin1() )
+{
+ init( command, handlerSlot, help, type, formatString, minArgs, maxArgs );
+}
+
+void Kopete::Command::init( const QString &command, const char* slot, const QString &help,
+ Kopete::CommandHandler::CommandType type, const QString &formatString, uint minArgs, int maxArgs )
+{
+ m_command = command;
+ m_help = help;
+ m_type = type;
+ m_formatString = formatString;
+ m_minArgs = minArgs;
+ m_maxArgs = maxArgs;
+ m_processing = false;
+
+ if( m_type == Kopete::CommandHandler::Normal )
+ {
+ QObject::connect( this, SIGNAL( handleCommand( const QString &, Kopete::ChatSession *) ),
+ parent(), slot );
+ }
+
+ QObject::connect( this, SIGNAL( activated() ), this, SLOT( slotAction() ) );
+}
+
+void Kopete::Command::slotAction()
+{
+ Kopete::ChatSession *manager = Kopete::ChatSessionManager::self()->activeView()->msgManager();
+
+ QString args;
+ if( m_minArgs > 0 )
+ {
+ args = KInputDialog::getText( i18n("Enter Arguments"), i18n("Enter the arguments to %1:").arg(m_command) );
+ if( args.isNull() )
+ return;
+ }
+
+ processCommand( args, manager, true );
+}
+
+void Kopete::Command::processCommand( const QString &args, Kopete::ChatSession *manager, bool gui )
+{
+ QStringList mArgs = Kopete::CommandHandler::parseArguments( args );
+ if( m_processing )
+ {
+ printError( i18n("Alias \"%1\" expands to itself.").arg( text() ), manager, gui );
+ }
+ else if( mArgs.count() < m_minArgs )
+ {
+ printError( i18n("\"%1\" requires at least %n argument.",
+ "\"%1\" requires at least %n arguments.", m_minArgs)
+ .arg( text() ), manager, gui );
+ }
+ else if( m_maxArgs > -1 && (int)mArgs.count() > m_maxArgs )
+ {
+ printError( i18n("\"%1\" has a maximum of %n argument.",
+ "\"%1\" has a maximum of %n arguments.", m_minArgs)
+ .arg( text() ), manager, gui );
+ }
+ else if( !KApplication::kApplication()->authorizeKAction( name() ) )
+ {
+ printError( i18n("You are not authorized to perform the command \"%1\".").arg(text()), manager, gui );
+ }
+ else
+ {
+ m_processing = true;
+ if( m_type == Kopete::CommandHandler::UserAlias ||
+ m_type == Kopete::CommandHandler::SystemAlias )
+ {
+ QString formatString = m_formatString;
+
+ // Translate %s to the whole string and %n to current nickname
+
+ formatString.replace( QString::fromLatin1("%n"), manager->myself()->nickName() );
+ formatString.replace( QString::fromLatin1("%s"), args );
+
+ // Translate %1..%N to word1..wordN
+
+ while( mArgs.count() > 0 )
+ {
+ formatString = formatString.arg( mArgs.front() );
+ mArgs.pop_front();
+ }
+
+ kdDebug(14010) << "New Command after processing alias: " << formatString << endl;
+
+ Kopete::CommandHandler::commandHandler()->processMessage( QString::fromLatin1("/") + formatString, manager );
+ }
+ else
+ {
+ emit( handleCommand( args, manager ) );
+ }
+ m_processing = false;
+ }
+}
+
+void Kopete::Command::printError( const QString &error, Kopete::ChatSession *manager, bool gui ) const
+{
+ if( gui )
+ {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(), error, i18n("Command Error") );
+ }
+ else
+ {
+ Kopete::Message msg( manager->myself(), manager->members(), error,
+ Kopete::Message::Internal, Kopete::Message::PlainText );
+ manager->appendMessage( msg );
+ }
+}
+
+#include "kopetecommand.moc"
diff --git a/kopete/libkopete/private/kopetecommand.h b/kopete/libkopete/private/kopetecommand.h
new file mode 100644
index 00000000..298872db
--- /dev/null
+++ b/kopete/libkopete/private/kopetecommand.h
@@ -0,0 +1,109 @@
+
+/*
+ kopetecommand.h - Command
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __KOPETECOMMAND_H__
+#define __KOPETECOMMAND_H__
+
+#include <qobject.h>
+#include <kaction.h>
+#include "kopetecommandhandler.h"
+
+namespace Kopete
+{
+
+class ChatSession;
+
+class Command : public KAction
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Creates a Kopete::Command object
+ *
+ * @param parent The plugin who owns this command
+ * @param command The command we want to handle, not including the '/'
+ * @param handlerSlot The slot used to handle the command. This slot must
+ * accept two parameters, a QString of arguments, and a Kopete::ChatSession
+ * pointer to the Manager under which the command was sent.
+ * @param help An optional help string to be shown when the user uses
+ * /help <i>command</i>
+ * @param type If this command is an alias, and what type
+ * @param formatString The formatString of the alias if any
+ * @param minArgs Minimum number of arguments
+ * @param maxArgs Maximum number of arguments
+ * @param cut The shortcut for the command
+ * @param pix The icon to use for the command
+ */
+ Command( QObject *parent, const QString &command, const char* handlerSlot,
+ const QString &help = QString::null, CommandHandler::CommandType type = CommandHandler::Normal, const QString &formatString = QString::null,
+ uint minArgs = 0, int maxArgs = -1, const KShortcut &cut = 0,
+ const QString &pix = QString::null );
+
+ /**
+ * Process this command
+ */
+ void processCommand( const QString &args, ChatSession *manager, bool gui = false );
+
+ /**
+ * Returns the command this object handles
+ */
+ const QString &command() const { return m_command; };
+
+ /**
+ * Returns the help string for this command
+ */
+ const QString &help() const { return m_help; };
+
+ /**
+ * Returns the type of the command
+ */
+ const CommandHandler::CommandType type() const { return m_type; };
+
+ signals:
+ /**
+ * Emitted whenever a command is handled by this object. When a command
+ * has been handled, all processing on it stops by the command handler
+ * (a command cannot be handled twice)
+ */
+ void handleCommand( const QString &args, Kopete::ChatSession *manager );
+
+ private slots:
+ /**
+ * Connected to our activated() signal
+ */
+ void slotAction();
+
+ private:
+ void init( const QString &command, const char* slot, const QString &help,
+ CommandHandler::CommandType type, const QString &formatString,
+ uint minArgs, int maxArgs );
+
+ void printError( const QString &error, ChatSession *manager, bool gui = false ) const;
+
+ QString m_command;
+ QString m_help;
+ QString m_formatString;
+ uint m_minArgs;
+ int m_maxArgs;
+ bool m_processing;
+ CommandHandler::CommandType m_type;
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/private/kopeteemoticons.cpp b/kopete/libkopete/private/kopeteemoticons.cpp
new file mode 100644
index 00000000..87da4cf7
--- /dev/null
+++ b/kopete/libkopete/private/kopeteemoticons.cpp
@@ -0,0 +1,559 @@
+/*
+ kopeteemoticons.cpp - Kopete Preferences Container-Class
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2002-2006 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteemoticons.h"
+
+#include "kopeteprefs.h"
+
+#include <qdom.h>
+#include <qfile.h>
+#include <qstylesheet.h>
+#include <qimage.h>
+#include <qdatetime.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kdeversion.h>
+
+#include <set>
+#include <algorithm>
+#include <iterator>
+
+
+/*
+ * Testcases can be found in the kopeteemoticontest app in the tests/ directory.
+ */
+
+
+namespace Kopete {
+
+
+struct Emoticons::Emoticon
+{
+ Emoticon(){}
+ /* sort by longest to shortest matchText */
+ bool operator< (const Emoticon &e){ return matchText.length() > e.matchText.length(); }
+ QString matchText;
+ QString matchTextEscaped;
+ QString picPath;
+ QString picHTMLCode;
+};
+
+/* This is the object we will store each emoticon match in */
+struct Emoticons::EmoticonNode {
+ const Emoticon emoticon;
+ int pos;
+ EmoticonNode() : emoticon(), pos( -1 ) {}
+ EmoticonNode( const Emoticon e, int p ) : emoticon( e ), pos( p ) {}
+};
+
+class Emoticons::Private
+{
+public:
+ QMap<QChar, QValueList<Emoticon> > emoticonMap;
+ QMap<QString, QStringList> emoticonAndPicList;
+
+ /**
+ * The current icon theme from KopetePrefs
+ */
+ QString theme;
+
+
+};
+
+
+Emoticons *Emoticons::s_self = 0L;
+
+Emoticons *Emoticons::self()
+{
+ if( !s_self )
+ s_self = new Emoticons;
+ return s_self;
+}
+
+
+QString Emoticons::parseEmoticons(const QString& message, ParseMode mode ) //static
+{
+ return self()->parse( message, mode );
+}
+
+QValueList<Emoticons::Token> Emoticons::tokenizeEmoticons( const QString& message, ParseMode mode ) // static
+{
+ return self()->tokenize( message, mode );
+}
+
+QValueList<Emoticons::Token> Emoticons::tokenize( const QString& message, uint mode )
+{
+ QValueList<Token> result;
+ if ( !KopetePrefs::prefs()->useEmoticons() )
+ {
+ result.append( Token( Text, message ) );
+ return result;
+ }
+
+ if( ! ( mode & (StrictParse|RelaxedParse) ) )
+ {
+ //if none of theses two mode are selected, use the mode from the config
+ mode |= KopetePrefs::prefs()->emoticonsRequireSpaces() ? StrictParse : RelaxedParse ;
+ }
+
+ /* previous char, in the firs iteration assume that it is space since we want
+ * to let emoticons at the beginning, the very first previous QChar must be a space. */
+ QChar p = ' ';
+ QChar c; /* current char */
+ QChar n; /* next character after a match candidate, if strict this should be QChar::null or space */
+
+ /* This is the EmoticonNode container, it will represent each matched emoticon */
+ QValueList<EmoticonNode> foundEmoticons;
+ QValueList<EmoticonNode>::const_iterator found;
+ /* First-pass, store the matched emoticon locations in foundEmoticons */
+ QValueList<Emoticon> emoticonList;
+ QValueList<Emoticon>::const_iterator it;
+ size_t pos;
+
+ bool inHTMLTag = false;
+ bool inHTMLLink = false;
+ bool inHTMLEntity = false;
+ QString needle; // search for this
+ for ( pos = 0; pos < message.length(); pos++ )
+ {
+ c = message[ pos ];
+
+ if ( mode & SkipHTML ) // Shall we skip HTML ?
+ {
+ if ( !inHTMLTag ) // Are we already in an HTML tag ?
+ {
+ if ( c == '<' ) { // If not check if are going into one
+ inHTMLTag = true; // If we are, change the state to inHTML
+ p = c;
+ continue;
+ }
+ }
+ else // We are already in a HTML tag
+ {
+ if ( c == '>' ) { // Check if it ends
+ inHTMLTag = false; // If so, change the state
+ if ( p == 'a' )
+ {
+ inHTMLLink = false;
+ }
+ }
+ else if ( c == 'a' && p == '<' ) // check if we just entered an achor tag
+ {
+ inHTMLLink = true; // don't put smileys in urls
+ }
+ p = c;
+ continue;
+ }
+
+ if( !inHTMLEntity )
+ { // are we
+ if( c == '&' )
+ {
+ inHTMLEntity = true;
+ }
+ }
+ }
+
+ if ( inHTMLLink ) // i can't think of any situation where a link adress might need emoticons
+ {
+ p = c;
+ continue;
+ }
+
+ if ( (mode & StrictParse) && !p.isSpace() && p != '>')
+ { // '>' may mark the end of an html tag
+ p = c;
+ continue;
+ } /* strict requires space before the emoticon */
+ if ( d->emoticonMap.contains( c ) )
+ {
+ emoticonList = d->emoticonMap[ c ];
+ bool found = false;
+ for ( it = emoticonList.begin(); it != emoticonList.end(); ++it )
+ {
+ // If this is an HTML, then search for the HTML form of the emoticon.
+ // For instance <o) => &gt;o)
+ needle = ( mode & SkipHTML ) ? (*it).matchTextEscaped : (*it).matchText;
+ if ( ( pos == (size_t)message.find( needle, pos ) ) )
+ {
+ if( mode & StrictParse )
+ {
+ /* check if the character after this match is space or end of string*/
+ n = message[ pos + needle.length() ];
+ //<br/> marks the end of a line
+ if( n != '<' && !n.isSpace() && !n.isNull() && n!= '&')
+ break;
+ }
+ /* Perfect match */
+ foundEmoticons.append( EmoticonNode( (*it), pos ) );
+ found = true;
+ /* Skip the matched emoticon's matchText */
+ pos += needle.length() - 1;
+ break;
+ }
+ }
+ if( !found )
+ {
+ if( inHTMLEntity ){
+ // If we are in an HTML entitiy such as &gt;
+ int htmlEnd = message.find( ';', pos );
+ // Search for where it ends
+ if( htmlEnd == -1 )
+ {
+ // Apparently this HTML entity isn't ended, something is wrong, try skip the '&'
+ // and continue
+ kdDebug( 14000 ) << k_funcinfo << "Broken HTML entity, trying to recover." << endl;
+ inHTMLEntity = false;
+ pos++;
+ }
+ else
+ {
+ pos = htmlEnd;
+ inHTMLEntity = false;
+ }
+ }
+ }
+ } /* else no emoticons begin with this character, so don't do anything */
+ p = c;
+ }
+
+ /* if no emoticons found just return the text */
+ if ( foundEmoticons.isEmpty() )
+ {
+ result.append( Token( Text, message ) );
+ return result;
+ }
+
+ /* Second-pass, generate tokens based on the matches */
+
+ pos = 0;
+ int length;
+
+ for ( found = foundEmoticons.begin(); found != foundEmoticons.end(); ++found )
+ {
+ needle = ( mode & SkipHTML ) ? (*found).emoticon.matchTextEscaped : (*found).emoticon.matchText;
+ if ( ( length = ( (*found).pos - pos ) ) )
+ {
+ result.append( Token( Text, message.mid( pos, length ) ) );
+ result.append( Token( Image, (*found).emoticon.matchTextEscaped, (*found).emoticon.picPath, (*found).emoticon.picHTMLCode ) );
+ pos += length + needle.length();
+ }
+ else
+ {
+ result.append( Token( Image, (*found).emoticon.matchTextEscaped, (*found).emoticon.picPath, (*found).emoticon.picHTMLCode ) );
+ pos += needle.length();
+ }
+ }
+
+ if ( message.length() - pos ) // if there is remaining regular text
+ {
+ result.append( Token( Text, message.mid( pos ) ) );
+ }
+
+ return result;
+}
+
+Emoticons::Emoticons( const QString &theme ) : QObject( kapp, "KopeteEmoticons" )
+{
+// kdDebug(14010) << "KopeteEmoticons::KopeteEmoticons" << endl;
+ d=new Private;
+ if(theme.isNull())
+ {
+ initEmoticons();
+ connect( KopetePrefs::prefs(), SIGNAL(saved()), this, SLOT(initEmoticons()) );
+ }
+ else
+ {
+ initEmoticons( theme );
+ }
+}
+
+
+Emoticons::~Emoticons( )
+{
+ delete d;
+}
+
+
+
+void Emoticons::addIfPossible( const QString& filenameNoExt, const QStringList &emoticons )
+{
+ KStandardDirs *dir = KGlobal::dirs();
+ QString pic;
+
+ //maybe an extension was given, so try to find the exact file
+ pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt );
+
+ if( pic.isNull() )
+ pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".mng" ) );
+ if ( pic.isNull() )
+ pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".png" ) );
+ if ( pic.isNull() )
+ pic = dir->findResource( "emoticons", d->theme + QString::fromLatin1( "/" ) + filenameNoExt + QString::fromLatin1( ".gif" ) );
+
+ if( !pic.isNull() ) // only add if we found one file
+ {
+ QPixmap p;
+ QString result;
+
+ d->emoticonAndPicList.insert( pic, emoticons );
+
+ for ( QStringList::const_iterator it = emoticons.constBegin(), end = emoticons.constEnd();
+ it != end; ++it )
+ {
+ QString matchEscaped=QStyleSheet::escape(*it);
+
+ Emoticon e;
+ e.picPath = pic;
+
+ // We need to include size (width, height attributes) hints in the emoticon HTML code
+ // Unless we do so, ChatMessagePart::slotScrollView does not work properly and causing
+ // HTMLPart not to be scrolled to the very last message.
+ p.load( e.picPath );
+ result = QString::fromLatin1( "<img align=\"center\" src=\"" ) +
+ e.picPath +
+ QString::fromLatin1( "\" title=\"" ) +
+ matchEscaped +
+ QString::fromLatin1( "\" width=\"" ) +
+ QString::number( p.width() ) +
+ QString::fromLatin1( "\" height=\"" ) +
+ QString::number( p.height() ) +
+ QString::fromLatin1( "\" />" );
+
+ e.picHTMLCode = result;
+ e.matchTextEscaped = matchEscaped;
+ e.matchText = *it;
+ d->emoticonMap[ matchEscaped[0] ].append( e );
+ d->emoticonMap[ (*it)[0] ].append( e );
+ }
+ }
+}
+
+void Emoticons::initEmoticons( const QString &theme )
+{
+ if(theme.isNull())
+ {
+ if ( d->theme == KopetePrefs::prefs()->iconTheme() )
+ return;
+
+ d->theme = KopetePrefs::prefs()->iconTheme();
+ }
+ else
+ {
+ d->theme = theme;
+ }
+
+// kdDebug(14010) << k_funcinfo << "Called" << endl;
+ d->emoticonAndPicList.clear();
+ d->emoticonMap.clear();
+
+ QString filename= KGlobal::dirs()->findResource( "emoticons", d->theme + QString::fromLatin1( "/emoticons.xml" ) );
+ if(!filename.isEmpty())
+ return initEmoticon_emoticonsxml( filename );
+ filename= KGlobal::dirs()->findResource( "emoticons", d->theme + QString::fromLatin1( "/icondef.xml" ) );
+ if(!filename.isEmpty())
+ return initEmoticon_JEP0038( filename );
+ kdWarning(14010) << k_funcinfo << "emotiucon XML theme description not found" <<endl;
+}
+
+void Emoticons::initEmoticon_emoticonsxml( const QString & filename)
+{
+ QDomDocument emoticonMap( QString::fromLatin1( "messaging-emoticon-map" ) );
+
+ QFile mapFile( filename );
+ mapFile.open( IO_ReadOnly );
+ emoticonMap.setContent( &mapFile );
+
+ QDomElement list = emoticonMap.documentElement();
+ QDomNode node = list.firstChild();
+ while( !node.isNull() )
+ {
+ QDomElement element = node.toElement();
+ if( !element.isNull() )
+ {
+ if( element.tagName() == QString::fromLatin1( "emoticon" ) )
+ {
+ QString emoticon_file = element.attribute(
+ QString::fromLatin1( "file" ), QString::null );
+ QStringList items;
+
+ QDomNode emoticonNode = node.firstChild();
+ while( !emoticonNode.isNull() )
+ {
+ QDomElement emoticonElement = emoticonNode.toElement();
+ if( !emoticonElement.isNull() )
+ {
+ if( emoticonElement.tagName() == QString::fromLatin1( "string" ) )
+ {
+ items << emoticonElement.text();
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "Warning: Unknown element '" << element.tagName() <<
+ "' in emoticon data" << endl;
+ }
+ }
+ emoticonNode = emoticonNode.nextSibling();
+ }
+
+ addIfPossible ( emoticon_file, items );
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "Warning: Unknown element '" <<
+ element.tagName() << "' in map file" << endl;
+ }
+ }
+ node = node.nextSibling();
+ }
+ mapFile.close();
+ sortEmoticons();
+}
+
+
+void Emoticons::initEmoticon_JEP0038( const QString & filename)
+{
+ QDomDocument emoticonMap( QString::fromLatin1( "icondef" ) );
+
+ QFile mapFile( filename );
+ mapFile.open( IO_ReadOnly );
+ emoticonMap.setContent( &mapFile );
+
+ QDomElement list = emoticonMap.documentElement();
+ QDomNode node = list.firstChild();
+ while( !node.isNull() )
+ {
+ QDomElement element = node.toElement();
+ if( !element.isNull() )
+ {
+ if( element.tagName() == QString::fromLatin1( "icon" ) )
+ {
+ QStringList items;
+ QString emoticon_file;
+
+ QDomNode emoticonNode = node.firstChild();
+ while( !emoticonNode.isNull() )
+ {
+ QDomElement emoticonElement = emoticonNode.toElement();
+ if( !emoticonElement.isNull() )
+ {
+ if( emoticonElement.tagName() == QString::fromLatin1( "text" ) )
+ {
+ //TODO xml:lang
+ items << emoticonElement.text();
+ }
+ else if( emoticonElement.tagName() == QString::fromLatin1( "object" ) && emoticon_file.isEmpty() )
+ {
+ QString mime= emoticonElement.attribute(
+ QString::fromLatin1( "mime" ), QString::fromLatin1("image/*") );
+ if(mime.startsWith(QString::fromLatin1("image/")) && !mime.endsWith(QString::fromLatin1("/svg+xml")))
+ {
+ emoticon_file = emoticonElement.text();
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "Warning: Unsupported format '" << mime << endl;
+ }
+ }
+ /*else
+ {
+ kdDebug(14010) << k_funcinfo <<
+ "Warning: Unknown element '" << element.tagName() <<
+ "' in emoticon data" << endl;
+ }*/
+ }
+ emoticonNode = emoticonNode.nextSibling();
+ }
+ if( !items.isEmpty() && !emoticon_file.isEmpty() )
+ addIfPossible ( emoticon_file, items );
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "Warning: Unknown element '" <<
+ element.tagName() << "' in map file" << endl;
+ }
+ }
+ node = node.nextSibling();
+ }
+ mapFile.close();
+ sortEmoticons();
+}
+
+
+void Emoticons::sortEmoticons()
+{
+ /* sort strings in order of longest to shortest to provide convenient input for
+ greedy matching in the tokenizer */
+ QValueList<QChar> keys = d->emoticonMap.keys();
+ for ( QValueList<QChar>::const_iterator it = keys.begin(); it != keys.end(); ++it )
+ {
+ QChar key = (*it);
+ QValueList<Emoticon> keyValues = d->emoticonMap[key];
+ qHeapSort(keyValues.begin(), keyValues.end());
+ d->emoticonMap[key] = keyValues;
+ }
+}
+
+
+
+
+QMap<QString, QStringList> Emoticons::emoticonAndPicList()
+{
+ return d->emoticonAndPicList;
+}
+
+
+QString Emoticons::parse( const QString &message, ParseMode mode )
+{
+ if ( !KopetePrefs::prefs()->useEmoticons() )
+ return message;
+
+ QValueList<Token> tokens = tokenize( message, mode );
+ QValueList<Token>::const_iterator token;
+ QString result;
+ QPixmap p;
+ for ( token = tokens.begin(); token != tokens.end(); ++token )
+ {
+ switch ( (*token).type )
+ {
+ case Text:
+ result += (*token).text;
+ break;
+ case Image:
+ result += (*token).picHTMLCode;
+ kdDebug( 14010 ) << k_funcinfo << "Emoticon html code: " << result << endl;
+ break;
+ default:
+ kdDebug( 14010 ) << k_funcinfo << "Unknown token type. Something's broken." << endl;
+ }
+ }
+ return result;
+}
+
+} //END namesapce Kopete
+
+#include "kopeteemoticons.moc"
+
+
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/private/kopeteemoticons.h b/kopete/libkopete/private/kopeteemoticons.h
new file mode 100644
index 00000000..848185e6
--- /dev/null
+++ b/kopete/libkopete/private/kopeteemoticons.h
@@ -0,0 +1,184 @@
+/*
+ kopeteemoticons.cpp - Kopete Preferences Container-Class
+
+ Copyright (c) 2002-2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+ Copyright (c) 2005 by Engin AYDOGAN <engin @ bzzzt.biz>
+
+ *************************************************************************
+ * *
+ * 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 kopeteemoticons_h__
+#define kopeteemoticons_h__
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qregexp.h>
+
+#include "kopete_export.h"
+
+namespace Kopete {
+
+class KOPETE_EXPORT Emoticons : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor: DON'T use it if you want to use the emoticon theme
+ * chosen by the user.
+ * Instead, use @ref Kopete::Emoticons::self()
+ **/
+ Emoticons( const QString &theme = QString::null );
+
+ ~Emoticons( );
+
+ /**
+ * The emoticons container-class by default is a singleton object.
+ * Use this method to retrieve the instance.
+ */
+ static Emoticons *self();
+
+ /**
+ * The possible parse modes
+ */
+ enum ParseMode { DefaultParseMode = 0x0 , /** Use strict or relaxed according the config */
+ StrictParse = 0x1, /** Strict parsing requires a space between each emoticon */
+ RelaxedParse = 0x4, /** Parse mode where all possible emoticon matches are allowed */
+ SkipHTML = 0x2 /** Skip emoticons within HTML */
+ };
+
+ /**
+ * Use it to parse emoticons in a text.
+ * You don't need to use this for chat windows,
+ * There is a special class that abstract a chat view
+ * and uses emoticons parser.
+ * This function will use the selected emoticon theme.
+ * If nicks is provided, they will not be parsed if they
+ * exist in message.
+ */
+ static QString parseEmoticons( const QString &message, ParseMode = SkipHTML ) ;
+
+
+ QString parse( const QString &message, ParseMode = SkipHTML );
+
+ /**
+ * TokenType, a token might be an image ( emoticon ) or text.
+ */
+ enum TokenType { Undefined, /** Undefined, for completeness only */
+ Image, /** Token contains a path to an image */
+ Text /** Token contains test */
+ };
+
+ /**
+ * A token consists of a QString text which is either a regular text
+ * or a path to image depending on the type.
+ * If type is Image the text refers to an image path.
+ * If type is Text the text refers to a regular text.
+ */
+ struct Token {
+ Token() : type( Undefined ) {}
+ Token( TokenType t, const QString &m ) : type( t ), text(m) {}
+ Token( TokenType t, const QString &m, const QString &p, const QString &html )
+ : type( t ), text( m ), picPath( p ), picHTMLCode( html ) {}
+ TokenType type;
+ QString text;
+ QString picPath;
+ QString picHTMLCode;
+ };
+
+
+ /**
+ * Static function which will call tokenize
+ * @see tokenize( const QString& )
+ */
+ static QValueList<Token> tokenizeEmoticons( const QString &message, ParseMode mode = DefaultParseMode );
+
+ /**
+ * Tokenizes an message.
+ * For example;
+ * Assume :], (H), :-x are three emoticons.
+ * A text "(H)(H) foo bar john :] :-x" would be tokenized as follows (not strict):
+ * 1- /path/to/shades.png
+ * 2- /path/to/shades.png
+ * 3- " foo bar john "
+ * 4- /path/to/bat.png
+ * 5- " "
+ * 6- /path/to/kiss.png
+ *
+ * Strict tokenization (require spaces around emoticons):
+ * 1- "(H)(H) foo bar john "
+ * 2- /path/to/bat.png
+ * 3- " "
+ * 4- /path/to/kiss.png
+ * Note: quotation marks are used to emphasize white spaces.
+ * @param message is the message to tokenize
+ * @param mode is a bitmask of ParseMode enum
+ * @return a QValueList which consiste of ordered tokens of the text.
+ * @author Engin AYDOGAN < engin@bzzzt.biz >
+ * @since 23-03-05
+ */
+ QValueList<Token> tokenize( const QString &message, uint mode = DefaultParseMode );
+
+ /**
+ * Return all emoticons and the corresponding icon.
+ * (only one emoticon per image)
+ */
+ QMap<QString, QStringList> emoticonAndPicList();
+
+
+private:
+ /**
+ * Our instance
+ **/
+ static Emoticons *s_self;
+
+ /**
+ * add an emoticon to our mapping if
+ * an animation/pixmap has been found for it
+ **/
+ void addIfPossible( const QString& filenameNoExt, const QStringList &emoticons );
+
+ /**
+ * uses the kopete's emoticons.xml for the theme
+ * @see initEmoticons
+ */
+ void initEmoticon_emoticonsxml( const QString & filename);
+
+ /**
+ * uses the JEP-0038 xml description for the theme
+ * @see initEmoticons
+ */
+ void initEmoticon_JEP0038( const QString & filename);
+
+ /**
+ * sorts emoticons for convenient parsing, which yields greedy matching on
+ * matchText
+ */
+ void sortEmoticons();
+
+
+ struct Emoticon;
+ struct EmoticonNode;
+ class Private;
+ Private *d;
+private slots:
+
+ /**
+ * Fills the map with paths and emoticons
+ * This needs to be done on every emoticon-theme change
+ **/
+ void initEmoticons ( const QString &theme = QString::null );
+};
+
+
+} //END namespace Kopete
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/private/kopeteutils_private.cpp b/kopete/libkopete/private/kopeteutils_private.cpp
new file mode 100644
index 00000000..3746bcd3
--- /dev/null
+++ b/kopete/libkopete/private/kopeteutils_private.cpp
@@ -0,0 +1,85 @@
+/*
+ Kopete Utils.
+ Copyright (c) 2005 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+
+#include <kmessagebox.h>
+
+#include <kdebug.h>
+
+#include "knotification.h"
+#include "kopeteutils_private.h"
+#include "kopeteuiglobal.h"
+
+namespace Kopete
+{
+namespace Utils
+{
+
+NotifyHelper* NotifyHelper::s_self = 0L;
+
+NotifyHelper::NotifyHelper()
+{
+}
+
+NotifyHelper::~NotifyHelper()
+{
+}
+
+NotifyHelper* NotifyHelper::self()
+{
+ if (!s_self)
+ s_self = new NotifyHelper();
+
+ return s_self;
+}
+
+void NotifyHelper::slotEventActivated(unsigned int action)
+{
+ const KNotification *n = dynamic_cast<const KNotification *>(QObject::sender());
+ if (n)
+ {
+ ErrorNotificationInfo info = m_events[n];
+ if ( info.debugInfo.isEmpty() )
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, info.explanation, info.caption);
+ else
+ KMessageBox::queuedDetailedError( Kopete::UI::Global::mainWidget(), info.explanation, info.debugInfo, info.caption);
+
+ unregisterNotification(n);
+ }
+}
+
+void NotifyHelper::slotEventClosed()
+{
+ const KNotification *n = dynamic_cast<const KNotification *>(QObject::sender());
+ if (n)
+ unregisterNotification(n);
+}
+
+void NotifyHelper::registerNotification(const KNotification* event, ErrorNotificationInfo error)
+{
+ m_events.insert( event, error);
+}
+
+void NotifyHelper::unregisterNotification(const KNotification* event)
+{
+ m_events.remove(event);
+}
+
+} // end ns ErrorNotifier
+} // end ns Kopete
+
+#include "kopeteutils_private.moc"
diff --git a/kopete/libkopete/private/kopeteutils_private.h b/kopete/libkopete/private/kopeteutils_private.h
new file mode 100644
index 00000000..a684c965
--- /dev/null
+++ b/kopete/libkopete/private/kopeteutils_private.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Utils.
+
+ Copyright (c) 2005 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_UTILS_PRIVATE_H
+#define KOPETE_UTILS_PRIVATE_H
+
+#include "qobject.h"
+#include "qstring.h"
+#include "qpixmap.h"
+
+class KNotification;
+
+namespace Kopete
+{
+
+namespace Utils
+{
+
+typedef struct
+{
+ QString caption;
+ QString explanation;
+ QString debugInfo;
+} ErrorNotificationInfo;
+
+class NotifyHelper : public QObject
+{
+Q_OBJECT
+public:
+ static NotifyHelper* self();
+ void registerNotification(const KNotification* event, ErrorNotificationInfo error);
+ void unregisterNotification(const KNotification* event);
+public slots:
+ void slotEventActivated(unsigned int action);
+ void slotEventClosed();
+private:
+ NotifyHelper();
+ ~NotifyHelper();
+ QMap<const KNotification*, ErrorNotificationInfo> m_events;
+ static NotifyHelper *s_self;
+};
+
+} // end ns Utils
+} // end ns Kopete
+
+#endif
diff --git a/kopete/libkopete/private/kopeteviewmanager.cpp b/kopete/libkopete/private/kopeteviewmanager.cpp
new file mode 100644
index 00000000..c6d295fd
--- /dev/null
+++ b/kopete/libkopete/private/kopeteviewmanager.cpp
@@ -0,0 +1,364 @@
+/*
+ kopeteviewmanager.cpp - View Manager
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <qptrlist.h>
+#include <qstylesheet.h>
+#include <kplugininfo.h>
+#include <knotification.h>
+#include <kglobal.h>
+#include <kwin.h>
+
+#include "kopeteprefs.h"
+#include "kopeteaccount.h"
+#include "kopetepluginmanager.h"
+#include "kopeteviewplugin.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopetenotifyevent.h"
+#include "kopetemessageevent.h"
+#include "kopeteview.h"
+//#include "systemtray.h"
+
+#include "kopeteviewmanager.h"
+
+typedef QMap<Kopete::ChatSession*,KopeteView*> ManagerMap;
+typedef QPtrList<Kopete::MessageEvent> EventList;
+
+struct KopeteViewManagerPrivate
+{
+ ManagerMap managerMap;
+ EventList eventList;
+ KopeteView *activeView;
+
+ bool useQueueOrStack;
+ bool raiseWindow;
+ bool queueUnreadMessages;
+ bool queueOnlyHighlightedMessagesInGroupChats;
+ bool queueOnlyMessagesOnAnotherDesktop;
+ bool balloonNotifyIgnoreClosesChatView;
+ bool foreignMessage;
+};
+
+KopeteViewManager *KopeteViewManager::s_viewManager = 0L;
+
+KopeteViewManager *KopeteViewManager::viewManager()
+{
+ if( !s_viewManager )
+ s_viewManager = new KopeteViewManager();
+ return s_viewManager;
+}
+
+KopeteViewManager::KopeteViewManager()
+{
+ s_viewManager=this;
+ d = new KopeteViewManagerPrivate;
+ d->activeView = 0L;
+ d->foreignMessage=false;
+
+ connect( KopetePrefs::prefs(), SIGNAL( saved() ), this, SLOT( slotPrefsChanged() ) );
+
+ connect( Kopete::ChatSessionManager::self() , SIGNAL( display( Kopete::Message &, Kopete::ChatSession *) ),
+ this, SLOT ( messageAppended( Kopete::Message &, Kopete::ChatSession *) ) );
+
+ connect( Kopete::ChatSessionManager::self() , SIGNAL( readMessage() ),
+ this, SLOT ( nextEvent() ) );
+
+ slotPrefsChanged();
+}
+
+KopeteViewManager::~KopeteViewManager()
+{
+// kdDebug(14000) << k_funcinfo << endl;
+
+ //delete all open chatwindow.
+ ManagerMap::Iterator it;
+ for ( it = d->managerMap.begin(); it != d->managerMap.end(); ++it )
+ it.data()->closeView( true ); //this does not clean the map, but we don't care
+
+ delete d;
+}
+
+void KopeteViewManager::slotPrefsChanged()
+{
+ d->useQueueOrStack = KopetePrefs::prefs()->useQueue() || KopetePrefs::prefs()->useStack();
+ d->raiseWindow = KopetePrefs::prefs()->raiseMsgWindow();
+ d->queueUnreadMessages = KopetePrefs::prefs()->queueUnreadMessages();
+ d->queueOnlyHighlightedMessagesInGroupChats = KopetePrefs::prefs()->queueOnlyHighlightedMessagesInGroupChats();
+ d->queueOnlyMessagesOnAnotherDesktop = KopetePrefs::prefs()->queueOnlyMessagesOnAnotherDesktop();
+ d->balloonNotifyIgnoreClosesChatView = KopetePrefs::prefs()->balloonNotifyIgnoreClosesChatView();
+}
+
+KopeteView *KopeteViewManager::view( Kopete::ChatSession* session, const QString &requestedPlugin )
+{
+// kdDebug(14000) << k_funcinfo << endl;
+
+ if( d->managerMap.contains( session ) && d->managerMap[ session ] )
+ {
+ return d->managerMap[ session ];
+ }
+ else
+ {
+ Kopete::PluginManager *pluginManager = Kopete::PluginManager::self();
+ Kopete::ViewPlugin *viewPlugin = 0L;
+
+ QString pluginName = requestedPlugin.isEmpty() ? KopetePrefs::prefs()->interfacePreference() : requestedPlugin;
+ if( !pluginName.isEmpty() )
+ {
+ viewPlugin = (Kopete::ViewPlugin*)pluginManager->loadPlugin( pluginName );
+
+ if( !viewPlugin )
+ {
+ kdWarning(14000) << "Requested view plugin, " << pluginName <<
+ ", was not found. Falling back to chat window plugin" << endl;
+ }
+ }
+
+ if( !viewPlugin )
+ viewPlugin = (Kopete::ViewPlugin*)pluginManager->loadPlugin( QString::fromLatin1("kopete_chatwindow") );
+
+ if( viewPlugin )
+ {
+ KopeteView *newView = viewPlugin->createView(session);
+
+ d->foreignMessage = false;
+ d->managerMap.insert( session, newView );
+
+ connect( session, SIGNAL( closing(Kopete::ChatSession *) ),
+ this, SLOT(slotChatSessionDestroyed(Kopete::ChatSession*)) );
+
+ return newView;
+ }
+ else
+ {
+ kdError(14000) << "Could not create a view, no plugins available!" << endl;
+ return 0L;
+ }
+ }
+}
+
+
+void KopeteViewManager::messageAppended( Kopete::Message &msg, Kopete::ChatSession *manager)
+{
+// kdDebug(14000) << k_funcinfo << endl;
+
+ bool outgoingMessage = ( msg.direction() == Kopete::Message::Outbound );
+
+ if( !outgoingMessage || d->managerMap.contains( manager ) )
+ {
+ d->foreignMessage=!outgoingMessage; //let know for the view we are about to create
+ manager->view(true,msg.requestedPlugin())->appendMessage( msg );
+ d->foreignMessage=false; //the view is created, reset the flag
+
+ bool appendMessageEvent = d->useQueueOrStack;
+
+ QWidget *w;
+ if( d->queueUnreadMessages && ( w = dynamic_cast<QWidget*>(view( manager )) ) )
+ {
+ // append msg event to queue if chat window is active but not the chat view in it...
+ appendMessageEvent = appendMessageEvent && !(w->isActiveWindow() && manager->view() == d->activeView);
+ // ...and chat window is on another desktop
+ appendMessageEvent = appendMessageEvent && (!d->queueOnlyMessagesOnAnotherDesktop || !KWin::windowInfo( w->topLevelWidget()->winId(), NET::WMDesktop ).isOnCurrentDesktop());
+ }
+ else
+ {
+ // append if no chat window exists already
+ appendMessageEvent = appendMessageEvent && !view( manager )->isVisible();
+ }
+
+ // in group chats always append highlighted messages to queue
+ appendMessageEvent = appendMessageEvent && (!d->queueOnlyHighlightedMessagesInGroupChats || manager->members().count() == 1 || msg.importance() == Kopete::Message::Highlight);
+
+ if( appendMessageEvent )
+ {
+ if ( !outgoingMessage )
+ {
+ Kopete::MessageEvent *event=new Kopete::MessageEvent(msg,manager);
+ d->eventList.append( event );
+ connect(event, SIGNAL(done(Kopete::MessageEvent *)), this, SLOT(slotEventDeleted(Kopete::MessageEvent *)));
+ Kopete::ChatSessionManager::self()->postNewEvent(event);
+ }
+ }
+ else if( d->eventList.isEmpty() )
+ {
+ readMessages( manager, outgoingMessage );
+ }
+
+ if ( !outgoingMessage && ( !manager->account()->isAway() || KopetePrefs::prefs()->soundIfAway() )
+ && msg.direction() != Kopete::Message::Internal )
+ {
+ QWidget *w=dynamic_cast<QWidget*>(manager->view(false));
+ KConfig *config = KGlobal::config();
+ config->setGroup("General");
+ if( (!manager->view(false) || !w || manager->view() != d->activeView ||
+ config->readBoolEntry("EventIfActive", true) || !w->isActiveWindow())
+ && msg.from())
+ {
+ QString msgFrom = QString::null;
+ if( msg.from()->metaContact() )
+ msgFrom = msg.from()->metaContact()->displayName();
+ else
+ msgFrom = msg.from()->contactId();
+
+ QString msgText = msg.plainBody();
+ if( msgText.length() > 90 )
+ msgText = msgText.left(88) + QString::fromLatin1("...");
+
+ QString event;
+ QString body =i18n( "<qt>Incoming message from %1<br>\"%2\"</qt>" );
+
+ switch( msg.importance() )
+ {
+ case Kopete::Message::Low:
+ event = QString::fromLatin1( "kopete_contact_lowpriority" );
+ break;
+ case Kopete::Message::Highlight:
+ event = QString::fromLatin1( "kopete_contact_highlight" );
+ body = i18n( "<qt>A highlighted message arrived from %1<br>\"%2\"</qt>" );
+ break;
+ default:
+ event = QString::fromLatin1( "kopete_contact_incoming" );
+ }
+ KNotification *notify=KNotification::event(msg.from()->metaContact() , event, body.arg( QStyleSheet::escape(msgFrom), QStyleSheet::escape(msgText) ), 0, /*msg.from()->metaContact(),*/
+ w , i18n("View") );
+
+ connect(notify,SIGNAL(activated(unsigned int )), manager , SLOT(raiseView()) );
+ }
+ }
+ }
+}
+
+void KopeteViewManager::readMessages( Kopete::ChatSession *manager, bool outgoingMessage, bool activate )
+{
+// kdDebug( 14000 ) << k_funcinfo << endl;
+ d->foreignMessage=!outgoingMessage; //let know for the view we are about to create
+ KopeteView *thisView = manager->view( true );
+ d->foreignMessage=false; //the view is created, reset the flag
+ if( ( outgoingMessage && !thisView->isVisible() ) || d->raiseWindow || activate )
+ thisView->raise( activate );
+ else if( !thisView->isVisible() )
+ thisView->makeVisible();
+
+ QPtrListIterator<Kopete::MessageEvent> it( d->eventList );
+ Kopete::MessageEvent* event;
+ while ( ( event = it.current() ) != 0 )
+ {
+ ++it;
+ if ( event->message().manager() == manager )
+ {
+ event->apply();
+ d->eventList.remove( event );
+ }
+ }
+}
+
+void KopeteViewManager::slotEventDeleted( Kopete::MessageEvent *event )
+{
+// kdDebug(14000) << k_funcinfo << endl;
+ Kopete::ChatSession *kmm=event->message().manager();
+ if(!kmm)
+ return;
+
+ d->eventList.remove( event );
+
+ if ( event->state() == Kopete::MessageEvent::Applied )
+ {
+ readMessages( kmm, false, true );
+ }
+ else if ( event->state() == Kopete::MessageEvent::Ignored && d->balloonNotifyIgnoreClosesChatView )
+ {
+ bool bAnotherWithThisManager = false;
+ for( QPtrListIterator<Kopete::MessageEvent> it( d->eventList ); it; ++it )
+ {
+ Kopete::MessageEvent *event = it.current();
+ if ( event->message().manager() == kmm )
+ bAnotherWithThisManager = true;
+ }
+ if ( !bAnotherWithThisManager && kmm->view( false ) )
+ kmm->view()->closeView( true );
+ }
+}
+
+void KopeteViewManager::nextEvent()
+{
+// kdDebug( 14000 ) << k_funcinfo << endl;
+
+ if( d->eventList.isEmpty() )
+ return;
+
+ Kopete::MessageEvent* event = d->eventList.first();
+
+ if ( event )
+ event->apply();
+}
+
+void KopeteViewManager::slotViewActivated( KopeteView *view )
+{
+// kdDebug( 14000 ) << k_funcinfo << endl;
+ d->activeView = view;
+
+ QPtrListIterator<Kopete::MessageEvent> it ( d->eventList );
+ Kopete::MessageEvent* event;
+ while ( ( event = it.current() ) != 0 )
+ {
+ ++it;
+ if ( event->message().manager() == view->msgManager() )
+ event->deleteLater();
+ }
+
+}
+
+void KopeteViewManager::slotViewDestroyed( KopeteView *closingView )
+{
+// kdDebug( 14000 ) << k_funcinfo << endl;
+
+ if( d->managerMap.contains( closingView->msgManager() ) )
+ {
+ d->managerMap.remove( closingView->msgManager() );
+// closingView->msgManager()->setCanBeDeleted( true );
+ }
+
+ if( closingView == d->activeView )
+ d->activeView = 0L;
+}
+
+void KopeteViewManager::slotChatSessionDestroyed( Kopete::ChatSession *manager )
+{
+// kdDebug( 14000 ) << k_funcinfo << endl;
+
+ if( d->managerMap.contains( manager ) )
+ {
+ KopeteView *v=d->managerMap[ manager ];
+ v->closeView( true );
+ delete v; //closeView call deleteLater, but in this case this is not enough, because some signal are called that case crash
+ d->managerMap.remove( manager );
+ }
+}
+
+KopeteView* KopeteViewManager::activeView() const
+{
+ return d->activeView;
+}
+
+
+#include "kopeteviewmanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/private/kopeteviewmanager.h b/kopete/libkopete/private/kopeteviewmanager.h
new file mode 100644
index 00000000..b1706906
--- /dev/null
+++ b/kopete/libkopete/private/kopeteviewmanager.h
@@ -0,0 +1,103 @@
+/*
+ kopeteviewmanager.h - View Manager
+
+ Copyright (c) 2003 by Jason Keirstead
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEVIEWMANAGER_H
+#define KOPETEVIEWMANAGER_H
+
+#include "kopetemessage.h"
+#include "kopete_export.h"
+
+namespace Kopete
+{
+ class ChatSession;
+ class Protocol;
+ class Contact;
+ class MessageEvent;
+}
+
+class KopeteView;
+class QTextEdit;
+
+struct KopeteViewManagerPrivate;
+
+/**
+ * Relates an actual chat to the means used to view it.
+ */
+class KOPETE_EXPORT KopeteViewManager : public QObject
+{
+ Q_OBJECT
+ public:
+ /** This is a singleton class. Call this method to get a pointer to
+ * a KopeteViewManager.
+ */
+ static KopeteViewManager *viewManager();
+
+ KopeteViewManager();
+ ~KopeteViewManager();
+
+ /**
+ * Return a view for the supplied Kopete::ChatSession. If one already
+ * exists, it will be returned, otherwise, a new view is created.
+ * @param session The Kopete::ChatSession we are viewing.
+ * @param requestedPlugin Specifies the view plugin to use.
+ */
+ KopeteView *view( Kopete::ChatSession *session, const QString &requestedPlugin = QString::null );
+
+ /**
+ * Provide access to the list of KopeteChatWindow the class maintains.
+ */
+ KopeteView *activeView() const;
+
+ private:
+
+
+ KopeteViewManagerPrivate *d;
+ static KopeteViewManager *s_viewManager;
+
+ public slots:
+ /**
+ * Make a view visible and on top.
+ * @param manager The originating Kopete::ChatSession.
+ * @param outgoingMessage Whether the message is inbound or outbound.
+ * @param activate Indicate whether the view should be activated
+ * @todo Document @p activate
+ */
+ void readMessages( Kopete::ChatSession* manager, bool outgoingMessage, bool activate = false );
+
+ /**
+ * Called when a new message has been appended to the given
+ * Kopete::ChatSession. Procures a view for the message, and generates any notification events or displays messages, as appropriate.
+ * @param msg The new message
+ * @param manager The originating Kopete::ChatSession
+ */
+ void messageAppended( Kopete::Message &msg, Kopete::ChatSession *manager);
+
+ void nextEvent();
+
+ private slots:
+ void slotViewDestroyed( KopeteView *);
+ void slotChatSessionDestroyed( Kopete::ChatSession * );
+
+ /**
+ * An event has been deleted.
+ */
+ void slotEventDeleted( Kopete::MessageEvent * );
+
+ void slotPrefsChanged();
+ void slotViewActivated( KopeteView * );
+};
+
+#endif
diff --git a/kopete/libkopete/tests/Makefile.am b/kopete/libkopete/tests/Makefile.am
new file mode 100644
index 00000000..417ce2a8
--- /dev/null
+++ b/kopete/libkopete/tests/Makefile.am
@@ -0,0 +1,41 @@
+SUBDIRS = mock .
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_ASCII_CAST -DQT_NO_COMPAT \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private -I$(top_srcdir)/kopete/libkopete/private -I$(top_srcdir)/kopete/libkopete/tests/mock $(all_includes) -DSRCDIR=\"$(top_srcdir)/kopete/libkopete/tests\"
+METASOURCES = AUTO
+
+check_LTLIBRARIES = kunittest_kopetemessage_test.la kunittest_kopetepropertiestest.la kunittest_kopetecontactlist_test.la
+noinst_LTLIBRARIES = kunittest_kopeteemoticontest.la
+
+check_PROGRAMS = kopetewallettest_program kopetepasswordtest_program
+
+kunittest_kopetepropertiestest_la_SOURCES = kopetepropertiestest.cpp ../kopeteproperties.cpp
+kunittest_kopetepropertiestest_la_LIBADD = -lkunittest ../libkopete.la
+kunittest_kopetepropertiestest_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+kunittest_kopeteemoticontest_la_SOURCES = kopeteemoticontest.cpp
+kunittest_kopeteemoticontest_la_LIBADD = -lkunittest ../libkopete.la
+kunittest_kopeteemoticontest_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+kunittest_kopetemessage_test_la_SOURCES = kopetemessage_test.cpp
+kunittest_kopetemessage_test_la_LIBADD = -lkunittest mock/libkopete_mock.la
+kunittest_kopetemessage_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+kopetewallettest_program_SOURCES = kopetewallettest_program.cpp
+kopetewallettest_program_LDFLAGS = -no-undefined $(all_libraries) $(KDE_RPATH)
+kopetewallettest_program_LDADD = ../libkopete.la
+
+kopetepasswordtest_program_SOURCES = kopetepasswordtest_program.cpp
+kopetepasswordtest_program_LDFLAGS = -no-undefined $(all_libraries) $(KDE_RPATH)
+kopetepasswordtest_program_LDADD = ../libkopete.la
+
+kunittest_kopetecontactlist_test_la_SOURCES = kopetecontactlist_test.cpp
+kunittest_kopetecontactlist_test_la_LIBADD = -lkunittest mock/libkopete_mock.la
+kunittest_kopetecontactlist_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
+
+noinst_HEADERS = kopetepropertiestest.h kopeteemoticontest.h
+
+check-local:
+ kunittestmodrunner
+guicheck:
+ kunittestmod $(PWD)
+
diff --git a/kopete/libkopete/tests/README b/kopete/libkopete/tests/README
new file mode 100644
index 00000000..ead9fbdc
--- /dev/null
+++ b/kopete/libkopete/tests/README
@@ -0,0 +1,47 @@
+LibKopete Unit Tests
+====================
+
+KopeteSuite:
+--------------
+Emoticon Test
+Link Test
+Property Test
+
+Test Programs:
+--------------
+Password Test Program
+Wallet Test Program
+
+
+HOWTO Run
+=========
+
+You can use the console or the GUI version:
+
+ $ make guicheck
+ $ make check
+
+The 'silent' switch in make is useful to reduce output:
+
+ $ make check -s
+
+
+Tricks
+======
+
+Accessing private data?, you should not. We will kill you.
+If it is really required, do something like:
+
+ #define private public
+ #include "kopetemessage.h"
+ #undef private
+
+Add a new test quickly:
+
+ $ ./create_test.rb Kopete::ContactList
+ Creating test for class Kopete::ContactList
+ kopetecontactlist_test.h and kopetecontactlist_test.cpp writen.
+ Please add the following to Makefile.am:
+ kunittest_kopetecontactlist_test_la_SOURCES = kopetecontactlist_test.cpp
+ kunittest_kopetecontactlist_test_la_LIBADD = -lkunittest ../mock/libkopete_mock.la
+ kunittest_kopetecontactlist_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)
diff --git a/kopete/libkopete/tests/create_test.rb b/kopete/libkopete/tests/create_test.rb
new file mode 100755
index 00000000..7951bf35
--- /dev/null
+++ b/kopete/libkopete/tests/create_test.rb
@@ -0,0 +1,56 @@
+#!/usr/bin/ruby
+#
+# Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+#
+# Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+#
+# *************************************************************************
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU General Public License as published by *
+# * the Free Software Foundation; either version 2 of the License, or *
+# * (at your option) any later version. *
+# * *
+# *************************************************************************
+
+className = ARGV[0]
+
+if className.nil?
+ puts "Need a class name"
+ exit
+end
+
+puts "Creating test for class #{className}"
+
+hBase = "template_test.h"
+cppBase = "template_test.cpp"
+
+fileH = File.new(hBase).read
+fileCpp = File.new(cppBase).read
+
+fileH.gsub!(/TEMPLATE/, className.upcase.gsub(/::/,""))
+fileH.gsub!(/Template/, className.gsub(/::/,""))
+fileH.gsub!(/some requirement/, className + " class.")
+
+fileCpp.gsub!(/TEMPLATE/, className.upcase.gsub(/::/,""))
+fileCpp.gsub!(/template/, className.downcase.gsub(/::/,""))
+fileCpp.gsub!(/Template/, className.gsub(/::/,""))
+fileCpp.gsub!(/some requirement/, className + " class.")
+
+makefileAm = "kunittest_template_test_la_SOURCES = template_test.cpp\nkunittest_template_test_la_LIBADD = -lkunittest ../mock/libkopete_mock.la\nkunittest_template_test_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries)\n"
+makefileAm.gsub!(/template/, className.downcase.gsub(/::/,""))
+
+hNew = hBase.gsub(/template/, className.downcase.gsub(/::/,""))
+cppNew = cppBase.gsub(/template/, className.downcase.gsub(/::/,""))
+
+hOut = File.new(hNew, "w")
+cppOut = File.new(cppNew, "w")
+
+hOut.write(fileH)
+cppOut.write(fileCpp)
+
+puts "#{hNew} and #{cppNew} writen."
+
+puts "Please add the following to Makefile.am:"
+puts makefileAm
+
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.input
new file mode 100644
index 00000000..795d3c7b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.input
@@ -0,0 +1 @@
+:)) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.output
new file mode 100644
index 00000000..795d3c7b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-1.output
@@ -0,0 +1 @@
+:)) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.input
new file mode 100644
index 00000000..6ddd0c7f
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.input
@@ -0,0 +1 @@
+:Ptesting:P \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.output
new file mode 100644
index 00000000..6ddd0c7f
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-10.output
@@ -0,0 +1 @@
+:Ptesting:P \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.input
new file mode 100644
index 00000000..2571b163
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.input
@@ -0,0 +1 @@
+In a sentence:practical example \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.output
new file mode 100644
index 00000000..2571b163
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-2.output
@@ -0,0 +1 @@
+In a sentence:practical example \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.input
new file mode 100644
index 00000000..2319ced9
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.input
@@ -0,0 +1 @@
+Bla (&nbsp;) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.output
new file mode 100644
index 00000000..2319ced9
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-3.output
@@ -0,0 +1 @@
+Bla (&nbsp;) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.input
new file mode 100644
index 00000000..f5d88878
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.input
@@ -0,0 +1 @@
+:D and :-D are not the same as :d and :-d \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.output
new file mode 100644
index 00000000..0d94eb97
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-4.output
@@ -0,0 +1 @@
+<img align="center" width="20" height="20" src="teeth.png" title=":D"/> and <img align="center" width="20" height="20" src="teeth.png" title=":-D"/> are not the same as :d and :-d \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.input
new file mode 100644
index 00000000..5b39691b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.input
@@ -0,0 +1 @@
+4d:D>:)F:/&gt;:-(:Pu:d9 \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.output
new file mode 100644
index 00000000..5b39691b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-5.output
@@ -0,0 +1 @@
+4d:D>:)F:/&gt;:-(:Pu:d9 \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.input
new file mode 100644
index 00000000..379e01a1
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.input
@@ -0,0 +1 @@
+&lt;::pvar:: test=1&gt; \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.output
new file mode 100644
index 00000000..379e01a1
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-6.output
@@ -0,0 +1 @@
+&lt;::pvar:: test=1&gt; \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.input
new file mode 100644
index 00000000..d6e7e6c0
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.input
@@ -0,0 +1 @@
+a non-breaking space (&nbsp;) character \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.output
new file mode 100644
index 00000000..d6e7e6c0
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-7.output
@@ -0,0 +1 @@
+a non-breaking space (&nbsp;) character \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.input
new file mode 100644
index 00000000..a3734027
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.input
@@ -0,0 +1 @@
+-+-[-:-(-:-)-:-]-+- \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.output
new file mode 100644
index 00000000..a3734027
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-8.output
@@ -0,0 +1 @@
+-+-[-:-(-:-)-:-]-+- \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.input b/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.input
new file mode 100644
index 00000000..538c5b0b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.input
@@ -0,0 +1 @@
+::shrugs:: \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.output b/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.output
new file mode 100644
index 00000000..538c5b0b
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/broken-9.output
@@ -0,0 +1 @@
+::shrugs:: \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-1.input b/kopete/libkopete/tests/emoticon-parser-testcases/working-1.input
new file mode 100644
index 00000000..a5440d64
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-1.input
@@ -0,0 +1 @@
+:):) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-1.output b/kopete/libkopete/tests/emoticon-parser-testcases/working-1.output
new file mode 100644
index 00000000..a7c018d4
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-1.output
@@ -0,0 +1 @@
+<img align="center" width="20" height="20" src="smile.png" title=":)"/><img align="center" width="20" height="20" src="smile.png" title=":)"/> \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-2.input b/kopete/libkopete/tests/emoticon-parser-testcases/working-2.input
new file mode 100644
index 00000000..223ce5be
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-2.input
@@ -0,0 +1 @@
+<img src="..." title=":-)" /> \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-2.output b/kopete/libkopete/tests/emoticon-parser-testcases/working-2.output
new file mode 100644
index 00000000..223ce5be
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-2.output
@@ -0,0 +1 @@
+<img src="..." title=":-)" /> \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-3.input b/kopete/libkopete/tests/emoticon-parser-testcases/working-3.input
new file mode 100644
index 00000000..d685c091
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-3.input
@@ -0,0 +1 @@
+End of sentence:p \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-3.output b/kopete/libkopete/tests/emoticon-parser-testcases/working-3.output
new file mode 100644
index 00000000..013515be
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-3.output
@@ -0,0 +1 @@
+End of sentence<img align="center" width="20" height="20" src="tongue.png" title=":p"/> \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-4.input b/kopete/libkopete/tests/emoticon-parser-testcases/working-4.input
new file mode 100644
index 00000000..093690c4
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-4.input
@@ -0,0 +1 @@
+http://www.kde.org \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-4.output b/kopete/libkopete/tests/emoticon-parser-testcases/working-4.output
new file mode 100644
index 00000000..093690c4
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-4.output
@@ -0,0 +1 @@
+http://www.kde.org \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-5.input b/kopete/libkopete/tests/emoticon-parser-testcases/working-5.input
new file mode 100644
index 00000000..1e3caf28
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-5.input
@@ -0,0 +1 @@
+&gt;:-) \ No newline at end of file
diff --git a/kopete/libkopete/tests/emoticon-parser-testcases/working-5.output b/kopete/libkopete/tests/emoticon-parser-testcases/working-5.output
new file mode 100644
index 00000000..3b1d4c31
--- /dev/null
+++ b/kopete/libkopete/tests/emoticon-parser-testcases/working-5.output
@@ -0,0 +1 @@
+<img align="center" width="20" height="20" src="devil.png" title="&gt;:-)"/> \ No newline at end of file
diff --git a/kopete/libkopete/tests/kopetecontactlist_test.cpp b/kopete/libkopete/tests/kopetecontactlist_test.cpp
new file mode 100644
index 00000000..001f3f0d
--- /dev/null
+++ b/kopete/libkopete/tests/kopetecontactlist_test.cpp
@@ -0,0 +1,55 @@
+/*
+ Tests for Kopete::ContactList class.
+
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qfile.h>
+#include <qdir.h>
+#include <kstandarddirs.h>
+#include <kunittest/module.h>
+#include "kopetecontactlist_test.h"
+
+using namespace KUnitTest;
+
+KUNITTEST_MODULE( kunittest_kopetecontactlist_test, "KopeteSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( KopeteContactList_Test );
+
+void KopeteContactList_Test::allTests()
+{
+ testSomething();
+}
+
+void KopeteContactList_Test::testSomething()
+{
+ // change user data dir to avoid messing with user's .kde dir
+ setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kopete-unittest" ), true );
+
+ QString filename = locateLocal( "appdata", QString::fromLatin1( "contactlist.xml" ) );
+ if( ! filename.isEmpty() )
+ {
+ // previous test run, delete the previous contact list
+ bool removed = QFile::remove(filename);
+ // if we cant remove the file, abort test
+ if (!removed)
+ return;
+ }
+
+ int result = 1;
+ int expected = 1;
+ // result should be the expected one
+ CHECK(result, expected);
+}
+
+
diff --git a/kopete/libkopete/tests/kopetecontactlist_test.h b/kopete/libkopete/tests/kopetecontactlist_test.h
new file mode 100644
index 00000000..faab1e48
--- /dev/null
+++ b/kopete/libkopete/tests/kopetecontactlist_test.h
@@ -0,0 +1,35 @@
+/*
+ Tests for Kopete::ContactList class.
+
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETECONTACTLIST_TEST_H
+#define KOPETECONTACTLIST_TEST_H
+
+#include <kunittest/tester.h>
+
+// change to SlotTester when it works
+class KopeteContactList_Test : public KUnitTest::Tester
+{
+public:
+ void allTests();
+public slots:
+ void testSomething();
+private:
+
+};
+
+#endif
+
diff --git a/kopete/libkopete/tests/kopeteemoticontest.cpp b/kopete/libkopete/tests/kopeteemoticontest.cpp
new file mode 100644
index 00000000..e9a81c1d
--- /dev/null
+++ b/kopete/libkopete/tests/kopeteemoticontest.cpp
@@ -0,0 +1,132 @@
+/*
+ Tests for Kopete::Message::parseEmoticons
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <stdlib.h>
+
+#include <qstring.h>
+#include <qdir.h>
+#include <qfile.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <kunittest/module.h>
+#include "kopeteemoticontest.h"
+#include "kopetemessage.h"
+#include "kopeteemoticons.h"
+
+using namespace KUnitTest;
+
+KUNITTEST_MODULE( kunittest_kopeteemoticontest, "KopeteSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( KopeteEmoticonTest );
+
+/*
+ There are three sets of tests, the Kopete 0.7 baseline with tests that were
+ working properly in Kopete 0.7.x. When these fail it's a real regression.
+
+ The second set are those known to work in the current codebase.
+ The last set is the set with tests that are known to fail right now.
+
+ the name convention is working|broken-number.input|output
+*/
+
+
+void KopeteEmoticonTest::allTests()
+{
+ // change user data dir to avoid messing with user's .kde dir
+ setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kopete-unittest" ), true );
+
+ //KApplication::disableAutoDcopRegistration();
+ //KApplication app;
+
+ testEmoticonParser();
+}
+
+void KopeteEmoticonTest::testEmoticonParser()
+{
+ Kopete::Emoticons emo("Default");
+ QString basePath = QString::fromLatin1( SRCDIR ) + QString::fromLatin1("/emoticon-parser-testcases");
+ QDir testCasesDir(basePath);
+
+ QStringList inputFileNames = testCasesDir.entryList("*.input");
+ for ( QStringList::ConstIterator it = inputFileNames.begin(); it != inputFileNames.end(); ++it)
+ {
+ QString fileName = *it;
+ kdDebug() << "testcase: " << fileName << endl;
+ QString outputFileName = fileName;
+ outputFileName.replace("input","output");
+ // open the input file
+ QFile inputFile(basePath + QString::fromLatin1("/") + fileName);
+ QFile expectedFile(basePath + QString::fromLatin1("/") + outputFileName);
+ // check if the expected output file exists
+ // if it doesn't, skip the testcase
+ if ( ! expectedFile.exists() )
+ {
+ SKIP("Warning! expected output for testcase "+ *it + " not found. Skiping testcase");
+ continue;
+ }
+ if ( inputFile.open( IO_ReadOnly ) && expectedFile.open( IO_ReadOnly ))
+ {
+ QTextStream inputStream(&inputFile);
+ QTextStream expectedStream(&expectedFile);
+ QString inputData;
+ QString expectedData;
+ inputData = inputStream.read();
+ expectedData = expectedStream.read();
+
+ inputFile.close();
+ expectedFile.close();
+
+ QString path = KGlobal::dirs()->findResource( "emoticons", "Default/smile.png" ).replace( "smile.png", QString::null );
+
+ Kopete::Emoticons::self();
+ QString result = emo.parse( inputData ).replace( path, QString::null );
+
+ // HACK to know the test case we applied, concatenate testcase name to both
+ // input and expected string. WIll remove when I can add some sort of metadata
+ // to a CHECK so debug its origin testcase
+ result = fileName + QString::fromLatin1(": ") + result;
+ expectedData = fileName + QString::fromLatin1(": ") + expectedData;
+ // if the test case begins with broken, we expect it to fail, then use XFAIL
+ // otherwise use CHECK
+ if ( fileName.section("-", 0, 0) == QString::fromLatin1("broken") )
+ {
+ kdDebug() << "checking known-broken testcase: " << fileName << endl;
+ XFAIL(result, expectedData);
+ }
+ else
+ {
+ kdDebug() << "checking known-working testcase: " << fileName << endl;
+ CHECK(result, expectedData);
+ }
+ }
+ else
+ {
+ SKIP("Warning! can't open testcase files for "+ *it + ". Skiping testcase");
+ continue;
+ }
+ }
+
+}
+
+
+
+
+ \ No newline at end of file
diff --git a/kopete/libkopete/tests/kopeteemoticontest.h b/kopete/libkopete/tests/kopeteemoticontest.h
new file mode 100644
index 00000000..a885b2c4
--- /dev/null
+++ b/kopete/libkopete/tests/kopeteemoticontest.h
@@ -0,0 +1,39 @@
+/*
+ Tests for Kopete::Message::parseEmoticons
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_EMOTICON_TEST_H
+#define KOPETE_EMOTICON_TEST_H
+
+#include <kunittest/tester.h>
+
+// change to SlotTester when it works
+class KopeteEmoticonTest : public KUnitTest::Tester
+{
+public:
+ //KopeteLinkTest();
+ //~KopeteLinkTest();
+ void allTests();
+public slots:
+ void testEmoticonParser();
+private:
+
+};
+
+#endif
+
+
diff --git a/kopete/libkopete/tests/kopetemessage.xsd b/kopete/libkopete/tests/kopetemessage.xsd
new file mode 100644
index 00000000..69f99d20
--- /dev/null
+++ b/kopete/libkopete/tests/kopetemessage.xsd
@@ -0,0 +1,180 @@
+<?xml version="1.0"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <xsd:annotation>
+ <xsd:documentation xml:lang="en">
+ <![CDATA[
+ This is the XSD schema of a Kopete message in XML form. This is both the
+ format that the XSL stylesheets will expect, as well as the format that
+ results from saving the chatwindow contents. This is *not* the same as
+ the format of the history plugin.
+
+ The XML format has one other little quirk - you can pass flags into the
+ engine as XML processing instructions. For example, if you add this
+ instruction to your document:
+
+ <?Kopete Flag:TransformAllMessages>
+
+ ... it will instruct the Kopete XSL engine that you want the entire contents
+ of the chat window to be re-drawn each time a new message is appended. This
+ is not the normal procedure, and is only required for special situations
+ (see the Adium style for an example).
+
+ TransformAllMessages is the only flag currently defined.
+ ]]>
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <!-- This is defined if we save a chat with multiple messages -->
+ <xsd:element name="document">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element ref="message" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- The main message element -->
+ <xsd:element name="message">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="from" type="metaContact" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="to" type="metaContact" minOccurs="0" maxOccurs="1"/>
+ <xsd:element name="body" type="messageBody" minOccurs="1" maxOccurs="1"/>
+ </xsd:sequence>
+
+ <!-- The time only. eg 12:00 pm -->
+ <xsd:attribute name="time" type="xsd:string" use="required"/>
+
+ <!-- Full timestamp. eg Tue Feb 8 19:04:49 AST 2005 -->
+ <xsd:attribute name="timestamp" type="xsd:string" use="required"/>
+
+ <!-- Formatted timestamp. eg 12:00:57 pm -->
+ <xsd:attribute name="formattedTimestamp" type="xsd:string" use="required"/>
+
+ <!-- Message subject. Used by Jabber Email. -->
+ <xsd:attribute name="subject" type="xsd:string" use="required"/>
+
+ <!-- Message direction (Inbound, Outbound, Internal).
+ This is deprecated. Use @type and @route -->
+ <xsd:attribute name="direction" type="direction" use="required"/>
+
+ <!-- Message route (inbound, outbound, internal).-->
+ <xsd:attribute name="route" type="route" use="required"/>
+
+ <!-- Message type (normal, action).-->
+ <xsd:attribute name="type" type="type" use="required"/>
+
+ <!-- Message importance.-->
+ <xsd:attribute name="importance" type="importance" use="required"/>
+
+ <!-- This is the main contact Id - the other person in the
+ converation besides you. If it is a group chat, it is the first
+ person who was being spoken to, or the group chat name. -->
+ <xsd:attribute name="mainContactId" type="xsd:string" use="optional"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <!-- Enumeration for message direction
+ (this is deprecated - use the route/type) -->
+ <xsd:simpleType name="direction">
+ <xsd:restriction base="xsd:integer">
+ <xsd:enumeration value="0"/> <!-- Inbound -->
+ <xsd:enumeration value="1"/> <!-- Outbound -->
+ <xsd:enumeration value="2"/> <!-- Internal -->
+ <xsd:enumeration value="3"/> <!-- Action -->
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Enumeration for message route -->
+ <xsd:simpleType name="route">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="inbound"/>
+ <xsd:enumeration value="outbound"/>
+ <xsd:enumeration value="internal"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Enumeration for message type -->
+ <xsd:simpleType name="type">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="normal"/>
+ <xsd:enumeration value="action"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Enumeration for message importance -->
+ <xsd:simpleType name="importance">
+ <xsd:restriction base="xsd:integer">
+ <xsd:enumeration value="0"/> <!-- Low -->
+ <xsd:enumeration value="1"/> <!-- Normal -->
+ <xsd:enumeration value="2"/> <!-- Highlight -->
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Enumeration for bidi direction -->
+ <xsd:simpleType name="bidiDirection">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="ltr"/>
+ <xsd:enumeration value="rtl"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- Element for display names -->
+ <xsd:complexType name="displayName">
+ <!-- The direction of the name, for Bidi suport. -->
+ <xsd:attribute name="dir" type="bidiDirection"/>
+
+ <!-- The actual name text -->
+ <xsd:attribute name="text" type="xsd:string"/>
+ </xsd:complexType>
+
+ <!-- The contact element -->
+ <xsd:complexType name="metaContact">
+ <xsd:sequence>
+ <xsd:element name="contact">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="contactDisplayName" type="displayName" minOccurs="1" maxOccurs="1"/>
+ <xsd:element name="metaContactDisplayName" type="displayName" minOccurs="1" maxOccurs="1"/>
+ </xsd:sequence>
+
+ <!-- The contact's id -->
+ <xsd:attribute name="contactId" type="xsd:string" use="required"/>
+
+ <!-- The contact's custom color -->
+ <xsd:attribute name="color" type="xsd:string" use="required"/>
+
+ <!-- The contact's photo. This file name only remains valid
+ while the message is in transit -->
+ <xsd:attribute name="userPhoto" type="xsd:string" use="optional"/>
+
+ <!-- The contact's protocol icon -->
+ <xsd:attribute name="protocolIcon" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <!-- The message body element -->
+ <xsd:complexType name="messageBody">
+ <xsd:simpleContent>
+ <xsd:extension base="xsd:string">
+ <!-- The foreground color of the message -->
+ <xsd:attribute name="color" type="xsd:string" use="optional"/>
+
+ <!-- The background color of the message -->
+ <xsd:attribute name="bgcolor" type="xsd:string" use="optional"/>
+
+ <!-- The font of the message. This is a CSS string
+ describing the font-family, font-size, text-decoration,
+ and font-weight -->
+ <xsd:attribute name="font" type="xsd:string" use="optional"/>
+
+ <!-- The direction of the message, for Bidi suport. -->
+ <xsd:attribute name="dir" type="bidiDirection" use="required"/>
+ </xsd:extension>
+ </xsd:simpleContent>
+ </xsd:complexType>
+
+</xsd:schema> \ No newline at end of file
diff --git a/kopete/libkopete/tests/kopetemessage_test.cpp b/kopete/libkopete/tests/kopetemessage_test.cpp
new file mode 100644
index 00000000..1ca57123
--- /dev/null
+++ b/kopete/libkopete/tests/kopetemessage_test.cpp
@@ -0,0 +1,324 @@
+/*
+ Tests for Kopete::Message
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <stdlib.h>
+
+#include <qdir.h>
+#include <qfile.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kinstance.h>
+#include <kprocess.h>
+#include <kunittest/module.h>
+#include <kdebug.h>
+
+#include "kopetemessage_test.h"
+#include "kopeteaccount_mock.h"
+#include "kopeteprotocol_mock.h"
+#include "kopetecontact_mock.h"
+#include "kopetemetacontact_mock.h"
+#include "kopeteaccount_mock.h"
+
+using namespace KUnitTest;
+
+KUNITTEST_MODULE( kunittest_kopetemessage_test, "KopeteSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( KopeteMessage_Test );
+
+/*
+ There are four sets of tests: for each of plain text and html, we have those
+ known to work in the current codebase, and those known to fail right now.
+
+ the name convention is working|broken-plaintext|html-number.input|output
+*/
+
+KopeteMessage_Test::KopeteMessage_Test()
+{
+ // change user data dir to avoid messing with user's .kde dir
+ setenv( "KDEHOME", QFile::encodeName( QDir::homeDirPath() + "/.kopete-unittest" ), true );
+
+ // create fake objects needed to build a reasonable testeable message
+ m_protocol = new Kopete::Test::Mock::Protocol( new KInstance(QCString("test-kopete-message")), 0L, "test-kopete-message");
+ m_account = new Kopete::Test::Mock::Account(m_protocol, "testaccount");
+ m_metaContactMyself = new Kopete::Test::Mock::MetaContact();
+ m_metaContactOther = new Kopete::Test::Mock::MetaContact();
+ m_contactFrom = new Kopete::Test::Mock::Contact(m_account, QString::fromLatin1("test-myself"), m_metaContactMyself, QString::null);
+ m_contactTo = new Kopete::Test::Mock::Contact(m_account, QString::fromLatin1("test-dest"), m_metaContactOther, QString::null);
+ m_message = new Kopete::Message( m_contactFrom, m_contactTo, QString::null, Kopete::Message::Outbound, Kopete::Message::PlainText);
+}
+
+void KopeteMessage_Test::allTests()
+{
+ KApplication::disableAutoDcopRegistration();
+ //KCmdLineArgs::init(argc,argv,"testkopetemessage", 0, 0, 0, 0);
+
+ // At least Kopete::Message::asXML() seems to require that a QApplication
+ // is created. Running the console version doesn't create it, but the GUI
+ // version does.
+
+ if (!kapp)
+ new KApplication();
+
+ testPrimitives();
+ testLinkParser();
+}
+
+void KopeteMessage_Test::testPrimitives()
+{
+ /**********************************************
+ * from(), to()
+ *********************************************/
+
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Inbound, Kopete::Message::PlainText);
+ Q_ASSERT(msg.from());
+ Q_ASSERT(!msg.to().isEmpty());
+ }
+
+ /**********************************************
+ * Direction
+ *********************************************/
+
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Inbound, Kopete::Message::PlainText);
+ CHECK(Kopete::Message::Inbound, msg.direction());
+ }
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Outbound, Kopete::Message::RichText);
+ CHECK(Kopete::Message::Outbound, msg.direction());
+ }
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Internal, Kopete::Message::RichText);
+ CHECK(Kopete::Message::Internal, msg.direction());
+ }
+
+ /**********************************************
+ * Message Format
+ *********************************************/
+
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Inbound, Kopete::Message::PlainText);
+ CHECK(Kopete::Message::PlainText, msg.format());
+ }
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foobar", Kopete::Message::Inbound, Kopete::Message::RichText);
+ CHECK(Kopete::Message::RichText, msg.format());
+ }
+ {
+ QString m = "foobar";
+ Kopete::Message msg( m_contactFrom, m_contactTo, m, Kopete::Message::Inbound, Kopete::Message::RichText);
+
+ msg.setBody(m, Kopete::Message::PlainText);
+ CHECK(Kopete::Message::PlainText, msg.format());
+
+ msg.setBody(m, Kopete::Message::RichText);
+ CHECK(Kopete::Message::RichText, msg.format());
+
+ msg.setBody(m, Kopete::Message::ParsedHTML);
+ CHECK(Kopete::Message::ParsedHTML, msg.format());
+
+ msg.setBody(m, Kopete::Message::Crypted);
+ CHECK(Kopete::Message::Crypted, msg.format());
+ }
+
+
+ /**********************************************
+ * setBody()
+ *********************************************/
+
+ {
+ QString m = "foobar";
+ Kopete::Message msg( m_contactFrom, m_contactTo, m, Kopete::Message::Inbound, Kopete::Message::RichText);
+
+ msg.setBody("NEW", Kopete::Message::PlainText);
+ CHECK(QString("NEW"), msg.plainBody());
+
+ msg.setBody("NEW_NEW", Kopete::Message::RichText);
+ CHECK(QString("NEW_NEW"), msg.plainBody());
+ }
+ {
+ QString m = "foobar";
+ Kopete::Message msg( m_contactFrom, m_contactTo, m, Kopete::Message::Inbound, Kopete::Message::PlainText);
+
+ msg.setBody("NEW", Kopete::Message::PlainText);
+ CHECK(QString("NEW"), msg.plainBody());
+
+ msg.setBody("NEW_NEW", Kopete::Message::RichText);
+ CHECK(QString("NEW_NEW"), msg.plainBody());
+ }
+ {
+ QString m = "<html><head></head><body foo=\"bar\"> <b>HELLO WORLD</b> </body></html>";
+ Kopete::Message msg( m_contactFrom, m_contactTo, m, Kopete::Message::Inbound, Kopete::Message::PlainText);
+ CHECK(m, msg.plainBody());
+
+ msg.setBody("<simple> SIMPLE", Kopete::Message::PlainText);
+ CHECK(msg.plainBody(), QString("<simple> SIMPLE") );
+ CHECK(msg.escapedBody(), QString("&lt;simple&gt; SIMPLE") );
+
+ msg.setBody("<simple>SIMPLE</simple>", Kopete::Message::RichText);
+ CHECK(msg.plainBody(), QString("SIMPLE") );
+ CHECK(msg.escapedBody(), QString("<simple>SIMPLE</simple>") );
+
+ CHECK(Kopete::Message::unescape( QString( "<simple>SIMPLE</simple>" ) ), QString("SIMPLE") );
+ CHECK(Kopete::Message::unescape( QString( "Foo <img src=\"foo.png\" />" ) ), QString("Foo ") );
+ CHECK(Kopete::Message::unescape( QString( "Foo <img src=\"foo.png\" title=\"Bar\" />" ) ), QString("Foo Bar") );
+
+ msg.setBody(m, Kopete::Message::RichText);
+
+ // FIXME: Should setBody() also strip extra white space?
+ //CHECK(msg.plainBody(), QString("HELLO WORLD"));
+ //CHECK(msg.escapedBody(), QString("<b>HELLO WORLD</b>"));
+
+ CHECK(msg.escapedBody(), QString(" &nbsp; <b>HELLO WORLD</b> &nbsp; "));
+ CHECK(msg.plainBody(), QString(" HELLO WORLD "));
+ CHECK(msg.plainBody().stripWhiteSpace(), QString("HELLO WORLD"));
+ CHECK(msg.escapedBody().stripWhiteSpace(), QString("&nbsp; <b>HELLO WORLD</b> &nbsp;"));
+ }
+ {
+ Kopete::Message msg( m_contactFrom, m_contactTo, "foo", Kopete::Message::Inbound, Kopete::Message::PlainText);
+
+ msg.setBody("<p>foo", Kopete::Message::RichText);
+ CHECK(msg.escapedBody(), QString("foo"));
+
+ msg.setBody("<p>foo</p>", Kopete::Message::RichText);
+ CHECK(msg.escapedBody(), QString("foo"));
+
+ msg.setBody("\n<p>foo</p>\n<br/>", Kopete::Message::RichText);
+ CHECK(msg.escapedBody(), QString("foo<br/>"));
+ }
+
+ /**********************************************
+ * Copy constructor
+ *********************************************/
+
+ {
+ Kopete::Message msg1(m_contactFrom, m_contactTo, "foo", Kopete::Message::Inbound, Kopete::Message::RichText);
+ Kopete::Message msg2(msg1);
+
+ CHECK(msg1.plainBody(), msg2.plainBody());
+ CHECK(msg1.escapedBody(), msg2.escapedBody());
+
+ msg1.setBody("NEW", Kopete::Message::PlainText);
+ CHECK(msg1.plainBody(), QString("NEW"));
+ CHECK(msg2.plainBody(), QString("foo"));
+ }
+
+ /**********************************************
+ * operator=
+ *********************************************/
+
+ {
+ Kopete::Message msg1(m_contactFrom, m_contactTo, "foo", Kopete::Message::Inbound, Kopete::Message::RichText);
+ {
+ Kopete::Message msg2;
+
+ CHECK(msg2.plainBody(), QString::null);
+
+ msg2 = msg1;
+
+ CHECK(msg1.plainBody(), msg2.plainBody());
+ CHECK(msg1.escapedBody(), msg2.escapedBody());
+
+ msg1.setBody("NEW", Kopete::Message::PlainText);
+ CHECK(msg1.plainBody(), QString("NEW"));
+ CHECK(msg2.plainBody(), QString("foo"));
+ }
+ CHECK(msg1.plainBody(), QString("NEW"));
+
+ msg1 = msg1;
+ CHECK(msg1.plainBody(), QString("NEW"));
+ }
+}
+
+void KopeteMessage_Test::setup()
+{
+}
+
+void KopeteMessage_Test::testLinkParser()
+{
+ QString basePath = QString::fromLatin1( SRCDIR ) + QString::fromLatin1("/link-parser-testcases");
+ QDir testCasesDir(basePath);
+
+ QStringList inputFileNames = testCasesDir.entryList("*.input");
+ for ( QStringList::ConstIterator it = inputFileNames.begin(); it != inputFileNames.end(); ++it)
+ {
+ QString fileName = *it;
+ QString outputFileName = fileName;
+ outputFileName.replace("input","output");
+ // open the input file
+ QFile inputFile(basePath + QString::fromLatin1("/") + fileName);
+ QFile expectedFile(basePath + QString::fromLatin1("/") + outputFileName);
+ // check if the expected output file exists
+ // if it doesn't, skip the testcase
+ if ( ! expectedFile.exists() )
+ {
+ SKIP("Warning! expected output for testcase "+ *it + " not found. Skiping testcase");
+ continue;
+ }
+ if ( inputFile.open( IO_ReadOnly ) && expectedFile.open( IO_ReadOnly ))
+ {
+ QTextStream inputStream(&inputFile);
+ QTextStream expectedStream(&expectedFile);
+ QString inputData;
+ QString expectedData;
+ inputData = inputStream.read();
+ expectedData = expectedStream.read();
+
+ inputFile.close();
+ expectedFile.close();
+
+ // use a concrete url
+ inputData.replace( "$URL","http://www.kde.org" );
+ expectedData.replace( "$URL","http://www.kde.org" );
+
+ // set message format for parsing according to textcase filename convention
+ Kopete::Message::MessageFormat format;
+ if ( fileName.section("-", 1, 1) == QString::fromLatin1("plaintext") )
+ format = Kopete::Message::PlainText;
+ else
+ format = Kopete::Message::RichText;
+
+ QString result = Kopete::Message::parseLinks( inputData, format );
+
+ // HACK to know the test case we applied, concatenate testcase name to both
+ // input and expected string. WIll remove when I can add some sort of metadata
+ // to a CHECK so debug its origin testcase
+ result = fileName + QString::fromLatin1(": ") + result;
+ expectedData = fileName + QString::fromLatin1(": ") + expectedData;
+ // if the test case begins with broken, we expect it to fail, then use XFAIL
+ // otherwise use CHECK
+ if ( fileName.section("-", 0, 0) == QString::fromLatin1("broken") )
+ {
+ //kdDebug() << "checking known-broken testcase: " << fileName << endl;
+ XFAIL(result, expectedData);
+ }
+ else
+ {
+ //kdDebug() << "checking known-working testcase: " << fileName << endl;
+ CHECK(result, expectedData);
+ }
+ }
+ else
+ {
+ SKIP("Warning! can't open testcase files for "+ *it + ". Skiping testcase");
+ continue;
+ }
+ }
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/tests/kopetemessage_test.h b/kopete/libkopete/tests/kopetemessage_test.h
new file mode 100644
index 00000000..52d09fb8
--- /dev/null
+++ b/kopete/libkopete/tests/kopetemessage_test.h
@@ -0,0 +1,56 @@
+/*
+ Tests for Kopete::Message
+
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEMESSAGE_TEST_H
+#define KOPETEMESSAGE_TEST_H
+
+#include <kunittest/tester.h>
+
+#define private public
+#include "kopetemessage.h"
+#undef private
+
+class Kopete::Protocol;
+class Kopete::Account;
+class Kopete::MetaContact;
+class Kopete::Contact;
+
+// change to SlotTester when it works
+class KopeteMessage_Test : public KUnitTest::Tester
+{
+public:
+ KopeteMessage_Test();
+ void allTests();
+
+public slots:
+ void testPrimitives();
+ void testLinkParser();
+
+private:
+ void setup();
+ Kopete::Message *m_message;
+ Kopete::Protocol *m_protocol;
+ Kopete::Account *m_account;
+ Kopete::MetaContact *m_metaContactMyself;
+ Kopete::MetaContact *m_metaContactOther;
+ Kopete::Contact *m_contactFrom;
+ Kopete::Contact *m_contactTo;
+};
+
+#endif
+
diff --git a/kopete/libkopete/tests/kopetepasswordtest_program.cpp b/kopete/libkopete/tests/kopetepasswordtest_program.cpp
new file mode 100644
index 00000000..a1f3a50e
--- /dev/null
+++ b/kopete/libkopete/tests/kopetepasswordtest_program.cpp
@@ -0,0 +1,132 @@
+/*
+ Tests for the Kopete::Password class
+
+ Copyright (c) 2003 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetepasswordtest_program.h"
+#include "kopetepassword.h"
+
+#include <qtextstream.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+static QTextStream _out( stdout, IO_WriteOnly );
+
+static KCmdLineOptions opts[] =
+{
+ { "id <id>", I18N_NOOP("Config group to store password in"), "TestAccount" },
+ { "set <new>", I18N_NOOP("Set password to new"), 0 },
+ { "error", I18N_NOOP("Claim password was erroneous"), 0 },
+ { "prompt <prompt>", I18N_NOOP("Password prompt"), "Enter a password" },
+ { "image <filename>", I18N_NOOP("Image to display in password dialog"), 0 },
+ KCmdLineLastOption
+};
+
+using namespace Kopete;
+
+QString retrieve( Password &pwd, const QPixmap &image, const QString &prompt )
+{
+ PasswordRetriever r;
+ pwd.request( &r, SLOT( gotPassword( const QString & ) ), image, prompt );
+ QTimer tmr;
+ r.connect( &tmr, SIGNAL( timeout() ), SLOT( timer() ) );
+ tmr.start( 1000 );
+ qApp->exec();
+ return r.password;
+}
+
+void PasswordRetriever::gotPassword( const QString &pass )
+{
+ password = pass;
+ qApp->quit();
+}
+
+void PasswordRetriever::timer()
+{
+ _out << "." << flush;
+}
+
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData( "kopetepasswordtest", "kopetepasswordtest", "version" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( opts );
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KApplication app( "kopetepasswordtest" );
+
+ bool setPassword = args->isSet("set");
+ QString newPwd = args->getOption("set");
+ QString passwordId = args->getOption("id");
+ bool error = args->isSet("error");
+ QString prompt = args->getOption("prompt");
+ QPixmap image = QString(args->getOption("image"));
+
+ _out << (image.isNull() ? "image is null" : "image is valid") << endl;
+
+ Password pwd( passwordId, 0, false );
+ pwd.setWrong( error );
+
+ _out << "Cached value is null: " << pwd.cachedValue().isNull() << endl;
+
+ QString pass = retrieve( pwd, image, prompt );
+
+ if ( !pass.isNull() )
+ _out << "Read password: " << pass << endl;
+ else
+ _out << "Could not read a password" << endl;
+
+ _out << "Cached value: " << (pwd.cachedValue().isNull() ? "null" : pwd.cachedValue()) << endl;
+
+ if ( setPassword )
+ {
+ if ( newPwd.isEmpty() )
+ {
+ _out << "Clearing password" << endl;
+ newPwd = QString::null;
+ }
+ else
+ {
+ _out << "Setting password to " << newPwd << endl;
+ }
+ pwd.set( newPwd );
+ }
+
+ // without this, setting passwords will fail since they're
+ // set asynchronously.
+ QTimer::singleShot( 0, &app, SLOT( deref() ) );
+ app.exec();
+
+ if ( setPassword )
+ {
+ pass = retrieve( pwd, image, i18n("Hopefully this popped up because you set the password to the empty string.") );
+ if( pass == newPwd )
+ _out << "Password successfully set." << endl;
+ else
+ _out << "Failed: password ended up as " << pass << endl;
+ }
+
+ return 0;
+}
+
+#include "kopetepasswordtest_program.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/tests/kopetepasswordtest_program.h b/kopete/libkopete/tests/kopetepasswordtest_program.h
new file mode 100644
index 00000000..507da2a1
--- /dev/null
+++ b/kopete/libkopete/tests/kopetepasswordtest_program.h
@@ -0,0 +1,16 @@
+#ifndef KOPETEPASSWORDTEST_H
+#define KOPETEPASSWORDTEST_H
+
+#include <qobject.h>
+
+class PasswordRetriever : public QObject
+{
+ Q_OBJECT
+public:
+ QString password;
+public slots:
+ void timer();
+ void gotPassword( const QString & );
+};
+
+#endif
diff --git a/kopete/libkopete/tests/kopetepropertiestest.cpp b/kopete/libkopete/tests/kopetepropertiestest.cpp
new file mode 100644
index 00000000..1e60c77c
--- /dev/null
+++ b/kopete/libkopete/tests/kopetepropertiestest.cpp
@@ -0,0 +1,59 @@
+/*
+ Tests for Kopete Properties
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kunittest/module.h>
+
+#include "kopeteproperties.h"
+
+#include <qstring.h>
+#include <qtextstream.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+#include "kopetepropertiestest.h"
+
+using namespace KUnitTest;
+
+KUNITTEST_MODULE( kunittest_kopetepropertiestest, "KopeteSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( KopetePropertiesTest );
+
+using namespace Kopete::Properties;
+
+static QTextStream _out( stdout, IO_WriteOnly );
+
+class PropertyHost : public WithProperties<PropertyHost> {};
+
+class FooProperty : public SimpleDataProperty<PropertyHost, QString>
+{
+public:
+ const char *name() const { return "foo"; }
+} fooProperty;
+
+void KopetePropertiesTest::allTests()
+{
+ PropertyHost myPropertyHost;
+ CHECK( myPropertyHost.property(fooProperty).isNull(), true);
+ myPropertyHost.setProperty( fooProperty, QString::fromLatin1("Foo!") );
+ CHECK( myPropertyHost.property(fooProperty), QString::fromLatin1("Foo!") );
+}
+
+
+ \ No newline at end of file
diff --git a/kopete/libkopete/tests/kopetepropertiestest.h b/kopete/libkopete/tests/kopetepropertiestest.h
new file mode 100644
index 00000000..c997dd80
--- /dev/null
+++ b/kopete/libkopete/tests/kopetepropertiestest.h
@@ -0,0 +1,36 @@
+/*
+ Tests for Kopete Properties
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_PROPERTIES_TEST_H
+#define KOPETE_PROPERTIES_TEST_H
+
+#include <kunittest/tester.h>
+
+// change to SlotTester when it works
+class KopetePropertiesTest : public KUnitTest::Tester
+{
+public:
+ void allTests();
+public slots:
+private:
+
+};
+
+#endif
+
+
diff --git a/kopete/libkopete/tests/kopetewallettest_program.cpp b/kopete/libkopete/tests/kopetewallettest_program.cpp
new file mode 100644
index 00000000..29de1edc
--- /dev/null
+++ b/kopete/libkopete/tests/kopetewallettest_program.cpp
@@ -0,0 +1,98 @@
+/*
+ Tests for the wallet manager
+
+ Copyright (c) 2003 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtextstream.h>
+#include <qtimer.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <dcopclient.h>
+#include <kwallet.h>
+
+#include "kopetewalletmanager.h"
+#include "kopetewallettest_program.h"
+
+static QTextStream _out( stdout, IO_WriteOnly );
+
+void closeWallet()
+{
+ Kopete::WalletManager::self()->closeWallet();
+}
+
+void delay()
+{
+ QTimer::singleShot( 3000, qApp, SLOT( quit() ) );
+ qApp->exec();
+}
+
+void openWalletAsync()
+{
+ WalletReciever *r = new WalletReciever;
+ _out << "[ASYNC] About to open wallet, receiver: " << r << endl;
+ Kopete::WalletManager::self()->openWallet( r, SLOT( gotWallet( KWallet::Wallet* ) ) );
+}
+
+void WalletReciever::gotWallet( KWallet::Wallet *w )
+{
+ _out << "[ASYNC] Received wallet pointer: " << w << " for receiver: " << this << endl;
+}
+
+void WalletReciever::timer()
+{
+ _out << "Timer..." << endl;
+}
+
+int main( int argc, char *argv[] )
+{
+ KAboutData aboutData( "kopetewallettest", "kopetewallettest", "version" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineOptions opts[] = { {"+action",0,0}, KCmdLineLastOption };
+ KCmdLineArgs::addCmdLineOptions( opts );
+ KApplication app( "kopetewallettest" );
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ // must register with DCOP or async callbacks will fail
+ _out << "DCOP registration returned " << app.dcopClient()->registerAs(app.name()) << endl;
+
+ for( int i = 0; i < args->count(); ++i )
+ {
+ QString arg = args->arg( i );
+ _out << "Processing " << arg << endl;
+ if( arg == QString::fromLatin1( "open" ) ) openWalletAsync();
+ if( arg == QString::fromLatin1( "close" ) ) closeWallet();
+ if( arg == QString::fromLatin1( "delay" ) ) delay();
+ _out << "Done." << endl;
+ }
+
+ WalletReciever *r = new WalletReciever;
+
+ QTimer timer;
+ r->connect( &timer, SIGNAL( timeout() ), SLOT( timer() ) );
+ timer.start( 1000 );
+
+ _out << "About to start 30 second event loop" << endl;
+ QTimer::singleShot( 30000, qApp, SLOT( quit() ) );
+ return qApp->exec();
+}
+
+#include "kopetewallettest_program.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/tests/kopetewallettest_program.h b/kopete/libkopete/tests/kopetewallettest_program.h
new file mode 100644
index 00000000..58bdbb6e
--- /dev/null
+++ b/kopete/libkopete/tests/kopetewallettest_program.h
@@ -0,0 +1,17 @@
+#ifndef KOPETEWALLETTEST_H
+#define KOPETEWALLETTEST_H
+
+#include <qobject.h>
+
+namespace KWallet { class Wallet; }
+
+class WalletReciever : public QObject
+{
+ Q_OBJECT
+public slots:
+ void timer();
+private slots:
+ void gotWallet( KWallet::Wallet *w );
+};
+
+#endif
diff --git a/kopete/libkopete/tests/link-parser-testcases/broken-html-1.input b/kopete/libkopete/tests/link-parser-testcases/broken-html-1.input
new file mode 100644
index 00000000..ecaf4b7e
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/broken-html-1.input
@@ -0,0 +1 @@
+<a href="$URL" title="$URL">$URL</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/broken-html-1.output b/kopete/libkopete/tests/link-parser-testcases/broken-html-1.output
new file mode 100644
index 00000000..5bf3f88a
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/broken-html-1.output
@@ -0,0 +1 @@
+<a href="$URL" title="$URL">$URL</a>
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-html-1.input b/kopete/libkopete/tests/link-parser-testcases/working-html-1.input
new file mode 100644
index 00000000..306ab458
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-html-1.input
@@ -0,0 +1 @@
+$URL \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-html-1.output b/kopete/libkopete/tests/link-parser-testcases/working-html-1.output
new file mode 100644
index 00000000..ecaf4b7e
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-html-1.output
@@ -0,0 +1 @@
+<a href="$URL" title="$URL">$URL</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-html-2.input b/kopete/libkopete/tests/link-parser-testcases/working-html-2.input
new file mode 100644
index 00000000..4480dee7
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-html-2.input
@@ -0,0 +1 @@
+<a href="$URL">KDE</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-html-2.output b/kopete/libkopete/tests/link-parser-testcases/working-html-2.output
new file mode 100644
index 00000000..4480dee7
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-html-2.output
@@ -0,0 +1 @@
+<a href="$URL">KDE</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.input b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.input
new file mode 100644
index 00000000..306ab458
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.input
@@ -0,0 +1 @@
+$URL \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.output b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.output
new file mode 100644
index 00000000..ecaf4b7e
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-1.output
@@ -0,0 +1 @@
+<a href="$URL" title="$URL">$URL</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.input b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.input
new file mode 100644
index 00000000..14e0e606
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.input
@@ -0,0 +1 @@
+$URL/ \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.output b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.output
new file mode 100644
index 00000000..109c616b
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-2.output
@@ -0,0 +1 @@
+<a href="$URL/" title="$URL/">$URL/</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.input b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.input
new file mode 100644
index 00000000..828cd483
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.input
@@ -0,0 +1 @@
+www.kde.org/ \ No newline at end of file
diff --git a/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.output b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.output
new file mode 100644
index 00000000..9e898eb0
--- /dev/null
+++ b/kopete/libkopete/tests/link-parser-testcases/working-plaintext-3.output
@@ -0,0 +1 @@
+<a href="$URL/" title="$URL/">www.kde.org/</a> \ No newline at end of file
diff --git a/kopete/libkopete/tests/mock/Makefile.am b/kopete/libkopete/tests/mock/Makefile.am
new file mode 100644
index 00000000..b132a2a5
--- /dev/null
+++ b/kopete/libkopete/tests/mock/Makefile.am
@@ -0,0 +1,14 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private $(all_includes)
+
+noinst_LTLIBRARIES = libkopete_mock.la
+
+libkopete_mock_la_SOURCES = kopetemessage_mock.cpp kopeteaccount_mock.cpp kopetecontact_mock.cpp kopetemetacontact_mock.cpp kopeteprotocol_mock.cpp
+
+libkopete_mock_la_LDFLAGS = $(all_libraries) -lkabc
+libkopete_mock_la_LIBADD = ../../libkopete.la ../../private/libkopeteprivate.la $(LIB_KHTML)
+
+noinst_HEADERS = kopetemessage_mock.h kopetecontact_mock.h kopetemetacontact_mock.h kopeteaccount_mock.h kopeteprotocol_mock.h
+
+
diff --git a/kopete/libkopete/tests/mock/kopeteaccount_mock.cpp b/kopete/libkopete/tests/mock/kopeteaccount_mock.cpp
new file mode 100644
index 00000000..8a8425bc
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopeteaccount_mock.cpp
@@ -0,0 +1,61 @@
+/*
+ Account mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteaccount_mock.h"
+#include "kopetemetacontact.h"
+#include "kopeteaccount_mock.h"
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+Account::Account(Kopete::Protocol *parent, const QString &accountID, const char *name) : Kopete::Account(parent, accountID, name)
+{
+
+}
+
+Account::~Account()
+{
+
+}
+
+bool Account::createContact( const QString &contactId, Kopete::MetaContact *parentContact )
+{
+ return true;
+}
+
+void Account::connect( const Kopete::OnlineStatus& initialStatus)
+{
+ // do nothing
+}
+
+void Account::disconnect()
+{
+ // do nothing
+}
+
+void Account::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason)
+{
+ // do nothing
+}
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
diff --git a/kopete/libkopete/tests/mock/kopeteaccount_mock.h b/kopete/libkopete/tests/mock/kopeteaccount_mock.h
new file mode 100644
index 00000000..55ba15cc
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopeteaccount_mock.h
@@ -0,0 +1,54 @@
+/*
+ Account mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETEACCOUNT_MOCK_H_
+#define _KOPETEACCOUNT_MOCK_H_
+
+#include "kopeteaccount.h"
+
+class Kopete::Protocol;
+class Kopete::OnlineStatus;
+class Kopete::MetaContact;
+
+class QString;
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+class Account : public Kopete::Account
+{
+public:
+ Account(Kopete::Protocol *parent, const QString &accountID, const char *name=0L);
+ ~Account();
+ // pure virtual functions implementation
+ virtual bool createContact( const QString &contactId, MetaContact *parentContact );
+ virtual void connect( const Kopete::OnlineStatus& initialStatus = OnlineStatus() );
+ virtual void disconnect();
+ virtual void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null );
+};
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
+
+
+#endif
+
diff --git a/kopete/libkopete/tests/mock/kopetecontact_mock.cpp b/kopete/libkopete/tests/mock/kopetecontact_mock.cpp
new file mode 100644
index 00000000..19cfa7b0
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetecontact_mock.cpp
@@ -0,0 +1,44 @@
+/*
+ Contact mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontact_mock.h"
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+Contact::Contact( Kopete::Account *account, const QString &id, Kopete::MetaContact *parent, const QString &icon) : Kopete::Contact( account, id, parent, icon)
+{
+
+}
+
+Contact::~Contact()
+{
+
+}
+
+Kopete::ChatSession* Contact::manager( CanCreateFlags canCreate)
+{
+ return 0L;
+}
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete \ No newline at end of file
diff --git a/kopete/libkopete/tests/mock/kopetecontact_mock.h b/kopete/libkopete/tests/mock/kopetecontact_mock.h
new file mode 100644
index 00000000..e445a571
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetecontact_mock.h
@@ -0,0 +1,49 @@
+/*
+ Contact mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETECONTACT_MOCK_H_
+#define _KOPETECONTACT_MOCK_H_
+
+#include "kopetecontact.h"
+
+class Kopete::MetaContact;
+class Kopete::Account;
+class Kopete::ChatSession;
+class QString;
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+class Contact : public Kopete::Contact
+{
+public:
+ Contact( Kopete::Account *account, const QString &id, Kopete::MetaContact *parent, const QString &icon = QString::null );
+ ~Contact();
+ virtual Kopete::ChatSession* manager( CanCreateFlags canCreate = CannotCreate );
+};
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
+
+
+#endif
+
diff --git a/kopete/libkopete/tests/mock/kopetemessage_mock.cpp b/kopete/libkopete/tests/mock/kopetemessage_mock.cpp
new file mode 100644
index 00000000..a3e543e3
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetemessage_mock.cpp
@@ -0,0 +1,20 @@
+/*
+ Message mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemessage_mock.h"
+
+
diff --git a/kopete/libkopete/tests/mock/kopetemessage_mock.h b/kopete/libkopete/tests/mock/kopetemessage_mock.h
new file mode 100644
index 00000000..13c92574
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetemessage_mock.h
@@ -0,0 +1,39 @@
+/*
+ Message mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETEMESSAGE_MOCK_H_
+#define _KOPETEMESSAGE_MOCK_H_
+
+#include "kopetemessage.h"
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+class Message : public Kopete::Message
+{
+
+};
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
+
+#endif \ No newline at end of file
diff --git a/kopete/libkopete/tests/mock/kopetemetacontact_mock.cpp b/kopete/libkopete/tests/mock/kopetemetacontact_mock.cpp
new file mode 100644
index 00000000..32f0fe1c
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetemetacontact_mock.cpp
@@ -0,0 +1,20 @@
+/*
+ MetaContact Mock Object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetemetacontact_mock.h"
+
+
diff --git a/kopete/libkopete/tests/mock/kopetemetacontact_mock.h b/kopete/libkopete/tests/mock/kopetemetacontact_mock.h
new file mode 100644
index 00000000..f3311713
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopetemetacontact_mock.h
@@ -0,0 +1,41 @@
+/*
+ MetaContact Mock Object
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETEMETACONTACT_MOCK_H_
+#define _KOPETEMETACONTACT_MOCK_H_
+
+#include "kopetemetacontact.h"
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+class MetaContact : public Kopete::MetaContact
+{
+
+};
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
+
+
+#endif
+
diff --git a/kopete/libkopete/tests/mock/kopeteprotocol_mock.cpp b/kopete/libkopete/tests/mock/kopeteprotocol_mock.cpp
new file mode 100644
index 00000000..d3bbd0e2
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopeteprotocol_mock.cpp
@@ -0,0 +1,49 @@
+/*
+ Protocol mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteprotocol_mock.h"
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+Protocol::Protocol( KInstance *instance, QObject *parent, const char *name ) : Kopete::Protocol(instance, parent, name)
+{
+
+}
+
+Account* Protocol::createNewAccount( const QString &accountId )
+{
+ return 0L;
+}
+
+AddContactPage* Protocol::createAddContactWidget( QWidget *parent, Kopete::Account *account )
+{
+ return 0L;
+}
+
+KopeteEditAccountWidget* Protocol::createEditAccountWidget( Kopete::Account *account, QWidget *parent )
+{
+ return 0L;
+}
+
+} // end ns mock
+} // end ns test
+} // end ns kopete
diff --git a/kopete/libkopete/tests/mock/kopeteprotocol_mock.h b/kopete/libkopete/tests/mock/kopeteprotocol_mock.h
new file mode 100644
index 00000000..189f7d79
--- /dev/null
+++ b/kopete/libkopete/tests/mock/kopeteprotocol_mock.h
@@ -0,0 +1,53 @@
+/*
+ Protocol mock object class
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _KOPETEPROTOCOL_MOCK_H_
+#define _KOPETEPROTOCOL_MOCK_H_
+
+#include "kopeteprotocol.h"
+
+class KInstance;
+class QObject;
+
+class KopeteEditAccountWidget;
+class AddContactPage;
+class KopeteEditAccountWidget;
+
+namespace Kopete
+{
+namespace Test
+{
+namespace Mock
+{
+
+class Protocol : public Kopete::Protocol
+{
+public:
+ Protocol( KInstance *instance, QObject *parent, const char *name );
+ // pure virtual functions implemented
+ virtual Account *createNewAccount( const QString &accountId );
+ virtual AddContactPage *createAddContactWidget( QWidget *parent, Kopete::Account *account );
+ virtual KopeteEditAccountWidget * createEditAccountWidget( Kopete::Account *account, QWidget *parent );
+};
+
+} // end ns Kopete::Test::Mock
+} // end ns Kopete::Test
+} // end ns Kopete
+
+
+#endif
+
diff --git a/kopete/libkopete/tests/template_test.cpp b/kopete/libkopete/tests/template_test.cpp
new file mode 100644
index 00000000..8598f79f
--- /dev/null
+++ b/kopete/libkopete/tests/template_test.cpp
@@ -0,0 +1,37 @@
+/*
+ Tests for some requirement
+
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kunittest/module.h>
+#include "template_test.h"
+
+using namespace KUnitTest;
+
+KUNITTEST_MODULE( kunittest_template_test, "KopeteSuite");
+KUNITTEST_MODULE_REGISTER_TESTER( Template_Test );
+
+void Template_Test::allTests()
+{
+ testSomething();
+}
+
+void Template_Test::testSomething()
+{
+ int result = 1;
+ int expected = 1;
+ // result should be the expected one
+ CHECK(result, expected);
+}
diff --git a/kopete/libkopete/tests/template_test.h b/kopete/libkopete/tests/template_test.h
new file mode 100644
index 00000000..4d0f1617
--- /dev/null
+++ b/kopete/libkopete/tests/template_test.h
@@ -0,0 +1,35 @@
+/*
+ Tests for some requirement
+
+ Copyright (c) 2005 by Duncan Mac-Vicar <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TEMPLATE_TEST_H
+#define TEMPLATE_TEST_H
+
+#include <kunittest/tester.h>
+
+// change to SlotTester when it works
+class Template_Test : public KUnitTest::Tester
+{
+public:
+ void allTests();
+public slots:
+ void testSomething();
+private:
+
+};
+
+#endif
+
diff --git a/kopete/libkopete/ui/Makefile.am b/kopete/libkopete/ui/Makefile.am
new file mode 100644
index 00000000..211e0b48
--- /dev/null
+++ b/kopete/libkopete/ui/Makefile.am
@@ -0,0 +1,31 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = -DKDE_NO_COMPAT -DQT_NO_COMPAT -DQT_NO_CAST_ASCII -DQT_NO_ASCII_CAST \
+ $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/libkopete/private $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteui.la
+
+libkopeteui_la_SOURCES = kopetecontactaction.cpp addcontactpage.cpp \
+ editaccountwidget.cpp kopetepassworddialog.ui kopetestdaction.cpp kopeteawaydialogbase.ui \
+ kopetefileconfirmdialog.cpp fileconfirmbase.ui userinfodialog.cpp kopeteview.cpp \
+ kopetepasswordwidgetbase.ui kopetepasswordwidget.cpp accountselector.cpp kopeteviewplugin.cpp \
+ addresseeitem.cpp addressbookselectorwidget_base.ui addressbookselectordialog.cpp \
+ addressbookselectorwidget.cpp metacontactselectorwidget_base.ui metacontactselectorwidget.cpp \
+ kopetelistview.cpp kopetelistviewitem.cpp kopetelistviewsearchline.cpp \
+ contactaddednotifywidget.ui contactaddednotifydialog.cpp addressbooklinkwidget_base.ui \
+ addressbooklinkwidget.cpp
+
+libkopeteui_la_LDFLAGS = $(all_libraries) -lkabc
+libkopeteui_la_LIBADD = ../private/libkopeteprivate.la $(LIB_KHTML)
+
+kopeteincludedir = $(includedir)/kopete/ui
+kopeteinclude_HEADERS = accountselector.h fileconfirmbase.h \
+ kopetefileconfirmdialog.h kopetepasswordwidget.h kopeteview.h addcontactpage.h \
+ kopeteawaydialogbase.h kopetepasswordwidgetbase.h kopeteviewplugin.h editaccountwidget.h \
+ kopetecontactaction.h kopetepassworddialog.h kopetestdaction.h userinfodialog.h \
+ addressbookselectordialog.h addressbookselectorwidget.h kopetelistview.h kopetelistviewitem.h \
+ kopetelistviewsearchline.h addressbooklinkwidget.h
+
+noinst_HEADERS = addresseeitem.h contactaddednotifywidget.h
+
+# vim: set noet:
+
diff --git a/kopete/libkopete/ui/accountselector.cpp b/kopete/libkopete/ui/accountselector.cpp
new file mode 100644
index 00000000..2ea8e719
--- /dev/null
+++ b/kopete/libkopete/ui/accountselector.cpp
@@ -0,0 +1,186 @@
+/*
+ accountselector.cpp - An Accountselector
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "accountselector.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+
+#include <qheader.h>
+#include <qlayout.h>
+#include <qpixmap.h>
+
+#include <kdebug.h>
+#include <klistview.h>
+
+class AccountListViewItem : public KListViewItem
+{
+ private:
+ Kopete::Account *mAccount;
+
+ public:
+ AccountListViewItem(QListView *parent, Kopete::Account *acc)
+ : KListViewItem(parent)
+ {
+ if (acc==0)
+ return;
+
+ /*kdDebug(14010) << k_funcinfo <<
+ "account name = " << acc->accountId() << endl;*/
+ mAccount = acc;
+ setText(0, mAccount->accountId());
+ setPixmap(0, mAccount->accountIcon());
+ }
+
+ Kopete::Account *account()
+ {
+ return mAccount;
+ }
+};
+
+
+// ----------------------------------------------------------------------------
+
+class AccountSelectorPrivate
+{
+ public:
+ KListView *lv;
+ Kopete::Protocol *proto;
+};
+
+
+AccountSelector::AccountSelector(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ //kdDebug(14010) << k_funcinfo << "for no special protocol" << endl;
+ d = new AccountSelectorPrivate;
+ d->proto = 0;
+ initUI();
+}
+
+
+AccountSelector::AccountSelector(Kopete::Protocol *proto, QWidget *parent,
+ const char *name) : QWidget(parent, name)
+{
+ //kdDebug(14010) << k_funcinfo << " for protocol " << proto->pluginId() << endl;
+ d = new AccountSelectorPrivate;
+ d->proto = proto;
+ initUI();
+}
+
+
+AccountSelector::~AccountSelector()
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ delete d;
+}
+
+
+void AccountSelector::initUI()
+{
+ kdDebug(14010) << k_funcinfo << endl;
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ d->lv = new KListView(this);
+ d->lv->setFullWidth(true);
+ d->lv->addColumn(QString::fromLatin1(""));
+ d->lv->header()->hide();
+
+ if(d->proto != 0)
+ {
+ kdDebug(14010) << k_funcinfo << "creating list for a certain protocol" << endl;
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(d->proto);
+ QDictIterator<Kopete::Account> it(accounts);
+ for(; Kopete::Account *account = it.current(); ++it)
+ {
+ new AccountListViewItem(d->lv, account);
+ }
+ }
+ else
+ {
+ kdDebug(14010) << k_funcinfo << "creating list of all accounts" << endl;
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ Kopete::Account *account = 0;
+ for(account = accounts.first(); account; account = accounts.next())
+ {
+ new AccountListViewItem(d->lv, account);
+ }
+ }
+
+ connect(d->lv, SIGNAL(selectionChanged(QListViewItem *)),
+ this, SLOT(slotSelectionChanged(QListViewItem *)));
+}
+
+
+void AccountSelector::setSelected(Kopete::Account *account)
+{
+ if (account==0)
+ return;
+
+ QListViewItemIterator it(d->lv);
+ while (it.current())
+ {
+ if(static_cast<AccountListViewItem *>(it.current())->account() == account)
+ {
+ it.current()->setSelected(true);
+ return;
+ }
+ }
+}
+
+
+bool AccountSelector::isSelected(Kopete::Account *account)
+{
+ if (account==0)
+ return false;
+
+ QListViewItemIterator it(d->lv);
+ while (it.current())
+ {
+ if(static_cast<AccountListViewItem *>(it.current())->account() == account)
+ return true;
+ }
+ return false;
+}
+
+
+Kopete::Account *AccountSelector::selectedItem()
+{
+ //kdDebug(14010) << k_funcinfo << endl;
+
+ if (d->lv->selectedItem() != 0)
+ return static_cast<AccountListViewItem *>(d->lv->selectedItem())->account();
+ return 0;
+}
+
+
+void AccountSelector::slotSelectionChanged(QListViewItem *item)
+{
+ //kdDebug(14010) << k_funcinfo << endl;
+ if (item != 0)
+ {
+ Kopete::Account *account = static_cast<AccountListViewItem *>(item)->account();
+ if (account != 0)
+ {
+ emit selectionChanged(account);
+ return;
+ }
+ }
+
+ emit selectionChanged(0);
+}
+
+#include "accountselector.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/accountselector.h b/kopete/libkopete/ui/accountselector.h
new file mode 100644
index 00000000..4f5d50ac
--- /dev/null
+++ b/kopete/libkopete/ui/accountselector.h
@@ -0,0 +1,92 @@
+/*
+ accountselector.cpp - An Accountselector
+
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ACCOUNTSELECTOR_H
+#define ACCOUNTSELECTOR_H
+
+#include <qwidget.h>
+#include <kopeteprotocol.h>
+#include "kopete_export.h"
+
+class AccountSelectorPrivate;
+class QListViewItem;
+/**
+ * \brief widget to select an account, based on KListView
+ * @author Stefan Gehn <metz AT gehn.net>
+ */
+class KOPETE_EXPORT AccountSelector : public QWidget
+{
+Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ *
+ * The parameters @p parent and @p name are handled by
+ * KListView.
+ */
+ AccountSelector(QWidget *parent=0, const char *name=0);
+
+ /**
+ * Constructor for a list of accounts for one protocol only
+ *
+ * The parameters @p parent and @p name are handled by
+ * KListView. @p proto defines the protocol whose accounts are
+ * shown in the list
+ */
+ AccountSelector(Kopete::Protocol *proto, QWidget *parent=0, const char *name=0);
+
+ /**
+ * Destructor.
+ */
+ ~AccountSelector();
+
+ /**
+ * Select @p account in the list, in case it's part of the list
+ */
+ void setSelected(Kopete::Account *account);
+
+ /**
+ * Returns true in case @p account is in the list and
+ * the currently selected item, false otherwise
+ */
+ bool isSelected(Kopete::Account *account);
+
+ /**
+ * @return the currently selected account.
+ */
+ Kopete::Account *selectedItem();
+
+ signals:
+ /**
+ * Emitted whenever the selection changed, @p acc is a pointer to the
+ * newly selected account
+ */
+ void selectionChanged(Kopete::Account *acc);
+
+ private slots:
+ void slotSelectionChanged(QListViewItem *item);
+
+ private:
+ void initUI();
+
+ private:
+ AccountSelectorPrivate *d;
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/addcontactpage.cpp b/kopete/libkopete/ui/addcontactpage.cpp
new file mode 100644
index 00000000..f308a7d4
--- /dev/null
+++ b/kopete/libkopete/ui/addcontactpage.cpp
@@ -0,0 +1,29 @@
+/*
+ addcontactpage.cpp - Kopete's Add Contact GUI
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "addcontactpage.h"
+
+AddContactPage::AddContactPage(QWidget *parent, const char *name ) : QWidget(parent,name)
+{
+}
+
+AddContactPage::~AddContactPage()
+{
+}
+
+#include "addcontactpage.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/addcontactpage.h b/kopete/libkopete/ui/addcontactpage.h
new file mode 100644
index 00000000..506c5bcc
--- /dev/null
+++ b/kopete/libkopete/ui/addcontactpage.h
@@ -0,0 +1,65 @@
+/*
+ addcontactpage.h - Kopete's Add Contact GUI
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ADDCONTACTPAGE_H
+#define ADDCONTACTPAGE_H
+
+#include <qwidget.h>
+#include <kopeteprotocol.h>
+
+#include "kopete_export.h"
+
+/**
+ * @author Duncan Mac-Vicar P. <duncan@kde.org>
+ * @todo i want to be able to have a assync apply.
+ * (in the case of jabber, i need to translate the legacy id to a JID)
+ * this could also be usefull in the case of MLSN to check if no error (and also jabber)
+ */
+class KOPETE_EXPORT AddContactPage : public QWidget
+{
+Q_OBJECT
+
+public:
+ AddContactPage(QWidget *parent=0, const char *name=0);
+ virtual ~AddContactPage();
+ //Kopete::Protocol *protocol;
+
+ /**
+ * Plugin should reimplement this methode.
+ * return true if the content of the page are valid
+ *
+ * This method is called in the add account wizzard when the user press the next button
+ * and this page is showed. when it return false, it does not go to the nextpage.
+ * You should popup a dialog to explain WHY the page has not been validate
+ */
+ virtual bool validateData()=0;
+
+ /**
+ * add the contact the the specified meta contact, with the given account
+ * return false if the contact has not been added
+ */
+ virtual bool apply(Kopete::Account * , Kopete::MetaContact *) = 0;
+
+signals:
+ /**
+ * New incarnation of validateData, emit it everytime you think the current data is valid/invalid
+ */
+ void dataValid( AddContactPage *, bool);
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/addressbooklinkwidget.cpp b/kopete/libkopete/ui/addressbooklinkwidget.cpp
new file mode 100644
index 00000000..a6aff32b
--- /dev/null
+++ b/kopete/libkopete/ui/addressbooklinkwidget.cpp
@@ -0,0 +1,99 @@
+/*
+ AddressBookLinkWidget
+
+ A compact widget for showing and changing which address book item a
+ particular Kopete::MetaContact is related to.
+
+ Comprises a label showing the contact's name, a Clear button, and a Change
+ button that usually invokes the AddressBookSelectorWidget.
+
+ Copyright (c) 2006 by Will Stephenson <wstephenson@kde.org>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qapplication.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+
+#include <kiconloader.h>
+
+#include <kopetemetacontact.h>
+
+#include "addressbooklinkwidget.h"
+#include "addressbookselectordialog.h"
+#include "addressbookselectorwidget.h"
+
+namespace Kopete {
+namespace UI {
+
+
+AddressBookLinkWidget::AddressBookLinkWidget( QWidget * parent, const char * name ) : AddressBookLinkWidgetBase( parent, name ), mMetaContact( 0 )
+{
+ btnClear->setIconSet( SmallIconSet( QApplication::reverseLayout() ? QString::fromLatin1( "locationbar_erase" ) : QString::fromLatin1( "clear_left") ) );
+ connect( btnClear, SIGNAL( clicked() ), this, SLOT( slotClearAddressee() ) );
+ connect( btnSelectAddressee, SIGNAL( clicked() ), SLOT( slotSelectAddressee() ) );
+}
+
+void AddressBookLinkWidget::setAddressee( const KABC::Addressee& addr )
+{
+ edtAddressee->setText( addr.realName() );
+ btnClear->setEnabled( !addr.isEmpty() );
+}
+
+void AddressBookLinkWidget::setMetaContact( const Kopete::MetaContact * mc )
+{
+ mMetaContact = mc;
+}
+
+QString AddressBookLinkWidget::uid() const
+{
+ return mSelectedUid;
+}
+
+void AddressBookLinkWidget::slotClearAddressee()
+{
+ edtAddressee->clear();
+ btnClear->setEnabled( false );
+ KABC::Addressee mrEmpty;
+ mSelectedUid = QString::null;
+ emit addresseeChanged( mrEmpty );
+}
+
+void AddressBookLinkWidget::slotSelectAddressee()
+{
+ QString message;
+ if ( mMetaContact )
+ message = i18n("Choose the corresponding entry for '%1'" ).arg( mMetaContact->displayName() );
+ else
+ message = i18n("Choose the corresponding entry in the address book" );
+
+ Kopete::UI::AddressBookSelectorDialog dialog( i18n("Addressbook Association"), message, ( mMetaContact ? mMetaContact->metaContactId() : QString::null ), this );
+ int result = dialog.exec();
+
+ KABC::Addressee addr;
+ if ( result == QDialog::Accepted )
+ {
+ addr = dialog.addressBookSelectorWidget()->addressee();
+
+ edtAddressee->setText( addr.realName() );
+ btnClear->setEnabled( !addr.isEmpty() );
+ mSelectedUid = ( addr.isEmpty() ? QString::null : addr.uid() );
+ emit addresseeChanged( addr );
+ }
+}
+
+} // end namespace UI
+} // end namespace Kopete
+
+#include "addressbooklinkwidget.moc"
diff --git a/kopete/libkopete/ui/addressbooklinkwidget.h b/kopete/libkopete/ui/addressbooklinkwidget.h
new file mode 100644
index 00000000..dff23c58
--- /dev/null
+++ b/kopete/libkopete/ui/addressbooklinkwidget.h
@@ -0,0 +1,81 @@
+/*
+ AddressBookLinkWidget
+
+ A compact widget for showing and changing which address book item a
+ particular Kopete::MetaContact is related to.
+
+ Comprises a label showing the contact's name, a Clear button, and a Change
+ button that usually invokes the AddressBookSelectorWidget.
+
+ Copyright (c) 2006 by Will Stephenson <wstephenson@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ADDRESSBOOKLINKWIDGET_H
+#define ADDRESSBOOKLINKWIDGET_H
+
+#include <kabc/addressee.h>
+
+#include "addressbooklinkwidget_base.h"
+
+namespace Kopete {
+class MetaContact;
+
+namespace UI {
+
+/**
+ * A compact widget for showing and changing which address book item a
+ * particular Kopete::MetaContact is related to.
+ *
+ * Comprises a label showing the contact's name, a Clear button, and a Change
+ * button that usually invokes the AddressBookSelectorWidget.
+ */
+class AddressBookLinkWidget : public AddressBookLinkWidgetBase
+{
+Q_OBJECT
+public:
+ AddressBookLinkWidget( QWidget * parent, const char * name );
+ ~AddressBookLinkWidget() {}
+ /**
+ * Set the currently selected addressee
+ */
+ void setAddressee( const KABC::Addressee& addr );
+ /**
+ * Set the current metacontact so that the selector dialog may be preselected
+ */
+ void setMetaContact( const Kopete::MetaContact * );
+ /**
+ * Return the selected addressbook UID.
+ */
+ QString uid() const;
+signals:
+ /**
+ * Emitted when the selected addressee changed. addr is the KABC::Addressee that was selected. If addr.isEmpty() is empty, the clear button was clicked.
+ */
+ void addresseeChanged( const KABC::Addressee& addr );
+
+ /**
+ * Provided so you can perform your own actions instead of opening the AddressBookSelectorWidget.
+ * To do so, QObject::disconnect() btnSelectAddressee and connect your own slot to this signal
+ */
+ void selectAddresseeClicked();
+protected slots:
+ void slotClearAddressee();
+ void slotSelectAddressee();
+private:
+ const Kopete::MetaContact * mMetaContact;
+ QString mSelectedUid;
+};
+} // end namespace UI
+} // end namespace Kopete
+#endif
diff --git a/kopete/libkopete/ui/addressbooklinkwidget_base.ui b/kopete/libkopete/ui/addressbooklinkwidget_base.ui
new file mode 100644
index 00000000..4656459c
--- /dev/null
+++ b/kopete/libkopete/ui/addressbooklinkwidget_base.ui
@@ -0,0 +1,78 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddressBookLinkWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddressBookLinkWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>350</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>edtAddressee</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The KDE Address Book entry associated with this Kopete Contact</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnClear</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Clear</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnSelectAddressee</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>C&amp;hange...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select an address book entry</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/addressbookselectordialog.cpp b/kopete/libkopete/ui/addressbookselectordialog.cpp
new file mode 100644
index 00000000..7d2e17ff
--- /dev/null
+++ b/kopete/libkopete/ui/addressbookselectordialog.cpp
@@ -0,0 +1,89 @@
+/*
+ AddressBookSelectorDialog
+ Nice Dialog to select a KDE AddressBook contact
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "addressbookselectordialog.h"
+#include "addressbookselectorwidget.h"
+#include <kdialogbase.h>
+#include <qdialog.h>
+#include <qlistview.h>
+#include <qvbox.h>
+#include <klocale.h>
+#include <kdialog.h>
+
+namespace Kopete
+{
+namespace UI
+{
+
+AddressBookSelectorDialog::AddressBookSelectorDialog(const QString &title, const QString &message, const QString &preSelectUid, QWidget *parent, const char *name, bool modal ) : KDialogBase(parent, name, modal, title, Help|Ok|Cancel, Ok, true )
+{
+ QVBox *vbox=new QVBox(this);
+ m_addressBookSelectorWidget= new AddressBookSelectorWidget(vbox);
+ m_addressBookSelectorWidget->setLabelMessage(message);
+
+ vbox->setSpacing( KDialog::spacingHint() );
+
+ setMainWidget(vbox);
+ enableButtonOK(false);
+ //setHelp("linkaddressbook");
+
+ connect(m_addressBookSelectorWidget, SIGNAL(addresseeListClicked( QListViewItem * )), SLOT(slotWidgetAddresseeListClicked( QListViewItem * )));
+
+ if ( !preSelectUid.isEmpty() )
+ m_addressBookSelectorWidget->selectAddressee(preSelectUid);
+}
+
+AddressBookSelectorDialog::~AddressBookSelectorDialog()
+{
+}
+
+KABC::Addressee AddressBookSelectorDialog::getAddressee( const QString &title, const QString &message, const QString &preSelectUid, QWidget *parent)
+{
+ AddressBookSelectorDialog dialog(title, message, preSelectUid, parent);
+ int result = dialog.exec();
+
+ KABC::Addressee adr;
+ if ( result == QDialog::Accepted )
+ adr = dialog.addressBookSelectorWidget()->addressee();
+
+ return adr;
+}
+
+void AddressBookSelectorDialog::slotWidgetAddresseeListClicked( QListViewItem *addressee )
+{
+ // enable ok if a valid addressee is selected
+ enableButtonOK( addressee ? addressee->isSelected() : false);
+}
+
+void AddressBookSelectorDialog::accept()
+{
+ QDialog::accept();
+}
+
+void AddressBookSelectorDialog::reject()
+{
+ QDialog::reject();
+}
+
+} // namespace UI
+} // namespace Kopete
+
+#include "addressbookselectordialog.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/addressbookselectordialog.h b/kopete/libkopete/ui/addressbookselectordialog.h
new file mode 100644
index 00000000..f391aa3a
--- /dev/null
+++ b/kopete/libkopete/ui/addressbookselectordialog.h
@@ -0,0 +1,90 @@
+/*
+ AddressBookSelectorDialog
+ Nice Dialog to select a KDE AddressBook contact
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ADDRESSBOOKSELECTORDIALOG_H
+#define ADDRESSBOOKSELECTORDIALOG_H
+
+#include <kdemacros.h>
+#include "kopete_export.h"
+#include <kdialogbase.h>
+
+namespace KABC
+{
+ class AddressBook;
+ class Addressee;
+}
+
+namespace Kopete
+{
+namespace UI
+{
+
+class AddressBookSelectorWidget;
+
+/**
+ * A dialog that uses AddressBookSelectorWidget to allow the user
+ * to select a KDE addressbook contact. If you want to use special features
+ * you can use @see addressBookSelectorWidget() to get the pointer to the
+ * AddressBookSelectorWidget object and set the desired options there.
+ *
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+class KOPETE_EXPORT AddressBookSelectorDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ /**
+ * The constructor of an empty AddressBookSelectorWidget
+ */
+ AddressBookSelectorDialog( const QString &title, const QString &message, const QString &preSelectUid, QWidget *parent=0L, const char *name=0L, bool modal = false );
+ /**
+ * The destructor of the dialog
+ */
+ ~AddressBookSelectorDialog();
+
+ /**
+ * @returns the AddressBookSelectorWidget widget so that additional
+ * parameters can be set by using it.
+ */
+ AddressBookSelectorWidget *addressBookSelectorWidget() const
+ { return m_addressBookSelectorWidget; };
+
+ /**
+ * Creates a modal dialog, lets the user to select a addressbook contact
+ * and returns when the dialog is closed.
+ *
+ * @returns the selected contact, or a null addressee if the user
+ * pressed the Cancel button. Optionally
+ */
+ static KABC::Addressee getAddressee( const QString &title, const QString &message, const QString &preSelectUid, QWidget *parent = 0L );
+
+protected slots:
+ virtual void accept();
+ virtual void reject();
+ void slotWidgetAddresseeListClicked( QListViewItem *addressee );
+protected:
+ AddressBookSelectorWidget *m_addressBookSelectorWidget;
+};
+
+} // namespace UI
+} // namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/addressbookselectorwidget.cpp b/kopete/libkopete/ui/addressbookselectorwidget.cpp
new file mode 100644
index 00000000..50c4a885
--- /dev/null
+++ b/kopete/libkopete/ui/addressbookselectorwidget.cpp
@@ -0,0 +1,171 @@
+/*
+ AddressBookSelectorWidget
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Based on LinkAddressBookUI whose code was shamelessly stolen from
+ kopete's add new contact wizard, used in Konversation, and then
+ reappropriated by Kopete.
+
+ LinkAddressBookUI:
+ Copyright (c) 2004 by John Tapsell <john@geola.co.uk>
+ Copyright (c) 2003-2005 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+
+#include <kpushbutton.h>
+#include <kactivelabel.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <klistviewsearchline.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include "addressbookselectorwidget.h"
+#include <addresseeitem.h>
+#include "kabcpersistence.h"
+
+using namespace Kopete::UI;
+
+namespace Kopete
+{
+namespace UI
+{
+
+AddressBookSelectorWidget::AddressBookSelectorWidget( QWidget *parent, const char *name )
+ : AddressBookSelectorWidget_Base( parent, name )
+{
+ m_addressBook = Kopete::KABCPersistence::self()->addressBook();
+
+ // Addressee validation connections
+ connect( addAddresseeButton, SIGNAL( clicked() ), SLOT( slotAddAddresseeClicked() ) );
+ connect( addAddresseeButton, SIGNAL( clicked() ), SIGNAL( addAddresseeClicked() ) );
+
+ connect( addresseeListView, SIGNAL( clicked(QListViewItem * ) ),
+ SIGNAL( addresseeListClicked( QListViewItem * ) ) );
+ connect( addresseeListView, SIGNAL( selectionChanged( QListViewItem * ) ),
+ SIGNAL( addresseeListClicked( QListViewItem * ) ) );
+ connect( addresseeListView, SIGNAL( spacePressed( QListViewItem * ) ),
+ SIGNAL( addresseeListClicked( QListViewItem * ) ) );
+
+ connect( m_addressBook, SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+
+ //We should add a clear KAction here. But we can't really do that with a designer file :\ this sucks
+
+ addresseeListView->setColumnText(2, SmallIconSet(QString::fromLatin1("email")), i18n("Email"));
+
+ kListViewSearchLine->setListView(addresseeListView);
+ slotLoadAddressees();
+
+ addresseeListView->setColumnWidthMode(0, QListView::Manual);
+ addresseeListView->setColumnWidth(0, 63); //Photo is 60, and it's nice to have a small gap, imho
+}
+
+
+AddressBookSelectorWidget::~AddressBookSelectorWidget()
+{
+ disconnect( m_addressBook, SIGNAL( addressBookChanged( AddressBook * ) ), this, SLOT( slotLoadAddressees() ) );
+}
+
+
+KABC::Addressee AddressBookSelectorWidget::addressee()
+{
+ AddresseeItem *item = 0L;
+ item = static_cast<AddresseeItem *>( addresseeListView->selectedItem() );
+
+ if ( item )
+ m_addressee = item->addressee();
+
+ return m_addressee;
+}
+
+void AddressBookSelectorWidget::selectAddressee( const QString &uid )
+{
+ // iterate trough list view
+ QListViewItemIterator it( addresseeListView );
+ while( it.current() )
+ {
+ AddresseeItem *addrItem = (AddresseeItem *) it.current();
+ if ( addrItem->addressee().uid() == uid )
+ {
+ // select the contact item
+ addresseeListView->setSelected( addrItem, true );
+ addresseeListView->ensureItemVisible( addrItem );
+ }
+ ++it;
+ }
+}
+
+bool AddressBookSelectorWidget::addresseeSelected()
+{
+ return addresseeListView->selectedItem() ? true : false;
+}
+
+/** Read in contacts from addressbook, and select the contact that is for our nick. */
+void AddressBookSelectorWidget::slotLoadAddressees()
+{
+ addresseeListView->clear();
+ KABC::AddressBook::Iterator it;
+ AddresseeItem *addr;
+ for( it = m_addressBook->begin(); it != m_addressBook->end(); ++it )
+ {
+ addr = new AddresseeItem( addresseeListView, (*it));
+ }
+
+}
+
+void AddressBookSelectorWidget::setLabelMessage( const QString &msg )
+{
+ lblHeader->setText(msg);
+}
+
+void AddressBookSelectorWidget::slotAddAddresseeClicked()
+{
+ // Pop up add addressee dialog
+ QString addresseeName = KInputDialog::getText( i18n( "New Address Book Entry" ), i18n( "Name the new entry:" ), QString::null, 0, this );
+
+ if ( !addresseeName.isEmpty() )
+ {
+ KABC::Addressee addr;
+ addr.setNameFromString( addresseeName );
+ m_addressBook->insertAddressee(addr);
+ Kopete::KABCPersistence::self()->writeAddressBook( 0 );
+ slotLoadAddressees();
+ // select the addressee we just added
+ QListViewItem * added = addresseeListView->findItem( addresseeName, 1 );
+ kListViewSearchLine->clear();
+ kListViewSearchLine->updateSearch();
+ addresseeListView->setSelected( added, true );
+ addresseeListView->ensureItemVisible( added );
+ }
+}
+
+} // namespace UI
+} // namespace Kopete
+
+#include "addressbookselectorwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/addressbookselectorwidget.h b/kopete/libkopete/ui/addressbookselectorwidget.h
new file mode 100644
index 00000000..3141f726
--- /dev/null
+++ b/kopete/libkopete/ui/addressbookselectorwidget.h
@@ -0,0 +1,90 @@
+/*
+ AddressBookSelectorWidget
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Based on LinkAddressBookUI whose code was shamelessly stolen from
+ kopete's add new contact wizard, used in Konversation, and then
+ reappropriated by Kopete.
+
+ LinkAddressBookUI:
+ Copyright (c) 2004 by John Tapsell <john@geola.co.uk>
+ Copyright (c) 2003-2005 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef AddressBookSelectorWidget_H
+#define AddressBookSelectorWidget_H
+
+#include <kdialogbase.h>
+#include <kabc/addressbook.h>
+
+#include <kdemacros.h>
+#include "kopete_export.h"
+
+#include "addressbookselectorwidget_base.h"
+
+namespace KABC {
+ class AddressBook;
+ class Addressee;
+}
+
+namespace Kopete
+{
+namespace UI
+{
+
+class KOPETE_EXPORT AddressBookSelectorWidget : public AddressBookSelectorWidget_Base
+{
+ Q_OBJECT
+public:
+ AddressBookSelectorWidget( QWidget *parent = 0, const char *name = 0 );
+ ~AddressBookSelectorWidget();
+ KABC::Addressee addressee();
+ /**
+ * sets the widget label message
+ * example: Please select a contact
+ * or, Choose a contact to delete
+ */
+ void setLabelMessage( const QString &msg );
+ /**
+ * pre-selects a contact
+ */
+ void selectAddressee( const QString &uid );
+ /**
+ * @return true if there is a contact selected
+ */
+ bool addresseeSelected();
+
+private:
+ KABC::AddressBook * m_addressBook;
+ KABC::Addressee m_addressee;
+
+protected slots:
+ void slotAddAddresseeClicked();
+ /**
+ * Utility function, populates the addressee list
+ */
+ void slotLoadAddressees();
+signals:
+ void addresseeListClicked( QListViewItem *addressee );
+ void addAddresseeClicked();
+};
+
+} // namespace UI
+} // namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/addressbookselectorwidget_base.ui b/kopete/libkopete/ui/addressbookselectorwidget_base.ui
new file mode 100644
index 00000000..d5e2e6f2
--- /dev/null
+++ b/kopete/libkopete/ui/addressbookselectorwidget_base.ui
@@ -0,0 +1,175 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AddressBookSelectorWidget_Base</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AddressBookSelectorWidget_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>596</width>
+ <height>572</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Select Contact</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer row="3" column="1">
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>405</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="3" column="0">
+ <property name="name">
+ <cstring>addAddresseeButton</cstring>
+ </property>
+ <property name="text">
+ <string>Create New Entr&amp;y...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Create a new entry in your address book</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lblHeader</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KListView" row="2" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Photo</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <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>Email</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>addresseeListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>10</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Select the contact you want to communicate with via Instant Messaging</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblSearch</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>S&amp;earch:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ <widget class="KListViewSearchLine">
+ <property name="name">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<includes>
+ <include location="global" impldecl="in declaration">klistviewsearchline.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kactivelabel.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>klistviewsearchline.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/addresseeitem.cpp b/kopete/libkopete/ui/addresseeitem.cpp
new file mode 100644
index 00000000..3888ee27
--- /dev/null
+++ b/kopete/libkopete/ui/addresseeitem.cpp
@@ -0,0 +1,63 @@
+/*
+ Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "addresseeitem.h"
+
+AddresseeItem::AddresseeItem( QListView *parent, const KABC::Addressee &addressee) :
+ KListViewItem( parent ),
+ mAddressee( addressee )
+{
+ //We can't save showphoto because we don't have a d pointer
+ KABC::Picture pic = mAddressee.photo();
+ if(!pic.isIntern())
+ pic = mAddressee.logo();
+ if(pic.isIntern())
+ {
+ QPixmap qpixmap( pic.data().scaleWidth(60) ); //60 pixels seems okay.. kmail uses 60 btw
+ setPixmap( Photo,qpixmap );
+ }
+
+ setText( Name, addressee.realName() );
+ setText( Email, addressee.preferredEmail() );
+}
+
+QString AddresseeItem::key( int column, bool ) const
+{
+ if (column == Email) {
+ QString value = text(Email);
+ QRegExp emailRe(QString::fromLatin1("<\\S*>"));
+ int match = emailRe.search(value);
+ if (match > -1)
+ value = value.mid(match + 1, emailRe.matchedLength() - 2);
+
+ return value.lower();
+ }
+
+ return text(column).lower();
+}
+
+
diff --git a/kopete/libkopete/ui/addresseeitem.h b/kopete/libkopete/ui/addresseeitem.h
new file mode 100644
index 00000000..b190fea6
--- /dev/null
+++ b/kopete/libkopete/ui/addresseeitem.h
@@ -0,0 +1,68 @@
+/*
+ This file is part of libkabc.
+ Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KABC_ADDRESSEEDIALOG_H
+#define KABC_ADDRESSEEDIALOG_H
+
+#include <qdict.h>
+
+#include <kdialogbase.h>
+#include <klineedit.h>
+#include <klistview.h>
+
+#include <kabc/addressbook.h>
+
+/**
+ @short Special ListViewItem
+*/
+class AddresseeItem : public KListViewItem
+{
+ public:
+
+ /**
+ Type of column
+ @li @p Name - Name in Addressee
+ @li @p Email - Email in Addressee
+ */
+ enum columns { Photo =0, Name = 1, Email = 2 };
+
+ /**
+ Constructor.
+
+ @param parent The parent listview.
+ @param addressee The associated addressee.
+ */
+ AddresseeItem( QListView *parent, const KABC::Addressee &addressee );
+
+ /**
+ Returns the addressee.
+ */
+ KABC::Addressee addressee() const { return mAddressee; }
+
+ /**
+ Method used by QListView to sort the items.
+ */
+ virtual QString key( int column, bool ascending ) const;
+
+ private:
+ KABC::Addressee mAddressee;
+};
+
+#endif
diff --git a/kopete/libkopete/ui/contactaddednotifydialog.cpp b/kopete/libkopete/ui/contactaddednotifydialog.cpp
new file mode 100644
index 00000000..abcd4c7e
--- /dev/null
+++ b/kopete/libkopete/ui/contactaddednotifydialog.cpp
@@ -0,0 +1,178 @@
+/*
+ Copyright (c) 2005 Olivier Goffart <ogoffart@ kde.org>
+
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "contactaddednotifydialog.h"
+
+
+#include <qvbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qstylesheet.h>
+#include <qapplication.h>
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+
+#include <kabc/addressee.h>
+
+#include "kopetegroup.h"
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+#include "kopeteprotocol.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "addressbooklinkwidget.h"
+#include "addressbookselectordialog.h"
+
+
+#include "contactaddednotifywidget.h"
+
+namespace Kopete {
+
+namespace UI {
+
+struct ContactAddedNotifyDialog::Private
+{
+ ContactAddedNotifyWidget *widget;
+ Account *account;
+ QString contactId;
+ QString addressbookId;
+};
+
+
+ContactAddedNotifyDialog::ContactAddedNotifyDialog(const QString& contactId,
+ const QString& contactNick, Kopete::Account *account, uint hide)
+ : KDialogBase( Global::mainWidget(), "ContactAddedNotify", /*modal=*/false,
+ i18n("Someone Has Added You"), Ok|Cancel )
+{
+
+ setWFlags(WDestructiveClose | getWFlags() );
+
+ d=new Private;
+ d->widget=new ContactAddedNotifyWidget(this);
+ setMainWidget(d->widget);
+
+ d->account=account;
+ d->contactId=contactId;
+ d->widget->m_label->setText(i18n("<qt><img src=\"kopete-account-icon:%1\" /> The contact <b>%2</b> has added you to his/her contactlist. (Account %3)</qt>")
+ .arg( KURL::encode_string( account->protocol()->pluginId() ) + QString::fromLatin1(":")
+ + KURL::encode_string( account->accountId() ) ,
+ contactNick.isEmpty() ? contactId : contactNick + QString::fromLatin1(" < ") + contactId + QString::fromLatin1(" >") ,
+ account->accountLabel() ) );
+ if( hide & InfoButton)
+ d->widget->m_infoButton->hide() ;
+ if( hide & AuthorizeCheckBox )
+ {
+ d->widget->m_authorizeCb->hide();
+ d->widget->m_authorizeCb->setChecked(false);
+ }
+ if( hide & AddCheckBox )
+ {
+ d->widget->m_addCb->hide();
+ d->widget->m_addCb->setChecked(false);
+ }
+ if( hide & AddGroupBox )
+ d->widget->m_contactInfoBox->hide();
+
+ // Populate the groups list
+ Kopete::GroupList groups=Kopete::ContactList::self()->groups();
+ for( Kopete::Group *it = groups.first(); it; it = groups.next() )
+ {
+ QString groupname = it->displayName();
+ if ( it->type() == Group::Normal && !groupname.isEmpty() )
+ {
+ d->widget->m_groupList->insertItem(groupname);
+ }
+ }
+ d->widget->m_groupList->setCurrentText(QString::null); //default to top-level
+
+ connect( d->widget->widAddresseeLink, SIGNAL( addresseeChanged( const KABC::Addressee& ) ), this, SLOT( slotAddresseeSelected( const KABC::Addressee& ) ) );
+ connect( d->widget->m_infoButton, SIGNAL( clicked() ), this, SLOT( slotInfoClicked() ) );
+
+ connect( this, SIGNAL(okClicked()) , this , SLOT(slotFinished()));
+
+}
+
+
+ContactAddedNotifyDialog::~ContactAddedNotifyDialog()
+{
+ delete d;
+}
+
+bool ContactAddedNotifyDialog::added() const
+{
+ return d->widget->m_addCb->isChecked();
+}
+
+bool ContactAddedNotifyDialog::authorized() const
+{
+ return d->widget->m_authorizeCb->isChecked();
+}
+
+QString ContactAddedNotifyDialog::displayName() const
+{
+ return d->widget->m_displayNameEdit->text();
+}
+
+Group *ContactAddedNotifyDialog::group() const
+{
+ QString grpName=d->widget->m_groupList->currentText();
+ if(grpName.isEmpty())
+ return Group::topLevel();
+
+ return ContactList::self()->findGroup( grpName );
+}
+
+MetaContact *ContactAddedNotifyDialog::addContact() const
+{
+ if(!added() || !d->account)
+ return 0L;
+
+ MetaContact *metacontact=d->account->addContact(d->contactId, displayName(), group());
+ if(!metacontact)
+ return 0L;
+
+ metacontact->setMetaContactId(d->addressbookId);
+
+ return metacontact;
+}
+
+void ContactAddedNotifyDialog::slotAddresseeSelected( const KABC::Addressee & addr )
+{
+ if ( !addr.isEmpty() )
+ {
+ d->addressbookId = addr.uid();
+ }
+}
+
+void ContactAddedNotifyDialog::slotInfoClicked()
+{
+ emit infoClicked(d->contactId);
+}
+
+void ContactAddedNotifyDialog::slotFinished()
+{
+ emit applyClicked(d->contactId);
+}
+
+
+
+} // namespace UI
+} // namespace Kopete
+#include "contactaddednotifydialog.moc"
diff --git a/kopete/libkopete/ui/contactaddednotifydialog.h b/kopete/libkopete/ui/contactaddednotifydialog.h
new file mode 100644
index 00000000..96f8844c
--- /dev/null
+++ b/kopete/libkopete/ui/contactaddednotifydialog.h
@@ -0,0 +1,175 @@
+/*
+ Copyright (c) 2005 Olivier Goffart <ogoffart@ kde.org>
+
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef KOPETE_UICONTACTADDEDNOTIFYDIALOG_H
+#define KOPETE_UICONTACTADDEDNOTIFYDIALOG_H
+
+#include <kdialogbase.h>
+#include "kopete_export.h"
+
+namespace KABC {
+ class Addressee;
+}
+
+namespace Kopete {
+
+class Group;
+class Account;
+class MetaContact;
+
+namespace UI {
+
+/**
+ * @brief Dialog which is shown when a contact added you in the contactlist.
+ *
+ * This dialog asks the user to give authorization for the addition to the
+ * person who added the user and also asks the user if the contact who you've
+ * received the notification for should be added to the user's contact list
+ *
+ * example of usage
+ * @code
+
+ Kopete::UI::ContactAddedNotifyDialog *dialog =
+ new ContactAddedNotifyDialog(contactId, QString::null,account);
+ QObject::connect(dialog,SIGNAL(applyClicked(const QString&)),this,SLOT(contactAddedDialogApplied()));
+ QObject::connect(dialog,SIGNAL(infoClicked(const QString&)),this,SLOT(contactAddedDialogInfo()));
+ dialog->show();
+
+ * @endcode
+ *
+ * and in your contactAddedDialogApplied slot
+ * @code
+ const Kopete::UI::ContactAddedNotifyDialog *dialog =
+ dynamic_cast<const Kopete::UI::ContactAddedNotifyDialog *>(sender());
+ if(!dialog)
+ return;
+ if(dialog->authorized())
+ socket->authorize(contactId);
+ if(dialog->added())
+ dialog->addContact();
+ * @endcode
+ *
+ * Note that you can also use exec() but this is not recommended
+ *
+ * @author Olivier Goffart
+ * @since 0.11
+ */
+class KOPETE_EXPORT ContactAddedNotifyDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ /**
+ * All widget in the dialog that may be hidden.
+ */
+ enum HideWidget
+ {
+ InfoButton = 0x01, /**< the button which ask for more info about the contact */
+ AuthorizeCheckBox = 0x02, /**< the checkbox which ask for authorize the contact */
+ AddCheckBox = 0x04, /**< the checkbox which ask if the contact should be added */
+ AddGroupBox = 0x08 /**< all the widget about metacontact properties */
+ };
+
+ /**
+ * @brief Constructor
+ *
+ * The dialog is by default not modal, and will delete itself when closed
+ *
+ * @param contactId the contactId of the contact which just added the user
+ * @param contactNick the nickname of the contact if available.
+ * @param account is used to display the account icon and informaiton about the account
+ * @param hide a bitmask of HideWidget used to hide some widget. By default, everything is shown.
+ *
+ */
+ ContactAddedNotifyDialog(const QString& contactId, const QString& contactNick=QString::null,
+ Kopete::Account *account=0L, uint hide=0x00);
+
+ /**
+ * @brief Destructor
+ */
+ ~ContactAddedNotifyDialog();
+
+ /**
+ * @brief return if the user has checked the "authorize" checkbox
+ * @return true if the authorize checkbox is checked, false otherwise
+ */
+ bool authorized() const;
+
+ /**
+ * @brief return if the user has checked the "add" checkbox
+ * @return true if the add checkbox is checked, false otherwise
+ */
+ bool added() const;
+
+ /**
+ * @brief return the display name the user has entered
+ */
+ QString displayName() const;
+
+ /**
+ * @brief return the group the user has selected
+ *
+ * If the user has entered a group which doesn't exist yet, it will be created now
+ */
+ Group* group() const;
+
+public slots:
+
+ /**
+ * @brief create a metacontact.
+ *
+ * This function only works if the add checkbox is checked, otherwise,
+ * it will return 0L.
+ *
+ * it uses the Account::addContact function to add the contact
+ *
+ * @return the new metacontact created, or 0L if the operation failed.
+ */
+ MetaContact *addContact() const;
+
+signals:
+ /**
+ * @brief the dialog has been applied
+ * @param contactId is the id of the contact passed in the constructor.
+ */
+ void applyClicked(const QString &contactId);
+
+ /**
+ * @brief the button "info" has been pressed
+ * If you haven't hidden the more info button, you should connect this
+ * signal to a slot which show a dialog with more info about the
+ * contact.
+ *
+ * hint: you can use sender() as parent of the new dialog
+ * @param contactId is the id of the contact passed in the constructor.
+ */
+ void infoClicked(const QString &contactId);
+
+
+private slots:
+ void slotAddresseeSelected( const KABC::Addressee &);
+ void slotInfoClicked();
+ void slotFinished();
+
+private:
+ struct Private;
+ Private *d;
+};
+
+
+
+} // namespace UI
+} // namespace Kopete
+#endif
diff --git a/kopete/libkopete/ui/contactaddednotifywidget.ui b/kopete/libkopete/ui/contactaddednotifywidget.ui
new file mode 100644
index 00000000..47d3f070
--- /dev/null
+++ b/kopete/libkopete/ui/contactaddednotifywidget.ui
@@ -0,0 +1,260 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ContactAddedNotifyWidget</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form2</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>466</width>
+ <height>342</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_label</cstring>
+ </property>
+ <property name="text">
+ <string>The contact XXX added you in his contactlist</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>151</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>m_infoButton</cstring>
+ </property>
+ <property name="text">
+ <string>Read More Info About This Contact</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_authorizeCb</cstring>
+ </property>
+ <property name="text">
+ <string>Authorize this contact to see my status</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_addCb</cstring>
+ </property>
+ <property name="text">
+ <string>Add this contact in my contactlist</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_contactInfoBox</cstring>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Display name:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The display name of the contact. Leave it empty to use the contact nickname</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the contact display name. This is how the contact will appears in the contactlist.
+Leave it empty if you want to see the contact nickname as display name.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_displayNameEdit</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The display name of the contact. Leave it empty to use the contact nickname</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the contact display name. This is how the contact will appears in the contactlist.
+Leave it empty if you want to see the contact nickname as display name.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>In the group:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the group where the contact should be added. Leave it empty to add it in the top level group.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>m_groupList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the group where the contact should be added. Leave it empty to add it in the top level group.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Addressbook link:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="Kopete::UI::AddressBookLinkWidget">
+ <property name="name">
+ <cstring>widAddresseeLink</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>m_addCb</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_contactInfoBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<customwidgets>
+ <customwidget>
+ <class>Kopete::UI::AddressBookLinkWidget</class>
+ <header>addressbooklinkwidget.h</header>
+ </customwidget>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/editaccountwidget.cpp b/kopete/libkopete/ui/editaccountwidget.cpp
new file mode 100644
index 00000000..7428a8ad
--- /dev/null
+++ b/kopete/libkopete/ui/editaccountwidget.cpp
@@ -0,0 +1,49 @@
+/*
+ editaccountwidget.cpp - Kopete Account Widget
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "editaccountwidget.h"
+
+class KopeteEditAccountWidgetPrivate
+{
+public:
+ Kopete::Account *account;
+};
+
+KopeteEditAccountWidget::KopeteEditAccountWidget( Kopete::Account *account )
+{
+ d = new KopeteEditAccountWidgetPrivate;
+ d->account = account;
+}
+
+KopeteEditAccountWidget::~KopeteEditAccountWidget()
+{
+ delete d;
+}
+
+Kopete::Account * KopeteEditAccountWidget::account() const
+{
+ return d->account;
+}
+
+void KopeteEditAccountWidget::setAccount( Kopete::Account *account )
+{
+ d->account = account;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/editaccountwidget.h b/kopete/libkopete/ui/editaccountwidget.h
new file mode 100644
index 00000000..533c90ff
--- /dev/null
+++ b/kopete/libkopete/ui/editaccountwidget.h
@@ -0,0 +1,104 @@
+/*
+ editaccountwidget.h - Kopete Account Widget
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef EDITACCOUNTWIDGET_H
+#define EDITACCOUNTWIDGET_H
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+class Account;
+}
+
+class KopeteEditAccountWidgetPrivate;
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ *
+ * This class is used by the protocol plugins to add specific protocol fields in the add account wizard,
+ * or in the account preferences. If the given account is 0L, then you will have to create a new account
+ * in @ref apply().
+ *
+ * Each protocol has to subclass this class, and the protocol's edit account page MUST inherits from
+ * QWidget too.
+ *
+ * We suggest to put at least these fields in the page:
+ *
+ * - The User login, or the accountId. you can retrieve it from @ref Kopete::Account::accountId(). This
+ * field has to be marked as ReadOnly or shown as a label if the account already exists. Remember
+ * that accountId should be constant after account creation!
+ *
+ * - The password, and the remember password checkboxes.
+ *
+ * - The auto connect checkbox: use @ref Kopete::Account::excludeConnect() and
+ * @ref Kopete::Account::setExcludeConnect() to get/set this flag.
+ *
+ * You may add other custom fields, e.g. the nickname. To save or retrieve these settings use
+ * @ref Kopete::ContactListElement::pluginData() with your protocol as plugin.
+ */
+class KOPETE_EXPORT KopeteEditAccountWidget
+{
+public:
+ /**
+ * Constructor.
+ *
+ * If 'account' is 0L we are in the 'add account wizard', otherwise
+ * we are editing an existing account.
+ */
+ KopeteEditAccountWidget( Kopete::Account *account );
+
+ /**
+ * Destructor
+ */
+ virtual ~KopeteEditAccountWidget();
+
+ /**
+ * This method must be reimplemented.
+ * It does the same as @ref AddContactPage::validateData()
+ */
+ virtual bool validateData() = 0;
+
+ /**
+ * Create a new account if we are in the 'add account wizard',
+ * otherwise update the existing account.
+ */
+ virtual Kopete::Account *apply() = 0;
+
+protected:
+ /**
+ * Get a pointer to the Kopete::Account passed to the constructor.
+ * You can modify it any way you like, just don't delete the object.
+ */
+ Kopete::Account * account() const;
+
+ /**
+ * Set the account
+ */
+ // FIXME: Is it possible to make the API not require this? A const account
+ // in this widget seems a lot cleaner to me - Martijn
+ void setAccount( Kopete::Account *account );
+
+private:
+ KopeteEditAccountWidgetPrivate *d;
+};
+
+// vim: set noet ts=4 sts=4 sw=4:
+
+#endif
+
diff --git a/kopete/libkopete/ui/fileconfirmbase.ui b/kopete/libkopete/ui/fileconfirmbase.ui
new file mode 100644
index 00000000..3d697b0f
--- /dev/null
+++ b/kopete/libkopete/ui/fileconfirmbase.ui
@@ -0,0 +1,151 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>FileConfirmBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>FileConfirmBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>410</width>
+ <height>307</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>A User Would Like to Send You a File</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>3</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>A user is trying to send you a file. The file will only be downloaded if you accept this dialog. If you do not wish to receive it, please click 'Refuse'. This file will never be executed by Kopete at any point during or after the transfer.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>From:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>File name:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="6" column="1">
+ <property name="name">
+ <cstring>m_saveto</cstring>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="6" column="2">
+ <property name="name">
+ <cstring>cmdBrowse</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Browse...</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>TextLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Size:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Description:</string>
+ </property>
+ </widget>
+ <widget class="QTextEdit" row="3" column="1" rowspan="2" colspan="2">
+ <property name="name">
+ <cstring>m_description</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="4" column="0">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>TextLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Save to:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_from</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_filename</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_size</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/kopete.widgets b/kopete/libkopete/ui/kopete.widgets
new file mode 100644
index 00000000..7c441d0f
--- /dev/null
+++ b/kopete/libkopete/ui/kopete.widgets
@@ -0,0 +1,24 @@
+[Global]
+PluginName=KopeteWidgets
+Includes=kinstance.h
+Init=new KInstance("kopetewidgets");
+
+[Kopete::UI::ListView::ListView]
+ToolTip=List View (Kopete)
+WhatsThis=A component capable list view widget.
+IncludeFile=kopetelistview.h
+Group=Views (Kopete)
+
+[Kopete::UI::ListView::SearchLine]
+ToolTip=List View Search Line (Kopete)
+WhatsThis=Search line able to use Kopete custom list View.
+IncludeFile=kopetelistviewsearchline.h
+ConstructorArgs=(parent, 0, name)
+Group=Input (Kopete)
+
+[Kopete::UI::AddressBookLinkWidget]
+ToolTip=Address Book Link Widget (Kopete)
+WhatsThis=KABC::Addressee display/selector
+IncludeFile=addressbooklinkwidget.h
+ConstructorArgs=(parent, name)
+Group=Input (Kopete)
diff --git a/kopete/libkopete/ui/kopeteawaydialogbase.ui b/kopete/libkopete/ui/kopeteawaydialogbase.ui
new file mode 100644
index 00000000..783fe4da
--- /dev/null
+++ b/kopete/libkopete/ui/kopeteawaydialogbase.ui
@@ -0,0 +1,85 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>KopeteAwayDialog_Base</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopeteAwayDialog_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>322</width>
+ <height>192</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Please specify an away message, or choose a predefined one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>txtOneShot</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>cmbHistory</cstring>
+ </property>
+ <property name="editable">
+ <bool>false</bool>
+ </property>
+ <property name="insertionPolicy">
+ <enum>AtCurrent</enum>
+ </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>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/libkopete/ui/kopetecontactaction.cpp b/kopete/libkopete/ui/kopetecontactaction.cpp
new file mode 100644
index 00000000..d02c2ff2
--- /dev/null
+++ b/kopete/libkopete/ui/kopetecontactaction.cpp
@@ -0,0 +1,54 @@
+/*
+ kopetecontactaction.cpp - KAction for selecting a Kopete::Contact
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetecontactaction.h"
+
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopeteonlinestatus.h"
+
+KopeteContactAction::KopeteContactAction( Kopete::Contact *contact, const QObject *receiver,
+ const char *slot, KAction *parent )
+: KAction( contact->metaContact()->displayName(), QIconSet( contact->onlineStatus().iconFor( contact ) ), KShortcut(),
+ parent, contact->contactId().latin1() )
+{
+ m_contact = contact;
+
+ connect( this, SIGNAL( activated() ), SLOT( slotContactActionActivated() ) );
+ connect( this, SIGNAL( activated( Kopete::Contact * ) ), receiver, slot );
+}
+
+KopeteContactAction::~KopeteContactAction()
+{
+}
+
+void KopeteContactAction::slotContactActionActivated()
+{
+ emit activated( m_contact );
+}
+
+Kopete::Contact * KopeteContactAction::contact() const
+{
+ return m_contact;
+}
+
+
+#include "kopetecontactaction.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
+
diff --git a/kopete/libkopete/ui/kopetecontactaction.h b/kopete/libkopete/ui/kopetecontactaction.h
new file mode 100644
index 00000000..bb9d9f76
--- /dev/null
+++ b/kopete/libkopete/ui/kopetecontactaction.h
@@ -0,0 +1,61 @@
+/*
+ kopetecontactaction.cpp - KAction for selecting a Kopete::Contact
+
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __kopetecontactaction_h__
+#define __kopetecontactaction_h__
+
+#include <kaction.h>
+#include "kopete_export.h"
+
+namespace Kopete
+{
+class Contact;
+}
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ */
+class KOPETE_EXPORT KopeteContactAction : public KAction
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a new KopeteContactAction
+ */
+ KopeteContactAction( Kopete::Contact *contact, const QObject* receiver, const char* slot, KAction* parent );
+ ~KopeteContactAction();
+
+ Kopete::Contact * contact() const;
+
+signals:
+ /**
+ * Overloaded signal to get the selected contact
+ */
+ void activated( Kopete::Contact *action );
+
+private slots:
+ void slotContactActionActivated();
+
+private:
+ Kopete::Contact *m_contact;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/kopetefileconfirmdialog.cpp b/kopete/libkopete/ui/kopetefileconfirmdialog.cpp
new file mode 100644
index 00000000..01036a05
--- /dev/null
+++ b/kopete/libkopete/ui/kopetefileconfirmdialog.cpp
@@ -0,0 +1,117 @@
+/*
+ kopetefileconfirmdialog.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtextedit.h>
+
+#include <klineedit.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+
+//#include "kopetetransfermanager.h"
+#include "fileconfirmbase.h"
+#include "kopetefileconfirmdialog.h"
+
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+
+KopeteFileConfirmDialog::KopeteFileConfirmDialog(const Kopete::FileTransferInfo &info,const QString& description,QWidget *parent, const char *name )
+: KDialogBase( parent, name, false, i18n( "A User Would Like to Send You a File" ),
+ KDialogBase::User1 | KDialogBase::User2, KDialogBase::User1, true, i18n( "&Refuse" ), i18n( "&Accept" ) ),
+ m_info( info )
+{
+ setWFlags( WDestructiveClose );
+ m_emited=false;
+
+ m_view=new FileConfirmBase(this, "FileConfirmView");
+ m_view->m_from->setText( info.contact()->metaContact()->displayName() + QString::fromLatin1( " <" ) +
+ info.contact()->contactId() + QString::fromLatin1( "> " ) );
+ m_view->m_size->setText( KGlobal::locale()->formatNumber( long( info.size() ), 0 ) );
+ m_view->m_description->setText( description );
+ m_view->m_filename->setText( info.file() );
+
+ KGlobal::config()->setGroup("File Transfer");
+ const QString defaultPath=KGlobal::config()->readEntry("defaultPath" , QDir::homeDirPath() );
+ m_view->m_saveto->setText(defaultPath + QString::fromLatin1( "/" ) + info.file() );
+
+ setMainWidget(m_view);
+
+ connect(m_view->cmdBrowse, SIGNAL(clicked()), this, SLOT(slotBrowsePressed()));
+}
+
+KopeteFileConfirmDialog::~KopeteFileConfirmDialog()
+{
+}
+
+void KopeteFileConfirmDialog::slotBrowsePressed()
+{
+ QString saveFileName = KFileDialog::getSaveFileName( m_view->m_saveto->text(), QString::fromLatin1( "*" ), 0L , i18n( "File Transfer" ) );
+ if ( !saveFileName.isNull())
+ {
+ m_view->m_saveto->setText(saveFileName);
+ }
+}
+
+void KopeteFileConfirmDialog::slotUser2()
+{
+ m_emited=true;
+ KURL url(m_view->m_saveto->text());
+ if(url.isValid() && url.isLocalFile() )
+ {
+ const QString directory=url.directory();
+ if(!directory.isEmpty())
+ {
+ KGlobal::config()->setGroup("File Transfer");
+ KGlobal::config()->writeEntry("defaultPath" , directory );
+ }
+
+ if(QFile(m_view->m_saveto->text()).exists())
+ {
+ int ret=KMessageBox::warningContinueCancel(this, i18n("The file '%1' already exists.\nDo you want to overwrite it ?").arg(m_view->m_saveto->text()) ,
+ i18n("Overwrite File") , KStdGuiItem::save());
+ if(ret==KMessageBox::Cancel)
+ return;
+ }
+
+ emit accepted(m_info,m_view->m_saveto->text());
+ close();
+ }
+ else
+ KMessageBox::queuedMessageBox (this, KMessageBox::Sorry, i18n("You must provide a valid local filename") );
+}
+
+void KopeteFileConfirmDialog::slotUser1()
+{
+ m_emited=true;
+ emit refused(m_info);
+ close();
+}
+
+void KopeteFileConfirmDialog::closeEvent( QCloseEvent *e)
+{
+ if(!m_emited)
+ {
+ m_emited=true;
+ emit refused(m_info);
+ }
+ KDialogBase::closeEvent(e);
+}
+
+#include "kopetefileconfirmdialog.moc"
+
diff --git a/kopete/libkopete/ui/kopetefileconfirmdialog.h b/kopete/libkopete/ui/kopetefileconfirmdialog.h
new file mode 100644
index 00000000..20d58d51
--- /dev/null
+++ b/kopete/libkopete/ui/kopetefileconfirmdialog.h
@@ -0,0 +1,57 @@
+/*
+ kopetefileconfirmdialog.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEFILECONFIRMDIALOG_H
+#define KOPETEFILECONFIRMDIALOG_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include "kopetetransfermanager.h"
+
+class FileConfirmBase;
+
+/**
+ *@author Olivier Goffart
+ */
+
+class KopeteFileConfirmDialog : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ KopeteFileConfirmDialog(const Kopete::FileTransferInfo &info,const QString& description=QString::null, QWidget *parent=0, const char* name=0);
+ ~KopeteFileConfirmDialog();
+
+private:
+ FileConfirmBase* m_view;
+ Kopete::FileTransferInfo m_info;
+ bool m_emited;
+
+public slots:
+ void slotBrowsePressed();
+
+protected slots:
+ virtual void slotUser2();
+ virtual void slotUser1();
+ virtual void closeEvent( QCloseEvent *e);
+
+signals:
+ void accepted(const Kopete::FileTransferInfo &info, const QString &filename);
+ void refused(const Kopete::FileTransferInfo &info);
+};
+
+#endif
diff --git a/kopete/libkopete/ui/kopetelistview.cpp b/kopete/libkopete/ui/kopetelistview.cpp
new file mode 100644
index 00000000..594f0920
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistview.cpp
@@ -0,0 +1,215 @@
+/*
+ kopetelistview.cpp - List View providing support for ListView::Items
+
+ Copyright (c) 2004 by Engin AYDOGAN <engin@bzzzt.biz>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetelistview.h"
+#include "kopetelistviewitem.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+#include "kopeteprefs.h"
+
+#include <qapplication.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qstyle.h>
+
+#include <utility>
+#include <memory>
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+/*
+ Custom QToolTip for the list view.
+ The decision whether or not to show tooltips is taken in
+ maybeTip(). See also the QListView sources from Qt itself.
+ Delegates to the list view items.
+*/
+class ToolTip : public QToolTip
+{
+public:
+ ToolTip( QWidget *parent, ListView *lv );
+ virtual ~ToolTip();
+
+ void maybeTip( const QPoint &pos );
+
+private:
+ ListView *m_listView;
+};
+
+ToolTip::ToolTip( QWidget *parent, ListView *lv )
+ : QToolTip( parent )
+{
+ m_listView = lv;
+}
+
+ToolTip::~ToolTip()
+{
+}
+
+void ToolTip::maybeTip( const QPoint &pos )
+{
+ if( !parentWidget() || !m_listView )
+ return;
+
+ if( Item *item = dynamic_cast<Item*>( m_listView->itemAt( pos ) ) )
+ {
+ QRect itemRect = m_listView->itemRect( item );
+
+ uint leftMargin = m_listView->treeStepSize() *
+ ( item->depth() + ( m_listView->rootIsDecorated() ? 1 : 0 ) ) +
+ m_listView->itemMargin();
+
+ uint xAdjust = itemRect.left() + leftMargin;
+ uint yAdjust = itemRect.top();
+ QPoint relativePos( pos.x() - xAdjust, pos.y() - yAdjust );
+
+ std::pair<QString,QRect> toolTip = item->toolTip( relativePos );
+ if ( toolTip.first.isEmpty() )
+ return;
+
+ toolTip.second.moveBy( xAdjust, yAdjust );
+// kdDebug( 14000 ) << k_funcinfo << "Adding tooltip: itemRect: "
+// << toolTip.second << ", tooltip: " << toolTip.first << endl;
+ tip( toolTip.second, toolTip.first );
+ }
+}
+
+struct ListView::Private
+{
+ QTimer sortTimer;
+ std::auto_ptr<ToolTip> toolTip;
+ //! C-tor
+ Private() {}
+};
+
+ListView::ListView( QWidget *parent, const char *name )
+ : KListView( parent, name ), d( new Private )
+{
+ connect( &d->sortTimer, SIGNAL( timeout() ), this, SLOT( slotSort() ) );
+
+ // We have our own tooltips, don't use the default QListView ones
+ setShowToolTips( false );
+ d->toolTip.reset( new ToolTip( viewport(), this ) );
+
+ connect( this, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ),
+ SLOT( slotContextMenu( KListView *, QListViewItem *, const QPoint & ) ) );
+ connect( this, SIGNAL( doubleClicked( QListViewItem * ) ),
+ SLOT( slotDoubleClicked( QListViewItem * ) ) );
+
+ // set up flags for nicer painting
+ clearWFlags( WStaticContents );
+ setWFlags( WNoAutoErase );
+
+ // clear the appropriate flags from the viewport - qt docs say we have to mask
+ // these flags out of the QListView to make weirdly painted list items work, but
+ // that doesn't do the job. masking them out of the viewport does.
+// class MyWidget : public QWidget { public: using QWidget::clearWFlags; };
+// static_cast<MyWidget*>( viewport() )->clearWFlags( WStaticContents );
+// static_cast<MyWidget*>( viewport() )->setWFlags( WNoAutoErase );
+
+ // The above causes compiler errors with the (broken) native TRU64 and IRIX compilers.
+ // This should make it compile for both platforms and still seems to work.
+ // This is, of course, a nasty hack, but it works, so...
+ static_cast<ListView*>(viewport())->clearWFlags( WStaticContents );
+ static_cast<ListView*>(viewport())->setWFlags( WNoAutoErase );
+}
+
+ListView::~ListView()
+{
+ delete d;
+}
+
+void ListView::slotDoubleClicked( QListViewItem *item )
+{
+ kdDebug( 14000 ) << k_funcinfo << endl;
+
+ if ( item )
+ setOpen( item, !isOpen( item ) );
+}
+
+void ListView::slotContextMenu( KListView * /*listview*/,
+ QListViewItem *item, const QPoint &/*point*/ )
+{
+ if ( item && !item->isSelected() )
+ {
+ clearSelection();
+ item->setSelected( true );
+ }
+ if ( !item )
+ clearSelection();
+
+// if( Item *myItem = dynamic_cast<Item*>( item ) )
+ ;// TODO: myItem->contextMenu( point );
+}
+
+void ListView::setShowTreeLines( bool bShowAsTree )
+{
+ if ( bShowAsTree )
+ {
+ setRootIsDecorated( true );
+ setTreeStepSize( 20 );
+ }
+ else
+ {
+ setRootIsDecorated( false );
+ setTreeStepSize( 0 );
+ }
+ // TODO: relayout all items. their width may have changed, but they won't know about it.
+}
+
+/* This is a small hack ensuring that only F2 triggers inline
+ * renaming. Won't win a beauty award, but whoever wrote it thinks
+ * relying on the fact that QListView intercepts and processes the
+ * F2 event through this event filter is sorta safe.
+ *
+ * Also use enter to execute the item since executed is not usually
+ * called when enter is pressed.
+ */
+void ListView::keyPressEvent( QKeyEvent *e )
+{
+ QListViewItem *item = currentItem();
+ if ( (e->key() == Qt::Key_F2) && item && item->isVisible() )
+ rename( item, 0 );
+ else if ( (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) && item && item->isVisible() )
+ {
+ // must provide a point within the item; emitExecute checks for this
+ QPoint p = viewport()->mapToGlobal(itemRect(item).center());
+ emitExecute( currentItem(), p, 0 );
+ }
+ else
+ KListView::keyPressEvent(e);
+}
+
+void ListView::delayedSort()
+{
+ if ( !d->sortTimer.isActive() )
+ d->sortTimer.start( 500, true );
+}
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+#include "kopetelistview.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopetelistview.h b/kopete/libkopete/ui/kopetelistview.h
new file mode 100644
index 00000000..8b2c579b
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistview.h
@@ -0,0 +1,74 @@
+/*
+ kopetelistview.h - List View providing extra support for ListView::Items
+
+ Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_LISTVIEW_H
+#define KOPETE_LISTVIEW_H
+
+#include <klistview.h>
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+/**
+ * @author Engin AYDOGAN <engin@bzzzt.biz>
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class ListView : public KListView
+{
+ Q_OBJECT
+
+public:
+ ListView( QWidget *parent = 0, const char *name = 0 );
+ ~ListView();
+
+ /**
+ * Schedule a delayed sort operation. Sorts will be withheld for at most
+ * half a second, after which they will be performed. This way multiple
+ * sort calls can be safely bundled without writing complex code to avoid
+ * the sorts entirely.
+ */
+ void delayedSort();
+
+ /**
+ * Set whether to show the lines and +/- boxes in the tree
+ */
+ void setShowTreeLines( bool bShowAsTree );
+
+public slots:
+ /**
+ * Calls QListView::sort()
+ */
+ void slotSort() { sort(); }
+protected:
+ virtual void keyPressEvent( QKeyEvent *e );
+private slots:
+ void slotContextMenu(KListView*,QListViewItem *item, const QPoint &point );
+ void slotDoubleClicked( QListViewItem *item );
+private:
+ struct Private;
+ Private *d;
+};
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopetelistviewitem.cpp b/kopete/libkopete/ui/kopetelistviewitem.cpp
new file mode 100644
index 00000000..fda2ff4c
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistviewitem.cpp
@@ -0,0 +1,1314 @@
+/*
+ kopetelistviewitem.cpp - Kopete's modular QListViewItems
+
+ Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "kopetecontact.h"
+#include "kopetelistviewitem.h"
+#include "kopeteemoticons.h"
+#include "kopeteonlinestatus.h"
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kstringhandler.h>
+
+#include <qapplication.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qptrlist.h>
+#include <qrect.h>
+#include <qtimer.h>
+#include <qheader.h>
+#include <qstyle.h>
+
+#ifdef HAVE_XRENDER
+# include <X11/Xlib.h>
+# include <X11/extensions/Xrender.h>
+#endif
+
+#include <limits.h>
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+// ComponentBase --------
+
+class ComponentBase::Private
+{
+public:
+ QPtrList<Component> components;
+};
+
+ComponentBase::ComponentBase()
+ : d( new Private )
+{
+}
+
+ComponentBase::~ComponentBase()
+{
+ d->components.setAutoDelete( true );
+ delete d;
+}
+
+uint ComponentBase::components() { return d->components.count(); }
+Component *ComponentBase::component( uint n ) { return d->components.at( n ); }
+
+Component *ComponentBase::componentAt( const QPoint &pt )
+{
+ for ( uint n = 0; n < components(); ++n )
+ {
+ if ( component( n )->rect().contains( pt ) )
+ {
+ if ( Component *comp = component( n )->componentAt( pt ) )
+ return comp;
+ return component( n );
+ }
+ }
+ return 0;
+}
+
+void ComponentBase::componentAdded( Component *component )
+{
+ d->components.append( component );
+}
+
+void ComponentBase::componentRemoved( Component *component )
+{
+ //TODO: make sure the component is in d->components once and only once.
+ // if not, the situation is best referred to as 'very very broken indeed'.
+ d->components.remove( component );
+}
+
+void ComponentBase::clear()
+{
+ /* I'm switching setAutoDelete back and forth instead of turning it
+ * on permenantly, because original author of this class set it to
+ * auto delete in the dtor, that might have a reason that I can't
+ * imagine right now */
+ bool tmp = d->components.autoDelete();
+ d->components.setAutoDelete( true );
+ d->components.clear();
+ d->components.setAutoDelete( tmp );
+}
+
+void ComponentBase::componentResized( Component * )
+{
+}
+
+std::pair<QString,QRect> ComponentBase::toolTip( const QPoint &relativePos )
+{
+ for ( uint n = 0; n < components(); ++n )
+ if ( component( n )->rect().contains( relativePos ) )
+ return component( n )->toolTip( relativePos );
+
+ return std::make_pair( QString::null, QRect() );
+}
+
+void ComponentBase::updateAnimationPosition( int p, int s )
+{
+ for ( uint n = 0; n < components(); ++n )
+ {
+ Component *comp = component( n );
+ QRect start = comp->startRect();
+ QRect target = comp->targetRect();
+ QRect rc( start.left() + ((target.left() - start.left()) * p) / s,
+ start.top() + ((target.top() - start.top()) * p) / s,
+ start.width() + ((target.width() - start.width()) * p) / s,
+ start.height() + ((target.height() - start.height()) * p) / s );
+ comp->setRect( rc );
+ comp->updateAnimationPosition( p, s );
+ }
+}
+
+// Component --------
+
+class Component::Private
+{
+public:
+ Private( ComponentBase *parent )
+ : parent( parent ), minWidth( 0 ), minHeight( 0 )
+ , growHoriz( false ), growVert( false )
+ , tipSource( 0 )
+ {
+ }
+ ComponentBase *parent;
+ QRect rect;
+ QRect startRect, targetRect;
+ int minWidth, minHeight;
+ bool growHoriz, growVert;
+ bool show; /** @since 23-03-2005 */
+ ToolTipSource *tipSource;
+};
+
+Component::Component( ComponentBase *parent )
+ : d( new Private( parent ) )
+{
+ d->parent->componentAdded( this );
+ d->show = true;
+}
+
+int Component::RTTI = Rtti_Component;
+
+Component::~Component()
+{
+ d->parent->componentRemoved( this );
+ delete d;
+}
+
+
+void Component::hide()
+{
+ d->show = false;
+}
+
+void Component::show()
+{
+ d->show = true;
+}
+
+bool Component::isShown()
+{
+ return d->show;
+}
+
+bool Component::isHidden()
+{
+ return !d->show;
+}
+
+void Component::setToolTipSource( ToolTipSource *source )
+{
+ d->tipSource = source;
+}
+
+std::pair<QString,QRect> Component::toolTip( const QPoint &relativePos )
+{
+ if ( !d->tipSource )
+ return ComponentBase::toolTip( relativePos );
+
+ QRect rc = rect();
+ QString result = (*d->tipSource)( this, relativePos, rc );
+ return std::make_pair(result, rc);
+}
+
+QRect Component::rect() { return d->rect; }
+QRect Component::startRect() { return d->startRect; }
+QRect Component::targetRect() { return d->targetRect; }
+
+int Component::minWidth() { return d->minWidth; }
+int Component::minHeight() { return d->minHeight; }
+int Component::widthForHeight( int ) { return minWidth(); }
+int Component::heightForWidth( int ) { return minHeight(); }
+
+bool Component::setMinWidth( int width )
+{
+ if ( d->minWidth == width ) return false;
+ d->minWidth = width;
+
+ d->parent->componentResized( this );
+ return true;
+}
+bool Component::setMinHeight( int height )
+{
+ if ( d->minHeight == height ) return false;
+ d->minHeight = height;
+
+ d->parent->componentResized( this );
+ return true;
+}
+
+void Component::layout( const QRect &newRect )
+{
+ if ( rect().isNull() )
+ d->startRect = QRect( newRect.topLeft(), newRect.topLeft() );
+ else
+ d->startRect = rect();
+ d->targetRect = newRect;
+ //kdDebug(14000) << k_funcinfo << "At " << rect << endl;
+}
+
+void Component::setRect( const QRect &rect )
+{
+ d->rect = rect;
+}
+
+void Component::paint( QPainter *painter, const QColorGroup &cg )
+{
+ /*painter->setPen( Qt::red );
+ painter->drawRect( rect() );*/
+ for ( uint n = 0; n < components(); ++n )
+ {
+ if( component( n )->isShown() )
+ component( n )->paint( painter, cg );
+ }
+}
+
+void Component::repaint()
+{
+ d->parent->repaint();
+}
+
+void Component::relayout()
+{
+ d->parent->relayout();
+}
+
+void Component::componentAdded( Component *component )
+{
+ ComponentBase::componentAdded( component );
+ //update( Relayout );
+}
+
+void Component::componentRemoved( Component *component )
+{
+ ComponentBase::componentRemoved( component );
+ //update( Relayout );
+}
+
+// BoxComponent --------
+
+class BoxComponent::Private
+{
+public:
+ Private( BoxComponent::Direction dir ) : direction( dir ) {}
+ BoxComponent::Direction direction;
+
+ static const int padding = 2;
+};
+
+BoxComponent::BoxComponent( ComponentBase *parent, Direction dir )
+ : Component( parent ), d( new Private( dir ) )
+{
+}
+
+int BoxComponent::RTTI = Rtti_BoxComponent;
+
+BoxComponent::~BoxComponent()
+{
+ delete d;
+}
+
+int BoxComponent::widthForHeight( int height )
+{
+ if ( d->direction != Horizontal )
+ {
+ int width = 0;
+ for ( uint n = 0; n < components(); ++n )
+ width = QMAX( width, component( n )->widthForHeight( height ) );
+ return width;
+ }
+ else
+ {
+ int width = (components() - 1) * Private::padding;
+ for ( uint n = 0; n < components(); ++n )
+ width += component( n )->widthForHeight( height );
+ return width;
+ }
+}
+
+int BoxComponent::heightForWidth( int width )
+{
+ if ( d->direction == Horizontal )
+ {
+ int height = 0;
+ for ( uint n = 0; n < components(); ++n )
+ height = QMAX( height, component( n )->heightForWidth( width ) );
+ return height;
+ }
+ else
+ {
+ int height = (components() - 1) * Private::padding;
+ for ( uint n = 0; n < components(); ++n )
+ height += component( n )->heightForWidth( width );
+ return height;
+ }
+}
+
+void BoxComponent::calcMinSize()
+{
+ int sum = (components() - 1) * Private::padding, max = 0;
+ for ( uint n = 0; n < components(); ++n )
+ {
+ Component *comp = component( n );
+ if ( d->direction == Horizontal )
+ {
+ max = QMAX( max, comp->minHeight() );
+ sum += comp->minWidth();
+ }
+ else
+ {
+ max = QMAX( max, comp->minWidth() );
+ sum += comp->minHeight();
+ }
+ }
+
+ bool sizeChanged = false;
+ if ( d->direction == Horizontal )
+ {
+ if ( setMinWidth( sum ) ) sizeChanged = true;
+ if ( setMinHeight( max ) ) sizeChanged = true;
+ }
+ else
+ {
+ if ( setMinWidth( max ) ) sizeChanged = true;
+ if ( setMinHeight( sum ) ) sizeChanged = true;
+ }
+
+ if ( sizeChanged )
+ repaint();
+ else
+ relayout();
+}
+
+void BoxComponent::layout( const QRect &rect )
+{
+ Component::layout( rect );
+
+ bool horiz = (d->direction == Horizontal);
+ int fixedSize = 0;
+ for ( uint n = 0; n < components(); ++n )
+ {
+ Component *comp = component( n );
+ if ( horiz )
+ fixedSize += comp->minWidth();
+ else
+ fixedSize += comp->minHeight();
+ }
+
+ // remaining space after all fixed items have been allocated
+ int padding = Private::padding;
+
+ // ensure total is at least minXXX. the only time the rect
+ // will be smaller than that is when we don't fit, and in
+ // that cases we should pretend that we're wide/high enough.
+ int total;
+ if ( horiz )
+ total = QMAX( rect.width(), minWidth() );
+ else
+ total = QMAX( rect.height(), minHeight() );
+
+ int remaining = total - fixedSize - padding * (components() - 1);
+
+ // finally, lay everything out
+ int pos = 0;
+ for ( uint n = 0; n < components(); ++n )
+ {
+ Component *comp = component( n );
+
+ QRect rc;
+ if ( horiz )
+ {
+ rc.setLeft( rect.left() + pos );
+ rc.setTop( rect.top() );
+ rc.setHeight( rect.height() );
+ int minWidth = comp->minWidth();
+ int desiredWidth = comp->widthForHeight( rect.height() );
+ rc.setWidth( QMIN( remaining + minWidth, desiredWidth ) );
+ pos += rc.width();
+ remaining -= rc.width() - minWidth;
+ }
+ else
+ {
+ rc.setLeft( rect.left() );
+ rc.setTop( rect.top() + pos );
+ rc.setWidth( rect.width() );
+ int minHeight = comp->minHeight();
+ int desiredHeight = comp->heightForWidth( rect.width() );
+ rc.setHeight( QMIN( remaining + minHeight, desiredHeight ) );
+ pos += rc.height();
+ remaining -= rc.height() - minHeight;
+ }
+ comp->layout( rc & rect );
+ pos += padding;
+ }
+}
+
+void BoxComponent::componentAdded( Component *component )
+{
+ Component::componentAdded( component );
+ calcMinSize();
+}
+
+void BoxComponent::componentRemoved( Component *component )
+{
+ Component::componentRemoved( component );
+ calcMinSize();
+}
+
+void BoxComponent::componentResized( Component *component )
+{
+ Component::componentResized( component );
+ calcMinSize();
+}
+
+// ImageComponent --------
+
+class ImageComponent::Private
+{
+public:
+ QPixmap image;
+};
+
+ImageComponent::ImageComponent( ComponentBase *parent )
+ : Component( parent ), d( new Private )
+{
+}
+
+int ImageComponent::RTTI = Rtti_ImageComponent;
+
+ImageComponent::ImageComponent( ComponentBase *parent, int minW, int minH )
+ : Component( parent ), d( new Private )
+{
+ setMinWidth( minW );
+ setMinHeight( minH );
+ repaint();
+}
+
+ImageComponent::~ImageComponent()
+{
+ delete d;
+}
+
+QPixmap ImageComponent::pixmap()
+{
+ return d->image;
+}
+
+void ImageComponent::setPixmap( const QPixmap &img, bool adjustSize)
+{
+ d->image = img;
+ if ( adjustSize )
+ {
+ setMinWidth( img.width() );
+ setMinHeight( img.height() );
+ }
+ repaint();
+}
+
+static QPoint operator+( const QPoint &pt, const QSize &sz )
+{
+ return QPoint( pt.x() + sz.width(), pt.y() + sz.height() );
+}
+
+/*static QPoint operator+( const QSize &sz, const QPoint &pt )
+{
+ return pt + sz;
+}*/
+
+void ImageComponent::paint( QPainter *painter, const QColorGroup & )
+{
+ QRect ourRc = rect();
+ QRect rc = d->image.rect();
+ // center rc within our rect
+ rc.moveTopLeft( ourRc.topLeft() + (ourRc.size() - rc.size()) / 2 );
+ // paint, shrunk to be within our rect
+ painter->drawPixmap( rc & ourRc, d->image );
+}
+
+void ImageComponent::scale( int w, int h, QImage::ScaleMode mode )
+{
+ QImage im = d->image.convertToImage();
+ setPixmap( QPixmap( im.smoothScale( w, h, mode ) ) );
+}
+// TextComponent
+
+class TextComponent::Private
+{
+public:
+ Private() : customColor( false ) {}
+ QString text;
+ bool customColor;
+ QColor color;
+ QFont font;
+};
+
+TextComponent::TextComponent( ComponentBase *parent, const QFont &font, const QString &text )
+ : Component( parent ), d( new Private )
+{
+ setFont( font );
+ setText( text );
+}
+
+int TextComponent::RTTI = Rtti_TextComponent;
+
+TextComponent::~TextComponent()
+{
+ delete d;
+}
+
+QString TextComponent::text()
+{
+ return d->text;
+}
+
+void TextComponent::setText( const QString &text )
+{
+ if ( text == d->text ) return;
+ d->text = text;
+ relayout();
+ calcMinSize();
+}
+
+QFont TextComponent::font()
+{
+ return d->font;
+}
+
+void TextComponent::setFont( const QFont &font )
+{
+ if ( font == d->font ) return;
+ d->font = font;
+ calcMinSize();
+}
+
+void TextComponent::calcMinSize()
+{
+ setMinWidth( 0 );
+
+ if ( !d->text.isEmpty() )
+ setMinHeight( QFontMetrics( font() ).height() );
+ else
+ setMinHeight( 0 );
+
+ repaint();
+}
+
+int TextComponent::widthForHeight( int )
+{
+ // add 2 to place an extra gap between the text and things to its right.
+ // allegedly if this is not done the protocol icons overlap the text.
+ // i however have never seen this problem (which would almost certainly
+ // be a bug somewhere else).
+ return QFontMetrics( font() ).width( d->text ) + 2;
+}
+
+QColor TextComponent::color()
+{
+ return d->customColor ? d->color : QColor();
+}
+
+void TextComponent::setColor( const QColor &color )
+{
+ d->color = color;
+ d->customColor = true;
+ repaint();
+}
+
+void TextComponent::setDefaultColor()
+{
+ d->customColor = false;
+ repaint();
+}
+
+void TextComponent::paint( QPainter *painter, const QColorGroup &cg )
+{
+ if ( d->customColor )
+ painter->setPen( d->color );
+ else
+ painter->setPen( cg.text() );
+ QString dispStr = KStringHandler::rPixelSqueeze( d->text, QFontMetrics( font() ), rect().width() );
+ painter->setFont( font() );
+ painter->drawText( rect(), Qt::SingleLine, dispStr );
+}
+
+// DisplayNameComponent
+
+class DisplayNameComponent::Private
+{
+public:
+ QString text;
+ QFont font;
+};
+
+DisplayNameComponent::DisplayNameComponent( ComponentBase *parent )
+ : BoxComponent( parent ), d( new Private )
+{
+}
+
+int DisplayNameComponent::RTTI = Rtti_DisplayNameComponent;
+
+DisplayNameComponent::~DisplayNameComponent()
+{
+ delete d;
+}
+
+void DisplayNameComponent::layout( const QRect &rect )
+{
+ Component::layout( rect );
+
+ // finally, lay everything out
+ QRect rc;
+ int totalWidth = rect.width();
+ int usedWidth = 0;
+ bool exceeded = false;
+ for ( uint n = 0; n < components(); ++n )
+ {
+ Component *comp = component( n );
+ if ( !exceeded )
+ {
+ if ( ( usedWidth + comp->widthForHeight( rect.height() ) ) > totalWidth )
+ {
+ exceeded = true;
+ // TextComponents can squeeze themselves
+ if ( comp->rtti() == Rtti_TextComponent )
+ {
+ comp->show();
+ comp->layout( QRect( usedWidth+ rect.left(), rect.top(),
+ totalWidth - usedWidth,
+ comp->heightForWidth( totalWidth - usedWidth ) ) );
+ } else {
+ comp->hide();
+ }
+ }
+ else
+ {
+ comp->show();
+ comp->layout( QRect( usedWidth+ rect.left(), rect.top(),
+ comp->widthForHeight( rect.height() ),
+ comp->heightForWidth( rect.width() ) ) );
+ }
+ usedWidth+= comp->widthForHeight( rect.height() );
+ }
+ else
+ {
+ // Shall we implement a hide()/show() in Component class ?
+ comp->hide();
+ }
+ }
+}
+
+void DisplayNameComponent::setText( const QString& text )
+{
+ if ( d->text == text )
+ return;
+ d->text = text;
+
+ redraw();
+}
+
+void DisplayNameComponent::redraw()
+{
+ QColor color;
+ for ( uint n = 0; n < components(); ++n )
+ if( component( n )->rtti() == Rtti_TextComponent )
+ {
+ ((TextComponent*)component(n))->color();
+ }
+
+ QValueList<Kopete::Emoticons::Token> tokens;
+ QValueList<Kopete::Emoticons::Token>::const_iterator token;
+
+ clear(); // clear childs
+
+ tokens = Kopete::Emoticons::tokenizeEmoticons( d->text );
+ ImageComponent *ic;
+ TextComponent *t;
+
+ QFontMetrics fontMetrics( d->font );
+ int fontHeight = fontMetrics.height();
+ for ( token = tokens.begin(); token != tokens.end(); ++token )
+ {
+ switch ( (*token).type )
+ {
+ case Kopete::Emoticons::Text:
+ t = new TextComponent( this, d->font, (*token).text );
+ break;
+ case Kopete::Emoticons::Image:
+ ic = new ImageComponent( this );
+ ic->setPixmap( QPixmap( (*token).picPath ) );
+ ic->scale( INT_MAX, fontHeight, QImage::ScaleMin );
+ break;
+ default:
+ kdDebug( 14010 ) << k_funcinfo << "This should have not happened!" << endl;
+ }
+ }
+
+ if(color.isValid())
+ setColor( color );
+}
+
+void DisplayNameComponent::setFont( const QFont& font )
+{
+ for ( uint n = 0; n < components(); ++n )
+ if( component( n )->rtti() == Rtti_TextComponent )
+ ((TextComponent*)component(n))->setFont( font );
+ d->font = font;
+}
+
+void DisplayNameComponent::setColor( const QColor& color )
+{
+ for ( uint n = 0; n < components(); ++n )
+ if( component( n )->rtti() == Rtti_TextComponent )
+ ((TextComponent*)component(n))->setColor( color );
+}
+
+void DisplayNameComponent::setDefaultColor()
+{
+ for ( uint n = 0; n < components(); ++n )
+ if( component( n )->rtti() == Rtti_TextComponent )
+ ((TextComponent*)component(n))->setDefaultColor();
+}
+
+QString DisplayNameComponent::text()
+{
+ return d->text;
+}
+
+// HSpacerComponent --------
+
+HSpacerComponent::HSpacerComponent( ComponentBase *parent )
+ : Component( parent )
+{
+ setMinWidth( 0 );
+ setMinHeight( 0 );
+}
+
+int HSpacerComponent::RTTI = Rtti_HSpacerComponent;
+
+int HSpacerComponent::widthForHeight( int )
+{
+ return INT_MAX;
+}
+
+// VSpacerComponent --------
+
+VSpacerComponent::VSpacerComponent( ComponentBase *parent )
+ : Component( parent )
+{
+ setMinWidth( 0 );
+ setMinHeight( 0 );
+}
+
+int VSpacerComponent::RTTI = Rtti_VSpacerComponent;
+
+int VSpacerComponent::heightForWidth( int )
+{
+ return INT_MAX;
+}
+
+////////////////// ContactComponent /////////////////////////
+
+class ContactComponent::Private
+{
+public:
+ Kopete::Contact *contact;
+ int iconSize;
+};
+
+ContactComponent::ContactComponent( ComponentBase *parent, Kopete::Contact *contact, int iconSize) : ImageComponent( parent ) , d( new Private )
+{
+ d->contact = contact;
+ d->iconSize = iconSize;
+ updatePixmap();
+}
+
+ContactComponent::~ContactComponent()
+{
+ delete d;
+}
+
+void ContactComponent::updatePixmap()
+{
+ setPixmap( contact()->onlineStatus().iconFor( contact(), d->iconSize ) );
+}
+Kopete::Contact *ContactComponent::contact()
+{
+ return d->contact;
+}
+
+// we don't need to use a tooltip source here - this way is simpler
+std::pair<QString,QRect> ContactComponent::toolTip( const QPoint &/*relativePos*/ )
+{
+ return std::make_pair(d->contact->toolTip(),rect());
+}
+
+////////////////// SpacerComponent /////////////////////////
+
+SpacerComponent::SpacerComponent( ComponentBase *parent, int w, int h ) : Component( parent )
+{
+ setMinWidth(w);
+ setMinHeight(h);
+}
+
+// Item --------
+
+/**
+ * A periodic timer intended to be shared amongst multiple objects. Will run only
+ * if an object is attached to it.
+ */
+class SharedTimer : private QTimer
+{
+ int period;
+ int users;
+public:
+ SharedTimer( int period ) : period(period), users(0) {}
+ void attach( QObject *target, const char *slot )
+ {
+ connect( this, SIGNAL(timeout()), target, slot );
+ if( users++ == 0 )
+ start( period );
+ //kdDebug(14000) << "SharedTimer::attach: users is now " << users << "\n";
+ }
+ void detach( QObject *target, const char *slot )
+ {
+ disconnect( this, SIGNAL(timeout()), target, slot );
+ if( --users == 0 )
+ stop();
+ //kdDebug(14000) << "SharedTimer::detach: users is now " << users << "\n";
+ }
+};
+
+class SharedTimerRef
+{
+ SharedTimer &timer;
+ QObject * const object;
+ const char * const slot;
+ bool attached;
+public:
+ SharedTimerRef( SharedTimer &timer, QObject *obj, const char *slot )
+ : timer(timer), object(obj), slot(slot), attached(false)
+ {
+ }
+ void start()
+ {
+ if( attached ) return;
+ timer.attach( object, slot );
+ attached = true;
+ }
+ void stop()
+ {
+ if( !attached ) return;
+ timer.detach( object, slot );
+ attached = false;
+ }
+ bool isActive()
+ {
+ return attached;
+ }
+};
+
+class Item::Private
+{
+public:
+ Private( Item *item )
+ : layoutAnimateTimer( theLayoutAnimateTimer(), item, SLOT( slotLayoutAnimateItems() ) )
+ , animateLayout( true ), opacity( 1.0 )
+ , visibilityTimer( theVisibilityTimer(), item, SLOT( slotUpdateVisibility() ) )
+ , visibilityLevel( 0 ), visibilityTarget( false ), searchMatch( true )
+ {
+ }
+
+ QTimer layoutTimer;
+
+ //QTimer layoutAnimateTimer;
+ SharedTimerRef layoutAnimateTimer;
+ SharedTimer &theLayoutAnimateTimer()
+ {
+ static SharedTimer timer( 10 );
+ return timer;
+ }
+
+ bool animateLayout;
+ int layoutAnimateSteps;
+ static const int layoutAnimateStepsTotal = 10;
+
+ float opacity;
+
+ //QTimer visibilityTimer;
+ SharedTimerRef visibilityTimer;
+ SharedTimer &theVisibilityTimer()
+ {
+ static SharedTimer timer( 40 );
+ return timer;
+ }
+
+ int visibilityLevel;
+ bool visibilityTarget;
+ static const int visibilityFoldSteps = 7;
+#ifdef HAVE_XRENDER
+ static const int visibilityFadeSteps = 7;
+#else
+ static const int visibilityFadeSteps = 0;
+#endif
+ static const int visibilityStepsTotal = visibilityFoldSteps + visibilityFadeSteps;
+
+ bool searchMatch;
+
+ static bool animateChanges;
+ static bool fadeVisibility;
+ static bool foldVisibility;
+};
+
+bool Item::Private::animateChanges = true;
+bool Item::Private::fadeVisibility = true;
+bool Item::Private::foldVisibility = true;
+
+Item::Item( QListViewItem *parent, QObject *owner, const char *name )
+ : QObject( owner, name ), KListViewItem( parent ), d( new Private(this) )
+{
+ initLVI();
+}
+
+Item::Item( QListView *parent, QObject *owner, const char *name )
+ : QObject( owner, name ), KListViewItem( parent ), d( new Private(this) )
+{
+ initLVI();
+}
+
+Item::~Item()
+{
+ delete d;
+}
+
+void Item::setEffects( bool animation, bool fading, bool folding )
+{
+ Private::animateChanges = animation;
+ Private::fadeVisibility = fading;
+ Private::foldVisibility = folding;
+}
+
+void Item::initLVI()
+{
+ connect( listView()->header(), SIGNAL( sizeChange( int, int, int ) ), SLOT( slotColumnResized() ) );
+ connect( &d->layoutTimer, SIGNAL( timeout() ), SLOT( slotLayoutItems() ) );
+ //connect( &d->layoutAnimateTimer, SIGNAL( timeout() ), SLOT( slotLayoutAnimateItems() ) );
+ //connect( &d->visibilityTimer, SIGNAL( timeout() ), SLOT( slotUpdateVisibility() ) );
+ setVisible( false );
+ setTargetVisibility( true );
+}
+
+void Item::slotColumnResized()
+{
+ scheduleLayout();
+ // if we've been resized, don't animate the layout
+ d->animateLayout = false;
+}
+
+void Item::scheduleLayout()
+{
+ // perform a delayed layout in order to speed it all up
+ if ( ! d->layoutTimer.isActive() )
+ d->layoutTimer.start( 30, true );
+}
+
+void Item::slotLayoutItems()
+{
+ d->layoutTimer.stop();
+
+ for ( uint n = 0; n < components(); ++n )
+ {
+ int width = listView()->columnWidth(n);
+ if ( n == 0 )
+ {
+ int d = depth() + (listView()->rootIsDecorated() ? 1 : 0);
+ width -= d * listView()->treeStepSize();
+ }
+
+ int height = component( n )->heightForWidth( width );
+ component( n )->layout( QRect( 0, 0, width, height ) );
+ //kdDebug(14000) << k_funcinfo << "Component " << n << " is " << width << " x " << height << endl;
+ }
+
+ if ( Private::animateChanges && d->animateLayout && !d->visibilityTimer.isActive() )
+ {
+ d->layoutAnimateTimer.start();
+ //if ( !d->layoutAnimateTimer.isActive() )
+ // d->layoutAnimateTimer.start( 10 );
+ d->layoutAnimateSteps = 0;
+ }
+ else
+ {
+ d->layoutAnimateSteps = Private::layoutAnimateStepsTotal;
+ d->animateLayout = true;
+ }
+ slotLayoutAnimateItems();
+}
+
+void Item::slotLayoutAnimateItems()
+{
+ if ( ++d->layoutAnimateSteps >= Private::layoutAnimateStepsTotal )
+ d->layoutAnimateTimer.stop();
+
+ const int s = Private::layoutAnimateStepsTotal;
+ const int p = QMIN( d->layoutAnimateSteps, s );
+
+ updateAnimationPosition( p, s );
+ setHeight(0);
+ repaint();
+}
+
+float Item::opacity()
+{
+ return d->opacity;
+}
+
+void Item::setOpacity( float opacity )
+{
+ if ( d->opacity == opacity ) return;
+ d->opacity = opacity;
+ repaint();
+}
+
+void Item::setSearchMatch( bool match )
+{
+ d->searchMatch = match;
+
+ if ( !match )
+ setVisible( false );
+ else
+ {
+ kdDebug(14000) << k_funcinfo << " match: " << match << ", vis timer active: " << d->visibilityTimer.isActive()
+ << ", target visibility: " << targetVisibility() << endl;
+ if ( d->visibilityTimer.isActive() )
+ setVisible( true );
+ else
+ setVisible( targetVisibility() );
+ }
+}
+
+bool Item::targetVisibility()
+{
+ return d->visibilityTarget;
+}
+
+void Item::setTargetVisibility( bool vis )
+{
+ if ( d->visibilityTarget == vis )
+ {
+ // in case we're getting called because our parent was shown and
+ // we need to be rehidden
+ if ( !d->visibilityTimer.isActive() )
+ setVisible( vis && d->searchMatch );
+ return;
+ }
+ d->visibilityTarget = vis;
+ d->visibilityTimer.start();
+ //d->visibilityTimer.start( 40 );
+ if ( targetVisibility() )
+ setVisible( d->searchMatch );
+ slotUpdateVisibility();
+}
+
+void Item::slotUpdateVisibility()
+{
+ if ( targetVisibility() )
+ ++d->visibilityLevel;
+ else
+ --d->visibilityLevel;
+
+ if ( !Private::foldVisibility && !Private::fadeVisibility )
+ d->visibilityLevel = targetVisibility() ? Private::visibilityStepsTotal : 0;
+ else if ( !Private::fadeVisibility && d->visibilityLevel >= Private::visibilityFoldSteps )
+ d->visibilityLevel = targetVisibility() ? Private::visibilityStepsTotal : Private::visibilityFoldSteps - 1;
+ else if ( !Private::foldVisibility && d->visibilityLevel <= Private::visibilityFoldSteps )
+ d->visibilityLevel = targetVisibility() ? Private::visibilityFoldSteps + 1 : 0;
+
+ if ( d->visibilityLevel >= Private::visibilityStepsTotal )
+ {
+ d->visibilityLevel = Private::visibilityStepsTotal;
+ d->visibilityTimer.stop();
+ }
+ else if ( d->visibilityLevel <= 0 )
+ {
+ d->visibilityLevel = 0;
+ d->visibilityTimer.stop();
+ setVisible( false );
+ }
+ setHeight( 0 );
+ repaint();
+}
+
+void Item::repaint()
+{
+ // if we're about to relayout, don't bother painting yet.
+ if ( d->layoutTimer.isActive() )
+ return;
+ listView()->repaintItem( this );
+}
+
+void Item::relayout()
+{
+ scheduleLayout();
+}
+
+void Item::setup()
+{
+ KListViewItem::setup();
+ slotLayoutItems();
+}
+
+void Item::setHeight( int )
+{
+ int minHeight = 0;
+ for ( uint n = 0; n < components(); ++n )
+ minHeight = QMAX( minHeight, component( n )->rect().height() );
+ //kdDebug(14000) << k_funcinfo << "Height is " << minHeight << endl;
+ if ( Private::foldVisibility && d->visibilityTimer.isActive() )
+ {
+ int vis = QMIN( d->visibilityLevel, Private::visibilityFoldSteps );
+ minHeight = (minHeight * vis) / Private::visibilityFoldSteps;
+ }
+ KListViewItem::setHeight( minHeight );
+}
+
+int Item::width( const QFontMetrics &, const QListView *lv, int c ) const
+{
+ // Qt computes the itemRect from this. we want the whole item to be
+ // clickable, so we return the widest we could possibly be.
+ return lv->header()->sectionSize( c );
+}
+
+void Item::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align )
+{
+ QPixmap back( width, height() );
+ QPainter paint( &back );
+ //KListViewItem::paintCell( &paint, cg, column, width, align );
+ // PASTED FROM KLISTVIEWITEM:
+ // set the alternate cell background colour if necessary
+ QColorGroup _cg = cg;
+ if (isAlternate())
+ if (listView()->viewport()->backgroundMode()==Qt::FixedColor)
+ _cg.setColor(QColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground());
+ else
+ _cg.setColor(QColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground());
+ // PASTED FROM QLISTVIEWITEM
+ {
+ QPainter *p = &paint;
+
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+ QFontMetrics fm( p->fontMetrics() );
+
+ // any text we render is done by the Components, not by this class, so make sure we've nothing to write
+ QString t;
+
+ // removed text truncating code from Qt - we do that differently, further on
+
+ int marg = lv->itemMargin();
+ int r = marg;
+ // const QPixmap * icon = pixmap( column );
+
+ const BackgroundMode bgmode = lv->viewport()->backgroundMode();
+ const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode );
+
+ if ( _cg.brush( crole ) != lv->colorGroup().brush( crole ) )
+ p->fillRect( 0, 0, width, height(), _cg.brush( crole ) );
+ else
+ {
+ // all copied from QListView::paintEmptyArea
+
+ //lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) );
+ QStyleOption opt( lv->sortColumn(), 0 ); // ### hack; in 3.1, add a property in QListView and QHeader
+ QStyle::SFlags how = QStyle::Style_Default;
+ if ( lv->isEnabled() )
+ how |= QStyle::Style_Enabled;
+
+ lv->style().drawComplexControl( QStyle::CC_ListView,
+ p, lv, QRect( 0, 0, width, height() ), lv->colorGroup(),
+ how, QStyle::SC_ListView, QStyle::SC_None,
+ opt );
+ }
+
+
+
+ if ( isSelected() &&
+ (column == 0 || lv->allColumnsShowFocus()) ) {
+ p->fillRect( r - marg, 0, width - r + marg, height(),
+ _cg.brush( QColorGroup::Highlight ) );
+ // removed text pen setting code from Qt
+ }
+
+ // removed icon drawing code from Qt
+
+ // draw the tree gubbins
+ if ( multiLinesEnabled() && column == 0 && isOpen() && childCount() ) {
+ int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin();
+ textheight = QMAX( textheight, QApplication::globalStrut().height() );
+ if ( textheight % 2 > 0 )
+ textheight++;
+ if ( textheight < height() ) {
+ int w = lv->treeStepSize() / 2;
+ lv->style().drawComplexControl( QStyle::CC_ListView, p, lv,
+ QRect( 0, textheight, w + 1, height() - textheight + 1 ), _cg,
+ lv->isEnabled() ? QStyle::Style_Enabled : QStyle::Style_Default,
+ QStyle::SC_ListViewExpand,
+ (uint)QStyle::SC_All, QStyleOption( this ) );
+ }
+ }
+ }
+ // END OF PASTE
+
+
+ //do you see a better way to tell the TextComponent we are selected ? - Olivier 2004-09-02
+ if ( isSelected() )
+ _cg.setColor(QColorGroup::Text , _cg.highlightedText() );
+
+ if ( Component *comp = component( column ) )
+ comp->paint( &paint, _cg );
+ paint.end();
+
+#ifdef HAVE_XRENDER
+ QColor rgb = cg.base();//backgroundColor();
+ float opac = 1.0;
+ if ( d->visibilityTimer.isActive() && Private::fadeVisibility )
+ {
+ int vis = QMAX( d->visibilityLevel - Private::visibilityFoldSteps, 0 );
+ opac = float(vis) / Private::visibilityFadeSteps;
+ }
+ opac *= opacity();
+ const int alpha = 257 - int(opac * 257);
+ if ( alpha != 0 )
+ {
+ XRenderColor clr = { alpha * rgb.red(), alpha * rgb.green(), alpha * rgb.blue(), alpha * 0xff };
+ XRenderFillRectangle( back.x11Display(), PictOpOver, back.x11RenderHandle(),
+ &clr, 0, 0, width, height() );
+ }
+#endif
+
+ p->drawPixmap( 0, 0, back );
+}
+
+void Item::componentAdded( Component *component )
+{
+ ComponentBase::componentAdded( component );
+ scheduleLayout();
+}
+
+void Item::componentRemoved( Component *component )
+{
+ ComponentBase::componentRemoved( component );
+ scheduleLayout();
+}
+
+void Item::componentResized( Component *component )
+{
+ ComponentBase::componentResized( component );
+ scheduleLayout();
+}
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+#include "kopetelistviewitem.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopetelistviewitem.h b/kopete/libkopete/ui/kopetelistviewitem.h
new file mode 100644
index 00000000..5952c569
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistviewitem.h
@@ -0,0 +1,462 @@
+/*
+ kopetelistviewitem.h - Kopete's modular QListViewItems
+
+ Copyright (c) 2005 by Engin AYDOGAN <engin@bzzzt.biz>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETE_LISTVIEWITEM_H
+#define KOPETE_LISTVIEWITEM_H
+
+#include <klistview.h>
+#include <kopetecontact.h>
+
+#include <utility>
+#include <qimage.h>
+
+class QPixmap;
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+class Component;
+
+class ComponentBase
+{
+public:
+ ComponentBase();
+ virtual ~ComponentBase() = 0;
+
+ uint components();
+ Component *component( uint n );
+ Component *componentAt( const QPoint &pt );
+
+ /** Repaint this item */
+ virtual void repaint() = 0;
+ /** Relayout this item */
+ virtual void relayout() = 0;
+
+ /**
+ * Get the tool tip string and rectangle for a tip request at position
+ * relativePos relative to this item. Queries the appropriate child component.
+ *
+ * @return a pair where the first element is the tooltip, and the second is
+ * the rectangle within the item for which the tip should be displayed.
+ */
+ virtual std::pair<QString,QRect> toolTip( const QPoint &relativePos );
+
+protected:
+ /** A child item has been added to this item */
+ virtual void componentAdded( Component *component );
+ /** A child item has been removed from this item */
+ virtual void componentRemoved( Component *component );
+ /** A child item has been resized */
+ virtual void componentResized( Component *component );
+ /** Remove all children */
+ virtual void clear();
+
+ /** @internal animate items */
+ void updateAnimationPosition( int p, int s );
+private:
+ class Private;
+ Private *d;
+
+ // calls componentAdded and componentRemoved
+ friend class Component;
+};
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class ToolTipSource
+{
+public:
+ /**
+ * Get the tooltip string and rect for a component
+ *
+ * @param component The component to get a tip for
+ * @param pt The point (relative to the list item) the mouse is at
+ * @param rect The tip will be removed when the mouse leaves this rect.
+ * Will initially be set to \p component's rect().
+ */
+ virtual QString operator() ( ComponentBase *component, const QPoint &pt, QRect &rect ) = 0;
+};
+
+/**
+ * This class represents a rectangular subsection of a ListItem.
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class Component : public ComponentBase
+{
+protected:
+ Component( ComponentBase *parent );
+public:
+ virtual ~Component() = 0;
+
+ /**
+ * Set the size and position of this item relative to the list view item. Should
+ * only be called by the containing item.
+ * @param rect the new rectangle this component will paint in, relative to the painter
+ * passed to the paint() function by the parent item.
+ */
+ virtual void layout( const QRect &rect );
+
+ /**
+ * Paint this item, inside the rectangle returned by rect().
+ * The default implementation calls paint on all children.
+ */
+ virtual void paint( QPainter *painter, const QColorGroup &cg );
+
+ void repaint();
+ void relayout();
+
+ /**
+ * @return the rect this component was allocated last time it was laid out
+ */
+ QRect rect();
+ /**
+ * Prevents this component to be drawn
+ */
+ void hide();
+
+ /**
+ * Makes this component to be drawn
+ */
+ void show();
+
+ bool isShown();
+ bool isHidden();
+
+ /**
+ * Returns the smallest this component can become horizontally while still
+ * being useful.
+ */
+ int minWidth();
+ /**
+ * Returns the smallest this component can become vertically while still
+ * being useful.
+ */
+ int minHeight();
+
+ /**
+ * Returns the width this component desires for a given @a height. By default
+ * this function returns minWidth().
+ */
+ virtual int widthForHeight( int height );
+ /**
+ * Returns the height this component desires for a given @a width. By default
+ * this function returns minHeight().
+ */
+ virtual int heightForWidth( int width );
+
+ /**
+ * Set a tool tip source for this item. The tool tip source object is
+ * still owned by the caller, and must live for at least as long as
+ * this component.
+ */
+ void setToolTipSource( ToolTipSource *source = 0 );
+
+ /**
+ * Get the tool tip string and rectangle for a tip request at position
+ * relativePos relative to this item. If a tooltip source is set, it will
+ * be used. Otherwise calls the base class.
+ *
+ * @return a pair where the first element is the tooltip, and the second is
+ * the rectangle within the item for which the tip should be displayed.
+ */
+ std::pair<QString,QRect> toolTip( const QPoint &relativePos );
+
+ /**
+ * RTTI: Runtime Type Information
+ * Exactly the same as Qt's approach to identify types of
+ * QCanvasItems.
+ */
+
+ enum RttiValues {
+ Rtti_Component, Rtti_BoxComponent, Rtti_TextComponent,
+ Rtti_ImageComponent, Rtti_DisplayNameComponent,
+ Rtti_HSpacerComponent, Rtti_VSpacerComponent
+ };
+
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+protected:
+ /**
+ * Change the minimum width, in pixels, this component requires in order
+ * to be at all useful. Note: do not call this from your layout() function.
+ * @param width the minimum width
+ * @return true if the size has actually changed, false if it's been set to
+ * the existing values. if it returns true, you do not need to relayout,
+ * since the parent component will do that for you.
+ */
+ bool setMinWidth( int width );
+ /**
+ * Change the minimum height, in pixels, this component requires in order
+ * to be at all useful. Note: do not call this from your layout() function.
+ * @param height the minimum height
+ * @return true if the size has actually changed, false if it's been set to
+ * the existing values. If it returns true, you do not need to relayout,
+ * since the parent component will do that for you.
+ */
+ bool setMinHeight( int height );
+
+ void componentAdded( Component *component );
+ void componentRemoved( Component *component );
+
+private:
+ // calls the three functions below
+ friend void ComponentBase::updateAnimationPosition( int p, int s );
+
+ // used for animation
+ void setRect( const QRect &rect );
+ QRect startRect();
+ QRect targetRect();
+
+ class Private;
+ Private *d;
+};
+
+class BoxComponent : public Component
+{
+public:
+ enum Direction { Horizontal, Vertical };
+ BoxComponent( ComponentBase *parent, Direction dir = Horizontal );
+ ~BoxComponent();
+
+ void layout( const QRect &rect );
+
+ virtual int widthForHeight( int height );
+ virtual int heightForWidth( int width );
+
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+
+protected:
+ void componentAdded( Component *component );
+ void componentRemoved( Component *component );
+ void componentResized( Component *component );
+
+private:
+ void calcMinSize();
+
+ class Private;
+ Private *d;
+};
+
+class TextComponent : public Component
+{
+public:
+ TextComponent( ComponentBase *parent, const QFont &font = QFont(), const QString &text = QString::null );
+ ~TextComponent();
+
+ QString text();
+ void setText( const QString &text );
+
+ QFont font();
+ void setFont( const QFont &font );
+
+ QColor color();
+ void setColor( const QColor &color );
+ void setDefaultColor();
+
+ int widthForHeight( int );
+
+ void paint( QPainter *painter, const QColorGroup &cg );
+
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+
+private:
+ void calcMinSize();
+
+ class Private;
+ Private *d;
+};
+
+class ImageComponent : public Component
+{
+public:
+ ImageComponent( ComponentBase *parent );
+ ImageComponent( ComponentBase *parent, int minW, int minH );
+ ~ImageComponent();
+
+ void setPixmap( const QPixmap &img, bool adjustSize = true);
+ QPixmap pixmap( void );
+
+ void paint( QPainter *painter, const QColorGroup &cg );
+
+ void scale( int w, int h, QImage::ScaleMode );
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+private:
+ class Private;
+ Private *d;
+};
+
+/**
+ * ContactComponent
+ */
+class ContactComponent : public ImageComponent
+{
+public:
+ ContactComponent( ComponentBase *parent, Kopete::Contact *contact, int iconSize);
+ ~ContactComponent();
+ void updatePixmap();
+ Kopete::Contact *contact();
+ std::pair<QString,QRect> toolTip( const QPoint &relativePos );
+protected:
+ class Private;
+ Private *d;
+};
+
+/**
+ * SpacerComponent
+ */
+class SpacerComponent : public Component
+{
+public:
+ SpacerComponent( ComponentBase *parent, int w, int h );
+};
+
+/**
+ * DisplayNameComponent
+ */
+
+class DisplayNameComponent : public BoxComponent
+{
+public:
+ /**
+ * Constructor
+ */
+ DisplayNameComponent( ComponentBase *parent );
+
+ /**
+ * Dtor
+ */
+ ~DisplayNameComponent();
+ void layout( const QRect& rect );
+
+ QString text();
+ void setText( const QString& text );
+ void setFont( const QFont& font );
+ void setColor( const QColor& color );
+ void setDefaultColor();
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+ /**
+ * reparse again for emoticon (call this when emoticon theme change)
+ */
+ void redraw();
+
+private:
+ class Private;
+ Private *d;
+};
+
+class HSpacerComponent : public Component
+{
+public:
+ HSpacerComponent( ComponentBase *parent );
+ int widthForHeight( int );
+
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+};
+
+class VSpacerComponent : public Component
+{
+public:
+ VSpacerComponent( ComponentBase *parent );
+ int heightForWidth( int );
+
+ static int RTTI;
+ virtual int rtti() const { return RTTI; }
+};
+
+/**
+ * List-view item composed of Component items. Supports height-for-width, tooltips and
+ * some animation effects.
+ *
+ * @author Richard Smith <kde@metafoo.co.uk>
+ */
+class Item : public QObject, public KListViewItem, public ComponentBase
+{
+ Q_OBJECT
+public:
+ Item( QListView *parent, QObject *owner = 0, const char *name = 0 );
+ Item( QListViewItem *parent, QObject *owner = 0, const char *name = 0 );
+ ~Item();
+
+ void repaint();
+ void relayout();
+
+ void setup();
+ virtual void paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align );
+ //TODO: startRename(...)
+
+ float opacity();
+ void setOpacity( float alpha );
+
+ bool targetVisibility();
+ void setTargetVisibility( bool vis );
+
+ /**
+ * Turn on and off certain visual effects for all Items.
+ * @param animation whether changes to items should be animated.
+ * @param fading whether requests to setTargetVisibility should cause fading of items.
+ * @param folding whether requests to setTargetVisibility should cause folding of items.
+ */
+ static void setEffects( bool animation, bool fading, bool folding );
+
+ int width( const QFontMetrics & fm, const QListView * lv, int c ) const;
+
+ /**
+ * Show or hide this item in a clean way depending on whether it matches
+ * the current quick search
+ * @param match If true, show or hide the item as normal. If false, hide the item immediately.
+ */
+ virtual void setSearchMatch( bool match );
+
+protected:
+ void componentAdded( Component *component );
+ void componentRemoved( Component *component );
+ void componentResized( Component *component );
+
+ void setHeight( int );
+
+private:
+ void initLVI();
+ void recalcHeight();
+ void scheduleLayout();
+
+private slots:
+ void slotColumnResized();
+ void slotLayoutItems();
+ void slotLayoutAnimateItems();
+ void slotUpdateVisibility();
+
+private:
+ class Private;
+ Private *d;
+};
+
+} // END namespace ListView
+} // END namespace UI
+} // END namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopetelistviewsearchline.cpp b/kopete/libkopete/ui/kopetelistviewsearchline.cpp
new file mode 100644
index 00000000..a71d86c0
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistviewsearchline.cpp
@@ -0,0 +1,138 @@
+/*
+ kopetelistviewsearchline.cpp - a widget for performing quick searches on Kopete::ListViews
+ Based on code from KMail, copyright (c) 2004 Till Adam <adam@kde.org>
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetelistviewsearchline.h"
+#include "kopetelistviewitem.h"
+#include "kopetelistview.h"
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+SearchLine::SearchLine( QWidget *parent, ListView *listView, const char *name )
+ : KListViewSearchLine( parent, listView, name )
+{
+}
+
+SearchLine::SearchLine(QWidget *parent, const char *name)
+ : KListViewSearchLine( parent, 0, name )
+{
+}
+
+SearchLine::~SearchLine()
+{
+}
+
+
+void SearchLine::updateSearch( const QString &s )
+{
+ // we copy a huge chunk of code here simply in order to override
+ // the way items are shown/hidden. KSearchLine rudely
+ // calls setVisible() on items with no way to customise this behaviour.
+
+ //BEGIN code from KSearchLine::updateSearch
+ if( !listView() )
+ return;
+
+ search = s.isNull() ? text() : s;
+
+ // If there's a selected item that is visible, make sure that it's visible
+ // when the search changes too (assuming that it still matches).
+
+ QListViewItem *currentItem = 0;
+
+ switch( listView()->selectionMode() )
+ {
+ case KListView::NoSelection:
+ break;
+ case KListView::Single:
+ currentItem = listView()->selectedItem();
+ break;
+ default:
+ for( QListViewItemIterator it(listView(), QListViewItemIterator::Selected | QListViewItemIterator::Visible);
+ it.current() && !currentItem; ++it )
+ {
+ if( listView()->itemRect( it.current() ).isValid() )
+ currentItem = it.current();
+ }
+ }
+
+ if( keepParentsVisible() )
+ checkItemParentsVisible( listView()->firstChild() );
+ else
+ checkItemParentsNotVisible();
+
+ if( currentItem )
+ listView()->ensureItemVisible( currentItem );
+ //END code from KSearchLine::updateSearch
+}
+
+void SearchLine::checkItemParentsNotVisible()
+{
+ //BEGIN code from KSearchLine::checkItemParentsNotVisible
+ QListViewItemIterator it( listView() );
+ for( ; it.current(); ++it )
+ {
+ QListViewItem *item = it.current();
+ if( itemMatches( item, search ) )
+ setItemVisible( item, true );
+ else
+ setItemVisible( item, false );
+ }
+ //END code from KSearchLine::checkItemParentsNotVisible
+}
+
+bool SearchLine::checkItemParentsVisible( QListViewItem *item )
+{
+ //BEGIN code from KSearchLine::checkItemParentsVisible
+ bool visible = false;
+ for( ; item; item = item->nextSibling() ) {
+ if( ( item->firstChild() && checkItemParentsVisible( item->firstChild() ) ) ||
+ itemMatches( item, search ) )
+ {
+ setItemVisible( item, true );
+ // OUCH! this operation just became exponential-time.
+ // however, setting an item visible sets all its descendents
+ // visible too, which we definitely don't want.
+ // plus, in Kopete the nesting is never more than 2 deep,
+ // so this really just doubles the runtime, if that.
+ // this still can be done in O(n) time by a mark-set process,
+ // but that's overkill in our case.
+ checkItemParentsVisible( item->firstChild() );
+ visible = true;
+ }
+ else
+ setItemVisible( item, false );
+ }
+ return visible;
+ //END code from KSearchLine::checkItemParentsVisible
+}
+
+void SearchLine::setItemVisible( QListViewItem *it, bool b )
+{
+ if( Item *item = dynamic_cast<Item*>( it ) )
+ item->setSearchMatch( b );
+ else
+ it->setVisible( b );
+}
+
+} // namespace ListView
+} // namespace UI
+} // namespace Kopete
+
+#include "kopetelistviewsearchline.moc"
diff --git a/kopete/libkopete/ui/kopetelistviewsearchline.h b/kopete/libkopete/ui/kopetelistviewsearchline.h
new file mode 100644
index 00000000..a453b844
--- /dev/null
+++ b/kopete/libkopete/ui/kopetelistviewsearchline.h
@@ -0,0 +1,66 @@
+/*
+ kopetelistviewsearchline.h - a widget for performing quick searches of Kopete::ListViews
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETELISTVIEWSEARCHLINE_H
+#define KOPETELISTVIEWSEARCHLINE_H
+
+#include <klistviewsearchline.h>
+
+namespace Kopete {
+namespace UI {
+namespace ListView {
+
+class ListView;
+
+class SearchLine : public KListViewSearchLine
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructs a SearchLine with \a listView being the
+ * ListView to be filtered.
+ *
+ * If \a listView is null then the widget will be disabled until a listview
+ * is set with setListView().
+ */
+ SearchLine( QWidget *parent, ListView *listView = 0, const char *name = 0 );
+ /**
+ * Constructs a SearchLine without any ListView to filter. The
+ * KListView object has to be set later with setListView().
+ */
+ SearchLine(QWidget *parent, const char *name);
+ /**
+ * Destroys the SearchLine.
+ */
+ ~SearchLine();
+
+ void updateSearch( const QString &s );
+
+protected:
+ virtual void checkItemParentsNotVisible();
+ virtual bool checkItemParentsVisible( QListViewItem *it );
+ virtual void setItemVisible( QListViewItem *it, bool visible );
+
+private:
+ QString search;
+};
+
+} // end namespace ListView
+} // end namespace UI
+} // end namespace Kopete
+
+#endif
diff --git a/kopete/libkopete/ui/kopetepassworddialog.ui b/kopete/libkopete/ui/kopetepassworddialog.ui
new file mode 100644
index 00000000..7ba4dff9
--- /dev/null
+++ b/kopete/libkopete/ui/kopetepassworddialog.ui
@@ -0,0 +1,109 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KopetePasswordDialog</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopetePasswordDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>472</width>
+ <height>117</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_image</cstring>
+ </property>
+ </widget>
+ <widget class="KActiveLabel">
+ <property name="name">
+ <cstring>m_text</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <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>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_password</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit">
+ <property name="name">
+ <cstring>m_password</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_save_passwd</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remember password</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>m_password</tabstop>
+ <tabstop>m_save_passwd</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/libkopete/ui/kopetepasswordwidget.cpp b/kopete/libkopete/ui/kopetepasswordwidget.cpp
new file mode 100644
index 00000000..2345f103
--- /dev/null
+++ b/kopete/libkopete/ui/kopetepasswordwidget.cpp
@@ -0,0 +1,132 @@
+/*
+ kopetepasswordwidget.cpp - widget for modifying a Kopete::Password
+
+ Copyright (c) 2003 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetepasswordwidget.h"
+#include "kopetepassword.h"
+
+#include <kpassdlg.h>
+
+#include <qcheckbox.h>
+
+class Kopete::UI::PasswordWidget::Private
+{
+public:
+ uint maxLength;
+};
+
+Kopete::UI::PasswordWidget::PasswordWidget( QWidget *parent, const char *name, Kopete::Password *from )
+ : KopetePasswordWidgetBase( parent, name ), d( new Private )
+{
+ load( from );
+}
+
+Kopete::UI::PasswordWidget::~PasswordWidget()
+{
+ delete d;
+}
+
+void Kopete::UI::PasswordWidget::load( Kopete::Password *source )
+{
+ disconnect( mRemembered, SIGNAL( stateChanged( int ) ), this, SLOT( slotRememberChanged() ) );
+ disconnect( mPassword, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ disconnect( mRemembered, SIGNAL( stateChanged( int ) ), this, SIGNAL( changed() ) );
+
+ if ( source && source->remembered() )
+ {
+ mRemembered->setTristate();
+ mRemembered->setNoChange();
+ source->requestWithoutPrompt( this, SLOT( receivePassword( const QString & ) ) );
+ }
+ else
+ {
+ mRemembered->setTristate( false );
+ mRemembered->setChecked( false );
+ }
+
+ if ( source )
+ d->maxLength = source->maximumLength();
+ else
+ d->maxLength = 0;
+
+ mPassword->setEnabled( false );
+
+ connect( mRemembered, SIGNAL( stateChanged( int ) ), this, SLOT( slotRememberChanged() ) );
+ connect( mPassword, SIGNAL( textChanged( const QString & ) ), this, SIGNAL( changed() ) );
+ connect( mRemembered, SIGNAL( stateChanged( int ) ), this, SIGNAL( changed() ) );
+
+ emit changed();
+}
+
+void Kopete::UI::PasswordWidget::slotRememberChanged()
+{
+ mRemembered->setTristate( false );
+ mPassword->setEnabled( mRemembered->isChecked() );
+}
+
+void Kopete::UI::PasswordWidget::receivePassword( const QString &pwd )
+{
+ // pwd == null could mean user declined to open wallet
+ // don't uncheck the remembered field in this case.
+ if ( !pwd.isNull() && mRemembered->state() == QButton::NoChange )
+ {
+ mRemembered->setChecked( true );
+ setPassword( pwd );
+ }
+}
+
+void Kopete::UI::PasswordWidget::save( Kopete::Password *target )
+{
+ if ( !target || mRemembered->state() == QButton::NoChange )
+ return;
+
+ if ( mRemembered->isChecked() )
+ target->set( password() );
+ else
+ target->set();
+}
+
+bool Kopete::UI::PasswordWidget::validate()
+{
+ if ( !mRemembered->isChecked() ) return true;
+ if ( d->maxLength == 0 ) return true;
+ return password().length() <= d->maxLength;
+}
+
+QString Kopete::UI::PasswordWidget::password() const
+{
+ return QString::fromLocal8Bit( mPassword->password() );
+}
+
+bool Kopete::UI::PasswordWidget::remember() const
+{
+ return mRemembered->state() == QButton::On;
+}
+
+void Kopete::UI::PasswordWidget::setPassword( const QString &pass )
+{
+ // switch out of 'waiting for wallet' mode if we're in it
+ mRemembered->setTristate( false );
+
+ // fill in the password text
+ mPassword->erase();
+ mPassword->insert( pass );
+ mPassword->setEnabled( remember() );
+}
+
+#include "kopetepasswordwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopetepasswordwidget.h b/kopete/libkopete/ui/kopetepasswordwidget.h
new file mode 100644
index 00000000..b1c51a39
--- /dev/null
+++ b/kopete/libkopete/ui/kopetepasswordwidget.h
@@ -0,0 +1,109 @@
+/*
+ kopetepasswordwidget.cpp - widget for editing a Kopete::Password
+
+ Copyright (c) 2003 by Richard Smith <kde@metafoo.co.uk>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEPASSWORDWIDGET_H
+#define KOPETEPASSWORDWIDGET_H
+
+#include "kopetepasswordwidgetbase.h"
+#include "kopete_export.h"
+
+namespace Kopete
+{
+
+class Password;
+
+namespace UI
+{
+
+/**
+ * @author Richard Smith <kde@metafoo.co.uk>
+ * This widget displays an editable password, including the Remember password checkbox.
+ * @todo This is NOT BC yet : it derives from a uic-generated class
+ */
+class KOPETE_EXPORT PasswordWidget : public KopetePasswordWidgetBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a Kopete::PasswordWidget.
+ * @param parent The widget to nest this one inside
+ * @param name The name of this QObject
+ * @param from The password to load the data for this widget from, or 0 if none
+ */
+ PasswordWidget( QWidget *parent, const char *name = 0, Kopete::Password *from = 0 );
+ ~PasswordWidget();
+
+ /**
+ * Loads the information stored in source into the widget
+ */
+ void load( Kopete::Password *source );
+ /**
+ * Saves the information in the widget into target
+ */
+ void save( Kopete::Password *target );
+
+ /**
+ * Returns true if the information in the widget is valid, false if it is not.
+ * Currently the only way this can fail is if the password is too long.
+ * @todo this should return an enum of failures.
+ */
+ bool validate();
+
+ /**
+ * Returns the string currently in the input box in the widget
+ */
+ QString password() const;
+ /**
+ * Returns a boolean indicating whether the Remember Password checkbox is checked.
+ * Result is undefined if the Remember Password field is in the 'no change' state
+ * because the user has not (yet) opened the wallet.
+ */
+ bool remember() const;
+
+ /**
+ * Set the password stored in the widget.
+ * @param pass The text to place in the password field.
+ */
+ void setPassword( const QString &pass );
+
+signals:
+ /**
+ * Emitted when the information stored in this widget changes
+ */
+ void changed();
+
+public slots:
+ /** @internal */
+ void receivePassword( const QString & );
+
+private slots:
+ void slotRememberChanged();
+
+private:
+ class Private;
+ Private *d;
+};
+
+}
+
+}
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/kopetepasswordwidgetbase.ui b/kopete/libkopete/ui/kopetepasswordwidgetbase.ui
new file mode 100644
index 00000000..5f6b665a
--- /dev/null
+++ b/kopete/libkopete/ui/kopetepasswordwidgetbase.ui
@@ -0,0 +1,98 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KopetePasswordWidgetBase</class>
+<author>Richard Smith &lt;kde@metafoo.co.uk&gt;</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KopetePasswordWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>497</width>
+ <height>50</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mRemembered</cstring>
+ </property>
+ <property name="text">
+ <string>Remember password</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this and enter your password below if you would like your password to be stored in your wallet, so Kopete does not have to ask you for it each time it is needed.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Password:</string>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="1" column="2">
+ <property name="name">
+ <cstring>mPassword</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter your password here.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter your password here. If you would rather not save your password, uncheck the Remember password checkbox above; you will then be prompted for your password whenever it is needed.</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>mRemembered</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/kopetestdaction.cpp b/kopete/libkopete/ui/kopetestdaction.cpp
new file mode 100644
index 00000000..e6731485
--- /dev/null
+++ b/kopete/libkopete/ui/kopetestdaction.cpp
@@ -0,0 +1,129 @@
+/*
+ kopetestdaction.cpp - Kopete Standard Actionds
+
+ Copyright (c) 2001-2002 by Ryan Cumming <ryan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2001-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopetestdaction.h"
+
+#include <qapplication.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kguiitem.h>
+#include <klocale.h>
+#include <ksettings/dialog.h>
+#include <kstdaction.h>
+#include <kstdguiitem.h>
+#include <kwin.h>
+#include <kcmultidialog.h>
+
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+
+KSettings::Dialog *KopetePreferencesAction::s_settingsDialog = 0L;
+
+KopetePreferencesAction::KopetePreferencesAction( KActionCollection *parent, const char *name )
+#if KDE_IS_VERSION( 3, 3, 90 )
+: KAction( KStdGuiItem::configure(), 0, 0, 0, parent, name )
+#else
+: KAction( KGuiItem( i18n( "&Configure Kopete..." ),
+ QString::fromLatin1( "configure" ) ), 0, 0, 0, parent, name )
+#endif
+{
+ connect( this, SIGNAL( activated() ), this, SLOT( slotShowPreferences() ) );
+}
+
+KopetePreferencesAction::~KopetePreferencesAction()
+{
+}
+
+void KopetePreferencesAction::slotShowPreferences()
+{
+ // FIXME: Use static deleter - Martijn
+ if ( !s_settingsDialog )
+ s_settingsDialog = new KSettings::Dialog( KSettings::Dialog::Static, Kopete::UI::Global::mainWidget() );
+ s_settingsDialog->show();
+
+ s_settingsDialog->dialog()->raise();
+
+ KWin::activateWindow( s_settingsDialog->dialog()->winId() );
+}
+
+KAction * KopeteStdAction::preferences( KActionCollection *parent, const char *name )
+{
+ return new KopetePreferencesAction( parent, name );
+}
+
+KAction * KopeteStdAction::chat( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "Start &Chat..." ), QString::fromLatin1( "mail_generic" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::sendMessage( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "&Send Single Message..." ), QString::fromLatin1( "mail_generic" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::contactInfo( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "User &Info" ), QString::fromLatin1( "messagebox_info" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::sendFile( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "Send &File..." ), QString::fromLatin1( "attach" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::viewHistory( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "View &History..." ), QString::fromLatin1( "history" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::addGroup( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "&Create Group..." ), QString::fromLatin1( "folder" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::changeMetaContact( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "Cha&nge Meta Contact..." ), QString::fromLatin1( "move" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::deleteContact( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "&Delete Contact" ), QString::fromLatin1( "delete_user" ), Qt::Key_Delete, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::changeAlias( const QObject *recvr, const char *slot, QObject *parent, const char *name )
+{
+ return new KAction( i18n( "Change A&lias..." ), QString::fromLatin1( "signature" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::blockContact( const QObject *recvr, const char *slot, QObject* parent, const char *name )
+{
+ return new KAction( i18n( "&Block Contact" ), QString::fromLatin1( "player_pause" ), 0, recvr, slot, parent, name );
+}
+
+KAction * KopeteStdAction::unblockContact( const QObject *recvr, const char *slot, QObject* parent, const char *name )
+{
+ return new KAction( i18n( "Un&block Contact" ), QString::fromLatin1( "player_play" ), 0, recvr, slot, parent, name );
+}
+
+#include "kopetestdaction.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/kopetestdaction.h b/kopete/libkopete/ui/kopetestdaction.h
new file mode 100644
index 00000000..8f06d296
--- /dev/null
+++ b/kopete/libkopete/ui/kopetestdaction.h
@@ -0,0 +1,119 @@
+/*
+ kopetestdaction.h - Kopete Standard Actionds
+
+ Copyright (c) 2001-2002 by Ryan Cumming <bodnar42@phalynx.dhs.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETESTDACTION_H
+#define KOPETESTDACTION_H
+
+#undef KDE_NO_COMPAT
+#include <kaction.h>
+#include <qobject.h>
+
+#include "kopete_export.h"
+
+/**
+ * @author Ryan Cumming <bodnar42@phalynx.dhs.org>
+ */
+class KOPETE_EXPORT KopeteStdAction
+{
+public:
+ /**
+ * Standard action to start a chat
+ */
+ static KAction *chat( const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0 );
+ /**
+ * Standard action to send a single message
+ */
+ static KAction *sendMessage(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to open a user info dialog
+ */
+ static KAction *contactInfo(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to open a history dialog or something similar
+ */
+ static KAction *viewHistory(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to initiate sending a file to a contact
+ */
+ static KAction *sendFile(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to change a contacts @ref Kopete::MetaContact
+ */
+ static KAction *changeMetaContact(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to add a group
+ */
+ static KAction *addGroup(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to delete a contact
+ */
+ static KAction *deleteContact(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to change a contact alias/nickname in your contactlist
+ */
+ static KAction *changeAlias(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to block a contact
+ */
+ static KAction *blockContact(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+ /**
+ * Standard action to unblock a contact
+ */
+ static KAction *unblockContact(const QObject *recvr, const char *slot,
+ QObject* parent, const char *name = 0);
+
+ /**
+ * Return an action to change the Kopete preferences.
+ *
+ * The object has no signal/slot, the prefs are automatically shown
+ */
+ static KAction *preferences(KActionCollection *parent, const char *name = 0);
+};
+
+
+namespace KSettings
+{
+ class Dialog;
+}
+
+class KOPETE_EXPORT KopetePreferencesAction : public KAction
+{
+ Q_OBJECT
+
+ public:
+ KopetePreferencesAction( KActionCollection *parent, const char *name = 0 );
+ ~KopetePreferencesAction();
+
+ protected slots:
+ void slotShowPreferences();
+ private:
+ static KSettings::Dialog *s_settingsDialog;
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/libkopete/ui/kopeteview.cpp b/kopete/libkopete/ui/kopeteview.cpp
new file mode 100644
index 00000000..91f1fa9c
--- /dev/null
+++ b/kopete/libkopete/ui/kopeteview.cpp
@@ -0,0 +1,52 @@
+/*
+ kopeteview.cpp - View Abstract Class
+
+ Copyright (c) 2003 by Jason Keirstead
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteview.h"
+
+KopeteView::KopeteView( Kopete::ChatSession *manager, Kopete::ViewPlugin *plugin )
+ : m_manager(manager), m_plugin(plugin)
+{
+}
+
+Kopete::ChatSession *KopeteView::msgManager() const
+{
+ return m_manager;
+}
+
+void KopeteView::clear()
+{
+ //Do nothing
+}
+
+void KopeteView::appendMessages(QValueList<Kopete::Message> msgs)
+{
+ QValueList<Kopete::Message>::iterator it;
+ for ( it = msgs.begin(); it != msgs.end(); ++it )
+ {
+ appendMessage(*it);
+ }
+
+}
+
+Kopete::ViewPlugin *KopeteView::plugin()
+{
+ return m_plugin;
+}
+
+KopeteView::~ KopeteView( )
+{
+}
diff --git a/kopete/libkopete/ui/kopeteview.h b/kopete/libkopete/ui/kopeteview.h
new file mode 100644
index 00000000..47320546
--- /dev/null
+++ b/kopete/libkopete/ui/kopeteview.h
@@ -0,0 +1,176 @@
+/*
+ kopeteview.h - View Manager
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2004 by Matt Rogers <matt.rogers@kdemail.net>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef KOPETEVIEW_H
+#define KOPETEVIEW_H
+
+#include "kopetemessage.h"
+#include <qvaluelist.h>
+#include "kopete_export.h"
+
+namespace Kopete
+{
+ class ViewPlugin;
+}
+
+/**
+ * @author Jason Keirstead
+ *
+ * Abstract parent class for all types of views used for messaging.These view objects
+ * are provided by a @ref Kopete::ViewPlugin
+ *
+ * @see Kopete::ViewPlugin
+ */
+class KOPETE_EXPORT KopeteView
+{
+ public:
+ /**
+ * constructor
+ */
+ KopeteView( Kopete::ChatSession *manager, Kopete::ViewPlugin *parent );
+ virtual ~KopeteView();
+
+ /**
+ * @brief Returns the message currently in the edit area
+ * @return The Kopete::Message object containing the message
+ */
+ virtual Kopete::Message currentMessage() = 0;
+ /**
+ * Set the message that the view is currently editing.
+ * @param newMessage The Kopete::Message object containing the message to be edited.
+ */
+ virtual void setCurrentMessage( const Kopete::Message &newMessage ) = 0;
+
+ /**
+ * @brief Get the message manager
+ * @return The Kopete::ChatSession that the view is in communication with.
+ */
+ Kopete::ChatSession *msgManager() const;
+
+ /**
+ * @brief add a message to the view
+ *
+ * The message gets added at the end of the view and is automatically
+ * displayed. Classes that inherit from KopeteView should make this a slot.
+ */
+ virtual void appendMessage( Kopete::Message & ) = 0;
+
+ /**
+ * @brief append multiple messages to the view
+ *
+ * This function does the same thing as the above function but
+ * can be reimplemented if it is faster to apend several messages
+ * in the same time.
+ *
+ * The default implementation just call @ref appendMessage() X times
+ */
+ virtual void appendMessages( QValueList<Kopete::Message> );
+
+ /**
+ * @brief Raises the view above other windows
+ * @param activate change the focus to the window
+ */
+ virtual void raise(bool activate = false) = 0;
+
+ /**
+ * @brief Clear the buffer
+ */
+ virtual void clear();
+
+ /**
+ * @brief Make the view visible
+ *
+ * Makes the view visible if it is currently hidden.
+ */
+ virtual void makeVisible() = 0;
+
+ /**
+ * @brief Close this view
+ */
+ virtual bool closeView( bool force = false ) = 0;
+
+ /**
+ * @brief Get the current visibility of the view
+ * @return Whether the view is visible or not.
+ */
+ virtual bool isVisible() = 0;
+
+ /**
+ * @brief Get the view widget
+ *
+ * Can be reimplemented to return this if derived object is a widget
+ */
+ virtual QWidget *mainWidget() = 0;
+
+ /**
+ * @brief Inform the view the message was sent successfully
+ *
+ * This should be reimplemented as a SLOT in any derived objects
+ */
+ virtual void messageSentSuccessfully() = 0;
+
+ /**
+ * @brief Register a handler for the context menu
+ *
+ * Plugins should call this slot at view creation to register
+ * themselves as handlers for the context menu of this view. Plugins
+ * can attach to the viewCreated signal of KopeteMessageManagerFactory
+ * to know when views are created.
+ *
+ * A view does not need to implement this method unless they have context
+ * menus that can be extended
+ *
+ * @param target A target QObject for the contextMenuEvent signal of the view
+ * @param slot A slot that matches the signature ( QString&, KPopupMenu *)
+ */
+ virtual void registerContextMenuHandler( QObject *target, const char*slot ){ Q_UNUSED(target); Q_UNUSED(slot); };
+
+ /**
+ * @brief Register a handler for the tooltip
+ *
+ * Plugins should call this slot at view creation to register
+ * themselves as handlers for the tooltip of this view. Plugins
+ * can attach to the viewCreated signal of KopeteMessageManagerFactory
+ * to know when views are created.
+ *
+ * A view does not need to impliment this method unless it has the ability
+ * to show tooltips
+ *
+ * @param target A target QObject for the contextMenuEvent signal of the view
+ * @param slot A slot that matches the signature ( QString&, KPopupMenu *)
+ */
+ virtual void registerTooltipHandler( QObject *target, const char*slot ){ Q_UNUSED(target); Q_UNUSED(slot); };
+
+ /**
+ * @brief Returns the Kopete::ViewPlugin responsible for this view
+ *
+ * KopeteView objects are created by plugins. This returns a pointer to the plugin
+ * that created this view. You can use this to infer other information on this view
+ * and it's capabilities.
+ */
+ Kopete::ViewPlugin *plugin();
+
+ protected:
+ /**
+ * a pointer to the Kopete::ChatSession given in the constructor
+ */
+ Kopete::ChatSession *m_manager;
+ Kopete::ViewPlugin *m_plugin;
+};
+
+#endif
diff --git a/kopete/libkopete/ui/kopeteviewplugin.cpp b/kopete/libkopete/ui/kopeteviewplugin.cpp
new file mode 100644
index 00000000..b358e547
--- /dev/null
+++ b/kopete/libkopete/ui/kopeteviewplugin.cpp
@@ -0,0 +1,28 @@
+/*
+ kopeteviewplugin.cpp - View Manager
+
+ Copyright (c) 2005 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kopeteviewplugin.h"
+
+Kopete::ViewPlugin::ViewPlugin( KInstance *instance, QObject *parent, const char *name ) :
+ Kopete::Plugin( instance, parent, name )
+{
+
+}
+
+void Kopete::ViewPlugin::aboutToUnload()
+{
+ emit readyForUnload();
+}
diff --git a/kopete/libkopete/ui/kopeteviewplugin.h b/kopete/libkopete/ui/kopeteviewplugin.h
new file mode 100644
index 00000000..e7797d56
--- /dev/null
+++ b/kopete/libkopete/ui/kopeteviewplugin.h
@@ -0,0 +1,59 @@
+/*
+ kopeteviewplugin.h - View Manager
+
+ Copyright (c) 2005 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KOPETEVIEWPLUGIN_H
+#define KOPETEVIEWPLUGIN_H
+
+#include "kopeteplugin.h"
+
+class KopeteView;
+
+namespace Kopete
+{
+
+class ChatSession;
+
+/**
+ * @author Jason Keirstead
+ *
+ * @brief Factory plugin for creating KopeteView objects.
+ *
+ * Kopete ships with two of these currently, a Chat Window view plugin, and
+ * an Email Window view plugin.
+ *
+ */
+class KOPETE_EXPORT ViewPlugin : public Plugin
+{
+ public:
+ /**
+ * @brief Create and initialize the plugin
+ */
+ ViewPlugin( KInstance *instance, QObject *parent = 0L, const char *name = 0L );
+
+ /**
+ * @brief Creates a view to be associated with the passed in session
+ */
+ virtual KopeteView *createView( ChatSession * /*session*/ ){ return 0L; };
+
+ /**
+ * @brief Reimplemented from Kopete::Plugin
+ */
+ virtual void aboutToUnload();
+};
+
+}
+
+#endif
diff --git a/kopete/libkopete/ui/kopetewidgets.cpp b/kopete/libkopete/ui/kopetewidgets.cpp
new file mode 100644
index 00000000..093ee48e
--- /dev/null
+++ b/kopete/libkopete/ui/kopetewidgets.cpp
@@ -0,0 +1,131 @@
+/**
+* This file was autogenerated by makekdewidgets. Any changes will be lost!
+* The generated code in this file is licensed under the same license that the
+* input file.
+*/
+#include <qwidgetplugin.h>
+
+#include <kinstance.h>
+#include <addressbooklinkwidget.h>
+#include <kopetelistview.h>
+#include <kopetelistviewsearchline.h>
+#ifndef EMBED_IMAGES
+#include <kstandarddirs.h>
+#endif
+
+class KopeteWidgets : public QWidgetPlugin
+{
+public:
+ KopeteWidgets();
+
+ virtual ~KopeteWidgets();
+
+ virtual QStringList keys() const
+ {
+ QStringList result;
+ for (WidgetInfos::ConstIterator it = m_widgets.begin(); it != m_widgets.end(); ++it)
+ result << it.key();
+ return result;
+ }
+
+ virtual QWidget *create(const QString &key, QWidget *parent = 0, const char *name = 0);
+
+ virtual QIconSet iconSet(const QString &key) const
+ {
+#ifdef EMBED_IMAGES
+ QPixmap pix(m_widgets[key].iconSet);
+#else
+ QPixmap pix(locate( "data",
+ QString::fromLatin1("kopetewidgets/pics/") + m_widgets[key].iconSet));
+#endif
+ return QIconSet(pix);
+ }
+
+ virtual bool isContainer(const QString &key) const { return m_widgets[key].isContainer; }
+
+ virtual QString group(const QString &key) const { return m_widgets[key].group; }
+
+ virtual QString includeFile(const QString &key) const { return m_widgets[key].includeFile; }
+
+ virtual QString toolTip(const QString &key) const { return m_widgets[key].toolTip; }
+
+ virtual QString whatsThis(const QString &key) const { return m_widgets[key].whatsThis; }
+private:
+ struct WidgetInfo
+ {
+ QString group;
+#ifdef EMBED_IMAGES
+ QPixmap iconSet;
+#else
+ QString iconSet;
+#endif
+ QString includeFile;
+ QString toolTip;
+ QString whatsThis;
+ bool isContainer;
+ };
+ typedef QMap<QString, WidgetInfo> WidgetInfos;
+ WidgetInfos m_widgets;
+};
+KopeteWidgets::KopeteWidgets()
+{
+ WidgetInfo widget;
+
+ widget.group = QString::fromLatin1("Input (Kopete)");
+#ifdef EMBED_IMAGES
+ widget.iconSet = QPixmap(kopete__ui__addressbooklinkwidget_xpm);
+#else
+ widget.iconSet = QString::fromLatin1("kopete__ui__addressbooklinkwidget.png");
+#endif
+ widget.includeFile = QString::fromLatin1("addressbooklinkwidget.h");
+ widget.toolTip = QString::fromLatin1("Address Book Link Widget (Kopete)");
+ widget.whatsThis = QString::fromLatin1("KABC::Addressee display/selector");
+ widget.isContainer = false;
+ m_widgets.insert(QString::fromLatin1("Kopete::UI::AddressBookLinkWidget"), widget);
+
+ widget.group = QString::fromLatin1("Views (Kopete)");
+#ifdef EMBED_IMAGES
+ widget.iconSet = QPixmap(kopete__ui__listview__listview_xpm);
+#else
+ widget.iconSet = QString::fromLatin1("kopete__ui__listview__listview.png");
+#endif
+ widget.includeFile = QString::fromLatin1("kopetelistview.h");
+ widget.toolTip = QString::fromLatin1("List View (Kopete)");
+ widget.whatsThis = QString::fromLatin1("A component capable list view widget.");
+ widget.isContainer = false;
+ m_widgets.insert(QString::fromLatin1("Kopete::UI::ListView::ListView"), widget);
+
+ widget.group = QString::fromLatin1("Input (Kopete)");
+#ifdef EMBED_IMAGES
+ widget.iconSet = QPixmap(kopete__ui__listview__searchline_xpm);
+#else
+ widget.iconSet = QString::fromLatin1("kopete__ui__listview__searchline.png");
+#endif
+ widget.includeFile = QString::fromLatin1("kopetelistviewsearchline.h");
+ widget.toolTip = QString::fromLatin1("List View Search Line (Kopete)");
+ widget.whatsThis = QString::fromLatin1("Search line able to use Kopete custom list View.");
+ widget.isContainer = false;
+ m_widgets.insert(QString::fromLatin1("Kopete::UI::ListView::SearchLine"), widget);
+
+ new KInstance("kopetewidgets");
+}
+KopeteWidgets::~KopeteWidgets()
+{
+
+}
+QWidget *KopeteWidgets::create(const QString &key, QWidget *parent, const char *name)
+{
+
+ if (key == QString::fromLatin1("Kopete::UI::AddressBookLinkWidget"))
+ return new Kopete::UI::AddressBookLinkWidget(parent, name);
+
+ if (key == QString::fromLatin1("Kopete::UI::ListView::ListView"))
+ return new Kopete::UI::ListView::ListView(parent, name);
+
+ if (key == QString::fromLatin1("Kopete::UI::ListView::SearchLine"))
+ return new Kopete::UI::ListView::SearchLine(parent, 0, name);
+
+ return 0;
+}
+KDE_Q_EXPORT_PLUGIN(KopeteWidgets)
+
diff --git a/kopete/libkopete/ui/metacontactselectorwidget.cpp b/kopete/libkopete/ui/metacontactselectorwidget.cpp
new file mode 100644
index 00000000..d9c75308
--- /dev/null
+++ b/kopete/libkopete/ui/metacontactselectorwidget.cpp
@@ -0,0 +1,287 @@
+/*
+ MetaContactSelectorWidget
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+#include <qvbox.h>
+#include <qimage.h>
+#include <qpixmap.h>
+#include <qpainter.h>
+#include <qlayout.h>
+#include <qvaluelist.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include <kdeversion.h>
+#include <kinputdialog.h>
+#include <kpushbutton.h>
+#include <kactivelabel.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <klistviewsearchline.h>
+
+#include "kopetelistview.h"
+#include "kopetelistviewsearchline.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "metacontactselectorwidget_base.h"
+#include "metacontactselectorwidget.h"
+
+using namespace Kopete::UI::ListView;
+
+namespace Kopete
+{
+namespace UI
+{
+
+class MetaContactSelectorWidgetLVI::Private
+{
+public:
+ Kopete::MetaContact *metaContact;
+ ImageComponent *metaContactPhoto;
+ ImageComponent *metaContactIcon;
+ DisplayNameComponent *nameText;
+ TextComponent *extraText;
+ BoxComponent *contactIconBox;
+ BoxComponent *spacerBox;
+ int photoSize;
+ int contactIconSize;
+};
+
+
+MetaContactSelectorWidgetLVI::MetaContactSelectorWidgetLVI(Kopete::MetaContact *mc, QListView *parent, QObject *owner, const char *name) : Kopete::UI::ListView::Item(parent, owner, name) , d( new Private() )
+{
+ d->metaContact = mc;
+ d->photoSize = 60;
+
+ connect( d->metaContact, SIGNAL( photoChanged() ),
+ SLOT( slotPhotoChanged() ) );
+ connect( d->metaContact, SIGNAL( displayNameChanged(const QString&, const QString&) ),
+ SLOT( slotDisplayNameChanged() ) );
+ buildVisualComponents();
+}
+
+Kopete::MetaContact* MetaContactSelectorWidgetLVI::metaContact()
+{
+ return d->metaContact;
+}
+
+void MetaContactSelectorWidgetLVI::slotDisplayNameChanged()
+{
+ if ( d->nameText )
+ {
+ d->nameText->setText( d->metaContact->displayName() );
+
+ // delay the sort if we can
+ if ( ListView::ListView *lv = dynamic_cast<ListView::ListView *>( listView() ) )
+ lv->delayedSort();
+ else
+ listView()->sort();
+ }
+}
+
+QString MetaContactSelectorWidgetLVI::text ( int /* column */ ) const
+{
+ return d->metaContact->displayName();
+}
+
+void MetaContactSelectorWidgetLVI::slotPhotoChanged()
+{
+ QPixmap photoPixmap;
+ QImage photoImg = d->metaContact->photo();
+ if ( !photoImg.isNull() && (photoImg.width() > 0) && (photoImg.height() > 0) )
+ {
+ int photoSize = d->photoSize;
+
+ photoImg = photoImg.smoothScale( photoSize, photoSize, QImage::ScaleMin ) ;
+
+ // draw a 1 pixel black border
+ photoPixmap = photoImg;
+ QPainter p(&photoPixmap);
+ p.setPen(Qt::black);
+ p.drawLine(0, 0, photoPixmap.width()-1, 0);
+ p.drawLine(0, photoPixmap.height()-1, photoPixmap.width()-1, photoPixmap.height()-1);
+ p.drawLine(0, 0, 0, photoPixmap.height()-1);
+ p.drawLine(photoPixmap.width()-1, 0, photoPixmap.width()-1, photoPixmap.height()-1);
+ }
+ else
+ {
+ // if no photo use the smilie icon
+ photoPixmap=SmallIcon(d->metaContact->statusIcon(), d->photoSize);
+ }
+ d->metaContactPhoto->setPixmap( photoPixmap, false);
+}
+
+void MetaContactSelectorWidgetLVI::buildVisualComponents()
+{
+ // empty...
+ while ( component( 0 ) )
+ delete component( 0 );
+
+ d->nameText = 0L;
+ d->metaContactPhoto = 0L;
+ d->extraText = 0L;
+ d->contactIconSize = 16;
+ d->photoSize = 48;
+
+ Component *hbox = new BoxComponent( this, BoxComponent::Horizontal );
+ d->spacerBox = new BoxComponent( hbox, BoxComponent::Horizontal );
+
+ d->contactIconSize = IconSize( KIcon::Small );
+ Component *imageBox = new BoxComponent( hbox, BoxComponent::Vertical );
+ new VSpacerComponent( imageBox );
+ // include borders in size
+ d->metaContactPhoto = new ImageComponent( imageBox, d->photoSize + 2 , d->photoSize + 2 );
+ new VSpacerComponent( imageBox );
+ Component *vbox = new BoxComponent( hbox, BoxComponent::Vertical );
+ d->nameText = new DisplayNameComponent( vbox );
+ d->extraText = new TextComponent( vbox );
+
+ Component *box = new BoxComponent( vbox, BoxComponent::Horizontal );
+ d->contactIconBox = new BoxComponent( box, BoxComponent::Horizontal );
+
+ slotUpdateContactBox();
+ slotDisplayNameChanged();
+ slotPhotoChanged();
+}
+
+void MetaContactSelectorWidgetLVI::slotUpdateContactBox()
+{
+ QPtrList<Kopete::Contact> contacts = d->metaContact->contacts();
+ for(Kopete::Contact *c = contacts.first(); c; c = contacts.next())
+ {
+ new ContactComponent(d->contactIconBox, c, IconSize( KIcon::Small ));
+ }
+}
+
+class MetaContactSelectorWidget::Private
+{
+public:
+ MetaContactSelectorWidget_Base *widget;
+ QValueList<Kopete::MetaContact *> excludedMetaContacts;
+};
+
+
+MetaContactSelectorWidget::MetaContactSelectorWidget( QWidget *parent, const char *name )
+ : QWidget( parent, name ), d( new Private() )
+{
+ QBoxLayout *l = new QVBoxLayout(this);
+ d->widget = new MetaContactSelectorWidget_Base(this);
+ l->addWidget(d->widget);
+
+ connect( d->widget->metaContactListView, SIGNAL( clicked(QListViewItem * ) ),
+ SIGNAL( metaContactListClicked( QListViewItem * ) ) );
+ connect( d->widget->metaContactListView, SIGNAL( selectionChanged( QListViewItem * ) ),
+ SIGNAL( metaContactListClicked( QListViewItem * ) ) );
+ connect( d->widget->metaContactListView, SIGNAL( spacePressed( QListViewItem * ) ),
+ SIGNAL( metaContactListClicked( QListViewItem * ) ) );
+
+ connect( Kopete::ContactList::self(), SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), this, SLOT( slotLoadMetaContacts() ) );
+
+ d->widget->kListViewSearchLine->setListView(d->widget->metaContactListView);
+ d->widget->metaContactListView->setFullWidth( true );
+ d->widget->metaContactListView->header()->hide();
+ d->widget->metaContactListView->setColumnWidthMode(0, QListView::Maximum);
+ slotLoadMetaContacts();
+}
+
+
+MetaContactSelectorWidget::~MetaContactSelectorWidget()
+{
+ disconnect( Kopete::ContactList::self(), SIGNAL( metaContactAdded( Kopete::MetaContact * ) ), this, SLOT( slotLoadMetaContacts() ) );
+}
+
+
+Kopete::MetaContact* MetaContactSelectorWidget::metaContact()
+{
+ MetaContactSelectorWidgetLVI *item = 0L;
+ item = static_cast<MetaContactSelectorWidgetLVI *>( d->widget->metaContactListView->selectedItem() );
+
+ if ( item )
+ return item->metaContact();
+
+ return 0L;
+}
+
+void MetaContactSelectorWidget::selectMetaContact( Kopete::MetaContact *mc )
+{
+ // iterate trough list view
+ QListViewItemIterator it( d->widget->metaContactListView );
+ while( it.current() )
+ {
+ MetaContactSelectorWidgetLVI *item = (MetaContactSelectorWidgetLVI *) it.current();
+ if (!item)
+ continue;
+
+ if ( mc == item->metaContact() )
+ {
+ // select the contact item
+ d->widget->metaContactListView->setSelected( item, true );
+ d->widget->metaContactListView->ensureItemVisible( item );
+ }
+ ++it;
+ }
+}
+
+void MetaContactSelectorWidget::excludeMetaContact( Kopete::MetaContact *mc )
+{
+ if( d->excludedMetaContacts.findIndex(mc) == -1 )
+ {
+ d->excludedMetaContacts.append(mc);
+ }
+ slotLoadMetaContacts();
+}
+
+bool MetaContactSelectorWidget::metaContactSelected()
+{
+ return d->widget->metaContactListView->selectedItem() ? true : false;
+}
+
+/** Read in metacontacts from contactlist */
+void MetaContactSelectorWidget::slotLoadMetaContacts()
+{
+ d->widget->metaContactListView->clear();
+
+ QPtrList<Kopete::MetaContact> metaContacts = Kopete::ContactList::self()->metaContacts();
+ for( Kopete::MetaContact *mc = metaContacts.first(); mc ; mc = metaContacts.next() )
+ {
+ if( !mc->isTemporary() && (d->excludedMetaContacts.findIndex(mc) == -1) )
+ {
+ new MetaContactSelectorWidgetLVI(mc, d->widget->metaContactListView);
+ }
+ }
+
+ d->widget->metaContactListView->sort();
+}
+
+void MetaContactSelectorWidget::setLabelMessage( const QString &msg )
+{
+ d->widget->lblHeader->setText(msg);
+}
+
+} // namespace UI
+} // namespace Kopete
+
+#include "metacontactselectorwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/metacontactselectorwidget.h b/kopete/libkopete/ui/metacontactselectorwidget.h
new file mode 100644
index 00000000..1c0a21ff
--- /dev/null
+++ b/kopete/libkopete/ui/metacontactselectorwidget.h
@@ -0,0 +1,103 @@
+/*
+ MetaContactSelectorWidget
+
+ Copyright (c) 2005 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MetaContactSelectorWidget_H
+#define MetaContactSelectorWidget_H
+
+#include <kdemacros.h>
+#include <qwidget.h>
+#include "kopetelistviewitem.h"
+#include "kopete_export.h"
+
+class Kopete::MetaContact;
+
+namespace Kopete
+{
+namespace UI
+{
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ * This class provides a widget which allows easy selection
+ * of available Kopete metacontacts.
+ */
+class KOPETE_EXPORT MetaContactSelectorWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ MetaContactSelectorWidget( QWidget *parent = 0, const char *name = 0 );
+ ~MetaContactSelectorWidget();
+ Kopete::MetaContact* metaContact();
+ /**
+ * sets the widget label message
+ * example: Please select a contact
+ * or, Choose a contact to delete
+ */
+ void setLabelMessage( const QString &msg );
+ /**
+ * pre-selects a contact
+ */
+ void selectMetaContact( Kopete::MetaContact *mc );
+ /**
+ * excludes a metacontact from being shown in the list
+ * if the metacontact is already excluded, do nothing
+ */
+ void excludeMetaContact( Kopete::MetaContact *mc );
+ /**
+ * @return true if there is a contact selected
+ */
+ bool metaContactSelected();
+protected slots:
+ /**
+ * Utility function, populates the metacontact list
+ */
+ void slotLoadMetaContacts();
+signals:
+ void metaContactListClicked( QListViewItem *mc );
+private:
+ class Private;
+ Private *d;
+};
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+
+class MetaContactSelectorWidgetLVI : public Kopete::UI::ListView::Item
+{
+ Q_OBJECT
+public:
+ MetaContactSelectorWidgetLVI(Kopete::MetaContact *mc, QListView *parent, QObject *owner = 0, const char *name = 0 );
+ Kopete::MetaContact* metaContact();
+ virtual QString text ( int column ) const;
+protected slots:
+ void slotPhotoChanged();
+ void slotDisplayNameChanged();
+ void buildVisualComponents();
+ void slotUpdateContactBox();
+private:
+ class Private;
+ Private *d;
+};
+
+} // namespace UI
+} // namespace Kopete
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/metacontactselectorwidget_base.ui b/kopete/libkopete/ui/metacontactselectorwidget_base.ui
new file mode 100644
index 00000000..bc1a38eb
--- /dev/null
+++ b/kopete/libkopete/ui/metacontactselectorwidget_base.ui
@@ -0,0 +1,107 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>MetaContactSelectorWidget_Base</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MetaContactSelectorWidget_Base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>427</width>
+ <height>306</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Select Contact</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KActiveLabel">
+ <property name="name">
+ <cstring>lblHeader</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <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>lblSearch</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>S&amp;earch:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ <widget class="Kopete::UI::ListView::SearchLine">
+ <property name="name">
+ <cstring>kListViewSearchLine</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::ListView::ListView">
+ <column>
+ <property name="text">
+ <string>Meta Contact</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>metaContactListView</cstring>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<includes>
+ <include location="local" impldecl="in declaration">kopetelistviewsearchline.h</include>
+ <include location="local" impldecl="in declaration">kopetelistview.h</include>
+ <include location="global" impldecl="in declaration">qheader.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kactivelabel.h</includehint>
+ <includehint>kopetelistviewsearchline.h</includehint>
+ <includehint>kopetelistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/libkopete/ui/userinfodialog.cpp b/kopete/libkopete/ui/userinfodialog.cpp
new file mode 100644
index 00000000..a25454a9
--- /dev/null
+++ b/kopete/libkopete/ui/userinfodialog.cpp
@@ -0,0 +1,277 @@
+/*
+ userinfodialog.h
+
+ Copyright (c) 2003 by Zack Rusin <zack@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "userinfodialog.h"
+#include "kopeteuiglobal.h"
+
+#include <khtml_part.h>
+#include <ktextbrowser.h>
+#include <kapplication.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qlabel.h>
+#include <qvbox.h>
+#include <qlayout.h>
+
+namespace Kopete {
+
+struct UserInfoDialog::UserInfoDialogPrivate {
+ QString name;
+ QString id;
+ QString awayMessage;
+ QString status;
+ QString warningLevel;
+ QString onlineSince;
+ QString info;
+ QString address;
+ QString phone;
+ QMap<QString,QString> customFields;
+ QVBoxLayout *topLayout;
+ QWidget *page;
+ DialogStyle style;
+ KHTMLPart *htmlPart;
+
+ KLineEdit *nameEdit;
+ KLineEdit *idEdit;
+ KLineEdit *statusEdit;
+ KLineEdit *warningEdit;
+ KLineEdit *onlineEdit;
+ KLineEdit *addressEdit;
+ KLineEdit *phoneEdit;
+ KTextBrowser *awayBrowser;
+ KTextBrowser *infoBrowser;
+};
+
+UserInfoDialog::UserInfoDialog( const QString& descr )
+: KDialogBase( Kopete::UI::Global::mainWidget(), "userinfodialog", true, i18n( "User Info for %1" ).arg( descr ), KDialogBase::Ok )
+{
+ d = new UserInfoDialogPrivate;
+ d->page = new QWidget( this );
+ setMainWidget( d->page );
+ d->topLayout = new QVBoxLayout( d->page, 0, spacingHint() );
+ d->style = Widget;
+}
+
+UserInfoDialog::~UserInfoDialog()
+{
+ delete d; d=0;
+}
+
+void UserInfoDialog::setStyle( DialogStyle style )
+{
+ d->style = style;
+}
+
+void UserInfoDialog::setName( const QString& name )
+{
+ d->name = name;
+}
+
+void UserInfoDialog::setId( const QString& id )
+{
+ d->id = id;
+}
+
+void UserInfoDialog::setAwayMessage( const QString& msg )
+{
+ d->awayMessage = msg;
+}
+
+void UserInfoDialog::setStatus( const QString& status )
+{
+ d->status = status;
+}
+
+void UserInfoDialog::setWarningLevel(const QString& level )
+{
+ d->warningLevel = level;
+}
+
+void UserInfoDialog::setOnlineSince( const QString& since )
+{
+ d->onlineSince = since;
+}
+
+void UserInfoDialog::setInfo( const QString& info )
+{
+ d->info = info;
+}
+
+void UserInfoDialog::setAddress( const QString& addr )
+{
+ d->address = addr;
+}
+
+void UserInfoDialog::setPhone( const QString& phone )
+{
+ d->phone = phone;
+}
+
+void UserInfoDialog::addCustomField( const QString& /*name*/, const QString& /*txt*/ )
+{
+
+}
+
+void UserInfoDialog::addHTMLText( const QString& /*str*/ )
+{
+
+}
+
+QHBox* UserInfoDialog::addLabelEdit( const QString& label, const QString& text, KLineEdit*& edit )
+{
+ QHBox *box = new QHBox( d->page );
+ new QLabel( label, box );
+ edit = new KLineEdit( box );
+ edit->setAlignment( Qt::AlignHCenter );
+ edit->setText( text );
+ edit->setReadOnly( true );
+ return box;
+}
+
+void UserInfoDialog::fillHTML()
+{
+ d->htmlPart = new KHTMLPart( this );
+
+ QString text;
+ /*
+ if ( d->name.isEmpty() ) {
+ text.append( QString("<div id=\"name\"><b>") + i18n("Name : ") +
+ QString("</b>") );
+ text.append( d->name + QString("</div><br>") );
+ }
+
+ if ( d->id.isEmpty() ) {
+ text.append( "<div id=\"id\"><b>" + i18n("Id : ") + "</b>" );
+ text.append( d->id + "</div><br>" );
+ }
+
+ if ( d->warningLevel.isEmpty() ) {
+ text.append( "<div id=\"warningLevel\"><b>" + i18n("Warning Level : ") + "</b>" );
+ text.append( d->warningLevel + "</div><br>" );
+ }
+
+ if ( d->onlineSince.isEmpty() ) {
+ text.append( "<div id=\"onlineSince\"><b>" + i18n("Online Since : ") + "</b>" );
+ text.append( d->onlineSince + "</div><br>" );
+ }
+
+ if ( d->address.isEmpty() ) {
+ text.append( "<div id=\"address\"><b>" + i18n("Address : ") + "</b>" );
+ text.append( d->address + "</div><br>" );
+ }
+
+ if ( d->phone.isEmpty() ) {
+ text.append( "<div id=\"phone\"><b>" + i18n("Phone : ") + "</b>" );
+ text.append( d->phone + "</div><br>" );
+ }
+
+ if ( d->status.isEmpty() ) {
+ text.append( "<div id=\"status\"><b>" + i18n("Status : ") + "</b>" );
+ text.append( d->status + "</div><br>" );
+ }
+
+ if ( d->awayMessage.isEmpty() ) {
+ text.append( "<div id=\"awayMessage\"><b>" + i18n("Away Message : ") + "</b>" );
+ text.append( d->awayMessage + "</div><br>" );
+ }
+
+ if ( d->info.isEmpty() ) {
+ text.append( "<div id=\"info\"><b>" + i18n("Info : ") + "</b>" );
+ text.append( d->info + "</div><br>" );
+ }
+*/
+ d->htmlPart->setOnlyLocalReferences( true );
+ d->htmlPart->begin();
+ d->htmlPart->write( text );
+ d->htmlPart->end();
+}
+
+void UserInfoDialog::fillWidgets()
+{
+ kdDebug(14010)<<"Creating widgets"<<endl;
+ if ( !d->name.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Name:"), d->name, d->nameEdit ) );
+ }
+
+ if ( !d->id.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Contact ID:"), d->id, d->idEdit ) );
+ }
+
+ if ( !d->status.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Status:"), d->status, d->statusEdit ) );
+ }
+
+ if ( !d->warningLevel.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Warning level:"), d->warningLevel, d->warningEdit ) );
+ }
+
+ if ( !d->onlineSince.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Online since:"), d->onlineSince, d->onlineEdit ) );
+ }
+
+ if ( !d->address.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Address:"), d->address, d->addressEdit ) );
+ }
+
+ if ( !d->phone.isEmpty() ) {
+ d->topLayout->addWidget( addLabelEdit( i18n("Phone:"), d->phone, d->phoneEdit ) );
+ }
+
+ if ( !d->awayMessage.isEmpty() ) {
+ QVBox *awayBox = new QVBox( d->page );
+ new QLabel( i18n("Away message:"), awayBox );
+ d->awayBrowser = new KTextBrowser( awayBox );
+ d->awayBrowser->setText( d->awayMessage );
+ d->topLayout->addWidget( awayBox );
+ }
+
+ if ( !d->info.isEmpty() ) {
+ QVBox *infoBox = new QVBox( d->page );
+ new QLabel( i18n("User info:"), infoBox );
+ d->infoBrowser = new KTextBrowser( infoBox );
+ d->infoBrowser->setText( d->info );
+ d->topLayout->addWidget( infoBox );
+ }
+}
+
+void UserInfoDialog::setStyleSheet( const QString& /*css*/ )
+{
+}
+
+void UserInfoDialog::create()
+{
+ if ( d->style == HTML ) {
+ fillHTML();
+ } else {
+ fillWidgets();
+ }
+}
+
+void UserInfoDialog::show()
+{
+ create();
+ KDialogBase::show();
+}
+
+}
+
+#include "userinfodialog.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/libkopete/ui/userinfodialog.h b/kopete/libkopete/ui/userinfodialog.h
new file mode 100644
index 00000000..7df19f4f
--- /dev/null
+++ b/kopete/libkopete/ui/userinfodialog.h
@@ -0,0 +1,91 @@
+/*
+ userinfodialog.h
+
+ Copyright (c) 2003 by Zack Rusin <zack@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef USERINFODIALOG_H
+#define USERINFODIALOG_H
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+#include "kopete_export.h"
+
+class KLineEdit;
+
+namespace Kopete {
+
+ class KOPETE_EXPORT UserInfoDialog : public KDialogBase
+ {
+ Q_OBJECT
+ public:
+ UserInfoDialog( const QString& descr );
+ virtual ~UserInfoDialog();
+
+
+ /**
+ * Specifies the look of this dialog. If set to HTML only
+ * KHTMLPart will be in the dialog and it's look can be customized
+ * through setStyleSheet
+ * @see setStyleSheet
+ */
+ enum DialogStyle { HTML, Widget };
+ void setStyle( DialogStyle style );
+
+ // The functions below set elements as specified in the name.
+ // If an element is not set it won't be displayed.
+ void setName( const QString& name );
+ void setId( const QString& id );
+ void setAwayMessage( const QString& msg );
+ void setStatus( const QString& status );
+ void setWarningLevel(const QString& level );
+ void setOnlineSince( const QString& since );
+ void setInfo( const QString& info );
+ void setAddress( const QString& addr );
+ void setPhone( const QString& phone );
+
+ void addCustomField( const QString& name, const QString& txt );
+ void addHTMLText( const QString& str );
+
+ ///Shows the dialog
+ virtual void show();
+ protected:
+ /**
+ * This function has to be called after setting all the fields.
+ * It builds the GUI for the dialog. By default show() calls it.
+ */
+ virtual void create();
+ //Fills the dialog HTML if DialogStyle is HTML
+ virtual void fillHTML();
+ //Fills the dialog with widgets if DialogStyle is Widget
+ virtual void fillWidgets();
+
+ /**
+ * If the DialogStyle is set to HTML one can customize the look of this
+ * dialog by setting the right stylesheet. The CSS id elements that can be
+ * customized include : "name", "id", "warningLevel", "onlineSince",
+ * "address", "phone", "status", "awayMessage" and "info".
+ */
+ void setStyleSheet( const QString& css );
+
+ QHBox* addLabelEdit( const QString& label, const QString& text, KLineEdit*& edit );
+
+ private:
+ struct UserInfoDialogPrivate;
+ UserInfoDialogPrivate *d;
+ };
+
+}
+#endif
diff --git a/kopete/libkopete/ui/widgets.cw b/kopete/libkopete/ui/widgets.cw
new file mode 100644
index 00000000..7f3b38dc
--- /dev/null
+++ b/kopete/libkopete/ui/widgets.cw
@@ -0,0 +1,21 @@
+<!DOCTYPE CW><CW>
+<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>
+ </sizepolicy>
+ <pixmap>
+ <data format="XPM.GZ" length="4462">789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495</data>
+ </pixmap>
+ <signal>changed()</signal>
+ </customwidget>
+</customwidgets>
+</CW>
diff --git a/kopete/libkopete/webcamwidget.cpp b/kopete/libkopete/webcamwidget.cpp
new file mode 100644
index 00000000..ae617ad5
--- /dev/null
+++ b/kopete/libkopete/webcamwidget.cpp
@@ -0,0 +1,107 @@
+/*
+ webcamwidget.h - A simple widget for displaying webcam frames
+
+ Copyright (c) 2006 by Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "webcamwidget.h"
+
+#include <qcolor.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+namespace Kopete
+{
+
+WebcamWidget::WebcamWidget( QWidget* parent, const char* name )
+: QWidget( parent, name )
+{
+ clear();
+}
+
+WebcamWidget::~WebcamWidget()
+{
+ // don't do anything either
+}
+
+void WebcamWidget::updatePixmap(const QPixmap& pixmap)
+{
+ mPixmap = pixmap;
+ mText = "";
+
+ QPaintEvent *ev = new QPaintEvent( rect(), true );
+ paintEvent( ev );
+ delete ev;
+}
+
+void WebcamWidget::clear()
+{
+ mText = "";
+ if (!mPixmap.isNull())
+ mPixmap.resize(0,0);
+
+ QPaintEvent *ev = new QPaintEvent( rect(), true );
+ paintEvent( ev );
+ delete ev;
+}
+
+void WebcamWidget::setText(const QString& text)
+{
+ mText = text;
+
+ // for now redraw everything
+ QPaintEvent *ev = new QPaintEvent( rect(), true );
+ paintEvent( ev );
+ delete ev;
+}
+
+void WebcamWidget::paintEvent( QPaintEvent* event )
+{
+ QMemArray<QRect> rects = event->region().rects();
+
+ if (!mPixmap.isNull())
+ {
+ for (unsigned int i = 0; i < rects.count(); ++i)
+ {
+ bitBlt(this, rects[i].topLeft(), &mPixmap, rects[i], Qt::CopyROP, true);
+ }
+ }
+ else
+ {
+ for (unsigned int i = 0; i < rects.count(); ++i)
+ {
+ QColor bgColor = paletteBackgroundColor();
+ QPainter p(this);
+ p.fillRect(rects[i], bgColor);
+ }
+ }
+
+ // TODO: draw the text
+ QPainter p(this);
+ QRect r = p.boundingRect(rect(), Qt::AlignCenter | Qt::WordBreak, mText );
+ if ( !mText.isEmpty() && event->rect().intersects(r))
+ {
+ p.setPen(Qt::black);
+ QRect rec = rect();
+ rec.moveTopLeft(QPoint(1,1));
+ p.drawText(rec, Qt::AlignCenter | Qt::WordBreak, mText, -1);
+
+ rec.moveTopLeft(QPoint(-1,-1));
+ p.setPen(Qt::white);
+ p.drawText(rec, Qt::AlignCenter | Qt::WordBreak, mText, -1);
+ }
+}
+
+} // end namespace Kopete
+
+#include "webcamwidget.moc"
diff --git a/kopete/libkopete/webcamwidget.h b/kopete/libkopete/webcamwidget.h
new file mode 100644
index 00000000..6458f60a
--- /dev/null
+++ b/kopete/libkopete/webcamwidget.h
@@ -0,0 +1,66 @@
+/*
+ webcamwidget.h - A simple widget for displaying webcam frames
+
+ Copyright (c) 2006 by Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef WEBCAMWIDGET_H
+#define WEBCAMWIDGET_H
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qstring.h>
+
+#include "kopete_export.h"
+
+namespace Kopete
+{
+/**
+ * A simple widget to display webcam frames.
+ */
+class KOPETE_EXPORT WebcamWidget : public QWidget
+{
+Q_OBJECT
+public:
+ /**
+ * @brief WebcamWidget constructor.
+ * @param parent The parent widget of this widget
+ * @param name The name for this QObject
+ */
+ WebcamWidget( QWidget* parent = 0, const char* name = 0 );
+ ~WebcamWidget();
+
+ /**
+ * @brief Updates the frame being displayed in the widget
+ * @param pixmap The frame to be displayed
+ */
+ void updatePixmap(const QPixmap& pixmap);
+
+ /**
+ * @brief Clear the widget
+ */
+ void clear();
+
+ /**
+ * @brief Set a text to be displayed in the widget
+ * @param text The text to be displayed
+ */
+ void setText(const QString& text);
+protected slots:
+ void paintEvent( QPaintEvent* event );
+ QPixmap mPixmap;
+ QString mText;
+};
+
+} // end namespace Kopete
+#endif
diff --git a/kopete/plugins/Makefile.am b/kopete/plugins/Makefile.am
new file mode 100644
index 00000000..8b817465
--- /dev/null
+++ b/kopete/plugins/Makefile.am
@@ -0,0 +1,12 @@
+if include_motionautoaway
+MOTIONAUTOAWAY_SUBDIR=motionautoaway
+endif
+
+if include_smpppdcs
+SMPPPDCS_SUBDIR=smpppdcs
+endif
+
+SUBDIRS = latex autoreplace history contactnotes cryptography\
+ connectionstatus translator nowlistening webpresence texteffect\
+ highlight alias $(MOTIONAUTOAWAY_SUBDIR) netmeeting addbookmarks\
+ statistics $(SMPPPDCS_SUBDIR)
diff --git a/kopete/plugins/addbookmarks/Makefile.am b/kopete/plugins/addbookmarks/Makefile.am
new file mode 100644
index 00000000..696ddfb9
--- /dev/null
+++ b/kopete/plugins/addbookmarks/Makefile.am
@@ -0,0 +1,22 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_HEADERS = addbookmarksplugin.h addbookmarkspreferences.h \
+ addbookmarksprefssettings.h addbookmarksprefsui.h
+
+kde_module_LTLIBRARIES = kopete_addbookmarks.la kcm_kopete_addbookmarks.la
+
+kopete_addbookmarks_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_addbookmarks_la_LIBADD = ../../libkopete/libkopete.la
+kopete_addbookmarks_la_SOURCES = addbookmarksplugin.cpp addbookmarksprefssettings.cpp
+
+kcm_kopete_addbookmarks_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_addbookmarks_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+kcm_kopete_addbookmarks_la_SOURCES = addbookmarkspreferences.cpp addbookmarksprefsui.ui \
+ addbookmarksprefssettings.cpp
+
+service_DATA = kopete_addbookmarks.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_addbookmarks_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
diff --git a/kopete/plugins/addbookmarks/addbookmarksplugin.cpp b/kopete/plugins/addbookmarks/addbookmarksplugin.cpp
new file mode 100644
index 00000000..fae164f1
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarksplugin.cpp
@@ -0,0 +1,194 @@
+//
+// C++ Implementation: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include <kdebug.h>
+#include <kbookmark.h>
+#include <qvariant.h>
+#include <qtextcodec.h>
+#include <qregexp.h>
+
+#include "addbookmarksplugin.moc"
+#include "addbookmarksplugin.h"
+#include "kopetecontact.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteglobal.h"
+#include "kopetemetacontact.h"
+
+
+K_EXPORT_COMPONENT_FACTORY( kopete_addbookmarks, BookmarksPluginFactory( "kopete_addbookmarks" ) )
+
+
+static bool isURLInGroup(const KURL& url, const KBookmarkGroup& group)
+{
+ KBookmark bookmark = group.first();
+
+ for( ; !bookmark.isNull() ; bookmark = group.next(bookmark) ){
+ if( !bookmark.isGroup() && !bookmark.isSeparator() )
+ if( url == bookmark.url() )
+ return true;
+ }
+ return false;
+}
+
+BookmarksPlugin::BookmarksPlugin(QObject *parent, const char *name, const QStringList &/*args*/)
+ : Kopete::Plugin(BookmarksPluginFactory::instance(), parent, name)
+{
+ //kdDebug(14501) << "plugin loading" << endl;
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToDisplay( Kopete::Message & ) ), this, SLOT( slotBookmarkURLsInMessage( Kopete::Message & ) ) );
+}
+
+/*!
+ \fn BookmarksPlugin::slotBookmarkURLsInMessage(KopeteMessage & msg)
+ */
+void BookmarksPlugin::slotBookmarkURLsInMessage(Kopete::Message & msg)
+{
+ //kdDebug(14501) << "recieved message:" << endl << msg.parsedBody() << endl;
+ if(msg.direction() != Kopete::Message::Inbound)
+ return;
+ KURL::List *URLsList;
+ KURL::List::iterator it;
+ URLsList = extractURLsFromString( msg.parsedBody() );
+ if (!URLsList->empty()) {
+ for( it = URLsList->begin() ; it != URLsList->end() ; ++it){
+ if ( m_settings.addBookmarksFromUnknownContacts() || !msg.from()->metaContact()->isTemporary() )
+ {
+ if ( msg.from()->metaContact() ) {
+ addKopeteBookmark(*it, msg.from()->metaContact()->displayName() );
+ //kdDebug (14501) << "name:" << msg.from()->metaContact()->displayName() << endl;
+ }
+ else {
+ addKopeteBookmark(*it, msg.from()->property(Kopete::Global::Properties::self()->nickName()).value().toString() );
+ //kdDebug (14501) << "name:" << msg.from()->property(Kopete::Global::Properties::self()->nickName()).value().toString() << endl;
+ }
+ }
+ }
+ }
+ delete URLsList;
+}
+
+void BookmarksPlugin::slotAddKopeteBookmark( KIO::Job *transfer, const QByteArray &data )
+{
+ QTextCodec *codec = getPageEncoding( data );
+ QString htmlpage = codec->toUnicode( data );
+ QRegExp rx("<title>([^<]*){1,96}</title>");
+ rx.setCaseSensitive(false);
+ int pos = rx.search( htmlpage );
+ KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager();
+ KBookmarkGroup group = getKopeteFolder();
+ QString sender = m_map[(KIO::TransferJob*)transfer].sender;
+
+ if ( m_settings.useSubfolderForContact( sender ) )
+ group = getFolder( group, sender );
+
+ if( pos == -1 ){
+ group.addBookmark( mgr, m_map[(KIO::TransferJob*)transfer].url.prettyURL(), m_map[(KIO::TransferJob*)transfer].url.url() );
+ kdDebug( 14501 ) << "failed to extract title from first data chunk" << endl;
+ }else {
+ group.addBookmark( mgr, rx.cap( 1 ).simplifyWhiteSpace(),
+ m_map[(KIO::TransferJob*)transfer].url.url() );
+ }
+ mgr->save();
+ mgr->emitChanged( group );
+ m_map.remove( (KIO::TransferJob*)transfer );
+ transfer->kill();
+}
+
+KURL::List* BookmarksPlugin::extractURLsFromString( const QString& text )
+{
+ KURL::List *list = new KURL::List;
+ QRegExp rx("<a href=\"[^\\s\"]+\"");
+ int pos=0;
+ KURL url;
+
+ for(; (pos=rx.search(text, pos))!=-1; pos+=rx.matchedLength()){
+ //as long as there is a matching URL in text
+ url = text.mid(pos+9, rx.matchedLength()-10);
+ // assuming that in formatted messages links appear as <a href="link"
+ if(url.isValid())
+ list->append(url);
+ }
+ return list;
+}
+
+void BookmarksPlugin::addKopeteBookmark( const KURL& url, const QString& sender )
+{
+ KBookmarkGroup group = getKopeteFolder();
+
+ if ( m_settings.useSubfolderForContact( sender ) ) {
+ group = getFolder( group, sender );
+ }
+ // either restrict to http(s) or to KProtocolInfo::protocolClass() == :internet
+ if( !isURLInGroup( url, group )
+ && url.isValid() && url.protocol().startsWith("http") ) {
+ KIO::TransferJob *transfer;
+ // make asynchronous transfer to avoid GUI freezing due to overloaded web servers
+ transfer = KIO::get(url, false, false);
+ transfer->setInteractive(false);
+ connect ( transfer, SIGNAL ( data( KIO::Job *, const QByteArray & ) ),
+ this, SLOT ( slotAddKopeteBookmark( KIO::Job *, const QByteArray & ) ) );
+ m_map[transfer].url = url;
+ m_map[transfer].sender = sender;
+ }
+}
+
+KBookmarkGroup BookmarksPlugin::getKopeteFolder()
+{
+ KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager();
+
+ return getFolder( mgr->root(), QString::fromLatin1("kopete") );
+}
+
+KBookmarkGroup BookmarksPlugin::getFolder( KBookmarkGroup group, const QString& folder )
+{
+ KBookmark bookmark;
+
+
+ for( bookmark=group.first(); !bookmark.isNull() && !(bookmark.isGroup() && !bookmark.fullText().compare( folder )); bookmark = group.next(bookmark));
+ if( bookmark.isNull() ){
+ KBookmarkManager *mgr = KBookmarkManager::userBookmarksManager();
+ //kdDebug (14501) << "GetFolder:" << folder << endl;
+ group = group.createNewFolder( mgr, folder, true);
+ }else {
+ group = bookmark.toGroup();
+ }
+ return group;
+}
+
+QTextCodec* BookmarksPlugin::getPageEncoding( const QByteArray& data )
+{
+ QString temp = QString::fromLatin1(data);
+ QRegExp rx("<meta[^>]*(charset|CHARSET)\\s*=\\s*[^>]*>");
+ int pos = rx.search( temp );
+ QTextCodec *codec;
+
+ if( pos == -1 ){
+ kdDebug( 14501 ) << "charset not found in first data chunk" << endl;
+ return QTextCodec::codecForName("iso8859-1");
+ }
+ //kdDebug(14501) << temp.mid(pos, rx.matchedLength()) << endl;
+ temp = temp.mid(pos, rx.matchedLength()-1);
+ temp = temp.mid( temp.find("charset", 0, false)+7);
+ temp = temp.remove('=').simplifyWhiteSpace();
+ for( pos = 0 ; temp[pos].isLetterOrNumber() || temp[pos] == '-' ; pos++ );
+ temp = temp.left( pos );
+ //kdDebug(14501) << "encoding: " << temp << endl;
+ codec = QTextCodec::codecForName( temp.latin1() );
+ if( !codec ){
+ return QTextCodec::codecForName("iso8859-1");
+ }
+ return codec;
+}
+
+void BookmarksPlugin::slotReloadSettings()
+{
+ m_settings.load();
+}
diff --git a/kopete/plugins/addbookmarks/addbookmarksplugin.h b/kopete/plugins/addbookmarks/addbookmarksplugin.h
new file mode 100644
index 00000000..4c425b9f
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarksplugin.h
@@ -0,0 +1,56 @@
+//
+// C++ Interface: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef ADDBOOKMARKSPLUGIN_H
+#define ADDBOOKMARKSPLUGIN_H
+
+#include "addbookmarksprefssettings.h"
+#include <kgenericfactory.h>
+#include <kopeteplugin.h>
+#include <kbookmarkmanager.h>
+#include <kio/job.h>
+#include <qcstring.h>
+#include <qmap.h>
+
+/**
+@author Roie Kerstein <sf_kersteinroie@bezeqint.net>
+*/
+
+class BookmarksPlugin : public Kopete::Plugin
+{
+Q_OBJECT
+public:
+ BookmarksPlugin(QObject *parent, const char *name, const QStringList &args);
+
+private:
+ typedef struct S_URLANDNAME{
+ KURL url;
+ QString sender;
+ } URLandName;
+ typedef QMap<KIO::TransferJob*,URLandName> JobsToURLsMap;
+ JobsToURLsMap m_map;
+ BookmarksPrefsSettings m_settings;
+ void addKopeteBookmark( const KURL& url, const QString& sender );
+ KURL::List* extractURLsFromString( const QString& text );
+ KBookmarkGroup getKopeteFolder();
+ KBookmarkGroup getFolder( KBookmarkGroup group, const QString& folder );
+ QTextCodec* getPageEncoding( const QByteArray& data );
+public slots:
+ void slotBookmarkURLsInMessage(Kopete::Message & msg);
+ void slotReloadSettings();
+
+private slots:
+ void slotAddKopeteBookmark( KIO::Job *transfer, const QByteArray &data );
+};
+
+typedef KGenericFactory<BookmarksPlugin> BookmarksPluginFactory;
+
+#endif
diff --git a/kopete/plugins/addbookmarks/addbookmarkspreferences.cpp b/kopete/plugins/addbookmarks/addbookmarkspreferences.cpp
new file mode 100644
index 00000000..12ebd877
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarkspreferences.cpp
@@ -0,0 +1,114 @@
+//
+// C++ Implementation: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "addbookmarkspreferences.h"
+#include "addbookmarksprefsui.h"
+#include "addbookmarksplugin.h"
+#include <kgenericfactory.h>
+#include <kopetepluginmanager.h>
+#include <kopetecontactlist.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qbuttongroup.h>
+#include <qlistbox.h>
+#include <qnamespace.h>
+#include <qradiobutton.h>
+
+
+typedef KGenericFactory<BookmarksPreferences> BookmarksPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_addbookmarks, BookmarksPreferencesFactory("kcm_kopete_addbookmarks") )
+
+BookmarksPreferences::BookmarksPreferences(QWidget *parent, const char *name, const QStringList &args)
+ : KCModule(BookmarksPreferencesFactory::instance(), parent, args)
+{
+ Q_UNUSED( name );
+ ( new QVBoxLayout (this) )->setAutoAdd( true );
+ p_dialog = new BookmarksPrefsUI( this );
+ load();
+ connect( p_dialog->yesButton, SIGNAL( toggled(bool) ), this, SLOT( slotSetStatusChanged() ));
+ connect( p_dialog->noButton, SIGNAL( toggled(bool) ), this, SLOT( slotSetStatusChanged() ));
+ connect( p_dialog->onlySelectedButton, SIGNAL( toggled(bool) ), this, SLOT( slotSetStatusChanged() ));
+ connect( p_dialog->onlyNotSelectedButton, SIGNAL( toggled(bool) ), this, SLOT( slotSetStatusChanged() ));
+ connect( p_dialog->contactList, SIGNAL( selectionChanged() ), this, SLOT( slotSetStatusChanged() ));
+ if(Kopete::PluginManager::self()->plugin("kopete_addbookmarks") )
+ connect( this, SIGNAL(PreferencesChanged()), Kopete::PluginManager::self()->plugin("kopete_addbookmarks") , SLOT(slotReloadSettings()));
+ connect( p_dialog->m_addUntrusted, SIGNAL( toggled(bool) ), this, SLOT( slotAddUntrustedChanged() ) );
+}
+
+
+BookmarksPreferences::~BookmarksPreferences()
+{
+}
+
+void BookmarksPreferences::save()
+{
+ QStringList list;
+ QStringList::iterator it;
+
+
+ m_settings.setFolderForEachContact( (BookmarksPrefsSettings::UseSubfolders)p_dialog->buttonGroup1->selectedId() );
+ if ( m_settings.isFolderForEachContact() == BookmarksPrefsSettings::SelectedContacts ||
+ m_settings.isFolderForEachContact() == BookmarksPrefsSettings::UnselectedContacts ) {
+ for( uint i = 0; i < p_dialog->contactList->count() ; ++i ){
+ if( p_dialog->contactList->isSelected( i ) ){
+ list += p_dialog->contactList->text( i );
+ }
+ }
+ m_settings.setContactsList( list );
+ }
+ m_settings.setAddBookmarksFromUnknownContacts( p_dialog->m_addUntrusted->isChecked() );
+ m_settings.save();
+ emit PreferencesChanged();
+ emit KCModule::changed(false);
+}
+
+void BookmarksPreferences::slotSetStatusChanged()
+{
+ if ( p_dialog->buttonGroup1->selectedId() == 1 || p_dialog->buttonGroup1->selectedId() == 0)
+ p_dialog->contactList->setEnabled(false);
+ else
+ p_dialog->contactList->setEnabled(true);
+
+ emit KCModule::changed(true);
+}
+
+void BookmarksPreferences::slotAddUntrustedChanged()
+{
+ emit KCModule::changed(true);
+}
+
+void BookmarksPreferences::load()
+{
+ QStringList list;
+ QStringList::iterator it;
+ QListBoxItem* item;
+
+ m_settings.load();
+ p_dialog->buttonGroup1->setButton(m_settings.isFolderForEachContact());
+ p_dialog->m_addUntrusted->setChecked( m_settings.addBookmarksFromUnknownContacts() );
+ if( p_dialog->contactList->count() == 0 ){
+ QStringList contacts = Kopete::ContactList::self()->contacts();
+ contacts.sort();
+ p_dialog->contactList->insertStringList( contacts );
+ }
+ p_dialog->contactList->clearSelection();
+ p_dialog->contactList->setEnabled( m_settings.isFolderForEachContact() == BookmarksPrefsSettings::SelectedContacts ||
+ m_settings.isFolderForEachContact() == BookmarksPrefsSettings::UnselectedContacts );
+ list = m_settings.getContactsList();
+ for( it = list.begin() ; it != list.end() ; ++it){
+ if ( ( item = p_dialog->contactList->findItem(*it, Qt::ExactMatch ) ) ){
+ p_dialog->contactList->setSelected( item, true );
+ }
+ }
+ emit KCModule::changed(false);
+}
+
+#include "addbookmarkspreferences.moc"
diff --git a/kopete/plugins/addbookmarks/addbookmarkspreferences.h b/kopete/plugins/addbookmarks/addbookmarkspreferences.h
new file mode 100644
index 00000000..7a9d5bff
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarkspreferences.h
@@ -0,0 +1,45 @@
+//
+// C++ Interface: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef ADDBOOKMARKSPREFERENCES_H
+#define ADDBOOKMARKSPREFERENCES_H
+
+#include <kcmodule.h>
+#include "addbookmarksprefssettings.h"
+#include "addbookmarksprefsui.h"
+
+/**
+@author Roie Kerstein <sf_kersteinroie@bezeqint.net>
+*/
+class BookmarksPreferences : public KCModule
+{
+Q_OBJECT
+public:
+ BookmarksPreferences(QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList());
+
+ ~BookmarksPreferences();
+
+ virtual void load();
+ virtual void save();
+
+signals:
+ void PreferencesChanged();
+
+private:
+ BookmarksPrefsUI *p_dialog;
+ BookmarksPrefsSettings m_settings;
+
+private slots:
+ void slotSetStatusChanged();
+ void slotAddUntrustedChanged();
+};
+
+#endif
diff --git a/kopete/plugins/addbookmarks/addbookmarksprefssettings.cpp b/kopete/plugins/addbookmarks/addbookmarksprefssettings.cpp
new file mode 100644
index 00000000..045ce801
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarksprefssettings.cpp
@@ -0,0 +1,87 @@
+//
+// C++ Implementation: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// License: GPL v2
+//
+//
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+#include "addbookmarksprefssettings.h"
+
+BookmarksPrefsSettings::BookmarksPrefsSettings(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+ load();
+}
+
+
+BookmarksPrefsSettings::~BookmarksPrefsSettings()
+{
+}
+
+void BookmarksPrefsSettings::load()
+{
+ KConfig * configfile = KGlobal::config();
+ m_isfolderforeachcontact = Always;
+ m_contactslist.clear();
+ m_addbookmarksfromunknowns = false;
+ if( configfile->getConfigState() == KConfigBase::NoAccess ){
+ kdDebug( 14501 ) << "load: failed to open config file for reading" << endl;
+ return;
+ }
+ if( !configfile->hasGroup("Bookmarks Plugin") ){
+ kdDebug( 14501 ) << "load: no config found in file" << endl;
+ return;
+ }
+ configfile->setGroup("Bookmarks Plugin");
+ m_isfolderforeachcontact = (UseSubfolders)configfile->readNumEntry( "UseSubfolderForEachContact", 0 );
+ m_contactslist = configfile->readListEntry( "ContactsList" );
+ m_addbookmarksfromunknowns = configfile->readBoolEntry( "AddBookmarksFromUnknownContacts" );
+}
+
+void BookmarksPrefsSettings::save()
+{
+ KConfig * configfile = KGlobal::config();
+
+ if( configfile->getConfigState() != KConfigBase::ReadWrite ){
+ kdDebug( 14501 ) << "save: failed to open config file for writing" << endl;
+ return;
+ }
+ configfile->setGroup( "Bookmarks Plugin" );
+ configfile->writeEntry( "UseSubfolderForEachContact", (int)m_isfolderforeachcontact );
+ configfile->writeEntry( "ContactsList", m_contactslist );
+ configfile->writeEntry( "AddBookmarksFromUnknownContacts", m_addbookmarksfromunknowns );
+ configfile->sync();
+}
+
+bool BookmarksPrefsSettings::useSubfolderForContact( QString nickname )
+{
+ if ( !nickname.isEmpty() )
+ {
+ switch( m_isfolderforeachcontact ){
+ case Never:
+ return false;
+ case Always:
+ return true;
+ case SelectedContacts:
+ return ( m_contactslist.find( nickname ) != m_contactslist.end() );
+ case UnselectedContacts:
+ return ( m_contactslist.find( nickname ) == m_contactslist.end() );
+ }
+ }
+ return false;
+}
+
+void BookmarksPrefsSettings::setAddBookmarksFromUnknownContacts( bool addUntrusted )
+{
+ m_addbookmarksfromunknowns = addUntrusted;
+}
+
+#include "addbookmarksprefssettings.moc"
diff --git a/kopete/plugins/addbookmarks/addbookmarksprefssettings.h b/kopete/plugins/addbookmarks/addbookmarksprefssettings.h
new file mode 100644
index 00000000..2d82e7c4
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarksprefssettings.h
@@ -0,0 +1,49 @@
+//
+// C++ Interface: %{MODULE}
+//
+// Description:
+//
+//
+// Author: Roie Kerstein <sf_kersteinroie@bezeqint.net>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef ADDBOOKMARKSPREFSSETTINGS_H
+#define ADDBOOKMARKSPREFSSETTINGS_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+/**
+@author Roie Kerstein <sf_kersteinroie@bezeqint.net>
+*/
+class BookmarksPrefsSettings : public QObject
+{
+Q_OBJECT
+public:
+ enum UseSubfolders { Always=0, Never=1, SelectedContacts=2, UnselectedContacts=3 };
+
+ BookmarksPrefsSettings(QObject *parent = 0, const char *name = 0);
+
+ ~BookmarksPrefsSettings();
+
+ void load();
+ void save();
+ UseSubfolders isFolderForEachContact() {return m_isfolderforeachcontact;}
+ void setFolderForEachContact(UseSubfolders val) {m_isfolderforeachcontact = val;}
+ bool useSubfolderForContact( QString nickname );
+ QStringList getContactsList() {return m_contactslist;}
+ void setContactsList(QStringList list) {m_contactslist = list;}
+ bool addBookmarksFromUnknownContacts() { return m_addbookmarksfromunknowns; };
+ void setAddBookmarksFromUnknownContacts( bool );
+
+private:
+ bool m_folderPerContact;
+ bool m_addbookmarksfromunknowns;
+ UseSubfolders m_isfolderforeachcontact;
+ QStringList m_contactslist;
+
+};
+
+#endif
diff --git a/kopete/plugins/addbookmarks/addbookmarksprefsui.ui b/kopete/plugins/addbookmarks/addbookmarksprefsui.ui
new file mode 100644
index 00000000..67be2b9e
--- /dev/null
+++ b/kopete/plugins/addbookmarks/addbookmarksprefsui.ui
@@ -0,0 +1,104 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>BookmarksPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>BookmarksPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>396</width>
+ <height>421</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Use Subfolder for Each Contact</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>yesButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Always</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>noButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Never</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>onlySelectedButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Onl&amp;y the selected contacts</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>2</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>onlyNotSelectedButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Not the selected contacts</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>contactList</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_addUntrusted</cstring>
+ </property>
+ <property name="text">
+ <string>Add Bookmarks from Contacts Not In Your Contact List</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/plugins/addbookmarks/kopete_addbookmarks.desktop b/kopete/plugins/addbookmarks/kopete_addbookmarks.desktop
new file mode 100644
index 00000000..c3c65428
--- /dev/null
+++ b/kopete/plugins/addbookmarks/kopete_addbookmarks.desktop
@@ -0,0 +1,126 @@
+[Desktop Entry]
+Type=Service
+Name=Bookmarks
+Name[be]=Закладкі
+Name[bg]=Отметки
+Name[bn]=বà§à¦•à¦®à¦¾à¦°à§à¦•
+Name[br]=Sinedoù
+Name[bs]=Zabilješke
+Name[ca]=Punts
+Name[cs]=Záložky
+Name[cy]=Nodau Tudalen
+Name[da]=Bogmærker
+Name[de]=Lesezeichen
+Name[el]=Σελιδοδείκτες
+Name[eo]=Legosignoj
+Name[es]=Marcadores
+Name[et]=Järjehoidjad
+Name[eu]=Lastermarkak
+Name[fa]=چوب الÙها
+Name[fi]=Kirjanmerkit
+Name[fr]=Signets
+Name[ga]=Leabharmharcanna
+Name[gl]=Marcadores
+Name[he]=סימניות
+Name[hu]=Könyvjelzők
+Name[id]=Bookmark
+Name[is]=Bókamerki
+Name[it]=Segnalibri
+Name[ja]=ブックマーク
+Name[ka]=სáƒáƒœáƒ˜áƒ¨áƒœáƒ”ები
+Name[kk]=Бетбелгілер
+Name[km]=ចំណាំ
+Name[lt]=Žymelės
+Name[lv]=GrÄmatzÄ«mes
+Name[mk]=Обележувачи
+Name[mt]=Favoriti
+Name[nb]=Bokmerker
+Name[nds]=Leestekens
+Name[ne]=पà¥à¤¸à¥à¤¤à¤•à¤šà¤¿à¤¨à¥‹
+Name[nl]=Bladwijzers
+Name[nn]=Bokmerke
+Name[pa]=ਬà©à©±à¨•à¨®à¨¾à¨°à¨•
+Name[pl]=Zakładki
+Name[pt]=Favoritos
+Name[pt_BR]=Favoritos
+Name[ro]=Semne de carte
+Name[ru]=Закладки
+Name[rw]=Utumenyetso
+Name[sk]=Záložky
+Name[sl]=Zaznamki
+Name[sr]=Маркери
+Name[sr@Latn]=Markeri
+Name[sv]=Bokmärken
+Name[ta]=பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿à®•à®³à¯
+Name[th]=ที่คั่นหนังสือ
+Name[tr]=Yer imleri
+Name[uk]=Закладки
+Name[uz]=Xatchoʻplar
+Name[uz@cyrillic]=Хатчўплар
+Name[ven]=Dzitswayo dza bugu
+Name[wa]=Rimåkes
+Name[xh]=Amanqaku encwadi
+Name[zh_CN]=书签
+Name[zh_HK]=書籤
+Name[zh_TW]=書籤
+Name[zu]=Amamaki encwadi
+Comment=Automatically bookmark links in incoming messages
+Comment[be]=Ðўтаматычна Ñтвараць закладкі Ð´Ð»Ñ ÑпаÑылак, перададзеных вам праз Ñ–Ð¼Ð³Ð½ÐµÐ½Ð½Ñ‹Ñ Ð¿Ð°Ð²ÐµÐ´Ð°Ð¼Ð»ÐµÐ½Ð½Ñ–
+Comment[bg]=Ðвтоматично добавÑне на връзките във входÑщите ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÑŠÐ¼ отметките
+Comment[bn]=অনà§à¦¤à¦°à§à¦®à§à¦–ী বারà§à¦¤à¦¾à¦®à¦§à§à¦¯à¦¸à§à¦¥ লিঙà§à¦•à¦—à§à¦²à§‹ সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ বà§à¦•à¦®à¦¾à¦°à§à¦• করে
+Comment[bs]=Automatski zabilježi linkove u dolaznim porukama
+Comment[ca]=Apunta automàticament els enllaços en els missatges entrants
+Comment[cs]=Automaticky přidat do záložek odkazy z příchozích zpráv
+Comment[da]=Sæt link til indkommende breve automatisk
+Comment[de]=Verknüpfungen in eingehenden Nachrichten automatisch als Lesezeichen ablegen
+Comment[el]=Αυτόματη τοποθέτηση σελιδοδεικτών στα εισεÏχόμενα μηνÏματα
+Comment[es]=Anota automáticamente los enlaces de los mensajes entrantes
+Comment[et]=Sissetulevates sõnumites olevate viitade automaatne lisamine järjehoidjatesse
+Comment[eu]=Automatikoki gorde lastermarketan sarrerako mezuetako loturak.
+Comment[fa]=چوب ال٠به طور خودکار به پیامهای واردشده پیوند می‌خورد
+Comment[fi]=Lisää saapuvien viestien sisältämät linkit automaattisesti kirjanmerkkeihin
+Comment[fr]=Ajouter automatiquement un signet pour les liens présents dans les messages rentrants
+Comment[gl]=Marcar automáticamente as ligazóns nas mensaxes entrantes
+Comment[he]=שמור ×§×™×©×•×¨×™× ×ž×”×ž×¡×¨×™× ×”× ×›× ×¡×™× ×‘×¨×©×™×ž×ª הסימניות ב×ופן ×וטומטי
+Comment[hu]=Könyvjelző létrehozása a bejövő üzenetekben található linkekről
+Comment[is]=Setja sjálfkrafa bókamerki á tengla í skilaboðum
+Comment[it]=Aggiungi automaticamente al segnalibro i collegamenti nei messaggi in entrata
+Comment[ja]=å—信メッセージ中ã®ãƒªãƒ³ã‚¯ã‚’自動的ã«ãƒ–ックマークã«è¿½åŠ 
+Comment[ka]=შემáƒáƒ›áƒáƒ•áƒáƒš შეტყáƒáƒ‘ინებებში ბმულების áƒáƒ•áƒ áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ ჩáƒáƒœáƒ˜áƒ¨áƒ•áƒœáƒ
+Comment[kk]=ÐšÑ–Ñ€Ñ–Ñ Ñ…Ð°Ð±Ð°Ñ€Ð»Ð°Ð¼Ð°Ð»Ð°Ñ€Ð´Ð°Ò“Ñ‹ Ñілтемелерді автоматты түрде бетбелгілеу
+Comment[km]=ចំណាំ​ážáŸ†ážŽâ€‹áž“ៅ​ក្នុង​សារ​ចូល ដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Comment[lt]=Gautose žinutÄ—se esanÄias nuorodas automatiÅ¡kai įtraukti į žymeles
+Comment[mk]=Ги обележува автоматÑки врÑките во дојдовните пораки
+Comment[nb]=Sett automatisk bokmerke for lenker i innkommende meldinger
+Comment[nds]=Links in rinkamen Narichten automaatsch de Leestekens tofögen
+Comment[ne]=आगमन सनà¥à¤¦à¥‡à¤¶à¤®à¤¾ सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¸à¥à¤¤à¤•à¤šà¤¿à¤¨à¥‹ लिङà¥à¤•
+Comment[nl]=Koppelingen in inkomende berichten automatisch als bladwijzer opslaan
+Comment[nn]=Lag automatisk bokmerke til lenkjer i innkomande meldingar
+Comment[pl]=Automatycznie dodawaj zakładkę dla odnośników w nadchodzących komunikatach
+Comment[pt]=Adicionar automaticamente ao favoritos ligações em mensagens recebidas
+Comment[pt_BR]=Adiciona automaticamente aos favoritos os links em mensagens recebidas
+Comment[ru]=ÐвтоматичеÑки делать закладки ÑÑылок из входных Ñообщений
+Comment[sk]=Automaticky vytvorí záložky odkazov v prichádzajúcich správach
+Comment[sl]=Samodejno doda povezave v prihajajoÄih sporoÄilih med zaznamke
+Comment[sr]=ÐутоматÑки маркирај везе у долазећим порукама
+Comment[sr@Latn]=Automatski markiraj veze u dolazećim porukama
+Comment[sv]=Bokmärk automatiskt länkar i inkommande meddelande.
+Comment[ta]=உளà¯à®µà®°à¯à®®à¯ செயà¯à®¤à®¿à®•à®³à®¿à®²à¯ தானாகவே பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿ இணைபà¯à®ªà¯à®•à®³à¯
+Comment[tr]=Otomatik olarak gelen mesajları yer imine bağlar
+Comment[uk]=Ðвтоматично робити закладки поÑилань з вхідних повідомлень
+Comment[zh_CN]=自动将收到消æ¯ä¸­çš„链接加入书签
+Comment[zh_HK]=自動將收到的訊æ¯å…§çš„連çµåŠ åˆ°æ›¸ç±¤
+Comment[zh_TW]=自動將接收訊æ¯ä¸­çš„連çµåŠ å…¥æ›¸ç±¤
+Icon=konqueror
+ServiceTypes=Kopete/Plugin
+X-Kopete-Version=1000900
+X-KDE-Library=kopete_addbookmarks
+X-KDE-PluginInfo-Author=Roie Kerstein
+X-KDE-PluginInfo-Email=sf_kersteinroie@bezeqint.net
+X-KDE-PluginInfo-Name=kopete_addbookmarks
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
diff --git a/kopete/plugins/addbookmarks/kopete_addbookmarks_config.desktop b/kopete/plugins/addbookmarks/kopete_addbookmarks_config.desktop
new file mode 100644
index 00000000..933a4ee1
--- /dev/null
+++ b/kopete/plugins/addbookmarks/kopete_addbookmarks_config.desktop
@@ -0,0 +1,121 @@
+[Desktop Entry]
+Type=Service
+Name=Bookmarks
+Name[be]=Закладкі
+Name[bg]=Отметки
+Name[bn]=বà§à¦•à¦®à¦¾à¦°à§à¦•
+Name[br]=Sinedoù
+Name[bs]=Zabilješke
+Name[ca]=Punts
+Name[cs]=Záložky
+Name[cy]=Nodau Tudalen
+Name[da]=Bogmærker
+Name[de]=Lesezeichen
+Name[el]=Σελιδοδείκτες
+Name[eo]=Legosignoj
+Name[es]=Marcadores
+Name[et]=Järjehoidjad
+Name[eu]=Lastermarkak
+Name[fa]=چوب الÙها
+Name[fi]=Kirjanmerkit
+Name[fr]=Signets
+Name[ga]=Leabharmharcanna
+Name[gl]=Marcadores
+Name[he]=סימניות
+Name[hu]=Könyvjelzők
+Name[id]=Bookmark
+Name[is]=Bókamerki
+Name[it]=Segnalibri
+Name[ja]=ブックマーク
+Name[ka]=სáƒáƒœáƒ˜áƒ¨áƒœáƒ”ები
+Name[kk]=Бетбелгілер
+Name[km]=ចំណាំ
+Name[lt]=Žymelės
+Name[lv]=GrÄmatzÄ«mes
+Name[mk]=Обележувачи
+Name[mt]=Favoriti
+Name[nb]=Bokmerker
+Name[nds]=Leestekens
+Name[ne]=पà¥à¤¸à¥à¤¤à¤•à¤šà¤¿à¤¨à¥‹
+Name[nl]=Bladwijzers
+Name[nn]=Bokmerke
+Name[pa]=ਬà©à©±à¨•à¨®à¨¾à¨°à¨•
+Name[pl]=Zakładki
+Name[pt]=Favoritos
+Name[pt_BR]=Favoritos
+Name[ro]=Semne de carte
+Name[ru]=Закладки
+Name[rw]=Utumenyetso
+Name[sk]=Záložky
+Name[sl]=Zaznamki
+Name[sr]=Маркери
+Name[sr@Latn]=Markeri
+Name[sv]=Bokmärken
+Name[ta]=பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿à®•à®³à¯
+Name[th]=ที่คั่นหนังสือ
+Name[tr]=Yer imleri
+Name[uk]=Закладки
+Name[uz]=Xatchoʻplar
+Name[uz@cyrillic]=Хатчўплар
+Name[ven]=Dzitswayo dza bugu
+Name[wa]=Rimåkes
+Name[xh]=Amanqaku encwadi
+Name[zh_CN]=书签
+Name[zh_HK]=書籤
+Name[zh_TW]=書籤
+Name[zu]=Amamaki encwadi
+Comment=Automatically bookmark links in incoming messages
+Comment[be]=Ðўтаматычна Ñтвараць закладкі Ð´Ð»Ñ ÑпаÑылак, перададзеных вам праз Ñ–Ð¼Ð³Ð½ÐµÐ½Ð½Ñ‹Ñ Ð¿Ð°Ð²ÐµÐ´Ð°Ð¼Ð»ÐµÐ½Ð½Ñ–
+Comment[bg]=Ðвтоматично добавÑне на връзките във входÑщите ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ ÐºÑŠÐ¼ отметките
+Comment[bn]=অনà§à¦¤à¦°à§à¦®à§à¦–ী বারà§à¦¤à¦¾à¦®à¦§à§à¦¯à¦¸à§à¦¥ লিঙà§à¦•à¦—à§à¦²à§‹ সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ বà§à¦•à¦®à¦¾à¦°à§à¦• করে
+Comment[bs]=Automatski zabilježi linkove u dolaznim porukama
+Comment[ca]=Apunta automàticament els enllaços en els missatges entrants
+Comment[cs]=Automaticky přidat do záložek odkazy z příchozích zpráv
+Comment[da]=Sæt link til indkommende breve automatisk
+Comment[de]=Verknüpfungen in eingehenden Nachrichten automatisch als Lesezeichen ablegen
+Comment[el]=Αυτόματη τοποθέτηση σελιδοδεικτών στα εισεÏχόμενα μηνÏματα
+Comment[es]=Anota automáticamente los enlaces de los mensajes entrantes
+Comment[et]=Sissetulevates sõnumites olevate viitade automaatne lisamine järjehoidjatesse
+Comment[eu]=Automatikoki gorde lastermarketan sarrerako mezuetako loturak.
+Comment[fa]=چوب ال٠به طور خودکار به پیامهای واردشده پیوند می‌خورد
+Comment[fi]=Lisää saapuvien viestien sisältämät linkit automaattisesti kirjanmerkkeihin
+Comment[fr]=Ajouter automatiquement un signet pour les liens présents dans les messages rentrants
+Comment[gl]=Marcar automáticamente as ligazóns nas mensaxes entrantes
+Comment[he]=שמור ×§×™×©×•×¨×™× ×ž×”×ž×¡×¨×™× ×”× ×›× ×¡×™× ×‘×¨×©×™×ž×ª הסימניות ב×ופן ×וטומטי
+Comment[hu]=Könyvjelző létrehozása a bejövő üzenetekben található linkekről
+Comment[is]=Setja sjálfkrafa bókamerki á tengla í skilaboðum
+Comment[it]=Aggiungi automaticamente al segnalibro i collegamenti nei messaggi in entrata
+Comment[ja]=å—信メッセージ中ã®ãƒªãƒ³ã‚¯ã‚’自動的ã«ãƒ–ックマークã«è¿½åŠ 
+Comment[ka]=შემáƒáƒ›áƒáƒ•áƒáƒš შეტყáƒáƒ‘ინებებში ბმულების áƒáƒ•áƒ áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ ჩáƒáƒœáƒ˜áƒ¨áƒ•áƒœáƒ
+Comment[kk]=ÐšÑ–Ñ€Ñ–Ñ Ñ…Ð°Ð±Ð°Ñ€Ð»Ð°Ð¼Ð°Ð»Ð°Ñ€Ð´Ð°Ò“Ñ‹ Ñілтемелерді автоматты түрде бетбелгілеу
+Comment[km]=ចំណាំ​ážáŸ†ážŽâ€‹áž“ៅ​ក្នុង​សារ​ចូល ដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Comment[lt]=Gautose žinutÄ—se esanÄias nuorodas automatiÅ¡kai įtraukti į žymeles
+Comment[mk]=Ги обележува автоматÑки врÑките во дојдовните пораки
+Comment[nb]=Sett automatisk bokmerke for lenker i innkommende meldinger
+Comment[nds]=Links in rinkamen Narichten automaatsch de Leestekens tofögen
+Comment[ne]=आगमन सनà¥à¤¦à¥‡à¤¶à¤®à¤¾ सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ पà¥à¤¸à¥à¤¤à¤•à¤šà¤¿à¤¨à¥‹ लिङà¥à¤•
+Comment[nl]=Koppelingen in inkomende berichten automatisch als bladwijzer opslaan
+Comment[nn]=Lag automatisk bokmerke til lenkjer i innkomande meldingar
+Comment[pl]=Automatycznie dodawaj zakładkę dla odnośników w nadchodzących komunikatach
+Comment[pt]=Adicionar automaticamente ao favoritos ligações em mensagens recebidas
+Comment[pt_BR]=Adiciona automaticamente aos favoritos os links em mensagens recebidas
+Comment[ru]=ÐвтоматичеÑки делать закладки ÑÑылок из входных Ñообщений
+Comment[sk]=Automaticky vytvorí záložky odkazov v prichádzajúcich správach
+Comment[sl]=Samodejno doda povezave v prihajajoÄih sporoÄilih med zaznamke
+Comment[sr]=ÐутоматÑки маркирај везе у долазећим порукама
+Comment[sr@Latn]=Automatski markiraj veze u dolazećim porukama
+Comment[sv]=Bokmärk automatiskt länkar i inkommande meddelande.
+Comment[ta]=உளà¯à®µà®°à¯à®®à¯ செயà¯à®¤à®¿à®•à®³à®¿à®²à¯ தானாகவே பà¯à®¤à¯à®¤à®•à®•à¯à®•à¯à®±à®¿ இணைபà¯à®ªà¯à®•à®³à¯
+Comment[tr]=Otomatik olarak gelen mesajları yer imine bağlar
+Comment[uk]=Ðвтоматично робити закладки поÑилань з вхідних повідомлень
+Comment[zh_CN]=自动将收到消æ¯ä¸­çš„链接加入书签
+Comment[zh_HK]=自動將收到的訊æ¯å…§çš„連çµåŠ åˆ°æ›¸ç±¤
+Comment[zh_TW]=自動將接收訊æ¯ä¸­çš„連çµåŠ å…¥æ›¸ç±¤
+Icon=konqueror
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_addbookmarks
+X-KDE-FactoryName=BookmarksConfigFactory
+X-KDE-ParentApp=kopete_addbookmarks
+X-KDE-ParentComponents=kopete_addbookmarks
diff --git a/kopete/plugins/alias/Makefile.am b/kopete/plugins/alias/Makefile.am
new file mode 100644
index 00000000..d295e0cc
--- /dev/null
+++ b/kopete/plugins/alias/Makefile.am
@@ -0,0 +1,19 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_alias.la kcm_kopete_alias.la
+
+kopete_alias_la_SOURCES = aliasplugin.cpp
+kopete_alias_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_alias_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_alias_la_SOURCES = aliaspreferences.cpp aliasdialogbase.ui aliasdialog.ui editaliasdialog.cpp
+kcm_kopete_alias_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_alias_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS) ../../libkopete/libkopete.la
+
+service_DATA = kopete_alias.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_alias_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
diff --git a/kopete/plugins/alias/aliasdialog.ui b/kopete/plugins/alias/aliasdialog.ui
new file mode 100644
index 00000000..1d980d52
--- /dev/null
+++ b/kopete/plugins/alias/aliasdialog.ui
@@ -0,0 +1,193 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AliasDialog</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>AliasDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>361</width>
+ <height>268</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Add New Alias</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Command:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>command</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>command</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>This is the command that you want to run when you execute this alias. </string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;This is the command that you want to run when you execute this alias.
+
+You can use the variables &lt;b&gt;%1, %2 ... %9&lt;/b&gt; in your command, and they will be replaced with the arguments of the alias. The variable &lt;b&gt;%s&lt;/b&gt; will be replaced with all arguments. &lt;b&gt;%n&lt;/b&gt; expands to your nickname.
+
+Do not include the '/' in the command (if you do it will be stripped off anyway).&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Alias:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>alias</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>alias</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>This is the alias you are adding (what you will type after the command identifier, '/').</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the alias you are adding (what you will type after the command identifier, '/'). Do not include the '/' (it will be stripped off if you do anyway).</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="4" column="1">
+ <property name="name">
+ <cstring>addButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Save</string>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="4" column="2">
+ <property name="name">
+ <cstring>kPushButton3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="2" column="1" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Protocols</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>protocolList</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Multi</enum>
+ </property>
+ <property name="itemMargin">
+ <number>0</number>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>If you want this alias to only be active for certain protocols, select those protocols here.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you want this alias to only be active for certain protocols, select those protocols here.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>For protocols:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>kListView2</cstring>
+ </property>
+ </widget>
+ <widget class="Line" row="3" column="0" rowspan="1" colspan="3">
+ <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>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kPushButton3</sender>
+ <signal>clicked()</signal>
+ <receiver>AliasDialog</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>addButton</sender>
+ <signal>clicked()</signal>
+ <receiver>AliasDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>alias</tabstop>
+ <tabstop>command</tabstop>
+ <tabstop>protocolList</tabstop>
+ <tabstop>addButton</tabstop>
+ <tabstop>kPushButton3</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/alias/aliasdialogbase.ui b/kopete/plugins/alias/aliasdialogbase.ui
new file mode 100644
index 00000000..f70cc5bf
--- /dev/null
+++ b/kopete/plugins/alias/aliasdialogbase.ui
@@ -0,0 +1,107 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AliasDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AliasDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>602</width>
+ <height>424</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="3">
+ <column>
+ <property name="text">
+ <string>Alias</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Command</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Protocols</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>aliasList</cstring>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Extended</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>false</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the list of custom aliases and the commands that you have already added</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>addButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add New Alias...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>deleteButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete Selected</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>editButton</cstring>
+ </property>
+ <property name="text">
+ <string>Edit Alias...</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/alias/aliasplugin.cpp b/kopete/plugins/alias/aliasplugin.cpp
new file mode 100644
index 00000000..1594a836
--- /dev/null
+++ b/kopete/plugins/alias/aliasplugin.cpp
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * *
+ * 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 <kgenericfactory.h>
+
+#include "kopetemessagemanagerfactory.h"
+
+#include "aliasplugin.h"
+
+typedef KGenericFactory<AliasPlugin> AliasPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_alias, AliasPluginFactory( "kopete_alias" ) )
+AliasPlugin * AliasPlugin::pluginStatic_ = 0L;
+
+AliasPlugin::AliasPlugin( QObject *parent, const char * name, const QStringList & )
+ : Kopete::Plugin( AliasPluginFactory::instance(), parent, name )
+{
+ if( !pluginStatic_ )
+ pluginStatic_ = this;
+
+}
+
+AliasPlugin::~AliasPlugin()
+{
+ pluginStatic_ = 0L;
+}
+
+AliasPlugin * AliasPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+#include "aliasplugin.moc"
diff --git a/kopete/plugins/alias/aliasplugin.h b/kopete/plugins/alias/aliasplugin.h
new file mode 100644
index 00000000..8e55dc20
--- /dev/null
+++ b/kopete/plugins/alias/aliasplugin.h
@@ -0,0 +1,31 @@
+/***************************************************************************
+ * *
+ * 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 AliasPLUGIN_H
+#define AliasPLUGIN_H
+
+#include "kopeteplugin.h"
+
+class AliasPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+ public:
+ static AliasPlugin *plugin();
+
+ AliasPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~AliasPlugin();
+
+ private:
+ static AliasPlugin * pluginStatic_;
+};
+
+#endif
+
+
diff --git a/kopete/plugins/alias/aliaspreferences.cpp b/kopete/plugins/alias/aliaspreferences.cpp
new file mode 100644
index 00000000..65342ddf
--- /dev/null
+++ b/kopete/plugins/alias/aliaspreferences.cpp
@@ -0,0 +1,502 @@
+/***************************************************************************
+ * *
+ * 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 <kpushbutton.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <kglobal.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <qregexp.h>
+#include <qlayout.h>
+#include <kplugininfo.h>
+#include <kiconloader.h>
+#include <qpainter.h>
+
+#include "kopetecommandhandler.h"
+#include "kopetepluginmanager.h"
+#include "kopeteaccount.h"
+#include "kopeteprotocol.h"
+
+#include "aliasdialogbase.h"
+#include "editaliasdialog.h"
+#include "aliaspreferences.h"
+
+typedef KGenericFactory<AliasPreferences> AliasPreferencesFactory;
+
+class AliasItem : public QListViewItem
+{
+ public:
+ AliasItem( QListView *parent,
+ uint number,
+ const QString &alias,
+ const QString &command, const ProtocolList &p ) :
+ QListViewItem( parent, alias, command )
+ {
+ protocolList = p;
+ id = number;
+ }
+
+ ProtocolList protocolList;
+ uint id;
+
+ protected:
+ void paintCell( QPainter *p, const QColorGroup &cg,
+ int column, int width, int align )
+ {
+ if ( column == 2 )
+ {
+ int cellWidth = width - ( protocolList.count() * 16 ) - 4;
+ if ( cellWidth < 0 )
+ cellWidth = 0;
+
+ QListViewItem::paintCell( p, cg, column, cellWidth, align );
+
+ // Draw the rest of the background
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+
+ int marg = lv->itemMargin();
+ int r = marg;
+ const BackgroundMode bgmode = lv->viewport()->backgroundMode();
+ const QColorGroup::ColorRole crole =
+ QPalette::backgroundRoleFromMode( bgmode );
+ p->fillRect( cellWidth, 0, width - cellWidth, height(),
+ cg.brush( crole ) );
+
+ if ( isSelected() && ( column == 0 || listView()->allColumnsShowFocus() ) )
+ {
+ p->fillRect( QMAX( cellWidth, r - marg ), 0,
+ width - cellWidth - r + marg, height(),
+ cg.brush( QColorGroup::Highlight ) );
+ if ( isEnabled() || !lv )
+ p->setPen( cg.highlightedText() );
+ else if ( !isEnabled() && lv )
+ p->setPen( lv->palette().disabled().highlightedText() );
+ }
+
+ // And last, draw the online status icons
+ int mc_x = 0;
+
+ for ( ProtocolList::Iterator it = protocolList.begin();
+ it != protocolList.end(); ++it )
+ {
+ QPixmap icon = SmallIcon( (*it)->pluginIcon() );
+ p->drawPixmap( mc_x + 4, height() - 16,
+ icon );
+ mc_x += 16;
+ }
+ }
+ else
+ {
+ // Use Qt's own drawing
+ QListViewItem::paintCell( p, cg, column, width, align );
+ }
+ }
+};
+
+class ProtocolItem : public QListViewItem
+{
+ public:
+ ProtocolItem( QListView *parent, KPluginInfo *p ) :
+ QListViewItem( parent, p->name() )
+ {
+ this->setPixmap( 0, SmallIcon( p->icon() ) );
+ id = p->pluginName();
+ }
+
+ QString id;
+};
+
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_alias, AliasPreferencesFactory( "kcm_kopete_alias" ) )
+
+AliasPreferences::AliasPreferences( QWidget *parent, const char *, const QStringList &args )
+ : KCModule( AliasPreferencesFactory::instance(), parent, args )
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new AliasDialogBase( this );
+
+ connect( preferencesDialog->addButton, SIGNAL(clicked()), this, SLOT( slotAddAlias() ) );
+ connect( preferencesDialog->editButton, SIGNAL(clicked()), this, SLOT( slotEditAlias() ) );
+ connect( preferencesDialog->deleteButton, SIGNAL(clicked()), this, SLOT( slotDeleteAliases() ) );
+ connect( Kopete::PluginManager::self(), SIGNAL( pluginLoaded( Kopete::Plugin * ) ),
+ this, SLOT( slotPluginLoaded( Kopete::Plugin * ) ) );
+
+ connect( preferencesDialog->aliasList, SIGNAL(selectionChanged()),
+ this, SLOT( slotCheckAliasSelected() ) );
+
+ load();
+}
+
+AliasPreferences::~AliasPreferences()
+{
+ QListViewItem *myChild = preferencesDialog->aliasList->firstChild();
+ while( myChild )
+ {
+ ProtocolList protocols = static_cast<AliasItem*>( myChild )->protocolList;
+ for( ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ {
+ Kopete::CommandHandler::commandHandler()->unregisterAlias(
+ *it,
+ myChild->text(0)
+ );
+ }
+
+ myChild = myChild->nextSibling();
+ }
+}
+
+// reload configuration reading it from kopeterc
+void AliasPreferences::load()
+{
+ KConfig *config = KGlobal::config();
+ if( config->hasGroup( "AliasPlugin" ) )
+ {
+ config->setGroup("AliasPlugin");
+ QStringList aliases = config->readListEntry("AliasNames");
+ for( QStringList::Iterator it = aliases.begin(); it != aliases.end(); ++it )
+ {
+ uint aliasNumber = config->readUnsignedNumEntry( (*it) + "_id" );
+ QString aliasCommand = config->readEntry( (*it) + "_command" );
+ QStringList protocols = config->readListEntry( (*it) + "_protocols" );
+
+ ProtocolList protocolList;
+ for( QStringList::Iterator it2 = protocols.begin(); it2 != protocols.end(); ++it2 )
+ {
+ Kopete::Plugin *p = Kopete::PluginManager::self()->plugin( *it2 );
+ protocolList.append( (Kopete::Protocol*)p );
+ }
+
+ addAlias( *it, aliasCommand, protocolList, aliasNumber );
+ }
+
+ }
+
+ slotCheckAliasSelected();
+}
+
+void AliasPreferences::slotPluginLoaded( Kopete::Plugin *plugin )
+{
+ Kopete::Protocol *protocol = static_cast<Kopete::Protocol*>( plugin );
+ if( protocol )
+ {
+ KConfig *config = KGlobal::config();
+ if( config->hasGroup( "AliasPlugin" ) )
+ {
+ config->setGroup("AliasPlugin");
+ QStringList aliases = config->readListEntry("AliasNames");
+ for( QStringList::Iterator it = aliases.begin(); it != aliases.end(); ++it )
+ {
+ uint aliasNumber = config->readUnsignedNumEntry( (*it) + "_id" );
+ QString aliasCommand = config->readEntry( (*it) + "_command" );
+ QStringList protocols = config->readListEntry( (*it) + "_protocols" );
+
+ for( QStringList::iterator it2 = protocols.begin(); it2 != protocols.end(); ++it2 )
+ {
+ if( *it2 == protocol->pluginId() )
+ {
+ QPair<Kopete::Protocol*, QString> pr( protocol, *it );
+ if( protocolMap.find( pr ) == protocolMap.end() )
+ {
+ Kopete::CommandHandler::commandHandler()->registerAlias(
+ protocol,
+ *it,
+ aliasCommand,
+ QString::fromLatin1("Custom alias for %1").arg(aliasCommand),
+ Kopete::CommandHandler::UserAlias
+ );
+
+ protocolMap.insert( pr, true );
+
+ AliasItem *item = aliasMap[ *it ];
+ if( item )
+ {
+ item->protocolList.append( protocol );
+ item->repaint();
+ }
+ else
+ {
+ ProtocolList pList;
+ pList.append( protocol );
+ aliasMap.insert( *it, new AliasItem( preferencesDialog->aliasList, aliasNumber, *it, aliasCommand, pList ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// save list to kopeterc and creates map out of it
+void AliasPreferences::save()
+{
+ KConfig *config = KGlobal::config();
+ config->deleteGroup( QString::fromLatin1("AliasPlugin") );
+ config->setGroup( QString::fromLatin1("AliasPlugin") );
+
+ QStringList aliases;
+ AliasItem *item = (AliasItem*)preferencesDialog->aliasList->firstChild();
+ while( item )
+ {
+ QStringList protocols;
+ for( ProtocolList::Iterator it = item->protocolList.begin();
+ it != item->protocolList.end(); ++it )
+ {
+ protocols += (*it)->pluginId();
+ }
+
+ aliases += item->text(0);
+
+ config->writeEntry( item->text(0) + "_id", item->id );
+ config->writeEntry( item->text(0) + "_command", item->text(1) );
+ config->writeEntry( item->text(0) + "_protocols", protocols );
+
+ item = (AliasItem*)item->nextSibling();
+ }
+
+ config->writeEntry( "AliasNames", aliases );
+ config->sync();
+ emit KCModule::changed(false);
+}
+
+void AliasPreferences::addAlias( QString &alias, QString &command, const ProtocolList &p, uint id )
+{
+ QRegExp spaces( QString::fromLatin1("\\s+") );
+
+ if( alias.startsWith( QString::fromLatin1("/") ) )
+ alias = alias.section( '/', 1 );
+ if( command.startsWith( QString::fromLatin1("/") ) )
+ command = command.section( '/', 1 );
+
+ if( id == 0 )
+ {
+ if( preferencesDialog->aliasList->lastItem() )
+ id = static_cast<AliasItem*>( preferencesDialog->aliasList->lastItem() )->id + 1;
+ else
+ id = 1;
+ }
+
+ QString newAlias = command.section( spaces, 0, 0 );
+
+ aliasMap.insert( alias, new AliasItem( preferencesDialog->aliasList, id, alias, command, p ) );
+
+ // count the number of arguments present in 'command'
+ QRegExp rx( "(%\\d+)" );
+ QStringList list;
+ int pos = 0;
+ while ( pos >= 0 ) {
+ pos = rx.search( command, pos );
+ if ( pos > -1 ) {
+ list += rx.cap( 1 );
+ pos += rx.matchedLength();
+ }
+ }
+ int argc = list.count();
+
+ for( ProtocolList::ConstIterator it = p.begin(); it != p.end(); ++it )
+ {
+ Kopete::CommandHandler::commandHandler()->registerAlias(
+ *it,
+ alias,
+ command,
+ QString::fromLatin1("Custom alias for %1").arg(command),
+ Kopete::CommandHandler::UserAlias,
+ 0,
+ argc
+ );
+
+ protocolMap.insert( QPair<Kopete::Protocol*,QString>( *it, alias ), true );
+ }
+}
+
+void AliasPreferences::slotAddAlias()
+{
+ EditAliasDialog addDialog;
+ loadProtocols( &addDialog );
+ addDialog.addButton->setText( i18n("&Add") );
+
+ if( addDialog.exec() == QDialog::Accepted )
+ {
+ QString alias = addDialog.alias->text();
+ if( alias.startsWith( QString::fromLatin1("/") ) )
+ alias = alias.section( '/', 1 );
+
+ if( alias.contains( QRegExp("[_=]") ) )
+ {
+ KMessageBox::error( this, i18n("<qt>Could not add alias <b>%1</b>. An"
+ " alias name cannot contain the characters \"_\" or \"=\"."
+ "</qt>").arg(alias),i18n("Invalid Alias Name") );
+ }
+ else
+ {
+ QString command = addDialog.command->text();
+ ProtocolList protocols = selectedProtocols( &addDialog );
+
+ // Loop through selected protocols
+
+ for( ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ {
+
+ // And check if they already have the command enabled
+
+ if( Kopete::CommandHandler::commandHandler()->commandHandledByProtocol( alias, *it ) )
+ {
+ KMessageBox::error( this, i18n("<qt>Could not add alias <b>%1</b>. This "
+ "command is already being handled by either another alias or "
+ "Kopete itself.</qt>").arg(alias), i18n("Could Not Add Alias") );
+ return;
+ }
+ }
+ addAlias( alias, command, protocols );
+ emit KCModule::changed(true);
+
+ }
+ }
+}
+
+const ProtocolList AliasPreferences::selectedProtocols( EditAliasDialog *dialog )
+{
+ ProtocolList protocolList;
+ QListViewItem *item = dialog->protocolList->firstChild();
+
+ while( item )
+ {
+ if( item->isSelected() )
+ {
+
+ // If you dont have the selected protocol enabled, Kopete::PluginManager::self()->plugin
+ // will return NULL, check for that
+
+ if(Kopete::PluginManager::self()->plugin( static_cast<ProtocolItem*>(item)->id) )
+ protocolList.append( (Kopete::Protocol*)
+ Kopete::PluginManager::self()->plugin( static_cast<ProtocolItem*>(item)->id )
+ );
+ }
+ item = item->nextSibling();
+ }
+
+ return protocolList;
+}
+
+void AliasPreferences::loadProtocols( EditAliasDialog *dialog )
+{
+ QValueList<KPluginInfo*> plugins = Kopete::PluginManager::self()->availablePlugins("Protocols");
+ for( QValueList<KPluginInfo*>::Iterator it = plugins.begin(); it != plugins.end(); ++it )
+ {
+ ProtocolItem *item = new ProtocolItem( dialog->protocolList, *it );
+ itemMap[ (Kopete::Protocol*)Kopete::PluginManager::self()->plugin( (*it)->pluginName() ) ] = item;
+ }
+}
+
+void AliasPreferences::slotEditAlias()
+{
+ EditAliasDialog editDialog;
+ loadProtocols( &editDialog );
+
+ QListViewItem *item = preferencesDialog->aliasList->selectedItems().first();
+ if( item )
+ {
+ QString oldAlias = item->text(0);
+ editDialog.alias->setText( oldAlias );
+ editDialog.command->setText( item->text(1) );
+ ProtocolList protocols = static_cast<AliasItem*>( item )->protocolList;
+ for( ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ {
+ itemMap[ *it ]->setSelected( true );
+ }
+
+ if( editDialog.exec() == QDialog::Accepted )
+ {
+ QString alias = editDialog.alias->text();
+ if( alias.startsWith( QString::fromLatin1("/") ) )
+ alias = alias.section( '/', 1 );
+ if( alias.contains( QRegExp("[_=]") ) )
+ {
+ KMessageBox::error( this, i18n("<qt>Could not add alias <b>%1</b>. An"
+ " alias name cannot contain the characters \"_\" or \"=\"."
+ "</qt>").arg(alias),i18n("Invalid Alias Name") );
+ }
+ else
+ {
+ QString command = editDialog.command->text();
+
+ if( alias == oldAlias )
+ {
+ for( ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ {
+ Kopete::CommandHandler::commandHandler()->unregisterAlias(
+ *it,
+ oldAlias
+ );
+ }
+
+
+ ProtocolList selProtocols = selectedProtocols( &editDialog );
+
+ for( ProtocolList::Iterator it = selProtocols.begin(); it != selProtocols.end(); ++it )
+ {
+ if( Kopete::CommandHandler::commandHandler()->commandHandledByProtocol( alias, *it ) )
+ {
+ KMessageBox::error( this, i18n("<qt>Could not add alias <b>%1</b>. This "
+ "command is already being handled by either another alias or "
+ "Kopete itself.</qt>").arg(alias), i18n("Could Not Add Alias") );
+ return;
+ }
+ }
+
+ delete item;
+
+ addAlias( alias, command, selProtocols );
+ emit KCModule::changed(true);
+ }
+ }
+ }
+ }
+}
+
+void AliasPreferences::slotDeleteAliases()
+{
+ if( KMessageBox::warningContinueCancel(this, i18n("Are you sure you want to delete the selected aliases?"), i18n("Delete Aliases"), KGuiItem(i18n("Delete"), "editdelete") ) == KMessageBox::Continue )
+ {
+ QPtrList< QListViewItem > items = preferencesDialog->aliasList->selectedItems();
+ for( QListViewItem *i = items.first(); i; i = items.next() )
+ {
+ ProtocolList protocols = static_cast<AliasItem*>( i )->protocolList;
+ for( ProtocolList::Iterator it = protocols.begin(); it != protocols.end(); ++it )
+ {
+ Kopete::CommandHandler::commandHandler()->unregisterAlias(
+ *it,
+ i->text(0)
+ );
+
+ protocolMap.erase( QPair<Kopete::Protocol*,QString>( *it, i->text(0) ) );
+ }
+
+ aliasMap.erase( i->text(0) );
+ delete i;
+ emit KCModule::changed(true);
+ }
+
+ save();
+ }
+}
+
+void AliasPreferences::slotCheckAliasSelected()
+{
+ int numItems = preferencesDialog->aliasList->selectedItems().count();
+ preferencesDialog->deleteButton->setEnabled( numItems > 0 );
+ preferencesDialog->editButton->setEnabled( numItems == 1 );
+}
+
+#include "aliaspreferences.moc"
+
diff --git a/kopete/plugins/alias/aliaspreferences.h b/kopete/plugins/alias/aliaspreferences.h
new file mode 100644
index 00000000..330553a3
--- /dev/null
+++ b/kopete/plugins/alias/aliaspreferences.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * *
+ * 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 AliasPREFERENCES_H
+#define AliasPREFERENCES_H
+
+#include "kcmodule.h"
+
+typedef QValueList<Kopete::Protocol*> ProtocolList;
+
+class AliasDialogBase;
+namespace Kopete { class Protocol; }
+class ProtocolItem;
+class AliasItem;
+class AliasDialog;
+namespace Kopete { class Plugin; }
+
+class AliasPreferences : public KCModule
+{
+ Q_OBJECT
+
+ public:
+ AliasPreferences( QWidget *parent = 0, const char *name = 0,
+ const QStringList &args = QStringList() );
+ ~AliasPreferences();
+
+ virtual void save();
+ virtual void load();
+
+ private slots:
+ void slotAddAlias();
+ void slotEditAlias();
+ void slotDeleteAliases();
+ void slotCheckAliasSelected();
+ void slotPluginLoaded( Kopete::Plugin * );
+
+ private:
+ AliasDialogBase * preferencesDialog;
+ void addAlias( QString &alias, QString &command, const ProtocolList &p, uint id = 0 );
+ void loadProtocols( EditAliasDialog *dialog );
+ const ProtocolList selectedProtocols( EditAliasDialog *dialog );
+ QMap<Kopete::Protocol*,ProtocolItem*> itemMap;
+ QMap<QPair<Kopete::Protocol*,QString>, bool> protocolMap;
+ QMap<QString,AliasItem*> aliasMap;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/alias/editaliasdialog.cpp b/kopete/plugins/alias/editaliasdialog.cpp
new file mode 100644
index 00000000..42eb2f4b
--- /dev/null
+++ b/kopete/plugins/alias/editaliasdialog.cpp
@@ -0,0 +1,51 @@
+/*
+ Kopete Alias Plugin
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete Copyright (c) 2002-2005 by the Kopete Developers <kopete-devel@kde.org>
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************
+
+*/
+
+#include "editaliasdialog.h"
+#include <qobject.h>
+#include <kpushbutton.h>
+#include <qwidget.h>
+#include <qstring.h>
+#include <klineedit.h>
+#include <klistview.h>
+
+
+EditAliasDialog::EditAliasDialog( QWidget* parent, const char* name )
+: AliasDialog( parent, name )
+{
+ QObject::connect( alias, SIGNAL( textChanged( const QString& ) ), this, SLOT( checkButtonsEnabled() ) );
+ QObject::connect( command, SIGNAL( textChanged( const QString& ) ), this, SLOT( checkButtonsEnabled() ) );
+ QObject::connect( protocolList, SIGNAL( selectionChanged() ), this, SLOT( checkButtonsEnabled() ) );
+
+ checkButtonsEnabled();
+}
+
+EditAliasDialog::~EditAliasDialog()
+{
+}
+
+void EditAliasDialog::checkButtonsEnabled()
+{
+ if ( !alias->text().isEmpty() && !command->text().isEmpty() && !protocolList->selectedItems().isEmpty() )
+ addButton->setEnabled( true );
+ else
+ addButton->setEnabled( false ) ;
+}
+
+#include "editaliasdialog.moc"
+
+// kate: space-indent off; replace-tabs off; tab-width 4; indent-mode csands;
diff --git a/kopete/plugins/alias/editaliasdialog.h b/kopete/plugins/alias/editaliasdialog.h
new file mode 100644
index 00000000..869e8903
--- /dev/null
+++ b/kopete/plugins/alias/editaliasdialog.h
@@ -0,0 +1,38 @@
+/*
+ Kopete Alias Plugin
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete Copyright (c) 2002-2005 by the Kopete Developers <kopete-devel@kde.org>
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************
+
+*/
+
+#ifndef _EDITALIASDIALOG_H_
+#define _EDITALIASDIALOG_H_
+
+#include "aliasdialog.h"
+
+class QWidget;
+
+class EditAliasDialog : public AliasDialog
+{
+ Q_OBJECT
+public:
+ EditAliasDialog( QWidget* parent = 0, const char* name = 0 );
+ virtual ~EditAliasDialog();
+
+public slots:
+ void checkButtonsEnabled();
+};
+
+#endif
+
+// kate: space-indent off; replace-tabs off; tab-width 4; indent-mode csands;
diff --git a/kopete/plugins/alias/kopete_alias.desktop b/kopete/plugins/alias/kopete_alias.desktop
new file mode 100644
index 00000000..4a23fcd3
--- /dev/null
+++ b/kopete/plugins/alias/kopete_alias.desktop
@@ -0,0 +1,108 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=alias
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_alias
+X-KDE-PluginInfo-Author=Jason Keirstead
+X-KDE-PluginInfo-Email=jason@keirstead.org
+X-KDE-PluginInfo-Name=kopete_alias
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Alias
+Name[ar]=اسم مستعار
+Name[be]=ПÑеўданім
+Name[bg]=Запазени думи
+Name[bn]=ছদà§à¦®à¦¨à¦¾à¦®
+Name[br]=Lesanv
+Name[ca]=Àlies
+Name[cy]=Ffugenw
+Name[el]=Αντιστοιχία
+Name[eo]=Alinomo
+Name[eu]=Aliasa
+Name[fa]=نام‌گردان
+Name[fi]=Uusi nimi
+Name[ga]=Ailias
+Name[he]=כינוי
+Name[hi]=अलाà¤à¤¸
+Name[hu]=Alias nevek
+Name[is]=Samheiti
+Name[ja]=エイリアス
+Name[ka]=ფსევდáƒáƒœáƒ˜áƒ›áƒ˜
+Name[kk]=Бүркеншік атаулар
+Name[km]=ឈ្មោះ​ក្លែងក្លាយ
+Name[lt]=Kitas vardas
+Name[mk]=Други имиња
+Name[ne]=उपनाम
+Name[pa]=ਉਪ-ਨਾਂ
+Name[pt]=Nome Alternativo
+Name[pt_BR]=Apelidos
+Name[ru]=ПÑевдоним
+Name[sl]=Drugo ime
+Name[sr]=ÐлијаÑ
+Name[sr@Latn]=Alijas
+Name[ta]=மாறà¯à®±à¯à®ªà¯à®ªà¯†à®¯à®°à¯
+Name[tg]=ТахаллуÑ
+Name[tr]=Takma Ä°sim
+Name[uk]=ПÑевдонім
+Name[zh_CN]=别å
+Name[zh_HK]=別å
+Name[zh_TW]=別å
+Comment=Adds custom aliases for commands
+Comment[ar]=يضي٠أسماء مستعارة للأوامر
+Comment[be]=Дадаць адмыÑÐ»Ð¾Ð²Ñ‹Ñ Ð¿Ñеўданімы Ð´Ð»Ñ Ð·Ð°Ð³Ð°Ð´Ð°Ñž
+Comment[bg]=ПриÑтавка за добавÑне на запазени думи, при въвеждането на които ще Ñе изпълнÑват зададени команди
+Comment[bn]=কমà§à¦¯à¦¾à¦¨à§à¦¡à§‡à¦° জনà§à¦¯ সà§à¦¬à¦¨à¦¿à¦°à§à¦¬à¦¾à¦šà¦¿à¦¤ ছদà§à¦®à¦¨à¦¾à¦® যোগ করে
+Comment[bs]=Dodajte vlastite aliase naredbama
+Comment[ca]=Afegeix àlies personalitzats als vostres comandaments
+Comment[cs]=Přidává vlastní přezdívky pro příkazy
+Comment[cy]=Ychwanegu ffugenwau addasiedig ar gyfer gorchmynion
+Comment[da]=Tilføj personligt alias for kommandoer
+Comment[de]=Fügt benutzerdefinierte Aliase für Befehle hinzu
+Comment[el]=ΠÏοσθέτει Ï€ÏοσαÏμοσμένες αντιστοιχίες για εντολές
+Comment[es]=Añade alias personales a las órdenes
+Comment[et]=Lisab käskudele kohandatud aliase
+Comment[eu]=Alias pertsonalizatuak gehitzen ditu aginduentzat
+Comment[fa]=نام‌گردانهای سÙارشی را برای Ùرمانها می‌اÙزاید
+Comment[fi]=Lisää uusia nimiä komennoille
+Comment[fr]=Ajoute des alias personnalisés à des commandes
+Comment[gl]=Engadir alias persoáis para comandos
+Comment[he]=הוספת ×›×™× ×•×™× ×ž×•×ª××ž×™× ×ישית עבור פקודות
+Comment[hi]=कमांडà¥à¤¸ के लिठमनपसंद अलाà¤à¤¸ जोड़े
+Comment[hr]=Dodaje posebne aliase za naredbe
+Comment[hu]=Egyéni másodlagos (alias) nevek megadása parancsokhoz
+Comment[is]=Bætir við samheitum á skipanir
+Comment[it]=Aggiungi alias personalizzati per i comandi
+Comment[ja]=コマンドã®ã‚«ã‚¹ã‚¿ãƒ ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’追加
+Comment[ka]=ბრძáƒáƒœáƒ”ბებს áƒáƒœáƒ˜áƒ­áƒ”ბს სხვáƒáƒ“áƒáƒ¡áƒ®áƒ•áƒ ფსევდáƒáƒœáƒ˜áƒ›áƒ”ბს
+Comment[kk]=Командаларға бүркеншік атауларды беру
+Comment[km]=បន្ážáŸ‚ម​ឈ្មោះ​ក្លែងក្លាយ​ផ្ទាល់​ážáŸ’លួន​សម្រាប់​ពាក្យ​បញ្ជា
+Comment[lt]=Komandoms suteikiami papildomi vardai
+Comment[mk]=Додава ÑопÑтвени алтернативни имиња на командите
+Comment[nb]=Legg til egne alias for kommandoer
+Comment[nds]=Föögt egen Aliases för Befehlen to
+Comment[ne]=आदेशका लागि अनà¥à¤•à¥‚ल उपनामहरू थपà¥à¤¦à¤›
+Comment[nl]=Toevoegen van eigen aliassen voor commando's
+Comment[nn]=Legg til eigne alias for kommandoar
+Comment[pl]=Dodawanie własnych aliasów dla poleceń
+Comment[pt]=Adiciona novos nomes para os comandos
+Comment[pt_BR]=Adiciona notas pessoais em seus contatos
+Comment[ru]=ДобавлÑет пÑевдонимы Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´
+Comment[se]=Lasit iežat aliasaid gohÄÄumiid várás
+Comment[sk]=Pridá vlastné aliasy pre príkazy
+Comment[sl]=Dodajanje drugih imen za ukaze
+Comment[sr]=Додаје поÑебне алијаÑе за наредбе
+Comment[sr@Latn]=Dodaje posebne alijase za naredbe
+Comment[sv]=Lägger till egna alias för kommandon
+Comment[ta]=கடà¯à®Ÿà®³à¯ˆà®•à®³à¯ தன௠விரà¯à®ªà¯à®ªà®ªà¯ பெயரை சேரà¯à®•à¯à®•à¯à®®à¯
+Comment[tg]=Барои фармонҳо тахаллуÑҳои дигарро илова мекунад
+Comment[tr]=Komutlar için özel takma isimler ekler
+Comment[uk]=Додає пÑевдоніми Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´
+Comment[zh_CN]=添加命令的自定义别å
+Comment[zh_HK]=為命令加上自訂別å
+Comment[zh_TW]=新增命令的別å
+
diff --git a/kopete/plugins/alias/kopete_alias_config.desktop b/kopete/plugins/alias/kopete_alias_config.desktop
new file mode 100644
index 00000000..01fadd24
--- /dev/null
+++ b/kopete/plugins/alias/kopete_alias_config.desktop
@@ -0,0 +1,104 @@
+[Desktop Entry]
+Icon=color
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_alias
+X-KDE-FactoryName=AliasConfigFactory
+X-KDE-ParentApp=kopete_alias
+X-KDE-ParentComponents=kopete_alias
+
+Name=Alias
+Name[ar]=اسم مستعار
+Name[be]=ПÑеўданім
+Name[bg]=Запазени думи
+Name[bn]=ছদà§à¦®à¦¨à¦¾à¦®
+Name[br]=Lesanv
+Name[ca]=Àlies
+Name[cy]=Ffugenw
+Name[el]=Αντιστοιχία
+Name[eo]=Alinomo
+Name[eu]=Aliasa
+Name[fa]=نام‌گردان
+Name[fi]=Uusi nimi
+Name[ga]=Ailias
+Name[he]=כינוי
+Name[hi]=अलाà¤à¤¸
+Name[hu]=Alias nevek
+Name[is]=Samheiti
+Name[ja]=エイリアス
+Name[ka]=ფსევდáƒáƒœáƒ˜áƒ›áƒ˜
+Name[kk]=Бүркеншік атаулар
+Name[km]=ឈ្មោះ​ក្លែងក្លាយ
+Name[lt]=Kitas vardas
+Name[mk]=Други имиња
+Name[ne]=उपनाम
+Name[pa]=ਉਪ-ਨਾਂ
+Name[pt]=Nome Alternativo
+Name[pt_BR]=Apelidos
+Name[ru]=ПÑевдоним
+Name[sl]=Drugo ime
+Name[sr]=ÐлијаÑ
+Name[sr@Latn]=Alijas
+Name[ta]=மாறà¯à®±à¯à®ªà¯à®ªà¯†à®¯à®°à¯
+Name[tg]=ТахаллуÑ
+Name[tr]=Takma Ä°sim
+Name[uk]=ПÑевдонім
+Name[zh_CN]=别å
+Name[zh_HK]=別å
+Name[zh_TW]=別å
+Comment=Adds custom aliases for commands
+Comment[ar]=يضي٠أسماء مستعارة للأوامر
+Comment[be]=Дадаць адмыÑÐ»Ð¾Ð²Ñ‹Ñ Ð¿Ñеўданімы Ð´Ð»Ñ Ð·Ð°Ð³Ð°Ð´Ð°Ñž
+Comment[bg]=ПриÑтавка за добавÑне на запазени думи, при въвеждането на които ще Ñе изпълнÑват зададени команди
+Comment[bn]=কমà§à¦¯à¦¾à¦¨à§à¦¡à§‡à¦° জনà§à¦¯ সà§à¦¬à¦¨à¦¿à¦°à§à¦¬à¦¾à¦šà¦¿à¦¤ ছদà§à¦®à¦¨à¦¾à¦® যোগ করে
+Comment[bs]=Dodajte vlastite aliase naredbama
+Comment[ca]=Afegeix àlies personalitzats als vostres comandaments
+Comment[cs]=Přidává vlastní přezdívky pro příkazy
+Comment[cy]=Ychwanegu ffugenwau addasiedig ar gyfer gorchmynion
+Comment[da]=Tilføj personligt alias for kommandoer
+Comment[de]=Fügt benutzerdefinierte Aliase für Befehle hinzu
+Comment[el]=ΠÏοσθέτει Ï€ÏοσαÏμοσμένες αντιστοιχίες για εντολές
+Comment[es]=Añade alias personales a las órdenes
+Comment[et]=Lisab käskudele kohandatud aliase
+Comment[eu]=Alias pertsonalizatuak gehitzen ditu aginduentzat
+Comment[fa]=نام‌گردانهای سÙارشی را برای Ùرمانها می‌اÙزاید
+Comment[fi]=Lisää uusia nimiä komennoille
+Comment[fr]=Ajoute des alias personnalisés à des commandes
+Comment[gl]=Engadir alias persoáis para comandos
+Comment[he]=הוספת ×›×™× ×•×™× ×ž×•×ª××ž×™× ×ישית עבור פקודות
+Comment[hi]=कमांडà¥à¤¸ के लिठमनपसंद अलाà¤à¤¸ जोड़े
+Comment[hr]=Dodaje posebne aliase za naredbe
+Comment[hu]=Egyéni másodlagos (alias) nevek megadása parancsokhoz
+Comment[is]=Bætir við samheitum á skipanir
+Comment[it]=Aggiungi alias personalizzati per i comandi
+Comment[ja]=コマンドã®ã‚«ã‚¹ã‚¿ãƒ ã‚¨ã‚¤ãƒªã‚¢ã‚¹ã‚’追加
+Comment[ka]=ბრძáƒáƒœáƒ”ბებს áƒáƒœáƒ˜áƒ­áƒ”ბს სხვáƒáƒ“áƒáƒ¡áƒ®áƒ•áƒ ფსევდáƒáƒœáƒ˜áƒ›áƒ”ბს
+Comment[kk]=Командаларға бүркеншік атауларды беру
+Comment[km]=បន្ážáŸ‚ម​ឈ្មោះ​ក្លែងក្លាយ​ផ្ទាល់​ážáŸ’លួន​សម្រាប់​ពាក្យ​បញ្ជា
+Comment[lt]=Komandoms suteikiami papildomi vardai
+Comment[mk]=Додава ÑопÑтвени алтернативни имиња на командите
+Comment[nb]=Legg til egne alias for kommandoer
+Comment[nds]=Föögt egen Aliases för Befehlen to
+Comment[ne]=आदेशका लागि अनà¥à¤•à¥‚ल उपनामहरू थपà¥à¤¦à¤›
+Comment[nl]=Toevoegen van eigen aliassen voor commando's
+Comment[nn]=Legg til eigne alias for kommandoar
+Comment[pl]=Dodawanie własnych aliasów dla poleceń
+Comment[pt]=Adiciona novos nomes para os comandos
+Comment[pt_BR]=Adiciona notas pessoais em seus contatos
+Comment[ru]=ДобавлÑет пÑевдонимы Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´
+Comment[se]=Lasit iežat aliasaid gohÄÄumiid várás
+Comment[sk]=Pridá vlastné aliasy pre príkazy
+Comment[sl]=Dodajanje drugih imen za ukaze
+Comment[sr]=Додаје поÑебне алијаÑе за наредбе
+Comment[sr@Latn]=Dodaje posebne alijase za naredbe
+Comment[sv]=Lägger till egna alias för kommandon
+Comment[ta]=கடà¯à®Ÿà®³à¯ˆà®•à®³à¯ தன௠விரà¯à®ªà¯à®ªà®ªà¯ பெயரை சேரà¯à®•à¯à®•à¯à®®à¯
+Comment[tg]=Барои фармонҳо тахаллуÑҳои дигарро илова мекунад
+Comment[tr]=Komutlar için özel takma isimler ekler
+Comment[uk]=Додає пÑевдоніми Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´
+Comment[zh_CN]=添加命令的自定义别å
+Comment[zh_HK]=為命令加上自訂別å
+Comment[zh_TW]=新增命令的別å
+
diff --git a/kopete/plugins/autoreplace/Makefile.am b/kopete/plugins/autoreplace/Makefile.am
new file mode 100644
index 00000000..511da48d
--- /dev/null
+++ b/kopete/plugins/autoreplace/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+SUBDIRS = icons
+
+kde_module_LTLIBRARIES = kopete_autoreplace.la kcm_kopete_autoreplace.la
+
+kopete_autoreplace_la_SOURCES = autoreplaceplugin.cpp autoreplaceconfig.cpp
+kopete_autoreplace_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_autoreplace_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_autoreplace_la_SOURCES = autoreplacepreferences.cpp autoreplaceconfig.cpp autoreplaceprefs.ui
+kcm_kopete_autoreplace_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_autoreplace_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_autoreplace.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_autoreplace_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
diff --git a/kopete/plugins/autoreplace/autoreplaceconfig.cpp b/kopete/plugins/autoreplace/autoreplaceconfig.cpp
new file mode 100644
index 00000000..0407990a
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplaceconfig.cpp
@@ -0,0 +1,133 @@
+/*
+ autoreplaceconfig.cpp
+
+ Copyright (c) 2003 by Roberto Pariset <victorheremita@fastwebnet.it>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "autoreplaceconfig.h"
+
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+AutoReplaceConfig::AutoReplaceConfig()
+{
+ load();
+}
+
+// reload configuration reading it from kopeterc
+void AutoReplaceConfig::load()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup( "AutoReplace Plugin" );
+
+ QStringList wordsList = config->readListEntry( "WordsToReplace" );
+ if( wordsList.isEmpty() )
+ {
+ // basic list, key/value
+ // a list based on i18n should be provided, i.e. for italian
+ // "qsa,qualcosa,qno,qualcuno" remember UTF-8 accents
+ wordsList = defaultAutoReplaceList();
+ }
+
+ // we may be reloading after removing an entry from the list
+ m_map.clear();
+ QString k, v;
+ for ( QStringList::Iterator it = wordsList.begin(); it != wordsList.end(); ++it )
+ {
+ k = *it;
+ ++it;
+ if( it == wordsList.end() )
+ break;
+ v = *it;
+ m_map.insert( k, v );
+ }
+
+ m_autoreplaceIncoming = config->readBoolEntry( "AutoReplaceIncoming" , false );
+ m_autoreplaceOutgoing = config->readBoolEntry( "AutoReplaceOutgoing" , true );
+ m_addDot = config->readBoolEntry( "DotEndSentence" , false );
+ m_upper = config->readBoolEntry( "CapitalizeBeginningSentence" , false );
+}
+
+QStringList AutoReplaceConfig::defaultAutoReplaceList()
+{
+ return QStringList::split( ",", i18n( "list_of_words_to_replace",
+ "ur,your,r,are,u,you,theres,there is,arent,are not,dont,do not" ) );
+}
+
+void AutoReplaceConfig::loadDefaultAutoReplaceList()
+{
+ QStringList wordsList = defaultAutoReplaceList();
+ m_map.clear();
+ QString k, v;
+ for ( QStringList::Iterator it = wordsList.begin(); it != wordsList.end(); ++it )
+ {
+ k = *it;
+ v = *( ++it );
+ m_map.insert( k, v );
+ }
+}
+
+
+bool AutoReplaceConfig::autoReplaceIncoming() const
+{
+ return m_autoreplaceIncoming;
+}
+
+bool AutoReplaceConfig::autoReplaceOutgoing() const
+{
+ return m_autoreplaceOutgoing;
+}
+
+bool AutoReplaceConfig::dotEndSentence() const
+{
+ return m_addDot;
+}
+
+bool AutoReplaceConfig::capitalizeBeginningSentence() const
+{
+ return m_upper;
+}
+
+void AutoReplaceConfig::setMap( const WordsToReplace &w )
+{
+ m_map = w;
+}
+
+AutoReplaceConfig::WordsToReplace AutoReplaceConfig::map() const
+{
+ return m_map;
+}
+
+void AutoReplaceConfig::save()
+{
+ KConfig * config = KGlobal::config();
+ config->setGroup( "AutoReplace Plugin" );
+
+ QStringList newWords;
+ WordsToReplace::Iterator it;
+ for ( it = m_map.begin(); it != m_map.end(); ++it )
+ {
+ newWords.append( it.key() );
+ newWords.append( it.data() );
+ }
+
+ config->writeEntry( "WordsToReplace", newWords );
+
+ config->sync();
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplaceconfig.h b/kopete/plugins/autoreplace/autoreplaceconfig.h
new file mode 100644
index 00000000..62b11fbf
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplaceconfig.h
@@ -0,0 +1,58 @@
+/*
+ autoreplaceconfig.h
+
+ Copyright (c) 2003 by Roberto Pariset <victorheremita@fastwebnet.it>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#ifndef AutoReplaceConfig_H
+#define AutoReplaceConfig_H
+
+class AutoReplaceConfig
+{
+public:
+ AutoReplaceConfig();
+
+ void save();
+ void load();
+
+ typedef QMap<QString, QString> WordsToReplace;
+
+ WordsToReplace map() const;
+ bool autoReplaceIncoming() const;
+ bool autoReplaceOutgoing() const;
+ bool dotEndSentence() const;
+ bool capitalizeBeginningSentence() const;
+
+ void setMap( const WordsToReplace &w );
+ QStringList defaultAutoReplaceList();
+ void loadDefaultAutoReplaceList();
+
+private:
+ WordsToReplace m_map;
+
+ bool m_autoreplaceIncoming;
+ bool m_autoreplaceOutgoing;
+ bool m_addDot;
+ bool m_upper;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplaceplugin.cpp b/kopete/plugins/autoreplace/autoreplaceplugin.cpp
new file mode 100644
index 00000000..c06bc0bd
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplaceplugin.cpp
@@ -0,0 +1,128 @@
+/***************************************************************************
+ autoreplaceplugin.cpp - description
+ -------------------
+ begin : 20030425
+ copyright : (C) 2003 by Roberto Pariset
+ email : victorheremita@fastwebnet.it
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <kgenericfactory.h>
+
+#include <kopetecontact.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopetesimplemessagehandler.h"
+
+#include "autoreplaceplugin.h"
+#include "autoreplaceconfig.h"
+
+typedef KGenericFactory<AutoReplacePlugin> AutoReplacePluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_autoreplace, AutoReplacePluginFactory( "kopete_autoreplace" ) )
+AutoReplacePlugin * AutoReplacePlugin::pluginStatic_ = 0L;
+
+AutoReplacePlugin::AutoReplacePlugin( QObject *parent, const char * name, const QStringList & )
+: Kopete::Plugin( AutoReplacePluginFactory::instance(), parent, name )
+{
+ if( !pluginStatic_ )
+ pluginStatic_ = this;
+
+ m_prefs = new AutoReplaceConfig;
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToSend( Kopete::Message & ) ),
+ this, SLOT( slotAboutToSend( Kopete::Message & ) ) );
+
+ // nb this connection causes the slot to be called on in- and outbound
+ // messages which suggests something is broken in the message handler
+ // system!
+ m_inboundHandler = new Kopete::SimpleMessageHandlerFactory( Kopete::Message::Inbound,
+ Kopete::MessageHandlerFactory::InStageToSent, this, SLOT( slotAboutToSend( Kopete::Message& ) ) );
+
+ connect( this, SIGNAL( settingsChanged() ), this, SLOT( slotSettingsChanged() ) );
+}
+
+AutoReplacePlugin::~AutoReplacePlugin()
+{
+ pluginStatic_ = 0L;
+ delete m_inboundHandler;
+ delete m_prefs;
+}
+
+AutoReplacePlugin * AutoReplacePlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+void AutoReplacePlugin::slotSettingsChanged()
+{
+ m_prefs->load();
+}
+
+void AutoReplacePlugin::slotAboutToSend( Kopete::Message &msg )
+{
+ if ( ( msg.direction() == Kopete::Message::Outbound && m_prefs->autoReplaceOutgoing() ) ||
+ ( msg.direction() == Kopete::Message::Inbound && m_prefs->autoReplaceIncoming() ) )
+ {
+ QString replaced_message = msg.plainBody();
+ AutoReplaceConfig::WordsToReplace map = m_prefs->map();
+
+ // replaces all matched words --> try to find a more 'economic' way
+ // "\\b(%1)\\b" doesn't work when substituting /me.
+ QString match = "(^|\\s|\\.|\\;|\\,|\\:)(%1)(\\b)";
+ AutoReplaceConfig::WordsToReplace::Iterator it;
+ bool isReplaced=false;
+ for ( it = map.begin(); it != map.end(); ++it )
+ {
+ QRegExp re( match.arg( QRegExp::escape( it.key() ) ) );
+ if( re.search( replaced_message ) != -1 )
+ {
+ QString before = re.cap(1);
+ QString after = re.cap(3);
+ replaced_message.replace( re, before + map.find( it.key() ).data() + after );
+ isReplaced=true;
+ }
+ }
+
+ // the message is now the one with replaced words
+ if(isReplaced)
+ msg.setBody( replaced_message, Kopete::Message::PlainText );
+
+ if( msg.direction() == Kopete::Message::Outbound )
+ {
+ if ( m_prefs->dotEndSentence() )
+ {
+ QString replaced_message = msg.plainBody();
+ // eventually add . at the end of the lines, sent lines only
+ replaced_message.replace( QRegExp( "([a-z])$" ), "\\1." );
+ // replaced_message.replace(QRegExp( "([\\w])$" ), "\\1." );
+
+ // the message is now the one with replaced words
+ msg.setBody( replaced_message, Kopete::Message::PlainText );
+ }
+
+ if( m_prefs->capitalizeBeginningSentence() )
+ {
+ QString replaced_message = msg.plainBody();
+ // eventually start each sent line with capital letter
+ // TODO ". " "? " "! "
+ replaced_message[ 0 ] = replaced_message.at( 0 ).upper();
+
+ // the message is now the one with replaced words
+ msg.setBody( replaced_message, Kopete::Message::PlainText );
+ }
+ }
+ }
+}
+
+#include "autoreplaceplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplaceplugin.h b/kopete/plugins/autoreplace/autoreplaceplugin.h
new file mode 100644
index 00000000..750f0614
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplaceplugin.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ autoreplaceplugin.h - description
+ -------------------
+ begin : 20030425
+ copyright : (C) 2003 by Roberto Pariset
+ email : victorheremita@fastwebnet.it
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 AutoReplacePLUGIN_H
+#define AutoReplacePLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qregexp.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+namespace Kopete {
+ class Message;
+ class MetaContact;
+ class ChatSession;
+ class SimpleMessageHandlerFactory;
+}
+
+class AutoReplaceConfig;
+
+class AutoReplacePlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ static AutoReplacePlugin *plugin();
+
+ AutoReplacePlugin( QObject *parent, const char *name, const QStringList &args );
+ ~AutoReplacePlugin();
+
+private slots:
+ void slotAboutToSend( Kopete::Message &msg );
+
+ void slotSettingsChanged();
+
+private:
+ static AutoReplacePlugin * pluginStatic_;
+ Kopete::SimpleMessageHandlerFactory *m_inboundHandler;
+
+ AutoReplaceConfig *m_prefs;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplacepreferences.cpp b/kopete/plugins/autoreplace/autoreplacepreferences.cpp
new file mode 100644
index 00000000..0a2a6b0f
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplacepreferences.cpp
@@ -0,0 +1,215 @@
+/***************************************************************************
+ autoreplacepreferences.cpp - description
+ -------------------
+ begin : 20030426
+ copyright : (C) 2003 by Roberto Pariset
+ email : victorheremita@fastwebnet.it
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qcheckbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qheader.h>
+#include <qlistview.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <kglobal.h>
+#include <kgenericfactory.h>
+#include <kautoconfig.h>
+
+#include "autoreplaceprefs.h"
+#include "autoreplacepreferences.h"
+#include "autoreplaceconfig.h"
+
+typedef KGenericFactory<AutoReplacePreferences> AutoReplacePreferencesFactory;
+
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_autoreplace, AutoReplacePreferencesFactory( "kcm_kopete_autoreplace" ) )
+
+AutoReplacePreferences::AutoReplacePreferences( QWidget *parent, const char * /* name */, const QStringList &args )
+: KCAutoConfigModule( AutoReplacePreferencesFactory::instance(), parent, args )
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new AutoReplacePrefsUI( this );
+
+ // creates table columns (avoids new columns every time)
+ preferencesDialog->m_list->addColumn( i18n("Text" ) );
+ preferencesDialog->m_list->addColumn( i18n("Replacement" ) );
+ preferencesDialog->m_list->header()->setStretchEnabled( true , 1 );
+
+ // connect SIGNALS/SLOTS
+ connect( preferencesDialog->m_add, SIGNAL(pressed()),
+ SLOT( slotAddCouple()) );
+ connect( preferencesDialog->m_edit, SIGNAL(pressed()),
+ SLOT( slotEditCouple()) );
+ connect( preferencesDialog->m_remove, SIGNAL(pressed()),
+ SLOT(slotRemoveCouple()) );
+ connect( preferencesDialog->m_list, SIGNAL(selectionChanged()),
+ SLOT(slotSelectionChanged()) );
+ connect( preferencesDialog->m_key, SIGNAL(textChanged ( const QString & )),
+ SLOT( slotEnableAddEdit( const QString & )) );
+
+ m_wordListChanged = false;
+
+ // Sentence options and which messages to apply autoreplace to
+ // are managed by KCMAutoConfigModule. The list of replacements
+ // itself is manually read/written as KCMAutoConfigModule doesn't support it.
+ autoConfig()->ignoreSubWidget( preferencesDialog->replacementsGroup );
+ setMainWidget( preferencesDialog, "AutoReplace Plugin" );
+
+ m_config = new AutoReplaceConfig;
+ load();
+}
+
+AutoReplacePreferences::~AutoReplacePreferences()
+{
+ delete m_config;
+}
+
+// reload configuration reading it from kopeterc
+void AutoReplacePreferences::load()
+{
+ m_config->load();
+
+ // Removes and deletes all the items in this list view and triggers an update
+ preferencesDialog->m_list->clear();
+
+ // show keys/values on gui
+ AutoReplaceConfig::WordsToReplace::Iterator it;
+ AutoReplaceConfig::WordsToReplace map = m_config->map();
+ for ( it = map.begin(); it != map.end(); ++it )
+ {
+ // notice: insertItem is called automatically by the constructor
+ new QListViewItem( preferencesDialog->m_list, it.key(), it.data() );
+ }
+
+ m_wordListChanged = false;
+ KCAutoConfigModule::load();
+}
+
+// save list to kopeterc and creates map out of it
+void AutoReplacePreferences::save()
+{
+ // make a list reading all values from gui
+ AutoReplaceConfig::WordsToReplace newWords;
+ for ( QListViewItem * i = preferencesDialog->m_list->firstChild(); i != 0; i = i->nextSibling() )
+ newWords[ i->text( 0 ) ] = i->text( 1 );
+
+ // save the words list
+ m_config->setMap( newWords );
+ m_config->save();
+
+ m_wordListChanged = false;
+ KCAutoConfigModule::save();
+}
+
+// read m_key m_value, create a QListViewItem
+void AutoReplacePreferences::slotAddCouple()
+{
+ QString k = preferencesDialog->m_key->text();
+ QString v = preferencesDialog->m_value->text();
+ if ( !k.isEmpty() && !k.isNull() && !v.isEmpty() && !v.isNull() )
+ {
+ QListViewItem * lvi;
+ QListViewItem * oldLvi = 0;
+ // see if we are replacing an existing entry
+ if ( ( oldLvi = preferencesDialog->m_list->findItem( k, 0 ) ) )
+ delete oldLvi;
+ lvi = new QListViewItem( preferencesDialog->m_list, k, v );
+ // Triggers a size, geometry and content update
+ // during the next iteration of the event loop
+ preferencesDialog->m_list->triggerUpdate();
+ // select last added
+ preferencesDialog->m_list->setSelected( lvi, true );
+ }
+
+ m_wordListChanged = true;
+ slotWidgetModified();
+}
+
+// edit the selected item
+void AutoReplacePreferences::slotEditCouple()
+{
+ QString k = preferencesDialog->m_key->text();
+ QString v = preferencesDialog->m_value->text();
+ QListViewItem * lvi;
+ if ( ( lvi = preferencesDialog->m_list->selectedItem() ) && !k.isEmpty() && !k.isNull() && !v.isEmpty() && !v.isNull() )
+ {
+ lvi->setText( 0, k );
+ lvi->setText( 1, v );
+ preferencesDialog->m_list->triggerUpdate();
+ m_wordListChanged = true;
+ slotWidgetModified();
+ }
+}
+
+// Returns a pointer to the selected item if the list view is in
+// Single selection mode and an item is selected
+void AutoReplacePreferences::slotRemoveCouple()
+{
+ delete preferencesDialog->m_list->selectedItem();
+
+ m_wordListChanged = true;
+ slotWidgetModified();
+}
+
+void AutoReplacePreferences::slotEnableAddEdit( const QString & keyText )
+{
+ preferencesDialog->m_add->setEnabled( !keyText.isEmpty() );
+ preferencesDialog->m_edit->setEnabled( !keyText.isEmpty() && preferencesDialog->m_list->selectedItem() );
+}
+
+void AutoReplacePreferences::slotSelectionChanged()
+{
+ QListViewItem *selection = 0;
+ if ( ( selection = preferencesDialog->m_list->selectedItem() ) )
+ {
+ // enable the remove button
+ preferencesDialog->m_remove->setEnabled( true );
+ // put the selection contents into the text entry widgets so they can be edited
+ preferencesDialog->m_key->setText( selection->text( 0 ) );
+ preferencesDialog->m_value->setText( selection->text( 1 ) );
+ }
+ else
+ {
+ preferencesDialog->m_remove->setEnabled( false );
+ preferencesDialog->m_key->clear();
+ preferencesDialog->m_value->clear();
+ }
+}
+
+void AutoReplacePreferences::slotWidgetModified()
+{
+ emit KCModule::changed( m_wordListChanged || autoConfig()->hasChanged() );
+}
+
+void AutoReplacePreferences::defaults()
+{
+ KCAutoConfigModule::defaults();
+ preferencesDialog->m_list->clear();
+ m_config->loadDefaultAutoReplaceList();
+ AutoReplaceConfig::WordsToReplace::Iterator it;
+ AutoReplaceConfig::WordsToReplace map = m_config->map();
+ for ( it = map.begin(); it != map.end(); ++it )
+ {
+ // notice: insertItem is called automatically by the constructor
+ new QListViewItem( preferencesDialog->m_list, it.key(), it.data() );
+ }
+ m_wordListChanged = true;
+ slotWidgetModified();
+}
+
+#include "autoreplacepreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplacepreferences.h b/kopete/plugins/autoreplace/autoreplacepreferences.h
new file mode 100644
index 00000000..a08b2ba2
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplacepreferences.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ autoreplacepreferences.h - description
+ -------------------
+ begin : 20030426
+ copyright : (C) 2003 by Roberto Pariset
+ email : victorheremita@fastwebnet.it
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 AutoReplacePREFERENCES_H
+#define AutoReplacePREFERENCES_H
+
+#include "kcautoconfigmodule.h"
+
+class AutoReplacePrefsUI;
+class AutoReplaceConfig;
+
+ // TODO
+ // add button enabled only when k and v are present
+ // remove button enabled only when a QListViewItem is selected
+ // signal/slot when map changes (needed?)
+ // capital letter not just at the beginning but always after ". ", "! "...
+
+class AutoReplacePreferences : public KCAutoConfigModule
+{
+ Q_OBJECT
+
+public:
+ AutoReplacePreferences( QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList() );
+ ~AutoReplacePreferences();
+
+ virtual void save();
+ virtual void load();
+ virtual void defaults();
+
+private slots:
+ //void slotSettingsDirty();
+ void slotAddCouple();
+ void slotEditCouple();
+ void slotRemoveCouple();
+ void slotEnableAddEdit( const QString & );
+ void slotSelectionChanged();
+
+protected slots:
+ virtual void slotWidgetModified();
+private:
+ AutoReplacePrefsUI * preferencesDialog;
+ AutoReplaceConfig *m_config;
+
+ bool m_wordListChanged;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/autoreplace/autoreplaceprefs.ui b/kopete/plugins/autoreplace/autoreplaceprefs.ui
new file mode 100644
index 00000000..09db8e9d
--- /dev/null
+++ b/kopete/plugins/autoreplace/autoreplaceprefs.ui
@@ -0,0 +1,219 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AutoReplacePrefsUI</class>
+<author>Roberto Pariset</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AutoReplacePrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>458</width>
+ <height>378</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>gb_sentences</cstring>
+ </property>
+ <property name="title">
+ <string>Sentence Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>DotEndSentence</cstring>
+ </property>
+ <property name="text">
+ <string>Add a dot at the end of each sent line</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>CapitalizeBeginningSentence</cstring>
+ </property>
+ <property name="text">
+ <string>Start each sent line with a capital letter</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>gb_options</cstring>
+ </property>
+ <property name="title">
+ <string>Replacement Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>AutoReplaceIncoming</cstring>
+ </property>
+ <property name="text">
+ <string>Auto replace on incoming messages</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>AutoReplaceOutgoing</cstring>
+ </property>
+ <property name="text">
+ <string>Auto replace on outgoing messages</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>replacementsGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Replacements List</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_add</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ <property name="on">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_edit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Edit</string>
+ </property>
+ <property name="on">
+ <bool>false</bool>
+ </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>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_remove</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <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>Te&amp;xt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_key</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_key</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;placement:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_value</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_value</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QListView" row="2" column="0">
+ <property name="name">
+ <cstring>m_list</cstring>
+ </property>
+ <property name="selectionMode">
+ <enum>Single</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/plugins/autoreplace/icons/Makefile.am b/kopete/plugins/autoreplace/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/plugins/autoreplace/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/plugins/autoreplace/icons/cr32-app-autoreplace.png b/kopete/plugins/autoreplace/icons/cr32-app-autoreplace.png
new file mode 100644
index 00000000..71f1fa32
--- /dev/null
+++ b/kopete/plugins/autoreplace/icons/cr32-app-autoreplace.png
Binary files differ
diff --git a/kopete/plugins/autoreplace/kopete_autoreplace.desktop b/kopete/plugins/autoreplace/kopete_autoreplace.desktop
new file mode 100644
index 00000000..7189fdc2
--- /dev/null
+++ b/kopete/plugins/autoreplace/kopete_autoreplace.desktop
@@ -0,0 +1,128 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=autoreplace
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_autoreplace
+X-KDE-PluginInfo-Author=Roberto Pariset
+X-KDE-PluginInfo-Email=victorheremita@fastwebnet.it
+X-KDE-PluginInfo-Name=kopete_autoreplace
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Auto Replace
+Name[ar]=الاستبدال التلقائي
+Name[be]=ÐÑžÑ‚Ð°Ð¼Ð°Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÐ½Ð°
+Name[bg]=Ðвтоматична замÑна
+Name[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿ পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨
+Name[bs]=Auto zamjena
+Name[ca]=Auto-substitució
+Name[cs]=Automatické nahrazení
+Name[cy]=Hunan-Amnewid
+Name[da]=Auto-erstat
+Name[de]=Automatische Ersetzung
+Name[el]=Αυτόματη αντικατάσταση
+Name[eo]=AÅ­tomata anstataÅ­igo
+Name[es]=Auto reemplazar
+Name[et]=Automaatne asendamine
+Name[eu]=Auto ordezkatu
+Name[fa]=جایگزینی خودکار
+Name[fi]=Automaattinen korvaus
+Name[fr]=Remplacement automatique
+Name[gl]=Auto-reemplazo
+Name[he]=החלפה ×וטומטית
+Name[hi]=सà¥à¤µà¤šà¤²à¤¿à¤¤ बदलें
+Name[hr]=Automatska zamijena
+Name[hu]=Automatikus szövegcsere
+Name[is]=Skipta sjálfkrafa út
+Name[it]=Sostituisci automaticamente
+Name[ja]=自動置æ›
+Name[ka]=áƒáƒ•áƒ¢áƒ ჩáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბáƒ
+Name[kk]=Ðвто алмаÑтыру
+Name[km]=ជំនួស​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Name[lt]=Automatinis keitimas
+Name[mk]=ÐвтоматÑка замена
+Name[nb]=Automatisk utskifting
+Name[nds]=Automaatsch utwesseln
+Name[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨
+Name[nl]=Automatisch vervangen
+Name[nn]=Automatisk utbyting
+Name[pa]=ਆਟੋ ਤਬਦੀਲ
+Name[pl]=Automatyczne zastępowanie
+Name[pt]=Substituição Automática
+Name[pt_BR]=Substituição Automática
+Name[ro]=Înlocuire automată
+Name[ru]=Ðвтозамена
+Name[se]=Auto-buhtte
+Name[sk]=Automatické náhrady
+Name[sl]=Samo-zamenjava
+Name[sr]=ÐутоматÑка замена
+Name[sr@Latn]=Automatska zamena
+Name[sv]=Ersätt automatiskt
+Name[ta]=தனà¯à®©à®¿à®¯à®•à¯à®• மாறà¯à®±à®¿
+Name[tg]=Ҷойивазкунии Худкор
+Name[tr]=Otomatik DeÄŸiÅŸtir
+Name[uk]=Ðвтоматична заміна
+Name[uz]=Avto-almashtirish
+Name[uz@cyrillic]=Ðвто-алмаштириш
+Name[wa]=Replaecî otomaticmint
+Name[zh_CN]=自动替æ¢
+Name[zh_HK]=自動å–代
+Name[zh_TW]=自動å–代
+Comment=Auto replaces some text you can choose
+Comment[ar]=يقوم بتغيير تلÙائي للنصوص التي يمكن اختيارها
+Comment[be]=ÐÑžÑ‚Ð°Ð¼Ð°Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÐ½Ð° Ñ‚ÑкÑту
+Comment[bg]=ПриÑтавка за автоматична замÑна на текÑÑ‚ в ÑъобщениÑта
+Comment[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ করে কিছৠটেকà§à¦¸à¦Ÿ যা আপনি বেছে নিতে পারেন
+Comment[bs]=Automatski zamjenjuje neki tekst koji izaberete
+Comment[ca]=Auto-substitueix algun text que podreu escollir
+Comment[cs]=Automaticky nahrazuje zvolený text
+Comment[cy]=Hunan-amnewid testun y gallwch ei ddewis
+Comment[da]=Autoerstatter noget tekst du kan vælge
+Comment[de]=Ersetzt wählbare Texte automatisch
+Comment[el]=Αντικαθιστά αυτόματα κάποιο κείμενο που επιλέγετε
+Comment[es]=Autoreemplaza texto que puede elegir
+Comment[et]=Asendab automaatselt sinu valitud teksti
+Comment[eu]=Hautatu dezakezun testua auto ordezten du
+Comment[fa]=بعضی از متنها را که می‌توانید انتخاب کنید، به طور خودکار جایگزین می‌کند
+Comment[fi]=Korvaa valitsemasi tekstit automaattisesti
+Comment[fr]=Remplace automatiquement du texte que vous pouvez choisir
+Comment[gl]=Reemplaza automáticamente algún texto que tí podes escoller
+Comment[he]=מחליף ×וטומטית טקסט לבחירתך
+Comment[hi]=कà¥à¤› पाठ जिनà¥à¤¹à¥‡à¤‚ आप चà¥à¤¨ सकते हैं, सà¥à¤µà¤šà¤²à¤¿à¤¤ बदलें
+Comment[hr]=Automatska zamjena teksta kojeg odaberete
+Comment[hu]=A beállított szövegeket automatikusan lecseréli
+Comment[is]=Skiptir sjálfkrafa út þeim texta sem þú velur
+Comment[it]=Sostituisci automaticamente del testo a scelta
+Comment[ja]=é¸æŠžã—ãŸãƒ†ã‚­ã‚¹ãƒˆã‚’自動置æ›
+Comment[ka]=áƒáƒ áƒ©áƒ”ული ტექსტის áƒáƒ•áƒ¢áƒ ჩáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბáƒ
+Comment[kk]=Таңдаған мәтінді автоматты түрде алмаÑтыру
+Comment[km]=ជំនួស​អážáŸ’ážáž”ទ​ដែល​អ្នក​អាច​ជ្រើស ដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Comment[lt]=AutomatiÅ¡kai keiÄia parinktÄ… tekstÄ…
+Comment[mk]=ÐвтоматÑка замена на некој текÑÑ‚ што го избирате
+Comment[nb]=Skift ut en valgt tekst automatisk
+Comment[nds]=Wesselt automaatsch wat instellbor Text ut
+Comment[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ हà¥à¤¨à¥‡ केही पाठ तपाईà¤à¤²à¥‡ रोजà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Vervangt automatisch tekst die u kunt instellen
+Comment[nn]=Skift ut ein vald tekst automatisk
+Comment[pl]=Automatycznie zastępuje określony tekst
+Comment[pt]=Substitui automaticamente algum texto seleccionado por si
+Comment[pt_BR]=Substitui automaticamente o texto que você escolher
+Comment[ru]=ÐвтоматичеÑки заменÑет текÑÑ‚, который вы вводите
+Comment[se]=AutomáhtalaÄÄat buhtte teavstta maid válljet
+Comment[sk]=Automaticky nahradzuje text, ktorý si vyberiete
+Comment[sl]=Samodejno zamenja besedilo, ki ga lahko izberete
+Comment[sr]=ÐутоматÑка замена неког текÑта који одаберете
+Comment[sr@Latn]=Automatska zamena nekog teksta koji odaberete
+Comment[sv]=Ersätt automatiskt text du kan markera
+Comment[ta]=நீஙà¯à®•à®³à¯ தேரà¯à®µà¯ செயà¯à®¤ உரையை தானாக மாறà¯à®±à®µà¯à®®à¯
+Comment[tg]=Матни интихобкардаи шуморо ба таври худкор иваз мекунад
+Comment[tr]=Seçilen bazı metinler otomatik değiştirilir
+Comment[uk]=Ðвтоматично замінÑÑ” текÑÑ‚, Ñкий ви вкажете
+Comment[zh_CN]=自动替æ¢æ‚¨å¯é€‰æ‹©çš„æŸäº›æ–‡å­—
+Comment[zh_HK]=自動å–代å¯é¸æ“‡çš„文字
+Comment[zh_TW]=自動å–代您所é¸æ“‡çš„文字
+
diff --git a/kopete/plugins/autoreplace/kopete_autoreplace_config.desktop b/kopete/plugins/autoreplace/kopete_autoreplace_config.desktop
new file mode 100644
index 00000000..4efc1abd
--- /dev/null
+++ b/kopete/plugins/autoreplace/kopete_autoreplace_config.desktop
@@ -0,0 +1,124 @@
+[Desktop Entry]
+Icon=color
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_autoreplace
+X-KDE-FactoryName=AutoReplaceConfigFactory
+X-KDE-ParentApp=kopete_autoreplace
+X-KDE-ParentComponents=kopete_autoreplace
+
+Name=Auto Replace
+Name[ar]=الاستبدال التلقائي
+Name[be]=ÐÑžÑ‚Ð°Ð¼Ð°Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÐ½Ð°
+Name[bg]=Ðвтоматична замÑна
+Name[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿ পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨
+Name[bs]=Auto zamjena
+Name[ca]=Auto-substitució
+Name[cs]=Automatické nahrazení
+Name[cy]=Hunan-Amnewid
+Name[da]=Auto-erstat
+Name[de]=Automatische Ersetzung
+Name[el]=Αυτόματη αντικατάσταση
+Name[eo]=AÅ­tomata anstataÅ­igo
+Name[es]=Auto reemplazar
+Name[et]=Automaatne asendamine
+Name[eu]=Auto ordezkatu
+Name[fa]=جایگزینی خودکار
+Name[fi]=Automaattinen korvaus
+Name[fr]=Remplacement automatique
+Name[gl]=Auto-reemplazo
+Name[he]=החלפה ×וטומטית
+Name[hi]=सà¥à¤µà¤šà¤²à¤¿à¤¤ बदलें
+Name[hr]=Automatska zamijena
+Name[hu]=Automatikus szövegcsere
+Name[is]=Skipta sjálfkrafa út
+Name[it]=Sostituisci automaticamente
+Name[ja]=自動置æ›
+Name[ka]=áƒáƒ•áƒ¢áƒ ჩáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბáƒ
+Name[kk]=Ðвто алмаÑтыру
+Name[km]=ជំនួស​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Name[lt]=Automatinis keitimas
+Name[mk]=ÐвтоматÑка замена
+Name[nb]=Automatisk utskifting
+Name[nds]=Automaatsch utwesseln
+Name[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨
+Name[nl]=Automatisch vervangen
+Name[nn]=Automatisk utbyting
+Name[pa]=ਆਟੋ ਤਬਦੀਲ
+Name[pl]=Automatyczne zastępowanie
+Name[pt]=Substituição Automática
+Name[pt_BR]=Substituição Automática
+Name[ro]=Înlocuire automată
+Name[ru]=Ðвтозамена
+Name[se]=Auto-buhtte
+Name[sk]=Automatické náhrady
+Name[sl]=Samo-zamenjava
+Name[sr]=ÐутоматÑка замена
+Name[sr@Latn]=Automatska zamena
+Name[sv]=Ersätt automatiskt
+Name[ta]=தனà¯à®©à®¿à®¯à®•à¯à®• மாறà¯à®±à®¿
+Name[tg]=Ҷойивазкунии Худкор
+Name[tr]=Otomatik DeÄŸiÅŸtir
+Name[uk]=Ðвтоматична заміна
+Name[uz]=Avto-almashtirish
+Name[uz@cyrillic]=Ðвто-алмаштириш
+Name[wa]=Replaecî otomaticmint
+Name[zh_CN]=自动替æ¢
+Name[zh_HK]=自動å–代
+Name[zh_TW]=自動å–代
+Comment=Autoreplaces some text you can choose
+Comment[ar]=تقوم بتغييرتلقائي لبعض النصوص التي يمكنك الاختيار منها
+Comment[be]=ÐÑžÑ‚Ð°Ð¼Ð°Ñ‚Ñ‹Ñ‡Ð½Ð°Ñ Ð·Ð°Ð¼ÐµÐ½Ð° Ñ‚ÑкÑту
+Comment[bg]=ПриÑтавка за автоматична замÑна на текÑÑ‚ в ÑъобщениÑта
+Comment[bn]=সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ পà§à¦°à¦¤à¦¿à¦¸à§à¦¥à¦¾à¦ªà¦¨ করে কিছৠটেকà§à¦¸à¦Ÿ যা আপনি বেছে নিতে পারেন
+Comment[bs]=Automatski zamjenjuje neki tekst koji izaberete
+Comment[ca]=Auto-substitueix algun text que podreu escollir
+Comment[cs]=Automaticky nahrazuje zvolený text
+Comment[cy]=Hunan-amnewid testun y gallwch ei ddewis
+Comment[da]=Autoerstatter noget tekst du kan vælge
+Comment[de]=Ersetzt wählbare Texte automatisch
+Comment[el]=Αντικαθιστά αυτόματα κάποιο κείμενο που επιλέγετε
+Comment[es]=Autoreemplaza texto que puede elegir
+Comment[et]=Asendab automaatselt sinu valitud teksti
+Comment[eu]=Hautatu dezakezun testua auto ordezten du
+Comment[fa]=بعضی از متنها را که می‌توانید انتخاب کنید، به طور خودکار جایگزین می‌کند
+Comment[fi]=Korvaa valitsemasi tekstit automaattisesti
+Comment[fr]=Remplace automatiquement du texte que vous pouvez choisir
+Comment[gl]=Reemplaza algún texto que podes escoller
+Comment[he]=מחליף ×וטומטית טקסט לבחירתך
+Comment[hi]=कà¥à¤› पाठ जिनà¥à¤¹à¥‡à¤‚ आप चà¥à¤¨ सकते हैं, सà¥à¤µà¤šà¤²à¤¿à¤¤ बदलें
+Comment[hr]=Automatska zamjena teksta kojeg odaberete
+Comment[hu]=A kiválasztott szövegeket automatikusan lecseréli
+Comment[is]=Skiptir sjálfkrafa út þeim texta sem þú velur
+Comment[it]=Sostituisci automaticamente del testo a scelta
+Comment[ja]=é¸æŠžã—ãŸãƒ†ã‚­ã‚¹ãƒˆã‚’自動置æ›
+Comment[ka]=áƒáƒ áƒ©áƒ”ული ტექსტის áƒáƒ•áƒ¢áƒ ჩáƒáƒœáƒáƒªáƒ•áƒšáƒ”ბáƒ
+Comment[kk]=Таңдаған мәтінді автоматты түрде алмаÑтыру
+Comment[km]=ជំនួស​អážáŸ’ážáž”ទ​ដែល​អ្នក​អាច​ជ្រើស ដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·
+Comment[lt]=AutomatiÅ¡kai keiÄia parinktÄ… tekstÄ…
+Comment[mk]=ÐвтоматÑка замена на некој текÑÑ‚ што го избирате
+Comment[nb]=Skift ut en valgt tekst automatisk
+Comment[nds]=Wesselt automaatsch wat instellbor Text ut
+Comment[ne]=सà¥à¤µà¤¤: पà¥à¤°à¤¤à¤¿à¤¸à¥à¤¥à¤¾à¤ªà¤¨ हà¥à¤¨à¥‡ केही पाठ तपाईà¤à¤²à¥‡ रोजà¥à¤¨ सकà¥à¤¨à¥à¤¹à¥à¤¨à¥à¤›
+Comment[nl]=Vervangt automatisch tekst die u kunt instellen
+Comment[nn]=Byter ut ein vald tekst automatisk
+Comment[pl]=Automatycznie zastępuje określony tekst
+Comment[pt]=Substitui automaticamente algum texto seleccionado por si
+Comment[pt_BR]=Substitui automaticamente o texto que você escolher
+Comment[ru]=ÐвтоматичеÑки заменÑет выбранный вами текÑÑ‚
+Comment[se]=Buhtte válljejuvvon teavstta automáhtalaš
+Comment[sk]=Automaticky nahradzuje text, ktorý si vyberiete
+Comment[sl]=Samodejno zamenja besedilo, ki ga lahko izberete
+Comment[sr]=ÐутоматÑка замена неког текÑта који одаберете
+Comment[sr@Latn]=Automatska zamena nekog teksta koji odaberete
+Comment[sv]=Ersätt automatiskt text du kan markera
+Comment[ta]=நீஙà¯à®•à®³à¯ தேரà¯à®µà¯ செயà¯à®¤ உரையை தானாக மாறà¯à®±à®µà¯à®®à¯
+Comment[tg]=Матни интихобкардаи шуморо ба таври худкор иваз мекунад
+Comment[tr]=Seçilen bazı metinler otomatik değiştirilir
+Comment[uk]=Ðвтоматично замінÑÑ” текÑÑ‚, Ñкий ви вкажете
+Comment[zh_CN]=自动替æ¢æ‚¨å¯é€‰æ‹©çš„æŸäº›æ–‡å­—
+Comment[zh_HK]=自動å–代å¯é¸æ“‡çš„文字
+Comment[zh_TW]=自動å–代您所é¸æ“‡çš„文字
+
diff --git a/kopete/plugins/connectionstatus/Makefile.am b/kopete/plugins/connectionstatus/Makefile.am
new file mode 100644
index 00000000..7fcb3ed8
--- /dev/null
+++ b/kopete/plugins/connectionstatus/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_connectionstatus.la
+
+kopete_connectionstatus_la_SOURCES = connectionstatusplugin.cpp
+kopete_connectionstatus_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+kopete_connectionstatus_la_LIBADD = ../../libkopete/libkopete.la
+
+service_DATA = kopete_connectionstatus.desktop
+servicedir = $(kde_servicesdir)
diff --git a/kopete/plugins/connectionstatus/connectionstatusplugin.cpp b/kopete/plugins/connectionstatus/connectionstatusplugin.cpp
new file mode 100644
index 00000000..8840c893
--- /dev/null
+++ b/kopete/plugins/connectionstatus/connectionstatusplugin.cpp
@@ -0,0 +1,137 @@
+/*
+ connectionstatusplugin.cpp
+
+ Copyright (c) 2002-2003 by Chris Howells <howells@kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include "connectionstatusplugin.h"
+
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kprocess.h>
+
+#include "kopeteaccountmanager.h"
+
+typedef KGenericFactory<ConnectionStatusPlugin> ConnectionStatusPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_connectionstatus, ConnectionStatusPluginFactory( "kopete_connectionstatus" ) )
+
+ConnectionStatusPlugin::ConnectionStatusPlugin( QObject *parent, const char *name, const QStringList& /* args */ )
+: Kopete::Plugin( ConnectionStatusPluginFactory::instance(), parent, name )
+{
+ kdDebug( 14301 ) << k_funcinfo << endl;
+
+ m_process = 0L;
+
+ m_timer = new QTimer();
+ connect( m_timer, SIGNAL( timeout() ), this, SLOT( slotCheckStatus() ) );
+ m_timer->start( 60000 );
+
+ m_pluginConnected = false;
+}
+
+ConnectionStatusPlugin::~ConnectionStatusPlugin()
+{
+ kdDebug( 14301 ) << k_funcinfo << endl;
+ delete m_timer;
+ delete m_process;
+}
+
+void ConnectionStatusPlugin::slotCheckStatus()
+{
+ kdDebug( 14301 ) << k_funcinfo << endl;
+
+ if ( m_process )
+ {
+ kdWarning( 14301 ) << k_funcinfo << "Previous netstat process is still running!" << endl
+ << "Not starting new netstat. Perhaps your system is under heavy load?" << endl;
+
+ return;
+ }
+
+ m_buffer = QString::null;
+
+ // Use KProcess to run netstat -rn. We'll then parse the output of
+ // netstat -rn in slotProcessStdout() to see if it mentions the
+ // default gateway. If so, we're connected, if not, we're offline
+ m_process = new KProcess;
+ *m_process << "netstat" << "-r";
+
+ connect( m_process, SIGNAL( receivedStdout( KProcess *, char *, int ) ), this, SLOT( slotProcessStdout( KProcess *, char *, int ) ) );
+ connect( m_process, SIGNAL( processExited( KProcess * ) ), this, SLOT( slotProcessExited( KProcess * ) ) );
+
+ if ( !m_process->start( KProcess::NotifyOnExit, KProcess::Stdout ) )
+ {
+ kdWarning( 14301 ) << k_funcinfo << "Unable to start netstat process!" << endl;
+
+ delete m_process;
+ m_process = 0L;
+ }
+}
+
+void ConnectionStatusPlugin::slotProcessExited( KProcess *process )
+{
+ kdDebug( 14301 ) << m_buffer << endl;
+
+ if ( process == m_process )
+ {
+ setConnectedStatus( m_buffer.contains( "default" ) );
+ m_buffer = QString::null;
+ delete m_process;
+ m_process = 0L;
+ }
+}
+
+void ConnectionStatusPlugin::slotProcessStdout( KProcess *, char *buffer, int buflen )
+{
+ // Look for a default gateway
+ //kdDebug( 14301 ) << k_funcinfo << endl;
+ m_buffer += QString::fromLatin1( buffer, buflen );
+ //kdDebug( 14301 ) << qsBuffer << endl;
+}
+
+void ConnectionStatusPlugin::setConnectedStatus( bool connected )
+{
+ //kdDebug( 14301 ) << k_funcinfo << endl;
+
+ // We have to handle a few cases here. First is the machine is connected, and the plugin thinks
+ // we're connected. Then we don't do anything. Next, we can have machine connected, but plugin thinks
+ // we're disconnected. Also, machine disconnected, plugin disconnected -- we
+ // don't do anything. Finally, we can have the machine disconnected, and the plugin thinks we're
+ // connected. This mechanism is required so that we don't keep calling the connect/disconnect functions
+ // constantly.
+
+ if ( connected && !m_pluginConnected )
+ {
+ // The machine is connected and plugin thinks we're disconnected
+ kdDebug( 14301 ) << k_funcinfo << "Setting m_pluginConnected to true" << endl;
+ m_pluginConnected = true;
+ Kopete::AccountManager::self()->connectAll();
+ kdDebug( 14301 ) << k_funcinfo << "We're connected" << endl;
+ }
+ else if ( !connected && m_pluginConnected )
+ {
+ // The machine isn't connected and plugin thinks we're connected
+ kdDebug( 14301 ) << k_funcinfo << "Setting m_pluginConnected to false" << endl;
+ m_pluginConnected = false;
+ Kopete::AccountManager::self()->disconnectAll();
+ kdDebug( 14301 ) << k_funcinfo << "We're offline" << endl;
+ }
+}
+
+#include "connectionstatusplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/connectionstatus/connectionstatusplugin.h b/kopete/plugins/connectionstatus/connectionstatusplugin.h
new file mode 100644
index 00000000..5f0a53bf
--- /dev/null
+++ b/kopete/plugins/connectionstatus/connectionstatusplugin.h
@@ -0,0 +1,58 @@
+/*
+ connectionstatusplugin.h
+
+ Copyright (c) 2002-2003 by Chris Howells <howells@kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CONNECTIONSTATUSPLUGIN_H
+#define CONNECTIONSTATUSPLUGIN_H
+
+#include "kopeteplugin.h"
+
+class QTimer;
+class KProcess;
+
+/**
+ * @author Chris Howells <howells@kde.org>
+ */
+class ConnectionStatusPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ ConnectionStatusPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~ConnectionStatusPlugin();
+
+private slots:
+ void slotCheckStatus();
+ void slotProcessStdout( KProcess *process, char *buffer, int len );
+
+ /**
+ * Notify when the netstat process has exited
+ */
+ void slotProcessExited( KProcess *process );
+
+private:
+ void setConnectedStatus( bool newStatus );
+
+ bool m_pluginConnected;
+ KProcess *m_process;
+ QTimer *m_timer;
+ QString m_buffer;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/connectionstatus/kopete_connectionstatus.desktop b/kopete/plugins/connectionstatus/kopete_connectionstatus.desktop
new file mode 100644
index 00000000..bbad7456
--- /dev/null
+++ b/kopete/plugins/connectionstatus/kopete_connectionstatus.desktop
@@ -0,0 +1,125 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_connectionstatus
+X-KDE-PluginInfo-Author=Chris Howells
+X-KDE-PluginInfo-Email=howells@kde.org
+X-KDE-PluginInfo-Name=kopete_connectionstatus
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Connection Status
+Name[ar]=وضع الاتصال
+Name[be]=Стан злучÑннÑ
+Name[bg]=СъÑтоÑние на връзката
+Name[bn]=সংযোগ অবসà§à¦¥à¦¾
+Name[bs]=Status veze
+Name[ca]=Estatus de la connexió
+Name[cs]=Stav spojení
+Name[cy]=Cyflwr Cysylltiad
+Name[da]=Forbindelsesstatus
+Name[de]=Verbindungsstatus
+Name[el]=Κατάσταση σÏνδεσης
+Name[eo]=Konektostato
+Name[es]=Estado de conexión
+Name[et]=Ãœhenduse staatus
+Name[eu]=Konexioaren egoera
+Name[fa]=وضعیت اتصال
+Name[fi]=Yhteyden tila
+Name[fr]=État de la connexion
+Name[ga]=Stádas Ceangail
+Name[gl]=Estado da conexión1
+Name[he]=מצב החיבור
+Name[hi]=कनेकà¥à¤¶à¤¨ सà¥à¤¥à¤¿à¤¤à¤¿
+Name[hr]=Status veze
+Name[hu]=A kapcsolat állapota
+Name[is]=Staða tengingar
+Name[it]=Stato della connessione
+Name[ja]=接続状æ³
+Name[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ˜
+Name[kk]=ҚоÑылымның күй-жайы
+Name[km]=ស្ážáž¶áž“ភាព​ážáž€áž¶ážšâ€‹â€‹áž—្ជាប់
+Name[lt]=Ryšio būsena
+Name[mk]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð½Ð° поврзувањето
+Name[nb]=Tilstand for forbindelsen
+Name[nds]=Verbinnen-Status
+Name[ne]=जडान वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿
+Name[nl]=Verbindingsstatus
+Name[nn]=Tilstand for sambandet
+Name[pa]=ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਹਾਲਤ
+Name[pl]=Status połączenia
+Name[pt]=Estado da Ligação
+Name[pt_BR]=Status da Conexão
+Name[ro]=Stare conexiune
+Name[ru]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑоединениÑ
+Name[se]=Oktavuohtástáhtus
+Name[sk]=Stav spojenia
+Name[sl]=Stanje povezave
+Name[sr]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð²ÐµÐ·Ðµ
+Name[sr@Latn]=Status veze
+Name[sv]=Anslutningsstatus
+Name[ta]=இணைபà¯à®ªà¯ நிலை
+Name[tg]=Ҳолати ПайваÑтшавӣ
+Name[tr]=Bağlantı Durumu
+Name[uk]=Стан з'єднаннÑ
+Name[uz]=Aloqaning holati
+Name[uz@cyrillic]=Ðлоқанинг ҳолати
+Name[wa]=Estat do raloyaedje
+Name[zh_CN]=连接状æ€
+Name[zh_HK]=連線狀態
+Name[zh_TW]=連線狀態
+Comment=Connects/disconnects Kopete automatically depending on availability of Internet connection
+Comment[ar]=يقوم بوصل Ùˆ Ùصل Kopete تلقائيا استنادا إلى وضع الاتصال بالشبكة
+Comment[be]=Злучае/адлучае Kopete ад ÑервіÑаў у залежнаÑці ад наÑўнаÑці інтÑрфÑйÑаў з ІнтÑрнÑтам
+Comment[bg]=Ðвтоматично уÑтановÑване и прекъÑване на връзката в завиÑимоÑÑ‚ от ÑÑŠÑтоÑнието на връзката Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚
+Comment[bn]=ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ সংযোগের সà§à¦¬à¦¿à¦§à¦¾ অনà§à¦¯à¦¾à§Ÿà§€ কপেট সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ সংযোগ/সংযোগবিচà§à¦›à¦¿à¦¨à§à¦¨à¦•à¦°à§‡
+Comment[bs]=Automatski spaja Kopete i prekida vezu ovisno o dostupnosti Internet konekcije
+Comment[ca]=Connecta/desconnecta automàticament depenent de la disponibilitat de la connexió a Internet
+Comment[cs]=Automaticky připojí nebo odpojí vzhledem k dostupnosti připojení na Internet
+Comment[cy]=Cysylltu/datgyslltu Kopete yn ymysgogol, yn dibynnu ar argaeledd cysylltiad Rhyngrwyd
+Comment[da]=Forbinder/afbryder Kopete automatisk afhængig af om der er en internet-forbindelse
+Comment[de]=Verbindet/trennt Kopete automatisch abhängig von der Verfügbarkeit einer Internetverbindung
+Comment[el]=Συνδέει/αποσυνδέει το Kopete αυτόματα ανάλογα με την κατάσταση της σÏνδεσης με το διαδίκτυο
+Comment[es]=Conecta/desconecta Kopete automáticamente según la disponibilidad de la conexión a internet
+Comment[et]=Kopete automaatne ühendamine/ühenduse katkestamine vastavalt internetiühenduse olemasolule
+Comment[eu]=Kopete automatikoki konektatu/deskonektatzen du internet-eko konexioaren eskuragarritasunaren arabera
+Comment[fa]=بسته به قابلیت دسترسی اتصال اینترنت، Kopete به طور خودکار وصل/قطع ارتباط می‌کند
+Comment[fi]=Yhdistää/katkaisee yhteyden automaattisesti riippuen onko Internet-yhteys päällä
+Comment[fr]=Connecte / déconnecte automatiquement Kopete en fonction de la disponibilité de la connexion Internet
+Comment[gl]=Conecta/desconecta a Kopete automáticamente dependendo da disponibilidade de conexión a Internet
+Comment[he]=חיבור\ניתוק Kopete ב×ופן ×וטומטי בתלות בזמינות של החיבור ל×ינטרנט
+Comment[hi]=इंटरनेट कनेकà¥à¤¶à¤¨ की उपलबà¥à¤§à¤¤à¤¾ की निरà¥à¤­à¤°à¤¤à¤¾ के आधार पर के-ऑपà¥à¤Ÿà¥€ को सà¥à¤µà¤šà¤²à¤¿à¤¤ कनेकà¥à¤Ÿ/डिसà¥à¤•à¤¨à¥‡à¤•à¥à¤Ÿ करता है
+Comment[hu]=A Kopete automatikus csatlakoztatása és bontása az internetkapcsolat elérhetőségétől függően
+Comment[is]=(Af)tengir Kopete sjálfkrafa miðað við stöðu Internettengingar
+Comment[it]=Connetti/disconnetti automaticamente Kopete a seconda della disponibilità della connessione ad internet
+Comment[ja]=インターãƒãƒƒãƒˆæŽ¥ç¶šã®æœ‰ç„¡ã«ã‚ˆã‚Šè‡ªå‹•çš„ã« Kopete を接続/切断ã—ã¾ã™
+Comment[ka]=ინტერნეტ კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ áƒáƒ áƒ¡áƒ”ბáƒáƒ‘ისáƒáƒ¡ áƒáƒ•áƒ¢áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ áƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბს Kopete-ს
+Comment[kk]=Интернетке қоÑылымның бар=жоғына қарай Kopete-Ñ‚Ñ– автоматты түрде қоÑады және ажыратады
+Comment[km]=ភ្ជាប់ ​ ​ផ្ដាច​ Kopeteដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·â€‹ážŠáŸ„យ​ផ្អែក​លើ​ភាព​មាន​នៃការណážâ€‹áž—្ជាប់​អ៊ីនធឺណិáž
+Comment[lt]=Automatiškai prijungia/atjungia Kopete, priklausomai nuo interneto ryšio buvimo
+Comment[mk]=ÐвтоматÑки го поврзува/иÑклучува Kopete во завиÑноÑÑ‚ на доÑтапноÑта на поврзувањето на Интернет
+Comment[nb]=Kobler Kopete automatisk til/fra avhengig av om internettforbindelse er tilgjengelig
+Comment[nds]=Koppelt Kopete automaatsch to- oder af, afhangen vun de Verföögborkeit vun de Internetverbinnen
+Comment[ne]=इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ जडानको उपलबà¥à¤§à¤¤à¤¾à¤®à¤¾ आधारित कोपेट सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ रूपमा जडान गरà¥à¤¦à¤›/विचà¥à¤›à¥‡à¤¦à¤¨ गरà¥à¤¦à¤›
+Comment[nl]=Bouwt automatisch verbindingen op voor Kopete of verbreekt ze, afhankelijk van de beschikbaarheid van de internetverbinding
+Comment[nn]=Koplar Kopete automatisk til eller frå avhengig av om Internett-sambandet er tilgjengeleg.
+Comment[pl]=Automatycznie podłącza/odłącza Kopete zależnie od stanu połączenia z Internetem
+Comment[pt]=Liga/desliga o Kopete automaticamente, dependendo da disponibilidade de uma ligação à Internet
+Comment[pt_BR]=Conecta/desconecta o Kopete automaticamente dependendo da disponibilidade da conexão com a Internet
+Comment[ru]=ÐвтоматичеÑки входит в Ñеть или выходит из неё в завиÑимоÑти от Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚
+Comment[sk]=Pripojí/odpojí Kopete v závislosti Äi existuje pripojenie na Internet
+Comment[sl]=Samodejno vzpostavi/prekine povezavo Kopete glede na dostopnost internetne povezave
+Comment[sr]=ÐутоматÑки уÑпоÑтавља или прекида везу у Kopete-у у завиÑноÑти од доÑтупноÑти интернет везе
+Comment[sr@Latn]=Automatski uspostavlja ili prekida vezu u Kopete-u u zavisnosti od dostupnosti internet veze
+Comment[sv]=Ansluter eller kopplar ner Kopete automatiskt beroende på Internetförbindelsens tillgänglighet
+Comment[ta]=இணைய இணைபà¯à®ªà¯ˆ பொரà¯à®¤à¯à®¤à¯ Kopete யà¯à®Ÿà®©à¯à®¤à®¾à®©à®¾à®• இணையà¯à®®à¯/நீகà¯à®•à¯à®®à¯
+Comment[tg]=ВобаÑта ба имкониÑтҳои алоқаи Интернет Kopete-ро ба таври худкор пайваÑÑ‚/канда мекунад
+Comment[tr]=İnternet bağlantısının kullanılabilir olabilirliğine göre Kopete'yi otomatik bağlar/bağlamaz
+Comment[uk]=Ðвтоматично входить в мережу чи виходить з неї в залежноÑÑ‚Ñ– від наÑвноÑÑ‚Ñ– з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Інтернет
+Comment[zh_CN]=æ ¹æ® Internet 连接是å¦å¯ç”¨è‡ªåŠ¨è¿žæŽ¥/æ–­å¼€ Kopete
+Comment[zh_HK]=自動根據互è¯ç¶²é€£æŽ¥æƒ…æ³é€£æŽ¥æˆ–中斷 Kopete 連線
+Comment[zh_TW]=自動ä¾æ“šç¶²è·¯ç‹€æ³ä¾†é€£ç·š/中斷連線 Kopete
diff --git a/kopete/plugins/contactnotes/Makefile.am b/kopete/plugins/contactnotes/Makefile.am
new file mode 100644
index 00000000..94ff86ce
--- /dev/null
+++ b/kopete/plugins/contactnotes/Makefile.am
@@ -0,0 +1,15 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_contactnotes.la
+
+kopete_contactnotes_la_SOURCES = contactnotesplugin.cpp contactnotesedit.cpp
+kopete_contactnotes_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_contactnotes_la_LIBADD = ../../libkopete/libkopete.la -lkio
+
+service_DATA = kopete_contactnotes.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_contactnotes
+mydata_DATA = contactnotesui.rc
diff --git a/kopete/plugins/contactnotes/contactnotesedit.cpp b/kopete/plugins/contactnotes/contactnotesedit.cpp
new file mode 100644
index 00000000..f7333c7b
--- /dev/null
+++ b/kopete/plugins/contactnotes/contactnotesedit.cpp
@@ -0,0 +1,55 @@
+/***************************************************************************
+ contactnotesedit.cpp - description
+ -------------------
+ begin : lun sep 16 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qlabel.h>
+#include <qtextedit.h>
+#include <qvbox.h>
+
+#include <klocale.h>
+
+#include "kopetemetacontact.h"
+
+#include "contactnotesplugin.h"
+#include "contactnotesedit.h"
+
+ContactNotesEdit::ContactNotesEdit(Kopete::MetaContact *m,ContactNotesPlugin *p,const char *name) : KDialogBase(0L, name , false, i18n("Contact Notes") , KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok)
+{
+ m_plugin=p;
+ m_metaContact=m;
+
+ QVBox *w=new QVBox(this);
+ w->setSpacing(KDialog::spacingHint());
+ m_label = new QLabel(i18n("Notes about %1:").arg(m->displayName()) , w , "m_label");
+ m_linesEdit= new QTextEdit ( w , "m_linesEdit");
+
+ m_linesEdit->setText(p->notes(m));
+
+ enableButtonSeparator(true);
+ setMainWidget(w);
+}
+
+ContactNotesEdit::~ContactNotesEdit()
+{
+}
+
+void ContactNotesEdit::slotOk()
+{
+ emit notesChanged(m_linesEdit->text(),m_metaContact) ;
+ KDialogBase::slotOk();
+}
+
+#include "contactnotesedit.moc"
diff --git a/kopete/plugins/contactnotes/contactnotesedit.h b/kopete/plugins/contactnotes/contactnotesedit.h
new file mode 100644
index 00000000..20fe7d10
--- /dev/null
+++ b/kopete/plugins/contactnotes/contactnotesedit.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ contactnotesedit.h - description
+ -------------------
+ begin : lun sep 16 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CONTACTNOTESEDIT_H
+#define CONTACTNOTESEDIT_H
+
+#include <qwidget.h>
+#include <qstring.h>
+#include <kdialogbase.h>
+
+class QLabel;
+class QTextEdit;
+namespace Kopete { class MetaContact; }
+class ContactNotesPlugin;
+
+/**
+ *@author Olivier Goffart
+ */
+
+class ContactNotesEdit : public KDialogBase {
+ Q_OBJECT
+public:
+ ContactNotesEdit(Kopete::MetaContact *m,ContactNotesPlugin *p=0 ,const char *name=0);
+ ~ContactNotesEdit();
+
+private:
+ ContactNotesPlugin *m_plugin;
+ Kopete::MetaContact *m_metaContact;
+
+ QLabel *m_label;
+ QTextEdit *m_linesEdit;
+
+protected slots: // Protected slots
+ virtual void slotOk();
+signals: // Signals
+ void notesChanged(const QString, Kopete::MetaContact*);
+};
+
+#endif
diff --git a/kopete/plugins/contactnotes/contactnotesplugin.cpp b/kopete/plugins/contactnotes/contactnotesplugin.cpp
new file mode 100644
index 00000000..5982200f
--- /dev/null
+++ b/kopete/plugins/contactnotes/contactnotesplugin.cpp
@@ -0,0 +1,83 @@
+/***************************************************************************
+ contactnotes.cpp - description
+ -------------------
+ begin : lun sep 16 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+
+#include "contactnotesedit.h"
+
+#include "contactnotesplugin.h"
+
+typedef KGenericFactory<ContactNotesPlugin> ContactNotesPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_contactnotes, ContactNotesPluginFactory( "kopete_contactnotes" ) )
+
+ContactNotesPlugin::ContactNotesPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Plugin( ContactNotesPluginFactory::instance(), parent, name )
+{
+ if ( pluginStatic_ )
+ kdDebug(14302)<<"ContactNotesPlugin::ContactNotesPlugin : plugin already initialized"<<endl;
+ else
+ pluginStatic_ = this;
+
+ KAction *m_actionEdit=new KAction( i18n("&Notes"), "identity", 0, this, SLOT (slotEditInfo()), actionCollection() , "editContactNotes");
+ connect ( Kopete::ContactList::self() , SIGNAL( metaContactSelected(bool)) , m_actionEdit , SLOT(setEnabled(bool)));
+ m_actionEdit->setEnabled(Kopete::ContactList::self()->selectedMetaContacts().count()==1 );
+
+ setXMLFile("contactnotesui.rc");
+}
+
+ContactNotesPlugin::~ContactNotesPlugin()
+{
+ pluginStatic_ = 0L;
+}
+
+ContactNotesPlugin* ContactNotesPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+ContactNotesPlugin* ContactNotesPlugin::pluginStatic_ = 0L;
+
+
+void ContactNotesPlugin::slotEditInfo()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if(!m)
+ return;
+ ContactNotesEdit *e=new ContactNotesEdit(m,this);
+ connect( e, SIGNAL( notesChanged( const QString, Kopete::MetaContact*) ),this,
+ SLOT( setNotes( const QString, Kopete::MetaContact * ) ) );
+ e->show();
+}
+
+
+QString ContactNotesPlugin::notes(Kopete::MetaContact *m)
+{
+ return m->pluginData( this, "notes" );
+}
+
+void ContactNotesPlugin::setNotes( const QString n, Kopete::MetaContact *m )
+{
+ m->setPluginData( this, "notes", n );
+}
+
+#include "contactnotesplugin.moc"
+
diff --git a/kopete/plugins/contactnotes/contactnotesplugin.h b/kopete/plugins/contactnotes/contactnotesplugin.h
new file mode 100644
index 00000000..c4fb8224
--- /dev/null
+++ b/kopete/plugins/contactnotes/contactnotesplugin.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ contactnotesplugin.h - description
+ -------------------
+ begin : lun sep 16 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef BABELFISHPLUGIN_H
+#define BABELFISHPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+
+#include "kopeteplugin.h"
+
+class QString;
+class KAction;
+class KActionCollection;
+
+namespace Kopete { class MetaContact; }
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ *
+ * Kopete Contact Notes Plugin
+ *
+ */
+
+class ContactNotesPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ static ContactNotesPlugin *plugin();
+
+ ContactNotesPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~ContactNotesPlugin();
+
+ QString notes(Kopete::MetaContact *m);
+
+
+public slots:
+ void setNotes(const QString n, Kopete::MetaContact *m);
+
+private:
+ static ContactNotesPlugin* pluginStatic_;
+
+private slots: // Private slots
+ /** No descriptions */
+ void slotEditInfo();
+};
+
+#endif
+
+
diff --git a/kopete/plugins/contactnotes/contactnotesui.rc b/kopete/plugins/contactnotes/contactnotesui.rc
new file mode 100644
index 00000000..9e7a5748
--- /dev/null
+++ b/kopete/plugins/contactnotes/contactnotesui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_contactnotes" version="1">
+ <MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="editContactNotes" />
+ </Menu>
+ </MenuBar>
+ <Menu name="contact_popup">
+ <Action name="editContactNotes" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/contactnotes/kopete_contactnotes.desktop b/kopete/plugins/contactnotes/kopete_contactnotes.desktop
new file mode 100644
index 00000000..455f299d
--- /dev/null
+++ b/kopete/plugins/contactnotes/kopete_contactnotes.desktop
@@ -0,0 +1,124 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=identity
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_contactnotes
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_contactnotes
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Contact Notes
+Name[ar]=ملاحظات الاتصال
+Name[be]=Ðататкі
+Name[bg]=Бележки за контактите
+Name[bn]=যোগাযোগ নোট
+Name[br]=Notennoù an darempred
+Name[bs]=Kontakt informacije
+Name[ca]=Notes del contacte
+Name[cs]=Poznámky ke kontaktu
+Name[cy]=Nodiadau Cysylltiad
+Name[da]=Kontaktnoter
+Name[de]=Kontakt-Notizen
+Name[el]=Σημειώσεις επαφής
+Name[eo]=Kontaktinformoj
+Name[es]=Notas de contacto
+Name[et]=Kontakti märkmed
+Name[eu]=Kontaktuaren oharrak
+Name[fa]=یادداشتهای تماس
+Name[fi]=Yhteystiedot
+Name[fr]=Notes sur les contacts
+Name[gl]=Notas de contacto
+Name[he]=הערות על ×יש-קשר
+Name[hi]=समà¥à¤ªà¤°à¥à¤• नोटà¥à¤¸
+Name[hr]=Zabilješke kontakata
+Name[hu]=Megjegyzés a partnerekhez
+Name[is]=Skráð um notanda
+Name[it]=Note contatto
+Name[ja]=コンタクトメモ
+Name[ka]=მეგáƒáƒ‘რის შენიშვნები
+Name[kk]=Контакт еÑкертпелері
+Name[km]=​ចំណាំ​ក្នុង​ទំនាក់​ទំនង
+Name[lt]=Kontaktų pastabos
+Name[mk]=Забелешки за контакт
+Name[nb]=Notater om kontakter
+Name[nds]=Kontakt-Notizen
+Name[ne]=समà¥à¤ªà¤°à¥à¤• दà¥à¤°à¤·à¥à¤Ÿà¤¬à¥à¤¯
+Name[nl]=Notities bij contacten
+Name[nn]=Notat om kontaktar
+Name[pa]=ਸੰਪਰਕ ਨੋਟਿਸ
+Name[pl]=Notatki kontaktu
+Name[pt]=Notas do Contacto
+Name[pt_BR]=Notas do Contato
+Name[ru]=ÐŸÑ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ðº контакту
+Name[sk]=Poznámky kontaktu
+Name[sl]=Zapiski za stike
+Name[sr]=Белешке о контакту
+Name[sr@Latn]=Beleške o kontaktu
+Name[sv]=Kontaktanteckningar
+Name[ta]=தொடரà¯à®ªà¯ கà¯à®±à®¿à®ªà¯à®ªà¯
+Name[tg]=Эзоҳи ПайваÑтшавӣ
+Name[tr]=Bağlantı Notları
+Name[uk]=Примітки до контакту
+Name[zh_CN]=è”系人备忘
+Name[zh_HK]=è¯çµ¡äººå‚™è¨»
+Name[zh_TW]=è¯çµ¡äººå‚™å¿˜éŒ„
+Comment=Add personal notes on your contacts
+Comment[ar]=أض٠ملاحظاتك الشخصية إلى بيانات اﻹتصال
+Comment[be]=Дадаць Ð¿Ñ€Ñ‹Ð²Ð°Ñ‚Ð½Ñ‹Ñ Ð½Ð°Ñ‚Ð°Ñ‚ÐºÑ– Ð´Ð»Ñ Ð»ÑŽÐ´Ð·ÐµÐ¹ у ÑпіÑе
+Comment[bg]=ДобавÑне и редактиране на бележки и коментари към контактите
+Comment[bn]=আপনার যোগাযোগে বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত নোট যোগ করà§à¦¨
+Comment[bs]=Dodajte liÄne biljeÅ¡ke vaÅ¡im kontaktima
+Comment[ca]=Afegeix notes personals als vostres contactes
+Comment[cs]=Osobní poznámky ke kontaktům
+Comment[cy]=Ychwanegu nodiadau personol ynglyn â'ch cysylltiadau
+Comment[da]=Tilføj personlige noter om dine kontakter
+Comment[de]=Ermöglicht das Hinzufügen persönlicher Notizen zu Kontakten
+Comment[el]=ΠÏοσθέστε Ï€Ïοσωπικές σημειώσεις στις επαφές σας
+Comment[eo]=Aldoni personajn notojn al viaj kontaktoj
+Comment[es]=Añade notas personales a tus contactos
+Comment[et]=Isiklike märkmete lisamine kontaktide kohta
+Comment[eu]=Gehitu ohar pertsonalak zure kontaktuei
+Comment[fa]=اÙزودن پیامهای شخصی به تماسهای شما
+Comment[fi]=Lisää omia muistiinpanoja yhteystietoihin
+Comment[fr]=Ajoutez des notes personnelles à vos contacts
+Comment[gl]=Engadir notas persoáis ós teus contactos
+Comment[he]=הוספת הערות ×ישיות לגבי ×נשי-הקשר שלך
+Comment[hi]=आपके समà¥à¤ªà¤°à¥à¤•à¥‹à¤‚ पर निजी टीप जोड़े
+Comment[hr]=Dodaje osobne zabilješke o vašim kontaktima
+Comment[hu]=Személyes megjegyzések fűzése a partnerek adataihoz
+Comment[is]=Bæta við upplýsingum um notanda
+Comment[it]=Aggiungi note personali ai tuoi contatti
+Comment[ja]=コンタクトã«å€‹äººçš„ãªãƒ¡ãƒ¢ã‚’加ãˆã¾ã™
+Comment[ka]=მეგáƒáƒ‘რებისთვის პერსáƒáƒœáƒáƒšáƒ£áƒ áƒ˜ შენიშვნების დáƒáƒ›áƒáƒ¢áƒ”ბáƒ
+Comment[kk]=Контакттарға жеке еÑкертпелерді қоÑу
+Comment[km]=បន្ážáŸ‚ម​ចំណាំ​ផ្ទាល់​ážáŸ’លួន​លើ​ទំនាក់​ទំនង​របស់​អ្នក
+Comment[lt]=Papildykite kontaktus asmeniniais užrašais
+Comment[mk]=Додадете лични забелешки на вашите контакти
+Comment[nb]=Legg til egne notater i kontaktlista
+Comment[nds]=Dien Kontaken persöönliche Notizen tofögen
+Comment[ne]=तपाईà¤à¤•à¥‹ समà¥à¤ªà¤°à¥à¤•à¤®à¤¾ वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त दà¥à¤°à¤·à¥à¤Ÿà¤¬à¥à¤¯ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Voeg persoonlijke notities toe aan contacten
+Comment[nn]=Legg til eigne notat i kontaktlista
+Comment[pl]=Dodaje osobiste notatki do kontaktu
+Comment[pt]=Adiciona notas pessoais aos seus contactos
+Comment[pt_BR]=Adiciona notas pessoais em seus contatos
+Comment[ru]=Добавить личные Ð¿Ñ€Ð¸Ð¼ÐµÑ‡Ð°Ð½Ð¸Ñ Ðº вашим контактам
+Comment[sk]=Pridá osobné poznámky ku kontaktom
+Comment[sl]=Dodajte osebne zapiske k vašim stikom
+Comment[sr]=Додаје личне забелешке о вашим контактима
+Comment[sr@Latn]=Dodaje liÄne zabeleÅ¡ke o vaÅ¡im kontaktima
+Comment[sv]=Lägg till personliga anteckningar om kontakter
+Comment[ta]=உஙà¯à®•à®³à¯ உரையில௠சிறபà¯à®ªà¯ விளைவà¯à®•à®³à¯ˆ சேரà¯à®•à¯à®•à¯à®®à¯
+Comment[tg]=Ба пайваÑтагии худ Ñзоҳҳои шахÑиро илова кунед
+Comment[tr]=Bağlantıların üzerine kişisel notlar ekle
+Comment[uk]=Додати оÑобиÑÑ‚Ñ– примітки до влаÑних контактів
+Comment[wa]=Radjouter des notes da vosse å calpin d' adresses
+Comment[zh_CN]=在您的è”系人上添加ç§äººå¤‡å¿˜
+Comment[zh_HK]=為您的è¯çµ¡äººæ–°å¢žå€‹äººå‚™è¨»
+Comment[zh_TW]=新增è¯çµ¡äººçš„備忘錄
diff --git a/kopete/plugins/cryptography/Makefile.am b/kopete/plugins/cryptography/Makefile.am
new file mode 100644
index 00000000..bbcaca2c
--- /dev/null
+++ b/kopete/plugins/cryptography/Makefile.am
@@ -0,0 +1,25 @@
+METASOURCES = AUTO
+
+SUBDIRS=icons
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_cryptography.la kcm_kopete_cryptography.la
+
+kopete_cryptography_la_SOURCES = cryptographyplugin.cpp kgpginterface.cpp cryptographyguiclient.cpp cryptographyselectuserkey.cpp cryptographyuserkey_ui.ui popuppublic.cpp kgpgselkey.cpp
+kopete_cryptography_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_cryptography_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_cryptography_la_SOURCES = cryptographypreferences.cpp cryptographyprefsbase.ui kgpgselkey.cpp
+kcm_kopete_cryptography_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_cryptography_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_cryptography.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_cryptography_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+mydatadir = $(kde_datadir)/kopete_cryptography
+mydata_DATA = cryptographyui.rc cryptographychatui.rc
+
diff --git a/kopete/plugins/cryptography/cryptographychatui.rc b/kopete/plugins/cryptography/cryptographychatui.rc
new file mode 100644
index 00000000..3d8835a6
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographychatui.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopetecryptographychat">
+ <MenuBar>
+ <Menu name="tools" >
+ <text>&amp;Tools</text>
+ <Action name="cryptographyToggle" />
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/kopete/plugins/cryptography/cryptographyguiclient.cpp b/kopete/plugins/cryptography/cryptographyguiclient.cpp
new file mode 100644
index 00000000..0c53eee0
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyguiclient.cpp
@@ -0,0 +1,75 @@
+/*
+ cryptographyguiclient.cpp
+
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "cryptographyguiclient.h"
+#include "cryptographyplugin.h"
+
+
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopetechatsession.h"
+
+#include <kaction.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+class CryptographyPlugin;
+
+CryptographyGUIClient::CryptographyGUIClient(Kopete::ChatSession *parent )
+ : QObject(parent) , KXMLGUIClient(parent)
+{
+ if(!parent || parent->members().isEmpty())
+ {
+ deleteLater(); //we refuse to build this client, it is based on wrong parametters
+ return;
+ }
+
+ QPtrList<Kopete::Contact> mb=parent->members();
+ Kopete::MetaContact *first=mb.first()->metaContact();
+
+ if(!first)
+ {
+ deleteLater(); //we refuse to build this client, it is based on wrong parametters
+ return;
+ }
+
+ setInstance( KGenericFactory<CryptographyPlugin>::instance() );
+
+
+ m_action=new KToggleAction( i18n("Encrypt Messages" ), QString::fromLatin1( "encrypted" ), 0, this, SLOT(slotToggled()), actionCollection() , "cryptographyToggle" );
+ m_action->setChecked( first->pluginData( CryptographyPlugin::plugin() , "encrypt_messages") != QString::fromLatin1("off") ) ;
+
+ setXMLFile("cryptographychatui.rc");
+}
+
+
+CryptographyGUIClient::~CryptographyGUIClient()
+{}
+
+void CryptographyGUIClient::slotToggled()
+{
+ QPtrList<Kopete::Contact> mb=static_cast<Kopete::ChatSession*>(parent())->members();
+ Kopete::MetaContact *first=mb.first()->metaContact();
+
+ if(!first)
+ return;
+
+ first->setPluginData(CryptographyPlugin::plugin() , "encrypt_messages" ,
+ m_action->isChecked() ? "on" : "off" );
+}
+
+
+#include "cryptographyguiclient.moc"
+
diff --git a/kopete/plugins/cryptography/cryptographyguiclient.h b/kopete/plugins/cryptography/cryptographyguiclient.h
new file mode 100644
index 00000000..5a1aee2c
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyguiclient.h
@@ -0,0 +1,41 @@
+/*
+ cryptographyguiclient.h
+
+ Copyright (c) 2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef CRYPTOGUICLIENT_H
+#define CRYPTOGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+namespace Kopete { class ChatSession; }
+class KToggleAction;
+
+/**
+ *@author Olivier Goffart
+ */
+class CryptographyGUIClient : public QObject, public KXMLGUIClient
+{
+Q_OBJECT
+public:
+ CryptographyGUIClient(Kopete::ChatSession *parent = 0);
+ ~CryptographyGUIClient();
+
+private:
+ KToggleAction *m_action;
+
+private slots:
+ void slotToggled();
+};
+
+#endif
diff --git a/kopete/plugins/cryptography/cryptographyplugin.cpp b/kopete/plugins/cryptography/cryptographyplugin.cpp
new file mode 100644
index 00000000..701ad8bd
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyplugin.cpp
@@ -0,0 +1,326 @@
+/***************************************************************************
+ cryptographyplugin.cpp - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002-2004 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qstylesheet.h>
+#include <qtimer.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <kconfig.h>
+#include <kgenericfactory.h>
+#include <kdeversion.h>
+#include <kaboutdata.h>
+
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetesimplemessagehandler.h"
+#include "kopeteuiglobal.h"
+#include "kopetecontact.h"
+
+#include "cryptographyplugin.h"
+#include "cryptographyselectuserkey.h"
+#include "cryptographyguiclient.h"
+
+#include "kgpginterface.h"
+
+//This regexp try to match an HTML text, but only some authorized tags.
+// used in slotIncomingMessage
+//There are not rules to know if the test should be sent in html or not.
+//In Jabber, the JEP says it's not. so we don't use richtext in our message, but some client did.
+//We limit the html to some basis tag to limit security problem (bad links)
+// - Olivier
+const QRegExp CryptographyPlugin::isHTML( QString::fromLatin1( "^[^<>]*(</?(html|body|br|p|font|center|b|i|u|span|div|pre)(>|[\\s/][^><]*>)[^><]*)+$" ) , false );
+
+typedef KGenericFactory<CryptographyPlugin> CryptographyPluginFactory;
+static const KAboutData aboutdata("kopete_cryptography", I18N_NOOP("Cryptography") , "1.0" );
+K_EXPORT_COMPONENT_FACTORY( kopete_cryptography, CryptographyPluginFactory( &aboutdata ) )
+
+CryptographyPlugin::CryptographyPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Plugin( CryptographyPluginFactory::instance(), parent, name ),
+ m_cachedPass()
+{
+ if( !pluginStatic_ )
+ pluginStatic_=this;
+
+ m_inboundHandler = new Kopete::SimpleMessageHandlerFactory( Kopete::Message::Inbound,
+ Kopete::MessageHandlerFactory::InStageToSent, this, SLOT( slotIncomingMessage( Kopete::Message& ) ) );
+ connect( Kopete::ChatSessionManager::self(),
+ SIGNAL( aboutToSend( Kopete::Message & ) ),
+ SLOT( slotOutgoingMessage( Kopete::Message & ) ) );
+
+ m_cachedPass_timer = new QTimer(this, "m_cachedPass_timer" );
+ QObject::connect(m_cachedPass_timer, SIGNAL(timeout()), this, SLOT(slotForgetCachedPass() ));
+
+
+ KAction *action=new KAction( i18n("&Select Cryptography Public Key..."), "encrypted", 0, this, SLOT (slotSelectContactKey()), actionCollection() , "contactSelectKey");
+ connect ( Kopete::ContactList::self() , SIGNAL( metaContactSelected(bool)) , action , SLOT(setEnabled(bool)));
+ action->setEnabled(Kopete::ContactList::self()->selectedMetaContacts().count()==1 );
+
+ setXMLFile("cryptographyui.rc");
+ loadSettings();
+ connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( chatSessionCreated( Kopete::ChatSession * )) , SLOT( slotNewKMM( Kopete::ChatSession * ) ) );
+ //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining)
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ {
+ slotNewKMM(*it);
+ }
+
+}
+
+CryptographyPlugin::~CryptographyPlugin()
+{
+ delete m_inboundHandler;
+ pluginStatic_ = 0L;
+}
+
+void CryptographyPlugin::loadSettings()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("Cryptography Plugin");
+
+ mPrivateKeyID = config->readEntry("PGP_private_key");
+ mAlsoMyKey = config->readBoolEntry("Also_my_key", false);
+
+ if(config->readBoolEntry("Cache_Till_App_Close", false))
+ mCachePassPhrase = Keep;
+ if(config->readBoolEntry("Cache_Till_Time", false))
+ mCachePassPhrase = Time;
+ if(config->readBoolEntry("Cache_Never", false))
+ mCachePassPhrase = Never;
+ mCacheTime = config->readNumEntry("Cache_Time", 15);
+ mAskPassPhrase = config->readBoolEntry("No_Passphrase_Handling", false);
+}
+
+CryptographyPlugin* CryptographyPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+CryptographyPlugin* CryptographyPlugin::pluginStatic_ = 0L;
+
+QCString CryptographyPlugin::cachedPass()
+{
+ return pluginStatic_->m_cachedPass;
+}
+
+void CryptographyPlugin::setCachedPass(const QCString& p)
+{
+ if(pluginStatic_->mCacheMode==Never)
+ return;
+ if(pluginStatic_->mCacheMode==Time)
+ pluginStatic_->m_cachedPass_timer->start(pluginStatic_->mCacheTime * 60000, false);
+
+ pluginStatic_->m_cachedPass=p;
+}
+
+bool CryptographyPlugin::passphraseHandling()
+{
+ return !pluginStatic_->mAskPassPhrase;
+}
+
+
+/*KActionCollection *CryptographyPlugin::customChatActions(Kopete::ChatSession *KMM)
+{
+ delete m_actionCollection;
+
+ m_actionCollection = new KActionCollection(this);
+ KAction *actionTranslate = new KAction( i18n ("Translate"), 0,
+ this, SLOT( slotTranslateChat() ), m_actionCollection, "actionTranslate" );
+ m_actionCollection->insert( actionTranslate );
+
+ m_currentChatSession=KMM;
+ return m_actionCollection;
+}*/
+
+void CryptographyPlugin::slotIncomingMessage( Kopete::Message& msg )
+{
+ QString body = msg.plainBody();
+ if( !body.startsWith( QString::fromLatin1("-----BEGIN PGP MESSAGE----") )
+ || !body.contains( QString::fromLatin1("-----END PGP MESSAGE----") ) )
+ return;
+
+ if( msg.direction() != Kopete::Message::Inbound )
+ {
+ QString plainBody;
+ if ( m_cachedMessages.contains( body ) )
+ {
+ plainBody = m_cachedMessages[ body ];
+ m_cachedMessages.remove( body );
+ }
+ else
+ {
+ plainBody = KgpgInterface::KgpgDecryptText( body, mPrivateKeyID );
+ }
+
+ if( !plainBody.isEmpty() )
+ {
+ //Check if this is a RTF message before escaping it
+ if( !isHTML.exactMatch( plainBody ) )
+ {
+ plainBody = QStyleSheet::escape( plainBody );
+
+ //this is the same algoritm as in Kopete::Message::escapedBody();
+ plainBody.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "<br/>" ) )
+ .replace( QString::fromLatin1( "\t" ), QString::fromLatin1( "&nbsp;&nbsp;&nbsp;&nbsp;" ) )
+ .replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( "&nbsp; " ) );
+ }
+
+ msg.setBody( QString::fromLatin1("<table width=\"100%\" border=0 cellspacing=0 cellpadding=0><tr><td class=\"highlight\"><font size=\"-1\"><b>")
+ + i18n("Outgoing Encrypted Message: ")
+ + QString::fromLatin1("</b></font></td></tr><tr><td class=\"highlight\">")
+ + plainBody
+ + QString::fromLatin1(" </td></tr></table>")
+ , Kopete::Message::RichText );
+ }
+
+ //if there are too messages in cache, clear the cache
+ if(m_cachedMessages.count() > 5)
+ m_cachedMessages.clear();
+
+ return;
+ }
+
+
+ //the Message::unescape is there because client like fire replace linebreak by <BR> to work even if the protocol doesn't allow newlines (IRC)
+ // cf http://fire.sourceforge.net/forums/viewtopic.php?t=174 and Bug #96052
+ if(body.contains("<"))
+ body= Kopete::Message::unescape(body);
+
+ body = KgpgInterface::KgpgDecryptText( body, mPrivateKeyID );
+
+ if( !body.isEmpty() )
+ {
+ //Check if this is a RTF message before escaping it
+ if( !isHTML.exactMatch( body ) )
+ {
+ body = Kopete::Message::escape( body );
+ }
+
+ msg.setBody( QString::fromLatin1("<table width=\"100%\" border=0 cellspacing=0 cellpadding=0><tr><td class=\"highlight\"><font size=\"-1\"><b>")
+ + i18n("Incoming Encrypted Message: ")
+ + QString::fromLatin1("</b></font></td></tr><tr><td class=\"highlight\">")
+ + body
+ + QString::fromLatin1(" </td></tr></table>")
+ , Kopete::Message::RichText );
+ }
+
+}
+
+void CryptographyPlugin::slotOutgoingMessage( Kopete::Message& msg )
+{
+ if(msg.direction() != Kopete::Message::Outbound)
+ return;
+
+ QStringList keys;
+ QPtrList<Kopete::Contact> contactlist = msg.to();
+ for( Kopete::Contact *c = contactlist.first(); c; c = contactlist.next() )
+ {
+ QString tmpKey;
+ if( c->metaContact() )
+ {
+ if(c->metaContact()->pluginData( this, "encrypt_messages" ) == "off" )
+ return;
+ tmpKey = c->metaContact()->pluginData( this, "gpgKey" );
+ }
+ if( tmpKey.isEmpty() )
+ {
+ // kdDebug( 14303 ) << "CryptographyPlugin::slotOutgoingMessage: no key selected for one contact" <<endl;
+ return;
+ }
+ keys.append( tmpKey );
+ }
+ if(mAlsoMyKey) //encrypt also with the self key
+ keys.append( mPrivateKeyID );
+
+ QString key = keys.join( " " );
+
+ if(key.isEmpty())
+ {
+ kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty key" <<endl;
+ return;
+ }
+
+ QString original=msg.plainBody();
+
+ /* Code From KGPG */
+
+ ////////////////// encode from editor
+ QString encryptOptions="";
+
+ //if (utrust==true)
+ encryptOptions+=" --always-trust ";
+ //if (arm==true)
+ encryptOptions+=" --armor ";
+
+ /* if (pubcryptography==true)
+ {
+ if (gpgversion<120) encryptOptions+=" --compress-algo 1 --cipher-algo cast5 ";
+ else encryptOptions+=" --cryptography6 ";
+ }*/
+
+// if (selec==NULL) {KMessageBox::sorry(Kopete::UI::Global::mainWidget(),i18n("You have not chosen an encryption key..."));return;}
+
+ QString resultat=KgpgInterface::KgpgEncryptText(original,key,encryptOptions);
+ if (!resultat.isEmpty())
+ {
+ msg.setBody(resultat,Kopete::Message::PlainText);
+ m_cachedMessages.insert(resultat,original);
+ }
+ else
+ kdDebug(14303) << "CryptographyPlugin::slotOutgoingMessage: empty result" <<endl;
+
+}
+
+void CryptographyPlugin::slotSelectContactKey()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if(!m)
+ return;
+ QString key = m->pluginData( this, "gpgKey" );
+ CryptographySelectUserKey *opts = new CryptographySelectUserKey( key, m );
+ opts->exec();
+ if( opts->result() )
+ {
+ key = opts->publicKey();
+ m->setPluginData( this, "gpgKey", key );
+ }
+ delete opts;
+}
+
+void CryptographyPlugin::slotForgetCachedPass()
+{
+ m_cachedPass=QCString();
+ m_cachedPass_timer->stop();
+}
+
+void CryptographyPlugin::slotNewKMM(Kopete::ChatSession *KMM)
+{
+ connect(this , SIGNAL( destroyed(QObject*)) ,
+ new CryptographyGUIClient(KMM) , SLOT(deleteLater()));
+}
+
+
+
+#include "cryptographyplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/cryptography/cryptographyplugin.h b/kopete/plugins/cryptography/cryptographyplugin.h
new file mode 100644
index 00000000..506617cc
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyplugin.h
@@ -0,0 +1,102 @@
+/***************************************************************************
+ cryptographyplugin.h - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002-2004 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CryptographyPLUGIN_H
+#define CryptographyPLUGIN_H
+
+
+#include "kopeteplugin.h"
+
+class QStringList;
+class QString;
+class QTimer;
+
+namespace Kopete
+{
+ class Message;
+ class MetaContact;
+ class ChatSession;
+ class SimpleMessageHandlerFactory;
+}
+
+/**
+ * @author Olivier Goffart
+ */
+
+class CryptographyPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ enum CacheMode
+ {
+ Keep = 0,
+ Time = 1,
+ Never = 2
+ };
+
+ static CryptographyPlugin *plugin();
+ static QCString cachedPass();
+ static void setCachedPass(const QCString &pass);
+ static bool passphraseHandling();
+ static const QRegExp isHTML;
+
+ CryptographyPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~CryptographyPlugin();
+
+public slots:
+
+ void slotIncomingMessage( Kopete::Message& msg );
+ void slotOutgoingMessage( Kopete::Message& msg );
+
+private slots:
+
+ void slotSelectContactKey();
+ void slotForgetCachedPass();
+ void loadSettings();
+
+ void slotNewKMM(Kopete::ChatSession *);
+
+private:
+ static CryptographyPlugin* pluginStatic_;
+ Kopete::SimpleMessageHandlerFactory *m_inboundHandler;
+ QCString m_cachedPass;
+ QTimer *m_cachedPass_timer;
+
+ //cache messages for showing
+ QMap<QString, QString> m_cachedMessages;
+
+ //Settings
+ QString mPrivateKeyID;
+ int mCacheMode;
+ unsigned int mCacheTime;
+ bool mAlsoMyKey;
+ bool mAskPassPhrase;
+ bool mCachePassPhrase;
+};
+
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/cryptography/cryptographypreferences.cpp b/kopete/plugins/cryptography/cryptographypreferences.cpp
new file mode 100644
index 00000000..1039aac8
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographypreferences.cpp
@@ -0,0 +1,49 @@
+/***************************************************************************
+ cryptographypreferences.cpp - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qpushbutton.h>
+
+#include <klineedit.h>
+#include <kgenericfactory.h>
+
+#include "cryptographyprefsbase.h"
+#include "cryptographypreferences.h"
+#include "kgpgselkey.h"
+
+typedef KGenericFactory<CryptographyPreferences> CryptographyPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_cryptography, CryptographyPreferencesFactory("kcm_kopete_cryptography"))
+
+CryptographyPreferences::CryptographyPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCAutoConfigModule(CryptographyPreferencesFactory::instance(), parent, args)
+{
+ // Add actuall widget generated from ui file.
+ preferencesDialog = new CryptographyPrefsUI(this);
+ connect (preferencesDialog->m_selectOwnKey , SIGNAL(pressed()) , this , SLOT(slotSelectPressed()));
+ setMainWidget( preferencesDialog ,"Cryptography Plugin");
+}
+
+void CryptographyPreferences::slotSelectPressed()
+{
+ KgpgSelKey opts(this,0,false);
+ opts.exec();
+ if (opts.result()==QDialog::Accepted)
+ preferencesDialog->PGP_private_key->setText(opts.getkeyID());
+}
+
+#include "cryptographypreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/cryptography/cryptographypreferences.h b/kopete/plugins/cryptography/cryptographypreferences.h
new file mode 100644
index 00000000..057eacf1
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographypreferences.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ cryptographypreferences.h
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CryptographyPREFERENCES_H
+#define CryptographyPREFERENCES_H
+
+#include "kcautoconfigmodule.h"
+
+class CryptographyPrefsUI;
+class KAutoConfig;
+
+/**
+ * Preference widget for the Cryptography plugin
+ * @author Olivier Goffart
+ */
+class CryptographyPreferences : public KCAutoConfigModule {
+ Q_OBJECT
+public:
+ CryptographyPreferences(QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList());
+private:
+ CryptographyPrefsUI *preferencesDialog;
+private slots: // Public slots
+ void slotSelectPressed();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/cryptography/cryptographyprefsbase.ui b/kopete/plugins/cryptography/cryptographyprefsbase.ui
new file mode 100644
index 00000000..ecec3507
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyprefsbase.ui
@@ -0,0 +1,196 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>CryptographyPrefsUI</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CryptographyPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>403</width>
+ <height>287</height>
+ </rect>
+ </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>Your private PGP key:</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="2">
+ <property name="name">
+ <cstring>m_selectOwnKey</cstring>
+ </property>
+ <property name="text">
+ <string>Select...</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>PGP_private_key</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Also_my_key</cstring>
+ </property>
+ <property name="text">
+ <string>Encrypt outgoing messages with this key</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Check this box if you want to encrypt outgoing messages with this key, so that you will be able to decrypt them yourself later.&lt;br&gt;
+&lt;b&gt;Warning:&lt;/b&gt; This can increase the size of messages, and some protocols will refuse to send your messages because they are too large.</string>
+ </property>
+ </widget>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup" row="4" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_cache</cstring>
+ </property>
+ <property name="title">
+ <string>Cache Passphrase</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Cache_Till_App_Close</cstring>
+ </property>
+ <property name="text">
+ <string>Until Kopete closes</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>Cache_Time</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>999</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>15</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>minutes</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>Cache_Till_Time</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>For</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>Cache_Never</cstring>
+ </property>
+ <property name="text">
+ <string>Never</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>No_Passphrase_Handling</cstring>
+ </property>
+ <property name="text">
+ <string>Do not ask for the passphrase</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>No_Passphrase_Handling</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_cache</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>PGP_private_key</tabstop>
+ <tabstop>m_selectOwnKey</tabstop>
+ <tabstop>Also_my_key</tabstop>
+ <tabstop>No_Passphrase_Handling</tabstop>
+ <tabstop>Cache_Till_App_Close</tabstop>
+ <tabstop>Cache_Till_Time</tabstop>
+ <tabstop>Cache_Time</tabstop>
+ <tabstop>Cache_Never</tabstop>
+</tabstops>
+<slots>
+ <slot>m_selectOwnKey_clicked()</slot>
+ <slot>m_selectOwnKey_toggled(bool)</slot>
+ <slot>m_selectOwnKey_stateChanged(int)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/cryptography/cryptographyselectuserkey.cpp b/kopete/plugins/cryptography/cryptographyselectuserkey.cpp
new file mode 100644
index 00000000..4f1cc35e
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyselectuserkey.cpp
@@ -0,0 +1,71 @@
+/***************************************************************************
+ cryptographyselectuserkey.cpp - description
+ -------------------
+ begin : dim nov 17 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <klineedit.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+
+#include "cryptographyuserkey_ui.h"
+#include "kopetemetacontact.h"
+#include "popuppublic.h"
+
+#include "cryptographyselectuserkey.h"
+
+CryptographySelectUserKey::CryptographySelectUserKey(const QString& key ,Kopete::MetaContact *mc) : KDialogBase( 0l, "CryptographySelectUserKey", /*modal = */true, i18n("Select Contact's Public Key") , KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok )
+{
+ m_metaContact=mc;
+ view = new CryptographyUserKey_ui(this,"CryptographyUserKey_ui");
+ setMainWidget(view);
+
+ connect (view->m_selectKey , SIGNAL(clicked()) , this , SLOT(slotSelectPressed()));
+ connect (view->m_removeButton , SIGNAL(clicked()) , this , SLOT(slotRemovePressed()));
+
+ view->m_titleLabel->setText(i18n("Select public key for %1").arg(mc->displayName()));
+ view->m_editKey->setText(key);
+}
+CryptographySelectUserKey::~CryptographySelectUserKey()
+{
+}
+
+void CryptographySelectUserKey::slotSelectPressed()
+{
+ popupPublic *dialog=new popupPublic(this, "public_keys", 0,false);
+ connect(dialog,SIGNAL(selectedKey(QString &,QString,bool,bool)),this,SLOT(keySelected(QString &)));
+ dialog->show();
+}
+
+
+void CryptographySelectUserKey::keySelected(QString &key)
+{
+ view->m_editKey->setText(key);
+}
+
+void CryptographySelectUserKey::slotRemovePressed()
+{
+ view->m_editKey->setText("");
+}
+
+QString CryptographySelectUserKey::publicKey() const
+{
+ return view->m_editKey->text();
+}
+
+
+
+#include "cryptographyselectuserkey.moc"
+
diff --git a/kopete/plugins/cryptography/cryptographyselectuserkey.h b/kopete/plugins/cryptography/cryptographyselectuserkey.h
new file mode 100644
index 00000000..1a8828cf
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyselectuserkey.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ cryptographyselectuserkey.h - description
+ -------------------
+ begin : dim nov 17 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CRYPTOGRAPHYSELECTUSERKEY_H
+#define CRYPTOGRAPHYSELECTUSERKEY_H
+
+#include <kdialogbase.h>
+
+namespace Kopete { class MetaContact; }
+class CryptographyUserKey_ui;
+
+/**
+ *@author OlivierGoffart
+ */
+
+class CryptographySelectUserKey : public KDialogBase {
+ Q_OBJECT
+public:
+ CryptographySelectUserKey(const QString &key, Kopete::MetaContact *mc);
+ ~CryptographySelectUserKey();
+
+
+ QString publicKey() const;
+
+private slots:
+ void keySelected(QString &);
+ void slotSelectPressed();
+ /** No descriptions */
+ void slotRemovePressed();
+
+private:
+ CryptographyUserKey_ui *view;
+ Kopete::MetaContact *m_metaContact;
+
+};
+
+#endif
diff --git a/kopete/plugins/cryptography/cryptographyui.rc b/kopete/plugins/cryptography/cryptographyui.rc
new file mode 100644
index 00000000..542cb597
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_cryptography" version="1">
+ <MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="contactSelectKey" />
+ </Menu>
+ </MenuBar>
+ <Menu name="contact_popup">
+ <Action name="contactSelectKey" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/cryptography/cryptographyuserkey_ui.ui b/kopete/plugins/cryptography/cryptographyuserkey_ui.ui
new file mode 100644
index 00000000..d84f2fec
--- /dev/null
+++ b/kopete/plugins/cryptography/cryptographyuserkey_ui.ui
@@ -0,0 +1,79 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>CryptographyUserKey_ui</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>CryptographyUserKey_ui</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>442</width>
+ <height>232</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>PGP key:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_editKey</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_selectKey</cstring>
+ </property>
+ <property name="text">
+ <string>Select...</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="1" column="3">
+ <property name="name">
+ <cstring>m_removeButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>m_titleLabel</cstring>
+ </property>
+ <property name="text">
+ <string>TextLabel2</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/plugins/cryptography/icons/Makefile.am b/kopete/plugins/cryptography/icons/Makefile.am
new file mode 100644
index 00000000..db2335c3
--- /dev/null
+++ b/kopete/plugins/cryptography/icons/Makefile.am
@@ -0,0 +1,2 @@
+kgpgiconsdir = $(kde_datadir)/kopete/icons
+kgpgicons_ICON = AUTO
diff --git a/kopete/plugins/cryptography/icons/cr16-action-kgpg_key1.png b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key1.png
new file mode 100644
index 00000000..ee3b21c7
--- /dev/null
+++ b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key1.png
Binary files differ
diff --git a/kopete/plugins/cryptography/icons/cr16-action-kgpg_key2.png b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key2.png
new file mode 100644
index 00000000..ad4e016f
--- /dev/null
+++ b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key2.png
Binary files differ
diff --git a/kopete/plugins/cryptography/icons/cr16-action-kgpg_key3.png b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key3.png
new file mode 100644
index 00000000..ae788cab
--- /dev/null
+++ b/kopete/plugins/cryptography/icons/cr16-action-kgpg_key3.png
Binary files differ
diff --git a/kopete/plugins/cryptography/kgpginterface.cpp b/kopete/plugins/cryptography/kgpginterface.cpp
new file mode 100644
index 00000000..51b35a63
--- /dev/null
+++ b/kopete/plugins/cryptography/kgpginterface.cpp
@@ -0,0 +1,176 @@
+#include "cryptographyplugin.h" //(for the cached passphrase)
+//Code from KGPG
+
+/***************************************************************************
+ kgpginterface.cpp - description
+ -------------------
+ begin : Mon Jul 8 2002
+ copyright : (C) 2002 by y0k0
+ email : bj@altern.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#include <klocale.h>
+#include <kpassdlg.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <qfile.h>
+
+#include <kprocio.h>
+
+//#include "kdetailedconsole.h"
+
+#include "kgpginterface.h"
+
+KgpgInterface::KgpgInterface()
+{}
+
+KgpgInterface::~KgpgInterface()
+{}
+
+QString KgpgInterface::KgpgEncryptText(QString text,QString userIDs, QString Options)
+{
+ FILE *fp;
+ QString dests,encResult;
+ char buffer[200];
+
+ userIDs=userIDs.stripWhiteSpace();
+ userIDs=userIDs.simplifyWhiteSpace();
+ Options=Options.stripWhiteSpace();
+
+ int ct=userIDs.find(" ");
+ while (ct!=-1) // if multiple keys...
+ {
+ dests+=" --recipient "+userIDs.section(' ',0,0);
+ userIDs.remove(0,ct+1);
+ ct=userIDs.find(" ");
+ }
+ dests+=" --recipient "+userIDs;
+
+ QCString gpgcmd = "echo -n ";
+ gpgcmd += KShellProcess::quote( text ).utf8();
+ gpgcmd += " | gpg --no-secmem-warning --no-tty ";
+ gpgcmd += Options.local8Bit();
+ gpgcmd += " -e ";
+ gpgcmd += dests.local8Bit();
+
+ ////////// encode with untrusted keys or armor if checked by user
+ fp = popen( gpgcmd, "r");
+ while ( fgets( buffer, sizeof(buffer), fp))
+ encResult+=buffer;
+ pclose(fp);
+
+ if( !encResult.isEmpty() )
+ return encResult;
+ else
+ return QString::null;
+}
+
+QString KgpgInterface::KgpgDecryptText(QString text,QString userID)
+{
+ FILE *fp,*pass;
+ QString encResult;
+
+ char buffer[200];
+ int counter=0,ppass[2];
+ QCString password = CryptographyPlugin::cachedPass();
+ bool passphraseHandling=CryptographyPlugin::passphraseHandling();
+
+ while ((counter<3) && (encResult.isEmpty()))
+ {
+ counter++;
+ if(passphraseHandling && password.isNull())
+ {
+ /// pipe for passphrase
+ //userID=QString::fromUtf8(userID);
+ userID.replace('<',"&lt;");
+ QString passdlg=i18n("Enter passphrase for <b>%1</b>:").arg(userID);
+ if (counter>1)
+ passdlg.prepend(i18n("<b>Bad passphrase</b><br> You have %1 tries left.<br>").arg(QString::number(4-counter)));
+
+ /// pipe for passphrase
+ int code=KPasswordDialog::getPassword(password,passdlg);
+ if (code!=QDialog::Accepted)
+ return QString::null;
+ CryptographyPlugin::setCachedPass(password);
+ }
+
+ if(passphraseHandling)
+ {
+ pipe(ppass);
+ pass = fdopen(ppass[1], "w");
+ fwrite(password, sizeof(char), strlen(password), pass);
+ // fwrite("\n", sizeof(char), 1, pass);
+ fclose(pass);
+ }
+
+ QCString gpgcmd="echo ";
+ gpgcmd += KShellProcess::quote(text).utf8();
+ gpgcmd += " | gpg --no-secmem-warning --no-tty ";
+ if(passphraseHandling)
+ gpgcmd += "--passphrase-fd " + QString::number(ppass[0]).local8Bit();
+ gpgcmd += " -d ";
+
+ ////////// encode with untrusted keys or armor if checked by user
+ fp = popen(gpgcmd, "r");
+ while ( fgets( buffer, sizeof(buffer), fp))
+ encResult += QString::fromUtf8(buffer);
+
+ pclose(fp);
+ password = QCString();
+ }
+
+ if( !encResult.isEmpty() )
+ return encResult;
+ else
+ return QString::null;
+}
+
+QString KgpgInterface::checkForUtf8(QString txt)
+{
+
+ // code borrowed from gpa
+ const char *s;
+
+ /* Make sure the encoding is UTF-8.
+ * Test structure suggested by Werner Koch */
+ if (txt.isEmpty())
+ return QString::null;
+
+ for (s = txt.ascii(); *s && !(*s & 0x80); s++)
+ ;
+ if (*s && !strchr (txt.ascii(), 0xc3) && (txt.find("\\x")==-1))
+ return txt;
+
+ /* The string is not in UTF-8 */
+ //if (strchr (txt.ascii(), 0xc3)) return (txt+" +++");
+ if (txt.find("\\x")==-1)
+ return QString::fromUtf8(txt.ascii());
+ // if (!strchr (txt.ascii(), 0xc3) || (txt.find("\\x")!=-1)) {
+ for ( int idx = 0 ; (idx = txt.find( "\\x", idx )) >= 0 ; ++idx ) {
+ char str[2] = "x";
+ str[0] = (char) QString( txt.mid( idx + 2, 2 ) ).toShort( 0, 16 );
+ txt.replace( idx, 4, str );
+ }
+ if (!strchr (txt.ascii(), 0xc3))
+ return QString::fromUtf8(txt.ascii());
+ else
+ return QString::fromUtf8(QString::fromUtf8(txt.ascii()).ascii()); // perform Utf8 twice, or some keys display badly
+}
+
+
+
+
+#include "kgpginterface.moc"
diff --git a/kopete/plugins/cryptography/kgpginterface.h b/kopete/plugins/cryptography/kgpginterface.h
new file mode 100644
index 00000000..b70bc68a
--- /dev/null
+++ b/kopete/plugins/cryptography/kgpginterface.h
@@ -0,0 +1,91 @@
+//Code from KGPG
+
+/***************************************************************************
+ kgpginterface.h - description
+ -------------------
+ begin : Sat Jun 29 2002
+ copyright : (C) 2002 by
+ email :
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KGPGINTERFACE_H
+#define KGPGINTERFACE_H
+
+
+#include <kurl.h>
+
+/**
+ * Encrypt a file using gpg.
+ */
+//class KgpgEncryptFile : public QObject {
+class KgpgInterface : public QObject {
+
+ Q_OBJECT
+
+ public:
+ /**
+ * Initialize the class
+ */
+ KgpgInterface();
+
+
+ /**Encrypt text function
+ * @param text QString text to be encrypted.
+ * @param userIDs the recipients key id's.
+ * @param Options String with the wanted gpg options. ex: "--armor"
+ * returns the encrypted text or empty string if encyption failed
+ */
+ static QString KgpgEncryptText(QString text,QString userIDs, QString Options="");
+
+ /**Decrypt text function
+ * @param text QString text to be decrypted.
+ * @param userID QString the name of the decryption key (only used to prompt user for passphrase)
+ */
+ static QString KgpgDecryptText(QString text,QString userID);
+// static QString KgpgDecryptFileToText(KURL srcUrl,QString userID);
+
+ /*
+ * Destructor for the class.
+ */
+ ~KgpgInterface();
+
+ static QString checkForUtf8(QString txt);
+
+
+ private slots:
+
+signals:
+
+ private:
+ /**
+ * @internal structure for communication
+ */
+ QString message,tempKeyFile,userIDs,txtprocess,output;
+ QCString passphrase;
+ bool deleteSuccess,konsLocal,anonymous,txtsent,decfinished,decok,badmdc;
+ int signSuccess;
+ int step,signb,sigsearch;
+ QString konsSignKey, konsKeyID;
+
+
+ /**
+ * @internal structure for the file information
+ */
+ KURL file;
+ /**
+ * @internal structure to send signal only once on error.
+ */
+ bool encError;
+};
+
+
+#endif
diff --git a/kopete/plugins/cryptography/kgpgselkey.cpp b/kopete/plugins/cryptography/kgpgselkey.cpp
new file mode 100644
index 00000000..70f76598
--- /dev/null
+++ b/kopete/plugins/cryptography/kgpgselkey.cpp
@@ -0,0 +1,245 @@
+//Code from KGPG
+
+/***************************************************************************
+ listkeys.cpp - description
+ -------------------
+ begin : Thu Jul 4 2002
+ copyright : (C) 2002 by y0k0
+ email : bj@altern.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+////////////////////////////////////////////////////// code for the key management
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+
+#include <klistview.h>
+#include <klocale.h>
+#include <qcheckbox.h>
+#include <kprocess.h>
+#include <kiconloader.h>
+
+#include "kgpgselkey.h"
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////// Secret key selection dialog, used when user wants to sign a key
+KgpgSelKey::KgpgSelKey(QWidget *parent, const char *name,bool showlocal):KDialogBase( parent, name, true,i18n("Private Key List"),Ok | Cancel)
+{
+ QString keyname;
+ QWidget *page = new QWidget(this);
+ QLabel *labeltxt;
+ KIconLoader *loader = KGlobal::iconLoader();
+
+ keyPair=loader->loadIcon("kgpg_key2",KIcon::Small,20);
+
+ setMinimumSize(300,200);
+ keysListpr = new KListView( page );
+ keysListpr->setRootIsDecorated(true);
+ keysListpr->addColumn( i18n( "Name" ) );
+ keysListpr->setShowSortIndicator(true);
+ keysListpr->setFullWidth(true);
+
+ labeltxt=new QLabel(i18n("Choose secret key:"),page);
+ QVBoxLayout *vbox=new QVBoxLayout(page,3);
+
+ vbox->addWidget(labeltxt);
+ vbox->addWidget(keysListpr);
+ if (showlocal==true)
+ {
+ local = new QCheckBox(i18n("Local signature (cannot be exported)"),page);
+ vbox->addWidget(local);
+ }
+
+ FILE *fp,*fp2;
+ QString tst,tst2;
+ char line[130];
+
+ // FIXME: Why use popen instead of KProcess, QProcess or KProcIO?!?
+ // Are we interested in having buffer overflows now? - Martijn
+ fp = popen( "gpg --no-tty --with-colon --list-secret-keys", "r" );
+ while ( fgets( line, sizeof(line), fp))
+ {
+ tst=line;
+ if (tst.startsWith("sec"))
+ {
+ const QString trust=tst.section(':',1,1);
+ QString val=tst.section(':',6,6);
+ QString id=QString("0x"+tst.section(':',4,4).right(8));
+ if (val.isEmpty())
+ val=i18n("Unlimited");
+ QString tr;
+ switch( trust[0] )
+ {
+ case 'o':
+ tr= i18n("Unknown");
+ break;
+ case 'i':
+ tr= i18n("Invalid");
+ break;
+ case 'd':
+ tr=i18n("Disabled");
+ break;
+ case 'r':
+ tr=i18n("Revoked");
+ break;
+ case 'e':
+ tr=i18n("Expired");
+ break;
+ case 'q':
+ tr=i18n("Undefined");
+ break;
+ case 'n':
+ tr=i18n("None");
+ break;
+ case 'm':
+ tr=i18n("Marginal");
+ break;
+ case 'f':
+ tr=i18n("Full");
+ break;
+ case 'u':
+ tr=i18n("Ultimate");
+ break;
+ default:
+ tr=i18n("?");
+ break;
+ }
+ tst=tst.section(":",9,9);
+
+ // FIXME: Same here: don't use popen! - Martijn
+ fp2 = popen( QString( "gpg --no-tty --with-colon --list-key %1" ).arg( KShellProcess::quote( id ) ).latin1(), "r" );
+ bool dead=true;
+ while ( fgets( line, sizeof(line), fp2))
+ {
+ tst2=line;
+ if (tst2.startsWith("pub"))
+ {
+ const QString trust2=tst2.section(':',1,1);
+ switch( trust2[0] )
+ {
+ case 'f':
+ dead=false;
+ break;
+ case 'u':
+ dead=false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ pclose(fp2);
+ if (!tst.isEmpty() && (!dead))
+ {
+ KListViewItem *item=new KListViewItem(keysListpr,extractKeyName(tst));
+ KListViewItem *sub= new KListViewItem(item,i18n("ID: %1, trust: %2, expiration: %3").arg(id).arg(tr).arg(val));
+ sub->setSelectable(false);
+ item->setPixmap(0,keyPair);
+ }
+ }
+ }
+ pclose(fp);
+
+
+ QObject::connect(keysListpr,SIGNAL(doubleClicked(QListViewItem *,const QPoint &,int)),this,SLOT(slotpreOk()));
+ QObject::connect(keysListpr,SIGNAL(clicked(QListViewItem *)),this,SLOT(slotSelect(QListViewItem *)));
+
+
+ keysListpr->setSelected(keysListpr->firstChild(),true);
+
+ page->show();
+ resize(this->minimumSize());
+ setMainWidget(page);
+}
+
+QString KgpgSelKey::extractKeyName(QString fullName)
+{
+ QString kMail;
+ if (fullName.find("<")!=-1)
+ {
+ kMail=fullName.section('<',-1,-1);
+ kMail.truncate(kMail.length()-1);
+ }
+ QString kName=fullName.section('<',0,0);
+ if (kName.find("(")!=-1) kName=kName.section('(',0,0);
+ return QString(kMail+" ("+kName+")").stripWhiteSpace();
+}
+
+void KgpgSelKey::slotpreOk()
+{
+ if (keysListpr->currentItem()->depth()!=0)
+ return;
+ else
+ slotOk();
+}
+
+void KgpgSelKey::slotOk()
+{
+ if (keysListpr->currentItem()==NULL)
+ reject();
+ else
+ accept();
+}
+
+void KgpgSelKey::slotSelect(QListViewItem *item)
+{
+ if (item==NULL) return;
+ if (item->depth()!=0)
+ {
+ keysListpr->setSelected(item->parent(),true);
+ keysListpr->setCurrentItem(item->parent());
+ }
+}
+
+
+QString KgpgSelKey::getkeyID()
+{
+ QString userid;
+ ///// emit selected key
+ if (keysListpr->currentItem()==NULL) return("");
+ else
+ {
+ userid=keysListpr->currentItem()->firstChild()->text(0);
+ userid=userid.section(',',0,0);
+ userid=userid.section(':',1,1);
+ userid=userid.stripWhiteSpace();
+ return(userid);
+ }
+}
+
+QString KgpgSelKey::getkeyMail()
+{
+ QString username;
+ ///// emit selected key
+ if (keysListpr->currentItem()==NULL) return("");
+ else
+ {
+ username=keysListpr->currentItem()->text(0);
+ //username=username.section(' ',0,0);
+ username=username.stripWhiteSpace();
+ return(username);
+ }
+}
+
+bool KgpgSelKey::getlocal()
+{
+ ///// emit exportation choice
+ return(local->isChecked());
+}
+
+#include "kgpgselkey.moc"
diff --git a/kopete/plugins/cryptography/kgpgselkey.h b/kopete/plugins/cryptography/kgpgselkey.h
new file mode 100644
index 00000000..11bcc498
--- /dev/null
+++ b/kopete/plugins/cryptography/kgpgselkey.h
@@ -0,0 +1,64 @@
+//Code from KGPG
+
+/***************************************************************************
+ listkeys.h - description
+ -------------------
+ begin : Thu Jul 4 2002
+ copyright : (C) 2002 by y0k0
+ email : bj@altern.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef LISTKEYS_H
+#define LISTKEYS_H
+
+
+#include <kdialogbase.h>
+
+class KListView;
+class QCheckBox;
+
+typedef struct gpgKey{
+ QString gpgkeymail;
+ QString gpgkeyname;
+ QString gpgkeyid;
+ QString gpgkeytrust;
+ QString gpgkeyvalidity;
+ QString gpgkeysize;
+ QString gpgkeycreation;
+ QString gpgkeyexpiration;
+ QString gpgkeyalgo;
+};
+
+class KgpgSelKey : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ KgpgSelKey( QWidget *parent = 0, const char *name = 0,bool showlocal=true);
+ KListView *keysListpr;
+QPixmap keyPair;
+QCheckBox *local;
+private slots:
+void slotOk();
+void slotpreOk();
+void slotSelect(QListViewItem *item);
+QString extractKeyName(QString fullName);
+
+public:
+ QString getkeyID();
+ QString getkeyMail();
+ bool getlocal();
+};
+
+
+
+#endif
diff --git a/kopete/plugins/cryptography/kopete_cryptography.desktop b/kopete/plugins/cryptography/kopete_cryptography.desktop
new file mode 100644
index 00000000..bbc3b591
--- /dev/null
+++ b/kopete/plugins/cryptography/kopete_cryptography.desktop
@@ -0,0 +1,129 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=encrypted
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_cryptography
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_cryptography
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Cryptography
+Name[ar]=التشÙير
+Name[be]=КрыптаграфіÑ
+Name[bg]=Шифроване
+Name[bn]=কà§à¦°à¦¿à¦ªà§à¦Ÿà§‹à¦—à§à¦°à¦¾à¦«à¦¿
+Name[bs]=Kriptografija
+Name[ca]=Xifrat
+Name[cs]=Šifrování
+Name[cy]=Cêl-ysgrifennaeth
+Name[da]=Kryptografi
+Name[de]=Kryptographie
+Name[el]=ΚÏυπτογÏαφία
+Name[eo]=Ĉifrado
+Name[es]=Criptografí­a
+Name[et]=Krüpto
+Name[eu]=Kriptografia
+Name[fa]=رمزنگاری
+Name[fi]=Salaus
+Name[fr]=Cryptographie
+Name[ga]=Criptiúchán
+Name[gl]=Criptografía
+Name[he]=קריפטוגרפיה
+Name[hi]=कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¥€
+Name[hr]=Kriptografija
+Name[hu]=Titkosítás
+Name[is]=Dulritun
+Name[it]=Crittografia
+Name[ja]=æš—å·åŒ–
+Name[ka]=კრიპტáƒáƒ’რáƒáƒ¤áƒ˜áƒ
+Name[kk]=Шифрлау
+Name[km]=ការ​គ្រីប
+Name[lt]=Å ifravimas
+Name[mk]=Криптографија
+Name[nb]=Kryptografi
+Name[nds]=Verslöteln
+Name[ne]=कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¥€
+Name[nl]=Cryptografie
+Name[nn]=Kryptografi
+Name[pl]=Szyfrowanie
+Name[pt]=Encriptação
+Name[pt_BR]=Criptografia
+Name[ro]=Criptografie
+Name[ru]=Шифрование
+Name[se]=Kryptográfiija
+Name[sk]=Å ifrovanie
+Name[sl]=Å ifriranje
+Name[sr]=Криптографија
+Name[sr@Latn]=Kriptografija
+Name[sv]=Kryptering
+Name[ta]=மறைகà¯à®•à¯à®±à®¿à®¯à®¿à®¯à®²à¯
+Name[tg]=Рамзгузорӣ
+Name[tr]=Åžifreleme
+Name[uk]=КриптографіÑ
+Name[uz]=Kriptografiya
+Name[uz@cyrillic]=КриптографиÑ
+Name[wa]=Criptografeye
+Name[zh_CN]=加密
+Name[zh_HK]=密碼學
+Name[zh_TW]=加密
+Comment=Encrypt and decrypt messages with GPG
+Comment[ar]=يقوم بتشÙير ÙˆÙÙ„ التشÙير الرسائل عن طريق GPG
+Comment[be]=Шыфраваць Ñ– раÑшыфроўваць паведамленні Ñž GPG
+Comment[bg]=Шифроване на ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ GPG
+Comment[bn]=জিপিজি বà§à¦¯à¦¬à¦¹à¦¾à¦° করে বারà§à¦¤à¦¾ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ à¦à¦¬à¦‚ ডিকà§à¦°à¦¿à¦ªà§à¦Ÿ করà§à¦¨
+Comment[bs]=Šifrujte poruke koristeći GPG
+Comment[ca]=Xifra i desxifra missatges amb GPG
+Comment[cs]=Šifrování a dešifrování zpráv pomocí GPG
+Comment[cy]=Celu a datgelu negeseuon efo GPG
+Comment[da]=Kryptér og dekryptér beskeder med GPG
+Comment[de]=Verschlüsselt und entschlüsselt Nachrichten mit GPG
+Comment[el]=ΚÏυπτογÏαφήστε και αποκÏυπτογÏαφήστε μηνÏματα με το GPG
+Comment[es]=Cifra y descifra mensajes con GPG
+Comment[et]=Sõnumite krüptimine ja lahtikrüptimine GPG abil
+Comment[eu]=Enkriptatu eta desenkriptatu mezuak GPG-rekin
+Comment[fa]=رمزبندی و سرگشایی پیامها با GPG
+Comment[fi]=Salaa ja pura salaus viesteistä GPG-ohjelmalla
+Comment[fr]=Chiffrer et déchiffrer les messages avec GPG
+Comment[gl]=Encriptar e desencriptar mensaxes con GPG
+Comment[he]=הצפנה ופענוח של הודעות בעזרת GPG
+Comment[hi]=जीपीजी के दà¥à¤µà¤¾à¤°à¤¾ संदेश à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ तथा डिकà¥à¤°à¤¿à¤ªà¥à¤Ÿ करे
+Comment[hr]=Kriptiranje i dekriptiranej poruka GPG-om
+Comment[hu]=Titkosítás/dekódolás GPG-vel
+Comment[is]=Dulrita og afkóða skeyti með GPG
+Comment[it]=Cifra e decifra i messaggi con GPG
+Comment[ja]=GPG を使ã£ã¦ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’æš—å·åŒ–/復å·
+Comment[ka]=GPG შეტყáƒáƒ‘ინებების დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ დრგáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ•áƒ
+Comment[kk]=GPG көмегімен хабарларды шифрлау не шифрын шешу
+Comment[km]=អ៊ិនគ្រីប និង​ឌិគ្រីប​សារ​ដោយ​ប្រើ GPG
+Comment[lt]=Užšifruoti ir atšifruoti žinutes GPG būdu
+Comment[mk]=Криптирајте и декриптирајте пораки Ñо GPG
+Comment[nb]=Krypter / dekrypter meldinger med GPG
+Comment[nds]=Narichten mit GPG ver- un opslöteln
+Comment[ne]=जीपीजी सà¤à¤— सनà¥à¤¦à¥‡à¤¶à¤¹à¤°à¥‚ गà¥à¤ªà¥à¤¤à¤¿à¤•à¤°à¤£ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥/गà¥à¤ªà¥à¤¤à¤²à¥‡à¤–न उलà¥à¤Ÿà¤¾à¤‰à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Versleutel en ontcijfer berichten met GPG
+Comment[nn]=Krypter og dekrypter meldingar med GPG
+Comment[pl]=Szyfruje i odszyfrowuje wiadomości za pomocą GPG
+Comment[pt]=Cifra e decifra as mensagens com o GPG
+Comment[pt_BR]=Criptografa e descriptografa mensagens com o GPG
+Comment[ro]=Criptează şi decriptează mesajele cu GPG
+Comment[ru]=Шифрование Ñообщений при помощи GPG
+Comment[se]=Kryptere ja dekryptere dieđáhusaid GPG:ain
+Comment[sk]=Šifruje a dešifruje správy pomocou GPG
+Comment[sl]=Å ifriranje in deÅ¡ifriranje sporoÄil z GPG
+Comment[sr]=Шифровање и дешифровање порука помоћу GPG-а
+Comment[sr@Latn]=Šifrovanje i dešifrovanje poruka pomoću GPG-a
+Comment[sv]=Kryptera och avkoda meddelanden med GPG
+Comment[ta]=GPG உடன௠மறையாகà¯à®•à®®à¯ மறà¯à®±à¯à®®à¯ மறைவிலகà¯à®•à®®à¯
+Comment[tg]=Рамзгузорӣ ва рамзкушоии пайёмҳо ба воÑитаи GPG
+Comment[tr]=GPG ile mesajları şifrele ve çöz
+Comment[uk]=Ð¨Ð¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° Ñ€Ð¾Ð·ÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ з GPG
+Comment[wa]=Ecripter eyet discripter les messaedjes avou GPG
+Comment[zh_CN]=用 GPG 加密和解密消æ¯
+Comment[zh_HK]=以 GPG 加密或解密訊æ¯
+Comment[zh_TW]=用 GPG 加密或解密您的訊æ¯
diff --git a/kopete/plugins/cryptography/kopete_cryptography_config.desktop b/kopete/plugins/cryptography/kopete_cryptography_config.desktop
new file mode 100644
index 00000000..ed2d4cdb
--- /dev/null
+++ b/kopete/plugins/cryptography/kopete_cryptography_config.desktop
@@ -0,0 +1,126 @@
+[Desktop Entry]
+Icon=encrypted
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_cryptography
+X-KDE-FactoryName=CryptographyConfigFactory
+X-KDE-ParentApp=kopete_cryptography
+X-KDE-ParentComponents=kopete_cryptography
+
+Name=Cryptography
+Name[ar]=التشÙير
+Name[be]=КрыптаграфіÑ
+Name[bg]=Шифроване
+Name[bn]=কà§à¦°à¦¿à¦ªà§à¦Ÿà§‹à¦—à§à¦°à¦¾à¦«à¦¿
+Name[bs]=Kriptografija
+Name[ca]=Xifrat
+Name[cs]=Šifrování
+Name[cy]=Cêl-ysgrifennaeth
+Name[da]=Kryptografi
+Name[de]=Kryptographie
+Name[el]=ΚÏυπτογÏαφία
+Name[eo]=Ĉifrado
+Name[es]=Criptografí­a
+Name[et]=Krüpto
+Name[eu]=Kriptografia
+Name[fa]=رمزنگاری
+Name[fi]=Salaus
+Name[fr]=Cryptographie
+Name[ga]=Criptiúchán
+Name[gl]=Criptografía
+Name[he]=קריפטוגרפיה
+Name[hi]=कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¥€
+Name[hr]=Kriptografija
+Name[hu]=Titkosítás
+Name[is]=Dulritun
+Name[it]=Crittografia
+Name[ja]=æš—å·åŒ–
+Name[ka]=კრიპტáƒáƒ’რáƒáƒ¤áƒ˜áƒ
+Name[kk]=Шифрлау
+Name[km]=ការ​គ្រីប
+Name[lt]=Å ifravimas
+Name[mk]=Криптографија
+Name[nb]=Kryptografi
+Name[nds]=Verslöteln
+Name[ne]=कà¥à¤°à¤¿à¤ªà¥à¤Ÿà¥‹à¤—à¥à¤°à¤¾à¤«à¥€
+Name[nl]=Cryptografie
+Name[nn]=Kryptografi
+Name[pl]=Szyfrowanie
+Name[pt]=Encriptação
+Name[pt_BR]=Criptografia
+Name[ro]=Criptografie
+Name[ru]=Шифрование
+Name[se]=Kryptográfiija
+Name[sk]=Å ifrovanie
+Name[sl]=Å ifriranje
+Name[sr]=Криптографија
+Name[sr@Latn]=Kriptografija
+Name[sv]=Kryptering
+Name[ta]=மறைகà¯à®•à¯à®±à®¿à®¯à®¿à®¯à®²à¯
+Name[tg]=Рамзгузорӣ
+Name[tr]=Åžifreleme
+Name[uk]=КриптографіÑ
+Name[uz]=Kriptografiya
+Name[uz@cyrillic]=КриптографиÑ
+Name[wa]=Criptografeye
+Name[zh_CN]=加密
+Name[zh_HK]=密碼學
+Name[zh_TW]=加密
+Comment=Encrypts messages using PGP
+Comment[ar]=يشÙر الرسائل باستخدام PGP
+Comment[be]=Шыфруе паведамленні ў PGP
+Comment[bg]=Шифроване на ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ GPG
+Comment[bn]=পিজিপি বà§à¦¯à¦¬à¦¹à¦¾à¦° করে বারà§à¦¤à¦¾ à¦à¦¨à¦•à§à¦°à¦¿à¦ªà§à¦Ÿ করে
+Comment[bs]=Šifruje poruke koristeći GPG
+Comment[ca]=Xifra missatges emprant PGP
+Comment[cs]=Šifrování zpráv pomocí GPG
+Comment[cy]=Celu negeseuon gan ddefnyddio PGP
+Comment[da]=Krypterer beskeder ved brug af PGP
+Comment[de]=Verschlüsselt Nachrichten mit PGP
+Comment[el]=ΚÏυπτογÏαφεί τα μηνÏματα χÏησιμοποιώντας το PGP
+Comment[es]=Cifra mensajes usando PGP
+Comment[et]=Sõnumite krüptimine PGP abil
+Comment[eu]=Enkriptatu mezuak PGP-rekin
+Comment[fa]=پیامها را با استÙاده از GPG رمزبندی می‌کند
+Comment[fi]=Salaa viestit käyttäen PGP-ohjelmaa
+Comment[fr]=Chiffrer les messages avec PGP
+Comment[gl]=Encripta mensaxes con PGP
+Comment[he]=מצפין הודעות בעזרת PGP
+Comment[hi]=जीपीजी के दà¥à¤µà¤¾à¤°à¤¾ संदेश à¤à¤¨à¤•à¥à¤°à¤¿à¤ªà¥à¤Ÿ करे
+Comment[hr]=Kriptiranje i dekriptiranej poruka PGP-om
+Comment[hu]=Üzenetek titkosítása PGP-vel
+Comment[is]=Dulritar skeyti með PGP
+Comment[it]=Cifra i messaggi con PGP
+Comment[ja]=PGP を使ã£ã¦ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’æš—å·åŒ–
+Comment[ka]=PGP შეტყáƒáƒ‘ინებების დáƒáƒ¨áƒ˜áƒ¤áƒ•áƒ áƒ•áƒ
+Comment[kk]=PGP көмегімен хабарларды шифрлау
+Comment[km]=អ៊ិនគ្រីប​សារ​ដោយ​ប្រើ PGP
+Comment[lt]=Užšifruoti žinutes PGP būdu
+Comment[mk]=Криптирајте пораки употребувајќи PGP
+Comment[nb]=Krypterer meldinger med PGP
+Comment[nds]=Verslötelt Narichten mit PGP
+Comment[ne]=पीजीपी पà¥à¤°à¤¯à¥‹à¤— गरेर सनà¥à¤¦à¥‡à¤¶ गà¥à¤ªà¥à¤¤à¤¿à¤•à¤°à¤£ गरà¥à¤¦à¤›
+Comment[nl]=Versleutelt berichten met PGP
+Comment[nn]=Krypterer meldingar med PGP
+Comment[pl]=Szyfruje wiadomości za pomocą PGP
+Comment[pt]=Cifra as mensagens usando o PGP
+Comment[pt_BR]=Criptografa mensagens usando o PGP
+Comment[ro]=Criptează mesajele cu PGP
+Comment[ru]=Шифрует Ñообщение Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ PGP
+Comment[se]=Kryptere dieđáhusaid PGP:ain
+Comment[sk]=Šifruje správy pomocou PGP
+Comment[sl]=Å ifriranje sporoÄil s PGP
+Comment[sr]=Шифровање и дешифровање порука помоћу PGP-а
+Comment[sr@Latn]=Šifrovanje i dešifrovanje poruka pomoću PGP-a
+Comment[sv]=Krypterar meddelanden med PGP
+Comment[ta]=PGP யை பயனà¯à®ªà®Ÿà¯à®¤à¯à®¤à®¿ செயà¯à®¤à®¿à®•à®³à¯ˆ சஙà¯à®•à¯‡à®¤à®™à¯à®•à®³à®¾à®•à¯à®•à¯
+Comment[tg]=Рамзгузории пайёмҳо ба воÑитаи PGP
+Comment[tr]=PGP kullanan mesajları şifreler
+Comment[uk]=Шифрує Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð·Ð° допомогою PGP
+Comment[wa]=Ecripter les messaedjes avou PGP
+Comment[zh_CN]=用 PGP 加密消æ¯
+Comment[zh_HK]=將訊æ¯ä»¥ PGP 加密
+Comment[zh_TW]=用 PGP 加密訊æ¯
+
diff --git a/kopete/plugins/cryptography/popuppublic.cpp b/kopete/plugins/cryptography/popuppublic.cpp
new file mode 100644
index 00000000..36008bcf
--- /dev/null
+++ b/kopete/plugins/cryptography/popuppublic.cpp
@@ -0,0 +1,514 @@
+//File Imported from KGPG ( 2004 - 09 - 03 )
+
+/***************************************************************************
+ popuppublic.cpp - description
+ -------------------
+ begin : Sat Jun 29 2002
+ copyright : (C) 2002 by Jean-Baptiste Mardelle
+ email : bj@altern.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+//////////////////////////////////////////////////////// code for choosing a public key from a list for encryption
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qptrlist.h>
+#include <qwhatsthis.h>
+#include <qpainter.h>
+#include <qiconset.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qhbuttongroup.h>
+#include <qtoolbutton.h>
+#include <qapplication.h>
+#include <qlabel.h>
+
+#include <kdeversion.h>
+#include <klistview.h>
+#include <kprocess.h>
+#include <kprocio.h>
+#include <klocale.h>
+#include <kaccel.h>
+#include <klistviewsearchline.h>
+#include <kactivelabel.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kconfig.h>
+
+
+#include "popuppublic.h"
+//#include "kgpgsettings.h"
+//#include "kgpgview.h"
+//#include "kgpg.h"
+#include "kgpginterface.h"
+
+///////////////// klistviewitem special
+
+class UpdateViewItem2 : public KListViewItem
+{
+public:
+ UpdateViewItem2(QListView *parent, QString name,QString mail,QString id,bool isDefault);
+ virtual void paintCell(QPainter *p, const QColorGroup &cg,int col, int width, int align);
+ virtual QString key(int c,bool ) const;
+ bool def;
+};
+
+UpdateViewItem2::UpdateViewItem2(QListView *parent, QString name,QString mail,QString id,bool isDefault)
+ : KListViewItem(parent)
+{
+def=isDefault;
+ setText(0,name);
+ setText(1,mail);
+ setText(2,id);
+}
+
+
+void UpdateViewItem2::paintCell(QPainter *p, const QColorGroup &cg,int column, int width, int alignment)
+{
+ if ((def) && (column<2)) {
+ QFont font(p->font());
+ font.setBold(true);
+ p->setFont(font);
+ }
+ KListViewItem::paintCell(p, cg, column, width, alignment);
+}
+
+QString UpdateViewItem2 :: key(int c,bool ) const
+{
+ return text(c).lower();
+}
+
+/////////////// main view
+
+popupPublic::popupPublic(QWidget *parent, const char *name,QString sfile,bool filemode,KShortcut goDefaultKey):
+KDialogBase( Plain, i18n("Select Public Key"), Details | Ok | Cancel, Ok, parent, name,true)
+{
+ QWidget *page = plainPage();
+ QVBoxLayout *vbox=new QVBoxLayout(page,0,spacingHint());
+ vbox->setAutoAdd(true);
+
+ setButtonText(KDialogBase::Details,i18n("Options"));
+
+/* if (KGpgSettings::allowCustomEncryptionOptions())
+ customOptions=KGpgSettings::customEncryptionOptions();*/
+
+ KIconLoader *loader = KGlobal::iconLoader();
+
+ keyPair=loader->loadIcon("kgpg_key2",KIcon::Small,20);
+ keySingle=loader->loadIcon("kgpg_key1",KIcon::Small,20);
+ keyGroup=loader->loadIcon("kgpg_key3",KIcon::Small,20);
+
+ if (filemode) setCaption(i18n("Select Public Key for %1").arg(sfile));
+ fmode=filemode;
+
+ QHButtonGroup *hBar=new QHButtonGroup(page);
+ //hBar->setFrameStyle(QFrame::NoFrame);
+ hBar->setMargin(0);
+
+ QToolButton *clearSearch = new QToolButton(hBar);
+ clearSearch->setTextLabel(i18n("Clear Search"), true);
+ clearSearch->setIconSet(SmallIconSet(QApplication::reverseLayout() ? "clear_left"
+ : "locationbar_erase"));
+ (void) new QLabel(i18n("Search: "),hBar);
+ KListViewSearchLine* listViewSearch = new KListViewSearchLine(hBar);
+ connect(clearSearch, SIGNAL(pressed()), listViewSearch, SLOT(clear()));
+
+ keysList = new KListView( page );
+ keysList->addColumn(i18n("Name"));
+ keysList->addColumn(i18n("Email"));
+ keysList->addColumn(i18n("ID"));
+
+ listViewSearch->setListView(keysList);
+
+ keysList->setRootIsDecorated(false);
+ page->setMinimumSize(540,200);
+ keysList->setShowSortIndicator(true);
+ keysList->setFullWidth(true);
+ keysList->setAllColumnsShowFocus(true);
+ keysList->setSelectionModeExt(KListView::Extended);
+ keysList->setColumnWidthMode(0,QListView::Manual);
+ keysList->setColumnWidthMode(1,QListView::Manual);
+ keysList->setColumnWidth(0,210);
+ keysList->setColumnWidth(1,210);
+
+ boutonboxoptions=new QButtonGroup(5,Qt::Vertical ,page,0);
+
+ KActionCollection *actcol=new KActionCollection(this);
+ (void) new KAction(i18n("&Go to Default Key"),goDefaultKey, this, SLOT(slotGotoDefaultKey()),actcol,"go_default_key");
+
+
+ CBarmor=new QCheckBox(i18n("ASCII armored encryption"),boutonboxoptions);
+ CBuntrusted=new QCheckBox(i18n("Allow encryption with untrusted keys"),boutonboxoptions);
+ CBhideid=new QCheckBox(i18n("Hide user id"),boutonboxoptions);
+ setDetailsWidget(boutonboxoptions);
+ QWhatsThis::add
+ (keysList,i18n("<b>Public keys list</b>: select the key that will be used for encryption."));
+ QWhatsThis::add
+ (CBarmor,i18n("<b>ASCII encryption</b>: makes it possible to open the encrypted file/message in a text editor"));
+ QWhatsThis::add
+ (CBhideid,i18n("<b>Hide user ID</b>: Do not put the keyid into encrypted packets. This option hides the receiver "
+ "of the message and is a countermeasure against traffic analysis. It may slow down the decryption process because "
+ "all available secret keys are tried."));
+ QWhatsThis::add
+ (CBuntrusted,i18n("<b>Allow encryption with untrusted keys</b>: when you import a public key, it is usually "
+ "marked as untrusted and you cannot use it unless you sign it in order to make it 'trusted'. Checking this "
+ "box enables you to use any key, even if it has not be signed."));
+
+ if (filemode) {
+ QWidget *parentBox=new QWidget(boutonboxoptions);
+ QHBoxLayout *shredBox=new QHBoxLayout(parentBox,0);
+ //shredBox->setFrameStyle(QFrame::NoFrame);
+ //shredBox->setMargin(0);
+ CBshred=new QCheckBox(i18n("Shred source file"),parentBox);
+ QWhatsThis::add
+ (CBshred,i18n("<b>Shred source file</b>: permanently remove source file. No recovery will be possible"));
+
+ QString shredWhatsThis = i18n( "<qt><b>Shred source file:</b><br /><p>Checking this option will shred (overwrite several times before erasing) the files you have encrypted. This way, it is almost impossible that the source file is recovered.</p><p><b>But you must be aware that this is not secure</b> on all file systems, and that parts of the file may have been saved in a temporary file or in the spooler of your printer if you previously opened it in an editor or tried to print it. Only works on files (not on folders).</p></qt>");
+ KActiveLabel *warn= new KActiveLabel( i18n("<a href=\"whatsthis:%1\">Read this before using shredding</a>").arg(shredWhatsThis),parentBox );
+ shredBox->addWidget(CBshred);
+ shredBox->addWidget(warn);
+ }
+
+ CBsymmetric=new QCheckBox(i18n("Symmetrical encryption"),boutonboxoptions);
+ QWhatsThis::add
+ (CBsymmetric,i18n("<b>Symmetrical encryption</b>: encryption does not use keys. You just need to give a password "
+ "to encrypt/decrypt the file"));
+ QObject::connect(CBsymmetric,SIGNAL(toggled(bool)),this,SLOT(isSymetric(bool)));
+
+//BEGIN modified for Kopete
+
+ setWFlags( getWFlags() | Qt::WDestructiveClose );
+
+
+ /*CBarmor->setChecked( KGpgSettings::asciiArmor() );
+ CBuntrusted->setChecked( KGpgSettings::allowUntrustedKeys() );
+ CBhideid->setChecked( KGpgSettings::hideUserID() );
+ if (filemode) CBshred->setChecked( KGpgSettings::shredSource() );*/
+ KConfig *config = KGlobal::config();
+ config->setGroup("Cryptography Plugin");
+
+ CBarmor->hide();
+ CBuntrusted->setChecked(config->readBoolEntry("UntrustedKeys", true));
+ CBhideid->hide();
+ if (filemode) CBshred->hide();
+ CBsymmetric->hide();
+
+//END modified for Kopete
+
+ /*if (KGpgSettings::allowCustomEncryptionOptions()) {
+ QHButtonGroup *bGroup = new QHButtonGroup(page);
+ //bGroup->setFrameStyle(QFrame::NoFrame);
+ (void) new QLabel(i18n("Custom option:"),bGroup);
+ KLineEdit *optiontxt=new KLineEdit(bGroup);
+ optiontxt->setText(customOptions);
+ QWhatsThis::add
+ (optiontxt,i18n("<b>Custom option</b>: for experienced users only, allows you to enter a gpg command line option, like: '--armor'"));
+ QObject::connect(optiontxt,SIGNAL(textChanged ( const QString & )),this,SLOT(customOpts(const QString & )));
+ }*/
+ QObject::connect(keysList,SIGNAL(doubleClicked(QListViewItem *,const QPoint &,int)),this,SLOT(slotOk()));
+// QObject::connect(this,SIGNAL(okClicked()),this,SLOT(crypte()));
+ QObject::connect(CBuntrusted,SIGNAL(toggled(bool)),this,SLOT(refresh(bool)));
+
+ char line[200]="\0";
+ FILE *fp2;
+ seclist=QString::null;
+
+ fp2 = popen("gpg --no-secmem-warning --no-tty --with-colon --list-secret-keys ", "r");
+ while ( fgets( line, sizeof(line), fp2))
+ {
+ QString readLine=line;
+ if (readLine.startsWith("sec")) seclist+=", 0x"+readLine.section(":",4,4).right(8);
+ }
+ pclose(fp2);
+
+ trusted=CBuntrusted->isChecked();
+
+ refreshkeys();
+ setMinimumSize(550,200);
+ updateGeometry();
+ keysList->setFocus();
+ show();
+}
+
+popupPublic::~popupPublic()
+{}
+
+
+void popupPublic::slotAccept()
+{
+accept();
+}
+
+void popupPublic::enable()
+{
+ QListViewItem *current = keysList->firstChild();
+ if (current==NULL)
+ return;
+ current->setVisible(true);
+ while ( current->nextSibling() ) {
+ current = current->nextSibling();
+ current->setVisible(true);
+ }
+ keysList->ensureItemVisible(keysList->currentItem());
+}
+
+void popupPublic::sort()
+{
+ bool reselect=false;
+ QListViewItem *current = keysList->firstChild();
+ if (current==NULL)
+ return;
+
+ if ((untrustedList.find(current->text(2))!=untrustedList.end()) && (!current->text(2).isEmpty())){
+ if (current->isSelected()) {
+ current->setSelected(false);
+ reselect=true;
+ }
+ current->setVisible(false);
+ }
+
+ while ( current->nextSibling() ) {
+ current = current->nextSibling();
+ if ((untrustedList.find(current->text(2))!=untrustedList.end()) && (!current->text(2).isEmpty())) {
+ if (current->isSelected()) {
+ current->setSelected(false);
+ reselect=true;
+ }
+ current->setVisible(false);
+ }
+ }
+
+ if (reselect) {
+ QListViewItem *firstvisible;
+ firstvisible=keysList->firstChild();
+ while (firstvisible->isVisible()!=true) {
+ firstvisible=firstvisible->nextSibling();
+ if (firstvisible==NULL)
+ return;
+ }
+ keysList->setSelected(firstvisible,true);
+ keysList->setCurrentItem(firstvisible);
+ keysList->ensureItemVisible(firstvisible);
+ }
+}
+
+void popupPublic::isSymetric(bool state)
+{
+ keysList->setEnabled(!state);
+ CBuntrusted->setEnabled(!state);
+ CBhideid->setEnabled(!state);
+}
+
+
+void popupPublic::customOpts(const QString &str)
+{
+ customOptions=str;
+}
+
+void popupPublic::slotGotoDefaultKey()
+{
+ /*QListViewItem *myDefaulKey = keysList->findItem(KGpgSettings::defaultKey(),2);
+ keysList->clearSelection();
+ keysList->setCurrentItem(myDefaulKey);
+ keysList->setSelected(myDefaulKey,true);
+ keysList->ensureItemVisible(myDefaulKey);*/
+}
+
+void popupPublic::refresh(bool state)
+{
+ if (state)
+ enable();
+ else
+ sort();
+}
+
+void popupPublic::refreshkeys()
+{
+ keysList->clear();
+ /*QStringList groups= QStringList::split(",", KGpgSettings::groups());
+ if (!groups.isEmpty())
+ {
+ for ( QStringList::Iterator it = groups.begin(); it != groups.end(); ++it )
+ {
+ if (!QString(*it).isEmpty())
+ {
+ UpdateViewItem2 *item=new UpdateViewItem2(keysList,QString(*it),QString::null,QString::null,false);
+ item->setPixmap(0,keyGroup);
+ }
+ }
+ }*/
+ KProcIO *encid=new KProcIO();
+ *encid << "gpg"<<"--no-secmem-warning"<<"--no-tty"<<"--with-colon"<<"--list-keys";
+ ///////// when process ends, update dialog infos
+ QObject::connect(encid, SIGNAL(processExited(KProcess *)),this, SLOT(slotpreselect()));
+ QObject::connect(encid, SIGNAL(readReady(KProcIO *)),this, SLOT(slotprocread(KProcIO *)));
+ encid->start(KProcess::NotifyOnExit,true);
+}
+
+void popupPublic::slotpreselect()
+{
+QListViewItem *it;
+ //if (fmode) it=keysList->findItem(KGpgSettings::defaultKey(),2);
+ //else {
+ it=keysList->firstChild();
+ if (it==NULL)
+ return;
+ while (!it->isVisible()) {
+ it=it->nextSibling();
+ if (it==NULL)
+ return;
+ }
+ //}
+if (!trusted)
+ sort();
+ keysList->setSelected(it,true);
+ keysList->setCurrentItem(it);
+ keysList->ensureItemVisible(it);
+emit keyListFilled();
+}
+
+void popupPublic::slotSetVisible()
+{
+ keysList->ensureItemVisible(keysList->currentItem());
+}
+
+void popupPublic::slotprocread(KProcIO *p)
+{
+ ///////////////////////////////////////////////////////////////// extract encryption keys
+ bool dead;
+ QString tst,keyname,keymail;
+
+ QString defaultKey ;// = KGpgSettings::defaultKey().right(8);
+
+ while (p->readln(tst)!=-1) {
+ if (tst.startsWith("pub")) {
+ QStringList keyString=QStringList::split(":",tst,true);
+ dead=false;
+ const QString trust=keyString[1];
+ QString val=keyString[6];
+ QString id=QString("0x"+keyString[4].right(8));
+ if (val.isEmpty())
+ val=i18n("Unlimited");
+ QString tr;
+ switch( trust[0] ) {
+ case 'o':
+ untrustedList<<id;
+ break;
+ case 'i':
+ dead=true;
+ break;
+ case 'd':
+ dead=true;
+ break;
+ case 'r':
+ dead=true;
+ break;
+ case 'e':
+ dead=true;
+ break;
+ case 'q':
+ untrustedList<<id;
+ break;
+ case 'n':
+ untrustedList<<id;
+ break;
+ case 'm':
+ untrustedList<<id;
+ break;
+ case 'f':
+ break;
+ case 'u':
+ break;
+ default:
+ untrustedList<<id;
+ break;
+ }
+ if (keyString[11].find('D')!=-1) dead=true;
+ tst=keyString[9];
+ if (tst.find("<")!=-1) {
+ keymail=tst.section('<',-1,-1);
+ keymail.truncate(keymail.length()-1);
+ keyname=tst.section('<',0,0);
+ //if (keyname.find("(")!=-1)
+ // keyname=keyname.section('(',0,0);
+ } else {
+ keymail=QString::null;
+ keyname=tst;//.section('(',0,0);
+ }
+
+ keyname=KgpgInterface::checkForUtf8(keyname);
+
+ if ((!dead) && (!tst.isEmpty())) {
+ bool isDefaultKey=false;
+ if (id.right(8)==defaultKey) isDefaultKey=true;
+ UpdateViewItem2 *item=new UpdateViewItem2(keysList,keyname,keymail,id,isDefaultKey);
+ //KListViewItem *sub= new KListViewItem(item,i18n("ID: %1, trust: %2, validity: %3").arg(id).arg(tr).arg(val));
+ //sub->setSelectable(false);
+ if (seclist.find(tst,0,FALSE)!=-1)
+ item->setPixmap(0,keyPair);
+ else
+ item->setPixmap(0,keySingle);
+ }
+ }
+ }
+}
+
+
+void popupPublic::slotOk()
+{
+//BEGIN modified for Kopete
+ KConfig *config = KGlobal::config();
+ config->setGroup("Cryptography Plugin");
+
+ config->writeEntry("UntrustedKeys", CBuntrusted->isChecked());
+ config->writeEntry("HideID", CBhideid->isChecked());
+
+//END modified for Kopete
+
+
+
+
+ ////// emit selected data
+kdDebug(2100)<<"Ok pressed"<<endl;
+ QStringList selectedKeys;
+ QString userid;
+ QPtrList<QListViewItem> list=keysList->selectedItems();
+
+ for ( uint i = 0; i < list.count(); ++i )
+ if ( list.at(i) ) {
+ if (!list.at(i)->text(2).isEmpty()) selectedKeys<<list.at(i)->text(2);
+ else selectedKeys<<list.at(i)->text(0);
+ }
+ if (selectedKeys.isEmpty() && !CBsymmetric->isChecked())
+ return;
+kdDebug(2100)<<"Selected Key:"<<selectedKeys<<endl;
+ QStringList returnOptions;
+ if (CBuntrusted->isChecked())
+ returnOptions<<"--always-trust";
+ if (CBarmor->isChecked())
+ returnOptions<<"--armor";
+ if (CBhideid->isChecked())
+ returnOptions<<"--throw-keyid";
+ /*if ((KGpgSettings::allowCustomEncryptionOptions()) && (!customOptions.stripWhiteSpace().isEmpty()))
+ returnOptions.operator+ (QStringList::split(QString(" "),customOptions.simplifyWhiteSpace()));*/
+ //hide();
+
+//MODIFIED for kopete
+ if (fmode)
+ emit selectedKey(selectedKeys.first(),QString(),CBshred->isChecked(),CBsymmetric->isChecked());
+ else
+ emit selectedKey(selectedKeys.first(),QString(),false,CBsymmetric->isChecked());
+ accept();
+}
+
+#include "popuppublic.moc"
diff --git a/kopete/plugins/cryptography/popuppublic.h b/kopete/plugins/cryptography/popuppublic.h
new file mode 100644
index 00000000..7e147385
--- /dev/null
+++ b/kopete/plugins/cryptography/popuppublic.h
@@ -0,0 +1,78 @@
+//File Imported from KGPG ( 2004 - 09 - 03 )
+
+/***************************************************************************
+ popuppublic.h - description
+ -------------------
+ begin : Sat Jun 29 2002
+ copyright : (C) 2002 by Jean-Baptiste Mardelle
+ email : bj@altern.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+#ifndef POPUPPUBLIC_H
+#define POPUPPUBLIC_H
+
+#include <kdialogbase.h>
+
+//#include <kiconloader.h>
+#include <kshortcut.h>
+
+
+class QPushButton;
+class QCheckBox;
+class KListView;
+class QButtonGroup;
+class KProcIO;
+
+class popupPublic : public KDialogBase //QDialog
+{
+ Q_OBJECT
+public:
+
+ popupPublic(QWidget *parent=0, const char *name=0,QString sfile="",bool filemode=false,KShortcut goDefaultKey=QKeySequence(CTRL+Qt::Key_Home));
+ ~popupPublic();
+ KListView *keysList;
+ QCheckBox *CBarmor,*CBuntrusted,*CBshred,*CBsymmetric,*CBhideid;
+ bool fmode,trusted;
+ QPixmap keyPair,keySingle,keyGroup;
+ QString seclist;
+ QStringList untrustedList;
+
+private:
+ KConfig *config;
+ QButtonGroup *boutonboxoptions;
+ QString customOptions;
+
+private slots:
+ void customOpts(const QString &);
+ void slotprocread(KProcIO *);
+ void slotpreselect();
+ void refreshkeys();
+ void refresh(bool state);
+ void isSymetric(bool state);
+ void sort();
+ void enable();
+ void slotGotoDefaultKey();
+
+public slots:
+void slotAccept();
+void slotSetVisible();
+
+protected slots:
+virtual void slotOk();
+
+signals:
+ void selectedKey(QString & ,QString,bool,bool);
+ void keyListFilled();
+
+};
+
+#endif // POPUPPUBLIC_H
+
diff --git a/kopete/plugins/highlight/Makefile.am b/kopete/plugins/highlight/Makefile.am
new file mode 100644
index 00000000..d74a3886
--- /dev/null
+++ b/kopete/plugins/highlight/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+
+SUBDIRS = icons
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_highlight.la kcm_kopete_highlight.la
+
+kopete_highlight_la_SOURCES = highlightplugin.cpp highlightconfig.cpp filter.cpp
+kopete_highlight_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_highlight_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_highlight_la_SOURCES = highlightprefsbase.ui highlightpreferences.cpp filter.cpp highlightconfig.cpp
+kcm_kopete_highlight_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_highlight_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_highlight.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_highlight_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
diff --git a/kopete/plugins/highlight/filter.cpp b/kopete/plugins/highlight/filter.cpp
new file mode 100644
index 00000000..814bd678
--- /dev/null
+++ b/kopete/plugins/highlight/filter.cpp
@@ -0,0 +1,32 @@
+/***************************************************************************
+ filter.cpp - filter for the highlight plugin
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "filter.h"
+
+Filter::Filter()
+{
+}
+
+Filter::~Filter()
+{
+}
+
+/*
+ * But is this file useful? :-D
+ */
+
+
diff --git a/kopete/plugins/highlight/filter.h b/kopete/plugins/highlight/filter.h
new file mode 100644
index 00000000..b2ac0794
--- /dev/null
+++ b/kopete/plugins/highlight/filter.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ filter.h - filter for the highlight plugin
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef FILTER_H
+#define FILTER_H
+
+
+#include <qstring.h>
+#include <qcolor.h>
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ **/
+class Filter
+{
+public:
+ Filter();
+ ~Filter();
+
+ QString displayName;
+ QString search;
+ bool caseSensitive;
+ bool isRegExp;
+
+ bool setImportance;
+ unsigned int importance;
+
+ bool setFG;
+ QColor FG;
+
+ bool setBG;
+ QColor BG;
+
+ bool playSound;
+ QString soundFN;
+
+ bool raiseView;
+};
+
+#endif
diff --git a/kopete/plugins/highlight/highlightconfig.cpp b/kopete/plugins/highlight/highlightconfig.cpp
new file mode 100644
index 00000000..ba97e6a8
--- /dev/null
+++ b/kopete/plugins/highlight/highlightconfig.cpp
@@ -0,0 +1,206 @@
+/*
+ highlightconfig.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qfile.h>
+#include <qstylesheet.h>
+#include <qregexp.h>
+#include <qdir.h>
+#include <qdom.h>
+
+#include <ksavefile.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+
+#include "filter.h"
+#include "highlightconfig.h"
+
+
+HighlightConfig::HighlightConfig()
+{
+ load();
+ m_filters.setAutoDelete(true);
+}
+
+HighlightConfig::~HighlightConfig()
+{
+ m_filters.clear();
+}
+
+void HighlightConfig::removeFilter(Filter *f)
+{
+ //m_filters is "autodelete (true) so when we use remove(...) it deleted f
+ //so don't use (delete (f) after otherwise ot crash
+ m_filters.remove(f);
+}
+
+void HighlightConfig::appendFilter(Filter *f)
+{
+ m_filters.append(f);
+}
+
+QPtrList<Filter> HighlightConfig::filters() const
+{
+ return m_filters;
+}
+
+Filter* HighlightConfig::newFilter()
+{
+ Filter *filtre=new Filter();
+ filtre->caseSensitive=false;
+ filtre->isRegExp=false;
+ filtre->setImportance=false;
+ filtre->importance=1;
+ filtre->setBG=false;
+ filtre->setFG=false;
+ filtre->playSound=false;
+ filtre->raiseView=false;
+ filtre->displayName=i18n("-New filter-");
+ m_filters.append(filtre);
+ return filtre;
+}
+
+void HighlightConfig::load()
+{
+ m_filters.clear(); //clear filters
+
+ QString filename = locateLocal( "appdata", QString::fromLatin1( "highlight.xml" ) );
+ if( filename.isEmpty() )
+ return ;
+
+ QDomDocument filterList( QString::fromLatin1( "highlight-plugin" ) );
+
+ QFile filterListFile( filename );
+ filterListFile.open( IO_ReadOnly );
+ filterList.setContent( &filterListFile );
+
+ QDomElement list = filterList.documentElement();
+
+ QDomNode node = list.firstChild();
+ while( !node.isNull() )
+ {
+ QDomElement element = node.toElement();
+ if( !element.isNull() )
+ {
+// if( element.tagName() == QString::fromLatin1("filter")
+// {
+ Filter *filtre=newFilter();
+ QDomNode filterNode = node.firstChild();
+
+ while( !filterNode.isNull() )
+ {
+ QDomElement filterElement = filterNode.toElement();
+ if( !filterElement.isNull() )
+ {
+ if( filterElement.tagName() == QString::fromLatin1( "display-name" ) )
+ {
+ filtre->displayName = filterElement.text();
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "search" ) )
+ {
+ filtre->search = filterElement.text();
+
+ filtre->caseSensitive= ( filterElement.attribute( QString::fromLatin1( "caseSensitive" ), QString::fromLatin1( "1" ) ) == QString::fromLatin1( "1" ) );
+ filtre->isRegExp= ( filterElement.attribute( QString::fromLatin1( "regExp" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "FG" ) )
+ {
+ filtre->FG = filterElement.text();
+ filtre->setFG= ( filterElement.attribute( QString::fromLatin1( "set" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "BG" ) )
+ {
+ filtre->BG = filterElement.text();
+ filtre->setBG= ( filterElement.attribute( QString::fromLatin1( "set" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "importance" ) )
+ {
+ filtre->importance = filterElement.text().toUInt();
+ filtre->setImportance= ( filterElement.attribute( QString::fromLatin1( "set" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "sound" ) )
+ {
+ filtre->soundFN = filterElement.text();
+ filtre->playSound = ( filterElement.attribute( QString::fromLatin1( "set" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ else if( filterElement.tagName() == QString::fromLatin1( "raise" ) )
+ {
+ filtre->raiseView = ( filterElement.attribute( QString::fromLatin1( "set" ), QString::fromLatin1( "0" ) ) == QString::fromLatin1( "1" ) );
+ }
+ }
+ filterNode = filterNode.nextSibling();
+ }
+// }
+ }
+ node = node.nextSibling();
+ }
+ filterListFile.close();
+}
+
+void HighlightConfig::save()
+{
+
+ QString fileName = locateLocal( "appdata", QString::fromLatin1( "highlight.xml" ) );
+
+ KSaveFile file( fileName );
+ if( file.status() == 0 )
+ {
+ QTextStream *stream = file.textStream();
+ stream->setEncoding( QTextStream::UnicodeUTF8 );
+
+ QString xml = QString::fromLatin1(
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE kopete-highlight-plugin>\n"
+ "<highlight-plugin>\n" );
+
+ // Save metafilter information.
+ QPtrListIterator<Filter> filtreIt( m_filters );
+ for( ; filtreIt.current(); ++filtreIt )
+ {
+ Filter *filtre = *filtreIt;
+ xml += QString::fromLatin1( " <filter>\n <display-name>" )
+ + QStyleSheet::escape(filtre->displayName)
+ + QString::fromLatin1( "</display-name>\n" );
+
+ xml += QString::fromLatin1(" <search caseSensitive=\"") + QString::number( static_cast<int>( filtre->caseSensitive ) ) +
+ QString::fromLatin1("\" regExp=\"") + QString::number( static_cast<int>( filtre->isRegExp ) ) +
+ QString::fromLatin1( "\">" ) + QStyleSheet::escape( filtre->search ) + QString::fromLatin1( "</search>\n" );
+
+ xml += QString::fromLatin1(" <BG set=\"") + QString::number( static_cast<int>( filtre->setBG ) ) +
+ QString::fromLatin1( "\">" ) + QStyleSheet::escape( filtre->BG.name() ) + QString::fromLatin1( "</BG>\n" );
+ xml += QString::fromLatin1(" <FG set=\"") + QString::number( static_cast<int>( filtre->setFG ) ) +
+ QString::fromLatin1( "\">" ) + QStyleSheet::escape( filtre->FG.name() ) + QString::fromLatin1( "</FG>\n" );
+
+ xml += QString::fromLatin1(" <importance set=\"") + QString::number( static_cast<int>( filtre->setImportance ) ) +
+ QString::fromLatin1( "\">" ) + QString::number( filtre->importance ) + QString::fromLatin1( "</importance>\n" );
+
+ xml += QString::fromLatin1(" <sound set=\"") + QString::number( static_cast<int>( filtre->playSound ) ) +
+ QString::fromLatin1( "\">" ) + QStyleSheet::escape( filtre->soundFN ) + QString::fromLatin1( "</sound>\n" );
+
+ xml += QString::fromLatin1(" <raise set=\"") + QString::number( static_cast<int>( filtre->raiseView ) ) +
+ QString::fromLatin1( "\"></raise>\n" );
+
+ xml += QString::fromLatin1( " </filter>\n" );
+ }
+
+ xml += QString::fromLatin1( "</highlight-plugin>\n" );
+
+ *stream << xml;
+ }
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/highlight/highlightconfig.h b/kopete/plugins/highlight/highlightconfig.h
new file mode 100644
index 00000000..35813403
--- /dev/null
+++ b/kopete/plugins/highlight/highlightconfig.h
@@ -0,0 +1,46 @@
+/*
+ highlightconfig.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef HIGHLIGHTCONFIG_H
+#define HIGHLIGHTCONFIG_H
+
+#include <qmap.h>
+#include <qstring.h>
+
+class Filter;
+
+class HighlightConfig
+{
+public:
+ HighlightConfig();
+ ~HighlightConfig();
+
+ void load();
+ void save();
+
+ QPtrList<Filter> filters() const;
+ void removeFilter (Filter *f);
+ void appendFilter (Filter *f);
+ Filter* newFilter();
+
+private:
+ QPtrList<Filter> m_filters;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/highlight/highlightplugin.cpp b/kopete/plugins/highlight/highlightplugin.cpp
new file mode 100644
index 00000000..2f1cbb43
--- /dev/null
+++ b/kopete/plugins/highlight/highlightplugin.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ highlightplugin.cpp - description
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qregexp.h>
+#include <kgenericfactory.h>
+#include <knotifyclient.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+
+#include "filter.h"
+#include "highlightplugin.h"
+#include "highlightconfig.h"
+
+typedef KGenericFactory<HighlightPlugin> HighlightPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_highlight, HighlightPluginFactory( "kopete_highlight" ) )
+
+HighlightPlugin::HighlightPlugin( QObject *parent, const char *name, const QStringList &/*args*/ )
+: Kopete::Plugin( HighlightPluginFactory::instance(), parent, name )
+{
+ if( !pluginStatic_ )
+ pluginStatic_=this;
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToDisplay( Kopete::Message & ) ), SLOT( slotIncomingMessage( Kopete::Message & ) ) );
+ connect ( this , SIGNAL( settingsChanged() ) , this , SLOT( slotSettingsChanged() ) );
+
+ m_config = new HighlightConfig;
+
+ m_config->load();
+}
+
+HighlightPlugin::~HighlightPlugin()
+{
+ pluginStatic_ = 0L;
+ delete m_config;
+}
+
+HighlightPlugin* HighlightPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+HighlightPlugin* HighlightPlugin::pluginStatic_ = 0L;
+
+
+void HighlightPlugin::slotIncomingMessage( Kopete::Message& msg )
+{
+ if(msg.direction() != Kopete::Message::Inbound)
+ return; // FIXME: highlighted internal/actions messages are not showed correctly in the chat window (bad style)
+ // but they should maybe be highlinghted if needed
+
+ QPtrList<Filter> filters=m_config->filters();
+ QPtrListIterator<Filter> it( filters );
+ Filter *f;
+ while ((f = it.current()) != 0 )
+ {
+ ++it;
+ if(f->isRegExp ?
+ msg.plainBody().contains(QRegExp(f->search , f->caseSensitive)) :
+ msg.plainBody().contains(f->search , f->caseSensitive) )
+ {
+ if(f->setBG)
+ msg.setBg(f->BG);
+ if(f->setFG)
+ msg.setFg(f->FG);
+ if(f->setImportance)
+ msg.setImportance((Kopete::Message::MessageImportance)f->importance);
+ if(f->playSound)
+ KNotifyClient::userEvent (QString::null, KNotifyClient::Sound, KNotifyClient::Default, f->soundFN );
+
+ if (f->raiseView &&
+ msg.manager() && msg.manager()->view()) {
+ KopeteView *theview = msg.manager()->view();
+ theview->raise();
+ }
+
+ break; //uh?
+ }
+ }
+}
+
+void HighlightPlugin::slotSettingsChanged()
+{
+ m_config->load();
+}
+
+
+
+#include "highlightplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/highlight/highlightplugin.h b/kopete/plugins/highlight/highlightplugin.h
new file mode 100644
index 00000000..0a421f55
--- /dev/null
+++ b/kopete/plugins/highlight/highlightplugin.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ highlightplugin.h - description
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HighlightPLUGIN_H
+#define HighlightPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+class QStringList;
+class QString;
+class QTimer;
+
+namespace Kopete { class Message; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class ChatSession; }
+
+class HighlightConfig;
+class Filter;
+
+/**
+ * @author Olivier Goffart
+ */
+
+class HighlightPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ static HighlightPlugin *plugin();
+
+ HighlightPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~HighlightPlugin();
+
+public slots:
+ void slotIncomingMessage( Kopete::Message& msg );
+ void slotSettingsChanged();
+
+
+private:
+ static HighlightPlugin* pluginStatic_;
+ HighlightConfig *m_config;
+};
+
+#endif
diff --git a/kopete/plugins/highlight/highlightpreferences.cpp b/kopete/plugins/highlight/highlightpreferences.cpp
new file mode 100644
index 00000000..9641d034
--- /dev/null
+++ b/kopete/plugins/highlight/highlightpreferences.cpp
@@ -0,0 +1,269 @@
+/***************************************************************************
+ highlightpreferences.cpp - description
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kparts/componentfactory.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kgenericfactory.h>
+#include <kcolorbutton.h>
+#include <kinputdialog.h>
+#include <kurlrequester.h>
+#include <kregexpeditorinterface.h>
+#include <kdebug.h>
+
+#include "filter.h"
+#include "highlightplugin.h"
+#include "highlightconfig.h"
+#include "highlightprefsbase.h"
+#include "highlightpreferences.h"
+
+typedef KGenericFactory<HighlightPreferences> HighlightPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_highlight, HighlightPreferencesFactory( "kcm_kopete_highlight" ) )
+
+HighlightPreferences::HighlightPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule(HighlightPreferencesFactory::instance(), parent, args)
+{
+ donttouch=true;
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new HighlightPrefsUI(this);
+ m_config = new HighlightConfig;
+
+ connect(preferencesDialog->m_list , SIGNAL(selectionChanged()) , this , SLOT(slotCurrentFilterChanged()));
+ connect(preferencesDialog->m_list , SIGNAL(doubleClicked ( QListViewItem *, const QPoint &, int )) , this , SLOT(slotRenameFilter()));
+ connect(preferencesDialog->m_add , SIGNAL(pressed()) , this , SLOT(slotAddFilter()));
+ connect(preferencesDialog->m_remove , SIGNAL(pressed()) , this , SLOT(slotRemoveFilter()));
+ connect(preferencesDialog->m_rename , SIGNAL(pressed()) , this , SLOT(slotRenameFilter()));
+ connect(preferencesDialog->m_editregexp , SIGNAL(pressed()) , this , SLOT(slotEditRegExp()));
+
+ //Maybe here i should use a slot per widget, but i am too lazy
+ connect(preferencesDialog->m_case , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_regexp , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_setImportance , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_setBG , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_setFG , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_search , SIGNAL(textChanged(const QString&)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_sound , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_soundFN , SIGNAL(textChanged(const QString&)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_raise , SIGNAL(stateChanged(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_search , SIGNAL(textChanged(const QString&)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_importance , SIGNAL(activated(int)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_FG , SIGNAL(changed(const QColor&)) , this , SLOT(slotSomethingHasChanged()));
+ connect(preferencesDialog->m_BG , SIGNAL(changed(const QColor&)) , this , SLOT(slotSomethingHasChanged()));
+
+ load();
+ donttouch=false;
+}
+
+HighlightPreferences::~HighlightPreferences()
+{
+ delete m_config;
+}
+
+void HighlightPreferences::load()
+{
+ m_config->load();
+ donttouch=true;
+ preferencesDialog->m_list->clear();
+ m_filterItems.clear();
+
+ QPtrList<Filter> filters=m_config->filters();
+ QPtrListIterator<Filter> it( filters );
+ Filter *f;
+ bool first=true;
+ while ( (f=it.current()) != 0 )
+ {
+ ++it;
+ QListViewItem* lvi= new QListViewItem(preferencesDialog->m_list);
+ lvi->setText(0,f->displayName );
+ m_filterItems.insert(lvi,f);
+ if(first)
+ preferencesDialog->m_list->setSelected(lvi, true);
+ first=false;
+ }
+ donttouch=false;
+ emit KCModule::changed(false);
+}
+
+void HighlightPreferences::save()
+{
+ m_config->save();
+ emit KCModule::changed(false);
+}
+
+
+void HighlightPreferences::slotCurrentFilterChanged()
+{
+ donttouch=true;
+ Filter *current;
+ if(!preferencesDialog->m_list->selectedItem() || !(current=m_filterItems[preferencesDialog->m_list->selectedItem()]))
+ {
+ preferencesDialog->m_search->setEnabled(false);
+ preferencesDialog->m_case->setEnabled(false);
+ preferencesDialog->m_regexp->setEnabled(false);
+ preferencesDialog->m_importance->setEnabled(false);
+ preferencesDialog->m_setImportance->setEnabled(false);
+ preferencesDialog->m_BG->setEnabled(false);
+ preferencesDialog->m_setBG->setEnabled(false);
+ preferencesDialog->m_FG->setEnabled(false);
+ preferencesDialog->m_setFG->setEnabled(false);
+ preferencesDialog->m_soundFN->setEnabled(false);
+ preferencesDialog->m_sound->setEnabled(false);
+ preferencesDialog->m_raise->setEnabled(false);
+ preferencesDialog->m_editregexp->setEnabled(false);
+ preferencesDialog->m_rename->setEnabled(false);
+ preferencesDialog->m_remove->setEnabled(false);
+ donttouch=false;
+ return;
+ }
+
+ preferencesDialog->m_rename->setEnabled(true);
+ preferencesDialog->m_remove->setEnabled(true);
+
+ preferencesDialog->m_search->setEnabled(true);
+ preferencesDialog->m_case->setEnabled(true);
+ preferencesDialog->m_regexp->setEnabled(true);
+ preferencesDialog->m_setImportance->setEnabled(true);
+ preferencesDialog->m_setBG->setEnabled(true);
+ preferencesDialog->m_setFG->setEnabled(true);
+ preferencesDialog->m_sound->setEnabled(true);
+ preferencesDialog->m_raise->setEnabled(true);
+
+
+ preferencesDialog->m_search->setText(current->search);
+ preferencesDialog->m_case->setChecked(current->caseSensitive);
+ preferencesDialog->m_regexp->setChecked(current->isRegExp);
+ preferencesDialog->m_editregexp->setEnabled(current->isRegExp);
+ preferencesDialog->m_importance->setCurrentItem(current->importance);
+ preferencesDialog->m_setImportance->setChecked(current->setImportance);
+ preferencesDialog->m_importance->setEnabled(current->setImportance);
+ preferencesDialog->m_BG->setColor(current->BG);
+ preferencesDialog->m_setBG->setChecked(current->setBG);
+ preferencesDialog->m_BG->setEnabled(current->setBG);
+ preferencesDialog->m_FG->setColor(current->FG);
+ preferencesDialog->m_setFG->setChecked(current->setFG);
+ preferencesDialog->m_FG->setEnabled(current->setFG);
+ preferencesDialog->m_soundFN->setURL(current->soundFN);
+ preferencesDialog->m_sound->setChecked(current->playSound);
+ preferencesDialog->m_raise->setChecked(current->raiseView);
+ preferencesDialog->m_soundFN->setEnabled(current->playSound);
+
+ donttouch=false;
+}
+
+void HighlightPreferences::slotAddFilter()
+{
+ Filter *filtre=m_config->newFilter();
+ QListViewItem* lvi= new QListViewItem(preferencesDialog->m_list);
+ lvi->setText(0,filtre->displayName );
+ m_filterItems.insert(lvi,filtre);
+ preferencesDialog->m_list->setSelected(lvi, true);
+}
+
+void HighlightPreferences::slotRemoveFilter()
+{
+ QListViewItem *lvi=preferencesDialog->m_list->selectedItem();
+ if(!lvi)
+ return;
+ Filter *current=m_filterItems[lvi];
+ if(!current)
+ return;
+
+ m_filterItems.remove(lvi);
+ delete lvi;
+ m_config->removeFilter(current);
+ emit KCModule::changed(true);
+}
+
+void HighlightPreferences::slotRenameFilter()
+{
+ QListViewItem *lvi=preferencesDialog->m_list->selectedItem();
+ if(!lvi)
+ return;
+ Filter *current=m_filterItems[lvi];
+ if(!current)
+ return;
+
+ bool ok;
+ QString newname = KInputDialog::getText(
+ i18n( "Rename Filter" ), i18n( "Please enter the new name for the filter:" ), current->displayName, &ok );
+ if( !ok )
+ return;
+ current->displayName=newname;
+ lvi->setText(0,newname);
+ emit KCModule::changed(true);
+}
+
+
+void HighlightPreferences::slotSomethingHasChanged()
+{
+ Filter *current;
+ if(donttouch || !preferencesDialog->m_list->selectedItem() || !(current=m_filterItems[preferencesDialog->m_list->selectedItem()]))
+ return;
+
+ current->search=preferencesDialog->m_search->text();
+ current->caseSensitive=preferencesDialog->m_case->isChecked();
+ current->isRegExp=preferencesDialog->m_regexp->isChecked();
+ preferencesDialog->m_editregexp->setEnabled(current->isRegExp);
+ current->importance=preferencesDialog->m_importance->currentItem();
+ current->setImportance=preferencesDialog->m_setImportance->isChecked();
+ preferencesDialog->m_importance->setEnabled(current->setImportance);
+ current->BG=preferencesDialog->m_BG->color();
+ current->setBG=preferencesDialog->m_setBG->isChecked();
+ preferencesDialog->m_BG->setEnabled(current->setBG);
+ current->FG=preferencesDialog->m_FG->color();
+ current->setFG=preferencesDialog->m_setFG->isChecked();
+ preferencesDialog->m_FG->setEnabled(current->setFG);
+ current->soundFN=preferencesDialog->m_soundFN->url();
+ current->playSound=preferencesDialog->m_sound->isChecked();
+ preferencesDialog->m_soundFN->setEnabled(current->playSound);
+ current->raiseView=preferencesDialog->m_raise->isChecked();
+
+ emit KCModule::changed(true);
+}
+
+void HighlightPreferences::slotEditRegExp()
+{
+ QDialog *editorDialog = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor" );
+ if ( editorDialog )
+ {
+ // kdeutils was installed, so the dialog was found fetch the editor interface
+ KRegExpEditorInterface *editor = static_cast<KRegExpEditorInterface *>( editorDialog->qt_cast( "KRegExpEditorInterface" ) );
+ Q_ASSERT( editor ); // This should not fail!
+ // now use the editor.
+ editor->setRegExp(preferencesDialog->m_search->text());
+
+ // Finally exec the dialog
+ if(editorDialog->exec() == QDialog::Accepted )
+ {
+ preferencesDialog->m_search->setText(editor->regExp());
+ }
+
+ }
+ else
+ {
+ // Don't offer the dialog.
+ }
+}
+
+#include "highlightpreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/highlight/highlightpreferences.h b/kopete/plugins/highlight/highlightpreferences.h
new file mode 100644
index 00000000..a2c7e31b
--- /dev/null
+++ b/kopete/plugins/highlight/highlightpreferences.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ highlightpreferences.h - description
+ -------------------
+ begin : mar 14 2003
+ copyright : (C) 2003 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HighlightPREFERENCES_H
+#define HighlightPREFERENCES_H
+
+#include <kcmodule.h>
+#include <qstring.h>
+
+class HighlightPrefsUI;
+class Filter;
+class QListViewItem;
+
+/**
+ *@author Olivier Goffart
+ */
+
+class HighlightPreferences : public KCModule {
+ Q_OBJECT
+public:
+
+ HighlightPreferences(QWidget *parent = 0, const char* name = 0, const QStringList &args = QStringList());
+ ~HighlightPreferences();
+
+ virtual void save();
+ virtual void load();
+
+private:
+ HighlightPrefsUI *preferencesDialog;
+ HighlightConfig *m_config;
+ QMap <QListViewItem*,Filter*> m_filterItems;
+
+ bool donttouch;
+
+private slots:
+ void slotCurrentFilterChanged();
+ void slotAddFilter();
+ void slotRemoveFilter();
+ void slotRenameFilter();
+ void slotSomethingHasChanged();
+ void slotEditRegExp();
+};
+
+#endif
diff --git a/kopete/plugins/highlight/highlightprefsbase.ui b/kopete/plugins/highlight/highlightprefsbase.ui
new file mode 100644
index 00000000..b8150c56
--- /dev/null
+++ b/kopete/plugins/highlight/highlightprefsbase.ui
@@ -0,0 +1,466 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>HighlightPrefsUI</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HighlighPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>513</width>
+ <height>504</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>HighlighPrefsUI</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Available Filters</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>m_add</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>m_remove</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_rename</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Rename...</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="3">
+ <column>
+ <property name="text">
+ <string>Filters</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="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Criteria</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>If the message contains:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>m_search</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_regexp</cstring>
+ </property>
+ <property name="text">
+ <string>Regular expression</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_editregexp</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Edit...</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>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_case</cstring>
+ </property>
+ <property name="text">
+ <string>Case sensitive</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Action</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_setImportance</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Set the message importance to:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>Low</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Normal</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Highlight</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_importance</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_setBG</cstring>
+ </property>
+ <property name="text">
+ <string>Change the background color to:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>m_BG</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_setFG</cstring>
+ </property>
+ <property name="text">
+ <string>Change the foreground color to:</string>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>m_FG</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_sound</cstring>
+ </property>
+ <property name="text">
+ <string>Play a sound:</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_soundFN</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_raise</cstring>
+ </property>
+ <property name="text">
+ <string>Raise window</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>m_list</tabstop>
+ <tabstop>m_add</tabstop>
+ <tabstop>m_remove</tabstop>
+ <tabstop>m_rename</tabstop>
+ <tabstop>m_search</tabstop>
+ <tabstop>m_regexp</tabstop>
+ <tabstop>m_editregexp</tabstop>
+ <tabstop>m_case</tabstop>
+ <tabstop>m_setImportance</tabstop>
+ <tabstop>m_importance</tabstop>
+ <tabstop>m_setBG</tabstop>
+ <tabstop>m_BG</tabstop>
+ <tabstop>m_setFG</tabstop>
+ <tabstop>m_FG</tabstop>
+ <tabstop>m_sound</tabstop>
+ <tabstop>m_soundFN</tabstop>
+ <tabstop>m_raise</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/highlight/icons/Makefile.am b/kopete/plugins/highlight/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/plugins/highlight/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/plugins/highlight/icons/cr32-app-highlight.png b/kopete/plugins/highlight/icons/cr32-app-highlight.png
new file mode 100644
index 00000000..54f6736d
--- /dev/null
+++ b/kopete/plugins/highlight/icons/cr32-app-highlight.png
Binary files differ
diff --git a/kopete/plugins/highlight/kopete_highlight.desktop b/kopete/plugins/highlight/kopete_highlight.desktop
new file mode 100644
index 00000000..0d43c122
--- /dev/null
+++ b/kopete/plugins/highlight/kopete_highlight.desktop
@@ -0,0 +1,129 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=highlight
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_highlight
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_highlight
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Highlight
+Name[ar]=تمييز
+Name[bg]=ОткроÑване
+Name[bn]=গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£
+Name[br]=Splannadur
+Name[bs]=Isticanje
+Name[ca]=Ressaltat
+Name[cs]=Zvýraznění
+Name[cy]=Amlygu
+Name[da]=Fremhæv
+Name[de]=Hervorhebung
+Name[el]=Τονισμός
+Name[eo]=Lumaĵo
+Name[es]=Resaltar
+Name[et]=Esiletõstmine
+Name[eu]=Nabarmendu
+Name[fa]=مشخص
+Name[fi]=Korostus
+Name[fr]=Surlignement
+Name[ga]=Aibhsiú
+Name[gl]=Resaltar
+Name[he]=מודגש
+Name[hi]=उभारें
+Name[hr]=Isticanje
+Name[hu]=Kiemelés
+Name[is]=Merkja
+Name[it]=Evidenziazione
+Name[ja]=強調
+Name[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული
+Name[kk]=Ерекше
+Name[km]=សំážáž¶áž“់
+Name[lt]=Paryškinti
+Name[mk]=ОÑветлување
+Name[nb]=Marker
+Name[nds]=Rutheven
+Name[ne]=हाइलाइट
+Name[nl]=Aanwijzen
+Name[nn]=Marker
+Name[pa]=ਉਘਾੜਨ
+Name[pl]=Podświetlenie
+Name[pt]=Realce
+Name[pt_BR]=Destaque
+Name[ro]=Evidenţiat
+Name[ru]=Выделение
+Name[se]=Merke
+Name[sk]=Zvýrazniť
+Name[sl]=Poudarjeno sporoÄilo
+Name[sr]=ИÑтицање
+Name[sr@Latn]=Isticanje
+Name[sv]=Markera
+Name[ta]=à®®à¯à®©à¯ˆà®ªà¯à®ªà¯à®±à¯à®¤à¯à®¤à®²à¯
+Name[tg]=Равшаннамоӣ
+Name[tr]=Vurgu
+Name[uk]=ПідÑвічуваннÑ
+Name[wa]=E sorbiyance
+Name[zh_CN]=çªå‡ºæ˜¾ç¤º
+Name[zh_HK]=加強顯示
+Name[zh_TW]=高亮度
+Comment=Highlight messages
+Comment[ar]=الرسائل المميزة
+Comment[bg]=ОткроÑване на ÑъобщениÑ
+Comment[bn]=বারà§à¦¤à¦¾ পà§à¦°à¦œà§à¦œà§à¦¬à¦²à¦¨
+Comment[bs]=Istakni poruke
+Comment[ca]=Missatges a ressaltar
+Comment[cs]=Zvýraznit zprávy
+Comment[cy]=Amlygu negeseuon
+Comment[da]=Fremhæv beskeder
+Comment[de]=Hervorhebung von Nachrichten
+Comment[el]=Τονισμός μηνυμάτων
+Comment[eo]=Elstarigi mesaÄojn
+Comment[es]=Resaltar mensajes
+Comment[et]=Sõnumite esiletõstmine
+Comment[eu]=Nabarmendu mezuak
+Comment[fa]=پیامهای مشخص
+Comment[fi]=Korosta viestit
+Comment[fr]=Surligner les messages
+Comment[ga]=Aibhsigh teachtaireachtaí
+Comment[gl]=Resaltar mensaxes
+Comment[he]=מדגיש הודעות
+Comment[hi]=संदेशों को उभारें
+Comment[hr]=Isticanje poruka
+Comment[hu]=Szövegkiemelés
+Comment[is]=Merkja skeyti
+Comment[it]=Evidenzia i messaggi
+Comment[ja]=メッセージを強調
+Comment[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული შეტყáƒáƒ‘ინებáƒ
+Comment[kk]=Хабарларды боÑулау
+Comment[km]=សារ​សំážáž¶áž“់
+Comment[lt]=Paryškinti žinutes
+Comment[mk]=Обележете пораки
+Comment[nb]=Marker meldinger
+Comment[nds]=Narichten rutheven
+Comment[ne]=सनà¥à¤¦à¥‡à¤¶ हाइलाइट गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Laat berichten oplichten
+Comment[nn]=Marker meldingar
+Comment[pl]=Podświetlanie wiadomości
+Comment[pt]=Realçar as mensagens
+Comment[pt_BR]=Destaca mensagens
+Comment[ro]=Evidenţiază mesajele
+Comment[ru]=ПодÑвечивание Ñообщений
+Comment[se]=Merke dieđuid
+Comment[sk]=Zvýraznenie správ
+Comment[sl]=Poudarjanje sporoÄil
+Comment[sr]=ИÑтицање порука
+Comment[sr@Latn]=Isticanje poruka
+Comment[sv]=Markera meddelanden
+Comment[ta]=தகவல௠தனிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à®²à¯
+Comment[tg]=РавшанÑозии пайёмҳо
+Comment[tr]=Vurgulanmış mesajlar
+Comment[uk]=ПідÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ
+Comment[wa]=Mete les messaedjes e sorbriyance
+Comment[zh_CN]=çªå‡ºæ˜¾ç¤ºæ¶ˆæ¯
+Comment[zh_HK]=將訊æ¯åŠ å¼·é¡¯ç¤º
+Comment[zh_TW]=高亮度訊æ¯
diff --git a/kopete/plugins/highlight/kopete_highlight_config.desktop b/kopete/plugins/highlight/kopete_highlight_config.desktop
new file mode 100644
index 00000000..ae29a289
--- /dev/null
+++ b/kopete/plugins/highlight/kopete_highlight_config.desktop
@@ -0,0 +1,125 @@
+[Desktop Entry]
+Icon=highlight
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_highlight
+X-KDE-FactoryName=HighlightConfigFactory
+X-KDE-ParentApp=kopete_highlight
+X-KDE-ParentComponents=kopete_highlight
+
+Name=Highlight
+Name[ar]=تمييز
+Name[bg]=ОткроÑване
+Name[bn]=গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£
+Name[br]=Splannadur
+Name[bs]=Isticanje
+Name[ca]=Ressaltat
+Name[cs]=Zvýraznění
+Name[cy]=Amlygu
+Name[da]=Fremhæv
+Name[de]=Hervorhebung
+Name[el]=Τονισμός
+Name[eo]=Lumaĵo
+Name[es]=Resaltar
+Name[et]=Esiletõstmine
+Name[eu]=Nabarmendu
+Name[fa]=مشخص
+Name[fi]=Korostus
+Name[fr]=Surlignement
+Name[ga]=Aibhsiú
+Name[gl]=Resaltar
+Name[he]=מודגש
+Name[hi]=उभारें
+Name[hr]=Isticanje
+Name[hu]=Kiemelés
+Name[is]=Merkja
+Name[it]=Evidenziazione
+Name[ja]=強調
+Name[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული
+Name[kk]=Ерекше
+Name[km]=សំážáž¶áž“់
+Name[lt]=Paryškinti
+Name[mk]=ОÑветлување
+Name[nb]=Marker
+Name[nds]=Rutheven
+Name[ne]=हाइलाइट
+Name[nl]=Aanwijzen
+Name[nn]=Marker
+Name[pa]=ਉਘਾੜਨ
+Name[pl]=Podświetlenie
+Name[pt]=Realce
+Name[pt_BR]=Destaque
+Name[ro]=Evidenţiat
+Name[ru]=Выделение
+Name[se]=Merke
+Name[sk]=Zvýrazniť
+Name[sl]=Poudarjeno sporoÄilo
+Name[sr]=ИÑтицање
+Name[sr@Latn]=Isticanje
+Name[sv]=Markera
+Name[ta]=à®®à¯à®©à¯ˆà®ªà¯à®ªà¯à®±à¯à®¤à¯à®¤à®²à¯
+Name[tg]=Равшаннамоӣ
+Name[tr]=Vurgu
+Name[uk]=ПідÑвічуваннÑ
+Name[wa]=E sorbiyance
+Name[zh_CN]=çªå‡ºæ˜¾ç¤º
+Name[zh_HK]=加強顯示
+Name[zh_TW]=高亮度
+Comment=Highlights text based on filters
+Comment[ar]=تميز النصوص بناءا على التصÙية
+Comment[be]=ПадÑвечвае Ñ‚ÑкÑÑ‚ згодна з фільтрамі
+Comment[bg]=ОткроÑване на текÑÑ‚ в ÑъобщениÑта на базата на ключови думи
+Comment[bn]=ফিলà§à¦Ÿà¦¾à¦°à§‡à¦° ওপর ভিতà§à¦¤à¦¿ করে টেকà§à¦¸à¦Ÿ পà§à¦°à¦œà§à¦œà§à¦¬à¦² করে
+Comment[bs]=IstiÄe poruke na osnovu filtera
+Comment[ca]=Ressalta el text basant-se en filtres
+Comment[cs]=Zvýraznit text podle filtrů
+Comment[cy]=Amlygu testun ar sail hidlau
+Comment[da]=Fremhæv tekst baseret på filtre
+Comment[de]=Texthervorhebung mit Hilfe von Filtern
+Comment[el]=Τονίζει το κείμενο βασισμένο σε φίλτÏα
+Comment[es]=Realza el texto basándose en filtros
+Comment[et]=Teksti esiletõstmine filtrite põhjal
+Comment[eu]=Iragazkietan oinarritutako testua nabarmentzen du
+Comment[fa]=متن را بر اساس پالایه‌ها مشخص می‌کند
+Comment[fi]=Korostaa tekstin suotimien perusteella
+Comment[fr]=Surligne les messages qui répondent à certains critères
+Comment[ga]=Aibhsíonn seo téacs de réir scagairí
+Comment[gl]=Resalta texto usando filtros
+Comment[he]=מדגיש ×ž×™×œ×™× ×¢×œ פי מסנני×
+Comment[hi]=फ़िलà¥à¤Ÿà¤° आधारित पाठ उभारें
+Comment[hr]=Isticanje poruka na osnovu filtera
+Comment[hu]=Szövegkiemelés üzenetekben megadott szempontok szerint
+Comment[is]=Merkir texta með tilliti til síunar
+Comment[it]=Evidenzia i testi attraverso dei filtri
+Comment[ja]=æ¡ä»¶ã«å¾“ã£ã¦ãƒ†ã‚­ã‚¹ãƒˆã‚’強調
+Comment[ka]=ტექსტის მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბáƒáƒ¡ áƒáƒ™áƒ”თებს ფილტრების მეშვეáƒáƒ‘ით
+Comment[kk]=Мәтінді Ñүзгілеп боÑулау
+Comment[km]=សារ​សំážáž¶áž“់​ដែល​ផ្អែក​លើ​ážáž˜áŸ’ážšáž„
+Comment[lt]=Paryškinamas tekstas, atsižvelgiant į filtrus
+Comment[mk]=Го обележува текÑтот базирано на филтри
+Comment[nb]=Marker meldinger ved bruk av filtere
+Comment[nds]=Filterbaseert Rutheven vun Text
+Comment[ne]=फिलà¥à¤Ÿà¤°à¤®à¤¾ आधारित पाठ हाइलाइट गरà¥à¤¦à¤›
+Comment[nl]=Laat tekst oplichten gebaseerd op filters
+Comment[nn]=Marker meldingar ved bruk av filter
+Comment[pl]=Podświetla tekst na podstawie ustawionych filtrów
+Comment[pt]=Realça o texto com base em filtros
+Comment[pt_BR]=Destaca o texto baseado em filtros
+Comment[ro]=Evidenţiază mesajele pe baza unor filtre
+Comment[ru]=ПодÑвечивание текÑта оÑновываетÑÑ Ð½Ð° фильтрах
+Comment[se]=Merke teavstta silliid bokte
+Comment[sk]=Zvýrazňuje text pomocou filtrov
+Comment[sl]=Poudarjanje besedila glede na filtre
+Comment[sr]=ИÑтицање порука на оÑнову филтера
+Comment[sr@Latn]=Isticanje poruka na osnovu filtera
+Comment[sv]=Markerar text baserat på filter
+Comment[ta]=வடிகடà¯à®Ÿà®¿à®¯à¯ˆ பொரà¯à®¤à¯à®¤à¯ உரையை தனிபà¯à®ªà®Ÿà¯à®¤à¯à®¤à¯
+Comment[tg]=РавшанÑозии матн дар аÑоÑи полоÑгарҳо
+Comment[tr]=Süzgeçlerde metin temelli vurgular
+Comment[uk]=ПідÑÐ²Ñ–Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚ÐµÐºÑту базуєтьÑÑ Ð½Ð° фільтрах
+Comment[wa]=Mete li tecse e sorbriyance d' après les passetes
+Comment[zh_CN]=æ ¹æ®è¿‡æ»¤å™¨çªå‡ºæ˜¾ç¤ºæ–‡å­—
+Comment[zh_HK]=根據éŽæ¿¾å™¨å°‡è¨Šæ¯åŠ å¼·é¡¯ç¤º
+Comment[zh_TW]=基於éŽæ¿¾å™¨çš„高亮度訊æ¯
diff --git a/kopete/plugins/history/Makefile.am b/kopete/plugins/history/Makefile.am
new file mode 100644
index 00000000..765e5197
--- /dev/null
+++ b/kopete/plugins/history/Makefile.am
@@ -0,0 +1,26 @@
+METASOURCES = AUTO
+
+INCLUDES = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_history.la kcm_kopete_history.la
+
+kopete_history_la_SOURCES = historyplugin.cpp historydialog.cpp historyviewer.ui\
+ historylogger.cpp converter.cpp historyguiclient.cpp historyconfig.kcfgc
+
+kopete_history_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_history_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_history_la_SOURCES = historyprefsui.ui historypreferences.cpp historyconfig.kcfgc
+kcm_kopete_history_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_history_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_history.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_history
+mydata_DATA = historyui.rc historychatui.rc
+
+kcm_DATA = kopete_history_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+kde_kcfg_DATA = historyconfig.kcfg
diff --git a/kopete/plugins/history/converter.cpp b/kopete/plugins/history/converter.cpp
new file mode 100644
index 00000000..22f662bc
--- /dev/null
+++ b/kopete/plugins/history/converter.cpp
@@ -0,0 +1,341 @@
+//Olivier Goffart <ogoffart @ kde.org>
+// 2003 06 26
+
+#include "historyplugin.h" //just needed because we are a member of this class
+ // we don't use any history function here
+
+/**-----------------------------------------------------------
+ * CONVERTER from the old kopete history.
+ * it port history from kopete 0.6, 0.5 and above the actual
+ * this should be placed in a perl script handled by KConf_update
+ * but i need to acess to some info i don't have with perl, like
+ * the accountId, to know each protocol id, and more
+ *-----------------------------------------------------------*/
+
+#include "kopetepluginmanager.h"
+#include "kopeteaccount.h"
+#include "kopeteaccountmanager.h"
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+#include "kopeteprotocol.h"
+#include "kopeteuiglobal.h"
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kprogress.h>
+#include <kapplication.h>
+#include <ksavefile.h>
+#include <qdir.h>
+#include <qdom.h>
+#include <qregexp.h>
+
+#define CBUFLENGTH 512 // buffer length for fgets()
+
+void HistoryPlugin::convertOldHistory()
+{
+ bool deleteFiles= KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n( "Would you like to remove old history files?" ) , i18n( "History Converter" ), KStdGuiItem::del(), i18n("Keep") ) == KMessageBox::Yes;
+
+ KProgressDialog *progressDlg=new KProgressDialog(Kopete::UI::Global::mainWidget() , "history_progress_dlg" , i18n( "History converter" ) ,
+ QString::null , true); //modal to make sure the user will not doing stupid things (we have a kapp->processEvents())
+ progressDlg->setAllowCancel(false); //because i am too lazy to allow to cancel
+
+
+ QString kopetedir=locateLocal( "data", QString::fromLatin1( "kopete"));
+ QDir d( kopetedir ); //d should point to ~/.kde/share/apps/kopete/
+
+ d.setFilter( QDir::Dirs );
+
+ const QFileInfoList *list = d.entryInfoList();
+ QFileInfoListIterator it( *list );
+ QFileInfo *fi;
+ while ( (fi = it.current()) != 0 )
+ {
+ QString protocolId;
+ QString accountId;
+
+ if( Kopete::Protocol *p = dynamic_cast<Kopete::Protocol *>( Kopete::PluginManager::self()->plugin( fi->fileName() ) ) )
+ {
+ protocolId=p->pluginId();
+ QDictIterator<Kopete::Account> it(Kopete::AccountManager::self()->accounts(p));
+ Kopete::Account *a = it.current();
+ if(a)
+ accountId=a->accountId();
+ }
+
+ if(accountId.isNull() || protocolId.isNull())
+ {
+ if(fi->fileName() == "MSNProtocol" || fi->fileName() == "msn_logs" )
+ {
+ protocolId="MSNProtocol";
+ KGlobal::config()->setGroup("MSN");
+ accountId=KGlobal::config()->readEntry( "UserID" );
+ }
+ else if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" )
+ {
+ protocolId="ICQProtocol";
+ KGlobal::config()->setGroup("ICQ");
+ accountId=KGlobal::config()->readEntry( "UIN" );
+ }
+ else if(fi->fileName() == "AIMProtocol" || fi->fileName() == "aim_logs" )
+ {
+ protocolId="AIMProtocol";
+ KGlobal::config()->setGroup("AIM");
+ accountId=KGlobal::config()->readEntry( "UserID" );
+ }
+ else if(fi->fileName() == "OscarProtocol" )
+ {
+ protocolId="AIMProtocol";
+ KGlobal::config()->setGroup("OSCAR");
+ accountId=KGlobal::config()->readEntry( "UserID" );
+ }
+ else if(fi->fileName() == "JabberProtocol" || fi->fileName() == "jabber_logs")
+ {
+ protocolId="JabberProtocol";
+ KGlobal::config()->setGroup("Jabber");
+ accountId=KGlobal::config()->readEntry( "UserID" );
+ }
+ //TODO: gadu, wp
+ }
+
+ if(!protocolId.isEmpty() || !accountId.isEmpty())
+ {
+ QDir d2( fi->absFilePath() );
+ d2.setFilter( QDir::Files );
+ d2.setNameFilter("*.log");
+ const QFileInfoList *list = d2.entryInfoList();
+ QFileInfoListIterator it2( *list );
+ QFileInfo *fi2;
+
+ progressDlg->progressBar()->reset();
+ progressDlg->progressBar()->setTotalSteps(d2.count());
+ progressDlg->setLabel(i18n("Parsing old history in %1").arg(fi->fileName()));
+ progressDlg->show(); //if it was not already showed...
+
+ while ( (fi2 = it2.current()) != 0 )
+ {
+ //we assume that all "-" are dots. (like in hotmail.com)
+ QString contactId=fi2->fileName().replace(".log" , QString::null).replace("-" , ".");
+
+ if(!contactId.isEmpty() )
+ {
+ progressDlg->setLabel(i18n("Parsing old history in %1:\n%2").arg(fi->fileName()).arg(contactId));
+ kapp->processEvents(0); //make sure the text is updated in the progressDlg
+
+ int month=0;
+ int year=0;
+ QDomDocument doc;
+ QDomElement docElem;
+
+ QDomElement msgelement;
+ QDomNode node;
+ QDomDocument xmllist;
+ Kopete::Message::MessageDirection dir;
+ QString body, date, nick;
+ QString buffer, msgBlock;
+ char cbuf[CBUFLENGTH]; // buffer for the log file
+
+ QString logFileName = fi2->absFilePath();
+
+ // open the file
+ FILE *f = fopen(QFile::encodeName(logFileName), "r");
+
+ // create a new <message> block
+ while ( ! feof( f ) )
+ {
+ fgets(cbuf, CBUFLENGTH, f);
+ buffer = QString::fromUtf8(cbuf);
+
+ while ( strchr(cbuf, '\n') == NULL && !feof(f) )
+ {
+ fgets( cbuf, CBUFLENGTH, f );
+ buffer += QString::fromUtf8(cbuf);
+ }
+
+ if( buffer.startsWith( QString::fromLatin1( "<message " ) ) )
+ {
+ msgBlock = buffer;
+
+ // find the end of the message block
+ while( !feof( f ) && buffer != QString::fromLatin1( "</message>\n" ) /*strcmp("</message>\n", cbuf )*/ )
+ {
+ fgets(cbuf, CBUFLENGTH, f);
+ buffer = QString::fromUtf8(cbuf);
+
+ while ( strchr(cbuf, '\n') == NULL && !feof(f) )
+ {
+ fgets( cbuf, CBUFLENGTH, f );
+ buffer += QString::fromUtf8(cbuf);
+ }
+ msgBlock.append(buffer);
+ }
+
+ // now let's work on this new block
+ xmllist.setContent(msgBlock, false);
+ msgelement = xmllist.documentElement();
+ node = msgelement.firstChild();
+
+ if( msgelement.attribute( QString::fromLatin1( "direction" ) ) == QString::fromLatin1( "inbound" ) )
+ dir = Kopete::Message::Inbound;
+ else
+ dir = Kopete::Message::Outbound;
+
+ // Read all the elements.
+ QString tagname;
+ QDomElement element;
+
+ while ( ! node.isNull() )
+ {
+ if ( node.isElement() )
+ {
+ element = node.toElement();
+ tagname = element.tagName();
+
+ if( tagname == QString::fromLatin1( "srcnick" ) )
+ nick = element.text();
+
+ else if( tagname == QString::fromLatin1( "date" ) )
+ date = element.text();
+ else if( tagname == QString::fromLatin1( "body" ) )
+ body = element.text().stripWhiteSpace();
+ }
+
+ node = node.nextSibling();
+ }
+ //FIXME!! The date in logs writed with kopete running with QT 3.0 is Localised.
+ // so QT can't parse it correctly.
+ QDateTime dt=QDateTime::fromString(date);
+ if(dt.date().month() != month || dt.date().year() != year)
+ {
+ if(!docElem.isNull())
+ {
+ QDate date(year,month,1);
+ QString name = protocolId.replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ QString::fromLatin1( "/" ) +
+ contactId.replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ date.toString(".yyyyMM");
+ KSaveFile file( locateLocal( "data", QString::fromLatin1( "kopete/logs/" ) + name+ QString::fromLatin1( ".xml" ) ) );
+ if( file.status() == 0 )
+ {
+ QTextStream *stream = file.textStream();
+ //stream->setEncoding( QTextStream::UnicodeUTF8 ); //???? oui ou non?
+ doc.save( *stream , 1 );
+ file.close();
+ }
+ }
+
+
+ month=dt.date().month();
+ year=dt.date().year();
+ docElem=QDomElement();
+ }
+
+ if(docElem.isNull())
+ {
+ doc=QDomDocument("Kopete-History");
+ docElem= doc.createElement( "kopete-history" );
+ docElem.setAttribute ( "version" , "0.7" );
+ doc.appendChild( docElem );
+ QDomElement headElem = doc.createElement( "head" );
+ docElem.appendChild( headElem );
+ QDomElement dateElem = doc.createElement( "date" );
+ dateElem.setAttribute( "year", QString::number(year) );
+ dateElem.setAttribute( "month", QString::number(month) );
+ headElem.appendChild(dateElem);
+ QDomElement myselfElem = doc.createElement( "contact" );
+ myselfElem.setAttribute( "type", "myself" );
+ myselfElem.setAttribute( "contactId", accountId );
+ headElem.appendChild(myselfElem);
+ QDomElement contactElem = doc.createElement( "contact" );
+ contactElem.setAttribute( "contactId", contactId );
+ headElem.appendChild(contactElem);
+ QDomElement importElem = doc.createElement( "imported" );
+ importElem.setAttribute( "from", fi->fileName() );
+ importElem.setAttribute( "date", QDateTime::currentDateTime().toString() );
+ headElem.appendChild(importElem);
+ }
+ QDomElement msgElem = doc.createElement( "msg" );
+ msgElem.setAttribute( "in", dir==Kopete::Message::Outbound ? "0" : "1" );
+ msgElem.setAttribute( "from", dir==Kopete::Message::Outbound ? accountId : contactId );
+ msgElem.setAttribute( "nick", nick ); //do we have to set this?
+ msgElem.setAttribute( "time", QString::number(dt.date().day()) + " " + QString::number(dt.time().hour()) + ":" + QString::number(dt.time().minute()) );
+ QDomText msgNode = doc.createTextNode( body.stripWhiteSpace() );
+ docElem.appendChild( msgElem );
+ msgElem.appendChild( msgNode );
+ }
+ }
+
+ fclose( f );
+ if(deleteFiles)
+ d2.remove(fi2->fileName() , false);
+
+ if(!docElem.isNull())
+ {
+ QDate date(year,month,1);
+ QString name = protocolId.replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ QString::fromLatin1( "/" ) +
+ contactId.replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ date.toString(".yyyyMM");
+ KSaveFile file( locateLocal( "data", QString::fromLatin1( "kopete/logs/" ) + name+ QString::fromLatin1( ".xml" ) ) );
+ if( file.status() == 0 )
+ {
+ QTextStream *stream = file.textStream();
+ //stream->setEncoding( QTextStream::UnicodeUTF8 ); //???? oui ou non?
+ doc.save( *stream ,1 );
+ file.close();
+ }
+ }
+
+ }
+ progressDlg->progressBar()->setProgress(progressDlg->progressBar()->progress()+1);
+ ++it2;
+ }
+ }
+ ++it;
+ }
+ delete progressDlg;
+
+}
+
+
+bool HistoryPlugin::detectOldHistory()
+{
+ KGlobal::config()->setGroup("History Plugin");
+ QString version=KGlobal::config()->readEntry( "Version" ,"0.6" );
+
+ if(version != "0.6")
+ return false;
+
+
+ QDir d( locateLocal( "data", QString::fromLatin1( "kopete/logs")) );
+ d.setFilter( QDir::Dirs );
+ if(d.count() >= 3) // '.' and '..' are included
+ return false; //the new history already exists
+
+ QDir d2( locateLocal( "data", QString::fromLatin1( "kopete")) );
+ d2.setFilter( QDir::Dirs );
+ const QFileInfoList *list = d2.entryInfoList();
+ QFileInfoListIterator it( *list );
+ QFileInfo *fi;
+ while ( (fi = it.current()) != 0 )
+ {
+ if( dynamic_cast<Kopete::Protocol *>( Kopete::PluginManager::self()->plugin( fi->fileName() ) ) )
+ return true;
+
+ if(fi->fileName() == "MSNProtocol" || fi->fileName() == "msn_logs" )
+ return true;
+ else if(fi->fileName() == "ICQProtocol" || fi->fileName() == "icq_logs" )
+ return true;
+ else if(fi->fileName() == "AIMProtocol" || fi->fileName() == "aim_logs" )
+ return true;
+ else if(fi->fileName() == "OscarProtocol" )
+ return true;
+ else if(fi->fileName() == "JabberProtocol" || fi->fileName() == "jabber_logs")
+ return true;
+ ++it;
+ }
+ return false;
+}
diff --git a/kopete/plugins/history/historychatui.rc b/kopete/plugins/history/historychatui.rc
new file mode 100644
index 00000000..2f49392f
--- /dev/null
+++ b/kopete/plugins/history/historychatui.rc
@@ -0,0 +1,17 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="19" name="kopetechatwindow">
+ <MenuBar>
+ <Menu name="tools" >
+ <text>&amp;Tools</text>
+ <Action name="historyPrevious" />
+ <Action name="historyNext" />
+ <Action name="historyLast" />
+ </Menu>
+ </MenuBar>
+
+ <ToolBar name="mainToolBar" fullWidth="true">
+ <Action name="historyPrevious" />
+ <Action name="historyNext" />
+ </ToolBar>
+
+</kpartgui>
diff --git a/kopete/plugins/history/historyconfig.kcfg b/kopete/plugins/history/historyconfig.kcfg
new file mode 100644
index 00000000..58e6c9d2
--- /dev/null
+++ b/kopete/plugins/history/historyconfig.kcfg
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author: Stefan Gehn -->
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kopeterc"/>
+
+ <group name="History Plugin">
+ <entry name="Auto_chatwindow" type="Bool">
+ <label>Show previous messages in new chats.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="Number_Auto_chatwindow" type="UInt">
+ <label>Number of messages to show.</label>
+ <default>7</default>
+ </entry>
+
+ <entry name="Number_ChatWindow" type="UInt">
+ <label>Number of messages per page</label>
+ <default>20</default>
+ </entry>
+
+ <entry name="History_color" type="Color">
+ <label>Color of messages</label>
+ <default>170, 170, 127</default>
+ </entry>
+
+ <entry name="BrowserStyle" type="Path">
+ <label>Style to use in history-browser.</label>
+ </entry>
+
+ </group>
+</kcfg>
diff --git a/kopete/plugins/history/historyconfig.kcfgc b/kopete/plugins/history/historyconfig.kcfgc
new file mode 100644
index 00000000..1e985622
--- /dev/null
+++ b/kopete/plugins/history/historyconfig.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=historyconfig.kcfg
+ClassName=HistoryConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
diff --git a/kopete/plugins/history/historydialog.cpp b/kopete/plugins/history/historydialog.cpp
new file mode 100644
index 00000000..4dd98fee
--- /dev/null
+++ b/kopete/plugins/history/historydialog.cpp
@@ -0,0 +1,613 @@
+/*
+ kopetehistorydialog.cpp - Kopete History Dialog
+
+ Copyright (c) 2002 by Richard Stellingwerff <remenic@linuxfromscratch.org>
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "historydialog.h"
+#include "historylogger.h"
+#include "historyviewer.h"
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetecontactlist.h"
+#include "kopeteprefs.h"
+
+#include <dom/dom_doc.h>
+#include <dom/dom_element.h>
+#include <dom/html_document.h>
+#include <dom/html_element.h>
+#include <khtml_part.h>
+#include <khtmlview.h>
+
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qheader.h>
+#include <qlabel.h>
+#include <qclipboard.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <krun.h>
+#include <kstandarddirs.h>
+#include <klistview.h>
+#include <klistviewsearchline.h>
+#include <kprogress.h>
+#include <kiconloader.h>
+#include <kcombobox.h>
+#include <kpopupmenu.h>
+#include <kstdaction.h>
+#include <kaction.h>
+
+class KListViewDateItem : public KListViewItem
+{
+public:
+ KListViewDateItem(KListView* parent, QDate date, Kopete::MetaContact *mc);
+ QDate date() { return mDate; }
+ Kopete::MetaContact *metaContact() { return mMetaContact; }
+
+public:
+ int compare(QListViewItem *i, int col, bool ascending) const;
+private:
+ QDate mDate;
+ Kopete::MetaContact *mMetaContact;
+};
+
+
+
+KListViewDateItem::KListViewDateItem(KListView* parent, QDate date, Kopete::MetaContact *mc)
+ : KListViewItem(parent, date.toString(Qt::ISODate), mc->displayName())
+{
+ mDate = date;
+ mMetaContact = mc;
+}
+
+int KListViewDateItem::compare(QListViewItem *i, int col, bool ascending) const
+{
+ if (col)
+ return QListViewItem::compare(i, col, ascending);
+
+ //compare dates - do NOT use ascending var here
+ KListViewDateItem* item = static_cast<KListViewDateItem*>(i);
+ if ( mDate < item->date() )
+ return -1;
+ return ( mDate > item->date() );
+}
+
+
+HistoryDialog::HistoryDialog(Kopete::MetaContact *mc, QWidget* parent,
+ const char* name) : KDialogBase(parent, name, false,
+ i18n("History for %1").arg(mc->displayName()), 0), mSearching(false)
+{
+ QString fontSize;
+ QString htmlCode;
+ QString fontStyle;
+
+ kdDebug(14310) << k_funcinfo << "called." << endl;
+ setWFlags(Qt::WDestructiveClose); // send SIGNAL(closing()) on quit
+
+ // FIXME: Allow to show this dialog for only one contact
+ mMetaContact = mc;
+
+
+
+ // Widgets initializations
+ mMainWidget = new HistoryViewer(this, "HistoryDialog::mMainWidget");
+ mMainWidget->searchLine->setFocus();
+ mMainWidget->searchLine->setTrapReturnKey (true);
+ mMainWidget->searchLine->setTrapReturnKey(true);
+ mMainWidget->searchErase->setPixmap(BarIcon("locationbar_erase"));
+
+ mMainWidget->contactComboBox->insertItem(i18n("All"));
+ mMetaContactList = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it(mMetaContactList);
+ for(; it.current(); ++it)
+ {
+ mMainWidget->contactComboBox->insertItem((*it)->displayName());
+ }
+
+ if (mMetaContact)
+ mMainWidget->contactComboBox->setCurrentItem(mMetaContactList.find(mMetaContact)+1);
+
+ mMainWidget->dateSearchLine->setListView(mMainWidget->dateListView);
+ mMainWidget->dateListView->setSorting(0, 0); //newest-first
+
+ setMainWidget(mMainWidget);
+
+ // Initializing HTML Part
+ mMainWidget->htmlFrame->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ QVBoxLayout *l = new QVBoxLayout(mMainWidget->htmlFrame);
+ mHtmlPart = new KHTMLPart(mMainWidget->htmlFrame, "htmlHistoryView");
+
+ //Security settings, we don't need this stuff
+ mHtmlPart->setJScriptEnabled(false);
+ mHtmlPart->setJavaEnabled(false);
+ mHtmlPart->setPluginsEnabled(false);
+ mHtmlPart->setMetaRefreshEnabled(false);
+ mHtmlPart->setOnlyLocalReferences(true);
+
+ mHtmlView = mHtmlPart->view();
+ mHtmlView->setMarginWidth(4);
+ mHtmlView->setMarginHeight(4);
+ mHtmlView->setFocusPolicy(NoFocus);
+ mHtmlView->setSizePolicy(
+ QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
+ l->addWidget(mHtmlView);
+
+ QTextOStream( &fontSize ) << KopetePrefs::prefs()->fontFace().pointSize();
+ fontStyle = "<style>.hf { font-size:" + fontSize + ".0pt; font-family:" + KopetePrefs::prefs()->fontFace().family() + "; color: " + KopetePrefs::prefs()->textColor().name() + "; }</style>";
+
+ mHtmlPart->begin();
+ htmlCode = "<html><head>" + fontStyle + "</head><body class=\"hf\"></body></html>";
+ mHtmlPart->write( QString::fromLatin1( htmlCode.latin1() ) );
+ mHtmlPart->end();
+
+
+ connect(mHtmlPart->browserExtension(), SIGNAL(openURLRequestDelayed(const KURL &, const KParts::URLArgs &)),
+ this, SLOT(slotOpenURLRequest(const KURL &, const KParts::URLArgs &)));
+ connect(mMainWidget->dateListView, SIGNAL(clicked(QListViewItem*)), this, SLOT(dateSelected(QListViewItem*)));
+ connect(mMainWidget->searchButton, SIGNAL(clicked()), this, SLOT(slotSearch()));
+ connect(mMainWidget->searchLine, SIGNAL(returnPressed()), this, SLOT(slotSearch()));
+ connect(mMainWidget->searchLine, SIGNAL(textChanged(const QString&)), this, SLOT(slotSearchTextChanged(const QString&)));
+ connect(mMainWidget->searchErase, SIGNAL(clicked()), this, SLOT(slotSearchErase()));
+ connect(mMainWidget->contactComboBox, SIGNAL(activated(int)), this, SLOT(slotContactChanged(int)));
+ connect(mMainWidget->messageFilterBox, SIGNAL(activated(int)), this, SLOT(slotFilterChanged(int )));
+ connect(mHtmlPart, SIGNAL(popupMenu(const QString &, const QPoint &)), this, SLOT(slotRightClick(const QString &, const QPoint &)));
+
+ //initActions
+ KActionCollection* ac = new KActionCollection(this);
+ mCopyAct = KStdAction::copy( this, SLOT(slotCopy()), ac );
+ mCopyURLAct = new KAction( i18n( "Copy Link Address" ), QString::fromLatin1( "editcopy" ), 0, this, SLOT( slotCopyURL() ), ac );
+
+ resize(650, 700);
+ centerOnScreen(this);
+
+ // show the dialog before people get impatient
+ show();
+
+ // Load history dates in the listview
+ init();
+}
+
+HistoryDialog::~HistoryDialog()
+{
+ mSearching = false;
+}
+
+void HistoryDialog::init()
+{
+ if(mMetaContact)
+ {
+ HistoryLogger logger(mMetaContact, this);
+ init(mMetaContact);
+ }
+ else
+ {
+ QPtrListIterator<Kopete::MetaContact> it(mMetaContactList);
+ for(; it.current(); ++it)
+ {
+ HistoryLogger logger(*it, this);
+ init(*it);
+ }
+
+ }
+
+ initProgressBar(i18n("Loading..."),mInit.dateMCList.count());
+ QTimer::singleShot(0,this,SLOT(slotLoadDays()));
+}
+
+void HistoryDialog::slotLoadDays()
+{
+ if(mInit.dateMCList.isEmpty())
+ {
+ if (!mMainWidget->searchLine->text().isEmpty())
+ QTimer::singleShot(0, this, SLOT(slotSearch()));
+ doneProgressBar();
+ return;
+ }
+
+ DMPair pair(mInit.dateMCList.first());
+ mInit.dateMCList.pop_front();
+ HistoryLogger logger(pair.metaContact(), this);
+ QValueList<int> dayList = logger.getDaysForMonth(pair.date());
+ for (unsigned int i=0; i<dayList.count(); i++)
+ {
+ QDate c2Date(pair.date().year(),pair.date().month(),dayList[i]);
+ if (mInit.dateMCList.find(pair) == mInit.dateMCList.end())
+ new KListViewDateItem(mMainWidget->dateListView, c2Date, pair.metaContact());
+ }
+ mMainWidget->searchProgress->advance(1);
+ QTimer::singleShot(0,this,SLOT(slotLoadDays()));
+
+
+}
+
+void HistoryDialog::init(Kopete::MetaContact *mc)
+{
+ QPtrList<Kopete::Contact> contacts=mc->contacts();
+ QPtrListIterator<Kopete::Contact> it( contacts );
+
+ for( ; it.current(); ++it )
+ {
+ init(*it);
+ }
+}
+
+void HistoryDialog::init(Kopete::Contact *c)
+{
+ // Get year and month list
+ QRegExp rx( "\\.(\\d\\d\\d\\d)(\\d\\d)" );
+ const QString contact_in_filename=c->contactId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) );
+ QFileInfo *fi;
+
+
+ // BEGIN check if there are Kopete 0.7.x
+ QDir d1(locateLocal("data",QString("kopete/logs/")+
+ c->protocol()->pluginId().replace( QRegExp(QString::fromLatin1("[./~?*]")),QString::fromLatin1("-"))
+ ));
+ d1.setFilter( QDir::Files | QDir::NoSymLinks );
+ d1.setSorting( QDir::Name );
+
+ const QFileInfoList *list1 = d1.entryInfoList();
+ if ( list1 != 0 )
+ {
+ QFileInfoListIterator it1( *list1 );
+ while ( (fi = it1.current()) != 0 )
+ {
+ if(fi->fileName().contains(contact_in_filename))
+ {
+ rx.search(fi->fileName());
+
+ QDate cDate = QDate(rx.cap(1).toInt(), rx.cap(2).toInt(), 1);
+
+ DMPair pair(cDate, c->metaContact());
+ mInit.dateMCList.append(pair);
+
+ }
+ ++it1;
+ }
+ }
+ // END of kopete 0.7.x check
+
+ QString logDir = locateLocal("data",QString("kopete/logs/")+
+ c->protocol()->pluginId().replace( QRegExp(QString::fromLatin1("[./~?*]")),QString::fromLatin1("-")) +
+ QString::fromLatin1( "/" ) +
+ c->account()->accountId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) )
+ );
+ QDir d(logDir);
+ d.setFilter( QDir::Files | QDir::NoSymLinks );
+ d.setSorting( QDir::Name );
+ const QFileInfoList *list = d.entryInfoList();
+ if ( list != 0 )
+ {
+ QFileInfoListIterator it( *list );
+ while ( (fi = it.current()) != 0 )
+ {
+ if(fi->fileName().contains(contact_in_filename))
+ {
+
+ rx.search(fi->fileName());
+
+ // We search for an item in the list view with the same year. If then we add the month
+ QDate cDate = QDate(rx.cap(1).toInt(), rx.cap(2).toInt(), 1);
+
+ DMPair pair(cDate, c->metaContact());
+ mInit.dateMCList.append(pair);
+ }
+ ++it;
+ }
+ }
+}
+
+void HistoryDialog::dateSelected(QListViewItem* it)
+{
+ KListViewDateItem *item = static_cast<KListViewDateItem*>(it);
+
+ if (!item) return;
+
+ QDate chosenDate = item->date();
+
+ HistoryLogger logger(item->metaContact(), this);
+ QValueList<Kopete::Message> msgs=logger.readMessages(chosenDate);
+
+ setMessages(msgs);
+}
+
+void HistoryDialog::setMessages(QValueList<Kopete::Message> msgs)
+{
+ // Clear View
+ DOM::HTMLElement htmlBody = mHtmlPart->htmlDocument().body();
+ while(htmlBody.hasChildNodes())
+ htmlBody.removeChild(htmlBody.childNodes().item(htmlBody.childNodes().length() - 1));
+ // ----
+
+ QString dir = (QApplication::reverseLayout() ? QString::fromLatin1("rtl") :
+ QString::fromLatin1("ltr"));
+
+ QValueList<Kopete::Message>::iterator it = msgs.begin();
+
+
+ QString accountLabel;
+ QString resultHTML = "<b><font color=\"red\">" + (*it).timestamp().date().toString() + "</font></b><br/>";
+ DOM::HTMLElement newNode = mHtmlPart->document().createElement(QString::fromLatin1("span"));
+ newNode.setAttribute(QString::fromLatin1("dir"), dir);
+ newNode.setInnerHTML(resultHTML);
+ mHtmlPart->htmlDocument().body().appendChild(newNode);
+
+ // Populating HTML Part with messages
+ for ( it = msgs.begin(); it != msgs.end(); ++it )
+ {
+ if ( mMainWidget->messageFilterBox->currentItem() == 0
+ || ( mMainWidget->messageFilterBox->currentItem() == 1 && (*it).direction() == Kopete::Message::Inbound )
+ || ( mMainWidget->messageFilterBox->currentItem() == 2 && (*it).direction() == Kopete::Message::Outbound ) )
+ {
+ resultHTML = "";
+
+ if (accountLabel.isEmpty() || accountLabel != (*it).from()->account()->accountLabel())
+ // If the message's account is new, just specify it to the user
+ {
+ if (!accountLabel.isEmpty())
+ resultHTML += "<br/><br/><br/>";
+ resultHTML += "<b><font color=\"blue\">" + (*it).from()->account()->accountLabel() + "</font></b><br/>";
+ }
+ accountLabel = (*it).from()->account()->accountLabel();
+
+ QString body = (*it).parsedBody();
+
+ if (!mMainWidget->searchLine->text().isEmpty())
+ // If there is a search, then we hightlight the keywords
+ {
+ body = body.replace(mMainWidget->searchLine->text(), "<span style=\"background-color:yellow\">" + mMainWidget->searchLine->text() + "</span>", false);
+ }
+
+ resultHTML += "(<b>" + (*it).timestamp().time().toString() + "</b>) "
+ + ((*it).direction() == Kopete::Message::Outbound ?
+ "<font color=\"" + KopetePrefs::prefs()->textColor().dark().name() + "\"><b>&gt;</b></font> "
+ : "<font color=\"" + KopetePrefs::prefs()->textColor().light(200).name() + "\"><b>&lt;</b></font> ")
+ + body + "<br/>";
+
+ newNode = mHtmlPart->document().createElement(QString::fromLatin1("span"));
+ newNode.setAttribute(QString::fromLatin1("dir"), dir);
+ newNode.setInnerHTML(resultHTML);
+
+ mHtmlPart->htmlDocument().body().appendChild(newNode);
+ }
+ }
+}
+
+void HistoryDialog::slotFilterChanged(int /* index */)
+{
+ dateSelected(mMainWidget->dateListView->currentItem());
+}
+
+void HistoryDialog::slotOpenURLRequest(const KURL &url, const KParts::URLArgs &/*args*/)
+{
+ kdDebug(14310) << k_funcinfo << "url=" << url.url() << endl;
+ new KRun(url, 0, false); // false = non-local files
+}
+
+// Disable search button if there is no search text
+void HistoryDialog::slotSearchTextChanged(const QString& searchText)
+{
+ if (searchText.isEmpty())
+ {
+ mMainWidget->searchButton->setEnabled(false);
+ slotSearchErase();
+ }
+ else
+ {
+ mMainWidget->searchButton->setEnabled(true);
+ }
+}
+
+void HistoryDialog::listViewShowElements(bool s)
+{
+ KListViewDateItem* item = static_cast<KListViewDateItem*>(mMainWidget->dateListView->firstChild());
+ while (item != 0)
+ {
+ item->setVisible(s);
+ item = static_cast<KListViewDateItem*>(item->nextSibling());
+ }
+}
+
+// Erase the search line, show all date/metacontacts items in the list (accordint to the
+// metacontact selected in the combobox)
+void HistoryDialog::slotSearchErase()
+{
+ mMainWidget->searchLine->clear();
+ listViewShowElements(true);
+}
+
+/*
+* How does the search work
+* ------------------------
+* We do the search respecting the current metacontact filter item. To do this, we iterate over the
+* elements in the KListView (KListViewDateItems) and, for each one, we iterate over its subcontacts,
+* manually searching the log files of each one. To avoid searching files twice, the months that have
+* been searched already are stored in searchedMonths. The matches are placed in the matches QMap.
+* Finally, the current date item is checked in the matches QMap, and if it is present, it is shown.
+*
+* Keyword highlighting is done in setMessages() : if the search field isn't empty, we highlight the
+* search keyword.
+*
+* The search is _not_ case sensitive
+*/
+void HistoryDialog::slotSearch()
+{
+ if (mMainWidget->dateListView->childCount() == 0) return;
+
+ QRegExp rx("^ <msg.*time=\"(\\d+) \\d+:\\d+:\\d+\" >([^<]*)<");
+ QMap<QDate, QValueList<Kopete::MetaContact*> > monthsSearched;
+ QMap<QDate, QValueList<Kopete::MetaContact*> > matches;
+
+ // cancel button pressed
+ if (mSearching)
+ {
+ listViewShowElements(true);
+ goto searchFinished;
+ }
+
+ listViewShowElements(false);
+
+ initProgressBar(i18n("Searching..."), mMainWidget->dateListView->childCount());
+ mMainWidget->searchButton->setText(i18n("&Cancel"));
+ mSearching = true;
+
+ // iterate over items in the date list widget
+ for(KListViewDateItem *curItem = static_cast<KListViewDateItem*>(mMainWidget->dateListView->firstChild());
+ curItem != 0;
+ curItem = static_cast<KListViewDateItem *>(curItem->nextSibling())
+ )
+ {
+ qApp->processEvents();
+ if (!mSearching) return;
+
+ QDate month(curItem->date().year(),curItem->date().month(),1);
+ // if we haven't searched the relevant history logs, search them now
+ if (!monthsSearched[month].contains(curItem->metaContact()))
+ {
+ monthsSearched[month].push_back(curItem->metaContact());
+ QPtrList<Kopete::Contact> contacts = curItem->metaContact()->contacts();
+ for(QPtrListIterator<Kopete::Contact> it( contacts ); it.current(); ++it)
+ {
+ // get filename and open file
+ QString filename(HistoryLogger::getFileName(*it, curItem->date()));
+ if (!QFile::exists(filename)) continue;
+ QFile file(filename);
+ file.open(IO_ReadOnly);
+ if (!file.isOpen())
+ {
+ kdWarning(14310) << k_funcinfo << "Error opening " <<
+ file.name() << ": " << file.errorString() << endl;
+ continue;
+ }
+
+ QTextStream stream(&file);
+ QString textLine;
+ while(!stream.atEnd())
+ {
+ textLine = stream.readLine();
+ if (textLine.contains(mMainWidget->searchLine->text(), false))
+ {
+ if(rx.search(textLine) != -1)
+ {
+ // only match message body
+ if (rx.cap(2).contains(mMainWidget->searchLine->text()))
+ matches[QDate(curItem->date().year(),curItem->date().month(),rx.cap(1).toInt())].push_back(curItem->metaContact());
+ }
+ // this will happen when multiline messages are searched, properly
+ // parsing the files would fix this
+ else { }
+ }
+ qApp->processEvents();
+ if (!mSearching) return;
+ }
+ file.close();
+ }
+ }
+
+ // relevant logfiles have been searched now, check if current date matches
+ if (matches[curItem->date()].contains(curItem->metaContact()))
+ curItem->setVisible(true);
+
+ // Next date item
+ mMainWidget->searchProgress->advance(1);
+ }
+
+searchFinished:
+ mMainWidget->searchButton->setText(i18n("Se&arch"));
+ mSearching = false;
+ doneProgressBar();
+}
+
+
+
+// When a contact is selected in the combobox. Item 0 is All contacts.
+void HistoryDialog::slotContactChanged(int index)
+{
+ mMainWidget->dateListView->clear();
+ if (index == 0)
+ {
+ setCaption(i18n("History for All Contacts"));
+ mMetaContact = 0;
+ init();
+ }
+ else
+ {
+ mMetaContact = mMetaContactList.at(index-1);
+ setCaption(i18n("History for %1").arg(mMetaContact->displayName()));
+ init();
+ }
+}
+
+void HistoryDialog::initProgressBar(const QString& text, int nbSteps)
+{
+ mMainWidget->searchProgress->setTotalSteps(nbSteps);
+ mMainWidget->searchProgress->setProgress(0);
+ mMainWidget->searchProgress->show();
+ mMainWidget->statusLabel->setText(text);
+}
+
+void HistoryDialog::doneProgressBar()
+{
+ mMainWidget->searchProgress->hide();
+ mMainWidget->statusLabel->setText(i18n("Ready"));
+}
+
+void HistoryDialog::slotRightClick(const QString &url, const QPoint &point)
+{
+ KPopupMenu *chatWindowPopup = 0L;
+ chatWindowPopup = new KPopupMenu();
+
+ if ( !url.isEmpty() )
+ {
+ mURL = url;
+ mCopyURLAct->plug( chatWindowPopup );
+ chatWindowPopup->insertSeparator();
+ }
+ mCopyAct->setEnabled( mHtmlPart->hasSelection() );
+ mCopyAct->plug( chatWindowPopup );
+
+ connect( chatWindowPopup, SIGNAL( aboutToHide() ), chatWindowPopup, SLOT( deleteLater() ) );
+ chatWindowPopup->popup(point);
+}
+
+void HistoryDialog::slotCopy()
+{
+ QString qsSelection;
+ qsSelection = mHtmlPart->selectedText();
+ if ( qsSelection.isEmpty() ) return;
+
+ disconnect( kapp->clipboard(), SIGNAL( selectionChanged()), mHtmlPart, SLOT(slotClearSelection()));
+ QApplication::clipboard()->setText(qsSelection, QClipboard::Clipboard);
+ QApplication::clipboard()->setText(qsSelection, QClipboard::Selection);
+ connect( kapp->clipboard(), SIGNAL( selectionChanged()), mHtmlPart, SLOT(slotClearSelection()));
+}
+
+void HistoryDialog::slotCopyURL()
+{
+ disconnect( kapp->clipboard(), SIGNAL( selectionChanged()), mHtmlPart, SLOT(slotClearSelection()));
+ QApplication::clipboard()->setText( mURL, QClipboard::Clipboard);
+ QApplication::clipboard()->setText( mURL, QClipboard::Selection);
+ connect( kapp->clipboard(), SIGNAL( selectionChanged()), mHtmlPart, SLOT(slotClearSelection()));
+}
+
+#include "historydialog.moc"
diff --git a/kopete/plugins/history/historydialog.h b/kopete/plugins/history/historydialog.h
new file mode 100644
index 00000000..cf26037d
--- /dev/null
+++ b/kopete/plugins/history/historydialog.h
@@ -0,0 +1,146 @@
+/*
+ kopetehistorydialog.h - Kopete History Dialog
+
+ Copyright (c) 2002 by Richard Stellingwerff <remenic@linuxfromscratch.org>
+ Copyright (c) 2004 by Stefan Gehn <metz AT gehn.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _HISTORYDIALOG_H
+#define _HISTORYDIALOG_H
+
+#include <qfile.h>
+#include <qstringlist.h>
+
+#include <kdialogbase.h>
+#include <klistview.h>
+
+#include "kopetemessage.h"
+
+class HistoryViewer;
+
+//class HistoryWidget;
+namespace Kopete { class MetaContact; }
+namespace Kopete { class XSLT; }
+class HistoryLogger;
+class KHTMLView;
+class KHTMLPart;
+
+class KURL;
+namespace KParts { struct URLArgs; class Part; }
+
+
+class KListViewDateItem;
+
+class DMPair
+{
+ public:
+ DMPair() {md = QDate(0, 0, 0); mc = 0; }
+ DMPair(QDate d, Kopete::MetaContact *c) { md = d; mc =c; }
+ QDate date() const { return md; }
+ Kopete::MetaContact* metaContact() const { return mc; }
+ bool operator==(const DMPair p1) const { return p1.date() == this->date() && p1.metaContact() == this->metaContact(); }
+ private:
+ QDate md;
+ Kopete::MetaContact *mc;
+};
+
+/**
+ * @author Richard Stellingwerff <remenic@linuxfromscratch.org>
+ * @author Stefan Gehn <metz AT gehn.net>
+ */
+class HistoryDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ HistoryDialog(Kopete::MetaContact *mc, QWidget* parent=0,
+ const char* name="HistoryDialog");
+ ~HistoryDialog();
+
+ /**
+ * Calls init(Kopete::Contact *c) for each subcontact of the metacontact
+ */
+
+
+ signals:
+ void closing();
+
+ private slots:
+ void slotOpenURLRequest(const KURL &url, const KParts::URLArgs &/*args*/);
+
+ // Called when a date is selected in the treeview
+ void dateSelected(QListViewItem *);
+
+ void slotSearch();
+
+ // Reinitialise search
+ void slotSearchErase();
+ void slotSearchTextChanged(const QString& txt); // To enable/disable search button
+ void slotContactChanged(int index);
+ void slotFilterChanged(int index);
+
+ void init();
+ void slotLoadDays();
+
+ void slotRightClick(const QString &url, const QPoint &point);
+ void slotCopy();
+ void slotCopyURL();
+
+ private:
+ enum Disabled { Prev=1, Next=2 };
+ void refreshEnabled( /*Disabled*/ uint disabled );
+
+ void initProgressBar(const QString& text, int nbSteps);
+ void doneProgressBar();
+ void init(Kopete::MetaContact *mc);
+ void init(Kopete::Contact *c);
+
+ /**
+ * Show the messages in the HTML View
+ */
+ void setMessages(QValueList<Kopete::Message> m);
+
+ void listViewShowElements(bool s);
+
+ /**
+ * Search if @param item already has @param text child
+ */
+ bool hasChild(KListViewItem* item, int month);
+
+ /**
+ * We show history dialog to look at the log for a metacontact. Here is this metacontact.
+ */
+ Kopete::MetaContact *mMetaContact;
+
+ QPtrList<Kopete::MetaContact> mMetaContactList;
+
+ // History View
+ KHTMLView *mHtmlView;
+ KHTMLPart *mHtmlPart;
+ HistoryViewer *mMainWidget;
+ Kopete::XSLT *mXsltParser;
+
+ struct Init
+ {
+ QValueList<DMPair> dateMCList; // mc for MetaContact
+ } mInit;
+
+ bool mSearching;
+
+ KAction *mCopyAct;
+ KAction *mCopyURLAct;
+ QString mURL;
+};
+
+#endif
diff --git a/kopete/plugins/history/historyguiclient.cpp b/kopete/plugins/history/historyguiclient.cpp
new file mode 100644
index 00000000..133e50a3
--- /dev/null
+++ b/kopete/plugins/history/historyguiclient.cpp
@@ -0,0 +1,115 @@
+/*
+ historyguiclient.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "historyguiclient.h"
+#include "historylogger.h"
+#include "historyconfig.h"
+
+#include "kopetechatsession.h"
+#include "kopetecontact.h"
+#include "kopeteview.h"
+
+#include <kaction.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+class HistoryPlugin;
+
+HistoryGUIClient::HistoryGUIClient(Kopete::ChatSession *parent, const char *name)
+ : QObject(parent, name), KXMLGUIClient(parent)
+{
+ setInstance(KGenericFactory<HistoryPlugin>::instance());
+
+ m_manager = parent;
+
+ // Refuse to build this client, it is based on wrong parameters
+ if(!m_manager || m_manager->members().isEmpty())
+ deleteLater();
+
+ QPtrList<Kopete::Contact> mb=m_manager->members();
+ m_logger=new HistoryLogger( mb.first() , this );
+
+ actionLast=new KAction( i18n("History Last" ), QString::fromLatin1( "finish" ), 0, this, SLOT(slotLast()), actionCollection() , "historyLast" );
+ actionPrev = KStdAction::back( this, SLOT(slotPrevious()), actionCollection() , "historyPrevious" );
+ actionNext = KStdAction::forward( this, SLOT(slotNext()), actionCollection() , "historyNext" );
+
+ // we are generally at last when begining
+ actionPrev->setEnabled(true);
+ actionNext->setEnabled(false);
+ actionLast->setEnabled(false);
+
+ setXMLFile("historychatui.rc");
+}
+
+
+HistoryGUIClient::~HistoryGUIClient()
+{
+}
+
+
+void HistoryGUIClient::slotPrevious()
+{
+ KopeteView *m_currentView = m_manager->view(true);
+ m_currentView->clear();
+
+ QPtrList<Kopete::Contact> mb = m_manager->members();
+ QValueList<Kopete::Message> msgs = m_logger->readMessages(
+ HistoryConfig::number_ChatWindow(), /*mb.first()*/ 0L,
+ HistoryLogger::AntiChronological, true);
+
+ actionPrev->setEnabled(msgs.count() == HistoryConfig::number_ChatWindow());
+ actionNext->setEnabled(true);
+ actionLast->setEnabled(true);
+
+ m_currentView->appendMessages(msgs);
+}
+
+void HistoryGUIClient::slotLast()
+{
+ KopeteView *m_currentView = m_manager->view(true);
+ m_currentView->clear();
+
+ QPtrList<Kopete::Contact> mb = m_manager->members();
+ m_logger->setPositionToLast();
+ QValueList<Kopete::Message> msgs = m_logger->readMessages(
+ HistoryConfig::number_ChatWindow(), /*mb.first()*/ 0L,
+ HistoryLogger::AntiChronological, true);
+
+ actionPrev->setEnabled(true);
+ actionNext->setEnabled(false);
+ actionLast->setEnabled(false);
+
+ m_currentView->appendMessages(msgs);
+}
+
+
+void HistoryGUIClient::slotNext()
+{
+ KopeteView *m_currentView = m_manager->view(true);
+ m_currentView->clear();
+
+ QPtrList<Kopete::Contact> mb = m_manager->members();
+ QValueList<Kopete::Message> msgs = m_logger->readMessages(
+ HistoryConfig::number_ChatWindow(), /*mb.first()*/ 0L,
+ HistoryLogger::Chronological, false);
+
+ actionPrev->setEnabled(true);
+ actionNext->setEnabled(msgs.count() == HistoryConfig::number_ChatWindow());
+ actionLast->setEnabled(msgs.count() == HistoryConfig::number_ChatWindow());
+
+ m_currentView->appendMessages(msgs);
+}
+
+#include "historyguiclient.moc"
diff --git a/kopete/plugins/history/historyguiclient.h b/kopete/plugins/history/historyguiclient.h
new file mode 100644
index 00000000..420795e0
--- /dev/null
+++ b/kopete/plugins/history/historyguiclient.h
@@ -0,0 +1,55 @@
+/*
+ historyguiclient.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef HISTORYGUICLIENT_H
+#define HISTORYGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+namespace Kopete { class ChatSession; }
+class HistoryLogger;
+class KAction;
+
+/**
+ *@author Olivier Goffart
+ */
+class HistoryGUIClient : public QObject , public KXMLGUIClient
+{
+Q_OBJECT
+public:
+ HistoryGUIClient(Kopete::ChatSession *parent = 0, const char *name = 0);
+ ~HistoryGUIClient();
+
+ HistoryLogger *logger() const { return m_logger; }
+
+private slots:
+ void slotPrevious();
+ void slotLast();
+ void slotNext();
+
+private:
+ HistoryLogger *m_logger;
+ Kopete::ChatSession *m_manager;
+ //bool m_autoChatWindow;
+ //int m_nbAutoChatWindow;
+ //unsigned int m_nbChatWindow;
+
+ KAction *actionPrev;
+ KAction *actionNext;
+ KAction *actionLast;
+};
+
+#endif
diff --git a/kopete/plugins/history/historylogger.cpp b/kopete/plugins/history/historylogger.cpp
new file mode 100644
index 00000000..7848136f
--- /dev/null
+++ b/kopete/plugins/history/historylogger.cpp
@@ -0,0 +1,851 @@
+/*
+ historylogger.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "historylogger.h"
+#include "historyconfig.h"
+
+#include <qregexp.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qdatetime.h>
+#include <qdom.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <ksavefile.h>
+
+#include "kopeteglobal.h"
+#include "kopetecontact.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccount.h"
+#include "kopetemetacontact.h"
+#include "kopetechatsession.h"
+
+// -----------------------------------------------------------------------------
+HistoryLogger::HistoryLogger( Kopete::MetaContact *m, QObject *parent, const char *name )
+ : QObject(parent, name)
+{
+ m_saveTimer=0L;
+ m_saveTimerTime=0;
+ m_metaContact=m;
+ m_hideOutgoing=false;
+ m_cachedMonth=-1;
+ m_realMonth=QDate::currentDate().month();
+ m_oldSens=Default;
+
+ //the contact may be destroyed, for example, if the contact changes its metacontact
+ connect(m_metaContact , SIGNAL(destroyed(QObject *)) , this , SLOT(slotMCDeleted()));
+
+ setPositionToLast();
+}
+
+
+HistoryLogger::HistoryLogger( Kopete::Contact *c, QObject *parent, const char *name )
+ : QObject(parent, name)
+{
+ m_saveTimer=0L;
+ m_saveTimerTime=0;
+ m_cachedMonth=-1;
+ m_metaContact=c->metaContact();
+ m_hideOutgoing=false;
+ m_realMonth=QDate::currentDate().month();
+ m_oldSens=Default;
+
+ //the contact may be destroyed, for example, if the contact changes its metacontact
+ connect(m_metaContact , SIGNAL(destroyed(QObject *)) , this , SLOT(slotMCDeleted()));
+
+ setPositionToLast();
+}
+
+
+HistoryLogger::~HistoryLogger()
+{
+ if(m_saveTimer && m_saveTimer->isActive())
+ saveToDisk();
+}
+
+
+void HistoryLogger::setPositionToLast()
+{
+ setCurrentMonth(0);
+ m_oldSens = AntiChronological;
+ m_oldMonth=0;
+ m_oldElements.clear();
+}
+
+
+void HistoryLogger::setPositionToFirst()
+{
+ setCurrentMonth( getFirstMonth() );
+ m_oldSens = Chronological;
+ m_oldMonth=m_currentMonth;
+ m_oldElements.clear();
+}
+
+
+void HistoryLogger::setCurrentMonth(int month)
+{
+ m_currentMonth = month;
+ m_currentElements.clear();
+}
+
+
+QDomDocument HistoryLogger::getDocument(const Kopete::Contact *c, unsigned int month , bool canLoad , bool* contain)
+{
+ if(m_realMonth!=QDate::currentDate().month())
+ { //We changed month, our indice are not correct anymore, clean memory.
+ // or we will see what i called "the 31 midnight bug"(TM) :-) -Olivier
+ m_documents.clear();
+ m_cachedMonth=-1;
+ m_currentMonth++; //Not usre it's ok, but should work;
+ m_oldMonth++; // idem
+ m_realMonth=QDate::currentDate().month();
+ }
+
+ if(!m_metaContact)
+ { //this may happen if the contact has been moved, and the MC deleted
+ if(c && c->metaContact())
+ m_metaContact=c->metaContact();
+ else
+ return QDomDocument();
+ }
+
+ if(!m_metaContact->contacts().contains(c))
+ {
+ if(contain)
+ *contain=false;
+ return QDomDocument();
+ }
+
+ QMap<unsigned int , QDomDocument> documents = m_documents[c];
+ if (documents.contains(month))
+ return documents[month];
+
+
+ QDomDocument doc = getDocument(c, QDate::currentDate().addMonths(0-month), canLoad, contain);
+
+ documents.insert(month, doc);
+ m_documents[c]=documents;
+
+ return doc;
+
+}
+
+QDomDocument HistoryLogger::getDocument(const Kopete::Contact *c, const QDate date , bool canLoad , bool* contain)
+{
+ if(!m_metaContact)
+ { //this may happen if the contact has been moved, and the MC deleted
+ if(c && c->metaContact())
+ m_metaContact=c->metaContact();
+ else
+ return QDomDocument();
+ }
+
+ if(!m_metaContact->contacts().contains(c))
+ {
+ if(contain)
+ *contain=false;
+ return QDomDocument();
+ }
+
+ if(!canLoad)
+ {
+ if(contain)
+ *contain=false;
+ return QDomDocument();
+ }
+
+ QString FileName = getFileName(c, date);
+
+ QDomDocument doc( "Kopete-History" );
+
+ QFile file( FileName );
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ if(contain)
+ *contain=false;
+ return doc;
+ }
+ if ( !doc.setContent( &file ) )
+ {
+ file.close();
+ if(contain)
+ *contain=false;
+ return doc;
+ }
+ file.close();
+
+ if(contain)
+ *contain=true;
+
+ return doc;
+}
+
+
+void HistoryLogger::appendMessage( const Kopete::Message &msg , const Kopete::Contact *ct )
+{
+ if(!msg.from())
+ return;
+
+ // If no contact are given: If the manager is availiable, use the manager's
+ // first contact (the channel on irc, or the other contact for others protocols
+ const Kopete::Contact *c = ct;
+ if(!c && msg.manager() )
+ {
+ QPtrList<Kopete::Contact> mb=msg.manager()->members() ;
+ c = mb.first();
+ }
+ if(!c) //If the contact is still not initialized, use the message author.
+ c = msg.direction()==Kopete::Message::Outbound ? msg.to().first() : msg.from() ;
+
+
+ if(!m_metaContact)
+ { //this may happen if the contact has been moved, and the MC deleted
+ if(c && c->metaContact())
+ m_metaContact=c->metaContact();
+ else
+ return;
+ }
+
+
+ if(!c || !m_metaContact->contacts().contains(c) )
+ {
+ /*QPtrList<Kopete::Contact> contacts= m_metaContact->contacts();
+ QPtrListIterator<Kopete::Contact> it( contacts );
+ for( ; it.current(); ++it )
+ {
+ if( (*it)->protocol()->pluginId() == msg.from()->protocol()->pluginId() )
+ {
+ c=*it;
+ break;
+ }
+ }*/
+ //if(!c)
+
+ kdWarning(14310) << k_funcinfo << "No contact found in this metacontact to" <<
+ " append in the history" << endl;
+ return;
+ }
+
+ QDomDocument doc=getDocument(c,0);
+ QDomElement docElem = doc.documentElement();
+
+ if(docElem.isNull())
+ {
+ docElem= doc.createElement( "kopete-history" );
+ docElem.setAttribute ( "version" , "0.9" );
+ doc.appendChild( docElem );
+ QDomElement headElem = doc.createElement( "head" );
+ docElem.appendChild( headElem );
+ QDomElement dateElem = doc.createElement( "date" );
+ dateElem.setAttribute( "year", QString::number(QDate::currentDate().year()) );
+ dateElem.setAttribute( "month", QString::number(QDate::currentDate().month()) );
+ headElem.appendChild(dateElem);
+ QDomElement myselfElem = doc.createElement( "contact" );
+ myselfElem.setAttribute( "type", "myself" );
+ myselfElem.setAttribute( "contactId", c->account()->myself()->contactId() );
+ headElem.appendChild(myselfElem);
+ QDomElement contactElem = doc.createElement( "contact" );
+ contactElem.setAttribute( "contactId", c->contactId() );
+ headElem.appendChild(contactElem);
+ }
+
+ QDomElement msgElem = doc.createElement( "msg" );
+ msgElem.setAttribute( "in", msg.direction()==Kopete::Message::Outbound ? "0" : "1" );
+ msgElem.setAttribute( "from", msg.from()->contactId() );
+ msgElem.setAttribute( "nick", msg.from()->property( Kopete::Global::Properties::self()->nickName() ).value().toString() ); //do we have to set this?
+ msgElem.setAttribute( "time", msg.timestamp().toString("d h:m:s") );
+
+ QDomText msgNode = doc.createTextNode( msg.plainBody() );
+ docElem.appendChild( msgElem );
+ msgElem.appendChild( msgNode );
+
+
+ // I'm temporizing the save.
+ // On hight-traffic channel, saving can take lots of CPU. (because the file is big)
+ // So i wait a time proportional to the time needed to save..
+
+ const QString filename=getFileName(c,QDate::currentDate());
+ if(!m_toSaveFileName.isEmpty() && m_toSaveFileName != filename)
+ { //that mean the contact or the month has changed, save it now.
+ saveToDisk();
+ }
+
+ m_toSaveFileName=filename;
+ m_toSaveDocument=doc;
+
+ if(!m_saveTimer)
+ {
+ m_saveTimer=new QTimer(this);
+ connect( m_saveTimer, SIGNAL( timeout() ) , this, SLOT(saveToDisk()) );
+ }
+ if(!m_saveTimer->isActive())
+ m_saveTimer->start( m_saveTimerTime, true /*singleshot*/ );
+}
+
+void HistoryLogger::saveToDisk()
+{
+ if(m_saveTimer)
+ m_saveTimer->stop();
+ if(m_toSaveFileName.isEmpty() || m_toSaveDocument.isNull())
+ return;
+
+ QTime t;
+ t.start(); //mesure the time needed to save.
+
+ KSaveFile file( m_toSaveFileName );
+ if( file.status() == 0 )
+ {
+ QTextStream *stream = file.textStream();
+ //stream->setEncoding( QTextStream::UnicodeUTF8 ); //???? oui ou non?
+ m_toSaveDocument.save( *stream, 1 );
+ file.close();
+
+ m_saveTimerTime=QMIN(t.elapsed()*1000, 300000);
+ //a time 1000 times supperior to the time needed to save. but with a upper limit of 5 minutes
+ //on a my machine, (2.4Ghz, but old HD) it should take about 10 ms to save the file.
+ // So that would mean save every 10 seconds, which seems to be ok.
+ // But it may take 500 ms if the file to save becomes too big (1Mb).
+ kdDebug(14310) << k_funcinfo << m_toSaveFileName << " saved in " << t.elapsed() << " ms " <<endl ;
+
+ m_toSaveFileName=QString::null;
+ m_toSaveDocument=QDomDocument();
+ }
+ else
+ kdError(14310) << k_funcinfo << "impossible to save the history file " << m_toSaveFileName << endl;
+
+}
+
+QValueList<Kopete::Message> HistoryLogger::readMessages(QDate date)
+{
+ QRegExp rxTime("(\\d+) (\\d+):(\\d+)($|:)(\\d*)"); //(with a 0.7.x compatibility)
+ QValueList<Kopete::Message> messages;
+
+
+ QPtrList<Kopete::Contact> ct=m_metaContact->contacts();
+ QPtrListIterator<Kopete::Contact> it( ct );
+
+ for( ; it.current(); ++it )
+ {
+ QDomDocument doc=getDocument(*it,date, true, 0L);
+ QDomElement docElem = doc.documentElement();
+ QDomNode n = docElem.firstChild();
+
+ while(!n.isNull())
+ {
+ QDomElement msgElem2 = n.toElement();
+ if( !msgElem2.isNull() && msgElem2.tagName()=="msg")
+ {
+ rxTime.search(msgElem2.attribute("time"));
+ QDateTime dt( QDate(date.year() , date.month() , rxTime.cap(1).toUInt()), QTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt(), rxTime.cap(5).toUInt() ) );
+
+ if (dt.date() != date)
+ {
+ n = n.nextSibling();
+ continue;
+ }
+
+ Kopete::Message::MessageDirection dir = (msgElem2.attribute("in") == "1") ?
+ Kopete::Message::Inbound : Kopete::Message::Outbound;
+
+ if(!m_hideOutgoing || dir != Kopete::Message::Outbound)
+ { //parse only if we don't hide it
+
+ QString f=msgElem2.attribute("from" );
+ const Kopete::Contact *from=f.isNull()? 0L : (*it)->account()->contacts()[f];
+
+ if(!from)
+ from= dir==Kopete::Message::Inbound ? (*it) : (*it)->account()->myself();
+
+ Kopete::ContactPtrList to;
+ to.append( dir==Kopete::Message::Inbound ? (*it)->account()->myself() : *it );
+
+ Kopete::Message msg(dt, from, to, msgElem2.text(), dir);
+ msg.setBody( QString::fromLatin1("<span title=\"%1\">%2</span>")
+ .arg( dt.toString(Qt::LocalDate), msg.escapedBody() ),
+ Kopete::Message::RichText);
+
+
+ // We insert it at the good place, given its date
+ QValueListIterator<Kopete::Message> msgIt;
+
+ for (msgIt = messages.begin(); msgIt != messages.end(); ++msgIt)
+ {
+ if ((*msgIt).timestamp() > msg.timestamp())
+ break;
+ }
+ messages.insert(msgIt, msg);
+ }
+ }
+
+ n = n.nextSibling();
+ } // end while on messages
+
+ }
+ return messages;
+}
+
+QValueList<Kopete::Message> HistoryLogger::readMessages(unsigned int lines,
+ const Kopete::Contact *c, Sens sens, bool reverseOrder, bool colorize)
+{
+ //QDate dd = QDate::currentDate().addMonths(0-m_currentMonth);
+
+ QValueList<Kopete::Message> messages;
+
+ // A regexp useful for this function
+ QRegExp rxTime("(\\d+) (\\d+):(\\d+)($|:)(\\d*)"); //(with a 0.7.x compatibility)
+
+ if(!m_metaContact)
+ { //this may happen if the contact has been moved, and the MC deleted
+ if(c && c->metaContact())
+ m_metaContact=c->metaContact();
+ else
+ return messages;
+ }
+
+ if(c && !m_metaContact->contacts().contains(c) )
+ return messages;
+
+ if(sens ==0 ) //if no sens are selected, just continue in the previous sens
+ sens = m_oldSens ;
+ if( m_oldSens != 0 && sens != m_oldSens )
+ { //we changed our sens! so retrieve the old position to fly in the other way
+ m_currentElements= m_oldElements;
+ m_currentMonth=m_oldMonth;
+ }
+ else
+ {
+ m_oldElements=m_currentElements;
+ m_oldMonth=m_currentMonth;
+ }
+ m_oldSens=sens;
+
+ //getting the color for messages:
+ QColor fgColor = HistoryConfig::history_color();
+
+ //Hello guest!
+
+ //there are two algoritms:
+ // - if a contact is given, or the metacontact contain only one contact, just read the history.
+ // - else, merge the history
+
+ //the merging algoritm is the following:
+ // we see what contact we have to read first, and we look at the firt date before another contact
+ // has a message with a bigger date.
+
+ QDateTime timeLimit;
+ const Kopete::Contact *currentContact=c;
+ if(!c && m_metaContact->contacts().count()==1)
+ currentContact=m_metaContact->contacts().first();
+ else if(!c && m_metaContact->contacts().count()== 0)
+ {
+ return messages;
+ }
+
+ while(messages.count() < lines)
+ {
+ timeLimit=QDateTime();
+ QDomElement msgElem; //here is the message element
+ QDateTime timestamp; //and the timestamp of this message
+
+ if(!c && m_metaContact->contacts().count()>1)
+ { //we have to merge the differents subcontact history
+ QPtrList<Kopete::Contact> ct=m_metaContact->contacts();
+ QPtrListIterator<Kopete::Contact> it( ct );
+ for( ; it.current(); ++it )
+ { //we loop over each contact. we are searching the contact with the next message with the smallest date,
+ // it will becomes our current contact, and the contact with the mext message with the second smallest
+ // date, this date will bocomes the limit.
+
+ QDomNode n;
+ if(m_currentElements.contains(*it))
+ n=m_currentElements[*it];
+ else //there is not yet "next message" register, so we will take the first (for the current month)
+ {
+ QDomDocument doc=getDocument(*it,m_currentMonth);
+ QDomElement docElem = doc.documentElement();
+ n= (sens==Chronological)?docElem.firstChild() : docElem.lastChild();
+
+ //i can't drop the root element
+ workaround.append(docElem);
+ }
+ while(!n.isNull())
+ {
+ QDomElement msgElem2 = n.toElement();
+ if( !msgElem2.isNull() && msgElem2.tagName()=="msg")
+ {
+ rxTime.search(msgElem2.attribute("time"));
+ QDate d=QDate::currentDate().addMonths(0-m_currentMonth);
+ QDateTime dt( QDate(d.year() , d.month() , rxTime.cap(1).toUInt()), QTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt(), rxTime.cap(5).toUInt() ) );
+ if(!timestamp.isValid() || ((sens==Chronological )? dt < timestamp : dt > timestamp) )
+ {
+ timeLimit=timestamp;
+ timestamp=dt;
+ msgElem=msgElem2;
+ currentContact=*it;
+
+ }
+ else if(!timeLimit.isValid() || ((sens==Chronological) ? timeLimit > dt : timeLimit < dt) )
+ {
+ timeLimit=dt;
+ }
+ break;
+ }
+ n=(sens==Chronological)? n.nextSibling() : n.previousSibling();
+ }
+ }
+ }
+ else //we don't have to merge the history. just take the next item in the contact
+ {
+ if(m_currentElements.contains(currentContact))
+ msgElem=m_currentElements[currentContact];
+ else
+ {
+ QDomDocument doc=getDocument(currentContact,m_currentMonth);
+ QDomElement docElem = doc.documentElement();
+ QDomNode n= (sens==Chronological)?docElem.firstChild() : docElem.lastChild();
+ msgElem=QDomElement();
+ while(!n.isNull()) //continue until we get a msg
+ {
+ msgElem=n.toElement();
+ if( !msgElem.isNull() && msgElem.tagName()=="msg")
+ {
+ m_currentElements[currentContact]=msgElem;
+ break;
+ }
+ n=(sens==Chronological)? n.nextSibling() : n.previousSibling();
+ }
+
+ //i can't drop the root element
+ workaround.append(docElem);
+ }
+ }
+
+
+ if(msgElem.isNull()) //we don't find ANY messages in any contact for this month. so we change the month
+ {
+ if(sens==Chronological)
+ {
+ if(m_currentMonth <= 0)
+ break; //there are no other messages to show. break even if we don't have nb messages
+ setCurrentMonth(m_currentMonth-1);
+ }
+ else
+ {
+ if(m_currentMonth >= getFirstMonth(c))
+ break; //we don't have any other messages to show
+ setCurrentMonth(m_currentMonth+1);
+ }
+ continue; //begin the loop from the bottom, and find currentContact and timeLimit again
+ }
+
+ while(
+ (messages.count() < lines) &&
+ !msgElem.isNull() &&
+ (!timestamp.isValid() || !timeLimit.isValid() ||
+ ((sens==Chronological) ? timestamp <= timeLimit : timestamp >= timeLimit)
+ ))
+ {
+ // break this loop, if we have reached the correct number of messages,
+ // if there are no more messages for this contact, or if we reached
+ // the timeLimit msgElem is the next message, still not parsed, so
+ // we parse it now
+
+ Kopete::Message::MessageDirection dir = (msgElem.attribute("in") == "1") ?
+ Kopete::Message::Inbound : Kopete::Message::Outbound;
+
+ if(!m_hideOutgoing || dir != Kopete::Message::Outbound)
+ { //parse only if we don't hide it
+
+ if( m_filter.isNull() || ( m_filterRegExp? msgElem.text().contains(QRegExp(m_filter,m_filterCaseSensitive)) : msgElem.text().contains(m_filter,m_filterCaseSensitive) ))
+ {
+ QString f=msgElem.attribute("from" );
+ const Kopete::Contact *from=(f.isNull() || !currentContact) ? 0L : currentContact->account()->contacts()[f];
+
+ if(!from)
+ from= dir==Kopete::Message::Inbound ? currentContact : currentContact->account()->myself();
+
+ Kopete::ContactPtrList to;
+ to.append( dir==Kopete::Message::Inbound ? currentContact->account()->myself() : currentContact );
+
+ if(!timestamp.isValid())
+ {
+ //parse timestamp only if it was not already parsed
+ rxTime.search(msgElem.attribute("time"));
+ QDate d=QDate::currentDate().addMonths(0-m_currentMonth);
+ timestamp=QDateTime( QDate(d.year() , d.month() , rxTime.cap(1).toUInt()), QTime( rxTime.cap(2).toUInt() , rxTime.cap(3).toUInt() , rxTime.cap(5).toUInt() ) );
+ }
+
+ Kopete::Message msg(timestamp, from, to, msgElem.text(), dir);
+ if (colorize)
+ {
+ msg.setBody( QString::fromLatin1("<span style=\"color:%1\" title=\"%2\">%3</span>")
+ .arg( fgColor.name(), timestamp.toString(Qt::LocalDate), msg.escapedBody() ),
+ Kopete::Message::RichText
+ );
+ msg.setFg( fgColor );
+ }
+ else
+ {
+ msg.setBody( QString::fromLatin1("<span title=\"%1\">%2</span>")
+ .arg( timestamp.toString(Qt::LocalDate), msg.escapedBody() ),
+ Kopete::Message::RichText
+ );
+ }
+
+ if(reverseOrder)
+ messages.prepend(msg);
+ else
+ messages.append(msg);
+ }
+ }
+
+ //here is the point of workaround. If i drop the root element, this crashes
+ //get the next message
+ QDomNode node = ( (sens==Chronological) ? msgElem.nextSibling() :
+ msgElem.previousSibling() );
+
+ msgElem = QDomElement(); //n.toElement();
+ while (!node.isNull() && msgElem.isNull())
+ {
+ msgElem = node.toElement();
+ if (!msgElem.isNull())
+ {
+ if (msgElem.tagName() == "msg")
+ {
+ if (!c && (m_metaContact->contacts().count() > 1))
+ {
+ // In case of hideoutgoing messages, it is faster to do
+ // this, so we don't parse the date if it is not needed
+ QRegExp rx("(\\d+) (\\d+):(\\d+):(\\d+)");
+ rx.search(msgElem.attribute("time"));
+
+ QDate d = QDate::currentDate().addMonths(0-m_currentMonth);
+ timestamp = QDateTime(
+ QDate(d.year(), d.month(), rx.cap(1).toUInt()),
+ QTime( rx.cap(2).toUInt(), rx.cap(3).toUInt() ) );
+ }
+ else
+ timestamp = QDateTime(); //invalid
+ }
+ else
+ msgElem = QDomElement();
+ }
+
+ node = (sens == Chronological) ? node.nextSibling() :
+ node.previousSibling();
+ }
+ m_currentElements[currentContact]=msgElem; //this is the next message
+ }
+ }
+
+ if(messages.count() < lines)
+ m_currentElements.clear(); //current elements are null this can't be allowed
+
+ return messages;
+}
+
+QString HistoryLogger::getFileName(const Kopete::Contact* c, QDate date)
+{
+
+ QString name = c->protocol()->pluginId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ QString::fromLatin1( "/" ) +
+ c->account()->accountId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ QString::fromLatin1( "/" ) +
+ c->contactId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ date.toString(".yyyyMM");
+
+ QString filename=locateLocal( "data", QString::fromLatin1( "kopete/logs/" ) + name+ QString::fromLatin1( ".xml" ) ) ;
+
+ //Check if there is a kopete 0.7.x file
+ QFileInfo fi(filename);
+ if(!fi.exists())
+ {
+ name = c->protocol()->pluginId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ QString::fromLatin1( "/" ) +
+ c->contactId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) ) +
+ date.toString(".yyyyMM");
+
+ QString filename2=locateLocal( "data", QString::fromLatin1( "kopete/logs/" ) + name+ QString::fromLatin1( ".xml" ) ) ;
+
+ QFileInfo fi2(filename2);
+ if(fi2.exists())
+ return filename2;
+ }
+
+ return filename;
+
+}
+
+unsigned int HistoryLogger::getFirstMonth(const Kopete::Contact *c)
+{
+ if(!c)
+ return getFirstMonth();
+
+ QRegExp rx( "\\.(\\d\\d\\d\\d)(\\d\\d)" );
+ QFileInfo *fi;
+
+ // BEGIN check if there are Kopete 0.7.x
+ QDir d1(locateLocal("data",QString("kopete/logs/")+
+ c->protocol()->pluginId().replace( QRegExp(QString::fromLatin1("[./~?*]")),QString::fromLatin1("-"))
+ ));
+ d1.setFilter( QDir::Files | QDir::NoSymLinks );
+ d1.setSorting( QDir::Name );
+
+ const QFileInfoList *list1 = d1.entryInfoList();
+ QFileInfoListIterator it1( *list1 );
+
+ while ( (fi = it1.current()) != 0 )
+ {
+ if(fi->fileName().contains(c->contactId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) )))
+ {
+ rx.search(fi->fileName());
+ int result = 12*(QDate::currentDate().year() - rx.cap(1).toUInt()) +QDate::currentDate().month() - rx.cap(2).toUInt();
+
+ if(result < 0)
+ {
+ kdWarning(14310) << k_funcinfo << "Kopete only found log file from Kopete 0.7.x made in the future. Check your date!" << endl;
+ break;
+ }
+ return result;
+ }
+ ++it1;
+ }
+ // END of kopete 0.7.x check
+
+
+ QDir d(locateLocal("data",QString("kopete/logs/")+
+ c->protocol()->pluginId().replace( QRegExp(QString::fromLatin1("[./~?*]")),QString::fromLatin1("-")) +
+ QString::fromLatin1( "/" ) +
+ c->account()->accountId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) )
+ ));
+
+ d.setFilter( QDir::Files | QDir::NoSymLinks );
+ d.setSorting( QDir::Name );
+
+ const QFileInfoList *list = d.entryInfoList();
+ QFileInfoListIterator it( *list );
+ while ( (fi = it.current()) != 0 )
+ {
+ if(fi->fileName().contains(c->contactId().replace( QRegExp( QString::fromLatin1( "[./~?*]" ) ), QString::fromLatin1( "-" ) )))
+ {
+ rx.search(fi->fileName());
+ int result = 12*(QDate::currentDate().year() - rx.cap(1).toUInt()) +QDate::currentDate().month() - rx.cap(2).toUInt();
+ if(result < 0)
+ {
+ kdWarning(14310) << k_funcinfo << "Kopete only found log file made in the future. Check your date!" << endl;
+ break;
+ }
+ return result;
+ }
+ ++it;
+ }
+ return 0;
+}
+
+unsigned int HistoryLogger::getFirstMonth()
+{
+ if(m_cachedMonth!=-1)
+ return m_cachedMonth;
+
+ if(!m_metaContact)
+ return 0;
+
+ int m=0;
+ QPtrList<Kopete::Contact> contacts=m_metaContact->contacts();
+ QPtrListIterator<Kopete::Contact> it( contacts );
+ for( ; it.current(); ++it )
+ {
+ int m2=getFirstMonth(*it);
+ if(m2>m) m=m2;
+ }
+ m_cachedMonth=m;
+ return m;
+}
+
+void HistoryLogger::setHideOutgoing(bool b)
+{
+ m_hideOutgoing = b;
+}
+
+void HistoryLogger::slotMCDeleted()
+{
+ m_metaContact = 0;
+}
+
+void HistoryLogger::setFilter(const QString& filter, bool caseSensitive , bool isRegExp)
+{
+ m_filter=filter;
+ m_filterCaseSensitive=caseSensitive;
+ m_filterRegExp=isRegExp;
+}
+
+QString HistoryLogger::filter() const
+{
+ return m_filter;
+}
+
+bool HistoryLogger::filterCaseSensitive() const
+{
+ return m_filterCaseSensitive;
+}
+
+bool HistoryLogger::filterRegExp() const
+{
+ return m_filterRegExp;
+}
+
+QValueList<int> HistoryLogger::getDaysForMonth(QDate date)
+{
+ QRegExp rxTime("time=\"(\\d+) \\d+:\\d+(:\\d+)?\""); //(with a 0.7.x compatibility)
+
+ QValueList<int> dayList;
+
+ QPtrList<Kopete::Contact> contacts = m_metaContact->contacts();
+ QPtrListIterator<Kopete::Contact> it(contacts);
+
+ int lastDay=0;
+ for(; it.current(); ++it)
+ {
+// kdDebug() << getFileName(*it, date) << endl;
+ QFile file(getFileName(*it, date));
+ if(!file.open(IO_ReadOnly))
+ {
+ continue;
+ }
+ QTextStream stream(&file);
+ QString fullText = stream.read();
+ file.close();
+
+ int pos = 0;
+ while( (pos = rxTime.search(fullText, pos)) != -1)
+ {
+ pos += rxTime.matchedLength();
+ int day=rxTime.capturedTexts()[1].toInt();
+
+ if ( day !=lastDay && dayList.find(day) == dayList.end()) // avoid duplicates
+ {
+ dayList.append(rxTime.capturedTexts()[1].toInt());
+ lastDay=day;
+ }
+ }
+ }
+ return dayList;
+}
+
+#include "historylogger.moc"
diff --git a/kopete/plugins/history/historylogger.h b/kopete/plugins/history/historylogger.h
new file mode 100644
index 00000000..85cdbdd7
--- /dev/null
+++ b/kopete/plugins/history/historylogger.h
@@ -0,0 +1,217 @@
+/*
+ historylogger.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef HISTORYLOGGER_H
+#define HISTORYLOGGER_H
+
+#include <qobject.h>
+#include "kopetemessage.h" //TODO: REMOVE
+
+namespace Kopete { class Contact; }
+namespace Kopete { class MetaContact; }
+class QFile;
+class QDomDocument;
+class QTimer;
+
+/**
+ * One hinstance of this class is opened for every Kopete::ChatSession,
+ * or for the history dialog
+ *
+ * @author Olivier Goffart
+ */
+class HistoryLogger : public QObject
+{
+Q_OBJECT
+public:
+
+ /**
+ * - Chronological: messages are read from the first to the last, in the time order
+ * - AntiChronological: messages are read from the last to the first, in the time reversed order
+ */
+ enum Sens { Default , Chronological , AntiChronological };
+
+ /**
+ * Constructor, takes the contact, and the color of messages
+ */
+ HistoryLogger( Kopete::MetaContact *m , QObject *parent = 0, const char *name = 0);
+ HistoryLogger( Kopete::Contact *c , QObject *parent = 0, const char *name = 0);
+
+
+ ~HistoryLogger();
+
+ /**
+ * return or setif yes or no outgoing message are hidden (and not parsed)
+ */
+ bool hideOutgoing() const { return m_hideOutgoing; }
+ void setHideOutgoing(bool);
+
+ /**
+ * set a searching filter
+ * @param filter is the string to search
+ * @param caseSensitive say if the case is important
+ * @param isRegExp say if the filter is a QRegExp, or a simle string
+ */
+ void setFilter(const QString& filter, bool caseSensitive=false , bool isRegExp=false);
+ QString filter() const;
+ bool filterCaseSensitive() const ;
+ bool filterRegExp() const;
+
+
+
+ //----------------------------------
+
+ /**
+ * log a message
+ * @param c add a presision to the contact to use, if null, autodetect.
+ */
+ void appendMessage( const Kopete::Message &msg , const Kopete::Contact *c=0L );
+
+ /**
+ * read @param lines message from the current position
+ * from Kopete::Contact @param c in the given @param sens
+ */
+ QValueList<Kopete::Message> readMessages(unsigned int lines,
+ const Kopete::Contact *c=0, Sens sens=Default,
+ bool reverseOrder=false, bool colorize=true);
+
+ /**
+ * Same as the following, but for one date. I did'nt reuse the above function
+ * because its structure is really different.
+ * Read all the messages for the given @param date
+ */
+ QValueList<Kopete::Message> readMessages(QDate date);
+
+
+ /**
+ * The pausition is set to the last message
+ */
+ void setPositionToLast();
+
+ /**
+ * The position is set to the first message
+ */
+ void setPositionToFirst();
+
+ /**
+ * Set the current month (in number of month since the actual month)
+ */
+ void setCurrentMonth(int month);
+
+ /**
+ * @return The list of the days for which there is a log for m_metaContact for month of * @param date (don't care of the day)
+ */
+ QValueList<int> getDaysForMonth(QDate date);
+
+ /**
+ * Get the filename of the xml file which contains the history from the
+ * contact in the specified @param date. Specify @param date in order to get the filename for
+ * the given date.year() date.month().
+ */
+ static QString getFileName(const Kopete::Contact* , QDate date);
+
+private:
+ bool m_hideOutgoing;
+ bool m_filterCaseSensitive;
+ bool m_filterRegExp;
+ QString m_filter;
+
+
+ /*
+ *contais all QDomDocument, for a KC, for a specified Month
+ */
+ QMap<const Kopete::Contact*,QMap<unsigned int , QDomDocument> > m_documents;
+
+ /**
+ * Contains the current message.
+ * in fact, this is the next, still not showed
+ */
+ QMap<const Kopete::Contact*, QDomElement> m_currentElements;
+
+ /**
+ * Get the document, open it is @param canload is true, contain is set to false if the document
+ * is not already contained
+ */
+ QDomDocument getDocument(const Kopete::Contact *c, unsigned int month , bool canLoad=true , bool* contain=0L);
+
+ QDomDocument getDocument(const Kopete::Contact *c, const QDate date, bool canLoad=true, bool* contain=0L);
+
+ /**
+ * look over files to get the last month for this contact
+ */
+ unsigned int getFirstMonth(const Kopete::Contact *c);
+ unsigned int getFirstMonth();
+
+
+ /*
+ * the current month
+ */
+ unsigned int m_currentMonth;
+
+ /*
+ * the cached getFirstMonth
+ */
+ int m_cachedMonth;
+
+
+
+ /*
+ * the metacontact we are using
+ */
+ Kopete::MetaContact *m_metaContact;
+
+ /*
+ * keep the old position in memory, so if we change the sens, we can begin here
+ */
+ QMap<const Kopete::Contact*, QDomElement> m_oldElements;
+ unsigned int m_oldMonth;
+ Sens m_oldSens;
+
+ /**
+ * the timer used to save the file
+ */
+ QTimer *m_saveTimer;
+ QDomDocument m_toSaveDocument;
+ QString m_toSaveFileName;
+ unsigned int m_saveTimerTime; //time in ms between each save
+
+ /**
+ * workaround for the 31 midnight bug.
+ * it contains the number of the current month.
+ */
+ int m_realMonth;
+
+ /*
+ * FIXME:
+ * WORKAROUND
+ * due to a bug in QT, i have to keep the document element in the memory to
+ * prevent crashes
+ */
+ QValueList<QDomElement> workaround;
+
+private slots:
+ /**
+ * the metacontact has been deleted
+ */
+ void slotMCDeleted();
+
+ /**
+ * save the current month's document on the disk.
+ * connected to the m_saveTimer signal
+ */
+ void saveToDisk();
+};
+
+#endif
diff --git a/kopete/plugins/history/historyplugin.cpp b/kopete/plugins/history/historyplugin.cpp
new file mode 100644
index 00000000..bf8d70b4
--- /dev/null
+++ b/kopete/plugins/history/historyplugin.cpp
@@ -0,0 +1,194 @@
+/*
+ historyplugin.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+ (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kgenericfactory.h>
+#include <kaboutdata.h>
+#include <kaction.h>
+#include <kmessagebox.h>
+//#include <kconfig.h>
+#include <kplugininfo.h>
+#include <kdeversion.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopeteview.h"
+#include "kopetecontactlist.h"
+#include "kopeteuiglobal.h"
+#include "kopetemessageevent.h"
+#include "kopeteviewplugin.h"
+
+#include "historydialog.h"
+#include "historyplugin.h"
+#include "historylogger.h"
+#include "historyguiclient.h"
+#include "historyconfig.h"
+
+typedef KGenericFactory<HistoryPlugin> HistoryPluginFactory;
+static const KAboutData aboutdata("kopete_history", I18N_NOOP("History") , "1.0" );
+K_EXPORT_COMPONENT_FACTORY( kopete_history, HistoryPluginFactory( &aboutdata ) )
+
+HistoryPlugin::HistoryPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Plugin( HistoryPluginFactory::instance(), parent, name ), m_loggerFactory( this )
+{
+ KAction *viewMetaContactHistory = new KAction( i18n("View &History" ),
+ QString::fromLatin1( "history" ), 0, this, SLOT(slotViewHistory()),
+ actionCollection(), "viewMetaContactHistory" );
+ viewMetaContactHistory->setEnabled(
+ Kopete::ContactList::self()->selectedMetaContacts().count() == 1 );
+
+ connect(Kopete::ContactList::self(), SIGNAL(metaContactSelected(bool)),
+ viewMetaContactHistory, SLOT(setEnabled(bool)));
+
+ connect(Kopete::ChatSessionManager::self(), SIGNAL(viewCreated(KopeteView*)),
+ this, SLOT(slotViewCreated(KopeteView*)));
+
+ connect(this, SIGNAL(settingsChanged()), this, SLOT(slotSettingsChanged()));
+
+ setXMLFile("historyui.rc");
+ if(detectOldHistory())
+ {
+ if(
+ KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(),
+ i18n( "Old history files from Kopete 0.6.x or older has been detected.\n"
+ "Do you want to import and convert it to the new history format?" ),
+ i18n( "History Plugin" ), i18n("Import && Convert"), i18n("Do Not Import") ) == KMessageBox::Yes )
+ {
+ convertOldHistory();
+ }
+ }
+
+ // Add GUI action to all existing kmm objects
+ // (Needed if the plugin is enabled while kopete is already running)
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ {
+ if(!m_loggers.contains(*it))
+ {
+ m_loggers.insert(*it, new HistoryGUIClient( *it ) );
+ connect( *it, SIGNAL(closing(Kopete::ChatSession*)),
+ this, SLOT(slotKMMClosed(Kopete::ChatSession*)));
+ }
+ }
+}
+
+
+HistoryPlugin::~HistoryPlugin()
+{
+}
+
+
+void HistoryMessageLogger::handleMessage( Kopete::MessageEvent *event )
+{
+ history->messageDisplayed( event->message() );
+ MessageHandler::handleMessage( event );
+}
+
+void HistoryPlugin::messageDisplayed(const Kopete::Message &m)
+{
+ if(m.direction()==Kopete::Message::Internal || !m.manager())
+ return;
+
+ if(!m_loggers.contains(m.manager()))
+ {
+ m_loggers.insert(m.manager() , new HistoryGUIClient( m.manager() ) );
+ connect(m.manager(), SIGNAL(closing(Kopete::ChatSession*)),
+ this, SLOT(slotKMMClosed(Kopete::ChatSession*)));
+ }
+
+ HistoryLogger *l=m_loggers[m.manager()]->logger();
+ if(l)
+ {
+ QPtrList<Kopete::Contact> mb=m.manager()->members();
+ l->appendMessage(m,mb.first());
+ }
+
+ m_lastmessage=m;
+}
+
+
+void HistoryPlugin::slotViewHistory()
+{
+ Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();
+ if(m)
+ {
+ int lines = HistoryConfig::number_ChatWindow();
+
+ // TODO: Keep track of open dialogs and raise instead of
+ // opening a new (duplicated) one
+ new HistoryDialog(m);
+ }
+}
+
+
+void HistoryPlugin::slotViewCreated( KopeteView* v )
+{
+ if(v->plugin()->pluginInfo()->pluginName() != QString::fromLatin1("kopete_chatwindow") )
+ return; //Email chat windows are not supported.
+
+ bool autoChatWindow = HistoryConfig::auto_chatwindow();
+ int nbAutoChatWindow = HistoryConfig::number_Auto_chatwindow();
+
+ KopeteView *m_currentView = v;
+ Kopete::ChatSession *m_currentChatSession = v->msgManager();
+ QPtrList<Kopete::Contact> mb = m_currentChatSession->members();
+
+ if(!m_currentChatSession)
+ return; //i am sorry
+
+ if(!m_loggers.contains(m_currentChatSession))
+ {
+ m_loggers.insert(m_currentChatSession , new HistoryGUIClient( m_currentChatSession ) );
+ connect( m_currentChatSession, SIGNAL(closing(Kopete::ChatSession*)),
+ this , SLOT(slotKMMClosed(Kopete::ChatSession*)));
+ }
+
+ if(!autoChatWindow || nbAutoChatWindow == 0)
+ return;
+
+ HistoryLogger *logger = m_loggers[m_currentChatSession]->logger();
+
+ logger->setPositionToLast();
+
+ QValueList<Kopete::Message> msgs = logger->readMessages(nbAutoChatWindow,
+ /*mb.first()*/ 0L, HistoryLogger::AntiChronological, true, true);
+
+ // make sure the last message is not the one which will be appened right
+ // after the view is created (and which has just been logged in)
+ if(
+ (msgs.last().plainBody() == m_lastmessage.plainBody()) &&
+ (m_lastmessage.manager() == m_currentChatSession))
+ {
+ msgs.remove(msgs.fromLast());
+ }
+
+ m_currentView->appendMessages( msgs );
+}
+
+
+void HistoryPlugin::slotKMMClosed( Kopete::ChatSession* kmm)
+{
+ m_loggers[kmm]->deleteLater();
+ m_loggers.remove(kmm);
+}
+
+void HistoryPlugin::slotSettingsChanged()
+{
+ kdDebug(14310) << k_funcinfo << "RELOADING CONFIG" << endl;
+ HistoryConfig::self()->readConfig();
+}
+
+#include "historyplugin.moc"
diff --git a/kopete/plugins/history/historyplugin.h b/kopete/plugins/history/historyplugin.h
new file mode 100644
index 00000000..63e2c87b
--- /dev/null
+++ b/kopete/plugins/history/historyplugin.h
@@ -0,0 +1,106 @@
+/*
+ historyplugin.h
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart at kde.org>
+ (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef HISTORYPLUGIN_H
+#define HISTORYPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+
+#include "kopeteplugin.h"
+
+#include "kopetemessage.h"
+#include "kopetemessagehandler.h"
+
+class KopeteView;
+class KActionCollection;
+
+namespace Kopete
+{
+class MetaContact;
+class ChatSession;
+}
+
+class HistoryPreferences;
+class HistoryGUIClient;
+class HistoryPlugin;
+
+/**
+ * @author Richard Smith
+ */
+class HistoryMessageLogger : public Kopete::MessageHandler
+{
+ HistoryPlugin *history;
+public:
+ HistoryMessageLogger( HistoryPlugin *history ) : history(history) {}
+ void handleMessage( Kopete::MessageEvent *event );
+};
+
+class HistoryMessageLoggerFactory : public Kopete::MessageHandlerFactory
+{
+ HistoryPlugin *history;
+public:
+ HistoryMessageLoggerFactory( HistoryPlugin *history ) : history(history) {}
+ Kopete::MessageHandler *create( Kopete::ChatSession * /*manager*/, Kopete::Message::MessageDirection direction )
+ {
+ if( direction != Kopete::Message::Inbound )
+ return 0;
+ return new HistoryMessageLogger(history);
+ }
+ int filterPosition( Kopete::ChatSession *, Kopete::Message::MessageDirection )
+ {
+ return Kopete::MessageHandlerFactory::InStageToSent+5;
+ }
+};
+
+/**
+ * @author Olivier Goffart
+ */
+class HistoryPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+ public:
+ HistoryPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~HistoryPlugin();
+
+ /**
+ * convert the Kopete 0.6 / 0.5 history to the new format
+ */
+ static void convertOldHistory();
+ /**
+ * return true if an old history has been detected, and no new ones
+ */
+ static bool detectOldHistory();
+
+ void messageDisplayed(const Kopete::Message &msg);
+
+ private slots:
+ void slotViewCreated( KopeteView* );
+ void slotViewHistory();
+ void slotKMMClosed( Kopete::ChatSession* );
+ void slotSettingsChanged();
+
+ private:
+ HistoryMessageLoggerFactory m_loggerFactory;
+ QMap<Kopete::ChatSession*,HistoryGUIClient*> m_loggers;
+ Kopete::Message m_lastmessage;
+};
+
+#endif
+
+
diff --git a/kopete/plugins/history/historypreferences.cpp b/kopete/plugins/history/historypreferences.cpp
new file mode 100644
index 00000000..61fce469
--- /dev/null
+++ b/kopete/plugins/history/historypreferences.cpp
@@ -0,0 +1,88 @@
+/*
+ historypreferences.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "historypreferences.h"
+#include "historyconfig.h"
+#include "historyprefsui.h"
+
+#include <kgenericfactory.h>
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <kcolorbutton.h>
+#include <knuminput.h>
+#include <qcheckbox.h>
+
+typedef KGenericFactory<HistoryPreferences> HistoryConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_history, HistoryConfigFactory( "kcm_kopete_history" ) )
+
+HistoryPreferences::HistoryPreferences(QWidget *parent, const char*/*name*/, const QStringList &args)
+ : KCModule(HistoryConfigFactory::instance(), parent, args)
+{
+ kdDebug(14310) << k_funcinfo << "called." << endl;
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ p = new HistoryPrefsUI(this);
+
+ connect(p->chkShowPrevious, SIGNAL(toggled(bool)), this, SLOT(slotShowPreviousChanged(bool)));
+ connect(p->Number_Auto_chatwindow, SIGNAL(valueChanged(int)),
+ this, SLOT(slotModified()));
+ connect(p->Number_ChatWindow, SIGNAL(valueChanged(int)),
+ this, SLOT(slotModified()));
+ connect(p->History_color, SIGNAL(changed(const QColor&)),
+ this, SLOT(slotModified()));
+ load();
+}
+
+HistoryPreferences::~HistoryPreferences()
+{
+ kdDebug(14310) << k_funcinfo << "called." << endl;
+}
+
+void HistoryPreferences::load()
+{
+ kdDebug(14310) << k_funcinfo << "called." << endl;
+ HistoryConfig::self()->readConfig();
+ p->chkShowPrevious->setChecked(HistoryConfig::auto_chatwindow());
+ slotShowPreviousChanged(p->chkShowPrevious->isChecked());
+ p->Number_Auto_chatwindow->setValue(HistoryConfig::number_Auto_chatwindow());
+ p->Number_ChatWindow->setValue(HistoryConfig::number_ChatWindow());
+ p->History_color->setColor(HistoryConfig::history_color());
+ //p-> HistoryConfig::browserStyle();
+ emit KCModule::changed(false);
+}
+
+void HistoryPreferences::save()
+{
+ kdDebug(14310) << k_funcinfo << "called." << endl;
+ HistoryConfig::setAuto_chatwindow(p->chkShowPrevious->isChecked());
+ HistoryConfig::setNumber_Auto_chatwindow(p->Number_Auto_chatwindow->value());
+ HistoryConfig::setNumber_ChatWindow(p->Number_ChatWindow->value());
+ HistoryConfig::setHistory_color(p->History_color->color());
+ HistoryConfig::self()->writeConfig();
+ emit KCModule::changed(false);
+}
+
+void HistoryPreferences::slotModified()
+{
+ emit KCModule::changed(true);
+}
+
+void HistoryPreferences::slotShowPreviousChanged(bool on)
+{
+ emit KCModule::changed(true);
+}
+
+#include "historypreferences.moc"
diff --git a/kopete/plugins/history/historypreferences.h b/kopete/plugins/history/historypreferences.h
new file mode 100644
index 00000000..247e2bc8
--- /dev/null
+++ b/kopete/plugins/history/historypreferences.h
@@ -0,0 +1,48 @@
+/*
+ historypreferences.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef HISTORYPREFERENCES_H
+#define HISTORYPREFERENCES_H
+
+#include <kcmodule.h>
+#include <qstring.h>
+
+class HistoryPrefsUI;
+
+/**
+ * @author Stefan Gehn
+ */
+class HistoryPreferences : public KCModule
+{
+ Q_OBJECT
+ public:
+ HistoryPreferences(QWidget *parent=0, const char* name=0,
+ const QStringList &args = QStringList());
+ ~HistoryPreferences();
+
+ virtual void save();
+ virtual void load();
+
+ private slots:
+ void slotModified();
+ void slotShowPreviousChanged(bool);
+
+ private:
+ HistoryPrefsUI *p;
+};
+
+#endif
diff --git a/kopete/plugins/history/historyprefsui.ui b/kopete/plugins/history/historyprefsui.ui
new file mode 100644
index 00000000..5942a07a
--- /dev/null
+++ b/kopete/plugins/history/historyprefsui.ui
@@ -0,0 +1,187 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>HistoryPrefsUI</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HistoryPrefsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>363</width>
+ <height>212</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>HistoryPrefsWidget</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>grpChatHistory</cstring>
+ </property>
+ <property name="title">
+ <string>Chat History</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lblNoLinesPerPage</cstring>
+ </property>
+ <property name="text">
+ <string>Number of messages per page:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The number of messages that are shown when browsing history in the chat window</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="3" column="1">
+ <property name="name">
+ <cstring>Number_ChatWindow</cstring>
+ </property>
+ <property name="maxValue">
+ <number>32768</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The number of message that are shown when borwsing history in the chat window</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>colorLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Color of messages:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>History_color</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Color of history messages in the chat window</string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>History_color</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="color">
+ <color>
+ <red>170</red>
+ <green>170</green>
+ <blue>127</blue>
+ </color>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Color of history messages in the chat window</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>Number_Auto_chatwindow</cstring>
+ </property>
+ <property name="maxValue">
+ <number>32768</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>7</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the number of messages that will be added automatically in the chat window when opening a new chat.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>numberLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Number of messages to show:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>Number_Auto_chatwindow</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the number of messages that will be added automatically in the chat window when opening a new chat.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>chkShowPrevious</cstring>
+ </property>
+ <property name="text">
+ <string>Show chat history in new chats</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When a new chat is opened, automatically add the last few messages between you and that contact.</string>
+ </property>
+ </widget>
+ </grid>
+ </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>31</width>
+ <height>90</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>chkShowPrevious</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>numberLabel</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkShowPrevious</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>Number_Auto_chatwindow</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>chkShowPrevious</tabstop>
+ <tabstop>Number_Auto_chatwindow</tabstop>
+ <tabstop>History_color</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/history/historyui.rc b/kopete/plugins/history/historyui.rc
new file mode 100644
index 00000000..5f72b22c
--- /dev/null
+++ b/kopete/plugins/history/historyui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_history" version="1">
+ <MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="viewMetaContactHistory" />
+ </Menu>
+ </MenuBar>
+ <Menu name="contact_popup">
+ <Action name="viewMetaContactHistory" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/history/historyviewer.ui b/kopete/plugins/history/historyviewer.ui
new file mode 100644
index 00000000..4cef647e
--- /dev/null
+++ b/kopete/plugins/history/historyviewer.ui
@@ -0,0 +1,347 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>HistoryViewer</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HistoryViewer</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>682</width>
+ <height>634</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>200</height>
+ </size>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>statusLabel</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Ready</string>
+ </property>
+ </widget>
+ <widget class="KProgress">
+ <property name="name">
+ <cstring>searchProgress</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>searchErase</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Search:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>searchLine</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>searchButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Se&amp;arch</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QSplitter" row="1" column="0">
+ <property name="name">
+ <cstring>splitter2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListViewSearchLine">
+ <property name="name">
+ <cstring>dateSearchLine</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="minimumSize">
+ <size>
+ <width>140</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Date</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Contact</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>dateListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>htmlFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>10</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>WinPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Contact:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>contactComboBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Message Filter:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>All messages</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Only incoming</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Only outgoing</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>messageFilterBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kprogress.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klistviewsearchline.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/history/kopete_history.desktop b/kopete/plugins/history/kopete_history.desktop
new file mode 100644
index 00000000..5f14aee0
--- /dev/null
+++ b/kopete/plugins/history/kopete_history.desktop
@@ -0,0 +1,139 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=history
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_history
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_history
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+Name=History
+Name[ar]=محÙوظات
+Name[az]=Keçmiş
+Name[be]=ГіÑторыÑ
+Name[bg]=ИÑториÑ
+Name[bn]=ইতিহাস
+Name[br]=Istor
+Name[bs]=Historija
+Name[ca]=Historial
+Name[cs]=Historie
+Name[cy]=Hanes
+Name[da]=Historik
+Name[de]=Verlauf
+Name[el]=ΙστοÏικό
+Name[eo]=Historio
+Name[es]=Historia
+Name[et]=Ajalugu
+Name[eu]=Historia
+Name[fa]=تاریخچه
+Name[fi]=Historia
+Name[fr]=Historique
+Name[ga]=Stair
+Name[gl]=Historial
+Name[he]=היסטוריה
+Name[hi]=इतिहास
+Name[hr]=Povijest
+Name[hu]=Üzenetnapló
+Name[id]=Sejarah
+Name[is]=Ferill
+Name[it]=Cronologia
+Name[ja]=履歴
+Name[ka]=ისტáƒáƒ áƒ˜áƒ
+Name[kk]=Журнал
+Name[km]=ប្រវážáŸ’ážáž·
+Name[lt]=Istorija
+Name[lv]=VÄ“sture
+Name[mk]=ИÑторија
+Name[mt]=Kronoloġija
+Name[nb]=Historie
+Name[nds]=Vörgeschicht
+Name[ne]=इतिहास
+Name[nl]=Geschiedenis
+Name[nn]=Historie
+Name[pa]=ਅਤੀਤ
+Name[pl]=Historia
+Name[pt]=Histórico
+Name[pt_BR]=História
+Name[ro]=Istoric
+Name[ru]=Журнал разговоров
+Name[rw]=Amateka
+Name[se]=Historihkka
+Name[sk]=História
+Name[sl]=Zgodovina
+Name[sr]=ИÑторија
+Name[sr@Latn]=Istorija
+Name[sv]=Historik
+Name[ta]=வரலாறà¯
+Name[tg]=ÐомнавиÑи Ñӯҳбатҳо
+Name[th]=ประวัติà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™
+Name[tr]=Geçmiş
+Name[uk]=ІÑторіÑ
+Name[uz]=Tarix
+Name[uz@cyrillic]=Тарих
+Name[ven]=Divhazwakale
+Name[wa]=Istwere
+Name[xh]=Imbali
+Name[zh_CN]=历å²
+Name[zh_HK]=歷程紀錄
+Name[zh_TW]=æ­·å²
+Name[zu]=Umlando
+Comment=Log all messages to keep track of your conversations
+Comment[ar]=سجل جميع الرسائل للمحاÙظة على محادثاتك
+Comment[be]=ЗапіÑваць уÑе паведамленні Ð´Ð»Ñ ÑтварÑÐ½Ð½Ñ Ð´Ð·Ñ‘Ð½Ð½Ñ–ÐºÐ°Ñž гутарак
+Comment[bg]=Ð—Ð°Ð¿Ð¸Ñ Ð½Ð° вÑички ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ñ Ñ†ÐµÐ» преглед и Ñ‚ÑŠÑ€Ñене в Ñ‚ÑÑ… в бъдеще
+Comment[bn]=আপনার কথোপকথনের খতিয়ান রাখতে সব বারà§à¦¤à¦¾ কারà§à¦¯à¦¬à¦¿à¦¬à¦°à¦£à§€à¦¤à§‡ লিখে রাখে
+Comment[bs]=Zapiši sve poruke u historiju
+Comment[ca]=Registra tots els missatges per seguir les vostres converses
+Comment[cs]=Záznam konverzace
+Comment[cy]=Cofnodi pob neges er mwyn cadw trefn ar eich sgwrsiau
+Comment[da]=Log alle beskeder for at holde styr på dine konversationer
+Comment[de]=Protokolliert alle Nachrichten der eigenen Gespräche
+Comment[el]=ΚαταγÏάψτε όλα τα μηνÏματά σας για να διατηÏήσετε αÏχείο με τις συζητήσεις σας
+Comment[es]=Registra todos los mensajes para guardar sus conversaciones
+Comment[et]=Kõigi sõnumite logimine, et neil ka hiljem silm peal hoida
+Comment[eu]=Gorde mezu guztiak zure elkarrizketak jarrai ahal ditzazun
+Comment[fa]=برای ردگیری مکالمات خود همۀ پیامها را ثبت کنید
+Comment[fi]=Laita kaikki viestisi lokiin
+Comment[fr]=Enregistrer tous les messages pour conserver une trace de vos discussions
+Comment[gl]=Rexitra tódolas mensajex para gardar conversacións
+Comment[he]=שומר תיעוד מסודר שלך כל שיחותיך
+Comment[hi]=आपके वारà¥à¤¤à¤¾à¤²à¤¾à¤ª की जानकारी बनाठरखने के लिठसभी संदेशों को लॉग करें
+Comment[hr]=Upisuje u dnevnik sve poruke kako biste vodili evidenciju o svojim razgovorima
+Comment[hu]=Az üzenetek archiválása
+Comment[is]=Halda til haga samskiptaannál
+Comment[it]=Effettua il log di tutti i messaggi in modo da avere traccia delle tue conversazioni
+Comment[ja]=会話を残ã™ãŸã‚ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®ãƒ­ã‚°ã‚’å–ã‚‹
+Comment[ka]=ყველრშეტყáƒáƒ‘ინების ჟურნáƒáƒšáƒ˜áƒ áƒ”ბრთქვენი სáƒáƒ£áƒ‘რების ჩáƒáƒ¡áƒáƒ¬áƒ”რáƒáƒ“
+Comment[kk]=ХабарлаÑу барыÑын журналға жазып отыру
+Comment[km]=ចុះ​កំណážáŸ‹â€‹áž áŸážáž»â€‹ážŸáž¶ážšâ€‹áž‘ាំងអស់ ដើម្បី​ážáž¶áž˜ážŠáž¶áž“​ការ​សន្ទនា​របស់​អ្នក
+Comment[lt]=Įrašinėti visas žinutes ir vesti pokalbių žurnalą
+Comment[mk]=Ги зачувува Ñите пораки за да ги Ñледите вашите разговори
+Comment[nb]=Logg alle meldinger for å ta vare på samtalene dine
+Comment[nds]=All Narichten för't Nakieken na't Logbook schrieven
+Comment[ne]=तपाईà¤à¤•à¥‹ वारà¥à¤¤à¤¾à¤²à¤¾à¤ªà¤•à¥‹ टà¥à¤°à¤¯à¤¾à¤• राखà¥à¤¨ सबै सनà¥à¤¦à¥‡à¤¶ लग गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Bewaar alle berichten in een logboek om uw conversaties later opnieuw te kunnen bekijken
+Comment[nn]=Logg alle meldingar for å ta vare på samtalane dine
+Comment[pl]=Zapisuje wszystkie wiadomości, aby trzymać historię Twoich rozmów
+Comment[pt]=Regista todas as mensagens para manter um registo da sua conversa
+Comment[pt_BR]=Registra todas as mensagens para manter o histórico de suas conversações
+Comment[ru]=Делать запиÑи ваших разговоров в журнале
+Comment[se]=Vurke buot dieđáhusaid vai oaidnit du ságastallamiid
+Comment[sk]=Záznam všetkých správ, aby ste mohli sledovať vaše rozhovory
+Comment[sl]=Beleži vsa sporoÄila za hranjenje vaÅ¡ih pogovorov
+Comment[sr]=УпиÑује у дневник Ñве поруке да би Ñте водили евиденцију о Ñвојим разговорима
+Comment[sr@Latn]=Upisuje u dnevnik sve poruke da bi ste vodili evidenciju o svojim razgovorima
+Comment[sv]=Logga alla meddelanden för att hålla ordning på samtalen
+Comment[ta]=உஙà¯à®•à®³à¯ உரையாடலை கவனிகà¯à®• அனைதà¯à®¤à¯ செயà¯à®¤à®¿à®•à®³à¯ˆà®¯à¯à®®à¯ பà¯à®•à¯à®ªà®¤à®¿
+Comment[tg]=Сабти ҳамаи пайёмҳо барои пайгардии ҳамаи Ñӯҳбатҳои шумо
+Comment[tr]=Konuşmalarınızın kaydedildiği bütün günlük mesajları
+Comment[uk]=Робити запиÑи в журналі Ð´Ð»Ñ ÑÐ»Ñ–Ð´ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° вашими розмовами
+Comment[wa]=Wårder on djournå di tos vos messaedjes, po vos poleur rivey li conversåcion
+Comment[zh_CN]=记录您对è¯çš„全部消æ¯
+Comment[zh_HK]=記錄所有訊æ¯ï¼Œè®“您能追查您的å°è©±ç´€éŒ„
+Comment[zh_TW]=紀錄所有å°è©±è¨Šæ¯
diff --git a/kopete/plugins/history/kopete_history_config.desktop b/kopete/plugins/history/kopete_history_config.desktop
new file mode 100644
index 00000000..5ee2d6b2
--- /dev/null
+++ b/kopete/plugins/history/kopete_history_config.desktop
@@ -0,0 +1,141 @@
+[Desktop Entry]
+Icon=history
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_history
+X-KDE-FactoryName=HistoryConfigFactory
+X-KDE-ParentApp=kopete_history
+X-KDE-ParentComponents=kopete_history
+
+Name=History
+Name[ar]=محÙوظات
+Name[az]=Keçmiş
+Name[be]=ГіÑторыÑ
+Name[bg]=ИÑториÑ
+Name[bn]=ইতিহাস
+Name[br]=Istor
+Name[bs]=Historija
+Name[ca]=Historial
+Name[cs]=Historie
+Name[cy]=Hanes
+Name[da]=Historik
+Name[de]=Verlauf
+Name[el]=ΙστοÏικό
+Name[eo]=Historio
+Name[es]=Historia
+Name[et]=Ajalugu
+Name[eu]=Historia
+Name[fa]=تاریخچه
+Name[fi]=Historia
+Name[fr]=Historique
+Name[ga]=Stair
+Name[gl]=Historial
+Name[he]=היסטוריה
+Name[hi]=इतिहास
+Name[hr]=Povijest
+Name[hu]=Üzenetnapló
+Name[id]=Sejarah
+Name[is]=Ferill
+Name[it]=Cronologia
+Name[ja]=履歴
+Name[ka]=ისტáƒáƒ áƒ˜áƒ
+Name[kk]=Журнал
+Name[km]=ប្រវážáŸ’ážáž·
+Name[lt]=Istorija
+Name[lv]=VÄ“sture
+Name[mk]=ИÑторија
+Name[mt]=Kronoloġija
+Name[nb]=Historie
+Name[nds]=Vörgeschicht
+Name[ne]=इतिहास
+Name[nl]=Geschiedenis
+Name[nn]=Historie
+Name[pa]=ਅਤੀਤ
+Name[pl]=Historia
+Name[pt]=Histórico
+Name[pt_BR]=História
+Name[ro]=Istoric
+Name[ru]=Журнал разговоров
+Name[rw]=Amateka
+Name[se]=Historihkka
+Name[sk]=História
+Name[sl]=Zgodovina
+Name[sr]=ИÑторија
+Name[sr@Latn]=Istorija
+Name[sv]=Historik
+Name[ta]=வரலாறà¯
+Name[tg]=ÐомнавиÑи Ñӯҳбатҳо
+Name[th]=ประวัติà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™
+Name[tr]=Geçmiş
+Name[uk]=ІÑторіÑ
+Name[uz]=Tarix
+Name[uz@cyrillic]=Тарих
+Name[ven]=Divhazwakale
+Name[wa]=Istwere
+Name[xh]=Imbali
+Name[zh_CN]=历å²
+Name[zh_HK]=歷程紀錄
+Name[zh_TW]=æ­·å²
+Name[zu]=Umlando
+Comment=History Plugin
+Comment[ar]=توصيلة المحÙوظات
+Comment[be]=Модуль гіÑторыі
+Comment[bg]=ПриÑтавка за иÑториÑта
+Comment[bn]=ইতিহাস পà§à¦²à¦¾à¦—িন
+Comment[br]=Lugent an istorig
+Comment[bs]=Dodatak za historiju
+Comment[ca]=Connector de l'historial
+Comment[cs]=Modul historie
+Comment[cy]=Ategyn Hanes
+Comment[da]=Historik-plugin
+Comment[de]=Verlaufsmodul
+Comment[el]=ΠÏόσθετο ιστοÏικοÏ
+Comment[eo]=Historio-kromaĵo
+Comment[es]=Complemento de Historial
+Comment[et]=Ajalooplugin
+Comment[eu]=Historia plugin-a
+Comment[fa]=وصلۀ تاریخچه
+Comment[fi]=Historia-liitännäinen
+Comment[fr]=Module d'historique
+Comment[ga]=Breiseán Staire
+Comment[gl]=Plugin de historial
+Comment[he]=תוסף ההיסטוריה
+Comment[hi]=इतिहास पà¥à¤²à¤—इन
+Comment[hr]=Umetak za povijest
+Comment[hu]=Előzmények bővítőmodul
+Comment[is]=Ferilsíforrit
+Comment[it]=Plugin cronologia
+Comment[ja]=履歴プラグイン
+Comment[ka]=ისტáƒáƒ áƒ˜áƒ˜áƒ¡ მáƒáƒ“ული
+Comment[kk]=Журнал плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ​ប្រវážáŸ’ážáž·
+Comment[lt]=Istorijos įskiepis
+Comment[mk]=Приклучок за иÑторија
+Comment[nb]=Programtillegg for historie
+Comment[nds]=Vörgeschichtmoduul
+Comment[ne]=इतिहास पà¥à¤²à¤—इन
+Comment[nl]=Geschiedenis-plugin
+Comment[nn]=Programtillegg for historie
+Comment[pl]=Wtyczka historii
+Comment[pt]='Plugin' de Historial
+Comment[pt_BR]=Plugin de Histórico
+Comment[ro]=Modul istoric
+Comment[ru]=Модуль журналированиÑ
+Comment[se]=Historihkkalassemoduvla
+Comment[sk]=Modul histórie
+Comment[sl]=Vstavek Zgodovina
+Comment[sr]=Прикључак за иÑторијат
+Comment[sr@Latn]=PrikljuÄak za istorijat
+Comment[sv]=Historikinsticksprogram
+Comment[ta]=வரலாறà¯à®±à¯ செரà¯à®•à®²à¯
+Comment[tg]=Модули ÐомнавиÑи Ñӯҳбатҳо
+Comment[tr]=Geçmiş Eklentisi
+Comment[uk]=Втулок Ñ–Ñторії
+Comment[uz]=Tarix plagini
+Comment[uz@cyrillic]=Тарих плагини
+Comment[wa]=Tchôke-divins del istwere
+Comment[zh_CN]=历å²æ’件
+Comment[zh_HK]=歷程紀錄æ’件
+Comment[zh_TW]=æ­·å²å¤–掛程å¼
diff --git a/kopete/plugins/latex/Makefile.am b/kopete/plugins/latex/Makefile.am
new file mode 100644
index 00000000..924da747
--- /dev/null
+++ b/kopete/plugins/latex/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+SUBDIRS = icons
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_latex.la kcm_kopete_latex.la
+
+kopete_latex_la_SOURCES = latexplugin.cpp latexconfig.kcfgc latexguiclient.cpp
+kopete_latex_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_latex_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_latex_la_SOURCES = latexprefsbase.ui latexpreferences.cpp latexconfig.kcfgc
+kcm_kopete_latex_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_latex_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_latex.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_latex_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+bin_SCRIPTS = kopete_latexconvert.sh
+kde_kcfg_DATA = latexconfig.kcfg
+
+mydatadir = $(kde_datadir)/kopete_latex
+mydata_DATA = latexchatui.rc \ No newline at end of file
diff --git a/kopete/plugins/latex/icons/Makefile.am b/kopete/plugins/latex/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/plugins/latex/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/plugins/latex/icons/cr32-app-latex.png b/kopete/plugins/latex/icons/cr32-app-latex.png
new file mode 100644
index 00000000..69df3f61
--- /dev/null
+++ b/kopete/plugins/latex/icons/cr32-app-latex.png
Binary files differ
diff --git a/kopete/plugins/latex/kopete_latex.desktop b/kopete/plugins/latex/kopete_latex.desktop
new file mode 100644
index 00000000..3d957701
--- /dev/null
+++ b/kopete/plugins/latex/kopete_latex.desktop
@@ -0,0 +1,71 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=latex
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_latex
+X-KDE-PluginInfo-Author=Duncan Mac-Vicar
+X-KDE-PluginInfo-Email=duncan@kde.org
+X-KDE-PluginInfo-Name=kopete_latex
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=KopeTeX
+Name[bn]=কপেটেক
+Name[ja]=KopeTex
+Name[ne]=कोपेटेकà¥à¤¸
+Name[sv]=Kopetex
+Comment=Render Latex formulas in the chatwindow
+Comment[be]=Паказваць формулы Latex у вакне гутаркі
+Comment[bg]=Показване на формули на Latex в прозореца за чат
+Comment[bn]=চà§à¦¯à¦¾à¦Ÿ উইনà§à¦¡à§‹à¦¤à§‡ লেটেক ফরà§à¦®à§‚লা পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে
+Comment[bs]=Iscrtava Latex formule u chat prozoru
+Comment[ca]=Representa fórmules Latex a la finestra de xat
+Comment[cs]=Vykresluje vzorce LaTeXu v oknÄ› rozhovoru
+Comment[da]=Viser Latex-formler i chat-vinduet
+Comment[de]=Latex-Formeln in einem Chat-Fenster anzeigen
+Comment[el]=Εξισώσεις Latex στο παÏάθυÏο συνομιλίας
+Comment[es]=Muestra fórmulas LaTeX en la ventana de charla
+Comment[et]=LaTeXi valemite renderdamine vestlusaknas
+Comment[eu]=Elkarrizketa leihoaetan Latex formulak marrazten ditu
+Comment[fa]=نمایش Ùرمولهای Latex در پنجرۀ Ú¯Ùتگو
+Comment[fi]=Renderöi Latex-kaavoja keskusteluikkunaan
+Comment[fr]=Affichage de formules LaTeX dans la fenêtre de discussion
+Comment[gl]=Debuxar as fórmulas de Laxtex na fiestra de conversa
+Comment[he]=מציג נוסח×ות של Latex בחלון השיחה
+Comment[hu]=Latex-es képletek megjelenítése a csevegési ablakban
+Comment[is]=Teikna Latex formúlur í spjallglugganum
+Comment[it]=Visualizza formule latex nella finestra di chat
+Comment[ja]=ãƒãƒ£ãƒƒãƒˆã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã§ LaTeX ã®æ•°å¼ã‚’表示
+Comment[ka]=Latex ფáƒáƒ áƒ›áƒ£áƒšáƒ”ბის სáƒáƒ£áƒ‘რის ფáƒáƒœáƒ¯áƒáƒ áƒáƒ¨áƒ˜ რენდერი
+Comment[kk]=Latex формулаларын әңгіме терезеÑінде келтіру
+Comment[km]=បង្ហាញ​រូបមន្ហLatex នៅ​ក្នុង​បង្អួច​ជជែក​កំសាន្ដ
+Comment[lt]=Vykdyti Latex formules pokalbių lange
+Comment[mk]=ИÑцртува Latex формули во прозорецот за разговори
+Comment[nb]=Tegn LaTeX-formler i pratevinduet
+Comment[nds]=Latex-Formeln binnen dat Klöönfinster wiesen
+Comment[ne]=कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ सञà¥à¤à¥à¤¯à¤¾à¤²à¤®à¤¾ लà¥à¤¯à¤¾à¤Ÿà¥‡à¤•à¥à¤¸ सूतà¥à¤° रेनà¥à¤¡à¤° गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Latex-formules renderen in het gespreksvenster
+Comment[nn]=Vis Latex-formlar i pratevindauget
+Comment[pl]=Wyświetla wyrażenia Latexa w oknie rozmowy
+Comment[pt]=Mostrar formulas de Latex na janela de conversação
+Comment[pt_BR]=Renderiza fórmulas do Latex em uma janela de bate-papo
+Comment[ro]=Randează formule LaTeX în fereastra de discuţii
+Comment[ru]=Ð’Ñтавка и вывод формул Latex в окнах разговоров Kopete
+Comment[se]=Sárggo LaTeX-hámuid Äáttenláses
+Comment[sk]=Zobrazovanie výrazov Latex v okne rozhovoru
+Comment[sl]=Izris formul LaTeX v oknu za klepet
+Comment[sr]=Приказ Latex формула у прозору за ћаÑкање
+Comment[sr@Latn]=Prikaz Latex formula u prozoru za ćaskanje
+Comment[sv]=Visa Latex-formler i chattfönstret
+Comment[ta]= Render Latex formulas in the chatwindow
+Comment[tg]=Формулаҳои Render Latex дар тирезаи чат
+Comment[tr]=Sohbet penceresindeki Latex formüllerini verir
+Comment[uk]=Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€Ð¼ÑƒÐ» Latex у вікнах розмов Kopete
+Comment[zh_CN]=在èŠå¤©çª—å£ä¸­æ¸²æŸ“ Latex å…¬å¼
+Comment[zh_HK]=在èŠå¤©è¦–窗內顯示 Latex 方程å¼
+Comment[zh_TW]=在èŠå¤©è¦–窗中加入 Latex å…¬å¼
+
diff --git a/kopete/plugins/latex/kopete_latex_config.desktop b/kopete/plugins/latex/kopete_latex_config.desktop
new file mode 100644
index 00000000..a5e67a2a
--- /dev/null
+++ b/kopete/plugins/latex/kopete_latex_config.desktop
@@ -0,0 +1,69 @@
+[Desktop Entry]
+Icon=latex
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_latex
+X-KDE-FactoryName=LatexConfigFactory
+X-KDE-ParentApp=kopete_latex
+X-KDE-ParentComponents=kopete_latex
+
+Name=Highlight
+Name[ar]=تمييز
+Name[bg]=ОткроÑване
+Name[bn]=গà§à¦°à§à¦¤à§à¦¬à¦ªà§‚রà§à¦£
+Name[br]=Splannadur
+Name[bs]=Isticanje
+Name[ca]=Ressaltat
+Name[cs]=Zvýraznění
+Name[cy]=Amlygu
+Name[da]=Fremhæv
+Name[de]=Hervorhebung
+Name[el]=Τονισμός
+Name[eo]=Lumaĵo
+Name[es]=Resaltar
+Name[et]=Esiletõstmine
+Name[eu]=Nabarmendu
+Name[fa]=مشخص
+Name[fi]=Korostus
+Name[fr]=Surlignement
+Name[ga]=Aibhsiú
+Name[gl]=Resaltar
+Name[he]=מודגש
+Name[hi]=उभारें
+Name[hr]=Isticanje
+Name[hu]=Kiemelés
+Name[is]=Merkja
+Name[it]=Evidenziazione
+Name[ja]=強調
+Name[ka]=მáƒáƒ áƒ™áƒ˜áƒ áƒ”ბული
+Name[kk]=Ерекше
+Name[km]=សំážáž¶áž“់
+Name[lt]=Paryškinti
+Name[mk]=ОÑветлување
+Name[nb]=Marker
+Name[nds]=Rutheven
+Name[ne]=हाइलाइट
+Name[nl]=Aanwijzen
+Name[nn]=Marker
+Name[pa]=ਉਘਾੜਨ
+Name[pl]=Podświetlenie
+Name[pt]=Realce
+Name[pt_BR]=Destaque
+Name[ro]=Evidenţiat
+Name[ru]=Выделение
+Name[se]=Merke
+Name[sk]=Zvýrazniť
+Name[sl]=Poudarjeno sporoÄilo
+Name[sr]=ИÑтицање
+Name[sr@Latn]=Isticanje
+Name[sv]=Markera
+Name[ta]=à®®à¯à®©à¯ˆà®ªà¯à®ªà¯à®±à¯à®¤à¯à®¤à®²à¯
+Name[tg]=Равшаннамоӣ
+Name[tr]=Vurgu
+Name[uk]=ПідÑвічуваннÑ
+Name[wa]=E sorbiyance
+Name[zh_CN]=çªå‡ºæ˜¾ç¤º
+Name[zh_HK]=加強顯示
+Name[zh_TW]=高亮度
diff --git a/kopete/plugins/latex/kopete_latexconvert.sh b/kopete/plugins/latex/kopete_latexconvert.sh
new file mode 100755
index 00000000..b7f92263
--- /dev/null
+++ b/kopete/plugins/latex/kopete_latexconvert.sh
@@ -0,0 +1,234 @@
+#!/bin/sh
+#############################################################
+# TEX2IM: Converts LaTeX formulas to pixel graphics which #
+# can be easily included in Text-Processors like #
+# M$ or Staroffice. #
+# #
+# Required software: latex, convert (image magic) #
+# to get color, the latex color package is required #
+#############################################################
+# Version 1.8 (http://www.nought.de/tex2im.html) #
+# published under the GNU public licence (GPL) #
+# (c) 14. May 2004 by Andreas Reigber #
+# Email: anderl@nought.de #
+#############################################################
+
+#
+# Default values
+#
+
+resolution="150x150"
+format="png"
+color1="white"
+color2="black"
+trans=1
+noformula=0
+aa=1
+extra_header="$HOME/.tex2im_header"
+
+if [ -f ~/.tex2imrc ]; then
+ source ~/.tex2imrc
+fi
+
+OPTERR=0
+
+if [ $# -lt 1 ]; then
+ echo "Usage: `basename $0` [options] file.tex, for help give option -h" 1>&2
+ exit 1
+fi
+
+while getopts hanzb:t:f:o:r:vx: Optionen; do
+ case $Optionen in
+ h) echo "tex2im [options] latex_expression
+
+The content of input file should be _plain_ latex mathmode code!
+Alternatively, a string containing the latex code can be specified.
+
+Options:
+-v show version
+-h show help
+-a change status of antialiasing
+ default is on for normal mode and
+ off for transparent mode
+-o file specifies output filename,
+ default is inputfile with new extension
+-f expr specifies output format,
+ possible examples: gif, jpg, tif......
+ all formates supported by 'convert' should work,
+ default: png
+-r expr specifies desired resolution in dpi,
+ possible examples: 100x100, 300x300, 200x150,
+ default is 150x150
+-b expr specifies the background color
+ default: white
+-t expr specifies the text color
+ default: black
+-n no-formula mode (do not wrap in eqnarray* environment)
+ default: off
+-z transparent background
+ default: off
+-x file file containing extra header lines.
+ default: ~/.tex2im_header"
+ exit 0 ;;
+ v) echo "TEX2IM Version 1.8"
+ exit 0 ;;
+ r) resolution=$OPTARG;;
+ o) outfile=$OPTARG;;
+ z) trans=1
+ aa=0;;
+ a) if [ $aa -eq 0 ]; then
+ aa=1
+ else
+ aa=0
+ fi;;
+ n) noformula=1;;
+ b) color1=$OPTARG;;
+ t) color2=$OPTARG;;
+ f) format=$OPTARG;;
+ x) extra_header=$OPTARG;;
+ esac
+done
+
+#
+# Generate temporary directory
+#
+
+if test -n "`type -p mktemp`" ; then
+ tmpdir="`mktemp /tmp/tex2imXXXXXX`"
+ rm $tmpdir
+ mkdir $tmpdir
+else
+ tmpdir=/tmp/tex2im$$
+ if [ -e $tmpdir ] ; then
+ echo "$0: Temporary directory $tmpdir already exists." 1>&2
+ exit 1
+ fi
+ mkdir $tmpdir
+fi
+homedir="`pwd`" || exit 1
+
+#
+# Names for input and output files
+#
+
+while [ $OPTIND -le $# ]
+do
+
+eval infile=\$${OPTIND}
+
+if [ -z $outfile ]; then
+ if [ -e "$infile" ]; then
+ base=`basename ${infile} .tex` ;
+ outfile=${base}.$format
+ else
+ outfile=out.$format
+ fi
+fi
+
+#
+# Here we go
+#
+
+(
+cat << ENDHEADER1
+\documentclass[12pt]{article}
+\usepackage{color}
+\usepackage[dvips]{graphicx}
+\pagestyle{empty}
+ENDHEADER1
+) > $tmpdir/out.tex
+
+#
+# Do we have a file containing extra files to include into the header?
+#
+
+if [ -f $extra_header ]; then
+ (
+ cat $extra_header
+ ) >> $tmpdir/out.tex
+fi
+
+if [ $noformula -eq 1 ]; then
+(
+cat << ENDHEADER2
+\pagecolor{$color1}
+\begin{document}
+{\color{$color2}
+ENDHEADER2
+) >> $tmpdir/out.tex
+else
+(
+cat << ENDHEADER2
+\pagecolor{$color1}
+\begin{document}
+{\color{$color2}
+\begin{eqnarray*}
+ENDHEADER2
+) >> $tmpdir/out.tex
+fi
+
+# Kopete does not need to parse the content of a file.
+#if [ -e "$infile" ]; then
+# cat $infile >> $tmpdir/out.tex
+#else
+ echo "$infile" >> $tmpdir/out.tex
+#fi
+
+if [ $noformula -eq 1 ]; then
+(
+cat << ENDFOOTER
+}\end{document}
+ENDFOOTER
+) >> $tmpdir/out.tex
+else
+(
+cat << ENDFOOTER
+\end{eqnarray*}}
+\end{document}
+ENDFOOTER
+) >> $tmpdir/out.tex
+fi
+
+cd $tmpdir
+for f in $homedir/*.eps; do
+ test -f ${f##*/} || ln -s $f . # multi-processing!
+done
+latex -interaction=batchmode out.tex > /dev/null
+cd "$homedir"
+dvips -o $tmpdir/out.eps -E $tmpdir/out.dvi 2> /dev/null
+
+#
+# Transparent background
+#
+
+if [ $trans -eq 1 ]; then
+ if [ $aa -eq 1 ]; then
+ convert +adjoin -antialias -transparent $color1 -density $resolution $tmpdir/out.eps $tmpdir/out.$format
+ else
+ convert +adjoin +antialias -transparent $color1 -density $resolution $tmpdir/out.eps $tmpdir/out.$format
+ fi
+else
+ if [ $aa -eq 1 ]; then
+ convert +adjoin -antialias -density $resolution $tmpdir/out.eps $tmpdir/out.$format
+ else
+ convert +adjoin +antialias -density $resolution $tmpdir/out.eps $tmpdir/out.$format
+ fi
+fi
+
+
+if [ -e $tmpdir/out.$format ]; then
+ mv $tmpdir/out.$format $outfile
+else
+ mv $tmpdir/out.$format.0 $outfile
+fi
+
+let OPTIND=$OPTIND+1
+outfile=""
+done
+
+#
+# Cleanup
+#
+
+rm -rf $tmpdir
+exit 0
diff --git a/kopete/plugins/latex/latexchatui.rc b/kopete/plugins/latex/latexchatui.rc
new file mode 100644
index 00000000..06e8c03a
--- /dev/null
+++ b/kopete/plugins/latex/latexchatui.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopete_latexchat">
+ <MenuBar>
+ <Menu name="tools">
+ <text>&amp;Tools</text>
+ <Action name="latexPreview" />
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/kopete/plugins/latex/latexconfig.kcfg b/kopete/plugins/latex/latexconfig.kcfg
new file mode 100644
index 00000000..f6d0b335
--- /dev/null
+++ b/kopete/plugins/latex/latexconfig.kcfg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author: Stefan Gehn -->
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kopeterc"/>
+
+ <group name="Latex Plugin">
+ <entry name="HorizontalDPI" type="UInt">
+ <label>Horizontal Rendering Resolution (DPI).</label>
+ <default>150</default>
+ </entry>
+ <entry name="VerticalDPI" type="UInt">
+ <label>Vertical Rendering Resolution (DPI).</label>
+ <default>150</default>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kopete/plugins/latex/latexconfig.kcfgc b/kopete/plugins/latex/latexconfig.kcfgc
new file mode 100644
index 00000000..9e4e4fec
--- /dev/null
+++ b/kopete/plugins/latex/latexconfig.kcfgc
@@ -0,0 +1,7 @@
+# Code generation options for kconfig_compiler
+File=latexconfig.kcfg
+ClassName=LatexConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
diff --git a/kopete/plugins/latex/latexguiclient.cpp b/kopete/plugins/latex/latexguiclient.cpp
new file mode 100644
index 00000000..8d7cbf3e
--- /dev/null
+++ b/kopete/plugins/latex/latexguiclient.cpp
@@ -0,0 +1,76 @@
+/*
+ latexguiclient.cpp
+
+ Kopete Latex plugin
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvariant.h>
+
+#include <kaction.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qimage.h>
+#include <qregexp.h>
+#include <qstylesheet.h>
+
+#include "kopetechatsession.h"
+#include "kopeteview.h"
+#include "kopetemessage.h"
+
+#include "latexplugin.h"
+#include "latexguiclient.h"
+
+LatexGUIClient::LatexGUIClient( Kopete::ChatSession *parent, const char *name )
+: QObject( parent, name ), KXMLGUIClient( parent )
+{
+ setInstance( LatexPlugin::plugin()->instance() );
+ connect( LatexPlugin::plugin(), SIGNAL( destroyed( QObject * ) ), this, SLOT( deleteLater() ) );
+
+ m_manager = parent;
+
+ new KAction( i18n( "Preview Latex Images" ), "latex", CTRL + Key_L, this, SLOT( slotPreview() ), actionCollection(), "latexPreview" );
+
+ setXMLFile( "latexchatui.rc" );
+}
+
+LatexGUIClient::~LatexGUIClient()
+{
+}
+
+void LatexGUIClient::slotPreview()
+{
+ if ( !m_manager->view() )
+ return;
+
+ Kopete::Message msg = m_manager->view()->currentMessage();
+ QString messageText = msg.plainBody();
+ if(!messageText.contains("$$")) //we haven't found any latex strings
+ {
+ KMessageBox::sorry(reinterpret_cast<QWidget*>(m_manager->view()) , i18n("There are no latex in the message you are typing. The latex formula must be included between $$ and $$ "), i18n("No Latex Formula") );
+ return;
+ }
+
+ msg=Kopete::Message( msg.from() , msg.to() ,
+ i18n("<b>Preview of the latex message :</b> <br />%1").arg(msg.plainBody()),
+ Kopete::Message::Internal , Kopete::Message::RichText);
+ m_manager->appendMessage(msg) ;
+}
+
+
+#include "latexguiclient.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/latex/latexguiclient.h b/kopete/plugins/latex/latexguiclient.h
new file mode 100644
index 00000000..c8ca9e99
--- /dev/null
+++ b/kopete/plugins/latex/latexguiclient.h
@@ -0,0 +1,53 @@
+/*
+ latexguiclient.h
+
+ Kopete Latex Plugin
+
+ Copyright (c) 2005 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSLATORGUICLIENT_H
+#define TRANSLATORGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+#include <kio/job.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+namespace Kopete { class ChatSession; }
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+
+class LatexGUIClient : public QObject , public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ LatexGUIClient( Kopete::ChatSession *parent, const char *name=0L);
+ ~LatexGUIClient();
+
+private slots:
+ void slotPreview();
+
+private:
+ Kopete::ChatSession *m_manager;
+};
+
+#endif
+
diff --git a/kopete/plugins/latex/latexplugin.cpp b/kopete/plugins/latex/latexplugin.cpp
new file mode 100644
index 00000000..7ceab209
--- /dev/null
+++ b/kopete/plugins/latex/latexplugin.cpp
@@ -0,0 +1,259 @@
+/*
+ Kopete Latex Plugin
+
+ Copyright (c) 2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2004-2005 by Olivier Goffart <ogoffart@kde. org>
+
+ Kopete (c) 2001-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qregexp.h>
+#include <qimage.h>
+#include <qbuffer.h>
+#include <qcstring.h>
+#include <qstylesheet.h>
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteuiglobal.h"
+
+#include "latexplugin.h"
+#include "latexconfig.h"
+#include "latexguiclient.h"
+
+#define ENCODED_IMAGE_MODE 0
+
+typedef KGenericFactory<LatexPlugin> LatexPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_latex, LatexPluginFactory( "kopete_latex" ) )
+
+LatexPlugin::LatexPlugin( QObject *parent, const char *name, const QStringList &/*args*/ )
+: Kopete::Plugin( LatexPluginFactory::instance(), parent, name )
+{
+// kdDebug() << k_funcinfo << endl;
+ if( !s_pluginStatic )
+ s_pluginStatic = this;
+
+ mMagickNotFoundShown = false;
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToDisplay( Kopete::Message & ) ), SLOT( slotMessageAboutToShow( Kopete::Message & ) ) );
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToSend(Kopete::Message& ) ), this, SLOT(slotMessageAboutToSend(Kopete::Message& ) ) );
+ connect ( this , SIGNAL( settingsChanged() ) , this , SLOT( slotSettingsChanged() ) );
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ),
+ this, SLOT( slotNewChatSession( Kopete::ChatSession * ) ) );
+
+ m_convScript = KStandardDirs::findExe("kopete_latexconvert.sh");
+ slotSettingsChanged();
+
+ //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining)
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ slotNewChatSession( *it );
+}
+
+LatexPlugin::~LatexPlugin()
+{
+ s_pluginStatic = 0L;
+}
+
+LatexPlugin* LatexPlugin::plugin()
+{
+ return s_pluginStatic ;
+}
+
+LatexPlugin* LatexPlugin::s_pluginStatic = 0L;
+
+void LatexPlugin::slotNewChatSession( Kopete::ChatSession *KMM )
+{
+ new LatexGUIClient( KMM );
+}
+
+
+void LatexPlugin::slotMessageAboutToShow( Kopete::Message& msg )
+{
+ QString mMagick = KStandardDirs::findExe("convert");
+ if ( mMagick.isEmpty() )
+ {
+ // show just once
+ if ( !mMagickNotFoundShown )
+ {
+ KMessageBox::queuedMessageBox(
+ Kopete::UI::Global::mainWidget(),
+ KMessageBox::Error, i18n("I cannot find the Magick convert program.\nconvert is required to render the Latex formulas.\nPlease go to www.imagemagick.org or to your distribution site and get the right package.")
+ );
+ mMagickNotFoundShown = true;
+ }
+ // dont try to parse if convert is not installed
+ return;
+ }
+
+ QString messageText = msg.plainBody();
+ if( !messageText.contains("$$"))
+ return;
+
+ //kdDebug() << k_funcinfo << " Using converter: " << m_convScript << endl;
+
+ // /\[([^]]).*?\[/$1\]/
+ // \$\$.+?\$\$
+
+ // this searches for $$formula$$
+ QRegExp rg("\\$\\$.+\\$\\$");
+ rg.setMinimal(true);
+ // this searches for [latex]formula[/latex]
+ //QRegExp rg("\\[([^]\]).*?\\[/$1\\]");
+
+ int pos = 0;
+
+ QMap<QString, QString> replaceMap;
+ while (pos >= 0 && (unsigned int)pos < messageText.length())
+ {
+// kdDebug() << k_funcinfo << " searching pos: " << pos << endl;
+ pos = rg.search(messageText, pos);
+
+ if (pos >= 0 )
+ {
+ QString match = rg.cap(0);
+ pos += rg.matchedLength();
+
+ QString formul=match;
+ if(!securityCheck(formul))
+ continue;
+
+ QString fileName=handleLatex(formul.replace("$$",""));
+
+ // get the image and encode it with base64
+ #if ENCODED_IMAGE_MODE
+ QImage renderedImage( fileName );
+ imagePxWidth = renderedImage.width();
+ imagePxHeight = renderedImage.height();
+ if ( !renderedImage.isNull() )
+ {
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ renderedImage.save( &buffer, "PNG" );
+ QString imageURL = QString::fromLatin1("data:image/png;base64,%1").arg( KCodecs::base64Encode( ba ) );
+ replaceMap[match] = imageURL;
+ }
+ #else
+ replaceMap[match] = fileName;
+ #endif
+ }
+ }
+
+ if(replaceMap.isEmpty()) //we haven't found any latex strings
+ return;
+
+ messageText= msg.escapedBody();
+
+ int imagePxWidth,imagePxHeight;
+ for (QMap<QString,QString>::ConstIterator it = replaceMap.begin(); it != replaceMap.end(); ++it)
+ {
+ QImage theImage(*it);
+ if(theImage.isNull())
+ continue;
+ imagePxWidth = theImage.width();
+ imagePxHeight = theImage.height();
+ QString escapedLATEX=QStyleSheet::escape(it.key()).replace("\"","&quot;"); //we need the escape quotes because that string will be in a title="" argument, but not the \n
+ messageText.replace(Kopete::Message::escape(it.key()), " <img width=\"" + QString::number(imagePxWidth) + "\" height=\"" + QString::number(imagePxHeight) + "\" src=\"" + (*it) + "\" alt=\"" + escapedLATEX +"\" title=\"" + escapedLATEX +"\" /> ");
+ }
+
+ msg.setBody( messageText, Kopete::Message::RichText );
+}
+
+
+void LatexPlugin::slotMessageAboutToSend( Kopete::Message& msg)
+{
+ Q_UNUSED(msg)
+ //disabled because to work correctly, we need to find what special has the gif we can send over MSN
+#if 0
+ KConfig *config = KGlobal::config();
+ config->setGroup("Latex Plugin");
+
+ if(!config->readBoolEntry("ParseOutgoing", false))
+ return;
+
+ QString messageText = msg.plainBody();
+ if( !messageText.contains("$$"))
+ return;
+/* if( msg.from()->protocol()->pluginId()!="MSNProtocol" )
+ return;*/
+
+ // this searches for $$formula$$
+ QRegExp rg("^\\s*\\$\\$([^$]+)\\$\\$\\s*$");
+
+ if( rg.search(messageText) != -1 )
+ {
+ QString latexFormula = rg.cap(1);
+ if(!securityCheck( latexFormula ))
+ return;
+
+ QString url = handleLatex(latexFormula);
+
+
+ if(!url.isNull())
+ {
+ QString escapedLATEX= QStyleSheet::escape(messageText).replace("\"","&quot;");
+ QString messageText="<img src=\"" + url + "\" alt=\"" + escapedLATEX + "\" title=\"" + escapedLATEX +"\" />";
+ msg.setBody( messageText, Kopete::Message::RichText );
+ }
+ }
+#endif
+}
+
+QString LatexPlugin::handleLatex(const QString &latexFormula)
+{
+ KTempFile *tempFile=new KTempFile( locateLocal( "tmp", "kopetelatex-" ), ".png" );
+ tempFile->setAutoDelete(true);
+ m_tempFiles.append(tempFile);
+ m_tempFiles.setAutoDelete(true);
+ QString fileName = tempFile->name();
+
+ KProcess p;
+
+ QString argumentRes = "-r %1x%2";
+ QString argumentOut = "-o %1";
+ //QString argumentFormat = "-fgif"; //we uses gif format because MSN only handle gif
+ int hDPI, vDPI;
+ hDPI = LatexConfig::self()->horizontalDPI();
+ vDPI = LatexConfig::self()->verticalDPI();
+ p << m_convScript << argumentRes.arg(QString::number(hDPI), QString::number(vDPI)) << argumentOut.arg(fileName) /*<< argumentFormat*/ << latexFormula ;
+
+ kdDebug() << k_funcinfo << " Rendering " << m_convScript << " " << argumentRes.arg(QString::number(hDPI), QString::number(vDPI)) << " " << argumentOut.arg(fileName) << endl;
+
+ // FIXME our sucky sync filter API limitations :-)
+ p.start(KProcess::Block);
+ return fileName;
+}
+
+bool LatexPlugin::securityCheck(const QString &latexFormula)
+{
+ return !latexFormula.contains(QRegExp("\\\\(def|let|futurelet|newcommand|renewcomment|else|fi|write|input|include"
+ "|chardef|catcode|makeatletter|noexpand|toksdef|every|errhelp|errorstopmode|scrollmode|nonstopmode|batchmode"
+ "|read|csname|newhelp|relax|afterground|afterassignment|expandafter|noexpand|special|command|loop|repeat|toks"
+ "|output|line|mathcode|name|item|section|mbox|DeclareRobustCommand)[^a-zA-Z]"));
+
+}
+
+void LatexPlugin::slotSettingsChanged()
+{
+ LatexConfig::self()->readConfig();
+}
+
+#include "latexplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/latex/latexplugin.h b/kopete/plugins/latex/latexplugin.h
new file mode 100644
index 00000000..a0fcd7fd
--- /dev/null
+++ b/kopete/plugins/latex/latexplugin.h
@@ -0,0 +1,77 @@
+/*
+ latexplugin.h
+
+ Kopete Latex Plugin
+
+ Copyright (c) 2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2004-2005 by Olivier Goffart <ogoffart@kde. org>
+
+ Kopete (c) 2001-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LATEXPLUGIN_H
+#define LATEXPLUGIN_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+#include <ktempfile.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+class QStringList;
+class QString;
+
+
+namespace Kopete { class Message; class ChatSession; }
+
+/**
+ * @author Duncan Mac-Vicar Prett
+ */
+
+class LatexPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ static LatexPlugin *plugin();
+
+ LatexPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~LatexPlugin();
+
+public slots:
+ void slotSettingsChanged();
+ void slotMessageAboutToShow( Kopete::Message& msg );
+ void slotMessageAboutToSend( Kopete::Message& msg );
+ void slotNewChatSession( Kopete::ChatSession *KMM);
+
+public:
+ /**
+ * gives a latex formula, and return the filename of the file where the latex is stored.
+ */
+ QString handleLatex(const QString &latex);
+
+ /**
+ * return false if the latex formula may contains malicious commands
+ */
+ bool securityCheck(const QString & formula);
+
+
+private:
+ static LatexPlugin* s_pluginStatic;
+ QString m_convScript;
+ bool mMagickNotFoundShown;
+ QPtrList<KTempFile> m_tempFiles;
+};
+
+#endif
diff --git a/kopete/plugins/latex/latexpreferences.cpp b/kopete/plugins/latex/latexpreferences.cpp
new file mode 100644
index 00000000..1727ae49
--- /dev/null
+++ b/kopete/plugins/latex/latexpreferences.cpp
@@ -0,0 +1,76 @@
+/*
+ Kopete Latex Plugin
+
+ Copyright (c) 2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2001-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlayout.h>
+#include <kparts/componentfactory.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#include "latexplugin.h"
+#include "latexconfig.h"
+#include "latexprefsbase.h"
+#include "latexpreferences.h"
+
+typedef KGenericFactory<LatexPreferences> LatexPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_latex, LatexPreferencesFactory( "kcm_kopete_latex" ) )
+
+LatexPreferences::LatexPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule(LatexPreferencesFactory::instance(), parent, args)
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ m_preferencesDialog = new LatexPrefsUI(this);
+ // connect widget signals here
+ m_preferencesDialog->horizontalDPI->setMinValue(1);
+ m_preferencesDialog->verticalDPI->setMinValue(1);
+
+ connect(m_preferencesDialog->horizontalDPI, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
+ connect(m_preferencesDialog->verticalDPI, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
+
+ load();
+}
+
+LatexPreferences::~LatexPreferences()
+{
+}
+
+void LatexPreferences::load()
+{
+ LatexConfig::self()->readConfig();
+ // load widgets here
+ m_preferencesDialog->horizontalDPI->setValue(LatexConfig::self()->horizontalDPI());
+ m_preferencesDialog->verticalDPI->setValue(LatexConfig::self()->verticalDPI());
+ emit KCModule::changed(false);
+}
+
+void LatexPreferences::slotModified()
+{
+ emit KCModule::changed(true);
+}
+
+void LatexPreferences::save()
+{
+ LatexConfig::self()->setHorizontalDPI(m_preferencesDialog->horizontalDPI->value());
+ LatexConfig::self()->setVerticalDPI(m_preferencesDialog->verticalDPI->value());
+ LatexConfig::self()->writeConfig();
+ emit KCModule::changed(false);
+}
+
+#include "latexpreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/latex/latexpreferences.h b/kopete/plugins/latex/latexpreferences.h
new file mode 100644
index 00000000..c08b35b5
--- /dev/null
+++ b/kopete/plugins/latex/latexpreferences.h
@@ -0,0 +1,48 @@
+/*
+ Kopete Latex Plugin
+
+ Copyright (c) 2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2001-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LatexPREFERENCES_H
+#define LatexPREFERENCES_H
+
+#include <kcmodule.h>
+#include <qstring.h>
+
+class LatexPrefsUI;
+class QListViewItem;
+
+/**
+ *@author Duncan Mac-Vicar Prett
+ */
+
+class LatexPreferences : public KCModule
+{
+ Q_OBJECT
+public:
+
+ LatexPreferences(QWidget *parent = 0, const char* name = 0, const QStringList &args = QStringList());
+ ~LatexPreferences();
+
+ virtual void save();
+ virtual void load();
+
+private:
+ LatexPrefsUI *m_preferencesDialog;
+private slots:
+ void slotModified();
+};
+
+#endif
diff --git a/kopete/plugins/latex/latexprefsbase.ui b/kopete/plugins/latex/latexprefsbase.ui
new file mode 100644
index 00000000..fbb11a21
--- /dev/null
+++ b/kopete/plugins/latex/latexprefsbase.ui
@@ -0,0 +1,168 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>LatexPrefsUI</class>
+<author>Duncan Mac-Vicar</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>LatexPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>513</width>
+ <height>232</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;The &lt;font size="+1"&gt;KopeTeX&lt;/font&gt; plugin allows &lt;font size="+1"&gt;Kopet&lt;/font&gt;e to render Latex formulas in the chat window. The sender must enclose the formula between two $ signs. ie: $$formula$$&lt;/p&gt;
+&lt;p&gt;This plugin requires ImageMagick convert program installed in order to work.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Rendering resolution (DPI):</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>280</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>horizontalDPI</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>x</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>verticalDPI</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>220</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+ </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>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/motionautoaway/COPYING.motion b/kopete/plugins/motionautoaway/COPYING.motion
new file mode 100644
index 00000000..96bdc086
--- /dev/null
+++ b/kopete/plugins/motionautoaway/COPYING.motion
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/kopete/plugins/motionautoaway/Makefile.am b/kopete/plugins/motionautoaway/Makefile.am
new file mode 100644
index 00000000..ff2c5bd8
--- /dev/null
+++ b/kopete/plugins/motionautoaway/Makefile.am
@@ -0,0 +1,24 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_motionaway.la kcm_kopete_motionaway.la
+
+kopete_motionaway_la_SOURCES = motionawayplugin.cpp motionawayconfig.kcfgc
+kopete_motionaway_la_LDFLAGS = -module $(KDE_PLUGIN)
+kopete_motionaway_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_motionaway_la_SOURCES = motionawayprefs.ui motionawaypreferences.cpp motionawayconfig.kcfgc
+kcm_kopete_motionaway_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_motionaway_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+
+
+service_DATA = kopete_motionaway.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_motionaway_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+kde_kcfg_DATA = motionawayconfig.kcfg
+
+
diff --git a/kopete/plugins/motionautoaway/configure.in.in b/kopete/plugins/motionautoaway/configure.in.in
new file mode 100644
index 00000000..5533607f
--- /dev/null
+++ b/kopete/plugins/motionautoaway/configure.in.in
@@ -0,0 +1,18 @@
+dnl Only compile motionautoaway on Linux (needs video4linux)
+
+dnl Disabled for now. It breaks with patched Linux 2.4 kernels and
+dnl vanilla Linux 2.5 and 2.6 kernels
+
+#AC_MSG_CHECKING([if motionautoaway plugin should be compiled])
+
+#if test "x`uname`" = "xLinux"; then
+# COMPILEMOTION=true
+# AC_SUBST(COMPILEMOTION)
+# AC_MSG_RESULT([yes])
+#else
+ COMPILEMOTION=
+# AC_SUBST(COMPILEMOTION)
+# AC_MSG_RESULT([no])
+#fi
+
+AM_CONDITIONAL(include_motionautoaway, test -n "$COMPILEMOTION")
diff --git a/kopete/plugins/motionautoaway/kopete_motionaway.desktop b/kopete/plugins/motionautoaway/kopete_motionaway.desktop
new file mode 100644
index 00000000..93c8d617
--- /dev/null
+++ b/kopete/plugins/motionautoaway/kopete_motionaway.desktop
@@ -0,0 +1,115 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_motionaway
+X-KDE-PluginInfo-Author=Duncan Mac-Vicar Prett
+X-KDE-PluginInfo-Email=duncan@kde.org
+X-KDE-PluginInfo-Name=kopete_motionaway
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Motion Auto-Away
+Name[ar]=حركة التبعيد التلقائية
+Name[bg]=ПромÑна на ÑÑŠÑтоÑнието
+Name[bn]=চলাচল সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿ-অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤à¦¿
+Name[bs]=Auto-odsutnost na osnovu kretanja
+Name[ca]=Auto-absent per moviment
+Name[cs]=Nepřítomnost podle pohybu
+Name[cy]=Dim Yma (Dim Symudiad)
+Name[da]=Bevægelses-auto-borte
+Name[de]=Auto-Abwesenheit
+Name[el]=Αυτόματη μετάβαση σε απουσία
+Name[es]=Ausencia automática por movimiento
+Name[et]=Automaatne äraolek
+Name[eu]=Auto-urrunduta
+Name[fa]=حرکت خودکار دور
+Name[fi]=Liikeen perusteella poistuminen
+Name[fr]=Détection de mouvement
+Name[gl]=Auto-alonxamento
+Name[he]=מסמן "×œ× × ×ž×¦×" בהת×× ×œ×ª× ×•×¢×”
+Name[hi]=मोशन आटो अवे
+Name[hr]=Automatska odsutnost u nedostatku pokreta
+Name[hu]=A távollét érzékelése mozgásdetektálással
+Name[it]=Automaticamente "assente" se inutilizzato
+Name[ja]=自動ä¸åœ¨æ™‚ã®å‹•ä½œ
+Name[ka]=áƒáƒ•áƒ¢áƒ-გáƒáƒ¡áƒ•áƒšáƒ
+Name[kk]=Орында жоқ күйін автоорнату
+Name[km]=ចលនា​ស្វáŸáž™áž”្រវážáŸ’ážáž· áž–áŸáž›â€‹áž˜áž·áž“​នៅ
+Name[lt]=Judėjimas „Pasitraukęs“
+Name[mk]=Ðвто-отÑутен за движење
+Name[nb]=Bli borte automatisk
+Name[nds]=Auto-Wegwesen
+Name[ne]=चाल सà¥à¤µà¤¤: बाहिर
+Name[nl]=Automatisch afwezig
+Name[nn]=Bli vekke automatisk
+Name[pl]=Wykrywanie nieaktywności za pomocą czujnika ruchu
+Name[pt]=Ausência Automática por Movimento
+Name[pt_BR]=Ausente Automático
+Name[ru]=Ðвтоопределение отÑутÑтвиÑ
+Name[sk]=Automatická prítomnosť podľa pohybu
+Name[sl]=Samo-odsotnost (premikanje)
+Name[sr]=ÐутоматÑка одÑутноÑÑ‚ без покретâ
+Name[sr@Latn]=Automatska odsutnost bez pokretâ
+Name[sv]=Automatisk rörelsefrånvaro
+Name[ta]=அகராதி
+Name[tg]=Ҳаракати Ðқибгардии Худкор
+Name[tr]=Otomatik Uzakta Hareketi
+Name[uk]=ÐвтовиÑÐ²Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ÑутноÑÑ‚Ñ–
+Name[zh_CN]=自动离开
+Name[zh_HK]=動作感應器
+Name[zh_TW]=動作åµæ¸¬è‡ªå‹•é›¢é–‹
+Comment=Sets away status when not detecting movement near the computer
+Comment[ar]=يحول وضع الاتصال إلى ÙÙŠ الخارج عندما لا يتم تحديد تعاملات مع الكومبيوتر
+Comment[bg]=ПромÑна на ÑÑŠÑтоÑнието, когато нÑма активноÑÑ‚ от Ñтрана на потребителÑ
+Comment[bn]=যখন কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° কাছে কোনও চলাচল অনà§à¦­à§‚ত হয়না তখন অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤ অবসà§à¦¥à¦¾ নিযà§à¦•à§à¦¤ করে
+Comment[bs]=Postavlja status odsutnosti ako nije detektovano kretanje u blizini raÄunara
+Comment[ca]=Estableix l'estatus d'absent en no detectar moviment a l'ordinador
+Comment[cs]=Nastaví automaticky stav "nepřítomen" při absenci pohybu
+Comment[cy]=Gosod cyflwr i "i fwrdd" os na chanfyddir symudiad wrth ymyl y cyfrifiadur
+Comment[da]=Sætter borte-status når ingen bevægelse detekteres nær computeren
+Comment[de]=Setzt automatisch den Status auf abwesend, wenn keine Aktivität am Rechner feststellbar ist
+Comment[el]=ΟÏίζει την κατάσταση σε απουσία όταν δεν ανιχνεÏει κίνηση κοντά στον υπολογιστή
+Comment[es]=Se pone en estado Ausente cuando no se detecte movimiento cerca de su equipo
+Comment[et]=Määrab äraoleku staatuse, kui arvuti juures mingit elutegevust ei täheldata
+Comment[eu]=Ezarri urrunduta egoera konputagailuan mugimendurik ez dagoenean
+Comment[fa]=اگر هیچ حرکتی نزدیک رایانۀ شما آشکار نشود، وضعیت را کنار می‌گذارد
+Comment[fi]=Asettaa poissaolevaksi, jos koneen lähistöllä ei havaita liikettä
+Comment[fr]=Active l'état absent lorsque aucune activité n'est détectée près de l'ordinateur
+Comment[gl]=Pónse en estado alonxado cando non se detectan movementos preto do seu ordenador
+Comment[he]=מגדיר מצב "×œ× × ×ž×¦×" בעת חוסר פעילות על המחשב
+Comment[hi]=जब कमà¥à¤ªà¥à¤¯à¥‚टर के आसपास गतिविधि पता नहीं लगता है तो सà¥à¤¥à¤¿à¤¤à¤¿ को दूर नियत करता है
+Comment[hr]=Postavlja status „odsutan“ kada ne detektira pomicanje u blizini raÄunala
+Comment[hu]=A távolléti állapot automatikus beállítása, ha nincs mozgás a számítógép körül
+Comment[is]=Breytir stöðu þinni ef tölvan er ekki notkun
+Comment[it]=Imposta lo stato ad assente quando non viene rilevato nessun movimento
+Comment[ja]=コンピュータã®è¿‘ãã«ã„ãªã„å ´åˆã€ä¸åœ¨çŠ¶æ…‹ã«ã‚»ãƒƒãƒˆã—ã¾ã™
+Comment[ka]=áƒáƒ§áƒ”ნებს გáƒáƒ¡áƒ•áƒšáƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ¡ რáƒáƒ“ესáƒáƒª კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რთáƒáƒœ áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რáƒáƒ  შეიმჩნევáƒ
+Comment[kk]=Компьютерде қимыл жоқты байқап орында жоқ деген күйді орнатады
+Comment[km]=កំណážáŸ‹â€‹ážŸáŸ’ážáž¶áž“ភាព​មិន​នៅ នៅ​ពáŸáž›â€‹áž€áž»áŸ†áž–្យូទáŸážš និង​អ្វីៗ​នៅ​ជិážâ€‹ážœáž¶ មិន​កម្រើក
+Comment[lt]=Būklė nustatoma „Pasitraukęs“, jei šalia kompiuterio nėra jokio judėjimo
+Comment[mk]=Го поÑтавува ÑтатуÑот отÑутен кога нема движење на компјутерот
+Comment[nb]=Sett som borte når det ikke er noen bevegelse på mus eller tastatur
+Comment[nds]=Stellt den Status automaatsch op "Nich dor", wenn sik an'n Reekner nix deit
+Comment[ne]=कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤° नजिक चाल पतà¥à¤¤à¤¾ नलागà¥à¤¦à¤¾ वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ बाहिर सेट गरà¥à¤¦à¤›
+Comment[nl]=Stelt automatisch afwezigheid in als er geen beweging wordt gedetecteerd
+Comment[nn]=Set som vekke når det ikkje er noka rørsle på mus eller tastatur
+Comment[pl]=Ustawia status "Zaraz wracam", gdy nie wykrywa żadnego ruchu w pobliżu komputera
+Comment[pt]=Configura o estado de ausência ao não detectar movimento perto do computador
+Comment[pt_BR]=Configura o status de ausente quando não detectar movimento próximo ao computador
+Comment[ru]=УÑтанавливает ÑоÑтоÑние "отÑутÑтвует", еÑли работа за компьютером не региÑтрируетÑÑ Ð² течение долгого времени
+Comment[sk]=Nastaví stav prítomnosti podľa toho, Äi bol zaznamenaný pohyb pri poÄítaÄi
+Comment[sl]=Nastavi stanje odsotnosti, ko ni premikanja v bližini raÄunalnika
+Comment[sr]=ПоÑтавља ÑÑ‚Ð°Ñ‚ÑƒÑ â€žÐ¾Ð´Ñутан“ када не детектује померање у близини рачунара
+Comment[sr@Latn]=Postavlja status „odsutan“ kada ne detektuje pomeranje u blizini raÄunara
+Comment[sv]=Ställer automatisk in frånvarostatus när ingen rörelse märks nära datorn
+Comment[ta]=கணிபà¯à®ªà¯Šà®±à®¿à®¯à®¿à®©à¯ à®…à®°à¯à®•à¯‡ நடகà¯à®•à¯à®®à¯ இயகà¯à®•à®¤à¯à®¤à¯ˆ கணà¯à®Ÿà¯à®ªà®¿à®Ÿà®¿à®•à¯à®•à®¾à®¤ போத௠அமைகà¯à®•à¯à®®à¯
+Comment[tg]=Ҳангоми муайÑн накардани ҳаракат дар назди компютер ҳолатро дур меÑозад
+Comment[tr]=Bilgisayarın başında olunmadığı algılanırsa uzakta olarak belirler
+Comment[uk]=Ð’Ñтановлює Ñтан у Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ "відÑутній", Ñкщо не реєÑтруєтьÑÑ Ñ€Ð¾Ð±Ð¾Ñ‚Ð° за комп'ютером
+Comment[zh_CN]=æ ¹æ®è®¡ç®—机æ—的状æ€è®¾ç½®ç¦»å¼€çŠ¶æ€
+Comment[zh_HK]=åµæ¸¬ä¸åˆ°é›»è…¦é™„近有動作時將狀態設為「離開ã€
+Comment[zh_TW]=當åµæ¸¬ä¸åˆ°å‹•ä½œæ™‚自動設為離開
diff --git a/kopete/plugins/motionautoaway/kopete_motionaway_config.desktop b/kopete/plugins/motionautoaway/kopete_motionaway_config.desktop
new file mode 100644
index 00000000..ffe5775b
--- /dev/null
+++ b/kopete/plugins/motionautoaway/kopete_motionaway_config.desktop
@@ -0,0 +1,111 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_motionaway
+X-KDE-FactoryName=MotionAwayConfigFactory
+X-KDE-ParentApp=kopete_motionaway
+X-KDE-ParentComponents=kopete_motionaway
+
+Name=Motion Auto-Away
+Name[ar]=حركة التبعيد التلقائية
+Name[bg]=ПромÑна на ÑÑŠÑтоÑнието
+Name[bn]=চলাচল সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿ-অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤à¦¿
+Name[bs]=Auto-odsutnost na osnovu kretanja
+Name[ca]=Auto-absent per moviment
+Name[cs]=Nepřítomnost podle pohybu
+Name[cy]=Dim Yma (Dim Symudiad)
+Name[da]=Bevægelses-auto-borte
+Name[de]=Auto-Abwesenheit
+Name[el]=Αυτόματη μετάβαση σε απουσία
+Name[es]=Ausencia automática por movimiento
+Name[et]=Automaatne äraolek
+Name[eu]=Auto-urrunduta
+Name[fa]=حرکت خودکار دور
+Name[fi]=Liikeen perusteella poistuminen
+Name[fr]=Détection de mouvement
+Name[gl]=Auto-alonxamento
+Name[he]=מסמן "×œ× × ×ž×¦×" בהת×× ×œ×ª× ×•×¢×”
+Name[hi]=मोशन आटो अवे
+Name[hr]=Automatska odsutnost u nedostatku pokreta
+Name[hu]=A távollét érzékelése mozgásdetektálással
+Name[it]=Automaticamente "assente" se inutilizzato
+Name[ja]=自動ä¸åœ¨æ™‚ã®å‹•ä½œ
+Name[ka]=áƒáƒ•áƒ¢áƒ-გáƒáƒ¡áƒ•áƒšáƒ
+Name[kk]=Орында жоқ күйін автоорнату
+Name[km]=ចលនា​ស្វáŸáž™áž”្រវážáŸ’ážáž· áž–áŸáž›â€‹áž˜áž·áž“​នៅ
+Name[lt]=Judėjimas „Pasitraukęs“
+Name[mk]=Ðвто-отÑутен за движење
+Name[nb]=Bli borte automatisk
+Name[nds]=Auto-Wegwesen
+Name[ne]=चाल सà¥à¤µà¤¤: बाहिर
+Name[nl]=Automatisch afwezig
+Name[nn]=Bli vekke automatisk
+Name[pl]=Wykrywanie nieaktywności za pomocą czujnika ruchu
+Name[pt]=Ausência Automática por Movimento
+Name[pt_BR]=Ausente Automático
+Name[ru]=Ðвтоопределение отÑутÑтвиÑ
+Name[sk]=Automatická prítomnosť podľa pohybu
+Name[sl]=Samo-odsotnost (premikanje)
+Name[sr]=ÐутоматÑка одÑутноÑÑ‚ без покретâ
+Name[sr@Latn]=Automatska odsutnost bez pokretâ
+Name[sv]=Automatisk rörelsefrånvaro
+Name[ta]=அகராதி
+Name[tg]=Ҳаракати Ðқибгардии Худкор
+Name[tr]=Otomatik Uzakta Hareketi
+Name[uk]=ÐвтовиÑÐ²Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ÑутноÑÑ‚Ñ–
+Name[zh_CN]=自动离开
+Name[zh_HK]=動作感應器
+Name[zh_TW]=動作åµæ¸¬è‡ªå‹•é›¢é–‹
+Comment=Sets away status when not detecting movement near the computer
+Comment[ar]=يحول وضع الاتصال إلى ÙÙŠ الخارج عندما لا يتم تحديد تعاملات مع الكومبيوتر
+Comment[bg]=ПромÑна на ÑÑŠÑтоÑнието, когато нÑма активноÑÑ‚ от Ñтрана на потребителÑ
+Comment[bn]=যখন কমà§à¦ªà¦¿à¦‰à¦Ÿà¦¾à¦°à§‡à¦° কাছে কোনও চলাচল অনà§à¦­à§‚ত হয়না তখন অনà§à¦ªà¦¸à§à¦¥à¦¿à¦¤ অবসà§à¦¥à¦¾ নিযà§à¦•à§à¦¤ করে
+Comment[bs]=Postavlja status odsutnosti ako nije detektovano kretanje u blizini raÄunara
+Comment[ca]=Estableix l'estatus d'absent en no detectar moviment a l'ordinador
+Comment[cs]=Nastaví automaticky stav "nepřítomen" při absenci pohybu
+Comment[cy]=Gosod cyflwr i "i fwrdd" os na chanfyddir symudiad wrth ymyl y cyfrifiadur
+Comment[da]=Sætter borte-status når ingen bevægelse detekteres nær computeren
+Comment[de]=Setzt automatisch den Status auf abwesend, wenn keine Aktivität am Rechner feststellbar ist
+Comment[el]=ΟÏίζει την κατάσταση σε απουσία όταν δεν ανιχνεÏει κίνηση κοντά στον υπολογιστή
+Comment[es]=Se pone en estado Ausente cuando no se detecte movimiento cerca de su equipo
+Comment[et]=Määrab äraoleku staatuse, kui arvuti juures mingit elutegevust ei täheldata
+Comment[eu]=Ezarri urrunduta egoera konputagailuan mugimendurik ez dagoenean
+Comment[fa]=اگر هیچ حرکتی نزدیک رایانۀ شما آشکار نشود، وضعیت را کنار می‌گذارد
+Comment[fi]=Asettaa poissaolevaksi, jos koneen lähistöllä ei havaita liikettä
+Comment[fr]=Active l'état absent lorsque aucune activité n'est détectée près de l'ordinateur
+Comment[gl]=Pónse en estado alonxado cando non se detectan movementos preto do seu ordenador
+Comment[he]=מגדיר מצב "×œ× × ×ž×¦×" בעת חוסר פעילות על המחשב
+Comment[hi]=जब कमà¥à¤ªà¥à¤¯à¥‚टर के आसपास गतिविधि पता नहीं लगता है तो सà¥à¤¥à¤¿à¤¤à¤¿ को दूर नियत करता है
+Comment[hr]=Postavlja status „odsutan“ kada ne detektira pomicanje u blizini raÄunala
+Comment[hu]=A távolléti állapot automatikus beállítása, ha nincs mozgás a számítógép körül
+Comment[is]=Breytir stöðu þinni ef tölvan er ekki notkun
+Comment[it]=Imposta lo stato ad assente quando non viene rilevato nessun movimento
+Comment[ja]=コンピュータã®è¿‘ãã«ã„ãªã„å ´åˆã€ä¸åœ¨çŠ¶æ…‹ã«ã‚»ãƒƒãƒˆã—ã¾ã™
+Comment[ka]=áƒáƒ§áƒ”ნებს გáƒáƒ¡áƒ•áƒšáƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ¡ რáƒáƒ“ესáƒáƒª კáƒáƒ›áƒžáƒ˜áƒ£áƒ¢áƒ”რთáƒáƒœ áƒáƒ¥áƒ¢áƒ˜áƒ•áƒáƒ‘რáƒáƒ  შეიმჩნევáƒ
+Comment[kk]=Компьютерде қимыл жоқты байқап орында жоқ деген күйді орнатады
+Comment[km]=កំណážáŸ‹â€‹ážŸáŸ’ážáž¶áž“ភាព​មិន​នៅ នៅ​ពáŸáž›â€‹áž€áž»áŸ†áž–្យូទáŸážš និង​អ្វីៗ​នៅ​ជិážâ€‹ážœáž¶ មិន​កម្រើក
+Comment[lt]=Būklė nustatoma „Pasitraukęs“, jei šalia kompiuterio nėra jokio judėjimo
+Comment[mk]=Го поÑтавува ÑтатуÑот отÑутен кога нема движење на компјутерот
+Comment[nb]=Sett som borte når det ikke er noen bevegelse på mus eller tastatur
+Comment[nds]=Stellt den Status automaatsch op "Nich dor", wenn sik an'n Reekner nix deit
+Comment[ne]=कमà¥à¤ªà¥à¤¯à¥à¤Ÿà¤° नजिक चाल पतà¥à¤¤à¤¾ नलागà¥à¤¦à¤¾ वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ बाहिर सेट गरà¥à¤¦à¤›
+Comment[nl]=Stelt automatisch afwezigheid in als er geen beweging wordt gedetecteerd
+Comment[nn]=Set som vekke når det ikkje er noka rørsle på mus eller tastatur
+Comment[pl]=Ustawia status "Zaraz wracam", gdy nie wykrywa żadnego ruchu w pobliżu komputera
+Comment[pt]=Configura o estado de ausência ao não detectar movimento perto do computador
+Comment[pt_BR]=Configura o status de ausente quando não detectar movimento próximo ao computador
+Comment[ru]=УÑтанавливает ÑоÑтоÑние "отÑутÑтвует", еÑли работа за компьютером не региÑтрируетÑÑ Ð² течение долгого времени
+Comment[sk]=Nastaví stav prítomnosti podľa toho, Äi bol zaznamenaný pohyb pri poÄítaÄi
+Comment[sl]=Nastavi stanje odsotnosti, ko ni premikanja v bližini raÄunalnika
+Comment[sr]=ПоÑтавља ÑÑ‚Ð°Ñ‚ÑƒÑ â€žÐ¾Ð´Ñутан“ када не детектује померање у близини рачунара
+Comment[sr@Latn]=Postavlja status „odsutan“ kada ne detektuje pomeranje u blizini raÄunara
+Comment[sv]=Ställer automatisk in frånvarostatus när ingen rörelse märks nära datorn
+Comment[ta]=கணிபà¯à®ªà¯Šà®±à®¿à®¯à®¿à®©à¯ à®…à®°à¯à®•à¯‡ நடகà¯à®•à¯à®®à¯ இயகà¯à®•à®¤à¯à®¤à¯ˆ கணà¯à®Ÿà¯à®ªà®¿à®Ÿà®¿à®•à¯à®•à®¾à®¤ போத௠அமைகà¯à®•à¯à®®à¯
+Comment[tg]=Ҳангоми муайÑн накардани ҳаракат дар назди компютер ҳолатро дур меÑозад
+Comment[tr]=Bilgisayarın başında olunmadığı algılanırsa uzakta olarak belirler
+Comment[uk]=Ð’Ñтановлює Ñтан у Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ "відÑутній", Ñкщо не реєÑтруєтьÑÑ Ñ€Ð¾Ð±Ð¾Ñ‚Ð° за комп'ютером
+Comment[zh_CN]=æ ¹æ®è®¡ç®—机æ—的状æ€è®¾ç½®ç¦»å¼€çŠ¶æ€
+Comment[zh_HK]=åµæ¸¬ä¸åˆ°é›»è…¦é™„近有動作時將狀態設為「離開ã€
+Comment[zh_TW]=當åµæ¸¬ä¸åˆ°å‹•ä½œæ™‚自動設為離開
diff --git a/kopete/plugins/motionautoaway/motionawayconfig.kcfg b/kopete/plugins/motionautoaway/motionawayconfig.kcfg
new file mode 100644
index 00000000..9bad717c
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawayconfig.kcfg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="motionawayconfig" />
+ <group name="MotionAutoAway Plugin" >
+ <entry key="BecomeAvailableWithActivity" type="Bool" >
+ <label>Become available again when the plugin detects motion</label>
+ <whatsthis>If this option is set, the plugin will put you in status available if you are away and it detects motion again.</whatsthis>
+ <default>true</default>
+ </entry>
+ <entry key="VideoDevice" type="Path" >
+ <label>Video device to use for motion detection</label>
+ <whatsthis>This is the Video4Linux path of the camera or device you want to use to detect motion. In most systems the first video device is /dev/v4l/video0.</whatsthis>
+ <default>/dev/v4l/video0</default>
+ </entry>
+ <entry key="AwayTimeout" type="UInt" >
+ <label>Become away after this many minutes of inactivity</label>
+ <whatsthis>This setting affects how fast the plugin switches to away status. Once the plugin detects no motion, it will wait this amount of minutes before switching to away status.</whatsthis>
+ <default>1</default>
+ <min>0</min>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kopete/plugins/motionautoaway/motionawayconfig.kcfgc b/kopete/plugins/motionautoaway/motionawayconfig.kcfgc
new file mode 100644
index 00000000..52f9d6ca
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawayconfig.kcfgc
@@ -0,0 +1,8 @@
+ClassName=MotionAwayConfig
+File=motionawayconfig.kcfg
+GlobalEnums=true
+ItemAccessors=true
+MemberVariables=private
+Mutators=true
+SetUserTexts=false
+Singleton=true
diff --git a/kopete/plugins/motionautoaway/motionawayplugin.cpp b/kopete/plugins/motionautoaway/motionawayplugin.cpp
new file mode 100644
index 00000000..d534a186
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawayplugin.cpp
@@ -0,0 +1,308 @@
+/*
+ motionawayplugin.cpp
+
+ Kopete Motion Detector Auto-Away plugin
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Contains code from motion.c ( Detect changes in a video stream )
+ Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ Distributed under the GNU public license version 2
+ See also the file 'COPYING.motion'
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "config.h"
+
+#include "motionawayplugin.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <qtimer.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include "kopeteaccountmanager.h"
+#include "kopeteaway.h"
+/* The following is a hack:
+ * e.g. Mandrake 9.x ships with a patched
+ * kernel which doesn't define this 64 bit types (we need GNU C lib
+ * because we use long long and warning - gcc extensions.)
+ *
+ * This is caused by the !defined(__STRICT_ANSI__) check in
+ * /usr/include/asm/types.h
+ */
+#if !defined(__u64) && defined(__GNUC__)
+#if SIZEOF_UNSIGNED_LONG >= 8
+typedef unsigned long __u64;
+#else
+typedef unsigned long long __u64;
+#endif
+#endif
+
+#if !defined(__s64) && defined(__GNUC__)
+#if SIZEOF_LONG >= 8
+typedef signed long __s64;
+#else
+typedef __signed__ long long __s64;
+#endif
+#endif
+/*
+ * End hack
+ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)
+#define _LINUX_TIME_H
+#endif
+#include <linux/videodev.h>
+
+#define DEF_WIDTH 352
+#define DEF_HEIGHT 288
+#define DEF_QUALITY 50
+#define DEF_CHANGES 5000
+
+#define DEF_POLL_INTERVAL 1500
+
+#define DEF_GAP 60*5 /* 5 minutes */
+
+#define NORM_DEFAULT 0
+#define IN_DEFAULT 8
+
+typedef KGenericFactory<MotionAwayPlugin> MotionAwayPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_motionaway, MotionAwayPluginFactory( "kopete_motionaway" ) )
+
+MotionAwayPlugin::MotionAwayPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Plugin( MotionAwayPluginFactory::instance(), parent, name )
+{
+ kdDebug(14305) << k_funcinfo << "Called." << endl;
+ /* This should be read from config someday may be */
+ m_width = DEF_WIDTH;
+ m_height = DEF_HEIGHT;
+ m_quality = DEF_QUALITY;
+ m_maxChanges = DEF_CHANGES;
+ m_gap = DEF_GAP;
+
+ /* We haven't took the first picture yet */
+ m_tookFirst = false;
+
+ m_captureTimer = new QTimer(this);
+ m_awayTimer = new QTimer(this);
+
+ connect( m_captureTimer, SIGNAL(timeout()), this, SLOT(slotCapture()) );
+ connect( m_awayTimer, SIGNAL(timeout()), this, SLOT(slotTimeout()) );
+
+ signal(SIGCHLD, SIG_IGN);
+
+ m_imageRef.resize( m_width * m_height * 3);
+ m_imageNew.resize( m_width * m_height * 3);
+ m_imageOld.resize( m_width * m_height * 3);
+ m_imageOut.resize( m_width * m_height * 3);
+
+
+ kdDebug(14305) << k_funcinfo << "Opening Video4Linux Device" << endl;
+
+ m_deviceHandler = open( videoDevice.latin1() , O_RDWR);
+
+ if (m_deviceHandler < 0)
+ {
+ kdDebug(14305) << k_funcinfo << "Can't open Video4Linux Device" << endl;
+ }
+ else
+ {
+ kdDebug(14305) << k_funcinfo << "Worked! Setting Capture timers!" << endl;
+ /* Capture first image, or we will get a alarm on start */
+ getImage (m_deviceHandler, m_imageRef, DEF_WIDTH, DEF_HEIGHT, IN_DEFAULT, NORM_DEFAULT,
+ VIDEO_PALETTE_RGB24);
+
+ /* We have the first image now */
+ m_tookFirst = true;
+ m_wentAway = false;
+
+ m_captureTimer->start( DEF_POLL_INTERVAL );
+ m_awayTimer->start( awayTimeout * 60 * 1000 );
+ }
+ loadSettings();
+ connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
+}
+
+MotionAwayPlugin::~MotionAwayPlugin()
+{
+ kdDebug(14305) << k_funcinfo << "Closing Video4Linux Device" << endl;
+ close (m_deviceHandler);
+ kdDebug(14305) << k_funcinfo << "Freeing memory" << endl;
+}
+
+void MotionAwayPlugin::loadSettings(){
+ KConfig *kconfig = KGlobal::config();
+ kconfig->setGroup("MotionAway Plugin");
+
+ awayTimeout = kconfig->readNumEntry("AwayTimeout", 1);
+ becomeAvailableWithActivity = kconfig->readBoolEntry("BecomeAvailableWithActivity", true);
+ videoDevice = kconfig->readEntry("VideoDevice", "/dev/video0");
+ m_awayTimer->changeInterval(awayTimeout * 60 * 1000);
+}
+
+int MotionAwayPlugin::getImage(int _dev, QByteArray &_image, int _width, int _height, int _input, int _norm, int _fmt)
+{
+ struct video_capability vid_caps;
+ struct video_channel vid_chnl;
+ struct video_window vid_win;
+ struct pollfd video_fd;
+
+ // Just to avoid a warning
+ _fmt = 0;
+
+ if (ioctl (_dev, VIDIOCGCAP, &vid_caps) == -1)
+ {
+ perror ("ioctl (VIDIOCGCAP)");
+ return (-1);
+ }
+ /* Set channels and norms, NOT TESTED my philips cam doesn't have a
+ * tuner. */
+ if (_input != IN_DEFAULT)
+ {
+ vid_chnl.channel = -1;
+ if (ioctl (_dev, VIDIOCGCHAN, &vid_chnl) == -1)
+ {
+ perror ("ioctl (VIDIOCGCHAN)");
+ }
+ else
+ {
+ vid_chnl.channel = _input;
+ vid_chnl.norm = _norm;
+
+ if (ioctl (_dev, VIDIOCSCHAN, &vid_chnl) == -1)
+ {
+ perror ("ioctl (VIDIOCSCHAN)");
+ return (-1);
+ }
+ }
+ }
+ /* Set image size */
+ if (ioctl (_dev, VIDIOCGWIN, &vid_win) == -1)
+ return (-1);
+
+ vid_win.width=_width;
+ vid_win.height=_height;
+
+ if (ioctl (_dev, VIDIOCSWIN, &vid_win) == -1)
+ return (-1);
+
+ /* Check if data available on the video device */
+ video_fd.fd = _dev;
+ video_fd.events = 0;
+ video_fd.events |= POLLIN;
+ video_fd.revents = 0;
+
+ poll(&video_fd, 1, 0);
+
+ if (video_fd.revents & POLLIN) {
+ /* Read an image */
+ return read (_dev, _image.data() , _width * _height * 3);
+ } else {
+ return (-1);
+ }
+}
+
+void MotionAwayPlugin::slotCapture()
+{
+ /* Should go on forever... emphasis on 'should' */
+ if ( getImage ( m_deviceHandler, m_imageNew, m_width, m_height, IN_DEFAULT, NORM_DEFAULT,
+ VIDEO_PALETTE_RGB24) == m_width * m_height *3 )
+ {
+ int diffs = 0;
+ if ( m_tookFirst )
+ {
+ /* Make a differences picture in image_out */
+ for (int i=0; i< m_width * m_height * 3 ; i++)
+ {
+ m_imageOut[i]= m_imageOld[i]- m_imageNew[i];
+ if ((signed char)m_imageOut[i] > 32 || (signed char)m_imageOut[i] < -32)
+ {
+ m_imageOld[i] = m_imageNew[i];
+ diffs++;
+ }
+ else
+ {
+ m_imageOut[i] = 0;
+ }
+ }
+ }
+ else
+ {
+ /* First picture: new image is now the old */
+ for (int i=0; i< m_width * m_height * 3; i++)
+ m_imageOld[i] = m_imageNew[i];
+ }
+
+ /* The cat just walked in :) */
+ if (diffs > m_maxChanges)
+ {
+ kdDebug(14305) << k_funcinfo << "Motion Detected. [" << diffs << "] Reseting Timeout" << endl;
+
+ /* If we were away, now we are available again */
+ if ( becomeAvailableWithActivity && !Kopete::Away::globalAway() && m_wentAway)
+ {
+ slotActivity();
+ }
+
+ /* We reset the away timer */
+ m_awayTimer->stop();
+ m_awayTimer->start( awayTimeout * 60 * 1000 );
+ }
+
+ /* Old image slowly decays, this will make it even harder on
+ slow moving object to stay undetected */
+ /*
+ for (i=0; i<m_width*m_height*3; i++)
+ {
+ image_ref[i]=(image_ref[i]+image_new[i])/2;
+ }
+ */
+ }
+ else
+ {
+ m_captureTimer->stop();
+ }
+}
+
+void MotionAwayPlugin::slotActivity()
+{
+ kdDebug(14305) << k_funcinfo << "User activity!, going available" << endl;
+ m_wentAway = false;
+ Kopete::AccountManager::self()->setAvailableAll();
+}
+
+void MotionAwayPlugin::slotTimeout()
+{
+ if(!Kopete::Away::globalAway() && !m_wentAway)
+ {
+ kdDebug(14305) << k_funcinfo << "Timeout and no user activity, going away" << endl;
+ m_wentAway = true;
+ Kopete::AccountManager::self()->setAwayAll();
+ }
+}
+
+#include "motionawayplugin.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/motionautoaway/motionawayplugin.h b/kopete/plugins/motionautoaway/motionawayplugin.h
new file mode 100644
index 00000000..98e7e343
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawayplugin.h
@@ -0,0 +1,93 @@
+/*
+ motionawayplugin.h
+
+ Kopete Motion Detector Auto-Away plugin
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Contains code from motion.c ( Detect changes in a video stream )
+ Copyright 2000 by Jeroen Vreeken (pe1rxq@amsat.org)
+ Distributed under the GNU public license version 2
+ See also the file 'COPYING.motion'
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MOTIONAWAYPLUGIN_H
+#define MOTIONAWAYPLUGIN_H
+
+#include "kopeteplugin.h"
+
+class QTimer;
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ */
+
+class MotionAwayPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ MotionAwayPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~MotionAwayPlugin();
+
+public slots:
+ void loadSettings();
+ void slotTimeout();
+ void slotCapture();
+ void slotActivity();
+
+private:
+ int awayTimeout;
+ bool becomeAvailableWithActivity;
+ QString videoDevice;
+
+ QTimer *m_captureTimer;
+ QTimer *m_awayTimer;
+
+ int getImage(int, QByteArray& ,int ,int ,int ,int ,int );
+
+ bool m_tookFirst;
+ bool m_wentAway;
+
+ int m_width;
+ int m_height;
+
+ int m_quality;
+ int m_maxChanges;
+
+ int m_deviceHandler;
+ int shots;
+ int m_gap;
+
+ QByteArray m_imageRef;
+ QByteArray m_imageNew;
+ QByteArray m_imageOld;
+ QByteArray m_imageOut;
+
+ /*
+ time_t currenttimep;
+ time_t lasttime;
+ struct tm *currenttime;
+
+ char file[255];
+ char filepath[255];
+ char c;
+
+ */
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/motionautoaway/motionawaypreferences.cpp b/kopete/plugins/motionautoaway/motionawaypreferences.cpp
new file mode 100644
index 00000000..a4962c5c
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawaypreferences.cpp
@@ -0,0 +1,70 @@
+/*
+ Kopete Motion Detector Auto-Away plugin
+
+ Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlayout.h>
+#include <qobject.h>
+#include <qcheckbox.h>
+
+#include <kgenericfactory.h>
+#include <klineedit.h>
+#include <knuminput.h>
+
+#include "motionawayprefs.h"
+#include "motionawaypreferences.h"
+#include "motionawayconfig.h"
+
+typedef KGenericFactory<MotionAwayPreferences> MotionAwayPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_motionaway, MotionAwayPreferencesFactory("kcm_kopete_motionaway"))
+
+MotionAwayPreferences::MotionAwayPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule(MotionAwayPreferencesFactory::instance(), parent, args)
+{
+ // Add actuall widget generated from ui file.
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new motionawayPrefsUI(this);
+ connect(preferencesDialog->BecomeAvailableWithActivity, SIGNAL(toggled(bool)), this, SLOT(slotWidgetModified()));
+ connect(preferencesDialog->AwayTimeout, SIGNAL(valueChanged(int)), this, SLOT(slotWidgetModified()));
+ connect(preferencesDialog->VideoDevice, SIGNAL(textChanged(const QString &)), this, SLOT(slotWidgetModified()));
+ load();
+}
+
+void MotionAwayPreferences::load()
+{
+ MotionAwayConfig::self()->readConfig();
+ preferencesDialog->AwayTimeout->setValue(MotionAwayConfig::self()->awayTimeout());
+ preferencesDialog->BecomeAvailableWithActivity->setChecked(MotionAwayConfig::self()->becomeAvailableWithActivity());
+ preferencesDialog->VideoDevice->setText(MotionAwayConfig::self()->videoDevice());
+ emit KCModule::changed(false);
+}
+
+void MotionAwayPreferences::slotWidgetModified()
+{
+ emit KCModule::changed(true);
+}
+
+void MotionAwayPreferences::save()
+{
+ MotionAwayConfig::self()->setAwayTimeout(preferencesDialog->AwayTimeout->value());
+ MotionAwayConfig::self()->setBecomeAvailableWithActivity(preferencesDialog->BecomeAvailableWithActivity->isChecked());
+ MotionAwayConfig::self()->setVideoDevice(preferencesDialog->VideoDevice->text());
+ MotionAwayConfig::self()->writeConfig();
+ emit KCModule::changed(false);
+}
+
+#include "motionawaypreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/motionautoaway/motionawaypreferences.h b/kopete/plugins/motionautoaway/motionawaypreferences.h
new file mode 100644
index 00000000..43b33411
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawaypreferences.h
@@ -0,0 +1,45 @@
+/*
+ Kopete Motion Detector Auto-Away plugin
+
+ Copyright (c) 2002-2004 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MOTIONAWAYPREFERENCES_H
+#define MOTIONAWAYPREFERENCES_H
+
+#include "kcmodule.h"
+
+class motionawayPrefsUI;
+
+/**
+ * Preference widget for the Motion Away plugin
+ * @author Duncan Mac-Vicar P.
+ */
+class MotionAwayPreferences : public KCModule
+{
+ Q_OBJECT
+public:
+ MotionAwayPreferences(QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList());
+ virtual void save();
+ virtual void load();
+
+private:
+ motionawayPrefsUI *preferencesDialog;
+private slots:
+ void slotWidgetModified();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/motionautoaway/motionawayprefs.ui b/kopete/plugins/motionautoaway/motionawayprefs.ui
new file mode 100644
index 00000000..134f939a
--- /dev/null
+++ b/kopete/plugins/motionautoaway/motionawayprefs.ui
@@ -0,0 +1,297 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>motionawayPrefsUI</class>
+<author>Duncan Mac-Vicar P.</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>motionawayPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>411</width>
+ <height>406</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;Motion Auto-Away can set you to be away automatically when it does not detect motion from your webcam or any video4linux device.&lt;/p&gt; &lt;p&gt;It will put you online again when it detects you moving in front of the camera.&lt;/p&gt;</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="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Video Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Video4Linux device:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>VideoDevice</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>95</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>VideoDevice</cstring>
+ </property>
+ <property name="text">
+ <string>/dev/video0</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Away Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>BecomeAvailableWithActivity</cstring>
+ </property>
+ <property name="text">
+ <string>Become available when &amp;detecting activity again</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Become away after this many minutes of inactivity:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>AwayTimeout</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>AwayTimeout</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>minutes</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>180</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer22</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/netmeeting/Makefile.am b/kopete/plugins/netmeeting/Makefile.am
new file mode 100644
index 00000000..2b3560be
--- /dev/null
+++ b/kopete/plugins/netmeeting/Makefile.am
@@ -0,0 +1,23 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/protocols/msn $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_netmeeting.la kcm_kopete_netmeeting.la
+
+kopete_netmeeting_la_SOURCES = netmeetingplugin.cpp netmeetinginvitation.cpp netmeetingguiclient.cpp
+kopete_netmeeting_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_netmeeting_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la $(top_builddir)/kopete/protocols/msn/libkopete_msn_shared.la
+
+service_DATA = kopete_netmeeting.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_netmeeting
+mydata_DATA = netmeetingchatui.rc
+
+kcm_kopete_netmeeting_la_SOURCES = netmeetingprefs_ui.ui netmeetingpreferences.cpp
+kcm_kopete_netmeeting_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_netmeeting_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+
+kcm_DATA = kopete_netmeeting_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
diff --git a/kopete/plugins/netmeeting/kopete_netmeeting.desktop b/kopete/plugins/netmeeting/kopete_netmeeting.desktop
new file mode 100644
index 00000000..3636f6ce
--- /dev/null
+++ b/kopete/plugins/netmeeting/kopete_netmeeting.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=phone
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_netmeeting
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_netmeeting
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=kopete_msn
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Netmeeting
+Name[ar]=الاجتماع على الشبكة
+Name[bg]=Видео чат
+Name[bn]=নেট মিটিং
+Name[da]=Netmøde
+Name[eo]=Reta renkontiÄo
+Name[fa]=نت میتینگ
+Name[fr]=Vidéo-conférence
+Name[hi]=नेटमीटिंग
+Name[ja]=ãƒãƒƒãƒˆãƒŸãƒ¼ãƒ†ã‚£ãƒ³ã‚°
+Name[km]=ប្រជុំ​លើ​បណ្ដាញ
+Name[lt]=Bendravimas tinkle
+Name[nds]=Nettmööt
+Name[ne]=नेट मिटिङ
+Name[nl]=NetMeeting
+Name[pa]=ਨੈੱਟ-ਮੀਟਿੰਗ
+Name[sv]=Nätverksmöte
+Name[ta]=இணைய சநà¯à®¤à®¿à®ªà¯à®ªà¯
+Name[tg]=Вохӯриҳои шабакавӣ
+Comment=Voice and Video with MSN Messenger
+Comment[be]=Гук Ñ– відÑа праз MSN Messenger
+Comment[bg]=ПриÑтавка за разговор Ñ Ð³Ð»Ð°Ñ Ð¸ видео Ñ MSN Messenger
+Comment[bn]=à¦à¦®à¦à¦¸à¦à¦¨ বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•à§‡à¦° সঙà§à¦—ে সà§à¦¬à¦° à¦à¦¬à¦‚ ভিডিও
+Comment[bs]=Glas i video sa MSN Messengerom
+Comment[ca]=Veu i vídeo amb MSN Messenger
+Comment[cs]=Hlas a video pomocí MSN Messenger
+Comment[da]=Stemme og video med MSN Messenger
+Comment[de]=Sprache und Video mit dem MSN-Messenger verwenden
+Comment[el]=Βίντεο και εικόνα με το MSN Messenger
+Comment[es]=Voz y vídeo con MSN Messenger
+Comment[et]=Audio ja video kasutamine MSN Messengeriga
+Comment[eu]=Ahotsa eta bideoa MSN Messenger-ekin
+Comment[fa]=ویدیو و صدا با پیام‌رسان ام‌اس‌ان
+Comment[fi]=Ääni ja videokuva MSN Messengerin kanssa
+Comment[fr]=Voix et vidéo avec MSN Messenger
+Comment[gl]=Voz e video con MSN Messenger
+Comment[he]=חוזי ושמע ×¢× MSN Messenger
+Comment[hu]=Hang és videó az MSN Messengerrel
+Comment[is]=Hljóð og vídeó með MSN Messenger
+Comment[it]=Voce e video con MSN Messenger
+Comment[ja]=MSN メッセンジャーã¨ãƒœã‚¤ã‚¹/ビデオãƒãƒ£ãƒƒãƒˆ
+Comment[ka]=ხმრდრვიდერMSN მესინჯერთáƒáƒœ
+Comment[kk]=MSN Messenger Ð´Ñ‹Ð±Ñ‹Ñ Ð¿ÐµÐ½ бейнемен
+Comment[km]=សំឡáŸáž„ និង​វីដáŸáž¢áž¼â€‹ážŠáŸ„យ​ប្រើ​កម្មវិធី​ផ្ញើ​សារ MSN
+Comment[lt]=Bendravimas balsu ir vaizdu per MSN Messenger
+Comment[mk]=Ð“Ð»Ð°Ñ Ð¸ видео Ñо ГлаÑникот на MSN
+Comment[nb]=Lyd og bilde med MSN Messenger
+Comment[nds]=Spraak un Video mit dat MSN-Kortnarichtenprogramm
+Comment[ne]=à¤à¤®à¤à¤¸à¤à¤¨ मेसेनà¥à¤œà¤°à¤¸à¤à¤— आवाज र भिडियो
+Comment[nl]=Beeld en geluid met MSN Messenger
+Comment[nn]=Lyd og bilete med MSN Messenger
+Comment[pl]=GÅ‚os i wideo za pomocÄ… MSN Messenger
+Comment[pt]=Voz e Vídeo com o MSN Messenger
+Comment[pt_BR]=Voz e Vídeo com o MSN Messenger
+Comment[ru]=Ðудио и видео Ñ MSN Messenger
+Comment[sk]=Hlas a video pomocou MSN Messenger
+Comment[sl]=Glas in video z MSN Messenger
+Comment[sr]=Ð“Ð»Ð°Ñ Ð¸ видео Ñа MSN Messenger-ом
+Comment[sr@Latn]=Glas i video sa MSN Messenger-om
+Comment[sv]=Ljud och video med MSN Messenger
+Comment[ta]=எமà¯à®Žà®¸à¯à®Žà®©à¯ செயà¯à®¤à®¿à®¯à®¿à®²à¯ கà¯à®°à®²à¯ மறà¯à®±à¯à®®à¯ படகà¯à®•à®¾à®Ÿà¯à®šà®¿
+Comment[tr]=MSN Messenger ile Video ve Ses
+Comment[uk]=Ðудіо Ñ– відео з MSN Messenger
+Comment[zh_CN]=与 MSN Messenger 一起使用影音
+Comment[zh_HK]=å’Œ MSN Messenger 一起使用語音和視åƒ
+Comment[zh_TW]=MSN Messenger å½±åƒèˆ‡è²éŸ³
diff --git a/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop b/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop
new file mode 100644
index 00000000..16f24f6c
--- /dev/null
+++ b/kopete/plugins/netmeeting/kopete_netmeeting_config.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Icon=highlight
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_netmeeting
+X-KDE-FactoryName=NetmeetingConfigFactory
+X-KDE-ParentApp=kopete_netmeeting
+X-KDE-ParentComponents=kopete_netmeeting
+
+Name=Netmeeting
+Name[ar]=الاجتماع على الشبكة
+Name[bg]=Видео чат
+Name[bn]=নেট মিটিং
+Name[da]=Netmøde
+Name[eo]=Reta renkontiÄo
+Name[fa]=نت میتینگ
+Name[fr]=Vidéo-conférence
+Name[hi]=नेटमीटिंग
+Name[ja]=ãƒãƒƒãƒˆãƒŸãƒ¼ãƒ†ã‚£ãƒ³ã‚°
+Name[km]=ប្រជុំ​លើ​បណ្ដាញ
+Name[lt]=Bendravimas tinkle
+Name[nds]=Nettmööt
+Name[ne]=नेट मिटिङ
+Name[nl]=NetMeeting
+Name[pa]=ਨੈੱਟ-ਮੀਟਿੰਗ
+Name[sv]=Nätverksmöte
+Name[ta]=இணைய சநà¯à®¤à®¿à®ªà¯à®ªà¯
+Name[tg]=Вохӯриҳои шабакавӣ
+Comment=Voice and Video with MSN Messenger
+Comment[be]=Гук Ñ– відÑа праз MSN Messenger
+Comment[bg]=ПриÑтавка за разговор Ñ Ð³Ð»Ð°Ñ Ð¸ видео Ñ MSN Messenger
+Comment[bn]=à¦à¦®à¦à¦¸à¦à¦¨ বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•à§‡à¦° সঙà§à¦—ে সà§à¦¬à¦° à¦à¦¬à¦‚ ভিডিও
+Comment[bs]=Glas i video sa MSN Messengerom
+Comment[ca]=Veu i vídeo amb MSN Messenger
+Comment[cs]=Hlas a video pomocí MSN Messenger
+Comment[da]=Stemme og video med MSN Messenger
+Comment[de]=Sprache und Video mit dem MSN-Messenger verwenden
+Comment[el]=Βίντεο και εικόνα με το MSN Messenger
+Comment[es]=Voz y vídeo con MSN Messenger
+Comment[et]=Audio ja video kasutamine MSN Messengeriga
+Comment[eu]=Ahotsa eta bideoa MSN Messenger-ekin
+Comment[fa]=ویدیو و صدا با پیام‌رسان ام‌اس‌ان
+Comment[fi]=Ääni ja videokuva MSN Messengerin kanssa
+Comment[fr]=Voix et vidéo avec MSN Messenger
+Comment[gl]=Voz e video con MSN Messenger
+Comment[he]=חוזי ושמע ×¢× MSN Messenger
+Comment[hu]=Hang és videó az MSN Messengerrel
+Comment[is]=Hljóð og vídeó með MSN Messenger
+Comment[it]=Voce e video con MSN Messenger
+Comment[ja]=MSN メッセンジャーã¨ãƒœã‚¤ã‚¹/ビデオãƒãƒ£ãƒƒãƒˆ
+Comment[ka]=ხმრდრვიდერMSN მესინჯერთáƒáƒœ
+Comment[kk]=MSN Messenger Ð´Ñ‹Ð±Ñ‹Ñ Ð¿ÐµÐ½ бейнемен
+Comment[km]=សំឡáŸáž„ និង​វីដáŸáž¢áž¼â€‹ážŠáŸ„យ​ប្រើ​កម្មវិធី​ផ្ញើ​សារ MSN
+Comment[lt]=Bendravimas balsu ir vaizdu per MSN Messenger
+Comment[mk]=Ð“Ð»Ð°Ñ Ð¸ видео Ñо ГлаÑникот на MSN
+Comment[nb]=Lyd og bilde med MSN Messenger
+Comment[nds]=Spraak un Video mit dat MSN-Kortnarichtenprogramm
+Comment[ne]=à¤à¤®à¤à¤¸à¤à¤¨ मेसेनà¥à¤œà¤°à¤¸à¤à¤— आवाज र भिडियो
+Comment[nl]=Beeld en geluid met MSN Messenger
+Comment[nn]=Lyd og bilete med MSN Messenger
+Comment[pl]=GÅ‚os i wideo za pomocÄ… MSN Messenger
+Comment[pt]=Voz e Vídeo com o MSN Messenger
+Comment[pt_BR]=Voz e Vídeo com o MSN Messenger
+Comment[ru]=Ðудио и видео Ñ MSN Messenger
+Comment[sk]=Hlas a video pomocou MSN Messenger
+Comment[sl]=Glas in video z MSN Messenger
+Comment[sr]=Ð“Ð»Ð°Ñ Ð¸ видео Ñа MSN Messenger-ом
+Comment[sr@Latn]=Glas i video sa MSN Messenger-om
+Comment[sv]=Ljud och video med MSN Messenger
+Comment[ta]=எமà¯à®Žà®¸à¯à®Žà®©à¯ செயà¯à®¤à®¿à®¯à®¿à®²à¯ கà¯à®°à®²à¯ மறà¯à®±à¯à®®à¯ படகà¯à®•à®¾à®Ÿà¯à®šà®¿
+Comment[tr]=MSN Messenger ile Video ve Ses
+Comment[uk]=Ðудіо Ñ– відео з MSN Messenger
+Comment[zh_CN]=与 MSN Messenger 一起使用影音
+Comment[zh_HK]=å’Œ MSN Messenger 一起使用語音和視åƒ
+Comment[zh_TW]=MSN Messenger å½±åƒèˆ‡è²éŸ³
diff --git a/kopete/plugins/netmeeting/netmeetingchatui.rc b/kopete/plugins/netmeeting/netmeetingchatui.rc
new file mode 100644
index 00000000..b0d139ae
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingchatui.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopete_msn_netmeeting">
+ <MenuBar>
+ <Menu name="tools">
+ <text>&amp;Tools</text>
+ <Action name="netmeeting" />
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/kopete/plugins/netmeeting/netmeetingguiclient.cpp b/kopete/plugins/netmeeting/netmeetingguiclient.cpp
new file mode 100644
index 00000000..e024a872
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingguiclient.cpp
@@ -0,0 +1,61 @@
+/*
+ netmeetingguiclient.cpp
+
+ Kopete NetMeeting plugin
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvariant.h>
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+#include "msnchatsession.h"
+#include "msncontact.h"
+
+#include "netmeetingguiclient.h"
+#include "netmeetinginvitation.h"
+
+class NetMeetingPlugin;
+
+NetMeetingGUIClient::NetMeetingGUIClient( MSNChatSession *parent, const char *name )
+: QObject( parent, name ) , KXMLGUIClient(parent)
+{
+ setInstance(KGenericFactory<NetMeetingPlugin>::instance());
+ m_manager=parent;
+
+ new KAction( i18n( "Invite to Use NetMeeting" ), 0, this, SLOT( slotStartInvitation() ), actionCollection() , "netmeeting" ) ;
+
+ setXMLFile("netmeetingchatui.rc");
+}
+
+NetMeetingGUIClient::~NetMeetingGUIClient()
+{
+
+}
+
+void NetMeetingGUIClient::slotStartInvitation()
+{
+ QPtrList<Kopete::Contact> c=m_manager->members();
+ NetMeetingInvitation *i=new NetMeetingInvitation(false, static_cast<MSNContact*>(c.first()),m_manager);
+ m_manager->initInvitation(i);
+}
+
+#include "netmeetingguiclient.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/netmeeting/netmeetingguiclient.h b/kopete/plugins/netmeeting/netmeetingguiclient.h
new file mode 100644
index 00000000..fa84b694
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingguiclient.h
@@ -0,0 +1,60 @@
+/*
+ netmeetingguiclient.h
+
+ Kopete NetMeeting Plugin
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSLATORGUICLIENT_H
+#define TRANSLATORGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+namespace Kopete { class ChatSession; }
+class MSNChatSession;
+class NetMeetingPlugin;
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+
+class NetMeetingGUIClient : public QObject , public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ NetMeetingGUIClient( MSNChatSession *parent, const char *name=0L);
+ ~NetMeetingGUIClient();
+
+private slots:
+ void slotStartInvitation();
+
+private:
+ MSNChatSession *m_manager;
+ NetMeetingPlugin *m_plugin;
+};
+
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/netmeeting/netmeetinginvitation.cpp b/kopete/plugins/netmeeting/netmeetinginvitation.cpp
new file mode 100644
index 00000000..191bc140
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetinginvitation.cpp
@@ -0,0 +1,183 @@
+/*
+ msninvitation.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "netmeetinginvitation.h"
+
+#include "kopeteuiglobal.h"
+
+#include "msnchatsession.h"
+#include "msnswitchboardsocket.h"
+#include "msncontact.h"
+#include "kopetemetacontact.h"
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+
+#include <qtimer.h>
+#include <kprocess.h>
+
+NetMeetingInvitation::NetMeetingInvitation(bool incoming, MSNContact *c, QObject *parent)
+ : QObject(parent) , MSNInvitation( incoming, NetMeetingInvitation::applicationID() , i18n("NetMeeting") )
+{
+ m_contact=c;
+ oki=false;
+}
+
+
+NetMeetingInvitation::~NetMeetingInvitation()
+{
+}
+
+
+QString NetMeetingInvitation::invitationHead()
+{
+ QTimer::singleShot( 10*60000, this, SLOT( slotTimeout() ) ); //send TIMEOUT in 10 minute if the invitation has not been accepted/refused
+ return QString( MSNInvitation::invitationHead()+
+ "Session-Protocol: SM1\r\n"
+ "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME i don't know what is the session id
+ "\r\n").utf8();
+}
+
+void NetMeetingInvitation::parseInvitation(const QString& msg)
+{
+ QRegExp rx("Invitation-Command: ([A-Z]*)");
+ rx.search(msg);
+ QString command=rx.cap(1);
+ if( msg.contains("Invitation-Command: INVITE") )
+ {
+ MSNInvitation::parseInvitation(msg); //for the cookie
+
+ unsigned int result = KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n("%1 wants to start a chat with NetMeeting; do you want to accept it? " ).arg(m_contact->metaContact()->displayName()),
+ i18n("MSN Plugin") , i18n("Accept"),i18n("Refuse"));
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+
+ if(manager && manager->service())
+ {
+ if(result==3) // Yes == 3
+ {
+ QCString message=QString(
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: ACCEPT\r\n"
+ "Invitation-Cookie: " + QString::number(cookie()) + "\r\n"
+ "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME
+ "Session-Protocol: SM1\r\n"
+ "Launch-Application: TRUE\r\n"
+ "Request-Data: IP-Address:\r\n"
+ "IP-Address: " + manager->service()->getLocalIP()+ "\r\n"
+ "\r\n" ).utf8();
+
+
+ manager->service()->sendCommand( "MSG" , "N", true, message );
+ oki=false;
+ QTimer::singleShot( 10* 60000, this, SLOT( slotTimeout() ) ); //TIMOUT afte 10 min
+ }
+ else //No
+ {
+ manager->service()->sendCommand( "MSG" , "N", true, rejectMessage() );
+ emit done(this);
+ }
+ }
+ }
+ else if( msg.contains("Invitation-Command: ACCEPT") )
+ {
+ if( ! incoming() )
+ {
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+ if(manager && manager->service())
+ {
+ QCString message=QString(
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: ACCEPT\r\n"
+ "Invitation-Cookie: " + QString::number(cookie()) + "\r\n"
+ "Session-ID: {6672F94C-45BF-11D7-B4AE-00010A1008DF}\r\n" //FIXME: what is session id?
+ "Session-Protocol: SM1\r\n"
+ "Launch-Application: TRUE\r\n"
+ "Request-Data: IP-Address:\r\n"
+ "IP-Address: " + manager->service()->getLocalIP() + "\r\n"
+ "\r\n" ).utf8();
+ manager->service()->sendCommand( "MSG" , "N", true, message );
+ }
+ rx=QRegExp("IP-Address: ([0-9\\:\\.]*)");
+ rx.search(msg);
+ QString ip_address = rx.cap(1);
+ startMeeting(ip_address);
+ kdDebug() << k_funcinfo << ip_address << endl;
+ }
+ else
+ {
+ rx=QRegExp("IP-Address: ([0-9\\:\\.]*)");
+ rx.search(msg);
+ QString ip_address = rx.cap(1);
+
+ startMeeting(ip_address);
+ }
+ }
+ else //CANCEL
+ {
+ emit done(this);
+ }
+}
+
+void NetMeetingInvitation::slotTimeout()
+{
+ if(oki)
+ return;
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+
+ if(manager && manager->service())
+ {
+ manager->service()->sendCommand( "MSG" , "N", true, rejectMessage("TIMEOUT") );
+ }
+ emit done(this);
+
+}
+
+
+void NetMeetingInvitation::startMeeting(const QString & ip_address)
+{
+ //TODO: use KProcess
+
+ KConfig *config=KGlobal::config();
+ config->setGroup("Netmeeting Plugin");
+ QString app=config->readEntry("NetmeetingApplication","ekiga -c callto://%1").arg(ip_address);
+
+ kdDebug() << k_funcinfo << app << endl ;
+
+ QStringList args=QStringList::split(" ", app);
+
+ KProcess p;
+ for(QStringList::Iterator it=args.begin() ; it != args.end() ; ++it)
+ {
+ p << *it;
+ }
+ p.start();
+}
+
+#include "netmeetinginvitation.moc"
+
+
+
+
diff --git a/kopete/plugins/netmeeting/netmeetinginvitation.h b/kopete/plugins/netmeeting/netmeetinginvitation.h
new file mode 100644
index 00000000..0fbaf318
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetinginvitation.h
@@ -0,0 +1,56 @@
+/*
+ netmeetinginvitation.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MSNVOICEINVITATION_H
+#define MSNVOICEINVITATION_H
+
+#include <qobject.h>
+#include "msninvitation.h"
+
+class MSNContact;
+
+/**
+ *@author Olivier Goffart
+ */
+class NetMeetingInvitation : public QObject , public MSNInvitation
+{
+Q_OBJECT
+public:
+ NetMeetingInvitation(bool incoming ,MSNContact*, QObject *parent = 0);
+ ~NetMeetingInvitation();
+
+ static QString applicationID() { return "44BBA842-CC51-11CF-AAFA-00AA00B6015C"; }
+ QString invitationHead();
+
+ virtual void parseInvitation(const QString& invitation);
+
+ virtual QObject* object() { return this; }
+
+signals:
+ void done( MSNInvitation * );
+
+private slots:
+ void slotTimeout();
+
+private:
+ MSNContact *m_contact;
+ bool oki;
+ void startMeeting(const QString & ip_address);
+
+};
+
+
+#endif
diff --git a/kopete/plugins/netmeeting/netmeetingplugin.cpp b/kopete/plugins/netmeeting/netmeetingplugin.cpp
new file mode 100644
index 00000000..d2ea501c
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingplugin.cpp
@@ -0,0 +1,91 @@
+/*
+ netmeetingplugin.cpp
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "netmeetingplugin.h"
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kaction.h>
+#include <kdeversion.h>
+#include <kaboutdata.h>
+
+#include "kopetepluginmanager.h"
+#include "kopetechatsessionmanager.h"
+
+#include "msnchatsession.h"
+#include "msnprotocol.h"
+#include "msncontact.h"
+
+#include "netmeetinginvitation.h"
+#include "netmeetingguiclient.h"
+
+
+static const KAboutData aboutdata("kopete_netmeeting", I18N_NOOP("NetMeeting") , "1.0" );
+K_EXPORT_COMPONENT_FACTORY( kopete_netmeeting, KGenericFactory<NetMeetingPlugin>( &aboutdata ) )
+
+NetMeetingPlugin::NetMeetingPlugin( QObject *parent, const char *name, const QStringList &/*args*/ )
+: Kopete::Plugin( KGlobal::instance(), parent, name )
+{
+ if(MSNProtocol::protocol())
+ slotPluginLoaded(MSNProtocol::protocol());
+ else
+ connect(Kopete::PluginManager::self() , SIGNAL(pluginLoaded(Kopete::Plugin*) ), this, SLOT(slotPluginLoaded(Kopete::Plugin*)));
+
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( chatSessionCreated( Kopete::ChatSession * )) , SLOT( slotNewKMM( Kopete::ChatSession * ) ) );
+ //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining)
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ {
+ slotNewKMM(*it);
+ }
+}
+
+NetMeetingPlugin::~NetMeetingPlugin()
+{
+
+}
+
+void NetMeetingPlugin::slotPluginLoaded(Kopete::Plugin *p)
+{
+ if(p->pluginId()=="MSNProtocol")
+ {
+ connect( p , SIGNAL(invitation(MSNInvitation*& , const QString & , long unsigned int , MSNChatSession* , MSNContact* )) ,
+ this, SLOT( slotInvitation(MSNInvitation*& , const QString & , long unsigned int , MSNChatSession* , MSNContact* )));
+ }
+}
+
+void NetMeetingPlugin::slotNewKMM(Kopete::ChatSession *KMM)
+{
+ MSNChatSession *msnMM=dynamic_cast<MSNChatSession*>(KMM);
+ if(msnMM)
+ {
+ connect(this , SIGNAL( destroyed(QObject*)) ,
+ new NetMeetingGUIClient(msnMM)
+ , SLOT(deleteLater()));
+ }
+}
+
+
+void NetMeetingPlugin::slotInvitation(MSNInvitation*& invitation, const QString &bodyMSG , long unsigned int /*cookie*/ , MSNChatSession* msnMM , MSNContact* c )
+{
+ if(!invitation && bodyMSG.contains(NetMeetingInvitation::applicationID()))
+ {
+ invitation=new NetMeetingInvitation(true,c,msnMM);
+ invitation->parseInvitation(bodyMSG);
+ }
+}
+
+#include "netmeetingplugin.moc"
diff --git a/kopete/plugins/netmeeting/netmeetingplugin.h b/kopete/plugins/netmeeting/netmeetingplugin.h
new file mode 100644
index 00000000..7427bbf8
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingplugin.h
@@ -0,0 +1,46 @@
+/*
+ netmeetingplugin.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+
+#ifndef NetMeetingPLUGIN_H
+#define NetMeetingPLUGIN_H
+
+#include "kopeteplugin.h"
+
+namespace Kopete { class ChatSession; }
+class MSNChatSession;
+class MSNContact;
+class MSNInvitation;
+
+
+class NetMeetingPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ NetMeetingPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~NetMeetingPlugin();
+
+private slots:
+ void slotNewKMM(Kopete::ChatSession *);
+ void slotPluginLoaded(Kopete::Plugin*);
+ void slotInvitation(MSNInvitation*& invitation, const QString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c );
+
+
+};
+
+#endif
+
diff --git a/kopete/plugins/netmeeting/netmeetingpreferences.cpp b/kopete/plugins/netmeeting/netmeetingpreferences.cpp
new file mode 100644
index 00000000..b28dfe09
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingpreferences.cpp
@@ -0,0 +1,81 @@
+/***************************************************************************
+ Netmeetingpreferences.cpp - description
+ -------------------
+ copyright : (C) 2004 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kparts/componentfactory.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kcombobox.h>
+#include <klistview.h>
+#include <kgenericfactory.h>
+#include <kcolorbutton.h>
+#include <kinputdialog.h>
+#include <kurlrequester.h>
+#include <kregexpeditorinterface.h>
+#include <kdebug.h>
+
+#include "netmeetingplugin.h"
+#include "netmeetingprefs_ui.h"
+#include "netmeetingpreferences.h"
+
+typedef KGenericFactory<NetmeetingPreferences> NetmeetingPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_netmeeting, NetmeetingPreferencesFactory( "kcm_kopete_netmeeting" ) )
+
+NetmeetingPreferences::NetmeetingPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule(NetmeetingPreferencesFactory::instance(), parent, args)
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new NetmeetingPrefsUI(this);
+
+ connect(preferencesDialog->m_app , SIGNAL(textChanged(const QString &)) , this , SLOT(slotChanged()));
+
+ load();
+}
+
+NetmeetingPreferences::~NetmeetingPreferences()
+{
+}
+
+void NetmeetingPreferences::load()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Netmeeting Plugin");
+ preferencesDialog->m_app->setCurrentText(config->readEntry("NetmeetingApplication","ekiga -c callto://%1"));
+ emit KCModule::changed(false);
+}
+
+void NetmeetingPreferences::save()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("Netmeeting Plugin");
+ config->writeEntry("NetmeetingApplication",preferencesDialog->m_app->currentText());
+ emit KCModule::changed(false);
+}
+
+
+void NetmeetingPreferences::slotChanged()
+{
+ emit KCModule::changed(true);
+}
+
+#include "netmeetingpreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/netmeeting/netmeetingpreferences.h b/kopete/plugins/netmeeting/netmeetingpreferences.h
new file mode 100644
index 00000000..94a7031e
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingpreferences.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ netmeetingpreferences.h - description
+ -------------------
+ copyright : (C) 2004 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef NetmeetingPREFERENCES_H
+#define NetmeetingPREFERENCES_H
+
+#include <kcmodule.h>
+#include <qstring.h>
+
+class NetmeetingPrefsUI;
+
+/**
+ *@author Olivier Goffart
+ */
+
+class NetmeetingPreferences : public KCModule {
+ Q_OBJECT
+public:
+
+ NetmeetingPreferences(QWidget *parent = 0, const char* name = 0, const QStringList &args = QStringList());
+ ~NetmeetingPreferences();
+
+ virtual void save();
+ virtual void load();
+
+private:
+ NetmeetingPrefsUI *preferencesDialog;
+
+private slots:
+ void slotChanged();
+};
+
+#endif
diff --git a/kopete/plugins/netmeeting/netmeetingprefs_ui.ui b/kopete/plugins/netmeeting/netmeetingprefs_ui.ui
new file mode 100644
index 00000000..ed84eb6b
--- /dev/null
+++ b/kopete/plugins/netmeeting/netmeetingprefs_ui.ui
@@ -0,0 +1,148 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>NetmeetingPrefsUI</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>424</width>
+ <height>297</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>The NetMeeting Plugin allows you to start a video or voice chat with your MSN Messenger contacts.
+
+This is not the same as webcam chat you can find in the newer Windows Messenger®, but uses the older NetMeeting chat you can find in old versions.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </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>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Application to launch:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <item>
+ <property name="text">
+ <string>ekiga -c callto://%1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>konference callto://%1</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>m_app</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="autoCompletion">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;%1&lt;/b&gt; will be replaced by the ip to call</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </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>21</width>
+ <height>60</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KActiveLabel">
+ <property name="name">
+ <cstring>kActiveLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>You can download Konference here: &lt;a href="http://www.kde-apps.org/content/show.php?content=10395"&gt;http://www.kde-apps.org/content/show.php?content=10395&lt;/a&gt;</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kactivelabel.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/nowlistening/DESIGN b/kopete/plugins/nowlistening/DESIGN
new file mode 100644
index 00000000..d2be5d5f
--- /dev/null
+++ b/kopete/plugins/nowlistening/DESIGN
@@ -0,0 +1,52 @@
+Now Listening Plugin
+
+What It Does
+
+This plugin tells your chat buddies what media (music, ...) you are currently enjoying. If you turn on "Now Listening" for a contact, when a new track begins, it automatically sends a user defined message.
+
+How It Does It
+Looks for running media applications (Noatun, Kscd, Xmms) using DCOP and asks them what track they are currently playing. The current track is discovered on a periodic basis, and if it has changed, it is advertised in live chatwindows to contacts that the user has indicated should be notified.
+
+Fair Points
+Do we want to tell ALL chat partners what we're listening to?
+- The operation of the plugin is configurable for each contact, using a context menu item.
+
+IRC chats probably DON'T want extraneous bumf.
+- Yes, by using KMM::protocol() we can discover which protocol a chat uses and squelch messages to it. Would need something like a view of QCheckListItems to change the user's per protocol preferences in the Config.
+
+Should we only advertise if the other party to the chat is online?
+- Yes
+
+Is it possible to tell whether media players are actually playing?
+- Not for KsCD, but we can and do for Noatun and xmms.
+
+Alternative methods
+Is there a way to be notified when tracks change?
+- Not yet, but xmms and noatun seem to provide an interface to find out how long a track will be playing for. Could try and predict when a track will change.
+Of course, if ppl change tracks before the track has ended then this prediction is no good. To do this we would need to add a per mediaplayer timer, or at least a pointer to a timer in the plugin (ugly!).
+
+Discovering all live chatwindows
+KopeteMessageManagerFactory::factory()->sessions(), or Kopete::sessionFactory() until that is implemented?
+- Done
+
+OOOH - HOW ABOUT SEEING IF XMMS-KDE HAS DCOP IN IT?
+NO IT DOESN'T
+
+How contact specific plugin data works
+Each metacontact has a method pluginData(KopetePlugin *). This takes a pointer to a plugin, and returns a QStringList containing the metacontact's data for that plugin. A corresponding setPluginData() method changes this. Who is responsible for making sure this data persists?
+
+What about custom actions?
+KopetePlugin::custom[Chat|ContextMenuActions] both return a set of KActions that the plugin wants to have added to the UI. Looking at contactnotes, it seems that this set is recreated every time thses methods are called and they change the state of the plugin (currentContact). Is this so that the context menu is generated individually for each MC, so that the method that is called when the men item is clicked know which MC it applies to?
+
+Choosing whether to advertise to all contacts
+We can either advertise periodically to (some) contacts, or we could just add an Action to advertise what we're currently listening to.
+DONE
+
+Maybe need a per-contact toggle, and a per-chat button to advertise.
+DONE
+
+Customising the advert message
+DONE
+Maybe provide substitution as in "Now listening to %track by %artist (on %album)". Here () indicates substitute whole clause only if enclosed variable is set.
+
+Change so action inserts text in current message?
diff --git a/kopete/plugins/nowlistening/Makefile.am b/kopete/plugins/nowlistening/Makefile.am
new file mode 100644
index 00000000..a9357d5f
--- /dev/null
+++ b/kopete/plugins/nowlistening/Makefile.am
@@ -0,0 +1,25 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(XMMS_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_nowlistening.la kcm_kopete_nowlistening.la
+
+kopete_nowlistening_la_SOURCES = nowlisteningconfig.kcfgc nowlisteningplugin.cpp nlkscd.cpp nlnoatun.cpp nlxmms.cpp nowlisteningguiclient.cpp nljuk.cpp nlamarok.cpp nlkaffeine.cpp
+kopete_nowlistening_la_LDFLAGS = -module $(KDE_PLUGIN) $(XMMS_LDFLAGS) $(all_libraries)
+kopete_nowlistening_la_LIBADD = ../../libkopete/libkopete.la $(XMMS_LIBS)
+
+kcm_kopete_nowlistening_la_SOURCES = nowlisteningprefs.ui nowlisteningpreferences.cpp nowlisteningconfig.kcfgc
+kcm_kopete_nowlistening_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_nowlistening_la_LIBADD = $(LIB_KOPETECOMPAT) $(LIB_KUTILS)
+
+service_DATA = kopete_nowlistening.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_nowlistening_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+kde_kcfg_DATA = nowlisteningconfig.kcfg
+
+mydatadir = $(kde_datadir)/kopete
+mydata_DATA = nowlisteningui.rc nowlisteningchatui.rc
+noinst_HEADERS = nlkaffeine.h
diff --git a/kopete/plugins/nowlistening/README b/kopete/plugins/nowlistening/README
new file mode 100644
index 00000000..83bdd6d1
--- /dev/null
+++ b/kopete/plugins/nowlistening/README
@@ -0,0 +1,41 @@
+README for Now Listening Plugin
+
+AUTHOR Will Stephenson <lists@stevello.free-online.co.uk>
+
+This plugin tells chat partners what you're currently listening to. On demand, it sends them a configurable message.
+
+Caveat: It currently only works with KsCD, Xmms, Noatun, JuK and amaroK. Other media players will be supported shortly.
+
+Caveat 2: It relies on DCOP - I doubt much will happen if you're not running the rest of KDE.
+
+Caveat 3: There's no escaping of the substituted strings, so you might get it stuck in a loop...
+
+REQUIREMENTS
+Requires XMMS remote control header xmmsctrl.h
+(can be found in:
+* SuSE's base xmms rpm
+* Mandrake's libxmms1-devel rpm
+* RedHat, TurboLinux, Conectiva: xmms-devel )
+Which in turn requires glib-devel and gtk-devel to build.
+
+IF CONFIGURE GIVES AN ERROR, OR IT DOESN'T WORK WITH XMMS, CHECK THESE ARE INSTALLED!
+
+Configure test doesn't tell us that this is why it bails.
+
+Rerun make -f Makefile.cvs && ./configure - or any subset of that which will
+reconfigure the new part of the tree.
+
+make
+
+USE
+
+Enable the plugin in Settings->Configure Plugins. You may wish to change the default message.
+
+You can force a notification by typing the string "/media" at the start of a new message, which will be substituted, or by using the Chat->Actions->Send Media Info menu item.
+
+BUGS
+
+Please report to bugs.kde.org. If you need help contact me at lists@stevello.free-online.co.uk.
+
+TODO
+More media players!
diff --git a/kopete/plugins/nowlistening/configure.in.in b/kopete/plugins/nowlistening/configure.in.in
new file mode 100644
index 00000000..08309761
--- /dev/null
+++ b/kopete/plugins/nowlistening/configure.in.in
@@ -0,0 +1,59 @@
+dnl AM_PATH_XMMS([1.0.0])
+dnl AM_INIT_AUTOMAKE(mediacontrol, 0.1)
+dnl AC_PATH_PROG(XMMS_CONFIG, xmms-config, no)
+dnl AM_PATH_XMMS(1.0.0,,AC_MSG_ERROR([*** XMMS >= 1.0.0 not installed - please install first ***]))
+
+AC_DEFUN([AC_CHECK_XMMS],
+[
+ AC_MSG_CHECKING([for libxmms])
+ AC_CACHE_VAL(ac_cv_have_xmms,
+ [
+ ac_save_libs="$LIBS"
+ LIBS="`xmms-config --libs`"
+ ac_CPPFLAGS_save="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS $all_includes `xmms-config --cflags`"
+ ac_LDFLAGS_save="$LDFLAGS"
+ LDFLAGS="$LDFLAGS $all_libraries"
+ AC_TRY_LINK(
+ [#include <xmms/xmmsctrl.h>],
+ [xmms_remote_stop(0);],
+ [ac_cv_have_xmms="yes"],
+ [ac_cv_have_xmms="no"]
+ )
+ LIBS="$ac_save_libs"
+ LDFLAGS="$ac_LDFLAGS_save"
+ CPPFLAGS="$ac_CPPFLAGS_save"
+ ])
+ AC_MSG_RESULT($ac_cv_have_xmms)
+ if test "$ac_cv_have_xmms" = "yes"; then
+ XMMS_INCLUDES="`xmms-config --cflags`"
+
+ for arg in `xmms-config --libs`; do
+ case $arg in
+ -[[lL]]*)
+ XMMS_LIBS="$XMMS_LIBS $arg"
+ ;;
+ *)
+ XMMS_LDFLAGS="$XMMS_LDFLAGS $arg"
+ esac
+ done
+ AC_DEFINE(HAVE_XMMS, 1, [Define if you have xmms libraries and header files.])
+ fi
+])
+
+AC_ARG_WITH(xmms,
+ [AC_HELP_STRING(--with-xmms,
+ [enable support for XMMS @<:@default=check@:>@])],
+ [], with_xmms=check)
+
+if test "x$with_xmms" != xno; then
+ AC_CHECK_XMMS
+
+ if test "x$with_xmms" != xcheck && test "x$ac_cv_have_xmms" = xno; then
+ AC_MSG_ERROR([--with-xmms was given, but test for XMMS failed])
+ fi
+fi
+
+AC_SUBST(XMMS_LIBS)
+AC_SUBST(XMMS_LDFLAGS)
+AC_SUBST(XMMS_INCLUDES)
diff --git a/kopete/plugins/nowlistening/kopete_nowlistening.desktop b/kopete/plugins/nowlistening/kopete_nowlistening.desktop
new file mode 100644
index 00000000..871e7574
--- /dev/null
+++ b/kopete/plugins/nowlistening/kopete_nowlistening.desktop
@@ -0,0 +1,125 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=kaboodle
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_nowlistening
+X-KDE-PluginInfo-Author=Will Stephenson
+X-KDE-PluginInfo-Email=will@stevello.free-online.co.uk
+X-KDE-PluginInfo-Name=kopete_nowlistening
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Now Listening
+Name[ar]=الاستماع جار
+Name[be]=Слухаю зараз
+Name[bg]=Инфо за пеÑен
+Name[bn]=à¦à¦–ন শà§à¦¨à¦›à§‡à¦¨
+Name[bs]=Sada slušam
+Name[ca]=Ara s'escolta
+Name[cs]=Nyní poslouchám
+Name[cy]=Rwan yn Gwrando ar
+Name[da]=Lytter nu
+Name[de]=Im Hintergrund läuft
+Name[el]=ΤώÏα ακοÏω
+Name[eo]=Nun aÅ­skultanta
+Name[es]=Escuchando
+Name[et]=Praegu kuulan
+Name[eu]=Orain entzuten
+Name[fa]=الان گوش می‌کند
+Name[fi]=Nyt soi
+Name[fr]=En écoute
+Name[ga]=Ag Éisteacht Anois
+Name[gl]=Escoitando
+Name[he]=מ×זין עכשיו
+Name[hi]=नाऊ लिसनिंग
+Name[hr]=Sada slušam
+Name[hu]=Most figyelek
+Name[is]=Hlustandi á
+Name[it]=Adesso sto ascoltando
+Name[ja]=今è´ã„ã¦ã„ã‚‹ã‚‚ã®
+Name[ka]=áƒáƒ®áƒšáƒ ისმინებáƒ
+Name[kk]=Тыңдауда
+Name[km]=កំពុង​ស្ដាប់
+Name[lt]=Dabar klausau
+Name[mk]=Сега Ñлуша
+Name[nb]=Hører nå
+Name[nds]=In'n Achtergrund löppt
+Name[ne]=अहिले सà¥à¤¨à¤¿à¤°à¤¹à¥‡à¤•à¥‹ छ
+Name[nl]=Luistert naar
+Name[nn]=Høyrer på
+Name[pa]=ਹà©à¨£ ਸà©à¨£à¨¦à¨¾
+Name[pl]=Czego słucham
+Name[pt]=Agora a Ouvir
+Name[pt_BR]=Ouvindo agora
+Name[ru]=Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð·Ð²ÑƒÑ‡Ð¸Ñ‚
+Name[se]=Dál guldaleamen
+Name[sk]=Teraz poÄúvam
+Name[sl]=Zdaj poslušam
+Name[sr]=Сада Ñлушам
+Name[sr@Latn]=Sada slušam
+Name[sv]=Lyssnar nu
+Name[ta]=தறà¯à®ªà¯‹à®¤à¯ கேடà¯à®Ÿà¯à®•à¯à®•à¯Šà®£à¯à®Ÿà¯à®³à¯à®³à®¾à®°à¯
+Name[tg]=Ҳоло гӯшкунӣ рафта иÑтодааÑÑ‚
+Name[tr]=Åžimdi Dinleniyor
+Name[uk]=Зараз Ñлухаю
+Name[zh_CN]=现在收å¬
+Name[zh_HK]=ç¾æ­£è†è½
+Name[zh_TW]=正在收è½
+Comment=Tells your buddies what you're listening to
+Comment[ar]=تخبر أصدقائك بما تستمع إليه
+Comment[be]=ПаведамлÑе ÑÑбрам, што вы зараз Ñлухаеце
+Comment[bg]=ПриÑтавка за информиране на приÑтелите какво Ñлушате
+Comment[bn]=আপনার বনà§à¦§à§à¦¦à§‡à¦° বলে আপনি কি শà§à¦¨à¦›à§‡à¦¨
+Comment[bs]=Obavještava vaše prijatelje o muzici koju slušate
+Comment[ca]=Li diu als vostres companys què esteu escoltant
+Comment[cs]=Sdělí vašim kamarádům, co právě posloucháte
+Comment[cy]=Dweud wrth eich cyfeillion be y gwrandwch arnodd
+Comment[da]=Fortæller dine venner hvad du lytter til
+Comment[de]=Teilt Ihren Freunden mit, welche Musik Sie gerade hören
+Comment[el]=ΕνημεÏώνει τους φίλους σας σχετικά με το τι ακοÏτε
+Comment[es]=Le cuenta a sus contactos lo que está escuchando
+Comment[et]=Semudele teatamine, mida parajasti kuulad
+Comment[eu]=Zure lagunei zer entzuten ari zaren esaten die
+Comment[fa]=به دوستان شما می‌گوید که به چه چیز گوش می‌دهید
+Comment[fi]=Kertoo kavereillesi mitä kuuntelet tällä hetkellä
+Comment[fr]=Indique à vos contacts ce que vous êtes en train d'écouter
+Comment[gl]=Dílle ós teus amigos que estás a escoitar
+Comment[he]=מספר לחבריך ל×יזו מוסיקה ×תה מ×זין כרגע
+Comment[hi]=आपके बडà¥à¤¡à¥€à¤¸ को बताता है कि आप कà¥à¤¯à¤¾ सà¥à¤¨ रहे हैं
+Comment[hr]=Govori vašim prijateljima što trenutno slušate
+Comment[hu]=A partnerek értesítése arról, hogy Ön mit hallgat
+Comment[is]=Segir vinum þínum á hvað þú ert að hlusta
+Comment[it]=Di' ai tuoi amici cosa stai ascoltando
+Comment[ja]=何をè´ã„ã¦ã„ã‚‹ã‹ã‚’仲間ã«ä¼ãˆã‚‹
+Comment[ka]=áƒáƒ¢áƒ§áƒáƒ‘ინებს თქვენს მეგáƒáƒ‘რებს, თუ რáƒáƒ¡ უსმენთ
+Comment[kk]=ДоÑтарыңызға тыңдап тұрғаныңыз туралы хабарлайды
+Comment[km]=ប្រាប់​សម្លាញ់​អ្នក​ážáž¶ អ្នក​កំពុង​ស្ដាប់​អ្វី
+Comment[lt]=PraneÅ¡kite biÄiuliams kÄ… dabar klausotÄ—s
+Comment[mk]=Им кажува на вашите пријатели што Ñлушате
+Comment[nb]=Forteller venner hva du hører på
+Comment[nds]=Vertellt Dien Frünnen, welke Musik Du jüst höörst
+Comment[ne]=तपाईà¤à¤²à¥‡ सà¥à¤¨à¤¿à¤°à¤¹à¤¨à¥à¤­à¤à¤•à¥‹ तपाईà¤à¤•à¥‹ साथीलाई भनà¥à¤¦à¤›
+Comment[nl]=Vertelt je vrienden naar welke muziek je luistert
+Comment[nn]=Fortel venner kva du høyrer på
+Comment[pl]=Przekazuje znajomym, czego obecnie słuchasz
+Comment[pt]=Indica aos seus amigos o que você está a ouvir
+Comment[pt_BR]=Diz a seus colegas o que você está escutando
+Comment[ru]=Сообщает вашим ÑобеÑедникам что вы ÑÐµÐ¹Ñ‡Ð°Ñ Ñлушаете
+Comment[se]=Muitala olbmáide maid dál leat guldaleamen
+Comment[sk]=Oznámi vaÅ¡im priateľom, koho práve poÄúvate
+Comment[sl]=SporoÄí vaÅ¡im prijateljem, kaj posluÅ¡ate
+Comment[sr]=Говори вашим другарима шта тренутно Ñлушате
+Comment[sr@Latn]=Govori vašim drugarima šta trenutno slušate
+Comment[sv]=Talar om för kompisar vad du lyssnar på
+Comment[ta]=நீஙà¯à®•à®³à¯ கேடà¯à®Ÿà¯à®•à¯ கொணà¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à¯ˆ உஙà¯à®•à®³à¯ எதிராலிகà¯à®•à¯ சொலà¯à®²à¯à®®à¯
+Comment[tg]=Ба дӯÑтонатон чи гӯш карда иÑтодаатонро мегӯÑд
+Comment[tr]=Dinlediğiniz arkadaşların listesini söyler
+Comment[uk]=Сповіщає ваших друзів про, те що ви зараз Ñлухаєте
+Comment[zh_CN]=告诉您的好å‹æ‚¨æ­£åœ¨æ”¶å¬çš„内容
+Comment[zh_HK]=告訴您的伙伴您正在è†è½ç”šéº¼
+Comment[zh_TW]=告訴您的好å‹æ‚¨æ­£åœ¨æ”¶è½ä»€éº¼
+
diff --git a/kopete/plugins/nowlistening/kopete_nowlistening_config.desktop b/kopete/plugins/nowlistening/kopete_nowlistening_config.desktop
new file mode 100644
index 00000000..8b01b2bd
--- /dev/null
+++ b/kopete/plugins/nowlistening/kopete_nowlistening_config.desktop
@@ -0,0 +1,121 @@
+[Desktop Entry]
+Icon=kaboodle
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_nowlistening
+X-KDE-FactoryName=NowListeningConfigFactory
+X-KDE-ParentApp=kopete_nowlistening
+X-KDE-ParentComponents=kopete_nowlistening
+
+Name=Now Listening
+Name[ar]=الاستماع جار
+Name[be]=Слухаю зараз
+Name[bg]=Инфо за пеÑен
+Name[bn]=à¦à¦–ন শà§à¦¨à¦›à§‡à¦¨
+Name[bs]=Sada slušam
+Name[ca]=Ara s'escolta
+Name[cs]=Nyní poslouchám
+Name[cy]=Rwan yn Gwrando ar
+Name[da]=Lytter nu
+Name[de]=Im Hintergrund läuft
+Name[el]=ΤώÏα ακοÏω
+Name[eo]=Nun aÅ­skultanta
+Name[es]=Escuchando
+Name[et]=Praegu kuulan
+Name[eu]=Orain entzuten
+Name[fa]=الان گوش می‌کند
+Name[fi]=Nyt soi
+Name[fr]=En écoute
+Name[ga]=Ag Éisteacht Anois
+Name[gl]=Escoitando
+Name[he]=מ×זין עכשיו
+Name[hi]=नाऊ लिसनिंग
+Name[hr]=Sada slušam
+Name[hu]=Most figyelek
+Name[is]=Hlustandi á
+Name[it]=Adesso sto ascoltando
+Name[ja]=今è´ã„ã¦ã„ã‚‹ã‚‚ã®
+Name[ka]=áƒáƒ®áƒšáƒ ისმინებáƒ
+Name[kk]=Тыңдауда
+Name[km]=កំពុង​ស្ដាប់
+Name[lt]=Dabar klausau
+Name[mk]=Сега Ñлуша
+Name[nb]=Hører nå
+Name[nds]=In'n Achtergrund löppt
+Name[ne]=अहिले सà¥à¤¨à¤¿à¤°à¤¹à¥‡à¤•à¥‹ छ
+Name[nl]=Luistert naar
+Name[nn]=Høyrer på
+Name[pa]=ਹà©à¨£ ਸà©à¨£à¨¦à¨¾
+Name[pl]=Czego słucham
+Name[pt]=Agora a Ouvir
+Name[pt_BR]=Ouvindo agora
+Name[ru]=Ð¡ÐµÐ¹Ñ‡Ð°Ñ Ð·Ð²ÑƒÑ‡Ð¸Ñ‚
+Name[se]=Dál guldaleamen
+Name[sk]=Teraz poÄúvam
+Name[sl]=Zdaj poslušam
+Name[sr]=Сада Ñлушам
+Name[sr@Latn]=Sada slušam
+Name[sv]=Lyssnar nu
+Name[ta]=தறà¯à®ªà¯‹à®¤à¯ கேடà¯à®Ÿà¯à®•à¯à®•à¯Šà®£à¯à®Ÿà¯à®³à¯à®³à®¾à®°à¯
+Name[tg]=Ҳоло гӯшкунӣ рафта иÑтодааÑÑ‚
+Name[tr]=Åžimdi Dinleniyor
+Name[uk]=Зараз Ñлухаю
+Name[zh_CN]=现在收å¬
+Name[zh_HK]=ç¾æ­£è†è½
+Name[zh_TW]=正在收è½
+Comment=Tells your buddies what you're listening to
+Comment[ar]=تخبر أصدقائك بما تستمع إليه
+Comment[be]=ПаведамлÑе ÑÑбрам, што вы зараз Ñлухаеце
+Comment[bg]=ПриÑтавка за информиране на приÑтелите какво Ñлушате
+Comment[bn]=আপনার বনà§à¦§à§à¦¦à§‡à¦° বলে আপনি কি শà§à¦¨à¦›à§‡à¦¨
+Comment[bs]=Obavještava vaše prijatelje o muzici koju slušate
+Comment[ca]=Li diu als vostres companys què esteu escoltant
+Comment[cs]=Sdělí vašim kamarádům, co právě posloucháte
+Comment[cy]=Dweud wrth eich cyfeillion be y gwrandwch arnodd
+Comment[da]=Fortæller dine venner hvad du lytter til
+Comment[de]=Teilt Ihren Freunden mit, welche Musik Sie gerade hören
+Comment[el]=ΕνημεÏώνει τους φίλους σας σχετικά με το τι ακοÏτε
+Comment[es]=Le cuenta a sus contactos lo que está escuchando
+Comment[et]=Semudele teatamine, mida parajasti kuulad
+Comment[eu]=Zure lagunei zer entzuten ari zaren esaten die
+Comment[fa]=به دوستان شما می‌گوید که به چه چیز گوش می‌دهید
+Comment[fi]=Kertoo kavereillesi mitä kuuntelet tällä hetkellä
+Comment[fr]=Indique à vos contacts ce que vous êtes en train d'écouter
+Comment[gl]=Dílle ós teus amigos que estás a escoitar
+Comment[he]=מספר לחבריך ל×יזו מוסיקה ×תה מ×זין כרגע
+Comment[hi]=आपके बडà¥à¤¡à¥€à¤¸ को बताता है कि आप कà¥à¤¯à¤¾ सà¥à¤¨ रहे हैं
+Comment[hr]=Govori vašim prijateljima što trenutno slušate
+Comment[hu]=A partnerek értesítése arról, hogy Ön mit hallgat
+Comment[is]=Segir vinum þínum á hvað þú ert að hlusta
+Comment[it]=Di' ai tuoi amici cosa stai ascoltando
+Comment[ja]=何をè´ã„ã¦ã„ã‚‹ã‹ã‚’仲間ã«ä¼ãˆã‚‹
+Comment[ka]=áƒáƒ¢áƒ§áƒáƒ‘ინებს თქვენს მეგáƒáƒ‘რებს, თუ რáƒáƒ¡ უსმენთ
+Comment[kk]=ДоÑтарыңызға тыңдап тұрғаныңыз туралы хабарлайды
+Comment[km]=ប្រាប់​សម្លាញ់​អ្នក​ážáž¶ អ្នក​កំពុង​ស្ដាប់​អ្វី
+Comment[lt]=PraneÅ¡kite biÄiuliams kÄ… dabar klausotÄ—s
+Comment[mk]=Им кажува на вашите пријатели што Ñлушате
+Comment[nb]=Forteller venner hva du hører på
+Comment[nds]=Vertellt Dien Frünnen, welke Musik Du jüst höörst
+Comment[ne]=तपाईà¤à¤²à¥‡ सà¥à¤¨à¤¿à¤°à¤¹à¤¨à¥à¤­à¤à¤•à¥‹ तपाईà¤à¤•à¥‹ साथीलाई भनà¥à¤¦à¤›
+Comment[nl]=Vertelt je vrienden naar welke muziek je luistert
+Comment[nn]=Fortel venner kva du høyrer på
+Comment[pl]=Przekazuje znajomym, czego obecnie słuchasz
+Comment[pt]=Indica aos seus amigos o que você está a ouvir
+Comment[pt_BR]=Diz a seus colegas o que você está escutando
+Comment[ru]=Сообщает вашим ÑобеÑедникам что вы ÑÐµÐ¹Ñ‡Ð°Ñ Ñлушаете
+Comment[se]=Muitala olbmáide maid dál leat guldaleamen
+Comment[sk]=Oznámi vaÅ¡im priateľom, koho práve poÄúvate
+Comment[sl]=SporoÄí vaÅ¡im prijateljem, kaj posluÅ¡ate
+Comment[sr]=Говори вашим другарима шта тренутно Ñлушате
+Comment[sr@Latn]=Govori vašim drugarima šta trenutno slušate
+Comment[sv]=Talar om för kompisar vad du lyssnar på
+Comment[ta]=நீஙà¯à®•à®³à¯ கேடà¯à®Ÿà¯à®•à¯ கொணà¯à®Ÿà®¿à®°à¯à®ªà¯à®ªà®¤à¯ˆ உஙà¯à®•à®³à¯ எதிராலிகà¯à®•à¯ சொலà¯à®²à¯à®®à¯
+Comment[tg]=Ба дӯÑтонатон чи гӯш карда иÑтодаатонро мегӯÑд
+Comment[tr]=Dinlediğiniz arkadaşların listesini söyler
+Comment[uk]=Сповіщає ваших друзів про, те що ви зараз Ñлухаєте
+Comment[zh_CN]=告诉您的好å‹æ‚¨æ­£åœ¨æ”¶å¬çš„内容
+Comment[zh_HK]=告訴您的伙伴您正在è†è½ç”šéº¼
+Comment[zh_TW]=告訴您的好å‹æ‚¨æ­£åœ¨æ”¶è½ä»€éº¼
+
diff --git a/kopete/plugins/nowlistening/nlamarok.cpp b/kopete/plugins/nowlistening/nlamarok.cpp
new file mode 100644
index 00000000..15d19411
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlamarok.cpp
@@ -0,0 +1,125 @@
+/*
+ nlamarok.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete
+ Copyright (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to amaroK by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include <qstring.h>
+
+#include "nlmediaplayer.h"
+#include "nlamarok.h"
+
+NLamaroK::NLamaroK( DCOPClient *client ) : NLMediaPlayer()
+{
+ m_client = client;
+ m_type = Audio;
+ m_name = "Amarok";
+}
+
+void NLamaroK::update()
+{
+ m_playing = false;
+ m_newTrack = false;
+ QString newTrack;
+ QByteArray data, replyData;
+ QCString replyType;
+ QString result;
+
+ // see if amaroK is registered with DCOP
+ if ( !m_client->isApplicationRegistered( "amarok" ) )
+ {
+ kdDebug ( 14307 ) << "AmaroK is not running!\n" << endl;
+ return;
+ }
+
+ // see if it's playing
+ // use status() call first, if not supported (amaroK 1.0 or earlier), use isPlaying
+
+ if ( !m_client->call( "amarok", "player", "status()", data,
+ replyType, replyData ) )
+ {
+ kdDebug( 14307 ) << k_funcinfo << " DCOP status() returned error, falling back to isPlaying()." << endl;
+ if ( !m_client->call( "amarok", "player", "isPlaying()", data,
+ replyType, replyData ) )
+ {
+ kdDebug( 14307 ) << k_funcinfo << " DCOP error on Amarok." << endl;
+ }
+ else
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "bool" ) {
+ reply >> m_playing;
+ }
+ }
+ }
+ else
+ {
+ int status = 0;
+
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "int" ) {
+ reply >> status;
+ kdDebug( 14307 ) << k_funcinfo << "Amarok status()=" << status << endl;
+ }
+
+ if ( status )
+ {
+ m_playing = true;
+ }
+ }
+
+ if ( m_client->call( "amarok", "player", "title()", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> newTrack;
+ }
+ }
+
+ if ( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+
+ if ( m_client->call( "amarok", "player", "album()", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> m_album;
+ }
+ }
+
+ if ( m_client->call( "amarok", "player", "artist()", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> m_artist;
+ }
+ }
+}
+
diff --git a/kopete/plugins/nowlistening/nlamarok.h b/kopete/plugins/nowlistening/nlamarok.h
new file mode 100644
index 00000000..b79900c2
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlamarok.h
@@ -0,0 +1,40 @@
+/*
+ nlamarok.h
+
+ Kopete Now Listening To plugin
+
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to amaroK by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 NLAMAROK_H
+#define NLAMAROK_H
+
+#include <dcopclient.h>
+
+class NLamaroK : public NLMediaPlayer
+{
+ public:
+ NLamaroK( DCOPClient *client );
+ virtual void update();
+ protected:
+ DCOPClient *m_client;
+};
+
+#endif
+
diff --git a/kopete/plugins/nowlistening/nljuk.cpp b/kopete/plugins/nowlistening/nljuk.cpp
new file mode 100644
index 00000000..41de23bc
--- /dev/null
+++ b/kopete/plugins/nowlistening/nljuk.cpp
@@ -0,0 +1,112 @@
+/*
+ nljuk.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2003 by Ismail Donmez <ismail.donmez@boun.edu.tr>
+ Copyright (c) 2002,2003 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to JuK by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include <qstring.h>
+
+#include "nlmediaplayer.h"
+#include "nljuk.h"
+
+NLJuk::NLJuk( DCOPClient *client ) : NLMediaPlayer()
+{
+ m_client = client;
+ m_type = Audio;
+ m_name = "JuK";
+}
+
+void NLJuk::update()
+{
+ m_playing = false;
+ QString newTrack;
+
+ // see if JuK is registered with DCOP
+ if ( m_client->isApplicationRegistered( "juk" ) )
+ {
+ // see if it's playing
+ QByteArray data, replyData;
+ QCString replyType;
+ QString result;
+
+ if ( m_client->call( "juk", "Player", "playing()", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "bool" ) {
+ reply >> m_playing;
+ }
+ }
+
+ {
+ QDataStream arg( data, IO_WriteOnly );
+ arg << QString::fromLatin1("Album");
+ if ( m_client->call( "juk", "Player", "trackProperty(QString)", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> m_album;
+ }
+ }
+ }
+
+ {
+ QDataStream arg( data, IO_WriteOnly );
+ arg << QString::fromLatin1("Artist");
+ if ( m_client->call( "juk", "Player", "trackProperty(QString)", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> m_artist;
+ }
+ }
+ }
+
+ {
+ QDataStream arg( data, IO_WriteOnly );
+ arg << QString::fromLatin1("Title");
+ if ( m_client->call( "juk", "Player", "trackProperty(QString)", data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> newTrack;
+ }
+ }
+ }
+
+ if ( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+ else
+ m_newTrack = false;
+ }
+ else
+ kdDebug( 14307 ) << "Juk is not running!\n" << endl;
+}
+
diff --git a/kopete/plugins/nowlistening/nljuk.h b/kopete/plugins/nowlistening/nljuk.h
new file mode 100644
index 00000000..40794c59
--- /dev/null
+++ b/kopete/plugins/nowlistening/nljuk.h
@@ -0,0 +1,41 @@
+/*
+ nljuk.h
+
+ Kopete Now Listening To plugin
+
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2003 by Ismail Donmez <ismail.donmez@boun.edu.tr>
+
+ Kopete (c) 2002,2003 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to JuK by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 NLJUK_H
+#define NLJUK_H
+
+#include <dcopclient.h>
+
+class NLJuk : public NLMediaPlayer
+{
+ public:
+ NLJuk( DCOPClient *client );
+ virtual void update();
+ protected:
+ DCOPClient *m_client;
+};
+
+#endif
+
diff --git a/kopete/plugins/nowlistening/nlkaffeine.cpp b/kopete/plugins/nowlistening/nlkaffeine.cpp
new file mode 100644
index 00000000..fe02077f
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlkaffeine.cpp
@@ -0,0 +1,101 @@
+/*
+ nlkaffeine.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete
+ Copyright (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to Kaffeine by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include <qstring.h>
+
+#include "nlmediaplayer.h"
+#include "nlkaffeine.h"
+
+NLKaffeine::NLKaffeine( DCOPClient *client ) : NLMediaPlayer()
+{
+ m_client = client;
+ m_type = Video;
+ m_name = "Kaffeine";
+}
+
+void NLKaffeine::update()
+{
+ m_playing = false;
+ m_newTrack = false;
+ QString newTrack;
+ bool error = true; // Asume we have a error first.
+ QCString kaffeineIface("Kaffeine"), kaffeineGetTrack("getTitle()");
+
+ // see if kaffeine is registered with DCOP
+ if ( m_client->isApplicationRegistered( "kaffeine" ) )
+ {
+ // see if it's playing
+ QByteArray data, replyData;
+ QCString replyType;
+ QString result;
+ if ( !m_client->call( "kaffeine", kaffeineIface, "isPlaying()", data,
+ replyType, replyData ) )
+ {
+ kdDebug ( 14307 ) << k_funcinfo << " Trying DCOP interface of Kaffeine >= 0.5" << endl;
+ // Trying with the new Kaffeine DCOP interface (>=0.5)
+ kaffeineIface = "KaffeineIface";
+ kaffeineGetTrack = "title()";
+ if( !m_client->call( "kaffeine", kaffeineIface, "isPlaying()", data, replyType, replyData ) )
+ {
+ kdDebug( 14307 ) << k_funcinfo << " DCOP error on Kaffeine." << endl;
+ }
+ else
+ {
+ error = false;
+ }
+ }
+ else
+ {
+ error = false;
+ }
+
+ // If we didn't get any DCOP error, check if Kaffeine is playing.
+ if(!error)
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "bool" ) {
+ reply >> m_playing;
+ kdDebug( 14307 ) << "checked if Kaffeine is playing!" << endl;
+ }
+ }
+
+ if ( m_client->call( "kaffeine", kaffeineIface, kaffeineGetTrack, data,
+ replyType, replyData ) )
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+
+ if ( replyType == "QString" ) {
+ reply >> newTrack;
+ }
+ }
+ if( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+ }
+ else
+ kdDebug ( 14307 ) << "Kaffeine is not running!\n" << endl;
+}
+
diff --git a/kopete/plugins/nowlistening/nlkaffeine.h b/kopete/plugins/nowlistening/nlkaffeine.h
new file mode 100644
index 00000000..7f868e79
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlkaffeine.h
@@ -0,0 +1,40 @@
+/*
+ nlkaffeine.h
+
+ Kopete Now Listening To plugin
+
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to Kaffeine by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 NLKAFFEINE_H
+#define NLKAFFEINE_H
+
+#include <dcopclient.h>
+
+class NLKaffeine : public NLMediaPlayer
+{
+ public:
+ NLKaffeine( DCOPClient *client );
+ virtual void update();
+ protected:
+ DCOPClient *m_client;
+};
+
+#endif
+
diff --git a/kopete/plugins/nowlistening/nlkscd.cpp b/kopete/plugins/nowlistening/nlkscd.cpp
new file mode 100644
index 00000000..a20c809b
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlkscd.cpp
@@ -0,0 +1,121 @@
+/*
+ nlkscd.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to KsCD by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include <qstringlist.h>
+
+#include "nlmediaplayer.h"
+
+#include "nlkscd.h"
+
+NLKscd::NLKscd( DCOPClient *client ) : NLMediaPlayer()
+{
+ m_client = client;
+ m_type = Audio;
+ m_name = "KsCD";
+}
+
+void NLKscd::update()
+{
+ m_playing = false;
+ QString newTrack;
+ // see if it's registered with DCOP
+ if ( m_client->isApplicationRegistered( "kscd" ) )
+ {
+ // see if it's playing
+ QByteArray data, replyData;
+ QCString replyType;
+ if ( !m_client->call( "kscd", "CDPlayer", "playing()", data,
+ replyType, replyData ) )
+ {
+ // we're talking to a KsCD without the playing() method
+ m_playing = true;
+// kdDebug( 14307 ) << "NLKscd::update() - KsCD without playing()"
+// << endl;
+ }
+ else
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "bool" ) {
+ reply >> m_playing;
+// kdDebug( 14307 ) << "NLKscd::update() - KsCD is " <<
+// ( m_playing ? "" : "not " ) << "playing!" << endl;
+ }
+ }
+ // poll it for its current artist
+ if ( !m_client->call( "kscd", "CDPlayer",
+ "currentArtist()", data, replyType, replyData ) )
+ kdDebug( 14307 ) << "NLKscd::update() DCOP error"
+ << endl;
+ else {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "QString" )
+ reply >> m_artist;
+ else
+ kdDebug( 14307 ) << "NLKscd::update() trackList returned unexpected reply type!" << endl;
+ }
+
+ //album
+ if ( !m_client->call( "kscd", "CDPlayer",
+ "currentAlbum()", data, replyType, replyData ) )
+ kdDebug( 14307 ) << "NLKscd::update() DCOP error"
+ << endl;
+ else {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "QString" )
+ reply >> m_album;
+ else
+ kdDebug( 14307 ) << "NLKscd::update() trackList returned unexpected reply type!" << endl;
+ }
+
+ // Get the current track title
+ if ( !m_client->call( "kscd", "CDPlayer",
+ "currentTrackTitle()", data, replyType, replyData ) )
+ kdDebug( 14307 ) << "NLKscd::update() - there was some error using DCOP." << endl;
+ else {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "QString" ) {
+ reply >> newTrack;
+ //kdDebug( 14307 ) << "the result is: " << newTrack.latin1()
+ // << endl;
+ } else
+ kdDebug( 14307 ) << "NLKscd::update()- currentTrackTitle "
+ << "returned unexpected reply type!" << endl;
+ }
+ // if the current track title has changed
+ if ( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+ else
+ m_newTrack = false;
+// kdDebug( 14307 ) << "NLKscd::update() - found kscd - "
+// << m_track << endl;
+
+ }
+// else
+// kdDebug( 14307 ) << "NLKscd::update() - kscd not found" << endl;
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlkscd.h b/kopete/plugins/nowlistening/nlkscd.h
new file mode 100644
index 00000000..e245ec76
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlkscd.h
@@ -0,0 +1,41 @@
+/*
+ nlkscd.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to Kscd by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 NLKSCD_H
+#define NLKSCD_H
+
+#include <dcopclient.h>
+
+class NLKscd : public NLMediaPlayer
+{
+ public:
+ NLKscd( DCOPClient *client );
+ virtual void update();
+ protected:
+ DCOPClient *m_client;
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlmediaplayer.h b/kopete/plugins/nowlistening/nlmediaplayer.h
new file mode 100644
index 00000000..5c619150
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlmediaplayer.h
@@ -0,0 +1,58 @@
+/*
+ nlmediaplayer.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ Represents a generic media player
+ and abstracts real media players' actual interfaces (DCOP for KDE apps,
+ otherwise anything goes!
+
+ *************************************************************************
+ * *
+ * 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 NLMEDIAPLAYER_H
+#define NLMEDIAPLAYER_H
+
+class NLMediaPlayer
+{
+ public:
+ enum NLMediaType { Audio, Video };
+ NLMediaPlayer() { m_playing = false; m_artist = ""; m_album = ""; m_track = ""; m_newTrack = false; }
+ virtual ~NLMediaPlayer() {}
+ /**
+ * This communicates with the actual mediaplayer and updates
+ * the model of its state in this class
+ */
+ virtual void update() = 0;
+ bool playing() const { return m_playing; }
+ bool newTrack() const { return m_newTrack; }
+ QString artist() const { return m_artist; }
+ QString album() const { return m_album; }
+ QString track() const { return m_track; }
+ QString name() const{ return m_name; }
+ NLMediaType mediaType() const { return m_type; }
+ protected:
+ // The name of the application
+ QString m_name;
+ bool m_playing;
+ bool m_newTrack;
+ QString m_artist;
+ QString m_album;
+ QString m_track;
+ NLMediaType m_type;
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlnoatun.cpp b/kopete/plugins/nowlistening/nlnoatun.cpp
new file mode 100644
index 00000000..62bdc8ba
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlnoatun.cpp
@@ -0,0 +1,147 @@
+/*
+ nlnoatun.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to Noatun by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include "nlmediaplayer.h"
+#include "nlnoatun.h"
+
+NLNoatun::NLNoatun( DCOPClient *client ) : NLMediaPlayer()
+{
+ m_client = client;
+ m_name = "noatun";
+ // FIXME - detect current media type in update()
+ m_type = Audio;
+}
+
+void NLNoatun::update()
+{
+ // Thanks mETz for telling me about Noatun's currentProperty()
+ m_playing = false;
+ QString newTrack;
+ // see if it's registered with DCOP
+ QCString appname = find();
+ if ( !appname.isEmpty() )
+ {
+ // see if it's playing
+ QByteArray data, replyData;
+ QCString replyType;
+ if ( !m_client->call( appname, "Noatun", "state()", data,
+ replyType, replyData ) )
+ {
+ kdDebug( 14307 ) << "NLNoatun::update() DCOP error on " << appname << endl;
+ }
+ else
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "int" ) {
+ int state = 0;
+ reply >> state;
+ m_playing = ( state == 2 );
+ //kdDebug( 14307 ) << "checked if Noatun is playing!" << endl;
+ }
+ }
+ // poll it for its current songtitle, artist and album
+ // Using properties
+ m_artist = currentProperty( appname, "author" );
+ m_album = currentProperty( appname, "album" );
+ QString title = currentProperty( appname, "title" );
+ // if properties not set ( no id3 tags... ) fallback to filename
+ if ( !title.isEmpty() )
+ newTrack = title;
+ else
+ // Using the title() method
+ if ( !m_client->call( appname, "Noatun",
+ "title()", data, replyType, replyData ) )
+ kdDebug( 14307 ) << "NLNoatun::update() DCOP error on " << appname
+ << endl;
+ else {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "QString" ) {
+ reply >> newTrack;
+ } else
+ kdDebug( 14307 ) << "NLNoatun::update(), title() returned unexpected reply type!" << endl;
+ }
+ // if the current track title has changed
+ if ( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+ else
+ m_newTrack = false;
+ kdDebug( 14307 ) << "NLNoatun::update() - found "<< appname << " - "
+ << m_track << endl;
+
+ }
+ else
+ kdDebug( 14307 ) << "NLNoatun::update() - noatun not found" << endl;
+}
+
+QCString NLNoatun::find() const
+{
+ QCString app = "noatun";
+ if ( !m_client->isApplicationRegistered( app ) )
+ {
+ // looking for a registered app prefixed with 'app'
+ QCStringList allApps = m_client->registeredApplications();
+ QCStringList::iterator it;
+ for ( it = allApps.begin(); it != allApps.end(); it++ )
+ {
+ //kdDebug( 14307 ) << ( *it ) << endl;
+ if ( ( *it ).left( 6 ) == app )
+ {
+ app = ( *it );
+ break;
+ }
+ }
+ // not found, set app to ""
+ if ( it == allApps.end() )
+ app = "";
+ }
+ return app;
+}
+
+QString NLNoatun::currentProperty( QCString appname, QString property ) const
+{
+ QByteArray data, replyData;
+ QCString replyType;
+ QDataStream arg( data, IO_WriteOnly );
+ QString result = "";
+ arg << property;
+ if ( !m_client->call( appname, "Noatun",
+ "currentProperty(QString)", data, replyType, replyData ) )
+ {
+ kdDebug( 14307 ) << "NLNoatun::currentProperty() DCOP error on "
+ << appname << endl;
+ }
+ else
+ {
+ QDataStream reply( replyData, IO_ReadOnly );
+ if ( replyType == "QString" )
+ {
+ reply >> result;
+ }
+ }
+ return result;
+}
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlnoatun.h b/kopete/plugins/nowlistening/nlnoatun.h
new file mode 100644
index 00000000..88441754
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlnoatun.h
@@ -0,0 +1,41 @@
+/*
+ nlnoatun.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to Noatun by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 NLNOATUN_H
+#define NLNOATUN_H
+
+#include <dcopclient.h>
+
+class NLNoatun : public NLMediaPlayer
+{
+ public:
+ NLNoatun( DCOPClient *client );
+ virtual void update();
+ protected:
+ QCString find() const;
+ QString currentProperty( QCString appname, QString property ) const;
+ DCOPClient *m_client;
+};
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlxmms.cpp b/kopete/plugins/nowlistening/nlxmms.cpp
new file mode 100644
index 00000000..f0a9f47a
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlxmms.cpp
@@ -0,0 +1,73 @@
+/*
+ nlxmms.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to the X Multimedia System (xmms) by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 "config.h"
+
+#ifdef HAVE_XMMS
+
+#include <kdebug.h>
+#include <xmmsctrl.h> // need to fix Makefile.am for this?
+#include "nlmediaplayer.h"
+#include "nlxmms.h"
+
+NLXmms::NLXmms() : NLMediaPlayer()
+{
+ m_name = "Xmms";
+}
+
+
+void NLXmms::update()
+{
+ //look for running xmms
+ if ( xmms_remote_get_version( 0 ) )
+ {
+ QString newTrack;
+ // see if it's playing
+ if ( xmms_remote_is_playing( 0 ) && !xmms_remote_is_paused( 0 ) )
+ {
+ m_playing = true;
+
+ // get the artist and album title
+ // get the song title
+ newTrack = xmms_remote_get_playlist_title( 0, xmms_remote_get_playlist_pos( 0 ) );
+ //kdDebug( 14307 ) << "NLXmms::update() - track is: " << m_track << endl;
+ m_artist = newTrack.section( " - ", 0, 0 );
+ newTrack = newTrack.section( " - ", -1, -1 );
+ }
+ else
+ m_playing = false;
+ // check if it's a new song
+ if ( newTrack != m_track )
+ {
+ m_newTrack = true;
+ m_track = newTrack;
+ }
+ else
+ m_newTrack = false;
+ kdDebug( 14307 ) << k_funcinfo << " - found xmms - " << m_track << endl;
+ }
+ else
+ kdDebug( 14307 ) << k_funcinfo << " - xmms not found" << endl;
+}
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nlxmms.h b/kopete/plugins/nowlistening/nlxmms.h
new file mode 100644
index 00000000..14c74ea8
--- /dev/null
+++ b/kopete/plugins/nowlistening/nlxmms.h
@@ -0,0 +1,41 @@
+/*
+ nlxmms.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Purpose:
+ This class abstracts the interface to the X Multimedia System (xmms) by
+ implementing NLMediaPlayer
+
+ *************************************************************************
+ * *
+ * 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 "config.h"
+
+#ifndef NLXMMS_H
+#define NLXMMS_H
+
+#ifdef HAVE_XMMS
+
+class NLXmms : public NLMediaPlayer
+{
+ public:
+ NLXmms();
+ virtual void update();
+};
+
+#endif
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nowlisteningchatui.rc b/kopete/plugins/nowlistening/nowlisteningchatui.rc
new file mode 100644
index 00000000..9ab501f7
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningchatui.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopete_nowlisteningchat">
+ <MenuBar>
+ <Menu name="tools">
+ <text>&amp;Tools</text>
+ <Action name="actionSendAdvert"/>
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/kopete/plugins/nowlistening/nowlisteningconfig.kcfg b/kopete/plugins/nowlistening/nowlisteningconfig.kcfg
new file mode 100644
index 00000000..8233b737
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningconfig.kcfg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Author: Michaël Larouche-->
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kopeterc"/>
+
+ <group name="Now Listening Plugin">
+ <entry name="Header" type="String">
+ <label>Header of the message advertised.</label>
+ <default code="true">i18n("Now listening to:")</default>
+ </entry>
+
+ <entry name="PerTrack" type="String">
+ <label>Core of the message advertised.</label>
+ <default code="true">i18n("%track( by %artist)( on %album)")</default>
+ </entry>
+
+ <entry name="Conjunction" type="String">
+ <label>Conjunction when multiple track are playing.</label>
+ <default code="true">i18n(", and ")</default>
+ </entry>
+
+ <entry name="ExplicitAdvertising" type="Bool">
+ <label>Show explicitly the current music listened via a menu or /media command.</label>
+ <default>true</default>
+ </entry>
+
+ <entry name="ChatAdvertising" type="Bool">
+ <label>Show the current music listened in chat window.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="StatusAdvertising" type="Bool">
+ <label>Show the current music listened in place of your status message.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="AppendStatusAdvertising" type="Bool">
+ <label>Show the current music listened appended to your status message.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="UseSpecifiedMediaPlayer" type="Bool">
+ <label>Use the specified media player.</label>
+ <default>false</default>
+ </entry>
+
+ <entry name="SelectedMediaPlayer" type="Int">
+ <label>Selected Media Player for source of listening advertising.</label>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kopete/plugins/nowlistening/nowlisteningconfig.kcfgc b/kopete/plugins/nowlistening/nowlisteningconfig.kcfgc
new file mode 100644
index 00000000..87c53b77
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningconfig.kcfgc
@@ -0,0 +1,8 @@
+# Code generation options for kconfig_compiler
+File=nowlisteningconfig.kcfg
+ClassName=NowListeningConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true
+IncludeFiles=klocale.h
diff --git a/kopete/plugins/nowlistening/nowlisteningguiclient.cpp b/kopete/plugins/nowlistening/nowlisteningguiclient.cpp
new file mode 100644
index 00000000..8e7b1908
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningguiclient.cpp
@@ -0,0 +1,79 @@
+/*
+ nowlisteningguiclient.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "nowlisteningguiclient.h"
+#include "nowlisteningplugin.h"
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+NowListeningGUIClient::NowListeningGUIClient( Kopete::ChatSession *parent, NowListeningPlugin *plugin )
+ : QObject(parent) , KXMLGUIClient(parent)
+{
+ connect(plugin, SIGNAL(readyForUnload()), SLOT(slotPluginUnloaded()));
+ m_msgManager = parent;
+ m_action = new KAction( i18n( "Send Media Info" ), 0, this,
+ SLOT( slotAdvertToCurrentChat() ), actionCollection(), "actionSendAdvert" );
+ setXMLFile("nowlisteningchatui.rc");
+}
+
+void NowListeningGUIClient::slotAdvertToCurrentChat()
+{
+ kdDebug( 14307 ) << k_funcinfo << endl;
+
+ // Sanity check - don't crash if the plugin is unloaded and we get called.
+ if (!NowListeningPlugin::plugin())
+ return;
+
+ QString message = NowListeningPlugin::plugin()->mediaPlayerAdvert();
+
+ // We warn in a mode appropriate to the mode the user invoked the
+ // plugin - GUI on menu action, in message if they typed '/media'
+ if ( message.isEmpty() )
+ {
+ QWidget * origin = 0L;
+ if ( m_msgManager && m_msgManager->view() )
+ origin = m_msgManager->view()->mainWidget();
+ KMessageBox::queuedMessageBox( origin, KMessageBox::Sorry,
+ i18n( "None of the supported media players (KsCD, JuK, amaroK, Noatun or Kaffeine) are playing anything." ),
+ i18n( "Nothing to Send" ) );
+ }
+ else
+ {
+ //advertise to a single chat
+ if ( m_msgManager )
+ NowListeningPlugin::plugin()->advertiseToChat( m_msgManager, message );
+ }
+}
+
+// The plugin itself is being unloaded - so remove the GUI entry.
+void NowListeningGUIClient::slotPluginUnloaded()
+{
+ m_action->unplugAll();
+}
+
+#include "nowlisteningguiclient.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nowlisteningguiclient.h b/kopete/plugins/nowlistening/nowlisteningguiclient.h
new file mode 100644
index 00000000..9f89d351
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningguiclient.h
@@ -0,0 +1,53 @@
+/*
+ nowlisteningguiclient.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef NOWLISTENINGGUICLIENT_H
+#define NOWLISTENINGGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+class KAction;
+class NowListeningPlugin;
+
+namespace Kopete {
+ class ChatSession;
+}
+
+class NowListeningGUIClient : public QObject, public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ NowListeningGUIClient( Kopete::ChatSession* parent, NowListeningPlugin* plugin );
+ virtual ~NowListeningGUIClient() {}
+
+protected slots:
+ void slotAdvertToCurrentChat();
+ void slotPluginUnloaded();
+
+private:
+ Kopete::ChatSession* m_msgManager;
+ KAction* m_action;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nowlisteningplugin.cpp b/kopete/plugins/nowlistening/nowlisteningplugin.cpp
new file mode 100644
index 00000000..e28316be
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningplugin.cpp
@@ -0,0 +1,554 @@
+/*
+ nowlisteningplugin.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kaction.h>
+
+#include "config.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopetecontact.h"
+#include "kopetecommandhandler.h"
+#include "kopeteaccount.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccountmanager.h"
+
+#include "nowlisteningconfig.h"
+#include "nowlisteningplugin.h"
+#include "nlmediaplayer.h"
+#include "nlkscd.h"
+#include "nlnoatun.h"
+#include "nljuk.h"
+#include "nlamarok.h"
+#include "nlkaffeine.h"
+#include "nowlisteningguiclient.h"
+
+#if defined Q_WS_X11 && !defined K_WS_QTONLY && defined HAVE_XMMS
+#include "nlxmms.h"
+#endif
+
+class NowListeningPlugin::Private
+{
+public:
+ Private() : m_currentMediaPlayer(0L), m_client(0L), m_currentChatSession(0L), m_currentMetaContact(0L),
+ advertTimer(0L)
+ {}
+
+ // abstracted media player interfaces
+ QPtrList<NLMediaPlayer> m_mediaPlayerList;
+ NLMediaPlayer *m_currentMediaPlayer;
+
+ // Needed for DCOP interprocess communication
+ DCOPClient *m_client;
+ Kopete::ChatSession *m_currentChatSession;
+ Kopete::MetaContact *m_currentMetaContact;
+
+ // Used when using automatic advertising to know who has already gotten
+ // the music information
+ QStringList m_musicSentTo;
+
+ // Used when advertising to status message.
+ QTimer *advertTimer;
+};
+
+typedef KGenericFactory<NowListeningPlugin> NowListeningPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_nowlistening, NowListeningPluginFactory( "kopete_nowlistening" ) )
+
+NowListeningPlugin::NowListeningPlugin( QObject *parent, const char* name, const QStringList& /*args*/ )
+: Kopete::Plugin( NowListeningPluginFactory::instance(), parent, name )
+{
+ if ( pluginStatic_ )
+ kdDebug( 14307 )<<"####"<<"Now Listening already initialized"<<endl;
+ else
+ pluginStatic_ = this;
+
+ d = new Private;
+
+ kdDebug(14307) << k_funcinfo << endl;
+
+ // Connection for the "/media" command (always needed)
+ connect( Kopete::ChatSessionManager::self(), SIGNAL(
+ chatSessionCreated( Kopete::ChatSession * )) , SLOT( slotNewKMM(
+ Kopete::ChatSession * ) ) );
+
+ // If autoadvertising is on...
+ connect(Kopete::ChatSessionManager::self(),
+ SIGNAL(aboutToSend(Kopete::Message&)),
+ this,
+ SLOT(slotOutgoingMessage(Kopete::Message&)));
+
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ slotNewKMM( *it );
+
+ // get a pointer to the dcop client
+ d->m_client = kapp->dcopClient(); //new DCOPClient();
+
+ // set up known media players
+ d->m_mediaPlayerList.setAutoDelete( true );
+ d->m_mediaPlayerList.append( new NLKscd( d->m_client ) );
+ d->m_mediaPlayerList.append( new NLNoatun( d->m_client ) );
+ d->m_mediaPlayerList.append( new NLJuk( d->m_client ) );
+ d->m_mediaPlayerList.append( new NLamaroK( d->m_client ) );
+ d->m_mediaPlayerList.append( new NLKaffeine( d->m_client ) );
+
+#if defined Q_WS_X11 && !defined K_WS_QTONLY && HAVE_XMMS
+ d->m_mediaPlayerList.append( new NLXmms() );
+#endif
+
+ // User has selected a specific mediaPlayer so update the currentMediaPlayer pointer.
+ if( NowListeningConfig::self()->useSpecifiedMediaPlayer() )
+ {
+ updateCurrentMediaPlayer();
+ }
+
+ // watch for '/media' getting typed
+ Kopete::CommandHandler::commandHandler()->registerCommand(
+ this,
+ "media",
+ SLOT( slotMediaCommand( const QString &, Kopete::ChatSession * ) ),
+ i18n("USAGE: /media - Displays information on current song"),
+ 0
+ );
+
+ connect ( this , SIGNAL( settingsChanged() ) , this , SLOT( slotSettingsChanged() ) );
+
+ // Advert the accounts with the current listened track.
+ d->advertTimer = new QTimer(this);
+ connect(d->advertTimer, SIGNAL( timeout() ), this, SLOT( slotAdvertCurrentMusic() ) );
+ d->advertTimer->start(5000); // Update every 5 seconds
+}
+
+NowListeningPlugin::~NowListeningPlugin()
+{
+ //kdDebug( 14307 ) << k_funcinfo << endl;
+
+ delete d;
+
+ pluginStatic_ = 0L;
+}
+
+void NowListeningPlugin::slotNewKMM(Kopete::ChatSession *KMM)
+{
+ new NowListeningGUIClient( KMM, this );
+}
+
+NowListeningPlugin* NowListeningPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+void NowListeningPlugin::slotMediaCommand( const QString &args, Kopete::ChatSession *theChat )
+{
+ QString advert = mediaPlayerAdvert();
+ if ( advert.isEmpty() )
+ {
+ // Catch no players/no track playing message case:
+ // Since we can't stop a message send in a plugin, add some message text to
+ // prevent us sending an empty message
+ advert = i18n("Message from Kopete user to another user; used when sending media information even though there are no songs playing or no media players running", "Now Listening for Kopete - it would tell you what I am listening to, if I was listening to something on a supported media player.");
+ }
+
+ Kopete::Message msg( theChat->myself(),
+ theChat->members(),
+ advert + " " + args,
+ Kopete::Message::Outbound,
+ Kopete::Message::RichText
+ );
+
+ theChat->sendMessage( msg );
+}
+
+void NowListeningPlugin::slotOutgoingMessage(Kopete::Message& msg)
+{
+ // Only do stuff if autoadvertising is on
+ if(!NowListeningConfig::self()->chatAdvertising())
+ return;
+
+ QString originalBody = msg.plainBody();
+
+ // If it is a /media message, don't process it
+ if(originalBody.startsWith(NowListeningConfig::self()->header()))
+ return;
+
+ // What will be sent
+ QString newBody;
+
+ // Getting the list of contacts the message will be sent to to determine if at least
+ // one of them has never gotten the current music information.
+ Kopete::ContactPtrList dest = msg.to();
+ bool mustSendAnyway = false;
+ for( Kopete::Contact *c = dest.first() ; c ; c = dest.next() )
+ {
+ const QString& cId = c->contactId();
+ if( 0 == d->m_musicSentTo.contains( cId ) )
+ {
+ mustSendAnyway = true;
+
+ // The contact will get the music information so we put it in the list.
+ d->m_musicSentTo.push_back( cId );
+ }
+ }
+
+ bool newTrack = newTrackPlaying();
+
+ // We must send the music information if someone has never gotten it or the track(s)
+ // has changed since it was last sent.
+ if ( mustSendAnyway || newTrack )
+ {
+ QString advert = mediaPlayerAdvert(false); // false since newTrackPlaying() did the update
+ if( !advert.isEmpty() )
+ newBody = originalBody + "<br>" + advert;
+
+ // If we send because the information has changed since it was last sent, we must
+ // rebuild the list of contacts the latest information was sent to.
+ if( newTrack )
+ {
+ d->m_musicSentTo.clear();
+ for( Kopete::Contact *c = dest.first() ; c ; c = dest.next() )
+ {
+ d->m_musicSentTo.push_back( c->contactId() );
+ }
+ }
+ }
+
+ // If the body has been modified, change the message
+ if( !newBody.isEmpty() )
+ {
+ msg.setBody( newBody, Kopete::Message::RichText );
+ }
+}
+
+void NowListeningPlugin::slotAdvertCurrentMusic()
+{
+ // Do anything when statusAdvertising is off.
+ if( !NowListeningConfig::self()->statusAdvertising() && !NowListeningConfig::self()->appendStatusAdvertising() )
+ return;
+
+ // This slot is called every 5 seconds, so we check if we have a new track playing.
+ if( newTrackPlaying() )
+ {
+ QString advert;
+
+ QPtrList<Kopete::Account> accountsList = Kopete::AccountManager::self()->accounts();
+ for( Kopete::Account* a = accountsList.first(); a; a = accountsList.next() )
+ {
+ /*
+ NOTE:
+ MSN status message(personal message) use a special tag to advert the current music playing.
+ So, we don't send the all formatted string, send a special string seperated by ";".
+
+ Also, do not use MSN hack in appending mode.
+ */
+ if( a->protocol()->pluginId() == "MSNProtocol" && !NowListeningConfig::self()->appendStatusAdvertising() )
+ {
+ QString track, artist, album, mediaList;
+ bool isPlaying=false;
+
+ if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer )
+ {
+ if( d->m_currentMediaPlayer->playing() )
+ {
+ track = d->m_currentMediaPlayer->track();
+ artist = d->m_currentMediaPlayer->artist();
+ album = d->m_currentMediaPlayer->album();
+ mediaList = track + ";" + artist + ";" + album;
+ isPlaying = true;
+ }
+ }
+ else
+ {
+ for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
+ {
+ if( i->playing() )
+ {
+ track = i->track();
+ artist = i->artist();
+ album = i->album();
+ mediaList = track + ";" + artist + ";" + album;
+ isPlaying = true;
+ }
+ }
+ }
+
+ // KDE4 TODO: Use the new status message framework, and remove this "hack".
+ if( isPlaying )
+ {
+ advert = QString("[Music]%1").arg(mediaList);
+ }
+
+ }
+ else
+ {
+ if( NowListeningConfig::self()->appendStatusAdvertising() )
+ {
+ // Check for the now listening message in parenthesis,
+ // include the header to not override other messages in parenthesis.
+ QRegExp statusSong( QString(" \\(%1.*\\)$").arg( NowListeningConfig::header()) );
+
+ // HACK: Don't keep appending the now listened song. Replace it in the status message.
+ advert = a->myself()->property( Kopete::Global::Properties::self()->awayMessage() ).value().toString();
+ // Remove the braces when they are no listened song.
+ QString mediaAdvert = mediaPlayerAdvert(false);
+ if(!mediaAdvert.isEmpty())
+ {
+ if(statusSong.search(advert) != -1)
+ {
+ advert = advert.replace(statusSong, QString(" (%1)").arg(mediaPlayerAdvert(false)) );
+ }
+ else
+ {
+ advert += QString(" (%1)").arg( mediaPlayerAdvert(false) );
+ }
+ }
+ else
+ {
+ advert = advert.replace(statusSong, "");
+ }
+ }
+ else
+ {
+ advert = mediaPlayerAdvert(false); // newTrackPlaying has done the update.
+ }
+ }
+
+ a->setOnlineStatus(a->myself()->onlineStatus(), advert);
+ }
+ }
+}
+
+QString NowListeningPlugin::mediaPlayerAdvert(bool update)
+{
+ // generate message for all players
+ QString message;
+
+ if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer != 0L )
+ {
+ buildTrackMessage(message, d->m_currentMediaPlayer, update);
+ }
+ else
+ {
+ for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
+ {
+ buildTrackMessage(message, i, update);
+ }
+ }
+
+ kdDebug( 14307 ) << k_funcinfo << message << endl;
+
+ return message;
+}
+
+void NowListeningPlugin::buildTrackMessage(QString &message, NLMediaPlayer *player, bool update)
+{
+ QString perTrack = NowListeningConfig::self()->perTrack();
+
+ if(update)
+ player->update();
+ if ( player->playing() )
+ {
+ kdDebug( 14307 ) << k_funcinfo << player->name() << " is playing" << endl;
+ if ( message.isEmpty() )
+ message = NowListeningConfig::self()->header();
+
+ if ( message != NowListeningConfig::self()->header() ) // > 1 track playing!
+ message = message + NowListeningConfig::self()->conjunction();
+ message = message + substDepthFirst( player, perTrack, false );
+ }
+}
+
+bool NowListeningPlugin::newTrackPlaying(void) const
+{
+ if( NowListeningConfig::self()->useSpecifiedMediaPlayer() && d->m_currentMediaPlayer != 0L )
+ {
+ d->m_currentMediaPlayer->update();
+ if( d->m_currentMediaPlayer->newTrack() )
+ return true;
+ }
+ else
+ {
+ for ( NLMediaPlayer* i = d->m_mediaPlayerList.first(); i; i = d->m_mediaPlayerList.next() )
+ {
+ i->update();
+ if( i->newTrack() )
+ return true;
+ }
+ }
+ return false;
+}
+
+QString NowListeningPlugin::substDepthFirst( NLMediaPlayer *player,
+ QString in, bool inBrackets ) const
+{
+ QString track = player->track();
+ QString artist = player->artist();
+ QString album = player->album();
+ QString playerName = player->name();
+
+ for ( unsigned int i = 0; i < in.length(); i++ )
+ {
+ QChar c = in.at( i );
+ //kdDebug(14307) << "Now working on:" << in << " char is: " << c << endl;
+ if ( c == '(' )
+ {
+ // find matching bracket
+ int depth = 0;
+ //kdDebug(14307) << "Looking for ')'" << endl;
+ for ( unsigned int j = i + 1; j < in.length(); j++ )
+ {
+ QChar d = in.at( j );
+ //kdDebug(14307) << "Got " << d << endl;
+ if ( d == '(' )
+ depth++;
+ if ( d == ')' )
+ {
+ // have we found the match?
+ if ( depth == 0 )
+ {
+ // recursively replace contents of matching ()
+ QString substitution = substDepthFirst( player,
+ in.mid( i + 1, j - i - 1), true ) ;
+ in.replace ( i, j - i + 1, substitution );
+ // perform substitution and return the result
+ i = i + substitution.length() - 1;
+ break;
+ }
+ else
+ depth--;
+ }
+ }
+ }
+ }
+ // no () found, perform substitution!
+ // get each string (to) to substitute for (from)
+ bool done = false;
+ if ( in.contains ( "%track" ) )
+ {
+ if ( track.isEmpty() )
+ track = i18n("Unknown track");
+
+ in.replace( "%track", track );
+ done = true;
+ }
+
+ if ( in.contains ( "%artist" ) && !artist.isEmpty() )
+ {
+ if ( artist.isEmpty() )
+ artist = i18n("Unknown artist");
+ in.replace( "%artist", artist );
+ done = true;
+ }
+ if ( in.contains ( "%album" ) && !album.isEmpty() )
+ {
+ if ( album.isEmpty() )
+ album = i18n("Unknown album");
+ in.replace( "%album", album );
+ done = true;
+ }
+ if ( in.contains ( "%player" ) && !playerName.isEmpty() )
+ {
+ if ( playerName.isEmpty() )
+ playerName = i18n("Unknown player");
+ in.replace( "%player", playerName );
+ done = true;
+ }
+ // make whether we return anything dependent on whether we
+ // were in brackets and if we were, if a substitution was made.
+ if ( inBrackets && !done )
+ return "";
+
+ return in;
+}
+
+void NowListeningPlugin::advertiseToChat( Kopete::ChatSession *theChat, QString message )
+{
+ Kopete::ContactPtrList pl = theChat->members();
+
+ // get on with it
+ kdDebug(14307) << k_funcinfo <<
+ ( pl.isEmpty() ? "has no " : "has " ) << "interested recipients: " << endl;
+/* for ( pl.first(); pl.current(); pl.next() )
+ kdDebug(14307) << "NowListeningPlugin::advertiseNewTracks() " << pl.current()->displayName() << endl; */
+ // if no-one in this KMM wants to be advertised to, don't send
+ // any message
+ if ( pl.isEmpty() )
+ return;
+ Kopete::Message msg( theChat->myself(),
+ pl,
+ message,
+ Kopete::Message::Outbound,
+ Kopete::Message::RichText );
+ theChat->sendMessage( msg );
+}
+
+void NowListeningPlugin::updateCurrentMediaPlayer()
+{
+ kdDebug(14307) << k_funcinfo << "Update current media player (single mode)" << endl;
+
+ d->m_currentMediaPlayer = d->m_mediaPlayerList.at( NowListeningConfig::self()->selectedMediaPlayer() );
+}
+
+void NowListeningPlugin::slotSettingsChanged()
+{
+ // Force reading config
+ NowListeningConfig::self()->readConfig();
+
+ // Update the currentMediaPlayer, because config has changed.
+ if( NowListeningConfig::useSpecifiedMediaPlayer() )
+ updateCurrentMediaPlayer();
+
+ disconnect(Kopete::ChatSessionManager::self(),
+ SIGNAL(aboutToSend(Kopete::Message&)),
+ this,
+ SLOT(slotOutgoingMessage(Kopete::Message&)));
+
+ d->advertTimer->stop();
+ disconnect(d->advertTimer, SIGNAL(timeout()), this, SLOT(slotAdvertCurrentMusic()));
+
+ if( NowListeningConfig::self()->chatAdvertising() )
+ {
+ kdDebug(14307) << k_funcinfo << "Now using chat window advertising." << endl;
+
+ connect(Kopete::ChatSessionManager::self(),
+ SIGNAL(aboutToSend(Kopete::Message&)),
+ this,
+ SLOT(slotOutgoingMessage(Kopete::Message&)));
+ }
+ else if( NowListeningConfig::self()->statusAdvertising() || NowListeningConfig::self()->appendStatusAdvertising() )
+ {
+ kdDebug(14307) << k_funcinfo << "Now using status message advertising." << endl;
+
+ connect(d->advertTimer, SIGNAL(timeout()), this, SLOT(slotAdvertCurrentMusic()));
+ d->advertTimer->start(5000);
+ }
+}
+
+NowListeningPlugin* NowListeningPlugin::pluginStatic_ = 0L;
+
+#include "nowlisteningplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nowlisteningplugin.h b/kopete/plugins/nowlistening/nowlisteningplugin.h
new file mode 100644
index 00000000..7a608fd2
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningplugin.h
@@ -0,0 +1,111 @@
+/*
+ nowlisteningplugin.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef NOWLISTENINGPLUGIN_H
+#define NOWLISTENINGPLUGIN_H
+
+
+#include "kopeteplugin.h"
+#include <qptrlist.h>
+
+namespace Kopete { class ChatSession; class Message; }
+
+class NLMediaPlayer;
+class QStringList;
+
+/**
+ * @author Will Stephenson
+ * @author Michaël Larouche
+ */
+class NowListeningPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+friend class NowListeningGUIClient;
+
+ public:
+ NowListeningPlugin( QObject *parent, const char *name, const QStringList &args );
+ virtual ~NowListeningPlugin();
+ static NowListeningPlugin* plugin();
+
+ public slots:
+ void slotMediaCommand( const QString &, Kopete::ChatSession *theChat );
+ void slotOutgoingMessage(Kopete::Message&);
+ void slotAdvertCurrentMusic();
+
+ protected:
+ /**
+ * Constructs a string containing the track information.
+ * @param update Whether the players must update their data. It can be
+ * useful to set it to false if one already has called
+ * update somewhere else, for instance in newTrackPlaying().
+ */
+ QString mediaPlayerAdvert(bool update = true);
+ /**
+ * @internal Build the message for @ref mediaPlayerAdvert
+ * @param message Reference to the messsage, because return QString cause data loss.
+ * @param player Pointer to the current Media Player.
+ * Used to get the information about the current track playing.
+ * @param update Whether the players must update their data. It can be
+ * useful to set it to false if one already has called
+ * update somewhere else, for instance in newTrackPlaying().
+ */
+ void buildTrackMessage(QString &message, NLMediaPlayer *player, bool update);
+ /**
+ * @return true if one of the players has changed track since the last message.
+ */
+ bool newTrackPlaying(void) const;
+ /**
+ * Creates the string for a single player
+ * @p player - the media player we're using
+ * @p in - the source format string
+ * @p bool - is this call within a set of brackets for conditional expansion?
+ */
+ QString substDepthFirst( NLMediaPlayer *player, QString in, bool inBrackets) const;
+ /**
+ * Sends a message to a single chat
+ */
+ void advertiseToChat( Kopete::ChatSession* theChat, QString message );
+ /**
+ * Update the currentMedia pointer on config change.
+ */
+ void updateCurrentMediaPlayer();
+
+ protected slots:
+ /**
+ * Reacts to a new chat starting and adds actions to its GUI
+ */
+ void slotNewKMM( Kopete::ChatSession* );
+
+ /**
+ * Reacts to the plugin's settings changed signal, originating from the KCModule dispatcher
+ */
+ void slotSettingsChanged();
+
+ private:
+ class Private;
+ Private *d;
+
+ static NowListeningPlugin* pluginStatic_;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/nowlistening/nowlisteningpreferences.cpp b/kopete/plugins/nowlistening/nowlisteningpreferences.cpp
new file mode 100644
index 00000000..179ce3a5
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningpreferences.cpp
@@ -0,0 +1,95 @@
+/*
+ nowlisteningpreferences.cpp
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qspinbox.h>
+#include <qlineedit.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+
+#include <klistbox.h>
+#include <klocale.h>
+#include <kgenericfactory.h>
+
+#include "config.h" // for HAVE_XMMS
+#include "nowlisteningprefs.h"
+#include "nowlisteningconfig.h"
+#include "nowlisteningpreferences.h"
+#include "nowlisteningpreferences.moc"
+
+typedef KGenericFactory<NowListeningPreferences> NowListeningPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_nowlistening, NowListeningPreferencesFactory( "kcm_kopete_nowlistening" ) )
+
+
+NowListeningPreferences::NowListeningPreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule( NowListeningPreferencesFactory::instance(), parent, args )
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new NowListeningPrefsUI( this );
+
+ addConfig( NowListeningConfig::self(), preferencesDialog );
+
+ // Fill the media player listbox.
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("Kscd"));
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("Noatun"));
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("Juk"));
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("amaroK"));
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("Kaffeine"));
+#if defined Q_WS_X11 && !defined K_WS_QTONLY && defined HAVE_XMMS
+ preferencesDialog->kcfg_SelectedMediaPlayer->insertItem(QString::fromUtf8("XMMS"));
+#endif
+ load();
+}
+
+NowListeningPreferences::~NowListeningPreferences( )
+{
+ delete preferencesDialog;
+}
+
+void NowListeningPreferences::save()
+{
+ KCModule::save();
+}
+
+void NowListeningPreferences::load()
+{
+ KCModule::load();
+}
+
+void NowListeningPreferences::slotSettingsChanged()
+{
+ emit changed( true );
+}
+
+void NowListeningPreferences::defaults()
+{
+ /*preferencesDialog->m_header->setText( i18n("Now Listening To: "));
+ preferencesDialog->m_perTrack->setText(i18n("%track( by %artist)( on %album)"));
+ preferencesDialog->m_conjunction->setText( i18n(", and "));
+ preferencesDialog->m_autoAdvertising->setChecked( false );*/
+}
+
+/*
+* Local variables:
+* c-indentation-style: k&r
+* c-basic-offset: 8
+* indent-tabs-mode: t
+* End:
+*/
+// vim: set noet ts=4 sts=4 sw=4:
+//
diff --git a/kopete/plugins/nowlistening/nowlisteningpreferences.h b/kopete/plugins/nowlistening/nowlisteningpreferences.h
new file mode 100644
index 00000000..14d9ceea
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningpreferences.h
@@ -0,0 +1,59 @@
+/*
+ nowlisteningpreferences.h
+
+ Kopete Now Listening To plugin
+
+ Copyright (c) 2002,2003,2004 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003,2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef NOWLISTENINGPREFERENCES_H
+#define NOWLISTENINGPREFERENCES_H
+
+#include <kcmodule.h>
+
+class NowListeningPrefsUI;
+class NowListeningConfig;
+
+/**
+ *@author Will Stephenson
+ */
+
+class NowListeningPreferences : public KCModule
+{
+Q_OBJECT
+public:
+ NowListeningPreferences(QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList());
+ virtual ~NowListeningPreferences();
+ virtual void save();
+ virtual void load();
+ virtual void defaults();
+
+private slots:
+ void slotSettingsChanged();
+
+private:
+ NowListeningPrefsUI *preferencesDialog;
+
+};
+
+#endif
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/nowlistening/nowlisteningprefs.ui b/kopete/plugins/nowlistening/nowlisteningprefs.ui
new file mode 100644
index 00000000..08dd72b9
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningprefs.ui
@@ -0,0 +1,376 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>NowListeningPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>NowListeningPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>391</width>
+ <height>370</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Now Listening</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>advertiseNewMediaToBuddiesLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Share Your Musical Taste&lt;/b&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QFrame" row="1" column="0">
+ <property name="name">
+ <cstring>advertiseNewMediaToBuddiesHLine</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QTabWidget" row="2" column="0">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Messa&amp;ge</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>useThisMessageLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Use this message when advertising:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>helperLabel</cstring>
+ </property>
+ <property name="text">
+ <string>%track, %artist, %album, %player will be substituted if known.
+Expressions in brackets depend on a substitution being made.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_headerLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Start with:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_header</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>kcfg_Header</cstring>
+ </property>
+ <property name="text">
+ <string>Now Listening To: </string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_perTrackLabel</cstring>
+ </property>
+ <property name="text">
+ <string>For each track:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_perTrack</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>kcfg_PerTrack</cstring>
+ </property>
+ <property name="text">
+ <string>%track (by %artist)(on %album)</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_conjunctionLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Conjunction (if &gt;1 track):</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_conjunction</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>kcfg_Conjunction</cstring>
+ </property>
+ <property name="text">
+ <string>, and </string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>A&amp;dvertising Mode</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_ExplicitAdvertising</cstring>
+ </property>
+ <property name="text">
+ <string>Explicit &amp;via "Tools-&gt;Send Media Info",
+or by typing "/media" in the chat
+window edit area.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_ChatAdvertising</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show in chat window (automatic)</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_StatusAdvertising</cstring>
+ </property>
+ <property name="text">
+ <string>Show &amp;the music you are listening to
+in place of your status message.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>kcfg_AppendStatusAdvertising</cstring>
+ </property>
+ <property name="text">
+ <string>Appe&amp;nd to your status message</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Media Pla&amp;yer</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2_2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_UseSpecifiedMediaPlayer</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;specified media player</string>
+ </property>
+ </widget>
+ <widget class="KListBox">
+ <property name="name">
+ <cstring>kcfg_SelectedMediaPlayer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="columnMode">
+ <enum>FixedNumber</enum>
+ </property>
+ <property name="rowMode">
+ <enum>Variable</enum>
+ </property>
+ <property name="variableHeight">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ <spacer row="3" column="0">
+ <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>40</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>kcfg_UseSpecifiedMediaPlayer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_SelectedMediaPlayer</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/nowlistening/nowlisteningui.rc b/kopete/plugins/nowlistening/nowlisteningui.rc
new file mode 100644
index 00000000..149b3a69
--- /dev/null
+++ b/kopete/plugins/nowlistening/nowlisteningui.rc
@@ -0,0 +1,6 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_nowlistening" version="1">
+ <Menu name="contact_popup">
+ <Action name="m_actionWantsAdvert"/>
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/smpppdcs/Changelog.smpppdcs b/kopete/plugins/smpppdcs/Changelog.smpppdcs
new file mode 100644
index 00000000..80854a86
--- /dev/null
+++ b/kopete/plugins/smpppdcs/Changelog.smpppdcs
@@ -0,0 +1,67 @@
+Changelog/README SMPPPDCS
+=========================
+
+The smpppdcs-plugin for Kopete provides a internet connection
+detection based on SuSE's kinternet/smpppd or on the netstat
+program.
+
+The smpppd is a controller to the internet interfaces. The plugin
+is inquiring this interface frequently and checks if it reports a
+connection to the internet and then activates all Kopete accounts.
+
+The netstat is checking if a default gateway is existing and then
+activates all Kopete accounts, too.
+
+Changelog
+=========
+
+0.79 (2006/01/25)
+* using KConfigXT for configuration
+* using dcopidl2cpp stub generated from kinternetiface.h (from kinternet package),
+ no more own implementation
+* experimental implementation of the the KDED-NetworkStatus (not active, yet)
+* significantly speeded up automatic detection of a SMPPPD
+* BUGFIX: reloading the plugin in a already running Kopete will no more
+ result in an inactive plugin
+* refactoring to allow easy implementation of new detection methods
+* even more speed improvements
+
+0.75 (2006/01/01)
+* use of KSocketStream instead of deprecated KExtendedSocket
+* progressbar while searching for an smpppd on the local network
+* automatically found smpppd server is resolved via DNS
+* Fixed Bug 111369: better detection of a SMPPPD and no more freeze of Kopete
+
+0.74 (2005/12/27)
+* minor bugfixes
+* disable netstat in config if the binary cannot be found
+
+0.72 (2005/09/07)
+* internal refactoring to provide online status
+
+0.7 (2004/11/20)
+
+* list to ignore accounts integrated.
+ Accounts can be excluded from the plugin connect/disconnect
+ mechanism
+* connection detection enhanced: first kinternet is asked via
+ DCOP for a running connection, if this fails the smpppd is asked
+* improved startup detection, compatible with recent CVS changes
+* some API chages in the config module
+
+0.6 (2004/10/18)
+
+* adapting to KDE 3.3.1
+
+0.4 (2004/10/05)
+
+* toggling between netstat and smpppdcs works without restart
+ of kopete
+
+0.3 (2004/10/03)
+
+* accounts get activated after they are loaded and initialized
+
+0.1 (2004/09/04)
+
+* first version of the plugin
diff --git a/kopete/plugins/smpppdcs/Makefile.am b/kopete/plugins/smpppdcs/Makefile.am
new file mode 100644
index 00000000..11173ac6
--- /dev/null
+++ b/kopete/plugins/smpppdcs/Makefile.am
@@ -0,0 +1,35 @@
+METASOURCES = AUTO
+
+SUBDIRS = icons libsmpppdclient unittest
+
+EXTRA_DIST = Changelog.smpppdcs
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes) -Ilibsmpppdclient
+
+kde_module_LTLIBRARIES = kopete_smpppdcs.la kcm_kopete_smpppdcs.la
+
+kopete_smpppdcs_la_SOURCES = kinternetiface.stub smpppdcsplugin.cpp \
+ onlineinquiry.cpp smpppdcsiface.skel detectordcop.cpp detectorsmpppd.cpp \
+ detectornetstat.cpp detectornetworkstatus.cpp smpppdcsconfig.kcfgc
+kopete_smpppdcs_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+kopete_smpppdcs_la_LIBADD = \
+ libsmpppdclient/libsmpppdclient.la ../../libkopete/libkopete.la
+
+kcm_kopete_smpppdcs_la_SOURCES = smpppdcsprefs.ui smpppdcspreferences.cpp \
+ smpppdsearcher.cpp smpppdcsprefsimpl.cpp smpppdlocationui.ui smpppdlocationwidget.cpp \
+ smpppdcsconfig.kcfgc
+kcm_kopete_smpppdcs_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_smpppdcs_la_LIBADD = libsmpppdclient/libsmpppdclient.la \
+ ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_smpppdcs.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_smpppdcs_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+kde_kcfg_DATA = smpppdcs.kcfg
+
+noinst_HEADERS = smpppdcsiface.h detectordcop.h detectorsmpppd.h \
+ detectornetstat.h kinternetiface.h detectornetworkstatus.h \
+ smpppdsearcher.h smpppdcsprefsimpl.h smpppdlocationwidget.h
diff --git a/kopete/plugins/smpppdcs/detector.h b/kopete/plugins/smpppdcs/detector.h
new file mode 100644
index 00000000..094de9e5
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detector.h
@@ -0,0 +1,59 @@
+/*
+ detector.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DETECTOR_H
+#define DETECTOR_H
+
+class IConnector;
+
+/**
+ * @brief Detector interface to find out if there is a connection to the internet.
+ *
+ * Subclasses should implement the specific ways to check for an internet
+ * connection
+ *
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ *
+ */
+
+class Detector {
+
+ Detector(const Detector&);
+ Detector& operator=(const Detector&);
+
+public:
+ /**
+ * @brief Creates an <code>Detector</code> instance.
+ *
+ * @param connector A connector to send feedback to the calling object
+ */
+ Detector(IConnector * connector) : m_connector(connector) {}
+
+ /**
+ * @brief Destroys an <code>Detector</code> instance.
+ *
+ */
+ virtual ~Detector() {}
+
+ virtual void checkStatus() const = 0;
+
+ virtual void smpppdServerChange() {}
+
+protected:
+ IConnector * m_connector;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/detectordcop.cpp b/kopete/plugins/smpppdcs/detectordcop.cpp
new file mode 100644
index 00000000..2536674d
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectordcop.cpp
@@ -0,0 +1,77 @@
+/*
+ detectordcop.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kdebug.h>
+
+#include "kinternetiface_stub.h"
+
+#include "detectordcop.h"
+#include "iconnector.h"
+
+QCString DetectorDCOP::m_kinternetApp = "";
+
+DetectorDCOP::DetectorDCOP(IConnector * connector)
+ : Detector(connector) {}
+
+DetectorDCOP::~DetectorDCOP() {}
+
+/*!
+ \fn DetectorDCOP::getKInternetDCOP()
+ */
+QCString DetectorDCOP::getKInternetDCOP() const {
+ DCOPClient * client = kapp->dcopClient();
+ if(m_kinternetApp.isEmpty() && client && client->isAttached()) {
+ // get all registered dcop apps and search for kinternet
+ QCStringList apps = client->registeredApplications();
+ QCStringList::iterator iter;
+ for(iter = apps.begin(); iter != apps.end(); ++iter) {
+ if((*iter).left(9) == "kinternet") {
+ return *iter;
+ }
+ }
+ }
+
+ return m_kinternetApp;
+}
+
+/*!
+ \fn DetectorDCOP::getConnectionStatusDCOP()
+ */
+DetectorDCOP::KInternetDCOPState DetectorDCOP::getConnectionStatusDCOP() const {
+ kdDebug(14312) << k_funcinfo << "Start inquiring " << m_kinternetApp << " via DCOP" << endl;
+
+
+ KInternetIface_stub stub = KInternetIface_stub(kapp->dcopClient(), m_kinternetApp, "KInternetIface");
+
+ bool status = stub.isOnline();
+
+ if(stub.ok()) {
+ if(status) {
+ kdDebug(14312) << k_funcinfo << "isOnline() returned true" << endl;
+ return CONNECTED;
+ } else {
+ kdDebug(14312) << k_funcinfo << "isOnline() returned false" << endl;
+ return DISCONNECTED;
+ }
+ } else {
+ kdWarning(14312) << k_funcinfo << "DCOP call to " << m_kinternetApp << " failed!";
+ }
+
+ return ERROR;
+}
+
diff --git a/kopete/plugins/smpppdcs/detectordcop.h b/kopete/plugins/smpppdcs/detectordcop.h
new file mode 100644
index 00000000..5306998b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectordcop.h
@@ -0,0 +1,51 @@
+/*
+ detectordcop.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DETECTORDCOP_H
+#define DETECTORDCOP_H
+
+#include "detector.h"
+
+class IConnector;
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class DetectorDCOP : public Detector {
+
+ DetectorDCOP(const DetectorDCOP&);
+ DetectorDCOP& operator=(const DetectorDCOP&);
+
+public:
+ DetectorDCOP(IConnector * connector);
+ virtual ~DetectorDCOP();
+
+protected:
+
+ enum KInternetDCOPState {
+ CONNECTED,
+ DISCONNECTED,
+ ERROR
+ };
+
+ QCString getKInternetDCOP() const;
+ KInternetDCOPState getConnectionStatusDCOP() const;
+
+protected:
+ static QCString m_kinternetApp;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/detectornetstat.cpp b/kopete/plugins/smpppdcs/detectornetstat.cpp
new file mode 100644
index 00000000..60dff658
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectornetstat.cpp
@@ -0,0 +1,76 @@
+/*
+ detectornetstat.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <kprocess.h>
+
+#include "iconnector.h"
+#include "detectornetstat.h"
+
+DetectorNetstat::DetectorNetstat(IConnector* connector)
+ : Detector(connector), m_buffer(QString::null), m_process(NULL) {}
+
+DetectorNetstat::~DetectorNetstat() {
+ delete m_process;
+}
+
+void DetectorNetstat::checkStatus() const {
+ kdDebug(14312) << k_funcinfo << endl;
+
+ if(m_process) {
+ kdWarning(14312) << k_funcinfo << "Previous netstat process is still running!" << endl
+ << "Not starting new netstat. Perhaps your system is under heavy load?" << endl;
+
+ return;
+ }
+
+ m_buffer = QString::null;
+
+ // Use KProcess to run netstat -r. We'll then parse the output of
+ // netstat -r in slotProcessStdout() to see if it mentions the
+ // default gateway. If so, we're connected, if not, we're offline
+ m_process = new KProcess;
+ *m_process << "netstat" << "-r";
+
+ connect(m_process, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(slotProcessStdout( KProcess *, char *, int)));
+ connect(m_process, SIGNAL(processExited(KProcess *)), this, SLOT(slotProcessExited(KProcess *)));
+
+ if(!m_process->start(KProcess::NotifyOnExit, KProcess::Stdout)) {
+ kdWarning(14312) << k_funcinfo << "Unable to start netstat process!" << endl;
+
+ delete m_process;
+ m_process = 0L;
+ }
+}
+
+void DetectorNetstat::slotProcessStdout(KProcess *, char *buffer, int buflen) {
+ // Look for a default gateway
+ kdDebug(14312) << k_funcinfo << endl;
+ m_buffer += QString::fromLatin1(buffer, buflen);
+ kdDebug(14312) << m_buffer << endl;
+}
+
+void DetectorNetstat::slotProcessExited(KProcess *process) {
+ kdDebug(14312) << k_funcinfo << m_buffer << endl;
+ if(process == m_process) {
+ m_connector->setConnectedStatus(m_buffer.contains("default"));
+ m_buffer = QString::null;
+ delete m_process;
+ m_process = 0L;
+ }
+}
+
+#include "detectornetstat.moc"
diff --git a/kopete/plugins/smpppdcs/detectornetstat.h b/kopete/plugins/smpppdcs/detectornetstat.h
new file mode 100644
index 00000000..d51a6d97
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectornetstat.h
@@ -0,0 +1,56 @@
+/*
+ detectornetstat.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DETECTORNETSTAT_H
+#define DETECTORNETSTAT_H
+
+#include <qobject.h>
+
+#include "detector.h"
+
+class KProcess;
+class IConnector;
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class DetectorNetstat : protected QObject, public Detector {
+ Q_OBJECT
+
+ DetectorNetstat(const DetectorNetstat&);
+ DetectorNetstat& operator=(const DetectorNetstat&);
+
+public:
+ DetectorNetstat(IConnector* connector);
+ virtual ~DetectorNetstat();
+
+ virtual void checkStatus() const;
+
+private slots:
+ // Original cs-plugin code
+ void slotProcessStdout(KProcess * process, char * buffer, int len);
+
+ /**
+ * Notify when the netstat process has exited
+ */
+ void slotProcessExited(KProcess *process);
+
+private:
+ mutable QString m_buffer;
+ mutable KProcess * m_process;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/detectornetworkstatus.cpp b/kopete/plugins/smpppdcs/detectornetworkstatus.cpp
new file mode 100644
index 00000000..921718b7
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectornetworkstatus.cpp
@@ -0,0 +1,68 @@
+/*
+ detectornetworkstatus.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include "kopeteuiglobal.h"
+#include "connectionmanager.h"
+
+#include "iconnector.h"
+#include "detectornetworkstatus.h"
+
+DetectorNetworkStatus::DetectorNetworkStatus(IConnector* connector)
+ : Detector(connector), m_connManager(NULL) {
+
+ m_connManager = ConnectionManager::self();
+ connect(m_connManager, SIGNAL(statusChanged(const QString&, NetworkStatus::EnumStatus)),
+ this, SLOT(statusChanged(const QString&, NetworkStatus::EnumStatus)));
+}
+
+DetectorNetworkStatus::~DetectorNetworkStatus() {}
+
+void DetectorNetworkStatus::checkStatus() const {
+ // needs to do nothing
+}
+
+void DetectorNetworkStatus::statusChanged(const QString& host, NetworkStatus::EnumStatus status) {
+ switch(status) {
+ case NetworkStatus::Offline:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::Offline" << endl;
+ break;
+ case NetworkStatus::OfflineFailed:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::OfflineFailed" << endl;
+ break;
+ case NetworkStatus::OfflineDisconnected:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::OfflineDisconnected" << endl;
+ break;
+ case NetworkStatus::ShuttingDown:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::ShuttingDown" << endl;
+ break;
+ case NetworkStatus::Establishing:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::Establishing" << endl;
+ break;
+ case NetworkStatus::Online:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::Online" << endl;
+ break;
+ case NetworkStatus::NoNetworks:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::NoNetworks" << endl;
+ break;
+ case NetworkStatus::Unreachable:
+ kdDebug(14312) << k_funcinfo << host << ": NetworkStatus::Unreachable" << endl;
+ break;
+ }
+}
+
+#include "detectornetworkstatus.moc"
diff --git a/kopete/plugins/smpppdcs/detectornetworkstatus.h b/kopete/plugins/smpppdcs/detectornetworkstatus.h
new file mode 100644
index 00000000..20315902
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectornetworkstatus.h
@@ -0,0 +1,50 @@
+/*
+ detectornetworkstatus.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DETECTORNETWORKSTATUS_H
+#define DETECTORNETWORKSTATUS_H
+
+#include <qobject.h>
+
+#include "detector.h"
+
+class IConnector;
+class ConnectionManager;
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class DetectorNetworkStatus : protected QObject, public Detector
+{
+ Q_OBJECT
+
+ DetectorNetworkStatus(const DetectorNetworkStatus&);
+ DetectorNetworkStatus& operator=(const DetectorNetworkStatus&);
+
+public:
+ DetectorNetworkStatus(IConnector* connector);
+ virtual ~DetectorNetworkStatus();
+
+ virtual void checkStatus() const;
+
+protected slots:
+ void statusChanged(const QString& host, NetworkStatus::EnumStatus status);
+
+private:
+ ConnectionManager * m_connManager;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/detectorsmpppd.cpp b/kopete/plugins/smpppdcs/detectorsmpppd.cpp
new file mode 100644
index 00000000..35ed1e05
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectorsmpppd.cpp
@@ -0,0 +1,71 @@
+/*
+ detectorsmpppd.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kapplication.h>
+
+#include "iconnector.h"
+#include "detectorsmpppd.h"
+#include "smpppdcsconfig.h"
+
+#include "smpppdclient.h"
+
+DetectorSMPPPD::DetectorSMPPPD(IConnector * connector)
+ : DetectorDCOP(connector) {}
+
+DetectorSMPPPD::~DetectorSMPPPD() {}
+
+/*!
+ \fn DetectorSMPPPD::checkStatus()
+ */
+void DetectorSMPPPD::checkStatus() const {
+ kdDebug(14312) << k_funcinfo << "Checking for online status..." << endl;
+
+#ifndef NOKINTERNETDCOP
+ m_kinternetApp = getKInternetDCOP();
+ if(kapp->dcopClient() && m_kinternetApp != "") {
+ switch(getConnectionStatusDCOP()) {
+ case CONNECTED:
+ m_connector->setConnectedStatus(true);
+ return;
+ case DISCONNECTED:
+ m_connector->setConnectedStatus(false);
+ return;
+ default:
+ break;
+ }
+ }
+#else
+#warning DCOP inquiry disabled
+ kdDebug(14312) << k_funcinfo << "DCOP inquiry disabled" << endl;
+#endif
+
+ SMPPPD::Client c;
+
+ unsigned int port = SMPPPDCSConfig::self()->port();
+ QString server = SMPPPDCSConfig::self()->server();
+
+ c.setPassword(SMPPPDCSConfig::self()->password().utf8());
+
+ if(c.connect(server, port)) {
+ m_connector->setConnectedStatus(c.isOnline());
+ } else {
+ kdDebug(14312) << k_funcinfo << "not connected to smpppd => I'll try again later" << endl;
+ m_connector->setConnectedStatus(false);
+ }
+}
diff --git a/kopete/plugins/smpppdcs/detectorsmpppd.h b/kopete/plugins/smpppdcs/detectorsmpppd.h
new file mode 100644
index 00000000..0f72d46d
--- /dev/null
+++ b/kopete/plugins/smpppdcs/detectorsmpppd.h
@@ -0,0 +1,46 @@
+/*
+ detectorsmpppd.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DETECTORSMPPPD_H
+#define DETECTORSMPPPD_H
+
+#include <qstringlist.h>
+
+#include "detectordcop.h"
+
+namespace KNetwork {
+class KStreamSocket;
+};
+
+class IConnector;
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class DetectorSMPPPD : public DetectorDCOP {
+
+ DetectorSMPPPD(const DetectorSMPPPD&);
+ DetectorSMPPPD& operator=(const DetectorSMPPPD&);
+
+public:
+ DetectorSMPPPD(IConnector* connector);
+ virtual ~DetectorSMPPPD();
+
+ virtual void checkStatus() const;
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/iconnector.h b/kopete/plugins/smpppdcs/iconnector.h
new file mode 100644
index 00000000..c4846862
--- /dev/null
+++ b/kopete/plugins/smpppdcs/iconnector.h
@@ -0,0 +1,45 @@
+/*
+ iconnector.h
+
+ Copyright (c) 2005-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICONNECTOR_H
+#define ICONNECTOR_H
+
+/**
+ * @brief Interface to an object setting a connection status.
+ *
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+class IConnector {
+ IConnector(const IConnector&);
+ IConnector& operator=(const IConnector&);
+
+public:
+ IConnector() {}
+
+ virtual ~IConnector() {}
+
+ /**
+ * @brief Set the connection status.
+ *
+ * This method needs to get reimplemented at classes which implement
+ * this interface.
+ *
+ * @param newStatus the status of the internet connection, <code>TRUE</code> if there is a connection, otherwise <code>FALSE</code>
+ */
+ virtual void setConnectedStatus(bool newStatus) = 0;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/icons/Makefile.am b/kopete/plugins/smpppdcs/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/plugins/smpppdcs/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/plugins/smpppdcs/icons/cr32-app-smpppdcs.png b/kopete/plugins/smpppdcs/icons/cr32-app-smpppdcs.png
new file mode 100644
index 00000000..d895298b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/icons/cr32-app-smpppdcs.png
Binary files differ
diff --git a/kopete/plugins/smpppdcs/kinternetiface.h b/kopete/plugins/smpppdcs/kinternetiface.h
new file mode 100644
index 00000000..b0ac8aa7
--- /dev/null
+++ b/kopete/plugins/smpppdcs/kinternetiface.h
@@ -0,0 +1,47 @@
+// -*- c++ -*-
+/***************************************************************************
+ * *
+ * Copyright: SuSE Linux AG, Nuernberg *
+ * *
+ * Author: Arvin Schnell <arvin@suse.de> *
+ * *
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KINTERNETIFACE_H
+#define KINTERNETIFACE_H
+
+
+#include <dcopobject.h>
+
+class KInternetIface : public DCOPObject
+{
+ K_DCOP
+
+public:
+
+ KInternetIface (const QCString& name) : DCOPObject (name) { }
+
+k_dcop:
+
+ // query function for susewatcher
+ bool isOnline () {
+#ifndef NDEBUG
+ fprintf (stderr, "%s\n", __PRETTY_FUNCTION__);
+#endif
+ return kinternet && kinternet->get_status () == KInternet::CONNECTED;
+ }
+
+};
+
+
+#endif
diff --git a/kopete/plugins/smpppdcs/kopete_smpppdcs.desktop b/kopete/plugins/smpppdcs/kopete_smpppdcs.desktop
new file mode 100644
index 00000000..09653c75
--- /dev/null
+++ b/kopete/plugins/smpppdcs/kopete_smpppdcs.desktop
@@ -0,0 +1,108 @@
+[Desktop Entry]
+Type=Service
+Icon=smpppdcs
+X-Kopete-Version=1000900
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_smpppdcs
+X-KDE-PluginInfo-Author=Heiko Schäfer
+X-KDE-PluginInfo-Email=heiko@rangun.de
+X-KDE-PluginInfo-Name=kopete_smpppdcs
+X-KDE-PluginInfo-Version=0.79
+X-KDE-PluginInfo-Website=http://www.rangun.de
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+Name=SUSE smpppd-enabled Connection Status (SMPPPD)
+Name[bn]=SUSE smpppd-enabled সংযোগ অবসà§à¦¥à¦¾ (SMPPPD)
+Name[bs]=Status veze koji koristi SUSE-ov smpppd servis
+Name[ca]=Estat actiu de la connexió SUSE smpppd (SMPPPD)
+Name[cs]=Stav spojení SUSE smpppd-enabled (SMPPPD)
+Name[da]=SUSE smpppd-aktiveret Forbindelsesstatus (SMPPPD)
+Name[de]=Verbindungsstatus mit Unterstützung für SuSE SMPPPD
+Name[el]=SuSE smpppd-ενεÏγοποίηση κατάσταση σÏνδεσης (SMPPPD)
+Name[es]=Estado de conexión con SUSE smpppd habilitado (SMPPPD)
+Name[et]=SUSE smpppd-võimalusega ühenduse staatus (SMPPPD)
+Name[eu]=SUSE smpppd-gaitutat konexioaren egoera (SMPPPD)
+Name[fa]=وضعیت اتصال Ùعال -SUSE smpppd (SMPPPD)
+Name[fi]=SUSE smpppd-enabled -yhteyden tila (SMPPPD)
+Name[fr]=État de la connexion pour « smpppd-enabled » de SUSE (SMPPPD)
+Name[he]=SUSE smpppd-מ×פשר מצב חיבור (SMPPPD)
+Name[hu]=SUSE smpppd-alapú állapotjellemző (SMPPPD)
+Name[is]=SUSE smpppd-virkt staða tengingar (SMPPPD)
+Name[it]=Stato della connessione di SUSE smpppd
+Name[ja]=SUSE smpppd を使ã£ãŸæŽ¥ç¶šçŠ¶æ…‹ (SMPPPD)
+Name[ka]=SUSE smpppd-ჩáƒáƒ áƒ—ული კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ˜ (SMPPPD)
+Name[kk]=SUSE smpppd (SMPPPD) қоÑылымының күйі
+Name[km]=ស្ážáž¶áž“ភាព​ážáž—្ជាប់ SUSE ដែល​អនុញ្ញាហsmpppd (SMPPPD)
+Name[lt]=SUSE smpppd ryšio būklė (SMPPPD)
+Name[nb]=Tilstand for SMPPPD – SUSE smpppd-forbindelse
+Name[nds]=Verbinnenstatus mit Ünnerstütten för SuSE-SMPPPD
+Name[ne]=SUSE smpppd ले जडान वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ (SMPPPD) सकà¥à¤·à¤® पारà¥à¤¯à¥‹
+Name[nl]=SUSE smpppd-geactiveerde verbindingsstatus (SMPPPD)
+Name[nn]=Tilstand for SMPPPD – SUSE smpppd-forbindelse
+Name[pl]=Status połączenia SUSE SMPPPD
+Name[pt]=Estado da Ligação activado pelo 'smpppd' da SUSE (SMPPPD)
+Name[pt_BR]=Status da Conexão compatível com SUSE smpppd
+Name[ru]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ SUSE smpppd (SMPPPD)
+Name[sk]=Stav spojenia SUSE smpppd-enabled (SMPPPD)
+Name[sl]=Stanje povezave z uporabo SuSE SMPPPD
+Name[sr]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ SUSE-ове smpppd-везе (SMPPPD)
+Name[sr@Latn]=Status SUSE-ove smpppd-veze (SMPPPD)
+Name[sv]=SUSE SMPPPD-aktiverad anslutningsstatus (SMPPPD)
+Name[tr]=SUSE smpppd Bağlantı Durumu (SMPPPD)
+Name[uk]=SUSE smpppd-уможливлений Стан з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (SMPPPD)
+Name[zh_CN]=SUSE smppp 连接状æ€(SMPPPD)
+Name[zh_HK]=連接 SUSE smpppd 的連線狀態 (SMPPPD)
+Name[zh_TW]=SUSE smpppd 連線狀態
+Comment=Connects/disconnects Kopete automatically depending on availability of Internet connection
+Comment[ar]=يقوم بوصل Ùˆ Ùصل Kopete تلقائيا استنادا إلى وضع الاتصال بالشبكة
+Comment[be]=Злучае/адлучае Kopete ад ÑервіÑаў у залежнаÑці ад наÑўнаÑці інтÑрфÑйÑаў з ІнтÑрнÑтам
+Comment[bg]=Ðвтоматично уÑтановÑване и прекъÑване на връзката в завиÑимоÑÑ‚ от ÑÑŠÑтоÑнието на връзката Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚
+Comment[bn]=ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ সংযোগের সà§à¦¬à¦¿à¦§à¦¾ অনà§à¦¯à¦¾à§Ÿà§€ কপেট সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ সংযোগ/সংযোগবিচà§à¦›à¦¿à¦¨à§à¦¨à¦•à¦°à§‡
+Comment[bs]=Automatski spaja Kopete i prekida vezu ovisno o dostupnosti Internet konekcije
+Comment[ca]=Connecta/desconnecta automàticament depenent de la disponibilitat de la connexió a Internet
+Comment[cs]=Automaticky připojí nebo odpojí vzhledem k dostupnosti připojení na Internet
+Comment[cy]=Cysylltu/datgyslltu Kopete yn ymysgogol, yn dibynnu ar argaeledd cysylltiad Rhyngrwyd
+Comment[da]=Forbinder/afbryder Kopete automatisk afhængig af om der er en internet-forbindelse
+Comment[de]=Verbindet/trennt Kopete automatisch abhängig von der Verfügbarkeit einer Internetverbindung
+Comment[el]=Συνδέει/αποσυνδέει το Kopete αυτόματα ανάλογα με την κατάσταση της σÏνδεσης με το διαδίκτυο
+Comment[es]=Conecta/desconecta Kopete automáticamente según la disponibilidad de la conexión a internet
+Comment[et]=Kopete automaatne ühendamine/ühenduse katkestamine vastavalt internetiühenduse olemasolule
+Comment[eu]=Kopete automatikoki konektatu/deskonektatzen du internet-eko konexioaren eskuragarritasunaren arabera
+Comment[fa]=بسته به قابلیت دسترسی اتصال اینترنت، Kopete به طور خودکار وصل/قطع ارتباط می‌کند
+Comment[fi]=Yhdistää/katkaisee yhteyden automaattisesti riippuen onko Internet-yhteys päällä
+Comment[fr]=Connecte / déconnecte automatiquement Kopete en fonction de la disponibilité de la connexion Internet
+Comment[gl]=Conecta/desconecta a Kopete automáticamente dependendo da disponibilidade de conexión a Internet
+Comment[he]=חיבור\ניתוק Kopete ב×ופן ×וטומטי בתלות בזמינות של החיבור ל×ינטרנט
+Comment[hi]=इंटरनेट कनेकà¥à¤¶à¤¨ की उपलबà¥à¤§à¤¤à¤¾ की निरà¥à¤­à¤°à¤¤à¤¾ के आधार पर के-ऑपà¥à¤Ÿà¥€ को सà¥à¤µà¤šà¤²à¤¿à¤¤ कनेकà¥à¤Ÿ/डिसà¥à¤•à¤¨à¥‡à¤•à¥à¤Ÿ करता है
+Comment[hu]=A Kopete automatikus csatlakoztatása és bontása az internetkapcsolat elérhetőségétől függően
+Comment[is]=(Af)tengir Kopete sjálfkrafa miðað við stöðu Internettengingar
+Comment[it]=Connetti/disconnetti automaticamente Kopete a seconda della disponibilità della connessione ad internet
+Comment[ja]=インターãƒãƒƒãƒˆæŽ¥ç¶šã®æœ‰ç„¡ã«ã‚ˆã‚Šè‡ªå‹•çš„ã« Kopete を接続/切断ã—ã¾ã™
+Comment[ka]=ინტერნეტ კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ áƒáƒ áƒ¡áƒ”ბáƒáƒ‘ისáƒáƒ¡ áƒáƒ•áƒ¢áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ áƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბს Kopete-ს
+Comment[kk]=Интернетке қоÑылымның бар=жоғына қарай Kopete-Ñ‚Ñ– автоматты түрде қоÑады және ажыратады
+Comment[km]=ភ្ជាប់ ​ ​ផ្ដាច​ Kopeteដោយ​ស្វáŸáž™áž”្រវážáŸ’ážáž·â€‹ážŠáŸ„យ​ផ្អែក​លើ​ភាព​មាន​នៃការណážâ€‹áž—្ជាប់​អ៊ីនធឺណិáž
+Comment[lt]=Automatiškai prijungia/atjungia Kopete, priklausomai nuo interneto ryšio buvimo
+Comment[mk]=ÐвтоматÑки го поврзува/иÑклучува Kopete во завиÑноÑÑ‚ на доÑтапноÑта на поврзувањето на Интернет
+Comment[nb]=Kobler Kopete automatisk til/fra avhengig av om internettforbindelse er tilgjengelig
+Comment[nds]=Koppelt Kopete automaatsch to- oder af, afhangen vun de Verföögborkeit vun de Internetverbinnen
+Comment[ne]=इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ जडानको उपलबà¥à¤§à¤¤à¤¾à¤®à¤¾ आधारित कोपेट सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ रूपमा जडान गरà¥à¤¦à¤›/विचà¥à¤›à¥‡à¤¦à¤¨ गरà¥à¤¦à¤›
+Comment[nl]=Bouwt automatisch verbindingen op voor Kopete of verbreekt ze, afhankelijk van de beschikbaarheid van de internetverbinding
+Comment[nn]=Koplar Kopete automatisk til eller frå avhengig av om Internett-sambandet er tilgjengeleg.
+Comment[pl]=Automatycznie podłącza/odłącza Kopete zależnie od stanu połączenia z Internetem
+Comment[pt]=Liga/desliga o Kopete automaticamente, dependendo da disponibilidade de uma ligação à Internet
+Comment[pt_BR]=Conecta/desconecta o Kopete automaticamente dependendo da disponibilidade da conexão com a Internet
+Comment[ru]=ÐвтоматичеÑки входит в Ñеть или выходит из неё в завиÑимоÑти от Ð½Ð°Ð»Ð¸Ñ‡Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚
+Comment[sk]=Pripojí/odpojí Kopete v závislosti Äi existuje pripojenie na Internet
+Comment[sl]=Samodejno vzpostavi/prekine povezavo Kopete glede na dostopnost internetne povezave
+Comment[sr]=ÐутоматÑки уÑпоÑтавља или прекида везу у Kopete-у у завиÑноÑти од доÑтупноÑти интернет везе
+Comment[sr@Latn]=Automatski uspostavlja ili prekida vezu u Kopete-u u zavisnosti od dostupnosti internet veze
+Comment[sv]=Ansluter eller kopplar ner Kopete automatiskt beroende på Internetförbindelsens tillgänglighet
+Comment[ta]=இணைய இணைபà¯à®ªà¯ˆ பொரà¯à®¤à¯à®¤à¯ Kopete யà¯à®Ÿà®©à¯à®¤à®¾à®©à®¾à®• இணையà¯à®®à¯/நீகà¯à®•à¯à®®à¯
+Comment[tg]=ВобаÑта ба имкониÑтҳои алоқаи Интернет Kopete-ро ба таври худкор пайваÑÑ‚/канда мекунад
+Comment[tr]=İnternet bağlantısının kullanılabilir olabilirliğine göre Kopete'yi otomatik bağlar/bağlamaz
+Comment[uk]=Ðвтоматично входить в мережу чи виходить з неї в залежноÑÑ‚Ñ– від наÑвноÑÑ‚Ñ– з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Інтернет
+Comment[zh_CN]=æ ¹æ® Internet 连接是å¦å¯ç”¨è‡ªåŠ¨è¿žæŽ¥/æ–­å¼€ Kopete
+Comment[zh_HK]=自動根據互è¯ç¶²é€£æŽ¥æƒ…æ³é€£æŽ¥æˆ–中斷 Kopete 連線
+Comment[zh_TW]=自動ä¾æ“šç¶²è·¯ç‹€æ³ä¾†é€£ç·š/中斷連線 Kopete
diff --git a/kopete/plugins/smpppdcs/kopete_smpppdcs_config.desktop b/kopete/plugins/smpppdcs/kopete_smpppdcs_config.desktop
new file mode 100644
index 00000000..3a2553cf
--- /dev/null
+++ b/kopete/plugins/smpppdcs/kopete_smpppdcs_config.desktop
@@ -0,0 +1,108 @@
+[Desktop Entry]
+Icon=smpppdcs
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_smpppdcs
+X-KDE-FactoryName=SMPPPDCSConfigFactory
+X-KDE-ParentApp=kopete_smpppdcs
+X-KDE-ParentComponents=kopete_smpppdcs
+
+X-Kopete-Version=1000900
+
+Name=SUSE SMPPPD Connection Status
+Name[be]=Стан злучÑÐ½Ð½Ñ SUSE SMPPPD
+Name[bn]=SUSE SMPPPD সংযোগ অবসà§à¦¥à¦¾
+Name[bs]=SUSE SMPPPD status veze
+Name[ca]=Estatus de la connexió SUSE SMPPPD
+Name[cs]=Stav spojení SUSE SMPPPD
+Name[da]=SUSE SMPPD Forbindelsesstatus
+Name[de]=SuSE SMPPPD-Verbindungsstatus
+Name[el]=Κατάσταση σÏνδεσης του SuSE SMPPPD
+Name[es]=Estado de conexión de SUSE SMPPPD
+Name[et]=SUSE SMPPPD ühenduse staatus
+Name[eu]=SUSE SMPPPD konexioaren egoera
+Name[fa]=وضعیت اتصال SUSE SMPPPD
+Name[fi]=SUSE SMPPPD -yhteyden tila
+Name[fr]=État de la connexion SUSE SMPPPD
+Name[ga]=Stádas Ceangail SUSE SMPPPD
+Name[gl]=Estado da conexión de SUSE SMPPPD
+Name[he]=מצב החיבור של SUSE SMPPPD
+Name[hu]=SUSE SMPPPD kapcsolati állapot
+Name[is]=SUSE SMPPPD tengingarstaða
+Name[it]=Stato della connessione di SUSE SMPPPD
+Name[ja]=SUSE SMPPPD 接続状態
+Name[ka]=SUSE SMPPPD კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ სტáƒáƒ¢áƒ£áƒ¡áƒ˜
+Name[kk]=SUSE SMPPPD Ð±Ð°Ð¹Ð»Ð°Ð½Ñ‹Ñ ÐºÒ¯Ð¹Ñ–
+Name[km]=ស្ážáž¶áž“ភាព​ការ​ážáž—្ជាប់ SUSE SMPPPD
+Name[lt]=SUSE SMPPPD ryšio būklė
+Name[nb]=Tilstand for SUSE-SMPPPD-forbindelsen
+Name[nds]=SUSE SMPPPD-Verbinnenstatus
+Name[ne]=SUSE SMPPPD जडान वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿
+Name[nl]=SUSE SMPPPD-verbindingsstatus
+Name[nn]=Tilstand for SUSE-SMPPPD-sambandet
+Name[pl]=Status połączenia SUSE SMPPPD
+Name[pt]=Estado da Ligação do SMPPPD para a SUSE
+Name[pt_BR]=Status da Conexão SUSE SMPPPD
+Name[ru]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ SUSE SMPPPD
+Name[sk]=Stav spojenia SUSE SMPPPD
+Name[sl]=Stanje povezave z uporabo SuSE SMPPPD
+Name[sr]=Ð¡Ñ‚Ð°Ñ‚ÑƒÑ SUSE-ове SMPPPD везе
+Name[sr@Latn]=Status SUSE-ove SMPPPD veze
+Name[sv]=SUSE SMPPPD anslutningsstatus
+Name[tr]=SUSE SMPPPD bağlantı durumu
+Name[uk]=Стан з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ SUSE SMPPPD
+Name[zh_CN]=SUSE SMPPPD 连接状æ€
+Name[zh_HK]=SUSE SMPPPD 連線狀態
+Name[zh_TW]=SUSE SMPPPD 連線狀態
+Comment=SMPPPDCS Plugin
+Comment[be]=Модуль SMPPPDCS
+Comment[bn]=SMPPPDCS পà§à¦²à¦¾à¦—িন
+Comment[br]=Lugant SMPPPDCS
+Comment[bs]=SMPPPDCS dodatak
+Comment[ca]=Connector SMPPPDCS
+Comment[cs]=SMPPPDCS modul
+Comment[da]=SMPPPDCS-Plugin
+Comment[de]=SMPPPDCS-Modul
+Comment[el]=ΠÏόσθετο SMPPPDCS
+Comment[eo]=SMPPPDCS-kromaĵo
+Comment[es]=Extensión SMPPPDCS
+Comment[et]=SMPPPDCS plugin
+Comment[eu]=SMPPPDCS plugin-a
+Comment[fa]=SUSE SMPPPD وصلۀ
+Comment[fi]=SMPPPDCS-liitännäinen
+Comment[fr]=Module SMPPPDCS
+Comment[ga]=Breiseán SMPPPDCS
+Comment[gl]=Plugin SMPPPDCS
+Comment[he]=תוסף SMPPPDCS
+Comment[hu]=SMPPPDCS bővítőmodul
+Comment[is]=SMPPPDCS íforrit
+Comment[it]=Plugin SMPPPDCS
+Comment[ja]=SMPPPDCS プラグイン
+Comment[ka]=SMPPPDCS მáƒáƒ“ული
+Comment[kk]=SMPPPDCS плагин модулі
+Comment[km]=កម្មវិធី​ជំនួយ SMPPPDCS
+Comment[lt]=SMPPPDCS įskiepis
+Comment[nb]=Programtillegg for SMPPPDCS
+Comment[nds]=SMPPPDCS-Moduul
+Comment[ne]=SMPPPDCS पà¥à¤²à¤—इन
+Comment[nl]=SMPPPDCS-plugin
+Comment[nn]=Programtillegg for SMPPPDCS
+Comment[pl]=Wtyczka SMPPPDCS
+Comment[pt]='Plugin' do SMPPPDCS
+Comment[pt_BR]=Plugin SMPPPDCS
+Comment[ro]=Modul SMPPPDCS
+Comment[ru]=Модуль SMPPPDCS
+Comment[sk]=SMPPPDCS modul
+Comment[sl]=Vstavek SMPPPDCS
+Comment[sr]=Прикључак SMPPPDCS
+Comment[sr@Latn]=PrikljuÄak SMPPPDCS
+Comment[sv]=SMPPPDCS-insticksprogram
+Comment[tr]=SMPPPDCS Eklentisi
+Comment[uk]=Втулок SMPPPDCS
+Comment[uz]=SMPPPDCS plagini
+Comment[uz@cyrillic]=SMPPPDCS плагини
+Comment[zh_CN]=SMPPPDCS æ’件
+Comment[zh_HK]=SMPPPDCS æ’件
+Comment[zh_TW]=SMPPPDCS 外掛程å¼
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am b/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am
new file mode 100644
index 00000000..9fc9258c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/Makefile.am
@@ -0,0 +1,10 @@
+AM_CPPFLAGS = $(all_includes)
+
+noinst_LTLIBRARIES = libsmpppdclient.la
+libsmpppdclient_la_LDFLAGS = -avoid-version $(all_libraries)
+
+noinst_HEADERS = smpppdclient.h smpppdstate.h smpppdready.h smpppdunsettled.h
+libsmpppdclient_la_SOURCES = smpppdclient.cpp smpppdstate.cpp smpppdready.cpp \
+ smpppdunsettled.cpp
+
+libsmpppdclient_la_LIBADD = -lcrypto
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp
new file mode 100644
index 00000000..d386b669
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.cpp
@@ -0,0 +1,104 @@
+/*
+ smpppdclient.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kstreamsocket.h>
+
+#include "smpppdunsettled.h"
+#include "smpppdclient.h"
+
+using namespace SMPPPD;
+
+Client::Client()
+ : m_state(NULL), m_sock(NULL), m_serverID(QString::null), m_serverVer(QString::null), m_password(QString::null) {
+ changeState(Unsettled::instance());
+}
+
+Client::~Client() {
+ disconnect();
+}
+
+bool Client::connect(const QString& server, uint port) {
+ return m_state->connect(this, server, port);
+}
+
+void Client::disconnect() {
+ m_state->disconnect(this);
+}
+
+QStringList Client::getInterfaceConfigurations() {
+ return m_state->getInterfaceConfigurations(this);
+}
+
+bool Client::statusInterface(const QString& ifcfg) {
+ return m_state->statusInterface(this, ifcfg);
+}
+
+QString Client::serverID() const {
+ return m_serverID;
+}
+
+QString Client::serverVersion() const {
+ return m_serverVer;
+}
+
+QStringList Client::read() const {
+ QStringList qsl;
+
+ if(isReady()) {
+ QDataStream stream(m_sock);
+ char s[1024];
+
+ stream.readRawBytes(s, 1023);
+ char *sp = s;
+
+ for(int i = 0; i < 1024; i++) {
+ if(s[i] == '\n') {
+ s[i] = 0;
+ qsl.push_back(sp);
+ sp = &(s[i+1]);
+ }
+ }
+ }
+
+ return qsl;
+}
+
+void Client::write(const char * cmd) {
+ if(isReady()) {
+ QDataStream stream(m_sock);
+ stream.writeRawBytes(cmd, strlen(cmd));
+ stream.writeRawBytes("\n", strlen("\n"));
+ m_sock->flush();
+ }
+}
+
+bool Client::isReady() const {
+ return m_sock && m_sock->state() == KNetwork::KStreamSocket::Connected;
+}
+
+bool Client::isOnline() {
+
+ if(isReady()) {
+ QStringList ifcfgs = getInterfaceConfigurations();
+ for(uint i = 0; i < ifcfgs.count(); i++) {
+ if(statusInterface(ifcfgs[i])) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h
new file mode 100644
index 00000000..a123cd4c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdclient.h
@@ -0,0 +1,80 @@
+/*
+ smpppdclient.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCLIENT_H
+#define SMPPPDCLIENT_H
+
+#include <qstringlist.h>
+
+namespace KNetwork {
+class KStreamSocket;
+};
+
+namespace SMPPPD {
+
+class State;
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Client {
+ Client(const Client&);
+ Client& operator=(const Client&);
+
+public:
+ Client();
+ ~Client();
+
+ bool isReady() const;
+
+ bool connect(const QString& server, uint port = 3185);
+ void disconnect();
+
+ QStringList getInterfaceConfigurations();
+ bool statusInterface(const QString& ifcfg);
+
+ bool isOnline();
+ QString serverID() const;
+ QString serverVersion() const;
+
+ void setPassword(const QString& password);
+
+private:
+ friend class State;
+
+ void changeState(State * newState);
+ QStringList read() const;
+ void write(const char * cmd);
+
+private:
+ State * m_state;
+ KNetwork::KStreamSocket * m_sock;
+ QString m_serverID;
+ QString m_serverVer;
+ QString m_password;
+};
+
+inline void Client::changeState(State * newState) {
+ m_state = newState;
+}
+
+inline void Client::setPassword(const QString& password) {
+ m_password = password;
+}
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp
new file mode 100644
index 00000000..421914bb
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.cpp
@@ -0,0 +1,104 @@
+/*
+ smpppdready.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kstreamsocket.h>
+
+#include "smpppdunsettled.h"
+#include "smpppdclient.h"
+#include "smpppdready.h"
+
+using namespace SMPPPD;
+
+Ready * Ready::m_instance = NULL;
+
+Ready::Ready() {}
+
+Ready::~Ready() {}
+
+Ready * Ready::instance() {
+ if(!m_instance) {
+ m_instance = new Ready;
+ }
+
+ return m_instance;
+}
+
+void Ready::disconnect(Client * client) {
+ kdDebug(14312) << k_funcinfo << endl;
+ if(socket(client)) {
+ socket(client)->flush();
+ socket(client)->close();
+
+ delete socket(client);
+ setSocket(client, NULL);
+
+ setServerID(client, QString::null);
+ setServerVersion(client, QString::null);
+ }
+
+ changeState(client, Unsettled::instance());
+}
+
+QStringList Ready::getInterfaceConfigurations(Client * client) {
+
+ QStringList ifcfgs;
+
+ // we want all ifcfgs
+ kdDebug(14312) << k_funcinfo << "smpppd req: list-ifcfgs" << endl;
+ write(client, "list-ifcfgs");
+ QStringList stream = read(client);
+ kdDebug(14312) << k_funcinfo << "smpppd ack: " << stream[0] << endl;
+ if(stream[0].startsWith("ok")) {
+ // we have now a QStringList with all ifcfgs
+ // we extract them and put them in the global ifcfgs-list
+ // stream[1] tells us how many ifcfgs are coming next
+ QRegExp numIfcfgsRex("^BEGIN IFCFGS ([0-9]+).*");
+ if(numIfcfgsRex.exactMatch(stream[1])) {
+ int count_ifcfgs = numIfcfgsRex.cap(1).toInt();
+ kdDebug(14312) << k_funcinfo << "ifcfgs: " << count_ifcfgs << endl;
+
+ for(int i = 0; i < count_ifcfgs; i++) {
+ QRegExp ifcfgRex("^i \"(ifcfg-[a-zA-Z]+[0-9]+)\".*");
+ if(ifcfgRex.exactMatch(stream[i+2])) {
+ ifcfgs.push_back(ifcfgRex.cap(1));
+ }
+ }
+ }
+ }
+
+ return ifcfgs;
+}
+
+bool Ready::statusInterface(Client * client, const QString& ifcfg) {
+
+ QString cmd = "list-status " + ifcfg;
+
+ write(client, cmd.latin1());
+ socket(client)->waitForMore(0);
+
+ QStringList stream = read(client);
+
+ if(stream[0].startsWith("ok")) {
+ if(stream[2].startsWith("status connected")) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h
new file mode 100644
index 00000000..9ec3ab93
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdready.h
@@ -0,0 +1,49 @@
+/*
+ smpppdready.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDREADY_H
+#define SMPPPDREADY_H
+
+#include "smpppdstate.h"
+
+namespace SMPPPD {
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Ready : public State
+{
+ Ready(const Ready&);
+ Ready& operator=(const Ready&);
+
+ Ready();
+
+public:
+ virtual ~Ready();
+
+ static Ready * instance();
+
+ virtual void disconnect(Client * client);
+ virtual QStringList getInterfaceConfigurations(Client * client);
+ virtual bool statusInterface(Client * client, const QString& ifcfg);
+
+private:
+ static Ready * m_instance;
+};
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp
new file mode 100644
index 00000000..9e4bd508
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.cpp
@@ -0,0 +1,76 @@
+/*
+ smpppdstate.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kstreamsocket.h>
+
+#include "smpppdclient.h"
+#include "smpppdstate.h"
+
+using namespace SMPPPD;
+
+State::State() {}
+
+State::~State() {}
+
+QStringList State::read(Client * client) const {
+ return client->read();
+}
+
+void State::write(Client * client, const char * cmd) {
+ client->write(cmd);
+}
+
+void State::changeState(Client * client, State * state) {
+ client->changeState(state);
+}
+
+KNetwork::KStreamSocket * State::socket(Client * client) const {
+ return client->m_sock;
+}
+
+QString State::password(Client * client) const {
+ return client->m_password;
+}
+
+void State::setPassword(Client * client, const QString& pass) {
+ client->m_password = pass;
+}
+
+void State::setServerID(Client * client, const QString& id) {
+ client->m_serverID = id;
+}
+
+void State::setServerVersion(Client * client, const QString& ver) {
+ client->m_serverVer = ver;
+}
+
+void State::setSocket(Client * client, KNetwork::KStreamSocket * sock) {
+ client->m_sock = sock;
+}
+
+bool State::connect(Client * /* client */, const QString& /* server */, uint /* port */) {
+ return false;
+}
+
+void State::disconnect(Client * /* client */) {}
+
+QStringList State::getInterfaceConfigurations(Client * /* client */) {
+ return QStringList();
+}
+
+bool State::statusInterface(Client * /* client */, const QString& /* ifcfg */) {
+ return false;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h
new file mode 100644
index 00000000..0e7d393b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdstate.h
@@ -0,0 +1,58 @@
+/*
+ smpppdstate.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDSTATE_H
+#define SMPPPDSTATE_H
+
+#include <qstringlist.h>
+
+namespace SMPPPD {
+
+class Client;
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class State {
+ State(const State&);
+ State& operator=(const State&);
+
+public:
+ State();
+ virtual ~State();
+
+ virtual bool connect(Client * client, const QString& server, uint port = 3185);
+ virtual void disconnect(Client * client);
+
+ virtual QStringList getInterfaceConfigurations(Client * client);
+ virtual bool statusInterface(Client * client, const QString& ifcfg);
+
+protected:
+ QStringList read(Client * client) const;
+ void write(Client * client, const char * cmd);
+ void changeState(Client * client, State * state);
+ KNetwork::KStreamSocket * socket(Client * client) const;
+ void setSocket(Client * client, KNetwork::KStreamSocket * sock);
+ QString password(Client * client) const;
+ void setPassword(Client * client, const QString& pass);
+ void setServerID(Client * client, const QString& id);
+ void setServerVersion(Client * client, const QString& ver);
+
+};
+
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp
new file mode 100644
index 00000000..7ed5f516
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.cpp
@@ -0,0 +1,153 @@
+/*
+ smpppdunsettled.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <cstdlib>
+#include <openssl/md5.h>
+
+#include <qregexp.h>
+
+#include <kdebug.h>
+#include <kstreamsocket.h>
+
+#include "smpppdready.h"
+#include "smpppdunsettled.h"
+
+using namespace SMPPPD;
+
+Unsettled * Unsettled::m_instance = NULL;
+
+Unsettled::Unsettled() {}
+
+Unsettled::~Unsettled() {}
+
+Unsettled * Unsettled::instance() {
+ if(!m_instance) {
+ m_instance = new Unsettled();
+ }
+
+ return m_instance;
+}
+
+bool Unsettled::connect(Client * client, const QString& server, uint port) {
+ if(!socket(client) ||
+ socket(client)->state() != KNetwork::KStreamSocket::Connected ||
+ socket(client)->state() != KNetwork::KStreamSocket::Connecting) {
+
+ QString resolvedServer = server;
+
+ changeState(client, Ready::instance());
+ disconnect(client);
+
+ // since a lookup on a non-existant host can take a lot of time we
+ // try to get the IP of server before and we do the lookup ourself
+ KNetwork::KResolver resolver(server);
+ resolver.start();
+ if(resolver.wait(500)) {
+ KNetwork::KResolverResults results = resolver.results();
+ if(!results.empty()) {
+ QString ip = results[0].address().asInet().ipAddress().toString();
+ kdDebug(14312) << k_funcinfo << "Found IP-Address for " << server << ": " << ip << endl;
+ resolvedServer = ip;
+ } else {
+ kdWarning(14312) << k_funcinfo << "No IP-Address found for " << server << endl;
+ return false;
+ }
+ } else {
+ kdWarning(14312) << k_funcinfo << "Looking up hostname timed out, consider to use IP or correct host" << endl;
+ return false;
+ }
+
+ setSocket(client, new KNetwork::KStreamSocket(resolvedServer, QString::number(port)));
+ socket(client)->setBlocking(TRUE);
+
+ if(!socket(client)->connect()) {
+ kdDebug(14312) << k_funcinfo << "Socket Error: " << KNetwork::KStreamSocket::errorString(socket(client)->error()) << endl;
+ } else {
+ kdDebug(14312) << k_funcinfo << "Successfully connected to smpppd \"" << server << ":" << port << "\"" << endl;
+
+ static QString verRex = "^SuSE Meta pppd \\(smpppd\\), Version (.*)$";
+ static QString clgRex = "^challenge = (.*)$";
+
+ QRegExp ver(verRex);
+ QRegExp clg(clgRex);
+
+ QString response = read(client)[0];
+
+ if(response != QString::null &&
+ ver.exactMatch(response)) {
+ setServerID(client, response);
+ setServerVersion(client, ver.cap(1));
+ changeState(client, Ready::instance());
+ return true;
+ } else if(response != QString::null &&
+ clg.exactMatch(response)) {
+ if(password(client) != QString::null) {
+ // we are challenged, ok, respond
+ write(client, QString("response = %1\n").arg(make_response(clg.cap(1).stripWhiteSpace(), password(client))).latin1());
+ response = read(client)[0];
+ if(ver.exactMatch(response)) {
+ setServerID(client, response);
+ setServerVersion(client, ver.cap(1));
+ return true;
+ } else {
+ kdWarning(14312) << k_funcinfo << "SMPPPD responded: " << response << endl;
+ changeState(client, Ready::instance());
+ disconnect(client);
+ }
+ } else {
+ kdWarning(14312) << k_funcinfo << "SMPPPD requested a challenge, but no password was supplied!" << endl;
+ changeState(client, Ready::instance());
+ disconnect(client);
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+QString Unsettled::make_response(const QString& chex, const QString& password) const {
+
+ int size = chex.length ();
+ if (size & 1)
+ return "error";
+ size >>= 1;
+
+ // convert challenge from hex to bin
+ QString cbin;
+ for (int i = 0; i < size; i++) {
+ QString tmp = chex.mid (2 * i, 2);
+ cbin.append ((char) strtol (tmp.ascii (), 0, 16));
+ }
+
+ // calculate response
+ unsigned char rbin[MD5_DIGEST_LENGTH];
+ MD5state_st md5;
+ MD5_Init (&md5);
+ MD5_Update (&md5, cbin.ascii (), size);
+ MD5_Update (&md5, password.ascii(), password.length ());
+ MD5_Final (rbin, &md5);
+
+ // convert response from bin to hex
+ QString rhex;
+ for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
+ char buffer[3];
+ snprintf (buffer, 3, "%02x", rbin[i]);
+ rhex.append (buffer);
+ }
+
+ return rhex;
+}
diff --git a/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h
new file mode 100644
index 00000000..57a83752
--- /dev/null
+++ b/kopete/plugins/smpppdcs/libsmpppdclient/smpppdunsettled.h
@@ -0,0 +1,49 @@
+/*
+ smpppdunsettled.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDSMPPPDUNSETTLED_H
+#define SMPPPDSMPPPDUNSETTLED_H
+
+#include "smpppdstate.h"
+
+namespace SMPPPD {
+
+/**
+ @author Heiko Schaefer <heiko@rangun.de>
+*/
+class Unsettled : public State
+{
+ Unsettled(const Unsettled&);
+ Unsettled& operator=(const Unsettled&);
+
+ Unsettled();
+public:
+ virtual ~Unsettled();
+
+ static Unsettled * instance();
+
+ virtual bool connect(Client * client, const QString& server, uint port = 3185);
+
+private:
+ QString make_response(const QString& chex, const QString& password) const;
+
+private:
+ static Unsettled * m_instance;
+};
+
+}
+
+#endif
diff --git a/kopete/plugins/smpppdcs/onlineinquiry.cpp b/kopete/plugins/smpppdcs/onlineinquiry.cpp
new file mode 100644
index 00000000..4cab45d7
--- /dev/null
+++ b/kopete/plugins/smpppdcs/onlineinquiry.cpp
@@ -0,0 +1,45 @@
+/*
+ onlineinquiry.cpp
+
+ Copyright (c) 2005-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include "detectornetstat.h"
+#include "detectorsmpppd.h"
+#include "onlineinquiry.h"
+
+OnlineInquiry::OnlineInquiry()
+ : m_detector(NULL), m_online(FALSE) {}
+
+OnlineInquiry::~OnlineInquiry() {
+ delete m_detector;
+}
+
+bool OnlineInquiry::isOnline(bool useSMPPPD) {
+
+ delete m_detector;
+
+ if(useSMPPPD) {
+ m_detector = new DetectorSMPPPD(this);
+ } else {
+ m_detector = new DetectorNetstat(this);
+ }
+
+ m_detector->checkStatus();
+
+ return m_online;
+}
+
+void OnlineInquiry::setConnectedStatus(bool newStatus) {
+ m_online = newStatus;
+}
diff --git a/kopete/plugins/smpppdcs/onlineinquiry.h b/kopete/plugins/smpppdcs/onlineinquiry.h
new file mode 100644
index 00000000..c9b5221a
--- /dev/null
+++ b/kopete/plugins/smpppdcs/onlineinquiry.h
@@ -0,0 +1,45 @@
+/*
+ onlineinquiry.h
+
+ Copyright (c) 2005-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ONLINEINQUIRY_H
+#define ONLINEINQUIRY_H
+
+#include "iconnector.h"
+
+class Detector;
+
+/**
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+
+class OnlineInquiry : public IConnector {
+ OnlineInquiry(const OnlineInquiry&);
+ OnlineInquiry& operator=(const OnlineInquiry&);
+
+public:
+ OnlineInquiry();
+ virtual ~OnlineInquiry();
+
+ bool isOnline(bool useSMPPPD);
+
+ virtual void setConnectedStatus(bool newStatus);
+
+private:
+ Detector * m_detector;
+ bool m_online;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/smpppdcs.kcfg b/kopete/plugins/smpppdcs/smpppdcs.kcfg
new file mode 100644
index 00000000..2ca65f54
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcs.kcfg
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd">
+<kcfg>
+ <kcfgfile name="kopeterc"/>
+ <group name="SMPPPDCS Plugin">
+ <entry name="Password" type="String">
+ <label>Password to connect to the SMPPPD.</label>
+ </entry>
+ <entry name="ignoredAccounts" type="StringList">
+ <label>Accounts to ignore in the plugin.</label>
+ </entry>
+ <entry name="server" type="String">
+ <label>SMPPPD-Server to connect.</label>
+ <default>localhost</default>
+ </entry>
+ <entry name="port" type="UInt">
+ <label>SMPPPD-Server port to connect.</label>
+ <default>3185</default>
+ </entry>
+ <entry name="useNetstat" type="Bool">
+ <label>Use the netstat tool to determine the connection status.</label>
+ <default>true</default>
+ </entry>
+ <entry name="useSmpppd" type="Bool">
+ <label>Use the SMPPPD to determine the connection status.</label>
+ <default>false</default>
+ </entry>
+ </group>
+</kcfg> \ No newline at end of file
diff --git a/kopete/plugins/smpppdcs/smpppdcsconfig.kcfgc b/kopete/plugins/smpppdcs/smpppdcsconfig.kcfgc
new file mode 100644
index 00000000..2e955708
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsconfig.kcfgc
@@ -0,0 +1,6 @@
+File=smpppdcs.kcfg
+ClassName=SMPPPDCSConfig
+Singleton=true
+Mutators=true
+MemberVariables=private
+GlobalEnums=true \ No newline at end of file
diff --git a/kopete/plugins/smpppdcs/smpppdcsiface.h b/kopete/plugins/smpppdcs/smpppdcsiface.h
new file mode 100644
index 00000000..face60ad
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsiface.h
@@ -0,0 +1,36 @@
+/*
+ smpppdcsiface.h
+
+ Copyright (c) 2005-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCSIFACE_H
+#define SMPPPDCSIFACE_H
+
+#include <dcopobject.h>
+
+/**
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+
+class SMPPPDCSIFace : virtual public DCOPObject
+{
+ K_DCOP
+
+ k_dcop:
+
+ virtual QString detectionMethod() const = 0;
+ virtual bool isOnline() const = 0;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/smpppdcsplugin.cpp b/kopete/plugins/smpppdcs/smpppdcsplugin.cpp
new file mode 100644
index 00000000..2ed8455c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsplugin.cpp
@@ -0,0 +1,221 @@
+/*
+ smpppdcsplugin.cpp
+
+ Copyright (c) 2002-2003 by Chris Howells <howells@kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include "onlineinquiry.h"
+#include "smpppdcsplugin.h"
+
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include "kopeteprotocol.h"
+#include "networkstatuscommon.h"
+#include "kopetepluginmanager.h"
+#include "kopeteaccountmanager.h"
+
+#include "detectornetworkstatus.h"
+#include "detectornetstat.h"
+#include "detectorsmpppd.h"
+#include "smpppdcsconfig.h"
+
+typedef KGenericFactory<SMPPPDCSPlugin> SMPPPDCSPluginFactory;
+K_EXPORT_COMPONENT_FACTORY(kopete_smpppdcs, SMPPPDCSPluginFactory("kopete_smpppdcs"))
+
+SMPPPDCSPlugin::SMPPPDCSPlugin(QObject *parent, const char * name, const QStringList& /* args */)
+ : DCOPObject("SMPPPDCSIface"), Kopete::Plugin(SMPPPDCSPluginFactory::instance(), parent, name),
+ m_detectorSMPPPD(NULL), m_detectorNetstat(NULL), m_detectorNetworkStatus(NULL), m_timer(NULL),
+m_onlineInquiry(NULL) {
+
+ kdDebug(14312) << k_funcinfo << endl;
+
+ m_pluginConnected = false;
+
+ m_onlineInquiry = new OnlineInquiry();
+ m_detectorSMPPPD = new DetectorSMPPPD(this);
+ m_detectorNetstat = new DetectorNetstat(this);
+
+ // experimental, not used yet
+ m_detectorNetworkStatus = new DetectorNetworkStatus(this);
+
+ // we wait for the allPluginsLoaded signal, to connect
+ // as early as possible after startup, but not before
+ // all accounts are ready
+ connect(Kopete::PluginManager::self(), SIGNAL(allPluginsLoaded()),
+ this, SLOT(allPluginsLoaded()));
+
+ // if kopete was already running and the plugin
+ // was loaded later, we check once after 15 secs
+ // if all other plugins have been loaded
+ QTimer::singleShot(15000, this, SLOT(allPluginsLoaded()));
+}
+
+SMPPPDCSPlugin::~SMPPPDCSPlugin() {
+
+ kdDebug(14312) << k_funcinfo << endl;
+
+ delete m_timer;
+ delete m_detectorSMPPPD;
+ delete m_detectorNetstat;
+ delete m_detectorNetworkStatus;
+ delete m_onlineInquiry;
+}
+
+void SMPPPDCSPlugin::allPluginsLoaded() {
+
+ if(Kopete::PluginManager::self()->isAllPluginsLoaded()) {
+ m_timer = new QTimer();
+ connect(m_timer, SIGNAL(timeout()), this, SLOT(slotCheckStatus()));
+
+ if(SMPPPDCSConfig::self()->useSmpppd()) {
+ m_timer->start(30000);
+ } else {
+ // we use 1 min interval, because it reflects
+ // the old connectionstatus plugin behaviour
+ m_timer->start(60000);
+ }
+
+ slotCheckStatus();
+ }
+}
+
+bool SMPPPDCSPlugin::isOnline() const {
+ return m_onlineInquiry->isOnline(SMPPPDCSConfig::self()->useSmpppd());
+}
+
+void SMPPPDCSPlugin::slotCheckStatus() {
+
+ // reread config to get changes
+ SMPPPDCSConfig::self()->readConfig();
+
+ if(SMPPPDCSConfig::self()->useSmpppd()) {
+ m_detectorSMPPPD->checkStatus();
+ } else {
+ m_detectorNetstat->checkStatus();
+ }
+}
+
+void SMPPPDCSPlugin::setConnectedStatus( bool connected ) {
+ kdDebug(14312) << k_funcinfo << connected << endl;
+
+ // We have to handle a few cases here. First is the machine is connected, and the plugin thinks
+ // we're connected. Then we don't do anything. Next, we can have machine connected, but plugin thinks
+ // we're disconnected. Also, machine disconnected, plugin disconnected -- we
+ // don't do anything. Finally, we can have the machine disconnected, and the plugin thinks we're
+ // connected. This mechanism is required so that we don't keep calling the connect/disconnect functions
+ // constantly.
+
+ if ( connected && !m_pluginConnected ) {
+ // The machine is connected and plugin thinks we're disconnected
+ kdDebug(14312) << k_funcinfo << "Setting m_pluginConnected to true" << endl;
+ m_pluginConnected = true;
+ connectAllowed();
+ kdDebug(14312) << k_funcinfo << "We're connected" << endl;
+ } else if ( !connected && m_pluginConnected ) {
+ // The machine isn't connected and plugin thinks we're connected
+ kdDebug(14312) << k_funcinfo << "Setting m_pluginConnected to false" << endl;
+ m_pluginConnected = false;
+ disconnectAllowed();
+ kdDebug(14312) << k_funcinfo << "We're offline" << endl;
+ }
+}
+
+void SMPPPDCSPlugin::connectAllowed() {
+
+ QStringList list = SMPPPDCSConfig::self()->ignoredAccounts();
+
+ Kopete::AccountManager * m = Kopete::AccountManager::self();
+ for(QPtrListIterator<Kopete::Account> it(m->accounts())
+ ;
+ it.current();
+ ++it) {
+
+#ifndef NDEBUG
+ if(it.current()->inherits("Kopete::ManagedConnectionAccount")) {
+ kdDebug(14312) << k_funcinfo << "Account " << it.current()->protocol()->pluginId() + "_" + it.current()->accountId() << " is an managed account!" << endl;
+ } else {
+ kdDebug(14312) << k_funcinfo << "Account " << it.current()->protocol()->pluginId() + "_" + it.current()->accountId() << " is an unmanaged account!" << endl;
+ }
+#endif
+
+ if(!list.contains(it.current()->protocol()->pluginId() + "_" + it.current()->
+ accountId())) {
+ it.current()->connect();
+ }
+ }
+}
+
+void SMPPPDCSPlugin::disconnectAllowed() {
+
+ QStringList list = SMPPPDCSConfig::self()->ignoredAccounts();
+
+ Kopete::AccountManager * m = Kopete::AccountManager::self();
+ for(QPtrListIterator<Kopete::Account> it(m->accounts())
+ ;
+ it.current();
+ ++it) {
+
+#ifndef NDEBUG
+ if(it.current()->inherits("Kopete::ManagedConnectionAccount")) {
+ kdDebug(14312) << k_funcinfo << "Account " << it.current()->protocol()->pluginId() + "_" + it.current()->accountId() << " is an managed account!" << endl;
+ } else {
+ kdDebug(14312) << k_funcinfo << "Account " << it.current()->protocol()->pluginId() + "_" + it.current()->accountId() << " is an unmanaged account!" << endl;
+ }
+#endif
+
+ if(!list.contains(it.current()->protocol()->pluginId() + "_" + it.current()->accountId())) {
+ it.current()->disconnect();
+ }
+ }
+}
+
+QString SMPPPDCSPlugin::detectionMethod() const {
+ if(SMPPPDCSConfig::self()->useSmpppd()) {
+ return "smpppd";
+ } else {
+ return "netstat";
+ }
+}
+
+/*!
+ \fn SMPPPDCSPlugin::smpppdServerChanged(const QString& server)
+ */
+void SMPPPDCSPlugin::smpppdServerChanged(const QString& server) {
+
+ QString oldServer = SMPPPDCSConfig::self()->server().utf8();
+
+ if(oldServer != server) {
+ kdDebug(14312) << k_funcinfo << "Detected a server change" << endl;
+ m_detectorSMPPPD->smpppdServerChange();
+ }
+}
+
+void SMPPPDCSPlugin::aboutToUnload() {
+
+ kdDebug(14312) << k_funcinfo << endl;
+
+ if(m_timer) {
+ m_timer->stop();
+ }
+
+ emit readyForUnload();
+}
+
+#include "smpppdcsplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/smpppdcs/smpppdcsplugin.h b/kopete/plugins/smpppdcs/smpppdcsplugin.h
new file mode 100644
index 00000000..789d9c41
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsplugin.h
@@ -0,0 +1,109 @@
+/*
+ smpppdcsplugin.h
+
+ Copyright (c) 2002-2003 by Chris Howells <howells@kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCSPLUGIN_H
+#define SMPPPDCSPLUGIN_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "detector.h"
+#include "iconnector.h"
+#include "smpppdcsiface.h"
+
+#include "kopeteplugin.h"
+#include "kopeteaccount.h"
+
+class QTimer;
+class Detector;
+class OnlineInquiry;
+
+/**
+ * @brief Plugin for the detection of an internet connection
+ *
+ * This plugin inquires either the smpppd or netstat
+ * for an existing internet connection and depending
+ * on that connects or disconnects all accounts.
+ *
+ * Therefore it should be enabled on dial up network
+ * connections.
+ *
+ * @author Chris Howells <howells@kde.org>, Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+class SMPPPDCSPlugin : public Kopete::Plugin, public IConnector, virtual public SMPPPDCSIFace {
+ Q_OBJECT
+ SMPPPDCSPlugin(const SMPPPDCSPlugin&);
+ SMPPPDCSPlugin& operator=(const SMPPPDCSPlugin&);
+
+public:
+ /**
+ * @brief Creates an <code>SMPPPDCSPlugin</code> instance
+ */
+ SMPPPDCSPlugin( QObject *parent, const char *name, const QStringList &args );
+
+ /**
+ * @brief Destroys an <code>SMPPPDCSPlugin</code> instance
+ */
+ virtual ~SMPPPDCSPlugin();
+
+ // Implementation of DCOP iface
+ /**
+ * @brief Checks if we are online.
+ * @note This method is reserved for future use. Do not use at the moment!
+ * @return <code>TRUE</code> if online, otherwise <code>FALSE</code>
+ */
+ virtual bool isOnline() const;
+
+ /**
+ * @brief Sets the status in all allowed accounts.
+ * Allowed accounts are set in the config dialog of the plugin.
+ *
+ * @see SMPPPDCSPrefs
+ */
+ virtual void setConnectedStatus( bool newStatus );
+
+ virtual QString detectionMethod() const;
+
+ virtual void aboutToUnload();
+
+public slots:
+ void smpppdServerChanged(const QString& server);
+
+private slots:
+ void slotCheckStatus();
+ void allPluginsLoaded();
+
+private:
+
+ void connectAllowed();
+ void disconnectAllowed();
+
+private:
+
+ Detector * m_detectorSMPPPD;
+ Detector * m_detectorNetstat;
+ Detector * m_detectorNetworkStatus;
+ bool m_pluginConnected;
+ QTimer * m_timer;
+ OnlineInquiry * m_onlineInquiry;
+};
+
+#endif /* SMPPPDCSPLUGIN_H */
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/smpppdcs/smpppdcspreferences.cpp b/kopete/plugins/smpppdcs/smpppdcspreferences.cpp
new file mode 100644
index 00000000..ddce3572
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcspreferences.cpp
@@ -0,0 +1,187 @@
+/*
+ smpppdcspreferences.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlayout.h>
+#include <qregexp.h>
+#include <qradiobutton.h>
+
+#include <klistview.h>
+#include <klineedit.h>
+#include <knuminput.h>
+#include <kgenericfactory.h>
+
+#include "kopeteaccount.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccountmanager.h"
+
+#include "smpppdlocationwidget.h"
+#include "smpppdcspreferences.h"
+#include "smpppdcsprefsimpl.h"
+#include "smpppdcsconfig.h"
+
+typedef KGenericFactory<SMPPPDCSPreferences> SMPPPDCSPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY(kcm_kopete_smpppdcs, SMPPPDCSPreferencesFactory("kcm_kopete_smpppdcs"))
+
+SMPPPDCSPreferences::SMPPPDCSPreferences(QWidget * parent, const char * /* name */, const QStringList& args)
+ : KCModule(SMPPPDCSPreferencesFactory::instance(), parent, args), m_ui(NULL) {
+
+ Kopete::AccountManager * manager = Kopete::AccountManager::self();
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ m_ui = new SMPPPDCSPrefs(this);
+
+ for(QPtrListIterator<Kopete::Account> it(manager->accounts()); it.current(); ++it)
+ {
+ QString protoName;
+ QRegExp rex("(.*)Protocol");
+
+ if(rex.search((*it)->protocol()->pluginId()) > -1) {
+ protoName = rex.cap(1);
+ } else {
+ protoName = (*it)->protocol()->pluginId();
+ }
+
+ if(it.current()->inherits("Kopete::ManagedConnectionAccount")) {
+ protoName += QString(", %1").arg(i18n("connection status is managed by Kopete"));
+ }
+
+ QCheckListItem * cli = new QCheckListItem(m_ui->accountList,
+ (*it)->accountId() + " (" + protoName + ")", QCheckListItem::CheckBox);
+ cli->setPixmap(0, (*it)->accountIcon());
+
+ m_accountMapOld[cli->text(0)] = AccountPrivMap(FALSE, (*it)->protocol()->pluginId() + "_" + (*it)->accountId());
+ m_accountMapCur[cli->text(0)] = AccountPrivMap(FALSE, (*it)->protocol()->pluginId() + "_" + (*it)->accountId());;
+ m_ui->accountList->insertItem(cli);
+ }
+
+ connect(m_ui->accountList, SIGNAL(clicked(QListViewItem *)), this, SLOT(listClicked(QListViewItem *)));
+
+ // connect for modified
+ connect(m_ui->useNetstat, SIGNAL(clicked()), this, SLOT(slotModified()));
+ connect(m_ui->useSmpppd, SIGNAL(clicked()), this, SLOT(slotModified()));
+
+ connect(m_ui->SMPPPDLocation->server, SIGNAL(textChanged(const QString&)), this, SLOT(slotModified()));
+ connect(m_ui->SMPPPDLocation->port, SIGNAL(valueChanged(int)), this, SLOT(slotModified()));
+ connect(m_ui->SMPPPDLocation->Password, SIGNAL(textChanged(const QString&)), this, SLOT(slotModified()));
+
+ load();
+}
+
+SMPPPDCSPreferences::~SMPPPDCSPreferences() {
+ delete m_ui;
+}
+
+void SMPPPDCSPreferences::listClicked(QListViewItem * item)
+{
+ QCheckListItem * cli = dynamic_cast<QCheckListItem *>(item);
+
+ if(cli->isOn() != m_accountMapCur[cli->text(0)].m_on) {
+ AccountMap::iterator itOld = m_accountMapOld.begin();
+ AccountMap::iterator itCur;
+ bool change = FALSE;
+
+ for(itCur = m_accountMapCur.begin(); itCur != m_accountMapCur.end(); ++itCur, ++itOld) {
+ if((*itCur).m_on != (*itOld).m_on){
+ change = TRUE;
+ break;
+ }
+ }
+ emit KCModule::changed(change);
+ }
+ m_accountMapCur[cli->text(0)].m_on = cli->isOn();
+}
+
+void SMPPPDCSPreferences::defaults()
+{
+ QListViewItemIterator it(m_ui->accountList);
+ while(it.current()) {
+ QCheckListItem * cli = dynamic_cast<QCheckListItem *>(it.current());
+ cli->setOn(FALSE);
+ ++it;
+ }
+
+ SMPPPDCSConfig::self()->setDefaults();
+
+ m_ui->useNetstat->setChecked(SMPPPDCSConfig::self()->useNetstat());
+ m_ui->useSmpppd->setChecked(SMPPPDCSConfig::self()->useSmpppd());
+
+ m_ui->SMPPPDLocation->server->setText(SMPPPDCSConfig::self()->server());
+ m_ui->SMPPPDLocation->port->setValue(SMPPPDCSConfig::self()->port());
+ m_ui->SMPPPDLocation->Password->setText(SMPPPDCSConfig::self()->password());
+}
+
+void SMPPPDCSPreferences::load()
+{
+
+ SMPPPDCSConfig::self()->readConfig();
+
+ static QString rexStr = "^(.*) \\((.*)\\)";
+ QRegExp rex(rexStr);
+ QStringList list = SMPPPDCSConfig::self()->ignoredAccounts();
+ QListViewItemIterator it(m_ui->accountList);
+ while(it.current()) {
+ QCheckListItem * cli = dynamic_cast<QCheckListItem *>(it.current());
+ if(rex.search(cli->text(0)) > -1) {
+ bool isOn = list.contains(rex.cap(2) + "Protocol_" + rex.cap(1));
+ // m_accountMapOld[cli->text(0)].m_on = isOn;
+ m_accountMapCur[cli->text(0)].m_on = isOn;
+ cli->setOn(isOn);
+ }
+ ++it;
+ }
+
+ m_ui->useNetstat->setChecked(SMPPPDCSConfig::self()->useNetstat());
+ m_ui->useSmpppd->setChecked(SMPPPDCSConfig::self()->useSmpppd());
+
+ m_ui->SMPPPDLocation->server->setText(SMPPPDCSConfig::self()->server());
+ m_ui->SMPPPDLocation->port->setValue(SMPPPDCSConfig::self()->port());
+ m_ui->SMPPPDLocation->Password->setText(SMPPPDCSConfig::self()->password());
+
+ emit KCModule::changed(false);
+}
+
+void SMPPPDCSPreferences::save()
+{
+ QStringList list;
+ QListViewItemIterator it(m_ui->accountList);
+ while(it.current()) {
+
+ QCheckListItem * cli = dynamic_cast<QCheckListItem *>(it.current());
+ if(cli->isOn()) {
+ list.append(m_accountMapCur[cli->text(0)].m_id);
+ }
+
+ ++it;
+ }
+
+ SMPPPDCSConfig::self()->setIgnoredAccounts(list);
+
+ SMPPPDCSConfig::self()->setUseNetstat(m_ui->useNetstat->isChecked());
+ SMPPPDCSConfig::self()->setUseSmpppd(m_ui->useSmpppd->isChecked());
+
+ SMPPPDCSConfig::self()->setServer(m_ui->SMPPPDLocation->server->text());
+ SMPPPDCSConfig::self()->setPort(m_ui->SMPPPDLocation->port->value());
+ SMPPPDCSConfig::self()->setPassword(m_ui->SMPPPDLocation->Password->text());
+
+ SMPPPDCSConfig::self()->writeConfig();
+
+ emit KCModule::changed(false);
+}
+
+void SMPPPDCSPreferences::slotModified() {
+ emit KCModule::changed(true);
+}
+
+#include "smpppdcspreferences.moc"
diff --git a/kopete/plugins/smpppdcs/smpppdcspreferences.h b/kopete/plugins/smpppdcs/smpppdcspreferences.h
new file mode 100644
index 00000000..8bbeff69
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcspreferences.h
@@ -0,0 +1,77 @@
+/*
+ smpppdcspreferences.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCSPREFERENCES_H
+#define SMPPPDCSPREFERENCES_H
+
+#include <kcmodule.h>
+
+class QListViewItem;
+
+class SMPPPDCSPrefs;
+
+class AccountPrivMap {
+public:
+ AccountPrivMap(bool isOn = FALSE, const QString& id = QString::null)
+ : m_on(isOn), m_id(id) {}
+ bool m_on;
+ QString m_id;
+};
+
+/**
+ * @brief Module for the configuration of the smpppdcs-plugin
+ *
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+class SMPPPDCSPreferences : public KCModule {
+ Q_OBJECT
+
+ SMPPPDCSPreferences(const SMPPPDCSPreferences&);
+ SMPPPDCSPreferences& operator=(const SMPPPDCSPreferences&);
+
+public:
+ typedef QMap<QString, AccountPrivMap> AccountMap;
+
+ /**
+ * @brief Creates an <code>SMPPPDCSPreferences</code> instance
+ */
+ SMPPPDCSPreferences(QWidget * parent = 0, const char * name = 0, const QStringList &args = QStringList());
+
+ /**
+ * @brief Destroys an <code>SMPPPDCSPreferences</code> instance
+ */
+ virtual ~SMPPPDCSPreferences();
+
+ virtual void load();
+ virtual void save();
+ virtual void defaults();
+
+protected slots:
+ void listClicked(QListViewItem * item);
+
+private slots:
+ void slotModified();
+
+protected:
+
+ /// The UI class generated by the QT-designer
+ SMPPPDCSPrefs * m_ui;
+
+ AccountMap m_accountMapOld;
+ AccountMap m_accountMapCur;
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/smpppdcsprefs.ui b/kopete/plugins/smpppdcs/smpppdcsprefs.ui
new file mode 100644
index 00000000..067c55a3
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsprefs.ui
@@ -0,0 +1,284 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SMPPPDCSPrefsBase</class>
+<author>Heiko Schaefer</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SMPPPDCSPrefsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>476</width>
+ <height>225</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>SMPPPDCS Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Connection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>csMethod</cstring>
+ </property>
+ <property name="title">
+ <string>Method of Connection Status Detection</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useNetstat</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;netstat - Standard method of connection status detection</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Uses the netstat command to find a gateway; suitable on dial-up computers</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>useSmpppd</cstring>
+ </property>
+ <property name="text">
+ <string>smpppd - Ad&amp;vanced method of connection status detection</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Uses the smpppd on a gateway; suitable for a computer in a private network</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>autoCSLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>autoCSTest</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Try to Detect Automatically</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Tries to find an appropriate connection method</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>341</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>smpppdPrefs</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Location of the SMPPPD</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="SMPPPDLocationWidget">
+ <property name="name">
+ <cstring>SMPPPDLocation</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Acco&amp;unts</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>6</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>toIgnoreLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Choose the accounts to ignore:</string>
+ </property>
+ </widget>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Account</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>accountList</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>LastColumn</enum>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>SMPPPDLocationWidget</class>
+ <header location="local">smpppdlocationwidget.h</header>
+ <sizehint>
+ <width>16</width>
+ <height>16</height>
+ </sizehint>
+ <container>1</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="PNG" length="1125">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154388db5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c6a2573410000000049454e44ae426082</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>tabWidget</tabstop>
+ <tabstop>useNetstat</tabstop>
+ <tabstop>autoCSTest</tabstop>
+ <tabstop>useSmpppd</tabstop>
+ <tabstop>accountList</tabstop>
+</tabstops>
+<layoutdefaults spacing="0" margin="0"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>smpppdlocationwidget.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/smpppdcs/smpppdcsprefsimpl.cpp b/kopete/plugins/smpppdcs/smpppdcsprefsimpl.cpp
new file mode 100644
index 00000000..5a834c97
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsprefsimpl.cpp
@@ -0,0 +1,165 @@
+/*
+ smpppdcsprefsimpl.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <qradiobutton.h>
+
+#include <kstandarddirs.h>
+#include <kapplication.h>
+#include <kpushbutton.h>
+#include <kresolver.h>
+#include <knuminput.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "kopetepluginmanager.h"
+
+#include "../smpppdcsplugin.h"
+
+#include "smpppdlocationwidget.h"
+#include "smpppdcsprefsimpl.h"
+#include "smpppdsearcher.h"
+
+SMPPPDCSPrefs::SMPPPDCSPrefs(QWidget* parent, const char* name, WFlags fl)
+ : SMPPPDCSPrefsBase(parent, name, fl), m_plugin(NULL), m_scanProgressDlg(NULL), m_curSearcher(NULL) {
+
+ // search for our main-plugin instance
+ Kopete::Plugin * p = Kopete::PluginManager::self()->plugin("kopete_smpppdcs");
+ if(p) {
+ m_plugin = static_cast<SMPPPDCSPlugin *>(p);
+ }
+
+ // signals and slots connections
+ connect(useNetstat, SIGNAL(toggled(bool)), this, SLOT(disableSMPPPDSettings()));
+ connect(useSmpppd, SIGNAL(toggled(bool)), this, SLOT(enableSMPPPDSettings()));
+ connect(autoCSTest, SIGNAL(clicked()), this, SLOT(determineCSType()));
+
+ if(m_plugin) {
+ connect((QObject *)SMPPPDLocation->server, SIGNAL(textChanged(const QString&)),
+ m_plugin, SLOT(smpppdServerChanged(const QString&)));
+ }
+
+ // if netstat is NOT available, disable the option and set to SMPPPD
+ if(KStandardDirs::findExe("netstat") == QString::null) {
+ autoCSTest->setEnabled(FALSE);
+ useNetstat->setEnabled(FALSE);
+ useNetstat->setChecked(FALSE);
+ useSmpppd->setChecked(TRUE);
+ }
+}
+
+SMPPPDCSPrefs::~SMPPPDCSPrefs() {
+ delete m_scanProgressDlg;
+}
+
+void SMPPPDCSPrefs::determineCSType() {
+
+ // while we search, we'll disable the button
+ autoCSTest->setEnabled(false);
+ //kapp->processEvents();
+
+ /* broadcast network for a smpppd.
+ If one is available set to smpppd method */
+
+ SMPPPDSearcher searcher;
+ m_curSearcher = &searcher;
+
+ connect(&searcher, SIGNAL(smpppdFound(const QString&)), this, SLOT(smpppdFound(const QString&)));
+ connect(&searcher, SIGNAL(smpppdNotFound()), this, SLOT(smpppdNotFound()));
+ connect(&searcher, SIGNAL(scanStarted(uint)), this, SLOT(scanStarted(uint)));
+ connect(&searcher, SIGNAL(scanProgress(uint)), this, SLOT(scanProgress(uint)));
+ connect(&searcher, SIGNAL(scanFinished()), this, SLOT(scanFinished()));
+
+ searcher.searchNetwork();
+ m_curSearcher = NULL;
+}
+
+void SMPPPDCSPrefs::scanStarted(uint total) {
+ kdDebug(14312) << k_funcinfo << "Scanning for a SMPPPD started. Will scan " << total << " IPs" << endl;
+
+ // setup the scanProgress Dialog
+ if(!m_scanProgressDlg) {
+ m_scanProgressDlg = new KProgressDialog(this, 0, i18n("Searching"), i18n("Searching for a SMPPPD on the local network..."), TRUE);
+ m_scanProgressDlg->setAutoClose(TRUE);
+ m_scanProgressDlg->setAllowCancel(TRUE);
+ m_scanProgressDlg->setMinimumDuration(2000);
+
+ connect(m_scanProgressDlg, SIGNAL(cancelClicked()), this, SLOT(cancelScanning()));
+ }
+ m_scanProgressDlg->progressBar()->setTotalSteps(total);
+ m_scanProgressDlg->progressBar()->setProgress(0);
+ m_scanProgressDlg->show();
+}
+
+void SMPPPDCSPrefs::scanProgress(uint cur) {
+ m_scanProgressDlg->progressBar()->setProgress(cur);
+ kapp->processEvents();
+}
+
+void SMPPPDCSPrefs::cancelScanning() {
+ kdDebug(14312) << k_funcinfo << endl;
+ Q_ASSERT(m_curSearcher);
+ m_curSearcher->cancelSearch();
+}
+
+void SMPPPDCSPrefs::smpppdFound(const QString& host) {
+ kdDebug(14312) << k_funcinfo << endl;
+
+ QString myHost = host;
+
+ // try to get the domain name
+ struct in_addr addr;
+ if(inet_aton(host.ascii(), &addr)) {
+ struct hostent * hostEnt = gethostbyaddr(&addr.s_addr, sizeof(addr.s_addr), AF_INET);
+ if(hostEnt) {
+ myHost = hostEnt->h_name;
+ } else {
+#ifndef NDEBUG
+ switch(h_errno) {
+ case HOST_NOT_FOUND:
+ kdDebug(14312) << k_funcinfo << "No such host is known in the database." << endl;
+ break;
+ case TRY_AGAIN:
+ kdDebug(14312) << k_funcinfo << "Couldn't contact DNS server." << endl;
+ break;
+ case NO_RECOVERY:
+ kdDebug(14312) << k_funcinfo << "A non-recoverable error occurred." << endl;
+ break;
+ case NO_ADDRESS:
+ kdDebug(14312) << k_funcinfo << "The host database contains an entry for the name, but it doesn't have an associated Internet address." << endl;
+ break;
+ }
+#endif
+
+ }
+ }
+
+ SMPPPDLocation->setServer(myHost);
+ useNetstat->setChecked(false);
+ useSmpppd->setChecked(true);
+ autoCSTest->setEnabled(true);
+}
+
+void SMPPPDCSPrefs::smpppdNotFound() {
+ kdDebug(14312) << k_funcinfo << endl;
+ useNetstat->setChecked(true);
+ useSmpppd->setChecked(false);
+ autoCSTest->setEnabled(true);
+}
+
+#include "smpppdcsprefsimpl.moc"
diff --git a/kopete/plugins/smpppdcs/smpppdcsprefsimpl.h b/kopete/plugins/smpppdcs/smpppdcsprefsimpl.h
new file mode 100644
index 00000000..181ba9fa
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdcsprefsimpl.h
@@ -0,0 +1,76 @@
+/*
+ smpppdcsprefsimpl.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDCSPREFSIMPL_H
+#define SMPPPDCSPREFSIMPL_H
+
+#include <qgroupbox.h>
+
+#include <kprogress.h>
+
+#include "smpppdcsprefs.h"
+
+class SMPPPDCSPlugin;
+class SMPPPDSearcher;
+
+/**
+@author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class SMPPPDCSPrefs : public SMPPPDCSPrefsBase
+{
+ Q_OBJECT
+
+ SMPPPDCSPrefs(const SMPPPDCSPrefs&);
+ SMPPPDCSPrefs& operator=(const SMPPPDCSPrefs&);
+
+public:
+
+ SMPPPDCSPrefs(QWidget* parent, const char* name = 0, WFlags fl = 0);
+ ~SMPPPDCSPrefs();
+
+signals:
+ void foundSMPPPD(bool found);
+
+protected slots:
+ void enableSMPPPDSettings();
+ void disableSMPPPDSettings();
+ void determineCSType();
+ void smpppdFound(const QString & host);
+ void smpppdNotFound();
+ void scanStarted(uint total);
+ void scanProgress(uint cur);
+ void scanFinished();
+ void cancelScanning();
+
+private:
+ SMPPPDCSPlugin * m_plugin;
+ KProgressDialog * m_scanProgressDlg;
+ SMPPPDSearcher * m_curSearcher;
+};
+
+inline void SMPPPDCSPrefs::enableSMPPPDSettings() {
+ smpppdPrefs->setEnabled(true);
+}
+
+inline void SMPPPDCSPrefs::disableSMPPPDSettings() {
+ smpppdPrefs->setEnabled(false);
+}
+
+inline void SMPPPDCSPrefs::scanFinished() {
+ m_scanProgressDlg->hide();
+}
+
+#endif
diff --git a/kopete/plugins/smpppdcs/smpppdlocationui.ui b/kopete/plugins/smpppdcs/smpppdlocationui.ui
new file mode 100644
index 00000000..0424f6f6
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdlocationui.ui
@@ -0,0 +1,149 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>SMPPPDLocationWidgetBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SMPPPDLocationWidgetBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>365</width>
+ <height>167</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>SMPPPDLocation</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>server</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>server</cstring>
+ </property>
+ <property name="cursor">
+ <cursor>4</cursor>
+ </property>
+ <property name="text">
+ <string>localhost</string>
+ </property>
+ <property name="maxLength">
+ <number>256</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The server on which the SMPPPD is running</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>P&amp;ort:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>port</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>port</cstring>
+ </property>
+ <property name="cursor">
+ <cursor>4</cursor>
+ </property>
+ <property name="value">
+ <number>3185</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on which the SMPPPD is running on</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Default: 3185</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>130</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_passwordLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>Password</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>Password</cstring>
+ </property>
+ <property name="cursor">
+ <cursor>4</cursor>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The password to authenticate with the smpppd</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="0"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/smpppdcs/smpppdlocationwidget.cpp b/kopete/plugins/smpppdcs/smpppdlocationwidget.cpp
new file mode 100644
index 00000000..b20509d9
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdlocationwidget.cpp
@@ -0,0 +1,30 @@
+/*
+ smpppdlocationwidget.cpp
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <klineedit.h>
+
+#include "smpppdlocationwidget.h"
+
+SMPPPDLocationWidget::SMPPPDLocationWidget(QWidget* parent, const char* name, WFlags fl)
+ : SMPPPDLocationWidgetBase(parent, name, fl) {}
+
+SMPPPDLocationWidget::~SMPPPDLocationWidget() {}
+
+void SMPPPDLocationWidget::setServer(const QString& serv) {
+ server->setText(serv);
+}
+
+#include "smpppdlocationwidget.moc"
diff --git a/kopete/plugins/smpppdcs/smpppdlocationwidget.h b/kopete/plugins/smpppdcs/smpppdlocationwidget.h
new file mode 100644
index 00000000..00fa9157
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdlocationwidget.h
@@ -0,0 +1,39 @@
+/*
+ smpppdlocationwidget.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMPPPDLOCATIONWIDGET_H
+#define SMPPPDLOCATIONWIDGET_H
+
+#include "smpppdlocationui.h"
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class SMPPPDLocationWidget : public SMPPPDLocationWidgetBase
+{
+ Q_OBJECT
+
+ SMPPPDLocationWidget(const SMPPPDLocationWidget&);
+ SMPPPDLocationWidget& operator=(const SMPPPDLocationWidget&);
+
+public:
+ SMPPPDLocationWidget(QWidget* parent = 0, const char* name = 0, WFlags fl = 0);
+ ~SMPPPDLocationWidget();
+
+ void setServer(const QString& serv);
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/smpppdsearcher.cpp b/kopete/plugins/smpppdcs/smpppdsearcher.cpp
new file mode 100644
index 00000000..6ee9c878
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdsearcher.cpp
@@ -0,0 +1,189 @@
+/*
+ smpppdsearcher.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <qregexp.h>
+#include <qfile.h>
+
+#include <kprocess.h>
+#include <kdebug.h>
+
+#include "smpppdclient.h"
+#include "smpppdsearcher.h"
+
+SMPPPDSearcher::SMPPPDSearcher()
+ : m_cancelSearchNow(FALSE),
+ m_procIfconfig(NULL),
+m_procNetstat(NULL) {}
+
+SMPPPDSearcher::~SMPPPDSearcher() {
+ delete m_procIfconfig;
+ delete m_procNetstat;
+}
+
+/*!
+ \fn SMPPPDSearcher::searchNetwork() const
+ */
+void SMPPPDSearcher::searchNetwork() {
+ kdDebug(14312) << k_funcinfo << endl;
+
+ // the first point to search is localhost
+ if(!scan("127.0.0.1", "255.0.0.0")) {
+
+ m_procNetstat = new KProcess;
+ m_procNetstat->setEnvironment("LANG", "C"); // we want to force english output
+
+ *m_procNetstat << "/bin/netstat" << "-rn";
+ connect(m_procNetstat, SIGNAL(receivedStdout(KProcess *,char *,int)), this, SLOT(slotStdoutReceivedNetstat(KProcess *,char *,int)));
+ if(!m_procNetstat->start(KProcess::Block, KProcess::Stdout)) {
+ kdDebug(14312) << k_funcinfo << "Couldn't execute /sbin/netstat -rn" << endl << "Perhaps the package net-tools isn't installed." << endl;
+
+ emit smpppdNotFound();
+ }
+
+ delete m_procNetstat;
+ m_procNetstat = NULL;
+ }
+}
+
+/*!
+ \fn SMPPPDSearcher::slotStdoutReceived(KProcess * proc, char * buf, int len)
+ */
+void SMPPPDSearcher::slotStdoutReceivedIfconfig(KProcess * /* proc */, char * buf, int len) {
+ kdDebug(14312) << k_funcinfo << endl;
+
+ QString myBuf = QString::fromLatin1(buf,len);
+ QRegExp rex("^[ ]{10}.*inet addr:([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}).*Mask:([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
+ // tokenize the string into lines
+ QStringList toks = QStringList::split("\n", myBuf);
+ for(QStringList::size_type i = 0; i < toks.count(); i++) {
+ if(rex.exactMatch(toks[i])) {
+ if(scan(rex.cap(1), rex.cap(2))) {
+ return;
+ }
+ }
+ }
+
+ emit smpppdNotFound();
+}
+void SMPPPDSearcher::slotStdoutReceivedNetstat(KProcess * /* proc */, char * buf, int len) {
+ kdDebug(14312) << k_funcinfo << endl;
+
+ QRegExp rexGW(".*\\n0.0.0.0[ ]*([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}).*");
+ QString myBuf = QString::fromLatin1(buf,len);
+
+ if(!(rexGW.exactMatch(myBuf) && scan(rexGW.cap(1), "255.255.255.255"))) {
+ // if netstat -r found no gateway we search the network
+ m_procIfconfig = new KProcess;
+ m_procIfconfig->setEnvironment("LANG", "C"); // we want to force english output
+
+ *m_procIfconfig << "/sbin/ifconfig";
+ connect(m_procIfconfig, SIGNAL(receivedStdout(KProcess *,char *,int)), this, SLOT(slotStdoutReceivedIfconfig(KProcess *,char *,int)));
+ if(!m_procIfconfig->start(KProcess::Block, KProcess::Stdout)) {
+ kdDebug(14312) << k_funcinfo << "Couldn't execute /sbin/ifconfig" << endl << "Perhaps the package net-tools isn't installed." << endl;
+
+ emit smpppdNotFound();
+ }
+
+ delete m_procIfconfig;
+ m_procIfconfig = NULL;
+ }
+}
+
+/*!
+ \fn SMPPPDSearcher::scan() const
+ */
+bool SMPPPDSearcher::scan(const QString& ip, const QString& mask) {
+ kdDebug(14312) << k_funcinfo << "Scanning " << ip << "/" << mask << "..." << endl;
+
+ SMPPPD::Client client;
+
+ if(ip == "127.0.0.1") { // if localhost, we only scan this one host
+ if(client.connect(ip, 3185)) {
+ client.disconnect();
+ emit smpppdFound(ip);
+ return true;
+ }
+
+ return false;
+ }
+
+ uint min_range = 0;
+ uint max_range = 255;
+
+ // calculate ip range (only last mask entry)
+ QRegExp lastRex("([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})");
+ if(lastRex.exactMatch(ip)) {
+
+ uint lastWordIP = lastRex.cap(4).toUInt();
+
+ QStringList ipToks;
+ for(int i = 1; i < 5; i++) {
+ ipToks.push_back(lastRex.cap(i));
+ }
+
+ if(lastRex.exactMatch(mask)) {
+ uint lastWordMask = lastRex.cap(4).toUInt();
+
+ if(lastWordMask == 0) {
+ kdDebug(14312) << k_funcinfo << "IP-Range: " << ipToks[0] << "." << ipToks[1] << "." << ipToks[2] << ".0 - " << ipToks[0] << "." << ipToks[1] << "." << ipToks[2] << ".255" << endl;
+ max_range = 255;
+ } else if(lastWordMask == 255) {
+ min_range = max_range = lastWordIP;
+ } else {
+ kdDebug(14312) << k_funcinfo << "IP-Range: " << ipToks[0] << "." << ipToks[1] << "." << ipToks[2] << ".0 - " << ipToks[0] << "." << ipToks[1] << "." << ipToks[2] << "." << lastWordMask << endl;
+ max_range = lastWordMask;
+ }
+ }
+
+ uint range = max_range - min_range;
+ m_cancelSearchNow = FALSE;
+ if(range > 1) {
+ emit scanStarted(max_range);
+ }
+ for(uint i = min_range; i <= max_range; i++) {
+ if(m_cancelSearchNow) {
+ if(range > 1) {
+ emit scanFinished();
+ }
+ break;
+ }
+ if(range > 1) {
+ emit scanProgress(i);
+ }
+
+ if(client.connect(QString(ipToks[0] + "." + ipToks[1] + "." + ipToks[2] + "." + QString::number(i)), 3185)) {
+ client.disconnect();
+ emit smpppdFound(ip);
+ if(range > 1) {
+ emit scanFinished();
+ }
+ return true;
+ }
+#ifndef NDEBUG
+ else {
+ kdDebug(14312) << k_funcinfo << "No smpppd found at " << QString(ipToks[0] + "." + ipToks[1] + "." + ipToks[2] + "." + QString::number(i)) << endl;
+ }
+#endif
+ }
+ if(range > 1) {
+ emit scanFinished();
+ }
+ }
+
+ return false;
+}
+
+#include "smpppdsearcher.moc"
diff --git a/kopete/plugins/smpppdcs/smpppdsearcher.h b/kopete/plugins/smpppdcs/smpppdsearcher.h
new file mode 100644
index 00000000..af36637d
--- /dev/null
+++ b/kopete/plugins/smpppdcs/smpppdsearcher.h
@@ -0,0 +1,102 @@
+/*
+ smpppdsearcher.h
+
+ Copyright (c) 2004-2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef SMPPPDSEARCHER_H
+#define SMPPPDSEARCHER_H
+
+#include <kresolver.h>
+
+class KProcess;
+
+/**
+ * @brief Searches a network for a smpppd
+ *
+ * @todo Use of the SLP to find the smpppd
+ * @author Heiko Sch&auml;fer <heiko@rangun.de>
+ */
+class SMPPPDSearcher : public QObject {
+ Q_OBJECT
+
+ SMPPPDSearcher(const SMPPPDSearcher&);
+ SMPPPDSearcher& operator=(const SMPPPDSearcher&);
+
+public:
+ /**
+ * @brief Creates an <code>SMPPPDSearcher</code> instance
+ */
+ SMPPPDSearcher();
+
+ /**
+ * @brief Destroys an <code>SMPPPDSearcher</code> instance
+ */
+ ~SMPPPDSearcher();
+
+ /**
+ * @brief Triggers a network scan to find a smpppd
+ * @see smpppdFound
+ * @see smpppdNotFound
+ */
+ void searchNetwork();
+
+ void cancelSearch();
+
+protected:
+ /**
+ * @brief Scans a network for a smpppd
+ *
+ * Scans a network for a smpppd described by
+ * ip and mask.
+ *
+ * @param ip the ntwork ip
+ * @param mask the network mask
+ * @return <code>TRUE</code> if an smpppd was found
+ */
+ bool scan(const QString& ip, const QString& mask);
+
+signals:
+ /**
+ * @brief A smppd was found
+ *
+ * @param host the host there the smpppd was found
+ */
+ void smpppdFound(const QString& host);
+
+ /**
+ * @brief No smpppd was found
+ */
+ void smpppdNotFound();
+
+ void scanStarted(uint total);
+ void scanProgress(uint cur);
+ void scanFinished();
+
+protected slots:
+ void slotStdoutReceivedIfconfig(KProcess * proc, char * buf, int len);
+ void slotStdoutReceivedNetstat (KProcess * proc, char * buf, int len);
+
+private:
+ bool m_cancelSearchNow;
+ KProcess * m_procIfconfig;
+ KProcess * m_procNetstat;
+};
+
+inline void SMPPPDSearcher::cancelSearch() {
+ m_cancelSearchNow = TRUE;
+}
+
+#endif
+
diff --git a/kopete/plugins/smpppdcs/unittest/Makefile.am b/kopete/plugins/smpppdcs/unittest/Makefile.am
new file mode 100644
index 00000000..9694bff9
--- /dev/null
+++ b/kopete/plugins/smpppdcs/unittest/Makefile.am
@@ -0,0 +1,15 @@
+INCLUDES = -I$(top_srcdir)/src $(all_includes) -I../libsmpppdclient
+METASOURCES = AUTO
+
+
+check_PROGRAMS = smpppdcstests
+
+smpppdcstests_SOURCES = main.cpp clienttest.cpp
+smpppdcstests_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+smpppdcstests_LDADD = ../libsmpppdclient/libsmpppdclient.la -lkunittestgui
+
+noinst_HEADERS = clienttest.h
+
+check:
+ kunittest ./smpppdcstests ClientTest
+
diff --git a/kopete/plugins/smpppdcs/unittest/clienttest.cpp b/kopete/plugins/smpppdcs/unittest/clienttest.cpp
new file mode 100644
index 00000000..5affd83c
--- /dev/null
+++ b/kopete/plugins/smpppdcs/unittest/clienttest.cpp
@@ -0,0 +1,121 @@
+/*
+ clienttest.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include "smpppdclient.h"
+
+#include "clienttest.h"
+
+ClientTest::ClientTest(const char * name)
+ : KUnitTest::SlotTester(name) {}
+
+ClientTest::~ClientTest() {}
+
+void ClientTest::testInitIsReady() {
+ SMPPPD::Client c;
+ CHECK(c.isReady(), false);
+}
+
+void ClientTest::testAfterConnectIsReady() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ CHECK(c.isReady(), true);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testConnect() {
+ SMPPPD::Client c;
+ CHECK(c.connect("warwar", 3185), true);
+ CHECK(c.connect("localhost", 3185), false);
+}
+
+void ClientTest::testCommunicationBeforeConnect() {
+ SMPPPD::Client c;
+ QStringList l = c.getInterfaceConfigurations();
+
+ CHECK(l.count() == 0, true);
+ CHECK(c.statusInterface("ifcfg0"), false);
+}
+
+void ClientTest::testServerIDBeforeConnect() {
+ SMPPPD::Client c;
+ CHECK(c.serverID(), QString::null);
+}
+
+void ClientTest::testServerVersionBeforeConnect() {
+ SMPPPD::Client c;
+ CHECK(c.serverVersion(), QString::null);
+}
+
+void ClientTest::testCommunicationAfterConnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ CHECK(c.getInterfaceConfigurations().count() > 0, true);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testServerIDAfterConnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ CHECK(c.serverID().isEmpty(), false);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testServerVersionAfterConnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ CHECK(c.serverVersion().isEmpty(), false);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testCommunicationAfterDisconnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ c.disconnect();
+ CHECK(c.getInterfaceConfigurations().count() == 0, true);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testServerIDAfterDisconnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ c.disconnect();
+ CHECK(c.serverID(), QString::null);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+void ClientTest::testServerVersionAfterDisconnect() {
+ SMPPPD::Client c;
+ if(c.connect("warwar", 3185)) {
+ c.disconnect();
+ CHECK(c.serverVersion(), QString::null);
+ } else {
+ SKIP("Test skipped because no smpppd at warwar:3185");
+ }
+}
+
+#include "clienttest.moc"
diff --git a/kopete/plugins/smpppdcs/unittest/clienttest.h b/kopete/plugins/smpppdcs/unittest/clienttest.h
new file mode 100644
index 00000000..5db7ef7b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/unittest/clienttest.h
@@ -0,0 +1,50 @@
+/*
+ clienttest.h
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CLIENTTEST_H
+#define CLIENTTEST_H
+
+#include <kunittest/tester.h>
+
+/**
+ @author Heiko Sch&auml;fer <heiko@rangun.de>
+*/
+class ClientTest : public KUnitTest::SlotTester {
+ Q_OBJECT
+
+ ClientTest(const ClientTest&);
+ ClientTest& operator=(const ClientTest&);
+
+public:
+ ClientTest(const char * name = 0);
+ virtual ~ClientTest();
+
+private slots:
+ void testInitIsReady();
+ void testAfterConnectIsReady();
+ void testConnect();
+ void testCommunicationBeforeConnect();
+ void testServerIDBeforeConnect();
+ void testServerVersionBeforeConnect();
+ void testCommunicationAfterConnect();
+ void testServerIDAfterConnect();
+ void testServerVersionAfterConnect();
+ void testCommunicationAfterDisconnect();
+ void testServerIDAfterDisconnect();
+ void testServerVersionAfterDisconnect();
+};
+
+#endif
diff --git a/kopete/plugins/smpppdcs/unittest/main.cpp b/kopete/plugins/smpppdcs/unittest/main.cpp
new file mode 100644
index 00000000..ec14489b
--- /dev/null
+++ b/kopete/plugins/smpppdcs/unittest/main.cpp
@@ -0,0 +1,45 @@
+/*
+ main.cpp
+
+ Copyright (c) 2006 by Heiko Schaefer <heiko@rangun.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kunittest/runnergui.h>
+
+#include "clienttest.h"
+
+static const char description[] = I18N_NOOP("SMPPPDClientTests");
+static const char version[] = "0.1";
+static KCmdLineOptions options[] = { KCmdLineLastOption };
+
+int main( int argc, char** argv ) {
+ KAboutData about("SMPPPDClientTests", I18N_NOOP("SMPPPDClientTests"), version, description,
+ KAboutData::License_BSD, "(C) 2006 Heiko Schäfer", 0, 0, "heiko@rangun.de");
+
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions(options);
+ KApplication app;
+
+ KUnitTest::Runner::registerTester("ClientTest", new ClientTest);
+
+ KUnitTest::RunnerGUI runner(0);
+ runner.show();
+ app.setMainWidget(&runner);
+
+ return app.exec();
+}
diff --git a/kopete/plugins/statistics/Makefile.am b/kopete/plugins/statistics/Makefile.am
new file mode 100644
index 00000000..b6aa7812
--- /dev/null
+++ b/kopete/plugins/statistics/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+
+INCLUDES = $(KOPETE_INCLUDES) $(all_includes)
+
+SUBDIRS = sqlite
+
+kde_module_LTLIBRARIES = kopete_statistics.la
+
+kopete_statistics_la_SOURCES = statisticsplugin.cpp statisticsdb.cpp statisticsdialog.cpp statisticswidget.ui statisticscontact.cpp statisticsdcopiface.skel
+
+kopete_statistics_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_statistics_la_LIBADD = ../../libkopete/libkopete.la sqlite/libsqlite.la
+
+service_DATA = kopete_statistics.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_statistics
+mydata_DATA = statisticsui.rc
+
+mydatadirimagesdir = $(kde_datadir)/kopete/pics/statistics
+mydatadirimages_DATA = images/blue.png images/navy.png images/black.png images/gray.png
diff --git a/kopete/plugins/statistics/TODO b/kopete/plugins/statistics/TODO
new file mode 100644
index 00000000..dba76062
--- /dev/null
+++ b/kopete/plugins/statistics/TODO
@@ -0,0 +1,3 @@
+* A database cleaner.
+ - If theyre are two Online status following themselves with small times betweens them (less than 1 minute ?), merge them.
+ \ No newline at end of file
diff --git a/kopete/plugins/statistics/images/black.png b/kopete/plugins/statistics/images/black.png
new file mode 100644
index 00000000..b8344f01
--- /dev/null
+++ b/kopete/plugins/statistics/images/black.png
Binary files differ
diff --git a/kopete/plugins/statistics/images/blue.png b/kopete/plugins/statistics/images/blue.png
new file mode 100644
index 00000000..e58e283d
--- /dev/null
+++ b/kopete/plugins/statistics/images/blue.png
Binary files differ
diff --git a/kopete/plugins/statistics/images/gray.png b/kopete/plugins/statistics/images/gray.png
new file mode 100644
index 00000000..49ba3af4
--- /dev/null
+++ b/kopete/plugins/statistics/images/gray.png
Binary files differ
diff --git a/kopete/plugins/statistics/images/navy.png b/kopete/plugins/statistics/images/navy.png
new file mode 100644
index 00000000..0038d5e1
--- /dev/null
+++ b/kopete/plugins/statistics/images/navy.png
Binary files differ
diff --git a/kopete/plugins/statistics/kopete_statistics.desktop b/kopete/plugins/statistics/kopete_statistics.desktop
new file mode 100644
index 00000000..239e5320
--- /dev/null
+++ b/kopete/plugins/statistics/kopete_statistics.desktop
@@ -0,0 +1,111 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=statistics
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_statistics
+X-KDE-PluginInfo-Author=Marc Cramdal
+X-KDE-PluginInfo-Email=marc.cramdal@yahoo.fr
+X-KDE-PluginInfo-Name=kopete_statistics
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Statistics
+Name[be]=СтатыÑтыка
+Name[bg]=СтатиÑтика
+Name[bn]=পরিসংখà§à¦¯à¦¾à¦¨
+Name[br]=Stadegoù
+Name[bs]=Statistike
+Name[ca]=Estadístiques
+Name[cs]=Statistika
+Name[cy]=Ystadegau
+Name[da]=Statistik
+Name[de]=Statistiken
+Name[el]=Στατιστικά
+Name[eo]=Statistikoj
+Name[es]=Estadísticas
+Name[et]=Statistika
+Name[eu]=Estatistikak
+Name[fa]=آمار
+Name[fi]=Tilastot
+Name[fr]=Statistiques
+Name[ga]=Staitistic
+Name[gl]=Estatísticas
+Name[he]=סטטיסטיקה
+Name[hu]=Statisztika
+Name[is]=Tölfræði
+Name[it]=Statistiche
+Name[ja]=統計
+Name[ka]=სტáƒáƒ¢áƒ˜áƒ¡áƒ¢áƒ˜áƒ™áƒ
+Name[kk]=СтатиÑтика
+Name[km]=ស្ážáž·ážáž·
+Name[lt]=Statistika
+Name[nb]=Statistikk
+Name[nds]=Statistik
+Name[ne]=तशà¥à¤¯à¤¾à¤™à¥à¤•
+Name[nl]=Statistieken
+Name[nn]=Statistikk
+Name[pa]=ਅੰਕੜੇ
+Name[pl]=Statystyki
+Name[pt]=Estatísticas
+Name[pt_BR]=Estatísticas
+Name[ro]=Statistici
+Name[ru]=СтатиÑтика
+Name[sk]=Å tatistiky
+Name[sl]=Statistika
+Name[sr]=СтатиÑтика
+Name[sr@Latn]=Statistika
+Name[sv]=Statistik
+Name[tr]=Ä°statistlikler
+Name[uk]=СтатиÑтика
+Name[uz]=Statistika
+Name[uz@cyrillic]=СтатиÑтика
+Name[zh_CN]=统计
+Name[zh_HK]=統計
+Name[zh_TW]=統計
+Comment=Gather some meaningful statistics
+Comment[bg]=ПриÑтавка за обобщаваща ÑтатиÑтика
+Comment[bn]=কিছৠঅরà§à¦¥à¦ªà§‚রà§à¦£ পরিসংখà§à¦¯à¦¾à¦¨ সমবেত করে
+Comment[bs]=Prikuplja neke interesantne statistike
+Comment[ca]=Obté estadístiques
+Comment[cs]=ShromažÄuje užiteÄné statistiky
+Comment[da]=Indsamling af noget meningsfuld statistik
+Comment[de]=Einige aussagekräftige Statistiken sammeln
+Comment[el]=Συλλογή μεÏικών σημαντικών στατιστικών
+Comment[es]=Recoge algunas estadí­sticas significativas
+Comment[et]=Veidi statistikat, millest võib kasu olla
+Comment[eu]=Estatistika esanguratsuak bildu
+Comment[fa]=جمع‌آوری بعضی از آمارهای با معنی
+Comment[fi]=Kerää hyödyllisiä tilastoja
+Comment[fr]=Récupération de quelques statistiques utiles
+Comment[he]=מלקט סטטיסטיקה בעלת משמעות
+Comment[hu]=Statisztikai adatok gyűjtése
+Comment[is]=Safna saman nokkrum gagnlegum tölfræði upplýsingum
+Comment[it]=Raccoglie delle statistiche significative
+Comment[ja]=有æ„義ãªçµ±è¨ˆãƒ‡ãƒ¼ã‚¿ã‚’åŽé›†ã—ã¾ã™
+Comment[ka]=სტáƒáƒ¢áƒ˜áƒ¡áƒ¢áƒ˜áƒ™áƒ˜áƒ¡ შეგრáƒáƒ•áƒ”ბáƒ
+Comment[kk]=Кейбір маңызды ÑтатиÑтиканы жинақтау
+Comment[km]=ប្រមែប្រមូល​ស្ážáž·ážáž·â€‹ážŸáŸ†ážáž¶áž“់ៗ​មួយ​ចំនួន
+Comment[lt]=Rinkti prasmingÄ… statistikÄ…
+Comment[nb]=Samle noen opplysninger med mening
+Comment[nds]=En poor sinnvulle Statistiken sammeln
+Comment[ne]=केही अरà¥à¤¥à¤ªà¥‚रà¥à¤£ तशà¥à¤¯à¤¾à¤™à¥à¤• भेला गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Verzamel wat betekenisvolle statistieken
+Comment[nn]=Samla nokre opplysningar med meining
+Comment[pl]=Zbieranie niektórych znaczących statystyk
+Comment[pt]=Recolher algumas estatísticas relevantes
+Comment[pt_BR]=Coleta estatísticas úteis
+Comment[ru]=Собрать некоторые ÑтатиÑтичеÑкие данные
+Comment[sk]=Zozbiera niektoré významné štatistiky
+Comment[sl]=Zbiranje uporabne statistike
+Comment[sr]=Сакупи нешто кориÑне ÑтатиÑтике
+Comment[sr@Latn]=Sakupi nešto korisne statistike
+Comment[sv]=Samla in en del användbar statistik
+Comment[tr]=Anlamlı istatistlikler topla
+Comment[uk]=Зібрати деÑкі ÑтатиÑтичні дані
+Comment[zh_CN]=收å–更有æ„义的统计
+Comment[zh_HK]=收集一些有用的統計
+Comment[zh_TW]=收集一些有用的統計
diff --git a/kopete/plugins/statistics/kopetestatistics.kateproject b/kopete/plugins/statistics/kopetestatistics.kateproject
new file mode 100644
index 00000000..e06ea21b
--- /dev/null
+++ b/kopete/plugins/statistics/kopetestatistics.kateproject
@@ -0,0 +1,7 @@
+[Project Dir]
+Dirs=
+Files=statisticscontact.cpp/statisticscontact.h/statisticsdb.cpp/statisticsdb.h/statisticsdialog.cpp/statisticsdialog.h/statisticsplugin.cpp/statisticsplugin.h/statisticsui.rc/statisticswidget.cpp/statisticswidget.h/statisticswidget.ui/TODO/Makefile.am/kopete_statistics.template.html/kopete_statistics.css/kopete_statistics.desktop/statisticsdcopiface.h
+
+[Project File]
+Name=KopeteStatistics
+Type=Default
diff --git a/kopete/plugins/statistics/sqlite/Makefile.am b/kopete/plugins/statistics/sqlite/Makefile.am
new file mode 100644
index 00000000..f647c6d5
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/Makefile.am
@@ -0,0 +1,51 @@
+noinst_LTLIBRARIES = \
+ libsqlite.la
+
+KDE_CFLAGS = \
+ -w
+
+libsqlite_la_CFLAGS = \
+ $(all_includes) \
+ -DTHREADSAFE=1
+
+libsqlite_la_LDFLAGS = \
+ $(LIBPTHREAD)
+
+libsqlite_la_SOURCES = \
+ attach.c \
+ auth.c \
+ btree.c \
+ build.c \
+ date.c \
+ delete.c \
+ encode.c \
+ expr.c \
+ func.c \
+ hash.c \
+ insert.c \
+ legacy.c \
+ main.c \
+ opcodes.c \
+ os_mac.c \
+ os_unix.c \
+ os_win.c \
+ pager.c \
+ parse.c \
+ pragma.c \
+ printf.c \
+ random.c \
+ select.c \
+ shell.c \
+ table.c \
+ tokenize.c \
+ trigger.c \
+ update.c \
+ utf.c \
+ util.c \
+ vacuum.c \
+ vdbe.c \
+ vdbeapi.c \
+ vdbeaux.c \
+ vdbemem.c \
+ where.c
+
diff --git a/kopete/plugins/statistics/sqlite/attach.c b/kopete/plugins/statistics/sqlite/attach.c
new file mode 100644
index 00000000..2f089986
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/attach.c
@@ -0,0 +1,329 @@
+/*
+** 2003 April 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used to implement the ATTACH and DETACH commands.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** This routine is called by the parser to process an ATTACH statement:
+**
+** ATTACH DATABASE filename AS dbname
+**
+** The pFilename and pDbname arguments are the tokens that define the
+** filename and dbname in the ATTACH statement.
+*/
+void sqlite3Attach(
+ Parse *pParse, /* The parser context */
+ Token *pFilename, /* Name of database file */
+ Token *pDbname, /* Name of the database to use internally */
+ int keyType, /* 0: no key. 1: TEXT, 2: BLOB */
+ Token *pKey /* Text of the key for keytype 1 and 2 */
+){
+ Db *aNew;
+ int rc, i;
+ char *zFile, *zName;
+ sqlite3 *db;
+ Vdbe *v;
+
+ v = sqlite3GetVdbe(pParse);
+ if( !v ) return;
+ sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ if( pParse->explain ) return;
+ db = pParse->db;
+ if( db->nDb>=MAX_ATTACHED+2 ){
+ sqlite3ErrorMsg(pParse, "too many attached databases - max %d",
+ MAX_ATTACHED);
+ pParse->rc = SQLITE_ERROR;
+ return;
+ }
+
+ if( !db->autoCommit ){
+ sqlite3ErrorMsg(pParse, "cannot ATTACH database within transaction");
+ pParse->rc = SQLITE_ERROR;
+ return;
+ }
+
+ zFile = sqlite3NameFromToken(pFilename);;
+ if( zFile==0 ) return;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( sqlite3AuthCheck(pParse, SQLITE_ATTACH, zFile, 0, 0)!=SQLITE_OK ){
+ sqliteFree(zFile);
+ return;
+ }
+#endif /* SQLITE_OMIT_AUTHORIZATION */
+
+ zName = sqlite3NameFromToken(pDbname);
+ if( zName==0 ) return;
+ for(i=0; i<db->nDb; i++){
+ char *z = db->aDb[i].zName;
+ if( z && sqlite3StrICmp(z, zName)==0 ){
+ sqlite3ErrorMsg(pParse, "database %z is already in use", zName);
+ pParse->rc = SQLITE_ERROR;
+ sqliteFree(zFile);
+ return;
+ }
+ }
+
+ if( db->aDb==db->aDbStatic ){
+ aNew = sqliteMalloc( sizeof(db->aDb[0])*3 );
+ if( aNew==0 ) return;
+ memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
+ }else{
+ aNew = sqliteRealloc(db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
+ if( aNew==0 ) return;
+ }
+ db->aDb = aNew;
+ aNew = &db->aDb[db->nDb++];
+ memset(aNew, 0, sizeof(*aNew));
+ sqlite3HashInit(&aNew->tblHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&aNew->idxHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&aNew->trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&aNew->aFKey, SQLITE_HASH_STRING, 1);
+ aNew->zName = zName;
+ aNew->safety_level = 3;
+ rc = sqlite3BtreeFactory(db, zFile, 0, MAX_PAGES, &aNew->pBt);
+ if( rc ){
+ sqlite3ErrorMsg(pParse, "unable to open database: %s", zFile);
+ }
+#if SQLITE_HAS_CODEC
+ {
+ extern int sqlite3CodecAttach(sqlite3*, int, void*, int);
+ char *zKey;
+ int nKey;
+ if( keyType==0 ){
+ /* No key specified. Use the key from the main database */
+ extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*);
+ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
+ }else if( keyType==1 ){
+ /* Key specified as text */
+ zKey = sqlite3NameFromToken(pKey);
+ nKey = strlen(zKey);
+ }else{
+ /* Key specified as a BLOB */
+ char *zTemp;
+ assert( keyType==2 );
+ pKey->z++;
+ pKey->n--;
+ zTemp = sqlite3NameFromToken(pKey);
+ zKey = sqlite3HexToBlob(zTemp);
+ sqliteFree(zTemp);
+ }
+ sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
+ if( keyType ){
+ sqliteFree(zKey);
+ }
+ }
+#endif
+ sqliteFree(zFile);
+ db->flags &= ~SQLITE_Initialized;
+ if( pParse->nErr==0 && rc==SQLITE_OK ){
+ rc = sqlite3ReadSchema(pParse);
+ }
+ if( rc ){
+ int i = db->nDb - 1;
+ assert( i>=2 );
+ if( db->aDb[i].pBt ){
+ sqlite3BtreeClose(db->aDb[i].pBt);
+ db->aDb[i].pBt = 0;
+ }
+ sqlite3ResetInternalSchema(db, 0);
+ if( 0==pParse->nErr ){
+ pParse->nErr++;
+ pParse->rc = SQLITE_ERROR;
+ }
+ }
+}
+
+/*
+** This routine is called by the parser to process a DETACH statement:
+**
+** DETACH DATABASE dbname
+**
+** The pDbname argument is the name of the database in the DETACH statement.
+*/
+void sqlite3Detach(Parse *pParse, Token *pDbname){
+ int i;
+ sqlite3 *db;
+ Vdbe *v;
+ Db *pDb = 0;
+
+ v = sqlite3GetVdbe(pParse);
+ if( !v ) return;
+ sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+ if( pParse->explain ) return;
+ db = pParse->db;
+ for(i=0; i<db->nDb; i++){
+ pDb = &db->aDb[i];
+ if( pDb->pBt==0 || pDb->zName==0 ) continue;
+ if( strlen(pDb->zName)!=pDbname->n ) continue;
+ if( sqlite3StrNICmp(pDb->zName, pDbname->z, pDbname->n)==0 ) break;
+ }
+ if( i>=db->nDb ){
+ sqlite3ErrorMsg(pParse, "no such database: %T", pDbname);
+ return;
+ }
+ if( i<2 ){
+ sqlite3ErrorMsg(pParse, "cannot detach database %T", pDbname);
+ return;
+ }
+ if( !db->autoCommit ){
+ sqlite3ErrorMsg(pParse, "cannot DETACH database within transaction");
+ pParse->rc = SQLITE_ERROR;
+ return;
+ }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ if( sqlite3AuthCheck(pParse,SQLITE_DETACH,db->aDb[i].zName,0,0)!=SQLITE_OK ){
+ return;
+ }
+#endif /* SQLITE_OMIT_AUTHORIZATION */
+ sqlite3BtreeClose(pDb->pBt);
+ pDb->pBt = 0;
+ sqlite3ResetInternalSchema(db, 0);
+}
+
+/*
+** Initialize a DbFixer structure. This routine must be called prior
+** to passing the structure to one of the sqliteFixAAAA() routines below.
+**
+** The return value indicates whether or not fixation is required. TRUE
+** means we do need to fix the database references, FALSE means we do not.
+*/
+int sqlite3FixInit(
+ DbFixer *pFix, /* The fixer to be initialized */
+ Parse *pParse, /* Error messages will be written here */
+ int iDb, /* This is the database that must be used */
+ const char *zType, /* "view", "trigger", or "index" */
+ const Token *pName /* Name of the view, trigger, or index */
+){
+ sqlite3 *db;
+
+ if( iDb<0 || iDb==1 ) return 0;
+ db = pParse->db;
+ assert( db->nDb>iDb );
+ pFix->pParse = pParse;
+ pFix->zDb = db->aDb[iDb].zName;
+ pFix->zType = zType;
+ pFix->pName = pName;
+ return 1;
+}
+
+/*
+** The following set of routines walk through the parse tree and assign
+** a specific database to all table references where the database name
+** was left unspecified in the original SQL statement. The pFix structure
+** must have been initialized by a prior call to sqlite3FixInit().
+**
+** These routines are used to make sure that an index, trigger, or
+** view in one database does not refer to objects in a different database.
+** (Exception: indices, triggers, and views in the TEMP database are
+** allowed to refer to anything.) If a reference is explicitly made
+** to an object in a different database, an error message is added to
+** pParse->zErrMsg and these routines return non-zero. If everything
+** checks out, these routines return 0.
+*/
+int sqlite3FixSrcList(
+ DbFixer *pFix, /* Context of the fixation */
+ SrcList *pList /* The Source list to check and modify */
+){
+ int i;
+ const char *zDb;
+ struct SrcList_item *pItem;
+
+ if( pList==0 ) return 0;
+ zDb = pFix->zDb;
+ for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
+ if( pItem->zDatabase==0 ){
+ pItem->zDatabase = sqliteStrDup(zDb);
+ }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return 1;
+ }
+ if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
+ if( sqlite3FixExpr(pFix, pItem->pOn) ) return 1;
+ }
+ return 0;
+}
+int sqlite3FixSelect(
+ DbFixer *pFix, /* Context of the fixation */
+ Select *pSelect /* The SELECT statement to be fixed to one database */
+){
+ while( pSelect ){
+ if( sqlite3FixExprList(pFix, pSelect->pEList) ){
+ return 1;
+ }
+ if( sqlite3FixSrcList(pFix, pSelect->pSrc) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
+ return 1;
+ }
+ pSelect = pSelect->pPrior;
+ }
+ return 0;
+}
+int sqlite3FixExpr(
+ DbFixer *pFix, /* Context of the fixation */
+ Expr *pExpr /* The expression to be fixed to one database */
+){
+ while( pExpr ){
+ if( sqlite3FixSelect(pFix, pExpr->pSelect) ){
+ return 1;
+ }
+ if( sqlite3FixExprList(pFix, pExpr->pList) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pExpr->pRight) ){
+ return 1;
+ }
+ pExpr = pExpr->pLeft;
+ }
+ return 0;
+}
+int sqlite3FixExprList(
+ DbFixer *pFix, /* Context of the fixation */
+ ExprList *pList /* The expression to be fixed to one database */
+){
+ int i;
+ struct ExprList_item *pItem;
+ if( pList==0 ) return 0;
+ for(i=0, pItem=pList->a; i<pList->nExpr; i++, pItem++){
+ if( sqlite3FixExpr(pFix, pItem->pExpr) ){
+ return 1;
+ }
+ }
+ return 0;
+}
+int sqlite3FixTriggerStep(
+ DbFixer *pFix, /* Context of the fixation */
+ TriggerStep *pStep /* The trigger step be fixed to one database */
+){
+ while( pStep ){
+ if( sqlite3FixSelect(pFix, pStep->pSelect) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pStep->pWhere) ){
+ return 1;
+ }
+ if( sqlite3FixExprList(pFix, pStep->pExprList) ){
+ return 1;
+ }
+ pStep = pStep->pNext;
+ }
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/auth.c b/kopete/plugins/statistics/sqlite/auth.c
new file mode 100644
index 00000000..b251eacf
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/auth.c
@@ -0,0 +1,223 @@
+/*
+** 2003 January 11
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used to implement the sqlite3_set_authorizer()
+** API. This facility is an optional feature of the library. Embedded
+** systems that do not need this facility may omit it by recompiling
+** the library with -DSQLITE_OMIT_AUTHORIZATION=1
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** All of the code in this file may be omitted by defining a single
+** macro.
+*/
+#ifndef SQLITE_OMIT_AUTHORIZATION
+
+/*
+** Set or clear the access authorization function.
+**
+** The access authorization function is be called during the compilation
+** phase to verify that the user has read and/or write access permission on
+** various fields of the database. The first argument to the auth function
+** is a copy of the 3rd argument to this routine. The second argument
+** to the auth function is one of these constants:
+**
+** SQLITE_CREATE_INDEX
+** SQLITE_CREATE_TABLE
+** SQLITE_CREATE_TEMP_INDEX
+** SQLITE_CREATE_TEMP_TABLE
+** SQLITE_CREATE_TEMP_TRIGGER
+** SQLITE_CREATE_TEMP_VIEW
+** SQLITE_CREATE_TRIGGER
+** SQLITE_CREATE_VIEW
+** SQLITE_DELETE
+** SQLITE_DROP_INDEX
+** SQLITE_DROP_TABLE
+** SQLITE_DROP_TEMP_INDEX
+** SQLITE_DROP_TEMP_TABLE
+** SQLITE_DROP_TEMP_TRIGGER
+** SQLITE_DROP_TEMP_VIEW
+** SQLITE_DROP_TRIGGER
+** SQLITE_DROP_VIEW
+** SQLITE_INSERT
+** SQLITE_PRAGMA
+** SQLITE_READ
+** SQLITE_SELECT
+** SQLITE_TRANSACTION
+** SQLITE_UPDATE
+**
+** The third and fourth arguments to the auth function are the name of
+** the table and the column that are being accessed. The auth function
+** should return either SQLITE_OK, SQLITE_DENY, or SQLITE_IGNORE. If
+** SQLITE_OK is returned, it means that access is allowed. SQLITE_DENY
+** means that the SQL statement will never-run - the sqlite3_exec() call
+** will return with an error. SQLITE_IGNORE means that the SQL statement
+** should run but attempts to read the specified column will return NULL
+** and attempts to write the column will be ignored.
+**
+** Setting the auth function to NULL disables this hook. The default
+** setting of the auth function is NULL.
+*/
+int sqlite3_set_authorizer(
+ sqlite3 *db,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pArg
+){
+ db->xAuth = xAuth;
+ db->pAuthArg = pArg;
+ return SQLITE_OK;
+}
+
+/*
+** Write an error message into pParse->zErrMsg that explains that the
+** user-supplied authorization function returned an illegal value.
+*/
+static void sqliteAuthBadReturnCode(Parse *pParse, int rc){
+ sqlite3ErrorMsg(pParse, "illegal return value (%d) from the "
+ "authorization function - should be SQLITE_OK, SQLITE_IGNORE, "
+ "or SQLITE_DENY", rc);
+ pParse->rc = SQLITE_ERROR;
+}
+
+/*
+** The pExpr should be a TK_COLUMN expression. The table referred to
+** is in pTabList or else it is the NEW or OLD table of a trigger.
+** Check to see if it is OK to read this particular column.
+**
+** If the auth function returns SQLITE_IGNORE, change the TK_COLUMN
+** instruction into a TK_NULL. If the auth function returns SQLITE_DENY,
+** then generate an error.
+*/
+void sqlite3AuthRead(
+ Parse *pParse, /* The parser context */
+ Expr *pExpr, /* The expression to check authorization on */
+ SrcList *pTabList /* All table that pExpr might refer to */
+){
+ sqlite3 *db = pParse->db;
+ int rc;
+ Table *pTab; /* The table being read */
+ const char *zCol; /* Name of the column of the table */
+ int iSrc; /* Index in pTabList->a[] of table being read */
+ const char *zDBase; /* Name of database being accessed */
+ TriggerStack *pStack; /* The stack of current triggers */
+
+ if( db->xAuth==0 ) return;
+ assert( pExpr->op==TK_COLUMN );
+ for(iSrc=0; iSrc<pTabList->nSrc; iSrc++){
+ if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break;
+ }
+ if( iSrc>=0 && iSrc<pTabList->nSrc ){
+ pTab = pTabList->a[iSrc].pTab;
+ }else if( (pStack = pParse->trigStack)!=0 ){
+ /* This must be an attempt to read the NEW or OLD pseudo-tables
+ ** of a trigger.
+ */
+ assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx );
+ pTab = pStack->pTab;
+ }else{
+ return;
+ }
+ if( pTab==0 ) return;
+ if( pExpr->iColumn>=0 ){
+ assert( pExpr->iColumn<pTab->nCol );
+ zCol = pTab->aCol[pExpr->iColumn].zName;
+ }else if( pTab->iPKey>=0 ){
+ assert( pTab->iPKey<pTab->nCol );
+ zCol = pTab->aCol[pTab->iPKey].zName;
+ }else{
+ zCol = "ROWID";
+ }
+ assert( pExpr->iDb<db->nDb );
+ zDBase = db->aDb[pExpr->iDb].zName;
+ rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase,
+ pParse->zAuthContext);
+ if( rc==SQLITE_IGNORE ){
+ pExpr->op = TK_NULL;
+ }else if( rc==SQLITE_DENY ){
+ if( db->nDb>2 || pExpr->iDb!=0 ){
+ sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",
+ zDBase, pTab->zName, zCol);
+ }else{
+ sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol);
+ }
+ pParse->rc = SQLITE_AUTH;
+ }else if( rc!=SQLITE_OK ){
+ sqliteAuthBadReturnCode(pParse, rc);
+ }
+}
+
+/*
+** Do an authorization check using the code and arguments given. Return
+** either SQLITE_OK (zero) or SQLITE_IGNORE or SQLITE_DENY. If SQLITE_DENY
+** is returned, then the error count and error message in pParse are
+** modified appropriately.
+*/
+int sqlite3AuthCheck(
+ Parse *pParse,
+ int code,
+ const char *zArg1,
+ const char *zArg2,
+ const char *zArg3
+){
+ sqlite3 *db = pParse->db;
+ int rc;
+
+ /* Don't do any authorization checks if the database is initialising. */
+ if( db->init.busy ){
+ return SQLITE_OK;
+ }
+
+ if( db->xAuth==0 ){
+ return SQLITE_OK;
+ }
+ rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
+ if( rc==SQLITE_DENY ){
+ sqlite3ErrorMsg(pParse, "not authorized");
+ pParse->rc = SQLITE_AUTH;
+ }else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
+ rc = SQLITE_DENY;
+ sqliteAuthBadReturnCode(pParse, rc);
+ }
+ return rc;
+}
+
+/*
+** Push an authorization context. After this routine is called, the
+** zArg3 argument to authorization callbacks will be zContext until
+** popped. Or if pParse==0, this routine is a no-op.
+*/
+void sqlite3AuthContextPush(
+ Parse *pParse,
+ AuthContext *pContext,
+ const char *zContext
+){
+ pContext->pParse = pParse;
+ if( pParse ){
+ pContext->zAuthContext = pParse->zAuthContext;
+ pParse->zAuthContext = zContext;
+ }
+}
+
+/*
+** Pop an authorization context that was previously pushed
+** by sqlite3AuthContextPush
+*/
+void sqlite3AuthContextPop(AuthContext *pContext){
+ if( pContext->pParse ){
+ pContext->pParse->zAuthContext = pContext->zAuthContext;
+ pContext->pParse = 0;
+ }
+}
+
+#endif /* SQLITE_OMIT_AUTHORIZATION */
diff --git a/kopete/plugins/statistics/sqlite/btree.c b/kopete/plugins/statistics/sqlite/btree.c
new file mode 100644
index 00000000..fe8754e0
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/btree.c
@@ -0,0 +1,4462 @@
+/*
+** 2004 April 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** $Id$
+**
+** This file implements a external (disk-based) database using BTrees.
+** For a detailed discussion of BTrees, refer to
+**
+** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
+** "Sorting And Searching", pages 473-480. Addison-Wesley
+** Publishing Company, Reading, Massachusetts.
+**
+** The basic idea is that each page of the file contains N database
+** entries and N+1 pointers to subpages.
+**
+** ----------------------------------------------------------------
+** | Ptr(0) | Key(0) | Ptr(1) | Key(1) | ... | Key(N) | Ptr(N+1) |
+** ----------------------------------------------------------------
+**
+** All of the keys on the page that Ptr(0) points to have values less
+** than Key(0). All of the keys on page Ptr(1) and its subpages have
+** values greater than Key(0) and less than Key(1). All of the keys
+** on Ptr(N+1) and its subpages have values greater than Key(N). And
+** so forth.
+**
+** Finding a particular key requires reading O(log(M)) pages from the
+** disk where M is the number of entries in the tree.
+**
+** In this implementation, a single file can hold one or more separate
+** BTrees. Each BTree is identified by the index of its root page. The
+** key and data for any entry are combined to form the "payload". A
+** fixed amount of payload can be carried directly on the database
+** page. If the payload is larger than the preset amount then surplus
+** bytes are stored on overflow pages. The payload for an entry
+** and the preceding pointer are combined to form a "Cell". Each
+** page has a small header which contains the Ptr(N+1) pointer and other
+** information such as the size of key and data.
+**
+** FORMAT DETAILS
+**
+** The file is divided into pages. The first page is called page 1,
+** the second is page 2, and so forth. A page number of zero indicates
+** "no such page". The page size can be anything between 512 and 65536.
+** Each page can be either a btree page, a freelist page or an overflow
+** page.
+**
+** The first page is always a btree page. The first 100 bytes of the first
+** page contain a special header (the "file header") that describes the file.
+** The format of the file header is as follows:
+**
+** OFFSET SIZE DESCRIPTION
+** 0 16 Header string: "SQLite format 3\000"
+** 16 2 Page size in bytes.
+** 18 1 File format write version
+** 19 1 File format read version
+** 20 1 Bytes of unused space at the end of each page
+** 21 1 Max embedded payload fraction
+** 22 1 Min embedded payload fraction
+** 23 1 Min leaf payload fraction
+** 24 4 File change counter
+** 28 4 Reserved for future use
+** 32 4 First freelist page
+** 36 4 Number of freelist pages in the file
+** 40 60 15 4-byte meta values passed to higher layers
+**
+** All of the integer values are big-endian (most significant byte first).
+**
+** The file change counter is incremented when the database is changed more
+** than once within the same second. This counter, together with the
+** modification time of the file, allows other processes to know
+** when the file has changed and thus when they need to flush their
+** cache.
+**
+** The max embedded payload fraction is the amount of the total usable
+** space in a page that can be consumed by a single cell for standard
+** B-tree (non-LEAFDATA) tables. A value of 255 means 100%. The default
+** is to limit the maximum cell size so that at least 4 cells will fit
+** on one page. Thus the default max embedded payload fraction is 64.
+**
+** If the payload for a cell is larger than the max payload, then extra
+** payload is spilled to overflow pages. Once an overflow page is allocated,
+** as many bytes as possible are moved into the overflow pages without letting
+** the cell size drop below the min embedded payload fraction.
+**
+** The min leaf payload fraction is like the min embedded payload fraction
+** except that it applies to leaf nodes in a LEAFDATA tree. The maximum
+** payload fraction for a LEAFDATA tree is always 100% (or 255) and it
+** not specified in the header.
+**
+** Each btree pages is divided into three sections: The header, the
+** cell pointer array, and the cell area area. Page 1 also has a 100-byte
+** file header that occurs before the page header.
+**
+** |----------------|
+** | file header | 100 bytes. Page 1 only.
+** |----------------|
+** | page header | 8 bytes for leaves. 12 bytes for interior nodes
+** |----------------|
+** | cell pointer | | 2 bytes per cell. Sorted order.
+** | array | | Grows downward
+** | | v
+** |----------------|
+** | unallocated |
+** | space |
+** |----------------| ^ Grows upwards
+** | cell content | | Arbitrary order interspersed with freeblocks.
+** | area | | and free space fragments.
+** |----------------|
+**
+** The page headers looks like this:
+**
+** OFFSET SIZE DESCRIPTION
+** 0 1 Flags. 1: intkey, 2: zerodata, 4: leafdata, 8: leaf
+** 1 2 byte offset to the first freeblock
+** 3 2 number of cells on this page
+** 5 2 first byte of the cell content area
+** 7 1 number of fragmented free bytes
+** 8 4 Right child (the Ptr(N+1) value). Omitted on leaves.
+**
+** The flags define the format of this btree page. The leaf flag means that
+** this page has no children. The zerodata flag means that this page carries
+** only keys and no data. The intkey flag means that the key is a integer
+** which is stored in the key size entry of the cell header rather than in
+** the payload area.
+**
+** The cell pointer array begins on the first byte after the page header.
+** The cell pointer array contains zero or more 2-byte numbers which are
+** offsets from the beginning of the page to the cell content in the cell
+** content area. The cell pointers occur in sorted order. The system strives
+** to keep free space after the last cell pointer so that new cells can
+** be easily added without having to defragment the page.
+**
+** Cell content is stored at the very end of the page and grows toward the
+** beginning of the page.
+**
+** Unused space within the cell content area is collected into a linked list of
+** freeblocks. Each freeblock is at least 4 bytes in size. The byte offset
+** to the first freeblock is given in the header. Freeblocks occur in
+** increasing order. Because a freeblock must be at least 4 bytes in size,
+** any group of 3 or fewer unused bytes in the cell content area cannot
+** exist on the freeblock chain. A group of 3 or fewer free bytes is called
+** a fragment. The total number of bytes in all fragments is recorded.
+** in the page header at offset 7.
+**
+** SIZE DESCRIPTION
+** 2 Byte offset of the next freeblock
+** 2 Bytes in this freeblock
+**
+** Cells are of variable length. Cells are stored in the cell content area at
+** the end of the page. Pointers to the cells are in the cell pointer array
+** that immediately follows the page header. Cells is not necessarily
+** contiguous or in order, but cell pointers are contiguous and in order.
+**
+** Cell content makes use of variable length integers. A variable
+** length integer is 1 to 9 bytes where the lower 7 bits of each
+** byte are used. The integer consists of all bytes that have bit 8 set and
+** the first byte with bit 8 clear. The most significant byte of the integer
+** appears first. A variable-length integer may not be more than 9 bytes long.
+** As a special case, all 8 bytes of the 9th byte are used as data. This
+** allows a 64-bit integer to be encoded in 9 bytes.
+**
+** 0x00 becomes 0x00000000
+** 0x7f becomes 0x0000007f
+** 0x81 0x00 becomes 0x00000080
+** 0x82 0x00 becomes 0x00000100
+** 0x80 0x7f becomes 0x0000007f
+** 0x8a 0x91 0xd1 0xac 0x78 becomes 0x12345678
+** 0x81 0x81 0x81 0x81 0x01 becomes 0x10204081
+**
+** Variable length integers are used for rowids and to hold the number of
+** bytes of key and data in a btree cell.
+**
+** The content of a cell looks like this:
+**
+** SIZE DESCRIPTION
+** 4 Page number of the left child. Omitted if leaf flag is set.
+** var Number of bytes of data. Omitted if the zerodata flag is set.
+** var Number of bytes of key. Or the key itself if intkey flag is set.
+** * Payload
+** 4 First page of the overflow chain. Omitted if no overflow
+**
+** Overflow pages form a linked list. Each page except the last is completely
+** filled with data (pagesize - 4 bytes). The last page can have as little
+** as 1 byte of data.
+**
+** SIZE DESCRIPTION
+** 4 Page number of next overflow page
+** * Data
+**
+** Freelist pages come in two subtypes: trunk pages and leaf pages. The
+** file header points to first in a linked list of trunk page. Each trunk
+** page points to multiple leaf pages. The content of a leaf page is
+** unspecified. A trunk page looks like this:
+**
+** SIZE DESCRIPTION
+** 4 Page number of next trunk page
+** 4 Number of leaf pointers on this page
+** * zero or more pages numbers of leaves
+*/
+#include "sqliteInt.h"
+#include "pager.h"
+#include "btree.h"
+#include "os.h"
+#include <assert.h>
+
+
+/* The following value is the maximum cell size assuming a maximum page
+** size give above.
+*/
+#define MX_CELL_SIZE(pBt) (pBt->pageSize-8)
+
+/* The maximum number of cells on a single page of the database. This
+** assumes a minimum cell size of 3 bytes. Such small cells will be
+** exceedingly rare, but they are possible.
+*/
+#define MX_CELL(pBt) ((pBt->pageSize-8)/3)
+
+/* Forward declarations */
+typedef struct MemPage MemPage;
+
+/*
+** This is a magic string that appears at the beginning of every
+** SQLite database in order to identify the file as a real database.
+** 123456789 123456 */
+static const char zMagicHeader[] = "SQLite format 3";
+
+/*
+** Page type flags. An ORed combination of these flags appear as the
+** first byte of every BTree page.
+*/
+#define PTF_INTKEY 0x01
+#define PTF_ZERODATA 0x02
+#define PTF_LEAFDATA 0x04
+#define PTF_LEAF 0x08
+
+/*
+** As each page of the file is loaded into memory, an instance of the following
+** structure is appended and initialized to zero. This structure stores
+** information about the page that is decoded from the raw file page.
+**
+** The pParent field points back to the parent page. This allows us to
+** walk up the BTree from any leaf to the root. Care must be taken to
+** unref() the parent page pointer when this page is no longer referenced.
+** The pageDestructor() routine handles that chore.
+*/
+struct MemPage {
+ u8 isInit; /* True if previously initialized. MUST BE FIRST! */
+ u8 idxShift; /* True if Cell indices have changed */
+ u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
+ u8 intKey; /* True if intkey flag is set */
+ u8 leaf; /* True if leaf flag is set */
+ u8 zeroData; /* True if table stores keys only */
+ u8 leafData; /* True if tables stores data on leaves only */
+ u8 hasData; /* True if this page stores data */
+ u8 hdrOffset; /* 100 for page 1. 0 otherwise */
+ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
+ u16 maxLocal; /* Copy of Btree.maxLocal or Btree.maxLeaf */
+ u16 minLocal; /* Copy of Btree.minLocal or Btree.minLeaf */
+ u16 cellOffset; /* Index in aData of first cell pointer */
+ u16 idxParent; /* Index in parent of this node */
+ u16 nFree; /* Number of free bytes on the page */
+ u16 nCell; /* Number of cells on this page, local and ovfl */
+ struct _OvflCell { /* Cells that will not fit on aData[] */
+ u8 *pCell; /* Pointers to the body of the overflow cell */
+ u16 idx; /* Insert this cell before idx-th non-overflow cell */
+ } aOvfl[5];
+ struct Btree *pBt; /* Pointer back to BTree structure */
+ u8 *aData; /* Pointer back to the start of the page */
+ Pgno pgno; /* Page number for this page */
+ MemPage *pParent; /* The parent of this page. NULL for root */
+};
+
+/*
+** The in-memory image of a disk page has the auxiliary information appended
+** to the end. EXTRA_SIZE is the number of bytes of space needed to hold
+** that extra information.
+*/
+#define EXTRA_SIZE sizeof(MemPage)
+
+/*
+** Everything we need to know about an open database
+*/
+struct Btree {
+ Pager *pPager; /* The page cache */
+ BtCursor *pCursor; /* A list of all open cursors */
+ MemPage *pPage1; /* First page of the database */
+ u8 inTrans; /* True if a transaction is in progress */
+ u8 inStmt; /* True if we are in a statement subtransaction */
+ u8 readOnly; /* True if the underlying file is readonly */
+ u8 maxEmbedFrac; /* Maximum payload as % of total page size */
+ u8 minEmbedFrac; /* Minimum payload as % of total page size */
+ u8 minLeafFrac; /* Minimum leaf payload as % of total page size */
+ u8 pageSizeFixed; /* True if the page size can no longer be changed */
+ u16 pageSize; /* Total number of bytes on a page */
+ u16 usableSize; /* Number of usable bytes on each page */
+ int maxLocal; /* Maximum local payload in non-LEAFDATA tables */
+ int minLocal; /* Minimum local payload in non-LEAFDATA tables */
+ int maxLeaf; /* Maximum local payload in a LEAFDATA table */
+ int minLeaf; /* Minimum local payload in a LEAFDATA table */
+};
+typedef Btree Bt;
+
+/*
+** Btree.inTrans may take one of the following values.
+*/
+#define TRANS_NONE 0
+#define TRANS_READ 1
+#define TRANS_WRITE 2
+
+/*
+** An instance of the following structure is used to hold information
+** about a cell. The parseCellPtr() function fills in this structure
+** based on information extract from the raw disk page.
+*/
+typedef struct CellInfo CellInfo;
+struct CellInfo {
+ u8 *pCell; /* Pointer to the start of cell content */
+ i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
+ u32 nData; /* Number of bytes of data */
+ u16 nHeader; /* Size of the cell content header in bytes */
+ u16 nLocal; /* Amount of payload held locally */
+ u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
+ u16 nSize; /* Size of the cell content on the main b-tree page */
+};
+
+/*
+** A cursor is a pointer to a particular entry in the BTree.
+** The entry is identified by its MemPage and the index in
+** MemPage.aCell[] of the entry.
+*/
+struct BtCursor {
+ Btree *pBt; /* The Btree to which this cursor belongs */
+ BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
+ int (*xCompare)(void*,int,const void*,int,const void*); /* Key comp func */
+ void *pArg; /* First arg to xCompare() */
+ Pgno pgnoRoot; /* The root page of this tree */
+ MemPage *pPage; /* Page that contains the entry */
+ int idx; /* Index of the entry in pPage->aCell[] */
+ CellInfo info; /* A parse of the cell we are pointing at */
+ u8 wrFlag; /* True if writable */
+ u8 isValid; /* TRUE if points to a valid entry */
+ u8 status; /* Set to SQLITE_ABORT if cursors is invalidated */
+};
+
+/*
+** Forward declaration
+*/
+static int checkReadLocks(Btree*,Pgno,BtCursor*);
+
+
+/*
+** Read or write a two- and four-byte big-endian integer values.
+*/
+static u32 get2byte(unsigned char *p){
+ return (p[0]<<8) | p[1];
+}
+static u32 get4byte(unsigned char *p){
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+}
+static void put2byte(unsigned char *p, u32 v){
+ p[0] = v>>8;
+ p[1] = v;
+}
+static void put4byte(unsigned char *p, u32 v){
+ p[0] = v>>24;
+ p[1] = v>>16;
+ p[2] = v>>8;
+ p[3] = v;
+}
+
+/*
+** Routines to read and write variable-length integers. These used to
+** be defined locally, but now we use the varint routines in the util.c
+** file.
+*/
+#define getVarint sqlite3GetVarint
+#define getVarint32 sqlite3GetVarint32
+#define putVarint sqlite3PutVarint
+
+/*
+** Given a btree page and a cell index (0 means the first cell on
+** the page, 1 means the second cell, and so forth) return a pointer
+** to the cell content.
+**
+** This routine works only for pages that do not contain overflow cells.
+*/
+static u8 *findCell(MemPage *pPage, int iCell){
+ u8 *data = pPage->aData;
+ assert( iCell>=0 );
+ assert( iCell<get2byte(&data[pPage->hdrOffset+3]) );
+ return data + get2byte(&data[pPage->cellOffset+2*iCell]);
+}
+
+/*
+** This a more complex version of findCell() that works for
+** pages that do contain overflow cells. See insert
+*/
+static u8 *findOverflowCell(MemPage *pPage, int iCell){
+ int i;
+ for(i=pPage->nOverflow-1; i>=0; i--){
+ int k;
+ struct _OvflCell *pOvfl;
+ pOvfl = &pPage->aOvfl[i];
+ k = pOvfl->idx;
+ if( k<=iCell ){
+ if( k==iCell ){
+ return pOvfl->pCell;
+ }
+ iCell--;
+ }
+ }
+ return findCell(pPage, iCell);
+}
+
+/*
+** Parse a cell content block and fill in the CellInfo structure. There
+** are two versions of this function. parseCell() takes a cell index
+** as the second argument and parseCellPtr() takes a pointer to the
+** body of the cell as its second argument.
+*/
+static void parseCellPtr(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ int n; /* Number bytes in cell content header */
+ u32 nPayload; /* Number of bytes of cell payload */
+
+ pInfo->pCell = pCell;
+ assert( pPage->leaf==0 || pPage->leaf==1 );
+ n = pPage->childPtrSize;
+ assert( n==4-4*pPage->leaf );
+ if( pPage->hasData ){
+ n += getVarint32(&pCell[n], &nPayload);
+ }else{
+ nPayload = 0;
+ }
+ n += getVarint(&pCell[n], (u64 *)&pInfo->nKey);
+ pInfo->nHeader = n;
+ pInfo->nData = nPayload;
+ if( !pPage->intKey ){
+ nPayload += pInfo->nKey;
+ }
+ if( nPayload<=pPage->maxLocal ){
+ /* This is the (easy) common case where the entire payload fits
+ ** on the local page. No overflow is required.
+ */
+ int nSize; /* Total size of cell content in bytes */
+ pInfo->nLocal = nPayload;
+ pInfo->iOverflow = 0;
+ nSize = nPayload + n;
+ if( nSize<4 ){
+ nSize = 4; /* Minimum cell size is 4 */
+ }
+ pInfo->nSize = nSize;
+ }else{
+ /* If the payload will not fit completely on the local page, we have
+ ** to decide how much to store locally and how much to spill onto
+ ** overflow pages. The strategy is to minimize the amount of unused
+ ** space on overflow pages while keeping the amount of local storage
+ ** in between minLocal and maxLocal.
+ **
+ ** Warning: changing the way overflow payload is distributed in any
+ ** way will result in an incompatible file format.
+ */
+ int minLocal; /* Minimum amount of payload held locally */
+ int maxLocal; /* Maximum amount of payload held locally */
+ int surplus; /* Overflow payload available for local storage */
+
+ minLocal = pPage->minLocal;
+ maxLocal = pPage->maxLocal;
+ surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4);
+ if( surplus <= maxLocal ){
+ pInfo->nLocal = surplus;
+ }else{
+ pInfo->nLocal = minLocal;
+ }
+ pInfo->iOverflow = pInfo->nLocal + n;
+ pInfo->nSize = pInfo->iOverflow + 4;
+ }
+}
+static void parseCell(
+ MemPage *pPage, /* Page containing the cell */
+ int iCell, /* The cell index. First cell is 0 */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ parseCellPtr(pPage, findCell(pPage, iCell), pInfo);
+}
+
+/*
+** Compute the total number of bytes that a Cell needs in the cell
+** data area of the btree-page. The return number includes the cell
+** data header and the local payload, but not any overflow page or
+** the space used by the cell pointer.
+*/
+#ifndef NDEBUG
+static int cellSize(MemPage *pPage, int iCell){
+ CellInfo info;
+ parseCell(pPage, iCell, &info);
+ return info.nSize;
+}
+#endif
+static int cellSizePtr(MemPage *pPage, u8 *pCell){
+ CellInfo info;
+ parseCellPtr(pPage, pCell, &info);
+ return info.nSize;
+}
+
+/*
+** Do sanity checking on a page. Throw an exception if anything is
+** not right.
+**
+** This routine is used for internal error checking only. It is omitted
+** from most builds.
+*/
+#if defined(BTREE_DEBUG) && !defined(NDEBUG) && 0
+static void _pageIntegrity(MemPage *pPage){
+ int usableSize;
+ u8 *data;
+ int i, j, idx, c, pc, hdr, nFree;
+ int cellOffset;
+ int nCell, cellLimit;
+ u8 *used;
+
+ used = sqliteMallocRaw( pPage->pBt->pageSize );
+ if( used==0 ) return;
+ usableSize = pPage->pBt->usableSize;
+ assert( pPage->aData==&((unsigned char*)pPage)[-pPage->pBt->pageSize] );
+ hdr = pPage->hdrOffset;
+ assert( hdr==(pPage->pgno==1 ? 100 : 0) );
+ assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
+ c = pPage->aData[hdr];
+ if( pPage->isInit ){
+ assert( pPage->leaf == ((c & PTF_LEAF)!=0) );
+ assert( pPage->zeroData == ((c & PTF_ZERODATA)!=0) );
+ assert( pPage->leafData == ((c & PTF_LEAFDATA)!=0) );
+ assert( pPage->intKey == ((c & (PTF_INTKEY|PTF_LEAFDATA))!=0) );
+ assert( pPage->hasData ==
+ !(pPage->zeroData || (!pPage->leaf && pPage->leafData)) );
+ assert( pPage->cellOffset==pPage->hdrOffset+12-4*pPage->leaf );
+ assert( pPage->nCell = get2byte(&pPage->aData[hdr+3]) );
+ }
+ data = pPage->aData;
+ memset(used, 0, usableSize);
+ for(i=0; i<hdr+10-pPage->leaf*4; i++) used[i] = 1;
+ nFree = 0;
+ pc = get2byte(&data[hdr+1]);
+ while( pc ){
+ int size;
+ assert( pc>0 && pc<usableSize-4 );
+ size = get2byte(&data[pc+2]);
+ assert( pc+size<=usableSize );
+ nFree += size;
+ for(i=pc; i<pc+size; i++){
+ assert( used[i]==0 );
+ used[i] = 1;
+ }
+ pc = get2byte(&data[pc]);
+ }
+ idx = 0;
+ nCell = get2byte(&data[hdr+3]);
+ cellLimit = get2byte(&data[hdr+5]);
+ assert( pPage->isInit==0
+ || pPage->nFree==nFree+data[hdr+7]+cellLimit-(cellOffset+2*nCell) );
+ cellOffset = pPage->cellOffset;
+ for(i=0; i<nCell; i++){
+ int size;
+ pc = get2byte(&data[cellOffset+2*i]);
+ assert( pc>0 && pc<usableSize-4 );
+ size = cellSize(pPage, &data[pc]);
+ assert( pc+size<=usableSize );
+ for(j=pc; j<pc+size; j++){
+ assert( used[j]==0 );
+ used[j] = 1;
+ }
+ }
+ for(i=cellOffset+2*nCell; i<cellimit; i++){
+ assert( used[i]==0 );
+ used[i] = 1;
+ }
+ nFree = 0;
+ for(i=0; i<usableSize; i++){
+ assert( used[i]<=1 );
+ if( used[i]==0 ) nFree++;
+ }
+ assert( nFree==data[hdr+7] );
+ sqliteFree(used);
+}
+#define pageIntegrity(X) _pageIntegrity(X)
+#else
+# define pageIntegrity(X)
+#endif
+
+/*
+** Defragment the page given. All Cells are moved to the
+** beginning of the page and all free space is collected
+** into one big FreeBlk at the end of the page.
+*/
+static int defragmentPage(MemPage *pPage){
+ int i; /* Loop counter */
+ int pc; /* Address of a i-th cell */
+ int addr; /* Offset of first byte after cell pointer array */
+ int hdr; /* Offset to the page header */
+ int size; /* Size of a cell */
+ int usableSize; /* Number of usable bytes on a page */
+ int cellOffset; /* Offset to the cell pointer array */
+ int brk; /* Offset to the cell content area */
+ int nCell; /* Number of cells on the page */
+ unsigned char *data; /* The page data */
+ unsigned char *temp; /* Temp area for cell content */
+
+ assert( sqlite3pager_iswriteable(pPage->aData) );
+ assert( pPage->pBt!=0 );
+ assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
+ assert( pPage->nOverflow==0 );
+ temp = sqliteMalloc( pPage->pBt->pageSize );
+ if( temp==0 ) return SQLITE_NOMEM;
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
+ cellOffset = pPage->cellOffset;
+ nCell = pPage->nCell;
+ assert( nCell==get2byte(&data[hdr+3]) );
+ usableSize = pPage->pBt->usableSize;
+ brk = get2byte(&data[hdr+5]);
+ memcpy(&temp[brk], &data[brk], usableSize - brk);
+ brk = usableSize;
+ for(i=0; i<nCell; i++){
+ u8 *pAddr; /* The i-th cell pointer */
+ pAddr = &data[cellOffset + i*2];
+ pc = get2byte(pAddr);
+ assert( pc<pPage->pBt->usableSize );
+ size = cellSizePtr(pPage, &temp[pc]);
+ brk -= size;
+ memcpy(&data[brk], &temp[pc], size);
+ put2byte(pAddr, brk);
+ }
+ assert( brk>=cellOffset+2*nCell );
+ put2byte(&data[hdr+5], brk);
+ data[hdr+1] = 0;
+ data[hdr+2] = 0;
+ data[hdr+7] = 0;
+ addr = cellOffset+2*nCell;
+ memset(&data[addr], 0, brk-addr);
+ sqliteFree(temp);
+ return SQLITE_OK;
+}
+
+/*
+** Allocate nByte bytes of space on a page.
+**
+** Return the index into pPage->aData[] of the first byte of
+** the new allocation. Or return 0 if there is not enough free
+** space on the page to satisfy the allocation request.
+**
+** If the page contains nBytes of free space but does not contain
+** nBytes of contiguous free space, then this routine automatically
+** calls defragementPage() to consolidate all free space before
+** allocating the new chunk.
+*/
+static int allocateSpace(MemPage *pPage, int nByte){
+ int addr, pc, hdr;
+ int size;
+ int nFrag;
+ int top;
+ int nCell;
+ int cellOffset;
+ unsigned char *data;
+
+ data = pPage->aData;
+ assert( sqlite3pager_iswriteable(data) );
+ assert( pPage->pBt );
+ if( nByte<4 ) nByte = 4;
+ if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
+ pPage->nFree -= nByte;
+ hdr = pPage->hdrOffset;
+
+ nFrag = data[hdr+7];
+ if( nFrag<60 ){
+ /* Search the freelist looking for a slot big enough to satisfy the
+ ** space request. */
+ addr = hdr+1;
+ while( (pc = get2byte(&data[addr]))>0 ){
+ size = get2byte(&data[pc+2]);
+ if( size>=nByte ){
+ if( size<nByte+4 ){
+ memcpy(&data[addr], &data[pc], 2);
+ data[hdr+7] = nFrag + size - nByte;
+ return pc;
+ }else{
+ put2byte(&data[pc+2], size-nByte);
+ return pc + size - nByte;
+ }
+ }
+ addr = pc;
+ }
+ }
+
+ /* Allocate memory from the gap in between the cell pointer array
+ ** and the cell content area.
+ */
+ top = get2byte(&data[hdr+5]);
+ nCell = get2byte(&data[hdr+3]);
+ cellOffset = pPage->cellOffset;
+ if( nFrag>=60 || cellOffset + 2*nCell > top - nByte ){
+ if( defragmentPage(pPage) ) return 0;
+ top = get2byte(&data[hdr+5]);
+ }
+ top -= nByte;
+ assert( cellOffset + 2*nCell <= top );
+ put2byte(&data[hdr+5], top);
+ return top;
+}
+
+/*
+** Return a section of the pPage->aData to the freelist.
+** The first byte of the new free block is pPage->aDisk[start]
+** and the size of the block is "size" bytes.
+**
+** Most of the effort here is involved in coalesing adjacent
+** free blocks into a single big free block.
+*/
+static void freeSpace(MemPage *pPage, int start, int size){
+ int addr, pbegin, hdr;
+ unsigned char *data = pPage->aData;
+
+ assert( pPage->pBt!=0 );
+ assert( sqlite3pager_iswriteable(data) );
+ assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
+ assert( (start + size)<=pPage->pBt->usableSize );
+ if( size<4 ) size = 4;
+
+ /* Add the space back into the linked list of freeblocks */
+ hdr = pPage->hdrOffset;
+ addr = hdr + 1;
+ while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
+ assert( pbegin<=pPage->pBt->usableSize-4 );
+ assert( pbegin>addr );
+ addr = pbegin;
+ }
+ assert( pbegin<=pPage->pBt->usableSize-4 );
+ assert( pbegin>addr || pbegin==0 );
+ put2byte(&data[addr], start);
+ put2byte(&data[start], pbegin);
+ put2byte(&data[start+2], size);
+ pPage->nFree += size;
+
+ /* Coalesce adjacent free blocks */
+ addr = pPage->hdrOffset + 1;
+ while( (pbegin = get2byte(&data[addr]))>0 ){
+ int pnext, psize;
+ assert( pbegin>addr );
+ assert( pbegin<=pPage->pBt->usableSize-4 );
+ pnext = get2byte(&data[pbegin]);
+ psize = get2byte(&data[pbegin+2]);
+ if( pbegin + psize + 3 >= pnext && pnext>0 ){
+ int frag = pnext - (pbegin+psize);
+ assert( frag<=data[pPage->hdrOffset+7] );
+ data[pPage->hdrOffset+7] -= frag;
+ put2byte(&data[pbegin], get2byte(&data[pnext]));
+ put2byte(&data[pbegin+2], pnext+get2byte(&data[pnext+2])-pbegin);
+ }else{
+ addr = pbegin;
+ }
+ }
+
+ /* If the cell content area begins with a freeblock, remove it. */
+ if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){
+ int top;
+ pbegin = get2byte(&data[hdr+1]);
+ memcpy(&data[hdr+1], &data[pbegin], 2);
+ top = get2byte(&data[hdr+5]);
+ put2byte(&data[hdr+5], top + get2byte(&data[pbegin+2]));
+ }
+}
+
+/*
+** Decode the flags byte (the first byte of the header) for a page
+** and initialize fields of the MemPage structure accordingly.
+*/
+static void decodeFlags(MemPage *pPage, int flagByte){
+ Btree *pBt; /* A copy of pPage->pBt */
+
+ assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
+ pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0;
+ pPage->zeroData = (flagByte & PTF_ZERODATA)!=0;
+ pPage->leaf = (flagByte & PTF_LEAF)!=0;
+ pPage->childPtrSize = 4*(pPage->leaf==0);
+ pBt = pPage->pBt;
+ if( flagByte & PTF_LEAFDATA ){
+ pPage->leafData = 1;
+ pPage->maxLocal = pBt->maxLeaf;
+ pPage->minLocal = pBt->minLeaf;
+ }else{
+ pPage->leafData = 0;
+ pPage->maxLocal = pBt->maxLocal;
+ pPage->minLocal = pBt->minLocal;
+ }
+ pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData));
+}
+
+/*
+** Initialize the auxiliary information for a disk block.
+**
+** The pParent parameter must be a pointer to the MemPage which
+** is the parent of the page being initialized. The root of a
+** BTree has no parent and so for that page, pParent==NULL.
+**
+** Return SQLITE_OK on success. If we see that the page does
+** not contain a well-formed database page, then return
+** SQLITE_CORRUPT. Note that a return of SQLITE_OK does not
+** guarantee that the page is well-formed. It only shows that
+** we failed to detect any corruption.
+*/
+static int initPage(
+ MemPage *pPage, /* The page to be initialized */
+ MemPage *pParent /* The parent. Might be NULL */
+){
+ int pc; /* Address of a freeblock within pPage->aData[] */
+ int i; /* Loop counter */
+ int hdr; /* Offset to beginning of page header */
+ u8 *data; /* Equal to pPage->aData */
+ Btree *pBt; /* The main btree structure */
+ int usableSize; /* Amount of usable space on each page */
+ int cellOffset; /* Offset from start of page to first cell pointer */
+ int nFree; /* Number of unused bytes on the page */
+ int top; /* First byte of the cell content area */
+
+ pBt = pPage->pBt;
+ assert( pBt!=0 );
+ assert( pParent==0 || pParent->pBt==pBt );
+ assert( pPage->pgno==sqlite3pager_pagenumber(pPage->aData) );
+ assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
+ if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
+ /* The parent page should never change unless the file is corrupt */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ if( pPage->isInit ) return SQLITE_OK;
+ if( pPage->pParent==0 && pParent!=0 ){
+ pPage->pParent = pParent;
+ sqlite3pager_ref(pParent->aData);
+ }
+ hdr = pPage->hdrOffset;
+ data = pPage->aData;
+ decodeFlags(pPage, data[hdr]);
+ pPage->nOverflow = 0;
+ pPage->idxShift = 0;
+ usableSize = pBt->usableSize;
+ pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
+ top = get2byte(&data[hdr+5]);
+ pPage->nCell = get2byte(&data[hdr+3]);
+ if( pPage->nCell>MX_CELL(pBt) ){
+ /* To many cells for a single page. The page must be corrupt */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ if( pPage->nCell==0 && pParent!=0 && pParent->pgno!=1 ){
+ /* All pages must have at least one cell, except for root pages */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+
+ /* Compute the total free space on the page */
+ pc = get2byte(&data[hdr+1]);
+ nFree = data[hdr+7] + top - (cellOffset + 2*pPage->nCell);
+ i = 0;
+ while( pc>0 ){
+ int next, size;
+ if( pc>usableSize-4 ){
+ /* Free block is off the page */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ if( i++>SQLITE_MAX_PAGE_SIZE/4 ){
+ /* The free block list forms an infinite loop */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ next = get2byte(&data[pc]);
+ size = get2byte(&data[pc+2]);
+ if( next>0 && next<=pc+size+3 ){
+ /* Free blocks must be in accending order */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ nFree += size;
+ pc = next;
+ }
+ pPage->nFree = nFree;
+ if( nFree>=usableSize ){
+ /* Free space cannot exceed total page size */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+
+ pPage->isInit = 1;
+ pageIntegrity(pPage);
+ return SQLITE_OK;
+}
+
+/*
+** Set up a raw page so that it looks like a database page holding
+** no entries.
+*/
+static void zeroPage(MemPage *pPage, int flags){
+ unsigned char *data = pPage->aData;
+ Btree *pBt = pPage->pBt;
+ int hdr = pPage->hdrOffset;
+ int first;
+
+ assert( sqlite3pager_pagenumber(data)==pPage->pgno );
+ assert( &data[pBt->pageSize] == (unsigned char*)pPage );
+ assert( sqlite3pager_iswriteable(data) );
+ memset(&data[hdr], 0, pBt->usableSize - hdr);
+ data[hdr] = flags;
+ first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
+ memset(&data[hdr+1], 0, 4);
+ data[hdr+7] = 0;
+ put2byte(&data[hdr+5], pBt->usableSize);
+ pPage->nFree = pBt->usableSize - first;
+ decodeFlags(pPage, flags);
+ pPage->hdrOffset = hdr;
+ pPage->cellOffset = first;
+ pPage->nOverflow = 0;
+ pPage->idxShift = 0;
+ pPage->nCell = 0;
+ pPage->isInit = 1;
+ pageIntegrity(pPage);
+}
+
+/*
+** Get a page from the pager. Initialize the MemPage.pBt and
+** MemPage.aData elements if needed.
+*/
+static int getPage(Btree *pBt, Pgno pgno, MemPage **ppPage){
+ int rc;
+ unsigned char *aData;
+ MemPage *pPage;
+ rc = sqlite3pager_get(pBt->pPager, pgno, (void**)&aData);
+ if( rc ) return rc;
+ pPage = (MemPage*)&aData[pBt->pageSize];
+ pPage->aData = aData;
+ pPage->pBt = pBt;
+ pPage->pgno = pgno;
+ pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
+ *ppPage = pPage;
+ return SQLITE_OK;
+}
+
+/*
+** Get a page from the pager and initialize it. This routine
+** is just a convenience wrapper around separate calls to
+** getPage() and initPage().
+*/
+static int getAndInitPage(
+ Btree *pBt, /* The database file */
+ Pgno pgno, /* Number of the page to get */
+ MemPage **ppPage, /* Write the page pointer here */
+ MemPage *pParent /* Parent of the page */
+){
+ int rc;
+ if( pgno==0 ){
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ rc = getPage(pBt, pgno, ppPage);
+ if( rc==SQLITE_OK && (*ppPage)->isInit==0 ){
+ rc = initPage(*ppPage, pParent);
+ }
+ return rc;
+}
+
+/*
+** Release a MemPage. This should be called once for each prior
+** call to getPage.
+*/
+static void releasePage(MemPage *pPage){
+ if( pPage ){
+ assert( pPage->aData );
+ assert( pPage->pBt );
+ assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );
+ sqlite3pager_unref(pPage->aData);
+ }
+}
+
+/*
+** This routine is called when the reference count for a page
+** reaches zero. We need to unref the pParent pointer when that
+** happens.
+*/
+static void pageDestructor(void *pData, int pageSize){
+ MemPage *pPage = (MemPage*)&((char*)pData)[pageSize];
+ if( pPage->pParent ){
+ MemPage *pParent = pPage->pParent;
+ pPage->pParent = 0;
+ releasePage(pParent);
+ }
+ pPage->isInit = 0;
+}
+
+/*
+** During a rollback, when the pager reloads information into the cache
+** so that the cache is restored to its original state at the start of
+** the transaction, for each page restored this routine is called.
+**
+** This routine needs to reset the extra data section at the end of the
+** page to agree with the restored data.
+*/
+static void pageReinit(void *pData, int pageSize){
+ MemPage *pPage = (MemPage*)&((char*)pData)[pageSize];
+ if( pPage->isInit ){
+ pPage->isInit = 0;
+ initPage(pPage, pPage->pParent);
+ }
+}
+
+/*
+** Open a database file.
+**
+** zFilename is the name of the database file. If zFilename is NULL
+** a new database with a random name is created. This randomly named
+** database file will be deleted when sqlite3BtreeClose() is called.
+*/
+int sqlite3BtreeOpen(
+ const char *zFilename, /* Name of the file containing the BTree database */
+ Btree **ppBtree, /* Pointer to new Btree object written here */
+ int flags /* Options */
+){
+ Btree *pBt;
+ int rc;
+ int nReserve;
+ unsigned char zDbHeader[100];
+
+ /*
+ ** The following asserts make sure that structures used by the btree are
+ ** the right size. This is to guard against size changes that result
+ ** when compiling on a different architecture.
+ */
+ assert( sizeof(i64)==8 );
+ assert( sizeof(u64)==8 );
+ assert( sizeof(u32)==4 );
+ assert( sizeof(u16)==2 );
+ assert( sizeof(Pgno)==4 );
+ assert( sizeof(ptr)==sizeof(char*) );
+ assert( sizeof(uptr)==sizeof(ptr) );
+
+ pBt = sqliteMalloc( sizeof(*pBt) );
+ if( pBt==0 ){
+ *ppBtree = 0;
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3pager_open(&pBt->pPager, zFilename, EXTRA_SIZE,
+ (flags & BTREE_OMIT_JOURNAL)==0);
+ if( rc!=SQLITE_OK ){
+ if( pBt->pPager ) sqlite3pager_close(pBt->pPager);
+ sqliteFree(pBt);
+ *ppBtree = 0;
+ return rc;
+ }
+ sqlite3pager_set_destructor(pBt->pPager, pageDestructor);
+ sqlite3pager_set_reiniter(pBt->pPager, pageReinit);
+ pBt->pCursor = 0;
+ pBt->pPage1 = 0;
+ pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
+ sqlite3pager_read_fileheader(pBt->pPager, sizeof(zDbHeader), zDbHeader);
+ pBt->pageSize = get2byte(&zDbHeader[16]);
+ if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE ){
+ pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE;
+ pBt->maxEmbedFrac = 64; /* 25% */
+ pBt->minEmbedFrac = 32; /* 12.5% */
+ pBt->minLeafFrac = 32; /* 12.5% */
+ nReserve = 0;
+ }else{
+ nReserve = zDbHeader[20];
+ pBt->maxEmbedFrac = zDbHeader[21];
+ pBt->minEmbedFrac = zDbHeader[22];
+ pBt->minLeafFrac = zDbHeader[23];
+ pBt->pageSizeFixed = 1;
+ }
+ pBt->usableSize = pBt->pageSize - nReserve;
+ sqlite3pager_set_pagesize(pBt->pPager, pBt->pageSize);
+ *ppBtree = pBt;
+ return SQLITE_OK;
+}
+
+/*
+** Close an open database and invalidate all cursors.
+*/
+int sqlite3BtreeClose(Btree *pBt){
+ while( pBt->pCursor ){
+ sqlite3BtreeCloseCursor(pBt->pCursor);
+ }
+ sqlite3pager_close(pBt->pPager);
+ sqliteFree(pBt);
+ return SQLITE_OK;
+}
+
+/*
+** Change the busy handler callback function.
+*/
+int sqlite3BtreeSetBusyHandler(Btree *pBt, BusyHandler *pHandler){
+ sqlite3pager_set_busyhandler(pBt->pPager, pHandler);
+ return SQLITE_OK;
+}
+
+/*
+** Change the limit on the number of pages allowed in the cache.
+**
+** The maximum number of cache pages is set to the absolute
+** value of mxPage. If mxPage is negative, the pager will
+** operate asynchronously - it will not stop to do fsync()s
+** to insure data is written to the disk surface before
+** continuing. Transactions still work if synchronous is off,
+** and the database cannot be corrupted if this program
+** crashes. But if the operating system crashes or there is
+** an abrupt power failure when synchronous is off, the database
+** could be left in an inconsistent and unrecoverable state.
+** Synchronous is on by default so database corruption is not
+** normally a worry.
+*/
+int sqlite3BtreeSetCacheSize(Btree *pBt, int mxPage){
+ sqlite3pager_set_cachesize(pBt->pPager, mxPage);
+ return SQLITE_OK;
+}
+
+/*
+** Change the way data is synced to disk in order to increase or decrease
+** how well the database resists damage due to OS crashes and power
+** failures. Level 1 is the same as asynchronous (no syncs() occur and
+** there is a high probability of damage) Level 2 is the default. There
+** is a very low but non-zero probability of damage. Level 3 reduces the
+** probability of damage to near zero but with a write performance reduction.
+*/
+int sqlite3BtreeSetSafetyLevel(Btree *pBt, int level){
+ sqlite3pager_set_safety_level(pBt->pPager, level);
+ return SQLITE_OK;
+}
+
+/*
+** Change the default pages size and the number of reserved bytes per page.
+*/
+int sqlite3BtreeSetPageSize(Btree *pBt, int pageSize, int nReserve){
+ if( pBt->pageSizeFixed ){
+ return SQLITE_READONLY;
+ }
+ if( nReserve<0 ){
+ nReserve = pBt->pageSize - pBt->usableSize;
+ }
+ if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE ){
+ pBt->pageSize = pageSize;
+ sqlite3pager_set_pagesize(pBt->pPager, pageSize);
+ }
+ pBt->usableSize = pBt->pageSize - nReserve;
+ return SQLITE_OK;
+}
+
+/*
+** Return the currently defined page size
+*/
+int sqlite3BtreeGetPageSize(Btree *pBt){
+ return pBt->pageSize;
+}
+int sqlite3BtreeGetReserve(Btree *pBt){
+ return pBt->pageSize - pBt->usableSize;
+}
+
+/*
+** Get a reference to pPage1 of the database file. This will
+** also acquire a readlock on that file.
+**
+** SQLITE_OK is returned on success. If the file is not a
+** well-formed database file, then SQLITE_CORRUPT is returned.
+** SQLITE_BUSY is returned if the database is locked. SQLITE_NOMEM
+** is returned if we run out of memory. SQLITE_PROTOCOL is returned
+** if there is a locking protocol violation.
+*/
+static int lockBtree(Btree *pBt){
+ int rc;
+ MemPage *pPage1;
+ if( pBt->pPage1 ) return SQLITE_OK;
+ rc = getPage(pBt, 1, &pPage1);
+ if( rc!=SQLITE_OK ) return rc;
+
+
+ /* Do some checking to help insure the file we opened really is
+ ** a valid database file.
+ */
+ rc = SQLITE_NOTADB;
+ if( sqlite3pager_pagecount(pBt->pPager)>0 ){
+ u8 *page1 = pPage1->aData;
+ if( memcmp(page1, zMagicHeader, 16)!=0 ){
+ goto page1_init_failed;
+ }
+ if( page1[18]>1 || page1[19]>1 ){
+ goto page1_init_failed;
+ }
+ pBt->pageSize = get2byte(&page1[16]);
+ pBt->usableSize = pBt->pageSize - page1[20];
+ if( pBt->usableSize<500 ){
+ goto page1_init_failed;
+ }
+ pBt->maxEmbedFrac = page1[21];
+ pBt->minEmbedFrac = page1[22];
+ pBt->minLeafFrac = page1[23];
+ }
+
+ /* maxLocal is the maximum amount of payload to store locally for
+ ** a cell. Make sure it is small enough so that at least minFanout
+ ** cells can will fit on one page. We assume a 10-byte page header.
+ ** Besides the payload, the cell must store:
+ ** 2-byte pointer to the cell
+ ** 4-byte child pointer
+ ** 9-byte nKey value
+ ** 4-byte nData value
+ ** 4-byte overflow page pointer
+ ** So a cell consists of a 2-byte poiner, a header which is as much as
+ ** 17 bytes long, 0 to N bytes of payload, and an optional 4 byte overflow
+ ** page pointer.
+ */
+ pBt->maxLocal = (pBt->usableSize-12)*pBt->maxEmbedFrac/255 - 23;
+ pBt->minLocal = (pBt->usableSize-12)*pBt->minEmbedFrac/255 - 23;
+ pBt->maxLeaf = pBt->usableSize - 35;
+ pBt->minLeaf = (pBt->usableSize-12)*pBt->minLeafFrac/255 - 23;
+ if( pBt->minLocal>pBt->maxLocal || pBt->maxLocal<0 ){
+ goto page1_init_failed;
+ }
+ assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
+ pBt->pPage1 = pPage1;
+ return SQLITE_OK;
+
+page1_init_failed:
+ releasePage(pPage1);
+ pBt->pPage1 = 0;
+ return rc;
+}
+
+/*
+** If there are no outstanding cursors and we are not in the middle
+** of a transaction but there is a read lock on the database, then
+** this routine unrefs the first page of the database file which
+** has the effect of releasing the read lock.
+**
+** If there are any outstanding cursors, this routine is a no-op.
+**
+** If there is a transaction in progress, this routine is a no-op.
+*/
+static void unlockBtreeIfUnused(Btree *pBt){
+ if( pBt->inTrans==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
+ if( pBt->pPage1->aData==0 ){
+ MemPage *pPage = pBt->pPage1;
+ pPage->aData = &((char*)pPage)[-pBt->pageSize];
+ pPage->pBt = pBt;
+ pPage->pgno = 1;
+ }
+ releasePage(pBt->pPage1);
+ pBt->pPage1 = 0;
+ pBt->inStmt = 0;
+ }
+}
+
+/*
+** Create a new database by initializing the first page of the
+** file.
+*/
+static int newDatabase(Btree *pBt){
+ MemPage *pP1;
+ unsigned char *data;
+ int rc;
+ if( sqlite3pager_pagecount(pBt->pPager)>0 ) return SQLITE_OK;
+ pP1 = pBt->pPage1;
+ assert( pP1!=0 );
+ data = pP1->aData;
+ rc = sqlite3pager_write(data);
+ if( rc ) return rc;
+ memcpy(data, zMagicHeader, sizeof(zMagicHeader));
+ assert( sizeof(zMagicHeader)==16 );
+ put2byte(&data[16], pBt->pageSize);
+ data[18] = 1;
+ data[19] = 1;
+ data[20] = pBt->pageSize - pBt->usableSize;
+ data[21] = pBt->maxEmbedFrac;
+ data[22] = pBt->minEmbedFrac;
+ data[23] = pBt->minLeafFrac;
+ memset(&data[24], 0, 100-24);
+ zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
+ pBt->pageSizeFixed = 1;
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to start a new transaction. A write-transaction
+** is started if the second argument is nonzero, otherwise a read-
+** transaction. If the second argument is 2 or more and exclusive
+** transaction is started, meaning that no other process is allowed
+** to access the database. A preexisting transaction may not be
+** upgrade to exclusive by calling this routine a second time - the
+** exclusivity flag only works for a new transaction.
+**
+** A write-transaction must be started before attempting any
+** changes to the database. None of the following routines
+** will work unless a transaction is started first:
+**
+** sqlite3BtreeCreateTable()
+** sqlite3BtreeCreateIndex()
+** sqlite3BtreeClearTable()
+** sqlite3BtreeDropTable()
+** sqlite3BtreeInsert()
+** sqlite3BtreeDelete()
+** sqlite3BtreeUpdateMeta()
+**
+** If wrflag is true, then nMaster specifies the maximum length of
+** a master journal file name supplied later via sqlite3BtreeSync().
+** This is so that appropriate space can be allocated in the journal file
+** when it is created..
+*/
+int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
+ int rc = SQLITE_OK;
+
+ /* If the btree is already in a write-transaction, or it
+ ** is already in a read-transaction and a read-transaction
+ ** is requested, this is a no-op.
+ */
+ if( pBt->inTrans==TRANS_WRITE ||
+ (pBt->inTrans==TRANS_READ && !wrflag) ){
+ return SQLITE_OK;
+ }
+ if( pBt->readOnly && wrflag ){
+ return SQLITE_READONLY;
+ }
+
+ if( pBt->pPage1==0 ){
+ rc = lockBtree(pBt);
+ }
+
+ if( rc==SQLITE_OK && wrflag ){
+ rc = sqlite3pager_begin(pBt->pPage1->aData, wrflag>1);
+ if( rc==SQLITE_OK ){
+ rc = newDatabase(pBt);
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
+ if( wrflag ) pBt->inStmt = 0;
+ }else{
+ unlockBtreeIfUnused(pBt);
+ }
+ return rc;
+}
+
+/*
+** Commit the transaction currently in progress.
+**
+** This will release the write lock on the database file. If there
+** are no active cursors, it also releases the read lock.
+*/
+int sqlite3BtreeCommit(Btree *pBt){
+ int rc = SQLITE_OK;
+ if( pBt->inTrans==TRANS_WRITE ){
+ rc = sqlite3pager_commit(pBt->pPager);
+ }
+ pBt->inTrans = TRANS_NONE;
+ pBt->inStmt = 0;
+ unlockBtreeIfUnused(pBt);
+ return rc;
+}
+
+#ifndef NDEBUG
+/*
+** Return the number of write-cursors open on this handle. This is for use
+** in assert() expressions, so it is only compiled if NDEBUG is not
+** defined.
+*/
+static int countWriteCursors(Btree *pBt){
+ BtCursor *pCur;
+ int r = 0;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ if( pCur->wrFlag ) r++;
+ }
+ return r;
+}
+#endif
+
+#if 0
+/*
+** Invalidate all cursors
+*/
+static void invalidateCursors(Btree *pBt){
+ BtCursor *pCur;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ MemPage *pPage = pCur->pPage;
+ if( pPage /* && !pPage->isInit */ ){
+ pageIntegrity(pPage);
+ releasePage(pPage);
+ pCur->pPage = 0;
+ pCur->isValid = 0;
+ pCur->status = SQLITE_ABORT;
+ }
+ }
+}
+#endif
+
+#ifdef SQLITE_TEST
+/*
+** Print debugging information about all cursors to standard output.
+*/
+void sqlite3BtreeCursorList(Btree *pBt){
+ BtCursor *pCur;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ MemPage *pPage = pCur->pPage;
+ char *zMode = pCur->wrFlag ? "rw" : "ro";
+ sqlite3DebugPrintf("CURSOR %p rooted at %4d(%s) currently at %d.%d%s\n",
+ pCur, pCur->pgnoRoot, zMode,
+ pPage ? pPage->pgno : 0, pCur->idx,
+ pCur->isValid ? "" : " eof"
+ );
+ }
+}
+#endif
+
+/*
+** Rollback the transaction in progress. All cursors will be
+** invalided by this operation. Any attempt to use a cursor
+** that was open at the beginning of this operation will result
+** in an error.
+**
+** This will release the write lock on the database file. If there
+** are no active cursors, it also releases the read lock.
+*/
+int sqlite3BtreeRollback(Btree *pBt){
+ int rc = SQLITE_OK;
+ MemPage *pPage1;
+ if( pBt->inTrans==TRANS_WRITE ){
+ rc = sqlite3pager_rollback(pBt->pPager);
+ /* The rollback may have destroyed the pPage1->aData value. So
+ ** call getPage() on page 1 again to make sure pPage1->aData is
+ ** set correctly. */
+ if( getPage(pBt, 1, &pPage1)==SQLITE_OK ){
+ releasePage(pPage1);
+ }
+ assert( countWriteCursors(pBt)==0 );
+ }
+ pBt->inTrans = TRANS_NONE;
+ pBt->inStmt = 0;
+ unlockBtreeIfUnused(pBt);
+ return rc;
+}
+
+/*
+** Start a statement subtransaction. The subtransaction can
+** can be rolled back independently of the main transaction.
+** You must start a transaction before starting a subtransaction.
+** The subtransaction is ended automatically if the main transaction
+** commits or rolls back.
+**
+** Only one subtransaction may be active at a time. It is an error to try
+** to start a new subtransaction if another subtransaction is already active.
+**
+** Statement subtransactions are used around individual SQL statements
+** that are contained within a BEGIN...COMMIT block. If a constraint
+** error occurs within the statement, the effect of that one statement
+** can be rolled back without having to rollback the entire transaction.
+*/
+int sqlite3BtreeBeginStmt(Btree *pBt){
+ int rc;
+ if( (pBt->inTrans!=TRANS_WRITE) || pBt->inStmt ){
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ rc = pBt->readOnly ? SQLITE_OK : sqlite3pager_stmt_begin(pBt->pPager);
+ pBt->inStmt = 1;
+ return rc;
+}
+
+
+/*
+** Commit the statment subtransaction currently in progress. If no
+** subtransaction is active, this is a no-op.
+*/
+int sqlite3BtreeCommitStmt(Btree *pBt){
+ int rc;
+ if( pBt->inStmt && !pBt->readOnly ){
+ rc = sqlite3pager_stmt_commit(pBt->pPager);
+ }else{
+ rc = SQLITE_OK;
+ }
+ pBt->inStmt = 0;
+ return rc;
+}
+
+/*
+** Rollback the active statement subtransaction. If no subtransaction
+** is active this routine is a no-op.
+**
+** All cursors will be invalidated by this operation. Any attempt
+** to use a cursor that was open at the beginning of this operation
+** will result in an error.
+*/
+int sqlite3BtreeRollbackStmt(Btree *pBt){
+ int rc;
+ if( pBt->inStmt==0 || pBt->readOnly ) return SQLITE_OK;
+ rc = sqlite3pager_stmt_rollback(pBt->pPager);
+ assert( countWriteCursors(pBt)==0 );
+ pBt->inStmt = 0;
+ return rc;
+}
+
+/*
+** Default key comparison function to be used if no comparison function
+** is specified on the sqlite3BtreeCursor() call.
+*/
+static int dfltCompare(
+ void *NotUsed, /* User data is not used */
+ int n1, const void *p1, /* First key to compare */
+ int n2, const void *p2 /* Second key to compare */
+){
+ int c;
+ c = memcmp(p1, p2, n1<n2 ? n1 : n2);
+ if( c==0 ){
+ c = n1 - n2;
+ }
+ return c;
+}
+
+/*
+** Create a new cursor for the BTree whose root is on the page
+** iTable. The act of acquiring a cursor gets a read lock on
+** the database file.
+**
+** If wrFlag==0, then the cursor can only be used for reading.
+** If wrFlag==1, then the cursor can be used for reading or for
+** writing if other conditions for writing are also met. These
+** are the conditions that must be met in order for writing to
+** be allowed:
+**
+** 1: The cursor must have been opened with wrFlag==1
+**
+** 2: No other cursors may be open with wrFlag==0 on the same table
+**
+** 3: The database must be writable (not on read-only media)
+**
+** 4: There must be an active transaction.
+**
+** Condition 2 warrants further discussion. If any cursor is opened
+** on a table with wrFlag==0, that prevents all other cursors from
+** writing to that table. This is a kind of "read-lock". When a cursor
+** is opened with wrFlag==0 it is guaranteed that the table will not
+** change as long as the cursor is open. This allows the cursor to
+** do a sequential scan of the table without having to worry about
+** entries being inserted or deleted during the scan. Cursors should
+** be opened with wrFlag==0 only if this read-lock property is needed.
+** That is to say, cursors should be opened with wrFlag==0 only if they
+** intend to use the sqlite3BtreeNext() system call. All other cursors
+** should be opened with wrFlag==1 even if they never really intend
+** to write.
+**
+** No checking is done to make sure that page iTable really is the
+** root page of a b-tree. If it is not, then the cursor acquired
+** will not work correctly.
+**
+** The comparison function must be logically the same for every cursor
+** on a particular table. Changing the comparison function will result
+** in incorrect operations. If the comparison function is NULL, a
+** default comparison function is used. The comparison function is
+** always ignored for INTKEY tables.
+*/
+int sqlite3BtreeCursor(
+ Btree *pBt, /* The btree */
+ int iTable, /* Root page of table to open */
+ int wrFlag, /* 1 to write. 0 read-only */
+ int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */
+ void *pArg, /* First arg to xCompare() */
+ BtCursor **ppCur /* Write new cursor here */
+){
+ int rc;
+ BtCursor *pCur;
+
+ *ppCur = 0;
+ if( wrFlag ){
+ if( pBt->readOnly ){
+ return SQLITE_READONLY;
+ }
+ if( checkReadLocks(pBt, iTable, 0) ){
+ return SQLITE_LOCKED;
+ }
+ }
+ if( pBt->pPage1==0 ){
+ rc = lockBtree(pBt);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+ pCur = sqliteMallocRaw( sizeof(*pCur) );
+ if( pCur==0 ){
+ rc = SQLITE_NOMEM;
+ goto create_cursor_exception;
+ }
+ pCur->pgnoRoot = (Pgno)iTable;
+ if( iTable==1 && sqlite3pager_pagecount(pBt->pPager)==0 ){
+ rc = SQLITE_EMPTY;
+ pCur->pPage = 0;
+ goto create_cursor_exception;
+ }
+ pCur->pPage = 0; /* For exit-handler, in case getAndInitPage() fails. */
+ rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->pPage, 0);
+ if( rc!=SQLITE_OK ){
+ goto create_cursor_exception;
+ }
+ pCur->xCompare = xCmp ? xCmp : dfltCompare;
+ pCur->pArg = pArg;
+ pCur->pBt = pBt;
+ pCur->wrFlag = wrFlag;
+ pCur->idx = 0;
+ memset(&pCur->info, 0, sizeof(pCur->info));
+ pCur->pNext = pBt->pCursor;
+ if( pCur->pNext ){
+ pCur->pNext->pPrev = pCur;
+ }
+ pCur->pPrev = 0;
+ pBt->pCursor = pCur;
+ pCur->isValid = 0;
+ pCur->status = SQLITE_OK;
+ *ppCur = pCur;
+ return SQLITE_OK;
+
+create_cursor_exception:
+ if( pCur ){
+ releasePage(pCur->pPage);
+ sqliteFree(pCur);
+ }
+ unlockBtreeIfUnused(pBt);
+ return rc;
+}
+
+#if 0 /* Not Used */
+/*
+** Change the value of the comparison function used by a cursor.
+*/
+void sqlite3BtreeSetCompare(
+ BtCursor *pCur, /* The cursor to whose comparison function is changed */
+ int(*xCmp)(void*,int,const void*,int,const void*), /* New comparison func */
+ void *pArg /* First argument to xCmp() */
+){
+ pCur->xCompare = xCmp ? xCmp : dfltCompare;
+ pCur->pArg = pArg;
+}
+#endif
+
+/*
+** Close a cursor. The read lock on the database file is released
+** when the last cursor is closed.
+*/
+int sqlite3BtreeCloseCursor(BtCursor *pCur){
+ Btree *pBt = pCur->pBt;
+ if( pCur->pPrev ){
+ pCur->pPrev->pNext = pCur->pNext;
+ }else{
+ pBt->pCursor = pCur->pNext;
+ }
+ if( pCur->pNext ){
+ pCur->pNext->pPrev = pCur->pPrev;
+ }
+ releasePage(pCur->pPage);
+ unlockBtreeIfUnused(pBt);
+ sqliteFree(pCur);
+ return SQLITE_OK;
+}
+
+/*
+** Make a temporary cursor by filling in the fields of pTempCur.
+** The temporary cursor is not on the cursor list for the Btree.
+*/
+static void getTempCursor(BtCursor *pCur, BtCursor *pTempCur){
+ memcpy(pTempCur, pCur, sizeof(*pCur));
+ pTempCur->pNext = 0;
+ pTempCur->pPrev = 0;
+ if( pTempCur->pPage ){
+ sqlite3pager_ref(pTempCur->pPage->aData);
+ }
+}
+
+/*
+** Delete a temporary cursor such as was made by the CreateTemporaryCursor()
+** function above.
+*/
+static void releaseTempCursor(BtCursor *pCur){
+ if( pCur->pPage ){
+ sqlite3pager_unref(pCur->pPage->aData);
+ }
+}
+
+/*
+** Make sure the BtCursor.info field of the given cursor is valid.
+** If it is not already valid, call parseCell() to fill it in.
+**
+** BtCursor.info is a cache of the information in the current cell.
+** Using this cache reduces the number of calls to parseCell().
+*/
+static void getCellInfo(BtCursor *pCur){
+ if( pCur->info.nSize==0 ){
+ parseCell(pCur->pPage, pCur->idx, &pCur->info);
+ }else{
+#ifndef NDEBUG
+ CellInfo info;
+ memset(&info, 0, sizeof(info));
+ parseCell(pCur->pPage, pCur->idx, &info);
+ assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
+#endif
+ }
+}
+
+/*
+** Set *pSize to the size of the buffer needed to hold the value of
+** the key for the current entry. If the cursor is not pointing
+** to a valid entry, *pSize is set to 0.
+**
+** For a table with the INTKEY flag set, this routine returns the key
+** itself, not the number of bytes in the key.
+*/
+int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
+ if( !pCur->isValid ){
+ *pSize = 0;
+ }else{
+ getCellInfo(pCur);
+ *pSize = pCur->info.nKey;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Set *pSize to the number of bytes of data in the entry the
+** cursor currently points to. Always return SQLITE_OK.
+** Failure is not possible. If the cursor is not currently
+** pointing to an entry (which can happen, for example, if
+** the database is empty) then *pSize is set to 0.
+*/
+int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
+ if( !pCur->isValid ){
+ /* Not pointing at a valid entry - set *pSize to 0. */
+ *pSize = 0;
+ }else{
+ getCellInfo(pCur);
+ *pSize = pCur->info.nData;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Read payload information from the entry that the pCur cursor is
+** pointing to. Begin reading the payload at "offset" and read
+** a total of "amt" bytes. Put the result in zBuf.
+**
+** This routine does not make a distinction between key and data.
+** It just reads bytes from the payload area. Data might appear
+** on the main page or be scattered out on multiple overflow pages.
+*/
+static int getPayload(
+ BtCursor *pCur, /* Cursor pointing to entry to read from */
+ int offset, /* Begin reading this far into payload */
+ int amt, /* Read this many bytes */
+ unsigned char *pBuf, /* Write the bytes into this buffer */
+ int skipKey /* offset begins at data if this is true */
+){
+ unsigned char *aPayload;
+ Pgno nextPage;
+ int rc;
+ MemPage *pPage;
+ Btree *pBt;
+ int ovflSize;
+ u32 nKey;
+
+ assert( pCur!=0 && pCur->pPage!=0 );
+ assert( pCur->isValid );
+ pBt = pCur->pBt;
+ pPage = pCur->pPage;
+ pageIntegrity(pPage);
+ assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+ getCellInfo(pCur);
+ aPayload = pCur->info.pCell;
+ aPayload += pCur->info.nHeader;
+ if( pPage->intKey ){
+ nKey = 0;
+ }else{
+ nKey = pCur->info.nKey;
+ }
+ assert( offset>=0 );
+ if( skipKey ){
+ offset += nKey;
+ }
+ if( offset+amt > nKey+pCur->info.nData ){
+ return SQLITE_ERROR;
+ }
+ if( offset<pCur->info.nLocal ){
+ int a = amt;
+ if( a+offset>pCur->info.nLocal ){
+ a = pCur->info.nLocal - offset;
+ }
+ memcpy(pBuf, &aPayload[offset], a);
+ if( a==amt ){
+ return SQLITE_OK;
+ }
+ offset = 0;
+ pBuf += a;
+ amt -= a;
+ }else{
+ offset -= pCur->info.nLocal;
+ }
+ ovflSize = pBt->usableSize - 4;
+ if( amt>0 ){
+ nextPage = get4byte(&aPayload[pCur->info.nLocal]);
+ while( amt>0 && nextPage ){
+ rc = sqlite3pager_get(pBt->pPager, nextPage, (void**)&aPayload);
+ if( rc!=0 ){
+ return rc;
+ }
+ nextPage = get4byte(aPayload);
+ if( offset<ovflSize ){
+ int a = amt;
+ if( a + offset > ovflSize ){
+ a = ovflSize - offset;
+ }
+ memcpy(pBuf, &aPayload[offset+4], a);
+ offset = 0;
+ amt -= a;
+ pBuf += a;
+ }else{
+ offset -= ovflSize;
+ }
+ sqlite3pager_unref(aPayload);
+ }
+ }
+
+ if( amt>0 ){
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Read part of the key associated with cursor pCur. Exactly
+** "amt" bytes will be transfered into pBuf[]. The transfer
+** begins at "offset".
+**
+** Return SQLITE_OK on success or an error code if anything goes
+** wrong. An error is returned if "offset+amt" is larger than
+** the available payload.
+*/
+int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
+ if( pCur->isValid==0 ){
+ return pCur->status;
+ }
+ assert( pCur->pPage!=0 );
+ assert( pCur->pPage->intKey==0 );
+ assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+ return getPayload(pCur, offset, amt, (unsigned char*)pBuf, 0);
+}
+
+/*
+** Read part of the data associated with cursor pCur. Exactly
+** "amt" bytes will be transfered into pBuf[]. The transfer
+** begins at "offset".
+**
+** Return SQLITE_OK on success or an error code if anything goes
+** wrong. An error is returned if "offset+amt" is larger than
+** the available payload.
+*/
+int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
+ if( !pCur->isValid ){
+ return pCur->status ? pCur->status : SQLITE_INTERNAL;
+ }
+ assert( pCur->pPage!=0 );
+ assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+ return getPayload(pCur, offset, amt, pBuf, 1);
+}
+
+/*
+** Return a pointer to payload information from the entry that the
+** pCur cursor is pointing to. The pointer is to the beginning of
+** the key if skipKey==0 and it points to the beginning of data if
+** skipKey==1. The number of bytes of available key/data is written
+** into *pAmt. If *pAmt==0, then the value returned will not be
+** a valid pointer.
+**
+** This routine is an optimization. It is common for the entire key
+** and data to fit on the local page and for there to be no overflow
+** pages. When that is so, this routine can be used to access the
+** key and data without making a copy. If the key and/or data spills
+** onto overflow pages, then getPayload() must be used to reassembly
+** the key/data and copy it into a preallocated buffer.
+**
+** The pointer returned by this routine looks directly into the cached
+** page of the database. The data might change or move the next time
+** any btree routine is called.
+*/
+static const unsigned char *fetchPayload(
+ BtCursor *pCur, /* Cursor pointing to entry to read from */
+ int *pAmt, /* Write the number of available bytes here */
+ int skipKey /* read beginning at data if this is true */
+){
+ unsigned char *aPayload;
+ MemPage *pPage;
+ Btree *pBt;
+ u32 nKey;
+ int nLocal;
+
+ assert( pCur!=0 && pCur->pPage!=0 );
+ assert( pCur->isValid );
+ pBt = pCur->pBt;
+ pPage = pCur->pPage;
+ pageIntegrity(pPage);
+ assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+ getCellInfo(pCur);
+ aPayload = pCur->info.pCell;
+ aPayload += pCur->info.nHeader;
+ if( pPage->intKey ){
+ nKey = 0;
+ }else{
+ nKey = pCur->info.nKey;
+ }
+ if( skipKey ){
+ aPayload += nKey;
+ nLocal = pCur->info.nLocal - nKey;
+ }else{
+ nLocal = pCur->info.nLocal;
+ if( nLocal>nKey ){
+ nLocal = nKey;
+ }
+ }
+ *pAmt = nLocal;
+ return aPayload;
+}
+
+
+/*
+** For the entry that cursor pCur is point to, return as
+** many bytes of the key or data as are available on the local
+** b-tree page. Write the number of available bytes into *pAmt.
+**
+** The pointer returned is ephemeral. The key/data may move
+** or be destroyed on the next call to any Btree routine.
+**
+** These routines is used to get quick access to key and data
+** in the common case where no overflow pages are used.
+*/
+const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
+ return (const void*)fetchPayload(pCur, pAmt, 0);
+}
+const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
+ return (const void*)fetchPayload(pCur, pAmt, 1);
+}
+
+
+/*
+** Move the cursor down to a new child page. The newPgno argument is the
+** page number of the child page to move to.
+*/
+static int moveToChild(BtCursor *pCur, u32 newPgno){
+ int rc;
+ MemPage *pNewPage;
+ MemPage *pOldPage;
+ Btree *pBt = pCur->pBt;
+
+ assert( pCur->isValid );
+ rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
+ if( rc ) return rc;
+ pageIntegrity(pNewPage);
+ pNewPage->idxParent = pCur->idx;
+ pOldPage = pCur->pPage;
+ pOldPage->idxShift = 0;
+ releasePage(pOldPage);
+ pCur->pPage = pNewPage;
+ pCur->idx = 0;
+ pCur->info.nSize = 0;
+ if( pNewPage->nCell<1 ){
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Return true if the page is the virtual root of its table.
+**
+** The virtual root page is the root page for most tables. But
+** for the table rooted on page 1, sometime the real root page
+** is empty except for the right-pointer. In such cases the
+** virtual root page is the page that the right-pointer of page
+** 1 is pointing to.
+*/
+static int isRootPage(MemPage *pPage){
+ MemPage *pParent = pPage->pParent;
+ if( pParent==0 ) return 1;
+ if( pParent->pgno>1 ) return 0;
+ if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
+ return 0;
+}
+
+/*
+** Move the cursor up to the parent page.
+**
+** pCur->idx is set to the cell index that contains the pointer
+** to the page we are coming from. If we are coming from the
+** right-most child page then pCur->idx is set to one more than
+** the largest cell index.
+*/
+static void moveToParent(BtCursor *pCur){
+ Pgno oldPgno;
+ MemPage *pParent;
+ MemPage *pPage;
+ int idxParent;
+
+ assert( pCur->isValid );
+ pPage = pCur->pPage;
+ assert( pPage!=0 );
+ assert( !isRootPage(pPage) );
+ pageIntegrity(pPage);
+ pParent = pPage->pParent;
+ assert( pParent!=0 );
+ pageIntegrity(pParent);
+ idxParent = pPage->idxParent;
+ sqlite3pager_ref(pParent->aData);
+ oldPgno = pPage->pgno;
+ releasePage(pPage);
+ pCur->pPage = pParent;
+ pCur->info.nSize = 0;
+ assert( pParent->idxShift==0 );
+ pCur->idx = idxParent;
+}
+
+/*
+** Move the cursor to the root page
+*/
+static int moveToRoot(BtCursor *pCur){
+ MemPage *pRoot;
+ int rc;
+ Btree *pBt = pCur->pBt;
+
+ rc = getAndInitPage(pBt, pCur->pgnoRoot, &pRoot, 0);
+ if( rc ){
+ pCur->isValid = 0;
+ return rc;
+ }
+ releasePage(pCur->pPage);
+ pageIntegrity(pRoot);
+ pCur->pPage = pRoot;
+ pCur->idx = 0;
+ pCur->info.nSize = 0;
+ if( pRoot->nCell==0 && !pRoot->leaf ){
+ Pgno subpage;
+ assert( pRoot->pgno==1 );
+ subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
+ assert( subpage>0 );
+ pCur->isValid = 1;
+ rc = moveToChild(pCur, subpage);
+ }
+ pCur->isValid = pCur->pPage->nCell>0;
+ return rc;
+}
+
+/*
+** Move the cursor down to the left-most leaf entry beneath the
+** entry to which it is currently pointing.
+*/
+static int moveToLeftmost(BtCursor *pCur){
+ Pgno pgno;
+ int rc;
+ MemPage *pPage;
+
+ assert( pCur->isValid );
+ while( !(pPage = pCur->pPage)->leaf ){
+ assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+ pgno = get4byte(findCell(pPage, pCur->idx));
+ rc = moveToChild(pCur, pgno);
+ if( rc ) return rc;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Move the cursor down to the right-most leaf entry beneath the
+** page to which it is currently pointing. Notice the difference
+** between moveToLeftmost() and moveToRightmost(). moveToLeftmost()
+** finds the left-most entry beneath the *entry* whereas moveToRightmost()
+** finds the right-most entry beneath the *page*.
+*/
+static int moveToRightmost(BtCursor *pCur){
+ Pgno pgno;
+ int rc;
+ MemPage *pPage;
+
+ assert( pCur->isValid );
+ while( !(pPage = pCur->pPage)->leaf ){
+ pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ pCur->idx = pPage->nCell;
+ rc = moveToChild(pCur, pgno);
+ if( rc ) return rc;
+ }
+ pCur->idx = pPage->nCell - 1;
+ pCur->info.nSize = 0;
+ return SQLITE_OK;
+}
+
+/* Move the cursor to the first entry in the table. Return SQLITE_OK
+** on success. Set *pRes to 0 if the cursor actually points to something
+** or set *pRes to 1 if the table is empty.
+*/
+int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
+ int rc;
+ if( pCur->status ){
+ return pCur->status;
+ }
+ rc = moveToRoot(pCur);
+ if( rc ) return rc;
+ if( pCur->isValid==0 ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ assert( pCur->pPage->nCell>0 );
+ *pRes = 0;
+ rc = moveToLeftmost(pCur);
+ return rc;
+}
+
+/* Move the cursor to the last entry in the table. Return SQLITE_OK
+** on success. Set *pRes to 0 if the cursor actually points to something
+** or set *pRes to 1 if the table is empty.
+*/
+int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
+ int rc;
+ if( pCur->status ){
+ return pCur->status;
+ }
+ rc = moveToRoot(pCur);
+ if( rc ) return rc;
+ if( pCur->isValid==0 ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ assert( pCur->isValid );
+ *pRes = 0;
+ rc = moveToRightmost(pCur);
+ return rc;
+}
+
+/* Move the cursor so that it points to an entry near pKey/nKey.
+** Return a success code.
+**
+** For INTKEY tables, only the nKey parameter is used. pKey is
+** ignored. For other tables, nKey is the number of bytes of data
+** in nKey. The comparison function specified when the cursor was
+** created is used to compare keys.
+**
+** If an exact match is not found, then the cursor is always
+** left pointing at a leaf page which would hold the entry if it
+** were present. The cursor might point to an entry that comes
+** before or after the key.
+**
+** The result of comparing the key with the entry to which the
+** cursor is written to *pRes if pRes!=NULL. The meaning of
+** this value is as follows:
+**
+** *pRes<0 The cursor is left pointing at an entry that
+** is smaller than pKey or if the table is empty
+** and the cursor is therefore left point to nothing.
+**
+** *pRes==0 The cursor is left pointing at an entry that
+** exactly matches pKey.
+**
+** *pRes>0 The cursor is left pointing at an entry that
+** is larger than pKey.
+*/
+int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
+ int rc;
+
+ if( pCur->status ){
+ return pCur->status;
+ }
+ rc = moveToRoot(pCur);
+ if( rc ) return rc;
+ assert( pCur->pPage );
+ assert( pCur->pPage->isInit );
+ if( pCur->isValid==0 ){
+ *pRes = -1;
+ assert( pCur->pPage->nCell==0 );
+ return SQLITE_OK;
+ }
+ for(;;){
+ int lwr, upr;
+ Pgno chldPg;
+ MemPage *pPage = pCur->pPage;
+ int c = -1; /* pRes return if table is empty must be -1 */
+ lwr = 0;
+ upr = pPage->nCell-1;
+ pageIntegrity(pPage);
+ while( lwr<=upr ){
+ void *pCellKey;
+ i64 nCellKey;
+ pCur->idx = (lwr+upr)/2;
+ pCur->info.nSize = 0;
+ sqlite3BtreeKeySize(pCur, &nCellKey);
+ if( pPage->intKey ){
+ if( nCellKey<nKey ){
+ c = -1;
+ }else if( nCellKey>nKey ){
+ c = +1;
+ }else{
+ c = 0;
+ }
+ }else{
+ int available;
+ pCellKey = (void *)fetchPayload(pCur, &available, 0);
+ if( available>=nCellKey ){
+ c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
+ }else{
+ pCellKey = sqliteMallocRaw( nCellKey );
+ if( pCellKey==0 ) return SQLITE_NOMEM;
+ rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
+ c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
+ sqliteFree(pCellKey);
+ if( rc ) return rc;
+ }
+ }
+ if( c==0 ){
+ if( pPage->leafData && !pPage->leaf ){
+ lwr = pCur->idx;
+ upr = lwr - 1;
+ break;
+ }else{
+ if( pRes ) *pRes = 0;
+ return SQLITE_OK;
+ }
+ }
+ if( c<0 ){
+ lwr = pCur->idx+1;
+ }else{
+ upr = pCur->idx-1;
+ }
+ }
+ assert( lwr==upr+1 );
+ assert( pPage->isInit );
+ if( pPage->leaf ){
+ chldPg = 0;
+ }else if( lwr>=pPage->nCell ){
+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ }else{
+ chldPg = get4byte(findCell(pPage, lwr));
+ }
+ if( chldPg==0 ){
+ assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
+ if( pRes ) *pRes = c;
+ return SQLITE_OK;
+ }
+ pCur->idx = lwr;
+ pCur->info.nSize = 0;
+ rc = moveToChild(pCur, chldPg);
+ if( rc ){
+ return rc;
+ }
+ }
+ /* NOT REACHED */
+}
+
+/*
+** Return TRUE if the cursor is not pointing at an entry of the table.
+**
+** TRUE will be returned after a call to sqlite3BtreeNext() moves
+** past the last entry in the table or sqlite3BtreePrev() moves past
+** the first entry. TRUE is also returned if the table is empty.
+*/
+int sqlite3BtreeEof(BtCursor *pCur){
+ return pCur->isValid==0;
+}
+
+/*
+** Advance the cursor to the next entry in the database. If
+** successful then set *pRes=0. If the cursor
+** was already pointing to the last entry in the database before
+** this routine was called, then set *pRes=1.
+*/
+int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+ int rc;
+ MemPage *pPage = pCur->pPage;
+
+ assert( pRes!=0 );
+ if( pCur->isValid==0 ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ assert( pPage->isInit );
+ assert( pCur->idx<pPage->nCell );
+ pCur->idx++;
+ pCur->info.nSize = 0;
+ if( pCur->idx>=pPage->nCell ){
+ if( !pPage->leaf ){
+ rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
+ if( rc ) return rc;
+ rc = moveToLeftmost(pCur);
+ *pRes = 0;
+ return rc;
+ }
+ do{
+ if( isRootPage(pPage) ){
+ *pRes = 1;
+ pCur->isValid = 0;
+ return SQLITE_OK;
+ }
+ moveToParent(pCur);
+ pPage = pCur->pPage;
+ }while( pCur->idx>=pPage->nCell );
+ *pRes = 0;
+ if( pPage->leafData ){
+ rc = sqlite3BtreeNext(pCur, pRes);
+ }else{
+ rc = SQLITE_OK;
+ }
+ return rc;
+ }
+ *pRes = 0;
+ if( pPage->leaf ){
+ return SQLITE_OK;
+ }
+ rc = moveToLeftmost(pCur);
+ return rc;
+}
+
+/*
+** Step the cursor to the back to the previous entry in the database. If
+** successful then set *pRes=0. If the cursor
+** was already pointing to the first entry in the database before
+** this routine was called, then set *pRes=1.
+*/
+int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+ int rc;
+ Pgno pgno;
+ MemPage *pPage;
+ if( pCur->isValid==0 ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ pPage = pCur->pPage;
+ assert( pPage->isInit );
+ assert( pCur->idx>=0 );
+ if( !pPage->leaf ){
+ pgno = get4byte( findCell(pPage, pCur->idx) );
+ rc = moveToChild(pCur, pgno);
+ if( rc ) return rc;
+ rc = moveToRightmost(pCur);
+ }else{
+ while( pCur->idx==0 ){
+ if( isRootPage(pPage) ){
+ pCur->isValid = 0;
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ moveToParent(pCur);
+ pPage = pCur->pPage;
+ }
+ pCur->idx--;
+ pCur->info.nSize = 0;
+ if( pPage->leafData ){
+ rc = sqlite3BtreePrevious(pCur, pRes);
+ }else{
+ rc = SQLITE_OK;
+ }
+ }
+ *pRes = 0;
+ return rc;
+}
+
+/*
+** The TRACE macro will print high-level status information about the
+** btree operation when the global variable sqlite3_btree_trace is
+** enabled.
+*/
+#if SQLITE_TEST
+# define TRACE(X) if( sqlite3_btree_trace )\
+ { sqlite3DebugPrintf X; fflush(stdout); }
+#else
+# define TRACE(X)
+#endif
+int sqlite3_btree_trace=0; /* True to enable tracing */
+
+/*
+** Allocate a new page from the database file.
+**
+** The new page is marked as dirty. (In other words, sqlite3pager_write()
+** has already been called on the new page.) The new page has also
+** been referenced and the calling routine is responsible for calling
+** sqlite3pager_unref() on the new page when it is done.
+**
+** SQLITE_OK is returned on success. Any other return value indicates
+** an error. *ppPage and *pPgno are undefined in the event of an error.
+** Do not invoke sqlite3pager_unref() on *ppPage if an error is returned.
+**
+** If the "nearby" parameter is not 0, then a (feeble) effort is made to
+** locate a page close to the page number "nearby". This can be used in an
+** attempt to keep related pages close to each other in the database file,
+** which in turn can make database access faster.
+*/
+static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){
+ MemPage *pPage1;
+ int rc;
+ int n; /* Number of pages on the freelist */
+ int k; /* Number of leaves on the trunk of the freelist */
+
+ pPage1 = pBt->pPage1;
+ n = get4byte(&pPage1->aData[36]);
+ if( n>0 ){
+ /* There are pages on the freelist. Reuse one of those pages. */
+ MemPage *pTrunk;
+ rc = sqlite3pager_write(pPage1->aData);
+ if( rc ) return rc;
+ put4byte(&pPage1->aData[36], n-1);
+ rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk);
+ if( rc ) return rc;
+ rc = sqlite3pager_write(pTrunk->aData);
+ if( rc ){
+ releasePage(pTrunk);
+ return rc;
+ }
+ k = get4byte(&pTrunk->aData[4]);
+ if( k==0 ){
+ /* The trunk has no leaves. So extract the trunk page itself and
+ ** use it as the newly allocated page */
+ *pPgno = get4byte(&pPage1->aData[32]);
+ memcpy(&pPage1->aData[32], &pTrunk->aData[0], 4);
+ *ppPage = pTrunk;
+ TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1));
+ }else if( k>pBt->usableSize/4 - 8 ){
+ /* Value of k is out of range. Database corruption */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }else{
+ /* Extract a leaf from the trunk */
+ int closest;
+ unsigned char *aData = pTrunk->aData;
+ if( nearby>0 ){
+ int i, dist;
+ closest = 0;
+ dist = get4byte(&aData[8]) - nearby;
+ if( dist<0 ) dist = -dist;
+ for(i=1; i<k; i++){
+ int d2 = get4byte(&aData[8+i*4]) - nearby;
+ if( d2<0 ) d2 = -d2;
+ if( d2<dist ) closest = i;
+ }
+ }else{
+ closest = 0;
+ }
+ *pPgno = get4byte(&aData[8+closest*4]);
+ if( *pPgno>sqlite3pager_pagecount(pBt->pPager) ){
+ /* Free page off the end of the file */
+ return SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d: %d more free pages\n",
+ *pPgno, closest+1, k, pTrunk->pgno, n-1));
+ if( closest<k-1 ){
+ memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
+ }
+ put4byte(&aData[4], k-1);
+ rc = getPage(pBt, *pPgno, ppPage);
+ releasePage(pTrunk);
+ if( rc==SQLITE_OK ){
+ sqlite3pager_dont_rollback((*ppPage)->aData);
+ rc = sqlite3pager_write((*ppPage)->aData);
+ }
+ }
+ }else{
+ /* There are no pages on the freelist, so create a new page at the
+ ** end of the file */
+ *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
+ rc = getPage(pBt, *pPgno, ppPage);
+ if( rc ) return rc;
+ rc = sqlite3pager_write((*ppPage)->aData);
+ TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
+ }
+ return rc;
+}
+
+/*
+** Add a page of the database file to the freelist.
+**
+** sqlite3pager_unref() is NOT called for pPage.
+*/
+static int freePage(MemPage *pPage){
+ Btree *pBt = pPage->pBt;
+ MemPage *pPage1 = pBt->pPage1;
+ int rc, n, k;
+
+ /* Prepare the page for freeing */
+ assert( pPage->pgno>1 );
+ pPage->isInit = 0;
+ releasePage(pPage->pParent);
+ pPage->pParent = 0;
+
+ /* Increment the free page count on pPage1 */
+ rc = sqlite3pager_write(pPage1->aData);
+ if( rc ) return rc;
+ n = get4byte(&pPage1->aData[36]);
+ put4byte(&pPage1->aData[36], n+1);
+
+ if( n==0 ){
+ /* This is the first free page */
+ rc = sqlite3pager_write(pPage->aData);
+ if( rc ) return rc;
+ memset(pPage->aData, 0, 8);
+ put4byte(&pPage1->aData[32], pPage->pgno);
+ TRACE(("FREE-PAGE: %d first\n", pPage->pgno));
+ }else{
+ /* Other free pages already exist. Retrive the first trunk page
+ ** of the freelist and find out how many leaves it has. */
+ MemPage *pTrunk;
+ rc = getPage(pBt, get4byte(&pPage1->aData[32]), &pTrunk);
+ if( rc ) return rc;
+ k = get4byte(&pTrunk->aData[4]);
+ if( k>=pBt->usableSize/4 - 8 ){
+ /* The trunk is full. Turn the page being freed into a new
+ ** trunk page with no leaves. */
+ rc = sqlite3pager_write(pPage->aData);
+ if( rc ) return rc;
+ put4byte(pPage->aData, pTrunk->pgno);
+ put4byte(&pPage->aData[4], 0);
+ put4byte(&pPage1->aData[32], pPage->pgno);
+ TRACE(("FREE-PAGE: %d new trunk page replacing %d\n",
+ pPage->pgno, pTrunk->pgno));
+ }else{
+ /* Add the newly freed page as a leaf on the current trunk */
+ rc = sqlite3pager_write(pTrunk->aData);
+ if( rc ) return rc;
+ put4byte(&pTrunk->aData[4], k+1);
+ put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
+ sqlite3pager_dont_write(pBt->pPager, pPage->pgno);
+ TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
+ }
+ releasePage(pTrunk);
+ }
+ return rc;
+}
+
+/*
+** Free any overflow pages associated with the given Cell.
+*/
+static int clearCell(MemPage *pPage, unsigned char *pCell){
+ Btree *pBt = pPage->pBt;
+ CellInfo info;
+ Pgno ovflPgno;
+ int rc;
+
+ parseCellPtr(pPage, pCell, &info);
+ if( info.iOverflow==0 ){
+ return SQLITE_OK; /* No overflow pages. Return without doing anything */
+ }
+ ovflPgno = get4byte(&pCell[info.iOverflow]);
+ while( ovflPgno!=0 ){
+ MemPage *pOvfl;
+ rc = getPage(pBt, ovflPgno, &pOvfl);
+ if( rc ) return rc;
+ ovflPgno = get4byte(pOvfl->aData);
+ rc = freePage(pOvfl);
+ if( rc ) return rc;
+ sqlite3pager_unref(pOvfl->aData);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Create the byte sequence used to represent a cell on page pPage
+** and write that byte sequence into pCell[]. Overflow pages are
+** allocated and filled in as necessary. The calling procedure
+** is responsible for making sure sufficient space has been allocated
+** for pCell[].
+**
+** Note that pCell does not necessary need to point to the pPage->aData
+** area. pCell might point to some temporary storage. The cell will
+** be constructed in this temporary area then copied into pPage->aData
+** later.
+*/
+static int fillInCell(
+ MemPage *pPage, /* The page that contains the cell */
+ unsigned char *pCell, /* Complete text of the cell */
+ const void *pKey, i64 nKey, /* The key */
+ const void *pData,int nData, /* The data */
+ int *pnSize /* Write cell size here */
+){
+ int nPayload;
+ const u8 *pSrc;
+ int nSrc, n, rc;
+ int spaceLeft;
+ MemPage *pOvfl = 0;
+ MemPage *pToRelease = 0;
+ unsigned char *pPrior;
+ unsigned char *pPayload;
+ Btree *pBt = pPage->pBt;
+ Pgno pgnoOvfl = 0;
+ int nHeader;
+ CellInfo info;
+
+ /* Fill in the header. */
+ nHeader = 0;
+ if( !pPage->leaf ){
+ nHeader += 4;
+ }
+ if( pPage->hasData ){
+ nHeader += putVarint(&pCell[nHeader], nData);
+ }else{
+ nData = 0;
+ }
+ nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
+ parseCellPtr(pPage, pCell, &info);
+ assert( info.nHeader==nHeader );
+ assert( info.nKey==nKey );
+ assert( info.nData==nData );
+
+ /* Fill in the payload */
+ nPayload = nData;
+ if( pPage->intKey ){
+ pSrc = pData;
+ nSrc = nData;
+ nData = 0;
+ }else{
+ nPayload += nKey;
+ pSrc = pKey;
+ nSrc = nKey;
+ }
+ *pnSize = info.nSize;
+ spaceLeft = info.nLocal;
+ pPayload = &pCell[nHeader];
+ pPrior = &pCell[info.iOverflow];
+
+ while( nPayload>0 ){
+ if( spaceLeft==0 ){
+ rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl);
+ if( rc ){
+ releasePage(pToRelease);
+ clearCell(pPage, pCell);
+ return rc;
+ }
+ put4byte(pPrior, pgnoOvfl);
+ releasePage(pToRelease);
+ pToRelease = pOvfl;
+ pPrior = pOvfl->aData;
+ put4byte(pPrior, 0);
+ pPayload = &pOvfl->aData[4];
+ spaceLeft = pBt->usableSize - 4;
+ }
+ n = nPayload;
+ if( n>spaceLeft ) n = spaceLeft;
+ if( n>nSrc ) n = nSrc;
+ memcpy(pPayload, pSrc, n);
+ nPayload -= n;
+ pPayload += n;
+ pSrc += n;
+ nSrc -= n;
+ spaceLeft -= n;
+ if( nSrc==0 ){
+ nSrc = nData;
+ pSrc = pData;
+ }
+ }
+ releasePage(pToRelease);
+ return SQLITE_OK;
+}
+
+/*
+** Change the MemPage.pParent pointer on the page whose number is
+** given in the second argument so that MemPage.pParent holds the
+** pointer in the third argument.
+*/
+static void reparentPage(Btree *pBt, Pgno pgno, MemPage *pNewParent, int idx){
+ MemPage *pThis;
+ unsigned char *aData;
+
+ if( pgno==0 ) return;
+ assert( pBt->pPager!=0 );
+ aData = sqlite3pager_lookup(pBt->pPager, pgno);
+ if( aData ){
+ pThis = (MemPage*)&aData[pBt->pageSize];
+ assert( pThis->aData==aData );
+ if( pThis->isInit ){
+ if( pThis->pParent!=pNewParent ){
+ if( pThis->pParent ) sqlite3pager_unref(pThis->pParent->aData);
+ pThis->pParent = pNewParent;
+ if( pNewParent ) sqlite3pager_ref(pNewParent->aData);
+ }
+ pThis->idxParent = idx;
+ }
+ sqlite3pager_unref(aData);
+ }
+}
+
+/*
+** Change the pParent pointer of all children of pPage to point back
+** to pPage.
+**
+** In other words, for every child of pPage, invoke reparentPage()
+** to make sure that each child knows that pPage is its parent.
+**
+** This routine gets called after you memcpy() one page into
+** another.
+*/
+static void reparentChildPages(MemPage *pPage){
+ int i;
+ Btree *pBt;
+
+ if( pPage->leaf ) return;
+ pBt = pPage->pBt;
+ for(i=0; i<pPage->nCell; i++){
+ reparentPage(pBt, get4byte(findCell(pPage,i)), pPage, i);
+ }
+ reparentPage(pBt, get4byte(&pPage->aData[pPage->hdrOffset+8]), pPage, i);
+ pPage->idxShift = 0;
+}
+
+/*
+** Remove the i-th cell from pPage. This routine effects pPage only.
+** The cell content is not freed or deallocated. It is assumed that
+** the cell content has been copied someplace else. This routine just
+** removes the reference to the cell from pPage.
+**
+** "sz" must be the number of bytes in the cell.
+*/
+static void dropCell(MemPage *pPage, int idx, int sz){
+ int i; /* Loop counter */
+ int pc; /* Offset to cell content of cell being deleted */
+ u8 *data; /* pPage->aData */
+ u8 *ptr; /* Used to move bytes around within data[] */
+
+ assert( idx>=0 && idx<pPage->nCell );
+ assert( sz==cellSize(pPage, idx) );
+ assert( sqlite3pager_iswriteable(pPage->aData) );
+ data = pPage->aData;
+ ptr = &data[pPage->cellOffset + 2*idx];
+ pc = get2byte(ptr);
+ assert( pc>10 && pc+sz<=pPage->pBt->usableSize );
+ freeSpace(pPage, pc, sz);
+ for(i=idx+1; i<pPage->nCell; i++, ptr+=2){
+ ptr[0] = ptr[2];
+ ptr[1] = ptr[3];
+ }
+ pPage->nCell--;
+ put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
+ pPage->nFree += 2;
+ pPage->idxShift = 1;
+}
+
+/*
+** Insert a new cell on pPage at cell index "i". pCell points to the
+** content of the cell.
+**
+** If the cell content will fit on the page, then put it there. If it
+** will not fit, then make a copy of the cell content into pTemp if
+** pTemp is not null. Regardless of pTemp, allocate a new entry
+** in pPage->aOvfl[] and make it point to the cell content (either
+** in pTemp or the original pCell) and also record its index.
+** Allocating a new entry in pPage->aCell[] implies that
+** pPage->nOverflow is incremented.
+*/
+static void insertCell(
+ MemPage *pPage, /* Page into which we are copying */
+ int i, /* New cell becomes the i-th cell of the page */
+ u8 *pCell, /* Content of the new cell */
+ int sz, /* Bytes of content in pCell */
+ u8 *pTemp /* Temp storage space for pCell, if needed */
+){
+ int idx; /* Where to write new cell content in data[] */
+ int j; /* Loop counter */
+ int top; /* First byte of content for any cell in data[] */
+ int end; /* First byte past the last cell pointer in data[] */
+ int ins; /* Index in data[] where new cell pointer is inserted */
+ int hdr; /* Offset into data[] of the page header */
+ int cellOffset; /* Address of first cell pointer in data[] */
+ u8 *data; /* The content of the whole page */
+ u8 *ptr; /* Used for moving information around in data[] */
+
+ assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
+ assert( sz==cellSizePtr(pPage, pCell) );
+ assert( sqlite3pager_iswriteable(pPage->aData) );
+ if( pPage->nOverflow || sz+2>pPage->nFree ){
+ if( pTemp ){
+ memcpy(pTemp, pCell, sz);
+ pCell = pTemp;
+ }
+ j = pPage->nOverflow++;
+ assert( j<sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0]) );
+ pPage->aOvfl[j].pCell = pCell;
+ pPage->aOvfl[j].idx = i;
+ pPage->nFree = 0;
+ }else{
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
+ top = get2byte(&data[hdr+5]);
+ cellOffset = pPage->cellOffset;
+ end = cellOffset + 2*pPage->nCell + 2;
+ ins = cellOffset + 2*i;
+ if( end > top - sz ){
+ defragmentPage(pPage);
+ top = get2byte(&data[hdr+5]);
+ assert( end + sz <= top );
+ }
+ idx = allocateSpace(pPage, sz);
+ assert( idx>0 );
+ assert( end <= get2byte(&data[hdr+5]) );
+ pPage->nCell++;
+ pPage->nFree -= 2;
+ memcpy(&data[idx], pCell, sz);
+ for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){
+ ptr[0] = ptr[-2];
+ ptr[1] = ptr[-1];
+ }
+ put2byte(&data[ins], idx);
+ put2byte(&data[hdr+3], pPage->nCell);
+ pPage->idxShift = 1;
+ pageIntegrity(pPage);
+ }
+}
+
+/*
+** Add a list of cells to a page. The page should be initially empty.
+** The cells are guaranteed to fit on the page.
+*/
+static void assemblePage(
+ MemPage *pPage, /* The page to be assemblied */
+ int nCell, /* The number of cells to add to this page */
+ u8 **apCell, /* Pointers to cell bodies */
+ int *aSize /* Sizes of the cells */
+){
+ int i; /* Loop counter */
+ int totalSize; /* Total size of all cells */
+ int hdr; /* Index of page header */
+ int cellptr; /* Address of next cell pointer */
+ int cellbody; /* Address of next cell body */
+ u8 *data; /* Data for the page */
+
+ assert( pPage->nOverflow==0 );
+ totalSize = 0;
+ for(i=0; i<nCell; i++){
+ totalSize += aSize[i];
+ }
+ assert( totalSize+2*nCell<=pPage->nFree );
+ assert( pPage->nCell==0 );
+ cellptr = pPage->cellOffset;
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
+ put2byte(&data[hdr+3], nCell);
+ cellbody = allocateSpace(pPage, totalSize);
+ assert( cellbody>0 );
+ assert( pPage->nFree >= 2*nCell );
+ pPage->nFree -= 2*nCell;
+ for(i=0; i<nCell; i++){
+ put2byte(&data[cellptr], cellbody);
+ memcpy(&data[cellbody], apCell[i], aSize[i]);
+ cellptr += 2;
+ cellbody += aSize[i];
+ }
+ assert( cellbody==pPage->pBt->usableSize );
+ pPage->nCell = nCell;
+}
+
+/*
+** GCC does not define the offsetof() macro so we'll have to do it
+** ourselves.
+*/
+#ifndef offsetof
+#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))
+#endif
+
+/*
+** The following parameters determine how many adjacent pages get involved
+** in a balancing operation. NN is the number of neighbors on either side
+** of the page that participate in the balancing operation. NB is the
+** total number of pages that participate, including the target page and
+** NN neighbors on either side.
+**
+** The minimum value of NN is 1 (of course). Increasing NN above 1
+** (to 2 or 3) gives a modest improvement in SELECT and DELETE performance
+** in exchange for a larger degradation in INSERT and UPDATE performance.
+** The value of NN appears to give the best results overall.
+*/
+#define NN 1 /* Number of neighbors on either side of pPage */
+#define NB (NN*2+1) /* Total pages involved in the balance */
+
+/* Forward reference */
+static int balance(MemPage*);
+
+/*
+** This routine redistributes Cells on pPage and up to NN*2 siblings
+** of pPage so that all pages have about the same amount of free space.
+** Usually NN siblings on either side of pPage is used in the balancing,
+** though more siblings might come from one side if pPage is the first
+** or last child of its parent. If pPage has fewer than 2*NN siblings
+** (something which can only happen if pPage is the root page or a
+** child of root) then all available siblings participate in the balancing.
+**
+** The number of siblings of pPage might be increased or decreased by one or
+** two in an effort to keep pages nearly full but not over full. The root page
+** is special and is allowed to be nearly empty. If pPage is
+** the root page, then the depth of the tree might be increased
+** or decreased by one, as necessary, to keep the root page from being
+** overfull or completely empty.
+**
+** Note that when this routine is called, some of the Cells on pPage
+** might not actually be stored in pPage->aData[]. This can happen
+** if the page is overfull. Part of the job of this routine is to
+** make sure all Cells for pPage once again fit in pPage->aData[].
+**
+** In the course of balancing the siblings of pPage, the parent of pPage
+** might become overfull or underfull. If that happens, then this routine
+** is called recursively on the parent.
+**
+** If this routine fails for any reason, it might leave the database
+** in a corrupted state. So if this routine fails, the database should
+** be rolled back.
+*/
+static int balance_nonroot(MemPage *pPage){
+ MemPage *pParent; /* The parent of pPage */
+ Btree *pBt; /* The whole database */
+ int nCell = 0; /* Number of cells in aCell[] */
+ int nOld; /* Number of pages in apOld[] */
+ int nNew; /* Number of pages in apNew[] */
+ int nDiv; /* Number of cells in apDiv[] */
+ int i, j, k; /* Loop counters */
+ int idx; /* Index of pPage in pParent->aCell[] */
+ int nxDiv; /* Next divider slot in pParent->aCell[] */
+ int rc; /* The return code */
+ int leafCorrection; /* 4 if pPage is a leaf. 0 if not */
+ int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
+ int usableSpace; /* Bytes in pPage beyond the header */
+ int pageFlags; /* Value of pPage->aData[0] */
+ int subtotal; /* Subtotal of bytes in cells on one page */
+ int iSpace = 0; /* First unused byte of aSpace[] */
+ int mxCellPerPage; /* Maximum number of cells in one page */
+ MemPage *apOld[NB]; /* pPage and up to two siblings */
+ Pgno pgnoOld[NB]; /* Page numbers for each page in apOld[] */
+ MemPage *apCopy[NB]; /* Private copies of apOld[] pages */
+ MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
+ Pgno pgnoNew[NB+2]; /* Page numbers for each page in apNew[] */
+ int idxDiv[NB]; /* Indices of divider cells in pParent */
+ u8 *apDiv[NB]; /* Divider cells in pParent */
+ int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
+ int szNew[NB+2]; /* Combined size of cells place on i-th page */
+ u8 **apCell; /* All cells begin balanced */
+ int *szCell; /* Local size of all cells in apCell[] */
+ u8 *aCopy[NB]; /* Space for holding data of apCopy[] */
+ u8 *aSpace; /* Space to hold copies of dividers cells */
+
+ /*
+ ** Find the parent page.
+ */
+ assert( pPage->isInit );
+ assert( sqlite3pager_iswriteable(pPage->aData) );
+ pBt = pPage->pBt;
+ pParent = pPage->pParent;
+ sqlite3pager_write(pParent->aData);
+ assert( pParent );
+ TRACE(("BALANCE: begin page %d child of %d\n", pPage->pgno, pParent->pgno));
+
+ /*
+ ** Allocate space for memory structures
+ */
+ mxCellPerPage = MX_CELL(pBt);
+ apCell = sqliteMallocRaw(
+ (mxCellPerPage+2)*NB*(sizeof(u8*)+sizeof(int))
+ + sizeof(MemPage)*NB
+ + pBt->pageSize*(5+NB)
+ );
+ if( apCell==0 ){
+ return SQLITE_NOMEM;
+ }
+ szCell = (int*)&apCell[(mxCellPerPage+2)*NB];
+ aCopy[0] = (u8*)&szCell[(mxCellPerPage+2)*NB];
+ for(i=1; i<NB; i++){
+ aCopy[i] = &aCopy[i-1][pBt->pageSize+sizeof(MemPage)];
+ }
+ aSpace = &aCopy[NB-1][pBt->pageSize+sizeof(MemPage)];
+
+ /*
+ ** Find the cell in the parent page whose left child points back
+ ** to pPage. The "idx" variable is the index of that cell. If pPage
+ ** is the rightmost child of pParent then set idx to pParent->nCell
+ */
+ if( pParent->idxShift ){
+ Pgno pgno;
+ pgno = pPage->pgno;
+ assert( pgno==sqlite3pager_pagenumber(pPage->aData) );
+ for(idx=0; idx<pParent->nCell; idx++){
+ if( get4byte(findCell(pParent, idx))==pgno ){
+ break;
+ }
+ }
+ assert( idx<pParent->nCell
+ || get4byte(&pParent->aData[pParent->hdrOffset+8])==pgno );
+ }else{
+ idx = pPage->idxParent;
+ }
+
+ /*
+ ** Initialize variables so that it will be safe to jump
+ ** directly to balance_cleanup at any moment.
+ */
+ nOld = nNew = 0;
+ sqlite3pager_ref(pParent->aData);
+
+ /*
+ ** Find sibling pages to pPage and the cells in pParent that divide
+ ** the siblings. An attempt is made to find NN siblings on either
+ ** side of pPage. More siblings are taken from one side, however, if
+ ** pPage there are fewer than NN siblings on the other side. If pParent
+ ** has NB or fewer children then all children of pParent are taken.
+ */
+ nxDiv = idx - NN;
+ if( nxDiv + NB > pParent->nCell ){
+ nxDiv = pParent->nCell - NB + 1;
+ }
+ if( nxDiv<0 ){
+ nxDiv = 0;
+ }
+ nDiv = 0;
+ for(i=0, k=nxDiv; i<NB; i++, k++){
+ if( k<pParent->nCell ){
+ idxDiv[i] = k;
+ apDiv[i] = findCell(pParent, k);
+ nDiv++;
+ assert( !pParent->leaf );
+ pgnoOld[i] = get4byte(apDiv[i]);
+ }else if( k==pParent->nCell ){
+ pgnoOld[i] = get4byte(&pParent->aData[pParent->hdrOffset+8]);
+ }else{
+ break;
+ }
+ rc = getAndInitPage(pBt, pgnoOld[i], &apOld[i], pParent);
+ if( rc ) goto balance_cleanup;
+ apOld[i]->idxParent = k;
+ apCopy[i] = 0;
+ assert( i==nOld );
+ nOld++;
+ }
+
+ /*
+ ** Make copies of the content of pPage and its siblings into aOld[].
+ ** The rest of this function will use data from the copies rather
+ ** that the original pages since the original pages will be in the
+ ** process of being overwritten.
+ */
+ for(i=0; i<nOld; i++){
+ MemPage *p = apCopy[i] = (MemPage*)&aCopy[i][pBt->pageSize];
+ p->aData = &((u8*)p)[-pBt->pageSize];
+ memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage));
+ p->aData = &((u8*)p)[-pBt->pageSize];
+ }
+
+ /*
+ ** Load pointers to all cells on sibling pages and the divider cells
+ ** into the local apCell[] array. Make copies of the divider cells
+ ** into space obtained form aSpace[] and remove the the divider Cells
+ ** from pParent.
+ **
+ ** If the siblings are on leaf pages, then the child pointers of the
+ ** divider cells are stripped from the cells before they are copied
+ ** into aSpace[]. In this way, all cells in apCell[] are without
+ ** child pointers. If siblings are not leaves, then all cell in
+ ** apCell[] include child pointers. Either way, all cells in apCell[]
+ ** are alike.
+ **
+ ** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf.
+ ** leafData: 1 if pPage holds key+data and pParent holds only keys.
+ */
+ nCell = 0;
+ leafCorrection = pPage->leaf*4;
+ leafData = pPage->leafData && pPage->leaf;
+ for(i=0; i<nOld; i++){
+ MemPage *pOld = apCopy[i];
+ int limit = pOld->nCell+pOld->nOverflow;
+ for(j=0; j<limit; j++){
+ apCell[nCell] = findOverflowCell(pOld, j);
+ szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
+ nCell++;
+ }
+ if( i<nOld-1 ){
+ int sz = cellSizePtr(pParent, apDiv[i]);
+ if( leafData ){
+ /* With the LEAFDATA flag, pParent cells hold only INTKEYs that
+ ** are duplicates of keys on the child pages. We need to remove
+ ** the divider cells from pParent, but the dividers cells are not
+ ** added to apCell[] because they are duplicates of child cells.
+ */
+ dropCell(pParent, nxDiv, sz);
+ }else{
+ u8 *pTemp;
+ szCell[nCell] = sz;
+ pTemp = &aSpace[iSpace];
+ iSpace += sz;
+ assert( iSpace<=pBt->pageSize*5 );
+ memcpy(pTemp, apDiv[i], sz);
+ apCell[nCell] = pTemp+leafCorrection;
+ dropCell(pParent, nxDiv, sz);
+ szCell[nCell] -= leafCorrection;
+ assert( get4byte(pTemp)==pgnoOld[i] );
+ if( !pOld->leaf ){
+ assert( leafCorrection==0 );
+ /* The right pointer of the child page pOld becomes the left
+ ** pointer of the divider cell */
+ memcpy(apCell[nCell], &pOld->aData[pOld->hdrOffset+8], 4);
+ }else{
+ assert( leafCorrection==4 );
+ }
+ nCell++;
+ }
+ }
+ }
+
+ /*
+ ** Figure out the number of pages needed to hold all nCell cells.
+ ** Store this number in "k". Also compute szNew[] which is the total
+ ** size of all cells on the i-th page and cntNew[] which is the index
+ ** in apCell[] of the cell that divides page i from page i+1.
+ ** cntNew[k] should equal nCell.
+ **
+ ** Values computed by this block:
+ **
+ ** k: The total number of sibling pages
+ ** szNew[i]: Spaced used on the i-th sibling page.
+ ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to
+ ** the right of the i-th sibling page.
+ ** usableSpace: Number of bytes of space available on each sibling.
+ **
+ */
+ usableSpace = pBt->usableSize - 12 + leafCorrection;
+ for(subtotal=k=i=0; i<nCell; i++){
+ subtotal += szCell[i] + 2;
+ if( subtotal > usableSpace ){
+ szNew[k] = subtotal - szCell[i];
+ cntNew[k] = i;
+ if( leafData ){ i--; }
+ subtotal = 0;
+ k++;
+ }
+ }
+ szNew[k] = subtotal;
+ cntNew[k] = nCell;
+ k++;
+
+ /*
+ ** The packing computed by the previous block is biased toward the siblings
+ ** on the left side. The left siblings are always nearly full, while the
+ ** right-most sibling might be nearly empty. This block of code attempts
+ ** to adjust the packing of siblings to get a better balance.
+ **
+ ** This adjustment is more than an optimization. The packing above might
+ ** be so out of balance as to be illegal. For example, the right-most
+ ** sibling might be completely empty. This adjustment is not optional.
+ */
+ for(i=k-1; i>0; i--){
+ int szRight = szNew[i]; /* Size of sibling on the right */
+ int szLeft = szNew[i-1]; /* Size of sibling on the left */
+ int r; /* Index of right-most cell in left sibling */
+ int d; /* Index of first cell to the left of right sibling */
+
+ r = cntNew[i-1] - 1;
+ d = r + 1 - leafData;
+ while( szRight==0 || szRight+szCell[d]+2<=szLeft-(szCell[r]+2) ){
+ szRight += szCell[d] + 2;
+ szLeft -= szCell[r] + 2;
+ cntNew[i-1]--;
+ r = cntNew[i-1] - 1;
+ d = r + 1 - leafData;
+ }
+ szNew[i] = szRight;
+ szNew[i-1] = szLeft;
+ }
+ assert( cntNew[0]>0 );
+
+ /*
+ ** Allocate k new pages. Reuse old pages where possible.
+ */
+ assert( pPage->pgno>1 );
+ pageFlags = pPage->aData[0];
+ for(i=0; i<k; i++){
+ MemPage *pNew;
+ if( i<nOld ){
+ pNew = apNew[i] = apOld[i];
+ pgnoNew[i] = pgnoOld[i];
+ apOld[i] = 0;
+ sqlite3pager_write(pNew->aData);
+ }else{
+ rc = allocatePage(pBt, &pNew, &pgnoNew[i], pgnoNew[i-1]);
+ if( rc ) goto balance_cleanup;
+ apNew[i] = pNew;
+ }
+ nNew++;
+ zeroPage(pNew, pageFlags);
+ }
+
+ /* Free any old pages that were not reused as new pages.
+ */
+ while( i<nOld ){
+ rc = freePage(apOld[i]);
+ if( rc ) goto balance_cleanup;
+ releasePage(apOld[i]);
+ apOld[i] = 0;
+ i++;
+ }
+
+ /*
+ ** Put the new pages in accending order. This helps to
+ ** keep entries in the disk file in order so that a scan
+ ** of the table is a linear scan through the file. That
+ ** in turn helps the operating system to deliver pages
+ ** from the disk more rapidly.
+ **
+ ** An O(n^2) insertion sort algorithm is used, but since
+ ** n is never more than NB (a small constant), that should
+ ** not be a problem.
+ **
+ ** When NB==3, this one optimization makes the database
+ ** about 25% faster for large insertions and deletions.
+ */
+ for(i=0; i<k-1; i++){
+ int minV = pgnoNew[i];
+ int minI = i;
+ for(j=i+1; j<k; j++){
+ if( pgnoNew[j]<(unsigned)minV ){
+ minI = j;
+ minV = pgnoNew[j];
+ }
+ }
+ if( minI>i ){
+ int t;
+ MemPage *pT;
+ t = pgnoNew[i];
+ pT = apNew[i];
+ pgnoNew[i] = pgnoNew[minI];
+ apNew[i] = apNew[minI];
+ pgnoNew[minI] = t;
+ apNew[minI] = pT;
+ }
+ }
+ TRACE(("BALANCE: old: %d %d %d new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n",
+ pgnoOld[0],
+ nOld>=2 ? pgnoOld[1] : 0,
+ nOld>=3 ? pgnoOld[2] : 0,
+ pgnoNew[0], szNew[0],
+ nNew>=2 ? pgnoNew[1] : 0, nNew>=2 ? szNew[1] : 0,
+ nNew>=3 ? pgnoNew[2] : 0, nNew>=3 ? szNew[2] : 0,
+ nNew>=4 ? pgnoNew[3] : 0, nNew>=4 ? szNew[3] : 0,
+ nNew>=5 ? pgnoNew[4] : 0, nNew>=5 ? szNew[4] : 0));
+
+
+ /*
+ ** Evenly distribute the data in apCell[] across the new pages.
+ ** Insert divider cells into pParent as necessary.
+ */
+ j = 0;
+ for(i=0; i<nNew; i++){
+ MemPage *pNew = apNew[i];
+ assert( pNew->pgno==pgnoNew[i] );
+ assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]);
+ j = cntNew[i];
+ assert( pNew->nCell>0 );
+ assert( pNew->nOverflow==0 );
+ if( i<nNew-1 && j<nCell ){
+ u8 *pCell;
+ u8 *pTemp;
+ int sz;
+ pCell = apCell[j];
+ sz = szCell[j] + leafCorrection;
+ if( !pNew->leaf ){
+ memcpy(&pNew->aData[8], pCell, 4);
+ pTemp = 0;
+ }else if( leafData ){
+ CellInfo info;
+ j--;
+ parseCellPtr(pNew, apCell[j], &info);
+ pCell = &aSpace[iSpace];
+ fillInCell(pParent, pCell, 0, info.nKey, 0, 0, &sz);
+ iSpace += sz;
+ assert( iSpace<=pBt->pageSize*5 );
+ pTemp = 0;
+ }else{
+ pCell -= 4;
+ pTemp = &aSpace[iSpace];
+ iSpace += sz;
+ assert( iSpace<=pBt->pageSize*5 );
+ }
+ insertCell(pParent, nxDiv, pCell, sz, pTemp);
+ put4byte(findOverflowCell(pParent,nxDiv), pNew->pgno);
+ j++;
+ nxDiv++;
+ }
+ }
+ assert( j==nCell );
+ if( (pageFlags & PTF_LEAF)==0 ){
+ memcpy(&apNew[nNew-1]->aData[8], &apCopy[nOld-1]->aData[8], 4);
+ }
+ if( nxDiv==pParent->nCell+pParent->nOverflow ){
+ /* Right-most sibling is the right-most child of pParent */
+ put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew[nNew-1]);
+ }else{
+ /* Right-most sibling is the left child of the first entry in pParent
+ ** past the right-most divider entry */
+ put4byte(findOverflowCell(pParent, nxDiv), pgnoNew[nNew-1]);
+ }
+
+ /*
+ ** Reparent children of all cells.
+ */
+ for(i=0; i<nNew; i++){
+ reparentChildPages(apNew[i]);
+ }
+ reparentChildPages(pParent);
+
+ /*
+ ** Balance the parent page. Note that the current page (pPage) might
+ ** have been added to the freelist is it might no longer be initialized.
+ ** But the parent page will always be initialized.
+ */
+ assert( pParent->isInit );
+ /* assert( pPage->isInit ); // No! pPage might have been added to freelist */
+ /* pageIntegrity(pPage); // No! pPage might have been added to freelist */
+ rc = balance(pParent);
+
+ /*
+ ** Cleanup before returning.
+ */
+balance_cleanup:
+ sqliteFree(apCell);
+ for(i=0; i<nOld; i++){
+ releasePage(apOld[i]);
+ }
+ for(i=0; i<nNew; i++){
+ releasePage(apNew[i]);
+ }
+ releasePage(pParent);
+ TRACE(("BALANCE: finished with %d: old=%d new=%d cells=%d\n",
+ pPage->pgno, nOld, nNew, nCell));
+ return rc;
+}
+
+/*
+** This routine is called for the root page of a btree when the root
+** page contains no cells. This is an opportunity to make the tree
+** shallower by one level.
+*/
+static int balance_shallower(MemPage *pPage){
+ MemPage *pChild; /* The only child page of pPage */
+ Pgno pgnoChild; /* Page number for pChild */
+ int rc = SQLITE_OK; /* Return code from subprocedures */
+ Btree *pBt; /* The main BTree structure */
+ int mxCellPerPage; /* Maximum number of cells per page */
+ u8 **apCell; /* All cells from pages being balanced */
+ int *szCell; /* Local size of all cells */
+
+ assert( pPage->pParent==0 );
+ assert( pPage->nCell==0 );
+ pBt = pPage->pBt;
+ mxCellPerPage = MX_CELL(pBt);
+ apCell = sqliteMallocRaw( mxCellPerPage*(sizeof(u8*)+sizeof(int)) );
+ if( apCell==0 ) return SQLITE_NOMEM;
+ szCell = (int*)&apCell[mxCellPerPage];
+ if( pPage->leaf ){
+ /* The table is completely empty */
+ TRACE(("BALANCE: empty table %d\n", pPage->pgno));
+ }else{
+ /* The root page is empty but has one child. Transfer the
+ ** information from that one child into the root page if it
+ ** will fit. This reduces the depth of the tree by one.
+ **
+ ** If the root page is page 1, it has less space available than
+ ** its child (due to the 100 byte header that occurs at the beginning
+ ** of the database fle), so it might not be able to hold all of the
+ ** information currently contained in the child. If this is the
+ ** case, then do not do the transfer. Leave page 1 empty except
+ ** for the right-pointer to the child page. The child page becomes
+ ** the virtual root of the tree.
+ */
+ pgnoChild = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ assert( pgnoChild>0 );
+ assert( pgnoChild<=sqlite3pager_pagecount(pPage->pBt->pPager) );
+ rc = getPage(pPage->pBt, pgnoChild, &pChild);
+ if( rc ) goto end_shallow_balance;
+ if( pPage->pgno==1 ){
+ rc = initPage(pChild, pPage);
+ if( rc ) goto end_shallow_balance;
+ assert( pChild->nOverflow==0 );
+ if( pChild->nFree>=100 ){
+ /* The child information will fit on the root page, so do the
+ ** copy */
+ int i;
+ zeroPage(pPage, pChild->aData[0]);
+ for(i=0; i<pChild->nCell; i++){
+ apCell[i] = findCell(pChild,i);
+ szCell[i] = cellSizePtr(pChild, apCell[i]);
+ }
+ assemblePage(pPage, pChild->nCell, apCell, szCell);
+ freePage(pChild);
+ TRACE(("BALANCE: child %d transfer to page 1\n", pChild->pgno));
+ }else{
+ /* The child has more information that will fit on the root.
+ ** The tree is already balanced. Do nothing. */
+ TRACE(("BALANCE: child %d will not fit on page 1\n", pChild->pgno));
+ }
+ }else{
+ memcpy(pPage->aData, pChild->aData, pPage->pBt->usableSize);
+ pPage->isInit = 0;
+ pPage->pParent = 0;
+ rc = initPage(pPage, 0);
+ assert( rc==SQLITE_OK );
+ freePage(pChild);
+ TRACE(("BALANCE: transfer child %d into root %d\n",
+ pChild->pgno, pPage->pgno));
+ }
+ reparentChildPages(pPage);
+ releasePage(pChild);
+ }
+end_shallow_balance:
+ sqliteFree(apCell);
+ return rc;
+}
+
+
+/*
+** The root page is overfull
+**
+** When this happens, Create a new child page and copy the
+** contents of the root into the child. Then make the root
+** page an empty page with rightChild pointing to the new
+** child. Finally, call balance_internal() on the new child
+** to cause it to split.
+*/
+static int balance_deeper(MemPage *pPage){
+ int rc; /* Return value from subprocedures */
+ MemPage *pChild; /* Pointer to a new child page */
+ Pgno pgnoChild; /* Page number of the new child page */
+ Btree *pBt; /* The BTree */
+ int usableSize; /* Total usable size of a page */
+ u8 *data; /* Content of the parent page */
+ u8 *cdata; /* Content of the child page */
+ int hdr; /* Offset to page header in parent */
+ int brk; /* Offset to content of first cell in parent */
+
+ assert( pPage->pParent==0 );
+ assert( pPage->nOverflow>0 );
+ pBt = pPage->pBt;
+ rc = allocatePage(pBt, &pChild, &pgnoChild, pPage->pgno);
+ if( rc ) return rc;
+ assert( sqlite3pager_iswriteable(pChild->aData) );
+ usableSize = pBt->usableSize;
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
+ brk = get2byte(&data[hdr+5]);
+ cdata = pChild->aData;
+ memcpy(cdata, &data[hdr], pPage->cellOffset+2*pPage->nCell-hdr);
+ memcpy(&cdata[brk], &data[brk], usableSize-brk);
+ rc = initPage(pChild, pPage);
+ if( rc ) return rc;
+ memcpy(pChild->aOvfl, pPage->aOvfl, pPage->nOverflow*sizeof(pPage->aOvfl[0]));
+ pChild->nOverflow = pPage->nOverflow;
+ if( pChild->nOverflow ){
+ pChild->nFree = 0;
+ }
+ assert( pChild->nCell==pPage->nCell );
+ zeroPage(pPage, pChild->aData[0] & ~PTF_LEAF);
+ put4byte(&pPage->aData[pPage->hdrOffset+8], pgnoChild);
+ TRACE(("BALANCE: copy root %d into %d\n", pPage->pgno, pChild->pgno));
+ rc = balance_nonroot(pChild);
+ releasePage(pChild);
+ return rc;
+}
+
+/*
+** Decide if the page pPage needs to be balanced. If balancing is
+** required, call the appropriate balancing routine.
+*/
+static int balance(MemPage *pPage){
+ int rc = SQLITE_OK;
+ if( pPage->pParent==0 ){
+ if( pPage->nOverflow>0 ){
+ rc = balance_deeper(pPage);
+ }
+ if( pPage->nCell==0 ){
+ rc = balance_shallower(pPage);
+ }
+ }else{
+ if( pPage->nOverflow>0 || pPage->nFree>pPage->pBt->usableSize*2/3 ){
+ rc = balance_nonroot(pPage);
+ }
+ }
+ return rc;
+}
+
+/*
+** This routine checks all cursors that point to table pgnoRoot.
+** If any of those cursors other than pExclude were opened with
+** wrFlag==0 then this routine returns SQLITE_LOCKED. If all
+** cursors that point to pgnoRoot were opened with wrFlag==1
+** then this routine returns SQLITE_OK.
+**
+** In addition to checking for read-locks (where a read-lock
+** means a cursor opened with wrFlag==0) this routine also moves
+** all cursors other than pExclude so that they are pointing to the
+** first Cell on root page. This is necessary because an insert
+** or delete might change the number of cells on a page or delete
+** a page entirely and we do not want to leave any cursors
+** pointing to non-existant pages or cells.
+*/
+static int checkReadLocks(Btree *pBt, Pgno pgnoRoot, BtCursor *pExclude){
+ BtCursor *p;
+ for(p=pBt->pCursor; p; p=p->pNext){
+ if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue;
+ if( p->wrFlag==0 ) return SQLITE_LOCKED;
+ if( p->pPage->pgno!=p->pgnoRoot ){
+ moveToRoot(p);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Insert a new record into the BTree. The key is given by (pKey,nKey)
+** and the data is given by (pData,nData). The cursor is used only to
+** define what table the record should be inserted into. The cursor
+** is left pointing at a random location.
+**
+** For an INTKEY table, only the nKey value of the key is used. pKey is
+** ignored. For a ZERODATA table, the pData and nData are both ignored.
+*/
+int sqlite3BtreeInsert(
+ BtCursor *pCur, /* Insert data into the table of this cursor */
+ const void *pKey, i64 nKey, /* The key of the new record */
+ const void *pData, int nData /* The data of the new record */
+){
+ int rc;
+ int loc;
+ int szNew;
+ MemPage *pPage;
+ Btree *pBt = pCur->pBt;
+ unsigned char *oldCell;
+ unsigned char *newCell = 0;
+
+ if( pCur->status ){
+ return pCur->status; /* A rollback destroyed this cursor */
+ }
+ if( pBt->inTrans!=TRANS_WRITE ){
+ /* Must start a transaction before doing an insert */
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ assert( !pBt->readOnly );
+ if( !pCur->wrFlag ){
+ return SQLITE_PERM; /* Cursor not open for writing */
+ }
+ if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ return SQLITE_LOCKED; /* The table pCur points to has a read lock */
+ }
+ rc = sqlite3BtreeMoveto(pCur, pKey, nKey, &loc);
+ if( rc ) return rc;
+ pPage = pCur->pPage;
+ assert( pPage->intKey || nKey>=0 );
+ assert( pPage->leaf || !pPage->leafData );
+ TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
+ pCur->pgnoRoot, nKey, nData, pPage->pgno,
+ loc==0 ? "overwrite" : "new entry"));
+ assert( pPage->isInit );
+ rc = sqlite3pager_write(pPage->aData);
+ if( rc ) return rc;
+ newCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
+ if( newCell==0 ) return SQLITE_NOMEM;
+ rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, &szNew);
+ if( rc ) goto end_insert;
+ assert( szNew==cellSizePtr(pPage, newCell) );
+ assert( szNew<=MX_CELL_SIZE(pBt) );
+ if( loc==0 && pCur->isValid ){
+ int szOld;
+ assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
+ oldCell = findCell(pPage, pCur->idx);
+ if( !pPage->leaf ){
+ memcpy(newCell, oldCell, 4);
+ }
+ szOld = cellSizePtr(pPage, oldCell);
+ rc = clearCell(pPage, oldCell);
+ if( rc ) goto end_insert;
+ dropCell(pPage, pCur->idx, szOld);
+ }else if( loc<0 && pPage->nCell>0 ){
+ assert( pPage->leaf );
+ pCur->idx++;
+ pCur->info.nSize = 0;
+ }else{
+ assert( pPage->leaf );
+ }
+ insertCell(pPage, pCur->idx, newCell, szNew, 0);
+ rc = balance(pPage);
+ /* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
+ /* fflush(stdout); */
+ moveToRoot(pCur);
+end_insert:
+ sqliteFree(newCell);
+ return rc;
+}
+
+/*
+** Delete the entry that the cursor is pointing to. The cursor
+** is left pointing at a random location.
+*/
+int sqlite3BtreeDelete(BtCursor *pCur){
+ MemPage *pPage = pCur->pPage;
+ unsigned char *pCell;
+ int rc;
+ Pgno pgnoChild = 0;
+ Btree *pBt = pCur->pBt;
+
+ assert( pPage->isInit );
+ if( pCur->status ){
+ return pCur->status; /* A rollback destroyed this cursor */
+ }
+ if( pBt->inTrans!=TRANS_WRITE ){
+ /* Must start a transaction before doing a delete */
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ assert( !pBt->readOnly );
+ if( pCur->idx >= pPage->nCell ){
+ return SQLITE_ERROR; /* The cursor is not pointing to anything */
+ }
+ if( !pCur->wrFlag ){
+ return SQLITE_PERM; /* Did not open this cursor for writing */
+ }
+ if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){
+ return SQLITE_LOCKED; /* The table pCur points to has a read lock */
+ }
+ rc = sqlite3pager_write(pPage->aData);
+ if( rc ) return rc;
+ pCell = findCell(pPage, pCur->idx);
+ if( !pPage->leaf ){
+ pgnoChild = get4byte(pCell);
+ }
+ clearCell(pPage, pCell);
+ if( !pPage->leaf ){
+ /*
+ ** The entry we are about to delete is not a leaf so if we do not
+ ** do something we will leave a hole on an internal page.
+ ** We have to fill the hole by moving in a cell from a leaf. The
+ ** next Cell after the one to be deleted is guaranteed to exist and
+ ** to be a leaf so we can use it.
+ */
+ BtCursor leafCur;
+ unsigned char *pNext;
+ int szNext;
+ int notUsed;
+ unsigned char *tempCell;
+ assert( !pPage->leafData );
+ getTempCursor(pCur, &leafCur);
+ rc = sqlite3BtreeNext(&leafCur, &notUsed);
+ if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_NOMEM ){
+ rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }
+ return rc;
+ }
+ rc = sqlite3pager_write(leafCur.pPage->aData);
+ if( rc ) return rc;
+ TRACE(("DELETE: table=%d delete internal from %d replace from leaf %d\n",
+ pCur->pgnoRoot, pPage->pgno, leafCur.pPage->pgno));
+ dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
+ pNext = findCell(leafCur.pPage, leafCur.idx);
+ szNext = cellSizePtr(leafCur.pPage, pNext);
+ assert( MX_CELL_SIZE(pBt)>=szNext+4 );
+ tempCell = sqliteMallocRaw( MX_CELL_SIZE(pBt) );
+ if( tempCell==0 ) return SQLITE_NOMEM;
+ insertCell(pPage, pCur->idx, pNext-4, szNext+4, tempCell);
+ put4byte(findOverflowCell(pPage, pCur->idx), pgnoChild);
+ rc = balance(pPage);
+ sqliteFree(tempCell);
+ if( rc ) return rc;
+ dropCell(leafCur.pPage, leafCur.idx, szNext);
+ rc = balance(leafCur.pPage);
+ releaseTempCursor(&leafCur);
+ }else{
+ TRACE(("DELETE: table=%d delete from leaf %d\n",
+ pCur->pgnoRoot, pPage->pgno));
+ dropCell(pPage, pCur->idx, cellSizePtr(pPage, pCell));
+ rc = balance(pPage);
+ }
+ moveToRoot(pCur);
+ return rc;
+}
+
+/*
+** Create a new BTree table. Write into *piTable the page
+** number for the root page of the new table.
+**
+** The type of type is determined by the flags parameter. Only the
+** following values of flags are currently in use. Other values for
+** flags might not work:
+**
+** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
+** BTREE_ZERODATA Used for SQL indices
+*/
+int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){
+ MemPage *pRoot;
+ Pgno pgnoRoot;
+ int rc;
+ if( pBt->inTrans!=TRANS_WRITE ){
+ /* Must start a transaction first */
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ if( pBt->readOnly ){
+ return SQLITE_READONLY;
+ }
+ rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
+ if( rc ) return rc;
+ assert( sqlite3pager_iswriteable(pRoot->aData) );
+ zeroPage(pRoot, flags | PTF_LEAF);
+ sqlite3pager_unref(pRoot->aData);
+ *piTable = (int)pgnoRoot;
+ return SQLITE_OK;
+}
+
+/*
+** Erase the given database page and all its children. Return
+** the page to the freelist.
+*/
+static int clearDatabasePage(
+ Btree *pBt, /* The BTree that contains the table */
+ Pgno pgno, /* Page number to clear */
+ MemPage *pParent, /* Parent page. NULL for the root */
+ int freePageFlag /* Deallocate page if true */
+){
+ MemPage *pPage;
+ int rc;
+ unsigned char *pCell;
+ int i;
+
+ rc = getAndInitPage(pBt, pgno, &pPage, pParent);
+ if( rc ) return rc;
+ rc = sqlite3pager_write(pPage->aData);
+ if( rc ) return rc;
+ for(i=0; i<pPage->nCell; i++){
+ pCell = findCell(pPage, i);
+ if( !pPage->leaf ){
+ rc = clearDatabasePage(pBt, get4byte(pCell), pPage->pParent, 1);
+ if( rc ) return rc;
+ }
+ rc = clearCell(pPage, pCell);
+ if( rc ) return rc;
+ }
+ if( !pPage->leaf ){
+ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), pPage->pParent, 1);
+ if( rc ) return rc;
+ }
+ if( freePageFlag ){
+ rc = freePage(pPage);
+ }else{
+ zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
+ }
+ releasePage(pPage);
+ return rc;
+}
+
+/*
+** Delete all information from a single table in the database. iTable is
+** the page number of the root of the table. After this routine returns,
+** the root page is empty, but still exists.
+**
+** This routine will fail with SQLITE_LOCKED if there are any open
+** read cursors on the table. Open write cursors are moved to the
+** root of the table.
+*/
+int sqlite3BtreeClearTable(Btree *pBt, int iTable){
+ int rc;
+ BtCursor *pCur;
+ if( pBt->inTrans!=TRANS_WRITE ){
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ if( pCur->pgnoRoot==(Pgno)iTable ){
+ if( pCur->wrFlag==0 ) return SQLITE_LOCKED;
+ moveToRoot(pCur);
+ }
+ }
+ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
+ if( rc ){
+ sqlite3BtreeRollback(pBt);
+ }
+ return rc;
+}
+
+/*
+** Erase all information in a table and add the root of the table to
+** the freelist. Except, the root of the principle table (the one on
+** page 1) is never added to the freelist.
+**
+** This routine will fail with SQLITE_LOCKED if there are any open
+** cursors on the table.
+*/
+int sqlite3BtreeDropTable(Btree *pBt, int iTable){
+ int rc;
+ MemPage *pPage;
+ BtCursor *pCur;
+ if( pBt->inTrans!=TRANS_WRITE ){
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ if( pCur->pgnoRoot==(Pgno)iTable ){
+ return SQLITE_LOCKED; /* Cannot drop a table that has a cursor */
+ }
+ }
+ rc = getPage(pBt, (Pgno)iTable, &pPage);
+ if( rc ) return rc;
+ rc = sqlite3BtreeClearTable(pBt, iTable);
+ if( rc ) return rc;
+ if( iTable>1 ){
+ rc = freePage(pPage);
+ }else{
+ zeroPage(pPage, PTF_INTKEY|PTF_LEAF );
+ }
+ releasePage(pPage);
+ return rc;
+}
+
+
+/*
+** Read the meta-information out of a database file. Meta[0]
+** is the number of free pages currently in the database. Meta[1]
+** through meta[15] are available for use by higher layers. Meta[0]
+** is read-only, the others are read/write.
+**
+** The schema layer numbers meta values differently. At the schema
+** layer (and the SetCookie and ReadCookie opcodes) the number of
+** free pages is not visible. So Cookie[0] is the same as Meta[1].
+*/
+int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){
+ int rc;
+ unsigned char *pP1;
+
+ assert( idx>=0 && idx<=15 );
+ rc = sqlite3pager_get(pBt->pPager, 1, (void**)&pP1);
+ if( rc ) return rc;
+ *pMeta = get4byte(&pP1[36 + idx*4]);
+ sqlite3pager_unref(pP1);
+
+ /* The current implementation is unable to handle writes to an autovacuumed
+ ** database. So make such a database readonly. */
+ if( idx==4 && *pMeta>0 ) pBt->readOnly = 1;
+
+ return SQLITE_OK;
+}
+
+/*
+** Write meta-information back into the database. Meta[0] is
+** read-only and may not be written.
+*/
+int sqlite3BtreeUpdateMeta(Btree *pBt, int idx, u32 iMeta){
+ unsigned char *pP1;
+ int rc;
+ assert( idx>=1 && idx<=15 );
+ if( pBt->inTrans!=TRANS_WRITE ){
+ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }
+ assert( pBt->pPage1!=0 );
+ pP1 = pBt->pPage1->aData;
+ rc = sqlite3pager_write(pP1);
+ if( rc ) return rc;
+ put4byte(&pP1[36 + idx*4], iMeta);
+ return SQLITE_OK;
+}
+
+/*
+** Return the flag byte at the beginning of the page that the cursor
+** is currently pointing to.
+*/
+int sqlite3BtreeFlags(BtCursor *pCur){
+ MemPage *pPage = pCur->pPage;
+ return pPage ? pPage->aData[pPage->hdrOffset] : 0;
+}
+
+/*
+** Print a disassembly of the given page on standard output. This routine
+** is used for debugging and testing only.
+*/
+#ifdef SQLITE_TEST
+int sqlite3BtreePageDump(Btree *pBt, int pgno, int recursive){
+ int rc;
+ MemPage *pPage;
+ int i, j, c;
+ int nFree;
+ u16 idx;
+ int hdr;
+ int nCell;
+ int isInit;
+ unsigned char *data;
+ char range[20];
+ unsigned char payload[20];
+
+ rc = getPage(pBt, (Pgno)pgno, &pPage);
+ isInit = pPage->isInit;
+ if( pPage->isInit==0 ){
+ initPage(pPage, 0);
+ }
+ if( rc ){
+ return rc;
+ }
+ hdr = pPage->hdrOffset;
+ data = pPage->aData;
+ c = data[hdr];
+ pPage->intKey = (c & (PTF_INTKEY|PTF_LEAFDATA))!=0;
+ pPage->zeroData = (c & PTF_ZERODATA)!=0;
+ pPage->leafData = (c & PTF_LEAFDATA)!=0;
+ pPage->leaf = (c & PTF_LEAF)!=0;
+ pPage->hasData = !(pPage->zeroData || (!pPage->leaf && pPage->leafData));
+ nCell = get2byte(&data[hdr+3]);
+ sqlite3DebugPrintf("PAGE %d: flags=0x%02x frag=%d parent=%d\n", pgno,
+ data[hdr], data[hdr+7],
+ (pPage->isInit && pPage->pParent) ? pPage->pParent->pgno : 0);
+ assert( hdr == (pgno==1 ? 100 : 0) );
+ idx = hdr + 12 - pPage->leaf*4;
+ for(i=0; i<nCell; i++){
+ CellInfo info;
+ Pgno child;
+ unsigned char *pCell;
+ int sz;
+ int addr;
+
+ addr = get2byte(&data[idx + 2*i]);
+ pCell = &data[addr];
+ parseCellPtr(pPage, pCell, &info);
+ sz = info.nSize;
+ sprintf(range,"%d..%d", addr, addr+sz-1);
+ if( pPage->leaf ){
+ child = 0;
+ }else{
+ child = get4byte(pCell);
+ }
+ sz = info.nData;
+ if( !pPage->intKey ) sz += info.nKey;
+ if( sz>sizeof(payload)-1 ) sz = sizeof(payload)-1;
+ memcpy(payload, &pCell[info.nHeader], sz);
+ for(j=0; j<sz; j++){
+ if( payload[j]<0x20 || payload[j]>0x7f ) payload[j] = '.';
+ }
+ payload[sz] = 0;
+ sqlite3DebugPrintf(
+ "cell %2d: i=%-10s chld=%-4d nk=%-4lld nd=%-4d payload=%s\n",
+ i, range, child, info.nKey, info.nData, payload
+ );
+ }
+ if( !pPage->leaf ){
+ sqlite3DebugPrintf("right_child: %d\n", get4byte(&data[hdr+8]));
+ }
+ nFree = 0;
+ i = 0;
+ idx = get2byte(&data[hdr+1]);
+ while( idx>0 && idx<pPage->pBt->usableSize ){
+ int sz = get2byte(&data[idx+2]);
+ sprintf(range,"%d..%d", idx, idx+sz-1);
+ nFree += sz;
+ sqlite3DebugPrintf("freeblock %2d: i=%-10s size=%-4d total=%d\n",
+ i, range, sz, nFree);
+ idx = get2byte(&data[idx]);
+ i++;
+ }
+ if( idx!=0 ){
+ sqlite3DebugPrintf("ERROR: next freeblock index out of range: %d\n", idx);
+ }
+ if( recursive && !pPage->leaf ){
+ for(i=0; i<nCell; i++){
+ unsigned char *pCell = findCell(pPage, i);
+ sqlite3BtreePageDump(pBt, get4byte(pCell), 1);
+ idx = get2byte(pCell);
+ }
+ sqlite3BtreePageDump(pBt, get4byte(&data[hdr+8]), 1);
+ }
+ pPage->isInit = isInit;
+ sqlite3pager_unref(data);
+ fflush(stdout);
+ return SQLITE_OK;
+}
+#endif
+
+#ifdef SQLITE_TEST
+/*
+** Fill aResult[] with information about the entry and page that the
+** cursor is pointing to.
+**
+** aResult[0] = The page number
+** aResult[1] = The entry number
+** aResult[2] = Total number of entries on this page
+** aResult[3] = Cell size (local payload + header)
+** aResult[4] = Number of free bytes on this page
+** aResult[5] = Number of free blocks on the page
+** aResult[6] = Total payload size (local + overflow)
+** aResult[7] = Header size in bytes
+** aResult[8] = Local payload size
+** aResult[9] = Parent page number
+**
+** This routine is used for testing and debugging only.
+*/
+int sqlite3BtreeCursorInfo(BtCursor *pCur, int *aResult, int upCnt){
+ int cnt, idx;
+ MemPage *pPage = pCur->pPage;
+ BtCursor tmpCur;
+
+ pageIntegrity(pPage);
+ assert( pPage->isInit );
+ getTempCursor(pCur, &tmpCur);
+ while( upCnt-- ){
+ moveToParent(&tmpCur);
+ }
+ pPage = tmpCur.pPage;
+ pageIntegrity(pPage);
+ aResult[0] = sqlite3pager_pagenumber(pPage->aData);
+ assert( aResult[0]==pPage->pgno );
+ aResult[1] = tmpCur.idx;
+ aResult[2] = pPage->nCell;
+ if( tmpCur.idx>=0 && tmpCur.idx<pPage->nCell ){
+ getCellInfo(&tmpCur);
+ aResult[3] = tmpCur.info.nSize;
+ aResult[6] = tmpCur.info.nData;
+ aResult[7] = tmpCur.info.nHeader;
+ aResult[8] = tmpCur.info.nLocal;
+ }else{
+ aResult[3] = 0;
+ aResult[6] = 0;
+ aResult[7] = 0;
+ aResult[8] = 0;
+ }
+ aResult[4] = pPage->nFree;
+ cnt = 0;
+ idx = get2byte(&pPage->aData[pPage->hdrOffset+1]);
+ while( idx>0 && idx<pPage->pBt->usableSize ){
+ cnt++;
+ idx = get2byte(&pPage->aData[idx]);
+ }
+ aResult[5] = cnt;
+ if( pPage->pParent==0 || isRootPage(pPage) ){
+ aResult[9] = 0;
+ }else{
+ aResult[9] = pPage->pParent->pgno;
+ }
+ releaseTempCursor(&tmpCur);
+ return SQLITE_OK;
+}
+#endif
+
+/*
+** Return the pager associated with a BTree. This routine is used for
+** testing and debugging only.
+*/
+Pager *sqlite3BtreePager(Btree *pBt){
+ return pBt->pPager;
+}
+
+/*
+** This structure is passed around through all the sanity checking routines
+** in order to keep track of some global state information.
+*/
+typedef struct IntegrityCk IntegrityCk;
+struct IntegrityCk {
+ Btree *pBt; /* The tree being checked out */
+ Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+ int nPage; /* Number of pages in the database */
+ int *anRef; /* Number of times each page is referenced */
+ char *zErrMsg; /* An error message. NULL of no errors seen. */
+};
+
+/*
+** Append a message to the error message string.
+*/
+static void checkAppendMsg(
+ IntegrityCk *pCheck,
+ char *zMsg1,
+ const char *zFormat,
+ ...
+){
+ va_list ap;
+ char *zMsg2;
+ va_start(ap, zFormat);
+ zMsg2 = sqlite3VMPrintf(zFormat, ap);
+ va_end(ap);
+ if( zMsg1==0 ) zMsg1 = "";
+ if( pCheck->zErrMsg ){
+ char *zOld = pCheck->zErrMsg;
+ pCheck->zErrMsg = 0;
+ sqlite3SetString(&pCheck->zErrMsg, zOld, "\n", zMsg1, zMsg2, (char*)0);
+ sqliteFree(zOld);
+ }else{
+ sqlite3SetString(&pCheck->zErrMsg, zMsg1, zMsg2, (char*)0);
+ }
+ sqliteFree(zMsg2);
+}
+
+/*
+** Add 1 to the reference count for page iPage. If this is the second
+** reference to the page, add an error message to pCheck->zErrMsg.
+** Return 1 if there are 2 ore more references to the page and 0 if
+** if this is the first reference to the page.
+**
+** Also check that the page number is in bounds.
+*/
+static int checkRef(IntegrityCk *pCheck, int iPage, char *zContext){
+ if( iPage==0 ) return 1;
+ if( iPage>pCheck->nPage || iPage<0 ){
+ checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
+ return 1;
+ }
+ if( pCheck->anRef[iPage]==1 ){
+ checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
+ return 1;
+ }
+ return (pCheck->anRef[iPage]++)>1;
+}
+
+/*
+** Check the integrity of the freelist or of an overflow page list.
+** Verify that the number of pages on the list is N.
+*/
+static void checkList(
+ IntegrityCk *pCheck, /* Integrity checking context */
+ int isFreeList, /* True for a freelist. False for overflow page list */
+ int iPage, /* Page number for first page in the list */
+ int N, /* Expected number of pages in the list */
+ char *zContext /* Context for error messages */
+){
+ int i;
+ int expected = N;
+ int iFirst = iPage;
+ while( N-- > 0 ){
+ unsigned char *pOvfl;
+ if( iPage<1 ){
+ checkAppendMsg(pCheck, zContext,
+ "%d of %d pages missing from overflow list starting at %d",
+ N+1, expected, iFirst);
+ break;
+ }
+ if( checkRef(pCheck, iPage, zContext) ) break;
+ if( sqlite3pager_get(pCheck->pPager, (Pgno)iPage, (void**)&pOvfl) ){
+ checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
+ break;
+ }
+ if( isFreeList ){
+ int n = get4byte(&pOvfl[4]);
+ if( n>pCheck->pBt->usableSize/4-8 ){
+ checkAppendMsg(pCheck, zContext,
+ "freelist leaf count too big on page %d", iPage);
+ N--;
+ }else{
+ for(i=0; i<n; i++){
+ checkRef(pCheck, get4byte(&pOvfl[8+i*4]), zContext);
+ }
+ N -= n;
+ }
+ }
+ iPage = get4byte(pOvfl);
+ sqlite3pager_unref(pOvfl);
+ }
+}
+
+/*
+** Do various sanity checks on a single page of a tree. Return
+** the tree depth. Root pages return 0. Parents of root pages
+** return 1, and so forth.
+**
+** These checks are done:
+**
+** 1. Make sure that cells and freeblocks do not overlap
+** but combine to completely cover the page.
+** NO 2. Make sure cell keys are in order.
+** NO 3. Make sure no key is less than or equal to zLowerBound.
+** NO 4. Make sure no key is greater than or equal to zUpperBound.
+** 5. Check the integrity of overflow pages.
+** 6. Recursively call checkTreePage on all children.
+** 7. Verify that the depth of all children is the same.
+** 8. Make sure this page is at least 33% full or else it is
+** the root of the tree.
+*/
+static int checkTreePage(
+ IntegrityCk *pCheck, /* Context for the sanity check */
+ int iPage, /* Page number of the page to check */
+ MemPage *pParent, /* Parent page */
+ char *zParentContext, /* Parent context */
+ char *zLowerBound, /* All keys should be greater than this, if not NULL */
+ int nLower, /* Number of characters in zLowerBound */
+ char *zUpperBound, /* All keys should be less than this, if not NULL */
+ int nUpper /* Number of characters in zUpperBound */
+){
+ MemPage *pPage;
+ int i, rc, depth, d2, pgno, cnt;
+ int hdr, cellStart;
+ int nCell;
+ u8 *data;
+ BtCursor cur;
+ Btree *pBt;
+ int maxLocal, usableSize;
+ char zContext[100];
+ char *hit;
+
+ /* Check that the page exists
+ */
+ cur.pBt = pBt = pCheck->pBt;
+ usableSize = pBt->usableSize;
+ if( iPage==0 ) return 0;
+ if( checkRef(pCheck, iPage, zParentContext) ) return 0;
+ if( (rc = getPage(pBt, (Pgno)iPage, &pPage))!=0 ){
+ checkAppendMsg(pCheck, zContext,
+ "unable to get the page. error code=%d", rc);
+ return 0;
+ }
+ maxLocal = pPage->leafData ? pBt->maxLeaf : pBt->maxLocal;
+ if( (rc = initPage(pPage, pParent))!=0 ){
+ checkAppendMsg(pCheck, zContext, "initPage() returns error code %d", rc);
+ releasePage(pPage);
+ return 0;
+ }
+
+ /* Check out all the cells.
+ */
+ depth = 0;
+ cur.pPage = pPage;
+ for(i=0; i<pPage->nCell; i++){
+ u8 *pCell;
+ int sz;
+ CellInfo info;
+
+ /* Check payload overflow pages
+ */
+ sprintf(zContext, "On tree page %d cell %d: ", iPage, i);
+ pCell = findCell(pPage,i);
+ parseCellPtr(pPage, pCell, &info);
+ sz = info.nData;
+ if( !pPage->intKey ) sz += info.nKey;
+ if( sz>info.nLocal ){
+ int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
+ checkList(pCheck, 0, get4byte(&pCell[info.iOverflow]),nPage,zContext);
+ }
+
+ /* Check sanity of left child page.
+ */
+ if( !pPage->leaf ){
+ pgno = get4byte(pCell);
+ d2 = checkTreePage(pCheck,pgno,pPage,zContext,0,0,0,0);
+ if( i>0 && d2!=depth ){
+ checkAppendMsg(pCheck, zContext, "Child page depth differs");
+ }
+ depth = d2;
+ }
+ }
+ if( !pPage->leaf ){
+ pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ sprintf(zContext, "On page %d at right child: ", iPage);
+ checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0);
+ }
+
+ /* Check for complete coverage of the page
+ */
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
+ hit = sqliteMalloc( usableSize );
+ if( hit ){
+ memset(hit, 1, get2byte(&data[hdr+5]));
+ nCell = get2byte(&data[hdr+3]);
+ cellStart = hdr + 12 - 4*pPage->leaf;
+ for(i=0; i<nCell; i++){
+ int pc = get2byte(&data[cellStart+i*2]);
+ int size = cellSizePtr(pPage, &data[pc]);
+ int j;
+ for(j=pc+size-1; j>=pc; j--) hit[j]++;
+ }
+ for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i<usableSize && cnt<10000;
+ cnt++){
+ int size = get2byte(&data[i+2]);
+ int j;
+ for(j=i+size-1; j>=i; j--) hit[j]++;
+ i = get2byte(&data[i]);
+ }
+ for(i=cnt=0; i<usableSize; i++){
+ if( hit[i]==0 ){
+ cnt++;
+ }else if( hit[i]>1 ){
+ checkAppendMsg(pCheck, 0,
+ "Multiple uses for byte %d of page %d", i, iPage);
+ break;
+ }
+ }
+ if( cnt!=data[hdr+7] ){
+ checkAppendMsg(pCheck, 0,
+ "Fragmented space is %d byte reported as %d on page %d",
+ cnt, data[hdr+7], iPage);
+ }
+ }
+ sqliteFree(hit);
+
+ releasePage(pPage);
+ return depth+1;
+}
+
+/*
+** This routine does a complete check of the given BTree file. aRoot[] is
+** an array of pages numbers were each page number is the root page of
+** a table. nRoot is the number of entries in aRoot.
+**
+** If everything checks out, this routine returns NULL. If something is
+** amiss, an error message is written into memory obtained from malloc()
+** and a pointer to that error message is returned. The calling function
+** is responsible for freeing the error message when it is done.
+*/
+char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
+ int i;
+ int nRef;
+ IntegrityCk sCheck;
+
+ nRef = *sqlite3pager_stats(pBt->pPager);
+ if( lockBtree(pBt)!=SQLITE_OK ){
+ return sqliteStrDup("Unable to acquire a read lock on the database");
+ }
+ sCheck.pBt = pBt;
+ sCheck.pPager = pBt->pPager;
+ sCheck.nPage = sqlite3pager_pagecount(sCheck.pPager);
+ if( sCheck.nPage==0 ){
+ unlockBtreeIfUnused(pBt);
+ return 0;
+ }
+ sCheck.anRef = sqliteMallocRaw( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
+ for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
+ i = PENDING_BYTE/pBt->pageSize + 1;
+ if( i<=sCheck.nPage ){
+ sCheck.anRef[i] = 1;
+ }
+ sCheck.zErrMsg = 0;
+
+ /* Check the integrity of the freelist
+ */
+ checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
+ get4byte(&pBt->pPage1->aData[36]), "Main freelist: ");
+
+ /* Check all the tables.
+ */
+ for(i=0; i<nRoot; i++){
+ if( aRoot[i]==0 ) continue;
+ checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0);
+ }
+
+ /* Make sure every page in the file is referenced
+ */
+ for(i=1; i<=sCheck.nPage; i++){
+ if( sCheck.anRef[i]==0 ){
+ checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
+ }
+ }
+
+ /* Make sure this analysis did not leave any unref() pages
+ */
+ unlockBtreeIfUnused(pBt);
+ if( nRef != *sqlite3pager_stats(pBt->pPager) ){
+ checkAppendMsg(&sCheck, 0,
+ "Outstanding page count goes from %d to %d during this analysis",
+ nRef, *sqlite3pager_stats(pBt->pPager)
+ );
+ }
+
+ /* Clean up and report errors.
+ */
+ sqliteFree(sCheck.anRef);
+ return sCheck.zErrMsg;
+}
+
+/*
+** Return the full pathname of the underlying database file.
+*/
+const char *sqlite3BtreeGetFilename(Btree *pBt){
+ assert( pBt->pPager!=0 );
+ return sqlite3pager_filename(pBt->pPager);
+}
+
+/*
+** Return the pathname of the directory that contains the database file.
+*/
+const char *sqlite3BtreeGetDirname(Btree *pBt){
+ assert( pBt->pPager!=0 );
+ return sqlite3pager_dirname(pBt->pPager);
+}
+
+/*
+** Return the pathname of the journal file for this database. The return
+** value of this routine is the same regardless of whether the journal file
+** has been created or not.
+*/
+const char *sqlite3BtreeGetJournalname(Btree *pBt){
+ assert( pBt->pPager!=0 );
+ return sqlite3pager_journalname(pBt->pPager);
+}
+
+/*
+** Copy the complete content of pBtFrom into pBtTo. A transaction
+** must be active for both files.
+**
+** The size of file pBtFrom may be reduced by this operation.
+** If anything goes wrong, the transaction on pBtFrom is rolled back.
+*/
+int sqlite3BtreeCopyFile(Btree *pBtTo, Btree *pBtFrom){
+ int rc = SQLITE_OK;
+ Pgno i, nPage, nToPage;
+
+ if( pBtTo->inTrans!=TRANS_WRITE || pBtFrom->inTrans!=TRANS_WRITE ){
+ return SQLITE_ERROR;
+ }
+ if( pBtTo->pCursor ) return SQLITE_BUSY;
+ nToPage = sqlite3pager_pagecount(pBtTo->pPager);
+ nPage = sqlite3pager_pagecount(pBtFrom->pPager);
+ for(i=1; rc==SQLITE_OK && i<=nPage; i++){
+ void *pPage;
+ rc = sqlite3pager_get(pBtFrom->pPager, i, &pPage);
+ if( rc ) break;
+ rc = sqlite3pager_overwrite(pBtTo->pPager, i, pPage);
+ if( rc ) break;
+ sqlite3pager_unref(pPage);
+ }
+ for(i=nPage+1; rc==SQLITE_OK && i<=nToPage; i++){
+ void *pPage;
+ rc = sqlite3pager_get(pBtTo->pPager, i, &pPage);
+ if( rc ) break;
+ rc = sqlite3pager_write(pPage);
+ sqlite3pager_unref(pPage);
+ sqlite3pager_dont_write(pBtTo->pPager, i);
+ }
+ if( !rc && nPage<nToPage ){
+ rc = sqlite3pager_truncate(pBtTo->pPager, nPage);
+ }
+ if( rc ){
+ sqlite3BtreeRollback(pBtTo);
+ }
+ return rc;
+}
+
+/*
+** Return non-zero if a transaction is active.
+*/
+int sqlite3BtreeIsInTrans(Btree *pBt){
+ return (pBt && (pBt->inTrans==TRANS_WRITE));
+}
+
+/*
+** Return non-zero if a statement transaction is active.
+*/
+int sqlite3BtreeIsInStmt(Btree *pBt){
+ return (pBt && pBt->inStmt);
+}
+
+/*
+** This call is a no-op if no write-transaction is currently active on pBt.
+**
+** Otherwise, sync the database file for the btree pBt. zMaster points to
+** the name of a master journal file that should be written into the
+** individual journal file, or is NULL, indicating no master journal file
+** (single database transaction).
+**
+** When this is called, the master journal should already have been
+** created, populated with this journal pointer and synced to disk.
+**
+** Once this is routine has returned, the only thing required to commit
+** the write-transaction for this database file is to delete the journal.
+*/
+int sqlite3BtreeSync(Btree *pBt, const char *zMaster){
+ if( pBt->inTrans==TRANS_WRITE ){
+ return sqlite3pager_sync(pBt->pPager, zMaster);
+ }
+ return SQLITE_OK;
+}
diff --git a/kopete/plugins/statistics/sqlite/btree.h b/kopete/plugins/statistics/sqlite/btree.h
new file mode 100644
index 00000000..48524aef
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/btree.h
@@ -0,0 +1,124 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This header file defines the interface that the sqlite B-Tree file
+** subsystem. See comments in the source code for a detailed description
+** of what each interface routine does.
+**
+** @(#) $Id$
+*/
+#ifndef _BTREE_H_
+#define _BTREE_H_
+
+/* TODO: This definition is just included so other modules compile. It
+** needs to be revisited.
+*/
+#define SQLITE_N_BTREE_META 10
+
+/*
+** Forward declarations of structure
+*/
+typedef struct Btree Btree;
+typedef struct BtCursor BtCursor;
+
+
+int sqlite3BtreeOpen(
+ const char *zFilename, /* Name of database file to open */
+ Btree **, /* Return open Btree* here */
+ int flags /* Flags */
+);
+
+/* The flags parameter to sqlite3BtreeOpen can be the bitwise or of the
+** following values.
+*/
+#define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */
+#define BTREE_MEMORY 2 /* In-memory DB. No argument */
+
+int sqlite3BtreeClose(Btree*);
+int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*);
+int sqlite3BtreeSetCacheSize(Btree*,int);
+int sqlite3BtreeSetSafetyLevel(Btree*,int);
+int sqlite3BtreeSetPageSize(Btree*,int,int);
+int sqlite3BtreeGetPageSize(Btree*);
+int sqlite3BtreeGetReserve(Btree*);
+int sqlite3BtreeBeginTrans(Btree*,int);
+int sqlite3BtreeCommit(Btree*);
+int sqlite3BtreeRollback(Btree*);
+int sqlite3BtreeBeginStmt(Btree*);
+int sqlite3BtreeCommitStmt(Btree*);
+int sqlite3BtreeRollbackStmt(Btree*);
+int sqlite3BtreeCreateTable(Btree*, int*, int flags);
+int sqlite3BtreeIsInTrans(Btree*);
+int sqlite3BtreeIsInStmt(Btree*);
+int sqlite3BtreeSync(Btree*, const char *zMaster);
+
+const char *sqlite3BtreeGetFilename(Btree *);
+const char *sqlite3BtreeGetDirname(Btree *);
+const char *sqlite3BtreeGetJournalname(Btree *);
+int sqlite3BtreeCopyFile(Btree *, Btree *);
+
+/* The flags parameter to sqlite3BtreeCreateTable can be the bitwise OR
+** of the following flags:
+*/
+#define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */
+#define BTREE_ZERODATA 2 /* Table has keys only - no data */
+#define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */
+
+int sqlite3BtreeDropTable(Btree*, int);
+int sqlite3BtreeClearTable(Btree*, int);
+int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue);
+int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
+
+int sqlite3BtreeCursor(
+ Btree*, /* BTree containing table to open */
+ int iTable, /* Index of root page */
+ int wrFlag, /* 1 for writing. 0 for read-only */
+ int(*)(void*,int,const void*,int,const void*), /* Key comparison function */
+ void*, /* First argument to compare function */
+ BtCursor **ppCursor /* Returned cursor */
+);
+
+void sqlite3BtreeSetCompare(
+ BtCursor *,
+ int(*)(void*,int,const void*,int,const void*),
+ void*
+);
+
+int sqlite3BtreeCloseCursor(BtCursor*);
+int sqlite3BtreeMoveto(BtCursor*, const void *pKey, i64 nKey, int *pRes);
+int sqlite3BtreeDelete(BtCursor*);
+int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
+ const void *pData, int nData);
+int sqlite3BtreeFirst(BtCursor*, int *pRes);
+int sqlite3BtreeLast(BtCursor*, int *pRes);
+int sqlite3BtreeNext(BtCursor*, int *pRes);
+int sqlite3BtreeEof(BtCursor*);
+int sqlite3BtreeFlags(BtCursor*);
+int sqlite3BtreePrevious(BtCursor*, int *pRes);
+int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
+int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
+const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
+const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
+int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
+int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
+
+char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot);
+struct Pager *sqlite3BtreePager(Btree*);
+
+
+#ifdef SQLITE_TEST
+int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
+void sqlite3BtreeCursorList(Btree*);
+int sqlite3BtreePageDump(Btree*, int, int recursive);
+#endif
+
+
+#endif /* _BTREE_H_ */
diff --git a/kopete/plugins/statistics/sqlite/build.c b/kopete/plugins/statistics/sqlite/build.c
new file mode 100644
index 00000000..3e5e08a5
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/build.c
@@ -0,0 +1,2564 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C code routines that are called by the SQLite parser
+** when syntax rules are reduced. The routines in this file handle the
+** following kinds of SQL syntax:
+**
+** CREATE TABLE
+** DROP TABLE
+** CREATE INDEX
+** DROP INDEX
+** creating ID lists
+** BEGIN TRANSACTION
+** COMMIT
+** ROLLBACK
+** PRAGMA
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include <ctype.h>
+
+/*
+** This routine is called when a new SQL statement is beginning to
+** be parsed. Check to see if the schema for the database needs
+** to be read from the SQLITE_MASTER and SQLITE_TEMP_MASTER tables.
+** If it does, then read it.
+*/
+void sqlite3BeginParse(Parse *pParse, int explainFlag){
+ pParse->explain = explainFlag;
+ pParse->nVar = 0;
+}
+
+/*
+** This routine is called after a single SQL statement has been
+** parsed and a VDBE program to execute that statement has been
+** prepared. This routine puts the finishing touches on the
+** VDBE program and resets the pParse structure for the next
+** parse.
+**
+** Note that if an error occurred, it might be the case that
+** no VDBE code was generated.
+*/
+void sqlite3FinishCoding(Parse *pParse){
+ sqlite3 *db;
+ Vdbe *v;
+
+ if( sqlite3_malloc_failed ) return;
+
+ /* Begin by generating some termination code at the end of the
+ ** vdbe program
+ */
+ db = pParse->db;
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ sqlite3VdbeAddOp(v, OP_Halt, 0, 0);
+
+ /* The cookie mask contains one bit for each database file open.
+ ** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
+ ** set for each database that is used. Generate code to start a
+ ** transaction on each used database and to verify the schema cookie
+ ** on each used database.
+ */
+ if( pParse->cookieGoto>0 ){
+ u32 mask;
+ int iDb;
+ sqlite3VdbeChangeP2(v, pParse->cookieGoto-1, sqlite3VdbeCurrentAddr(v));
+ for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
+ if( (mask & pParse->cookieMask)==0 ) continue;
+ sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
+ sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]);
+ }
+ sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->cookieGoto);
+ }
+
+ /* Add a No-op that contains the complete text of the compiled SQL
+ ** statement as its P3 argument. This does not change the functionality
+ ** of the program.
+ **
+ ** This is used to implement sqlite3_trace() functionality.
+ */
+ sqlite3VdbeOp3(v, OP_Noop, 0, 0, pParse->zSql, pParse->zTail-pParse->zSql);
+ }
+
+
+ /* Get the VDBE program ready for execution
+ */
+ if( v && pParse->nErr==0 ){
+ FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
+ sqlite3VdbeTrace(v, trace);
+ sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem+3,
+ pParse->nTab+3, pParse->explain);
+ pParse->rc = pParse->nErr ? SQLITE_ERROR : SQLITE_DONE;
+ pParse->colNamesSet = 0;
+ }else if( pParse->rc==SQLITE_OK ){
+ pParse->rc = SQLITE_ERROR;
+ }
+ pParse->nTab = 0;
+ pParse->nMem = 0;
+ pParse->nSet = 0;
+ pParse->nAgg = 0;
+ pParse->nVar = 0;
+ pParse->cookieMask = 0;
+ pParse->cookieGoto = 0;
+}
+
+/*
+** Locate the in-memory structure that describes a particular database
+** table given the name of that table and (optionally) the name of the
+** database containing the table. Return NULL if not found.
+**
+** If zDatabase is 0, all databases are searched for the table and the
+** first matching table is returned. (No checking for duplicate table
+** names is done.) The search order is TEMP first, then MAIN, then any
+** auxiliary databases added using the ATTACH command.
+**
+** See also sqlite3LocateTable().
+*/
+Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
+ Table *p = 0;
+ int i;
+ assert( zName!=0 );
+ assert( (db->flags & SQLITE_Initialized) || db->init.busy );
+ for(i=0; i<db->nDb; i++){
+ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
+ p = sqlite3HashFind(&db->aDb[j].tblHash, zName, strlen(zName)+1);
+ if( p ) break;
+ }
+ return p;
+}
+
+/*
+** Locate the in-memory structure that describes a particular database
+** table given the name of that table and (optionally) the name of the
+** database containing the table. Return NULL if not found. Also leave an
+** error message in pParse->zErrMsg.
+**
+** The difference between this routine and sqlite3FindTable() is that this
+** routine leaves an error message in pParse->zErrMsg where
+** sqlite3FindTable() does not.
+*/
+Table *sqlite3LocateTable(Parse *pParse, const char *zName, const char *zDbase){
+ Table *p;
+
+ /* Read the database schema. If an error occurs, leave an error message
+ ** and code in pParse and return NULL. */
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
+ return 0;
+ }
+
+ p = sqlite3FindTable(pParse->db, zName, zDbase);
+ if( p==0 ){
+ if( zDbase ){
+ sqlite3ErrorMsg(pParse, "no such table: %s.%s", zDbase, zName);
+ }else if( sqlite3FindTable(pParse->db, zName, 0)!=0 ){
+ sqlite3ErrorMsg(pParse, "table \"%s\" is not in database \"%s\"",
+ zName, zDbase);
+ }else{
+ sqlite3ErrorMsg(pParse, "no such table: %s", zName);
+ }
+ pParse->checkSchema = 1;
+ }
+ return p;
+}
+
+/*
+** Locate the in-memory structure that describes
+** a particular index given the name of that index
+** and the name of the database that contains the index.
+** Return NULL if not found.
+**
+** If zDatabase is 0, all databases are searched for the
+** table and the first matching index is returned. (No checking
+** for duplicate index names is done.) The search order is
+** TEMP first, then MAIN, then any auxiliary databases added
+** using the ATTACH command.
+*/
+Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
+ Index *p = 0;
+ int i;
+ assert( (db->flags & SQLITE_Initialized) || db->init.busy );
+ for(i=0; i<db->nDb; i++){
+ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
+ p = sqlite3HashFind(&db->aDb[j].idxHash, zName, strlen(zName)+1);
+ if( p ) break;
+ }
+ return p;
+}
+
+/*
+** Reclaim the memory used by an index
+*/
+static void freeIndex(Index *p){
+ sqliteFree(p->zColAff);
+ sqliteFree(p);
+}
+
+/*
+** Remove the given index from the index hash table, and free
+** its memory structures.
+**
+** The index is removed from the database hash tables but
+** it is not unlinked from the Table that it indexes.
+** Unlinking from the Table must be done by the calling function.
+*/
+static void sqliteDeleteIndex(sqlite3 *db, Index *p){
+ Index *pOld;
+
+ assert( db!=0 && p->zName!=0 );
+ pOld = sqlite3HashInsert(&db->aDb[p->iDb].idxHash, p->zName,
+ strlen(p->zName)+1, 0);
+ if( pOld!=0 && pOld!=p ){
+ sqlite3HashInsert(&db->aDb[p->iDb].idxHash, pOld->zName,
+ strlen(pOld->zName)+1, pOld);
+ }
+ freeIndex(p);
+}
+
+/*
+** Unlink the given index from its table, then remove
+** the index from the index hash table and free its memory
+** structures.
+*/
+void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
+ Index *pIndex;
+ int len;
+
+ len = strlen(zIdxName);
+ pIndex = sqlite3HashInsert(&db->aDb[iDb].idxHash, zIdxName, len+1, 0);
+ if( pIndex ){
+ if( pIndex->pTable->pIndex==pIndex ){
+ pIndex->pTable->pIndex = pIndex->pNext;
+ }else{
+ Index *p;
+ for(p=pIndex->pTable->pIndex; p && p->pNext!=pIndex; p=p->pNext){}
+ if( p && p->pNext==pIndex ){
+ p->pNext = pIndex->pNext;
+ }
+ }
+ freeIndex(pIndex);
+ }
+ db->flags |= SQLITE_InternChanges;
+}
+
+/*
+** Erase all schema information from the in-memory hash tables of
+** a single database. This routine is called to reclaim memory
+** before the database closes. It is also called during a rollback
+** if there were schema changes during the transaction or if a
+** schema-cookie mismatch occurs.
+**
+** If iDb<=0 then reset the internal schema tables for all database
+** files. If iDb>=2 then reset the internal schema for only the
+** single file indicated.
+*/
+void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
+ HashElem *pElem;
+ Hash temp1;
+ Hash temp2;
+ int i, j;
+
+ assert( iDb>=0 && iDb<db->nDb );
+ db->flags &= ~SQLITE_Initialized;
+ for(i=iDb; i<db->nDb; i++){
+ Db *pDb = &db->aDb[i];
+ temp1 = pDb->tblHash;
+ temp2 = pDb->trigHash;
+ sqlite3HashInit(&pDb->trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashClear(&pDb->aFKey);
+ sqlite3HashClear(&pDb->idxHash);
+ for(pElem=sqliteHashFirst(&temp2); pElem; pElem=sqliteHashNext(pElem)){
+ Trigger *pTrigger = sqliteHashData(pElem);
+ sqlite3DeleteTrigger(pTrigger);
+ }
+ sqlite3HashClear(&temp2);
+ sqlite3HashInit(&pDb->tblHash, SQLITE_HASH_STRING, 0);
+ for(pElem=sqliteHashFirst(&temp1); pElem; pElem=sqliteHashNext(pElem)){
+ Table *pTab = sqliteHashData(pElem);
+ sqlite3DeleteTable(db, pTab);
+ }
+ sqlite3HashClear(&temp1);
+ DbClearProperty(db, i, DB_SchemaLoaded);
+ if( iDb>0 ) return;
+ }
+ assert( iDb==0 );
+ db->flags &= ~SQLITE_InternChanges;
+
+ /* If one or more of the auxiliary database files has been closed,
+ ** then remove then from the auxiliary database list. We take the
+ ** opportunity to do this here since we have just deleted all of the
+ ** schema hash tables and therefore do not have to make any changes
+ ** to any of those tables.
+ */
+ for(i=0; i<db->nDb; i++){
+ struct Db *pDb = &db->aDb[i];
+ if( pDb->pBt==0 ){
+ if( pDb->pAux && pDb->xFreeAux ) pDb->xFreeAux(pDb->pAux);
+ pDb->pAux = 0;
+ }
+ }
+ for(i=j=2; i<db->nDb; i++){
+ struct Db *pDb = &db->aDb[i];
+ if( pDb->pBt==0 ){
+ sqliteFree(pDb->zName);
+ pDb->zName = 0;
+ continue;
+ }
+ if( j<i ){
+ db->aDb[j] = db->aDb[i];
+ }
+ j++;
+ }
+ memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
+ db->nDb = j;
+ if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
+ memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
+ sqliteFree(db->aDb);
+ db->aDb = db->aDbStatic;
+ }
+}
+
+/*
+** This routine is called whenever a rollback occurs. If there were
+** schema changes during the transaction, then we have to reset the
+** internal hash tables and reload them from disk.
+*/
+void sqlite3RollbackInternalChanges(sqlite3 *db){
+ if( db->flags & SQLITE_InternChanges ){
+ sqlite3ResetInternalSchema(db, 0);
+ }
+}
+
+/*
+** This routine is called when a commit occurs.
+*/
+void sqlite3CommitInternalChanges(sqlite3 *db){
+ db->flags &= ~SQLITE_InternChanges;
+}
+
+/*
+** Clear the column names from a table or view.
+*/
+static void sqliteResetColumnNames(Table *pTable){
+ int i;
+ Column *pCol;
+ assert( pTable!=0 );
+ for(i=0, pCol=pTable->aCol; i<pTable->nCol; i++, pCol++){
+ sqliteFree(pCol->zName);
+ sqliteFree(pCol->zDflt);
+ sqliteFree(pCol->zType);
+ }
+ sqliteFree(pTable->aCol);
+ pTable->aCol = 0;
+ pTable->nCol = 0;
+}
+
+/*
+** Remove the memory data structures associated with the given
+** Table. No changes are made to disk by this routine.
+**
+** This routine just deletes the data structure. It does not unlink
+** the table data structure from the hash table. Nor does it remove
+** foreign keys from the sqlite.aFKey hash table. But it does destroy
+** memory structures of the indices and foreign keys associated with
+** the table.
+**
+** Indices associated with the table are unlinked from the "db"
+** data structure if db!=NULL. If db==NULL, indices attached to
+** the table are deleted, but it is assumed they have already been
+** unlinked.
+*/
+void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
+ Index *pIndex, *pNext;
+ FKey *pFKey, *pNextFKey;
+
+ if( pTable==0 ) return;
+
+ /* Delete all indices associated with this table
+ */
+ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
+ pNext = pIndex->pNext;
+ assert( pIndex->iDb==pTable->iDb || (pTable->iDb==0 && pIndex->iDb==1) );
+ sqliteDeleteIndex(db, pIndex);
+ }
+
+ /* Delete all foreign keys associated with this table. The keys
+ ** should have already been unlinked from the db->aFKey hash table
+ */
+ for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){
+ pNextFKey = pFKey->pNextFrom;
+ assert( pTable->iDb<db->nDb );
+ assert( sqlite3HashFind(&db->aDb[pTable->iDb].aFKey,
+ pFKey->zTo, strlen(pFKey->zTo)+1)!=pFKey );
+ sqliteFree(pFKey);
+ }
+
+ /* Delete the Table structure itself.
+ */
+ sqliteResetColumnNames(pTable);
+ sqliteFree(pTable->zName);
+ sqliteFree(pTable->zColAff);
+ sqlite3SelectDelete(pTable->pSelect);
+ sqliteFree(pTable);
+}
+
+/*
+** Unlink the given table from the hash tables and the delete the
+** table structure with all its indices and foreign keys.
+*/
+void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){
+ Table *p;
+ FKey *pF1, *pF2;
+ Db *pDb;
+
+ assert( db!=0 );
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( zTabName && zTabName[0] );
+ pDb = &db->aDb[iDb];
+ p = sqlite3HashInsert(&pDb->tblHash, zTabName, strlen(zTabName)+1, 0);
+ if( p ){
+ for(pF1=p->pFKey; pF1; pF1=pF1->pNextFrom){
+ int nTo = strlen(pF1->zTo) + 1;
+ pF2 = sqlite3HashFind(&pDb->aFKey, pF1->zTo, nTo);
+ if( pF2==pF1 ){
+ sqlite3HashInsert(&pDb->aFKey, pF1->zTo, nTo, pF1->pNextTo);
+ }else{
+ while( pF2 && pF2->pNextTo!=pF1 ){ pF2=pF2->pNextTo; }
+ if( pF2 ){
+ pF2->pNextTo = pF1->pNextTo;
+ }
+ }
+ }
+ sqlite3DeleteTable(db, p);
+ }
+ db->flags |= SQLITE_InternChanges;
+}
+
+/*
+** Given a token, return a string that consists of the text of that
+** token with any quotations removed. Space to hold the returned string
+** is obtained from sqliteMalloc() and must be freed by the calling
+** function.
+**
+** Tokens are really just pointers into the original SQL text and so
+** are not \000 terminated and are not persistent. The returned string
+** is \000 terminated and is persistent.
+*/
+char *sqlite3NameFromToken(Token *pName){
+ char *zName;
+ if( pName ){
+ zName = sqliteStrNDup(pName->z, pName->n);
+ sqlite3Dequote(zName);
+ }else{
+ zName = 0;
+ }
+ return zName;
+}
+
+/*
+** Open the sqlite_master table stored in database number iDb for
+** writing. The table is opened using cursor 0.
+*/
+void sqlite3OpenMasterTable(Vdbe *v, int iDb){
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenWrite, 0, MASTER_ROOT);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, 0, 5); /* sqlite_master has 5 columns */
+}
+
+/*
+** The token *pName contains the name of a database (either "main" or
+** "temp" or the name of an attached db). This routine returns the
+** index of the named database in db->aDb[], or -1 if the named db
+** does not exist.
+*/
+int findDb(sqlite3 *db, Token *pName){
+ int i;
+ Db *pDb;
+ for(pDb=db->aDb, i=0; i<db->nDb; i++, pDb++){
+ if( pName->n==strlen(pDb->zName) &&
+ 0==sqlite3StrNICmp(pDb->zName, pName->z, pName->n) ){
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* The table or view or trigger name is passed to this routine via tokens
+** pName1 and pName2. If the table name was fully qualified, for example:
+**
+** CREATE TABLE xxx.yyy (...);
+**
+** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
+** the table name is not fully qualified, i.e.:
+**
+** CREATE TABLE yyy(...);
+**
+** Then pName1 is set to "yyy" and pName2 is "".
+**
+** This routine sets the *ppUnqual pointer to point at the token (pName1 or
+** pName2) that stores the unqualified table name. The index of the
+** database "xxx" is returned.
+*/
+int sqlite3TwoPartName(
+ Parse *pParse, /* Parsing and code generating context */
+ Token *pName1, /* The "xxx" in the name "xxx.yyy" or "xxx" */
+ Token *pName2, /* The "yyy" in the name "xxx.yyy" */
+ Token **pUnqual /* Write the unqualified object name here */
+){
+ int iDb; /* Database holding the object */
+ sqlite3 *db = pParse->db;
+
+ if( pName2 && pName2->n>0 ){
+ assert( !db->init.busy );
+ *pUnqual = pName2;
+ iDb = findDb(db, pName1);
+ if( iDb<0 ){
+ sqlite3ErrorMsg(pParse, "unknown database %T", pName1);
+ pParse->nErr++;
+ return -1;
+ }
+ }else{
+ assert( db->init.iDb==0 || db->init.busy );
+ iDb = db->init.iDb;
+ *pUnqual = pName1;
+ }
+ return iDb;
+}
+
+/*
+** This routine is used to check if the UTF-8 string zName is a legal
+** unqualified name for a new schema object (table, index, view or
+** trigger). All names are legal except those that begin with the string
+** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
+** is reserved for internal use.
+*/
+int sqlite3CheckObjectName(Parse *pParse, const char *zName){
+ if( !pParse->db->init.busy && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
+ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Begin constructing a new table representation in memory. This is
+** the first of several action routines that get called in response
+** to a CREATE TABLE statement. In particular, this routine is called
+** after seeing tokens "CREATE" and "TABLE" and the table name. The
+** pStart token is the CREATE and pName is the table name. The isTemp
+** flag is true if the table should be stored in the auxiliary database
+** file instead of in the main database file. This is normally the case
+** when the "TEMP" or "TEMPORARY" keyword occurs in between
+** CREATE and TABLE.
+**
+** The new table record is initialized and put in pParse->pNewTable.
+** As more of the CREATE TABLE statement is parsed, additional action
+** routines will be called to add more information to this record.
+** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine
+** is called to complete the construction of the new table record.
+*/
+void sqlite3StartTable(
+ Parse *pParse, /* Parser context */
+ Token *pStart, /* The "CREATE" token */
+ Token *pName1, /* First part of the name of the table or view */
+ Token *pName2, /* Second part of the name of the table or view */
+ int isTemp, /* True if this is a TEMP table */
+ int isView /* True if this is a VIEW */
+){
+ Table *pTable;
+ Index *pIdx;
+ char *zName;
+ sqlite3 *db = pParse->db;
+ Vdbe *v;
+ int iDb; /* Database number to create the table in */
+ Token *pName; /* Unqualified name of the table to create */
+
+ /* The table or view name to create is passed to this routine via tokens
+ ** pName1 and pName2. If the table name was fully qualified, for example:
+ **
+ ** CREATE TABLE xxx.yyy (...);
+ **
+ ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
+ ** the table name is not fully qualified, i.e.:
+ **
+ ** CREATE TABLE yyy(...);
+ **
+ ** Then pName1 is set to "yyy" and pName2 is "".
+ **
+ ** The call below sets the pName pointer to point at the token (pName1 or
+ ** pName2) that stores the unqualified table name. The variable iDb is
+ ** set to the index of the database that the table or view is to be
+ ** created in.
+ */
+ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( iDb<0 ) return;
+ if( isTemp && iDb>1 ){
+ /* If creating a temp table, the name may not be qualified */
+ sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
+ pParse->nErr++;
+ return;
+ }
+ if( isTemp ) iDb = 1;
+
+ pParse->sNameToken = *pName;
+ zName = sqlite3NameFromToken(pName);
+ if( zName==0 ) return;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ sqliteFree(zName);
+ return;
+ }
+ if( db->init.iDb==1 ) isTemp = 1;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ assert( (isTemp & 1)==isTemp );
+ {
+ int code;
+ char *zDb = db->aDb[iDb].zName;
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+ sqliteFree(zName);
+ return;
+ }
+ if( isView ){
+ if( isTemp ){
+ code = SQLITE_CREATE_TEMP_VIEW;
+ }else{
+ code = SQLITE_CREATE_VIEW;
+ }
+ }else{
+ if( isTemp ){
+ code = SQLITE_CREATE_TEMP_TABLE;
+ }else{
+ code = SQLITE_CREATE_TABLE;
+ }
+ }
+ if( sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
+ sqliteFree(zName);
+ return;
+ }
+ }
+#endif
+
+ /* Make sure the new table name does not collide with an existing
+ ** index or table name in the same database. Issue an error message if
+ ** it does.
+ */
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) return;
+ pTable = sqlite3FindTable(db, zName, db->aDb[iDb].zName);
+ if( pTable ){
+ sqlite3ErrorMsg(pParse, "table %T already exists", pName);
+ sqliteFree(zName);
+ return;
+ }
+ if( (pIdx = sqlite3FindIndex(db, zName, 0))!=0 &&
+ ( iDb==0 || !db->init.busy) ){
+ sqlite3ErrorMsg(pParse, "there is already an index named %s", zName);
+ sqliteFree(zName);
+ return;
+ }
+ pTable = sqliteMalloc( sizeof(Table) );
+ if( pTable==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ pParse->nErr++;
+ sqliteFree(zName);
+ return;
+ }
+ pTable->zName = zName;
+ pTable->nCol = 0;
+ pTable->aCol = 0;
+ pTable->iPKey = -1;
+ pTable->pIndex = 0;
+ pTable->iDb = iDb;
+ if( pParse->pNewTable ) sqlite3DeleteTable(db, pParse->pNewTable);
+ pParse->pNewTable = pTable;
+
+ /* Begin generating the code that will insert the table record into
+ ** the SQLITE_MASTER table. Note in particular that we must go ahead
+ ** and allocate the record number for the table entry now. Before any
+ ** PRIMARY KEY or UNIQUE keywords are parsed. Those keywords will cause
+ ** indices to be created and the table record must come before the
+ ** indices. Hence, the record number for the table must be allocated
+ ** now.
+ */
+ if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ /* Every time a new table is created the file-format
+ ** and encoding meta-values are set in the database, in
+ ** case this is the first table created.
+ */
+ sqlite3VdbeAddOp(v, OP_Integer, db->file_format, 0);
+ sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 1);
+ sqlite3VdbeAddOp(v, OP_Integer, db->enc, 0);
+ sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 4);
+
+ sqlite3OpenMasterTable(v, iDb);
+ sqlite3VdbeAddOp(v, OP_NewRecno, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0);
+ }
+}
+
+/*
+** Add a new column to the table currently being constructed.
+**
+** The parser calls this routine once for each column declaration
+** in a CREATE TABLE statement. sqlite3StartTable() gets called
+** first to get things going. Then this routine is called for each
+** column.
+*/
+void sqlite3AddColumn(Parse *pParse, Token *pName){
+ Table *p;
+ int i;
+ char *z;
+ Column *pCol;
+ if( (p = pParse->pNewTable)==0 ) return;
+ z = sqlite3NameFromToken(pName);
+ if( z==0 ) return;
+ for(i=0; i<p->nCol; i++){
+ if( sqlite3StrICmp(z, p->aCol[i].zName)==0 ){
+ sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
+ sqliteFree(z);
+ return;
+ }
+ }
+ if( (p->nCol & 0x7)==0 ){
+ Column *aNew;
+ aNew = sqliteRealloc( p->aCol, (p->nCol+8)*sizeof(p->aCol[0]));
+ if( aNew==0 ) return;
+ p->aCol = aNew;
+ }
+ pCol = &p->aCol[p->nCol];
+ memset(pCol, 0, sizeof(p->aCol[0]));
+ pCol->zName = z;
+
+ /* If there is no type specified, columns have the default affinity
+ ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
+ ** be called next to set pCol->affinity correctly.
+ */
+ pCol->affinity = SQLITE_AFF_NONE;
+ pCol->pColl = pParse->db->pDfltColl;
+ p->nCol++;
+}
+
+/*
+** This routine is called by the parser while in the middle of
+** parsing a CREATE TABLE statement. A "NOT NULL" constraint has
+** been seen on a column. This routine sets the notNull flag on
+** the column currently under construction.
+*/
+void sqlite3AddNotNull(Parse *pParse, int onError){
+ Table *p;
+ int i;
+ if( (p = pParse->pNewTable)==0 ) return;
+ i = p->nCol-1;
+ if( i>=0 ) p->aCol[i].notNull = onError;
+}
+
+/*
+** This routine is called by the parser while in the middle of
+** parsing a CREATE TABLE statement. The pFirst token is the first
+** token in the sequence of tokens that describe the type of the
+** column currently under construction. pLast is the last token
+** in the sequence. Use this information to construct a string
+** that contains the typename of the column and store that string
+** in zType.
+*/
+void sqlite3AddColumnType(Parse *pParse, Token *pFirst, Token *pLast){
+ Table *p;
+ int i, j;
+ int n;
+ char *z, **pz;
+ Column *pCol;
+ if( (p = pParse->pNewTable)==0 ) return;
+ i = p->nCol-1;
+ if( i<0 ) return;
+ pCol = &p->aCol[i];
+ pz = &pCol->zType;
+ n = pLast->n + (pLast->z - pFirst->z);
+ assert( pCol->zType==0 );
+ z = pCol->zType = sqlite3MPrintf("%.*s", n, pFirst->z);
+ if( z==0 ) return;
+ for(i=j=0; z[i]; i++){
+ int c = z[i];
+ if( isspace(c) ) continue;
+ z[j++] = c;
+ }
+ z[j] = 0;
+ pCol->affinity = sqlite3AffinityType(z, n);
+}
+
+/*
+** The given token is the default value for the last column added to
+** the table currently under construction. If "minusFlag" is true, it
+** means the value token was preceded by a minus sign.
+**
+** This routine is called by the parser while in the middle of
+** parsing a CREATE TABLE statement.
+*/
+void sqlite3AddDefaultValue(Parse *pParse, Token *pVal, int minusFlag){
+ Table *p;
+ int i;
+ char *z;
+ if( (p = pParse->pNewTable)==0 ) return;
+ i = p->nCol-1;
+ if( i<0 ) return;
+ assert( p->aCol[i].zDflt==0 );
+ z = p->aCol[i].zDflt = sqlite3MPrintf("%s%T", minusFlag ? "-" : "", pVal);
+ sqlite3Dequote(z);
+}
+
+/*
+** Designate the PRIMARY KEY for the table. pList is a list of names
+** of columns that form the primary key. If pList is NULL, then the
+** most recently added column of the table is the primary key.
+**
+** A table can have at most one primary key. If the table already has
+** a primary key (and this is the second primary key) then create an
+** error.
+**
+** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
+** then we will try to use that column as the row id. (Exception:
+** For backwards compatibility with older databases, do not do this
+** if the file format version number is less than 1.) Set the Table.iPKey
+** field of the table under construction to be the index of the
+** INTEGER PRIMARY KEY column. Table.iPKey is set to -1 if there is
+** no INTEGER PRIMARY KEY.
+**
+** If the key is not an INTEGER PRIMARY KEY, then create a unique
+** index for the key. No index is created for INTEGER PRIMARY KEYs.
+*/
+void sqlite3AddPrimaryKey(Parse *pParse, ExprList *pList, int onError){
+ Table *pTab = pParse->pNewTable;
+ char *zType = 0;
+ int iCol = -1, i;
+ if( pTab==0 ) goto primary_key_exit;
+ if( pTab->hasPrimKey ){
+ sqlite3ErrorMsg(pParse,
+ "table \"%s\" has more than one primary key", pTab->zName);
+ goto primary_key_exit;
+ }
+ pTab->hasPrimKey = 1;
+ if( pList==0 ){
+ iCol = pTab->nCol - 1;
+ pTab->aCol[iCol].isPrimKey = 1;
+ }else{
+ for(i=0; i<pList->nExpr; i++){
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
+ break;
+ }
+ }
+ if( iCol<pTab->nCol ) pTab->aCol[iCol].isPrimKey = 1;
+ }
+ if( pList->nExpr>1 ) iCol = -1;
+ }
+ if( iCol>=0 && iCol<pTab->nCol ){
+ zType = pTab->aCol[iCol].zType;
+ }
+ if( zType && sqlite3StrICmp(zType, "INTEGER")==0 ){
+ pTab->iPKey = iCol;
+ pTab->keyConf = onError;
+ }else{
+ sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0);
+ pList = 0;
+ }
+
+primary_key_exit:
+ sqlite3ExprListDelete(pList);
+ return;
+}
+
+/*
+** Set the collation function of the most recently parsed table column
+** to the CollSeq given.
+*/
+void sqlite3AddCollateType(Parse *pParse, const char *zType, int nType){
+ Table *p;
+ Index *pIdx;
+ CollSeq *pColl;
+ int i;
+
+ if( (p = pParse->pNewTable)==0 ) return;
+ i = p->nCol-1;
+
+ pColl = sqlite3LocateCollSeq(pParse, zType, nType);
+ p->aCol[i].pColl = pColl;
+
+ /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
+ ** then an index may have been created on this column before the
+ ** collation type was added. Correct this if it is the case.
+ */
+ for(pIdx = p->pIndex; pIdx; pIdx=pIdx->pNext){
+ assert( pIdx->nColumn==1 );
+ if( pIdx->aiColumn[0]==i ) pIdx->keyInfo.aColl[0] = pColl;
+ }
+}
+
+/*
+** Locate and return an entry from the db.aCollSeq hash table. If the entry
+** specified by zName and nName is not found and parameter 'create' is
+** true, then create a new entry. Otherwise return NULL.
+**
+** Each pointer stored in the sqlite3.aCollSeq hash table contains an
+** array of three CollSeq structures. The first is the collation sequence
+** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be.
+**
+** Stored immediately after the three collation sequences is a copy of
+** the collation sequence name. A pointer to this string is stored in
+** each collation sequence structure.
+*/
+static CollSeq * findCollSeqEntry(
+ sqlite3 *db,
+ const char *zName,
+ int nName,
+ int create
+){
+ CollSeq *pColl;
+ if( nName<0 ) nName = strlen(zName);
+ pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
+
+ if( 0==pColl && create ){
+ pColl = sqliteMalloc( 3*sizeof(*pColl) + nName + 1 );
+ if( pColl ){
+ pColl[0].zName = (char*)&pColl[3];
+ pColl[0].enc = SQLITE_UTF8;
+ pColl[1].zName = (char*)&pColl[3];
+ pColl[1].enc = SQLITE_UTF16LE;
+ pColl[2].zName = (char*)&pColl[3];
+ pColl[2].enc = SQLITE_UTF16BE;
+ memcpy(pColl[0].zName, zName, nName);
+ pColl[0].zName[nName] = 0;
+ sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl);
+ }
+ }
+ return pColl;
+}
+
+/*
+** Parameter zName points to a UTF-8 encoded string nName bytes long.
+** Return the CollSeq* pointer for the collation sequence named zName
+** for the encoding 'enc' from the database 'db'.
+**
+** If the entry specified is not found and 'create' is true, then create a
+** new entry. Otherwise return NULL.
+*/
+CollSeq *sqlite3FindCollSeq(
+ sqlite3 *db,
+ u8 enc,
+ const char *zName,
+ int nName,
+ int create
+){
+ CollSeq *pColl = findCollSeqEntry(db, zName, nName, create);
+ assert( SQLITE_UTF8==1 && SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ assert( enc>=SQLITE_UTF8 && enc<=SQLITE_UTF16BE );
+ if( pColl ) pColl += enc-1;
+ return pColl;
+}
+
+/*
+** Invoke the 'collation needed' callback to request a collation sequence
+** in the database text encoding of name zName, length nName.
+** If the collation sequence
+*/
+static void callCollNeeded(sqlite3 *db, const char *zName, int nName){
+ assert( !db->xCollNeeded || !db->xCollNeeded16 );
+ if( nName<0 ) nName = strlen(zName);
+ if( db->xCollNeeded ){
+ char *zExternal = sqliteStrNDup(zName, nName);
+ if( !zExternal ) return;
+ db->xCollNeeded(db->pCollNeededArg, db, (int)db->enc, zExternal);
+ sqliteFree(zExternal);
+ }
+ if( db->xCollNeeded16 ){
+ char const *zExternal;
+ sqlite3_value *pTmp = sqlite3GetTransientValue(db);
+ sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF8, SQLITE_STATIC);
+ zExternal = sqlite3ValueText(pTmp, SQLITE_UTF16NATIVE);
+ if( !zExternal ) return;
+ db->xCollNeeded16(db->pCollNeededArg, db, (int)db->enc, zExternal);
+ }
+}
+
+/*
+** This routine is called if the collation factory fails to deliver a
+** collation function in the best encoding but there may be other versions
+** of this collation function (for other text encodings) available. Use one
+** of these instead if they exist. Avoid a UTF-8 <-> UTF-16 conversion if
+** possible.
+*/
+static int synthCollSeq(Parse *pParse, CollSeq *pColl){
+ CollSeq *pColl2;
+ char *z = pColl->zName;
+ int n = strlen(z);
+ sqlite3 *db = pParse->db;
+ int i;
+ static const u8 aEnc[] = { SQLITE_UTF16BE, SQLITE_UTF16LE, SQLITE_UTF8 };
+ for(i=0; i<3; i++){
+ pColl2 = sqlite3FindCollSeq(db, aEnc[i], z, n, 0);
+ if( pColl2->xCmp!=0 ){
+ memcpy(pColl, pColl2, sizeof(CollSeq));
+ return SQLITE_OK;
+ }
+ }
+ if( pParse->nErr==0 ){
+ sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", n, z);
+ }
+ pParse->nErr++;
+ return SQLITE_ERROR;
+}
+
+/*
+** This routine is called on a collation sequence before it is used to
+** check that it is defined. An undefined collation sequence exists when
+** a database is loaded that contains references to collation sequences
+** that have not been defined by sqlite3_create_collation() etc.
+**
+** If required, this routine calls the 'collation needed' callback to
+** request a definition of the collating sequence. If this doesn't work,
+** an equivalent collating sequence that uses a text encoding different
+** from the main database is substituted, if one is available.
+*/
+int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){
+ if( pColl && !pColl->xCmp ){
+ /* No collation sequence of this type for this encoding is registered.
+ ** Call the collation factory to see if it can supply us with one.
+ */
+ callCollNeeded(pParse->db, pColl->zName, strlen(pColl->zName));
+ if( !pColl->xCmp && synthCollSeq(pParse, pColl) ){
+ return SQLITE_ERROR;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Call sqlite3CheckCollSeq() for all collating sequences in an index,
+** in order to verify that all the necessary collating sequences are
+** loaded.
+*/
+int sqlite3CheckIndexCollSeq(Parse *pParse, Index *pIdx){
+ if( pIdx ){
+ int i;
+ for(i=0; i<pIdx->nColumn; i++){
+ if( sqlite3CheckCollSeq(pParse, pIdx->keyInfo.aColl[i]) ){
+ return SQLITE_ERROR;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** This function returns the collation sequence for database native text
+** encoding identified by the string zName, length nName.
+**
+** If the requested collation sequence is not available, or not available
+** in the database native encoding, the collation factory is invoked to
+** request it. If the collation factory does not supply such a sequence,
+** and the sequence is available in another text encoding, then that is
+** returned instead.
+**
+** If no versions of the requested collations sequence are available, or
+** another error occurs, NULL is returned and an error message written into
+** pParse.
+*/
+CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName){
+ u8 enc = pParse->db->enc;
+ u8 initbusy = pParse->db->init.busy;
+ CollSeq *pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, initbusy);
+ if( nName<0 ) nName = strlen(zName);
+ if( !initbusy && (!pColl || !pColl->xCmp) ){
+ /* No collation sequence of this type for this encoding is registered.
+ ** Call the collation factory to see if it can supply us with one.
+ */
+ callCollNeeded(pParse->db, zName, nName);
+ pColl = sqlite3FindCollSeq(pParse->db, enc, zName, nName, 0);
+ if( pColl && !pColl->xCmp ){
+ /* There may be a version of the collation sequence that requires
+ ** translation between encodings. Search for it with synthCollSeq().
+ */
+ if( synthCollSeq(pParse, pColl) ){
+ return 0;
+ }
+ }
+ }
+
+ /* If nothing has been found, write the error message into pParse */
+ if( !initbusy && (!pColl || !pColl->xCmp) ){
+ if( pParse->nErr==0 ){
+ sqlite3ErrorMsg(pParse, "no such collation sequence: %.*s", nName, zName);
+ }
+ pColl = 0;
+ }
+ return pColl;
+}
+
+
+
+/*
+** Scan the column type name zType (length nType) and return the
+** associated affinity type.
+*/
+char sqlite3AffinityType(const char *zType, int nType){
+ int n, i;
+ static const struct {
+ const char *zSub; /* Keywords substring to search for */
+ char nSub; /* length of zSub */
+ char affinity; /* Affinity to return if it matches */
+ } substrings[] = {
+ {"INT", 3, SQLITE_AFF_INTEGER},
+ {"CHAR", 4, SQLITE_AFF_TEXT},
+ {"CLOB", 4, SQLITE_AFF_TEXT},
+ {"TEXT", 4, SQLITE_AFF_TEXT},
+ {"BLOB", 4, SQLITE_AFF_NONE},
+ };
+
+ if( nType==0 ){
+ return SQLITE_AFF_NONE;
+ }
+ for(i=0; i<sizeof(substrings)/sizeof(substrings[0]); i++){
+ int c1 = substrings[i].zSub[0];
+ int c2 = tolower(c1);
+ int limit = nType - substrings[i].nSub;
+ const char *z = substrings[i].zSub;
+ for(n=0; n<=limit; n++){
+ int c = zType[n];
+ if( (c==c1 || c==c2)
+ && 0==sqlite3StrNICmp(&zType[n], z, substrings[i].nSub) ){
+ return substrings[i].affinity;
+ }
+ }
+ }
+ return SQLITE_AFF_NUMERIC;
+}
+
+/*
+** Generate code that will increment the schema cookie.
+**
+** The schema cookie is used to determine when the schema for the
+** database changes. After each schema change, the cookie value
+** changes. When a process first reads the schema it records the
+** cookie. Thereafter, whenever it goes to access the database,
+** it checks the cookie to make sure the schema has not changed
+** since it was last read.
+**
+** This plan is not completely bullet-proof. It is possible for
+** the schema to change multiple times and for the cookie to be
+** set back to prior value. But schema changes are infrequent
+** and the probability of hitting the same cookie value is only
+** 1 chance in 2^32. So we're safe enough.
+*/
+void sqlite3ChangeCookie(sqlite3 *db, Vdbe *v, int iDb){
+ sqlite3VdbeAddOp(v, OP_Integer, db->aDb[iDb].schema_cookie+1, 0);
+ sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 0);
+}
+
+/*
+** Measure the number of characters needed to output the given
+** identifier. The number returned includes any quotes used
+** but does not include the null terminator.
+**
+** The estimate is conservative. It might be larger that what is
+** really needed.
+*/
+static int identLength(const char *z){
+ int n;
+ for(n=0; *z; n++, z++){
+ if( *z=='"' ){ n++; }
+ }
+ return n + 2;
+}
+
+/*
+** Write an identifier onto the end of the given string. Add
+** quote characters as needed.
+*/
+static void identPut(char *z, int *pIdx, char *zSignedIdent){
+ unsigned char *zIdent = (unsigned char*)zSignedIdent;
+ int i, j, needQuote;
+ i = *pIdx;
+ for(j=0; zIdent[j]; j++){
+ if( !isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
+ }
+ needQuote = zIdent[j]!=0 || isdigit(zIdent[0])
+ || sqlite3KeywordCode(zIdent, j)!=TK_ID;
+ if( needQuote ) z[i++] = '"';
+ for(j=0; zIdent[j]; j++){
+ z[i++] = zIdent[j];
+ if( zIdent[j]=='"' ) z[i++] = '"';
+ }
+ if( needQuote ) z[i++] = '"';
+ z[i] = 0;
+ *pIdx = i;
+}
+
+/*
+** Generate a CREATE TABLE statement appropriate for the given
+** table. Memory to hold the text of the statement is obtained
+** from sqliteMalloc() and must be freed by the calling function.
+*/
+static char *createTableStmt(Table *p){
+ int i, k, n;
+ char *zStmt;
+ char *zSep, *zSep2, *zEnd, *z;
+ Column *pCol;
+ n = 0;
+ for(pCol = p->aCol, i=0; i<p->nCol; i++, pCol++){
+ n += identLength(pCol->zName);
+ z = pCol->zType;
+ if( z ){
+ n += (strlen(z) + 1);
+ }
+ }
+ n += identLength(p->zName);
+ if( n<50 ){
+ zSep = "";
+ zSep2 = ",";
+ zEnd = ")";
+ }else{
+ zSep = "\n ";
+ zSep2 = ",\n ";
+ zEnd = "\n)";
+ }
+ n += 35 + 6*p->nCol;
+ zStmt = sqliteMallocRaw( n );
+ if( zStmt==0 ) return 0;
+ strcpy(zStmt, p->iDb==1 ? "CREATE TEMP TABLE " : "CREATE TABLE ");
+ k = strlen(zStmt);
+ identPut(zStmt, &k, p->zName);
+ zStmt[k++] = '(';
+ for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
+ strcpy(&zStmt[k], zSep);
+ k += strlen(&zStmt[k]);
+ zSep = zSep2;
+ identPut(zStmt, &k, pCol->zName);
+ if( (z = pCol->zType)!=0 ){
+ zStmt[k++] = ' ';
+ strcpy(&zStmt[k], z);
+ k += strlen(z);
+ }
+ }
+ strcpy(&zStmt[k], zEnd);
+ return zStmt;
+}
+
+/*
+** This routine is called to report the final ")" that terminates
+** a CREATE TABLE statement.
+**
+** The table structure that other action routines have been building
+** is added to the internal hash tables, assuming no errors have
+** occurred.
+**
+** An entry for the table is made in the master table on disk, unless
+** this is a temporary table or db->init.busy==1. When db->init.busy==1
+** it means we are reading the sqlite_master table because we just
+** connected to the database or because the sqlite_master table has
+** recently changes, so the entry for this table already exists in
+** the sqlite_master table. We do not want to create it again.
+**
+** If the pSelect argument is not NULL, it means that this routine
+** was called to create a table generated from a
+** "CREATE TABLE ... AS SELECT ..." statement. The column names of
+** the new table will match the result set of the SELECT.
+*/
+void sqlite3EndTable(Parse *pParse, Token *pEnd, Select *pSelect){
+ Table *p;
+ sqlite3 *db = pParse->db;
+
+ if( (pEnd==0 && pSelect==0) || pParse->nErr || sqlite3_malloc_failed ) return;
+ p = pParse->pNewTable;
+ if( p==0 ) return;
+
+ assert( !db->init.busy || !pSelect );
+
+ /* If the db->init.busy is 1 it means we are reading the SQL off the
+ ** "sqlite_master" or "sqlite_temp_master" table on the disk.
+ ** So do not write to the disk again. Extract the root page number
+ ** for the table from the db->init.newTnum field. (The page number
+ ** should have been put there by the sqliteOpenCb routine.)
+ */
+ if( db->init.busy ){
+ p->tnum = db->init.newTnum;
+ }
+
+ /* If not initializing, then create a record for the new table
+ ** in the SQLITE_MASTER table of the database. The record number
+ ** for the new table entry should already be on the stack.
+ **
+ ** If this is a TEMPORARY table, write the entry into the auxiliary
+ ** file instead of into the main database file.
+ */
+ if( !db->init.busy ){
+ int n;
+ Vdbe *v;
+
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return;
+
+ if( p->pSelect==0 ){
+ /* A regular table */
+ sqlite3VdbeAddOp(v, OP_CreateTable, p->iDb, 0);
+ }else{
+ /* A view */
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ }
+
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+
+ /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
+ ** statement to populate the new table. The root-page number for the
+ ** new table is on the top of the vdbe stack.
+ **
+ ** Once the SELECT has been coded by sqlite3Select(), it is in a
+ ** suitable state to query for the column names and types to be used
+ ** by the new table.
+ */
+ if( pSelect ){
+ Table *pSelTab;
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, p->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenWrite, 1, 0);
+ pParse->nTab = 2;
+ sqlite3Select(pParse, pSelect, SRT_Table, 1, 0, 0, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Close, 1, 0);
+ if( pParse->nErr==0 ){
+ pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSelect);
+ if( pSelTab==0 ) return;
+ assert( p->aCol==0 );
+ p->nCol = pSelTab->nCol;
+ p->aCol = pSelTab->aCol;
+ pSelTab->nCol = 0;
+ pSelTab->aCol = 0;
+ sqlite3DeleteTable(0, pSelTab);
+ }
+ }
+
+ sqlite3OpenMasterTable(v, p->iDb);
+
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, p->pSelect==0?"table":"view",P3_STATIC);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, p->zName, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, p->zName, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 3, 0);
+
+ if( pSelect ){
+ char *z = createTableStmt(p);
+ n = z ? strlen(z) : 0;
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeChangeP3(v, -1, z, n);
+ sqliteFree(z);
+ }else{
+ if( p->pSelect ){
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, "CREATE VIEW ", P3_STATIC);
+ }else{
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, "CREATE TABLE ", P3_STATIC);
+ }
+ assert( pEnd!=0 );
+ n = Addr(pEnd->z) - Addr(pParse->sNameToken.z) + 1;
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeChangeP3(v, -1, pParse->sNameToken.z, n);
+ sqlite3VdbeAddOp(v, OP_Concat, 0, 0);
+ }
+ sqlite3VdbeOp3(v, OP_MakeRecord, 5, 0, "tttit", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0);
+ sqlite3ChangeCookie(db, v, p->iDb);
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeOp3(v, OP_ParseSchema, p->iDb, 0,
+ sqlite3MPrintf("tbl_name='%q'",p->zName), P3_DYNAMIC);
+ }
+
+ /* Add the table to the in-memory representation of the database.
+ */
+ if( db->init.busy && pParse->nErr==0 ){
+ Table *pOld;
+ FKey *pFKey;
+ Db *pDb = &db->aDb[p->iDb];
+ pOld = sqlite3HashInsert(&pDb->tblHash, p->zName, strlen(p->zName)+1, p);
+ if( pOld ){
+ assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
+ return;
+ }
+ for(pFKey=p->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+ int nTo = strlen(pFKey->zTo) + 1;
+ pFKey->pNextTo = sqlite3HashFind(&pDb->aFKey, pFKey->zTo, nTo);
+ sqlite3HashInsert(&pDb->aFKey, pFKey->zTo, nTo, pFKey);
+ }
+ pParse->pNewTable = 0;
+ db->nTable++;
+ db->flags |= SQLITE_InternChanges;
+ }
+}
+
+/*
+** The parser calls this routine in order to create a new VIEW
+*/
+void sqlite3CreateView(
+ Parse *pParse, /* The parsing context */
+ Token *pBegin, /* The CREATE token that begins the statement */
+ Token *pName1, /* The token that holds the name of the view */
+ Token *pName2, /* The token that holds the name of the view */
+ Select *pSelect, /* A SELECT statement that will become the new view */
+ int isTemp /* TRUE for a TEMPORARY view */
+){
+ Table *p;
+ int n;
+ const unsigned char *z;
+ Token sEnd;
+ DbFixer sFix;
+ Token *pName;
+
+ sqlite3StartTable(pParse, pBegin, pName1, pName2, isTemp, 1);
+ p = pParse->pNewTable;
+ if( p==0 || pParse->nErr ){
+ sqlite3SelectDelete(pSelect);
+ return;
+ }
+ sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( sqlite3FixInit(&sFix, pParse, p->iDb, "view", pName)
+ && sqlite3FixSelect(&sFix, pSelect)
+ ){
+ sqlite3SelectDelete(pSelect);
+ return;
+ }
+
+ /* Make a copy of the entire SELECT statement that defines the view.
+ ** This will force all the Expr.token.z values to be dynamically
+ ** allocated rather than point to the input string - which means that
+ ** they will persist after the current sqlite3_exec() call returns.
+ */
+ p->pSelect = sqlite3SelectDup(pSelect);
+ sqlite3SelectDelete(pSelect);
+ if( !pParse->db->init.busy ){
+ sqlite3ViewGetColumnNames(pParse, p);
+ }
+
+ /* Locate the end of the CREATE VIEW statement. Make sEnd point to
+ ** the end.
+ */
+ sEnd = pParse->sLastToken;
+ if( sEnd.z[0]!=0 && sEnd.z[0]!=';' ){
+ sEnd.z += sEnd.n;
+ }
+ sEnd.n = 0;
+ n = sEnd.z - pBegin->z;
+ z = (const unsigned char*)pBegin->z;
+ while( n>0 && (z[n-1]==';' || isspace(z[n-1])) ){ n--; }
+ sEnd.z = &z[n-1];
+ sEnd.n = 1;
+
+ /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
+ sqlite3EndTable(pParse, &sEnd, 0);
+ return;
+}
+
+/*
+** The Table structure pTable is really a VIEW. Fill in the names of
+** the columns of the view in the pTable structure. Return the number
+** of errors. If an error is seen leave an error message in pParse->zErrMsg.
+*/
+int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
+ ExprList *pEList;
+ Select *pSel;
+ Table *pSelTab;
+ int nErr = 0;
+
+ assert( pTable );
+
+ /* A positive nCol means the columns names for this view are
+ ** already known.
+ */
+ if( pTable->nCol>0 ) return 0;
+
+ /* A negative nCol is a special marker meaning that we are currently
+ ** trying to compute the column names. If we enter this routine with
+ ** a negative nCol, it means two or more views form a loop, like this:
+ **
+ ** CREATE VIEW one AS SELECT * FROM two;
+ ** CREATE VIEW two AS SELECT * FROM one;
+ **
+ ** Actually, this error is caught previously and so the following test
+ ** should always fail. But we will leave it in place just to be safe.
+ */
+ if( pTable->nCol<0 ){
+ sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable->zName);
+ return 1;
+ }
+
+ /* If we get this far, it means we need to compute the table names.
+ */
+ assert( pTable->pSelect ); /* If nCol==0, then pTable must be a VIEW */
+ pSel = pTable->pSelect;
+
+ /* Note that the call to sqlite3ResultSetOfSelect() will expand any
+ ** "*" elements in this list. But we will need to restore the list
+ ** back to its original configuration afterwards, so we save a copy of
+ ** the original in pEList.
+ */
+ pEList = pSel->pEList;
+ pSel->pEList = sqlite3ExprListDup(pEList);
+ if( pSel->pEList==0 ){
+ pSel->pEList = pEList;
+ return 1; /* Malloc failed */
+ }
+ pTable->nCol = -1;
+ pSelTab = sqlite3ResultSetOfSelect(pParse, 0, pSel);
+ if( pSelTab ){
+ assert( pTable->aCol==0 );
+ pTable->nCol = pSelTab->nCol;
+ pTable->aCol = pSelTab->aCol;
+ pSelTab->nCol = 0;
+ pSelTab->aCol = 0;
+ sqlite3DeleteTable(0, pSelTab);
+ DbSetProperty(pParse->db, pTable->iDb, DB_UnresetViews);
+ }else{
+ pTable->nCol = 0;
+ nErr++;
+ }
+ sqlite3SelectUnbind(pSel);
+ sqlite3ExprListDelete(pSel->pEList);
+ pSel->pEList = pEList;
+ return nErr;
+}
+
+/*
+** Clear the column names from every VIEW in database idx.
+*/
+static void sqliteViewResetAll(sqlite3 *db, int idx){
+ HashElem *i;
+ if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
+ for(i=sqliteHashFirst(&db->aDb[idx].tblHash); i; i=sqliteHashNext(i)){
+ Table *pTab = sqliteHashData(i);
+ if( pTab->pSelect ){
+ sqliteResetColumnNames(pTab);
+ }
+ }
+ DbClearProperty(db, idx, DB_UnresetViews);
+}
+
+/*
+** This routine is called to do the work of a DROP TABLE statement.
+** pName is the name of the table to be dropped.
+*/
+void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView){
+ Table *pTab;
+ Vdbe *v;
+ int base;
+ sqlite3 *db = pParse->db;
+ int iDb;
+
+ if( pParse->nErr || sqlite3_malloc_failed ) goto exit_drop_table;
+ assert( pName->nSrc==1 );
+ pTab = sqlite3LocateTable(pParse, pName->a[0].zName, pName->a[0].zDatabase);
+
+ if( pTab==0 ) goto exit_drop_table;
+ iDb = pTab->iDb;
+ assert( iDb>=0 && iDb<db->nDb );
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int code;
+ const char *zTab = SCHEMA_TABLE(pTab->iDb);
+ const char *zDb = db->aDb[pTab->iDb].zName;
+ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
+ goto exit_drop_table;
+ }
+ if( isView ){
+ if( iDb==1 ){
+ code = SQLITE_DROP_TEMP_VIEW;
+ }else{
+ code = SQLITE_DROP_VIEW;
+ }
+ }else{
+ if( iDb==1 ){
+ code = SQLITE_DROP_TEMP_TABLE;
+ }else{
+ code = SQLITE_DROP_TABLE;
+ }
+ }
+ if( sqlite3AuthCheck(pParse, code, pTab->zName, 0, zDb) ){
+ goto exit_drop_table;
+ }
+ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
+ goto exit_drop_table;
+ }
+ }
+#endif
+ if( pTab->readOnly ){
+ sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
+ pParse->nErr++;
+ goto exit_drop_table;
+ }
+ if( isView && pTab->pSelect==0 ){
+ sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
+ goto exit_drop_table;
+ }
+ if( !isView && pTab->pSelect ){
+ sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
+ goto exit_drop_table;
+ }
+
+ /* Generate code to remove the table from the master table
+ ** on disk.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ static const VdbeOpList dropTable[] = {
+ { OP_Rewind, 0, ADDR(13), 0},
+ { OP_String8, 0, 0, 0}, /* 1 */
+ { OP_MemStore, 1, 1, 0},
+ { OP_MemLoad, 1, 0, 0}, /* 3 */
+ { OP_Column, 0, 2, 0}, /* sqlite_master.tbl_name */
+ { OP_Ne, 0, ADDR(12), 0},
+ { OP_String8, 0, 0, "trigger"},
+ { OP_Column, 0, 2, 0}, /* sqlite_master.type */
+ { OP_Eq, 0, ADDR(12), 0},
+ { OP_Delete, 0, 0, 0},
+ { OP_Rewind, 0, ADDR(13), 0},
+ { OP_Goto, 0, ADDR(3), 0},
+ { OP_Next, 0, ADDR(3), 0}, /* 12 */
+ };
+ Index *pIdx;
+ Trigger *pTrigger;
+ sqlite3BeginWriteOperation(pParse, 0, pTab->iDb);
+
+ /* Drop all triggers associated with the table being dropped. Code
+ ** is generated to remove entries from sqlite_master and/or
+ ** sqlite_temp_master if required.
+ */
+ pTrigger = pTab->pTrigger;
+ while( pTrigger ){
+ assert( pTrigger->iDb==pTab->iDb || pTrigger->iDb==1 );
+ sqlite3DropTriggerPtr(pParse, pTrigger, 1);
+ pTrigger = pTrigger->pNext;
+ }
+
+ /* Drop all SQLITE_MASTER table and index entries that refer to the
+ ** table. The program name loops through the master table and deletes
+ ** every row that refers to a table of the same name as the one being
+ ** dropped. Triggers are handled seperately because a trigger can be
+ ** created in the temp database that refers to a table in another
+ ** database.
+ */
+ sqlite3OpenMasterTable(v, pTab->iDb);
+ base = sqlite3VdbeAddOpList(v, ArraySize(dropTable), dropTable);
+ sqlite3VdbeChangeP3(v, base+1, pTab->zName, 0);
+ sqlite3ChangeCookie(db, v, pTab->iDb);
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Destroy, pTab->tnum, pTab->iDb);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb);
+ }
+ }
+ sqlite3VdbeOp3(v, OP_DropTable, pTab->iDb, 0, pTab->zName, 0);
+ }
+ sqliteViewResetAll(db, iDb);
+
+exit_drop_table:
+ sqlite3SrcListDelete(pName);
+}
+
+/*
+** This routine is called to create a new foreign key on the table
+** currently under construction. pFromCol determines which columns
+** in the current table point to the foreign key. If pFromCol==0 then
+** connect the key to the last column inserted. pTo is the name of
+** the table referred to. pToCol is a list of tables in the other
+** pTo table that the foreign key points to. flags contains all
+** information about the conflict resolution algorithms specified
+** in the ON DELETE, ON UPDATE and ON INSERT clauses.
+**
+** An FKey structure is created and added to the table currently
+** under construction in the pParse->pNewTable field. The new FKey
+** is not linked into db->aFKey at this point - that does not happen
+** until sqlite3EndTable().
+**
+** The foreign key is set for IMMEDIATE processing. A subsequent call
+** to sqlite3DeferForeignKey() might change this to DEFERRED.
+*/
+void sqlite3CreateForeignKey(
+ Parse *pParse, /* Parsing context */
+ ExprList *pFromCol, /* Columns in this table that point to other table */
+ Token *pTo, /* Name of the other table */
+ ExprList *pToCol, /* Columns in the other table */
+ int flags /* Conflict resolution algorithms. */
+){
+ Table *p = pParse->pNewTable;
+ int nByte;
+ int i;
+ int nCol;
+ char *z;
+ FKey *pFKey = 0;
+
+ assert( pTo!=0 );
+ if( p==0 || pParse->nErr ) goto fk_end;
+ if( pFromCol==0 ){
+ int iCol = p->nCol-1;
+ if( iCol<0 ) goto fk_end;
+ if( pToCol && pToCol->nExpr!=1 ){
+ sqlite3ErrorMsg(pParse, "foreign key on %s"
+ " should reference only one column of table %T",
+ p->aCol[iCol].zName, pTo);
+ goto fk_end;
+ }
+ nCol = 1;
+ }else if( pToCol && pToCol->nExpr!=pFromCol->nExpr ){
+ sqlite3ErrorMsg(pParse,
+ "number of columns in foreign key does not match the number of "
+ "columns in the referenced table");
+ goto fk_end;
+ }else{
+ nCol = pFromCol->nExpr;
+ }
+ nByte = sizeof(*pFKey) + nCol*sizeof(pFKey->aCol[0]) + pTo->n + 1;
+ if( pToCol ){
+ for(i=0; i<pToCol->nExpr; i++){
+ nByte += strlen(pToCol->a[i].zName) + 1;
+ }
+ }
+ pFKey = sqliteMalloc( nByte );
+ if( pFKey==0 ) goto fk_end;
+ pFKey->pFrom = p;
+ pFKey->pNextFrom = p->pFKey;
+ z = (char*)&pFKey[1];
+ pFKey->aCol = (struct sColMap*)z;
+ z += sizeof(struct sColMap)*nCol;
+ pFKey->zTo = z;
+ memcpy(z, pTo->z, pTo->n);
+ z[pTo->n] = 0;
+ z += pTo->n+1;
+ pFKey->pNextTo = 0;
+ pFKey->nCol = nCol;
+ if( pFromCol==0 ){
+ pFKey->aCol[0].iFrom = p->nCol-1;
+ }else{
+ for(i=0; i<nCol; i++){
+ int j;
+ for(j=0; j<p->nCol; j++){
+ if( sqlite3StrICmp(p->aCol[j].zName, pFromCol->a[i].zName)==0 ){
+ pFKey->aCol[i].iFrom = j;
+ break;
+ }
+ }
+ if( j>=p->nCol ){
+ sqlite3ErrorMsg(pParse,
+ "unknown column \"%s\" in foreign key definition",
+ pFromCol->a[i].zName);
+ goto fk_end;
+ }
+ }
+ }
+ if( pToCol ){
+ for(i=0; i<nCol; i++){
+ int n = strlen(pToCol->a[i].zName);
+ pFKey->aCol[i].zCol = z;
+ memcpy(z, pToCol->a[i].zName, n);
+ z[n] = 0;
+ z += n+1;
+ }
+ }
+ pFKey->isDeferred = 0;
+ pFKey->deleteConf = flags & 0xff;
+ pFKey->updateConf = (flags >> 8 ) & 0xff;
+ pFKey->insertConf = (flags >> 16 ) & 0xff;
+
+ /* Link the foreign key to the table as the last step.
+ */
+ p->pFKey = pFKey;
+ pFKey = 0;
+
+fk_end:
+ sqliteFree(pFKey);
+ sqlite3ExprListDelete(pFromCol);
+ sqlite3ExprListDelete(pToCol);
+}
+
+/*
+** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
+** clause is seen as part of a foreign key definition. The isDeferred
+** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
+** The behavior of the most recently created foreign key is adjusted
+** accordingly.
+*/
+void sqlite3DeferForeignKey(Parse *pParse, int isDeferred){
+ Table *pTab;
+ FKey *pFKey;
+ if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return;
+ pFKey->isDeferred = isDeferred;
+}
+
+/*
+** Create a new index for an SQL table. pIndex is the name of the index
+** and pTable is the name of the table that is to be indexed. Both will
+** be NULL for a primary key or an index that is created to satisfy a
+** UNIQUE constraint. If pTable and pIndex are NULL, use pParse->pNewTable
+** as the table to be indexed. pParse->pNewTable is a table that is
+** currently being constructed by a CREATE TABLE statement.
+**
+** pList is a list of columns to be indexed. pList will be NULL if this
+** is a primary key or unique-constraint on the most recent column added
+** to the table currently under construction.
+*/
+void sqlite3CreateIndex(
+ Parse *pParse, /* All information about this parse */
+ Token *pName1, /* First part of index name. May be NULL */
+ Token *pName2, /* Second part of index name. May be NULL */
+ SrcList *pTblName, /* Table to index. Use pParse->pNewTable if 0 */
+ ExprList *pList, /* A list of columns to be indexed */
+ int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
+ Token *pStart, /* The CREATE token that begins a CREATE TABLE statement */
+ Token *pEnd /* The ")" that closes the CREATE INDEX statement */
+){
+ Table *pTab = 0; /* Table to be indexed */
+ Index *pIndex = 0; /* The index to be created */
+ char *zName = 0;
+ int i, j;
+ Token nullId; /* Fake token for an empty ID list */
+ DbFixer sFix; /* For assigning database names to pTable */
+ int isTemp; /* True for a temporary index */
+ sqlite3 *db = pParse->db;
+
+ int iDb; /* Index of the database that is being written */
+ Token *pName = 0; /* Unqualified name of the index to create */
+
+ if( pParse->nErr || sqlite3_malloc_failed ) goto exit_create_index;
+
+ /*
+ ** Find the table that is to be indexed. Return early if not found.
+ */
+ if( pTblName!=0 ){
+
+ /* Use the two-part index name to determine the database
+ ** to search for the table. 'Fix' the table name to this db
+ ** before looking up the table.
+ */
+ assert( pName1 && pName2 );
+ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( iDb<0 ) goto exit_create_index;
+
+ /* If the index name was unqualified, check if the the table
+ ** is a temp table. If so, set the database to 1.
+ */
+ pTab = sqlite3SrcListLookup(pParse, pTblName);
+ if( pName2 && pName2->n==0 && pTab && pTab->iDb==1 ){
+ iDb = 1;
+ }
+
+ if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) &&
+ sqlite3FixSrcList(&sFix, pTblName)
+ ){
+ goto exit_create_index;
+ }
+ pTab = sqlite3LocateTable(pParse, pTblName->a[0].zName,
+ pTblName->a[0].zDatabase);
+ if( !pTab ) goto exit_create_index;
+ assert( iDb==pTab->iDb );
+ }else{
+ assert( pName==0 );
+ pTab = pParse->pNewTable;
+ iDb = pTab->iDb;
+ }
+
+ if( pTab==0 || pParse->nErr ) goto exit_create_index;
+ if( pTab->readOnly ){
+ sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
+ goto exit_create_index;
+ }
+ if( pTab->pSelect ){
+ sqlite3ErrorMsg(pParse, "views may not be indexed");
+ goto exit_create_index;
+ }
+ isTemp = pTab->iDb==1;
+
+ /*
+ ** Find the name of the index. Make sure there is not already another
+ ** index or table with the same name.
+ **
+ ** Exception: If we are reading the names of permanent indices from the
+ ** sqlite_master table (because some other process changed the schema) and
+ ** one of the index names collides with the name of a temporary table or
+ ** index, then we will continue to process this index.
+ **
+ ** If pName==0 it means that we are
+ ** dealing with a primary key or UNIQUE constraint. We have to invent our
+ ** own name.
+ */
+ if( pName ){
+ zName = sqlite3NameFromToken(pName);
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index;
+ if( zName==0 ) goto exit_create_index;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ goto exit_create_index;
+ }
+ if( !db->init.busy ){
+ Index *pISameName; /* Another index with the same name */
+ Table *pTSameName; /* A table with same name as the index */
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) goto exit_create_index;
+ if( (pISameName = sqlite3FindIndex(db, zName, db->aDb[iDb].zName))!=0 ){
+ sqlite3ErrorMsg(pParse, "index %s already exists", zName);
+ goto exit_create_index;
+ }
+ if( (pTSameName = sqlite3FindTable(db, zName, 0))!=0 ){
+ sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
+ goto exit_create_index;
+ }
+ }
+ }else if( pName==0 ){
+ char zBuf[30];
+ int n;
+ Index *pLoop;
+ for(pLoop=pTab->pIndex, n=1; pLoop; pLoop=pLoop->pNext, n++){}
+ sprintf(zBuf,"_%d",n);
+ zName = 0;
+ sqlite3SetString(&zName, "sqlite_autoindex_", pTab->zName, zBuf, (char*)0);
+ if( zName==0 ) goto exit_create_index;
+ }
+
+ /* Check for authorization to create an index.
+ */
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ const char *zDb = db->aDb[pTab->iDb].zName;
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
+ goto exit_create_index;
+ }
+ i = SQLITE_CREATE_INDEX;
+ if( isTemp ) i = SQLITE_CREATE_TEMP_INDEX;
+ if( sqlite3AuthCheck(pParse, i, zName, pTab->zName, zDb) ){
+ goto exit_create_index;
+ }
+ }
+#endif
+
+ /* If pList==0, it means this routine was called to make a primary
+ ** key out of the last column added to the table under construction.
+ ** So create a fake list to simulate this.
+ */
+ if( pList==0 ){
+ nullId.z = pTab->aCol[pTab->nCol-1].zName;
+ nullId.n = strlen(nullId.z);
+ pList = sqlite3ExprListAppend(0, 0, &nullId);
+ if( pList==0 ) goto exit_create_index;
+ }
+
+ /*
+ ** Allocate the index structure.
+ */
+ pIndex = sqliteMalloc( sizeof(Index) + strlen(zName) + 1 +
+ (sizeof(int) + sizeof(CollSeq*))*pList->nExpr );
+ if( pIndex==0 ) goto exit_create_index;
+ pIndex->aiColumn = (int*)&pIndex->keyInfo.aColl[pList->nExpr];
+ pIndex->zName = (char*)&pIndex->aiColumn[pList->nExpr];
+ strcpy(pIndex->zName, zName);
+ pIndex->pTable = pTab;
+ pIndex->nColumn = pList->nExpr;
+ pIndex->onError = onError;
+ pIndex->autoIndex = pName==0;
+ pIndex->iDb = iDb;
+
+ /* Scan the names of the columns of the table to be indexed and
+ ** load the column indices into the Index structure. Report an error
+ ** if any column is not found.
+ */
+ for(i=0; i<pList->nExpr; i++){
+ for(j=0; j<pTab->nCol; j++){
+ if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[j].zName)==0 ) break;
+ }
+ if( j>=pTab->nCol ){
+ sqlite3ErrorMsg(pParse, "table %s has no column named %s",
+ pTab->zName, pList->a[i].zName);
+ goto exit_create_index;
+ }
+ pIndex->aiColumn[i] = j;
+ if( pList->a[i].pExpr ){
+ assert( pList->a[i].pExpr->pColl );
+ pIndex->keyInfo.aColl[i] = pList->a[i].pExpr->pColl;
+ }else{
+ pIndex->keyInfo.aColl[i] = pTab->aCol[j].pColl;
+ }
+ assert( pIndex->keyInfo.aColl[i] );
+ if( !db->init.busy &&
+ sqlite3CheckCollSeq(pParse, pIndex->keyInfo.aColl[i])
+ ){
+ goto exit_create_index;
+ }
+ }
+ pIndex->keyInfo.nField = pList->nExpr;
+
+ if( pTab==pParse->pNewTable ){
+ /* This routine has been called to create an automatic index as a
+ ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
+ ** a PRIMARY KEY or UNIQUE clause following the column definitions.
+ ** i.e. one of:
+ **
+ ** CREATE TABLE t(x PRIMARY KEY, y);
+ ** CREATE TABLE t(x, y, UNIQUE(x, y));
+ **
+ ** Either way, check to see if the table already has such an index. If
+ ** so, don't bother creating this one. This only applies to
+ ** automatically created indices. Users can do as they wish with
+ ** explicit indices.
+ */
+ Index *pIdx;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int k;
+ assert( pIdx->onError!=OE_None );
+ assert( pIdx->autoIndex );
+ assert( pIndex->onError!=OE_None );
+
+ if( pIdx->nColumn!=pIndex->nColumn ) continue;
+ for(k=0; k<pIdx->nColumn; k++){
+ if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
+ if( pIdx->keyInfo.aColl[k]!=pIndex->keyInfo.aColl[k] ) break;
+ }
+ if( k==pIdx->nColumn ){
+ if( pIdx->onError!=pIndex->onError ){
+ /* This constraint creates the same index as a previous
+ ** constraint specified somewhere in the CREATE TABLE statement.
+ ** However the ON CONFLICT clauses are different. If both this
+ ** constraint and the previous equivalent constraint have explicit
+ ** ON CONFLICT clauses this is an error. Otherwise, use the
+ ** explicitly specified behaviour for the index.
+ */
+ if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){
+ sqlite3ErrorMsg(pParse,
+ "conflicting ON CONFLICT clauses specified", 0);
+ }
+ if( pIdx->onError==OE_Default ){
+ pIdx->onError = pIndex->onError;
+ }
+ }
+ goto exit_create_index;
+ }
+ }
+ }
+
+ /* Link the new Index structure to its table and to the other
+ ** in-memory database structures.
+ */
+ if( db->init.busy ){
+ Index *p;
+ p = sqlite3HashInsert(&db->aDb[pIndex->iDb].idxHash,
+ pIndex->zName, strlen(pIndex->zName)+1, pIndex);
+ if( p ){
+ assert( p==pIndex ); /* Malloc must have failed */
+ goto exit_create_index;
+ }
+ db->flags |= SQLITE_InternChanges;
+ if( pTblName!=0 ){
+ pIndex->tnum = db->init.newTnum;
+ }
+ }
+
+ /* If the db->init.busy is 0 then create the index on disk. This
+ ** involves writing the index into the master table and filling in the
+ ** index with the current table contents.
+ **
+ ** The db->init.busy is 0 when the user first enters a CREATE INDEX
+ ** command. db->init.busy is 1 when a database is opened and
+ ** CREATE INDEX statements are read out of the master table. In
+ ** the latter case the index already exists on disk, which is why
+ ** we don't want to recreate it.
+ **
+ ** If pTblName==0 it means this index is generated as a primary key
+ ** or UNIQUE constraint of a CREATE TABLE statement. Since the table
+ ** has just been created, it contains no data and the index initialization
+ ** step can be skipped.
+ */
+ else if( db->init.busy==0 ){
+ int n;
+ Vdbe *v;
+ int lbl1, lbl2;
+
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto exit_create_index;
+ if( pTblName!=0 ){
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3OpenMasterTable(v, iDb);
+ }
+ sqlite3VdbeAddOp(v, OP_NewRecno, 0, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, "index", P3_STATIC);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pIndex->zName, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->zName, 0);
+ sqlite3VdbeAddOp(v, OP_CreateIndex, iDb, 0);
+ if( pTblName ){
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, iDb, 0);
+ sqlite3VdbeOp3(v, OP_OpenWrite, 1, 0,
+ (char*)&pIndex->keyInfo, P3_KEYINFO);
+ }
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ if( pStart && pEnd ){
+ if( onError==OE_None ){
+ sqlite3VdbeChangeP3(v, -1, "CREATE INDEX ", P3_STATIC);
+ }else{
+ sqlite3VdbeChangeP3(v, -1, "CREATE UNIQUE INDEX ", P3_STATIC);
+ }
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ n = Addr(pEnd->z) - Addr(pName->z) + 1;
+ sqlite3VdbeChangeP3(v, -1, pName->z, n);
+ sqlite3VdbeAddOp(v, OP_Concat, 0, 0);
+ }
+ sqlite3VdbeOp3(v, OP_MakeRecord, 5, 0, "tttit", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, 0, 0);
+ if( pTblName ){
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenRead, 2, pTab->tnum);
+ /* VdbeComment((v, "%s", pTab->zName)); */
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, 2, pTab->nCol);
+ lbl2 = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Rewind, 2, lbl2);
+ lbl1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3GenerateIndexKey(v, pIndex, 2);
+ sqlite3VdbeOp3(v, OP_IdxPut, 1, pIndex->onError!=OE_None,
+ "indexed columns are not unique", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_Next, 2, lbl1);
+ sqlite3VdbeResolveLabel(v, lbl2);
+ sqlite3VdbeAddOp(v, OP_Close, 2, 0);
+ sqlite3VdbeAddOp(v, OP_Close, 1, 0);
+ sqlite3ChangeCookie(db, v, iDb);
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeOp3(v, OP_ParseSchema, iDb, 0,
+ sqlite3MPrintf("name='%q'", pIndex->zName), P3_DYNAMIC);
+ }
+ }
+
+ /* When adding an index to the list of indices for a table, make
+ ** sure all indices labeled OE_Replace come after all those labeled
+ ** OE_Ignore. This is necessary for the correct operation of UPDATE
+ ** and INSERT.
+ */
+ if( db->init.busy || pTblName==0 ){
+ if( onError!=OE_Replace || pTab->pIndex==0
+ || pTab->pIndex->onError==OE_Replace){
+ pIndex->pNext = pTab->pIndex;
+ pTab->pIndex = pIndex;
+ }else{
+ Index *pOther = pTab->pIndex;
+ while( pOther->pNext && pOther->pNext->onError!=OE_Replace ){
+ pOther = pOther->pNext;
+ }
+ pIndex->pNext = pOther->pNext;
+ pOther->pNext = pIndex;
+ }
+ pIndex = 0;
+ }
+
+ /* Clean up before exiting */
+exit_create_index:
+ if( pIndex ){
+ freeIndex(pIndex);
+ }
+ sqlite3ExprListDelete(pList);
+ sqlite3SrcListDelete(pTblName);
+ sqliteFree(zName);
+ return;
+}
+
+/*
+** This routine will drop an existing named index. This routine
+** implements the DROP INDEX statement.
+*/
+void sqlite3DropIndex(Parse *pParse, SrcList *pName){
+ Index *pIndex;
+ Vdbe *v;
+ sqlite3 *db = pParse->db;
+
+ if( pParse->nErr || sqlite3_malloc_failed ) return;
+ assert( pName->nSrc==1 );
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ) return;
+ pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase);
+ if( pIndex==0 ){
+ sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0);
+ pParse->checkSchema = 1;
+ goto exit_drop_index;
+ }
+ if( pIndex->autoIndex ){
+ sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
+ "or PRIMARY KEY constraint cannot be dropped", 0);
+ goto exit_drop_index;
+ }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int code = SQLITE_DROP_INDEX;
+ Table *pTab = pIndex->pTable;
+ const char *zDb = db->aDb[pIndex->iDb].zName;
+ const char *zTab = SCHEMA_TABLE(pIndex->iDb);
+ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
+ goto exit_drop_index;
+ }
+ if( pIndex->iDb ) code = SQLITE_DROP_TEMP_INDEX;
+ if( sqlite3AuthCheck(pParse, code, pIndex->zName, pTab->zName, zDb) ){
+ goto exit_drop_index;
+ }
+ }
+#endif
+
+ /* Generate code to remove the index and from the master table */
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ static const VdbeOpList dropIndex[] = {
+ { OP_Rewind, 0, ADDR(9), 0},
+ { OP_String8, 0, 0, 0}, /* 1 */
+ { OP_MemStore, 1, 1, 0},
+ { OP_MemLoad, 1, 0, 0}, /* 3 */
+ { OP_Column, 0, 1, 0},
+ { OP_Eq, 0, ADDR(8), 0},
+ { OP_Next, 0, ADDR(3), 0},
+ { OP_Goto, 0, ADDR(9), 0},
+ { OP_Delete, 0, 0, 0}, /* 8 */
+ };
+ int base;
+
+ sqlite3BeginWriteOperation(pParse, 0, pIndex->iDb);
+ sqlite3OpenMasterTable(v, pIndex->iDb);
+ base = sqlite3VdbeAddOpList(v, ArraySize(dropIndex), dropIndex);
+ sqlite3VdbeChangeP3(v, base+1, pIndex->zName, 0);
+ sqlite3ChangeCookie(db, v, pIndex->iDb);
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb);
+ sqlite3VdbeOp3(v, OP_DropIndex, pIndex->iDb, 0, pIndex->zName, 0);
+ }
+
+exit_drop_index:
+ sqlite3SrcListDelete(pName);
+}
+
+/*
+** Append a new element to the given IdList. Create a new IdList if
+** need be.
+**
+** A new IdList is returned, or NULL if malloc() fails.
+*/
+IdList *sqlite3IdListAppend(IdList *pList, Token *pToken){
+ if( pList==0 ){
+ pList = sqliteMalloc( sizeof(IdList) );
+ if( pList==0 ) return 0;
+ pList->nAlloc = 0;
+ }
+ if( pList->nId>=pList->nAlloc ){
+ struct IdList_item *a;
+ pList->nAlloc = pList->nAlloc*2 + 5;
+ a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]) );
+ if( a==0 ){
+ sqlite3IdListDelete(pList);
+ return 0;
+ }
+ pList->a = a;
+ }
+ memset(&pList->a[pList->nId], 0, sizeof(pList->a[0]));
+ pList->a[pList->nId].zName = sqlite3NameFromToken(pToken);
+ pList->nId++;
+ return pList;
+}
+
+/*
+** Append a new table name to the given SrcList. Create a new SrcList if
+** need be. A new entry is created in the SrcList even if pToken is NULL.
+**
+** A new SrcList is returned, or NULL if malloc() fails.
+**
+** If pDatabase is not null, it means that the table has an optional
+** database name prefix. Like this: "database.table". The pDatabase
+** points to the table name and the pTable points to the database name.
+** The SrcList.a[].zName field is filled with the table name which might
+** come from pTable (if pDatabase is NULL) or from pDatabase.
+** SrcList.a[].zDatabase is filled with the database name from pTable,
+** or with NULL if no database is specified.
+**
+** In other words, if call like this:
+**
+** sqlite3SrcListAppend(A,B,0);
+**
+** Then B is a table name and the database name is unspecified. If called
+** like this:
+**
+** sqlite3SrcListAppend(A,B,C);
+**
+** Then C is the table name and B is the database name.
+*/
+SrcList *sqlite3SrcListAppend(SrcList *pList, Token *pTable, Token *pDatabase){
+ struct SrcList_item *pItem;
+ if( pList==0 ){
+ pList = sqliteMalloc( sizeof(SrcList) );
+ if( pList==0 ) return 0;
+ pList->nAlloc = 1;
+ }
+ if( pList->nSrc>=pList->nAlloc ){
+ SrcList *pNew;
+ pList->nAlloc *= 2;
+ pNew = sqliteRealloc(pList,
+ sizeof(*pList) + (pList->nAlloc-1)*sizeof(pList->a[0]) );
+ if( pNew==0 ){
+ sqlite3SrcListDelete(pList);
+ return 0;
+ }
+ pList = pNew;
+ }
+ pItem = &pList->a[pList->nSrc];
+ memset(pItem, 0, sizeof(pList->a[0]));
+ if( pDatabase && pDatabase->z==0 ){
+ pDatabase = 0;
+ }
+ if( pDatabase && pTable ){
+ Token *pTemp = pDatabase;
+ pDatabase = pTable;
+ pTable = pTemp;
+ }
+ pItem->zName = sqlite3NameFromToken(pTable);
+ pItem->zDatabase = sqlite3NameFromToken(pDatabase);
+ pItem->iCursor = -1;
+ pList->nSrc++;
+ return pList;
+}
+
+/*
+** Assign cursors to all tables in a SrcList
+*/
+void sqlite3SrcListAssignCursors(Parse *pParse, SrcList *pList){
+ int i;
+ for(i=0; i<pList->nSrc; i++){
+ if( pList->a[i].iCursor<0 ){
+ pList->a[i].iCursor = pParse->nTab++;
+ }
+ }
+}
+
+/*
+** Add an alias to the last identifier on the given identifier list.
+*/
+void sqlite3SrcListAddAlias(SrcList *pList, Token *pToken){
+ if( pList && pList->nSrc>0 ){
+ pList->a[pList->nSrc-1].zAlias = sqlite3NameFromToken(pToken);
+ }
+}
+
+/*
+** Delete an IdList.
+*/
+void sqlite3IdListDelete(IdList *pList){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; i<pList->nId; i++){
+ sqliteFree(pList->a[i].zName);
+ }
+ sqliteFree(pList->a);
+ sqliteFree(pList);
+}
+
+/*
+** Return the index in pList of the identifier named zId. Return -1
+** if not found.
+*/
+int sqlite3IdListIndex(IdList *pList, const char *zName){
+ int i;
+ if( pList==0 ) return -1;
+ for(i=0; i<pList->nId; i++){
+ if( sqlite3StrICmp(pList->a[i].zName, zName)==0 ) return i;
+ }
+ return -1;
+}
+
+/*
+** Delete an entire SrcList including all its substructure.
+*/
+void sqlite3SrcListDelete(SrcList *pList){
+ int i;
+ struct SrcList_item *pItem;
+ if( pList==0 ) return;
+ for(pItem=pList->a, i=0; i<pList->nSrc; i++, pItem++){
+ sqliteFree(pItem->zDatabase);
+ sqliteFree(pItem->zName);
+ sqliteFree(pItem->zAlias);
+ if( pItem->pTab && pItem->pTab->isTransient ){
+ sqlite3DeleteTable(0, pItem->pTab);
+ }
+ sqlite3SelectDelete(pItem->pSelect);
+ sqlite3ExprDelete(pItem->pOn);
+ sqlite3IdListDelete(pItem->pUsing);
+ }
+ sqliteFree(pList);
+}
+
+/*
+** Begin a transaction
+*/
+void sqlite3BeginTransaction(Parse *pParse, int type){
+ sqlite3 *db;
+ Vdbe *v;
+ int i;
+
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
+ if( pParse->nErr || sqlite3_malloc_failed ) return;
+ if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ) return;
+
+ v = sqlite3GetVdbe(pParse);
+ if( !v ) return;
+ if( type!=TK_DEFERRED ){
+ for(i=0; i<db->nDb; i++){
+ sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0);
+}
+
+/*
+** Commit a transaction
+*/
+void sqlite3CommitTransaction(Parse *pParse){
+ sqlite3 *db;
+ Vdbe *v;
+
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
+ if( pParse->nErr || sqlite3_malloc_failed ) return;
+ if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ) return;
+
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 0);
+ }
+}
+
+/*
+** Rollback a transaction
+*/
+void sqlite3RollbackTransaction(Parse *pParse){
+ sqlite3 *db;
+ Vdbe *v;
+
+ if( pParse==0 || (db=pParse->db)==0 || db->aDb[0].pBt==0 ) return;
+ if( pParse->nErr || sqlite3_malloc_failed ) return;
+ if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ) return;
+
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ sqlite3VdbeAddOp(v, OP_AutoCommit, 1, 1);
+ }
+}
+
+/*
+** Make sure the TEMP database is open and available for use. Return
+** the number of errors. Leave any error messages in the pParse structure.
+*/
+static int sqlite3OpenTempDatabase(Parse *pParse){
+ sqlite3 *db = pParse->db;
+ if( db->aDb[1].pBt==0 && !pParse->explain ){
+ int rc = sqlite3BtreeFactory(db, 0, 0, MAX_PAGES, &db->aDb[1].pBt);
+ if( rc!=SQLITE_OK ){
+ sqlite3ErrorMsg(pParse, "unable to open a temporary database "
+ "file for storing temporary tables");
+ pParse->rc = rc;
+ return 1;
+ }
+ if( db->flags & !db->autoCommit ){
+ rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
+ if( rc!=SQLITE_OK ){
+ sqlite3ErrorMsg(pParse, "unable to get a write lock on "
+ "the temporary database file");
+ pParse->rc = rc;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Generate VDBE code that will verify the schema cookie and start
+** a read-transaction for all named database files.
+**
+** It is important that all schema cookies be verified and all
+** read transactions be started before anything else happens in
+** the VDBE program. But this routine can be called after much other
+** code has been generated. So here is what we do:
+**
+** The first time this routine is called, we code an OP_Goto that
+** will jump to a subroutine at the end of the program. Then we
+** record every database that needs its schema verified in the
+** pParse->cookieMask field. Later, after all other code has been
+** generated, the subroutine that does the cookie verifications and
+** starts the transactions will be coded and the OP_Goto P2 value
+** will be made to point to that subroutine. The generation of the
+** cookie verification subroutine code happens in sqlite3FinishCoding().
+**
+** If iDb<0 then code the OP_Goto only - don't set flag to verify the
+** schema on any databases. This can be used to position the OP_Goto
+** early in the code, before we know if any database tables will be used.
+*/
+void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
+ sqlite3 *db;
+ Vdbe *v;
+ int mask;
+
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return; /* This only happens if there was a prior error */
+ db = pParse->db;
+ if( pParse->cookieGoto==0 ){
+ pParse->cookieGoto = sqlite3VdbeAddOp(v, OP_Goto, 0, 0)+1;
+ }
+ if( iDb>=0 ){
+ assert( iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 || iDb==1 );
+ assert( iDb<32 );
+ mask = 1<<iDb;
+ if( (pParse->cookieMask & mask)==0 ){
+ pParse->cookieMask |= mask;
+ pParse->cookieValue[iDb] = db->aDb[iDb].schema_cookie;
+ if( iDb==1 ){
+ sqlite3OpenTempDatabase(pParse);
+ }
+ }
+ }
+}
+
+/*
+** Generate VDBE code that prepares for doing an operation that
+** might change the database.
+**
+** This routine starts a new transaction if we are not already within
+** a transaction. If we are already within a transaction, then a checkpoint
+** is set if the setStatement parameter is true. A checkpoint should
+** be set for operations that might fail (due to a constraint) part of
+** the way through and which will need to undo some writes without having to
+** rollback the whole transaction. For operations where all constraints
+** can be checked before any changes are made to the database, it is never
+** necessary to undo a write and the checkpoint should not be set.
+**
+** Only database iDb and the temp database are made writable by this call.
+** If iDb==0, then the main and temp databases are made writable. If
+** iDb==1 then only the temp database is made writable. If iDb>1 then the
+** specified auxiliary database and the temp database are made writable.
+*/
+void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ pParse->writeMask |= 1<<iDb;
+ if( setStatement ){
+ sqlite3VdbeAddOp(v, OP_Statement, iDb, 0);
+ }
+ if( iDb!=1 && pParse->db->aDb[1].pBt!=0 ){
+ sqlite3BeginWriteOperation(pParse, setStatement, 1);
+ }
+}
+
+/*
+** Return the transient sqlite3_value object used for encoding conversions
+** during SQL compilation.
+*/
+sqlite3_value *sqlite3GetTransientValue(sqlite3 *db){
+ if( !db->pValue ){
+ db->pValue = sqlite3ValueNew();
+ }
+ return db->pValue;
+}
diff --git a/kopete/plugins/statistics/sqlite/date.c b/kopete/plugins/statistics/sqlite/date.c
new file mode 100644
index 00000000..634e81d5
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/date.c
@@ -0,0 +1,893 @@
+/*
+** 2003 October 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains the C functions that implement date and time
+** functions for SQLite.
+**
+** There is only one exported symbol in this file - the function
+** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
+** All other code has file scope.
+**
+** $Id$
+**
+** NOTES:
+**
+** SQLite processes all times and dates as Julian Day numbers. The
+** dates and times are stored as the number of days since noon
+** in Greenwich on November 24, 4714 B.C. according to the Gregorian
+** calendar system.
+**
+** 1970-01-01 00:00:00 is JD 2440587.5
+** 2000-01-01 00:00:00 is JD 2451544.5
+**
+** This implemention requires years to be expressed as a 4-digit number
+** which means that only dates between 0000-01-01 and 9999-12-31 can
+** be represented, even though julian day numbers allow a much wider
+** range of dates.
+**
+** The Gregorian calendar system is used for all dates and times,
+** even those that predate the Gregorian calendar. Historians usually
+** use the Julian calendar for dates prior to 1582-10-15 and for some
+** dates afterwards, depending on locale. Beware of this difference.
+**
+** The conversion algorithms are implemented based on descriptions
+** in the following text:
+**
+** Jean Meeus
+** Astronomical Algorithms, 2nd Edition, 1998
+** ISBM 0-943396-61-1
+** Willmann-Bell, Inc
+** Richmond, Virginia (USA)
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
+
+/*
+** A structure for holding a single date and time.
+*/
+typedef struct DateTime DateTime;
+struct DateTime {
+ double rJD; /* The julian day number */
+ int Y, M, D; /* Year, month, and day */
+ int h, m; /* Hour and minutes */
+ int tz; /* Timezone offset in minutes */
+ double s; /* Seconds */
+ char validYMD; /* True if Y,M,D are valid */
+ char validHMS; /* True if h,m,s are valid */
+ char validJD; /* True if rJD is valid */
+ char validTZ; /* True if tz is valid */
+};
+
+
+/*
+** Convert zDate into one or more integers. Additional arguments
+** come in groups of 5 as follows:
+**
+** N number of digits in the integer
+** min minimum allowed value of the integer
+** max maximum allowed value of the integer
+** nextC first character after the integer
+** pVal where to write the integers value.
+**
+** Conversions continue until one with nextC==0 is encountered.
+** The function returns the number of successful conversions.
+*/
+static int getDigits(const char *zDate, ...){
+ va_list ap;
+ int val;
+ int N;
+ int min;
+ int max;
+ int nextC;
+ int *pVal;
+ int cnt = 0;
+ va_start(ap, zDate);
+ do{
+ N = va_arg(ap, int);
+ min = va_arg(ap, int);
+ max = va_arg(ap, int);
+ nextC = va_arg(ap, int);
+ pVal = va_arg(ap, int*);
+ val = 0;
+ while( N-- ){
+ if( !isdigit(*(u8*)zDate) ){
+ return cnt;
+ }
+ val = val*10 + *zDate - '0';
+ zDate++;
+ }
+ if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){
+ return cnt;
+ }
+ *pVal = val;
+ zDate++;
+ cnt++;
+ }while( nextC );
+ return cnt;
+}
+
+/*
+** Read text from z[] and convert into a floating point number. Return
+** the number of digits converted.
+*/
+static int getValue(const char *z, double *pR){
+ const char *zEnd;
+ *pR = sqlite3AtoF(z, &zEnd);
+ return zEnd - z;
+}
+
+/*
+** Parse a timezone extension on the end of a date-time.
+** The extension is of the form:
+**
+** (+/-)HH:MM
+**
+** If the parse is successful, write the number of minutes
+** of change in *pnMin and return 0. If a parser error occurs,
+** return 0.
+**
+** A missing specifier is not considered an error.
+*/
+static int parseTimezone(const char *zDate, DateTime *p){
+ int sgn = 0;
+ int nHr, nMn;
+ while( isspace(*(u8*)zDate) ){ zDate++; }
+ p->tz = 0;
+ if( *zDate=='-' ){
+ sgn = -1;
+ }else if( *zDate=='+' ){
+ sgn = +1;
+ }else{
+ return *zDate!=0;
+ }
+ zDate++;
+ if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
+ return 1;
+ }
+ zDate += 5;
+ p->tz = sgn*(nMn + nHr*60);
+ while( isspace(*(u8*)zDate) ){ zDate++; }
+ return *zDate!=0;
+}
+
+/*
+** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
+** The HH, MM, and SS must each be exactly 2 digits. The
+** fractional seconds FFFF can be one or more digits.
+**
+** Return 1 if there is a parsing error and 0 on success.
+*/
+static int parseHhMmSs(const char *zDate, DateTime *p){
+ int h, m, s;
+ double ms = 0.0;
+ if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){
+ return 1;
+ }
+ zDate += 5;
+ if( *zDate==':' ){
+ zDate++;
+ if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
+ return 1;
+ }
+ zDate += 2;
+ if( *zDate=='.' && isdigit((u8)zDate[1]) ){
+ double rScale = 1.0;
+ zDate++;
+ while( isdigit(*(u8*)zDate) ){
+ ms = ms*10.0 + *zDate - '0';
+ rScale *= 10.0;
+ zDate++;
+ }
+ ms /= rScale;
+ }
+ }else{
+ s = 0;
+ }
+ p->validJD = 0;
+ p->validHMS = 1;
+ p->h = h;
+ p->m = m;
+ p->s = s + ms;
+ if( parseTimezone(zDate, p) ) return 1;
+ p->validTZ = p->tz!=0;
+ return 0;
+}
+
+/*
+** Convert from YYYY-MM-DD HH:MM:SS to julian day. We always assume
+** that the YYYY-MM-DD is according to the Gregorian calendar.
+**
+** Reference: Meeus page 61
+*/
+static void computeJD(DateTime *p){
+ int Y, M, D, A, B, X1, X2;
+
+ if( p->validJD ) return;
+ if( p->validYMD ){
+ Y = p->Y;
+ M = p->M;
+ D = p->D;
+ }else{
+ Y = 2000; /* If no YMD specified, assume 2000-Jan-01 */
+ M = 1;
+ D = 1;
+ }
+ if( M<=2 ){
+ Y--;
+ M += 12;
+ }
+ A = Y/100;
+ B = 2 - A + (A/4);
+ X1 = 365.25*(Y+4716);
+ X2 = 30.6001*(M+1);
+ p->rJD = X1 + X2 + D + B - 1524.5;
+ p->validJD = 1;
+ p->validYMD = 0;
+ if( p->validHMS ){
+ p->rJD += (p->h*3600.0 + p->m*60.0 + p->s)/86400.0;
+ if( p->validTZ ){
+ p->rJD += p->tz*60/86400.0;
+ p->validHMS = 0;
+ p->validTZ = 0;
+ }
+ }
+}
+
+/*
+** Parse dates of the form
+**
+** YYYY-MM-DD HH:MM:SS.FFF
+** YYYY-MM-DD HH:MM:SS
+** YYYY-MM-DD HH:MM
+** YYYY-MM-DD
+**
+** Write the result into the DateTime structure and return 0
+** on success and 1 if the input string is not a well-formed
+** date.
+*/
+static int parseYyyyMmDd(const char *zDate, DateTime *p){
+ int Y, M, D, neg;
+
+ if( zDate[0]=='-' ){
+ zDate++;
+ neg = 1;
+ }else{
+ neg = 0;
+ }
+ if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
+ return 1;
+ }
+ zDate += 10;
+ while( isspace(*(u8*)zDate) ){ zDate++; }
+ if( parseHhMmSs(zDate, p)==0 ){
+ /* We got the time */
+ }else if( *zDate==0 ){
+ p->validHMS = 0;
+ }else{
+ return 1;
+ }
+ p->validJD = 0;
+ p->validYMD = 1;
+ p->Y = neg ? -Y : Y;
+ p->M = M;
+ p->D = D;
+ if( p->validTZ ){
+ computeJD(p);
+ }
+ return 0;
+}
+
+/*
+** Attempt to parse the given string into a Julian Day Number. Return
+** the number of errors.
+**
+** The following are acceptable forms for the input string:
+**
+** YYYY-MM-DD HH:MM:SS.FFF +/-HH:MM
+** DDDD.DD
+** now
+**
+** In the first form, the +/-HH:MM is always optional. The fractional
+** seconds extension (the ".FFF") is optional. The seconds portion
+** (":SS.FFF") is option. The year and date can be omitted as long
+** as there is a time string. The time string can be omitted as long
+** as there is a year and date.
+*/
+static int parseDateOrTime(const char *zDate, DateTime *p){
+ memset(p, 0, sizeof(*p));
+ if( parseYyyyMmDd(zDate,p)==0 ){
+ return 0;
+ }else if( parseHhMmSs(zDate, p)==0 ){
+ return 0;
+ }else if( sqlite3StrICmp(zDate,"now")==0){
+ double r;
+ if( sqlite3OsCurrentTime(&r)==0 ){
+ p->rJD = r;
+ p->validJD = 1;
+ return 0;
+ }
+ return 1;
+ }else if( sqlite3IsNumber(zDate, 0, SQLITE_UTF8) ){
+ p->rJD = sqlite3AtoF(zDate, 0);
+ p->validJD = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Compute the Year, Month, and Day from the julian day number.
+*/
+static void computeYMD(DateTime *p){
+ int Z, A, B, C, D, E, X1;
+ if( p->validYMD ) return;
+ if( !p->validJD ){
+ p->Y = 2000;
+ p->M = 1;
+ p->D = 1;
+ }else{
+ Z = p->rJD + 0.5;
+ A = (Z - 1867216.25)/36524.25;
+ A = Z + 1 + A - (A/4);
+ B = A + 1524;
+ C = (B - 122.1)/365.25;
+ D = 365.25*C;
+ E = (B-D)/30.6001;
+ X1 = 30.6001*E;
+ p->D = B - D - X1;
+ p->M = E<14 ? E-1 : E-13;
+ p->Y = p->M>2 ? C - 4716 : C - 4715;
+ }
+ p->validYMD = 1;
+}
+
+/*
+** Compute the Hour, Minute, and Seconds from the julian day number.
+*/
+static void computeHMS(DateTime *p){
+ int Z, s;
+ if( p->validHMS ) return;
+ Z = p->rJD + 0.5;
+ s = (p->rJD + 0.5 - Z)*86400000.0 + 0.5;
+ p->s = 0.001*s;
+ s = p->s;
+ p->s -= s;
+ p->h = s/3600;
+ s -= p->h*3600;
+ p->m = s/60;
+ p->s += s - p->m*60;
+ p->validHMS = 1;
+}
+
+/*
+** Compute both YMD and HMS
+*/
+static void computeYMD_HMS(DateTime *p){
+ computeYMD(p);
+ computeHMS(p);
+}
+
+/*
+** Clear the YMD and HMS and the TZ
+*/
+static void clearYMD_HMS_TZ(DateTime *p){
+ p->validYMD = 0;
+ p->validHMS = 0;
+ p->validTZ = 0;
+}
+
+/*
+** Compute the difference (in days) between localtime and UTC (a.k.a. GMT)
+** for the time value p where p is in UTC.
+*/
+static double localtimeOffset(DateTime *p){
+ DateTime x, y;
+ time_t t;
+ struct tm *pTm;
+ x = *p;
+ computeYMD_HMS(&x);
+ if( x.Y<1971 || x.Y>=2038 ){
+ x.Y = 2000;
+ x.M = 1;
+ x.D = 1;
+ x.h = 0;
+ x.m = 0;
+ x.s = 0.0;
+ } else {
+ int s = x.s + 0.5;
+ x.s = s;
+ }
+ x.tz = 0;
+ x.validJD = 0;
+ computeJD(&x);
+ t = (x.rJD-2440587.5)*86400.0 + 0.5;
+ sqlite3OsEnterMutex();
+ pTm = localtime(&t);
+ y.Y = pTm->tm_year + 1900;
+ y.M = pTm->tm_mon + 1;
+ y.D = pTm->tm_mday;
+ y.h = pTm->tm_hour;
+ y.m = pTm->tm_min;
+ y.s = pTm->tm_sec;
+ sqlite3OsLeaveMutex();
+ y.validYMD = 1;
+ y.validHMS = 1;
+ y.validJD = 0;
+ y.validTZ = 0;
+ computeJD(&y);
+ return y.rJD - x.rJD;
+}
+
+/*
+** Process a modifier to a date-time stamp. The modifiers are
+** as follows:
+**
+** NNN days
+** NNN hours
+** NNN minutes
+** NNN.NNNN seconds
+** NNN months
+** NNN years
+** start of month
+** start of year
+** start of week
+** start of day
+** weekday N
+** unixepoch
+** localtime
+** utc
+**
+** Return 0 on success and 1 if there is any kind of error.
+*/
+static int parseModifier(const char *zMod, DateTime *p){
+ int rc = 1;
+ int n;
+ double r;
+ char *z, zBuf[30];
+ z = zBuf;
+ for(n=0; n<sizeof(zBuf)-1 && zMod[n]; n++){
+ z[n] = tolower(zMod[n]);
+ }
+ z[n] = 0;
+ switch( z[0] ){
+ case 'l': {
+ /* localtime
+ **
+ ** Assuming the current time value is UTC (a.k.a. GMT), shift it to
+ ** show local time.
+ */
+ if( strcmp(z, "localtime")==0 ){
+ computeJD(p);
+ p->rJD += localtimeOffset(p);
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ }
+ break;
+ }
+ case 'u': {
+ /*
+ ** unixepoch
+ **
+ ** Treat the current value of p->rJD as the number of
+ ** seconds since 1970. Convert to a real julian day number.
+ */
+ if( strcmp(z, "unixepoch")==0 && p->validJD ){
+ p->rJD = p->rJD/86400.0 + 2440587.5;
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ }else if( strcmp(z, "utc")==0 ){
+ double c1;
+ computeJD(p);
+ c1 = localtimeOffset(p);
+ p->rJD -= c1;
+ clearYMD_HMS_TZ(p);
+ p->rJD += c1 - localtimeOffset(p);
+ rc = 0;
+ }
+ break;
+ }
+ case 'w': {
+ /*
+ ** weekday N
+ **
+ ** Move the date to the same time on the next occurrence of
+ ** weekday N where 0==Sunday, 1==Monday, and so forth. If the
+ ** date is already on the appropriate weekday, this is a no-op.
+ */
+ if( strncmp(z, "weekday ", 8)==0 && getValue(&z[8],&r)>0
+ && (n=r)==r && n>=0 && r<7 ){
+ int Z;
+ computeYMD_HMS(p);
+ p->validTZ = 0;
+ p->validJD = 0;
+ computeJD(p);
+ Z = p->rJD + 1.5;
+ Z %= 7;
+ if( Z>n ) Z -= 7;
+ p->rJD += n - Z;
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ }
+ break;
+ }
+ case 's': {
+ /*
+ ** start of TTTTT
+ **
+ ** Move the date backwards to the beginning of the current day,
+ ** or month or year.
+ */
+ if( strncmp(z, "start of ", 9)!=0 ) break;
+ z += 9;
+ computeYMD(p);
+ p->validHMS = 1;
+ p->h = p->m = 0;
+ p->s = 0.0;
+ p->validTZ = 0;
+ p->validJD = 0;
+ if( strcmp(z,"month")==0 ){
+ p->D = 1;
+ rc = 0;
+ }else if( strcmp(z,"year")==0 ){
+ computeYMD(p);
+ p->M = 1;
+ p->D = 1;
+ rc = 0;
+ }else if( strcmp(z,"day")==0 ){
+ rc = 0;
+ }
+ break;
+ }
+ case '+':
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ n = getValue(z, &r);
+ if( n<=0 ) break;
+ if( z[n]==':' ){
+ /* A modifier of the form (+|-)HH:MM:SS.FFF adds (or subtracts) the
+ ** specified number of hours, minutes, seconds, and fractional seconds
+ ** to the time. The ".FFF" may be omitted. The ":SS.FFF" may be
+ ** omitted.
+ */
+ const char *z2 = z;
+ DateTime tx;
+ int day;
+ if( !isdigit(*(u8*)z2) ) z2++;
+ memset(&tx, 0, sizeof(tx));
+ if( parseHhMmSs(z2, &tx) ) break;
+ computeJD(&tx);
+ tx.rJD -= 0.5;
+ day = (int)tx.rJD;
+ tx.rJD -= day;
+ if( z[0]=='-' ) tx.rJD = -tx.rJD;
+ computeJD(p);
+ clearYMD_HMS_TZ(p);
+ p->rJD += tx.rJD;
+ rc = 0;
+ break;
+ }
+ z += n;
+ while( isspace(*(u8*)z) ) z++;
+ n = strlen(z);
+ if( n>10 || n<3 ) break;
+ if( z[n-1]=='s' ){ z[n-1] = 0; n--; }
+ computeJD(p);
+ rc = 0;
+ if( n==3 && strcmp(z,"day")==0 ){
+ p->rJD += r;
+ }else if( n==4 && strcmp(z,"hour")==0 ){
+ p->rJD += r/24.0;
+ }else if( n==6 && strcmp(z,"minute")==0 ){
+ p->rJD += r/(24.0*60.0);
+ }else if( n==6 && strcmp(z,"second")==0 ){
+ p->rJD += r/(24.0*60.0*60.0);
+ }else if( n==5 && strcmp(z,"month")==0 ){
+ int x, y;
+ computeYMD_HMS(p);
+ p->M += r;
+ x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
+ p->Y += x;
+ p->M -= x*12;
+ p->validJD = 0;
+ computeJD(p);
+ y = r;
+ if( y!=r ){
+ p->rJD += (r - y)*30.0;
+ }
+ }else if( n==4 && strcmp(z,"year")==0 ){
+ computeYMD_HMS(p);
+ p->Y += r;
+ p->validJD = 0;
+ computeJD(p);
+ }else{
+ rc = 1;
+ }
+ clearYMD_HMS_TZ(p);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ return rc;
+}
+
+/*
+** Process time function arguments. argv[0] is a date-time stamp.
+** argv[1] and following are modifiers. Parse them all and write
+** the resulting time into the DateTime structure p. Return 0
+** on success and 1 if there are any errors.
+*/
+static int isDate(int argc, sqlite3_value **argv, DateTime *p){
+ int i;
+ if( argc==0 ) return 1;
+ if( SQLITE_NULL==sqlite3_value_type(argv[0]) ||
+ parseDateOrTime(sqlite3_value_text(argv[0]), p) ) return 1;
+ for(i=1; i<argc; i++){
+ if( SQLITE_NULL==sqlite3_value_type(argv[i]) ||
+ parseModifier(sqlite3_value_text(argv[i]), p) ) return 1;
+ }
+ return 0;
+}
+
+
+/*
+** The following routines implement the various date and time functions
+** of SQLite.
+*/
+
+/*
+** julianday( TIMESTRING, MOD, MOD, ...)
+**
+** Return the julian day number of the date specified in the arguments
+*/
+static void juliandayFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(argc, argv, &x)==0 ){
+ computeJD(&x);
+ sqlite3_result_double(context, x.rJD);
+ }
+}
+
+/*
+** datetime( TIMESTRING, MOD, MOD, ...)
+**
+** Return YYYY-MM-DD HH:MM:SS
+*/
+static void datetimeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(argc, argv, &x)==0 ){
+ char zBuf[100];
+ computeYMD_HMS(&x);
+ sprintf(zBuf, "%04d-%02d-%02d %02d:%02d:%02d",x.Y, x.M, x.D, x.h, x.m,
+ (int)(x.s));
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ }
+}
+
+/*
+** time( TIMESTRING, MOD, MOD, ...)
+**
+** Return HH:MM:SS
+*/
+static void timeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(argc, argv, &x)==0 ){
+ char zBuf[100];
+ computeHMS(&x);
+ sprintf(zBuf, "%02d:%02d:%02d", x.h, x.m, (int)x.s);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ }
+}
+
+/*
+** date( TIMESTRING, MOD, MOD, ...)
+**
+** Return YYYY-MM-DD
+*/
+static void dateFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(argc, argv, &x)==0 ){
+ char zBuf[100];
+ computeYMD(&x);
+ sprintf(zBuf, "%04d-%02d-%02d", x.Y, x.M, x.D);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ }
+}
+
+/*
+** strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
+**
+** Return a string described by FORMAT. Conversions as follows:
+**
+** %d day of month
+** %f ** fractional seconds SS.SSS
+** %H hour 00-24
+** %j day of year 000-366
+** %J ** Julian day number
+** %m month 01-12
+** %M minute 00-59
+** %s seconds since 1970-01-01
+** %S seconds 00-59
+** %w day of week 0-6 sunday==0
+** %W week of year 00-53
+** %Y year 0000-9999
+** %% %
+*/
+static void strftimeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ int n, i, j;
+ char *z;
+ const char *zFmt = sqlite3_value_text(argv[0]);
+ char zBuf[100];
+ if( zFmt==0 || isDate(argc-1, argv+1, &x) ) return;
+ for(i=0, n=1; zFmt[i]; i++, n++){
+ if( zFmt[i]=='%' ){
+ switch( zFmt[i+1] ){
+ case 'd':
+ case 'H':
+ case 'm':
+ case 'M':
+ case 'S':
+ case 'W':
+ n++;
+ /* fall thru */
+ case 'w':
+ case '%':
+ break;
+ case 'f':
+ n += 8;
+ break;
+ case 'j':
+ n += 3;
+ break;
+ case 'Y':
+ n += 8;
+ break;
+ case 's':
+ case 'J':
+ n += 50;
+ break;
+ default:
+ return; /* ERROR. return a NULL */
+ }
+ i++;
+ }
+ }
+ if( n<sizeof(zBuf) ){
+ z = zBuf;
+ }else{
+ z = sqliteMalloc( n );
+ if( z==0 ) return;
+ }
+ computeJD(&x);
+ computeYMD_HMS(&x);
+ for(i=j=0; zFmt[i]; i++){
+ if( zFmt[i]!='%' ){
+ z[j++] = zFmt[i];
+ }else{
+ i++;
+ switch( zFmt[i] ){
+ case 'd': sprintf(&z[j],"%02d",x.D); j+=2; break;
+ case 'f': {
+ int s = x.s;
+ int ms = (x.s - s)*1000.0;
+ sprintf(&z[j],"%02d.%03d",s,ms);
+ j += strlen(&z[j]);
+ break;
+ }
+ case 'H': sprintf(&z[j],"%02d",x.h); j+=2; break;
+ case 'W': /* Fall thru */
+ case 'j': {
+ int n; /* Number of days since 1st day of year */
+ DateTime y = x;
+ y.validJD = 0;
+ y.M = 1;
+ y.D = 1;
+ computeJD(&y);
+ n = x.rJD - y.rJD;
+ if( zFmt[i]=='W' ){
+ int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
+ wd = ((int)(x.rJD+0.5)) % 7;
+ sprintf(&z[j],"%02d",(n+7-wd)/7);
+ j += 2;
+ }else{
+ sprintf(&z[j],"%03d",n+1);
+ j += 3;
+ }
+ break;
+ }
+ case 'J': sprintf(&z[j],"%.16g",x.rJD); j+=strlen(&z[j]); break;
+ case 'm': sprintf(&z[j],"%02d",x.M); j+=2; break;
+ case 'M': sprintf(&z[j],"%02d",x.m); j+=2; break;
+ case 's': {
+ sprintf(&z[j],"%d",(int)((x.rJD-2440587.5)*86400.0 + 0.5));
+ j += strlen(&z[j]);
+ break;
+ }
+ case 'S': sprintf(&z[j],"%02d",(int)(x.s+0.5)); j+=2; break;
+ case 'w': z[j++] = (((int)(x.rJD+1.5)) % 7) + '0'; break;
+ case 'Y': sprintf(&z[j],"%04d",x.Y); j+=strlen(&z[j]); break;
+ case '%': z[j++] = '%'; break;
+ }
+ }
+ }
+ z[j] = 0;
+ sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
+ if( z!=zBuf ){
+ sqliteFree(z);
+ }
+}
+
+
+#endif /* !defined(SQLITE_OMIT_DATETIME_FUNCS) */
+
+/*
+** This function registered all of the above C functions as SQL
+** functions. This should be the only routine in this file with
+** external linkage.
+*/
+void sqlite3RegisterDateTimeFunctions(sqlite3 *db){
+#ifndef SQLITE_OMIT_DATETIME_FUNCS
+ static const struct {
+ char *zName;
+ int nArg;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFuncs[] = {
+ { "julianday", -1, juliandayFunc },
+ { "date", -1, dateFunc },
+ { "time", -1, timeFunc },
+ { "datetime", -1, datetimeFunc },
+ { "strftime", -1, strftimeFunc },
+ };
+ int i;
+
+ for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
+ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg,
+ SQLITE_UTF8, 0, aFuncs[i].xFunc, 0, 0);
+ }
+#endif
+}
diff --git a/kopete/plugins/statistics/sqlite/delete.c b/kopete/plugins/statistics/sqlite/delete.c
new file mode 100644
index 00000000..866da61d
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/delete.c
@@ -0,0 +1,419 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle DELETE FROM statements.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** Look up every table that is named in pSrc. If any table is not found,
+** add an error message to pParse->zErrMsg and return NULL. If all tables
+** are found, return a pointer to the last table.
+*/
+Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
+ Table *pTab = 0;
+ int i;
+ struct SrcList_item *pItem;
+ for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
+ pTab = sqlite3LocateTable(pParse, pItem->zName, pItem->zDatabase);
+ pItem->pTab = pTab;
+ }
+ return pTab;
+}
+
+/*
+** Check to make sure the given table is writable. If it is not
+** writable, generate an error message and return 1. If it is
+** writable return 0;
+*/
+int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){
+ if( pTab->readOnly ){
+ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName);
+ return 1;
+ }
+ if( !viewOk && pTab->pSelect ){
+ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Generate code that will open a table for reading.
+*/
+void sqlite3OpenTableForReading(
+ Vdbe *v, /* Generate code into this VDBE */
+ int iCur, /* The cursor number of the table */
+ Table *pTab /* The table to be opened */
+){
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenRead, iCur, pTab->tnum);
+ VdbeComment((v, "# %s", pTab->zName));
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
+}
+
+
+/*
+** Process a DELETE FROM statement.
+*/
+void sqlite3DeleteFrom(
+ Parse *pParse, /* The parser context */
+ SrcList *pTabList, /* The table from which we should delete things */
+ Expr *pWhere /* The WHERE clause. May be null */
+){
+ Vdbe *v; /* The virtual database engine */
+ Table *pTab; /* The table from which records will be deleted */
+ const char *zDb; /* Name of database holding pTab */
+ int end, addr = 0; /* A couple addresses of generated code */
+ int i; /* Loop counter */
+ WhereInfo *pWInfo; /* Information about the WHERE clause */
+ Index *pIdx; /* For looping over indices of the table */
+ int iCur; /* VDBE Cursor number for pTab */
+ sqlite3 *db; /* Main database structure */
+ int isView; /* True if attempting to delete from a view */
+ AuthContext sContext; /* Authorization context */
+
+ int row_triggers_exist = 0; /* True if any triggers exist */
+ int before_triggers; /* True if there are BEFORE triggers */
+ int after_triggers; /* True if there are AFTER triggers */
+ int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */
+
+ sContext.pParse = 0;
+ if( pParse->nErr || sqlite3_malloc_failed ){
+ pTabList = 0;
+ goto delete_from_cleanup;
+ }
+ db = pParse->db;
+ assert( pTabList->nSrc==1 );
+
+ /* Locate the table which we want to delete. This table has to be
+ ** put in an SrcList structure because some of the subroutines we
+ ** will be calling are designed to work with multiple tables and expect
+ ** an SrcList* parameter instead of just a Table* parameter.
+ */
+ pTab = sqlite3SrcListLookup(pParse, pTabList);
+ if( pTab==0 ) goto delete_from_cleanup;
+ before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
+ TK_DELETE, TK_BEFORE, TK_ROW, 0);
+ after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
+ TK_DELETE, TK_AFTER, TK_ROW, 0);
+ row_triggers_exist = before_triggers || after_triggers;
+ isView = pTab->pSelect!=0;
+ if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){
+ goto delete_from_cleanup;
+ }
+ assert( pTab->iDb<db->nDb );
+ zDb = db->aDb[pTab->iDb].zName;
+ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){
+ goto delete_from_cleanup;
+ }
+
+ /* If pTab is really a view, make sure it has been initialized.
+ */
+ if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){
+ goto delete_from_cleanup;
+ }
+
+ /* Allocate a cursor used to store the old.* data for a trigger.
+ */
+ if( row_triggers_exist ){
+ oldIdx = pParse->nTab++;
+ }
+
+ /* Resolve the column names in all the expressions.
+ */
+ assert( pTabList->nSrc==1 );
+ iCur = pTabList->a[0].iCursor = pParse->nTab++;
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){
+ goto delete_from_cleanup;
+ }
+
+ /* Start the view context
+ */
+ if( isView ){
+ sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
+ }
+
+ /* Begin generating code.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ){
+ goto delete_from_cleanup;
+ }
+ sqlite3VdbeCountChanges(v);
+ sqlite3BeginWriteOperation(pParse, row_triggers_exist, pTab->iDb);
+
+ /* If we are trying to delete from a view, construct that view into
+ ** a temporary table.
+ */
+ if( isView ){
+ Select *pView = sqlite3SelectDup(pTab->pSelect);
+ sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0);
+ sqlite3SelectDelete(pView);
+ }
+
+ /* Initialize the counter of the number of rows deleted, if
+ ** we are counting rows.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ }
+
+ /* Special case: A DELETE without a WHERE clause deletes everything.
+ ** It is easier just to erase the whole table. Note, however, that
+ ** this means that the row change count will be incorrect.
+ */
+ if( pWhere==0 && !row_triggers_exist ){
+ if( db->flags & SQLITE_CountRows ){
+ /* If counting rows deleted, just count the total number of
+ ** entries in the table. */
+ int endOfLoop = sqlite3VdbeMakeLabel(v);
+ int addr;
+ if( !isView ){
+ sqlite3OpenTableForReading(v, iCur, pTab);
+ }
+ sqlite3VdbeAddOp(v, OP_Rewind, iCur, sqlite3VdbeCurrentAddr(v)+2);
+ addr = sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Next, iCur, addr);
+ sqlite3VdbeResolveLabel(v, endOfLoop);
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Clear, pTab->tnum, pTab->iDb);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Clear, pIdx->tnum, pIdx->iDb);
+ }
+ }
+ }
+
+ /* The usual case: There is a WHERE clause so we have to scan through
+ ** the table and pick which records to delete.
+ */
+ else{
+ /* Ensure all required collation sequences are available. */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){
+ goto delete_from_cleanup;
+ }
+ }
+
+ /* Begin the database scan
+ */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
+ if( pWInfo==0 ) goto delete_from_cleanup;
+
+ /* Remember the key of every item to be deleted.
+ */
+ sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0);
+ if( db->flags & SQLITE_CountRows ){
+ sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+
+ /* Open the pseudo-table used to store OLD if there are triggers.
+ */
+ if( row_triggers_exist ){
+ sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
+ }
+
+ /* Delete every item whose key was written to the list during the
+ ** database scan. We have to delete items after the scan is complete
+ ** because deleting an item can change the scan order.
+ */
+ sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
+ end = sqlite3VdbeMakeLabel(v);
+
+ /* This is the beginning of the delete loop when there are
+ ** row triggers.
+ */
+ if( row_triggers_exist ){
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ if( !isView ){
+ sqlite3OpenTableForReading(v, iCur, pTab);
+ }
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+
+ sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_BEFORE, pTab, -1,
+ oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
+ addr);
+ }
+
+ if( !isView ){
+ /* Open cursors for the table we are deleting from and all its
+ ** indices. If there are row triggers, this happens inside the
+ ** OP_ListRead loop because the cursor have to all be closed
+ ** before the trigger fires. If there are no row triggers, the
+ ** cursors are opened only once on the outside the loop.
+ */
+ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+
+ /* This is the beginning of the delete loop when there are no
+ ** row triggers */
+ if( !row_triggers_exist ){
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, end);
+ }
+
+ /* Delete the row */
+ sqlite3GenerateRowDelete(db, v, pTab, iCur, 1);
+ }
+
+ /* If there are row triggers, close all cursors then invoke
+ ** the AFTER triggers
+ */
+ if( row_triggers_exist ){
+ if( !isView ){
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+ sqlite3CodeRowTrigger(pParse, TK_DELETE, 0, TK_AFTER, pTab, -1,
+ oldIdx, (pParse->trigStack)?pParse->trigStack->orconf:OE_Default,
+ addr);
+ }
+
+ /* End of the delete loop */
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, end);
+ sqlite3VdbeAddOp(v, OP_ListReset, 0, 0);
+
+ /* Close the cursors after the loop if there are no row triggers */
+ if( !row_triggers_exist ){
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp(v, OP_Close, iCur + i, pIdx->tnum);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+ }
+
+ /*
+ ** Return the number of rows that were deleted.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "rows deleted", P3_STATIC);
+ }
+
+delete_from_cleanup:
+ sqlite3AuthContextPop(&sContext);
+ sqlite3SrcListDelete(pTabList);
+ sqlite3ExprDelete(pWhere);
+ return;
+}
+
+/*
+** This routine generates VDBE code that causes a single row of a
+** single table to be deleted.
+**
+** The VDBE must be in a particular state when this routine is called.
+** These are the requirements:
+**
+** 1. A read/write cursor pointing to pTab, the table containing the row
+** to be deleted, must be opened as cursor number "base".
+**
+** 2. Read/write cursors for all indices of pTab must be open as
+** cursor number base+i for the i-th index.
+**
+** 3. The record number of the row to be deleted must be on the top
+** of the stack.
+**
+** This routine pops the top of the stack to remove the record number
+** and then generates code to remove both the table record and all index
+** entries that point to that record.
+*/
+void sqlite3GenerateRowDelete(
+ sqlite3 *db, /* The database containing the index */
+ Vdbe *v, /* Generate code into this VDBE */
+ Table *pTab, /* Table containing the row to be deleted */
+ int iCur, /* Cursor number for the table */
+ int count /* Increment the row change counter */
+){
+ int addr;
+ addr = sqlite3VdbeAddOp(v, OP_NotExists, iCur, 0);
+ sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
+ sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v));
+}
+
+/*
+** This routine generates VDBE code that causes the deletion of all
+** index entries associated with a single row of a single table.
+**
+** The VDBE must be in a particular state when this routine is called.
+** These are the requirements:
+**
+** 1. A read/write cursor pointing to pTab, the table containing the row
+** to be deleted, must be opened as cursor number "iCur".
+**
+** 2. Read/write cursors for all indices of pTab must be open as
+** cursor number iCur+i for the i-th index.
+**
+** 3. The "iCur" cursor must be pointing to the row that is to be
+** deleted.
+*/
+void sqlite3GenerateRowIndexDelete(
+ sqlite3 *db, /* The database containing the index */
+ Vdbe *v, /* Generate code into this VDBE */
+ Table *pTab, /* Table containing the row to be deleted */
+ int iCur, /* Cursor number for the table */
+ char *aIdxUsed /* Only delete if aIdxUsed!=0 && aIdxUsed[i]!=0 */
+){
+ int i;
+ Index *pIdx;
+
+ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
+ sqlite3GenerateIndexKey(v, pIdx, iCur);
+ sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
+ }
+}
+
+/*
+** Generate code that will assemble an index key and put it on the top
+** of the tack. The key with be for index pIdx which is an index on pTab.
+** iCur is the index of a cursor open on the pTab table and pointing to
+** the entry that needs indexing.
+*/
+void sqlite3GenerateIndexKey(
+ Vdbe *v, /* Generate code into this VDBE */
+ Index *pIdx, /* The index for which to generate a key */
+ int iCur /* Cursor number for the pIdx->pTable table */
+){
+ int j;
+ Table *pTab = pIdx->pTable;
+
+ sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
+ for(j=0; j<pIdx->nColumn; j++){
+ int idx = pIdx->aiColumn[j];
+ if( idx==pTab->iPKey ){
+ sqlite3VdbeAddOp(v, OP_Dup, j, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Column, iCur, idx);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pIdx->nColumn, (1<<24));
+ sqlite3IndexAffinityStr(v, pIdx);
+}
diff --git a/kopete/plugins/statistics/sqlite/encode.c b/kopete/plugins/statistics/sqlite/encode.c
new file mode 100644
index 00000000..b10c96b3
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/encode.c
@@ -0,0 +1,257 @@
+/*
+** 2002 April 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains helper routines used to translate binary data into
+** a null-terminated string (suitable for use in SQLite) and back again.
+** These are convenience routines for use by people who want to store binary
+** data in an SQLite database. The code in this file is not used by any other
+** part of the SQLite library.
+**
+** $Id$
+*/
+#include <string.h>
+#include <assert.h>
+
+/*
+** How This Encoder Works
+**
+** The output is allowed to contain any character except 0x27 (') and
+** 0x00. This is accomplished by using an escape character to encode
+** 0x27 and 0x00 as a two-byte sequence. The escape character is always
+** 0x01. An 0x00 is encoded as the two byte sequence 0x01 0x01. The
+** 0x27 character is encoded as the two byte sequence 0x01 0x28. Finally,
+** the escape character itself is encoded as the two-character sequence
+** 0x01 0x02.
+**
+** To summarize, the encoder works by using an escape sequences as follows:
+**
+** 0x00 -> 0x01 0x01
+** 0x01 -> 0x01 0x02
+** 0x27 -> 0x01 0x28
+**
+** If that were all the encoder did, it would work, but in certain cases
+** it could double the size of the encoded string. For example, to
+** encode a string of 100 0x27 characters would require 100 instances of
+** the 0x01 0x03 escape sequence resulting in a 200-character output.
+** We would prefer to keep the size of the encoded string smaller than
+** this.
+**
+** To minimize the encoding size, we first add a fixed offset value to each
+** byte in the sequence. The addition is modulo 256. (That is to say, if
+** the sum of the original character value and the offset exceeds 256, then
+** the higher order bits are truncated.) The offset is chosen to minimize
+** the number of characters in the string that need to be escaped. For
+** example, in the case above where the string was composed of 100 0x27
+** characters, the offset might be 0x01. Each of the 0x27 characters would
+** then be converted into an 0x28 character which would not need to be
+** escaped at all and so the 100 character input string would be converted
+** into just 100 characters of output. Actually 101 characters of output -
+** we have to record the offset used as the first byte in the sequence so
+** that the string can be decoded. Since the offset value is stored as
+** part of the output string and the output string is not allowed to contain
+** characters 0x00 or 0x27, the offset cannot be 0x00 or 0x27.
+**
+** Here, then, are the encoding steps:
+**
+** (1) Choose an offset value and make it the first character of
+** output.
+**
+** (2) Copy each input character into the output buffer, one by
+** one, adding the offset value as you copy.
+**
+** (3) If the value of an input character plus offset is 0x00, replace
+** that one character by the two-character sequence 0x01 0x01.
+** If the sum is 0x01, replace it with 0x01 0x02. If the sum
+** is 0x27, replace it with 0x01 0x03.
+**
+** (4) Put a 0x00 terminator at the end of the output.
+**
+** Decoding is obvious:
+**
+** (5) Copy encoded characters except the first into the decode
+** buffer. Set the first encoded character aside for use as
+** the offset in step 7 below.
+**
+** (6) Convert each 0x01 0x01 sequence into a single character 0x00.
+** Convert 0x01 0x02 into 0x01. Convert 0x01 0x28 into 0x27.
+**
+** (7) Subtract the offset value that was the first character of
+** the encoded buffer from all characters in the output buffer.
+**
+** The only tricky part is step (1) - how to compute an offset value to
+** minimize the size of the output buffer. This is accomplished by testing
+** all offset values and picking the one that results in the fewest number
+** of escapes. To do that, we first scan the entire input and count the
+** number of occurances of each character value in the input. Suppose
+** the number of 0x00 characters is N(0), the number of occurances of 0x01
+** is N(1), and so forth up to the number of occurances of 0xff is N(255).
+** An offset of 0 is not allowed so we don't have to test it. The number
+** of escapes required for an offset of 1 is N(1)+N(2)+N(40). The number
+** of escapes required for an offset of 2 is N(2)+N(3)+N(41). And so forth.
+** In this way we find the offset that gives the minimum number of escapes,
+** and thus minimizes the length of the output string.
+*/
+
+/*
+** Encode a binary buffer "in" of size n bytes so that it contains
+** no instances of characters '\'' or '\000'. The output is
+** null-terminated and can be used as a string value in an INSERT
+** or UPDATE statement. Use sqlite_decode_binary() to convert the
+** string back into its original binary.
+**
+** The result is written into a preallocated output buffer "out".
+** "out" must be able to hold at least 2 +(257*n)/254 bytes.
+** In other words, the output will be expanded by as much as 3
+** bytes for every 254 bytes of input plus 2 bytes of fixed overhead.
+** (This is approximately 2 + 1.0118*n or about a 1.2% size increase.)
+**
+** The return value is the number of characters in the encoded
+** string, excluding the "\000" terminator.
+**
+** If out==NULL then no output is generated but the routine still returns
+** the number of characters that would have been generated if out had
+** not been NULL.
+*/
+int sqlite_encode_binary(const unsigned char *in, int n, unsigned char *out){
+ int i, j, e, m;
+ unsigned char x;
+ int cnt[256];
+ if( n<=0 ){
+ if( out ){
+ out[0] = 'x';
+ out[1] = 0;
+ }
+ return 1;
+ }
+ memset(cnt, 0, sizeof(cnt));
+ for(i=n-1; i>=0; i--){ cnt[in[i]]++; }
+ m = n;
+ for(i=1; i<256; i++){
+ int sum;
+ if( i=='\'' ) continue;
+ sum = cnt[i] + cnt[(i+1)&0xff] + cnt[(i+'\'')&0xff];
+ if( sum<m ){
+ m = sum;
+ e = i;
+ if( m==0 ) break;
+ }
+ }
+ if( out==0 ){
+ return n+m+1;
+ }
+ out[0] = e;
+ j = 1;
+ for(i=0; i<n; i++){
+ x = in[i] - e;
+ if( x==0 || x==1 || x=='\''){
+ out[j++] = 1;
+ x++;
+ }
+ out[j++] = x;
+ }
+ out[j] = 0;
+ assert( j==n+m+1 );
+ return j;
+}
+
+/*
+** Decode the string "in" into binary data and write it into "out".
+** This routine reverses the encoding created by sqlite_encode_binary().
+** The output will always be a few bytes less than the input. The number
+** of bytes of output is returned. If the input is not a well-formed
+** encoding, -1 is returned.
+**
+** The "in" and "out" parameters may point to the same buffer in order
+** to decode a string in place.
+*/
+int sqlite_decode_binary(const unsigned char *in, unsigned char *out){
+ int i, e;
+ unsigned char c;
+ e = *(in++);
+ i = 0;
+ while( (c = *(in++))!=0 ){
+ if( c==1 ){
+ c = *(in++) - 1;
+ }
+ out[i++] = c + e;
+ }
+ return i;
+}
+
+#ifdef ENCODER_TEST
+#include <stdio.h>
+/*
+** The subroutines above are not tested by the usual test suite. To test
+** these routines, compile just this one file with a -DENCODER_TEST=1 option
+** and run the result.
+*/
+int main(int argc, char **argv){
+ int i, j, n, m, nOut, nByteIn, nByteOut;
+ unsigned char in[30000];
+ unsigned char out[33000];
+
+ nByteIn = nByteOut = 0;
+ for(i=0; i<sizeof(in); i++){
+ printf("Test %d: ", i+1);
+ n = rand() % (i+1);
+ if( i%100==0 ){
+ int k;
+ for(j=k=0; j<n; j++){
+ /* if( k==0 || k=='\'' ) k++; */
+ in[j] = k;
+ k = (k+1)&0xff;
+ }
+ }else{
+ for(j=0; j<n; j++) in[j] = rand() & 0xff;
+ }
+ nByteIn += n;
+ nOut = sqlite_encode_binary(in, n, out);
+ nByteOut += nOut;
+ if( nOut!=strlen(out) ){
+ printf(" ERROR return value is %d instead of %d\n", nOut, strlen(out));
+ exit(1);
+ }
+ if( nOut!=sqlite_encode_binary(in, n, 0) ){
+ printf(" ERROR actual output size disagrees with predicted size\n");
+ exit(1);
+ }
+ m = (256*n + 1262)/253;
+ printf("size %d->%d (max %d)", n, strlen(out)+1, m);
+ if( strlen(out)+1>m ){
+ printf(" ERROR output too big\n");
+ exit(1);
+ }
+ for(j=0; out[j]; j++){
+ if( out[j]=='\'' ){
+ printf(" ERROR contains (')\n");
+ exit(1);
+ }
+ }
+ j = sqlite_decode_binary(out, out);
+ if( j!=n ){
+ printf(" ERROR decode size %d\n", j);
+ exit(1);
+ }
+ if( memcmp(in, out, n)!=0 ){
+ printf(" ERROR decode mismatch\n");
+ exit(1);
+ }
+ printf(" OK\n");
+ }
+ fprintf(stderr,"Finished. Total encoding: %d->%d bytes\n",
+ nByteIn, nByteOut);
+ fprintf(stderr,"Avg size increase: %.3f%%\n",
+ (nByteOut-nByteIn)*100.0/(double)nByteIn);
+}
+#endif /* ENCODER_TEST */
+
+
+
diff --git a/kopete/plugins/statistics/sqlite/expr.c b/kopete/plugins/statistics/sqlite/expr.c
new file mode 100644
index 00000000..2da3645b
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/expr.c
@@ -0,0 +1,1927 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains routines used for analyzing expressions and
+** for generating VDBE code that evaluates expressions in SQLite.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include <ctype.h>
+
+/*
+** Return the 'affinity' of the expression pExpr if any.
+**
+** If pExpr is a column, a reference to a column via an 'AS' alias,
+** or a sub-select with a column as the return value, then the
+** affinity of that column is returned. Otherwise, 0x00 is returned,
+** indicating no affinity for the expression.
+**
+** i.e. the WHERE clause expresssions in the following statements all
+** have an affinity:
+**
+** CREATE TABLE t1(a);
+** SELECT * FROM t1 WHERE a;
+** SELECT a AS b FROM t1 WHERE b;
+** SELECT * FROM t1 WHERE (select a from t1);
+*/
+char sqlite3ExprAffinity(Expr *pExpr){
+ if( pExpr->op==TK_AS ){
+ return sqlite3ExprAffinity(pExpr->pLeft);
+ }
+ if( pExpr->op==TK_SELECT ){
+ return sqlite3ExprAffinity(pExpr->pSelect->pEList->a[0].pExpr);
+ }
+ return pExpr->affinity;
+}
+
+/*
+** Return the default collation sequence for the expression pExpr. If
+** there is no default collation type, return 0.
+*/
+CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
+ CollSeq *pColl = 0;
+ if( pExpr ){
+ pColl = pExpr->pColl;
+ if( pExpr->op==TK_AS && !pColl ){
+ return sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ }
+ }
+ if( sqlite3CheckCollSeq(pParse, pColl) ){
+ pColl = 0;
+ }
+ return pColl;
+}
+
+/*
+** pExpr is the left operand of a comparison operator. aff2 is the
+** type affinity of the right operand. This routine returns the
+** type affinity that should be used for the comparison operator.
+*/
+char sqlite3CompareAffinity(Expr *pExpr, char aff2){
+ char aff1 = sqlite3ExprAffinity(pExpr);
+ if( aff1 && aff2 ){
+ /* Both sides of the comparison are columns. If one has numeric or
+ ** integer affinity, use that. Otherwise use no affinity.
+ */
+ if( aff1==SQLITE_AFF_INTEGER || aff2==SQLITE_AFF_INTEGER ){
+ return SQLITE_AFF_INTEGER;
+ }else if( aff1==SQLITE_AFF_NUMERIC || aff2==SQLITE_AFF_NUMERIC ){
+ return SQLITE_AFF_NUMERIC;
+ }else{
+ return SQLITE_AFF_NONE;
+ }
+ }else if( !aff1 && !aff2 ){
+ /* Neither side of the comparison is a column. Compare the
+ ** results directly.
+ */
+ /* return SQLITE_AFF_NUMERIC; // Ticket #805 */
+ return SQLITE_AFF_NONE;
+ }else{
+ /* One side is a column, the other is not. Use the columns affinity. */
+ return (aff1 + aff2);
+ }
+}
+
+/*
+** pExpr is a comparison operator. Return the type affinity that should
+** be applied to both operands prior to doing the comparison.
+*/
+static char comparisonAffinity(Expr *pExpr){
+ char aff;
+ assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT ||
+ pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE ||
+ pExpr->op==TK_NE );
+ assert( pExpr->pLeft );
+ aff = sqlite3ExprAffinity(pExpr->pLeft);
+ if( pExpr->pRight ){
+ aff = sqlite3CompareAffinity(pExpr->pRight, aff);
+ }
+ else if( pExpr->pSelect ){
+ aff = sqlite3CompareAffinity(pExpr->pSelect->pEList->a[0].pExpr, aff);
+ }
+ else if( !aff ){
+ aff = SQLITE_AFF_NUMERIC;
+ }
+ return aff;
+}
+
+/*
+** pExpr is a comparison expression, eg. '=', '<', IN(...) etc.
+** idx_affinity is the affinity of an indexed column. Return true
+** if the index with affinity idx_affinity may be used to implement
+** the comparison in pExpr.
+*/
+int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
+ char aff = comparisonAffinity(pExpr);
+ return
+ (aff==SQLITE_AFF_NONE) ||
+ (aff==SQLITE_AFF_NUMERIC && idx_affinity==SQLITE_AFF_INTEGER) ||
+ (aff==SQLITE_AFF_INTEGER && idx_affinity==SQLITE_AFF_NUMERIC) ||
+ (aff==idx_affinity);
+}
+
+/*
+** Return the P1 value that should be used for a binary comparison
+** opcode (OP_Eq, OP_Ge etc.) used to compare pExpr1 and pExpr2.
+** If jumpIfNull is true, then set the low byte of the returned
+** P1 value to tell the opcode to jump if either expression
+** evaluates to NULL.
+*/
+static int binaryCompareP1(Expr *pExpr1, Expr *pExpr2, int jumpIfNull){
+ char aff = sqlite3ExprAffinity(pExpr2);
+ return (((int)sqlite3CompareAffinity(pExpr1, aff))<<8)+(jumpIfNull?1:0);
+}
+
+/*
+** Return a pointer to the collation sequence that should be used by
+** a binary comparison operator comparing pLeft and pRight.
+**
+** If the left hand expression has a collating sequence type, then it is
+** used. Otherwise the collation sequence for the right hand expression
+** is used, or the default (BINARY) if neither expression has a collating
+** type.
+*/
+static CollSeq* binaryCompareCollSeq(Parse *pParse, Expr *pLeft, Expr *pRight){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pLeft);
+ if( !pColl ){
+ pColl = sqlite3ExprCollSeq(pParse, pRight);
+ }
+ return pColl;
+}
+
+/*
+** Generate code for a comparison operator.
+*/
+static int codeCompare(
+ Parse *pParse, /* The parsing (and code generating) context */
+ Expr *pLeft, /* The left operand */
+ Expr *pRight, /* The right operand */
+ int opcode, /* The comparison opcode */
+ int dest, /* Jump here if true. */
+ int jumpIfNull /* If true, jump if either operand is NULL */
+){
+ int p1 = binaryCompareP1(pLeft, pRight, jumpIfNull);
+ CollSeq *p3 = binaryCompareCollSeq(pParse, pLeft, pRight);
+ return sqlite3VdbeOp3(pParse->pVdbe, opcode, p1, dest, (void*)p3, P3_COLLSEQ);
+}
+
+/*
+** Construct a new expression node and return a pointer to it. Memory
+** for this node is obtained from sqliteMalloc(). The calling function
+** is responsible for making sure the node eventually gets freed.
+*/
+Expr *sqlite3Expr(int op, Expr *pLeft, Expr *pRight, Token *pToken){
+ Expr *pNew;
+ pNew = sqliteMalloc( sizeof(Expr) );
+ if( pNew==0 ){
+ /* When malloc fails, we leak memory from pLeft and pRight */
+ return 0;
+ }
+ pNew->op = op;
+ pNew->pLeft = pLeft;
+ pNew->pRight = pRight;
+ if( pToken ){
+ assert( pToken->dyn==0 );
+ pNew->span = pNew->token = *pToken;
+ }else if( pLeft && pRight ){
+ sqlite3ExprSpan(pNew, &pLeft->span, &pRight->span);
+ }
+ return pNew;
+}
+
+/*
+** Join two expressions using an AND operator. If either expression is
+** NULL, then just return the other expression.
+*/
+Expr *sqlite3ExprAnd(Expr *pLeft, Expr *pRight){
+ if( pLeft==0 ){
+ return pRight;
+ }else if( pRight==0 ){
+ return pLeft;
+ }else{
+ return sqlite3Expr(TK_AND, pLeft, pRight, 0);
+ }
+}
+
+/*
+** Set the Expr.span field of the given expression to span all
+** text between the two given tokens.
+*/
+void sqlite3ExprSpan(Expr *pExpr, Token *pLeft, Token *pRight){
+ assert( pRight!=0 );
+ assert( pLeft!=0 );
+ if( !sqlite3_malloc_failed && pRight->z && pLeft->z ){
+ assert( pLeft->dyn==0 || pLeft->z[pLeft->n]==0 );
+ if( pLeft->dyn==0 && pRight->dyn==0 ){
+ pExpr->span.z = pLeft->z;
+ pExpr->span.n = pRight->n + Addr(pRight->z) - Addr(pLeft->z);
+ }else{
+ pExpr->span.z = 0;
+ }
+ }
+}
+
+/*
+** Construct a new expression node for a function with multiple
+** arguments.
+*/
+Expr *sqlite3ExprFunction(ExprList *pList, Token *pToken){
+ Expr *pNew;
+ pNew = sqliteMalloc( sizeof(Expr) );
+ if( pNew==0 ){
+ /* sqlite3ExprListDelete(pList); // Leak pList when malloc fails */
+ return 0;
+ }
+ pNew->op = TK_FUNCTION;
+ pNew->pList = pList;
+ if( pToken ){
+ assert( pToken->dyn==0 );
+ pNew->token = *pToken;
+ }else{
+ pNew->token.z = 0;
+ }
+ pNew->span = pNew->token;
+ return pNew;
+}
+
+/*
+** Assign a variable number to an expression that encodes a wildcard
+** in the original SQL statement.
+**
+** Wildcards consisting of a single "?" are assigned the next sequential
+** variable number.
+**
+** Wildcards of the form "?nnn" are assigned the number "nnn". We make
+** sure "nnn" is not too be to avoid a denial of service attack when
+** the SQL statement comes from an external source.
+**
+** Wildcards of the form ":aaa" or "$aaa" are assigned the same number
+** as the previous instance of the same wildcard. Or if this is the first
+** instance of the wildcard, the next sequenial variable number is
+** assigned.
+*/
+void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
+ Token *pToken;
+ if( pExpr==0 ) return;
+ pToken = &pExpr->token;
+ assert( pToken->n>=1 );
+ assert( pToken->z!=0 );
+ assert( pToken->z[0]!=0 );
+ if( pToken->n==1 ){
+ /* Wildcard of the form "?". Assign the next variable number */
+ pExpr->iTable = ++pParse->nVar;
+ }else if( pToken->z[0]=='?' ){
+ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and
+ ** use it as the variable number */
+ int i;
+ pExpr->iTable = i = atoi(&pToken->z[1]);
+ if( i<1 || i>SQLITE_MAX_VARIABLE_NUMBER ){
+ sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
+ SQLITE_MAX_VARIABLE_NUMBER);
+ }
+ if( i>pParse->nVar ){
+ pParse->nVar = i;
+ }
+ }else{
+ /* Wildcards of the form ":aaa" or "$aaa". Reuse the same variable
+ ** number as the prior appearance of the same name, or if the name
+ ** has never appeared before, reuse the same variable number
+ */
+ int i, n;
+ n = pToken->n;
+ for(i=0; i<pParse->nVarExpr; i++){
+ Expr *pE;
+ if( (pE = pParse->apVarExpr[i])!=0
+ && pE->token.n==n
+ && memcmp(pE->token.z, pToken->z, n)==0 ){
+ pExpr->iTable = pE->iTable;
+ break;
+ }
+ }
+ if( i>=pParse->nVarExpr ){
+ pExpr->iTable = ++pParse->nVar;
+ if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){
+ pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10;
+ pParse->apVarExpr = sqliteRealloc(pParse->apVarExpr,
+ pParse->nVarExprAlloc*sizeof(pParse->apVarExpr[0]) );
+ }
+ if( !sqlite3_malloc_failed ){
+ assert( pParse->apVarExpr!=0 );
+ pParse->apVarExpr[pParse->nVarExpr++] = pExpr;
+ }
+ }
+ }
+}
+
+/*
+** Recursively delete an expression tree.
+*/
+void sqlite3ExprDelete(Expr *p){
+ if( p==0 ) return;
+ if( p->span.dyn ) sqliteFree((char*)p->span.z);
+ if( p->token.dyn ) sqliteFree((char*)p->token.z);
+ sqlite3ExprDelete(p->pLeft);
+ sqlite3ExprDelete(p->pRight);
+ sqlite3ExprListDelete(p->pList);
+ sqlite3SelectDelete(p->pSelect);
+ sqliteFree(p);
+}
+
+
+/*
+** The following group of routines make deep copies of expressions,
+** expression lists, ID lists, and select statements. The copies can
+** be deleted (by being passed to their respective ...Delete() routines)
+** without effecting the originals.
+**
+** The expression list, ID, and source lists return by sqlite3ExprListDup(),
+** sqlite3IdListDup(), and sqlite3SrcListDup() can not be further expanded
+** by subsequent calls to sqlite*ListAppend() routines.
+**
+** Any tables that the SrcList might point to are not duplicated.
+*/
+Expr *sqlite3ExprDup(Expr *p){
+ Expr *pNew;
+ if( p==0 ) return 0;
+ pNew = sqliteMallocRaw( sizeof(*p) );
+ if( pNew==0 ) return 0;
+ memcpy(pNew, p, sizeof(*pNew));
+ if( p->token.z!=0 ){
+ pNew->token.z = sqliteStrDup(p->token.z);
+ pNew->token.dyn = 1;
+ }else{
+ assert( pNew->token.z==0 );
+ }
+ pNew->span.z = 0;
+ pNew->pLeft = sqlite3ExprDup(p->pLeft);
+ pNew->pRight = sqlite3ExprDup(p->pRight);
+ pNew->pList = sqlite3ExprListDup(p->pList);
+ pNew->pSelect = sqlite3SelectDup(p->pSelect);
+ return pNew;
+}
+void sqlite3TokenCopy(Token *pTo, Token *pFrom){
+ if( pTo->dyn ) sqliteFree((char*)pTo->z);
+ if( pFrom->z ){
+ pTo->n = pFrom->n;
+ pTo->z = sqliteStrNDup(pFrom->z, pFrom->n);
+ pTo->dyn = 1;
+ }else{
+ pTo->z = 0;
+ }
+}
+ExprList *sqlite3ExprListDup(ExprList *p){
+ ExprList *pNew;
+ struct ExprList_item *pItem, *pOldItem;
+ int i;
+ if( p==0 ) return 0;
+ pNew = sqliteMalloc( sizeof(*pNew) );
+ if( pNew==0 ) return 0;
+ pNew->nExpr = pNew->nAlloc = p->nExpr;
+ pNew->a = pItem = sqliteMalloc( p->nExpr*sizeof(p->a[0]) );
+ if( pItem==0 ){
+ sqliteFree(pNew);
+ return 0;
+ }
+ pOldItem = p->a;
+ for(i=0; i<p->nExpr; i++, pItem++, pOldItem++){
+ Expr *pNewExpr, *pOldExpr;
+ pItem->pExpr = pNewExpr = sqlite3ExprDup(pOldExpr = pOldItem->pExpr);
+ if( pOldExpr->span.z!=0 && pNewExpr ){
+ /* Always make a copy of the span for top-level expressions in the
+ ** expression list. The logic in SELECT processing that determines
+ ** the names of columns in the result set needs this information */
+ sqlite3TokenCopy(&pNewExpr->span, &pOldExpr->span);
+ }
+ assert( pNewExpr==0 || pNewExpr->span.z!=0
+ || pOldExpr->span.z==0 || sqlite3_malloc_failed );
+ pItem->zName = sqliteStrDup(pOldItem->zName);
+ pItem->sortOrder = pOldItem->sortOrder;
+ pItem->isAgg = pOldItem->isAgg;
+ pItem->done = 0;
+ }
+ return pNew;
+}
+SrcList *sqlite3SrcListDup(SrcList *p){
+ SrcList *pNew;
+ int i;
+ int nByte;
+ if( p==0 ) return 0;
+ nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
+ pNew = sqliteMallocRaw( nByte );
+ if( pNew==0 ) return 0;
+ pNew->nSrc = pNew->nAlloc = p->nSrc;
+ for(i=0; i<p->nSrc; i++){
+ struct SrcList_item *pNewItem = &pNew->a[i];
+ struct SrcList_item *pOldItem = &p->a[i];
+ pNewItem->zDatabase = sqliteStrDup(pOldItem->zDatabase);
+ pNewItem->zName = sqliteStrDup(pOldItem->zName);
+ pNewItem->zAlias = sqliteStrDup(pOldItem->zAlias);
+ pNewItem->jointype = pOldItem->jointype;
+ pNewItem->iCursor = pOldItem->iCursor;
+ pNewItem->pTab = 0;
+ pNewItem->pSelect = sqlite3SelectDup(pOldItem->pSelect);
+ pNewItem->pOn = sqlite3ExprDup(pOldItem->pOn);
+ pNewItem->pUsing = sqlite3IdListDup(pOldItem->pUsing);
+ }
+ return pNew;
+}
+IdList *sqlite3IdListDup(IdList *p){
+ IdList *pNew;
+ int i;
+ if( p==0 ) return 0;
+ pNew = sqliteMallocRaw( sizeof(*pNew) );
+ if( pNew==0 ) return 0;
+ pNew->nId = pNew->nAlloc = p->nId;
+ pNew->a = sqliteMallocRaw( p->nId*sizeof(p->a[0]) );
+ if( pNew->a==0 ) return 0;
+ for(i=0; i<p->nId; i++){
+ struct IdList_item *pNewItem = &pNew->a[i];
+ struct IdList_item *pOldItem = &p->a[i];
+ pNewItem->zName = sqliteStrDup(pOldItem->zName);
+ pNewItem->idx = pOldItem->idx;
+ }
+ return pNew;
+}
+Select *sqlite3SelectDup(Select *p){
+ Select *pNew;
+ if( p==0 ) return 0;
+ pNew = sqliteMallocRaw( sizeof(*p) );
+ if( pNew==0 ) return 0;
+ pNew->isDistinct = p->isDistinct;
+ pNew->pEList = sqlite3ExprListDup(p->pEList);
+ pNew->pSrc = sqlite3SrcListDup(p->pSrc);
+ pNew->pWhere = sqlite3ExprDup(p->pWhere);
+ pNew->pGroupBy = sqlite3ExprListDup(p->pGroupBy);
+ pNew->pHaving = sqlite3ExprDup(p->pHaving);
+ pNew->pOrderBy = sqlite3ExprListDup(p->pOrderBy);
+ pNew->op = p->op;
+ pNew->pPrior = sqlite3SelectDup(p->pPrior);
+ pNew->nLimit = p->nLimit;
+ pNew->nOffset = p->nOffset;
+ pNew->zSelect = 0;
+ pNew->iLimit = -1;
+ pNew->iOffset = -1;
+ pNew->ppOpenTemp = 0;
+ return pNew;
+}
+
+
+/*
+** Add a new element to the end of an expression list. If pList is
+** initially NULL, then create a new expression list.
+*/
+ExprList *sqlite3ExprListAppend(ExprList *pList, Expr *pExpr, Token *pName){
+ if( pList==0 ){
+ pList = sqliteMalloc( sizeof(ExprList) );
+ if( pList==0 ){
+ /* sqlite3ExprDelete(pExpr); // Leak memory if malloc fails */
+ return 0;
+ }
+ assert( pList->nAlloc==0 );
+ }
+ if( pList->nAlloc<=pList->nExpr ){
+ pList->nAlloc = pList->nAlloc*2 + 4;
+ pList->a = sqliteRealloc(pList->a, pList->nAlloc*sizeof(pList->a[0]));
+ if( pList->a==0 ){
+ /* sqlite3ExprDelete(pExpr); // Leak memory if malloc fails */
+ pList->nExpr = pList->nAlloc = 0;
+ return pList;
+ }
+ }
+ assert( pList->a!=0 );
+ if( pExpr || pName ){
+ struct ExprList_item *pItem = &pList->a[pList->nExpr++];
+ memset(pItem, 0, sizeof(*pItem));
+ pItem->pExpr = pExpr;
+ pItem->zName = sqlite3NameFromToken(pName);
+ }
+ return pList;
+}
+
+/*
+** Delete an entire expression list.
+*/
+void sqlite3ExprListDelete(ExprList *pList){
+ int i;
+ struct ExprList_item *pItem;
+ if( pList==0 ) return;
+ assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
+ assert( pList->nExpr<=pList->nAlloc );
+ for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
+ sqlite3ExprDelete(pItem->pExpr);
+ sqliteFree(pItem->zName);
+ }
+ sqliteFree(pList->a);
+ sqliteFree(pList);
+}
+
+/*
+** Walk an expression tree. Return 1 if the expression is constant
+** and 0 if it involves variables.
+**
+** For the purposes of this function, a double-quoted string (ex: "abc")
+** is considered a variable but a single-quoted string (ex: 'abc') is
+** a constant.
+*/
+int sqlite3ExprIsConstant(Expr *p){
+ switch( p->op ){
+ case TK_ID:
+ case TK_COLUMN:
+ case TK_DOT:
+ case TK_FUNCTION:
+ return 0;
+ case TK_NULL:
+ case TK_STRING:
+ case TK_BLOB:
+ case TK_INTEGER:
+ case TK_FLOAT:
+ case TK_VARIABLE:
+ return 1;
+ default: {
+ if( p->pLeft && !sqlite3ExprIsConstant(p->pLeft) ) return 0;
+ if( p->pRight && !sqlite3ExprIsConstant(p->pRight) ) return 0;
+ if( p->pList ){
+ int i;
+ for(i=0; i<p->pList->nExpr; i++){
+ if( !sqlite3ExprIsConstant(p->pList->a[i].pExpr) ) return 0;
+ }
+ }
+ return p->pLeft!=0 || p->pRight!=0 || (p->pList && p->pList->nExpr>0);
+ }
+ }
+ return 0;
+}
+
+/*
+** If the given expression codes a constant integer that is small enough
+** to fit in a 32-bit integer, return 1 and put the value of the integer
+** in *pValue. If the expression is not an integer or if it is too big
+** to fit in a signed 32-bit integer, return 0 and leave *pValue unchanged.
+*/
+int sqlite3ExprIsInteger(Expr *p, int *pValue){
+ switch( p->op ){
+ case TK_INTEGER: {
+ if( sqlite3GetInt32(p->token.z, pValue) ){
+ return 1;
+ }
+ break;
+ }
+ case TK_STRING: {
+ const u8 *z = (u8*)p->token.z;
+ int n = p->token.n;
+ if( n>0 && z[0]=='-' ){ z++; n--; }
+ while( n>0 && *z && isdigit(*z) ){ z++; n--; }
+ if( n==0 && sqlite3GetInt32(p->token.z, pValue) ){
+ return 1;
+ }
+ break;
+ }
+ case TK_UPLUS: {
+ return sqlite3ExprIsInteger(p->pLeft, pValue);
+ }
+ case TK_UMINUS: {
+ int v;
+ if( sqlite3ExprIsInteger(p->pLeft, &v) ){
+ *pValue = -v;
+ return 1;
+ }
+ break;
+ }
+ default: break;
+ }
+ return 0;
+}
+
+/*
+** Return TRUE if the given string is a row-id column name.
+*/
+int sqlite3IsRowid(const char *z){
+ if( sqlite3StrICmp(z, "_ROWID_")==0 ) return 1;
+ if( sqlite3StrICmp(z, "ROWID")==0 ) return 1;
+ if( sqlite3StrICmp(z, "OID")==0 ) return 1;
+ return 0;
+}
+
+/*
+** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
+** that name in the set of source tables in pSrcList and make the pExpr
+** expression node refer back to that source column. The following changes
+** are made to pExpr:
+**
+** pExpr->iDb Set the index in db->aDb[] of the database holding
+** the table.
+** pExpr->iTable Set to the cursor number for the table obtained
+** from pSrcList.
+** pExpr->iColumn Set to the column number within the table.
+** pExpr->op Set to TK_COLUMN.
+** pExpr->pLeft Any expression this points to is deleted
+** pExpr->pRight Any expression this points to is deleted.
+**
+** The pDbToken is the name of the database (the "X"). This value may be
+** NULL meaning that name is of the form Y.Z or Z. Any available database
+** can be used. The pTableToken is the name of the table (the "Y"). This
+** value can be NULL if pDbToken is also NULL. If pTableToken is NULL it
+** means that the form of the name is Z and that columns from any table
+** can be used.
+**
+** If the name cannot be resolved unambiguously, leave an error message
+** in pParse and return non-zero. Return zero on success.
+*/
+static int lookupName(
+ Parse *pParse, /* The parsing context */
+ Token *pDbToken, /* Name of the database containing table, or NULL */
+ Token *pTableToken, /* Name of table containing column, or NULL */
+ Token *pColumnToken, /* Name of the column. */
+ SrcList *pSrcList, /* List of tables used to resolve column names */
+ ExprList *pEList, /* List of expressions used to resolve "AS" */
+ Expr *pExpr /* Make this EXPR node point to the selected column */
+){
+ char *zDb = 0; /* Name of the database. The "X" in X.Y.Z */
+ char *zTab = 0; /* Name of the table. The "Y" in X.Y.Z or Y.Z */
+ char *zCol = 0; /* Name of the column. The "Z" */
+ int i, j; /* Loop counters */
+ int cnt = 0; /* Number of matching column names */
+ int cntTab = 0; /* Number of matching table names */
+ sqlite3 *db = pParse->db; /* The database */
+
+ assert( pColumnToken && pColumnToken->z ); /* The Z in X.Y.Z cannot be NULL */
+ zDb = sqlite3NameFromToken(pDbToken);
+ zTab = sqlite3NameFromToken(pTableToken);
+ zCol = sqlite3NameFromToken(pColumnToken);
+ if( sqlite3_malloc_failed ){
+ return 1; /* Leak memory (zDb and zTab) if malloc fails */
+ }
+ assert( zTab==0 || pEList==0 );
+
+ pExpr->iTable = -1;
+ for(i=0; i<pSrcList->nSrc; i++){
+ struct SrcList_item *pItem = &pSrcList->a[i];
+ Table *pTab = pItem->pTab;
+ Column *pCol;
+
+ if( pTab==0 ) continue;
+ assert( pTab->nCol>0 );
+ if( zTab ){
+ if( pItem->zAlias ){
+ char *zTabName = pItem->zAlias;
+ if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
+ }else{
+ char *zTabName = pTab->zName;
+ if( zTabName==0 || sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
+ if( zDb!=0 && sqlite3StrICmp(db->aDb[pTab->iDb].zName, zDb)!=0 ){
+ continue;
+ }
+ }
+ }
+ if( 0==(cntTab++) ){
+ pExpr->iTable = pItem->iCursor;
+ pExpr->iDb = pTab->iDb;
+ }
+ for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
+ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
+ cnt++;
+ pExpr->iTable = pItem->iCursor;
+ pExpr->iDb = pTab->iDb;
+ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
+ pExpr->iColumn = j==pTab->iPKey ? -1 : j;
+ pExpr->affinity = pTab->aCol[j].affinity;
+ pExpr->pColl = pTab->aCol[j].pColl;
+ break;
+ }
+ }
+ }
+
+ /* If we have not already resolved the name, then maybe
+ ** it is a new.* or old.* trigger argument reference
+ */
+ if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){
+ TriggerStack *pTriggerStack = pParse->trigStack;
+ Table *pTab = 0;
+ if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){
+ pExpr->iTable = pTriggerStack->newIdx;
+ assert( pTriggerStack->pTab );
+ pTab = pTriggerStack->pTab;
+ }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab) == 0 ){
+ pExpr->iTable = pTriggerStack->oldIdx;
+ assert( pTriggerStack->pTab );
+ pTab = pTriggerStack->pTab;
+ }
+
+ if( pTab ){
+ int j;
+ Column *pCol = pTab->aCol;
+
+ pExpr->iDb = pTab->iDb;
+ cntTab++;
+ for(j=0; j < pTab->nCol; j++, pCol++) {
+ if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
+ cnt++;
+ pExpr->iColumn = j==pTab->iPKey ? -1 : j;
+ pExpr->affinity = pTab->aCol[j].affinity;
+ pExpr->pColl = pTab->aCol[j].pColl;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ ** Perhaps the name is a reference to the ROWID
+ */
+ if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
+ cnt = 1;
+ pExpr->iColumn = -1;
+ pExpr->affinity = SQLITE_AFF_INTEGER;
+ }
+
+ /*
+ ** If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z
+ ** might refer to an result-set alias. This happens, for example, when
+ ** we are resolving names in the WHERE clause of the following command:
+ **
+ ** SELECT a+b AS x FROM table WHERE x<10;
+ **
+ ** In cases like this, replace pExpr with a copy of the expression that
+ ** forms the result set entry ("a+b" in the example) and return immediately.
+ ** Note that the expression in the result set should have already been
+ ** resolved by the time the WHERE clause is resolved.
+ */
+ if( cnt==0 && pEList!=0 ){
+ for(j=0; j<pEList->nExpr; j++){
+ char *zAs = pEList->a[j].zName;
+ if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
+ assert( pExpr->pLeft==0 && pExpr->pRight==0 );
+ pExpr->op = TK_AS;
+ pExpr->iColumn = j;
+ pExpr->pLeft = sqlite3ExprDup(pEList->a[j].pExpr);
+ sqliteFree(zCol);
+ assert( zTab==0 && zDb==0 );
+ return 0;
+ }
+ }
+ }
+
+ /*
+ ** If X and Y are NULL (in other words if only the column name Z is
+ ** supplied) and the value of Z is enclosed in double-quotes, then
+ ** Z is a string literal if it doesn't match any column names. In that
+ ** case, we need to return right away and not make any changes to
+ ** pExpr.
+ */
+ if( cnt==0 && zTab==0 && pColumnToken->z[0]=='"' ){
+ sqliteFree(zCol);
+ return 0;
+ }
+
+ /*
+ ** cnt==0 means there was not match. cnt>1 means there were two or
+ ** more matches. Either way, we have an error.
+ */
+ if( cnt!=1 ){
+ char *z = 0;
+ char *zErr;
+ zErr = cnt==0 ? "no such column: %s" : "ambiguous column name: %s";
+ if( zDb ){
+ sqlite3SetString(&z, zDb, ".", zTab, ".", zCol, 0);
+ }else if( zTab ){
+ sqlite3SetString(&z, zTab, ".", zCol, 0);
+ }else{
+ z = sqliteStrDup(zCol);
+ }
+ sqlite3ErrorMsg(pParse, zErr, z);
+ sqliteFree(z);
+ }
+
+ /* Clean up and return
+ */
+ sqliteFree(zDb);
+ sqliteFree(zTab);
+ sqliteFree(zCol);
+ sqlite3ExprDelete(pExpr->pLeft);
+ pExpr->pLeft = 0;
+ sqlite3ExprDelete(pExpr->pRight);
+ pExpr->pRight = 0;
+ pExpr->op = TK_COLUMN;
+ sqlite3AuthRead(pParse, pExpr, pSrcList);
+ return cnt!=1;
+}
+
+/*
+** This routine walks an expression tree and resolves references to
+** table columns. Nodes of the form ID.ID or ID resolve into an
+** index to the table in the table list and a column offset. The
+** Expr.opcode for such nodes is changed to TK_COLUMN. The Expr.iTable
+** value is changed to the index of the referenced table in pTabList
+** plus the "base" value. The base value will ultimately become the
+** VDBE cursor number for a cursor that is pointing into the referenced
+** table. The Expr.iColumn value is changed to the index of the column
+** of the referenced table. The Expr.iColumn value for the special
+** ROWID column is -1. Any INTEGER PRIMARY KEY column is tried as an
+** alias for ROWID.
+**
+** We also check for instances of the IN operator. IN comes in two
+** forms:
+**
+** expr IN (exprlist)
+** and
+** expr IN (SELECT ...)
+**
+** The first form is handled by creating a set holding the list
+** of allowed values. The second form causes the SELECT to generate
+** a temporary table.
+**
+** This routine also looks for scalar SELECTs that are part of an expression.
+** If it finds any, it generates code to write the value of that select
+** into a memory cell.
+**
+** Unknown columns or tables provoke an error. The function returns
+** the number of errors seen and leaves an error message on pParse->zErrMsg.
+*/
+int sqlite3ExprResolveIds(
+ Parse *pParse, /* The parser context */
+ SrcList *pSrcList, /* List of tables used to resolve column names */
+ ExprList *pEList, /* List of expressions used to resolve "AS" */
+ Expr *pExpr /* The expression to be analyzed. */
+){
+ int i;
+
+ if( pExpr==0 || pSrcList==0 ) return 0;
+ for(i=0; i<pSrcList->nSrc; i++){
+ assert( pSrcList->a[i].iCursor>=0 && pSrcList->a[i].iCursor<pParse->nTab );
+ }
+ switch( pExpr->op ){
+ /* Double-quoted strings (ex: "abc") are used as identifiers if
+ ** possible. Otherwise they remain as strings. Single-quoted
+ ** strings (ex: 'abc') are always string literals.
+ */
+ case TK_STRING: {
+ if( pExpr->token.z[0]=='\'' ) break;
+ /* Fall thru into the TK_ID case if this is a double-quoted string */
+ }
+ /* A lone identifier is the name of a columnd.
+ */
+ case TK_ID: {
+ if( lookupName(pParse, 0, 0, &pExpr->token, pSrcList, pEList, pExpr) ){
+ return 1;
+ }
+ break;
+ }
+
+ /* A table name and column name: ID.ID
+ ** Or a database, table and column: ID.ID.ID
+ */
+ case TK_DOT: {
+ Token *pColumn;
+ Token *pTable;
+ Token *pDb;
+ Expr *pRight;
+
+ pRight = pExpr->pRight;
+ if( pRight->op==TK_ID ){
+ pDb = 0;
+ pTable = &pExpr->pLeft->token;
+ pColumn = &pRight->token;
+ }else{
+ assert( pRight->op==TK_DOT );
+ pDb = &pExpr->pLeft->token;
+ pTable = &pRight->pLeft->token;
+ pColumn = &pRight->pRight->token;
+ }
+ if( lookupName(pParse, pDb, pTable, pColumn, pSrcList, 0, pExpr) ){
+ return 1;
+ }
+ break;
+ }
+
+ case TK_IN: {
+ char affinity;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ KeyInfo keyInfo;
+ int addr; /* Address of OP_OpenTemp instruction */
+
+ if( v==0 ) return 1;
+ if( sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){
+ return 1;
+ }
+ affinity = sqlite3ExprAffinity(pExpr->pLeft);
+
+ /* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
+ ** expression it is handled the same way. A temporary table is
+ ** filled with single-field index keys representing the results
+ ** from the SELECT or the <exprlist>.
+ **
+ ** If the 'x' expression is a column value, or the SELECT...
+ ** statement returns a column value, then the affinity of that
+ ** column is used to build the index keys. If both 'x' and the
+ ** SELECT... statement are columns, then numeric affinity is used
+ ** if either column has NUMERIC or INTEGER affinity. If neither
+ ** 'x' nor the SELECT... statement are columns, then numeric affinity
+ ** is used.
+ */
+ pExpr->iTable = pParse->nTab++;
+ addr = sqlite3VdbeAddOp(v, OP_OpenTemp, pExpr->iTable, 0);
+ memset(&keyInfo, 0, sizeof(keyInfo));
+ keyInfo.nField = 1;
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, pExpr->iTable, 1);
+
+ if( pExpr->pSelect ){
+ /* Case 1: expr IN (SELECT ...)
+ **
+ ** Generate code to write the results of the select into the temporary
+ ** table allocated and opened above.
+ */
+ int iParm = pExpr->iTable + (((int)affinity)<<16);
+ ExprList *pEList;
+ assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
+ sqlite3Select(pParse, pExpr->pSelect, SRT_Set, iParm, 0, 0, 0, 0);
+ pEList = pExpr->pSelect->pEList;
+ if( pEList && pEList->nExpr>0 ){
+ keyInfo.aColl[0] = binaryCompareCollSeq(pParse, pExpr->pLeft,
+ pEList->a[0].pExpr);
+ }
+ }else if( pExpr->pList ){
+ /* Case 2: expr IN (exprlist)
+ **
+ ** For each expression, build an index key from the evaluation and
+ ** store it in the temporary table. If <expr> is a column, then use
+ ** that columns affinity when building index keys. If <expr> is not
+ ** a column, use numeric affinity.
+ */
+ int i;
+ if( !affinity ){
+ affinity = SQLITE_AFF_NUMERIC;
+ }
+ keyInfo.aColl[0] = pExpr->pLeft->pColl;
+
+ /* Loop through each expression in <exprlist>. */
+ for(i=0; i<pExpr->pList->nExpr; i++){
+ Expr *pE2 = pExpr->pList->a[i].pExpr;
+
+ /* Check that the expression is constant and valid. */
+ if( !sqlite3ExprIsConstant(pE2) ){
+ sqlite3ErrorMsg(pParse,
+ "right-hand side of IN operator must be constant");
+ return 1;
+ }
+ if( sqlite3ExprCheck(pParse, pE2, 0, 0) ){
+ return 1;
+ }
+
+ /* Evaluate the expression and insert it into the temp table */
+ sqlite3ExprCode(pParse, pE2);
+ sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutStrKey, pExpr->iTable, 0);
+ }
+ }
+ sqlite3VdbeChangeP3(v, addr, (void *)&keyInfo, P3_KEYINFO);
+
+ break;
+ }
+
+ case TK_SELECT: {
+ /* This has to be a scalar SELECT. Generate code to put the
+ ** value of this select in a memory cell and record the number
+ ** of the memory cell in iColumn.
+ */
+ pExpr->iColumn = pParse->nMem++;
+ if(sqlite3Select(pParse, pExpr->pSelect, SRT_Mem,pExpr->iColumn,0,0,0,0)){
+ return 1;
+ }
+ break;
+ }
+
+ /* For all else, just recursively walk the tree */
+ default: {
+ if( pExpr->pLeft
+ && sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pLeft) ){
+ return 1;
+ }
+ if( pExpr->pRight
+ && sqlite3ExprResolveIds(pParse, pSrcList, pEList, pExpr->pRight) ){
+ return 1;
+ }
+ if( pExpr->pList ){
+ int i;
+ ExprList *pList = pExpr->pList;
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pArg = pList->a[i].pExpr;
+ if( sqlite3ExprResolveIds(pParse, pSrcList, pEList, pArg) ){
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** pExpr is a node that defines a function of some kind. It might
+** be a syntactic function like "count(x)" or it might be a function
+** that implements an operator, like "a LIKE b".
+**
+** This routine makes *pzName point to the name of the function and
+** *pnName hold the number of characters in the function name.
+*/
+static void getFunctionName(Expr *pExpr, const char **pzName, int *pnName){
+ switch( pExpr->op ){
+ case TK_FUNCTION: {
+ *pzName = pExpr->token.z;
+ *pnName = pExpr->token.n;
+ break;
+ }
+ case TK_LIKE: {
+ *pzName = "like";
+ *pnName = 4;
+ break;
+ }
+ case TK_GLOB: {
+ *pzName = "glob";
+ *pnName = 4;
+ break;
+ }
+ default: {
+ *pzName = "can't happen";
+ *pnName = 12;
+ break;
+ }
+ }
+}
+
+/*
+** Error check the functions in an expression. Make sure all
+** function names are recognized and all functions have the correct
+** number of arguments. Leave an error message in pParse->zErrMsg
+** if anything is amiss. Return the number of errors.
+**
+** if pIsAgg is not null and this expression is an aggregate function
+** (like count(*) or max(value)) then write a 1 into *pIsAgg.
+*/
+int sqlite3ExprCheck(Parse *pParse, Expr *pExpr, int allowAgg, int *pIsAgg){
+ int nErr = 0;
+ if( pExpr==0 ) return 0;
+ switch( pExpr->op ){
+ case TK_GLOB:
+ case TK_LIKE:
+ case TK_FUNCTION: {
+ int n = pExpr->pList ? pExpr->pList->nExpr : 0; /* Number of arguments */
+ int no_such_func = 0; /* True if no such function exists */
+ int wrong_num_args = 0; /* True if wrong number of arguments */
+ int is_agg = 0; /* True if is an aggregate function */
+ int i;
+ int nId; /* Number of characters in function name */
+ const char *zId; /* The function name. */
+ FuncDef *pDef;
+ int enc = pParse->db->enc;
+
+ getFunctionName(pExpr, &zId, &nId);
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
+ if( pDef==0 ){
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0);
+ if( pDef==0 ){
+ no_such_func = 1;
+ }else{
+ wrong_num_args = 1;
+ }
+ }else{
+ is_agg = pDef->xFunc==0;
+ }
+ if( is_agg && !allowAgg ){
+ sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId, zId);
+ nErr++;
+ is_agg = 0;
+ }else if( no_such_func ){
+ sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
+ nErr++;
+ }else if( wrong_num_args ){
+ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()",
+ nId, zId);
+ nErr++;
+ }
+ if( is_agg ){
+ pExpr->op = TK_AGG_FUNCTION;
+ if( pIsAgg ) *pIsAgg = 1;
+ }
+ for(i=0; nErr==0 && i<n; i++){
+ nErr = sqlite3ExprCheck(pParse, pExpr->pList->a[i].pExpr,
+ allowAgg && !is_agg, pIsAgg);
+ }
+ /* FIX ME: Compute pExpr->affinity based on the expected return
+ ** type of the function
+ */
+ }
+ default: {
+ if( pExpr->pLeft ){
+ nErr = sqlite3ExprCheck(pParse, pExpr->pLeft, allowAgg, pIsAgg);
+ }
+ if( nErr==0 && pExpr->pRight ){
+ nErr = sqlite3ExprCheck(pParse, pExpr->pRight, allowAgg, pIsAgg);
+ }
+ if( nErr==0 && pExpr->pList ){
+ int n = pExpr->pList->nExpr;
+ int i;
+ for(i=0; nErr==0 && i<n; i++){
+ Expr *pE2 = pExpr->pList->a[i].pExpr;
+ nErr = sqlite3ExprCheck(pParse, pE2, allowAgg, pIsAgg);
+ }
+ }
+ break;
+ }
+ }
+ return nErr;
+}
+
+/*
+** Call sqlite3ExprResolveIds() followed by sqlite3ExprCheck().
+**
+** This routine is provided as a convenience since it is very common
+** to call ResolveIds() and Check() back to back.
+*/
+int sqlite3ExprResolveAndCheck(
+ Parse *pParse, /* The parser context */
+ SrcList *pSrcList, /* List of tables used to resolve column names */
+ ExprList *pEList, /* List of expressions used to resolve "AS" */
+ Expr *pExpr, /* The expression to be analyzed. */
+ int allowAgg, /* True to allow aggregate expressions */
+ int *pIsAgg /* Set to TRUE if aggregates are found */
+){
+ if( pExpr==0 ) return 0;
+ if( sqlite3ExprResolveIds(pParse,pSrcList,pEList,pExpr) ){
+ return 1;
+ }
+ return sqlite3ExprCheck(pParse, pExpr, allowAgg, pIsAgg);
+}
+
+/*
+** Generate an instruction that will put the integer describe by
+** text z[0..n-1] on the stack.
+*/
+static void codeInteger(Vdbe *v, const char *z, int n){
+ int i;
+ if( sqlite3GetInt32(z, &i) ){
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ }else if( sqlite3FitsIn64Bits(z) ){
+ sqlite3VdbeOp3(v, OP_Integer, 0, 0, z, n);
+ }else{
+ sqlite3VdbeOp3(v, OP_Real, 0, 0, z, n);
+ }
+}
+
+/*
+** Generate code into the current Vdbe to evaluate the given
+** expression and leave the result on the top of stack.
+**
+** This code depends on the fact that certain token values (ex: TK_EQ)
+** are the same as opcode values (ex: OP_Eq) that implement the corresponding
+** operation. Special comments in vdbe.c and the mkopcodeh.awk script in
+** the make process cause these values to align. Assert()s in the code
+** below verify that the numbers are aligned correctly.
+*/
+void sqlite3ExprCode(Parse *pParse, Expr *pExpr){
+ Vdbe *v = pParse->pVdbe;
+ int op;
+ if( v==0 || pExpr==0 ) return;
+ op = pExpr->op;
+ switch( op ){
+ case TK_COLUMN: {
+ if( pParse->useAgg ){
+ sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg);
+ }else if( pExpr->iColumn>=0 ){
+ sqlite3VdbeAddOp(v, OP_Column, pExpr->iTable, pExpr->iColumn);
+#ifndef NDEBUG
+ if( pExpr->span.z && pExpr->span.n>0 && pExpr->span.n<100 ){
+ VdbeComment((v, "# %T", &pExpr->span));
+ }
+#endif
+ }else{
+ sqlite3VdbeAddOp(v, OP_Recno, pExpr->iTable, 0);
+ }
+ break;
+ }
+ case TK_INTEGER: {
+ codeInteger(v, pExpr->token.z, pExpr->token.n);
+ break;
+ }
+ case TK_FLOAT:
+ case TK_STRING: {
+ assert( TK_FLOAT==OP_Real );
+ assert( TK_STRING==OP_String8 );
+ sqlite3VdbeOp3(v, op, 0, 0, pExpr->token.z, pExpr->token.n);
+ sqlite3VdbeDequoteP3(v, -1);
+ break;
+ }
+ case TK_BLOB: {
+ assert( TK_BLOB==OP_HexBlob );
+ sqlite3VdbeOp3(v, op, 0, 0, pExpr->token.z+1, pExpr->token.n-1);
+ sqlite3VdbeDequoteP3(v, -1);
+ break;
+ }
+ case TK_NULL: {
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ break;
+ }
+ case TK_VARIABLE: {
+ sqlite3VdbeAddOp(v, OP_Variable, pExpr->iTable, 0);
+ if( pExpr->token.n>1 ){
+ sqlite3VdbeChangeP3(v, -1, pExpr->token.z, pExpr->token.n);
+ }
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ assert( TK_LT==OP_Lt );
+ assert( TK_LE==OP_Le );
+ assert( TK_GT==OP_Gt );
+ assert( TK_GE==OP_Ge );
+ assert( TK_EQ==OP_Eq );
+ assert( TK_NE==OP_Ne );
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3ExprCode(pParse, pExpr->pRight);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, 0, 0);
+ break;
+ }
+ case TK_AND:
+ case TK_OR:
+ case TK_PLUS:
+ case TK_STAR:
+ case TK_MINUS:
+ case TK_REM:
+ case TK_BITAND:
+ case TK_BITOR:
+ case TK_SLASH:
+ case TK_LSHIFT:
+ case TK_RSHIFT:
+ case TK_CONCAT: {
+ assert( TK_AND==OP_And );
+ assert( TK_OR==OP_Or );
+ assert( TK_PLUS==OP_Add );
+ assert( TK_MINUS==OP_Subtract );
+ assert( TK_REM==OP_Remainder );
+ assert( TK_BITAND==OP_BitAnd );
+ assert( TK_BITOR==OP_BitOr );
+ assert( TK_SLASH==OP_Divide );
+ assert( TK_LSHIFT==OP_ShiftLeft );
+ assert( TK_RSHIFT==OP_ShiftRight );
+ assert( TK_CONCAT==OP_Concat );
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3ExprCode(pParse, pExpr->pRight);
+ sqlite3VdbeAddOp(v, op, 0, 0);
+ break;
+ }
+ case TK_UMINUS: {
+ Expr *pLeft = pExpr->pLeft;
+ assert( pLeft );
+ if( pLeft->op==TK_FLOAT || pLeft->op==TK_INTEGER ){
+ Token *p = &pLeft->token;
+ char *z = sqliteMalloc( p->n + 2 );
+ sprintf(z, "-%.*s", p->n, p->z);
+ if( pLeft->op==TK_FLOAT ){
+ sqlite3VdbeOp3(v, OP_Real, 0, 0, z, p->n+1);
+ }else{
+ codeInteger(v, z, p->n+1);
+ }
+ sqliteFree(z);
+ break;
+ }
+ /* Fall through into TK_NOT */
+ }
+ case TK_BITNOT:
+ case TK_NOT: {
+ assert( TK_BITNOT==OP_BitNot );
+ assert( TK_NOT==OP_Not );
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3VdbeAddOp(v, op, 0, 0);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ int dest;
+ assert( TK_ISNULL==OP_IsNull );
+ assert( TK_NOTNULL==OP_NotNull );
+ sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ dest = sqlite3VdbeCurrentAddr(v) + 2;
+ sqlite3VdbeAddOp(v, op, 1, dest);
+ sqlite3VdbeAddOp(v, OP_AddImm, -1, 0);
+ break;
+ }
+ case TK_AGG_FUNCTION: {
+ sqlite3VdbeAddOp(v, OP_AggGet, 0, pExpr->iAgg);
+ break;
+ }
+ case TK_GLOB:
+ case TK_LIKE:
+ case TK_FUNCTION: {
+ ExprList *pList = pExpr->pList;
+ int nExpr = pList ? pList->nExpr : 0;
+ FuncDef *pDef;
+ int nId;
+ const char *zId;
+ int p2 = 0;
+ int i;
+ u8 enc = pParse->db->enc;
+ CollSeq *pColl = 0;
+ getFunctionName(pExpr, &zId, &nId);
+ pDef = sqlite3FindFunction(pParse->db, zId, nId, nExpr, enc, 0);
+ assert( pDef!=0 );
+ nExpr = sqlite3ExprCodeExprList(pParse, pList);
+ for(i=0; i<nExpr && i<32; i++){
+ if( sqlite3ExprIsConstant(pList->a[i].pExpr) ){
+ p2 |= (1<<i);
+ }
+ if( pDef->needCollSeq && !pColl ){
+ pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
+ }
+ }
+ if( pDef->needCollSeq ){
+ if( !pColl ) pColl = pParse->db->pDfltColl;
+ sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ }
+ sqlite3VdbeOp3(v, OP_Function, nExpr, p2, (char*)pDef, P3_FUNCDEF);
+ break;
+ }
+ case TK_SELECT: {
+ sqlite3VdbeAddOp(v, OP_MemLoad, pExpr->iColumn, 0);
+ VdbeComment((v, "# load subquery result"));
+ break;
+ }
+ case TK_IN: {
+ int addr;
+ char affinity;
+
+ /* Figure out the affinity to use to create a key from the results
+ ** of the expression. affinityStr stores a static string suitable for
+ ** P3 of OP_MakeRecord.
+ */
+ affinity = comparisonAffinity(pExpr);
+
+ sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
+
+ /* Code the <expr> from "<expr> IN (...)". The temporary table
+ ** pExpr->iTable contains the values that make up the (...) set.
+ */
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp(v, OP_NotNull, -1, addr+4); /* addr + 0 */
+ sqlite3VdbeAddOp(v, OP_Pop, 2, 0);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addr+7);
+ sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &affinity, 1); /* addr + 4 */
+ sqlite3VdbeAddOp(v, OP_Found, pExpr->iTable, addr+7);
+ sqlite3VdbeAddOp(v, OP_AddImm, -1, 0); /* addr + 6 */
+
+ break;
+ }
+ case TK_BETWEEN: {
+ Expr *pLeft = pExpr->pLeft;
+ struct ExprList_item *pLItem = pExpr->pList->a;
+ Expr *pRight = pLItem->pExpr;
+ sqlite3ExprCode(pParse, pLeft);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3ExprCode(pParse, pRight);
+ codeCompare(pParse, pLeft, pRight, OP_Ge, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ pLItem++;
+ pRight = pLItem->pExpr;
+ sqlite3ExprCode(pParse, pRight);
+ codeCompare(pParse, pLeft, pRight, OP_Le, 0, 0);
+ sqlite3VdbeAddOp(v, OP_And, 0, 0);
+ break;
+ }
+ case TK_UPLUS:
+ case TK_AS: {
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ break;
+ }
+ case TK_CASE: {
+ int expr_end_label;
+ int jumpInst;
+ int addr;
+ int nExpr;
+ int i;
+ ExprList *pEList;
+ struct ExprList_item *aListelem;
+
+ assert(pExpr->pList);
+ assert((pExpr->pList->nExpr % 2) == 0);
+ assert(pExpr->pList->nExpr > 0);
+ pEList = pExpr->pList;
+ aListelem = pEList->a;
+ nExpr = pEList->nExpr;
+ expr_end_label = sqlite3VdbeMakeLabel(v);
+ if( pExpr->pLeft ){
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ }
+ for(i=0; i<nExpr; i=i+2){
+ sqlite3ExprCode(pParse, aListelem[i].pExpr);
+ if( pExpr->pLeft ){
+ sqlite3VdbeAddOp(v, OP_Dup, 1, 1);
+ jumpInst = codeCompare(pParse, pExpr->pLeft, aListelem[i].pExpr,
+ OP_Ne, 0, 1);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ }else{
+ jumpInst = sqlite3VdbeAddOp(v, OP_IfNot, 1, 0);
+ }
+ sqlite3ExprCode(pParse, aListelem[i+1].pExpr);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, expr_end_label);
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeChangeP2(v, jumpInst, addr);
+ }
+ if( pExpr->pLeft ){
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ }
+ if( pExpr->pRight ){
+ sqlite3ExprCode(pParse, pExpr->pRight);
+ }else{
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ }
+ sqlite3VdbeResolveLabel(v, expr_end_label);
+ break;
+ }
+ case TK_RAISE: {
+ if( !pParse->trigStack ){
+ sqlite3ErrorMsg(pParse,
+ "RAISE() may only be used within a trigger-program");
+ return;
+ }
+ if( pExpr->iColumn!=OE_Ignore ){
+ assert( pExpr->iColumn==OE_Rollback ||
+ pExpr->iColumn == OE_Abort ||
+ pExpr->iColumn == OE_Fail );
+ sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->iColumn,
+ pExpr->token.z, pExpr->token.n);
+ sqlite3VdbeDequoteP3(v, -1);
+ } else {
+ assert( pExpr->iColumn == OE_Ignore );
+ sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, pParse->trigStack->ignoreJump);
+ VdbeComment((v, "# raise(IGNORE)"));
+ }
+ }
+ break;
+ }
+}
+
+/*
+** Generate code that pushes the value of every element of the given
+** expression list onto the stack.
+**
+** Return the number of elements pushed onto the stack.
+*/
+int sqlite3ExprCodeExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList /* The expression list to be coded */
+){
+ struct ExprList_item *pItem;
+ int i, n;
+ Vdbe *v;
+ if( pList==0 ) return 0;
+ v = sqlite3GetVdbe(pParse);
+ n = pList->nExpr;
+ for(pItem=pList->a, i=0; i<n; i++, pItem++){
+ sqlite3ExprCode(pParse, pItem->pExpr);
+ }
+ return n;
+}
+
+/*
+** Generate code for a boolean expression such that a jump is made
+** to the label "dest" if the expression is true but execution
+** continues straight thru if the expression is false.
+**
+** If the expression evaluates to NULL (neither true nor false), then
+** take the jump if the jumpIfNull flag is true.
+**
+** This code depends on the fact that certain token values (ex: TK_EQ)
+** are the same as opcode values (ex: OP_Eq) that implement the corresponding
+** operation. Special comments in vdbe.c and the mkopcodeh.awk script in
+** the make process cause these values to align. Assert()s in the code
+** below verify that the numbers are aligned correctly.
+*/
+void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
+ Vdbe *v = pParse->pVdbe;
+ int op = 0;
+ if( v==0 || pExpr==0 ) return;
+ op = pExpr->op;
+ switch( op ){
+ case TK_AND: {
+ int d2 = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2, !jumpIfNull);
+ sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3VdbeResolveLabel(v, d2);
+ break;
+ }
+ case TK_OR: {
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ break;
+ }
+ case TK_NOT: {
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ assert( TK_LT==OP_Lt );
+ assert( TK_LE==OP_Le );
+ assert( TK_GT==OP_Gt );
+ assert( TK_GE==OP_Ge );
+ assert( TK_EQ==OP_Eq );
+ assert( TK_NE==OP_Ne );
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3ExprCode(pParse, pExpr->pRight);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ assert( TK_ISNULL==OP_IsNull );
+ assert( TK_NOTNULL==OP_NotNull );
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3VdbeAddOp(v, op, 1, dest);
+ break;
+ }
+ case TK_BETWEEN: {
+ /* The expression "x BETWEEN y AND z" is implemented as:
+ **
+ ** 1 IF (x < y) GOTO 3
+ ** 2 IF (x <= z) GOTO <dest>
+ ** 3 ...
+ */
+ int addr;
+ Expr *pLeft = pExpr->pLeft;
+ Expr *pRight = pExpr->pList->a[0].pExpr;
+ sqlite3ExprCode(pParse, pLeft);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3ExprCode(pParse, pRight);
+ addr = codeCompare(pParse, pLeft, pRight, OP_Lt, 0, !jumpIfNull);
+
+ pRight = pExpr->pList->a[1].pExpr;
+ sqlite3ExprCode(pParse, pRight);
+ codeCompare(pParse, pLeft, pRight, OP_Le, dest, jumpIfNull);
+
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ break;
+ }
+ default: {
+ sqlite3ExprCode(pParse, pExpr);
+ sqlite3VdbeAddOp(v, OP_If, jumpIfNull, dest);
+ break;
+ }
+ }
+}
+
+/*
+** Generate code for a boolean expression such that a jump is made
+** to the label "dest" if the expression is false but execution
+** continues straight thru if the expression is true.
+**
+** If the expression evaluates to NULL (neither true nor false) then
+** jump if jumpIfNull is true or fall through if jumpIfNull is false.
+*/
+void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
+ Vdbe *v = pParse->pVdbe;
+ int op = 0;
+ if( v==0 || pExpr==0 ) return;
+
+ /* The value of pExpr->op and op are related as follows:
+ **
+ ** pExpr->op op
+ ** --------- ----------
+ ** TK_ISNULL OP_NotNull
+ ** TK_NOTNULL OP_IsNull
+ ** TK_NE OP_Eq
+ ** TK_EQ OP_Ne
+ ** TK_GT OP_Le
+ ** TK_LE OP_Gt
+ ** TK_GE OP_Lt
+ ** TK_LT OP_Ge
+ **
+ ** For other values of pExpr->op, op is undefined and unused.
+ ** The value of TK_ and OP_ constants are arranged such that we
+ ** can compute the mapping above using the following expression.
+ ** Assert()s verify that the computation is correct.
+ */
+ op = ((pExpr->op+(TK_ISNULL&1))^1)-(TK_ISNULL&1);
+
+ /* Verify correct alignment of TK_ and OP_ constants
+ */
+ assert( pExpr->op!=TK_ISNULL || op==OP_NotNull );
+ assert( pExpr->op!=TK_NOTNULL || op==OP_IsNull );
+ assert( pExpr->op!=TK_NE || op==OP_Eq );
+ assert( pExpr->op!=TK_EQ || op==OP_Ne );
+ assert( pExpr->op!=TK_LT || op==OP_Ge );
+ assert( pExpr->op!=TK_LE || op==OP_Gt );
+ assert( pExpr->op!=TK_GT || op==OP_Le );
+ assert( pExpr->op!=TK_GE || op==OP_Lt );
+
+ switch( pExpr->op ){
+ case TK_AND: {
+ sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ break;
+ }
+ case TK_OR: {
+ int d2 = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, !jumpIfNull);
+ sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3VdbeResolveLabel(v, d2);
+ break;
+ }
+ case TK_NOT: {
+ sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ break;
+ }
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_NE:
+ case TK_EQ: {
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3ExprCode(pParse, pExpr->pRight);
+ codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, dest, jumpIfNull);
+ break;
+ }
+ case TK_ISNULL:
+ case TK_NOTNULL: {
+ sqlite3ExprCode(pParse, pExpr->pLeft);
+ sqlite3VdbeAddOp(v, op, 1, dest);
+ break;
+ }
+ case TK_BETWEEN: {
+ /* The expression is "x BETWEEN y AND z". It is implemented as:
+ **
+ ** 1 IF (x >= y) GOTO 3
+ ** 2 GOTO <dest>
+ ** 3 IF (x > z) GOTO <dest>
+ */
+ int addr;
+ Expr *pLeft = pExpr->pLeft;
+ Expr *pRight = pExpr->pList->a[0].pExpr;
+ sqlite3ExprCode(pParse, pLeft);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ sqlite3ExprCode(pParse, pRight);
+ addr = sqlite3VdbeCurrentAddr(v);
+ codeCompare(pParse, pLeft, pRight, OP_Ge, addr+3, !jumpIfNull);
+
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, dest);
+ pRight = pExpr->pList->a[1].pExpr;
+ sqlite3ExprCode(pParse, pRight);
+ codeCompare(pParse, pLeft, pRight, OP_Gt, dest, jumpIfNull);
+ break;
+ }
+ default: {
+ sqlite3ExprCode(pParse, pExpr);
+ sqlite3VdbeAddOp(v, OP_IfNot, jumpIfNull, dest);
+ break;
+ }
+ }
+}
+
+/*
+** Do a deep comparison of two expression trees. Return TRUE (non-zero)
+** if they are identical and return FALSE if they differ in any way.
+*/
+int sqlite3ExprCompare(Expr *pA, Expr *pB){
+ int i;
+ if( pA==0 ){
+ return pB==0;
+ }else if( pB==0 ){
+ return 0;
+ }
+ if( pA->op!=pB->op ) return 0;
+ if( !sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 0;
+ if( !sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 0;
+ if( pA->pList ){
+ if( pB->pList==0 ) return 0;
+ if( pA->pList->nExpr!=pB->pList->nExpr ) return 0;
+ for(i=0; i<pA->pList->nExpr; i++){
+ if( !sqlite3ExprCompare(pA->pList->a[i].pExpr, pB->pList->a[i].pExpr) ){
+ return 0;
+ }
+ }
+ }else if( pB->pList ){
+ return 0;
+ }
+ if( pA->pSelect || pB->pSelect ) return 0;
+ if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 0;
+ if( pA->token.z ){
+ if( pB->token.z==0 ) return 0;
+ if( pB->token.n!=pA->token.n ) return 0;
+ if( sqlite3StrNICmp(pA->token.z, pB->token.z, pB->token.n)!=0 ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Add a new element to the pParse->aAgg[] array and return its index.
+*/
+static int appendAggInfo(Parse *pParse){
+ if( (pParse->nAgg & 0x7)==0 ){
+ int amt = pParse->nAgg + 8;
+ AggExpr *aAgg = sqliteRealloc(pParse->aAgg, amt*sizeof(pParse->aAgg[0]));
+ if( aAgg==0 ){
+ return -1;
+ }
+ pParse->aAgg = aAgg;
+ }
+ memset(&pParse->aAgg[pParse->nAgg], 0, sizeof(pParse->aAgg[0]));
+ return pParse->nAgg++;
+}
+
+/*
+** Analyze the given expression looking for aggregate functions and
+** for variables that need to be added to the pParse->aAgg[] array.
+** Make additional entries to the pParse->aAgg[] array as necessary.
+**
+** This routine should only be called after the expression has been
+** analyzed by sqlite3ExprResolveIds() and sqlite3ExprCheck().
+**
+** If errors are seen, leave an error message in zErrMsg and return
+** the number of errors.
+*/
+int sqlite3ExprAnalyzeAggregates(Parse *pParse, Expr *pExpr){
+ int i;
+ AggExpr *aAgg;
+ int nErr = 0;
+
+ if( pExpr==0 ) return 0;
+ switch( pExpr->op ){
+ case TK_COLUMN: {
+ aAgg = pParse->aAgg;
+ for(i=0; i<pParse->nAgg; i++){
+ if( aAgg[i].isAgg ) continue;
+ if( aAgg[i].pExpr->iTable==pExpr->iTable
+ && aAgg[i].pExpr->iColumn==pExpr->iColumn ){
+ break;
+ }
+ }
+ if( i>=pParse->nAgg ){
+ i = appendAggInfo(pParse);
+ if( i<0 ) return 1;
+ pParse->aAgg[i].isAgg = 0;
+ pParse->aAgg[i].pExpr = pExpr;
+ }
+ pExpr->iAgg = i;
+ break;
+ }
+ case TK_AGG_FUNCTION: {
+ aAgg = pParse->aAgg;
+ for(i=0; i<pParse->nAgg; i++){
+ if( !aAgg[i].isAgg ) continue;
+ if( sqlite3ExprCompare(aAgg[i].pExpr, pExpr) ){
+ break;
+ }
+ }
+ if( i>=pParse->nAgg ){
+ u8 enc = pParse->db->enc;
+ i = appendAggInfo(pParse);
+ if( i<0 ) return 1;
+ pParse->aAgg[i].isAgg = 1;
+ pParse->aAgg[i].pExpr = pExpr;
+ pParse->aAgg[i].pFunc = sqlite3FindFunction(pParse->db,
+ pExpr->token.z, pExpr->token.n,
+ pExpr->pList ? pExpr->pList->nExpr : 0, enc, 0);
+ }
+ pExpr->iAgg = i;
+ break;
+ }
+ default: {
+ if( pExpr->pLeft ){
+ nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pLeft);
+ }
+ if( nErr==0 && pExpr->pRight ){
+ nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pRight);
+ }
+ if( nErr==0 && pExpr->pList ){
+ int n = pExpr->pList->nExpr;
+ int i;
+ for(i=0; nErr==0 && i<n; i++){
+ nErr = sqlite3ExprAnalyzeAggregates(pParse, pExpr->pList->a[i].pExpr);
+ }
+ }
+ break;
+ }
+ }
+ return nErr;
+}
+
+/*
+** Locate a user function given a name, a number of arguments and a flag
+** indicating whether the function prefers UTF-16 over UTF-8. Return a
+** pointer to the FuncDef structure that defines that function, or return
+** NULL if the function does not exist.
+**
+** If the createFlag argument is true, then a new (blank) FuncDef
+** structure is created and liked into the "db" structure if a
+** no matching function previously existed. When createFlag is true
+** and the nArg parameter is -1, then only a function that accepts
+** any number of arguments will be returned.
+**
+** If createFlag is false and nArg is -1, then the first valid
+** function found is returned. A function is valid if either xFunc
+** or xStep is non-zero.
+**
+** If createFlag is false, then a function with the required name and
+** number of arguments may be returned even if the eTextRep flag does not
+** match that requested.
+*/
+FuncDef *sqlite3FindFunction(
+ sqlite3 *db, /* An open database */
+ const char *zName, /* Name of the function. Not null-terminated */
+ int nName, /* Number of characters in the name */
+ int nArg, /* Number of arguments. -1 means any number */
+ u8 enc, /* Preferred text encoding */
+ int createFlag /* Create new entry if true and does not otherwise exist */
+){
+ FuncDef *p; /* Iterator variable */
+ FuncDef *pFirst; /* First function with this name */
+ FuncDef *pBest = 0; /* Best match found so far */
+ int bestmatch = 0;
+
+
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
+ if( nArg<-1 ) nArg = -1;
+
+ pFirst = (FuncDef*)sqlite3HashFind(&db->aFunc, zName, nName);
+ for(p=pFirst; p; p=p->pNext){
+ /* During the search for the best function definition, bestmatch is set
+ ** as follows to indicate the quality of the match with the definition
+ ** pointed to by pBest:
+ **
+ ** 0: pBest is NULL. No match has been found.
+ ** 1: A variable arguments function that prefers UTF-8 when a UTF-16
+ ** encoding is requested, or vice versa.
+ ** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
+ ** requested, or vice versa.
+ ** 3: A variable arguments function using the same text encoding.
+ ** 4: A function with the exact number of arguments requested that
+ ** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
+ ** 5: A function with the exact number of arguments requested that
+ ** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
+ ** 6: An exact match.
+ **
+ ** A larger value of 'matchqual' indicates a more desirable match.
+ */
+ if( p->nArg==-1 || p->nArg==nArg || nArg==-1 ){
+ int match = 1; /* Quality of this match */
+ if( p->nArg==nArg || nArg==-1 ){
+ match = 4;
+ }
+ if( enc==p->iPrefEnc ){
+ match += 2;
+ }
+ else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
+ (enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
+ match += 1;
+ }
+
+ if( match>bestmatch ){
+ pBest = p;
+ bestmatch = match;
+ }
+ }
+ }
+
+ /* If the createFlag parameter is true, and the seach did not reveal an
+ ** exact match for the name, number of arguments and encoding, then add a
+ ** new entry to the hash table and return it.
+ */
+ if( createFlag && bestmatch<6 &&
+ (pBest = sqliteMalloc(sizeof(*pBest)+nName+1)) ){
+ pBest->nArg = nArg;
+ pBest->pNext = pFirst;
+ pBest->zName = (char*)&pBest[1];
+ pBest->iPrefEnc = enc;
+ memcpy(pBest->zName, zName, nName);
+ pBest->zName[nName] = 0;
+ sqlite3HashInsert(&db->aFunc, pBest->zName, nName, (void*)pBest);
+ }
+
+ if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
+ return pBest;
+ }
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/func.c b/kopete/plugins/statistics/sqlite/func.c
new file mode 100644
index 00000000..f61bdae3
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/func.c
@@ -0,0 +1,1018 @@
+/*
+** 2002 February 23
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains the C functions that implement various SQL
+** functions of SQLite.
+**
+** There is only one exported symbol in this file - the function
+** sqliteRegisterBuildinFunctions() found at the bottom of the file.
+** All other code has file scope.
+**
+** $Id$
+*/
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+#include "os.h"
+
+static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
+ return context->pColl;
+}
+
+/*
+** Implementation of the non-aggregate min() and max() functions
+*/
+static void minmaxFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ int mask; /* 0 for min() or 0xffffffff for max() */
+ int iBest;
+ CollSeq *pColl;
+
+ if( argc==0 ) return;
+ mask = sqlite3_user_data(context)==0 ? 0 : -1;
+ pColl = sqlite3GetFuncCollSeq(context);
+ assert( pColl );
+ assert( mask==-1 || mask==0 );
+ iBest = 0;
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ for(i=1; i<argc; i++){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) return;
+ if( (sqlite3MemCompare(argv[iBest], argv[i], pColl)^mask)>=0 ){
+ iBest = i;
+ }
+ }
+ sqlite3_result_value(context, argv[iBest]);
+}
+
+/*
+** Return the type of the argument.
+*/
+static void typeofFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *z = 0;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: z = "null"; break;
+ case SQLITE_INTEGER: z = "integer"; break;
+ case SQLITE_TEXT: z = "text"; break;
+ case SQLITE_FLOAT: z = "real"; break;
+ case SQLITE_BLOB: z = "blob"; break;
+ }
+ sqlite3_result_text(context, z, -1, SQLITE_STATIC);
+}
+
+/*
+** Implementation of the length() function
+*/
+static void lengthFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int len;
+
+ assert( argc==1 );
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_BLOB:
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ sqlite3_result_int(context, sqlite3_value_bytes(argv[0]));
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *z = sqlite3_value_text(argv[0]);
+ for(len=0; *z; z++){ if( (0xc0&*z)!=0x80 ) len++; }
+ sqlite3_result_int(context, len);
+ break;
+ }
+ default: {
+ sqlite3_result_null(context);
+ break;
+ }
+ }
+}
+
+/*
+** Implementation of the abs() function
+*/
+static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ assert( argc==1 );
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_INTEGER: {
+ i64 iVal = sqlite3_value_int64(argv[0]);
+ if( iVal<0 ) iVal = iVal * -1;
+ sqlite3_result_int64(context, iVal);
+ break;
+ }
+ case SQLITE_NULL: {
+ sqlite3_result_null(context);
+ break;
+ }
+ default: {
+ double rVal = sqlite3_value_double(argv[0]);
+ if( rVal<0 ) rVal = rVal * -1.0;
+ sqlite3_result_double(context, rVal);
+ break;
+ }
+ }
+}
+
+/*
+** Implementation of the substr() function
+*/
+static void substrFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *z;
+ const char *z2;
+ int i;
+ int p1, p2, len;
+
+ assert( argc==3 );
+ z = sqlite3_value_text(argv[0]);
+ if( z==0 ) return;
+ p1 = sqlite3_value_int(argv[1]);
+ p2 = sqlite3_value_int(argv[2]);
+ for(len=0, z2=z; *z2; z2++){ if( (0xc0&*z2)!=0x80 ) len++; }
+ if( p1<0 ){
+ p1 += len;
+ if( p1<0 ){
+ p2 += p1;
+ p1 = 0;
+ }
+ }else if( p1>0 ){
+ p1--;
+ }
+ if( p1+p2>len ){
+ p2 = len-p1;
+ }
+ for(i=0; i<p1 && z[i]; i++){
+ if( (z[i]&0xc0)==0x80 ) p1++;
+ }
+ while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p1++; }
+ for(; i<p1+p2 && z[i]; i++){
+ if( (z[i]&0xc0)==0x80 ) p2++;
+ }
+ while( z[i] && (z[i]&0xc0)==0x80 ){ i++; p2++; }
+ if( p2<0 ) p2 = 0;
+ sqlite3_result_text(context, &z[p1], p2, SQLITE_TRANSIENT);
+}
+
+/*
+** Implementation of the round() function
+*/
+static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ int n = 0;
+ double r;
+ char zBuf[100];
+ assert( argc==1 || argc==2 );
+ if( argc==2 ){
+ if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
+ n = sqlite3_value_int(argv[1]);
+ if( n>30 ) n = 30;
+ if( n<0 ) n = 0;
+ }
+ if( SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
+ r = sqlite3_value_double(argv[0]);
+ sprintf(zBuf,"%.*f",n,r);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+}
+
+/*
+** Implementation of the upper() and lower() SQL functions.
+*/
+static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ unsigned char *z;
+ int i;
+ if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
+ z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
+ if( z==0 ) return;
+ strcpy(z, sqlite3_value_text(argv[0]));
+ for(i=0; z[i]; i++){
+ z[i] = toupper(z[i]);
+ }
+ sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
+ sqliteFree(z);
+}
+static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ unsigned char *z;
+ int i;
+ if( argc<1 || SQLITE_NULL==sqlite3_value_type(argv[0]) ) return;
+ z = sqliteMalloc(sqlite3_value_bytes(argv[0])+1);
+ if( z==0 ) return;
+ strcpy(z, sqlite3_value_text(argv[0]));
+ for(i=0; z[i]; i++){
+ z[i] = tolower(z[i]);
+ }
+ sqlite3_result_text(context, z, -1, SQLITE_TRANSIENT);
+ sqliteFree(z);
+}
+
+/*
+** Implementation of the IFNULL(), NVL(), and COALESCE() functions.
+** All three do the same thing. They return the first non-NULL
+** argument.
+*/
+static void ifnullFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ for(i=0; i<argc; i++){
+ if( SQLITE_NULL!=sqlite3_value_type(argv[i]) ){
+ sqlite3_result_value(context, argv[i]);
+ break;
+ }
+ }
+}
+
+/*
+** Implementation of random(). Return a random integer.
+*/
+static void randomFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int r;
+ sqlite3Randomness(sizeof(r), &r);
+ sqlite3_result_int(context, r);
+}
+
+/*
+** Implementation of the last_insert_rowid() SQL function. The return
+** value is the same as the sqlite3_last_insert_rowid() API function.
+*/
+static void last_insert_rowid(
+ sqlite3_context *context,
+ int arg,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_user_data(context);
+ sqlite3_result_int64(context, sqlite3_last_insert_rowid(db));
+}
+
+/*
+** Implementation of the changes() SQL function. The return value is the
+** same as the sqlite3_changes() API function.
+*/
+static void changes(
+ sqlite3_context *context,
+ int arg,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_user_data(context);
+ sqlite3_result_int(context, sqlite3_changes(db));
+}
+
+/*
+** Implementation of the total_changes() SQL function. The return value is
+** the same as the sqlite3_total_changes() API function.
+*/
+static void total_changes(
+ sqlite3_context *context,
+ int arg,
+ sqlite3_value **argv
+){
+ sqlite3 *db = sqlite3_user_data(context);
+ sqlite3_result_int(context, sqlite3_total_changes(db));
+}
+
+/*
+** A structure defining how to do GLOB-style comparisons.
+*/
+struct compareInfo {
+ u8 matchAll;
+ u8 matchOne;
+ u8 matchSet;
+ u8 noCase;
+};
+static const struct compareInfo globInfo = { '*', '?', '[', 0 };
+static const struct compareInfo likeInfo = { '%', '_', 0, 1 };
+
+/*
+** X is a pointer to the first byte of a UTF-8 character. Increment
+** X so that it points to the next character. This only works right
+** if X points to a well-formed UTF-8 string.
+*/
+#define sqliteNextChar(X) while( (0xc0&*++(X))==0x80 ){}
+#define sqliteCharVal(X) sqlite3ReadUtf8(X)
+
+
+/*
+** Compare two UTF-8 strings for equality where the first string can
+** potentially be a "glob" expression. Return true (1) if they
+** are the same and false (0) if they are different.
+**
+** Globbing rules:
+**
+** '*' Matches any sequence of zero or more characters.
+**
+** '?' Matches exactly one character.
+**
+** [...] Matches one character from the enclosed list of
+** characters.
+**
+** [^...] Matches one character not in the enclosed list.
+**
+** With the [...] and [^...] matching, a ']' character can be included
+** in the list by making it the first character after '[' or '^'. A
+** range of characters can be specified using '-'. Example:
+** "[a-z]" matches any single lower-case letter. To match a '-', make
+** it the last character in the list.
+**
+** This routine is usually quick, but can be N**2 in the worst case.
+**
+** Hints: to match '*' or '?', put them in "[]". Like this:
+**
+** abc[*]xyz Matches "abc*xyz" only
+*/
+int patternCompare(
+ const u8 *zPattern, /* The glob pattern */
+ const u8 *zString, /* The string to compare against the glob */
+ const struct compareInfo *pInfo /* Information about how to do the compare */
+){
+ register int c;
+ int invert;
+ int seen;
+ int c2;
+ u8 matchOne = pInfo->matchOne;
+ u8 matchAll = pInfo->matchAll;
+ u8 matchSet = pInfo->matchSet;
+ u8 noCase = pInfo->noCase;
+
+ while( (c = *zPattern)!=0 ){
+ if( c==matchAll ){
+ while( (c=zPattern[1]) == matchAll || c == matchOne ){
+ if( c==matchOne ){
+ if( *zString==0 ) return 0;
+ sqliteNextChar(zString);
+ }
+ zPattern++;
+ }
+ if( c==0 ) return 1;
+ if( c==matchSet ){
+ while( *zString && patternCompare(&zPattern[1],zString,pInfo)==0 ){
+ sqliteNextChar(zString);
+ }
+ return *zString!=0;
+ }else{
+ while( (c2 = *zString)!=0 ){
+ if( noCase ){
+ c2 = sqlite3UpperToLower[c2];
+ c = sqlite3UpperToLower[c];
+ while( c2 != 0 && c2 != c ){ c2 = sqlite3UpperToLower[*++zString]; }
+ }else{
+ while( c2 != 0 && c2 != c ){ c2 = *++zString; }
+ }
+ if( c2==0 ) return 0;
+ if( patternCompare(&zPattern[1],zString,pInfo) ) return 1;
+ sqliteNextChar(zString);
+ }
+ return 0;
+ }
+ }else if( c==matchOne ){
+ if( *zString==0 ) return 0;
+ sqliteNextChar(zString);
+ zPattern++;
+ }else if( c==matchSet ){
+ int prior_c = 0;
+ seen = 0;
+ invert = 0;
+ c = sqliteCharVal(zString);
+ if( c==0 ) return 0;
+ c2 = *++zPattern;
+ if( c2=='^' ){ invert = 1; c2 = *++zPattern; }
+ if( c2==']' ){
+ if( c==']' ) seen = 1;
+ c2 = *++zPattern;
+ }
+ while( (c2 = sqliteCharVal(zPattern))!=0 && c2!=']' ){
+ if( c2=='-' && zPattern[1]!=']' && zPattern[1]!=0 && prior_c>0 ){
+ zPattern++;
+ c2 = sqliteCharVal(zPattern);
+ if( c>=prior_c && c<=c2 ) seen = 1;
+ prior_c = 0;
+ }else if( c==c2 ){
+ seen = 1;
+ prior_c = c2;
+ }else{
+ prior_c = c2;
+ }
+ sqliteNextChar(zPattern);
+ }
+ if( c2==0 || (seen ^ invert)==0 ) return 0;
+ sqliteNextChar(zString);
+ zPattern++;
+ }else{
+ if( noCase ){
+ if( sqlite3UpperToLower[c] != sqlite3UpperToLower[*zString] ) return 0;
+ }else{
+ if( c != *zString ) return 0;
+ }
+ zPattern++;
+ zString++;
+ }
+ }
+ return *zString==0;
+}
+
+
+/*
+** Implementation of the like() SQL function. This function implements
+** the build-in LIKE operator. The first argument to the function is the
+** pattern and the second argument is the string. So, the SQL statements:
+**
+** A LIKE B
+**
+** is implemented as like(B,A).
+**
+** If the pointer retrieved by via a call to sqlite3_user_data() is
+** not NULL, then this function uses UTF-16. Otherwise UTF-8.
+*/
+static void likeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *zA = sqlite3_value_text(argv[0]);
+ const unsigned char *zB = sqlite3_value_text(argv[1]);
+ if( zA && zB ){
+ sqlite3_result_int(context, patternCompare(zA, zB, &likeInfo));
+ }
+}
+
+/*
+** Implementation of the glob() SQL function. This function implements
+** the build-in GLOB operator. The first argument to the function is the
+** string and the second argument is the pattern. So, the SQL statements:
+**
+** A GLOB B
+**
+** is implemented as glob(A,B).
+*/
+static void globFunc(sqlite3_context *context, int arg, sqlite3_value **argv){
+ const unsigned char *zA = sqlite3_value_text(argv[0]);
+ const unsigned char *zB = sqlite3_value_text(argv[1]);
+ if( zA && zB ){
+ sqlite3_result_int(context, patternCompare(zA, zB, &globInfo));
+ }
+}
+
+/*
+** Implementation of the NULLIF(x,y) function. The result is the first
+** argument if the arguments are different. The result is NULL if the
+** arguments are equal to each other.
+*/
+static void nullifFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ CollSeq *pColl = sqlite3GetFuncCollSeq(context);
+ if( sqlite3MemCompare(argv[0], argv[1], pColl)!=0 ){
+ sqlite3_result_value(context, argv[0]);
+ }
+}
+
+/*
+** Implementation of the VERSION(*) function. The result is the version
+** of the SQLite library that is running.
+*/
+static void versionFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC);
+}
+
+/*
+** EXPERIMENTAL - This is not an official function. The interface may
+** change. This function may disappear. Do not write code that depends
+** on this function.
+**
+** Implementation of the QUOTE() function. This function takes a single
+** argument. If the argument is numeric, the return value is the same as
+** the argument. If the argument is NULL, the return value is the string
+** "NULL". Otherwise, the argument is enclosed in single quotes with
+** single-quote escapes.
+*/
+static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ if( argc<1 ) return;
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: {
+ sqlite3_result_text(context, "NULL", 4, SQLITE_STATIC);
+ break;
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ sqlite3_result_value(context, argv[0]);
+ break;
+ }
+ case SQLITE_BLOB: {
+ static const char hexdigits[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+ char *zText = 0;
+ int nBlob = sqlite3_value_bytes(argv[0]);
+ char const *zBlob = sqlite3_value_blob(argv[0]);
+
+ zText = (char *)sqliteMalloc((2*nBlob)+4);
+ if( !zText ){
+ sqlite3_result_error(context, "out of memory", -1);
+ }else{
+ int i;
+ for(i=0; i<nBlob; i++){
+ zText[(i*2)+2] = hexdigits[(zBlob[i]>>4)&0x0F];
+ zText[(i*2)+3] = hexdigits[(zBlob[i])&0x0F];
+ }
+ zText[(nBlob*2)+2] = '\'';
+ zText[(nBlob*2)+3] = '\0';
+ zText[0] = 'X';
+ zText[1] = '\'';
+ sqlite3_result_text(context, zText, -1, SQLITE_TRANSIENT);
+ sqliteFree(zText);
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ int i,j,n;
+ const char *zArg = sqlite3_value_text(argv[0]);
+ char *z;
+
+ for(i=n=0; zArg[i]; i++){ if( zArg[i]=='\'' ) n++; }
+ z = sqliteMalloc( i+n+3 );
+ if( z==0 ) return;
+ z[0] = '\'';
+ for(i=0, j=1; zArg[i]; i++){
+ z[j++] = zArg[i];
+ if( zArg[i]=='\'' ){
+ z[j++] = '\'';
+ }
+ }
+ z[j++] = '\'';
+ z[j] = 0;
+ sqlite3_result_text(context, z, j, SQLITE_TRANSIENT);
+ sqliteFree(z);
+ }
+ }
+}
+
+#ifdef SQLITE_SOUNDEX
+/*
+** Compute the soundex encoding of a word.
+*/
+static void soundexFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
+ char zResult[8];
+ const u8 *zIn;
+ int i, j;
+ static const unsigned char iCode[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
+ 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 0, 1, 2, 0, 0, 2, 2, 4, 5, 5, 0,
+ 1, 2, 6, 2, 3, 0, 1, 0, 2, 0, 2, 0, 0, 0, 0, 0,
+ };
+ assert( argc==1 );
+ zIn = (u8*)sqlite3_value_text(argv[0]);
+ for(i=0; zIn[i] && !isalpha(zIn[i]); i++){}
+ if( zIn[i] ){
+ zResult[0] = toupper(zIn[i]);
+ for(j=1; j<4 && zIn[i]; i++){
+ int code = iCode[zIn[i]&0x7f];
+ if( code>0 ){
+ zResult[j++] = code + '0';
+ }
+ }
+ while( j<4 ){
+ zResult[j++] = '0';
+ }
+ zResult[j] = 0;
+ sqlite3_result_text(context, zResult, 4, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_text(context, "?000", 4, SQLITE_STATIC);
+ }
+}
+#endif
+
+#ifdef SQLITE_TEST
+/*
+** This function generates a string of random characters. Used for
+** generating test data.
+*/
+static void randStr(sqlite3_context *context, int argc, sqlite3_value **argv){
+ static const unsigned char zSrc[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ ".-!,:*^+=_|?/<> ";
+ int iMin, iMax, n, r, i;
+ unsigned char zBuf[1000];
+ if( argc>=1 ){
+ iMin = sqlite3_value_int(argv[0]);
+ if( iMin<0 ) iMin = 0;
+ if( iMin>=sizeof(zBuf) ) iMin = sizeof(zBuf)-1;
+ }else{
+ iMin = 1;
+ }
+ if( argc>=2 ){
+ iMax = sqlite3_value_int(argv[1]);
+ if( iMax<iMin ) iMax = iMin;
+ if( iMax>=sizeof(zBuf) ) iMax = sizeof(zBuf)-1;
+ }else{
+ iMax = 50;
+ }
+ n = iMin;
+ if( iMax>iMin ){
+ sqlite3Randomness(sizeof(r), &r);
+ r &= 0x7fffffff;
+ n += r%(iMax + 1 - iMin);
+ }
+ assert( n<sizeof(zBuf) );
+ sqlite3Randomness(n, zBuf);
+ for(i=0; i<n; i++){
+ zBuf[i] = zSrc[zBuf[i]%(sizeof(zSrc)-1)];
+ }
+ zBuf[n] = 0;
+ sqlite3_result_text(context, zBuf, n, SQLITE_TRANSIENT);
+}
+#endif /* SQLITE_TEST */
+
+#ifdef SQLITE_TEST
+/*
+** The following two SQL functions are used to test returning a text
+** result with a destructor. Function 'test_destructor' takes one argument
+** and returns the same argument interpreted as TEXT. A destructor is
+** passed with the sqlite3_result_text() call.
+**
+** SQL function 'test_destructor_count' returns the number of outstanding
+** allocations made by 'test_destructor';
+**
+** WARNING: Not threadsafe.
+*/
+static int test_destructor_count_var = 0;
+static void destructor(void *p){
+ char *zVal = (char *)p;
+ assert(zVal);
+ zVal--;
+ sqliteFree(zVal);
+ test_destructor_count_var--;
+}
+static void test_destructor(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ char *zVal;
+ int len;
+ sqlite3 *db = sqlite3_user_data(pCtx);
+
+ test_destructor_count_var++;
+ assert( nArg==1 );
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ len = sqlite3ValueBytes(argv[0], db->enc);
+ zVal = sqliteMalloc(len+3);
+ zVal[len] = 0;
+ zVal[len-1] = 0;
+ assert( zVal );
+ zVal++;
+ memcpy(zVal, sqlite3ValueText(argv[0], db->enc), len);
+ if( db->enc==SQLITE_UTF8 ){
+ sqlite3_result_text(pCtx, zVal, -1, destructor);
+ }else if( db->enc==SQLITE_UTF16LE ){
+ sqlite3_result_text16le(pCtx, zVal, -1, destructor);
+ }else{
+ sqlite3_result_text16be(pCtx, zVal, -1, destructor);
+ }
+}
+static void test_destructor_count(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ sqlite3_result_int(pCtx, test_destructor_count_var);
+}
+#endif /* SQLITE_TEST */
+
+#ifdef SQLITE_TEST
+/*
+** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata()
+** interface.
+**
+** The test_auxdata() SQL function attempts to register each of its arguments
+** as auxiliary data. If there are no prior registrations of aux data for
+** that argument (meaning the argument is not a constant or this is its first
+** call) then the result for that argument is 0. If there is a prior
+** registration, the result for that argument is 1. The overall result
+** is the individual argument results separated by spaces.
+*/
+static void free_test_auxdata(void *p) {sqliteFree(p);}
+static void test_auxdata(
+ sqlite3_context *pCtx,
+ int nArg,
+ sqlite3_value **argv
+){
+ int i;
+ char *zRet = sqliteMalloc(nArg*2);
+ if( !zRet ) return;
+ for(i=0; i<nArg; i++){
+ char const *z = sqlite3_value_text(argv[i]);
+ if( z ){
+ char *zAux = sqlite3_get_auxdata(pCtx, i);
+ if( zAux ){
+ zRet[i*2] = '1';
+ if( strcmp(zAux, z) ){
+ sqlite3_result_error(pCtx, "Auxilary data corruption", -1);
+ return;
+ }
+ }else{
+ zRet[i*2] = '0';
+ zAux = sqliteStrDup(z);
+ sqlite3_set_auxdata(pCtx, i, zAux, free_test_auxdata);
+ }
+ zRet[i*2+1] = ' ';
+ }
+ }
+ sqlite3_result_text(pCtx, zRet, 2*nArg-1, free_test_auxdata);
+}
+#endif /* SQLITE_TEST */
+
+/*
+** An instance of the following structure holds the context of a
+** sum() or avg() aggregate computation.
+*/
+typedef struct SumCtx SumCtx;
+struct SumCtx {
+ double sum; /* Sum of terms */
+ int cnt; /* Number of elements summed */
+};
+
+/*
+** Routines used to compute the sum or average.
+*/
+static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
+ SumCtx *p;
+ if( argc<1 ) return;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p && SQLITE_NULL!=sqlite3_value_type(argv[0]) ){
+ p->sum += sqlite3_value_double(argv[0]);
+ p->cnt++;
+ }
+}
+static void sumFinalize(sqlite3_context *context){
+ SumCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ sqlite3_result_double(context, p ? p->sum : 0.0);
+}
+static void avgFinalize(sqlite3_context *context){
+ SumCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p && p->cnt>0 ){
+ sqlite3_result_double(context, p->sum/(double)p->cnt);
+ }
+}
+
+/*
+** An instance of the following structure holds the context of a
+** variance or standard deviation computation.
+*/
+typedef struct StdDevCtx StdDevCtx;
+struct StdDevCtx {
+ double sum; /* Sum of terms */
+ double sum2; /* Sum of the squares of terms */
+ int cnt; /* Number of terms counted */
+};
+
+#if 0 /* Omit because math library is required */
+/*
+** Routines used to compute the standard deviation as an aggregate.
+*/
+static void stdDevStep(sqlite3_context *context, int argc, const char **argv){
+ StdDevCtx *p;
+ double x;
+ if( argc<1 ) return;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p && argv[0] ){
+ x = sqlite3AtoF(argv[0], 0);
+ p->sum += x;
+ p->sum2 += x*x;
+ p->cnt++;
+ }
+}
+static void stdDevFinalize(sqlite3_context *context){
+ double rN = sqlite3_aggregate_count(context);
+ StdDevCtx *p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( p && p->cnt>1 ){
+ double rCnt = cnt;
+ sqlite3_set_result_double(context,
+ sqrt((p->sum2 - p->sum*p->sum/rCnt)/(rCnt-1.0)));
+ }
+}
+#endif
+
+/*
+** The following structure keeps track of state information for the
+** count() aggregate function.
+*/
+typedef struct CountCtx CountCtx;
+struct CountCtx {
+ int n;
+};
+
+/*
+** Routines to implement the count() aggregate function.
+*/
+static void countStep(sqlite3_context *context, int argc, sqlite3_value **argv){
+ CountCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ if( (argc==0 || SQLITE_NULL!=sqlite3_value_type(argv[0])) && p ){
+ p->n++;
+ }
+}
+static void countFinalize(sqlite3_context *context){
+ CountCtx *p;
+ p = sqlite3_aggregate_context(context, sizeof(*p));
+ sqlite3_result_int(context, p ? p->n : 0);
+}
+
+/*
+** This function tracks state information for the min() and max()
+** aggregate functions.
+*/
+typedef struct MinMaxCtx MinMaxCtx;
+struct MinMaxCtx {
+ char *z; /* The best so far */
+ char zBuf[28]; /* Space that can be used for storage */
+};
+
+/*
+** Routines to implement min() and max() aggregate functions.
+*/
+static void minmaxStep(sqlite3_context *context, int argc, sqlite3_value **argv){
+ Mem *pArg = (Mem *)argv[0];
+ Mem *pBest;
+
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
+ pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
+ if( !pBest ) return;
+
+ if( pBest->flags ){
+ int max;
+ int cmp;
+ CollSeq *pColl = sqlite3GetFuncCollSeq(context);
+ /* This step function is used for both the min() and max() aggregates,
+ ** the only difference between the two being that the sense of the
+ ** comparison is inverted. For the max() aggregate, the
+ ** sqlite3_user_data() function returns (void *)-1. For min() it
+ ** returns (void *)db, where db is the sqlite3* database pointer.
+ ** Therefore the next statement sets variable 'max' to 1 for the max()
+ ** aggregate, or 0 for min().
+ */
+ max = ((sqlite3_user_data(context)==(void *)-1)?1:0);
+ cmp = sqlite3MemCompare(pBest, pArg, pColl);
+ if( (max && cmp<0) || (!max && cmp>0) ){
+ sqlite3VdbeMemCopy(pBest, pArg);
+ }
+ }else{
+ sqlite3VdbeMemCopy(pBest, pArg);
+ }
+}
+static void minMaxFinalize(sqlite3_context *context){
+ sqlite3_value *pRes;
+ pRes = (sqlite3_value *)sqlite3_aggregate_context(context, sizeof(Mem));
+ if( pRes->flags ){
+ sqlite3_result_value(context, pRes);
+ }
+ sqlite3VdbeMemRelease(pRes);
+}
+
+
+/*
+** This function registered all of the above C functions as SQL
+** functions. This should be the only routine in this file with
+** external linkage.
+*/
+void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
+ static const struct {
+ char *zName;
+ signed char nArg;
+ u8 argType; /* 0: none. 1: db 2: (-1) */
+ u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */
+ u8 needCollSeq;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value **);
+ } aFuncs[] = {
+ { "min", -1, 0, SQLITE_UTF8, 1, minmaxFunc },
+ { "min", 0, 0, SQLITE_UTF8, 1, 0 },
+ { "max", -1, 2, SQLITE_UTF8, 1, minmaxFunc },
+ { "max", 0, 2, SQLITE_UTF8, 1, 0 },
+ { "typeof", 1, 0, SQLITE_UTF8, 0, typeofFunc },
+ { "length", 1, 0, SQLITE_UTF8, 0, lengthFunc },
+ { "substr", 3, 0, SQLITE_UTF8, 0, substrFunc },
+ { "substr", 3, 0, SQLITE_UTF16LE, 0, sqlite3utf16Substr },
+ { "abs", 1, 0, SQLITE_UTF8, 0, absFunc },
+ { "round", 1, 0, SQLITE_UTF8, 0, roundFunc },
+ { "round", 2, 0, SQLITE_UTF8, 0, roundFunc },
+ { "upper", 1, 0, SQLITE_UTF8, 0, upperFunc },
+ { "lower", 1, 0, SQLITE_UTF8, 0, lowerFunc },
+ { "coalesce", -1, 0, SQLITE_UTF8, 0, ifnullFunc },
+ { "coalesce", 0, 0, SQLITE_UTF8, 0, 0 },
+ { "coalesce", 1, 0, SQLITE_UTF8, 0, 0 },
+ { "ifnull", 2, 0, SQLITE_UTF8, 1, ifnullFunc },
+ { "random", -1, 0, SQLITE_UTF8, 0, randomFunc },
+ { "like", 2, 0, SQLITE_UTF8, 0, likeFunc },
+ { "glob", 2, 0, SQLITE_UTF8, 0, globFunc },
+ { "nullif", 2, 0, SQLITE_UTF8, 1, nullifFunc },
+ { "sqlite_version", 0, 0, SQLITE_UTF8, 0, versionFunc},
+ { "quote", 1, 0, SQLITE_UTF8, 0, quoteFunc },
+ { "last_insert_rowid", 0, 1, SQLITE_UTF8, 0, last_insert_rowid },
+ { "changes", 0, 1, SQLITE_UTF8, 0, changes },
+ { "total_changes", 0, 1, SQLITE_UTF8, 0, total_changes },
+#ifdef SQLITE_SOUNDEX
+ { "soundex", 1, 0, SQLITE_UTF8, 0, soundexFunc},
+#endif
+#ifdef SQLITE_TEST
+ { "randstr", 2, 0, SQLITE_UTF8, 0, randStr },
+ { "test_destructor", 1, 1, SQLITE_UTF8, 0, test_destructor},
+ { "test_destructor_count", 0, 0, SQLITE_UTF8, 0, test_destructor_count},
+ { "test_auxdata", -1, 0, SQLITE_UTF8, 0, test_auxdata},
+#endif
+ };
+ static const struct {
+ char *zName;
+ signed char nArg;
+ u8 argType;
+ u8 needCollSeq;
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
+ void (*xFinalize)(sqlite3_context*);
+ } aAggs[] = {
+ { "min", 1, 0, 1, minmaxStep, minMaxFinalize },
+ { "max", 1, 2, 1, minmaxStep, minMaxFinalize },
+ { "sum", 1, 0, 0, sumStep, sumFinalize },
+ { "avg", 1, 0, 0, sumStep, avgFinalize },
+ { "count", 0, 0, 0, countStep, countFinalize },
+ { "count", 1, 0, 0, countStep, countFinalize },
+#if 0
+ { "stddev", 1, 0, stdDevStep, stdDevFinalize },
+#endif
+ };
+ int i;
+
+ for(i=0; i<sizeof(aFuncs)/sizeof(aFuncs[0]); i++){
+ void *pArg = 0;
+ switch( aFuncs[i].argType ){
+ case 1: pArg = db; break;
+ case 2: pArg = (void *)(-1); break;
+ }
+ sqlite3_create_function(db, aFuncs[i].zName, aFuncs[i].nArg,
+ aFuncs[i].eTextRep, pArg, aFuncs[i].xFunc, 0, 0);
+ if( aFuncs[i].needCollSeq ){
+ FuncDef *pFunc = sqlite3FindFunction(db, aFuncs[i].zName,
+ strlen(aFuncs[i].zName), aFuncs[i].nArg, aFuncs[i].eTextRep, 0);
+ if( pFunc && aFuncs[i].needCollSeq ){
+ pFunc->needCollSeq = 1;
+ }
+ }
+ }
+ for(i=0; i<sizeof(aAggs)/sizeof(aAggs[0]); i++){
+ void *pArg = 0;
+ switch( aAggs[i].argType ){
+ case 1: pArg = db; break;
+ case 2: pArg = (void *)(-1); break;
+ }
+ sqlite3_create_function(db, aAggs[i].zName, aAggs[i].nArg, SQLITE_UTF8,
+ pArg, 0, aAggs[i].xStep, aAggs[i].xFinalize);
+ if( aAggs[i].needCollSeq ){
+ FuncDef *pFunc = sqlite3FindFunction( db, aAggs[i].zName,
+ strlen(aAggs[i].zName), aAggs[i].nArg, SQLITE_UTF8, 0);
+ if( pFunc && aAggs[i].needCollSeq ){
+ pFunc->needCollSeq = 1;
+ }
+ }
+ }
+ sqlite3RegisterDateTimeFunctions(db);
+}
diff --git a/kopete/plugins/statistics/sqlite/hash.c b/kopete/plugins/statistics/sqlite/hash.c
new file mode 100644
index 00000000..23e2e197
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/hash.c
@@ -0,0 +1,380 @@
+/*
+** 2001 September 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the implementation of generic hash-tables
+** used in SQLite.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include <assert.h>
+
+/* Turn bulk memory into a hash table object by initializing the
+** fields of the Hash structure.
+**
+** "pNew" is a pointer to the hash table that is to be initialized.
+** keyClass is one of the constants SQLITE_HASH_INT, SQLITE_HASH_POINTER,
+** SQLITE_HASH_BINARY, or SQLITE_HASH_STRING. The value of keyClass
+** determines what kind of key the hash table will use. "copyKey" is
+** true if the hash table should make its own private copy of keys and
+** false if it should just use the supplied pointer. CopyKey only makes
+** sense for SQLITE_HASH_STRING and SQLITE_HASH_BINARY and is ignored
+** for other key classes.
+*/
+void sqlite3HashInit(Hash *pNew, int keyClass, int copyKey){
+ assert( pNew!=0 );
+ assert( keyClass>=SQLITE_HASH_STRING && keyClass<=SQLITE_HASH_BINARY );
+ pNew->keyClass = keyClass;
+#if 0
+ if( keyClass==SQLITE_HASH_POINTER || keyClass==SQLITE_HASH_INT ) copyKey = 0;
+#endif
+ pNew->copyKey = copyKey;
+ pNew->first = 0;
+ pNew->count = 0;
+ pNew->htsize = 0;
+ pNew->ht = 0;
+}
+
+/* Remove all entries from a hash table. Reclaim all memory.
+** Call this routine to delete a hash table or to reset a hash table
+** to the empty state.
+*/
+void sqlite3HashClear(Hash *pH){
+ HashElem *elem; /* For looping over all elements of the table */
+
+ assert( pH!=0 );
+ elem = pH->first;
+ pH->first = 0;
+ if( pH->ht ) sqliteFree(pH->ht);
+ pH->ht = 0;
+ pH->htsize = 0;
+ while( elem ){
+ HashElem *next_elem = elem->next;
+ if( pH->copyKey && elem->pKey ){
+ sqliteFree(elem->pKey);
+ }
+ sqliteFree(elem);
+ elem = next_elem;
+ }
+ pH->count = 0;
+}
+
+#if 0 /* NOT USED */
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_INT
+*/
+static int intHash(const void *pKey, int nKey){
+ return nKey ^ (nKey<<8) ^ (nKey>>8);
+}
+static int intCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ return n2 - n1;
+}
+#endif
+
+#if 0 /* NOT USED */
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_POINTER
+*/
+static int ptrHash(const void *pKey, int nKey){
+ uptr x = Addr(pKey);
+ return x ^ (x<<8) ^ (x>>8);
+}
+static int ptrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( pKey1==pKey2 ) return 0;
+ if( pKey1<pKey2 ) return -1;
+ return 1;
+}
+#endif
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_STRING
+*/
+static int strHash(const void *pKey, int nKey){
+ return sqlite3HashNoCase((const char*)pKey, nKey);
+}
+static int strCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return sqlite3StrNICmp((const char*)pKey1,(const char*)pKey2,n1);
+}
+
+/*
+** Hash and comparison functions when the mode is SQLITE_HASH_BINARY
+*/
+static int binHash(const void *pKey, int nKey){
+ int h = 0;
+ const char *z = (const char *)pKey;
+ while( nKey-- > 0 ){
+ h = (h<<3) ^ h ^ *(z++);
+ }
+ return h & 0x7fffffff;
+}
+static int binCompare(const void *pKey1, int n1, const void *pKey2, int n2){
+ if( n1!=n2 ) return 1;
+ return memcmp(pKey1,pKey2,n1);
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** The C syntax in this function definition may be unfamilar to some
+** programmers, so we provide the following additional explanation:
+**
+** The name of the function is "hashFunction". The function takes a
+** single parameter "keyClass". The return value of hashFunction()
+** is a pointer to another function. Specifically, the return value
+** of hashFunction() is a pointer to a function that takes two parameters
+** with types "const void*" and "int" and returns an "int".
+*/
+static int (*hashFunction(int keyClass))(const void*,int){
+#if 0 /* HASH_INT and HASH_POINTER are never used */
+ switch( keyClass ){
+ case SQLITE_HASH_INT: return &intHash;
+ case SQLITE_HASH_POINTER: return &ptrHash;
+ case SQLITE_HASH_STRING: return &strHash;
+ case SQLITE_HASH_BINARY: return &binHash;;
+ default: break;
+ }
+ return 0;
+#else
+ if( keyClass==SQLITE_HASH_STRING ){
+ return &strHash;
+ }else{
+ assert( keyClass==SQLITE_HASH_BINARY );
+ return &binHash;
+ }
+#endif
+}
+
+/*
+** Return a pointer to the appropriate hash function given the key class.
+**
+** For help in interpreted the obscure C code in the function definition,
+** see the header comment on the previous function.
+*/
+static int (*compareFunction(int keyClass))(const void*,int,const void*,int){
+#if 0 /* HASH_INT and HASH_POINTER are never used */
+ switch( keyClass ){
+ case SQLITE_HASH_INT: return &intCompare;
+ case SQLITE_HASH_POINTER: return &ptrCompare;
+ case SQLITE_HASH_STRING: return &strCompare;
+ case SQLITE_HASH_BINARY: return &binCompare;
+ default: break;
+ }
+ return 0;
+#else
+ if( keyClass==SQLITE_HASH_STRING ){
+ return &strCompare;
+ }else{
+ assert( keyClass==SQLITE_HASH_BINARY );
+ return &binCompare;
+ }
+#endif
+}
+
+/* Link an element into the hash table
+*/
+static void insertElement(
+ Hash *pH, /* The complete hash table */
+ struct _ht *pEntry, /* The entry into which pNew is inserted */
+ HashElem *pNew /* The element to be inserted */
+){
+ HashElem *pHead; /* First element already in pEntry */
+ pHead = pEntry->chain;
+ if( pHead ){
+ pNew->next = pHead;
+ pNew->prev = pHead->prev;
+ if( pHead->prev ){ pHead->prev->next = pNew; }
+ else { pH->first = pNew; }
+ pHead->prev = pNew;
+ }else{
+ pNew->next = pH->first;
+ if( pH->first ){ pH->first->prev = pNew; }
+ pNew->prev = 0;
+ pH->first = pNew;
+ }
+ pEntry->count++;
+ pEntry->chain = pNew;
+}
+
+
+/* Resize the hash table so that it cantains "new_size" buckets.
+** "new_size" must be a power of 2. The hash table might fail
+** to resize if sqliteMalloc() fails.
+*/
+static void rehash(Hash *pH, int new_size){
+ struct _ht *new_ht; /* The new hash table */
+ HashElem *elem, *next_elem; /* For looping over existing elements */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( (new_size & (new_size-1))==0 );
+ new_ht = (struct _ht *)sqliteMalloc( new_size*sizeof(struct _ht) );
+ if( new_ht==0 ) return;
+ if( pH->ht ) sqliteFree(pH->ht);
+ pH->ht = new_ht;
+ pH->htsize = new_size;
+ xHash = hashFunction(pH->keyClass);
+ for(elem=pH->first, pH->first=0; elem; elem = next_elem){
+ int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1);
+ next_elem = elem->next;
+ insertElement(pH, &new_ht[h], elem);
+ }
+}
+
+/* This function (for internal use only) locates an element in an
+** hash table that matches the given key. The hash for this key has
+** already been computed and is passed as the 4th parameter.
+*/
+static HashElem *findElementGivenHash(
+ const Hash *pH, /* The pH to be searched */
+ const void *pKey, /* The key we are searching for */
+ int nKey,
+ int h /* The hash for this key. */
+){
+ HashElem *elem; /* Used to loop thru the element list */
+ int count; /* Number of elements left to test */
+ int (*xCompare)(const void*,int,const void*,int); /* comparison function */
+
+ if( pH->ht ){
+ struct _ht *pEntry = &pH->ht[h];
+ elem = pEntry->chain;
+ count = pEntry->count;
+ xCompare = compareFunction(pH->keyClass);
+ while( count-- && elem ){
+ if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){
+ return elem;
+ }
+ elem = elem->next;
+ }
+ }
+ return 0;
+}
+
+/* Remove a single entry from the hash table given a pointer to that
+** element and a hash on the element's key.
+*/
+static void removeElementGivenHash(
+ Hash *pH, /* The pH containing "elem" */
+ HashElem* elem, /* The element to be removed from the pH */
+ int h /* Hash value for the element */
+){
+ struct _ht *pEntry;
+ if( elem->prev ){
+ elem->prev->next = elem->next;
+ }else{
+ pH->first = elem->next;
+ }
+ if( elem->next ){
+ elem->next->prev = elem->prev;
+ }
+ pEntry = &pH->ht[h];
+ if( pEntry->chain==elem ){
+ pEntry->chain = elem->next;
+ }
+ pEntry->count--;
+ if( pEntry->count<=0 ){
+ pEntry->chain = 0;
+ }
+ if( pH->copyKey && elem->pKey ){
+ sqliteFree(elem->pKey);
+ }
+ sqliteFree( elem );
+ pH->count--;
+}
+
+/* Attempt to locate an element of the hash table pH with a key
+** that matches pKey,nKey. Return the data for this element if it is
+** found, or NULL if there is no match.
+*/
+void *sqlite3HashFind(const Hash *pH, const void *pKey, int nKey){
+ int h; /* A hash on key */
+ HashElem *elem; /* The element that matches key */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ if( pH==0 || pH->ht==0 ) return 0;
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ h = (*xHash)(pKey,nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ elem = findElementGivenHash(pH,pKey,nKey, h & (pH->htsize-1));
+ return elem ? elem->data : 0;
+}
+
+/* Insert an element into the hash table pH. The key is pKey,nKey
+** and the data is "data".
+**
+** If no element exists with a matching key, then a new
+** element is created. A copy of the key is made if the copyKey
+** flag is set. NULL is returned.
+**
+** If another element already exists with the same key, then the
+** new data replaces the old data and the old data is returned.
+** The key is not copied in this instance. If a malloc fails, then
+** the new data is returned and the hash table is unchanged.
+**
+** If the "data" parameter to this function is NULL, then the
+** element corresponding to "key" is removed from the hash table.
+*/
+void *sqlite3HashInsert(Hash *pH, const void *pKey, int nKey, void *data){
+ int hraw; /* Raw hash value of the key */
+ int h; /* the hash of the key modulo hash table size */
+ HashElem *elem; /* Used to loop thru the element list */
+ HashElem *new_elem; /* New element added to the pH */
+ int (*xHash)(const void*,int); /* The hash function */
+
+ assert( pH!=0 );
+ xHash = hashFunction(pH->keyClass);
+ assert( xHash!=0 );
+ hraw = (*xHash)(pKey, nKey);
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ elem = findElementGivenHash(pH,pKey,nKey,h);
+ if( elem ){
+ void *old_data = elem->data;
+ if( data==0 ){
+ removeElementGivenHash(pH,elem,h);
+ }else{
+ elem->data = data;
+ }
+ return old_data;
+ }
+ if( data==0 ) return 0;
+ new_elem = (HashElem*)sqliteMalloc( sizeof(HashElem) );
+ if( new_elem==0 ) return data;
+ if( pH->copyKey && pKey!=0 ){
+ new_elem->pKey = sqliteMallocRaw( nKey );
+ if( new_elem->pKey==0 ){
+ sqliteFree(new_elem);
+ return data;
+ }
+ memcpy((void*)new_elem->pKey, pKey, nKey);
+ }else{
+ new_elem->pKey = (void*)pKey;
+ }
+ new_elem->nKey = nKey;
+ pH->count++;
+ if( pH->htsize==0 ){
+ rehash(pH,8);
+ if( pH->htsize==0 ){
+ pH->count = 0;
+ sqliteFree(new_elem);
+ return data;
+ }
+ }
+ if( pH->count > pH->htsize ){
+ rehash(pH,pH->htsize*2);
+ }
+ assert( pH->htsize>0 );
+ assert( (pH->htsize & (pH->htsize-1))==0 );
+ h = hraw & (pH->htsize-1);
+ insertElement(pH, &pH->ht[h], new_elem);
+ new_elem->data = data;
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/hash.h b/kopete/plugins/statistics/sqlite/hash.h
new file mode 100644
index 00000000..cf004ddc
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/hash.h
@@ -0,0 +1,109 @@
+/*
+** 2001 September 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the header file for the generic hash-table implemenation
+** used in SQLite.
+**
+** $Id$
+*/
+#ifndef _SQLITE_HASH_H_
+#define _SQLITE_HASH_H_
+
+/* Forward declarations of structures. */
+typedef struct Hash Hash;
+typedef struct HashElem HashElem;
+
+/* A complete hash table is an instance of the following structure.
+** The internals of this structure are intended to be opaque -- client
+** code should not attempt to access or modify the fields of this structure
+** directly. Change this structure only by using the routines below.
+** However, many of the "procedures" and "functions" for modifying and
+** accessing this structure are really macros, so we can't really make
+** this structure opaque.
+*/
+struct Hash {
+ char keyClass; /* SQLITE_HASH_INT, _POINTER, _STRING, _BINARY */
+ char copyKey; /* True if copy of key made on insert */
+ int count; /* Number of entries in this table */
+ HashElem *first; /* The first element of the array */
+ int htsize; /* Number of buckets in the hash table */
+ struct _ht { /* the hash table */
+ int count; /* Number of entries with this hash */
+ HashElem *chain; /* Pointer to first entry with this hash */
+ } *ht;
+};
+
+/* Each element in the hash table is an instance of the following
+** structure. All elements are stored on a single doubly-linked list.
+**
+** Again, this structure is intended to be opaque, but it can't really
+** be opaque because it is used by macros.
+*/
+struct HashElem {
+ HashElem *next, *prev; /* Next and previous elements in the table */
+ void *data; /* Data associated with this element */
+ void *pKey; int nKey; /* Key associated with this element */
+};
+
+/*
+** There are 4 different modes of operation for a hash table:
+**
+** SQLITE_HASH_INT nKey is used as the key and pKey is ignored.
+**
+** SQLITE_HASH_POINTER pKey is used as the key and nKey is ignored.
+**
+** SQLITE_HASH_STRING pKey points to a string that is nKey bytes long
+** (including the null-terminator, if any). Case
+** is ignored in comparisons.
+**
+** SQLITE_HASH_BINARY pKey points to binary data nKey bytes long.
+** memcmp() is used to compare keys.
+**
+** A copy of the key is made for SQLITE_HASH_STRING and SQLITE_HASH_BINARY
+** if the copyKey parameter to HashInit is 1.
+*/
+/* #define SQLITE_HASH_INT 1 // NOT USED */
+/* #define SQLITE_HASH_POINTER 2 // NOT USED */
+#define SQLITE_HASH_STRING 3
+#define SQLITE_HASH_BINARY 4
+
+/*
+** Access routines. To delete, insert a NULL pointer.
+*/
+void sqlite3HashInit(Hash*, int keytype, int copyKey);
+void *sqlite3HashInsert(Hash*, const void *pKey, int nKey, void *pData);
+void *sqlite3HashFind(const Hash*, const void *pKey, int nKey);
+void sqlite3HashClear(Hash*);
+
+/*
+** Macros for looping over all elements of a hash table. The idiom is
+** like this:
+**
+** Hash h;
+** HashElem *p;
+** ...
+** for(p=sqliteHashFirst(&h); p; p=sqliteHashNext(p)){
+** SomeStructure *pData = sqliteHashData(p);
+** // do something with pData
+** }
+*/
+#define sqliteHashFirst(H) ((H)->first)
+#define sqliteHashNext(E) ((E)->next)
+#define sqliteHashData(E) ((E)->data)
+#define sqliteHashKey(E) ((E)->pKey)
+#define sqliteHashKeysize(E) ((E)->nKey)
+
+/*
+** Number of entries in a hash table
+*/
+#define sqliteHashCount(H) ((H)->count)
+
+#endif /* _SQLITE_HASH_H_ */
diff --git a/kopete/plugins/statistics/sqlite/insert.c b/kopete/plugins/statistics/sqlite/insert.c
new file mode 100644
index 00000000..65cbdc8f
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/insert.c
@@ -0,0 +1,1018 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle INSERT statements in SQLite.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** Set P3 of the most recently inserted opcode to a column affinity
+** string for index pIdx. A column affinity string has one character
+** for each column in the table, according to the affinity of the column:
+**
+** Character Column affinity
+** ------------------------------
+** 'n' NUMERIC
+** 'i' INTEGER
+** 't' TEXT
+** 'o' NONE
+*/
+void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
+ if( !pIdx->zColAff ){
+ /* The first time a column affinity string for a particular index is
+ ** required, it is allocated and populated here. It is then stored as
+ ** a member of the Index structure for subsequent use.
+ **
+ ** The column affinity string will eventually be deleted by
+ ** sqliteDeleteIndex() when the Index structure itself is cleaned
+ ** up.
+ */
+ int n;
+ Table *pTab = pIdx->pTable;
+ pIdx->zColAff = (char *)sqliteMalloc(pIdx->nColumn+1);
+ if( !pIdx->zColAff ){
+ return;
+ }
+ for(n=0; n<pIdx->nColumn; n++){
+ pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
+ }
+ pIdx->zColAff[pIdx->nColumn] = '\0';
+ }
+
+ sqlite3VdbeChangeP3(v, -1, pIdx->zColAff, 0);
+}
+
+/*
+** Set P3 of the most recently inserted opcode to a column affinity
+** string for table pTab. A column affinity string has one character
+** for each column indexed by the index, according to the affinity of the
+** column:
+**
+** Character Column affinity
+** ------------------------------
+** 'n' NUMERIC
+** 'i' INTEGER
+** 't' TEXT
+** 'o' NONE
+*/
+void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
+ /* The first time a column affinity string for a particular table
+ ** is required, it is allocated and populated here. It is then
+ ** stored as a member of the Table structure for subsequent use.
+ **
+ ** The column affinity string will eventually be deleted by
+ ** sqlite3DeleteTable() when the Table structure itself is cleaned up.
+ */
+ if( !pTab->zColAff ){
+ char *zColAff;
+ int i;
+
+ zColAff = (char *)sqliteMalloc(pTab->nCol+1);
+ if( !zColAff ){
+ return;
+ }
+
+ for(i=0; i<pTab->nCol; i++){
+ zColAff[i] = pTab->aCol[i].affinity;
+ }
+ zColAff[pTab->nCol] = '\0';
+
+ pTab->zColAff = zColAff;
+ }
+
+ sqlite3VdbeChangeP3(v, -1, pTab->zColAff, 0);
+}
+
+
+/*
+** This routine is call to handle SQL of the following forms:
+**
+** insert into TABLE (IDLIST) values(EXPRLIST)
+** insert into TABLE (IDLIST) select
+**
+** The IDLIST following the table name is always optional. If omitted,
+** then a list of all columns for the table is substituted. The IDLIST
+** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted.
+**
+** The pList parameter holds EXPRLIST in the first form of the INSERT
+** statement above, and pSelect is NULL. For the second form, pList is
+** NULL and pSelect is a pointer to the select statement used to generate
+** data for the insert.
+**
+** The code generated follows one of three templates. For a simple
+** select with data coming from a VALUES clause, the code executes
+** once straight down through. The template looks like this:
+**
+** open write cursor to <table> and its indices
+** puts VALUES clause expressions onto the stack
+** write the resulting record into <table>
+** cleanup
+**
+** If the statement is of the form
+**
+** INSERT INTO <table> SELECT ...
+**
+** And the SELECT clause does not read from <table> at any time, then
+** the generated code follows this template:
+**
+** goto B
+** A: setup for the SELECT
+** loop over the tables in the SELECT
+** gosub C
+** end loop
+** cleanup after the SELECT
+** goto D
+** B: open write cursor to <table> and its indices
+** goto A
+** C: insert the select result into <table>
+** return
+** D: cleanup
+**
+** The third template is used if the insert statement takes its
+** values from a SELECT but the data is being inserted into a table
+** that is also read as part of the SELECT. In the third form,
+** we have to use a intermediate table to store the results of
+** the select. The template is like this:
+**
+** goto B
+** A: setup for the SELECT
+** loop over the tables in the SELECT
+** gosub C
+** end loop
+** cleanup after the SELECT
+** goto D
+** C: insert the select result into the intermediate table
+** return
+** B: open a cursor to an intermediate table
+** goto A
+** D: open write cursor to <table> and its indices
+** loop over the intermediate table
+** transfer values form intermediate table into <table>
+** end the loop
+** cleanup
+*/
+void sqlite3Insert(
+ Parse *pParse, /* Parser context */
+ SrcList *pTabList, /* Name of table into which we are inserting */
+ ExprList *pList, /* List of values to be inserted */
+ Select *pSelect, /* A SELECT statement to use as the data source */
+ IdList *pColumn, /* Column names corresponding to IDLIST. */
+ int onError /* How to handle constraint errors */
+){
+ Table *pTab; /* The table to insert into */
+ char *zTab; /* Name of the table into which we are inserting */
+ const char *zDb; /* Name of the database holding this table */
+ int i, j, idx; /* Loop counters */
+ Vdbe *v; /* Generate code into this virtual machine */
+ Index *pIdx; /* For looping over indices of the table */
+ int nColumn; /* Number of columns in the data */
+ int base = 0; /* VDBE Cursor number for pTab */
+ int iCont=0,iBreak=0; /* Beginning and end of the loop over srcTab */
+ sqlite3 *db; /* The main database structure */
+ int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
+ int endOfLoop; /* Label for the end of the insertion loop */
+ int useTempTable; /* Store SELECT results in intermediate table */
+ int srcTab = 0; /* Data comes from this temporary cursor if >=0 */
+ int iSelectLoop = 0; /* Address of code that implements the SELECT */
+ int iCleanup = 0; /* Address of the cleanup code */
+ int iInsertBlock = 0; /* Address of the subroutine used to insert data */
+ int iCntMem = 0; /* Memory cell used for the row counter */
+ int isView; /* True if attempting to insert into a view */
+
+ int row_triggers_exist = 0; /* True if there are FOR EACH ROW triggers */
+ int before_triggers; /* True if there are BEFORE triggers */
+ int after_triggers; /* True if there are AFTER triggers */
+ int newIdx = -1; /* Cursor for the NEW table */
+
+ if( pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup;
+ db = pParse->db;
+
+ /* Locate the table into which we will be inserting new information.
+ */
+ assert( pTabList->nSrc==1 );
+ zTab = pTabList->a[0].zName;
+ if( zTab==0 ) goto insert_cleanup;
+ pTab = sqlite3SrcListLookup(pParse, pTabList);
+ if( pTab==0 ){
+ goto insert_cleanup;
+ }
+ assert( pTab->iDb<db->nDb );
+ zDb = db->aDb[pTab->iDb].zName;
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
+ goto insert_cleanup;
+ }
+
+ /* Ensure that:
+ * (a) the table is not read-only,
+ * (b) that if it is a view then ON INSERT triggers exist
+ */
+ before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT,
+ TK_BEFORE, TK_ROW, 0);
+ after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger, TK_INSERT,
+ TK_AFTER, TK_ROW, 0);
+ row_triggers_exist = before_triggers || after_triggers;
+ isView = pTab->pSelect!=0;
+ if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){
+ goto insert_cleanup;
+ }
+ if( pTab==0 ) goto insert_cleanup;
+
+ /* If pTab is really a view, make sure it has been initialized.
+ */
+ if( isView && sqlite3ViewGetColumnNames(pParse, pTab) ){
+ goto insert_cleanup;
+ }
+
+ /* Ensure all required collation sequences are available. */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( sqlite3CheckIndexCollSeq(pParse, pIdx) ){
+ goto insert_cleanup;
+ }
+ }
+
+ /* Allocate a VDBE
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto insert_cleanup;
+ sqlite3VdbeCountChanges(v);
+ sqlite3BeginWriteOperation(pParse, pSelect || row_triggers_exist, pTab->iDb);
+
+ /* if there are row triggers, allocate a temp table for new.* references. */
+ if( row_triggers_exist ){
+ newIdx = pParse->nTab++;
+ }
+
+ /* Figure out how many columns of data are supplied. If the data
+ ** is coming from a SELECT statement, then this step also generates
+ ** all the code to implement the SELECT statement and invoke a subroutine
+ ** to process each row of the result. (Template 2.) If the SELECT
+ ** statement uses the the table that is being inserted into, then the
+ ** subroutine is also coded here. That subroutine stores the SELECT
+ ** results in a temporary table. (Template 3.)
+ */
+ if( pSelect ){
+ /* Data is coming from a SELECT. Generate code to implement that SELECT
+ */
+ int rc, iInitCode;
+ iInitCode = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
+ iSelectLoop = sqlite3VdbeCurrentAddr(v);
+ iInsertBlock = sqlite3VdbeMakeLabel(v);
+ rc = sqlite3Select(pParse, pSelect, SRT_Subroutine, iInsertBlock, 0,0,0,0);
+ if( rc || pParse->nErr || sqlite3_malloc_failed ) goto insert_cleanup;
+ iCleanup = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iCleanup);
+ assert( pSelect->pEList );
+ nColumn = pSelect->pEList->nExpr;
+
+ /* Set useTempTable to TRUE if the result of the SELECT statement
+ ** should be written into a temporary table. Set to FALSE if each
+ ** row of the SELECT can be written directly into the result table.
+ **
+ ** A temp table must be used if the table being updated is also one
+ ** of the tables being read by the SELECT statement. Also use a
+ ** temp table in the case of row triggers.
+ */
+ if( row_triggers_exist ){
+ useTempTable = 1;
+ }else{
+ int addr = 0;
+ useTempTable = 0;
+ while( useTempTable==0 ){
+ VdbeOp *pOp;
+ addr = sqlite3VdbeFindOp(v, addr, OP_OpenRead, pTab->tnum);
+ if( addr==0 ) break;
+ pOp = sqlite3VdbeGetOp(v, addr-2);
+ if( pOp->opcode==OP_Integer && pOp->p1==pTab->iDb ){
+ useTempTable = 1;
+ }
+ }
+ }
+
+ if( useTempTable ){
+ /* Generate the subroutine that SELECT calls to process each row of
+ ** the result. Store the result in a temporary table
+ */
+ srcTab = pParse->nTab++;
+ sqlite3VdbeResolveLabel(v, iInsertBlock);
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ sqlite3TableAffinityStr(v, pTab);
+ sqlite3VdbeAddOp(v, OP_NewRecno, srcTab, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, srcTab, 0);
+ sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+
+ /* The following code runs first because the GOTO at the very top
+ ** of the program jumps to it. Create the temporary table, then jump
+ ** back up and execute the SELECT code above.
+ */
+ sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeAddOp(v, OP_OpenTemp, srcTab, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, srcTab, nColumn);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
+ sqlite3VdbeResolveLabel(v, iCleanup);
+ }else{
+ sqlite3VdbeChangeP2(v, iInitCode, sqlite3VdbeCurrentAddr(v));
+ }
+ }else{
+ /* This is the case if the data for the INSERT is coming from a VALUES
+ ** clause
+ */
+ SrcList dummy;
+ assert( pList!=0 );
+ srcTab = -1;
+ useTempTable = 0;
+ assert( pList );
+ nColumn = pList->nExpr;
+ dummy.nSrc = 0;
+ for(i=0; i<nColumn; i++){
+ if( sqlite3ExprResolveAndCheck(pParse,&dummy,0,pList->a[i].pExpr,0,0) ){
+ goto insert_cleanup;
+ }
+ }
+ }
+
+ /* Make sure the number of columns in the source data matches the number
+ ** of columns to be inserted into the table.
+ */
+ if( pColumn==0 && nColumn!=pTab->nCol ){
+ sqlite3ErrorMsg(pParse,
+ "table %S has %d columns but %d values were supplied",
+ pTabList, 0, pTab->nCol, nColumn);
+ goto insert_cleanup;
+ }
+ if( pColumn!=0 && nColumn!=pColumn->nId ){
+ sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
+ goto insert_cleanup;
+ }
+
+ /* If the INSERT statement included an IDLIST term, then make sure
+ ** all elements of the IDLIST really are columns of the table and
+ ** remember the column indices.
+ **
+ ** If the table has an INTEGER PRIMARY KEY column and that column
+ ** is named in the IDLIST, then record in the keyColumn variable
+ ** the index into IDLIST of the primary key column. keyColumn is
+ ** the index of the primary key as it appears in IDLIST, not as
+ ** is appears in the original table. (The index of the primary
+ ** key in the original table is pTab->iPKey.)
+ */
+ if( pColumn ){
+ for(i=0; i<pColumn->nId; i++){
+ pColumn->a[i].idx = -1;
+ }
+ for(i=0; i<pColumn->nId; i++){
+ for(j=0; j<pTab->nCol; j++){
+ if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
+ pColumn->a[i].idx = j;
+ if( j==pTab->iPKey ){
+ keyColumn = i;
+ }
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ if( sqlite3IsRowid(pColumn->a[i].zName) ){
+ keyColumn = i;
+ }else{
+ sqlite3ErrorMsg(pParse, "table %S has no column named %s",
+ pTabList, 0, pColumn->a[i].zName);
+ pParse->nErr++;
+ goto insert_cleanup;
+ }
+ }
+ }
+ }
+
+ /* If there is no IDLIST term but the table has an integer primary
+ ** key, the set the keyColumn variable to the primary key column index
+ ** in the original table definition.
+ */
+ if( pColumn==0 ){
+ keyColumn = pTab->iPKey;
+ }
+
+ /* Open the temp table for FOR EACH ROW triggers
+ */
+ if( row_triggers_exist ){
+ sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);
+ }
+
+ /* Initialize the count of rows to be inserted
+ */
+ if( db->flags & SQLITE_CountRows ){
+ iCntMem = pParse->nMem++;
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iCntMem, 1);
+ }
+
+ /* Open tables and indices if there are no row triggers */
+ if( !row_triggers_exist ){
+ base = pParse->nTab;
+ sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
+ }
+
+ /* If the data source is a temporary table, then we have to create
+ ** a loop because there might be multiple rows of data. If the data
+ ** source is a subroutine call from the SELECT statement, then we need
+ ** to launch the SELECT statement processing.
+ */
+ if( useTempTable ){
+ iBreak = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Rewind, srcTab, iBreak);
+ iCont = sqlite3VdbeCurrentAddr(v);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iSelectLoop);
+ sqlite3VdbeResolveLabel(v, iInsertBlock);
+ }
+
+ /* Run the BEFORE and INSTEAD OF triggers, if there are any
+ */
+ endOfLoop = sqlite3VdbeMakeLabel(v);
+ if( before_triggers ){
+
+ /* build the NEW.* reference row. Note that if there is an INTEGER
+ ** PRIMARY KEY into which a NULL is being inserted, that NULL will be
+ ** translated into a unique ID for the row. But on a BEFORE trigger,
+ ** we do not know what the unique ID will be (because the insert has
+ ** not happened yet) so we substitute a rowid of -1
+ */
+ if( keyColumn<0 ){
+ sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
+ }else if( useTempTable ){
+ sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
+ }else{
+ sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
+ sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, -1, 0);
+ sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ }
+
+ /* Create the new column data
+ */
+ for(i=0; i<pTab->nCol; i++){
+ if( pColumn==0 ){
+ j = i;
+ }else{
+ for(j=0; j<pColumn->nId; j++){
+ if( pColumn->a[j].idx==i ) break;
+ }
+ }
+ if( pColumn && j>=pColumn->nId ){
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
+ }else if( useTempTable ){
+ sqlite3VdbeAddOp(v, OP_Column, srcTab, j);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Dup, nColumn-j-1, 1);
+ }else{
+ sqlite3ExprCode(pParse, pList->a[j].pExpr);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+
+ /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
+ ** do not attempt any conversions before assembling the record.
+ ** If this is a real table, attempt conversions as required by the
+ ** table column affinities.
+ */
+ if( !isView ){
+ sqlite3TableAffinityStr(v, pTab);
+ }
+ sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0);
+
+ /* Fire BEFORE or INSTEAD OF triggers */
+ if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_BEFORE, pTab,
+ newIdx, -1, onError, endOfLoop) ){
+ goto insert_cleanup;
+ }
+ }
+
+ /* If any triggers exists, the opening of tables and indices is deferred
+ ** until now.
+ */
+ if( row_triggers_exist && !isView ){
+ base = pParse->nTab;
+ sqlite3OpenTableAndIndices(pParse, pTab, base, OP_OpenWrite);
+ }
+
+ /* Push the record number for the new entry onto the stack. The
+ ** record number is a randomly generate integer created by NewRecno
+ ** except when the table has an INTEGER PRIMARY KEY column, in which
+ ** case the record number is the same as that column.
+ */
+ if( !isView ){
+ if( keyColumn>=0 ){
+ if( useTempTable ){
+ sqlite3VdbeAddOp(v, OP_Column, srcTab, keyColumn);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Dup, nColumn - keyColumn - 1, 1);
+ }else{
+ sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr);
+ }
+ /* If the PRIMARY KEY expression is NULL, then use OP_NewRecno
+ ** to generate a unique primary key value.
+ */
+ sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeAddOp(v, OP_NewRecno, base, 0);
+ sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_NewRecno, base, 0);
+ }
+
+ /* Push onto the stack, data for all columns of the new entry, beginning
+ ** with the first column.
+ */
+ for(i=0; i<pTab->nCol; i++){
+ if( i==pTab->iPKey ){
+ /* The value of the INTEGER PRIMARY KEY column is always a NULL.
+ ** Whenever this column is read, the record number will be substituted
+ ** in its place. So will fill this column with a NULL to avoid
+ ** taking up data space with information that will never be used. */
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ continue;
+ }
+ if( pColumn==0 ){
+ j = i;
+ }else{
+ for(j=0; j<pColumn->nId; j++){
+ if( pColumn->a[j].idx==i ) break;
+ }
+ }
+ if( pColumn && j>=pColumn->nId ){
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
+ }else if( useTempTable ){
+ sqlite3VdbeAddOp(v, OP_Column, srcTab, j);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Dup, i+nColumn-j, 1);
+ }else{
+ sqlite3ExprCode(pParse, pList->a[j].pExpr);
+ }
+ }
+
+ /* Generate code to check constraints and generate index keys and
+ ** do the insertion.
+ */
+ sqlite3GenerateConstraintChecks(pParse, pTab, base, 0, keyColumn>=0,
+ 0, onError, endOfLoop);
+ sqlite3CompleteInsertion(pParse, pTab, base, 0,0,0,
+ after_triggers ? newIdx : -1);
+ }
+
+ /* Update the count of rows that are inserted
+ */
+ if( (db->flags & SQLITE_CountRows)!=0 ){
+ sqlite3VdbeAddOp(v, OP_MemIncr, iCntMem, 0);
+ }
+
+ if( row_triggers_exist ){
+ /* Close all tables opened */
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Close, base, 0);
+ for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+ sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
+ }
+ }
+
+ /* Code AFTER triggers */
+ if( sqlite3CodeRowTrigger(pParse, TK_INSERT, 0, TK_AFTER, pTab, newIdx, -1,
+ onError, endOfLoop) ){
+ goto insert_cleanup;
+ }
+ }
+
+ /* The bottom of the loop, if the data source is a SELECT statement
+ */
+ sqlite3VdbeResolveLabel(v, endOfLoop);
+ if( useTempTable ){
+ sqlite3VdbeAddOp(v, OP_Next, srcTab, iCont);
+ sqlite3VdbeResolveLabel(v, iBreak);
+ sqlite3VdbeAddOp(v, OP_Close, srcTab, 0);
+ }else if( pSelect ){
+ sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
+ sqlite3VdbeAddOp(v, OP_Return, 0, 0);
+ sqlite3VdbeResolveLabel(v, iCleanup);
+ }
+
+ if( !row_triggers_exist ){
+ /* Close all tables opened */
+ sqlite3VdbeAddOp(v, OP_Close, base, 0);
+ for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+ sqlite3VdbeAddOp(v, OP_Close, idx+base, 0);
+ }
+ }
+
+ /*
+ ** Return the number of rows inserted.
+ */
+ if( db->flags & SQLITE_CountRows ){
+ sqlite3VdbeAddOp(v, OP_MemLoad, iCntMem, 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "rows inserted", P3_STATIC);
+ }
+
+insert_cleanup:
+ sqlite3SrcListDelete(pTabList);
+ if( pList ) sqlite3ExprListDelete(pList);
+ if( pSelect ) sqlite3SelectDelete(pSelect);
+ sqlite3IdListDelete(pColumn);
+}
+
+/*
+** Generate code to do a constraint check prior to an INSERT or an UPDATE.
+**
+** When this routine is called, the stack contains (from bottom to top)
+** the following values:
+**
+** 1. The recno of the row to be updated before the update. This
+** value is omitted unless we are doing an UPDATE that involves a
+** change to the record number.
+**
+** 2. The recno of the row after the update.
+**
+** 3. The data in the first column of the entry after the update.
+**
+** i. Data from middle columns...
+**
+** N. The data in the last column of the entry after the update.
+**
+** The old recno shown as entry (1) above is omitted unless both isUpdate
+** and recnoChng are 1. isUpdate is true for UPDATEs and false for
+** INSERTs and recnoChng is true if the record number is being changed.
+**
+** The code generated by this routine pushes additional entries onto
+** the stack which are the keys for new index entries for the new record.
+** The order of index keys is the same as the order of the indices on
+** the pTable->pIndex list. A key is only created for index i if
+** aIdxUsed!=0 and aIdxUsed[i]!=0.
+**
+** This routine also generates code to check constraints. NOT NULL,
+** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
+** then the appropriate action is performed. There are five possible
+** actions: ROLLBACK, ABORT, FAIL, REPLACE, and IGNORE.
+**
+** Constraint type Action What Happens
+** --------------- ---------- ----------------------------------------
+** any ROLLBACK The current transaction is rolled back and
+** sqlite3_exec() returns immediately with a
+** return code of SQLITE_CONSTRAINT.
+**
+** any ABORT Back out changes from the current command
+** only (do not do a complete rollback) then
+** cause sqlite3_exec() to return immediately
+** with SQLITE_CONSTRAINT.
+**
+** any FAIL Sqlite_exec() returns immediately with a
+** return code of SQLITE_CONSTRAINT. The
+** transaction is not rolled back and any
+** prior changes are retained.
+**
+** any IGNORE The record number and data is popped from
+** the stack and there is an immediate jump
+** to label ignoreDest.
+**
+** NOT NULL REPLACE The NULL value is replace by the default
+** value for that column. If the default value
+** is NULL, the action is the same as ABORT.
+**
+** UNIQUE REPLACE The other row that conflicts with the row
+** being inserted is removed.
+**
+** CHECK REPLACE Illegal. The results in an exception.
+**
+** Which action to take is determined by the overrideError parameter.
+** Or if overrideError==OE_Default, then the pParse->onError parameter
+** is used. Or if pParse->onError==OE_Default then the onError value
+** for the constraint is used.
+**
+** The calling routine must open a read/write cursor for pTab with
+** cursor number "base". All indices of pTab must also have open
+** read/write cursors with cursor number base+i for the i-th cursor.
+** Except, if there is no possibility of a REPLACE action then
+** cursors do not need to be open for indices where aIdxUsed[i]==0.
+**
+** If the isUpdate flag is true, it means that the "base" cursor is
+** initially pointing to an entry that is being updated. The isUpdate
+** flag causes extra code to be generated so that the "base" cursor
+** is still pointing at the same entry after the routine returns.
+** Without the isUpdate flag, the "base" cursor might be moved.
+*/
+void sqlite3GenerateConstraintChecks(
+ Parse *pParse, /* The parser context */
+ Table *pTab, /* the table into which we are inserting */
+ int base, /* Index of a read/write cursor pointing at pTab */
+ char *aIdxUsed, /* Which indices are used. NULL means all are used */
+ int recnoChng, /* True if the record number will change */
+ int isUpdate, /* True for UPDATE, False for INSERT */
+ int overrideError, /* Override onError to this if not OE_Default */
+ int ignoreDest /* Jump to this label on an OE_Ignore resolution */
+){
+ int i;
+ Vdbe *v;
+ int nCol;
+ int onError;
+ int addr;
+ int extra;
+ int iCur;
+ Index *pIdx;
+ int seenReplace = 0;
+ int jumpInst1=0, jumpInst2;
+ int contAddr;
+ int hasTwoRecnos = (isUpdate && recnoChng);
+
+ v = sqlite3GetVdbe(pParse);
+ assert( v!=0 );
+ assert( pTab->pSelect==0 ); /* This table is not a VIEW */
+ nCol = pTab->nCol;
+
+ /* Test all NOT NULL constraints.
+ */
+ for(i=0; i<nCol; i++){
+ if( i==pTab->iPKey ){
+ continue;
+ }
+ onError = pTab->aCol[i].notNull;
+ if( onError==OE_None ) continue;
+ if( overrideError!=OE_Default ){
+ onError = overrideError;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
+ }
+ if( onError==OE_Replace && pTab->aCol[i].zDflt==0 ){
+ onError = OE_Abort;
+ }
+ sqlite3VdbeAddOp(v, OP_Dup, nCol-1-i, 1);
+ addr = sqlite3VdbeAddOp(v, OP_NotNull, 1, 0);
+ switch( onError ){
+ case OE_Rollback:
+ case OE_Abort:
+ case OE_Fail: {
+ char *zMsg = 0;
+ sqlite3VdbeAddOp(v, OP_Halt, SQLITE_CONSTRAINT, onError);
+ sqlite3SetString(&zMsg, pTab->zName, ".", pTab->aCol[i].zName,
+ " may not be NULL", (char*)0);
+ sqlite3VdbeChangeP3(v, -1, zMsg, P3_DYNAMIC);
+ break;
+ }
+ case OE_Ignore: {
+ sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ break;
+ }
+ case OE_Replace: {
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zDflt, P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_Push, nCol-i, 0);
+ break;
+ }
+ default: assert(0);
+ }
+ sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v));
+ }
+
+ /* Test all CHECK constraints
+ */
+ /**** TBD ****/
+
+ /* If we have an INTEGER PRIMARY KEY, make sure the primary key
+ ** of the new record does not previously exist. Except, if this
+ ** is an UPDATE and the primary key is not changing, that is OK.
+ */
+ if( recnoChng ){
+ onError = pTab->keyConf;
+ if( overrideError!=OE_Default ){
+ onError = overrideError;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
+ }
+
+ if( isUpdate ){
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
+ jumpInst1 = sqlite3VdbeAddOp(v, OP_Eq, 0, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_Dup, nCol, 1);
+ jumpInst2 = sqlite3VdbeAddOp(v, OP_NotExists, base, 0);
+ switch( onError ){
+ default: {
+ onError = OE_Abort;
+ /* Fall thru into the next case */
+ }
+ case OE_Rollback:
+ case OE_Abort:
+ case OE_Fail: {
+ sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError,
+ "PRIMARY KEY must be unique", P3_STATIC);
+ break;
+ }
+ case OE_Replace: {
+ sqlite3GenerateRowIndexDelete(pParse->db, v, pTab, base, 0);
+ if( isUpdate ){
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+hasTwoRecnos, 1);
+ sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
+ }
+ seenReplace = 1;
+ break;
+ }
+ case OE_Ignore: {
+ assert( seenReplace==0 );
+ sqlite3VdbeAddOp(v, OP_Pop, nCol+1+hasTwoRecnos, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ break;
+ }
+ }
+ contAddr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeChangeP2(v, jumpInst2, contAddr);
+ if( isUpdate ){
+ sqlite3VdbeChangeP2(v, jumpInst1, contAddr);
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+1, 1);
+ sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
+ }
+ }
+
+ /* Test all UNIQUE constraints by creating entries for each UNIQUE
+ ** index and making sure that duplicate entries do not already exist.
+ ** Add the new records to the indices as we go.
+ */
+ extra = -1;
+ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
+ if( aIdxUsed && aIdxUsed[iCur]==0 ) continue; /* Skip unused indices */
+ extra++;
+
+ /* Create a key for accessing the index entry */
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+extra, 1);
+ for(i=0; i<pIdx->nColumn; i++){
+ int idx = pIdx->aiColumn[i];
+ if( idx==pTab->iPKey ){
+ sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol+1, 1);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Dup, i+extra+nCol-idx, 1);
+ }
+ }
+ jumpInst1 = sqlite3VdbeAddOp(v, OP_MakeRecord, pIdx->nColumn, (1<<24));
+ sqlite3IndexAffinityStr(v, pIdx);
+
+ /* Find out what action to take in case there is an indexing conflict */
+ onError = pIdx->onError;
+ if( onError==OE_None ) continue; /* pIdx is not a UNIQUE index */
+ if( overrideError!=OE_Default ){
+ onError = overrideError;
+ }else if( onError==OE_Default ){
+ onError = OE_Abort;
+ }
+ if( seenReplace ){
+ if( onError==OE_Ignore ) onError = OE_Replace;
+ else if( onError==OE_Fail ) onError = OE_Abort;
+ }
+
+
+ /* Check to see if the new index entry will be unique */
+ sqlite3VdbeAddOp(v, OP_Dup, extra+nCol+1+hasTwoRecnos, 1);
+ jumpInst2 = sqlite3VdbeAddOp(v, OP_IsUnique, base+iCur+1, 0);
+
+ /* Generate code that executes if the new index entry is not unique */
+ switch( onError ){
+ case OE_Rollback:
+ case OE_Abort:
+ case OE_Fail: {
+ int j, n1, n2;
+ char zErrMsg[200];
+ strcpy(zErrMsg, pIdx->nColumn>1 ? "columns " : "column ");
+ n1 = strlen(zErrMsg);
+ for(j=0; j<pIdx->nColumn && n1<sizeof(zErrMsg)-30; j++){
+ char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ n2 = strlen(zCol);
+ if( j>0 ){
+ strcpy(&zErrMsg[n1], ", ");
+ n1 += 2;
+ }
+ if( n1+n2>sizeof(zErrMsg)-30 ){
+ strcpy(&zErrMsg[n1], "...");
+ n1 += 3;
+ break;
+ }else{
+ strcpy(&zErrMsg[n1], zCol);
+ n1 += n2;
+ }
+ }
+ strcpy(&zErrMsg[n1],
+ pIdx->nColumn>1 ? " are not unique" : " is not unique");
+ sqlite3VdbeOp3(v, OP_Halt, SQLITE_CONSTRAINT, onError, zErrMsg, 0);
+ break;
+ }
+ case OE_Ignore: {
+ assert( seenReplace==0 );
+ sqlite3VdbeAddOp(v, OP_Pop, nCol+extra+3+hasTwoRecnos, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, ignoreDest);
+ break;
+ }
+ case OE_Replace: {
+ sqlite3GenerateRowDelete(pParse->db, v, pTab, base, 0);
+ if( isUpdate ){
+ sqlite3VdbeAddOp(v, OP_Dup, nCol+extra+1+hasTwoRecnos, 1);
+ sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
+ }
+ seenReplace = 1;
+ break;
+ }
+ default: assert(0);
+ }
+ contAddr = sqlite3VdbeCurrentAddr(v);
+ assert( contAddr<(1<<24) );
+#if NULL_DISTINCT_FOR_UNIQUE
+ sqlite3VdbeChangeP2(v, jumpInst1, contAddr | (1<<24));
+#endif
+ sqlite3VdbeChangeP2(v, jumpInst2, contAddr);
+ }
+}
+
+/*
+** This routine generates code to finish the INSERT or UPDATE operation
+** that was started by a prior call to sqlite3GenerateConstraintChecks.
+** The stack must contain keys for all active indices followed by data
+** and the recno for the new entry. This routine creates the new
+** entries in all indices and in the main table.
+**
+** The arguments to this routine should be the same as the first six
+** arguments to sqlite3GenerateConstraintChecks.
+*/
+void sqlite3CompleteInsertion(
+ Parse *pParse, /* The parser context */
+ Table *pTab, /* the table into which we are inserting */
+ int base, /* Index of a read/write cursor pointing at pTab */
+ char *aIdxUsed, /* Which indices are used. NULL means all are used */
+ int recnoChng, /* True if the record number will change */
+ int isUpdate, /* True for UPDATE, False for INSERT */
+ int newIdx /* Index of NEW table for triggers. -1 if none */
+){
+ int i;
+ Vdbe *v;
+ int nIdx;
+ Index *pIdx;
+ int pik_flags;
+
+ v = sqlite3GetVdbe(pParse);
+ assert( v!=0 );
+ assert( pTab->pSelect==0 ); /* This table is not a VIEW */
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
+ for(i=nIdx-1; i>=0; i--){
+ if( aIdxUsed && aIdxUsed[i]==0 ) continue;
+ sqlite3VdbeAddOp(v, OP_IdxPut, base+i+1, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ sqlite3TableAffinityStr(v, pTab);
+ if( newIdx>=0 ){
+ sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Dup, 1, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0);
+ }
+ pik_flags = (OPFLAG_NCHANGE|(isUpdate?0:OPFLAG_LASTROWID));
+ sqlite3VdbeAddOp(v, OP_PutIntKey, base, pik_flags);
+
+ if( isUpdate && recnoChng ){
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ }
+}
+
+/*
+** Generate code that will open cursors for a table and for all
+** indices of that table. The "base" parameter is the cursor number used
+** for the table. Indices are opened on subsequent cursors.
+*/
+void sqlite3OpenTableAndIndices(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* Table to be opened */
+ int base, /* Cursor number assigned to the table */
+ int op /* OP_OpenRead or OP_OpenWrite */
+){
+ int i;
+ Index *pIdx;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ assert( v!=0 );
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, op, base, pTab->tnum);
+ VdbeComment((v, "# %s", pTab->zName));
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, base, pTab->nCol);
+ for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqlite3VdbeOp3(v, op, i+base, pIdx->tnum,
+ (char*)&pIdx->keyInfo, P3_KEYINFO);
+ }
+ if( pParse->nTab<=base+i ){
+ pParse->nTab = base+i;
+ }
+}
diff --git a/kopete/plugins/statistics/sqlite/legacy.c b/kopete/plugins/statistics/sqlite/legacy.c
new file mode 100644
index 00000000..f575f1f0
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/legacy.c
@@ -0,0 +1,138 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Main file for the SQLite library. The routines in this file
+** implement the programmer interface to the library. Routines in
+** other files are for internal use by SQLite and should not be
+** accessed by users of the library.
+**
+** $Id$
+*/
+
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+
+/*
+** Execute SQL code. Return one of the SQLITE_ success/failure
+** codes. Also write an error message into memory obtained from
+** malloc() and make *pzErrMsg point to that message.
+**
+** If the SQL is a query, then for each row in the query result
+** the xCallback() function is called. pArg becomes the first
+** argument to xCallback(). If xCallback=NULL then no callback
+** is invoked, even for queries.
+*/
+int sqlite3_exec(
+ sqlite3 *db, /* The database on which the SQL executes */
+ const char *zSql, /* The SQL to be executed */
+ sqlite3_callback xCallback, /* Invoke this callback routine */
+ void *pArg, /* First argument to xCallback() */
+ char **pzErrMsg /* Write error messages here */
+){
+ int rc = SQLITE_OK;
+ const char *zLeftover;
+ sqlite3_stmt *pStmt = 0;
+ char **azCols = 0;
+
+ int nRetry = 0;
+ int nChange = 0;
+ int nCallback;
+
+ if( zSql==0 ) return SQLITE_OK;
+ while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
+ int nCol;
+ char **azVals = 0;
+
+ pStmt = 0;
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover);
+ if( rc!=SQLITE_OK ){
+ if( pStmt ) sqlite3_finalize(pStmt);
+ continue;
+ }
+ if( !pStmt ){
+ /* this happens for a comment or white-space */
+ zSql = zLeftover;
+ continue;
+ }
+
+ db->nChange += nChange;
+ nCallback = 0;
+
+ nCol = sqlite3_column_count(pStmt);
+ azCols = sqliteMalloc(2*nCol*sizeof(const char *));
+ if( nCol && !azCols ){
+ rc = SQLITE_NOMEM;
+ goto exec_out;
+ }
+
+ while( 1 ){
+ int i;
+ rc = sqlite3_step(pStmt);
+
+ /* Invoke the callback function if required */
+ if( xCallback && (SQLITE_ROW==rc ||
+ (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){
+ if( 0==nCallback ){
+ for(i=0; i<nCol; i++){
+ azCols[i] = (char *)sqlite3_column_name(pStmt, i);
+ }
+ nCallback++;
+ }
+ if( rc==SQLITE_ROW ){
+ azVals = &azCols[nCol];
+ for(i=0; i<nCol; i++){
+ azVals[i] = (char *)sqlite3_column_text(pStmt, i);
+ }
+ }
+ if( xCallback(pArg, nCol, azVals, azCols) ){
+ rc = SQLITE_ABORT;
+ goto exec_out;
+ }
+ }
+
+ if( rc!=SQLITE_ROW ){
+ rc = sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( db->pVdbe==0 ){
+ nChange = db->nChange;
+ }
+ if( rc!=SQLITE_SCHEMA ){
+ nRetry = 0;
+ zSql = zLeftover;
+ while( isspace((unsigned char)zSql[0]) ) zSql++;
+ }
+ break;
+ }
+ }
+
+ sqliteFree(azCols);
+ azCols = 0;
+ }
+
+exec_out:
+ if( pStmt ) sqlite3_finalize(pStmt);
+ if( azCols ) sqliteFree(azCols);
+
+ if( sqlite3_malloc_failed ){
+ rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){
+ *pzErrMsg = malloc(1+strlen(sqlite3_errmsg(db)));
+ if( *pzErrMsg ){
+ strcpy(*pzErrMsg, sqlite3_errmsg(db));
+ }
+ }else if( pzErrMsg ){
+ *pzErrMsg = 0;
+ }
+
+ return rc;
+}
diff --git a/kopete/plugins/statistics/sqlite/lempar.c b/kopete/plugins/statistics/sqlite/lempar.c
new file mode 100644
index 00000000..ee1edbfa
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/lempar.c
@@ -0,0 +1,687 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is include which follows the "include" declaration
+** in the input file. */
+#include <stdio.h>
+%%
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+%%
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYFALLBACK If defined, this indicates that one or more tokens
+** have fall-back values which should be used if the
+** original value of the token will not parse.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** ParseTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is ParseTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack.
+** ParseARG_SDECL A static variable declaration for the %extra_argument
+** ParseARG_PDECL A parameter declaration for the %extra_argument
+** ParseARG_STORE Code to store %extra_argument into yypParser
+** ParseARG_FETCH Code to extract %extra_argument from yypParser
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+%%
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+
+/* Next are that tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+**
+** N == YYNSTATE+YYNRULE A syntax error has occurred.
+**
+** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+**
+** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+** yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** yy_action[] A single table containing all actions.
+** yy_lookahead[] A table containing the lookahead for each entry in
+** yy_action. Used to detect hash collisions.
+** yy_shift_ofst[] For each state, the offset into yy_action for
+** shifting terminals.
+** yy_reduce_ofst[] For each state, the offset into yy_action for
+** shifting non-terminals after a reduce.
+** yy_default[] Default action for each state.
+*/
+%%
+#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0]))
+
+/* The next table maps tokens into fallback tokens. If a construct
+** like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammer, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+%%
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ int stateno; /* The state-number */
+ int major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int yyidx; /* Index of top element in stack */
+ int yyerrcnt; /* Shifts left before out of the error */
+ ParseARG_SDECL /* A place to hold %extra_argument */
+ yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void ParseTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *yyTokenName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *yyRuleName[] = {
+%%
+};
+#endif /* NDEBUG */
+
+/*
+** This function returns the symbolic name associated with a token
+** value.
+*/
+const char *ParseTokenName(int tokenType){
+#ifndef NDEBUG
+ if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
+ return yyTokenName[tokenType];
+ }else{
+ return "Unknown";
+ }
+#else
+ return "";
+#endif
+}
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to Parse and ParseFree.
+*/
+void *ParseAlloc(void *(*mallocProc)(size_t)){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ if( pParser ){
+ pParser->yyidx = -1;
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+%%
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+ if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->yyidx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ yymajor = yytos->major;
+ yy_destructor( yymajor, &yytos->minor);
+ pParser->yyidx--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from ParseAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void ParseFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */
+ i = yy_shift_ofst[stateno];
+ if( i==YY_SHIFT_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ if( iLookAhead==YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+#ifdef YYFALLBACK
+ int iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
+#endif
+ return yy_find_shift_action(pParser, iFallback);
+ }
+#endif
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ i = yy_reduce_ofst[stateno];
+ if( i==YY_REDUCE_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ if( iLookAhead==YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+){
+ yyStackEntry *yytos;
+ yypParser->yyidx++;
+ if( yypParser->yyidx>=YYSTACKDEPTH ){
+ ParseARG_FETCH;
+ yypParser->yyidx--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument var */
+ return;
+ }
+ yytos = &yypParser->yystack[yypParser->yyidx];
+ yytos->stateno = yyNewState;
+ yytos->major = yyMajor;
+ yytos->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->yyidx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+%%
+};
+
+static void yy_accept(yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ ParseARG_FETCH;
+ yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+ if( yyTraceFILE && yyruleno>=0
+ && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){
+ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+ yyRuleName[yyruleno]);
+ }
+#endif /* NDEBUG */
+
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+%%
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->yyidx -= yysize;
+ yyact = yy_find_reduce_action(yypParser,yygoto);
+ if( yyact < YYNSTATE ){
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }else if( yyact == YYNSTATE + YYNRULE + 1 ){
+ yy_accept(yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+){
+ ParseARG_FETCH;
+#define TOKEN (yyminor.yy0)
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+){
+ ParseARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+%%
+ ParseARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "ParseAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void Parse(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ ParseTOKENTYPE yyminor /* The value for the token */
+ ParseARG_PDECL /* Optional %extra_argument parameter */
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->yyidx<0 ){
+ if( yymajor==0 ) return;
+ yypParser->yyidx = 0;
+ yypParser->yyerrcnt = -1;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+ ParseARG_STORE;
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_shift_action(yypParser,yymajor);
+ if( yyact<YYNSTATE ){
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->yyerrcnt--;
+ if( yyendofinput && yypParser->yyidx>=0 ){
+ yymajor = 0;
+ }else{
+ yymajor = YYNOCODE;
+ }
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE);
+ }else if( yyact == YY_ERROR_ACTION ){
+ int yymx;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->yyerrcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yymx = yypParser->yystack[yypParser->yyidx].major;
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->yyidx >= 0 &&
+ yymx != YYERRORSYMBOL &&
+ (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->yyidx < 0 || yymajor==0 ){
+ yy_destructor(yymajor,&yyminorunion);
+ yy_parse_failed(yypParser);
+ yymajor = YYNOCODE;
+ }else if( yymx!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->yyerrcnt = 3;
+ yyerrorhit = 1;
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->yyerrcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yypParser->yyerrcnt = 3;
+ yy_destructor(yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }else{
+ yy_accept(yypParser);
+ yymajor = YYNOCODE;
+ }
+ }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ return;
+}
diff --git a/kopete/plugins/statistics/sqlite/main.c b/kopete/plugins/statistics/sqlite/main.c
new file mode 100644
index 00000000..0ae7e1b2
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/main.c
@@ -0,0 +1,1346 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Main file for the SQLite library. The routines in this file
+** implement the programmer interface to the library. Routines in
+** other files are for internal use by SQLite and should not be
+** accessed by users of the library.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+
+/*
+** The following constant value is used by the SQLITE_BIGENDIAN and
+** SQLITE_LITTLEENDIAN macros.
+*/
+const int sqlite3one = 1;
+
+/*
+** Fill the InitData structure with an error message that indicates
+** that the database is corrupt.
+*/
+static void corruptSchema(InitData *pData, const char *zExtra){
+ if( !sqlite3_malloc_failed ){
+ sqlite3SetString(pData->pzErrMsg, "malformed database schema",
+ zExtra!=0 && zExtra[0]!=0 ? " - " : (char*)0, zExtra, (char*)0);
+ }
+}
+
+/*
+** This is the callback routine for the code that initializes the
+** database. See sqlite3Init() below for additional information.
+** This routine is also called from the OP_ParseSchema opcode of the VDBE.
+**
+** Each callback contains the following information:
+**
+** argv[0] = name of thing being created
+** argv[1] = root page number for table or index. NULL for trigger or view.
+** argv[2] = SQL text for the CREATE statement.
+** argv[3] = "1" for temporary files, "0" for main database, "2" or more
+** for auxiliary database files.
+**
+*/
+int sqlite3InitCallback(void *pInit, int argc, char **argv, char **azColName){
+ InitData *pData = (InitData*)pInit;
+ sqlite3 *db = pData->db;
+ int iDb;
+
+ assert( argc==4 );
+ if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
+ if( argv[1]==0 || argv[3]==0 ){
+ corruptSchema(pData, 0);
+ return 1;
+ }
+ iDb = atoi(argv[3]);
+ assert( iDb>=0 && iDb<db->nDb );
+ if( argv[2] && argv[2][0] ){
+ /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
+ ** But because db->init.busy is set to 1, no VDBE code is generated
+ ** or executed. All the parser does is build the internal data
+ ** structures that describe the table, index, or view.
+ */
+ char *zErr;
+ int rc;
+ assert( db->init.busy );
+ db->init.iDb = iDb;
+ db->init.newTnum = atoi(argv[1]);
+ rc = sqlite3_exec(db, argv[2], 0, 0, &zErr);
+ db->init.iDb = 0;
+ if( SQLITE_OK!=rc ){
+ corruptSchema(pData, zErr);
+ sqlite3_free(zErr);
+ return rc;
+ }
+ }else{
+ /* If the SQL column is blank it means this is an index that
+ ** was created to be the PRIMARY KEY or to fulfill a UNIQUE
+ ** constraint for a CREATE TABLE. The index should have already
+ ** been created when we processed the CREATE TABLE. All we have
+ ** to do here is record the root page number for that index.
+ */
+ Index *pIndex;
+ pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
+ if( pIndex==0 || pIndex->tnum!=0 ){
+ /* This can occur if there exists an index on a TEMP table which
+ ** has the same name as another index on a permanent index. Since
+ ** the permanent table is hidden by the TEMP table, we can also
+ ** safely ignore the index on the permanent table.
+ */
+ /* Do Nothing */;
+ }else{
+ pIndex->tnum = atoi(argv[1]);
+ }
+ }
+ return 0;
+}
+
+/*
+** Attempt to read the database schema and initialize internal
+** data structures for a single database file. The index of the
+** database file is given by iDb. iDb==0 is used for the main
+** database. iDb==1 should never be used. iDb>=2 is used for
+** auxiliary databases. Return one of the SQLITE_ error codes to
+** indicate success or failure.
+*/
+static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
+ int rc;
+ BtCursor *curMain;
+ int size;
+ Table *pTab;
+ char const *azArg[5];
+ char zDbNum[30];
+ int meta[10];
+ InitData initData;
+ char const *zMasterSchema;
+ char const *zMasterName;
+
+ /*
+ ** The master database table has a structure like this
+ */
+ static const char master_schema[] =
+ "CREATE TABLE sqlite_master(\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")"
+ ;
+ static const char temp_master_schema[] =
+ "CREATE TEMP TABLE sqlite_temp_master(\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")"
+ ;
+
+ assert( iDb>=0 && iDb<db->nDb );
+
+ /* zMasterSchema and zInitScript are set to point at the master schema
+ ** and initialisation script appropriate for the database being
+ ** initialised. zMasterName is the name of the master table.
+ */
+ if( iDb==1 ){
+ zMasterSchema = temp_master_schema;
+ zMasterName = TEMP_MASTER_NAME;
+ }else{
+ zMasterSchema = master_schema;
+ zMasterName = MASTER_NAME;
+ }
+
+ /* Construct the schema tables. */
+ sqlite3SafetyOff(db);
+ azArg[0] = zMasterName;
+ azArg[1] = "1";
+ azArg[2] = zMasterSchema;
+ sprintf(zDbNum, "%d", iDb);
+ azArg[3] = zDbNum;
+ azArg[4] = 0;
+ initData.db = db;
+ initData.pzErrMsg = pzErrMsg;
+ rc = sqlite3InitCallback(&initData, 4, (char **)azArg, 0);
+ if( rc!=SQLITE_OK ){
+ sqlite3SafetyOn(db);
+ return rc;
+ }
+ pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
+ if( pTab ){
+ pTab->readOnly = 1;
+ }
+ sqlite3SafetyOn(db);
+
+ /* Create a cursor to hold the database open
+ */
+ if( db->aDb[iDb].pBt==0 ){
+ if( iDb==1 ) DbSetProperty(db, 1, DB_SchemaLoaded);
+ return SQLITE_OK;
+ }
+ rc = sqlite3BtreeCursor(db->aDb[iDb].pBt, MASTER_ROOT, 0, 0, 0, &curMain);
+ if( rc!=SQLITE_OK && rc!=SQLITE_EMPTY ){
+ sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
+ return rc;
+ }
+
+ /* Get the database meta information.
+ **
+ ** Meta values are as follows:
+ ** meta[0] Schema cookie. Changes with each schema change.
+ ** meta[1] File format of schema layer.
+ ** meta[2] Size of the page cache.
+ ** meta[3] Use freelist if 0. Autovacuum if greater than zero.
+ ** meta[4] Db text encoding. 1:UTF-8 3:UTF-16 LE 4:UTF-16 BE
+ ** meta[5]
+ ** meta[6]
+ ** meta[7]
+ ** meta[8]
+ ** meta[9]
+ **
+ ** Note: The hash defined SQLITE_UTF* symbols in sqliteInt.h correspond to
+ ** the possible values of meta[4].
+ */
+ if( rc==SQLITE_OK ){
+ int i;
+ for(i=0; rc==SQLITE_OK && i<sizeof(meta)/sizeof(meta[0]); i++){
+ rc = sqlite3BtreeGetMeta(db->aDb[iDb].pBt, i+1, (u32 *)&meta[i]);
+ }
+ if( rc ){
+ sqlite3SetString(pzErrMsg, sqlite3ErrStr(rc), (char*)0);
+ sqlite3BtreeCloseCursor(curMain);
+ return rc;
+ }
+ }else{
+ memset(meta, 0, sizeof(meta));
+ }
+ db->aDb[iDb].schema_cookie = meta[0];
+
+ /* If opening a non-empty database, check the text encoding. For the
+ ** main database, set sqlite3.enc to the encoding of the main database.
+ ** For an attached db, it is an error if the encoding is not the same
+ ** as sqlite3.enc.
+ */
+ if( meta[4] ){ /* text encoding */
+ if( iDb==0 ){
+ /* If opening the main database, set db->enc. */
+ db->enc = (u8)meta[4];
+ db->pDfltColl = sqlite3FindCollSeq(db, db->enc, "BINARY", 6, 0);
+ }else{
+ /* If opening an attached database, the encoding much match db->enc */
+ if( meta[4]!=db->enc ){
+ sqlite3BtreeCloseCursor(curMain);
+ sqlite3SetString(pzErrMsg, "attached databases must use the same"
+ " text encoding as main database", (char*)0);
+ return SQLITE_ERROR;
+ }
+ }
+ }
+
+ size = meta[2];
+ if( size==0 ){ size = MAX_PAGES; }
+ db->aDb[iDb].cache_size = size;
+
+ if( iDb==0 ){
+ db->file_format = meta[1];
+ if( db->file_format==0 ){
+ /* This happens if the database was initially empty */
+ db->file_format = 1;
+ }
+ }
+
+ /*
+ ** file_format==1 Version 3.0.0.
+ */
+ if( meta[1]>1 ){
+ sqlite3BtreeCloseCursor(curMain);
+ sqlite3SetString(pzErrMsg, "unsupported file format", (char*)0);
+ return SQLITE_ERROR;
+ }
+
+ sqlite3BtreeSetCacheSize(db->aDb[iDb].pBt, db->aDb[iDb].cache_size);
+
+ /* Read the schema information out of the schema tables
+ */
+ assert( db->init.busy );
+ if( rc==SQLITE_EMPTY ){
+ /* For an empty database, there is nothing to read */
+ rc = SQLITE_OK;
+ }else{
+ char *zSql;
+ zSql = sqlite3MPrintf(
+ "SELECT name, rootpage, sql, %s FROM '%q'.%s",
+ zDbNum, db->aDb[iDb].zName, zMasterName);
+ sqlite3SafetyOff(db);
+ rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+ sqlite3SafetyOn(db);
+ sqliteFree(zSql);
+ sqlite3BtreeCloseCursor(curMain);
+ }
+ if( sqlite3_malloc_failed ){
+ sqlite3SetString(pzErrMsg, "out of memory", (char*)0);
+ rc = SQLITE_NOMEM;
+ sqlite3ResetInternalSchema(db, 0);
+ }
+ if( rc==SQLITE_OK ){
+ DbSetProperty(db, iDb, DB_SchemaLoaded);
+ }else{
+ sqlite3ResetInternalSchema(db, iDb);
+ }
+ return rc;
+}
+
+/*
+** Initialize all database files - the main database file, the file
+** used to store temporary tables, and any additional database files
+** created using ATTACH statements. Return a success code. If an
+** error occurs, write an error message into *pzErrMsg.
+**
+** After the database is initialized, the SQLITE_Initialized
+** bit is set in the flags field of the sqlite structure.
+*/
+int sqlite3Init(sqlite3 *db, char **pzErrMsg){
+ int i, rc;
+
+ if( db->init.busy ) return SQLITE_OK;
+ assert( (db->flags & SQLITE_Initialized)==0 );
+ rc = SQLITE_OK;
+ db->init.busy = 1;
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
+ rc = sqlite3InitOne(db, i, pzErrMsg);
+ if( rc ){
+ sqlite3ResetInternalSchema(db, i);
+ }
+ }
+
+ /* Once all the other databases have been initialised, load the schema
+ ** for the TEMP database. This is loaded last, as the TEMP database
+ ** schema may contain references to objects in other databases.
+ */
+ if( rc==SQLITE_OK && db->nDb>1 && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
+ rc = sqlite3InitOne(db, 1, pzErrMsg);
+ if( rc ){
+ sqlite3ResetInternalSchema(db, 1);
+ }
+ }
+
+ db->init.busy = 0;
+ if( rc==SQLITE_OK ){
+ db->flags |= SQLITE_Initialized;
+ sqlite3CommitInternalChanges(db);
+ }
+
+ if( rc!=SQLITE_OK ){
+ db->flags &= ~SQLITE_Initialized;
+ }
+ return rc;
+}
+
+/*
+** This routine is a no-op if the database schema is already initialised.
+** Otherwise, the schema is loaded. An error code is returned.
+*/
+int sqlite3ReadSchema(Parse *pParse){
+ int rc = SQLITE_OK;
+ sqlite3 *db = pParse->db;
+ if( !db->init.busy ){
+ if( (db->flags & SQLITE_Initialized)==0 ){
+ rc = sqlite3Init(db, &pParse->zErrMsg);
+ }
+ }
+ assert( rc!=SQLITE_OK || (db->flags & SQLITE_Initialized)||db->init.busy );
+ if( rc!=SQLITE_OK ){
+ pParse->rc = rc;
+ pParse->nErr++;
+ }
+ return rc;
+}
+
+/*
+** The version of the library
+*/
+const char rcsid3[] = "@(#) \044Id: SQLite version " SQLITE_VERSION " $";
+const char sqlite3_version[] = SQLITE_VERSION;
+const char *sqlite3_libversion(void){ return sqlite3_version; }
+
+/*
+** This is the default collating function named "BINARY" which is always
+** available.
+*/
+static int binaryCollatingFunc(
+ void *NotUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ int rc, n;
+ n = nKey1<nKey2 ? nKey1 : nKey2;
+ rc = memcmp(pKey1, pKey2, n);
+ if( rc==0 ){
+ rc = nKey1 - nKey2;
+ }
+ return rc;
+}
+
+/*
+** Another built-in collating sequence: NOCASE.
+**
+** This collating sequence is intended to be used for "case independant
+** comparison". SQLite's knowledge of upper and lower case equivalents
+** extends only to the 26 characters used in the English language.
+**
+** At the moment there is only a UTF-8 implementation.
+*/
+static int nocaseCollatingFunc(
+ void *NotUsed,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ int r = sqlite3StrNICmp(
+ (const char *)pKey1, (const char *)pKey2, (nKey1<nKey2)?nKey1:nKey2);
+ if( 0==r ){
+ r = nKey1-nKey2;
+ }
+ return r;
+}
+
+/*
+** Return the ROWID of the most recent insert
+*/
+sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
+ return db->lastRowid;
+}
+
+/*
+** Return the number of changes in the most recent call to sqlite3_exec().
+*/
+int sqlite3_changes(sqlite3 *db){
+ return db->nChange;
+}
+
+/*
+** Return the number of changes since the database handle was opened.
+*/
+int sqlite3_total_changes(sqlite3 *db){
+ return db->nTotalChange;
+}
+
+/*
+** Close an existing SQLite database
+*/
+int sqlite3_close(sqlite3 *db){
+ HashElem *i;
+ int j;
+
+ if( !db ){
+ return SQLITE_OK;
+ }
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+
+ /* If there are any outstanding VMs, return SQLITE_BUSY. */
+ if( db->pVdbe ){
+ sqlite3Error(db, SQLITE_BUSY,
+ "Unable to close due to unfinalised statements");
+ return SQLITE_BUSY;
+ }
+ assert( !sqlite3SafetyCheck(db) );
+
+ /* FIX ME: db->magic may be set to SQLITE_MAGIC_CLOSED if the database
+ ** cannot be opened for some reason. So this routine needs to run in
+ ** that case. But maybe there should be an extra magic value for the
+ ** "failed to open" state.
+ */
+ if( db->magic!=SQLITE_MAGIC_CLOSED && sqlite3SafetyOn(db) ){
+ /* printf("DID NOT CLOSE\n"); fflush(stdout); */
+ return SQLITE_ERROR;
+ }
+
+ for(j=0; j<db->nDb; j++){
+ struct Db *pDb = &db->aDb[j];
+ if( pDb->pBt ){
+ sqlite3BtreeClose(pDb->pBt);
+ pDb->pBt = 0;
+ }
+ }
+ sqlite3ResetInternalSchema(db, 0);
+ assert( db->nDb<=2 );
+ assert( db->aDb==db->aDbStatic );
+ for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
+ FuncDef *pFunc, *pNext;
+ for(pFunc = (FuncDef*)sqliteHashData(i); pFunc; pFunc=pNext){
+ pNext = pFunc->pNext;
+ sqliteFree(pFunc);
+ }
+ }
+
+ for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
+ CollSeq *pColl = (CollSeq *)sqliteHashData(i);
+ sqliteFree(pColl);
+ }
+ sqlite3HashClear(&db->aCollSeq);
+
+ sqlite3HashClear(&db->aFunc);
+ sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
+ if( db->pValue ){
+ sqlite3ValueFree(db->pValue);
+ }
+ if( db->pErr ){
+ sqlite3ValueFree(db->pErr);
+ }
+
+ db->magic = SQLITE_MAGIC_ERROR;
+ sqliteFree(db);
+ return SQLITE_OK;
+}
+
+/*
+** Rollback all database files.
+*/
+void sqlite3RollbackAll(sqlite3 *db){
+ int i;
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt ){
+ sqlite3BtreeRollback(db->aDb[i].pBt);
+ db->aDb[i].inTrans = 0;
+ }
+ }
+ sqlite3ResetInternalSchema(db, 0);
+}
+
+/*
+** Return a static string that describes the kind of error specified in the
+** argument.
+*/
+const char *sqlite3ErrStr(int rc){
+ const char *z;
+ switch( rc ){
+ case SQLITE_ROW:
+ case SQLITE_DONE:
+ case SQLITE_OK: z = "not an error"; break;
+ case SQLITE_ERROR: z = "SQL logic error or missing database"; break;
+ case SQLITE_INTERNAL: z = "internal SQLite implementation flaw"; break;
+ case SQLITE_PERM: z = "access permission denied"; break;
+ case SQLITE_ABORT: z = "callback requested query abort"; break;
+ case SQLITE_BUSY: z = "database is locked"; break;
+ case SQLITE_LOCKED: z = "database table is locked"; break;
+ case SQLITE_NOMEM: z = "out of memory"; break;
+ case SQLITE_READONLY: z = "attempt to write a readonly database"; break;
+ case SQLITE_INTERRUPT: z = "interrupted"; break;
+ case SQLITE_IOERR: z = "disk I/O error"; break;
+ case SQLITE_CORRUPT: z = "database disk image is malformed"; break;
+ case SQLITE_NOTFOUND: z = "table or record not found"; break;
+ case SQLITE_FULL: z = "database is full"; break;
+ case SQLITE_CANTOPEN: z = "unable to open database file"; break;
+ case SQLITE_PROTOCOL: z = "database locking protocol failure"; break;
+ case SQLITE_EMPTY: z = "table contains no data"; break;
+ case SQLITE_SCHEMA: z = "database schema has changed"; break;
+ case SQLITE_TOOBIG: z = "too much data for one table row"; break;
+ case SQLITE_CONSTRAINT: z = "constraint failed"; break;
+ case SQLITE_MISMATCH: z = "datatype mismatch"; break;
+ case SQLITE_MISUSE: z = "library routine called out of sequence";break;
+ case SQLITE_NOLFS: z = "kernel lacks large file support"; break;
+ case SQLITE_AUTH: z = "authorization denied"; break;
+ case SQLITE_FORMAT: z = "auxiliary database format error"; break;
+ case SQLITE_RANGE: z = "bind index out of range"; break;
+ case SQLITE_NOTADB: z = "file is encrypted or is not a database";break;
+ default: z = "unknown error"; break;
+ }
+ return z;
+}
+
+/*
+** This routine implements a busy callback that sleeps and tries
+** again until a timeout value is reached. The timeout value is
+** an integer number of milliseconds passed in as the first
+** argument.
+*/
+static int sqliteDefaultBusyCallback(
+ void *Timeout, /* Maximum amount of time to wait */
+ int count /* Number of times table has been busy */
+){
+#if SQLITE_MIN_SLEEP_MS==1
+ static const char delays[] =
+ { 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 50, 100};
+ static const short int totals[] =
+ { 0, 1, 3, 8, 18, 33, 53, 78, 103, 128, 178, 228, 287};
+# define NDELAY (sizeof(delays)/sizeof(delays[0]))
+ ptr timeout = (ptr)Timeout;
+ ptr delay, prior;
+
+ if( count <= NDELAY ){
+ delay = delays[count-1];
+ prior = totals[count-1];
+ }else{
+ delay = delays[NDELAY-1];
+ prior = totals[NDELAY-1] + delay*(count-NDELAY-1);
+ }
+ if( prior + delay > timeout ){
+ delay = timeout - prior;
+ if( delay<=0 ) return 0;
+ }
+ sqlite3OsSleep(delay);
+ return 1;
+#else
+ int timeout = (int)Timeout;
+ if( (count+1)*1000 > timeout ){
+ return 0;
+ }
+ sqlite3OsSleep(1000);
+ return 1;
+#endif
+}
+
+/*
+** This routine sets the busy callback for an Sqlite database to the
+** given callback function with the given argument.
+*/
+int sqlite3_busy_handler(
+ sqlite3 *db,
+ int (*xBusy)(void*,int),
+ void *pArg
+){
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ db->busyHandler.xFunc = xBusy;
+ db->busyHandler.pArg = pArg;
+ return SQLITE_OK;
+}
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+/*
+** This routine sets the progress callback for an Sqlite database to the
+** given callback function with the given argument. The progress callback will
+** be invoked every nOps opcodes.
+*/
+void sqlite3_progress_handler(
+ sqlite3 *db,
+ int nOps,
+ int (*xProgress)(void*),
+ void *pArg
+){
+ if( !sqlite3SafetyCheck(db) ){
+ if( nOps>0 ){
+ db->xProgress = xProgress;
+ db->nProgressOps = nOps;
+ db->pProgressArg = pArg;
+ }else{
+ db->xProgress = 0;
+ db->nProgressOps = 0;
+ db->pProgressArg = 0;
+ }
+ }
+}
+#endif
+
+
+/*
+** This routine installs a default busy handler that waits for the
+** specified number of milliseconds before returning 0.
+*/
+int sqlite3_busy_timeout(sqlite3 *db, int ms){
+ if( ms>0 ){
+ sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)(ptr)ms);
+ }else{
+ sqlite3_busy_handler(db, 0, 0);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Cause any pending operation to stop at its earliest opportunity.
+*/
+void sqlite3_interrupt(sqlite3 *db){
+ if( !sqlite3SafetyCheck(db) ){
+ db->flags |= SQLITE_Interrupt;
+ }
+}
+
+/*
+** Windows systems should call this routine to free memory that
+** is returned in the in the errmsg parameter of sqlite3_open() when
+** SQLite is a DLL. For some reason, it does not work to call free()
+** directly.
+**
+** Note that we need to call free() not sqliteFree() here.
+*/
+void sqlite3_free(char *p){ free(p); }
+
+/*
+** Create new user functions.
+*/
+int sqlite3_create_function(
+ sqlite3 *db,
+ const char *zFunctionName,
+ int nArg,
+ int enc,
+ void *pUserData,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value **),
+ void (*xFinal)(sqlite3_context*)
+){
+ FuncDef *p;
+ int nName;
+
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ if( zFunctionName==0 ||
+ (xFunc && (xFinal || xStep)) ||
+ (!xFunc && (xFinal && !xStep)) ||
+ (!xFunc && (!xFinal && xStep)) ||
+ (nArg<-1 || nArg>127) ||
+ (255<(nName = strlen(zFunctionName))) ){
+ return SQLITE_ERROR;
+ }
+
+ /* If SQLITE_UTF16 is specified as the encoding type, transform this
+ ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the
+ ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally.
+ **
+ ** If SQLITE_ANY is specified, add three versions of the function
+ ** to the hash table.
+ */
+ if( enc==SQLITE_UTF16 ){
+ enc = SQLITE_UTF16NATIVE;
+ }else if( enc==SQLITE_ANY ){
+ int rc;
+ rc = sqlite3_create_function(db, zFunctionName, nArg, SQLITE_UTF8,
+ pUserData, xFunc, xStep, xFinal);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3_create_function(db, zFunctionName, nArg, SQLITE_UTF16LE,
+ pUserData, xFunc, xStep, xFinal);
+ if( rc!=SQLITE_OK ) return rc;
+ enc = SQLITE_UTF16BE;
+ }
+
+ p = sqlite3FindFunction(db, zFunctionName, nName, nArg, enc, 1);
+ if( p==0 ) return SQLITE_NOMEM;
+ p->xFunc = xFunc;
+ p->xStep = xStep;
+ p->xFinalize = xFinal;
+ p->pUserData = pUserData;
+ return SQLITE_OK;
+}
+int sqlite3_create_function16(
+ sqlite3 *db,
+ const void *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void *pUserData,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+){
+ int rc;
+ char const *zFunc8;
+ sqlite3_value *pTmp;
+
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ pTmp = sqlite3GetTransientValue(db);
+ sqlite3ValueSetStr(pTmp, -1, zFunctionName, SQLITE_UTF16NATIVE,SQLITE_STATIC);
+ zFunc8 = sqlite3ValueText(pTmp, SQLITE_UTF8);
+
+ if( !zFunc8 ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_create_function(db, zFunc8, nArg, eTextRep,
+ pUserData, xFunc, xStep, xFinal);
+ return rc;
+}
+
+/*
+** Register a trace function. The pArg from the previously registered trace
+** is returned.
+**
+** A NULL trace function means that no tracing is executes. A non-NULL
+** trace is a pointer to a function that is invoked at the start of each
+** sqlite3_exec().
+*/
+void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
+ void *pOld = db->pTraceArg;
+ db->xTrace = xTrace;
+ db->pTraceArg = pArg;
+ return pOld;
+}
+
+/*** EXPERIMENTAL ***
+**
+** Register a function to be invoked when a transaction comments.
+** If either function returns non-zero, then the commit becomes a
+** rollback.
+*/
+void *sqlite3_commit_hook(
+ sqlite3 *db, /* Attach the hook to this database */
+ int (*xCallback)(void*), /* Function to invoke on each commit */
+ void *pArg /* Argument to the function */
+){
+ void *pOld = db->pCommitArg;
+ db->xCommitCallback = xCallback;
+ db->pCommitArg = pArg;
+ return pOld;
+}
+
+
+/*
+** This routine is called to create a connection to a database BTree
+** driver. If zFilename is the name of a file, then that file is
+** opened and used. If zFilename is the magic name ":memory:" then
+** the database is stored in memory (and is thus forgotten as soon as
+** the connection is closed.) If zFilename is NULL then the database
+** is for temporary use only and is deleted as soon as the connection
+** is closed.
+**
+** A temporary database can be either a disk file (that is automatically
+** deleted when the file is closed) or a set of red-black trees held in memory,
+** depending on the values of the TEMP_STORE compile-time macro and the
+** db->temp_store variable, according to the following chart:
+**
+** TEMP_STORE db->temp_store Location of temporary database
+** ---------- -------------- ------------------------------
+** 0 any file
+** 1 1 file
+** 1 2 memory
+** 1 0 file
+** 2 1 file
+** 2 2 memory
+** 2 0 memory
+** 3 any memory
+*/
+int sqlite3BtreeFactory(
+ const sqlite3 *db, /* Main database when opening aux otherwise 0 */
+ const char *zFilename, /* Name of the file containing the BTree database */
+ int omitJournal, /* if TRUE then do not journal this file */
+ int nCache, /* How many pages in the page cache */
+ Btree **ppBtree /* Pointer to new Btree object written here */
+){
+ int btree_flags = 0;
+ int rc;
+
+ assert( ppBtree != 0);
+ if( omitJournal ){
+ btree_flags |= BTREE_OMIT_JOURNAL;
+ }
+ if( zFilename==0 ){
+#ifndef TEMP_STORE
+# define TEMP_STORE 1
+#endif
+#if TEMP_STORE==0
+ /* Do nothing */
+#endif
+#if TEMP_STORE==1
+ if( db->temp_store==2 ) zFilename = ":memory:";
+#endif
+#if TEMP_STORE==2
+ if( db->temp_store!=1 ) zFilename = ":memory:";
+#endif
+#if TEMP_STORE==3
+ zFilename = ":memory:";
+#endif
+ }
+
+ rc = sqlite3BtreeOpen(zFilename, ppBtree, btree_flags);
+ if( rc==SQLITE_OK ){
+ sqlite3BtreeSetBusyHandler(*ppBtree, (void*)&db->busyHandler);
+ sqlite3BtreeSetCacheSize(*ppBtree, nCache);
+ }
+ return rc;
+}
+
+/*
+** Return UTF-8 encoded English language explanation of the most recent
+** error.
+*/
+const char *sqlite3_errmsg(sqlite3 *db){
+ const char *z;
+ if( sqlite3_malloc_failed ){
+ return sqlite3ErrStr(SQLITE_NOMEM);
+ }
+ if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){
+ return sqlite3ErrStr(SQLITE_MISUSE);
+ }
+ z = sqlite3_value_text(db->pErr);
+ if( z==0 ){
+ z = sqlite3ErrStr(db->errCode);
+ }
+ return z;
+}
+
+/*
+** Return UTF-16 encoded English language explanation of the most recent
+** error.
+*/
+const void *sqlite3_errmsg16(sqlite3 *db){
+ /* Because all the characters in the string are in the unicode
+ ** range 0x00-0xFF, if we pad the big-endian string with a
+ ** zero byte, we can obtain the little-endian string with
+ ** &big_endian[1].
+ */
+ static const char outOfMemBe[] = {
+ 0, 'o', 0, 'u', 0, 't', 0, ' ',
+ 0, 'o', 0, 'f', 0, ' ',
+ 0, 'm', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 0, 0
+ };
+ static const char misuseBe [] = {
+ 0, 'l', 0, 'i', 0, 'b', 0, 'r', 0, 'a', 0, 'r', 0, 'y', 0, ' ',
+ 0, 'r', 0, 'o', 0, 'u', 0, 't', 0, 'i', 0, 'n', 0, 'e', 0, ' ',
+ 0, 'c', 0, 'a', 0, 'l', 0, 'l', 0, 'e', 0, 'd', 0, ' ',
+ 0, 'o', 0, 'u', 0, 't', 0, ' ',
+ 0, 'o', 0, 'f', 0, ' ',
+ 0, 's', 0, 'e', 0, 'q', 0, 'u', 0, 'e', 0, 'n', 0, 'c', 0, 'e', 0, 0, 0
+ };
+
+ const void *z;
+ if( sqlite3_malloc_failed ){
+ return (void *)(&outOfMemBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
+ }
+ if( sqlite3SafetyCheck(db) || db->errCode==SQLITE_MISUSE ){
+ return (void *)(&misuseBe[SQLITE_UTF16NATIVE==SQLITE_UTF16LE?1:0]);
+ }
+ z = sqlite3_value_text16(db->pErr);
+ if( z==0 ){
+ sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
+ SQLITE_UTF8, SQLITE_STATIC);
+ z = sqlite3_value_text16(db->pErr);
+ }
+ return z;
+}
+
+/*
+** Return the most recent error code generated by an SQLite routine.
+*/
+int sqlite3_errcode(sqlite3 *db){
+ if( sqlite3_malloc_failed ){
+ return SQLITE_NOMEM;
+ }
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ return db->errCode;
+}
+
+/*
+** Check schema cookies in all databases. If any cookie is out
+** of date, return 0. If all schema cookies are current, return 1.
+*/
+static int schemaIsValid(sqlite3 *db){
+ int iDb;
+ int rc;
+ BtCursor *curTemp;
+ int cookie;
+ int allOk = 1;
+
+ for(iDb=0; allOk && iDb<db->nDb; iDb++){
+ Btree *pBt;
+ pBt = db->aDb[iDb].pBt;
+ if( pBt==0 ) continue;
+ rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, 0, &curTemp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&cookie);
+ if( rc==SQLITE_OK && cookie!=db->aDb[iDb].schema_cookie ){
+ allOk = 0;
+ }
+ sqlite3BtreeCloseCursor(curTemp);
+ }
+ }
+ return allOk;
+}
+
+/*
+** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
+*/
+int sqlite3_prepare(
+ sqlite3 *db, /* Database handle. */
+ const char *zSql, /* UTF-8 encoded SQL statement. */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
+ const char** pzTail /* OUT: End of parsed string */
+){
+ Parse sParse;
+ char *zErrMsg = 0;
+ int rc = SQLITE_OK;
+
+ if( sqlite3_malloc_failed ){
+ return SQLITE_NOMEM;
+ }
+
+ assert( ppStmt );
+ *ppStmt = 0;
+ if( sqlite3SafetyOn(db) ){
+ return SQLITE_MISUSE;
+ }
+
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = db;
+ sqlite3RunParser(&sParse, zSql, &zErrMsg);
+
+ if( sqlite3_malloc_failed ){
+ rc = SQLITE_NOMEM;
+ sqlite3RollbackAll(db);
+ sqlite3ResetInternalSchema(db, 0);
+ db->flags &= ~SQLITE_InTrans;
+ goto prepare_out;
+ }
+ if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
+ if( sParse.rc!=SQLITE_OK && sParse.checkSchema && !schemaIsValid(db) ){
+ sParse.rc = SQLITE_SCHEMA;
+ }
+ if( sParse.rc==SQLITE_SCHEMA ){
+ sqlite3ResetInternalSchema(db, 0);
+ }
+ if( pzTail ) *pzTail = sParse.zTail;
+ rc = sParse.rc;
+
+ if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 5);
+ sqlite3VdbeSetColName(sParse.pVdbe, 0, "addr", P3_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 1, "opcode", P3_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 2, "p1", P3_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 3, "p2", P3_STATIC);
+ sqlite3VdbeSetColName(sParse.pVdbe, 4, "p3", P3_STATIC);
+ }
+
+prepare_out:
+ if( sqlite3SafetyOff(db) ){
+ rc = SQLITE_MISUSE;
+ }
+ if( rc==SQLITE_OK ){
+ *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
+ }else if( sParse.pVdbe ){
+ sqlite3_finalize((sqlite3_stmt*)sParse.pVdbe);
+ }
+
+ if( zErrMsg ){
+ sqlite3Error(db, rc, "%s", zErrMsg);
+ sqliteFree(zErrMsg);
+ }else{
+ sqlite3Error(db, rc, 0);
+ }
+ return rc;
+}
+
+/*
+** Compile the UTF-16 encoded SQL statement zSql into a statement handle.
+*/
+int sqlite3_prepare16(
+ sqlite3 *db, /* Database handle. */
+ const void *zSql, /* UTF-8 encoded SQL statement. */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
+ const void **pzTail /* OUT: End of parsed string */
+){
+ /* This function currently works by first transforming the UTF-16
+ ** encoded string to UTF-8, then invoking sqlite3_prepare(). The
+ ** tricky bit is figuring out the pointer to return in *pzTail.
+ */
+ char const *zSql8 = 0;
+ char const *zTail8 = 0;
+ int rc;
+ sqlite3_value *pTmp;
+
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ pTmp = sqlite3GetTransientValue(db);
+ sqlite3ValueSetStr(pTmp, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
+ zSql8 = sqlite3ValueText(pTmp, SQLITE_UTF8);
+ if( !zSql8 ){
+ sqlite3Error(db, SQLITE_NOMEM, 0);
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_prepare(db, zSql8, -1, ppStmt, &zTail8);
+
+ if( zTail8 && pzTail ){
+ /* If sqlite3_prepare returns a tail pointer, we calculate the
+ ** equivalent pointer into the UTF-16 string by counting the unicode
+ ** characters between zSql8 and zTail8, and then returning a pointer
+ ** the same number of characters into the UTF-16 string.
+ */
+ int chars_parsed = sqlite3utf8CharLen(zSql8, zTail8-zSql8);
+ *pzTail = (u8 *)zSql + sqlite3utf16ByteLen(zSql, chars_parsed);
+ }
+
+ return rc;
+}
+
+/*
+** This routine does the work of opening a database on behalf of
+** sqlite3_open() and sqlite3_open16(). The database filename "zFilename"
+** is UTF-8 encoded. The fourth argument, "def_enc" is one of the TEXT_*
+** macros from sqliteInt.h. If we end up creating a new database file
+** (not opening an existing one), the text encoding of the database
+** will be set to this value.
+*/
+static int openDatabase(
+ const char *zFilename, /* Database filename UTF-8 encoded */
+ sqlite3 **ppDb /* OUT: Returned database handle */
+){
+ sqlite3 *db;
+ int rc, i;
+ char *zErrMsg = 0;
+
+ /* Allocate the sqlite data structure */
+ db = sqliteMalloc( sizeof(sqlite3) );
+ if( db==0 ) goto opendb_out;
+ db->priorNewRowid = 0;
+ db->magic = SQLITE_MAGIC_BUSY;
+ db->nDb = 2;
+ db->aDb = db->aDbStatic;
+ db->enc = SQLITE_UTF8;
+ db->autoCommit = 1;
+ /* db->flags |= SQLITE_ShortColNames; */
+ sqlite3HashInit(&db->aFunc, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&db->aCollSeq, SQLITE_HASH_STRING, 0);
+ for(i=0; i<db->nDb; i++){
+ sqlite3HashInit(&db->aDb[i].tblHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&db->aDb[i].idxHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&db->aDb[i].trigHash, SQLITE_HASH_STRING, 0);
+ sqlite3HashInit(&db->aDb[i].aFKey, SQLITE_HASH_STRING, 1);
+ }
+
+ /* Add the default collation sequence BINARY. BINARY works for both UTF-8
+ ** and UTF-16, so add a version for each to avoid any unnecessary
+ ** conversions. The only error that can occur here is a malloc() failure.
+ */
+ sqlite3_create_collation(db, "BINARY", SQLITE_UTF8, 0,binaryCollatingFunc);
+ sqlite3_create_collation(db, "BINARY", SQLITE_UTF16LE, 0,binaryCollatingFunc);
+ sqlite3_create_collation(db, "BINARY", SQLITE_UTF16BE, 0,binaryCollatingFunc);
+ db->pDfltColl = sqlite3FindCollSeq(db, db->enc, "BINARY", 6, 0);
+ if( !db->pDfltColl ){
+ rc = db->errCode;
+ assert( rc!=SQLITE_OK );
+ db->magic = SQLITE_MAGIC_CLOSED;
+ goto opendb_out;
+ }
+
+ /* Also add a UTF-8 case-insensitive collation sequence. */
+ sqlite3_create_collation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc);
+
+ /* Open the backend database driver */
+ rc = sqlite3BtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt);
+ if( rc!=SQLITE_OK ){
+ sqlite3Error(db, rc, 0);
+ db->magic = SQLITE_MAGIC_CLOSED;
+ goto opendb_out;
+ }
+ db->aDb[0].zName = "main";
+ db->aDb[1].zName = "temp";
+
+ /* The default safety_level for the main database is 'full' for the temp
+ ** database it is 'NONE'. This matches the pager layer defaults. */
+ db->aDb[0].safety_level = 3;
+ db->aDb[1].safety_level = 1;
+
+ /* Register all built-in functions, but do not attempt to read the
+ ** database schema yet. This is delayed until the first time the database
+ ** is accessed.
+ */
+ sqlite3RegisterBuiltinFunctions(db);
+ if( rc==SQLITE_OK ){
+ sqlite3Error(db, SQLITE_OK, 0);
+ db->magic = SQLITE_MAGIC_OPEN;
+ }else{
+ sqlite3Error(db, rc, "%s", zErrMsg, 0);
+ if( zErrMsg ) sqliteFree(zErrMsg);
+ db->magic = SQLITE_MAGIC_CLOSED;
+ }
+
+opendb_out:
+ if( sqlite3_errcode(db)==SQLITE_OK && sqlite3_malloc_failed ){
+ sqlite3Error(db, SQLITE_NOMEM, 0);
+ }
+ *ppDb = db;
+ return sqlite3_errcode(db);
+}
+
+/*
+** Open a new database handle.
+*/
+int sqlite3_open(
+ const char *zFilename,
+ sqlite3 **ppDb
+){
+ return openDatabase(zFilename, ppDb);
+}
+
+/*
+** Open a new database handle.
+*/
+int sqlite3_open16(
+ const void *zFilename,
+ sqlite3 **ppDb
+){
+ char const *zFilename8; /* zFilename encoded in UTF-8 instead of UTF-16 */
+ int rc = SQLITE_NOMEM;
+ sqlite3_value *pVal;
+
+ assert( ppDb );
+ *ppDb = 0;
+ pVal = sqlite3ValueNew();
+ sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC);
+ zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8);
+ if( zFilename8 ){
+ rc = openDatabase(zFilename8, ppDb);
+ if( rc==SQLITE_OK && *ppDb ){
+ sqlite3_exec(*ppDb, "PRAGMA encoding = 'UTF-16'", 0, 0, 0);
+ }
+ }
+ if( pVal ){
+ sqlite3ValueFree(pVal);
+ }
+
+ return rc;
+}
+
+/*
+** The following routine destroys a virtual machine that is created by
+** the sqlite3_compile() routine. The integer returned is an SQLITE_
+** success/failure code that describes the result of executing the virtual
+** machine.
+**
+** This routine sets the error code and string returned by
+** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
+*/
+int sqlite3_finalize(sqlite3_stmt *pStmt){
+ int rc;
+ if( pStmt==0 ){
+ rc = SQLITE_OK;
+ }else{
+ rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
+ }
+ return rc;
+}
+
+/*
+** Terminate the current execution of an SQL statement and reset it
+** back to its starting state so that it can be reused. A success code from
+** the prior execution is returned.
+**
+** This routine sets the error code and string returned by
+** sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16().
+*/
+int sqlite3_reset(sqlite3_stmt *pStmt){
+ int rc;
+ if( pStmt==0 ){
+ rc = SQLITE_OK;
+ }else{
+ rc = sqlite3VdbeReset((Vdbe*)pStmt);
+ sqlite3VdbeMakeReady((Vdbe*)pStmt, -1, 0, 0, 0);
+ }
+ return rc;
+}
+
+/*
+** Register a new collation sequence with the database handle db.
+*/
+int sqlite3_create_collation(
+ sqlite3* db,
+ const char *zName,
+ int enc,
+ void* pCtx,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+){
+ CollSeq *pColl;
+ int rc = SQLITE_OK;
+
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+
+ /* If SQLITE_UTF16 is specified as the encoding type, transform this
+ ** to one of SQLITE_UTF16LE or SQLITE_UTF16BE using the
+ ** SQLITE_UTF16NATIVE macro. SQLITE_UTF16 is not used internally.
+ */
+ if( enc==SQLITE_UTF16 ){
+ enc = SQLITE_UTF16NATIVE;
+ }
+
+ if( enc!=SQLITE_UTF8 && enc!=SQLITE_UTF16LE && enc!=SQLITE_UTF16BE ){
+ sqlite3Error(db, SQLITE_ERROR,
+ "Param 3 to sqlite3_create_collation() must be one of "
+ "SQLITE_UTF8, SQLITE_UTF16, SQLITE_UTF16LE or SQLITE_UTF16BE"
+ );
+ return SQLITE_ERROR;
+ }
+ pColl = sqlite3FindCollSeq(db, (u8)enc, zName, strlen(zName), 1);
+ if( 0==pColl ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pColl->xCmp = xCompare;
+ pColl->pUser = pCtx;
+ pColl->enc = enc;
+ }
+ sqlite3Error(db, rc, 0);
+ return rc;
+}
+
+/*
+** Register a new collation sequence with the database handle db.
+*/
+int sqlite3_create_collation16(
+ sqlite3* db,
+ const char *zName,
+ int enc,
+ void* pCtx,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+){
+ char const *zName8;
+ sqlite3_value *pTmp;
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ pTmp = sqlite3GetTransientValue(db);
+ sqlite3ValueSetStr(pTmp, -1, zName, SQLITE_UTF16NATIVE, SQLITE_STATIC);
+ zName8 = sqlite3ValueText(pTmp, SQLITE_UTF8);
+ return sqlite3_create_collation(db, zName8, enc, pCtx, xCompare);
+}
+
+/*
+** Register a collation sequence factory callback with the database handle
+** db. Replace any previously installed collation sequence factory.
+*/
+int sqlite3_collation_needed(
+ sqlite3 *db,
+ void *pCollNeededArg,
+ void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)
+){
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ db->xCollNeeded = xCollNeeded;
+ db->xCollNeeded16 = 0;
+ db->pCollNeededArg = pCollNeededArg;
+ return SQLITE_OK;
+}
+
+/*
+** Register a collation sequence factory callback with the database handle
+** db. Replace any previously installed collation sequence factory.
+*/
+int sqlite3_collation_needed16(
+ sqlite3 *db,
+ void *pCollNeededArg,
+ void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
+){
+ if( sqlite3SafetyCheck(db) ){
+ return SQLITE_MISUSE;
+ }
+ db->xCollNeeded = 0;
+ db->xCollNeeded16 = xCollNeeded16;
+ db->pCollNeededArg = pCollNeededArg;
+ return SQLITE_OK;
+}
diff --git a/kopete/plugins/statistics/sqlite/opcodes.c b/kopete/plugins/statistics/sqlite/opcodes.c
new file mode 100644
index 00000000..b6f01219
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/opcodes.c
@@ -0,0 +1,128 @@
+/* Automatically generated. Do not edit */
+/* See the mkopcodec.h script for details. */
+const char *const sqlite3OpcodeNames[] = { "?",
+ "ContextPop",
+ "IntegrityCk",
+ "DropTrigger",
+ "DropIndex",
+ "Recno",
+ "KeyAsData",
+ "Delete",
+ "MoveGt",
+ "VerifyCookie",
+ "Push",
+ "Dup",
+ "Blob",
+ "IdxGT",
+ "IdxRecno",
+ "RowKey",
+ "PutStrKey",
+ "IsUnique",
+ "SetNumColumns",
+ "IdxIsNull",
+ "NullRow",
+ "OpenPseudo",
+ "OpenWrite",
+ "OpenRead",
+ "Transaction",
+ "AutoCommit",
+ "Pop",
+ "Halt",
+ "Vacuum",
+ "ListRead",
+ "RowData",
+ "NotExists",
+ "MoveLe",
+ "SetCookie",
+ "Variable",
+ "AggNext",
+ "AggReset",
+ "Sort",
+ "IdxDelete",
+ "ResetCount",
+ "OpenTemp",
+ "IdxColumn",
+ "Integer",
+ "AggSet",
+ "CreateIndex",
+ "IdxPut",
+ "MoveLt",
+ "Return",
+ "MemLoad",
+ "SortNext",
+ "IdxLT",
+ "Rewind",
+ "AddImm",
+ "AggFunc",
+ "AggInit",
+ "MemIncr",
+ "ListReset",
+ "Clear",
+ "Or",
+ "And",
+ "Not",
+ "PutIntKey",
+ "If",
+ "Callback",
+ "IsNull",
+ "NotNull",
+ "Ne",
+ "Eq",
+ "Gt",
+ "Le",
+ "Lt",
+ "Ge",
+ "BitAnd",
+ "BitOr",
+ "ShiftLeft",
+ "ShiftRight",
+ "Add",
+ "Subtract",
+ "Multiply",
+ "Divide",
+ "Remainder",
+ "Concat",
+ "Negative",
+ "SortReset",
+ "BitNot",
+ "String8",
+ "SortPut",
+ "Last",
+ "NotFound",
+ "MakeRecord",
+ "String",
+ "Goto",
+ "AggFocus",
+ "DropTable",
+ "Column",
+ "Noop",
+ "AggGet",
+ "CreateTable",
+ "NewRecno",
+ "Found",
+ "Distinct",
+ "Close",
+ "Statement",
+ "IfNot",
+ "Pull",
+ "MemStore",
+ "Next",
+ "Prev",
+ "MoveGe",
+ "MustBeInt",
+ "ForceInt",
+ "CollSeq",
+ "Gosub",
+ "ContextPush",
+ "ListRewind",
+ "ListWrite",
+ "ParseSchema",
+ "Destroy",
+ "IdxGE",
+ "FullKey",
+ "ReadCookie",
+ "AbsValue",
+ "Real",
+ "HexBlob",
+ "Function",
+};
diff --git a/kopete/plugins/statistics/sqlite/opcodes.h b/kopete/plugins/statistics/sqlite/opcodes.h
new file mode 100644
index 00000000..7b792c5a
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/opcodes.h
@@ -0,0 +1,126 @@
+/* Automatically generated. Do not edit */
+/* See the mkopcodeh.awk script for details */
+#define OP_ContextPop 1
+#define OP_IntegrityCk 2
+#define OP_DropTrigger 3
+#define OP_DropIndex 4
+#define OP_Recno 5
+#define OP_KeyAsData 6
+#define OP_Delete 7
+#define OP_MoveGt 8
+#define OP_VerifyCookie 9
+#define OP_Push 10
+#define OP_Dup 11
+#define OP_Blob 12
+#define OP_IdxGT 13
+#define OP_IdxRecno 14
+#define OP_RowKey 15
+#define OP_PutStrKey 16
+#define OP_IsUnique 17
+#define OP_SetNumColumns 18
+#define OP_Eq 67
+#define OP_IdxIsNull 19
+#define OP_NullRow 20
+#define OP_OpenPseudo 21
+#define OP_OpenWrite 22
+#define OP_OpenRead 23
+#define OP_Transaction 24
+#define OP_AutoCommit 25
+#define OP_Negative 82
+#define OP_Pop 26
+#define OP_Halt 27
+#define OP_Vacuum 28
+#define OP_ListRead 29
+#define OP_RowData 30
+#define OP_NotExists 31
+#define OP_MoveLe 32
+#define OP_SetCookie 33
+#define OP_Variable 34
+#define OP_AggNext 35
+#define OP_AggReset 36
+#define OP_Sort 37
+#define OP_IdxDelete 38
+#define OP_ResetCount 39
+#define OP_OpenTemp 40
+#define OP_IdxColumn 41
+#define OP_NotNull 65
+#define OP_Ge 71
+#define OP_Remainder 80
+#define OP_Divide 79
+#define OP_Integer 42
+#define OP_AggSet 43
+#define OP_CreateIndex 44
+#define OP_IdxPut 45
+#define OP_MoveLt 46
+#define OP_And 59
+#define OP_ShiftLeft 74
+#define OP_Real 122
+#define OP_Return 47
+#define OP_MemLoad 48
+#define OP_SortNext 49
+#define OP_IdxLT 50
+#define OP_Rewind 51
+#define OP_Gt 68
+#define OP_AddImm 52
+#define OP_Subtract 77
+#define OP_AggFunc 53
+#define OP_AggInit 54
+#define OP_MemIncr 55
+#define OP_ListReset 56
+#define OP_Clear 57
+#define OP_PutIntKey 61
+#define OP_IsNull 64
+#define OP_If 62
+#define OP_Callback 63
+#define OP_SortReset 83
+#define OP_SortPut 86
+#define OP_Last 87
+#define OP_NotFound 88
+#define OP_MakeRecord 89
+#define OP_BitAnd 72
+#define OP_Add 76
+#define OP_HexBlob 123
+#define OP_String 90
+#define OP_Goto 91
+#define OP_AggFocus 92
+#define OP_DropTable 93
+#define OP_Column 94
+#define OP_Noop 95
+#define OP_Not 60
+#define OP_Le 69
+#define OP_BitOr 73
+#define OP_Multiply 78
+#define OP_String8 85
+#define OP_AggGet 96
+#define OP_CreateTable 97
+#define OP_NewRecno 98
+#define OP_Found 99
+#define OP_Distinct 100
+#define OP_Close 101
+#define OP_Statement 102
+#define OP_IfNot 103
+#define OP_Pull 104
+#define OP_MemStore 105
+#define OP_Next 106
+#define OP_Prev 107
+#define OP_MoveGe 108
+#define OP_Lt 70
+#define OP_Ne 66
+#define OP_MustBeInt 109
+#define OP_ForceInt 110
+#define OP_ShiftRight 75
+#define OP_CollSeq 111
+#define OP_Gosub 112
+#define OP_ContextPush 113
+#define OP_ListRewind 114
+#define OP_ListWrite 115
+#define OP_ParseSchema 116
+#define OP_Destroy 117
+#define OP_IdxGE 118
+#define OP_FullKey 119
+#define OP_ReadCookie 120
+#define OP_BitNot 84
+#define OP_AbsValue 121
+#define OP_Or 58
+#define OP_Function 124
+#define OP_Concat 81
diff --git a/kopete/plugins/statistics/sqlite/os.h b/kopete/plugins/statistics/sqlite/os.h
new file mode 100644
index 00000000..fc478baa
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os.h
@@ -0,0 +1,197 @@
+/*
+** 2001 September 16
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This header file (together with is companion C source-code file
+** "os.c") attempt to abstract the underlying operating system so that
+** the SQLite library will work on both POSIX and windows systems.
+*/
+#ifndef _SQLITE_OS_H_
+#define _SQLITE_OS_H_
+
+/*
+** Figure out if we are dealing with Unix, Windows or MacOS.
+**
+** N.B. MacOS means Mac Classic (or Carbon). Treat Darwin (OS X) as Unix.
+** The MacOS build is designed to use CodeWarrior (tested with v8)
+*/
+#if !defined(OS_UNIX) && !defined(OS_TEST)
+# ifndef OS_WIN
+# ifndef OS_MAC
+# if defined(__MACOS__)
+# define OS_MAC 1
+# define OS_WIN 0
+# define OS_UNIX 0
+# elif defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
+# define OS_MAC 0
+# define OS_WIN 1
+# define OS_UNIX 0
+# else
+# define OS_MAC 0
+# define OS_WIN 0
+# define OS_UNIX 1
+# endif
+# else
+# define OS_WIN 0
+# define OS_UNIX 0
+# endif
+# else
+# define OS_MAC 0
+# define OS_UNIX 0
+# endif
+#else
+# define OS_MAC 0
+# ifndef OS_WIN
+# define OS_WIN 0
+# endif
+#endif
+
+/*
+** Invoke the appropriate operating-system specific header file.
+*/
+#if OS_TEST
+# include "os_test.h"
+#endif
+#if OS_UNIX
+# include "os_unix.h"
+#endif
+#if OS_WIN
+# include "os_win.h"
+#endif
+#if OS_MAC
+# include "os_mac.h"
+#endif
+
+/*
+** Temporary files are named starting with this prefix followed by 16 random
+** alphanumeric characters, and no file extension. They are stored in the
+** OS's standard temporary file directory, and are deleted prior to exit.
+** If sqlite is being embedded in another program, you may wish to change the
+** prefix to reflect your program's name, so that if your program exits
+** prematurely, old temporary files can be easily identified. This can be done
+** using -DTEMP_FILE_PREFIX=myprefix_ on the compiler command line.
+*/
+#ifndef TEMP_FILE_PREFIX
+# define TEMP_FILE_PREFIX "sqlite_"
+#endif
+
+/*
+** The following values may be passed as the second argument to
+** sqlite3OsLock(). The various locks exhibit the following semantics:
+**
+** SHARED: Any number of processes may hold a SHARED lock simultaneously.
+** RESERVED: A single process may hold a RESERVED lock on a file at
+** any time. Other processes may hold and obtain new SHARED locks.
+** PENDING: A single process may hold a PENDING lock on a file at
+** any one time. Existing SHARED locks may persist, but no new
+** SHARED locks may be obtained by other processes.
+** EXCLUSIVE: An EXCLUSIVE lock precludes all other locks.
+**
+** PENDING_LOCK may not be passed directly to sqlite3OsLock(). Instead, a
+** process that requests an EXCLUSIVE lock may actually obtain a PENDING
+** lock. This can be upgraded to an EXCLUSIVE lock by a subsequent call to
+** sqlite3OsLock().
+*/
+#define NO_LOCK 0
+#define SHARED_LOCK 1
+#define RESERVED_LOCK 2
+#define PENDING_LOCK 3
+#define EXCLUSIVE_LOCK 4
+
+/*
+** File Locking Notes: (Mostly about windows but also some info for Unix)
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** A SHARED_LOCK is obtained by locking a single randomly-chosen
+** byte out of a specific range of bytes. The lock byte is obtained at
+** random so two separate readers can probably access the file at the
+** same time, unless they are unlucky and choose the same lock byte.
+** An EXCLUSIVE_LOCK is obtained by locking all bytes in the range.
+** There can only be one writer. A RESERVED_LOCK is obtained by locking
+** a single byte of the file that is designated as the reserved lock byte.
+** A PENDING_LOCK is obtained by locking a designated byte different from
+** the RESERVED_LOCK byte.
+**
+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
+** which means we can use reader/writer locks. When reader/writer locks
+** are used, the lock is placed on the same range of bytes that is used
+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
+** will support two or more Win95 readers or two or more WinNT readers.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** The following #defines specify the range of bytes used for locking.
+** SHARED_SIZE is the number of bytes available in the pool from which
+** a random byte is selected for a shared lock. The pool of bytes for
+** shared locks begins at SHARED_FIRST.
+**
+** These #defines are available in os.h so that Unix can use the same
+** byte ranges for locking. This leaves open the possiblity of having
+** clients on win95, winNT, and unix all talking to the same shared file
+** and all locking correctly. To do so would require that samba (or whatever
+** tool is being used for file sharing) implements locks correctly between
+** windows and unix. I'm guessing that isn't likely to happen, but by
+** using the same locking range we are at least open to the possibility.
+**
+** Locking in windows is manditory. For this reason, we cannot store
+** actual data in the bytes used for locking. The pager never allocates
+** the pages involved in locking therefore. SHARED_SIZE is selected so
+** that all locks will fit on a single page even at the minimum page size.
+** PENDING_BYTE defines the beginning of the locks. By default PENDING_BYTE
+** is set high so that we don't have to allocate an unused page except
+** for very large databases. But one should test the page skipping logic
+** by setting PENDING_BYTE low and running the entire regression suite.
+**
+** Changing the value of PENDING_BYTE results in a subtly incompatible
+** file format. Depending on how it is changed, you might not notice
+** the incompatibility right away, even running a full regression test.
+** The default location of PENDING_BYTE is the first byte past the
+** 1GB boundary.
+**
+*/
+#define PENDING_BYTE 0x40000000 /* First byte past the 1GB boundary */
+/* #define PENDING_BYTE 0x5400 // Page 20 - for testing */
+#define RESERVED_BYTE (PENDING_BYTE+1)
+#define SHARED_FIRST (PENDING_BYTE+2)
+#define SHARED_SIZE 510
+
+
+int sqlite3OsDelete(const char*);
+int sqlite3OsFileExists(const char*);
+int sqlite3OsOpenReadWrite(const char*, OsFile*, int*);
+int sqlite3OsOpenExclusive(const char*, OsFile*, int);
+int sqlite3OsOpenReadOnly(const char*, OsFile*);
+int sqlite3OsOpenDirectory(const char*, OsFile*);
+int sqlite3OsSyncDirectory(const char*);
+int sqlite3OsTempFileName(char*);
+int sqlite3OsClose(OsFile*);
+int sqlite3OsRead(OsFile*, void*, int amt);
+int sqlite3OsWrite(OsFile*, const void*, int amt);
+int sqlite3OsSeek(OsFile*, i64 offset);
+int sqlite3OsSync(OsFile*);
+int sqlite3OsTruncate(OsFile*, i64 size);
+int sqlite3OsFileSize(OsFile*, i64 *pSize);
+int sqlite3OsRandomSeed(char*);
+int sqlite3OsSleep(int ms);
+int sqlite3OsCurrentTime(double*);
+int sqlite3OsFileModTime(OsFile*, double*);
+void sqlite3OsEnterMutex(void);
+void sqlite3OsLeaveMutex(void);
+char *sqlite3OsFullPathname(const char*);
+int sqlite3OsLock(OsFile*, int);
+int sqlite3OsUnlock(OsFile*, int);
+int sqlite3OsCheckReservedLock(OsFile *id);
+
+#endif /* _SQLITE_OS_H_ */
diff --git a/kopete/plugins/statistics/sqlite/os_common.h b/kopete/plugins/statistics/sqlite/os_common.h
new file mode 100644
index 00000000..94311b96
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_common.h
@@ -0,0 +1,107 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains macros and a little bit of code that is common to
+** all of the platform-specific files (os_*.c) and is #included into those
+** files.
+**
+** This file should be #included by the os_*.c files only. It is not a
+** general purpose header file.
+*/
+
+/*
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the
+** switch. The following code should catch this problem at compile-time.
+*/
+#ifdef MEMORY_DEBUG
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
+#endif
+
+
+int sqlite3_os_trace = 0;
+#ifdef SQLITE_DEBUG
+static int last_page = 0;
+#define SEEK(X) last_page=(X)
+#define TRACE1(X) if( sqlite3_os_trace ) sqlite3DebugPrintf(X)
+#define TRACE2(X,Y) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y)
+#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
+#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
+#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
+#define TRACE6(X,Y,Z,A,B,C) if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
+#define TRACE7(X,Y,Z,A,B,C,D) \
+ if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
+#else
+#define SEEK(X)
+#define TRACE1(X)
+#define TRACE2(X,Y)
+#define TRACE3(X,Y,Z)
+#define TRACE4(X,Y,Z,A)
+#define TRACE5(X,Y,Z,A,B)
+#define TRACE6(X,Y,Z,A,B,C)
+#define TRACE7(X,Y,Z,A,B,C,D)
+#endif
+
+/*
+** Macros for performance tracing. Normally turned off. Only works
+** on i486 hardware.
+*/
+#ifdef SQLITE_PERFORMANCE_TRACE
+__inline__ unsigned long long int hwtime(void){
+ unsigned long long int x;
+ __asm__("rdtsc\n\t"
+ "mov %%edx, %%ecx\n\t"
+ :"=A" (x));
+ return x;
+}
+static unsigned long long int g_start;
+static unsigned int elapse;
+#define TIMER_START g_start=hwtime()
+#define TIMER_END elapse=hwtime()-g_start
+#define TIMER_ELAPSED elapse
+#else
+#define TIMER_START
+#define TIMER_END
+#define TIMER_ELAPSED 0
+#endif
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_io_error_pending = 0;
+int sqlite3_diskfull_pending = 0;
+#define SimulateIOError(A) \
+ if( sqlite3_io_error_pending ) \
+ if( sqlite3_io_error_pending-- == 1 ){ local_ioerr(); return A; }
+static void local_ioerr(){
+ sqlite3_io_error_pending = 0; /* Really just a place to set a breakpoint */
+}
+#define SimulateDiskfullError \
+ if( sqlite3_diskfull_pending ) \
+ if( sqlite3_diskfull_pending-- == 1 ){ local_ioerr(); return SQLITE_FULL; }
+#else
+#define SimulateIOError(A)
+#define SimulateDiskfullError
+#endif
+
+/*
+** When testing, keep a count of the number of open files.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_open_file_count = 0;
+#define OpenCounter(X) sqlite3_open_file_count+=(X)
+#else
+#define OpenCounter(X)
+#endif
diff --git a/kopete/plugins/statistics/sqlite/os_mac.c b/kopete/plugins/statistics/sqlite/os_mac.c
new file mode 100644
index 00000000..f84c168d
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_mac.c
@@ -0,0 +1,738 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code that is specific classic mac. Mac OS X
+** uses the os_unix.c file, not this one.
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#if OS_MAC /* This file used on classic mac only */
+
+#include <extras.h>
+#include <path2fss.h>
+#include <TextUtils.h>
+#include <FinderRegistry.h>
+#include <Folders.h>
+#include <Timer.h>
+#include <OSUtils.h>
+
+/*
+** Macros used to determine whether or not to use threads.
+*/
+#if defined(THREADSAFE) && THREADSAFE
+# include <Multiprocessing.h>
+# define SQLITE_MACOS_MULTITASKING 1
+#endif
+
+/*
+** Include code that is common to all os_*.c files
+*/
+#include "os_common.h"
+
+/*
+** Delete the named file
+*/
+int sqlite3OsDelete(const char *zFilename){
+ unlink(zFilename);
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the named file exists.
+*/
+int sqlite3OsFileExists(const char *zFilename){
+ return access(zFilename, 0)==0;
+}
+
+/*
+** Attempt to open a file for both reading and writing. If that
+** fails, try opening it read-only. If the file does not exist,
+** try to create it.
+**
+** On success, a handle for the open file is written to *id
+** and *pReadonly is set to 0 if the file was opened for reading and
+** writing or 1 if the file was opened read-only. The function returns
+** SQLITE_OK.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id and *pReadonly unchanged.
+*/
+int sqlite3OsOpenReadWrite(
+ const char *zFilename,
+ OsFile *id,
+ int *pReadonly
+){
+ FSSpec fsSpec;
+# ifdef _LARGE_FILE
+ HFSUniStr255 dfName;
+ FSRef fsRef;
+ if( __path2fss(zFilename, &fsSpec) != noErr ){
+ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
+ return SQLITE_CANTOPEN;
+ }
+ if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
+ return SQLITE_CANTOPEN;
+ FSGetDataForkName(&dfName);
+ if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
+ fsRdWrShPerm, &(id->refNum)) != noErr ){
+ if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
+ fsRdWrPerm, &(id->refNum)) != noErr ){
+ if (FSOpenFork(&fsRef, dfName.length, dfName.unicode,
+ fsRdPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+ else
+ *pReadonly = 1;
+ } else
+ *pReadonly = 0;
+ } else
+ *pReadonly = 0;
+# else
+ __path2fss(zFilename, &fsSpec);
+ if( !sqlite3OsFileExists(zFilename) ){
+ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
+ return SQLITE_CANTOPEN;
+ }
+ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNum)) != noErr ){
+ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr ){
+ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+ else
+ *pReadonly = 1;
+ } else
+ *pReadonly = 0;
+ } else
+ *pReadonly = 0;
+# endif
+ if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){
+ id->refNumRF = -1;
+ }
+ id->locked = 0;
+ id->delOnClose = 0;
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+
+/*
+** Attempt to open a new file for exclusive access by this process.
+** The file will be opened for both reading and writing. To avoid
+** a potential security problem, we do not allow the file to have
+** previously existed. Nor do we allow the file to be a symbolic
+** link.
+**
+** If delFlag is true, then make arrangements to automatically delete
+** the file when it is closed.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
+ FSSpec fsSpec;
+# ifdef _LARGE_FILE
+ HFSUniStr255 dfName;
+ FSRef fsRef;
+ __path2fss(zFilename, &fsSpec);
+ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
+ return SQLITE_CANTOPEN;
+ if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
+ return SQLITE_CANTOPEN;
+ FSGetDataForkName(&dfName);
+ if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
+ fsRdWrPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+# else
+ __path2fss(zFilename, &fsSpec);
+ if( HCreate(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, 'SQLI', cDocumentFile) != noErr )
+ return SQLITE_CANTOPEN;
+ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+# endif
+ id->refNumRF = -1;
+ id->locked = 0;
+ id->delOnClose = delFlag;
+ if (delFlag)
+ id->pathToDel = sqlite3OsFullPathname(zFilename);
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a new file for read-only access.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
+ FSSpec fsSpec;
+# ifdef _LARGE_FILE
+ HFSUniStr255 dfName;
+ FSRef fsRef;
+ if( __path2fss(zFilename, &fsSpec) != noErr )
+ return SQLITE_CANTOPEN;
+ if( FSpMakeFSRef(&fsSpec, &fsRef) != noErr )
+ return SQLITE_CANTOPEN;
+ FSGetDataForkName(&dfName);
+ if( FSOpenFork(&fsRef, dfName.length, dfName.unicode,
+ fsRdPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+# else
+ __path2fss(zFilename, &fsSpec);
+ if( HOpenDF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdPerm, &(id->refNum)) != noErr )
+ return SQLITE_CANTOPEN;
+# endif
+ if( HOpenRF(fsSpec.vRefNum, fsSpec.parID, fsSpec.name, fsRdWrShPerm, &(id->refNumRF)) != noErr){
+ id->refNumRF = -1;
+ }
+ id->locked = 0;
+ id->delOnClose = 0;
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a file descriptor for the directory that contains a
+** file. This file descriptor can be used to fsync() the directory
+** in order to make sure the creation of a new file is actually written
+** to disk.
+**
+** This routine is only meaningful for Unix. It is a no-op under
+** windows since windows does not support hard links.
+**
+** On success, a handle for a previously open file is at *id is
+** updated with the new directory file descriptor and SQLITE_OK is
+** returned.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id unchanged.
+*/
+int sqlite3OsOpenDirectory(
+ const char *zDirname,
+ OsFile *id
+){
+ return SQLITE_OK;
+}
+
+/*
+** Create a temporary file name in zBuf. zBuf must be big enough to
+** hold at least SQLITE_TEMPNAME_SIZE characters.
+*/
+int sqlite3OsTempFileName(char *zBuf){
+ static char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ int i, j;
+ char zTempPath[SQLITE_TEMPNAME_SIZE];
+ char zdirName[32];
+ CInfoPBRec infoRec;
+ Str31 dirName;
+ memset(&infoRec, 0, sizeof(infoRec));
+ memset(zTempPath, 0, SQLITE_TEMPNAME_SIZE);
+ if( FindFolder(kOnSystemDisk, kTemporaryFolderType, kCreateFolder,
+ &(infoRec.dirInfo.ioVRefNum), &(infoRec.dirInfo.ioDrParID)) == noErr ){
+ infoRec.dirInfo.ioNamePtr = dirName;
+ do{
+ infoRec.dirInfo.ioFDirIndex = -1;
+ infoRec.dirInfo.ioDrDirID = infoRec.dirInfo.ioDrParID;
+ if( PBGetCatInfoSync(&infoRec) == noErr ){
+ CopyPascalStringToC(dirName, zdirName);
+ i = strlen(zdirName);
+ memmove(&(zTempPath[i+1]), zTempPath, strlen(zTempPath));
+ strcpy(zTempPath, zdirName);
+ zTempPath[i] = ':';
+ }else{
+ *zTempPath = 0;
+ break;
+ }
+ } while( infoRec.dirInfo.ioDrDirID != fsRtDirID );
+ }
+ if( *zTempPath == 0 )
+ getcwd(zTempPath, SQLITE_TEMPNAME_SIZE-24);
+ for(;;){
+ sprintf(zBuf, "%s"TEMP_FILE_PREFIX, zTempPath);
+ j = strlen(zBuf);
+ sqlite3Randomness(15, &zBuf[j]);
+ for(i=0; i<15; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ if( !sqlite3OsFileExists(zBuf) ) break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Close a file.
+*/
+int sqlite3OsClose(OsFile *id){
+ if( id->refNumRF!=-1 )
+ FSClose(id->refNumRF);
+# ifdef _LARGE_FILE
+ FSCloseFork(id->refNum);
+# else
+ FSClose(id->refNum);
+# endif
+ if( id->delOnClose ){
+ unlink(id->pathToDel);
+ sqliteFree(id->pathToDel);
+ }
+ OpenCounter(-1);
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a file into a buffer. Return SQLITE_OK if all
+** bytes were read successfully and SQLITE_IOERR if anything goes
+** wrong.
+*/
+int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
+ int got;
+ SimulateIOError(SQLITE_IOERR);
+ TRACE2("READ %d\n", last_page);
+# ifdef _LARGE_FILE
+ FSReadFork(id->refNum, fsAtMark, 0, (ByteCount)amt, pBuf, (ByteCount*)&got);
+# else
+ got = amt;
+ FSRead(id->refNum, &got, pBuf);
+# endif
+ if( got==amt ){
+ return SQLITE_OK;
+ }else{
+ return SQLITE_IOERR;
+ }
+}
+
+/*
+** Write data from a buffer into a file. Return SQLITE_OK on success
+** or some other error code on failure.
+*/
+int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
+ OSErr oserr;
+ int wrote = 0;
+ SimulateIOError(SQLITE_IOERR);
+ TRACE2("WRITE %d\n", last_page);
+ while( amt>0 ){
+# ifdef _LARGE_FILE
+ oserr = FSWriteFork(id->refNum, fsAtMark, 0,
+ (ByteCount)amt, pBuf, (ByteCount*)&wrote);
+# else
+ wrote = amt;
+ oserr = FSWrite(id->refNum, &wrote, pBuf);
+# endif
+ if( wrote == 0 || oserr != noErr)
+ break;
+ amt -= wrote;
+ pBuf = &((char*)pBuf)[wrote];
+ }
+ if( oserr != noErr || amt>wrote ){
+ return SQLITE_FULL;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Move the read/write pointer in a file.
+*/
+int sqlite3OsSeek(OsFile *id, off_t offset){
+ off_t curSize;
+ SEEK(offset/1024 + 1);
+ if( sqlite3OsFileSize(id, &curSize) != SQLITE_OK ){
+ return SQLITE_IOERR;
+ }
+ if( offset >= curSize ){
+ if( sqlite3OsTruncate(id, offset+1) != SQLITE_OK ){
+ return SQLITE_IOERR;
+ }
+ }
+# ifdef _LARGE_FILE
+ if( FSSetForkPosition(id->refNum, fsFromStart, offset) != noErr ){
+# else
+ if( SetFPos(id->refNum, fsFromStart, offset) != noErr ){
+# endif
+ return SQLITE_IOERR;
+ }else{
+ return SQLITE_OK;
+ }
+}
+
+/*
+** Make sure all writes to a particular file are committed to disk.
+**
+** Under Unix, also make sure that the directory entry for the file
+** has been created by fsync-ing the directory that contains the file.
+** If we do not do this and we encounter a power failure, the directory
+** entry for the journal might not exist after we reboot. The next
+** SQLite to access the file will not know that the journal exists (because
+** the directory entry for the journal was never created) and the transaction
+** will not roll back - possibly leading to database corruption.
+*/
+int sqlite3OsSync(OsFile *id){
+# ifdef _LARGE_FILE
+ if( FSFlushFork(id->refNum) != noErr ){
+# else
+ ParamBlockRec params;
+ memset(&params, 0, sizeof(ParamBlockRec));
+ params.ioParam.ioRefNum = id->refNum;
+ if( PBFlushFileSync(&params) != noErr ){
+# endif
+ return SQLITE_IOERR;
+ }else{
+ return SQLITE_OK;
+ }
+}
+
+/*
+** Sync the directory zDirname. This is a no-op on operating systems other
+** than UNIX.
+*/
+int sqlite3OsSyncDirectory(const char *zDirname){
+ SimulateIOError(SQLITE_IOERR);
+ return SQLITE_OK;
+}
+
+/*
+** Truncate an open file to a specified size
+*/
+int sqlite3OsTruncate(OsFile *id, off_t nByte){
+ SimulateIOError(SQLITE_IOERR);
+# ifdef _LARGE_FILE
+ if( FSSetForkSize(id->refNum, fsFromStart, nByte) != noErr){
+# else
+ if( SetEOF(id->refNum, nByte) != noErr ){
+# endif
+ return SQLITE_IOERR;
+ }else{
+ return SQLITE_OK;
+ }
+}
+
+/*
+** Determine the current size of a file in bytes
+*/
+int sqlite3OsFileSize(OsFile *id, off_t *pSize){
+# ifdef _LARGE_FILE
+ if( FSGetForkSize(id->refNum, pSize) != noErr){
+# else
+ if( GetEOF(id->refNum, pSize) != noErr ){
+# endif
+ return SQLITE_IOERR;
+ }else{
+ return SQLITE_OK;
+ }
+}
+
+/*
+** Windows file locking notes: [similar issues apply to MacOS]
+**
+** We cannot use LockFileEx() or UnlockFileEx() on Win95/98/ME because
+** those functions are not available. So we use only LockFile() and
+** UnlockFile().
+**
+** LockFile() prevents not just writing but also reading by other processes.
+** (This is a design error on the part of Windows, but there is nothing
+** we can do about that.) So the region used for locking is at the
+** end of the file where it is unlikely to ever interfere with an
+** actual read attempt.
+**
+** A database read lock is obtained by locking a single randomly-chosen
+** byte out of a specific range of bytes. The lock byte is obtained at
+** random so two separate readers can probably access the file at the
+** same time, unless they are unlucky and choose the same lock byte.
+** A database write lock is obtained by locking all bytes in the range.
+** There can only be one writer.
+**
+** A lock is obtained on the first byte of the lock range before acquiring
+** either a read lock or a write lock. This prevents two processes from
+** attempting to get a lock at a same time. The semantics of
+** sqlite3OsReadLock() require that if there is already a write lock, that
+** lock is converted into a read lock atomically. The lock on the first
+** byte allows us to drop the old write lock and get the read lock without
+** another process jumping into the middle and messing us up. The same
+** argument applies to sqlite3OsWriteLock().
+**
+** On WinNT/2K/XP systems, LockFileEx() and UnlockFileEx() are available,
+** which means we can use reader/writer locks. When reader writer locks
+** are used, the lock is placed on the same range of bytes that is used
+** for probabilistic locking in Win95/98/ME. Hence, the locking scheme
+** will support two or more Win95 readers or two or more WinNT readers.
+** But a single Win95 reader will lock out all WinNT readers and a single
+** WinNT reader will lock out all other Win95 readers.
+**
+** Note: On MacOS we use the resource fork for locking.
+**
+** The following #defines specify the range of bytes used for locking.
+** N_LOCKBYTE is the number of bytes available for doing the locking.
+** The first byte used to hold the lock while the lock is changing does
+** not count toward this number. FIRST_LOCKBYTE is the address of
+** the first byte in the range of bytes used for locking.
+*/
+#define N_LOCKBYTE 10239
+#define FIRST_LOCKBYTE (0x000fffff - N_LOCKBYTE)
+
+/*
+** Change the status of the lock on the file "id" to be a readlock.
+** If the file was write locked, then this reduces the lock to a read.
+** If the file was read locked, then this acquires a new read lock.
+**
+** Return SQLITE_OK on success and SQLITE_BUSY on failure. If this
+** library was compiled with large file support (LFS) but LFS is not
+** available on the host, then an SQLITE_NOLFS is returned.
+*/
+int sqlite3OsReadLock(OsFile *id){
+ int rc;
+ if( id->locked>0 || id->refNumRF == -1 ){
+ rc = SQLITE_OK;
+ }else{
+ int lk;
+ OSErr res;
+ int cnt = 5;
+ ParamBlockRec params;
+ sqlite3Randomness(sizeof(lk), &lk);
+ lk = (lk & 0x7fffffff)%N_LOCKBYTE + 1;
+ memset(&params, 0, sizeof(params));
+ params.ioParam.ioRefNum = id->refNumRF;
+ params.ioParam.ioPosMode = fsFromStart;
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
+ params.ioParam.ioReqCount = 1;
+ while( cnt-->0 && (res = PBLockRangeSync(&params))!=noErr ){
+ UInt32 finalTicks;
+ Delay(1, &finalTicks); /* 1/60 sec */
+ }
+ if( res == noErr ){
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
+ params.ioParam.ioReqCount = N_LOCKBYTE;
+ PBUnlockRangeSync(&params);
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+lk;
+ params.ioParam.ioReqCount = 1;
+ res = PBLockRangeSync(&params);
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
+ params.ioParam.ioReqCount = 1;
+ PBUnlockRangeSync(&params);
+ }
+ if( res == noErr ){
+ id->locked = lk;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+ }
+ return rc;
+}
+
+/*
+** Change the lock status to be an exclusive or write lock. Return
+** SQLITE_OK on success and SQLITE_BUSY on a failure. If this
+** library was compiled with large file support (LFS) but LFS is not
+** available on the host, then an SQLITE_NOLFS is returned.
+*/
+int sqlite3OsWriteLock(OsFile *id){
+ int rc;
+ if( id->locked<0 || id->refNumRF == -1 ){
+ rc = SQLITE_OK;
+ }else{
+ OSErr res;
+ int cnt = 5;
+ ParamBlockRec params;
+ memset(&params, 0, sizeof(params));
+ params.ioParam.ioRefNum = id->refNumRF;
+ params.ioParam.ioPosMode = fsFromStart;
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
+ params.ioParam.ioReqCount = 1;
+ while( cnt-->0 && (res = PBLockRangeSync(&params))!=noErr ){
+ UInt32 finalTicks;
+ Delay(1, &finalTicks); /* 1/60 sec */
+ }
+ if( res == noErr ){
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE + id->locked;
+ params.ioParam.ioReqCount = 1;
+ if( id->locked==0
+ || PBUnlockRangeSync(&params)==noErr ){
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
+ params.ioParam.ioReqCount = N_LOCKBYTE;
+ res = PBLockRangeSync(&params);
+ }else{
+ res = afpRangeNotLocked;
+ }
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE;
+ params.ioParam.ioReqCount = 1;
+ PBUnlockRangeSync(&params);
+ }
+ if( res == noErr ){
+ id->locked = -1;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_BUSY;
+ }
+ }
+ return rc;
+}
+
+/*
+** Unlock the given file descriptor. If the file descriptor was
+** not previously locked, then this routine is a no-op. If this
+** library was compiled with large file support (LFS) but LFS is not
+** available on the host, then an SQLITE_NOLFS is returned.
+*/
+int sqlite3OsUnlock(OsFile *id){
+ int rc;
+ ParamBlockRec params;
+ memset(&params, 0, sizeof(params));
+ params.ioParam.ioRefNum = id->refNumRF;
+ params.ioParam.ioPosMode = fsFromStart;
+ if( id->locked==0 || id->refNumRF == -1 ){
+ rc = SQLITE_OK;
+ }else if( id->locked<0 ){
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+1;
+ params.ioParam.ioReqCount = N_LOCKBYTE;
+ PBUnlockRangeSync(&params);
+ rc = SQLITE_OK;
+ id->locked = 0;
+ }else{
+ params.ioParam.ioPosOffset = FIRST_LOCKBYTE+id->locked;
+ params.ioParam.ioReqCount = 1;
+ PBUnlockRangeSync(&params);
+ rc = SQLITE_OK;
+ id->locked = 0;
+ }
+ return rc;
+}
+
+/*
+** Get information to seed the random number generator. The seed
+** is written into the buffer zBuf[256]. The calling function must
+** supply a sufficiently large buffer.
+*/
+int sqlite3OsRandomSeed(char *zBuf){
+ /* We have to initialize zBuf to prevent valgrind from reporting
+ ** errors. The reports issued by valgrind are incorrect - we would
+ ** prefer that the randomness be increased by making use of the
+ ** uninitialized space in zBuf - but valgrind errors tend to worry
+ ** some users. Rather than argue, it seems easier just to initialize
+ ** the whole array and silence valgrind, even if that means less randomness
+ ** in the random seed.
+ **
+ ** When testing, initializing zBuf[] to zero is all we do. That means
+ ** that we always use the same random number sequence.* This makes the
+ ** tests repeatable.
+ */
+ memset(zBuf, 0, 256);
+#if !defined(SQLITE_TEST)
+ {
+ int pid;
+ Microseconds((UnsignedWide*)zBuf);
+ pid = getpid();
+ memcpy(&zBuf[sizeof(UnsignedWide)], &pid, sizeof(pid));
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** Sleep for a little while. Return the amount of time slept.
+*/
+int sqlite3OsSleep(int ms){
+ UInt32 finalTicks;
+ UInt32 ticks = (((UInt32)ms+16)*3)/50; /* 1/60 sec per tick */
+ Delay(ticks, &finalTicks);
+ return (int)((ticks*50)/3);
+}
+
+/*
+** Static variables used for thread synchronization
+*/
+static int inMutex = 0;
+#ifdef SQLITE_MACOS_MULTITASKING
+ static MPCriticalRegionID criticalRegion;
+#endif
+
+/*
+** The following pair of routine implement mutual exclusion for
+** multi-threaded processes. Only a single thread is allowed to
+** executed code that is surrounded by EnterMutex() and LeaveMutex().
+**
+** SQLite uses only a single Mutex. There is not much critical
+** code and what little there is executes quickly and without blocking.
+*/
+void sqlite3OsEnterMutex(){
+#ifdef SQLITE_MACOS_MULTITASKING
+ static volatile int notInit = 1;
+ if( notInit ){
+ if( notInit == 2 ) /* as close as you can get to thread safe init */
+ MPYield();
+ else{
+ notInit = 2;
+ MPCreateCriticalRegion(&criticalRegion);
+ notInit = 0;
+ }
+ }
+ MPEnterCriticalRegion(criticalRegion, kDurationForever);
+#endif
+ assert( !inMutex );
+ inMutex = 1;
+}
+void sqlite3OsLeaveMutex(){
+ assert( inMutex );
+ inMutex = 0;
+#ifdef SQLITE_MACOS_MULTITASKING
+ MPExitCriticalRegion(criticalRegion);
+#endif
+}
+
+/*
+** Turn a relative pathname into a full pathname. Return a pointer
+** to the full pathname stored in space obtained from sqliteMalloc().
+** The calling function is responsible for freeing this space once it
+** is no longer needed.
+*/
+char *sqlite3OsFullPathname(const char *zRelative){
+ char *zFull = 0;
+ if( zRelative[0]==':' ){
+ char zBuf[_MAX_PATH+1];
+ sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), &(zRelative[1]),
+ (char*)0);
+ }else{
+ if( strchr(zRelative, ':') ){
+ sqlite3SetString(&zFull, zRelative, (char*)0);
+ }else{
+ char zBuf[_MAX_PATH+1];
+ sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), zRelative, (char*)0);
+ }
+ }
+ return zFull;
+}
+
+/*
+** The following variable, if set to a non-zero value, becomes the result
+** returned from sqlite3OsCurrentTime(). This is used for testing.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_current_time = 0;
+#endif
+
+/*
+** Find the current time (in Universal Coordinated Time). Write the
+** current time and date as a Julian Day number into *prNow and
+** return 0. Return 1 if the time and date cannot be found.
+*/
+int sqlite3OsCurrentTime(double *prNow){
+ *prNow = 0.0; /**** FIX ME *****/
+#ifdef SQLITE_TEST
+ if( sqlite3_current_time ){
+ *prNow = sqlite3_current_time/86400.0 + 2440587.5;
+ }
+#endif
+ return 0;
+}
+
+#endif /* OS_MAC */
diff --git a/kopete/plugins/statistics/sqlite/os_mac.h b/kopete/plugins/statistics/sqlite/os_mac.h
new file mode 100644
index 00000000..5b60f818
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_mac.h
@@ -0,0 +1,41 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This header file defines OS-specific features of classic Mac.
+** OS X uses the os_unix.h file, not this one.
+*/
+#ifndef _SQLITE_OS_MAC_H_
+#define _SQLITE_OS_MAC_H_
+
+
+#include <unistd.h>
+#include <Files.h>
+#define SQLITE_TEMPNAME_SIZE _MAX_PATH
+#define SQLITE_MIN_SLEEP_MS 17
+
+/*
+** The OsFile structure is a operating-system independing representation
+** of an open file handle. It is defined differently for each architecture.
+**
+** This is the definition for class Mac.
+*/
+typedef struct OsFile OsFile;
+struct OsFile {
+ SInt16 refNum; /* Data fork/file reference number */
+ SInt16 refNumRF; /* Resource fork reference number (for locking) */
+ int locked; /* 0: unlocked, <0: write lock, >0: read lock */
+ int delOnClose; /* True if file is to be deleted on close */
+ char *pathToDel; /* Name of file to delete on close */
+};
+
+
+#endif /* _SQLITE_OS_MAC_H_ */
diff --git a/kopete/plugins/statistics/sqlite/os_unix.c b/kopete/plugins/statistics/sqlite/os_unix.c
new file mode 100644
index 00000000..94fca701
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_unix.c
@@ -0,0 +1,1276 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code that is specific to Unix systems.
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#if OS_UNIX /* This file is used on unix only */
+
+
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#ifndef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+#ifdef SQLITE_DISABLE_LFS
+# undef O_LARGEFILE
+# define O_LARGEFILE 0
+#endif
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+
+/*
+** The DJGPP compiler environment looks mostly like Unix, but it
+** lacks the fcntl() system call. So redefine fcntl() to be something
+** that always succeeds. This means that locking does not occur under
+** DJGPP. But its DOS - what did you expect?
+*/
+#ifdef __DJGPP__
+# define fcntl(A,B,C) 0
+#endif
+
+/*
+** Macros used to determine whether or not to use threads. The
+** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
+** Posix threads and SQLITE_W32_THREADS is defined if we are
+** synchronizing using Win32 threads.
+*/
+#if defined(THREADSAFE) && THREADSAFE
+# include <pthread.h>
+# define SQLITE_UNIX_THREADS 1
+#endif
+
+
+/*
+** Include code that is common to all os_*.c files
+*/
+#include "os_common.h"
+
+#if defined(THREADSAFE) && THREADSAFE && defined(__linux__)
+#define getpid pthread_self
+#endif
+
+/*
+** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996)
+** section 6.5.2.2 lines 483 through 490 specify that when a process
+** sets or clears a lock, that operation overrides any prior locks set
+** by the same process. It does not explicitly say so, but this implies
+** that it overrides locks set by the same process using a different
+** file descriptor. Consider this test case:
+**
+** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644);
+** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644);
+**
+** Suppose ./file1 and ./file2 are really the same file (because
+** one is a hard or symbolic link to the other) then if you set
+** an exclusive lock on fd1, then try to get an exclusive lock
+** on fd2, it works. I would have expected the second lock to
+** fail since there was already a lock on the file due to fd1.
+** But not so. Since both locks came from the same process, the
+** second overrides the first, even though they were on different
+** file descriptors opened on different file names.
+**
+** Bummer. If you ask me, this is broken. Badly broken. It means
+** that we cannot use POSIX locks to synchronize file access among
+** competing threads of the same process. POSIX locks will work fine
+** to synchronize access for threads in separate processes, but not
+** threads within the same process.
+**
+** To work around the problem, SQLite has to manage file locks internally
+** on its own. Whenever a new database is opened, we have to find the
+** specific inode of the database file (the inode is determined by the
+** st_dev and st_ino fields of the stat structure that fstat() fills in)
+** and check for locks already existing on that inode. When locks are
+** created or removed, we have to look at our own internal record of the
+** locks to see if another thread has previously set a lock on that same
+** inode.
+**
+** The OsFile structure for POSIX is no longer just an integer file
+** descriptor. It is now a structure that holds the integer file
+** descriptor and a pointer to a structure that describes the internal
+** locks on the corresponding inode. There is one locking structure
+** per inode, so if the same inode is opened twice, both OsFile structures
+** point to the same locking structure. The locking structure keeps
+** a reference count (so we will know when to delete it) and a "cnt"
+** field that tells us its internal lock status. cnt==0 means the
+** file is unlocked. cnt==-1 means the file has an exclusive lock.
+** cnt>0 means there are cnt shared locks on the file.
+**
+** Any attempt to lock or unlock a file first checks the locking
+** structure. The fcntl() system call is only invoked to set a
+** POSIX lock if the internal lock structure transitions between
+** a locked and an unlocked state.
+**
+** 2004-Jan-11:
+** More recent discoveries about POSIX advisory locks. (The more
+** I discover, the more I realize the a POSIX advisory locks are
+** an abomination.)
+**
+** If you close a file descriptor that points to a file that has locks,
+** all locks on that file that are owned by the current process are
+** released. To work around this problem, each OsFile structure contains
+** a pointer to an openCnt structure. There is one openCnt structure
+** per open inode, which means that multiple OsFiles can point to a single
+** openCnt. When an attempt is made to close an OsFile, if there are
+** other OsFiles open on the same inode that are holding locks, the call
+** to close() the file descriptor is deferred until all of the locks clear.
+** The openCnt structure keeps a list of file descriptors that need to
+** be closed and that list is walked (and cleared) when the last lock
+** clears.
+**
+** First, under Linux threads, because each thread has a separate
+** process ID, lock operations in one thread do not override locks
+** to the same file in other threads. Linux threads behave like
+** separate processes in this respect. But, if you close a file
+** descriptor in linux threads, all locks are cleared, even locks
+** on other threads and even though the other threads have different
+** process IDs. Linux threads is inconsistent in this respect.
+** (I'm beginning to think that linux threads is an abomination too.)
+** The consequence of this all is that the hash table for the lockInfo
+** structure has to include the process id as part of its key because
+** locks in different threads are treated as distinct. But the
+** openCnt structure should not include the process id in its
+** key because close() clears lock on all threads, not just the current
+** thread. Were it not for this goofiness in linux threads, we could
+** combine the lockInfo and openCnt structures into a single structure.
+**
+** 2004-Jun-28:
+** On some versions of linux, threads can override each others locks.
+** On others not. Sometimes you can change the behavior on the same
+** system by setting the LD_ASSUME_KERNEL environment variable. The
+** POSIX standard is silent as to which behavior is correct, as far
+** as I can tell, so other versions of unix might show the same
+** inconsistency. There is no little doubt in my mind that posix
+** advisory locks and linux threads are profoundly broken.
+**
+** To work around the inconsistencies, we have to test at runtime
+** whether or not threads can override each others locks. This test
+** is run once, the first time any lock is attempted. A static
+** variable is set to record the results of this test for future
+** use.
+*/
+
+/*
+** An instance of the following structure serves as the key used
+** to locate a particular lockInfo structure given its inode.
+**
+** If threads cannot override each others locks, then we set the
+** lockKey.tid field to the thread ID. If threads can override
+** each others locks then tid is always set to zero. tid is also
+** set to zero if we compile without threading support.
+*/
+struct lockKey {
+ dev_t dev; /* Device number */
+ ino_t ino; /* Inode number */
+#ifdef SQLITE_UNIX_THREADS
+ pthread_t tid; /* Thread ID or zero if threads cannot override each other */
+#endif
+};
+
+/*
+** An instance of the following structure is allocated for each open
+** inode on each thread with a different process ID. (Threads have
+** different process IDs on linux, but not on most other unixes.)
+**
+** A single inode can have multiple file descriptors, so each OsFile
+** structure contains a pointer to an instance of this object and this
+** object keeps a count of the number of OsFiles pointing to it.
+*/
+struct lockInfo {
+ struct lockKey key; /* The lookup key */
+ int cnt; /* Number of SHARED locks held */
+ int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
+ int nRef; /* Number of pointers to this structure */
+};
+
+/*
+** An instance of the following structure serves as the key used
+** to locate a particular openCnt structure given its inode. This
+** is the same as the lockKey except that the thread ID is omitted.
+*/
+struct openKey {
+ dev_t dev; /* Device number */
+ ino_t ino; /* Inode number */
+};
+
+/*
+** An instance of the following structure is allocated for each open
+** inode. This structure keeps track of the number of locks on that
+** inode. If a close is attempted against an inode that is holding
+** locks, the close is deferred until all locks clear by adding the
+** file descriptor to be closed to the pending list.
+*/
+struct openCnt {
+ struct openKey key; /* The lookup key */
+ int nRef; /* Number of pointers to this structure */
+ int nLock; /* Number of outstanding locks */
+ int nPending; /* Number of pending close() operations */
+ int *aPending; /* Malloced space holding fd's awaiting a close() */
+};
+
+/*
+** These hash table maps inodes and process IDs into lockInfo and openCnt
+** structures. Access to these hash tables must be protected by a mutex.
+*/
+static Hash lockHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
+static Hash openHash = { SQLITE_HASH_BINARY, 0, 0, 0, 0, 0 };
+
+
+#ifdef SQLITE_UNIX_THREADS
+/*
+** This variable records whether or not threads can override each others
+** locks.
+**
+** 0: No. Threads cannot override each others locks.
+** 1: Yes. Threads can override each others locks.
+** -1: We don't know yet.
+*/
+static int threadsOverrideEachOthersLocks = -1;
+
+/*
+** This structure holds information passed into individual test
+** threads by the testThreadLockingBehavior() routine.
+*/
+struct threadTestData {
+ int fd; /* File to be locked */
+ struct flock lock; /* The locking operation */
+ int result; /* Result of the locking operation */
+};
+
+/*
+** The testThreadLockingBehavior() routine launches two separate
+** threads on this routine. This routine attempts to lock a file
+** descriptor then returns. The success or failure of that attempt
+** allows the testThreadLockingBehavior() procedure to determine
+** whether or not threads can override each others locks.
+*/
+static void *threadLockingTest(void *pArg){
+ struct threadTestData *pData = (struct threadTestData*)pArg;
+ pData->result = fcntl(pData->fd, F_SETLK, &pData->lock);
+ return pArg;
+}
+
+/*
+** This procedure attempts to determine whether or not threads
+** can override each others locks then sets the
+** threadsOverrideEachOthersLocks variable appropriately.
+*/
+static void testThreadLockingBehavior(fd_orig){
+ int fd;
+ struct threadTestData d[2];
+ pthread_t t[2];
+
+ fd = dup(fd_orig);
+ if( fd<0 ) return;
+ memset(d, 0, sizeof(d));
+ d[0].fd = fd;
+ d[0].lock.l_type = F_RDLCK;
+ d[0].lock.l_len = 1;
+ d[0].lock.l_start = 0;
+ d[0].lock.l_whence = SEEK_SET;
+ d[1] = d[0];
+ d[1].lock.l_type = F_WRLCK;
+ pthread_create(&t[0], 0, threadLockingTest, &d[0]);
+ pthread_create(&t[1], 0, threadLockingTest, &d[1]);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ close(fd);
+ threadsOverrideEachOthersLocks = d[0].result==0 && d[1].result==0;
+}
+#endif /* SQLITE_UNIX_THREADS */
+
+/*
+** Release a lockInfo structure previously allocated by findLockInfo().
+*/
+static void releaseLockInfo(struct lockInfo *pLock){
+ pLock->nRef--;
+ if( pLock->nRef==0 ){
+ sqlite3HashInsert(&lockHash, &pLock->key, sizeof(pLock->key), 0);
+ sqliteFree(pLock);
+ }
+}
+
+/*
+** Release a openCnt structure previously allocated by findLockInfo().
+*/
+static void releaseOpenCnt(struct openCnt *pOpen){
+ pOpen->nRef--;
+ if( pOpen->nRef==0 ){
+ sqlite3HashInsert(&openHash, &pOpen->key, sizeof(pOpen->key), 0);
+ sqliteFree(pOpen->aPending);
+ sqliteFree(pOpen);
+ }
+}
+
+/*
+** Given a file descriptor, locate lockInfo and openCnt structures that
+** describes that file descriptor. Create a new ones if necessary. The
+** return values might be unset if an error occurs.
+**
+** Return the number of errors.
+*/
+static int findLockInfo(
+ int fd, /* The file descriptor used in the key */
+ struct lockInfo **ppLock, /* Return the lockInfo structure here */
+ struct openCnt **ppOpen /* Return the openCnt structure here */
+){
+ int rc;
+ struct lockKey key1;
+ struct openKey key2;
+ struct stat statbuf;
+ struct lockInfo *pLock;
+ struct openCnt *pOpen;
+ rc = fstat(fd, &statbuf);
+ if( rc!=0 ) return 1;
+ memset(&key1, 0, sizeof(key1));
+ key1.dev = statbuf.st_dev;
+ key1.ino = statbuf.st_ino;
+#ifdef SQLITE_UNIX_THREADS
+ if( threadsOverrideEachOthersLocks<0 ){
+ testThreadLockingBehavior(fd);
+ }
+ key1.tid = threadsOverrideEachOthersLocks ? 0 : pthread_self();
+#endif
+ memset(&key2, 0, sizeof(key2));
+ key2.dev = statbuf.st_dev;
+ key2.ino = statbuf.st_ino;
+ pLock = (struct lockInfo*)sqlite3HashFind(&lockHash, &key1, sizeof(key1));
+ if( pLock==0 ){
+ struct lockInfo *pOld;
+ pLock = sqliteMallocRaw( sizeof(*pLock) );
+ if( pLock==0 ) return 1;
+ pLock->key = key1;
+ pLock->nRef = 1;
+ pLock->cnt = 0;
+ pLock->locktype = 0;
+ pOld = sqlite3HashInsert(&lockHash, &pLock->key, sizeof(key1), pLock);
+ if( pOld!=0 ){
+ assert( pOld==pLock );
+ sqliteFree(pLock);
+ return 1;
+ }
+ }else{
+ pLock->nRef++;
+ }
+ *ppLock = pLock;
+ pOpen = (struct openCnt*)sqlite3HashFind(&openHash, &key2, sizeof(key2));
+ if( pOpen==0 ){
+ struct openCnt *pOld;
+ pOpen = sqliteMallocRaw( sizeof(*pOpen) );
+ if( pOpen==0 ){
+ releaseLockInfo(pLock);
+ return 1;
+ }
+ pOpen->key = key2;
+ pOpen->nRef = 1;
+ pOpen->nLock = 0;
+ pOpen->nPending = 0;
+ pOpen->aPending = 0;
+ pOld = sqlite3HashInsert(&openHash, &pOpen->key, sizeof(key2), pOpen);
+ if( pOld!=0 ){
+ assert( pOld==pOpen );
+ sqliteFree(pOpen);
+ releaseLockInfo(pLock);
+ return 1;
+ }
+ }else{
+ pOpen->nRef++;
+ }
+ *ppOpen = pOpen;
+ return 0;
+}
+
+/*
+** Delete the named file
+*/
+int sqlite3OsDelete(const char *zFilename){
+ unlink(zFilename);
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the named file exists.
+*/
+int sqlite3OsFileExists(const char *zFilename){
+ return access(zFilename, 0)==0;
+}
+
+/*
+** Attempt to open a file for both reading and writing. If that
+** fails, try opening it read-only. If the file does not exist,
+** try to create it.
+**
+** On success, a handle for the open file is written to *id
+** and *pReadonly is set to 0 if the file was opened for reading and
+** writing or 1 if the file was opened read-only. The function returns
+** SQLITE_OK.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id and *pReadonly unchanged.
+*/
+int sqlite3OsOpenReadWrite(
+ const char *zFilename,
+ OsFile *id,
+ int *pReadonly
+){
+ int rc;
+ assert( !id->isOpen );
+ id->dirfd = -1;
+ id->h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
+ if( id->h<0 ){
+#ifdef EISDIR
+ if( errno==EISDIR ){
+ return SQLITE_CANTOPEN;
+ }
+#endif
+ id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
+ if( id->h<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ *pReadonly = 1;
+ }else{
+ *pReadonly = 0;
+ }
+ sqlite3OsEnterMutex();
+ rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
+ sqlite3OsLeaveMutex();
+ if( rc ){
+ close(id->h);
+ return SQLITE_NOMEM;
+ }
+ id->locktype = 0;
+ id->isOpen = 1;
+ TRACE3("OPEN %-3d %s\n", id->h, zFilename);
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+
+/*
+** Attempt to open a new file for exclusive access by this process.
+** The file will be opened for both reading and writing. To avoid
+** a potential security problem, we do not allow the file to have
+** previously existed. Nor do we allow the file to be a symbolic
+** link.
+**
+** If delFlag is true, then make arrangements to automatically delete
+** the file when it is closed.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
+ int rc;
+ assert( !id->isOpen );
+ if( access(zFilename, 0)==0 ){
+ return SQLITE_CANTOPEN;
+ }
+ id->dirfd = -1;
+ id->h = open(zFilename,
+ O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600);
+ if( id->h<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ sqlite3OsEnterMutex();
+ rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
+ sqlite3OsLeaveMutex();
+ if( rc ){
+ close(id->h);
+ unlink(zFilename);
+ return SQLITE_NOMEM;
+ }
+ id->locktype = 0;
+ id->isOpen = 1;
+ if( delFlag ){
+ unlink(zFilename);
+ }
+ TRACE3("OPEN-EX %-3d %s\n", id->h, zFilename);
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a new file for read-only access.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
+ int rc;
+ assert( !id->isOpen );
+ id->dirfd = -1;
+ id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
+ if( id->h<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ sqlite3OsEnterMutex();
+ rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
+ sqlite3OsLeaveMutex();
+ if( rc ){
+ close(id->h);
+ return SQLITE_NOMEM;
+ }
+ id->locktype = 0;
+ id->isOpen = 1;
+ TRACE3("OPEN-RO %-3d %s\n", id->h, zFilename);
+ OpenCounter(+1);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a file descriptor for the directory that contains a
+** file. This file descriptor can be used to fsync() the directory
+** in order to make sure the creation of a new file is actually written
+** to disk.
+**
+** This routine is only meaningful for Unix. It is a no-op under
+** windows since windows does not support hard links.
+**
+** On success, a handle for a previously open file is at *id is
+** updated with the new directory file descriptor and SQLITE_OK is
+** returned.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id unchanged.
+*/
+int sqlite3OsOpenDirectory(
+ const char *zDirname,
+ OsFile *id
+){
+ if( !id->isOpen ){
+ /* Do not open the directory if the corresponding file is not already
+ ** open. */
+ return SQLITE_CANTOPEN;
+ }
+ assert( id->dirfd<0 );
+ id->dirfd = open(zDirname, O_RDONLY|O_BINARY, 0644);
+ if( id->dirfd<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ TRACE3("OPENDIR %-3d %s\n", id->dirfd, zDirname);
+ return SQLITE_OK;
+}
+
+/*
+** If the following global variable points to a string which is the
+** name of a directory, then that directory will be used to store
+** temporary files.
+*/
+const char *sqlite3_temp_directory = 0;
+
+/*
+** Create a temporary file name in zBuf. zBuf must be big enough to
+** hold at least SQLITE_TEMPNAME_SIZE characters.
+*/
+int sqlite3OsTempFileName(char *zBuf){
+ static const char *azDirs[] = {
+ 0,
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ };
+ static const unsigned char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ int i, j;
+ struct stat buf;
+ const char *zDir = ".";
+ azDirs[0] = sqlite3_temp_directory;
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){
+ if( azDirs[i]==0 ) continue;
+ if( stat(azDirs[i], &buf) ) continue;
+ if( !S_ISDIR(buf.st_mode) ) continue;
+ if( access(azDirs[i], 07) ) continue;
+ zDir = azDirs[i];
+ break;
+ }
+ do{
+ sprintf(zBuf, "%s/"TEMP_FILE_PREFIX, zDir);
+ j = strlen(zBuf);
+ sqlite3Randomness(15, &zBuf[j]);
+ for(i=0; i<15; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ }while( access(zBuf,0)==0 );
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a file into a buffer. Return SQLITE_OK if all
+** bytes were read successfully and SQLITE_IOERR if anything goes
+** wrong.
+*/
+int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
+ int got;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ TIMER_START;
+ got = read(id->h, pBuf, amt);
+ TIMER_END;
+ TRACE4("READ %-3d %7d %d\n", id->h, last_page, TIMER_ELAPSED);
+ SEEK(0);
+ /* if( got<0 ) got = 0; */
+ if( got==amt ){
+ return SQLITE_OK;
+ }else{
+ return SQLITE_IOERR;
+ }
+}
+
+/*
+** Write data from a buffer into a file. Return SQLITE_OK on success
+** or some other error code on failure.
+*/
+int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
+ int wrote = 0;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ SimulateDiskfullError;
+ TIMER_START;
+ while( amt>0 && (wrote = write(id->h, pBuf, amt))>0 ){
+ amt -= wrote;
+ pBuf = &((char*)pBuf)[wrote];
+ }
+ TIMER_END;
+ TRACE4("WRITE %-3d %7d %d\n", id->h, last_page, TIMER_ELAPSED);
+ SEEK(0);
+ if( amt>0 ){
+ return SQLITE_FULL;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Move the read/write pointer in a file.
+*/
+int sqlite3OsSeek(OsFile *id, i64 offset){
+ assert( id->isOpen );
+ SEEK(offset/1024 + 1);
+ lseek(id->h, offset, SEEK_SET);
+ return SQLITE_OK;
+}
+
+/*
+** The fsync() system call does not work as advertised on many
+** unix systems. The following procedure is an attempt to make
+** it work better.
+*/
+static int full_fsync(int fd){
+ int rc;
+#ifdef F_FULLFSYNC
+ rc = fcntl(fd, F_FULLFSYNC, 0);
+ if( rc ) rc = fsync(fd);
+#else
+ rc = fsync(fd);
+#endif
+ return rc;
+}
+
+/*
+** Make sure all writes to a particular file are committed to disk.
+**
+** Under Unix, also make sure that the directory entry for the file
+** has been created by fsync-ing the directory that contains the file.
+** If we do not do this and we encounter a power failure, the directory
+** entry for the journal might not exist after we reboot. The next
+** SQLite to access the file will not know that the journal exists (because
+** the directory entry for the journal was never created) and the transaction
+** will not roll back - possibly leading to database corruption.
+*/
+int sqlite3OsSync(OsFile *id){
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ TRACE2("SYNC %-3d\n", id->h);
+ if( full_fsync(id->h) ){
+ return SQLITE_IOERR;
+ }
+ if( id->dirfd>=0 ){
+ TRACE2("DIRSYNC %-3d\n", id->dirfd);
+ full_fsync(id->dirfd);
+ close(id->dirfd); /* Only need to sync once, so close the directory */
+ id->dirfd = -1; /* when we are done. */
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Sync the directory zDirname. This is a no-op on operating systems other
+** than UNIX.
+*/
+int sqlite3OsSyncDirectory(const char *zDirname){
+ int fd;
+ int r;
+ SimulateIOError(SQLITE_IOERR);
+ fd = open(zDirname, O_RDONLY|O_BINARY, 0644);
+ TRACE3("DIRSYNC %-3d (%s)\n", fd, zDirname);
+ if( fd<0 ){
+ return SQLITE_CANTOPEN;
+ }
+ r = fsync(fd);
+ close(fd);
+ return ((r==0)?SQLITE_OK:SQLITE_IOERR);
+}
+
+/*
+** Truncate an open file to a specified size
+*/
+int sqlite3OsTruncate(OsFile *id, i64 nByte){
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ return ftruncate(id->h, nByte)==0 ? SQLITE_OK : SQLITE_IOERR;
+}
+
+/*
+** Determine the current size of a file in bytes
+*/
+int sqlite3OsFileSize(OsFile *id, i64 *pSize){
+ struct stat buf;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ if( fstat(id->h, &buf)!=0 ){
+ return SQLITE_IOERR;
+ }
+ *pSize = buf.st_size;
+ return SQLITE_OK;
+}
+
+/*
+** This routine checks if there is a RESERVED lock held on the specified
+** file by this or any other process. If such a lock is held, return
+** non-zero. If the file is unlocked or holds only SHARED locks, then
+** return zero.
+*/
+int sqlite3OsCheckReservedLock(OsFile *id){
+ int r = 0;
+
+ assert( id->isOpen );
+ sqlite3OsEnterMutex(); /* Needed because id->pLock is shared across threads */
+
+ /* Check if a thread in this process holds such a lock */
+ if( id->pLock->locktype>SHARED_LOCK ){
+ r = 1;
+ }
+
+ /* Otherwise see if some other process holds it.
+ */
+ if( !r ){
+ struct flock lock;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = RESERVED_BYTE;
+ lock.l_len = 1;
+ lock.l_type = F_WRLCK;
+ fcntl(id->h, F_GETLK, &lock);
+ if( lock.l_type!=F_UNLCK ){
+ r = 1;
+ }
+ }
+
+ sqlite3OsLeaveMutex();
+ TRACE3("TEST WR-LOCK %d %d\n", id->h, r);
+
+ return r;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Helper function for printing out trace information from debugging
+** binaries. This returns the string represetation of the supplied
+** integer lock-type.
+*/
+static const char * locktypeName(int locktype){
+ switch( locktype ){
+ case NO_LOCK: return "NONE";
+ case SHARED_LOCK: return "SHARED";
+ case RESERVED_LOCK: return "RESERVED";
+ case PENDING_LOCK: return "PENDING";
+ case EXCLUSIVE_LOCK: return "EXCLUSIVE";
+ }
+ return "ERROR";
+}
+#endif
+
+/*
+** Lock the file with the lock specified by parameter locktype - one
+** of the following:
+**
+** (1) SHARED_LOCK
+** (2) RESERVED_LOCK
+** (3) PENDING_LOCK
+** (4) EXCLUSIVE_LOCK
+**
+** Sometimes when requesting one lock state, additional lock states
+** are inserted in between. The locking might fail on one of the later
+** transitions leaving the lock state different from what it started but
+** still short of its goal. The following chart shows the allowed
+** transitions and the inserted intermediate states:
+**
+** UNLOCKED -> SHARED
+** SHARED -> RESERVED
+** SHARED -> (PENDING) -> EXCLUSIVE
+** RESERVED -> (PENDING) -> EXCLUSIVE
+** PENDING -> EXCLUSIVE
+**
+** This routine will only increase a lock. Use the sqlite3OsUnlock()
+** routine to lower a locking level.
+*/
+int sqlite3OsLock(OsFile *id, int locktype){
+ /* The following describes the implementation of the various locks and
+ ** lock transitions in terms of the POSIX advisory shared and exclusive
+ ** lock primitives (called read-locks and write-locks below, to avoid
+ ** confusion with SQLite lock names). The algorithms are complicated
+ ** slightly in order to be compatible with windows systems simultaneously
+ ** accessing the same database file, in case that is ever required.
+ **
+ ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
+ ** byte', each single bytes at well known offsets, and the 'shared byte
+ ** range', a range of 510 bytes at a well known offset.
+ **
+ ** To obtain a SHARED lock, a read-lock is obtained on the 'pending
+ ** byte'. If this is successful, a random byte from the 'shared byte
+ ** range' is read-locked and the lock on the 'pending byte' released.
+ **
+ ** A process may only obtain a RESERVED lock after it has a SHARED lock.
+ ** A RESERVED lock is implemented by grabbing a write-lock on the
+ ** 'reserved byte'.
+ **
+ ** A process may only obtain a PENDING lock after it has obtained a
+ ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock
+ ** on the 'pending byte'. This ensures that no new SHARED locks can be
+ ** obtained, but existing SHARED locks are allowed to persist. A process
+ ** does not have to obtain a RESERVED lock on the way to a PENDING lock.
+ ** This property is used by the algorithm for rolling back a journal file
+ ** after a crash.
+ **
+ ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is
+ ** implemented by obtaining a write-lock on the entire 'shared byte
+ ** range'. Since all other locks require a read-lock on one of the bytes
+ ** within this range, this ensures that no other locks are held on the
+ ** database.
+ **
+ ** The reason a single byte cannot be used instead of the 'shared byte
+ ** range' is that some versions of windows do not support read-locks. By
+ ** locking a random byte from a range, concurrent SHARED locks may exist
+ ** even if the locking primitive used is always a write-lock.
+ */
+ int rc = SQLITE_OK;
+ struct lockInfo *pLock = id->pLock;
+ struct flock lock;
+ int s;
+
+ assert( id->isOpen );
+ TRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", id->h, locktypeName(locktype),
+ locktypeName(id->locktype), locktypeName(pLock->locktype), pLock->cnt
+ ,getpid() );
+
+ /* If there is already a lock of this type or more restrictive on the
+ ** OsFile, do nothing. Don't use the end_lock: exit path, as
+ ** sqlite3OsEnterMutex() hasn't been called yet.
+ */
+ if( id->locktype>=locktype ){
+ TRACE3("LOCK %d %s ok (already held)\n", id->h, locktypeName(locktype));
+ return SQLITE_OK;
+ }
+
+ /* Make sure the locking sequence is correct
+ */
+ assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK );
+ assert( locktype!=PENDING_LOCK );
+ assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK );
+
+ /* This mutex is needed because id->pLock is shared across threads
+ */
+ sqlite3OsEnterMutex();
+
+ /* If some thread using this PID has a lock via a different OsFile*
+ ** handle that precludes the requested lock, return BUSY.
+ */
+ if( (id->locktype!=pLock->locktype &&
+ (pLock->locktype>=PENDING_LOCK || locktype>SHARED_LOCK))
+ ){
+ rc = SQLITE_BUSY;
+ goto end_lock;
+ }
+
+ /* If a SHARED lock is requested, and some thread using this PID already
+ ** has a SHARED or RESERVED lock, then increment reference counts and
+ ** return SQLITE_OK.
+ */
+ if( locktype==SHARED_LOCK &&
+ (pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){
+ assert( locktype==SHARED_LOCK );
+ assert( id->locktype==0 );
+ assert( pLock->cnt>0 );
+ id->locktype = SHARED_LOCK;
+ pLock->cnt++;
+ id->pOpen->nLock++;
+ goto end_lock;
+ }
+
+ lock.l_len = 1L;
+ lock.l_whence = SEEK_SET;
+
+ /* A PENDING lock is needed before acquiring a SHARED lock and before
+ ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will
+ ** be released.
+ */
+ if( locktype==SHARED_LOCK
+ || (locktype==EXCLUSIVE_LOCK && id->locktype<PENDING_LOCK)
+ ){
+ lock.l_type = (locktype==SHARED_LOCK?F_RDLCK:F_WRLCK);
+ lock.l_start = PENDING_BYTE;
+ s = fcntl(id->h, F_SETLK, &lock);
+ if( s ){
+ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ goto end_lock;
+ }
+ }
+
+
+ /* If control gets to this point, then actually go ahead and make
+ ** operating system calls for the specified lock.
+ */
+ if( locktype==SHARED_LOCK ){
+ assert( pLock->cnt==0 );
+ assert( pLock->locktype==0 );
+
+ /* Now get the read-lock */
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
+ s = fcntl(id->h, F_SETLK, &lock);
+
+ /* Drop the temporary PENDING lock */
+ lock.l_start = PENDING_BYTE;
+ lock.l_len = 1L;
+ lock.l_type = F_UNLCK;
+ fcntl(id->h, F_SETLK, &lock);
+ if( s ){
+ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ }else{
+ id->locktype = SHARED_LOCK;
+ id->pOpen->nLock++;
+ pLock->cnt = 1;
+ }
+ }else if( locktype==EXCLUSIVE_LOCK && pLock->cnt>1 ){
+ /* We are trying for an exclusive lock but another thread in this
+ ** same process is still holding a shared lock. */
+ rc = SQLITE_BUSY;
+ }else{
+ /* The request was for a RESERVED or EXCLUSIVE lock. It is
+ ** assumed that there is a SHARED or greater lock on the file
+ ** already.
+ */
+ assert( 0!=id->locktype );
+ lock.l_type = F_WRLCK;
+ switch( locktype ){
+ case RESERVED_LOCK:
+ lock.l_start = RESERVED_BYTE;
+ break;
+ case EXCLUSIVE_LOCK:
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
+ break;
+ default:
+ assert(0);
+ }
+ s = fcntl(id->h, F_SETLK, &lock);
+ if( s ){
+ rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ id->locktype = locktype;
+ pLock->locktype = locktype;
+ }else if( locktype==EXCLUSIVE_LOCK ){
+ id->locktype = PENDING_LOCK;
+ pLock->locktype = PENDING_LOCK;
+ }
+
+end_lock:
+ sqlite3OsLeaveMutex();
+ TRACE4("LOCK %d %s %s\n", id->h, locktypeName(locktype),
+ rc==SQLITE_OK ? "ok" : "failed");
+ return rc;
+}
+
+/*
+** Lower the locking level on file descriptor id to locktype. locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+**
+** It is not possible for this routine to fail if the second argument
+** is NO_LOCK. If the second argument is SHARED_LOCK, this routine
+** might return SQLITE_IOERR instead of SQLITE_OK.
+*/
+int sqlite3OsUnlock(OsFile *id, int locktype){
+ struct lockInfo *pLock;
+ struct flock lock;
+ int rc = SQLITE_OK;
+
+ assert( id->isOpen );
+ TRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", id->h, locktype, id->locktype,
+ id->pLock->locktype, id->pLock->cnt, getpid());
+
+ assert( locktype<=SHARED_LOCK );
+ if( id->locktype<=locktype ){
+ return SQLITE_OK;
+ }
+ sqlite3OsEnterMutex();
+ pLock = id->pLock;
+ assert( pLock->cnt!=0 );
+ if( id->locktype>SHARED_LOCK ){
+ assert( pLock->locktype==id->locktype );
+ if( locktype==SHARED_LOCK ){
+ lock.l_type = F_RDLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = SHARED_FIRST;
+ lock.l_len = SHARED_SIZE;
+ if( fcntl(id->h, F_SETLK, &lock)!=0 ){
+ /* This should never happen */
+ rc = SQLITE_IOERR;
+ }
+ }
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = PENDING_BYTE;
+ lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
+ fcntl(id->h, F_SETLK, &lock);
+ pLock->locktype = SHARED_LOCK;
+ }
+ if( locktype==NO_LOCK ){
+ struct openCnt *pOpen;
+
+ /* Decrement the shared lock counter. Release the lock using an
+ ** OS call only when all threads in this same process have released
+ ** the lock.
+ */
+ pLock->cnt--;
+ if( pLock->cnt==0 ){
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = lock.l_len = 0L;
+ fcntl(id->h, F_SETLK, &lock);
+ pLock->locktype = NO_LOCK;
+ }
+
+ /* Decrement the count of locks against this same file. When the
+ ** count reaches zero, close any other file descriptors whose close
+ ** was deferred because of outstanding locks.
+ */
+ pOpen = id->pOpen;
+ pOpen->nLock--;
+ assert( pOpen->nLock>=0 );
+ if( pOpen->nLock==0 && pOpen->nPending>0 ){
+ int i;
+ for(i=0; i<pOpen->nPending; i++){
+ close(pOpen->aPending[i]);
+ }
+ sqliteFree(pOpen->aPending);
+ pOpen->nPending = 0;
+ pOpen->aPending = 0;
+ }
+ }
+ sqlite3OsLeaveMutex();
+ id->locktype = locktype;
+ return rc;
+}
+
+/*
+** Close a file.
+*/
+int sqlite3OsClose(OsFile *id){
+ if( !id->isOpen ) return SQLITE_OK;
+ sqlite3OsUnlock(id, NO_LOCK);
+ if( id->dirfd>=0 ) close(id->dirfd);
+ id->dirfd = -1;
+ sqlite3OsEnterMutex();
+ if( id->pOpen->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pOpen->aPending. It will be automatically closed when
+ ** the last lock is cleared.
+ */
+ int *aNew;
+ struct openCnt *pOpen = id->pOpen;
+ pOpen->nPending++;
+ aNew = sqliteRealloc( pOpen->aPending, pOpen->nPending*sizeof(int) );
+ if( aNew==0 ){
+ /* If a malloc fails, just leak the file descriptor */
+ }else{
+ pOpen->aPending = aNew;
+ pOpen->aPending[pOpen->nPending-1] = id->h;
+ }
+ }else{
+ /* There are no outstanding locks so we can close the file immediately */
+ close(id->h);
+ }
+ releaseLockInfo(id->pLock);
+ releaseOpenCnt(id->pOpen);
+ sqlite3OsLeaveMutex();
+ id->isOpen = 0;
+ TRACE2("CLOSE %-3d\n", id->h);
+ OpenCounter(-1);
+ return SQLITE_OK;
+}
+
+/*
+** Get information to seed the random number generator. The seed
+** is written into the buffer zBuf[256]. The calling function must
+** supply a sufficiently large buffer.
+*/
+int sqlite3OsRandomSeed(char *zBuf){
+ /* We have to initialize zBuf to prevent valgrind from reporting
+ ** errors. The reports issued by valgrind are incorrect - we would
+ ** prefer that the randomness be increased by making use of the
+ ** uninitialized space in zBuf - but valgrind errors tend to worry
+ ** some users. Rather than argue, it seems easier just to initialize
+ ** the whole array and silence valgrind, even if that means less randomness
+ ** in the random seed.
+ **
+ ** When testing, initializing zBuf[] to zero is all we do. That means
+ ** that we always use the same random number sequence.* This makes the
+ ** tests repeatable.
+ */
+ memset(zBuf, 0, 256);
+#if !defined(SQLITE_TEST)
+ {
+ int pid;
+ time((time_t*)zBuf);
+ pid = getpid();
+ memcpy(&zBuf[sizeof(time_t)], &pid, sizeof(pid));
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** Sleep for a little while. Return the amount of time slept.
+*/
+int sqlite3OsSleep(int ms){
+#if defined(HAVE_USLEEP) && HAVE_USLEEP
+ usleep(ms*1000);
+ return ms;
+#else
+ sleep((ms+999)/1000);
+ return 1000*((ms+999)/1000);
+#endif
+}
+
+/*
+** Static variables used for thread synchronization
+*/
+static int inMutex = 0;
+#ifdef SQLITE_UNIX_THREADS
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+/*
+** The following pair of routine implement mutual exclusion for
+** multi-threaded processes. Only a single thread is allowed to
+** executed code that is surrounded by EnterMutex() and LeaveMutex().
+**
+** SQLite uses only a single Mutex. There is not much critical
+** code and what little there is executes quickly and without blocking.
+*/
+void sqlite3OsEnterMutex(){
+#ifdef SQLITE_UNIX_THREADS
+ pthread_mutex_lock(&mutex);
+#endif
+ assert( !inMutex );
+ inMutex = 1;
+}
+void sqlite3OsLeaveMutex(){
+ assert( inMutex );
+ inMutex = 0;
+#ifdef SQLITE_UNIX_THREADS
+ pthread_mutex_unlock(&mutex);
+#endif
+}
+
+/*
+** Turn a relative pathname into a full pathname. Return a pointer
+** to the full pathname stored in space obtained from sqliteMalloc().
+** The calling function is responsible for freeing this space once it
+** is no longer needed.
+*/
+char *sqlite3OsFullPathname(const char *zRelative){
+ char *zFull = 0;
+ if( zRelative[0]=='/' ){
+ sqlite3SetString(&zFull, zRelative, (char*)0);
+ }else{
+ char zBuf[5000];
+ sqlite3SetString(&zFull, getcwd(zBuf, sizeof(zBuf)), "/", zRelative,
+ (char*)0);
+ }
+ return zFull;
+}
+
+/*
+** The following variable, if set to a non-zero value, becomes the result
+** returned from sqlite3OsCurrentTime(). This is used for testing.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_current_time = 0;
+#endif
+
+/*
+** Find the current time (in Universal Coordinated Time). Write the
+** current time and date as a Julian Day number into *prNow and
+** return 0. Return 1 if the time and date cannot be found.
+*/
+int sqlite3OsCurrentTime(double *prNow){
+ time_t t;
+ time(&t);
+ *prNow = t/86400.0 + 2440587.5;
+#ifdef SQLITE_TEST
+ if( sqlite3_current_time ){
+ *prNow = sqlite3_current_time/86400.0 + 2440587.5;
+ }
+#endif
+ return 0;
+}
+
+#if 0 /* NOT USED */
+/*
+** Find the time that the file was last modified. Write the
+** modification time and date as a Julian Day number into *prNow and
+** return SQLITE_OK. Return SQLITE_ERROR if the modification
+** time cannot be found.
+*/
+int sqlite3OsFileModTime(OsFile *id, double *prNow){
+ int rc;
+ struct stat statbuf;
+ if( fstat(id->h, &statbuf)==0 ){
+ *prNow = statbuf.st_mtime/86400.0 + 2440587.5;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+#endif /* NOT USED */
+
+#endif /* OS_UNIX */
diff --git a/kopete/plugins/statistics/sqlite/os_unix.h b/kopete/plugins/statistics/sqlite/os_unix.h
new file mode 100644
index 00000000..72f818be
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_unix.h
@@ -0,0 +1,89 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This header file defined OS-specific features for Unix.
+*/
+#ifndef _SQLITE_OS_UNIX_H_
+#define _SQLITE_OS_UNIX_H_
+
+/*
+** Helpful hint: To get this to compile on HP/UX, add -D_INCLUDE_POSIX_SOURCE
+** to the compiler command line.
+*/
+
+/*
+** These #defines should enable >2GB file support on Posix if the
+** underlying operating system supports it. If the OS lacks
+** large file support, or if the OS is windows, these should be no-ops.
+**
+** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
+** on the compiler command line. This is necessary if you are compiling
+** on a recent machine (ex: RedHat 7.2) but you want your code to work
+** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
+** without this option, LFS is enable. But LFS does not exist in the kernel
+** in RedHat 6.0, so the code won't work. Hence, for maximum binary
+** portability you should omit LFS.
+**
+** Similar is true for MacOS. LFS is only supported on MacOS 9 and later.
+*/
+#ifndef SQLITE_DISABLE_LFS
+# define _LARGE_FILE 1
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+# define _LARGEFILE_SOURCE 1
+#endif
+
+/*
+** standard include files.
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/*
+** The OsFile structure is a operating-system independing representation
+** of an open file handle. It is defined differently for each architecture.
+**
+** This is the definition for Unix.
+**
+** OsFile.locktype takes one of the values SHARED_LOCK, RESERVED_LOCK,
+** PENDING_LOCK or EXCLUSIVE_LOCK.
+*/
+typedef struct OsFile OsFile;
+struct OsFile {
+ struct Pager *pPager; /* The pager that owns this OsFile. Might be 0 */
+ struct openCnt *pOpen; /* Info about all open fd's on this inode */
+ struct lockInfo *pLock; /* Info about locks on this inode */
+ int h; /* The file descriptor */
+ unsigned char locktype; /* The type of lock held on this fd */
+ unsigned char isOpen; /* True if needs to be closed */
+ int dirfd; /* File descriptor for the directory */
+};
+
+/*
+** Maximum number of characters in a temporary file name
+*/
+#define SQLITE_TEMPNAME_SIZE 200
+
+/*
+** Minimum interval supported by sqlite3OsSleep().
+*/
+#if defined(HAVE_USLEEP) && HAVE_USLEEP
+# define SQLITE_MIN_SLEEP_MS 1
+#else
+# define SQLITE_MIN_SLEEP_MS 1000
+#endif
+
+
+#endif /* _SQLITE_OS_UNIX_H_ */
diff --git a/kopete/plugins/statistics/sqlite/os_win.c b/kopete/plugins/statistics/sqlite/os_win.c
new file mode 100644
index 00000000..f6e3e3ea
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_win.c
@@ -0,0 +1,747 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code that is specific to windows.
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#if OS_WIN /* This file is used for windows only */
+
+#include <winbase.h>
+
+/*
+** Macros used to determine whether or not to use threads.
+*/
+#if defined(THREADSAFE) && THREADSAFE
+# define SQLITE_W32_THREADS 1
+#endif
+
+/*
+** Include code that is common to all os_*.c files
+*/
+#include "os_common.h"
+
+/*
+** Delete the named file
+*/
+int sqlite3OsDelete(const char *zFilename){
+ DeleteFileA(zFilename);
+ TRACE2("DELETE \"%s\"\n", zFilename);
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the named file exists.
+*/
+int sqlite3OsFileExists(const char *zFilename){
+ return GetFileAttributesA(zFilename) != 0xffffffff;
+}
+
+/*
+** Attempt to open a file for both reading and writing. If that
+** fails, try opening it read-only. If the file does not exist,
+** try to create it.
+**
+** On success, a handle for the open file is written to *id
+** and *pReadonly is set to 0 if the file was opened for reading and
+** writing or 1 if the file was opened read-only. The function returns
+** SQLITE_OK.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id and *pReadonly unchanged.
+*/
+int sqlite3OsOpenReadWrite(
+ const char *zFilename,
+ OsFile *id,
+ int *pReadonly
+){
+ HANDLE h;
+ assert( !id->isOpen );
+ h = CreateFileA(zFilename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
+ NULL
+ );
+ if( h==INVALID_HANDLE_VALUE ){
+ h = CreateFileA(zFilename,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
+ NULL
+ );
+ if( h==INVALID_HANDLE_VALUE ){
+ return SQLITE_CANTOPEN;
+ }
+ *pReadonly = 1;
+ }else{
+ *pReadonly = 0;
+ }
+ id->h = h;
+ id->locktype = NO_LOCK;
+ id->sharedLockByte = 0;
+ id->isOpen = 1;
+ OpenCounter(+1);
+ TRACE3("OPEN R/W %d \"%s\"\n", h, zFilename);
+ return SQLITE_OK;
+}
+
+
+/*
+** Attempt to open a new file for exclusive access by this process.
+** The file will be opened for both reading and writing. To avoid
+** a potential security problem, we do not allow the file to have
+** previously existed. Nor do we allow the file to be a symbolic
+** link.
+**
+** If delFlag is true, then make arrangements to automatically delete
+** the file when it is closed.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
+ HANDLE h;
+ int fileflags;
+ assert( !id->isOpen );
+ if( delFlag ){
+ fileflags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_RANDOM_ACCESS
+ | FILE_FLAG_DELETE_ON_CLOSE;
+ }else{
+ fileflags = FILE_FLAG_RANDOM_ACCESS;
+ }
+ h = CreateFileA(zFilename,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ fileflags,
+ NULL
+ );
+ if( h==INVALID_HANDLE_VALUE ){
+ return SQLITE_CANTOPEN;
+ }
+ id->h = h;
+ id->locktype = NO_LOCK;
+ id->sharedLockByte = 0;
+ id->isOpen = 1;
+ OpenCounter(+1);
+ TRACE3("OPEN EX %d \"%s\"\n", h, zFilename);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a new file for read-only access.
+**
+** On success, write the file handle into *id and return SQLITE_OK.
+**
+** On failure, return SQLITE_CANTOPEN.
+*/
+int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
+ HANDLE h;
+ assert( !id->isOpen );
+ h = CreateFileA(zFilename,
+ GENERIC_READ,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
+ NULL
+ );
+ if( h==INVALID_HANDLE_VALUE ){
+ return SQLITE_CANTOPEN;
+ }
+ id->h = h;
+ id->locktype = NO_LOCK;
+ id->sharedLockByte = 0;
+ id->isOpen = 1;
+ OpenCounter(+1);
+ TRACE3("OPEN RO %d \"%s\"\n", h, zFilename);
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to open a file descriptor for the directory that contains a
+** file. This file descriptor can be used to fsync() the directory
+** in order to make sure the creation of a new file is actually written
+** to disk.
+**
+** This routine is only meaningful for Unix. It is a no-op under
+** windows since windows does not support hard links.
+**
+** On success, a handle for a previously open file is at *id is
+** updated with the new directory file descriptor and SQLITE_OK is
+** returned.
+**
+** On failure, the function returns SQLITE_CANTOPEN and leaves
+** *id unchanged.
+*/
+int sqlite3OsOpenDirectory(
+ const char *zDirname,
+ OsFile *id
+){
+ return SQLITE_OK;
+}
+
+/*
+** If the following global variable points to a string which is the
+** name of a directory, then that directory will be used to store
+** temporary files.
+*/
+const char *sqlite3_temp_directory = 0;
+
+/*
+** Create a temporary file name in zBuf. zBuf must be big enough to
+** hold at least SQLITE_TEMPNAME_SIZE characters.
+*/
+int sqlite3OsTempFileName(char *zBuf){
+ static char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789";
+ int i, j;
+ char zTempPath[SQLITE_TEMPNAME_SIZE];
+ if( sqlite3_temp_directory ){
+ strncpy(zTempPath, sqlite3_temp_directory, SQLITE_TEMPNAME_SIZE-30);
+ zTempPath[SQLITE_TEMPNAME_SIZE-30] = 0;
+ }else{
+ GetTempPathA(SQLITE_TEMPNAME_SIZE-30, zTempPath);
+ }
+ for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
+ zTempPath[i] = 0;
+ for(;;){
+ sprintf(zBuf, "%s\\"TEMP_FILE_PREFIX, zTempPath);
+ j = strlen(zBuf);
+ sqlite3Randomness(15, &zBuf[j]);
+ for(i=0; i<15; i++, j++){
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
+ }
+ zBuf[j] = 0;
+ if( !sqlite3OsFileExists(zBuf) ) break;
+ }
+ TRACE2("TEMP FILENAME: %s\n", zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** Close a file.
+*/
+int sqlite3OsClose(OsFile *id){
+ if( id->isOpen ){
+ TRACE2("CLOSE %d\n", id->h);
+ CloseHandle(id->h);
+ OpenCounter(-1);
+ id->isOpen = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Read data from a file into a buffer. Return SQLITE_OK if all
+** bytes were read successfully and SQLITE_IOERR if anything goes
+** wrong.
+*/
+int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
+ DWORD got;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ TRACE3("READ %d lock=%d\n", id->h, id->locktype);
+ if( !ReadFile(id->h, pBuf, amt, &got, 0) ){
+ got = 0;
+ }
+ if( got==(DWORD)amt ){
+ return SQLITE_OK;
+ }else{
+ return SQLITE_IOERR;
+ }
+}
+
+/*
+** Write data from a buffer into a file. Return SQLITE_OK on success
+** or some other error code on failure.
+*/
+int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
+ int rc;
+ DWORD wrote;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ SimulateDiskfullError;
+ TRACE3("WRITE %d lock=%d\n", id->h, id->locktype);
+ while( amt>0 && (rc = WriteFile(id->h, pBuf, amt, &wrote, 0))!=0 && wrote>0 ){
+ amt -= wrote;
+ pBuf = &((char*)pBuf)[wrote];
+ }
+ if( !rc || amt>(int)wrote ){
+ return SQLITE_FULL;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Move the read/write pointer in a file.
+*/
+int sqlite3OsSeek(OsFile *id, i64 offset){
+ LONG upperBits = offset>>32;
+ LONG lowerBits = offset & 0xffffffff;
+ DWORD rc;
+ assert( id->isOpen );
+ SEEK(offset/1024 + 1);
+ rc = SetFilePointer(id->h, lowerBits, &upperBits, FILE_BEGIN);
+ TRACE3("SEEK %d %lld\n", id->h, offset);
+ return SQLITE_OK;
+}
+
+/*
+** Make sure all writes to a particular file are committed to disk.
+*/
+int sqlite3OsSync(OsFile *id){
+ assert( id->isOpen );
+ TRACE3("SYNC %d lock=%d\n", id->h, id->locktype);
+ if( FlushFileBuffers(id->h) ){
+ return SQLITE_OK;
+ }else{
+ return SQLITE_IOERR;
+ }
+}
+
+/*
+** Sync the directory zDirname. This is a no-op on operating systems other
+** than UNIX.
+*/
+int sqlite3OsSyncDirectory(const char *zDirname){
+ SimulateIOError(SQLITE_IOERR);
+ return SQLITE_OK;
+}
+
+/*
+** Truncate an open file to a specified size
+*/
+int sqlite3OsTruncate(OsFile *id, i64 nByte){
+ LONG upperBits = nByte>>32;
+ assert( id->isOpen );
+ TRACE3("TRUNCATE %d %lld\n", id->h, nByte);
+ SimulateIOError(SQLITE_IOERR);
+ SetFilePointer(id->h, nByte, &upperBits, FILE_BEGIN);
+ SetEndOfFile(id->h);
+ return SQLITE_OK;
+}
+
+/*
+** Determine the current size of a file in bytes
+*/
+int sqlite3OsFileSize(OsFile *id, i64 *pSize){
+ DWORD upperBits, lowerBits;
+ assert( id->isOpen );
+ SimulateIOError(SQLITE_IOERR);
+ lowerBits = GetFileSize(id->h, &upperBits);
+ *pSize = (((i64)upperBits)<<32) + lowerBits;
+ return SQLITE_OK;
+}
+
+/*
+** Return true (non-zero) if we are running under WinNT, Win2K or WinXP.
+** Return false (zero) for Win95, Win98, or WinME.
+**
+** Here is an interesting observation: Win95, Win98, and WinME lack
+** the LockFileEx() API. But we can still statically link against that
+** API as long as we don't call it win running Win95/98/ME. A call to
+** this routine is used to determine if the host is Win95/98/ME or
+** WinNT/2K/XP so that we will know whether or not we can safely call
+** the LockFileEx() API.
+*/
+static int isNT(void){
+ static int osType = 0; /* 0=unknown 1=win95 2=winNT */
+ if( osType==0 ){
+ OSVERSIONINFO sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ GetVersionEx(&sInfo);
+ osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
+ }
+ return osType==2;
+}
+
+/*
+** Acquire a reader lock.
+** Different API routines are called depending on whether or not this
+** is Win95 or WinNT.
+*/
+static int getReadLock(OsFile *id){
+ int res;
+ if( isNT() ){
+ OVERLAPPED ovlp;
+ ovlp.Offset = SHARED_FIRST;
+ ovlp.OffsetHigh = 0;
+ ovlp.hEvent = 0;
+ res = LockFileEx(id->h, LOCKFILE_FAIL_IMMEDIATELY, 0, SHARED_SIZE,0,&ovlp);
+ }else{
+ int lk;
+ sqlite3Randomness(sizeof(lk), &lk);
+ id->sharedLockByte = (lk & 0x7fffffff)%(SHARED_SIZE - 1);
+ res = LockFile(id->h, SHARED_FIRST+id->sharedLockByte, 0, 1, 0);
+ }
+ return res;
+}
+
+/*
+** Undo a readlock
+*/
+static int unlockReadLock(OsFile *id){
+ int res;
+ if( isNT() ){
+ res = UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ }else{
+ res = UnlockFile(id->h, SHARED_FIRST + id->sharedLockByte, 0, 1, 0);
+ }
+ return res;
+}
+
+/*
+** Lock the file with the lock specified by parameter locktype - one
+** of the following:
+**
+** (1) SHARED_LOCK
+** (2) RESERVED_LOCK
+** (3) PENDING_LOCK
+** (4) EXCLUSIVE_LOCK
+**
+** Sometimes when requesting one lock state, additional lock states
+** are inserted in between. The locking might fail on one of the later
+** transitions leaving the lock state different from what it started but
+** still short of its goal. The following chart shows the allowed
+** transitions and the inserted intermediate states:
+**
+** UNLOCKED -> SHARED
+** SHARED -> RESERVED
+** SHARED -> (PENDING) -> EXCLUSIVE
+** RESERVED -> (PENDING) -> EXCLUSIVE
+** PENDING -> EXCLUSIVE
+**
+** This routine will only increase a lock. The sqlite3OsUnlock() routine
+** erases all locks at once and returns us immediately to locking level 0.
+** It is not possible to lower the locking level one step at a time. You
+** must go straight to locking level 0.
+*/
+int sqlite3OsLock(OsFile *id, int locktype){
+ int rc = SQLITE_OK; /* Return code from subroutines */
+ int res = 1; /* Result of a windows lock call */
+ int newLocktype; /* Set id->locktype to this value before exiting */
+ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
+
+ assert( id->isOpen );
+ TRACE5("LOCK %d %d was %d(%d)\n",
+ id->h, locktype, id->locktype, id->sharedLockByte);
+
+ /* If there is already a lock of this type or more restrictive on the
+ ** OsFile, do nothing. Don't use the end_lock: exit path, as
+ ** sqlite3OsEnterMutex() hasn't been called yet.
+ */
+ if( id->locktype>=locktype ){
+ return SQLITE_OK;
+ }
+
+ /* Make sure the locking sequence is correct
+ */
+ assert( id->locktype!=NO_LOCK || locktype==SHARED_LOCK );
+ assert( locktype!=PENDING_LOCK );
+ assert( locktype!=RESERVED_LOCK || id->locktype==SHARED_LOCK );
+
+ /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
+ ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
+ ** the PENDING_LOCK byte is temporary.
+ */
+ newLocktype = id->locktype;
+ if( id->locktype==NO_LOCK
+ || (locktype==EXCLUSIVE_LOCK && id->locktype==RESERVED_LOCK)
+ ){
+ int cnt = 3;
+ while( cnt-->0 && (res = LockFile(id->h, PENDING_BYTE, 0, 1, 0))==0 ){
+ /* Try 3 times to get the pending lock. The pending lock might be
+ ** held by another reader process who will release it momentarily.
+ */
+ TRACE2("could not get a PENDING lock. cnt=%d\n", cnt);
+ Sleep(1);
+ }
+ gotPendingLock = res;
+ }
+
+ /* Acquire a shared lock
+ */
+ if( locktype==SHARED_LOCK && res ){
+ assert( id->locktype==NO_LOCK );
+ res = getReadLock(id);
+ if( res ){
+ newLocktype = SHARED_LOCK;
+ }
+ }
+
+ /* Acquire a RESERVED lock
+ */
+ if( locktype==RESERVED_LOCK && res ){
+ assert( id->locktype==SHARED_LOCK );
+ res = LockFile(id->h, RESERVED_BYTE, 0, 1, 0);
+ if( res ){
+ newLocktype = RESERVED_LOCK;
+ }
+ }
+
+ /* Acquire a PENDING lock
+ */
+ if( locktype==EXCLUSIVE_LOCK && res ){
+ newLocktype = PENDING_LOCK;
+ gotPendingLock = 0;
+ }
+
+ /* Acquire an EXCLUSIVE lock
+ */
+ if( locktype==EXCLUSIVE_LOCK && res ){
+ assert( id->locktype>=SHARED_LOCK );
+ res = unlockReadLock(id);
+ TRACE2("unreadlock = %d\n", res);
+ res = LockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ if( res ){
+ newLocktype = EXCLUSIVE_LOCK;
+ }else{
+ TRACE2("error-code = %d\n", GetLastError());
+ }
+ }
+
+ /* If we are holding a PENDING lock that ought to be released, then
+ ** release it now.
+ */
+ if( gotPendingLock && locktype==SHARED_LOCK ){
+ UnlockFile(id->h, PENDING_BYTE, 0, 1, 0);
+ }
+
+ /* Update the state of the lock has held in the file descriptor then
+ ** return the appropriate result code.
+ */
+ if( res ){
+ rc = SQLITE_OK;
+ }else{
+ TRACE4("LOCK FAILED %d trying for %d but got %d\n", id->h,
+ locktype, newLocktype);
+ rc = SQLITE_BUSY;
+ }
+ id->locktype = newLocktype;
+ return rc;
+}
+
+/*
+** This routine checks if there is a RESERVED lock held on the specified
+** file by this or any other process. If such a lock is held, return
+** non-zero, otherwise zero.
+*/
+int sqlite3OsCheckReservedLock(OsFile *id){
+ int rc;
+ assert( id->isOpen );
+ if( id->locktype>=RESERVED_LOCK ){
+ rc = 1;
+ TRACE3("TEST WR-LOCK %d %d (local)\n", id->h, rc);
+ }else{
+ rc = LockFile(id->h, RESERVED_BYTE, 0, 1, 0);
+ if( rc ){
+ UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
+ }
+ rc = !rc;
+ TRACE3("TEST WR-LOCK %d %d (remote)\n", id->h, rc);
+ }
+ return rc;
+}
+
+/*
+** Lower the locking level on file descriptor id to locktype. locktype
+** must be either NO_LOCK or SHARED_LOCK.
+**
+** If the locking level of the file descriptor is already at or below
+** the requested locking level, this routine is a no-op.
+**
+** It is not possible for this routine to fail if the second argument
+** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
+** might return SQLITE_IOERR;
+*/
+int sqlite3OsUnlock(OsFile *id, int locktype){
+ int type;
+ int rc = SQLITE_OK;
+ assert( id->isOpen );
+ assert( locktype<=SHARED_LOCK );
+ TRACE5("UNLOCK %d to %d was %d(%d)\n", id->h, locktype,
+ id->locktype, id->sharedLockByte);
+ type = id->locktype;
+ if( type>=EXCLUSIVE_LOCK ){
+ UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ if( locktype==SHARED_LOCK && !getReadLock(id) ){
+ /* This should never happen. We should always be able to
+ ** reacquire the read lock */
+ rc = SQLITE_IOERR;
+ }
+ }
+ if( type>=RESERVED_LOCK ){
+ UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
+ }
+ if( locktype==NO_LOCK && type>=SHARED_LOCK ){
+ unlockReadLock(id);
+ }
+ if( type>=PENDING_LOCK ){
+ UnlockFile(id->h, PENDING_BYTE, 0, 1, 0);
+ }
+ id->locktype = locktype;
+ return rc;
+}
+
+/*
+** Get information to seed the random number generator. The seed
+** is written into the buffer zBuf[256]. The calling function must
+** supply a sufficiently large buffer.
+*/
+int sqlite3OsRandomSeed(char *zBuf){
+ /* We have to initialize zBuf to prevent valgrind from reporting
+ ** errors. The reports issued by valgrind are incorrect - we would
+ ** prefer that the randomness be increased by making use of the
+ ** uninitialized space in zBuf - but valgrind errors tend to worry
+ ** some users. Rather than argue, it seems easier just to initialize
+ ** the whole array and silence valgrind, even if that means less randomness
+ ** in the random seed.
+ **
+ ** When testing, initializing zBuf[] to zero is all we do. That means
+ ** that we always use the same random number sequence.* This makes the
+ ** tests repeatable.
+ */
+ memset(zBuf, 0, 256);
+ GetSystemTime((LPSYSTEMTIME)zBuf);
+ return SQLITE_OK;
+}
+
+/*
+** Sleep for a little while. Return the amount of time slept.
+*/
+int sqlite3OsSleep(int ms){
+ Sleep(ms);
+ return ms;
+}
+
+/*
+** Static variables used for thread synchronization
+*/
+static int inMutex = 0;
+#ifdef SQLITE_W32_THREADS
+ static CRITICAL_SECTION cs;
+#endif
+
+/*
+** The following pair of routine implement mutual exclusion for
+** multi-threaded processes. Only a single thread is allowed to
+** executed code that is surrounded by EnterMutex() and LeaveMutex().
+**
+** SQLite uses only a single Mutex. There is not much critical
+** code and what little there is executes quickly and without blocking.
+*/
+void sqlite3OsEnterMutex(){
+#ifdef SQLITE_W32_THREADS
+ static int isInit = 0;
+ while( !isInit ){
+ static long lock = 0;
+ if( InterlockedIncrement(&lock)==1 ){
+ InitializeCriticalSection(&cs);
+ isInit = 1;
+ }else{
+ Sleep(1);
+ }
+ }
+ EnterCriticalSection(&cs);
+#endif
+ assert( !inMutex );
+ inMutex = 1;
+}
+void sqlite3OsLeaveMutex(){
+ assert( inMutex );
+ inMutex = 0;
+#ifdef SQLITE_W32_THREADS
+ LeaveCriticalSection(&cs);
+#endif
+}
+
+/*
+** Turn a relative pathname into a full pathname. Return a pointer
+** to the full pathname stored in space obtained from sqliteMalloc().
+** The calling function is responsible for freeing this space once it
+** is no longer needed.
+*/
+char *sqlite3OsFullPathname(const char *zRelative){
+ char *zNotUsed;
+ char *zFull;
+ int nByte;
+ nByte = GetFullPathNameA(zRelative, 0, 0, &zNotUsed) + 1;
+ zFull = sqliteMalloc( nByte );
+ if( zFull==0 ) return 0;
+ GetFullPathNameA(zRelative, nByte, zFull, &zNotUsed);
+ return zFull;
+}
+
+/*
+** The following variable, if set to a non-zero value, becomes the result
+** returned from sqlite3OsCurrentTime(). This is used for testing.
+*/
+#ifdef SQLITE_TEST
+int sqlite3_current_time = 0;
+#endif
+
+/*
+** Find the current time (in Universal Coordinated Time). Write the
+** current time and date as a Julian Day number into *prNow and
+** return 0. Return 1 if the time and date cannot be found.
+*/
+int sqlite3OsCurrentTime(double *prNow){
+ FILETIME ft;
+ /* FILETIME structure is a 64-bit value representing the number of
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
+ */
+ double now;
+ GetSystemTimeAsFileTime( &ft );
+ now = ((double)ft.dwHighDateTime) * 4294967296.0;
+ *prNow = (now + ft.dwLowDateTime)/864000000000.0 + 2305813.5;
+#ifdef SQLITE_TEST
+ if( sqlite3_current_time ){
+ *prNow = sqlite3_current_time/86400.0 + 2440587.5;
+ }
+#endif
+ return 0;
+}
+
+/*
+** Find the time that the file was last modified. Write the
+** modification time and date as a Julian Day number into *prNow and
+** return SQLITE_OK. Return SQLITE_ERROR if the modification
+** time cannot be found.
+*/
+int sqlite3OsFileModTime(OsFile *id, double *prMTime){
+ int rc;
+ FILETIME ft;
+ /* FILETIME structure is a 64-bit value representing the number of
+ ** 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
+ */
+ if( GetFileTime(id->h, 0, 0, &ft) ){
+ double t;
+ t = ((double)ft.dwHighDateTime) * 4294967296.0;
+ *prMTime = (t + ft.dwLowDateTime)/864000000000.0 + 2305813.5;
+ rc = SQLITE_OK;
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+
+#endif /* OS_WIN */
diff --git a/kopete/plugins/statistics/sqlite/os_win.h b/kopete/plugins/statistics/sqlite/os_win.h
new file mode 100644
index 00000000..baf937b2
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/os_win.h
@@ -0,0 +1,40 @@
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This header file defines OS-specific features for Win32
+*/
+#ifndef _SQLITE_OS_WIN_H_
+#define _SQLITE_OS_WIN_H_
+
+#include <windows.h>
+#include <winbase.h>
+
+/*
+** The OsFile structure is a operating-system independing representation
+** of an open file handle. It is defined differently for each architecture.
+**
+** This is the definition for Win32.
+*/
+typedef struct OsFile OsFile;
+struct OsFile {
+ HANDLE h; /* Handle for accessing the file */
+ unsigned char locktype; /* Type of lock currently held on this file */
+ unsigned char isOpen; /* True if needs to be closed */
+ short sharedLockByte; /* Randomly chosen byte used as a shared lock */
+};
+
+
+#define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
+#define SQLITE_MIN_SLEEP_MS 1
+
+
+#endif /* _SQLITE_OS_WIN_H_ */
diff --git a/kopete/plugins/statistics/sqlite/pager.c b/kopete/plugins/statistics/sqlite/pager.c
new file mode 100644
index 00000000..a374562b
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/pager.c
@@ -0,0 +1,3205 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the implementation of the page cache subsystem or "pager".
+**
+** The pager is used to access a database disk file. It implements
+** atomic commit and rollback through the use of a journal file that
+** is separate from the database file. The pager also implements file
+** locking to prevent two processes from writing the same database
+** file simultaneously, or one process from reading the database while
+** another is writing.
+**
+** @(#) $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include "pager.h"
+#include <assert.h>
+#include <string.h>
+
+/*
+** Macros for troubleshooting. Normally turned off
+*/
+#if 0
+#define TRACE1(X) sqlite3DebugPrintf(X)
+#define TRACE2(X,Y) sqlite3DebugPrintf(X,Y)
+#define TRACE3(X,Y,Z) sqlite3DebugPrintf(X,Y,Z)
+#define TRACE4(X,Y,Z,W) sqlite3DebugPrintf(X,Y,Z,W)
+#else
+#define TRACE1(X)
+#define TRACE2(X,Y)
+#define TRACE3(X,Y,Z)
+#define TRACE4(X,Y,Z,W)
+#endif
+
+
+/*
+** The page cache as a whole is always in one of the following
+** states:
+**
+** PAGER_UNLOCK The page cache is not currently reading or
+** writing the database file. There is no
+** data held in memory. This is the initial
+** state.
+**
+** PAGER_SHARED The page cache is reading the database.
+** Writing is not permitted. There can be
+** multiple readers accessing the same database
+** file at the same time.
+**
+** PAGER_RESERVED This process has reserved the database for writing
+** but has not yet made any changes. Only one process
+** at a time can reserve the database. The original
+** database file has not been modified so other
+** processes may still be reading the on-disk
+** database file.
+**
+** PAGER_EXCLUSIVE The page cache is writing the database.
+** Access is exclusive. No other processes or
+** threads can be reading or writing while one
+** process is writing.
+**
+** PAGER_SYNCED The pager moves to this state from PAGER_EXCLUSIVE
+** after all dirty pages have been written to the
+** database file and the file has been synced to
+** disk. All that remains to do is to remove the
+** journal file and the transaction will be
+** committed.
+**
+** The page cache comes up in PAGER_UNLOCK. The first time a
+** sqlite3pager_get() occurs, the state transitions to PAGER_SHARED.
+** After all pages have been released using sqlite_page_unref(),
+** the state transitions back to PAGER_UNLOCK. The first time
+** that sqlite3pager_write() is called, the state transitions to
+** PAGER_RESERVED. (Note that sqlite_page_write() can only be
+** called on an outstanding page which means that the pager must
+** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
+** The transition to PAGER_EXCLUSIVE occurs when before any changes
+** are made to the database file. After an sqlite3pager_rollback()
+** or sqlite_pager_commit(), the state goes back to PAGER_SHARED.
+*/
+#define PAGER_UNLOCK 0
+#define PAGER_SHARED 1 /* same as SHARED_LOCK */
+#define PAGER_RESERVED 2 /* same as RESERVED_LOCK */
+#define PAGER_EXCLUSIVE 4 /* same as EXCLUSIVE_LOCK */
+#define PAGER_SYNCED 5
+
+/*
+** If the SQLITE_BUSY_RESERVED_LOCK macro is set to true at compile-time,
+** then failed attempts to get a reserved lock will invoke the busy callback.
+** This is off by default. To see why, consider the following scenario:
+**
+** Suppose thread A already has a shared lock and wants a reserved lock.
+** Thread B already has a reserved lock and wants an exclusive lock. If
+** both threads are using their busy callbacks, it might be a long time
+** be for one of the threads give up and allows the other to proceed.
+** But if the thread trying to get the reserved lock gives up quickly
+** (if it never invokes its busy callback) then the contention will be
+** resolved quickly.
+*/
+#ifndef SQLITE_BUSY_RESERVED_LOCK
+# define SQLITE_BUSY_RESERVED_LOCK 0
+#endif
+
+/*
+** Each in-memory image of a page begins with the following header.
+** This header is only visible to this pager module. The client
+** code that calls pager sees only the data that follows the header.
+**
+** Client code should call sqlite3pager_write() on a page prior to making
+** any modifications to that page. The first time sqlite3pager_write()
+** is called, the original page contents are written into the rollback
+** journal and PgHdr.inJournal and PgHdr.needSync are set. Later, once
+** the journal page has made it onto the disk surface, PgHdr.needSync
+** is cleared. The modified page cannot be written back into the original
+** database file until the journal pages has been synced to disk and the
+** PgHdr.needSync has been cleared.
+**
+** The PgHdr.dirty flag is set when sqlite3pager_write() is called and
+** is cleared again when the page content is written back to the original
+** database file.
+*/
+typedef struct PgHdr PgHdr;
+struct PgHdr {
+ Pager *pPager; /* The pager to which this page belongs */
+ Pgno pgno; /* The page number for this page */
+ PgHdr *pNextHash, *pPrevHash; /* Hash collision chain for PgHdr.pgno */
+ PgHdr *pNextFree, *pPrevFree; /* Freelist of pages where nRef==0 */
+ PgHdr *pNextAll; /* A list of all pages */
+ PgHdr *pNextStmt, *pPrevStmt; /* List of pages in the statement journal */
+ u8 inJournal; /* TRUE if has been written to journal */
+ u8 inStmt; /* TRUE if in the statement subjournal */
+ u8 dirty; /* TRUE if we need to write back changes */
+ u8 needSync; /* Sync journal before writing this page */
+ u8 alwaysRollback; /* Disable dont_rollback() for this page */
+ short int nRef; /* Number of users of this page */
+ PgHdr *pDirty; /* Dirty pages sorted by PgHdr.pgno */
+ /* pPager->pageSize bytes of page data follow this header */
+ /* Pager.nExtra bytes of local data follow the page data */
+};
+
+/*
+** For an in-memory only database, some extra information is recorded about
+** each page so that changes can be rolled back. (Journal files are not
+** used for in-memory databases.) The following information is added to
+** the end of every EXTRA block for in-memory databases.
+**
+** This information could have been added directly to the PgHdr structure.
+** But then it would take up an extra 8 bytes of storage on every PgHdr
+** even for disk-based databases. Splitting it out saves 8 bytes. This
+** is only a savings of 0.8% but those percentages add up.
+*/
+typedef struct PgHistory PgHistory;
+struct PgHistory {
+ u8 *pOrig; /* Original page text. Restore to this on a full rollback */
+ u8 *pStmt; /* Text as it was at the beginning of the current statement */
+};
+
+/*
+** A macro used for invoking the codec if there is one
+*/
+#ifdef SQLITE_HAS_CODEC
+# define CODEC(P,D,N,X) if( P->xCodec ){ P->xCodec(P->pCodecArg,D,N,X); }
+#else
+# define CODEC(P,D,N,X)
+#endif
+
+/*
+** Convert a pointer to a PgHdr into a pointer to its data
+** and back again.
+*/
+#define PGHDR_TO_DATA(P) ((void*)(&(P)[1]))
+#define DATA_TO_PGHDR(D) (&((PgHdr*)(D))[-1])
+#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->pageSize])
+#define PGHDR_TO_HIST(P,PGR) \
+ ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra])
+
+/*
+** How big to make the hash table used for locating in-memory pages
+** by page number.
+*/
+#define N_PG_HASH 2048
+
+/*
+** Hash a page number
+*/
+#define pager_hash(PN) ((PN)&(N_PG_HASH-1))
+
+/*
+** A open page cache is an instance of the following structure.
+*/
+struct Pager {
+ char *zFilename; /* Name of the database file */
+ char *zJournal; /* Name of the journal file */
+ char *zDirectory; /* Directory hold database and journal files */
+ OsFile fd, jfd; /* File descriptors for database and journal */
+ OsFile stfd; /* File descriptor for the statement subjournal*/
+ int dbSize; /* Number of pages in the file */
+ int origDbSize; /* dbSize before the current change */
+ int stmtSize; /* Size of database (in pages) at stmt_begin() */
+ i64 stmtJSize; /* Size of journal at stmt_begin() */
+ int nRec; /* Number of pages written to the journal */
+ u32 cksumInit; /* Quasi-random value added to every checksum */
+ int stmtNRec; /* Number of records in stmt subjournal */
+ int nExtra; /* Add this many bytes to each in-memory page */
+ void (*xDestructor)(void*,int); /* Call this routine when freeing pages */
+ void (*xReiniter)(void*,int); /* Call this routine when reloading pages */
+ int pageSize; /* Number of bytes in a page */
+ int nPage; /* Total number of in-memory pages */
+ int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
+ int mxPage; /* Maximum number of pages to hold in cache */
+ int nHit, nMiss, nOvfl; /* Cache hits, missing, and LRU overflows */
+ void (*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
+ void *pCodecArg; /* First argument to xCodec() */
+ u8 journalOpen; /* True if journal file descriptors is valid */
+ u8 journalStarted; /* True if header of journal is synced */
+ u8 useJournal; /* Use a rollback journal on this file */
+ u8 stmtOpen; /* True if the statement subjournal is open */
+ u8 stmtInUse; /* True we are in a statement subtransaction */
+ u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/
+ u8 noSync; /* Do not sync the journal if true */
+ u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
+ u8 errMask; /* One of several kinds of errors */
+ u8 tempFile; /* zFilename is a temporary file */
+ u8 readOnly; /* True for a read-only database */
+ u8 needSync; /* True if an fsync() is needed on the journal */
+ u8 dirtyCache; /* True if cached pages have changed */
+ u8 alwaysRollback; /* Disable dont_rollback() for all pages */
+ u8 memDb; /* True to inhibit all file I/O */
+ u8 *aInJournal; /* One bit for each page in the database file */
+ u8 *aInStmt; /* One bit for each page in the database */
+ u8 setMaster; /* True if a m-j name has been written to jrnl */
+ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */
+ PgHdr *pFirst, *pLast; /* List of free pages */
+ PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */
+ PgHdr *pAll; /* List of all pages */
+ PgHdr *pStmt; /* List of pages in the statement subjournal */
+ i64 journalOff; /* Current byte offset in the journal file */
+ i64 journalHdr; /* Byte offset to previous journal header */
+ i64 stmtHdrOff; /* First journal header written this statement */
+ i64 stmtCksum; /* cksumInit when statement was started */
+ int sectorSize; /* Assumed sector size during rollback */
+ PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number to PgHdr */
+};
+
+/*
+** These are bits that can be set in Pager.errMask.
+*/
+#define PAGER_ERR_FULL 0x01 /* a write() failed */
+#define PAGER_ERR_MEM 0x02 /* malloc() failed */
+#define PAGER_ERR_LOCK 0x04 /* error in the locking protocol */
+#define PAGER_ERR_CORRUPT 0x08 /* database or journal corruption */
+#define PAGER_ERR_DISK 0x10 /* general disk I/O error - bad hard drive? */
+
+/*
+** Journal files begin with the following magic string. The data
+** was obtained from /dev/random. It is used only as a sanity check.
+**
+** Since version 2.8.0, the journal format contains additional sanity
+** checking information. If the power fails while the journal is begin
+** written, semi-random garbage data might appear in the journal
+** file after power is restored. If an attempt is then made
+** to roll the journal back, the database could be corrupted. The additional
+** sanity checking data is an attempt to discover the garbage in the
+** journal and ignore it.
+**
+** The sanity checking information for the new journal format consists
+** of a 32-bit checksum on each page of data. The checksum covers both
+** the page number and the pPager->pageSize bytes of data for the page.
+** This cksum is initialized to a 32-bit random value that appears in the
+** journal file right after the header. The random initializer is important,
+** because garbage data that appears at the end of a journal is likely
+** data that was once in other files that have now been deleted. If the
+** garbage data came from an obsolete journal file, the checksums might
+** be correct. But by initializing the checksum to random value which
+** is different for every journal, we minimize that risk.
+*/
+static const unsigned char aJournalMagic[] = {
+ 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7,
+};
+
+/*
+** The size of the header and of each page in the journal is determined
+** by the following macros.
+*/
+#define JOURNAL_PG_SZ(pPager) ((pPager->pageSize) + 8)
+
+/*
+** The journal header size for this pager. In the future, this could be
+** set to some value read from the disk controller. The important
+** characteristic is that it is the same size as a disk sector.
+*/
+#define JOURNAL_HDR_SZ(pPager) (pPager->sectorSize)
+
+#define PAGER_SECTOR_SIZE 512
+
+/*
+** Page number PAGER_MJ_PGNO is never used in an SQLite database (it is
+** reserved for working around a windows/posix incompatibility). It is
+** used in the journal to signify that the remainder of the journal file
+** is devoted to storing a master journal name - there are no more pages to
+** roll back. See comments for function writeMasterJournal() for details.
+*/
+#define PAGER_MJ_PGNO(x) (PENDING_BYTE/((x)->pageSize))
+
+/*
+** Enable reference count tracking (for debugging) here:
+*/
+#ifdef SQLITE_TEST
+ int pager3_refinfo_enable = 0;
+ static void pager_refinfo(PgHdr *p){
+ static int cnt = 0;
+ if( !pager3_refinfo_enable ) return;
+ sqlite3DebugPrintf(
+ "REFCNT: %4d addr=%p nRef=%d\n",
+ p->pgno, PGHDR_TO_DATA(p), p->nRef
+ );
+ cnt++; /* Something to set a breakpoint on */
+ }
+# define REFINFO(X) pager_refinfo(X)
+#else
+# define REFINFO(X)
+#endif
+
+/*
+** Read a 32-bit integer from the given file descriptor. Store the integer
+** that is read in *pRes. Return SQLITE_OK if everything worked, or an
+** error code is something goes wrong.
+**
+** All values are stored on disk as big-endian.
+*/
+static int read32bits(OsFile *fd, u32 *pRes){
+ u32 res;
+ int rc;
+ rc = sqlite3OsRead(fd, &res, sizeof(res));
+ if( rc==SQLITE_OK ){
+ unsigned char ac[4];
+ memcpy(ac, &res, 4);
+ res = (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3];
+ }
+ *pRes = res;
+ return rc;
+}
+
+/*
+** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK
+** on success or an error code is something goes wrong.
+*/
+static int write32bits(OsFile *fd, u32 val){
+ unsigned char ac[4];
+ ac[0] = (val>>24) & 0xff;
+ ac[1] = (val>>16) & 0xff;
+ ac[2] = (val>>8) & 0xff;
+ ac[3] = val & 0xff;
+ return sqlite3OsWrite(fd, ac, 4);
+}
+
+/*
+** Write the 32-bit integer 'val' into the page identified by page header
+** 'p' at offset 'offset'.
+*/
+static void store32bits(u32 val, PgHdr *p, int offset){
+ unsigned char *ac;
+ ac = &((unsigned char*)PGHDR_TO_DATA(p))[offset];
+ ac[0] = (val>>24) & 0xff;
+ ac[1] = (val>>16) & 0xff;
+ ac[2] = (val>>8) & 0xff;
+ ac[3] = val & 0xff;
+}
+
+/*
+** Read a 32-bit integer at offset 'offset' from the page identified by
+** page header 'p'.
+*/
+static u32 retrieve32bits(PgHdr *p, int offset){
+ unsigned char *ac;
+ ac = &((unsigned char*)PGHDR_TO_DATA(p))[offset];
+ return (ac[0]<<24) | (ac[1]<<16) | (ac[2]<<8) | ac[3];
+}
+
+
+/*
+** Convert the bits in the pPager->errMask into an approprate
+** return code.
+*/
+static int pager_errcode(Pager *pPager){
+ int rc = SQLITE_OK;
+ if( pPager->errMask & PAGER_ERR_LOCK ) rc = SQLITE_PROTOCOL;
+ if( pPager->errMask & PAGER_ERR_DISK ) rc = SQLITE_IOERR;
+ if( pPager->errMask & PAGER_ERR_FULL ) rc = SQLITE_FULL;
+ if( pPager->errMask & PAGER_ERR_MEM ) rc = SQLITE_NOMEM;
+ if( pPager->errMask & PAGER_ERR_CORRUPT ) rc = SQLITE_CORRUPT;
+ return rc;
+}
+
+/*
+** When this is called the journal file for pager pPager must be open.
+** The master journal file name is read from the end of the file and
+** written into memory obtained from sqliteMalloc(). *pzMaster is
+** set to point at the memory and SQLITE_OK returned. The caller must
+** sqliteFree() *pzMaster.
+**
+** If no master journal file name is present *pzMaster is set to 0 and
+** SQLITE_OK returned.
+*/
+static int readMasterJournal(OsFile *pJrnl, char **pzMaster){
+ int rc;
+ u32 len;
+ i64 szJ;
+ u32 cksum;
+ int i;
+ unsigned char aMagic[8]; /* A buffer to hold the magic header */
+
+ *pzMaster = 0;
+
+ rc = sqlite3OsFileSize(pJrnl, &szJ);
+ if( rc!=SQLITE_OK || szJ<16 ) return rc;
+
+ rc = sqlite3OsSeek(pJrnl, szJ-16);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = read32bits(pJrnl, &len);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = read32bits(pJrnl, &cksum);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3OsRead(pJrnl, aMagic, 8);
+ if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc;
+
+ rc = sqlite3OsSeek(pJrnl, szJ-16-len);
+ if( rc!=SQLITE_OK ) return rc;
+
+ *pzMaster = (char *)sqliteMalloc(len+1);
+ if( !*pzMaster ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3OsRead(pJrnl, *pzMaster, len);
+ if( rc!=SQLITE_OK ){
+ sqliteFree(*pzMaster);
+ *pzMaster = 0;
+ return rc;
+ }
+
+ /* See if the checksum matches the master journal name */
+ for(i=0; i<len; i++){
+ cksum -= (*pzMaster)[i];
+ }
+ if( cksum ){
+ /* If the checksum doesn't add up, then one or more of the disk sectors
+ ** containing the master journal filename is corrupted. This means
+ ** definitely roll back, so just return SQLITE_OK and report a (nul)
+ ** master-journal filename.
+ */
+ sqliteFree(*pzMaster);
+ *pzMaster = 0;
+ }
+ (*pzMaster)[len] = '\0';
+
+ return SQLITE_OK;
+}
+
+/*
+** Seek the journal file descriptor to the next sector boundary where a
+** journal header may be read or written. Pager.journalOff is updated with
+** the new seek offset.
+**
+** i.e for a sector size of 512:
+**
+** Input Offset Output Offset
+** ---------------------------------------
+** 0 0
+** 512 512
+** 100 512
+** 2000 2048
+**
+*/
+static int seekJournalHdr(Pager *pPager){
+ i64 offset = 0;
+ i64 c = pPager->journalOff;
+ if( c ){
+ offset = ((c-1)/JOURNAL_HDR_SZ(pPager) + 1) * JOURNAL_HDR_SZ(pPager);
+ }
+ assert( offset%JOURNAL_HDR_SZ(pPager)==0 );
+ assert( offset>=c );
+ assert( (offset-c)<JOURNAL_HDR_SZ(pPager) );
+ pPager->journalOff = offset;
+ return sqlite3OsSeek(&pPager->jfd, pPager->journalOff);
+}
+
+/*
+** The journal file must be open when this routine is called. A journal
+** header (JOURNAL_HDR_SZ bytes) is written into the journal file at the
+** current location.
+**
+** The format for the journal header is as follows:
+** - 8 bytes: Magic identifying journal format.
+** - 4 bytes: Number of records in journal, or -1 no-sync mode is on.
+** - 4 bytes: Random number used for page hash.
+** - 4 bytes: Initial database page count.
+** - 4 bytes: Sector size used by the process that wrote this journal.
+**
+** Followed by (JOURNAL_HDR_SZ - 24) bytes of unused space.
+*/
+static int writeJournalHdr(Pager *pPager){
+
+ int rc = seekJournalHdr(pPager);
+ if( rc ) return rc;
+
+ pPager->journalHdr = pPager->journalOff;
+ if( pPager->stmtHdrOff==0 ){
+ pPager->stmtHdrOff = pPager->journalHdr;
+ }
+ pPager->journalOff += JOURNAL_HDR_SZ(pPager);
+
+ /* FIX ME:
+ **
+ ** Possibly for a pager not in no-sync mode, the journal magic should not
+ ** be written until nRec is filled in as part of next syncJournal().
+ **
+ ** Actually maybe the whole journal header should be delayed until that
+ ** point. Think about this.
+ */
+ rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
+
+ if( rc==SQLITE_OK ){
+ /* The nRec Field. 0xFFFFFFFF for no-sync journals. */
+ rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
+ }
+ if( rc==SQLITE_OK ){
+ /* The random check-hash initialiser */
+ sqlite3Randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ rc = write32bits(&pPager->jfd, pPager->cksumInit);
+ }
+ if( rc==SQLITE_OK ){
+ /* The initial database size */
+ rc = write32bits(&pPager->jfd, pPager->dbSize);
+ }
+ if( rc==SQLITE_OK ){
+ /* The assumed sector size for this process */
+ rc = write32bits(&pPager->jfd, pPager->sectorSize);
+ }
+
+ /* The journal header has been written successfully. Seek the journal
+ ** file descriptor to the end of the journal header sector.
+ */
+ if( rc==SQLITE_OK ){
+ sqlite3OsSeek(&pPager->jfd, pPager->journalOff-1);
+ rc = sqlite3OsWrite(&pPager->jfd, "\000", 1);
+ }
+ return rc;
+}
+
+/*
+** The journal file must be open when this is called. A journal header file
+** (JOURNAL_HDR_SZ bytes) is read from the current location in the journal
+** file. See comments above function writeJournalHdr() for a description of
+** the journal header format.
+**
+** If the header is read successfully, *nRec is set to the number of
+** page records following this header and *dbSize is set to the size of the
+** database before the transaction began, in pages. Also, pPager->cksumInit
+** is set to the value read from the journal header. SQLITE_OK is returned
+** in this case.
+**
+** If the journal header file appears to be corrupted, SQLITE_DONE is
+** returned and *nRec and *dbSize are not set. If JOURNAL_HDR_SZ bytes
+** cannot be read from the journal file an error code is returned.
+*/
+static int readJournalHdr(
+ Pager *pPager,
+ i64 journalSize,
+ u32 *pNRec,
+ u32 *pDbSize
+){
+ int rc;
+ unsigned char aMagic[8]; /* A buffer to hold the magic header */
+
+ rc = seekJournalHdr(pPager);
+ if( rc ) return rc;
+
+ if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){
+ return SQLITE_DONE;
+ }
+
+ rc = sqlite3OsRead(&pPager->jfd, aMagic, sizeof(aMagic));
+ if( rc ) return rc;
+
+ if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){
+ return SQLITE_DONE;
+ }
+
+ rc = read32bits(&pPager->jfd, pNRec);
+ if( rc ) return rc;
+
+ rc = read32bits(&pPager->jfd, &pPager->cksumInit);
+ if( rc ) return rc;
+
+ rc = read32bits(&pPager->jfd, pDbSize);
+ if( rc ) return rc;
+
+ /* Update the assumed sector-size to match the value used by
+ ** the process that created this journal. If this journal was
+ ** created by a process other than this one, then this routine
+ ** is being called from within pager_playback(). The local value
+ ** of Pager.sectorSize is restored at the end of that routine.
+ */
+ rc = read32bits(&pPager->jfd, (u32 *)&pPager->sectorSize);
+ if( rc ) return rc;
+
+ pPager->journalOff += JOURNAL_HDR_SZ(pPager);
+ rc = sqlite3OsSeek(&pPager->jfd, pPager->journalOff);
+ return rc;
+}
+
+
+/*
+** Write the supplied master journal name into the journal file for pager
+** pPager at the current location. The master journal name must be the last
+** thing written to a journal file. If the pager is in full-sync mode, the
+** journal file descriptor is advanced to the next sector boundary before
+** anything is written. The format is:
+**
+** + 4 bytes: PAGER_MJ_PGNO.
+** + N bytes: length of master journal name.
+** + 4 bytes: N
+** + 4 bytes: Master journal name checksum.
+** + 8 bytes: aJournalMagic[].
+**
+** The master journal page checksum is the sum of the bytes in the master
+** journal name.
+*/
+static int writeMasterJournal(Pager *pPager, const char *zMaster){
+ int rc;
+ int len;
+ int i;
+ u32 cksum = 0;
+
+ if( !zMaster || pPager->setMaster) return SQLITE_OK;
+ pPager->setMaster = 1;
+
+ len = strlen(zMaster);
+ for(i=0; i<len; i++){
+ cksum += zMaster[i];
+ }
+
+ /* If in full-sync mode, advance to the next disk sector before writing
+ ** the master journal name. This is in case the previous page written to
+ ** the journal has already been synced.
+ */
+ if( pPager->fullSync ){
+ rc = seekJournalHdr(pPager);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ pPager->journalOff += (len+20);
+
+ rc = write32bits(&pPager->jfd, PAGER_MJ_PGNO(pPager));
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3OsWrite(&pPager->jfd, zMaster, len);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = write32bits(&pPager->jfd, len);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = write32bits(&pPager->jfd, cksum);
+ if( rc!=SQLITE_OK ) return rc;
+
+ rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
+ pPager->needSync = 1;
+ return rc;
+}
+
+/*
+** Add or remove a page from the list of all pages that are in the
+** statement journal.
+**
+** The Pager keeps a separate list of pages that are currently in
+** the statement journal. This helps the sqlite3pager_stmt_commit()
+** routine run MUCH faster for the common case where there are many
+** pages in memory but only a few are in the statement journal.
+*/
+static void page_add_to_stmt_list(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ if( pPg->inStmt ) return;
+ assert( pPg->pPrevStmt==0 && pPg->pNextStmt==0 );
+ pPg->pPrevStmt = 0;
+ if( pPager->pStmt ){
+ pPager->pStmt->pPrevStmt = pPg;
+ }
+ pPg->pNextStmt = pPager->pStmt;
+ pPager->pStmt = pPg;
+ pPg->inStmt = 1;
+}
+static void page_remove_from_stmt_list(PgHdr *pPg){
+ if( !pPg->inStmt ) return;
+ if( pPg->pPrevStmt ){
+ assert( pPg->pPrevStmt->pNextStmt==pPg );
+ pPg->pPrevStmt->pNextStmt = pPg->pNextStmt;
+ }else{
+ assert( pPg->pPager->pStmt==pPg );
+ pPg->pPager->pStmt = pPg->pNextStmt;
+ }
+ if( pPg->pNextStmt ){
+ assert( pPg->pNextStmt->pPrevStmt==pPg );
+ pPg->pNextStmt->pPrevStmt = pPg->pPrevStmt;
+ }
+ pPg->pNextStmt = 0;
+ pPg->pPrevStmt = 0;
+ pPg->inStmt = 0;
+}
+
+/*
+** Find a page in the hash table given its page number. Return
+** a pointer to the page or NULL if not found.
+*/
+static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
+ PgHdr *p = pPager->aHash[pager_hash(pgno)];
+ while( p && p->pgno!=pgno ){
+ p = p->pNextHash;
+ }
+ return p;
+}
+
+/*
+** Unlock the database and clear the in-memory cache. This routine
+** sets the state of the pager back to what it was when it was first
+** opened. Any outstanding pages are invalidated and subsequent attempts
+** to access those pages will likely result in a coredump.
+*/
+static void pager_reset(Pager *pPager){
+ PgHdr *pPg, *pNext;
+ for(pPg=pPager->pAll; pPg; pPg=pNext){
+ pNext = pPg->pNextAll;
+ sqliteFree(pPg);
+ }
+ pPager->pFirst = 0;
+ pPager->pFirstSynced = 0;
+ pPager->pLast = 0;
+ pPager->pAll = 0;
+ memset(pPager->aHash, 0, sizeof(pPager->aHash));
+ pPager->nPage = 0;
+ if( pPager->state>=PAGER_RESERVED ){
+ sqlite3pager_rollback(pPager);
+ }
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ pPager->state = PAGER_UNLOCK;
+ pPager->dbSize = -1;
+ pPager->nRef = 0;
+ assert( pPager->journalOpen==0 );
+}
+
+/*
+** When this routine is called, the pager has the journal file open and
+** a RESERVED or EXCLUSIVE lock on the database. This routine releases
+** the database lock and acquires a SHARED lock in its place. The journal
+** file is deleted and closed.
+**
+** TODO: Consider keeping the journal file open for temporary databases.
+** This might give a performance improvement on windows where opening
+** a file is an expensive operation.
+*/
+static int pager_unwritelock(Pager *pPager){
+ PgHdr *pPg;
+ int rc;
+ assert( !pPager->memDb );
+ if( pPager->state<PAGER_RESERVED ){
+ return SQLITE_OK;
+ }
+ sqlite3pager_stmt_commit(pPager);
+ if( pPager->stmtOpen ){
+ sqlite3OsClose(&pPager->stfd);
+ pPager->stmtOpen = 0;
+ }
+ if( pPager->journalOpen ){
+ sqlite3OsClose(&pPager->jfd);
+ pPager->journalOpen = 0;
+ sqlite3OsDelete(pPager->zJournal);
+ sqliteFree( pPager->aInJournal );
+ pPager->aInJournal = 0;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ pPg->inJournal = 0;
+ pPg->dirty = 0;
+ pPg->needSync = 0;
+ }
+ pPager->dirtyCache = 0;
+ pPager->nRec = 0;
+ }else{
+ assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
+ }
+ rc = sqlite3OsUnlock(&pPager->fd, SHARED_LOCK);
+ pPager->state = PAGER_SHARED;
+ pPager->origDbSize = 0;
+ pPager->setMaster = 0;
+ return rc;
+}
+
+/*
+** Compute and return a checksum for the page of data.
+**
+** This is not a real checksum. It is really just the sum of the
+** random initial value and the page number. We experimented with
+** a checksum of the entire data, but that was found to be too slow.
+**
+** Note that the page number is stored at the beginning of data and
+** the checksum is stored at the end. This is important. If journal
+** corruption occurs due to a power failure, the most likely scenario
+** is that one end or the other of the record will be changed. It is
+** much less likely that the two ends of the journal record will be
+** correct and the middle be corrupt. Thus, this "checksum" scheme,
+** though fast and simple, catches the mostly likely kind of corruption.
+**
+** FIX ME: Consider adding every 200th (or so) byte of the data to the
+** checksum. That way if a single page spans 3 or more disk sectors and
+** only the middle sector is corrupt, we will still have a reasonable
+** chance of failing the checksum and thus detecting the problem.
+*/
+static u32 pager_cksum(Pager *pPager, Pgno pgno, const char *aData){
+ u32 cksum = pPager->cksumInit;
+ int i = pPager->pageSize-200;
+ while( i>0 ){
+ cksum += aData[i];
+ i -= 200;
+ }
+ return cksum;
+}
+
+/*
+** Read a single page from the journal file opened on file descriptor
+** jfd. Playback this one page.
+**
+** If useCksum==0 it means this journal does not use checksums. Checksums
+** are not used in statement journals because statement journals do not
+** need to survive power failures.
+*/
+static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){
+ int rc;
+ PgHdr *pPg; /* An existing page in the cache */
+ Pgno pgno; /* The page number of a page in journal */
+ u32 cksum; /* Checksum used for sanity checking */
+ u8 aData[SQLITE_MAX_PAGE_SIZE]; /* Temp storage for a page */
+
+ rc = read32bits(jfd, &pgno);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3OsRead(jfd, &aData, pPager->pageSize);
+ if( rc!=SQLITE_OK ) return rc;
+ pPager->journalOff += pPager->pageSize + 4;
+
+ /* Sanity checking on the page. This is more important that I originally
+ ** thought. If a power failure occurs while the journal is being written,
+ ** it could cause invalid data to be written into the journal. We need to
+ ** detect this invalid data (with high probability) and ignore it.
+ */
+ if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){
+ return SQLITE_DONE;
+ }
+ if( pgno>(unsigned)pPager->dbSize ){
+ return SQLITE_OK;
+ }
+ if( useCksum ){
+ rc = read32bits(jfd, &cksum);
+ if( rc ) return rc;
+ pPager->journalOff += 4;
+ if( pager_cksum(pPager, pgno, aData)!=cksum ){
+ return SQLITE_DONE;
+ }
+ }
+
+ assert( pPager->state==PAGER_RESERVED || pPager->state>=PAGER_EXCLUSIVE );
+
+ /* If the pager is in RESERVED state, then there must be a copy of this
+ ** page in the pager cache. In this case just update the pager cache,
+ ** not the database file. The page is left marked dirty in this case.
+ **
+ ** If in EXCLUSIVE state, then we update the pager cache if it exists
+ ** and the main file. The page is then marked not dirty.
+ */
+ pPg = pager_lookup(pPager, pgno);
+ assert( pPager->state>=PAGER_EXCLUSIVE || pPg );
+ TRACE3("PLAYBACK %d page %d\n", pPager->fd.h, pgno);
+ if( pPager->state>=PAGER_EXCLUSIVE ){
+ sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize);
+ rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize);
+ }
+ if( pPg ){
+ /* No page should ever be rolled back that is in use, except for page
+ ** 1 which is held in use in order to keep the lock on the database
+ ** active.
+ */
+ void *pData;
+ assert( pPg->nRef==0 || pPg->pgno==1 );
+ pData = PGHDR_TO_DATA(pPg);
+ memcpy(pData, aData, pPager->pageSize);
+ if( pPager->xDestructor ){ /*** FIX ME: Should this be xReinit? ***/
+ pPager->xDestructor(pData, pPager->pageSize);
+ }
+ if( pPager->state>=PAGER_EXCLUSIVE ){
+ pPg->dirty = 0;
+ pPg->needSync = 0;
+ }
+ CODEC(pPager, pData, pPg->pgno, 3);
+ }
+ return rc;
+}
+
+/*
+** Parameter zMaster is the name of a master journal file. A single journal
+** file that referred to the master journal file has just been rolled back.
+** This routine checks if it is possible to delete the master journal file,
+** and does so if it is.
+**
+** The master journal file contains the names of all child journals.
+** To tell if a master journal can be deleted, check to each of the
+** children. If all children are either missing or do not refer to
+** a different master journal, then this master journal can be deleted.
+*/
+static int pager_delmaster(const char *zMaster){
+ int rc;
+ int master_open = 0;
+ OsFile master;
+ char *zMasterJournal = 0; /* Contents of master journal file */
+ i64 nMasterJournal; /* Size of master journal file */
+
+ /* Open the master journal file exclusively in case some other process
+ ** is running this routine also. Not that it makes too much difference.
+ */
+ memset(&master, 0, sizeof(master));
+ rc = sqlite3OsOpenReadOnly(zMaster, &master);
+ if( rc!=SQLITE_OK ) goto delmaster_out;
+ master_open = 1;
+ rc = sqlite3OsFileSize(&master, &nMasterJournal);
+ if( rc!=SQLITE_OK ) goto delmaster_out;
+
+ if( nMasterJournal>0 ){
+ char *zJournal;
+ char *zMasterPtr = 0;
+
+ /* Load the entire master journal file into space obtained from
+ ** sqliteMalloc() and pointed to by zMasterJournal.
+ */
+ zMasterJournal = (char *)sqliteMalloc(nMasterJournal);
+ if( !zMasterJournal ){
+ rc = SQLITE_NOMEM;
+ goto delmaster_out;
+ }
+ rc = sqlite3OsRead(&master, zMasterJournal, nMasterJournal);
+ if( rc!=SQLITE_OK ) goto delmaster_out;
+
+ zJournal = zMasterJournal;
+ while( (zJournal-zMasterJournal)<nMasterJournal ){
+ if( sqlite3OsFileExists(zJournal) ){
+ /* One of the journals pointed to by the master journal exists.
+ ** Open it and check if it points at the master journal. If
+ ** so, return without deleting the master journal file.
+ */
+ OsFile journal;
+
+ memset(&journal, 0, sizeof(journal));
+ rc = sqlite3OsOpenReadOnly(zJournal, &journal);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
+ }
+
+ rc = readMasterJournal(&journal, &zMasterPtr);
+ sqlite3OsClose(&journal);
+ if( rc!=SQLITE_OK ){
+ goto delmaster_out;
+ }
+
+ if( zMasterPtr && !strcmp(zMasterPtr, zMaster) ){
+ /* We have a match. Do not delete the master journal file. */
+ goto delmaster_out;
+ }
+ }
+ zJournal += (strlen(zJournal)+1);
+ }
+ }
+
+ sqlite3OsDelete(zMaster);
+
+delmaster_out:
+ if( zMasterJournal ){
+ sqliteFree(zMasterJournal);
+ }
+ if( master_open ){
+ sqlite3OsClose(&master);
+ }
+ return rc;
+}
+
+/*
+** Make every page in the cache agree with what is on disk. In other words,
+** reread the disk to reset the state of the cache.
+**
+** This routine is called after a rollback in which some of the dirty cache
+** pages had never been written out to disk. We need to roll back the
+** cache content and the easiest way to do that is to reread the old content
+** back from the disk.
+*/
+static int pager_reload_cache(Pager *pPager){
+ PgHdr *pPg;
+ int rc = SQLITE_OK;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ char zBuf[SQLITE_MAX_PAGE_SIZE];
+ if( !pPg->dirty ) continue;
+ if( (int)pPg->pgno <= pPager->origDbSize ){
+ sqlite3OsSeek(&pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1));
+ rc = sqlite3OsRead(&pPager->fd, zBuf, pPager->pageSize);
+ TRACE3("REFETCH %d page %d\n", pPager->fd.h, pPg->pgno);
+ if( rc ) break;
+ CODEC(pPager, zBuf, pPg->pgno, 2);
+ }else{
+ memset(zBuf, 0, pPager->pageSize);
+ }
+ if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), pPager->pageSize) ){
+ memcpy(PGHDR_TO_DATA(pPg), zBuf, pPager->pageSize);
+ if( pPager->xReiniter ){
+ pPager->xReiniter(PGHDR_TO_DATA(pPg), pPager->pageSize);
+ }else{
+ memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
+ }
+ }
+ pPg->needSync = 0;
+ pPg->dirty = 0;
+ }
+ return rc;
+}
+
+/*
+** Truncate the main file of the given pager to the number of pages
+** indicated.
+*/
+static int pager_truncate(Pager *pPager, int nPage){
+ return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(i64)nPage);
+}
+
+/*
+** Playback the journal and thus restore the database file to
+** the state it was in before we started making changes.
+**
+** The journal file format is as follows:
+**
+** (1) 8 byte prefix. A copy of aJournalMagic[].
+** (2) 4 byte big-endian integer which is the number of valid page records
+** in the journal. If this value is 0xffffffff, then compute the
+** number of page records from the journal size.
+** (3) 4 byte big-endian integer which is the initial value for the
+** sanity checksum.
+** (4) 4 byte integer which is the number of pages to truncate the
+** database to during a rollback.
+** (5) 4 byte integer which is the number of bytes in the master journal
+** name. The value may be zero (indicate that there is no master
+** journal.)
+** (6) N bytes of the master journal name. The name will be nul-terminated
+** and might be shorter than the value read from (5). If the first byte
+** of the name is \000 then there is no master journal. The master
+** journal name is stored in UTF-8.
+** (7) Zero or more pages instances, each as follows:
+** + 4 byte page number.
+** + pPager->pageSize bytes of data.
+** + 4 byte checksum
+**
+** When we speak of the journal header, we mean the first 6 items above.
+** Each entry in the journal is an instance of the 7th item.
+**
+** Call the value from the second bullet "nRec". nRec is the number of
+** valid page entries in the journal. In most cases, you can compute the
+** value of nRec from the size of the journal file. But if a power
+** failure occurred while the journal was being written, it could be the
+** case that the size of the journal file had already been increased but
+** the extra entries had not yet made it safely to disk. In such a case,
+** the value of nRec computed from the file size would be too large. For
+** that reason, we always use the nRec value in the header.
+**
+** If the nRec value is 0xffffffff it means that nRec should be computed
+** from the file size. This value is used when the user selects the
+** no-sync option for the journal. A power failure could lead to corruption
+** in this case. But for things like temporary table (which will be
+** deleted when the power is restored) we don't care.
+**
+** If the file opened as the journal file is not a well-formed
+** journal file then all pages up to the first corrupted page are rolled
+** back (or no pages if the journal header is corrupted). The journal file
+** is then deleted and SQLITE_OK returned, just as if no corruption had
+** been encountered.
+**
+** If an I/O or malloc() error occurs, the journal-file is not deleted
+** and an error code is returned.
+*/
+static int pager_playback(Pager *pPager){
+ i64 szJ; /* Size of the journal file in bytes */
+ u32 nRec; /* Number of Records in the journal */
+ int i; /* Loop counter */
+ Pgno mxPg = 0; /* Size of the original file in pages */
+ int rc; /* Result code of a subroutine */
+ char *zMaster = 0; /* Name of master journal file if any */
+
+ /* Figure out how many records are in the journal. Abort early if
+ ** the journal is empty.
+ */
+ assert( pPager->journalOpen );
+ rc = sqlite3OsFileSize(&pPager->jfd, &szJ);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+
+ /* Read the master journal name from the journal, if it is present.
+ ** If a master journal file name is specified, but the file is not
+ ** present on disk, then the journal is not hot and does not need to be
+ ** played back.
+ */
+ rc = readMasterJournal(&pPager->jfd, &zMaster);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK || (zMaster && !sqlite3OsFileExists(zMaster)) ){
+ sqliteFree(zMaster);
+ zMaster = 0;
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ goto end_playback;
+ }
+ sqlite3OsSeek(&pPager->jfd, 0);
+ pPager->journalOff = 0;
+
+ /* This loop terminates either when the readJournalHdr() call returns
+ ** SQLITE_DONE or an IO error occurs. */
+ while( 1 ){
+
+ /* Read the next journal header from the journal file. If there are
+ ** not enough bytes left in the journal file for a complete header, or
+ ** it is corrupted, then a process must of failed while writing it.
+ ** This indicates nothing more needs to be rolled back.
+ */
+ rc = readJournalHdr(pPager, szJ, &nRec, &mxPg);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ }
+ goto end_playback;
+ }
+
+ /* If nRec is 0xffffffff, then this journal was created by a process
+ ** working in no-sync mode. This means that the rest of the journal
+ ** file consists of pages, there are no more journal headers. Compute
+ ** the value of nRec based on this assumption.
+ */
+ if( nRec==0xffffffff ){
+ assert( pPager->journalOff==JOURNAL_HDR_SZ(pPager) );
+ nRec = (szJ - JOURNAL_HDR_SZ(pPager))/JOURNAL_PG_SZ(pPager);
+ }
+
+ /* If this is the first header read from the journal, truncate the
+ ** database file back to it's original size.
+ */
+ if( pPager->journalOff==JOURNAL_HDR_SZ(pPager) ){
+ assert( pPager->origDbSize==0 || pPager->origDbSize==mxPg );
+ rc = pager_truncate(pPager, mxPg);
+ if( rc!=SQLITE_OK ){
+ goto end_playback;
+ }
+ pPager->dbSize = mxPg;
+ }
+
+ /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */
+ if( rc!=SQLITE_OK ) goto end_playback;
+
+ /* Copy original pages out of the journal and back into the database file.
+ */
+ for(i=0; i<nRec; i++){
+ rc = pager_playback_one_page(pPager, &pPager->jfd, 1);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ pPager->journalOff = szJ;
+ break;
+ }else{
+ goto end_playback;
+ }
+ }
+ }
+ }
+
+ /* Pages that have been written to the journal but never synced
+ ** where not restored by the loop above. We have to restore those
+ ** pages by reading them back from the original database.
+ */
+ assert( rc==SQLITE_OK );
+ pager_reload_cache(pPager);
+
+end_playback:
+ if( rc==SQLITE_OK ){
+ rc = pager_unwritelock(pPager);
+ }
+ if( zMaster ){
+ /* If there was a master journal and this routine will return true,
+ ** see if it is possible to delete the master journal. If errors
+ ** occur during this process, ignore them.
+ */
+ if( rc==SQLITE_OK ){
+ pager_delmaster(zMaster);
+ }
+ sqliteFree(zMaster);
+ }
+
+ /* The Pager.sectorSize variable may have been updated while rolling
+ ** back a journal created by a process with a different PAGER_SECTOR_SIZE
+ ** value. Reset it to the correct value for this process.
+ */
+ pPager->sectorSize = PAGER_SECTOR_SIZE;
+ return rc;
+}
+
+/*
+** Playback the statement journal.
+**
+** This is similar to playing back the transaction journal but with
+** a few extra twists.
+**
+** (1) The number of pages in the database file at the start of
+** the statement is stored in pPager->stmtSize, not in the
+** journal file itself.
+**
+** (2) In addition to playing back the statement journal, also
+** playback all pages of the transaction journal beginning
+** at offset pPager->stmtJSize.
+*/
+static int pager_stmt_playback(Pager *pPager){
+ i64 szJ; /* Size of the full journal */
+ i64 hdrOff;
+ int nRec; /* Number of Records */
+ int i; /* Loop counter */
+ int rc;
+
+ szJ = pPager->journalOff;
+#ifndef NDEBUG
+ {
+ i64 os_szJ;
+ rc = sqlite3OsFileSize(&pPager->jfd, &os_szJ);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( szJ==os_szJ );
+ }
+#endif
+
+ /* Set hdrOff to be the offset to the first journal header written
+ ** this statement transaction, or the end of the file if no journal
+ ** header was written.
+ */
+ hdrOff = pPager->stmtHdrOff;
+ assert( pPager->fullSync || !hdrOff );
+ if( !hdrOff ){
+ hdrOff = szJ;
+ }
+
+
+ /* Truncate the database back to its original size.
+ */
+ rc = pager_truncate(pPager, pPager->stmtSize);
+ pPager->dbSize = pPager->stmtSize;
+
+ /* Figure out how many records are in the statement journal.
+ */
+ assert( pPager->stmtInUse && pPager->journalOpen );
+ sqlite3OsSeek(&pPager->stfd, 0);
+ nRec = pPager->stmtNRec;
+
+ /* Copy original pages out of the statement journal and back into the
+ ** database file. Note that the statement journal omits checksums from
+ ** each record since power-failure recovery is not important to statement
+ ** journals.
+ */
+ for(i=nRec-1; i>=0; i--){
+ rc = pager_playback_one_page(pPager, &pPager->stfd, 0);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
+ }
+
+ /* Now roll some pages back from the transaction journal. Pager.stmtJSize
+ ** was the size of the journal file when this statement was started, so
+ ** everything after that needs to be rolled back, either into the
+ ** database, the memory cache, or both.
+ **
+ ** If it is not zero, then Pager.stmtHdrOff is the offset to the start
+ ** of the first journal header written during this statement transaction.
+ */
+ rc = sqlite3OsSeek(&pPager->jfd, pPager->stmtJSize);
+ if( rc!=SQLITE_OK ){
+ goto end_stmt_playback;
+ }
+ pPager->journalOff = pPager->stmtJSize;
+ pPager->cksumInit = pPager->stmtCksum;
+ assert( JOURNAL_HDR_SZ(pPager)<(pPager->pageSize+8) );
+ while( pPager->journalOff <= (hdrOff-(pPager->pageSize+8)) ){
+ rc = pager_playback_one_page(pPager, &pPager->jfd, 1);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
+ }
+
+ while( pPager->journalOff < szJ ){
+ u32 nRec;
+ u32 dummy;
+ rc = readJournalHdr(pPager, szJ, &nRec, &dummy);
+ if( rc!=SQLITE_OK ){
+ assert( rc!=SQLITE_DONE );
+ goto end_stmt_playback;
+ }
+ if( nRec==0 ){
+ nRec = (szJ - pPager->journalOff) / (pPager->pageSize+8);
+ }
+ for(i=nRec-1; i>=0 && pPager->journalOff < szJ; i--){
+ rc = pager_playback_one_page(pPager, &pPager->jfd, 1);
+ assert( rc!=SQLITE_DONE );
+ if( rc!=SQLITE_OK ) goto end_stmt_playback;
+ }
+ }
+
+ pPager->journalOff = szJ;
+
+end_stmt_playback:
+ if( rc!=SQLITE_OK ){
+ pPager->errMask |= PAGER_ERR_CORRUPT;
+ rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ }else{
+ pPager->journalOff = szJ;
+ /* pager_reload_cache(pPager); */
+ }
+ return rc;
+}
+
+/*
+** Change the maximum number of in-memory pages that are allowed.
+**
+** The maximum number is the absolute value of the mxPage parameter.
+** If mxPage is negative, the noSync flag is also set. noSync bypasses
+** calls to sqlite3OsSync(). The pager runs much faster with noSync on,
+** but if the operating system crashes or there is an abrupt power
+** failure, the database file might be left in an inconsistent and
+** unrepairable state.
+*/
+void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){
+ if( mxPage>=0 ){
+ pPager->noSync = pPager->tempFile;
+ if( pPager->noSync ) pPager->needSync = 0;
+ }else{
+ pPager->noSync = 1;
+ mxPage = -mxPage;
+ }
+ if( mxPage>10 ){
+ pPager->mxPage = mxPage;
+ }else{
+ pPager->mxPage = 10;
+ }
+}
+
+/*
+** Adjust the robustness of the database to damage due to OS crashes
+** or power failures by changing the number of syncs()s when writing
+** the rollback journal. There are three levels:
+**
+** OFF sqlite3OsSync() is never called. This is the default
+** for temporary and transient files.
+**
+** NORMAL The journal is synced once before writes begin on the
+** database. This is normally adequate protection, but
+** it is theoretically possible, though very unlikely,
+** that an inopertune power failure could leave the journal
+** in a state which would cause damage to the database
+** when it is rolled back.
+**
+** FULL The journal is synced twice before writes begin on the
+** database (with some additional information - the nRec field
+** of the journal header - being written in between the two
+** syncs). If we assume that writing a
+** single disk sector is atomic, then this mode provides
+** assurance that the journal will not be corrupted to the
+** point of causing damage to the database during rollback.
+**
+** Numeric values associated with these states are OFF==1, NORMAL=2,
+** and FULL=3.
+*/
+void sqlite3pager_set_safety_level(Pager *pPager, int level){
+ pPager->noSync = level==1 || pPager->tempFile;
+ pPager->fullSync = level==3 && !pPager->tempFile;
+ if( pPager->noSync ) pPager->needSync = 0;
+}
+
+/*
+** Open a temporary file. Write the name of the file into zName
+** (zName must be at least SQLITE_TEMPNAME_SIZE bytes long.) Write
+** the file descriptor into *fd. Return SQLITE_OK on success or some
+** other error code if we fail.
+**
+** The OS will automatically delete the temporary file when it is
+** closed.
+*/
+static int sqlite3pager_opentemp(char *zFile, OsFile *fd){
+ int cnt = 8;
+ int rc;
+ do{
+ cnt--;
+ sqlite3OsTempFileName(zFile);
+ rc = sqlite3OsOpenExclusive(zFile, fd, 1);
+ }while( cnt>0 && rc!=SQLITE_OK && rc!=SQLITE_NOMEM );
+ return rc;
+}
+
+/*
+** Create a new page cache and put a pointer to the page cache in *ppPager.
+** The file to be cached need not exist. The file is not locked until
+** the first call to sqlite3pager_get() and is only held open until the
+** last page is released using sqlite3pager_unref().
+**
+** If zFilename is NULL then a randomly-named temporary file is created
+** and used as the file to be cached. The file will be deleted
+** automatically when it is closed.
+**
+** If zFilename is ":memory:" then all information is held in cache.
+** It is never written to disk. This can be used to implement an
+** in-memory database.
+*/
+int sqlite3pager_open(
+ Pager **ppPager, /* Return the Pager structure here */
+ const char *zFilename, /* Name of the database file to open */
+ int nExtra, /* Extra bytes append to each in-memory page */
+ int useJournal /* TRUE to use a rollback journal on this file */
+){
+ Pager *pPager;
+ char *zFullPathname = 0;
+ int nameLen;
+ OsFile fd;
+ int rc = SQLITE_OK;
+ int i;
+ int tempFile = 0;
+ int memDb = 0;
+ int readOnly = 0;
+ char zTemp[SQLITE_TEMPNAME_SIZE];
+
+ *ppPager = 0;
+ memset(&fd, 0, sizeof(fd));
+ if( sqlite3_malloc_failed ){
+ return SQLITE_NOMEM;
+ }
+ if( zFilename && zFilename[0] ){
+ if( strcmp(zFilename,":memory:")==0 ){
+ memDb = 1;
+ zFullPathname = sqliteStrDup("");
+ rc = SQLITE_OK;
+ }else{
+ zFullPathname = sqlite3OsFullPathname(zFilename);
+ if( zFullPathname ){
+ rc = sqlite3OsOpenReadWrite(zFullPathname, &fd, &readOnly);
+ }
+ }
+ }else{
+ rc = sqlite3pager_opentemp(zTemp, &fd);
+ zFilename = zTemp;
+ zFullPathname = sqlite3OsFullPathname(zFilename);
+ if( rc==SQLITE_OK ){
+ tempFile = 1;
+ }
+ }
+ if( !zFullPathname ){
+ sqlite3OsClose(&fd);
+ return SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3OsClose(&fd);
+ sqliteFree(zFullPathname);
+ return rc;
+ }
+ nameLen = strlen(zFullPathname);
+ pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 );
+ if( pPager==0 ){
+ sqlite3OsClose(&fd);
+ sqliteFree(zFullPathname);
+ return SQLITE_NOMEM;
+ }
+ TRACE3("OPEN %d %s\n", fd.h, zFullPathname);
+ pPager->zFilename = (char*)&pPager[1];
+ pPager->zDirectory = &pPager->zFilename[nameLen+1];
+ pPager->zJournal = &pPager->zDirectory[nameLen+1];
+ strcpy(pPager->zFilename, zFullPathname);
+ strcpy(pPager->zDirectory, zFullPathname);
+ for(i=nameLen; i>0 && pPager->zDirectory[i-1]!='/'; i--){}
+ if( i>0 ) pPager->zDirectory[i-1] = 0;
+ strcpy(pPager->zJournal, zFullPathname);
+ sqliteFree(zFullPathname);
+ strcpy(&pPager->zJournal[nameLen], "-journal");
+ pPager->fd = fd;
+#if OS_UNIX
+ pPager->fd.pPager = pPager;
+#endif
+ pPager->journalOpen = 0;
+ pPager->useJournal = useJournal && !memDb;
+ pPager->stmtOpen = 0;
+ pPager->stmtInUse = 0;
+ pPager->nRef = 0;
+ pPager->dbSize = memDb-1;
+ pPager->pageSize = SQLITE_DEFAULT_PAGE_SIZE;
+ pPager->stmtSize = 0;
+ pPager->stmtJSize = 0;
+ pPager->nPage = 0;
+ pPager->mxPage = 100;
+ pPager->state = PAGER_UNLOCK;
+ pPager->errMask = 0;
+ pPager->tempFile = tempFile;
+ pPager->memDb = memDb;
+ pPager->readOnly = readOnly;
+ pPager->needSync = 0;
+ pPager->noSync = pPager->tempFile || !useJournal;
+ pPager->fullSync = (pPager->noSync?0:1);
+ pPager->pFirst = 0;
+ pPager->pFirstSynced = 0;
+ pPager->pLast = 0;
+ pPager->nExtra = nExtra;
+ pPager->sectorSize = PAGER_SECTOR_SIZE;
+ pPager->pBusyHandler = 0;
+ memset(pPager->aHash, 0, sizeof(pPager->aHash));
+ *ppPager = pPager;
+ return SQLITE_OK;
+}
+
+/*
+** Set the busy handler function.
+*/
+void sqlite3pager_set_busyhandler(Pager *pPager, BusyHandler *pBusyHandler){
+ pPager->pBusyHandler = pBusyHandler;
+}
+
+/*
+** Set the destructor for this pager. If not NULL, the destructor is called
+** when the reference count on each page reaches zero. The destructor can
+** be used to clean up information in the extra segment appended to each page.
+**
+** The destructor is not called as a result sqlite3pager_close().
+** Destructors are only called by sqlite3pager_unref().
+*/
+void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*,int)){
+ pPager->xDestructor = xDesc;
+}
+
+/*
+** Set the reinitializer for this pager. If not NULL, the reinitializer
+** is called when the content of a page in cache is restored to its original
+** value as a result of a rollback. The callback gives higher-level code
+** an opportunity to restore the EXTRA section to agree with the restored
+** page data.
+*/
+void sqlite3pager_set_reiniter(Pager *pPager, void (*xReinit)(void*,int)){
+ pPager->xReiniter = xReinit;
+}
+
+/*
+** Set the page size.
+**
+** The page size must only be changed when the cache is empty.
+*/
+void sqlite3pager_set_pagesize(Pager *pPager, int pageSize){
+ assert( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE );
+ pPager->pageSize = pageSize;
+}
+
+/*
+** Read the first N bytes from the beginning of the file into memory
+** that pDest points to. No error checking is done.
+*/
+void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){
+ memset(pDest, 0, N);
+ if( pPager->memDb==0 ){
+ sqlite3OsSeek(&pPager->fd, 0);
+ sqlite3OsRead(&pPager->fd, pDest, N);
+ }
+}
+
+/*
+** Return the total number of pages in the disk file associated with
+** pPager.
+*/
+int sqlite3pager_pagecount(Pager *pPager){
+ i64 n;
+ assert( pPager!=0 );
+ if( pPager->dbSize>=0 ){
+ return pPager->dbSize;
+ }
+ if( sqlite3OsFileSize(&pPager->fd, &n)!=SQLITE_OK ){
+ pPager->errMask |= PAGER_ERR_DISK;
+ return 0;
+ }
+ n /= pPager->pageSize;
+ if( !pPager->memDb && n==PENDING_BYTE/pPager->pageSize ){
+ n++;
+ }
+ if( pPager->state!=PAGER_UNLOCK ){
+ pPager->dbSize = n;
+ }
+ return n;
+}
+
+/*
+** Forward declaration
+*/
+static int syncJournal(Pager*);
+
+
+/*
+** Unlink a page from the free list (the list of all pages where nRef==0)
+** and from its hash collision chain.
+*/
+static void unlinkPage(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+
+ /* Keep the pFirstSynced pointer pointing at the first synchronized page */
+ if( pPg==pPager->pFirstSynced ){
+ PgHdr *p = pPg->pNextFree;
+ while( p && p->needSync ){ p = p->pNextFree; }
+ pPager->pFirstSynced = p;
+ }
+
+ /* Unlink from the freelist */
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg->pNextFree;
+ }else{
+ assert( pPager->pFirst==pPg );
+ pPager->pFirst = pPg->pNextFree;
+ }
+ if( pPg->pNextFree ){
+ pPg->pNextFree->pPrevFree = pPg->pPrevFree;
+ }else{
+ assert( pPager->pLast==pPg );
+ pPager->pLast = pPg->pPrevFree;
+ }
+ pPg->pNextFree = pPg->pPrevFree = 0;
+
+ /* Unlink from the pgno hash table */
+ if( pPg->pNextHash ){
+ pPg->pNextHash->pPrevHash = pPg->pPrevHash;
+ }
+ if( pPg->pPrevHash ){
+ pPg->pPrevHash->pNextHash = pPg->pNextHash;
+ }else{
+ int h = pager_hash(pPg->pgno);
+ assert( pPager->aHash[h]==pPg );
+ pPager->aHash[h] = pPg->pNextHash;
+ }
+ pPg->pNextHash = pPg->pPrevHash = 0;
+}
+
+/*
+** This routine is used to truncate an in-memory database. Delete
+** all pages whose pgno is larger than pPager->dbSize and is unreferenced.
+** Referenced pages larger than pPager->dbSize are zeroed.
+*/
+static void memoryTruncate(Pager *pPager){
+ PgHdr *pPg;
+ PgHdr **ppPg;
+ int dbSize = pPager->dbSize;
+
+ ppPg = &pPager->pAll;
+ while( (pPg = *ppPg)!=0 ){
+ if( pPg->pgno<=dbSize ){
+ ppPg = &pPg->pNextAll;
+ }else if( pPg->nRef>0 ){
+ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
+ ppPg = &pPg->pNextAll;
+ }else{
+ *ppPg = pPg->pNextAll;
+ unlinkPage(pPg);
+ sqliteFree(pPg);
+ pPager->nPage--;
+ }
+ }
+}
+
+/*
+** Truncate the file to the number of pages specified.
+*/
+int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
+ int rc;
+ sqlite3pager_pagecount(pPager);
+ if( pPager->errMask!=0 ){
+ rc = pager_errcode(pPager);
+ return rc;
+ }
+ if( nPage>=(unsigned)pPager->dbSize ){
+ return SQLITE_OK;
+ }
+ if( pPager->memDb ){
+ pPager->dbSize = nPage;
+ memoryTruncate(pPager);
+ return SQLITE_OK;
+ }
+ rc = syncJournal(pPager);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ rc = pager_truncate(pPager, nPage);
+ if( rc==SQLITE_OK ){
+ pPager->dbSize = nPage;
+ }
+ return rc;
+}
+
+/*
+** Shutdown the page cache. Free all memory and close all files.
+**
+** If a transaction was in progress when this routine is called, that
+** transaction is rolled back. All outstanding pages are invalidated
+** and their memory is freed. Any attempt to use a page associated
+** with this page cache after this function returns will likely
+** result in a coredump.
+*/
+int sqlite3pager_close(Pager *pPager){
+ PgHdr *pPg, *pNext;
+ switch( pPager->state ){
+ case PAGER_RESERVED:
+ case PAGER_SYNCED:
+ case PAGER_EXCLUSIVE: {
+ sqlite3pager_rollback(pPager);
+ if( !pPager->memDb ){
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ }
+ assert( pPager->journalOpen==0 );
+ break;
+ }
+ case PAGER_SHARED: {
+ if( !pPager->memDb ){
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ }
+ break;
+ }
+ default: {
+ /* Do nothing */
+ break;
+ }
+ }
+ for(pPg=pPager->pAll; pPg; pPg=pNext){
+#ifndef NDEBUG
+ if( pPager->memDb ){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ assert( !pPg->alwaysRollback );
+ assert( !pHist->pOrig );
+ assert( !pHist->pStmt );
+ }
+#endif
+ pNext = pPg->pNextAll;
+ sqliteFree(pPg);
+ }
+ TRACE2("CLOSE %d\n", pPager->fd.h);
+ sqlite3OsClose(&pPager->fd);
+ assert( pPager->journalOpen==0 );
+ /* Temp files are automatically deleted by the OS
+ ** if( pPager->tempFile ){
+ ** sqlite3OsDelete(pPager->zFilename);
+ ** }
+ */
+ if( pPager->zFilename!=(char*)&pPager[1] ){
+ assert( 0 ); /* Cannot happen */
+ sqliteFree(pPager->zFilename);
+ sqliteFree(pPager->zJournal);
+ sqliteFree(pPager->zDirectory);
+ }
+ sqliteFree(pPager);
+ return SQLITE_OK;
+}
+
+/*
+** Return the page number for the given page data.
+*/
+Pgno sqlite3pager_pagenumber(void *pData){
+ PgHdr *p = DATA_TO_PGHDR(pData);
+ return p->pgno;
+}
+
+/*
+** The page_ref() function increments the reference count for a page.
+** If the page is currently on the freelist (the reference count is zero) then
+** remove it from the freelist.
+**
+** For non-test systems, page_ref() is a macro that calls _page_ref()
+** online of the reference count is zero. For test systems, page_ref()
+** is a real function so that we can set breakpoints and trace it.
+*/
+static void _page_ref(PgHdr *pPg){
+ if( pPg->nRef==0 ){
+ /* The page is currently on the freelist. Remove it. */
+ if( pPg==pPg->pPager->pFirstSynced ){
+ PgHdr *p = pPg->pNextFree;
+ while( p && p->needSync ){ p = p->pNextFree; }
+ pPg->pPager->pFirstSynced = p;
+ }
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg->pNextFree;
+ }else{
+ pPg->pPager->pFirst = pPg->pNextFree;
+ }
+ if( pPg->pNextFree ){
+ pPg->pNextFree->pPrevFree = pPg->pPrevFree;
+ }else{
+ pPg->pPager->pLast = pPg->pPrevFree;
+ }
+ pPg->pPager->nRef++;
+ }
+ pPg->nRef++;
+ REFINFO(pPg);
+}
+#ifdef SQLITE_TEST
+ static void page_ref(PgHdr *pPg){
+ if( pPg->nRef==0 ){
+ _page_ref(pPg);
+ }else{
+ pPg->nRef++;
+ REFINFO(pPg);
+ }
+ }
+#else
+# define page_ref(P) ((P)->nRef==0?_page_ref(P):(void)(P)->nRef++)
+#endif
+
+/*
+** Increment the reference count for a page. The input pointer is
+** a reference to the page data.
+*/
+int sqlite3pager_ref(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ page_ref(pPg);
+ return SQLITE_OK;
+}
+
+/*
+** Sync the journal. In other words, make sure all the pages that have
+** been written to the journal have actually reached the surface of the
+** disk. It is not safe to modify the original database file until after
+** the journal has been synced. If the original database is modified before
+** the journal is synced and a power failure occurs, the unsynced journal
+** data would be lost and we would be unable to completely rollback the
+** database changes. Database corruption would occur.
+**
+** This routine also updates the nRec field in the header of the journal.
+** (See comments on the pager_playback() routine for additional information.)
+** If the sync mode is FULL, two syncs will occur. First the whole journal
+** is synced, then the nRec field is updated, then a second sync occurs.
+**
+** For temporary databases, we do not care if we are able to rollback
+** after a power failure, so sync occurs.
+**
+** This routine clears the needSync field of every page current held in
+** memory.
+*/
+static int syncJournal(Pager *pPager){
+ PgHdr *pPg;
+ int rc = SQLITE_OK;
+
+ /* Sync the journal before modifying the main database
+ ** (assuming there is a journal and it needs to be synced.)
+ */
+ if( pPager->needSync ){
+ if( !pPager->tempFile ){
+ assert( pPager->journalOpen );
+ /* assert( !pPager->noSync ); // noSync might be set if synchronous
+ ** was turned off after the transaction was started. Ticket #615 */
+#ifndef NDEBUG
+ {
+ /* Make sure the pPager->nRec counter we are keeping agrees
+ ** with the nRec computed from the size of the journal file.
+ */
+ i64 jSz;
+ rc = sqlite3OsFileSize(&pPager->jfd, &jSz);
+ if( rc!=0 ) return rc;
+ assert( pPager->journalOff==jSz );
+ }
+#endif
+ {
+ /* Write the nRec value into the journal file header. If in
+ ** full-synchronous mode, sync the journal first. This ensures that
+ ** all data has really hit the disk before nRec is updated to mark
+ ** it as a candidate for rollback.
+ */
+ if( pPager->fullSync ){
+ TRACE2("SYNC journal of %d\n", pPager->fd.h);
+ rc = sqlite3OsSync(&pPager->jfd);
+ if( rc!=0 ) return rc;
+ }
+ sqlite3OsSeek(&pPager->jfd, pPager->journalHdr + sizeof(aJournalMagic));
+ rc = write32bits(&pPager->jfd, pPager->nRec);
+ if( rc ) return rc;
+
+ sqlite3OsSeek(&pPager->jfd, pPager->journalOff);
+ }
+ TRACE2("SYNC journal of %d\n", pPager->fd.h);
+ rc = sqlite3OsSync(&pPager->jfd);
+ if( rc!=0 ) return rc;
+ pPager->journalStarted = 1;
+ }
+ pPager->needSync = 0;
+
+ /* Erase the needSync flag from every page.
+ */
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ pPg->needSync = 0;
+ }
+ pPager->pFirstSynced = pPager->pFirst;
+ }
+
+#ifndef NDEBUG
+ /* If the Pager.needSync flag is clear then the PgHdr.needSync
+ ** flag must also be clear for all pages. Verify that this
+ ** invariant is true.
+ */
+ else{
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ assert( pPg->needSync==0 );
+ }
+ assert( pPager->pFirstSynced==pPager->pFirst );
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** Try to obtain a lock on a file. Invoke the busy callback if the lock
+** is currently not available. Repeate until the busy callback returns
+** false or until the lock succeeds.
+**
+** Return SQLITE_OK on success and an error code if we cannot obtain
+** the lock.
+*/
+static int pager_wait_on_lock(Pager *pPager, int locktype){
+ int rc;
+ assert( PAGER_SHARED==SHARED_LOCK );
+ assert( PAGER_RESERVED==RESERVED_LOCK );
+ assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK );
+ if( pPager->state>=locktype ){
+ rc = SQLITE_OK;
+ }else{
+ int busy = 1;
+ do {
+ rc = sqlite3OsLock(&pPager->fd, locktype);
+ }while( rc==SQLITE_BUSY &&
+ pPager->pBusyHandler &&
+ pPager->pBusyHandler->xFunc &&
+ pPager->pBusyHandler->xFunc(pPager->pBusyHandler->pArg, busy++)
+ );
+ if( rc==SQLITE_OK ){
+ pPager->state = locktype;
+ }
+ }
+ return rc;
+}
+
+/*
+** Given a list of pages (connected by the PgHdr.pDirty pointer) write
+** every one of those pages out to the database file and mark them all
+** as clean.
+*/
+static int pager_write_pagelist(PgHdr *pList){
+ Pager *pPager;
+ int rc;
+
+ if( pList==0 ) return SQLITE_OK;
+ pPager = pList->pPager;
+
+ /* At this point there may be either a RESERVED or EXCLUSIVE lock on the
+ ** database file. If there is already an EXCLUSIVE lock, the following
+ ** calls to sqlite3OsLock() are no-ops.
+ **
+ ** Moving the lock from RESERVED to EXCLUSIVE actually involves going
+ ** through an intermediate state PENDING. A PENDING lock prevents new
+ ** readers from attaching to the database but is unsufficient for us to
+ ** write. The idea of a PENDING lock is to prevent new readers from
+ ** coming in while we wait for existing readers to clear.
+ **
+ ** While the pager is in the RESERVED state, the original database file
+ ** is unchanged and we can rollback without having to playback the
+ ** journal into the original database file. Once we transition to
+ ** EXCLUSIVE, it means the database file has been changed and any rollback
+ ** will require a journal playback.
+ */
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ while( pList ){
+ assert( pList->dirty );
+ sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize);
+ CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
+ TRACE3("STORE %d page %d\n", pPager->fd.h, pList->pgno);
+ rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize);
+ CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
+ if( rc ) return rc;
+ pList->dirty = 0;
+ pList = pList->pDirty;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Collect every dirty page into a dirty list and
+** return a pointer to the head of that list. All pages are
+** collected even if they are still in use.
+*/
+static PgHdr *pager_get_all_dirty_pages(Pager *pPager){
+ PgHdr *p, *pList;
+ pList = 0;
+ for(p=pPager->pAll; p; p=p->pNextAll){
+ if( p->dirty ){
+ p->pDirty = pList;
+ pList = p;
+ }
+ }
+ return pList;
+}
+
+/*
+** Acquire a page.
+**
+** A read lock on the disk file is obtained when the first page is acquired.
+** This read lock is dropped when the last page is released.
+**
+** A _get works for any page number greater than 0. If the database
+** file is smaller than the requested page, then no actual disk
+** read occurs and the memory image of the page is initialized to
+** all zeros. The extra data appended to a page is always initialized
+** to zeros the first time a page is loaded into memory.
+**
+** The acquisition might fail for several reasons. In all cases,
+** an appropriate error code is returned and *ppPage is set to NULL.
+**
+** See also sqlite3pager_lookup(). Both this routine and _lookup() attempt
+** to find a page in the in-memory cache first. If the page is not already
+** in memory, this routine goes to disk to read it in whereas _lookup()
+** just returns 0. This routine acquires a read-lock the first time it
+** has to go to disk, and could also playback an old journal if necessary.
+** Since _lookup() never goes to disk, it never has to deal with locks
+** or journal files.
+*/
+int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
+ PgHdr *pPg;
+ int rc;
+
+ /* Make sure we have not hit any critical errors.
+ */
+ assert( pPager!=0 );
+ assert( pgno!=0 );
+ *ppPage = 0;
+ if( pPager->errMask & ~(PAGER_ERR_FULL) ){
+ return pager_errcode(pPager);
+ }
+
+ /* If this is the first page accessed, then get a SHARED lock
+ ** on the database file.
+ */
+ if( pPager->nRef==0 && !pPager->memDb ){
+ rc = pager_wait_on_lock(pPager, SHARED_LOCK);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ /* If a journal file exists, and there is no RESERVED lock on the
+ ** database file, then it either needs to be played back or deleted.
+ */
+ if( pPager->useJournal &&
+ sqlite3OsFileExists(pPager->zJournal) &&
+ !sqlite3OsCheckReservedLock(&pPager->fd)
+ ){
+ int rc;
+
+ /* Get an EXCLUSIVE lock on the database file. At this point it is
+ ** important that a RESERVED lock is not obtained on the way to the
+ ** EXCLUSIVE lock. If it were, another process might open the
+ ** database file, detect the RESERVED lock, and conclude that the
+ ** database is safe to read while this process is still rolling it
+ ** back.
+ **
+ ** Because the intermediate RESERVED lock is not requested, the
+ ** second process will get to this point in the code and fail to
+ ** obtain it's own EXCLUSIVE lock on the database file.
+ */
+ rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ pPager->state = PAGER_UNLOCK;
+ return rc;
+ }
+ pPager->state = PAGER_EXCLUSIVE;
+
+ /* Open the journal for reading only. Return SQLITE_BUSY if
+ ** we are unable to open the journal file.
+ **
+ ** The journal file does not need to be locked itself. The
+ ** journal file is never open unless the main database file holds
+ ** a write lock, so there is never any chance of two or more
+ ** processes opening the journal at the same time.
+ */
+ rc = sqlite3OsOpenReadOnly(pPager->zJournal, &pPager->jfd);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ pPager->state = PAGER_UNLOCK;
+ return SQLITE_BUSY;
+ }
+ pPager->journalOpen = 1;
+ pPager->journalStarted = 0;
+ pPager->journalOff = 0;
+ pPager->setMaster = 0;
+ pPager->journalHdr = 0;
+
+ /* Playback and delete the journal. Drop the database write
+ ** lock and reacquire the read lock.
+ */
+ rc = pager_playback(pPager);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }
+ pPg = 0;
+ }else{
+ /* Search for page in cache */
+ pPg = pager_lookup(pPager, pgno);
+ if( pPager->memDb && pPager->state==PAGER_UNLOCK ){
+ pPager->state = PAGER_SHARED;
+ }
+ }
+ if( pPg==0 ){
+ /* The requested page is not in the page cache. */
+ int h;
+ pPager->nMiss++;
+ if( pPager->nPage<pPager->mxPage || pPager->pFirst==0 || pPager->memDb ){
+ /* Create a new page */
+ pPg = sqliteMallocRaw( sizeof(*pPg) + pPager->pageSize
+ + sizeof(u32) + pPager->nExtra
+ + pPager->memDb*sizeof(PgHistory) );
+ if( pPg==0 ){
+ if( !pPager->memDb ){
+ pager_unwritelock(pPager);
+ }
+ pPager->errMask |= PAGER_ERR_MEM;
+ return SQLITE_NOMEM;
+ }
+ memset(pPg, 0, sizeof(*pPg));
+ if( pPager->memDb ){
+ memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
+ }
+ pPg->pPager = pPager;
+ pPg->pNextAll = pPager->pAll;
+ pPager->pAll = pPg;
+ pPager->nPage++;
+ }else{
+ /* Find a page to recycle. Try to locate a page that does not
+ ** require us to do an fsync() on the journal.
+ */
+ pPg = pPager->pFirstSynced;
+
+ /* If we could not find a page that does not require an fsync()
+ ** on the journal file then fsync the journal file. This is a
+ ** very slow operation, so we work hard to avoid it. But sometimes
+ ** it can't be helped.
+ */
+ if( pPg==0 ){
+ int rc = syncJournal(pPager);
+ if( rc!=0 ){
+ sqlite3pager_rollback(pPager);
+ return SQLITE_IOERR;
+ }
+ if( pPager->fullSync ){
+ /* If in full-sync mode, write a new journal header into the
+ ** journal file. This is done to avoid ever modifying a journal
+ ** header that is involved in the rollback of pages that have
+ ** already been written to the database (in case the header is
+ ** trashed when the nRec field is updated).
+ */
+ pPager->nRec = 0;
+ assert( pPager->journalOff > 0 );
+ rc = writeJournalHdr(pPager);
+ if( rc!=0 ){
+ sqlite3pager_rollback(pPager);
+ return SQLITE_IOERR;
+ }
+ }
+ pPg = pPager->pFirst;
+ }
+ assert( pPg->nRef==0 );
+
+ /* Write the page to the database file if it is dirty.
+ */
+ if( pPg->dirty ){
+ assert( pPg->needSync==0 );
+ pPg->pDirty = 0;
+ rc = pager_write_pagelist( pPg );
+ if( rc!=SQLITE_OK ){
+ sqlite3pager_rollback(pPager);
+ return SQLITE_IOERR;
+ }
+ }
+ assert( pPg->dirty==0 );
+
+ /* If the page we are recycling is marked as alwaysRollback, then
+ ** set the global alwaysRollback flag, thus disabling the
+ ** sqlite_dont_rollback() optimization for the rest of this transaction.
+ ** It is necessary to do this because the page marked alwaysRollback
+ ** might be reloaded at a later time but at that point we won't remember
+ ** that is was marked alwaysRollback. This means that all pages must
+ ** be marked as alwaysRollback from here on out.
+ */
+ if( pPg->alwaysRollback ){
+ pPager->alwaysRollback = 1;
+ }
+
+ /* Unlink the old page from the free list and the hash table
+ */
+ unlinkPage(pPg);
+ pPager->nOvfl++;
+ }
+ pPg->pgno = pgno;
+ if( pPager->aInJournal && (int)pgno<=pPager->origDbSize ){
+ sqlite3CheckMemory(pPager->aInJournal, pgno/8);
+ assert( pPager->journalOpen );
+ pPg->inJournal = (pPager->aInJournal[pgno/8] & (1<<(pgno&7)))!=0;
+ pPg->needSync = 0;
+ }else{
+ pPg->inJournal = 0;
+ pPg->needSync = 0;
+ }
+ if( pPager->aInStmt && (int)pgno<=pPager->stmtSize
+ && (pPager->aInStmt[pgno/8] & (1<<(pgno&7)))!=0 ){
+ page_add_to_stmt_list(pPg);
+ }else{
+ page_remove_from_stmt_list(pPg);
+ }
+ pPg->dirty = 0;
+ pPg->nRef = 1;
+ REFINFO(pPg);
+ pPager->nRef++;
+ h = pager_hash(pgno);
+ pPg->pNextHash = pPager->aHash[h];
+ pPager->aHash[h] = pPg;
+ if( pPg->pNextHash ){
+ assert( pPg->pNextHash->pPrevHash==0 );
+ pPg->pNextHash->pPrevHash = pPg;
+ }
+ if( pPager->nExtra>0 ){
+ memset(PGHDR_TO_EXTRA(pPg, pPager), 0, pPager->nExtra);
+ }
+ sqlite3pager_pagecount(pPager);
+ if( pPager->errMask!=0 ){
+ sqlite3pager_unref(PGHDR_TO_DATA(pPg));
+ rc = pager_errcode(pPager);
+ return rc;
+ }
+ if( pPager->dbSize<(int)pgno ){
+ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
+ }else{
+ int rc;
+ assert( pPager->memDb==0 );
+ sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize);
+ rc = sqlite3OsRead(&pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize);
+ TRACE3("FETCH %d page %d\n", pPager->fd.h, pPg->pgno);
+ CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
+ if( rc!=SQLITE_OK ){
+ i64 fileSize;
+ if( sqlite3OsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK
+ || fileSize>=pgno*pPager->pageSize ){
+ sqlite3pager_unref(PGHDR_TO_DATA(pPg));
+ return rc;
+ }else{
+ memset(PGHDR_TO_DATA(pPg), 0, pPager->pageSize);
+ }
+ }
+ }
+ }else{
+ /* The requested page is in the page cache. */
+ pPager->nHit++;
+ page_ref(pPg);
+ }
+ *ppPage = PGHDR_TO_DATA(pPg);
+ return SQLITE_OK;
+}
+
+/*
+** Acquire a page if it is already in the in-memory cache. Do
+** not read the page from disk. Return a pointer to the page,
+** or 0 if the page is not in cache.
+**
+** See also sqlite3pager_get(). The difference between this routine
+** and sqlite3pager_get() is that _get() will go to the disk and read
+** in the page if the page is not already in cache. This routine
+** returns NULL if the page is not in cache or if a disk I/O error
+** has ever happened.
+*/
+void *sqlite3pager_lookup(Pager *pPager, Pgno pgno){
+ PgHdr *pPg;
+
+ assert( pPager!=0 );
+ assert( pgno!=0 );
+ if( pPager->errMask & ~(PAGER_ERR_FULL) ){
+ return 0;
+ }
+ pPg = pager_lookup(pPager, pgno);
+ if( pPg==0 ) return 0;
+ page_ref(pPg);
+ return PGHDR_TO_DATA(pPg);
+}
+
+/*
+** Release a page.
+**
+** If the number of references to the page drop to zero, then the
+** page is added to the LRU list. When all references to all pages
+** are released, a rollback occurs and the lock on the database is
+** removed.
+*/
+int sqlite3pager_unref(void *pData){
+ PgHdr *pPg;
+
+ /* Decrement the reference count for this page
+ */
+ pPg = DATA_TO_PGHDR(pData);
+ assert( pPg->nRef>0 );
+ pPg->nRef--;
+ REFINFO(pPg);
+
+ /* When the number of references to a page reach 0, call the
+ ** destructor and add the page to the freelist.
+ */
+ if( pPg->nRef==0 ){
+ Pager *pPager;
+ pPager = pPg->pPager;
+ pPg->pNextFree = 0;
+ pPg->pPrevFree = pPager->pLast;
+ pPager->pLast = pPg;
+ if( pPg->pPrevFree ){
+ pPg->pPrevFree->pNextFree = pPg;
+ }else{
+ pPager->pFirst = pPg;
+ }
+ if( pPg->needSync==0 && pPager->pFirstSynced==0 ){
+ pPager->pFirstSynced = pPg;
+ }
+ if( pPager->xDestructor ){
+ pPager->xDestructor(pData, pPager->pageSize);
+ }
+
+ /* When all pages reach the freelist, drop the read lock from
+ ** the database file.
+ */
+ pPager->nRef--;
+ assert( pPager->nRef>=0 );
+ if( pPager->nRef==0 && !pPager->memDb ){
+ pager_reset(pPager);
+ }
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Create a journal file for pPager. There should already be a RESERVED
+** or EXCLUSIVE lock on the database file when this routine is called.
+**
+** Return SQLITE_OK if everything. Return an error code and release the
+** write lock if anything goes wrong.
+*/
+static int pager_open_journal(Pager *pPager){
+ int rc;
+ assert( !pPager->memDb );
+ assert( pPager->state>=PAGER_RESERVED );
+ assert( pPager->journalOpen==0 );
+ assert( pPager->useJournal );
+ sqlite3pager_pagecount(pPager);
+ pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
+ if( pPager->aInJournal==0 ){
+ rc = SQLITE_NOMEM;
+ goto failed_to_open_journal;
+ }
+ rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
+ pPager->journalOff = 0;
+ pPager->setMaster = 0;
+ pPager->journalHdr = 0;
+ if( rc!=SQLITE_OK ){
+ goto failed_to_open_journal;
+ }
+ sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd);
+ pPager->journalOpen = 1;
+ pPager->journalStarted = 0;
+ pPager->needSync = 0;
+ pPager->alwaysRollback = 0;
+ pPager->nRec = 0;
+ if( pPager->errMask!=0 ){
+ rc = pager_errcode(pPager);
+ return rc;
+ }
+ pPager->origDbSize = pPager->dbSize;
+
+ rc = writeJournalHdr(pPager);
+
+ if( pPager->stmtAutoopen && rc==SQLITE_OK ){
+ rc = sqlite3pager_stmt_begin(pPager);
+ }
+ if( rc!=SQLITE_OK ){
+ rc = pager_unwritelock(pPager);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_FULL;
+ }
+ }
+ return rc;
+
+failed_to_open_journal:
+ sqliteFree(pPager->aInJournal);
+ pPager->aInJournal = 0;
+ sqlite3OsUnlock(&pPager->fd, NO_LOCK);
+ pPager->state = PAGER_UNLOCK;
+ return rc;
+}
+
+/*
+** Acquire a write-lock on the database. The lock is removed when
+** the any of the following happen:
+**
+** * sqlite3pager_commit() is called.
+** * sqlite3pager_rollback() is called.
+** * sqlite3pager_close() is called.
+** * sqlite3pager_unref() is called to on every outstanding page.
+**
+** The first parameter to this routine is a pointer to any open page of the
+** database file. Nothing changes about the page - it is used merely to
+** acquire a pointer to the Pager structure and as proof that there is
+** already a read-lock on the database.
+**
+** The second parameter indicates how much space in bytes to reserve for a
+** master journal file-name at the start of the journal when it is created.
+**
+** A journal file is opened if this is not a temporary file. For temporary
+** files, the opening of the journal file is deferred until there is an
+** actual need to write to the journal.
+**
+** If the database is already reserved for writing, this routine is a no-op.
+**
+** If exFlag is true, go ahead and get an EXCLUSIVE lock on the file
+** immediately instead of waiting until we try to flush the cache. The
+** exFlag is ignored if a transaction is already active.
+*/
+int sqlite3pager_begin(void *pData, int exFlag){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+ int rc = SQLITE_OK;
+ assert( pPg->nRef>0 );
+ assert( pPager->state!=PAGER_UNLOCK );
+ if( pPager->state==PAGER_SHARED ){
+ assert( pPager->aInJournal==0 );
+ if( pPager->memDb ){
+ pPager->state = PAGER_EXCLUSIVE;
+ pPager->origDbSize = pPager->dbSize;
+ }else{
+ if( SQLITE_BUSY_RESERVED_LOCK || exFlag ){
+ rc = pager_wait_on_lock(pPager, RESERVED_LOCK);
+ }else{
+ rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
+ }
+ if( rc==SQLITE_OK ){
+ pPager->state = PAGER_RESERVED;
+ if( exFlag ){
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ pPager->dirtyCache = 0;
+ TRACE2("TRANSACTION %d\n", pPager->fd.h);
+ if( pPager->useJournal && !pPager->tempFile ){
+ rc = pager_open_journal(pPager);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Mark a data page as writeable. The page is written into the journal
+** if it is not there already. This routine must be called before making
+** changes to a page.
+**
+** The first time this routine is called, the pager creates a new
+** journal and acquires a RESERVED lock on the database. If the RESERVED
+** lock could not be acquired, this routine returns SQLITE_BUSY. The
+** calling routine must check for that return value and be careful not to
+** change any page data until this routine returns SQLITE_OK.
+**
+** If the journal file could not be written because the disk is full,
+** then this routine returns SQLITE_FULL and does an immediate rollback.
+** All subsequent write attempts also return SQLITE_FULL until there
+** is a call to sqlite3pager_commit() or sqlite3pager_rollback() to
+** reset.
+*/
+int sqlite3pager_write(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+ int rc = SQLITE_OK;
+
+ /* Check for errors
+ */
+ if( pPager->errMask ){
+ return pager_errcode(pPager);
+ }
+ if( pPager->readOnly ){
+ return SQLITE_PERM;
+ }
+
+ assert( !pPager->setMaster );
+
+ /* Mark the page as dirty. If the page has already been written
+ ** to the journal then we can return right away.
+ */
+ pPg->dirty = 1;
+ if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){
+ pPager->dirtyCache = 1;
+ return SQLITE_OK;
+ }
+
+ /* If we get this far, it means that the page needs to be
+ ** written to the transaction journal or the ckeckpoint journal
+ ** or both.
+ **
+ ** First check to see that the transaction journal exists and
+ ** create it if it does not.
+ */
+ assert( pPager->state!=PAGER_UNLOCK );
+ rc = sqlite3pager_begin(pData, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ assert( pPager->state>=PAGER_RESERVED );
+ if( !pPager->journalOpen && pPager->useJournal ){
+ rc = pager_open_journal(pPager);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ assert( pPager->journalOpen || !pPager->useJournal );
+ pPager->dirtyCache = 1;
+
+ /* The transaction journal now exists and we have a RESERVED or an
+ ** EXCLUSIVE lock on the main database file. Write the current page to
+ ** the transaction journal if it is not there already.
+ */
+ if( !pPg->inJournal && (pPager->useJournal || pPager->memDb) ){
+ if( (int)pPg->pgno <= pPager->origDbSize ){
+ int szPg;
+ u32 saved;
+ if( pPager->memDb ){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ TRACE3("JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno);
+ assert( pHist->pOrig==0 );
+ pHist->pOrig = sqliteMallocRaw( pPager->pageSize );
+ if( pHist->pOrig ){
+ memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
+ }
+ }else{
+ u32 cksum;
+ CODEC(pPager, pData, pPg->pgno, 7);
+ cksum = pager_cksum(pPager, pPg->pgno, pData);
+ saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager);
+ store32bits(cksum, pPg, pPager->pageSize);
+ szPg = pPager->pageSize+8;
+ store32bits(pPg->pgno, pPg, -4);
+ rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
+ pPager->journalOff += szPg;
+ TRACE4("JOURNAL %d page %d needSync=%d\n",
+ pPager->fd.h, pPg->pgno, pPg->needSync);
+ CODEC(pPager, pData, pPg->pgno, 0);
+ *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved;
+ if( rc!=SQLITE_OK ){
+ sqlite3pager_rollback(pPager);
+ pPager->errMask |= PAGER_ERR_FULL;
+ return rc;
+ }
+ pPager->nRec++;
+ assert( pPager->aInJournal!=0 );
+ pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ pPg->needSync = !pPager->noSync;
+ if( pPager->stmtInUse ){
+ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_stmt_list(pPg);
+ }
+ }
+ }else{
+ pPg->needSync = !pPager->journalStarted && !pPager->noSync;
+ TRACE4("APPEND %d page %d needSync=%d\n",
+ pPager->fd.h, pPg->pgno, pPg->needSync);
+ }
+ if( pPg->needSync ){
+ pPager->needSync = 1;
+ }
+ pPg->inJournal = 1;
+ }
+
+ /* If the statement journal is open and the page is not in it,
+ ** then write the current page to the statement journal. Note that
+ ** the statement journal format differs from the standard journal format
+ ** in that it omits the checksums and the header.
+ */
+ if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){
+ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
+ if( pPager->memDb ){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ assert( pHist->pStmt==0 );
+ pHist->pStmt = sqliteMallocRaw( pPager->pageSize );
+ if( pHist->pStmt ){
+ memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
+ }
+ TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno);
+ }else{
+ store32bits(pPg->pgno, pPg, -4);
+ CODEC(pPager, pData, pPg->pgno, 7);
+ rc = sqlite3OsWrite(&pPager->stfd, ((char*)pData)-4, pPager->pageSize+4);
+ TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno);
+ CODEC(pPager, pData, pPg->pgno, 0);
+ if( rc!=SQLITE_OK ){
+ sqlite3pager_rollback(pPager);
+ pPager->errMask |= PAGER_ERR_FULL;
+ return rc;
+ }
+ pPager->stmtNRec++;
+ assert( pPager->aInStmt!=0 );
+ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ }
+ page_add_to_stmt_list(pPg);
+ }
+
+ /* Update the database size and return.
+ */
+ if( pPager->dbSize<(int)pPg->pgno ){
+ pPager->dbSize = pPg->pgno;
+ if( !pPager->memDb && pPager->dbSize==PENDING_BYTE/pPager->pageSize ){
+ pPager->dbSize++;
+ }
+ }
+ return rc;
+}
+
+/*
+** Return TRUE if the page given in the argument was previously passed
+** to sqlite3pager_write(). In other words, return TRUE if it is ok
+** to change the content of the page.
+*/
+int sqlite3pager_iswriteable(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ return pPg->dirty;
+}
+
+/*
+** Replace the content of a single page with the information in the third
+** argument.
+*/
+int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void *pData){
+ void *pPage;
+ int rc;
+
+ rc = sqlite3pager_get(pPager, pgno, &pPage);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3pager_write(pPage);
+ if( rc==SQLITE_OK ){
+ memcpy(pPage, pData, pPager->pageSize);
+ }
+ sqlite3pager_unref(pPage);
+ }
+ return rc;
+}
+
+/*
+** A call to this routine tells the pager that it is not necessary to
+** write the information on page "pgno" back to the disk, even though
+** that page might be marked as dirty.
+**
+** The overlying software layer calls this routine when all of the data
+** on the given page is unused. The pager marks the page as clean so
+** that it does not get written to disk.
+**
+** Tests show that this optimization, together with the
+** sqlite3pager_dont_rollback() below, more than double the speed
+** of large INSERT operations and quadruple the speed of large DELETEs.
+**
+** When this routine is called, set the alwaysRollback flag to true.
+** Subsequent calls to sqlite3pager_dont_rollback() for the same page
+** will thereafter be ignored. This is necessary to avoid a problem
+** where a page with data is added to the freelist during one part of
+** a transaction then removed from the freelist during a later part
+** of the same transaction and reused for some other purpose. When it
+** is first added to the freelist, this routine is called. When reused,
+** the dont_rollback() routine is called. But because the page contains
+** critical data, we still need to be sure it gets rolled back in spite
+** of the dont_rollback() call.
+*/
+void sqlite3pager_dont_write(Pager *pPager, Pgno pgno){
+ PgHdr *pPg;
+
+ if( pPager->memDb ) return;
+
+ pPg = pager_lookup(pPager, pgno);
+ pPg->alwaysRollback = 1;
+ if( pPg && pPg->dirty ){
+ if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
+ /* If this pages is the last page in the file and the file has grown
+ ** during the current transaction, then do NOT mark the page as clean.
+ ** When the database file grows, we must make sure that the last page
+ ** gets written at least once so that the disk file will be the correct
+ ** size. If you do not write this page and the size of the file
+ ** on the disk ends up being too small, that can lead to database
+ ** corruption during the next transaction.
+ */
+ }else{
+ TRACE3("DONT_WRITE page %d of %d\n", pgno, pPager->fd.h);
+ pPg->dirty = 0;
+ }
+ }
+}
+
+/*
+** A call to this routine tells the pager that if a rollback occurs,
+** it is not necessary to restore the data on the given page. This
+** means that the pager does not have to record the given page in the
+** rollback journal.
+*/
+void sqlite3pager_dont_rollback(void *pData){
+ PgHdr *pPg = DATA_TO_PGHDR(pData);
+ Pager *pPager = pPg->pPager;
+
+ if( pPager->state!=PAGER_EXCLUSIVE || pPager->journalOpen==0 ) return;
+ if( pPg->alwaysRollback || pPager->alwaysRollback || pPager->memDb ) return;
+ if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
+ assert( pPager->aInJournal!=0 );
+ pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ pPg->inJournal = 1;
+ if( pPager->stmtInUse ){
+ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_stmt_list(pPg);
+ }
+ TRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, pPager->fd.h);
+ }
+ if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){
+ assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
+ assert( pPager->aInStmt!=0 );
+ pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
+ page_add_to_stmt_list(pPg);
+ }
+}
+
+
+/*
+** Clear a PgHistory block
+*/
+static void clearHistory(PgHistory *pHist){
+ sqliteFree(pHist->pOrig);
+ sqliteFree(pHist->pStmt);
+ pHist->pOrig = 0;
+ pHist->pStmt = 0;
+}
+
+/*
+** Commit all changes to the database and release the write lock.
+**
+** If the commit fails for any reason, a rollback attempt is made
+** and an error code is returned. If the commit worked, SQLITE_OK
+** is returned.
+*/
+int sqlite3pager_commit(Pager *pPager){
+ int rc;
+ PgHdr *pPg;
+
+ if( pPager->errMask==PAGER_ERR_FULL ){
+ rc = sqlite3pager_rollback(pPager);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_FULL;
+ }
+ return rc;
+ }
+ if( pPager->errMask!=0 ){
+ rc = pager_errcode(pPager);
+ return rc;
+ }
+ if( pPager->state<PAGER_RESERVED ){
+ return SQLITE_ERROR;
+ }
+ TRACE2("COMMIT %d\n", pPager->fd.h);
+ if( pPager->memDb ){
+ pPg = pager_get_all_dirty_pages(pPager);
+ while( pPg ){
+ clearHistory(PGHDR_TO_HIST(pPg, pPager));
+ pPg->dirty = 0;
+ pPg->inJournal = 0;
+ pPg->inStmt = 0;
+ pPg->pPrevStmt = pPg->pNextStmt = 0;
+ pPg = pPg->pDirty;
+ }
+#ifndef NDEBUG
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ assert( !pPg->alwaysRollback );
+ assert( !pHist->pOrig );
+ assert( !pHist->pStmt );
+ }
+#endif
+ pPager->pStmt = 0;
+ pPager->state = PAGER_SHARED;
+ return SQLITE_OK;
+ }
+ if( pPager->dirtyCache==0 ){
+ /* Exit early (without doing the time-consuming sqlite3OsSync() calls)
+ ** if there have been no changes to the database file. */
+ assert( pPager->needSync==0 );
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+ }
+ assert( pPager->journalOpen );
+ rc = sqlite3pager_sync(pPager, 0);
+ if( rc!=SQLITE_OK ){
+ goto commit_abort;
+ }
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+
+ /* Jump here if anything goes wrong during the commit process.
+ */
+commit_abort:
+ sqlite3pager_rollback(pPager);
+ return rc;
+}
+
+/*
+** Rollback all changes. The database falls back to PAGER_SHARED mode.
+** All in-memory cache pages revert to their original data contents.
+** The journal is deleted.
+**
+** This routine cannot fail unless some other process is not following
+** the correct locking protocol (SQLITE_PROTOCOL) or unless some other
+** process is writing trash into the journal file (SQLITE_CORRUPT) or
+** unless a prior malloc() failed (SQLITE_NOMEM). Appropriate error
+** codes are returned for all these occasions. Otherwise,
+** SQLITE_OK is returned.
+*/
+int sqlite3pager_rollback(Pager *pPager){
+ int rc;
+ TRACE2("ROLLBACK %d\n", pPager->fd.h);
+ if( pPager->memDb ){
+ PgHdr *p;
+ for(p=pPager->pAll; p; p=p->pNextAll){
+ PgHistory *pHist;
+ assert( !p->alwaysRollback );
+ if( !p->dirty ){
+ assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
+ assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
+ continue;
+ }
+
+ pHist = PGHDR_TO_HIST(p, pPager);
+ if( pHist->pOrig ){
+ memcpy(PGHDR_TO_DATA(p), pHist->pOrig, pPager->pageSize);
+ TRACE3("ROLLBACK-PAGE %d of %d\n", p->pgno, pPager->fd.h);
+ }else{
+ TRACE3("PAGE %d is clean on %d\n", p->pgno, pPager->fd.h);
+ }
+ clearHistory(pHist);
+ p->dirty = 0;
+ p->inJournal = 0;
+ p->inStmt = 0;
+ p->pPrevStmt = p->pNextStmt = 0;
+
+ if( pPager->xReiniter ){
+ pPager->xReiniter(PGHDR_TO_DATA(p), pPager->pageSize);
+ }
+
+ }
+ pPager->pStmt = 0;
+ pPager->dbSize = pPager->origDbSize;
+ memoryTruncate(pPager);
+ pPager->stmtInUse = 0;
+ pPager->state = PAGER_SHARED;
+ return SQLITE_OK;
+ }
+
+ if( !pPager->dirtyCache || !pPager->journalOpen ){
+ rc = pager_unwritelock(pPager);
+ pPager->dbSize = -1;
+ return rc;
+ }
+
+ if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
+ if( pPager->state>=PAGER_EXCLUSIVE ){
+ pager_playback(pPager);
+ }
+ return pager_errcode(pPager);
+ }
+ if( pPager->state==PAGER_RESERVED ){
+ int rc2, rc3;
+ rc = pager_reload_cache(pPager);
+ rc2 = pager_truncate(pPager, pPager->origDbSize);
+ rc3 = pager_unwritelock(pPager);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ if( rc3 ) rc = rc3;
+ }
+ }else{
+ rc = pager_playback(pPager);
+ }
+ if( rc!=SQLITE_OK ){
+ rc = SQLITE_CORRUPT; /* bkpt-CORRUPT */
+ pPager->errMask |= PAGER_ERR_CORRUPT;
+ }
+ pPager->dbSize = -1;
+ return rc;
+}
+
+/*
+** Return TRUE if the database file is opened read-only. Return FALSE
+** if the database is (in theory) writable.
+*/
+int sqlite3pager_isreadonly(Pager *pPager){
+ return pPager->readOnly;
+}
+
+/*
+** This routine is used for testing and analysis only.
+*/
+int *sqlite3pager_stats(Pager *pPager){
+ static int a[9];
+ a[0] = pPager->nRef;
+ a[1] = pPager->nPage;
+ a[2] = pPager->mxPage;
+ a[3] = pPager->dbSize;
+ a[4] = pPager->state;
+ a[5] = pPager->errMask;
+ a[6] = pPager->nHit;
+ a[7] = pPager->nMiss;
+ a[8] = pPager->nOvfl;
+ return a;
+}
+
+/*
+** Set the statement rollback point.
+**
+** This routine should be called with the transaction journal already
+** open. A new statement journal is created that can be used to rollback
+** changes of a single SQL command within a larger transaction.
+*/
+int sqlite3pager_stmt_begin(Pager *pPager){
+ int rc;
+ char zTemp[SQLITE_TEMPNAME_SIZE];
+ assert( !pPager->stmtInUse );
+ assert( pPager->dbSize>=0 );
+ TRACE2("STMT-BEGIN %d\n", pPager->fd.h);
+ if( pPager->memDb ){
+ pPager->stmtInUse = 1;
+ pPager->stmtSize = pPager->dbSize;
+ return SQLITE_OK;
+ }
+ if( !pPager->journalOpen ){
+ pPager->stmtAutoopen = 1;
+ return SQLITE_OK;
+ }
+ assert( pPager->journalOpen );
+ pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 );
+ if( pPager->aInStmt==0 ){
+ sqlite3OsLock(&pPager->fd, SHARED_LOCK);
+ return SQLITE_NOMEM;
+ }
+#ifndef NDEBUG
+ rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize);
+ if( rc ) goto stmt_begin_failed;
+ assert( pPager->stmtJSize == pPager->journalOff );
+#endif
+ pPager->stmtJSize = pPager->journalOff;
+ pPager->stmtSize = pPager->dbSize;
+ pPager->stmtHdrOff = 0;
+ pPager->stmtCksum = pPager->cksumInit;
+ if( !pPager->stmtOpen ){
+ rc = sqlite3pager_opentemp(zTemp, &pPager->stfd);
+ if( rc ) goto stmt_begin_failed;
+ pPager->stmtOpen = 1;
+ pPager->stmtNRec = 0;
+ }
+ pPager->stmtInUse = 1;
+ return SQLITE_OK;
+
+stmt_begin_failed:
+ if( pPager->aInStmt ){
+ sqliteFree(pPager->aInStmt);
+ pPager->aInStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Commit a statement.
+*/
+int sqlite3pager_stmt_commit(Pager *pPager){
+ if( pPager->stmtInUse ){
+ PgHdr *pPg, *pNext;
+ TRACE2("STMT-COMMIT %d\n", pPager->fd.h);
+ if( !pPager->memDb ){
+ sqlite3OsSeek(&pPager->stfd, 0);
+ /* sqlite3OsTruncate(&pPager->stfd, 0); */
+ sqliteFree( pPager->aInStmt );
+ pPager->aInStmt = 0;
+ }
+ for(pPg=pPager->pStmt; pPg; pPg=pNext){
+ pNext = pPg->pNextStmt;
+ assert( pPg->inStmt );
+ pPg->inStmt = 0;
+ pPg->pPrevStmt = pPg->pNextStmt = 0;
+ if( pPager->memDb ){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ sqliteFree(pHist->pStmt);
+ pHist->pStmt = 0;
+ }
+ }
+ pPager->stmtNRec = 0;
+ pPager->stmtInUse = 0;
+ pPager->pStmt = 0;
+ }
+ pPager->stmtAutoopen = 0;
+ return SQLITE_OK;
+}
+
+/*
+** Rollback a statement.
+*/
+int sqlite3pager_stmt_rollback(Pager *pPager){
+ int rc;
+ if( pPager->stmtInUse ){
+ TRACE2("STMT-ROLLBACK %d\n", pPager->fd.h);
+ if( pPager->memDb ){
+ PgHdr *pPg;
+ for(pPg=pPager->pStmt; pPg; pPg=pPg->pNextStmt){
+ PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
+ if( pHist->pStmt ){
+ memcpy(PGHDR_TO_DATA(pPg), pHist->pStmt, pPager->pageSize);
+ sqliteFree(pHist->pStmt);
+ pHist->pStmt = 0;
+ }
+ }
+ pPager->dbSize = pPager->stmtSize;
+ memoryTruncate(pPager);
+ rc = SQLITE_OK;
+ }else{
+ rc = pager_stmt_playback(pPager);
+ }
+ sqlite3pager_stmt_commit(pPager);
+ }else{
+ rc = SQLITE_OK;
+ }
+ pPager->stmtAutoopen = 0;
+ return rc;
+}
+
+/*
+** Return the full pathname of the database file.
+*/
+const char *sqlite3pager_filename(Pager *pPager){
+ return pPager->zFilename;
+}
+
+/*
+** Return the directory of the database file.
+*/
+const char *sqlite3pager_dirname(Pager *pPager){
+ return pPager->zDirectory;
+}
+
+/*
+** Return the full pathname of the journal file.
+*/
+const char *sqlite3pager_journalname(Pager *pPager){
+ return pPager->zJournal;
+}
+
+/*
+** Set the codec for this pager
+*/
+void sqlite3pager_set_codec(
+ Pager *pPager,
+ void (*xCodec)(void*,void*,Pgno,int),
+ void *pCodecArg
+){
+ pPager->xCodec = xCodec;
+ pPager->pCodecArg = pCodecArg;
+}
+
+/*
+** This routine is called to increment the database file change-counter,
+** stored at byte 24 of the pager file.
+*/
+static int pager_incr_changecounter(Pager *pPager){
+ void *pPage;
+ PgHdr *pPgHdr;
+ u32 change_counter;
+ int rc;
+
+ /* Open page 1 of the file for writing. */
+ rc = sqlite3pager_get(pPager, 1, &pPage);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3pager_write(pPage);
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Read the current value at byte 24. */
+ pPgHdr = DATA_TO_PGHDR(pPage);
+ change_counter = retrieve32bits(pPgHdr, 24);
+
+ /* Increment the value just read and write it back to byte 24. */
+ change_counter++;
+ store32bits(change_counter, pPgHdr, 24);
+
+ /* Release the page reference. */
+ sqlite3pager_unref(pPage);
+ return SQLITE_OK;
+}
+
+/*
+** Sync the database file for the pager pPager. zMaster points to the name
+** of a master journal file that should be written into the individual
+** journal file. zMaster may be NULL, which is interpreted as no master
+** journal (a single database transaction).
+**
+** This routine ensures that the journal is synced, all dirty pages written
+** to the database file and the database file synced. The only thing that
+** remains to commit the transaction is to delete the journal file (or
+** master journal file if specified).
+**
+** Note that if zMaster==NULL, this does not overwrite a previous value
+** passed to an sqlite3pager_sync() call.
+*/
+int sqlite3pager_sync(Pager *pPager, const char *zMaster){
+ int rc = SQLITE_OK;
+
+ /* If this is an in-memory db, or no pages have been written to, or this
+ ** function has already been called, it is a no-op.
+ */
+ if( pPager->state!=PAGER_SYNCED && !pPager->memDb && pPager->dirtyCache ){
+ PgHdr *pPg;
+ assert( pPager->journalOpen );
+
+ /* If a master journal file name has already been written to the
+ ** journal file, then no sync is required. This happens when it is
+ ** written, then the process fails to upgrade from a RESERVED to an
+ ** EXCLUSIVE lock. The next time the process tries to commit the
+ ** transaction the m-j name will have already been written.
+ */
+ if( !pPager->setMaster ){
+ rc = pager_incr_changecounter(pPager);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ rc = writeMasterJournal(pPager, zMaster);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ rc = syncJournal(pPager);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+ }
+
+ /* Write all dirty pages to the database file */
+ pPg = pager_get_all_dirty_pages(pPager);
+ rc = pager_write_pagelist(pPg);
+ if( rc!=SQLITE_OK ) goto sync_exit;
+
+ /* Sync the database file. */
+ if( !pPager->noSync ){
+ rc = sqlite3OsSync(&pPager->fd);
+ }
+
+ pPager->state = PAGER_SYNCED;
+ }
+
+sync_exit:
+ return rc;
+}
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+/*
+** Return the current state of the file lock for the given pager.
+** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK,
+** PENDING_LOCK, or EXCLUSIVE_LOCK.
+*/
+int sqlite3pager_lockstate(Pager *pPager){
+#ifdef OS_TEST
+ return pPager->fd->fd.locktype;
+#else
+ return pPager->fd.locktype;
+#endif
+}
+#endif
+
+#ifdef SQLITE_TEST
+/*
+** Print a listing of all referenced pages and their ref count.
+*/
+void sqlite3pager_refdump(Pager *pPager){
+ PgHdr *pPg;
+ for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+ if( pPg->nRef<=0 ) continue;
+ sqlite3DebugPrintf("PAGE %3d addr=%p nRef=%d\n",
+ pPg->pgno, PGHDR_TO_DATA(pPg), pPg->nRef);
+ }
+}
+#endif
diff --git a/kopete/plugins/statistics/sqlite/pager.h b/kopete/plugins/statistics/sqlite/pager.h
new file mode 100644
index 00000000..0231e27a
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/pager.h
@@ -0,0 +1,102 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This header file defines the interface that the sqlite page cache
+** subsystem. The page cache subsystem reads and writes a file a page
+** at a time and provides a journal for rollback.
+**
+** @(#) $Id$
+*/
+
+/*
+** The default size of a database page.
+*/
+#ifndef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE 1024
+#endif
+
+/* Maximum page size. The upper bound on this value is 65536 (a limit
+** imposed by the 2-byte size of cell array pointers.) The
+** maximum page size determines the amount of stack space allocated
+** by many of the routines in pager.c and btree.c On embedded architectures
+** or any machine where memory and especially stack memory is limited,
+** one may wish to chose a smaller value for the maximum page size.
+*/
+#ifndef SQLITE_MAX_PAGE_SIZE
+# define SQLITE_MAX_PAGE_SIZE 8192
+#endif
+
+/*
+** Maximum number of pages in one database.
+*/
+#define SQLITE_MAX_PAGE 1073741823
+
+/*
+** The type used to represent a page number. The first page in a file
+** is called page 1. 0 is used to represent "not a page".
+*/
+typedef unsigned int Pgno;
+
+/*
+** Each open file is managed by a separate instance of the "Pager" structure.
+*/
+typedef struct Pager Pager;
+
+
+/*
+** See source code comments for a detailed description of the following
+** routines:
+*/
+int sqlite3pager_open(Pager **ppPager, const char *zFilename,
+ int nExtra, int useJournal);
+void sqlite3pager_set_busyhandler(Pager*, BusyHandler *pBusyHandler);
+void sqlite3pager_set_destructor(Pager*, void(*)(void*,int));
+void sqlite3pager_set_reiniter(Pager*, void(*)(void*,int));
+void sqlite3pager_set_pagesize(Pager*, int);
+void sqlite3pager_read_fileheader(Pager*, int, unsigned char*);
+void sqlite3pager_set_cachesize(Pager*, int);
+int sqlite3pager_close(Pager *pPager);
+int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage);
+void *sqlite3pager_lookup(Pager *pPager, Pgno pgno);
+int sqlite3pager_ref(void*);
+int sqlite3pager_unref(void*);
+Pgno sqlite3pager_pagenumber(void*);
+int sqlite3pager_write(void*);
+int sqlite3pager_iswriteable(void*);
+int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*);
+int sqlite3pager_pagecount(Pager*);
+int sqlite3pager_truncate(Pager*,Pgno);
+int sqlite3pager_begin(void*, int exFlag);
+int sqlite3pager_commit(Pager*);
+int sqlite3pager_sync(Pager*,const char *zMaster);
+int sqlite3pager_rollback(Pager*);
+int sqlite3pager_isreadonly(Pager*);
+int sqlite3pager_stmt_begin(Pager*);
+int sqlite3pager_stmt_commit(Pager*);
+int sqlite3pager_stmt_rollback(Pager*);
+void sqlite3pager_dont_rollback(void*);
+void sqlite3pager_dont_write(Pager*, Pgno);
+int *sqlite3pager_stats(Pager*);
+void sqlite3pager_set_safety_level(Pager*,int);
+const char *sqlite3pager_filename(Pager*);
+const char *sqlite3pager_dirname(Pager*);
+const char *sqlite3pager_journalname(Pager*);
+int sqlite3pager_rename(Pager*, const char *zNewName);
+void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+int sqlite3pager_lockstate(Pager*);
+#endif
+
+#ifdef SQLITE_TEST
+void sqlite3pager_refdump(Pager*);
+int pager3_refinfo_enable;
+#endif
diff --git a/kopete/plugins/statistics/sqlite/parse.c b/kopete/plugins/statistics/sqlite/parse.c
new file mode 100644
index 00000000..d3e68e02
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/parse.c
@@ -0,0 +1,3143 @@
+/* Driver template for the LEMON parser generator.
+** The author disclaims copyright to this source code.
+*/
+/* First off, code is include which follows the "include" declaration
+** in the input file. */
+#include <stdio.h>
+#line 33 "parse.y"
+
+#include "sqliteInt.h"
+#include "parse.h"
+
+/*
+** An instance of this structure holds information about the
+** LIMIT clause of a SELECT statement.
+*/
+struct LimitVal {
+ int limit; /* The LIMIT value. -1 if there is no limit */
+ int offset; /* The OFFSET. 0 if there is none */
+};
+
+/*
+** An instance of this structure is used to store the LIKE,
+** GLOB, NOT LIKE, and NOT GLOB operators.
+*/
+struct LikeOp {
+ int opcode; /* Either TK_GLOB or TK_LIKE */
+ int not; /* True if the NOT keyword is present */
+};
+
+/*
+** An instance of the following structure describes the event of a
+** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
+** TK_DELETE, or TK_INSTEAD. If the event is of the form
+**
+** UPDATE ON (a,b,c)
+**
+** Then the "b" IdList records the list "a,b,c".
+*/
+struct TrigEvent { int a; IdList * b; };
+
+/*
+** An instance of this structure holds the ATTACH key and the key type.
+*/
+struct AttachKey { int type; Token key; };
+
+#line 48 "parse.c"
+/* Next is all token values, in a form suitable for use by makeheaders.
+** This section will be null unless lemon is run with the -m switch.
+*/
+/*
+** These constants (all generated automatically by the parser generator)
+** specify the various kinds of tokens (terminals) that the parser
+** understands.
+**
+** Each symbol here is a terminal symbol in the grammar.
+*/
+/* Make sure the INTERFACE macro is defined.
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/* The next thing included is series of defines which control
+** various aspects of the generated parser.
+** YYCODETYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 terminals
+** and nonterminals. "int" is used otherwise.
+** YYNOCODE is a number of type YYCODETYPE which corresponds
+** to no legal terminal or nonterminal number. This
+** number is used to fill in empty slots of the hash
+** table.
+** YYFALLBACK If defined, this indicates that one or more tokens
+** have fall-back values which should be used if the
+** original value of the token will not parse.
+** YYACTIONTYPE is the data type used for storing terminal
+** and nonterminal numbers. "unsigned char" is
+** used if there are fewer than 250 rules and
+** states combined. "int" is used otherwise.
+** sqlite3ParserTOKENTYPE is the data type used for minor tokens given
+** directly to the parser from the tokenizer.
+** YYMINORTYPE is the data type used for all minor tokens.
+** This is typically a union of many types, one of
+** which is sqlite3ParserTOKENTYPE. The entry in the union
+** for base tokens is called "yy0".
+** YYSTACKDEPTH is the maximum depth of the parser's stack.
+** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument
+** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument
+** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
+** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+*/
+#define YYCODETYPE unsigned char
+#define YYNOCODE 225
+#define YYACTIONTYPE unsigned short int
+#define sqlite3ParserTOKENTYPE Token
+typedef union {
+ sqlite3ParserTOKENTYPE yy0;
+ struct {int value; int mask;} yy47;
+ TriggerStep* yy91;
+ Token yy98;
+ Select* yy107;
+ struct TrigEvent yy146;
+ ExprList* yy210;
+ Expr* yy258;
+ SrcList* yy259;
+ IdList* yy272;
+ int yy284;
+ struct AttachKey yy292;
+ struct LikeOp yy342;
+ struct LimitVal yy404;
+ int yy449;
+} YYMINORTYPE;
+#define YYSTACKDEPTH 100
+#define sqlite3ParserARG_SDECL Parse *pParse;
+#define sqlite3ParserARG_PDECL ,Parse *pParse
+#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
+#define sqlite3ParserARG_STORE yypParser->pParse = pParse
+#define YYNSTATE 537
+#define YYNRULE 292
+#define YYERRORSYMBOL 130
+#define YYERRSYMDT yy449
+#define YYFALLBACK 1
+#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
+#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
+#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
+
+/* Next are that tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+**
+** N == YYNSTATE+YYNRULE A syntax error has occurred.
+**
+** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+**
+** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** slots in the yy_action[] table.
+**
+** The action table is constructed as a single large table named yy_action[].
+** Given state S and lookahead X, the action is computed as
+**
+** yy_action[ yy_shift_ofst[S] + X ]
+**
+** If the index value yy_shift_ofst[S]+X is out of range or if the value
+** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
+** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
+** and that yy_default[S] should be used instead.
+**
+** The formula above is for computing the action when the lookahead is
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the yy_reduce_ofst[] array is used in place of
+** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
+** YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** yy_action[] A single table containing all actions.
+** yy_lookahead[] A table containing the lookahead for each entry in
+** yy_action. Used to detect hash collisions.
+** yy_shift_ofst[] For each state, the offset into yy_action for
+** shifting terminals.
+** yy_reduce_ofst[] For each state, the offset into yy_action for
+** shifting non-terminals after a reduce.
+** yy_default[] Default action for each state.
+*/
+static const YYACTIONTYPE yy_action[] = {
+ /* 0 */ 257, 325, 255, 138, 140, 142, 144, 146, 148, 150,
+ /* 10 */ 152, 154, 156, 89, 87, 88, 159, 12, 4, 6,
+ /* 20 */ 158, 537, 38, 24, 830, 1, 536, 3, 329, 488,
+ /* 30 */ 534, 535, 319, 50, 124, 112, 160, 169, 174, 179,
+ /* 40 */ 168, 173, 134, 136, 128, 130, 126, 132, 138, 140,
+ /* 50 */ 142, 144, 146, 148, 150, 152, 154, 156, 26, 73,
+ /* 60 */ 384, 256, 39, 58, 64, 66, 299, 330, 612, 611,
+ /* 70 */ 351, 30, 92, 332, 326, 159, 13, 14, 353, 158,
+ /* 80 */ 5, 355, 361, 366, 499, 146, 148, 150, 152, 154,
+ /* 90 */ 156, 12, 369, 124, 112, 160, 169, 174, 179, 168,
+ /* 100 */ 173, 134, 136, 128, 130, 126, 132, 138, 140, 142,
+ /* 110 */ 144, 146, 148, 150, 152, 154, 156, 128, 130, 126,
+ /* 120 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154,
+ /* 130 */ 156, 659, 353, 244, 62, 355, 361, 366, 79, 12,
+ /* 140 */ 63, 98, 96, 289, 159, 280, 369, 349, 158, 181,
+ /* 150 */ 13, 14, 27, 12, 546, 383, 32, 10, 368, 273,
+ /* 160 */ 515, 765, 124, 112, 160, 169, 174, 179, 168, 173,
+ /* 170 */ 134, 136, 128, 130, 126, 132, 138, 140, 142, 144,
+ /* 180 */ 146, 148, 150, 152, 154, 156, 810, 349, 47, 73,
+ /* 190 */ 222, 763, 223, 114, 246, 31, 32, 48, 13, 14,
+ /* 200 */ 74, 274, 252, 166, 175, 180, 275, 304, 49, 8,
+ /* 210 */ 255, 45, 13, 14, 159, 290, 350, 382, 158, 245,
+ /* 220 */ 441, 46, 378, 183, 247, 185, 186, 15, 16, 17,
+ /* 230 */ 73, 205, 124, 112, 160, 169, 174, 179, 168, 173,
+ /* 240 */ 134, 136, 128, 130, 126, 132, 138, 140, 142, 144,
+ /* 250 */ 146, 148, 150, 152, 154, 156, 542, 306, 438, 159,
+ /* 260 */ 98, 96, 332, 158, 272, 475, 447, 437, 12, 256,
+ /* 270 */ 288, 12, 304, 339, 287, 50, 77, 124, 112, 160,
+ /* 280 */ 169, 174, 179, 168, 173, 134, 136, 128, 130, 126,
+ /* 290 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154,
+ /* 300 */ 156, 547, 36, 335, 39, 58, 64, 66, 299, 330,
+ /* 310 */ 35, 334, 291, 545, 114, 332, 114, 329, 12, 625,
+ /* 320 */ 353, 187, 306, 355, 361, 366, 422, 13, 14, 159,
+ /* 330 */ 13, 14, 184, 158, 369, 636, 188, 259, 188, 764,
+ /* 340 */ 91, 87, 88, 100, 87, 88, 219, 124, 112, 160,
+ /* 350 */ 169, 174, 179, 168, 173, 134, 136, 128, 130, 126,
+ /* 360 */ 132, 138, 140, 142, 144, 146, 148, 150, 152, 154,
+ /* 370 */ 156, 297, 282, 114, 292, 51, 237, 13, 14, 150,
+ /* 380 */ 152, 154, 156, 114, 12, 225, 53, 225, 159, 166,
+ /* 390 */ 175, 180, 158, 380, 303, 111, 433, 658, 69, 92,
+ /* 400 */ 379, 183, 92, 185, 186, 111, 124, 112, 160, 169,
+ /* 410 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132,
+ /* 420 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156,
+ /* 430 */ 103, 230, 561, 159, 773, 12, 286, 158, 631, 534,
+ /* 440 */ 535, 105, 815, 13, 14, 166, 175, 180, 203, 808,
+ /* 450 */ 215, 124, 112, 160, 169, 174, 179, 168, 173, 134,
+ /* 460 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146,
+ /* 470 */ 148, 150, 152, 154, 156, 2, 3, 183, 159, 185,
+ /* 480 */ 186, 813, 158, 43, 44, 569, 33, 633, 41, 348,
+ /* 490 */ 340, 413, 415, 414, 13, 14, 124, 112, 160, 169,
+ /* 500 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132,
+ /* 510 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156,
+ /* 520 */ 249, 336, 697, 159, 337, 338, 183, 158, 185, 186,
+ /* 530 */ 56, 57, 183, 11, 185, 186, 183, 416, 185, 186,
+ /* 540 */ 402, 124, 112, 160, 169, 174, 179, 168, 173, 134,
+ /* 550 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146,
+ /* 560 */ 148, 150, 152, 154, 156, 342, 87, 88, 159, 345,
+ /* 570 */ 87, 88, 158, 98, 96, 183, 404, 185, 186, 240,
+ /* 580 */ 9, 183, 92, 185, 186, 802, 124, 177, 160, 169,
+ /* 590 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132,
+ /* 600 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156,
+ /* 610 */ 787, 341, 257, 159, 255, 255, 183, 158, 185, 186,
+ /* 620 */ 94, 95, 480, 518, 92, 307, 314, 316, 92, 548,
+ /* 630 */ 325, 171, 112, 160, 169, 174, 179, 168, 173, 134,
+ /* 640 */ 136, 128, 130, 126, 132, 138, 140, 142, 144, 146,
+ /* 650 */ 148, 150, 152, 154, 156, 255, 25, 486, 159, 482,
+ /* 660 */ 170, 358, 158, 19, 241, 242, 252, 266, 513, 267,
+ /* 670 */ 259, 553, 72, 256, 256, 402, 68, 244, 160, 169,
+ /* 680 */ 174, 179, 168, 173, 134, 136, 128, 130, 126, 132,
+ /* 690 */ 138, 140, 142, 144, 146, 148, 150, 152, 154, 156,
+ /* 700 */ 207, 255, 72, 326, 780, 260, 68, 267, 514, 47,
+ /* 710 */ 189, 428, 388, 385, 256, 325, 259, 21, 48, 162,
+ /* 720 */ 395, 12, 114, 161, 516, 517, 195, 193, 294, 49,
+ /* 730 */ 207, 484, 209, 312, 191, 70, 71, 387, 246, 113,
+ /* 740 */ 189, 164, 165, 73, 198, 114, 363, 396, 114, 391,
+ /* 750 */ 73, 277, 529, 313, 436, 182, 195, 193, 72, 467,
+ /* 760 */ 256, 623, 68, 245, 191, 70, 71, 188, 163, 113,
+ /* 770 */ 188, 119, 120, 121, 122, 197, 114, 803, 691, 72,
+ /* 780 */ 13, 14, 92, 68, 73, 73, 207, 77, 326, 73,
+ /* 790 */ 199, 807, 99, 436, 452, 293, 189, 223, 474, 325,
+ /* 800 */ 309, 119, 120, 121, 122, 197, 423, 207, 221, 460,
+ /* 810 */ 434, 419, 195, 193, 418, 90, 224, 189, 77, 225,
+ /* 820 */ 191, 70, 71, 73, 442, 113, 420, 114, 325, 444,
+ /* 830 */ 372, 468, 114, 195, 193, 283, 325, 311, 310, 402,
+ /* 840 */ 470, 191, 70, 71, 114, 7, 113, 41, 460, 474,
+ /* 850 */ 18, 20, 22, 386, 296, 114, 457, 119, 120, 121,
+ /* 860 */ 122, 197, 766, 446, 521, 554, 123, 430, 444, 23,
+ /* 870 */ 531, 114, 326, 114, 114, 481, 114, 125, 119, 120,
+ /* 880 */ 121, 122, 197, 510, 72, 441, 114, 238, 68, 114,
+ /* 890 */ 508, 506, 114, 127, 114, 129, 131, 114, 133, 411,
+ /* 900 */ 412, 322, 114, 114, 114, 114, 407, 114, 135, 326,
+ /* 910 */ 660, 137, 207, 114, 139, 114, 141, 451, 114, 143,
+ /* 920 */ 114, 114, 189, 114, 145, 147, 149, 151, 114, 153,
+ /* 930 */ 489, 493, 437, 114, 114, 155, 479, 157, 195, 193,
+ /* 940 */ 167, 77, 176, 178, 114, 190, 191, 70, 71, 114,
+ /* 950 */ 192, 113, 114, 114, 114, 194, 196, 114, 691, 114,
+ /* 960 */ 269, 320, 343, 321, 344, 269, 204, 114, 359, 284,
+ /* 970 */ 321, 206, 114, 555, 216, 218, 220, 114, 364, 234,
+ /* 980 */ 321, 239, 660, 119, 120, 121, 122, 197, 373, 271,
+ /* 990 */ 321, 281, 114, 114, 367, 227, 227, 269, 431, 408,
+ /* 1000 */ 321, 503, 439, 44, 465, 473, 267, 471, 114, 77,
+ /* 1010 */ 402, 402, 402, 402, 455, 459, 265, 457, 402, 402,
+ /* 1020 */ 823, 417, 504, 507, 556, 471, 28, 29, 560, 37,
+ /* 1030 */ 472, 73, 34, 55, 40, 41, 42, 54, 59, 67,
+ /* 1040 */ 570, 571, 52, 75, 60, 78, 483, 485, 487, 491,
+ /* 1050 */ 61, 65, 76, 464, 495, 501, 101, 527, 77, 238,
+ /* 1060 */ 233, 235, 85, 93, 86, 80, 97, 238, 102, 81,
+ /* 1070 */ 104, 82, 108, 107, 109, 110, 83, 115, 497, 84,
+ /* 1080 */ 117, 116, 156, 172, 637, 217, 638, 118, 202, 226,
+ /* 1090 */ 639, 208, 106, 211, 227, 210, 213, 214, 212, 229,
+ /* 1100 */ 228, 231, 236, 223, 200, 243, 201, 251, 248, 250,
+ /* 1110 */ 254, 253, 232, 258, 261, 270, 264, 263, 262, 268,
+ /* 1120 */ 276, 278, 285, 295, 318, 279, 300, 303, 301, 305,
+ /* 1130 */ 333, 346, 298, 323, 327, 356, 357, 362, 370, 302,
+ /* 1140 */ 371, 53, 374, 394, 399, 354, 331, 375, 401, 409,
+ /* 1150 */ 308, 347, 315, 324, 406, 317, 405, 328, 795, 390,
+ /* 1160 */ 389, 392, 397, 410, 421, 800, 360, 381, 365, 393,
+ /* 1170 */ 398, 352, 376, 403, 801, 377, 400, 425, 426, 424,
+ /* 1180 */ 427, 429, 771, 432, 772, 435, 440, 698, 443, 794,
+ /* 1190 */ 445, 438, 809, 449, 699, 450, 453, 448, 454, 456,
+ /* 1200 */ 811, 458, 461, 462, 463, 469, 812, 814, 476, 630,
+ /* 1210 */ 478, 632, 779, 821, 490, 477, 690, 492, 494, 496,
+ /* 1220 */ 498, 693, 500, 505, 696, 509, 781, 511, 782, 783,
+ /* 1230 */ 466, 784, 785, 502, 512, 786, 520, 822, 519, 530,
+ /* 1240 */ 524, 824, 523, 825, 525, 528, 533, 828, 518, 518,
+ /* 1250 */ 518, 518, 518, 518, 522, 518, 526, 518, 518, 532,
+};
+static const YYCODETYPE yy_lookahead[] = {
+ /* 0 */ 24, 139, 26, 72, 73, 74, 75, 76, 77, 78,
+ /* 10 */ 79, 80, 81, 154, 155, 156, 40, 26, 135, 136,
+ /* 20 */ 44, 0, 158, 140, 131, 132, 133, 134, 164, 146,
+ /* 30 */ 9, 10, 170, 60, 58, 59, 60, 61, 62, 63,
+ /* 40 */ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ /* 50 */ 74, 75, 76, 77, 78, 79, 80, 81, 22, 176,
+ /* 60 */ 24, 85, 89, 90, 91, 92, 93, 94, 23, 23,
+ /* 70 */ 25, 25, 213, 100, 212, 40, 85, 86, 87, 44,
+ /* 80 */ 9, 90, 91, 92, 201, 76, 77, 78, 79, 80,
+ /* 90 */ 81, 26, 101, 58, 59, 60, 61, 62, 63, 64,
+ /* 100 */ 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ /* 110 */ 75, 76, 77, 78, 79, 80, 81, 68, 69, 70,
+ /* 120 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ /* 130 */ 81, 23, 87, 25, 29, 90, 91, 92, 179, 26,
+ /* 140 */ 35, 76, 77, 23, 40, 186, 101, 139, 44, 22,
+ /* 150 */ 85, 86, 144, 26, 9, 147, 148, 12, 159, 146,
+ /* 160 */ 95, 126, 58, 59, 60, 61, 62, 63, 64, 65,
+ /* 170 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ /* 180 */ 76, 77, 78, 79, 80, 81, 17, 139, 18, 176,
+ /* 190 */ 23, 17, 25, 139, 86, 147, 148, 27, 85, 86,
+ /* 200 */ 146, 188, 189, 204, 205, 206, 193, 45, 38, 137,
+ /* 210 */ 26, 41, 85, 86, 40, 161, 168, 169, 44, 111,
+ /* 220 */ 51, 51, 60, 103, 111, 105, 106, 13, 14, 15,
+ /* 230 */ 176, 127, 58, 59, 60, 61, 62, 63, 64, 65,
+ /* 240 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ /* 250 */ 76, 77, 78, 79, 80, 81, 9, 95, 58, 40,
+ /* 260 */ 76, 77, 100, 44, 22, 96, 97, 98, 26, 85,
+ /* 270 */ 104, 26, 45, 89, 108, 60, 107, 58, 59, 60,
+ /* 280 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ /* 290 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ /* 300 */ 81, 9, 87, 88, 89, 90, 91, 92, 93, 94,
+ /* 310 */ 157, 158, 23, 9, 139, 100, 139, 164, 26, 119,
+ /* 320 */ 87, 23, 95, 90, 91, 92, 21, 85, 86, 40,
+ /* 330 */ 85, 86, 104, 44, 101, 107, 161, 152, 161, 17,
+ /* 340 */ 154, 155, 156, 154, 155, 156, 127, 58, 59, 60,
+ /* 350 */ 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
+ /* 360 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
+ /* 370 */ 81, 23, 187, 139, 199, 89, 199, 85, 86, 78,
+ /* 380 */ 79, 80, 81, 139, 26, 210, 100, 210, 40, 204,
+ /* 390 */ 205, 206, 44, 164, 165, 161, 91, 23, 22, 213,
+ /* 400 */ 171, 103, 213, 105, 106, 161, 58, 59, 60, 61,
+ /* 410 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ /* 420 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ /* 430 */ 196, 197, 9, 40, 129, 26, 78, 44, 9, 9,
+ /* 440 */ 10, 197, 9, 85, 86, 204, 205, 206, 126, 11,
+ /* 450 */ 128, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ /* 460 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ /* 470 */ 77, 78, 79, 80, 81, 133, 134, 103, 40, 105,
+ /* 480 */ 106, 9, 44, 173, 174, 109, 149, 9, 95, 152,
+ /* 490 */ 153, 96, 97, 98, 85, 86, 58, 59, 60, 61,
+ /* 500 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ /* 510 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ /* 520 */ 111, 152, 9, 40, 155, 156, 103, 44, 105, 106,
+ /* 530 */ 13, 14, 103, 139, 105, 106, 103, 47, 105, 106,
+ /* 540 */ 139, 58, 59, 60, 61, 62, 63, 64, 65, 66,
+ /* 550 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ /* 560 */ 77, 78, 79, 80, 81, 154, 155, 156, 40, 154,
+ /* 570 */ 155, 156, 44, 76, 77, 103, 175, 105, 106, 25,
+ /* 580 */ 138, 103, 213, 105, 106, 95, 58, 59, 60, 61,
+ /* 590 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ /* 600 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ /* 610 */ 9, 22, 24, 40, 26, 26, 103, 44, 105, 106,
+ /* 620 */ 121, 122, 20, 22, 213, 96, 97, 98, 213, 9,
+ /* 630 */ 139, 60, 59, 60, 61, 62, 63, 64, 65, 66,
+ /* 640 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ /* 650 */ 77, 78, 79, 80, 81, 26, 141, 55, 40, 57,
+ /* 660 */ 89, 170, 44, 138, 110, 188, 189, 23, 67, 25,
+ /* 670 */ 152, 9, 22, 85, 85, 139, 26, 25, 60, 61,
+ /* 680 */ 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
+ /* 690 */ 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
+ /* 700 */ 50, 26, 22, 212, 9, 187, 26, 25, 139, 18,
+ /* 710 */ 60, 175, 20, 146, 85, 139, 152, 138, 27, 40,
+ /* 720 */ 146, 26, 139, 44, 155, 156, 76, 77, 78, 38,
+ /* 730 */ 50, 129, 41, 32, 84, 85, 86, 142, 86, 89,
+ /* 740 */ 60, 62, 63, 176, 161, 139, 170, 55, 139, 57,
+ /* 750 */ 176, 187, 123, 52, 146, 146, 76, 77, 22, 146,
+ /* 760 */ 85, 9, 26, 111, 84, 85, 86, 161, 89, 89,
+ /* 770 */ 161, 121, 122, 123, 124, 125, 139, 95, 9, 22,
+ /* 780 */ 85, 86, 213, 26, 176, 176, 50, 107, 212, 176,
+ /* 790 */ 207, 11, 25, 146, 25, 23, 60, 25, 161, 139,
+ /* 800 */ 99, 121, 122, 123, 124, 125, 211, 50, 199, 201,
+ /* 810 */ 215, 28, 76, 77, 31, 48, 210, 60, 107, 210,
+ /* 820 */ 84, 85, 86, 176, 216, 89, 43, 139, 139, 221,
+ /* 830 */ 170, 120, 139, 76, 77, 78, 139, 88, 89, 139,
+ /* 840 */ 203, 84, 85, 86, 139, 11, 89, 95, 201, 161,
+ /* 850 */ 16, 17, 18, 19, 161, 139, 139, 121, 122, 123,
+ /* 860 */ 124, 125, 126, 216, 30, 9, 161, 170, 221, 138,
+ /* 870 */ 36, 139, 212, 139, 139, 175, 139, 161, 121, 122,
+ /* 880 */ 123, 124, 125, 49, 22, 51, 139, 118, 26, 139,
+ /* 890 */ 56, 203, 139, 161, 139, 161, 161, 139, 161, 53,
+ /* 900 */ 54, 212, 139, 139, 139, 139, 126, 139, 161, 212,
+ /* 910 */ 24, 161, 50, 139, 161, 139, 161, 200, 139, 161,
+ /* 920 */ 139, 139, 60, 139, 161, 161, 161, 161, 139, 161,
+ /* 930 */ 96, 97, 98, 139, 139, 161, 102, 161, 76, 77,
+ /* 940 */ 161, 107, 161, 161, 139, 161, 84, 85, 86, 139,
+ /* 950 */ 161, 89, 139, 139, 139, 161, 161, 139, 9, 139,
+ /* 960 */ 139, 23, 23, 25, 25, 139, 161, 139, 23, 139,
+ /* 970 */ 25, 161, 139, 9, 161, 161, 161, 139, 23, 161,
+ /* 980 */ 25, 161, 95, 121, 122, 123, 124, 125, 23, 161,
+ /* 990 */ 25, 161, 139, 139, 161, 109, 109, 139, 23, 161,
+ /* 1000 */ 25, 146, 173, 174, 23, 23, 25, 25, 139, 107,
+ /* 1010 */ 139, 139, 139, 139, 161, 161, 195, 139, 139, 139,
+ /* 1020 */ 9, 195, 120, 23, 9, 25, 145, 23, 9, 139,
+ /* 1030 */ 161, 176, 150, 42, 159, 95, 33, 167, 46, 22,
+ /* 1040 */ 109, 109, 159, 177, 160, 178, 175, 175, 175, 175,
+ /* 1050 */ 159, 159, 176, 195, 175, 175, 113, 46, 107, 118,
+ /* 1060 */ 116, 115, 185, 214, 117, 180, 214, 118, 114, 181,
+ /* 1070 */ 25, 182, 94, 160, 26, 151, 183, 109, 200, 184,
+ /* 1080 */ 109, 139, 81, 89, 107, 126, 107, 139, 17, 139,
+ /* 1090 */ 107, 22, 198, 174, 109, 23, 139, 23, 25, 143,
+ /* 1100 */ 139, 198, 114, 25, 208, 190, 209, 111, 139, 139,
+ /* 1110 */ 143, 139, 160, 139, 191, 95, 22, 112, 192, 139,
+ /* 1120 */ 23, 191, 109, 23, 22, 192, 139, 165, 162, 139,
+ /* 1130 */ 167, 23, 159, 198, 198, 46, 22, 22, 46, 163,
+ /* 1140 */ 22, 100, 93, 24, 217, 139, 151, 139, 95, 39,
+ /* 1150 */ 166, 152, 166, 160, 220, 166, 219, 160, 11, 143,
+ /* 1160 */ 139, 139, 139, 37, 47, 95, 159, 169, 159, 143,
+ /* 1170 */ 143, 169, 162, 143, 95, 163, 218, 139, 143, 129,
+ /* 1180 */ 95, 22, 9, 159, 129, 11, 172, 119, 17, 9,
+ /* 1190 */ 9, 58, 17, 139, 119, 99, 139, 172, 67, 181,
+ /* 1200 */ 9, 67, 119, 139, 22, 22, 9, 9, 110, 9,
+ /* 1210 */ 181, 9, 9, 9, 110, 139, 9, 181, 172, 99,
+ /* 1220 */ 181, 9, 119, 22, 9, 139, 9, 139, 9, 9,
+ /* 1230 */ 202, 9, 9, 202, 143, 9, 23, 9, 139, 34,
+ /* 1240 */ 24, 9, 152, 9, 139, 152, 139, 9, 224, 224,
+ /* 1250 */ 224, 224, 224, 224, 222, 224, 223, 224, 224, 222,
+};
+#define YY_SHIFT_USE_DFLT (-70)
+static const short yy_shift_ofst[] = {
+ /* 0 */ 430, 21, -70, 834, 71, -70, 247, 214, 145, 304,
+ /* 10 */ 292, 620, -70, -70, -70, -70, -70, -70, 145, 662,
+ /* 20 */ 145, 856, 145, 964, 36, 1015, 245, 46, 1004, 1019,
+ /* 30 */ -9, -70, 675, -70, 215, -70, 245, -27, -70, 940,
+ /* 40 */ -70, 1003, 170, -70, -70, -70, -70, -70, -70, -70,
+ /* 50 */ 286, 940, -70, 991, -70, 517, -70, -70, 992, 105,
+ /* 60 */ 940, -70, -70, -70, 940, -70, 1017, 862, 376, 650,
+ /* 70 */ 931, 932, 680, -70, 120, 951, -70, 166, -70, 554,
+ /* 80 */ 941, 946, 944, 943, 947, -70, 497, -70, -70, 767,
+ /* 90 */ 497, -70, 499, -70, -70, -70, 499, -70, -70, 497,
+ /* 100 */ -70, 954, 862, 1045, 862, 978, 105, -70, 1048, -70,
+ /* 110 */ -70, 483, 862, -70, 968, 245, 971, 245, -70, -70,
+ /* 120 */ -70, -70, -70, 618, 862, 573, 862, -69, 862, -69,
+ /* 130 */ 862, -69, 862, -69, 862, 49, 862, 49, 862, 9,
+ /* 140 */ 862, 9, 862, 9, 862, 9, 862, 301, 862, 301,
+ /* 150 */ 862, 1001, 862, 1001, 862, 1001, 862, -70, -70, -70,
+ /* 160 */ 679, -70, -70, -70, -70, -70, 862, 49, -70, 571,
+ /* 170 */ -70, 994, -70, -70, -70, 862, 528, 862, 49, -70,
+ /* 180 */ 127, 680, 298, 228, 977, 979, 983, -70, 483, 862,
+ /* 190 */ 618, 862, -70, 862, -70, 862, -70, 736, 35, 959,
+ /* 200 */ 322, 1071, -70, 862, 104, 862, 483, 1069, 691, 1072,
+ /* 210 */ -70, 1073, 245, 1074, -70, 862, 174, 862, 219, 862,
+ /* 220 */ 483, 167, -70, 862, -70, -70, 985, 245, -70, -70,
+ /* 230 */ 978, 105, -70, 862, 483, 988, 862, 1078, 862, 483,
+ /* 240 */ -70, -70, 652, -70, -70, -70, 113, -70, 409, -70,
+ /* 250 */ 996, -70, 242, 985, 588, -70, -70, 245, -70, -70,
+ /* 260 */ 1020, 1005, -70, 1094, 245, 644, -70, 245, -70, -70,
+ /* 270 */ 862, 483, 951, 374, 108, 1097, 588, 1020, 1005, -70,
+ /* 280 */ 757, -24, -70, -70, 1013, 358, -70, -70, -70, -70,
+ /* 290 */ 289, -70, 772, -70, 1100, -70, 348, 940, -70, 245,
+ /* 300 */ 1102, -70, 227, -70, 245, -70, 529, 701, -70, 749,
+ /* 310 */ -70, -70, -70, -70, 701, -70, 701, -70, 245, 938,
+ /* 320 */ -70, 245, 978, 105, -70, -70, 978, 105, -70, -70,
+ /* 330 */ 1048, -70, 991, -70, -70, 184, -70, -70, -70, -70,
+ /* 340 */ 589, 497, 939, -70, 497, 1108, -70, -70, -70, -70,
+ /* 350 */ 45, 233, -70, 245, -70, 1089, 1114, 245, 945, 940,
+ /* 360 */ -70, 1115, 245, 955, 940, -70, 862, 393, -70, 1092,
+ /* 370 */ 1118, 245, 965, 1049, 245, 1102, -70, 162, 1041, -70,
+ /* 380 */ -70, -70, -70, -70, 951, 423, 305, 692, 245, 985,
+ /* 390 */ -70, 245, 886, 1119, 951, 429, 245, 985, 783, 395,
+ /* 400 */ 1053, 245, 985, -70, 1110, 780, 1147, 862, 438, 1126,
+ /* 410 */ 846, -70, -70, 1070, 1079, 490, 245, 682, -70, -70,
+ /* 420 */ 1117, -70, -70, 1050, 245, 887, 1085, 245, 1159, 245,
+ /* 430 */ 975, 752, 1173, 1055, 1174, 169, 433, 200, 170, -70,
+ /* 440 */ 1068, 1075, 1171, 1180, 1181, 169, 1175, 1133, 245, 1096,
+ /* 450 */ 245, 769, 245, 1131, 862, 483, 1191, 1134, 862, 483,
+ /* 460 */ 1083, 245, 1182, 245, 981, -70, 711, 472, 1183, 862,
+ /* 470 */ 982, 862, 483, 1197, 483, 1098, 245, 949, 1198, 602,
+ /* 480 */ 245, 1200, 245, 1202, 245, 1203, 245, 1204, 478, 1104,
+ /* 490 */ 245, 949, 1207, 1133, 245, 1120, 245, 769, 1212, 1103,
+ /* 500 */ 245, 1182, 902, 513, 1201, 862, 1000, 1215, 695, 1217,
+ /* 510 */ 245, 985, 601, 65, 1219, 1220, 1222, 1223, 245, 1213,
+ /* 520 */ 1226, 1205, 675, 1216, 245, 1011, 1228, 629, 1232, 1234,
+ /* 530 */ -70, 1205, 245, 1238, -70, -70, -70,
+};
+#define YY_REDUCE_USE_DFLT (-142)
+static const short yy_reduce_ofst[] = {
+ /* 0 */ -107, 342, -142, -117, -142, -142, -142, 72, 442, -142,
+ /* 10 */ 394, -142, -142, -142, -142, -142, -142, -142, 525, -142,
+ /* 20 */ 579, -142, 731, -142, 515, -142, 8, 881, -142, -142,
+ /* 30 */ 48, -142, 337, 882, 153, -142, 890, -136, -142, 875,
+ /* 40 */ -142, -142, 310, -142, -142, -142, -142, -142, -142, -142,
+ /* 50 */ -142, 883, -142, 870, -142, -142, -142, -142, -142, 884,
+ /* 60 */ 891, -142, -142, -142, 892, -142, -142, 693, -142, 175,
+ /* 70 */ -142, -142, 54, -142, 866, 876, -142, 867, -41, 885,
+ /* 80 */ 888, 889, 893, 895, 877, -142, -141, -142, -142, -142,
+ /* 90 */ 186, -142, 849, -142, -142, -142, 852, -142, -142, 189,
+ /* 100 */ -142, -142, 234, -142, 244, 894, 913, -142, 924, -142,
+ /* 110 */ -142, 241, 705, -142, -142, 942, -142, 948, -142, -142,
+ /* 120 */ -142, -142, -142, 241, 716, 241, 732, 241, 734, 241,
+ /* 130 */ 735, 241, 737, 241, 747, 241, 750, 241, 753, 241,
+ /* 140 */ 755, 241, 758, 241, 763, 241, 764, 241, 765, 241,
+ /* 150 */ 766, 241, 768, 241, 774, 241, 776, 241, -142, -142,
+ /* 160 */ -142, -142, -142, -142, -142, -142, 779, 241, -142, -142,
+ /* 170 */ -142, -142, -142, -142, -142, 781, 241, 782, 241, -142,
+ /* 180 */ 950, 609, 866, -142, -142, -142, -142, -142, 241, 784,
+ /* 190 */ 241, 789, 241, 794, 241, 795, 241, 583, 241, 896,
+ /* 200 */ 897, -142, -142, 805, 241, 810, 241, -142, 919, -142,
+ /* 210 */ -142, -142, 957, -142, -142, 813, 241, 814, 241, 815,
+ /* 220 */ 241, -142, -142, 606, -142, -142, 956, 961, -142, -142,
+ /* 230 */ 903, 952, -142, 818, 241, -142, 177, -142, 820, 241,
+ /* 240 */ -142, 477, 915, -142, -142, -142, 969, -142, 970, -142,
+ /* 250 */ -142, -142, 972, 967, 518, -142, -142, 974, -142, -142,
+ /* 260 */ 923, 926, -142, -142, 821, -142, -142, 980, -142, -142,
+ /* 270 */ 828, 241, 13, 866, 915, -142, 564, 930, 933, -142,
+ /* 280 */ 830, 185, -142, -142, -142, 942, -142, -142, -142, -142,
+ /* 290 */ 241, -142, -142, -142, -142, -142, 241, 973, -142, 987,
+ /* 300 */ 966, 976, 962, -142, 990, -142, -142, 984, -142, -142,
+ /* 310 */ -142, -142, -142, -142, 986, -142, 989, -142, -138, -142,
+ /* 320 */ -142, 689, 935, 993, -142, -142, 936, 997, -142, -142,
+ /* 330 */ 995, -142, 963, -142, -142, 369, -142, -142, -142, -142,
+ /* 340 */ 999, 411, -142, -142, 415, -142, -142, -142, -142, -142,
+ /* 350 */ 998, 1002, -142, 1006, -142, -142, -142, 491, -142, 1007,
+ /* 360 */ -142, -142, 576, -142, 1009, -142, 833, -1, -142, -142,
+ /* 370 */ -142, 660, -142, -142, 1008, 1010, 1012, 229, -142, -142,
+ /* 380 */ -142, -142, -142, -142, 567, 866, 595, -142, 1021, 1016,
+ /* 390 */ -142, 1022, 1026, -142, 574, 866, 1023, 1027, 927, 958,
+ /* 400 */ -142, 401, 1030, -142, 937, 934, -142, 838, 241, -142,
+ /* 410 */ -142, -142, -142, -142, -142, -142, 826, -142, -142, -142,
+ /* 420 */ -142, -142, -142, -142, 1038, 1035, -142, 536, -142, 697,
+ /* 430 */ -142, 1024, -142, -142, -142, 608, 866, 1014, 829, -142,
+ /* 440 */ -142, -142, -142, -142, -142, 647, -142, 1025, 1054, -142,
+ /* 450 */ 717, 1018, 1057, -142, 853, 241, -142, -142, 854, 241,
+ /* 460 */ -142, 1064, 1028, 858, -142, -142, 613, 866, -142, 637,
+ /* 470 */ -142, 869, 241, -142, 241, -142, 1076, 1029, -142, -142,
+ /* 480 */ 700, -142, 871, -142, 872, -142, 873, -142, 866, -142,
+ /* 490 */ 874, 1036, -142, 1046, 879, -142, 878, 1039, -142, -142,
+ /* 500 */ 880, 1031, 855, 866, -142, 688, -142, -142, 1086, -142,
+ /* 510 */ 1088, 1091, -142, 569, -142, -142, -142, -142, 1099, -142,
+ /* 520 */ -142, 1032, 1090, -142, 1105, 1033, -142, 1093, -142, -142,
+ /* 530 */ -142, 1037, 1107, -142, -142, -142, -142,
+};
+static const YYACTIONTYPE yy_default[] = {
+ /* 0 */ 544, 544, 538, 829, 829, 540, 829, 549, 829, 829,
+ /* 10 */ 829, 829, 569, 570, 571, 550, 551, 552, 829, 829,
+ /* 20 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ /* 30 */ 829, 562, 572, 581, 564, 580, 829, 829, 582, 623,
+ /* 40 */ 588, 829, 829, 624, 627, 628, 629, 818, 819, 820,
+ /* 50 */ 829, 623, 589, 608, 606, 829, 609, 610, 829, 679,
+ /* 60 */ 623, 590, 677, 678, 623, 591, 829, 829, 708, 770,
+ /* 70 */ 714, 709, 829, 634, 829, 829, 635, 643, 645, 652,
+ /* 80 */ 691, 682, 684, 672, 686, 640, 793, 578, 579, 687,
+ /* 90 */ 793, 688, 829, 788, 790, 791, 829, 789, 792, 793,
+ /* 100 */ 689, 829, 829, 673, 829, 680, 679, 674, 829, 566,
+ /* 110 */ 681, 676, 829, 707, 829, 829, 710, 829, 711, 712,
+ /* 120 */ 713, 715, 716, 719, 829, 720, 829, 721, 829, 722,
+ /* 130 */ 829, 723, 829, 724, 829, 725, 829, 726, 829, 727,
+ /* 140 */ 829, 728, 829, 729, 829, 730, 829, 731, 829, 732,
+ /* 150 */ 829, 733, 829, 734, 829, 735, 829, 736, 737, 738,
+ /* 160 */ 829, 739, 740, 745, 753, 756, 829, 741, 742, 829,
+ /* 170 */ 743, 829, 746, 744, 752, 829, 829, 829, 754, 755,
+ /* 180 */ 829, 770, 829, 829, 829, 829, 829, 758, 769, 829,
+ /* 190 */ 747, 829, 748, 829, 749, 829, 750, 829, 829, 829,
+ /* 200 */ 829, 829, 760, 829, 829, 829, 761, 829, 829, 829,
+ /* 210 */ 816, 829, 829, 829, 817, 829, 829, 829, 829, 829,
+ /* 220 */ 762, 829, 757, 770, 767, 768, 660, 829, 661, 759,
+ /* 230 */ 680, 679, 675, 829, 685, 829, 770, 683, 829, 692,
+ /* 240 */ 644, 655, 653, 654, 663, 664, 829, 665, 829, 666,
+ /* 250 */ 829, 667, 829, 660, 651, 567, 568, 829, 649, 650,
+ /* 260 */ 669, 671, 656, 829, 829, 829, 670, 829, 704, 705,
+ /* 270 */ 829, 668, 655, 829, 829, 829, 651, 669, 671, 657,
+ /* 280 */ 829, 651, 646, 647, 829, 829, 648, 641, 642, 751,
+ /* 290 */ 829, 706, 829, 717, 829, 718, 829, 623, 592, 829,
+ /* 300 */ 774, 596, 593, 597, 829, 598, 829, 829, 599, 829,
+ /* 310 */ 602, 603, 604, 605, 829, 600, 829, 601, 829, 829,
+ /* 320 */ 775, 829, 680, 679, 776, 778, 680, 679, 777, 594,
+ /* 330 */ 829, 595, 608, 607, 583, 793, 584, 585, 586, 587,
+ /* 340 */ 573, 793, 829, 574, 793, 829, 575, 577, 576, 565,
+ /* 350 */ 829, 829, 613, 829, 616, 829, 829, 829, 829, 623,
+ /* 360 */ 617, 829, 829, 829, 623, 618, 829, 623, 619, 829,
+ /* 370 */ 829, 829, 829, 829, 829, 774, 596, 621, 829, 620,
+ /* 380 */ 622, 614, 615, 563, 829, 829, 559, 829, 829, 660,
+ /* 390 */ 557, 829, 829, 829, 829, 829, 829, 660, 799, 829,
+ /* 400 */ 829, 829, 660, 662, 804, 829, 829, 829, 829, 829,
+ /* 410 */ 829, 805, 806, 829, 829, 829, 829, 829, 796, 797,
+ /* 420 */ 829, 798, 558, 829, 829, 829, 829, 829, 829, 829,
+ /* 430 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 626,
+ /* 440 */ 829, 829, 829, 829, 829, 829, 829, 625, 829, 829,
+ /* 450 */ 829, 829, 829, 829, 829, 694, 829, 829, 829, 695,
+ /* 460 */ 829, 829, 702, 829, 829, 703, 829, 829, 829, 829,
+ /* 470 */ 829, 829, 700, 829, 701, 829, 829, 829, 829, 829,
+ /* 480 */ 829, 829, 829, 829, 829, 829, 829, 829, 829, 829,
+ /* 490 */ 829, 829, 829, 625, 829, 829, 829, 829, 829, 829,
+ /* 500 */ 829, 702, 829, 829, 829, 829, 829, 829, 829, 829,
+ /* 510 */ 829, 660, 829, 793, 829, 829, 829, 829, 829, 829,
+ /* 520 */ 829, 827, 829, 829, 829, 829, 829, 829, 829, 829,
+ /* 530 */ 826, 827, 829, 829, 541, 543, 539,
+};
+#define YY_SZ_ACTTAB (sizeof(yy_action)/sizeof(yy_action[0]))
+
+/* The next table maps tokens into fallback tokens. If a construct
+** like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammer, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+*/
+#ifdef YYFALLBACK
+static const YYCODETYPE yyFallback[] = {
+ 0, /* $ => nothing */
+ 0, /* END_OF_FILE => nothing */
+ 0, /* ILLEGAL => nothing */
+ 0, /* SPACE => nothing */
+ 0, /* UNCLOSED_STRING => nothing */
+ 0, /* COMMENT => nothing */
+ 0, /* FUNCTION => nothing */
+ 0, /* COLUMN => nothing */
+ 0, /* AGG_FUNCTION => nothing */
+ 0, /* SEMI => nothing */
+ 26, /* EXPLAIN => ID */
+ 26, /* BEGIN => ID */
+ 0, /* TRANSACTION => nothing */
+ 26, /* DEFERRED => ID */
+ 26, /* IMMEDIATE => ID */
+ 26, /* EXCLUSIVE => ID */
+ 0, /* COMMIT => nothing */
+ 26, /* END => ID */
+ 0, /* ROLLBACK => nothing */
+ 0, /* CREATE => nothing */
+ 0, /* TABLE => nothing */
+ 26, /* TEMP => ID */
+ 0, /* LP => nothing */
+ 0, /* RP => nothing */
+ 0, /* AS => nothing */
+ 0, /* COMMA => nothing */
+ 0, /* ID => nothing */
+ 26, /* ABORT => ID */
+ 26, /* AFTER => ID */
+ 26, /* ASC => ID */
+ 26, /* ATTACH => ID */
+ 26, /* BEFORE => ID */
+ 26, /* CASCADE => ID */
+ 26, /* CONFLICT => ID */
+ 26, /* DATABASE => ID */
+ 26, /* DESC => ID */
+ 26, /* DETACH => ID */
+ 26, /* EACH => ID */
+ 26, /* FAIL => ID */
+ 26, /* FOR => ID */
+ 26, /* GLOB => ID */
+ 26, /* IGNORE => ID */
+ 26, /* INITIALLY => ID */
+ 26, /* INSTEAD => ID */
+ 26, /* LIKE => ID */
+ 26, /* MATCH => ID */
+ 26, /* KEY => ID */
+ 26, /* OF => ID */
+ 26, /* OFFSET => ID */
+ 26, /* PRAGMA => ID */
+ 26, /* RAISE => ID */
+ 26, /* REPLACE => ID */
+ 26, /* RESTRICT => ID */
+ 26, /* ROW => ID */
+ 26, /* STATEMENT => ID */
+ 26, /* TRIGGER => ID */
+ 26, /* VACUUM => ID */
+ 26, /* VIEW => ID */
+ 0, /* OR => nothing */
+ 0, /* AND => nothing */
+ 0, /* NOT => nothing */
+ 0, /* IS => nothing */
+ 0, /* BETWEEN => nothing */
+ 0, /* IN => nothing */
+ 0, /* ISNULL => nothing */
+ 0, /* NOTNULL => nothing */
+ 0, /* NE => nothing */
+ 0, /* EQ => nothing */
+ 0, /* GT => nothing */
+ 0, /* LE => nothing */
+ 0, /* LT => nothing */
+ 0, /* GE => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* UMINUS => nothing */
+ 0, /* UPLUS => nothing */
+ 0, /* BITNOT => nothing */
+ 0, /* STRING => nothing */
+ 0, /* JOIN_KW => nothing */
+ 0, /* CONSTRAINT => nothing */
+ 0, /* DEFAULT => nothing */
+ 0, /* NULL => nothing */
+ 0, /* PRIMARY => nothing */
+ 0, /* UNIQUE => nothing */
+ 0, /* CHECK => nothing */
+ 0, /* REFERENCES => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* ON => nothing */
+ 0, /* DELETE => nothing */
+ 0, /* UPDATE => nothing */
+ 0, /* INSERT => nothing */
+ 0, /* SET => nothing */
+ 0, /* DEFERRABLE => nothing */
+ 0, /* FOREIGN => nothing */
+ 0, /* DROP => nothing */
+ 0, /* UNION => nothing */
+ 0, /* ALL => nothing */
+ 0, /* INTERSECT => nothing */
+ 0, /* EXCEPT => nothing */
+ 0, /* SELECT => nothing */
+ 0, /* DISTINCT => nothing */
+ 0, /* DOT => nothing */
+ 0, /* FROM => nothing */
+ 0, /* JOIN => nothing */
+ 0, /* USING => nothing */
+ 0, /* ORDER => nothing */
+ 0, /* BY => nothing */
+ 0, /* GROUP => nothing */
+ 0, /* HAVING => nothing */
+ 0, /* LIMIT => nothing */
+ 0, /* WHERE => nothing */
+ 0, /* INTO => nothing */
+ 0, /* VALUES => nothing */
+ 0, /* INTEGER => nothing */
+ 0, /* FLOAT => nothing */
+ 0, /* BLOB => nothing */
+ 0, /* VARIABLE => nothing */
+ 0, /* CASE => nothing */
+ 0, /* WHEN => nothing */
+ 0, /* THEN => nothing */
+ 0, /* ELSE => nothing */
+ 0, /* INDEX => nothing */
+};
+#endif /* YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+*/
+struct yyStackEntry {
+ int stateno; /* The state-number */
+ int major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct yyStackEntry yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct yyParser {
+ int yyidx; /* Index of top element in stack */
+ int yyerrcnt; /* Shifts left before out of the error */
+ sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
+ yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
+};
+typedef struct yyParser yyParser;
+
+#ifndef NDEBUG
+#include <stdio.h>
+static FILE *yyTraceFILE = 0;
+static char *yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void sqlite3ParserTrace(FILE *TraceFILE, char *zTracePrompt){
+ yyTraceFILE = TraceFILE;
+ yyTracePrompt = zTracePrompt;
+ if( yyTraceFILE==0 ) yyTracePrompt = 0;
+ else if( yyTracePrompt==0 ) yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const yyTokenName[] = {
+ "$", "END_OF_FILE", "ILLEGAL", "SPACE",
+ "UNCLOSED_STRING", "COMMENT", "FUNCTION", "COLUMN",
+ "AGG_FUNCTION", "SEMI", "EXPLAIN", "BEGIN",
+ "TRANSACTION", "DEFERRED", "IMMEDIATE", "EXCLUSIVE",
+ "COMMIT", "END", "ROLLBACK", "CREATE",
+ "TABLE", "TEMP", "LP", "RP",
+ "AS", "COMMA", "ID", "ABORT",
+ "AFTER", "ASC", "ATTACH", "BEFORE",
+ "CASCADE", "CONFLICT", "DATABASE", "DESC",
+ "DETACH", "EACH", "FAIL", "FOR",
+ "GLOB", "IGNORE", "INITIALLY", "INSTEAD",
+ "LIKE", "MATCH", "KEY", "OF",
+ "OFFSET", "PRAGMA", "RAISE", "REPLACE",
+ "RESTRICT", "ROW", "STATEMENT", "TRIGGER",
+ "VACUUM", "VIEW", "OR", "AND",
+ "NOT", "IS", "BETWEEN", "IN",
+ "ISNULL", "NOTNULL", "NE", "EQ",
+ "GT", "LE", "LT", "GE",
+ "BITAND", "BITOR", "LSHIFT", "RSHIFT",
+ "PLUS", "MINUS", "STAR", "SLASH",
+ "REM", "CONCAT", "UMINUS", "UPLUS",
+ "BITNOT", "STRING", "JOIN_KW", "CONSTRAINT",
+ "DEFAULT", "NULL", "PRIMARY", "UNIQUE",
+ "CHECK", "REFERENCES", "COLLATE", "ON",
+ "DELETE", "UPDATE", "INSERT", "SET",
+ "DEFERRABLE", "FOREIGN", "DROP", "UNION",
+ "ALL", "INTERSECT", "EXCEPT", "SELECT",
+ "DISTINCT", "DOT", "FROM", "JOIN",
+ "USING", "ORDER", "BY", "GROUP",
+ "HAVING", "LIMIT", "WHERE", "INTO",
+ "VALUES", "INTEGER", "FLOAT", "BLOB",
+ "VARIABLE", "CASE", "WHEN", "THEN",
+ "ELSE", "INDEX", "error", "input",
+ "cmdlist", "ecmd", "explain", "cmdx",
+ "cmd", "transtype", "trans_opt", "nm",
+ "create_table", "create_table_args", "temp", "dbnm",
+ "columnlist", "conslist_opt", "select", "column",
+ "columnid", "type", "carglist", "id",
+ "ids", "typename", "signed", "plus_num",
+ "minus_num", "carg", "ccons", "onconf",
+ "sortorder", "expr", "idxlist_opt", "refargs",
+ "defer_subclause", "refarg", "refact", "init_deferred_pred_opt",
+ "conslist", "tcons", "idxlist", "defer_subclause_opt",
+ "orconf", "resolvetype", "raisetype", "fullname",
+ "oneselect", "multiselect_op", "distinct", "selcollist",
+ "from", "where_opt", "groupby_opt", "having_opt",
+ "orderby_opt", "limit_opt", "sclp", "as",
+ "seltablist", "stl_prefix", "joinop", "on_opt",
+ "using_opt", "seltablist_paren", "joinop2", "inscollist",
+ "sortlist", "sortitem", "collate", "exprlist",
+ "setlist", "insert_cmd", "inscollist_opt", "itemlist",
+ "likeop", "between_op", "in_op", "case_operand",
+ "case_exprlist", "case_else", "expritem", "uniqueflag",
+ "idxitem", "plus_opt", "number", "trigger_decl",
+ "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause",
+ "when_clause", "trigger_cmd", "database_kw_opt", "key_opt",
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const yyRuleName[] = {
+ /* 0 */ "input ::= cmdlist",
+ /* 1 */ "cmdlist ::= cmdlist ecmd",
+ /* 2 */ "cmdlist ::= ecmd",
+ /* 3 */ "ecmd ::= explain cmdx SEMI",
+ /* 4 */ "ecmd ::= SEMI",
+ /* 5 */ "cmdx ::= cmd",
+ /* 6 */ "explain ::= EXPLAIN",
+ /* 7 */ "explain ::=",
+ /* 8 */ "cmd ::= BEGIN transtype trans_opt",
+ /* 9 */ "trans_opt ::=",
+ /* 10 */ "trans_opt ::= TRANSACTION",
+ /* 11 */ "trans_opt ::= TRANSACTION nm",
+ /* 12 */ "transtype ::=",
+ /* 13 */ "transtype ::= DEFERRED",
+ /* 14 */ "transtype ::= IMMEDIATE",
+ /* 15 */ "transtype ::= EXCLUSIVE",
+ /* 16 */ "cmd ::= COMMIT trans_opt",
+ /* 17 */ "cmd ::= END trans_opt",
+ /* 18 */ "cmd ::= ROLLBACK trans_opt",
+ /* 19 */ "cmd ::= create_table create_table_args",
+ /* 20 */ "create_table ::= CREATE temp TABLE nm dbnm",
+ /* 21 */ "temp ::= TEMP",
+ /* 22 */ "temp ::=",
+ /* 23 */ "create_table_args ::= LP columnlist conslist_opt RP",
+ /* 24 */ "create_table_args ::= AS select",
+ /* 25 */ "columnlist ::= columnlist COMMA column",
+ /* 26 */ "columnlist ::= column",
+ /* 27 */ "column ::= columnid type carglist",
+ /* 28 */ "columnid ::= nm",
+ /* 29 */ "id ::= ID",
+ /* 30 */ "ids ::= ID",
+ /* 31 */ "ids ::= STRING",
+ /* 32 */ "nm ::= ID",
+ /* 33 */ "nm ::= STRING",
+ /* 34 */ "nm ::= JOIN_KW",
+ /* 35 */ "type ::=",
+ /* 36 */ "type ::= typename",
+ /* 37 */ "type ::= typename LP signed RP",
+ /* 38 */ "type ::= typename LP signed COMMA signed RP",
+ /* 39 */ "typename ::= ids",
+ /* 40 */ "typename ::= typename ids",
+ /* 41 */ "signed ::= plus_num",
+ /* 42 */ "signed ::= minus_num",
+ /* 43 */ "carglist ::= carglist carg",
+ /* 44 */ "carglist ::=",
+ /* 45 */ "carg ::= CONSTRAINT nm ccons",
+ /* 46 */ "carg ::= ccons",
+ /* 47 */ "carg ::= DEFAULT ids",
+ /* 48 */ "carg ::= DEFAULT plus_num",
+ /* 49 */ "carg ::= DEFAULT minus_num",
+ /* 50 */ "carg ::= DEFAULT NULL",
+ /* 51 */ "ccons ::= NULL onconf",
+ /* 52 */ "ccons ::= NOT NULL onconf",
+ /* 53 */ "ccons ::= PRIMARY KEY sortorder onconf",
+ /* 54 */ "ccons ::= UNIQUE onconf",
+ /* 55 */ "ccons ::= CHECK LP expr RP onconf",
+ /* 56 */ "ccons ::= REFERENCES nm idxlist_opt refargs",
+ /* 57 */ "ccons ::= defer_subclause",
+ /* 58 */ "ccons ::= COLLATE id",
+ /* 59 */ "refargs ::=",
+ /* 60 */ "refargs ::= refargs refarg",
+ /* 61 */ "refarg ::= MATCH nm",
+ /* 62 */ "refarg ::= ON DELETE refact",
+ /* 63 */ "refarg ::= ON UPDATE refact",
+ /* 64 */ "refarg ::= ON INSERT refact",
+ /* 65 */ "refact ::= SET NULL",
+ /* 66 */ "refact ::= SET DEFAULT",
+ /* 67 */ "refact ::= CASCADE",
+ /* 68 */ "refact ::= RESTRICT",
+ /* 69 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
+ /* 70 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
+ /* 71 */ "init_deferred_pred_opt ::=",
+ /* 72 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
+ /* 73 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
+ /* 74 */ "conslist_opt ::=",
+ /* 75 */ "conslist_opt ::= COMMA conslist",
+ /* 76 */ "conslist ::= conslist COMMA tcons",
+ /* 77 */ "conslist ::= conslist tcons",
+ /* 78 */ "conslist ::= tcons",
+ /* 79 */ "tcons ::= CONSTRAINT nm",
+ /* 80 */ "tcons ::= PRIMARY KEY LP idxlist RP onconf",
+ /* 81 */ "tcons ::= UNIQUE LP idxlist RP onconf",
+ /* 82 */ "tcons ::= CHECK expr onconf",
+ /* 83 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt",
+ /* 84 */ "defer_subclause_opt ::=",
+ /* 85 */ "defer_subclause_opt ::= defer_subclause",
+ /* 86 */ "onconf ::=",
+ /* 87 */ "onconf ::= ON CONFLICT resolvetype",
+ /* 88 */ "orconf ::=",
+ /* 89 */ "orconf ::= OR resolvetype",
+ /* 90 */ "resolvetype ::= raisetype",
+ /* 91 */ "resolvetype ::= IGNORE",
+ /* 92 */ "resolvetype ::= REPLACE",
+ /* 93 */ "cmd ::= DROP TABLE fullname",
+ /* 94 */ "cmd ::= CREATE temp VIEW nm dbnm AS select",
+ /* 95 */ "cmd ::= DROP VIEW fullname",
+ /* 96 */ "cmd ::= select",
+ /* 97 */ "select ::= oneselect",
+ /* 98 */ "select ::= select multiselect_op oneselect",
+ /* 99 */ "multiselect_op ::= UNION",
+ /* 100 */ "multiselect_op ::= UNION ALL",
+ /* 101 */ "multiselect_op ::= INTERSECT",
+ /* 102 */ "multiselect_op ::= EXCEPT",
+ /* 103 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
+ /* 104 */ "distinct ::= DISTINCT",
+ /* 105 */ "distinct ::= ALL",
+ /* 106 */ "distinct ::=",
+ /* 107 */ "sclp ::= selcollist COMMA",
+ /* 108 */ "sclp ::=",
+ /* 109 */ "selcollist ::= sclp expr as",
+ /* 110 */ "selcollist ::= sclp STAR",
+ /* 111 */ "selcollist ::= sclp nm DOT STAR",
+ /* 112 */ "as ::= AS nm",
+ /* 113 */ "as ::= ids",
+ /* 114 */ "as ::=",
+ /* 115 */ "from ::=",
+ /* 116 */ "from ::= FROM seltablist",
+ /* 117 */ "stl_prefix ::= seltablist joinop",
+ /* 118 */ "stl_prefix ::=",
+ /* 119 */ "seltablist ::= stl_prefix nm dbnm as on_opt using_opt",
+ /* 120 */ "seltablist ::= stl_prefix LP seltablist_paren RP as on_opt using_opt",
+ /* 121 */ "seltablist_paren ::= select",
+ /* 122 */ "seltablist_paren ::= seltablist",
+ /* 123 */ "dbnm ::=",
+ /* 124 */ "dbnm ::= DOT nm",
+ /* 125 */ "fullname ::= nm dbnm",
+ /* 126 */ "joinop ::= COMMA",
+ /* 127 */ "joinop ::= JOIN",
+ /* 128 */ "joinop ::= JOIN_KW JOIN",
+ /* 129 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 130 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 131 */ "on_opt ::= ON expr",
+ /* 132 */ "on_opt ::=",
+ /* 133 */ "using_opt ::= USING LP inscollist RP",
+ /* 134 */ "using_opt ::=",
+ /* 135 */ "orderby_opt ::=",
+ /* 136 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 137 */ "sortlist ::= sortlist COMMA sortitem collate sortorder",
+ /* 138 */ "sortlist ::= sortitem collate sortorder",
+ /* 139 */ "sortitem ::= expr",
+ /* 140 */ "sortorder ::= ASC",
+ /* 141 */ "sortorder ::= DESC",
+ /* 142 */ "sortorder ::=",
+ /* 143 */ "collate ::=",
+ /* 144 */ "collate ::= COLLATE id",
+ /* 145 */ "groupby_opt ::=",
+ /* 146 */ "groupby_opt ::= GROUP BY exprlist",
+ /* 147 */ "having_opt ::=",
+ /* 148 */ "having_opt ::= HAVING expr",
+ /* 149 */ "limit_opt ::=",
+ /* 150 */ "limit_opt ::= LIMIT signed",
+ /* 151 */ "limit_opt ::= LIMIT signed OFFSET signed",
+ /* 152 */ "limit_opt ::= LIMIT signed COMMA signed",
+ /* 153 */ "cmd ::= DELETE FROM fullname where_opt",
+ /* 154 */ "where_opt ::=",
+ /* 155 */ "where_opt ::= WHERE expr",
+ /* 156 */ "cmd ::= UPDATE orconf fullname SET setlist where_opt",
+ /* 157 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 158 */ "setlist ::= nm EQ expr",
+ /* 159 */ "cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP",
+ /* 160 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select",
+ /* 161 */ "insert_cmd ::= INSERT orconf",
+ /* 162 */ "insert_cmd ::= REPLACE",
+ /* 163 */ "itemlist ::= itemlist COMMA expr",
+ /* 164 */ "itemlist ::= expr",
+ /* 165 */ "inscollist_opt ::=",
+ /* 166 */ "inscollist_opt ::= LP inscollist RP",
+ /* 167 */ "inscollist ::= inscollist COMMA nm",
+ /* 168 */ "inscollist ::= nm",
+ /* 169 */ "expr ::= LP expr RP",
+ /* 170 */ "expr ::= NULL",
+ /* 171 */ "expr ::= ID",
+ /* 172 */ "expr ::= JOIN_KW",
+ /* 173 */ "expr ::= nm DOT nm",
+ /* 174 */ "expr ::= nm DOT nm DOT nm",
+ /* 175 */ "expr ::= INTEGER",
+ /* 176 */ "expr ::= FLOAT",
+ /* 177 */ "expr ::= STRING",
+ /* 178 */ "expr ::= BLOB",
+ /* 179 */ "expr ::= VARIABLE",
+ /* 180 */ "expr ::= ID LP exprlist RP",
+ /* 181 */ "expr ::= ID LP STAR RP",
+ /* 182 */ "expr ::= expr AND expr",
+ /* 183 */ "expr ::= expr OR expr",
+ /* 184 */ "expr ::= expr LT expr",
+ /* 185 */ "expr ::= expr GT expr",
+ /* 186 */ "expr ::= expr LE expr",
+ /* 187 */ "expr ::= expr GE expr",
+ /* 188 */ "expr ::= expr NE expr",
+ /* 189 */ "expr ::= expr EQ expr",
+ /* 190 */ "expr ::= expr BITAND expr",
+ /* 191 */ "expr ::= expr BITOR expr",
+ /* 192 */ "expr ::= expr LSHIFT expr",
+ /* 193 */ "expr ::= expr RSHIFT expr",
+ /* 194 */ "expr ::= expr PLUS expr",
+ /* 195 */ "expr ::= expr MINUS expr",
+ /* 196 */ "expr ::= expr STAR expr",
+ /* 197 */ "expr ::= expr SLASH expr",
+ /* 198 */ "expr ::= expr REM expr",
+ /* 199 */ "expr ::= expr CONCAT expr",
+ /* 200 */ "likeop ::= LIKE",
+ /* 201 */ "likeop ::= GLOB",
+ /* 202 */ "likeop ::= NOT LIKE",
+ /* 203 */ "likeop ::= NOT GLOB",
+ /* 204 */ "expr ::= expr likeop expr",
+ /* 205 */ "expr ::= expr ISNULL",
+ /* 206 */ "expr ::= expr IS NULL",
+ /* 207 */ "expr ::= expr NOTNULL",
+ /* 208 */ "expr ::= expr NOT NULL",
+ /* 209 */ "expr ::= expr IS NOT NULL",
+ /* 210 */ "expr ::= NOT expr",
+ /* 211 */ "expr ::= BITNOT expr",
+ /* 212 */ "expr ::= MINUS expr",
+ /* 213 */ "expr ::= PLUS expr",
+ /* 214 */ "expr ::= LP select RP",
+ /* 215 */ "between_op ::= BETWEEN",
+ /* 216 */ "between_op ::= NOT BETWEEN",
+ /* 217 */ "expr ::= expr between_op expr AND expr",
+ /* 218 */ "in_op ::= IN",
+ /* 219 */ "in_op ::= NOT IN",
+ /* 220 */ "expr ::= expr in_op LP exprlist RP",
+ /* 221 */ "expr ::= expr in_op LP select RP",
+ /* 222 */ "expr ::= expr in_op nm dbnm",
+ /* 223 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 224 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 225 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 226 */ "case_else ::= ELSE expr",
+ /* 227 */ "case_else ::=",
+ /* 228 */ "case_operand ::= expr",
+ /* 229 */ "case_operand ::=",
+ /* 230 */ "exprlist ::= exprlist COMMA expritem",
+ /* 231 */ "exprlist ::= expritem",
+ /* 232 */ "expritem ::= expr",
+ /* 233 */ "expritem ::=",
+ /* 234 */ "cmd ::= CREATE uniqueflag INDEX nm dbnm ON fullname LP idxlist RP onconf",
+ /* 235 */ "uniqueflag ::= UNIQUE",
+ /* 236 */ "uniqueflag ::=",
+ /* 237 */ "idxlist_opt ::=",
+ /* 238 */ "idxlist_opt ::= LP idxlist RP",
+ /* 239 */ "idxlist ::= idxlist COMMA idxitem collate sortorder",
+ /* 240 */ "idxlist ::= idxitem collate sortorder",
+ /* 241 */ "idxitem ::= nm",
+ /* 242 */ "cmd ::= DROP INDEX fullname",
+ /* 243 */ "cmd ::= VACUUM",
+ /* 244 */ "cmd ::= VACUUM nm",
+ /* 245 */ "cmd ::= PRAGMA nm dbnm EQ nm",
+ /* 246 */ "cmd ::= PRAGMA nm dbnm EQ ON",
+ /* 247 */ "cmd ::= PRAGMA nm dbnm EQ plus_num",
+ /* 248 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 249 */ "cmd ::= PRAGMA nm dbnm LP nm RP",
+ /* 250 */ "cmd ::= PRAGMA nm dbnm",
+ /* 251 */ "plus_num ::= plus_opt number",
+ /* 252 */ "minus_num ::= MINUS number",
+ /* 253 */ "number ::= INTEGER",
+ /* 254 */ "number ::= FLOAT",
+ /* 255 */ "plus_opt ::= PLUS",
+ /* 256 */ "plus_opt ::=",
+ /* 257 */ "cmd ::= CREATE trigger_decl BEGIN trigger_cmd_list END",
+ /* 258 */ "trigger_decl ::= temp TRIGGER nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 259 */ "trigger_time ::= BEFORE",
+ /* 260 */ "trigger_time ::= AFTER",
+ /* 261 */ "trigger_time ::= INSTEAD OF",
+ /* 262 */ "trigger_time ::=",
+ /* 263 */ "trigger_event ::= DELETE",
+ /* 264 */ "trigger_event ::= INSERT",
+ /* 265 */ "trigger_event ::= UPDATE",
+ /* 266 */ "trigger_event ::= UPDATE OF inscollist",
+ /* 267 */ "foreach_clause ::=",
+ /* 268 */ "foreach_clause ::= FOR EACH ROW",
+ /* 269 */ "foreach_clause ::= FOR EACH STATEMENT",
+ /* 270 */ "when_clause ::=",
+ /* 271 */ "when_clause ::= WHEN expr",
+ /* 272 */ "trigger_cmd_list ::= trigger_cmd SEMI trigger_cmd_list",
+ /* 273 */ "trigger_cmd_list ::=",
+ /* 274 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt",
+ /* 275 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP",
+ /* 276 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select",
+ /* 277 */ "trigger_cmd ::= DELETE FROM nm where_opt",
+ /* 278 */ "trigger_cmd ::= select",
+ /* 279 */ "expr ::= RAISE LP IGNORE RP",
+ /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 281 */ "raisetype ::= ROLLBACK",
+ /* 282 */ "raisetype ::= ABORT",
+ /* 283 */ "raisetype ::= FAIL",
+ /* 284 */ "cmd ::= DROP TRIGGER fullname",
+ /* 285 */ "cmd ::= ATTACH database_kw_opt ids AS nm key_opt",
+ /* 286 */ "key_opt ::=",
+ /* 287 */ "key_opt ::= KEY ids",
+ /* 288 */ "key_opt ::= KEY BLOB",
+ /* 289 */ "database_kw_opt ::= DATABASE",
+ /* 290 */ "database_kw_opt ::=",
+ /* 291 */ "cmd ::= DETACH database_kw_opt nm",
+};
+#endif /* NDEBUG */
+
+/*
+** This function returns the symbolic name associated with a token
+** value.
+*/
+const char *sqlite3ParserTokenName(int tokenType){
+#ifndef NDEBUG
+ if( tokenType>0 && tokenType<(sizeof(yyTokenName)/sizeof(yyTokenName[0])) ){
+ return yyTokenName[tokenType];
+ }else{
+ return "Unknown";
+ }
+#else
+ return "";
+#endif
+}
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to sqlite3Parser and sqlite3ParserFree.
+*/
+void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){
+ yyParser *pParser;
+ pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ if( pParser ){
+ pParser->yyidx = -1;
+ }
+ return pParser;
+}
+
+/* The following function deletes the value associated with a
+** symbol. The symbol can be either a terminal or nonterminal.
+** "yymajor" is the symbol code, and "yypminor" is a pointer to
+** the value.
+*/
+static void yy_destructor(YYCODETYPE yymajor, YYMINORTYPE *yypminor){
+ switch( yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are not used
+ ** inside the C code.
+ */
+ case 146:
+ case 176:
+ case 193:
+#line 303 "parse.y"
+{sqlite3SelectDelete((yypminor->yy107));}
+#line 1236 "parse.c"
+ break;
+ case 161:
+ case 181:
+ case 183:
+ case 191:
+ case 197:
+ case 210:
+#line 552 "parse.y"
+{sqlite3ExprDelete((yypminor->yy258));}
+#line 1246 "parse.c"
+ break;
+ case 162:
+ case 170:
+ case 179:
+ case 182:
+ case 184:
+ case 186:
+ case 196:
+ case 199:
+ case 200:
+ case 203:
+ case 208:
+#line 744 "parse.y"
+{sqlite3ExprListDelete((yypminor->yy210));}
+#line 1261 "parse.c"
+ break;
+ case 175:
+ case 180:
+ case 188:
+ case 189:
+#line 428 "parse.y"
+{sqlite3SrcListDelete((yypminor->yy259));}
+#line 1269 "parse.c"
+ break;
+ case 192:
+ case 195:
+ case 202:
+#line 446 "parse.y"
+{sqlite3IdListDelete((yypminor->yy272));}
+#line 1276 "parse.c"
+ break;
+ case 216:
+ case 221:
+#line 833 "parse.y"
+{sqlite3DeleteTriggerStep((yypminor->yy91));}
+#line 1282 "parse.c"
+ break;
+ case 218:
+#line 817 "parse.y"
+{sqlite3IdListDelete((yypminor->yy146).b);}
+#line 1287 "parse.c"
+ break;
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+**
+** Return the major token number for the symbol popped.
+*/
+static int yy_pop_parser_stack(yyParser *pParser){
+ YYCODETYPE yymajor;
+ yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
+
+ if( pParser->yyidx<0 ) return 0;
+#ifndef NDEBUG
+ if( yyTraceFILE && pParser->yyidx>=0 ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ yymajor = yytos->major;
+ yy_destructor( yymajor, &yytos->minor);
+ pParser->yyidx--;
+ return yymajor;
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are all called for
+** all stack elements before shutting the parser down.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser. This should be a pointer
+** obtained from sqlite3ParserAlloc.
+** <li> A pointer to a function used to reclaim memory obtained
+** from malloc.
+** </ul>
+*/
+void sqlite3ParserFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ yyParser *pParser = (yyParser*)p;
+ if( pParser==0 ) return;
+ while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_shift_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ /* if( pParser->yyidx<0 ) return YY_NO_ACTION; */
+ i = yy_shift_ofst[stateno];
+ if( i==YY_SHIFT_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ if( iLookAhead==YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+#ifdef YYFALLBACK
+ int iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
+ && (iFallback = yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
+ yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
+ }
+#endif
+ return yy_find_shift_action(pParser, iFallback);
+ }
+#endif
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+**
+** If the look-ahead token is YYNOCODE, then check to see if the action is
+** independent of the look-ahead. If it is, return the action, otherwise
+** return YY_NO_ACTION.
+*/
+static int yy_find_reduce_action(
+ yyParser *pParser, /* The parser */
+ int iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->yystack[pParser->yyidx].stateno;
+
+ i = yy_reduce_ofst[stateno];
+ if( i==YY_REDUCE_USE_DFLT ){
+ return yy_default[stateno];
+ }
+ if( iLookAhead==YYNOCODE ){
+ return YY_NO_ACTION;
+ }
+ i += iLookAhead;
+ if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
+ }
+}
+
+/*
+** Perform a shift action.
+*/
+static void yy_shift(
+ yyParser *yypParser, /* The parser to be shifted */
+ int yyNewState, /* The new state to shift in */
+ int yyMajor, /* The major token to shift in */
+ YYMINORTYPE *yypMinor /* Pointer ot the minor token to shift in */
+){
+ yyStackEntry *yytos;
+ yypParser->yyidx++;
+ if( yypParser->yyidx>=YYSTACKDEPTH ){
+ sqlite3ParserARG_FETCH;
+ yypParser->yyidx--;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+ sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
+ return;
+ }
+ yytos = &yypParser->yystack[yypParser->yyidx];
+ yytos->stateno = yyNewState;
+ yytos->major = yyMajor;
+ yytos->minor = *yypMinor;
+#ifndef NDEBUG
+ if( yyTraceFILE && yypParser->yyidx>0 ){
+ int i;
+ fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
+ fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
+ for(i=1; i<=yypParser->yyidx; i++)
+ fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
+ fprintf(yyTraceFILE,"\n");
+ }
+#endif
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} yyRuleInfo[] = {
+ { 131, 1 },
+ { 132, 2 },
+ { 132, 1 },
+ { 133, 3 },
+ { 133, 1 },
+ { 135, 1 },
+ { 134, 1 },
+ { 134, 0 },
+ { 136, 3 },
+ { 138, 0 },
+ { 138, 1 },
+ { 138, 2 },
+ { 137, 0 },
+ { 137, 1 },
+ { 137, 1 },
+ { 137, 1 },
+ { 136, 2 },
+ { 136, 2 },
+ { 136, 2 },
+ { 136, 2 },
+ { 140, 5 },
+ { 142, 1 },
+ { 142, 0 },
+ { 141, 4 },
+ { 141, 2 },
+ { 144, 3 },
+ { 144, 1 },
+ { 147, 3 },
+ { 148, 1 },
+ { 151, 1 },
+ { 152, 1 },
+ { 152, 1 },
+ { 139, 1 },
+ { 139, 1 },
+ { 139, 1 },
+ { 149, 0 },
+ { 149, 1 },
+ { 149, 4 },
+ { 149, 6 },
+ { 153, 1 },
+ { 153, 2 },
+ { 154, 1 },
+ { 154, 1 },
+ { 150, 2 },
+ { 150, 0 },
+ { 157, 3 },
+ { 157, 1 },
+ { 157, 2 },
+ { 157, 2 },
+ { 157, 2 },
+ { 157, 2 },
+ { 158, 2 },
+ { 158, 3 },
+ { 158, 4 },
+ { 158, 2 },
+ { 158, 5 },
+ { 158, 4 },
+ { 158, 1 },
+ { 158, 2 },
+ { 163, 0 },
+ { 163, 2 },
+ { 165, 2 },
+ { 165, 3 },
+ { 165, 3 },
+ { 165, 3 },
+ { 166, 2 },
+ { 166, 2 },
+ { 166, 1 },
+ { 166, 1 },
+ { 164, 3 },
+ { 164, 2 },
+ { 167, 0 },
+ { 167, 2 },
+ { 167, 2 },
+ { 145, 0 },
+ { 145, 2 },
+ { 168, 3 },
+ { 168, 2 },
+ { 168, 1 },
+ { 169, 2 },
+ { 169, 6 },
+ { 169, 5 },
+ { 169, 3 },
+ { 169, 10 },
+ { 171, 0 },
+ { 171, 1 },
+ { 159, 0 },
+ { 159, 3 },
+ { 172, 0 },
+ { 172, 2 },
+ { 173, 1 },
+ { 173, 1 },
+ { 173, 1 },
+ { 136, 3 },
+ { 136, 7 },
+ { 136, 3 },
+ { 136, 1 },
+ { 146, 1 },
+ { 146, 3 },
+ { 177, 1 },
+ { 177, 2 },
+ { 177, 1 },
+ { 177, 1 },
+ { 176, 9 },
+ { 178, 1 },
+ { 178, 1 },
+ { 178, 0 },
+ { 186, 2 },
+ { 186, 0 },
+ { 179, 3 },
+ { 179, 2 },
+ { 179, 4 },
+ { 187, 2 },
+ { 187, 1 },
+ { 187, 0 },
+ { 180, 0 },
+ { 180, 2 },
+ { 189, 2 },
+ { 189, 0 },
+ { 188, 6 },
+ { 188, 7 },
+ { 193, 1 },
+ { 193, 1 },
+ { 143, 0 },
+ { 143, 2 },
+ { 175, 2 },
+ { 190, 1 },
+ { 190, 1 },
+ { 190, 2 },
+ { 190, 3 },
+ { 190, 4 },
+ { 191, 2 },
+ { 191, 0 },
+ { 192, 4 },
+ { 192, 0 },
+ { 184, 0 },
+ { 184, 3 },
+ { 196, 5 },
+ { 196, 3 },
+ { 197, 1 },
+ { 160, 1 },
+ { 160, 1 },
+ { 160, 0 },
+ { 198, 0 },
+ { 198, 2 },
+ { 182, 0 },
+ { 182, 3 },
+ { 183, 0 },
+ { 183, 2 },
+ { 185, 0 },
+ { 185, 2 },
+ { 185, 4 },
+ { 185, 4 },
+ { 136, 4 },
+ { 181, 0 },
+ { 181, 2 },
+ { 136, 6 },
+ { 200, 5 },
+ { 200, 3 },
+ { 136, 8 },
+ { 136, 5 },
+ { 201, 2 },
+ { 201, 1 },
+ { 203, 3 },
+ { 203, 1 },
+ { 202, 0 },
+ { 202, 3 },
+ { 195, 3 },
+ { 195, 1 },
+ { 161, 3 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 3 },
+ { 161, 5 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 1 },
+ { 161, 4 },
+ { 161, 4 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 161, 3 },
+ { 204, 1 },
+ { 204, 1 },
+ { 204, 2 },
+ { 204, 2 },
+ { 161, 3 },
+ { 161, 2 },
+ { 161, 3 },
+ { 161, 2 },
+ { 161, 3 },
+ { 161, 4 },
+ { 161, 2 },
+ { 161, 2 },
+ { 161, 2 },
+ { 161, 2 },
+ { 161, 3 },
+ { 205, 1 },
+ { 205, 2 },
+ { 161, 5 },
+ { 206, 1 },
+ { 206, 2 },
+ { 161, 5 },
+ { 161, 5 },
+ { 161, 4 },
+ { 161, 5 },
+ { 208, 5 },
+ { 208, 4 },
+ { 209, 2 },
+ { 209, 0 },
+ { 207, 1 },
+ { 207, 0 },
+ { 199, 3 },
+ { 199, 1 },
+ { 210, 1 },
+ { 210, 0 },
+ { 136, 11 },
+ { 211, 1 },
+ { 211, 0 },
+ { 162, 0 },
+ { 162, 3 },
+ { 170, 5 },
+ { 170, 3 },
+ { 212, 1 },
+ { 136, 3 },
+ { 136, 1 },
+ { 136, 2 },
+ { 136, 5 },
+ { 136, 5 },
+ { 136, 5 },
+ { 136, 5 },
+ { 136, 6 },
+ { 136, 3 },
+ { 155, 2 },
+ { 156, 2 },
+ { 214, 1 },
+ { 214, 1 },
+ { 213, 1 },
+ { 213, 0 },
+ { 136, 5 },
+ { 215, 10 },
+ { 217, 1 },
+ { 217, 1 },
+ { 217, 2 },
+ { 217, 0 },
+ { 218, 1 },
+ { 218, 1 },
+ { 218, 1 },
+ { 218, 3 },
+ { 219, 0 },
+ { 219, 3 },
+ { 219, 3 },
+ { 220, 0 },
+ { 220, 2 },
+ { 216, 3 },
+ { 216, 0 },
+ { 221, 6 },
+ { 221, 8 },
+ { 221, 5 },
+ { 221, 4 },
+ { 221, 1 },
+ { 161, 4 },
+ { 161, 6 },
+ { 174, 1 },
+ { 174, 1 },
+ { 174, 1 },
+ { 136, 3 },
+ { 136, 6 },
+ { 223, 0 },
+ { 223, 2 },
+ { 223, 2 },
+ { 222, 1 },
+ { 222, 0 },
+ { 136, 3 },
+};
+
+static void yy_accept(yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void yy_reduce(
+ yyParser *yypParser, /* The parser */
+ int yyruleno /* Number of the rule by which to reduce */
+){
+ int yygoto; /* The next state */
+ int yyact; /* The next action */
+ YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
+ yyStackEntry *yymsp; /* The top of the parser's stack */
+ int yysize; /* Amount to pop the stack */
+ sqlite3ParserARG_FETCH;
+ yymsp = &yypParser->yystack[yypParser->yyidx];
+#ifndef NDEBUG
+ if( yyTraceFILE && yyruleno>=0
+ && yyruleno<sizeof(yyRuleName)/sizeof(yyRuleName[0]) ){
+ fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
+ yyRuleName[yyruleno]);
+ }
+#endif /* NDEBUG */
+
+ switch( yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+ case 5:
+#line 86 "parse.y"
+{ sqlite3FinishCoding(pParse); }
+#line 1794 "parse.c"
+ break;
+ case 6:
+#line 87 "parse.y"
+{ sqlite3BeginParse(pParse, 1); }
+#line 1799 "parse.c"
+ break;
+ case 7:
+#line 88 "parse.y"
+{ sqlite3BeginParse(pParse, 0); }
+#line 1804 "parse.c"
+ break;
+ case 8:
+#line 93 "parse.y"
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy284);}
+#line 1809 "parse.c"
+ break;
+ case 12:
+#line 98 "parse.y"
+{yygotominor.yy284 = TK_DEFERRED;}
+#line 1814 "parse.c"
+ break;
+ case 13:
+ case 14:
+ case 15:
+ case 99:
+ case 101:
+ case 102:
+#line 99 "parse.y"
+{yygotominor.yy284 = yymsp[0].major;}
+#line 1824 "parse.c"
+ break;
+ case 16:
+ case 17:
+#line 102 "parse.y"
+{sqlite3CommitTransaction(pParse);}
+#line 1830 "parse.c"
+ break;
+ case 18:
+#line 104 "parse.y"
+{sqlite3RollbackTransaction(pParse);}
+#line 1835 "parse.c"
+ break;
+ case 20:
+#line 109 "parse.y"
+{
+ sqlite3StartTable(pParse,&yymsp[-4].minor.yy0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98,yymsp[-3].minor.yy284,0);
+}
+#line 1842 "parse.c"
+ break;
+ case 21:
+ case 72:
+ case 104:
+ case 216:
+ case 219:
+#line 113 "parse.y"
+{yygotominor.yy284 = 1;}
+#line 1851 "parse.c"
+ break;
+ case 22:
+ case 71:
+ case 73:
+ case 84:
+ case 105:
+ case 106:
+ case 215:
+ case 218:
+#line 114 "parse.y"
+{yygotominor.yy284 = 0;}
+#line 1863 "parse.c"
+ break;
+ case 23:
+#line 115 "parse.y"
+{
+ sqlite3EndTable(pParse,&yymsp[0].minor.yy0,0);
+}
+#line 1870 "parse.c"
+ break;
+ case 24:
+#line 118 "parse.y"
+{
+ sqlite3EndTable(pParse,0,yymsp[0].minor.yy107);
+ sqlite3SelectDelete(yymsp[0].minor.yy107);
+}
+#line 1878 "parse.c"
+ break;
+ case 28:
+#line 130 "parse.y"
+{sqlite3AddColumn(pParse,&yymsp[0].minor.yy98);}
+#line 1883 "parse.c"
+ break;
+ case 29:
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 253:
+ case 254:
+#line 136 "parse.y"
+{yygotominor.yy98 = yymsp[0].minor.yy0;}
+#line 1895 "parse.c"
+ break;
+ case 36:
+#line 185 "parse.y"
+{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy98,&yymsp[0].minor.yy98);}
+#line 1900 "parse.c"
+ break;
+ case 37:
+#line 186 "parse.y"
+{sqlite3AddColumnType(pParse,&yymsp[-3].minor.yy98,&yymsp[0].minor.yy0);}
+#line 1905 "parse.c"
+ break;
+ case 38:
+#line 188 "parse.y"
+{sqlite3AddColumnType(pParse,&yymsp[-5].minor.yy98,&yymsp[0].minor.yy0);}
+#line 1910 "parse.c"
+ break;
+ case 39:
+ case 112:
+ case 113:
+ case 124:
+ case 144:
+ case 241:
+ case 251:
+ case 252:
+#line 190 "parse.y"
+{yygotominor.yy98 = yymsp[0].minor.yy98;}
+#line 1922 "parse.c"
+ break;
+ case 40:
+#line 191 "parse.y"
+{yygotominor.yy98.z=yymsp[-1].minor.yy98.z; yygotominor.yy98.n=yymsp[0].minor.yy98.n+(yymsp[0].minor.yy98.z-yymsp[-1].minor.yy98.z);}
+#line 1927 "parse.c"
+ break;
+ case 41:
+#line 193 "parse.y"
+{ yygotominor.yy284 = atoi(yymsp[0].minor.yy98.z); }
+#line 1932 "parse.c"
+ break;
+ case 42:
+#line 194 "parse.y"
+{ yygotominor.yy284 = -atoi(yymsp[0].minor.yy98.z); }
+#line 1937 "parse.c"
+ break;
+ case 47:
+ case 48:
+#line 199 "parse.y"
+{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy98,0);}
+#line 1943 "parse.c"
+ break;
+ case 49:
+#line 201 "parse.y"
+{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy98,1);}
+#line 1948 "parse.c"
+ break;
+ case 52:
+#line 208 "parse.y"
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy284);}
+#line 1953 "parse.c"
+ break;
+ case 53:
+#line 209 "parse.y"
+{sqlite3AddPrimaryKey(pParse,0,yymsp[0].minor.yy284);}
+#line 1958 "parse.c"
+ break;
+ case 54:
+#line 210 "parse.y"
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy284,0,0);}
+#line 1963 "parse.c"
+ break;
+ case 56:
+#line 213 "parse.y"
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy98,yymsp[-1].minor.yy210,yymsp[0].minor.yy284);}
+#line 1968 "parse.c"
+ break;
+ case 57:
+#line 214 "parse.y"
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy284);}
+#line 1973 "parse.c"
+ break;
+ case 58:
+#line 215 "parse.y"
+{sqlite3AddCollateType(pParse, yymsp[0].minor.yy98.z, yymsp[0].minor.yy98.n);}
+#line 1978 "parse.c"
+ break;
+ case 59:
+#line 223 "parse.y"
+{ yygotominor.yy284 = OE_Restrict * 0x010101; }
+#line 1983 "parse.c"
+ break;
+ case 60:
+#line 224 "parse.y"
+{ yygotominor.yy284 = (yymsp[-1].minor.yy284 & yymsp[0].minor.yy47.mask) | yymsp[0].minor.yy47.value; }
+#line 1988 "parse.c"
+ break;
+ case 61:
+#line 226 "parse.y"
+{ yygotominor.yy47.value = 0; yygotominor.yy47.mask = 0x000000; }
+#line 1993 "parse.c"
+ break;
+ case 62:
+#line 227 "parse.y"
+{ yygotominor.yy47.value = yymsp[0].minor.yy284; yygotominor.yy47.mask = 0x0000ff; }
+#line 1998 "parse.c"
+ break;
+ case 63:
+#line 228 "parse.y"
+{ yygotominor.yy47.value = yymsp[0].minor.yy284<<8; yygotominor.yy47.mask = 0x00ff00; }
+#line 2003 "parse.c"
+ break;
+ case 64:
+#line 229 "parse.y"
+{ yygotominor.yy47.value = yymsp[0].minor.yy284<<16; yygotominor.yy47.mask = 0xff0000; }
+#line 2008 "parse.c"
+ break;
+ case 65:
+#line 231 "parse.y"
+{ yygotominor.yy284 = OE_SetNull; }
+#line 2013 "parse.c"
+ break;
+ case 66:
+#line 232 "parse.y"
+{ yygotominor.yy284 = OE_SetDflt; }
+#line 2018 "parse.c"
+ break;
+ case 67:
+#line 233 "parse.y"
+{ yygotominor.yy284 = OE_Cascade; }
+#line 2023 "parse.c"
+ break;
+ case 68:
+#line 234 "parse.y"
+{ yygotominor.yy284 = OE_Restrict; }
+#line 2028 "parse.c"
+ break;
+ case 69:
+ case 70:
+ case 85:
+ case 87:
+ case 89:
+ case 90:
+ case 161:
+#line 236 "parse.y"
+{yygotominor.yy284 = yymsp[0].minor.yy284;}
+#line 2039 "parse.c"
+ break;
+ case 80:
+#line 253 "parse.y"
+{sqlite3AddPrimaryKey(pParse,yymsp[-2].minor.yy210,yymsp[0].minor.yy284);}
+#line 2044 "parse.c"
+ break;
+ case 81:
+#line 255 "parse.y"
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy210,yymsp[0].minor.yy284,0,0);}
+#line 2049 "parse.c"
+ break;
+ case 83:
+#line 258 "parse.y"
+{
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy210, &yymsp[-3].minor.yy98, yymsp[-2].minor.yy210, yymsp[-1].minor.yy284);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy284);
+}
+#line 2057 "parse.c"
+ break;
+ case 86:
+ case 88:
+#line 272 "parse.y"
+{yygotominor.yy284 = OE_Default;}
+#line 2063 "parse.c"
+ break;
+ case 91:
+#line 277 "parse.y"
+{yygotominor.yy284 = OE_Ignore;}
+#line 2068 "parse.c"
+ break;
+ case 92:
+ case 162:
+#line 278 "parse.y"
+{yygotominor.yy284 = OE_Replace;}
+#line 2074 "parse.c"
+ break;
+ case 93:
+#line 282 "parse.y"
+{
+ sqlite3DropTable(pParse, yymsp[0].minor.yy259, 0);
+}
+#line 2081 "parse.c"
+ break;
+ case 94:
+#line 288 "parse.y"
+{
+ sqlite3CreateView(pParse, &yymsp[-6].minor.yy0, &yymsp[-3].minor.yy98, &yymsp[-2].minor.yy98, yymsp[0].minor.yy107, yymsp[-5].minor.yy284);
+}
+#line 2088 "parse.c"
+ break;
+ case 95:
+#line 291 "parse.y"
+{
+ sqlite3DropTable(pParse, yymsp[0].minor.yy259, 1);
+}
+#line 2095 "parse.c"
+ break;
+ case 96:
+#line 297 "parse.y"
+{
+ sqlite3Select(pParse, yymsp[0].minor.yy107, SRT_Callback, 0, 0, 0, 0, 0);
+ sqlite3SelectDelete(yymsp[0].minor.yy107);
+}
+#line 2103 "parse.c"
+ break;
+ case 97:
+ case 121:
+#line 307 "parse.y"
+{yygotominor.yy107 = yymsp[0].minor.yy107;}
+#line 2109 "parse.c"
+ break;
+ case 98:
+#line 308 "parse.y"
+{
+ if( yymsp[0].minor.yy107 ){
+ yymsp[0].minor.yy107->op = yymsp[-1].minor.yy284;
+ yymsp[0].minor.yy107->pPrior = yymsp[-2].minor.yy107;
+ }
+ yygotominor.yy107 = yymsp[0].minor.yy107;
+}
+#line 2120 "parse.c"
+ break;
+ case 100:
+#line 317 "parse.y"
+{yygotominor.yy284 = TK_ALL;}
+#line 2125 "parse.c"
+ break;
+ case 103:
+#line 321 "parse.y"
+{
+ yygotominor.yy107 = sqlite3SelectNew(yymsp[-6].minor.yy210,yymsp[-5].minor.yy259,yymsp[-4].minor.yy258,yymsp[-3].minor.yy210,yymsp[-2].minor.yy258,yymsp[-1].minor.yy210,yymsp[-7].minor.yy284,yymsp[0].minor.yy404.limit,yymsp[0].minor.yy404.offset);
+}
+#line 2132 "parse.c"
+ break;
+ case 107:
+ case 238:
+#line 342 "parse.y"
+{yygotominor.yy210 = yymsp[-1].minor.yy210;}
+#line 2138 "parse.c"
+ break;
+ case 108:
+ case 135:
+ case 145:
+ case 237:
+#line 343 "parse.y"
+{yygotominor.yy210 = 0;}
+#line 2146 "parse.c"
+ break;
+ case 109:
+#line 344 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-2].minor.yy210,yymsp[-1].minor.yy258,yymsp[0].minor.yy98.n?&yymsp[0].minor.yy98:0);
+}
+#line 2153 "parse.c"
+ break;
+ case 110:
+#line 347 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-1].minor.yy210, sqlite3Expr(TK_ALL, 0, 0, 0), 0);
+}
+#line 2160 "parse.c"
+ break;
+ case 111:
+#line 350 "parse.y"
+{
+ Expr *pRight = sqlite3Expr(TK_ALL, 0, 0, 0);
+ Expr *pLeft = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98);
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-3].minor.yy210, sqlite3Expr(TK_DOT, pLeft, pRight, 0), 0);
+}
+#line 2169 "parse.c"
+ break;
+ case 114:
+#line 362 "parse.y"
+{yygotominor.yy98.n = 0;}
+#line 2174 "parse.c"
+ break;
+ case 115:
+#line 374 "parse.y"
+{yygotominor.yy259 = sqliteMalloc(sizeof(*yygotominor.yy259));}
+#line 2179 "parse.c"
+ break;
+ case 116:
+#line 375 "parse.y"
+{yygotominor.yy259 = yymsp[0].minor.yy259;}
+#line 2184 "parse.c"
+ break;
+ case 117:
+#line 380 "parse.y"
+{
+ yygotominor.yy259 = yymsp[-1].minor.yy259;
+ if( yygotominor.yy259 && yygotominor.yy259->nSrc>0 ) yygotominor.yy259->a[yygotominor.yy259->nSrc-1].jointype = yymsp[0].minor.yy284;
+}
+#line 2192 "parse.c"
+ break;
+ case 118:
+#line 384 "parse.y"
+{yygotominor.yy259 = 0;}
+#line 2197 "parse.c"
+ break;
+ case 119:
+#line 385 "parse.y"
+{
+ yygotominor.yy259 = sqlite3SrcListAppend(yymsp[-5].minor.yy259,&yymsp[-4].minor.yy98,&yymsp[-3].minor.yy98);
+ if( yymsp[-2].minor.yy98.n ) sqlite3SrcListAddAlias(yygotominor.yy259,&yymsp[-2].minor.yy98);
+ if( yymsp[-1].minor.yy258 ){
+ if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pOn = yymsp[-1].minor.yy258; }
+ else { sqlite3ExprDelete(yymsp[-1].minor.yy258); }
+ }
+ if( yymsp[0].minor.yy272 ){
+ if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pUsing = yymsp[0].minor.yy272; }
+ else { sqlite3IdListDelete(yymsp[0].minor.yy272); }
+ }
+}
+#line 2213 "parse.c"
+ break;
+ case 120:
+#line 398 "parse.y"
+{
+ yygotominor.yy259 = sqlite3SrcListAppend(yymsp[-6].minor.yy259,0,0);
+ yygotominor.yy259->a[yygotominor.yy259->nSrc-1].pSelect = yymsp[-4].minor.yy107;
+ if( yymsp[-2].minor.yy98.n ) sqlite3SrcListAddAlias(yygotominor.yy259,&yymsp[-2].minor.yy98);
+ if( yymsp[-1].minor.yy258 ){
+ if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pOn = yymsp[-1].minor.yy258; }
+ else { sqlite3ExprDelete(yymsp[-1].minor.yy258); }
+ }
+ if( yymsp[0].minor.yy272 ){
+ if( yygotominor.yy259 && yygotominor.yy259->nSrc>1 ){ yygotominor.yy259->a[yygotominor.yy259->nSrc-2].pUsing = yymsp[0].minor.yy272; }
+ else { sqlite3IdListDelete(yymsp[0].minor.yy272); }
+ }
+}
+#line 2230 "parse.c"
+ break;
+ case 122:
+#line 419 "parse.y"
+{
+ yygotominor.yy107 = sqlite3SelectNew(0,yymsp[0].minor.yy259,0,0,0,0,0,-1,0);
+}
+#line 2237 "parse.c"
+ break;
+ case 123:
+#line 424 "parse.y"
+{yygotominor.yy98.z=0; yygotominor.yy98.n=0;}
+#line 2242 "parse.c"
+ break;
+ case 125:
+#line 429 "parse.y"
+{yygotominor.yy259 = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98);}
+#line 2247 "parse.c"
+ break;
+ case 126:
+ case 127:
+#line 433 "parse.y"
+{ yygotominor.yy284 = JT_INNER; }
+#line 2253 "parse.c"
+ break;
+ case 128:
+#line 435 "parse.y"
+{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
+#line 2258 "parse.c"
+ break;
+ case 129:
+#line 436 "parse.y"
+{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy98,0); }
+#line 2263 "parse.c"
+ break;
+ case 130:
+#line 438 "parse.y"
+{ yygotominor.yy284 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy98,&yymsp[-1].minor.yy98); }
+#line 2268 "parse.c"
+ break;
+ case 131:
+ case 139:
+ case 148:
+ case 155:
+ case 226:
+ case 228:
+ case 232:
+#line 442 "parse.y"
+{yygotominor.yy258 = yymsp[0].minor.yy258;}
+#line 2279 "parse.c"
+ break;
+ case 132:
+ case 147:
+ case 154:
+ case 227:
+ case 229:
+ case 233:
+#line 443 "parse.y"
+{yygotominor.yy258 = 0;}
+#line 2289 "parse.c"
+ break;
+ case 133:
+ case 166:
+#line 447 "parse.y"
+{yygotominor.yy272 = yymsp[-1].minor.yy272;}
+#line 2295 "parse.c"
+ break;
+ case 134:
+ case 165:
+#line 448 "parse.y"
+{yygotominor.yy272 = 0;}
+#line 2301 "parse.c"
+ break;
+ case 136:
+ case 146:
+#line 459 "parse.y"
+{yygotominor.yy210 = yymsp[0].minor.yy210;}
+#line 2307 "parse.c"
+ break;
+ case 137:
+#line 460 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210,yymsp[-2].minor.yy258,yymsp[-1].minor.yy98.n>0?&yymsp[-1].minor.yy98:0);
+ if( yygotominor.yy210 ) yygotominor.yy210->a[yygotominor.yy210->nExpr-1].sortOrder = yymsp[0].minor.yy284;
+}
+#line 2315 "parse.c"
+ break;
+ case 138:
+#line 464 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[-2].minor.yy258,yymsp[-1].minor.yy98.n>0?&yymsp[-1].minor.yy98:0);
+ if( yygotominor.yy210 && yygotominor.yy210->a ) yygotominor.yy210->a[0].sortOrder = yymsp[0].minor.yy284;
+}
+#line 2323 "parse.c"
+ break;
+ case 140:
+ case 142:
+#line 473 "parse.y"
+{yygotominor.yy284 = SQLITE_SO_ASC;}
+#line 2329 "parse.c"
+ break;
+ case 141:
+#line 474 "parse.y"
+{yygotominor.yy284 = SQLITE_SO_DESC;}
+#line 2334 "parse.c"
+ break;
+ case 143:
+#line 476 "parse.y"
+{yygotominor.yy98.z = 0; yygotominor.yy98.n = 0;}
+#line 2339 "parse.c"
+ break;
+ case 149:
+#line 490 "parse.y"
+{yygotominor.yy404.limit = -1; yygotominor.yy404.offset = 0;}
+#line 2344 "parse.c"
+ break;
+ case 150:
+#line 491 "parse.y"
+{yygotominor.yy404.limit = yymsp[0].minor.yy284; yygotominor.yy404.offset = 0;}
+#line 2349 "parse.c"
+ break;
+ case 151:
+#line 493 "parse.y"
+{yygotominor.yy404.limit = yymsp[-2].minor.yy284; yygotominor.yy404.offset = yymsp[0].minor.yy284;}
+#line 2354 "parse.c"
+ break;
+ case 152:
+#line 495 "parse.y"
+{yygotominor.yy404.limit = yymsp[0].minor.yy284; yygotominor.yy404.offset = yymsp[-2].minor.yy284;}
+#line 2359 "parse.c"
+ break;
+ case 153:
+#line 499 "parse.y"
+{sqlite3DeleteFrom(pParse,yymsp[-1].minor.yy259,yymsp[0].minor.yy258);}
+#line 2364 "parse.c"
+ break;
+ case 156:
+#line 513 "parse.y"
+{sqlite3Update(pParse,yymsp[-3].minor.yy259,yymsp[-1].minor.yy210,yymsp[0].minor.yy258,yymsp[-4].minor.yy284);}
+#line 2369 "parse.c"
+ break;
+ case 157:
+#line 516 "parse.y"
+{yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210,yymsp[0].minor.yy258,&yymsp[-2].minor.yy98);}
+#line 2374 "parse.c"
+ break;
+ case 158:
+#line 517 "parse.y"
+{yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[0].minor.yy258,&yymsp[-2].minor.yy98);}
+#line 2379 "parse.c"
+ break;
+ case 159:
+#line 523 "parse.y"
+{sqlite3Insert(pParse, yymsp[-5].minor.yy259, yymsp[-1].minor.yy210, 0, yymsp[-4].minor.yy272, yymsp[-7].minor.yy284);}
+#line 2384 "parse.c"
+ break;
+ case 160:
+#line 525 "parse.y"
+{sqlite3Insert(pParse, yymsp[-2].minor.yy259, 0, yymsp[0].minor.yy107, yymsp[-1].minor.yy272, yymsp[-4].minor.yy284);}
+#line 2389 "parse.c"
+ break;
+ case 163:
+ case 230:
+#line 535 "parse.y"
+{yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-2].minor.yy210,yymsp[0].minor.yy258,0);}
+#line 2395 "parse.c"
+ break;
+ case 164:
+ case 231:
+#line 536 "parse.y"
+{yygotominor.yy210 = sqlite3ExprListAppend(0,yymsp[0].minor.yy258,0);}
+#line 2401 "parse.c"
+ break;
+ case 167:
+#line 545 "parse.y"
+{yygotominor.yy272 = sqlite3IdListAppend(yymsp[-2].minor.yy272,&yymsp[0].minor.yy98);}
+#line 2406 "parse.c"
+ break;
+ case 168:
+#line 546 "parse.y"
+{yygotominor.yy272 = sqlite3IdListAppend(0,&yymsp[0].minor.yy98);}
+#line 2411 "parse.c"
+ break;
+ case 169:
+#line 554 "parse.y"
+{yygotominor.yy258 = yymsp[-1].minor.yy258; sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); }
+#line 2416 "parse.c"
+ break;
+ case 170:
+ case 175:
+ case 176:
+ case 177:
+ case 178:
+#line 555 "parse.y"
+{yygotominor.yy258 = sqlite3Expr(yymsp[0].major, 0, 0, &yymsp[0].minor.yy0);}
+#line 2425 "parse.c"
+ break;
+ case 171:
+ case 172:
+#line 556 "parse.y"
+{yygotominor.yy258 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy0);}
+#line 2431 "parse.c"
+ break;
+ case 173:
+#line 558 "parse.y"
+{
+ Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98);
+ Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy98);
+ yygotominor.yy258 = sqlite3Expr(TK_DOT, temp1, temp2, 0);
+}
+#line 2440 "parse.c"
+ break;
+ case 174:
+#line 563 "parse.y"
+{
+ Expr *temp1 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-4].minor.yy98);
+ Expr *temp2 = sqlite3Expr(TK_ID, 0, 0, &yymsp[-2].minor.yy98);
+ Expr *temp3 = sqlite3Expr(TK_ID, 0, 0, &yymsp[0].minor.yy98);
+ Expr *temp4 = sqlite3Expr(TK_DOT, temp2, temp3, 0);
+ yygotominor.yy258 = sqlite3Expr(TK_DOT, temp1, temp4, 0);
+}
+#line 2451 "parse.c"
+ break;
+ case 179:
+#line 574 "parse.y"
+{
+ Token *pToken = &yymsp[0].minor.yy0;
+ Expr *pExpr = yygotominor.yy258 = sqlite3Expr(TK_VARIABLE, 0, 0, pToken);
+ sqlite3ExprAssignVarNumber(pParse, pExpr);
+}
+#line 2460 "parse.c"
+ break;
+ case 180:
+#line 579 "parse.y"
+{
+ yygotominor.yy258 = sqlite3ExprFunction(yymsp[-1].minor.yy210, &yymsp[-3].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
+}
+#line 2468 "parse.c"
+ break;
+ case 181:
+#line 583 "parse.y"
+{
+ yygotominor.yy258 = sqlite3ExprFunction(0, &yymsp[-3].minor.yy0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
+}
+#line 2476 "parse.c"
+ break;
+ case 182:
+ case 183:
+ case 184:
+ case 185:
+ case 186:
+ case 187:
+ case 188:
+ case 189:
+ case 190:
+ case 191:
+ case 192:
+ case 193:
+ case 194:
+ case 195:
+ case 196:
+ case 197:
+ case 198:
+ case 199:
+#line 587 "parse.y"
+{yygotominor.yy258 = sqlite3Expr(yymsp[-1].major, yymsp[-2].minor.yy258, yymsp[0].minor.yy258, 0);}
+#line 2498 "parse.c"
+ break;
+ case 200:
+#line 606 "parse.y"
+{yygotominor.yy342.opcode = TK_LIKE; yygotominor.yy342.not = 0;}
+#line 2503 "parse.c"
+ break;
+ case 201:
+#line 607 "parse.y"
+{yygotominor.yy342.opcode = TK_GLOB; yygotominor.yy342.not = 0;}
+#line 2508 "parse.c"
+ break;
+ case 202:
+#line 608 "parse.y"
+{yygotominor.yy342.opcode = TK_LIKE; yygotominor.yy342.not = 1;}
+#line 2513 "parse.c"
+ break;
+ case 203:
+#line 609 "parse.y"
+{yygotominor.yy342.opcode = TK_GLOB; yygotominor.yy342.not = 1;}
+#line 2518 "parse.c"
+ break;
+ case 204:
+#line 610 "parse.y"
+{
+ ExprList *pList = sqlite3ExprListAppend(0, yymsp[0].minor.yy258, 0);
+ pList = sqlite3ExprListAppend(pList, yymsp[-2].minor.yy258, 0);
+ yygotominor.yy258 = sqlite3ExprFunction(pList, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->op = yymsp[-1].minor.yy342.opcode;
+ if( yymsp[-1].minor.yy342.not ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258, &yymsp[-2].minor.yy258->span, &yymsp[0].minor.yy258->span);
+}
+#line 2530 "parse.c"
+ break;
+ case 205:
+#line 618 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_ISNULL, yymsp[-1].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2538 "parse.c"
+ break;
+ case 206:
+#line 622 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_ISNULL, yymsp[-2].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2546 "parse.c"
+ break;
+ case 207:
+#line 626 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-1].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2554 "parse.c"
+ break;
+ case 208:
+#line 630 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-2].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2562 "parse.c"
+ break;
+ case 209:
+#line 634 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_NOTNULL, yymsp[-3].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2570 "parse.c"
+ break;
+ case 210:
+ case 211:
+#line 638 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(yymsp[-1].major, yymsp[0].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span);
+}
+#line 2579 "parse.c"
+ break;
+ case 212:
+#line 646 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_UMINUS, yymsp[0].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span);
+}
+#line 2587 "parse.c"
+ break;
+ case 213:
+#line 650 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_UPLUS, yymsp[0].minor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy258->span);
+}
+#line 2595 "parse.c"
+ break;
+ case 214:
+#line 654 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_SELECT, 0, 0, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pSelect = yymsp[-1].minor.yy107;
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+}
+#line 2604 "parse.c"
+ break;
+ case 217:
+#line 662 "parse.y"
+{
+ ExprList *pList = sqlite3ExprListAppend(0, yymsp[-2].minor.yy258, 0);
+ pList = sqlite3ExprListAppend(pList, yymsp[0].minor.yy258, 0);
+ yygotominor.yy258 = sqlite3Expr(TK_BETWEEN, yymsp[-4].minor.yy258, 0, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pList = pList;
+ if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy258->span);
+}
+#line 2616 "parse.c"
+ break;
+ case 220:
+#line 673 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy258, 0, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pList = yymsp[-1].minor.yy210;
+ if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2626 "parse.c"
+ break;
+ case 221:
+#line 679 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-4].minor.yy258, 0, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pSelect = yymsp[-1].minor.yy107;
+ if( yymsp[-3].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-4].minor.yy258->span,&yymsp[0].minor.yy0);
+}
+#line 2636 "parse.c"
+ break;
+ case 222:
+#line 685 "parse.y"
+{
+ SrcList *pSrc = sqlite3SrcListAppend(0,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98);
+ yygotominor.yy258 = sqlite3Expr(TK_IN, yymsp[-3].minor.yy258, 0, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pSelect = sqlite3SelectNew(0,pSrc,0,0,0,0,0,-1,0);
+ if( yymsp[-2].minor.yy284 ) yygotominor.yy258 = sqlite3Expr(TK_NOT, yygotominor.yy258, 0, 0);
+ sqlite3ExprSpan(yygotominor.yy258,&yymsp[-3].minor.yy258->span,yymsp[0].minor.yy98.z?&yymsp[0].minor.yy98:&yymsp[-1].minor.yy98);
+}
+#line 2647 "parse.c"
+ break;
+ case 223:
+#line 695 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_CASE, yymsp[-3].minor.yy258, yymsp[-1].minor.yy258, 0);
+ if( yygotominor.yy258 ) yygotominor.yy258->pList = yymsp[-2].minor.yy210;
+ sqlite3ExprSpan(yygotominor.yy258, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
+}
+#line 2656 "parse.c"
+ break;
+ case 224:
+#line 702 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210, yymsp[-2].minor.yy258, 0);
+ yygotominor.yy210 = sqlite3ExprListAppend(yygotominor.yy210, yymsp[0].minor.yy258, 0);
+}
+#line 2664 "parse.c"
+ break;
+ case 225:
+#line 706 "parse.y"
+{
+ yygotominor.yy210 = sqlite3ExprListAppend(0, yymsp[-2].minor.yy258, 0);
+ yygotominor.yy210 = sqlite3ExprListAppend(yygotominor.yy210, yymsp[0].minor.yy258, 0);
+}
+#line 2672 "parse.c"
+ break;
+ case 234:
+#line 731 "parse.y"
+{
+ if( yymsp[-9].minor.yy284!=OE_None ) yymsp[-9].minor.yy284 = yymsp[0].minor.yy284;
+ if( yymsp[-9].minor.yy284==OE_Default) yymsp[-9].minor.yy284 = OE_Abort;
+ sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy98, &yymsp[-6].minor.yy98, yymsp[-4].minor.yy259, yymsp[-2].minor.yy210, yymsp[-9].minor.yy284, &yymsp[-10].minor.yy0, &yymsp[-1].minor.yy0);
+}
+#line 2681 "parse.c"
+ break;
+ case 235:
+ case 282:
+#line 738 "parse.y"
+{yygotominor.yy284 = OE_Abort;}
+#line 2687 "parse.c"
+ break;
+ case 236:
+#line 739 "parse.y"
+{yygotominor.yy284 = OE_None;}
+#line 2692 "parse.c"
+ break;
+ case 239:
+#line 749 "parse.y"
+{
+ Expr *p = 0;
+ if( yymsp[-1].minor.yy98.n>0 ){
+ p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
+ if( p ) p->pColl = sqlite3LocateCollSeq(pParse, yymsp[-1].minor.yy98.z, yymsp[-1].minor.yy98.n);
+ }
+ yygotominor.yy210 = sqlite3ExprListAppend(yymsp[-4].minor.yy210, p, &yymsp[-2].minor.yy98);
+}
+#line 2704 "parse.c"
+ break;
+ case 240:
+#line 757 "parse.y"
+{
+ Expr *p = 0;
+ if( yymsp[-1].minor.yy98.n>0 ){
+ p = sqlite3Expr(TK_COLUMN, 0, 0, 0);
+ if( p ) p->pColl = sqlite3LocateCollSeq(pParse, yymsp[-1].minor.yy98.z, yymsp[-1].minor.yy98.n);
+ }
+ yygotominor.yy210 = sqlite3ExprListAppend(0, p, &yymsp[-2].minor.yy98);
+}
+#line 2716 "parse.c"
+ break;
+ case 242:
+#line 770 "parse.y"
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy259);}
+#line 2721 "parse.c"
+ break;
+ case 243:
+ case 244:
+#line 774 "parse.y"
+{sqlite3Vacuum(pParse,0);}
+#line 2727 "parse.c"
+ break;
+ case 245:
+ case 247:
+#line 779 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy98,0);}
+#line 2733 "parse.c"
+ break;
+ case 246:
+#line 780 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy0,0);}
+#line 2738 "parse.c"
+ break;
+ case 248:
+#line 782 "parse.y"
+{
+ sqlite3Pragma(pParse,&yymsp[-3].minor.yy98,&yymsp[-2].minor.yy98,&yymsp[0].minor.yy98,1);
+}
+#line 2745 "parse.c"
+ break;
+ case 249:
+#line 785 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-4].minor.yy98,&yymsp[-3].minor.yy98,&yymsp[-1].minor.yy98,0);}
+#line 2750 "parse.c"
+ break;
+ case 250:
+#line 786 "parse.y"
+{sqlite3Pragma(pParse,&yymsp[-1].minor.yy98,&yymsp[0].minor.yy98,0,0);}
+#line 2755 "parse.c"
+ break;
+ case 257:
+#line 796 "parse.y"
+{
+ Token all;
+ all.z = yymsp[-3].minor.yy98.z;
+ all.n = (yymsp[0].minor.yy0.z - yymsp[-3].minor.yy98.z) + yymsp[0].minor.yy0.n;
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy91, &all);
+}
+#line 2765 "parse.c"
+ break;
+ case 258:
+#line 805 "parse.y"
+{
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy98, &yymsp[-6].minor.yy98, yymsp[-5].minor.yy284, yymsp[-4].minor.yy146.a, yymsp[-4].minor.yy146.b, yymsp[-2].minor.yy259, yymsp[-1].minor.yy284, yymsp[0].minor.yy258, yymsp[-9].minor.yy284);
+ yygotominor.yy98 = (yymsp[-6].minor.yy98.n==0?yymsp[-7].minor.yy98:yymsp[-6].minor.yy98);
+}
+#line 2773 "parse.c"
+ break;
+ case 259:
+ case 262:
+#line 811 "parse.y"
+{ yygotominor.yy284 = TK_BEFORE; }
+#line 2779 "parse.c"
+ break;
+ case 260:
+#line 812 "parse.y"
+{ yygotominor.yy284 = TK_AFTER; }
+#line 2784 "parse.c"
+ break;
+ case 261:
+#line 813 "parse.y"
+{ yygotominor.yy284 = TK_INSTEAD;}
+#line 2789 "parse.c"
+ break;
+ case 263:
+ case 264:
+ case 265:
+#line 818 "parse.y"
+{yygotominor.yy146.a = yymsp[0].major; yygotominor.yy146.b = 0;}
+#line 2796 "parse.c"
+ break;
+ case 266:
+#line 821 "parse.y"
+{yygotominor.yy146.a = TK_UPDATE; yygotominor.yy146.b = yymsp[0].minor.yy272;}
+#line 2801 "parse.c"
+ break;
+ case 267:
+ case 268:
+#line 824 "parse.y"
+{ yygotominor.yy284 = TK_ROW; }
+#line 2807 "parse.c"
+ break;
+ case 269:
+#line 826 "parse.y"
+{ yygotominor.yy284 = TK_STATEMENT; }
+#line 2812 "parse.c"
+ break;
+ case 270:
+#line 829 "parse.y"
+{ yygotominor.yy258 = 0; }
+#line 2817 "parse.c"
+ break;
+ case 271:
+#line 830 "parse.y"
+{ yygotominor.yy258 = yymsp[0].minor.yy258; }
+#line 2822 "parse.c"
+ break;
+ case 272:
+#line 834 "parse.y"
+{
+ yymsp[-2].minor.yy91->pNext = yymsp[0].minor.yy91;
+ yygotominor.yy91 = yymsp[-2].minor.yy91;
+}
+#line 2830 "parse.c"
+ break;
+ case 273:
+#line 838 "parse.y"
+{ yygotominor.yy91 = 0; }
+#line 2835 "parse.c"
+ break;
+ case 274:
+#line 844 "parse.y"
+{ yygotominor.yy91 = sqlite3TriggerUpdateStep(&yymsp[-3].minor.yy98, yymsp[-1].minor.yy210, yymsp[0].minor.yy258, yymsp[-4].minor.yy284); }
+#line 2840 "parse.c"
+ break;
+ case 275:
+#line 849 "parse.y"
+{yygotominor.yy91 = sqlite3TriggerInsertStep(&yymsp[-5].minor.yy98, yymsp[-4].minor.yy272, yymsp[-1].minor.yy210, 0, yymsp[-7].minor.yy284);}
+#line 2845 "parse.c"
+ break;
+ case 276:
+#line 852 "parse.y"
+{yygotominor.yy91 = sqlite3TriggerInsertStep(&yymsp[-2].minor.yy98, yymsp[-1].minor.yy272, 0, yymsp[0].minor.yy107, yymsp[-4].minor.yy284);}
+#line 2850 "parse.c"
+ break;
+ case 277:
+#line 856 "parse.y"
+{yygotominor.yy91 = sqlite3TriggerDeleteStep(&yymsp[-1].minor.yy98, yymsp[0].minor.yy258);}
+#line 2855 "parse.c"
+ break;
+ case 278:
+#line 859 "parse.y"
+{yygotominor.yy91 = sqlite3TriggerSelectStep(yymsp[0].minor.yy107); }
+#line 2860 "parse.c"
+ break;
+ case 279:
+#line 862 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_RAISE, 0, 0, 0);
+ yygotominor.yy258->iColumn = OE_Ignore;
+ sqlite3ExprSpan(yygotominor.yy258, &yymsp[-3].minor.yy0, &yymsp[0].minor.yy0);
+}
+#line 2869 "parse.c"
+ break;
+ case 280:
+#line 867 "parse.y"
+{
+ yygotominor.yy258 = sqlite3Expr(TK_RAISE, 0, 0, &yymsp[-1].minor.yy98);
+ yygotominor.yy258->iColumn = yymsp[-3].minor.yy284;
+ sqlite3ExprSpan(yygotominor.yy258, &yymsp[-5].minor.yy0, &yymsp[0].minor.yy0);
+}
+#line 2878 "parse.c"
+ break;
+ case 281:
+#line 873 "parse.y"
+{yygotominor.yy284 = OE_Rollback;}
+#line 2883 "parse.c"
+ break;
+ case 283:
+#line 875 "parse.y"
+{yygotominor.yy284 = OE_Fail;}
+#line 2888 "parse.c"
+ break;
+ case 284:
+#line 879 "parse.y"
+{
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy259);
+}
+#line 2895 "parse.c"
+ break;
+ case 285:
+#line 884 "parse.y"
+{
+ sqlite3Attach(pParse, &yymsp[-3].minor.yy98, &yymsp[-1].minor.yy98, yymsp[0].minor.yy292.type, &yymsp[0].minor.yy292.key);
+}
+#line 2902 "parse.c"
+ break;
+ case 286:
+#line 888 "parse.y"
+{ yygotominor.yy292.type = 0; }
+#line 2907 "parse.c"
+ break;
+ case 287:
+#line 889 "parse.y"
+{ yygotominor.yy292.type=1; yygotominor.yy292.key = yymsp[0].minor.yy98; }
+#line 2912 "parse.c"
+ break;
+ case 288:
+#line 890 "parse.y"
+{ yygotominor.yy292.type=2; yygotominor.yy292.key = yymsp[0].minor.yy0; }
+#line 2917 "parse.c"
+ break;
+ case 291:
+#line 896 "parse.y"
+{
+ sqlite3Detach(pParse, &yymsp[0].minor.yy98);
+}
+#line 2924 "parse.c"
+ break;
+ };
+ yygoto = yyRuleInfo[yyruleno].lhs;
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ yypParser->yyidx -= yysize;
+ yyact = yy_find_reduce_action(yypParser,yygoto);
+ if( yyact < YYNSTATE ){
+ yy_shift(yypParser,yyact,yygoto,&yygotominor);
+ }else if( yyact == YYNSTATE + YYNRULE + 1 ){
+ yy_accept(yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+static void yy_parse_failed(
+ yyParser *yypParser /* The parser */
+){
+ sqlite3ParserARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+ sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void yy_syntax_error(
+ yyParser *yypParser, /* The parser */
+ int yymajor, /* The major type of the error token */
+ YYMINORTYPE yyminor /* The minor type of the error token */
+){
+ sqlite3ParserARG_FETCH;
+#define TOKEN (yyminor.yy0)
+#line 23 "parse.y"
+
+ if( pParse->zErrMsg==0 ){
+ if( TOKEN.z[0] ){
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+ }else{
+ sqlite3ErrorMsg(pParse, "incomplete SQL statement");
+ }
+ }
+#line 2976 "parse.c"
+ sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void yy_accept(
+ yyParser *yypParser /* The parser */
+){
+ sqlite3ParserARG_FETCH;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
+ }
+#endif
+ while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+ sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "sqlite3ParserAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+void sqlite3Parser(
+ void *yyp, /* The parser */
+ int yymajor, /* The major token code number */
+ sqlite3ParserTOKENTYPE yyminor /* The value for the token */
+ sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */
+){
+ YYMINORTYPE yyminorunion;
+ int yyact; /* The parser action. */
+ int yyendofinput; /* True if we are at the end of input */
+ int yyerrorhit = 0; /* True if yymajor has invoked an error */
+ yyParser *yypParser; /* The parser */
+
+ /* (re)initialize the parser, if necessary */
+ yypParser = (yyParser*)yyp;
+ if( yypParser->yyidx<0 ){
+ if( yymajor==0 ) return;
+ yypParser->yyidx = 0;
+ yypParser->yyerrcnt = -1;
+ yypParser->yystack[0].stateno = 0;
+ yypParser->yystack[0].major = 0;
+ }
+ yyminorunion.yy0 = yyminor;
+ yyendofinput = (yymajor==0);
+ sqlite3ParserARG_STORE;
+
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+
+ do{
+ yyact = yy_find_shift_action(yypParser,yymajor);
+ if( yyact<YYNSTATE ){
+ yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ yypParser->yyerrcnt--;
+ if( yyendofinput && yypParser->yyidx>=0 ){
+ yymajor = 0;
+ }else{
+ yymajor = YYNOCODE;
+ }
+ }else if( yyact < YYNSTATE + YYNRULE ){
+ yy_reduce(yypParser,yyact-YYNSTATE);
+ }else if( yyact == YY_ERROR_ACTION ){
+ int yymx;
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sSyntax Error!\n",yyTracePrompt);
+ }
+#endif
+#ifdef YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( yypParser->yyerrcnt<0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yymx = yypParser->yystack[yypParser->yyidx].major;
+ if( yymx==YYERRORSYMBOL || yyerrorhit ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sDiscard input token %s\n",
+ yyTracePrompt,yyTokenName[yymajor]);
+ }
+#endif
+ yy_destructor(yymajor,&yyminorunion);
+ yymajor = YYNOCODE;
+ }else{
+ while(
+ yypParser->yyidx >= 0 &&
+ yymx != YYERRORSYMBOL &&
+ (yyact = yy_find_shift_action(yypParser,YYERRORSYMBOL)) >= YYNSTATE
+ ){
+ yy_pop_parser_stack(yypParser);
+ }
+ if( yypParser->yyidx < 0 || yymajor==0 ){
+ yy_destructor(yymajor,&yyminorunion);
+ yy_parse_failed(yypParser);
+ yymajor = YYNOCODE;
+ }else if( yymx!=YYERRORSYMBOL ){
+ YYMINORTYPE u2;
+ u2.YYERRSYMDT = 0;
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ }
+ }
+ yypParser->yyerrcnt = 3;
+ yyerrorhit = 1;
+#else /* YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( yypParser->yyerrcnt<=0 ){
+ yy_syntax_error(yypParser,yymajor,yyminorunion);
+ }
+ yypParser->yyerrcnt = 3;
+ yy_destructor(yymajor,&yyminorunion);
+ if( yyendofinput ){
+ yy_parse_failed(yypParser);
+ }
+ yymajor = YYNOCODE;
+#endif
+ }else{
+ yy_accept(yypParser);
+ yymajor = YYNOCODE;
+ }
+ }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ return;
+}
diff --git a/kopete/plugins/statistics/sqlite/parse.h b/kopete/plugins/statistics/sqlite/parse.h
new file mode 100644
index 00000000..547319ed
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/parse.h
@@ -0,0 +1,129 @@
+#define TK_END_OF_FILE 1
+#define TK_ILLEGAL 2
+#define TK_SPACE 3
+#define TK_UNCLOSED_STRING 4
+#define TK_COMMENT 5
+#define TK_FUNCTION 6
+#define TK_COLUMN 7
+#define TK_AGG_FUNCTION 8
+#define TK_SEMI 9
+#define TK_EXPLAIN 10
+#define TK_BEGIN 11
+#define TK_TRANSACTION 12
+#define TK_DEFERRED 13
+#define TK_IMMEDIATE 14
+#define TK_EXCLUSIVE 15
+#define TK_COMMIT 16
+#define TK_END 17
+#define TK_ROLLBACK 18
+#define TK_CREATE 19
+#define TK_TABLE 20
+#define TK_TEMP 21
+#define TK_LP 22
+#define TK_RP 23
+#define TK_AS 24
+#define TK_COMMA 25
+#define TK_ID 26
+#define TK_ABORT 27
+#define TK_AFTER 28
+#define TK_ASC 29
+#define TK_ATTACH 30
+#define TK_BEFORE 31
+#define TK_CASCADE 32
+#define TK_CONFLICT 33
+#define TK_DATABASE 34
+#define TK_DESC 35
+#define TK_DETACH 36
+#define TK_EACH 37
+#define TK_FAIL 38
+#define TK_FOR 39
+#define TK_GLOB 40
+#define TK_IGNORE 41
+#define TK_INITIALLY 42
+#define TK_INSTEAD 43
+#define TK_LIKE 44
+#define TK_MATCH 45
+#define TK_KEY 46
+#define TK_OF 47
+#define TK_OFFSET 48
+#define TK_PRAGMA 49
+#define TK_RAISE 50
+#define TK_REPLACE 51
+#define TK_RESTRICT 52
+#define TK_ROW 53
+#define TK_STATEMENT 54
+#define TK_TRIGGER 55
+#define TK_VACUUM 56
+#define TK_VIEW 57
+#define TK_OR 58
+#define TK_AND 59
+#define TK_NOT 60
+#define TK_IS 61
+#define TK_BETWEEN 62
+#define TK_IN 63
+#define TK_ISNULL 64
+#define TK_NOTNULL 65
+#define TK_NE 66
+#define TK_EQ 67
+#define TK_GT 68
+#define TK_LE 69
+#define TK_LT 70
+#define TK_GE 71
+#define TK_BITAND 72
+#define TK_BITOR 73
+#define TK_LSHIFT 74
+#define TK_RSHIFT 75
+#define TK_PLUS 76
+#define TK_MINUS 77
+#define TK_STAR 78
+#define TK_SLASH 79
+#define TK_REM 80
+#define TK_CONCAT 81
+#define TK_UMINUS 82
+#define TK_UPLUS 83
+#define TK_BITNOT 84
+#define TK_STRING 85
+#define TK_JOIN_KW 86
+#define TK_CONSTRAINT 87
+#define TK_DEFAULT 88
+#define TK_NULL 89
+#define TK_PRIMARY 90
+#define TK_UNIQUE 91
+#define TK_CHECK 92
+#define TK_REFERENCES 93
+#define TK_COLLATE 94
+#define TK_ON 95
+#define TK_DELETE 96
+#define TK_UPDATE 97
+#define TK_INSERT 98
+#define TK_SET 99
+#define TK_DEFERRABLE 100
+#define TK_FOREIGN 101
+#define TK_DROP 102
+#define TK_UNION 103
+#define TK_ALL 104
+#define TK_INTERSECT 105
+#define TK_EXCEPT 106
+#define TK_SELECT 107
+#define TK_DISTINCT 108
+#define TK_DOT 109
+#define TK_FROM 110
+#define TK_JOIN 111
+#define TK_USING 112
+#define TK_ORDER 113
+#define TK_BY 114
+#define TK_GROUP 115
+#define TK_HAVING 116
+#define TK_LIMIT 117
+#define TK_WHERE 118
+#define TK_INTO 119
+#define TK_VALUES 120
+#define TK_INTEGER 121
+#define TK_FLOAT 122
+#define TK_BLOB 123
+#define TK_VARIABLE 124
+#define TK_CASE 125
+#define TK_WHEN 126
+#define TK_THEN 127
+#define TK_ELSE 128
+#define TK_INDEX 129
diff --git a/kopete/plugins/statistics/sqlite/pragma.c b/kopete/plugins/statistics/sqlite/pragma.c
new file mode 100644
index 00000000..94a21863
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/pragma.c
@@ -0,0 +1,754 @@
+/*
+** 2003 April 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used to implement the PRAGMA command.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include <ctype.h>
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+# include "pager.h"
+# include "btree.h"
+#endif
+
+/*
+** Interpret the given string as a boolean value.
+*/
+static int getBoolean(const u8 *z){
+ static const u8 *azTrue[] = { "yes", "on", "true" };
+ int i;
+ if( z[0]==0 ) return 0;
+ if( sqlite3IsNumber(z, 0, SQLITE_UTF8) ){
+ return atoi(z);
+ }
+ for(i=0; i<sizeof(azTrue)/sizeof(azTrue[0]); i++){
+ if( sqlite3StrICmp(z,azTrue[i])==0 ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Interpret the given string as a safety level. Return 0 for OFF,
+** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
+** unrecognized string argument.
+**
+** Note that the values returned are one less that the values that
+** should be passed into sqlite3BtreeSetSafetyLevel(). The is done
+** to support legacy SQL code. The safety level used to be boolean
+** and older scripts may have used numbers 0 for OFF and 1 for ON.
+*/
+static int getSafetyLevel(u8 *z){
+ static const struct {
+ const u8 *zWord;
+ int val;
+ } aKey[] = {
+ { "no", 0 },
+ { "off", 0 },
+ { "false", 0 },
+ { "yes", 1 },
+ { "on", 1 },
+ { "true", 1 },
+ { "full", 2 },
+ };
+ int i;
+ if( z[0]==0 ) return 1;
+ if( sqlite3IsNumber(z, 0, SQLITE_UTF8) ){
+ return atoi(z);
+ }
+ for(i=0; i<sizeof(aKey)/sizeof(aKey[0]); i++){
+ if( sqlite3StrICmp(z,aKey[i].zWord)==0 ) return aKey[i].val;
+ }
+ return 1;
+}
+
+/*
+** Interpret the given string as a temp db location. Return 1 for file
+** backed temporary databases, 2 for the Red-Black tree in memory database
+** and 0 to use the compile-time default.
+*/
+static int getTempStore(const char *z){
+ if( z[0]>='0' && z[0]<='2' ){
+ return z[0] - '0';
+ }else if( sqlite3StrICmp(z, "file")==0 ){
+ return 1;
+ }else if( sqlite3StrICmp(z, "memory")==0 ){
+ return 2;
+ }else{
+ return 0;
+ }
+}
+
+/*
+** If the TEMP database is open, close it and mark the database schema
+** as needing reloading. This must be done when using the TEMP_STORE
+** or DEFAULT_TEMP_STORE pragmas.
+*/
+static int changeTempStorage(Parse *pParse, const char *zStorageType){
+ int ts = getTempStore(zStorageType);
+ sqlite3 *db = pParse->db;
+ if( db->temp_store==ts ) return SQLITE_OK;
+ if( db->aDb[1].pBt!=0 ){
+ if( db->flags & SQLITE_InTrans ){
+ sqlite3ErrorMsg(pParse, "temporary storage cannot be changed "
+ "from within a transaction");
+ return SQLITE_ERROR;
+ }
+ sqlite3BtreeClose(db->aDb[1].pBt);
+ db->aDb[1].pBt = 0;
+ sqlite3ResetInternalSchema(db, 0);
+ }
+ db->temp_store = ts;
+ return SQLITE_OK;
+}
+
+/*
+** Generate code to return a single integer value.
+*/
+static void returnSingleInt(Parse *pParse, const char *zLabel, int value){
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ sqlite3VdbeAddOp(v, OP_Integer, value, 0);
+ if( pParse->explain==0 ){
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, zLabel, P3_STATIC);
+ }
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+}
+
+/*
+** Check to see if zRight and zLeft refer to a pragma that queries
+** or changes one of the flags in db->flags. Return 1 if so and 0 if not.
+** Also, implement the pragma.
+*/
+static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
+ static const struct {
+ const char *zName; /* Name of the pragma */
+ int mask; /* Mask for the db->flags value */
+ } aPragma[] = {
+ { "vdbe_trace", SQLITE_VdbeTrace },
+ { "sql_trace", SQLITE_SqlTrace },
+ { "vdbe_listing", SQLITE_VdbeListing },
+#if 1 /* FIX ME: Remove the following pragmas */
+ { "full_column_names", SQLITE_FullColNames },
+ { "short_column_names", SQLITE_ShortColNames },
+ { "count_changes", SQLITE_CountRows },
+ { "empty_result_callbacks", SQLITE_NullCallback },
+#endif
+ };
+ int i;
+ for(i=0; i<sizeof(aPragma)/sizeof(aPragma[0]); i++){
+ if( sqlite3StrICmp(zLeft, aPragma[i].zName)==0 ){
+ sqlite3 *db = pParse->db;
+ Vdbe *v;
+ if( zRight==0 ){
+ v = sqlite3GetVdbe(pParse);
+ if( v ){
+ returnSingleInt(pParse,
+ aPragma[i].zName, (db->flags&aPragma[i].mask)!=0);
+ }
+ }else if( getBoolean(zRight) ){
+ db->flags |= aPragma[i].mask;
+ }else{
+ db->flags &= ~aPragma[i].mask;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Process a pragma statement.
+**
+** Pragmas are of this form:
+**
+** PRAGMA [database.]id [= value]
+**
+** The identifier might also be a string. The value is a string, and
+** identifier, or a number. If minusFlag is true, then the value is
+** a number that was preceded by a minus sign.
+**
+** If the left side is "database.id" then pId1 is the database name
+** and pId2 is the id. If the left side is just "id" then pId1 is the
+** id and pId2 is any empty string.
+*/
+void sqlite3Pragma(
+ Parse *pParse,
+ Token *pId1, /* First part of [database.]id field */
+ Token *pId2, /* Second part of [database.]id field, or NULL */
+ Token *pValue, /* Token for <value>, or NULL */
+ int minusFlag /* True if a '-' sign preceded <value> */
+){
+ char *zLeft = 0; /* Nul-terminated UTF-8 string <id> */
+ char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */
+ const char *zDb = 0; /* The database name */
+ Token *pId; /* Pointer to <id> token */
+ int iDb; /* Database index for <database> */
+ sqlite3 *db = pParse->db;
+ Db *pDb;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return;
+
+ /* Interpret the [database.] part of the pragma statement. iDb is the
+ ** index of the database this pragma is being applied to in db.aDb[]. */
+ iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId);
+ if( iDb<0 ) return;
+ pDb = &db->aDb[iDb];
+
+ zLeft = sqlite3NameFromToken(pId);
+ if( !zLeft ) return;
+ if( minusFlag ){
+ zRight = sqlite3MPrintf("-%T", pValue);
+ }else{
+ zRight = sqlite3NameFromToken(pValue);
+ }
+
+ zDb = ((iDb>0)?pDb->zName:0);
+ if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
+ goto pragma_out;
+ }
+
+ /*
+ ** PRAGMA [database.]default_cache_size
+ ** PRAGMA [database.]default_cache_size=N
+ **
+ ** The first form reports the current persistent setting for the
+ ** page cache size. The value returned is the maximum number of
+ ** pages in the page cache. The second form sets both the current
+ ** page cache size value and the persistent page cache size value
+ ** stored in the database file.
+ **
+ ** The default cache size is stored in meta-value 2 of page 1 of the
+ ** database file. The cache size is actually the absolute value of
+ ** this memory location. The sign of meta-value 2 determines the
+ ** synchronous setting. A negative value means synchronous is off
+ ** and a positive value means synchronous is on.
+ */
+ if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){
+ static const VdbeOpList getCacheSize[] = {
+ { OP_ReadCookie, 0, 2, 0}, /* 0 */
+ { OP_AbsValue, 0, 0, 0},
+ { OP_Dup, 0, 0, 0},
+ { OP_Integer, 0, 0, 0},
+ { OP_Ne, 0, 6, 0},
+ { OP_Integer, 0, 0, 0}, /* 5 */
+ { OP_Callback, 1, 0, 0},
+ };
+ int addr;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ if( !zRight ){
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "cache_size", P3_STATIC);
+ addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
+ sqlite3VdbeChangeP1(v, addr, iDb);
+ sqlite3VdbeChangeP1(v, addr+5, MAX_PAGES);
+ }else{
+ int size = atoi(zRight);
+ if( size<0 ) size = -size;
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3VdbeAddOp(v, OP_Integer, size, 0);
+ sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 2);
+ addr = sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Ge, 0, addr+3);
+ sqlite3VdbeAddOp(v, OP_Negative, 0, 0);
+ sqlite3VdbeAddOp(v, OP_SetCookie, iDb, 2);
+ pDb->cache_size = size;
+ sqlite3BtreeSetCacheSize(pDb->pBt, pDb->cache_size);
+ }
+ }else
+
+ /*
+ ** PRAGMA [database.]page_size
+ ** PRAGMA [database.]page_size=N
+ **
+ ** The first form reports the current setting for the
+ ** database page size in bytes. The second form sets the
+ ** database page size value. The value can only be set if
+ ** the database has not yet been created.
+ */
+ if( sqlite3StrICmp(zLeft,"page_size")==0 ){
+ Btree *pBt = pDb->pBt;
+ if( !zRight ){
+ int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
+ returnSingleInt(pParse, "page_size", size);
+ }else{
+ sqlite3BtreeSetPageSize(pBt, atoi(zRight), sqlite3BtreeGetReserve(pBt));
+ }
+ }else
+
+ /*
+ ** PRAGMA [database.]cache_size
+ ** PRAGMA [database.]cache_size=N
+ **
+ ** The first form reports the current local setting for the
+ ** page cache size. The local setting can be different from
+ ** the persistent cache size value that is stored in the database
+ ** file itself. The value returned is the maximum number of
+ ** pages in the page cache. The second form sets the local
+ ** page cache size value. It does not change the persistent
+ ** cache size stored on the disk so the cache size will revert
+ ** to its default value when the database is closed and reopened.
+ ** N should be a positive integer.
+ */
+ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ if( !zRight ){
+ returnSingleInt(pParse, "cache_size", pDb->cache_size);
+ }else{
+ int size = atoi(zRight);
+ if( size<0 ) size = -size;
+ pDb->cache_size = size;
+ sqlite3BtreeSetCacheSize(pDb->pBt, pDb->cache_size);
+ }
+ }else
+
+ /*
+ ** PRAGMA temp_store
+ ** PRAGMA temp_store = "default"|"memory"|"file"
+ **
+ ** Return or set the local value of the temp_store flag. Changing
+ ** the local value does not make changes to the disk file and the default
+ ** value will be restored the next time the database is opened.
+ **
+ ** Note that it is possible for the library compile-time options to
+ ** override this setting
+ */
+ if( sqlite3StrICmp(zLeft, "temp_store")==0 ){
+ if( !zRight ){
+ returnSingleInt(pParse, "temp_store", db->temp_store);
+ }else{
+ changeTempStorage(pParse, zRight);
+ }
+ }else
+
+ /*
+ ** PRAGMA [database.]synchronous
+ ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL
+ **
+ ** Return or set the local value of the synchronous flag. Changing
+ ** the local value does not make changes to the disk file and the
+ ** default value will be restored the next time the database is
+ ** opened.
+ */
+ if( sqlite3StrICmp(zLeft,"synchronous")==0 ){
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ if( !zRight ){
+ returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
+ }else{
+ if( !db->autoCommit ){
+ sqlite3ErrorMsg(pParse,
+ "Safety level may not be changed inside a transaction");
+ }else{
+ pDb->safety_level = getSafetyLevel(zRight)+1;
+ sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level);
+ }
+ }
+ }else
+
+#if 0 /* Used once during development. No longer needed */
+ if( sqlite3StrICmp(zLeft, "trigger_overhead_test")==0 ){
+ if( getBoolean(zRight) ){
+ sqlite3_always_code_trigger_setup = 1;
+ }else{
+ sqlite3_always_code_trigger_setup = 0;
+ }
+ }else
+#endif
+
+ if( flagPragma(pParse, zLeft, zRight) ){
+ /* The flagPragma() subroutine also generates any necessary code
+ ** there is nothing more to do here */
+ }else
+
+ /*
+ ** PRAGMA table_info(<table>)
+ **
+ ** Return a single row for each column of the named table. The columns of
+ ** the returned data set are:
+ **
+ ** cid: Column id (numbered from left to right, starting at 0)
+ ** name: Column name
+ ** type: Column declaration type.
+ ** notnull: True if 'NOT NULL' is part of column declaration
+ ** dflt_value: The default value for the column, if any.
+ */
+ if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
+ Table *pTab;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ pTab = sqlite3FindTable(db, zRight, zDb);
+ if( pTab ){
+ int i;
+ sqlite3VdbeSetNumCols(v, 6);
+ sqlite3VdbeSetColName(v, 0, "cid", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "name", P3_STATIC);
+ sqlite3VdbeSetColName(v, 2, "type", P3_STATIC);
+ sqlite3VdbeSetColName(v, 3, "notnull", P3_STATIC);
+ sqlite3VdbeSetColName(v, 4, "dflt_value", P3_STATIC);
+ sqlite3VdbeSetColName(v, 5, "pk", P3_STATIC);
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ for(i=0; i<pTab->nCol; i++){
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[i].zName, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ pTab->aCol[i].zType ? pTab->aCol[i].zType : "numeric", 0);
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->aCol[i].notNull, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ pTab->aCol[i].zDflt, P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->aCol[i].isPrimKey, 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 6, 0);
+ }
+ }
+ }else
+
+ if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){
+ Index *pIdx;
+ Table *pTab;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ pIdx = sqlite3FindIndex(db, zRight, zDb);
+ if( pIdx ){
+ int i;
+ pTab = pIdx->pTable;
+ sqlite3VdbeSetNumCols(v, 3);
+ sqlite3VdbeSetColName(v, 0, "seqno", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "cid", P3_STATIC);
+ sqlite3VdbeSetColName(v, 2, "name", P3_STATIC);
+ for(i=0; i<pIdx->nColumn; i++){
+ int cnum = pIdx->aiColumn[i];
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, cnum, 0);
+ assert( pTab->nCol>cnum );
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pTab->aCol[cnum].zName, 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ }
+ }
+ }else
+
+ if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){
+ Index *pIdx;
+ Table *pTab;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ pTab = sqlite3FindTable(db, zRight, zDb);
+ if( pTab ){
+ v = sqlite3GetVdbe(pParse);
+ pIdx = pTab->pIndex;
+ if( pIdx ){
+ int i = 0;
+ sqlite3VdbeSetNumCols(v, 3);
+ sqlite3VdbeSetColName(v, 0, "seq", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "name", P3_STATIC);
+ sqlite3VdbeSetColName(v, 2, "unique", P3_STATIC);
+ while(pIdx){
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pIdx->zName, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, pIdx->onError!=OE_None, 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ ++i;
+ pIdx = pIdx->pNext;
+ }
+ }
+ }
+ }else
+
+ if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){
+ FKey *pFK;
+ Table *pTab;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ pTab = sqlite3FindTable(db, zRight, zDb);
+ if( pTab ){
+ v = sqlite3GetVdbe(pParse);
+ pFK = pTab->pFKey;
+ if( pFK ){
+ int i = 0;
+ sqlite3VdbeSetNumCols(v, 5);
+ sqlite3VdbeSetColName(v, 0, "id", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "seq", P3_STATIC);
+ sqlite3VdbeSetColName(v, 2, "table", P3_STATIC);
+ sqlite3VdbeSetColName(v, 3, "from", P3_STATIC);
+ sqlite3VdbeSetColName(v, 4, "to", P3_STATIC);
+ while(pFK){
+ int j;
+ for(j=0; j<pFK->nCol; j++){
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ sqlite3VdbeAddOp(v, OP_Integer, j, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pFK->zTo, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, pFK->aCol[j].zCol, 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 5, 0);
+ }
+ ++i;
+ pFK = pFK->pNextFrom;
+ }
+ }
+ }
+ }else
+
+ if( sqlite3StrICmp(zLeft, "database_list")==0 ){
+ int i;
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ sqlite3VdbeSetNumCols(v, 3);
+ sqlite3VdbeSetColName(v, 0, "seq", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "name", P3_STATIC);
+ sqlite3VdbeSetColName(v, 2, "file", P3_STATIC);
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt==0 ) continue;
+ assert( db->aDb[i].zName!=0 );
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0, db->aDb[i].zName, 0);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
+ sqlite3VdbeAddOp(v, OP_Callback, 3, 0);
+ }
+ }else
+
+#ifndef NDEBUG
+ if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
+ extern void sqlite3ParserTrace(FILE*, char *);
+ if( getBoolean(zRight) ){
+ sqlite3ParserTrace(stdout, "parser: ");
+ }else{
+ sqlite3ParserTrace(0, 0);
+ }
+ }else
+#endif
+
+ if( sqlite3StrICmp(zLeft, "integrity_check")==0 ){
+ int i, j, addr;
+
+ /* Code that initializes the integrity check program. Set the
+ ** error count 0
+ */
+ static const VdbeOpList initCode[] = {
+ { OP_Integer, 0, 0, 0},
+ { OP_MemStore, 0, 1, 0},
+ };
+
+ /* Code that appears at the end of the integrity check. If no error
+ ** messages have been generated, output OK. Otherwise output the
+ ** error message
+ */
+ static const VdbeOpList endCode[] = {
+ { OP_MemLoad, 0, 0, 0},
+ { OP_Integer, 0, 0, 0},
+ { OP_Ne, 0, 0, 0}, /* 2 */
+ { OP_String8, 0, 0, "ok"},
+ { OP_Callback, 1, 0, 0},
+ };
+
+ /* Initialize the VDBE program */
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "integrity_check", P3_STATIC);
+ sqlite3VdbeAddOpList(v, ArraySize(initCode), initCode);
+
+ /* Do an integrity check on each database file */
+ for(i=0; i<db->nDb; i++){
+ HashElem *x;
+ int cnt = 0;
+
+ sqlite3CodeVerifySchema(pParse, i);
+
+ /* Do an integrity check of the B-Tree
+ */
+ for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->tnum, 0);
+ cnt++;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto pragma_out;
+ sqlite3VdbeAddOp(v, OP_Integer, pIdx->tnum, 0);
+ cnt++;
+ }
+ }
+ assert( cnt>0 );
+ sqlite3VdbeAddOp(v, OP_IntegrityCk, cnt, i);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 1);
+ addr = sqlite3VdbeOp3(v, OP_String8, 0, 0, "ok", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_Eq, 0, addr+6);
+ sqlite3VdbeOp3(v, OP_String8, 0, 0,
+ sqlite3MPrintf("*** in database %s ***\n", db->aDb[i].zName),
+ P3_DYNAMIC);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Concat, 0, 1);
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+
+ /* Make sure all the indices are constructed correctly.
+ */
+ sqlite3CodeVerifySchema(pParse, i);
+ for(x=sqliteHashFirst(&db->aDb[i].tblHash); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ int loopTop;
+
+ if( pTab->pIndex==0 ) continue;
+ sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, 1, 1);
+ loopTop = sqlite3VdbeAddOp(v, OP_Rewind, 1, 0);
+ sqlite3VdbeAddOp(v, OP_MemIncr, 1, 0);
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ int jmp2;
+ static const VdbeOpList idxErr[] = {
+ { OP_MemIncr, 0, 0, 0},
+ { OP_String8, 0, 0, "rowid "},
+ { OP_Recno, 1, 0, 0},
+ { OP_String8, 0, 0, " missing from index "},
+ { OP_String8, 0, 0, 0}, /* 4 */
+ { OP_Concat, 2, 0, 0},
+ { OP_Callback, 1, 0, 0},
+ };
+ sqlite3GenerateIndexKey(v, pIdx, 1);
+ jmp2 = sqlite3VdbeAddOp(v, OP_Found, j+2, 0);
+ addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
+ sqlite3VdbeChangeP3(v, addr+4, pIdx->zName, P3_STATIC);
+ sqlite3VdbeChangeP2(v, jmp2, sqlite3VdbeCurrentAddr(v));
+ }
+ sqlite3VdbeAddOp(v, OP_Next, 1, loopTop+1);
+ sqlite3VdbeChangeP2(v, loopTop, sqlite3VdbeCurrentAddr(v));
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ static const VdbeOpList cntIdx[] = {
+ { OP_Integer, 0, 0, 0},
+ { OP_MemStore, 2, 1, 0},
+ { OP_Rewind, 0, 0, 0}, /* 2 */
+ { OP_MemIncr, 2, 0, 0},
+ { OP_Next, 0, 0, 0}, /* 4 */
+ { OP_MemLoad, 1, 0, 0},
+ { OP_MemLoad, 2, 0, 0},
+ { OP_Eq, 0, 0, 0}, /* 7 */
+ { OP_MemIncr, 0, 0, 0},
+ { OP_String8, 0, 0, "wrong # of entries in index "},
+ { OP_String8, 0, 0, 0}, /* 10 */
+ { OP_Concat, 0, 0, 0},
+ { OP_Callback, 1, 0, 0},
+ };
+ if( pIdx->tnum==0 ) continue;
+ addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
+ sqlite3VdbeChangeP1(v, addr+2, j+2);
+ sqlite3VdbeChangeP2(v, addr+2, addr+5);
+ sqlite3VdbeChangeP1(v, addr+4, j+2);
+ sqlite3VdbeChangeP2(v, addr+4, addr+3);
+ sqlite3VdbeChangeP2(v, addr+7, addr+ArraySize(cntIdx));
+ sqlite3VdbeChangeP3(v, addr+10, pIdx->zName, P3_STATIC);
+ }
+ }
+ }
+ addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
+ sqlite3VdbeChangeP2(v, addr+2, addr+ArraySize(endCode));
+ }else
+ /*
+ ** PRAGMA encoding
+ ** PRAGMA encoding = "utf-8"|"utf-16"|"utf-16le"|"utf-16be"
+ **
+ ** In it's first form, this pragma returns the encoding of the main
+ ** database. If the database is not initialized, it is initialized now.
+ **
+ ** The second form of this pragma is a no-op if the main database file
+ ** has not already been initialized. In this case it sets the default
+ ** encoding that will be used for the main database file if a new file
+ ** is created. If an existing main database file is opened, then the
+ ** default text encoding for the existing database is used.
+ **
+ ** In all cases new databases created using the ATTACH command are
+ ** created to use the same default text encoding as the main database. If
+ ** the main database has not been initialized and/or created when ATTACH
+ ** is executed, this is done before the ATTACH operation.
+ **
+ ** In the second form this pragma sets the text encoding to be used in
+ ** new database files created using this database handle. It is only
+ ** useful if invoked immediately after the main database i
+ */
+ if( sqlite3StrICmp(zLeft, "encoding")==0 ){
+ static struct EncName {
+ char *zName;
+ u8 enc;
+ } encnames[] = {
+ { "UTF-8", SQLITE_UTF8 },
+ { "UTF8", SQLITE_UTF8 },
+ { "UTF-16le", SQLITE_UTF16LE },
+ { "UTF16le", SQLITE_UTF16LE },
+ { "UTF-16be", SQLITE_UTF16BE },
+ { "UTF16be", SQLITE_UTF16BE },
+ { "UTF-16", 0 /* Filled in at run-time */ },
+ { "UTF16", 0 /* Filled in at run-time */ },
+ { 0, 0 }
+ };
+ struct EncName *pEnc;
+ encnames[6].enc = encnames[7].enc = SQLITE_UTF16NATIVE;
+ if( !zRight ){ /* "PRAGMA encoding" */
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "encoding", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
+ if( pEnc->enc==pParse->db->enc ){
+ sqlite3VdbeChangeP3(v, -1, pEnc->zName, P3_STATIC);
+ break;
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ }else{ /* "PRAGMA encoding = XXX" */
+ /* Only change the value of sqlite.enc if the database handle is not
+ ** initialized. If the main database exists, the new sqlite.enc value
+ ** will be overwritten when the schema is next loaded. If it does not
+ ** already exists, it will be created to use the new encoding value.
+ */
+ if( !(pParse->db->flags&SQLITE_Initialized) ){
+ for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
+ if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){
+ pParse->db->enc = pEnc->enc;
+ break;
+ }
+ }
+ if( !pEnc->zName ){
+ sqlite3ErrorMsg(pParse, "unsupported encoding: %s", zRight);
+ }
+ }
+ }
+ }else
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ /*
+ ** Report the current state of file logs for all databases
+ */
+ if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
+ static const char *const azLockName[] = {
+ "unlocked", "shared", "reserved", "pending", "exclusive"
+ };
+ int i;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ sqlite3VdbeSetNumCols(v, 2);
+ sqlite3VdbeSetColName(v, 0, "database", P3_STATIC);
+ sqlite3VdbeSetColName(v, 1, "status", P3_STATIC);
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt;
+ Pager *pPager;
+ if( db->aDb[i].zName==0 ) continue;
+ sqlite3VdbeOp3(v, OP_String, 0, 0, db->aDb[i].zName, P3_STATIC);
+ pBt = db->aDb[i].pBt;
+ if( pBt==0 || (pPager = sqlite3BtreePager(pBt))==0 ){
+ sqlite3VdbeOp3(v, OP_String, 0, 0, "closed", P3_STATIC);
+ }else{
+ int j = sqlite3pager_lockstate(pPager);
+ sqlite3VdbeOp3(v, OP_String, 0, 0,
+ (j>=0 && j<=4) ? azLockName[j] : "unknown", P3_STATIC);
+ }
+ sqlite3VdbeAddOp(v, OP_Callback, 2, 0);
+ }
+ }else
+#endif
+
+ {}
+pragma_out:
+ sqliteFree(zLeft);
+ sqliteFree(zRight);
+}
diff --git a/kopete/plugins/statistics/sqlite/printf.c b/kopete/plugins/statistics/sqlite/printf.c
new file mode 100644
index 00000000..43e12863
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/printf.c
@@ -0,0 +1,825 @@
+/*
+** The "printf" code that follows dates from the 1980's. It is in
+** the public domain. The original comments are included here for
+** completeness. They are very out-of-date but might be useful as
+** an historical reference. Most of the "enhancements" have been backed
+** out so that the functionality is now the same as standard printf().
+**
+**************************************************************************
+**
+** The following modules is an enhanced replacement for the "printf" subroutines
+** found in the standard C library. The following enhancements are
+** supported:
+**
+** + Additional functions. The standard set of "printf" functions
+** includes printf, fprintf, sprintf, vprintf, vfprintf, and
+** vsprintf. This module adds the following:
+**
+** * snprintf -- Works like sprintf, but has an extra argument
+** which is the size of the buffer written to.
+**
+** * mprintf -- Similar to sprintf. Writes output to memory
+** obtained from malloc.
+**
+** * xprintf -- Calls a function to dispose of output.
+**
+** * nprintf -- No output, but returns the number of characters
+** that would have been output by printf.
+**
+** * A v- version (ex: vsnprintf) of every function is also
+** supplied.
+**
+** + A few extensions to the formatting notation are supported:
+**
+** * The "=" flag (similar to "-") causes the output to be
+** be centered in the appropriately sized field.
+**
+** * The %b field outputs an integer in binary notation.
+**
+** * The %c field now accepts a precision. The character output
+** is repeated by the number of times the precision specifies.
+**
+** * The %' field works like %c, but takes as its character the
+** next character of the format string, instead of the next
+** argument. For example, printf("%.78'-") prints 78 minus
+** signs, the same as printf("%.78c",'-').
+**
+** + When compiled using GCC on a SPARC, this version of printf is
+** faster than the library printf for SUN OS 4.1.
+**
+** + All functions are fully reentrant.
+**
+*/
+#include "sqliteInt.h"
+
+/*
+** Conversion types fall into various categories as defined by the
+** following enumeration.
+*/
+#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
+#define etFLOAT 2 /* Floating point. %f */
+#define etEXP 3 /* Exponentional notation. %e and %E */
+#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
+#define etSIZE 5 /* Return number of characters processed so far. %n */
+#define etSTRING 6 /* Strings. %s */
+#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
+#define etPERCENT 8 /* Percent symbol. %% */
+#define etCHARX 9 /* Characters. %c */
+#define etERROR 10 /* Used to indicate no such conversion type */
+/* The rest are extensions, not normally found in printf() */
+#define etCHARLIT 11 /* Literal characters. %' */
+#define etSQLESCAPE 12 /* Strings with '\'' doubled. %q */
+#define etSQLESCAPE2 13 /* Strings with '\'' doubled and enclosed in '',
+ NULL pointers replaced by SQL NULL. %Q */
+#define etTOKEN 14 /* a pointer to a Token structure */
+#define etSRCLIST 15 /* a pointer to a SrcList */
+#define etPOINTER 16 /* The %p conversion */
+
+
+/*
+** An "etByte" is an 8-bit unsigned value.
+*/
+typedef unsigned char etByte;
+
+/*
+** Each builtin conversion character (ex: the 'd' in "%d") is described
+** by an instance of the following structure
+*/
+typedef struct et_info { /* Information about each format field */
+ char fmttype; /* The format field code letter */
+ etByte base; /* The base for radix conversion */
+ etByte flags; /* One or more of FLAG_ constants below */
+ etByte type; /* Conversion paradigm */
+ etByte charset; /* Offset into aDigits[] of the digits string */
+ etByte prefix; /* Offset into aPrefix[] of the prefix string */
+} et_info;
+
+/*
+** Allowed values for et_info.flags
+*/
+#define FLAG_SIGNED 1 /* True if the value to convert is signed */
+#define FLAG_INTERN 2 /* True if for internal use only */
+
+
+/*
+** The following table is searched linearly, so it is good to put the
+** most frequently used conversion types first.
+*/
+static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
+static const char aPrefix[] = "-x0\000X0";
+static const et_info fmtinfo[] = {
+ { 'd', 10, 1, etRADIX, 0, 0 },
+ { 's', 0, 0, etSTRING, 0, 0 },
+ { 'z', 0, 2, etDYNSTRING, 0, 0 },
+ { 'q', 0, 0, etSQLESCAPE, 0, 0 },
+ { 'Q', 0, 0, etSQLESCAPE2, 0, 0 },
+ { 'c', 0, 0, etCHARX, 0, 0 },
+ { 'o', 8, 0, etRADIX, 0, 2 },
+ { 'u', 10, 0, etRADIX, 0, 0 },
+ { 'x', 16, 0, etRADIX, 16, 1 },
+ { 'X', 16, 0, etRADIX, 0, 4 },
+ { 'f', 0, 1, etFLOAT, 0, 0 },
+ { 'e', 0, 1, etEXP, 30, 0 },
+ { 'E', 0, 1, etEXP, 14, 0 },
+ { 'g', 0, 1, etGENERIC, 30, 0 },
+ { 'G', 0, 1, etGENERIC, 14, 0 },
+ { 'i', 10, 1, etRADIX, 0, 0 },
+ { 'n', 0, 0, etSIZE, 0, 0 },
+ { '%', 0, 0, etPERCENT, 0, 0 },
+ { 'p', 16, 0, etPOINTER, 0, 1 },
+ { 'T', 0, 2, etTOKEN, 0, 0 },
+ { 'S', 0, 2, etSRCLIST, 0, 0 },
+};
+#define etNINFO (sizeof(fmtinfo)/sizeof(fmtinfo[0]))
+
+/*
+** If NOFLOATINGPOINT is defined, then none of the floating point
+** conversions will work.
+*/
+#ifndef etNOFLOATINGPOINT
+/*
+** "*val" is a double such that 0.1 <= *val < 10.0
+** Return the ascii code for the leading digit of *val, then
+** multiply "*val" by 10.0 to renormalize.
+**
+** Example:
+** input: *val = 3.14159
+** output: *val = 1.4159 function return = '3'
+**
+** The counter *cnt is incremented each time. After counter exceeds
+** 16 (the number of significant digits in a 64-bit float) '0' is
+** always returned.
+*/
+static int et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
+ int digit;
+ LONGDOUBLE_TYPE d;
+ if( (*cnt)++ >= 16 ) return '0';
+ digit = (int)*val;
+ d = digit;
+ digit += '0';
+ *val = (*val - d)*10.0;
+ return digit;
+}
+#endif
+
+#define etBUFSIZE 1000 /* Size of the output buffer */
+
+/*
+** The root program. All variations call this core.
+**
+** INPUTS:
+** func This is a pointer to a function taking three arguments
+** 1. A pointer to anything. Same as the "arg" parameter.
+** 2. A pointer to the list of characters to be output
+** (Note, this list is NOT null terminated.)
+** 3. An integer number of characters to be output.
+** (Note: This number might be zero.)
+**
+** arg This is the pointer to anything which will be passed as the
+** first argument to "func". Use it for whatever you like.
+**
+** fmt This is the format string, as in the usual print.
+**
+** ap This is a pointer to a list of arguments. Same as in
+** vfprint.
+**
+** OUTPUTS:
+** The return value is the total number of characters sent to
+** the function "func". Returns -1 on a error.
+**
+** Note that the order in which automatic variables are declared below
+** seems to make a big difference in determining how fast this beast
+** will run.
+*/
+static int vxprintf(
+ void (*func)(void*,const char*,int), /* Consumer of text */
+ void *arg, /* First argument to the consumer */
+ int useExtended, /* Allow extended %-conversions */
+ const char *fmt, /* Format string */
+ va_list ap /* arguments */
+){
+ int c; /* Next character in the format string */
+ char *bufpt; /* Pointer to the conversion buffer */
+ int precision; /* Precision of the current field */
+ int length; /* Length of the field */
+ int idx; /* A general purpose loop counter */
+ int count; /* Total number of characters output */
+ int width; /* Width of the current field */
+ etByte flag_leftjustify; /* True if "-" flag is present */
+ etByte flag_plussign; /* True if "+" flag is present */
+ etByte flag_blanksign; /* True if " " flag is present */
+ etByte flag_alternateform; /* True if "#" flag is present */
+ etByte flag_zeropad; /* True if field width constant starts with zero */
+ etByte flag_long; /* True if "l" flag is present */
+ etByte flag_longlong; /* True if the "ll" flag is present */
+ UINT64_TYPE longvalue; /* Value for integer types */
+ LONGDOUBLE_TYPE realvalue; /* Value for real types */
+ const et_info *infop; /* Pointer to the appropriate info structure */
+ char buf[etBUFSIZE]; /* Conversion buffer */
+ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
+ etByte errorflag = 0; /* True if an error is encountered */
+ etByte xtype; /* Conversion paradigm */
+ char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
+ static const char spaces[] =
+ " ";
+#define etSPACESIZE (sizeof(spaces)-1)
+#ifndef etNOFLOATINGPOINT
+ int exp; /* exponent of real numbers */
+ double rounder; /* Used for rounding floating point values */
+ etByte flag_dp; /* True if decimal point should be shown */
+ etByte flag_rtz; /* True if trailing zeros should be removed */
+ etByte flag_exp; /* True to force display of the exponent */
+ int nsd; /* Number of significant digits returned */
+#endif
+
+ func(arg,"",0);
+ count = length = 0;
+ bufpt = 0;
+ for(; (c=(*fmt))!=0; ++fmt){
+ if( c!='%' ){
+ int amt;
+ bufpt = (char *)fmt;
+ amt = 1;
+ while( (c=(*++fmt))!='%' && c!=0 ) amt++;
+ (*func)(arg,bufpt,amt);
+ count += amt;
+ if( c==0 ) break;
+ }
+ if( (c=(*++fmt))==0 ){
+ errorflag = 1;
+ (*func)(arg,"%",1);
+ count++;
+ break;
+ }
+ /* Find out what flags are present */
+ flag_leftjustify = flag_plussign = flag_blanksign =
+ flag_alternateform = flag_zeropad = 0;
+ do{
+ switch( c ){
+ case '-': flag_leftjustify = 1; c = 0; break;
+ case '+': flag_plussign = 1; c = 0; break;
+ case ' ': flag_blanksign = 1; c = 0; break;
+ case '#': flag_alternateform = 1; c = 0; break;
+ case '0': flag_zeropad = 1; c = 0; break;
+ default: break;
+ }
+ }while( c==0 && (c=(*++fmt))!=0 );
+ /* Get the field width */
+ width = 0;
+ if( c=='*' ){
+ width = va_arg(ap,int);
+ if( width<0 ){
+ flag_leftjustify = 1;
+ width = -width;
+ }
+ c = *++fmt;
+ }else{
+ while( c>='0' && c<='9' ){
+ width = width*10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if( width > etBUFSIZE-10 ){
+ width = etBUFSIZE-10;
+ }
+ /* Get the precision */
+ if( c=='.' ){
+ precision = 0;
+ c = *++fmt;
+ if( c=='*' ){
+ precision = va_arg(ap,int);
+ if( precision<0 ) precision = -precision;
+ c = *++fmt;
+ }else{
+ while( c>='0' && c<='9' ){
+ precision = precision*10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ /* Limit the precision to prevent overflowing buf[] during conversion */
+ if( precision>etBUFSIZE-40 ) precision = etBUFSIZE-40;
+ }else{
+ precision = -1;
+ }
+ /* Get the conversion type modifier */
+ if( c=='l' ){
+ flag_long = 1;
+ c = *++fmt;
+ if( c=='l' ){
+ flag_longlong = 1;
+ c = *++fmt;
+ }else{
+ flag_longlong = 0;
+ }
+ }else{
+ flag_long = flag_longlong = 0;
+ }
+ /* Fetch the info entry for the field */
+ infop = 0;
+ xtype = etERROR;
+ for(idx=0; idx<etNINFO; idx++){
+ if( c==fmtinfo[idx].fmttype ){
+ infop = &fmtinfo[idx];
+ if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
+ xtype = infop->type;
+ }
+ break;
+ }
+ }
+ zExtra = 0;
+
+ /*
+ ** At this point, variables are initialized as follows:
+ **
+ ** flag_alternateform TRUE if a '#' is present.
+ ** flag_plussign TRUE if a '+' is present.
+ ** flag_leftjustify TRUE if a '-' is present or if the
+ ** field width was negative.
+ ** flag_zeropad TRUE if the width began with 0.
+ ** flag_long TRUE if the letter 'l' (ell) prefixed
+ ** the conversion character.
+ ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed
+ ** the conversion character.
+ ** flag_blanksign TRUE if a ' ' is present.
+ ** width The specified field width. This is
+ ** always non-negative. Zero is the default.
+ ** precision The specified precision. The default
+ ** is -1.
+ ** xtype The class of the conversion.
+ ** infop Pointer to the appropriate info struct.
+ */
+ switch( xtype ){
+ case etPOINTER:
+ flag_longlong = sizeof(char*)==sizeof(i64);
+ flag_long = sizeof(char*)==sizeof(long int);
+ /* Fall through into the next case */
+ case etRADIX:
+ if( infop->flags & FLAG_SIGNED ){
+ i64 v;
+ if( flag_longlong ) v = va_arg(ap,i64);
+ else if( flag_long ) v = va_arg(ap,long int);
+ else v = va_arg(ap,int);
+ if( v<0 ){
+ longvalue = -v;
+ prefix = '-';
+ }else{
+ longvalue = v;
+ if( flag_plussign ) prefix = '+';
+ else if( flag_blanksign ) prefix = ' ';
+ else prefix = 0;
+ }
+ }else{
+ if( flag_longlong ) longvalue = va_arg(ap,u64);
+ else if( flag_long ) longvalue = va_arg(ap,unsigned long int);
+ else longvalue = va_arg(ap,unsigned int);
+ prefix = 0;
+ }
+ if( longvalue==0 ) flag_alternateform = 0;
+ if( flag_zeropad && precision<width-(prefix!=0) ){
+ precision = width-(prefix!=0);
+ }
+ bufpt = &buf[etBUFSIZE-1];
+ {
+ register const char *cset; /* Use registers for speed */
+ register int base;
+ cset = &aDigits[infop->charset];
+ base = infop->base;
+ do{ /* Convert to ascii */
+ *(--bufpt) = cset[longvalue%base];
+ longvalue = longvalue/base;
+ }while( longvalue>0 );
+ }
+ length = &buf[etBUFSIZE-1]-bufpt;
+ for(idx=precision-length; idx>0; idx--){
+ *(--bufpt) = '0'; /* Zero pad */
+ }
+ if( prefix ) *(--bufpt) = prefix; /* Add sign */
+ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */
+ const char *pre;
+ char x;
+ pre = &aPrefix[infop->prefix];
+ if( *bufpt!=pre[0] ){
+ for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
+ }
+ }
+ length = &buf[etBUFSIZE-1]-bufpt;
+ break;
+ case etFLOAT:
+ case etEXP:
+ case etGENERIC:
+ realvalue = va_arg(ap,double);
+#ifndef etNOFLOATINGPOINT
+ if( precision<0 ) precision = 6; /* Set default precision */
+ if( precision>etBUFSIZE-10 ) precision = etBUFSIZE-10;
+ if( realvalue<0.0 ){
+ realvalue = -realvalue;
+ prefix = '-';
+ }else{
+ if( flag_plussign ) prefix = '+';
+ else if( flag_blanksign ) prefix = ' ';
+ else prefix = 0;
+ }
+ if( infop->type==etGENERIC && precision>0 ) precision--;
+ rounder = 0.0;
+#if 0
+ /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
+ for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
+#else
+ /* It makes more sense to use 0.5 */
+ for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1);
+#endif
+ if( infop->type==etFLOAT ) realvalue += rounder;
+ /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
+ exp = 0;
+ if( realvalue>0.0 ){
+ while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
+ while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ while( realvalue<1e-8 && exp>=-350 ){ realvalue *= 1e8; exp-=8; }
+ while( realvalue<1.0 && exp>=-350 ){ realvalue *= 10.0; exp--; }
+ if( exp>350 || exp<-350 ){
+ bufpt = "NaN";
+ length = 3;
+ break;
+ }
+ }
+ bufpt = buf;
+ /*
+ ** If the field type is etGENERIC, then convert to either etEXP
+ ** or etFLOAT, as appropriate.
+ */
+ flag_exp = xtype==etEXP;
+ if( xtype!=etFLOAT ){
+ realvalue += rounder;
+ if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
+ }
+ if( xtype==etGENERIC ){
+ flag_rtz = !flag_alternateform;
+ if( exp<-4 || exp>precision ){
+ xtype = etEXP;
+ }else{
+ precision = precision - exp;
+ xtype = etFLOAT;
+ }
+ }else{
+ flag_rtz = 0;
+ }
+ /*
+ ** The "exp+precision" test causes output to be of type etEXP if
+ ** the precision is too large to fit in buf[].
+ */
+ nsd = 0;
+ if( xtype==etFLOAT && exp+precision<etBUFSIZE-30 ){
+ flag_dp = (precision>0 || flag_alternateform);
+ if( prefix ) *(bufpt++) = prefix; /* Sign */
+ if( exp<0 ) *(bufpt++) = '0'; /* Digits before "." */
+ else for(; exp>=0; exp--) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+ if( flag_dp ) *(bufpt++) = '.'; /* The decimal point */
+ for(exp++; exp<0 && precision>0; precision--, exp++){
+ *(bufpt++) = '0';
+ }
+ while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+ *(bufpt--) = 0; /* Null terminate */
+ if( flag_rtz && flag_dp ){ /* Remove trailing zeros and "." */
+ while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+ if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+ }
+ bufpt++; /* point to next free slot */
+ }else{ /* etEXP or etGENERIC */
+ flag_dp = (precision>0 || flag_alternateform);
+ if( prefix ) *(bufpt++) = prefix; /* Sign */
+ *(bufpt++) = et_getdigit(&realvalue,&nsd); /* First digit */
+ if( flag_dp ) *(bufpt++) = '.'; /* Decimal point */
+ while( (precision--)>0 ) *(bufpt++) = et_getdigit(&realvalue,&nsd);
+ bufpt--; /* point to last digit */
+ if( flag_rtz && flag_dp ){ /* Remove tail zeros */
+ while( bufpt>=buf && *bufpt=='0' ) *(bufpt--) = 0;
+ if( bufpt>=buf && *bufpt=='.' ) *(bufpt--) = 0;
+ }
+ bufpt++; /* point to next free slot */
+ if( exp || flag_exp ){
+ *(bufpt++) = aDigits[infop->charset];
+ if( exp<0 ){ *(bufpt++) = '-'; exp = -exp; } /* sign of exp */
+ else { *(bufpt++) = '+'; }
+ if( exp>=100 ){
+ *(bufpt++) = (exp/100)+'0'; /* 100's digit */
+ exp %= 100;
+ }
+ *(bufpt++) = exp/10+'0'; /* 10's digit */
+ *(bufpt++) = exp%10+'0'; /* 1's digit */
+ }
+ }
+ /* The converted number is in buf[] and zero terminated. Output it.
+ ** Note that the number is in the usual order, not reversed as with
+ ** integer conversions. */
+ length = bufpt-buf;
+ bufpt = buf;
+
+ /* Special case: Add leading zeros if the flag_zeropad flag is
+ ** set and we are not left justified */
+ if( flag_zeropad && !flag_leftjustify && length < width){
+ int i;
+ int nPad = width - length;
+ for(i=width; i>=nPad; i--){
+ bufpt[i] = bufpt[i-nPad];
+ }
+ i = prefix!=0;
+ while( nPad-- ) bufpt[i++] = '0';
+ length = width;
+ }
+#endif
+ break;
+ case etSIZE:
+ *(va_arg(ap,int*)) = count;
+ length = width = 0;
+ break;
+ case etPERCENT:
+ buf[0] = '%';
+ bufpt = buf;
+ length = 1;
+ break;
+ case etCHARLIT:
+ case etCHARX:
+ c = buf[0] = (xtype==etCHARX ? va_arg(ap,int) : *++fmt);
+ if( precision>=0 ){
+ for(idx=1; idx<precision; idx++) buf[idx] = c;
+ length = precision;
+ }else{
+ length =1;
+ }
+ bufpt = buf;
+ break;
+ case etSTRING:
+ case etDYNSTRING:
+ bufpt = va_arg(ap,char*);
+ if( bufpt==0 ){
+ bufpt = "";
+ }else if( xtype==etDYNSTRING ){
+ zExtra = bufpt;
+ }
+ length = strlen(bufpt);
+ if( precision>=0 && precision<length ) length = precision;
+ break;
+ case etSQLESCAPE:
+ case etSQLESCAPE2:
+ {
+ int i, j, n, c, isnull;
+ char *arg = va_arg(ap,char*);
+ isnull = arg==0;
+ if( isnull ) arg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
+ for(i=n=0; (c=arg[i])!=0; i++){
+ if( c=='\'' ) n++;
+ }
+ n += i + 1 + ((!isnull && xtype==etSQLESCAPE2) ? 2 : 0);
+ if( n>etBUFSIZE ){
+ bufpt = zExtra = sqliteMalloc( n );
+ if( bufpt==0 ) return -1;
+ }else{
+ bufpt = buf;
+ }
+ j = 0;
+ if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\'';
+ for(i=0; (c=arg[i])!=0; i++){
+ bufpt[j++] = c;
+ if( c=='\'' ) bufpt[j++] = c;
+ }
+ if( !isnull && xtype==etSQLESCAPE2 ) bufpt[j++] = '\'';
+ bufpt[j] = 0;
+ length = j;
+ if( precision>=0 && precision<length ) length = precision;
+ }
+ break;
+ case etTOKEN: {
+ Token *pToken = va_arg(ap, Token*);
+ if( pToken && pToken->z ){
+ (*func)(arg, pToken->z, pToken->n);
+ }
+ length = width = 0;
+ break;
+ }
+ case etSRCLIST: {
+ SrcList *pSrc = va_arg(ap, SrcList*);
+ int k = va_arg(ap, int);
+ struct SrcList_item *pItem = &pSrc->a[k];
+ assert( k>=0 && k<pSrc->nSrc );
+ if( pItem->zDatabase && pItem->zDatabase[0] ){
+ (*func)(arg, pItem->zDatabase, strlen(pItem->zDatabase));
+ (*func)(arg, ".", 1);
+ }
+ (*func)(arg, pItem->zName, strlen(pItem->zName));
+ length = width = 0;
+ break;
+ }
+ case etERROR:
+ buf[0] = '%';
+ buf[1] = c;
+ errorflag = 0;
+ idx = 1+(c!=0);
+ (*func)(arg,"%",idx);
+ count += idx;
+ if( c==0 ) fmt--;
+ break;
+ }/* End switch over the format type */
+ /*
+ ** The text of the conversion is pointed to by "bufpt" and is
+ ** "length" characters long. The field width is "width". Do
+ ** the output.
+ */
+ if( !flag_leftjustify ){
+ register int nspace;
+ nspace = width-length;
+ if( nspace>0 ){
+ count += nspace;
+ while( nspace>=etSPACESIZE ){
+ (*func)(arg,spaces,etSPACESIZE);
+ nspace -= etSPACESIZE;
+ }
+ if( nspace>0 ) (*func)(arg,spaces,nspace);
+ }
+ }
+ if( length>0 ){
+ (*func)(arg,bufpt,length);
+ count += length;
+ }
+ if( flag_leftjustify ){
+ register int nspace;
+ nspace = width-length;
+ if( nspace>0 ){
+ count += nspace;
+ while( nspace>=etSPACESIZE ){
+ (*func)(arg,spaces,etSPACESIZE);
+ nspace -= etSPACESIZE;
+ }
+ if( nspace>0 ) (*func)(arg,spaces,nspace);
+ }
+ }
+ if( zExtra ){
+ sqliteFree(zExtra);
+ }
+ }/* End for loop over the format string */
+ return errorflag ? -1 : count;
+} /* End of function */
+
+
+/* This structure is used to store state information about the
+** write to memory that is currently in progress.
+*/
+struct sgMprintf {
+ char *zBase; /* A base allocation */
+ char *zText; /* The string collected so far */
+ int nChar; /* Length of the string so far */
+ int nTotal; /* Output size if unconstrained */
+ int nAlloc; /* Amount of space allocated in zText */
+ void *(*xRealloc)(void*,int); /* Function used to realloc memory */
+};
+
+/*
+** This function implements the callback from vxprintf.
+**
+** This routine add nNewChar characters of text in zNewText to
+** the sgMprintf structure pointed to by "arg".
+*/
+static void mout(void *arg, const char *zNewText, int nNewChar){
+ struct sgMprintf *pM = (struct sgMprintf*)arg;
+ pM->nTotal += nNewChar;
+ if( pM->nChar + nNewChar + 1 > pM->nAlloc ){
+ if( pM->xRealloc==0 ){
+ nNewChar = pM->nAlloc - pM->nChar - 1;
+ }else{
+ pM->nAlloc = pM->nChar + nNewChar*2 + 1;
+ if( pM->zText==pM->zBase ){
+ pM->zText = pM->xRealloc(0, pM->nAlloc);
+ if( pM->zText && pM->nChar ){
+ memcpy(pM->zText, pM->zBase, pM->nChar);
+ }
+ }else{
+ pM->zText = pM->xRealloc(pM->zText, pM->nAlloc);
+ }
+ }
+ }
+ if( pM->zText ){
+ if( nNewChar>0 ){
+ memcpy(&pM->zText[pM->nChar], zNewText, nNewChar);
+ pM->nChar += nNewChar;
+ }
+ pM->zText[pM->nChar] = 0;
+ }
+}
+
+/*
+** This routine is a wrapper around xprintf() that invokes mout() as
+** the consumer.
+*/
+static char *base_vprintf(
+ void *(*xRealloc)(void*,int), /* Routine to realloc memory. May be NULL */
+ int useInternal, /* Use internal %-conversions if true */
+ char *zInitBuf, /* Initially write here, before mallocing */
+ int nInitBuf, /* Size of zInitBuf[] */
+ const char *zFormat, /* format string */
+ va_list ap /* arguments */
+){
+ struct sgMprintf sM;
+ sM.zBase = sM.zText = zInitBuf;
+ sM.nChar = sM.nTotal = 0;
+ sM.nAlloc = nInitBuf;
+ sM.xRealloc = xRealloc;
+ vxprintf(mout, &sM, useInternal, zFormat, ap);
+ if( xRealloc ){
+ if( sM.zText==sM.zBase ){
+ sM.zText = xRealloc(0, sM.nChar+1);
+ if( sM.zText ){
+ memcpy(sM.zText, sM.zBase, sM.nChar+1);
+ }
+ }else if( sM.nAlloc>sM.nChar+10 ){
+ sM.zText = xRealloc(sM.zText, sM.nChar+1);
+ }
+ }
+ return sM.zText;
+}
+
+/*
+** Realloc that is a real function, not a macro.
+*/
+static void *printf_realloc(void *old, int size){
+ return sqliteRealloc(old,size);
+}
+
+/*
+** Print into memory obtained from sqliteMalloc(). Use the internal
+** %-conversion extensions.
+*/
+char *sqlite3VMPrintf(const char *zFormat, va_list ap){
+ char zBase[1000];
+ return base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
+}
+
+/*
+** Print into memory obtained from sqliteMalloc(). Use the internal
+** %-conversion extensions.
+*/
+char *sqlite3MPrintf(const char *zFormat, ...){
+ va_list ap;
+ char *z;
+ char zBase[1000];
+ va_start(ap, zFormat);
+ z = base_vprintf(printf_realloc, 1, zBase, sizeof(zBase), zFormat, ap);
+ va_end(ap);
+ return z;
+}
+
+/*
+** Print into memory obtained from malloc(). Do not use the internal
+** %-conversion extensions. This routine is for use by external users.
+*/
+char *sqlite3_mprintf(const char *zFormat, ...){
+ va_list ap;
+ char *z;
+ char zBuf[200];
+
+ va_start(ap,zFormat);
+ z = base_vprintf((void*(*)(void*,int))realloc, 0,
+ zBuf, sizeof(zBuf), zFormat, ap);
+ va_end(ap);
+ return z;
+}
+
+/* This is the varargs version of sqlite3_mprintf.
+*/
+char *sqlite3_vmprintf(const char *zFormat, va_list ap){
+ char zBuf[200];
+ return base_vprintf((void*(*)(void*,int))realloc, 0,
+ zBuf, sizeof(zBuf), zFormat, ap);
+}
+
+/*
+** sqlite3_snprintf() works like snprintf() except that it ignores the
+** current locale settings. This is important for SQLite because we
+** are not able to use a "," as the decimal point in place of "." as
+** specified by some locales.
+*/
+char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
+ char *z;
+ va_list ap;
+
+ va_start(ap,zFormat);
+ z = base_vprintf(0, 0, zBuf, n, zFormat, ap);
+ va_end(ap);
+ return z;
+}
+
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+/*
+** A version of printf() that understands %lld. Used for debugging.
+** The printf() built into some versions of windows does not understand %lld
+** and segfaults if you give it a long long int.
+*/
+void sqlite3DebugPrintf(const char *zFormat, ...){
+ extern int getpid(void);
+ va_list ap;
+ char zBuf[500];
+ va_start(ap, zFormat);
+ base_vprintf(0, 0, zBuf, sizeof(zBuf), zFormat, ap);
+ va_end(ap);
+ fprintf(stdout,"%d: %s", getpid(), zBuf);
+ fflush(stdout);
+}
+#endif
diff --git a/kopete/plugins/statistics/sqlite/random.c b/kopete/plugins/statistics/sqlite/random.c
new file mode 100644
index 00000000..de74e291
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/random.c
@@ -0,0 +1,100 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code to implement a pseudo-random number
+** generator (PRNG) for SQLite.
+**
+** Random numbers are used by some of the database backends in order
+** to generate random integer keys for tables or random filenames.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+
+
+/*
+** Get a single 8-bit random value from the RC4 PRNG. The Mutex
+** must be held while executing this routine.
+**
+** Why not just use a library random generator like lrand48() for this?
+** Because the OP_NewRecno opcode in the VDBE depends on having a very
+** good source of random numbers. The lrand48() library function may
+** well be good enough. But maybe not. Or maybe lrand48() has some
+** subtle problems on some systems that could cause problems. It is hard
+** to know. To minimize the risk of problems due to bad lrand48()
+** implementations, SQLite uses this random number generator based
+** on RC4, which we know works very well.
+*/
+static int randomByte(){
+ unsigned char t;
+
+ /* All threads share a single random number generator.
+ ** This structure is the current state of the generator.
+ */
+ static struct {
+ unsigned char isInit; /* True if initialized */
+ unsigned char i, j; /* State variables */
+ unsigned char s[256]; /* State variables */
+ } prng;
+
+ /* Initialize the state of the random number generator once,
+ ** the first time this routine is called. The seed value does
+ ** not need to contain a lot of randomness since we are not
+ ** trying to do secure encryption or anything like that...
+ **
+ ** Nothing in this file or anywhere else in SQLite does any kind of
+ ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random
+ ** number generator) not as an encryption device.
+ */
+ if( !prng.isInit ){
+ int i;
+ char k[256];
+ prng.j = 0;
+ prng.i = 0;
+ sqlite3OsRandomSeed(k);
+ for(i=0; i<256; i++){
+ prng.s[i] = i;
+ }
+ for(i=0; i<256; i++){
+ prng.j += prng.s[i] + k[i];
+ t = prng.s[prng.j];
+ prng.s[prng.j] = prng.s[i];
+ prng.s[i] = t;
+ }
+ prng.isInit = 1;
+ }
+
+ /* Generate and return single random byte
+ */
+ prng.i++;
+ t = prng.s[prng.i];
+ prng.j += t;
+ prng.s[prng.i] = prng.s[prng.j];
+ prng.s[prng.j] = t;
+ t += prng.s[prng.i];
+ return prng.s[t];
+}
+
+/*
+** Return N random bytes.
+*/
+void sqlite3Randomness(int N, void *pBuf){
+ unsigned char *zBuf = pBuf;
+ sqlite3OsEnterMutex();
+ while( N-- ){
+ *(zBuf++) = randomByte();
+ }
+ sqlite3OsLeaveMutex();
+}
+
+
+
diff --git a/kopete/plugins/statistics/sqlite/select.c b/kopete/plugins/statistics/sqlite/select.c
new file mode 100644
index 00000000..8bee7897
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/select.c
@@ -0,0 +1,2628 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle SELECT statements in SQLite.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+
+/*
+** Allocate a new Select structure and return a pointer to that
+** structure.
+*/
+Select *sqlite3SelectNew(
+ ExprList *pEList, /* which columns to include in the result */
+ SrcList *pSrc, /* the FROM clause -- which tables to scan */
+ Expr *pWhere, /* the WHERE clause */
+ ExprList *pGroupBy, /* the GROUP BY clause */
+ Expr *pHaving, /* the HAVING clause */
+ ExprList *pOrderBy, /* the ORDER BY clause */
+ int isDistinct, /* true if the DISTINCT keyword is present */
+ int nLimit, /* LIMIT value. -1 means not used */
+ int nOffset /* OFFSET value. 0 means no offset */
+){
+ Select *pNew;
+ pNew = sqliteMalloc( sizeof(*pNew) );
+ if( pNew==0 ){
+ sqlite3ExprListDelete(pEList);
+ sqlite3SrcListDelete(pSrc);
+ sqlite3ExprDelete(pWhere);
+ sqlite3ExprListDelete(pGroupBy);
+ sqlite3ExprDelete(pHaving);
+ sqlite3ExprListDelete(pOrderBy);
+ }else{
+ if( pEList==0 ){
+ pEList = sqlite3ExprListAppend(0, sqlite3Expr(TK_ALL,0,0,0), 0);
+ }
+ pNew->pEList = pEList;
+ pNew->pSrc = pSrc;
+ pNew->pWhere = pWhere;
+ pNew->pGroupBy = pGroupBy;
+ pNew->pHaving = pHaving;
+ pNew->pOrderBy = pOrderBy;
+ pNew->isDistinct = isDistinct;
+ pNew->op = TK_SELECT;
+ pNew->nLimit = nLimit;
+ pNew->nOffset = nOffset;
+ pNew->iLimit = -1;
+ pNew->iOffset = -1;
+ }
+ return pNew;
+}
+
+/*
+** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
+** type of join. Return an integer constant that expresses that type
+** in terms of the following bit values:
+**
+** JT_INNER
+** JT_OUTER
+** JT_NATURAL
+** JT_LEFT
+** JT_RIGHT
+**
+** A full outer join is the combination of JT_LEFT and JT_RIGHT.
+**
+** If an illegal or unsupported join type is seen, then still return
+** a join type, but put an error in the pParse structure.
+*/
+int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *pC){
+ int jointype = 0;
+ Token *apAll[3];
+ Token *p;
+ static const struct {
+ const char *zKeyword;
+ u8 nChar;
+ u8 code;
+ } keywords[] = {
+ { "natural", 7, JT_NATURAL },
+ { "left", 4, JT_LEFT|JT_OUTER },
+ { "right", 5, JT_RIGHT|JT_OUTER },
+ { "full", 4, JT_LEFT|JT_RIGHT|JT_OUTER },
+ { "outer", 5, JT_OUTER },
+ { "inner", 5, JT_INNER },
+ { "cross", 5, JT_INNER },
+ };
+ int i, j;
+ apAll[0] = pA;
+ apAll[1] = pB;
+ apAll[2] = pC;
+ for(i=0; i<3 && apAll[i]; i++){
+ p = apAll[i];
+ for(j=0; j<sizeof(keywords)/sizeof(keywords[0]); j++){
+ if( p->n==keywords[j].nChar
+ && sqlite3StrNICmp(p->z, keywords[j].zKeyword, p->n)==0 ){
+ jointype |= keywords[j].code;
+ break;
+ }
+ }
+ if( j>=sizeof(keywords)/sizeof(keywords[0]) ){
+ jointype |= JT_ERROR;
+ break;
+ }
+ }
+ if(
+ (jointype & (JT_INNER|JT_OUTER))==(JT_INNER|JT_OUTER) ||
+ (jointype & JT_ERROR)!=0
+ ){
+ const char *zSp1 = " ";
+ const char *zSp2 = " ";
+ if( pB==0 ){ zSp1++; }
+ if( pC==0 ){ zSp2++; }
+ sqlite3ErrorMsg(pParse, "unknown or unsupported join type: "
+ "%T%s%T%s%T", pA, zSp1, pB, zSp2, pC);
+ jointype = JT_INNER;
+ }else if( jointype & JT_RIGHT ){
+ sqlite3ErrorMsg(pParse,
+ "RIGHT and FULL OUTER JOINs are not currently supported");
+ jointype = JT_INNER;
+ }
+ return jointype;
+}
+
+/*
+** Return the index of a column in a table. Return -1 if the column
+** is not contained in the table.
+*/
+static int columnIndex(Table *pTab, const char *zCol){
+ int i;
+ for(i=0; i<pTab->nCol; i++){
+ if( sqlite3StrICmp(pTab->aCol[i].zName, zCol)==0 ) return i;
+ }
+ return -1;
+}
+
+/*
+** Set the value of a token to a '\000'-terminated string.
+*/
+static void setToken(Token *p, const char *z){
+ p->z = z;
+ p->n = strlen(z);
+ p->dyn = 0;
+}
+
+
+/*
+** Add a term to the WHERE expression in *ppExpr that requires the
+** zCol column to be equal in the two tables pTab1 and pTab2.
+*/
+static void addWhereTerm(
+ const char *zCol, /* Name of the column */
+ const Table *pTab1, /* First table */
+ const Table *pTab2, /* Second table */
+ Expr **ppExpr /* Add the equality term to this expression */
+){
+ Token dummy;
+ Expr *pE1a, *pE1b, *pE1c;
+ Expr *pE2a, *pE2b, *pE2c;
+ Expr *pE;
+
+ setToken(&dummy, zCol);
+ pE1a = sqlite3Expr(TK_ID, 0, 0, &dummy);
+ pE2a = sqlite3Expr(TK_ID, 0, 0, &dummy);
+ setToken(&dummy, pTab1->zName);
+ pE1b = sqlite3Expr(TK_ID, 0, 0, &dummy);
+ setToken(&dummy, pTab2->zName);
+ pE2b = sqlite3Expr(TK_ID, 0, 0, &dummy);
+ pE1c = sqlite3Expr(TK_DOT, pE1b, pE1a, 0);
+ pE2c = sqlite3Expr(TK_DOT, pE2b, pE2a, 0);
+ pE = sqlite3Expr(TK_EQ, pE1c, pE2c, 0);
+ ExprSetProperty(pE, EP_FromJoin);
+ *ppExpr = sqlite3ExprAnd(*ppExpr, pE);
+}
+
+/*
+** Set the EP_FromJoin property on all terms of the given expression.
+**
+** The EP_FromJoin property is used on terms of an expression to tell
+** the LEFT OUTER JOIN processing logic that this term is part of the
+** join restriction specified in the ON or USING clause and not a part
+** of the more general WHERE clause. These terms are moved over to the
+** WHERE clause during join processing but we need to remember that they
+** originated in the ON or USING clause.
+*/
+static void setJoinExpr(Expr *p){
+ while( p ){
+ ExprSetProperty(p, EP_FromJoin);
+ setJoinExpr(p->pLeft);
+ p = p->pRight;
+ }
+}
+
+/*
+** This routine processes the join information for a SELECT statement.
+** ON and USING clauses are converted into extra terms of the WHERE clause.
+** NATURAL joins also create extra WHERE clause terms.
+**
+** The terms of a FROM clause are contained in the Select.pSrc structure.
+** The left most table is the first entry in Select.pSrc. The right-most
+** table is the last entry. The join operator is held in the entry to
+** the left. Thus entry 0 contains the join operator for the join between
+** entries 0 and 1. Any ON or USING clauses associated with the join are
+** also attached to the left entry.
+**
+** This routine returns the number of errors encountered.
+*/
+static int sqliteProcessJoin(Parse *pParse, Select *p){
+ SrcList *pSrc; /* All tables in the FROM clause */
+ int i, j; /* Loop counters */
+ struct SrcList_item *pLeft; /* Left table being joined */
+ struct SrcList_item *pRight; /* Right table being joined */
+
+ pSrc = p->pSrc;
+ pLeft = &pSrc->a[0];
+ pRight = &pLeft[1];
+ for(i=0; i<pSrc->nSrc-1; i++, pRight++, pLeft++){
+ Table *pLeftTab = pLeft->pTab;
+ Table *pRightTab = pRight->pTab;
+
+ if( pLeftTab==0 || pRightTab==0 ) continue;
+
+ /* When the NATURAL keyword is present, add WHERE clause terms for
+ ** every column that the two tables have in common.
+ */
+ if( pLeft->jointype & JT_NATURAL ){
+ if( pLeft->pOn || pLeft->pUsing ){
+ sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
+ "an ON or USING clause", 0);
+ return 1;
+ }
+ for(j=0; j<pLeftTab->nCol; j++){
+ char *zName = pLeftTab->aCol[j].zName;
+ if( columnIndex(pRightTab, zName)>=0 ){
+ addWhereTerm(zName, pLeftTab, pRightTab, &p->pWhere);
+ }
+ }
+ }
+
+ /* Disallow both ON and USING clauses in the same join
+ */
+ if( pLeft->pOn && pLeft->pUsing ){
+ sqlite3ErrorMsg(pParse, "cannot have both ON and USING "
+ "clauses in the same join");
+ return 1;
+ }
+
+ /* Add the ON clause to the end of the WHERE clause, connected by
+ ** an AND operator.
+ */
+ if( pLeft->pOn ){
+ setJoinExpr(pLeft->pOn);
+ p->pWhere = sqlite3ExprAnd(p->pWhere, pLeft->pOn);
+ pLeft->pOn = 0;
+ }
+
+ /* Create extra terms on the WHERE clause for each column named
+ ** in the USING clause. Example: If the two tables to be joined are
+ ** A and B and the USING clause names X, Y, and Z, then add this
+ ** to the WHERE clause: A.X=B.X AND A.Y=B.Y AND A.Z=B.Z
+ ** Report an error if any column mentioned in the USING clause is
+ ** not contained in both tables to be joined.
+ */
+ if( pLeft->pUsing ){
+ IdList *pList = pLeft->pUsing;
+ for(j=0; j<pList->nId; j++){
+ char *zName = pList->a[j].zName;
+ if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){
+ sqlite3ErrorMsg(pParse, "cannot join using column %s - column "
+ "not present in both tables", zName);
+ return 1;
+ }
+ addWhereTerm(zName, pLeftTab, pRightTab, &p->pWhere);
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Delete the given Select structure and all of its substructures.
+*/
+void sqlite3SelectDelete(Select *p){
+ if( p==0 ) return;
+ sqlite3ExprListDelete(p->pEList);
+ sqlite3SrcListDelete(p->pSrc);
+ sqlite3ExprDelete(p->pWhere);
+ sqlite3ExprListDelete(p->pGroupBy);
+ sqlite3ExprDelete(p->pHaving);
+ sqlite3ExprListDelete(p->pOrderBy);
+ sqlite3SelectDelete(p->pPrior);
+ sqliteFree(p->zSelect);
+ sqliteFree(p);
+}
+
+/*
+** Delete the aggregate information from the parse structure.
+*/
+static void sqliteAggregateInfoReset(Parse *pParse){
+ sqliteFree(pParse->aAgg);
+ pParse->aAgg = 0;
+ pParse->nAgg = 0;
+ pParse->useAgg = 0;
+}
+
+/*
+** Insert code into "v" that will push the record on the top of the
+** stack into the sorter.
+*/
+static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){
+ int i;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ sqlite3ExprCode(pParse, pOrderBy->a[i].pExpr);
+ }
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pOrderBy->nExpr, 0);
+ sqlite3VdbeAddOp(v, OP_SortPut, 0, 0);
+}
+
+/*
+** Add code to implement the OFFSET and LIMIT
+*/
+static void codeLimiter(
+ Vdbe *v, /* Generate code into this VM */
+ Select *p, /* The SELECT statement being coded */
+ int iContinue, /* Jump here to skip the current record */
+ int iBreak, /* Jump here to end the loop */
+ int nPop /* Number of times to pop stack when jumping */
+){
+ if( p->iOffset>=0 ){
+ int addr = sqlite3VdbeCurrentAddr(v) + 2;
+ if( nPop>0 ) addr++;
+ sqlite3VdbeAddOp(v, OP_MemIncr, p->iOffset, addr);
+ if( nPop>0 ){
+ sqlite3VdbeAddOp(v, OP_Pop, nPop, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
+ VdbeComment((v, "# skip OFFSET records"));
+ }
+ if( p->iLimit>=0 ){
+ sqlite3VdbeAddOp(v, OP_MemIncr, p->iLimit, iBreak);
+ VdbeComment((v, "# exit when LIMIT reached"));
+ }
+}
+
+/*
+** This routine generates the code for the inside of the inner loop
+** of a SELECT.
+**
+** If srcTab and nColumn are both zero, then the pEList expressions
+** are evaluated in order to get the data for this row. If nColumn>0
+** then data is pulled from srcTab and pEList is used only to get the
+** datatypes for each column.
+*/
+static int selectInnerLoop(
+ Parse *pParse, /* The parser context */
+ Select *p, /* The complete select statement being coded */
+ ExprList *pEList, /* List of values being extracted */
+ int srcTab, /* Pull data from this table */
+ int nColumn, /* Number of columns in the source table */
+ ExprList *pOrderBy, /* If not NULL, sort results using this key */
+ int distinct, /* If >=0, make sure results are distinct */
+ int eDest, /* How to dispose of the results */
+ int iParm, /* An argument to the disposal method */
+ int iContinue, /* Jump here to continue with next row */
+ int iBreak, /* Jump here to break out of the inner loop */
+ char *aff /* affinity string if eDest is SRT_Union */
+){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ int hasDistinct; /* True if the DISTINCT keyword is present */
+
+ if( v==0 ) return 0;
+ assert( pEList!=0 );
+
+ /* If there was a LIMIT clause on the SELECT statement, then do the check
+ ** to see if this row should be output.
+ */
+ hasDistinct = distinct>=0 && pEList && pEList->nExpr>0;
+ if( pOrderBy==0 && !hasDistinct ){
+ codeLimiter(v, p, iContinue, iBreak, 0);
+ }
+
+ /* Pull the requested columns.
+ */
+ if( nColumn>0 ){
+ for(i=0; i<nColumn; i++){
+ sqlite3VdbeAddOp(v, OP_Column, srcTab, i);
+ }
+ }else{
+ nColumn = pEList->nExpr;
+ for(i=0; i<pEList->nExpr; i++){
+ sqlite3ExprCode(pParse, pEList->a[i].pExpr);
+ }
+ }
+
+ /* If the DISTINCT keyword was present on the SELECT statement
+ ** and this row has been seen before, then do not make this row
+ ** part of the result.
+ */
+ if( hasDistinct ){
+#if NULL_ALWAYS_DISTINCT
+ sqlite3VdbeAddOp(v, OP_IsNull, -pEList->nExpr, sqlite3VdbeCurrentAddr(v)+7);
+#endif
+ /* Deliberately leave the affinity string off of the following
+ ** OP_MakeRecord */
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pEList->nExpr * -1, 0);
+ sqlite3VdbeAddOp(v, OP_Distinct, distinct, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp(v, OP_Pop, pEList->nExpr+1, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iContinue);
+ VdbeComment((v, "# skip indistinct records"));
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutStrKey, distinct, 0);
+ if( pOrderBy==0 ){
+ codeLimiter(v, p, iContinue, iBreak, nColumn);
+ }
+ }
+
+ switch( eDest ){
+ /* In this mode, write each query result to the key of the temporary
+ ** table iParm.
+ */
+ case SRT_Union: {
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT);
+ sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutStrKey, iParm, 0);
+ break;
+ }
+
+ /* Store the result as data using a unique key.
+ */
+ case SRT_Table:
+ case SRT_TempTable: {
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ if( pOrderBy ){
+ pushOntoSorter(pParse, v, pOrderBy);
+ }else{
+ sqlite3VdbeAddOp(v, OP_NewRecno, iParm, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, iParm, 0);
+ }
+ break;
+ }
+
+ /* Construct a record from the query result, but instead of
+ ** saving that record, use it as a key to delete elements from
+ ** the temporary table iParm.
+ */
+ case SRT_Except: {
+ int addr;
+ addr = sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, NULL_ALWAYS_DISTINCT);
+ sqlite3VdbeChangeP3(v, -1, aff, P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_NotFound, iParm, addr+3);
+ sqlite3VdbeAddOp(v, OP_Delete, iParm, 0);
+ break;
+ }
+
+ /* If we are creating a set for an "expr IN (SELECT ...)" construct,
+ ** then there should be a single item on the stack. Write this
+ ** item into the set table with bogus data.
+ */
+ case SRT_Set: {
+ int addr1 = sqlite3VdbeCurrentAddr(v);
+ int addr2;
+
+ assert( nColumn==1 );
+ sqlite3VdbeAddOp(v, OP_NotNull, -1, addr1+3);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ addr2 = sqlite3VdbeAddOp(v, OP_Goto, 0, 0);
+ if( pOrderBy ){
+ pushOntoSorter(pParse, v, pOrderBy);
+ }else{
+ char aff = (iParm>>16)&0xFF;
+ aff = sqlite3CompareAffinity(pEList->a[0].pExpr, aff);
+ sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, &aff, 1);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutStrKey, (iParm&0x0000FFFF), 0);
+ }
+ sqlite3VdbeChangeP2(v, addr2, sqlite3VdbeCurrentAddr(v));
+ break;
+ }
+
+ /* If this is a scalar select that is part of an expression, then
+ ** store the results in the appropriate memory cell and break out
+ ** of the scan loop.
+ */
+ case SRT_Mem: {
+ assert( nColumn==1 );
+ if( pOrderBy ){
+ pushOntoSorter(pParse, v, pOrderBy);
+ }else{
+ sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, iBreak);
+ }
+ break;
+ }
+
+ /* Send the data to the callback function.
+ */
+ case SRT_Callback:
+ case SRT_Sorter: {
+ if( pOrderBy ){
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ pushOntoSorter(pParse, v, pOrderBy);
+ }else{
+ assert( eDest==SRT_Callback );
+ sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0);
+ }
+ break;
+ }
+
+ /* Invoke a subroutine to handle the results. The subroutine itself
+ ** is responsible for popping the results off of the stack.
+ */
+ case SRT_Subroutine: {
+ if( pOrderBy ){
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ pushOntoSorter(pParse, v, pOrderBy);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm);
+ }
+ break;
+ }
+
+ /* Discard the results. This is used for SELECT statements inside
+ ** the body of a TRIGGER. The purpose of such selects is to call
+ ** user-defined functions that have side effects. We do not care
+ ** about the actual results of the select.
+ */
+ default: {
+ assert( eDest==SRT_Discard );
+ sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+** If the inner loop was generated using a non-null pOrderBy argument,
+** then the results were placed in a sorter. After the loop is terminated
+** we need to run the sorter and output the results. The following
+** routine generates the code needed to do that.
+*/
+static void generateSortTail(
+ Parse *pParse, /* The parsing context */
+ Select *p, /* The SELECT statement */
+ Vdbe *v, /* Generate code into this VDBE */
+ int nColumn, /* Number of columns of data */
+ int eDest, /* Write the sorted results here */
+ int iParm /* Optional parameter associated with eDest */
+){
+ int end1 = sqlite3VdbeMakeLabel(v);
+ int end2 = sqlite3VdbeMakeLabel(v);
+ int addr;
+ KeyInfo *pInfo;
+ ExprList *pOrderBy;
+ int nCol, i;
+ sqlite3 *db = pParse->db;
+
+ if( eDest==SRT_Sorter ) return;
+ pOrderBy = p->pOrderBy;
+ nCol = pOrderBy->nExpr;
+ pInfo = sqliteMalloc( sizeof(*pInfo) + nCol*(sizeof(CollSeq*)+1) );
+ if( pInfo==0 ) return;
+ pInfo->aSortOrder = (char*)&pInfo->aColl[nCol];
+ pInfo->nField = nCol;
+ for(i=0; i<nCol; i++){
+ /* If a collation sequence was specified explicity, then it
+ ** is stored in pOrderBy->a[i].zName. Otherwise, use the default
+ ** collation type for the expression.
+ */
+ pInfo->aColl[i] = sqlite3ExprCollSeq(pParse, pOrderBy->a[i].pExpr);
+ if( !pInfo->aColl[i] ){
+ pInfo->aColl[i] = db->pDfltColl;
+ }
+ pInfo->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ }
+ sqlite3VdbeOp3(v, OP_Sort, 0, 0, (char*)pInfo, P3_KEYINFO_HANDOFF);
+ addr = sqlite3VdbeAddOp(v, OP_SortNext, 0, end1);
+ codeLimiter(v, p, addr, end2, 1);
+ switch( eDest ){
+ case SRT_Table:
+ case SRT_TempTable: {
+ sqlite3VdbeAddOp(v, OP_NewRecno, iParm, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, iParm, 0);
+ break;
+ }
+ case SRT_Set: {
+ assert( nColumn==1 );
+ sqlite3VdbeAddOp(v, OP_NotNull, -1, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeOp3(v, OP_MakeRecord, 1, 0, "n", P3_STATIC);
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_PutStrKey, (iParm&0x0000FFFF), 0);
+ break;
+ }
+ case SRT_Mem: {
+ assert( nColumn==1 );
+ sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, end1);
+ break;
+ }
+ case SRT_Callback:
+ case SRT_Subroutine: {
+ int i;
+ sqlite3VdbeAddOp(v, OP_Integer, p->pEList->nExpr, 0);
+ sqlite3VdbeAddOp(v, OP_Pull, 1, 0);
+ for(i=0; i<nColumn; i++){
+ sqlite3VdbeAddOp(v, OP_Column, -1-i, i);
+ }
+ if( eDest==SRT_Callback ){
+ sqlite3VdbeAddOp(v, OP_Callback, nColumn, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Gosub, 0, iParm);
+ }
+ sqlite3VdbeAddOp(v, OP_Pop, 2, 0);
+ break;
+ }
+ default: {
+ /* Do nothing */
+ break;
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeResolveLabel(v, end2);
+ sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
+ sqlite3VdbeResolveLabel(v, end1);
+ sqlite3VdbeAddOp(v, OP_SortReset, 0, 0);
+}
+
+/*
+** Return a pointer to a string containing the 'declaration type' of the
+** expression pExpr. The string may be treated as static by the caller.
+**
+** If the declaration type is the exact datatype definition extracted from
+** the original CREATE TABLE statement if the expression is a column.
+**
+** The declaration type for an expression is either TEXT, NUMERIC or ANY.
+** The declaration type for a ROWID field is INTEGER.
+*/
+static const char *columnType(Parse *pParse, SrcList *pTabList, Expr *pExpr){
+ char const *zType;
+ int j;
+ if( pExpr==0 || pTabList==0 ) return 0;
+
+ switch( pExpr->op ){
+ case TK_COLUMN: {
+ Table *pTab;
+ int iCol = pExpr->iColumn;
+ for(j=0; j<pTabList->nSrc && pTabList->a[j].iCursor!=pExpr->iTable; j++){}
+ assert( j<pTabList->nSrc );
+ pTab = pTabList->a[j].pTab;
+ if( iCol<0 ) iCol = pTab->iPKey;
+ assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
+ if( iCol<0 ){
+ zType = "INTEGER";
+ }else{
+ zType = pTab->aCol[iCol].zType;
+ }
+ break;
+ }
+ case TK_AS:
+ zType = columnType(pParse, pTabList, pExpr->pLeft);
+ break;
+ case TK_SELECT: {
+ Select *pS = pExpr->pSelect;
+ zType = columnType(pParse, pS->pSrc, pS->pEList->a[0].pExpr);
+ break;
+ }
+ default:
+ zType = 0;
+ }
+
+ return zType;
+}
+
+/*
+** Generate code that will tell the VDBE the declaration types of columns
+** in the result set.
+*/
+static void generateColumnTypes(
+ Parse *pParse, /* Parser context */
+ SrcList *pTabList, /* List of tables */
+ ExprList *pEList /* Expressions defining the result set */
+){
+ Vdbe *v = pParse->pVdbe;
+ int i;
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *p = pEList->a[i].pExpr;
+ const char *zType = columnType(pParse, pTabList, p);
+ if( zType==0 ) continue;
+ /* The vdbe must make it's own copy of the column-type, in case the
+ ** schema is reset before this virtual machine is deleted.
+ */
+ sqlite3VdbeSetColName(v, i+pEList->nExpr, zType, strlen(zType));
+ }
+}
+
+/*
+** Generate code that will tell the VDBE the names of columns
+** in the result set. This information is used to provide the
+** azCol[] values in the callback.
+*/
+static void generateColumnNames(
+ Parse *pParse, /* Parser context */
+ SrcList *pTabList, /* List of tables */
+ ExprList *pEList /* Expressions defining the result set */
+){
+ Vdbe *v = pParse->pVdbe;
+ int i, j;
+ sqlite3 *db = pParse->db;
+ int fullNames, shortNames;
+
+ /* If this is an EXPLAIN, skip this step */
+ if( pParse->explain ){
+ return;
+ }
+
+ assert( v!=0 );
+ if( pParse->colNamesSet || v==0 || sqlite3_malloc_failed ) return;
+ pParse->colNamesSet = 1;
+ fullNames = (db->flags & SQLITE_FullColNames)!=0;
+ shortNames = (db->flags & SQLITE_ShortColNames)!=0;
+ sqlite3VdbeSetNumCols(v, pEList->nExpr);
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *p;
+ p = pEList->a[i].pExpr;
+ if( p==0 ) continue;
+ if( pEList->a[i].zName ){
+ char *zName = pEList->a[i].zName;
+ sqlite3VdbeSetColName(v, i, zName, strlen(zName));
+ continue;
+ }
+ if( p->op==TK_COLUMN && pTabList ){
+ Table *pTab;
+ char *zCol;
+ int iCol = p->iColumn;
+ for(j=0; j<pTabList->nSrc && pTabList->a[j].iCursor!=p->iTable; j++){}
+ assert( j<pTabList->nSrc );
+ pTab = pTabList->a[j].pTab;
+ if( iCol<0 ) iCol = pTab->iPKey;
+ assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
+ if( iCol<0 ){
+ zCol = "_ROWID_";
+ }else{
+ zCol = pTab->aCol[iCol].zName;
+ }
+ if( !shortNames && !fullNames && p->span.z && p->span.z[0] ){
+ sqlite3VdbeSetColName(v, i, p->span.z, p->span.n);
+ }else if( fullNames || (!shortNames && pTabList->nSrc>1) ){
+ char *zName = 0;
+ char *zTab;
+
+ zTab = pTabList->a[j].zAlias;
+ if( fullNames || zTab==0 ) zTab = pTab->zName;
+ sqlite3SetString(&zName, zTab, ".", zCol, 0);
+ sqlite3VdbeSetColName(v, i, zName, P3_DYNAMIC);
+ }else{
+ sqlite3VdbeSetColName(v, i, zCol, 0);
+ }
+ }else if( p->span.z && p->span.z[0] ){
+ sqlite3VdbeSetColName(v, i, p->span.z, p->span.n);
+ /* sqlite3VdbeCompressSpace(v, addr); */
+ }else{
+ char zName[30];
+ assert( p->op!=TK_COLUMN || pTabList==0 );
+ sprintf(zName, "column%d", i+1);
+ sqlite3VdbeSetColName(v, i, zName, 0);
+ }
+ }
+ generateColumnTypes(pParse, pTabList, pEList);
+}
+
+/*
+** Name of the connection operator, used for error messages.
+*/
+static const char *selectOpName(int id){
+ char *z;
+ switch( id ){
+ case TK_ALL: z = "UNION ALL"; break;
+ case TK_INTERSECT: z = "INTERSECT"; break;
+ case TK_EXCEPT: z = "EXCEPT"; break;
+ default: z = "UNION"; break;
+ }
+ return z;
+}
+
+/*
+** Forward declaration
+*/
+static int fillInColumnList(Parse*, Select*);
+
+/*
+** Given a SELECT statement, generate a Table structure that describes
+** the result set of that SELECT.
+*/
+Table *sqlite3ResultSetOfSelect(Parse *pParse, char *zTabName, Select *pSelect){
+ Table *pTab;
+ int i, j;
+ ExprList *pEList;
+ Column *aCol, *pCol;
+
+ if( fillInColumnList(pParse, pSelect) ){
+ return 0;
+ }
+ pTab = sqliteMalloc( sizeof(Table) );
+ if( pTab==0 ){
+ return 0;
+ }
+ pTab->zName = zTabName ? sqliteStrDup(zTabName) : 0;
+ pEList = pSelect->pEList;
+ pTab->nCol = pEList->nExpr;
+ assert( pTab->nCol>0 );
+ pTab->aCol = aCol = sqliteMalloc( sizeof(pTab->aCol[0])*pTab->nCol );
+ for(i=0, pCol=aCol; i<pTab->nCol; i++, pCol++){
+ Expr *pR;
+ char *zType;
+ char *zName;
+ Expr *p = pEList->a[i].pExpr;
+ assert( p->pRight==0 || p->pRight->token.z==0 || p->pRight->token.z[0]!=0 );
+ if( (zName = pEList->a[i].zName)!=0 ){
+ zName = sqliteStrDup(zName);
+ }else if( p->op==TK_DOT
+ && (pR=p->pRight)!=0 && pR->token.z && pR->token.z[0] ){
+ int cnt;
+ zName = sqlite3MPrintf("%T", &pR->token);
+ for(j=cnt=0; j<i; j++){
+ if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
+ sqliteFree(zName);
+ zName = sqlite3MPrintf("%T_%d", &pR->token, ++cnt);
+ j = -1;
+ }
+ }
+ }else if( p->span.z && p->span.z[0] ){
+ zName = sqlite3MPrintf("%T", &p->span);
+ }else{
+ zName = sqlite3MPrintf("column%d", i+1);
+ }
+ sqlite3Dequote(zName);
+ pCol->zName = zName;
+
+ zType = sqliteStrDup(columnType(pParse, pSelect->pSrc ,p));
+ pCol->zType = zType;
+ pCol->affinity = SQLITE_AFF_NUMERIC;
+ if( zType ){
+ pCol->affinity = sqlite3AffinityType(zType, strlen(zType));
+ }
+ pCol->pColl = sqlite3ExprCollSeq(pParse, p);
+ if( !pCol->pColl ){
+ pCol->pColl = pParse->db->pDfltColl;
+ }
+ }
+ pTab->iPKey = -1;
+ return pTab;
+}
+
+/*
+** For the given SELECT statement, do three things.
+**
+** (1) Fill in the pTabList->a[].pTab fields in the SrcList that
+** defines the set of tables that should be scanned. For views,
+** fill pTabList->a[].pSelect with a copy of the SELECT statement
+** that implements the view. A copy is made of the view's SELECT
+** statement so that we can freely modify or delete that statement
+** without worrying about messing up the presistent representation
+** of the view.
+**
+** (2) Add terms to the WHERE clause to accomodate the NATURAL keyword
+** on joins and the ON and USING clause of joins.
+**
+** (3) Scan the list of columns in the result set (pEList) looking
+** for instances of the "*" operator or the TABLE.* operator.
+** If found, expand each "*" to be every column in every table
+** and TABLE.* to be every column in TABLE.
+**
+** Return 0 on success. If there are problems, leave an error message
+** in pParse and return non-zero.
+*/
+static int fillInColumnList(Parse *pParse, Select *p){
+ int i, j, k, rc;
+ SrcList *pTabList;
+ ExprList *pEList;
+ Table *pTab;
+ struct SrcList_item *pFrom;
+
+ if( p==0 || p->pSrc==0 ) return 1;
+ pTabList = p->pSrc;
+ pEList = p->pEList;
+
+ /* Look up every table in the table list.
+ */
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ if( pFrom->pTab ){
+ /* This routine has run before! No need to continue */
+ return 0;
+ }
+ if( pFrom->zName==0 ){
+ /* A sub-query in the FROM clause of a SELECT */
+ assert( pFrom->pSelect!=0 );
+ if( pFrom->zAlias==0 ){
+ pFrom->zAlias =
+ sqlite3MPrintf("sqlite_subquery_%p_", (void*)pFrom->pSelect);
+ }
+ pFrom->pTab = pTab =
+ sqlite3ResultSetOfSelect(pParse, pFrom->zAlias, pFrom->pSelect);
+ if( pTab==0 ){
+ return 1;
+ }
+ /* The isTransient flag indicates that the Table structure has been
+ ** dynamically allocated and may be freed at any time. In other words,
+ ** pTab is not pointing to a persistent table structure that defines
+ ** part of the schema. */
+ pTab->isTransient = 1;
+ }else{
+ /* An ordinary table or view name in the FROM clause */
+ pFrom->pTab = pTab =
+ sqlite3LocateTable(pParse,pFrom->zName,pFrom->zDatabase);
+ if( pTab==0 ){
+ return 1;
+ }
+ if( pTab->pSelect ){
+ /* We reach here if the named table is a really a view */
+ if( sqlite3ViewGetColumnNames(pParse, pTab) ){
+ return 1;
+ }
+ /* If pFrom->pSelect!=0 it means we are dealing with a
+ ** view within a view. The SELECT structure has already been
+ ** copied by the outer view so we can skip the copy step here
+ ** in the inner view.
+ */
+ if( pFrom->pSelect==0 ){
+ pFrom->pSelect = sqlite3SelectDup(pTab->pSelect);
+ }
+ }
+ }
+ }
+
+ /* Process NATURAL keywords, and ON and USING clauses of joins.
+ */
+ if( sqliteProcessJoin(pParse, p) ) return 1;
+
+ /* For every "*" that occurs in the column list, insert the names of
+ ** all columns in all tables. And for every TABLE.* insert the names
+ ** of all columns in TABLE. The parser inserted a special expression
+ ** with the TK_ALL operator for each "*" that it found in the column list.
+ ** The following code just has to locate the TK_ALL expressions and expand
+ ** each one to the list of all columns in all tables.
+ **
+ ** The first loop just checks to see if there are any "*" operators
+ ** that need expanding.
+ */
+ for(k=0; k<pEList->nExpr; k++){
+ Expr *pE = pEList->a[k].pExpr;
+ if( pE->op==TK_ALL ) break;
+ if( pE->op==TK_DOT && pE->pRight && pE->pRight->op==TK_ALL
+ && pE->pLeft && pE->pLeft->op==TK_ID ) break;
+ }
+ rc = 0;
+ if( k<pEList->nExpr ){
+ /*
+ ** If we get here it means the result set contains one or more "*"
+ ** operators that need to be expanded. Loop through each expression
+ ** in the result set and expand them one by one.
+ */
+ struct ExprList_item *a = pEList->a;
+ ExprList *pNew = 0;
+ for(k=0; k<pEList->nExpr; k++){
+ Expr *pE = a[k].pExpr;
+ if( pE->op!=TK_ALL &&
+ (pE->op!=TK_DOT || pE->pRight==0 || pE->pRight->op!=TK_ALL) ){
+ /* This particular expression does not need to be expanded.
+ */
+ pNew = sqlite3ExprListAppend(pNew, a[k].pExpr, 0);
+ pNew->a[pNew->nExpr-1].zName = a[k].zName;
+ a[k].pExpr = 0;
+ a[k].zName = 0;
+ }else{
+ /* This expression is a "*" or a "TABLE.*" and needs to be
+ ** expanded. */
+ int tableSeen = 0; /* Set to 1 when TABLE matches */
+ char *zTName; /* text of name of TABLE */
+ if( pE->op==TK_DOT && pE->pLeft ){
+ zTName = sqlite3NameFromToken(&pE->pLeft->token);
+ }else{
+ zTName = 0;
+ }
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ char *zTabName = pFrom->zAlias;
+ if( zTabName==0 || zTabName[0]==0 ){
+ zTabName = pTab->zName;
+ }
+ if( zTName && (zTabName==0 || zTabName[0]==0 ||
+ sqlite3StrICmp(zTName, zTabName)!=0) ){
+ continue;
+ }
+ tableSeen = 1;
+ for(j=0; j<pTab->nCol; j++){
+ Expr *pExpr, *pLeft, *pRight;
+ char *zName = pTab->aCol[j].zName;
+
+ if( i>0 ){
+ struct SrcList_item *pLeft = &pTabList->a[i-1];
+ if( (pLeft->jointype & JT_NATURAL)!=0 &&
+ columnIndex(pLeft->pTab, zName)>=0 ){
+ /* In a NATURAL join, omit the join columns from the
+ ** table on the right */
+ continue;
+ }
+ if( sqlite3IdListIndex(pLeft->pUsing, zName)>=0 ){
+ /* In a join with a USING clause, omit columns in the
+ ** using clause from the table on the right. */
+ continue;
+ }
+ }
+ pRight = sqlite3Expr(TK_ID, 0, 0, 0);
+ if( pRight==0 ) break;
+ setToken(&pRight->token, zName);
+ if( zTabName && pTabList->nSrc>1 ){
+ pLeft = sqlite3Expr(TK_ID, 0, 0, 0);
+ pExpr = sqlite3Expr(TK_DOT, pLeft, pRight, 0);
+ if( pExpr==0 ) break;
+ setToken(&pLeft->token, zTabName);
+ setToken(&pExpr->span, sqlite3MPrintf("%s.%s", zTabName, zName));
+ pExpr->span.dyn = 1;
+ pExpr->token.z = 0;
+ pExpr->token.n = 0;
+ pExpr->token.dyn = 0;
+ }else{
+ pExpr = pRight;
+ pExpr->span = pExpr->token;
+ }
+ pNew = sqlite3ExprListAppend(pNew, pExpr, 0);
+ }
+ }
+ if( !tableSeen ){
+ if( zTName ){
+ sqlite3ErrorMsg(pParse, "no such table: %s", zTName);
+ }else{
+ sqlite3ErrorMsg(pParse, "no tables specified");
+ }
+ rc = 1;
+ }
+ sqliteFree(zTName);
+ }
+ }
+ sqlite3ExprListDelete(pEList);
+ p->pEList = pNew;
+ }
+ return rc;
+}
+
+/*
+** This routine recursively unlinks the Select.pSrc.a[].pTab pointers
+** in a select structure. It just sets the pointers to NULL. This
+** routine is recursive in the sense that if the Select.pSrc.a[].pSelect
+** pointer is not NULL, this routine is called recursively on that pointer.
+**
+** This routine is called on the Select structure that defines a
+** VIEW in order to undo any bindings to tables. This is necessary
+** because those tables might be DROPed by a subsequent SQL command.
+** If the bindings are not removed, then the Select.pSrc->a[].pTab field
+** will be left pointing to a deallocated Table structure after the
+** DROP and a coredump will occur the next time the VIEW is used.
+*/
+void sqlite3SelectUnbind(Select *p){
+ int i;
+ SrcList *pSrc = p->pSrc;
+ struct SrcList_item *pItem;
+ Table *pTab;
+ if( p==0 ) return;
+ for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
+ if( (pTab = pItem->pTab)!=0 ){
+ if( pTab->isTransient ){
+ sqlite3DeleteTable(0, pTab);
+ }
+ pItem->pTab = 0;
+ if( pItem->pSelect ){
+ sqlite3SelectUnbind(pItem->pSelect);
+ }
+ }
+ }
+}
+
+/*
+** This routine associates entries in an ORDER BY expression list with
+** columns in a result. For each ORDER BY expression, the opcode of
+** the top-level node is changed to TK_COLUMN and the iColumn value of
+** the top-level node is filled in with column number and the iTable
+** value of the top-level node is filled with iTable parameter.
+**
+** If there are prior SELECT clauses, they are processed first. A match
+** in an earlier SELECT takes precedence over a later SELECT.
+**
+** Any entry that does not match is flagged as an error. The number
+** of errors is returned.
+*/
+static int matchOrderbyToColumn(
+ Parse *pParse, /* A place to leave error messages */
+ Select *pSelect, /* Match to result columns of this SELECT */
+ ExprList *pOrderBy, /* The ORDER BY values to match against columns */
+ int iTable, /* Insert this value in iTable */
+ int mustComplete /* If TRUE all ORDER BYs must match */
+){
+ int nErr = 0;
+ int i, j;
+ ExprList *pEList;
+
+ if( pSelect==0 || pOrderBy==0 ) return 1;
+ if( mustComplete ){
+ for(i=0; i<pOrderBy->nExpr; i++){ pOrderBy->a[i].done = 0; }
+ }
+ if( fillInColumnList(pParse, pSelect) ){
+ return 1;
+ }
+ if( pSelect->pPrior ){
+ if( matchOrderbyToColumn(pParse, pSelect->pPrior, pOrderBy, iTable, 0) ){
+ return 1;
+ }
+ }
+ pEList = pSelect->pEList;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ Expr *pE = pOrderBy->a[i].pExpr;
+ int iCol = -1;
+ if( pOrderBy->a[i].done ) continue;
+ if( sqlite3ExprIsInteger(pE, &iCol) ){
+ if( iCol<=0 || iCol>pEList->nExpr ){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY position %d should be between 1 and %d",
+ iCol, pEList->nExpr);
+ nErr++;
+ break;
+ }
+ if( !mustComplete ) continue;
+ iCol--;
+ }
+ for(j=0; iCol<0 && j<pEList->nExpr; j++){
+ if( pEList->a[j].zName && (pE->op==TK_ID || pE->op==TK_STRING) ){
+ char *zName, *zLabel;
+ zName = pEList->a[j].zName;
+ zLabel = sqlite3NameFromToken(&pE->token);
+ assert( zLabel!=0 );
+ if( sqlite3StrICmp(zName, zLabel)==0 ){
+ iCol = j;
+ }
+ sqliteFree(zLabel);
+ }
+ if( iCol<0 && sqlite3ExprCompare(pE, pEList->a[j].pExpr) ){
+ iCol = j;
+ }
+ }
+ if( iCol>=0 ){
+ pE->op = TK_COLUMN;
+ pE->iColumn = iCol;
+ pE->iTable = iTable;
+ pOrderBy->a[i].done = 1;
+ }
+ if( iCol<0 && mustComplete ){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY term number %d does not match any result column", i+1);
+ nErr++;
+ break;
+ }
+ }
+ return nErr;
+}
+
+/*
+** Get a VDBE for the given parser context. Create a new one if necessary.
+** If an error occurs, return NULL and leave a message in pParse.
+*/
+Vdbe *sqlite3GetVdbe(Parse *pParse){
+ Vdbe *v = pParse->pVdbe;
+ if( v==0 ){
+ v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db);
+ }
+ return v;
+}
+
+/*
+** Compute the iLimit and iOffset fields of the SELECT based on the
+** nLimit and nOffset fields. nLimit and nOffset hold the integers
+** that appear in the original SQL statement after the LIMIT and OFFSET
+** keywords. Or that hold -1 and 0 if those keywords are omitted.
+** iLimit and iOffset are the integer memory register numbers for
+** counters used to compute the limit and offset. If there is no
+** limit and/or offset, then iLimit and iOffset are negative.
+**
+** This routine changes the values if iLimit and iOffset only if
+** a limit or offset is defined by nLimit and nOffset. iLimit and
+** iOffset should have been preset to appropriate default values
+** (usually but not always -1) prior to calling this routine.
+** Only if nLimit>=0 or nOffset>0 do the limit registers get
+** redefined. The UNION ALL operator uses this property to force
+** the reuse of the same limit and offset registers across multiple
+** SELECT statements.
+*/
+static void computeLimitRegisters(Parse *pParse, Select *p){
+ /*
+ ** If the comparison is p->nLimit>0 then "LIMIT 0" shows
+ ** all rows. It is the same as no limit. If the comparision is
+ ** p->nLimit>=0 then "LIMIT 0" show no rows at all.
+ ** "LIMIT -1" always shows all rows. There is some
+ ** contraversy about what the correct behavior should be.
+ ** The current implementation interprets "LIMIT 0" to mean
+ ** no rows.
+ */
+ if( p->nLimit>=0 ){
+ int iMem = pParse->nMem++;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return;
+ sqlite3VdbeAddOp(v, OP_Integer, -p->nLimit, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1);
+ VdbeComment((v, "# LIMIT counter"));
+ p->iLimit = iMem;
+ }
+ if( p->nOffset>0 ){
+ int iMem = pParse->nMem++;
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return;
+ sqlite3VdbeAddOp(v, OP_Integer, -p->nOffset, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iMem, 1);
+ VdbeComment((v, "# OFFSET counter"));
+ p->iOffset = iMem;
+ }
+}
+
+/*
+** Generate VDBE instructions that will open a transient table that
+** will be used for an index or to store keyed results for a compound
+** select. In other words, open a transient table that needs a
+** KeyInfo structure. The number of columns in the KeyInfo is determined
+** by the result set of the SELECT statement in the second argument.
+**
+** Specifically, this routine is called to open an index table for
+** DISTINCT, UNION, INTERSECT and EXCEPT select statements (but not
+** UNION ALL).
+**
+** Make the new table a KeyAsData table if keyAsData is true.
+**
+** The value returned is the address of the OP_OpenTemp instruction.
+*/
+static int openTempIndex(Parse *pParse, Select *p, int iTab, int keyAsData){
+ KeyInfo *pKeyInfo;
+ int nColumn;
+ sqlite3 *db = pParse->db;
+ int i;
+ Vdbe *v = pParse->pVdbe;
+ int addr;
+
+ if( fillInColumnList(pParse, p) ){
+ return 0;
+ }
+ nColumn = p->pEList->nExpr;
+ pKeyInfo = sqliteMalloc( sizeof(*pKeyInfo)+nColumn*sizeof(CollSeq*) );
+ if( pKeyInfo==0 ) return 0;
+ pKeyInfo->enc = db->enc;
+ pKeyInfo->nField = nColumn;
+ for(i=0; i<nColumn; i++){
+ pKeyInfo->aColl[i] = sqlite3ExprCollSeq(pParse, p->pEList->a[i].pExpr);
+ if( !pKeyInfo->aColl[i] ){
+ pKeyInfo->aColl[i] = db->pDfltColl;
+ }
+ }
+ addr = sqlite3VdbeOp3(v, OP_OpenTemp, iTab, 0,
+ (char*)pKeyInfo, P3_KEYINFO_HANDOFF);
+ if( keyAsData ){
+ sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1);
+ }
+ return addr;
+}
+
+/*
+** Add the address "addr" to the set of all OpenTemp opcode addresses
+** that are being accumulated in p->ppOpenTemp.
+*/
+static int multiSelectOpenTempAddr(Select *p, int addr){
+ IdList *pList = *p->ppOpenTemp = sqlite3IdListAppend(*p->ppOpenTemp, 0);
+ if( pList==0 ){
+ return SQLITE_NOMEM;
+ }
+ pList->a[pList->nId-1].idx = addr;
+ return SQLITE_OK;
+}
+
+/*
+** Return the appropriate collating sequence for the iCol-th column of
+** the result set for the compound-select statement "p". Return NULL if
+** the column has no default collating sequence.
+**
+** The collating sequence for the compound select is taken from the
+** left-most term of the select that has a collating sequence.
+*/
+static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
+ CollSeq *pRet;
+ if( p->pPrior ){
+ pRet = multiSelectCollSeq(pParse, p->pPrior, iCol);
+ }else{
+ pRet = 0;
+ }
+ if( pRet==0 ){
+ pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
+ }
+ return pRet;
+}
+
+/*
+** This routine is called to process a query that is really the union
+** or intersection of two or more separate queries.
+**
+** "p" points to the right-most of the two queries. the query on the
+** left is p->pPrior. The left query could also be a compound query
+** in which case this routine will be called recursively.
+**
+** The results of the total query are to be written into a destination
+** of type eDest with parameter iParm.
+**
+** Example 1: Consider a three-way compound SQL statement.
+**
+** SELECT a FROM t1 UNION SELECT b FROM t2 UNION SELECT c FROM t3
+**
+** This statement is parsed up as follows:
+**
+** SELECT c FROM t3
+** |
+** `-----> SELECT b FROM t2
+** |
+** `------> SELECT a FROM t1
+**
+** The arrows in the diagram above represent the Select.pPrior pointer.
+** So if this routine is called with p equal to the t3 query, then
+** pPrior will be the t2 query. p->op will be TK_UNION in this case.
+**
+** Notice that because of the way SQLite parses compound SELECTs, the
+** individual selects always group from left to right.
+*/
+static int multiSelect(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The right-most of SELECTs to be coded */
+ int eDest, /* \___ Store query results as specified */
+ int iParm, /* / by these two parameters. */
+ char *aff /* If eDest is SRT_Union, the affinity string */
+){
+ int rc = SQLITE_OK; /* Success code from a subroutine */
+ Select *pPrior; /* Another SELECT immediately to our left */
+ Vdbe *v; /* Generate code to this VDBE */
+ IdList *pOpenTemp = 0;/* OP_OpenTemp opcodes that need a KeyInfo */
+ int aAddr[5]; /* Addresses of SetNumColumns operators */
+ int nAddr = 0; /* Number used */
+ int nCol; /* Number of columns in the result set */
+
+ /* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
+ ** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
+ */
+ if( p==0 || p->pPrior==0 ){
+ rc = 1;
+ goto multi_select_end;
+ }
+ pPrior = p->pPrior;
+ if( pPrior->pOrderBy ){
+ sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
+ selectOpName(p->op));
+ rc = 1;
+ goto multi_select_end;
+ }
+ if( pPrior->nLimit>=0 || pPrior->nOffset>0 ){
+ sqlite3ErrorMsg(pParse,"LIMIT clause should come after %s not before",
+ selectOpName(p->op));
+ rc = 1;
+ goto multi_select_end;
+ }
+
+ /* Make sure we have a valid query engine. If not, create a new one.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ){
+ rc = 1;
+ goto multi_select_end;
+ }
+
+ /* If *p this is the right-most select statement, then initialize
+ ** p->ppOpenTemp to point to pOpenTemp. If *p is not the right most
+ ** statement then p->ppOpenTemp will have already been initialized
+ ** by a prior call to this same procedure. Pass along the pOpenTemp
+ ** pointer to pPrior, the next statement to our left.
+ */
+ if( p->ppOpenTemp==0 ){
+ p->ppOpenTemp = &pOpenTemp;
+ }
+ pPrior->ppOpenTemp = p->ppOpenTemp;
+
+ /* Create the destination temporary table if necessary
+ */
+ if( eDest==SRT_TempTable ){
+ assert( p->pEList );
+ sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0);
+ assert( nAddr==0 );
+ aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 0);
+ eDest = SRT_Table;
+ }
+
+ /* Generate code for the left and right SELECT statements.
+ */
+ switch( p->op ){
+ case TK_ALL: {
+ if( p->pOrderBy==0 ){
+ pPrior->nLimit = p->nLimit;
+ pPrior->nOffset = p->nOffset;
+ rc = sqlite3Select(pParse, pPrior, eDest, iParm, 0, 0, 0, aff);
+ if( rc ){
+ goto multi_select_end;
+ }
+ p->pPrior = 0;
+ p->iLimit = pPrior->iLimit;
+ p->iOffset = pPrior->iOffset;
+ p->nLimit = -1;
+ p->nOffset = 0;
+ rc = sqlite3Select(pParse, p, eDest, iParm, 0, 0, 0, aff);
+ p->pPrior = pPrior;
+ if( rc ){
+ goto multi_select_end;
+ }
+ break;
+ }
+ /* For UNION ALL ... ORDER BY fall through to the next case */
+ }
+ case TK_EXCEPT:
+ case TK_UNION: {
+ int unionTab; /* Cursor number of the temporary table holding result */
+ int op = 0; /* One of the SRT_ operations to apply to self */
+ int priorOp; /* The SRT_ operation to apply to prior selects */
+ int nLimit, nOffset; /* Saved values of p->nLimit and p->nOffset */
+ ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */
+ int addr;
+
+ priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
+ if( eDest==priorOp && p->pOrderBy==0 && p->nLimit<0 && p->nOffset==0 ){
+ /* We can reuse a temporary table generated by a SELECT to our
+ ** right.
+ */
+ unionTab = iParm;
+ }else{
+ /* We will need to create our own temporary table to hold the
+ ** intermediate results.
+ */
+ unionTab = pParse->nTab++;
+ if( p->pOrderBy
+ && matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){
+ rc = 1;
+ goto multi_select_end;
+ }
+ addr = sqlite3VdbeAddOp(v, OP_OpenTemp, unionTab, 0);
+ if( p->op!=TK_ALL ){
+ rc = multiSelectOpenTempAddr(p, addr);
+ if( rc!=SQLITE_OK ){
+ goto multi_select_end;
+ }
+ sqlite3VdbeAddOp(v, OP_KeyAsData, unionTab, 1);
+ }
+ assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+ aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, unionTab, 0);
+ assert( p->pEList );
+ }
+
+ /* Code the SELECT statements to our left
+ */
+ rc = sqlite3Select(pParse, pPrior, priorOp, unionTab, 0, 0, 0, aff);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT statement
+ */
+ switch( p->op ){
+ case TK_EXCEPT: op = SRT_Except; break;
+ case TK_UNION: op = SRT_Union; break;
+ case TK_ALL: op = SRT_Table; break;
+ }
+ p->pPrior = 0;
+ pOrderBy = p->pOrderBy;
+ p->pOrderBy = 0;
+ nLimit = p->nLimit;
+ p->nLimit = -1;
+ nOffset = p->nOffset;
+ p->nOffset = 0;
+ rc = sqlite3Select(pParse, p, op, unionTab, 0, 0, 0, aff);
+ p->pPrior = pPrior;
+ p->pOrderBy = pOrderBy;
+ p->nLimit = nLimit;
+ p->nOffset = nOffset;
+ if( rc ){
+ goto multi_select_end;
+ }
+
+
+ /* Convert the data in the temporary table into whatever form
+ ** it is that we currently need.
+ */
+ if( eDest!=priorOp || unionTab!=iParm ){
+ int iCont, iBreak, iStart;
+ assert( p->pEList );
+ if( eDest==SRT_Callback ){
+ generateColumnNames(pParse, 0, p->pEList);
+ }
+ iBreak = sqlite3VdbeMakeLabel(v);
+ iCont = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Rewind, unionTab, iBreak);
+ computeLimitRegisters(pParse, p);
+ iStart = sqlite3VdbeCurrentAddr(v);
+ rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
+ p->pOrderBy, -1, eDest, iParm,
+ iCont, iBreak, 0);
+ if( rc ){
+ rc = 1;
+ goto multi_select_end;
+ }
+ sqlite3VdbeResolveLabel(v, iCont);
+ sqlite3VdbeAddOp(v, OP_Next, unionTab, iStart);
+ sqlite3VdbeResolveLabel(v, iBreak);
+ sqlite3VdbeAddOp(v, OP_Close, unionTab, 0);
+ }
+ break;
+ }
+ case TK_INTERSECT: {
+ int tab1, tab2;
+ int iCont, iBreak, iStart;
+ int nLimit, nOffset;
+ int addr;
+
+ /* INTERSECT is different from the others since it requires
+ ** two temporary tables. Hence it has its own case. Begin
+ ** by allocating the tables we will need.
+ */
+ tab1 = pParse->nTab++;
+ tab2 = pParse->nTab++;
+ if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){
+ rc = 1;
+ goto multi_select_end;
+ }
+
+ addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab1, 0);
+ rc = multiSelectOpenTempAddr(p, addr);
+ if( rc!=SQLITE_OK ){
+ goto multi_select_end;
+ }
+ sqlite3VdbeAddOp(v, OP_KeyAsData, tab1, 1);
+ assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+ aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab1, 0);
+ assert( p->pEList );
+
+ /* Code the SELECTs to our left into temporary table "tab1".
+ */
+ rc = sqlite3Select(pParse, pPrior, SRT_Union, tab1, 0, 0, 0, aff);
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Code the current SELECT into temporary table "tab2"
+ */
+ addr = sqlite3VdbeAddOp(v, OP_OpenTemp, tab2, 0);
+ rc = multiSelectOpenTempAddr(p, addr);
+ if( rc!=SQLITE_OK ){
+ goto multi_select_end;
+ }
+ sqlite3VdbeAddOp(v, OP_KeyAsData, tab2, 1);
+ assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
+ aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0);
+ p->pPrior = 0;
+ nLimit = p->nLimit;
+ p->nLimit = -1;
+ nOffset = p->nOffset;
+ p->nOffset = 0;
+ rc = sqlite3Select(pParse, p, SRT_Union, tab2, 0, 0, 0, aff);
+ p->pPrior = pPrior;
+ p->nLimit = nLimit;
+ p->nOffset = nOffset;
+ if( rc ){
+ goto multi_select_end;
+ }
+
+ /* Generate code to take the intersection of the two temporary
+ ** tables.
+ */
+ assert( p->pEList );
+ if( eDest==SRT_Callback ){
+ generateColumnNames(pParse, 0, p->pEList);
+ }
+ iBreak = sqlite3VdbeMakeLabel(v);
+ iCont = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Rewind, tab1, iBreak);
+ computeLimitRegisters(pParse, p);
+ iStart = sqlite3VdbeAddOp(v, OP_FullKey, tab1, 0);
+ sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont);
+ rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
+ p->pOrderBy, -1, eDest, iParm,
+ iCont, iBreak, 0);
+ if( rc ){
+ rc = 1;
+ goto multi_select_end;
+ }
+ sqlite3VdbeResolveLabel(v, iCont);
+ sqlite3VdbeAddOp(v, OP_Next, tab1, iStart);
+ sqlite3VdbeResolveLabel(v, iBreak);
+ sqlite3VdbeAddOp(v, OP_Close, tab2, 0);
+ sqlite3VdbeAddOp(v, OP_Close, tab1, 0);
+ break;
+ }
+ }
+
+ /* Make sure all SELECTs in the statement have the same number of elements
+ ** in their result sets.
+ */
+ assert( p->pEList && pPrior->pEList );
+ if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
+ sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
+ " do not have the same number of result columns", selectOpName(p->op));
+ rc = 1;
+ goto multi_select_end;
+ }
+
+ /* Set the number of columns in temporary tables
+ */
+ nCol = p->pEList->nExpr;
+ while( nAddr>0 ){
+ nAddr--;
+ sqlite3VdbeChangeP2(v, aAddr[nAddr], nCol);
+ }
+
+ /* Compute collating sequences used by either the ORDER BY clause or
+ ** by any temporary tables needed to implement the compound select.
+ ** Attach the KeyInfo structure to all temporary tables. Invoke the
+ ** ORDER BY processing if there is an ORDER BY clause.
+ **
+ ** This section is run by the right-most SELECT statement only.
+ ** SELECT statements to the left always skip this part. The right-most
+ ** SELECT might also skip this part if it has no ORDER BY clause and
+ ** no temp tables are required.
+ */
+ if( p->pOrderBy || (pOpenTemp && pOpenTemp->nId>0) ){
+ int i; /* Loop counter */
+ KeyInfo *pKeyInfo; /* Collating sequence for the result set */
+
+ assert( p->ppOpenTemp == &pOpenTemp );
+ pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nCol*sizeof(CollSeq*));
+ if( !pKeyInfo ){
+ rc = SQLITE_NOMEM;
+ goto multi_select_end;
+ }
+
+ pKeyInfo->enc = pParse->db->enc;
+ pKeyInfo->nField = nCol;
+
+ for(i=0; i<nCol; i++){
+ pKeyInfo->aColl[i] = multiSelectCollSeq(pParse, p, i);
+ if( !pKeyInfo->aColl[i] ){
+ pKeyInfo->aColl[i] = pParse->db->pDfltColl;
+ }
+ }
+
+ for(i=0; pOpenTemp && i<pOpenTemp->nId; i++){
+ int p3type = (i==0?P3_KEYINFO_HANDOFF:P3_KEYINFO);
+ int addr = pOpenTemp->a[i].idx;
+ sqlite3VdbeChangeP3(v, addr, (char *)pKeyInfo, p3type);
+ }
+
+ if( p->pOrderBy ){
+ struct ExprList_item *pOrderByTerm = p->pOrderBy->a;
+ for(i=0; i<p->pOrderBy->nExpr; i++, pOrderByTerm++){
+ Expr *pExpr = pOrderByTerm->pExpr;
+ char *zName = pOrderByTerm->zName;
+ assert( pExpr->op==TK_COLUMN && pExpr->iColumn<nCol );
+ assert( !pExpr->pColl );
+ if( zName ){
+ pExpr->pColl = sqlite3LocateCollSeq(pParse, zName, -1);
+ }else{
+ pExpr->pColl = pKeyInfo->aColl[pExpr->iColumn];
+ }
+ }
+ generateSortTail(pParse, p, v, p->pEList->nExpr, eDest, iParm);
+ }
+
+ if( !pOpenTemp ){
+ /* This happens for UNION ALL ... ORDER BY */
+ sqliteFree(pKeyInfo);
+ }
+ }
+
+multi_select_end:
+ if( pOpenTemp ){
+ sqlite3IdListDelete(pOpenTemp);
+ }
+ p->ppOpenTemp = 0;
+ return rc;
+}
+
+/*
+** Scan through the expression pExpr. Replace every reference to
+** a column in table number iTable with a copy of the iColumn-th
+** entry in pEList. (But leave references to the ROWID column
+** unchanged.)
+**
+** This routine is part of the flattening procedure. A subquery
+** whose result set is defined by pEList appears as entry in the
+** FROM clause of a SELECT such that the VDBE cursor assigned to that
+** FORM clause entry is iTable. This routine make the necessary
+** changes to pExpr so that it refers directly to the source table
+** of the subquery rather the result set of the subquery.
+*/
+static void substExprList(ExprList*,int,ExprList*); /* Forward Decl */
+static void substExpr(Expr *pExpr, int iTable, ExprList *pEList){
+ if( pExpr==0 ) return;
+ if( pExpr->op==TK_COLUMN && pExpr->iTable==iTable ){
+ if( pExpr->iColumn<0 ){
+ pExpr->op = TK_NULL;
+ }else{
+ Expr *pNew;
+ assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
+ assert( pExpr->pLeft==0 && pExpr->pRight==0 && pExpr->pList==0 );
+ pNew = pEList->a[pExpr->iColumn].pExpr;
+ assert( pNew!=0 );
+ pExpr->op = pNew->op;
+ assert( pExpr->pLeft==0 );
+ pExpr->pLeft = sqlite3ExprDup(pNew->pLeft);
+ assert( pExpr->pRight==0 );
+ pExpr->pRight = sqlite3ExprDup(pNew->pRight);
+ assert( pExpr->pList==0 );
+ pExpr->pList = sqlite3ExprListDup(pNew->pList);
+ pExpr->iTable = pNew->iTable;
+ pExpr->iColumn = pNew->iColumn;
+ pExpr->iAgg = pNew->iAgg;
+ sqlite3TokenCopy(&pExpr->token, &pNew->token);
+ sqlite3TokenCopy(&pExpr->span, &pNew->span);
+ }
+ }else{
+ substExpr(pExpr->pLeft, iTable, pEList);
+ substExpr(pExpr->pRight, iTable, pEList);
+ substExprList(pExpr->pList, iTable, pEList);
+ }
+}
+static void
+substExprList(ExprList *pList, int iTable, ExprList *pEList){
+ int i;
+ if( pList==0 ) return;
+ for(i=0; i<pList->nExpr; i++){
+ substExpr(pList->a[i].pExpr, iTable, pEList);
+ }
+}
+
+/*
+** This routine attempts to flatten subqueries in order to speed
+** execution. It returns 1 if it makes changes and 0 if no flattening
+** occurs.
+**
+** To understand the concept of flattening, consider the following
+** query:
+**
+** SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5
+**
+** The default way of implementing this query is to execute the
+** subquery first and store the results in a temporary table, then
+** run the outer query on that temporary table. This requires two
+** passes over the data. Furthermore, because the temporary table
+** has no indices, the WHERE clause on the outer query cannot be
+** optimized.
+**
+** This routine attempts to rewrite queries such as the above into
+** a single flat select, like this:
+**
+** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
+**
+** The code generated for this simpification gives the same result
+** but only has to scan the data once. And because indices might
+** exist on the table t1, a complete scan of the data might be
+** avoided.
+**
+** Flattening is only attempted if all of the following are true:
+**
+** (1) The subquery and the outer query do not both use aggregates.
+**
+** (2) The subquery is not an aggregate or the outer query is not a join.
+**
+** (3) The subquery is not the right operand of a left outer join, or
+** the subquery is not itself a join. (Ticket #306)
+**
+** (4) The subquery is not DISTINCT or the outer query is not a join.
+**
+** (5) The subquery is not DISTINCT or the outer query does not use
+** aggregates.
+**
+** (6) The subquery does not use aggregates or the outer query is not
+** DISTINCT.
+**
+** (7) The subquery has a FROM clause.
+**
+** (8) The subquery does not use LIMIT or the outer query is not a join.
+**
+** (9) The subquery does not use LIMIT or the outer query does not use
+** aggregates.
+**
+** (10) The subquery does not use aggregates or the outer query does not
+** use LIMIT.
+**
+** (11) The subquery and the outer query do not both have ORDER BY clauses.
+**
+** (12) The subquery is not the right term of a LEFT OUTER JOIN or the
+** subquery has no WHERE clause. (added by ticket #350)
+**
+** In this routine, the "p" parameter is a pointer to the outer query.
+** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
+** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
+**
+** If flattening is not attempted, this routine is a no-op and returns 0.
+** If flattening is attempted this routine returns 1.
+**
+** All of the expression analysis must occur on both the outer query and
+** the subquery before this routine runs.
+*/
+static int flattenSubquery(
+ Parse *pParse, /* The parsing context */
+ Select *p, /* The parent or outer SELECT statement */
+ int iFrom, /* Index in p->pSrc->a[] of the inner subquery */
+ int isAgg, /* True if outer SELECT uses aggregate functions */
+ int subqueryIsAgg /* True if the subquery uses aggregate functions */
+){
+ Select *pSub; /* The inner query or "subquery" */
+ SrcList *pSrc; /* The FROM clause of the outer query */
+ SrcList *pSubSrc; /* The FROM clause of the subquery */
+ ExprList *pList; /* The result set of the outer query */
+ int iParent; /* VDBE cursor number of the pSub result set temp table */
+ int i; /* Loop counter */
+ Expr *pWhere; /* The WHERE clause */
+ struct SrcList_item *pSubitem; /* The subquery */
+
+ /* Check to see if flattening is permitted. Return 0 if not.
+ */
+ if( p==0 ) return 0;
+ pSrc = p->pSrc;
+ assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
+ pSubitem = &pSrc->a[iFrom];
+ pSub = pSubitem->pSelect;
+ assert( pSub!=0 );
+ if( isAgg && subqueryIsAgg ) return 0;
+ if( subqueryIsAgg && pSrc->nSrc>1 ) return 0;
+ pSubSrc = pSub->pSrc;
+ assert( pSubSrc );
+ if( pSubSrc->nSrc==0 ) return 0;
+ if( (pSub->isDistinct || pSub->nLimit>=0) && (pSrc->nSrc>1 || isAgg) ){
+ return 0;
+ }
+ if( (p->isDistinct || p->nLimit>=0) && subqueryIsAgg ) return 0;
+ if( p->pOrderBy && pSub->pOrderBy ) return 0;
+
+ /* Restriction 3: If the subquery is a join, make sure the subquery is
+ ** not used as the right operand of an outer join. Examples of why this
+ ** is not allowed:
+ **
+ ** t1 LEFT OUTER JOIN (t2 JOIN t3)
+ **
+ ** If we flatten the above, we would get
+ **
+ ** (t1 LEFT OUTER JOIN t2) JOIN t3
+ **
+ ** which is not at all the same thing.
+ */
+ if( pSubSrc->nSrc>1 && iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0 ){
+ return 0;
+ }
+
+ /* Restriction 12: If the subquery is the right operand of a left outer
+ ** join, make sure the subquery has no WHERE clause.
+ ** An examples of why this is not allowed:
+ **
+ ** t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0)
+ **
+ ** If we flatten the above, we would get
+ **
+ ** (t1 LEFT OUTER JOIN t2) WHERE t2.x>0
+ **
+ ** But the t2.x>0 test will always fail on a NULL row of t2, which
+ ** effectively converts the OUTER JOIN into an INNER JOIN.
+ */
+ if( iFrom>0 && (pSrc->a[iFrom-1].jointype & JT_OUTER)!=0
+ && pSub->pWhere!=0 ){
+ return 0;
+ }
+
+ /* If we reach this point, it means flattening is permitted for the
+ ** iFrom-th entry of the FROM clause in the outer query.
+ */
+
+ /* Move all of the FROM elements of the subquery into the
+ ** the FROM clause of the outer query. Before doing this, remember
+ ** the cursor number for the original outer query FROM element in
+ ** iParent. The iParent cursor will never be used. Subsequent code
+ ** will scan expressions looking for iParent references and replace
+ ** those references with expressions that resolve to the subquery FROM
+ ** elements we are now copying in.
+ */
+ iParent = pSubitem->iCursor;
+ {
+ int nSubSrc = pSubSrc->nSrc;
+ int jointype = pSubitem->jointype;
+ Table *pTab = pSubitem->pTab;
+
+ if( pTab && pTab->isTransient ){
+ sqlite3DeleteTable(0, pSubitem->pTab);
+ }
+ sqliteFree(pSubitem->zDatabase);
+ sqliteFree(pSubitem->zName);
+ sqliteFree(pSubitem->zAlias);
+ if( nSubSrc>1 ){
+ int extra = nSubSrc - 1;
+ for(i=1; i<nSubSrc; i++){
+ pSrc = sqlite3SrcListAppend(pSrc, 0, 0);
+ }
+ p->pSrc = pSrc;
+ for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){
+ pSrc->a[i] = pSrc->a[i-extra];
+ }
+ }
+ for(i=0; i<nSubSrc; i++){
+ pSrc->a[i+iFrom] = pSubSrc->a[i];
+ memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
+ }
+ pSrc->a[iFrom+nSubSrc-1].jointype = jointype;
+ }
+
+ /* Now begin substituting subquery result set expressions for
+ ** references to the iParent in the outer query.
+ **
+ ** Example:
+ **
+ ** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
+ ** \ \_____________ subquery __________/ /
+ ** \_____________________ outer query ______________________________/
+ **
+ ** We look at every expression in the outer query and every place we see
+ ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
+ */
+ substExprList(p->pEList, iParent, pSub->pEList);
+ pList = p->pEList;
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pExpr;
+ if( pList->a[i].zName==0 && (pExpr = pList->a[i].pExpr)->span.z!=0 ){
+ pList->a[i].zName = sqliteStrNDup(pExpr->span.z, pExpr->span.n);
+ }
+ }
+ if( isAgg ){
+ substExprList(p->pGroupBy, iParent, pSub->pEList);
+ substExpr(p->pHaving, iParent, pSub->pEList);
+ }
+ if( pSub->pOrderBy ){
+ assert( p->pOrderBy==0 );
+ p->pOrderBy = pSub->pOrderBy;
+ pSub->pOrderBy = 0;
+ }else if( p->pOrderBy ){
+ substExprList(p->pOrderBy, iParent, pSub->pEList);
+ }
+ if( pSub->pWhere ){
+ pWhere = sqlite3ExprDup(pSub->pWhere);
+ }else{
+ pWhere = 0;
+ }
+ if( subqueryIsAgg ){
+ assert( p->pHaving==0 );
+ p->pHaving = p->pWhere;
+ p->pWhere = pWhere;
+ substExpr(p->pHaving, iParent, pSub->pEList);
+ p->pHaving = sqlite3ExprAnd(p->pHaving, sqlite3ExprDup(pSub->pHaving));
+ assert( p->pGroupBy==0 );
+ p->pGroupBy = sqlite3ExprListDup(pSub->pGroupBy);
+ }else{
+ substExpr(p->pWhere, iParent, pSub->pEList);
+ p->pWhere = sqlite3ExprAnd(p->pWhere, pWhere);
+ }
+
+ /* The flattened query is distinct if either the inner or the
+ ** outer query is distinct.
+ */
+ p->isDistinct = p->isDistinct || pSub->isDistinct;
+
+ /* Transfer the limit expression from the subquery to the outer
+ ** query.
+ */
+ if( pSub->nLimit>=0 ){
+ if( p->nLimit<0 ){
+ p->nLimit = pSub->nLimit;
+ }else if( p->nLimit+p->nOffset > pSub->nLimit+pSub->nOffset ){
+ p->nLimit = pSub->nLimit + pSub->nOffset - p->nOffset;
+ }
+ }
+ p->nOffset += pSub->nOffset;
+
+ /* Finially, delete what is left of the subquery and return
+ ** success.
+ */
+ sqlite3SelectDelete(pSub);
+ return 1;
+}
+
+/*
+** Analyze the SELECT statement passed in as an argument to see if it
+** is a simple min() or max() query. If it is and this query can be
+** satisfied using a single seek to the beginning or end of an index,
+** then generate the code for this SELECT and return 1. If this is not a
+** simple min() or max() query, then return 0;
+**
+** A simply min() or max() query looks like this:
+**
+** SELECT min(a) FROM table;
+** SELECT max(a) FROM table;
+**
+** The query may have only a single table in its FROM argument. There
+** can be no GROUP BY or HAVING or WHERE clauses. The result set must
+** be the min() or max() of a single column of the table. The column
+** in the min() or max() function must be indexed.
+**
+** The parameters to this routine are the same as for sqlite3Select().
+** See the header comment on that routine for additional information.
+*/
+static int simpleMinMaxQuery(Parse *pParse, Select *p, int eDest, int iParm){
+ Expr *pExpr;
+ int iCol;
+ Table *pTab;
+ Index *pIdx;
+ int base;
+ Vdbe *v;
+ int seekOp;
+ int cont;
+ ExprList *pEList, *pList, eList;
+ struct ExprList_item eListItem;
+ SrcList *pSrc;
+
+
+ /* Check to see if this query is a simple min() or max() query. Return
+ ** zero if it is not.
+ */
+ if( p->pGroupBy || p->pHaving || p->pWhere ) return 0;
+ pSrc = p->pSrc;
+ if( pSrc->nSrc!=1 ) return 0;
+ pEList = p->pEList;
+ if( pEList->nExpr!=1 ) return 0;
+ pExpr = pEList->a[0].pExpr;
+ if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
+ pList = pExpr->pList;
+ if( pList==0 || pList->nExpr!=1 ) return 0;
+ if( pExpr->token.n!=3 ) return 0;
+ if( sqlite3StrNICmp(pExpr->token.z,"min",3)==0 ){
+ seekOp = OP_Rewind;
+ }else if( sqlite3StrNICmp(pExpr->token.z,"max",3)==0 ){
+ seekOp = OP_Last;
+ }else{
+ return 0;
+ }
+ pExpr = pList->a[0].pExpr;
+ if( pExpr->op!=TK_COLUMN ) return 0;
+ iCol = pExpr->iColumn;
+ pTab = pSrc->a[0].pTab;
+
+ /* If we get to here, it means the query is of the correct form.
+ ** Check to make sure we have an index and make pIdx point to the
+ ** appropriate index. If the min() or max() is on an INTEGER PRIMARY
+ ** key column, no index is necessary so set pIdx to NULL. If no
+ ** usable index is found, return 0.
+ */
+ if( iCol<0 ){
+ pIdx = 0;
+ }else{
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ assert( pIdx->nColumn>=1 );
+ if( pIdx->aiColumn[0]==iCol && pIdx->keyInfo.aColl[0]==pColl ) break;
+ }
+ if( pIdx==0 ) return 0;
+ }
+
+ /* Identify column types if we will be using the callback. This
+ ** step is skipped if the output is going to a table or a memory cell.
+ ** The column names have already been generated in the calling function.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) return 0;
+
+ /* If the output is destined for a temporary table, open that table.
+ */
+ if( eDest==SRT_TempTable ){
+ sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 1);
+ }
+
+ /* Generating code to find the min or the max. Basically all we have
+ ** to do is find the first or the last entry in the chosen index. If
+ ** the min() or max() is on the INTEGER PRIMARY KEY, then find the first
+ ** or last entry in the main table.
+ */
+ sqlite3CodeVerifySchema(pParse, pTab->iDb);
+ base = pSrc->a[0].iCursor;
+ computeLimitRegisters(pParse, p);
+ if( pSrc->a[0].pSelect==0 ){
+ sqlite3OpenTableForReading(v, base, pTab);
+ }
+ cont = sqlite3VdbeMakeLabel(v);
+ if( pIdx==0 ){
+ sqlite3VdbeAddOp(v, seekOp, base, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqlite3VdbeOp3(v, OP_OpenRead, base+1, pIdx->tnum,
+ (char*)&pIdx->keyInfo, P3_KEYINFO);
+ if( seekOp==OP_Rewind ){
+ sqlite3VdbeAddOp(v, OP_String, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MakeRecord, 1, 0);
+ seekOp = OP_MoveGt;
+ }
+ sqlite3VdbeAddOp(v, seekOp, base+1, 0);
+ sqlite3VdbeAddOp(v, OP_IdxRecno, base+1, 0);
+ sqlite3VdbeAddOp(v, OP_Close, base+1, 0);
+ sqlite3VdbeAddOp(v, OP_MoveGe, base, 0);
+ }
+ eList.nExpr = 1;
+ memset(&eListItem, 0, sizeof(eListItem));
+ eList.a = &eListItem;
+ eList.a[0].pExpr = pExpr;
+ selectInnerLoop(pParse, p, &eList, 0, 0, 0, -1, eDest, iParm, cont, cont, 0);
+ sqlite3VdbeResolveLabel(v, cont);
+ sqlite3VdbeAddOp(v, OP_Close, base, 0);
+
+ return 1;
+}
+
+/*
+** Analyze and ORDER BY or GROUP BY clause in a SELECT statement. Return
+** the number of errors seen.
+**
+** An ORDER BY or GROUP BY is a list of expressions. If any expression
+** is an integer constant, then that expression is replaced by the
+** corresponding entry in the result set.
+*/
+static int processOrderGroupBy(
+ Parse *pParse, /* Parsing context */
+ ExprList *pOrderBy, /* The ORDER BY or GROUP BY clause to be processed */
+ SrcList *pTabList, /* The FROM clause */
+ ExprList *pEList, /* The result set */
+ int isAgg, /* True if aggregate functions are involved */
+ const char *zType /* Either "ORDER" or "GROUP", as appropriate */
+){
+ int i;
+ if( pOrderBy==0 ) return 0;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ int iCol;
+ Expr *pE = pOrderBy->a[i].pExpr;
+ if( sqlite3ExprIsInteger(pE, &iCol) && iCol>0 && iCol<=pEList->nExpr ){
+ sqlite3ExprDelete(pE);
+ pE = pOrderBy->a[i].pExpr = sqlite3ExprDup(pEList->a[iCol-1].pExpr);
+ }
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList, pE, isAgg, 0) ){
+ return 1;
+ }
+ if( sqlite3ExprIsConstant(pE) ){
+ if( sqlite3ExprIsInteger(pE, &iCol)==0 ){
+ sqlite3ErrorMsg(pParse,
+ "%s BY terms must not be non-integer constants", zType);
+ return 1;
+ }else if( iCol<=0 || iCol>pEList->nExpr ){
+ sqlite3ErrorMsg(pParse,
+ "%s BY column number %d out of range - should be "
+ "between 1 and %d", zType, iCol, pEList->nExpr);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Generate code for the given SELECT statement.
+**
+** The results are distributed in various ways depending on the
+** value of eDest and iParm.
+**
+** eDest Value Result
+** ------------ -------------------------------------------
+** SRT_Callback Invoke the callback for each row of the result.
+**
+** SRT_Mem Store first result in memory cell iParm
+**
+** SRT_Set Store results as keys of table iParm.
+**
+** SRT_Union Store results as a key in a temporary table iParm
+**
+** SRT_Except Remove results from the temporary table iParm.
+**
+** SRT_Table Store results in temporary table iParm
+**
+** The table above is incomplete. Additional eDist value have be added
+** since this comment was written. See the selectInnerLoop() function for
+** a complete listing of the allowed values of eDest and their meanings.
+**
+** This routine returns the number of errors. If any errors are
+** encountered, then an appropriate error message is left in
+** pParse->zErrMsg.
+**
+** This routine does NOT free the Select structure passed in. The
+** calling function needs to do that.
+**
+** The pParent, parentTab, and *pParentAgg fields are filled in if this
+** SELECT is a subquery. This routine may try to combine this SELECT
+** with its parent to form a single flat query. In so doing, it might
+** change the parent query from a non-aggregate to an aggregate query.
+** For that reason, the pParentAgg flag is passed as a pointer, so it
+** can be changed.
+**
+** Example 1: The meaning of the pParent parameter.
+**
+** SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3;
+** \ \_______ subquery _______/ /
+** \ /
+** \____________________ outer query ___________________/
+**
+** This routine is called for the outer query first. For that call,
+** pParent will be NULL. During the processing of the outer query, this
+** routine is called recursively to handle the subquery. For the recursive
+** call, pParent will point to the outer query. Because the subquery is
+** the second element in a three-way join, the parentTab parameter will
+** be 1 (the 2nd value of a 0-indexed array.)
+*/
+int sqlite3Select(
+ Parse *pParse, /* The parser context */
+ Select *p, /* The SELECT statement being coded. */
+ int eDest, /* How to dispose of the results */
+ int iParm, /* A parameter used by the eDest disposal method */
+ Select *pParent, /* Another SELECT for which this is a sub-query */
+ int parentTab, /* Index in pParent->pSrc of this query */
+ int *pParentAgg, /* True if pParent uses aggregate functions */
+ char *aff /* If eDest is SRT_Union, the affinity string */
+){
+ int i;
+ WhereInfo *pWInfo;
+ Vdbe *v;
+ int isAgg = 0; /* True for select lists like "count(*)" */
+ ExprList *pEList; /* List of columns to extract. */
+ SrcList *pTabList; /* List of tables to select from */
+ Expr *pWhere; /* The WHERE clause. May be NULL */
+ ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
+ ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
+ Expr *pHaving; /* The HAVING clause. May be NULL */
+ int isDistinct; /* True if the DISTINCT keyword is present */
+ int distinct; /* Table to use for the distinct set */
+ int rc = 1; /* Value to return from this function */
+
+ if( sqlite3_malloc_failed || pParse->nErr || p==0 ) return 1;
+ if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
+
+ /* If there is are a sequence of queries, do the earlier ones first.
+ */
+ if( p->pPrior ){
+ return multiSelect(pParse, p, eDest, iParm, aff);
+ }
+
+ /* Make local copies of the parameters for this query.
+ */
+ pTabList = p->pSrc;
+ pWhere = p->pWhere;
+ pOrderBy = p->pOrderBy;
+ pGroupBy = p->pGroupBy;
+ pHaving = p->pHaving;
+ isDistinct = p->isDistinct;
+
+ /* Allocate VDBE cursors for each table in the FROM clause
+ */
+ sqlite3SrcListAssignCursors(pParse, pTabList);
+
+ /*
+ ** Do not even attempt to generate any code if we have already seen
+ ** errors before this routine starts.
+ */
+ if( pParse->nErr>0 ) goto select_end;
+
+ /* Expand any "*" terms in the result set. (For example the "*" in
+ ** "SELECT * FROM t1") The fillInColumnlist() routine also does some
+ ** other housekeeping - see the header comment for details.
+ */
+ if( fillInColumnList(pParse, p) ){
+ goto select_end;
+ }
+ pWhere = p->pWhere;
+ pEList = p->pEList;
+ if( pEList==0 ) goto select_end;
+
+ /* If writing to memory or generating a set
+ ** only a single column may be output.
+ */
+ if( (eDest==SRT_Mem || eDest==SRT_Set) && pEList->nExpr>1 ){
+ sqlite3ErrorMsg(pParse, "only a single result allowed for "
+ "a SELECT that is part of an expression");
+ goto select_end;
+ }
+
+ /* ORDER BY is ignored for some destinations.
+ */
+ switch( eDest ){
+ case SRT_Union:
+ case SRT_Except:
+ case SRT_Discard:
+ pOrderBy = 0;
+ break;
+ default:
+ break;
+ }
+
+ /* At this point, we should have allocated all the cursors that we
+ ** need to handle subquerys and temporary tables.
+ **
+ ** Resolve the column names and do a semantics check on all the expressions.
+ */
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pEList->a[i].pExpr,
+ 1, &isAgg) ){
+ goto select_end;
+ }
+ }
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList, pWhere, 0, 0) ){
+ goto select_end;
+ }
+ if( pHaving ){
+ if( pGroupBy==0 ){
+ sqlite3ErrorMsg(pParse, "a GROUP BY clause is required before HAVING");
+ goto select_end;
+ }
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, pEList,pHaving,1,&isAgg) ){
+ goto select_end;
+ }
+ }
+ if( processOrderGroupBy(pParse, pOrderBy, pTabList, pEList, isAgg, "ORDER")
+ || processOrderGroupBy(pParse, pGroupBy, pTabList, pEList, isAgg, "GROUP")
+ ){
+ goto select_end;
+ }
+
+ /* Begin generating code.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto select_end;
+
+ /* Identify column names if we will be using them in a callback. This
+ ** step is skipped if the output is going to some other destination.
+ */
+ if( eDest==SRT_Callback ){
+ generateColumnNames(pParse, pTabList, pEList);
+ }
+
+ /* Generate code for all sub-queries in the FROM clause
+ */
+ for(i=0; i<pTabList->nSrc; i++){
+ const char *zSavedAuthContext = 0;
+ int needRestoreContext;
+
+ if( pTabList->a[i].pSelect==0 ) continue;
+ if( pTabList->a[i].zName!=0 ){
+ zSavedAuthContext = pParse->zAuthContext;
+ pParse->zAuthContext = pTabList->a[i].zName;
+ needRestoreContext = 1;
+ }else{
+ needRestoreContext = 0;
+ }
+ sqlite3Select(pParse, pTabList->a[i].pSelect, SRT_TempTable,
+ pTabList->a[i].iCursor, p, i, &isAgg, 0);
+ if( needRestoreContext ){
+ pParse->zAuthContext = zSavedAuthContext;
+ }
+ pTabList = p->pSrc;
+ pWhere = p->pWhere;
+ if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){
+ pOrderBy = p->pOrderBy;
+ }
+ pGroupBy = p->pGroupBy;
+ pHaving = p->pHaving;
+ isDistinct = p->isDistinct;
+ }
+
+ /* Check for the special case of a min() or max() function by itself
+ ** in the result set.
+ */
+ if( simpleMinMaxQuery(pParse, p, eDest, iParm) ){
+ rc = 0;
+ goto select_end;
+ }
+
+ /* Check to see if this is a subquery that can be "flattened" into its parent.
+ ** If flattening is a possiblity, do so and return immediately.
+ */
+ if( pParent && pParentAgg &&
+ flattenSubquery(pParse, pParent, parentTab, *pParentAgg, isAgg) ){
+ if( isAgg ) *pParentAgg = 1;
+ return rc;
+ }
+
+ /* If there is an ORDER BY clause, resolve any collation sequences
+ ** names that have been explicitly specified.
+ */
+ if( pOrderBy ){
+ for(i=0; i<pOrderBy->nExpr; i++){
+ if( pOrderBy->a[i].zName ){
+ pOrderBy->a[i].pExpr->pColl =
+ sqlite3LocateCollSeq(pParse, pOrderBy->a[i].zName, -1);
+ }
+ }
+ if( pParse->nErr ){
+ goto select_end;
+ }
+ }
+
+ /* Set the limiter.
+ */
+ computeLimitRegisters(pParse, p);
+
+ /* If the output is destined for a temporary table, open that table.
+ */
+ if( eDest==SRT_TempTable ){
+ sqlite3VdbeAddOp(v, OP_OpenTemp, iParm, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, pEList->nExpr);
+ }
+
+ /* Do an analysis of aggregate expressions.
+ */
+ sqliteAggregateInfoReset(pParse);
+ if( isAgg || pGroupBy ){
+ assert( pParse->nAgg==0 );
+ isAgg = 1;
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprAnalyzeAggregates(pParse, pEList->a[i].pExpr) ){
+ goto select_end;
+ }
+ }
+ if( pGroupBy ){
+ for(i=0; i<pGroupBy->nExpr; i++){
+ if( sqlite3ExprAnalyzeAggregates(pParse, pGroupBy->a[i].pExpr) ){
+ goto select_end;
+ }
+ }
+ }
+ if( pHaving && sqlite3ExprAnalyzeAggregates(pParse, pHaving) ){
+ goto select_end;
+ }
+ if( pOrderBy ){
+ for(i=0; i<pOrderBy->nExpr; i++){
+ if( sqlite3ExprAnalyzeAggregates(pParse, pOrderBy->a[i].pExpr) ){
+ goto select_end;
+ }
+ }
+ }
+ }
+
+ /* Reset the aggregator
+ */
+ if( isAgg ){
+ int addr = sqlite3VdbeAddOp(v, OP_AggReset, (pGroupBy?0:1), pParse->nAgg);
+ for(i=0; i<pParse->nAgg; i++){
+ FuncDef *pFunc;
+ if( (pFunc = pParse->aAgg[i].pFunc)!=0 && pFunc->xFinalize!=0 ){
+ sqlite3VdbeOp3(v, OP_AggInit, 0, i, (char*)pFunc, P3_FUNCDEF);
+ }
+ }
+ if( pGroupBy ){
+ int sz = sizeof(KeyInfo) + pGroupBy->nExpr*sizeof(CollSeq*);
+ KeyInfo *pKey = (KeyInfo *)sqliteMalloc(sz);
+ if( 0==pKey ){
+ goto select_end;
+ }
+ pKey->enc = pParse->db->enc;
+ pKey->nField = pGroupBy->nExpr;
+ for(i=0; i<pGroupBy->nExpr; i++){
+ pKey->aColl[i] = sqlite3ExprCollSeq(pParse, pGroupBy->a[i].pExpr);
+ if( !pKey->aColl[i] ){
+ pKey->aColl[i] = pParse->db->pDfltColl;
+ }
+ }
+ sqlite3VdbeChangeP3(v, addr, (char *)pKey, P3_KEYINFO_HANDOFF);
+ }
+ }
+
+ /* Initialize the memory cell to NULL
+ */
+ if( eDest==SRT_Mem ){
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
+ }
+
+ /* Open a temporary table to use for the distinct set.
+ */
+ if( isDistinct ){
+ distinct = pParse->nTab++;
+ openTempIndex(pParse, p, distinct, 0);
+ }else{
+ distinct = -1;
+ }
+
+ /* Begin the database scan
+ */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0,
+ pGroupBy ? 0 : &pOrderBy);
+ if( pWInfo==0 ) goto select_end;
+
+ /* Use the standard inner loop if we are not dealing with
+ ** aggregates
+ */
+ if( !isAgg ){
+ if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
+ iParm, pWInfo->iContinue, pWInfo->iBreak, aff) ){
+ goto select_end;
+ }
+ }
+
+ /* If we are dealing with aggregates, then do the special aggregate
+ ** processing.
+ */
+ else{
+ AggExpr *pAgg;
+ if( pGroupBy ){
+ int lbl1;
+ for(i=0; i<pGroupBy->nExpr; i++){
+ sqlite3ExprCode(pParse, pGroupBy->a[i].pExpr);
+ }
+ /* No affinity string is attached to the following OP_MakeRecord
+ ** because we do not need to do any coercion of datatypes. */
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pGroupBy->nExpr, 0);
+ lbl1 = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_AggFocus, 0, lbl1);
+ for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
+ if( pAgg->isAgg ) continue;
+ sqlite3ExprCode(pParse, pAgg->pExpr);
+ sqlite3VdbeAddOp(v, OP_AggSet, 0, i);
+ }
+ sqlite3VdbeResolveLabel(v, lbl1);
+ }
+ for(i=0, pAgg=pParse->aAgg; i<pParse->nAgg; i++, pAgg++){
+ Expr *pE;
+ int nExpr;
+ FuncDef *pDef;
+ if( !pAgg->isAgg ) continue;
+ assert( pAgg->pFunc!=0 );
+ assert( pAgg->pFunc->xStep!=0 );
+ pDef = pAgg->pFunc;
+ pE = pAgg->pExpr;
+ assert( pE!=0 );
+ assert( pE->op==TK_AGG_FUNCTION );
+ nExpr = sqlite3ExprCodeExprList(pParse, pE->pList);
+ sqlite3VdbeAddOp(v, OP_Integer, i, 0);
+ if( pDef->needCollSeq ){
+ CollSeq *pColl = 0;
+ int j;
+ for(j=0; !pColl && j<nExpr; j++){
+ pColl = sqlite3ExprCollSeq(pParse, pE->pList->a[j].pExpr);
+ }
+ if( !pColl ) pColl = pParse->db->pDfltColl;
+ sqlite3VdbeOp3(v, OP_CollSeq, 0, 0, (char *)pColl, P3_COLLSEQ);
+ }
+ sqlite3VdbeOp3(v, OP_AggFunc, 0, nExpr, (char*)pDef, P3_POINTER);
+ }
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+
+ /* If we are processing aggregates, we need to set up a second loop
+ ** over all of the aggregate values and process them.
+ */
+ if( isAgg ){
+ int endagg = sqlite3VdbeMakeLabel(v);
+ int startagg;
+ startagg = sqlite3VdbeAddOp(v, OP_AggNext, 0, endagg);
+ pParse->useAgg = 1;
+ if( pHaving ){
+ sqlite3ExprIfFalse(pParse, pHaving, startagg, 1);
+ }
+ if( selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, eDest,
+ iParm, startagg, endagg, aff) ){
+ goto select_end;
+ }
+ sqlite3VdbeAddOp(v, OP_Goto, 0, startagg);
+ sqlite3VdbeResolveLabel(v, endagg);
+ sqlite3VdbeAddOp(v, OP_Noop, 0, 0);
+ pParse->useAgg = 0;
+ }
+
+ /* If there is an ORDER BY clause, then we need to sort the results
+ ** and send them to the callback one by one.
+ */
+ if( pOrderBy ){
+ generateSortTail(pParse, p, v, pEList->nExpr, eDest, iParm);
+ }
+
+ /* If this was a subquery, we have now converted the subquery into a
+ ** temporary table. So delete the subquery structure from the parent
+ ** to prevent this subquery from being evaluated again and to force the
+ ** the use of the temporary table.
+ */
+ if( pParent ){
+ assert( pParent->pSrc->nSrc>parentTab );
+ assert( pParent->pSrc->a[parentTab].pSelect==p );
+ sqlite3SelectDelete(p);
+ pParent->pSrc->a[parentTab].pSelect = 0;
+ }
+
+ /* The SELECT was successfully coded. Set the return code to 0
+ ** to indicate no errors.
+ */
+ rc = 0;
+
+ /* Control jumps to here if an error is encountered above, or upon
+ ** successful coding of the SELECT.
+ */
+select_end:
+ sqliteAggregateInfoReset(pParse);
+ return rc;
+}
diff --git a/kopete/plugins/statistics/sqlite/shell.c b/kopete/plugins/statistics/sqlite/shell.c
new file mode 100644
index 00000000..bdd13cc9
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/shell.c
@@ -0,0 +1,1786 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code to implement the "sqlite" command line
+** utility for accessing SQLite databases.
+**
+** $Id$
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "sqlite3.h"
+#include <ctype.h>
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__)
+# include <signal.h>
+# include <pwd.h>
+# include <unistd.h>
+# include <sys/types.h>
+#endif
+
+#ifdef __MACOS__
+# include <console.h>
+# include <signal.h>
+# include <unistd.h>
+# include <extras.h>
+# include <Files.h>
+# include <Folders.h>
+#endif
+
+#if defined(HAVE_READLINE) && HAVE_READLINE==1
+# include <readline/readline.h>
+# include <readline/history.h>
+#else
+# define readline(p) local_getline(p,stdin)
+# define add_history(X)
+# define read_history(X)
+# define write_history(X)
+# define stifle_history(X)
+#endif
+
+/* Make sure isatty() has a prototype.
+*/
+extern int isatty();
+
+/*
+** The following is the open SQLite database. We make a pointer
+** to this database a static variable so that it can be accessed
+** by the SIGINT handler to interrupt database processing.
+*/
+static sqlite3 *db = 0;
+
+/*
+** True if an interrupt (Control-C) has been received.
+*/
+static int seenInterrupt = 0;
+
+/*
+** This is the name of our program. It is set in main(), used
+** in a number of other places, mostly for error messages.
+*/
+static char *Argv0;
+
+/*
+** Prompt strings. Initialized in main. Settable with
+** .prompt main continue
+*/
+static char mainPrompt[20]; /* First line prompt. default: "sqlite> "*/
+static char continuePrompt[20]; /* Continuation prompt. default: " ...> " */
+
+
+/*
+** Determines if a string is a number of not.
+*/
+static int isNumber(const unsigned char *z, int *realnum){
+ if( *z=='-' || *z=='+' ) z++;
+ if( !isdigit(*z) ){
+ return 0;
+ }
+ z++;
+ if( realnum ) *realnum = 0;
+ while( isdigit(*z) ){ z++; }
+ if( *z=='.' ){
+ z++;
+ if( !isdigit(*z) ) return 0;
+ while( isdigit(*z) ){ z++; }
+ if( realnum ) *realnum = 1;
+ }
+ if( *z=='e' || *z=='E' ){
+ z++;
+ if( *z=='+' || *z=='-' ) z++;
+ if( !isdigit(*z) ) return 0;
+ while( isdigit(*z) ){ z++; }
+ if( realnum ) *realnum = 1;
+ }
+ return *z==0;
+}
+
+/*
+** A global char* and an SQL function to access its current value
+** from within an SQL statement. This program used to use the
+** sqlite_exec_printf() API to substitue a string into an SQL statement.
+** The correct way to do this with sqlite3 is to use the bind API, but
+** since the shell is built around the callback paradigm it would be a lot
+** of work. Instead just use this hack, which is quite harmless.
+*/
+static const char *zShellStatic = 0;
+static void shellstaticFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( 0==argc );
+ assert( zShellStatic );
+ sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
+}
+
+
+/*
+** This routine reads a line of text from FILE in, stores
+** the text in memory obtained from malloc() and returns a pointer
+** to the text. NULL is returned at end of file, or if malloc()
+** fails.
+**
+** The interface is like "readline" but no command-line editing
+** is done.
+*/
+static char *local_getline(char *zPrompt, FILE *in){
+ char *zLine;
+ int nLine;
+ int n;
+ int eol;
+
+ if( zPrompt && *zPrompt ){
+ printf("%s",zPrompt);
+ fflush(stdout);
+ }
+ nLine = 100;
+ zLine = malloc( nLine );
+ if( zLine==0 ) return 0;
+ n = 0;
+ eol = 0;
+ while( !eol ){
+ if( n+100>nLine ){
+ nLine = nLine*2 + 100;
+ zLine = realloc(zLine, nLine);
+ if( zLine==0 ) return 0;
+ }
+ if( fgets(&zLine[n], nLine - n, in)==0 ){
+ if( n==0 ){
+ free(zLine);
+ return 0;
+ }
+ zLine[n] = 0;
+ eol = 1;
+ break;
+ }
+ while( zLine[n] ){ n++; }
+ if( n>0 && zLine[n-1]=='\n' ){
+ n--;
+ zLine[n] = 0;
+ eol = 1;
+ }
+ }
+ zLine = realloc( zLine, n+1 );
+ return zLine;
+}
+
+/*
+** Retrieve a single line of input text. "isatty" is true if text
+** is coming from a terminal. In that case, we issue a prompt and
+** attempt to use "readline" for command-line editing. If "isatty"
+** is false, use "local_getline" instead of "readline" and issue no prompt.
+**
+** zPrior is a string of prior text retrieved. If not the empty
+** string, then issue a continuation prompt.
+*/
+static char *one_input_line(const char *zPrior, FILE *in){
+ char *zPrompt;
+ char *zResult;
+ if( in!=0 ){
+ return local_getline(0, in);
+ }
+ if( zPrior && zPrior[0] ){
+ zPrompt = continuePrompt;
+ }else{
+ zPrompt = mainPrompt;
+ }
+ zResult = readline(zPrompt);
+ if( zResult ) add_history(zResult);
+ return zResult;
+}
+
+struct previous_mode_data {
+ int valid; /* Is there legit data in here? */
+ int mode;
+ int showHeader;
+ int colWidth[100];
+};
+/*
+** An pointer to an instance of this structure is passed from
+** the main program to the callback. This is used to communicate
+** state and mode information.
+*/
+struct callback_data {
+ sqlite3 *db; /* The database */
+ int echoOn; /* True to echo input commands */
+ int cnt; /* Number of records displayed so far */
+ FILE *out; /* Write results here */
+ int mode; /* An output mode setting */
+ int showHeader; /* True to show column names in List or Column mode */
+ char *zDestTable; /* Name of destination table when MODE_Insert */
+ char separator[20]; /* Separator character for MODE_List */
+ int colWidth[100]; /* Requested width of each column when in column mode*/
+ int actualWidth[100]; /* Actual width of each column */
+ char nullvalue[20]; /* The text to print when a NULL comes back from
+ ** the database */
+ struct previous_mode_data explainPrev;
+ /* Holds the mode information just before
+ ** .explain ON */
+ char outfile[FILENAME_MAX]; /* Filename for *out */
+ const char *zDbFilename; /* name of the database file */
+ char *zKey; /* Encryption key */
+};
+
+/*
+** These are the allowed modes.
+*/
+#define MODE_Line 0 /* One column per line. Blank line between records */
+#define MODE_Column 1 /* One record per line in neat columns */
+#define MODE_List 2 /* One record per line with a separator */
+#define MODE_Semi 3 /* Same as MODE_List but append ";" to each line */
+#define MODE_Html 4 /* Generate an XHTML table */
+#define MODE_Insert 5 /* Generate SQL "insert" statements */
+#define MODE_Tcl 6 /* Generate ANSI-C or TCL quoted elements */
+#define MODE_Csv 7 /* Quote strings, numbers are plain */
+#define MODE_NUM_OF 8 /* The number of modes (not a mode itself) */
+
+char *modeDescr[MODE_NUM_OF] = {
+ "line",
+ "column",
+ "list",
+ "semi",
+ "html",
+ "insert",
+ "tcl",
+ "csv",
+};
+
+/*
+** Number of elements in an array
+*/
+#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** Output the given string as a quoted string using SQL quoting conventions.
+*/
+static void output_quoted_string(FILE *out, const char *z){
+ int i;
+ int nSingle = 0;
+ for(i=0; z[i]; i++){
+ if( z[i]=='\'' ) nSingle++;
+ }
+ if( nSingle==0 ){
+ fprintf(out,"'%s'",z);
+ }else{
+ fprintf(out,"'");
+ while( *z ){
+ for(i=0; z[i] && z[i]!='\''; i++){}
+ if( i==0 ){
+ fprintf(out,"''");
+ z++;
+ }else if( z[i]=='\'' ){
+ fprintf(out,"%.*s''",i,z);
+ z += i+1;
+ }else{
+ fprintf(out,"%s",z);
+ break;
+ }
+ }
+ fprintf(out,"'");
+ }
+}
+
+/*
+** Output the given string as a quoted according to C or TCL quoting rules.
+*/
+static void output_c_string(FILE *out, const char *z){
+ unsigned int c;
+ fputc('"', out);
+ while( (c = *(z++))!=0 ){
+ if( c=='\\' ){
+ fputc(c, out);
+ fputc(c, out);
+ }else if( c=='\t' ){
+ fputc('\\', out);
+ fputc('t', out);
+ }else if( c=='\n' ){
+ fputc('\\', out);
+ fputc('n', out);
+ }else if( c=='\r' ){
+ fputc('\\', out);
+ fputc('r', out);
+ }else if( !isprint(c) ){
+ fprintf(out, "\\%03o", c);
+ }else{
+ fputc(c, out);
+ }
+ }
+ fputc('"', out);
+}
+
+/*
+** Output the given string with characters that are special to
+** HTML escaped.
+*/
+static void output_html_string(FILE *out, const char *z){
+ int i;
+ while( *z ){
+ for(i=0; z[i] && z[i]!='<' && z[i]!='&'; i++){}
+ if( i>0 ){
+ fprintf(out,"%.*s",i,z);
+ }
+ if( z[i]=='<' ){
+ fprintf(out,"&lt;");
+ }else if( z[i]=='&' ){
+ fprintf(out,"&amp;");
+ }else{
+ break;
+ }
+ z += i + 1;
+ }
+}
+
+/*
+** Output a single term of CSV. Actually, p->separator is used for
+** the separator, which may or may not be a comma. p->nullvalue is
+** the null value. Strings are quoted using ANSI-C rules. Numbers
+** appear outside of quotes.
+*/
+static void output_csv(struct callback_data *p, const char *z, int bSep){
+ if( z==0 ){
+ fprintf(p->out,"%s",p->nullvalue);
+ }else if( isNumber(z, 0) ){
+ fprintf(p->out,"%s",z);
+ }else{
+ output_c_string(p->out, z);
+ }
+ if( bSep ){
+ fprintf(p->out, p->separator);
+ }
+}
+
+/*
+** This routine runs when the user presses Ctrl-C
+*/
+static void interrupt_handler(int NotUsed){
+ seenInterrupt = 1;
+ if( db ) sqlite3_interrupt(db);
+}
+
+/*
+** This is the callback routine that the SQLite library
+** invokes for each row of a query result.
+*/
+static int callback(void *pArg, int nArg, char **azArg, char **azCol){
+ int i;
+ struct callback_data *p = (struct callback_data*)pArg;
+ switch( p->mode ){
+ case MODE_Line: {
+ int w = 5;
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ int len = strlen(azCol[i]);
+ if( len>w ) w = len;
+ }
+ if( p->cnt++>0 ) fprintf(p->out,"\n");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"%*s = %s\n", w, azCol[i],
+ azArg[i] ? azArg[i] : p->nullvalue);
+ }
+ break;
+ }
+ case MODE_Column: {
+ if( p->cnt++==0 ){
+ for(i=0; i<nArg; i++){
+ int w, n;
+ if( i<ArraySize(p->colWidth) ){
+ w = p->colWidth[i];
+ }else{
+ w = 0;
+ }
+ if( w<=0 ){
+ w = strlen(azCol[i] ? azCol[i] : "");
+ if( w<10 ) w = 10;
+ n = strlen(azArg && azArg[i] ? azArg[i] : p->nullvalue);
+ if( w<n ) w = n;
+ }
+ if( i<ArraySize(p->actualWidth) ){
+ p->actualWidth[i] = w;
+ }
+ if( p->showHeader ){
+ fprintf(p->out,"%-*.*s%s",w,w,azCol[i], i==nArg-1 ? "\n": " ");
+ }
+ }
+ if( p->showHeader ){
+ for(i=0; i<nArg; i++){
+ int w;
+ if( i<ArraySize(p->actualWidth) ){
+ w = p->actualWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,"-----------------------------------"
+ "----------------------------------------------------------",
+ i==nArg-1 ? "\n": " ");
+ }
+ }
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ int w;
+ if( i<ArraySize(p->actualWidth) ){
+ w = p->actualWidth[i];
+ }else{
+ w = 10;
+ }
+ fprintf(p->out,"%-*.*s%s",w,w,
+ azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " ");
+ }
+ break;
+ }
+ case MODE_Semi:
+ case MODE_List: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"%s%s",azCol[i], i==nArg-1 ? "\n" : p->separator);
+ }
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ char *z = azArg[i];
+ if( z==0 ) z = p->nullvalue;
+ fprintf(p->out, "%s", z);
+ if( i<nArg-1 ){
+ fprintf(p->out, "%s", p->separator);
+ }else if( p->mode==MODE_Semi ){
+ fprintf(p->out, ";\n");
+ }else{
+ fprintf(p->out, "\n");
+ }
+ }
+ break;
+ }
+ case MODE_Html: {
+ if( p->cnt++==0 && p->showHeader ){
+ fprintf(p->out,"<TR>");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"<TH>%s</TH>",azCol[i]);
+ }
+ fprintf(p->out,"</TR>\n");
+ }
+ if( azArg==0 ) break;
+ fprintf(p->out,"<TR>");
+ for(i=0; i<nArg; i++){
+ fprintf(p->out,"<TD>");
+ output_html_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
+ fprintf(p->out,"</TD>\n");
+ }
+ fprintf(p->out,"</TR>\n");
+ break;
+ }
+ case MODE_Tcl: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ output_c_string(p->out,azCol[i]);
+ fprintf(p->out, "%s", p->separator);
+ }
+ fprintf(p->out,"\n");
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ output_c_string(p->out, azArg[i] ? azArg[i] : p->nullvalue);
+ fprintf(p->out, "%s", p->separator);
+ }
+ fprintf(p->out,"\n");
+ break;
+ }
+ case MODE_Csv: {
+ if( p->cnt++==0 && p->showHeader ){
+ for(i=0; i<nArg; i++){
+ output_csv(p, azCol[i], i<nArg-1);
+ }
+ fprintf(p->out,"\n");
+ }
+ if( azArg==0 ) break;
+ for(i=0; i<nArg; i++){
+ output_csv(p, azArg[i], i<nArg-1);
+ }
+ fprintf(p->out,"\n");
+ break;
+ }
+ case MODE_Insert: {
+ if( azArg==0 ) break;
+ fprintf(p->out,"INSERT INTO %s VALUES(",p->zDestTable);
+ for(i=0; i<nArg; i++){
+ char *zSep = i>0 ? ",": "";
+ if( azArg[i]==0 ){
+ fprintf(p->out,"%sNULL",zSep);
+ }else if( isNumber(azArg[i], 0) ){
+ fprintf(p->out,"%s%s",zSep, azArg[i]);
+ }else{
+ if( zSep[0] ) fprintf(p->out,"%s",zSep);
+ output_quoted_string(p->out, azArg[i]);
+ }
+ }
+ fprintf(p->out,");\n");
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+** Set the destination table field of the callback_data structure to
+** the name of the table given. Escape any quote characters in the
+** table name.
+*/
+static void set_table_name(struct callback_data *p, const char *zName){
+ int i, n;
+ int needQuote;
+ char *z;
+
+ if( p->zDestTable ){
+ free(p->zDestTable);
+ p->zDestTable = 0;
+ }
+ if( zName==0 ) return;
+ needQuote = !isalpha((unsigned char)*zName) && *zName!='_';
+ for(i=n=0; zName[i]; i++, n++){
+ if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ){
+ needQuote = 1;
+ if( zName[i]=='\'' ) n++;
+ }
+ }
+ if( needQuote ) n += 2;
+ z = p->zDestTable = malloc( n+1 );
+ if( z==0 ){
+ fprintf(stderr,"Out of memory!\n");
+ exit(1);
+ }
+ n = 0;
+ if( needQuote ) z[n++] = '\'';
+ for(i=0; zName[i]; i++){
+ z[n++] = zName[i];
+ if( zName[i]=='\'' ) z[n++] = '\'';
+ }
+ if( needQuote ) z[n++] = '\'';
+ z[n] = 0;
+}
+
+/* zIn is either a pointer to a NULL-terminated string in memory obtained
+** from malloc(), or a NULL pointer. The string pointed to by zAppend is
+** added to zIn, and the result returned in memory obtained from malloc().
+** zIn, if it was not NULL, is freed.
+**
+** If the third argument, quote, is not '\0', then it is used as a
+** quote character for zAppend.
+*/
+static char * appendText(char *zIn, char const *zAppend, char quote){
+ int len;
+ int i;
+ int nAppend = strlen(zAppend);
+ int nIn = (zIn?strlen(zIn):0);
+
+ len = nAppend+nIn+1;
+ if( quote ){
+ len += 2;
+ for(i=0; i<nAppend; i++){
+ if( zAppend[i]==quote ) len++;
+ }
+ }
+
+ zIn = (char *)realloc(zIn, len);
+ if( !zIn ){
+ return 0;
+ }
+
+ if( quote ){
+ char *zCsr = &zIn[nIn];
+ *zCsr++ = quote;
+ for(i=0; i<nAppend; i++){
+ *zCsr++ = zAppend[i];
+ if( zAppend[i]==quote ) *zCsr++ = quote;
+ }
+ *zCsr++ = quote;
+ *zCsr++ = '\0';
+ assert( (zCsr-zIn)==len );
+ }else{
+ memcpy(&zIn[nIn], zAppend, nAppend);
+ zIn[len-1] = '\0';
+ }
+
+ return zIn;
+}
+
+
+/*
+** Execute a query statement that has a single result column. Print
+** that result column on a line by itself with a semicolon terminator.
+*/
+static int run_table_dump_query(FILE *out, sqlite3 *db, const char *zSelect){
+ sqlite3_stmt *pSelect;
+ int rc;
+ rc = sqlite3_prepare(db, zSelect, -1, &pSelect, 0);
+ if( rc!=SQLITE_OK || !pSelect ){
+ return rc;
+ }
+ rc = sqlite3_step(pSelect);
+ while( rc==SQLITE_ROW ){
+ fprintf(out, "%s;\n", sqlite3_column_text(pSelect, 0));
+ rc = sqlite3_step(pSelect);
+ }
+ return sqlite3_finalize(pSelect);
+}
+
+
+/*
+** This is a different callback routine used for dumping the database.
+** Each row received by this callback consists of a table name,
+** the table type ("index" or "table") and SQL to create the table.
+** This routine should print text sufficient to recreate the table.
+*/
+static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
+ int rc;
+ const char *zTable;
+ const char *zType;
+ const char *zSql;
+ struct callback_data *p = (struct callback_data *)pArg;
+
+ if( nArg!=3 ) return 1;
+ zTable = azArg[0];
+ zType = azArg[1];
+ zSql = azArg[2];
+
+ fprintf(p->out, "%s;\n", zSql);
+
+ if( strcmp(zType, "table")==0 ){
+ sqlite3_stmt *pTableInfo = 0;
+ char *zSelect = 0;
+ char *zTableInfo = 0;
+ char *zTmp = 0;
+
+ zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0);
+ zTableInfo = appendText(zTableInfo, zTable, '"');
+ zTableInfo = appendText(zTableInfo, ");", 0);
+
+ rc = sqlite3_prepare(p->db, zTableInfo, -1, &pTableInfo, 0);
+ if( zTableInfo ) free(zTableInfo);
+ if( rc!=SQLITE_OK || !pTableInfo ){
+ return 1;
+ }
+
+ zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
+ zTmp = appendText(zTmp, zTable, '"');
+ if( zTmp ){
+ zSelect = appendText(zSelect, zTmp, '\'');
+ }
+ zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
+ rc = sqlite3_step(pTableInfo);
+ while( rc==SQLITE_ROW ){
+ zSelect = appendText(zSelect, "quote(", 0);
+ zSelect = appendText(zSelect, sqlite3_column_text(pTableInfo, 1), '"');
+ rc = sqlite3_step(pTableInfo);
+ if( rc==SQLITE_ROW ){
+ zSelect = appendText(zSelect, ") || ', ' || ", 0);
+ }else{
+ zSelect = appendText(zSelect, ") ", 0);
+ }
+ }
+ rc = sqlite3_finalize(pTableInfo);
+ if( rc!=SQLITE_OK ){
+ if( zSelect ) free(zSelect);
+ return 1;
+ }
+ zSelect = appendText(zSelect, "|| ')' FROM ", 0);
+ zSelect = appendText(zSelect, zTable, '"');
+
+ rc = run_table_dump_query(p->out, p->db, zSelect);
+ if( rc==SQLITE_CORRUPT ){
+ zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0);
+ rc = run_table_dump_query(p->out, p->db, zSelect);
+ }
+ if( zSelect ) free(zSelect);
+ if( rc!=SQLITE_OK ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Run zQuery. Update dump_callback() as the callback routine.
+** If we get a SQLITE_CORRUPT error, rerun the query after appending
+** "ORDER BY rowid DESC" to the end.
+*/
+static int run_schema_dump_query(
+ struct callback_data *p,
+ const char *zQuery,
+ char **pzErrMsg
+){
+ int rc;
+ rc = sqlite3_exec(p->db, zQuery, dump_callback, p, pzErrMsg);
+ if( rc==SQLITE_CORRUPT ){
+ char *zQ2;
+ int len = strlen(zQuery);
+ if( pzErrMsg ) sqlite3_free(*pzErrMsg);
+ zQ2 = malloc( len+100 );
+ if( zQ2==0 ) return rc;
+ sprintf(zQ2, "%s ORDER BY rowid DESC", zQuery);
+ rc = sqlite3_exec(p->db, zQ2, dump_callback, p, pzErrMsg);
+ free(zQ2);
+ }
+ return rc;
+}
+
+/*
+** Text of a help message
+*/
+static char zHelp[] =
+ ".databases List names and files of attached databases\n"
+ ".dump ?TABLE? ... Dump the database in an SQL text format\n"
+ ".echo ON|OFF Turn command echo on or off\n"
+ ".exit Exit this program\n"
+ ".explain ON|OFF Turn output mode suitable for EXPLAIN on or off.\n"
+ ".header(s) ON|OFF Turn display of headers on or off\n"
+ ".help Show this message\n"
+ ".import FILE TABLE Import data from FILE into TABLE\n"
+ ".indices TABLE Show names of all indices on TABLE\n"
+ ".mode MODE ?TABLE? Set output mode where MODE is on of:\n"
+ " csv Comma-separated values\n"
+ " column Left-aligned columns. (See .width)\n"
+ " html HTML <table> code\n"
+ " insert SQL insert statements for TABLE\n"
+ " line One value per line\n"
+ " list Values delimited by .separator string\n"
+ " tabs Tab-separated values\n"
+ " tcl TCL list elements\n"
+ ".nullvalue STRING Print STRING in place of NULL values\n"
+ ".output FILENAME Send output to FILENAME\n"
+ ".output stdout Send output to the screen\n"
+ ".prompt MAIN CONTINUE Replace the standard prompts\n"
+ ".quit Exit this program\n"
+ ".read FILENAME Execute SQL in FILENAME\n"
+#ifdef SQLITE_HAS_CODEC
+ ".rekey OLD NEW NEW Change the encryption key\n"
+#endif
+ ".schema ?TABLE? Show the CREATE statements\n"
+ ".separator STRING Change separator used by output mode and .import\n"
+ ".show Show the current values for various settings\n"
+ ".tables ?PATTERN? List names of tables matching a LIKE pattern\n"
+ ".timeout MS Try opening locked tables for MS milliseconds\n"
+ ".width NUM NUM ... Set column widths for \"column\" mode\n"
+;
+
+/* Forward reference */
+static void process_input(struct callback_data *p, FILE *in);
+
+/*
+** Make sure the database is open. If it is not, then open it. If
+** the database fails to open, print an error message and exit.
+*/
+static void open_db(struct callback_data *p){
+ if( p->db==0 ){
+ sqlite3_open(p->zDbFilename, &p->db);
+ db = p->db;
+#ifdef SQLITE_HAS_CODEC
+ sqlite3_key(p->db, p->zKey, p->zKey ? strlen(p->zKey) : 0);
+#endif
+ sqlite3_create_function(db, "shellstatic", 0, SQLITE_UTF8, 0,
+ shellstaticFunc, 0, 0);
+ if( SQLITE_OK!=sqlite3_errcode(db) ){
+ fprintf(stderr,"Unable to open database \"%s\": %s\n",
+ p->zDbFilename, sqlite3_errmsg(db));
+ exit(1);
+ }
+ }
+}
+
+/*
+** Do C-language style dequoting.
+**
+** \t -> tab
+** \n -> newline
+** \r -> carriage return
+** \NNN -> ascii character NNN in octal
+** \\ -> backslash
+*/
+static void resolve_backslashes(char *z){
+ int i, j, c;
+ for(i=j=0; (c = z[i])!=0; i++, j++){
+ if( c=='\\' ){
+ c = z[++i];
+ if( c=='n' ){
+ c = '\n';
+ }else if( c=='t' ){
+ c = '\t';
+ }else if( c=='r' ){
+ c = '\r';
+ }else if( c>='0' && c<='7' ){
+ c =- '0';
+ if( z[i+1]>='0' && z[i+1]<='7' ){
+ i++;
+ c = (c<<3) + z[i] - '0';
+ if( z[i+1]>='0' && z[i+1]<='7' ){
+ i++;
+ c = (c<<3) + z[i] - '0';
+ }
+ }
+ }
+ }
+ z[j] = c;
+ }
+ z[j] = 0;
+}
+
+/*
+** If an input line begins with "." then invoke this routine to
+** process that line.
+**
+** Return 1 to exit and 0 to continue.
+*/
+static int do_meta_command(char *zLine, struct callback_data *p){
+ int i = 1;
+ int nArg = 0;
+ int n, c;
+ int rc = 0;
+ char *azArg[50];
+
+ /* Parse the input line into tokens.
+ */
+ while( zLine[i] && nArg<ArraySize(azArg) ){
+ while( isspace((unsigned char)zLine[i]) ){ i++; }
+ if( zLine[i]==0 ) break;
+ if( zLine[i]=='\'' || zLine[i]=='"' ){
+ int delim = zLine[i++];
+ azArg[nArg++] = &zLine[i];
+ while( zLine[i] && zLine[i]!=delim ){ i++; }
+ if( zLine[i]==delim ){
+ zLine[i++] = 0;
+ }
+ if( delim=='"' ) resolve_backslashes(azArg[nArg-1]);
+ }else{
+ azArg[nArg++] = &zLine[i];
+ while( zLine[i] && !isspace((unsigned char)zLine[i]) ){ i++; }
+ if( zLine[i] ) zLine[i++] = 0;
+ resolve_backslashes(azArg[nArg-1]);
+ }
+ }
+
+ /* Process the input line.
+ */
+ if( nArg==0 ) return rc;
+ n = strlen(azArg[0]);
+ c = azArg[0][0];
+ if( c=='d' && n>1 && strncmp(azArg[0], "databases", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 1;
+ data.mode = MODE_Column;
+ data.colWidth[0] = 3;
+ data.colWidth[1] = 15;
+ data.colWidth[2] = 58;
+ sqlite3_exec(p->db, "PRAGMA database_list; ", callback, &data, &zErrMsg);
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
+ char *zErrMsg = 0;
+ open_db(p);
+ fprintf(p->out, "BEGIN TRANSACTION;\n");
+ if( nArg==1 ){
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE sql NOT NULL AND type=='table'", 0
+ );
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE sql NOT NULL AND type!='table' AND type!='meta'", 0
+ );
+ }else{
+ int i;
+ for(i=1; i<nArg; i++){
+ zShellStatic = azArg[i];
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE tbl_name LIKE shellstatic() AND type=='table'"
+ " AND sql NOT NULL", 0);
+ run_schema_dump_query(p,
+ "SELECT name, type, sql FROM sqlite_master "
+ "WHERE tbl_name LIKE shellstatic() AND type!='table'"
+ " AND type!='meta' AND sql NOT NULL", 0);
+ zShellStatic = 0;
+ }
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }else{
+ fprintf(p->out, "COMMIT;\n");
+ }
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "echo", n)==0 && nArg>1 ){
+ int j;
+ char *z = azArg[1];
+ int val = atoi(azArg[1]);
+ for(j=0; z[j]; j++){
+ z[j] = tolower((unsigned char)z[j]);
+ }
+ if( strcmp(z,"on")==0 ){
+ val = 1;
+ }else if( strcmp(z,"yes")==0 ){
+ val = 1;
+ }
+ p->echoOn = val;
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
+ rc = 1;
+ }else
+
+ if( c=='e' && strncmp(azArg[0], "explain", n)==0 ){
+ int j;
+ static char zOne[] = "1";
+ char *z = nArg>=2 ? azArg[1] : zOne;
+ int val = atoi(z);
+ for(j=0; z[j]; j++){
+ z[j] = tolower((unsigned char)z[j]);
+ }
+ if( strcmp(z,"on")==0 ){
+ val = 1;
+ }else if( strcmp(z,"yes")==0 ){
+ val = 1;
+ }
+ if(val == 1) {
+ if(!p->explainPrev.valid) {
+ p->explainPrev.valid = 1;
+ p->explainPrev.mode = p->mode;
+ p->explainPrev.showHeader = p->showHeader;
+ memcpy(p->explainPrev.colWidth,p->colWidth,sizeof(p->colWidth));
+ }
+ /* We could put this code under the !p->explainValid
+ ** condition so that it does not execute if we are already in
+ ** explain mode. However, always executing it allows us an easy
+ ** was to reset to explain mode in case the user previously
+ ** did an .explain followed by a .width, .mode or .header
+ ** command.
+ */
+ p->mode = MODE_Column;
+ p->showHeader = 1;
+ memset(p->colWidth,0,ArraySize(p->colWidth));
+ p->colWidth[0] = 4;
+ p->colWidth[1] = 12;
+ p->colWidth[2] = 10;
+ p->colWidth[3] = 10;
+ p->colWidth[4] = 35;
+ }else if (p->explainPrev.valid) {
+ p->explainPrev.valid = 0;
+ p->mode = p->explainPrev.mode;
+ p->showHeader = p->explainPrev.showHeader;
+ memcpy(p->colWidth,p->explainPrev.colWidth,sizeof(p->colWidth));
+ }
+ }else
+
+ if( c=='h' && (strncmp(azArg[0], "header", n)==0
+ ||
+ strncmp(azArg[0], "headers", n)==0 )&& nArg>1 ){
+ int j;
+ char *z = azArg[1];
+ int val = atoi(azArg[1]);
+ for(j=0; z[j]; j++){
+ z[j] = tolower((unsigned char)z[j]);
+ }
+ if( strcmp(z,"on")==0 ){
+ val = 1;
+ }else if( strcmp(z,"yes")==0 ){
+ val = 1;
+ }
+ p->showHeader = val;
+ }else
+
+ if( c=='h' && strncmp(azArg[0], "help", n)==0 ){
+ fprintf(stderr,zHelp);
+ }else
+
+ if( c=='i' && strncmp(azArg[0], "import", n)==0 && nArg>=3 ){
+ char *zTable = azArg[2]; /* Insert data into this table */
+ char *zFile = azArg[1]; /* The file from which to extract data */
+ sqlite3_stmt *pStmt; /* A statement */
+ int rc; /* Result code */
+ int nCol; /* Number of columns in the table */
+ int nByte; /* Number of bytes in an SQL string */
+ int i, j; /* Loop counters */
+ int nSep; /* Number of bytes in p->separator[] */
+ char *zSql; /* An SQL statement */
+ char *zLine; /* A single line of input from the file */
+ char **azCol; /* zLine[] broken up into columns */
+ char *zCommit; /* How to commit changes */
+ FILE *in; /* The input file */
+ int lineno = 0; /* Line number of input file */
+
+ nSep = strlen(p->separator);
+ if( nSep==0 ){
+ fprintf(stderr, "non-null separator required for import\n");
+ return 0;
+ }
+ zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
+ if( zSql==0 ) return 0;
+ nByte = strlen(zSql);
+ rc = sqlite3_prepare(p->db, zSql, 0, &pStmt, 0);
+ sqlite3_free(zSql);
+ if( rc ){
+ fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ nCol = 0;
+ }else{
+ nCol = sqlite3_column_count(pStmt);
+ }
+ sqlite3_finalize(pStmt);
+ if( nCol==0 ) return 0;
+ zSql = malloc( nByte + 20 + nCol*2 );
+ if( zSql==0 ) return 0;
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
+ j = strlen(zSql);
+ for(i=1; i<nCol; i++){
+ zSql[j++] = ',';
+ zSql[j++] = '?';
+ }
+ zSql[j++] = ')';
+ zSql[j] = 0;
+ rc = sqlite3_prepare(p->db, zSql, 0, &pStmt, 0);
+ free(zSql);
+ if( rc ){
+ fprintf(stderr, "Error: %s\n", sqlite3_errmsg(db));
+ sqlite3_finalize(pStmt);
+ return 0;
+ }
+ in = fopen(zFile, "rb");
+ if( in==0 ){
+ fprintf(stderr, "cannot open file: %s\n", zFile);
+ sqlite3_finalize(pStmt);
+ return 0;
+ }
+ azCol = malloc( sizeof(azCol[0])*(nCol+1) );
+ if( azCol==0 ) return 0;
+ sqlite3_exec(p->db, "BEGIN", 0, 0, 0);
+ zCommit = "COMMIT";
+ while( (zLine = local_getline(0, in))!=0 ){
+ char *z;
+ i = 0;
+ lineno++;
+ azCol[0] = zLine;
+ for(i=0, z=zLine; *z && *z!='\n' && *z!='\r'; z++){
+ if( *z==p->separator[0] && strncmp(z, p->separator, nSep)==0 ){
+ *z = 0;
+ i++;
+ if( i<nCol ){
+ azCol[i] = &z[nSep];
+ z += nSep-1;
+ }
+ }
+ }
+ if( i+1!=nCol ){
+ fprintf(stderr,"%s line %d: expected %d columns of data but found %d\n",
+ zFile, lineno, nCol, i+1);
+ zCommit = "ROLLBACK";
+ break;
+ }
+ for(i=0; i<nCol; i++){
+ sqlite3_bind_text(pStmt, i+1, azCol[i], -1, SQLITE_STATIC);
+ }
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ free(zLine);
+ if( rc!=SQLITE_OK ){
+ fprintf(stderr,"Error: %s\n", sqlite3_errmsg(db));
+ zCommit = "ROLLBACK";
+ break;
+ }
+ }
+ free(azCol);
+ fclose(in);
+ sqlite3_finalize(pStmt);
+ sqlite3_exec(p->db, zCommit, 0, 0, 0);
+ }else
+
+ if( c=='i' && strncmp(azArg[0], "indices", n)==0 && nArg>1 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_List;
+ zShellStatic = azArg[1];
+ sqlite3_exec(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type='index' AND tbl_name LIKE shellstatic() "
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type='index' AND tbl_name LIKE shellstatic() "
+ "ORDER BY 1",
+ callback, &data, &zErrMsg
+ );
+ zShellStatic = 0;
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+ if( c=='m' && strncmp(azArg[0], "mode", n)==0 && nArg>=2 ){
+ int n2 = strlen(azArg[1]);
+ if( strncmp(azArg[1],"line",n2)==0
+ ||
+ strncmp(azArg[1],"lines",n2)==0 ){
+ p->mode = MODE_Line;
+ }else if( strncmp(azArg[1],"column",n2)==0
+ ||
+ strncmp(azArg[1],"columns",n2)==0 ){
+ p->mode = MODE_Column;
+ }else if( strncmp(azArg[1],"list",n2)==0 ){
+ p->mode = MODE_List;
+ }else if( strncmp(azArg[1],"html",n2)==0 ){
+ p->mode = MODE_Html;
+ }else if( strncmp(azArg[1],"tcl",n2)==0 ){
+ p->mode = MODE_Tcl;
+ }else if( strncmp(azArg[1],"csv",n2)==0 ){
+ p->mode = MODE_Csv;
+ strcpy(p->separator, ",");
+ }else if( strncmp(azArg[1],"tabs",n2)==0 ){
+ p->mode = MODE_List;
+ strcpy(p->separator, "\t");
+ }else if( strncmp(azArg[1],"insert",n2)==0 ){
+ p->mode = MODE_Insert;
+ if( nArg>=3 ){
+ set_table_name(p, azArg[2]);
+ }else{
+ set_table_name(p, "table");
+ }
+ }else {
+ fprintf(stderr,"mode should be on of: "
+ "column csv html insert line list tabs tcl\n");
+ }
+ }else
+
+ if( c=='n' && strncmp(azArg[0], "nullvalue", n)==0 && nArg==2 ) {
+ sprintf(p->nullvalue, "%.*s", (int)ArraySize(p->nullvalue)-1, azArg[1]);
+ }else
+
+ if( c=='o' && strncmp(azArg[0], "output", n)==0 && nArg==2 ){
+ if( p->out!=stdout ){
+ fclose(p->out);
+ }
+ if( strcmp(azArg[1],"stdout")==0 ){
+ p->out = stdout;
+ strcpy(p->outfile,"stdout");
+ }else{
+ p->out = fopen(azArg[1], "wb");
+ if( p->out==0 ){
+ fprintf(stderr,"can't write to \"%s\"\n", azArg[1]);
+ p->out = stdout;
+ } else {
+ strcpy(p->outfile,azArg[1]);
+ }
+ }
+ }else
+
+ if( c=='p' && strncmp(azArg[0], "prompt", n)==0 && (nArg==2 || nArg==3)){
+ if( nArg >= 2) {
+ strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
+ }
+ if( nArg >= 3) {
+ strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
+ }
+ }else
+
+ if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
+ rc = 1;
+ }else
+
+ if( c=='r' && strncmp(azArg[0], "read", n)==0 && nArg==2 ){
+ FILE *alt = fopen(azArg[1], "rb");
+ if( alt==0 ){
+ fprintf(stderr,"can't open \"%s\"\n", azArg[1]);
+ }else{
+ process_input(p, alt);
+ fclose(alt);
+ }
+ }else
+
+#ifdef SQLITE_HAS_CODEC
+ if( c=='r' && strncmp(azArg[0],"rekey", n)==0 && nArg==4 ){
+ char *zOld = p->zKey;
+ if( zOld==0 ) zOld = "";
+ if( strcmp(azArg[1],zOld) ){
+ fprintf(stderr,"old key is incorrect\n");
+ }else if( strcmp(azArg[2], azArg[3]) ){
+ fprintf(stderr,"2nd copy of new key does not match the 1st\n");
+ }else{
+ sqlite3_free(p->zKey);
+ p->zKey = sqlite3_mprintf("%s", azArg[2]);
+ sqlite3_rekey(p->db, p->zKey, strlen(p->zKey));
+ }
+ }else
+#endif
+
+ if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){
+ struct callback_data data;
+ char *zErrMsg = 0;
+ open_db(p);
+ memcpy(&data, p, sizeof(data));
+ data.showHeader = 0;
+ data.mode = MODE_Semi;
+ if( nArg>1 ){
+ int i;
+ for(i=0; azArg[1][i]; i++) azArg[1][i] = tolower(azArg[1][i]);
+ if( strcmp(azArg[1],"sqlite_master")==0 ){
+ char *new_argv[2], *new_colv[2];
+ new_argv[0] = "CREATE TABLE sqlite_master (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")";
+ new_argv[1] = 0;
+ new_colv[0] = "sql";
+ new_colv[1] = 0;
+ callback(&data, 1, new_argv, new_colv);
+ }else if( strcmp(azArg[1],"sqlite_temp_master")==0 ){
+ char *new_argv[2], *new_colv[2];
+ new_argv[0] = "CREATE TEMP TABLE sqlite_temp_master (\n"
+ " type text,\n"
+ " name text,\n"
+ " tbl_name text,\n"
+ " rootpage integer,\n"
+ " sql text\n"
+ ")";
+ new_argv[1] = 0;
+ new_colv[0] = "sql";
+ new_colv[1] = 0;
+ callback(&data, 1, new_argv, new_colv);
+ }else{
+ zShellStatic = azArg[1];
+ sqlite3_exec(p->db,
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
+ "WHERE tbl_name LIKE shellstatic() AND type!='meta' AND sql NOTNULL "
+ "ORDER BY substr(type,2,1), name",
+ callback, &data, &zErrMsg);
+ zShellStatic = 0;
+ }
+ }else{
+ sqlite3_exec(p->db,
+ "SELECT sql FROM "
+ " (SELECT * FROM sqlite_master UNION ALL"
+ " SELECT * FROM sqlite_temp_master) "
+ "WHERE type!='meta' AND sql NOTNULL "
+ "ORDER BY substr(type,2,1), name",
+ callback, &data, &zErrMsg
+ );
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "separator", n)==0 && nArg==2 ){
+ sprintf(p->separator, "%.*s", (int)ArraySize(p->separator)-1, azArg[1]);
+ }else
+
+ if( c=='s' && strncmp(azArg[0], "show", n)==0){
+ int i;
+ fprintf(p->out,"%9.9s: %s\n","echo", p->echoOn ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","explain", p->explainPrev.valid ? "on" :"off");
+ fprintf(p->out,"%9.9s: %s\n","headers", p->showHeader ? "on" : "off");
+ fprintf(p->out,"%9.9s: %s\n","mode", modeDescr[p->mode]);
+ fprintf(p->out,"%9.9s: ", "nullvalue");
+ output_c_string(p->out, p->nullvalue);
+ fprintf(p->out, "\n");
+ fprintf(p->out,"%9.9s: %s\n","output",
+ strlen(p->outfile) ? p->outfile : "stdout");
+ fprintf(p->out,"%9.9s: ", "separator");
+ output_c_string(p->out, p->separator);
+ fprintf(p->out, "\n");
+ fprintf(p->out,"%9.9s: ","width");
+ for (i=0;i<(int)ArraySize(p->colWidth) && p->colWidth[i] != 0;i++) {
+ fprintf(p->out,"%d ",p->colWidth[i]);
+ }
+ fprintf(p->out,"\n");
+ }else
+
+ if( c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0 ){
+ char **azResult;
+ int nRow, rc;
+ char *zErrMsg;
+ open_db(p);
+ if( nArg==1 ){
+ rc = sqlite3_get_table(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type IN ('table','view') "
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') "
+ "ORDER BY 1",
+ &azResult, &nRow, 0, &zErrMsg
+ );
+ }else{
+ zShellStatic = azArg[1];
+ rc = sqlite3_get_table(p->db,
+ "SELECT name FROM sqlite_master "
+ "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' "
+ "UNION ALL "
+ "SELECT name FROM sqlite_temp_master "
+ "WHERE type IN ('table','view') AND name LIKE '%'||shellstatic()||'%' "
+ "ORDER BY 1",
+ &azResult, &nRow, 0, &zErrMsg
+ );
+ zShellStatic = 0;
+ }
+ if( zErrMsg ){
+ fprintf(stderr,"Error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ }
+ if( rc==SQLITE_OK ){
+ int len, maxlen = 0;
+ int i, j;
+ int nPrintCol, nPrintRow;
+ for(i=1; i<=nRow; i++){
+ if( azResult[i]==0 ) continue;
+ len = strlen(azResult[i]);
+ if( len>maxlen ) maxlen = len;
+ }
+ nPrintCol = 80/(maxlen+2);
+ if( nPrintCol<1 ) nPrintCol = 1;
+ nPrintRow = (nRow + nPrintCol - 1)/nPrintCol;
+ for(i=0; i<nPrintRow; i++){
+ for(j=i+1; j<=nRow; j+=nPrintRow){
+ char *zSp = j<=nPrintRow ? "" : " ";
+ printf("%s%-*s", zSp, maxlen, azResult[j] ? azResult[j] : "");
+ }
+ printf("\n");
+ }
+ }
+ sqlite3_free_table(azResult);
+ }else
+
+ if( c=='t' && n>1 && strncmp(azArg[0], "timeout", n)==0 && nArg>=2 ){
+ open_db(p);
+ sqlite3_busy_timeout(p->db, atoi(azArg[1]));
+ }else
+
+ if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
+ int j;
+ for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
+ p->colWidth[j-1] = atoi(azArg[j]);
+ }
+ }else
+
+ {
+ fprintf(stderr, "unknown command or invalid arguments: "
+ " \"%s\". Enter \".help\" for help\n", azArg[0]);
+ }
+
+ return rc;
+}
+
+/*
+** Return TRUE if the last non-whitespace character in z[] is a semicolon.
+** z[] is N characters long.
+*/
+static int _ends_with_semicolon(const char *z, int N){
+ while( N>0 && isspace((unsigned char)z[N-1]) ){ N--; }
+ return N>0 && z[N-1]==';';
+}
+
+/*
+** Test to see if a line consists entirely of whitespace.
+*/
+static int _all_whitespace(const char *z){
+ for(; *z; z++){
+ if( isspace(*(unsigned char*)z) ) continue;
+ if( *z=='/' && z[1]=='*' ){
+ z += 2;
+ while( *z && (*z!='*' || z[1]!='/') ){ z++; }
+ if( *z==0 ) return 0;
+ z++;
+ continue;
+ }
+ if( *z=='-' && z[1]=='-' ){
+ z += 2;
+ while( *z && *z!='\n' ){ z++; }
+ if( *z==0 ) return 1;
+ continue;
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Return TRUE if the line typed in is an SQL command terminator other
+** than a semi-colon. The SQL Server style "go" command is understood
+** as is the Oracle "/".
+*/
+static int _is_command_terminator(const char *zLine){
+ while( isspace(*(unsigned char*)zLine) ){ zLine++; };
+ if( zLine[0]=='/' && _all_whitespace(&zLine[1]) ) return 1; /* Oracle */
+ if( tolower(zLine[0])=='g' && tolower(zLine[1])=='o'
+ && _all_whitespace(&zLine[2]) ){
+ return 1; /* SQL Server */
+ }
+ return 0;
+}
+
+/*
+** Read input from *in and process it. If *in==0 then input
+** is interactive - the user is typing it it. Otherwise, input
+** is coming from a file or device. A prompt is issued and history
+** is saved only if input is interactive. An interrupt signal will
+** cause this routine to exit immediately, unless input is interactive.
+*/
+static void process_input(struct callback_data *p, FILE *in){
+ char *zLine;
+ char *zSql = 0;
+ int nSql = 0;
+ char *zErrMsg;
+ int rc;
+ while( fflush(p->out), (zLine = one_input_line(zSql, in))!=0 ){
+ if( seenInterrupt ){
+ if( in!=0 ) break;
+ seenInterrupt = 0;
+ }
+ if( p->echoOn ) printf("%s\n", zLine);
+ if( (zSql==0 || zSql[0]==0) && _all_whitespace(zLine) ) continue;
+ if( zLine && zLine[0]=='.' && nSql==0 ){
+ int rc = do_meta_command(zLine, p);
+ free(zLine);
+ if( rc ) break;
+ continue;
+ }
+ if( _is_command_terminator(zLine) ){
+ strcpy(zLine,";");
+ }
+ if( zSql==0 ){
+ int i;
+ for(i=0; zLine[i] && isspace((unsigned char)zLine[i]); i++){}
+ if( zLine[i]!=0 ){
+ nSql = strlen(zLine);
+ zSql = malloc( nSql+1 );
+ strcpy(zSql, zLine);
+ }
+ }else{
+ int len = strlen(zLine);
+ zSql = realloc( zSql, nSql + len + 2 );
+ if( zSql==0 ){
+ fprintf(stderr,"%s: out of memory!\n", Argv0);
+ exit(1);
+ }
+ strcpy(&zSql[nSql++], "\n");
+ strcpy(&zSql[nSql], zLine);
+ nSql += len;
+ }
+ free(zLine);
+ if( zSql && _ends_with_semicolon(zSql, nSql) && sqlite3_complete(zSql) ){
+ p->cnt = 0;
+ open_db(p);
+ rc = sqlite3_exec(p->db, zSql, callback, p, &zErrMsg);
+ if( rc || zErrMsg ){
+ if( in!=0 && !p->echoOn ) printf("%s\n",zSql);
+ if( zErrMsg!=0 ){
+ printf("SQL error: %s\n", zErrMsg);
+ sqlite3_free(zErrMsg);
+ zErrMsg = 0;
+ }else{
+ printf("SQL error: %s\n", sqlite3_errmsg(p->db));
+ }
+ }
+ free(zSql);
+ zSql = 0;
+ nSql = 0;
+ }
+ }
+ if( zSql ){
+ if( !_all_whitespace(zSql) ) printf("Incomplete SQL: %s\n", zSql);
+ free(zSql);
+ }
+}
+
+/*
+** Return a pathname which is the user's home directory. A
+** 0 return indicates an error of some kind. Space to hold the
+** resulting string is obtained from malloc(). The calling
+** function should free the result.
+*/
+static char *find_home_dir(void){
+ char *home_dir = NULL;
+
+#if !defined(_WIN32) && !defined(WIN32) && !defined(__MACOS__)
+ struct passwd *pwent;
+ uid_t uid = getuid();
+ if( (pwent=getpwuid(uid)) != NULL) {
+ home_dir = pwent->pw_dir;
+ }
+#endif
+
+#ifdef __MACOS__
+ char home_path[_MAX_PATH+1];
+ home_dir = getcwd(home_path, _MAX_PATH);
+#endif
+
+ if (!home_dir) {
+ home_dir = getenv("HOME");
+ if (!home_dir) {
+ home_dir = getenv("HOMEPATH"); /* Windows? */
+ }
+ }
+
+#if defined(_WIN32) || defined(WIN32)
+ if (!home_dir) {
+ home_dir = "c:";
+ }
+#endif
+
+ if( home_dir ){
+ char *z = malloc( strlen(home_dir)+1 );
+ if( z ) strcpy(z, home_dir);
+ home_dir = z;
+ }
+
+ return home_dir;
+}
+
+/*
+** Read input from the file given by sqliterc_override. Or if that
+** parameter is NULL, take input from ~/.sqliterc
+*/
+static void process_sqliterc(
+ struct callback_data *p, /* Configuration data */
+ const char *sqliterc_override /* Name of config file. NULL to use default */
+){
+ char *home_dir = NULL;
+ const char *sqliterc = sqliterc_override;
+ char *zBuf;
+ FILE *in = NULL;
+
+ if (sqliterc == NULL) {
+ home_dir = find_home_dir();
+ if( home_dir==0 ){
+ fprintf(stderr,"%s: cannot locate your home directory!\n", Argv0);
+ return;
+ }
+ zBuf = malloc(strlen(home_dir) + 15);
+ if( zBuf==0 ){
+ fprintf(stderr,"%s: out of memory!\n", Argv0);
+ exit(1);
+ }
+ sprintf(zBuf,"%s/.sqliterc",home_dir);
+ free(home_dir);
+ sqliterc = (const char*)zBuf;
+ }
+ in = fopen(sqliterc,"rb");
+ if( in ){
+ if( isatty(fileno(stdout)) ){
+ printf("Loading resources from %s\n",sqliterc);
+ }
+ process_input(p,in);
+ fclose(in);
+ }
+ return;
+}
+
+/*
+** Show available command line options
+*/
+static const char zOptions[] =
+ " -init filename read/process named file\n"
+ " -echo print commands before execution\n"
+ " -[no]header turn headers on or off\n"
+ " -column set output mode to 'column'\n"
+ " -html set output mode to HTML\n"
+#ifdef SQLITE_HAS_CODEC
+ " -key KEY encryption key\n"
+#endif
+ " -line set output mode to 'line'\n"
+ " -list set output mode to 'list'\n"
+ " -separator 'x' set output field separator (|)\n"
+ " -nullvalue 'text' set text string for NULL values\n"
+ " -version show SQLite version\n"
+ " -help show this text, also show dot-commands\n"
+;
+static void usage(int showDetail){
+ fprintf(stderr, "Usage: %s [OPTIONS] FILENAME [SQL]\n", Argv0);
+ if( showDetail ){
+ fprintf(stderr, "Options are:\n%s", zOptions);
+ }else{
+ fprintf(stderr, "Use the -help option for additional information\n");
+ }
+ exit(1);
+}
+
+/*
+** Initialize the state information in data
+*/
+void main_init(struct callback_data *data) {
+ memset(data, 0, sizeof(*data));
+ data->mode = MODE_List;
+ strcpy(data->separator,"|");
+ data->showHeader = 0;
+ strcpy(mainPrompt,"sqlite> ");
+ strcpy(continuePrompt," ...> ");
+}
+
+int main(int argc, char **argv){
+ char *zErrMsg = 0;
+ struct callback_data data;
+ const char *zInitFile = 0;
+ char *zFirstCmd = 0;
+ int i;
+
+#ifdef __MACOS__
+ argc = ccommand(&argv);
+#endif
+
+ Argv0 = argv[0];
+ main_init(&data);
+
+ /* Make sure we have a valid signal handler early, before anything
+ ** else is done.
+ */
+#ifdef SIGINT
+ signal(SIGINT, interrupt_handler);
+#endif
+
+ /* Do an initial pass through the command-line argument to locate
+ ** the name of the database file, the name of the initialization file,
+ ** and the first command to execute.
+ */
+ for(i=1; i<argc-1; i++){
+ if( argv[i][0]!='-' ) break;
+ if( strcmp(argv[i],"-separator")==0 || strcmp(argv[i],"-nullvalue")==0 ){
+ i++;
+ }else if( strcmp(argv[i],"-init")==0 ){
+ i++;
+ zInitFile = argv[i];
+ }else if( strcmp(argv[i],"-key")==0 ){
+ i++;
+ data.zKey = sqlite3_mprintf("%s",argv[i]);
+ }
+ }
+ if( i<argc ){
+ data.zDbFilename = argv[i++];
+ }else{
+ data.zDbFilename = ":memory:";
+ }
+ if( i<argc ){
+ zFirstCmd = argv[i++];
+ }
+ data.out = stdout;
+
+ /* Go ahead and open the database file if it already exists. If the
+ ** file does not exist, delay opening it. This prevents empty database
+ ** files from being created if a user mistypes the database name argument
+ ** to the sqlite command-line tool.
+ */
+ if( access(data.zDbFilename, 0)==0 ){
+ open_db(&data);
+ }
+
+ /* Process the initialization file if there is one. If no -init option
+ ** is given on the command line, look for a file named ~/.sqliterc and
+ ** try to process it.
+ */
+ process_sqliterc(&data,zInitFile);
+
+ /* Make a second pass through the command-line argument and set
+ ** options. This second pass is delayed until after the initialization
+ ** file is processed so that the command-line arguments will override
+ ** settings in the initialization file.
+ */
+ for(i=1; i<argc && argv[i][0]=='-'; i++){
+ char *z = argv[i];
+ if( strcmp(z,"-init")==0 || strcmp(z,"-key")==0 ){
+ i++;
+ }else if( strcmp(z,"-html")==0 ){
+ data.mode = MODE_Html;
+ }else if( strcmp(z,"-list")==0 ){
+ data.mode = MODE_List;
+ }else if( strcmp(z,"-line")==0 ){
+ data.mode = MODE_Line;
+ }else if( strcmp(z,"-column")==0 ){
+ data.mode = MODE_Column;
+ }else if( strcmp(z,"-separator")==0 ){
+ i++;
+ sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[i]);
+ }else if( strcmp(z,"-nullvalue")==0 ){
+ i++;
+ sprintf(data.nullvalue,"%.*s",(int)sizeof(data.nullvalue)-1,argv[i]);
+ }else if( strcmp(z,"-header")==0 ){
+ data.showHeader = 1;
+ }else if( strcmp(z,"-noheader")==0 ){
+ data.showHeader = 0;
+ }else if( strcmp(z,"-echo")==0 ){
+ data.echoOn = 1;
+ }else if( strcmp(z,"-version")==0 ){
+ printf("%s\n", sqlite3_libversion());
+ return 1;
+ }else if( strcmp(z,"-help")==0 ){
+ usage(1);
+ }else{
+ fprintf(stderr,"%s: unknown option: %s\n", Argv0, z);
+ fprintf(stderr,"Use -help for a list of options.\n");
+ return 1;
+ }
+ }
+
+ if( zFirstCmd ){
+ /* Run just the command that follows the database name
+ */
+ if( zFirstCmd[0]=='.' ){
+ do_meta_command(zFirstCmd, &data);
+ exit(0);
+ }else{
+ int rc;
+ open_db(&data);
+ rc = sqlite3_exec(data.db, zFirstCmd, callback, &data, &zErrMsg);
+ if( rc!=0 && zErrMsg!=0 ){
+ fprintf(stderr,"SQL error: %s\n", zErrMsg);
+ exit(1);
+ }
+ }
+ }else{
+ /* Run commands received from standard input
+ */
+ if( isatty(fileno(stdout)) && isatty(fileno(stdin)) ){
+ char *zHome;
+ char *zHistory = 0;
+ printf(
+ "SQLite version %s\n"
+ "Enter \".help\" for instructions\n",
+ sqlite3_libversion()
+ );
+ zHome = find_home_dir();
+ if( zHome && (zHistory = malloc(strlen(zHome)+20))!=0 ){
+ sprintf(zHistory,"%s/.sqlite_history", zHome);
+ }
+ if( zHistory ) read_history(zHistory);
+ process_input(&data, 0);
+ if( zHistory ){
+ stifle_history(100);
+ write_history(zHistory);
+ }
+ }else{
+ process_input(&data, stdin);
+ }
+ }
+ set_table_name(&data, 0);
+ if( db ) sqlite3_close(db);
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/sqlite3.h b/kopete/plugins/statistics/sqlite/sqlite3.h
new file mode 100644
index 00000000..d6b99049
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/sqlite3.h
@@ -0,0 +1,1166 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This header file defines the interface that the SQLite library
+** presents to client programs.
+**
+** @(#) $Id$
+*/
+#ifndef _SQLITE3_H_
+#define _SQLITE3_H_
+#include <stdarg.h> /* Needed for the definition of va_list */
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** The version of the SQLite library.
+*/
+#ifdef SQLITE_VERSION
+# undef SQLITE_VERSION
+#else
+# define SQLITE_VERSION "3.0.8"
+#endif
+
+/*
+** The version string is also compiled into the library so that a program
+** can check to make sure that the lib*.a file and the *.h file are from
+** the same version. The sqlite3_libversion() function returns a pointer
+** to the sqlite3_version variable - useful in DLLs which cannot access
+** global variables.
+*/
+extern const char sqlite3_version[];
+const char *sqlite3_libversion(void);
+
+/*
+** Each open sqlite database is represented by an instance of the
+** following opaque structure.
+*/
+typedef struct sqlite3 sqlite3;
+
+
+/*
+** Some compilers do not support the "long long" datatype. So we have
+** to do a typedef that for 64-bit integers that depends on what compiler
+** is being used.
+*/
+#if defined(_MSC_VER) || defined(__BORLANDC__)
+ typedef __int64 sqlite_int64;
+ typedef unsigned __int64 sqlite_uint64;
+#else
+ typedef long long int sqlite_int64;
+ typedef unsigned long long int sqlite_uint64;
+#endif
+
+
+/*
+** A function to close the database.
+**
+** Call this function with a pointer to a structure that was previously
+** returned from sqlite3_open() and the corresponding database will by closed.
+**
+** All SQL statements prepared using sqlite3_prepare() or
+** sqlite3_prepare16() must be deallocated using sqlite3_finalize() before
+** this routine is called. Otherwise, SQLITE_BUSY is returned and the
+** database connection remains open.
+*/
+int sqlite3_close(sqlite3 *);
+
+/*
+** The type for a callback function.
+*/
+typedef int (*sqlite3_callback)(void*,int,char**, char**);
+
+/*
+** A function to executes one or more statements of SQL.
+**
+** If one or more of the SQL statements are queries, then
+** the callback function specified by the 3rd parameter is
+** invoked once for each row of the query result. This callback
+** should normally return 0. If the callback returns a non-zero
+** value then the query is aborted, all subsequent SQL statements
+** are skipped and the sqlite3_exec() function returns the SQLITE_ABORT.
+**
+** The 4th parameter is an arbitrary pointer that is passed
+** to the callback function as its first parameter.
+**
+** The 2nd parameter to the callback function is the number of
+** columns in the query result. The 3rd parameter to the callback
+** is an array of strings holding the values for each column.
+** The 4th parameter to the callback is an array of strings holding
+** the names of each column.
+**
+** The callback function may be NULL, even for queries. A NULL
+** callback is not an error. It just means that no callback
+** will be invoked.
+**
+** If an error occurs while parsing or evaluating the SQL (but
+** not while executing the callback) then an appropriate error
+** message is written into memory obtained from malloc() and
+** *errmsg is made to point to that message. The calling function
+** is responsible for freeing the memory that holds the error
+** message. Use sqlite3_free() for this. If errmsg==NULL,
+** then no error message is ever written.
+**
+** The return value is is SQLITE_OK if there are no errors and
+** some other return code if there is an error. The particular
+** return value depends on the type of error.
+**
+** If the query could not be executed because a database file is
+** locked or busy, then this function returns SQLITE_BUSY. (This
+** behavior can be modified somewhat using the sqlite3_busy_handler()
+** and sqlite3_busy_timeout() functions below.)
+*/
+int sqlite3_exec(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be executed */
+ sqlite3_callback, /* Callback function */
+ void *, /* 1st argument to callback function */
+ char **errmsg /* Error msg written here */
+);
+
+/*
+** Return values for sqlite3_exec() and sqlite3_step()
+*/
+#define SQLITE_OK 0 /* Successful result */
+#define SQLITE_ERROR 1 /* SQL error or missing database */
+#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
+#define SQLITE_PERM 3 /* Access permission denied */
+#define SQLITE_ABORT 4 /* Callback routine requested an abort */
+#define SQLITE_BUSY 5 /* The database file is locked */
+#define SQLITE_LOCKED 6 /* A table in the database is locked */
+#define SQLITE_NOMEM 7 /* A malloc() failed */
+#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
+#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite3_interrupt()*/
+#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
+#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
+#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
+#define SQLITE_FULL 13 /* Insertion failed because database is full */
+#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
+#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
+#define SQLITE_EMPTY 16 /* Database is empty */
+#define SQLITE_SCHEMA 17 /* The database schema changed */
+#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
+#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
+#define SQLITE_MISMATCH 20 /* Data type mismatch */
+#define SQLITE_MISUSE 21 /* Library used incorrectly */
+#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
+#define SQLITE_AUTH 23 /* Authorization denied */
+#define SQLITE_FORMAT 24 /* Auxiliary database format error */
+#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
+#define SQLITE_NOTADB 26 /* File opened that is not a database file */
+#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
+#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
+
+/*
+** Each entry in an SQLite table has a unique integer key. (The key is
+** the value of the INTEGER PRIMARY KEY column if there is such a column,
+** otherwise the key is generated at random. The unique key is always
+** available as the ROWID, OID, or _ROWID_ column.) The following routine
+** returns the integer key of the most recent insert in the database.
+**
+** This function is similar to the mysql_insert_id() function from MySQL.
+*/
+sqlite_int64 sqlite3_last_insert_rowid(sqlite3*);
+
+/*
+** This function returns the number of database rows that were changed
+** (or inserted or deleted) by the most recent called sqlite3_exec().
+**
+** All changes are counted, even if they were later undone by a
+** ROLLBACK or ABORT. Except, changes associated with creating and
+** dropping tables are not counted.
+**
+** If a callback invokes sqlite3_exec() recursively, then the changes
+** in the inner, recursive call are counted together with the changes
+** in the outer call.
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements form the table.) Because of
+** this optimization, the change count for "DELETE FROM table" will be
+** zero regardless of the number of elements that were originally in the
+** table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+*/
+int sqlite3_changes(sqlite3*);
+
+/*
+** This function returns the number of database rows that have been
+** modified by INSERT, UPDATE or DELETE statements since the database handle
+** was opened. This includes UPDATE, INSERT and DELETE statements executed
+** as part of trigger programs. All changes are counted as soon as the
+** statement that makes them is completed (when the statement handle is
+** passed to sqlite3_reset() or sqlite_finalise()).
+**
+** SQLite implements the command "DELETE FROM table" without a WHERE clause
+** by dropping and recreating the table. (This is much faster than going
+** through and deleting individual elements form the table.) Because of
+** this optimization, the change count for "DELETE FROM table" will be
+** zero regardless of the number of elements that were originally in the
+** table. To get an accurate count of the number of rows deleted, use
+** "DELETE FROM table WHERE 1" instead.
+*/
+int sqlite3_total_changes(sqlite3*);
+
+/* This function causes any pending database operation to abort and
+** return at its earliest opportunity. This routine is typically
+** called in response to a user action such as pressing "Cancel"
+** or Ctrl-C where the user wants a long query operation to halt
+** immediately.
+*/
+void sqlite3_interrupt(sqlite3*);
+
+
+/* These functions return true if the given input string comprises
+** one or more complete SQL statements. For the sqlite3_complete() call,
+** the parameter must be a nul-terminated UTF-8 string. For
+** sqlite3_complete16(), a nul-terminated machine byte order UTF-16 string
+** is required.
+**
+** The algorithm is simple. If the last token other than spaces
+** and comments is a semicolon, then return true. otherwise return
+** false.
+*/
+int sqlite3_complete(const char *sql);
+int sqlite3_complete16(const void *sql);
+
+/*
+** This routine identifies a callback function that is invoked
+** whenever an attempt is made to open a database table that is
+** currently locked by another process or thread. If the busy callback
+** is NULL, then sqlite3_exec() returns SQLITE_BUSY immediately if
+** it finds a locked table. If the busy callback is not NULL, then
+** sqlite3_exec() invokes the callback with three arguments. The
+** second argument is the name of the locked table and the third
+** argument is the number of times the table has been busy. If the
+** busy callback returns 0, then sqlite3_exec() immediately returns
+** SQLITE_BUSY. If the callback returns non-zero, then sqlite3_exec()
+** tries to open the table again and the cycle repeats.
+**
+** The default busy callback is NULL.
+**
+** Sqlite is re-entrant, so the busy handler may start a new query.
+** (It is not clear why anyone would every want to do this, but it
+** is allowed, in theory.) But the busy handler may not close the
+** database. Closing the database from a busy handler will delete
+** data structures out from under the executing query and will
+** probably result in a coredump.
+*/
+int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+
+/*
+** This routine sets a busy handler that sleeps for a while when a
+** table is locked. The handler will sleep multiple times until
+** at least "ms" milleseconds of sleeping have been done. After
+** "ms" milleseconds of sleeping, the handler returns 0 which
+** causes sqlite3_exec() to return SQLITE_BUSY.
+**
+** Calling this routine with an argument less than or equal to zero
+** turns off all busy handlers.
+*/
+int sqlite3_busy_timeout(sqlite3*, int ms);
+
+/*
+** This next routine is really just a wrapper around sqlite3_exec().
+** Instead of invoking a user-supplied callback for each row of the
+** result, this routine remembers each row of the result in memory
+** obtained from malloc(), then returns all of the result after the
+** query has finished.
+**
+** As an example, suppose the query result where this table:
+**
+** Name | Age
+** -----------------------
+** Alice | 43
+** Bob | 28
+** Cindy | 21
+**
+** If the 3rd argument were &azResult then after the function returns
+** azResult will contain the following data:
+**
+** azResult[0] = "Name";
+** azResult[1] = "Age";
+** azResult[2] = "Alice";
+** azResult[3] = "43";
+** azResult[4] = "Bob";
+** azResult[5] = "28";
+** azResult[6] = "Cindy";
+** azResult[7] = "21";
+**
+** Notice that there is an extra row of data containing the column
+** headers. But the *nrow return value is still 3. *ncolumn is
+** set to 2. In general, the number of values inserted into azResult
+** will be ((*nrow) + 1)*(*ncolumn).
+**
+** After the calling function has finished using the result, it should
+** pass the result data pointer to sqlite3_free_table() in order to
+** release the memory that was malloc-ed. Because of the way the
+** malloc() happens, the calling function must not try to call
+** malloc() directly. Only sqlite3_free_table() is able to release
+** the memory properly and safely.
+**
+** The return value of this routine is the same as from sqlite3_exec().
+*/
+int sqlite3_get_table(
+ sqlite3*, /* An open database */
+ const char *sql, /* SQL to be executed */
+ char ***resultp, /* Result written to a char *[] that this points to */
+ int *nrow, /* Number of result rows written here */
+ int *ncolumn, /* Number of result columns written here */
+ char **errmsg /* Error msg written here */
+);
+
+/*
+** Call this routine to free the memory that sqlite3_get_table() allocated.
+*/
+void sqlite3_free_table(char **result);
+
+/*
+** The following routines are variants of the "sprintf()" from the
+** standard C library. The resulting string is written into memory
+** obtained from malloc() so that there is never a possiblity of buffer
+** overflow. These routines also implement some additional formatting
+** options that are useful for constructing SQL statements.
+**
+** The strings returned by these routines should be freed by calling
+** sqlite3_free().
+**
+** All of the usual printf formatting options apply. In addition, there
+** is a "%q" option. %q works like %s in that it substitutes a null-terminated
+** string from the argument list. But %q also doubles every '\'' character.
+** %q is designed for use inside a string literal. By doubling each '\''
+** character it escapes that character and allows it to be inserted into
+** the string.
+**
+** For example, so some string variable contains text as follows:
+**
+** char *zText = "It's a happy day!";
+**
+** We can use this text in an SQL statement as follows:
+**
+** sqlite3_exec_printf(db, "INSERT INTO table VALUES('%q')",
+** callback1, 0, 0, zText);
+**
+** Because the %q format string is used, the '\'' character in zText
+** is escaped and the SQL generated is as follows:
+**
+** INSERT INTO table1 VALUES('It''s a happy day!')
+**
+** This is correct. Had we used %s instead of %q, the generated SQL
+** would have looked like this:
+**
+** INSERT INTO table1 VALUES('It's a happy day!');
+**
+** This second example is an SQL syntax error. As a general rule you
+** should always use %q instead of %s when inserting text into a string
+** literal.
+*/
+char *sqlite3_mprintf(const char*,...);
+char *sqlite3_vmprintf(const char*, va_list);
+void sqlite3_free(char *z);
+char *sqlite3_snprintf(int,char*,const char*, ...);
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This routine registers a callback with the SQLite library. The
+** callback is invoked (at compile-time, not at run-time) for each
+** attempt to access a column of a table in the database. The callback
+** returns SQLITE_OK if access is allowed, SQLITE_DENY if the entire
+** SQL statement should be aborted with an error and SQLITE_IGNORE
+** if the column should be treated as a NULL value.
+*/
+int sqlite3_set_authorizer(
+ sqlite3*,
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
+ void *pUserData
+);
+#endif
+
+/*
+** The second parameter to the access authorization function above will
+** be one of the values below. These values signify what kind of operation
+** is to be authorized. The 3rd and 4th parameters to the authorization
+** function will be parameters or NULL depending on which of the following
+** codes is used as the second parameter. The 5th parameter is the name
+** of the database ("main", "temp", etc.) if applicable. The 6th parameter
+** is the name of the inner-most trigger or view that is responsible for
+** the access attempt or NULL if this access attempt is directly from
+** input SQL code.
+**
+** Arg-3 Arg-4
+*/
+#define SQLITE_COPY 0 /* Table Name File Name */
+#define SQLITE_CREATE_INDEX 1 /* Index Name Table Name */
+#define SQLITE_CREATE_TABLE 2 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_INDEX 3 /* Index Name Table Name */
+#define SQLITE_CREATE_TEMP_TABLE 4 /* Table Name NULL */
+#define SQLITE_CREATE_TEMP_TRIGGER 5 /* Trigger Name Table Name */
+#define SQLITE_CREATE_TEMP_VIEW 6 /* View Name NULL */
+#define SQLITE_CREATE_TRIGGER 7 /* Trigger Name Table Name */
+#define SQLITE_CREATE_VIEW 8 /* View Name NULL */
+#define SQLITE_DELETE 9 /* Table Name NULL */
+#define SQLITE_DROP_INDEX 10 /* Index Name Table Name */
+#define SQLITE_DROP_TABLE 11 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_INDEX 12 /* Index Name Table Name */
+#define SQLITE_DROP_TEMP_TABLE 13 /* Table Name NULL */
+#define SQLITE_DROP_TEMP_TRIGGER 14 /* Trigger Name Table Name */
+#define SQLITE_DROP_TEMP_VIEW 15 /* View Name NULL */
+#define SQLITE_DROP_TRIGGER 16 /* Trigger Name Table Name */
+#define SQLITE_DROP_VIEW 17 /* View Name NULL */
+#define SQLITE_INSERT 18 /* Table Name NULL */
+#define SQLITE_PRAGMA 19 /* Pragma Name 1st arg or NULL */
+#define SQLITE_READ 20 /* Table Name Column Name */
+#define SQLITE_SELECT 21 /* NULL NULL */
+#define SQLITE_TRANSACTION 22 /* NULL NULL */
+#define SQLITE_UPDATE 23 /* Table Name Column Name */
+#define SQLITE_ATTACH 24 /* Filename NULL */
+#define SQLITE_DETACH 25 /* Database Name NULL */
+
+
+/*
+** The return value of the authorization function should be one of the
+** following constants:
+*/
+/* #define SQLITE_OK 0 // Allow access (This is actually defined above) */
+#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
+#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
+
+/*
+** Register a function that is called at every invocation of sqlite3_exec()
+** or sqlite3_prepare(). This function can be used (for example) to generate
+** a log file of all SQL executed against a database.
+*/
+void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
+
+/*
+** This routine configures a callback function - the progress callback - that
+** is invoked periodically during long running calls to sqlite3_exec(),
+** sqlite3_step() and sqlite3_get_table(). An example use for this API is to keep
+** a GUI updated during a large query.
+**
+** The progress callback is invoked once for every N virtual machine opcodes,
+** where N is the second argument to this function. The progress callback
+** itself is identified by the third argument to this function. The fourth
+** argument to this function is a void pointer passed to the progress callback
+** function each time it is invoked.
+**
+** If a call to sqlite3_exec(), sqlite3_step() or sqlite3_get_table() results
+** in less than N opcodes being executed, then the progress callback is not
+** invoked.
+**
+** To remove the progress callback altogether, pass NULL as the third
+** argument to this function.
+**
+** If the progress callback returns a result other than 0, then the current
+** query is immediately terminated and any database changes rolled back. If the
+** query was part of a larger transaction, then the transaction is not rolled
+** back and remains active. The sqlite3_exec() call returns SQLITE_ABORT.
+**
+******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
+*/
+void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
+
+/*
+** Register a callback function to be invoked whenever a new transaction
+** is committed. The pArg argument is passed through to the callback.
+** callback. If the callback function returns non-zero, then the commit
+** is converted into a rollback.
+**
+** If another function was previously registered, its pArg value is returned.
+** Otherwise NULL is returned.
+**
+** Registering a NULL function disables the callback.
+**
+******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
+*/
+void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*);
+
+/*
+** Open the sqlite database file "filename". The "filename" is UTF-8
+** encoded for sqlite3_open() and UTF-16 encoded in the native byte order
+** for sqlite3_open16(). An sqlite3* handle is returned in *ppDb, even
+** if an error occurs. If the database is opened (or created) successfully,
+** then SQLITE_OK is returned. Otherwise an error code is returned. The
+** sqlite3_errmsg() or sqlite3_errmsg16() routines can be used to obtain
+** an English language description of the error.
+**
+** If the database file does not exist, then a new database is created.
+** The encoding for the database is UTF-8 if sqlite3_open() is called and
+** UTF-16 if sqlite3_open16 is used.
+**
+** Whether or not an error occurs when it is opened, resources associated
+** with the sqlite3* handle should be released by passing it to
+** sqlite3_close() when it is no longer required.
+*/
+int sqlite3_open(
+ const char *filename, /* Database filename (UTF-8) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+int sqlite3_open16(
+ const void *filename, /* Database filename (UTF-16) */
+ sqlite3 **ppDb /* OUT: SQLite db handle */
+);
+
+/*
+** Return the error code for the most recent sqlite3_* API call associated
+** with sqlite3 handle 'db'. SQLITE_OK is returned if the most recent
+** API call was successful.
+**
+** Calls to many sqlite3_* functions set the error code and string returned
+** by sqlite3_errcode(), sqlite3_errmsg() and sqlite3_errmsg16()
+** (overwriting the previous values). Note that calls to sqlite3_errcode(),
+** sqlite3_errmsg() and sqlite3_errmsg16() themselves do not affect the
+** results of future invocations.
+**
+** Assuming no other intervening sqlite3_* API calls are made, the error
+** code returned by this function is associated with the same error as
+** the strings returned by sqlite3_errmsg() and sqlite3_errmsg16().
+*/
+int sqlite3_errcode(sqlite3 *db);
+
+/*
+** Return a pointer to a UTF-8 encoded string describing in english the
+** error condition for the most recent sqlite3_* API call. The returned
+** string is always terminated by an 0x00 byte.
+**
+** The string "not an error" is returned when the most recent API call was
+** successful.
+*/
+const char *sqlite3_errmsg(sqlite3*);
+
+/*
+** Return a pointer to a UTF-16 native byte order encoded string describing
+** in english the error condition for the most recent sqlite3_* API call.
+** The returned string is always terminated by a pair of 0x00 bytes.
+**
+** The string "not an error" is returned when the most recent API call was
+** successful.
+*/
+const void *sqlite3_errmsg16(sqlite3*);
+
+/*
+** An instance of the following opaque structure is used to represent
+** a compiled SQL statment.
+*/
+typedef struct sqlite3_stmt sqlite3_stmt;
+
+/*
+** To execute an SQL query, it must first be compiled into a byte-code
+** program using one of the following routines. The only difference between
+** them is that the second argument, specifying the SQL statement to
+** compile, is assumed to be encoded in UTF-8 for the sqlite3_prepare()
+** function and UTF-16 for sqlite3_prepare16().
+**
+** The first parameter "db" is an SQLite database handle. The second
+** parameter "zSql" is the statement to be compiled, encoded as either
+** UTF-8 or UTF-16 (see above). If the next parameter, "nBytes", is less
+** than zero, then zSql is read up to the first nul terminator. If
+** "nBytes" is not less than zero, then it is the length of the string zSql
+** in bytes (not characters).
+**
+** *pzTail is made to point to the first byte past the end of the first
+** SQL statement in zSql. This routine only compiles the first statement
+** in zSql, so *pzTail is left pointing to what remains uncompiled.
+**
+** *ppStmt is left pointing to a compiled SQL statement that can be
+** executed using sqlite3_step(). Or if there is an error, *ppStmt may be
+** set to NULL. If the input text contained no SQL (if the input is and
+** empty string or a comment) then *ppStmt is set to NULL.
+**
+** On success, SQLITE_OK is returned. Otherwise an error code is returned.
+*/
+int sqlite3_prepare(
+ sqlite3 *db, /* Database handle */
+ const char *zSql, /* SQL statement, UTF-8 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const char **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+int sqlite3_prepare16(
+ sqlite3 *db, /* Database handle */
+ const void *zSql, /* SQL statement, UTF-16 encoded */
+ int nBytes, /* Length of zSql in bytes. */
+ sqlite3_stmt **ppStmt, /* OUT: Statement handle */
+ const void **pzTail /* OUT: Pointer to unused portion of zSql */
+);
+
+/*
+** Pointers to the following two opaque structures are used to communicate
+** with the implementations of user-defined functions.
+*/
+typedef struct sqlite3_context sqlite3_context;
+typedef struct Mem sqlite3_value;
+
+/*
+** In the SQL strings input to sqlite3_prepare() and sqlite3_prepare16(),
+** one or more literals can be replace by a wildcard "?" or ":N:" where
+** N is an integer. These value of these wildcard literals can be set
+** using the routines listed below.
+**
+** In every case, the first parameter is a pointer to the sqlite3_stmt
+** structure returned from sqlite3_prepare(). The second parameter is the
+** index of the wildcard. The first "?" has an index of 1. ":N:" wildcards
+** use the index N.
+**
+** The fifth parameter to sqlite3_bind_blob(), sqlite3_bind_text(), and
+** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+** text after SQLite has finished with it. If the fifth argument is the
+** special value SQLITE_STATIC, then the library assumes that the information
+** is in static, unmanaged space and does not need to be freed. If the
+** fifth argument has the value SQLITE_TRANSIENT, then SQLite makes its
+** own private copy of the data.
+**
+** The sqlite3_bind_* routine must be called before sqlite3_step() after
+** an sqlite3_prepare() or sqlite3_reset(). Unbound wildcards are interpreted
+** as NULL.
+*/
+int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+int sqlite3_bind_double(sqlite3_stmt*, int, double);
+int sqlite3_bind_int(sqlite3_stmt*, int, int);
+int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite_int64);
+int sqlite3_bind_null(sqlite3_stmt*, int);
+int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
+
+/*
+** Return the number of wildcards in a compiled SQL statement. This
+** routine was added to support DBD::SQLite.
+*/
+int sqlite3_bind_parameter_count(sqlite3_stmt*);
+
+/*
+** Return the name of the i-th parameter. Ordinary wildcards "?" are
+** nameless and a NULL is returned. For wildcards of the form :N or
+** $vvvv the complete text of the wildcard is returned.
+** NULL is returned if the index is out of range.
+*/
+const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
+
+/*
+** Return the index of a parameter with the given name. The name
+** must match exactly. If no parameter with the given name is found,
+** return 0.
+*/
+int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
+
+/*
+** Return the number of columns in the result set returned by the compiled
+** SQL statement. This routine returns 0 if pStmt is an SQL statement
+** that does not return data (for example an UPDATE).
+*/
+int sqlite3_column_count(sqlite3_stmt *pStmt);
+
+/*
+** The first parameter is a compiled SQL statement. This function returns
+** the column heading for the Nth column of that statement, where N is the
+** second function parameter. The string returned is UTF-8 for
+** sqlite3_column_name() and UTF-16 for sqlite3_column_name16().
+*/
+const char *sqlite3_column_name(sqlite3_stmt*,int);
+const void *sqlite3_column_name16(sqlite3_stmt*,int);
+
+/*
+** The first parameter is a compiled SQL statement. If this statement
+** is a SELECT statement, the Nth column of the returned result set
+** of the SELECT is a table column then the declared type of the table
+** column is returned. If the Nth column of the result set is not at table
+** column, then a NULL pointer is returned. The returned string is always
+** UTF-8 encoded. For example, in the database schema:
+**
+** CREATE TABLE t1(c1 VARIANT);
+**
+** And the following statement compiled:
+**
+** SELECT c1 + 1, 0 FROM t1;
+**
+** Then this routine would return the string "VARIANT" for the second
+** result column (i==1), and a NULL pointer for the first result column
+** (i==0).
+*/
+const char *sqlite3_column_decltype(sqlite3_stmt *, int i);
+
+/*
+** The first parameter is a compiled SQL statement. If this statement
+** is a SELECT statement, the Nth column of the returned result set
+** of the SELECT is a table column then the declared type of the table
+** column is returned. If the Nth column of the result set is not at table
+** column, then a NULL pointer is returned. The returned string is always
+** UTF-16 encoded. For example, in the database schema:
+**
+** CREATE TABLE t1(c1 INTEGER);
+**
+** And the following statement compiled:
+**
+** SELECT c1 + 1, 0 FROM t1;
+**
+** Then this routine would return the string "INTEGER" for the second
+** result column (i==1), and a NULL pointer for the first result column
+** (i==0).
+*/
+const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
+
+/*
+** After an SQL query has been compiled with a call to either
+** sqlite3_prepare() or sqlite3_prepare16(), then this function must be
+** called one or more times to execute the statement.
+**
+** The return value will be either SQLITE_BUSY, SQLITE_DONE,
+** SQLITE_ROW, SQLITE_ERROR, or SQLITE_MISUSE.
+**
+** SQLITE_BUSY means that the database engine attempted to open
+** a locked database and there is no busy callback registered.
+** Call sqlite3_step() again to retry the open.
+**
+** SQLITE_DONE means that the statement has finished executing
+** successfully. sqlite3_step() should not be called again on this virtual
+** machine.
+**
+** If the SQL statement being executed returns any data, then
+** SQLITE_ROW is returned each time a new row of data is ready
+** for processing by the caller. The values may be accessed using
+** the sqlite3_column_*() functions described below. sqlite3_step()
+** is called again to retrieve the next row of data.
+**
+** SQLITE_ERROR means that a run-time error (such as a constraint
+** violation) has occurred. sqlite3_step() should not be called again on
+** the VM. More information may be found by calling sqlite3_errmsg().
+**
+** SQLITE_MISUSE means that the this routine was called inappropriately.
+** Perhaps it was called on a virtual machine that had already been
+** finalized or on one that had previously returned SQLITE_ERROR or
+** SQLITE_DONE. Or it could be the case the the same database connection
+** is being used simulataneously by two or more threads.
+*/
+int sqlite3_step(sqlite3_stmt*);
+
+/*
+** Return the number of values in the current row of the result set.
+**
+** After a call to sqlite3_step() that returns SQLITE_ROW, this routine
+** will return the same value as the sqlite3_column_count() function.
+** After sqlite3_step() has returned an SQLITE_DONE, SQLITE_BUSY or
+** error code, or before sqlite3_step() has been called on a
+** compiled SQL statement, this routine returns zero.
+*/
+int sqlite3_data_count(sqlite3_stmt *pStmt);
+
+/*
+** Values are stored in the database in one of the following fundamental
+** types.
+*/
+#define SQLITE_INTEGER 1
+#define SQLITE_FLOAT 2
+/* #define SQLITE_TEXT 3 // See below */
+#define SQLITE_BLOB 4
+#define SQLITE_NULL 5
+
+/*
+** SQLite version 2 defines SQLITE_TEXT differently. To allow both
+** version 2 and version 3 to be included, undefine them both if a
+** conflict is seen. Define SQLITE3_TEXT to be the version 3 value.
+*/
+#ifdef SQLITE_TEXT
+# undef SQLITE_TEXT
+#else
+# define SQLITE_TEXT 3
+#endif
+#define SQLITE3_TEXT 3
+
+/*
+** The next group of routines returns information about the information
+** in a single column of the current result row of a query. In every
+** case the first parameter is a pointer to the SQL statement that is being
+** executed (the sqlite_stmt* that was returned from sqlite3_prepare()) and
+** the second argument is the index of the column for which information
+** should be returned. iCol is zero-indexed. The left-most column as an
+** index of 0.
+**
+** If the SQL statement is not currently point to a valid row, or if the
+** the colulmn index is out of range, the result is undefined.
+**
+** These routines attempt to convert the value where appropriate. For
+** example, if the internal representation is FLOAT and a text result
+** is requested, sprintf() is used internally to do the conversion
+** automatically. The following table details the conversions that
+** are applied:
+**
+** Internal Type Requested Type Conversion
+** ------------- -------------- --------------------------
+** NULL INTEGER Result is 0
+** NULL FLOAT Result is 0.0
+** NULL TEXT Result is an empty string
+** NULL BLOB Result is a zero-length BLOB
+** INTEGER FLOAT Convert from integer to float
+** INTEGER TEXT ASCII rendering of the integer
+** INTEGER BLOB Same as for INTEGER->TEXT
+** FLOAT INTEGER Convert from float to integer
+** FLOAT TEXT ASCII rendering of the float
+** FLOAT BLOB Same as FLOAT->TEXT
+** TEXT INTEGER Use atoi()
+** TEXT FLOAT Use atof()
+** TEXT BLOB No change
+** BLOB INTEGER Convert to TEXT then use atoi()
+** BLOB FLOAT Convert to TEXT then use atof()
+** BLOB TEXT Add a \000 terminator if needed
+**
+** The following access routines are provided:
+**
+** _type() Return the datatype of the result. This is one of
+** SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB,
+** or SQLITE_NULL.
+** _blob() Return the value of a BLOB.
+** _bytes() Return the number of bytes in a BLOB value or the number
+** of bytes in a TEXT value represented as UTF-8. The \000
+** terminator is included in the byte count for TEXT values.
+** _bytes16() Return the number of bytes in a BLOB value or the number
+** of bytes in a TEXT value represented as UTF-16. The \u0000
+** terminator is included in the byte count for TEXT values.
+** _double() Return a FLOAT value.
+** _int() Return an INTEGER value in the host computer's native
+** integer representation. This might be either a 32- or 64-bit
+** integer depending on the host.
+** _int64() Return an INTEGER value as a 64-bit signed integer.
+** _text() Return the value as UTF-8 text.
+** _text16() Return the value as UTF-16 text.
+*/
+const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
+int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
+double sqlite3_column_double(sqlite3_stmt*, int iCol);
+int sqlite3_column_int(sqlite3_stmt*, int iCol);
+sqlite_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
+const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
+const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
+int sqlite3_column_type(sqlite3_stmt*, int iCol);
+
+/*
+** The sqlite3_finalize() function is called to delete a compiled
+** SQL statement obtained by a previous call to sqlite3_prepare()
+** or sqlite3_prepare16(). If the statement was executed successfully, or
+** not executed at all, then SQLITE_OK is returned. If execution of the
+** statement failed then an error code is returned.
+**
+** This routine can be called at any point during the execution of the
+** virtual machine. If the virtual machine has not completed execution
+** when this routine is called, that is like encountering an error or
+** an interrupt. (See sqlite3_interrupt().) Incomplete updates may be
+** rolled back and transactions cancelled, depending on the circumstances,
+** and the result code returned will be SQLITE_ABORT.
+*/
+int sqlite3_finalize(sqlite3_stmt *pStmt);
+
+/*
+** The sqlite3_reset() function is called to reset a compiled SQL
+** statement obtained by a previous call to sqlite3_prepare() or
+** sqlite3_prepare16() back to it's initial state, ready to be re-executed.
+** Any SQL statement variables that had values bound to them using
+** the sqlite3_bind_*() API retain their values.
+*/
+int sqlite3_reset(sqlite3_stmt *pStmt);
+
+/*
+** The following two functions are used to add user functions or aggregates
+** implemented in C to the SQL langauge interpreted by SQLite. The
+** difference only between the two is that the second parameter, the
+** name of the (scalar) function or aggregate, is encoded in UTF-8 for
+** sqlite3_create_function() and UTF-16 for sqlite3_create_function16().
+**
+** The first argument is the database handle that the new function or
+** aggregate is to be added to. If a single program uses more than one
+** database handle internally, then user functions or aggregates must
+** be added individually to each database handle with which they will be
+** used.
+**
+** The third parameter is the number of arguments that the function or
+** aggregate takes. If this parameter is negative, then the function or
+** aggregate may take any number of arguments.
+**
+** The fourth parameter is one of SQLITE_UTF* values defined below,
+** indicating the encoding that the function is most likely to handle
+** values in. This does not change the behaviour of the programming
+** interface. However, if two versions of the same function are registered
+** with different encoding values, SQLite invokes the version likely to
+** minimize conversions between text encodings.
+**
+** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are
+** pointers to user implemented C functions that implement the user
+** function or aggregate. A scalar function requires an implementation of
+** the xFunc callback only, NULL pointers should be passed as the xStep
+** and xFinal parameters. An aggregate function requires an implementation
+** of xStep and xFinal, but NULL should be passed for xFunc. To delete an
+** existing user function or aggregate, pass NULL for all three function
+** callback. Specifying an inconstent set of callback values, such as an
+** xFunc and an xFinal, or an xStep but no xFinal, SQLITE_ERROR is
+** returned.
+*/
+int sqlite3_create_function(
+ sqlite3 *,
+ const char *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+int sqlite3_create_function16(
+ sqlite3*,
+ const void *zFunctionName,
+ int nArg,
+ int eTextRep,
+ void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*)
+);
+
+/*
+** The next routine returns the number of calls to xStep for a particular
+** aggregate function instance. The current call to xStep counts so this
+** routine always returns at least 1.
+*/
+int sqlite3_aggregate_count(sqlite3_context*);
+
+/*
+** The next group of routines returns information about parameters to
+** a user-defined function. Function implementations use these routines
+** to access their parameters. These routines are the same as the
+** sqlite3_column_* routines except that these routines take a single
+** sqlite3_value* pointer instead of an sqlite3_stmt* and an integer
+** column number.
+*/
+const void *sqlite3_value_blob(sqlite3_value*);
+int sqlite3_value_bytes(sqlite3_value*);
+int sqlite3_value_bytes16(sqlite3_value*);
+double sqlite3_value_double(sqlite3_value*);
+int sqlite3_value_int(sqlite3_value*);
+sqlite_int64 sqlite3_value_int64(sqlite3_value*);
+const unsigned char *sqlite3_value_text(sqlite3_value*);
+const void *sqlite3_value_text16(sqlite3_value*);
+const void *sqlite3_value_text16le(sqlite3_value*);
+const void *sqlite3_value_text16be(sqlite3_value*);
+int sqlite3_value_type(sqlite3_value*);
+
+/*
+** Aggregate functions use the following routine to allocate
+** a structure for storing their state. The first time this routine
+** is called for a particular aggregate, a new structure of size nBytes
+** is allocated, zeroed, and returned. On subsequent calls (for the
+** same aggregate instance) the same buffer is returned. The implementation
+** of the aggregate can use the returned buffer to accumulate data.
+**
+** The buffer allocated is freed automatically by SQLite.
+*/
+void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
+
+/*
+** The pUserData parameter to the sqlite3_create_function() and
+** sqlite3_create_aggregate() routines used to register user functions
+** is available to the implementation of the function using this
+** call.
+*/
+void *sqlite3_user_data(sqlite3_context*);
+
+/*
+** The following two functions may be used by scalar user functions to
+** associate meta-data with argument values. If the same value is passed to
+** multiple invocations of the user-function during query execution, under
+** some circumstances the associated meta-data may be preserved. This may
+** be used, for example, to add a regular-expression matching scalar
+** function. The compiled version of the regular expression is stored as
+** meta-data associated with the SQL value passed as the regular expression
+** pattern.
+**
+** Calling sqlite3_get_auxdata() returns a pointer to the meta data
+** associated with the Nth argument value to the current user function
+** call, where N is the second parameter. If no meta-data has been set for
+** that value, then a NULL pointer is returned.
+**
+** The sqlite3_set_auxdata() is used to associate meta data with a user
+** function argument. The third parameter is a pointer to the meta data
+** to be associated with the Nth user function argument value. The fourth
+** parameter specifies a 'delete function' that will be called on the meta
+** data pointer to release it when it is no longer required. If the delete
+** function pointer is NULL, it is not invoked.
+**
+** In practice, meta-data is preserved between function calls for
+** expressions that are constant at compile time. This includes literal
+** values and SQL variables.
+*/
+void *sqlite3_get_auxdata(sqlite3_context*, int);
+void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
+
+
+/*
+** These are special value for the destructor that is passed in as the
+** final argument to routines like sqlite3_result_blob(). If the destructor
+** argument is SQLITE_STATIC, it means that the content pointer is constant
+** and will never change. It does not need to be destroyed. The
+** SQLITE_TRANSIENT value means that the content will likely change in
+** the near future and that SQLite should make its own private copy of
+** the content before returning.
+*/
+#define SQLITE_STATIC ((void(*)(void *))0)
+#define SQLITE_TRANSIENT ((void(*)(void *))-1)
+
+/*
+** User-defined functions invoke the following routines in order to
+** set their return value.
+*/
+void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_double(sqlite3_context*, double);
+void sqlite3_result_error(sqlite3_context*, const char*, int);
+void sqlite3_result_error16(sqlite3_context*, const void*, int);
+void sqlite3_result_int(sqlite3_context*, int);
+void sqlite3_result_int64(sqlite3_context*, sqlite_int64);
+void sqlite3_result_null(sqlite3_context*);
+void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
+void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
+void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
+void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
+
+/*
+** These are the allowed values for the eTextRep argument to
+** sqlite3_create_collation and sqlite3_create_function.
+*/
+#define SQLITE_UTF8 1
+#define SQLITE_UTF16LE 2
+#define SQLITE_UTF16BE 3
+#define SQLITE_UTF16 4 /* Use native byte order */
+#define SQLITE_ANY 5 /* sqlite3_create_function only */
+
+/*
+** These two functions are used to add new collation sequences to the
+** sqlite3 handle specified as the first argument.
+**
+** The name of the new collation sequence is specified as a UTF-8 string
+** for sqlite3_create_collation() and a UTF-16 string for
+** sqlite3_create_collation16(). In both cases the name is passed as the
+** second function argument.
+**
+** The third argument must be one of the constants SQLITE_UTF8,
+** SQLITE_UTF16LE or SQLITE_UTF16BE, indicating that the user-supplied
+** routine expects to be passed pointers to strings encoded using UTF-8,
+** UTF-16 little-endian or UTF-16 big-endian respectively.
+**
+** A pointer to the user supplied routine must be passed as the fifth
+** argument. If it is NULL, this is the same as deleting the collation
+** sequence (so that SQLite cannot call it anymore). Each time the user
+** supplied function is invoked, it is passed a copy of the void* passed as
+** the fourth argument to sqlite3_create_collation() or
+** sqlite3_create_collation16() as its first parameter.
+**
+** The remaining arguments to the user-supplied routine are two strings,
+** each represented by a [length, data] pair and encoded in the encoding
+** that was passed as the third argument when the collation sequence was
+** registered. The user routine should return negative, zero or positive if
+** the first string is less than, equal to, or greater than the second
+** string. i.e. (STRING1 - STRING2).
+*/
+int sqlite3_create_collation(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+int sqlite3_create_collation16(
+ sqlite3*,
+ const char *zName,
+ int eTextRep,
+ void*,
+ int(*xCompare)(void*,int,const void*,int,const void*)
+);
+
+/*
+** To avoid having to register all collation sequences before a database
+** can be used, a single callback function may be registered with the
+** database handle to be called whenever an undefined collation sequence is
+** required.
+**
+** If the function is registered using the sqlite3_collation_needed() API,
+** then it is passed the names of undefined collation sequences as strings
+** encoded in UTF-8. If sqlite3_collation_needed16() is used, the names
+** are passed as UTF-16 in machine native byte order. A call to either
+** function replaces any existing callback.
+**
+** When the user-function is invoked, the first argument passed is a copy
+** of the second argument to sqlite3_collation_needed() or
+** sqlite3_collation_needed16(). The second argument is the database
+** handle. The third argument is one of SQLITE_UTF8, SQLITE_UTF16BE or
+** SQLITE_UTF16LE, indicating the most desirable form of the collation
+** sequence function required. The fourth parameter is the name of the
+** required collation sequence.
+**
+** The collation sequence is returned to SQLite by a collation-needed
+** callback using the sqlite3_create_collation() or
+** sqlite3_create_collation16() APIs, described above.
+*/
+int sqlite3_collation_needed(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const char*)
+);
+int sqlite3_collation_needed16(
+ sqlite3*,
+ void*,
+ void(*)(void*,sqlite3*,int eTextRep,const void*)
+);
+
+/*
+** Specify the key for an encrypted database. This routine should be
+** called right after sqlite3_open().
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_key(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The key */
+);
+
+/*
+** Change the key on an open database. If the current database is not
+** encrypted, this routine will encrypt it. If pNew==0 or nNew==0, the
+** database is decrypted.
+**
+** The code to implement this API is not available in the public release
+** of SQLite.
+*/
+int sqlite3_rekey(
+ sqlite3 *db, /* Database to be rekeyed */
+ const void *pKey, int nKey /* The new key */
+);
+
+/*
+** If the following global variable is made to point to a constant
+** string which is the name of a directory, then all temporary files
+** created by SQLite will be placed in that directory. If this variable
+** is NULL pointer, then SQLite does a search for an appropriate temporary
+** file directory.
+**
+** This variable should only be changed when there are no open databases.
+** Once sqlite3_open() has been called, this variable should not be changed
+** until all database connections are closed.
+*/
+extern const char *sqlite3_temp_directory;
+
+#ifdef __cplusplus
+} /* End of the 'extern "C"' block */
+#endif
+#endif
diff --git a/kopete/plugins/statistics/sqlite/sqliteInt.h b/kopete/plugins/statistics/sqlite/sqliteInt.h
new file mode 100644
index 00000000..b4fa474b
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/sqliteInt.h
@@ -0,0 +1,1419 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Internal interface definitions for SQLite.
+**
+** @(#) $Id$
+*/
+#ifndef _SQLITEINT_H_
+#define _SQLITEINT_H_
+
+/*
+** These #defines should enable >2GB file support on Posix if the
+** underlying operating system supports it. If the OS lacks
+** large file support, or if the OS is windows, these should be no-ops.
+**
+** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
+** on the compiler command line. This is necessary if you are compiling
+** on a recent machine (ex: RedHat 7.2) but you want your code to work
+** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
+** without this option, LFS is enable. But LFS does not exist in the kernel
+** in RedHat 6.0, so the code won't work. Hence, for maximum binary
+** portability you should omit LFS.
+**
+** Similar is true for MacOS. LFS is only supported on MacOS 9 and later.
+*/
+#ifndef SQLITE_DISABLE_LFS
+# define _LARGE_FILE 1
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+# define _LARGEFILE_SOURCE 1
+#endif
+
+#include "config.h"
+#include "sqlite3.h"
+#include "hash.h"
+#include "parse.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/*
+** The maximum number of in-memory pages to use for the main database
+** table and for temporary tables.
+*/
+#define MAX_PAGES 2000
+#define TEMP_PAGES 500
+
+/*
+** If the following macro is set to 1, then NULL values are considered
+** distinct for the SELECT DISTINCT statement and for UNION or EXCEPT
+** compound queries. No other SQL database engine (among those tested)
+** works this way except for OCELOT. But the SQL92 spec implies that
+** this is how things should work.
+**
+** If the following macro is set to 0, then NULLs are indistinct for
+** SELECT DISTINCT and for UNION.
+*/
+#define NULL_ALWAYS_DISTINCT 0
+
+/*
+** If the following macro is set to 1, then NULL values are considered
+** distinct when determining whether or not two entries are the same
+** in a UNIQUE index. This is the way PostgreSQL, Oracle, DB2, MySQL,
+** OCELOT, and Firebird all work. The SQL92 spec explicitly says this
+** is the way things are suppose to work.
+**
+** If the following macro is set to 0, the NULLs are indistinct for
+** a UNIQUE index. In this mode, you can only have a single NULL entry
+** for a column declared UNIQUE. This is the way Informix and SQL Server
+** work.
+*/
+#define NULL_DISTINCT_FOR_UNIQUE 1
+
+/*
+** The maximum number of attached databases. This must be at least 2
+** in order to support the main database file (0) and the file used to
+** hold temporary tables (1). And it must be less than 32 because
+** we use a bitmask of databases with a u32 in places (for example
+** the Parse.cookieMask field).
+*/
+#define MAX_ATTACHED 10
+
+/*
+** The maximum value of a ?nnn wildcard that the parser will accept.
+*/
+#define SQLITE_MAX_VARIABLE_NUMBER 999
+
+/*
+** When building SQLite for embedded systems where memory is scarce,
+** you can define one or more of the following macros to omit extra
+** features of the library and thus keep the size of the library to
+** a minimum.
+*/
+/* #define SQLITE_OMIT_AUTHORIZATION 1 */
+/* #define SQLITE_OMIT_INMEMORYDB 1 */
+/* #define SQLITE_OMIT_VACUUM 1 */
+/* #define SQLITE_OMIT_DATETIME_FUNCS 1 */
+/* #define SQLITE_OMIT_PROGRESS_CALLBACK 1 */
+
+/*
+** Integers of known sizes. These typedefs might change for architectures
+** where the sizes very. Preprocessor macros are available so that the
+** types can be conveniently redefined at compile-type. Like this:
+**
+** cc '-DUINTPTR_TYPE=long long int' ...
+*/
+#ifndef UINT64_TYPE
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+# define UINT64_TYPE unsigned __int64
+# else
+# define UINT64_TYPE unsigned long long int
+# endif
+#endif
+#ifndef UINT32_TYPE
+# define UINT32_TYPE unsigned int
+#endif
+#ifndef UINT16_TYPE
+# define UINT16_TYPE unsigned short int
+#endif
+#ifndef INT16_TYPE
+# define INT16_TYPE short int
+#endif
+#ifndef UINT8_TYPE
+# define UINT8_TYPE unsigned char
+#endif
+#ifndef INT8_TYPE
+# define INT8_TYPE signed char
+#endif
+#ifndef LONGDOUBLE_TYPE
+# define LONGDOUBLE_TYPE long double
+#endif
+#ifndef INTPTR_TYPE
+# if SQLITE_PTR_SZ==4
+# define INTPTR_TYPE int
+# else
+# define INTPTR_TYPE sqlite_int64
+# endif
+#endif
+#ifndef UINTPTR_TYPE
+# if SQLITE_PTR_SZ==4
+# define UINTPTR_TYPE unsigned int
+# else
+# define UINTPTR_TYPE sqlite_uint64
+# endif
+#endif
+typedef sqlite_int64 i64; /* 8-byte signed integer */
+typedef UINT64_TYPE u64; /* 8-byte unsigned integer */
+typedef UINT32_TYPE u32; /* 4-byte unsigned integer */
+typedef UINT16_TYPE u16; /* 2-byte unsigned integer */
+typedef INT16_TYPE i16; /* 2-byte signed integer */
+typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
+typedef UINT8_TYPE i8; /* 1-byte signed integer */
+typedef INTPTR_TYPE ptr; /* Big enough to hold a pointer */
+typedef UINTPTR_TYPE uptr; /* Big enough to hold a pointer */
+
+/*
+** Macros to determine whether the machine is big or little endian,
+** evaluated at runtime.
+*/
+extern const int sqlite3one;
+#define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0)
+#define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1)
+
+/*
+** An instance of the following structure is used to store the busy-handler
+** callback for a given sqlite handle.
+**
+** The sqlite.busyHandler member of the sqlite struct contains the busy
+** callback for the database handle. Each pager opened via the sqlite
+** handle is passed a pointer to sqlite.busyHandler. The busy-handler
+** callback is currently invoked only from within pager.c.
+*/
+typedef struct BusyHandler BusyHandler;
+struct BusyHandler {
+ int (*xFunc)(void *,int); /* The busy callback */
+ void *pArg; /* First arg to busy callback */
+};
+
+/*
+** Defer sourcing vdbe.h and btree.h until after the "u8" and
+** "BusyHandler typedefs.
+*/
+#include "vdbe.h"
+#include "btree.h"
+
+/*
+** This macro casts a pointer to an integer. Useful for doing
+** pointer arithmetic.
+*/
+#define Addr(X) ((uptr)X)
+
+/*
+** If memory allocation problems are found, recompile with
+**
+** -DSQLITE_DEBUG=1
+**
+** to enable some sanity checking on malloc() and free(). To
+** check for memory leaks, recompile with
+**
+** -DSQLITE_DEBUG=2
+**
+** and a line of text will be written to standard error for
+** each malloc() and free(). This output can be analyzed
+** by an AWK script to determine if there are any leaks.
+*/
+#ifdef SQLITE_DEBUG
+# define sqliteMalloc(X) sqlite3Malloc_(X,1,__FILE__,__LINE__)
+# define sqliteMallocRaw(X) sqlite3Malloc_(X,0,__FILE__,__LINE__)
+# define sqliteFree(X) sqlite3Free_(X,__FILE__,__LINE__)
+# define sqliteRealloc(X,Y) sqlite3Realloc_(X,Y,__FILE__,__LINE__)
+# define sqliteStrDup(X) sqlite3StrDup_(X,__FILE__,__LINE__)
+# define sqliteStrNDup(X,Y) sqlite3StrNDup_(X,Y,__FILE__,__LINE__)
+#else
+# define sqliteFree sqlite3FreeX
+# define sqliteMalloc sqlite3Malloc
+# define sqliteMallocRaw sqlite3MallocRaw
+# define sqliteRealloc sqlite3Realloc
+# define sqliteStrDup sqlite3StrDup
+# define sqliteStrNDup sqlite3StrNDup
+#endif
+
+/*
+** This variable gets set if malloc() ever fails. After it gets set,
+** the SQLite library shuts down permanently.
+*/
+extern int sqlite3_malloc_failed;
+
+/*
+** The following global variables are used for testing and debugging
+** only. They only work if SQLITE_DEBUG is defined.
+*/
+#ifdef SQLITE_DEBUG
+extern int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
+extern int sqlite3_nFree; /* Number of sqliteFree() calls */
+extern int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
+#endif
+
+/*
+** Name of the master database table. The master database table
+** is a special table that holds the names and attributes of all
+** user tables and indices.
+*/
+#define MASTER_NAME "sqlite_master"
+#define TEMP_MASTER_NAME "sqlite_temp_master"
+
+/*
+** The root-page of the master database table.
+*/
+#define MASTER_ROOT 1
+
+/*
+** The name of the schema table.
+*/
+#define SCHEMA_TABLE(x) (x==1?TEMP_MASTER_NAME:MASTER_NAME)
+
+/*
+** A convenience macro that returns the number of elements in
+** an array.
+*/
+#define ArraySize(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** Forward references to structures
+*/
+typedef struct Column Column;
+typedef struct Table Table;
+typedef struct Index Index;
+typedef struct Instruction Instruction;
+typedef struct Expr Expr;
+typedef struct ExprList ExprList;
+typedef struct Parse Parse;
+typedef struct Token Token;
+typedef struct IdList IdList;
+typedef struct SrcList SrcList;
+typedef struct WhereInfo WhereInfo;
+typedef struct WhereLevel WhereLevel;
+typedef struct Select Select;
+typedef struct AggExpr AggExpr;
+typedef struct FuncDef FuncDef;
+typedef struct Trigger Trigger;
+typedef struct TriggerStep TriggerStep;
+typedef struct TriggerStack TriggerStack;
+typedef struct FKey FKey;
+typedef struct Db Db;
+typedef struct AuthContext AuthContext;
+typedef struct KeyClass KeyClass;
+typedef struct CollSeq CollSeq;
+typedef struct KeyInfo KeyInfo;
+
+/*
+** Each database file to be accessed by the system is an instance
+** of the following structure. There are normally two of these structures
+** in the sqlite.aDb[] array. aDb[0] is the main database file and
+** aDb[1] is the database file used to hold temporary tables. Additional
+** databases may be attached.
+*/
+struct Db {
+ char *zName; /* Name of this database */
+ Btree *pBt; /* The B*Tree structure for this database file */
+ int schema_cookie; /* Database schema version number for this file */
+ Hash tblHash; /* All tables indexed by name */
+ Hash idxHash; /* All (named) indices indexed by name */
+ Hash trigHash; /* All triggers indexed by name */
+ Hash aFKey; /* Foreign keys indexed by to-table */
+ u16 flags; /* Flags associated with this database */
+ u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
+ u8 safety_level; /* How aggressive at synching data to disk */
+ int cache_size; /* Number of pages to use in the cache */
+ void *pAux; /* Auxiliary data. Usually NULL */
+ void (*xFreeAux)(void*); /* Routine to free pAux */
+};
+
+/*
+** These macros can be used to test, set, or clear bits in the
+** Db.flags field.
+*/
+#define DbHasProperty(D,I,P) (((D)->aDb[I].flags&(P))==(P))
+#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].flags&(P))!=0)
+#define DbSetProperty(D,I,P) (D)->aDb[I].flags|=(P)
+#define DbClearProperty(D,I,P) (D)->aDb[I].flags&=~(P)
+
+/*
+** Allowed values for the DB.flags field.
+**
+** The DB_SchemaLoaded flag is set after the database schema has been
+** read into internal hash tables.
+**
+** DB_UnresetViews means that one or more views have column names that
+** have been filled out. If the schema changes, these column names might
+** changes and so the view will need to be reset.
+*/
+#define DB_SchemaLoaded 0x0001 /* The schema has been loaded */
+#define DB_UnresetViews 0x0002 /* Some views have defined column names */
+
+#define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE)
+
+/*
+** Each database is an instance of the following structure.
+**
+** The sqlite.lastRowid records the last insert rowid generated by an
+** insert statement. Inserts on views do not affect its value. Each
+** trigger has its own context, so that lastRowid can be updated inside
+** triggers as usual. The previous value will be restored once the trigger
+** exits. Upon entering a before or instead of trigger, lastRowid is no
+** longer (since after version 2.8.12) reset to -1.
+**
+** The sqlite.nChange does not count changes within triggers and keeps no
+** context. It is reset at start of sqlite3_exec.
+** The sqlite.lsChange represents the number of changes made by the last
+** insert, update, or delete statement. It remains constant throughout the
+** length of a statement and is then updated by OP_SetCounts. It keeps a
+** context stack just like lastRowid so that the count of changes
+** within a trigger is not seen outside the trigger. Changes to views do not
+** affect the value of lsChange.
+** The sqlite.csChange keeps track of the number of current changes (since
+** the last statement) and is used to update sqlite_lsChange.
+**
+** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16
+** store the most recent error code and, if applicable, string. The
+** internal function sqlite3Error() is used to set these variables
+** consistently.
+*/
+struct sqlite3 {
+ int nDb; /* Number of backends currently in use */
+ Db *aDb; /* All backends */
+ Db aDbStatic[2]; /* Static space for the 2 default backends */
+ int flags; /* Miscellanous flags. See below */
+ u8 file_format; /* What file format version is this database? */
+ u8 temp_store; /* 1: file 2: memory 0: default */
+ int nTable; /* Number of tables in the database */
+ BusyHandler busyHandler; /* Busy callback */
+ void *pCommitArg; /* Argument to xCommitCallback() */
+ int (*xCommitCallback)(void*);/* Invoked at every commit. */
+ Hash aFunc; /* All functions that can be in SQL exprs */
+ Hash aCollSeq; /* All collating sequences */
+ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
+ i64 lastRowid; /* ROWID of most recent insert (see above) */
+ i64 priorNewRowid; /* Last randomly generated ROWID */
+ int magic; /* Magic number for detect library misuse */
+ int nChange; /* Value returned by sqlite3_changes() */
+ int nTotalChange; /* Value returned by sqlite3_total_changes() */
+ struct sqlite3InitInfo { /* Information used during initialization */
+ int iDb; /* When back is being initialized */
+ int newTnum; /* Rootpage of table being initialized */
+ u8 busy; /* TRUE if currently initializing */
+ } init;
+ struct Vdbe *pVdbe; /* List of active virtual machines */
+ int activeVdbeCnt; /* Number of vdbes currently executing */
+ void (*xTrace)(void*,const char*); /* Trace function */
+ void *pTraceArg; /* Argument to the trace function */
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
+ /* Access authorization function */
+ void *pAuthArg; /* 1st argument to the access auth function */
+#endif
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ int (*xProgress)(void *); /* The progress callback */
+ void *pProgressArg; /* Argument to the progress callback */
+ int nProgressOps; /* Number of opcodes for progress callback */
+#endif
+
+ int errCode; /* Most recent error code (SQLITE_*) */
+ u8 enc; /* Text encoding for this database. */
+ u8 autoCommit; /* The auto-commit flag. */
+ void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
+ void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
+ void *pCollNeededArg;
+ sqlite3_value *pValue; /* Value used for transient conversions */
+ sqlite3_value *pErr; /* Most recent error message */
+
+ char *zErrMsg; /* Most recent error message (UTF-8 encoded) */
+ char *zErrMsg16; /* Most recent error message (UTF-8 encoded) */
+};
+
+/*
+** Possible values for the sqlite.flags and or Db.flags fields.
+**
+** On sqlite.flags, the SQLITE_InTrans value means that we have
+** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement
+** transaction is active on that particular database file.
+*/
+#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
+#define SQLITE_Initialized 0x00000002 /* True after initialization */
+#define SQLITE_Interrupt 0x00000004 /* Cancel current operation */
+#define SQLITE_InTrans 0x00000008 /* True if in a transaction */
+#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */
+#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */
+#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
+#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
+ /* DELETE, or UPDATE and return */
+ /* the count using a callback. */
+#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
+ /* result set is empty */
+#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */
+
+/*
+** Possible values for the sqlite.magic field.
+** The numbers are obtained at random and have no special meaning, other
+** than being distinct from one another.
+*/
+#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */
+#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */
+#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
+#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
+
+/*
+** Each SQL function is defined by an instance of the following
+** structure. A pointer to this structure is stored in the sqlite.aFunc
+** hash table. When multiple functions have the same name, the hash table
+** points to a linked list of these structures.
+*/
+struct FuncDef {
+ char *zName; /* SQL name of the function */
+ int nArg; /* Number of arguments. -1 means unlimited */
+ u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
+ void *pUserData; /* User data parameter */
+ FuncDef *pNext; /* Next function with same name */
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */
+ void (*xFinalize)(sqlite3_context*); /* Aggregate finializer */
+ u8 needCollSeq; /* True if sqlite3GetFuncCollSeq() might be called */
+};
+
+/*
+** information about each column of an SQL table is held in an instance
+** of this structure.
+*/
+struct Column {
+ char *zName; /* Name of this column */
+ char *zDflt; /* Default value of this column */
+ char *zType; /* Data type for this column */
+ CollSeq *pColl; /* Collating sequence. If NULL, use the default */
+ u8 notNull; /* True if there is a NOT NULL constraint */
+ u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */
+ char affinity; /* One of the SQLITE_AFF_... values */
+};
+
+/*
+** A "Collating Sequence" is defined by an instance of the following
+** structure. Conceptually, a collating sequence consists of a name and
+** a comparison routine that defines the order of that sequence.
+**
+** There may two seperate implementations of the collation function, one
+** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that
+** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine
+** native byte order. When a collation sequence is invoked, SQLite selects
+** the version that will require the least expensive encoding
+** transalations, if any.
+**
+** The CollSeq.pUser member variable is an extra parameter that passed in
+** as the first argument to the UTF-8 comparison function, xCmp.
+** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function,
+** xCmp16.
+**
+** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the
+** collating sequence is undefined. Indices built on an undefined
+** collating sequence may not be read or written.
+*/
+struct CollSeq {
+ char *zName; /* Name of the collating sequence, UTF-8 encoded */
+ u8 enc; /* Text encoding handled by xCmp() */
+ void *pUser; /* First argument to xCmp() */
+ int (*xCmp)(void*,int, const void*, int, const void*);
+};
+
+/*
+** A sort order can be either ASC or DESC.
+*/
+#define SQLITE_SO_ASC 0 /* Sort in ascending order */
+#define SQLITE_SO_DESC 1 /* Sort in ascending order */
+
+/*
+** Column affinity types.
+*/
+#define SQLITE_AFF_INTEGER 'i'
+#define SQLITE_AFF_NUMERIC 'n'
+#define SQLITE_AFF_TEXT 't'
+#define SQLITE_AFF_NONE 'o'
+
+
+/*
+** Each SQL table is represented in memory by an instance of the
+** following structure.
+**
+** Table.zName is the name of the table. The case of the original
+** CREATE TABLE statement is stored, but case is not significant for
+** comparisons.
+**
+** Table.nCol is the number of columns in this table. Table.aCol is a
+** pointer to an array of Column structures, one for each column.
+**
+** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of
+** the column that is that key. Otherwise Table.iPKey is negative. Note
+** that the datatype of the PRIMARY KEY must be INTEGER for this field to
+** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of
+** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid
+** is generated for each row of the table. Table.hasPrimKey is true if
+** the table has any PRIMARY KEY, INTEGER or otherwise.
+**
+** Table.tnum is the page number for the root BTree page of the table in the
+** database file. If Table.iDb is the index of the database table backend
+** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that
+** holds temporary tables and indices. If Table.isTransient
+** is true, then the table is stored in a file that is automatically deleted
+** when the VDBE cursor to the table is closed. In this case Table.tnum
+** refers VDBE cursor number that holds the table open, not to the root
+** page number. Transient tables are used to hold the results of a
+** sub-query that appears instead of a real table name in the FROM clause
+** of a SELECT statement.
+*/
+struct Table {
+ char *zName; /* Name of the table */
+ int nCol; /* Number of columns in this table */
+ Column *aCol; /* Information about each column */
+ int iPKey; /* If not less then 0, use aCol[iPKey] as the primary key */
+ Index *pIndex; /* List of SQL indexes on this table. */
+ int tnum; /* Root BTree node for this table (see note above) */
+ Select *pSelect; /* NULL for tables. Points to definition if a view. */
+ u8 readOnly; /* True if this table should not be written by the user */
+ u8 iDb; /* Index into sqlite.aDb[] of the backend for this table */
+ u8 isTransient; /* True if automatically deleted when VDBE finishes */
+ u8 hasPrimKey; /* True if there exists a primary key */
+ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
+ Trigger *pTrigger; /* List of SQL triggers on this table */
+ FKey *pFKey; /* Linked list of all foreign keys in this table */
+ char *zColAff; /* String defining the affinity of each column */
+};
+
+/*
+** Each foreign key constraint is an instance of the following structure.
+**
+** A foreign key is associated with two tables. The "from" table is
+** the table that contains the REFERENCES clause that creates the foreign
+** key. The "to" table is the table that is named in the REFERENCES clause.
+** Consider this example:
+**
+** CREATE TABLE ex1(
+** a INTEGER PRIMARY KEY,
+** b INTEGER CONSTRAINT fk1 REFERENCES ex2(x)
+** );
+**
+** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
+**
+** Each REFERENCES clause generates an instance of the following structure
+** which is attached to the from-table. The to-table need not exist when
+** the from-table is created. The existance of the to-table is not checked
+** until an attempt is made to insert data into the from-table.
+**
+** The sqlite.aFKey hash table stores pointers to this structure
+** given the name of a to-table. For each to-table, all foreign keys
+** associated with that table are on a linked list using the FKey.pNextTo
+** field.
+*/
+struct FKey {
+ Table *pFrom; /* The table that constains the REFERENCES clause */
+ FKey *pNextFrom; /* Next foreign key in pFrom */
+ char *zTo; /* Name of table that the key points to */
+ FKey *pNextTo; /* Next foreign key that points to zTo */
+ int nCol; /* Number of columns in this key */
+ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
+ int iFrom; /* Index of column in pFrom */
+ char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
+ } *aCol; /* One entry for each of nCol column s */
+ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
+ u8 updateConf; /* How to resolve conflicts that occur on UPDATE */
+ u8 deleteConf; /* How to resolve conflicts that occur on DELETE */
+ u8 insertConf; /* How to resolve conflicts that occur on INSERT */
+};
+
+/*
+** SQLite supports many different ways to resolve a contraint
+** error. ROLLBACK processing means that a constraint violation
+** causes the operation in process to fail and for the current transaction
+** to be rolled back. ABORT processing means the operation in process
+** fails and any prior changes from that one operation are backed out,
+** but the transaction is not rolled back. FAIL processing means that
+** the operation in progress stops and returns an error code. But prior
+** changes due to the same operation are not backed out and no rollback
+** occurs. IGNORE means that the particular row that caused the constraint
+** error is not inserted or updated. Processing continues and no error
+** is returned. REPLACE means that preexisting database rows that caused
+** a UNIQUE constraint violation are removed so that the new insert or
+** update can proceed. Processing continues and no error is reported.
+**
+** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys.
+** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
+** same as ROLLBACK for DEFERRED keys. SETNULL means that the foreign
+** key is set to NULL. CASCADE means that a DELETE or UPDATE of the
+** referenced table row is propagated into the row that holds the
+** foreign key.
+**
+** The following symbolic values are used to record which type
+** of action to take.
+*/
+#define OE_None 0 /* There is no constraint to check */
+#define OE_Rollback 1 /* Fail the operation and rollback the transaction */
+#define OE_Abort 2 /* Back out changes but do no rollback transaction */
+#define OE_Fail 3 /* Stop the operation but leave all prior changes */
+#define OE_Ignore 4 /* Ignore the error. Do not do the INSERT or UPDATE */
+#define OE_Replace 5 /* Delete existing record, then do INSERT or UPDATE */
+
+#define OE_Restrict 6 /* OE_Abort for IMMEDIATE, OE_Rollback for DEFERRED */
+#define OE_SetNull 7 /* Set the foreign key value to NULL */
+#define OE_SetDflt 8 /* Set the foreign key value to its default */
+#define OE_Cascade 9 /* Cascade the changes */
+
+#define OE_Default 99 /* Do whatever the default action is */
+
+
+/*
+** An instance of the following structure is passed as the first
+** argument to sqlite3VdbeKeyCompare and is used to control the
+** comparison of the two index keys.
+**
+** If the KeyInfo.incrKey value is true and the comparison would
+** otherwise be equal, then return a result as if the second key larger.
+*/
+struct KeyInfo {
+ u8 enc; /* Text encoding - one of the TEXT_Utf* values */
+ u8 incrKey; /* Increase 2nd key by epsilon before comparison */
+ int nField; /* Number of entries in aColl[] */
+ u8 *aSortOrder; /* If defined an aSortOrder[i] is true, sort DESC */
+ CollSeq *aColl[1]; /* Collating sequence for each term of the key */
+};
+
+/*
+** Each SQL index is represented in memory by an
+** instance of the following structure.
+**
+** The columns of the table that are to be indexed are described
+** by the aiColumn[] field of this structure. For example, suppose
+** we have the following table and index:
+**
+** CREATE TABLE Ex1(c1 int, c2 int, c3 text);
+** CREATE INDEX Ex2 ON Ex1(c3,c1);
+**
+** In the Table structure describing Ex1, nCol==3 because there are
+** three columns in the table. In the Index structure describing
+** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed.
+** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
+** first column to be indexed (c3) has an index of 2 in Ex1.aCol[].
+** The second column to be indexed (c1) has an index of 0 in
+** Ex1.aCol[], hence Ex2.aiColumn[1]==0.
+**
+** The Index.onError field determines whether or not the indexed columns
+** must be unique and what to do if they are not. When Index.onError=OE_None,
+** it means this is not a unique index. Otherwise it is a unique index
+** and the value of Index.onError indicate the which conflict resolution
+** algorithm to employ whenever an attempt is made to insert a non-unique
+** element.
+*/
+struct Index {
+ char *zName; /* Name of this index */
+ int nColumn; /* Number of columns in the table used by this index */
+ int *aiColumn; /* Which columns are used by this index. 1st is 0 */
+ Table *pTable; /* The SQL table being indexed */
+ int tnum; /* Page containing root of this index in database file */
+ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
+ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
+ u8 iDb; /* Index in sqlite.aDb[] of where this index is stored */
+ char *zColAff; /* String defining the affinity of each column */
+ Index *pNext; /* The next index associated with the same table */
+ KeyInfo keyInfo; /* Info on how to order keys. MUST BE LAST */
+};
+
+/*
+** Each token coming out of the lexer is an instance of
+** this structure. Tokens are also used as part of an expression.
+**
+** Note if Token.z==0 then Token.dyn and Token.n are undefined and
+** may contain random values. Do not make any assuptions about Token.dyn
+** and Token.n when Token.z==0.
+*/
+struct Token {
+ const unsigned char *z; /* Text of the token. Not NULL-terminated! */
+ unsigned dyn : 1; /* True for malloced memory, false for static */
+ unsigned n : 31; /* Number of characters in this token */
+};
+
+/*
+** Each node of an expression in the parse tree is an instance
+** of this structure.
+**
+** Expr.op is the opcode. The integer parser token codes are reused
+** as opcodes here. For example, the parser defines TK_GE to be an integer
+** code representing the ">=" operator. This same integer code is reused
+** to represent the greater-than-or-equal-to operator in the expression
+** tree.
+**
+** Expr.pRight and Expr.pLeft are subexpressions. Expr.pList is a list
+** of argument if the expression is a function.
+**
+** Expr.token is the operator token for this node. For some expressions
+** that have subexpressions, Expr.token can be the complete text that gave
+** rise to the Expr. In the latter case, the token is marked as being
+** a compound token.
+**
+** An expression of the form ID or ID.ID refers to a column in a table.
+** For such expressions, Expr.op is set to TK_COLUMN and Expr.iTable is
+** the integer cursor number of a VDBE cursor pointing to that table and
+** Expr.iColumn is the column number for the specific column. If the
+** expression is used as a result in an aggregate SELECT, then the
+** value is also stored in the Expr.iAgg column in the aggregate so that
+** it can be accessed after all aggregates are computed.
+**
+** If the expression is a function, the Expr.iTable is an integer code
+** representing which function. If the expression is an unbound variable
+** marker (a question mark character '?' in the original SQL) then the
+** Expr.iTable holds the index number for that variable.
+**
+** The Expr.pSelect field points to a SELECT statement. The SELECT might
+** be the right operand of an IN operator. Or, if a scalar SELECT appears
+** in an expression the opcode is TK_SELECT and Expr.pSelect is the only
+** operand.
+*/
+struct Expr {
+ u8 op; /* Operation performed by this node */
+ char affinity; /* The affinity of the column or 0 if not a column */
+ u8 iDb; /* Database referenced by this expression */
+ u8 flags; /* Various flags. See below */
+ CollSeq *pColl; /* The collation type of the column or 0 */
+ Expr *pLeft, *pRight; /* Left and right subnodes */
+ ExprList *pList; /* A list of expressions used as function arguments
+ ** or in "<expr> IN (<expr-list)" */
+ Token token; /* An operand token */
+ Token span; /* Complete text of the expression */
+ int iTable, iColumn; /* When op==TK_COLUMN, then this expr node means the
+ ** iColumn-th field of the iTable-th table. */
+ int iAgg; /* When op==TK_COLUMN and pParse->useAgg==TRUE, pull
+ ** result from the iAgg-th element of the aggregator */
+ Select *pSelect; /* When the expression is a sub-select. Also the
+ ** right side of "<expr> IN (<select>)" */
+};
+
+/*
+** The following are the meanings of bits in the Expr.flags field.
+*/
+#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */
+
+/*
+** These macros can be used to test, set, or clear bits in the
+** Expr.flags field.
+*/
+#define ExprHasProperty(E,P) (((E)->flags&(P))==(P))
+#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0)
+#define ExprSetProperty(E,P) (E)->flags|=(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(P)
+
+/*
+** A list of expressions. Each expression may optionally have a
+** name. An expr/name combination can be used in several ways, such
+** as the list of "expr AS ID" fields following a "SELECT" or in the
+** list of "ID = expr" items in an UPDATE. A list of expressions can
+** also be used as the argument to a function, in which case the a.zName
+** field is not used.
+*/
+struct ExprList {
+ int nExpr; /* Number of expressions on the list */
+ int nAlloc; /* Number of entries allocated below */
+ struct ExprList_item {
+ Expr *pExpr; /* The list of expressions */
+ char *zName; /* Token associated with this expression */
+ u8 sortOrder; /* 1 for DESC or 0 for ASC */
+ u8 isAgg; /* True if this is an aggregate like count(*) */
+ u8 done; /* A flag to indicate when processing is finished */
+ } *a; /* One entry for each expression */
+};
+
+/*
+** An instance of this structure can hold a simple list of identifiers,
+** such as the list "a,b,c" in the following statements:
+**
+** INSERT INTO t(a,b,c) VALUES ...;
+** CREATE INDEX idx ON t(a,b,c);
+** CREATE TRIGGER trig BEFORE UPDATE ON t(a,b,c) ...;
+**
+** The IdList.a.idx field is used when the IdList represents the list of
+** column names after a table name in an INSERT statement. In the statement
+**
+** INSERT INTO t(a,b,c) ...
+**
+** If "a" is the k-th column of table "t", then IdList.a[0].idx==k.
+*/
+struct IdList {
+ int nId; /* Number of identifiers on the list */
+ int nAlloc; /* Number of entries allocated for a[] below */
+ struct IdList_item {
+ char *zName; /* Name of the identifier */
+ int idx; /* Index in some Table.aCol[] of a column named zName */
+ } *a;
+};
+
+/*
+** The following structure describes the FROM clause of a SELECT statement.
+** Each table or subquery in the FROM clause is a separate element of
+** the SrcList.a[] array.
+**
+** With the addition of multiple database support, the following structure
+** can also be used to describe a particular table such as the table that
+** is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL,
+** such a table must be a simple name: ID. But in SQLite, the table can
+** now be identified by a database name, a dot, then the table name: ID.ID.
+*/
+struct SrcList {
+ i16 nSrc; /* Number of tables or subqueries in the FROM clause */
+ i16 nAlloc; /* Number of entries allocated in a[] below */
+ struct SrcList_item {
+ char *zDatabase; /* Name of database holding this table */
+ char *zName; /* Name of the table */
+ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
+ Table *pTab; /* An SQL table corresponding to zName */
+ Select *pSelect; /* A SELECT statement used in place of a table name */
+ int jointype; /* Type of join between this table and the next */
+ int iCursor; /* The VDBE cursor number used to access this table */
+ Expr *pOn; /* The ON clause of a join */
+ IdList *pUsing; /* The USING clause of a join */
+ } a[1]; /* One entry for each identifier on the list */
+};
+
+/*
+** Permitted values of the SrcList.a.jointype field
+*/
+#define JT_INNER 0x0001 /* Any kind of inner or cross join */
+#define JT_NATURAL 0x0002 /* True for a "natural" join */
+#define JT_LEFT 0x0004 /* Left outer join */
+#define JT_RIGHT 0x0008 /* Right outer join */
+#define JT_OUTER 0x0010 /* The "OUTER" keyword is present */
+#define JT_ERROR 0x0020 /* unknown or unsupported join type */
+
+/*
+** For each nested loop in a WHERE clause implementation, the WhereInfo
+** structure contains a single instance of this structure. This structure
+** is intended to be private the the where.c module and should not be
+** access or modified by other modules.
+*/
+struct WhereLevel {
+ int iMem; /* Memory cell used by this level */
+ Index *pIdx; /* Index used */
+ int iCur; /* Cursor number used for this index */
+ int score; /* How well this indexed scored */
+ int brk; /* Jump here to break out of the loop */
+ int cont; /* Jump here to continue with the next loop cycle */
+ int op, p1, p2; /* Opcode used to terminate the loop */
+ int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
+ int top; /* First instruction of interior of the loop */
+ int inOp, inP1, inP2;/* Opcode used to implement an IN operator */
+ int bRev; /* Do the scan in the reverse direction */
+};
+
+/*
+** The WHERE clause processing routine has two halves. The
+** first part does the start of the WHERE loop and the second
+** half does the tail of the WHERE loop. An instance of
+** this structure is returned by the first half and passed
+** into the second half to give some continuity.
+*/
+struct WhereInfo {
+ Parse *pParse;
+ SrcList *pTabList; /* List of tables in the join */
+ int iContinue; /* Jump here to continue with next record */
+ int iBreak; /* Jump here to break out of the loop */
+ int nLevel; /* Number of nested loop */
+ WhereLevel a[1]; /* Information about each nest loop in the WHERE */
+};
+
+/*
+** An instance of the following structure contains all information
+** needed to generate code for a single SELECT statement.
+**
+** The zSelect field is used when the Select structure must be persistent.
+** Normally, the expression tree points to tokens in the original input
+** string that encodes the select. But if the Select structure must live
+** longer than its input string (for example when it is used to describe
+** a VIEW) we have to make a copy of the input string so that the nodes
+** of the expression tree will have something to point to. zSelect is used
+** to hold that copy.
+**
+** nLimit is set to -1 if there is no LIMIT clause. nOffset is set to 0.
+** If there is a LIMIT clause, the parser sets nLimit to the value of the
+** limit and nOffset to the value of the offset (or 0 if there is not
+** offset). But later on, nLimit and nOffset become the memory locations
+** in the VDBE that record the limit and offset counters.
+*/
+struct Select {
+ ExprList *pEList; /* The fields of the result */
+ u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
+ u8 isDistinct; /* True if the DISTINCT keyword is present */
+ SrcList *pSrc; /* The FROM clause */
+ Expr *pWhere; /* The WHERE clause */
+ ExprList *pGroupBy; /* The GROUP BY clause */
+ Expr *pHaving; /* The HAVING clause */
+ ExprList *pOrderBy; /* The ORDER BY clause */
+ Select *pPrior; /* Prior select in a compound select statement */
+ int nLimit, nOffset; /* LIMIT and OFFSET values. -1 means not used */
+ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
+ char *zSelect; /* Complete text of the SELECT command */
+ IdList **ppOpenTemp; /* OP_OpenTemp addresses used by multi-selects */
+};
+
+/*
+** The results of a select can be distributed in several ways.
+*/
+#define SRT_Callback 1 /* Invoke a callback with each row of result */
+#define SRT_Mem 2 /* Store result in a memory cell */
+#define SRT_Set 3 /* Store result as unique keys in a table */
+#define SRT_Union 5 /* Store result as keys in a table */
+#define SRT_Except 6 /* Remove result from a UNION table */
+#define SRT_Table 7 /* Store result as data with a unique key */
+#define SRT_TempTable 8 /* Store result in a trasient table */
+#define SRT_Discard 9 /* Do not save the results anywhere */
+#define SRT_Sorter 10 /* Store results in the sorter */
+#define SRT_Subroutine 11 /* Call a subroutine to handle results */
+
+/*
+** When a SELECT uses aggregate functions (like "count(*)" or "avg(f1)")
+** we have to do some additional analysis of expressions. An instance
+** of the following structure holds information about a single subexpression
+** somewhere in the SELECT statement. An array of these structures holds
+** all the information we need to generate code for aggregate
+** expressions.
+**
+** Note that when analyzing a SELECT containing aggregates, both
+** non-aggregate field variables and aggregate functions are stored
+** in the AggExpr array of the Parser structure.
+**
+** The pExpr field points to an expression that is part of either the
+** field list, the GROUP BY clause, the HAVING clause or the ORDER BY
+** clause. The expression will be freed when those clauses are cleaned
+** up. Do not try to delete the expression attached to AggExpr.pExpr.
+**
+** If AggExpr.pExpr==0, that means the expression is "count(*)".
+*/
+struct AggExpr {
+ int isAgg; /* if TRUE contains an aggregate function */
+ Expr *pExpr; /* The expression */
+ FuncDef *pFunc; /* Information about the aggregate function */
+};
+
+/*
+** An SQL parser context. A copy of this structure is passed through
+** the parser and down into all the parser action routine in order to
+** carry around information that is global to the entire parse.
+*/
+struct Parse {
+ sqlite3 *db; /* The main database structure */
+ int rc; /* Return code from execution */
+ char *zErrMsg; /* An error message */
+ Token sErrToken; /* The token at which the error occurred */
+ Token sNameToken; /* Token with unqualified schema object name */
+ Token sLastToken; /* The last token parsed */
+ const char *zSql; /* All SQL text */
+ const char *zTail; /* All SQL text past the last semicolon parsed */
+ Table *pNewTable; /* A table being constructed by CREATE TABLE */
+ Vdbe *pVdbe; /* An engine for executing database bytecode */
+ u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
+ u8 explain; /* True if the EXPLAIN flag is found on the query */
+ u8 nameClash; /* A permanent table name clashes with temp table name */
+ u8 useAgg; /* If true, extract field values from the aggregator
+ ** while generating expressions. Normally false */
+ u8 checkSchema; /* Causes schema cookie check after an error */
+ int nErr; /* Number of errors seen */
+ int nTab; /* Number of previously allocated VDBE cursors */
+ int nMem; /* Number of memory cells used so far */
+ int nSet; /* Number of sets used so far */
+ int nAgg; /* Number of aggregate expressions */
+ int nVar; /* Number of '?' variables seen in the SQL so far */
+ int nVarExpr; /* Number of used slots in apVarExpr[] */
+ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */
+ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */
+ AggExpr *aAgg; /* An array of aggregate expressions */
+ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
+ TriggerStack *trigStack; /* Trigger actions being coded */
+ u32 cookieMask; /* Bitmask of schema verified databases */
+ int cookieValue[MAX_ATTACHED+2]; /* Values of cookies to verify */
+ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
+ u32 writeMask; /* Start a write transaction on these databases */
+};
+
+/*
+** An instance of the following structure can be declared on a stack and used
+** to save the Parse.zAuthContext value so that it can be restored later.
+*/
+struct AuthContext {
+ const char *zAuthContext; /* Put saved Parse.zAuthContext here */
+ Parse *pParse; /* The Parse structure */
+};
+
+/*
+** Bitfield flags for P2 value in OP_PutIntKey and OP_Delete
+*/
+#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */
+#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */
+
+/*
+ * Each trigger present in the database schema is stored as an instance of
+ * struct Trigger.
+ *
+ * Pointers to instances of struct Trigger are stored in two ways.
+ * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
+ * database). This allows Trigger structures to be retrieved by name.
+ * 2. All triggers associated with a single table form a linked list, using the
+ * pNext member of struct Trigger. A pointer to the first element of the
+ * linked list is stored as the "pTrigger" member of the associated
+ * struct Table.
+ *
+ * The "step_list" member points to the first element of a linked list
+ * containing the SQL statements specified as the trigger program.
+ */
+struct Trigger {
+ char *name; /* The name of the trigger */
+ char *table; /* The table or view to which the trigger applies */
+ u8 iDb; /* Database containing this trigger */
+ u8 iTabDb; /* Database containing Trigger.table */
+ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */
+ u8 tr_tm; /* One of TK_BEFORE, TK_AFTER */
+ Expr *pWhen; /* The WHEN clause of the expresion (may be NULL) */
+ IdList *pColumns; /* If this is an UPDATE OF <column-list> trigger,
+ the <column-list> is stored here */
+ int foreach; /* One of TK_ROW or TK_STATEMENT */
+ Token nameToken; /* Token containing zName. Use during parsing only */
+
+ TriggerStep *step_list; /* Link list of trigger program steps */
+ Trigger *pNext; /* Next trigger associated with the table */
+};
+
+/*
+ * An instance of struct TriggerStep is used to store a single SQL statement
+ * that is a part of a trigger-program.
+ *
+ * Instances of struct TriggerStep are stored in a singly linked list (linked
+ * using the "pNext" member) referenced by the "step_list" member of the
+ * associated struct Trigger instance. The first element of the linked list is
+ * the first step of the trigger-program.
+ *
+ * The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or
+ * "SELECT" statement. The meanings of the other members is determined by the
+ * value of "op" as follows:
+ *
+ * (op == TK_INSERT)
+ * orconf -> stores the ON CONFLICT algorithm
+ * pSelect -> If this is an INSERT INTO ... SELECT ... statement, then
+ * this stores a pointer to the SELECT statement. Otherwise NULL.
+ * target -> A token holding the name of the table to insert into.
+ * pExprList -> If this is an INSERT INTO ... VALUES ... statement, then
+ * this stores values to be inserted. Otherwise NULL.
+ * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
+ * statement, then this stores the column-names to be
+ * inserted into.
+ *
+ * (op == TK_DELETE)
+ * target -> A token holding the name of the table to delete from.
+ * pWhere -> The WHERE clause of the DELETE statement if one is specified.
+ * Otherwise NULL.
+ *
+ * (op == TK_UPDATE)
+ * target -> A token holding the name of the table to update rows of.
+ * pWhere -> The WHERE clause of the UPDATE statement if one is specified.
+ * Otherwise NULL.
+ * pExprList -> A list of the columns to update and the expressions to update
+ * them to. See sqlite3Update() documentation of "pChanges"
+ * argument.
+ *
+ */
+struct TriggerStep {
+ int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
+ int orconf; /* OE_Rollback etc. */
+ Trigger *pTrig; /* The trigger that this step is a part of */
+
+ Select *pSelect; /* Valid for SELECT and sometimes
+ INSERT steps (when pExprList == 0) */
+ Token target; /* Valid for DELETE, UPDATE, INSERT steps */
+ Expr *pWhere; /* Valid for DELETE, UPDATE steps */
+ ExprList *pExprList; /* Valid for UPDATE statements and sometimes
+ INSERT steps (when pSelect == 0) */
+ IdList *pIdList; /* Valid for INSERT statements only */
+
+ TriggerStep * pNext; /* Next in the link-list */
+};
+
+/*
+ * An instance of struct TriggerStack stores information required during code
+ * generation of a single trigger program. While the trigger program is being
+ * coded, its associated TriggerStack instance is pointed to by the
+ * "pTriggerStack" member of the Parse structure.
+ *
+ * The pTab member points to the table that triggers are being coded on. The
+ * newIdx member contains the index of the vdbe cursor that points at the temp
+ * table that stores the new.* references. If new.* references are not valid
+ * for the trigger being coded (for example an ON DELETE trigger), then newIdx
+ * is set to -1. The oldIdx member is analogous to newIdx, for old.* references.
+ *
+ * The ON CONFLICT policy to be used for the trigger program steps is stored
+ * as the orconf member. If this is OE_Default, then the ON CONFLICT clause
+ * specified for individual triggers steps is used.
+ *
+ * struct TriggerStack has a "pNext" member, to allow linked lists to be
+ * constructed. When coding nested triggers (triggers fired by other triggers)
+ * each nested trigger stores its parent trigger's TriggerStack as the "pNext"
+ * pointer. Once the nested trigger has been coded, the pNext value is restored
+ * to the pTriggerStack member of the Parse stucture and coding of the parent
+ * trigger continues.
+ *
+ * Before a nested trigger is coded, the linked list pointed to by the
+ * pTriggerStack is scanned to ensure that the trigger is not about to be coded
+ * recursively. If this condition is detected, the nested trigger is not coded.
+ */
+struct TriggerStack {
+ Table *pTab; /* Table that triggers are currently being coded on */
+ int newIdx; /* Index of vdbe cursor to "new" temp table */
+ int oldIdx; /* Index of vdbe cursor to "old" temp table */
+ int orconf; /* Current orconf policy */
+ int ignoreJump; /* where to jump to for a RAISE(IGNORE) */
+ Trigger *pTrigger; /* The trigger currently being coded */
+ TriggerStack *pNext; /* Next trigger down on the trigger stack */
+};
+
+/*
+** The following structure contains information used by the sqliteFix...
+** routines as they walk the parse tree to make database references
+** explicit.
+*/
+typedef struct DbFixer DbFixer;
+struct DbFixer {
+ Parse *pParse; /* The parsing context. Error messages written here */
+ const char *zDb; /* Make sure all objects are contained in this database */
+ const char *zType; /* Type of the container - used for error messages */
+ const Token *pName; /* Name of the container - used for error messages */
+};
+
+/*
+** A pointer to this structure is used to communicate information
+** from sqlite3Init and OP_ParseSchema into the sqlite3InitCallback.
+*/
+typedef struct {
+ sqlite3 *db; /* The database being initialized */
+ char **pzErrMsg; /* Error message stored here */
+} InitData;
+
+
+/*
+ * This global flag is set for performance testing of triggers. When it is set
+ * SQLite will perform the overhead of building new and old trigger references
+ * even when no triggers exist
+ */
+extern int sqlite3_always_code_trigger_setup;
+
+/*
+** Internal function prototypes
+*/
+int sqlite3StrICmp(const char *, const char *);
+int sqlite3StrNICmp(const char *, const char *, int);
+int sqlite3HashNoCase(const char *, int);
+int sqlite3IsNumber(const char*, int*, u8);
+int sqlite3Compare(const char *, const char *);
+int sqlite3SortCompare(const char *, const char *);
+void sqlite3RealToSortable(double r, char *);
+#ifdef SQLITE_DEBUG
+ void *sqlite3Malloc_(int,int,char*,int);
+ void sqlite3Free_(void*,char*,int);
+ void *sqlite3Realloc_(void*,int,char*,int);
+ char *sqlite3StrDup_(const char*,char*,int);
+ char *sqlite3StrNDup_(const char*, int,char*,int);
+ void sqlite3CheckMemory(void*,int);
+#else
+ void *sqlite3Malloc(int);
+ void *sqlite3MallocRaw(int);
+ void sqlite3Free(void*);
+ void *sqlite3Realloc(void*,int);
+ char *sqlite3StrDup(const char*);
+ char *sqlite3StrNDup(const char*, int);
+# define sqlite3CheckMemory(a,b)
+#endif
+void sqlite3FreeX(void*);
+char *sqlite3MPrintf(const char*, ...);
+char *sqlite3VMPrintf(const char*, va_list);
+void sqlite3DebugPrintf(const char*, ...);
+void *sqlite3TextToPtr(const char*);
+void sqlite3SetString(char **, const char *, ...);
+void sqlite3ErrorMsg(Parse*, const char*, ...);
+void sqlite3Dequote(char*);
+int sqlite3KeywordCode(const char*, int);
+int sqlite3RunParser(Parse*, const char*, char **);
+void sqlite3FinishCoding(Parse*);
+Expr *sqlite3Expr(int, Expr*, Expr*, Token*);
+Expr *sqlite3ExprAnd(Expr*, Expr*);
+void sqlite3ExprSpan(Expr*,Token*,Token*);
+Expr *sqlite3ExprFunction(ExprList*, Token*);
+void sqlite3ExprAssignVarNumber(Parse*, Expr*);
+void sqlite3ExprDelete(Expr*);
+ExprList *sqlite3ExprListAppend(ExprList*,Expr*,Token*);
+void sqlite3ExprListDelete(ExprList*);
+int sqlite3Init(sqlite3*, char**);
+int sqlite3InitCallback(void*, int, char**, char**);
+void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
+void sqlite3ResetInternalSchema(sqlite3*, int);
+void sqlite3BeginParse(Parse*,int);
+void sqlite3RollbackInternalChanges(sqlite3*);
+void sqlite3CommitInternalChanges(sqlite3*);
+Table *sqlite3ResultSetOfSelect(Parse*,char*,Select*);
+void sqlite3OpenMasterTable(Vdbe *v, int);
+void sqlite3StartTable(Parse*,Token*,Token*,Token*,int,int);
+void sqlite3AddColumn(Parse*,Token*);
+void sqlite3AddNotNull(Parse*, int);
+void sqlite3AddPrimaryKey(Parse*, ExprList*, int);
+void sqlite3AddColumnType(Parse*,Token*,Token*);
+void sqlite3AddDefaultValue(Parse*,Token*,int);
+void sqlite3AddCollateType(Parse*, const char*, int);
+void sqlite3EndTable(Parse*,Token*,Select*);
+void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int);
+int sqlite3ViewGetColumnNames(Parse*,Table*);
+void sqlite3DropTable(Parse*, SrcList*, int);
+void sqlite3DeleteTable(sqlite3*, Table*);
+void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
+IdList *sqlite3IdListAppend(IdList*, Token*);
+int sqlite3IdListIndex(IdList*,const char*);
+SrcList *sqlite3SrcListAppend(SrcList*, Token*, Token*);
+void sqlite3SrcListAddAlias(SrcList*, Token*);
+void sqlite3SrcListAssignCursors(Parse*, SrcList*);
+void sqlite3IdListDelete(IdList*);
+void sqlite3SrcListDelete(SrcList*);
+void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
+ Token*);
+void sqlite3DropIndex(Parse*, SrcList*);
+void sqlite3AddKeyType(Vdbe*, ExprList*);
+void sqlite3AddIdxKeyType(Vdbe*, Index*);
+int sqlite3Select(Parse*, Select*, int, int, Select*, int, int*, char *aff);
+Select *sqlite3SelectNew(ExprList*,SrcList*,Expr*,ExprList*,Expr*,ExprList*,
+ int,int,int);
+void sqlite3SelectDelete(Select*);
+void sqlite3SelectUnbind(Select*);
+Table *sqlite3SrcListLookup(Parse*, SrcList*);
+int sqlite3IsReadOnly(Parse*, Table*, int);
+void sqlite3OpenTableForReading(Vdbe*, int iCur, Table*);
+void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
+void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
+WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, int, ExprList**);
+void sqlite3WhereEnd(WhereInfo*);
+void sqlite3ExprCode(Parse*, Expr*);
+int sqlite3ExprCodeExprList(Parse*, ExprList*);
+void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
+void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
+Table *sqlite3FindTable(sqlite3*,const char*, const char*);
+Table *sqlite3LocateTable(Parse*,const char*, const char*);
+Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
+void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
+void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
+void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
+void sqlite3Vacuum(Parse*, Token*);
+int sqlite3RunVacuum(char**, sqlite3*);
+char *sqlite3NameFromToken(Token*);
+int sqlite3ExprCheck(Parse*, Expr*, int, int*);
+int sqlite3ExprCompare(Expr*, Expr*);
+int sqliteFuncId(Token*);
+int sqlite3ExprResolveIds(Parse*, SrcList*, ExprList*, Expr*);
+int sqlite3ExprResolveAndCheck(Parse*,SrcList*,ExprList*,Expr*,int,int*);
+int sqlite3ExprAnalyzeAggregates(Parse*, Expr*);
+Vdbe *sqlite3GetVdbe(Parse*);
+void sqlite3Randomness(int, void*);
+void sqlite3RollbackAll(sqlite3*);
+void sqlite3CodeVerifySchema(Parse*, int);
+void sqlite3BeginTransaction(Parse*, int);
+void sqlite3CommitTransaction(Parse*);
+void sqlite3RollbackTransaction(Parse*);
+int sqlite3ExprIsConstant(Expr*);
+int sqlite3ExprIsInteger(Expr*, int*);
+int sqlite3IsRowid(const char*);
+void sqlite3GenerateRowDelete(sqlite3*, Vdbe*, Table*, int, int);
+void sqlite3GenerateRowIndexDelete(sqlite3*, Vdbe*, Table*, int, char*);
+void sqlite3GenerateIndexKey(Vdbe*, Index*, int);
+void sqlite3GenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
+void sqlite3CompleteInsertion(Parse*, Table*, int, char*, int, int, int);
+void sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
+void sqlite3BeginWriteOperation(Parse*, int, int);
+Expr *sqlite3ExprDup(Expr*);
+void sqlite3TokenCopy(Token*, Token*);
+ExprList *sqlite3ExprListDup(ExprList*);
+SrcList *sqlite3SrcListDup(SrcList*);
+IdList *sqlite3IdListDup(IdList*);
+Select *sqlite3SelectDup(Select*);
+FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
+void sqlite3RegisterBuiltinFunctions(sqlite3*);
+void sqlite3RegisterDateTimeFunctions(sqlite3*);
+int sqlite3SafetyOn(sqlite3*);
+int sqlite3SafetyOff(sqlite3*);
+int sqlite3SafetyCheck(sqlite3*);
+void sqlite3ChangeCookie(sqlite3*, Vdbe*, int);
+void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*,
+ int,Expr*,int);
+void sqlite3FinishTrigger(Parse*, TriggerStep*, Token*);
+void sqlite3DropTrigger(Parse*, SrcList*);
+void sqlite3DropTriggerPtr(Parse*, Trigger*, int);
+int sqlite3TriggersExist(Parse* , Trigger* , int , int , int, ExprList*);
+int sqlite3CodeRowTrigger(Parse*, int, ExprList*, int, Table *, int, int,
+ int, int);
+void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*);
+void sqlite3DeleteTriggerStep(TriggerStep*);
+TriggerStep *sqlite3TriggerSelectStep(Select*);
+TriggerStep *sqlite3TriggerInsertStep(Token*, IdList*, ExprList*, Select*, int);
+TriggerStep *sqlite3TriggerUpdateStep(Token*, ExprList*, Expr*, int);
+TriggerStep *sqlite3TriggerDeleteStep(Token*, Expr*);
+void sqlite3DeleteTrigger(Trigger*);
+int sqlite3JoinType(Parse*, Token*, Token*, Token*);
+void sqlite3CreateForeignKey(Parse*, ExprList*, Token*, ExprList*, int);
+void sqlite3DeferForeignKey(Parse*, int);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ void sqlite3AuthRead(Parse*,Expr*,SrcList*);
+ int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*);
+ void sqlite3AuthContextPush(Parse*, AuthContext*, const char*);
+ void sqlite3AuthContextPop(AuthContext*);
+#else
+# define sqlite3AuthRead(a,b,c)
+# define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK
+# define sqlite3AuthContextPush(a,b,c)
+# define sqlite3AuthContextPop(a) ((void)(a))
+#endif
+void sqlite3Attach(Parse*, Token*, Token*, int, Token*);
+void sqlite3Detach(Parse*, Token*);
+int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename,
+ int omitJournal, int nCache, Btree **ppBtree);
+int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
+int sqlite3FixSrcList(DbFixer*, SrcList*);
+int sqlite3FixSelect(DbFixer*, Select*);
+int sqlite3FixExpr(DbFixer*, Expr*);
+int sqlite3FixExprList(DbFixer*, ExprList*);
+int sqlite3FixTriggerStep(DbFixer*, TriggerStep*);
+double sqlite3AtoF(const char *z, const char **);
+char *sqlite3_snprintf(int,char*,const char*,...);
+int sqlite3GetInt32(const char *, int*);
+int sqlite3FitsIn64Bits(const char *);
+int sqlite3utf16ByteLen(const void *pData, int nChar);
+int sqlite3utf8CharLen(const char *pData, int nByte);
+int sqlite3ReadUtf8(const unsigned char *);
+int sqlite3PutVarint(unsigned char *, u64);
+int sqlite3GetVarint(const unsigned char *, u64 *);
+int sqlite3GetVarint32(const unsigned char *, u32 *);
+int sqlite3VarintLen(u64 v);
+char sqlite3AffinityType(const char *, int);
+void sqlite3IndexAffinityStr(Vdbe *, Index *);
+void sqlite3TableAffinityStr(Vdbe *, Table *);
+char sqlite3CompareAffinity(Expr *pExpr, char aff2);
+int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
+char sqlite3ExprAffinity(Expr *pExpr);
+int sqlite3atoi64(const char*, i64*);
+void sqlite3Error(sqlite3*, int, const char*,...);
+void *sqlite3HexToBlob(const char *z);
+int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
+const char *sqlite3ErrStr(int);
+int sqlite3ReadUniChar(const char *zStr, int *pOffset, u8 *pEnc, int fold);
+int sqlite3ReadSchema(Parse *pParse);
+CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char *,int,int);
+CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName, int nName);
+CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
+int sqlite3CheckCollSeq(Parse *, CollSeq *);
+int sqlite3CheckIndexCollSeq(Parse *, Index *);
+int sqlite3CheckObjectName(Parse *, const char *);
+void sqlite3VdbeSetChanges(sqlite3 *, int);
+void sqlite3utf16Substr(sqlite3_context *,int,sqlite3_value **);
+
+const void *sqlite3ValueText(sqlite3_value*, u8);
+int sqlite3ValueBytes(sqlite3_value*, u8);
+void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*));
+void sqlite3ValueFree(sqlite3_value*);
+sqlite3_value *sqlite3ValueNew();
+sqlite3_value *sqlite3GetTransientValue(sqlite3*db);
+extern const unsigned char sqlite3UpperToLower[];
+
+#endif
diff --git a/kopete/plugins/statistics/sqlite/table.c b/kopete/plugins/statistics/sqlite/table.c
new file mode 100644
index 00000000..d4ef2c8a
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/table.c
@@ -0,0 +1,195 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains the sqlite3_get_table() and sqlite3_free_table()
+** interface routines. These are just wrappers around the main
+** interface routine of sqlite3_exec().
+**
+** These routines are in a separate files so that they will not be linked
+** if they are not used.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "sqliteInt.h"
+
+/*
+** This structure is used to pass data from sqlite3_get_table() through
+** to the callback function is uses to build the result.
+*/
+typedef struct TabResult {
+ char **azResult;
+ char *zErrMsg;
+ int nResult;
+ int nAlloc;
+ int nRow;
+ int nColumn;
+ int nData;
+ int rc;
+} TabResult;
+
+/*
+** This routine is called once for each row in the result table. Its job
+** is to fill in the TabResult structure appropriately, allocating new
+** memory as necessary.
+*/
+static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
+ TabResult *p = (TabResult*)pArg;
+ int need;
+ int i;
+ char *z;
+
+ /* Make sure there is enough space in p->azResult to hold everything
+ ** we need to remember from this invocation of the callback.
+ */
+ if( p->nRow==0 && argv!=0 ){
+ need = nCol*2;
+ }else{
+ need = nCol;
+ }
+ if( p->nData + need >= p->nAlloc ){
+ char **azNew;
+ p->nAlloc = p->nAlloc*2 + need + 1;
+ azNew = realloc( p->azResult, sizeof(char*)*p->nAlloc );
+ if( azNew==0 ) goto malloc_failed;
+ p->azResult = azNew;
+ }
+
+ /* If this is the first row, then generate an extra row containing
+ ** the names of all columns.
+ */
+ if( p->nRow==0 ){
+ p->nColumn = nCol;
+ for(i=0; i<nCol; i++){
+ if( colv[i]==0 ){
+ z = 0;
+ }else{
+ z = malloc( strlen(colv[i])+1 );
+ if( z==0 ) goto malloc_failed;
+ strcpy(z, colv[i]);
+ }
+ p->azResult[p->nData++] = z;
+ }
+ }else if( p->nColumn!=nCol ){
+ sqlite3SetString(&p->zErrMsg,
+ "sqlite3_get_table() called with two or more incompatible queries",
+ (char*)0);
+ p->rc = SQLITE_ERROR;
+ return 1;
+ }
+
+ /* Copy over the row data
+ */
+ if( argv!=0 ){
+ for(i=0; i<nCol; i++){
+ if( argv[i]==0 ){
+ z = 0;
+ }else{
+ z = malloc( strlen(argv[i])+1 );
+ if( z==0 ) goto malloc_failed;
+ strcpy(z, argv[i]);
+ }
+ p->azResult[p->nData++] = z;
+ }
+ p->nRow++;
+ }
+ return 0;
+
+malloc_failed:
+ p->rc = SQLITE_NOMEM;
+ return 1;
+}
+
+/*
+** Query the database. But instead of invoking a callback for each row,
+** malloc() for space to hold the result and return the entire results
+** at the conclusion of the call.
+**
+** The result that is written to ***pazResult is held in memory obtained
+** from malloc(). But the caller cannot free this memory directly.
+** Instead, the entire table should be passed to sqlite3_free_table() when
+** the calling procedure is finished using it.
+*/
+int sqlite3_get_table(
+ sqlite3 *db, /* The database on which the SQL executes */
+ const char *zSql, /* The SQL to be executed */
+ char ***pazResult, /* Write the result table here */
+ int *pnRow, /* Write the number of rows in the result here */
+ int *pnColumn, /* Write the number of columns of result here */
+ char **pzErrMsg /* Write error messages here */
+){
+ int rc;
+ TabResult res;
+ if( pazResult==0 ){ return SQLITE_ERROR; }
+ *pazResult = 0;
+ if( pnColumn ) *pnColumn = 0;
+ if( pnRow ) *pnRow = 0;
+ res.zErrMsg = 0;
+ res.nResult = 0;
+ res.nRow = 0;
+ res.nColumn = 0;
+ res.nData = 1;
+ res.nAlloc = 20;
+ res.rc = SQLITE_OK;
+ res.azResult = malloc( sizeof(char*)*res.nAlloc );
+ if( res.azResult==0 ) return SQLITE_NOMEM;
+ res.azResult[0] = 0;
+ rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
+ if( res.azResult ){
+ res.azResult[0] = (char*)res.nData;
+ }
+ if( rc==SQLITE_ABORT ){
+ sqlite3_free_table(&res.azResult[1]);
+ if( res.zErrMsg ){
+ if( pzErrMsg ){
+ free(*pzErrMsg);
+ *pzErrMsg = sqlite3_mprintf("%s",res.zErrMsg);
+ }
+ sqliteFree(res.zErrMsg);
+ }
+ db->errCode = res.rc;
+ return res.rc;
+ }
+ sqliteFree(res.zErrMsg);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free_table(&res.azResult[1]);
+ return rc;
+ }
+ if( res.nAlloc>res.nData ){
+ char **azNew;
+ azNew = realloc( res.azResult, sizeof(char*)*(res.nData+1) );
+ if( azNew==0 ){
+ sqlite3_free_table(&res.azResult[1]);
+ return SQLITE_NOMEM;
+ }
+ res.nAlloc = res.nData+1;
+ res.azResult = azNew;
+ }
+ *pazResult = &res.azResult[1];
+ if( pnColumn ) *pnColumn = res.nColumn;
+ if( pnRow ) *pnRow = res.nRow;
+ return rc;
+}
+
+/*
+** This routine frees the space the sqlite3_get_table() malloced.
+*/
+void sqlite3_free_table(
+ char **azResult /* Result returned from from sqlite3_get_table() */
+){
+ if( azResult ){
+ int i, n;
+ azResult--;
+ if( azResult==0 ) return;
+ n = (int)azResult[0];
+ for(i=1; i<n; i++){ if( azResult[i] ) free(azResult[i]); }
+ free(azResult);
+ }
+}
diff --git a/kopete/plugins/statistics/sqlite/tokenize.c b/kopete/plugins/statistics/sqlite/tokenize.c
new file mode 100644
index 00000000..061e5b9a
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/tokenize.c
@@ -0,0 +1,707 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** An tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens and sends those tokens one-by-one over to the
+** parser for analysis.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+#include <stdlib.h>
+
+/*
+** This function looks up an identifier to determine if it is a
+** keyword. If it is a keyword, the token code of that keyword is
+** returned. If the input is not a keyword, TK_ID is returned.
+**
+** The implementation of this routine was generated by a program,
+** mkkeywordhash.c, located in the tool subdirectory of the distribution.
+** The output of the mkkeywordhash.c program was manually cut and pasted
+** into this file. When the set of keywords for SQLite changes, you
+** must modify the mkkeywordhash.c program (to add or remove keywords from
+** the data tables) then rerun that program to regenerate this function.
+*/
+int sqlite3KeywordCode(const char *z, int n){
+ static const char zText[519] =
+ "ABORTAFTERALLANDASCATTACHBEFOREBEGINBETWEENBYCASCADECASECHECK"
+ "COLLATECOMMITCONFLICTCONSTRAINTCREATECROSSDATABASEDEFAULTDEFERRABLE"
+ "DEFERREDDELETEDESCDETACHDISTINCTDROPEACHELSEENDEXCEPTEXCLUSIVE"
+ "EXPLAINFAILFOREIGNFROMFULLGLOBGROUPHAVINGIGNOREIMMEDIATEINDEX"
+ "INITIALLYINNERINSERTINSTEADINTERSECTINTOISNULLJOINKEYLEFTLIKE"
+ "LIMITMATCHNATURALNOTNULLNULLOFFSETONORDEROUTERPRAGMAPRIMARYRAISE"
+ "REFERENCESREPLACERESTRICTRIGHTROLLBACKROWSELECTSETSTATEMENTTABLE"
+ "TEMPORARYTHENTRANSACTIONTRIGGERUNIONUNIQUEUPDATEUSINGVACUUMVALUES"
+ "VIEWWHENWHERE";
+ static const unsigned char aHash[154] = {
+ 0, 75, 82, 0, 0, 97, 80, 0, 83, 0, 0, 0, 0,
+ 0, 0, 6, 0, 95, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 96, 86, 8, 0, 26, 13, 7, 19, 15, 0, 0, 32,
+ 25, 0, 21, 31, 41, 0, 0, 0, 34, 27, 0, 0, 30,
+ 0, 0, 0, 9, 0, 10, 0, 0, 0, 0, 51, 0, 44,
+ 43, 0, 45, 40, 0, 29, 39, 35, 0, 0, 20, 0, 59,
+ 0, 16, 0, 17, 0, 18, 0, 55, 42, 72, 0, 33, 0,
+ 0, 61, 66, 56, 0, 0, 0, 0, 0, 0, 0, 54, 0,
+ 0, 0, 0, 0, 74, 50, 76, 64, 52, 0, 0, 0, 0,
+ 68, 84, 0, 47, 0, 58, 60, 92, 0, 0, 48, 0, 93,
+ 0, 63, 71, 98, 0, 0, 0, 0, 0, 67, 0, 0, 0,
+ 0, 87, 0, 0, 0, 0, 0, 90, 88, 0, 94,
+ };
+ static const unsigned char aNext[98] = {
+ 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 12, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0,
+ 0, 0, 0, 14, 3, 24, 0, 0, 0, 1, 22, 0, 0,
+ 36, 23, 28, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0,
+ 0, 49, 37, 0, 0, 0, 38, 0, 53, 0, 57, 62, 0,
+ 0, 0, 0, 0, 0, 70, 46, 0, 65, 0, 0, 0, 0,
+ 69, 73, 0, 77, 0, 0, 0, 0, 0, 0, 81, 85, 0,
+ 91, 79, 78, 0, 0, 89, 0,
+ };
+ static const unsigned char aLen[98] = {
+ 5, 5, 3, 3, 2, 3, 6, 6, 5, 7, 2, 7, 4,
+ 5, 7, 6, 8, 10, 6, 5, 8, 7, 10, 8, 6, 4,
+ 6, 8, 4, 4, 4, 3, 6, 9, 7, 4, 3, 7, 4,
+ 4, 4, 5, 6, 6, 9, 2, 5, 9, 5, 6, 7, 9,
+ 4, 2, 6, 4, 3, 4, 4, 5, 5, 7, 3, 7, 4,
+ 2, 6, 2, 2, 5, 5, 6, 7, 5, 10, 7, 8, 5,
+ 8, 3, 6, 3, 9, 5, 4, 9, 4, 11, 7, 5, 6,
+ 6, 5, 6, 6, 4, 4, 5,
+ };
+ static const unsigned short int aOffset[98] = {
+ 0, 5, 10, 13, 16, 16, 19, 25, 31, 36, 43, 45, 52,
+ 56, 61, 68, 74, 82, 92, 98, 103, 111, 118, 128, 136, 142,
+ 146, 152, 160, 164, 168, 172, 175, 181, 190, 197, 201, 201, 208,
+ 212, 216, 220, 225, 231, 237, 246, 246, 251, 260, 265, 271, 278,
+ 287, 291, 291, 297, 301, 304, 308, 312, 317, 322, 329, 329, 336,
+ 340, 340, 346, 348, 348, 353, 358, 364, 371, 376, 386, 393, 401,
+ 406, 414, 417, 423, 426, 435, 440, 440, 449, 453, 464, 471, 476,
+ 482, 488, 493, 499, 505, 509, 513,
+ };
+ static const unsigned char aCode[98] = {
+ TK_ABORT, TK_AFTER, TK_ALL, TK_AND, TK_AS,
+ TK_ASC, TK_ATTACH, TK_BEFORE, TK_BEGIN, TK_BETWEEN,
+ TK_BY, TK_CASCADE, TK_CASE, TK_CHECK, TK_COLLATE,
+ TK_COMMIT, TK_CONFLICT, TK_CONSTRAINT, TK_CREATE, TK_JOIN_KW,
+ TK_DATABASE, TK_DEFAULT, TK_DEFERRABLE, TK_DEFERRED, TK_DELETE,
+ TK_DESC, TK_DETACH, TK_DISTINCT, TK_DROP, TK_EACH,
+ TK_ELSE, TK_END, TK_EXCEPT, TK_EXCLUSIVE, TK_EXPLAIN,
+ TK_FAIL, TK_FOR, TK_FOREIGN, TK_FROM, TK_JOIN_KW,
+ TK_GLOB, TK_GROUP, TK_HAVING, TK_IGNORE, TK_IMMEDIATE,
+ TK_IN, TK_INDEX, TK_INITIALLY, TK_JOIN_KW, TK_INSERT,
+ TK_INSTEAD, TK_INTERSECT, TK_INTO, TK_IS, TK_ISNULL,
+ TK_JOIN, TK_KEY, TK_JOIN_KW, TK_LIKE, TK_LIMIT,
+ TK_MATCH, TK_JOIN_KW, TK_NOT, TK_NOTNULL, TK_NULL,
+ TK_OF, TK_OFFSET, TK_ON, TK_OR, TK_ORDER,
+ TK_JOIN_KW, TK_PRAGMA, TK_PRIMARY, TK_RAISE, TK_REFERENCES,
+ TK_REPLACE, TK_RESTRICT, TK_JOIN_KW, TK_ROLLBACK, TK_ROW,
+ TK_SELECT, TK_SET, TK_STATEMENT, TK_TABLE, TK_TEMP,
+ TK_TEMP, TK_THEN, TK_TRANSACTION,TK_TRIGGER, TK_UNION,
+ TK_UNIQUE, TK_UPDATE, TK_USING, TK_VACUUM, TK_VALUES,
+ TK_VIEW, TK_WHEN, TK_WHERE,
+ };
+ int h, i;
+ if( n<2 ) return TK_ID;
+ h = (sqlite3UpperToLower[((unsigned char*)z)[0]]*5 +
+ sqlite3UpperToLower[((unsigned char*)z)[n-1]]*3 +
+ n) % 154;
+ for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
+ if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){
+ return aCode[i];
+ }
+ }
+ return TK_ID;
+}
+
+/*
+** If X is a character that can be used in an identifier and
+** X&0x80==0 then isIdChar[X] will be 1. If X&0x80==0x80 then
+** X is always an identifier character. (Hence all UTF-8
+** characters can be part of an identifier). isIdChar[X] will
+** be 0 for every character in the lower 128 ASCII characters
+** that cannot be used as part of an identifier.
+**
+** In this implementation, an identifier can be a string of
+** alphabetic characters, digits, and "_" plus any character
+** with the high-order bit set. The latter rule means that
+** any sequence of UTF-8 characters or characters taken from
+** an extended ISO8859 character set can form an identifier.
+*/
+static const char isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
+};
+
+#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x2f && isIdChar[c-0x30]))
+
+/*
+** Return the length of the token that begins at z[0].
+** Store the token type in *tokenType before returning.
+*/
+static int sqliteGetToken(const unsigned char *z, int *tokenType){
+ int i, c;
+ switch( *z ){
+ case ' ': case '\t': case '\n': case '\f': case '\r': {
+ for(i=1; isspace(z[i]); i++){}
+ *tokenType = TK_SPACE;
+ return i;
+ }
+ case '-': {
+ if( z[1]=='-' ){
+ for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
+ *tokenType = TK_COMMENT;
+ return i;
+ }
+ *tokenType = TK_MINUS;
+ return 1;
+ }
+ case '(': {
+ *tokenType = TK_LP;
+ return 1;
+ }
+ case ')': {
+ *tokenType = TK_RP;
+ return 1;
+ }
+ case ';': {
+ *tokenType = TK_SEMI;
+ return 1;
+ }
+ case '+': {
+ *tokenType = TK_PLUS;
+ return 1;
+ }
+ case '*': {
+ *tokenType = TK_STAR;
+ return 1;
+ }
+ case '/': {
+ if( z[1]!='*' || z[2]==0 ){
+ *tokenType = TK_SLASH;
+ return 1;
+ }
+ for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
+ if( c ) i++;
+ *tokenType = TK_COMMENT;
+ return i;
+ }
+ case '%': {
+ *tokenType = TK_REM;
+ return 1;
+ }
+ case '=': {
+ *tokenType = TK_EQ;
+ return 1 + (z[1]=='=');
+ }
+ case '<': {
+ if( (c=z[1])=='=' ){
+ *tokenType = TK_LE;
+ return 2;
+ }else if( c=='>' ){
+ *tokenType = TK_NE;
+ return 2;
+ }else if( c=='<' ){
+ *tokenType = TK_LSHIFT;
+ return 2;
+ }else{
+ *tokenType = TK_LT;
+ return 1;
+ }
+ }
+ case '>': {
+ if( (c=z[1])=='=' ){
+ *tokenType = TK_GE;
+ return 2;
+ }else if( c=='>' ){
+ *tokenType = TK_RSHIFT;
+ return 2;
+ }else{
+ *tokenType = TK_GT;
+ return 1;
+ }
+ }
+ case '!': {
+ if( z[1]!='=' ){
+ *tokenType = TK_ILLEGAL;
+ return 2;
+ }else{
+ *tokenType = TK_NE;
+ return 2;
+ }
+ }
+ case '|': {
+ if( z[1]!='|' ){
+ *tokenType = TK_BITOR;
+ return 1;
+ }else{
+ *tokenType = TK_CONCAT;
+ return 2;
+ }
+ }
+ case ',': {
+ *tokenType = TK_COMMA;
+ return 1;
+ }
+ case '&': {
+ *tokenType = TK_BITAND;
+ return 1;
+ }
+ case '~': {
+ *tokenType = TK_BITNOT;
+ return 1;
+ }
+ case '\'': case '"': {
+ int delim = z[0];
+ for(i=1; (c=z[i])!=0; i++){
+ if( c==delim ){
+ if( z[i+1]==delim ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ }
+ if( c ) i++;
+ *tokenType = TK_STRING;
+ return i;
+ }
+ case '.': {
+ *tokenType = TK_DOT;
+ return 1;
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ *tokenType = TK_INTEGER;
+ for(i=1; isdigit(z[i]); i++){}
+ if( z[i]=='.' && isdigit(z[i+1]) ){
+ i += 2;
+ while( isdigit(z[i]) ){ i++; }
+ *tokenType = TK_FLOAT;
+ }
+ if( (z[i]=='e' || z[i]=='E') &&
+ ( isdigit(z[i+1])
+ || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+ )
+ ){
+ i += 2;
+ while( isdigit(z[i]) ){ i++; }
+ *tokenType = TK_FLOAT;
+ }
+ return i;
+ }
+ case '[': {
+ for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
+ *tokenType = TK_ID;
+ return i;
+ }
+ case '?': {
+ *tokenType = TK_VARIABLE;
+ for(i=1; isdigit(z[i]); i++){}
+ return i;
+ }
+ case ':': {
+ for(i=1; IdChar(z[i]); i++){}
+ *tokenType = i>1 ? TK_VARIABLE : TK_ILLEGAL;
+ return i;
+ }
+ case '$': {
+ *tokenType = TK_VARIABLE;
+ if( z[1]=='{' ){
+ int nBrace = 1;
+ for(i=2; (c=z[i])!=0 && nBrace; i++){
+ if( c=='{' ){
+ nBrace++;
+ }else if( c=='}' ){
+ nBrace--;
+ }
+ }
+ if( c==0 ) *tokenType = TK_ILLEGAL;
+ }else{
+ int n = 0;
+ for(i=1; (c=z[i])!=0; i++){
+ if( isalnum(c) || c=='_' ){
+ n++;
+ }else if( c=='(' && n>0 ){
+ do{
+ i++;
+ }while( (c=z[i])!=0 && !isspace(c) && c!=')' );
+ if( c==')' ){
+ i++;
+ }else{
+ *tokenType = TK_ILLEGAL;
+ }
+ break;
+ }else if( c==':' && z[i+1]==':' ){
+ i++;
+ }else{
+ break;
+ }
+ }
+ if( n==0 ) *tokenType = TK_ILLEGAL;
+ }
+ return i;
+ }
+ case 'x': case 'X': {
+ if( (c=z[1])=='\'' || c=='"' ){
+ int delim = c;
+ *tokenType = TK_BLOB;
+ for(i=2; (c=z[i])!=0; i++){
+ if( c==delim ){
+ if( i%2 ) *tokenType = TK_ILLEGAL;
+ break;
+ }
+ if( !isxdigit(c) ){
+ *tokenType = TK_ILLEGAL;
+ return i;
+ }
+ }
+ if( c ) i++;
+ return i;
+ }
+ /* Otherwise fall through to the next case */
+ }
+ default: {
+ if( !IdChar(*z) ){
+ break;
+ }
+ for(i=1; IdChar(z[i]); i++){}
+ *tokenType = sqlite3KeywordCode((char*)z, i);
+ return i;
+ }
+ }
+ *tokenType = TK_ILLEGAL;
+ return 1;
+}
+
+/*
+** Run the parser on the given SQL string. The parser structure is
+** passed in. An SQLITE_ status code is returned. If an error occurs
+** and pzErrMsg!=NULL then an error message might be written into
+** memory obtained from malloc() and *pzErrMsg made to point to that
+** error message. Or maybe not.
+*/
+int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzErrMsg){
+ int nErr = 0;
+ int i;
+ void *pEngine;
+ int tokenType;
+ int lastTokenParsed = -1;
+ sqlite3 *db = pParse->db;
+ extern void *sqlite3ParserAlloc(void*(*)(int));
+ extern void sqlite3ParserFree(void*, void(*)(void*));
+ extern int sqlite3Parser(void*, int, Token, Parse*);
+
+ db->flags &= ~SQLITE_Interrupt;
+ pParse->rc = SQLITE_OK;
+ i = 0;
+ pEngine = sqlite3ParserAlloc((void*(*)(int))malloc);
+ if( pEngine==0 ){
+ sqlite3SetString(pzErrMsg, "out of memory", (char*)0);
+ return 1;
+ }
+ assert( pParse->sLastToken.dyn==0 );
+ assert( pParse->pNewTable==0 );
+ assert( pParse->pNewTrigger==0 );
+ assert( pParse->nVar==0 );
+ assert( pParse->nVarExpr==0 );
+ assert( pParse->nVarExprAlloc==0 );
+ assert( pParse->apVarExpr==0 );
+ pParse->zTail = pParse->zSql = zSql;
+ while( sqlite3_malloc_failed==0 && zSql[i]!=0 ){
+ assert( i>=0 );
+ pParse->sLastToken.z = &zSql[i];
+ assert( pParse->sLastToken.dyn==0 );
+ pParse->sLastToken.n = sqliteGetToken((unsigned char*)&zSql[i], &tokenType);
+ i += pParse->sLastToken.n;
+ switch( tokenType ){
+ case TK_SPACE:
+ case TK_COMMENT: {
+ if( (db->flags & SQLITE_Interrupt)!=0 ){
+ pParse->rc = SQLITE_INTERRUPT;
+ sqlite3SetString(pzErrMsg, "interrupt", (char*)0);
+ goto abort_parse;
+ }
+ break;
+ }
+ case TK_ILLEGAL: {
+ if( pzErrMsg ){
+ sqliteFree(*pzErrMsg);
+ *pzErrMsg = sqlite3MPrintf("unrecognized token: \"%T\"",
+ &pParse->sLastToken);
+ }
+ nErr++;
+ goto abort_parse;
+ }
+ case TK_SEMI: {
+ pParse->zTail = &zSql[i];
+ /* Fall thru into the default case */
+ }
+ default: {
+ sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
+ lastTokenParsed = tokenType;
+ if( pParse->rc!=SQLITE_OK ){
+ goto abort_parse;
+ }
+ break;
+ }
+ }
+ }
+abort_parse:
+ if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
+ if( lastTokenParsed!=TK_SEMI ){
+ sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
+ pParse->zTail = &zSql[i];
+ }
+ sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
+ }
+ sqlite3ParserFree(pEngine, free);
+ if( sqlite3_malloc_failed ){
+ pParse->rc = SQLITE_NOMEM;
+ }
+ if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
+ sqlite3SetString(&pParse->zErrMsg, sqlite3ErrStr(pParse->rc),
+ (char*)0);
+ }
+ if( pParse->zErrMsg ){
+ if( pzErrMsg && *pzErrMsg==0 ){
+ *pzErrMsg = pParse->zErrMsg;
+ }else{
+ sqliteFree(pParse->zErrMsg);
+ }
+ pParse->zErrMsg = 0;
+ if( !nErr ) nErr++;
+ }
+ if( pParse->pVdbe && pParse->nErr>0 ){
+ sqlite3VdbeDelete(pParse->pVdbe);
+ pParse->pVdbe = 0;
+ }
+ sqlite3DeleteTable(pParse->db, pParse->pNewTable);
+ sqlite3DeleteTrigger(pParse->pNewTrigger);
+ sqliteFree(pParse->apVarExpr);
+ if( nErr>0 && (pParse->rc==SQLITE_OK || pParse->rc==SQLITE_DONE) ){
+ pParse->rc = SQLITE_ERROR;
+ }
+ return nErr;
+}
+
+/*
+** Token types used by the sqlite3_complete() routine. See the header
+** comments on that procedure for additional information.
+*/
+#define tkEXPLAIN 0
+#define tkCREATE 1
+#define tkTEMP 2
+#define tkTRIGGER 3
+#define tkEND 4
+#define tkSEMI 5
+#define tkWS 6
+#define tkOTHER 7
+
+/*
+** Return TRUE if the given SQL string ends in a semicolon.
+**
+** Special handling is require for CREATE TRIGGER statements.
+** Whenever the CREATE TRIGGER keywords are seen, the statement
+** must end with ";END;".
+**
+** This implementation uses a state machine with 7 states:
+**
+** (0) START At the beginning or end of an SQL statement. This routine
+** returns 1 if it ends in the START state and 0 if it ends
+** in any other state.
+**
+** (1) EXPLAIN The keyword EXPLAIN has been seen at the beginning of
+** a statement.
+**
+** (2) CREATE The keyword CREATE has been seen at the beginning of a
+** statement, possibly preceeded by EXPLAIN and/or followed by
+** TEMP or TEMPORARY
+**
+** (3) NORMAL We are in the middle of statement which ends with a single
+** semicolon.
+**
+** (4) TRIGGER We are in the middle of a trigger definition that must be
+** ended by a semicolon, the keyword END, and another semicolon.
+**
+** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at
+** the end of a trigger definition.
+**
+** (6) END We've seen the ";END" of the ";END;" that occurs at the end
+** of a trigger difinition.
+**
+** Transitions between states above are determined by tokens extracted
+** from the input. The following tokens are significant:
+**
+** (0) tkEXPLAIN The "explain" keyword.
+** (1) tkCREATE The "create" keyword.
+** (2) tkTEMP The "temp" or "temporary" keyword.
+** (3) tkTRIGGER The "trigger" keyword.
+** (4) tkEND The "end" keyword.
+** (5) tkSEMI A semicolon.
+** (6) tkWS Whitespace
+** (7) tkOTHER Any other SQL token.
+**
+** Whitespace never causes a state transition and is always ignored.
+*/
+int sqlite3_complete(const char *zSql){
+ u8 state = 0; /* Current state, using numbers defined in header comment */
+ u8 token; /* Value of the next token */
+
+ /* The following matrix defines the transition from one state to another
+ ** according to what token is seen. trans[state][token] returns the
+ ** next state.
+ */
+ static const u8 trans[7][8] = {
+ /* Token: */
+ /* State: ** EXPLAIN CREATE TEMP TRIGGER END SEMI WS OTHER */
+ /* 0 START: */ { 1, 2, 3, 3, 3, 0, 0, 3, },
+ /* 1 EXPLAIN: */ { 3, 2, 3, 3, 3, 0, 1, 3, },
+ /* 2 CREATE: */ { 3, 3, 2, 4, 3, 0, 2, 3, },
+ /* 3 NORMAL: */ { 3, 3, 3, 3, 3, 0, 3, 3, },
+ /* 4 TRIGGER: */ { 4, 4, 4, 4, 4, 5, 4, 4, },
+ /* 5 SEMI: */ { 4, 4, 4, 4, 6, 5, 5, 4, },
+ /* 6 END: */ { 4, 4, 4, 4, 4, 0, 6, 4, },
+ };
+
+ while( *zSql ){
+ switch( *zSql ){
+ case ';': { /* A semicolon */
+ token = tkSEMI;
+ break;
+ }
+ case ' ':
+ case '\r':
+ case '\t':
+ case '\n':
+ case '\f': { /* White space is ignored */
+ token = tkWS;
+ break;
+ }
+ case '/': { /* C-style comments */
+ if( zSql[1]!='*' ){
+ token = tkOTHER;
+ break;
+ }
+ zSql += 2;
+ while( zSql[0] && (zSql[0]!='*' || zSql[1]!='/') ){ zSql++; }
+ if( zSql[0]==0 ) return 0;
+ zSql++;
+ token = tkWS;
+ break;
+ }
+ case '-': { /* SQL-style comments from "--" to end of line */
+ if( zSql[1]!='-' ){
+ token = tkOTHER;
+ break;
+ }
+ while( *zSql && *zSql!='\n' ){ zSql++; }
+ if( *zSql==0 ) return state==0;
+ token = tkWS;
+ break;
+ }
+ case '[': { /* Microsoft-style identifiers in [...] */
+ zSql++;
+ while( *zSql && *zSql!=']' ){ zSql++; }
+ if( *zSql==0 ) return 0;
+ token = tkOTHER;
+ break;
+ }
+ case '"': /* single- and double-quoted strings */
+ case '\'': {
+ int c = *zSql;
+ zSql++;
+ while( *zSql && *zSql!=c ){ zSql++; }
+ if( *zSql==0 ) return 0;
+ token = tkOTHER;
+ break;
+ }
+ default: {
+ int c;
+ if( IdChar((u8)*zSql) ){
+ /* Keywords and unquoted identifiers */
+ int nId;
+ for(nId=1; IdChar(zSql[nId]); nId++){}
+ switch( *zSql ){
+ case 'c': case 'C': {
+ if( nId==6 && sqlite3StrNICmp(zSql, "create", 6)==0 ){
+ token = tkCREATE;
+ }else{
+ token = tkOTHER;
+ }
+ break;
+ }
+ case 't': case 'T': {
+ if( nId==7 && sqlite3StrNICmp(zSql, "trigger", 7)==0 ){
+ token = tkTRIGGER;
+ }else if( nId==4 && sqlite3StrNICmp(zSql, "temp", 4)==0 ){
+ token = tkTEMP;
+ }else if( nId==9 && sqlite3StrNICmp(zSql, "temporary", 9)==0 ){
+ token = tkTEMP;
+ }else{
+ token = tkOTHER;
+ }
+ break;
+ }
+ case 'e': case 'E': {
+ if( nId==3 && sqlite3StrNICmp(zSql, "end", 3)==0 ){
+ token = tkEND;
+ }else if( nId==7 && sqlite3StrNICmp(zSql, "explain", 7)==0 ){
+ token = tkEXPLAIN;
+ }else{
+ token = tkOTHER;
+ }
+ break;
+ }
+ default: {
+ token = tkOTHER;
+ break;
+ }
+ }
+ zSql += nId-1;
+ }else{
+ /* Operators and special symbols */
+ token = tkOTHER;
+ }
+ break;
+ }
+ }
+ state = trans[state][token];
+ zSql++;
+ }
+ return state==0;
+}
+
+/*
+** This routine is the same as the sqlite3_complete() routine described
+** above, except that the parameter is required to be UTF-16 encoded, not
+** UTF-8.
+*/
+int sqlite3_complete16(const void *zSql){
+ sqlite3_value *pVal;
+ char const *zSql8;
+ int rc = 0;
+
+ pVal = sqlite3ValueNew();
+ sqlite3ValueSetStr(pVal, -1, zSql, SQLITE_UTF16NATIVE, SQLITE_STATIC);
+ zSql8 = sqlite3ValueText(pVal, SQLITE_UTF8);
+ if( zSql8 ){
+ rc = sqlite3_complete(zSql8);
+ }
+ sqlite3ValueFree(pVal);
+ return rc;
+}
diff --git a/kopete/plugins/statistics/sqlite/trigger.c b/kopete/plugins/statistics/sqlite/trigger.c
new file mode 100644
index 00000000..bbb526f8
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/trigger.c
@@ -0,0 +1,804 @@
+/*
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+*
+*/
+#include "sqliteInt.h"
+
+/*
+** Delete a linked list of TriggerStep structures.
+*/
+void sqlite3DeleteTriggerStep(TriggerStep *pTriggerStep){
+ while( pTriggerStep ){
+ TriggerStep * pTmp = pTriggerStep;
+ pTriggerStep = pTriggerStep->pNext;
+
+ if( pTmp->target.dyn ) sqliteFree((char*)pTmp->target.z);
+ sqlite3ExprDelete(pTmp->pWhere);
+ sqlite3ExprListDelete(pTmp->pExprList);
+ sqlite3SelectDelete(pTmp->pSelect);
+ sqlite3IdListDelete(pTmp->pIdList);
+
+ sqliteFree(pTmp);
+ }
+}
+
+/*
+** This is called by the parser when it sees a CREATE TRIGGER statement
+** up to the point of the BEGIN before the trigger actions. A Trigger
+** structure is generated based on the information available and stored
+** in pParse->pNewTrigger. After the trigger actions have been parsed, the
+** sqlite3FinishTrigger() function is called to complete the trigger
+** construction process.
+*/
+void sqlite3BeginTrigger(
+ Parse *pParse, /* The parse context of the CREATE TRIGGER statement */
+ Token *pName1, /* The name of the trigger */
+ Token *pName2, /* The name of the trigger */
+ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
+ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */
+ IdList *pColumns, /* column list if this is an UPDATE OF trigger */
+ SrcList *pTableName,/* The name of the table/view the trigger applies to */
+ int foreach, /* One of TK_ROW or TK_STATEMENT */
+ Expr *pWhen, /* WHEN clause */
+ int isTemp /* True if the TEMPORARY keyword is present */
+){
+ Trigger *pTrigger;
+ Table *pTab;
+ char *zName = 0; /* Name of the trigger */
+ sqlite3 *db = pParse->db;
+ int iDb; /* The database to store the trigger in */
+ Token *pName; /* The unqualified db name */
+ DbFixer sFix;
+
+ if( isTemp ){
+ /* If TEMP was specified, then the trigger name may not be qualified. */
+ if( pName2 && pName2->n>0 ){
+ sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name");
+ goto trigger_cleanup;
+ }
+ iDb = 1;
+ pName = pName1;
+ }else{
+ /* Figure out the db that the the trigger will be created in */
+ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( iDb<0 ){
+ goto trigger_cleanup;
+ }
+ }
+
+ /* If the trigger name was unqualified, and the table is a temp table,
+ ** then set iDb to 1 to create the trigger in the temporary database.
+ ** If sqlite3SrcListLookup() returns 0, indicating the table does not
+ ** exist, the error is caught by the block below.
+ */
+ if( !pTableName || sqlite3_malloc_failed ) goto trigger_cleanup;
+ pTab = sqlite3SrcListLookup(pParse, pTableName);
+ if( pName2->n==0 && pTab && pTab->iDb==1 ){
+ iDb = 1;
+ }
+
+ /* Ensure the table name matches database name and that the table exists */
+ if( sqlite3_malloc_failed ) goto trigger_cleanup;
+ assert( pTableName->nSrc==1 );
+ if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
+ sqlite3FixSrcList(&sFix, pTableName) ){
+ goto trigger_cleanup;
+ }
+ pTab = sqlite3SrcListLookup(pParse, pTableName);
+ if( !pTab ){
+ /* The table does not exist. */
+ goto trigger_cleanup;
+ }
+
+ /* Check that the trigger name is not reserved and that no trigger of the
+ ** specified name exists */
+ zName = sqlite3NameFromToken(pName);
+ if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ goto trigger_cleanup;
+ }
+ if( sqlite3HashFind(&(db->aDb[iDb].trigHash), zName,pName->n+1) ){
+ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
+ goto trigger_cleanup;
+ }
+
+ /* Do not create a trigger on a system table */
+ if( (iDb!=1 && sqlite3StrICmp(pTab->zName, MASTER_NAME)==0) ||
+ (iDb==1 && sqlite3StrICmp(pTab->zName, TEMP_MASTER_NAME)==0)
+ ){
+ sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
+ pParse->nErr++;
+ goto trigger_cleanup;
+ }
+
+ /* INSTEAD of triggers are only for views and views only support INSTEAD
+ ** of triggers.
+ */
+ if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
+ sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
+ (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName, 0);
+ goto trigger_cleanup;
+ }
+ if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
+ sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
+ " trigger on table: %S", pTableName, 0);
+ goto trigger_cleanup;
+ }
+
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int code = SQLITE_CREATE_TRIGGER;
+ const char *zDb = db->aDb[pTab->iDb].zName;
+ const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
+ if( pTab->iDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
+ if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
+ goto trigger_cleanup;
+ }
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(pTab->iDb),0,zDb)){
+ goto trigger_cleanup;
+ }
+ }
+#endif
+
+ /* INSTEAD OF triggers can only appear on views and BEFORE triggers
+ ** cannot appear on views. So we might as well translate every
+ ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code
+ ** elsewhere.
+ */
+ if (tr_tm == TK_INSTEAD){
+ tr_tm = TK_BEFORE;
+ }
+
+ /* Build the Trigger object */
+ pTrigger = (Trigger*)sqliteMalloc(sizeof(Trigger));
+ if( pTrigger==0 ) goto trigger_cleanup;
+ pTrigger->name = zName;
+ zName = 0;
+ pTrigger->table = sqliteStrDup(pTableName->a[0].zName);
+ if( sqlite3_malloc_failed ) goto trigger_cleanup;
+ pTrigger->iDb = iDb;
+ pTrigger->iTabDb = pTab->iDb;
+ pTrigger->op = op;
+ pTrigger->tr_tm = tr_tm;
+ pTrigger->pWhen = sqlite3ExprDup(pWhen);
+ pTrigger->pColumns = sqlite3IdListDup(pColumns);
+ pTrigger->foreach = foreach;
+ sqlite3TokenCopy(&pTrigger->nameToken,pName);
+ assert( pParse->pNewTrigger==0 );
+ pParse->pNewTrigger = pTrigger;
+
+trigger_cleanup:
+ sqliteFree(zName);
+ sqlite3SrcListDelete(pTableName);
+ sqlite3IdListDelete(pColumns);
+ sqlite3ExprDelete(pWhen);
+}
+
+/*
+** This routine is called after all of the trigger actions have been parsed
+** in order to complete the process of building the trigger.
+*/
+void sqlite3FinishTrigger(
+ Parse *pParse, /* Parser context */
+ TriggerStep *pStepList, /* The triggered program */
+ Token *pAll /* Token that describes the complete CREATE TRIGGER */
+){
+ Trigger *nt = 0; /* The trigger whose construction is finishing up */
+ sqlite3 *db = pParse->db; /* The database */
+ DbFixer sFix;
+
+ if( pParse->nErr || pParse->pNewTrigger==0 ) goto triggerfinish_cleanup;
+ nt = pParse->pNewTrigger;
+ pParse->pNewTrigger = 0;
+ nt->step_list = pStepList;
+ while( pStepList ){
+ pStepList->pTrig = nt;
+ pStepList = pStepList->pNext;
+ }
+ if( sqlite3FixInit(&sFix, pParse, nt->iDb, "trigger", &nt->nameToken)
+ && sqlite3FixTriggerStep(&sFix, nt->step_list) ){
+ goto triggerfinish_cleanup;
+ }
+
+ /* if we are not initializing, and this trigger is not on a TEMP table,
+ ** build the sqlite_master entry
+ */
+ if( !db->init.busy ){
+ static const VdbeOpList insertTrig[] = {
+ { OP_NewRecno, 0, 0, 0 },
+ { OP_String8, 0, 0, "trigger" },
+ { OP_String8, 0, 0, 0 }, /* 2: trigger name */
+ { OP_String8, 0, 0, 0 }, /* 3: table name */
+ { OP_Integer, 0, 0, 0 },
+ { OP_String8, 0, 0, "CREATE TRIGGER "},
+ { OP_String8, 0, 0, 0 }, /* 6: SQL */
+ { OP_Concat, 0, 0, 0 },
+ { OP_MakeRecord, 5, 0, "tttit" },
+ { OP_PutIntKey, 0, 0, 0 },
+ };
+ int addr;
+ Vdbe *v;
+
+ /* Make an entry in the sqlite_master table */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto triggerfinish_cleanup;
+ sqlite3BeginWriteOperation(pParse, 0, nt->iDb);
+ sqlite3OpenMasterTable(v, nt->iDb);
+ addr = sqlite3VdbeAddOpList(v, ArraySize(insertTrig), insertTrig);
+ sqlite3VdbeChangeP3(v, addr+2, nt->name, 0);
+ sqlite3VdbeChangeP3(v, addr+3, nt->table, 0);
+ sqlite3VdbeChangeP3(v, addr+6, pAll->z, pAll->n);
+ if( nt->iDb!=0 ){
+ sqlite3ChangeCookie(db, v, nt->iDb);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeOp3(v, OP_ParseSchema, nt->iDb, 0,
+ sqlite3MPrintf("type='trigger' AND name='%q'", nt->name), P3_DYNAMIC);
+ }
+
+ if( db->init.busy ){
+ Table *pTab;
+ sqlite3HashInsert(&db->aDb[nt->iDb].trigHash,
+ nt->name, strlen(nt->name)+1, nt);
+ pTab = sqlite3LocateTable(pParse, nt->table, db->aDb[nt->iTabDb].zName);
+ assert( pTab!=0 );
+ nt->pNext = pTab->pTrigger;
+ pTab->pTrigger = nt;
+ nt = 0;
+ }
+
+triggerfinish_cleanup:
+ sqlite3DeleteTrigger(nt);
+ sqlite3DeleteTrigger(pParse->pNewTrigger);
+ pParse->pNewTrigger = 0;
+ sqlite3DeleteTriggerStep(pStepList);
+}
+
+/*
+** Make a copy of all components of the given trigger step. This has
+** the effect of copying all Expr.token.z values into memory obtained
+** from sqliteMalloc(). As initially created, the Expr.token.z values
+** all point to the input string that was fed to the parser. But that
+** string is ephemeral - it will go away as soon as the sqlite3_exec()
+** call that started the parser exits. This routine makes a persistent
+** copy of all the Expr.token.z strings so that the TriggerStep structure
+** will be valid even after the sqlite3_exec() call returns.
+*/
+static void sqlitePersistTriggerStep(TriggerStep *p){
+ if( p->target.z ){
+ p->target.z = sqliteStrNDup(p->target.z, p->target.n);
+ p->target.dyn = 1;
+ }
+ if( p->pSelect ){
+ Select *pNew = sqlite3SelectDup(p->pSelect);
+ sqlite3SelectDelete(p->pSelect);
+ p->pSelect = pNew;
+ }
+ if( p->pWhere ){
+ Expr *pNew = sqlite3ExprDup(p->pWhere);
+ sqlite3ExprDelete(p->pWhere);
+ p->pWhere = pNew;
+ }
+ if( p->pExprList ){
+ ExprList *pNew = sqlite3ExprListDup(p->pExprList);
+ sqlite3ExprListDelete(p->pExprList);
+ p->pExprList = pNew;
+ }
+ if( p->pIdList ){
+ IdList *pNew = sqlite3IdListDup(p->pIdList);
+ sqlite3IdListDelete(p->pIdList);
+ p->pIdList = pNew;
+ }
+}
+
+/*
+** Turn a SELECT statement (that the pSelect parameter points to) into
+** a trigger step. Return a pointer to a TriggerStep structure.
+**
+** The parser calls this routine when it finds a SELECT statement in
+** body of a TRIGGER.
+*/
+TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){
+ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ if( pTriggerStep==0 ) return 0;
+
+ pTriggerStep->op = TK_SELECT;
+ pTriggerStep->pSelect = pSelect;
+ pTriggerStep->orconf = OE_Default;
+ sqlitePersistTriggerStep(pTriggerStep);
+
+ return pTriggerStep;
+}
+
+/*
+** Build a trigger step out of an INSERT statement. Return a pointer
+** to the new trigger step.
+**
+** The parser calls this routine when it sees an INSERT inside the
+** body of a trigger.
+*/
+TriggerStep *sqlite3TriggerInsertStep(
+ Token *pTableName, /* Name of the table into which we insert */
+ IdList *pColumn, /* List of columns in pTableName to insert into */
+ ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
+ Select *pSelect, /* A SELECT statement that supplies values */
+ int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
+){
+ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ if( pTriggerStep==0 ) return 0;
+
+ assert(pEList == 0 || pSelect == 0);
+ assert(pEList != 0 || pSelect != 0);
+
+ pTriggerStep->op = TK_INSERT;
+ pTriggerStep->pSelect = pSelect;
+ pTriggerStep->target = *pTableName;
+ pTriggerStep->pIdList = pColumn;
+ pTriggerStep->pExprList = pEList;
+ pTriggerStep->orconf = orconf;
+ sqlitePersistTriggerStep(pTriggerStep);
+
+ return pTriggerStep;
+}
+
+/*
+** Construct a trigger step that implements an UPDATE statement and return
+** a pointer to that trigger step. The parser calls this routine when it
+** sees an UPDATE statement inside the body of a CREATE TRIGGER.
+*/
+TriggerStep *sqlite3TriggerUpdateStep(
+ Token *pTableName, /* Name of the table to be updated */
+ ExprList *pEList, /* The SET clause: list of column and new values */
+ Expr *pWhere, /* The WHERE clause */
+ int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */
+){
+ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ if( pTriggerStep==0 ) return 0;
+
+ pTriggerStep->op = TK_UPDATE;
+ pTriggerStep->target = *pTableName;
+ pTriggerStep->pExprList = pEList;
+ pTriggerStep->pWhere = pWhere;
+ pTriggerStep->orconf = orconf;
+ sqlitePersistTriggerStep(pTriggerStep);
+
+ return pTriggerStep;
+}
+
+/*
+** Construct a trigger step that implements a DELETE statement and return
+** a pointer to that trigger step. The parser calls this routine when it
+** sees a DELETE statement inside the body of a CREATE TRIGGER.
+*/
+TriggerStep *sqlite3TriggerDeleteStep(Token *pTableName, Expr *pWhere){
+ TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
+ if( pTriggerStep==0 ) return 0;
+
+ pTriggerStep->op = TK_DELETE;
+ pTriggerStep->target = *pTableName;
+ pTriggerStep->pWhere = pWhere;
+ pTriggerStep->orconf = OE_Default;
+ sqlitePersistTriggerStep(pTriggerStep);
+
+ return pTriggerStep;
+}
+
+/*
+** Recursively delete a Trigger structure
+*/
+void sqlite3DeleteTrigger(Trigger *pTrigger){
+ if( pTrigger==0 ) return;
+ sqlite3DeleteTriggerStep(pTrigger->step_list);
+ sqliteFree(pTrigger->name);
+ sqliteFree(pTrigger->table);
+ sqlite3ExprDelete(pTrigger->pWhen);
+ sqlite3IdListDelete(pTrigger->pColumns);
+ if( pTrigger->nameToken.dyn ) sqliteFree((char*)pTrigger->nameToken.z);
+ sqliteFree(pTrigger);
+}
+
+/*
+** This function is called to drop a trigger from the database schema.
+**
+** This may be called directly from the parser and therefore identifies
+** the trigger by name. The sqlite3DropTriggerPtr() routine does the
+** same job as this routine except it takes a pointer to the trigger
+** instead of the trigger name.
+**/
+void sqlite3DropTrigger(Parse *pParse, SrcList *pName){
+ Trigger *pTrigger = 0;
+ int i;
+ const char *zDb;
+ const char *zName;
+ int nName;
+ sqlite3 *db = pParse->db;
+
+ if( sqlite3_malloc_failed ) goto drop_trigger_cleanup;
+ if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
+ goto drop_trigger_cleanup;
+ }
+
+ assert( pName->nSrc==1 );
+ zDb = pName->a[0].zDatabase;
+ zName = pName->a[0].zName;
+ nName = strlen(zName);
+ for(i=0; i<db->nDb; i++){
+ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
+ if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
+ pTrigger = sqlite3HashFind(&(db->aDb[j].trigHash), zName, nName+1);
+ if( pTrigger ) break;
+ }
+ if( !pTrigger ){
+ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0);
+ goto drop_trigger_cleanup;
+ }
+ sqlite3DropTriggerPtr(pParse, pTrigger, 0);
+
+drop_trigger_cleanup:
+ sqlite3SrcListDelete(pName);
+}
+
+/*
+** Return a pointer to the Table structure for the table that a trigger
+** is set on.
+*/
+static Table *tableOfTrigger(sqlite3 *db, Trigger *pTrigger){
+ return sqlite3FindTable(db,pTrigger->table,db->aDb[pTrigger->iTabDb].zName);
+}
+
+
+/*
+** Drop a trigger given a pointer to that trigger. If nested is false,
+** then also generate code to remove the trigger from the SQLITE_MASTER
+** table.
+*/
+void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger, int nested){
+ Table *pTable;
+ Vdbe *v;
+ sqlite3 *db = pParse->db;
+ int iDb;
+
+ iDb = pTrigger->iDb;
+ assert( iDb>=0 && iDb<db->nDb );
+ pTable = tableOfTrigger(db, pTrigger);
+ assert(pTable);
+ assert( pTable->iDb==iDb || iDb==1 );
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int code = SQLITE_DROP_TRIGGER;
+ const char *zDb = db->aDb[iDb].zName;
+ const char *zTab = SCHEMA_TABLE(iDb);
+ if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;
+ if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) ||
+ sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
+ return;
+ }
+ }
+#endif
+
+ /* Generate code to destroy the database record of the trigger.
+ */
+ if( pTable!=0 && (v = sqlite3GetVdbe(pParse))!=0 ){
+ int base;
+ static const VdbeOpList dropTrigger[] = {
+ { OP_Rewind, 0, ADDR(9), 0},
+ { OP_String8, 0, 0, 0}, /* 1 */
+ { OP_Column, 0, 1, 0},
+ { OP_Ne, 0, ADDR(8), 0},
+ { OP_String8, 0, 0, "trigger"},
+ { OP_Column, 0, 0, 0},
+ { OP_Ne, 0, ADDR(8), 0},
+ { OP_Delete, 0, 0, 0},
+ { OP_Next, 0, ADDR(1), 0}, /* 8 */
+ };
+
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ sqlite3OpenMasterTable(v, iDb);
+ base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
+ sqlite3VdbeChangeP3(v, base+1, pTrigger->name, 0);
+ sqlite3ChangeCookie(db, v, iDb);
+ sqlite3VdbeAddOp(v, OP_Close, 0, 0);
+ sqlite3VdbeOp3(v, OP_DropTrigger, iDb, 0, pTrigger->name, 0);
+ }
+}
+
+/*
+** Remove a trigger from the hash tables of the sqlite* pointer.
+*/
+void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){
+ Trigger *pTrigger;
+ int nName = strlen(zName);
+ pTrigger = sqlite3HashInsert(&(db->aDb[iDb].trigHash), zName, nName+1, 0);
+ if( pTrigger ){
+ Table *pTable = tableOfTrigger(db, pTrigger);
+ assert( pTable!=0 );
+ if( pTable->pTrigger == pTrigger ){
+ pTable->pTrigger = pTrigger->pNext;
+ }else{
+ Trigger *cc = pTable->pTrigger;
+ while( cc ){
+ if( cc->pNext == pTrigger ){
+ cc->pNext = cc->pNext->pNext;
+ break;
+ }
+ cc = cc->pNext;
+ }
+ assert(cc);
+ }
+ sqlite3DeleteTrigger(pTrigger);
+ db->flags |= SQLITE_InternChanges;
+ }
+}
+
+/*
+** pEList is the SET clause of an UPDATE statement. Each entry
+** in pEList is of the format <id>=<expr>. If any of the entries
+** in pEList have an <id> which matches an identifier in pIdList,
+** then return TRUE. If pIdList==NULL, then it is considered a
+** wildcard that matches anything. Likewise if pEList==NULL then
+** it matches anything so always return true. Return false only
+** if there is no match.
+*/
+static int checkColumnOverLap(IdList *pIdList, ExprList *pEList){
+ int e;
+ if( !pIdList || !pEList ) return 1;
+ for(e=0; e<pEList->nExpr; e++){
+ if( sqlite3IdListIndex(pIdList, pEList->a[e].zName)>=0 ) return 1;
+ }
+ return 0;
+}
+
+/* A global variable that is TRUE if we should always set up temp tables for
+ * for triggers, even if there are no triggers to code. This is used to test
+ * how much overhead the triggers algorithm is causing.
+ *
+ * This flag can be set or cleared using the "trigger_overhead_test" pragma.
+ * The pragma is not documented since it is not really part of the interface
+ * to SQLite, just the test procedure.
+*/
+int sqlite3_always_code_trigger_setup = 0;
+
+/*
+ * Returns true if a trigger matching op, tr_tm and foreach that is NOT already
+ * on the Parse objects trigger-stack (to prevent recursive trigger firing) is
+ * found in the list specified as pTrigger.
+ */
+int sqlite3TriggersExist(
+ Parse *pParse, /* Used to check for recursive triggers */
+ Trigger *pTrigger, /* A list of triggers associated with a table */
+ int op, /* one of TK_DELETE, TK_INSERT, TK_UPDATE */
+ int tr_tm, /* one of TK_BEFORE, TK_AFTER */
+ int foreach, /* one of TK_ROW or TK_STATEMENT */
+ ExprList *pChanges /* Columns that change in an UPDATE statement */
+){
+ Trigger * pTriggerCursor;
+
+ if( sqlite3_always_code_trigger_setup ){
+ return 1;
+ }
+
+ pTriggerCursor = pTrigger;
+ while( pTriggerCursor ){
+ if( pTriggerCursor->op == op &&
+ pTriggerCursor->tr_tm == tr_tm &&
+ pTriggerCursor->foreach == foreach &&
+ checkColumnOverLap(pTriggerCursor->pColumns, pChanges) ){
+ TriggerStack * ss;
+ ss = pParse->trigStack;
+ while( ss && ss->pTrigger != pTrigger ){
+ ss = ss->pNext;
+ }
+ if( !ss )return 1;
+ }
+ pTriggerCursor = pTriggerCursor->pNext;
+ }
+
+ return 0;
+}
+
+/*
+** Convert the pStep->target token into a SrcList and return a pointer
+** to that SrcList.
+**
+** This routine adds a specific database name, if needed, to the target when
+** forming the SrcList. This prevents a trigger in one database from
+** referring to a target in another database. An exception is when the
+** trigger is in TEMP in which case it can refer to any other database it
+** wants.
+*/
+static SrcList *targetSrcList(
+ Parse *pParse, /* The parsing context */
+ TriggerStep *pStep /* The trigger containing the target token */
+){
+ Token sDb; /* Dummy database name token */
+ int iDb; /* Index of the database to use */
+ SrcList *pSrc; /* SrcList to be returned */
+
+ iDb = pStep->pTrig->iDb;
+ if( iDb==0 || iDb>=2 ){
+ assert( iDb<pParse->db->nDb );
+ sDb.z = pParse->db->aDb[iDb].zName;
+ sDb.n = strlen(sDb.z);
+ pSrc = sqlite3SrcListAppend(0, &sDb, &pStep->target);
+ } else {
+ pSrc = sqlite3SrcListAppend(0, &pStep->target, 0);
+ }
+ return pSrc;
+}
+
+/*
+** Generate VDBE code for zero or more statements inside the body of a
+** trigger.
+*/
+static int codeTriggerProgram(
+ Parse *pParse, /* The parser context */
+ TriggerStep *pStepList, /* List of statements inside the trigger body */
+ int orconfin /* Conflict algorithm. (OE_Abort, etc) */
+){
+ TriggerStep * pTriggerStep = pStepList;
+ int orconf;
+ Vdbe *v = pParse->pVdbe;
+
+ assert( pTriggerStep!=0 );
+ assert( v!=0 );
+ sqlite3VdbeAddOp(v, OP_ContextPush, 0, 0);
+ VdbeComment((v, "# begin trigger %s", pStepList->pTrig->name));
+ while( pTriggerStep ){
+ orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin;
+ pParse->trigStack->orconf = orconf;
+ switch( pTriggerStep->op ){
+ case TK_SELECT: {
+ Select * ss = sqlite3SelectDup(pTriggerStep->pSelect);
+ assert(ss);
+ assert(ss->pSrc);
+ sqlite3Select(pParse, ss, SRT_Discard, 0, 0, 0, 0, 0);
+ sqlite3SelectDelete(ss);
+ break;
+ }
+ case TK_UPDATE: {
+ SrcList *pSrc;
+ pSrc = targetSrcList(pParse, pTriggerStep);
+ sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ sqlite3Update(pParse, pSrc,
+ sqlite3ExprListDup(pTriggerStep->pExprList),
+ sqlite3ExprDup(pTriggerStep->pWhere), orconf);
+ sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ break;
+ }
+ case TK_INSERT: {
+ SrcList *pSrc;
+ pSrc = targetSrcList(pParse, pTriggerStep);
+ sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ sqlite3Insert(pParse, pSrc,
+ sqlite3ExprListDup(pTriggerStep->pExprList),
+ sqlite3SelectDup(pTriggerStep->pSelect),
+ sqlite3IdListDup(pTriggerStep->pIdList), orconf);
+ sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ break;
+ }
+ case TK_DELETE: {
+ SrcList *pSrc;
+ sqlite3VdbeAddOp(v, OP_ResetCount, 0, 0);
+ pSrc = targetSrcList(pParse, pTriggerStep);
+ sqlite3DeleteFrom(pParse, pSrc, sqlite3ExprDup(pTriggerStep->pWhere));
+ sqlite3VdbeAddOp(v, OP_ResetCount, 1, 0);
+ break;
+ }
+ default:
+ assert(0);
+ }
+ pTriggerStep = pTriggerStep->pNext;
+ }
+ sqlite3VdbeAddOp(v, OP_ContextPop, 0, 0);
+ VdbeComment((v, "# end trigger %s", pStepList->pTrig->name));
+
+ return 0;
+}
+
+/*
+** This is called to code FOR EACH ROW triggers.
+**
+** When the code that this function generates is executed, the following
+** must be true:
+**
+** 1. No cursors may be open in the main database. (But newIdx and oldIdx
+** can be indices of cursors in temporary tables. See below.)
+**
+** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then
+** a temporary vdbe cursor (index newIdx) must be open and pointing at
+** a row containing values to be substituted for new.* expressions in the
+** trigger program(s).
+**
+** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then
+** a temporary vdbe cursor (index oldIdx) must be open and pointing at
+** a row containing values to be substituted for old.* expressions in the
+** trigger program(s).
+**
+*/
+int sqlite3CodeRowTrigger(
+ Parse *pParse, /* Parse context */
+ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */
+ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */
+ int tr_tm, /* One of TK_BEFORE, TK_AFTER */
+ Table *pTab, /* The table to code triggers from */
+ int newIdx, /* The indice of the "new" row to access */
+ int oldIdx, /* The indice of the "old" row to access */
+ int orconf, /* ON CONFLICT policy */
+ int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */
+){
+ Trigger *pTrigger;
+ TriggerStack *pStack;
+ TriggerStack trigStackEntry;
+
+ assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
+ assert(tr_tm == TK_BEFORE || tr_tm == TK_AFTER );
+
+ assert(newIdx != -1 || oldIdx != -1);
+
+ pTrigger = pTab->pTrigger;
+ while( pTrigger ){
+ int fire_this = 0;
+
+ /* determine whether we should code this trigger */
+ if( pTrigger->op == op && pTrigger->tr_tm == tr_tm &&
+ pTrigger->foreach == TK_ROW ){
+ fire_this = 1;
+ for(pStack=pParse->trigStack; pStack; pStack=pStack->pNext){
+ if( pStack->pTrigger==pTrigger ){
+ fire_this = 0;
+ }
+ }
+ if( op == TK_UPDATE && pTrigger->pColumns &&
+ !checkColumnOverLap(pTrigger->pColumns, pChanges) ){
+ fire_this = 0;
+ }
+ }
+
+ if( fire_this ){
+ int endTrigger;
+ SrcList dummyTablist;
+ Expr * whenExpr;
+ AuthContext sContext;
+
+ dummyTablist.nSrc = 0;
+
+ /* Push an entry on to the trigger stack */
+ trigStackEntry.pTrigger = pTrigger;
+ trigStackEntry.newIdx = newIdx;
+ trigStackEntry.oldIdx = oldIdx;
+ trigStackEntry.pTab = pTab;
+ trigStackEntry.pNext = pParse->trigStack;
+ trigStackEntry.ignoreJump = ignoreJump;
+ pParse->trigStack = &trigStackEntry;
+ sqlite3AuthContextPush(pParse, &sContext, pTrigger->name);
+
+ /* code the WHEN clause */
+ endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe);
+ whenExpr = sqlite3ExprDup(pTrigger->pWhen);
+ if( sqlite3ExprResolveIds(pParse, &dummyTablist, 0, whenExpr) ){
+ pParse->trigStack = trigStackEntry.pNext;
+ sqlite3ExprDelete(whenExpr);
+ return 1;
+ }
+ sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, 1);
+ sqlite3ExprDelete(whenExpr);
+
+ codeTriggerProgram(pParse, pTrigger->step_list, orconf);
+
+ /* Pop the entry off the trigger stack */
+ pParse->trigStack = trigStackEntry.pNext;
+ sqlite3AuthContextPop(&sContext);
+
+ sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger);
+ }
+ pTrigger = pTrigger->pNext;
+ }
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/update.c b/kopete/plugins/statistics/sqlite/update.c
new file mode 100644
index 00000000..08c7987c
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/update.c
@@ -0,0 +1,450 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains C code routines that are called by the parser
+** to handle UPDATE statements.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** Process an UPDATE statement.
+**
+** UPDATE OR IGNORE table_wxyz SET a=b, c=d WHERE e<5 AND f NOT NULL;
+** \_______/ \________/ \______/ \________________/
+* onError pTabList pChanges pWhere
+*/
+void sqlite3Update(
+ Parse *pParse, /* The parser context */
+ SrcList *pTabList, /* The table in which we should change things */
+ ExprList *pChanges, /* Things to be changed */
+ Expr *pWhere, /* The WHERE clause. May be null */
+ int onError /* How to handle constraint errors */
+){
+ int i, j; /* Loop counters */
+ Table *pTab; /* The table to be updated */
+ int addr = 0; /* VDBE instruction address of the start of the loop */
+ WhereInfo *pWInfo; /* Information about the WHERE clause */
+ Vdbe *v; /* The virtual database engine */
+ Index *pIdx; /* For looping over indices */
+ int nIdx; /* Number of indices that need updating */
+ int nIdxTotal; /* Total number of indices */
+ int iCur; /* VDBE Cursor number of pTab */
+ sqlite3 *db; /* The database structure */
+ Index **apIdx = 0; /* An array of indices that need updating too */
+ char *aIdxUsed = 0; /* aIdxUsed[i]==1 if the i-th index is used */
+ int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
+ ** an expression for the i-th column of the table.
+ ** aXRef[i]==-1 if the i-th column is not changed. */
+ int chngRecno; /* True if the record number is being changed */
+ Expr *pRecnoExpr = 0; /* Expression defining the new record number */
+ int openAll = 0; /* True if all indices need to be opened */
+ int isView; /* Trying to update a view */
+ AuthContext sContext; /* The authorization context */
+
+ int before_triggers; /* True if there are any BEFORE triggers */
+ int after_triggers; /* True if there are any AFTER triggers */
+ int row_triggers_exist = 0; /* True if any row triggers exist */
+
+ int newIdx = -1; /* index of trigger "new" temp table */
+ int oldIdx = -1; /* index of trigger "old" temp table */
+
+ sContext.pParse = 0;
+ if( pParse->nErr || sqlite3_malloc_failed ) goto update_cleanup;
+ db = pParse->db;
+ assert( pTabList->nSrc==1 );
+
+ /* Locate the table which we want to update.
+ */
+ pTab = sqlite3SrcListLookup(pParse, pTabList);
+ if( pTab==0 ) goto update_cleanup;
+ before_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
+ TK_UPDATE, TK_BEFORE, TK_ROW, pChanges);
+ after_triggers = sqlite3TriggersExist(pParse, pTab->pTrigger,
+ TK_UPDATE, TK_AFTER, TK_ROW, pChanges);
+ row_triggers_exist = before_triggers || after_triggers;
+ isView = pTab->pSelect!=0;
+ if( sqlite3IsReadOnly(pParse, pTab, before_triggers) ){
+ goto update_cleanup;
+ }
+ if( isView ){
+ if( sqlite3ViewGetColumnNames(pParse, pTab) ){
+ goto update_cleanup;
+ }
+ }
+ aXRef = sqliteMallocRaw( sizeof(int) * pTab->nCol );
+ if( aXRef==0 ) goto update_cleanup;
+ for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
+
+ /* If there are FOR EACH ROW triggers, allocate cursors for the
+ ** special OLD and NEW tables
+ */
+ if( row_triggers_exist ){
+ newIdx = pParse->nTab++;
+ oldIdx = pParse->nTab++;
+ }
+
+ /* Allocate a cursors for the main database table and for all indices.
+ ** The index cursors might not be used, but if they are used they
+ ** need to occur right after the database cursor. So go ahead and
+ ** allocate enough space, just in case.
+ */
+ pTabList->a[0].iCursor = iCur = pParse->nTab++;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ pParse->nTab++;
+ }
+
+ /* Resolve the column names in all the expressions of the
+ ** of the UPDATE statement. Also find the column index
+ ** for each column to be updated in the pChanges array. For each
+ ** column to be updated, make sure we have authorization to change
+ ** that column.
+ */
+ chngRecno = 0;
+ for(i=0; i<pChanges->nExpr; i++){
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0,
+ pChanges->a[i].pExpr, 0, 0) ){
+ goto update_cleanup;
+ }
+ for(j=0; j<pTab->nCol; j++){
+ if( sqlite3StrICmp(pTab->aCol[j].zName, pChanges->a[i].zName)==0 ){
+ if( j==pTab->iPKey ){
+ chngRecno = 1;
+ pRecnoExpr = pChanges->a[i].pExpr;
+ }
+ aXRef[j] = i;
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ if( sqlite3IsRowid(pChanges->a[i].zName) ){
+ chngRecno = 1;
+ pRecnoExpr = pChanges->a[i].pExpr;
+ }else{
+ sqlite3ErrorMsg(pParse, "no such column: %s", pChanges->a[i].zName);
+ goto update_cleanup;
+ }
+ }
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ {
+ int rc;
+ rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
+ pTab->aCol[j].zName, db->aDb[pTab->iDb].zName);
+ if( rc==SQLITE_DENY ){
+ goto update_cleanup;
+ }else if( rc==SQLITE_IGNORE ){
+ aXRef[j] = -1;
+ }
+ }
+#endif
+ }
+
+ /* Allocate memory for the array apIdx[] and fill it with pointers to every
+ ** index that needs to be updated. Indices only need updating if their
+ ** key includes one of the columns named in pChanges or if the record
+ ** number of the original table entry is changing.
+ */
+ for(nIdx=nIdxTotal=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdxTotal++){
+ if( chngRecno ){
+ i = 0;
+ }else {
+ for(i=0; i<pIdx->nColumn; i++){
+ if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
+ }
+ }
+ if( i<pIdx->nColumn ) nIdx++;
+ }
+ if( nIdxTotal>0 ){
+ apIdx = sqliteMallocRaw( sizeof(Index*) * nIdx + nIdxTotal );
+ if( apIdx==0 ) goto update_cleanup;
+ aIdxUsed = (char*)&apIdx[nIdx];
+ }
+ for(nIdx=j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ if( chngRecno ){
+ i = 0;
+ }else{
+ for(i=0; i<pIdx->nColumn; i++){
+ if( aXRef[pIdx->aiColumn[i]]>=0 ) break;
+ }
+ }
+ if( i<pIdx->nColumn ){
+ if( sqlite3CheckIndexCollSeq(pParse, pIdx) ) goto update_cleanup;
+ apIdx[nIdx++] = pIdx;
+ aIdxUsed[j] = 1;
+ }else{
+ aIdxUsed[j] = 0;
+ }
+ }
+
+ /* Resolve the column names in all the expressions in the
+ ** WHERE clause.
+ */
+ if( sqlite3ExprResolveAndCheck(pParse, pTabList, 0, pWhere, 0, 0) ){
+ goto update_cleanup;
+ }
+
+ /* Start the view context
+ */
+ if( isView ){
+ sqlite3AuthContextPush(pParse, &sContext, pTab->zName);
+ }
+
+ /* Begin generating code.
+ */
+ v = sqlite3GetVdbe(pParse);
+ if( v==0 ) goto update_cleanup;
+ sqlite3VdbeCountChanges(v);
+ sqlite3BeginWriteOperation(pParse, 1, pTab->iDb);
+
+ /* If we are trying to update a view, construct that view into
+ ** a temporary table.
+ */
+ if( isView ){
+ Select *pView;
+ pView = sqlite3SelectDup(pTab->pSelect);
+ sqlite3Select(pParse, pView, SRT_TempTable, iCur, 0, 0, 0, 0);
+ sqlite3SelectDelete(pView);
+ }
+
+ /* Begin the database scan
+ */
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 1, 0);
+ if( pWInfo==0 ) goto update_cleanup;
+
+ /* Remember the index of every item to be updated.
+ */
+ sqlite3VdbeAddOp(v, OP_ListWrite, 0, 0);
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+
+ /* Initialize the count of updated rows
+ */
+ if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
+ sqlite3VdbeAddOp(v, OP_Integer, 0, 0);
+ }
+
+ if( row_triggers_exist ){
+ /* Create pseudo-tables for NEW and OLD
+ */
+ sqlite3VdbeAddOp(v, OP_OpenPseudo, oldIdx, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, oldIdx, pTab->nCol);
+ sqlite3VdbeAddOp(v, OP_OpenPseudo, newIdx, 0);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, newIdx, pTab->nCol);
+
+ /* The top of the update loop for when there are triggers.
+ */
+ sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+
+ /* Open a cursor and make it point to the record that is
+ ** being updated.
+ */
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ if( !isView ){
+ sqlite3OpenTableForReading(v, iCur, pTab);
+ }
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+
+ /* Generate the OLD table
+ */
+ sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_RowData, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_PutIntKey, oldIdx, 0);
+
+ /* Generate the NEW table
+ */
+ if( chngRecno ){
+ sqlite3ExprCode(pParse, pRecnoExpr);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
+ }
+ for(i=0; i<pTab->nCol; i++){ /* TODO: Factor out this loop as common code */
+ if( i==pTab->iPKey ){
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ continue;
+ }
+ j = aXRef[i];
+ if( j<0 ){
+ sqlite3VdbeAddOp(v, OP_Column, iCur, i);
+ }else{
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0);
+ if( !isView ){
+ sqlite3TableAffinityStr(v, pTab);
+ }
+ if( pParse->nErr ) goto update_cleanup;
+ sqlite3VdbeAddOp(v, OP_PutIntKey, newIdx, 0);
+ if( !isView ){
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+
+ /* Fire the BEFORE and INSTEAD OF triggers
+ */
+ if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_BEFORE, pTab,
+ newIdx, oldIdx, onError, addr) ){
+ goto update_cleanup;
+ }
+ }
+
+ if( !isView ){
+ /*
+ ** Open every index that needs updating. Note that if any
+ ** index could potentially invoke a REPLACE conflict resolution
+ ** action, then we need to open all indices because we might need
+ ** to be deleting some records.
+ */
+ sqlite3VdbeAddOp(v, OP_Integer, pTab->iDb, 0);
+ sqlite3VdbeAddOp(v, OP_OpenWrite, iCur, pTab->tnum);
+ sqlite3VdbeAddOp(v, OP_SetNumColumns, iCur, pTab->nCol);
+ if( onError==OE_Replace ){
+ openAll = 1;
+ }else{
+ openAll = 0;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->onError==OE_Replace ){
+ openAll = 1;
+ break;
+ }
+ }
+ }
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ if( openAll || aIdxUsed[i] ){
+ sqlite3VdbeAddOp(v, OP_Integer, pIdx->iDb, 0);
+ sqlite3VdbeOp3(v, OP_OpenWrite, iCur+i+1, pIdx->tnum,
+ (char*)&pIdx->keyInfo, P3_KEYINFO);
+ assert( pParse->nTab>iCur+i+1 );
+ }
+ }
+
+ /* Loop over every record that needs updating. We have to load
+ ** the old data for each record to be updated because some columns
+ ** might not change and we will need to copy the old value.
+ ** Also, the old data is needed to delete the old index entires.
+ ** So make the cursor point at the old record.
+ */
+ if( !row_triggers_exist ){
+ sqlite3VdbeAddOp(v, OP_ListRewind, 0, 0);
+ addr = sqlite3VdbeAddOp(v, OP_ListRead, 0, 0);
+ sqlite3VdbeAddOp(v, OP_Dup, 0, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_NotExists, iCur, addr);
+
+ /* If the record number will change, push the record number as it
+ ** will be after the update. (The old record number is currently
+ ** on top of the stack.)
+ */
+ if( chngRecno ){
+ sqlite3ExprCode(pParse, pRecnoExpr);
+ sqlite3VdbeAddOp(v, OP_MustBeInt, 0, 0);
+ }
+
+ /* Compute new data for this record.
+ */
+ for(i=0; i<pTab->nCol; i++){
+ if( i==pTab->iPKey ){
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ continue;
+ }
+ j = aXRef[i];
+ if( j<0 ){
+ sqlite3VdbeAddOp(v, OP_Column, iCur, i);
+ }else{
+ sqlite3ExprCode(pParse, pChanges->a[j].pExpr);
+ }
+ }
+
+ /* Do constraint checks
+ */
+ sqlite3GenerateConstraintChecks(pParse, pTab, iCur, aIdxUsed, chngRecno, 1,
+ onError, addr);
+
+ /* Delete the old indices for the current record.
+ */
+ sqlite3GenerateRowIndexDelete(db, v, pTab, iCur, aIdxUsed);
+
+ /* If changing the record number, delete the old record.
+ */
+ if( chngRecno ){
+ sqlite3VdbeAddOp(v, OP_Delete, iCur, 0);
+ }
+
+ /* Create the new index entries and the new record.
+ */
+ sqlite3CompleteInsertion(pParse, pTab, iCur, aIdxUsed, chngRecno, 1, -1);
+ }
+
+ /* Increment the row counter
+ */
+ if( db->flags & SQLITE_CountRows && !pParse->trigStack){
+ sqlite3VdbeAddOp(v, OP_AddImm, 1, 0);
+ }
+
+ /* If there are triggers, close all the cursors after each iteration
+ ** through the loop. The fire the after triggers.
+ */
+ if( row_triggers_exist ){
+ if( !isView ){
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ if( openAll || aIdxUsed[i] )
+ sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }
+ if( sqlite3CodeRowTrigger(pParse, TK_UPDATE, pChanges, TK_AFTER, pTab,
+ newIdx, oldIdx, onError, addr) ){
+ goto update_cleanup;
+ }
+ }
+
+ /* Repeat the above with the next record to be updated, until
+ ** all record selected by the WHERE clause have been updated.
+ */
+ sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
+ sqlite3VdbeChangeP2(v, addr, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeAddOp(v, OP_ListReset, 0, 0);
+
+ /* Close all tables if there were no FOR EACH ROW triggers */
+ if( !row_triggers_exist ){
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ if( openAll || aIdxUsed[i] ){
+ sqlite3VdbeAddOp(v, OP_Close, iCur+i+1, 0);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_Close, iCur, 0);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Close, newIdx, 0);
+ sqlite3VdbeAddOp(v, OP_Close, oldIdx, 0);
+ }
+
+ /*
+ ** Return the number of rows that were changed.
+ */
+ if( db->flags & SQLITE_CountRows && !pParse->trigStack ){
+ sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
+ sqlite3VdbeSetNumCols(v, 1);
+ sqlite3VdbeSetColName(v, 0, "rows updated", P3_STATIC);
+ }
+
+update_cleanup:
+ sqlite3AuthContextPop(&sContext);
+ sqliteFree(apIdx);
+ sqliteFree(aXRef);
+ sqlite3SrcListDelete(pTabList);
+ sqlite3ExprListDelete(pChanges);
+ sqlite3ExprDelete(pWhere);
+ return;
+}
diff --git a/kopete/plugins/statistics/sqlite/utf.c b/kopete/plugins/statistics/sqlite/utf.c
new file mode 100644
index 00000000..58b1a972
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/utf.c
@@ -0,0 +1,566 @@
+/*
+** 2004 April 13
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains routines used to translate between UTF-8,
+** UTF-16, UTF-16BE, and UTF-16LE.
+**
+** $Id$
+**
+** Notes on UTF-8:
+**
+** Byte-0 Byte-1 Byte-2 Byte-3 Value
+** 0xxxxxxx 00000000 00000000 0xxxxxxx
+** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx
+** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx
+** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx
+**
+**
+** Notes on UTF-16: (with wwww+1==uuuuu)
+**
+** Word-0 Word-1 Value
+** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
+** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx
+**
+**
+** BOM or Byte Order Mark:
+** 0xff 0xfe little-endian utf-16 follows
+** 0xfe 0xff big-endian utf-16 follows
+**
+**
+** Handling of malformed strings:
+**
+** SQLite accepts and processes malformed strings without an error wherever
+** possible. However this is not possible when converting between UTF-8 and
+** UTF-16.
+**
+** When converting malformed UTF-8 strings to UTF-16, one instance of the
+** replacement character U+FFFD for each byte that cannot be interpeted as
+** part of a valid unicode character.
+**
+** When converting malformed UTF-16 strings to UTF-8, one instance of the
+** replacement character U+FFFD for each pair of bytes that cannot be
+** interpeted as part of a valid unicode character.
+**
+** This file contains the following public routines:
+**
+** sqlite3VdbeMemTranslate() - Translate the encoding used by a Mem* string.
+** sqlite3VdbeMemHandleBom() - Handle byte-order-marks in UTF16 Mem* strings.
+** sqlite3utf16ByteLen() - Calculate byte-length of a void* UTF16 string.
+** sqlite3utf8CharLen() - Calculate char-length of a char* UTF8 string.
+** sqlite3utf8LikeCompare() - Do a LIKE match given two UTF8 char* strings.
+**
+*/
+#include <assert.h>
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
+/*
+** This table maps from the first byte of a UTF-8 character to the number
+** of trailing bytes expected. A value '255' indicates that the table key
+** is not a legal first byte for a UTF-8 character.
+*/
+static const u8 xtra_utf8_bytes[256] = {
+/* 0xxxxxxx */
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* 10wwwwww */
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+
+/* 110yyyyy */
+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,
+
+/* 1110zzzz */
+2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+
+/* 11110yyy */
+3, 3, 3, 3, 3, 3, 3, 3, 255, 255, 255, 255, 255, 255, 255, 255,
+};
+
+/*
+** This table maps from the number of trailing bytes in a UTF-8 character
+** to an integer constant that is effectively calculated for each character
+** read by a naive implementation of a UTF-8 character reader. The code
+** in the READ_UTF8 macro explains things best.
+*/
+static const int xtra_utf8_bits[4] = {
+0,
+12416, /* (0xC0 << 6) + (0x80) */
+925824, /* (0xE0 << 12) + (0x80 << 6) + (0x80) */
+63447168 /* (0xF0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
+};
+
+#define READ_UTF8(zIn, c) { \
+ int xtra; \
+ c = *(zIn)++; \
+ xtra = xtra_utf8_bytes[c]; \
+ switch( xtra ){ \
+ case 255: c = (int)0xFFFD; break; \
+ case 3: c = (c<<6) + *(zIn)++; \
+ case 2: c = (c<<6) + *(zIn)++; \
+ case 1: c = (c<<6) + *(zIn)++; \
+ c -= xtra_utf8_bits[xtra]; \
+ } \
+}
+int sqlite3ReadUtf8(const unsigned char *z){
+ int c;
+ READ_UTF8(z, c);
+ return c;
+}
+
+#define SKIP_UTF8(zIn) { \
+ zIn += (xtra_utf8_bytes[*(u8 *)zIn] + 1); \
+}
+
+#define WRITE_UTF8(zOut, c) { \
+ if( c<0x00080 ){ \
+ *zOut++ = (c&0xFF); \
+ } \
+ else if( c<0x00800 ){ \
+ *zOut++ = 0xC0 + ((c>>6)&0x1F); \
+ *zOut++ = 0x80 + (c & 0x3F); \
+ } \
+ else if( c<0x10000 ){ \
+ *zOut++ = 0xE0 + ((c>>12)&0x0F); \
+ *zOut++ = 0x80 + ((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (c & 0x3F); \
+ }else{ \
+ *zOut++ = 0xF0 + ((c>>18) & 0x07); \
+ *zOut++ = 0x80 + ((c>>12) & 0x3F); \
+ *zOut++ = 0x80 + ((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (c & 0x3F); \
+ } \
+}
+
+#define WRITE_UTF16LE(zOut, c) { \
+ if( c<=0xFFFF ){ \
+ *zOut++ = (c&0x00FF); \
+ *zOut++ = ((c>>8)&0x00FF); \
+ }else{ \
+ *zOut++ = (((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
+ *zOut++ = (0x00D8 + (((c-0x10000)>>18)&0x03)); \
+ *zOut++ = (c&0x00FF); \
+ *zOut++ = (0x00DC + ((c>>8)&0x03)); \
+ } \
+}
+
+#define WRITE_UTF16BE(zOut, c) { \
+ if( c<=0xFFFF ){ \
+ *zOut++ = ((c>>8)&0x00FF); \
+ *zOut++ = (c&0x00FF); \
+ }else{ \
+ *zOut++ = (0x00D8 + (((c-0x10000)>>18)&0x03)); \
+ *zOut++ = (((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
+ *zOut++ = (0x00DC + ((c>>8)&0x03)); \
+ *zOut++ = (c&0x00FF); \
+ } \
+}
+
+#define READ_UTF16LE(zIn, c){ \
+ c = (*zIn++); \
+ c += ((*zIn++)<<8); \
+ if( c>=0xD800 && c<=0xE000 ){ \
+ int c2 = (*zIn++); \
+ c2 += ((*zIn++)<<8); \
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
+ } \
+}
+
+#define READ_UTF16BE(zIn, c){ \
+ c = ((*zIn++)<<8); \
+ c += (*zIn++); \
+ if( c>=0xD800 && c<=0xE000 ){ \
+ int c2 = ((*zIn++)<<8); \
+ c2 += (*zIn++); \
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \
+ } \
+}
+
+#define SKIP_UTF16BE(zIn){ \
+ if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn+1)==0x00)) ){ \
+ zIn += 4; \
+ }else{ \
+ zIn += 2; \
+ } \
+}
+#define SKIP_UTF16LE(zIn){ \
+ zIn++; \
+ if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn-1)==0x00)) ){ \
+ zIn += 3; \
+ }else{ \
+ zIn += 1; \
+ } \
+}
+
+#define RSKIP_UTF16LE(zIn){ \
+ if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn-1)==0x00)) ){ \
+ zIn -= 4; \
+ }else{ \
+ zIn -= 2; \
+ } \
+}
+#define RSKIP_UTF16BE(zIn){ \
+ zIn--; \
+ if( *zIn>=0xD8 && (*zIn<0xE0 || (*zIn==0xE0 && *(zIn+1)==0x00)) ){ \
+ zIn -= 3; \
+ }else{ \
+ zIn -= 1; \
+ } \
+}
+
+/*
+** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
+** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
+*/
+/* #define TRANSLATE_TRACE 1 */
+
+/*
+** This routine transforms the internal text encoding used by pMem to
+** desiredEnc. It is an error if the string is already of the desired
+** encoding, or if *pMem does not contain a string value.
+*/
+int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
+ unsigned char zShort[NBFS]; /* Temporary short output buffer */
+ int len; /* Maximum length of output string in bytes */
+ unsigned char *zOut; /* Output buffer */
+ unsigned char *zIn; /* Input iterator */
+ unsigned char *zTerm; /* End of input */
+ unsigned char *z; /* Output iterator */
+ int c;
+
+ assert( pMem->flags&MEM_Str );
+ assert( pMem->enc!=desiredEnc );
+ assert( pMem->enc!=0 );
+ assert( pMem->n>=0 );
+
+#ifdef TRANSLATE_TRACE
+ {
+ char zBuf[100];
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf, 100);
+ fprintf(stderr, "INPUT: %s\n", zBuf);
+ }
+#endif
+
+ /* If the translation is between UTF-16 little and big endian, then
+ ** all that is required is to swap the byte order. This case is handled
+ ** differently from the others.
+ */
+ if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){
+ u8 temp;
+ int rc;
+ rc = sqlite3VdbeMemMakeWriteable(pMem);
+ if( rc!=SQLITE_OK ){
+ assert( rc==SQLITE_NOMEM );
+ return SQLITE_NOMEM;
+ }
+ zIn = pMem->z;
+ zTerm = &zIn[pMem->n];
+ while( zIn<zTerm ){
+ temp = *zIn;
+ *zIn = *(zIn+1);
+ zIn++;
+ *zIn++ = temp;
+ }
+ pMem->enc = desiredEnc;
+ goto translate_out;
+ }
+
+ /* Set len to the maximum number of bytes required in the output buffer. */
+ if( desiredEnc==SQLITE_UTF8 ){
+ /* When converting from UTF-16, the maximum growth results from
+ ** translating a 2-byte character to a 3-byte UTF-8 character (i.e.
+ ** code-point 0xFFFC). A single byte is required for the output string
+ ** nul-terminator.
+ */
+ len = (pMem->n/2) * 3 + 1;
+ }else{
+ /* When converting from UTF-8 to UTF-16 the maximum growth is caused
+ ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16
+ ** character. Two bytes are required in the output buffer for the
+ ** nul-terminator.
+ */
+ len = pMem->n * 2 + 2;
+ }
+
+ /* Set zIn to point at the start of the input buffer and zTerm to point 1
+ ** byte past the end.
+ **
+ ** Variable zOut is set to point at the output buffer. This may be space
+ ** obtained from malloc(), or Mem.zShort, if it large enough and not in
+ ** use, or the zShort array on the stack (see above).
+ */
+ zIn = pMem->z;
+ zTerm = &zIn[pMem->n];
+ if( len>NBFS ){
+ zOut = sqliteMallocRaw(len);
+ if( !zOut ) return SQLITE_NOMEM;
+ }else{
+ zOut = zShort;
+ }
+ z = zOut;
+
+ if( pMem->enc==SQLITE_UTF8 ){
+ if( desiredEnc==SQLITE_UTF16LE ){
+ /* UTF-8 -> UTF-16 Little-endian */
+ while( zIn<zTerm ){
+ READ_UTF8(zIn, c);
+ WRITE_UTF16LE(z, c);
+ }
+ }else{
+ assert( desiredEnc==SQLITE_UTF16BE );
+ /* UTF-8 -> UTF-16 Big-endian */
+ while( zIn<zTerm ){
+ READ_UTF8(zIn, c);
+ WRITE_UTF16BE(z, c);
+ }
+ }
+ pMem->n = z - zOut;
+ *z++ = 0;
+ }else{
+ assert( desiredEnc==SQLITE_UTF8 );
+ if( pMem->enc==SQLITE_UTF16LE ){
+ /* UTF-16 Little-endian -> UTF-8 */
+ while( zIn<zTerm ){
+ READ_UTF16LE(zIn, c);
+ WRITE_UTF8(z, c);
+ }
+ }else{
+ /* UTF-16 Little-endian -> UTF-8 */
+ while( zIn<zTerm ){
+ READ_UTF16BE(zIn, c);
+ WRITE_UTF8(z, c);
+ }
+ }
+ pMem->n = z - zOut;
+ }
+ *z = 0;
+ assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
+
+ sqlite3VdbeMemRelease(pMem);
+ pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short);
+ pMem->enc = desiredEnc;
+ if( zOut==zShort ){
+ memcpy(pMem->zShort, zOut, len);
+ zOut = pMem->zShort;
+ pMem->flags |= (MEM_Term|MEM_Short);
+ }else{
+ pMem->flags |= (MEM_Term|MEM_Dyn);
+ }
+ pMem->z = zOut;
+
+translate_out:
+#ifdef TRANSLATE_TRACE
+ {
+ char zBuf[100];
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf, 100);
+ fprintf(stderr, "OUTPUT: %s\n", zBuf);
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** This routine checks for a byte-order mark at the beginning of the
+** UTF-16 string stored in *pMem. If one is present, it is removed and
+** the encoding of the Mem adjusted. This routine does not do any
+** byte-swapping, it just sets Mem.enc appropriately.
+**
+** The allocation (static, dynamic etc.) and encoding of the Mem may be
+** changed by this function.
+*/
+int sqlite3VdbeMemHandleBom(Mem *pMem){
+ int rc = SQLITE_OK;
+ u8 bom = 0;
+
+ if( pMem->n<0 || pMem->n>1 ){
+ u8 b1 = *(u8 *)pMem->z;
+ u8 b2 = *(((u8 *)pMem->z) + 1);
+ if( b1==0xFE && b2==0xFF ){
+ bom = SQLITE_UTF16BE;
+ }
+ if( b1==0xFF && b2==0xFE ){
+ bom = SQLITE_UTF16LE;
+ }
+ }
+
+ if( bom ){
+ /* This function is called as soon as a string is stored in a Mem*,
+ ** from within sqlite3VdbeMemSetStr(). At that point it is not possible
+ ** for the string to be stored in Mem.zShort, or for it to be stored
+ ** in dynamic memory with no destructor.
+ */
+ assert( !(pMem->flags&MEM_Short) );
+ assert( !(pMem->flags&MEM_Dyn) || pMem->xDel );
+ if( pMem->flags & MEM_Dyn ){
+ void (*xDel)(void*) = pMem->xDel;
+ char *z = pMem->z;
+ pMem->z = 0;
+ pMem->xDel = 0;
+ rc = sqlite3VdbeMemSetStr(pMem, &z[2], pMem->n-2, bom, SQLITE_TRANSIENT);
+ xDel(z);
+ }else{
+ rc = sqlite3VdbeMemSetStr(pMem, &pMem->z[2], pMem->n-2, bom,
+ SQLITE_TRANSIENT);
+ }
+ }
+ return rc;
+}
+
+/*
+** pZ is a UTF-8 encoded unicode string. If nByte is less than zero,
+** return the number of unicode characters in pZ up to (but not including)
+** the first 0x00 byte. If nByte is not less than zero, return the
+** number of unicode characters in the first nByte of pZ (or up to
+** the first 0x00, whichever comes first).
+*/
+int sqlite3utf8CharLen(const char *z, int nByte){
+ int r = 0;
+ const char *zTerm;
+ if( nByte>=0 ){
+ zTerm = &z[nByte];
+ }else{
+ zTerm = (const char *)(-1);
+ }
+ assert( z<=zTerm );
+ while( *z!=0 && z<zTerm ){
+ SKIP_UTF8(z);
+ r++;
+ }
+ return r;
+}
+
+/*
+** pZ is a UTF-16 encoded unicode string. If nChar is less than zero,
+** return the number of bytes up to (but not including), the first pair
+** of consecutive 0x00 bytes in pZ. If nChar is not less than zero,
+** then return the number of bytes in the first nChar unicode characters
+** in pZ (or up until the first pair of 0x00 bytes, whichever comes first).
+*/
+int sqlite3utf16ByteLen(const void *zIn, int nChar){
+ int c = 1;
+ char const *z = zIn;
+ int n = 0;
+ if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){
+ while( c && ((nChar<0) || n<nChar) ){
+ READ_UTF16BE(z, c);
+ n++;
+ }
+ }else{
+ while( c && ((nChar<0) || n<nChar) ){
+ READ_UTF16LE(z, c);
+ n++;
+ }
+ }
+ return (z-(char const *)zIn)-((c==0)?2:0);
+}
+
+/*
+** UTF-16 implementation of the substr()
+*/
+void sqlite3utf16Substr(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int y, z;
+ unsigned char const *zStr;
+ unsigned char const *zStrEnd;
+ unsigned char const *zStart;
+ unsigned char const *zEnd;
+ int i;
+
+ zStr = (unsigned char const *)sqlite3_value_text16(argv[0]);
+ zStrEnd = &zStr[sqlite3_value_bytes16(argv[0])];
+ y = sqlite3_value_int(argv[1]);
+ z = sqlite3_value_int(argv[2]);
+
+ if( y>0 ){
+ y = y-1;
+ zStart = zStr;
+ if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){
+ for(i=0; i<y && zStart<zStrEnd; i++) SKIP_UTF16BE(zStart);
+ }else{
+ for(i=0; i<y && zStart<zStrEnd; i++) SKIP_UTF16LE(zStart);
+ }
+ }else{
+ zStart = zStrEnd;
+ if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){
+ for(i=y; i<0 && zStart>zStr; i++) RSKIP_UTF16BE(zStart);
+ }else{
+ for(i=y; i<0 && zStart>zStr; i++) RSKIP_UTF16LE(zStart);
+ }
+ for(; i<0; i++) z -= 1;
+ }
+
+ zEnd = zStart;
+ if( SQLITE_UTF16BE==SQLITE_UTF16NATIVE ){
+ for(i=0; i<z && zEnd<zStrEnd; i++) SKIP_UTF16BE(zEnd);
+ }else{
+ for(i=0; i<z && zEnd<zStrEnd; i++) SKIP_UTF16LE(zEnd);
+ }
+
+ sqlite3_result_text16(context, zStart, zEnd-zStart, SQLITE_TRANSIENT);
+}
+
+#if defined(SQLITE_TEST)
+/*
+** This routine is called from the TCL test function "translate_selftest".
+** It checks that the primitives for serializing and deserializing
+** characters in each encoding are inverses of each other.
+*/
+void sqlite3utfSelfTest(){
+ int i;
+ unsigned char zBuf[20];
+ unsigned char *z;
+ int n;
+ int c;
+
+ for(i=0; i<0x00110000; i++){
+ z = zBuf;
+ WRITE_UTF8(z, i);
+ n = z-zBuf;
+ z = zBuf;
+ READ_UTF8(z, c);
+ assert( c==i );
+ assert( (z-zBuf)==n );
+ }
+ for(i=0; i<0x00110000; i++){
+ if( i>=0xD800 && i<=0xE000 ) continue;
+ z = zBuf;
+ WRITE_UTF16LE(z, i);
+ n = z-zBuf;
+ z = zBuf;
+ READ_UTF16LE(z, c);
+ assert( c==i );
+ assert( (z-zBuf)==n );
+ }
+ for(i=0; i<0x00110000; i++){
+ if( i>=0xD800 && i<=0xE000 ) continue;
+ z = zBuf;
+ WRITE_UTF16BE(z, i);
+ n = z-zBuf;
+ z = zBuf;
+ READ_UTF16BE(z, c);
+ assert( c==i );
+ assert( (z-zBuf)==n );
+ }
+}
+#endif
diff --git a/kopete/plugins/statistics/sqlite/util.c b/kopete/plugins/statistics/sqlite/util.c
new file mode 100644
index 00000000..74ec8979
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/util.c
@@ -0,0 +1,962 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Utility functions used throughout sqlite.
+**
+** This file contains functions for allocating memory, comparing
+** strings, and stuff like that.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+#if SQLITE_DEBUG>2 && defined(__GLIBC__)
+#include <execinfo.h>
+void print_stack_trace(){
+ void *bt[30];
+ int i;
+ int n = backtrace(bt, 30);
+
+ sqlite3DebugPrintf("STACK: ");
+ for(i=0; i<n;i++){
+ sqlite3DebugPrintf("%p ", bt[i]);
+ }
+ sqlite3DebugPrintf("\n");
+}
+#else
+#define print_stack_trace()
+#endif
+
+/*
+** If malloc() ever fails, this global variable gets set to 1.
+** This causes the library to abort and never again function.
+*/
+int sqlite3_malloc_failed = 0;
+
+/*
+** If SQLITE_DEBUG is defined, then use versions of malloc() and
+** free() that track memory usage and check for buffer overruns.
+*/
+#ifdef SQLITE_DEBUG
+
+/*
+** For keeping track of the number of mallocs and frees. This
+** is used to check for memory leaks.
+*/
+int sqlite3_nMalloc; /* Number of sqliteMalloc() calls */
+int sqlite3_nFree; /* Number of sqliteFree() calls */
+int sqlite3_iMallocFail; /* Fail sqliteMalloc() after this many calls */
+#if SQLITE_DEBUG>1
+static int memcnt = 0;
+#endif
+
+/*
+** Number of 32-bit guard words
+*/
+#define N_GUARD 1
+
+/*
+** Allocate new memory and set it to zero. Return NULL if
+** no memory is available.
+*/
+void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){
+ void *p;
+ int *pi;
+ int i, k;
+ if( sqlite3_iMallocFail>=0 ){
+ sqlite3_iMallocFail--;
+ if( sqlite3_iMallocFail==0 ){
+ sqlite3_malloc_failed++;
+#if SQLITE_DEBUG>1
+ fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n",
+ n, zFile,line);
+#endif
+ sqlite3_iMallocFail--;
+ return 0;
+ }
+ }
+ if( n==0 ) return 0;
+ k = (n+sizeof(int)-1)/sizeof(int);
+ pi = malloc( (N_GUARD*2+1+k)*sizeof(int));
+ if( pi==0 ){
+ sqlite3_malloc_failed++;
+ return 0;
+ }
+ sqlite3_nMalloc++;
+ for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
+ pi[N_GUARD] = n;
+ for(i=0; i<N_GUARD; i++) pi[k+1+N_GUARD+i] = 0xdead3344;
+ p = &pi[N_GUARD+1];
+ memset(p, bZero==0, n);
+#if SQLITE_DEBUG>1
+ print_stack_trace();
+ fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n",
+ ++memcnt, n, (int)p, zFile,line);
+#endif
+ return p;
+}
+
+/*
+** Check to see if the given pointer was obtained from sqliteMalloc()
+** and is able to hold at least N bytes. Raise an exception if this
+** is not the case.
+**
+** This routine is used for testing purposes only.
+*/
+void sqlite3CheckMemory(void *p, int N){
+ int *pi = p;
+ int n, i, k;
+ pi -= N_GUARD+1;
+ for(i=0; i<N_GUARD; i++){
+ assert( pi[i]==0xdead1122 );
+ }
+ n = pi[N_GUARD];
+ assert( N>=0 && N<n );
+ k = (n+sizeof(int)-1)/sizeof(int);
+ for(i=0; i<N_GUARD; i++){
+ assert( pi[k+N_GUARD+1+i]==0xdead3344 );
+ }
+}
+
+/*
+** Free memory previously obtained from sqliteMalloc()
+*/
+void sqlite3Free_(void *p, char *zFile, int line){
+ if( p ){
+ int *pi, i, k, n;
+ pi = p;
+ pi -= N_GUARD+1;
+ sqlite3_nFree++;
+ for(i=0; i<N_GUARD; i++){
+ if( pi[i]!=0xdead1122 ){
+ fprintf(stderr,"Low-end memory corruption at 0x%x\n", (int)p);
+ return;
+ }
+ }
+ n = pi[N_GUARD];
+ k = (n+sizeof(int)-1)/sizeof(int);
+ for(i=0; i<N_GUARD; i++){
+ if( pi[k+N_GUARD+1+i]!=0xdead3344 ){
+ fprintf(stderr,"High-end memory corruption at 0x%x\n", (int)p);
+ return;
+ }
+ }
+ memset(pi, 0xff, (k+N_GUARD*2+1)*sizeof(int));
+#if SQLITE_DEBUG>1
+ fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n",
+ ++memcnt, n, (int)p, zFile,line);
+#endif
+ free(pi);
+ }
+}
+
+/*
+** Resize a prior allocation. If p==0, then this routine
+** works just like sqliteMalloc(). If n==0, then this routine
+** works just like sqliteFree().
+*/
+void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){
+ int *oldPi, *pi, i, k, oldN, oldK;
+ void *p;
+ if( oldP==0 ){
+ return sqlite3Malloc_(n,1,zFile,line);
+ }
+ if( n==0 ){
+ sqlite3Free_(oldP,zFile,line);
+ return 0;
+ }
+ oldPi = oldP;
+ oldPi -= N_GUARD+1;
+ if( oldPi[0]!=0xdead1122 ){
+ fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP);
+ return 0;
+ }
+ oldN = oldPi[N_GUARD];
+ oldK = (oldN+sizeof(int)-1)/sizeof(int);
+ for(i=0; i<N_GUARD; i++){
+ if( oldPi[oldK+N_GUARD+1+i]!=0xdead3344 ){
+ fprintf(stderr,"High-end memory corruption in realloc at 0x%x\n",
+ (int)oldP);
+ return 0;
+ }
+ }
+ k = (n + sizeof(int) - 1)/sizeof(int);
+ pi = malloc( (k+N_GUARD*2+1)*sizeof(int) );
+ if( pi==0 ){
+ sqlite3_malloc_failed++;
+ return 0;
+ }
+ for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
+ pi[N_GUARD] = n;
+ for(i=0; i<N_GUARD; i++) pi[k+N_GUARD+1+i] = 0xdead3344;
+ p = &pi[N_GUARD+1];
+ memcpy(p, oldP, n>oldN ? oldN : n);
+ if( n>oldN ){
+ memset(&((char*)p)[oldN], 0x55, n-oldN);
+ }
+ memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int));
+ free(oldPi);
+#if SQLITE_DEBUG>1
+ print_stack_trace();
+ fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n",
+ ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line);
+#endif
+ return p;
+}
+
+/*
+** Make a copy of a string in memory obtained from sqliteMalloc()
+*/
+char *sqlite3StrDup_(const char *z, char *zFile, int line){
+ char *zNew;
+ if( z==0 ) return 0;
+ zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line);
+ if( zNew ) strcpy(zNew, z);
+ return zNew;
+}
+char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){
+ char *zNew;
+ if( z==0 ) return 0;
+ zNew = sqlite3Malloc_(n+1, 0, zFile, line);
+ if( zNew ){
+ memcpy(zNew, z, n);
+ zNew[n] = 0;
+ }
+ return zNew;
+}
+
+/*
+** A version of sqliteFree that is always a function, not a macro.
+*/
+void sqlite3FreeX(void *p){
+ sqliteFree(p);
+}
+#endif /* SQLITE_DEBUG */
+
+/*
+** The following versions of malloc() and free() are for use in a
+** normal build.
+*/
+#if !defined(SQLITE_DEBUG)
+
+/*
+** Allocate new memory and set it to zero. Return NULL if
+** no memory is available. See also sqliteMallocRaw().
+*/
+void *sqlite3Malloc(int n){
+ void *p;
+ if( (p = malloc(n))==0 ){
+ if( n>0 ) sqlite3_malloc_failed++;
+ }else{
+ memset(p, 0, n);
+ }
+ return p;
+}
+
+/*
+** Allocate new memory but do not set it to zero. Return NULL if
+** no memory is available. See also sqliteMalloc().
+*/
+void *sqlite3MallocRaw(int n){
+ void *p;
+ if( (p = malloc(n))==0 ){
+ if( n>0 ) sqlite3_malloc_failed++;
+ }
+ return p;
+}
+
+/*
+** Free memory previously obtained from sqliteMalloc()
+*/
+void sqlite3FreeX(void *p){
+ if( p ){
+ free(p);
+ }
+}
+
+/*
+** Resize a prior allocation. If p==0, then this routine
+** works just like sqliteMalloc(). If n==0, then this routine
+** works just like sqliteFree().
+*/
+void *sqlite3Realloc(void *p, int n){
+ void *p2;
+ if( p==0 ){
+ return sqliteMalloc(n);
+ }
+ if( n==0 ){
+ sqliteFree(p);
+ return 0;
+ }
+ p2 = realloc(p, n);
+ if( p2==0 ){
+ sqlite3_malloc_failed++;
+ }
+ return p2;
+}
+
+/*
+** Make a copy of a string in memory obtained from sqliteMalloc()
+*/
+char *sqlite3StrDup(const char *z){
+ char *zNew;
+ if( z==0 ) return 0;
+ zNew = sqliteMallocRaw(strlen(z)+1);
+ if( zNew ) strcpy(zNew, z);
+ return zNew;
+}
+char *sqlite3StrNDup(const char *z, int n){
+ char *zNew;
+ if( z==0 ) return 0;
+ zNew = sqliteMallocRaw(n+1);
+ if( zNew ){
+ memcpy(zNew, z, n);
+ zNew[n] = 0;
+ }
+ return zNew;
+}
+#endif /* !defined(SQLITE_DEBUG) */
+
+/*
+** Create a string from the 2nd and subsequent arguments (up to the
+** first NULL argument), store the string in memory obtained from
+** sqliteMalloc() and make the pointer indicated by the 1st argument
+** point to that string. The 1st argument must either be NULL or
+** point to memory obtained from sqliteMalloc().
+*/
+void sqlite3SetString(char **pz, const char *zFirst, ...){
+ va_list ap;
+ int nByte;
+ const char *z;
+ char *zResult;
+
+ if( pz==0 ) return;
+ nByte = strlen(zFirst) + 1;
+ va_start(ap, zFirst);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ nByte += strlen(z);
+ }
+ va_end(ap);
+ sqliteFree(*pz);
+ *pz = zResult = sqliteMallocRaw( nByte );
+ if( zResult==0 ){
+ return;
+ }
+ strcpy(zResult, zFirst);
+ zResult += strlen(zResult);
+ va_start(ap, zFirst);
+ while( (z = va_arg(ap, const char*))!=0 ){
+ strcpy(zResult, z);
+ zResult += strlen(zResult);
+ }
+ va_end(ap);
+#ifdef SQLITE_DEBUG
+#if SQLITE_DEBUG>1
+ fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz);
+#endif
+#endif
+}
+
+/*
+** Set the most recent error code and error string for the sqlite
+** handle "db". The error code is set to "err_code".
+**
+** If it is not NULL, string zFormat specifies the format of the
+** error string in the style of the printf functions: The following
+** format characters are allowed:
+**
+** %s Insert a string
+** %z A string that should be freed after use
+** %d Insert an integer
+** %T Insert a token
+** %S Insert the first element of a SrcList
+**
+** zFormat and any string tokens that follow it are assumed to be
+** encoded in UTF-8.
+**
+** To clear the most recent error for slqite handle "db", sqlite3Error
+** should be called with err_code set to SQLITE_OK and zFormat set
+** to NULL.
+*/
+void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
+ if( db && (db->pErr || (db->pErr = sqlite3ValueNew())) ){
+ db->errCode = err_code;
+ if( zFormat ){
+ char *z;
+ va_list ap;
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(zFormat, ap);
+ va_end(ap);
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, sqlite3FreeX);
+ }else{
+ sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
+ }
+ }
+}
+
+/*
+** Add an error message to pParse->zErrMsg and increment pParse->nErr.
+** The following formatting characters are allowed:
+**
+** %s Insert a string
+** %z A string that should be freed after use
+** %d Insert an integer
+** %T Insert a token
+** %S Insert the first element of a SrcList
+**
+** This function should be used to report any error that occurs whilst
+** compiling an SQL statement (i.e. within sqlite3_prepare()). The
+** last thing the sqlite3_prepare() function does is copy the error
+** stored by this function into the database handle using sqlite3Error().
+** Function sqlite3Error() should be used during statement execution
+** (sqlite3_step() etc.).
+*/
+void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
+ va_list ap;
+ pParse->nErr++;
+ sqliteFree(pParse->zErrMsg);
+ va_start(ap, zFormat);
+ pParse->zErrMsg = sqlite3VMPrintf(zFormat, ap);
+ va_end(ap);
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** 2002-Feb-14: This routine is extended to remove MS-Access style
+** brackets from around identifers. For example: "[a-b-c]" becomes
+** "a-b-c".
+*/
+void sqlite3Dequote(char *z){
+ int quote;
+ int i, j;
+ if( z==0 ) return;
+ quote = z[0];
+ switch( quote ){
+ case '\'': break;
+ case '"': break;
+ case '[': quote = ']'; break;
+ default: return;
+ }
+ for(i=1, j=0; z[i]; i++){
+ if( z[i]==quote ){
+ if( z[i+1]==quote ){
+ z[j++] = quote;
+ i++;
+ }else{
+ z[j++] = 0;
+ break;
+ }
+ }else{
+ z[j++] = z[i];
+ }
+ }
+}
+
+/* An array to map all upper-case characters into their corresponding
+** lower-case character.
+*/
+const unsigned char sqlite3UpperToLower[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 97, 98, 99,100,101,102,103,
+ 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104,105,106,107,
+ 108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
+ 126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,
+ 162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,
+ 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,
+ 198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,
+ 216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,
+ 234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,
+ 252,253,254,255
+};
+#define UpperToLower sqlite3UpperToLower
+
+/*
+** This function computes a hash on the name of a keyword.
+** Case is not significant.
+*/
+int sqlite3HashNoCase(const char *z, int n){
+ int h = 0;
+ if( n<=0 ) n = strlen(z);
+ while( n > 0 ){
+ h = (h<<3) ^ h ^ UpperToLower[(unsigned char)*z++];
+ n--;
+ }
+ return h & 0x7fffffff;
+}
+
+/*
+** Some systems have stricmp(). Others have strcasecmp(). Because
+** there is no consistency, we will define our own.
+*/
+int sqlite3StrICmp(const char *zLeft, const char *zRight){
+ register unsigned char *a, *b;
+ a = (unsigned char *)zLeft;
+ b = (unsigned char *)zRight;
+ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
+ return UpperToLower[*a] - UpperToLower[*b];
+}
+int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){
+ register unsigned char *a, *b;
+ a = (unsigned char *)zLeft;
+ b = (unsigned char *)zRight;
+ while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
+ return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
+}
+
+/*
+** Return TRUE if z is a pure numeric string. Return FALSE if the
+** string contains any character which is not part of a number. If
+** the string is numeric and contains the '.' character, set *realnum
+** to TRUE (otherwise FALSE).
+**
+** An empty string is considered non-numeric.
+*/
+int sqlite3IsNumber(const char *z, int *realnum, u8 enc){
+ int incr = (enc==SQLITE_UTF8?1:2);
+ if( enc==SQLITE_UTF16BE ) z++;
+ if( *z=='-' || *z=='+' ) z += incr;
+ if( !isdigit(*(u8*)z) ){
+ return 0;
+ }
+ z += incr;
+ if( realnum ) *realnum = 0;
+ while( isdigit(*(u8*)z) ){ z += incr; }
+ if( *z=='.' ){
+ z += incr;
+ if( !isdigit(*(u8*)z) ) return 0;
+ while( isdigit(*(u8*)z) ){ z += incr; }
+ if( realnum ) *realnum = 1;
+ }
+ if( *z=='e' || *z=='E' ){
+ z += incr;
+ if( *z=='+' || *z=='-' ) z += incr;
+ if( !isdigit(*(u8*)z) ) return 0;
+ while( isdigit(*(u8*)z) ){ z += incr; }
+ if( realnum ) *realnum = 1;
+ }
+ return *z==0;
+}
+
+/*
+** The string z[] is an ascii representation of a real number.
+** Convert this string to a double.
+**
+** This routine assumes that z[] really is a valid number. If it
+** is not, the result is undefined.
+**
+** This routine is used instead of the library atof() function because
+** the library atof() might want to use "," as the decimal point instead
+** of "." depending on how locale is set. But that would cause problems
+** for SQL. So this routine always uses "." regardless of locale.
+*/
+double sqlite3AtoF(const char *z, const char **pzEnd){
+ int sign = 1;
+ LONGDOUBLE_TYPE v1 = 0.0;
+ if( *z=='-' ){
+ sign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+ while( isdigit(*(u8*)z) ){
+ v1 = v1*10.0 + (*z - '0');
+ z++;
+ }
+ if( *z=='.' ){
+ LONGDOUBLE_TYPE divisor = 1.0;
+ z++;
+ while( isdigit(*(u8*)z) ){
+ v1 = v1*10.0 + (*z - '0');
+ divisor *= 10.0;
+ z++;
+ }
+ v1 /= divisor;
+ }
+ if( *z=='e' || *z=='E' ){
+ int esign = 1;
+ int eval = 0;
+ LONGDOUBLE_TYPE scale = 1.0;
+ z++;
+ if( *z=='-' ){
+ esign = -1;
+ z++;
+ }else if( *z=='+' ){
+ z++;
+ }
+ while( isdigit(*(u8*)z) ){
+ eval = eval*10 + *z - '0';
+ z++;
+ }
+ while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; }
+ while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; }
+ while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; }
+ while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; }
+ if( esign<0 ){
+ v1 /= scale;
+ }else{
+ v1 *= scale;
+ }
+ }
+ if( pzEnd ) *pzEnd = z;
+ return sign<0 ? -v1 : v1;
+}
+
+/*
+** Return TRUE if zNum is a 64-bit signed integer and write
+** the value of the integer into *pNum. If zNum is not an integer
+** or is an integer that is too large to be expressed with 64 bits,
+** then return false. If n>0 and the integer is string is not
+** exactly n bytes long, return false.
+**
+** When this routine was originally written it dealt with only
+** 32-bit numbers. At that time, it was much faster than the
+** atoi() library routine in RedHat 7.2.
+*/
+int sqlite3atoi64(const char *zNum, i64 *pNum){
+ i64 v = 0;
+ int neg;
+ int i, c;
+ if( *zNum=='-' ){
+ neg = 1;
+ zNum++;
+ }else if( *zNum=='+' ){
+ neg = 0;
+ zNum++;
+ }else{
+ neg = 0;
+ }
+ for(i=0; (c=zNum[i])>='0' && c<='9'; i++){
+ v = v*10 + c - '0';
+ }
+ *pNum = neg ? -v : v;
+ return c==0 && i>0 &&
+ (i<19 || (i==19 && memcmp(zNum,"9223372036854775807",19)<=0));
+}
+
+/*
+** The string zNum represents an integer. There might be some other
+** information following the integer too, but that part is ignored.
+** If the integer that the prefix of zNum represents will fit in a
+** 32-bit signed integer, return TRUE. Otherwise return FALSE.
+**
+** This routine returns FALSE for the string -2147483648 even that
+** that number will in fact fit in a 32-bit integer. But positive
+** 2147483648 will not fit in 32 bits. So it seems safer to return
+** false.
+*/
+static int sqlite3FitsIn32Bits(const char *zNum){
+ int i, c;
+ if( *zNum=='-' || *zNum=='+' ) zNum++;
+ for(i=0; (c=zNum[i])>='0' && c<='9'; i++){}
+ return i<10 || (i==10 && memcmp(zNum,"2147483647",10)<=0);
+}
+
+/*
+** If zNum represents an integer that will fit in 32-bits, then set
+** *pValue to that integer and return true. Otherwise return false.
+*/
+int sqlite3GetInt32(const char *zNum, int *pValue){
+ if( sqlite3FitsIn32Bits(zNum) ){
+ *pValue = atoi(zNum);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** The string zNum represents an integer. There might be some other
+** information following the integer too, but that part is ignored.
+** If the integer that the prefix of zNum represents will fit in a
+** 64-bit signed integer, return TRUE. Otherwise return FALSE.
+**
+** This routine returns FALSE for the string -9223372036854775808 even that
+** that number will, in theory fit in a 64-bit integer. Positive
+** 9223373036854775808 will not fit in 64 bits. So it seems safer to return
+** false.
+*/
+int sqlite3FitsIn64Bits(const char *zNum){
+ int i, c;
+ if( *zNum=='-' || *zNum=='+' ) zNum++;
+ for(i=0; (c=zNum[i])>='0' && c<='9'; i++){}
+ return i<19 || (i==19 && memcmp(zNum,"9223372036854775807",19)<=0);
+}
+
+
+/*
+** Change the sqlite.magic from SQLITE_MAGIC_OPEN to SQLITE_MAGIC_BUSY.
+** Return an error (non-zero) if the magic was not SQLITE_MAGIC_OPEN
+** when this routine is called.
+**
+** This routine is a attempt to detect if two threads use the
+** same sqlite* pointer at the same time. There is a race
+** condition so it is possible that the error is not detected.
+** But usually the problem will be seen. The result will be an
+** error which can be used to debug the application that is
+** using SQLite incorrectly.
+**
+** Ticket #202: If db->magic is not a valid open value, take care not
+** to modify the db structure at all. It could be that db is a stale
+** pointer. In other words, it could be that there has been a prior
+** call to sqlite3_close(db) and db has been deallocated. And we do
+** not want to write into deallocated memory.
+*/
+int sqlite3SafetyOn(sqlite3 *db){
+ if( db->magic==SQLITE_MAGIC_OPEN ){
+ db->magic = SQLITE_MAGIC_BUSY;
+ return 0;
+ }else if( db->magic==SQLITE_MAGIC_BUSY || db->magic==SQLITE_MAGIC_ERROR ){
+ db->magic = SQLITE_MAGIC_ERROR;
+ db->flags |= SQLITE_Interrupt;
+ }
+ return 1;
+}
+
+/*
+** Change the magic from SQLITE_MAGIC_BUSY to SQLITE_MAGIC_OPEN.
+** Return an error (non-zero) if the magic was not SQLITE_MAGIC_BUSY
+** when this routine is called.
+*/
+int sqlite3SafetyOff(sqlite3 *db){
+ if( db->magic==SQLITE_MAGIC_BUSY ){
+ db->magic = SQLITE_MAGIC_OPEN;
+ return 0;
+ }else if( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ERROR ){
+ db->magic = SQLITE_MAGIC_ERROR;
+ db->flags |= SQLITE_Interrupt;
+ }
+ return 1;
+}
+
+/*
+** Check to make sure we have a valid db pointer. This test is not
+** foolproof but it does provide some measure of protection against
+** misuse of the interface such as passing in db pointers that are
+** NULL or which have been previously closed. If this routine returns
+** TRUE it means that the db pointer is invalid and should not be
+** dereferenced for any reason. The calling function should invoke
+** SQLITE_MISUSE immediately.
+*/
+int sqlite3SafetyCheck(sqlite3 *db){
+ int magic;
+ if( db==0 ) return 1;
+ magic = db->magic;
+ if( magic!=SQLITE_MAGIC_CLOSED &&
+ magic!=SQLITE_MAGIC_OPEN &&
+ magic!=SQLITE_MAGIC_BUSY ) return 1;
+ return 0;
+}
+
+/*
+** The variable-length integer encoding is as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+** C = xxxxxxxx 8 bits of data
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** 28 bits - BBBA
+** 35 bits - BBBBA
+** 42 bits - BBBBBA
+** 49 bits - BBBBBBA
+** 56 bits - BBBBBBBA
+** 64 bits - BBBBBBBBC
+*/
+
+/*
+** Write a 64-bit variable-length integer to memory starting at p[0].
+** The length of data write will be between 1 and 9 bytes. The number
+** of bytes written is returned.
+**
+** A variable-length integer consists of the lower 7 bits of each byte
+** for all bytes that have the 8th bit set and one byte with the 8th
+** bit clear. Except, if we get to the 9th byte, it stores the full
+** 8 bits and is the last byte.
+*/
+int sqlite3PutVarint(unsigned char *p, u64 v){
+ int i, j, n;
+ u8 buf[10];
+ if( v & 0xff00000000000000 ){
+ p[8] = v;
+ v >>= 8;
+ for(i=7; i>=0; i--){
+ p[i] = (v & 0x7f) | 0x80;
+ v >>= 7;
+ }
+ return 9;
+ }
+ n = 0;
+ do{
+ buf[n++] = (v & 0x7f) | 0x80;
+ v >>= 7;
+ }while( v!=0 );
+ buf[0] &= 0x7f;
+ assert( n<=9 );
+ for(i=0, j=n-1; j>=0; j--, i++){
+ p[i] = buf[j];
+ }
+ return n;
+}
+
+/*
+** Read a 64-bit variable-length integer from memory starting at p[0].
+** Return the number of bytes read. The value is stored in *v.
+*/
+int sqlite3GetVarint(const unsigned char *p, u64 *v){
+ u32 x;
+ u64 x64;
+ int n;
+ unsigned char c;
+ if( ((c = p[0]) & 0x80)==0 ){
+ *v = c;
+ return 1;
+ }
+ x = c & 0x7f;
+ if( ((c = p[1]) & 0x80)==0 ){
+ *v = (x<<7) | c;
+ return 2;
+ }
+ x = (x<<7) | (c&0x7f);
+ if( ((c = p[2]) & 0x80)==0 ){
+ *v = (x<<7) | c;
+ return 3;
+ }
+ x = (x<<7) | (c&0x7f);
+ if( ((c = p[3]) & 0x80)==0 ){
+ *v = (x<<7) | c;
+ return 4;
+ }
+ x64 = (x<<7) | (c&0x7f);
+ n = 4;
+ do{
+ c = p[n++];
+ if( n==9 ){
+ x64 = (x64<<8) | c;
+ break;
+ }
+ x64 = (x64<<7) | (c&0x7f);
+ }while( (c & 0x80)!=0 );
+ *v = x64;
+ return n;
+}
+
+/*
+** Read a 32-bit variable-length integer from memory starting at p[0].
+** Return the number of bytes read. The value is stored in *v.
+*/
+int sqlite3GetVarint32(const unsigned char *p, u32 *v){
+ u32 x;
+ int n;
+ unsigned char c;
+ if( ((c = p[0]) & 0x80)==0 ){
+ *v = c;
+ return 1;
+ }
+ x = c & 0x7f;
+ if( ((c = p[1]) & 0x80)==0 ){
+ *v = (x<<7) | c;
+ return 2;
+ }
+ x = (x<<7) | (c & 0x7f);
+ n = 2;
+ do{
+ x = (x<<7) | ((c = p[n++])&0x7f);
+ }while( (c & 0x80)!=0 && n<9 );
+ *v = x;
+ return n;
+}
+
+/*
+** Return the number of bytes that will be needed to store the given
+** 64-bit integer.
+*/
+int sqlite3VarintLen(u64 v){
+ int i = 0;
+ do{
+ i++;
+ v >>= 7;
+ }while( v!=0 && i<9 );
+ return i;
+}
+
+/*
+** Translate a single byte of Hex into an integer.
+*/
+static int hexToInt(int h){
+ if( h>='0' && h<='9' ){
+ return h - '0';
+ }else if( h>='a' && h<='f' ){
+ return h - 'a' + 10;
+ }else if( h>='A' && h<='F' ){
+ return h - 'A' + 10;
+ }else{
+ return 0;
+ }
+}
+
+/*
+** Convert a BLOB literal of the form "x'hhhhhh'" into its binary
+** value. Return a pointer to its binary value. Space to hold the
+** binary value has been obtained from malloc and must be freed by
+** the calling routine.
+*/
+void *sqlite3HexToBlob(const char *z){
+ char *zBlob;
+ int i;
+ int n = strlen(z);
+ if( n%2 ) return 0;
+
+ zBlob = (char *)sqliteMalloc(n/2);
+ for(i=0; i<n; i+=2){
+ zBlob[i/2] = (hexToInt(z[i])<<4) | hexToInt(z[i+1]);
+ }
+ return zBlob;
+}
+
+#if defined(SQLITE_TEST)
+/*
+** Convert text generated by the "%p" conversion format back into
+** a pointer.
+*/
+void *sqlite3TextToPtr(const char *z){
+ void *p;
+ u64 v;
+ u32 v2;
+ if( z[0]=='0' && z[1]=='x' ){
+ z += 2;
+ }
+ v = 0;
+ while( *z ){
+ v = (v<<4) + hexToInt(*z);
+ z++;
+ }
+ if( sizeof(p)==sizeof(v) ){
+ p = *(void**)&v;
+ }else{
+ assert( sizeof(p)==sizeof(v2) );
+ v2 = (u32)v;
+ p = *(void**)&v2;
+ }
+ return p;
+}
+#endif
diff --git a/kopete/plugins/statistics/sqlite/vacuum.c b/kopete/plugins/statistics/sqlite/vacuum.c
new file mode 100644
index 00000000..371a8557
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vacuum.c
@@ -0,0 +1,262 @@
+/*
+** 2003 April 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used to implement the VACUUM command.
+**
+** Most of the code in this file may be omitted by defining the
+** SQLITE_OMIT_VACUUM macro.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+
+#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
+/*
+** Generate a random name of 20 character in length.
+*/
+static void randomName(unsigned char *zBuf){
+ static const unsigned char zChars[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789";
+ int i;
+ sqlite3Randomness(20, zBuf);
+ for(i=0; i<20; i++){
+ zBuf[i] = zChars[ zBuf[i]%(sizeof(zChars)-1) ];
+ }
+}
+
+/*
+** Execute zSql on database db. Return an error code.
+*/
+static int execSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
+ return sqlite3_errcode(db);
+ }
+ while( SQLITE_ROW==sqlite3_step(pStmt) );
+ return sqlite3_finalize(pStmt);
+}
+
+/*
+** Execute zSql on database db. The statement returns exactly
+** one column. Execute this as SQL on the same database.
+*/
+static int execExecSql(sqlite3 *db, const char *zSql){
+ sqlite3_stmt *pStmt;
+ int rc;
+
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ rc = execSql(db, sqlite3_column_text(pStmt, 0));
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pStmt);
+ return rc;
+ }
+ }
+
+ return sqlite3_finalize(pStmt);
+}
+
+#endif
+
+/*
+** The non-standard VACUUM command is used to clean up the database,
+** collapse free space, etc. It is modelled after the VACUUM command
+** in PostgreSQL.
+**
+** In version 1.0.x of SQLite, the VACUUM command would call
+** gdbm_reorganize() on all the database tables. But beginning
+** with 2.0.0, SQLite no longer uses GDBM so this command has
+** become a no-op.
+*/
+void sqlite3Vacuum(Parse *pParse, Token *pTableName){
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ if( v ){
+ sqlite3VdbeAddOp(v, OP_Vacuum, 0, 0);
+ }
+ return;
+}
+
+/*
+** This routine implements the OP_Vacuum opcode of the VDBE.
+*/
+int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
+ int rc = SQLITE_OK; /* Return code from service routines */
+#if !defined(SQLITE_OMIT_VACUUM) || SQLITE_OMIT_VACUUM
+ const char *zFilename; /* full pathname of the database file */
+ int nFilename; /* number of characters in zFilename[] */
+ char *zTemp = 0; /* a temporary file in same directory as zFilename */
+ int i; /* Loop counter */
+ Btree *pMain; /* The database being vacuumed */
+ Btree *pTemp;
+ char *zSql = 0;
+
+ if( !db->autoCommit ){
+ sqlite3SetString(pzErrMsg, "cannot VACUUM from within a transaction",
+ (char*)0);
+ rc = SQLITE_ERROR;
+ goto end_of_vacuum;
+ }
+
+ /* Get the full pathname of the database file and create a
+ ** temporary filename in the same directory as the original file.
+ */
+ pMain = db->aDb[0].pBt;
+ zFilename = sqlite3BtreeGetFilename(pMain);
+ assert( zFilename );
+ if( zFilename[0]=='\0' ){
+ /* The in-memory database. Do nothing. Return directly to avoid causing
+ ** an error trying to DETACH the vacuum_db (which never got attached)
+ ** in the exit-handler.
+ */
+ return SQLITE_OK;
+ }
+ nFilename = strlen(zFilename);
+ zTemp = sqliteMalloc( nFilename+100 );
+ if( zTemp==0 ){
+ rc = SQLITE_NOMEM;
+ goto end_of_vacuum;
+ }
+ strcpy(zTemp, zFilename);
+ i = 0;
+ do {
+ zTemp[nFilename] = '-';
+ randomName((unsigned char*)&zTemp[nFilename+1]);
+ } while( i<10 && sqlite3OsFileExists(zTemp) );
+
+ /* Attach the temporary database as 'vacuum_db'. The synchronous pragma
+ ** can be set to 'off' for this file, as it is not recovered if a crash
+ ** occurs anyway. The integrity of the database is maintained by a
+ ** (possibly synchronous) transaction opened on the main database before
+ ** sqlite3BtreeCopyFile() is called.
+ **
+ ** An optimisation would be to use a non-journaled pager.
+ */
+ zSql = sqlite3MPrintf("ATTACH '%q' AS vacuum_db;", zTemp);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ goto end_of_vacuum;
+ }
+ rc = execSql(db, zSql);
+ sqliteFree(zSql);
+ zSql = 0;
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 );
+ pTemp = db->aDb[db->nDb-1].pBt;
+ sqlite3BtreeSetPageSize(pTemp, sqlite3BtreeGetPageSize(pMain),
+ sqlite3BtreeGetReserve(pMain));
+ assert( sqlite3BtreeGetPageSize(pTemp)==sqlite3BtreeGetPageSize(pMain) );
+ execSql(db, "PRAGMA vacuum_db.synchronous=OFF");
+
+ /* Begin a transaction */
+ rc = execSql(db, "BEGIN;");
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Query the schema of the main database. Create a mirror schema
+ ** in the temporary database.
+ */
+ rc = execExecSql(db,
+ "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14,100000000) "
+ " FROM sqlite_master WHERE type='table' "
+ "UNION ALL "
+ "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14,100000000) "
+ " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' "
+ "UNION ALL "
+ "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21,100000000) "
+ " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'"
+ "UNION ALL "
+ "SELECT 'CREATE VIEW vacuum_db.' || substr(sql,13,100000000) "
+ " FROM sqlite_master WHERE type='view'"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Loop through the tables in the main database. For each, do
+ ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy
+ ** the contents to the temporary database.
+ */
+ rc = execExecSql(db,
+ "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
+ "|| ' SELECT * FROM ' || quote(name) || ';'"
+ "FROM sqlite_master "
+ "WHERE type = 'table';"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Copy the triggers from the main database to the temporary database.
+ ** This was deferred before in case the triggers interfered with copying
+ ** the data. It's possible the indices should be deferred until this
+ ** point also.
+ */
+ rc = execExecSql(db,
+ "SELECT 'CREATE TRIGGER vacuum_db.' || substr(sql, 16, 1000000) "
+ "FROM sqlite_master WHERE type='trigger'"
+ );
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+
+ /* At this point, unless the main db was completely empty, there is now a
+ ** transaction open on the vacuum database, but not on the main database.
+ ** Open a btree level transaction on the main database. This allows a
+ ** call to sqlite3BtreeCopyFile(). The main database btree level
+ ** transaction is then committed, so the SQL level never knows it was
+ ** opened for writing. This way, the SQL transaction used to create the
+ ** temporary database never needs to be committed.
+ */
+ if( sqlite3BtreeIsInTrans(pTemp) ){
+ u32 meta;
+
+ assert( 0==sqlite3BtreeIsInTrans(pMain) );
+ rc = sqlite3BtreeBeginTrans(pMain, 1);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ /* Copy Btree meta values 3 and 4. These correspond to SQL layer meta
+ ** values 2 and 3, the default values of a couple of pragmas.
+ */
+ rc = sqlite3BtreeGetMeta(pMain, 3, &meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeUpdateMeta(pTemp, 3, meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeGetMeta(pMain, 4, &meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeUpdateMeta(pTemp, 4, meta);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
+ rc = sqlite3BtreeCopyFile(pMain, pTemp);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeCommit(pMain);
+ }
+
+end_of_vacuum:
+ /* Currently there is an SQL level transaction open on the vacuum
+ ** database. No locks are held on any other files (since the main file
+ ** was committed at the btree level). So it safe to end the transaction
+ ** by manually setting the autoCommit flag to true and detaching the
+ ** vacuum database. The vacuum_db journal file is deleted when the pager
+ ** is closed by the DETACH.
+ */
+ db->autoCommit = 1;
+ if( rc==SQLITE_OK ){
+ rc = execSql(db, "DETACH vacuum_db;");
+ }else{
+ execSql(db, "DETACH vacuum_db;");
+ }
+ if( zTemp ){
+ sqlite3OsDelete(zTemp);
+ sqliteFree(zTemp);
+ }
+ if( zSql ) sqliteFree( zSql );
+ sqlite3ResetInternalSchema(db, 0);
+#endif
+ return rc;
+}
diff --git a/kopete/plugins/statistics/sqlite/vdbe.c b/kopete/plugins/statistics/sqlite/vdbe.c
new file mode 100644
index 00000000..58f8c731
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbe.c
@@ -0,0 +1,4450 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** The code in this file implements execution method of the
+** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c")
+** handles housekeeping details such as creating and deleting
+** VDBE instances. This file is solely interested in executing
+** the VDBE program.
+**
+** In the external interface, an "sqlite3_stmt*" is an opaque pointer
+** to a VDBE.
+**
+** The SQL parser generates a program which is then executed by
+** the VDBE to do the work of the SQL statement. VDBE programs are
+** similar in form to assembly language. The program consists of
+** a linear sequence of operations. Each operation has an opcode
+** and 3 operands. Operands P1 and P2 are integers. Operand P3
+** is a null-terminated string. The P2 operand must be non-negative.
+** Opcodes will typically ignore one or more operands. Many opcodes
+** ignore all three operands.
+**
+** Computation results are stored on a stack. Each entry on the
+** stack is either an integer, a null-terminated string, a floating point
+** number, or the SQL "NULL" value. An inplicit conversion from one
+** type to the other occurs as necessary.
+**
+** Most of the code in this file is taken up by the sqlite3VdbeExec()
+** function which does the work of interpreting a VDBE program.
+** But other routines are also provided to help in building up
+** a program instruction by instruction.
+**
+** Various scripts scan this source file in order to generate HTML
+** documentation, headers files, or other derived files. The formatting
+** of the code in this file is, therefore, important. See other comments
+** in this file for details. If in doubt, do not deviate from existing
+** commenting and indentation practices when changing or adding code.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+#include "vdbeInt.h"
+
+/*
+** The following global variable is incremented every time a cursor
+** moves, either by the OP_MoveXX, OP_Next, or OP_Prev opcodes. The test
+** procedures use this information to make sure that indices are
+** working correctly. This variable has no function other than to
+** help verify the correct operation of the library.
+*/
+int sqlite3_search_count = 0;
+
+/*
+** When this global variable is positive, it gets decremented once before
+** each instruction in the VDBE. When reaches zero, the SQLITE_Interrupt
+** of the db.flags field is set in order to simulate and interrupt.
+**
+** This facility is used for testing purposes only. It does not function
+** in an ordinary build.
+*/
+int sqlite3_interrupt_count = 0;
+
+/*
+** Release the memory associated with the given stack level. This
+** leaves the Mem.flags field in an inconsistent state.
+*/
+#define Release(P) if((P)->flags&MEM_Dyn){ sqlite3VdbeMemRelease(P); }
+
+/*
+** Convert the given stack entity into a string if it isn't one
+** already. Return non-zero if a malloc() fails.
+*/
+#define Stringify(P, enc) \
+ if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc)) \
+ { goto no_mem; }
+
+/*
+** Convert the given stack entity into a string that has been obtained
+** from sqliteMalloc(). This is different from Stringify() above in that
+** Stringify() will use the NBFS bytes of static string space if the string
+** will fit but this routine always mallocs for space.
+** Return non-zero if we run out of memory.
+*/
+#define Dynamicify(P,enc) sqlite3VdbeMemDynamicify(P)
+
+
+/*
+** An ephemeral string value (signified by the MEM_Ephem flag) contains
+** a pointer to a dynamically allocated string where some other entity
+** is responsible for deallocating that string. Because the stack entry
+** does not control the string, it might be deleted without the stack
+** entry knowing it.
+**
+** This routine converts an ephemeral string into a dynamically allocated
+** string that the stack entry itself controls. In other words, it
+** converts an MEM_Ephem string into an MEM_Dyn string.
+*/
+#define Deephemeralize(P) \
+ if( ((P)->flags&MEM_Ephem)!=0 \
+ && sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
+
+/*
+** Convert the given stack entity into a integer if it isn't one
+** already.
+**
+** Any prior string or real representation is invalidated.
+** NULLs are converted into 0.
+*/
+#define Integerify(P) sqlite3VdbeMemIntegerify(P)
+
+/*
+** Convert P so that it has type MEM_Real.
+**
+** Any prior string or integer representation is invalidated.
+** NULLs are converted into 0.0.
+*/
+#define Realify(P) sqlite3VdbeMemRealify(P)
+
+/*
+** Argument pMem points at a memory cell that will be passed to a
+** user-defined function or returned to the user as the result of a query.
+** The second argument, 'db_enc' is the text encoding used by the vdbe for
+** stack variables. This routine sets the pMem->enc and pMem->type
+** variables used by the sqlite3_value_*() routines.
+*/
+#define storeTypeInfo(A,B) _storeTypeInfo(A)
+static void _storeTypeInfo(Mem *pMem){
+ int flags = pMem->flags;
+ if( flags & MEM_Null ){
+ pMem->type = SQLITE_NULL;
+ }
+ else if( flags & MEM_Int ){
+ pMem->type = SQLITE_INTEGER;
+ }
+ else if( flags & MEM_Real ){
+ pMem->type = SQLITE_FLOAT;
+ }
+ else if( flags & MEM_Str ){
+ pMem->type = SQLITE_TEXT;
+ }else{
+ pMem->type = SQLITE_BLOB;
+ }
+}
+
+/*
+** Insert a new aggregate element and make it the element that
+** has focus.
+**
+** Return 0 on success and 1 if memory is exhausted.
+*/
+static int AggInsert(Agg *p, char *zKey, int nKey){
+ AggElem *pElem;
+ int i;
+ int rc;
+ pElem = sqliteMalloc( sizeof(AggElem) + nKey +
+ (p->nMem-1)*sizeof(pElem->aMem[0]) );
+ if( pElem==0 ) return SQLITE_NOMEM;
+ pElem->zKey = (char*)&pElem->aMem[p->nMem];
+ memcpy(pElem->zKey, zKey, nKey);
+ pElem->nKey = nKey;
+
+ if( p->pCsr ){
+ rc = sqlite3BtreeInsert(p->pCsr, zKey, nKey, &pElem, sizeof(AggElem*));
+ if( rc!=SQLITE_OK ){
+ sqliteFree(pElem);
+ return rc;
+ }
+ }
+
+ for(i=0; i<p->nMem; i++){
+ pElem->aMem[i].flags = MEM_Null;
+ }
+ p->pCurrent = pElem;
+ return 0;
+}
+
+/*
+** Pop the stack N times.
+*/
+static void popStack(Mem **ppTos, int N){
+ Mem *pTos = *ppTos;
+ while( N>0 ){
+ N--;
+ Release(pTos);
+ pTos--;
+ }
+ *ppTos = pTos;
+}
+
+/*
+** The parameters are pointers to the head of two sorted lists
+** of Sorter structures. Merge these two lists together and return
+** a single sorted list. This routine forms the core of the merge-sort
+** algorithm.
+**
+** In the case of a tie, left sorts in front of right.
+*/
+static Sorter *Merge(Sorter *pLeft, Sorter *pRight, KeyInfo *pKeyInfo){
+ Sorter sHead;
+ Sorter *pTail;
+ pTail = &sHead;
+ pTail->pNext = 0;
+ while( pLeft && pRight ){
+ int c = sqlite3VdbeRecordCompare(pKeyInfo, pLeft->nKey, pLeft->zKey,
+ pRight->nKey, pRight->zKey);
+ if( c<=0 ){
+ pTail->pNext = pLeft;
+ pLeft = pLeft->pNext;
+ }else{
+ pTail->pNext = pRight;
+ pRight = pRight->pNext;
+ }
+ pTail = pTail->pNext;
+ }
+ if( pLeft ){
+ pTail->pNext = pLeft;
+ }else if( pRight ){
+ pTail->pNext = pRight;
+ }
+ return sHead.pNext;
+}
+
+/*
+** Allocate cursor number iCur. Return a pointer to it. Return NULL
+** if we run out of memory.
+*/
+static Cursor *allocateCursor(Vdbe *p, int iCur){
+ Cursor *pCx;
+ assert( iCur<p->nCursor );
+ if( p->apCsr[iCur] ){
+ sqlite3VdbeFreeCursor(p->apCsr[iCur]);
+ }
+ p->apCsr[iCur] = pCx = sqliteMalloc( sizeof(Cursor) );
+ return pCx;
+}
+
+/*
+** Apply any conversion required by the supplied column affinity to
+** memory cell pRec. affinity may be one of:
+**
+** SQLITE_AFF_NUMERIC
+** SQLITE_AFF_TEXT
+** SQLITE_AFF_NONE
+** SQLITE_AFF_INTEGER
+**
+*/
+static void applyAffinity(Mem *pRec, char affinity, u8 enc){
+ if( affinity==SQLITE_AFF_NONE ){
+ /* do nothing */
+ }else if( affinity==SQLITE_AFF_TEXT ){
+ /* Only attempt the conversion to TEXT if there is an integer or real
+ ** representation (blob and NULL do not get converted) but no string
+ ** representation.
+ */
+ if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
+ sqlite3VdbeMemStringify(pRec, enc);
+ }
+ pRec->flags &= ~(MEM_Real|MEM_Int);
+ }else{
+ if( 0==(pRec->flags&(MEM_Real|MEM_Int)) ){
+ /* pRec does not have a valid integer or real representation.
+ ** Attempt a conversion if pRec has a string representation and
+ ** it looks like a number.
+ */
+ int realnum;
+ sqlite3VdbeMemNulTerminate(pRec);
+ if( pRec->flags&MEM_Str && sqlite3IsNumber(pRec->z, &realnum, enc) ){
+ if( realnum ){
+ Realify(pRec);
+ }else{
+ Integerify(pRec);
+ }
+ }
+ }
+
+ if( affinity==SQLITE_AFF_INTEGER ){
+ /* For INTEGER affinity, try to convert a real value to an int */
+ if( (pRec->flags&MEM_Real) && !(pRec->flags&MEM_Int) ){
+ pRec->i = pRec->r;
+ if( ((double)pRec->i)==pRec->r ){
+ pRec->flags |= MEM_Int;
+ }
+ }
+ }
+ }
+}
+
+#ifndef NDEBUG
+/*
+** Write a nice string representation of the contents of cell pMem
+** into buffer zBuf, length nBuf.
+*/
+void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf){
+ char *zCsr = zBuf;
+ int f = pMem->flags;
+
+ static const char *const encnames[] = {"(X)", "(8)", "(16LE)", "(16BE)"};
+
+ if( f&MEM_Blob ){
+ int i;
+ char c;
+ if( f & MEM_Dyn ){
+ c = 'z';
+ assert( (f & (MEM_Static|MEM_Ephem))==0 );
+ }else if( f & MEM_Static ){
+ c = 't';
+ assert( (f & (MEM_Dyn|MEM_Ephem))==0 );
+ }else if( f & MEM_Ephem ){
+ c = 'e';
+ assert( (f & (MEM_Static|MEM_Dyn))==0 );
+ }else{
+ c = 's';
+ }
+
+ zCsr += sprintf(zCsr, "%c", c);
+ zCsr += sprintf(zCsr, "%d[", pMem->n);
+ for(i=0; i<16 && i<pMem->n; i++){
+ zCsr += sprintf(zCsr, "%02X ", ((int)pMem->z[i] & 0xFF));
+ }
+ for(i=0; i<16 && i<pMem->n; i++){
+ char z = pMem->z[i];
+ if( z<32 || z>126 ) *zCsr++ = '.';
+ else *zCsr++ = z;
+ }
+
+ zCsr += sprintf(zCsr, "]");
+ *zCsr = '\0';
+ }else if( f & MEM_Str ){
+ int j, k;
+ zBuf[0] = ' ';
+ if( f & MEM_Dyn ){
+ zBuf[1] = 'z';
+ assert( (f & (MEM_Static|MEM_Ephem))==0 );
+ }else if( f & MEM_Static ){
+ zBuf[1] = 't';
+ assert( (f & (MEM_Dyn|MEM_Ephem))==0 );
+ }else if( f & MEM_Ephem ){
+ zBuf[1] = 'e';
+ assert( (f & (MEM_Static|MEM_Dyn))==0 );
+ }else{
+ zBuf[1] = 's';
+ }
+ k = 2;
+ k += sprintf(&zBuf[k], "%d", pMem->n);
+ zBuf[k++] = '[';
+ for(j=0; j<15 && j<pMem->n; j++){
+ u8 c = pMem->z[j];
+ if( c>=0x20 && c<0x7f ){
+ zBuf[k++] = c;
+ }else{
+ zBuf[k++] = '.';
+ }
+ }
+ zBuf[k++] = ']';
+ k += sprintf(&zBuf[k], encnames[pMem->enc]);
+ zBuf[k++] = 0;
+ }
+}
+#endif
+
+
+#ifdef VDBE_PROFILE
+/*
+** The following routine only works on pentium-class processors.
+** It uses the RDTSC opcode to read cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+__inline__ unsigned long long int hwtime(void){
+ unsigned long long int x;
+ __asm__("rdtsc\n\t"
+ "mov %%edx, %%ecx\n\t"
+ :"=A" (x));
+ return x;
+}
+#endif
+
+/*
+** The CHECK_FOR_INTERRUPT macro defined here looks to see if the
+** sqlite3_interrupt() routine has been called. If it has been, then
+** processing of the VDBE program is interrupted.
+**
+** This macro added to every instruction that does a jump in order to
+** implement a loop. This test used to be on every single instruction,
+** but that meant we more testing that we needed. By only testing the
+** flag on jump instructions, we get a (small) speed improvement.
+*/
+#define CHECK_FOR_INTERRUPT \
+ if( db->flags & SQLITE_Interrupt ) goto abort_due_to_interrupt;
+
+
+/*
+** Execute as much of a VDBE program as we can then return.
+**
+** sqlite3VdbeMakeReady() must be called before this routine in order to
+** close the program with a final OP_Halt and to set up the callbacks
+** and the error message pointer.
+**
+** Whenever a row or result data is available, this routine will either
+** invoke the result callback (if there is one) or return with
+** SQLITE_ROW.
+**
+** If an attempt is made to open a locked database, then this routine
+** will either invoke the busy callback (if there is one) or it will
+** return SQLITE_BUSY.
+**
+** If an error occurs, an error message is written to memory obtained
+** from sqliteMalloc() and p->zErrMsg is made to point to that memory.
+** The error code is stored in p->rc and this routine returns SQLITE_ERROR.
+**
+** If the callback ever returns non-zero, then the program exits
+** immediately. There will be no error message but the p->rc field is
+** set to SQLITE_ABORT and this routine will return SQLITE_ERROR.
+**
+** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this
+** routine to return SQLITE_ERROR.
+**
+** Other fatal errors return SQLITE_ERROR.
+**
+** After this routine has finished, sqlite3VdbeFinalize() should be
+** used to clean up the mess that was left behind.
+*/
+int sqlite3VdbeExec(
+ Vdbe *p /* The VDBE */
+){
+ int pc; /* The program counter */
+ Op *pOp; /* Current operation */
+ int rc = SQLITE_OK; /* Value to return */
+ sqlite3 *db = p->db; /* The database */
+ Mem *pTos; /* Top entry in the operand stack */
+ char zBuf[100]; /* Space to sprintf() an integer */
+#ifdef VDBE_PROFILE
+ unsigned long long start; /* CPU clock count at start of opcode */
+ int origPc; /* Program counter at start of opcode */
+#endif
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ int nProgressOps = 0; /* Opcodes executed since progress callback. */
+#endif
+
+ if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
+ assert( db->magic==SQLITE_MAGIC_BUSY );
+ assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ p->rc = SQLITE_OK;
+ assert( p->explain==0 );
+ pTos = p->pTos;
+ if( sqlite3_malloc_failed ) goto no_mem;
+ if( p->popStack ){
+ popStack(&pTos, p->popStack);
+ p->popStack = 0;
+ }
+ p->resOnStack = 0;
+ CHECK_FOR_INTERRUPT;
+ for(pc=p->pc; rc==SQLITE_OK; pc++){
+ assert( pc>=0 && pc<p->nOp );
+ assert( pTos<=&p->aStack[pc] );
+#ifdef VDBE_PROFILE
+ origPc = pc;
+ start = hwtime();
+#endif
+ pOp = &p->aOp[pc];
+
+ /* Only allow tracing if NDEBUG is not defined.
+ */
+#ifndef NDEBUG
+ if( p->trace ){
+ if( pc==0 ){
+ printf("VDBE Execution Trace:\n");
+ sqlite3VdbePrintSql(p);
+ }
+ sqlite3VdbePrintOp(p->trace, pc, pOp);
+ }
+#endif
+#ifdef SQLITE_TEST
+ if( p->trace==0 && pc==0 && sqlite3OsFileExists("vdbe_sqltrace") ){
+ sqlite3VdbePrintSql(p);
+ }
+#endif
+
+
+ /* Check to see if we need to simulate an interrupt. This only happens
+ ** if we have a special test build.
+ */
+#ifdef SQLITE_TEST
+ if( sqlite3_interrupt_count>0 ){
+ sqlite3_interrupt_count--;
+ if( sqlite3_interrupt_count==0 ){
+ sqlite3_interrupt(db);
+ }
+ }
+#endif
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Call the progress callback if it is configured and the required number
+ ** of VDBE ops have been executed (either since this invocation of
+ ** sqlite3VdbeExec() or since last time the progress callback was called).
+ ** If the progress callback returns non-zero, exit the virtual machine with
+ ** a return code SQLITE_ABORT.
+ */
+ if( db->xProgress ){
+ if( db->nProgressOps==nProgressOps ){
+ if( db->xProgress(db->pProgressArg)!=0 ){
+ rc = SQLITE_ABORT;
+ continue; /* skip to the next iteration of the for loop */
+ }
+ nProgressOps = 0;
+ }
+ nProgressOps++;
+ }
+#endif
+
+ switch( pOp->opcode ){
+
+/*****************************************************************************
+** What follows is a massive switch statement where each case implements a
+** separate instruction in the virtual machine. If we follow the usual
+** indentation conventions, each case should be indented by 6 spaces. But
+** that is a lot of wasted space on the left margin. So the code within
+** the switch statement will break with convention and be flush-left. Another
+** big comment (similar to this one) will mark the point in the code where
+** we transition back to normal indentation.
+**
+** The formatting of each case is important. The makefile for SQLite
+** generates two C files "opcodes.h" and "opcodes.c" by scanning this
+** file looking for lines that begin with "case OP_". The opcodes.h files
+** will be filled with #defines that give unique integer values to each
+** opcode and the opcodes.c file is filled with an array of strings where
+** each string is the symbolic name for the corresponding opcode. If the
+** case statement is followed by a comment of the form "/# same as ... #/"
+** that comment is used to determine the particular value of the opcode.
+**
+** Documentation about VDBE opcodes is generated by scanning this file
+** for lines of that contain "Opcode:". That line and all subsequent
+** comment lines are used in the generation of the opcode.html documentation
+** file.
+**
+** SUMMARY:
+**
+** Formatting is important to scripts that scan this file.
+** Do not deviate from the formatting style currently in use.
+**
+*****************************************************************************/
+
+/* Opcode: Goto * P2 *
+**
+** An unconditional jump to address P2.
+** The next instruction executed will be
+** the one at index P2 from the beginning of
+** the program.
+*/
+case OP_Goto: {
+ CHECK_FOR_INTERRUPT;
+ pc = pOp->p2 - 1;
+ break;
+}
+
+/* Opcode: Gosub * P2 *
+**
+** Push the current address plus 1 onto the return address stack
+** and then jump to address P2.
+**
+** The return address stack is of limited depth. If too many
+** OP_Gosub operations occur without intervening OP_Returns, then
+** the return address stack will fill up and processing will abort
+** with a fatal error.
+*/
+case OP_Gosub: {
+ assert( p->returnDepth<sizeof(p->returnStack)/sizeof(p->returnStack[0]) );
+ p->returnStack[p->returnDepth++] = pc+1;
+ pc = pOp->p2 - 1;
+ break;
+}
+
+/* Opcode: Return * * *
+**
+** Jump immediately to the next instruction after the last unreturned
+** OP_Gosub. If an OP_Return has occurred for all OP_Gosubs, then
+** processing aborts with a fatal error.
+*/
+case OP_Return: {
+ assert( p->returnDepth>0 );
+ p->returnDepth--;
+ pc = p->returnStack[p->returnDepth] - 1;
+ break;
+}
+
+/* Opcode: Halt P1 P2 *
+**
+** Exit immediately. All open cursors, Lists, Sorts, etc are closed
+** automatically.
+**
+** P1 is the result code returned by sqlite3_exec(), sqlite3_reset(),
+** or sqlite3_finalize(). For a normal halt, this should be SQLITE_OK (0).
+** For errors, it can be some other value. If P1!=0 then P2 will determine
+** whether or not to rollback the current transaction. Do not rollback
+** if P2==OE_Fail. Do the rollback if P2==OE_Rollback. If P2==OE_Abort,
+** then back out all changes that have occurred during this execution of the
+** VDBE, but do not rollback the transaction.
+**
+** There is an implied "Halt 0 0 0" instruction inserted at the very end of
+** every program. So a jump past the last instruction of the program
+** is the same as executing Halt.
+*/
+case OP_Halt: {
+ p->pTos = pTos;
+ p->rc = pOp->p1;
+ p->pc = pc;
+ p->errorAction = pOp->p2;
+ if( pOp->p3 ){
+ sqlite3SetString(&p->zErrMsg, pOp->p3, (char*)0);
+ }
+ rc = sqlite3VdbeHalt(p);
+ if( rc==SQLITE_BUSY ){
+ p->rc = SQLITE_BUSY;
+ return SQLITE_BUSY;
+ }else if( rc!=SQLITE_OK ){
+ p->rc = rc;
+ }
+ return p->rc ? SQLITE_ERROR : SQLITE_DONE;
+}
+
+/* Opcode: Integer P1 * P3
+**
+** The integer value P1 is pushed onto the stack. If P3 is not zero
+** then it is assumed to be a string representation of the same integer.
+** If P1 is zero and P3 is not zero, then the value is derived from P3.
+*/
+case OP_Integer: {
+ pTos++;
+ if( pOp->p3==0 ){
+ pTos->flags = MEM_Int;
+ pTos->i = pOp->p1;
+ }else{
+ pTos->flags = MEM_Str|MEM_Static|MEM_Term;
+ pTos->z = pOp->p3;
+ pTos->n = strlen(pTos->z);
+ pTos->enc = SQLITE_UTF8;
+ pTos->i = sqlite3VdbeIntValue(pTos);
+ pTos->flags |= MEM_Int;
+ }
+ break;
+}
+
+/* Opcode: Real * * P3
+**
+** The string value P3 is converted to a real and pushed on to the stack.
+*/
+case OP_Real: { /* same as TK_FLOAT */
+ pTos++;
+ pTos->flags = MEM_Str|MEM_Static|MEM_Term;
+ pTos->z = pOp->p3;
+ pTos->n = strlen(pTos->z);
+ pTos->enc = SQLITE_UTF8;
+ pTos->r = sqlite3VdbeRealValue(pTos);
+ pTos->flags |= MEM_Real;
+ sqlite3VdbeChangeEncoding(pTos, db->enc);
+ break;
+}
+
+/* Opcode: String8 * * P3
+**
+** P3 points to a nul terminated UTF-8 string. This opcode is transformed
+** into an OP_String before it is executed for the first time.
+*/
+case OP_String8: { /* same as TK_STRING */
+ pOp->opcode = OP_String;
+
+ if( db->enc!=SQLITE_UTF8 && pOp->p3 ){
+ pTos++;
+ sqlite3VdbeMemSetStr(pTos, pOp->p3, -1, SQLITE_UTF8, SQLITE_STATIC);
+ if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pTos, db->enc) ) goto no_mem;
+ if( SQLITE_OK!=sqlite3VdbeMemDynamicify(pTos) ) goto no_mem;
+ pTos->flags &= ~(MEM_Dyn);
+ pTos->flags |= MEM_Static;
+ if( pOp->p3type==P3_DYNAMIC ){
+ sqliteFree(pOp->p3);
+ }
+ pOp->p3type = P3_DYNAMIC;
+ pOp->p3 = pTos->z;
+ break;
+ }
+ /* Otherwise fall through to the next case, OP_String */
+}
+
+/* Opcode: String * * P3
+**
+** The string value P3 is pushed onto the stack. If P3==0 then a
+** NULL is pushed onto the stack. P3 is assumed to be a nul terminated
+** string encoded with the database native encoding.
+*/
+case OP_String: {
+ pTos++;
+ if( pOp->p3 ){
+ pTos->flags = MEM_Str|MEM_Static|MEM_Term;
+ pTos->z = pOp->p3;
+ if( db->enc==SQLITE_UTF8 ){
+ pTos->n = strlen(pTos->z);
+ }else{
+ pTos->n = sqlite3utf16ByteLen(pTos->z, -1);
+ }
+ pTos->enc = db->enc;
+ }else{
+ pTos->flags = MEM_Null;
+ }
+ break;
+}
+
+/* Opcode: HexBlob * * P3
+**
+** P3 is an UTF-8 SQL hex encoding of a blob. The blob is pushed onto the
+** vdbe stack.
+**
+** The first time this instruction executes, in transforms itself into a
+** 'Blob' opcode with a binary blob as P3.
+*/
+case OP_HexBlob: { /* same as TK_BLOB */
+ pOp->opcode = OP_Blob;
+ pOp->p1 = strlen(pOp->p3)/2;
+ if( pOp->p1 ){
+ char *zBlob = sqlite3HexToBlob(pOp->p3);
+ if( !zBlob ) goto no_mem;
+ if( pOp->p3type==P3_DYNAMIC ){
+ sqliteFree(pOp->p3);
+ }
+ pOp->p3 = zBlob;
+ pOp->p3type = P3_DYNAMIC;
+ }else{
+ if( pOp->p3type==P3_DYNAMIC ){
+ sqliteFree(pOp->p3);
+ }
+ pOp->p3type = P3_STATIC;
+ pOp->p3 = "";
+ }
+
+ /* Fall through to the next case, OP_Blob. */
+}
+
+/* Opcode: Blob P1 * P3
+**
+** P3 points to a blob of data P1 bytes long. Push this
+** value onto the stack. This instruction is not coded directly
+** by the compiler. Instead, the compiler layer specifies
+** an OP_HexBlob opcode, with the hex string representation of
+** the blob as P3. This opcode is transformed to an OP_Blob
+** before execution (within the sqlite3_prepare() function).
+*/
+case OP_Blob: {
+ pTos++;
+ sqlite3VdbeMemSetStr(pTos, pOp->p3, pOp->p1, 0, 0);
+ break;
+}
+
+/* Opcode: Variable P1 * *
+**
+** Push the value of variable P1 onto the stack. A variable is
+** an unknown in the original SQL string as handed to sqlite3_compile().
+** Any occurance of the '?' character in the original SQL is considered
+** a variable. Variables in the SQL string are number from left to
+** right beginning with 1. The values of variables are set using the
+** sqlite3_bind() API.
+*/
+case OP_Variable: {
+ int j = pOp->p1 - 1;
+ assert( j>=0 && j<p->nVar );
+
+ pTos++;
+ sqlite3VdbeMemShallowCopy(pTos, &p->aVar[j], MEM_Static);
+ break;
+}
+
+/* Opcode: Pop P1 * *
+**
+** P1 elements are popped off of the top of stack and discarded.
+*/
+case OP_Pop: {
+ assert( pOp->p1>=0 );
+ popStack(&pTos, pOp->p1);
+ assert( pTos>=&p->aStack[-1] );
+ break;
+}
+
+/* Opcode: Dup P1 P2 *
+**
+** A copy of the P1-th element of the stack
+** is made and pushed onto the top of the stack.
+** The top of the stack is element 0. So the
+** instruction "Dup 0 0 0" will make a copy of the
+** top of the stack.
+**
+** If the content of the P1-th element is a dynamically
+** allocated string, then a new copy of that string
+** is made if P2==0. If P2!=0, then just a pointer
+** to the string is copied.
+**
+** Also see the Pull instruction.
+*/
+case OP_Dup: {
+ Mem *pFrom = &pTos[-pOp->p1];
+ assert( pFrom<=pTos && pFrom>=p->aStack );
+ pTos++;
+ sqlite3VdbeMemShallowCopy(pTos, pFrom, MEM_Ephem);
+ if( pOp->p2 ){
+ Deephemeralize(pTos);
+ }
+ break;
+}
+
+/* Opcode: Pull P1 * *
+**
+** The P1-th element is removed from its current location on
+** the stack and pushed back on top of the stack. The
+** top of the stack is element 0, so "Pull 0 0 0" is
+** a no-op. "Pull 1 0 0" swaps the top two elements of
+** the stack.
+**
+** See also the Dup instruction.
+*/
+case OP_Pull: {
+ Mem *pFrom = &pTos[-pOp->p1];
+ int i;
+ Mem ts;
+
+ ts = *pFrom;
+ Deephemeralize(pTos);
+ for(i=0; i<pOp->p1; i++, pFrom++){
+ Deephemeralize(&pFrom[1]);
+ assert( (pFrom->flags & MEM_Ephem)==0 );
+ *pFrom = pFrom[1];
+ if( pFrom->flags & MEM_Short ){
+ assert( pFrom->flags & (MEM_Str|MEM_Blob) );
+ assert( pFrom->z==pFrom[1].zShort );
+ pFrom->z = pFrom->zShort;
+ }
+ }
+ *pTos = ts;
+ if( pTos->flags & MEM_Short ){
+ assert( pTos->flags & (MEM_Str|MEM_Blob) );
+ assert( pTos->z==pTos[-pOp->p1].zShort );
+ pTos->z = pTos->zShort;
+ }
+ break;
+}
+
+/* Opcode: Push P1 * *
+**
+** Overwrite the value of the P1-th element down on the
+** stack (P1==0 is the top of the stack) with the value
+** of the top of the stack. Then pop the top of the stack.
+*/
+case OP_Push: {
+ Mem *pTo = &pTos[-pOp->p1];
+
+ assert( pTo>=p->aStack );
+ sqlite3VdbeMemMove(pTo, pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: Callback P1 * *
+**
+** Pop P1 values off the stack and form them into an array. Then
+** invoke the callback function using the newly formed array as the
+** 3rd parameter.
+*/
+case OP_Callback: {
+ int i;
+ assert( p->nResColumn==pOp->p1 );
+
+ for(i=0; i<pOp->p1; i++){
+ Mem *pVal = &pTos[0-i];
+ sqlite3VdbeMemNulTerminate(pVal);
+ storeTypeInfo(pVal, db->enc);
+ }
+
+ p->resOnStack = 1;
+ p->nCallback++;
+ p->popStack = pOp->p1;
+ p->pc = pc + 1;
+ p->pTos = pTos;
+ return SQLITE_ROW;
+}
+
+/* Opcode: Concat P1 P2 *
+**
+** Look at the first P1+2 elements of the stack. Append them all
+** together with the lowest element first. The original P1+2 elements
+** are popped from the stack if P2==0 and retained if P2==1. If
+** any element of the stack is NULL, then the result is NULL.
+**
+** When P1==1, this routine makes a copy of the top stack element
+** into memory obtained from sqliteMalloc().
+*/
+case OP_Concat: { /* same as TK_CONCAT */
+ char *zNew;
+ int nByte;
+ int nField;
+ int i, j;
+ Mem *pTerm;
+
+ /* Loop through the stack elements to see how long the result will be. */
+ nField = pOp->p1 + 2;
+ pTerm = &pTos[1-nField];
+ nByte = 0;
+ for(i=0; i<nField; i++, pTerm++){
+ assert( pOp->p2==0 || (pTerm->flags&MEM_Str) );
+ if( pTerm->flags&MEM_Null ){
+ nByte = -1;
+ break;
+ }
+ Stringify(pTerm, db->enc);
+ nByte += pTerm->n;
+ }
+
+ if( nByte<0 ){
+ /* If nByte is less than zero, then there is a NULL value on the stack.
+ ** In this case just pop the values off the stack (if required) and
+ ** push on a NULL.
+ */
+ if( pOp->p2==0 ){
+ popStack(&pTos, nField);
+ }
+ pTos++;
+ pTos->flags = MEM_Null;
+ }else{
+ /* Otherwise malloc() space for the result and concatenate all the
+ ** stack values.
+ */
+ zNew = sqliteMallocRaw( nByte+2 );
+ if( zNew==0 ) goto no_mem;
+ j = 0;
+ pTerm = &pTos[1-nField];
+ for(i=j=0; i<nField; i++, pTerm++){
+ int n = pTerm->n;
+ assert( pTerm->flags & MEM_Str );
+ memcpy(&zNew[j], pTerm->z, n);
+ j += n;
+ }
+ zNew[j] = 0;
+ zNew[j+1] = 0;
+ assert( j==nByte );
+
+ if( pOp->p2==0 ){
+ popStack(&pTos, nField);
+ }
+ pTos++;
+ pTos->n = j;
+ pTos->flags = MEM_Str|MEM_Dyn|MEM_Term;
+ pTos->xDel = 0;
+ pTos->enc = db->enc;
+ pTos->z = zNew;
+ }
+ break;
+}
+
+/* Opcode: Add * * *
+**
+** Pop the top two elements from the stack, add them together,
+** and push the result back onto the stack. If either element
+** is a string then it is converted to a double using the atof()
+** function before the addition.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: Multiply * * *
+**
+** Pop the top two elements from the stack, multiply them together,
+** and push the result back onto the stack. If either element
+** is a string then it is converted to a double using the atof()
+** function before the multiplication.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: Subtract * * *
+**
+** Pop the top two elements from the stack, subtract the
+** first (what was on top of the stack) from the second (the
+** next on stack)
+** and push the result back onto the stack. If either element
+** is a string then it is converted to a double using the atof()
+** function before the subtraction.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: Divide * * *
+**
+** Pop the top two elements from the stack, divide the
+** first (what was on top of the stack) from the second (the
+** next on stack)
+** and push the result back onto the stack. If either element
+** is a string then it is converted to a double using the atof()
+** function before the division. Division by zero returns NULL.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: Remainder * * *
+**
+** Pop the top two elements from the stack, divide the
+** first (what was on top of the stack) from the second (the
+** next on stack)
+** and push the remainder after division onto the stack. If either element
+** is a string then it is converted to a double using the atof()
+** function before the division. Division by zero returns NULL.
+** If either operand is NULL, the result is NULL.
+*/
+case OP_Add: /* same as TK_PLUS */
+case OP_Subtract: /* same as TK_MINUS */
+case OP_Multiply: /* same as TK_STAR */
+case OP_Divide: /* same as TK_SLASH */
+case OP_Remainder: { /* same as TK_REM */
+ Mem *pNos = &pTos[-1];
+ assert( pNos>=p->aStack );
+ if( ((pTos->flags | pNos->flags) & MEM_Null)!=0 ){
+ Release(pTos);
+ pTos--;
+ Release(pTos);
+ pTos->flags = MEM_Null;
+ }else if( (pTos->flags & pNos->flags & MEM_Int)==MEM_Int ){
+ i64 a, b;
+ a = pTos->i;
+ b = pNos->i;
+ switch( pOp->opcode ){
+ case OP_Add: b += a; break;
+ case OP_Subtract: b -= a; break;
+ case OP_Multiply: b *= a; break;
+ case OP_Divide: {
+ if( a==0 ) goto divide_by_zero;
+ b /= a;
+ break;
+ }
+ default: {
+ if( a==0 ) goto divide_by_zero;
+ b %= a;
+ break;
+ }
+ }
+ Release(pTos);
+ pTos--;
+ Release(pTos);
+ pTos->i = b;
+ pTos->flags = MEM_Int;
+ }else{
+ double a, b;
+ a = sqlite3VdbeRealValue(pTos);
+ b = sqlite3VdbeRealValue(pNos);
+ switch( pOp->opcode ){
+ case OP_Add: b += a; break;
+ case OP_Subtract: b -= a; break;
+ case OP_Multiply: b *= a; break;
+ case OP_Divide: {
+ if( a==0.0 ) goto divide_by_zero;
+ b /= a;
+ break;
+ }
+ default: {
+ int ia = (int)a;
+ int ib = (int)b;
+ if( ia==0.0 ) goto divide_by_zero;
+ b = ib % ia;
+ break;
+ }
+ }
+ Release(pTos);
+ pTos--;
+ Release(pTos);
+ pTos->r = b;
+ pTos->flags = MEM_Real;
+ }
+ break;
+
+divide_by_zero:
+ Release(pTos);
+ pTos--;
+ Release(pTos);
+ pTos->flags = MEM_Null;
+ break;
+}
+
+/* Opcode: CollSeq * * P3
+**
+** P3 is a pointer to a CollSeq struct. If the next call to a user function
+** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
+** be returned. This is used by the built-in min(), max() and nullif()
+** built-in functions.
+**
+** The interface used by the implementation of the aforementioned functions
+** to retrieve the collation sequence set by this opcode is not available
+** publicly, only to user functions defined in func.c.
+*/
+case OP_CollSeq: {
+ assert( pOp->p3type==P3_COLLSEQ );
+ break;
+}
+
+/* Opcode: Function P1 P2 P3
+**
+** Invoke a user function (P3 is a pointer to a Function structure that
+** defines the function) with P1 arguments taken from the stack. Pop all
+** arguments from the stack and push back the result.
+**
+** P2 is a 32-bit bitmask indicating whether or not each argument to the
+** function was determined to be constant at compile time. If the first
+** argument was constant then bit 0 of P2 is set. This is used to determine
+** whether meta data associated with a user function argument using the
+** sqlite3_set_auxdata() API may be safely retained until the next
+** invocation of this opcode.
+**
+** See also: AggFunc
+*/
+case OP_Function: {
+ int i;
+ Mem *pArg;
+ sqlite3_context ctx;
+ sqlite3_value **apVal;
+ int n = pOp->p1;
+
+ n = pOp->p1;
+ apVal = p->apArg;
+ assert( apVal || n==0 );
+
+ pArg = &pTos[1-n];
+ for(i=0; i<n; i++, pArg++){
+ apVal[i] = pArg;
+ storeTypeInfo(pArg, db->enc);
+ }
+
+ assert( pOp->p3type==P3_FUNCDEF || pOp->p3type==P3_VDBEFUNC );
+ if( pOp->p3type==P3_FUNCDEF ){
+ ctx.pFunc = (FuncDef*)pOp->p3;
+ ctx.pVdbeFunc = 0;
+ }else{
+ ctx.pVdbeFunc = (VdbeFunc*)pOp->p3;
+ ctx.pFunc = ctx.pVdbeFunc->pFunc;
+ }
+
+ ctx.s.flags = MEM_Null;
+ ctx.s.z = 0;
+ ctx.s.xDel = 0;
+ ctx.isError = 0;
+ ctx.isStep = 0;
+ if( ctx.pFunc->needCollSeq ){
+ assert( pOp>p->aOp );
+ assert( pOp[-1].p3type==P3_COLLSEQ );
+ assert( pOp[-1].opcode==OP_CollSeq );
+ ctx.pColl = (CollSeq *)pOp[-1].p3;
+ }
+ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ (*ctx.pFunc->xFunc)(&ctx, n, apVal);
+ if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
+ if( sqlite3_malloc_failed ) goto no_mem;
+ popStack(&pTos, n);
+
+ /* If any auxilary data functions have been called by this user function,
+ ** immediately call the destructor for any non-static values.
+ */
+ if( ctx.pVdbeFunc ){
+ sqlite3VdbeDeleteAuxData(ctx.pVdbeFunc, pOp->p2);
+ pOp->p3 = (char *)ctx.pVdbeFunc;
+ pOp->p3type = P3_VDBEFUNC;
+ }
+
+ /* Copy the result of the function to the top of the stack */
+ sqlite3VdbeChangeEncoding(&ctx.s, db->enc);
+ pTos++;
+ pTos->flags = 0;
+ sqlite3VdbeMemMove(pTos, &ctx.s);
+
+ /* If the function returned an error, throw an exception */
+ if( ctx.isError ){
+ if( !(pTos->flags&MEM_Str) ){
+ sqlite3SetString(&p->zErrMsg, "user function error", (char*)0);
+ }else{
+ sqlite3SetString(&p->zErrMsg, sqlite3_value_text(pTos), (char*)0);
+ sqlite3VdbeChangeEncoding(pTos, db->enc);
+ }
+ rc = SQLITE_ERROR;
+ }
+ break;
+}
+
+/* Opcode: BitAnd * * *
+**
+** Pop the top two elements from the stack. Convert both elements
+** to integers. Push back onto the stack the bit-wise AND of the
+** two elements.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: BitOr * * *
+**
+** Pop the top two elements from the stack. Convert both elements
+** to integers. Push back onto the stack the bit-wise OR of the
+** two elements.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: ShiftLeft * * *
+**
+** Pop the top two elements from the stack. Convert both elements
+** to integers. Push back onto the stack the second element shifted
+** left by N bits where N is the top element on the stack.
+** If either operand is NULL, the result is NULL.
+*/
+/* Opcode: ShiftRight * * *
+**
+** Pop the top two elements from the stack. Convert both elements
+** to integers. Push back onto the stack the second element shifted
+** right by N bits where N is the top element on the stack.
+** If either operand is NULL, the result is NULL.
+*/
+case OP_BitAnd: /* same as TK_BITAND */
+case OP_BitOr: /* same as TK_BITOR */
+case OP_ShiftLeft: /* same as TK_LSHIFT */
+case OP_ShiftRight: { /* same as TK_RSHIFT */
+ Mem *pNos = &pTos[-1];
+ int a, b;
+
+ assert( pNos>=p->aStack );
+ if( (pTos->flags | pNos->flags) & MEM_Null ){
+ popStack(&pTos, 2);
+ pTos++;
+ pTos->flags = MEM_Null;
+ break;
+ }
+ a = sqlite3VdbeIntValue(pNos);
+ b = sqlite3VdbeIntValue(pTos);
+ switch( pOp->opcode ){
+ case OP_BitAnd: a &= b; break;
+ case OP_BitOr: a |= b; break;
+ case OP_ShiftLeft: a <<= b; break;
+ case OP_ShiftRight: a >>= b; break;
+ default: /* CANT HAPPEN */ break;
+ }
+ Release(pTos);
+ pTos--;
+ Release(pTos);
+ pTos->i = a;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: AddImm P1 * *
+**
+** Add the value P1 to whatever is on top of the stack. The result
+** is always an integer.
+**
+** To force the top of the stack to be an integer, just add 0.
+*/
+case OP_AddImm: {
+ assert( pTos>=p->aStack );
+ Integerify(pTos);
+ pTos->i += pOp->p1;
+ break;
+}
+
+/* Opcode: ForceInt P1 P2 *
+**
+** Convert the top of the stack into an integer. If the current top of
+** the stack is not numeric (meaning that is is a NULL or a string that
+** does not look like an integer or floating point number) then pop the
+** stack and jump to P2. If the top of the stack is numeric then
+** convert it into the least integer that is greater than or equal to its
+** current value if P1==0, or to the least integer that is strictly
+** greater than its current value if P1==1.
+*/
+case OP_ForceInt: {
+ int v;
+ assert( pTos>=p->aStack );
+ applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc);
+ if( (pTos->flags & (MEM_Int|MEM_Real))==0 ){
+ Release(pTos);
+ pTos--;
+ pc = pOp->p2 - 1;
+ break;
+ }
+ if( pTos->flags & MEM_Int ){
+ v = pTos->i + (pOp->p1!=0);
+ }else{
+ Realify(pTos);
+ v = (int)pTos->r;
+ if( pTos->r>(double)v ) v++;
+ if( pOp->p1 && pTos->r==(double)v ) v++;
+ }
+ Release(pTos);
+ pTos->i = v;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: MustBeInt P1 P2 *
+**
+** Force the top of the stack to be an integer. If the top of the
+** stack is not an integer and cannot be converted into an integer
+** with out data loss, then jump immediately to P2, or if P2==0
+** raise an SQLITE_MISMATCH exception.
+**
+** If the top of the stack is not an integer and P2 is not zero and
+** P1 is 1, then the stack is popped. In all other cases, the depth
+** of the stack is unchanged.
+*/
+case OP_MustBeInt: {
+ assert( pTos>=p->aStack );
+ applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc);
+ if( (pTos->flags & MEM_Int)==0 ){
+ if( pOp->p2==0 ){
+ rc = SQLITE_MISMATCH;
+ goto abort_due_to_error;
+ }else{
+ if( pOp->p1 ) popStack(&pTos, 1);
+ pc = pOp->p2 - 1;
+ }
+ }else{
+ Release(pTos);
+ pTos->flags = MEM_Int;
+ }
+ break;
+}
+
+/* Opcode: Eq P1 P2 P3
+**
+** Pop the top two elements from the stack. If they are equal, then
+** jump to instruction P2. Otherwise, continue to the next instruction.
+**
+** The least significant byte of P1 may be either 0x00 or 0x01. If either
+** operand is NULL (and thus if the result is unknown) then take the jump
+** only if the least significant byte of P1 is 0x01.
+**
+** The second least significant byte of P1 must be an affinity character -
+** 'n', 't', 'i' or 'o' - or 0x00. An attempt is made to coerce both values
+** according to the affinity before the comparison is made. If the byte is
+** 0x00, then numeric affinity is used.
+**
+** Once any conversions have taken place, and neither value is NULL,
+** the values are compared. If both values are blobs, or both are text,
+** then memcmp() is used to determine the results of the comparison. If
+** both values are numeric, then a numeric comparison is used. If the
+** two values are of different types, then they are inequal.
+**
+** If P2 is zero, do not jump. Instead, push an integer 1 onto the
+** stack if the jump would have been taken, or a 0 if not. Push a
+** NULL if either operand was NULL.
+**
+** If P3 is not NULL it is a pointer to a collating sequence (a CollSeq
+** structure) that defines how to compare text.
+*/
+/* Opcode: Ne P1 P2 P3
+**
+** This works just like the Eq opcode except that the jump is taken if
+** the operands from the stack are not equal. See the Eq opcode for
+** additional information.
+*/
+/* Opcode: Lt P1 P2 P3
+**
+** This works just like the Eq opcode except that the jump is taken if
+** the 2nd element down on the stack is less than the top of the stack.
+** See the Eq opcode for additional information.
+*/
+/* Opcode: Le P1 P2 P3
+**
+** This works just like the Eq opcode except that the jump is taken if
+** the 2nd element down on the stack is less than or equal to the
+** top of the stack. See the Eq opcode for additional information.
+*/
+/* Opcode: Gt P1 P2 P3
+**
+** This works just like the Eq opcode except that the jump is taken if
+** the 2nd element down on the stack is greater than the top of the stack.
+** See the Eq opcode for additional information.
+*/
+/* Opcode: Ge P1 P2 P3
+**
+** This works just like the Eq opcode except that the jump is taken if
+** the 2nd element down on the stack is greater than or equal to the
+** top of the stack. See the Eq opcode for additional information.
+*/
+case OP_Eq: /* same as TK_EQ */
+case OP_Ne: /* same as TK_NE */
+case OP_Lt: /* same as TK_LT */
+case OP_Le: /* same as TK_LE */
+case OP_Gt: /* same as TK_GT */
+case OP_Ge: { /* same as TK_GE */
+ Mem *pNos;
+ int flags;
+ int res;
+ char affinity;
+
+ pNos = &pTos[-1];
+ flags = pTos->flags|pNos->flags;
+
+ /* If either value is a NULL P2 is not zero, take the jump if the least
+ ** significant byte of P1 is true. If P2 is zero, then push a NULL onto
+ ** the stack.
+ */
+ if( flags&MEM_Null ){
+ popStack(&pTos, 2);
+ if( pOp->p2 ){
+ if( (pOp->p1&0xFF) ) pc = pOp->p2-1;
+ }else{
+ pTos++;
+ pTos->flags = MEM_Null;
+ }
+ break;
+ }
+
+ affinity = (pOp->p1>>8)&0xFF;
+ if( affinity ){
+ applyAffinity(pNos, affinity, db->enc);
+ applyAffinity(pTos, affinity, db->enc);
+ }
+
+ assert( pOp->p3type==P3_COLLSEQ || pOp->p3==0 );
+ res = sqlite3MemCompare(pNos, pTos, (CollSeq*)pOp->p3);
+ switch( pOp->opcode ){
+ case OP_Eq: res = res==0; break;
+ case OP_Ne: res = res!=0; break;
+ case OP_Lt: res = res<0; break;
+ case OP_Le: res = res<=0; break;
+ case OP_Gt: res = res>0; break;
+ default: res = res>=0; break;
+ }
+
+ popStack(&pTos, 2);
+ if( pOp->p2 ){
+ if( res ){
+ pc = pOp->p2-1;
+ }
+ }else{
+ pTos++;
+ pTos->flags = MEM_Int;
+ pTos->i = res;
+ }
+ break;
+}
+
+/* Opcode: And * * *
+**
+** Pop two values off the stack. Take the logical AND of the
+** two values and push the resulting boolean value back onto the
+** stack.
+*/
+/* Opcode: Or * * *
+**
+** Pop two values off the stack. Take the logical OR of the
+** two values and push the resulting boolean value back onto the
+** stack.
+*/
+case OP_And: /* same as TK_AND */
+case OP_Or: { /* same as TK_OR */
+ Mem *pNos = &pTos[-1];
+ int v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */
+
+ assert( pNos>=p->aStack );
+ if( pTos->flags & MEM_Null ){
+ v1 = 2;
+ }else{
+ Integerify(pTos);
+ v1 = pTos->i==0;
+ }
+ if( pNos->flags & MEM_Null ){
+ v2 = 2;
+ }else{
+ Integerify(pNos);
+ v2 = pNos->i==0;
+ }
+ if( pOp->opcode==OP_And ){
+ static const unsigned char and_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
+ v1 = and_logic[v1*3+v2];
+ }else{
+ static const unsigned char or_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
+ v1 = or_logic[v1*3+v2];
+ }
+ popStack(&pTos, 2);
+ pTos++;
+ if( v1==2 ){
+ pTos->flags = MEM_Null;
+ }else{
+ pTos->i = v1==0;
+ pTos->flags = MEM_Int;
+ }
+ break;
+}
+
+/* Opcode: Negative * * *
+**
+** Treat the top of the stack as a numeric quantity. Replace it
+** with its additive inverse. If the top of the stack is NULL
+** its value is unchanged.
+*/
+/* Opcode: AbsValue * * *
+**
+** Treat the top of the stack as a numeric quantity. Replace it
+** with its absolute value. If the top of the stack is NULL
+** its value is unchanged.
+*/
+case OP_Negative: /* same as TK_UMINUS */
+case OP_AbsValue: {
+ assert( pTos>=p->aStack );
+ if( pTos->flags & MEM_Real ){
+ Release(pTos);
+ if( pOp->opcode==OP_Negative || pTos->r<0.0 ){
+ pTos->r = -pTos->r;
+ }
+ pTos->flags = MEM_Real;
+ }else if( pTos->flags & MEM_Int ){
+ Release(pTos);
+ if( pOp->opcode==OP_Negative || pTos->i<0 ){
+ pTos->i = -pTos->i;
+ }
+ pTos->flags = MEM_Int;
+ }else if( pTos->flags & MEM_Null ){
+ /* Do nothing */
+ }else{
+ Realify(pTos);
+ if( pOp->opcode==OP_Negative || pTos->r<0.0 ){
+ pTos->r = -pTos->r;
+ }
+ pTos->flags = MEM_Real;
+ }
+ break;
+}
+
+/* Opcode: Not * * *
+**
+** Interpret the top of the stack as a boolean value. Replace it
+** with its complement. If the top of the stack is NULL its value
+** is unchanged.
+*/
+case OP_Not: { /* same as TK_NOT */
+ assert( pTos>=p->aStack );
+ if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
+ Integerify(pTos);
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos->i = !pTos->i;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: BitNot * * *
+**
+** Interpret the top of the stack as an value. Replace it
+** with its ones-complement. If the top of the stack is NULL its
+** value is unchanged.
+*/
+case OP_BitNot: { /* same as TK_BITNOT */
+ assert( pTos>=p->aStack );
+ if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */
+ Integerify(pTos);
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos->i = ~pTos->i;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: Noop * * *
+**
+** Do nothing. This instruction is often useful as a jump
+** destination.
+*/
+case OP_Noop: {
+ break;
+}
+
+/* Opcode: If P1 P2 *
+**
+** Pop a single boolean from the stack. If the boolean popped is
+** true, then jump to p2. Otherwise continue to the next instruction.
+** An integer is false if zero and true otherwise. A string is
+** false if it has zero length and true otherwise.
+**
+** If the value popped of the stack is NULL, then take the jump if P1
+** is true and fall through if P1 is false.
+*/
+/* Opcode: IfNot P1 P2 *
+**
+** Pop a single boolean from the stack. If the boolean popped is
+** false, then jump to p2. Otherwise continue to the next instruction.
+** An integer is false if zero and true otherwise. A string is
+** false if it has zero length and true otherwise.
+**
+** If the value popped of the stack is NULL, then take the jump if P1
+** is true and fall through if P1 is false.
+*/
+case OP_If:
+case OP_IfNot: {
+ int c;
+ assert( pTos>=p->aStack );
+ if( pTos->flags & MEM_Null ){
+ c = pOp->p1;
+ }else{
+ c = sqlite3VdbeIntValue(pTos);
+ if( pOp->opcode==OP_IfNot ) c = !c;
+ }
+ Release(pTos);
+ pTos--;
+ if( c ) pc = pOp->p2-1;
+ break;
+}
+
+/* Opcode: IsNull P1 P2 *
+**
+** If any of the top abs(P1) values on the stack are NULL, then jump
+** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack
+** unchanged.
+*/
+case OP_IsNull: { /* same as TK_ISNULL */
+ int i, cnt;
+ Mem *pTerm;
+ cnt = pOp->p1;
+ if( cnt<0 ) cnt = -cnt;
+ pTerm = &pTos[1-cnt];
+ assert( pTerm>=p->aStack );
+ for(i=0; i<cnt; i++, pTerm++){
+ if( pTerm->flags & MEM_Null ){
+ pc = pOp->p2-1;
+ break;
+ }
+ }
+ if( pOp->p1>0 ) popStack(&pTos, cnt);
+ break;
+}
+
+/* Opcode: NotNull P1 P2 *
+**
+** Jump to P2 if the top P1 values on the stack are all not NULL. Pop the
+** stack if P1 times if P1 is greater than zero. If P1 is less than
+** zero then leave the stack unchanged.
+*/
+case OP_NotNull: { /* same as TK_NOTNULL */
+ int i, cnt;
+ cnt = pOp->p1;
+ if( cnt<0 ) cnt = -cnt;
+ assert( &pTos[1-cnt] >= p->aStack );
+ for(i=0; i<cnt && (pTos[1+i-cnt].flags & MEM_Null)==0; i++){}
+ if( i>=cnt ) pc = pOp->p2-1;
+ if( pOp->p1>0 ) popStack(&pTos, cnt);
+ break;
+}
+
+/* Opcode: SetNumColumns P1 P2 *
+**
+** Before the OP_Column opcode can be executed on a cursor, this
+** opcode must be called to set the number of fields in the table.
+**
+** This opcode sets the number of columns for cursor P1 to P2.
+*/
+case OP_SetNumColumns: {
+ assert( (pOp->p1)<p->nCursor );
+ assert( p->apCsr[pOp->p1]!=0 );
+ p->apCsr[pOp->p1]->nField = pOp->p2;
+ break;
+}
+
+/* Opcode: IdxColumn P1 * *
+**
+** P1 is a cursor opened on an index. Push the first field from the
+** current index key onto the stack.
+*/
+/* Opcode: Column P1 P2 *
+**
+** Interpret the data that cursor P1 points to as a structure built using
+** the MakeRecord instruction. (See the MakeRecord opcode for additional
+** information about the format of the data.) Push onto the stack the value
+** of the P2-th column contained in the data.
+**
+** If the KeyAsData opcode has previously executed on this cursor, then the
+** field might be extracted from the key rather than the data.
+**
+** If P1 is negative, then the record is stored on the stack rather than in
+** a table. For P1==-1, the top of the stack is used. For P1==-2, the
+** next on the stack is used. And so forth. The value pushed is always
+** just a pointer into the record which is stored further down on the
+** stack. The column value is not copied. The number of columns in the
+** record is stored on the stack just above the record itself.
+*/
+case OP_IdxColumn:
+case OP_Column: {
+ u32 payloadSize; /* Number of bytes in the record */
+ int p1 = pOp->p1; /* P1 value of the opcode */
+ int p2 = pOp->p2; /* column number to retrieve */
+ Cursor *pC = 0; /* The VDBE cursor */
+ char *zRec; /* Pointer to complete record-data */
+ BtCursor *pCrsr; /* The BTree cursor */
+ u32 *aType; /* aType[i] holds the numeric type of the i-th column */
+ u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
+ u32 nField; /* number of fields in the record */
+ u32 szHdr; /* Number of bytes in the record header */
+ int len; /* The length of the serialized data for the column */
+ int offset = 0; /* Offset into the data */
+ int idx; /* Index into the header */
+ int i; /* Loop counter */
+ char *zData; /* Part of the record being decoded */
+ Mem sMem; /* For storing the record being decoded */
+
+ sMem.flags = 0;
+ assert( p1<p->nCursor );
+ pTos++;
+ pTos->flags = MEM_Null;
+
+ /* This block sets the variable payloadSize to be the total number of
+ ** bytes in the record.
+ **
+ ** zRec is set to be the complete text of the record if it is available.
+ ** The complete record text is always available for pseudo-tables and
+ ** when we are decoded a record from the stack. If the record is stored
+ ** in a cursor, the complete record text might be available in the
+ ** pC->aRow cache. Or it might not be. If the data is unavailable,
+ ** zRec is set to NULL.
+ **
+ ** We also compute the number of columns in the record. For cursors,
+ ** the number of columns is stored in the Cursor.nField element. For
+ ** records on the stack, the next entry down on the stack is an integer
+ ** which is the number of records.
+ */
+ assert( p1<0 || p->apCsr[p1]!=0 );
+ if( p1<0 ){
+ /* Take the record off of the stack */
+ Mem *pRec = &pTos[p1];
+ Mem *pCnt = &pRec[-1];
+ assert( pRec>=p->aStack );
+ assert( pRec->flags & MEM_Blob );
+ payloadSize = pRec->n;
+ zRec = pRec->z;
+ assert( pCnt>=p->aStack );
+ assert( pCnt->flags & MEM_Int );
+ nField = pCnt->i;
+ pCrsr = 0;
+ }else if( (pC = p->apCsr[p1])->pCursor!=0 ){
+ /* The record is stored in a B-Tree */
+ sqlite3VdbeCursorMoveto(pC);
+ zRec = 0;
+ pCrsr = pC->pCursor;
+ if( pC->nullRow ){
+ payloadSize = 0;
+ }else if( pC->cacheValid ){
+ payloadSize = pC->payloadSize;
+ zRec = pC->aRow;
+ }else if( pC->keyAsData ){
+ i64 payloadSize64;
+ sqlite3BtreeKeySize(pCrsr, &payloadSize64);
+ payloadSize = payloadSize64;
+ }else{
+ sqlite3BtreeDataSize(pCrsr, &payloadSize);
+ }
+ nField = pC->nField;
+ }else if( pC->pseudoTable ){
+ /* The record is the sole entry of a pseudo-table */
+ payloadSize = pC->nData;
+ zRec = pC->pData;
+ pC->cacheValid = 0;
+ assert( payloadSize==0 || zRec!=0 );
+ nField = pC->nField;
+ pCrsr = 0;
+ }else{
+ zRec = 0;
+ payloadSize = 0;
+ pCrsr = 0;
+ nField = 0;
+ }
+
+ /* If payloadSize is 0, then just push a NULL onto the stack. */
+ if( payloadSize==0 ){
+ pTos->flags = MEM_Null;
+ break;
+ }
+
+ assert( p2<nField );
+
+ /* Read and parse the table header. Store the results of the parse
+ ** into the record header cache fields of the cursor.
+ */
+ if( pC && pC->cacheValid ){
+ aType = pC->aType;
+ aOffset = pC->aOffset;
+ }else{
+ int avail; /* Number of bytes of available data */
+ if( pC && pC->aType ){
+ aType = pC->aType;
+ }else{
+ aType = sqliteMallocRaw( 2*nField*sizeof(aType) );
+ }
+ aOffset = &aType[nField];
+ if( aType==0 ){
+ goto no_mem;
+ }
+
+ /* Figure out how many bytes are in the header */
+ if( zRec ){
+ zData = zRec;
+ }else{
+ if( pC->keyAsData ){
+ zData = (char*)sqlite3BtreeKeyFetch(pCrsr, &avail);
+ }else{
+ zData = (char*)sqlite3BtreeDataFetch(pCrsr, &avail);
+ }
+ /* If KeyFetch()/DataFetch() managed to get the entire payload,
+ ** save the payload in the pC->aRow cache. That will save us from
+ ** having to make additional calls to fetch the content portion of
+ ** the record.
+ */
+ if( avail>=payloadSize ){
+ zRec = pC->aRow = zData;
+ }else{
+ pC->aRow = 0;
+ }
+ }
+ idx = sqlite3GetVarint32(zData, &szHdr);
+
+
+ /* The KeyFetch() or DataFetch() above are fast and will get the entire
+ ** record header in most cases. But they will fail to get the complete
+ ** record header if the record header does not fit on a single page
+ ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to
+ ** acquire the complete header text.
+ */
+ if( !zRec && avail<szHdr ){
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, szHdr, pC->keyAsData, &sMem);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ zData = sMem.z;
+ }
+
+ /* Scan the header and use it to fill in the aType[] and aOffset[]
+ ** arrays. aType[i] will contain the type integer for the i-th
+ ** column and aOffset[i] will contain the offset from the beginning
+ ** of the record to the start of the data for the i-th column
+ */
+ offset = szHdr;
+ i = 0;
+ while( idx<szHdr && i<nField && offset<=payloadSize ){
+ aOffset[i] = offset;
+ idx += sqlite3GetVarint32(&zData[idx], &aType[i]);
+ offset += sqlite3VdbeSerialTypeLen(aType[i]);
+ i++;
+ }
+ Release(&sMem);
+ sMem.flags = MEM_Null;
+
+ /* The header should end at the start of data and the data should
+ ** end at last byte of the record. If this is not the case then
+ ** we are dealing with a malformed record.
+ */
+ if( idx!=szHdr || offset!=payloadSize ){
+ sqliteFree(aType);
+ if( pC ) pC->aType = 0;
+ rc = SQLITE_CORRUPT;
+ break;
+ }
+
+ /* Remember all aType and aColumn information if we have a cursor
+ ** to remember it in. */
+ if( pC ){
+ pC->payloadSize = payloadSize;
+ pC->aType = aType;
+ pC->aOffset = aOffset;
+ pC->cacheValid = 1;
+ }
+ }
+
+ /* Get the column information.
+ */
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( zRec ){
+ zData = &zRec[aOffset[p2]];
+ }else{
+ len = sqlite3VdbeSerialTypeLen(aType[p2]);
+ sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, pC->keyAsData, &sMem);
+ zData = sMem.z;
+ }
+ sqlite3VdbeSerialGet(zData, aType[p2], pTos);
+ pTos->enc = db->enc;
+
+ /* If we dynamically allocated space to hold the data (in the
+ ** sqlite3VdbeMemFromBtree() call above) then transfer control of that
+ ** dynamically allocated space over to the pTos structure rather.
+ ** This prevents a memory copy.
+ */
+ if( (sMem.flags & MEM_Dyn)!=0 ){
+ assert( pTos->flags & MEM_Ephem );
+ assert( pTos->flags & (MEM_Str|MEM_Blob) );
+ assert( pTos->z==sMem.z );
+ assert( sMem.flags & MEM_Term );
+ pTos->flags &= ~MEM_Ephem;
+ pTos->flags |= MEM_Dyn|MEM_Term;
+ }
+
+ /* pTos->z might be pointing to sMem.zShort[]. Fix that so that we
+ ** can abandon sMem */
+ rc = sqlite3VdbeMemMakeWriteable(pTos);
+
+ /* Release the aType[] memory if we are not dealing with cursor */
+ if( !pC ){
+ sqliteFree(aType);
+ }
+ break;
+}
+
+/* Opcode MakeRecord P1 P2 P3
+**
+** Convert the top abs(P1) entries of the stack into a single entry
+** suitable for use as a data record in a database table or as a key
+** in an index. The details of the format are irrelavant as long as
+** the OP_Column opcode can decode the record later and as long as the
+** sqlite3VdbeRecordCompare function will correctly compare two encoded
+** records. Refer to source code comments for the details of the record
+** format.
+**
+** The original stack entries are popped from the stack if P1>0 but
+** remain on the stack if P1<0.
+**
+** The P2 argument is divided into two 16-bit words before it is processed.
+** If the hi-word is non-zero, then an extra integer is read from the stack
+** and appended to the record as a varint. If the low-word of P2 is not
+** zero and one or more of the entries are NULL, then jump to the value of
+** the low-word of P2. This feature can be used to skip a uniqueness test
+** on indices.
+**
+** P3 may be a string that is P1 characters long. The nth character of the
+** string indicates the column affinity that should be used for the nth
+** field of the index key (i.e. the first character of P3 corresponds to the
+** lowest element on the stack).
+**
+** Character Column affinity
+** ------------------------------
+** 'n' NUMERIC
+** 'i' INTEGER
+** 't' TEXT
+** 'o' NONE
+**
+** If P3 is NULL then all index fields have the affinity NONE.
+*/
+case OP_MakeRecord: {
+ /* Assuming the record contains N fields, the record format looks
+ ** like this:
+ **
+ ** ------------------------------------------------------------------------
+ ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
+ ** ------------------------------------------------------------------------
+ **
+ ** Data(0) is taken from the lowest element of the stack and data(N-1) is
+ ** the top of the stack.
+ **
+ ** Each type field is a varint representing the serial type of the
+ ** corresponding data element (see sqlite3VdbeSerialType()). The
+ ** hdr-size field is also a varint which is the offset from the beginning
+ ** of the record to data0.
+ */
+ unsigned char *zNewRecord;
+ unsigned char *zCsr;
+ Mem *pRec;
+ Mem *pRowid = 0;
+ int nData = 0; /* Number of bytes of data space */
+ int nHdr = 0; /* Number of bytes of header space */
+ int nByte = 0; /* Space required for this record */
+ u32 serial_type; /* Type field */
+ int containsNull = 0; /* True if any of the data fields are NULL */
+ char zTemp[NBFS]; /* Space to hold small records */
+ Mem *pData0;
+
+ int leaveOnStack; /* If true, leave the entries on the stack */
+ int nField; /* Number of fields in the record */
+ int jumpIfNull; /* Jump here if non-zero and any entries are NULL. */
+ int addRowid; /* True to append a rowid column at the end */
+ char *zAffinity; /* The affinity string for the record */
+
+ leaveOnStack = ((pOp->p1<0)?1:0);
+ nField = pOp->p1 * (leaveOnStack?-1:1);
+ jumpIfNull = (pOp->p2 & 0x00FFFFFF);
+ addRowid = ((pOp->p2>>24) & 0x0000FFFF)?1:0;
+ zAffinity = pOp->p3;
+
+ pData0 = &pTos[1-nField];
+ assert( pData0>=p->aStack );
+ containsNull = 0;
+
+ /* Loop through the elements that will make up the record to figure
+ ** out how much space is required for the new record.
+ */
+ for(pRec=pData0; pRec<=pTos; pRec++){
+ if( zAffinity ){
+ applyAffinity(pRec, zAffinity[pRec-pData0], db->enc);
+ }
+ if( pRec->flags&MEM_Null ){
+ containsNull = 1;
+ }
+ serial_type = sqlite3VdbeSerialType(pRec);
+ nData += sqlite3VdbeSerialTypeLen(serial_type);
+ nHdr += sqlite3VarintLen(serial_type);
+ }
+
+ /* If we have to append a varint rowid to this record, set 'rowid'
+ ** to the value of the rowid and increase nByte by the amount of space
+ ** required to store it and the 0x00 seperator byte.
+ */
+ if( addRowid ){
+ pRowid = &pTos[0-nField];
+ assert( pRowid>=p->aStack );
+ Integerify(pRowid);
+ serial_type = sqlite3VdbeSerialType(pRowid);
+ nData += sqlite3VdbeSerialTypeLen(serial_type);
+ nHdr += sqlite3VarintLen(serial_type);
+ }
+
+ /* Add the initial header varint and total the size */
+ nHdr += sqlite3VarintLen(nHdr);
+ nByte = nHdr+nData;
+
+ /* Allocate space for the new record. */
+ if( nByte>sizeof(zTemp) ){
+ zNewRecord = sqliteMallocRaw(nByte);
+ if( !zNewRecord ){
+ goto no_mem;
+ }
+ }else{
+ zNewRecord = zTemp;
+ }
+
+ /* Write the record */
+ zCsr = zNewRecord;
+ zCsr += sqlite3PutVarint(zCsr, nHdr);
+ for(pRec=pData0; pRec<=pTos; pRec++){
+ serial_type = sqlite3VdbeSerialType(pRec);
+ zCsr += sqlite3PutVarint(zCsr, serial_type); /* serial type */
+ }
+ if( addRowid ){
+ zCsr += sqlite3PutVarint(zCsr, sqlite3VdbeSerialType(pRowid));
+ }
+ for(pRec=pData0; pRec<=pTos; pRec++){
+ zCsr += sqlite3VdbeSerialPut(zCsr, pRec); /* serial data */
+ }
+ if( addRowid ){
+ zCsr += sqlite3VdbeSerialPut(zCsr, pRowid);
+ }
+
+ /* If zCsr has not been advanced exactly nByte bytes, then one
+ ** of the sqlite3PutVarint() or sqlite3VdbeSerialPut() calls above
+ ** failed. This indicates a corrupted memory cell or code bug.
+ */
+ if( zCsr!=(zNewRecord+nByte) ){
+ rc = SQLITE_INTERNAL;
+ goto abort_due_to_error;
+ }
+
+ /* Pop entries off the stack if required. Push the new record on. */
+ if( !leaveOnStack ){
+ popStack(&pTos, nField+addRowid);
+ }
+ pTos++;
+ pTos->n = nByte;
+ if( nByte<=sizeof(zTemp) ){
+ assert( zNewRecord==(unsigned char *)zTemp );
+ pTos->z = pTos->zShort;
+ memcpy(pTos->zShort, zTemp, nByte);
+ pTos->flags = MEM_Blob | MEM_Short;
+ }else{
+ assert( zNewRecord!=(unsigned char *)zTemp );
+ pTos->z = zNewRecord;
+ pTos->flags = MEM_Blob | MEM_Dyn;
+ pTos->xDel = 0;
+ }
+
+ /* If a NULL was encountered and jumpIfNull is non-zero, take the jump. */
+ if( jumpIfNull && containsNull ){
+ pc = jumpIfNull - 1;
+ }
+ break;
+}
+
+/* Opcode: Statement P1 * *
+**
+** Begin an individual statement transaction which is part of a larger
+** BEGIN..COMMIT transaction. This is needed so that the statement
+** can be rolled back after an error without having to roll back the
+** entire transaction. The statement transaction will automatically
+** commit when the VDBE halts.
+**
+** The statement is begun on the database file with index P1. The main
+** database file has an index of 0 and the file used for temporary tables
+** has an index of 1.
+*/
+case OP_Statement: {
+ int i = pOp->p1;
+ Btree *pBt;
+ if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt) && !(db->autoCommit) ){
+ assert( sqlite3BtreeIsInTrans(pBt) );
+ if( !sqlite3BtreeIsInStmt(pBt) ){
+ rc = sqlite3BtreeBeginStmt(pBt);
+ }
+ }
+ break;
+}
+
+/* Opcode: AutoCommit P1 P2 *
+**
+** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll
+** back any currently active btree transactions. If there are any active
+** VMs (apart from this one), then the COMMIT or ROLLBACK statement fails.
+**
+** This instruction causes the VM to halt.
+*/
+case OP_AutoCommit: {
+ u8 i = pOp->p1;
+ u8 rollback = pOp->p2;
+
+ assert( i==1 || i==0 );
+ assert( i==1 || rollback==0 );
+
+ assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
+
+ if( db->activeVdbeCnt>1 && i && !db->autoCommit ){
+ /* If this instruction implements a COMMIT or ROLLBACK, other VMs are
+ ** still running, and a transaction is active, return an error indicating
+ ** that the other VMs must complete first.
+ */
+ sqlite3SetString(&p->zErrMsg, "cannot ", rollback?"rollback":"commit",
+ " transaction - SQL statements in progress", 0);
+ rc = SQLITE_ERROR;
+ }else if( i!=db->autoCommit ){
+ db->autoCommit = i;
+ if( pOp->p2 ){
+ assert( i==1 );
+ sqlite3RollbackAll(db);
+ }else if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
+ p->pTos = pTos;
+ p->pc = pc;
+ db->autoCommit = 1-i;
+ p->rc = SQLITE_BUSY;
+ return SQLITE_BUSY;
+ }
+ return SQLITE_DONE;
+ }else{
+ sqlite3SetString(&p->zErrMsg,
+ (!i)?"cannot start a transaction within a transaction":(
+ (rollback)?"cannot rollback - no transaction is active":
+ "cannot commit - no transaction is active"), 0);
+
+ rc = SQLITE_ERROR;
+ }
+ break;
+}
+
+/* Opcode: Transaction P1 P2 *
+**
+** Begin a transaction. The transaction ends when a Commit or Rollback
+** opcode is encountered. Depending on the ON CONFLICT setting, the
+** transaction might also be rolled back if an error is encountered.
+**
+** P1 is the index of the database file on which the transaction is
+** started. Index 0 is the main database file and index 1 is the
+** file used for temporary tables.
+**
+** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is
+** obtained on the database file when a write-transaction is started. No
+** other process can start another write transaction while this transaction is
+** underway. Starting a write transaction also creates a rollback journal. A
+** write transaction must be started before any changes can be made to the
+** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
+** on the file.
+**
+** If P2 is zero, then a read-lock is obtained on the database file.
+*/
+case OP_Transaction: {
+ int i = pOp->p1;
+ Btree *pBt;
+
+ assert( i>=0 && i<db->nDb );
+ pBt = db->aDb[i].pBt;
+
+ if( pBt ){
+ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
+ if( rc==SQLITE_BUSY ){
+ p->pc = pc;
+ p->rc = SQLITE_BUSY;
+ p->pTos = pTos;
+ return SQLITE_BUSY;
+ }
+ if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){
+ goto abort_due_to_error;
+ }
+ }
+ break;
+}
+
+/* Opcode: ReadCookie P1 P2 *
+**
+** Read cookie number P2 from database P1 and push it onto the stack.
+** P2==0 is the schema version. P2==1 is the database format.
+** P2==2 is the recommended pager cache size, and so forth. P1==0 is
+** the main database file and P1==1 is the database file used to store
+** temporary tables.
+**
+** There must be a read-lock on the database (either a transaction
+** must be started or there must be an open cursor) before
+** executing this instruction.
+*/
+case OP_ReadCookie: {
+ int iMeta;
+ assert( pOp->p2<SQLITE_N_BTREE_META );
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( db->aDb[pOp->p1].pBt!=0 );
+ /* The indexing of meta values at the schema layer is off by one from
+ ** the indexing in the btree layer. The btree considers meta[0] to
+ ** be the number of free pages in the database (a read-only value)
+ ** and meta[1] to be the schema cookie. The schema layer considers
+ ** meta[1] to be the schema cookie. So we have to shift the index
+ ** by one in the following statement.
+ */
+ rc = sqlite3BtreeGetMeta(db->aDb[pOp->p1].pBt, 1 + pOp->p2, (u32 *)&iMeta);
+ pTos++;
+ pTos->i = iMeta;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: SetCookie P1 P2 *
+**
+** Write the top of the stack into cookie number P2 of database P1.
+** P2==0 is the schema version. P2==1 is the database format.
+** P2==2 is the recommended pager cache size, and so forth. P1==0 is
+** the main database file and P1==1 is the database file used to store
+** temporary tables.
+**
+** A transaction must be started before executing this opcode.
+*/
+case OP_SetCookie: {
+ Db *pDb;
+ assert( pOp->p2<SQLITE_N_BTREE_META );
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ pDb = &db->aDb[pOp->p1];
+ assert( pDb->pBt!=0 );
+ assert( pTos>=p->aStack );
+ Integerify(pTos);
+ /* See note about index shifting on OP_ReadCookie */
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, 1+pOp->p2, (int)pTos->i);
+ if( pOp->p2==0 ){
+ /* When the schema cookie changes, record the new cookie internally */
+ pDb->schema_cookie = pTos->i;
+ db->flags |= SQLITE_InternChanges;
+ }
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ break;
+}
+
+/* Opcode: VerifyCookie P1 P2 *
+**
+** Check the value of global database parameter number 0 (the
+** schema version) and make sure it is equal to P2.
+** P1 is the database number which is 0 for the main database file
+** and 1 for the file holding temporary tables and some higher number
+** for auxiliary databases.
+**
+** The cookie changes its value whenever the database schema changes.
+** This operation is used to detect when that the cookie has changed
+** and that the current process needs to reread the schema.
+**
+** Either a transaction needs to have been started or an OP_Open needs
+** to be executed (to establish a read lock) before this opcode is
+** invoked.
+*/
+case OP_VerifyCookie: {
+ int iMeta;
+ Btree *pBt;
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ pBt = db->aDb[pOp->p1].pBt;
+ if( pBt ){
+ rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&iMeta);
+ }else{
+ rc = SQLITE_OK;
+ iMeta = 0;
+ }
+ if( rc==SQLITE_OK && iMeta!=pOp->p2 ){
+ sqlite3SetString(&p->zErrMsg, "database schema has changed", (char*)0);
+ rc = SQLITE_SCHEMA;
+ }
+ break;
+}
+
+/* Opcode: OpenRead P1 P2 P3
+**
+** Open a read-only cursor for the database table whose root page is
+** P2 in a database file. The database file is determined by an
+** integer from the top of the stack. 0 means the main database and
+** 1 means the database used for temporary tables. Give the new
+** cursor an identifier of P1. The P1 values need not be contiguous
+** but all P1 values should be small integers. It is an error for
+** P1 to be negative.
+**
+** If P2==0 then take the root page number from the next of the stack.
+**
+** There will be a read lock on the database whenever there is an
+** open cursor. If the database was unlocked prior to this instruction
+** then a read lock is acquired as part of this instruction. A read
+** lock allows other processes to read the database but prohibits
+** any other process from modifying the database. The read lock is
+** released when all cursors are closed. If this instruction attempts
+** to get a read lock but fails, the script terminates with an
+** SQLITE_BUSY error code.
+**
+** The P3 value is a pointer to a KeyInfo structure that defines the
+** content and collating sequence of indices. P3 is NULL for cursors
+** that are not pointing to indices.
+**
+** See also OpenWrite.
+*/
+/* Opcode: OpenWrite P1 P2 P3
+**
+** Open a read/write cursor named P1 on the table or index whose root
+** page is P2. If P2==0 then take the root page number from the stack.
+**
+** The P3 value is a pointer to a KeyInfo structure that defines the
+** content and collating sequence of indices. P3 is NULL for cursors
+** that are not pointing to indices.
+**
+** This instruction works just like OpenRead except that it opens the cursor
+** in read/write mode. For a given table, there can be one or more read-only
+** cursors or a single read/write cursor but not both.
+**
+** See also OpenRead.
+*/
+case OP_OpenRead:
+case OP_OpenWrite: {
+ int i = pOp->p1;
+ int p2 = pOp->p2;
+ int wrFlag;
+ Btree *pX;
+ int iDb;
+ Cursor *pCur;
+
+ assert( pTos>=p->aStack );
+ Integerify(pTos);
+ iDb = pTos->i;
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ assert( iDb>=0 && iDb<db->nDb );
+ pX = db->aDb[iDb].pBt;
+ assert( pX!=0 );
+ wrFlag = pOp->opcode==OP_OpenWrite;
+ if( p2<=0 ){
+ assert( pTos>=p->aStack );
+ Integerify(pTos);
+ p2 = pTos->i;
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ if( p2<2 ){
+ sqlite3SetString(&p->zErrMsg, "root page number less than 2", (char*)0);
+ rc = SQLITE_INTERNAL;
+ break;
+ }
+ }
+ assert( i>=0 );
+ pCur = allocateCursor(p, i);
+ if( pCur==0 ) goto no_mem;
+ pCur->nullRow = 1;
+ if( pX==0 ) break;
+ /* We always provide a key comparison function. If the table being
+ ** opened is of type INTKEY, the comparision function will be ignored. */
+ rc = sqlite3BtreeCursor(pX, p2, wrFlag,
+ sqlite3VdbeRecordCompare, pOp->p3,
+ &pCur->pCursor);
+ pCur->pKeyInfo = (KeyInfo*)pOp->p3;
+ if( pCur->pKeyInfo ){
+ pCur->pIncrKey = &pCur->pKeyInfo->incrKey;
+ pCur->pKeyInfo->enc = p->db->enc;
+ }else{
+ pCur->pIncrKey = &pCur->bogusIncrKey;
+ }
+ switch( rc ){
+ case SQLITE_BUSY: {
+ p->pc = pc;
+ p->rc = SQLITE_BUSY;
+ p->pTos = &pTos[1 + (pOp->p2<=0)]; /* Operands must remain on stack */
+ return SQLITE_BUSY;
+ }
+ case SQLITE_OK: {
+ int flags = sqlite3BtreeFlags(pCur->pCursor);
+ pCur->intKey = (flags & BTREE_INTKEY)!=0;
+ pCur->zeroData = (flags & BTREE_ZERODATA)!=0;
+ break;
+ }
+ case SQLITE_EMPTY: {
+ rc = SQLITE_OK;
+ break;
+ }
+ default: {
+ goto abort_due_to_error;
+ }
+ }
+ break;
+}
+
+/* Opcode: OpenTemp P1 * P3
+**
+** Open a new cursor to a transient table.
+** The transient cursor is always opened read/write even if
+** the main database is read-only. The transient table is deleted
+** automatically when the cursor is closed.
+**
+** The cursor points to a BTree table if P3==0 and to a BTree index
+** if P3 is not 0. If P3 is not NULL, it points to a KeyInfo structure
+** that defines the format of keys in the index.
+**
+** This opcode is used for tables that exist for the duration of a single
+** SQL statement only. Tables created using CREATE TEMPORARY TABLE
+** are opened using OP_OpenRead or OP_OpenWrite. "Temporary" in the
+** context of this opcode means for the duration of a single SQL statement
+** whereas "Temporary" in the context of CREATE TABLE means for the duration
+** of the connection to the database. Same word; different meanings.
+*/
+case OP_OpenTemp: {
+ int i = pOp->p1;
+ Cursor *pCx;
+ assert( i>=0 );
+ pCx = allocateCursor(p, i);
+ if( pCx==0 ) goto no_mem;
+ pCx->nullRow = 1;
+ rc = sqlite3BtreeFactory(db, 0, 1, TEMP_PAGES, &pCx->pBt);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
+ }
+ if( rc==SQLITE_OK ){
+ /* If a transient index is required, create it by calling
+ ** sqlite3BtreeCreateTable() with the BTREE_ZERODATA flag before
+ ** opening it. If a transient table is required, just use the
+ ** automatically created table with root-page 1 (an INTKEY table).
+ */
+ if( pOp->p3 ){
+ int pgno;
+ assert( pOp->p3type==P3_KEYINFO );
+ rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_ZERODATA);
+ if( rc==SQLITE_OK ){
+ assert( pgno==MASTER_ROOT+1 );
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, 1, sqlite3VdbeRecordCompare,
+ pOp->p3, &pCx->pCursor);
+ pCx->pKeyInfo = (KeyInfo*)pOp->p3;
+ pCx->pKeyInfo->enc = p->db->enc;
+ pCx->pIncrKey = &pCx->pKeyInfo->incrKey;
+ }
+ }else{
+ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, 1, 0, 0, &pCx->pCursor);
+ pCx->intKey = 1;
+ pCx->pIncrKey = &pCx->bogusIncrKey;
+ }
+ }
+ break;
+}
+
+/* Opcode: OpenPseudo P1 * *
+**
+** Open a new cursor that points to a fake table that contains a single
+** row of data. Any attempt to write a second row of data causes the
+** first row to be deleted. All data is deleted when the cursor is
+** closed.
+**
+** A pseudo-table created by this opcode is useful for holding the
+** NEW or OLD tables in a trigger.
+*/
+case OP_OpenPseudo: {
+ int i = pOp->p1;
+ Cursor *pCx;
+ assert( i>=0 );
+ pCx = allocateCursor(p, i);
+ if( pCx==0 ) goto no_mem;
+ pCx->nullRow = 1;
+ pCx->pseudoTable = 1;
+ pCx->pIncrKey = &pCx->bogusIncrKey;
+ break;
+}
+
+/* Opcode: Close P1 * *
+**
+** Close a cursor previously opened as P1. If P1 is not
+** currently open, this instruction is a no-op.
+*/
+case OP_Close: {
+ int i = pOp->p1;
+ if( i>=0 && i<p->nCursor ){
+ sqlite3VdbeFreeCursor(p->apCsr[i]);
+ p->apCsr[i] = 0;
+ }
+ break;
+}
+
+/* Opcode: MoveGe P1 P2 *
+**
+** Pop the top of the stack and use its value as a key. Reposition
+** cursor P1 so that it points to the smallest entry that is greater
+** than or equal to the key that was popped ffrom the stack.
+** If there are no records greater than or equal to the key and P2
+** is not zero, then jump to P2.
+**
+** See also: Found, NotFound, Distinct, MoveLt, MoveGt, MoveLe
+*/
+/* Opcode: MoveGt P1 P2 *
+**
+** Pop the top of the stack and use its value as a key. Reposition
+** cursor P1 so that it points to the smallest entry that is greater
+** than the key from the stack.
+** If there are no records greater than the key and P2 is not zero,
+** then jump to P2.
+**
+** See also: Found, NotFound, Distinct, MoveLt, MoveGe, MoveLe
+*/
+/* Opcode: MoveLt P1 P2 *
+**
+** Pop the top of the stack and use its value as a key. Reposition
+** cursor P1 so that it points to the largest entry that is less
+** than the key from the stack.
+** If there are no records less than the key and P2 is not zero,
+** then jump to P2.
+**
+** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLe
+*/
+/* Opcode: MoveLe P1 P2 *
+**
+** Pop the top of the stack and use its value as a key. Reposition
+** cursor P1 so that it points to the largest entry that is less than
+** or equal to the key that was popped from the stack.
+** If there are no records less than or eqal to the key and P2 is not zero,
+** then jump to P2.
+**
+** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt
+*/
+case OP_MoveLt:
+case OP_MoveLe:
+case OP_MoveGe:
+case OP_MoveGt: {
+ int i = pOp->p1;
+ Cursor *pC;
+
+ assert( pTos>=p->aStack );
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ if( pC->pCursor!=0 ){
+ int res, oc;
+ oc = pOp->opcode;
+ pC->nullRow = 0;
+ *pC->pIncrKey = oc==OP_MoveGt || oc==OP_MoveLe;
+ if( pC->intKey ){
+ i64 iKey;
+ assert( !pOp->p3 );
+ Integerify(pTos);
+ iKey = intToKey(pTos->i);
+ if( pOp->p2==0 && pOp->opcode==OP_MoveGe ){
+ pC->movetoTarget = iKey;
+ pC->deferredMoveto = 1;
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ break;
+ }
+ sqlite3BtreeMoveto(pC->pCursor, 0, (u64)iKey, &res);
+ pC->lastRecno = pTos->i;
+ pC->recnoIsValid = res==0;
+ }else{
+ Stringify(pTos, db->enc);
+ sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res);
+ pC->recnoIsValid = 0;
+ }
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ *pC->pIncrKey = 0;
+ sqlite3_search_count++;
+ if( oc==OP_MoveGe || oc==OP_MoveGt ){
+ if( res<0 ){
+ sqlite3BtreeNext(pC->pCursor, &res);
+ pC->recnoIsValid = 0;
+ }else{
+ res = 0;
+ }
+ }else{
+ assert( oc==OP_MoveLt || oc==OP_MoveLe );
+ if( res>=0 ){
+ sqlite3BtreePrevious(pC->pCursor, &res);
+ pC->recnoIsValid = 0;
+ }else{
+ /* res might be negative because the table is empty. Check to
+ ** see if this is the case.
+ */
+ res = sqlite3BtreeEof(pC->pCursor);
+ }
+ }
+ if( res ){
+ if( pOp->p2>0 ){
+ pc = pOp->p2 - 1;
+ }else{
+ pC->nullRow = 1;
+ }
+ }
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: Distinct P1 P2 *
+**
+** Use the top of the stack as a string key. If a record with that key does
+** not exist in the table of cursor P1, then jump to P2. If the record
+** does already exist, then fall thru. The cursor is left pointing
+** at the record if it exists. The key is not popped from the stack.
+**
+** This operation is similar to NotFound except that this operation
+** does not pop the key from the stack.
+**
+** See also: Found, NotFound, MoveTo, IsUnique, NotExists
+*/
+/* Opcode: Found P1 P2 *
+**
+** Use the top of the stack as a string key. If a record with that key
+** does exist in table of P1, then jump to P2. If the record
+** does not exist, then fall thru. The cursor is left pointing
+** to the record if it exists. The key is popped from the stack.
+**
+** See also: Distinct, NotFound, MoveTo, IsUnique, NotExists
+*/
+/* Opcode: NotFound P1 P2 *
+**
+** Use the top of the stack as a string key. If a record with that key
+** does not exist in table of P1, then jump to P2. If the record
+** does exist, then fall thru. The cursor is left pointing to the
+** record if it exists. The key is popped from the stack.
+**
+** The difference between this operation and Distinct is that
+** Distinct does not pop the key from the stack.
+**
+** See also: Distinct, Found, MoveTo, NotExists, IsUnique
+*/
+case OP_Distinct:
+case OP_NotFound:
+case OP_Found: {
+ int i = pOp->p1;
+ int alreadyExists = 0;
+ Cursor *pC;
+ assert( pTos>=p->aStack );
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ if( (pC = p->apCsr[i])->pCursor!=0 ){
+ int res, rx;
+ assert( pC->intKey==0 );
+ Stringify(pTos, db->enc);
+ rx = sqlite3BtreeMoveto(pC->pCursor, pTos->z, pTos->n, &res);
+ alreadyExists = rx==SQLITE_OK && res==0;
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ }
+ if( pOp->opcode==OP_Found ){
+ if( alreadyExists ) pc = pOp->p2 - 1;
+ }else{
+ if( !alreadyExists ) pc = pOp->p2 - 1;
+ }
+ if( pOp->opcode!=OP_Distinct ){
+ Release(pTos);
+ pTos--;
+ }
+ break;
+}
+
+/* Opcode: IsUnique P1 P2 *
+**
+** The top of the stack is an integer record number. Call this
+** record number R. The next on the stack is an index key created
+** using MakeIdxKey. Call it K. This instruction pops R from the
+** stack but it leaves K unchanged.
+**
+** P1 is an index. So it has no data and its key consists of a
+** record generated by OP_MakeIdxKey. This key contains one or more
+** fields followed by a ROWID field.
+**
+** This instruction asks if there is an entry in P1 where the
+** fields matches K but the rowid is different from R.
+** If there is no such entry, then there is an immediate
+** jump to P2. If any entry does exist where the index string
+** matches K but the record number is not R, then the record
+** number for that entry is pushed onto the stack and control
+** falls through to the next instruction.
+**
+** See also: Distinct, NotFound, NotExists, Found
+*/
+case OP_IsUnique: {
+ int i = pOp->p1;
+ Mem *pNos = &pTos[-1];
+ Cursor *pCx;
+ BtCursor *pCrsr;
+ i64 R;
+
+ /* Pop the value R off the top of the stack
+ */
+ assert( pNos>=p->aStack );
+ Integerify(pTos);
+ R = pTos->i;
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ assert( i>=0 && i<=p->nCursor );
+ pCx = p->apCsr[i];
+ assert( pCx!=0 );
+ pCrsr = pCx->pCursor;
+ if( pCrsr!=0 ){
+ int res, rc;
+ i64 v; /* The record number on the P1 entry that matches K */
+ char *zKey; /* The value of K */
+ int nKey; /* Number of bytes in K */
+ int len; /* Number of bytes in K without the rowid at the end */
+ int szRowid; /* Size of the rowid column at the end of zKey */
+
+ /* Make sure K is a string and make zKey point to K
+ */
+ Stringify(pNos, db->enc);
+ zKey = pNos->z;
+ nKey = pNos->n;
+
+ szRowid = sqlite3VdbeIdxRowidLen(nKey, zKey);
+ len = nKey-szRowid;
+
+ /* Search for an entry in P1 where all but the last four bytes match K.
+ ** If there is no such entry, jump immediately to P2.
+ */
+ assert( pCx->deferredMoveto==0 );
+ pCx->cacheValid = 0;
+ rc = sqlite3BtreeMoveto(pCrsr, zKey, len, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ if( res<0 ){
+ rc = sqlite3BtreeNext(pCrsr, &res);
+ if( res ){
+ pc = pOp->p2 - 1;
+ break;
+ }
+ }
+ rc = sqlite3VdbeIdxKeyCompare(pCx, len, zKey, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ if( res>0 ){
+ pc = pOp->p2 - 1;
+ break;
+ }
+
+ /* At this point, pCrsr is pointing to an entry in P1 where all but
+ ** the final entry (the rowid) matches K. Check to see if the
+ ** final rowid column is different from R. If it equals R then jump
+ ** immediately to P2.
+ */
+ rc = sqlite3VdbeIdxRowid(pCrsr, &v);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( v==R ){
+ pc = pOp->p2 - 1;
+ break;
+ }
+
+ /* The final varint of the key is different from R. Push it onto
+ ** the stack. (The record number of an entry that violates a UNIQUE
+ ** constraint.)
+ */
+ pTos++;
+ pTos->i = v;
+ pTos->flags = MEM_Int;
+ }
+ break;
+}
+
+/* Opcode: NotExists P1 P2 *
+**
+** Use the top of the stack as a integer key. If a record with that key
+** does not exist in table of P1, then jump to P2. If the record
+** does exist, then fall thru. The cursor is left pointing to the
+** record if it exists. The integer key is popped from the stack.
+**
+** The difference between this operation and NotFound is that this
+** operation assumes the key is an integer and NotFound assumes it
+** is a string.
+**
+** See also: Distinct, Found, MoveTo, NotFound, IsUnique
+*/
+case OP_NotExists: {
+ int i = pOp->p1;
+ Cursor *pC;
+ BtCursor *pCrsr;
+ assert( pTos>=p->aStack );
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ int res, rx;
+ u64 iKey;
+ assert( pTos->flags & MEM_Int );
+ assert( p->apCsr[i]->intKey );
+ iKey = intToKey(pTos->i);
+ rx = sqlite3BtreeMoveto(pCrsr, 0, iKey, &res);
+ pC->lastRecno = pTos->i;
+ pC->recnoIsValid = res==0;
+ pC->nullRow = 0;
+ pC->cacheValid = 0;
+ if( rx!=SQLITE_OK || res!=0 ){
+ pc = pOp->p2 - 1;
+ pC->recnoIsValid = 0;
+ }
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: NewRecno P1 * *
+**
+** Get a new integer record number used as the key to a table.
+** The record number is not previously used as a key in the database
+** table that cursor P1 points to. The new record number is pushed
+** onto the stack.
+*/
+case OP_NewRecno: {
+ int i = pOp->p1;
+ i64 v = 0;
+ Cursor *pC;
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ if( (pC = p->apCsr[i])->pCursor==0 ){
+ /* The zero initialization above is all that is needed */
+ }else{
+ /* The next rowid or record number (different terms for the same
+ ** thing) is obtained in a two-step algorithm.
+ **
+ ** First we attempt to find the largest existing rowid and add one
+ ** to that. But if the largest existing rowid is already the maximum
+ ** positive integer, we have to fall through to the second
+ ** probabilistic algorithm
+ **
+ ** The second algorithm is to select a rowid at random and see if
+ ** it already exists in the table. If it does not exist, we have
+ ** succeeded. If the random rowid does exist, we select a new one
+ ** and try again, up to 1000 times.
+ **
+ ** For a table with less than 2 billion entries, the probability
+ ** of not finding a unused rowid is about 1.0e-300. This is a
+ ** non-zero probability, but it is still vanishingly small and should
+ ** never cause a problem. You are much, much more likely to have a
+ ** hardware failure than for this algorithm to fail.
+ **
+ ** The analysis in the previous paragraph assumes that you have a good
+ ** source of random numbers. Is a library function like lrand48()
+ ** good enough? Maybe. Maybe not. It's hard to know whether there
+ ** might be subtle bugs is some implementations of lrand48() that
+ ** could cause problems. To avoid uncertainty, SQLite uses its own
+ ** random number generator based on the RC4 algorithm.
+ **
+ ** To promote locality of reference for repetitive inserts, the
+ ** first few attempts at chosing a random rowid pick values just a little
+ ** larger than the previous rowid. This has been shown experimentally
+ ** to double the speed of the COPY operation.
+ */
+ int res, rx=SQLITE_OK, cnt;
+ i64 x;
+ cnt = 0;
+ assert( (sqlite3BtreeFlags(pC->pCursor) & BTREE_INTKEY)!=0 );
+ assert( (sqlite3BtreeFlags(pC->pCursor) & BTREE_ZERODATA)==0 );
+ if( !pC->useRandomRowid ){
+ if( pC->nextRowidValid ){
+ v = pC->nextRowid;
+ }else{
+ rx = sqlite3BtreeLast(pC->pCursor, &res);
+ if( res ){
+ v = 1;
+ }else{
+ sqlite3BtreeKeySize(pC->pCursor, &v);
+ v = keyToInt(v);
+ if( v==0x7fffffffffffffff ){
+ pC->useRandomRowid = 1;
+ }else{
+ v++;
+ }
+ }
+ }
+ if( v<0x7fffffffffffffff ){
+ pC->nextRowidValid = 1;
+ pC->nextRowid = v+1;
+ }else{
+ pC->nextRowidValid = 0;
+ }
+ }
+ if( pC->useRandomRowid ){
+ v = db->priorNewRowid;
+ cnt = 0;
+ do{
+ if( v==0 || cnt>2 ){
+ sqlite3Randomness(sizeof(v), &v);
+ if( cnt<5 ) v &= 0xffffff;
+ }else{
+ unsigned char r;
+ sqlite3Randomness(1, &r);
+ v += r + 1;
+ }
+ if( v==0 ) continue;
+ x = intToKey(v);
+ rx = sqlite3BtreeMoveto(pC->pCursor, 0, (u64)x, &res);
+ cnt++;
+ }while( cnt<1000 && rx==SQLITE_OK && res==0 );
+ db->priorNewRowid = v;
+ if( rx==SQLITE_OK && res==0 ){
+ rc = SQLITE_FULL;
+ goto abort_due_to_error;
+ }
+ }
+ pC->recnoIsValid = 0;
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ }
+ pTos++;
+ pTos->i = v;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: PutIntKey P1 P2 *
+**
+** Write an entry into the table of cursor P1. A new entry is
+** created if it doesn't already exist or the data for an existing
+** entry is overwritten. The data is the value on the top of the
+** stack. The key is the next value down on the stack. The key must
+** be an integer. The stack is popped twice by this instruction.
+**
+** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
+** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P2 is set,
+** then rowid is stored for subsequent return by the
+** sqlite3_last_insert_rowid() function (otherwise it's unmodified).
+*/
+/* Opcode: PutStrKey P1 * *
+**
+** Write an entry into the table of cursor P1. A new entry is
+** created if it doesn't already exist or the data for an existing
+** entry is overwritten. The data is the value on the top of the
+** stack. The key is the next value down on the stack. The key must
+** be a string. The stack is popped twice by this instruction.
+**
+** P1 may not be a pseudo-table opened using the OpenPseudo opcode.
+*/
+case OP_PutIntKey:
+case OP_PutStrKey: {
+ Mem *pNos = &pTos[-1];
+ int i = pOp->p1;
+ Cursor *pC;
+ assert( pNos>=p->aStack );
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ if( ((pC = p->apCsr[i])->pCursor!=0 || pC->pseudoTable) ){
+ char *zKey;
+ i64 nKey;
+ i64 iKey;
+ if( pOp->opcode==OP_PutStrKey ){
+ Stringify(pNos, db->enc);
+ nKey = pNos->n;
+ zKey = pNos->z;
+ }else{
+ assert( pNos->flags & MEM_Int );
+
+ /* If the table is an INTKEY table, set nKey to the value of
+ ** the integer key, and zKey to NULL. Otherwise, set nKey to
+ ** sizeof(i64) and point zKey at iKey. iKey contains the integer
+ ** key in the on-disk byte order.
+ */
+ iKey = intToKey(pNos->i);
+ if( pC->intKey ){
+ nKey = intToKey(pNos->i);
+ zKey = 0;
+ }else{
+ nKey = sizeof(i64);
+ zKey = (char*)&iKey;
+ }
+
+ if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
+ if( pOp->p2 & OPFLAG_LASTROWID ) db->lastRowid = pNos->i;
+ if( pC->nextRowidValid && pTos->i>=pC->nextRowid ){
+ pC->nextRowidValid = 0;
+ }
+ }
+ if( pTos->flags & MEM_Null ){
+ pTos->z = 0;
+ pTos->n = 0;
+ }else{
+ assert( pTos->flags & (MEM_Blob|MEM_Str) );
+ }
+ if( pC->pseudoTable ){
+ /* PutStrKey does not work for pseudo-tables.
+ ** The following assert makes sure we are not trying to use
+ ** PutStrKey on a pseudo-table
+ */
+ assert( pOp->opcode==OP_PutIntKey );
+ sqliteFree(pC->pData);
+ pC->iKey = iKey;
+ pC->nData = pTos->n;
+ if( pTos->flags & MEM_Dyn ){
+ pC->pData = pTos->z;
+ pTos->flags = MEM_Null;
+ }else{
+ pC->pData = sqliteMallocRaw( pC->nData+2 );
+ if( !pC->pData ) goto no_mem;
+ memcpy(pC->pData, pTos->z, pC->nData);
+ pC->pData[pC->nData] = 0;
+ pC->pData[pC->nData+1] = 0;
+ }
+ pC->nullRow = 0;
+ }else{
+ rc = sqlite3BtreeInsert(pC->pCursor, zKey, nKey, pTos->z, pTos->n);
+ }
+ pC->recnoIsValid = 0;
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ }
+ popStack(&pTos, 2);
+ break;
+}
+
+/* Opcode: Delete P1 P2 *
+**
+** Delete the record at which the P1 cursor is currently pointing.
+**
+** The cursor will be left pointing at either the next or the previous
+** record in the table. If it is left pointing at the next record, then
+** the next Next instruction will be a no-op. Hence it is OK to delete
+** a record from within an Next loop.
+**
+** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
+** incremented (otherwise not).
+**
+** If P1 is a pseudo-table, then this instruction is a no-op.
+*/
+case OP_Delete: {
+ int i = pOp->p1;
+ Cursor *pC;
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ if( pC->pCursor!=0 ){
+ sqlite3VdbeCursorMoveto(pC);
+ rc = sqlite3BtreeDelete(pC->pCursor);
+ pC->nextRowidValid = 0;
+ pC->cacheValid = 0;
+ }
+ if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
+ break;
+}
+
+/* Opcode: ResetCount P1 * *
+**
+** This opcode resets the VMs internal change counter to 0. If P1 is true,
+** then the value of the change counter is copied to the database handle
+** change counter (returned by subsequent calls to sqlite3_changes())
+** before it is reset. This is used by trigger programs.
+*/
+case OP_ResetCount: {
+ if( pOp->p1 ){
+ sqlite3VdbeSetChanges(db, p->nChange);
+ }
+ p->nChange = 0;
+ break;
+}
+
+/* Opcode: KeyAsData P1 P2 *
+**
+** Turn the key-as-data mode for cursor P1 either on (if P2==1) or
+** off (if P2==0). In key-as-data mode, the OP_Column opcode pulls
+** data off of the key rather than the data. This is used for
+** processing compound selects.
+*/
+case OP_KeyAsData: {
+ int i = pOp->p1;
+ Cursor *pC;
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ pC->keyAsData = pOp->p2;
+ break;
+}
+
+/* Opcode: RowData P1 * *
+**
+** Push onto the stack the complete row data for cursor P1.
+** There is no interpretation of the data. It is just copied
+** onto the stack exactly as it is found in the database file.
+**
+** If the cursor is not pointing to a valid row, a NULL is pushed
+** onto the stack.
+*/
+/* Opcode: RowKey P1 * *
+**
+** Push onto the stack the complete row key for cursor P1.
+** There is no interpretation of the key. It is just copied
+** onto the stack exactly as it is found in the database file.
+**
+** If the cursor is not pointing to a valid row, a NULL is pushed
+** onto the stack.
+*/
+case OP_RowKey:
+case OP_RowData: {
+ int i = pOp->p1;
+ Cursor *pC;
+ u32 n;
+
+ pTos++;
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ if( pC->nullRow ){
+ pTos->flags = MEM_Null;
+ }else if( pC->pCursor!=0 ){
+ BtCursor *pCrsr = pC->pCursor;
+ sqlite3VdbeCursorMoveto(pC);
+ if( pC->nullRow ){
+ pTos->flags = MEM_Null;
+ break;
+ }else if( pC->keyAsData || pOp->opcode==OP_RowKey ){
+ i64 n64;
+ assert( !pC->intKey );
+ sqlite3BtreeKeySize(pCrsr, &n64);
+ n = n64;
+ }else{
+ sqlite3BtreeDataSize(pCrsr, &n);
+ }
+ pTos->n = n;
+ if( n<=NBFS ){
+ pTos->flags = MEM_Blob | MEM_Short;
+ pTos->z = pTos->zShort;
+ }else{
+ char *z = sqliteMallocRaw( n );
+ if( z==0 ) goto no_mem;
+ pTos->flags = MEM_Blob | MEM_Dyn;
+ pTos->xDel = 0;
+ pTos->z = z;
+ }
+ if( pC->keyAsData || pOp->opcode==OP_RowKey ){
+ sqlite3BtreeKey(pCrsr, 0, n, pTos->z);
+ }else{
+ sqlite3BtreeData(pCrsr, 0, n, pTos->z);
+ }
+ }else if( pC->pseudoTable ){
+ pTos->n = pC->nData;
+ pTos->z = pC->pData;
+ pTos->flags = MEM_Blob|MEM_Ephem;
+ }else{
+ pTos->flags = MEM_Null;
+ }
+ break;
+}
+
+/* Opcode: Recno P1 * *
+**
+** Push onto the stack an integer which is the first 4 bytes of the
+** the key to the current entry in a sequential scan of the database
+** file P1. The sequential scan should have been started using the
+** Next opcode.
+*/
+case OP_Recno: {
+ int i = pOp->p1;
+ Cursor *pC;
+ i64 v;
+
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ sqlite3VdbeCursorMoveto(pC);
+ pTos++;
+ if( pC->recnoIsValid ){
+ v = pC->lastRecno;
+ }else if( pC->pseudoTable ){
+ v = keyToInt(pC->iKey);
+ }else if( pC->nullRow || pC->pCursor==0 ){
+ pTos->flags = MEM_Null;
+ break;
+ }else{
+ assert( pC->pCursor!=0 );
+ sqlite3BtreeKeySize(pC->pCursor, &v);
+ v = keyToInt(v);
+ }
+ pTos->i = v;
+ pTos->flags = MEM_Int;
+ break;
+}
+
+/* Opcode: FullKey P1 * *
+**
+** Extract the complete key from the record that cursor P1 is currently
+** pointing to and push the key onto the stack as a string.
+**
+** Compare this opcode to Recno. The Recno opcode extracts the first
+** 4 bytes of the key and pushes those bytes onto the stack as an
+** integer. This instruction pushes the entire key as a string.
+**
+** This opcode may not be used on a pseudo-table.
+*/
+case OP_FullKey: {
+ int i = pOp->p1;
+ BtCursor *pCrsr;
+ Cursor *pC;
+
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ assert( p->apCsr[i]->keyAsData );
+ assert( !p->apCsr[i]->pseudoTable );
+ pTos++;
+ pTos->flags = MEM_Null;
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ i64 amt;
+ char *z;
+
+ sqlite3VdbeCursorMoveto(pC);
+ assert( pC->intKey==0 );
+ sqlite3BtreeKeySize(pCrsr, &amt);
+ if( amt<=0 ){
+ rc = SQLITE_CORRUPT;
+ goto abort_due_to_error;
+ }
+ if( amt>NBFS ){
+ z = sqliteMallocRaw( amt );
+ if( z==0 ) goto no_mem;
+ pTos->flags = MEM_Blob | MEM_Dyn;
+ pTos->xDel = 0;
+ }else{
+ z = pTos->zShort;
+ pTos->flags = MEM_Blob | MEM_Short;
+ }
+ sqlite3BtreeKey(pCrsr, 0, amt, z);
+ pTos->z = z;
+ pTos->n = amt;
+ }
+ break;
+}
+
+/* Opcode: NullRow P1 * *
+**
+** Move the cursor P1 to a null row. Any OP_Column operations
+** that occur while the cursor is on the null row will always push
+** a NULL onto the stack.
+*/
+case OP_NullRow: {
+ int i = pOp->p1;
+ Cursor *pC;
+
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ pC->nullRow = 1;
+ pC->recnoIsValid = 0;
+ break;
+}
+
+/* Opcode: Last P1 P2 *
+**
+** The next use of the Recno or Column or Next instruction for P1
+** will refer to the last entry in the database table or index.
+** If the table or index is empty and P2>0, then jump immediately to P2.
+** If P2 is 0 or if the table or index is not empty, fall through
+** to the following instruction.
+*/
+case OP_Last: {
+ int i = pOp->p1;
+ Cursor *pC;
+ BtCursor *pCrsr;
+
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ if( (pCrsr = pC->pCursor)!=0 ){
+ int res;
+ rc = sqlite3BtreeLast(pCrsr, &res);
+ pC->nullRow = res;
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ if( res && pOp->p2>0 ){
+ pc = pOp->p2 - 1;
+ }
+ }else{
+ pC->nullRow = 0;
+ }
+ break;
+}
+
+/* Opcode: Rewind P1 P2 *
+**
+** The next use of the Recno or Column or Next instruction for P1
+** will refer to the first entry in the database table or index.
+** If the table or index is empty and P2>0, then jump immediately to P2.
+** If P2 is 0 or if the table or index is not empty, fall through
+** to the following instruction.
+*/
+case OP_Rewind: {
+ int i = pOp->p1;
+ Cursor *pC;
+ BtCursor *pCrsr;
+ int res;
+
+ assert( i>=0 && i<p->nCursor );
+ pC = p->apCsr[i];
+ assert( pC!=0 );
+ if( (pCrsr = pC->pCursor)!=0 ){
+ rc = sqlite3BtreeFirst(pCrsr, &res);
+ pC->atFirst = res==0;
+ pC->deferredMoveto = 0;
+ pC->cacheValid = 0;
+ }else{
+ res = 1;
+ }
+ pC->nullRow = res;
+ if( res && pOp->p2>0 ){
+ pc = pOp->p2 - 1;
+ }
+ break;
+}
+
+/* Opcode: Next P1 P2 *
+**
+** Advance cursor P1 so that it points to the next key/data pair in its
+** table or index. If there are no more key/value pairs then fall through
+** to the following instruction. But if the cursor advance was successful,
+** jump immediately to P2.
+**
+** See also: Prev
+*/
+/* Opcode: Prev P1 P2 *
+**
+** Back up cursor P1 so that it points to the previous key/data pair in its
+** table or index. If there is no previous key/value pairs then fall through
+** to the following instruction. But if the cursor backup was successful,
+** jump immediately to P2.
+*/
+case OP_Prev:
+case OP_Next: {
+ Cursor *pC;
+ BtCursor *pCrsr;
+
+ CHECK_FOR_INTERRUPT;
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ if( (pCrsr = pC->pCursor)!=0 ){
+ int res;
+ if( pC->nullRow ){
+ res = 1;
+ }else{
+ assert( pC->deferredMoveto==0 );
+ rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(pCrsr, &res) :
+ sqlite3BtreePrevious(pCrsr, &res);
+ pC->nullRow = res;
+ pC->cacheValid = 0;
+ }
+ if( res==0 ){
+ pc = pOp->p2 - 1;
+ sqlite3_search_count++;
+ }
+ }else{
+ pC->nullRow = 1;
+ }
+ pC->recnoIsValid = 0;
+ break;
+}
+
+/* Opcode: IdxPut P1 P2 P3
+**
+** The top of the stack holds a SQL index key made using the
+** MakeIdxKey instruction. This opcode writes that key into the
+** index P1. Data for the entry is nil.
+**
+** If P2==1, then the key must be unique. If the key is not unique,
+** the program aborts with a SQLITE_CONSTRAINT error and the database
+** is rolled back. If P3 is not null, then it becomes part of the
+** error message returned with the SQLITE_CONSTRAINT.
+*/
+case OP_IdxPut: {
+ int i = pOp->p1;
+ Cursor *pC;
+ BtCursor *pCrsr;
+ assert( pTos>=p->aStack );
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ assert( pTos->flags & MEM_Blob );
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ int nKey = pTos->n;
+ const char *zKey = pTos->z;
+ if( pOp->p2 ){
+ int res;
+ int len;
+
+ /* 'len' is the length of the key minus the rowid at the end */
+ len = nKey - sqlite3VdbeIdxRowidLen(nKey, zKey);
+
+ rc = sqlite3BtreeMoveto(pCrsr, zKey, len, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ while( res!=0 && !sqlite3BtreeEof(pCrsr) ){
+ int c;
+ if( sqlite3VdbeIdxKeyCompare(pC, len, zKey, &c)==SQLITE_OK && c==0 ){
+ rc = SQLITE_CONSTRAINT;
+ if( pOp->p3 && pOp->p3[0] ){
+ sqlite3SetString(&p->zErrMsg, pOp->p3, (char*)0);
+ }
+ goto abort_due_to_error;
+ }
+ if( res<0 ){
+ sqlite3BtreeNext(pCrsr, &res);
+ res = +1;
+ }else{
+ break;
+ }
+ }
+ }
+ assert( pC->intKey==0 );
+ rc = sqlite3BtreeInsert(pCrsr, zKey, nKey, "", 0);
+ assert( pC->deferredMoveto==0 );
+ pC->cacheValid = 0;
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: IdxDelete P1 * *
+**
+** The top of the stack is an index key built using the MakeIdxKey opcode.
+** This opcode removes that entry from the index.
+*/
+case OP_IdxDelete: {
+ int i = pOp->p1;
+ Cursor *pC;
+ BtCursor *pCrsr;
+ assert( pTos>=p->aStack );
+ assert( pTos->flags & MEM_Blob );
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ int rx, res;
+ rx = sqlite3BtreeMoveto(pCrsr, pTos->z, pTos->n, &res);
+ if( rx==SQLITE_OK && res==0 ){
+ rc = sqlite3BtreeDelete(pCrsr);
+ }
+ assert( pC->deferredMoveto==0 );
+ pC->cacheValid = 0;
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: IdxRecno P1 * *
+**
+** Push onto the stack an integer which is the varint located at the
+** end of the index key pointed to by cursor P1. These integer should be
+** the record number of the table entry to which this index entry points.
+**
+** See also: Recno, MakeIdxKey.
+*/
+case OP_IdxRecno: {
+ int i = pOp->p1;
+ BtCursor *pCrsr;
+ Cursor *pC;
+
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ pTos++;
+ pTos->flags = MEM_Null;
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ i64 rowid;
+
+ assert( pC->deferredMoveto==0 );
+ assert( pC->intKey==0 );
+ if( pC->nullRow ){
+ pTos->flags = MEM_Null;
+ }else{
+ rc = sqlite3VdbeIdxRowid(pCrsr, &rowid);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ pTos->flags = MEM_Int;
+ pTos->i = rowid;
+ }
+ }
+ break;
+}
+
+/* Opcode: IdxGT P1 P2 *
+**
+** The top of the stack is an index entry that omits the ROWID. Compare
+** the top of stack against the index that P1 is currently pointing to.
+** Ignore the ROWID on the P1 index.
+**
+** The top of the stack might have fewer columns that P1.
+**
+** If the P1 index entry is greater than the top of the stack
+** then jump to P2. Otherwise fall through to the next instruction.
+** In either case, the stack is popped once.
+*/
+/* Opcode: IdxGE P1 P2 P3
+**
+** The top of the stack is an index entry that omits the ROWID. Compare
+** the top of stack against the index that P1 is currently pointing to.
+** Ignore the ROWID on the P1 index.
+**
+** If the P1 index entry is greater than or equal to the top of the stack
+** then jump to P2. Otherwise fall through to the next instruction.
+** In either case, the stack is popped once.
+**
+** If P3 is the "+" string (or any other non-NULL string) then the
+** index taken from the top of the stack is temporarily increased by
+** an epsilon prior to the comparison. This make the opcode work
+** like IdxGT except that if the key from the stack is a prefix of
+** the key in the cursor, the result is false whereas it would be
+** true with IdxGT.
+*/
+/* Opcode: IdxLT P1 P2 P3
+**
+** The top of the stack is an index entry that omits the ROWID. Compare
+** the top of stack against the index that P1 is currently pointing to.
+** Ignore the ROWID on the P1 index.
+**
+** If the P1 index entry is less than the top of the stack
+** then jump to P2. Otherwise fall through to the next instruction.
+** In either case, the stack is popped once.
+**
+** If P3 is the "+" string (or any other non-NULL string) then the
+** index taken from the top of the stack is temporarily increased by
+** an epsilon prior to the comparison. This makes the opcode work
+** like IdxLE.
+*/
+case OP_IdxLT:
+case OP_IdxGT:
+case OP_IdxGE: {
+ int i= pOp->p1;
+ BtCursor *pCrsr;
+ Cursor *pC;
+
+ assert( i>=0 && i<p->nCursor );
+ assert( p->apCsr[i]!=0 );
+ assert( pTos>=p->aStack );
+ if( (pCrsr = (pC = p->apCsr[i])->pCursor)!=0 ){
+ int res, rc;
+
+ assert( pTos->flags & MEM_Blob ); /* Created using OP_Make*Key */
+ Stringify(pTos, db->enc);
+ assert( pC->deferredMoveto==0 );
+ *pC->pIncrKey = pOp->p3!=0;
+ assert( pOp->p3==0 || pOp->opcode!=OP_IdxGT );
+ rc = sqlite3VdbeIdxKeyCompare(pC, pTos->n, pTos->z, &res);
+ *pC->pIncrKey = 0;
+ if( rc!=SQLITE_OK ){
+ break;
+ }
+ if( pOp->opcode==OP_IdxLT ){
+ res = -res;
+ }else if( pOp->opcode==OP_IdxGE ){
+ res++;
+ }
+ if( res>0 ){
+ pc = pOp->p2 - 1 ;
+ }
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: IdxIsNull P1 P2 *
+**
+** The top of the stack contains an index entry such as might be generated
+** by the MakeIdxKey opcode. This routine looks at the first P1 fields of
+** that key. If any of the first P1 fields are NULL, then a jump is made
+** to address P2. Otherwise we fall straight through.
+**
+** The index entry is always popped from the stack.
+*/
+case OP_IdxIsNull: {
+ int i = pOp->p1;
+ int k, n;
+ const char *z;
+ u32 serial_type;
+
+ assert( pTos>=p->aStack );
+ assert( pTos->flags & MEM_Blob );
+ z = pTos->z;
+ n = pTos->n;
+ k = sqlite3GetVarint32(z, &serial_type);
+ for(; k<n && i>0; i--){
+ k += sqlite3GetVarint32(&z[k], &serial_type);
+ if( serial_type==0 ){ /* Serial type 0 is a NULL */
+ pc = pOp->p2-1;
+ break;
+ }
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: Destroy P1 P2 *
+**
+** Delete an entire database table or index whose root page in the database
+** file is given by P1.
+**
+** The table being destroyed is in the main database file if P2==0. If
+** P2==1 then the table to be clear is in the auxiliary database file
+** that is used to store tables create using CREATE TEMPORARY TABLE.
+**
+** See also: Clear
+*/
+case OP_Destroy: {
+ rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1);
+ break;
+}
+
+/* Opcode: Clear P1 P2 *
+**
+** Delete all contents of the database table or index whose root page
+** in the database file is given by P1. But, unlike Destroy, do not
+** remove the table or index from the database file.
+**
+** The table being clear is in the main database file if P2==0. If
+** P2==1 then the table to be clear is in the auxiliary database file
+** that is used to store tables create using CREATE TEMPORARY TABLE.
+**
+** See also: Destroy
+*/
+case OP_Clear: {
+ rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1);
+ break;
+}
+
+/* Opcode: CreateTable P1 * *
+**
+** Allocate a new table in the main database file if P2==0 or in the
+** auxiliary database file if P2==1. Push the page number
+** for the root page of the new table onto the stack.
+**
+** The difference between a table and an index is this: A table must
+** have a 4-byte integer key and can have arbitrary data. An index
+** has an arbitrary key but no data.
+**
+** See also: CreateIndex
+*/
+/* Opcode: CreateIndex P1 * *
+**
+** Allocate a new index in the main database file if P2==0 or in the
+** auxiliary database file if P2==1. Push the page number of the
+** root page of the new index onto the stack.
+**
+** See documentation on OP_CreateTable for additional information.
+*/
+case OP_CreateIndex:
+case OP_CreateTable: {
+ int pgno;
+ int flags;
+ Db *pDb;
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ pDb = &db->aDb[pOp->p1];
+ assert( pDb->pBt!=0 );
+ if( pOp->opcode==OP_CreateTable ){
+ /* flags = BTREE_INTKEY; */
+ flags = BTREE_LEAFDATA|BTREE_INTKEY;
+ }else{
+ flags = BTREE_ZERODATA;
+ }
+ rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
+ pTos++;
+ if( rc==SQLITE_OK ){
+ pTos->i = pgno;
+ pTos->flags = MEM_Int;
+ }else{
+ pTos->flags = MEM_Null;
+ }
+ break;
+}
+
+/* Opcode: ParseSchema P1 * P3
+**
+** Read and parse all entries from the SQLITE_MASTER table of database P1
+** that match the WHERE clause P3.
+**
+** This opcode invokes the parser to create a new virtual machine,
+** then runs the new virtual machine. It is thus a reentrant opcode.
+*/
+case OP_ParseSchema: {
+ char *zSql;
+ int iDb = pOp->p1;
+ const char *zMaster;
+ InitData initData;
+
+ assert( iDb>=0 && iDb<db->nDb );
+ if( !DbHasProperty(db, iDb, DB_SchemaLoaded) ) break;
+ zMaster = iDb==1 ? TEMP_MASTER_NAME : MASTER_NAME;
+ initData.db = db;
+ initData.pzErrMsg = &p->zErrMsg;
+ zSql = sqlite3MPrintf(
+ "SELECT name, rootpage, sql, %d FROM '%q'.%s WHERE %s",
+ pOp->p1, db->aDb[iDb].zName, zMaster, pOp->p3);
+ if( zSql==0 ) goto no_mem;
+ sqlite3SafetyOff(db);
+ assert( db->init.busy==0 );
+ db->init.busy = 1;
+ rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+ db->init.busy = 0;
+ sqlite3SafetyOn(db);
+ sqliteFree(zSql);
+ break;
+}
+
+/* Opcode: DropTable P1 * P3
+**
+** Remove the internal (in-memory) data structures that describe
+** the table named P3 in database P1. This is called after a table
+** is dropped in order to keep the internal representation of the
+** schema consistent with what is on disk.
+*/
+case OP_DropTable: {
+ sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p3);
+ break;
+}
+
+/* Opcode: DropIndex P1 * P3
+**
+** Remove the internal (in-memory) data structures that describe
+** the index named P3 in database P1. This is called after an index
+** is dropped in order to keep the internal representation of the
+** schema consistent with what is on disk.
+*/
+case OP_DropIndex: {
+ sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p3);
+ break;
+}
+
+/* Opcode: DropTrigger P1 * P3
+**
+** Remove the internal (in-memory) data structures that describe
+** the trigger named P3 in database P1. This is called after a trigger
+** is dropped in order to keep the internal representation of the
+** schema consistent with what is on disk.
+*/
+case OP_DropTrigger: {
+ sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p3);
+ break;
+}
+
+
+/* Opcode: IntegrityCk * P2 *
+**
+** Do an analysis of the currently open database. Push onto the
+** stack the text of an error message describing any problems.
+** If there are no errors, push a "ok" onto the stack.
+**
+** The root page numbers of all tables in the database are integer
+** values on the stack. This opcode pulls as many integers as it
+** can off of the stack and uses those numbers as the root pages.
+**
+** If P2 is not zero, the check is done on the auxiliary database
+** file, not the main database file.
+**
+** This opcode is used for testing purposes only.
+*/
+case OP_IntegrityCk: {
+ int nRoot;
+ int *aRoot;
+ int j;
+ char *z;
+
+ for(nRoot=0; &pTos[-nRoot]>=p->aStack; nRoot++){
+ if( (pTos[-nRoot].flags & MEM_Int)==0 ) break;
+ }
+ assert( nRoot>0 );
+ aRoot = sqliteMallocRaw( sizeof(int*)*(nRoot+1) );
+ if( aRoot==0 ) goto no_mem;
+ for(j=0; j<nRoot; j++){
+ Mem *pMem = &pTos[-j];
+ aRoot[j] = pMem->i;
+ }
+ aRoot[j] = 0;
+ popStack(&pTos, nRoot);
+ pTos++;
+ z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot);
+ if( z==0 || z[0]==0 ){
+ if( z ) sqliteFree(z);
+ pTos->z = "ok";
+ pTos->n = 2;
+ pTos->flags = MEM_Str | MEM_Static | MEM_Term;
+ }else{
+ pTos->z = z;
+ pTos->n = strlen(z);
+ pTos->flags = MEM_Str | MEM_Dyn | MEM_Term;
+ pTos->xDel = 0;
+ }
+ pTos->enc = SQLITE_UTF8;
+ sqlite3VdbeChangeEncoding(pTos, db->enc);
+ sqliteFree(aRoot);
+ break;
+}
+
+/* Opcode: ListWrite * * *
+**
+** Write the integer on the top of the stack
+** into the temporary storage list.
+*/
+case OP_ListWrite: {
+ Keylist *pKeylist;
+ assert( pTos>=p->aStack );
+ pKeylist = p->pList;
+ if( pKeylist==0 || pKeylist->nUsed>=pKeylist->nKey ){
+ pKeylist = sqliteMallocRaw( sizeof(Keylist)+999*sizeof(pKeylist->aKey[0]) );
+ if( pKeylist==0 ) goto no_mem;
+ pKeylist->nKey = 1000;
+ pKeylist->nRead = 0;
+ pKeylist->nUsed = 0;
+ pKeylist->pNext = p->pList;
+ p->pList = pKeylist;
+ }
+ Integerify(pTos);
+ pKeylist->aKey[pKeylist->nUsed++] = pTos->i;
+ assert( (pTos->flags & MEM_Dyn)==0 );
+ pTos--;
+ break;
+}
+
+/* Opcode: ListRewind * * *
+**
+** Rewind the temporary buffer back to the beginning.
+*/
+case OP_ListRewind: {
+ /* What this opcode codes, really, is reverse the order of the
+ ** linked list of Keylist structures so that they are read out
+ ** in the same order that they were read in. */
+ Keylist *pRev, *pTop;
+ pRev = 0;
+ while( p->pList ){
+ pTop = p->pList;
+ p->pList = pTop->pNext;
+ pTop->pNext = pRev;
+ pRev = pTop;
+ }
+ p->pList = pRev;
+ break;
+}
+
+/* Opcode: ListRead * P2 *
+**
+** Attempt to read an integer from the temporary storage buffer
+** and push it onto the stack. If the storage buffer is empty,
+** push nothing but instead jump to P2.
+*/
+case OP_ListRead: {
+ Keylist *pKeylist;
+ CHECK_FOR_INTERRUPT;
+ pKeylist = p->pList;
+ if( pKeylist!=0 ){
+ assert( pKeylist->nRead>=0 );
+ assert( pKeylist->nRead<pKeylist->nUsed );
+ assert( pKeylist->nRead<pKeylist->nKey );
+ pTos++;
+ pTos->i = pKeylist->aKey[pKeylist->nRead++];
+ pTos->flags = MEM_Int;
+ if( pKeylist->nRead>=pKeylist->nUsed ){
+ p->pList = pKeylist->pNext;
+ sqliteFree(pKeylist);
+ }
+ }else{
+ pc = pOp->p2 - 1;
+ }
+ break;
+}
+
+/* Opcode: ListReset * * *
+**
+** Reset the temporary storage buffer so that it holds nothing.
+*/
+case OP_ListReset: {
+ if( p->pList ){
+ sqlite3VdbeKeylistFree(p->pList);
+ p->pList = 0;
+ }
+ break;
+}
+
+/* Opcode: ContextPush * * *
+**
+** Save the current Vdbe context such that it can be restored by a ContextPop
+** opcode. The context stores the last insert row id, the last statement change
+** count, and the current statement change count.
+*/
+case OP_ContextPush: {
+ int i = p->contextStackTop++;
+ Context *pContext;
+
+ assert( i>=0 );
+ /* FIX ME: This should be allocated as part of the vdbe at compile-time */
+ if( i>=p->contextStackDepth ){
+ p->contextStackDepth = i+1;
+ p->contextStack = sqliteRealloc(p->contextStack, sizeof(Context)*(i+1));
+ if( p->contextStack==0 ) goto no_mem;
+ }
+ pContext = &p->contextStack[i];
+ pContext->lastRowid = db->lastRowid;
+ pContext->nChange = p->nChange;
+ pContext->pList = p->pList;
+ p->pList = 0;
+ break;
+}
+
+/* Opcode: ContextPop * * *
+**
+** Restore the Vdbe context to the state it was in when contextPush was last
+** executed. The context stores the last insert row id, the last statement
+** change count, and the current statement change count.
+*/
+case OP_ContextPop: {
+ Context *pContext = &p->contextStack[--p->contextStackTop];
+ assert( p->contextStackTop>=0 );
+ db->lastRowid = pContext->lastRowid;
+ p->nChange = pContext->nChange;
+ sqlite3VdbeKeylistFree(p->pList);
+ p->pList = pContext->pList;
+ break;
+}
+
+/* Opcode: SortPut * * *
+**
+** The TOS is the key and the NOS is the data. Pop both from the stack
+** and put them on the sorter. The key and data should have been
+** made using SortMakeKey and SortMakeRec, respectively.
+*/
+case OP_SortPut: {
+ Mem *pNos = &pTos[-1];
+ Sorter *pSorter;
+ assert( pNos>=p->aStack );
+ if( Dynamicify(pTos, db->enc) ) goto no_mem;
+ pSorter = sqliteMallocRaw( sizeof(Sorter) );
+ if( pSorter==0 ) goto no_mem;
+ pSorter->pNext = p->pSort;
+ p->pSort = pSorter;
+ assert( pTos->flags & MEM_Dyn );
+ pSorter->nKey = pTos->n;
+ pSorter->zKey = pTos->z;
+ pSorter->data.flags = MEM_Null;
+ rc = sqlite3VdbeMemMove(&pSorter->data, pNos);
+ pTos -= 2;
+ break;
+}
+
+/* Opcode: Sort * * P3
+**
+** Sort all elements on the sorter. The algorithm is a
+** mergesort. The P3 argument is a pointer to a KeyInfo structure
+** that describes the keys to be sorted.
+*/
+case OP_Sort: {
+ int i;
+ KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3;
+ Sorter *pElem;
+ Sorter *apSorter[NSORT];
+ pKeyInfo->enc = p->db->enc;
+ for(i=0; i<NSORT; i++){
+ apSorter[i] = 0;
+ }
+ while( p->pSort ){
+ pElem = p->pSort;
+ p->pSort = pElem->pNext;
+ pElem->pNext = 0;
+ for(i=0; i<NSORT-1; i++){
+ if( apSorter[i]==0 ){
+ apSorter[i] = pElem;
+ break;
+ }else{
+ pElem = Merge(apSorter[i], pElem, pKeyInfo);
+ apSorter[i] = 0;
+ }
+ }
+ if( i>=NSORT-1 ){
+ apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem, pKeyInfo);
+ }
+ }
+ pElem = 0;
+ for(i=0; i<NSORT; i++){
+ pElem = Merge(apSorter[i], pElem, pKeyInfo);
+ }
+ p->pSort = pElem;
+ break;
+}
+
+/* Opcode: SortNext * P2 *
+**
+** Push the data for the topmost element in the sorter onto the
+** stack, then remove the element from the sorter. If the sorter
+** is empty, push nothing on the stack and instead jump immediately
+** to instruction P2.
+*/
+case OP_SortNext: {
+ Sorter *pSorter = p->pSort;
+ CHECK_FOR_INTERRUPT;
+ if( pSorter!=0 ){
+ p->pSort = pSorter->pNext;
+ pTos++;
+ pTos->flags = MEM_Null;
+ rc = sqlite3VdbeMemMove(pTos, &pSorter->data);
+ sqliteFree(pSorter->zKey);
+ sqliteFree(pSorter);
+ }else{
+ pc = pOp->p2 - 1;
+ }
+ break;
+}
+
+/* Opcode: SortReset * * *
+**
+** Remove any elements that remain on the sorter.
+*/
+case OP_SortReset: {
+ sqlite3VdbeSorterReset(p);
+ break;
+}
+
+/* Opcode: MemStore P1 P2 *
+**
+** Write the top of the stack into memory location P1.
+** P1 should be a small integer since space is allocated
+** for all memory locations between 0 and P1 inclusive.
+**
+** After the data is stored in the memory location, the
+** stack is popped once if P2 is 1. If P2 is zero, then
+** the original data remains on the stack.
+*/
+case OP_MemStore: {
+ assert( pTos>=p->aStack );
+ assert( pOp->p1>=0 && pOp->p1<p->nMem );
+ rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], pTos);
+ pTos--;
+
+ /* If P2 is 0 then fall thru to the next opcode, OP_MemLoad, that will
+ ** restore the top of the stack to its original value.
+ */
+ if( pOp->p2 ){
+ break;
+ }
+}
+/* Opcode: MemLoad P1 * *
+**
+** Push a copy of the value in memory location P1 onto the stack.
+**
+** If the value is a string, then the value pushed is a pointer to
+** the string that is stored in the memory location. If the memory
+** location is subsequently changed (using OP_MemStore) then the
+** value pushed onto the stack will change too.
+*/
+case OP_MemLoad: {
+ int i = pOp->p1;
+ assert( i>=0 && i<p->nMem );
+ pTos++;
+ sqlite3VdbeMemShallowCopy(pTos, &p->aMem[i], MEM_Ephem);
+ break;
+}
+
+/* Opcode: MemIncr P1 P2 *
+**
+** Increment the integer valued memory cell P1 by 1. If P2 is not zero
+** and the result after the increment is greater than zero, then jump
+** to P2.
+**
+** This instruction throws an error if the memory cell is not initially
+** an integer.
+*/
+case OP_MemIncr: {
+ int i = pOp->p1;
+ Mem *pMem;
+ assert( i>=0 && i<p->nMem );
+ pMem = &p->aMem[i];
+ assert( pMem->flags==MEM_Int );
+ pMem->i++;
+ if( pOp->p2>0 && pMem->i>0 ){
+ pc = pOp->p2 - 1;
+ }
+ break;
+}
+
+/* Opcode: AggReset P1 P2 P3
+**
+** Reset the aggregator so that it no longer contains any data.
+** Future aggregator elements will contain P2 values each and be sorted
+** using the KeyInfo structure pointed to by P3.
+**
+** If P1 is non-zero, then only a single aggregator row is available (i.e.
+** there is no GROUP BY expression). In this case it is illegal to invoke
+** OP_AggFocus.
+*/
+case OP_AggReset: {
+ assert( !pOp->p3 || pOp->p3type==P3_KEYINFO );
+ if( pOp->p1 ){
+ rc = sqlite3VdbeAggReset(0, &p->agg, (KeyInfo *)pOp->p3);
+ p->agg.nMem = pOp->p2; /* Agg.nMem is used by AggInsert() */
+ rc = AggInsert(&p->agg, 0, 0);
+ }else{
+ rc = sqlite3VdbeAggReset(db, &p->agg, (KeyInfo *)pOp->p3);
+ p->agg.nMem = pOp->p2;
+ }
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ p->agg.apFunc = sqliteMalloc( p->agg.nMem*sizeof(p->agg.apFunc[0]) );
+ if( p->agg.apFunc==0 ) goto no_mem;
+ break;
+}
+
+/* Opcode: AggInit * P2 P3
+**
+** Initialize the function parameters for an aggregate function.
+** The aggregate will operate out of aggregate column P2.
+** P3 is a pointer to the FuncDef structure for the function.
+*/
+case OP_AggInit: {
+ int i = pOp->p2;
+ assert( i>=0 && i<p->agg.nMem );
+ p->agg.apFunc[i] = (FuncDef*)pOp->p3;
+ break;
+}
+
+/* Opcode: AggFunc * P2 P3
+**
+** Execute the step function for an aggregate. The
+** function has P2 arguments. P3 is a pointer to the FuncDef
+** structure that specifies the function.
+**
+** The top of the stack must be an integer which is the index of
+** the aggregate column that corresponds to this aggregate function.
+** Ideally, this index would be another parameter, but there are
+** no free parameters left. The integer is popped from the stack.
+*/
+case OP_AggFunc: {
+ int n = pOp->p2;
+ int i;
+ Mem *pMem, *pRec;
+ sqlite3_context ctx;
+ sqlite3_value **apVal;
+
+ assert( n>=0 );
+ assert( pTos->flags==MEM_Int );
+ pRec = &pTos[-n];
+ assert( pRec>=p->aStack );
+
+ apVal = p->apArg;
+ assert( apVal || n==0 );
+
+ for(i=0; i<n; i++, pRec++){
+ apVal[i] = pRec;
+ storeTypeInfo(pRec, db->enc);
+ }
+ i = pTos->i;
+ assert( i>=0 && i<p->agg.nMem );
+ ctx.pFunc = (FuncDef*)pOp->p3;
+ pMem = &p->agg.pCurrent->aMem[i];
+ ctx.s.z = pMem->zShort; /* Space used for small aggregate contexts */
+ ctx.pAgg = pMem->z;
+ ctx.cnt = ++pMem->i;
+ ctx.isError = 0;
+ ctx.isStep = 1;
+ ctx.pColl = 0;
+ if( ctx.pFunc->needCollSeq ){
+ assert( pOp>p->aOp );
+ assert( pOp[-1].p3type==P3_COLLSEQ );
+ assert( pOp[-1].opcode==OP_CollSeq );
+ ctx.pColl = (CollSeq *)pOp[-1].p3;
+ }
+ (ctx.pFunc->xStep)(&ctx, n, apVal);
+ pMem->z = ctx.pAgg;
+ pMem->flags = MEM_AggCtx;
+ popStack(&pTos, n+1);
+ if( ctx.isError ){
+ rc = SQLITE_ERROR;
+ }
+ break;
+}
+
+/* Opcode: AggFocus * P2 *
+**
+** Pop the top of the stack and use that as an aggregator key. If
+** an aggregator with that same key already exists, then make the
+** aggregator the current aggregator and jump to P2. If no aggregator
+** with the given key exists, create one and make it current but
+** do not jump.
+**
+** The order of aggregator opcodes is important. The order is:
+** AggReset AggFocus AggNext. In other words, you must execute
+** AggReset first, then zero or more AggFocus operations, then
+** zero or more AggNext operations. You must not execute an AggFocus
+** in between an AggNext and an AggReset.
+*/
+case OP_AggFocus: {
+ char *zKey;
+ int nKey;
+ int res;
+ assert( pTos>=p->aStack );
+ Stringify(pTos, db->enc);
+ zKey = pTos->z;
+ nKey = pTos->n;
+ assert( p->agg.pBtree );
+ assert( p->agg.pCsr );
+ rc = sqlite3BtreeMoveto(p->agg.pCsr, zKey, nKey, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( res==0 ){
+ rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*),
+ (char *)&p->agg.pCurrent);
+ pc = pOp->p2 - 1;
+ }else{
+ rc = AggInsert(&p->agg, zKey, nKey);
+ }
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ Release(pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: AggSet * P2 *
+**
+** Move the top of the stack into the P2-th field of the current
+** aggregate. String values are duplicated into new memory.
+*/
+case OP_AggSet: {
+ AggElem *pFocus;
+ int i = pOp->p2;
+ pFocus = p->agg.pCurrent;
+ assert( pTos>=p->aStack );
+ if( pFocus==0 ) goto no_mem;
+ assert( i>=0 && i<p->agg.nMem );
+ rc = sqlite3VdbeMemMove(&pFocus->aMem[i], pTos);
+ pTos--;
+ break;
+}
+
+/* Opcode: AggGet * P2 *
+**
+** Push a new entry onto the stack which is a copy of the P2-th field
+** of the current aggregate. Strings are not duplicated so
+** string values will be ephemeral.
+*/
+case OP_AggGet: {
+ AggElem *pFocus;
+ int i = pOp->p2;
+ pFocus = p->agg.pCurrent;
+ if( pFocus==0 ) goto no_mem;
+ assert( i>=0 && i<p->agg.nMem );
+ pTos++;
+ sqlite3VdbeMemShallowCopy(pTos, &pFocus->aMem[i], MEM_Ephem);
+ if( pTos->flags&MEM_Str ){
+ sqlite3VdbeChangeEncoding(pTos, db->enc);
+ }
+ break;
+}
+
+/* Opcode: AggNext * P2 *
+**
+** Make the next aggregate value the current aggregate. The prior
+** aggregate is deleted. If all aggregate values have been consumed,
+** jump to P2.
+**
+** The order of aggregator opcodes is important. The order is:
+** AggReset AggFocus AggNext. In other words, you must execute
+** AggReset first, then zero or more AggFocus operations, then
+** zero or more AggNext operations. You must not execute an AggFocus
+** in between an AggNext and an AggReset.
+*/
+case OP_AggNext: {
+ int res;
+ assert( rc==SQLITE_OK );
+ CHECK_FOR_INTERRUPT;
+ if( p->agg.searching==0 ){
+ p->agg.searching = 1;
+ if( p->agg.pCsr ){
+ rc = sqlite3BtreeFirst(p->agg.pCsr, &res);
+ }else{
+ res = 0;
+ }
+ }else{
+ if( p->agg.pCsr ){
+ rc = sqlite3BtreeNext(p->agg.pCsr, &res);
+ }else{
+ res = 1;
+ }
+ }
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ if( res!=0 ){
+ pc = pOp->p2 - 1;
+ }else{
+ int i;
+ sqlite3_context ctx;
+ Mem *aMem;
+
+ if( p->agg.pCsr ){
+ rc = sqlite3BtreeData(p->agg.pCsr, 0, sizeof(AggElem*),
+ (char *)&p->agg.pCurrent);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ }
+ aMem = p->agg.pCurrent->aMem;
+ for(i=0; i<p->agg.nMem; i++){
+ FuncDef *pFunc = p->agg.apFunc[i];
+ Mem *pMem = &aMem[i];
+ if( pFunc==0 || pFunc->xFinalize==0 ) continue;
+ ctx.s.flags = MEM_Null;
+ ctx.s.z = pMem->zShort;
+ ctx.pAgg = (void*)pMem->z;
+ ctx.cnt = pMem->i;
+ ctx.isStep = 0;
+ ctx.pFunc = pFunc;
+ pFunc->xFinalize(&ctx);
+ pMem->z = ctx.pAgg;
+ if( pMem->z && pMem->z!=pMem->zShort ){
+ sqliteFree( pMem->z );
+ }
+ *pMem = ctx.s;
+ if( pMem->flags & MEM_Short ){
+ pMem->z = pMem->zShort;
+ }
+ }
+ }
+ break;
+}
+
+/* Opcode: Vacuum * * *
+**
+** Vacuum the entire database. This opcode will cause other virtual
+** machines to be created and run. It may not be called from within
+** a transaction.
+*/
+case OP_Vacuum: {
+ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse;
+ rc = sqlite3RunVacuum(&p->zErrMsg, db);
+ if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse;
+ break;
+}
+
+/* An other opcode is illegal...
+*/
+default: {
+ sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",pOp->opcode);
+ sqlite3SetString(&p->zErrMsg, "unknown opcode ", zBuf, (char*)0);
+ rc = SQLITE_INTERNAL;
+ break;
+}
+
+/*****************************************************************************
+** The cases of the switch statement above this line should all be indented
+** by 6 spaces. But the left-most 6 spaces have been removed to improve the
+** readability. From this point on down, the normal indentation rules are
+** restored.
+*****************************************************************************/
+ }
+
+#ifdef VDBE_PROFILE
+ {
+ long long elapse = hwtime() - start;
+ pOp->cycles += elapse;
+ pOp->cnt++;
+#if 0
+ fprintf(stdout, "%10lld ", elapse);
+ sqlite3VdbePrintOp(stdout, origPc, &p->aOp[origPc]);
+#endif
+ }
+#endif
+
+ /* The following code adds nothing to the actual functionality
+ ** of the program. It is only here for testing and debugging.
+ ** On the other hand, it does burn CPU cycles every time through
+ ** the evaluator loop. So we can leave it out when NDEBUG is defined.
+ */
+#ifndef NDEBUG
+ /* Sanity checking on the top element of the stack */
+ if( pTos>=p->aStack ){
+ sqlite3VdbeMemSanity(pTos, db->enc);
+ }
+ if( pc<-1 || pc>=p->nOp ){
+ sqlite3SetString(&p->zErrMsg, "jump destination out of range", (char*)0);
+ rc = SQLITE_INTERNAL;
+ }
+ if( p->trace && pTos>=p->aStack ){
+ int i;
+ fprintf(p->trace, "Stack:");
+ for(i=0; i>-5 && &pTos[i]>=p->aStack; i--){
+ if( pTos[i].flags & MEM_Null ){
+ fprintf(p->trace, " NULL");
+ }else if( (pTos[i].flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
+ fprintf(p->trace, " si:%lld", pTos[i].i);
+ }else if( pTos[i].flags & MEM_Int ){
+ fprintf(p->trace, " i:%lld", pTos[i].i);
+ }else if( pTos[i].flags & MEM_Real ){
+ fprintf(p->trace, " r:%g", pTos[i].r);
+ }else{
+ char zBuf[100];
+ sqlite3VdbeMemPrettyPrint(&pTos[i], zBuf, 100);
+ fprintf(p->trace, " ");
+ fprintf(p->trace, "%s", zBuf);
+ }
+ }
+ if( rc!=0 ) fprintf(p->trace," rc=%d",rc);
+ fprintf(p->trace,"\n");
+ }
+#endif
+ } /* The end of the for(;;) loop the loops through opcodes */
+
+ /* If we reach this point, it means that execution is finished.
+ */
+vdbe_halt:
+ if( rc ){
+ p->rc = rc;
+ rc = SQLITE_ERROR;
+ }else{
+ rc = SQLITE_DONE;
+ }
+ sqlite3VdbeHalt(p);
+ p->pTos = pTos;
+ return rc;
+
+ /* Jump to here if a malloc() fails. It's hard to get a malloc()
+ ** to fail on a modern VM computer, so this code is untested.
+ */
+no_mem:
+ sqlite3SetString(&p->zErrMsg, "out of memory", (char*)0);
+ rc = SQLITE_NOMEM;
+ goto vdbe_halt;
+
+ /* Jump to here for an SQLITE_MISUSE error.
+ */
+abort_due_to_misuse:
+ rc = SQLITE_MISUSE;
+ /* Fall thru into abort_due_to_error */
+
+ /* Jump to here for any other kind of fatal error. The "rc" variable
+ ** should hold the error number.
+ */
+abort_due_to_error:
+ if( p->zErrMsg==0 ){
+ if( sqlite3_malloc_failed ) rc = SQLITE_NOMEM;
+ sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0);
+ }
+ goto vdbe_halt;
+
+ /* Jump to here if the sqlite3_interrupt() API sets the interrupt
+ ** flag.
+ */
+abort_due_to_interrupt:
+ assert( db->flags & SQLITE_Interrupt );
+ db->flags &= ~SQLITE_Interrupt;
+ if( db->magic!=SQLITE_MAGIC_BUSY ){
+ rc = SQLITE_MISUSE;
+ }else{
+ rc = SQLITE_INTERRUPT;
+ }
+ p->rc = rc;
+ sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(rc), (char*)0);
+ goto vdbe_halt;
+}
diff --git a/kopete/plugins/statistics/sqlite/vdbe.h b/kopete/plugins/statistics/sqlite/vdbe.h
new file mode 100644
index 00000000..490417a4
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbe.h
@@ -0,0 +1,131 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Header file for the Virtual DataBase Engine (VDBE)
+**
+** This header defines the interface to the virtual database engine
+** or VDBE. The VDBE implements an abstract machine that runs a
+** simple program to access and modify the underlying database.
+**
+** $Id$
+*/
+#ifndef _SQLITE_VDBE_H_
+#define _SQLITE_VDBE_H_
+#include <stdio.h>
+
+/*
+** A single VDBE is an opaque structure named "Vdbe". Only routines
+** in the source file sqliteVdbe.c are allowed to see the insides
+** of this structure.
+*/
+typedef struct Vdbe Vdbe;
+
+/*
+** A single instruction of the virtual machine has an opcode
+** and as many as three operands. The instruction is recorded
+** as an instance of the following structure:
+*/
+struct VdbeOp {
+ u8 opcode; /* What operation to perform */
+ int p1; /* First operand */
+ int p2; /* Second parameter (often the jump destination) */
+ char *p3; /* Third parameter */
+ int p3type; /* P3_STATIC, P3_DYNAMIC or P3_POINTER */
+#ifdef VDBE_PROFILE
+ int cnt; /* Number of times this instruction was executed */
+ long long cycles; /* Total time spend executing this instruction */
+#endif
+};
+typedef struct VdbeOp VdbeOp;
+
+/*
+** A smaller version of VdbeOp used for the VdbeAddOpList() function because
+** it takes up less space.
+*/
+struct VdbeOpList {
+ u8 opcode; /* What operation to perform */
+ signed char p1; /* First operand */
+ short int p2; /* Second parameter (often the jump destination) */
+ char *p3; /* Third parameter */
+};
+typedef struct VdbeOpList VdbeOpList;
+
+/*
+** Allowed values of VdbeOp.p3type
+*/
+#define P3_NOTUSED 0 /* The P3 parameter is not used */
+#define P3_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */
+#define P3_STATIC (-2) /* Pointer to a static string */
+#define P3_POINTER (-3) /* P3 is a pointer to some structure or object */
+#define P3_COLLSEQ (-4) /* P3 is a pointer to a CollSeq structure */
+#define P3_FUNCDEF (-5) /* P3 is a pointer to a FuncDef structure */
+#define P3_KEYINFO (-6) /* P3 is a pointer to a KeyInfo structure */
+#define P3_VDBEFUNC (-7) /* P3 is a pointer to a VdbeFunc structure */
+
+/* When adding a P3 argument using P3_KEYINFO, a copy of the KeyInfo structure
+** is made. That copy is freed when the Vdbe is finalized. But if the
+** argument is P3_KEYINFO_HANDOFF, the passed in pointer is used. It still
+** gets freed when the Vdbe is finalized so it still should be obtained
+** from a single sqliteMalloc(). But no copy is made and the calling
+** function should *not* try to free the KeyInfo.
+*/
+#define P3_KEYINFO_HANDOFF (-7)
+
+/*
+** The following macro converts a relative address in the p2 field
+** of a VdbeOp structure into a negative number so that
+** sqlite3VdbeAddOpList() knows that the address is relative. Calling
+** the macro again restores the address.
+*/
+#define ADDR(X) (-1-(X))
+
+/*
+** The makefile scans the vdbe.c source file and creates the "opcodes.h"
+** header file that defines a number for each opcode used by the VDBE.
+*/
+#include "opcodes.h"
+
+/*
+** Prototypes for the VDBE interface. See comments on the implementation
+** for a description of what each of these routines does.
+*/
+Vdbe *sqlite3VdbeCreate(sqlite3*);
+void sqlite3VdbeCreateCallback(Vdbe*, int*);
+int sqlite3VdbeAddOp(Vdbe*,int,int,int);
+int sqlite3VdbeOp3(Vdbe*,int,int,int,const char *zP3,int);
+int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
+void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
+void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
+void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N);
+void sqlite3VdbeDequoteP3(Vdbe*, int addr);
+int sqlite3VdbeFindOp(Vdbe*, int, int, int);
+VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
+int sqlite3VdbeMakeLabel(Vdbe*);
+void sqlite3VdbeDelete(Vdbe*);
+void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int);
+int sqlite3VdbeFinalize(Vdbe*);
+void sqlite3VdbeResolveLabel(Vdbe*, int);
+int sqlite3VdbeCurrentAddr(Vdbe*);
+void sqlite3VdbeTrace(Vdbe*,FILE*);
+int sqlite3VdbeReset(Vdbe*);
+int sqliteVdbeSetVariables(Vdbe*,int,const char**);
+void sqlite3VdbeSetNumCols(Vdbe*,int);
+int sqlite3VdbeSetColName(Vdbe*, int, const char *, int);
+void sqlite3VdbeCountChanges(Vdbe*);
+
+#ifndef NDEBUG
+ void sqlite3VdbeComment(Vdbe*, const char*, ...);
+# define VdbeComment(X) sqlite3VdbeComment X
+#else
+# define VdbeComment(X)
+#endif
+
+#endif
diff --git a/kopete/plugins/statistics/sqlite/vdbeInt.h b/kopete/plugins/statistics/sqlite/vdbeInt.h
new file mode 100644
index 00000000..a929cb95
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbeInt.h
@@ -0,0 +1,408 @@
+/*
+** 2003 September 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This is the header file for information that is private to the
+** VDBE. This information used to all be at the top of the single
+** source code file "vdbe.c". When that file became too big (over
+** 6000 lines long) it was split up into several smaller files and
+** this header information was factored out.
+*/
+
+/*
+** intToKey() and keyToInt() used to transform the rowid. But with
+** the latest versions of the design they are no-ops.
+*/
+#define keyToInt(X) (X)
+#define intToKey(X) (X)
+
+/*
+** The makefile scans the vdbe.c source file and creates the following
+** array of string constants which are the names of all VDBE opcodes. This
+** array is defined in a separate source code file named opcode.c which is
+** automatically generated by the makefile.
+*/
+extern char *sqlite3OpcodeNames[];
+
+/*
+** SQL is translated into a sequence of instructions to be
+** executed by a virtual machine. Each instruction is an instance
+** of the following structure.
+*/
+typedef struct VdbeOp Op;
+
+/*
+** Boolean values
+*/
+typedef unsigned char Bool;
+
+/*
+** A cursor is a pointer into a single BTree within a database file.
+** The cursor can seek to a BTree entry with a particular key, or
+** loop over all entries of the Btree. You can also insert new BTree
+** entries or retrieve the key or data from the entry that the cursor
+** is currently pointing to.
+**
+** Every cursor that the virtual machine has open is represented by an
+** instance of the following structure.
+**
+** If the Cursor.isTriggerRow flag is set it means that this cursor is
+** really a single row that represents the NEW or OLD pseudo-table of
+** a row trigger. The data for the row is stored in Cursor.pData and
+** the rowid is in Cursor.iKey.
+*/
+struct Cursor {
+ BtCursor *pCursor; /* The cursor structure of the backend */
+ i64 lastRecno; /* Last recno from a Next or NextIdx operation */
+ i64 nextRowid; /* Next rowid returned by OP_NewRowid */
+ Bool zeroed; /* True if zeroed out and ready for reuse */
+ Bool recnoIsValid; /* True if lastRecno is valid */
+ Bool keyAsData; /* The OP_Column command works on key instead of data */
+ Bool atFirst; /* True if pointing to first entry */
+ Bool useRandomRowid; /* Generate new record numbers semi-randomly */
+ Bool nullRow; /* True if pointing to a row with no data */
+ Bool nextRowidValid; /* True if the nextRowid field is valid */
+ Bool pseudoTable; /* This is a NEW or OLD pseudo-tables of a trigger */
+ Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
+ Bool intKey; /* True if the table requires integer keys */
+ Bool zeroData; /* True if table contains keys only - no data */
+ u8 bogusIncrKey; /* Something for pIncrKey to point to if pKeyInfo==0 */
+ i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
+ Btree *pBt; /* Separate file holding temporary table */
+ int nData; /* Number of bytes in pData */
+ char *pData; /* Data for a NEW or OLD pseudo-table */
+ i64 iKey; /* Key for the NEW or OLD pseudo-table row */
+ u8 *pIncrKey; /* Pointer to pKeyInfo->incrKey */
+ KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
+ int nField; /* Number of fields in the header */
+
+ /* Cached information about the header for the data record that the
+ ** cursor is currently pointing to. Only valid if cacheValid is true.
+ ** zRow might point to (ephemeral) data for the current row, or it might
+ ** be NULL. */
+ Bool cacheValid; /* True if the cache is valid */
+ int payloadSize; /* Total number of bytes in the record */
+ u32 *aType; /* Type values for all entries in the record */
+ u32 *aOffset; /* Cached offsets to the start of each columns data */
+ u8 *aRow; /* Data for the current row, if all on one page */
+};
+typedef struct Cursor Cursor;
+
+/*
+** Number of bytes of string storage space available to each stack
+** layer without having to malloc. NBFS is short for Number of Bytes
+** For Strings.
+*/
+#define NBFS 32
+
+/*
+** Internally, the vdbe manipulates nearly all SQL values as Mem
+** structures. Each Mem struct may cache multiple representations (string,
+** integer etc.) of the same value. A value (and therefore Mem structure)
+** has the following properties:
+**
+** Each value has a manifest type. The manifest type of the value stored
+** in a Mem struct is returned by the MemType(Mem*) macro. The type is
+** one of SQLITE_NULL, SQLITE_INTEGER, SQLITE_REAL, SQLITE_TEXT or
+** SQLITE_BLOB.
+*/
+struct Mem {
+ i64 i; /* Integer value */
+ int n; /* Number of characters in string value, including '\0' */
+ u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
+ u8 type; /* One of MEM_Null, MEM_Str, etc. */
+ u8 enc; /* TEXT_Utf8, TEXT_Utf16le, or TEXT_Utf16be */
+ double r; /* Real value */
+ char *z; /* String or BLOB value */
+ void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
+ char zShort[NBFS]; /* Space for short strings */
+};
+typedef struct Mem Mem;
+
+/*
+** A sorter builds a list of elements to be sorted. Each element of
+** the list is an instance of the following structure.
+*/
+typedef struct Sorter Sorter;
+struct Sorter {
+ int nKey; /* Number of bytes in the key */
+ char *zKey; /* The key by which we will sort */
+ Mem data;
+ Sorter *pNext; /* Next in the list */
+};
+
+/*
+** Number of buckets used for merge-sort.
+*/
+#define NSORT 30
+
+/* One or more of the following flags are set to indicate the validOK
+** representations of the value stored in the Mem struct.
+**
+** If the MEM_Null flag is set, then the value is an SQL NULL value.
+** No other flags may be set in this case.
+**
+** If the MEM_Str flag is set then Mem.z points at a string representation.
+** Usually this is encoded in the same unicode encoding as the main
+** database (see below for exceptions). If the MEM_Term flag is also
+** set, then the string is nul terminated. The MEM_Int and MEM_Real
+** flags may coexist with the MEM_Str flag.
+**
+** Multiple of these values can appear in Mem.flags. But only one
+** at a time can appear in Mem.type.
+*/
+#define MEM_Null 0x0001 /* Value is NULL */
+#define MEM_Str 0x0002 /* Value is a string */
+#define MEM_Int 0x0004 /* Value is an integer */
+#define MEM_Real 0x0008 /* Value is a real number */
+#define MEM_Blob 0x0010 /* Value is a BLOB */
+
+/* Whenever Mem contains a valid string or blob representation, one of
+** the following flags must be set to determine the memory management
+** policy for Mem.z. The MEM_Term flag tells us whether or not the
+** string is \000 or \u0000 terminated
+*/
+#define MEM_Term 0x0020 /* String rep is nul terminated */
+#define MEM_Dyn 0x0040 /* Need to call sqliteFree() on Mem.z */
+#define MEM_Static 0x0080 /* Mem.z points to a static string */
+#define MEM_Ephem 0x0100 /* Mem.z points to an ephemeral string */
+#define MEM_Short 0x0200 /* Mem.z points to Mem.zShort */
+
+/* The following MEM_ value appears only in AggElem.aMem.s.flag fields.
+** It indicates that the corresponding AggElem.aMem.z points to a
+** aggregate function context that needs to be finalized.
+*/
+#define MEM_AggCtx 0x0400 /* Mem.z points to an agg function context */
+
+
+/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
+** additional information about auxiliary information bound to arguments
+** of the function. This is used to implement the sqlite3_get_auxdata()
+** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
+** that can be associated with a constant argument to a function. This
+** allows functions such as "regexp" to compile their constant regular
+** expression argument once and reused the compiled code for multiple
+** invocations.
+*/
+struct VdbeFunc {
+ FuncDef *pFunc; /* The definition of the function */
+ int nAux; /* Number of entries allocated for apAux[] */
+ struct AuxData {
+ void *pAux; /* Aux data for the i-th argument */
+ void (*xDelete)(void *); /* Destructor for the aux data */
+ } apAux[1]; /* One slot for each function argument */
+};
+typedef struct VdbeFunc VdbeFunc;
+
+/*
+** The "context" argument for a installable function. A pointer to an
+** instance of this structure is the first argument to the routines used
+** implement the SQL functions.
+**
+** There is a typedef for this structure in sqlite.h. So all routines,
+** even the public interface to SQLite, can use a pointer to this structure.
+** But this file is the only place where the internal details of this
+** structure are known.
+**
+** This structure is defined inside of vdbe.c because it uses substructures
+** (Mem) which are only defined there.
+*/
+struct sqlite3_context {
+ FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
+ VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
+ Mem s; /* The return value is stored here */
+ void *pAgg; /* Aggregate context */
+ u8 isError; /* Set to true for an error */
+ u8 isStep; /* Current in the step function */
+ int cnt; /* Number of times that the step function has been called */
+ CollSeq *pColl;
+};
+
+/*
+** An Agg structure describes an Aggregator. Each Agg consists of
+** zero or more Aggregator elements (AggElem). Each AggElem contains
+** a key and one or more values. The values are used in processing
+** aggregate functions in a SELECT. The key is used to implement
+** the GROUP BY clause of a select.
+*/
+typedef struct Agg Agg;
+typedef struct AggElem AggElem;
+struct Agg {
+ int nMem; /* Number of values stored in each AggElem */
+ AggElem *pCurrent; /* The AggElem currently in focus */
+ FuncDef **apFunc; /* Information about aggregate functions */
+ Btree *pBtree; /* The tmp. btree used to group elements, if required. */
+ BtCursor *pCsr; /* Read/write cursor to the table in pBtree */
+ int nTab; /* Root page of the table in pBtree */
+ u8 searching; /* True between the first AggNext and AggReset */
+};
+struct AggElem {
+ char *zKey; /* The key to this AggElem */
+ int nKey; /* Number of bytes in the key, including '\0' at end */
+ Mem aMem[1]; /* The values for this AggElem */
+};
+
+/*
+** A Set structure is used for quick testing to see if a value
+** is part of a small set. Sets are used to implement code like
+** this:
+** x.y IN ('hi','hoo','hum')
+*/
+typedef struct Set Set;
+struct Set {
+ Hash hash; /* A set is just a hash table */
+ HashElem *prev; /* Previously accessed hash elemen */
+};
+
+/*
+** A Keylist is a bunch of keys into a table. The keylist can
+** grow without bound. The keylist stores the ROWIDs of database
+** records that need to be deleted or updated.
+*/
+typedef struct Keylist Keylist;
+struct Keylist {
+ int nKey; /* Number of slots in aKey[] */
+ int nUsed; /* Next unwritten slot in aKey[] */
+ int nRead; /* Next unread slot in aKey[] */
+ Keylist *pNext; /* Next block of keys */
+ i64 aKey[1]; /* One or more keys. Extra space allocated as needed */
+};
+
+/*
+** A Context stores the last insert rowid, the last statement change count,
+** and the current statement change count (i.e. changes since last statement).
+** The current keylist is also stored in the context.
+** Elements of Context structure type make up the ContextStack, which is
+** updated by the ContextPush and ContextPop opcodes (used by triggers).
+** The context is pushed before executing a trigger a popped when the
+** trigger finishes.
+*/
+typedef struct Context Context;
+struct Context {
+ int lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
+ int nChange; /* Statement changes (Vdbe.nChanges) */
+ Keylist *pList; /* Records that will participate in a DELETE or UPDATE */
+};
+
+/*
+** An instance of the virtual machine. This structure contains the complete
+** state of the virtual machine.
+**
+** The "sqlite3_stmt" structure pointer that is returned by sqlite3_compile()
+** is really a pointer to an instance of this structure.
+*/
+struct Vdbe {
+ sqlite3 *db; /* The whole database */
+ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
+ FILE *trace; /* Write an execution trace here, if not NULL */
+ int nOp; /* Number of instructions in the program */
+ int nOpAlloc; /* Number of slots allocated for aOp[] */
+ Op *aOp; /* Space to hold the virtual machine's program */
+ int nLabel; /* Number of labels used */
+ int nLabelAlloc; /* Number of slots allocated in aLabel[] */
+ int *aLabel; /* Space to hold the labels */
+ Mem *aStack; /* The operand stack, except string values */
+ Mem *pTos; /* Top entry in the operand stack */
+ Mem **apArg; /* Arguments to currently executing user function */
+ Mem *aColName; /* Column names to return */
+ int nCursor; /* Number of slots in apCsr[] */
+ Cursor **apCsr; /* One element of this array for each open cursor */
+ Sorter *pSort; /* A linked list of objects to be sorted */
+ int nVar; /* Number of entries in aVar[] */
+ Mem *aVar; /* Values for the OP_Variable opcode. */
+ char **azVar; /* Name of variables */
+ int okVar; /* True if azVar[] has been initialized */
+ int magic; /* Magic number for sanity checking */
+ int nMem; /* Number of memory locations currently allocated */
+ Mem *aMem; /* The memory locations */
+ Agg agg; /* Aggregate information */
+ int nCallback; /* Number of callbacks invoked so far */
+ Keylist *pList; /* A list of ROWIDs */
+ int contextStackTop; /* Index of top element in the context stack */
+ int contextStackDepth; /* The size of the "context" stack */
+ Context *contextStack; /* Stack used by opcodes ContextPush & ContextPop*/
+ int pc; /* The program counter */
+ int rc; /* Value to return */
+ unsigned uniqueCnt; /* Used by OP_MakeRecord when P2!=0 */
+ int errorAction; /* Recovery action to do in case of an error */
+ int inTempTrans; /* True if temp database is transactioned */
+ int returnStack[100]; /* Return address stack for OP_Gosub & OP_Return */
+ int returnDepth; /* Next unused element in returnStack[] */
+ int nResColumn; /* Number of columns in one row of the result set */
+ char **azResColumn; /* Values for one row of result */
+ int popStack; /* Pop the stack this much on entry to VdbeExec() */
+ char *zErrMsg; /* Error message written here */
+ u8 resOnStack; /* True if there are result values on the stack */
+ u8 explain; /* True if EXPLAIN present on SQL command */
+ u8 changeCntOn; /* True to update the change-counter */
+ u8 aborted; /* True if ROLLBACK in another VM causes an abort */
+ int nChange; /* Number of db changes made since last reset */
+};
+
+/*
+** The following are allowed values for Vdbe.magic
+*/
+#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */
+#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */
+#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
+#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
+
+/*
+** Function prototypes
+*/
+void sqlite3VdbeFreeCursor(Cursor*);
+void sqlite3VdbeSorterReset(Vdbe*);
+int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
+void sqlite3VdbeKeylistFree(Keylist*);
+void sqliteVdbePopStack(Vdbe*,int);
+int sqlite3VdbeCursorMoveto(Cursor*);
+#if !defined(NDEBUG) || defined(VDBE_PROFILE)
+void sqlite3VdbePrintOp(FILE*, int, Op*);
+#endif
+void sqlite3VdbePrintSql(Vdbe*);
+int sqlite3VdbeSerialTypeLen(u32);
+u32 sqlite3VdbeSerialType(Mem*);
+int sqlite3VdbeSerialPut(unsigned char*, Mem*);
+int sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
+void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
+
+int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
+int sqlite3VdbeIdxKeyCompare(Cursor*, int , const unsigned char*, int*);
+int sqlite3VdbeIdxRowid(BtCursor *, i64 *);
+int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
+int sqlite3VdbeRecordCompare(void*,int,const void*,int, const void*);
+int sqlite3VdbeIdxRowidLen(int,const u8*);
+int sqlite3VdbeExec(Vdbe*);
+int sqlite3VdbeList(Vdbe*);
+int sqlite3VdbeHalt(Vdbe*);
+int sqlite3VdbeChangeEncoding(Mem *, int);
+int sqlite3VdbeMemCopy(Mem*, const Mem*);
+void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
+int sqlite3VdbeMemMove(Mem*, Mem*);
+int sqlite3VdbeMemNulTerminate(Mem*);
+int sqlite3VdbeMemSetStr(Mem*, const char*, int, u8, void(*)(void*));
+void sqlite3VdbeMemSetInt64(Mem*, i64);
+void sqlite3VdbeMemSetDouble(Mem*, double);
+void sqlite3VdbeMemSetNull(Mem*);
+int sqlite3VdbeMemMakeWriteable(Mem*);
+int sqlite3VdbeMemDynamicify(Mem*);
+int sqlite3VdbeMemStringify(Mem*, int);
+i64 sqlite3VdbeIntValue(Mem*);
+int sqlite3VdbeMemIntegerify(Mem*);
+double sqlite3VdbeRealValue(Mem*);
+int sqlite3VdbeMemRealify(Mem*);
+int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
+void sqlite3VdbeMemRelease(Mem *p);
+#ifndef NDEBUG
+void sqlite3VdbeMemSanity(Mem*, u8);
+#endif
+int sqlite3VdbeMemTranslate(Mem*, u8);
+void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf);
+int sqlite3VdbeMemHandleBom(Mem *pMem);
diff --git a/kopete/plugins/statistics/sqlite/vdbeapi.c b/kopete/plugins/statistics/sqlite/vdbeapi.c
new file mode 100644
index 00000000..f6047f6f
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbeapi.c
@@ -0,0 +1,588 @@
+/*
+** 2004 May 26
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains code use to implement APIs that are part of the
+** VDBE.
+*/
+#include "sqliteInt.h"
+#include "vdbeInt.h"
+
+/**************************** sqlite3_value_ *******************************
+** The following routines extract information from a Mem or sqlite3_value
+** structure.
+*/
+const void *sqlite3_value_blob(sqlite3_value *pVal){
+ Mem *p = (Mem*)pVal;
+ if( p->flags & (MEM_Blob|MEM_Str) ){
+ return p->z;
+ }else{
+ return sqlite3_value_text(pVal);
+ }
+}
+int sqlite3_value_bytes(sqlite3_value *pVal){
+ return sqlite3ValueBytes(pVal, SQLITE_UTF8);
+}
+int sqlite3_value_bytes16(sqlite3_value *pVal){
+ return sqlite3ValueBytes(pVal, SQLITE_UTF16NATIVE);
+}
+double sqlite3_value_double(sqlite3_value *pVal){
+ return sqlite3VdbeRealValue((Mem*)pVal);
+}
+int sqlite3_value_int(sqlite3_value *pVal){
+ return sqlite3VdbeIntValue((Mem*)pVal);
+}
+sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){
+ return sqlite3VdbeIntValue((Mem*)pVal);
+}
+const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
+ return (const char *)sqlite3ValueText(pVal, SQLITE_UTF8);
+}
+const void *sqlite3_value_text16(sqlite3_value* pVal){
+ return sqlite3ValueText(pVal, SQLITE_UTF16NATIVE);
+}
+const void *sqlite3_value_text16be(sqlite3_value *pVal){
+ return sqlite3ValueText(pVal, SQLITE_UTF16BE);
+}
+const void *sqlite3_value_text16le(sqlite3_value *pVal){
+ return sqlite3ValueText(pVal, SQLITE_UTF16LE);
+}
+int sqlite3_value_type(sqlite3_value* pVal){
+ return pVal->type;
+}
+
+/**************************** sqlite3_result_ *******************************
+** The following routines are used by user-defined functions to specify
+** the function result.
+*/
+void sqlite3_result_blob(
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
+ void (*xDel)(void *)
+){
+ assert( n>0 );
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, 0, xDel);
+}
+void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+ sqlite3VdbeMemSetDouble(&pCtx->s, rVal);
+}
+void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
+ pCtx->isError = 1;
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
+}
+void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
+ pCtx->isError = 1;
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
+}
+void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+ sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal);
+}
+void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+ sqlite3VdbeMemSetInt64(&pCtx->s, iVal);
+}
+void sqlite3_result_null(sqlite3_context *pCtx){
+ sqlite3VdbeMemSetNull(&pCtx->s);
+}
+void sqlite3_result_text(
+ sqlite3_context *pCtx,
+ const char *z,
+ int n,
+ void (*xDel)(void *)
+){
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, xDel);
+}
+void sqlite3_result_text16(
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
+ void (*xDel)(void *)
+){
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, xDel);
+}
+void sqlite3_result_text16be(
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
+ void (*xDel)(void *)
+){
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16BE, xDel);
+}
+void sqlite3_result_text16le(
+ sqlite3_context *pCtx,
+ const void *z,
+ int n,
+ void (*xDel)(void *)
+){
+ sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16LE, xDel);
+}
+void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
+ sqlite3VdbeMemCopy(&pCtx->s, pValue);
+}
+
+
+/*
+** Execute the statement pStmt, either until a row of data is ready, the
+** statement is completely executed or an error occurs.
+*/
+int sqlite3_step(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe*)pStmt;
+ sqlite3 *db;
+ int rc;
+
+ if( p==0 || p->magic!=VDBE_MAGIC_RUN ){
+ return SQLITE_MISUSE;
+ }
+ if( p->aborted ){
+ return SQLITE_ABORT;
+ }
+ db = p->db;
+ if( sqlite3SafetyOn(db) ){
+ p->rc = SQLITE_MISUSE;
+ return SQLITE_MISUSE;
+ }
+ if( p->pc<0 ){
+ /* Invoke the trace callback if there is one
+ */
+ if( (db = p->db)->xTrace && !db->init.busy ){
+ assert( p->nOp>0 );
+ assert( p->aOp[p->nOp-1].opcode==OP_Noop );
+ assert( p->aOp[p->nOp-1].p3!=0 );
+ assert( p->aOp[p->nOp-1].p3type==P3_DYNAMIC );
+ sqlite3SafetyOff(db);
+ db->xTrace(db->pTraceArg, p->aOp[p->nOp-1].p3);
+ if( sqlite3SafetyOn(db) ){
+ p->rc = SQLITE_MISUSE;
+ return SQLITE_MISUSE;
+ }
+ }
+
+ /* Print a copy of SQL as it is executed if the SQL_TRACE pragma is turned
+ ** on in debugging mode.
+ */
+#ifdef SQLITE_DEBUG
+ if( (db->flags & SQLITE_SqlTrace)!=0 ){
+ sqlite3DebugPrintf("SQL-trace: %s\n", p->aOp[p->nOp-1].p3);
+ }
+#endif /* SQLITE_DEBUG */
+
+ db->activeVdbeCnt++;
+ p->pc = 0;
+ }
+ if( p->explain ){
+ rc = sqlite3VdbeList(p);
+ }else{
+ rc = sqlite3VdbeExec(p);
+ }
+
+ if( sqlite3SafetyOff(db) ){
+ rc = SQLITE_MISUSE;
+ }
+
+ sqlite3Error(p->db, rc, p->zErrMsg);
+ return rc;
+}
+
+/*
+** Extract the user data from a sqlite3_context structure and return a
+** pointer to it.
+*/
+void *sqlite3_user_data(sqlite3_context *p){
+ assert( p && p->pFunc );
+ return p->pFunc->pUserData;
+}
+
+/*
+** Allocate or return the aggregate context for a user function. A new
+** context is allocated on the first call. Subsequent calls return the
+** same context that was returned on prior calls.
+**
+** This routine is defined here in vdbe.c because it depends on knowing
+** the internals of the sqlite3_context structure which is only defined in
+** this source file.
+*/
+void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
+ assert( p && p->pFunc && p->pFunc->xStep );
+ if( p->pAgg==0 ){
+ if( nByte<=NBFS ){
+ p->pAgg = (void*)p->s.z;
+ memset(p->pAgg, 0, nByte);
+ }else{
+ p->pAgg = sqliteMalloc( nByte );
+ }
+ }
+ return p->pAgg;
+}
+
+/*
+** Return the auxilary data pointer, if any, for the iArg'th argument to
+** the user-function defined by pCtx.
+*/
+void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
+ VdbeFunc *pVdbeFunc = pCtx->pVdbeFunc;
+ if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
+ return 0;
+ }
+ return pVdbeFunc->apAux[iArg].pAux;
+}
+
+/*
+** Set the auxilary data pointer and delete function, for the iArg'th
+** argument to the user-function defined by pCtx. Any previous value is
+** deleted by calling the delete function specified when it was set.
+*/
+void sqlite3_set_auxdata(
+ sqlite3_context *pCtx,
+ int iArg,
+ void *pAux,
+ void (*xDelete)(void*)
+){
+ struct AuxData *pAuxData;
+ VdbeFunc *pVdbeFunc;
+ if( iArg<0 ) return;
+
+ pVdbeFunc = pCtx->pVdbeFunc;
+ if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){
+ int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg;
+ pCtx->pVdbeFunc = pVdbeFunc = sqliteRealloc(pVdbeFunc, nMalloc);
+ if( !pVdbeFunc ) return;
+ memset(&pVdbeFunc->apAux[pVdbeFunc->nAux], 0,
+ sizeof(struct AuxData)*(iArg+1-pVdbeFunc->nAux));
+ pVdbeFunc->nAux = iArg+1;
+ pVdbeFunc->pFunc = pCtx->pFunc;
+ }
+
+ pAuxData = &pVdbeFunc->apAux[iArg];
+ if( pAuxData->pAux && pAuxData->xDelete ){
+ pAuxData->xDelete(pAuxData->pAux);
+ }
+ pAuxData->pAux = pAux;
+ pAuxData->xDelete = xDelete;
+}
+
+/*
+** Return the number of times the Step function of a aggregate has been
+** called.
+**
+** This routine is defined here in vdbe.c because it depends on knowing
+** the internals of the sqlite3_context structure which is only defined in
+** this source file.
+*/
+int sqlite3_aggregate_count(sqlite3_context *p){
+ assert( p && p->pFunc && p->pFunc->xStep );
+ return p->cnt;
+}
+
+/*
+** Return the number of columns in the result set for the statement pStmt.
+*/
+int sqlite3_column_count(sqlite3_stmt *pStmt){
+ Vdbe *pVm = (Vdbe *)pStmt;
+ return pVm ? pVm->nResColumn : 0;
+}
+
+/*
+** Return the number of values available from the current row of the
+** currently executing statement pStmt.
+*/
+int sqlite3_data_count(sqlite3_stmt *pStmt){
+ Vdbe *pVm = (Vdbe *)pStmt;
+ if( pVm==0 || !pVm->resOnStack ) return 0;
+ return pVm->nResColumn;
+}
+
+
+/*
+** Check to see if column iCol of the given statement is valid. If
+** it is, return a pointer to the Mem for the value of that column.
+** If iCol is not valid, return a pointer to a Mem which has a value
+** of NULL.
+*/
+static Mem *columnMem(sqlite3_stmt *pStmt, int i){
+ Vdbe *pVm = (Vdbe *)pStmt;
+ int vals = sqlite3_data_count(pStmt);
+ if( i>=vals || i<0 ){
+ static Mem nullMem;
+ if( nullMem.flags==0 ){ nullMem.flags = MEM_Null; }
+ sqlite3Error(pVm->db, SQLITE_RANGE, 0);
+ return &nullMem;
+ }
+ return &pVm->pTos[(1-vals)+i];
+}
+
+/**************************** sqlite3_column_ *******************************
+** The following routines are used to access elements of the current row
+** in the result set.
+*/
+const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_blob( columnMem(pStmt,i) );
+}
+int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_bytes( columnMem(pStmt,i) );
+}
+int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_bytes16( columnMem(pStmt,i) );
+}
+double sqlite3_column_double(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_double( columnMem(pStmt,i) );
+}
+int sqlite3_column_int(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_int( columnMem(pStmt,i) );
+}
+sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_int64( columnMem(pStmt,i) );
+}
+const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_text( columnMem(pStmt,i) );
+}
+const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_text16( columnMem(pStmt,i) );
+}
+int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
+ return sqlite3_value_type( columnMem(pStmt,i) );
+}
+
+/*
+** Convert the N-th element of pStmt->pColName[] into a string using
+** xFunc() then return that string. If N is out of range, return 0.
+** If useType is 1, then use the second set of N elements (the datatype
+** names) instead of the first set.
+*/
+static const void *columnName(
+ sqlite3_stmt *pStmt,
+ int N,
+ const void *(*xFunc)(Mem*),
+ int useType
+){
+ Vdbe *p = (Vdbe *)pStmt;
+ int n = sqlite3_column_count(pStmt);
+
+ if( p==0 || N>=n || N<0 ){
+ return 0;
+ }
+ if( useType ){
+ N += n;
+ }
+ return xFunc(&p->aColName[N]);
+}
+
+
+/*
+** Return the name of the Nth column of the result set returned by SQL
+** statement pStmt.
+*/
+const char *sqlite3_column_name(sqlite3_stmt *pStmt, int N){
+ return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, 0);
+}
+
+/*
+** Return the name of the 'i'th column of the result set of SQL statement
+** pStmt, encoded as UTF-16.
+*/
+const void *sqlite3_column_name16(sqlite3_stmt *pStmt, int N){
+ return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, 0);
+}
+
+/*
+** Return the column declaration type (if applicable) of the 'i'th column
+** of the result set of SQL statement pStmt, encoded as UTF-8.
+*/
+const char *sqlite3_column_decltype(sqlite3_stmt *pStmt, int N){
+ return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text, 1);
+}
+
+/*
+** Return the column declaration type (if applicable) of the 'i'th column
+** of the result set of SQL statement pStmt, encoded as UTF-16.
+*/
+const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
+ return columnName(pStmt, N, (const void*(*)(Mem*))sqlite3_value_text16, 1);
+}
+
+/******************************* sqlite3_bind_ ***************************
+**
+** Routines used to attach values to wildcards in a compiled SQL statement.
+*/
+/*
+** Unbind the value bound to variable i in virtual machine p. This is the
+** the same as binding a NULL value to the column. If the "i" parameter is
+** out of range, then SQLITE_RANGE is returned. Othewise SQLITE_OK.
+**
+** The error code stored in database p->db is overwritten with the return
+** value in any case.
+*/
+static int vdbeUnbind(Vdbe *p, int i){
+ Mem *pVar;
+ if( p==0 || p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
+ sqlite3Error(p->db, SQLITE_MISUSE, 0);
+ return SQLITE_MISUSE;
+ }
+ if( i<1 || i>p->nVar ){
+ sqlite3Error(p->db, SQLITE_RANGE, 0);
+ return SQLITE_RANGE;
+ }
+ i--;
+ pVar = &p->aVar[i];
+ sqlite3VdbeMemRelease(pVar);
+ pVar->flags = MEM_Null;
+ sqlite3Error(p->db, SQLITE_OK, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Bind a text or BLOB value.
+*/
+static int bindText(
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ int nData,
+ void (*xDel)(void*),
+ int encoding
+){
+ Vdbe *p = (Vdbe *)pStmt;
+ Mem *pVar;
+ int rc;
+
+ rc = vdbeUnbind(p, i);
+ if( rc || zData==0 ){
+ return rc;
+ }
+ pVar = &p->aVar[i-1];
+ rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
+ if( rc ){
+ return rc;
+ }
+ if( rc==SQLITE_OK && encoding!=0 ){
+ rc = sqlite3VdbeChangeEncoding(pVar, p->db->enc);
+ }
+ return rc;
+}
+
+
+/*
+** Bind a blob value to an SQL statement variable.
+*/
+int sqlite3_bind_blob(
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ int nData,
+ void (*xDel)(void*)
+){
+ return bindText(pStmt, i, zData, nData, xDel, 0);
+}
+int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
+ int rc;
+ Vdbe *p = (Vdbe *)pStmt;
+ rc = vdbeUnbind(p, i);
+ if( rc==SQLITE_OK ){
+ sqlite3VdbeMemSetDouble(&p->aVar[i-1], rValue);
+ }
+ return rc;
+}
+int sqlite3_bind_int(sqlite3_stmt *p, int i, int iValue){
+ return sqlite3_bind_int64(p, i, (i64)iValue);
+}
+int sqlite3_bind_int64(sqlite3_stmt *pStmt, int i, sqlite_int64 iValue){
+ int rc;
+ Vdbe *p = (Vdbe *)pStmt;
+ rc = vdbeUnbind(p, i);
+ if( rc==SQLITE_OK ){
+ sqlite3VdbeMemSetInt64(&p->aVar[i-1], iValue);
+ }
+ return rc;
+}
+int sqlite3_bind_null(sqlite3_stmt* p, int i){
+ return vdbeUnbind((Vdbe *)p, i);
+}
+int sqlite3_bind_text(
+ sqlite3_stmt *pStmt,
+ int i,
+ const char *zData,
+ int nData,
+ void (*xDel)(void*)
+){
+ return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8);
+}
+int sqlite3_bind_text16(
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ int nData,
+ void (*xDel)(void*)
+){
+ return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF16NATIVE);
+}
+
+/*
+** Return the number of wildcards that can be potentially bound to.
+** This routine is added to support DBD::SQLite.
+*/
+int sqlite3_bind_parameter_count(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe*)pStmt;
+ return p ? p->nVar : 0;
+}
+
+/*
+** Create a mapping from variable numbers to variable names
+** in the Vdbe.azVar[] array, if such a mapping does not already
+** exist.
+*/
+static void createVarMap(Vdbe *p){
+ if( !p->okVar ){
+ int j;
+ Op *pOp;
+ for(j=0, pOp=p->aOp; j<p->nOp; j++, pOp++){
+ if( pOp->opcode==OP_Variable ){
+ assert( pOp->p1>0 && pOp->p1<=p->nVar );
+ p->azVar[pOp->p1-1] = pOp->p3;
+ }
+ }
+ p->okVar = 1;
+ }
+}
+
+/*
+** Return the name of a wildcard parameter. Return NULL if the index
+** is out of range or if the wildcard is unnamed.
+**
+** The result is always UTF-8.
+*/
+const char *sqlite3_bind_parameter_name(sqlite3_stmt *pStmt, int i){
+ Vdbe *p = (Vdbe*)pStmt;
+ if( p==0 || i<1 || i>p->nVar ){
+ return 0;
+ }
+ createVarMap(p);
+ return p->azVar[i-1];
+}
+
+/*
+** Given a wildcard parameter name, return the index of the variable
+** with that name. If there is no variable with the given name,
+** return 0.
+*/
+int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){
+ Vdbe *p = (Vdbe*)pStmt;
+ int i;
+ if( p==0 ){
+ return 0;
+ }
+ createVarMap(p);
+ for(i=0; i<p->nVar; i++){
+ const char *z = p->azVar[i];
+ if( z && strcmp(z,zName)==0 ){
+ return i+1;
+ }
+ }
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/vdbeaux.c b/kopete/plugins/statistics/sqlite/vdbeaux.c
new file mode 100644
index 00000000..fa9751da
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbeaux.c
@@ -0,0 +1,1806 @@
+/*
+** 2003 September 6
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This file contains code used for creating, destroying, and populating
+** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior
+** to version 2.8.7, all this code was combined into the vdbe.c source file.
+** But that file was getting too big so this subroutines were split out.
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+#include "vdbeInt.h"
+
+
+/*
+** When debugging the code generator in a symbolic debugger, one can
+** set the sqlite3_vdbe_addop_trace to 1 and all opcodes will be printed
+** as they are added to the instruction stream.
+*/
+#ifndef NDEBUG
+int sqlite3_vdbe_addop_trace = 0;
+#endif
+
+
+/*
+** Create a new virtual database engine.
+*/
+Vdbe *sqlite3VdbeCreate(sqlite3 *db){
+ Vdbe *p;
+ p = sqliteMalloc( sizeof(Vdbe) );
+ if( p==0 ) return 0;
+ p->db = db;
+ if( db->pVdbe ){
+ db->pVdbe->pPrev = p;
+ }
+ p->pNext = db->pVdbe;
+ p->pPrev = 0;
+ db->pVdbe = p;
+ p->magic = VDBE_MAGIC_INIT;
+ return p;
+}
+
+/*
+** Turn tracing on or off
+*/
+void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
+ p->trace = trace;
+}
+
+/*
+** Resize the Vdbe.aOp array so that it contains at least N
+** elements.
+*/
+static void resizeOpArray(Vdbe *p, int N){
+ if( p->nOpAlloc<N ){
+ int oldSize = p->nOpAlloc;
+ p->nOpAlloc = N+100;
+ p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op));
+ if( p->aOp ){
+ memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op));
+ }
+ }
+}
+
+/*
+** Add a new instruction to the list of instructions current in the
+** VDBE. Return the address of the new instruction.
+**
+** Parameters:
+**
+** p Pointer to the VDBE
+**
+** op The opcode for this instruction
+**
+** p1, p2 First two of the three possible operands.
+**
+** Use the sqlite3VdbeResolveLabel() function to fix an address and
+** the sqlite3VdbeChangeP3() function to change the value of the P3
+** operand.
+*/
+int sqlite3VdbeAddOp(Vdbe *p, int op, int p1, int p2){
+ int i;
+ VdbeOp *pOp;
+
+ i = p->nOp;
+ p->nOp++;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ resizeOpArray(p, i+1);
+ if( p->aOp==0 ){
+ return 0;
+ }
+ pOp = &p->aOp[i];
+ pOp->opcode = op;
+ pOp->p1 = p1;
+ pOp->p2 = p2;
+ pOp->p3 = 0;
+ pOp->p3type = P3_NOTUSED;
+#ifndef NDEBUG
+ if( sqlite3_vdbe_addop_trace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+#endif
+ return i;
+}
+
+/*
+** Add an opcode that includes the p3 value.
+*/
+int sqlite3VdbeOp3(Vdbe *p, int op, int p1, int p2, const char *zP3,int p3type){
+ int addr = sqlite3VdbeAddOp(p, op, p1, p2);
+ sqlite3VdbeChangeP3(p, addr, zP3, p3type);
+ return addr;
+}
+
+/*
+** Create a new symbolic label for an instruction that has yet to be
+** coded. The symbolic label is really just a negative number. The
+** label can be used as the P2 value of an operation. Later, when
+** the label is resolved to a specific address, the VDBE will scan
+** through its operation list and change all values of P2 which match
+** the label into the resolved address.
+**
+** The VDBE knows that a P2 value is a label because labels are
+** always negative and P2 values are suppose to be non-negative.
+** Hence, a negative P2 value is a label that has yet to be resolved.
+**
+** Zero is returned if a malloc() fails.
+*/
+int sqlite3VdbeMakeLabel(Vdbe *p){
+ int i;
+ i = p->nLabel++;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( i>=p->nLabelAlloc ){
+ p->nLabelAlloc = p->nLabelAlloc*2 + 10;
+ p->aLabel = sqliteRealloc( p->aLabel, p->nLabelAlloc*sizeof(p->aLabel[0]));
+ }
+ if( p->aLabel ){
+ p->aLabel[i] = -1;
+ }
+ return -1-i;
+}
+
+/*
+** Resolve label "x" to be the address of the next instruction to
+** be inserted. The parameter "x" must have been obtained from
+** a prior call to sqlite3VdbeMakeLabel().
+*/
+void sqlite3VdbeResolveLabel(Vdbe *p, int x){
+ int j = -1-x;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ assert( j>=0 && j<p->nLabel );
+ if( p->aLabel ){
+ p->aLabel[j] = p->nOp;
+ }
+}
+
+/*
+** Loop through the program looking for P2 values that are negative.
+** Each such value is a label. Resolve the label by setting the P2
+** value to its correct non-zero value.
+**
+** This routine is called once after all opcodes have been inserted.
+*/
+static void resolveP2Values(Vdbe *p){
+ int i;
+ Op *pOp;
+ int *aLabel = p->aLabel;
+ if( aLabel==0 ) return;
+ for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
+ if( pOp->p2>=0 ) continue;
+ assert( -1-pOp->p2<p->nLabel );
+ pOp->p2 = aLabel[-1-pOp->p2];
+ }
+ sqliteFree(p->aLabel);
+ p->aLabel = 0;
+}
+
+/*
+** Return the address of the next instruction to be inserted.
+*/
+int sqlite3VdbeCurrentAddr(Vdbe *p){
+ assert( p->magic==VDBE_MAGIC_INIT );
+ return p->nOp;
+}
+
+/*
+** Add a whole list of operations to the operation stack. Return the
+** address of the first operation added.
+*/
+int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
+ int addr;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ resizeOpArray(p, p->nOp + nOp);
+ if( p->aOp==0 ){
+ return 0;
+ }
+ addr = p->nOp;
+ if( nOp>0 ){
+ int i;
+ VdbeOpList const *pIn = aOp;
+ for(i=0; i<nOp; i++, pIn++){
+ int p2 = pIn->p2;
+ VdbeOp *pOut = &p->aOp[i+addr];
+ pOut->opcode = pIn->opcode;
+ pOut->p1 = pIn->p1;
+ pOut->p2 = p2<0 ? addr + ADDR(p2) : p2;
+ pOut->p3 = pIn->p3;
+ pOut->p3type = pIn->p3 ? P3_STATIC : P3_NOTUSED;
+#ifndef NDEBUG
+ if( sqlite3_vdbe_addop_trace ){
+ sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
+ }
+#endif
+ }
+ p->nOp += nOp;
+ }
+ return addr;
+}
+
+/*
+** Change the value of the P1 operand for a specific instruction.
+** This routine is useful when a large program is loaded from a
+** static array using sqlite3VdbeAddOpList but we want to make a
+** few minor changes to the program.
+*/
+void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( p && addr>=0 && p->nOp>addr && p->aOp ){
+ p->aOp[addr].p1 = val;
+ }
+}
+
+/*
+** Change the value of the P2 operand for a specific instruction.
+** This routine is useful for setting a jump destination.
+*/
+void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
+ assert( val>=0 );
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( p && addr>=0 && p->nOp>addr && p->aOp ){
+ p->aOp[addr].p2 = val;
+ }
+}
+
+/*
+** Change the value of the P3 operand for a specific instruction.
+** This routine is useful when a large program is loaded from a
+** static array using sqlite3VdbeAddOpList but we want to make a
+** few minor changes to the program.
+**
+** If n>=0 then the P3 operand is dynamic, meaning that a copy of
+** the string is made into memory obtained from sqliteMalloc().
+** A value of n==0 means copy bytes of zP3 up to and including the
+** first null byte. If n>0 then copy n+1 bytes of zP3.
+**
+** If n==P3_STATIC it means that zP3 is a pointer to a constant static
+** string and we can just copy the pointer. n==P3_POINTER means zP3 is
+** a pointer to some object other than a string. n==P3_COLLSEQ and
+** n==P3_KEYINFO mean that zP3 is a pointer to a CollSeq or KeyInfo
+** structure. A copy is made of KeyInfo structures into memory obtained
+** from sqliteMalloc.
+**
+** If addr<0 then change P3 on the most recently inserted instruction.
+*/
+void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
+ Op *pOp;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( p==0 || p->aOp==0 ) return;
+ if( addr<0 || addr>=p->nOp ){
+ addr = p->nOp - 1;
+ if( addr<0 ) return;
+ }
+ pOp = &p->aOp[addr];
+ if( pOp->p3 && pOp->p3type==P3_DYNAMIC ){
+ sqliteFree(pOp->p3);
+ pOp->p3 = 0;
+ }
+ if( zP3==0 ){
+ pOp->p3 = 0;
+ pOp->p3type = P3_NOTUSED;
+ }else if( n==P3_KEYINFO ){
+ KeyInfo *pKeyInfo;
+ int nField, nByte;
+ nField = ((KeyInfo*)zP3)->nField;
+ nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]);
+ pKeyInfo = sqliteMallocRaw( nByte );
+ pOp->p3 = (char*)pKeyInfo;
+ if( pKeyInfo ){
+ memcpy(pKeyInfo, zP3, nByte);
+ pOp->p3type = P3_KEYINFO;
+ }else{
+ pOp->p3type = P3_NOTUSED;
+ }
+ }else if( n==P3_KEYINFO_HANDOFF ){
+ pOp->p3 = (char*)zP3;
+ pOp->p3type = P3_KEYINFO;
+ }else if( n<0 ){
+ pOp->p3 = (char*)zP3;
+ pOp->p3type = n;
+ }else{
+ if( n==0 ) n = strlen(zP3);
+ pOp->p3 = sqliteStrNDup(zP3, n);
+ pOp->p3type = P3_DYNAMIC;
+ }
+}
+
+#ifndef NDEBUG
+/*
+** Replace the P3 field of the most recently coded instruction with
+** comment text.
+*/
+void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ assert( p->nOp>0 );
+ assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 );
+ va_start(ap, zFormat);
+ sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC);
+ va_end(ap);
+}
+#endif
+
+/*
+** If the P3 operand to the specified instruction appears
+** to be a quoted string token, then this procedure removes
+** the quotes.
+**
+** The quoting operator can be either a grave ascent (ASCII 0x27)
+** or a double quote character (ASCII 0x22). Two quotes in a row
+** resolve to be a single actual quote character within the string.
+*/
+void sqlite3VdbeDequoteP3(Vdbe *p, int addr){
+ Op *pOp;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ if( p->aOp==0 ) return;
+ if( addr<0 || addr>=p->nOp ){
+ addr = p->nOp - 1;
+ if( addr<0 ) return;
+ }
+ pOp = &p->aOp[addr];
+ if( pOp->p3==0 || pOp->p3[0]==0 ) return;
+ if( pOp->p3type==P3_STATIC ){
+ pOp->p3 = sqliteStrDup(pOp->p3);
+ pOp->p3type = P3_DYNAMIC;
+ }
+ assert( pOp->p3type==P3_DYNAMIC );
+ sqlite3Dequote(pOp->p3);
+}
+
+/*
+** Search the current program starting at instruction addr for the given
+** opcode and P2 value. Return the address plus 1 if found and 0 if not
+** found.
+*/
+int sqlite3VdbeFindOp(Vdbe *p, int addr, int op, int p2){
+ int i;
+ assert( p->magic==VDBE_MAGIC_INIT );
+ for(i=addr; i<p->nOp; i++){
+ if( p->aOp[i].opcode==op && p->aOp[i].p2==p2 ) return i+1;
+ }
+ return 0;
+}
+
+/*
+** Return the opcode for a given address.
+*/
+VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
+ assert( p->magic==VDBE_MAGIC_INIT );
+ assert( addr>=0 && addr<p->nOp );
+ return &p->aOp[addr];
+}
+
+/*
+** Compute a string that describes the P3 parameter for an opcode.
+** Use zTemp for any required temporary buffer space.
+*/
+static char *displayP3(Op *pOp, char *zTemp, int nTemp){
+ char *zP3;
+ assert( nTemp>=20 );
+ switch( pOp->p3type ){
+ case P3_POINTER: {
+ sprintf(zTemp, "ptr(%#x)", (int)pOp->p3);
+ zP3 = zTemp;
+ break;
+ }
+ case P3_KEYINFO: {
+ int i, j;
+ KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3;
+ sprintf(zTemp, "keyinfo(%d", pKeyInfo->nField);
+ i = strlen(zTemp);
+ for(j=0; j<pKeyInfo->nField; j++){
+ CollSeq *pColl = pKeyInfo->aColl[j];
+ if( pColl ){
+ int n = strlen(pColl->zName);
+ if( i+n>nTemp-6 ){
+ strcpy(&zTemp[i],",...");
+ break;
+ }
+ zTemp[i++] = ',';
+ if( pKeyInfo->aSortOrder && pKeyInfo->aSortOrder[j] ){
+ zTemp[i++] = '-';
+ }
+ strcpy(&zTemp[i], pColl->zName);
+ i += n;
+ }else if( i+4<nTemp-6 ){
+ strcpy(&zTemp[i],",nil");
+ i += 4;
+ }
+ }
+ zTemp[i++] = ')';
+ zTemp[i] = 0;
+ assert( i<nTemp );
+ zP3 = zTemp;
+ break;
+ }
+ case P3_COLLSEQ: {
+ CollSeq *pColl = (CollSeq*)pOp->p3;
+ sprintf(zTemp, "collseq(%.20s)", pColl->zName);
+ zP3 = zTemp;
+ break;
+ }
+ case P3_FUNCDEF: {
+ FuncDef *pDef = (FuncDef*)pOp->p3;
+ char zNum[30];
+ sprintf(zTemp, "%.*s", nTemp, pDef->zName);
+ sprintf(zNum,"(%d)", pDef->nArg);
+ if( strlen(zTemp)+strlen(zNum)+1<=nTemp ){
+ strcat(zTemp, zNum);
+ }
+ zP3 = zTemp;
+ break;
+ }
+ default: {
+ zP3 = pOp->p3;
+ if( zP3==0 || pOp->opcode==OP_Noop ){
+ zP3 = "";
+ }
+ }
+ }
+ return zP3;
+}
+
+
+#if !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+/*
+** Print a single opcode. This routine is used for debugging only.
+*/
+void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
+ char *zP3;
+ char zPtr[50];
+ static const char *zFormat1 = "%4d %-13s %4d %4d %s\n";
+ if( pOut==0 ) pOut = stdout;
+ zP3 = displayP3(pOp, zPtr, sizeof(zPtr));
+ fprintf(pOut, zFormat1,
+ pc, sqlite3OpcodeNames[pOp->opcode], pOp->p1, pOp->p2, zP3);
+ fflush(pOut);
+}
+#endif
+
+/*
+** Release an array of N Mem elements
+*/
+static void releaseMemArray(Mem *p, int N){
+ if( p ){
+ while( N-->0 ){
+ sqlite3VdbeMemRelease(p++);
+ }
+ }
+}
+
+/*
+** Give a listing of the program in the virtual machine.
+**
+** The interface is the same as sqlite3VdbeExec(). But instead of
+** running the code, it invokes the callback once for each instruction.
+** This feature is used to implement "EXPLAIN".
+*/
+int sqlite3VdbeList(
+ Vdbe *p /* The VDBE */
+){
+ sqlite3 *db = p->db;
+ int i;
+ int rc = SQLITE_OK;
+
+ assert( p->explain );
+
+ /* Even though this opcode does not put dynamic strings onto the
+ ** the stack, they may become dynamic if the user calls
+ ** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
+ */
+ if( p->pTos==&p->aStack[4] ){
+ releaseMemArray(p->aStack, 5);
+ }
+ p->resOnStack = 0;
+
+ i = p->pc++;
+ if( i>=p->nOp ){
+ p->rc = SQLITE_OK;
+ rc = SQLITE_DONE;
+ }else if( db->flags & SQLITE_Interrupt ){
+ db->flags &= ~SQLITE_Interrupt;
+ if( db->magic!=SQLITE_MAGIC_BUSY ){
+ p->rc = SQLITE_MISUSE;
+ }else{
+ p->rc = SQLITE_INTERRUPT;
+ }
+ rc = SQLITE_ERROR;
+ sqlite3SetString(&p->zErrMsg, sqlite3ErrStr(p->rc), (char*)0);
+ }else{
+ Op *pOp = &p->aOp[i];
+ Mem *pMem = p->aStack;
+ pMem->flags = MEM_Int;
+ pMem->type = SQLITE_INTEGER;
+ pMem->i = i; /* Program counter */
+ pMem++;
+
+ pMem->flags = MEM_Static|MEM_Str|MEM_Term;
+ pMem->z = sqlite3OpcodeNames[pOp->opcode]; /* Opcode */
+ pMem->n = strlen(pMem->z);
+ pMem->type = SQLITE_TEXT;
+ pMem->enc = SQLITE_UTF8;
+ pMem++;
+
+ pMem->flags = MEM_Int;
+ pMem->i = pOp->p1; /* P1 */
+ pMem->type = SQLITE_INTEGER;
+ pMem++;
+
+ pMem->flags = MEM_Int;
+ pMem->i = pOp->p2; /* P2 */
+ pMem->type = SQLITE_INTEGER;
+ pMem++;
+
+ pMem->flags = MEM_Short|MEM_Str|MEM_Term; /* P3 */
+ pMem->z = displayP3(pOp, pMem->zShort, sizeof(pMem->zShort));
+ pMem->type = SQLITE_TEXT;
+ pMem->enc = SQLITE_UTF8;
+
+ p->nResColumn = 5;
+ p->pTos = pMem;
+ p->rc = SQLITE_OK;
+ p->resOnStack = 1;
+ rc = SQLITE_ROW;
+ }
+ return rc;
+}
+
+/*
+** Print the SQL that was used to generate a VDBE program.
+*/
+void sqlite3VdbePrintSql(Vdbe *p){
+#ifdef SQLITE_DEBUG
+ int nOp = p->nOp;
+ VdbeOp *pOp;
+ if( nOp<1 ) return;
+ pOp = &p->aOp[nOp-1];
+ if( pOp->opcode==OP_Noop && pOp->p3!=0 ){
+ const char *z = pOp->p3;
+ while( isspace(*(u8*)z) ) z++;
+ printf("SQL: [%s]\n", z);
+ }
+#endif
+}
+
+/*
+** Prepare a virtual machine for execution. This involves things such
+** as allocating stack space and initializing the program counter.
+** After the VDBE has be prepped, it can be executed by one or more
+** calls to sqlite3VdbeExec().
+**
+** This is the only way to move a VDBE from VDBE_MAGIC_INIT to
+** VDBE_MAGIC_RUN.
+*/
+void sqlite3VdbeMakeReady(
+ Vdbe *p, /* The VDBE */
+ int nVar, /* Number of '?' see in the SQL statement */
+ int nMem, /* Number of memory cells to allocate */
+ int nCursor, /* Number of cursors to allocate */
+ int isExplain /* True if the EXPLAIN keywords is present */
+){
+ int n;
+
+ assert( p!=0 );
+ assert( p->magic==VDBE_MAGIC_INIT );
+
+ /* There should be at least one opcode.
+ */
+ assert( p->nOp>0 );
+
+ /* No instruction ever pushes more than a single element onto the
+ ** stack. And the stack never grows on successive executions of the
+ ** same loop. So the total number of instructions is an upper bound
+ ** on the maximum stack depth required.
+ **
+ ** Allocation all the stack space we will ever need.
+ */
+ if( p->aStack==0 ){
+ resolveP2Values(p);
+ assert( nVar>=0 );
+ n = isExplain ? 10 : p->nOp;
+ p->aStack = sqliteMalloc(
+ n*sizeof(p->aStack[0]) /* aStack */
+ + n*sizeof(Mem*) /* apArg */
+ + nVar*sizeof(Mem) /* aVar */
+ + nVar*sizeof(char*) /* azVar */
+ + nMem*sizeof(Mem) /* aMem */
+ + nCursor*sizeof(Cursor*) /* apCsr */
+ );
+ if( !sqlite3_malloc_failed ){
+ p->aMem = &p->aStack[n];
+ p->nMem = nMem;
+ p->aVar = &p->aMem[nMem];
+ p->nVar = nVar;
+ p->okVar = 0;
+ p->apArg = (Mem**)&p->aVar[nVar];
+ p->azVar = (char**)&p->apArg[n];
+ p->apCsr = (Cursor**)&p->azVar[nVar];
+ p->nCursor = nCursor;
+ for(n=0; n<nVar; n++){
+ p->aVar[n].flags = MEM_Null;
+ }
+ for(n=0; n<nMem; n++){
+ p->aMem[n].flags = MEM_Null;
+ }
+ }
+ }
+
+#ifdef SQLITE_DEBUG
+ if( (p->db->flags & SQLITE_VdbeListing)!=0
+ || sqlite3OsFileExists("vdbe_explain")
+ ){
+ int i;
+ printf("VDBE Program Listing:\n");
+ sqlite3VdbePrintSql(p);
+ for(i=0; i<p->nOp; i++){
+ sqlite3VdbePrintOp(stdout, i, &p->aOp[i]);
+ }
+ }
+ if( sqlite3OsFileExists("vdbe_trace") ){
+ p->trace = stdout;
+ }
+#endif
+ p->pTos = &p->aStack[-1];
+ p->pc = -1;
+ p->rc = SQLITE_OK;
+ p->uniqueCnt = 0;
+ p->returnDepth = 0;
+ p->errorAction = OE_Abort;
+ p->popStack = 0;
+ p->explain |= isExplain;
+ p->magic = VDBE_MAGIC_RUN;
+ p->nChange = 0;
+#ifdef VDBE_PROFILE
+ {
+ int i;
+ for(i=0; i<p->nOp; i++){
+ p->aOp[i].cnt = 0;
+ p->aOp[i].cycles = 0;
+ }
+ }
+#endif
+}
+
+
+/*
+** Remove any elements that remain on the sorter for the VDBE given.
+*/
+void sqlite3VdbeSorterReset(Vdbe *p){
+ while( p->pSort ){
+ Sorter *pSorter = p->pSort;
+ p->pSort = pSorter->pNext;
+ sqliteFree(pSorter->zKey);
+ sqlite3VdbeMemRelease(&pSorter->data);
+ sqliteFree(pSorter);
+ }
+}
+
+/*
+** Free all resources allociated with AggElem pElem, an element of
+** aggregate pAgg.
+*/
+void freeAggElem(AggElem *pElem, Agg *pAgg){
+ int i;
+ for(i=0; i<pAgg->nMem; i++){
+ Mem *pMem = &pElem->aMem[i];
+ if( pAgg->apFunc && pAgg->apFunc[i] && (pMem->flags & MEM_AggCtx)!=0 ){
+ sqlite3_context ctx;
+ ctx.pFunc = pAgg->apFunc[i];
+ ctx.s.flags = MEM_Null;
+ ctx.pAgg = pMem->z;
+ ctx.cnt = pMem->i;
+ ctx.isStep = 0;
+ ctx.isError = 0;
+ (*pAgg->apFunc[i]->xFinalize)(&ctx);
+ pMem->z = ctx.pAgg;
+ if( pMem->z!=0 && pMem->z!=pMem->zShort ){
+ sqliteFree(pMem->z);
+ }
+ sqlite3VdbeMemRelease(&ctx.s);
+ }else{
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ sqliteFree(pElem);
+}
+
+/*
+** Reset an Agg structure. Delete all its contents.
+**
+** For installable aggregate functions, if the step function has been
+** called, make sure the finalizer function has also been called. The
+** finalizer might need to free memory that was allocated as part of its
+** private context. If the finalizer has not been called yet, call it
+** now.
+**
+** If db is NULL, then this is being called from sqliteVdbeReset(). In
+** this case clean up all references to the temp-table used for
+** aggregates (if it was ever opened).
+**
+** If db is not NULL, then this is being called from with an OP_AggReset
+** opcode. Open the temp-table, if it has not already been opened and
+** delete the contents of the table used for aggregate information, ready
+** for the next round of aggregate processing.
+*/
+int sqlite3VdbeAggReset(sqlite3 *db, Agg *pAgg, KeyInfo *pKeyInfo){
+ int rc = 0;
+ BtCursor *pCsr = pAgg->pCsr;
+
+ assert( (pCsr && pAgg->nTab>0) || (!pCsr && pAgg->nTab==0)
+ || sqlite3_malloc_failed );
+
+ /* If pCsr is not NULL, then the table used for aggregate information
+ ** is open. Loop through it and free the AggElem* structure pointed at
+ ** by each entry. If the finalizer has not been called for an AggElem,
+ ** do that too. Finally, clear the btree table itself.
+ */
+ if( pCsr ){
+ int res;
+ assert( pAgg->pBtree );
+ assert( pAgg->nTab>0 );
+
+ rc=sqlite3BtreeFirst(pCsr, &res);
+ while( res==0 && rc==SQLITE_OK ){
+ AggElem *pElem;
+ rc = sqlite3BtreeData(pCsr, 0, sizeof(AggElem*), (char *)&pElem);
+ if( res!=SQLITE_OK ){
+ return rc;
+ }
+ assert( pAgg->apFunc!=0 );
+ freeAggElem(pElem, pAgg);
+ rc=sqlite3BtreeNext(pCsr, &res);
+ }
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+
+ sqlite3BtreeCloseCursor(pCsr);
+ sqlite3BtreeClearTable(pAgg->pBtree, pAgg->nTab);
+ }else{
+ /* The cursor may not be open because the aggregator was never used,
+ ** or it could be that it was used but there was no GROUP BY clause.
+ */
+ if( pAgg->pCurrent ){
+ freeAggElem(pAgg->pCurrent, pAgg);
+ }
+ }
+
+ /* If db is not NULL and we have not yet and we have not yet opened
+ ** the temporary btree then do so and create the table to store aggregate
+ ** information.
+ **
+ ** If db is NULL, then close the temporary btree if it is open.
+ */
+ if( db ){
+ if( !pAgg->pBtree ){
+ assert( pAgg->nTab==0 );
+ rc = sqlite3BtreeFactory(db, ":memory:", 0, TEMP_PAGES, &pAgg->pBtree);
+ if( rc!=SQLITE_OK ) return rc;
+ sqlite3BtreeBeginTrans(pAgg->pBtree, 1);
+ rc = sqlite3BtreeCreateTable(pAgg->pBtree, &pAgg->nTab, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ assert( pAgg->nTab!=0 );
+
+ rc = sqlite3BtreeCursor(pAgg->pBtree, pAgg->nTab, 1,
+ sqlite3VdbeRecordCompare, pKeyInfo, &pAgg->pCsr);
+ if( rc!=SQLITE_OK ) return rc;
+ }else{
+ if( pAgg->pBtree ){
+ sqlite3BtreeClose(pAgg->pBtree);
+ pAgg->pBtree = 0;
+ pAgg->nTab = 0;
+ }
+ pAgg->pCsr = 0;
+ }
+
+ if( pAgg->apFunc ){
+ sqliteFree(pAgg->apFunc);
+ pAgg->apFunc = 0;
+ }
+ pAgg->pCurrent = 0;
+ pAgg->nMem = 0;
+ pAgg->searching = 0;
+ return SQLITE_OK;
+}
+
+
+/*
+** Delete a keylist
+*/
+void sqlite3VdbeKeylistFree(Keylist *p){
+ while( p ){
+ Keylist *pNext = p->pNext;
+ sqliteFree(p);
+ p = pNext;
+ }
+}
+
+/*
+** Close a cursor and release all the resources that cursor happens
+** to hold.
+*/
+void sqlite3VdbeFreeCursor(Cursor *pCx){
+ if( pCx==0 ){
+ return;
+ }
+ if( pCx->pCursor ){
+ sqlite3BtreeCloseCursor(pCx->pCursor);
+ }
+ if( pCx->pBt ){
+ sqlite3BtreeClose(pCx->pBt);
+ }
+ sqliteFree(pCx->pData);
+ sqliteFree(pCx->aType);
+ sqliteFree(pCx);
+}
+
+/*
+** Close all cursors
+*/
+static void closeAllCursors(Vdbe *p){
+ int i;
+ if( p->apCsr==0 ) return;
+ for(i=0; i<p->nCursor; i++){
+ sqlite3VdbeFreeCursor(p->apCsr[i]);
+ p->apCsr[i] = 0;
+ }
+}
+
+/*
+** Clean up the VM after execution.
+**
+** This routine will automatically close any cursors, lists, and/or
+** sorters that were left open. It also deletes the values of
+** variables in the aVar[] array.
+*/
+static void Cleanup(Vdbe *p){
+ int i;
+ if( p->aStack ){
+ releaseMemArray(p->aStack, 1 + (p->pTos - p->aStack));
+ p->pTos = &p->aStack[-1];
+ }
+ closeAllCursors(p);
+ releaseMemArray(p->aMem, p->nMem);
+ if( p->pList ){
+ sqlite3VdbeKeylistFree(p->pList);
+ p->pList = 0;
+ }
+ if( p->contextStack ){
+ for(i=0; i<p->contextStackTop; i++){
+ sqlite3VdbeKeylistFree(p->contextStack[i].pList);
+ }
+ sqliteFree(p->contextStack);
+ }
+ sqlite3VdbeSorterReset(p);
+ sqlite3VdbeAggReset(0, &p->agg, 0);
+ p->contextStack = 0;
+ p->contextStackDepth = 0;
+ p->contextStackTop = 0;
+ sqliteFree(p->zErrMsg);
+ p->zErrMsg = 0;
+}
+
+/*
+** Set the number of result columns that will be returned by this SQL
+** statement. This is now set at compile time, rather than during
+** execution of the vdbe program so that sqlite3_column_count() can
+** be called on an SQL statement before sqlite3_step().
+*/
+void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
+ Mem *pColName;
+ int n;
+ assert( 0==p->nResColumn );
+ p->nResColumn = nResColumn;
+ n = nResColumn*2;
+ p->aColName = pColName = (Mem*)sqliteMalloc( sizeof(Mem)*n );
+ if( p->aColName==0 ) return;
+ while( n-- > 0 ){
+ (pColName++)->flags = MEM_Null;
+ }
+}
+
+/*
+** Set the name of the idx'th column to be returned by the SQL statement.
+** zName must be a pointer to a nul terminated string.
+**
+** This call must be made after a call to sqlite3VdbeSetNumCols().
+**
+** If N==P3_STATIC it means that zName is a pointer to a constant static
+** string and we can just copy the pointer. If it is P3_DYNAMIC, then
+** the string is freed using sqliteFree() when the vdbe is finished with
+** it. Otherwise, N bytes of zName are copied.
+*/
+int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){
+ int rc;
+ Mem *pColName;
+ assert( idx<(2*p->nResColumn) );
+ if( sqlite3_malloc_failed ) return SQLITE_NOMEM;
+ assert( p->aColName!=0 );
+ pColName = &(p->aColName[idx]);
+ if( N==P3_DYNAMIC || N==P3_STATIC ){
+ rc = sqlite3VdbeMemSetStr(pColName, zName, -1, SQLITE_UTF8, SQLITE_STATIC);
+ }else{
+ rc = sqlite3VdbeMemSetStr(pColName, zName, N, SQLITE_UTF8,SQLITE_TRANSIENT);
+ }
+ if( rc==SQLITE_OK && N==P3_DYNAMIC ){
+ pColName->flags = (pColName->flags&(~MEM_Static))|MEM_Dyn;
+ pColName->xDel = 0;
+ }
+ return rc;
+}
+
+/*
+** A read or write transaction may or may not be active on database handle
+** db. If a transaction is active, commit it. If there is a
+** write-transaction spanning more than one database file, this routine
+** takes care of the master journal trickery.
+*/
+static int vdbeCommit(sqlite3 *db){
+ int i;
+ int nTrans = 0; /* Number of databases with an active write-transaction */
+ int rc = SQLITE_OK;
+ int needXcommit = 0;
+
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ needXcommit = 1;
+ if( i!=1 ) nTrans++;
+ }
+ }
+
+ /* If there are any write-transactions at all, invoke the commit hook */
+ if( needXcommit && db->xCommitCallback ){
+ int rc;
+ sqlite3SafetyOff(db);
+ rc = db->xCommitCallback(db->pCommitArg);
+ sqlite3SafetyOn(db);
+ if( rc ){
+ return SQLITE_CONSTRAINT;
+ }
+ }
+
+ /* The simple case - no more than one database file (not counting the
+ ** TEMP database) has a transaction active. There is no need for the
+ ** master-journal.
+ **
+ ** If the return value of sqlite3BtreeGetFilename() is a zero length
+ ** string, it means the main database is :memory:. In that case we do
+ ** not support atomic multi-file commits, so use the simple case then
+ ** too.
+ */
+ if( 0==strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt)) || nTrans<=1 ){
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ rc = sqlite3BtreeSync(pBt, 0);
+ }
+ }
+
+ /* Do the commit only if all databases successfully synced */
+ if( rc==SQLITE_OK ){
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ sqlite3BtreeCommit(pBt);
+ }
+ }
+ }
+ }
+
+ /* The complex case - There is a multi-file write-transaction active.
+ ** This requires a master journal file to ensure the transaction is
+ ** committed atomicly.
+ */
+ else{
+ char *zMaster = 0; /* File-name for the master journal */
+ char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
+ OsFile master;
+
+ /* Select a master journal file name */
+ do {
+ u32 random;
+ sqliteFree(zMaster);
+ sqlite3Randomness(sizeof(random), &random);
+ zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random&0x7fffffff);
+ if( !zMaster ){
+ return SQLITE_NOMEM;
+ }
+ }while( sqlite3OsFileExists(zMaster) );
+
+ /* Open the master journal. */
+ memset(&master, 0, sizeof(master));
+ rc = sqlite3OsOpenExclusive(zMaster, &master, 0);
+ if( rc!=SQLITE_OK ){
+ sqliteFree(zMaster);
+ return rc;
+ }
+
+ /* Write the name of each database file in the transaction into the new
+ ** master journal file. If an error occurs at this point close
+ ** and delete the master journal file. All the individual journal files
+ ** still have 'null' as the master journal pointer, so they will roll
+ ** back independantly if a failure occurs.
+ */
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( i==1 ) continue; /* Ignore the TEMP database */
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ char const *zFile = sqlite3BtreeGetJournalname(pBt);
+ if( zFile[0]==0 ) continue; /* Ignore :memory: databases */
+ rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsClose(&master);
+ sqlite3OsDelete(zMaster);
+ sqliteFree(zMaster);
+ return rc;
+ }
+ }
+ }
+
+
+ /* Sync the master journal file. Before doing this, open the directory
+ ** the master journal file is store in so that it gets synced too.
+ */
+ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt);
+ rc = sqlite3OsOpenDirectory(zMainFile, &master);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsClose(&master);
+ sqlite3OsDelete(zMaster);
+ sqliteFree(zMaster);
+ return rc;
+ }
+ rc = sqlite3OsSync(&master);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsClose(&master);
+ sqliteFree(zMaster);
+ return rc;
+ }
+
+ /* Sync all the db files involved in the transaction. The same call
+ ** sets the master journal pointer in each individual journal. If
+ ** an error occurs here, do not delete the master journal file.
+ **
+ ** If the error occurs during the first call to sqlite3BtreeSync(),
+ ** then there is a chance that the master journal file will be
+ ** orphaned. But we cannot delete it, in case the master journal
+ ** file name was written into the journal file before the failure
+ ** occured.
+ */
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ rc = sqlite3BtreeSync(pBt, zMaster);
+ if( rc!=SQLITE_OK ){
+ sqlite3OsClose(&master);
+ sqliteFree(zMaster);
+ return rc;
+ }
+ }
+ }
+ sqlite3OsClose(&master);
+
+ /* Delete the master journal file. This commits the transaction. After
+ ** doing this the directory is synced again before any individual
+ ** transaction files are deleted.
+ */
+ rc = sqlite3OsDelete(zMaster);
+ assert( rc==SQLITE_OK );
+ sqliteFree(zMaster);
+ zMaster = 0;
+ rc = sqlite3OsSyncDirectory(zMainFile);
+ if( rc!=SQLITE_OK ){
+ /* This is not good. The master journal file has been deleted, but
+ ** the directory sync failed. There is no completely safe course of
+ ** action from here. The individual journals contain the name of the
+ ** master journal file, but there is no way of knowing if that
+ ** master journal exists now or if it will exist after the operating
+ ** system crash that may follow the fsync() failure.
+ */
+ assert(0);
+ sqliteFree(zMaster);
+ return rc;
+ }
+
+ /* All files and directories have already been synced, so the following
+ ** calls to sqlite3BtreeCommit() are only closing files and deleting
+ ** journals. If something goes wrong while this is happening we don't
+ ** really care. The integrity of the transaction is already guaranteed,
+ ** but some stray 'cold' journals may be lying around. Returning an
+ ** error code won't help matters.
+ */
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ sqlite3BtreeCommit(pBt);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Find every active VM other than pVdbe and change its status to
+** aborted. This happens when one VM causes a rollback due to an
+** ON CONFLICT ROLLBACK clause (for example). The other VMs must be
+** aborted so that they do not have data rolled out from underneath
+** them leading to a segfault.
+*/
+static void abortOtherActiveVdbes(Vdbe *pVdbe){
+ Vdbe *pOther;
+ for(pOther=pVdbe->db->pVdbe; pOther; pOther=pOther->pNext){
+ if( pOther==pVdbe ) continue;
+ if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue;
+ closeAllCursors(pOther);
+ pOther->aborted = 1;
+ }
+}
+
+/*
+** This routine checks that the sqlite3.activeVdbeCnt count variable
+** matches the number of vdbe's in the list sqlite3.pVdbe that are
+** currently active. An assertion fails if the two counts do not match.
+** This is an internal self-check only - it is not an essential processing
+** step.
+**
+** This is a no-op if NDEBUG is defined.
+*/
+#ifndef NDEBUG
+static void checkActiveVdbeCnt(sqlite3 *db){
+ Vdbe *p;
+ int cnt = 0;
+ p = db->pVdbe;
+ while( p ){
+ if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
+ cnt++;
+ }
+ p = p->pNext;
+ }
+ assert( cnt==db->activeVdbeCnt );
+}
+#else
+#define checkActiveVdbeCnt(x)
+#endif
+
+/*
+** This routine is called the when a VDBE tries to halt. If the VDBE
+** has made changes and is in autocommit mode, then commit those
+** changes. If a rollback is needed, then do the rollback.
+**
+** This routine is the only way to move the state of a VM from
+** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT.
+**
+** Return an error code. If the commit could not complete because of
+** lock contention, return SQLITE_BUSY. If SQLITE_BUSY is returned, it
+** means the close did not happen and needs to be repeated.
+*/
+int sqlite3VdbeHalt(Vdbe *p){
+ sqlite3 *db = p->db;
+ int i;
+ int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */
+
+ if( p->magic!=VDBE_MAGIC_RUN ){
+ /* Already halted. Nothing to do. */
+ assert( p->magic==VDBE_MAGIC_HALT );
+ return SQLITE_OK;
+ }
+ closeAllCursors(p);
+ checkActiveVdbeCnt(db);
+ if( db->autoCommit && db->activeVdbeCnt==1 ){
+ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
+ /* The auto-commit flag is true, there are no other active queries
+ ** using this handle and the vdbe program was successful or hit an
+ ** 'OR FAIL' constraint. This means a commit is required.
+ */
+ int rc = vdbeCommit(db);
+ if( rc==SQLITE_BUSY ){
+ return SQLITE_BUSY;
+ }else if( rc!=SQLITE_OK ){
+ p->rc = rc;
+ xFunc = sqlite3BtreeRollback;
+ }
+ }else{
+ xFunc = sqlite3BtreeRollback;
+ }
+ }else{
+ if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
+ xFunc = sqlite3BtreeCommitStmt;
+ }else if( p->errorAction==OE_Abort ){
+ xFunc = sqlite3BtreeRollbackStmt;
+ }else{
+ xFunc = sqlite3BtreeRollback;
+ db->autoCommit = 1;
+ abortOtherActiveVdbes(p);
+ }
+ }
+
+ /* If xFunc is not NULL, then it is one of sqlite3BtreeRollback,
+ ** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
+ ** each backend. If an error occurs and the return code is still
+ ** SQLITE_OK, set the return code to the new error value.
+ */
+ for(i=0; xFunc && i<db->nDb; i++){
+ int rc;
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ rc = xFunc(pBt);
+ if( p->rc==SQLITE_OK ) p->rc = rc;
+ }
+ }
+
+ /* If this was an INSERT, UPDATE or DELETE, set the change counter. */
+ if( p->changeCntOn ){
+ if( !xFunc || xFunc==sqlite3BtreeCommitStmt ){
+ sqlite3VdbeSetChanges(db, p->nChange);
+ }else{
+ sqlite3VdbeSetChanges(db, 0);
+ }
+ p->nChange = 0;
+ }
+
+ /* Rollback or commit any schema changes that occurred. */
+ if( p->rc!=SQLITE_OK ){
+ sqlite3RollbackInternalChanges(db);
+ }else if( db->flags & SQLITE_InternChanges ){
+ sqlite3CommitInternalChanges(db);
+ }
+
+ /* We have successfully halted and closed the VM. Record this fact. */
+ if( p->pc>=0 ){
+ db->activeVdbeCnt--;
+ }
+ p->magic = VDBE_MAGIC_HALT;
+ checkActiveVdbeCnt(db);
+
+ return SQLITE_OK;
+}
+
+/*
+** Clean up a VDBE after execution but do not delete the VDBE just yet.
+** Write any error messages into *pzErrMsg. Return the result code.
+**
+** After this routine is run, the VDBE should be ready to be executed
+** again.
+**
+** To look at it another way, this routine resets the state of the
+** virtual machine from VDBE_MAGIC_RUN or VDBE_MAGIC_HALT back to
+** VDBE_MAGIC_INIT.
+*/
+int sqlite3VdbeReset(Vdbe *p){
+ if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){
+ sqlite3Error(p->db, SQLITE_MISUSE, 0);
+ return SQLITE_MISUSE;
+ }
+
+ /* If the VM did not run to completion or if it encountered an
+ ** error, then it might not have been halted properly. So halt
+ ** it now.
+ */
+ sqlite3VdbeHalt(p);
+
+ /* Transfer the error code and error message from the VDBE into the
+ ** main database structure.
+ */
+ if( p->zErrMsg ){
+ sqlite3Error(p->db, p->rc, "%s", p->zErrMsg);
+ sqliteFree(p->zErrMsg);
+ p->zErrMsg = 0;
+ }else if( p->rc ){
+ sqlite3Error(p->db, p->rc, 0);
+ }else{
+ sqlite3Error(p->db, SQLITE_OK, 0);
+ }
+
+ /* Reclaim all memory used by the VDBE
+ */
+ Cleanup(p);
+
+ /* Save profiling information from this VDBE run.
+ */
+ assert( p->pTos<&p->aStack[p->pc<0?0:p->pc] || sqlite3_malloc_failed==1 );
+#ifdef VDBE_PROFILE
+ {
+ FILE *out = fopen("vdbe_profile.out", "a");
+ if( out ){
+ int i;
+ fprintf(out, "---- ");
+ for(i=0; i<p->nOp; i++){
+ fprintf(out, "%02x", p->aOp[i].opcode);
+ }
+ fprintf(out, "\n");
+ for(i=0; i<p->nOp; i++){
+ fprintf(out, "%6d %10lld %8lld ",
+ p->aOp[i].cnt,
+ p->aOp[i].cycles,
+ p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
+ );
+ sqlite3VdbePrintOp(out, i, &p->aOp[i]);
+ }
+ fclose(out);
+ }
+ }
+#endif
+ p->magic = VDBE_MAGIC_INIT;
+ p->aborted = 0;
+ return p->rc;
+}
+
+/*
+** Clean up and delete a VDBE after execution. Return an integer which is
+** the result code. Write any error message text into *pzErrMsg.
+*/
+int sqlite3VdbeFinalize(Vdbe *p){
+ int rc = SQLITE_OK;
+ sqlite3 *db = p->db;
+
+ if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){
+ rc = sqlite3VdbeReset(p);
+ }else if( p->magic!=VDBE_MAGIC_INIT ){
+ /* sqlite3Error(p->db, SQLITE_MISUSE, 0); */
+ return SQLITE_MISUSE;
+ }
+ sqlite3VdbeDelete(p);
+ if( rc==SQLITE_SCHEMA ){
+ sqlite3ResetInternalSchema(db, 0);
+ }
+ return rc;
+}
+
+/*
+** Call the destructor for each auxdata entry in pVdbeFunc for which
+** the corresponding bit in mask is clear. Auxdata entries beyond 31
+** are always destroyed. To destroy all auxdata entries, call this
+** routine with mask==0.
+*/
+void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
+ int i;
+ for(i=0; i<pVdbeFunc->nAux; i++){
+ struct AuxData *pAux = &pVdbeFunc->apAux[i];
+ if( (i>31 || !(mask&(1<<i))) && pAux->pAux ){
+ if( pAux->xDelete ){
+ pAux->xDelete(pAux->pAux);
+ }
+ pAux->pAux = 0;
+ }
+ }
+}
+
+/*
+** Delete an entire VDBE.
+*/
+void sqlite3VdbeDelete(Vdbe *p){
+ int i;
+ if( p==0 ) return;
+ Cleanup(p);
+ if( p->pPrev ){
+ p->pPrev->pNext = p->pNext;
+ }else{
+ assert( p->db->pVdbe==p );
+ p->db->pVdbe = p->pNext;
+ }
+ if( p->pNext ){
+ p->pNext->pPrev = p->pPrev;
+ }
+ if( p->aOp ){
+ for(i=0; i<p->nOp; i++){
+ Op *pOp = &p->aOp[i];
+ if( pOp->p3type==P3_DYNAMIC || pOp->p3type==P3_KEYINFO ){
+ sqliteFree(pOp->p3);
+ }
+ if( pOp->p3type==P3_VDBEFUNC ){
+ VdbeFunc *pVdbeFunc = (VdbeFunc *)pOp->p3;
+ sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
+ sqliteFree(pVdbeFunc);
+ }
+ }
+ sqliteFree(p->aOp);
+ }
+ releaseMemArray(p->aVar, p->nVar);
+ sqliteFree(p->aLabel);
+ sqliteFree(p->aStack);
+ releaseMemArray(p->aColName, p->nResColumn*2);
+ sqliteFree(p->aColName);
+ p->magic = VDBE_MAGIC_DEAD;
+ sqliteFree(p);
+}
+
+/*
+** If a MoveTo operation is pending on the given cursor, then do that
+** MoveTo now. Return an error code. If no MoveTo is pending, this
+** routine does nothing and returns SQLITE_OK.
+*/
+int sqlite3VdbeCursorMoveto(Cursor *p){
+ if( p->deferredMoveto ){
+ int res;
+ extern int sqlite3_search_count;
+ assert( p->intKey );
+ if( p->intKey ){
+ sqlite3BtreeMoveto(p->pCursor, 0, p->movetoTarget, &res);
+ }else{
+ sqlite3BtreeMoveto(p->pCursor,(char*)&p->movetoTarget,sizeof(i64),&res);
+ }
+ *p->pIncrKey = 0;
+ p->lastRecno = keyToInt(p->movetoTarget);
+ p->recnoIsValid = res==0;
+ if( res<0 ){
+ sqlite3BtreeNext(p->pCursor, &res);
+ }
+ sqlite3_search_count++;
+ p->deferredMoveto = 0;
+ p->cacheValid = 0;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** The following functions:
+**
+** sqlite3VdbeSerialType()
+** sqlite3VdbeSerialTypeLen()
+** sqlite3VdbeSerialRead()
+** sqlite3VdbeSerialLen()
+** sqlite3VdbeSerialWrite()
+**
+** encapsulate the code that serializes values for storage in SQLite
+** data and index records. Each serialized value consists of a
+** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned
+** integer, stored as a varint.
+**
+** In an SQLite index record, the serial type is stored directly before
+** the blob of data that it corresponds to. In a table record, all serial
+** types are stored at the start of the record, and the blobs of data at
+** the end. Hence these functions allow the caller to handle the
+** serial-type and data blob seperately.
+**
+** The following table describes the various storage classes for data:
+**
+** serial type bytes of data type
+** -------------- --------------- ---------------
+** 0 0 NULL
+** 1 1 signed integer
+** 2 2 signed integer
+** 3 3 signed integer
+** 4 4 signed integer
+** 5 6 signed integer
+** 6 8 signed integer
+** 7 8 IEEE float
+** 8-11 reserved for expansion
+** N>=12 and even (N-12)/2 BLOB
+** N>=13 and odd (N-13)/2 text
+**
+*/
+
+/*
+** Return the serial-type for the value stored in pMem.
+*/
+u32 sqlite3VdbeSerialType(Mem *pMem){
+ int flags = pMem->flags;
+
+ if( flags&MEM_Null ){
+ return 0;
+ }
+ if( flags&MEM_Int ){
+ /* Figure out whether to use 1, 2, 4 or 8 bytes. */
+ i64 i = pMem->i;
+ if( i>=-127 && i<=127 ) return 1;
+ if( i>=-32767 && i<=32767 ) return 2;
+ if( i>=-8388607 && i<=8388607 ) return 3;
+ if( i>=-2147483647 && i<=2147483647 ) return 4;
+ if( i>=-140737488355328L && i<=140737488355328L ) return 5;
+ return 6;
+ }
+ if( flags&MEM_Real ){
+ return 7;
+ }
+ if( flags&MEM_Str ){
+ int n = pMem->n;
+ assert( n>=0 );
+ return ((n*2) + 13);
+ }
+ if( flags&MEM_Blob ){
+ return (pMem->n*2 + 12);
+ }
+ return 0;
+}
+
+/*
+** Return the length of the data corresponding to the supplied serial-type.
+*/
+int sqlite3VdbeSerialTypeLen(u32 serial_type){
+ if( serial_type>=12 ){
+ return (serial_type-12)/2;
+ }else{
+ static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 };
+ return aSize[serial_type];
+ }
+}
+
+/*
+** Write the serialized data blob for the value stored in pMem into
+** buf. It is assumed that the caller has allocated sufficient space.
+** Return the number of bytes written.
+*/
+int sqlite3VdbeSerialPut(unsigned char *buf, Mem *pMem){
+ u32 serial_type = sqlite3VdbeSerialType(pMem);
+ int len;
+
+ /* NULL */
+ if( serial_type==0 ){
+ return 0;
+ }
+
+ /* Integer and Real */
+ if( serial_type<=7 ){
+ u64 v;
+ int i;
+ if( serial_type==7 ){
+ v = *(u64*)&pMem->r;
+ }else{
+ v = *(u64*)&pMem->i;
+ }
+ len = i = sqlite3VdbeSerialTypeLen(serial_type);
+ while( i-- ){
+ buf[i] = (v&0xFF);
+ v >>= 8;
+ }
+ return len;
+ }
+
+ /* String or blob */
+ assert( serial_type>=12 );
+ len = sqlite3VdbeSerialTypeLen(serial_type);
+ memcpy(buf, pMem->z, len);
+ return len;
+}
+
+/*
+** Deserialize the data blob pointed to by buf as serial type serial_type
+** and store the result in pMem. Return the number of bytes read.
+*/
+int sqlite3VdbeSerialGet(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ u32 serial_type, /* Serial type to deserialize */
+ Mem *pMem /* Memory cell to write value into */
+){
+ int len;
+
+ if( serial_type==0 ){
+ /* NULL */
+ pMem->flags = MEM_Null;
+ return 0;
+ }
+ len = sqlite3VdbeSerialTypeLen(serial_type);
+ if( serial_type<=7 ){
+ /* Integer and Real */
+ if( serial_type<=4 ){
+ /* 32-bit integer type. This is handled by a special case for
+ ** performance reasons. */
+ int v = buf[0];
+ int n;
+ if( v&0x80 ){
+ v |= -256;
+ }
+ for(n=1; n<len; n++){
+ v = (v<<8) | buf[n];
+ }
+ pMem->flags = MEM_Int;
+ pMem->i = v;
+ return n;
+ }else{
+ u64 v = 0;
+ int n;
+
+ if( buf[0]&0x80 ){
+ v = -1;
+ }
+ for(n=0; n<len; n++){
+ v = (v<<8) | buf[n];
+ }
+ if( serial_type==7 ){
+ pMem->flags = MEM_Real;
+ pMem->r = *(double*)&v;
+ }else{
+ pMem->flags = MEM_Int;
+ pMem->i = *(i64*)&v;
+ }
+ }
+ }else{
+ /* String or blob */
+ assert( serial_type>=12 );
+ pMem->z = (char *)buf;
+ pMem->n = len;
+ pMem->xDel = 0;
+ if( serial_type&0x01 ){
+ pMem->flags = MEM_Str | MEM_Ephem;
+ }else{
+ pMem->flags = MEM_Blob | MEM_Ephem;
+ }
+ }
+ return len;
+}
+
+/*
+** This function compares the two table rows or index records specified by
+** {nKey1, pKey1} and {nKey2, pKey2}, returning a negative, zero
+** or positive integer if {nKey1, pKey1} is less than, equal to or
+** greater than {nKey2, pKey2}. Both Key1 and Key2 must be byte strings
+** composed by the OP_MakeRecord opcode of the VDBE.
+*/
+int sqlite3VdbeRecordCompare(
+ void *userData,
+ int nKey1, const void *pKey1,
+ int nKey2, const void *pKey2
+){
+ KeyInfo *pKeyInfo = (KeyInfo*)userData;
+ u32 d1, d2; /* Offset into aKey[] of next data element */
+ u32 idx1, idx2; /* Offset into aKey[] of next header element */
+ u32 szHdr1, szHdr2; /* Number of bytes in header */
+ int i = 0;
+ int nField;
+ int rc = 0;
+ const unsigned char *aKey1 = (const unsigned char *)pKey1;
+ const unsigned char *aKey2 = (const unsigned char *)pKey2;
+
+ Mem mem1;
+ Mem mem2;
+ mem1.enc = pKeyInfo->enc;
+ mem2.enc = pKeyInfo->enc;
+
+ idx1 = sqlite3GetVarint32(pKey1, &szHdr1);
+ d1 = szHdr1;
+ idx2 = sqlite3GetVarint32(pKey2, &szHdr2);
+ d2 = szHdr2;
+ nField = pKeyInfo->nField;
+ while( idx1<szHdr1 && idx2<szHdr2 ){
+ u32 serial_type1;
+ u32 serial_type2;
+
+ /* Read the serial types for the next element in each key. */
+ idx1 += sqlite3GetVarint32(&aKey1[idx1], &serial_type1);
+ if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
+ idx2 += sqlite3GetVarint32(&aKey2[idx2], &serial_type2);
+ if( d2>=nKey2 && sqlite3VdbeSerialTypeLen(serial_type2)>0 ) break;
+
+ /* Assert that there is enough space left in each key for the blob of
+ ** data to go with the serial type just read. This assert may fail if
+ ** the file is corrupted. Then read the value from each key into mem1
+ ** and mem2 respectively.
+ */
+ d1 += sqlite3VdbeSerialGet(&aKey1[d1], serial_type1, &mem1);
+ d2 += sqlite3VdbeSerialGet(&aKey2[d2], serial_type2, &mem2);
+
+ rc = sqlite3MemCompare(&mem1, &mem2, i<nField ? pKeyInfo->aColl[i] : 0);
+ sqlite3VdbeMemRelease(&mem1);
+ sqlite3VdbeMemRelease(&mem2);
+ if( rc!=0 ){
+ break;
+ }
+ i++;
+ }
+
+ /* One of the keys ran out of fields, but all the fields up to that point
+ ** were equal. If the incrKey flag is true, then the second key is
+ ** treated as larger.
+ */
+ if( rc==0 ){
+ if( pKeyInfo->incrKey ){
+ rc = -1;
+ }else if( d1<nKey1 ){
+ rc = 1;
+ }else if( d2<nKey2 ){
+ rc = -1;
+ }
+ }
+
+ if( pKeyInfo->aSortOrder && i<pKeyInfo->nField && pKeyInfo->aSortOrder[i] ){
+ rc = -rc;
+ }
+
+ return rc;
+}
+
+/*
+** The argument is an index entry composed using the OP_MakeRecord opcode.
+** The last entry in this record should be an integer (specifically
+** an integer rowid). This routine returns the number of bytes in
+** that integer.
+*/
+int sqlite3VdbeIdxRowidLen(int nKey, const u8 *aKey){
+ u32 szHdr; /* Size of the header */
+ u32 typeRowid; /* Serial type of the rowid */
+
+ sqlite3GetVarint32(aKey, &szHdr);
+ sqlite3GetVarint32(&aKey[szHdr-1], &typeRowid);
+ return sqlite3VdbeSerialTypeLen(typeRowid);
+}
+
+
+/*
+** pCur points at an index entry created using the OP_MakeRecord opcode.
+** Read the rowid (the last field in the record) and store it in *rowid.
+** Return SQLITE_OK if everything works, or an error code otherwise.
+*/
+int sqlite3VdbeIdxRowid(BtCursor *pCur, i64 *rowid){
+ i64 nCellKey;
+ int rc;
+ u32 szHdr; /* Size of the header */
+ u32 typeRowid; /* Serial type of the rowid */
+ u32 lenRowid; /* Size of the rowid */
+ Mem m, v;
+
+ sqlite3BtreeKeySize(pCur, &nCellKey);
+ if( nCellKey<=0 ){
+ return SQLITE_CORRUPT;
+ }
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, nCellKey, 1, &m);
+ if( rc ){
+ return rc;
+ }
+ sqlite3GetVarint32(m.z, &szHdr);
+ sqlite3GetVarint32(&m.z[szHdr-1], &typeRowid);
+ lenRowid = sqlite3VdbeSerialTypeLen(typeRowid);
+ sqlite3VdbeSerialGet(&m.z[m.n-lenRowid], typeRowid, &v);
+ *rowid = v.i;
+ sqlite3VdbeMemRelease(&m);
+ return SQLITE_OK;
+}
+
+/*
+** Compare the key of the index entry that cursor pC is point to against
+** the key string in pKey (of length nKey). Write into *pRes a number
+** that is negative, zero, or positive if pC is less than, equal to,
+** or greater than pKey. Return SQLITE_OK on success.
+**
+** pKey is either created without a rowid or is truncated so that it
+** omits the rowid at the end. The rowid at the end of the index entry
+** is ignored as well.
+*/
+int sqlite3VdbeIdxKeyCompare(
+ Cursor *pC, /* The cursor to compare against */
+ int nKey, const u8 *pKey, /* The key to compare */
+ int *res /* Write the comparison result here */
+){
+ i64 nCellKey;
+ int rc;
+ BtCursor *pCur = pC->pCursor;
+ int lenRowid;
+ Mem m;
+
+ sqlite3BtreeKeySize(pCur, &nCellKey);
+ if( nCellKey<=0 ){
+ *res = 0;
+ return SQLITE_OK;
+ }
+ rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, nCellKey, 1, &m);
+ if( rc ){
+ return rc;
+ }
+ lenRowid = sqlite3VdbeIdxRowidLen(m.n, m.z);
+ *res = sqlite3VdbeRecordCompare(pC->pKeyInfo, m.n-lenRowid, m.z, nKey, pKey);
+ sqlite3VdbeMemRelease(&m);
+ return SQLITE_OK;
+}
+
+/*
+** This routine sets the value to be returned by subsequent calls to
+** sqlite3_changes() on the database handle 'db'.
+*/
+void sqlite3VdbeSetChanges(sqlite3 *db, int nChange){
+ db->nChange = nChange;
+ db->nTotalChange += nChange;
+}
+
+/*
+** Set a flag in the vdbe to update the change counter when it is finalised
+** or reset.
+*/
+void sqlite3VdbeCountChanges(Vdbe *p){
+ p->changeCntOn = 1;
+}
diff --git a/kopete/plugins/statistics/sqlite/vdbemem.c b/kopete/plugins/statistics/sqlite/vdbemem.c
new file mode 100644
index 00000000..c6cd94e6
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/vdbemem.c
@@ -0,0 +1,724 @@
+/*
+** 2004 May 26
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains code use to manipulate "Mem" structure. A "Mem"
+** stores a single value in the VDBE. Mem is an opaque structure visible
+** only within the VDBE. Interface routines refer to a Mem using the
+** name sqlite_value
+*/
+#include "sqliteInt.h"
+#include "os.h"
+#include <ctype.h>
+#include "vdbeInt.h"
+
+/*
+** If pMem is an object with a valid string representation, this routine
+** ensures the internal encoding for the string representation is
+** 'desiredEnc', one of SQLITE_UTF8, SQLITE_UTF16LE or SQLITE_UTF16BE.
+**
+** If pMem is not a string object, or the encoding of the string
+** representation is already stored using the requested encoding, then this
+** routine is a no-op.
+**
+** SQLITE_OK is returned if the conversion is successful (or not required).
+** SQLITE_NOMEM may be returned if a malloc() fails during conversion
+** between formats.
+*/
+int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
+ if( !(pMem->flags&MEM_Str) || pMem->enc==desiredEnc ){
+ return SQLITE_OK;
+ }
+ return sqlite3VdbeMemTranslate(pMem, desiredEnc);
+}
+
+/*
+** Make the given Mem object MEM_Dyn.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
+*/
+int sqlite3VdbeMemDynamicify(Mem *pMem){
+ int n = pMem->n;
+ u8 *z;
+ if( (pMem->flags & (MEM_Ephem|MEM_Static|MEM_Short))==0 ){
+ return SQLITE_OK;
+ }
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ assert( pMem->flags & (MEM_Str|MEM_Blob) );
+ z = sqliteMallocRaw( n+2 );
+ if( z==0 ){
+ return SQLITE_NOMEM;
+ }
+ pMem->flags |= MEM_Dyn|MEM_Term;
+ pMem->xDel = 0;
+ memcpy(z, pMem->z, n );
+ z[n] = 0;
+ z[n+1] = 0;
+ pMem->z = z;
+ pMem->flags &= ~(MEM_Ephem|MEM_Static|MEM_Short);
+ return SQLITE_OK;
+}
+
+/*
+** Make the given Mem object either MEM_Short or MEM_Dyn so that bytes
+** of the Mem.z[] array can be modified.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
+*/
+int sqlite3VdbeMemMakeWriteable(Mem *pMem){
+ int n;
+ u8 *z;
+ if( (pMem->flags & (MEM_Ephem|MEM_Static))==0 ){
+ return SQLITE_OK;
+ }
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ assert( pMem->flags & (MEM_Str|MEM_Blob) );
+ if( (n = pMem->n)+2<sizeof(pMem->zShort) ){
+ z = pMem->zShort;
+ pMem->flags |= MEM_Short|MEM_Term;
+ }else{
+ z = sqliteMallocRaw( n+2 );
+ if( z==0 ){
+ return SQLITE_NOMEM;
+ }
+ pMem->flags |= MEM_Dyn|MEM_Term;
+ pMem->xDel = 0;
+ }
+ memcpy(z, pMem->z, n );
+ z[n] = 0;
+ z[n+1] = 0;
+ pMem->z = z;
+ pMem->flags &= ~(MEM_Ephem|MEM_Static);
+ return SQLITE_OK;
+}
+
+/*
+** Make sure the given Mem is \u0000 terminated.
+*/
+int sqlite3VdbeMemNulTerminate(Mem *pMem){
+ /* In SQLite, a string without a nul terminator occurs when a string
+ ** is loaded from disk (in this case the memory management is ephemeral),
+ ** or when it is supplied by the user as a bound variable or function
+ ** return value. Therefore, the memory management of the string must be
+ ** either ephemeral, static or controlled by a user-supplied destructor.
+ */
+ assert(
+ !(pMem->flags&MEM_Str) || /* it's not a string, or */
+ (pMem->flags&MEM_Term) || /* it's nul term. already, or */
+ (pMem->flags&(MEM_Ephem|MEM_Static)) || /* it's static or ephem, or */
+ (pMem->flags&MEM_Dyn && pMem->xDel) /* external management */
+ );
+ if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){
+ return SQLITE_OK; /* Nothing to do */
+ }
+
+ if( pMem->flags & (MEM_Static|MEM_Ephem) ){
+ return sqlite3VdbeMemMakeWriteable(pMem);
+ }else{
+ char *z = sqliteMalloc(pMem->n+2);
+ if( !z ) return SQLITE_NOMEM;
+ memcpy(z, pMem->z, pMem->n);
+ z[pMem->n] = 0;
+ z[pMem->n+1] = 0;
+ pMem->xDel(pMem->z);
+ pMem->xDel = 0;
+ pMem->z = z;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Add MEM_Str to the set of representations for the given Mem. Numbers
+** are converted using sqlite3_snprintf(). Converting a BLOB to a string
+** is a no-op.
+**
+** Existing representations MEM_Int and MEM_Real are *not* invalidated.
+**
+** A MEM_Null value will never be passed to this function. This function is
+** used for converting values to text for returning to the user (i.e. via
+** sqlite3_value_text()), or for ensuring that values to be used as btree
+** keys are strings. In the former case a NULL pointer is returned the
+** user and the later is an internal programming error.
+*/
+int sqlite3VdbeMemStringify(Mem *pMem, int enc){
+ int rc = SQLITE_OK;
+ int fg = pMem->flags;
+ u8 *z = pMem->zShort;
+
+ assert( !(fg&(MEM_Str|MEM_Blob)) );
+ assert( fg&(MEM_Int|MEM_Real) );
+
+ /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
+ ** string representation of the value. Then, if the required encoding
+ ** is UTF-16le or UTF-16be do a translation.
+ **
+ ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
+ */
+ if( fg & MEM_Real ){
+ sqlite3_snprintf(NBFS, z, "%.15g", pMem->r);
+ }else{
+ assert( fg & MEM_Int );
+ sqlite3_snprintf(NBFS, z, "%lld", pMem->i);
+ }
+ pMem->n = strlen(z);
+ pMem->z = z;
+ pMem->enc = SQLITE_UTF8;
+ pMem->flags |= MEM_Str | MEM_Short | MEM_Term;
+ sqlite3VdbeChangeEncoding(pMem, enc);
+ return rc;
+}
+
+/*
+** Release any memory held by the Mem. This may leave the Mem in an
+** inconsistent state, for example with (Mem.z==0) and
+** (Mem.type==SQLITE_TEXT).
+*/
+void sqlite3VdbeMemRelease(Mem *p){
+ if( p->flags & MEM_Dyn ){
+ if( p->xDel ){
+ p->xDel((void *)p->z);
+ }else{
+ sqliteFree(p->z);
+ }
+ p->z = 0;
+ p->xDel = 0;
+ }
+}
+
+/*
+** Return some kind of integer value which is the best we can do
+** at representing the value that *pMem describes as an integer.
+** If pMem is an integer, then the value is exact. If pMem is
+** a floating-point then the value returned is the integer part.
+** If pMem is a string or blob, then we make an attempt to convert
+** it into a integer and return that. If pMem is NULL, return 0.
+**
+** If pMem is a string, its encoding might be changed.
+*/
+i64 sqlite3VdbeIntValue(Mem *pMem){
+ int flags = pMem->flags;
+ if( flags & MEM_Int ){
+ return pMem->i;
+ }else if( flags & MEM_Real ){
+ return (i64)pMem->r;
+ }else if( flags & (MEM_Str|MEM_Blob) ){
+ i64 value;
+ if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
+ || sqlite3VdbeMemNulTerminate(pMem) ){
+ return SQLITE_NOMEM;
+ }
+ assert( pMem->z );
+ sqlite3atoi64(pMem->z, &value);
+ return value;
+ }else{
+ return 0;
+ }
+}
+
+/*
+** Convert pMem to type integer. Invalidate any prior representations.
+*/
+int sqlite3VdbeMemIntegerify(Mem *pMem){
+ pMem->i = sqlite3VdbeIntValue(pMem);
+ sqlite3VdbeMemRelease(pMem);
+ pMem->flags = MEM_Int;
+ return SQLITE_OK;
+}
+
+/*
+** Return the best representation of pMem that we can get into a
+** double. If pMem is already a double or an integer, return its
+** value. If it is a string or blob, try to convert it to a double.
+** If it is a NULL, return 0.0.
+*/
+double sqlite3VdbeRealValue(Mem *pMem){
+ if( pMem->flags & MEM_Real ){
+ return pMem->r;
+ }else if( pMem->flags & MEM_Int ){
+ return (double)pMem->i;
+ }else if( pMem->flags & (MEM_Str|MEM_Blob) ){
+ if( sqlite3VdbeChangeEncoding(pMem, SQLITE_UTF8)
+ || sqlite3VdbeMemNulTerminate(pMem) ){
+ return SQLITE_NOMEM;
+ }
+ assert( pMem->z );
+ return sqlite3AtoF(pMem->z, 0);
+ }else{
+ return 0.0;
+ }
+}
+
+/*
+** Convert pMem so that it is of type MEM_Real. Invalidate any
+** prior representations.
+*/
+int sqlite3VdbeMemRealify(Mem *pMem){
+ pMem->r = sqlite3VdbeRealValue(pMem);
+ sqlite3VdbeMemRelease(pMem);
+ pMem->flags = MEM_Real;
+ return SQLITE_OK;
+}
+
+/*
+** Delete any previous value and set the value stored in *pMem to NULL.
+*/
+void sqlite3VdbeMemSetNull(Mem *pMem){
+ sqlite3VdbeMemRelease(pMem);
+ pMem->flags = MEM_Null;
+ pMem->type = SQLITE_NULL;
+}
+
+/*
+** Delete any previous value and set the value stored in *pMem to val,
+** manifest type INTEGER.
+*/
+void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
+ sqlite3VdbeMemRelease(pMem);
+ pMem->i = val;
+ pMem->flags = MEM_Int;
+ pMem->type = SQLITE_INTEGER;
+}
+
+/*
+** Delete any previous value and set the value stored in *pMem to val,
+** manifest type REAL.
+*/
+void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
+ sqlite3VdbeMemRelease(pMem);
+ pMem->r = val;
+ pMem->flags = MEM_Real;
+ pMem->type = SQLITE_FLOAT;
+}
+
+/*
+** Make an shallow copy of pFrom into pTo. Prior contents of
+** pTo are overwritten. The pFrom->z field is not duplicated. If
+** pFrom->z is used, then pTo->z points to the same thing as pFrom->z
+** and flags gets srcType (either MEM_Ephem or MEM_Static).
+*/
+void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
+ memcpy(pTo, pFrom, sizeof(*pFrom)-sizeof(pFrom->zShort));
+ pTo->xDel = 0;
+ if( pTo->flags & (MEM_Str|MEM_Blob) ){
+ pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Short|MEM_Ephem);
+ assert( srcType==MEM_Ephem || srcType==MEM_Static );
+ pTo->flags |= srcType;
+ }
+}
+
+/*
+** Make a full copy of pFrom into pTo. Prior contents of pTo are
+** freed before the copy is made.
+*/
+int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
+ int rc;
+ if( pTo->flags & MEM_Dyn ){
+ sqlite3VdbeMemRelease(pTo);
+ }
+ sqlite3VdbeMemShallowCopy(pTo, pFrom, MEM_Ephem);
+ if( pTo->flags & MEM_Ephem ){
+ rc = sqlite3VdbeMemMakeWriteable(pTo);
+ }else{
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Transfer the contents of pFrom to pTo. Any existing value in pTo is
+** freed. If pFrom contains ephemeral data, a copy is made.
+**
+** pFrom contains an SQL NULL when this routine returns. SQLITE_NOMEM
+** might be returned if pFrom held ephemeral data and we were unable
+** to allocate enough space to make a copy.
+*/
+int sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
+ int rc;
+ if( pTo->flags & MEM_Dyn ){
+ sqlite3VdbeMemRelease(pTo);
+ }
+ memcpy(pTo, pFrom, sizeof(Mem));
+ if( pFrom->flags & MEM_Short ){
+ pTo->z = pTo->zShort;
+ }
+ pFrom->flags = MEM_Null;
+ pFrom->xDel = 0;
+ if( pTo->flags & MEM_Ephem ){
+ rc = sqlite3VdbeMemMakeWriteable(pTo);
+ }else{
+ rc = SQLITE_OK;
+ }
+ return rc;
+}
+
+/*
+** Change the value of a Mem to be a string or a BLOB.
+*/
+int sqlite3VdbeMemSetStr(
+ Mem *pMem, /* Memory cell to set to string value */
+ const char *z, /* String pointer */
+ int n, /* Bytes in string, or negative */
+ u8 enc, /* Encoding of z. 0 for BLOBs */
+ void (*xDel)(void*) /* Destructor function */
+){
+ sqlite3VdbeMemRelease(pMem);
+ if( !z ){
+ pMem->flags = MEM_Null;
+ pMem->type = SQLITE_NULL;
+ return SQLITE_OK;
+ }
+
+ pMem->z = (char *)z;
+ if( xDel==SQLITE_STATIC ){
+ pMem->flags = MEM_Static;
+ }else if( xDel==SQLITE_TRANSIENT ){
+ pMem->flags = MEM_Ephem;
+ }else{
+ pMem->flags = MEM_Dyn;
+ pMem->xDel = xDel;
+ }
+
+ pMem->enc = enc;
+ pMem->type = enc==0 ? SQLITE_BLOB : SQLITE_TEXT;
+ pMem->n = n;
+
+ switch( enc ){
+ case 0:
+ pMem->flags |= MEM_Blob;
+ break;
+
+ case SQLITE_UTF8:
+ pMem->flags |= MEM_Str;
+ if( n<0 ){
+ pMem->n = strlen(z);
+ pMem->flags |= MEM_Term;
+ }
+ break;
+
+ case SQLITE_UTF16LE:
+ case SQLITE_UTF16BE:
+ pMem->flags |= MEM_Str;
+ if( pMem->n<0 ){
+ pMem->n = sqlite3utf16ByteLen(pMem->z,-1);
+ pMem->flags |= MEM_Term;
+ }
+ if( sqlite3VdbeMemHandleBom(pMem) ){
+ return SQLITE_NOMEM;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ if( pMem->flags&MEM_Ephem ){
+ return sqlite3VdbeMemMakeWriteable(pMem);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Compare the values contained by the two memory cells, returning
+** negative, zero or positive if pMem1 is less than, equal to, or greater
+** than pMem2. Sorting order is NULL's first, followed by numbers (integers
+** and reals) sorted numerically, followed by text ordered by the collating
+** sequence pColl and finally blob's ordered by memcmp().
+**
+** Two NULL values are considered equal by this function.
+*/
+int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
+ int rc;
+ int f1, f2;
+ int combined_flags;
+
+ /* Interchange pMem1 and pMem2 if the collating sequence specifies
+ ** DESC order.
+ */
+ f1 = pMem1->flags;
+ f2 = pMem2->flags;
+ combined_flags = f1|f2;
+
+ /* If one value is NULL, it is less than the other. If both values
+ ** are NULL, return 0.
+ */
+ if( combined_flags&MEM_Null ){
+ return (f2&MEM_Null) - (f1&MEM_Null);
+ }
+
+ /* If one value is a number and the other is not, the number is less.
+ ** If both are numbers, compare as reals if one is a real, or as integers
+ ** if both values are integers.
+ */
+ if( combined_flags&(MEM_Int|MEM_Real) ){
+ if( !(f1&(MEM_Int|MEM_Real)) ){
+ return 1;
+ }
+ if( !(f2&(MEM_Int|MEM_Real)) ){
+ return -1;
+ }
+ if( (f1 & f2 & MEM_Int)==0 ){
+ double r1, r2;
+ if( (f1&MEM_Real)==0 ){
+ r1 = pMem1->i;
+ }else{
+ r1 = pMem1->r;
+ }
+ if( (f2&MEM_Real)==0 ){
+ r2 = pMem2->i;
+ }else{
+ r2 = pMem2->r;
+ }
+ if( r1<r2 ) return -1;
+ if( r1>r2 ) return 1;
+ return 0;
+ }else{
+ assert( f1&MEM_Int );
+ assert( f2&MEM_Int );
+ if( pMem1->i < pMem2->i ) return -1;
+ if( pMem1->i > pMem2->i ) return 1;
+ return 0;
+ }
+ }
+
+ /* If one value is a string and the other is a blob, the string is less.
+ ** If both are strings, compare using the collating functions.
+ */
+ if( combined_flags&MEM_Str ){
+ if( (f1 & MEM_Str)==0 ){
+ return 1;
+ }
+ if( (f2 & MEM_Str)==0 ){
+ return -1;
+ }
+
+ assert( pMem1->enc==pMem2->enc );
+ assert( pMem1->enc==SQLITE_UTF8 ||
+ pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
+
+ /* This assert may fail if the collation sequence is deleted after this
+ ** vdbe program is compiled. The documentation defines this as an
+ ** undefined condition. A crash is usual result.
+ */
+ assert( !pColl || pColl->xCmp );
+
+ if( pColl ){
+ if( pMem1->enc==pColl->enc ){
+ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
+ }else{
+ u8 origEnc = pMem1->enc;
+ rc = pColl->xCmp(
+ pColl->pUser,
+ sqlite3ValueBytes((sqlite3_value*)pMem1, pColl->enc),
+ sqlite3ValueText((sqlite3_value*)pMem1, pColl->enc),
+ sqlite3ValueBytes((sqlite3_value*)pMem2, pColl->enc),
+ sqlite3ValueText((sqlite3_value*)pMem2, pColl->enc)
+ );
+ sqlite3ValueBytes((sqlite3_value*)pMem1, origEnc);
+ sqlite3ValueText((sqlite3_value*)pMem1, origEnc);
+ sqlite3ValueBytes((sqlite3_value*)pMem2, origEnc);
+ sqlite3ValueText((sqlite3_value*)pMem2, origEnc);
+ return rc;
+ }
+ }
+ /* If a NULL pointer was passed as the collate function, fall through
+ ** to the blob case and use memcmp(). */
+ }
+
+ /* Both values must be blobs. Compare using memcmp(). */
+ rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
+ if( rc==0 ){
+ rc = pMem1->n - pMem2->n;
+ }
+ return rc;
+}
+
+/*
+** Move data out of a btree key or data field and into a Mem structure.
+** The data or key is taken from the entry that pCur is currently pointing
+** to. offset and amt determine what portion of the data or key to retrieve.
+** key is true to get the key or false to get data. The result is written
+** into the pMem element.
+**
+** The pMem structure is assumed to be uninitialized. Any prior content
+** is overwritten without being freed.
+**
+** If this routine fails for any reason (malloc returns NULL or unable
+** to read from the disk) then the pMem is left in an inconsistent state.
+*/
+int sqlite3VdbeMemFromBtree(
+ BtCursor *pCur, /* Cursor pointing at record to retrieve. */
+ int offset, /* Offset from the start of data to return bytes from. */
+ int amt, /* Number of bytes to return. */
+ int key, /* If true, retrieve from the btree key, not data. */
+ Mem *pMem /* OUT: Return data in this Mem structure. */
+){
+ char *zData; /* Data from the btree layer */
+ int available; /* Number of bytes available on the local btree page */
+
+ if( key ){
+ zData = (char *)sqlite3BtreeKeyFetch(pCur, &available);
+ }else{
+ zData = (char *)sqlite3BtreeDataFetch(pCur, &available);
+ }
+
+ pMem->n = amt;
+ if( offset+amt<=available ){
+ pMem->z = &zData[offset];
+ pMem->flags = MEM_Blob|MEM_Ephem;
+ }else{
+ int rc;
+ if( amt>NBFS-2 ){
+ zData = (char *)sqliteMallocRaw(amt+2);
+ if( !zData ){
+ return SQLITE_NOMEM;
+ }
+ pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term;
+ pMem->xDel = 0;
+ }else{
+ zData = &(pMem->zShort[0]);
+ pMem->flags = MEM_Blob|MEM_Short|MEM_Term;
+ }
+ pMem->z = zData;
+ pMem->enc = 0;
+ pMem->type = SQLITE_BLOB;
+
+ if( key ){
+ rc = sqlite3BtreeKey(pCur, offset, amt, zData);
+ }else{
+ rc = sqlite3BtreeData(pCur, offset, amt, zData);
+ }
+ zData[amt] = 0;
+ zData[amt+1] = 0;
+ if( rc!=SQLITE_OK ){
+ if( amt>NBFS ){
+ sqliteFree(zData);
+ }
+ return rc;
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+#ifndef NDEBUG
+/*
+** Perform various checks on the memory cell pMem. An assert() will
+** fail if pMem is internally inconsistent.
+*/
+void sqlite3VdbeMemSanity(Mem *pMem, u8 db_enc){
+ int flags = pMem->flags;
+ assert( flags!=0 ); /* Must define some type */
+ if( pMem->flags & (MEM_Str|MEM_Blob) ){
+ int x = pMem->flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short);
+ assert( x!=0 ); /* Strings must define a string subtype */
+ assert( (x & (x-1))==0 ); /* Only one string subtype can be defined */
+ assert( pMem->z!=0 ); /* Strings must have a value */
+ /* Mem.z points to Mem.zShort iff the subtype is MEM_Short */
+ assert( (pMem->flags & MEM_Short)==0 || pMem->z==pMem->zShort );
+ assert( (pMem->flags & MEM_Short)!=0 || pMem->z!=pMem->zShort );
+ /* No destructor unless there is MEM_Dyn */
+ assert( pMem->xDel==0 || (pMem->flags & MEM_Dyn)!=0 );
+
+ if( (flags & MEM_Str) ){
+ assert( pMem->enc==SQLITE_UTF8 ||
+ pMem->enc==SQLITE_UTF16BE ||
+ pMem->enc==SQLITE_UTF16LE
+ );
+ /* If the string is UTF-8 encoded and nul terminated, then pMem->n
+ ** must be the length of the string. (Later:) If the database file
+ ** has been corrupted, '\000' characters might have been inserted
+ ** into the middle of the string. In that case, the strlen() might
+ ** be less.
+ */
+ if( pMem->enc==SQLITE_UTF8 && (flags & MEM_Term) ){
+ assert( strlen(pMem->z)<=pMem->n );
+ assert( pMem->z[pMem->n]==0 );
+ }
+ }
+ }else{
+ /* Cannot define a string subtype for non-string objects */
+ assert( (pMem->flags & (MEM_Static|MEM_Dyn|MEM_Ephem|MEM_Short))==0 );
+ assert( pMem->xDel==0 );
+ }
+ /* MEM_Null excludes all other types */
+ assert( (pMem->flags&(MEM_Str|MEM_Int|MEM_Real|MEM_Blob))==0
+ || (pMem->flags&MEM_Null)==0 );
+ if( (pMem->flags & (MEM_Int|MEM_Real))==(MEM_Int|MEM_Real) ){
+ assert( pMem->r==pMem->i );
+ }
+}
+#endif
+
+/* This function is only available internally, it is not part of the
+** external API. It works in a similar way to sqlite3_value_text(),
+** except the data returned is in the encoding specified by the second
+** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or
+** SQLITE_UTF8.
+*/
+const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
+ if( !pVal ) return 0;
+ assert( enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE || enc==SQLITE_UTF8);
+
+ if( pVal->flags&MEM_Null ){
+ return 0;
+ }
+ if( pVal->flags&MEM_Str ){
+ sqlite3VdbeChangeEncoding(pVal, enc);
+ }else if( !(pVal->flags&MEM_Blob) ){
+ sqlite3VdbeMemStringify(pVal, enc);
+ }
+ return (const void *)(pVal->z);
+}
+
+/*
+** Create a new sqlite3_value object.
+*/
+sqlite3_value* sqlite3ValueNew(){
+ Mem *p = sqliteMalloc(sizeof(*p));
+ if( p ){
+ p->flags = MEM_Null;
+ p->type = SQLITE_NULL;
+ }
+ return p;
+}
+
+/*
+** Change the string value of an sqlite3_value object
+*/
+void sqlite3ValueSetStr(
+ sqlite3_value *v,
+ int n,
+ const void *z,
+ u8 enc,
+ void (*xDel)(void*)
+){
+ if( v ) sqlite3VdbeMemSetStr((Mem *)v, z, n, enc, xDel);
+}
+
+/*
+** Free an sqlite3_value object
+*/
+void sqlite3ValueFree(sqlite3_value *v){
+ if( !v ) return;
+ sqlite3ValueSetStr(v, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
+ sqliteFree(v);
+}
+
+/*
+** Return the number of bytes in the sqlite3_value object assuming
+** that it uses the encoding "enc"
+*/
+int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
+ Mem *p = (Mem*)pVal;
+ if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){
+ return p->n;
+ }
+ return 0;
+}
diff --git a/kopete/plugins/statistics/sqlite/where.c b/kopete/plugins/statistics/sqlite/where.c
new file mode 100644
index 00000000..08c174e9
--- /dev/null
+++ b/kopete/plugins/statistics/sqlite/where.c
@@ -0,0 +1,1210 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements.
+**
+** $Id$
+*/
+#include "sqliteInt.h"
+
+/*
+** The query generator uses an array of instances of this structure to
+** help it analyze the subexpressions of the WHERE clause. Each WHERE
+** clause subexpression is separated from the others by an AND operator.
+*/
+typedef struct ExprInfo ExprInfo;
+struct ExprInfo {
+ Expr *p; /* Pointer to the subexpression */
+ u8 indexable; /* True if this subexprssion is usable by an index */
+ short int idxLeft; /* p->pLeft is a column in this table number. -1 if
+ ** p->pLeft is not the column of any table */
+ short int idxRight; /* p->pRight is a column in this table number. -1 if
+ ** p->pRight is not the column of any table */
+ unsigned prereqLeft; /* Bitmask of tables referenced by p->pLeft */
+ unsigned prereqRight; /* Bitmask of tables referenced by p->pRight */
+ unsigned prereqAll; /* Bitmask of tables referenced by p */
+};
+
+/*
+** An instance of the following structure keeps track of a mapping
+** between VDBE cursor numbers and bitmasks. The VDBE cursor numbers
+** are small integers contained in SrcList_item.iCursor and Expr.iTable
+** fields. For any given WHERE clause, we want to track which cursors
+** are being used, so we assign a single bit in a 32-bit word to track
+** that cursor. Then a 32-bit integer is able to show the set of all
+** cursors being used.
+*/
+typedef struct ExprMaskSet ExprMaskSet;
+struct ExprMaskSet {
+ int n; /* Number of assigned cursor values */
+ int ix[31]; /* Cursor assigned to each bit */
+};
+
+/*
+** Determine the number of elements in an array.
+*/
+#define ARRAYSIZE(X) (sizeof(X)/sizeof(X[0]))
+
+/*
+** This routine is used to divide the WHERE expression into subexpressions
+** separated by the AND operator.
+**
+** aSlot[] is an array of subexpressions structures.
+** There are nSlot spaces left in this array. This routine attempts to
+** split pExpr into subexpressions and fills aSlot[] with those subexpressions.
+** The return value is the number of slots filled.
+*/
+static int exprSplit(int nSlot, ExprInfo *aSlot, Expr *pExpr){
+ int cnt = 0;
+ if( pExpr==0 || nSlot<1 ) return 0;
+ if( nSlot==1 || pExpr->op!=TK_AND ){
+ aSlot[0].p = pExpr;
+ return 1;
+ }
+ if( pExpr->pLeft->op!=TK_AND ){
+ aSlot[0].p = pExpr->pLeft;
+ cnt = 1 + exprSplit(nSlot-1, &aSlot[1], pExpr->pRight);
+ }else{
+ cnt = exprSplit(nSlot, aSlot, pExpr->pLeft);
+ cnt += exprSplit(nSlot-cnt, &aSlot[cnt], pExpr->pRight);
+ }
+ return cnt;
+}
+
+/*
+** Initialize an expression mask set
+*/
+#define initMaskSet(P) memset(P, 0, sizeof(*P))
+
+/*
+** Return the bitmask for the given cursor. Assign a new bitmask
+** if this is the first time the cursor has been seen.
+*/
+static int getMask(ExprMaskSet *pMaskSet, int iCursor){
+ int i;
+ for(i=0; i<pMaskSet->n; i++){
+ if( pMaskSet->ix[i]==iCursor ) return 1<<i;
+ }
+ if( i==pMaskSet->n && i<ARRAYSIZE(pMaskSet->ix) ){
+ pMaskSet->n++;
+ pMaskSet->ix[i] = iCursor;
+ return 1<<i;
+ }
+ return 0;
+}
+
+/*
+** Destroy an expression mask set
+*/
+#define freeMaskSet(P) /* NO-OP */
+
+/*
+** This routine walks (recursively) an expression tree and generates
+** a bitmask indicating which tables are used in that expression
+** tree.
+**
+** In order for this routine to work, the calling function must have
+** previously invoked sqlite3ExprResolveIds() on the expression. See
+** the header comment on that routine for additional information.
+** The sqlite3ExprResolveIds() routines looks for column names and
+** sets their opcodes to TK_COLUMN and their Expr.iTable fields to
+** the VDBE cursor number of the table.
+*/
+static int exprTableUsage(ExprMaskSet *pMaskSet, Expr *p){
+ unsigned int mask = 0;
+ if( p==0 ) return 0;
+ if( p->op==TK_COLUMN ){
+ mask = getMask(pMaskSet, p->iTable);
+ if( mask==0 ) mask = -1;
+ return mask;
+ }
+ if( p->pRight ){
+ mask = exprTableUsage(pMaskSet, p->pRight);
+ }
+ if( p->pLeft ){
+ mask |= exprTableUsage(pMaskSet, p->pLeft);
+ }
+ if( p->pList ){
+ int i;
+ for(i=0; i<p->pList->nExpr; i++){
+ mask |= exprTableUsage(pMaskSet, p->pList->a[i].pExpr);
+ }
+ }
+ return mask;
+}
+
+/*
+** Return TRUE if the given operator is one of the operators that is
+** allowed for an indexable WHERE clause. The allowed operators are
+** "=", "<", ">", "<=", ">=", and "IN".
+*/
+static int allowedOp(int op){
+ assert( TK_GT==TK_LE-1 && TK_LE==TK_LT-1 && TK_LT==TK_GE-1 && TK_EQ==TK_GT-1);
+ return op==TK_IN || (op>=TK_EQ && op<=TK_GE);
+}
+
+/*
+** Swap two integers.
+*/
+#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
+
+/*
+** Return the index in the SrcList that uses cursor iCur. If iCur is
+** used by the first entry in SrcList return 0. If iCur is used by
+** the second entry return 1. And so forth.
+**
+** SrcList is the set of tables in the FROM clause in the order that
+** they will be processed. The value returned here gives us an index
+** of which tables will be processed first.
+*/
+static int tableOrder(SrcList *pList, int iCur){
+ int i;
+ for(i=0; i<pList->nSrc; i++){
+ if( pList->a[i].iCursor==iCur ) return i;
+ }
+ return -1;
+}
+
+/*
+** The input to this routine is an ExprInfo structure with only the
+** "p" field filled in. The job of this routine is to analyze the
+** subexpression and populate all the other fields of the ExprInfo
+** structure.
+*/
+static void exprAnalyze(SrcList *pSrc, ExprMaskSet *pMaskSet, ExprInfo *pInfo){
+ Expr *pExpr = pInfo->p;
+ pInfo->prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
+ pInfo->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
+ pInfo->prereqAll = exprTableUsage(pMaskSet, pExpr);
+ pInfo->indexable = 0;
+ pInfo->idxLeft = -1;
+ pInfo->idxRight = -1;
+ if( allowedOp(pExpr->op) && (pInfo->prereqRight & pInfo->prereqLeft)==0 ){
+ if( pExpr->pRight && pExpr->pRight->op==TK_COLUMN ){
+ pInfo->idxRight = pExpr->pRight->iTable;
+ pInfo->indexable = 1;
+ }
+ if( pExpr->pLeft->op==TK_COLUMN ){
+ pInfo->idxLeft = pExpr->pLeft->iTable;
+ pInfo->indexable = 1;
+ }
+ }
+ if( pInfo->indexable ){
+ assert( pInfo->idxLeft!=pInfo->idxRight );
+
+ /* We want the expression to be of the form "X = expr", not "expr = X".
+ ** So flip it over if necessary. If the expression is "X = Y", then
+ ** we want Y to come from an earlier table than X.
+ **
+ ** The collating sequence rule is to always choose the left expression.
+ ** So if we do a flip, we also have to move the collating sequence.
+ */
+ if( tableOrder(pSrc,pInfo->idxLeft)<tableOrder(pSrc,pInfo->idxRight) ){
+ assert( pExpr->op!=TK_IN );
+ SWAP(CollSeq*,pExpr->pRight->pColl,pExpr->pLeft->pColl);
+ SWAP(Expr*,pExpr->pRight,pExpr->pLeft);
+ if( pExpr->op>=TK_GT ){
+ assert( TK_LT==TK_GT+2 );
+ assert( TK_GE==TK_LE+2 );
+ assert( TK_GT>TK_EQ );
+ assert( TK_GT<TK_LE );
+ assert( pExpr->op>=TK_GT && pExpr->op<=TK_GE );
+ pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT;
+ }
+ SWAP(unsigned, pInfo->prereqLeft, pInfo->prereqRight);
+ SWAP(short int, pInfo->idxLeft, pInfo->idxRight);
+ }
+ }
+
+}
+
+/*
+** pOrderBy is an ORDER BY clause from a SELECT statement. pTab is the
+** left-most table in the FROM clause of that same SELECT statement and
+** the table has a cursor number of "base".
+**
+** This routine attempts to find an index for pTab that generates the
+** correct record sequence for the given ORDER BY clause. The return value
+** is a pointer to an index that does the job. NULL is returned if the
+** table has no index that will generate the correct sort order.
+**
+** If there are two or more indices that generate the correct sort order
+** and pPreferredIdx is one of those indices, then return pPreferredIdx.
+**
+** nEqCol is the number of columns of pPreferredIdx that are used as
+** equality constraints. Any index returned must have exactly this same
+** set of columns. The ORDER BY clause only matches index columns beyond the
+** the first nEqCol columns.
+**
+** All terms of the ORDER BY clause must be either ASC or DESC. The
+** *pbRev value is set to 1 if the ORDER BY clause is all DESC and it is
+** set to 0 if the ORDER BY clause is all ASC.
+*/
+static Index *findSortingIndex(
+ Parse *pParse,
+ Table *pTab, /* The table to be sorted */
+ int base, /* Cursor number for pTab */
+ ExprList *pOrderBy, /* The ORDER BY clause */
+ Index *pPreferredIdx, /* Use this index, if possible and not NULL */
+ int nEqCol, /* Number of index columns used with == constraints */
+ int *pbRev /* Set to 1 if ORDER BY is DESC */
+){
+ int i, j;
+ Index *pMatch;
+ Index *pIdx;
+ int sortOrder;
+ sqlite3 *db = pParse->db;
+
+ assert( pOrderBy!=0 );
+ assert( pOrderBy->nExpr>0 );
+ sortOrder = pOrderBy->a[0].sortOrder;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ Expr *p;
+ if( pOrderBy->a[i].sortOrder!=sortOrder ){
+ /* Indices can only be used if all ORDER BY terms are either
+ ** DESC or ASC. Indices cannot be used on a mixture. */
+ return 0;
+ }
+ p = pOrderBy->a[i].pExpr;
+ if( p->op!=TK_COLUMN || p->iTable!=base ){
+ /* Can not use an index sort on anything that is not a column in the
+ ** left-most table of the FROM clause */
+ return 0;
+ }
+ }
+
+ /* If we get this far, it means the ORDER BY clause consists only of
+ ** ascending columns in the left-most table of the FROM clause. Now
+ ** check for a matching index.
+ */
+ pMatch = 0;
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int nExpr = pOrderBy->nExpr;
+ if( pIdx->nColumn < nEqCol || pIdx->nColumn < nExpr ) continue;
+ for(i=j=0; i<nEqCol; i++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[j].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ if( pPreferredIdx->aiColumn[i]!=pIdx->aiColumn[i] ) break;
+ if( pPreferredIdx->keyInfo.aColl[i]!=pIdx->keyInfo.aColl[i] ) break;
+ if( j<nExpr &&
+ pOrderBy->a[j].pExpr->iColumn==pIdx->aiColumn[i] &&
+ pColl==pIdx->keyInfo.aColl[i]
+ ){
+ j++;
+ }
+ }
+ if( i<nEqCol ) continue;
+ for(i=0; i+j<nExpr; i++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pOrderBy->a[i+j].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ if( pOrderBy->a[i+j].pExpr->iColumn!=pIdx->aiColumn[i+nEqCol] ||
+ pColl!=pIdx->keyInfo.aColl[i+nEqCol] ) break;
+ }
+ if( i+j>=nExpr ){
+ pMatch = pIdx;
+ if( pIdx==pPreferredIdx ) break;
+ }
+ }
+ if( pMatch && pbRev ){
+ *pbRev = sortOrder==SQLITE_SO_DESC;
+ }
+ return pMatch;
+}
+
+/*
+** Disable a term in the WHERE clause. Except, do not disable the term
+** if it controls a LEFT OUTER JOIN and it did not originate in the ON
+** or USING clause of that join.
+**
+** Consider the term t2.z='ok' in the following queries:
+**
+** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok'
+** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok'
+** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok'
+**
+** The t2.z='ok' is disabled in the in (2) because it did not originate
+** in the ON clause. The term is disabled in (3) because it is not part
+** of a LEFT OUTER JOIN. In (1), the term is not disabled.
+**
+** Disabling a term causes that term to not be tested in the inner loop
+** of the join. Disabling is an optimization. We would get the correct
+** results if nothing were ever disabled, but joins might run a little
+** slower. The trick is to disable as much as we can without disabling
+** too much. If we disabled in (1), we'd get the wrong answer.
+** See ticket #813.
+*/
+static void disableTerm(WhereLevel *pLevel, Expr **ppExpr){
+ Expr *pExpr = *ppExpr;
+ if( pLevel->iLeftJoin==0 || ExprHasProperty(pExpr, EP_FromJoin) ){
+ *ppExpr = 0;
+ }
+}
+
+/*
+** Generate code that builds a probe for an index. Details:
+**
+** * Check the top nColumn entries on the stack. If any
+** of those entries are NULL, jump immediately to brk,
+** which is the loop exit, since no index entry will match
+** if any part of the key is NULL.
+**
+** * Construct a probe entry from the top nColumn entries in
+** the stack with affinities appropriate for index pIdx.
+*/
+static void buildIndexProbe(Vdbe *v, int nColumn, int brk, Index *pIdx){
+ sqlite3VdbeAddOp(v, OP_NotNull, -nColumn, sqlite3VdbeCurrentAddr(v)+3);
+ sqlite3VdbeAddOp(v, OP_Pop, nColumn, 0);
+ sqlite3VdbeAddOp(v, OP_Goto, 0, brk);
+ sqlite3VdbeAddOp(v, OP_MakeRecord, nColumn, 0);
+ sqlite3IndexAffinityStr(v, pIdx);
+}
+
+/*
+** Generate code for an equality term of the WHERE clause. An equality
+** term can be either X=expr or X IN (...). pTerm is the X.
+*/
+static void codeEqualityTerm(
+ Parse *pParse, /* The parsing context */
+ ExprInfo *pTerm, /* The term of the WHERE clause to be coded */
+ int brk, /* Jump here to abandon the loop */
+ WhereLevel *pLevel /* When level of the FROM clause we are working on */
+){
+ Expr *pX = pTerm->p;
+ if( pX->op!=TK_IN ){
+ assert( pX->op==TK_EQ );
+ sqlite3ExprCode(pParse, pX->pRight);
+ }else{
+ int iTab = pX->iTable;
+ Vdbe *v = pParse->pVdbe;
+ sqlite3VdbeAddOp(v, OP_Rewind, iTab, brk);
+ sqlite3VdbeAddOp(v, OP_KeyAsData, iTab, 1);
+ pLevel->inP2 = sqlite3VdbeAddOp(v, OP_IdxColumn, iTab, 0);
+ pLevel->inOp = OP_Next;
+ pLevel->inP1 = iTab;
+ }
+ disableTerm(pLevel, &pTerm->p);
+}
+
+
+/*
+** Generate the beginning of the loop used for WHERE clause processing.
+** The return value is a pointer to an (opaque) structure that contains
+** information needed to terminate the loop. Later, the calling routine
+** should invoke sqlite3WhereEnd() with the return value of this function
+** in order to complete the WHERE clause processing.
+**
+** If an error occurs, this routine returns NULL.
+**
+** The basic idea is to do a nested loop, one loop for each table in
+** the FROM clause of a select. (INSERT and UPDATE statements are the
+** same as a SELECT with only a single table in the FROM clause.) For
+** example, if the SQL is this:
+**
+** SELECT * FROM t1, t2, t3 WHERE ...;
+**
+** Then the code generated is conceptually like the following:
+**
+** foreach row1 in t1 do \ Code generated
+** foreach row2 in t2 do |-- by sqlite3WhereBegin()
+** foreach row3 in t3 do /
+** ...
+** end \ Code generated
+** end |-- by sqlite3WhereEnd()
+** end /
+**
+** There are Btree cursors associated with each table. t1 uses cursor
+** number pTabList->a[0].iCursor. t2 uses the cursor pTabList->a[1].iCursor.
+** And so forth. This routine generates code to open those VDBE cursors
+** and sqlite3WhereEnd() generates the code to close them.
+**
+** If the WHERE clause is empty, the foreach loops must each scan their
+** entire tables. Thus a three-way join is an O(N^3) operation. But if
+** the tables have indices and there are terms in the WHERE clause that
+** refer to those indices, a complete table scan can be avoided and the
+** code will run much faster. Most of the work of this routine is checking
+** to see if there are indices that can be used to speed up the loop.
+**
+** Terms of the WHERE clause are also used to limit which rows actually
+** make it to the "..." in the middle of the loop. After each "foreach",
+** terms of the WHERE clause that use only terms in that loop and outer
+** loops are evaluated and if false a jump is made around all subsequent
+** inner loops (or around the "..." if the test occurs within the inner-
+** most loop)
+**
+** OUTER JOINS
+**
+** An outer join of tables t1 and t2 is conceptally coded as follows:
+**
+** foreach row1 in t1 do
+** flag = 0
+** foreach row2 in t2 do
+** start:
+** ...
+** flag = 1
+** end
+** if flag==0 then
+** move the row2 cursor to a null row
+** goto start
+** fi
+** end
+**
+** ORDER BY CLAUSE PROCESSING
+**
+** *ppOrderBy is a pointer to the ORDER BY clause of a SELECT statement,
+** if there is one. If there is no ORDER BY clause or if this routine
+** is called from an UPDATE or DELETE statement, then ppOrderBy is NULL.
+**
+** If an index can be used so that the natural output order of the table
+** scan is correct for the ORDER BY clause, then that index is used and
+** *ppOrderBy is set to NULL. This is an optimization that prevents an
+** unnecessary sort of the result set if an index appropriate for the
+** ORDER BY clause already exists.
+**
+** If the where clause loops cannot be arranged to provide the correct
+** output order, then the *ppOrderBy is unchanged.
+*/
+WhereInfo *sqlite3WhereBegin(
+ Parse *pParse, /* The parser context */
+ SrcList *pTabList, /* A list of all tables to be scanned */
+ Expr *pWhere, /* The WHERE clause */
+ int pushKey, /* If TRUE, leave the table key on the stack */
+ ExprList **ppOrderBy /* An ORDER BY clause, or NULL */
+){
+ int i; /* Loop counter */
+ WhereInfo *pWInfo; /* Will become the return value of this function */
+ Vdbe *v = pParse->pVdbe; /* The virtual database engine */
+ int brk, cont = 0; /* Addresses used during code generation */
+ int nExpr; /* Number of subexpressions in the WHERE clause */
+ int loopMask; /* One bit set for each outer loop */
+ int haveKey = 0; /* True if KEY is on the stack */
+ ExprInfo *pTerm; /* A single term in the WHERE clause; ptr to aExpr[] */
+ ExprMaskSet maskSet; /* The expression mask set */
+ int iDirectEq[32]; /* Term of the form ROWID==X for the N-th table */
+ int iDirectLt[32]; /* Term of the form ROWID<X or ROWID<=X */
+ int iDirectGt[32]; /* Term of the form ROWID>X or ROWID>=X */
+ ExprInfo aExpr[101]; /* The WHERE clause is divided into these terms */
+
+ /* pushKey is only allowed if there is a single table (as in an INSERT or
+ ** UPDATE statement)
+ */
+ assert( pushKey==0 || pTabList->nSrc==1 );
+
+ /* Split the WHERE clause into separate subexpressions where each
+ ** subexpression is separated by an AND operator. If the aExpr[]
+ ** array fills up, the last entry might point to an expression which
+ ** contains additional unfactored AND operators.
+ */
+ initMaskSet(&maskSet);
+ memset(aExpr, 0, sizeof(aExpr));
+ nExpr = exprSplit(ARRAYSIZE(aExpr), aExpr, pWhere);
+ if( nExpr==ARRAYSIZE(aExpr) ){
+ sqlite3ErrorMsg(pParse, "WHERE clause too complex - no more "
+ "than %d terms allowed", (int)ARRAYSIZE(aExpr)-1);
+ return 0;
+ }
+
+ /* Allocate and initialize the WhereInfo structure that will become the
+ ** return value.
+ */
+ pWInfo = sqliteMalloc( sizeof(WhereInfo) + pTabList->nSrc*sizeof(WhereLevel));
+ if( sqlite3_malloc_failed ){
+ /* sqliteFree(pWInfo); // Leak memory when malloc fails */
+ return 0;
+ }
+ pWInfo->pParse = pParse;
+ pWInfo->pTabList = pTabList;
+ pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
+
+ /* Special case: a WHERE clause that is constant. Evaluate the
+ ** expression and either jump over all of the code or fall thru.
+ */
+ if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstant(pWhere)) ){
+ sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, 1);
+ pWhere = 0;
+ }
+
+ /* Analyze all of the subexpressions.
+ */
+ for(pTerm=aExpr, i=0; i<nExpr; i++, pTerm++){
+ TriggerStack *pStack;
+ exprAnalyze(pTabList, &maskSet, pTerm);
+
+ /* If we are executing a trigger body, remove all references to
+ ** new.* and old.* tables from the prerequisite masks.
+ */
+ if( (pStack = pParse->trigStack)!=0 ){
+ int x;
+ if( (x=pStack->newIdx) >= 0 ){
+ int mask = ~getMask(&maskSet, x);
+ pTerm->prereqRight &= mask;
+ pTerm->prereqLeft &= mask;
+ pTerm->prereqAll &= mask;
+ }
+ if( (x=pStack->oldIdx) >= 0 ){
+ int mask = ~getMask(&maskSet, x);
+ pTerm->prereqRight &= mask;
+ pTerm->prereqLeft &= mask;
+ pTerm->prereqAll &= mask;
+ }
+ }
+ }
+
+ /* Figure out what index to use (if any) for each nested loop.
+ ** Make pWInfo->a[i].pIdx point to the index to use for the i-th nested
+ ** loop where i==0 is the outer loop and i==pTabList->nSrc-1 is the inner
+ ** loop.
+ **
+ ** If terms exist that use the ROWID of any table, then set the
+ ** iDirectEq[], iDirectLt[], or iDirectGt[] elements for that table
+ ** to the index of the term containing the ROWID. We always prefer
+ ** to use a ROWID which can directly access a table rather than an
+ ** index which requires reading an index first to get the rowid then
+ ** doing a second read of the actual database table.
+ **
+ ** Actually, if there are more than 32 tables in the join, only the
+ ** first 32 tables are candidates for indices. This is (again) due
+ ** to the limit of 32 bits in an integer bitmask.
+ */
+ loopMask = 0;
+ for(i=0; i<pTabList->nSrc && i<ARRAYSIZE(iDirectEq); i++){
+ int j;
+ WhereLevel *pLevel = &pWInfo->a[i];
+ int iCur = pTabList->a[i].iCursor; /* The cursor for this table */
+ int mask = getMask(&maskSet, iCur); /* Cursor mask for this table */
+ Table *pTab = pTabList->a[i].pTab;
+ Index *pIdx;
+ Index *pBestIdx = 0;
+ int bestScore = 0;
+
+ /* Check to see if there is an expression that uses only the
+ ** ROWID field of this table. For terms of the form ROWID==expr
+ ** set iDirectEq[i] to the index of the term. For terms of the
+ ** form ROWID<expr or ROWID<=expr set iDirectLt[i] to the term index.
+ ** For terms like ROWID>expr or ROWID>=expr set iDirectGt[i].
+ **
+ ** (Added:) Treat ROWID IN expr like ROWID=expr.
+ */
+ pLevel->iCur = -1;
+ iDirectEq[i] = -1;
+ iDirectLt[i] = -1;
+ iDirectGt[i] = -1;
+ for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){
+ Expr *pX = pTerm->p;
+ if( pTerm->idxLeft==iCur && pX->pLeft->iColumn<0
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight ){
+ switch( pX->op ){
+ case TK_IN:
+ case TK_EQ: iDirectEq[i] = j; break;
+ case TK_LE:
+ case TK_LT: iDirectLt[i] = j; break;
+ case TK_GE:
+ case TK_GT: iDirectGt[i] = j; break;
+ }
+ }
+ }
+ if( iDirectEq[i]>=0 ){
+ loopMask |= mask;
+ pLevel->pIdx = 0;
+ continue;
+ }
+
+ /* Do a search for usable indices. Leave pBestIdx pointing to
+ ** the "best" index. pBestIdx is left set to NULL if no indices
+ ** are usable.
+ **
+ ** The best index is determined as follows. For each of the
+ ** left-most terms that is fixed by an equality operator, add
+ ** 8 to the score. The right-most term of the index may be
+ ** constrained by an inequality. Add 1 if for an "x<..." constraint
+ ** and add 2 for an "x>..." constraint. Chose the index that
+ ** gives the best score.
+ **
+ ** This scoring system is designed so that the score can later be
+ ** used to determine how the index is used. If the score&7 is 0
+ ** then all constraints are equalities. If score&1 is not 0 then
+ ** there is an inequality used as a termination key. (ex: "x<...")
+ ** If score&2 is not 0 then there is an inequality used as the
+ ** start key. (ex: "x>..."). A score or 4 is the special case
+ ** of an IN operator constraint. (ex: "x IN ...").
+ **
+ ** The IN operator (as in "<expr> IN (...)") is treated the same as
+ ** an equality comparison except that it can only be used on the
+ ** left-most column of an index and other terms of the WHERE clause
+ ** cannot be used in conjunction with the IN operator to help satisfy
+ ** other columns of the index.
+ */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int eqMask = 0; /* Index columns covered by an x=... term */
+ int ltMask = 0; /* Index columns covered by an x<... term */
+ int gtMask = 0; /* Index columns covered by an x>... term */
+ int inMask = 0; /* Index columns covered by an x IN .. term */
+ int nEq, m, score;
+
+ if( pIdx->nColumn>32 ) continue; /* Ignore indices too many columns */
+ for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){
+ Expr *pX = pTerm->p;
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pX->pLeft);
+ if( !pColl && pX->pRight ){
+ pColl = sqlite3ExprCollSeq(pParse, pX->pRight);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ if( pTerm->idxLeft==iCur
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight ){
+ int iColumn = pX->pLeft->iColumn;
+ int k;
+ char idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ for(k=0; k<pIdx->nColumn; k++){
+ /* If the collating sequences or affinities don't match,
+ ** ignore this index. */
+ if( pColl!=pIdx->keyInfo.aColl[k] ) continue;
+ if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
+ if( pIdx->aiColumn[k]==iColumn ){
+ switch( pX->op ){
+ case TK_IN: {
+ if( k==0 ) inMask |= 1;
+ break;
+ }
+ case TK_EQ: {
+ eqMask |= 1<<k;
+ break;
+ }
+ case TK_LE:
+ case TK_LT: {
+ ltMask |= 1<<k;
+ break;
+ }
+ case TK_GE:
+ case TK_GT: {
+ gtMask |= 1<<k;
+ break;
+ }
+ default: {
+ /* CANT_HAPPEN */
+ assert( 0 );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /* The following loop ends with nEq set to the number of columns
+ ** on the left of the index with == constraints.
+ */
+ for(nEq=0; nEq<pIdx->nColumn; nEq++){
+ m = (1<<(nEq+1))-1;
+ if( (m & eqMask)!=m ) break;
+ }
+ score = nEq*8; /* Base score is 8 times number of == constraints */
+ m = 1<<nEq;
+ if( m & ltMask ) score++; /* Increase score for a < constraint */
+ if( m & gtMask ) score+=2; /* Increase score for a > constraint */
+ if( score==0 && inMask ) score = 4; /* Default score for IN constraint */
+ if( score>bestScore ){
+ pBestIdx = pIdx;
+ bestScore = score;
+ }
+ }
+ pLevel->pIdx = pBestIdx;
+ pLevel->score = bestScore;
+ pLevel->bRev = 0;
+ loopMask |= mask;
+ if( pBestIdx ){
+ pLevel->iCur = pParse->nTab++;
+ }
+ }
+
+ /* Check to see if the ORDER BY clause is or can be satisfied by the
+ ** use of an index on the first table.
+ */
+ if( ppOrderBy && *ppOrderBy && pTabList->nSrc>0 ){
+ Index *pSortIdx;
+ Index *pIdx;
+ Table *pTab;
+ int bRev = 0;
+
+ pTab = pTabList->a[0].pTab;
+ pIdx = pWInfo->a[0].pIdx;
+ if( pIdx && pWInfo->a[0].score==4 ){
+ /* If there is already an IN index on the left-most table,
+ ** it will not give the correct sort order.
+ ** So, pretend that no suitable index is found.
+ */
+ pSortIdx = 0;
+ }else if( iDirectEq[0]>=0 || iDirectLt[0]>=0 || iDirectGt[0]>=0 ){
+ /* If the left-most column is accessed using its ROWID, then do
+ ** not try to sort by index.
+ */
+ pSortIdx = 0;
+ }else{
+ int nEqCol = (pWInfo->a[0].score+4)/8;
+ pSortIdx = findSortingIndex(pParse, pTab, pTabList->a[0].iCursor,
+ *ppOrderBy, pIdx, nEqCol, &bRev);
+ }
+ if( pSortIdx && (pIdx==0 || pIdx==pSortIdx) ){
+ if( pIdx==0 ){
+ pWInfo->a[0].pIdx = pSortIdx;
+ pWInfo->a[0].iCur = pParse->nTab++;
+ }
+ pWInfo->a[0].bRev = bRev;
+ *ppOrderBy = 0;
+ }
+ }
+
+ /* Open all tables in the pTabList and all indices used by those tables.
+ */
+ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
+ for(i=0; i<pTabList->nSrc; i++){
+ Table *pTab;
+ Index *pIx;
+
+ pTab = pTabList->a[i].pTab;
+ if( pTab->isTransient || pTab->pSelect ) continue;
+ sqlite3OpenTableForReading(v, pTabList->a[i].iCursor, pTab);
+ sqlite3CodeVerifySchema(pParse, pTab->iDb);
+ if( (pIx = pWInfo->a[i].pIdx)!=0 ){
+ sqlite3VdbeAddOp(v, OP_Integer, pIx->iDb, 0);
+ sqlite3VdbeOp3(v, OP_OpenRead, pWInfo->a[i].iCur, pIx->tnum,
+ (char*)&pIx->keyInfo, P3_KEYINFO);
+ }
+ }
+
+ /* Generate the code to do the search
+ */
+ loopMask = 0;
+ for(i=0; i<pTabList->nSrc; i++){
+ int j, k;
+ int iCur = pTabList->a[i].iCursor;
+ Index *pIdx;
+ WhereLevel *pLevel = &pWInfo->a[i];
+
+ /* If this is the right table of a LEFT OUTER JOIN, allocate and
+ ** initialize a memory cell that records if this table matches any
+ ** row of the left table of the join.
+ */
+ if( i>0 && (pTabList->a[i-1].jointype & JT_LEFT)!=0 ){
+ if( !pParse->nMem ) pParse->nMem++;
+ pLevel->iLeftJoin = pParse->nMem++;
+ sqlite3VdbeAddOp(v, OP_String8, 0, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1);
+ VdbeComment((v, "# init LEFT JOIN no-match flag"));
+ }
+
+ pIdx = pLevel->pIdx;
+ pLevel->inOp = OP_Noop;
+ if( i<ARRAYSIZE(iDirectEq) && (k = iDirectEq[i])>=0 ){
+ /* Case 1: We can directly reference a single row using an
+ ** equality comparison against the ROWID field. Or
+ ** we reference multiple rows using a "rowid IN (...)"
+ ** construct.
+ */
+ assert( k<nExpr );
+ pTerm = &aExpr[k];
+ assert( pTerm->p!=0 );
+ assert( pTerm->idxLeft==iCur );
+ brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
+ codeEqualityTerm(pParse, pTerm, brk, pLevel);
+ cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_MustBeInt, 1, brk);
+ haveKey = 0;
+ sqlite3VdbeAddOp(v, OP_NotExists, iCur, brk);
+ pLevel->op = OP_Noop;
+ }else if( pIdx!=0 && pLevel->score>0 && pLevel->score%4==0 ){
+ /* Case 2: There is an index and all terms of the WHERE clause that
+ ** refer to the index use the "==" or "IN" operators.
+ */
+ int start;
+ int nColumn = (pLevel->score+4)/8;
+ brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
+
+ /* For each column of the index, find the term of the WHERE clause that
+ ** constraints that column. If the WHERE clause term is X=expr, then
+ ** evaluation expr and leave the result on the stack */
+ for(j=0; j<nColumn; j++){
+ for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){
+ Expr *pX = pTerm->p;
+ if( pX==0 ) continue;
+ if( pTerm->idxLeft==iCur
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight
+ && pX->pLeft->iColumn==pIdx->aiColumn[j]
+ ){
+ char idxaff = pIdx->pTable->aCol[pX->pLeft->iColumn].affinity;
+ if( sqlite3IndexAffinityOk(pX, idxaff) ){
+ codeEqualityTerm(pParse, pTerm, brk, pLevel);
+ break;
+ }
+ }
+ }
+ }
+ pLevel->iMem = pParse->nMem++;
+ cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
+ buildIndexProbe(v, nColumn, brk, pIdx);
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 0);
+
+ /* Generate code (1) to move to the first matching element of the table.
+ ** Then generate code (2) that jumps to "brk" after the cursor is past
+ ** the last matching element of the table. The code (1) is executed
+ ** once to initialize the search, the code (2) is executed before each
+ ** iteration of the scan to see if the scan has finished. */
+ if( pLevel->bRev ){
+ /* Scan in reverse order */
+ sqlite3VdbeAddOp(v, OP_MoveLe, pLevel->iCur, brk);
+ start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
+ sqlite3VdbeAddOp(v, OP_IdxLT, pLevel->iCur, brk);
+ pLevel->op = OP_Prev;
+ }else{
+ /* Scan in the forward order */
+ sqlite3VdbeAddOp(v, OP_MoveGe, pLevel->iCur, brk);
+ start = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
+ sqlite3VdbeOp3(v, OP_IdxGE, pLevel->iCur, brk, "+", P3_STATIC);
+ pLevel->op = OP_Next;
+ }
+ sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
+ sqlite3VdbeAddOp(v, OP_IdxIsNull, nColumn, cont);
+ sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
+ if( i==pTabList->nSrc-1 && pushKey ){
+ haveKey = 1;
+ }else{
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ haveKey = 0;
+ }
+ pLevel->p1 = pLevel->iCur;
+ pLevel->p2 = start;
+ }else if( i<ARRAYSIZE(iDirectLt) && (iDirectLt[i]>=0 || iDirectGt[i]>=0) ){
+ /* Case 3: We have an inequality comparison against the ROWID field.
+ */
+ int testOp = OP_Noop;
+ int start;
+
+ brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
+ cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
+ if( iDirectGt[i]>=0 ){
+ Expr *pX;
+ k = iDirectGt[i];
+ assert( k<nExpr );
+ pTerm = &aExpr[k];
+ pX = pTerm->p;
+ assert( pX!=0 );
+ assert( pTerm->idxLeft==iCur );
+ sqlite3ExprCode(pParse, pX->pRight);
+ sqlite3VdbeAddOp(v, OP_ForceInt, pX->op==TK_LT || pX->op==TK_GT, brk);
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, brk);
+ disableTerm(pLevel, &pTerm->p);
+ }else{
+ sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk);
+ }
+ if( iDirectLt[i]>=0 ){
+ Expr *pX;
+ k = iDirectLt[i];
+ assert( k<nExpr );
+ pTerm = &aExpr[k];
+ pX = pTerm->p;
+ assert( pX!=0 );
+ assert( pTerm->idxLeft==iCur );
+ sqlite3ExprCode(pParse, pX->pRight);
+ pLevel->iMem = pParse->nMem++;
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+ if( pX->op==TK_LT || pX->op==TK_GT ){
+ testOp = OP_Ge;
+ }else{
+ testOp = OP_Gt;
+ }
+ disableTerm(pLevel, &pTerm->p);
+ }
+ start = sqlite3VdbeCurrentAddr(v);
+ pLevel->op = OP_Next;
+ pLevel->p1 = iCur;
+ pLevel->p2 = start;
+ if( testOp!=OP_Noop ){
+ sqlite3VdbeAddOp(v, OP_Recno, iCur, 0);
+ sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
+ sqlite3VdbeAddOp(v, testOp, 0, brk);
+ }
+ haveKey = 0;
+ }else if( pIdx==0 ){
+ /* Case 4: There is no usable index. We must do a complete
+ ** scan of the entire database table.
+ */
+ int start;
+
+ brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
+ cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp(v, OP_Rewind, iCur, brk);
+ start = sqlite3VdbeCurrentAddr(v);
+ pLevel->op = OP_Next;
+ pLevel->p1 = iCur;
+ pLevel->p2 = start;
+ haveKey = 0;
+ }else{
+ /* Case 5: The WHERE clause term that refers to the right-most
+ ** column of the index is an inequality. For example, if
+ ** the index is on (x,y,z) and the WHERE clause is of the
+ ** form "x=5 AND y<10" then this case is used. Only the
+ ** right-most column can be an inequality - the rest must
+ ** use the "==" operator.
+ **
+ ** This case is also used when there are no WHERE clause
+ ** constraints but an index is selected anyway, in order
+ ** to force the output order to conform to an ORDER BY.
+ */
+ int score = pLevel->score;
+ int nEqColumn = score/8;
+ int start;
+ int leFlag=0, geFlag=0;
+ int testOp;
+
+ /* Evaluate the equality constraints
+ */
+ for(j=0; j<nEqColumn; j++){
+ int iIdxCol = pIdx->aiColumn[j];
+ for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){
+ Expr *pX = pTerm->p;
+ if( pX==0 ) continue;
+ if( pTerm->idxLeft==iCur
+ && pX->op==TK_EQ
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight
+ && pX->pLeft->iColumn==iIdxCol
+ ){
+ sqlite3ExprCode(pParse, pX->pRight);
+ disableTerm(pLevel, &pTerm->p);
+ break;
+ }
+ }
+ }
+
+ /* Duplicate the equality term values because they will all be
+ ** used twice: once to make the termination key and once to make the
+ ** start key.
+ */
+ for(j=0; j<nEqColumn; j++){
+ sqlite3VdbeAddOp(v, OP_Dup, nEqColumn-1, 0);
+ }
+
+ /* Labels for the beginning and end of the loop
+ */
+ cont = pLevel->cont = sqlite3VdbeMakeLabel(v);
+ brk = pLevel->brk = sqlite3VdbeMakeLabel(v);
+
+ /* Generate the termination key. This is the key value that
+ ** will end the search. There is no termination key if there
+ ** are no equality terms and no "X<..." term.
+ **
+ ** 2002-Dec-04: On a reverse-order scan, the so-called "termination"
+ ** key computed here really ends up being the start key.
+ */
+ if( (score & 1)!=0 ){
+ for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){
+ Expr *pX = pTerm->p;
+ if( pX==0 ) continue;
+ if( pTerm->idxLeft==iCur
+ && (pX->op==TK_LT || pX->op==TK_LE)
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight
+ && pX->pLeft->iColumn==pIdx->aiColumn[j]
+ ){
+ sqlite3ExprCode(pParse, pX->pRight);
+ leFlag = pX->op==TK_LE;
+ disableTerm(pLevel, &pTerm->p);
+ break;
+ }
+ }
+ testOp = OP_IdxGE;
+ }else{
+ testOp = nEqColumn>0 ? OP_IdxGE : OP_Noop;
+ leFlag = 1;
+ }
+ if( testOp!=OP_Noop ){
+ int nCol = nEqColumn + (score & 1);
+ pLevel->iMem = pParse->nMem++;
+ buildIndexProbe(v, nCol, brk, pIdx);
+ if( pLevel->bRev ){
+ int op = leFlag ? OP_MoveLe : OP_MoveLt;
+ sqlite3VdbeAddOp(v, op, pLevel->iCur, brk);
+ }else{
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+ }
+ }else if( pLevel->bRev ){
+ sqlite3VdbeAddOp(v, OP_Last, pLevel->iCur, brk);
+ }
+
+ /* Generate the start key. This is the key that defines the lower
+ ** bound on the search. There is no start key if there are no
+ ** equality terms and if there is no "X>..." term. In
+ ** that case, generate a "Rewind" instruction in place of the
+ ** start key search.
+ **
+ ** 2002-Dec-04: In the case of a reverse-order search, the so-called
+ ** "start" key really ends up being used as the termination key.
+ */
+ if( (score & 2)!=0 ){
+ for(pTerm=aExpr, k=0; k<nExpr; k++, pTerm++){
+ Expr *pX = pTerm->p;
+ if( pX==0 ) continue;
+ if( pTerm->idxLeft==iCur
+ && (pX->op==TK_GT || pX->op==TK_GE)
+ && (pTerm->prereqRight & loopMask)==pTerm->prereqRight
+ && pX->pLeft->iColumn==pIdx->aiColumn[j]
+ ){
+ sqlite3ExprCode(pParse, pX->pRight);
+ geFlag = pX->op==TK_GE;
+ disableTerm(pLevel, &pTerm->p);
+ break;
+ }
+ }
+ }else{
+ geFlag = 1;
+ }
+ if( nEqColumn>0 || (score&2)!=0 ){
+ int nCol = nEqColumn + ((score&2)!=0);
+ buildIndexProbe(v, nCol, brk, pIdx);
+ if( pLevel->bRev ){
+ pLevel->iMem = pParse->nMem++;
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iMem, 1);
+ testOp = OP_IdxLT;
+ }else{
+ int op = geFlag ? OP_MoveGe : OP_MoveGt;
+ sqlite3VdbeAddOp(v, op, pLevel->iCur, brk);
+ }
+ }else if( pLevel->bRev ){
+ testOp = OP_Noop;
+ }else{
+ sqlite3VdbeAddOp(v, OP_Rewind, pLevel->iCur, brk);
+ }
+
+ /* Generate the the top of the loop. If there is a termination
+ ** key we have to test for that key and abort at the top of the
+ ** loop.
+ */
+ start = sqlite3VdbeCurrentAddr(v);
+ if( testOp!=OP_Noop ){
+ sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iMem, 0);
+ sqlite3VdbeAddOp(v, testOp, pLevel->iCur, brk);
+ if( (leFlag && !pLevel->bRev) || (!geFlag && pLevel->bRev) ){
+ sqlite3VdbeChangeP3(v, -1, "+", P3_STATIC);
+ }
+ }
+ sqlite3VdbeAddOp(v, OP_RowKey, pLevel->iCur, 0);
+ sqlite3VdbeAddOp(v, OP_IdxIsNull, nEqColumn + (score & 1), cont);
+ sqlite3VdbeAddOp(v, OP_IdxRecno, pLevel->iCur, 0);
+ if( i==pTabList->nSrc-1 && pushKey ){
+ haveKey = 1;
+ }else{
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ haveKey = 0;
+ }
+
+ /* Record the instruction used to terminate the loop.
+ */
+ pLevel->op = pLevel->bRev ? OP_Prev : OP_Next;
+ pLevel->p1 = pLevel->iCur;
+ pLevel->p2 = start;
+ }
+ loopMask |= getMask(&maskSet, iCur);
+
+ /* Insert code to test every subexpression that can be completely
+ ** computed using the current set of tables.
+ */
+ for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){
+ if( pTerm->p==0 ) continue;
+ if( (pTerm->prereqAll & loopMask)!=pTerm->prereqAll ) continue;
+ if( pLevel->iLeftJoin && !ExprHasProperty(pTerm->p,EP_FromJoin) ){
+ continue;
+ }
+ if( haveKey ){
+ haveKey = 0;
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ }
+ sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1);
+ pTerm->p = 0;
+ }
+ brk = cont;
+
+ /* For a LEFT OUTER JOIN, generate code that will record the fact that
+ ** at least one row of the right table has matched the left table.
+ */
+ if( pLevel->iLeftJoin ){
+ pLevel->top = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp(v, OP_Integer, 1, 0);
+ sqlite3VdbeAddOp(v, OP_MemStore, pLevel->iLeftJoin, 1);
+ VdbeComment((v, "# record LEFT JOIN hit"));
+ for(pTerm=aExpr, j=0; j<nExpr; j++, pTerm++){
+ if( pTerm->p==0 ) continue;
+ if( (pTerm->prereqAll & loopMask)!=pTerm->prereqAll ) continue;
+ if( haveKey ){
+ /* Cannot happen. "haveKey" can only be true if pushKey is true
+ ** an pushKey can only be true for DELETE and UPDATE and there are
+ ** no outer joins with DELETE and UPDATE.
+ */
+ haveKey = 0;
+ sqlite3VdbeAddOp(v, OP_MoveGe, iCur, 0);
+ }
+ sqlite3ExprIfFalse(pParse, pTerm->p, cont, 1);
+ pTerm->p = 0;
+ }
+ }
+ }
+ pWInfo->iContinue = cont;
+ if( pushKey && !haveKey ){
+ sqlite3VdbeAddOp(v, OP_Recno, pTabList->a[0].iCursor, 0);
+ }
+ freeMaskSet(&maskSet);
+ return pWInfo;
+}
+
+/*
+** Generate the end of the WHERE loop. See comments on
+** sqlite3WhereBegin() for additional information.
+*/
+void sqlite3WhereEnd(WhereInfo *pWInfo){
+ Vdbe *v = pWInfo->pParse->pVdbe;
+ int i;
+ WhereLevel *pLevel;
+ SrcList *pTabList = pWInfo->pTabList;
+
+ for(i=pTabList->nSrc-1; i>=0; i--){
+ pLevel = &pWInfo->a[i];
+ sqlite3VdbeResolveLabel(v, pLevel->cont);
+ if( pLevel->op!=OP_Noop ){
+ sqlite3VdbeAddOp(v, pLevel->op, pLevel->p1, pLevel->p2);
+ }
+ sqlite3VdbeResolveLabel(v, pLevel->brk);
+ if( pLevel->inOp!=OP_Noop ){
+ sqlite3VdbeAddOp(v, pLevel->inOp, pLevel->inP1, pLevel->inP2);
+ }
+ if( pLevel->iLeftJoin ){
+ int addr;
+ addr = sqlite3VdbeAddOp(v, OP_MemLoad, pLevel->iLeftJoin, 0);
+ sqlite3VdbeAddOp(v, OP_NotNull, 1, addr+4 + (pLevel->iCur>=0));
+ sqlite3VdbeAddOp(v, OP_NullRow, pTabList->a[i].iCursor, 0);
+ if( pLevel->iCur>=0 ){
+ sqlite3VdbeAddOp(v, OP_NullRow, pLevel->iCur, 0);
+ }
+ sqlite3VdbeAddOp(v, OP_Goto, 0, pLevel->top);
+ }
+ }
+ sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
+ for(i=0; i<pTabList->nSrc; i++){
+ Table *pTab = pTabList->a[i].pTab;
+ assert( pTab!=0 );
+ if( pTab->isTransient || pTab->pSelect ) continue;
+ pLevel = &pWInfo->a[i];
+ sqlite3VdbeAddOp(v, OP_Close, pTabList->a[i].iCursor, 0);
+ if( pLevel->pIdx!=0 ){
+ sqlite3VdbeAddOp(v, OP_Close, pLevel->iCur, 0);
+ }
+ }
+ sqliteFree(pWInfo);
+ return;
+}
diff --git a/kopete/plugins/statistics/statisticscontact.cpp b/kopete/plugins/statistics/statisticscontact.cpp
new file mode 100644
index 00000000..e9068fe5
--- /dev/null
+++ b/kopete/plugins/statistics/statisticscontact.cpp
@@ -0,0 +1,515 @@
+/*
+ statisticscontact.cpp
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 <stdlib.h>
+
+#include <qvaluelist.h>
+#include <quuid.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kopetemetacontact.h"
+#include "kopeteonlinestatus.h"
+
+#include "statisticscontact.h"
+#include "statisticsdb.h"
+
+StatisticsContact::StatisticsContact(Kopete::MetaContact *mc, StatisticsDB *db) : m_metaContact(mc),m_db(db), m_oldStatus(Kopete::OnlineStatus::Unknown)
+{
+ m_isChatWindowOpen = false;
+ m_oldStatusDateTime = QDateTime::currentDateTime();
+
+ // Last*Changed are always false at start
+ m_timeBetweenTwoMessagesChanged = false;
+ m_lastTalkChanged = false;
+ m_lastPresentChanged = false;
+ m_messageLengthChanged = false;
+}
+
+/**
+ * \brief saves contact statistics
+ */
+StatisticsContact::~StatisticsContact()
+{
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ commonStatsSave("timebetweentwomessages",QString::number(m_timeBetweenTwoMessages),
+ QString::number(m_timeBetweenTwoMessagesOn), m_timeBetweenTwoMessagesChanged);
+ commonStatsSave("messagelength",QString::number(m_messageLength), QString::number(m_messageLengthOn), m_messageLengthChanged);
+ commonStatsSave("lasttalk", m_lastTalk.toString(), "", m_lastTalkChanged);
+ commonStatsSave("lastpresent", m_lastPresent.toString(), "", m_lastPresentChanged);
+}
+
+void StatisticsContact::initialize(Kopete::Contact *c)
+{
+ // Generate statisticsContactId or get it from database
+ QStringList buffer = m_db->query(QString("SELECT statisticid FROM contacts "
+ "WHERE contactid LIKE '%1';"
+ ).arg(c->contactId()));
+
+ if (buffer.isEmpty())
+ {
+ // Check if we don't have old data
+ if ( !c->metaContact()->metaContactId().isEmpty() &&
+ !m_db->query(QString("SELECT metacontactid FROM commonstats "
+ "WHERE metacontactid LIKE '%1';"
+ ).arg(c->metaContact()->metaContactId())).isEmpty())
+ {
+ // Use old style id
+ m_statisticsContactId = c->metaContact()->metaContactId();
+ }
+ else
+ {
+ // Create new id
+ m_statisticsContactId = QUuid::createUuid().toString();
+ }
+
+ // Assign contactId to m_statisticsContactId
+ m_db->query(QString("INSERT INTO contacts (statisticid, contactid) VALUES('%1', '%2');"
+ ).arg(m_statisticsContactId).arg(c->contactId()));
+ }
+ else
+ {
+ m_statisticsContactId = buffer[0];
+ }
+
+ kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl;
+
+ commonStatsCheck("timebetweentwomessages", m_timeBetweenTwoMessages, m_timeBetweenTwoMessagesOn, 0, -1);
+ commonStatsCheck("messagelength", m_messageLength, m_messageLengthOn, 0, 0);
+
+ // Check for last talk
+ QString lastTalk;
+ QString dummy = "";
+ commonStatsCheck("lasttalk", lastTalk, dummy);
+ if (lastTalk.isEmpty())
+ {
+ m_lastTalk.setTime_t(0);
+ m_lastTalkChanged = true;
+ }
+ else
+ m_lastTalk = QDateTime::fromString(lastTalk);
+
+
+ // Get last time a message was received
+ m_lastMessageReceived = QDateTime::currentDateTime();
+
+
+ // Check for lastPresent
+ QString lastPresent = "";
+ commonStatsCheck("lastpresent", lastPresent, dummy);
+ if (lastPresent.isEmpty())
+ {
+ m_lastPresent.setTime_t(0);
+ m_lastPresentChanged = true;
+ }
+ else
+ m_lastPresent = QDateTime::fromString(lastPresent);
+}
+
+void StatisticsContact::contactAdded( Kopete::Contact *c )
+{
+ if ( !m_statisticsContactId.isEmpty() )
+ {
+ // Check if contact is allready in database if not add it
+ if (m_db->query(QString("SELECT id FROM contacts "
+ "WHERE statisticid LIKE '%1' AND contactid LIKE '%2';"
+ ).arg(m_statisticsContactId).arg(c->contactId())).isEmpty())
+ {
+ // Assign contactId to m_statisticsContactId
+ m_db->query(QString("INSERT INTO contacts (statisticid, contactid) VALUES('%1', '%2');"
+ ).arg(m_statisticsContactId).arg(c->contactId()));
+ }
+ kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl;
+ }
+ else
+ {
+ // This is first contact, we need to initialize this object
+ initialize(c);
+ }
+}
+
+void StatisticsContact::contactRemoved( Kopete::Contact *c )
+{
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl;
+ m_db->query(QString("DELETE FROM contacts WHERE statisticid LIKE '%1' AND contactid LIKE '%2';"
+ ).arg(m_statisticsContactId).arg(c->contactId()));
+}
+
+void StatisticsContact::removeFromDB()
+{
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ kdDebug() << k_funcinfo << " m_statisticsContactId: " << m_statisticsContactId << endl;
+ m_db->query(QString("DELETE FROM contacts WHERE statisticid LIKE '%1';").arg(m_statisticsContactId));
+ m_db->query(QString("DELETE FROM contactstatus WHERE metacontactid LIKE '%1';").arg(m_statisticsContactId));
+ m_db->query(QString("DELETE FROM commonstats WHERE metacontactid LIKE '%1';").arg(m_statisticsContactId));
+
+ m_statisticsContactId = QString::null;
+}
+
+void StatisticsContact::commonStatsSave(const QString name, const QString statVar1, const QString statVar2, const bool statVarChanged)
+{
+ // Only update the database if there was a change
+ if (!statVarChanged) return;
+
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ m_db->query(QString("UPDATE commonstats SET statvalue1 = '%1', statvalue2='%2'"
+ "WHERE statname LIKE '%3' AND metacontactid LIKE '%4';").arg(statVar1).arg(statVar2).arg(name).arg(m_statisticsContactId));
+
+}
+
+void StatisticsContact::commonStatsCheck(const QString name, int& statVar1, int& statVar2, const int defaultValue1, const int defaultValue2)
+{
+ QString a = QString::number(statVar1);
+ QString b = QString::number(statVar2);
+
+ commonStatsCheck(name, a, b, QString::number(defaultValue1), QString::number(defaultValue2));
+
+ statVar1 = a.toInt();
+ statVar2 = b.toInt();
+}
+
+void StatisticsContact::commonStatsCheck(const QString name, QString& statVar1, QString& statVar2, const QString defaultValue1, const QString defaultValue2)
+{
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ QStringList buffer = m_db->query(QString("SELECT statvalue1,statvalue2 FROM commonstats WHERE statname LIKE '%1' AND metacontactid LIKE '%2';").arg(name, m_statisticsContactId));
+ if (!buffer.isEmpty())
+ {
+ statVar1 = buffer[0];
+ statVar2 = buffer[1];
+ }
+ else
+ {
+ m_db->query(QString("INSERT INTO commonstats (metacontactid, statname, statvalue1, statvalue2) VALUES('%1', '%2', 0, 0);").arg(m_statisticsContactId, name));
+ statVar1 = defaultValue1;
+ statVar2 = defaultValue2;
+ }
+}
+
+/**
+ * \brief records informations from the new message
+ *
+ * Currently it does :
+ * <ul>
+ * <li>Recalculate the average time between two messages
+ * It should only calculate this time if a chatwindow is open (sure, it isn't
+ * perfect, because we could let a chatwindow open a whole day but that's at this * time the nicest way, maybe we could check the time between the two last messages
+ * and if it is greater than, say, 10 min, do as there where no previous message)
+ * So we do this when the chatwindow is open. We don't set m_isChatWindowOpen to true
+ * when a new chatwindow is open, but when a new message arrives. However, we set it
+ * to false when the chatwindow is closed (see StatisticsPlugin::slotViewClosed).
+ *
+ * Then it is only a question of some calculations.
+ *
+ * <li>Recalculate the average message lenght
+ *
+ * <li>Change last-talk datetime
+ * </ul>
+ *
+ */
+void StatisticsContact::newMessageReceived(Kopete::Message& m)
+{
+ kdDebug() << "statistics: new message received" << endl;
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+
+ if (m_timeBetweenTwoMessagesOn != -1 && m_isChatWindowOpen)
+ {
+ m_timeBetweenTwoMessages = (m_timeBetweenTwoMessages*m_timeBetweenTwoMessagesOn + m_lastMessageReceived.secsTo(currentDateTime))/(1 + m_timeBetweenTwoMessagesOn);
+
+ }
+
+ setIsChatWindowOpen(true);
+
+ m_timeBetweenTwoMessagesOn += 1;
+ m_lastMessageReceived = currentDateTime;
+
+
+ // Message lenght
+ m_messageLength= (m.plainBody().length() + m_messageLength * m_messageLengthOn)/(1 + m_messageLengthOn);
+ m_messageLengthOn++;
+
+ // Last talked
+ /// @todo do this in message sent too. So we need setLastTalk()
+ m_lastTalk = currentDateTime;
+
+ m_messageLengthChanged = true;
+ m_lastTalkChanged = true;
+ m_timeBetweenTwoMessagesChanged = true;
+}
+
+/**
+ * \brief Update the database for this contact when required.
+ */
+void StatisticsContact::onlineStatusChanged(Kopete::OnlineStatus::StatusType status)
+{
+ if (m_statisticsContactId.isEmpty())
+ return;
+
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+
+ /// We don't want to log if oldStatus is unknown
+ /// the change could not be a real one; see StatisticsPlugin::slotMySelfOnlineStatusChanged
+ if (m_oldStatus != Kopete::OnlineStatus::Unknown)
+ {
+
+ kdDebug() << "statistics - status change for "<< metaContact()->metaContactId() << " : "<< QString::number(m_oldStatus) << endl;
+ m_db->query(QString("INSERT INTO contactstatus "
+ "(metacontactid, status, datetimebegin, datetimeend) "
+ "VALUES('%1', '%2', '%3', '%4'" ");").arg(m_statisticsContactId).arg(Kopete::OnlineStatus::statusTypeToString(m_oldStatus)).arg(QString::number(m_oldStatusDateTime.toTime_t())).arg(QString::number(currentDateTime.toTime_t())));
+ }
+
+ if (m_oldStatus == Kopete::OnlineStatus::Online || m_oldStatus == Kopete::OnlineStatus::Away)
+ // If the last status was Online or Away, the last time contact was present is the time he goes offline
+ {
+ m_lastPresent = currentDateTime;
+ m_lastPresentChanged = true;
+ }
+
+ m_oldStatus = status;
+ m_oldStatusDateTime = currentDateTime;
+
+}
+
+bool StatisticsContact::wasStatus(QDateTime dt, Kopete::OnlineStatus::StatusType status)
+{
+ if (m_statisticsContactId.isEmpty())
+ return false;
+
+ QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend "
+ "FROM contactstatus WHERE metacontactid LIKE '%1' AND datetimebegin <= %2 AND datetimeend >= %3 "
+ "AND status LIKE '%4' "
+ "ORDER BY datetimebegin;"
+ ).arg(m_statisticsContactId).arg(dt.toTime_t()).arg(dt.toTime_t()).arg(Kopete::OnlineStatus::statusTypeToString(status)));
+
+ if (!values.isEmpty()) return true;
+
+ return false;
+}
+
+QString StatisticsContact::statusAt(QDateTime dt)
+{
+ if (m_statisticsContactId.isEmpty())
+ return "";
+
+ QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend "
+ "FROM contactstatus WHERE metacontactid LIKE '%1' AND datetimebegin <= %2 AND datetimeend >= %3 "
+ "ORDER BY datetimebegin;"
+ ).arg(m_statisticsContactId).arg(dt.toTime_t()).arg(dt.toTime_t()));
+
+ if (!values.isEmpty()) return Kopete::OnlineStatus(Kopete::OnlineStatus::statusStringToType(values[0])).description();
+ else return "";
+}
+
+QString StatisticsContact::mainStatusDate(const QDate& date)
+{
+ if (m_statisticsContactId.isEmpty())
+ return "";
+
+ QDateTime dt1(date, QTime(0,0,0));
+ QDateTime dt2(date.addDays(1), QTime(0,0,0));
+ kdDebug() << "dt1:" << dt1.toString() << " dt2:" << dt2.toString() << endl;
+ QString request = QString("SELECT status, datetimebegin, datetimeend, metacontactid "
+ "FROM contactstatus WHERE metacontactid = '%1' AND "
+ "(datetimebegin >= %2 AND datetimebegin <= %3 OR "
+ "datetimeend >= %4 AND datetimeend <= %5) "
+ "ORDER BY datetimebegin;"
+ ).arg(m_statisticsContactId).arg(dt1.toTime_t()).arg(dt2.toTime_t()).arg(dt1.toTime_t()).arg(dt2.toTime_t());
+ kdDebug() << request << endl;
+ QStringList values = m_db->query(request);
+
+ unsigned int online = 0, offline = 0, away = 0;
+ for(uint i=0; i<values.count(); i+=4)
+ {
+ unsigned int datetimebegin = values[i+1].toInt(), datetimeend = values[i+2].toInt();
+ kdDebug() << "statistics: id "<< values[i+3]<< " status " << values[i] << " datetimeend " << QString::number(datetimeend) << " datetimebegin " << QString::number(datetimebegin) << endl;
+ if (datetimebegin <= dt1.toTime_t()) datetimebegin = dt1.toTime_t();
+ if (datetimeend >= dt2.toTime_t()) datetimeend = dt2.toTime_t();
+
+
+
+ if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Online))
+ online += datetimeend - datetimebegin;
+ else if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Away))
+ away += datetimeend - datetimebegin;
+ else if (values[i]==Kopete::OnlineStatus::statusTypeToString(Kopete::OnlineStatus::Offline))
+ offline += datetimeend - datetimebegin;
+ }
+
+ if (online > away && online > offline) return i18n("Online");
+ else if (away > online && away > offline) return i18n("Away");
+ else if (offline > online && offline > away) return i18n("Offline");
+
+ return "";
+}
+
+// QDateTime StatisticsContact::nextOfflineEvent()
+// {
+// return nextEvent(Kopete::OnlineStatus::Offline);
+// }
+//
+// QDateTime StatisticsContact::nextOnlineEvent()
+// {
+// return nextEvent(Kopete::OnlineStatus::Online);
+// }
+
+// QDateTime StatisticsContact::nextEvent(const Kopete::OnlineStatus::StatusType& status)
+// {
+//
+// }
+
+QValueList<QTime> StatisticsContact::mainEvents(const Kopete::OnlineStatus::StatusType& status)
+{
+ QStringList buffer;
+ QValueList<QTime> mainEvents;
+
+ if (m_statisticsContactId.isEmpty())
+ return mainEvents;
+
+ QDateTime currentDateTime = QDateTime::currentDateTime();
+ buffer = m_db->query(QString("SELECT datetimebegin, datetimeend, status FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin").arg(m_statisticsContactId));
+
+
+ // Only select the events for which the previous is not Unknown AND the status is status.
+ QStringList values;
+ for (uint i=0; i<buffer.count(); i += 3)
+ {
+ if (buffer[i+2] == Kopete::OnlineStatus::statusTypeToString(status)
+ && abs(buffer[i+1].toInt()-buffer[i].toInt()) > 120)
+ {
+ values.push_back(buffer[i]);
+ }
+ }
+
+ // No entries for this contact ...
+ if (!values.count()) return mainEvents;
+
+ // First we compute the average number of events/day : avEventsPerDay;
+ int avEventsPerDay = 0;
+ QDateTime dt1, dt2;
+ dt1.setTime_t(values[0].toInt());
+ dt2.setTime_t(values[values.count()-1].toInt());
+
+ avEventsPerDay = qRound((double)values.count()/(double)dt1.daysTo(dt2));
+ kdDebug() << "statistics: average events per day : " <<avEventsPerDay << endl;
+
+ // We want to work on hours
+ QValueList<int> hoursValues;
+ for (uint i=0; i<values.count(); i++)
+ {
+ QDateTime dt;
+ dt.setTime_t(values[i].toInt());
+ hoursValues.push_back(QTime(0, 0, 0).secsTo(dt.time()));
+ }
+
+ // Sort the list
+ qHeapSort(hoursValues);
+
+ // Then we put some centroids (centroids in [0..24[)
+ QValueList<int> centroids;
+ int incr=qRound((double)hoursValues.count()/(double)avEventsPerDay);
+ incr = incr ? incr : 1;
+ for (uint i=0; i<hoursValues.count(); i+=incr)
+ {
+ centroids.push_back(hoursValues[i]);
+ kdDebug() << "statistics: add a centroid : " << centroids[centroids.count()-1] << endl;
+ }
+
+
+ // We need to compute the centroids
+ centroids = computeCentroids(centroids, hoursValues);
+
+ // Convert to QDateTime
+ for (uint i=0; i<centroids.count(); i++)
+ {
+ kdDebug() << "statistics: new centroid : " << centroids[i] << endl;
+
+ QTime dt(0, 0, 0);
+ dt = dt.addSecs(centroids[i]);
+ mainEvents.push_back(dt);
+ }
+
+
+ return mainEvents;
+}
+
+QValueList<int> StatisticsContact::computeCentroids(const QValueList<int>& centroids, const QValueList<int>& values)
+{
+ kdDebug() << "statistics: enter compute centroids"<< endl;
+
+ QValueList<int> whichCentroid; // whichCentroid[i] = j <=> values[i] has centroid j for closest one
+ QValueList<int> newCentroids;
+ for (uint i=0; i<values.count(); i++)
+ // Iterates over the values. For each one we need to get the closest centroid.
+ {
+ int value = values[i];
+ int distanceToNearestCentroid = abs(centroids[0]-value);
+ int nearestCentroid = 0;
+ for (uint j=1; j<centroids.count(); j++)
+ {
+ if (abs(centroids[j]-value) < distanceToNearestCentroid)
+ {
+ distanceToNearestCentroid = abs(centroids[j]-value);
+ nearestCentroid = j;
+ }
+ }
+ whichCentroid.push_back(nearestCentroid);
+ }
+
+ // Recompute centroids
+ newCentroids = centroids;
+
+ for (uint i=0; i<newCentroids.count(); i++)
+ {
+ kdDebug() << "statistics: compute new centroids"<< i << endl;
+ int weight = 0;
+ for (uint j=0; j<values.count(); j++)
+ {
+ int value = values[j];
+ if (whichCentroid[j] == i)
+ {
+ newCentroids[i] = qRound((double)(value + newCentroids[i]*weight)/(double)(weight + 1));
+ weight++;
+
+ }
+ }
+ }
+
+
+
+ // Should we recompute or are we OK ?
+ int dist = 0;
+ for (uint i=0; i < newCentroids.count(); i++)
+ dist += abs(newCentroids[i]-centroids[i]);
+
+ if (dist > 10)
+ return computeCentroids(newCentroids, values);
+ else
+ {
+
+ return newCentroids;
+ }
+}
diff --git a/kopete/plugins/statistics/statisticscontact.h b/kopete/plugins/statistics/statisticscontact.h
new file mode 100644
index 00000000..217000db
--- /dev/null
+++ b/kopete/plugins/statistics/statisticscontact.h
@@ -0,0 +1,260 @@
+/*
+ statisticscontact.h
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 STATISTICSCONTACT_H
+#define STATISTICSCONTACT_H
+
+#include "kopeteonlinestatus.h"
+#include "kopetemessage.h"
+
+class StatisticsDB;
+class QDateTime;
+
+class StatisticsContact
+{
+
+public:
+ StatisticsContact(Kopete::MetaContact *mc, StatisticsDB *db);
+
+ /**
+ * We save all stats to the database (commonstats)
+ */
+ ~StatisticsContact();
+
+
+ /*
+ * Access method
+ */
+
+ /** \brief Access method
+ * \return m_db
+ */
+ StatisticsDB *db() { return m_db; }
+
+ /** \brief Access method
+ * \return m_metaContact
+ */
+ Kopete::MetaContact *metaContact() { return m_metaContact; }
+
+ /** \brief Access method
+ * \return m_statisticsContactId
+ */
+ QString statisticsContactId() { return m_statisticsContactId; }
+
+ /** \brief Access method
+ * \return m_oldStatus
+ */
+ Kopete::OnlineStatus::StatusType oldStatus() { return m_oldStatus; }
+
+ /** \brief Access method
+ * \return m_oldStatusDateTime
+ */
+ QDateTime oldStatusDateTime() { return m_oldStatusDateTime; }
+
+ /** \brief Access method
+ * \return m_messageLength
+ */
+ int messageLength() { return m_messageLength; }
+ /** \brief Access method
+ * \return m_timeBetweenTwoMessages
+ */
+ int timeBetweenTwoMessages() { return m_timeBetweenTwoMessages; }
+ /**
+ * \brief Access method
+ * \return m_lastTalk
+ */
+ QDateTime lastTalk() { return m_lastTalk; }
+ /**
+ * \brief Access method
+ * \return m_lastPresent
+ */
+ QDateTime lastPresent() { return m_lastPresent; }
+ /**
+ * \brief sets \p m_isChatWindowOpen to true
+ */
+ void setIsChatWindowOpen(bool c) { m_isChatWindowOpen = c; }
+
+
+
+ /*
+ * Method performing some useful actions
+ */
+ /**
+ * \brief update the events database with the new statuss
+ */
+ void onlineStatusChanged(Kopete::OnlineStatus::StatusType status);
+
+ /**
+ * \brief update the average time between to messages for this contact
+ * Should be called when a new message is received by this contact
+ */
+ void newMessageReceived(Kopete::Message& m);
+
+ /**
+ * \returns true if contact was status at dt, false else.
+ */
+ bool wasStatus(QDateTime dt, Kopete::OnlineStatus::StatusType status);
+
+ /**
+ * \returns the status of the contact at dt. Return false if dt is invalid.
+ */
+ QString statusAt(QDateTime dt);
+
+ /**
+ * \returns the main (most used) status of the contact at date (not time) dt. return false if dt is invalid.
+ */
+ QString mainStatusDate(const QDate& date);
+ /*
+ * Prevision methods
+ */
+ /**
+// * \brief Give informations on when the next event will occur
+// *
+// * \param status the status to be checked.
+// * \retval nextEventDateTime the next event average prevision datetime.
+// */
+// QDateTime nextEvent(const Kopete::OnlineStatus::StatusType& status);
+//
+// /**
+// * \brief Convenience method for nextEvent with Offline status
+// */
+// QDateTime nextOfflineEvent();
+//
+// /**
+// * \brief Convenience method for nextEvent with Online status
+// */
+// QDateTime nextOnlineEvent();
+
+
+ /**
+ * \brief computes the main "status" events for the contact
+ */
+ QValueList<QTime> mainEvents(const Kopete::OnlineStatus::StatusType& status);
+ /// \brief used by mainEvents()
+ QValueList<int> computeCentroids(const QValueList<int>& centroids, const QValueList<int>& values);
+
+ /**
+ * \brief adds contact to "contacts" database and generates m_statisticsContactId if needed
+ */
+ void contactAdded( Kopete::Contact *c );
+
+ /**
+ * \brief removes contact from "contacts" database
+ */
+ void contactRemoved( Kopete::Contact *c );
+
+ /**
+ * \brief removes all records from database that are related to this class and clears m_statisticsContactId
+ */
+ void removeFromDB();
+
+private:
+ /**
+ * \brief initializes this object and sets m_statisticsContactId
+ *
+ */
+ void initialize(Kopete::Contact *c);
+
+ /**
+ * \brief Checks if the value name exists in "commonstats" table, if not, add the row.
+ *
+ * \param name the name of the entry to be checked
+ * \param statVar1 retrieve this var from the database. If it doesn't exists, get it from \p defaultValue1
+ * \param statVar2 retrieve this var from the database. If it doesn't exists, get it from \p defaultValue2
+ * \param defaultValue1 defaultValue for \p statVar1
+ * \param defaultValue2 defaultValue for \p statVar2
+ * \retval statvar1
+ * \retval statvar2
+ */
+ void commonStatsCheck(const QString name, QString& statVar1, QString& statVar2, const QString defaultValue1 = "", const QString defaultValue2 = "");
+
+ /**
+ * @brief Same as commonStatsCheck for integers.
+ * Provided for convenience
+ */
+ void commonStatsCheck(const QString name, int& statVar1, int& statVar2, const int defaultValue1 = 0, const int defaultValue2 = -1);
+
+ /**
+ * @brief Save a value in the "commonstats" table
+ * \param name the name of the entry to be saved
+ * \param statVar1 what we are going to store in the first column for this entry
+ * \param statVar2 the second stat we can save in this table for this entry
+ * \param statVarChanged if this param is true, we save. Else, we don't. Spare some disk usage.
+ */
+ void commonStatsSave(const QString name, const QString statVar1, const QString statVar2, const bool statVarChanged);
+
+ /**
+ * Kopete::MetaContact linked to this StatisticsContact
+ * Each StatisticsContact object _has_ to be linked to a metaContact
+ */
+ Kopete::MetaContact *m_metaContact;
+
+ /**
+ * Required to be able to write to the database
+ */
+ StatisticsDB *m_db;
+
+ /**
+ * The interest of statistics contact is to manage the changes of status
+ * in order to correctly update the database. That's why here we keep the oldStatus
+ */
+ Kopete::OnlineStatus::StatusType m_oldStatus;
+ /// We keep the old status datetime here
+ QDateTime m_oldStatusDateTime;
+
+ /**
+ * Average time this user takes between two of his messages
+ * It may be used to compute a "speed" or "availability" for this contact
+ */
+ int m_timeBetweenTwoMessages;
+ bool m_timeBetweenTwoMessagesChanged;
+ /// Date at which the last message was received.
+ /// Used to compute m_timeBetweenTwoMessages
+ QDateTime m_lastMessageReceived;
+ /// This is the ponderation corresponding to m_timeBetweenTwoMessagesOn
+ int m_timeBetweenTwoMessagesOn;
+ /// We don't count time if a chatwindow isn't open
+ bool m_isChatWindowOpen;
+
+ /**
+ * Average length of contact's messages
+ */
+ int m_messageLength;
+ bool m_messageLengthChanged;
+ /// This is the ponderation corresponding to m_messageLength
+ int m_messageLengthOn;
+
+ /**
+ * Last time user talked with this contact
+ */
+ QDateTime m_lastTalk;
+ bool m_lastTalkChanged;
+
+ /**
+ * Last time user was present (=online or away)
+ */
+ QDateTime m_lastPresent;
+ bool m_lastPresentChanged;
+
+ /**
+ * Unique id that identifies StatisticsContact
+ * It's also identifier for database records
+ */
+ QString m_statisticsContactId;
+};
+
+
+#endif
diff --git a/kopete/plugins/statistics/statisticsdb.cpp b/kopete/plugins/statistics/statisticsdb.cpp
new file mode 100644
index 00000000..450c4371
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsdb.cpp
@@ -0,0 +1,208 @@
+/*
+ statisticsdb.cpp
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 <qfile.h>
+
+#include "sqlite/sqlite3.h"
+
+#include <kgenericfactory.h>
+#include <kaboutdata.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kdeversion.h>
+
+#include "statisticsdb.h"
+
+#include <unistd.h>
+#include <time.h>
+
+StatisticsDB::StatisticsDB()
+{
+ QCString path = (::locateLocal("appdata", "kopete_statistics-0.1.db")).latin1();
+ kdDebug() << "statistics: DB path:" << path << endl;
+
+ // Open database file and check for correctness
+ bool failOpen = true;
+ QFile file( path );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ QString format;
+ file.readLine( format, 50 );
+ if ( !format.startsWith( "SQLite format 3" ) )
+ {
+ kdWarning() << "[statistics] Database versions incompatible. Removing and rebuilding database.\n";
+ }
+ else if ( sqlite3_open( path, &m_db ) != SQLITE_OK )
+ {
+ kdWarning() << "[statistics] Database file corrupt. Removing and rebuilding database.\n";
+ sqlite3_close( m_db );
+ }
+ else
+ failOpen = false;
+ }
+
+ if ( failOpen )
+ {
+ // Remove old db file; create new
+ QFile::remove( path );
+ sqlite3_open( path, &m_db );
+ }
+
+ kdDebug() << "[Statistics] Contructor"<< endl;
+
+ // Creates the tables if they do not exist.
+ QStringList result = query("SELECT name FROM sqlite_master WHERE type='table'");
+
+ if (!result.contains("contacts"))
+ {
+ query(QString("CREATE TABLE contacts "
+ "(id INTEGER PRIMARY KEY,"
+ "statisticid TEXT,"
+ "contactid TEXT"
+ ");"));
+ }
+
+ if (!result.contains("contactstatus"))
+ {
+ kdDebug() << "[Statistics] Database empty"<< endl;
+ query(QString("CREATE TABLE contactstatus "
+ "(id INTEGER PRIMARY KEY,"
+ "metacontactid TEXT,"
+ "status TEXT,"
+ "datetimebegin INTEGER,"
+ "datetimeend INTEGER"
+ ");"));
+ }
+
+ if (!result.contains("commonstats"))
+ {
+ // To store things like the contact answer time etc.
+ query(QString("CREATE TABLE commonstats"
+ " (id INTEGER PRIMARY KEY,"
+ "metacontactid TEXT,"
+ "statname TEXT," // for instance, answertime, lastmessage, messagelength ...
+ "statvalue1 TEXT,"
+ "statvalue2 TEXT"
+ ");"));
+ }
+
+ /// @fixme This is not used anywhere
+ if (!result.contains("statsgroup"))
+ {
+ query(QString("CREATE TABLE statsgroup"
+ "(id INTEGER PRIMARY KEY,"
+ "datetimebegin INTEGER,"
+ "datetimeend INTEGER,"
+ "caption TEXT);"));
+ }
+
+}
+
+StatisticsDB::~StatisticsDB()
+{
+ sqlite3_close(m_db);
+}
+
+ /**
+ * Executes a SQL query on the already opened database
+ * @param statement SQL program to execute. Only one SQL statement is allowed.
+ * @param debug Set to true for verbose debug output.
+ * @retval names Will contain all column names, set to NULL if not used.
+ * @return The queried data, or QStringList() on error.
+ */
+ QStringList StatisticsDB::query( const QString& statement, QStringList* const names, bool debug )
+ {
+
+ if ( debug )
+ kdDebug() << "query-start: " << statement << endl;
+
+ clock_t start = clock();
+
+ if ( !m_db )
+ {
+ kdError() << k_funcinfo << "[CollectionDB] SQLite pointer == NULL.\n";
+ return QStringList();
+ }
+
+ int error;
+ QStringList values;
+ const char* tail;
+ sqlite3_stmt* stmt;
+
+ //compile SQL program to virtual machine
+ error = sqlite3_prepare( m_db, statement.utf8(), statement.length(), &stmt, &tail );
+
+ if ( error != SQLITE_OK )
+ {
+ kdError() << k_funcinfo << "[CollectionDB] sqlite3_compile error:" << endl;
+ kdError() << sqlite3_errmsg( m_db ) << endl;
+ kdError() << "on query: " << statement << endl;
+
+ return QStringList();
+ }
+
+ int busyCnt = 0;
+ int number = sqlite3_column_count( stmt );
+ //execute virtual machine by iterating over rows
+ while ( true )
+ {
+ error = sqlite3_step( stmt );
+
+ if ( error == SQLITE_BUSY )
+ {
+ if ( busyCnt++ > 20 ) {
+ kdError() << "[CollectionDB] Busy-counter has reached maximum. Aborting this sql statement!\n";
+ break;
+ }
+ ::usleep( 100000 ); // Sleep 100 msec
+ kdDebug() << "[CollectionDB] sqlite3_step: BUSY counter: " << busyCnt << endl;
+ }
+ if ( error == SQLITE_MISUSE )
+ kdDebug() << "[CollectionDB] sqlite3_step: MISUSE" << endl;
+ if ( error == SQLITE_DONE || error == SQLITE_ERROR )
+ break;
+
+ //iterate over columns
+ for ( int i = 0; i < number; i++ )
+ {
+ values << QString::fromUtf8( (const char*) sqlite3_column_text( stmt, i ) );
+ if ( names ) *names << QString( sqlite3_column_name( stmt, i ) );
+ }
+ }
+ //deallocate vm ressources
+ sqlite3_finalize( stmt );
+
+ if ( error != SQLITE_DONE )
+ {
+ kdError() << k_funcinfo << "sqlite_step error.\n";
+ kdError() << sqlite3_errmsg( m_db ) << endl;
+ kdError() << "on query: " << statement << endl;
+
+ return QStringList();
+ }
+
+ if ( debug )
+ {
+ clock_t finish = clock();
+ const double duration = (double) (finish - start) / CLOCKS_PER_SEC;
+ kdDebug() << "[CollectionDB] SQL-query (" << duration << "s): " << statement << endl;
+ }
+
+
+ return values;
+}
diff --git a/kopete/plugins/statistics/statisticsdb.h b/kopete/plugins/statistics/statisticsdb.h
new file mode 100644
index 00000000..130b1d0e
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsdb.h
@@ -0,0 +1,36 @@
+/*
+ statisticsdb.h
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 _STATISTICSDB_H_H
+#define _STATISTICSDB_H_H 1
+
+typedef struct sqlite3;
+
+class StatisticsDB
+{
+
+public:
+ StatisticsDB();
+ ~StatisticsDB();
+ //sql helper methods
+ QStringList query( const QString& statement, QStringList* const names = 0, bool debug = false );
+ QString escapeString( QString string );
+private:
+ sqlite3 *m_db;
+};
+
+#endif
+
diff --git a/kopete/plugins/statistics/statisticsdcopiface.h b/kopete/plugins/statistics/statisticsdcopiface.h
new file mode 100644
index 00000000..b45a2c95
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsdcopiface.h
@@ -0,0 +1,74 @@
+/*
+ statisticsdcopiface.h
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 STATISTICSDCOP_H
+#define STATISTICSDCOP_H
+
+#include <dcopobject.h>
+
+
+class StatisticsDCOPIface : virtual public DCOPObject
+{
+ K_DCOP
+public:
+
+k_dcop:
+ /**
+ * Shows the statistics dialog for contact which has KABC id \var contactId
+ */
+ virtual void dcopStatisticsDialog(QString contactId) = 0;
+ /**
+ * \returns true if contact was online at time timeStamp, false else. Returns false if contact does not exist.
+ */
+ virtual bool dcopWasOnline(QString id, int timeStamp) = 0;
+ /**
+ * \returns true if contact was online at dt, false else. Returns false if contact does not exist or if date is invalid.
+ */
+ virtual bool dcopWasOnline(QString id, QString datetime) = 0;
+
+ /**
+ * \returns true if contact was away at time timeStamp, false else. Returns false if contact does not exist.
+ */
+ virtual bool dcopWasAway(QString id, int timeStamp) = 0;
+ /**
+ * \returns true if contact was away at dt, false else. Returns false if contact does not exist or if date is invalid.
+ */
+ virtual bool dcopWasAway(QString id, QString datetime) = 0;
+
+ /**
+ * \returns true if contact was offline at time timeStamp, false else. Returns false if contact does not exist.
+ */
+ virtual bool dcopWasOffline(QString id, int timeStamp) = 0;
+ /**
+ * \returns true if contact was offline at dt, false else. Returns false if contact does not exist or if date is invalid.
+ */
+ virtual bool dcopWasOffline(QString id, QString datetime) = 0;
+
+ /**
+ * \returns return the status of the contact at datetime.
+ */
+ virtual QString dcopStatus(QString id, QString datetime) = 0;
+ /**
+ * \returns return the status of the contact at timeStamp.
+ */
+ virtual QString dcopStatus(QString id, int timeStamp) = 0;
+ /**
+ * \returns the main status (most used status) of the contact id at date (not time) timeStamp. Will take the day where timeStamp is.
+ */
+ virtual QString dcopMainStatus(QString id, int timeStamp) = 0;
+};
+
+#endif // STATISTICSDCOP_H
diff --git a/kopete/plugins/statistics/statisticsdialog.cpp b/kopete/plugins/statistics/statisticsdialog.cpp
new file mode 100644
index 00000000..485eb7ad
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsdialog.cpp
@@ -0,0 +1,543 @@
+/*
+ statisticsdialog.cpp - Kopete History Dialog
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 <qtabwidget.h>
+#include <qwidget.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtextedit.h>
+#include <qcombobox.h>
+#include <qstring.h>
+
+#include "kdialogbase.h"
+#include "klocale.h"
+#include "klistview.h"
+#include "khtml_part.h"
+#include "kstandarddirs.h"
+#include "kdatepicker.h"
+#include "ktimewidget.h"
+
+#include "kopetemetacontact.h"
+#include "kopeteonlinestatus.h"
+
+#include "statisticsdialog.h"
+#include "statisticscontact.h"
+#include "statisticswidget.h"
+#include "statisticsplugin.h"
+#include "statisticsdb.h"
+
+StatisticsDialog::StatisticsDialog(StatisticsContact *contact, StatisticsDB *db, QWidget* parent,
+ const char* name) : KDialogBase(parent, name, false,
+ i18n("Statistics for %1").arg(contact->metaContact()->displayName()), Close, Close), m_db(db), m_contact(contact)
+{
+ mainWidget = new StatisticsWidget(this);
+ setMainWidget(mainWidget);
+
+ setMinimumWidth(640);
+ setMinimumHeight(400);
+ adjustSize();
+
+ QHBox *hbox = new QHBox(this);
+
+ generalHTMLPart = new KHTMLPart(hbox);
+ generalHTMLPart->setOnlyLocalReferences(true);
+ connect ( generalHTMLPart->browserExtension(), SIGNAL( openURLRequestDelayed( const KURL &, const KParts::URLArgs & ) ),
+ this, SLOT( slotOpenURLRequest( const KURL &, const KParts::URLArgs & ) ) );
+
+
+ mainWidget->tabWidget->insertTab(hbox, i18n("General"), 0);
+ mainWidget->tabWidget->setCurrentPage(0);
+
+ mainWidget->timePicker->setTime(QTime::currentTime());
+ mainWidget->datePicker->setDate(QDate::currentDate());
+ connect(mainWidget->askButton, SIGNAL(clicked()), this, SLOT(slotAskButtonClicked()));
+
+ setFocus();
+ setEscapeButton(Close);
+
+ generatePageGeneral();
+}
+
+// We only generate pages when the user clicks on a link
+void StatisticsDialog::slotOpenURLRequest(const KURL& url, const KParts::URLArgs&)
+{
+ if (url.protocol() == "main")
+ {
+ generatePageGeneral();
+ }
+ else if (url.protocol() == "dayofweek")
+ {
+ generatePageForDay(url.path().toInt());
+ }
+ else if (url.protocol() == "monthofyear")
+ {
+ generatePageForMonth(url.path().toInt());
+ }
+}
+
+/*void StatisticsDialog::parseTemplate(QString Template)
+{
+ QString fileString = ::locate("appdata", "kopete_statistics.template.html");
+ QString templateString;
+ QFile file(file);
+ if (file.open(IO_ReadOnly))
+ {
+ QTextStream stream(&file);
+ templateString = stream.read();
+ file.close();
+ }
+ // The template is loaded in templateString now.
+ templateString.strReplace(
+}*/
+
+void StatisticsDialog::generatePageForMonth(const int monthOfYear)
+{
+ QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend "
+ "FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;").arg(m_contact->statisticsContactId()));
+
+ QStringList values2;
+
+ for (uint i=0; i<values.count(); i+=3)
+ {
+ QDateTime dateTimeBegin;
+ dateTimeBegin.setTime_t(values[i+1].toInt());
+ /// @todo Same as for Day, check if second datetime is on the same month
+ if (dateTimeBegin.date().month() == monthOfYear)
+ {
+ values2.push_back(values[i]);
+ values2.push_back(values[i+1]);
+ values2.push_back(values[i+2]);
+ }
+ }
+ generatePageFromQStringList(values2, QDate::longMonthName(monthOfYear));
+}
+
+void StatisticsDialog::generatePageForDay(const int dayOfWeek)
+{
+ QStringList values = m_db->query(QString("SELECT status, datetimebegin, datetimeend "
+ "FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;").arg(m_contact->statisticsContactId()));
+
+ QStringList values2;
+
+ for (uint i=0; i<values.count(); i+=3)
+ {
+ QDateTime dateTimeBegin;
+ dateTimeBegin.setTime_t(values[i+1].toInt());
+ QDateTime dateTimeEnd;
+ dateTimeEnd.setTime_t(values[i+2].toInt());
+ if (dateTimeBegin.date().dayOfWeek() == dayOfWeek)
+ {
+ if (dateTimeEnd.date().dayOfWeek() != dayOfWeek)
+ // Day of week is not the same at beginning and at end of the event
+ {
+ values2.push_back(values[i]);
+ values2.push_back(values[i+1]);
+
+ // datetime from value[i+1]
+
+ dateTimeBegin = QDateTime(dateTimeBegin.date(), QTime(0, 0, 0));
+ dateTimeBegin.addSecs(dateTimeBegin.time().secsTo(QTime(23, 59, 59)));
+ values2.push_back(QString::number(dateTimeBegin.toTime_t()));
+ }
+ else
+ {
+ values2.push_back(values[i]);
+ values2.push_back(values[i+1]);
+ values2.push_back(values[i+2]);
+ }
+ }
+ }
+ generatePageFromQStringList(values2, QDate::longDayName(dayOfWeek));
+
+}
+
+/// @todo chart problem at midnight.
+void StatisticsDialog::generatePageFromQStringList(QStringList values, const QString & subTitle)
+{
+ generalHTMLPart->begin();
+ generalHTMLPart->write(QString("<html><head><style>.bar { margin:0px;} "
+ "body"
+ "{"
+ "font-size:11px"
+ "}"
+ ".chart" // Style for the charts
+ "{ height:100px;"
+ "border-left:1px solid #999;"
+ "border-bottom:1px solid #999;"
+ "vertical-align:bottom;"
+ "}"
+ ".statgroup" // Style for groups of similar statistics
+ "{ margin-bottom:10px;"
+ "background-color:white;"
+ "border-left: 5px solid #369;"
+ "border-top: 1px dashed #999;"
+ "border-bottom: 1px dashed #999;"
+ "margin-left: 10px;"
+ "margin-right: 5px;"
+ "padding:3px 3px 3px 10px;}"
+ "</style></head><body>" +
+ i18n("<h1>Statistics for %1</h1>").arg(m_contact->metaContact()->displayName()) +
+ "<h3>%1</h3><hr>").arg(subTitle));
+
+ generalHTMLPart->write(i18n("<div class=\"statgroup\"><b><a href=\"main:generalinfo\" title=\"General summary view\">General</a></b><br>"
+ "<span title=\"Select the a day or a month to view the stat for\"><b>Days: </b>"
+ "<a href=\"dayofweek:1\">Monday</a>&nbsp;"
+ "<a href=\"dayofweek:2\">Tuesday</a>&nbsp;"
+ "<a href=\"dayofweek:3\">Wednesday</a>&nbsp;"
+ "<a href=\"dayofweek:4\">Thursday</a>&nbsp;"
+ "<a href=\"dayofweek:5\">Friday</a>&nbsp;"
+ "<a href=\"dayofweek:6\">Saturday</a>&nbsp;"
+ "<a href=\"dayofweek:7\">Sunday</a><br>"
+ "<b>Months: </b>"
+ "<a href=\"monthofyear:1\">January</a>&nbsp;"
+ "<a href=\"monthofyear:2\">February</a>&nbsp;"
+ "<a href=\"monthofyear:3\">March</a>&nbsp;"
+ "<a href=\"monthofyear:4\">April</a>&nbsp;"
+ "<a href=\"monthofyear:5\">May</a>&nbsp;"
+ "<a href=\"monthofyear:6\">June</a>&nbsp;"
+ "<a href=\"monthofyear:7\">July</a>&nbsp;"
+ "<a href=\"monthofyear:8\">August</a>&nbsp;"
+ "<a href=\"monthofyear:9\">September</a>&nbsp;"
+ "<a href=\"monthofyear:10\">October</a>&nbsp;"
+ "<a href=\"monthofyear:11\">November</a>&nbsp;"
+ "<a href=\"monthofyear:12\">December</a>&nbsp;"
+ "</span></div><br>"));
+
+// mainWidget->listView->addColumn(i18n("Status"));
+// mainWidget->listView->addColumn(i18n("Start Date"));
+// mainWidget->listView->addColumn(i18n("End Date"));
+// mainWidget->listView->addColumn(i18n("Start Date"));
+// mainWidget->listView->addColumn(i18n("End Date"));
+
+ QString todayString;
+ todayString.append(i18n("<div class=\"statgroup\" title=\"Contact status history for today\"><h2>Today</h2><table width=\"100%\"><tr><td>Status</td><td>From</td><td>To</td></tr>"));
+
+ bool today;
+
+ int totalTime = 0; // this is in seconds
+ int totalAwayTime = 0; // this is in seconds
+ int totalOnlineTime = 0; // this is in seconds
+ int totalOfflineTime = 0; // idem
+
+ int hours[24]; // in seconds, too
+ int iMaxHours = 0;
+ int hoursOnline[24]; // this is in seconds
+ int iMaxHoursOnline = 0;
+ int hoursAway[24]; // this is in seconds
+ int iMaxHoursAway = 0;
+ int hoursOffline[24]; // this is in seconds. Hours where we are sure contact is offline
+ int iMaxHoursOffline = 0;
+
+ for (uint i=0; i<24; i++)
+ {
+ hours[i] = 0;
+ hoursOnline[i] = 0;
+ hoursAway[i] = 0;
+ hoursOffline[i] = 0;
+ }
+
+ for (uint i=0; i<values.count(); i+=3 /* because SELECT 3 columns */)
+ {
+ /* Here we try to interpret one database entry...
+ What's important here, is to not count two times the same hour for instance
+ This is why there are some if in all this stuff ;-)
+ */
+
+
+ // it is the STARTDATE from the database
+ QDateTime dateTime1;
+ dateTime1.setTime_t(values[i+1].toInt());
+ // it is the ENDDATE from the database
+ QDateTime dateTime2;
+ dateTime2.setTime_t(values[i+2].toInt());
+
+ if (dateTime1.date() == QDate::currentDate() || dateTime2.date() == QDate::currentDate())
+ today = true;
+ else today = false;
+
+ totalTime += dateTime1.secsTo(dateTime2);
+
+ if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
+ totalOnlineTime += dateTime1.secsTo(dateTime2);
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
+ totalAwayTime += dateTime1.secsTo(dateTime2);
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
+ totalOfflineTime += dateTime1.secsTo(dateTime2);
+
+
+ /*
+ * To build the chart/hours
+ */
+
+ // Number of hours between dateTime1 and dateTime2
+ int nbHours = (int)(dateTime1.secsTo(dateTime2)/3600.0);
+
+ uint tempHour =
+ dateTime1.time().hour() == dateTime2.time().hour()
+ ? dateTime1.secsTo(dateTime2) // (*)
+ : 3600 - dateTime1.time().minute()*60 - dateTime1.time().second();
+ hours[dateTime1.time().hour()] += tempHour;
+
+ if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
+ hoursOnline[dateTime1.time().hour()] += tempHour;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
+ hoursAway[dateTime1.time().hour()] += tempHour;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
+ hoursOffline[dateTime1.time().hour()] += tempHour;
+
+ for (int j= dateTime1.time().hour()+1; j < dateTime1.time().hour() + nbHours - 1; j++)
+ {
+ hours[j%24] += 3600;
+ if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
+ hoursOnline[j%24] += 3600;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
+ hoursAway[j%24] += 3600;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
+ hoursOffline[j%24] += 3600;
+ }
+
+
+ if (dateTime1.time().hour() != dateTime2.time().hour())
+ // We dont want to count this if the hour from dateTime2 is the same than the one from dateTime1
+ // since it as already been taken in account in the (*) instruction
+ {
+ tempHour = dateTime2.time().minute()*60 +dateTime2.time().second();
+ hours[dateTime2.time().hour()] += tempHour;
+
+ if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
+ hoursOnline[dateTime2.time().hour()] += tempHour;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
+ hoursAway[dateTime2.time().hour()] += tempHour;
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
+ hoursOffline[dateTime2.time().hour()] += tempHour;
+
+
+ }
+
+
+
+ QString color;
+ if (today)
+ {
+ QString status;
+ if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Online)
+ {
+ color="blue";
+ status = i18n("Online");
+ }
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Away)
+ {
+ color="navy";
+ status = i18n("Away");
+ }
+ else if (Kopete::OnlineStatus::statusStringToType(values[i]) == Kopete::OnlineStatus::Offline)
+ {
+ color="gray";
+ status = i18n("Offline");
+ }
+ else color="white";
+
+ todayString.append(QString("<tr style=\"color:%1\"><td>%2</td><td>%3</td><td>%4</td></tr>").arg(color, status, dateTime1.time().toString(), dateTime2.time().toString()));
+
+ }
+
+ // We add a listview item to the log list
+ // QDateTime listViewDT1, listViewDT2;
+ // listViewDT1.setTime_t(values[i+1].toInt());
+ // listViewDT2.setTime_t(values[i+2].toInt());
+ // new KListViewItem(mainWidget->listView, values[i], values[i+1], values[i+2], listViewDT1.toString(), listViewDT2.toString());
+ }
+
+
+ todayString.append("</table></div>");
+
+ // Get the max from the hours*
+ for (uint i=1; i<24; i++)
+ {
+ if (hours[iMaxHours] < hours[i])
+ iMaxHours = i;
+ if (hoursOnline[iMaxHoursOnline] < hoursOnline[i])
+ iMaxHoursOnline = i;
+ if (hoursOffline[iMaxHoursOffline] < hoursOffline[i])
+ iMaxHoursOffline = i;
+ if (hoursAway[iMaxHoursAway] < hoursAway[i])
+ iMaxHoursAway = i;
+ }
+
+ //
+
+ /*
+ * Here we really generate the page
+ */
+ // Some "total times"
+ generalHTMLPart->write(i18n("<div class=\"statgroup\">"));
+ generalHTMLPart->write(i18n("<b title=\"The total time I have been able to see %1 status\">"
+ "Total seen time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalTime)));
+ generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 online\">"
+ "Total online time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalOnlineTime)));
+ generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 away\">Total busy time :</b> %2 hour(s)<br>").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalAwayTime)));
+ generalHTMLPart->write(i18n("<b title=\"The total time I have seen %1 offline\">Total offline time :</b> %2 hour(s)").arg(m_contact->metaContact()->displayName()).arg(stringFromSeconds(totalOfflineTime)));
+ generalHTMLPart->write(QString("</div>"));
+
+ if (subTitle == i18n("General information"))
+ /*
+ * General stats that should not be shown on "day" or "month" pages
+ */
+ {
+ generalHTMLPart->write(QString("<div class=\"statgroup\">"));
+ generalHTMLPart->write(i18n("<b>Average message length :</b> %1 characters<br>").arg(m_contact->messageLength()));
+ generalHTMLPart->write(i18n("<b>Time between two messages : </b> %1 second(s)").arg(m_contact->timeBetweenTwoMessages()));
+ generalHTMLPart->write(QString("</div>"));
+
+ generalHTMLPart->write(QString("<div class=\"statgroup\">"));
+ generalHTMLPart->write(i18n("<b title=\"The last time you talked with %1\">Last talk :</b> %2<br>").arg(m_contact->metaContact()->displayName()).arg(KGlobal::locale()->formatDateTime(m_contact->lastTalk())));
+ generalHTMLPart->write(i18n("<b title=\"The last time I have seen %1 online or away\">Last time contact was present :</b> %2").arg(m_contact->metaContact()->displayName()).arg(KGlobal::locale()->formatDateTime(m_contact->lastPresent())));
+ generalHTMLPart->write(QString("</div>"));
+
+ //generalHTMLPart->write(QString("<div class=\"statgroup\">"));
+ //generalHTMLPart->write(i18n("<b title=\"%1 uses to set his status online at these hours (EXPERIMENTAL)\">Main online events :</b><br>").arg(m_contact->metaContact()->displayName()));
+ //QValueList<QTime> mainEvents = m_contact->mainEvents(Kopete::OnlineStatus::Online);
+ //for (uint i=0; i<mainEvents.count(); i++)
+ //generalHTMLPart->write(QString("%1<br>").arg(mainEvents[i].toString()));
+ //generalHTMLPart->write(QString("</div>"));
+
+ generalHTMLPart->write("<div title=\"" +i18n("Current status") + "\" class=\"statgroup\">");
+ generalHTMLPart->write(i18n("Is <b>%1</b> since <b>%2</b>").arg(
+ Kopete::OnlineStatus(m_contact->oldStatus()).description(),
+ KGlobal::locale()->formatDateTime(m_contact->oldStatusDateTime())));
+ generalHTMLPart->write(QString("</div>"));
+ }
+
+ /*
+ * Chart which show the hours where plugin has seen this contact online
+ */
+ generalHTMLPart->write(QString("<div class=\"statgroup\">"));
+ generalHTMLPart->write(QString("<table width=\"100%\"><tr><td colspan=\"3\">") + i18n("When have I seen this contact ?") + QString("</td></tr>"));
+ generalHTMLPart->write(QString("<tr><td height=\"200\" valign=\"bottom\" colspan=\"3\" class=\"chart\">"));
+
+ QString chartString;
+ QString colorPath = ::locate("appdata", "pics/statistics/black.png");
+ for (uint i=0; i<24; i++)
+ {
+
+ int hrWidth = qRound((double)hours[i]/(double)hours[iMaxHours]*100.);
+ chartString += QString("<img class=\"margin:0px;\" height=\"")
+ +(totalTime ? QString::number(hrWidth) : QString::number(0))
+ +QString("\" src=\"file://")
+ +colorPath
+ +"\" width=\"4%\" title=\""
+ +i18n("Between %1:00 and %2:00, I was able to see %3 status %4% of the hour.").arg(i).arg((i+1)%24).arg(m_contact->metaContact()->displayName()).arg(hrWidth)
+ +QString("\">");
+ }
+ generalHTMLPart->write(chartString);
+ generalHTMLPart->write(QString("</td></tr>"));
+
+
+
+ generalHTMLPart->write(QString( "<tr>"
+ "<td>")+i18n("Online time")+QString("</td><td>")+i18n("Away time")+QString("</td><td>")+i18n("Offline time")+QString("</td>"
+ "</tr>"
+ "<td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
+
+
+ generalHTMLPart->write(generateHTMLChart(hoursOnline, hoursAway, hoursOffline, i18n("online"), "blue"));
+ generalHTMLPart->write(QString("</td><td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
+ generalHTMLPart->write(generateHTMLChart(hoursAway, hoursOnline, hoursOffline, i18n("away"), "navy"));
+ generalHTMLPart->write(QString("</td><td valign=\"bottom\" width=\"33%\" class=\"chart\">"));
+ generalHTMLPart->write(generateHTMLChart(hoursOffline, hoursAway, hoursOnline, i18n("offline"), "gray"));
+ generalHTMLPart->write(QString("</td></tr></table></div>"));
+
+ if (subTitle == i18n("General information"))
+ /* On main page, show the different status of the contact today
+ */
+ {
+ generalHTMLPart->write(QString(todayString));
+ }
+ generalHTMLPart->write(QString("</body></html>"));
+
+ generalHTMLPart->end();
+
+}
+
+void StatisticsDialog::generatePageGeneral()
+{
+ QStringList values;
+ values = m_db->query(QString("SELECT status, datetimebegin, datetimeend "
+ "FROM contactstatus WHERE metacontactid LIKE '%1' ORDER BY datetimebegin;")
+ .arg(m_contact->statisticsContactId()));
+ generatePageFromQStringList(values, i18n("General information"));
+}
+
+QString StatisticsDialog::generateHTMLChart(const int *hours, const int *hours2, const int *hours3, const QString & caption, const QString & color)
+{
+ QString chartString;
+
+ QString colorPath = ::locate("appdata", "pics/statistics/"+color+".png");
+
+
+ for (uint i=0; i<24; i++)
+ {
+ int totalTime = hours[i] + hours2[i] + hours3[i];
+
+ int hrWidth = qRound((double)hours[i]/(double)totalTime*100.);
+ chartString += QString("<img class=\"margin:0px;\" height=\"")
+ +(totalTime ? QString::number(hrWidth) : QString::number(0))
+ +QString("\" src=\"file://")
+ +colorPath
+ +"\" width=\"4%\" title=\""+
+ i18n("Between %1:00 and %2:00, I have seen %3 %4% %5.").
+ arg(i).
+ arg((i+1) % 24).
+ arg(m_contact->metaContact()->displayName()).
+ arg(hrWidth).
+ arg(caption)
+ +".\">";
+ }
+ return chartString;
+}
+
+QString StatisticsDialog::stringFromSeconds(const int seconds)
+{
+ int h, m, s;
+ h = seconds/3600;
+ m = (seconds % 3600)/60;
+ s = (seconds % 3600) % 60;
+ return QString::number(h)+":"+QString::number(m)+":"+QString::number(s);
+}
+
+void StatisticsDialog::slotAskButtonClicked()
+{
+ if (mainWidget->questionComboBox->currentItem()==0)
+ {
+ QString text = i18n("1 is date, 2 is contact name, 3 is online status", "%1, %2 was %3")
+ .arg(KGlobal::locale()->formatDateTime(QDateTime(mainWidget->datePicker->date(), mainWidget->timePicker->time())))
+ .arg(m_contact->metaContact()->displayName())
+ .arg(m_contact->statusAt(QDateTime(mainWidget->datePicker->date(), mainWidget->timePicker->time())));
+ mainWidget->answerEdit->setText(text);
+ }
+ else if (mainWidget->questionComboBox->currentItem()==1)
+ {
+ mainWidget->answerEdit->setText(m_contact->mainStatusDate(mainWidget->datePicker->date()));
+ }
+ else if (mainWidget->questionComboBox->currentItem()==2)
+ // Next online
+ {
+
+ }
+}
+
+#include "statisticsdialog.moc"
diff --git a/kopete/plugins/statistics/statisticsdialog.h b/kopete/plugins/statistics/statisticsdialog.h
new file mode 100644
index 00000000..32a5aaaf
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsdialog.h
@@ -0,0 +1,81 @@
+/*
+ statisticsdialog.h - Kopete History Dialog
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 _STATISTICSDIALOG_H
+#define _STATISTICSDIALOG_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include "kopetemetacontact.h"
+
+class QCanvasView;
+class QCanvas;
+class QStringList;
+
+class StatisticsWidget;
+class StatisticsPlugin;
+class StatisticsDB;
+class StatisticsContact;
+
+class KHTMLPart;
+class KURL;
+namespace KParts
+{
+ class URLArgs;
+}
+
+class StatisticsDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ StatisticsDialog(StatisticsContact *contact, StatisticsDB* db, QWidget* parent=0,
+ const char* name="StatisticsDialog");
+ private:
+ QString generateHTMLChart(const int *hours, const int *hours2, const int *hours3, const QString & caption, const QString & color);
+ QString generateHTMLChartBar(int height, const QString & color, const QString & caption);
+ QString stringFromSeconds(const int seconds);
+
+ StatisticsWidget *mainWidget;
+ KHTMLPart *generalHTMLPart;
+
+ /// Database from which we get the statistics
+ StatisticsDB *m_db;
+ /// Metacontact for which we get the statistics from m_db
+ StatisticsContact *m_contact;
+
+ void generatePageFromQStringList(QStringList values, const QString & subTitle);
+
+ /// Generates the main page
+ void generatePageGeneral();
+ /**
+ * @brief Generates the page for a given day of the week.
+ * \param dayOfWeek Monday..Sunday, 0..7
+ */
+ void generatePageForDay(const int dayOfWeek);
+ void generatePageForMonth(const int monthOfYear);
+
+
+private slots:
+ /**
+ * We manage the openURLRequestDelayed signal from the generalHTMLPart->browserExtension() in order to
+ * generate requested pages on the flow.
+ */
+ void slotOpenURLRequest(const KURL& url, const KParts::URLArgs&);
+ void slotAskButtonClicked();
+
+};
+
+
+#endif // _STATISTICSDIALOG_H
diff --git a/kopete/plugins/statistics/statisticsplugin.cpp b/kopete/plugins/statistics/statisticsplugin.cpp
new file mode 100644
index 00000000..f0d190b3
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsplugin.cpp
@@ -0,0 +1,283 @@
+/*
+ statisticsplugin.cpp
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 <qfile.h>
+#include <qdict.h>
+#include <qtimer.h>
+
+#include <kgenericfactory.h>
+#include <kaboutdata.h>
+#include <kaction.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kdeversion.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopeteview.h"
+#include "kopetecontactlist.h"
+#include "kopeteuiglobal.h"
+#include "kopetemessageevent.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+
+#include "statisticscontact.h"
+#include "statisticsdialog.h"
+#include "statisticsplugin.h"
+#include "statisticsdb.h"
+
+typedef KGenericFactory<StatisticsPlugin> StatisticsPluginFactory;
+
+
+static const KAboutData aboutdata("kopete_statistics", I18N_NOOP("Statistics") , "0.1" );
+K_EXPORT_COMPONENT_FACTORY( kopete_statistics, StatisticsPluginFactory( &aboutdata ) )
+
+StatisticsPlugin::StatisticsPlugin( QObject *parent, const char *name, const QStringList &)
+ : DCOPObject("StatisticsDCOPIface"),
+ Kopete::Plugin( StatisticsPluginFactory::instance(), parent, name )
+
+
+{
+ KAction *viewMetaContactStatistics = new KAction( i18n("View &Statistics" ),
+ QString::fromLatin1( "log" ), 0, this, SLOT(slotViewStatistics()),
+ actionCollection(), "viewMetaContactStatistics" );
+ viewMetaContactStatistics->setEnabled(Kopete::ContactList::self()->selectedMetaContacts().count() == 1);
+
+ connect(Kopete::ChatSessionManager::self(),SIGNAL(chatSessionCreated(Kopete::ChatSession*)),
+ this, SLOT(slotViewCreated(Kopete::ChatSession*)));
+ connect(Kopete::ChatSessionManager::self(),SIGNAL(aboutToReceive(Kopete::Message&)),
+ this, SLOT(slotAboutToReceive(Kopete::Message&)));
+
+ connect(Kopete::ContactList::self(), SIGNAL(metaContactSelected(bool)),
+ viewMetaContactStatistics, SLOT(setEnabled(bool)));
+ connect(Kopete::ContactList::self(), SIGNAL(metaContactAdded(Kopete::MetaContact*)),
+ this, SLOT(slotMetaContactAdded(Kopete::MetaContact*)));
+ connect(Kopete::ContactList::self(), SIGNAL(metaContactRemoved(Kopete::MetaContact*)),
+ this, SLOT(slotMetaContactRemoved(Kopete::MetaContact*)));
+
+ setXMLFile("statisticsui.rc");
+
+ /* Initialization reads the database, so it could be a bit time-consuming
+ due to disk access. This should overcome the problem and makes it non-blocking. */
+ QTimer::singleShot(0, this, SLOT(slotInitialize()));
+}
+
+void StatisticsPlugin::slotInitialize()
+{
+ // Initializes the database
+ m_db = new StatisticsDB();
+
+ QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
+ QPtrListIterator<Kopete::MetaContact> it( list );
+ for (; it.current(); ++it)
+ {
+ slotMetaContactAdded(it.current());
+ }
+}
+
+StatisticsPlugin::~StatisticsPlugin()
+{
+ QMap<Kopete::MetaContact*, StatisticsContact*>::Iterator it;
+ for ( it = statisticsMetaContactMap.begin(); it != statisticsMetaContactMap.end(); ++it )
+ {
+ delete it.data();
+ }
+ delete m_db;
+}
+
+void StatisticsPlugin::slotAboutToReceive(Kopete::Message& m)
+{
+ if ( statisticsMetaContactMap.contains(m.from()->metaContact()) )
+ statisticsMetaContactMap[m.from()->metaContact()]->newMessageReceived(m);
+}
+
+void StatisticsPlugin::slotViewCreated(Kopete::ChatSession* session)
+{
+ connect(session, SIGNAL(closing(Kopete::ChatSession*)), this, SLOT(slotViewClosed(Kopete::ChatSession*)));
+}
+
+void StatisticsPlugin::slotViewClosed(Kopete::ChatSession* session)
+{
+ QPtrList<Kopete::Contact> list = session->members();
+ QPtrListIterator<Kopete::Contact> it( list );
+
+ for (; it.current(); ++it)
+ {
+ // If this contact is not in other chat sessions
+ if (!it.current()->manager() && statisticsMetaContactMap.contains(it.current()->metaContact()))
+ statisticsMetaContactMap[it.current()->metaContact()]->setIsChatWindowOpen(false);
+ }
+}
+
+void StatisticsPlugin::slotViewStatistics()
+{
+ Kopete::MetaContact *mc=Kopete::ContactList::self()->selectedMetaContacts().first();
+
+ kdDebug() << k_funcinfo << "statistics - dialog :"+ mc->displayName() << endl;
+
+ if ( mc && statisticsMetaContactMap.contains(mc) )
+ {
+ (new StatisticsDialog(statisticsMetaContactMap[mc], db()))->show();
+ }
+}
+
+void StatisticsPlugin::slotOnlineStatusChanged(Kopete::MetaContact *mc, Kopete::OnlineStatus::StatusType status)
+{
+ if ( statisticsMetaContactMap.contains(mc) )
+ statisticsMetaContactMap[mc]->onlineStatusChanged(status);
+}
+
+void StatisticsPlugin::slotMetaContactAdded(Kopete::MetaContact *mc)
+{
+ statisticsMetaContactMap[mc] = new StatisticsContact(mc, db());
+
+ QPtrList<Kopete::Contact> clist = mc->contacts();
+ Kopete::Contact *contact;
+
+ // we need to call slotContactAdded if MetaContact allready have contacts
+ for ( contact = clist.first(); contact; contact = clist.next() )
+ {
+ this->slotContactAdded(contact);
+ }
+
+ connect(mc, SIGNAL(onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType)), this,
+ SLOT(slotOnlineStatusChanged(Kopete::MetaContact*, Kopete::OnlineStatus::StatusType)));
+ connect(mc, SIGNAL(contactAdded( Kopete::Contact *)), this,
+ SLOT(slotContactAdded( Kopete::Contact *)));
+ connect(mc, SIGNAL(contactRemoved( Kopete::Contact *)), this,
+ SLOT(slotContactRemoved( Kopete::Contact *)));
+}
+
+void StatisticsPlugin::slotMetaContactRemoved(Kopete::MetaContact *mc)
+{
+ if (statisticsMetaContactMap.contains(mc))
+ {
+ StatisticsContact *sc = statisticsMetaContactMap[mc];
+ statisticsMetaContactMap.remove(mc);
+ sc->removeFromDB();
+ delete sc;
+ }
+}
+
+void StatisticsPlugin::slotContactAdded( Kopete::Contact *c)
+{
+ if (statisticsMetaContactMap.contains(c->metaContact()))
+ {
+ StatisticsContact *sc = statisticsMetaContactMap[c->metaContact()];
+ sc->contactAdded(c);
+ statisticsContactMap[c->contactId()] = sc;
+ }
+}
+
+void StatisticsPlugin::slotContactRemoved( Kopete::Contact *c)
+{
+ if (statisticsMetaContactMap.contains(c->metaContact()))
+ statisticsMetaContactMap[c->metaContact()]->contactRemoved(c);
+
+ statisticsContactMap.remove(c->contactId());
+}
+
+void StatisticsPlugin::dcopStatisticsDialog(QString id)
+{
+ kdDebug() << k_funcinfo << "statistics - DCOP dialog :" << id << endl;
+
+ if (statisticsContactMap.contains(id))
+ {
+ (new StatisticsDialog(statisticsContactMap[id], db()))->show();
+ }
+}
+
+bool StatisticsPlugin::dcopWasOnline(QString id, int timeStamp)
+{
+ QDateTime dt;
+ dt.setTime_t(timeStamp);
+ return dcopWasStatus(id, dt, Kopete::OnlineStatus::Online);
+}
+
+bool StatisticsPlugin::dcopWasOnline(QString id, QString dateTime)
+{
+ return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Online);
+}
+
+bool StatisticsPlugin::dcopWasAway(QString id, int timeStamp)
+{
+ QDateTime dt;
+ dt.setTime_t(timeStamp);
+ return dcopWasStatus(id, dt, Kopete::OnlineStatus::Away);
+}
+
+bool StatisticsPlugin::dcopWasAway(QString id, QString dateTime)
+{
+ return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Away);
+}
+
+bool StatisticsPlugin::dcopWasOffline(QString id, int timeStamp)
+{
+ QDateTime dt;
+ dt.setTime_t(timeStamp);
+ return dcopWasStatus(id, dt, Kopete::OnlineStatus::Offline);
+}
+
+bool StatisticsPlugin::dcopWasOffline(QString id, QString dateTime)
+{
+ return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Offline);
+}
+
+bool StatisticsPlugin::dcopWasStatus(QString id, QDateTime dateTime, Kopete::OnlineStatus::StatusType status)
+{
+ kdDebug() << k_funcinfo << "statistics - DCOP wasOnline :" << id << endl;
+
+ if (dateTime.isValid() && statisticsContactMap.contains(id))
+ {
+ return statisticsContactMap[id]->wasStatus(dateTime, status);
+ }
+
+ return false;
+}
+
+QString StatisticsPlugin::dcopStatus(QString id, int timeStamp)
+{
+ QDateTime dt;
+ dt.setTime_t(timeStamp);
+ return dcopStatus(id, dt.toString());
+
+}
+
+QString StatisticsPlugin::dcopStatus(QString id, QString dateTime)
+{
+ QDateTime dt = QDateTime::fromString(dateTime);
+
+ if (dt.isValid() && statisticsContactMap.contains(id))
+ {
+ return statisticsContactMap[id]->statusAt(dt);
+ }
+
+ return "";
+}
+
+QString StatisticsPlugin::dcopMainStatus(QString id, int timeStamp)
+{
+ QDateTime dt;
+ dt.setTime_t(timeStamp);
+ if (dt.isValid() && statisticsContactMap.contains(id))
+ {
+ return statisticsContactMap[id]->mainStatusDate(dt.date());
+ }
+
+ return "";
+}
+#include "statisticsplugin.moc"
diff --git a/kopete/plugins/statistics/statisticsplugin.h b/kopete/plugins/statistics/statisticsplugin.h
new file mode 100644
index 00000000..d757b424
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsplugin.h
@@ -0,0 +1,213 @@
+/*
+ statisticsplugin.h
+
+ Copyright (c) 2003-2004 by Marc Cramdal <marc.cramdal@gmail.com>
+
+
+ *************************************************************************
+ * *
+ * 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 STATISTICSPLUGIN_H
+#define STATISTICSPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <dcopobject.h>
+
+#include "kopeteplugin.h"
+
+#include "kopetemessage.h"
+#include "kopetemessagehandler.h"
+#include "kopeteonlinestatus.h"
+
+#include "statisticsdcopiface.h"
+
+class QString;
+
+class StatisticsDB;
+class StatisticsContact;
+class StatisticsDCOPIface;
+
+class KopeteView;
+class KActionCollection;
+
+/** \section Kopete Statistics Plugin
+ *
+ * \subsection intro_sec Introduction
+ *
+ * This plugin aims at giving detailed statistics on metacontacts, for instance, how long was
+ * the metacontact online, how long was it busy etc.
+ * In the future, it will maybe make prediction on when the contact should be available for chat.
+ *
+ * \subsection install_sec How it works ...
+ * Each Metacontact is bound to a StatisticsContact which has access to the SQLITE database.
+ * This StatisticsContact stores the last status of the metacontact; the member function onlineStatusChanged is called when the
+ * metacontact status changed (this is managed in the slot slotOnlineStatusChanged of StatisticsPlugin) and then the DB is
+ * updated for the contact.
+ *
+ * More exactly the DB is updated only if the oldstatus was not Offline
+
+ * To have an idea how it works, here is a table :
+ *
+ * <table>
+ * <tr>
+ * <td>Event</td><td>Changes to database</td><td>oldStatus</td>
+ * </tr>
+ * <tr>
+ * <td>John 17:44 Away <i>(connexion)</i></td><td> - <i>(oldstatus was offline)</i></td><td>oldstatus = away </td>
+ * </tr>
+ * <tr>
+ * <td>John 18:01 Online</td><td>(+) Away 17:44 18:01</td><td>oldstatus = online</td>
+ * </tr>
+ * <tr>
+ * <td>John 18:30 Offline <i>(disconnect)</i></td><td>(+) Online 18:01 18:30</td><td>oldstatus = offline</td>
+ * </tr>
+ * <tr>
+ * <td>John 18:45 Online <i>(connexion)</i></td><td> - <i>(oldstatus was offline)</i></td><td>oldstatus = online</td>
+ * </tr>
+ * <tr>
+ * <td>John 20:30 Offline <i>(disconnect)</i></td><td>(+) Online 18:45 20:30</td><td>oldstatus = offline</td>
+ * </tr>
+ * </table>
+ *
+ * etc.
+ *
+ * \subsection install_sec Some little stats
+ * This plugin is able to record some other stats, not based on events. Theyre saved in the commonstat table in which we store stats
+ * like this :
+ *
+ * <code>statname, statvalue1, statvalue2</code>
+ *
+ * Generally, we store the value, and its ponderation. If an average on one hundred messages says that the contact X takes about
+ * 3 seconds between two messages, we store "timebetweentwomessages", "3", "100"
+ *
+ *
+ *
+ * StatisticsPlugin is the main Statistics plugin class.
+ * Contains mainly slots.
+ */
+class StatisticsPlugin : public Kopete::Plugin, virtual public StatisticsDCOPIface
+{
+ Q_OBJECT
+public:
+ /// Standard plugin constructors
+ StatisticsPlugin(QObject *parent, const char *name, const QStringList &args);
+ ~StatisticsPlugin();
+
+ /// Method to access m_db member
+ StatisticsDB *db() { return m_db; }
+private slots:
+ // Do the initializations
+ void slotInitialize();
+
+public slots:
+
+ /** \brief This slot is called when the status of a contact changed.
+ *
+ * Then it searches for the contact bind to the metacontact who triggered the signal and calls
+ * the specific StatisticsContact::onlineStatusChanged of the StatisticsContact to update the StatisticsContact status,
+ * and maybe, update the database.
+ */
+ void slotOnlineStatusChanged(Kopete::MetaContact *contact, Kopete::OnlineStatus::StatusType status );
+
+ /**
+ * Builds and show the StatisticsDialog for a contact
+ */
+ void slotViewStatistics();
+
+ /**
+ *
+ * Extract the metaContactId from the message, and calls the
+ * StatisticsContact::newMessageReceived(Kopete::Message& m) function
+ * for the corresponding contact
+ */
+ void slotAboutToReceive(Kopete::Message& m);
+
+ /*
+ * Managing views
+ */
+
+ /**
+ * \brief Only connects the Kopete::ChatSession::closing() signal to our slotViewClosed().
+ */
+ void slotViewCreated(Kopete::ChatSession* session);
+
+ /**
+ * One aim of this slot is to be able to stop recording time between two messages
+ * for the contact in the chatsession. But, we only
+ * want to do this if the contact is not in an other chatsession.
+ */
+ void slotViewClosed(Kopete::ChatSession* session);
+
+ /**
+ * Slot called when a new metacontact is added to make some slots connections and to create a new
+ * StatisticsContact object.
+ *
+ * In the constructor, we connect the metacontacts already existing to some slots, but we need to do this
+ * when new metacontacts are added.
+ * This function is also called when we loop over the contact list in the constructor.
+ */
+ void slotMetaContactAdded(Kopete::MetaContact *mc);
+
+ /**
+ * Slot called when a metacontact is removed to delete statistic data from db and to remove StatisticsContact object.
+ */
+ void slotMetaContactRemoved(Kopete::MetaContact *mc);
+
+ /**
+ * Slot called when a contact is added to metacontact.
+ */
+ void slotContactAdded(Kopete::Contact *c);
+
+ /**
+ * Slot called when a contact is removed from metacontact.
+ */
+ void slotContactRemoved(Kopete::Contact *c);
+
+
+ /*
+ * DCOP functions
+ * See statisticsdcopiface.h for the documentation
+ */
+ void dcopStatisticsDialog(QString id);
+
+ bool dcopWasOnline(QString id, int timeStamp);
+ bool dcopWasOnline(QString id, QString dt);
+
+ bool dcopWasAway(QString id, int timeStamp);
+ bool dcopWasAway(QString id, QString dt);
+
+ bool dcopWasOffline(QString id, int timeStamp);
+ bool dcopWasOffline(QString id, QString dt);
+
+ bool dcopWasStatus(QString id, QDateTime dateTime, Kopete::OnlineStatus::StatusType status);
+
+ QString dcopStatus(QString id, QString dateTime);
+ QString dcopStatus(QString id, int timeStamp);
+
+ QString dcopMainStatus(QString id, int timeStamp);
+
+private:
+ StatisticsDB *m_db;
+ /** Associate a Kopete::Contact id to a StatisticsContact to retrieve
+ * the StatisticsContact corresponding to the Kopete::Contact
+ */
+ QMap<QString, StatisticsContact*> statisticsContactMap;
+ /** Associate a Kopete::MetaContact to a StatisticsContact to retrieve
+ * the StatisticsContact corresponding to the MetaContact
+ */
+ QMap<Kopete::MetaContact*, StatisticsContact*> statisticsMetaContactMap;
+};
+
+
+#endif
diff --git a/kopete/plugins/statistics/statisticsui.rc b/kopete/plugins/statistics/statisticsui.rc
new file mode 100644
index 00000000..79d5898c
--- /dev/null
+++ b/kopete/plugins/statistics/statisticsui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_history" version="1">
+ <MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="viewMetaContactStatistics" />
+ </Menu>
+ </MenuBar>
+ <Menu name="contact_popup">
+ <Action name="viewMetaContactStatistics" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/statistics/statisticswidget.ui b/kopete/plugins/statistics/statisticswidget.ui
new file mode 100644
index 00000000..ca866e18
--- /dev/null
+++ b/kopete/plugins/statistics/statisticswidget.ui
@@ -0,0 +1,246 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>StatisticsWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>StatisticsWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>586</width>
+ <height>506</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>2</hsizetype>
+ <vsizetype>2</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Ask &amp;Database</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Date &amp;&amp; Time</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KDatePicker">
+ <property name="name">
+ <cstring>datePicker</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Time :</string>
+ </property>
+ </widget>
+ <widget class="KTimeWidget">
+ <property name="name">
+ <cstring>timePicker</cstring>
+ </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>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>60</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Question</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Contact Status at Date &amp; Time</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Most Used Status at Date</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>questionComboBox</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>askButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Ask</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Answer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTextEdit" row="0" column="0">
+ <property name="name">
+ <cstring>answerEdit</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kdatepicker.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kdatetbl.h</includehint>
+ <includehint>ktimewidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/texteffect/Makefile.am b/kopete/plugins/texteffect/Makefile.am
new file mode 100644
index 00000000..0d657dc5
--- /dev/null
+++ b/kopete/plugins/texteffect/Makefile.am
@@ -0,0 +1,20 @@
+METASOURCES = AUTO
+
+SUBDIRS = icons
+INCLUDES = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_texteffect.la kcm_kopete_texteffect.la
+
+kopete_texteffect_la_SOURCES = texteffectplugin.cpp texteffectconfig.cpp
+kopete_texteffect_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_texteffect_la_LIBADD = ../../libkopete/libkopete.la
+
+kcm_kopete_texteffect_la_SOURCES = texteffectconfig.cpp texteffectprefs.ui texteffectpreferences.cpp
+kcm_kopete_texteffect_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_texteffect_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_texteffect.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_texteffect_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
diff --git a/kopete/plugins/texteffect/icons/Makefile.am b/kopete/plugins/texteffect/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/plugins/texteffect/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/plugins/texteffect/icons/cr32-app-texteffect.png b/kopete/plugins/texteffect/icons/cr32-app-texteffect.png
new file mode 100644
index 00000000..9f44eb65
--- /dev/null
+++ b/kopete/plugins/texteffect/icons/cr32-app-texteffect.png
Binary files differ
diff --git a/kopete/plugins/texteffect/kopete_texteffect.desktop b/kopete/plugins/texteffect/kopete_texteffect.desktop
new file mode 100644
index 00000000..f929b2f2
--- /dev/null
+++ b/kopete/plugins/texteffect/kopete_texteffect.desktop
@@ -0,0 +1,131 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=texteffect
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_texteffect
+X-KDE-PluginInfo-Author=Olivier Goffart
+X-KDE-PluginInfo-Email=ogoffart@tiscalinet.be
+X-KDE-PluginInfo-Name=kopete_texteffect
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Text Effect
+Name[ar]=التأثير النصي
+Name[be]=ТÑкÑтавы Ñфект
+Name[bg]=ТекÑтови ефекти
+Name[bn]=টেকà§à¦¸à¦Ÿ পà§à¦°à¦­à¦¾à¦¬
+Name[bs]=Tekst efekti
+Name[ca]=Efecte de text
+Name[cs]=Textový efekt
+Name[cy]=Effaith Testun
+Name[da]=Teksteffekt
+Name[de]=Texteffekte
+Name[el]=Εφέ κειμένου
+Name[eo]=Tekst-efektoj
+Name[es]=Efecto de texto
+Name[et]=Tekstiefektid
+Name[eu]=Testuaren efektua
+Name[fa]=جلوۀ متن
+Name[fi]=Tekstitehoste
+Name[fr]=Effets de texte
+Name[ga]=Maisíocht Téacs
+Name[gl]=Efecto de texto
+Name[he]=××¤×§×˜×™× ×©×œ טקסט
+Name[hi]=पाठ पà¥à¤°à¤­à¤¾à¤µ
+Name[hr]=Tekstualni efekti
+Name[hu]=Szövegeffektus
+Name[is]=Breyta letri
+Name[it]=Effetto di testo
+Name[ja]=テキスト効果
+Name[ka]=ტექსტის ეფექტები
+Name[kk]=Мәтінді безендіру
+Name[km]=បែបផែន​អážáŸ’ážáž”ទ​
+Name[lt]=Teksto efektai
+Name[mk]=Ефекти за текÑÑ‚
+Name[nb]=Teksteffekt
+Name[nds]=Texteffekt
+Name[ne]=पाठ पà¥à¤°à¤­à¤¾à¤µ
+Name[nl]=Teksteffect
+Name[nn]=Teksteffekt
+Name[pa]=ਪਾਠ ਪਰਭਾਵ
+Name[pl]=Efekty tekstowe
+Name[pt]=Efeito de Texto
+Name[pt_BR]=Efeito de texto
+Name[ro]=Efect text
+Name[ru]=ТекÑтовые Ñффекты
+Name[se]=Teakstaeffeakta
+Name[sk]=Textový efekt
+Name[sl]=Besedilni uÄinki
+Name[sr]=ТекÑтуални ефекти
+Name[sr@Latn]=Tekstualni efekti
+Name[sv]=Texteffekter
+Name[ta]=செயலà¯
+Name[tg]=Ðатиҷаҳои Матн
+Name[tr]=Metin Efekti
+Name[uk]=ТекÑтові ефекти
+Name[uz]=Matn effekti
+Name[uz@cyrillic]=Матн Ñффекти
+Name[zh_CN]=文字特效
+Name[zh_HK]=文字效果
+Name[zh_TW]=文字效果
+Comment=Add nice effects to your messages
+Comment[ar]=تضي٠مؤثرات لطيÙØ© لرسائلك
+Comment[be]=Дадаць Ñ„Ð°Ð¹Ð½Ñ‹Ñ Ñфекты да вашых паведамленнÑÑž
+Comment[bg]=ДобавÑне на текÑтови ефекти към ÑъобщениÑта
+Comment[bn]=আপনার বারà§à¦¤à¦¾à¦¤à§‡ সà§à¦¨à§à¦¦à¦° পà§à¦°à¦­à¦¾à¦¬ যোগ করে
+Comment[bs]=Dodaj efekte porukama
+Comment[ca]=Afegeix bonics efectes als vostres missatges
+Comment[cs]=Přidává efekty ke zprávám
+Comment[cy]=Ychwanegu effeithiau del i'ch negeseuon
+Comment[da]=Tilføj rare effekter til dine beskeder
+Comment[de]=Verschönern Sie eigene Nachrichten durch nette Effekte
+Comment[el]=ΠÏοσθέτει όμοÏφα εφέ στα μηνÏματά σας
+Comment[eo]=Ornami viajn mesaÄojn per belaj efektoj
+Comment[es]=Añade efectos agradables a sus mensajes
+Comment[et]=Lisab sõnumitele vahvaid efekte
+Comment[eu]=Gehitu efektu atseginak zure mezuei
+Comment[fa]=اÙزودن جلوه‌های زیبا به پیامهایتان
+Comment[fi]=Lisää tehosteita viesteihisi
+Comment[fr]=Ajouter des effets sympathiques à vos messages
+Comment[ga]=Cuir maisíochtaí deasa le do theachtaireachtaí
+Comment[gl]=Engadir efectos agradables ás túas mensaxes
+Comment[he]=הוסף ××¤×§×˜×™× × ×—×ž×“×™× ×œ×”×•×“×¢×•×ª×š
+Comment[hi]=आपके संदेशों में सà¥à¤‚दर पà¥à¤°à¤­à¤¾à¤µ जोड़े
+Comment[hu]=Effektusok hozzáadása az üzenetekhez
+Comment[is]=Gera skeytin þín flottari
+Comment[it]=Aggiungi effetti carini ai tuoi messaggi
+Comment[ja]=メッセージã«åŠ¹æžœã‚’付加
+Comment[ka]=თქვენს შეტყáƒáƒ‘ინებებს áƒáƒ›áƒáƒ¢áƒ”ბს სáƒáƒ¡áƒ˜áƒáƒ›áƒáƒ•áƒœáƒ ეფექტებს
+Comment[kk]=Хабарыңызды безендіру Ñ‚Ó™Ñілдері
+Comment[km]=បន្ážáŸ‚ម​បែបផែន​ស្រស់ស្អាážâ€‹áž‘ៅ​សារ​របស់​អ្នក
+Comment[lt]=Papuoškite žinutes gražiais teksto efektais
+Comment[mk]=Додадете фини ефекти на вашите пораки
+Comment[nb]=Legg til effekter på meldingene dine
+Comment[nds]=Dien Narichten smucke Effekten tofögen
+Comment[ne]=तपाईà¤à¤•à¥‹ सनà¥à¤¦à¥‡à¤¶à¤®à¤¾ उतà¥à¤¤à¤® पà¥à¤°à¤­à¤¾à¤µ थपà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Voegt leuke effecten to aan uw berichten
+Comment[nn]=Legg til effektar på meldingane
+Comment[pl]=Dodaje ładne efekty do Twoich wiadomości
+Comment[pt]=Adicionar efeitos engraçados às suas mensagens
+Comment[pt_BR]=Adiciona um efeito às suas mensagens
+Comment[ro]=Adaucă efecte drăguţe la mesajele dumneavoastră
+Comment[ru]=Добавить Ñффекты к вашим ÑообщениÑм
+Comment[se]=Lasit fiinna effeavttaid du dieđáhusaide
+Comment[sk]=Pridá pekné efekty k vašim správam
+Comment[sl]=Doda lepe uÄinke vaÅ¡im sporoÄilom
+Comment[sr]=Додаје лепе ефекте вашим порукама
+Comment[sr@Latn]=Dodaje lepe efekte vašim porukama
+Comment[sv]=Lägg till trevliga effekter i dina meddelanden
+Comment[ta]=இனிய விளைவà¯à®•à®³à¯ˆ உஙà¯à®•à®³à¯à®•à¯à®•à¯ சேரà¯à®•à¯à®•à¯à®®à¯
+Comment[tg]=Ðатиҷаҳои хубро ба пайёмҳои шумо ҳамроҳ мекунад
+Comment[tr]=Mesajlarınıza hoş efektler ekleyin
+Comment[uk]=Додати ефекти до ваших повідомлень
+Comment[uz]=Xabarlarga chiroyli effektlarni qoʻshish
+Comment[uz@cyrillic]=Хабарларга чиройли Ñффектларни қўшиш
+Comment[zh_CN]=在您的消æ¯ä¸­æ·»åŠ æ–‡å­—特效
+Comment[zh_HK]=為您的訊æ¯å¢žåŠ æœ‰è¶£çš„效果
+Comment[zh_TW]=在您的訊æ¯ä¸­åŠ å…¥ä¸€äº›æ•ˆæžœ
diff --git a/kopete/plugins/texteffect/kopete_texteffect_config.desktop b/kopete/plugins/texteffect/kopete_texteffect_config.desktop
new file mode 100644
index 00000000..06678e51
--- /dev/null
+++ b/kopete/plugins/texteffect/kopete_texteffect_config.desktop
@@ -0,0 +1,126 @@
+[Desktop Entry]
+Icon=texteffect
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_texteffect
+X-KDE-FactoryName=TextEffectConfigFactory
+X-KDE-ParentApp=kopete_texteffect
+X-KDE-ParentComponents=kopete_texteffect
+
+Name=Text Effect
+Name[ar]=التأثير النصي
+Name[be]=ТÑкÑтавы Ñфект
+Name[bg]=ТекÑтови ефекти
+Name[bn]=টেকà§à¦¸à¦Ÿ পà§à¦°à¦­à¦¾à¦¬
+Name[bs]=Tekst efekti
+Name[ca]=Efecte de text
+Name[cs]=Textový efekt
+Name[cy]=Effaith Testun
+Name[da]=Teksteffekt
+Name[de]=Texteffekte
+Name[el]=Εφέ κειμένου
+Name[eo]=Tekst-efektoj
+Name[es]=Efecto de texto
+Name[et]=Tekstiefektid
+Name[eu]=Testuaren efektua
+Name[fa]=جلوۀ متن
+Name[fi]=Tekstitehoste
+Name[fr]=Effets de texte
+Name[ga]=Maisíocht Téacs
+Name[gl]=Efecto de texto
+Name[he]=××¤×§×˜×™× ×©×œ טקסט
+Name[hi]=पाठ पà¥à¤°à¤­à¤¾à¤µ
+Name[hr]=Tekstualni efekti
+Name[hu]=Szövegeffektus
+Name[is]=Breyta letri
+Name[it]=Effetto di testo
+Name[ja]=テキスト効果
+Name[ka]=ტექსტის ეფექტები
+Name[kk]=Мәтінді безендіру
+Name[km]=បែបផែន​អážáŸ’ážáž”ទ​
+Name[lt]=Teksto efektai
+Name[mk]=Ефекти за текÑÑ‚
+Name[nb]=Teksteffekt
+Name[nds]=Texteffekt
+Name[ne]=पाठ पà¥à¤°à¤­à¤¾à¤µ
+Name[nl]=Teksteffect
+Name[nn]=Teksteffekt
+Name[pa]=ਪਾਠ ਪਰਭਾਵ
+Name[pl]=Efekty tekstowe
+Name[pt]=Efeito de Texto
+Name[pt_BR]=Efeito de texto
+Name[ro]=Efect text
+Name[ru]=ТекÑтовые Ñффекты
+Name[se]=Teakstaeffeakta
+Name[sk]=Textový efekt
+Name[sl]=Besedilni uÄinki
+Name[sr]=ТекÑтуални ефекти
+Name[sr@Latn]=Tekstualni efekti
+Name[sv]=Texteffekter
+Name[ta]=செயலà¯
+Name[tg]=Ðатиҷаҳои Матн
+Name[tr]=Metin Efekti
+Name[uk]=ТекÑтові ефекти
+Name[uz]=Matn effekti
+Name[uz@cyrillic]=Матн Ñффекти
+Name[zh_CN]=文字特效
+Name[zh_HK]=文字效果
+Name[zh_TW]=文字效果
+Comment=Adds special effects to your text
+Comment[ar]=يضي٠مؤثرات خاصة لنصوصك
+Comment[be]=Дадае адмыÑÐ»Ð¾Ð²Ñ‹Ñ Ñфекты да вашага Ñ‚ÑкÑту
+Comment[bg]=ДобавÑне на текÑтови ефекти към ÑъобщениÑта
+Comment[bn]=আপনার টেকà§à¦¸à¦Ÿà§‡ বিশেষ পà§à¦°à¦­à¦¾à¦¬ যোগ করে
+Comment[bs]=Dodaje specijalne efekte vašim porukama
+Comment[ca]=Afegeix bonics efectes al vostre text
+Comment[cs]=Přidává speciální efekty do vašeho textu
+Comment[cy]=Ychwanegu effeithiau arbennig i'ch testun
+Comment[da]=Tilføj specielle effekter til din tekst
+Comment[de]=Verschönern Sie eigene Nachrichten durch nette Effekte
+Comment[el]=ΠÏοσθέτει ειδικά εφέ στο κείμενό σας
+Comment[es]=Añade efectos especiales a su texto
+Comment[et]=Lisab tekstile eriefekte
+Comment[eu]=Gehitu efektu bereziak zure testuar
+Comment[fa]=جلوه‌های ویژه را به متن شما اضاÙÙ‡ می‌کند
+Comment[fi]=Lisää erikoisefektejä tekstiisi
+Comment[fr]=Ajoute des effets spéciaux à vos messages
+Comment[ga]=Cuir maisíochtaí speisialta le do théacs
+Comment[gl]=Engadir efectos especiáis ó teu texto
+Comment[he]=הוסף ××¤×§×˜×™× ×ž×™×•×—×“×™× ×œ×”×•×“×¢×•×ª×š
+Comment[hi]=आपके संदेशों में विशिषà¥à¤Ÿ पà¥à¤°à¤­à¤¾à¤µ जोड़े
+Comment[hr]=Dodaje specijalne efekte vašem tekstu
+Comment[hu]=Speciális effektusok hozzáadása az üzenetek szövegéhez
+Comment[is]=Bæta skreytingum í textann
+Comment[it]=Aggiunti effetti speciali al tuo testo
+Comment[ja]=テキストã«ç‰¹åˆ¥ãªåŠ¹æžœã‚’付加
+Comment[ka]=თქვენს ტექსტს áƒáƒ›áƒáƒ¢áƒ”ბს ეფექტებს
+Comment[kk]=Мәтініңізді арнаулы безендіру Ñ‚Ó™Ñілдері
+Comment[km]=បន្ážáŸ‚ម​បែបផែន​ពិសáŸážŸáŸ—​ទៅ​អážáŸ’ážáž”ទ​របស់​អ្នក
+Comment[lt]=Pridėkite į tekstą specialiųjų efektų
+Comment[mk]=Додава Ñпцијални ефекти на вашиот текÑÑ‚
+Comment[nb]=Legg til spesielle effekter på teksten
+Comment[nds]=Föögt Dien Text smucke Effekten to
+Comment[ne]=तपाईà¤à¤•à¥‹ पाठमा विशेष पà¥à¤°à¤­à¤¾à¤µ थपà¥à¤¦à¤›
+Comment[nl]=Voegt speciale effecten aan uw teksten toe
+Comment[nn]=Legg til spesielle effektar på teksten
+Comment[pl]=Dodaje specjalne efekty do Twojego tekstu
+Comment[pt]=Adiciona efeitos especiais ao seu texto
+Comment[pt_BR]=Adiciona efeitos especiais em seu texto
+Comment[ro]=Adaugă efecte speciale la textele dumneavoastră
+Comment[ru]=ДобавлÑет Ñффекты к вашим ÑообщениÑм
+Comment[se]=Lasiha erenoamaš effeavttaid du tekstii
+Comment[sk]=Pridá špeciálne efekty k vášmu textu
+Comment[sl]=Doda posebne uÄinke vaÅ¡emu besedilu
+Comment[sr]=Додаје Ñпецијалне ефекте вашем текÑту
+Comment[sr@Latn]=Dodaje specijalne efekte vašem tekstu
+Comment[sv]=Lägger till specialeffekter till din text
+Comment[ta]=உஙà¯à®•à®³à¯ உரையில௠சிறபà¯à®ªà¯ விளைவிகளை சேரà¯à®•à¯à®•à¯à®®à¯
+Comment[tg]=Ðатиҷаҳои махÑуÑро ба матни шумо ҳамроҳ мекунад
+Comment[tr]=Metinlerinize özel efektler ekleyin
+Comment[uk]=Додає ефекти до ваших повідомлень
+Comment[zh_CN]=在您的文字中添加特殊效果
+Comment[zh_HK]=為您的訊æ¯å¢žåŠ ç‰¹åˆ¥çš„效果
+Comment[zh_TW]=在您的文字中加入一些特效
+
diff --git a/kopete/plugins/texteffect/texteffectconfig.cpp b/kopete/plugins/texteffect/texteffectconfig.cpp
new file mode 100644
index 00000000..9ecca3f0
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectconfig.cpp
@@ -0,0 +1,140 @@
+/*
+ texteffectconfig.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstring.h>
+
+#include <kglobal.h>
+#include <kconfig.h>
+
+#include "texteffectconfig.h"
+
+TextEffectConfig::TextEffectConfig()
+{
+ load();
+}
+
+void TextEffectConfig::load()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("TextEffect Plugin");
+
+ mColors = config->readListEntry("Colors");
+ if(mColors.isEmpty())
+ {
+ mColors= defaultColorList();
+ }
+ mColorRandom = config->readBoolEntry("Color Random Order", false);
+ mColorLines = config->readBoolEntry("Color change every lines", true);
+ mColorWords = config->readBoolEntry("Color change every words", false);
+ mColorChar = config->readBoolEntry("Color change every char", false);
+
+ mLamer = config->readBoolEntry("L4m3r", false);
+ mWaves = config->readBoolEntry("WaVeS", false);
+}
+
+QStringList TextEffectConfig::defaultColorList()
+{
+ return QStringList::split( ",", "#00BBDD,#0088DD,#0000DD,#8800DD,#DD00DD,#DD0088,#DD0000,#DD8800,#DDBB00,#88BB00,#00BB00" );
+}
+
+void TextEffectConfig::save()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup("TextEffect Plugin");
+
+ config->writeEntry("Colors", mColors );
+ config->writeEntry("Color Random Order", mColorRandom);
+ config->writeEntry("Color change every lines", mColorLines);
+ config->writeEntry("Color change every words", mColorWords);
+ config->writeEntry("Color change every char", mColorChar);
+
+ config->writeEntry("L4m3r", mLamer);
+ config->writeEntry("WaVeS", mWaves);
+
+ config->sync();
+}
+
+QStringList TextEffectConfig::colors() const
+{
+ return mColors;
+}
+
+bool TextEffectConfig::colorRandom() const
+{
+ return mColorRandom;
+}
+
+bool TextEffectConfig::colorWords() const
+{
+ return mColorWords;
+}
+
+bool TextEffectConfig::colorLines() const
+{
+ return mColorLines;
+}
+
+bool TextEffectConfig::colorChar() const
+{
+ return mColorChar;
+}
+
+bool TextEffectConfig::lamer() const
+{
+ return mLamer;
+}
+
+bool TextEffectConfig::waves() const
+{
+ return mWaves;
+}
+
+void TextEffectConfig::setColors(const QStringList &newColors)
+{
+ mColors = newColors;
+}
+
+void TextEffectConfig::setColorWords(bool newWords)
+{
+ mColorWords = newWords;
+}
+
+void TextEffectConfig::setColorLines(bool newLines)
+{
+ mColorLines = newLines;
+}
+
+void TextEffectConfig::setColorRandom(bool newRandom)
+{
+ mColorRandom = newRandom;
+}
+
+void TextEffectConfig::setColorChar(bool newChar)
+{
+ mColorChar = newChar;
+}
+
+void TextEffectConfig::setLamer(bool newLamers)
+{
+ mLamer = newLamers;
+}
+
+void TextEffectConfig::setWaves(bool newWaves)
+{
+ mWaves = newWaves;
+}
diff --git a/kopete/plugins/texteffect/texteffectconfig.h b/kopete/plugins/texteffect/texteffectconfig.h
new file mode 100644
index 00000000..80b19151
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectconfig.h
@@ -0,0 +1,62 @@
+/*
+ texteffectconfig.h
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TEXTEFFECTCONFIG_H
+#define TEXTEFFECTCONFIG_H
+
+class QStringList;
+
+class TextEffectConfig
+{
+public:
+ TextEffectConfig();
+
+ void load();
+ void save();
+
+ //accessor functions
+ QStringList colors() const;
+ bool colorLines() const;
+ bool colorWords() const;
+ bool colorChar() const;
+ bool colorRandom() const;
+ bool lamer() const;
+ bool waves() const;
+
+ void setColors(const QStringList &newColors = QStringList());
+ void setColorLines(bool newLines);
+ void setColorChar(bool newChar);
+ void setColorWords(bool newWords);
+ void setColorRandom(bool newRandom);
+ void setLamer(bool newLamer);
+ void setWaves(bool newWaves);
+ QStringList defaultColorList();
+
+
+private:
+ QStringList mColors;
+ bool mColorLines;
+ bool mColorWords;
+ bool mColorChar;
+ bool mColorRandom;
+ bool mLamer;
+ bool mWaves;
+
+};
+
+#endif
diff --git a/kopete/plugins/texteffect/texteffectplugin.cpp b/kopete/plugins/texteffect/texteffectplugin.cpp
new file mode 100644
index 00000000..5374b2ca
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectplugin.cpp
@@ -0,0 +1,198 @@
+/***************************************************************************
+ texteffectplugin.cpp - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdlib.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+
+#include "kopetechatsessionmanager.h"
+
+#include "texteffectplugin.h"
+#include "texteffectconfig.h"
+
+typedef KGenericFactory<TextEffectPlugin> TextEffectPluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_texteffect, TextEffectPluginFactory( "kopete_texteffect" ) )
+
+TextEffectPlugin::TextEffectPlugin( QObject *parent, const char *name, const QStringList &/*args*/ )
+: Kopete::Plugin( TextEffectPluginFactory::instance(), parent, name )
+{
+ if( !pluginStatic_ )
+ pluginStatic_=this;
+
+ m_config = new TextEffectConfig;
+
+ connect ( this , SIGNAL( settingsChanged() ) , this , SLOT( slotSettingsChanged() ) );
+
+ connect( Kopete::ChatSessionManager::self(),
+ SIGNAL( aboutToSend( Kopete::Message & ) ),
+ SLOT( slotOutgoingMessage( Kopete::Message & ) ) );
+
+ last_color=0;
+}
+
+TextEffectPlugin::~TextEffectPlugin()
+{
+ delete m_config;
+ pluginStatic_ = 0L;
+}
+
+TextEffectPlugin* TextEffectPlugin::plugin()
+{
+ return pluginStatic_ ;
+}
+
+TextEffectPlugin* TextEffectPlugin::pluginStatic_ = 0L;
+
+
+void TextEffectPlugin::slotOutgoingMessage( Kopete::Message& msg )
+{
+ if(msg.direction() != Kopete::Message::Outbound)
+ return;
+
+ QStringList colors=m_config->colors();
+
+ if(m_config->colorChar() || m_config->colorWords() || m_config->lamer() || m_config->waves() )
+ {
+ QString original=msg.plainBody();
+ QString resultat;
+
+ unsigned int c=0;
+ bool wavein=false;
+
+ for(unsigned int f=0;f<original.length();f++)
+ {
+ QChar x=original[f];
+ if(f==0 || m_config->colorChar() || (m_config->colorWords() && x==' ' ))
+ {
+ if(f!=0)
+ resultat+="</font>";
+ resultat+="<font color=\"";
+ resultat+=colors[c];
+ if(m_config->colorRandom())
+ c=rand()%colors.count();
+ else
+ {
+ c++;
+ if(c >= colors.count())
+ c=0;
+ }
+ resultat+="\">";
+ }
+ switch (x.latin1())
+ {
+ case '>':
+ resultat+="&gt;";
+ break;
+ case '<':
+ resultat+="&lt;";
+ break;
+ case '&':
+ resultat+="&amp;";
+ break;
+ case '\n':
+ resultat+="<br>";
+ case 'a':
+ case 'A':
+ if(m_config->lamer())
+ {
+ resultat+="4";
+ break;
+ } //else, go to the default, all other case have this check
+ case 'e':
+ case 'E':
+ if(m_config->lamer())
+ {
+ resultat+="3";
+ break;
+ }//else, go to the default, all other case have this check
+ case 'i':
+ case 'I':
+ if(m_config->lamer())
+ {
+ resultat+="1";
+ break;
+ }//else, go to the default, all other case have this check
+ case 'l':
+ case 'L':
+ if(m_config->lamer())
+ {
+ resultat+="|";
+ break;
+ }//else, go to the default, all other case have this check
+ case 't':
+ case 'T':
+ if(m_config->lamer())
+ {
+ resultat+="7";
+ break;
+ }//else, go to the default, all other case have this check
+ case 's':
+ case 'S':
+ if(m_config->lamer())
+ {
+ resultat+="5";
+ break;
+ }//else, go to the default, all other case have this check
+ case 'o':
+ case 'O':
+ if(m_config->lamer())
+ {
+ resultat+="0";
+ break;
+ }//else, go to the default, all other case have this check
+ default:
+ if(m_config->waves())
+ {
+ resultat+= wavein ? x.lower() : x.upper();
+ wavein=!wavein;
+ }
+ else
+ resultat+=x;
+ break;
+ }
+ }
+ if( m_config->colorChar() || m_config->colorWords() )
+ resultat+="</font>";
+ msg.setBody(resultat,Kopete::Message::RichText);
+ }
+
+ if(m_config->colorLines())
+ {
+ if(m_config->colorRandom())
+ {
+ last_color=rand()%colors.count();
+ }
+ else
+ {
+ last_color++;
+ if(last_color >= colors.count())
+ last_color=0;
+ }
+
+ msg.setFg(QColor (colors[last_color]));
+ }
+}
+
+void TextEffectPlugin::slotSettingsChanged()
+{
+ m_config->load();
+}
+
+
+#include "texteffectplugin.moc"
+
diff --git a/kopete/plugins/texteffect/texteffectplugin.h b/kopete/plugins/texteffect/texteffectplugin.h
new file mode 100644
index 00000000..db34fdcb
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectplugin.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ texteffectplugin.h - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TextEffectPLUGIN_H
+#define TextEffectPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+
+class QStringList;
+class QString;
+
+namespace Kopete { class Message; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class ChatSession; }
+class TextEffectConfig;
+
+/**
+ * @author Olivier Goffart
+ */
+
+class TextEffectPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+public:
+ static TextEffectPlugin *plugin();
+
+ TextEffectPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~TextEffectPlugin();
+
+public slots:
+ void slotOutgoingMessage( Kopete::Message& msg );
+ void slotSettingsChanged();
+
+private:
+ static TextEffectPlugin* pluginStatic_;
+ unsigned int last_color;
+ TextEffectConfig *m_config;
+};
+
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/texteffect/texteffectpreferences.cpp b/kopete/plugins/texteffect/texteffectpreferences.cpp
new file mode 100644
index 00000000..c9f0c03b
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectpreferences.cpp
@@ -0,0 +1,232 @@
+/***************************************************************************
+ texteffectpreferences.cpp - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qstring.h>
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+#include <kcolordialog.h>
+#include <kgenericfactory.h>
+#include <kautoconfig.h>
+#include <kdebug.h>
+
+#include <kdeversion.h>
+
+#include "texteffectprefs.h"
+#include "texteffectpreferences.h"
+#include "texteffectconfig.h"
+
+typedef KGenericFactory<TextEffectPreferences> TextEffectPreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_texteffect, TextEffectPreferencesFactory( "kcm_kopete_texteffect" ) )
+
+TextEffectPreferences::TextEffectPreferences(QWidget *parent,
+ const char* /*name*/,
+ const QStringList &args)
+ : KCModule(TextEffectPreferencesFactory::instance(), parent, args)
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+
+ kdDebug( 14310 ) << "Creating preferences dialog" << endl;
+
+ preferencesDialog = new TextEffectPrefs(this);
+
+ kdDebug( 14310 ) << "Creating config object" << endl;
+
+ config = new TextEffectConfig;
+
+ kdDebug( 14310 ) << "Setting up connections" << endl;
+
+ connect(preferencesDialog->mColorsAdd , SIGNAL(pressed()) ,
+ this , SLOT(slotAddPressed()));
+
+ connect(preferencesDialog->mColorsRemove , SIGNAL(pressed()) ,
+ this , SLOT(slotRemovePressed()));
+
+ connect(preferencesDialog->mColorsUp , SIGNAL(pressed()) ,
+ this , SLOT(slotUpPressed()));
+
+ connect(preferencesDialog->mColorsDown , SIGNAL(pressed()) ,
+ this , SLOT(slotDownPressed()));
+
+ // Connect up all the check boxes
+ connect( preferencesDialog->m_lamer, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+ connect( preferencesDialog->m_casewaves, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+
+ connect( preferencesDialog->m_colorRandom, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+ connect( preferencesDialog->m_fg, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+ connect( preferencesDialog->m_char, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+ connect( preferencesDialog->m_words, SIGNAL( clicked() ),
+ this, SLOT( slotSettingChanged() ) );
+
+ //setMainWidget( preferencesDialog, "Text Effect Plugin" );
+
+ load();
+
+}
+
+TextEffectPreferences::~TextEffectPreferences()
+{
+ delete preferencesDialog;
+ delete config;
+}
+
+
+void TextEffectPreferences::load()
+{
+ kdDebug( 14310 ) << k_funcinfo << "ENTER" << endl;
+
+ config->load();
+
+ preferencesDialog->mColorsListBox->insertStringList(config->colors());
+ preferencesDialog->m_fg->setChecked(config->colorLines());
+ preferencesDialog->m_words->setChecked(config->colorWords());
+ preferencesDialog->m_char->setChecked(config->colorChar());
+ preferencesDialog->m_lamer->setChecked(config->lamer());
+ preferencesDialog->m_casewaves->setChecked(config->waves());
+
+
+ // Call parent's save method
+ KCModule::load();
+
+ // Indicate that we have not changed ^_^
+ emit changed( false );
+
+ kdDebug( 14310 ) << k_funcinfo << "EXIT" << endl;
+
+}
+
+void TextEffectPreferences::save()
+{
+ kdDebug() << k_funcinfo << "ENTER" << endl;
+ // Save the settings
+ config->setColors(colors());
+ config->setColorRandom(preferencesDialog->m_colorRandom->isChecked());
+ config->setColorLines(preferencesDialog->m_fg->isChecked());
+ config->setColorWords(preferencesDialog->m_words->isChecked());
+ config->setColorChar(preferencesDialog->m_char->isChecked());
+
+ config->setLamer(preferencesDialog->m_lamer->isChecked());
+ config->setWaves(preferencesDialog->m_casewaves->isChecked());
+
+ config->save();
+
+ // Notify the plugin that the settings have changed
+ //TextEffectPlugin::plugin()->slotSettingsChanged();
+
+ // Call parent's save method
+ KCModule::save();
+
+ // Indicate that we have not changed ^_^
+ emit changed( false );
+ kdDebug() << k_funcinfo << "EXIT" << endl;
+}
+
+QStringList TextEffectPreferences::colors()
+{
+ QStringList ret;
+ for(unsigned int f=0; f<preferencesDialog->mColorsListBox->count() ; f++)
+ {
+ ret.append(preferencesDialog->mColorsListBox->text(f));
+ }
+ return ret;
+}
+
+void TextEffectPreferences::slotAddPressed()
+{
+ QColor myColor;
+ if( KColorDialog::getColor( myColor ) == KColorDialog::Accepted )
+ {
+ preferencesDialog->mColorsListBox->insertItem(myColor.name());
+ }
+
+ // Indicate that something has changed
+ slotSettingChanged();
+
+}
+void TextEffectPreferences::slotRemovePressed()
+{
+ delete preferencesDialog->mColorsListBox->selectedItem();
+ // Indicate that something has changed
+ slotSettingChanged();
+}
+
+
+void TextEffectPreferences::slotUpPressed()
+{
+ int p=preferencesDialog->mColorsListBox->currentItem();
+ if(p <= 0 )
+ return;
+ QListBoxItem *i=preferencesDialog->mColorsListBox->selectedItem();
+ if(!i)
+ return;
+ preferencesDialog->mColorsListBox->setSelected(i,false);
+ preferencesDialog->mColorsListBox->takeItem(i);
+ preferencesDialog->mColorsListBox->insertItem(i , p-1 );
+ preferencesDialog->mColorsListBox->setSelected(i,true);
+
+ // Indicate that something has changed
+ slotSettingChanged();
+
+}
+void TextEffectPreferences::slotDownPressed()
+{
+ int p=preferencesDialog->mColorsListBox->currentItem();
+ if(p < 0 )
+ return;
+ QListBoxItem *i=preferencesDialog->mColorsListBox->selectedItem();
+ if(!i)
+ return;
+ preferencesDialog->mColorsListBox->setSelected(i,false);
+ preferencesDialog->mColorsListBox->takeItem(i);
+ preferencesDialog->mColorsListBox->insertItem(i , p+1 );
+ preferencesDialog->mColorsListBox->setSelected(i,true);
+
+ // Indicate that something has changed
+ slotSettingChanged();
+}
+
+
+
+void TextEffectPreferences::slotSettingChanged()
+{
+ kdDebug() << k_funcinfo << "Called"
+ << endl;
+ // Indicate that our settings have changed
+ emit changed( true );
+}
+
+void TextEffectPreferences::defaults()
+{
+ preferencesDialog->mColorsListBox->clear();
+ preferencesDialog->mColorsListBox->insertStringList(config->defaultColorList());
+ preferencesDialog->m_fg->setChecked(false);
+ preferencesDialog->m_words->setChecked(false);
+ preferencesDialog->m_char->setChecked(false);
+ preferencesDialog->m_lamer->setChecked(false);
+ preferencesDialog->m_casewaves->setChecked(false);
+ preferencesDialog->m_colorRandom->setChecked( false );
+ emit changed( true );
+}
+
+#include "texteffectpreferences.moc"
diff --git a/kopete/plugins/texteffect/texteffectpreferences.h b/kopete/plugins/texteffect/texteffectpreferences.h
new file mode 100644
index 00000000..21dc7bff
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectpreferences.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ texteffectpreferences.h - description
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TextEffectPREFERENCES_H
+#define TextEffectPREFERENCES_H
+
+#include <kcmodule.h>
+
+class TextEffectPrefs;
+class TextEffectConfig;
+class QStringList;
+
+/**
+ *@author Olivier Goffart
+ */
+
+class TextEffectPreferences : public KCModule {
+ Q_OBJECT
+public:
+
+ TextEffectPreferences(QWidget *parent = 0, const char* name = 0, const QStringList &args = QStringList());
+ ~TextEffectPreferences();
+
+ // Overloaded from parent
+ virtual void save();
+ virtual void load();
+ virtual void defaults();
+
+private:
+ QStringList colors();
+ TextEffectPrefs *preferencesDialog;
+ TextEffectConfig *config;
+
+private slots: // Public slots
+ void slotAddPressed();
+ void slotRemovePressed();
+ void slotUpPressed();
+ void slotDownPressed();
+ void slotSettingChanged();
+
+};
+
+#endif
+
diff --git a/kopete/plugins/texteffect/texteffectprefs.ui b/kopete/plugins/texteffect/texteffectprefs.ui
new file mode 100644
index 00000000..95ff801c
--- /dev/null
+++ b/kopete/plugins/texteffect/texteffectprefs.ui
@@ -0,0 +1,231 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>TextEffectPrefs</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TextEffectPrefs</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>630</width>
+ <height>529</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>TabWidget3</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Colors</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Colors</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListBox" row="0" column="0" rowspan="5" colspan="1">
+ <property name="name">
+ <cstring>mColorsListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>mColorsAdd</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>mColorsRemove</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>mColorsUp</cstring>
+ </property>
+ <property name="text">
+ <string>Move &amp;Up</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>mColorsDown</cstring>
+ </property>
+ <property name="text">
+ <string>Move &amp;Down</string>
+ </property>
+ </widget>
+ <spacer row="4" column="1">
+ <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>21</width>
+ <height>81</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>m_colorRandom</cstring>
+ </property>
+ <property name="text">
+ <string>Random order</string>
+ </property>
+ </widget>
+ <widget class="Line" row="2" column="0">
+ <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="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>m_fg</cstring>
+ </property>
+ <property name="text">
+ <string>Change global text foreground color</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>m_char</cstring>
+ </property>
+ <property name="text">
+ <string>Change color every letter</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>m_words</cstring>
+ </property>
+ <property name="text">
+ <string>Change color every word</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Effects</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_lamer</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>L4m3r t4lk</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_casewaves</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>CasE wAVes</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>279</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>TabWidget3</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">knuminput.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistbox.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/plugins/translator/Makefile.am b/kopete/plugins/translator/Makefile.am
new file mode 100644
index 00000000..7ab367f9
--- /dev/null
+++ b/kopete/plugins/translator/Makefile.am
@@ -0,0 +1,25 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_translator.la kcm_kopete_translator.la
+
+kopete_translator_la_SOURCES = translatorplugin.cpp \
+ translatordialog.cpp translatorguiclient.cpp translatorlanguages.cpp
+kopete_translator_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_translator_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KIO)
+
+kcm_kopete_translator_la_SOURCES = translatorprefsbase.ui translatorprefs.cpp translatorlanguages.cpp
+kcm_kopete_translator_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_translator_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_translator.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_translator_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+mydatadir = $(kde_datadir)/kopete_translator
+mydata_DATA = translatorui.rc translatorchatui.rc
+
+
diff --git a/kopete/plugins/translator/kopete_translator.desktop b/kopete/plugins/translator/kopete_translator.desktop
new file mode 100644
index 00000000..5da3f373
--- /dev/null
+++ b/kopete/plugins/translator/kopete_translator.desktop
@@ -0,0 +1,128 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=locale
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_translator
+X-KDE-PluginInfo-Author=Duncan Mac-Vicar Prett
+X-KDE-PluginInfo-Email=duncan@kde.org
+X-KDE-PluginInfo-Name=kopete_translator
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://kopete.kde.org
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=Translator
+Name[ar]=المترجم
+Name[be]=Перакладнік
+Name[bg]=Ðвтоматичен превод
+Name[bn]=অনà§à¦¬à¦¾à¦¦à¦•
+Name[br]=Troer
+Name[bs]=Prevodilac
+Name[ca]=Traductor
+Name[cs]=PÅ™ekladaÄ
+Name[cy]=Cyfieithydd
+Name[da]=Oversætter
+Name[de]=Ãœbersetzer
+Name[el]=ΜεταφÏαστής
+Name[eo]=Tradukilo
+Name[es]=Traductor
+Name[et]=Tõlketeenus
+Name[eu]=Itzultzailea
+Name[fa]=مترجم
+Name[fi]=Kääntäjä
+Name[fr]=Traducteur
+Name[ga]=Aistritheoir
+Name[gl]=Traductor
+Name[he]=מתרג×
+Name[hi]=अनà¥à¤µà¤¾à¤¦à¤•
+Name[hr]=Prevoditelj
+Name[hu]=Tolmács
+Name[is]=Þýðandi
+Name[it]=Traduttore
+Name[ja]=翻訳ソフト
+Name[ka]=მთáƒáƒ áƒ’მნელი
+Name[km]=កម្មវិធី​បកប្រែ
+Name[lt]=VertÄ—jas
+Name[mk]=Преведувач
+Name[nb]=Oversetter
+Name[nds]=Översetter
+Name[ne]=अनà¥à¤µà¤¾à¤¦à¤•
+Name[nl]=Vertaler
+Name[nn]=Omsetjar
+Name[pa]=ਅਨà©à¨µà¨¾à¨¦
+Name[pl]=TÅ‚umacz
+Name[pt]=Tradutor
+Name[pt_BR]=Tradutor
+Name[ro]=Traducător
+Name[ru]=Переводчик
+Name[se]=Jorgaleaddji
+Name[sk]=Preklad
+Name[sl]=Prevajalec
+Name[sr]=Преводилац
+Name[sr@Latn]=Prevodilac
+Name[sv]=Översättning
+Name[ta]=மொழிமாறà¯à®±à®¿
+Name[tg]=Тарҷумон
+Name[tr]=Çevirmen
+Name[uk]=Перекладач
+Name[uz]=Tarjimon
+Name[uz@cyrillic]=Таржимон
+Name[wa]=Ratournaedje
+Name[zh_CN]=翻译家
+Name[zh_HK]=翻譯者
+Name[zh_TW]=翻譯器
+Comment=Chat with foreign buddies in your native language
+Comment[ar]=تحدث مع أصدقائك اﻷجانب بلغتك اﻷم
+Comment[be]=Гутарка з замежнікамі на роднай мове
+Comment[bg]=ПриÑтавка за превод от един език на друг в реално време
+Comment[bn]=আপনার সà§à¦¥à¦¾à¦¨à§€à§Ÿ ভাষাতে বিদেশী বনà§à¦§à§à¦° সঙà§à¦—ে চà§à¦¯à¦¾à¦Ÿ করà§à¦¨
+Comment[bs]=Razgovarajte sa prijateljima iz inostranstva u vašem jeziku
+Comment[ca]=Feu un xat amb els vostres amics en la vostra llengua nativa
+Comment[cs]=Pokecejte si s cizinci ve svém rodném jazyku
+Comment[cy]=Sgwrsio â chyfeillion tramor yn eich iaith gyntaf
+Comment[da]=Chat med udenlandske venner på dit eget sprog
+Comment[de]=Chatten mit fremdsprachigen Freunden in der eigenen Muttersprache
+Comment[el]=Συνομιλήστε με ξένους φίλους στη μητÏική σας γλώσσα
+Comment[es]=Charle con compañeros extranjeros en su lengua
+Comment[et]=Vestlemine välismaiste semudega enda emakeeles
+Comment[eu]=Atzerriko lagunekin zure ama-hizkuntzan hitz egin
+Comment[fa]=گپ زدن با دوستان خارجی با زبان مادریتان
+Comment[fi]=Juttele ulkomaisten kaveriesi kanssa omalla kielelläsi
+Comment[fr]=Discutez avec des étrangers dans votre langue natale
+Comment[gl]=Fala con amigos extranxeiros na túa lingua nativa
+Comment[he]=שוחח ×¢× ×—×‘×¨×™× ×ž×—×•"ל בשפת ×”×× ×©×œ×š
+Comment[hi]=आपकी मातृ भाषा में विदेशी बडà¥à¤¡à¥€à¤¸ से गपशप करें
+Comment[hr]=Razgovo sa stranim prijateljima na vašem materinjem jeziku
+Comment[hu]=Csevegés más nyelvet beszélőkkel a saját nyelven
+Comment[is]=rabbaðu við útlenda félaga á þínu máli
+Comment[it]=Parla con un tuo amico straniero nella tua lingua
+Comment[ja]=æ¯å›½èªžã®ã¾ã¾ã§ä»–国ã®äººã¨ãƒãƒ£ãƒƒãƒˆã™ã‚‹
+Comment[ka]=უცხáƒáƒ”ლ მეგáƒáƒ‘რებთáƒáƒœ სáƒáƒ™áƒ£áƒ—áƒáƒ  ენáƒáƒ–ე სáƒáƒ£áƒ‘áƒáƒ áƒ˜
+Comment[kk]=Шетел доÑтарымен өз тіліңізде әңгімелеÑу
+Comment[km]=ជជែក​កំសាន្ážâ€‹áž‡áž¶áž˜áž½áž™â€‹ážŸáž˜áŸ’លាញ់​ជនជាážáž·â€‹áž”ážšáž‘áŸážŸ ដោយ​ប្រើ​ភាសា​កំណើážâ€‹ážšáž”ស់​អ្នក
+Comment[lt]=Bendraukite su biÄiuliais iÅ¡ užsieno gimtÄ…ja kalba
+Comment[mk]=Разговарајте Ñо ÑтранÑки пријатели на вашиот мајчин јазик
+Comment[nb]=Snakk med utenlandske venner på ditt eget språk
+Comment[nds]=Klöön in Dien Moderspraak mit frömdspreken Frünnen
+Comment[ne]=तपाईà¤à¤•à¥‹ मातृ भाषामा विदेशी साथीसà¤à¤— कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€ गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Praat met vrienden uit andere landen in uw eigen taal
+Comment[nn]=Prat med utanlandske venner på ditt eige språk
+Comment[pl]=Umożliwia rozmowę z zagranicznymi znajomymi w Twoim macierzystym języku
+Comment[pt]=Converse com amigos estrangeiros na sua língua nativa
+Comment[pt_BR]=Conversa com colegas estrangeiros em seu idioma nativo
+Comment[ru]=ПозволÑет говорить Ñ Ð¸Ð½Ð¾Ñтранцами на вашем родном Ñзыке
+Comment[se]=Čátte olgoriikkalaš ustibiiguin iežat gillii
+Comment[sk]=Rozprávajte sa s vašimi priateľmi v cudzine v inom jazyku
+Comment[sl]=Klepetajte s prijatelji iz tujine v svojem domaÄem jeziku
+Comment[sr]=ЋаÑкајте Ñа вашим другарима Ñтранцима на вашем матерњем језику
+Comment[sr@Latn]=Ćaskajte sa vašim drugarima strancima na vašem maternjem jeziku
+Comment[sv]=Chatta med utländska kompisar på ditt modersmål
+Comment[ta]=வெளிநாடà¯à®Ÿà¯ நபரà¯à®Ÿà®©à¯ உஙà¯à®•à®³à¯ சொநà¯à®¤ மொழியில௠அரடà¯à®Ÿà¯ˆ அடிகà¯à®• à®®à¯à®Ÿà®¿à®¯à¯à®®à¯
+Comment[tg]=Чат бо дӯÑтони хориҷӣ дар забони модарии шумо
+Comment[tr]=Anadilinizdeki arkadaÅŸlarla sohbet edin
+Comment[uk]=Розмова з іноземними друзÑми на вашій рідній мові
+Comment[zh_CN]=以您的æ¯è¯­ä¸Žå¤–国朋å‹èŠå¤©
+Comment[zh_HK]=以您的æ¯èªžå’Œå¤–地的伙伴èŠå¤©
+Comment[zh_TW]=用您的æ¯èªžè·Ÿå¤–國朋å‹èŠå¤©
diff --git a/kopete/plugins/translator/kopete_translator_config.desktop b/kopete/plugins/translator/kopete_translator_config.desktop
new file mode 100644
index 00000000..288709c4
--- /dev/null
+++ b/kopete/plugins/translator/kopete_translator_config.desktop
@@ -0,0 +1,127 @@
+[Desktop Entry]
+Icon=locale
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_translator
+X-KDE-FactoryName=TranslatorConfigFactory
+X-KDE-ParentApp=kopete_translator
+X-KDE-ParentComponents=kopete_translator
+
+Name=Translator
+Name[ar]=المترجم
+Name[be]=Перакладнік
+Name[bg]=Ðвтоматичен превод
+Name[bn]=অনà§à¦¬à¦¾à¦¦à¦•
+Name[br]=Troer
+Name[bs]=Prevodilac
+Name[ca]=Traductor
+Name[cs]=PÅ™ekladaÄ
+Name[cy]=Cyfieithydd
+Name[da]=Oversætter
+Name[de]=Ãœbersetzer
+Name[el]=ΜεταφÏαστής
+Name[eo]=Tradukilo
+Name[es]=Traductor
+Name[et]=Tõlketeenus
+Name[eu]=Itzultzailea
+Name[fa]=مترجم
+Name[fi]=Kääntäjä
+Name[fr]=Traducteur
+Name[ga]=Aistritheoir
+Name[gl]=Traductor
+Name[he]=מתרג×
+Name[hi]=अनà¥à¤µà¤¾à¤¦à¤•
+Name[hr]=Prevoditelj
+Name[hu]=Tolmács
+Name[is]=Þýðandi
+Name[it]=Traduttore
+Name[ja]=翻訳ソフト
+Name[ka]=მთáƒáƒ áƒ’მნელი
+Name[km]=កម្មវិធី​បកប្រែ
+Name[lt]=VertÄ—jas
+Name[mk]=Преведувач
+Name[nb]=Oversetter
+Name[nds]=Översetter
+Name[ne]=अनà¥à¤µà¤¾à¤¦à¤•
+Name[nl]=Vertaler
+Name[nn]=Omsetjar
+Name[pa]=ਅਨà©à¨µà¨¾à¨¦
+Name[pl]=TÅ‚umacz
+Name[pt]=Tradutor
+Name[pt_BR]=Tradutor
+Name[ro]=Traducător
+Name[ru]=Переводчик
+Name[se]=Jorgaleaddji
+Name[sk]=Preklad
+Name[sl]=Prevajalec
+Name[sr]=Преводилац
+Name[sr@Latn]=Prevodilac
+Name[sv]=Översättning
+Name[ta]=மொழிமாறà¯à®±à®¿
+Name[tg]=Тарҷумон
+Name[tr]=Çevirmen
+Name[uk]=Перекладач
+Name[uz]=Tarjimon
+Name[uz@cyrillic]=Таржимон
+Name[wa]=Ratournaedje
+Name[zh_CN]=翻译家
+Name[zh_HK]=翻譯者
+Name[zh_TW]=翻譯器
+Comment=Translates messages from your native language to another language
+Comment[ar]=يترجم رسائل من لغتك اﻷم إلى لغة أخرى
+Comment[be]=Перакладае паведамленні з роднай мовы на замежныÑ
+Comment[bg]=ПриÑтавка за превод от един език на друг в реално време
+Comment[bn]=আপনার সà§à¦¥à¦¾à¦¨à§€à§Ÿ ভাষা থেকে অনà§à¦¯ à¦à¦•à¦Ÿà¦¿ ভাষাতে বারà§à¦¤à¦¾ অনà§à¦¬à¦¾à¦¦ করে
+Comment[bs]=Prevodi poruke iz vašeg maternjeg jezika u neki drugi
+Comment[ca]=Tradueix els missatges des del vostre idioma natiu cap a un altre
+Comment[cs]=Překládá zprávy z vašeho rodného jazyka do jiného
+Comment[cy]=Cyfieithu negeseuon o'ch iaith gyntaf i iaith arall
+Comment[da]=Oversætter beskeder fra dit indfødte sprog til et andet sprog
+Comment[de]=Ãœbersetzt Nachrichten von der eigenen in eine fremde Sprache
+Comment[el]=ΜεταφÏάζει μηνÏματα από τη μητÏική σας γλώσσα σε μια άλλη
+Comment[es]=Traduce mensajes de su lengua a otra
+Comment[et]=Tõlgib sõnumid sinu emakeelest mingisse muusse keelde
+Comment[eu]=Mezuak zure ama-hizkuntzatik beste hizkuntzara itzultzen ditu
+Comment[fa]=ترجمۀ پیامهایی با زبان مادری شما به زبانهای دیگر
+Comment[fi]=Kääntää viestit omasta kielestä toiseen kieleen
+Comment[fr]=Traduit les messages de votre langue natale en une autre langue
+Comment[ga]=Aistríonn seo teachtaireachtaí ó do theanga dúchais go teanga eile
+Comment[gl]=Traduce as túas mensaxes da túa lingua nativa a outra
+Comment[he]=×ž×ª×¨×’× ×ת מסריך משפת ×”×× ×©×œ×š לשפה ×חרת
+Comment[hi]=आपकी मातृ भाषा के संदेशों को अनà¥à¤¯ भाषा में अनà¥à¤µà¤¾à¤¦ करे
+Comment[hr]=Prevodi poruke s vašeg maternjeg jezika na neki drugi
+Comment[hu]=Az üzenetek lefordítása a saját nyelvről egy idegen nyelvre
+Comment[is]=Þýðir skeyti úr þinni tungu yfir í annað
+Comment[it]=Traduci messaggi dalla tua lingua in un'altra
+Comment[ja]=æ¯å›½èªžã‹ã‚‰ä»–ã®è¨€èªžã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’翻訳ã™ã‚‹
+Comment[ka]=შეტყáƒáƒ‘ინებებს თáƒáƒ áƒ’მნის თქვენი მშáƒáƒ‘ლიური ენიდáƒáƒœ სხვრენáƒáƒ–ე
+Comment[kk]=Хабарларды бір тілден баÑқа тілге аудару
+Comment[km]=បកប្រែ​សារ​ពី​ភាសា​កំណើážâ€‹ážšáž”ស់​អ្នក​ទៅ​ជា​ភាសា​មួយ​ទៀáž
+Comment[lt]=VerÄia žinutes iÅ¡ gimtosios kalbos į užsienio kalbas
+Comment[mk]=Ги преведува пораките од вашиот мајчин јазик на друг јазик
+Comment[nb]=Oversetter meldinger fra ditt eget språk til et annet
+Comment[nds]=Översett Narichten vun Dien Moderspraak na anner Spraken
+Comment[ne]=तपाईठमातृ भाषाबाट अरà¥à¤•à¥‹ भाषामा सनà¥à¤¦à¥‡à¤¶ अनà¥à¤µà¤¾à¤¦ गरà¥à¤¦à¤›
+Comment[nl]=Vertaalt berichten van uw eigen taal naar een andere taal
+Comment[nn]=Set om meldingar frå ditt eige språk til eit anna
+Comment[pl]=Tłumaczy wiadomości z Twojego języka na inny
+Comment[pt]=Traduz as mensagens da sua língua nativa para outra língua
+Comment[pt_BR]=Traduz mensagens de seu idioma nativo para outro idioma
+Comment[ru]=Переводит Ñообщение Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ родного Ñзыка на Ñзык вашего ÑобеÑедника
+Comment[se]=Jorgala dieđáhusaid du gielas muhton eará gillii
+Comment[sk]=Prekladá správy z jedného jazyka do iného
+Comment[sl]=Prevede sporoÄila iz vaÅ¡ega domaÄega jezika v drugi jezik
+Comment[sr]=Преводи поруке Ñа вашег матерњег језика на неки други
+Comment[sr@Latn]=Prevodi poruke sa vašeg maternjeg jezika na neki drugi
+Comment[sv]=Översätt meddelanden från ditt modersmål till ett annat språk
+Comment[ta]=உஙà¯à®•à®³à¯ மொழியிலிரà¯à®¨à¯à®¤à¯ வேற௠மொழிகà¯à®•à¯ மொழிபெயரà¯
+Comment[tg]=Пайёмҳоро аз забони модарии шумо ба дигар забонҳо тарҷума мекунад
+Comment[tr]=Diğer dillerden ana dilinize çevrilen mesajlar
+Comment[uk]=Перекладає Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· вашої рідної мови на ÑкуÑÑŒ іншу мову
+Comment[zh_CN]=将消æ¯ä»Žæ‚¨çš„æ¯è¯­ç¿»è¯‘æˆå…¶å®ƒè¯­è¨€
+Comment[zh_HK]=將您æ¯èªžçš„訊æ¯ç¿»è­¯ç‚ºå¦ä¸€ç¨®èªžè¨€
+Comment[zh_TW]=將訊æ¯å¾žæ‚¨çš„æ¯èªžç¿»æˆå…¶ä»–語言
+
+
diff --git a/kopete/plugins/translator/translatorchatui.rc b/kopete/plugins/translator/translatorchatui.rc
new file mode 100644
index 00000000..7dc86e2c
--- /dev/null
+++ b/kopete/plugins/translator/translatorchatui.rc
@@ -0,0 +1,9 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="1" name="kopete_translatorchat">
+ <MenuBar>
+ <Menu name="tools">
+ <text>&amp;Tools</text>
+ <Action name="translateCurrentMessage" />
+ </Menu>
+ </MenuBar>
+</kpartgui>
diff --git a/kopete/plugins/translator/translatordialog.cpp b/kopete/plugins/translator/translatordialog.cpp
new file mode 100644
index 00000000..c03921fc
--- /dev/null
+++ b/kopete/plugins/translator/translatordialog.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ translatordialog.cpp - description
+ -------------------
+ begin : sam oct 19 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <klocale.h>
+#include <ktextedit.h>
+
+#include "translatordialog.h"
+
+
+TranslatorDialog::TranslatorDialog(const QString &text,QWidget *parent, const char *name ) : KDialogBase(parent,name,true,i18n("Translator Plugin"), Ok)
+{
+ m_textEdit=new KTextEdit(this);
+ setMainWidget(m_textEdit);
+ m_textEdit->setText(text);
+}
+TranslatorDialog::~TranslatorDialog()
+{
+}
+
+QString TranslatorDialog::translatedText()
+{
+ return m_textEdit->text();
+}
+/*void TranslatorDialog::slotFinished()
+{
+ emit finished(m_textEdit->text());
+} */
+
+
+#include "translatordialog.moc"
diff --git a/kopete/plugins/translator/translatordialog.h b/kopete/plugins/translator/translatordialog.h
new file mode 100644
index 00000000..70fd46c0
--- /dev/null
+++ b/kopete/plugins/translator/translatordialog.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ translatordialog.h - description
+ -------------------
+ begin : sam oct 19 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TRANSLATORDIALOG_H
+#define TRANSLATORDIALOG_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+
+//#include <kopetemessage.h>
+
+class KTextEdit;
+
+/**
+ * @author Olivier Goffart
+ */
+class TranslatorDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ TranslatorDialog(const QString &translated, QWidget *parent=0, const char *name=0);
+ ~TranslatorDialog();
+
+ QString translatedText();
+
+private:
+ KTextEdit *m_textEdit;
+
+private slots: // Public slots
+// void slotFinished();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/translator/translatorguiclient.cpp b/kopete/plugins/translator/translatorguiclient.cpp
new file mode 100644
index 00000000..ae175f41
--- /dev/null
+++ b/kopete/plugins/translator/translatorguiclient.cpp
@@ -0,0 +1,100 @@
+/*
+ translatorguiclient.cpp
+
+ Kopete Translator plugin
+
+ Copyright (c) 2003-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvariant.h>
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include "kopetemessagemanager.h"
+#include "kopeteview.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopetemessage.h"
+
+#include "translatorplugin.h"
+#include "translatorguiclient.h"
+#include "translatorlanguages.h"
+
+TranslatorGUIClient::TranslatorGUIClient( Kopete::ChatSession *parent, const char *name )
+: QObject( parent, name ), KXMLGUIClient( parent )
+{
+ setInstance( TranslatorPlugin::plugin()->instance() );
+ connect( TranslatorPlugin::plugin(), SIGNAL( destroyed( QObject * ) ), this, SLOT( deleteLater() ) );
+
+ m_manager = parent;
+
+ new KAction( i18n( "Translate" ), "locale", CTRL + Key_T, this, SLOT( slotTranslateChat() ), actionCollection(), "translateCurrentMessage" );
+
+ setXMLFile( "translatorchatui.rc" );
+}
+
+TranslatorGUIClient::~TranslatorGUIClient()
+{
+}
+
+void TranslatorGUIClient::slotTranslateChat()
+{
+ if ( !m_manager->view() )
+ return;
+
+ Kopete::Message msg = m_manager->view()->currentMessage();
+ QString body = msg.plainBody();
+ if ( body.isEmpty() )
+ return;
+
+ QString src_lang = TranslatorPlugin::plugin()->m_myLang;
+ QString dst_lang;
+
+ QPtrList<Kopete::Contact> list = m_manager->members();
+ Kopete::MetaContact *to = list.first()->metaContact();
+ dst_lang = to->pluginData( TranslatorPlugin::plugin(), "languageKey" );
+ if ( dst_lang.isEmpty() || dst_lang == "null" )
+ {
+ kdDebug( 14308 ) << k_funcinfo << "Cannot determine dst Metacontact language (" << to->displayName() << ")" << endl;
+ return;
+ }
+
+ // We search for src_dst
+ TranslatorPlugin::plugin()->translateMessage( body, src_lang, dst_lang, this, SLOT( messageTranslated( const QVariant & ) ) );
+}
+
+void TranslatorGUIClient::messageTranslated( const QVariant &result )
+{
+ QString translated = result.toString();
+ if ( translated.isEmpty() )
+ {
+ kdDebug( 14308 ) << k_funcinfo << "Empty string returned" << endl;
+ return;
+ }
+
+ //if the user close the window before the translation arrive, return
+ if ( !m_manager->view() )
+ return;
+
+ Kopete::Message msg = m_manager->view()->currentMessage();
+ msg.setBody( translated );
+ m_manager->view()->setCurrentMessage( msg );
+}
+
+#include "translatorguiclient.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/translator/translatorguiclient.h b/kopete/plugins/translator/translatorguiclient.h
new file mode 100644
index 00000000..32ff015f
--- /dev/null
+++ b/kopete/plugins/translator/translatorguiclient.h
@@ -0,0 +1,63 @@
+/*
+ translatorguiclient.h
+
+ Kopete Translator Plugin
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSLATORGUICLIENT_H
+#define TRANSLATORGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+#include <kio/job.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+namespace Kopete { class ChatSession; }
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+
+class TranslatorGUIClient : public QObject , public KXMLGUIClient
+{
+ Q_OBJECT
+
+public:
+ TranslatorGUIClient( Kopete::ChatSession *parent, const char *name=0L);
+ ~TranslatorGUIClient();
+
+private slots:
+ void slotTranslateChat();
+ void messageTranslated(const QVariant&);
+
+private:
+ Kopete::ChatSession *m_manager;
+};
+
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/translator/translatorlanguages.cpp b/kopete/plugins/translator/translatorlanguages.cpp
new file mode 100644
index 00000000..4e59fa79
--- /dev/null
+++ b/kopete/plugins/translator/translatorlanguages.cpp
@@ -0,0 +1,101 @@
+/*
+ translatorlanguages.cpp
+
+ Kopete Translatorfish Translator plugin
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstring.h>
+#include <qmap.h>
+#include <klocale.h>
+
+#include "translatorlanguages.h"
+
+TranslatorLanguages::TranslatorLanguages()
+{
+ m_lc = 0;
+ m_sc = 0;
+ m_services.insert("babelfish", "BabelFish");
+ m_services.insert("google", "Google");
+
+ m_langs.insert("null", i18n("Unknown"));
+ m_langs.insert("en", i18n("English"));
+ m_langs.insert("zh", i18n("Chinese"));
+ m_langs.insert("fr", i18n("French"));
+ m_langs.insert("de", i18n("German"));
+ m_langs.insert("it", i18n("Italian"));
+ m_langs.insert("ja", i18n("Japanese"));
+ m_langs.insert("ko", i18n("Korean"));
+ m_langs.insert("pt", i18n("Portuguese"));
+ m_langs.insert("ru", i18n("Russian"));
+ m_langs.insert("es", i18n("Spanish"));
+
+ /* English to .. */
+ m_supported["babelfish"].append("en_zh");
+ m_supported["babelfish"].append("en_fr");
+ m_supported["babelfish"].append("en_de");
+ m_supported["babelfish"].append("en_it");
+ m_supported["babelfish"].append("en_ja");
+ m_supported["babelfish"].append("en_ko");
+ m_supported["babelfish"].append("en_pt");
+ m_supported["babelfish"].append("en_es");
+ /* Chinese to .. */
+ m_supported["babelfish"].append("zh_en");
+ /* French to ... */
+ m_supported["babelfish"].append("fr_en");
+ m_supported["babelfish"].append("fr_de");
+ /* German to ... */
+ m_supported["babelfish"].append("de_en");
+ m_supported["babelfish"].append("de_fr");
+
+ m_supported["babelfish"].append("it_en");
+ m_supported["babelfish"].append("ja_en");
+ m_supported["babelfish"].append("ko_en");
+ m_supported["babelfish"].append("pt_en");
+ m_supported["babelfish"].append("ru_en");
+ m_supported["babelfish"].append("es_en");
+
+ /* Google Service */
+ m_supported["google"].append("en_de");
+ m_supported["google"].append("en_es");
+ m_supported["google"].append("en_fr");
+ m_supported["google"].append("en_it");
+ m_supported["google"].append("en_pt");
+ m_supported["google"].append("de_en");
+ m_supported["google"].append("de_fr");
+ m_supported["google"].append("es_en");
+ m_supported["google"].append("fr_en");
+ m_supported["google"].append("fr_de");
+ m_supported["google"].append("it_en");
+ m_supported["google"].append("pt_en");
+
+ QMap<QString,QString>::ConstIterator i;
+
+ for ( i = m_langs.begin(); i != m_langs.end() ; ++i )
+ {
+ m_langIntKeyMap[m_lc] = i.key();
+ m_langKeyIntMap[i.key()] = m_lc;
+ m_lc++;
+ }
+
+ for ( i = m_services.begin(); i != m_services.end() ; ++i )
+ {
+ m_servicesIntKeyMap[m_sc] = i.key();
+ m_servicesKeyIntMap[i.key()] = m_sc;
+ m_sc++;
+ }
+}
diff --git a/kopete/plugins/translator/translatorlanguages.h b/kopete/plugins/translator/translatorlanguages.h
new file mode 100644
index 00000000..cbd6385f
--- /dev/null
+++ b/kopete/plugins/translator/translatorlanguages.h
@@ -0,0 +1,94 @@
+/*
+ translatorlanguages.h
+
+ Kopete Translatorfish Translator plugin
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Matt Rogers <matt@matt.rogers.name>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSLATORLANGUAGES_H_
+#define TRANSLATORLANGUAGES_H_
+
+#include <qmap.h>
+#include <qstringlist.h>
+
+class QString;
+
+
+class TranslatorLanguages
+{
+public:
+ TranslatorLanguages();
+
+ /***************************************************************************
+ * Language APIs *
+ ***************************************************************************/
+
+ const QString& languageName( const QString &key )
+ { return m_langs[key]; };
+
+ const int languageIndex ( const QString &key )
+ { return m_langKeyIntMap[key]; };
+
+ const QString& languageKey( const int index )
+ { return m_langIntKeyMap[index]; };
+
+ const QMap<QString,QString>& languagesMap()
+ { return m_langs; };
+
+ const QMap<QString,QString>& servicesMap()
+ { return m_services; };
+
+ const QStringList& supported( const QString &servicekey )
+ { return m_supported[servicekey]; };
+
+ const int serviceIndex ( const QString &key )
+ { return m_servicesKeyIntMap[key]; };
+
+ const QString& serviceKey( const int index )
+ { return m_servicesIntKeyMap[index]; };
+
+ int numLanguages() { return m_lc; };
+
+ int numServices() { return m_sc; };
+
+private:
+
+ /* Known Languages key -> desc ie: en -> English */
+ QMap< QString, QString> m_langs;
+
+ /* Known Services key -> desc ie: en -> English */
+ QMap< QString, QString> m_services;
+
+ /* Supported translations per service, src_dst format ( ie: en_es )*/
+ QMap< QString, QStringList > m_supported;
+
+ /* int to lang key and viceversa*/
+ QMap<int, QString> m_langIntKeyMap;
+ QMap<QString, int> m_langKeyIntMap;
+
+ /* int to services key and viceversa*/
+ QMap<int, QString> m_servicesIntKeyMap;
+ QMap<QString, int> m_servicesKeyIntMap;
+
+ /* Lang counter */
+ int m_lc;
+ /* Service counter */
+ int m_sc;
+
+};
+
+#endif
diff --git a/kopete/plugins/translator/translatorplugin.cpp b/kopete/plugins/translator/translatorplugin.cpp
new file mode 100644
index 00000000..694f0bd1
--- /dev/null
+++ b/kopete/plugins/translator/translatorplugin.cpp
@@ -0,0 +1,402 @@
+/*
+ translatorplugin.cpp
+
+ Kopete Translator plugin
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qsignal.h>
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+#include <kaboutdata.h>
+
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetemessagemanagerfactory.h"
+#include "kopetecontact.h"
+
+#include "translatorplugin.h"
+#include "translatordialog.h"
+#include "translatorguiclient.h"
+#include "translatorlanguages.h"
+
+typedef KGenericFactory<TranslatorPlugin> TranslatorPluginFactory;
+#if KDE_IS_VERSION(3,2,90)
+static const KAboutData aboutdata("kopete_translator", I18N_NOOP("Translator") , "1.0" );
+K_EXPORT_COMPONENT_FACTORY( kopete_translator, TranslatorPluginFactory( &aboutdata ) )
+#else
+K_EXPORT_COMPONENT_FACTORY( kopete_translator, TranslatorPluginFactory( "kopete_translator" ) )
+#endif
+
+TranslatorPlugin::TranslatorPlugin( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Plugin( TranslatorPluginFactory::instance(), parent, name )
+{
+ kdDebug( 14308 ) << k_funcinfo << endl;
+
+
+ if ( pluginStatic_ )
+ kdWarning( 14308 ) << k_funcinfo << "Translator already initialized" << endl;
+ else
+ pluginStatic_ = this;
+
+ m_languages = new TranslatorLanguages;
+
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToDisplay( Kopete::Message & ) ),
+ this, SLOT( slotIncomingMessage( Kopete::Message & ) ) );
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( aboutToSend( Kopete::Message & ) ),
+ this, SLOT( slotOutgoingMessage( Kopete::Message & ) ) );
+ connect( Kopete::ChatSessionManager::self(), SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ),
+ this, SLOT( slotNewKMM( Kopete::ChatSession * ) ) );
+
+ QStringList keys;
+ QMap<QString, QString> m = m_languages->languagesMap();
+ for ( int k = 0; k <= m_languages->numLanguages(); k++ )
+ keys << m[ m_languages->languageKey( k ) ];
+
+ m_actionLanguage = new KSelectAction( i18n( "Set &Language" ), "locale", 0, actionCollection(), "contactLanguage" );
+ m_actionLanguage->setItems( keys );
+ connect( m_actionLanguage, SIGNAL( activated() ), this, SLOT(slotSetLanguage() ) );
+ connect( Kopete::ContactList::self(), SIGNAL( metaContactSelected( bool ) ), this, SLOT( slotSelectionChanged( bool ) ) );
+
+ setXMLFile( "translatorui.rc" );
+
+ //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining)
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueListIterator<Kopete::ChatSession*> it= sessions.begin(); it!=sessions.end() ; ++it)
+ slotNewKMM( *it );
+
+ loadSettings();
+ connect( this, SIGNAL( settingsChanged() ), this, SLOT( loadSettings() ) );
+}
+
+TranslatorPlugin::~TranslatorPlugin()
+{
+ kdDebug( 14308 ) << k_funcinfo << endl;
+ pluginStatic_ = 0L;
+}
+
+TranslatorPlugin* TranslatorPlugin::plugin()
+{
+ return pluginStatic_;
+}
+
+TranslatorPlugin* TranslatorPlugin::pluginStatic_ = 0L;
+
+void TranslatorPlugin::loadSettings()
+{
+ KConfig *config = KGlobal::config();
+ int mode = 0;
+
+ config->setGroup( "Translator Plugin" );
+ m_myLang = m_languages->languageKey( config->readNumEntry( "myLang" , 0 ) );
+ m_service = m_languages->serviceKey( config->readNumEntry( "Service", 0 ) );
+
+ if ( config->readBoolEntry( "IncomingDontTranslate", true ) )
+ mode = 0;
+ else if ( config->readBoolEntry( "IncomingShowOriginal", false ) )
+ mode = 1;
+ else if ( config->readBoolEntry( "IncomingTranslate", false ) )
+ mode = 2;
+
+ m_incomingMode = mode;
+
+ if ( config->readBoolEntry( "OutgoingDontTranslate", true ) )
+ mode = 0;
+ else if ( config->readBoolEntry( "OutgoingShowOriginal", false ) )
+ mode = 1;
+ else if ( config->readBoolEntry( "OutgoingTranslate", false ) )
+ mode = 2;
+ else if ( config->readBoolEntry( "OutgoingAsk", false ) )
+ mode = 3;
+
+ m_outgoingMode = mode;
+}
+
+void TranslatorPlugin::slotSelectionChanged( bool b )
+{
+ m_actionLanguage->setEnabled( b );
+
+ if ( !b )
+ return;
+
+ Kopete::MetaContact *m = Kopete::ContactList::self()->selectedMetaContacts().first();
+
+ if( !m )
+ return;
+
+ QString languageKey = m->pluginData( this, "languageKey" );
+ if ( !languageKey.isEmpty() && languageKey != "null" )
+ m_actionLanguage->setCurrentItem( m_languages->languageIndex( languageKey ) );
+ else
+ m_actionLanguage->setCurrentItem( m_languages->languageIndex( "null" ) );
+}
+
+void TranslatorPlugin::slotNewKMM( Kopete::ChatSession *KMM )
+{
+ new TranslatorGUIClient( KMM );
+}
+
+void TranslatorPlugin::slotIncomingMessage( Kopete::Message &msg )
+{
+ if ( m_incomingMode == DontTranslate )
+ return;
+
+ QString src_lang;
+ QString dst_lang;
+
+ if ( ( msg.direction() == Kopete::Message::Inbound ) && !msg.plainBody().isEmpty() )
+ {
+ Kopete::MetaContact *from = msg.from()->metaContact();
+ if ( !from )
+ {
+// kdDebug( 14308 ) << k_funcinfo << "No metaContact for source contact" << endl;
+ return;
+ }
+ src_lang = from->pluginData( this, "languageKey" );
+ if( src_lang.isEmpty() || src_lang == "null" )
+ {
+// kdDebug( 14308 ) << k_funcinfo << "Cannot determine src Metacontact language (" << from->displayName() << ")" << endl;
+ return;
+ }
+
+ dst_lang = m_myLang;
+
+ sendTranslation( msg, translateMessage( msg.plainBody(), src_lang, dst_lang ) );
+ }
+}
+
+void TranslatorPlugin::slotOutgoingMessage( Kopete::Message &msg )
+{
+ if ( m_outgoingMode == DontTranslate )
+ return;
+
+ QString src_lang;
+ QString dst_lang;
+
+ if ( ( msg.direction() == Kopete::Message::Outbound ) && ( !msg.plainBody().isEmpty() ) )
+ {
+ src_lang = m_myLang;
+
+ // Sad, we have to consider only the first To: metacontact
+ Kopete::MetaContact *to = msg.to().first()->metaContact();
+ if ( !to )
+ {
+// kdDebug( 14308 ) << k_funcinfo << "No metaContact for first contact" << endl;
+ return;
+ }
+ dst_lang = to->pluginData( this, "languageKey" );
+ if ( dst_lang.isEmpty() || dst_lang == "null" )
+ {
+// kdDebug( 14308 ) << k_funcinfo << "Cannot determine dst Metacontact language (" << to->displayName() << ")" << endl;
+ return;
+ }
+
+ sendTranslation( msg, translateMessage( msg.plainBody(), src_lang, dst_lang ) );
+ }
+}
+
+void TranslatorPlugin::translateMessage( const QString &msg, const QString &from, const QString &to, QObject *obj, const char* slot )
+{
+ QSignal completeSignal;
+ completeSignal.connect( obj, slot );
+
+ QString result = translateMessage( msg, from, to );
+
+ if(!result.isNull())
+ {
+ completeSignal.setValue( result );
+ completeSignal.activate();
+ }
+}
+
+QString TranslatorPlugin::translateMessage( const QString &msg, const QString &from, const QString &to )
+{
+ if ( from == to )
+ {
+ kdDebug( 14308 ) << k_funcinfo << "Src and Dst languages are the same" << endl;
+ return QString::null;
+ }
+
+ // We search for src_dst
+ if(! m_languages->supported( m_service ).contains( from + "_" + to ) )
+ {
+ kdDebug( 14308 ) << k_funcinfo << from << "_" << to << " is not supported by service " << m_service << endl;
+ return QString::null;
+ }
+
+
+ if ( m_service == "babelfish" )
+ return babelTranslateMessage( msg ,from, to );
+ else if ( m_service == "google" )
+ return googleTranslateMessage( msg ,from, to );
+ else
+ return QString::null;
+}
+
+QString TranslatorPlugin::googleTranslateMessage( const QString &msg, const QString &from, const QString &to )
+{
+ KURL translatorURL ( "http://translate.google.com/translate_t");
+
+ QString body = KURL::encode_string( msg );
+ QString lp = from + "|" + to;
+
+ QCString postData = QString( "text=" + body + "&langpair=" + lp ).utf8();
+
+ QString gurl = "http://translate.google.com/translate_t?text=" + body +"&langpair=" + lp;
+ kdDebug(14308) << k_funcinfo << " URL: " << gurl << endl;
+ KURL geturl ( gurl );
+
+ KIO::TransferJob *job = KIO::get( geturl, false, true );
+ //job = KIO::http_post( translatorURL, postData, true );
+
+ //job->addMetaData( "content-type", "application/x-www-form-urlencoded" );
+ //job->addMetaData( "referrer", "http://www.google.com" );
+
+ QObject::connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotDataReceived( KIO::Job *, const QByteArray & ) ) );
+ QObject::connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobDone( KIO::Job * ) ) );
+
+ // KIO is async and we use a sync API, so use the processEvents hack to work around that
+ // FIXME: We need to make the libkopete API async to get rid of this processEvents.
+ // It often causes crashes in the code. - Martijn
+ while ( !m_completed[ job ] )
+ qApp->processEvents();
+
+ QString data = QString::fromLatin1( m_data[ job ] );
+
+ // After hacks, we need to clean
+ m_data.remove( job );
+ m_completed.remove( job );
+
+// kdDebug( 14308 ) << k_funcinfo << "Google response:"<< endl << data << endl;
+
+ QRegExp re( "<textarea name=q rows=5 cols=45 wrap=PHYSICAL>(.*)</textarea>" );
+ re.setMinimal( true );
+ re.search( data );
+
+ return re.cap( 1 );
+}
+
+QString TranslatorPlugin::babelTranslateMessage( const QString &msg, const QString &from, const QString &to )
+{
+ QString body = KURL::encode_string( msg);
+ QString lp = from + "_" + to;
+ QString gurl = "http://babelfish.altavista.com/babelfish/tr?enc=utf8&doit=done&tt=urltext&urltext=" + body + "&lp=" + lp;
+ KURL geturl ( gurl );
+
+ kdDebug( 14308 ) << k_funcinfo << "URL: " << gurl << endl;
+
+ KIO::TransferJob *job = KIO::get( geturl, false, true );
+
+ QObject::connect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotDataReceived( KIO::Job *, const QByteArray & ) ) );
+ QObject::connect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobDone( KIO::Job * ) ) );
+
+ // KIO is async and we use a sync API, so use the processEvents hack to work around that
+ // FIXME: We need to make the libkopete API async to get rid of this processEvents.
+ // It often causes crashes in the code. - Martijn
+ while ( !m_completed[ job ] )
+ qApp->processEvents();
+
+ QString data = QString::fromUtf8( m_data[ job ] );
+
+ // After hacks, we need to clean
+ m_data.remove( job );
+ m_completed.remove( job );
+
+ //kdDebug( 14308 ) << k_funcinfo << "Babelfish response: " << endl << data << endl;
+
+ QRegExp re( "<Div style=padding:10px; lang=..>(.*)</div" );
+ re.setMinimal( true );
+ re.search( data );
+
+ return re.cap( 1 );
+}
+
+void TranslatorPlugin::sendTranslation( Kopete::Message &msg, const QString &translated )
+{
+ if ( translated.isEmpty() )
+ {
+ kdWarning( 14308 ) << k_funcinfo << "Translated text is empty" << endl;
+ return;
+ }
+
+ TranslateMode mode = DontTranslate;
+
+ switch ( msg.direction() )
+ {
+ case Kopete::Message::Outbound:
+ mode = TranslateMode( m_outgoingMode );
+ break;
+ case Kopete::Message::Inbound:
+ mode = TranslateMode( m_incomingMode );
+ break;
+ default:
+ kdWarning( 14308 ) << k_funcinfo << "Can't determine if it is an incoming or outgoing message" << endl;
+ };
+
+ switch ( mode )
+ {
+ case JustTranslate:
+ msg.setBody( translated, msg.format() );
+ break;
+ case ShowOriginal:
+ msg.setBody( i18n( "%2\nAuto Translated: %1" ).arg( translated, msg.plainBody() ), msg.format() );
+ break;
+ case ShowDialog:
+ {
+ TranslatorDialog *d = new TranslatorDialog( translated );
+ d->exec();
+ msg.setBody( d->translatedText(), msg.format() );
+ delete d;
+ break;
+ }
+ case DontTranslate:
+ default:
+ //do nothing
+ break;
+ };
+}
+
+void TranslatorPlugin::slotDataReceived ( KIO::Job *job, const QByteArray &data )
+{
+ m_data[ job ] += QCString( data, data.size() + 1 );
+}
+
+void TranslatorPlugin::slotJobDone ( KIO::Job *job )
+{
+ m_completed[ job ] = true;
+ QObject::disconnect( job, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotDataReceived( KIO::Job *, const QByteArray & ) ) );
+ QObject::disconnect( job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobDone( KIO::Job * ) ) );
+}
+
+void TranslatorPlugin::slotSetLanguage()
+{
+ Kopete::MetaContact *m = Kopete::ContactList::self()->selectedMetaContacts().first();
+ if( m && m_actionLanguage )
+ m->setPluginData( this, "languageKey", m_languages->languageKey( m_actionLanguage->currentItem() ) );
+}
+
+#include "translatorplugin.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/translator/translatorplugin.h b/kopete/plugins/translator/translatorplugin.h
new file mode 100644
index 00000000..2a04b509
--- /dev/null
+++ b/kopete/plugins/translator/translatorplugin.h
@@ -0,0 +1,113 @@
+/*
+ translatorplugin.h
+
+ Kopete Translatorfish Translator plugin
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef BABELFISHPLUGIN_H
+#define BABELFISHPLUGIN_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qcstring.h>
+#include <qintdict.h>
+
+
+#include <kio/job.h>
+
+#include "kopetemessage.h"
+#include "kopeteplugin.h"
+
+
+class QString;
+class KSelectAction;
+
+namespace Kopete { class Message; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class ChatSession; }
+
+class TranslatorPreferences;
+class TranslatorGUIClient;
+class TranslatorLanguages;
+
+/**
+ * @author Duncan Mac-Vicar Prett <duncan@kde.org>
+ *
+ * Kopete Translator Plugin
+ */
+class TranslatorPlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+friend class TranslatorGUIClient;
+
+public:
+ static TranslatorPlugin *plugin();
+
+ TranslatorPlugin( QObject *parent, const char *name, const QStringList &args );
+ ~TranslatorPlugin();
+
+ enum TranslateMode
+ {
+ DontTranslate = 0,
+ ShowOriginal = 1,
+ JustTranslate = 2,
+ ShowDialog = 3
+ };
+
+private slots:
+ void slotIncomingMessage( Kopete::Message& msg );
+ void slotOutgoingMessage( Kopete::Message& msg );
+ void slotDataReceived ( KIO::Job *, const QByteArray &data);
+ void slotJobDone ( KIO::Job *);
+ void slotSetLanguage();
+ void slotSelectionChanged(bool);
+ void slotNewKMM(Kopete::ChatSession *);
+ void loadSettings();
+
+public:
+ QString translateMessage( const QString &, const QString &, const QString & );
+ void translateMessage( const QString &, const QString &, const QString & , QObject * , const char*);
+
+protected:
+ QString googleTranslateMessage( const QString &, const QString &, const QString & );
+ QString babelTranslateMessage(const QString &, const QString &, const QString & );
+
+private:
+
+ QMap< KIO::Job *, QCString> m_data;
+ QMap< KIO::Job *, bool> m_completed;
+
+ KSelectAction* m_actionLanguage;
+
+ static TranslatorPlugin* pluginStatic_;
+ TranslatorLanguages *m_languages;
+
+ //Settings
+ QString m_myLang;
+ QString m_service;
+ unsigned int m_outgoingMode;
+ unsigned int m_incomingMode;
+
+private:
+ void sendTranslation(Kopete::Message &msg, const QString &translated);
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/plugins/translator/translatorprefs.cpp b/kopete/plugins/translator/translatorprefs.cpp
new file mode 100644
index 00000000..cb67c4c6
--- /dev/null
+++ b/kopete/plugins/translator/translatorprefs.cpp
@@ -0,0 +1,52 @@
+/*
+ translatorprefs.cpp - Kopete Translator plugin
+
+ Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include <kgenericfactory.h>
+#include "kcautoconfigmodule.h"
+#include "translatorprefsbase.h"
+#include "translatorlanguages.h"
+#include <qcombobox.h>
+
+
+class TranslatorPreferences;
+typedef KGenericFactory<TranslatorPreferences> TranslatorConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_translator, TranslatorConfigFactory( "kcm_kopete_translator" ) )
+
+class TranslatorPreferences : public KCAutoConfigModule
+{
+public:
+ TranslatorPreferences( QWidget *parent = 0, const char * = 0, const QStringList &args = QStringList() ) : KCAutoConfigModule( TranslatorConfigFactory::instance(), parent, args )
+ {
+ TranslatorPrefsUI *preferencesDialog = new TranslatorPrefsUI(this);
+
+ TranslatorLanguages languages;
+ QMap<QString,QString>::ConstIterator i;
+ QMap<QString,QString> m;
+
+ m = languages.languagesMap();
+ for ( i = m.begin(); i != m.end() ; ++i )
+ preferencesDialog->myLang->insertItem( i.data(), languages.languageIndex(i.key()) );
+
+ m = languages.servicesMap();
+ for ( i = m.begin(); i != m.end() ; ++i )
+ preferencesDialog->Service->insertItem( i.data(), languages.serviceIndex(i.key()) );
+
+ setMainWidget( preferencesDialog , "Translator Plugin");
+ }
+};
+
diff --git a/kopete/plugins/translator/translatorprefsbase.ui b/kopete/plugins/translator/translatorprefsbase.ui
new file mode 100644
index 00000000..56b75543
--- /dev/null
+++ b/kopete/plugins/translator/translatorprefsbase.ui
@@ -0,0 +1,191 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>TranslatorPrefsUI</class>
+<author>Duncan Mac-Vicar P.</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TranslatorPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>401</width>
+ <height>417</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>Service</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Translation service:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Default native language:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>myLang</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>IncomingMessages</cstring>
+ </property>
+ <property name="title">
+ <string>Incoming Messages</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>IncomingDontTranslate</cstring>
+ </property>
+ <property name="text">
+ <string>Do not translate</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>IncomingShowOriginal</cstring>
+ </property>
+ <property name="text">
+ <string>Show the original message</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>IncomingTranslate</cstring>
+ </property>
+ <property name="text">
+ <string>Translate directly</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>2</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>OutgoingMessages</cstring>
+ </property>
+ <property name="title">
+ <string>Outgoing Messages</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>OutgoingDontTranslate</cstring>
+ </property>
+ <property name="text">
+ <string>Do not translate</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>OutgoingShowOriginal</cstring>
+ </property>
+ <property name="text">
+ <string>Show the original message</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>OutgoingTranslate</cstring>
+ </property>
+ <property name="text">
+ <string>Translate directly</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>OutgoingAsk</cstring>
+ </property>
+ <property name="text">
+ <string>Show dialog before sending</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="4" column="0" rowspan="1" colspan="2">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/plugins/translator/translatorui.rc b/kopete/plugins/translator/translatorui.rc
new file mode 100644
index 00000000..d583b6a4
--- /dev/null
+++ b/kopete/plugins/translator/translatorui.rc
@@ -0,0 +1,12 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kopete_translator" version="1">
+ <MenuBar>
+ <Menu name="edit">
+ <text>&amp;Edit</text>
+ <Action name="contactLanguage" />
+ </Menu>
+ </MenuBar>
+ <Menu name="contact_popup">
+ <Action name="contactLanguage" />
+ </Menu>
+</kpartgui>
diff --git a/kopete/plugins/webpresence/DESIGN b/kopete/plugins/webpresence/DESIGN
new file mode 100644
index 00000000..d62c9c34
--- /dev/null
+++ b/kopete/plugins/webpresence/DESIGN
@@ -0,0 +1,12 @@
+Kopete Web Presence Plugin
+
+What It Does
+Provides a view of the current state of your contact list as a webpage.
+
+How It Does It
+Every so often, it writes a file containing a snapshot of who is online and who is not in your contactlist to a location you specify. This can be a local file, an FTP server, a HTTP server, or anywhere else that KIO can access.
+
+Use KIO::NetAccess to upload the files!
+
+Getting Info about Local User's Status
+Goal is to allow ppl who don't have us on their contactlist to see what our current status is and what our UIN/id is for each protocol. So we need to know (protocol, uin, status).
diff --git a/kopete/plugins/webpresence/Makefile.am b/kopete/plugins/webpresence/Makefile.am
new file mode 100644
index 00000000..7f859379
--- /dev/null
+++ b/kopete/plugins/webpresence/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes) $(LIBXML_CFLAGS) $(LIBXSLT_CFLAGS)
+
+kde_module_LTLIBRARIES = kopete_webpresence.la kcm_kopete_webpresence.la
+
+kopete_webpresence_la_SOURCES = webpresenceplugin.cpp
+
+kopete_webpresence_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_webpresence_la_LIBADD = ../../libkopete/libkopete.la $(LIBXML_LIBS) $(LIBXSLT_LIBS)
+
+kcm_kopete_webpresence_la_SOURCES = webpresencepreferences.cpp webpresenceprefs.ui
+kcm_kopete_webpresence_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_webpresence_la_LIBADD = ../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_webpresence.desktop
+servicedir = $(kde_servicesdir)
+
+kcm_DATA = kopete_webpresence_config.desktop
+kcmdir = $(kde_servicesdir)/kconfiguredialog
+
+mydata_DATA = webpresence_html.xsl\
+ webpresence_html_images.xsl\
+ webpresence_xhtml.xsl\
+ webpresence_xhtml_images.xsl
+mydatadir = $(kde_datadir)/kopete/webpresence
+EXTRA_DIST = $(mydata_DATA)
diff --git a/kopete/plugins/webpresence/TODO b/kopete/plugins/webpresence/TODO
new file mode 100644
index 00000000..28b78c3c
--- /dev/null
+++ b/kopete/plugins/webpresence/TODO
@@ -0,0 +1,5 @@
+1) Investigate how to include the plugin's output server side to give users a start.
+1a) XSLT processing?
+
+2) Icon
+
diff --git a/kopete/plugins/webpresence/kopete_webpresence.desktop b/kopete/plugins/webpresence/kopete_webpresence.desktop
new file mode 100644
index 00000000..85068ce2
--- /dev/null
+++ b/kopete/plugins/webpresence/kopete_webpresence.desktop
@@ -0,0 +1,120 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=html
+ServiceTypes=Kopete/Plugin
+X-KDE-Library=kopete_webpresence
+X-KDE-PluginInfo-Author=Will Stephenson
+X-KDE-PluginInfo-Email=will@stevello.free-online.co.uk
+X-KDE-PluginInfo-Name=kopete_webpresence
+X-KDE-PluginInfo-Version=0.8.0
+X-KDE-PluginInfo-Website=http://www.cs.ncl.ac.uk/old/people/william.stephenson
+X-KDE-PluginInfo-Category=Plugins
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Comment=Show the status of (parts of) your contact list on a webpage
+Comment[ar]=يظهر حالة أجزاء من قائمة أتصالاتك على صÙحة الشبكة
+Comment[be]=Паказвае ÑÐ¿Ñ–Ñ ÐºÐ°Ð½Ñ‚Ð°ÐºÑ‚Ð°Ñž ці Ñго чаÑтку на Ñтаронцы Сеціва
+Comment[bg]=ПриÑтавка за показвана ÑÑŠÑтоÑнието на (чаÑÑ‚ от) ÑпиÑъка Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¸ като уеб Ñтраница
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ওয়েবপেজে আপনার যোগাযোগ তালিকার (à¦à¦° অংশের) অবসà§à¦¥à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে
+Comment[bs]=Pokazuje status (dijelova) vaše kontakt liste na web stranici
+Comment[ca]=Mostra l'estatus de (parts de) la vostra llista de contactes a una pàgina web
+Comment[cs]=Zobrazí stav (Äásti) seznamu kontaktů na webu
+Comment[cy]=Dangos cyflwr o (rannau o) eich rhestr cysylltiadau ar dudalen wê
+Comment[da]=Vis status for (dele af) din kontaktliste på en netside
+Comment[de]=Zeigt den Status (von Teilen) der eigenen Kontaktliste auf einer Internetseite
+Comment[el]=Εμφάνιση της κατάστασης της λίστας επαφών σας σε ιστοσελίδα
+Comment[es]=Mostrar el estado de (partes de) su lista de contactos en una página web
+Comment[et]=Näitab (vähemalt osa) kontaktide staatust veebileheküljel
+Comment[eu]=Erakutsi zure kontaktu zerrendaren (edo zati baten) egoera web orri batean
+Comment[fa]=وضعیت )اجزایی از( Ùهرست تماس شما را روی صÙØ­Û€ وب نمایش می‌دهد
+Comment[fi]=Näytä kontaktien tila www-sivulla
+Comment[fr]=Affiche l'état de connexion de votre liste (ou d'une partie) sur une page internet
+Comment[gl]=Mostra o status da túa lista de contactos (ou parte) nunha páxina web
+Comment[he]=×ž×¦×™× ×ת מצב ההתחברות של ×—×‘×¨×™× ×‘×¨×©×™×ž×ª הקשר שלך )×ו של חלק×( בעמוד WEB.
+Comment[hi]=वेब पृषà¥à¤  पर आपकी समà¥à¤ªà¤°à¥à¤• सूची की सà¥à¤¥à¤¿à¤¤à¤¿ (या आंशिक) दिखाà¤
+Comment[hr]=Prikazivanje statusa (dijelova) vaše liste kontakata na web stranici
+Comment[hu]=A partnerlista-tagok állapotának megjelenítése weboldalon
+Comment[is]=Sýnir stöðu (hluta) lista þinna á vefsíðu
+Comment[it]=Mostra lo stato della (o parte della) tua lista contatti su una pagina web
+Comment[ja]=コンタクトリスト (ã®ä¸€éƒ¨) ã®çŠ¶æ…‹ã‚’ウェブページã«è¡¨ç¤º
+Comment[ka]=სტáƒáƒ¢áƒ£áƒ¡áƒ˜áƒ¡ ვებ გვერდზე ჩვენებáƒ
+Comment[kk]=Контакттарыңыздың тізімінің (не оның бөлігінің) күйін веб парақта көрÑетеді
+Comment[km]=បង្ហាញ​ស្ážáž¶áž“ភាព (ផ្នែក) នៃ​បញ្ជី​ទំនាក់​ទំនង​របស់​អ្នក​នៅ​លើ​ទំពáŸážšâ€‹ážœáŸ‰áŸáž”
+Comment[lt]=Rodyti kontaktų sąrašo (sąrašo dalies) būklę internetiniame puslapyje
+Comment[mk]=Го прикажува ÑтатуÑот на (деловите на) вашата лиÑта Ñо контакти на вебÑтраница
+Comment[nb]=Vis tilstanden til (deler av) kontaktlista de på en nettside
+Comment[nds]=Wiest den Status (vun en poor) vun Dien Kontakten op en Nettsiet
+Comment[ne]=वेबपृषà¥à¤ à¤®à¤¾ तपाईà¤à¤•à¥‹ समà¥à¤ªà¤°à¥à¤• सूची (भागको) वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ देखाउनà¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Toont de status van (delen van) uw contactenlijst op een webpagina
+Comment[nn]=Vis tilstanden til (delar av) kontaktlista på ei nettside
+Comment[pl]=Pokazuje status (części) Twojej listy kontaktów na stronie WWW
+Comment[pt]=Mostra o estado (de partes) da sua lista de contactos numa página Web
+Comment[pt_BR]=Mostra o status de (ou parte de) sua lista de contatos em uma página web
+Comment[ru]=Отображает ÑоÑтоÑние вашего ÑпиÑка контактов (или его чаÑти) на Ñтранице в Сети
+Comment[sk]=Zobrazenie stavu (Äasti) zo zoznamu kontaktov na webovej stránke
+Comment[sl]=Pokaže stanje (dela) vašega seznama stikov na spletni strani
+Comment[sr]=Приказивање ÑтатуÑа (делова) ваше лиÑте контаката на веб Ñтраници
+Comment[sr@Latn]=Prikazivanje statusa (delova) vaše liste kontakata na veb stranici
+Comment[sv]=Visa status för (delar av) din kontaktlista på en webbsida
+Comment[ta]=இணைய பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உஙà¯à®•à®³à¯ தொடரà¯à®ªà®¾à®³à®°à®¿à®©à¯ நிலைபà¯à®ªà®Ÿà¯à®Ÿà®¿à®¯à®²à¯ˆ காடà¯à®Ÿà¯
+Comment[tg]=Ҳолати (қиÑме аз) рӯйхати пайваÑтшавии шуморо дар Ñаҳифаи шабака нишон медиҳад
+Comment[tr]=Bağlantı listesinde web sayfası olanların (bölümler) durumunu göster
+Comment[uk]=Показує Ñтан вашого ÑпиÑку контактів (або його чаÑтини) на Ñторінці у Тенетах
+Comment[zh_CN]=在网页上显示您è”系人的状æ€
+Comment[zh_HK]=在網é ä¸Šé¡¯ç¤ºæ‚¨ï¼ˆä¸€éƒ¨ä»½ï¼‰è¯çµ¡äººæ¸…單的狀態
+Comment[zh_TW]=在網é ä¸Šé¡¯ç¤ºï¼ˆéƒ¨ä»½ï¼‰æ‚¨çš„è¯çµ¡äººçš„狀態
+Name=Web Presence
+Name[ar]=موقع على الشبكة
+Name[be]=Ð¡ÐµÑ†Ñ–ÑžÐ½Ð°Ñ Ð¿Ñ€Ñ‹ÑутнаÑць
+Name[bg]=Уеб приÑÑŠÑтвие
+Name[bn]=ওয়েব উপসà§à¦¥à¦¿à¦¤à¦¿
+Name[bs]=Web prisustvo
+Name[ca]=Web de presència
+Name[cs]=Přítomnost na webu
+Name[cy]=Presenoldeb Gwê
+Name[da]=WWW-nærvær
+Name[de]=Web-Präsenz
+Name[el]=ΠαÏουσία σε ιστοσελίδα
+Name[eo]=TTT-Prezenco
+Name[es]=Presencia Web
+Name[et]=Veebinimekiri
+Name[fa]=حضور وب
+Name[fi]=WWW-paikallaolo
+Name[fr]=Présence sur le web
+Name[gl]=Presencia na web
+Name[he]=נוכחות רשת
+Name[hi]=वेब उपसà¥à¤¥à¤¿à¤¤à¤¿
+Name[hr]=Prisutnost na webu
+Name[hu]=Webes jelenlét
+Name[is]=Ã vefnum
+Name[it]=Presenza web
+Name[ja]=ウェブプレゼンス
+Name[ka]=ვებ თვისებები
+Name[kk]=Вебте қатыÑу
+Name[km]=ážœážáŸ’ážáž˜áž¶áž“​វ៉áŸáž”
+Name[lt]=Rodyti internete
+Name[mk]=ПриÑуÑтво на веб
+Name[nb]=Profil på nettet
+Name[nds]=Nett-Praatschap
+Name[ne]=वेब उपसà¥à¤¥à¤¿à¤¤à¤¿
+Name[nl]=Web-aanwezigheid
+Name[nn]=Profil på nettet
+Name[pl]=Status na WWW
+Name[pt]=Presença na Web
+Name[pt_BR]=Presença Web
+Name[ro]=Prezenţă Web
+Name[ru]=ПриÑутÑтвие в Сети
+Name[sk]=Prítomnosť na webe
+Name[sl]=Spletna prisotnost
+Name[sr]=ПриÑутноÑÑ‚ на Вебу
+Name[sr@Latn]=Prisutnost na Vebu
+Name[sv]=Webbnärvaro
+Name[ta]=இணைய இரà¯à®ªà¯à®ªà¯
+Name[tg]=МавҷудиÑти Web
+Name[tr]=Web Hazır Bulunması
+Name[uk]=ПриÑутніÑÑ‚ÑŒ у Тенет
+Name[zh_CN]=Web 状æ€
+Name[zh_HK]=網上狀態
+Name[zh_TW]=網é é€£ç·š
diff --git a/kopete/plugins/webpresence/kopete_webpresence_config.desktop b/kopete/plugins/webpresence/kopete_webpresence_config.desktop
new file mode 100644
index 00000000..a714d602
--- /dev/null
+++ b/kopete/plugins/webpresence/kopete_webpresence_config.desktop
@@ -0,0 +1,116 @@
+[Desktop Entry]
+Type=Service
+Icon=html
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_webpresence
+X-KDE-FactoryName=WebPrecencePreferencesFactory
+X-KDE-ParentApp=kopete_webpresence
+X-KDE-ParentComponents=kopete_webpresence
+
+Comment=Show the status of (parts of) your contact list on a web page
+Comment[ar]=يظهر حالة أجزاء من قائمة أتصالاتك على الشبكة
+Comment[be]=Паказвае Ñтан ÑпіÑа кантактаў ці Ñго чаÑткі на Ñтаронцы Сеціва
+Comment[bg]=ПриÑтавка за показвана ÑÑŠÑтоÑнието на (чаÑÑ‚ от) ÑпиÑъка Ñ ÐºÐ¾Ð½Ñ‚Ð°ÐºÑ‚Ð¸ като уеб Ñтраница
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ওয়েবপেজে আপনার যোগাযোগ তালিকার (à¦à¦° অংশের) অবসà§à¦¥à¦¾ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে
+Comment[bs]=Pokazuje status (dijelova) vaše kontakt liste na web stranici
+Comment[ca]=Mostra l'estatus de (parts de) la vostra llista de contactes sobre una pàgina web
+Comment[cs]=Zobrazí stav (Äásti) seznamu kontaktů na webu
+Comment[cy]=Dangos cyflwr o (rannau o) eich rhestr cysylltiadau ar dudalen wê
+Comment[da]=Vis status for (dele af) din kontaktliste på en netside
+Comment[de]=Zeigt den Status (von Teilen) der eigenen Kontaktliste auf einer Internetseite
+Comment[el]=Εμφάνιση της κατάστασης της λίστας επαφών σας σε ιστοσελίδα
+Comment[es]=Mostrar el estado de (partes de) su lista de contactos en una página web
+Comment[et]=Näitab (vähemalt osa) kontaktide staatust veebileheküljel
+Comment[eu]=Erakutsi zure kontaktu zerrendaren (edo zati baten) egoera web orri batean
+Comment[fa]=وضعیت )اجزایی از( Ùهرست تماس شما را روی صÙØ­Û€ وب نمایش می‌دهد
+Comment[fi]=Näytä kontaktien tila www-sivulla
+Comment[fr]=Afficher l'état de connexion de votre liste (ou d'une partie) sur une page internet
+Comment[gl]=Amosar o estado da (parte da) súa lista de contactos nunha páxina web
+Comment[he]=×ž×¦×™× ×ת מצב ההתחברות של ×—×‘×¨×™× ×‘×¨×©×™×ž×ª הקשר שלך )×ו של חלק×( בעמוד WEB.
+Comment[hi]=वेब पृषà¥à¤  पर आपकी समà¥à¤ªà¤°à¥à¤• सूची की सà¥à¤¥à¤¿à¤¤à¤¿ (या आंशिक) दिखाà¤
+Comment[hr]=Prikazivanje statusa (dijelova) vaše liste kontakata na web stranici
+Comment[hu]=A partnerek állapotának megjelenítése weboldalon
+Comment[is]=Sýnir stöðu (hluta) lista þinna á vefsíðu
+Comment[it]=Mostra lo stato (o una parte) della tua lista contatti su una pagina web
+Comment[ja]=コンタクトリスト (ã®ä¸€éƒ¨) ã®çŠ¶æ…‹ã‚’ウェブページã«è¡¨ç¤º
+Comment[ka]=სტáƒáƒ¢áƒ£áƒ¡áƒ˜áƒ¡ ვებ გვერდზე ჩვენებáƒ
+Comment[kk]=Контакттарыңыздың тізімінің (не оның бөлігінің) күйін веб парақта көрÑетеді
+Comment[km]=បង្ហាញ​ស្ážáž¶áž“ភាព (ផ្នែក) នៃ​បញ្ជី​ទំនាក់​ទំនង​របស់​អ្នក​នៅ​លើ​ទំពáŸážšâ€‹ážœáŸ‰áŸáž”
+Comment[lt]=Rodyti kontaktų sąrašo (sąrašo dalies) būklę internetiniame puslapyje
+Comment[mk]=Го прикажува ÑтатуÑот на (деловите на) вашата лиÑта Ñо контакти на вебÑтраница
+Comment[nb]=Vis tilstanden til (deler av) kontaktlista de på en nettside
+Comment[nds]=Wiest den Status (vun en poor) vun Dien Kontakten op en Nettsiet
+Comment[ne]=वेबपृषà¥à¤ à¤®à¤¾ तपाईà¤à¤•à¥‹ समà¥à¤ªà¤°à¥à¤• सूची (भागको) वसà¥à¤¤à¥à¤¸à¥à¤¥à¤¿à¤¤à¤¿ देखाउनà¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Toont de status van (delen van) uw contactenlijst op een webpagina
+Comment[nn]=Vis tilstanden til (delar av) kontaktlista på ei nettside
+Comment[pl]=Pokazuje status (części) Twojej listy kontaktów na stronie WWW
+Comment[pt]=Mostra o estado (de partes) da sua lista de contactos numa página Web
+Comment[pt_BR]=Mostra o status de (ou parte de) sua lista de contatos em uma página web
+Comment[ru]=Отображает ÑоÑтоÑние вашего ÑпиÑка контактов (или его чаÑти) на Ñтранице в Сети
+Comment[sk]=Zobrazenie stavu (Äasti) zo zoznamu kontaktov na webovej stránke
+Comment[sl]=Pokaže stanje (dela) vašega seznama stikov na spletni strani
+Comment[sr]=Приказивање ÑтатуÑа (делова) ваше лиÑте контаката на веб Ñтраници
+Comment[sr@Latn]=Prikazivanje statusa (delova) vaše liste kontakata na veb stranici
+Comment[sv]=Visa status för (delar av) din kontaktlista på en webbsida
+Comment[ta]=இணைய பகà¯à®•à®¤à¯à®¤à®¿à®²à¯ உஙà¯à®•à®³à¯ தொடரà¯à®ªà®¾à®³à®°à¯ நிலைபà¯à®ªà®Ÿà¯à®Ÿà®¿à®¯à®²à¯ˆ காடà¯à®Ÿà¯
+Comment[tg]=Ҳолати (қиÑме аз) рӯйхати пайваÑтшавии шуморо дар Ñаҳифаи шабака нишон медиҳад
+Comment[tr]=Bağlantı listesinde web sayfası olanların (bölümler) durumunu göster
+Comment[uk]=Показує Ñтан вашого ÑпиÑку контактів (або його чаÑтини) на Ñторінці у Тенетах
+Comment[zh_CN]=在网页上显示您è”系人的状æ€
+Comment[zh_HK]=在網é ä¸Šé¡¯ç¤ºæ‚¨ï¼ˆä¸€éƒ¨ä»½ï¼‰è¯çµ¡äººæ¸…單的狀態
+Comment[zh_TW]=在網é ä¸Šé¡¯ç¤ºï¼ˆéƒ¨ä»½ï¼‰æ‚¨çš„è¯çµ¡äººçš„狀態
+Name=Web Presence
+Name[ar]=موقع على الشبكة
+Name[be]=Ð¡ÐµÑ†Ñ–ÑžÐ½Ð°Ñ Ð¿Ñ€Ñ‹ÑутнаÑць
+Name[bg]=Уеб приÑÑŠÑтвие
+Name[bn]=ওয়েব উপসà§à¦¥à¦¿à¦¤à¦¿
+Name[bs]=Web prisustvo
+Name[ca]=Web de presència
+Name[cs]=Přítomnost na webu
+Name[cy]=Presenoldeb Gwê
+Name[da]=WWW-nærvær
+Name[de]=Web-Präsenz
+Name[el]=ΠαÏουσία σε ιστοσελίδα
+Name[eo]=TTT-Prezenco
+Name[es]=Presencia Web
+Name[et]=Veebinimekiri
+Name[fa]=حضور وب
+Name[fi]=WWW-paikallaolo
+Name[fr]=Présence sur le web
+Name[gl]=Presencia na web
+Name[he]=נוכחות רשת
+Name[hi]=वेब उपसà¥à¤¥à¤¿à¤¤à¤¿
+Name[hr]=Prisutnost na webu
+Name[hu]=Webes jelenlét
+Name[is]=Ã vefnum
+Name[it]=Presenza web
+Name[ja]=ウェブプレゼンス
+Name[ka]=ვებ თვისებები
+Name[kk]=Вебте қатыÑу
+Name[km]=ážœážáŸ’ážáž˜áž¶áž“​វ៉áŸáž”
+Name[lt]=Rodyti internete
+Name[mk]=ПриÑуÑтво на веб
+Name[nb]=Profil på nettet
+Name[nds]=Nett-Praatschap
+Name[ne]=वेब उपसà¥à¤¥à¤¿à¤¤à¤¿
+Name[nl]=Web-aanwezigheid
+Name[nn]=Profil på nettet
+Name[pl]=Status na WWW
+Name[pt]=Presença na Web
+Name[pt_BR]=Presença Web
+Name[ro]=Prezenţă Web
+Name[ru]=ПриÑутÑтвие в Сети
+Name[sk]=Prítomnosť na webe
+Name[sl]=Spletna prisotnost
+Name[sr]=ПриÑутноÑÑ‚ на Вебу
+Name[sr@Latn]=Prisutnost na Vebu
+Name[sv]=Webbnärvaro
+Name[ta]=இணைய இரà¯à®ªà¯à®ªà¯
+Name[tg]=МавҷудиÑти Web
+Name[tr]=Web Hazır Bulunması
+Name[uk]=ПриÑутніÑÑ‚ÑŒ у Тенет
+Name[zh_CN]=Web 状æ€
+Name[zh_HK]=網上狀態
+Name[zh_TW]=網é é€£ç·š
diff --git a/kopete/plugins/webpresence/webpresence_html.xsl b/kopete/plugins/webpresence/webpresence_html.xsl
new file mode 100644
index 00000000..f768ccf5
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresence_html.xsl
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <!--
+ HTML 4.01 Transitional
+ -->
+
+ <xsl:output
+ doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
+ doctype-system="http://www.w3.org/TR/html4/loose.dtd"
+ indent="yes"
+ method="html"
+ encoding="ISO-8859-1"
+ omit-xml-declaration="yes"/>
+
+ <xsl:template match="webpresence">
+ <html>
+ <head>
+ <title>My IM Status</title>
+ </head>
+ <body>
+ <p class="name"><xsl:value-of select="name"/></p>
+ <xsl:apply-templates select="accounts"/>
+ <hr/>
+ <font size="-2">
+ <xsl:text>Last update at: </xsl:text>
+ <xsl:value-of select="listdate"/>
+ </font>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="accounts">
+ <table>
+ <xsl:for-each select="account">
+ <tr>
+ <td class="protocol">
+ <xsl:apply-templates select="protocol"/>
+ </td>
+ <td class="accountname">
+ <xsl:value-of select="accountname"/>
+ </td>
+ <td class="accountstatus">
+ <xsl:apply-templates select="accountstatus"/>
+ </td>
+ <td class="accountaddress">
+ <xsl:value-of select="accountaddress"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="protocol">
+ <xsl:choose>
+ <xsl:when test=".='AIMProtocol'">
+ <xsl:text>AIM</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='MSNProtocol'">
+ <xsl:text>MSN</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='ICQProtocol'">
+ <xsl:text>ICQ</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='JabberProtocol'">
+ <xsl:text>Jabber</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='YahooProtocol'">
+ <xsl:text>Yahoo</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='GaduProtocol'">
+ <xsl:text>Gadu-Gadu</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='WPProtocol'">
+ <xsl:text>WinPopup</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='SMSProtocol'">
+ <xsl:text>SMS</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='IRCProtocol'">
+ <xsl:text>IRC</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Unknown</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="accountstatus">
+ <xsl:choose>
+ <xsl:when test=".='ONLINE'">
+ <font color="#00FF00">
+ <xsl:value-of select="."/>
+ </font>
+ </xsl:when>
+ <xsl:when test=".='OFFLINE'">
+ <font color="red">
+ <xsl:value-of select="."/>
+ </font>
+ </xsl:when>
+ <xsl:when test=".='AWAY'">
+ <font color="maroon">
+ <xsl:value-of select="."/>
+ </font>
+ <xsl:if test="@statusdescription != 'Away' or @awayreason">
+ <xsl:text> (</xsl:text>
+ </xsl:if>
+ <xsl:if test="@statusdescription != 'Away'">
+ <xsl:value-of select="@statusdescription"/>
+ <xsl:if test="@awayreason">
+ <xsl:text>: </xsl:text>
+ </xsl:if>
+ </xsl:if>
+ <xsl:if test="@awayreason">
+ <xsl:value-of select="@awayreason"/>
+ </xsl:if>
+ <xsl:if test="@statusdescription != 'Away' or @awayreason">
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test=".='UNKNOWN'">
+ <font color="gray">
+ <xsl:value-of select="."/>
+ </font>
+ <xsl:if test="@statusdescription">
+ <xsl:text> (</xsl:text>
+ <xsl:value-of select="@statusdescription"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="."/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
+
+<!-- vim: set ts=4 sts=4 sw=4: -->
diff --git a/kopete/plugins/webpresence/webpresence_html_images.xsl b/kopete/plugins/webpresence/webpresence_html_images.xsl
new file mode 100644
index 00000000..5b707046
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresence_html_images.xsl
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <!--
+ HTML 4.01 Transitional - protocol text replaced with icons
+
+ NOTE: You will have to create a dir 'images' at the
+ upload location containing the files named below.
+ -->
+
+ <!--
+ Import the HTML XSL sheet, and replace the <protocol>
+ handling with our own template.
+ -->
+
+ <xsl:import href="webpresence_html.xsl"/>
+
+ <!--
+ You can change the image directory with this variable.
+ -->
+
+ <xsl:variable name="images">images</xsl:variable>
+
+ <xsl:template match="protocol">
+ <xsl:choose>
+ <xsl:when test=".='MSNProtocol'">
+ <img src="{$images}/msn_protocol.png" alt="MSN" title="MSN"/>
+ </xsl:when>
+ <xsl:when test=".='ICQProtocol'">
+ <img src="{$images}/icq_protocol.png" alt="ICQ" title="ICQ"/>
+ </xsl:when>
+ <xsl:when test=".='JabberProtocol'">
+ <img src="{$images}/jabber_protocol.png" alt="Jabber" title="Jabber"/>
+ </xsl:when>
+ <xsl:when test=".='YahooProtocol'">
+ <img src="{$images}/yahoo_protocol.png" alt="Yahoo" title="Yahoo"/>
+ </xsl:when>
+ <xsl:when test=".='AIMProtocol'">
+ <img src="{$images}/aim_protocol.png" alt="AIM" title="AIM"/>
+ </xsl:when>
+ <xsl:when test=".='IRCProtocol'">
+ <img src="{$images}/irc_protocol.png" alt="IRC" title="IRC"/>
+ </xsl:when>
+ <xsl:when test=".='SMSProtocol'">
+ <img src="{$images}/sms_protocol.png" alt="SMS" title="SMS"/>
+ </xsl:when>
+ <xsl:when test=".='GaduProtocol'">
+ <img src="{$images}/gadu_protocol.png" alt="Gadu-Gadu" title="Gadu-Gadu"/>
+ </xsl:when>
+ <xsl:when test=".='WPProtocol'">
+ <img src="{$images}/winpopup_protocol.png" alt="WinPopup" title="WinPopup"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
+
+<!-- vim: set ts=4 sts=4 sw=4: -->
diff --git a/kopete/plugins/webpresence/webpresence_xhtml.xsl b/kopete/plugins/webpresence/webpresence_xhtml.xsl
new file mode 100644
index 00000000..9d749c14
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresence_xhtml.xsl
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml">
+
+ <!--
+ XHTML 1.0 Strict
+ -->
+
+ <xsl:output
+ doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
+ doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
+ indent="yes"
+ encoding="UTF-8"/>
+
+ <xsl:template match="webpresence">
+ <html>
+ <head>
+ <title>My IM Status</title>
+ </head>
+ <body>
+ <p class="name"><xsl:value-of select="name"/></p>
+ <xsl:apply-templates select="accounts"/>
+ <hr/>
+ <p style="font-size: x-small">
+ <xsl:text>Last update at: </xsl:text>
+ <xsl:value-of select="listdate"/>
+ </p>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="accounts">
+ <table>
+ <xsl:for-each select="account">
+ <tr>
+ <td class="protocol">
+ <xsl:apply-templates select="protocol"/>
+ </td>
+ <td class="accountname">
+ <xsl:value-of select="accountname"/>
+ </td>
+ <td class="accountstatus">
+ <xsl:apply-templates select="accountstatus"/>
+ </td>
+ <td class="accountaddress">
+ <xsl:value-of select="accountaddress"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="protocol">
+ <xsl:choose>
+ <xsl:when test=".='AIMProtocol'">
+ <xsl:text>AIM</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='MSNProtocol'">
+ <xsl:text>MSN</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='ICQProtocol'">
+ <xsl:text>ICQ</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='JabberProtocol'">
+ <xsl:text>Jabber</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='YahooProtocol'">
+ <xsl:text>Yahoo</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='GaduProtocol'">
+ <xsl:text>Gadu-Gadu</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='WPProtocol'">
+ <xsl:text>WinPopup</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='SMSProtocol'">
+ <xsl:text>SMS</xsl:text>
+ </xsl:when>
+ <xsl:when test=".='IRCProtocol'">
+ <xsl:text>IRC</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>Unknown</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template match="accountstatus">
+ <xsl:choose>
+ <xsl:when test=".='ONLINE'">
+ <span style="color: lime">
+ <xsl:value-of select="."/>
+ </span>
+ </xsl:when>
+ <xsl:when test=".='OFFLINE'">
+ <span style="color: red">
+ <xsl:value-of select="."/>
+ </span>
+ </xsl:when>
+ <xsl:when test=".='AWAY'">
+ <span style="color: maroon">
+ <xsl:value-of select="."/>
+ </span>
+ <xsl:if test="@statusdescription != 'Away' or @awayreason">
+ <xsl:text> (</xsl:text>
+ </xsl:if>
+ <xsl:if test="@statusdescription != 'Away'">
+ <xsl:value-of select="@statusdescription"/>
+ <xsl:if test="@awayreason">
+ <xsl:text>: </xsl:text>
+ </xsl:if>
+ </xsl:if>
+ <xsl:if test="@awayreason">
+ <xsl:value-of select="@awayreason"/>
+ </xsl:if>
+ <xsl:if test="@statusdescription != 'Away' or @awayreason">
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test=".='UNKNOWN'">
+ <span style="color: gray">
+ <xsl:value-of select="."/>
+ </span>
+ <xsl:if test="@statusdescription">
+ <xsl:text> (</xsl:text>
+ <xsl:value-of select="@statusdescription"/>
+ <xsl:text>)</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="."/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
+
+<!-- vim: set ts=4 sts=4 sw=4: -->
diff --git a/kopete/plugins/webpresence/webpresence_xhtml_images.xsl b/kopete/plugins/webpresence/webpresence_xhtml_images.xsl
new file mode 100644
index 00000000..8254a674
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresence_xhtml_images.xsl
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml">
+
+ <!--
+ XHTML 1.0 Strict - protocol text replaced with icons
+
+ NOTE: You will have to create a dir 'images' at the
+ upload location containing the files named below.
+ -->
+
+ <!--
+ Import the XHTML XSL sheet, and replace the <protocol>
+ handling with our own template.
+ -->
+
+ <xsl:import href="webpresence_xhtml.xsl"/>
+
+ <!--
+ You can change the image directory with this variable.
+ -->
+
+ <xsl:variable name="images">images</xsl:variable>
+
+ <xsl:template match="protocol">
+ <xsl:choose>
+ <xsl:when test=".='MSNProtocol'">
+ <img src="{$images}/msn_protocol.png" alt="MSN" title="MSN"/>
+ </xsl:when>
+ <xsl:when test=".='ICQProtocol'">
+ <img src="{$images}/icq_protocol.png" alt="ICQ" title="ICQ"/>
+ </xsl:when>
+ <xsl:when test=".='JabberProtocol'">
+ <img src="{$images}/jabber_protocol.png" alt="Jabber" title="Jabber"/>
+ </xsl:when>
+ <xsl:when test=".='YahooProtocol'">
+ <img src="{$images}/yahoo_protocol.png" alt="Yahoo" title="Yahoo"/>
+ </xsl:when>
+ <xsl:when test=".='AIMProtocol'">
+ <img src="{$images}/aim_protocol.png" alt="AIM" title="AIM"/>
+ </xsl:when>
+ <xsl:when test=".='IRCProtocol'">
+ <img src="{$images}/irc_protocol.png" alt="IRC" title="IRC"/>
+ </xsl:when>
+ <xsl:when test=".='SMSProtocol'">
+ <img src="{$images}/sms_protocol.png" alt="SMS" title="SMS"/>
+ </xsl:when>
+ <xsl:when test=".='GaduProtocol'">
+ <img src="{$images}/gadu_protocol.png" alt="Gadu-Gadu" title="Gadu-Gadu"/>
+ </xsl:when>
+ <xsl:when test=".='WPProtocol'">
+ <img src="{$images}/winpopup_protocol.png" alt="WinPopup" title="WinPopup"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
+
+<!-- vim: set ts=4 sts=4 sw=4: -->
diff --git a/kopete/plugins/webpresence/webpresenceplugin.cpp b/kopete/plugins/webpresence/webpresenceplugin.cpp
new file mode 100644
index 00000000..1856d94c
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresenceplugin.cpp
@@ -0,0 +1,473 @@
+/*
+ webpresenceplugin.cpp
+
+ Kopete Web Presence plugin
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#include "config.h"
+
+#include <qdom.h>
+#include <qtimer.h>
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <ktempfile.h>
+#include <kstandarddirs.h>
+
+#ifdef HAVE_XSLT
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <libxslt/xsltconfig.h>
+#include <libxslt/xsltInternals.h>
+#include <libxslt/transform.h>
+#include <libxslt/xsltutils.h>
+#endif
+
+#include "kopetepluginmanager.h"
+#include "kopeteprotocol.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+
+#include "webpresenceplugin.h"
+
+typedef KGenericFactory<WebPresencePlugin> WebPresencePluginFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_webpresence, WebPresencePluginFactory( "kopete_webpresence" ) )
+
+WebPresencePlugin::WebPresencePlugin( QObject *parent, const char *name, const QStringList& /*args*/ )
+ : Kopete::Plugin( WebPresencePluginFactory::instance(), parent, name ),
+ shuttingDown( false ), resultFormatting( WEB_HTML )
+{
+ m_writeScheduler = new QTimer( this );
+ connect ( m_writeScheduler, SIGNAL( timeout() ), this, SLOT( slotWriteFile() ) );
+ connect( Kopete::AccountManager::self(), SIGNAL(accountRegistered(Kopete::Account*)),
+ this, SLOT( listenToAllAccounts() ) );
+ connect( Kopete::AccountManager::self(), SIGNAL(accountUnregistered(Kopete::Account*)),
+ this, SLOT( listenToAllAccounts() ) );
+
+ connect(this, SIGNAL(settingsChanged()), this, SLOT( loadSettings() ) );
+ loadSettings();
+ listenToAllAccounts();
+}
+
+WebPresencePlugin::~WebPresencePlugin()
+{
+}
+
+void WebPresencePlugin::loadSettings()
+{
+ KConfig *kconfig = KGlobal::config();
+ kconfig->setGroup( "Web Presence Plugin" );
+
+ frequency = kconfig->readNumEntry("UploadFrequency", 15);
+ resultURL = kconfig->readPathEntry("uploadURL");
+
+ resultFormatting = WEB_UNDEFINED;
+
+ if ( kconfig->readBoolEntry( "formatHTML", false ) ) {
+ resultFormatting = WEB_HTML;
+ } else if ( kconfig->readBoolEntry( "formatXHTML", false ) ) {
+ resultFormatting = WEB_XHTML;
+ } else if ( kconfig->readBoolEntry( "formatXML", false ) ) {
+ resultFormatting = WEB_XML;
+ } else if ( kconfig->readBoolEntry( "formatStylesheet", false ) ) {
+ resultFormatting = WEB_CUSTOM;
+ userStyleSheet = kconfig->readEntry("formatStylesheetURL");
+ }
+
+ // Default to HTML if we dont get anything useful from config file.
+ if ( resultFormatting == WEB_UNDEFINED )
+ resultFormatting = WEB_HTML;
+
+ useImagesInHTML = kconfig->readBoolEntry( "useImagesHTML", false );
+ useImName = kconfig->readBoolEntry("showName", true);
+ userName = kconfig->readEntry("showThisName");
+ showAddresses = kconfig->readBoolEntry("includeIMAddress", false);
+
+ // Update file when settings are changed.
+ slotWriteFile();
+}
+
+void WebPresencePlugin::listenToAllAccounts()
+{
+ // connect to signals notifying of all accounts' status changes
+ ProtocolList protocols = allProtocols();
+
+ for ( ProtocolList::Iterator it = protocols.begin();
+ it != protocols.end(); ++it )
+ {
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( *it );
+ QDictIterator<Kopete::Account> acIt( accounts );
+
+ for( ; Kopete::Account *account = acIt.current(); ++acIt )
+ {
+ listenToAccount( account );
+ }
+ }
+ slotWaitMoreStatusChanges();
+}
+
+void WebPresencePlugin::listenToAccount( Kopete::Account* account )
+{
+ if(account && account->myself())
+ {
+ // Connect to the account's status changed signal
+ // because we can't know if the account has already connected
+ QObject::disconnect( account->myself(),
+ SIGNAL(onlineStatusChanged( Kopete::Contact *,
+ const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus & ) ),
+ this,
+ SLOT( slotWaitMoreStatusChanges() ) ) ;
+ QObject::connect( account->myself(),
+ SIGNAL(onlineStatusChanged( Kopete::Contact *,
+ const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus & ) ),
+ this,
+ SLOT( slotWaitMoreStatusChanges() ) );
+ }
+}
+
+void WebPresencePlugin::slotWaitMoreStatusChanges()
+{
+ if ( !m_writeScheduler->isActive() )
+ m_writeScheduler->start( frequency * 1000 );
+}
+
+void WebPresencePlugin::slotWriteFile()
+{
+ m_writeScheduler->stop();
+
+ // generate the (temporary) XML file representing the current contactlist
+ KURL dest( resultURL );
+ if ( resultURL.isEmpty() || !dest.isValid() )
+ {
+ kdDebug(14309) << "url is empty or not valid. NOT UPDATING!" << endl;
+ return;
+ }
+
+ KTempFile* xml = generateFile();
+ xml->setAutoDelete( true );
+ kdDebug(14309) << k_funcinfo << " " << xml->name() << endl;
+
+ switch( resultFormatting ) {
+ case WEB_XML:
+ m_output = xml;
+ xml = 0L;
+ break;
+ case WEB_HTML:
+ case WEB_XHTML:
+ case WEB_CUSTOM:
+ m_output = new KTempFile();
+ m_output->setAutoDelete( true );
+
+ if ( !transform( xml, m_output ) )
+ {
+ //TODO: give some error to user, even better if shown only once
+ delete m_output;
+ m_output = 0L;
+
+ delete xml;
+ return;
+ }
+
+ delete xml; // might make debugging harder!
+ break;
+ default:
+ return;
+ }
+
+ // upload it to the specified URL
+ KURL src( m_output->name() );
+ KIO::FileCopyJob *job = KIO::file_move( src, dest, -1, true, false, false );
+ connect( job, SIGNAL( result( KIO::Job * ) ),
+ SLOT( slotUploadJobResult( KIO::Job * ) ) );
+}
+
+void WebPresencePlugin::slotUploadJobResult( KIO::Job *job )
+{
+ if ( job->error() ) {
+ kdDebug(14309) << "Error uploading presence info." << endl;
+ KMessageBox::queuedDetailedError( 0, i18n("An error occurred when uploading your presence page.\nCheck the path and write permissions of the destination."), 0, displayName() );
+ delete m_output;
+ m_output = 0L;
+ }
+}
+
+KTempFile* WebPresencePlugin::generateFile()
+{
+ // generate the (temporary) XML file representing the current contactlist
+ kdDebug( 14309 ) << k_funcinfo << endl;
+ QString notKnown = i18n( "Not yet known" );
+
+ QDomDocument doc;
+
+ doc.appendChild( doc.createProcessingInstruction( "xml",
+ "version=\"1.0\" encoding=\"UTF-8\"" ) );
+
+ QDomElement root = doc.createElement( "webpresence" );
+ doc.appendChild( root );
+
+ // insert the current date/time
+ QDomElement date = doc.createElement( "listdate" );
+ QDomText t = doc.createTextNode(
+ KGlobal::locale()->formatDateTime( QDateTime::currentDateTime() ) );
+ date.appendChild( t );
+ root.appendChild( date );
+
+ // insert the user's name
+ QDomElement name = doc.createElement( "name" );
+ QDomText nameText;
+ if ( !useImName && !userName.isEmpty() )
+ nameText = doc.createTextNode( userName );
+ else
+ nameText = doc.createTextNode( notKnown );
+ name.appendChild( nameText );
+ root.appendChild( name );
+
+ // insert the list of the user's accounts
+ QDomElement accounts = doc.createElement( "accounts" );
+ root.appendChild( accounts );
+
+ QPtrList<Kopete::Account> list = Kopete::AccountManager::self()->accounts();
+ // If no accounts, stop here
+ if ( !list.isEmpty() )
+ {
+ for( QPtrListIterator<Kopete::Account> it( list );
+ Kopete::Account *account=it.current();
+ ++it )
+ {
+ QDomElement acc = doc.createElement( "account" );
+ //output += h.openTag( "account" );
+
+ QDomElement protoName = doc.createElement( "protocol" );
+ QDomText protoNameText = doc.createTextNode(
+ account->protocol()->pluginId() );
+ protoName.appendChild( protoNameText );
+ acc.appendChild( protoName );
+
+ Kopete::Contact* me = account->myself();
+ QString displayName = me->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ QDomElement accName = doc.createElement( "accountname" );
+ QDomText accNameText = doc.createTextNode( ( me )
+ ? displayName
+ : notKnown );
+ accName.appendChild( accNameText );
+ acc.appendChild( accName );
+
+ QDomElement accStatus = doc.createElement( "accountstatus" );
+ QDomText statusText = doc.createTextNode( ( me )
+ ? statusAsString( me->onlineStatus() )
+ : notKnown ) ;
+ accStatus.appendChild( statusText );
+
+ // Dont add these if we're shutting down, because the result
+ // would be quite weird.
+ if ( !shuttingDown ) {
+
+ // Add away message as an attribute, if one exists.
+ if ( me->onlineStatus().status() == Kopete::OnlineStatus::Away &&
+ !me->property("awayMessage").value().toString().isEmpty() ) {
+ accStatus.setAttribute( "awayreason",
+ me->property("awayMessage").value().toString() );
+ }
+
+ // Add the online status description as an attribute, if one exits.
+ if ( !me->onlineStatus().description().isEmpty() ) {
+ accStatus.setAttribute( "statusdescription",
+ me->onlineStatus().description() );
+ }
+ }
+ acc.appendChild( accStatus );
+
+ if ( showAddresses )
+ {
+ QDomElement accAddress = doc.createElement( "accountaddress" );
+ QDomText addressText = doc.createTextNode( ( me )
+ ? me->contactId()
+ : notKnown );
+ accAddress.appendChild( addressText );
+ acc.appendChild( accAddress );
+ }
+
+ accounts.appendChild( acc );
+ }
+ }
+
+ // write the XML to a temporary file
+ KTempFile* file = new KTempFile();
+ QTextStream *stream = file->textStream();
+ stream->setEncoding( QTextStream::UnicodeUTF8 );
+ doc.save( *stream, 4 );
+ file->close();
+ return file;
+}
+
+bool WebPresencePlugin::transform( KTempFile * src, KTempFile * dest )
+{
+#ifdef HAVE_XSLT
+ bool retval = true;
+ xmlSubstituteEntitiesDefault( 1 );
+ xmlLoadExtDtdDefaultValue = 1;
+
+ QFile sheet;
+
+ switch ( resultFormatting ) {
+ case WEB_XML:
+ // Oops! We tried to call transform() but XML was requested.
+ return false;
+ case WEB_HTML:
+ if ( useImagesInHTML ) {
+ sheet.setName( locate( "appdata", "webpresence/webpresence_html_images.xsl" ) );
+ } else {
+ sheet.setName( locate( "appdata", "webpresence/webpresence_html.xsl" ) );
+ }
+ break;
+ case WEB_XHTML:
+ if ( useImagesInHTML ) {
+ sheet.setName( locate( "appdata", "webpresence/webpresence_xhtml_images.xsl" ) );
+ } else {
+ sheet.setName( locate( "appdata", "webpresence/webpresence_xhtml.xsl" ) );
+ }
+ break;
+ case WEB_CUSTOM:
+ sheet.setName( userStyleSheet );
+ break;
+ default:
+ // Shouldn't ever reach here.
+ return false;
+ }
+
+ // TODO: auto / smart pointers would be useful here
+ xsltStylesheetPtr cur = 0;
+ xmlDocPtr doc = 0;
+ xmlDocPtr res = 0;
+
+ if ( !sheet.exists() ) {
+ kdDebug(14309) << k_funcinfo << "ERROR: Style sheet not found" << endl;
+ retval = false;
+ goto end;
+ }
+
+ // is the cast safe?
+ cur = xsltParseStylesheetFile( (const xmlChar *) sheet.name().latin1() );
+ if ( !cur ) {
+ kdDebug(14309) << k_funcinfo << "ERROR: Style sheet parsing failed" << endl;
+ retval = false;
+ goto end;
+ }
+
+ doc = xmlParseFile( QFile::encodeName( src->name() ) );
+ if ( !doc ) {
+ kdDebug(14309) << k_funcinfo << "ERROR: XML parsing failed" << endl;
+ retval = false;
+ goto end;
+ }
+
+ res = xsltApplyStylesheet( cur, doc, 0 );
+ if ( !res ) {
+ kdDebug(14309) << k_funcinfo << "ERROR: Style sheet apply failed" << endl;
+ retval = false;
+ goto end;
+ }
+
+ if ( xsltSaveResultToFile(dest->fstream(), res, cur) == -1 ) {
+ kdDebug(14309) << k_funcinfo << "ERROR: Style sheet apply failed" << endl;
+ retval = false;
+ goto end;
+ }
+
+ // then it all worked!
+ dest->close();
+
+end:
+ xsltCleanupGlobals();
+ xmlCleanupParser();
+ if (doc) xmlFreeDoc(doc);
+ if (res) xmlFreeDoc(res);
+ if (cur) xsltFreeStylesheet(cur);
+
+ return retval;
+
+#else
+ Q_UNUSED( src );
+ Q_UNUSED( dest );
+
+ return false;
+#endif
+}
+
+ProtocolList WebPresencePlugin::allProtocols()
+{
+ kdDebug( 14309 ) << k_funcinfo << endl;
+
+ Kopete::PluginList plugins = Kopete::PluginManager::self()->loadedPlugins( "Protocols" );
+ Kopete::PluginList::ConstIterator it;
+
+ ProtocolList result;
+
+ for ( it = plugins.begin(); it != plugins.end(); ++it ) {
+ result.append( static_cast<Kopete::Protocol *>( *it ) );
+ }
+
+ return result;
+}
+
+QString WebPresencePlugin::statusAsString( const Kopete::OnlineStatus &newStatus )
+{
+ if (shuttingDown)
+ return "OFFLINE";
+
+ QString status;
+ switch ( newStatus.status() )
+ {
+ case Kopete::OnlineStatus::Online:
+ status = "ONLINE";
+ break;
+ case Kopete::OnlineStatus::Away:
+ status = "AWAY";
+ break;
+ case Kopete::OnlineStatus::Offline:
+ case Kopete::OnlineStatus::Invisible:
+ status = "OFFLINE";
+ break;
+ default:
+ status = "UNKNOWN";
+ }
+
+ return status;
+}
+
+void WebPresencePlugin::aboutToUnload()
+{
+ // Stop timer. Dont need it anymore.
+ m_writeScheduler->stop();
+
+ // Force statusAsString() report all accounts as OFFLINE.
+ shuttingDown = true;
+
+ // Do final update of webpresence file.
+ slotWriteFile();
+
+ emit readyForUnload();
+}
+
+// vim: set noet ts=4 sts=4 sw=4:
+#include "webpresenceplugin.moc"
diff --git a/kopete/plugins/webpresence/webpresenceplugin.h b/kopete/plugins/webpresence/webpresenceplugin.h
new file mode 100644
index 00000000..3aea9af0
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresenceplugin.h
@@ -0,0 +1,123 @@
+/*
+ webpresenceplugin.h
+
+ Kopete Web Presence plugin
+
+ Copyright (c) 2002,2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002,2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef WEBPRESENCEPLUGIN_H
+#define WEBPRESENCEPLUGIN_H
+
+#include <qvaluestack.h>
+
+#include <kio/job.h>
+
+#include "kopetecontact.h"
+#include "kopeteonlinestatus.h"
+
+class QTimer;
+class KTempFile;
+namespace Kopete { class MetaContact; }
+class KToggleAction;
+class KActionCollection;
+
+typedef QValueList<Kopete::Protocol*> ProtocolList;
+
+class WebPresencePlugin : public Kopete::Plugin
+{
+ Q_OBJECT
+
+private:
+ int frequency;
+ bool showAddresses;
+ bool useImName;
+ QString userName;
+ QString userStyleSheet;
+ bool useImagesInHTML;
+
+ // Is set to true when Kopete has notified us
+ // that we're about to be unloaded.
+ bool shuttingDown;
+
+ enum {
+ WEB_HTML,
+ WEB_XHTML,
+ WEB_XML,
+ WEB_CUSTOM,
+ WEB_UNDEFINED
+ } resultFormatting;
+
+ QString resultURL;
+
+public:
+ WebPresencePlugin( QObject *parent, const char *name, const QStringList &args );
+ virtual ~WebPresencePlugin();
+
+ virtual void aboutToUnload();
+
+protected slots:
+ void loadSettings();
+
+ /**
+ * Write a file to the specified location,
+ */
+ void slotWriteFile();
+ /**
+ * Called when an upload finished, displays error if needed
+ */
+ void slotUploadJobResult( KIO::Job * );
+ /**
+ * Called to schedule a write, after waiting to see if more changes
+ * occur (accounts tend to change status together)
+ */
+ void slotWaitMoreStatusChanges();
+ /**
+ * Sets us up to respond to account status changes
+ */
+ void listenToAllAccounts();
+ /**
+ * Sets us up to respond to a new account
+ */
+ void listenToAccount( Kopete::Account* account );
+
+protected:
+ /**
+ * Generate the file (HTML, text) to be uploaded
+ */
+ KTempFile* generateFile();
+ /**
+ * Apply named stylesheet to get content and presentation
+ */
+ bool transform( KTempFile* src, KTempFile* dest );
+ /**
+ * Helper method, generates list of all IM protocols
+ */
+ ProtocolList allProtocols();
+ /**
+ * Converts numeric status to a string
+ */
+ QString statusAsString( const Kopete::OnlineStatus &newStatus );
+ /**
+ * Schedules writes
+ */
+ QTimer* m_writeScheduler;
+
+ // The file to be uploaded to the WWW
+ KTempFile *m_output;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/webpresence/webpresencepreferences.cpp b/kopete/plugins/webpresence/webpresencepreferences.cpp
new file mode 100644
index 00000000..9b00435a
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresencepreferences.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ webpresencepreferences.cpp
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qlayout.h>
+
+#include <kgenericfactory.h>
+#include <kautoconfig.h>
+#include <kurlrequester.h>
+
+#include "webpresenceprefs.h"
+#include "webpresencepreferences.h"
+
+typedef KGenericFactory<WebPresencePreferences> WebPresencePreferencesFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_webpresence, WebPresencePreferencesFactory("kcm_kopete_webpresence"))
+
+WebPresencePreferences::WebPresencePreferences(QWidget *parent, const char* /*name*/, const QStringList &args)
+ : KCModule(WebPresencePreferencesFactory::instance(), parent, args)
+{
+ // Add actuall widget generated from ui file.
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ preferencesDialog = new WebPresencePrefsUI(this);
+ preferencesDialog->uploadURL->setMode( KFile::File );
+ preferencesDialog->formatStylesheetURL->setFilter( "*.xsl" );
+
+ // KAutoConfig stuff
+ kautoconfig = new KAutoConfig(KGlobal::config(), this, "kautoconfig");
+ connect(kautoconfig, SIGNAL(widgetModified()), SLOT(widgetModified()));
+ connect(kautoconfig, SIGNAL(settingsChanged()), SLOT(widgetModified()));
+ kautoconfig->addWidget(preferencesDialog, "Web Presence Plugin");
+ kautoconfig->retrieveSettings(true);
+}
+
+void WebPresencePreferences::widgetModified()
+{
+ emit KCModule::changed(kautoconfig->hasChanged());
+}
+
+void WebPresencePreferences::save()
+{
+ kautoconfig->saveSettings();
+}
+
+void WebPresencePreferences::defaults ()
+{
+ kautoconfig->resetSettings();
+}
+
+#include "webpresencepreferences.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/webpresence/webpresencepreferences.h b/kopete/plugins/webpresence/webpresencepreferences.h
new file mode 100644
index 00000000..120e7a9a
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresencepreferences.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ webpresencepreferences.h
+ -------------------
+ begin : jeu nov 14 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WEBPRSENCEPREFERECES_H
+#define WEBPRSENCEPREFERECES_H
+
+#include "kcmodule.h"
+
+class WebPresencePrefsUI;
+class KAutoConfig;
+
+/**
+ * Preference widget for the Now Listening plugin, copied from the Cryptography plugin
+ * @author Olivier Goffart
+ */
+class WebPresencePreferences : public KCModule {
+ Q_OBJECT
+
+public:
+ WebPresencePreferences(QWidget *parent = 0, const char *name = 0, const QStringList &args = QStringList());
+
+ virtual void save();
+ virtual void defaults();
+
+private:
+ WebPresencePrefsUI *preferencesDialog;
+ KAutoConfig *kautoconfig;
+
+private slots: // Public slots
+ void widgetModified();
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/plugins/webpresence/webpresenceprefs.ui b/kopete/plugins/webpresence/webpresenceprefs.ui
new file mode 100644
index 00000000..9aae819a
--- /dev/null
+++ b/kopete/plugins/webpresence/webpresenceprefs.ui
@@ -0,0 +1,369 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WebPresencePrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WebPresencePrefsUI</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>426</width>
+ <height>554</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Uploading</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="text">
+ <string>Uplo&amp;ad to:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>uploadURL</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>uploadURL</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>449</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2_2</cstring>
+ </property>
+ <property name="title">
+ <string>Formatting</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>formatHTML</cstring>
+ </property>
+ <property name="text">
+ <string>HTML (simple loo&amp;k)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>HTML 4.01 Transitional using the ISO-8859-1 (aka. Latin 1) character set encoding.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>HTML 4.01 Transitional formatting using ISO-8859-1 (aka. Latin 1) character set encoding.
+
+This version should be easily opened by most web browsers.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>formatXHTML</cstring>
+ </property>
+ <property name="text">
+ <string>XHTML (simple look)</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>XHTML 1.0 Strict</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The resulting page will be formatted using the XHTML 1.0 Strict W3C Recommendation. The character set encoding is UTF-8.
+
+Note that some web browsers do not support XHTML. You should also make sure your web server serves it out with the correct mime type, such as application/xhtml+xml.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>formatXML</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;XML</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Save the output in XML format using UTF-8 character set.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Save the output in XML format using the UTF-8 encoding.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>formatStylesheet</cstring>
+ </property>
+ <property name="text">
+ <string>XML transformation &amp;using this XSLT sheet:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>formatStylesheetURL</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>useImagesHTML</cstring>
+ </property>
+ <property name="text">
+ <string>Repla&amp;ce protocol text with images in (X)HTML</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Replaces the protocol names, such as MSN and IRC with images.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Replaces the protocol names, such as MSN and IRC with images.
+
+Note that you have to manually copy the PNG files into place.
+
+The following files are used by default:
+
+images/msn_protocol.png
+images/icq_protocol.png
+images/jabber_protocol.png
+images/yahoo_protocol.png
+images/aim_protocol.png
+images/irc_protocol.png
+images/sms_protocol.png
+images/gadu_protocol.png
+images/winpopup_protocol.png</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="title">
+ <string>Display Name</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>showName</cstring>
+ </property>
+ <property name="text">
+ <string>Use one of &amp;your IM names</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>showAnotherName</cstring>
+ </property>
+ <property name="text">
+ <string>Use another &amp;name:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>showThisName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>includeIMAddress</cstring>
+ </property>
+ <property name="text">
+ <string>Include &amp;IM addresses</string>
+ </property>
+ </widget>
+ </vbox>
+ </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>16</width>
+ <height>93</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>formatStylesheet</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>formatStylesheetURL</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>showAnotherName</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>showThisName</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>formatXML</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>useImagesHTML</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>formatStylesheet</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>useImagesHTML</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>uploadURL</tabstop>
+ <tabstop>formatHTML</tabstop>
+ <tabstop>formatStylesheetURL</tabstop>
+ <tabstop>useImagesHTML</tabstop>
+ <tabstop>showName</tabstop>
+ <tabstop>showThisName</tabstop>
+ <tabstop>includeIMAddress</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/Makefile.am b/kopete/protocols/Makefile.am
new file mode 100644
index 00000000..6612e8a9
--- /dev/null
+++ b/kopete/protocols/Makefile.am
@@ -0,0 +1,21 @@
+if include_meanwhile
+MEANWHILE=meanwhile
+endif
+
+if include_gadu
+GADU=gadu
+endif
+
+if include_jabber
+JABBER=jabber
+endif
+
+if include_testbed
+TESTBED=testbed
+endif
+
+if include_smsgsm
+SMS=sms
+endif
+
+SUBDIRS = $(TESTBED) groupwise msn irc oscar yahoo winpopup $(SMS) $(JABBER) $(GADU) $(MEANWHILE)
diff --git a/kopete/protocols/configure.in.bot b/kopete/protocols/configure.in.bot
new file mode 100644
index 00000000..6b2c2a18
--- /dev/null
+++ b/kopete/protocols/configure.in.bot
@@ -0,0 +1,11 @@
+# if libidn test fails the following line will be written:
+# include_jabber_TRUE = #
+# the following test will then issue a warning at the end of configure output
+# so users see it more easily
+if test "$have_glib" = no; then
+ echo ""
+ echo "You're missing glib >= 2.0 and its development files. Kopete's MSN"
+ echo "plugin will not have webcam support. If you want webcam support for"
+ echo "MSN, be sure to install glib and its development packages"
+ all_tests=bad
+fi
diff --git a/kopete/protocols/configure.in.in b/kopete/protocols/configure.in.in
new file mode 100644
index 00000000..bc946d92
--- /dev/null
+++ b/kopete/protocols/configure.in.in
@@ -0,0 +1,243 @@
+LIBGG_INCLUDES=""
+LIBGG_LIBS=""
+ac_libgadu_includes=""
+ac_libgadu_libs=""
+
+AC_ARG_WITH(external-libgadu,
+ [AC_HELP_STRING(--with-external-libgadu,
+ [use external libgadu library @<:@default=check@:>@])],
+ [], with_external_libgadu=check)
+
+AC_ARG_WITH(libgadu-includes,
+ AC_HELP_STRING([--with-libgadu-includes=DIR], [where the libgadu includes are.]),
+ [ ac_libgadu_includes="$withval" ])
+
+if test "$ac_libgadu_includes" != "" ; then
+LIBGG_INCLUDES="-I$ac_libgadu_includes"
+fi
+
+AC_ARG_WITH(libgadu-libs,
+ AC_HELP_STRING([--with-libgadu-libs=DIR], [where the libgadu libraries are.]),
+ [ ac_libgadu_libs="$withval" ])
+
+if test "$ac_libgadu_libs" != "" ; then
+ LIBGG_LIBS="-L$ac_libgadu_libs"
+fi
+
+if test "x$with_external_libgadu" != xno; then
+ ac_save_LIBS="$LIBS"
+ ac_save_CFLAGS="$CFLAGS"
+ LIBS="$LIBGG_LIBS -lgadu $LIBPTHREAD"
+ CFLAGS="$CFLAGS $LIBGG_INCLUDES"
+ AC_MSG_CHECKING([libgadu version 1.5(rcX) with pthread support])
+ AC_TRY_RUN(
+ [
+
+ #include <libgadu.h>
+ #include <stdio.h>
+ #include <string.h>
+
+ int main()
+ {
+#if defined __GG_LIBGADU_HAVE_PTHREAD && defined GG_LOGIN60
+ int maj, min, date;
+ sscanf( gg_libgadu_version(), "%u.%u.%u", &maj,&min,&date );
+ if ( maj != 1 ) {
+ return 1;
+ }
+ if ( ( min == 4 || min == 5 ) && date < 20040520 ) {
+ return 1;
+ }
+
+ if ( min == 5 || min == 6 ){
+ return 0;
+ }
+
+#endif
+ return 1;
+ }
+ ], [
+ LIBGG_LIBS="$LIBGG_LIBS -lgadu $LIBPTHREAD"
+ AC_MSG_RESULT([yes])
+ COMPILE_GADU=true
+ use_libgadu_copy=
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ LIBS="$ac_save_LIBS"
+ CFLAGS="$ac_save_CFLAGS"
+
+ if test "x$with_external_libgadu" != xcheck && test -z "$COMPILE_GADU"; then
+ AC_MSG_ERROR([--with-external-libgadu was given, but test for libgadu failed])
+ fi
+fi
+
+if test -z "$COMPILE_GADU"; then
+ AC_MSG_CHECKING([if supplied libgadu-copy can be used])
+ if test "$kde_use_threading" = "yes"; then
+ AC_MSG_RESULT([yes])
+ use_libgadu_copy=yes
+ COMPILE_GADU=true
+ else
+ AC_MSG_RESULT([no (no pthread), support for Gadu-Gadu will be disabled])
+ use_libgadu_copy=
+ COMPILE_GADU=
+ fi
+fi
+
+AC_SUBST(LIBGG_INCLUDES)
+AC_SUBST(LIBGG_LIBS)
+AC_SUBST(COMPILE_GADU)
+AM_CONDITIONAL(include_gadu, test -n "$COMPILE_GADU")
+AM_CONDITIONAL(include_libggcopy, test -n "$use_libgadu_copy")
+
+if test "$use_libgadu_copy" = "yes"; then
+ AM_CONFIG_HEADER(kopete/protocols/gadu/libgadu/libgadu-config.h)
+
+ if test "$ac_cv_c_bigendian" = "yes"; then
+ AC_DEFINE_UNQUOTED([__GG_LIBGADU_BIGENDIAN], 1, [Define if big endian])
+ fi
+ KDE_CHECK_LONG_LONG()
+ if test "$kde_cv_c_long_long" = "yes"; then
+ AC_DEFINE_UNQUOTED([__GG_LIBGADU_HAVE_LONG_LONG], 1, [long long support])
+ fi
+ KDE_CHECK_SSL()
+ if test "$have_ssl" = "yes"; then
+ AC_DEFINE_UNQUOTED([__GG_LIBGADU_HAVE_OPENSSL], 1, [Define if SSL support is available])
+ fi
+ AC_MSG_CHECKING([for C99-compatible vsnprintf()])
+ AC_TRY_RUN(
+ [
+ #include <stdio.h>
+ int main()
+ {
+ char tmp;
+ return (snprintf(&tmp, sizeof(tmp), "test") != 4);
+ }
+ ],[
+ AC_MSG_RESULT([yes])
+ AC_DEFINE_UNQUOTED([__GG_LIBGADU_HAVE_C99_VSNPRINTF], 1, [C99 vsnprintf() available])
+ ], [
+ AC_MSG_RESULT([no])
+ ])
+ AC_CHECK_FUNCS([va_copy],
+ [AC_DEFINE_UNQUOTED([__GG_LIBGADU_HAVE_VA_COPY], 1, [va_copy])],[])
+ AC_CHECK_FUNCS([_va_copy],
+ [AC_DEFINE_UNQUOTED([__GG_LIBGADU_HAVE__VA_COPY], 1, [__va_copy])],[])
+fi
+
+KDE_PKG_CHECK_MODULES(IDN, libidn, have_libidn=yes, have_libidn=no)
+if test x$have_libidn = xno; then
+ AC_MSG_WARN([Libidn not found, Kopete Jabber plugin will not be compiled])
+else
+ AC_DEFINE(LIBIDN, 1, [Define to 1 if you want IDN support.])
+fi
+AC_SUBST(IDN_CFLAGS)
+AC_SUBST(IDN_LIBS)
+
+AC_MSG_CHECKING([if Libidn can be used])
+AC_MSG_RESULT($have_libidn)
+
+AM_CONDITIONAL(include_jabber, test "$have_libidn" = "yes")
+
+# Sametime support
+
+# lower and upper-bound versions of Meanwhile library
+m4_define(libmeanwhile_version_min, 1.0.1)
+m4_define(libmeanwhile_version_max, 1.1.0)
+
+# Let the user disable the plugin
+AC_ARG_ENABLE(meanwhile,
+ AC_HELP_STRING([--disable-meanwhile],
+ [disable the Kopete Meanwhile plugin (Lotus Sametime support) @<:@default=enabled@:>@]),
+ )
+
+if test "x$enable_meanwhile" != "xno"; then
+ # Check and setup for libmeanwhile
+ KDE_PKG_CHECK_MODULES(MEANWHILE,
+ [meanwhile >= libmeanwhile_version_min meanwhile < libmeanwhile_version_max],
+ [have_libmeanwhile=yes], [have_libmeanwhile=no])
+
+ if test "x$have_libmeanwhile" = "xno"; then
+ enable_meanwhile=no
+ AC_MSG_RESULT([not found])
+ else
+ AC_MSG_RESULT([found])
+ fi
+fi
+
+AC_SUBST(MEANWHILE_CFLAGS)
+AC_SUBST(MEANWHILE_LIBS)
+
+AC_MSG_CHECKING([if Meanwhile plugin should be compiled])
+if test "x$enable_meanwhile" != "xno"; then
+ AC_MSG_RESULT([yes])
+else
+ AC_MSG_RESULT([no])
+fi
+
+# Set the flag to compile meanwhile
+AM_CONDITIONAL(include_meanwhile, [test "x$enable_meanwhile" != "xno"])
+
+# testbed protocol
+dnl define the configure option that disables testbed protocol
+AC_ARG_ENABLE(testbed, [ --disable-testbed disable kopete testbed protocol compilation ], with_testbed=$enableval, with_testbed=yes)
+AM_CONDITIONAL(include_testbed, test "$with_testbed" = "yes")
+
+PKG_CHECK_MODULES(GLIB, glib-2.0 gmodule-2.0, have_glib=yes, have_glib=no)
+if test x$have_glib = xno; then
+ AC_MSG_WARN([GLib 2.0 is required for MSN webcam and Jabber Jingle. You can get it from http://www.gtk.org/])
+else
+ AC_SUBST(GLIB_CFLAGS)
+ AC_SUBST(GLIB_LIBS)
+ AC_DEFINE(HAVE_GLIB, 1, [Glib is required for oRTP code and libmimic code])
+fi
+
+if test "x$have_glib" != "xyes"; then
+ compile_msn_webcam=no
+ msn_webcam_val=0
+else
+ compile_msn_webcam=yes
+ msn_webcam_val=1
+fi
+
+AC_MSG_CHECKING([if MSN webcam support should be enabled])
+AC_MSG_RESULT($compile_msn_webcam)
+AC_DEFINE_UNQUOTED(MSN_WEBCAM, $msn_webcam_val, [Define if MSN webcam support can be enabled])
+
+AM_CONDITIONAL(include_msn_webcam, test "x$compile_msn_webcam" = "xyes")
+
+# Check for sms protocol
+AC_ARG_ENABLE(smsgsm,
+ AC_HELP_STRING([--disable-smsgsm], [disable the GSM SMS protocol]),
+ [compile_smsgsm=$enableval],
+ [compile_smsgsm=yes]
+ )
+
+AC_LANG_PUSH(C++)
+ac_save_LIBS="$LIBS"
+LIBS="-lgsmme $LIBS"
+AC_TRY_LINK([#include <gsmlib/gsm_util.h>],[(void)gsmlib::latin1ToGsm("text");],
+ [have_smsgsm_lib=yes],
+ [have_smsgsm_lib=no])
+LIBS=$ac_save_LIBS
+
+AC_CHECK_HEADER(gsmlib/gsm_util.h,
+ [have_smsgsm_inc=yes],
+ [have_smsgsm_inc=no])
+
+if test "x$have_smsgsm_lib" != "xyes" || test "x$have_smsgsm_inc" != "xyes"; then
+ compile_smsgsm=no
+fi
+AC_LANG_POP(C++)
+
+# Let the user know
+AC_MSG_CHECKING([if SMSGSM Plugin should be compiled])
+AC_MSG_RESULT($compile_smsgsm)
+
+# Here we go
+AM_CONDITIONAL(include_smsgsm, [test "x$compile_smsgsm" = "xyes"])
+
+if test "x$compile_smsgsm" = "xyes"; then
+ AC_DEFINE(INCLUDE_SMSGSM, 1, [Define to compile with GSM SMS support])
+fi
diff --git a/kopete/protocols/gadu/Makefile.am b/kopete/protocols/gadu/Makefile.am
new file mode 100644
index 00000000..d896950a
--- /dev/null
+++ b/kopete/protocols/gadu/Makefile.am
@@ -0,0 +1,38 @@
+METASOURCES = AUTO
+if include_libggcopy
+LIBGADU_COPY=libgadu
+GG_INCLUDES=-Ilibgadu -I$(srcdir)/libgadu $(SSL_INCLUDES)
+GG_LIBS=$(top_builddir)/kopete/protocols/gadu/libgadu/libgadu_copy.la \
+ $(LIBPTHREAD)
+else
+LIBGADU_COPY=
+GG_INCLUDES=$(LIBGG_INCLUDES)
+GG_LIBS=$(LIBGG_LIBS)
+endif
+
+
+SUBDIRS = ui icons $(LIBGADU_COPY)
+AM_CPPFLAGS = -I$(srcdir)/ui \
+ -I./ui \
+ -I./lib \
+ -I$(srcdir)/lib \
+ $(KOPETE_INCLUDES) \
+ $(all_includes) \
+ $(GG_INCLUDES)
+
+kde_module_LTLIBRARIES = kopete_gadu.la
+
+kopete_gadu_la_SOURCES = gaduaway.cpp gadueditcontact.cpp gaducommands.cpp \
+ gadueditaccount.cpp gadusession.cpp gaducontact.cpp \
+ gaduaddcontactpage.cpp gaduprotocol.cpp gaduaccount.cpp \
+ gadupubdir.cpp gaduregisteraccount.cpp \
+ gaducontactlist.cpp gadurichtextformat.cpp \
+ gadudccserver.cpp gadudcctransaction.cpp gadudcc.cpp
+
+kopete_gadu_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_gadu_la_LIBADD = ./ui/libgaduui.la \
+ $(top_builddir)/kopete/libkopete/libkopete.la \
+ $(GG_LIBS)
+
+service_DATA = kopete_gadu.desktop
+servicedir = $(kde_servicesdir)
diff --git a/kopete/protocols/gadu/README.gadu b/kopete/protocols/gadu/README.gadu
new file mode 100644
index 00000000..6c7a929e
--- /dev/null
+++ b/kopete/protocols/gadu/README.gadu
@@ -0,0 +1,43 @@
+
+GaduGadu is primarily used and created for Poles, but it does not mean that it
+cannot be used by anyone else. There is only one small issue, by design
+protocol uses cp1250 encoding. Unfortunately, there's nothing we can do about
+that, as it's needed to maintain compatibility with the Windows client, which
+is the only one supported (and created) by the creators of gadu-gadu - sms-
+express company.
+
+Gadu-Gadu plugin uses libgadu. If it's not avaliable in the system,
+it will automaticaly switch to it's copy in /protocols/gadu/lib (v1.5).
+
+Following describes what to do if you still want to use external library version:
+
+You can download libgadu (part of ekg package) from http://dev.null.pl/ekg,
+This should be version 1.5 (release candidate or stable), please stick with version
+1.5 if possible. Author of libgadu/ekg does not keep any compatibility between
+minor releases. It should support protocol version 6.0 by default.
+
+You have to download the ekg communicator package
+(which is a gadugadu client for the console).
+To install from sources, run:
+
+./configure --enable-shared --disable-static --enable-dynamic --with-pthread --prefix=/usr
+make
+(and as root)
+make install.
+
+It is also available pre-packaged, e.g. for debian unstable, by default as
+libgadu2 (binary) and libgadu-dev (development).
+In order to compile kopete from sources, including the gadu-gadu plugin you
+will need the libgadu headers and libraries installed and set up in your
+system.
+
+Please refer INSTALL for information about building kopete from sources.
+
+If you're looking for more information, please refer to http://kopete.kde.org
+and read the FAQ .
+
+If you have found an error in kopete, or in the gadu-gadu plugin
+- please use the kde bug tracking system at http://bugs.kde.org/
+
+Grzegorz Jaskiewicz aka Kain/K4
+gj AT pointblue DOT com DOT pl
diff --git a/kopete/protocols/gadu/gaduaccount.cpp b/kopete/protocols/gadu/gaduaccount.cpp
new file mode 100644
index 00000000..6dd737ce
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaccount.cpp
@@ -0,0 +1,1261 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2003 Zack Rusin <zack@kde.org>
+//
+// gaduaccount.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA
+
+#include "gaduaccount.h"
+#include "gaducontact.h"
+#include "gaduprotocol.h"
+#include "gaduawayui.h"
+#include "gaduaway.h"
+#include "gadupubdir.h"
+#include "gadudcc.h"
+#include "gadudcctransaction.h"
+
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopetepassword.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+
+#include <kpassdlg.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+
+#include <qapplication.h>
+#include <qdialog.h>
+#include <qtimer.h>
+#include <qtextcodec.h>
+#include <qptrlist.h>
+#include <qtextstream.h>
+#include <qhostaddress.h>
+
+#include <netinet/in.h>
+
+class GaduAccountPrivate {
+
+public:
+ GaduAccountPrivate() {}
+
+ GaduSession* session_;
+ GaduDCC* gaduDcc_;
+
+ QTimer* pingTimer_;
+
+ QTextCodec* textcodec_;
+ KFileDialog* saveListDialog;
+ KFileDialog* loadListDialog;
+
+ KActionMenu* actionMenu_;
+ KAction* searchAction;
+ KAction* listputAction;
+ KAction* listToFileAction;
+ KAction* listFromFileAction;
+ KAction* friendsModeAction;
+ bool connectWithSSL;
+
+ int currentServer;
+ unsigned int serverIP;
+
+ QString lastDescription;
+ bool forFriends;
+ bool ignoreAnons;
+
+ QTimer* exportTimer_;
+ bool exportUserlist;
+
+ KConfigGroup* config;
+ Kopete::OnlineStatus status;
+ QValueList<unsigned int> servers;
+ KGaduLoginParams loginInfo;
+};
+
+// 10s is enough ;p
+#define USERLISTEXPORT_TIMEOUT (10*1000)
+
+// FIXME: use dynamic cache please, i consider this as broken resolution of this problem
+static const char* const servers_ip[] = {
+ "217.17.41.85",
+ "217.17.41.83",
+ "217.17.41.84",
+ "217.17.41.86",
+ "217.17.41.87",
+ "217.17.41.88",
+ "217.17.41.92",
+ "217.17.41.93",
+ "217.17.45.133",
+ "217.17.45.143",
+ "217.17.45.144"
+};
+
+#define NUM_SERVERS (sizeof(servers_ip)/sizeof(char*))
+
+ GaduAccount::GaduAccount( Kopete::Protocol* parent, const QString& accountID,const char* name )
+: Kopete::PasswordedAccount( parent, accountID, 0, name )
+{
+ QHostAddress ip;
+ p = new GaduAccountPrivate;
+
+ p->pingTimer_ = NULL;
+ p->saveListDialog = NULL;
+ p->loadListDialog = NULL;
+ p->forFriends = false;
+
+ p->textcodec_ = QTextCodec::codecForName( "CP1250" );
+ p->session_ = new GaduSession( this, "GaduSession" );
+
+ KGlobal::config()->setGroup( "Gadu" );
+
+ setMyself( new GaduContact( accountId().toInt(), accountId(), this, Kopete::ContactList::self()->myself() ) );
+
+ p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
+ p->lastDescription = QString::null;
+
+ for ( unsigned int i = 0; i < NUM_SERVERS ; i++ ) {
+ ip.setAddress( QString( servers_ip[i] ) );
+ p->servers.append( htonl( ip.toIPv4Address() ) );
+ kdDebug( 14100 ) << "adding IP: " << p->servers[ i ] << " to cache" << endl;
+ }
+ p->currentServer = -1;
+ p->serverIP = 0;
+
+ // initialize KGaduLogin structure to default values
+ p->loginInfo.uin = accountId().toInt();
+ p->loginInfo.useTls = false;
+ p->loginInfo.status = GG_STATUS_AVAIL;
+ p->loginInfo.server = 0;
+ p->loginInfo.client_port = 0;
+ p->loginInfo.client_addr = 0;
+
+ p->pingTimer_ = new QTimer( this );
+ p->exportTimer_ = new QTimer( this );
+
+ p->exportUserlist = false;
+ p->gaduDcc_ = NULL;
+
+ p->config = configGroup();
+
+ p->ignoreAnons = ignoreAnons();
+ p->forFriends = loadFriendsMode();
+
+ initConnections();
+ initActions();
+
+ QString nick = p->config->readEntry( QString::fromAscii( "nickName" ) );
+ if ( !nick.isNull() ) {
+ myself()->setProperty( Kopete::Global::Properties::self()->nickName(), nick );
+ }
+ else {
+ myself()->setProperty( Kopete::Global::Properties::self()->nickName(), accountId() );
+ p->config->writeEntry( QString::fromAscii( "nickName" ), accountId() );
+ }
+}
+
+GaduAccount::~GaduAccount()
+{
+ delete p;
+}
+
+void
+GaduAccount::initActions()
+{
+ p->searchAction = new KAction( i18n( "&Search for Friends" ), "", 0,
+ this, SLOT( slotSearch() ), this, "actionSearch" );
+ p->listputAction = new KAction( i18n( "Export Contacts to Server" ), "", 0,
+ this, SLOT( slotExportContactsList() ), this, "actionListput" );
+ p->listToFileAction = new KAction( i18n( "Export Contacts to File..." ), "", 0,
+ this, SLOT( slotExportContactsListToFile() ), this, "actionListputFile" );
+ p->listFromFileAction = new KAction( i18n( "Import Contacts From File..." ), "", 0,
+ this, SLOT( slotImportContactsFromFile() ), this, "actionListgetFile" );
+ p->friendsModeAction = new KToggleAction( i18n( "Only for Friends" ), "", 0,
+ this, SLOT( slotFriendsMode() ), this,
+ "actionFriendsMode" );
+
+ static_cast<KToggleAction*>(p->friendsModeAction)->setChecked( p->forFriends );
+}
+
+void
+GaduAccount::initConnections()
+{
+ QObject::connect( p->session_, SIGNAL( error( const QString&, const QString& ) ),
+ SLOT( error( const QString&, const QString& ) ) );
+ QObject::connect( p->session_, SIGNAL( messageReceived( KGaduMessage* ) ),
+ SLOT( messageReceived( KGaduMessage* ) ) );
+ QObject::connect( p->session_, SIGNAL( contactStatusChanged( KGaduNotify* ) ),
+ SLOT( contactStatusChanged( KGaduNotify* ) ) );
+ QObject::connect( p->session_, SIGNAL( connectionFailed( gg_failure_t )),
+ SLOT( connectionFailed( gg_failure_t ) ) );
+ QObject::connect( p->session_, SIGNAL( connectionSucceed( ) ),
+ SLOT( connectionSucceed( ) ) );
+ QObject::connect( p->session_, SIGNAL( disconnect( Kopete::Account::DisconnectReason ) ),
+ SLOT( slotSessionDisconnect( Kopete::Account::DisconnectReason ) ) );
+ QObject::connect( p->session_, SIGNAL( ackReceived( unsigned int ) ),
+ SLOT( ackReceived( unsigned int ) ) );
+ QObject::connect( p->session_, SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ),
+ SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) );
+ QObject::connect( p->session_, SIGNAL( userListExported() ),
+ SLOT( userListExportDone() ) );
+ QObject::connect( p->session_, SIGNAL( userListRecieved( const QString& ) ),
+ SLOT( userlist( const QString& ) ) );
+ QObject::connect( p->session_, SIGNAL( incomingCtcp( unsigned int ) ),
+ SLOT( slotIncomingDcc( unsigned int ) ) );
+
+ QObject::connect( p->pingTimer_, SIGNAL( timeout() ),
+ SLOT( pingServer() ) );
+
+ QObject::connect( p->exportTimer_, SIGNAL( timeout() ),
+ SLOT( slotUserlistSynch() ) );
+}
+
+void
+GaduAccount::setAway( bool isAway, const QString& awayMessage )
+{
+ unsigned int currentStatus;
+
+ if ( isAway ) {
+ currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_BUSY : GG_STATUS_BUSY_DESCR;
+ }
+ else{
+ currentStatus = ( awayMessage.isEmpty() ) ? GG_STATUS_AVAIL : GG_STATUS_AVAIL_DESCR;
+ }
+ changeStatus( GaduProtocol::protocol()->convertStatus( currentStatus ), awayMessage );
+}
+
+
+KActionMenu*
+GaduAccount::actionMenu()
+{
+ kdDebug(14100) << "actionMenu() " << endl;
+
+ p->actionMenu_ = new KActionMenu( accountId(), myself()->onlineStatus().iconFor( this ), this );
+ p->actionMenu_->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ), i18n( "%1 <%2> " ).
+ arg( myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(), accountId() ) );
+
+ if ( p->session_->isConnected() ) {
+ p->searchAction->setEnabled( TRUE );
+ p->listputAction->setEnabled( TRUE );
+ p->friendsModeAction->setEnabled( TRUE );
+ }
+ else {
+ p->searchAction->setEnabled( FALSE );
+ p->listputAction->setEnabled( FALSE );
+ p->friendsModeAction->setEnabled( FALSE );
+ }
+
+ if ( contacts().count() > 1 ) {
+ if ( p->saveListDialog ) {
+ p->listToFileAction->setEnabled( FALSE );
+ }
+ else {
+ p->listToFileAction->setEnabled( TRUE );
+ }
+
+ p->listToFileAction->setEnabled( TRUE );
+ }
+ else {
+ p->listToFileAction->setEnabled( FALSE );
+ }
+
+ if ( p->loadListDialog ) {
+ p->listFromFileAction->setEnabled( FALSE );
+ }
+ else {
+ p->listFromFileAction->setEnabled( TRUE );
+ }
+ p->actionMenu_->insert( new KAction( i18n( "Go O&nline" ),
+ GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ).iconFor( this ),
+ 0, this, SLOT( slotGoOnline() ), this, "actionGaduConnect" ) );
+
+ p->actionMenu_->insert( new KAction( i18n( "Set &Busy" ),
+ GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ).iconFor( this ),
+ 0, this, SLOT( slotGoBusy() ), this, "actionGaduConnect" ) );
+
+ p->actionMenu_->insert( new KAction( i18n( "Set &Invisible" ),
+ GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ).iconFor( this ),
+ 0, this, SLOT( slotGoInvisible() ), this, "actionGaduConnect" ) );
+
+ p->actionMenu_->insert( new KAction( i18n( "Go &Offline" ),
+ GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ).iconFor( this ),
+ 0, this, SLOT( slotGoOffline() ), this, "actionGaduConnect" ) );
+
+ p->actionMenu_->insert( new KAction( i18n( "Set &Description..." ), "info",
+ 0, this, SLOT( slotDescription() ), this, "actionGaduDescription" ) );
+
+ p->actionMenu_->insert( p->friendsModeAction );
+
+ p->actionMenu_->popupMenu()->insertSeparator();
+
+ p->actionMenu_->insert( p->searchAction );
+
+ p->actionMenu_->popupMenu()->insertSeparator();
+
+ p->actionMenu_->insert( p->listputAction );
+
+ p->actionMenu_->popupMenu()->insertSeparator();
+
+ p->actionMenu_->insert( p->listToFileAction );
+ p->actionMenu_->insert( p->listFromFileAction );
+
+ return p->actionMenu_;
+}
+
+void
+GaduAccount::connectWithPassword(const QString& password)
+{
+ if (password.isEmpty()) {
+ return;
+ }
+ if (isConnected ())
+ return;
+ // FIXME: add status description to this mechainsm, this is a hack now. libkopete design issue.
+ changeStatus( initialStatus(), p->lastDescription );
+}
+
+void
+GaduAccount::disconnect()
+{
+ disconnect( Manual );
+}
+
+void
+GaduAccount::disconnect( DisconnectReason reason )
+{
+ slotGoOffline();
+ p->connectWithSSL = true;
+ Kopete::Account::disconnected( reason );
+}
+
+void
+GaduAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason )
+{
+ kdDebug(14100) << k_funcinfo << "Called" << endl;
+ changeStatus( status, reason);
+}
+
+void
+GaduAccount::slotUserlistSynch()
+{
+ if ( !p->exportUserlist ) {
+ return;
+ }
+ p->exportUserlist = false;
+ kdDebug(14100) << "userlist changed, exporting" << endl;
+ slotExportContactsList();
+}
+
+void
+GaduAccount::userlistChanged()
+{
+ p->exportUserlist = true;
+ p->exportTimer_->changeInterval( USERLISTEXPORT_TIMEOUT );
+}
+
+bool
+GaduAccount::createContact( const QString& contactId, Kopete::MetaContact* parentContact )
+{
+ kdDebug(14100) << "createContact " << contactId << endl;
+
+ uin_t uinNumber = contactId.toUInt();
+ GaduContact* newContact = new GaduContact( uinNumber, parentContact->displayName(),this, parentContact );
+ newContact->setParentIdentity( accountId() );
+ addNotify( uinNumber );
+
+ userlistChanged();
+
+ return true;
+}
+
+void
+GaduAccount::changeStatus( const Kopete::OnlineStatus& status, const QString& descr )
+{
+ unsigned int ns;
+
+ kdDebug(14100) << "##### change status #####" << endl;
+ kdDebug(14100) << "### Status = " << p->session_->isConnected() << endl;
+ kdDebug(14100) << "### Status description = \"" << descr << "\"" << endl;
+
+ // if change to not available, log off
+ if ( GG_S_NA( status.internalStatus() ) ) {
+ if ( !p->session_->isConnected() ) {
+ return;//already logged off
+ }
+ else {
+ if ( status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) {
+ if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 ) {
+ return;
+ }
+ }
+ }
+ p->session_->logoff();
+ dccOff();
+ }
+ else {
+ // if status is for no desc, but we get some desc, than convert it to status with desc
+ if (!descr.isEmpty() && !GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) {
+ // and rerun us again. This won't cause any recursive call, as both conversions are static
+ ns = GaduProtocol::protocol()->statusToWithDescription( status );
+ changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr );
+ return;
+ }
+
+ // well, if it's empty but we want to set status with desc, change it too
+ if (descr.isEmpty() && GaduProtocol::protocol()->statusWithDescription( status.internalStatus() ) ) {
+ ns = GaduProtocol::protocol()->statusToWithoutDescription( status );
+ changeStatus( GaduProtocol::protocol()->convertStatus( ns ), descr );
+ return;
+ }
+
+ if ( !p->session_->isConnected() ) {
+ if ( password().cachedValue().isEmpty() ) {
+ // FIXME: when status string added to connect(), use it here
+ p->lastDescription = descr;
+ connect( status/*, descr*/ );
+ return;
+ }
+
+ if ( useTls() != TLS_no ) {
+ p->connectWithSSL = true;
+ }
+ else {
+ p->connectWithSSL = false;
+ }
+ dccOn();
+ p->serverIP = 0;
+ p->currentServer = -1;
+ p->status = status;
+ kdDebug(14100) << "#### Connecting..., tls option "<< (int)useTls() << " " << endl;
+ p->lastDescription = descr;
+ slotLogin( status.internalStatus(), descr );
+ return;
+ }
+ else {
+ p->status = status;
+ if ( descr.isEmpty() ) {
+ if ( p->session_->changeStatus( status.internalStatus(), p->forFriends ) != 0 )
+ return;
+ }
+ else {
+ if ( p->session_->changeStatusDescription( status.internalStatus(), descr, p->forFriends ) != 0 )
+ return;
+ }
+ }
+ }
+
+ myself()->setOnlineStatus( status );
+ myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, descr );
+
+ if ( status.internalStatus() == GG_STATUS_NOT_AVAIL || status.internalStatus() == GG_STATUS_NOT_AVAIL_DESCR ) {
+ if ( p->pingTimer_ ){
+ p->pingTimer_->stop();
+ }
+ }
+ p->lastDescription = descr;
+}
+
+void
+GaduAccount::slotLogin( int status, const QString& dscr )
+{
+ p->lastDescription = dscr;
+
+ myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING ));
+ myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, dscr );
+
+ if ( !p->session_->isConnected() ) {
+ if ( password().cachedValue().isEmpty() ) {
+ connectionFailed( GG_FAILURE_PASSWORD );
+ }
+ else {
+ p->loginInfo.password = password().cachedValue();
+ p->loginInfo.useTls = p->connectWithSSL;
+ p->loginInfo.status = status;
+ p->loginInfo.statusDescr = dscr;
+ p->loginInfo.forFriends = p->forFriends;
+ p->loginInfo.server = p->serverIP;
+ if ( dccEnabled() ) {
+ p->loginInfo.client_addr = gg_dcc_ip;
+ p->loginInfo.client_port = gg_dcc_port;
+ }
+ else {
+ p->loginInfo.client_addr = 0;
+ p->loginInfo.client_port = 0;
+ }
+ p->session_->login( &p->loginInfo );
+ }
+ }
+ else {
+ p->session_->changeStatus( status );
+ }
+}
+
+void
+GaduAccount::slotLogoff()
+{
+ if ( p->session_->isConnected() || p->status == GaduProtocol::protocol()->convertStatus( GG_STATUS_CONNECTING )) {
+ p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
+ changeStatus( p->status );
+ p->session_->logoff();
+ dccOff();
+ }
+}
+
+void
+GaduAccount::slotGoOnline()
+{
+ changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_AVAIL ) );
+}
+void
+GaduAccount::slotGoOffline()
+{
+ slotLogoff();
+ dccOff();
+}
+
+void
+GaduAccount::slotGoInvisible()
+{
+ changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_INVISIBLE ) );
+}
+
+void
+GaduAccount::slotGoBusy()
+{
+ changeStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_BUSY ) );
+}
+
+void
+GaduAccount::removeContact( const GaduContact* c )
+{
+ if ( isConnected() ) {
+ const uin_t u = c->uin();
+ p->session_->removeNotify( u );
+ userlistChanged();
+ }
+}
+
+void
+GaduAccount::addNotify( uin_t uin )
+{
+ if ( p->session_->isConnected() ) {
+ p->session_->addNotify( uin );
+ }
+}
+
+void
+GaduAccount::notify( uin_t* userlist, int count )
+{
+ if ( p->session_->isConnected() ) {
+ p->session_->notify( userlist, count );
+ }
+}
+
+void
+GaduAccount::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass )
+{
+ if ( p->session_->isConnected() ) {
+ p->session_->sendMessage( recipient, msg, msgClass );
+ }
+}
+
+void
+GaduAccount::error( const QString& title, const QString& message )
+{
+ KMessageBox::error( Kopete::UI::Global::mainWidget(), title, message );
+}
+
+void
+GaduAccount::messageReceived( KGaduMessage* gaduMessage )
+{
+ GaduContact* contact = 0;
+ QPtrList<Kopete::Contact> contactsListTmp;
+
+ // FIXME:check for ignored users list
+
+ if ( gaduMessage->sender_id == 0 ) {
+ //system message, display them or not?
+ kdDebug(14100) << "####" << " System Message " << gaduMessage->message << endl;
+ return;
+ }
+
+ contact = static_cast<GaduContact*> ( contacts()[ QString::number( gaduMessage->sender_id ) ] );
+
+ if ( !contact ) {
+ if ( p->ignoreAnons == true ) {
+ return;
+ }
+
+ Kopete::MetaContact* metaContact = new Kopete::MetaContact ();
+ metaContact->setTemporary ( true );
+ contact = new GaduContact( gaduMessage->sender_id,
+ QString::number( gaduMessage->sender_id ), this, metaContact );
+ Kopete::ContactList::self ()->addMetaContact( metaContact );
+ addNotify( gaduMessage->sender_id );
+ }
+
+ contactsListTmp.append( myself() );
+ Kopete::Message msg( gaduMessage->sendTime, contact, contactsListTmp, gaduMessage->message, Kopete::Message::Inbound, Kopete::Message::RichText );
+ contact->messageReceived( msg );
+}
+
+void
+GaduAccount::ackReceived( unsigned int recipient )
+{
+ GaduContact* contact;
+
+ contact = static_cast<GaduContact*> ( contacts()[ QString::number( recipient ) ] );
+ if ( contact ) {
+ kdDebug(14100) << "####" << "Received an ACK from " << contact->uin() << endl;
+ contact->messageAck();
+ }
+ else {
+ kdDebug(14100) << "####" << "Received an ACK from an unknown user : " << recipient << endl;
+ }
+}
+
+void
+GaduAccount::contactStatusChanged( KGaduNotify* gaduNotify )
+{
+ kdDebug(14100) << "####" << " contact's status changed, uin:" << gaduNotify->contact_id <<endl;
+
+ GaduContact* contact;
+
+ contact = static_cast<GaduContact*>( contacts()[ QString::number( gaduNotify->contact_id ) ] );
+ if( !contact ) {
+ kdDebug(14100) << "Notify not in the list " << gaduNotify->contact_id << endl;
+ return;
+ }
+
+ contact->changedStatus( gaduNotify );
+}
+
+void
+GaduAccount::pong()
+{
+ kdDebug(14100) << "####" << " Pong..." << endl;
+}
+
+void
+GaduAccount::pingServer()
+{
+ kdDebug(14100) << "####" << " Ping..." << endl;
+ p->session_->ping();
+}
+
+void
+GaduAccount::connectionFailed( gg_failure_t failure )
+{
+ bool tryReconnect = false;
+ QString pass;
+
+
+ switch (failure) {
+ case GG_FAILURE_PASSWORD:
+ password().setWrong();
+ // user pressed CANCEL
+ p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
+ myself()->setOnlineStatus( p->status );
+ disconnected( BadPassword );
+ return;
+ default:
+ if ( p->connectWithSSL ) {
+ if ( useTls() != TLS_only ) {
+ slotCommandDone( QString::null, i18n( "connection using SSL was not possible, retrying without." ) );
+ kdDebug( 14100 ) << "try without tls now" << endl;
+ p->connectWithSSL = false;
+ tryReconnect = true;
+ p->currentServer = -1;
+ p->serverIP = 0;
+ break;
+ }
+ }
+ else {
+ if ( p->currentServer == NUM_SERVERS - 1 ) {
+ p->serverIP = 0;
+ p->currentServer = -1;
+ kdDebug(14100) << "trying : " << "IP from hub " << endl;
+ }
+ else {
+ p->serverIP = p->servers[ ++p->currentServer ];
+ kdDebug(14100) << "trying : " << p->currentServer << " IP " << p->serverIP << endl;
+ tryReconnect = true;
+ }
+ }
+ break;
+ }
+
+ if ( tryReconnect ) {
+ slotLogin( p->status.internalStatus() , p->lastDescription );
+ }
+ else {
+ error( i18n( "unable to connect to the Gadu-Gadu server(\"%1\")." ).arg( GaduSession::failureDescription( failure ) ),
+ i18n( "Connection Error" ) );
+ p->status = GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL );
+ myself()->setOnlineStatus( p->status );
+ disconnected( InvalidHost );
+ }
+}
+
+void
+GaduAccount::dccOn()
+{
+ if ( dccEnabled() ) {
+ if ( !p->gaduDcc_ ) {
+ p->gaduDcc_ = new GaduDCC( this );
+ }
+ kdDebug( 14100 ) << " turn DCC on for " << accountId() << endl;
+ p->gaduDcc_->registerAccount( this );
+ p->loginInfo.client_port = p->gaduDcc_->listeingPort();
+ }
+}
+
+void
+GaduAccount::dccOff()
+{
+ if ( p->gaduDcc_ ) {
+ kdDebug( 14100 ) << "destroying dcc in gaduaccount " << endl;
+ delete p->gaduDcc_;
+ p->gaduDcc_ = NULL;
+ p->loginInfo.client_port = 0;
+ p->loginInfo.client_addr = 0;
+ }
+}
+
+void
+GaduAccount::slotIncomingDcc( unsigned int uin )
+{
+ GaduContact* contact;
+ GaduDCCTransaction* trans;
+
+ if ( !uin ) {
+ return;
+ }
+
+ contact = static_cast<GaduContact*>( contacts()[ QString::number( uin ) ] );
+
+ if ( !contact ) {
+ kdDebug(14100) << "attempt to make dcc connection from unknown uin " << uin << endl;
+ return;
+ }
+
+ // if incapabile to transfer files, forget about it.
+ if ( contact->contactPort() < 10 ) {
+ kdDebug(14100) << "can't respond to " << uin << " request, his listeing port is too low" << endl;
+ return;
+ }
+
+ trans = new GaduDCCTransaction( p->gaduDcc_ );
+ if ( trans->setupIncoming( p->loginInfo.uin, contact ) == false ) {
+ delete trans;
+ }
+
+}
+
+void
+GaduAccount::connectionSucceed( )
+{
+ kdDebug(14100) << "#### Gadu-Gadu connected! " << endl;
+ p->status = GaduProtocol::protocol()->convertStatus( p->session_->status() );
+ myself()->setOnlineStatus( p->status );
+ myself()->setProperty( GaduProtocol::protocol()->propAwayMessage, p->lastDescription );
+ startNotify();
+
+ p->session_->requestContacts();
+ p->pingTimer_->start( 3*60*1000 );//3 minute timeout
+ pingServer();
+
+ // check if we need to export userlist every USERLISTEXPORT_TIMEOUT ms
+ p->exportTimer_->start( USERLISTEXPORT_TIMEOUT );
+}
+
+void
+GaduAccount::startNotify()
+{
+ int i = 0;
+ if ( !contacts().count() ) {
+ return;
+ }
+
+ QDictIterator<Kopete::Contact> kopeteContactsList( contacts() );
+
+ uin_t* userlist = 0;
+ userlist = new uin_t[ contacts().count() ];
+
+ for( i=0 ; kopeteContactsList.current() ; ++kopeteContactsList ) {
+ userlist[i++] = static_cast<GaduContact*> ((*kopeteContactsList))->uin();
+ }
+
+ p->session_->notify( userlist, contacts().count() );
+ delete [] userlist;
+}
+
+void
+GaduAccount::slotSessionDisconnect( Kopete::Account::DisconnectReason reason )
+{
+ uin_t status;
+
+ kdDebug(14100) << "Disconnecting" << endl;
+
+ if (p->pingTimer_) {
+ p->pingTimer_->stop();
+ }
+
+ setAllContactsStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) );
+
+ status = myself()->onlineStatus().internalStatus();
+ if ( status != GG_STATUS_NOT_AVAIL || status != GG_STATUS_NOT_AVAIL_DESCR ) {
+ myself()->setOnlineStatus( GaduProtocol::protocol()->convertStatus( GG_STATUS_NOT_AVAIL ) );
+ }
+ GaduAccount::disconnect( reason );
+}
+
+void
+GaduAccount::userlist( const QString& contactsListString )
+{
+ kdDebug(14100)<<"### Got userlist - gadu account"<<endl;
+
+ GaduContactsList contactsList( contactsListString );
+ QString contactName;
+ QStringList groups;
+ GaduContact* contact;
+ Kopete::MetaContact* metaContact;
+ unsigned int i;
+
+ // don't export any new changes that were just imported :-)
+ p->exportTimer_->stop();
+
+ for ( i = 0; i != contactsList.size() ; i++ ) {
+ kdDebug(14100) << "uin " << contactsList[i].uin << endl;
+
+ if ( contactsList[i].uin.isNull() ) {
+ kdDebug(14100) << "no Uin, strange.. "<<endl;
+ continue;
+ }
+
+ if ( contacts()[ contactsList[i].uin ] ) {
+ kdDebug(14100) << "UIN already exists in contacts "<< contactsList[i].uin << endl;
+ }
+ else {
+ contactName = GaduContact::findBestContactName( &contactsList[i] );
+ bool s = addContact( contactsList[i].uin, contactName, 0L, Kopete::Account::DontChangeKABC);
+ if ( s == false ) {
+ kdDebug(14100) << "There was a problem adding UIN "<< contactsList[i].uin << "to users list" << endl;
+ continue;
+ }
+ }
+ contact = static_cast<GaduContact*>( contacts()[ contactsList[i].uin ] );
+ if ( contact == NULL ) {
+ kdDebug(14100) << "oops, no Kopete::Contact in contacts()[] for some reason, for \"" << contactsList[i].uin << "\"" << endl;
+ continue;
+ }
+
+ // update/add infor for contact
+ contact->setContactDetails( &contactsList[i] );
+
+ if ( !( contactsList[i].group.isEmpty() ) ) {
+ // FIXME: libkopete bug i guess, by default contact goes to top level group
+ // if user desrired to see contact somewhere else, remove it from top level one
+ metaContact = contact->metaContact();
+ metaContact->removeFromGroup( Kopete::Group::topLevel() );
+ // put him in all desired groups:
+ groups = QStringList::split( ",", contactsList[i].group );
+ for ( QStringList::Iterator groupsIterator = groups.begin(); groupsIterator != groups.end(); ++groupsIterator ) {
+ metaContact->addToGroup( Kopete::ContactList::self ()->findGroup ( *groupsIterator) );
+ }
+ }
+ }
+ // start to check if we need to export userlist
+ p->exportUserlist = false;
+ p->exportTimer_->start( USERLISTEXPORT_TIMEOUT );
+}
+
+void
+GaduAccount::userListExportDone()
+{
+ slotCommandDone( QString::null, i18n( "Contacts exported to the server.") );
+}
+
+void
+GaduAccount::slotFriendsMode()
+{
+ p->forFriends = !p->forFriends;
+ kdDebug( 14100 ) << "for friends mode: " << p->forFriends << " desc" << p->lastDescription << endl;
+ // now change status, it will changing it with p->forFriends flag
+ changeStatus( p->status, p->lastDescription );
+
+ saveFriendsMode( p->forFriends );
+
+}
+
+// FIXME: make loading and saving nonblocking (at the moment KFileDialog stops plugin/kopete)
+
+void
+GaduAccount::slotExportContactsListToFile()
+{
+ KTempFile tempFile;
+ tempFile.setAutoDelete( true );
+
+ if ( p->saveListDialog ) {
+ kdDebug( 14100 ) << " save contacts to file: alread waiting for input " << endl ;
+ return;
+ }
+
+ p->saveListDialog = new KFileDialog( "::kopete-gadu" + accountId(), QString::null,
+ Kopete::UI::Global::mainWidget(), "gadu-list-save", false );
+ p->saveListDialog->setCaption(
+ i18n("Save Contacts List for Account %1 As").arg(
+ myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) );
+
+ if ( p->saveListDialog->exec() == QDialog::Accepted ) {
+ QCString list = p->textcodec_->fromUnicode( userlist()->asString() );
+
+ if ( tempFile.status() ) {
+ // say cheese, can't create file.....
+ error( i18n( "Unable to create temporary file." ), i18n( "Save Contacts List Failed" ) );
+ }
+ else {
+ QTextStream* tempStream = tempFile.textStream();
+ (*tempStream) << list.data();
+ tempFile.close();
+
+ bool res = KIO::NetAccess::upload(
+ tempFile.name() ,
+ p->saveListDialog->selectedURL() ,
+ Kopete::UI::Global::mainWidget()
+ );
+ if ( !res ) {
+ // say it failed
+ error( KIO::NetAccess::lastErrorString(), i18n( "Save Contacts List Failed" ) );
+ }
+ }
+
+ }
+ delete p->saveListDialog;
+ p->saveListDialog = NULL;
+}
+
+void
+GaduAccount::slotImportContactsFromFile()
+{
+ KURL url;
+ QCString list;
+ QString oname;
+
+ if ( p->loadListDialog ) {
+ kdDebug( 14100 ) << "load contacts from file: alread waiting for input " << endl ;
+ return;
+ }
+
+ p->loadListDialog = new KFileDialog( "::kopete-gadu" + accountId(), QString::null,
+ Kopete::UI::Global::mainWidget(), "gadu-list-load", true );
+ p->loadListDialog->setCaption(
+ i18n("Load Contacts List for Account %1 As").arg(
+ myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString() ) );
+
+ if ( p->loadListDialog->exec() == QDialog::Accepted ) {
+ url = p->loadListDialog->selectedURL();
+ kdDebug(14100) << "a:" << url << "\nb:" << oname << endl;
+ if ( KIO::NetAccess::download( url, oname, Kopete::UI::Global::mainWidget() ) ) {
+ QFile tempFile( oname );
+ if ( tempFile.open( IO_ReadOnly ) ) {
+ list = tempFile.readAll();
+ tempFile.close();
+ KIO::NetAccess::removeTempFile( oname );
+ // and store it
+ kdDebug( 14100 ) << "loaded list:" << endl;
+ kdDebug( 14100 ) << list << endl;
+ kdDebug( 14100 ) << " --------------- " << endl;
+ userlist( p->textcodec_->toUnicode( list ) );
+ }
+ else {
+ error( tempFile.errorString(),
+ i18n( "Contacts List Load Has Failed" ) );
+ }
+ }
+ else {
+ // say, it failed misourably
+ error( KIO::NetAccess::lastErrorString(),
+ i18n( "Contacts List Load Has Failed" ) );
+ }
+
+ }
+ delete p->loadListDialog;
+ p->loadListDialog = NULL;
+}
+
+unsigned int
+GaduAccount::getPersonalInformation()
+{
+ return p->session_->getPersonalInformation();
+}
+
+bool
+GaduAccount::publishPersonalInformation( ResLine& d )
+{
+ return p->session_->publishPersonalInformation( d );
+}
+
+void
+GaduAccount::slotExportContactsList()
+{
+ p->session_->exportContactsOnServer( userlist() );
+}
+
+GaduContactsList*
+GaduAccount::userlist()
+{
+ GaduContact* contact;
+ GaduContactsList* contactsList = new GaduContactsList();
+ int i;
+
+ if ( !contacts().count() ) {
+ return contactsList;
+ }
+
+ QDictIterator<Kopete::Contact> contactsIterator( contacts() );
+
+ for( i=0 ; contactsIterator.current() ; ++contactsIterator ) {
+ contact = static_cast<GaduContact*>( *contactsIterator );
+ if ( contact->uin() != static_cast<GaduContact*>( myself() )->uin() ) {
+ contactsList->addContact( *contact->contactDetails() );
+ }
+ }
+
+ return contactsList;
+}
+
+void
+GaduAccount::slotSearch( int uin )
+{
+ new GaduPublicDir( this, uin );
+}
+
+void
+GaduAccount::slotChangePassword()
+{
+}
+
+void
+GaduAccount::slotCommandDone( const QString& /*title*/, const QString& what )
+{
+ //FIXME: any chance to have my own title in event popup ?
+ KNotifyClient::userEvent( 0, what,
+ KNotifyClient::PassivePopup, KNotifyClient::Notification );
+}
+
+void
+GaduAccount::slotCommandError(const QString& title, const QString& what )
+{
+ error( title, what );
+}
+
+void
+GaduAccount::slotDescription()
+{
+ GaduAway* away = new GaduAway( this );
+
+ if( away->exec() == QDialog::Accepted ) {
+ changeStatus( GaduProtocol::protocol()->convertStatus( away->status() ),
+ away->awayText() );
+ }
+ delete away;
+}
+
+unsigned int
+GaduAccount::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive )
+{
+ return p->session_->pubDirSearch( query, ageFrom, ageTo, onlyAlive );
+}
+
+void
+GaduAccount::pubDirSearchClose()
+{
+ p->session_->pubDirSearchClose();
+}
+
+void
+GaduAccount::slotSearchResult( const SearchResult& result, unsigned int seq )
+{
+ emit pubDirSearchResult( result, seq );
+}
+
+void
+GaduAccount::sendFile( GaduContact* peer, QString& filePath )
+{
+ GaduDCCTransaction* gtran = new GaduDCCTransaction( p->gaduDcc_ );
+ gtran->setupOutgoing( peer, filePath );
+}
+
+void
+GaduAccount::dccRequest( GaduContact* peer )
+{
+ if ( peer && p->session_ ) {
+ p->session_->dccRequest( peer->uin() );
+ }
+}
+
+// dcc settings
+bool
+GaduAccount::dccEnabled()
+{
+ QString s = p->config->readEntry( QString::fromAscii( "useDcc" ) );
+ kdDebug( 14100 ) << "dccEnabled: "<< s << endl;
+ if ( s == QString::fromAscii( "enabled" ) ) {
+ return true;
+ }
+ return false;
+}
+
+bool
+GaduAccount::setDcc( bool d )
+{
+ QString s;
+ bool f = true;
+
+ if ( d == false ) {
+ dccOff();
+ s = QString::fromAscii( "disabled" );
+ }
+ else {
+ s = QString::fromAscii( "enabled" );
+ }
+
+ p->config->writeEntry( QString::fromAscii( "useDcc" ), s );
+
+ if ( p->session_->isConnected() && d ) {
+ dccOn();
+ }
+
+ kdDebug( 14100 ) << "s: "<<s<<endl;
+
+ return f;
+}
+
+void
+GaduAccount::saveFriendsMode( bool i )
+{
+ p->config->writeEntry( QString::fromAscii( "forFriends" ),
+ i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
+}
+
+bool
+GaduAccount::loadFriendsMode()
+{
+ QString s;
+ bool r;
+ int n;
+
+ s = p->config->readEntry( QString::fromAscii( "forFriends" ) );
+ n = s.toInt( &r );
+
+ if ( n ) {
+ return true;
+ }
+
+ return false;
+
+}
+
+// might be bit inconsistent with what I used in DCC, but hell, it is so much easier to parse :-)
+bool
+GaduAccount::ignoreAnons()
+{
+ QString s;
+ bool r;
+ int n;
+
+ s = p->config->readEntry( QString::fromAscii( "ignoreAnons" ) );
+ n = s.toInt( &r );
+
+ if ( n ) {
+ return true;
+ }
+
+ return false;
+
+}
+
+void
+GaduAccount::setIgnoreAnons( bool i )
+{
+ p->ignoreAnons = i;
+ p->config->writeEntry( QString::fromAscii( "ignoreAnons" ),
+ i == true ? QString::fromAscii( "1" ) : QString::fromAscii( "0" ) );
+}
+
+GaduAccount::tlsConnection
+GaduAccount::useTls()
+{
+ QString s;
+ bool c;
+ unsigned int oldC;
+ tlsConnection Tls;
+
+ s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ) );
+ oldC = s.toUInt( &c );
+ // we have old format
+ if ( c ) {
+ kdDebug( 14100 ) << "old format for param useEncryptedConnection, value " <<
+ oldC << " willl be converted to new string value" << endl;
+ setUseTls( (tlsConnection) oldC );
+ // should be string now, unless there was an error reading
+ s = p->config->readEntry( QString::fromAscii( "useEncryptedConnection" ) );
+ kdDebug( 14100 ) << "new useEncryptedConnection value : " << s << endl;
+ }
+
+ Tls = TLS_no;
+ if ( s == "TLS_ifAvaliable" ) {
+ Tls = TLS_ifAvaliable;
+ }
+ if ( s == "TLS_only" ) {
+ Tls = TLS_only;
+ }
+
+ return Tls;
+}
+
+void
+GaduAccount::setUseTls( tlsConnection ut )
+{
+ QString s;
+ switch( ut ) {
+ case TLS_ifAvaliable:
+ s = "TLS_ifAvaliable";
+ break;
+
+ case TLS_only:
+ s = "TLS_only";
+ break;
+
+ default:
+ case TLS_no:
+ s = "TLS_no";
+ break;
+ }
+
+ p->config->writeEntry( QString::fromAscii( "useEncryptedConnection" ), s );
+}
+
+#include "gaduaccount.moc"
diff --git a/kopete/protocols/gadu/gaduaccount.h b/kopete/protocols/gadu/gaduaccount.h
new file mode 100644
index 00000000..b71e0d6e
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaccount.h
@@ -0,0 +1,173 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2003 Zack Rusin <zack@kde.org>
+//
+// gaduaccount.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUACCOUNT_H
+#define GADUACCOUNT_H
+
+#include "kopetepasswordedaccount.h"
+#include "kopeteonlinestatus.h"
+#include "kopetecontact.h"
+
+#include "gaducontactlist.h"
+#include "gadusession.h"
+
+#include <libgadu.h>
+
+#include <qhostaddress.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <kaction.h>
+#include <kfiledialog.h>
+
+class GaduAccountPrivate;
+
+class GaduContact;
+class GaduProtocol;
+namespace Kopete { class Protocol; }
+namespace Kopete { class Message; }
+class GaduCommand;
+class QTimer;
+class KActionMenu;
+class GaduDCC;
+class GaduDCCTransaction;
+
+class GaduAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+
+public:
+ GaduAccount( Kopete::Protocol*, const QString& accountID, const char* name = 0L );
+ ~GaduAccount();
+ //{
+ void setAway( bool isAway, const QString& awayMessage = QString::null );
+ KActionMenu* actionMenu();
+ void dccRequest( GaduContact* );
+ void sendFile( GaduContact* , QString& );
+ //}
+ enum tlsConnection{ TLS_ifAvaliable = 0, TLS_only, TLS_no };
+ unsigned int getPersonalInformation();
+ bool publishPersonalInformation( ResLine& d );
+
+public slots:
+ //{
+ void connectWithPassword(const QString &password);
+ void disconnect( DisconnectReason );
+ void disconnect();
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+ //}
+
+ void changeStatus( const Kopete::OnlineStatus& status, const QString& descr = QString::null );
+ void slotLogin( int status = GG_STATUS_AVAIL, const QString& dscr = QString::null );
+ void slotLogoff();
+ void slotGoOnline();
+ void slotGoOffline();
+ void slotGoInvisible();
+ void slotGoBusy();
+ void slotDescription();
+ void slotSearch( int uin = 0);
+
+ void removeContact( const GaduContact* );
+
+ void addNotify( uin_t );
+ void notify( uin_t*, int );
+
+ void sendMessage( uin_t recipient, const Kopete::Message& msg,
+ int msgClass = GG_CLASS_CHAT );
+
+ void error( const QString& title, const QString& message );
+
+ void pong();
+ void pingServer();
+
+ // those two functions are connected straight to gadusession ones
+ // with the same names/params. This was the easiest way to
+ // make this interface public
+ unsigned int pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive );
+ void pubDirSearchClose();
+
+ // tls
+ tlsConnection useTls();
+ void setUseTls( tlsConnection );
+
+ // dcc
+ bool dccEnabled();
+ bool setDcc( bool );
+
+ // anons
+ bool ignoreAnons();
+ void setIgnoreAnons( bool );
+
+ // forFriends
+ bool loadFriendsMode();
+ void saveFriendsMode( bool );
+
+signals:
+ void pubDirSearchResult( const SearchResult&, unsigned int );
+
+protected:
+ //{
+ bool createContact( const QString& contactId,
+ Kopete::MetaContact* parentContact );
+ //}
+
+private slots:
+ void startNotify();
+
+ void messageReceived( KGaduMessage* );
+ void ackReceived( unsigned int );
+ void contactStatusChanged( KGaduNotify* );
+ void slotSessionDisconnect( Kopete::Account::DisconnectReason );
+
+ void slotExportContactsList();
+ void slotExportContactsListToFile();
+ void slotImportContactsFromFile();
+ void slotFriendsMode();
+
+ void userlist( const QString& contacts );
+ GaduContactsList* userlist();
+ void slotUserlistSynch();
+
+ void connectionFailed( gg_failure_t failure );
+ void connectionSucceed( );
+
+ void slotChangePassword();
+
+ void slotCommandDone( const QString&, const QString& );
+ void slotCommandError( const QString&, const QString& );
+
+ void slotSearchResult( const SearchResult& result, unsigned int seq );
+ void userListExportDone();
+
+ void slotIncomingDcc( unsigned int );
+
+private:
+ void initConnections();
+ void initActions();
+ void dccOn();
+ void dccOff();
+ void userlistChanged();
+
+ GaduAccountPrivate* p;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gaduaddcontactpage.cpp b/kopete/protocols/gadu/gaduaddcontactpage.cpp
new file mode 100644
index 00000000..d2aed62b
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaddcontactpage.cpp
@@ -0,0 +1,134 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaduaddconectpage.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include "kopetemetacontact.h"
+
+#include "gaduadd.h"
+#include "gaduprotocol.h"
+#include "gaduaccount.h"
+#include "gaduaddcontactpage.h"
+#include "gaducontact.h"
+#include "gaducontactlist.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kopetecontactlist.h>
+#include <kopetegroup.h>
+
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qcombobox.h>
+#include <krestrictedline.h>
+
+GaduAddContactPage::GaduAddContactPage( GaduAccount* owner, QWidget* parent, const char* name )
+: AddContactPage( parent, name )
+{
+ account_ = owner;
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ addUI_ = new GaduAddUI( this );
+ connect( addUI_->addEdit_, SIGNAL( textChanged( const QString & ) ), SLOT( slotUinChanged( const QString & ) ) );
+ addUI_->addEdit_->setValidChars( "1234567890" );
+ addUI_->addEdit_->setText( "" );
+ addUI_->groups->setDisabled( TRUE );
+
+ kdDebug(14100) << "filling gropus" << endl;
+
+ fillGroups();
+}
+
+GaduAddContactPage::~GaduAddContactPage()
+{
+ delete addUI_;
+}
+
+void
+GaduAddContactPage::fillGroups()
+{
+ /*
+ Kopete::Group *g;
+ QPtrList<Kopete::Group> gl = Kopete::ContactList::self()->groups();
+ for( g = gl.first(); g; g = gl.next() ) {
+ QCheckListItem* item = new QCheckListItem( addUI_->groups, g->displayName(), QCheckListItem::CheckBox );
+ kdDebug(14100) << g->displayName() << " " << g->groupId() << endl;
+ }
+ */
+}
+
+void
+GaduAddContactPage::showEvent( QShowEvent* e )
+{
+ slotUinChanged( QString::null );
+ AddContactPage::showEvent( e );
+}
+
+void
+GaduAddContactPage::slotUinChanged( const QString & )
+{
+ emit dataValid( this, validateData() );
+}
+
+bool
+GaduAddContactPage::validateData()
+{
+ bool ok;
+ long u;
+
+ u = addUI_->addEdit_->text().toULong( &ok );
+ if ( u == 0 ) {
+ return false;
+ }
+
+ return ok;
+}
+
+bool
+GaduAddContactPage::apply( Kopete::Account* a , Kopete::MetaContact* mc )
+{
+ if ( validateData() ) {
+ QString userid = addUI_->addEdit_->text().stripWhiteSpace();
+ QString name = addUI_->nickEdit_->text().stripWhiteSpace();
+ if ( a != account_ ) {
+ kdDebug(14100) << "Problem because accounts differ: " << a->accountId()
+ << " , " << account_->accountId() << endl;
+ }
+ if ( !a->addContact( userid, mc, Kopete::Account::ChangeKABC ) ) {
+ return false;
+ }
+ GaduContact *contact = static_cast<GaduContact*>( a->contacts()[ userid ] );
+
+ contact->setProperty( GaduProtocol::protocol()->propEmail, addUI_->emailEdit_->text().stripWhiteSpace() );
+ contact->setProperty( GaduProtocol::protocol()->propFirstName, addUI_->fornameEdit_->text().stripWhiteSpace() );
+ contact->setProperty( GaduProtocol::protocol()->propLastName, addUI_->snameEdit_->text().stripWhiteSpace() );
+ contact->setProperty( GaduProtocol::protocol()->propPhoneNr, addUI_->telephoneEdit_ ->text().stripWhiteSpace() );
+ /*
+ contact->setProperty( "ignored", i18n( "ignored" ), "false" );
+ contact->setProperty( "nickName", i18n( "nick name" ), name );
+ */
+ }
+ return true;
+}
+
+#include "gaduaddcontactpage.moc"
diff --git a/kopete/protocols/gadu/gaduaddcontactpage.h b/kopete/protocols/gadu/gaduaddcontactpage.h
new file mode 100644
index 00000000..39e4d52e
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaddcontactpage.h
@@ -0,0 +1,61 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaduaddconectpage.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUADDCONTACTPAGE_H
+#define GADUADDCONTACTPAGE_H
+
+#include "addcontactpage.h"
+
+class GaduAccount;
+class GaduAddUI;
+class QLabel;
+namespace Kopete { class MetaContact; }
+class QString;
+class QShowEvent;
+class GaduAddUI;
+
+
+class GaduAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+
+public:
+ GaduAddContactPage( GaduAccount*, QWidget* parent = 0, const char* name = 0 );
+ ~GaduAddContactPage();
+ virtual bool validateData();
+ virtual bool apply( Kopete::Account* , Kopete::MetaContact * );
+
+ protected:
+ void showEvent(QShowEvent *e);
+
+public slots:
+ void slotUinChanged( const QString& );
+
+private:
+ void fillGroups();
+ GaduAccount* account_;
+ GaduAddUI* addUI_;
+ QLabel* noaddMsg1_;
+ QLabel* noaddMsg2_;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gaduaway.cpp b/kopete/protocols/gadu/gaduaway.cpp
new file mode 100644
index 00000000..a84fcd0f
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaway.cpp
@@ -0,0 +1,86 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gaduaway.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include "gaduaccount.h"
+#include "gaduprotocol.h"
+#include "gaduawayui.h"
+#include "gaduaway.h"
+
+#include "kopeteonlinestatus.h"
+
+#include <ktextedit.h>
+#include <klocale.h>
+
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+
+#include "gaduawayui.h"
+#include "gaduaway.h"
+
+GaduAway::GaduAway( GaduAccount* account, QWidget* parent, const char* name )
+: KDialogBase( parent, name, true, i18n( "Away Dialog" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true ), account_( account )
+{
+ Kopete::OnlineStatus ks;
+ int s;
+
+ ui_ = new GaduAwayUI( this );
+ setMainWidget( ui_ );
+
+ ks = account->myself()->onlineStatus();
+ s = GaduProtocol::protocol()->statusToWithDescription( ks );
+
+ if ( s == GG_STATUS_NOT_AVAIL_DESCR ) {
+ ui_->statusGroup_->find( GG_STATUS_NOT_AVAIL_DESCR )->setDisabled( TRUE );
+ ui_->statusGroup_->setButton( GG_STATUS_AVAIL_DESCR );
+ }
+ else {
+ ui_->statusGroup_->setButton( s );
+ }
+
+ ui_->textEdit_->setText( account->myself()->property( "awayMessage" ).value().toString() );
+ connect( this, SIGNAL( applyClicked() ), SLOT( slotApply() ) );
+}
+
+int
+GaduAway::status() const
+{
+ return ui_->statusGroup_->id( ui_->statusGroup_->selected() );
+}
+
+QString
+GaduAway::awayText() const
+{
+ return ui_->textEdit_->text();
+}
+
+
+void
+GaduAway::slotApply()
+{
+ if ( account_ ) {
+ account_->changeStatus( GaduProtocol::protocol()->convertStatus( status() ),awayText() );
+ }
+}
+
+#include "gaduaway.moc"
diff --git a/kopete/protocols/gadu/gaduaway.h b/kopete/protocols/gadu/gaduaway.h
new file mode 100644
index 00000000..bebbcd9f
--- /dev/null
+++ b/kopete/protocols/gadu/gaduaway.h
@@ -0,0 +1,49 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaduaway.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUAWAY_H
+#define GADUAWAY_H
+
+#include <kdialogbase.h>
+#include <qstring.h>
+
+class GaduAccount;
+class GaduAwayUI;
+
+class GaduAway : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ GaduAway( GaduAccount*, QWidget* parent = 0, const char* name = 0 );
+ int status() const;
+ QString awayText() const;
+
+protected slots:
+ void slotApply();
+
+private:
+ GaduAccount* account_;
+ GaduAwayUI* ui_;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gaducommands.cpp b/kopete/protocols/gadu/gaducommands.cpp
new file mode 100644
index 00000000..431b1ab4
--- /dev/null
+++ b/kopete/protocols/gadu/gaducommands.cpp
@@ -0,0 +1,411 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaducommands.cpp - all basic, and not-session dependent commands
+// (meaning you don't have to be logged in for any of these).
+// These delete themselves, meaning you don't
+// have to/can't delete them explicitly and have to create
+// them dynamically (via the 'new' call).
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include "gaducommands.h"
+#include "gadusession.h"
+
+#include <qsocketnotifier.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <qimage.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <errno.h>
+
+GaduCommand::GaduCommand( QObject* parent, const char* name )
+: QObject( parent, name ), read_( 0 ), write_( 0 )
+{
+}
+
+GaduCommand::~GaduCommand()
+{
+ //QSocketNotifiers are children and will
+ //be deleted anyhow
+}
+
+bool
+GaduCommand::done() const
+{
+ return done_;
+}
+
+void
+GaduCommand::checkSocket( int fd, int checkWhat )
+{
+ read_ = new QSocketNotifier( fd, QSocketNotifier::Read, this );
+ read_->setEnabled( false );
+ QObject::connect( read_, SIGNAL( activated(int) ), SLOT( forwarder() ) );
+
+ write_ = new QSocketNotifier( fd, QSocketNotifier::Write, this );
+ write_->setEnabled( false );
+ QObject::connect( write_, SIGNAL( activated(int) ), SLOT( forwarder() ) );
+
+ enableNotifiers( checkWhat );
+}
+
+void
+GaduCommand::enableNotifiers( int checkWhat )
+{
+ if ( read_ ) {
+ if( checkWhat & GG_CHECK_READ ) {
+ read_->setEnabled( true );
+ }
+ }
+
+ if ( write_ ) {
+ if( checkWhat & GG_CHECK_WRITE ) {
+ write_->setEnabled( true );
+ }
+ }
+}
+
+void
+GaduCommand::disableNotifiers()
+{
+ if ( read_ ) {
+ read_->setEnabled( false );
+ }
+ if ( write_ ) {
+ write_->setEnabled( false );
+ }
+}
+
+void
+GaduCommand::deleteNotifiers()
+{
+ if ( read_ ) {
+ delete read_;
+ read_ = NULL;
+ }
+ if ( write_ ) {
+ delete write_;
+ write_ = NULL;
+ }
+}
+
+void
+GaduCommand::forwarder()
+{
+ emit socketReady();
+}
+
+RegisterCommand::RegisterCommand( QObject* parent, const char* name )
+:GaduCommand( parent, name ), state( RegisterStateNoToken ), session_( 0 ), uin( 0 )
+{
+}
+
+RegisterCommand::RegisterCommand( const QString& email, const QString& password, QObject* parent, const char* name )
+:GaduCommand( parent, name ), state( RegisterStateNoToken ), email_( email ), password_( password ), session_( 0 ), uin( 0 )
+{
+}
+
+RegisterCommand::~RegisterCommand()
+{
+}
+
+unsigned int RegisterCommand::newUin()
+{
+ if ( state == RegisterStateDone ) {
+ return uin;
+ }
+// else
+ return 0;
+}
+void
+RegisterCommand::requestToken()
+{
+ kdDebug( 14100 ) << "requestToken Initialisation" << endl;
+ state = RegisterStateWaitingForToken;
+
+ if ( !( session_ = gg_token( 1 ) ) ) {
+ emit error( i18n( "Gadu-Gadu" ), i18n( "Unable to retrieve token." ) );
+ state = RegisterStateNoToken;
+ return;
+ }
+
+ connect( this, SIGNAL( socketReady() ), SLOT( watcher() ) );
+ checkSocket( session_->fd, session_->check );
+
+ return;
+}
+
+void
+RegisterCommand::setUserinfo( const QString& email, const QString& password, const QString& token )
+{
+ email_ = email;
+ password_ = password;
+ tokenString = token;
+}
+
+void
+RegisterCommand::cancel()
+{
+ deleteNotifiers();
+ session_ = NULL;
+
+}
+
+void
+RegisterCommand::execute()
+{
+ if ( state != RegisterStateGotToken || email_.isEmpty() || password_.isEmpty() || tokenString.isEmpty() ) {
+ // get token first || fill information
+ kdDebug(14100) << "not enough info to run execute, state: " << state << " , email: " << email_ << ", password present " << !password_.isEmpty() << ", token string:" << tokenString << endl;
+ return;
+ }
+ session_ = gg_register3( email_.ascii(), password_.ascii(), tokenId.ascii(), tokenString.ascii(), 1 );
+ if ( !session_ ) {
+ error( i18n( "Gadu-Gadu" ), i18n( "Registration FAILED" ) );
+ return;
+ }
+ state = RegisterStateWaitingForNumber;
+ connect( this, SIGNAL( socketReady() ), SLOT( watcher() ) );
+ checkSocket( session_->fd, session_->check );
+}
+
+void RegisterCommand::watcher()
+{
+ gg_pubdir* pubDir;
+
+ if ( state == RegisterStateWaitingForToken ) {
+ disableNotifiers();
+ if ( gg_token_watch_fd( session_ ) == -1 ) {
+ deleteNotifiers();
+ emit error( i18n( "Gadu-Gadu" ), i18n( "Unknown connection error while retrieving token." ) );
+ gg_token_free( session_ );
+ session_ = NULL;
+ state = RegisterStateNoToken;
+ return;
+ }
+
+ pubDir = (struct gg_pubdir *)session_->data;
+ emit operationStatus( i18n( "Token retrieving status: %1" ).arg( GaduSession::stateDescription( session_->state ) ) );
+ switch ( session_->state ) {
+ case GG_STATE_CONNECTING:
+ kdDebug( 14100 ) << "Recreating notifiers " << endl;
+ deleteNotifiers();
+ checkSocket( session_->fd, 0);
+ break;
+ case GG_STATE_ERROR:
+ deleteNotifiers();
+ emit error( i18n( "Gadu-Gadu token retrieve problem" ), GaduSession::errorDescription( session_->error ) );
+ gg_token_free( session_ );
+ session_ = NULL;
+ state = RegisterStateNoToken;
+ return;
+ break;
+ case GG_STATE_DONE:
+ struct gg_token* sp = ( struct gg_token* )session_->data;
+ tokenId = (char *)sp->tokenid;
+ kdDebug( 14100 ) << "got Token!, ID: " << tokenId << endl;
+ deleteNotifiers();
+ if ( pubDir->success ) {
+ QPixmap tokenImg;
+ tokenImg.loadFromData( (const unsigned char *)session_->body, session_->body_size );
+ state = RegisterStateGotToken;
+ emit tokenRecieved( tokenImg, tokenId );
+ }
+ else {
+ emit error( i18n( "Gadu-Gadu" ), i18n( "Unable to retrieve token." ) );
+ state = RegisterStateNoToken;
+ deleteLater();
+ }
+ gg_token_free( session_ );
+ session_ = NULL;
+ disconnect( this, SLOT( watcher() ) );
+ return;
+ break;
+ }
+ enableNotifiers( session_->check );
+ }
+ if ( state == RegisterStateWaitingForNumber ) {
+ disableNotifiers();
+ if ( gg_register_watch_fd( session_ ) == -1 ) {
+ deleteNotifiers();
+ emit error( i18n( "Gadu-Gadu" ), i18n( "Unknown connection error while registering." ) );
+ gg_free_register( session_ );
+ session_ = NULL;
+ state = RegisterStateGotToken;
+ return;
+ }
+ pubDir = (gg_pubdir*) session_->data;
+ emit operationStatus( i18n( "Registration status: %1" ).arg( GaduSession::stateDescription( session_->state ) ) );
+ switch ( session_->state ) {
+ case GG_STATE_CONNECTING:
+ kdDebug( 14100 ) << "Recreating notifiers " << endl;
+ deleteNotifiers();
+ checkSocket( session_->fd, 0);
+ break;
+ case GG_STATE_ERROR:
+ deleteNotifiers();
+ emit error( i18n( "Gadu-Gadu Registration Error" ), GaduSession::errorDescription( session_->error ) );
+ gg_free_register( session_ );
+ session_ = NULL;
+ state = RegisterStateGotToken;
+ return;
+ break;
+
+ case GG_STATE_DONE:
+ deleteNotifiers();
+ if ( pubDir->success && pubDir->uin ) {
+ uin= pubDir->uin;
+ state = RegisterStateDone;
+ emit done( i18n( "Registration Finished" ), i18n( "Registration has completed successfully." ) );
+ }
+ else {
+ emit error( i18n( "Registration Error" ), i18n( "Incorrect data sent to server." ) );
+ state = RegisterStateGotToken;
+ }
+ gg_free_register( session_ );
+ session_ = NULL;
+ disconnect( this, SLOT( watcher() ) );
+ deleteLater();
+ return;
+ break;
+ }
+ enableNotifiers( session_->check );
+ return;
+ }
+}
+
+RemindPasswordCommand::RemindPasswordCommand( QObject* parent, const char* name )
+: GaduCommand( parent, name ), uin_( 0 ), session_( 0 )
+{
+}
+
+RemindPasswordCommand::RemindPasswordCommand( uin_t uin, QObject* parent, const char* name )
+: GaduCommand( parent, name ), uin_( uin ), session_( 0 )
+{
+}
+
+RemindPasswordCommand::~RemindPasswordCommand()
+{
+}
+
+void
+RemindPasswordCommand::setUIN( uin_t uin )
+{
+ uin_ = uin;
+}
+
+void
+RemindPasswordCommand::execute()
+{
+}
+
+void
+RemindPasswordCommand::watcher()
+{
+ disableNotifiers();
+
+ if ( gg_remind_passwd_watch_fd( session_ ) == -1 ) {
+ gg_free_remind_passwd( session_ );
+ emit error( i18n( "Connection Error" ), i18n( "Password reminding finished prematurely due to a connection error." ) );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+
+ if ( session_->state == GG_STATE_ERROR ) {
+ gg_free_remind_passwd( session_ );
+ emit error( i18n( "Connection Error" ), i18n( "Password reminding finished prematurely due to a connection error." ) );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+
+ if ( session_->state == GG_STATE_DONE ) {
+ struct gg_pubdir* p = static_cast<struct gg_pubdir*>( session_->data );
+ QString finished = (p->success) ? i18n( "Successfully" ) : i18n( "Unsuccessful. Please retry." );
+ emit done( i18n( "Remind Password" ), i18n( "Remind password finished: " ) + finished );
+ gg_free_remind_passwd( session_ );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+ enableNotifiers( session_->check );
+}
+
+ChangePasswordCommand::ChangePasswordCommand( QObject* parent, const char* name )
+: GaduCommand( parent, name ), session_( 0 )
+{
+}
+
+ChangePasswordCommand::~ChangePasswordCommand()
+{
+}
+
+void
+ChangePasswordCommand::setInfo( uin_t uin, const QString& passwd, const QString& newpasswd, const QString& newemail )
+{
+ uin_ = uin;
+ passwd_ = passwd;
+ newpasswd_ = newpasswd;
+ newemail_ = newemail;
+}
+
+void
+ChangePasswordCommand::execute()
+{
+}
+
+void
+ChangePasswordCommand::watcher()
+{
+ disableNotifiers();
+
+ if ( gg_pubdir_watch_fd( session_ ) == -1 ) {
+ gg_change_passwd_free( session_ );
+ emit error( i18n( "Connection Error" ), i18n( "Password changing finished prematurely due to a connection error." ) );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+
+ if ( session_->state == GG_STATE_ERROR ) {
+ gg_free_change_passwd( session_ );
+ emit error( i18n( "State Error" ),
+ i18n( "Password changing finished prematurely due to a session related problem (try again later)." ) );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+
+ if ( session_->state == GG_STATE_DONE ) {
+ emit done( i18n( "Changed Password" ), i18n( "Your password has been changed." ) );
+ gg_free_change_passwd( session_ );
+ done_ = true;
+ deleteLater();
+ return;
+ }
+
+ enableNotifiers( session_->check );
+}
+
+
+#include "gaducommands.moc"
diff --git a/kopete/protocols/gadu/gaducommands.h b/kopete/protocols/gadu/gaducommands.h
new file mode 100644
index 00000000..8a48ec3d
--- /dev/null
+++ b/kopete/protocols/gadu/gaducommands.h
@@ -0,0 +1,150 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaducommands.h - all basic, and not-session dependent commands
+// (meaning you don't have to be logged in for any of these).
+// These delete themselves, meaning you don't
+// have to/can't delete them explicitly and have to create
+// them dynamically (via the 'new' call).
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUCOMMANDS_H
+#define GADUCOMMANDS_H
+
+#include <libgadu.h>
+
+#include <qobject.h>
+
+class QSocketNotifier;
+class QStringList;
+class QPixmap;
+
+class GaduCommand : public QObject
+{
+ Q_OBJECT
+
+public:
+ GaduCommand( QObject* parent = 0, const char* name = 0 );
+ virtual ~GaduCommand();
+
+ virtual void execute() = 0;
+
+ bool done() const;
+
+signals:
+ //e.g. emit done( i18n("Done"), i18n("Registration complete") );
+ void done( const QString& title, const QString& what );
+ void error( const QString& title, const QString& error );
+ void socketReady();
+ void operationStatus( const QString );
+
+protected:
+ void checkSocket( int, int );
+ void enableNotifiers( int );
+ void disableNotifiers();
+ void deleteNotifiers();
+
+ bool done_;
+
+protected slots:
+ void forwarder();
+
+private:
+ QSocketNotifier* read_;
+ QSocketNotifier* write_;
+};
+
+class RegisterCommand : public GaduCommand
+{
+ Q_OBJECT
+
+public:
+ RegisterCommand( QObject* parent = 0, const char* name = 0 );
+ RegisterCommand( const QString& email, const QString& password ,
+ QObject* parent = 0, const char* name = 0 );
+ ~RegisterCommand();
+
+ void setUserinfo( const QString& email, const QString& password, const QString& token );
+ void execute();
+ unsigned int newUin();
+ void requestToken();
+ void cancel();
+
+signals:
+ void tokenRecieved( QPixmap, QString );
+
+protected slots:
+ void watcher();
+
+private:
+ enum RegisterState{ RegisterStateNoToken, RegisterStateWaitingForToken, RegisterStateGotToken, RegisterStateWaitingForNumber, RegisterStateDone };
+ RegisterState state;
+ QString email_;
+ QString password_;
+ struct gg_http* session_;
+ int uin;
+ QString tokenId;
+ QString tokenString;
+};
+
+class RemindPasswordCommand : public GaduCommand
+{
+ Q_OBJECT
+
+public:
+ RemindPasswordCommand( uin_t uin, QObject* parent = 0, const char* name = 0 );
+ RemindPasswordCommand( QObject* parent = 0, const char* name = 0 );
+ ~RemindPasswordCommand();
+
+ void setUIN( uin_t );
+ void execute();
+
+protected slots:
+ void watcher();
+
+private:
+ uin_t uin_;
+ struct gg_http* session_;
+};
+
+class ChangePasswordCommand : public GaduCommand
+{
+ Q_OBJECT
+
+public:
+ ChangePasswordCommand( QObject* parent = 0, const char* name = 0 );
+ ~ChangePasswordCommand();
+
+ void setInfo( uin_t uin, const QString& passwd, const QString& newpasswd,
+ const QString& newemail );
+ void execute();
+
+protected slots:
+ void watcher();
+
+private:
+ struct gg_http* session_;
+ QString passwd_;
+ QString newpasswd_;
+ QString newemail_;
+ uin_t uin_;
+};
+
+
+#endif
diff --git a/kopete/protocols/gadu/gaducontact.cpp b/kopete/protocols/gadu/gaducontact.cpp
new file mode 100644
index 00000000..dddd965f
--- /dev/null
+++ b/kopete/protocols/gadu/gaducontact.cpp
@@ -0,0 +1,384 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaducontact.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <klocale.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+
+#include "gaduaccount.h"
+#include "gaduprotocol.h"
+#include "gaducontact.h"
+#include "gadupubdir.h"
+#include "gadueditcontact.h"
+#include "gaducontactlist.h"
+#include "gadusession.h"
+
+#include "kopetechatsessionmanager.h"
+#include "kopetegroup.h"
+#include "kopetemetacontact.h"
+#include "kopetestdaction.h"
+#include "kopeteuiglobal.h"
+
+#include "userinfodialog.h"
+
+using Kopete::UserInfoDialog;
+
+GaduContact::GaduContact( uin_t uin, const QString& name, Kopete::Account* account, Kopete::MetaContact* parent )
+: Kopete::Contact( account, QString::number( uin ), parent ), uin_( uin )
+{
+ msgManager_ = 0L;
+ account_ = static_cast<GaduAccount*>( account );
+
+ remote_port = 0;
+ version = 0;
+ image_size = 0;
+ // let us not ignore the contact by default right? causes ugly bug if
+ // setContactDetails is not run on a contact right after it is added
+ ignored_ = false;
+
+ thisContact_.append( this );
+
+ initActions();
+
+ // don't call libkopete functions like these until the object is fully
+ // constructed. all GaduContact construction must be above this point.
+ setFileCapable( true );
+
+ //offline
+ setOnlineStatus( GaduProtocol::protocol()->convertStatus( 0 ) );
+
+ setProperty( Kopete::Global::Properties::self()->nickName(), name );
+}
+
+QString
+GaduContact::identityId() const
+{
+ return parentIdentity_;
+}
+
+void
+GaduContact::setParentIdentity( const QString& id)
+{
+ parentIdentity_ = id;
+}
+
+uin_t
+GaduContact::uin() const
+{
+ return uin_;
+}
+
+void
+GaduContact::sendFile( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ //If the file location is null, then get it from a file open dialog
+ if( !sourceURL.isValid() )
+ filePath = KFileDialog::getOpenFileName(QString::null, "*", 0l , i18n("Kopete File Transfer"));
+ else
+ filePath = sourceURL.path(-1);
+
+ kdDebug(14120) << k_funcinfo << "File chosen to send:" << filePath << endl;
+
+ account_->sendFile( this, filePath );
+}
+
+
+void
+GaduContact::changedStatus( KGaduNotify* newstatus )
+{
+ if ( newstatus->description.isNull() ) {
+ setOnlineStatus( GaduProtocol::protocol()->convertStatus( newstatus->status ) );
+ removeProperty( GaduProtocol::protocol()->propAwayMessage );
+ }
+ else {
+ setOnlineStatus( GaduProtocol::protocol()->convertStatus( newstatus->status ) );
+ setProperty( GaduProtocol::protocol()->propAwayMessage, newstatus->description );
+ }
+
+ remote_ip = newstatus->remote_ip;
+ remote_port = newstatus->remote_port;
+ version = newstatus->version;
+ image_size = newstatus->image_size;
+
+ setFileCapable( newstatus->fileCap );
+
+ kdDebug(14100) << "uin:" << uin() << " port: " << remote_port << " remote ip: " << remote_ip.ip4Addr() << " image size: " << image_size << " version: " << version << endl;
+
+}
+
+QHostAddress&
+GaduContact::contactIp()
+{
+ return remote_ip;
+}
+
+unsigned short
+GaduContact::contactPort()
+{
+ return remote_port;
+}
+
+Kopete::ChatSession*
+GaduContact::manager( Kopete::Contact::CanCreateFlags canCreate )
+{
+ if ( !msgManager_ && canCreate ) {
+ msgManager_ = Kopete::ChatSessionManager::self()->create( account_->myself(), thisContact_, GaduProtocol::protocol() );
+ connect( msgManager_, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession*) ),
+ this, SLOT( messageSend( Kopete::Message&, Kopete::ChatSession*) ) );
+ connect( msgManager_, SIGNAL( destroyed() ), this, SLOT( slotChatSessionDestroyed() ) );
+
+ }
+ kdDebug(14100) << "GaduContact::manager returning: " << msgManager_ << endl;
+ return msgManager_;
+}
+
+void
+GaduContact::slotChatSessionDestroyed()
+{
+ msgManager_ = 0L;
+}
+
+void
+GaduContact::initActions()
+{
+ actionSendMessage_ = KopeteStdAction::sendMessage( this, SLOT( execute() ), this, "actionMessage" );
+ actionInfo_ = KopeteStdAction::contactInfo( this, SLOT( slotUserInfo() ), this, "actionInfo" );
+}
+
+void
+GaduContact::messageReceived( Kopete::Message& msg )
+{
+ manager(Kopete::Contact::CanCreate)->appendMessage( msg );
+}
+
+void
+GaduContact::messageSend( Kopete::Message& msg, Kopete::ChatSession* mgr )
+{
+ if ( msg.plainBody().isEmpty() ) {
+ return;
+ }
+ mgr->appendMessage( msg );
+ account_->sendMessage( uin_, msg );
+}
+
+bool
+GaduContact::isReachable()
+{
+ return account_->isConnected();
+}
+
+QPtrList<KAction>*
+GaduContact::customContextMenuActions()
+{
+ QPtrList<KAction> *fakeCollection = new QPtrList<KAction>();
+ //show profile
+ KAction* actionShowProfile = new KAction( i18n("Show Profile") , "info", 0,
+ this, SLOT( slotShowPublicProfile() ),
+ this, "actionShowPublicProfile" );
+
+ fakeCollection->append( actionShowProfile );
+
+ KAction* actionEditContact = new KAction( i18n("Edit...") , "edit", 0,
+ this, SLOT( slotEditContact() ),
+ this, "actionEditContact" );
+
+ fakeCollection->append( actionEditContact );
+
+ return fakeCollection;
+}
+
+void
+GaduContact::slotEditContact()
+{
+ new GaduEditContact( static_cast<GaduAccount*>(account()), this, Kopete::UI::Global::mainWidget() );
+}
+
+void
+GaduContact::slotShowPublicProfile()
+{
+ account_->slotSearch( uin_ );
+}
+
+void
+GaduContact::slotUserInfo()
+{
+ /// FIXME: use more decent information here
+ UserInfoDialog *dlg = new UserInfoDialog( i18n( "Gadu contact" ) );
+
+ dlg->setName( metaContact()->displayName() );
+ dlg->setId( QString::number( uin_ ) );
+ dlg->setStatus( onlineStatus().description() );
+ dlg->setAwayMessage( description_ );
+ dlg->show();
+}
+
+void
+GaduContact::deleteContact()
+{
+ if ( account_->isConnected() ) {
+ account_->removeContact( this );
+ deleteLater();
+ }
+ else {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Please go online to remove a contact from your contact list.</qt>" ),
+ i18n( "Gadu-Gadu Plugin" ));
+ }
+}
+
+void
+GaduContact::serialize( QMap<QString, QString>& serializedData, QMap<QString, QString>& )
+{
+ serializedData[ "email" ] = property( GaduProtocol::protocol()->propEmail ).value().toString();
+ serializedData[ "FirstName" ] = property( GaduProtocol::protocol()->propFirstName ).value().toString();
+ serializedData[ "SecondName" ] = property( GaduProtocol::protocol()->propLastName ).value().toString();
+ serializedData[ "telephone" ] = property( GaduProtocol::protocol()->propPhoneNr ).value().toString();
+ serializedData[ "ignored" ] = ignored_ ? "true" : "false";
+}
+
+bool
+GaduContact::setContactDetails( const GaduContactsList::ContactLine* cl )
+{
+ setProperty( GaduProtocol::protocol()->propEmail, cl->email );
+ setProperty( GaduProtocol::protocol()->propFirstName, cl->firstname );
+ setProperty( GaduProtocol::protocol()->propLastName, cl->surname );
+ setProperty( GaduProtocol::protocol()->propPhoneNr, cl->phonenr );
+ //setProperty( "ignored", i18n( "ignored" ), cl->ignored ? "true" : "false" );
+ ignored_ = cl->ignored;
+ //setProperty( "nickName", i18n( "nick name" ), cl->nickname );
+
+ return true;
+}
+
+GaduContactsList::ContactLine*
+GaduContact::contactDetails()
+{
+ Kopete::GroupList groupList;
+ QString groups;
+
+ GaduContactsList::ContactLine* cl = new GaduContactsList::ContactLine;
+
+ cl->firstname = property( GaduProtocol::protocol()->propFirstName ).value().toString();
+ cl->surname = property( GaduProtocol::protocol()->propLastName ).value().toString();
+ //cl->nickname = property( "nickName" ).value().toString();
+ cl->email = property( GaduProtocol::protocol()->propEmail ).value().toString();
+ cl->phonenr = property( GaduProtocol::protocol()->propPhoneNr ).value().toString();
+ cl->ignored = ignored_; //( property( "ignored" ).value().toString() == "true" );
+
+ cl->uin = QString::number( uin_ );
+ cl->displayname = metaContact()->displayName();
+
+ cl->offlineTo = false;
+ cl->landline = QString("");
+
+ groupList = metaContact()->groups();
+
+ Kopete::Group* gr;
+ for ( gr = groupList.first (); gr ; gr = groupList.next () ) {
+// if present in any group, don't export to top level
+// FIXME: again, probably bug in libkopete
+// in case of topLevel group, Kopete::Group::displayName() returns "TopLevel" ineasted of just " " or "/"
+// imo TopLevel group should be detected like i am doing that below
+ if ( gr!=Kopete::Group::topLevel() ) {
+ groups += gr->displayName()+",";
+ }
+ }
+
+ if ( groups.length() ) {
+ groups.truncate( groups.length()-1 );
+ }
+ cl->group = groups;
+
+ return cl;
+}
+
+QString
+GaduContact::findBestContactName( const GaduContactsList::ContactLine* cl )
+{
+ QString name;
+
+ if ( cl == NULL ) {
+ return name;
+ }
+
+ if ( cl->uin.isEmpty() ) {
+ return name;
+ }
+
+ name = cl->uin;
+
+ if ( cl->displayname.length() ) {
+ name = cl->displayname;
+ }
+ else {
+ // no name either
+ if ( cl->nickname.isEmpty() ) {
+ // maybe we can use fistname + surname ?
+ if ( cl->firstname.isEmpty() && cl->surname.isEmpty() ) {
+ name = cl->uin;
+ }
+ // what a shame, i have to use UIN than :/
+ else {
+ if ( cl->firstname.isEmpty() ) {
+ name = cl->surname;
+ }
+ else {
+ if ( cl->surname.isEmpty() ) {
+ name = cl->firstname;
+ }
+ else {
+ name = cl->firstname + " " + cl->surname;
+ }
+ }
+ }
+ }
+ else {
+ name = cl->nickname;
+ }
+ }
+
+ return name;
+}
+
+void
+GaduContact::messageAck()
+{
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+void
+GaduContact::setIgnored( bool val )
+{
+ ignored_ = val;
+}
+
+bool
+GaduContact::ignored()
+{
+ return ignored_;
+}
+
+#include "gaducontact.moc"
diff --git a/kopete/protocols/gadu/gaducontact.h b/kopete/protocols/gadu/gaducontact.h
new file mode 100644
index 00000000..9a51838e
--- /dev/null
+++ b/kopete/protocols/gadu/gaducontact.h
@@ -0,0 +1,119 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaducontact.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUCONTACT_H
+#define GADUCONTACT_H
+
+#include <qpoint.h>
+#include <qhostaddress.h>
+
+#include "gaducontactlist.h"
+
+#include "kopeteaccount.h"
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+
+#include <libgadu.h>
+
+class KAction;
+class GaduAccount;
+namespace Kopete { class Account; }
+namespace Kopete { class ChatSession; }
+class KGaduNotify;
+class QString;
+class QStringList;
+
+class GaduContact : public Kopete::Contact
+{
+ Q_OBJECT
+
+public:
+ GaduContact( unsigned int, const QString&, Kopete::Account*, Kopete::MetaContact* );
+
+ virtual bool isReachable();
+ virtual void serialize( QMap<QString, QString>&, QMap<QString, QString>& );
+ virtual QPtrList<KAction>* customContextMenuActions();
+ virtual QString identityId() const;
+
+ GaduContactsList::ContactLine* contactDetails();
+
+ // this one set's only:
+ // email, firstname, surname, phonenr, ignored, nickname
+ // uin is const for GaduContact, and displayname needs to be changed through metaContact
+ bool setContactDetails( const GaduContactsList::ContactLine* );
+
+ void setParentIdentity( const QString& );
+ void setIgnored( bool );
+ bool ignored();
+
+ static QString findBestContactName( const GaduContactsList::ContactLine* );
+ void changedStatus( KGaduNotify* );
+
+ uin_t uin() const;
+
+ QHostAddress& contactIp();
+ unsigned short contactPort();
+
+public slots:
+ void slotUserInfo();
+ void deleteContact();
+ void messageReceived( Kopete::Message& );
+ void messageSend( Kopete::Message&, Kopete::ChatSession* );
+ void messageAck();
+ void slotShowPublicProfile();
+ void slotEditContact();
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+
+protected:
+ virtual Kopete::ChatSession* manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CanCreate );
+ void initActions();
+
+private:
+ const uin_t uin_;
+ bool ignored_;
+
+ Kopete::ChatSession* msgManager_;
+ QString description_;
+ QString parentIdentity_;
+ GaduAccount* account_;
+
+ KAction* actionSendMessage_;
+ KAction* actionInfo_;
+ KAction* actionRemove_;
+
+ QPtrList<Kopete::Contact> thisContact_;
+
+
+ QHostAddress remote_ip;
+ unsigned int remote_port;
+ unsigned int version;
+ unsigned int image_size;
+
+
+private slots:
+ void slotChatSessionDestroyed();
+
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gaducontactlist.cpp b/kopete/protocols/gadu/gaducontactlist.cpp
new file mode 100644
index 00000000..750f5224
--- /dev/null
+++ b/kopete/protocols/gadu/gaducontactlist.cpp
@@ -0,0 +1,207 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gaducontactlist.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+
+#include "gaducontactlist.h"
+#include "qstringlist.h"
+#include "kdebug.h"
+
+GaduContactsList::GaduContactsList()
+{
+}
+
+GaduContactsList::~GaduContactsList()
+{
+}
+
+GaduContactsList::GaduContactsList( QString sList )
+{
+ QStringList::iterator stringIterator;
+ QStringList strList;
+ QString empty;
+ ContactLine cl;
+ bool email;
+
+ if ( sList.isEmpty() || sList.isNull() ) {
+ return;
+ }
+
+ if ( ( !sList.contains( '\n' ) && sList.contains( ';' ) ) || !sList.contains( ';' ) ) {
+ return;
+ }
+
+ QStringList ln = QStringList::split( QChar( '\n' ), sList, true );
+ QStringList::iterator lni = ln.begin( );
+
+ while( lni != ln.end() ) {
+
+ QString cline = (*lni);
+ if ( cline.isNull() ) {
+ break;
+ }
+
+ strList = QStringList::split( QChar( ';' ), cline, true );
+
+ stringIterator = strList.begin();
+
+ if ( strList.count() >= 12 ) {
+ email = true;
+ }
+ else {
+ email = false;
+ }
+
+
+//each line ((firstname);(secondname);(nickname);(altnick);(tel);(group);(uin);
+// new stuff attached at the end:
+// email;aliveSoundfile;notifyType;msgSoundType;messageSound;offlineTo;homePhone;
+ stringIterator = strList.begin();
+
+ cl.firstname = (*stringIterator);
+
+ if ( cl.firstname == QString( "i" ) ) {
+ kdDebug(14100) << cline << " ignored" << endl;
+ cl.ignored = true;
+ cl.uin = strList[6];
+ ++lni;
+ cList.append( cl );
+ continue;
+ }
+ else {
+ cl.ignored = false;
+ }
+
+ cl.surname = (*++stringIterator);
+ cl.nickname = (*++stringIterator);
+ cl.displayname = (*++stringIterator);
+ cl.phonenr = (*++stringIterator);
+ cl.group = (*++stringIterator);
+ cl.uin = (*++stringIterator);
+ if ( email ) {
+ cl.email = (*++stringIterator);
+ // no use for custom sounds, at least now
+ ++stringIterator;
+ ++stringIterator;
+ ++stringIterator;
+ ++stringIterator;
+
+ if ( stringIterator != strList.end() ) {
+ cl.offlineTo = (*++stringIterator) == QString("0") ? false : true;
+ cl.landline = (*++stringIterator);
+ }
+ }
+ else {
+ cl.email = empty;
+ }
+
+ ++lni;
+
+ if ( cl.uin.isNull() ) {
+ continue;
+ }
+
+ cList.append( cl );
+ }
+
+ return;
+}
+
+void
+GaduContactsList::addContact( ContactLine& cl )
+{
+ cList.append( cl );
+}
+
+void
+GaduContactsList::addContact(
+ QString& displayname,
+ QString& group,
+ QString& uin,
+ QString& firstname,
+ QString& surname,
+ QString& nickname,
+ QString& phonenr,
+ QString& email,
+ bool ignored,
+ bool offlineTo,
+ QString& landline
+)
+{
+ ContactLine cl;
+
+ cl.displayname = displayname;
+ cl.group = group;
+ cl.uin = uin;
+ cl.firstname = firstname;
+ cl.surname = surname;
+ cl.nickname = nickname;
+ cl.phonenr = phonenr;
+ cl.email = email;
+ cl.ignored = ignored;
+ cl.offlineTo = offlineTo;
+ cl.landline = landline;
+
+ cList.append( cl );
+
+}
+
+QString
+GaduContactsList::asString()
+{
+ QString contacts;
+
+ for ( it = cList.begin(); it != cList.end(); ++it ) {
+ if ( (*it).ignored ) {
+ contacts += "i;;;;;;" + (*it).uin + "\n";
+ }
+ else {
+// name;surname;nick;displayname;telephone;group(s);uin;email;;0;0;;offlineTo;homePhone;
+ contacts +=
+ (*it).firstname + ";"+
+ (*it).surname + ";"+
+ (*it).nickname + ";"+
+ (*it).displayname + ";"+
+ (*it).phonenr + ";"+
+ (*it).group + ";"+
+ (*it).uin + ";"+
+ (*it).email +
+ ";;0;0;;" +
+ ((*it).offlineTo == true ? QString("1") : QString("0"))
+ + ";" +
+ (*it).landline +
+ ";\r\n";
+ }
+ }
+ return contacts;
+}
+
+unsigned int
+GaduContactsList::size()
+{
+ return cList.size();
+}
+
+const GaduContactsList::ContactLine&
+GaduContactsList::operator[]( unsigned int i )
+{
+ return cList[i];
+}
diff --git a/kopete/protocols/gadu/gaducontactlist.h b/kopete/protocols/gadu/gaducontactlist.h
new file mode 100644
index 00000000..cfedeba4
--- /dev/null
+++ b/kopete/protocols/gadu/gaducontactlist.h
@@ -0,0 +1,67 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gaducontactlist.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+#ifndef GADUCONTACTLIST_H
+#define GADUCONTACTLIST_H
+
+#include <qvaluelist.h>
+
+class QString;
+
+class GaduContactsList
+{
+public:
+ struct ContactLine {
+
+ QString displayname;
+ QString group;
+ QString uin;
+ QString firstname;
+ QString surname;
+ QString nickname;
+ QString phonenr;
+ QString email;
+ bool ignored;
+ bool offlineTo;
+ QString landline;
+ };
+
+ GaduContactsList();
+ GaduContactsList( QString );
+ ~GaduContactsList();
+ QString asString();
+ void addContact( ContactLine &cl );
+ void addContact( QString& displayname, QString& group,
+ QString& uin, QString& firstname,
+ QString& surname, QString& nickname,
+ QString& phonenr, QString& email,
+ bool ignored, bool offlineTo,
+ QString& landline
+ );
+ unsigned int size();
+ const GaduContactsList::ContactLine& operator[]( unsigned int i );
+private:
+ typedef QValueList<ContactLine> CList;
+ CList cList;
+ CList::iterator it;
+};
+#endif
diff --git a/kopete/protocols/gadu/gadudcc.cpp b/kopete/protocols/gadu/gadudcc.cpp
new file mode 100644
index 00000000..ab6a6223
--- /dev/null
+++ b/kopete/protocols/gadu/gadudcc.cpp
@@ -0,0 +1,186 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadudcc.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <kdebug.h>
+
+#include "gadudccserver.h"
+#include "gadudcc.h"
+#include "gadudcctransaction.h"
+#include "gaduaccount.h"
+
+#include "libgadu.h"
+
+#include <qsocketnotifier.h>
+#include <qhostaddress.h>
+#include <qmutex.h>
+#include <qmap.h>
+#include <qstring.h>
+
+volatile unsigned int GaduDCC::referenceCount = 0;
+
+GaduDCCServer* GaduDCC::dccServer = NULL;
+
+static QMutex initmutex;
+
+typedef QMap< unsigned int, GaduAccount* > gaduAccounts;
+static gaduAccounts accounts;
+
+GaduDCC::GaduDCC( QObject* parent, const char* name )
+:QObject( parent, name )
+{
+}
+
+bool
+GaduDCC::unregisterAccount()
+{
+ return unregisterAccount( accountId );
+}
+
+GaduAccount*
+GaduDCC::account( unsigned int uin )
+{
+ return accounts[ uin ];
+}
+
+bool
+GaduDCC::unregisterAccount( unsigned int id )
+{
+ initmutex.lock();
+
+ if ( id == 0 ) {
+ kdDebug(14100) << "ID nan" << endl;
+ initmutex.unlock();
+ return false;
+ }
+
+ if ( !accounts.contains( id ) ) {
+ kdDebug(14100) << "attempt to unregister not registered account" << endl;
+ initmutex.unlock();
+ return false;
+ }
+
+ accounts.remove( id );
+
+ if ( --referenceCount <= 0 ) {
+ kdDebug(14100) << "closing dcc socket" << endl;
+ referenceCount = 0;
+ if ( dccServer ) {
+ delete dccServer;
+ dccServer = NULL;
+ }
+ }
+ kdDebug(14100) << "reference count " << referenceCount << endl;
+ initmutex.unlock();
+
+ return true;
+}
+
+bool
+GaduDCC::registerAccount( GaduAccount* account )
+{
+ unsigned int aid;
+
+ if ( !account ) {
+ return false;
+ }
+
+ if ( account->accountId().isEmpty() ) {
+ kdDebug(14100) << "attempt to register account with empty ID" << endl;
+ return false;
+ }
+
+ initmutex.lock();
+
+ aid = account->accountId().toInt();
+
+ if ( accounts.contains( aid ) ) {
+ kdDebug(14100) << "attempt to register already registered account" << endl;
+ initmutex.unlock();
+ return false;
+ }
+
+ accountId = aid;
+ kdDebug( 14100 ) << " attempt to register " << accountId << endl;
+
+ accounts[ accountId ] = account;
+
+ referenceCount++;
+
+ if ( !dccServer) {
+ dccServer = new GaduDCCServer();
+ }
+
+ connect( dccServer, SIGNAL( incoming( gg_dcc*, bool& ) ), SLOT( slotIncoming( gg_dcc*, bool& ) ) );
+
+ initmutex.unlock();
+
+ return true;
+}
+void
+GaduDCC::slotIncoming( gg_dcc* incoming, bool& handled )
+{
+ gg_dcc* newdcc;
+ GaduDCCTransaction* dt;
+
+ kdDebug( 14100 ) << "slotIncomming for UIN: " << incoming->uin << endl;
+
+ // no uin? I'm so sorry
+ // this screws file receiving (using kadu 0.4.x as peer) for me
+// if ( !incoming->uin ) {
+// return;
+// }
+
+ handled = true;
+ // TODO: limit number of connections per contact, or maybe even use parametr for that
+ newdcc = new gg_dcc;
+ memcpy( newdcc, incoming, sizeof( gg_dcc ) );
+ dt = new GaduDCCTransaction( this );
+ if ( dt->setupIncoming( newdcc ) == false ) {
+ // FIXME: write something to user, maybe, or not...
+ delete dt;
+ }
+}
+
+GaduDCC::~GaduDCC()
+{
+ if ( accounts.contains( accountId ) ) {
+ kdDebug( 14100 ) << "unregister account " << accountId << " in destructor " << endl;
+ unregisterAccount( accountId );
+ }
+}
+
+unsigned int
+GaduDCC::listeingPort()
+{
+ if ( dccServer ) {
+ return dccServer->listeingPort();
+ }
+ return 0;
+}
+
+#include "gadudcc.moc"
diff --git a/kopete/protocols/gadu/gadudcc.h b/kopete/protocols/gadu/gadudcc.h
new file mode 100644
index 00000000..37ae3e9a
--- /dev/null
+++ b/kopete/protocols/gadu/gadudcc.h
@@ -0,0 +1,66 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadurichtextformat.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+
+#ifndef GADUDCC_H
+#define GADUDCC_H
+
+#include <qobject.h>
+
+class QSocketNotifier;
+class QHostAddress;
+class QString;
+class gg_dcc;
+class GaduDCCTransaction;
+class GaduAccount;
+class GaduDCCServer;
+
+class GaduDCC: public QObject {
+ Q_OBJECT
+public:
+ GaduDCC( QObject* parent, const char* name = NULL );
+ ~GaduDCC();
+ bool unregisterAccount();
+ bool registerAccount( GaduAccount* );
+ unsigned int listeingPort();
+ void unset();
+ void execute();
+ GaduAccount* account( unsigned int );
+
+ QMap<unsigned int,QString> requests;
+signals:
+ void dccConnect( GaduDCCTransaction* dccTransaction );
+
+private slots:
+ void slotIncoming( gg_dcc*, bool& );
+
+private:
+ void closeDCC();
+ bool unregisterAccount( unsigned int );
+
+ unsigned int accountId;
+
+ static GaduDCCServer* dccServer;
+
+ static volatile unsigned int referenceCount;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gadudccserver.cpp b/kopete/protocols/gadu/gadudccserver.cpp
new file mode 100644
index 00000000..fb14277e
--- /dev/null
+++ b/kopete/protocols/gadu/gadudccserver.cpp
@@ -0,0 +1,196 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadurichtextformat.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <kdebug.h>
+
+#include "gadudccserver.h"
+#include "libgadu.h"
+#include "gaduaccount.h"
+
+#include <qobject.h>
+#include <qsocketnotifier.h>
+#include <qhostaddress.h>
+
+GaduDCCServer::GaduDCCServer( QHostAddress* dccIp, unsigned int port )
+:QObject()
+{
+ kdDebug( 14100 ) << "dcc socket NULL, creating new liteining socket " << endl;
+
+ // don't care about UIN at that point
+ dccSock = gg_dcc_socket_create( (unsigned int)-1, port );
+
+ if ( dccSock == NULL ){
+ kdDebug(14100) << "attempt to initialize gadu-dcc listeing socket FAILED" << endl;
+ return;
+ }
+
+ kdDebug(14100) << "attempt to initialize gadu-dcc listeing socket sucess" << endl;
+
+ // using global variables sucks, don't have too much choice thou
+ if ( dccIp == NULL ) {
+ gg_dcc_ip = 0xffffffff; // 255.255.255.255
+ }
+ else {
+ gg_dcc_ip = htonl( dccIp->ip4Addr() );
+ }
+ gg_dcc_port = dccSock->port;
+
+ createNotifiers( true );
+ enableNotifiers( dccSock->check );
+}
+
+GaduDCCServer::~GaduDCCServer()
+{
+ kdDebug( 14100 ) << "gadu dcc server destructor " << endl;
+ closeDCC();
+}
+
+void
+GaduDCCServer::closeDCC()
+{
+ if ( dccSock ) {
+ disableNotifiers();
+ destroyNotifiers();
+ gg_dcc_free( dccSock );
+ dccSock = NULL;
+ gg_dcc_ip = 0;
+ gg_dcc_port = 0;
+ }
+
+}
+
+unsigned int
+GaduDCCServer::listeingPort()
+{
+ if ( dccSock == NULL ) {
+ return 0;
+ }
+// else
+ return dccSock->port;
+}
+
+void
+GaduDCCServer::destroyNotifiers()
+{
+ disableNotifiers();
+ if ( read_ ) {
+ delete read_;
+ read_ = NULL;
+ }
+ if ( write_ ) {
+ delete write_;
+ write_ = NULL;
+ }
+}
+
+void
+GaduDCCServer::createNotifiers( bool connect )
+{
+ if ( !dccSock ){
+ return;
+ }
+
+ read_ = new QSocketNotifier( dccSock->fd, QSocketNotifier::Read, this );
+ read_->setEnabled( false );
+
+ write_ = new QSocketNotifier( dccSock->fd, QSocketNotifier::Write, this );
+ write_->setEnabled( false );
+
+ if ( connect ) {
+ QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( watcher() ) );
+ QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( watcher() ) );
+ }
+}
+
+void
+GaduDCCServer::enableNotifiers( int checkWhat )
+{
+ if( (checkWhat & GG_CHECK_READ) && read_ ) {
+ read_->setEnabled( true );
+ }
+ if( (checkWhat & GG_CHECK_WRITE) && write_ ) {
+ write_->setEnabled( true );
+ }
+}
+
+void
+GaduDCCServer::disableNotifiers()
+{
+ if ( read_ ) {
+ read_->setEnabled( false );
+ }
+ if ( write_ ) {
+ write_->setEnabled( false );
+ }
+}
+
+void
+GaduDCCServer::watcher() {
+
+ gg_event* dccEvent;
+ bool handled = false;
+
+ disableNotifiers();
+
+ dccEvent = gg_dcc_watch_fd( dccSock );
+ if ( ! dccEvent ) {
+ // connection is fucked
+ // we should try to reenable it
+// closeDCC();
+ return;
+ }
+ switch ( dccEvent->type ) {
+ case GG_EVENT_NONE:
+ break;
+ case GG_EVENT_DCC_ERROR:
+ kdDebug( 14100 ) << " dcc error occured " << endl;
+ break;
+ case GG_EVENT_DCC_NEW:
+ // I do expect reciver to set this boolean to true if he handled signal
+ // if so, no other reciver should be bothered with it, and I shall not close it
+ // otherwise connection is closed as not handled
+ emit incoming( dccEvent->event.dcc_new, handled );
+ if ( !handled ) {
+ if ( dccEvent->event.dcc_new->file_fd > 0) {
+ close( dccEvent->event.dcc_new->file_fd );
+ }
+ gg_dcc_free( dccEvent->event.dcc_new );
+ }
+ break;
+ default:
+ kdDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type << endl;
+ break;
+ }
+
+ if ( dccEvent ) {
+ gg_free_event( dccEvent );
+ }
+
+ enableNotifiers( dccSock->check );
+}
+#include "gadudccserver.moc"
diff --git a/kopete/protocols/gadu/gadudccserver.h b/kopete/protocols/gadu/gadudccserver.h
new file mode 100644
index 00000000..50916533
--- /dev/null
+++ b/kopete/protocols/gadu/gadudccserver.h
@@ -0,0 +1,66 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadurichtextformat.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+
+#ifndef GADUDCCSERVER_H
+#define GADUDCCSERVER_H
+
+#include <qobject.h>
+#include <qhostaddress.h>
+
+class QSocketNotifier;
+class QString;
+class gg_dcc;
+class GaduDCCTransaction;
+class GaduAccount;
+
+class GaduDCCServer: public QObject {
+ Q_OBJECT
+public:
+ GaduDCCServer( QHostAddress* dccIp = NULL, unsigned int port = 1550 );
+ ~GaduDCCServer();
+ unsigned int listeingPort();
+
+signals:
+ void incoming( gg_dcc*, bool& );
+
+private slots:
+ void watcher();
+
+private:
+ void enableNotifiers( int );
+ void disableNotifiers();
+ void checkDescriptor();
+
+ void destroyNotifiers();
+ void createNotifiers( bool );
+ void closeDCC();
+
+ QHostAddress config_dccip;
+ QHostAddress config_extip;
+
+ gg_dcc* dccSock;
+
+ QSocketNotifier* read_;
+ QSocketNotifier* write_;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gadudcctransaction.cpp b/kopete/protocols/gadu/gadudcctransaction.cpp
new file mode 100644
index 00000000..561852fe
--- /dev/null
+++ b/kopete/protocols/gadu/gadudcctransaction.cpp
@@ -0,0 +1,454 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadudcctransaction.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "kopetetransfermanager.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+
+#include <qsocketnotifier.h>
+#include <qfile.h>
+
+#include "gadudcctransaction.h"
+#include "gaducontact.h"
+#include "gaduaccount.h"
+#include "gadudcc.h"
+
+#include "libgadu.h"
+
+GaduDCCTransaction::GaduDCCTransaction( GaduDCC* parent, const char* name )
+:QObject( parent, name ), gaduDCC_( parent )
+{
+ read_ = NULL;
+ write_ = NULL;
+ contact = NULL;
+ transfer_ = NULL;
+ dccSock_ = NULL;
+ peer = 0;
+}
+
+GaduDCCTransaction::~GaduDCCTransaction()
+{
+ closeDCC();
+}
+
+unsigned int
+GaduDCCTransaction::recvUIN()
+{
+ if ( dccSock_ ) {
+ return dccSock_->uin;
+ }
+ return 0;
+}
+
+unsigned int
+GaduDCCTransaction::peerUIN()
+{
+ if ( dccSock_ ) {
+ return dccSock_->peer_uin;
+ }
+ return 0;
+}
+
+bool
+GaduDCCTransaction::setupOutgoing( GaduContact* peerContact, QString& filePath )
+{
+ GaduContact* me;
+ GaduAccount* metoo;
+
+ if ( !peerContact ) {
+ return false;
+ }
+
+ me = static_cast<GaduContact*>( peerContact->account()->myself() );
+
+ QString aaa = peerContact->contactIp().toString();
+ kdDebug( 14100 ) << "slotOutgoin for UIN: " << peerContact->uin() << " port " << peerContact->contactPort() << " ip " <<aaa<< endl;
+ kdDebug( 14100 ) << "File path is " << filePath << endl;
+
+ if ( peerContact->contactPort() >= 10 ) {
+ dccSock_ = gg_dcc_send_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), me->uin(), peerContact->uin() );
+ gg_dcc_fill_file_info(dccSock_,filePath.ascii());
+ transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( peerContact,
+ filePath, dccSock_->file_info.size, peerContact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing );
+ createNotifiers( true );
+ enableNotifiers( dccSock_->check );
+ }
+ else {
+ kdDebug( 14100 ) << "Peer " << peerContact->uin() << " is passive, requesting reverse connection" << endl;
+ metoo = static_cast<GaduAccount*>( me->account() );
+ gaduDCC_->requests[peerContact->uin()]=filePath;
+ metoo->dccRequest( peerContact );
+ }
+
+ return false;
+}
+
+bool
+GaduDCCTransaction::setupIncoming( const unsigned int uin, GaduContact* peerContact )
+{
+
+ if ( !peerContact ) {
+ kdDebug( 14100 ) << "setupIncoming called with peerContact == NULL " << endl;
+ return false;
+ }
+
+ QString aaa = peerContact->contactIp().toString();
+ kdDebug( 14100 ) << "setupIncoming for UIN: " << uin << " port " << peerContact->contactPort() << " ip " <<aaa<< endl;
+
+ peer = peerContact->uin();
+ dccSock_ = gg_dcc_get_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), uin, peer );
+
+ contact = peerContact;
+ return setupIncoming( dccSock_ );
+
+}
+
+bool
+GaduDCCTransaction::setupIncoming( gg_dcc* dccS )
+{
+ if ( !dccS ) {
+ kdDebug(14100) << "gg_dcc_get_file failed in GaduDCCTransaction::setupIncoming" << endl;
+ return false;
+ }
+
+ dccSock_ = dccS;
+
+ peer = dccS->uin;
+
+ connect ( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString & ) ),
+ this, SLOT( slotIncomingTransferAccepted ( Kopete::Transfer *, const QString & ) ) );
+ connect ( Kopete::TransferManager::transferManager(), SIGNAL( refused( const Kopete::FileTransferInfo & ) ),
+ this, SLOT( slotTransferRefused( const Kopete::FileTransferInfo & ) ) );
+
+ incoming = true;
+ createNotifiers( true );
+ enableNotifiers( dccSock_->check );
+
+ return true;
+}
+
+
+void
+GaduDCCTransaction::closeDCC()
+{
+ kdDebug(14100) << "closeDCC()" << endl;
+
+ disableNotifiers();
+ destroyNotifiers();
+ gg_dcc_free( dccSock_ );
+ dccSock_ = NULL;
+}
+
+void
+GaduDCCTransaction::destroyNotifiers()
+{
+ disableNotifiers();
+ if ( read_ ) {
+ delete read_;
+ read_ = NULL;
+ }
+ if ( write_ ) {
+ delete write_;
+ write_ = NULL;
+ }
+}
+
+void
+GaduDCCTransaction::createNotifiers( bool connect )
+{
+ if ( !dccSock_ ){
+ return;
+ }
+
+ read_ = new QSocketNotifier( dccSock_->fd, QSocketNotifier::Read, this );
+ read_->setEnabled( false );
+
+ write_ = new QSocketNotifier( dccSock_->fd, QSocketNotifier::Write, this );
+ write_->setEnabled( false );
+
+ if ( connect ) {
+ QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( watcher() ) );
+ QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( watcher() ) );
+ }
+}
+
+void
+GaduDCCTransaction::enableNotifiers( int checkWhat )
+{
+ if( (checkWhat & GG_CHECK_READ) && read_ ) {
+ read_->setEnabled( true );
+ }
+ if( (checkWhat & GG_CHECK_WRITE) && write_ ) {
+ write_->setEnabled( true );
+ }
+}
+
+void
+GaduDCCTransaction::disableNotifiers()
+{
+ if ( read_ ) {
+ read_->setEnabled( false );
+ }
+ if ( write_ ) {
+ write_->setEnabled( false );
+ }
+}
+void
+GaduDCCTransaction::slotIncomingTransferAccepted ( Kopete::Transfer* transfer, const QString& fileName )
+{
+
+ if ( (long)transfer->info().transferId () != transferId_ ) {
+ return;
+ }
+
+ transfer_ = transfer;
+ localFile_.setName( fileName );
+
+ if ( localFile_.exists() ) {
+ KGuiItem resumeButton( i18n ( "&Resume" ) );
+ KGuiItem overwriteButton( i18n ( "Over&write" ) );
+ switch ( KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget (),
+ i18n( "The file %1 already exists, do you want to resume or overwrite it?" ).arg( fileName ),
+ i18n( "File Exists: %1" ).arg( fileName ), resumeButton, overwriteButton ) )
+ {
+ // resume
+ case KMessageBox::Yes:
+ if ( localFile_.open( IO_WriteOnly | IO_Append ) ) {
+ dccSock_->offset = localFile_.size();
+ dccSock_->file_fd = localFile_.handle();
+ }
+ break;
+ // overwrite
+ case KMessageBox::No:
+ if ( localFile_.open( IO_ReadWrite ) ) {
+ dccSock_->offset = 0;
+ dccSock_->file_fd = localFile_.handle();
+ }
+ break;
+
+ // cancel
+ default:
+ closeDCC();
+ deleteLater();
+ return;
+ break;
+ }
+ if ( localFile_.handle() < 1 ) {
+ closeDCC();
+ deleteLater();
+ return;
+ }
+ }
+ else {
+ // overwrite by default
+ if ( localFile_.open( IO_ReadWrite ) == FALSE ) {
+ transfer->slotError ( KIO::ERR_COULD_NOT_WRITE, fileName );
+ closeDCC();
+ deleteLater ();
+ return;
+ }
+ dccSock_->offset = 0;
+ dccSock_->file_fd = localFile_.handle();
+ }
+
+ connect ( transfer, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotTransferResult() ) );
+
+ // reenable notifiers
+ enableNotifiers( dccSock_->check );
+
+}
+
+void
+GaduDCCTransaction::slotTransferResult()
+{
+ if ( transfer_->error() == KIO::ERR_USER_CANCELED ) {
+ closeDCC();
+ deleteLater();
+ }
+}
+
+void
+GaduDCCTransaction::slotTransferRefused ( const Kopete::FileTransferInfo& transfer )
+{
+ if ( (long)transfer.transferId () != transferId_ )
+ return;
+ closeDCC();
+ deleteLater();
+}
+
+void
+GaduDCCTransaction::askIncommingTransfer()
+{
+
+ transferId_ = Kopete::TransferManager::transferManager()->askIncomingTransfer ( contact,
+ QString( (const char*)dccSock_->file_info.filename ), dccSock_->file_info.size );
+
+}
+
+void
+GaduDCCTransaction::watcher() {
+
+ gg_event* dccEvent;
+ GaduAccount* account;
+
+ disableNotifiers();
+
+ dccEvent = gg_dcc_watch_fd( dccSock_ );
+ if ( ! dccEvent ) {
+ // connection is fucked
+ closeDCC();
+ return;
+ }
+ switch ( dccEvent->type ) {
+ case GG_EVENT_DCC_CLIENT_ACCEPT:
+ kdDebug(14100) << " GG_EVENT_DCC_CLIENT_ACCEPT " << endl;
+ // check dccsock->peer_uin, if unknown, fuck it;
+
+ // is it for us ?
+ account = gaduDCC_->account( dccSock_->uin );
+ if ( !account ) {
+ kdDebug( 14100 ) << " this dcc transaction is for uin " << dccSock_->uin << ", which is not quite for me... closing" << endl;
+ // unknown 'to' ?, we're off
+ gg_free_event( dccEvent );
+ closeDCC();
+ deleteLater();
+ return;
+ }
+
+ if ( !peer ) {
+ contact = static_cast<GaduContact*> (account->contacts()[ QString::number( dccSock_->peer_uin ) ]);
+ }
+ else {
+ contact = static_cast<GaduContact*> (account->contacts()[ QString::number( peer ) ]);
+ }
+
+ if ( contact == NULL ) {
+ // refusing, contact on the list
+ kdDebug(14100) << " dcc connection from " << dccSock_->peer_uin << " refused, UIN not on the list " <<endl;
+ gg_free_event( dccEvent );
+ closeDCC();
+ // emit error
+ deleteLater();
+ return;
+ }
+ else {
+ // ask user to accept that transfer
+ kdDebug(14100) << " dcc accepted from " << dccSock_->peer_uin << endl;
+ }
+
+ break;
+ case GG_EVENT_DCC_CALLBACK:
+ kdDebug(14100) << "GG_DCC_EVENT_CALLBACK" << endl;
+ break;
+ case GG_EVENT_NONE:
+ kdDebug(14100) << " GG_EVENT_NONE" << endl;
+ // update gui with progress
+ if ( transfer_ ) {
+ transfer_->slotProcessed( dccSock_->offset );
+ }
+ break;
+
+ case GG_EVENT_DCC_NEED_FILE_ACK:
+ kdDebug(14100) << " GG_EVENT_DCC_NEED_FILE_ACK " << endl;
+ gg_free_event( dccEvent );
+ askIncommingTransfer();
+ return;
+ break;
+ case GG_EVENT_DCC_NEED_FILE_INFO:
+ if (gaduDCC_->requests.contains(dccSock_->peer_uin)) {
+ QString filePath = gaduDCC_->requests[dccSock_->peer_uin];
+ kdDebug() << "Callback request found. Sending " << filePath << endl;
+ gaduDCC_->requests.remove(dccSock_->peer_uin);
+ gg_dcc_fill_file_info(dccSock_,filePath.ascii());
+ transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( contact,
+ filePath, dccSock_->file_info.size, contact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing );
+ } else {
+ gg_free_event( dccEvent );
+ closeDCC();
+ deleteLater();
+ return;
+ }
+ break;
+
+ case GG_EVENT_DCC_ERROR:
+ kdDebug(14100) << " GG_EVENT_DCC_ERROR :" << dccEvent->event.dcc_error << endl;
+ if ( transfer_ ) {
+ switch( dccEvent->event.dcc_error ) {
+
+ case GG_ERROR_DCC_REFUSED:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Connection to peer was refused; it possibly does not listen for incoming connections." ) );
+ break;
+
+ case GG_ERROR_DCC_EOF:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer transaction was not agreed by peer." ) );
+ break;
+
+ case GG_ERROR_DCC_HANDSHAKE:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File-transfer handshake failure." ) );
+ break;
+ case GG_ERROR_DCC_FILE:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer had problems with the file." ) );
+ break;
+ case GG_ERROR_DCC_NET:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "There was network error during file transfer." ) );
+ break;
+ default:
+ transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Unknown File-Transfer error." ) );
+ break;
+ }
+ }
+ gg_free_event( dccEvent );
+ closeDCC();
+ deleteLater();
+ return;
+
+ case GG_EVENT_DCC_DONE:
+ if ( transfer_ ) {
+ transfer_->slotComplete();
+ }
+ closeDCC();
+ deleteLater();
+ return;
+
+ default:
+ kdDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type << endl;
+ break;
+ }
+
+ if ( dccEvent ) {
+ gg_free_event( dccEvent );
+ }
+
+ enableNotifiers( dccSock_->check );
+}
+
+#include "gadudcctransaction.moc"
diff --git a/kopete/protocols/gadu/gadudcctransaction.h b/kopete/protocols/gadu/gadudcctransaction.h
new file mode 100644
index 00000000..4c2edb58
--- /dev/null
+++ b/kopete/protocols/gadu/gadudcctransaction.h
@@ -0,0 +1,87 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadudcctransaction.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+
+#ifndef GADUDCCTRANS_H
+#define GADUDCCTRANS_H
+
+#include <qobject.h>
+#include <qfile.h>
+
+class QSocketNotifier;
+class gg_dcc;
+class GaduAccount;
+class GaduContact;
+namespace Kopete { class Transfer; }
+namespace Kopete { class FileTransferInfo; }
+class GaduDCC;
+
+class GaduDCCTransaction: QObject {
+ Q_OBJECT
+public:
+ GaduDCCTransaction( GaduDCC*, const char* name = NULL );
+ ~GaduDCCTransaction();
+
+ bool setupIncoming( const unsigned int, GaduContact* );
+ bool setupIncoming( gg_dcc* );
+ bool setupOutgoing( GaduContact*, QString& );
+ unsigned int recvUIN();
+ unsigned int peerUIN();
+
+public slots:
+
+signals:
+
+protected:
+
+protected slots:
+
+private slots:
+ void watcher();
+ void slotIncomingTransferAccepted ( Kopete::Transfer*, const QString& );
+ void slotTransferRefused ( const Kopete::FileTransferInfo& );
+ void slotTransferResult();
+
+private:
+ void enableNotifiers( int );
+ void disableNotifiers();
+ void checkDescriptor();
+ void closeDCC();
+ void destroyNotifiers();
+ void createNotifiers( bool );
+ void askIncommingTransfer();
+
+ gg_dcc* dccSock_;
+
+ QSocketNotifier* read_;
+ QSocketNotifier* write_;
+
+ GaduContact* contact;
+
+ Kopete::Transfer* transfer_;
+ long transferId_;
+ QFile localFile_;
+ int peer;
+ unsigned int incoming;
+ GaduDCC* gaduDCC_;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gadueditaccount.cpp b/kopete/protocols/gadu/gadueditaccount.cpp
new file mode 100644
index 00000000..250ae4cd
--- /dev/null
+++ b/kopete/protocols/gadu/gadueditaccount.cpp
@@ -0,0 +1,263 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadueditaccount.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include "gadueditaccount.h"
+#include "gaduaccount.h"
+#include "gaduprotocol.h"
+#include "gadusession.h"
+
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qbutton.h>
+#include <qregexp.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpassdlg.h>
+
+#include "kopetepasswordwidget.h"
+
+GaduEditAccount::GaduEditAccount( GaduProtocol* proto, Kopete::Account* ident, QWidget* parent, const char* name )
+: GaduAccountEditUI( parent, name ), KopeteEditAccountWidget( ident ), protocol_( proto ), rcmd( 0 )
+{
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ isSsl = true;
+#else
+ isSsl = false;
+#endif
+
+ useTls_->setDisabled( !isSsl );
+
+ if ( account() == NULL ) {
+ useTls_->setCurrentItem( GaduAccount::TLS_no );
+ registerNew->setEnabled( true );
+ account_ = NULL;
+ }
+ else {
+ account_ = static_cast<GaduAccount*>(ident);
+
+ registerNew->setDisabled( true );
+ loginEdit_->setDisabled( true );
+ loginEdit_->setText( account_->accountId() );
+
+ passwordWidget_->load( &account_->password() );
+
+ QString nick = account()->myself()->property(
+ Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if ( nick.isEmpty() ) {
+ nick = account_->myself()->contactId();
+ }
+
+ nickName->setText( nick );
+
+ autoLoginCheck_->setChecked( account_->excludeConnect() );
+ dccCheck_->setChecked( account_->dccEnabled() );
+ useTls_->setCurrentItem( isSsl ? ( account_->useTls() ) : 2 );
+ ignoreCheck_->setChecked( account_->ignoreAnons() );
+
+ connect( account(), SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ),
+ SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) );
+ connectLabel->setText( i18n( "personal information being fetched from server",
+ "<p align=\"center\">Fetching from server</p>" ) );
+ seqNr = account_->getPersonalInformation();
+ }
+
+ connect( registerNew, SIGNAL( clicked( ) ), SLOT( registerNewAccount( ) ) );
+
+ QWidget::setTabOrder( loginEdit_, passwordWidget_->mRemembered );
+ QWidget::setTabOrder( passwordWidget_->mRemembered, passwordWidget_->mPassword );
+ QWidget::setTabOrder( passwordWidget_->mPassword, autoLoginCheck_ );
+}
+
+void
+GaduEditAccount::publishUserInfo()
+{
+ ResLine sr;
+
+ enableUserInfo( false );
+
+ sr.firstname = uiName->text();
+ sr.surname = uiSurname->text();
+ sr.nickname = nickName->text();
+ sr.age = uiYOB->text();
+ sr.city = uiCity->text();
+ sr.meiden = uiMeiden->text();
+ sr.orgin = uiOrgin->text();
+
+ kdDebug(14100) << uiGender->currentItem() << " gender " << endl;
+ if ( uiGender->currentItem() == 1 ) {
+ kdDebug(14100) << "so you become female now" << endl;
+ sr.gender = QString( GG_PUBDIR50_GENDER_SET_FEMALE );
+ }
+ if ( uiGender->currentItem() == 2 ) {
+ kdDebug(14100) << "so you become male now" << endl;
+ sr.gender = QString( GG_PUBDIR50_GENDER_SET_MALE );
+ }
+
+ if ( account_ ) {
+ account_->publishPersonalInformation( sr );
+ }
+}
+
+void
+GaduEditAccount::slotSearchResult( const SearchResult& result, unsigned int seq )
+{
+ if ( !( seq != 0 && seqNr != 0 && seq == seqNr ) ) {
+ return;
+ }
+
+ connectLabel->setText( " " );
+
+ uiName->setText( result[0].firstname );
+ uiSurname->setText( result[0].surname );
+ nickName->setText( result[0].nickname );
+ uiYOB->setText( result[0].age );
+ uiCity->setText( result[0].city );
+
+ kdDebug( 14100 ) << "gender found: " << result[0].gender << endl;
+ if ( result[0].gender == QString( GG_PUBDIR50_GENDER_SET_FEMALE ) ) {
+ uiGender->setCurrentItem( 1 );
+ kdDebug(14100) << "looks like female" << endl;
+ }
+ else {
+ if ( result[0].gender == QString( GG_PUBDIR50_GENDER_SET_MALE ) ) {
+ uiGender->setCurrentItem( 2 );
+ kdDebug( 14100 ) <<" looks like male" << endl;
+ }
+ }
+
+ uiMeiden->setText( result[0].meiden );
+ uiOrgin->setText( result[0].orgin );
+
+ enableUserInfo( true );
+
+ disconnect( SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) );
+}
+
+void
+GaduEditAccount::enableUserInfo( bool e )
+{
+ uiName->setEnabled( e );
+ uiSurname->setEnabled( e );
+ uiYOB->setEnabled( e );
+ uiCity->setEnabled( e );
+ uiGender->setEnabled( e );
+ uiMeiden->setEnabled( e );
+ uiOrgin->setEnabled( e );
+
+// connectLabel->setEnabled( !e );
+}
+
+void
+GaduEditAccount::registerNewAccount()
+{
+ registerNew->setDisabled( true );
+ regDialog = new GaduRegisterAccount( NULL , "Register account dialog" );
+ connect( regDialog, SIGNAL( registeredNumber( unsigned int, QString ) ), SLOT( newUin( unsigned int, QString ) ) );
+ if ( regDialog->exec() != QDialog::Accepted ) {
+ loginEdit_->setText( "" );
+ return;
+ }
+ registerNew->setDisabled( false );
+}
+
+void
+GaduEditAccount::registrationFailed()
+{
+ KMessageBox::sorry( this, i18n( "<b>Registration FAILED.</b>" ), i18n( "Gadu-Gadu" ) );
+}
+
+void
+GaduEditAccount::newUin( unsigned int uin, QString password )
+{
+ if ( uin ) {
+ loginEdit_->setText( QString::number( uin ) );
+ passwordWidget_->setPassword( password );
+ }
+ else {
+ // registration failed, enable button again
+ registerNew->setDisabled( false );
+ }
+}
+
+bool
+GaduEditAccount::validateData()
+{
+
+ if ( loginEdit_->text().isEmpty() ) {
+ KMessageBox::sorry( this, i18n( "<b>Enter UIN please.</b>" ), i18n( "Gadu-Gadu" ) );
+ return false;
+ }
+
+ if ( loginEdit_->text().toInt() < 0 || loginEdit_->text().toInt() == 0 ) {
+ KMessageBox::sorry( this, i18n( "<b>UIN should be a positive number.</b>" ), i18n( "Gadu-Gadu" ) );
+ return false;
+ }
+
+ if ( !passwordWidget_->validate() ) {
+ KMessageBox::sorry( this, i18n( "<b>Enter password please.</b>" ), i18n( "Gadu-Gadu" ) );
+ return false;
+ }
+
+ return true;
+}
+
+Kopete::Account*
+GaduEditAccount::apply()
+{
+ publishUserInfo();
+
+ if ( account() == NULL ) {
+ setAccount( new GaduAccount( protocol_, loginEdit_->text() ) );
+ account_ = static_cast<GaduAccount*>( account() );
+ }
+
+ account_->setExcludeConnect( autoLoginCheck_->isChecked() );
+
+ passwordWidget_->save( &account_->password() );
+
+ account_->myself()->setProperty( Kopete::Global::Properties::self()->nickName(), nickName->text() );
+
+ // this is changed only here, so i won't add any proper handling now
+ account_->configGroup()->writeEntry( QString::fromAscii( "nickName" ), nickName->text() );
+
+ account_->setExcludeConnect( autoLoginCheck_->isChecked() );
+ account_->setUseTls( (GaduAccount::tlsConnection) useTls_->currentItem() );
+ account_->setIgnoreAnons( ignoreCheck_->isChecked() );
+
+ if ( account_->setDcc( dccCheck_->isChecked() ) == false ) {
+ KMessageBox::sorry( this, i18n( "<b>Starting DCC listening socket failed; dcc is not working now.</b>" ), i18n( "Gadu-Gadu" ) );
+ }
+
+ return account();
+}
+
+#include "gadueditaccount.moc"
+
diff --git a/kopete/protocols/gadu/gadueditaccount.h b/kopete/protocols/gadu/gadueditaccount.h
new file mode 100644
index 00000000..87775f2a
--- /dev/null
+++ b/kopete/protocols/gadu/gadueditaccount.h
@@ -0,0 +1,63 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadueditaccount.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUEDITACCOUNT_H
+#define GADUEDITACCOUNT_H
+
+#include "gadueditaccountui.h"
+#include "editaccountwidget.h"
+#include "gaduregisteraccount.h"
+#include "gadusession.h"
+
+class GaduAccount;
+class GaduProtocol;
+
+namespace Kopete { class Account; }
+
+class GaduEditAccount : public GaduAccountEditUI, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+
+public:
+ GaduEditAccount( GaduProtocol*, Kopete::Account*, QWidget* parent = 0, const char* name = 0 );
+ virtual bool validateData();
+ Kopete::Account* apply();
+
+private slots:
+ void registerNewAccount();
+ void newUin( unsigned int, QString );
+ void registrationFailed();
+ void slotSearchResult( const SearchResult&, unsigned int );
+
+private:
+ void enableUserInfo( bool );
+ void publishUserInfo();
+
+ GaduProtocol* protocol_;
+ bool reg_in_progress;
+ bool isSsl;
+ RegisterCommand* rcmd;
+ GaduRegisterAccount* regDialog;
+ GaduAccount* account_;
+ unsigned int seqNr;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gadueditcontact.cpp b/kopete/protocols/gadu/gadueditcontact.cpp
new file mode 100644
index 00000000..3844e691
--- /dev/null
+++ b/kopete/protocols/gadu/gadueditcontact.cpp
@@ -0,0 +1,203 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadueditcontact.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include "gaduaccount.h"
+#include "gaducontact.h"
+#include "gadueditcontact.h"
+#include "kopeteonlinestatus.h"
+
+#include "gaducontactlist.h"
+#include "gaduadd.h"
+
+#include <ktextedit.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kopetegroup.h>
+#include <kopetecontactlist.h>
+#include <kopetemetacontact.h>
+
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <qlayout.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+
+#include <krestrictedline.h>
+
+// FIXME: this and gaduadcontactpage should have one base class, with some code duplicated in both.
+
+GaduEditContact::GaduEditContact( GaduAccount* account, GaduContact* contact,
+ QWidget* parent, const char* name )
+: KDialogBase( parent, name, true, i18n( "Edit Contact's Properties" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true ), account_( account ), contact_( contact )
+{
+ if ( contact && account ) {
+ cl_ = contact->contactDetails();
+ }
+ else {
+ return;
+ }
+
+ init();
+ fillGroups();
+ fillIn();
+}
+
+GaduEditContact::GaduEditContact( GaduAccount* account, GaduContactsList::ContactLine* clin,
+ QWidget* parent , const char* name )
+: KDialogBase( parent, name, true, i18n( "Edit Contact's Properties" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok, true ), account_( account ), contact_( NULL )
+{
+
+ if ( !account ) {
+ return;
+ }
+ cl_ = clin;
+ init();
+ fillGroups();
+ fillIn();
+}
+
+void
+GaduEditContact::fillGroups()
+{
+ Kopete::Group *g, *cg;
+ QPtrList<Kopete::Group> cgl;
+ QPtrList<Kopete::Group> gl;
+
+ if ( contact_ ) {
+ cgl = contact_->metaContact()->groups();
+ }
+
+ gl = Kopete::ContactList::self()->groups();
+
+ for( g = gl.first(); g; g = gl.next() ) {
+ if ( g->type() == Kopete::Group::Temporary ) {
+ continue;
+ }
+ QCheckListItem* item = new QCheckListItem( ui_->groups, g->displayName(), QCheckListItem::CheckBox );
+ // FIXME: optimize this O(2) search
+ for( cg = cgl.first(); cg; cg = cgl.next() ) {
+ if ( cg->groupId() == g->groupId() ) {
+ item->setOn( TRUE );
+ break;
+ }
+ }
+ kdDebug(14100) << g->displayName() << " " << g->groupId() << endl;
+ }
+}
+
+void
+GaduEditContact::init()
+{
+ ui_ = new GaduAddUI( this );
+ setMainWidget( ui_ );
+ ui_->addEdit_->setValidChars( "1234567890" );
+
+ // fill values from cl into proper fields on widget
+
+ show();
+ connect( this, SIGNAL( okClicked() ), SLOT( slotApply() ) );
+ connect( ui_->groups, SIGNAL( clicked( QListViewItem * ) ), SLOT( listClicked( QListViewItem * ) ) );
+}
+
+void
+GaduEditContact::listClicked( QListViewItem* /*item*/ )
+{
+
+}
+
+void
+GaduEditContact::fillIn()
+{
+// grey it out, it shouldn't be editable
+ ui_->addEdit_->setReadOnly( true );
+ ui_->addEdit_->setText( cl_->uin );
+
+ ui_->fornameEdit_->setText( cl_->firstname );
+ ui_->snameEdit_->setText( cl_->surname );
+ ui_->nickEdit_->setText( cl_->nickname );
+ ui_->emailEdit_->setText( cl_->email );
+ ui_->telephoneEdit_->setText( cl_->phonenr );
+// ui_->notAFriend_;
+
+}
+
+void
+GaduEditContact::slotApply()
+{
+ QPtrList<Kopete::Group> gl;
+ Kopete::Group* group;
+
+ cl_->firstname = ui_->fornameEdit_->text().stripWhiteSpace();
+ cl_->surname = ui_->snameEdit_->text().stripWhiteSpace();
+ cl_->nickname = ui_->nickEdit_->text().stripWhiteSpace();
+ cl_->email = ui_->emailEdit_->text().stripWhiteSpace();
+ cl_->phonenr = ui_->telephoneEdit_->text().stripWhiteSpace();
+
+ if ( contact_ == NULL ) {
+ // contact doesn't exists yet, create it and set all the details
+ bool s = account_->addContact( cl_->uin, GaduContact::findBestContactName( cl_ ), 0L, Kopete::Account::DontChangeKABC);
+ if ( s == false ) {
+ kdDebug(14100) << "There was a problem adding UIN "<< cl_->uin << "to users list" << endl;
+ return;
+ }
+ contact_ = static_cast<GaduContact*>( account_->contacts()[ cl_->uin ] );
+ if ( contact_ == NULL ) {
+ kdDebug(14100) << "oops, no Kopete::Contact in contacts()[] for some reason, for \"" << cl_->uin << "\"" << endl;
+ return;
+ }
+ }
+
+ contact_->setContactDetails( cl_ );
+
+ gl = Kopete::ContactList::self()->groups();
+ for ( QListViewItemIterator it( ui_->groups ); it.current(); ++it ) {
+ QCheckListItem *check = dynamic_cast<QCheckListItem *>( it.current() );
+
+ if ( !check ) {
+ continue;
+ }
+
+ if ( check->isOn() ) {
+ for( group = gl.first(); group; group = gl.next() ) {
+ if ( group->displayName() == check->text() ) {
+ contact_->metaContact()->addToGroup( group );
+ }
+ }
+ }
+ else {
+ // check metacontact's in the group, and if so, remove it from
+ for( group = gl.first(); group; group = gl.next() ) {
+ if ( group->displayName() == check->text() ) {
+ contact_->metaContact()->removeFromGroup( group );
+ }
+ }
+ }
+ }
+
+ if( contact_->metaContact()->groups().isEmpty() == TRUE )
+ contact_->metaContact()->addToGroup( Kopete::Group::topLevel() );
+}
+#include "gadueditcontact.moc"
diff --git a/kopete/protocols/gadu/gadueditcontact.h b/kopete/protocols/gadu/gadueditcontact.h
new file mode 100644
index 00000000..6b209b79
--- /dev/null
+++ b/kopete/protocols/gadu/gadueditcontact.h
@@ -0,0 +1,60 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gadueditcontact.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUEDITCONTACT_H
+#define GADUEDITCONTACT_H
+
+#include <kdialogbase.h>
+
+class GaduAccount;
+class GaduAddUI;
+class QLabel;
+class QString;
+class QWidget;
+class GaduContact;
+class GaduContactsList::ContactLine;
+class QListViewItem;
+
+class GaduEditContact : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ GaduEditContact( GaduAccount*, GaduContact*,
+ QWidget* parent = 0, const char* name = 0 );
+ GaduEditContact( GaduAccount*, GaduContactsList::ContactLine*,
+ QWidget* parent = 0, const char* name = 0 );
+protected slots:
+ void slotApply();
+ void listClicked( QListViewItem* );
+private:
+
+ void init();
+ void fillIn();
+ void fillGroups();
+ GaduAccount* account_;
+ GaduContact* contact_;
+ GaduAddUI* ui_;
+ GaduContactsList::ContactLine* cl_;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gaduprotocol.cpp b/kopete/protocols/gadu/gaduprotocol.cpp
new file mode 100644
index 00000000..cab6bfe0
--- /dev/null
+++ b/kopete/protocols/gadu/gaduprotocol.cpp
@@ -0,0 +1,243 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaduprotocol.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kconfig.h>
+
+#include <libgadu.h>
+
+#include "gaduaccount.h"
+#include "gaducontact.h"
+#include "gaduprotocol.h"
+
+#include "gadueditaccount.h"
+#include "gaduaddcontactpage.h"
+
+#include "kopeteaccountmanager.h"
+#include "kopeteaccount.h"
+#include "kopetemetacontact.h"
+#include "kopeteglobal.h"
+#include "kopeteonlinestatusmanager.h"
+
+typedef KGenericFactory<GaduProtocol> GaduProtocolFactory;
+
+K_EXPORT_COMPONENT_FACTORY( kopete_gadu, KGenericFactory<GaduProtocol>( "kopete_gadu" ) )
+
+GaduProtocol* GaduProtocol::protocolStatic_ = 0L;
+
+GaduProtocol::GaduProtocol( QObject* parent, const char* name, const QStringList& )
+:Kopete::Protocol( GaduProtocolFactory::instance(), parent, name ),
+ propFirstName(Kopete::Global::Properties::self()->firstName()),
+ propLastName(Kopete::Global::Properties::self()->lastName()),
+ propEmail(Kopete::Global::Properties::self()->emailAddress()),
+ propAwayMessage(Kopete::Global::Properties::self()->awayMessage()),
+ propPhoneNr(Kopete::Global::Properties::self()->privatePhone()),
+ defaultAccount_( 0 ),
+ gaduStatusBlocked_( Kopete::OnlineStatus::Away, GG_STATUS_BLOCKED, this, GG_STATUS_BLOCKED,
+ "gg_ignored", i18n( "Blocked" ) ),
+ gaduStatusOffline_( Kopete::OnlineStatus::Offline, GG_STATUS_NOT_AVAIL, this, GG_STATUS_NOT_AVAIL,
+ "gg_offline", i18n( "Offline" ) , i18n( "O&ffline" ) , Kopete::OnlineStatusManager::Offline ),
+ gaduStatusOfflineDescr_( Kopete::OnlineStatus::Offline, GG_STATUS_NOT_AVAIL_DESCR, this, GG_STATUS_NOT_AVAIL_DESCR,
+ QStringList::split( '|', "contact_away_overlay|gg_description_overlay" ), i18n( "Offline" ), i18n( "A&way" ) , Kopete::OnlineStatusManager::Offline ),
+ gaduStatusBusy_(Kopete::OnlineStatus::Away, GG_STATUS_BUSY, this, GG_STATUS_BUSY,
+ "contact_away_overlay", i18n( "Busy" ) , i18n( "B&usy" ) , Kopete::OnlineStatusManager::Busy ),
+ gaduStatusBusyDescr_(Kopete::OnlineStatus::Away, GG_STATUS_BUSY_DESCR, this, GG_STATUS_BUSY_DESCR,
+ QStringList::split( '|', "contact_away_overlay|gg_description_overlay" ), i18n( "Busy" ) , i18n( "B&usy" ) , Kopete::OnlineStatusManager::Idle ),
+ gaduStatusInvisible_( Kopete::OnlineStatus::Invisible, GG_STATUS_INVISIBLE, this, GG_STATUS_INVISIBLE,
+ "contact_invisible_overlay", i18n( "Invisible" ) , i18n( "I&nvisible" ) , Kopete::OnlineStatusManager::Invisible),
+ gaduStatusInvisibleDescr_(Kopete::OnlineStatus::Invisible, GG_STATUS_INVISIBLE_DESCR, this, GG_STATUS_INVISIBLE_DESCR,
+ QStringList::split( '|', "contact_invisible_overlay|gg_description_overlay" ), i18n( "Invisible" ) , i18n( "I&nvisible" )),
+ gaduStatusAvail_(Kopete::OnlineStatus::Online, GG_STATUS_AVAIL, this, GG_STATUS_AVAIL,
+ QString::null, i18n( "Online" ) , i18n( "&Online" ) , Kopete::OnlineStatusManager::Online ),
+ gaduStatusAvailDescr_(Kopete::OnlineStatus::Online, GG_STATUS_AVAIL_DESCR, this, GG_STATUS_AVAIL_DESCR,
+ "gg_description_overlay", i18n( "Online" ) , i18n( "&Online" )),
+ gaduConnecting_(Kopete::OnlineStatus::Offline, GG_STATUS_CONNECTING, this, GG_STATUS_CONNECTING,
+ "gg_con", i18n( "Connecting" ) )
+{
+ if ( protocolStatic_ ) {
+ kdDebug(14100)<<"####"<<"GaduProtocol already initialized"<<endl;
+ }
+ else {
+ protocolStatic_ = this;
+ }
+
+ addAddressBookField( "messaging/gadu", Kopete::Plugin::MakeIndexField );
+
+ setCapabilities( Kopete::Protocol::RichFormatting | Kopete::Protocol::RichFgColor );
+
+}
+
+GaduProtocol::~GaduProtocol()
+{
+ protocolStatic_ = 0L;
+}
+
+GaduProtocol*
+GaduProtocol::protocol()
+{
+ return protocolStatic_;
+}
+
+AddContactPage*
+GaduProtocol::createAddContactWidget( QWidget* parent, Kopete::Account* account )
+{
+ return new GaduAddContactPage( static_cast<GaduAccount*>( account ), parent );
+}
+
+void
+GaduProtocol::settingsChanged()
+{
+}
+
+Kopete::Contact *
+GaduProtocol::deserializeContact( Kopete::MetaContact* metaContact,
+ const QMap<QString, QString>& serializedData,
+ const QMap<QString, QString>& /* addressBookData */ )
+{
+
+ const QString aid = serializedData[ "accountId" ];
+ const QString cid = serializedData[ "contactId" ];
+ const QString dn = serializedData[ "displayName" ];
+
+ QDict<Kopete::Account> daccounts = Kopete::AccountManager::self()->accounts( this );
+
+ Kopete::Account* account = daccounts[ aid ];
+ if (!account) {
+ account = createNewAccount(aid);
+ }
+
+ GaduAccount* gaccount = static_cast<GaduAccount *>( account );
+
+ GaduContact* contact = new GaduContact( cid.toUInt(), dn, account, metaContact );
+
+ contact->setParentIdentity( aid );
+ gaccount->addNotify( cid.toUInt() );
+
+ contact->setProperty( propEmail, serializedData["email"] );
+ contact->setProperty( propFirstName, serializedData["FirstName"] );
+ contact->setProperty( propLastName, serializedData["SecondName"] );
+ contact->setProperty( propPhoneNr, serializedData["telephone"] );
+ contact->setIgnored(serializedData["ignored"] == "true");
+ return contact;
+}
+
+uint
+GaduProtocol::statusToWithDescription( Kopete::OnlineStatus status )
+{
+
+ if ( status == gaduStatusOffline_ || status == gaduStatusOfflineDescr_ ) {
+ return GG_STATUS_NOT_AVAIL_DESCR;
+ }
+
+ if ( status == gaduStatusBusyDescr_ || status == gaduStatusBusy_ ){
+ return GG_STATUS_BUSY_DESCR;
+ }
+
+ if ( status == gaduStatusInvisibleDescr_ || status == gaduStatusInvisible_ ){
+ return GG_STATUS_INVISIBLE_DESCR;
+ }
+
+ return GG_STATUS_AVAIL_DESCR;
+}
+
+uint
+GaduProtocol::statusToWithoutDescription( Kopete::OnlineStatus status )
+{
+ if ( status == gaduStatusOffline_ || status == gaduStatusOfflineDescr_ ) {
+ return GG_STATUS_NOT_AVAIL;
+ }
+
+ if ( status == gaduStatusBusyDescr_ || status == gaduStatusBusy_ ){
+ return GG_STATUS_BUSY;
+ }
+
+ if ( status == gaduStatusInvisibleDescr_ || status == gaduStatusInvisible_ ){
+ return GG_STATUS_INVISIBLE;
+ }
+
+ return GG_STATUS_AVAIL;
+}
+
+bool
+GaduProtocol::statusWithDescription( uint status )
+{
+ switch( status ) {
+ case GG_STATUS_NOT_AVAIL:
+ case GG_STATUS_BUSY:
+ case GG_STATUS_INVISIBLE:
+ case GG_STATUS_AVAIL:
+ case GG_STATUS_CONNECTING:
+ case GG_STATUS_BLOCKED:
+ return false;
+ case GG_STATUS_INVISIBLE_DESCR:
+ case GG_STATUS_NOT_AVAIL_DESCR:
+ case GG_STATUS_BUSY_DESCR:
+ case GG_STATUS_AVAIL_DESCR:
+ return true;
+ }
+ return false;
+}
+
+Kopete::OnlineStatus
+GaduProtocol::convertStatus( uint status ) const
+{
+ switch( status ) {
+ case GG_STATUS_NOT_AVAIL:
+ return gaduStatusOffline_;
+ case GG_STATUS_NOT_AVAIL_DESCR:
+ return gaduStatusOfflineDescr_;
+ case GG_STATUS_BUSY:
+ return gaduStatusBusy_;
+ case GG_STATUS_BUSY_DESCR:
+ return gaduStatusBusyDescr_;
+ case GG_STATUS_INVISIBLE:
+ return gaduStatusInvisible_;
+ case GG_STATUS_INVISIBLE_DESCR:
+ return gaduStatusInvisibleDescr_;
+ case GG_STATUS_AVAIL:
+ return gaduStatusAvail_;
+ case GG_STATUS_AVAIL_DESCR:
+ return gaduStatusAvailDescr_;
+ case GG_STATUS_CONNECTING:
+ return gaduConnecting_;
+ case GG_STATUS_BLOCKED:
+ return gaduStatusBlocked_;
+ default:
+ return gaduStatusOffline_;
+ }
+}
+
+Kopete::Account*
+GaduProtocol::createNewAccount( const QString& accountId )
+{
+ defaultAccount_ = new GaduAccount( this, accountId );
+ return defaultAccount_ ;
+}
+
+KopeteEditAccountWidget*
+GaduProtocol::createEditAccountWidget( Kopete::Account* account, QWidget* parent )
+{
+ return( new GaduEditAccount( this, account, parent ) );
+}
+
+#include "gaduprotocol.moc"
diff --git a/kopete/protocols/gadu/gaduprotocol.h b/kopete/protocols/gadu/gaduprotocol.h
new file mode 100644
index 00000000..079763c4
--- /dev/null
+++ b/kopete/protocols/gadu/gaduprotocol.h
@@ -0,0 +1,108 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002-2003 Zack Rusin <zack@kde.org>
+//
+// gaduprotocol.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUPROTOCOL_H
+#define GADUPROTOCOL_H
+
+#include <qmap.h>
+
+#include "kopeteprotocol.h"
+#include "kopeteonlinestatus.h"
+#include "kopetecontactproperty.h"
+
+#include "gaducommands.h"
+
+class KAction;
+class KActionMenu;
+
+class QWidget;
+class QString;
+
+namespace Kopete { class Contact; }
+namespace Kopete { class MetaContact; }
+
+class GaduSession;
+class GaduContact;
+class GaduAccount;
+class GaduPreferences;
+
+#define GG_STATUS_CONNECTING 0x0100
+
+class GaduProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ GaduProtocol( QObject* parent, const char* name, const QStringList& str);
+ ~GaduProtocol();
+
+ static GaduProtocol *protocol();
+
+ // Plugin reimplementation
+ // {
+ AddContactPage* createAddContactWidget( QWidget* parent, Kopete::Account* account );
+ Kopete::Account* createNewAccount( const QString& accountId );
+ KopeteEditAccountWidget *createEditAccountWidget( Kopete::Account* account, QWidget* parent );
+ bool canSendOffline() const { return true; }
+
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact* metaContact,
+ const QMap<QString, QString>& serializedData,
+ const QMap<QString, QString>& addressBookData );
+ // }
+ //!Plugin reimplementation
+
+ Kopete::OnlineStatus convertStatus( uint ) const;
+ bool statusWithDescription( uint status );
+
+ uint statusToWithDescription( Kopete::OnlineStatus status );
+ uint statusToWithoutDescription( Kopete::OnlineStatus status );
+
+ const Kopete::ContactPropertyTmpl propFirstName;
+ const Kopete::ContactPropertyTmpl propLastName;
+ const Kopete::ContactPropertyTmpl propEmail;
+ const Kopete::ContactPropertyTmpl propAwayMessage;
+ const Kopete::ContactPropertyTmpl propPhoneNr;
+ //const Kopete::ContactPropertyTmpl propIgnore;
+
+private slots:
+ void settingsChanged();
+
+private:
+ static GaduProtocol* protocolStatic_;
+ GaduAccount* defaultAccount_;
+ //GaduPreferences* prefs_;
+
+ const Kopete::OnlineStatus gaduStatusBlocked_;
+ const Kopete::OnlineStatus gaduStatusOffline_;
+ const Kopete::OnlineStatus gaduStatusOfflineDescr_;
+ const Kopete::OnlineStatus gaduStatusBusy_;
+ const Kopete::OnlineStatus gaduStatusBusyDescr_;
+ const Kopete::OnlineStatus gaduStatusInvisible_;
+ const Kopete::OnlineStatus gaduStatusInvisibleDescr_;
+ const Kopete::OnlineStatus gaduStatusAvail_;
+ const Kopete::OnlineStatus gaduStatusAvailDescr_;
+ const Kopete::OnlineStatus gaduConnecting_;
+
+};
+
+
+#endif
diff --git a/kopete/protocols/gadu/gadupubdir.cpp b/kopete/protocols/gadu/gadupubdir.cpp
new file mode 100644
index 00000000..8b722894
--- /dev/null
+++ b/kopete/protocols/gadu/gadupubdir.cpp
@@ -0,0 +1,344 @@
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadupubdir.cpp
+// Gadu-Gadu Public directory contains people data, using it you can search friends
+// different criteria
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+#include "gadupubdir.h"
+#include "gadueditcontact.h"
+#include "gaducontactlist.h"
+#include "gaduaccount.h"
+#include "gaduprotocol.h"
+
+#include <qwidgetstack.h>
+#include <qlistview.h>
+#include <qptrlist.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+#include <qcheckbox.h>
+
+#include <kcombobox.h>
+#include <krestrictedline.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <klocale.h>
+
+GaduPublicDir::GaduPublicDir( GaduAccount* account, QWidget* parent, const char* name )
+: KDialogBase( parent, name, false, QString::null, User1|User2|User3|Cancel, User2 )
+{
+ mAccount = account;
+ createWidget();
+ initConnections();
+
+ show();
+}
+
+GaduPublicDir::GaduPublicDir( GaduAccount* account, int searchFor, QWidget* parent, const char* name )
+: KDialogBase( parent, name, false, QString::null, User1|User2|User3|Cancel, User2 )
+{
+ ResLine rs;
+
+ mAccount = account;
+ createWidget();
+ initConnections();
+
+ kdDebug( 14100 ) << "search for Uin: " << searchFor << endl;
+
+ mMainWidget->listFound->clear();
+ show();
+
+ if ( searchFor == 0 ) {
+ return;
+ }
+
+ mMainWidget->pubsearch->raiseWidget( 1 );
+ mMainWidget->radioByUin->setChecked( true );
+
+ setButtonText( User2, i18n( "Search &More..." ) );
+ showButton( User3, true );
+ showButton( User1, true );
+ enableButton( User3, false );
+ enableButton( User2, false );
+
+ // now it is time to switch to Right Page(tm)
+ rs.uin = searchFor;
+
+ fName = fSurname = fNick = fCity = QString::null;
+ fUin = searchFor;
+ fGender = fAgeFrom = fAgeTo = 0;
+ fOnlyOnline = false;
+
+ mAccount->pubDirSearch( rs, fAgeFrom, fAgeTo, fOnlyOnline );
+
+}
+
+void
+GaduPublicDir::createWidget()
+{
+ setCaption( i18n( "Gadu-Gadu Public Directory" ) );
+
+ mMainWidget = new GaduPublicDirectory( this );
+ setMainWidget( mMainWidget );
+
+ mMainWidget->UIN->setValidChars( "1234567890" );
+
+ setButtonText( User1, i18n( "&New Search" ) );
+ setButtonText( User2, i18n( "S&earch" ) );
+ setButtonText( User3, i18n( "&Add User..." ) );
+ setButtonText( Cancel, i18n( "&Close" ) );
+
+ showButton( User1, false );
+ showButton( User3, false );
+ enableButton( User2, false );
+
+ mMainWidget->radioByData->setChecked( true );
+
+ mAccount->pubDirSearchClose();
+
+}
+
+void
+GaduPublicDir::slotAddContact()
+{
+ GaduContactsList::ContactLine* cl = new GaduContactsList::ContactLine;
+ QListViewItem* item = mMainWidget->listFound->currentItem();
+
+ cl->ignored = false;
+ cl->firstname = item->text( 1 );
+ cl->uin = item->text( 5 );
+ cl->nickname = item->text( 2 );
+
+ cl->surname = fSurname;
+
+// GaduEditContact *ed =
+ new GaduEditContact( mAccount, cl, this );
+}
+
+void
+GaduPublicDir::slotListSelected( )
+{
+ QListViewItem* item = mMainWidget->listFound->currentItem();
+ if ( item ) {
+ enableButton( User3, true );
+ }
+ else {
+ enableButton( User3, false );
+ }
+}
+
+void
+GaduPublicDir::initConnections()
+{
+ connect( this, SIGNAL( user2Clicked() ), SLOT( slotSearch() ) );
+ connect( this, SIGNAL( user1Clicked() ), SLOT( slotNewSearch() ) );
+ connect( this, SIGNAL( user3Clicked() ), SLOT( slotAddContact() ) );
+
+ connect( mAccount, SIGNAL( pubDirSearchResult( const SearchResult&, unsigned int ) ),
+ SLOT( slotSearchResult( const SearchResult&, unsigned int ) ) );
+
+ connect( mMainWidget->nameS, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->surname, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->nick, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->UIN, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->cityS, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->gender, SIGNAL( activated( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->ageFrom, SIGNAL( valueChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->ageTo, SIGNAL( valueChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( mMainWidget->radioByData, SIGNAL( toggled( bool ) ), SLOT( inputChanged( bool ) ) );
+
+ connect( mMainWidget->listFound, SIGNAL( selectionChanged () ), SLOT( slotListSelected() ) );
+
+}
+
+void
+GaduPublicDir::inputChanged( bool )
+{
+ inputChanged( QString::null );
+}
+
+void
+GaduPublicDir::inputChanged( const QString& )
+{
+ if ( validateData() == false ) {
+ enableButton( User2, false );
+ }
+ else {
+ enableButton( User2, true );
+ }
+}
+
+void
+GaduPublicDir::getData()
+{
+ fName = mMainWidget->nameS->text();
+ fSurname = mMainWidget->surname->text();
+ fNick = mMainWidget->nick->text();
+ fUin = mMainWidget->UIN->text().toInt();
+ fGender = mMainWidget->gender->currentItem();
+ fOnlyOnline = mMainWidget->onlyOnline->isChecked();
+ fAgeFrom = mMainWidget->ageFrom->value();
+ fAgeTo = mMainWidget->ageTo->value();
+ fCity = mMainWidget->cityS->text();
+}
+
+// return true if not empty
+#define CHECK_STRING(A) { if ( !A.isEmpty() ) { return true; } }
+#define CHECK_INT(A) { if ( A ) { return true; } }
+
+bool
+GaduPublicDir::validateData()
+{
+ getData();
+
+ if ( mMainWidget->radioByData->isChecked() ) {
+ CHECK_STRING( fCity );
+ CHECK_STRING( fName );
+ CHECK_STRING( fSurname );
+ CHECK_STRING( fNick );
+ CHECK_INT( fGender );
+ CHECK_INT( fAgeFrom );
+ CHECK_INT( fAgeTo );
+ }
+ else {
+ fSurname = QString::null;
+ CHECK_INT( fUin );
+ }
+ return false;
+}
+
+// Move to GaduProtocol someday
+QPixmap
+GaduPublicDir::iconForStatus( uint status )
+{
+ QPixmap n;
+
+ if ( GaduProtocol::protocol() ) {
+ return GaduProtocol::protocol()->convertStatus( status ).protocolIcon();
+ }
+ return n;
+}
+
+void
+GaduPublicDir::slotSearchResult( const SearchResult& result, unsigned int )
+{
+ QListView* list = mMainWidget->listFound;
+
+ kdDebug(14100) << "searchResults(" << result.count() <<")" << endl;
+
+ QListViewItem* sl;
+
+ SearchResult::const_iterator r;
+
+ for ( r = result.begin(); r != result.end() ; ++r ){
+ kdDebug(14100) << "adding" << (*r).uin << endl;
+ sl= new QListViewItem(
+ list, QString::fromAscii(""),
+ (*r).firstname,
+ (*r).nickname,
+ (*r).age,
+ (*r).city,
+ QString::number( (*r).uin ).ascii()
+ );
+ sl->setPixmap( 0, iconForStatus( (*r).status ) );
+ }
+
+ // if not found anything, obviously we don't want to search for more
+ // if we are looking just for one UIN, don't allow search more - it is pointless
+
+ if ( result.count() && fUin==0 ) {
+ enableButton( User2, true );
+ }
+
+ enableButton( User1, true );
+ enableButton( User3, false );
+ mMainWidget->pubsearch->setDisabled( false );
+
+}
+
+void
+GaduPublicDir::slotNewSearch()
+{
+ mMainWidget->pubsearch->raiseWidget( 0 );
+
+ setButtonText( User2, i18n( "S&earch" ) );
+
+ showButton( User1, false );
+ showButton( User3, false );
+ enableButton( User2, false );
+ inputChanged( QString::null );
+ mAccount->pubDirSearchClose();
+}
+
+void
+GaduPublicDir::slotSearch()
+{
+
+ mMainWidget->listFound->clear();
+ QString empty;
+
+ // search more, or search ?
+ if ( mMainWidget->pubsearch->id( mMainWidget->pubsearch->visibleWidget() ) == 0 ) {
+ kdDebug(14100) << "start search... " << endl;
+ getData();
+
+ // validate data
+ if ( validateData() == false ) {
+ return;
+ }
+
+ // go on
+ mMainWidget->pubsearch->raiseWidget( 1 );
+
+ }
+ else{
+ kdDebug(14100) << "search more... " << endl;
+ // Search for more
+ }
+ mMainWidget->pubsearch->setDisabled( true );
+ setButtonText( User2, i18n( "Search &More..." ) );
+ showButton( User3, true );
+ showButton( User1, true );
+ enableButton( User3, false );
+ enableButton( User2, false );
+
+ ResLine rs;
+ rs.firstname = fName;
+ rs.surname = fSurname;
+ rs.nickname = fNick;
+ rs.uin = fUin;
+ rs.city = fCity;
+
+ if ( fGender == 1 ) {
+ rs.gender = GG_PUBDIR50_GENDER_MALE;
+ }
+ if ( fGender == 2 ) {
+ rs.gender = GG_PUBDIR50_GENDER_FEMALE;
+ }
+
+
+ if ( mMainWidget->radioByData->isChecked() ) {
+ mAccount->pubDirSearch( rs, fAgeFrom, fAgeTo, fOnlyOnline );
+ }
+ else {
+ mAccount->pubDirSearch( rs, 0, 0, fOnlyOnline );
+ }
+}
+
+#include "gadupubdir.moc"
diff --git a/kopete/protocols/gadu/gadupubdir.h b/kopete/protocols/gadu/gadupubdir.h
new file mode 100644
index 00000000..11c03981
--- /dev/null
+++ b/kopete/protocols/gadu/gadupubdir.h
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadupubdir.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+#ifndef GADUPUBDIR_H
+#define GADUPUBDIR_H
+
+#include "gadusearch.h"
+#include "gadusession.h"
+
+#include <kdebug.h>
+#include <kdialogbase.h>
+
+class GaduAccount;
+class GaduProtocol;
+class GaduContact;
+class GaduAccount;
+class GaduPublicDirectory;
+class QListViewItem;
+class GaduContact;
+
+class GaduPublicDir : public KDialogBase
+{
+Q_OBJECT
+
+public:
+ GaduPublicDir( GaduAccount* , QWidget *parent = 0, const char* name = "GaduPublicDir" );
+ GaduPublicDir( GaduAccount* , int searchFor, QWidget* parent = 0, const char* name = "GaduPublicDir" );
+ QPixmap iconForStatus( uint status );
+
+private slots:
+ void slotSearch();
+ void slotNewSearch();
+ void slotSearchResult( const SearchResult& result, unsigned int seq );
+ void slotAddContact();
+ void inputChanged( const QString& );
+ void inputChanged( bool );
+ void slotListSelected();
+
+
+private:
+ void getData();
+ bool validateData();
+ void createWidget();
+ void initConnections();
+
+ GaduProtocol* p;
+ GaduAccount* mAccount;
+ GaduContact* mContact;
+ GaduPublicDirectory* mMainWidget;
+
+// form data
+ QString fName;
+ QString fSurname;
+ QString fNick;
+ QString fCity;
+ int fUin;
+ int fGender;
+ bool fOnlyOnline;
+ int fAgeFrom;
+ int fAgeTo;
+};
+#endif
diff --git a/kopete/protocols/gadu/gaduregisteraccount.cpp b/kopete/protocols/gadu/gaduregisteraccount.cpp
new file mode 100644
index 00000000..e48ee551
--- /dev/null
+++ b/kopete/protocols/gadu/gaduregisteraccount.cpp
@@ -0,0 +1,212 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gaduregisteraccount.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+
+#include "gaduregisteraccountui.h"
+#include "gaduregisteraccount.h"
+#include "gaducommands.h"
+
+GaduRegisterAccount::GaduRegisterAccount( QWidget* parent, const char* name )
+: KDialogBase( parent, name, true, i18n( "Register New Account" ), KDialogBase::User1 | KDialogBase::Ok, KDialogBase::User1, true )
+{
+ ui = new GaduRegisterAccountUI( this );
+ setMainWidget( ui );
+
+ ui->valueVerificationSequence->setDisabled( true );
+ setButtonText( User1, i18n( "&Register" ) );
+ setButtonText( Ok, i18n( "&Cancel" ) );
+ enableButton( User1, false );
+
+ cRegister = new RegisterCommand( this );
+
+ emailRegexp = new QRegExp( "[\\w\\d.+_-]{1,}@[\\w\\d.-]{1,}" );
+ hintPixmap = KGlobal::iconLoader()->loadIcon ( "gadu_protocol", KIcon::Small );
+
+ connect( this, SIGNAL( user1Clicked() ), SLOT( doRegister() ) );
+ connect( this, SIGNAL( okClicked() ), SLOT( slotClose() ) );
+
+ connect( ui->valueEmailAddress, SIGNAL( textChanged( const QString &) ), SLOT( inputChanged( const QString & ) ) );
+ connect( ui->valuePassword, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) );
+ connect( ui->valuePasswordVerify, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) );
+ connect( ui->valueVerificationSequence, SIGNAL( textChanged( const QString & ) ), SLOT( inputChanged( const QString & ) ) );
+
+ connect( cRegister, SIGNAL( tokenRecieved( QPixmap, QString ) ), SLOT( displayToken( QPixmap, QString ) ) );
+ connect( cRegister, SIGNAL( done( const QString&, const QString& ) ), SLOT( registrationDone( const QString&, const QString& ) ) );
+ connect( cRegister, SIGNAL( error( const QString&, const QString& ) ), SLOT( registrationError( const QString&, const QString& ) ) );
+ connect( cRegister, SIGNAL( operationStatus( const QString ) ), SLOT( updateStatus( const QString ) ) );
+
+ updateStatus( i18n( "Retrieving token" ) );
+ cRegister->requestToken();
+
+ show();
+}
+
+void
+GaduRegisterAccount::doRegister( )
+{
+ cRegister->setUserinfo( ui->valueEmailAddress->text(), ui->valuePassword->text(), ui->valueVerificationSequence->text() );
+ cRegister->execute();
+ enableButton( User1, false );
+}
+
+void
+GaduRegisterAccount::validateInput()
+{
+ int valid = true;
+ int passwordHighlight = false;
+
+ if ( !emailRegexp->exactMatch( ui->valueEmailAddress->text() ) )
+ {
+ updateStatus( i18n( "Please enter a valid E-Mail Address." ) );
+ ui->pixmapEmailAddress->setPixmap ( hintPixmap );
+ valid = false;
+ }
+ else {
+ ui->pixmapEmailAddress->setText ( "" );
+ }
+
+ if ( valid && ( ( ui->valuePassword->text().isEmpty() ) || ( ui->valuePasswordVerify->text().isEmpty() ) ) )
+ {
+ updateStatus( i18n( "Please enter the same password twice." ) );
+ valid = false;
+ passwordHighlight = true;
+ }
+
+ if ( valid && ( ui->valuePassword->text() != ui->valuePasswordVerify->text() ) )
+ {
+ updateStatus( i18n( "Password entries do not match." ) );
+ valid = false;
+ passwordHighlight = true;
+ }
+
+ if ( valid && ( ui->valueVerificationSequence->text().isEmpty() ) )
+ {
+ updateStatus( i18n( "Please enter the verification sequence." ) );
+ ui->pixmapVerificationSequence->setPixmap ( hintPixmap );
+ valid = false;
+ }
+ else {
+ ui->pixmapVerificationSequence->setText ( "" );
+ }
+
+ if ( passwordHighlight == true )
+ {
+ ui->pixmapPassword->setPixmap ( hintPixmap );
+ ui->pixmapPasswordVerify->setPixmap ( hintPixmap );
+ }
+ else {
+ ui->pixmapPassword->setText ( "" );
+ ui->pixmapPasswordVerify->setText ( "" );
+ }
+
+ if ( valid )
+ {
+ // clear status message if we have valid data
+ updateStatus( i18n( "" ) );
+ }
+
+ enableButton( User1, valid );
+}
+
+void
+GaduRegisterAccount::inputChanged( const QString & )
+{
+ validateInput();
+}
+
+void
+GaduRegisterAccount::registrationDone( const QString& /*title*/, const QString& /*what */ )
+{
+ ui->valueEmailAddress->setDisabled( true );
+ ui->valuePassword->setDisabled( true );
+ ui->valuePasswordVerify->setDisabled( true );
+ ui->valueVerificationSequence->setDisabled( true );
+ ui->labelEmailAddress->setDisabled( true );
+ ui->labelPassword->setDisabled( true );
+ ui->labelPasswordVerify->setDisabled( true );
+ ui->labelVerificationSequence->setDisabled( true );
+ ui->labelInstructions->setDisabled( true );
+ emit registeredNumber( cRegister->newUin(), ui->valuePassword->text() );
+ updateStatus( i18n( "Account created; your new UIN is %1." ).arg(QString::number( cRegister->newUin() ) ) );
+ enableButton( User1, false );
+ setButtonText( Ok, i18n( "&Close" ) );
+}
+
+void
+GaduRegisterAccount::registrationError( const QString& title, const QString& what )
+{
+ updateStatus( i18n( "Registration failed: %1" ).arg( what ) );
+ KMessageBox::sorry( this, "Registration was unsucessful, please try again.", title );
+
+ disconnect( this, SLOT( displayToken( QPixmap, QString ) ) );
+ disconnect( this, SLOT( registrationDone( const QString&, const QString& ) ) );
+ disconnect( this, SLOT( registrationError( const QString&, const QString& ) ) );
+ disconnect( this, SLOT( updateStatus( const QString ) ) );
+
+ ui->valueVerificationSequence->setDisabled( true );
+ ui->valueVerificationSequence->setText( "" );
+ enableButton( User1, false );
+ updateStatus( "" );
+
+ // emit UIN 0, to enable 'register new account' button again in dialog below
+ emit registeredNumber( 0, QString( "" ) );
+
+ deleteLater();
+}
+
+void
+GaduRegisterAccount::displayToken( QPixmap image, QString /*tokenId */ )
+{
+ ui->valueVerificationSequence->setDisabled( false );
+ ui->pixmapToken->setPixmap( image );
+ validateInput();
+}
+
+void
+GaduRegisterAccount::updateStatus( const QString status )
+{
+ ui->labelStatusMessage->setAlignment( AlignCenter );
+ ui->labelStatusMessage->setText( status );
+}
+
+void
+GaduRegisterAccount::slotClose()
+{
+ deleteLater();
+}
+
+GaduRegisterAccount::~GaduRegisterAccount( )
+{
+ kdDebug( 14100 ) << " register Cancel " << endl;
+}
+
+#include "gaduregisteraccount.moc"
diff --git a/kopete/protocols/gadu/gaduregisteraccount.h b/kopete/protocols/gadu/gaduregisteraccount.h
new file mode 100644
index 00000000..339e4c36
--- /dev/null
+++ b/kopete/protocols/gadu/gaduregisteraccount.h
@@ -0,0 +1,62 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gaduregisteraccount.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUREGISTERACCOUNT_H
+#define GADUREGISTERACCOUNT_H
+
+#include <kdialogbase.h>
+
+class QString;
+class QPixmap;
+class RegisterCommand;
+class QRegExp;
+class GaduRegisterAccountUI;
+
+class GaduRegisterAccount : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ GaduRegisterAccount( QWidget* , const char* );
+ ~GaduRegisterAccount( );
+
+signals:
+ void registeredNumber( unsigned int, QString );
+
+protected slots:
+ void slotClose();
+ void displayToken( QPixmap, QString );
+ void registrationError( const QString&, const QString& );
+ void registrationDone( const QString&, const QString& );
+ void inputChanged( const QString & );
+ void doRegister();
+ void updateStatus( const QString status );
+
+private:
+ void validateInput();
+
+ GaduRegisterAccountUI* ui;
+ RegisterCommand* cRegister;
+ QRegExp* emailRegexp;
+ QPixmap hintPixmap;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/gadurichtextformat.cpp b/kopete/protocols/gadu/gadurichtextformat.cpp
new file mode 100644
index 00000000..a93b95fd
--- /dev/null
+++ b/kopete/protocols/gadu/gadurichtextformat.cpp
@@ -0,0 +1,291 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadurichtextformat.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#include <knotifyclient.h>
+#include <kdebug.h>
+#include <kopetemessage.h>
+
+#include "gadurichtextformat.h"
+#include "gadusession.h"
+
+#include <qstring.h>
+#include <qregexp.h>
+
+GaduRichTextFormat::GaduRichTextFormat()
+{
+}
+
+GaduRichTextFormat::~GaduRichTextFormat()
+{
+}
+
+QString
+GaduRichTextFormat::convertToHtml( const QString& msg, unsigned int formats, void* formatStructure)
+{
+ QString tmp, nb;
+ gg_msg_richtext_format *format;
+ char *pointer = (char*) formatStructure;
+
+ unsigned int i,j;
+ int r, g, b;
+ r = g = b = 0;
+ bool opened = false;
+
+ if ( formatStructure == NULL || formats == 0 ) {
+ tmp = msg;
+ escapeBody( tmp );
+ return tmp;
+ }
+
+ for ( i = 0, j = 0 ; i < formats ; ) {
+ format = (gg_msg_richtext_format*) pointer;
+ unsigned int position = format->position;
+ char font = format->font;
+ QString style;
+
+ if ( position < j || position > msg.length() ) {
+ break;
+ }
+
+ if ( font & GG_FONT_IMAGE ) {
+ i += sizeof( gg_msg_richtext_image );
+ pointer += sizeof( gg_msg_richtext_image );
+ tmp += "<b>[this should be a picture, not yet implemented]</b>";
+ }
+ else {
+ nb = msg.mid( j, position - j );
+ tmp += escapeBody( nb );
+
+ j = position;
+
+ // add message bit between formating
+ if ( opened ) {
+ tmp += formatClosingTag("span");
+ opened = false;
+ }
+ // set font attributes
+ if ( font & GG_FONT_BOLD ) {
+ style += (" font-weight:bold; ");
+ }
+ if ( font & GG_FONT_ITALIC ) {
+ style += (" font-style:italic; ");
+ }
+ if ( font & GG_FONT_UNDERLINE ) {
+ style += (" text-decoration:underline; ");
+ }
+ // add color
+ if ( font & GG_FONT_COLOR ) {
+ pointer += sizeof( gg_msg_richtext_format );
+ i += sizeof( gg_msg_richtext_format );
+ gg_msg_richtext_color *color = (gg_msg_richtext_color*)( pointer );
+ r = (int)color->red;
+ g = (int)color->green;
+ b = (int)color->blue;
+ }
+ style += QString(" color: rgb( %1, %2, %3 ); ").arg( r ).arg( g ).arg( b );
+
+ tmp += formatOpeningTag( QString::fromLatin1("span"), QString::fromLatin1("style=\"%1\"").arg( style ) );
+ opened = true;
+
+ }
+
+ // advance to next structure in row
+ pointer += sizeof( gg_msg_richtext_format );
+ i += sizeof( gg_msg_richtext_format );
+ }
+
+ nb = msg.mid( j, msg.length() );
+ tmp += escapeBody( nb );
+ if ( opened ) {
+ tmp += formatClosingTag("span");
+ }
+
+ return tmp;
+}
+
+QString
+GaduRichTextFormat::formatOpeningTag( const QString& tag, const QString& attributes )
+{
+ QString res = "<" + tag;
+ if(!attributes.isEmpty())
+ res.append(" " + attributes);
+ return res + ">";
+}
+
+QString
+GaduRichTextFormat::formatClosingTag( const QString& tag )
+{
+ return "</" + tag + ">";
+}
+
+// the initial idea stolen from IRC plugin
+KGaduMessage*
+GaduRichTextFormat::convertToGaduMessage( const Kopete::Message& message )
+{
+ QString htmlString = message.escapedBody();
+ KGaduMessage* output = new KGaduMessage;
+ rtcs.blue = rtcs.green = rtcs.red = 0;
+ color = QColor();
+ int position = 0;
+
+ rtf.resize( sizeof( gg_msg_richtext) );
+ output->rtf.resize(0);
+
+ // test first if there is any HTML formating in it
+ if( htmlString.find( QString::fromLatin1("</span") ) > -1 ) {
+ QRegExp findTags( QString::fromLatin1("<span style=\"(.*)\">(.*)</span>") );
+ findTags.setMinimal( true );
+ int pos = 0;
+ int lastpos = 0;
+
+ while ( pos >= 0 ){
+ pos = findTags.search( htmlString );
+ rtfs.font = 0;
+ if ( pos != lastpos ) {
+ QString tmp;
+ if ( pos < 0 ) {
+ tmp = htmlString.mid( lastpos );
+ }
+ else {
+ tmp = htmlString.mid( lastpos, pos - lastpos );
+ }
+ if ( !tmp.isEmpty() ) {
+ color.setRgb( 0, 0, 0 );
+ if ( insertRtf( position ) == false ) {
+ delete output;
+ return NULL;
+ }
+ tmp = unescapeGaduMessage( tmp );
+ output->message += tmp;
+ position += tmp.length();
+ }
+ }
+
+ if ( pos > -1 ) {
+ QString styleHTML = findTags.cap(1);
+ QString replacement = findTags.cap(2);
+ QStringList styleAttrs = QStringList::split( ';', styleHTML );
+ rtfs.font = 0;
+
+ lastpos = pos + replacement.length();
+
+ for( QStringList::Iterator attrPair = styleAttrs.begin(); attrPair != styleAttrs.end(); ++attrPair ) {
+ QString attribute = (*attrPair).section(':',0,0);
+ QString value = (*attrPair).section(':',1);
+ parseAttributes( attribute, value );
+ }
+
+ if ( insertRtf( position ) == false ) {
+ delete output;
+ return NULL;
+ }
+
+ QString rep = QString("<span style=\"%1\">%2</span>" ).arg( styleHTML ).arg( replacement );
+ htmlString.replace( findTags.pos( 0 ), rep.length(), replacement );
+
+ replacement = unescapeGaduMessage( replacement );
+ output->message += replacement;
+ position += replacement.length();
+ }
+
+ }
+ output->rtf = rtf;
+ // this is sick, but that's the way libgadu is designed
+ // here I am adding network header !, should sit in libgadu IMO
+ header = (gg_msg_richtext*) output->rtf.data();
+ header->length = output->rtf.size() - sizeof( gg_msg_richtext );
+ header->flag = 2;
+ }
+ else {
+ output->message = message.escapedBody();
+ output->message = unescapeGaduMessage( output->message );
+ }
+
+ return output;
+
+}
+
+void
+GaduRichTextFormat::parseAttributes( const QString attribute, const QString value )
+{
+ if( attribute == QString::fromLatin1("color") ) {
+ color.setNamedColor( value );
+ }
+ if( attribute == QString::fromLatin1("font-weight") && value == QString::fromLatin1("600") ) {
+ rtfs.font |= GG_FONT_BOLD;
+ }
+ if( attribute == QString::fromLatin1("text-decoration") && value == QString::fromLatin1("underline") ) {
+ rtfs.font |= GG_FONT_UNDERLINE ;
+ }
+ if( attribute == QString::fromLatin1("font-style") && value == QString::fromLatin1("italic") ) {
+ rtfs.font |= GG_FONT_ITALIC;
+ }
+}
+
+QString
+GaduRichTextFormat::unescapeGaduMessage( QString& ns )
+{
+ QString s;
+ s = Kopete::Message::unescape( ns );
+ s.replace( QString::fromAscii( "\n" ), QString::fromAscii( "\r\n" ) );
+ return s;
+}
+
+bool
+GaduRichTextFormat::insertRtf( uint position)
+{
+ if ( color != QColor( rtcs.red, rtcs.green, rtcs.blue ) ) {
+ rtcs.red = color.red();
+ rtcs.green = color.green();
+ rtcs.blue = color.blue();
+ rtfs.font |= GG_FONT_COLOR;
+ }
+
+ if ( rtfs.font ) {
+ // append font description
+ rtfs.position = position;
+ uint csize = rtf.size();
+ if ( rtf.resize( csize + sizeof( gg_msg_richtext_format ) ) == FALSE ) {
+ return false;
+ };
+ memcpy( rtf.data() + csize, &rtfs, sizeof( rtfs ) );
+ // append color description, if color has changed
+ if ( rtfs.font & GG_FONT_COLOR ) {
+ csize = rtf.size();
+ if ( rtf.resize( csize + sizeof( gg_msg_richtext_color ) ) == FALSE ) {
+ return false;
+ };
+ memcpy( rtf.data() + csize, &rtcs, sizeof( rtcs ) );
+ }
+ }
+ return true;
+}
+
+QString
+GaduRichTextFormat::escapeBody( QString& input )
+{
+ input.replace( '<', QString::fromLatin1("&lt;") );
+ input.replace( '>', QString::fromLatin1("&gt;") );
+ input.replace( '\n', QString::fromLatin1( "<br />" ) );
+ input.replace( '\t', QString::fromLatin1( "&nbsp;&nbsp;&nbsp;&nbsp;" ) );
+ input.replace( QRegExp( QString::fromLatin1( "\\s\\s" ) ), QString::fromLatin1( " &nbsp;" ) );
+ return input;
+}
diff --git a/kopete/protocols/gadu/gadurichtextformat.h b/kopete/protocols/gadu/gadurichtextformat.h
new file mode 100644
index 00000000..34dfcd37
--- /dev/null
+++ b/kopete/protocols/gadu/gadurichtextformat.h
@@ -0,0 +1,53 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+//
+// gadurichtextformat.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+
+#ifndef GADURTF_H
+#define GADURTF_H
+
+#include <libgadu.h>
+
+class Qstring;
+namespace Kopete { class Message; }
+class KGaduMessage;
+
+class GaduRichTextFormat {
+public:
+ GaduRichTextFormat();
+ ~GaduRichTextFormat();
+ QString convertToHtml( const QString&, unsigned int, void* );
+ KGaduMessage* convertToGaduMessage( const Kopete::Message& );
+
+private:
+ QString formatOpeningTag( const QString& , const QString& = QString::null );
+ QString formatClosingTag( const QString& );
+ bool insertRtf( uint );
+ QString unescapeGaduMessage( QString& );
+ void parseAttributes( const QString, const QString );
+ QString escapeBody( QString& );
+ QColor color;
+ gg_msg_richtext_format rtfs;
+ gg_msg_richtext_color rtcs;
+ gg_msg_richtext* header;
+ QByteArray rtf;
+
+};
+#endif
diff --git a/kopete/protocols/gadu/gadusession.cpp b/kopete/protocols/gadu/gadusession.cpp
new file mode 100644
index 00000000..92f9137d
--- /dev/null
+++ b/kopete/protocols/gadu/gadusession.cpp
@@ -0,0 +1,811 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002 Zack Rusin <zack@kde.org>
+//
+// gadusession.cpp
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+//
+
+#include "ctime"
+
+#include "gadusession.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+#include "kopetemessage.h"
+
+#include <qsocketnotifier.h>
+#include <qtextcodec.h>
+#include <qdatetime.h>
+#include "gadurichtextformat.h"
+
+#include <errno.h>
+#include <string.h>
+#include <netinet/in.h>
+
+GaduSession::GaduSession( QObject* parent, const char* name )
+: QObject( parent, name ), session_( 0 ), searchSeqNr_( 0 )
+{
+ textcodec = QTextCodec::codecForName( "CP1250" );
+ rtf = new GaduRichTextFormat;
+}
+
+GaduSession::~GaduSession()
+{
+ logoff();
+}
+
+bool
+GaduSession::isConnected() const
+{
+ if ( session_ ) {
+ return ( session_->state & GG_STATE_CONNECTED );
+ }
+ return false;
+}
+
+int
+GaduSession::status() const
+{
+ kdDebug(14100)<<"Status = " << session_->status <<", initial = "<< session_->initial_status <<endl;
+ if ( session_ ) {
+ return session_->status & ( ~GG_STATUS_FRIENDS_MASK );
+ }
+ return GG_STATUS_NOT_AVAIL;
+}
+
+void
+GaduSession::login( struct gg_login_params* p )
+{
+ if ( !isConnected() ) {
+
+// turn on in case you have any problems, and you want
+// to report it better. libgadu needs to be recompiled with debug enabled
+// gg_debug_level=GG_DEBUG_MISC|GG_DEBUG_FUNCTION;
+
+ kdDebug(14100) << "Login" << endl;
+
+ if ( !( session_ = gg_login( p ) ) ) {
+ destroySession();
+ kdDebug( 14100 ) << "libgadu internal error " << endl;
+ emit connectionFailed( GG_FAILURE_CONNECTING );
+ return;
+ }
+
+ createNotifiers( true );
+ enableNotifiers( session_->check );
+ searchSeqNr_=0;
+ }
+}
+
+void
+GaduSession::destroyNotifiers()
+{
+ disableNotifiers();
+ if ( read_ ) {
+ delete read_;
+ read_ = NULL;
+ }
+ if ( write_ ) {
+ delete write_;
+ write_ = NULL;
+ }
+}
+
+void
+GaduSession::createNotifiers( bool connect )
+{
+ if ( !session_ ){
+ return;
+ }
+
+ read_ = new QSocketNotifier( session_->fd, QSocketNotifier::Read, this );
+ read_->setEnabled( false );
+
+ write_ = new QSocketNotifier( session_->fd, QSocketNotifier::Write, this );
+ write_->setEnabled( false );
+
+ if ( connect ) {
+ QObject::connect( read_, SIGNAL( activated( int ) ), SLOT( checkDescriptor() ) );
+ QObject::connect( write_, SIGNAL( activated( int ) ), SLOT( checkDescriptor() ) );
+ }
+}
+
+void
+GaduSession::enableNotifiers( int checkWhat )
+{
+ if( (checkWhat & GG_CHECK_READ) && read_ ) {
+ read_->setEnabled( true );
+ }
+ if( (checkWhat & GG_CHECK_WRITE) && write_ ) {
+ write_->setEnabled( true );
+ }
+}
+
+void
+GaduSession::disableNotifiers()
+{
+ if ( read_ ) {
+ read_->setEnabled( false );
+ }
+ if ( write_ ) {
+ write_->setEnabled( false );
+ }
+}
+
+void
+GaduSession::dccRequest( const unsigned int uin )
+{
+ if ( session_ ) {
+ gg_dcc_request( session_, uin );
+ }
+}
+
+void
+GaduSession::login( KGaduLoginParams* loginp )
+{
+ QCString desc = textcodec->fromUnicode( loginp->statusDescr );
+
+ memset( &params_, 0, sizeof(params_) );
+
+ params_.status_descr = (char*)desc.data();
+
+ params_.uin = loginp->uin;
+ params_.password = (char *)( loginp->password.ascii() );
+ params_.status = loginp->status | ( loginp->forFriends ? GG_STATUS_FRIENDS_MASK : 0 );
+ params_.async = 1;
+ params_.tls = loginp->useTls;
+ params_ .server_addr = loginp->server;
+ params_.client_addr = loginp->client_addr;
+ params_.client_port = loginp->client_port;
+
+ kdDebug(14100) << "LOGIN IP: " << loginp->client_addr << endl;
+
+ if ( loginp->useTls ) {
+ params_.server_port = GG_HTTPS_PORT;
+ }
+ else {
+ if ( loginp->server ) {
+ params_.server_port = GG_DEFAULT_PORT;
+ }
+ }
+
+ kdDebug(14100)<<"gadusession::login, server ( " << loginp->server << " ), tls(" << loginp->useTls << ") " <<endl;
+ login( &params_ );
+
+}
+
+void
+GaduSession::destroySession()
+{
+ if ( session_ ) {
+ destroyNotifiers();
+ gg_free_session( session_ );
+ session_ = 0;
+ }
+}
+
+void
+GaduSession::logoff( Kopete::Account::DisconnectReason reason )
+{
+ destroySession();
+ emit disconnect( reason );
+}
+
+int
+GaduSession::notify( uin_t* userlist, int count )
+{
+ if ( isConnected() ) {
+ return gg_notify( session_, userlist, count );
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
+ }
+
+ return 1;
+}
+
+int
+GaduSession::addNotify( uin_t uin )
+{
+ if ( isConnected() ) {
+ return gg_add_notify( session_, uin );
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
+ }
+ return 1;
+}
+
+int
+GaduSession::removeNotify( uin_t uin )
+{
+ if ( isConnected() ) {
+ gg_remove_notify( session_, uin );
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
+ }
+
+ return 1;
+}
+
+int
+GaduSession::sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass )
+{
+ QString sendMsg;
+ QCString cpMsg;
+ KGaduMessage* gadumessage;
+
+ if ( isConnected() ) {
+ gadumessage = rtf->convertToGaduMessage( msg );
+ if ( gadumessage ) {
+ const void* data = (const void*)gadumessage->rtf.data();
+ cpMsg = textcodec->fromUnicode( gadumessage->message );
+ int o;
+ o = gg_send_message_richtext( session_, msgClass, recipient, (const unsigned char *)cpMsg.data(), (const unsigned char*) data, gadumessage->rtf.size() );
+ gadumessage->rtf.resize(0);
+ delete gadumessage;
+ return o;
+ }
+ else {
+ sendMsg = msg.plainBody();
+ sendMsg.replace( QString::fromAscii( "\n" ), QString::fromAscii( "\r\n" ) );
+ cpMsg = textcodec->fromUnicode( sendMsg );
+
+ return gg_send_message( session_, msgClass, recipient, (const unsigned char *)cpMsg.data() );
+ }
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You are not connected to the server.") );
+ }
+
+ return 1;
+}
+
+int
+GaduSession::changeStatus( int status, bool forFriends )
+{
+ kdDebug(14101)<<"## Changing to "<<status<<endl;
+ if ( isConnected() ) {
+ return gg_change_status( session_, status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0) );
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") );
+ }
+
+ return 1;
+}
+
+int
+GaduSession::changeStatusDescription( int status, const QString& descr, bool forFriends )
+{
+ QCString ndescr;
+
+ ndescr= textcodec->fromUnicode(descr);
+
+ if ( isConnected() ) {
+ return gg_change_status_descr( session_,
+ status | ( forFriends ? GG_STATUS_FRIENDS_MASK : 0), ndescr.data() );
+ }
+ else {
+ emit error( i18n("Not Connected"), i18n("You have to be connected to the server to change your status.") );
+ }
+
+ return 1;
+}
+
+int
+GaduSession::ping()
+{
+ if ( isConnected() ) {
+ return gg_ping( session_ );
+ }
+
+ return 1;
+}
+
+void
+GaduSession::pubDirSearchClose()
+{
+ searchSeqNr_=0;
+}
+
+unsigned int
+GaduSession::getPersonalInformation()
+{
+ gg_pubdir50_t searchRequest;
+ unsigned int seqNr;
+
+ if ( isConnected() == false ) {
+ return 0;
+ }
+
+ searchRequest = gg_pubdir50_new( GG_PUBDIR50_READ );
+ if ( !searchRequest ) {
+ return 0;
+ }
+
+ seqNr = gg_pubdir50( session_, searchRequest );
+ gg_pubdir50_free( searchRequest );
+
+ return seqNr;
+}
+
+bool
+GaduSession::publishPersonalInformation( ResLine& d )
+{
+ gg_pubdir50_t r;
+
+ if ( !session_ ) {
+ return 0;
+ }
+
+ r = gg_pubdir50_new( GG_PUBDIR50_WRITE );
+
+ if ( d.firstname.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_FIRSTNAME,
+ (const char *)((const char*)textcodec->fromUnicode( d.firstname ) ) );
+ if ( d.surname.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_LASTNAME,
+ (const char *)((const char*)textcodec->fromUnicode( d.surname ) ) );
+ if ( d.nickname.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_NICKNAME,
+ (const char *)((const char*)textcodec->fromUnicode( d.nickname ) ) );
+ if ( d.age.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_BIRTHYEAR,
+ (const char *)((const char*)textcodec->fromUnicode( d.age ) ) );
+ if ( d.city.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_CITY,
+ (const char *)((const char*)textcodec->fromUnicode( d.city ) ) );
+ if ( d.meiden.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_FAMILYNAME,
+ (const char *)((const char*)textcodec->fromUnicode( d.meiden ) ) );
+ if ( d.orgin.length() )
+ gg_pubdir50_add( r, GG_PUBDIR50_FAMILYCITY,
+ (const char *)((const char*)textcodec->fromUnicode( d.orgin ) ) );
+ if ( d.gender.length() == 1 )
+ gg_pubdir50_add( r, GG_PUBDIR50_GENDER,
+ (const char *)((const char*)textcodec->fromUnicode( d.gender ) ) );
+
+ gg_pubdir50( session_, r );
+
+ gg_pubdir50_free( r );
+
+ return true;
+}
+
+unsigned int
+GaduSession::pubDirSearch( ResLine& query, int ageFrom, int ageTo, bool onlyAlive )
+{
+ QString bufYear;
+ unsigned int reqNr;
+ gg_pubdir50_t searchRequest;
+
+ if ( !session_ ) {
+ return 0;
+ }
+
+ searchRequest = gg_pubdir50_new( GG_PUBDIR50_SEARCH_REQUEST );
+ if ( !searchRequest ) {
+ return 0;
+ }
+
+ if ( query.uin == 0 ) {
+ if (query.firstname.length()) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_FIRSTNAME,
+ (const char*)textcodec->fromUnicode( query.firstname ) );
+ }
+ if ( query.surname.length() ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_LASTNAME,
+ (const char*)textcodec->fromUnicode( query.surname ) );
+ }
+ if ( query.nickname.length() ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_NICKNAME,
+ (const char*)textcodec->fromUnicode( query.nickname ) );
+ }
+ if ( query.city.length() ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_CITY,
+ (const char*)textcodec->fromUnicode( query.city ) );
+ }
+ if ( ageFrom || ageTo ) {
+ QString yearFrom = QString::number( QDate::currentDate().year() - ageFrom );
+ QString yearTo = QString::number( QDate::currentDate().year() - ageTo );
+
+ if ( ageFrom && ageTo ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
+ (const char*)textcodec->fromUnicode( yearFrom + " " + yearTo ) );
+ }
+ if ( ageFrom ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
+ (const char*)textcodec->fromUnicode( yearFrom ) );
+ }
+ else {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_BIRTHYEAR,
+ (const char*)textcodec->fromUnicode( yearTo ) );
+ }
+ }
+
+ if ( query.gender.length() == 1 ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_GENDER,
+ (const char *)((const char*)textcodec->fromUnicode( query.gender ) ) );
+ }
+
+ if ( onlyAlive ) {
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE );
+ }
+ }
+ // otherwise we are looking only for one fellow with this nice UIN
+ else{
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_UIN, QString::number( query.uin ).ascii() );
+ }
+
+ gg_pubdir50_add( searchRequest, GG_PUBDIR50_START, QString::number( searchSeqNr_ ).ascii() );
+ reqNr = gg_pubdir50( session_, searchRequest );
+ gg_pubdir50_free( searchRequest );
+
+ return reqNr;
+}
+
+void
+GaduSession::sendResult( gg_pubdir50_t result )
+{
+ int i, count, age;
+ ResLine resultLine;
+ SearchResult sres;
+
+ count = gg_pubdir50_count( result );
+
+ if ( !count ) {
+ kdDebug(14100) << "there was nothing found in public directory for requested details" << endl;
+ }
+
+ for ( i = 0; i < count; i++ ) {
+ resultLine.uin = QString( gg_pubdir50_get( result, i, GG_PUBDIR50_UIN ) ).toInt();
+ resultLine.firstname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FIRSTNAME ) );
+ resultLine.surname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_LASTNAME ) );
+ resultLine.nickname = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_NICKNAME ) );
+ resultLine.age = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_BIRTHYEAR ) );
+ resultLine.city = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_CITY ) );
+ QString stat = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_STATUS ) );
+ resultLine.orgin = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYCITY ) );
+ resultLine.meiden = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_FAMILYNAME ) );
+ resultLine.gender = textcodec->toUnicode( gg_pubdir50_get( result, i, GG_PUBDIR50_GENDER ) );
+
+ resultLine.status = stat.toInt();
+ age = resultLine.age.toInt();
+ if ( age ) {
+ resultLine.age = QString::number( QDate::currentDate().year() - age );
+ }
+ else {
+ resultLine.age.truncate( 0 );
+ }
+ sres.append( resultLine );
+ kdDebug(14100) << "found line "<< resultLine.uin << " " << resultLine.firstname << endl;
+ }
+
+ searchSeqNr_ = gg_pubdir50_next( result );
+ emit pubDirSearchResult( sres, gg_pubdir50_seq( result ) );
+}
+
+void
+GaduSession::requestContacts()
+{
+ if ( !session_ || session_->state != GG_STATE_CONNECTED ) {
+ kdDebug(14100) <<" you need to be connected to send " << endl;
+ return;
+ }
+
+ if ( gg_userlist_request( session_, GG_USERLIST_GET, NULL ) == -1 ) {
+ kdDebug(14100) <<" userlist export ERROR " << endl;
+ return;
+ }
+ kdDebug( 14100 ) << "Contacts list import..started " << endl;
+}
+
+
+void
+GaduSession::exportContactsOnServer( GaduContactsList* contactsList )
+{
+ QCString plist;
+
+ if ( !session_ || session_->state != GG_STATE_CONNECTED ) {
+ kdDebug( 14100 ) << "you need to connect to export Contacts list " << endl;
+ return;
+ }
+
+ plist = textcodec->fromUnicode( contactsList->asString() );
+ kdDebug(14100) <<"--------------------userlists\n" << plist << endl;
+ kdDebug(14100) << "----------------------------" << endl;
+
+ if ( gg_userlist_request( session_, GG_USERLIST_PUT, plist.data() ) == -1 ) {
+ kdDebug( 14100 ) << "export contact list failed " << endl;
+ return;
+ }
+ kdDebug( 14100 ) << "Contacts list export..started " << endl;
+}
+
+
+void
+GaduSession::handleUserlist( gg_event* event )
+{
+ QString ul;
+ switch( event->event.userlist.type ) {
+ case GG_USERLIST_GET_REPLY:
+ if ( event->event.userlist.reply ) {
+ ul = event->event.userlist.reply;
+ kdDebug( 14100 ) << "Got Contacts list OK " << endl;
+ }
+ else {
+ kdDebug( 14100 ) << "Got Contacts list FAILED/EMPTY " << endl;
+ // FIXME: send failed?
+ }
+ emit userListRecieved( ul );
+ break;
+
+ case GG_USERLIST_PUT_REPLY:
+ kdDebug( 14100 ) << "Contacts list exported OK " << endl;
+ emit userListExported();
+ break;
+
+ }
+}
+
+QString
+GaduSession::stateDescription( int state )
+{
+ switch( state ) {
+ case GG_STATE_IDLE:
+ return i18n( "idle" );
+ case GG_STATE_RESOLVING:
+ return i18n( "resolving host" );
+ case GG_STATE_CONNECTING:
+ return i18n( "connecting" );
+ case GG_STATE_READING_DATA:
+ return i18n( "reading data" );
+ case GG_STATE_ERROR:
+ return i18n( "error" );
+ case GG_STATE_CONNECTING_HUB:
+ return i18n( "connecting to hub" );
+ case GG_STATE_CONNECTING_GG:
+ return i18n( "connecting to server" );
+ case GG_STATE_READING_KEY:
+ return i18n( "retrieving key" );
+ case GG_STATE_READING_REPLY:
+ return i18n( "waiting for reply" );
+ case GG_STATE_CONNECTED:
+ return i18n( "connected" );
+ case GG_STATE_SENDING_QUERY:
+ return i18n( "sending query" );
+ case GG_STATE_READING_HEADER:
+ return i18n( "reading header" );
+ case GG_STATE_PARSING:
+ return i18n( "parse data" );
+ case GG_STATE_DONE:
+ return i18n( "done" );
+ case GG_STATE_TLS_NEGOTIATION:
+ return i18n( "Tls connection negotiation" );
+ default:
+ return i18n( "unknown" );
+ }
+}
+QString
+GaduSession::errorDescription( int err )
+{
+ switch( err ){
+ case GG_ERROR_RESOLVING:
+ return i18n( "Resolving error." );
+ case GG_ERROR_CONNECTING:
+ return i18n( "Connecting error." );
+ case GG_ERROR_READING:
+ return i18n( "Reading error." );
+ case GG_ERROR_WRITING:
+ return i18n( "Writing error." );
+ default:
+ return i18n( "Unknown error number %1." ).arg( QString::number( (unsigned int)err ) );
+ }
+}
+
+QString
+GaduSession::failureDescription( gg_failure_t f )
+{
+ switch( f ) {
+ case GG_FAILURE_RESOLVING:
+ return i18n( "Unable to resolve server address. DNS failure." );
+ case GG_FAILURE_CONNECTING:
+ return i18n( "Unable to connect to server." );
+ case GG_FAILURE_INVALID:
+ return i18n( "Server send incorrect data. Protocol error." );
+ case GG_FAILURE_READING:
+ return i18n( "Problem reading data from server." );
+ case GG_FAILURE_WRITING:
+ return i18n( "Problem sending data to server." );
+ case GG_FAILURE_PASSWORD:
+ return i18n( "Incorrect password." );
+ case GG_FAILURE_404:
+ return QString::fromAscii( "404." );
+ case GG_FAILURE_TLS:
+ return i18n( "Unable to connect over encrypted channel.\nTry to turn off encryption support in Gadu account settings and reconnect." );
+ default:
+ return i18n( "Unknown error number %1." ).arg( QString::number( (unsigned int)f ) );
+ }
+}
+
+void
+GaduSession::notify60( gg_event* event )
+{
+ KGaduNotify* gn = NULL;
+ unsigned int n;
+
+ if ( event->event.notify60[0].uin ) {
+ gn = new KGaduNotify;
+ }
+ else {
+ return;
+ }
+
+ for( n=0 ; event->event.notify60[n].uin ; n++ ) {
+ gn->contact_id = event->event.notify60[n].uin;
+ gn->status = event->event.notify60[n].status;
+ gn->remote_ip.setAddress( ntohl( event->event.notify60[n].remote_ip ) );
+ gn->remote_port = event->event.notify60[n].remote_port;
+ if ( event->event.notify60[n].remote_ip && gn->remote_port > 10 ) {
+ gn->fileCap = true;
+ }
+ else {
+ gn->fileCap = false;
+ }
+ gn->version = event->event.notify60[n].version;
+ gn->image_size = event->event.notify60[n].image_size;
+ gn->description = textcodec->toUnicode( event->event.notify60[n].descr );
+ emit contactStatusChanged( gn );
+ }
+ delete gn;
+}
+
+void
+GaduSession::checkDescriptor()
+{
+ disableNotifiers();
+
+ struct gg_event* event;
+// struct gg_dcc* dccSock;
+ KGaduMessage gaduMessage;
+ KGaduNotify gaduNotify;
+
+ if ( !( event = gg_watch_fd( session_ ) ) ) {
+ kdDebug(14100)<<"Connection was broken for some reason"<<endl;
+ destroyNotifiers();
+ logoff( Kopete::Account::ConnectionReset );
+ return;
+ }
+
+ // FD changed, recreate socket notifiers
+ if ( session_->state == GG_STATE_CONNECTING_HUB || session_->state == GG_STATE_CONNECTING_GG ) {
+ kdDebug(14100)<<"recreating notifiers"<<endl;
+ destroyNotifiers();
+ createNotifiers( true );
+ }
+
+ switch( event->type ) {
+ case GG_EVENT_MSG:
+ kdDebug(14100) << "incoming message:class:" << event->event.msg.msgclass << endl;
+ if ( event->event.msg.msgclass & GG_CLASS_CTCP ) {
+ kdDebug( 14100 ) << "incomming ctcp " << endl;
+ // TODO: DCC CONNECTION
+ emit incomingCtcp( event->event.msg.sender );
+ }
+
+ if ( (event->event.msg.msgclass & GG_CLASS_MSG) || (event->event.msg.msgclass & GG_CLASS_CHAT) ) {
+ gaduMessage.message =
+ textcodec->toUnicode((const char*)event->event.msg.message);
+ gaduMessage.sender_id = event->event.msg.sender;
+ gaduMessage.sendTime.setTime_t( event->event.msg.time, Qt::LocalTime );
+ gaduMessage.message = rtf->convertToHtml( gaduMessage.message, event->event.msg.formats_length, event->event.msg.formats );
+ emit messageReceived( &gaduMessage );
+ }
+ break;
+ case GG_EVENT_ACK:
+ emit ackReceived( event->event.ack.recipient );
+ break;
+ case GG_EVENT_STATUS:
+ gaduNotify.status = event->event.status.status;
+ gaduNotify.contact_id = event->event.status.uin;
+ if ( event->event.status.descr ) {
+ gaduNotify.description = textcodec->toUnicode( event->event.status.descr );
+ }
+ else {
+ gaduNotify.description = QString::null;
+ }
+ gaduNotify.remote_port = 0;
+ gaduNotify.version = 0;
+ gaduNotify.image_size = 0;
+ gaduNotify.time = 0;
+ gaduNotify.fileCap = false;
+
+ emit contactStatusChanged( &gaduNotify );
+ break;
+ case GG_EVENT_STATUS60:
+ gaduNotify.status = event->event.status60.status;
+ gaduNotify.contact_id = event->event.status60.uin;
+ if ( event->event.status60.descr ) {
+ gaduNotify.description = textcodec->toUnicode( event->event.status60.descr );
+ }
+ else {
+ gaduNotify.description = QString::null;
+ }
+ gaduNotify.remote_ip.setAddress( ntohl( event->event.status60.remote_ip ) );
+ gaduNotify.remote_port = event->event.status60.remote_port;
+ gaduNotify.version = event->event.status60.version;
+ gaduNotify.image_size = event->event.status60.image_size;
+ gaduNotify.time = event->event.status60.time;
+ if ( event->event.status60.remote_ip && gaduNotify.remote_port > 10 ) {
+ gaduNotify.fileCap = true;
+ }
+ else {
+ gaduNotify.fileCap = false;
+ }
+
+ emit contactStatusChanged( &gaduNotify );
+ break;
+ case GG_EVENT_NOTIFY60:
+ notify60( event );
+ break;
+ case GG_EVENT_CONN_SUCCESS:
+ kdDebug(14100) << "success server: " << session_->server_addr << endl;
+ emit connectionSucceed();
+ break;
+ case GG_EVENT_CONN_FAILED:
+ kdDebug(14100) << "failed server: " << session_->server_addr << endl;
+ destroySession();
+ kdDebug(14100) << "emit connection failed(" << event->event.failure << ") signal" << endl;
+ emit connectionFailed( (gg_failure_t)event->event.failure );
+ break;
+ case GG_EVENT_DISCONNECT:
+ kdDebug(14100)<<"event Disconnected"<<endl;
+ // it should be called either when we requested disconnect, or when other client connects with our UID
+ logoff( Kopete::Account::Manual );
+ break;
+ case GG_EVENT_PONG:
+ emit pong();
+ break;
+ case GG_EVENT_NONE:
+ break;
+ case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+ case GG_EVENT_PUBDIR50_WRITE:
+ case GG_EVENT_PUBDIR50_READ:
+ sendResult( event->event.pubdir50 );
+ break;
+ case GG_EVENT_USERLIST:
+ handleUserlist( event );
+ break;
+ default:
+ kdDebug(14100)<<"Unprocessed GaduGadu Event = "<<event->type<<endl;
+ break;
+ }
+
+ if ( event ) {
+ gg_free_event( event );
+ }
+
+ if ( session_ ) {
+ enableNotifiers( session_->check );
+ }
+}
+
+#include "gadusession.moc"
diff --git a/kopete/protocols/gadu/gadusession.h b/kopete/protocols/gadu/gadusession.h
new file mode 100644
index 00000000..79626d7f
--- /dev/null
+++ b/kopete/protocols/gadu/gadusession.h
@@ -0,0 +1,178 @@
+// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
+//
+// Copyright (C) 2003-2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
+// Copyright (C) 2002 Zack Rusin <zack@kde.org>
+//
+// gadusession.h
+//
+// 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.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+// 02111-1307, USA.
+
+#ifndef GADUSESSION_H
+#define GADUSESSION_H
+
+#include "kopeteaccount.h"
+
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qcstring.h>
+#include <qhostaddress.h>
+
+#include "gaducontactlist.h"
+
+#include <libgadu.h>
+
+struct KGaduMessage {
+ QString message; // Unicode
+ unsigned int sender_id; // sender's UIN
+ QDateTime sendTime;
+ QByteArray rtf;
+};
+
+struct KGaduLoginParams {
+ uin_t uin;
+ QString password;
+ bool useTls;
+ int status;
+ QString statusDescr;
+ unsigned int server;
+ bool forFriends;
+ unsigned int client_addr;
+ unsigned int client_port;
+};
+
+struct KGaduNotify {
+ int status;
+ QHostAddress remote_ip;
+ unsigned short remote_port;
+ bool fileCap;
+ int version;
+ int image_size;
+ int time;
+ QString description;
+ unsigned int contact_id;
+};
+
+struct ResLine{
+ unsigned int uin;
+ QString firstname;
+ QString surname;
+ QString nickname;
+ QString age;
+ QString city;
+ QString orgin;
+ QString meiden;
+ QString gender;
+ int status;
+};
+
+typedef QValueList<ResLine> SearchResult;
+
+class QSocketNotifier;
+class QStringList;
+namespace Kopete { class Message; }
+class GaduRichTextFormat;
+
+class GaduSession : public QObject
+{
+ Q_OBJECT
+
+public:
+ GaduSession( QObject* parent = 0, const char* name = 0 );
+ virtual ~GaduSession();
+ bool isConnected() const;
+ int status() const;
+ QString contactsToString( GaduContactsList* contactsList );
+ bool stringToContacts( GaduContactsList& , const QString& );
+ static QString failureDescription( gg_failure_t );
+ static QString errorDescription( int err );
+ static QString stateDescription( int state );
+ void dccRequest( const unsigned int );
+ unsigned int getPersonalInformation();
+ /*
+ * Initiates search in public directory, we need to be logged on to perform search !
+ * This returns 0, if you are unable to search (fe you are not logged on, you don't have memory)
+ * This does not checks parametrs !
+ * Calling this function more times with the same params, will continue this search as long as
+ * @ref pubDirSearchClose() will not be called
+ * You must set @ref pubDirSearchResult() signal before calling this function, otherwise no result
+ * will be returned
+ */
+ unsigned int pubDirSearch( ResLine&, int, int, bool );
+
+public slots:
+ void login( KGaduLoginParams* login );
+ void logoff( Kopete::Account::DisconnectReason reason = Kopete::Account::Manual );
+ int notify( uin_t*, int );
+ int addNotify( uin_t );
+ int removeNotify( uin_t );
+ int sendMessage( uin_t recipient, const Kopete::Message& msg, int msgClass );
+ int changeStatus( int, bool forFriends = false );
+ int changeStatusDescription( int, const QString&, bool forFriends = false );
+ int ping();
+
+ void requestContacts();
+
+ /*
+ * Releases all allocated memory needed to perform search.
+ * This will be done on each @ref pubDirNewSearch(), if previuos is not released
+ */
+ void pubDirSearchClose();
+ void exportContactsOnServer( GaduContactsList* );
+ bool publishPersonalInformation( ResLine& );
+
+signals:
+ void error( const QString&, const QString& );
+ void messageReceived( KGaduMessage* );
+ void ackReceived( unsigned int );
+ void contactStatusChanged( KGaduNotify* );
+ void pong();
+ void connectionFailed( gg_failure_t failure );
+ void connectionSucceed( );
+ void disconnect( Kopete::Account::DisconnectReason );
+ void pubDirSearchResult( const SearchResult&, unsigned int );
+ void userListRecieved( const QString& );
+ void userListExported();
+ void incomingCtcp( unsigned int );
+
+protected slots:
+ void enableNotifiers( int );
+ void disableNotifiers();
+ void checkDescriptor();
+ void login( struct gg_login_params* );
+
+private:
+
+ void sendResult( gg_pubdir50_t );
+ void handleUserlist( gg_event* );
+ void notify60( gg_event* );
+ void destroySession();
+ void destroyNotifiers();
+ void createNotifiers( bool connect );
+
+ gg_session* session_;
+ QSocketNotifier* read_;
+ QSocketNotifier* write_;
+ gg_login_params params_;
+ QTextCodec* textcodec;
+ int searchSeqNr_;
+ GaduRichTextFormat* rtf;
+};
+
+#endif
diff --git a/kopete/protocols/gadu/icons/Makefile.am b/kopete/protocols/gadu/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/protocols/gadu/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_away.png b/kopete/protocols/gadu/icons/cr16-action-gg_away.png
new file mode 100644
index 00000000..a3ecf056
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_away.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png
new file mode 100644
index 00000000..18d1640c
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png
new file mode 100644
index 00000000..d9790b54
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_busy_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_con.mng b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng
new file mode 100644
index 00000000..64eea9ea
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_con.mng
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png
new file mode 100644
index 00000000..23d80cb4
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_connecting.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png
new file mode 100644
index 00000000..128e27eb
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_description_overlay.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png
new file mode 100644
index 00000000..98674ef5
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_ignored.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png
new file mode 100644
index 00000000..ab994042
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png
new file mode 100644
index 00000000..e2f44f62
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_invi_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png
new file mode 100644
index 00000000..65569e12
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png
new file mode 100644
index 00000000..5b5740b3
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_offline_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online.png b/kopete/protocols/gadu/icons/cr16-action-gg_online.png
new file mode 100644
index 00000000..652c127a
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_online.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png
new file mode 100644
index 00000000..26b28d49
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-action-gg_online_d.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png
new file mode 100644
index 00000000..46f062c3
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr16-app-gadu_protocol.png
Binary files differ
diff --git a/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png
new file mode 100644
index 00000000..2ff51736
--- /dev/null
+++ b/kopete/protocols/gadu/icons/cr32-app-gadu_protocol.png
Binary files differ
diff --git a/kopete/protocols/gadu/kopete_gadu.desktop b/kopete/protocols/gadu/kopete_gadu.desktop
new file mode 100644
index 00000000..5c2750d5
--- /dev/null
+++ b/kopete/protocols/gadu/kopete_gadu.desktop
@@ -0,0 +1,78 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=gadu_protocol
+ServiceTypes=Kopete/Protocol
+X-Kopete-Messaging-Protocol=messaging/gadu
+X-KDE-Library=kopete_gadu
+X-KDE-PluginInfo-Author=Grzegorz Jaskiewicz
+X-KDE-PluginInfo-Email=gj@pointblue.com.pl
+X-KDE-PluginInfo-Name=kopete_gadu
+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=Gadu-Gadu
+Name[fa]=Gadu-Gadu‌
+Name[hi]=गडू-गडू
+Name[ne]=गादà¥-गादà¥
+Name[tr]=Gadu Gadu
+Comment=Protocol to connect to Gadu-Gadu
+Comment[ar]=البرتوكول سيتصل بـ Gadu-Gadu
+Comment[be]=Пратакол Gadu-Gadu
+Comment[bg]=Протокол за връзка Ñ Gadu-Gadu
+Comment[bn]=Gadu-Gadu-তে সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh Gadu-Gadu
+Comment[bs]=Gadu-Gadu protokol
+Comment[ca]=Protocol per a connectar-se a Gadu-Gadu
+Comment[cs]=Protokol k připojení ke Gadu-Gadu
+Comment[cy]=Protocol i gysylltu â Gadu-Gadu
+Comment[da]=Protokol til at forbinde til Gadu-Gadu
+Comment[de]=Protokoll zur Verbindung mit Gadu-Gadu
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο Gadu-Gadu
+Comment[es]=Protocolo de conexión a Gadu-Gadu
+Comment[et]=Protokoll ühendumiseks Gadu-Gaduga
+Comment[eu]=Gadu-Gadu-ra konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال Gadu-Gadu‌
+Comment[fi]=Yhteyskäytäntö Gadu-Gadu-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur Gadu-Gadu
+Comment[ga]=Prótacal chun ceangal le Gadu-Gadu
+Comment[gl]=Protocolo para conectarse a Gadu-Gadu
+Comment[he]=פרוטוקול התחברות ל- Gadu-Gadu
+Comment[hi]=गडू-गडू से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na Gadu-Gadu
+Comment[hu]=Protokoll a Gadu-Gadu használatához
+Comment[is]=Samskiptamáti til að tengjast Gadu-Gadu
+Comment[it]=Protocollo per connessione a Gadu-Gadu
+Comment[ja]=Gadu-Gadu ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=Gadu-Gadu-სთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Gadu-Gadu-ға қоÑылу протоколы
+Comment[km]=ពិធីការ​​ភ្ជាប់​ទៅ Gadu-Gadu​
+Comment[lt]=Protokolas prisijungimui prie Gadu-Gadu
+Comment[mk]=Протокол за поврзување на Gadu-Gadu
+Comment[nb]=Protokoll for å koble til Gadu-Gadu
+Comment[nds]=Protokoll för't Tokoppeln na Gadu-Gadu
+Comment[ne]=गादà¥-गादà¥à¤®à¤¾ जडान गरà¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor Gadu-Gadu
+Comment[nn]=Protokoll for å kopla til Gadu-Gadu
+Comment[pl]=Protokół połączenia z serwerem Gadu-Gadu
+Comment[pt]=Um protocolo para se ligar ao Gadu-Gadu
+Comment[pt_BR]=Protocolo para conexão ao Gadu-Gadu
+Comment[ro]=Protocol de conectare la Gadu-Gadu
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Gadu-Gadu
+Comment[sk]=Protokol pre pripojenie k Gadu-Gadu
+Comment[sl]=Protokol za povezavo na Gadu-Gadu
+Comment[sr]=Протокол за повезивање на Gadu-Gadu
+Comment[sr@Latn]=Protokol za povezivanje na Gadu-Gadu
+Comment[sv]=Protokoll för att ansluta till Gadu-Gadu
+Comment[ta]=Gadu-Gadu உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба Gadu-Gadu
+Comment[tr]=Gadu Gadu'ya bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Gadu-Gadu
+Comment[uz]=Gadu-Gadu bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=Gadu-Gadu билан алоқа ўрнатиш учун протокол
+Comment[zh_CN]=连接到 Gadu-Gadu åè®®
+Comment[zh_HK]=用來連接至 Gadu-Gadu 的通訊å”定
+Comment[zh_TW]=連線到 Gadu-Gadu çš„å”定
diff --git a/kopete/protocols/gadu/libgadu/COPYING b/kopete/protocols/gadu/libgadu/COPYING
new file mode 100644
index 00000000..512ede36
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/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/gadu/libgadu/Makefile.am b/kopete/protocols/gadu/libgadu/Makefile.am
new file mode 100644
index 00000000..94693d38
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libgadu_copy.la
+INCLUDES = $(SSL_INCLUDES) $(all_includes)
+libgadu_copy_la_LDFLAGS = $(SSL_LDFLAGS) $(all_libraries)
+libgadu_copy_la_LIBADD = $(LIBSSL) $(LIBPTHREAD)
+libgadu_copy_la_SOURCES = common.c \
+ dcc.c \
+ events.c \
+ http.c \
+ libgadu.c \
+ pubdir50.c \
+ pubdir.c
diff --git a/kopete/protocols/gadu/libgadu/common.c b/kopete/protocols/gadu/libgadu/common.c
new file mode 100644
index 00000000..2e835fca
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/common.c
@@ -0,0 +1,822 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Wo¼ny <speedy@ziew.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+FILE *gg_debug_file = NULL;
+
+#ifndef GG_DEBUG_DISABLE
+
+/*
+ * gg_debug() // funkcja wewnêtrzna
+ *
+ * wy¶wietla komunikat o danym poziomie, o ile u¿ytkownik sobie tego ¿yczy.
+ *
+ * - level - poziom wiadomo¶ci
+ * - format... - tre¶æ wiadomo¶ci (kompatybilna z printf())
+ */
+void gg_debug(int level, const char *format, ...)
+{
+ va_list ap;
+ int old_errno = errno;
+
+ if (gg_debug_handler) {
+ va_start(ap, format);
+ (*gg_debug_handler)(level, format, ap);
+ va_end(ap);
+
+ goto cleanup;
+ }
+
+ if ((gg_debug_level & level)) {
+ va_start(ap, format);
+ vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
+ va_end(ap);
+ }
+
+cleanup:
+ errno = old_errno;
+}
+
+#endif
+
+/*
+ * gg_vsaprintf() // funkcja pomocnicza
+ *
+ * robi dok³adnie to samo, co vsprintf(), tyle ¿e alokuje sobie wcze¶niej
+ * miejsce na dane. powinno dzia³aæ na tych maszynach, które maj± funkcjê
+ * vsnprintf() zgodn± z C99, jak i na wcze¶niejszych.
+ *
+ * - format - opis wy¶wietlanego tekstu jak dla printf()
+ * - ap - lista argumentów dla printf()
+ *
+ * zaalokowany bufor, który nale¿y pó¼niej zwolniæ, lub NULL
+ * je¶li nie uda³o siê wykonaæ zadania.
+ */
+char *gg_vsaprintf(const char *format, va_list ap)
+{
+ int size = 0;
+ const char *start;
+ char *buf = NULL;
+
+#ifdef __GG_LIBGADU_HAVE_VA_COPY
+ va_list aq;
+
+ va_copy(aq, ap);
+#else
+# ifdef __GG_LIBGADU_HAVE___VA_COPY
+ va_list aq;
+
+ __va_copy(aq, ap);
+# endif
+#endif
+
+ start = format;
+
+#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+ {
+ int res;
+ char *tmp;
+
+ size = 128;
+ do {
+ size *= 2;
+ if (!(tmp = realloc(buf, size))) {
+ free(buf);
+ return NULL;
+ }
+ buf = tmp;
+ res = vsnprintf(buf, size, format, ap);
+ } while (res == size - 1 || res == -1);
+ }
+#else
+ {
+ char tmp[2];
+
+ /* libce Solarisa przy buforze NULL zawsze zwracaj± -1, wiêc
+ * musimy podaæ co¶ istniej±cego jako cel printf()owania. */
+ size = vsnprintf(tmp, sizeof(tmp), format, ap);
+ if (!(buf = malloc(size + 1)))
+ return NULL;
+ }
+#endif
+
+ format = start;
+
+#ifdef __GG_LIBGADU_HAVE_VA_COPY
+ vsnprintf(buf, size + 1, format, aq);
+ va_end(aq);
+#else
+# ifdef __GG_LIBGADU_HAVE___VA_COPY
+ vsnprintf(buf, size + 1, format, aq);
+ va_end(aq);
+# else
+ vsnprintf(buf, size + 1, format, ap);
+# endif
+#endif
+
+ return buf;
+}
+
+/*
+ * gg_saprintf() // funkcja pomocnicza
+ *
+ * robi dok³adnie to samo, co sprintf(), tyle ¿e alokuje sobie wcze¶niej
+ * miejsce na dane. powinno dzia³aæ na tych maszynach, które maj± funkcjê
+ * vsnprintf() zgodn± z C99, jak i na wcze¶niejszych.
+ *
+ * - format... - tre¶æ taka sama jak w funkcji printf()
+ *
+ * zaalokowany bufor, który nale¿y pó¼niej zwolniæ, lub NULL
+ * je¶li nie uda³o siê wykonaæ zadania.
+ */
+char *gg_saprintf(const char *format, ...)
+{
+ va_list ap;
+ char *res;
+
+ va_start(ap, format);
+ res = gg_vsaprintf(format, ap);
+ va_end(ap);
+
+ return res;
+}
+
+/*
+ * gg_get_line() // funkcja pomocnicza
+ *
+ * podaje kolejn± liniê z bufora tekstowego. niszczy go bezpowrotnie, dziel±c
+ * na kolejne stringi. zdarza siê, nie ma potrzeby pisania funkcji dubluj±cej
+ * bufor ¿eby tylko mieæ nieruszone dane wej¶ciowe, skoro i tak nie bêd± nam
+ * po¼niej potrzebne. obcina `\r\n'.
+ *
+ * - ptr - wska¼nik do zmiennej, która przechowuje aktualn± pozycjê
+ * w przemiatanym buforze
+ *
+ * wska¼nik do kolejnej linii tekstu lub NULL, je¶li to ju¿ koniec bufora.
+ */
+char *gg_get_line(char **ptr)
+{
+ char *foo, *res;
+
+ if (!ptr || !*ptr || !strcmp(*ptr, ""))
+ return NULL;
+
+ res = *ptr;
+
+ if (!(foo = strchr(*ptr, '\n')))
+ *ptr += strlen(*ptr);
+ else {
+ *ptr = foo + 1;
+ *foo = 0;
+ if (strlen(res) > 1 && res[strlen(res) - 1] == '\r')
+ res[strlen(res) - 1] = 0;
+ }
+
+ return res;
+}
+
+/*
+ * gg_connect() // funkcja pomocnicza
+ *
+ * ³±czy siê z serwerem. pierwszy argument jest typu (void *), ¿eby nie
+ * musieæ niczego inkludowaæ w libgadu.h i nie psuæ jaki¶ g³upich zale¿no¶ci
+ * na dziwnych systemach.
+ *
+ * - addr - adres serwera (struct in_addr *)
+ * - port - port serwera
+ * - async - asynchroniczne po³±czenie
+ *
+ * deskryptor gniazda lub -1 w przypadku b³êdu (kod b³êdu w zmiennej errno).
+ */
+int gg_connect(void *addr, int port, int async)
+{
+ int sock, one = 1, errno2;
+ struct sockaddr_in sin;
+ struct in_addr *a = addr;
+ struct sockaddr_in myaddr;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async);
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno));
+ return -1;
+ }
+
+ memset(&myaddr, 0, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+
+ myaddr.sin_addr.s_addr = gg_local_ip;
+
+ if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno));
+ return -1;
+ }
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+ gg_win32_thread_socket(0, sock);
+#endif
+
+ if (async) {
+#ifdef FIONBIO
+ if (ioctl(sock, FIONBIO, &one) == -1) {
+#else
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno));
+ errno2 = errno;
+ close(sock);
+ errno = errno2;
+ return -1;
+ }
+ }
+
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = a->s_addr;
+
+ if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
+ if (errno && (!async || errno != EINPROGRESS)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno));
+ errno2 = errno;
+ close(sock);
+ errno = errno2;
+ return -1;
+ }
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+ }
+
+ return sock;
+}
+
+/*
+ * gg_read_line() // funkcja pomocnicza
+ *
+ * czyta jedn± liniê tekstu z gniazda.
+ *
+ * - sock - deskryptor gniazda
+ * - buf - wska¼nik do bufora
+ * - length - d³ugo¶æ bufora
+ *
+ * je¶li trafi na b³±d odczytu lub podano nieprawid³owe parametry, zwraca NULL.
+ * inaczej zwraca buf.
+ */
+char *gg_read_line(int sock, char *buf, int length)
+{
+ int ret;
+
+ if (!buf || length < 0)
+ return NULL;
+
+ for (; length > 1; buf++, length--) {
+ do {
+ if ((ret = read(sock, buf, 1)) == -1 && errno != EINTR) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
+ *buf = 0;
+ return NULL;
+ } else if (ret == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
+ *buf = 0;
+ return NULL;
+ }
+ } while (ret == -1 && errno == EINTR);
+
+ if (*buf == '\n') {
+ buf++;
+ break;
+ }
+ }
+
+ *buf = 0;
+ return buf;
+}
+
+/*
+ * gg_chomp() // funkcja pomocnicza
+ *
+ * ucina "\r\n" lub "\n" z koñca linii.
+ *
+ * - line - linia do przyciêcia
+ */
+void gg_chomp(char *line)
+{
+ int len;
+
+ if (!line)
+ return;
+
+ len = strlen(line);
+
+ if (len > 0 && line[len - 1] == '\n')
+ line[--len] = 0;
+ if (len > 0 && line[len - 1] == '\r')
+ line[--len] = 0;
+}
+
+/*
+ * gg_urlencode() // funkcja wewnêtrzna
+ *
+ * zamienia podany tekst na ci±g znaków do formularza http. przydaje siê
+ * przy ró¿nych us³ugach katalogu publicznego.
+ *
+ * - str - ci±g znaków do zakodowania
+ *
+ * zaalokowany bufor, który nale¿y pó¼niej zwolniæ albo NULL
+ * w przypadku b³êdu.
+ */
+char *gg_urlencode(const char *str)
+{
+ char *q, *buf, hex[] = "0123456789abcdef";
+ const char *p;
+ unsigned int size = 0;
+
+ if (!str)
+ str = "";
+
+ for (p = str; *p; p++, size++) {
+ if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-'))
+ size += 2;
+ }
+
+ if (!(buf = malloc(size + 1)))
+ return NULL;
+
+ for (p = str, q = buf; *p; p++, q++) {
+ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-'))
+ *q = *p;
+ else {
+ if (*p == ' ')
+ *q = '+';
+ else {
+ *q++ = '%';
+ *q++ = hex[*p >> 4 & 15];
+ *q = hex[*p & 15];
+ }
+ }
+ }
+
+ *q = 0;
+
+ return buf;
+}
+
+/*
+ * gg_http_hash() // funkcja wewnêtrzna
+ *
+ * funkcja licz±ca hash dla adresu e-mail, has³a i paru innych.
+ *
+ * - format... - format kolejnych parametrów ('s' je¶li dany parametr jest
+ * ci±giem znaków lub 'u' je¶li numerem GG)
+ *
+ * hash wykorzystywany przy rejestracji i wszelkich manipulacjach w³asnego
+ * wpisu w katalogu publicznym.
+ */
+int gg_http_hash(const char *format, ...)
+{
+ unsigned int a, c, i, j;
+ va_list ap;
+ int b = -1;
+
+ va_start(ap, format);
+
+ for (j = 0; j < strlen(format); j++) {
+ char *arg, buf[16];
+
+ if (format[j] == 'u') {
+ snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t));
+ arg = buf;
+ } else {
+ if (!(arg = va_arg(ap, char*)))
+ arg = "";
+ }
+
+ i = 0;
+ while ((c = (unsigned char) arg[i++]) != 0) {
+ a = (c ^ b) + (c << 8);
+ b = (a >> 24) | (a << 8);
+ }
+ }
+
+ va_end(ap);
+
+ return (b < 0 ? -b : b);
+}
+
+/*
+ * gg_gethostbyname() // funkcja pomocnicza
+ *
+ * odpowiednik gethostbyname() troszcz±cy siê o wspó³bie¿no¶æ, gdy mamy do
+ * dyspozycji funkcjê gethostbyname_r().
+ *
+ * - hostname - nazwa serwera
+ *
+ * zwraca wska¼nik na strukturê in_addr, któr± nale¿y zwolniæ.
+ */
+struct in_addr *gg_gethostbyname(const char *hostname)
+{
+ struct in_addr *addr = NULL;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ char *tmpbuf = NULL, *buf = NULL;
+ struct hostent *hp = NULL, *hp2 = NULL;
+ int h_errnop, ret;
+ size_t buflen = 1024;
+ int new_errno;
+
+ new_errno = ENOMEM;
+
+ if (!(addr = malloc(sizeof(struct in_addr))))
+ goto cleanup;
+
+ if (!(hp = calloc(1, sizeof(*hp))))
+ goto cleanup;
+
+ if (!(buf = malloc(buflen)))
+ goto cleanup;
+
+ tmpbuf = buf;
+
+ while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) {
+ buflen *= 2;
+
+ if (!(tmpbuf = realloc(buf, buflen)))
+ break;
+
+ buf = tmpbuf;
+ }
+
+ if (ret)
+ new_errno = h_errnop;
+
+ if (ret || !hp2 || !tmpbuf)
+ goto cleanup;
+
+ memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+
+ free(buf);
+ free(hp);
+
+ return addr;
+
+cleanup:
+ errno = new_errno;
+
+ if (addr)
+ free(addr);
+ if (hp)
+ free(hp);
+ if (buf)
+ free(buf);
+
+ return NULL;
+#else
+ struct hostent *hp;
+
+ if (!(addr = malloc(sizeof(struct in_addr)))) {
+ goto cleanup;
+ }
+
+ if (!(hp = gethostbyname(hostname)))
+ goto cleanup;
+
+ memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+
+ return addr;
+
+cleanup:
+ if (addr)
+ free(addr);
+
+ return NULL;
+#endif
+}
+
+#ifdef ASSIGN_SOCKETS_TO_THREADS
+
+typedef struct gg_win32_thread {
+ int id;
+ int socket;
+ struct gg_win32_thread *next;
+} gg_win32_thread;
+
+struct gg_win32_thread *gg_win32_threads = 0;
+
+/*
+ * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32
+ *
+ * zwraca deskryptor gniazda, które by³o ostatnio tworzone dla w±tku
+ * o podanym identyfikatorze.
+ *
+ * je¶li na win32 przy po³±czeniach synchronicznych zapamiêtamy w jakim
+ * w±tku uruchomili¶my funkcjê, która siê z czymkolwiek ³±czy, to z osobnego
+ * w±tku mo¿emy anulowaæ po³±czenie poprzez gg_win32_thread_socket(watek, -1);
+ *
+ * - thread_id - id w±tku. je¶li jest równe 0, brany jest aktualny w±tek,
+ * je¶li równe -1, usuwa wpis o podanym sockecie.
+ * - socket - deskryptor gniazda. je¶li równe 0, zwraca deskryptor gniazda
+ * dla podanego w±tku, je¶li równe -1, usuwa wpis, je¶li co¶
+ * innego, ustawia dla podanego w±tku dany numer deskryptora.
+ *
+ * je¶li socket jest równe 0, zwraca deskryptor gniazda dla podanego w±tku.
+ */
+int gg_win32_thread_socket(int thread_id, int socket)
+{
+ char close = (thread_id == -1) || socket == -1;
+ gg_win32_thread *wsk = gg_win32_threads;
+ gg_win32_thread **p_wsk = &gg_win32_threads;
+
+ if (!thread_id)
+ thread_id = GetCurrentThreadId();
+
+ while (wsk) {
+ if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) {
+ if (close) {
+ /* socket zostaje usuniety */
+ closesocket(wsk->socket);
+ *p_wsk = wsk->next;
+ free(wsk);
+ return 1;
+ } else if (!socket) {
+ /* socket zostaje zwrocony */
+ return wsk->socket;
+ } else {
+ /* socket zostaje ustawiony */
+ wsk->socket = socket;
+ return socket;
+ }
+ }
+ p_wsk = &(wsk->next);
+ wsk = wsk->next;
+ }
+
+ if (close && socket != -1)
+ closesocket(socket);
+ if (close || !socket)
+ return 0;
+
+ /* Dodaje nowy element */
+ wsk = malloc(sizeof(gg_win32_thread));
+ wsk->id = thread_id;
+ wsk->socket = socket;
+ wsk->next = 0;
+ *p_wsk = wsk;
+
+ return socket;
+}
+
+#endif /* ASSIGN_SOCKETS_TO_THREADS */
+
+static char gg_base64_charset[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * gg_base64_encode()
+ *
+ * zapisuje ci±g znaków w base64.
+ *
+ * - buf - ci±g znaków.
+ *
+ * zaalokowany bufor.
+ */
+char *gg_base64_encode(const char *buf)
+{
+ char *out, *res;
+ unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
+
+ res = out = malloc((len / 3 + 1) * 4 + 2);
+
+ if (!res)
+ return NULL;
+
+ while (j <= len) {
+ switch (i % 4) {
+ case 0:
+ k = (buf[j] & 252) >> 2;
+ break;
+ case 1:
+ if (j < len)
+ k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4);
+ else
+ k = (buf[j] & 3) << 4;
+
+ j++;
+ break;
+ case 2:
+ if (j < len)
+ k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6);
+ else
+ k = (buf[j] & 15) << 2;
+
+ j++;
+ break;
+ case 3:
+ k = buf[j++] & 63;
+ break;
+ }
+ *out++ = gg_base64_charset[k];
+ i++;
+ }
+
+ if (i % 4)
+ for (j = 0; j < 4 - (i % 4); j++, out++)
+ *out = '=';
+
+ *out = 0;
+
+ return res;
+}
+
+/*
+ * gg_base64_decode()
+ *
+ * dekoduje ci±g znaków z base64.
+ *
+ * - buf - ci±g znaków.
+ *
+ * zaalokowany bufor.
+ */
+char *gg_base64_decode(const char *buf)
+{
+ char *res, *save, *foo, val;
+ const char *end;
+ unsigned int index = 0;
+
+ if (!buf)
+ return NULL;
+
+ save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
+
+ if (!save)
+ return NULL;
+
+ end = buf + strlen(buf);
+
+ while (*buf && buf < end) {
+ if (*buf == '\r' || *buf == '\n') {
+ buf++;
+ continue;
+ }
+ if (!(foo = strchr(gg_base64_charset, *buf)))
+ foo = gg_base64_charset;
+ val = (int)(foo - gg_base64_charset);
+ buf++;
+ switch (index) {
+ case 0:
+ *res |= val << 2;
+ break;
+ case 1:
+ *res++ |= val >> 4;
+ *res |= val << 4;
+ break;
+ case 2:
+ *res++ |= val >> 2;
+ *res |= val << 6;
+ break;
+ case 3:
+ *res++ |= val;
+ break;
+ }
+ index++;
+ index %= 4;
+ }
+ *res = 0;
+
+ return save;
+}
+
+/*
+ * gg_proxy_auth() // funkcja wewnêtrzna
+ *
+ * tworzy nag³ówek autoryzacji dla proxy.
+ *
+ * zaalokowany tekst lub NULL, je¶li proxy nie jest w³±czone lub nie wymaga
+ * autoryzacji.
+ */
+char *gg_proxy_auth()
+{
+ char *tmp, *enc, *out;
+ unsigned int tmp_size;
+
+ if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
+ return NULL;
+
+ if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2))))
+ return NULL;
+
+ snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password);
+
+ if (!(enc = gg_base64_encode(tmp))) {
+ free(tmp);
+ return NULL;
+ }
+
+ free(tmp);
+
+ if (!(out = malloc(strlen(enc) + 40))) {
+ free(enc);
+ return NULL;
+ }
+
+ snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc);
+
+ free(enc);
+
+ return out;
+}
+
+static uint32_t gg_crc32_table[256];
+static int gg_crc32_initialized = 0;
+
+/*
+ * gg_crc32_make_table() // funkcja wewnêtrzna
+ */
+static void gg_crc32_make_table()
+{
+ uint32_t h = 1;
+ unsigned int i, j;
+
+ memset(gg_crc32_table, 0, sizeof(gg_crc32_table));
+
+ for (i = 128; i; i >>= 1) {
+ h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
+
+ for (j = 0; j < 256; j += 2 * i)
+ gg_crc32_table[i + j] = gg_crc32_table[j] ^ h;
+ }
+
+ gg_crc32_initialized = 1;
+}
+
+/*
+ * gg_crc32()
+ *
+ * wyznacza sumê kontroln± CRC32 danego bloku danych.
+ *
+ * - crc - suma kontrola poprzedniego bloku danych lub 0 je¶li pierwszy
+ * - buf - bufor danych
+ * - size - ilo¶æ danych
+ *
+ * suma kontrolna CRC32.
+ */
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
+{
+ if (!gg_crc32_initialized)
+ gg_crc32_make_table();
+
+ if (!buf || len < 0)
+ return crc;
+
+ crc ^= 0xffffffffL;
+
+ while (len--)
+ crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff];
+
+ return crc ^ 0xffffffffL;
+}
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/compat.h b/kopete/protocols/gadu/libgadu/compat.h
new file mode 100644
index 00000000..715fcb3a
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/compat.h
@@ -0,0 +1,29 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Wo¼ny <speedy@ziew.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __COMPAT_H
+#define __COMPAT_H
+
+#ifdef sun
+# define INADDR_NONE ((in_addr_t) 0xffffffff)
+#endif
+
+#endif
diff --git a/kopete/protocols/gadu/libgadu/dcc.c b/kopete/protocols/gadu/libgadu/dcc.c
new file mode 100644
index 00000000..085d9d01
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/dcc.c
@@ -0,0 +1,1298 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Tomasz Chiliñski <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+#ifndef GG_DEBUG_DISABLE
+/*
+ * gg_dcc_debug_data() // funkcja wewnêtrzna
+ *
+ * wy¶wietla zrzut pakietu w hexie.
+ *
+ * - prefix - prefiks zrzutu pakietu
+ * - fd - deskryptor gniazda
+ * - buf - bufor z danymi
+ * - size - rozmiar danych
+ */
+static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
+{
+ unsigned int i;
+
+ gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
+
+ for (i = 0; i < size; i++)
+ gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]);
+
+ gg_debug(GG_DEBUG_MISC, "\n");
+}
+#else
+#define gg_dcc_debug_data(a,b,c,d) do { } while (0)
+#endif
+
+/*
+ * gg_dcc_request()
+ *
+ * wysy³a informacjê o tym, ¿e dany klient powinien siê z nami po³±czyæ.
+ * wykorzystywane, kiedy druga strona, której chcemy co¶ wys³aæ jest za
+ * maskarad±.
+ *
+ * - sess - struktura opisuj±ca sesjê GG
+ * - uin - numerek odbiorcy
+ *
+ * patrz gg_send_message_ctcp().
+ */
+int gg_dcc_request(struct gg_session *sess, uin_t uin)
+{
+ return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, "\002", 1);
+}
+
+/*
+ * gg_dcc_fill_filetime() // funkcja wewnêtrzna
+ *
+ * zamienia czas w postaci unixowej na windowsowy.
+ *
+ * - unix - czas w postaci unixowej
+ * - filetime - czas w postaci windowsowej
+ */
+static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
+{
+#ifdef __GG_LIBGADU_HAVE_LONG_LONG
+ unsigned long long tmp;
+
+ tmp = ut;
+ tmp += 11644473600LL;
+ tmp *= 10000000LL;
+
+#ifndef __GG_LIBGADU_BIGENDIAN
+ ft[0] = (uint32_t) tmp;
+ ft[1] = (uint32_t) (tmp >> 32);
+#else
+ ft[0] = gg_fix32((uint32_t) (tmp >> 32));
+ ft[1] = gg_fix32((uint32_t) tmp);
+#endif
+
+#endif
+}
+
+/*
+ * gg_dcc_fill_file_info()
+ *
+ * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku.
+ *
+ * - d - struktura opisuj±ca po³±czenie DCC
+ * - filename - nazwa pliku
+ *
+ * 0, -1.
+ */
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
+{
+ return gg_dcc_fill_file_info2(d, filename, filename);
+}
+
+/*
+ * gg_dcc_fill_file_info2()
+ *
+ * wype³nia pola struct gg_dcc niezbêdne do wys³ania pliku.
+ *
+ * - d - struktura opisuj±ca po³±czenie DCC
+ * - filename - nazwa pliku
+ * - local_filename - nazwa na lokalnym systemie plików
+ *
+ * 0, -1.
+ */
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
+{
+ struct stat st;
+ const char *name, *ext, *p;
+ unsigned char *q;
+ int i, j;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename);
+
+ if (!d || d->type != GG_SESSION_DCC_SEND) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (stat(local_filename, &st) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ if ((st.st_mode & S_IFDIR)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&d->file_info, 0, sizeof(d->file_info));
+
+ if (!(st.st_mode & S_IWUSR))
+ d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY);
+
+ gg_dcc_fill_filetime(st.st_atime, d->file_info.atime);
+ gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime);
+ gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime);
+
+ d->file_info.size = gg_fix32(st.st_size);
+ d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */
+
+ if (!(name = strrchr(filename, '/')))
+ name = filename;
+ else
+ name++;
+
+ if (!(ext = strrchr(name, '.')))
+ ext = name + strlen(name);
+
+ for (i = 0, p = name; i < 8 && p < ext; i++, p++)
+ d->file_info.short_filename[i] = toupper(name[i]);
+
+ if (i == 8 && p < ext) {
+ d->file_info.short_filename[6] = '~';
+ d->file_info.short_filename[7] = '1';
+ }
+
+ if (strlen(ext) > 0) {
+ for (j = 0; *ext && j < 4; j++, p++)
+ d->file_info.short_filename[i + j] = toupper(ext[j]);
+ }
+
+ for (q = d->file_info.short_filename; *q; q++) {
+ if (*q == 185) {
+ *q = 165;
+ } else if (*q == 230) {
+ *q = 198;
+ } else if (*q == 234) {
+ *q = 202;
+ } else if (*q == 179) {
+ *q = 163;
+ } else if (*q == 241) {
+ *q = 209;
+ } else if (*q == 243) {
+ *q = 211;
+ } else if (*q == 156) {
+ *q = 140;
+ } else if (*q == 159) {
+ *q = 143;
+ } else if (*q == 191) {
+ *q = 175;
+ }
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
+ strncpy(d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+
+ return 0;
+}
+
+/*
+ * gg_dcc_transfer() // funkcja wewnêtrzna
+ *
+ * inicjuje proces wymiany pliku z danym klientem.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - w³asny numer
+ * - peer_uin - numer obiorcy
+ * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET)
+ *
+ * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d.
+ */
+static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
+{
+ struct gg_dcc *d = NULL;
+ struct in_addr addr;
+
+ addr.s_addr = ip;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
+
+ if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(d = (void*) calloc(1, sizeof(*d)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n");
+ return NULL;
+ }
+
+ d->check = GG_CHECK_WRITE;
+ d->state = GG_STATE_CONNECTING;
+ d->type = type;
+ d->timeout = GG_DEFAULT_TIMEOUT;
+ d->file_fd = -1;
+ d->active = 1;
+ d->fd = -1;
+ d->uin = my_uin;
+ d->peer_uin = peer_uin;
+
+ if ((d->fd = gg_connect(&addr, port, 1)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n");
+ free(d);
+ return NULL;
+ }
+
+ return d;
+}
+
+/*
+ * gg_dcc_get_file()
+ *
+ * inicjuje proces odbierania pliku od danego klienta, gdy ten wys³a³ do
+ * nas ¿±danie po³±czenia.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - w³asny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
+}
+
+/*
+ * gg_dcc_send_file()
+ *
+ * inicjuje proces wysy³ania pliku do danego klienta.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - w³asny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
+}
+
+/*
+ * gg_dcc_voice_chat()
+ *
+ * próbuje nawi±zaæ po³±czenie g³osowe.
+ *
+ * - ip - adres ip odbiorcy
+ * - port - port odbiorcy
+ * - my_uin - w³asny numer
+ * - peer_uin - numer obiorcy
+ *
+ * zaalokowana struct gg_dcc lub NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
+}
+
+/*
+ * gg_dcc_set_type()
+ *
+ * po zdarzeniu GG_EVENT_DCC_CALLBACK nale¿y ustawiæ typ po³±czenia za
+ * pomoc± tej funkcji.
+ *
+ * - d - struktura opisuj±ca po³±czenie
+ * - type - typ po³±czenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE)
+ */
+void gg_dcc_set_type(struct gg_dcc *d, int type)
+{
+ d->type = type;
+ d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
+}
+
+/*
+ * gg_dcc_callback() // funkcja wewnêtrzna
+ *
+ * wywo³ywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza
+ * rezultat w struct gg_dcc->event.
+ *
+ * - d - structura opisuj±ca po³±czenie
+ *
+ * 0, -1.
+ */
+static int gg_dcc_callback(struct gg_dcc *d)
+{
+ struct gg_event *e = gg_dcc_watch_fd(d);
+
+ d->event = e;
+
+ return (e != NULL) ? 0 : -1;
+}
+
+/*
+ * gg_dcc_socket_create()
+ *
+ * tworzy gniazdo dla bezpo¶redniej komunikacji miêdzy klientami.
+ *
+ * - uin - w³asny numer
+ * - port - preferowany port, je¶li równy 0 lub -1, próbuje domy¶lnego
+ *
+ * zaalokowana struct gg_dcc, któr± po¼niej nale¿y zwolniæ funkcj±
+ * gg_dcc_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
+{
+ struct gg_dcc *c;
+ struct sockaddr_in sin;
+ int sock, bound = 0, errno2;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
+
+ if (!uin) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno));
+ return NULL;
+ }
+
+ if (!port)
+ port = GG_DEFAULT_DCC_PORT;
+
+ while (!bound) {
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
+ if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
+ bound = 1;
+ else {
+ if (++port == 65535) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n");
+ close(sock);
+ return NULL;
+ }
+ }
+ }
+
+ if (listen(sock, 10)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno));
+ errno2 = errno;
+ close(sock);
+ errno = errno2;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
+
+ if (!(c = malloc(sizeof(*c)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n");
+ close(sock);
+ return NULL;
+ }
+ memset(c, 0, sizeof(*c));
+
+ c->port = c->id = port;
+ c->fd = sock;
+ c->type = GG_SESSION_DCC_SOCKET;
+ c->uin = uin;
+ c->timeout = -1;
+ c->state = GG_STATE_LISTENING;
+ c->check = GG_CHECK_READ;
+ c->callback = gg_dcc_callback;
+ c->destroy = gg_dcc_free;
+
+ return c;
+}
+
+/*
+ * gg_dcc_voice_send()
+ *
+ * wysy³a ramkê danych dla rozmowy g³osowej.
+ *
+ * - d - struktura opisuj±ca po³±czenie dcc
+ * - buf - bufor z danymi
+ * - length - rozmiar ramki
+ *
+ * 0, -1.
+ */
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
+{
+ struct packet_s {
+ uint8_t type;
+ uint32_t length;
+ } GG_PACKED;
+ struct packet_s packet;
+
+ gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length);
+ if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ packet.type = 0x03; /* XXX */
+ packet.length = gg_fix32(length);
+
+ if (write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+ return -1;
+ }
+ gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet));
+
+ if (write(d->fd, buf, length) < length) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+ return -1;
+ }
+ gg_dcc_debug_data("write", d->fd, buf, length);
+
+ return 0;
+}
+
+#define gg_read(fd, buf, size) \
+{ \
+ int tmp = read(fd, buf, size); \
+ \
+ if (tmp < (int) size) { \
+ if (tmp == -1) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+ } else if (tmp == 0) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \
+ } else { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \
+ } \
+ e->type = GG_EVENT_DCC_ERROR; \
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+ return e; \
+ } \
+ gg_dcc_debug_data("read", fd, buf, size); \
+}
+
+#define gg_write(fd, buf, size) \
+{ \
+ int tmp; \
+ gg_dcc_debug_data("write", fd, buf, size); \
+ tmp = write(fd, buf, size); \
+ if (tmp < (int) size) { \
+ if (tmp == -1) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+ } else { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \
+ } \
+ e->type = GG_EVENT_DCC_ERROR; \
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+ return e; \
+ } \
+}
+
+/*
+ * gg_dcc_watch_fd()
+ *
+ * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê zmieni na gg_dcc->fd.
+ *
+ * - h - struktura zwrócona przez gg_create_dcc_socket()
+ *
+ * zaalokowana struct gg_event lub NULL, je¶li zabrak³o pamiêci na ni±.
+ */
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
+{
+ struct gg_event *e;
+ int foo;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
+
+ if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(e = (void*) calloc(1, sizeof(*e)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n");
+ return NULL;
+ }
+
+ e->type = GG_EVENT_NONE;
+
+ if (h->type == GG_SESSION_DCC_SOCKET) {
+ struct sockaddr_in sin;
+ struct gg_dcc *c;
+ int fd, sin_len = sizeof(sin), one = 1;
+
+ if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno));
+ return e;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+ if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno));
+ close(fd);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ if (!(c = (void*) calloc(1, sizeof(*c)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n");
+
+ free(e);
+ close(fd);
+ return NULL;
+ }
+
+ c->fd = fd;
+ c->check = GG_CHECK_READ;
+ c->state = GG_STATE_READING_UIN_1;
+ c->type = GG_SESSION_DCC;
+ c->timeout = GG_DEFAULT_TIMEOUT;
+ c->file_fd = -1;
+ c->remote_addr = sin.sin_addr.s_addr;
+ c->remote_port = ntohs(sin.sin_port);
+
+ e->type = GG_EVENT_DCC_NEW;
+ e->event.dcc_new = c;
+
+ return e;
+ } else {
+ struct gg_dcc_tiny_packet tiny;
+ struct gg_dcc_small_packet small;
+ struct gg_dcc_big_packet big;
+ int size, tmp, res, res_size = sizeof(res);
+ unsigned int utmp;
+ char buf[1024], ack[] = "UDAG";
+
+ struct gg_dcc_file_info_packet {
+ struct gg_dcc_big_packet big;
+ struct gg_file_info file_info;
+ } GG_PACKED;
+ struct gg_dcc_file_info_packet file_info_packet;
+
+ switch (h->state) {
+ case GG_STATE_READING_UIN_1:
+ case GG_STATE_READING_UIN_2:
+ {
+ uin_t uin;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
+
+ gg_read(h->fd, &uin, sizeof(uin));
+
+ if (h->state == GG_STATE_READING_UIN_1) {
+ h->state = GG_STATE_READING_UIN_2;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->peer_uin = gg_fix32(uin);
+ } else {
+ h->state = GG_STATE_SENDING_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->uin = gg_fix32(uin);
+ e->type = GG_EVENT_DCC_CLIENT_ACCEPT;
+ }
+
+ return e;
+ }
+
+ case GG_STATE_SENDING_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
+
+ gg_write(h->fd, ack, 4);
+
+ h->state = GG_STATE_READING_TYPE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_READING_TYPE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
+
+ gg_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ switch (small.type) {
+ case 0x0003: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n");
+ h->type = GG_SESSION_DCC_SEND;
+ h->state = GG_STATE_SENDING_FILE_INFO;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ e->type = GG_EVENT_DCC_CALLBACK;
+
+ break;
+
+ case 0x0002: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n");
+ h->type = GG_SESSION_DCC_GET;
+ h->state = GG_STATE_READING_REQUEST;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->incoming = 1;
+
+ break;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %ld\n", small.type, h->peer_uin);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ }
+
+ return e;
+
+ case GG_STATE_READING_REQUEST:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
+
+ gg_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ switch (small.type) {
+ case 0x0001: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n");
+ h->state = GG_STATE_READING_FILE_INFO;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+
+ case 0x0003: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
+ h->state = GG_STATE_SENDING_VOICE_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DCC_TIMEOUT_VOICE_ACK;
+ h->type = GG_SESSION_DCC_VOICE;
+ e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
+
+ break;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ }
+
+ return e;
+
+ case GG_STATE_READING_FILE_INFO:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
+
+ gg_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
+
+ h->file_info.mode = gg_fix32(h->file_info.mode);
+ h->file_info.size = gg_fix32(h->file_info.size);
+
+ h->state = GG_STATE_SENDING_FILE_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+ e->type = GG_EVENT_DCC_NEED_FILE_ACK;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
+
+ big.type = gg_fix32(0x0006); /* XXX */
+ big.dunno1 = gg_fix32(h->offset);
+ big.dunno2 = 0;
+
+ gg_write(h->fd, &big, sizeof(big));
+
+ h->state = GG_STATE_READING_FILE_HEADER;
+ h->chunk_size = sizeof(big);
+ h->chunk_offset = 0;
+ if (!(h->chunk_buf = malloc(sizeof(big)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+ free(e);
+ return NULL;
+ }
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_SENDING_VOICE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
+
+ tiny.type = 0x01; /* XXX */
+
+ gg_write(h->fd, &tiny, sizeof(tiny));
+
+ h->state = GG_STATE_READING_VOICE_HEADER;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ h->offset = 0;
+
+ return e;
+
+ case GG_STATE_READING_FILE_HEADER:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
+
+ tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+
+ h->chunk_offset += tmp;
+
+ if (h->chunk_offset < h->chunk_size)
+ return e;
+
+ memcpy(&big, h->chunk_buf, sizeof(big));
+ free(h->chunk_buf);
+ h->chunk_buf = NULL;
+
+ big.type = gg_fix32(big.type);
+ h->chunk_size = gg_fix32(big.dunno1);
+ h->chunk_offset = 0;
+
+ if (big.type == 0x0005) { /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+ return e;
+ }
+
+ if (h->chunk_size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ h->state = GG_STATE_GETTING_FILE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_HEADER:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
+
+ gg_read(h->fd, &tiny, sizeof(tiny));
+
+ switch (tiny.type) {
+ case 0x03: /* XXX */
+ h->state = GG_STATE_READING_VOICE_SIZE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+ break;
+ case 0x04: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
+ /* XXX zwracaæ odpowiedni event */
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ }
+
+ return e;
+
+ case GG_STATE_READING_VOICE_SIZE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
+
+ gg_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ if (small.type < 16 || small.type > sizeof(buf)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+
+ return e;
+ }
+
+ h->chunk_size = small.type;
+ h->chunk_offset = 0;
+
+ if (!(h->voice_buf = malloc(h->chunk_size))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+ free(e);
+ return NULL;
+ }
+
+ h->state = GG_STATE_READING_VOICE_DATA;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_DATA:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
+
+ tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+ if (tmp < 1) {
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+ } else {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n");
+ }
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp);
+
+ h->chunk_offset += tmp;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ e->type = GG_EVENT_DCC_VOICE_DATA;
+ e->event.dcc_voice_data.data = h->voice_buf;
+ e->event.dcc_voice_data.length = h->chunk_size;
+ h->state = GG_STATE_READING_VOICE_HEADER;
+ h->voice_buf = NULL;
+ }
+
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_CONNECTING:
+ {
+ uin_t uins[2];
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
+
+ res = 0;
+ if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
+
+ uins[0] = gg_fix32(h->uin);
+ uins[1] = gg_fix32(h->peer_uin);
+
+ gg_write(h->fd, uins, sizeof(uins));
+
+ h->state = GG_STATE_READING_ACK;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+ }
+
+ case GG_STATE_READING_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
+
+ gg_read(h->fd, buf, 4);
+
+ if (strncmp(buf, ack, 4)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->state = GG_STATE_SENDING_REQUEST;
+
+ return e;
+
+ case GG_STATE_SENDING_VOICE_REQUEST:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
+
+ small.type = gg_fix32(0x0003);
+
+ gg_write(h->fd, &small, sizeof(small));
+
+ h->state = GG_STATE_READING_VOICE_ACK;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_SENDING_REQUEST:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
+
+ small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */
+
+ gg_write(h->fd, &small, sizeof(small));
+
+ switch (h->type) {
+ case GG_SESSION_DCC_GET:
+ h->state = GG_STATE_READING_REQUEST;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+
+ case GG_SESSION_DCC_SEND:
+ h->state = GG_STATE_SENDING_FILE_INFO;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ if (h->file_fd == -1)
+ e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+ break;
+
+ case GG_SESSION_DCC_VOICE:
+ h->state = GG_STATE_SENDING_VOICE_REQUEST;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_INFO:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
+
+ if (h->file_fd == -1) {
+ e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+ return e;
+ }
+
+ small.type = gg_fix32(0x0001); /* XXX */
+
+ gg_write(h->fd, &small, sizeof(small));
+
+ file_info_packet.big.type = gg_fix32(0x0003); /* XXX */
+ file_info_packet.big.dunno1 = 0;
+ file_info_packet.big.dunno2 = 0;
+
+ memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
+
+ /* zostaj± teraz u nas, wiêc odwracamy z powrotem */
+ h->file_info.size = gg_fix32(h->file_info.size);
+ h->file_info.mode = gg_fix32(h->file_info.mode);
+
+ gg_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ h->state = GG_STATE_READING_FILE_ACK;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+ return e;
+
+ case GG_STATE_READING_FILE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
+
+ gg_read(h->fd, &big, sizeof(big));
+
+ /* XXX sprawdzaæ wynik */
+ h->offset = gg_fix32(big.dunno1);
+
+ h->state = GG_STATE_SENDING_FILE_HEADER;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ e->type = GG_EVENT_DCC_ACK;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
+
+ gg_read(h->fd, &tiny, sizeof(tiny));
+
+ if (tiny.type != 0x01) {
+ gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+ return e;
+ }
+
+ h->state = GG_STATE_READING_VOICE_HEADER;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ e->type = GG_EVENT_DCC_ACK;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_HEADER:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
+
+ h->chunk_offset = 0;
+
+ if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
+ h->chunk_size = 4096;
+ big.type = gg_fix32(0x0003); /* XXX */
+ } else
+ big.type = gg_fix32(0x0002); /* XXX */
+
+ big.dunno1 = gg_fix32(h->chunk_size);
+ big.dunno2 = 0;
+
+ gg_write(h->fd, &big, sizeof(big));
+
+ h->state = GG_STATE_SENDING_FILE;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
+
+ if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+ utmp = sizeof(buf);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
+
+ /* koniec pliku? */
+ if (h->file_info.size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n");
+ e->type = GG_EVENT_DCC_DONE;
+
+ return e;
+ }
+
+ lseek(h->file_fd, h->offset, SEEK_SET);
+
+ size = read(h->file_fd, buf, utmp);
+
+ /* b³±d */
+ if (size == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_FILE;
+
+ return e;
+ }
+
+ /* koniec pliku? */
+ if (size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+ return e;
+ }
+
+ /* je¶li wczytali¶my wiêcej, utnijmy. */
+ if (h->offset + size > h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
+ size = h->file_info.size - h->offset;
+
+ if (size < 1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+ }
+
+ tmp = write(h->fd, buf, size);
+
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ h->offset += size;
+
+ if (h->offset >= h->file_info.size) {
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ h->chunk_offset += size;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+ h->state = GG_STATE_SENDING_FILE_HEADER;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ h->state = GG_STATE_SENDING_FILE;
+ h->timeout = GG_DCC_TIMEOUT_SEND;
+ }
+
+ h->check = GG_CHECK_WRITE;
+
+ return e;
+
+ case GG_STATE_GETTING_FILE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
+
+ if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+ utmp = sizeof(buf);
+
+ size = read(h->fd, buf, utmp);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
+
+ /* b³±d */
+ if (size == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+
+ return e;
+ }
+
+ /* koniec? */
+ if (size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+ return e;
+ }
+
+ tmp = write(h->file_fd, buf, size);
+
+ if (tmp == -1 || tmp < size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ h->offset += size;
+
+ if (h->offset >= h->file_info.size) {
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ h->chunk_offset += size;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+ h->state = GG_STATE_READING_FILE_HEADER;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->chunk_offset = 0;
+ h->chunk_size = sizeof(big);
+ if (!(h->chunk_buf = malloc(sizeof(big)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+ free(e);
+ return NULL;
+ }
+ } else {
+ h->state = GG_STATE_GETTING_FILE;
+ h->timeout = GG_DCC_TIMEOUT_GET;
+ }
+
+ h->check = GG_CHECK_READ;
+
+ return e;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+
+ return e;
+ }
+ }
+
+ return e;
+}
+
+#undef gg_read
+#undef gg_write
+
+/*
+ * gg_dcc_free()
+ *
+ * zwalnia pamiêæ po strukturze po³±czenia dcc.
+ *
+ * - d - zwalniana struktura
+ */
+void gg_dcc_free(struct gg_dcc *d)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
+
+ if (!d)
+ return;
+
+ if (d->fd != -1)
+ close(d->fd);
+
+ if (d->chunk_buf) {
+ free(d->chunk_buf);
+ d->chunk_buf = NULL;
+ }
+
+ free(d);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/events.c b/kopete/protocols/gadu/libgadu/events.c
new file mode 100644
index 00000000..97b84912
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/events.c
@@ -0,0 +1,1580 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Wo¼ny <speedy@ziew.org>
+ * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu-config.h"
+
+#include <errno.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+# include <pthread.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+# include <openssl/err.h>
+# include <openssl/x509.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+
+/*
+ * gg_event_free()
+ *
+ * zwalnia pamiêæ zajmowan± przez informacjê o zdarzeniu.
+ *
+ * - e - wska¼nik do informacji o zdarzeniu
+ */
+void gg_event_free(struct gg_event *e)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
+
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case GG_EVENT_MSG:
+ free(e->event.msg.message);
+ free(e->event.msg.formats);
+ free(e->event.msg.recipients);
+ break;
+
+ case GG_EVENT_NOTIFY:
+ free(e->event.notify);
+ break;
+
+ case GG_EVENT_NOTIFY60:
+ {
+ int i;
+
+ for (i = 0; e->event.notify60[i].uin; i++)
+ free(e->event.notify60[i].descr);
+
+ free(e->event.notify60);
+
+ break;
+ }
+
+ case GG_EVENT_STATUS60:
+ free(e->event.status60.descr);
+ break;
+
+ case GG_EVENT_STATUS:
+ free(e->event.status.descr);
+ break;
+
+ case GG_EVENT_NOTIFY_DESCR:
+ free(e->event.notify_descr.notify);
+ free(e->event.notify_descr.descr);
+ break;
+
+ case GG_EVENT_DCC_VOICE_DATA:
+ free(e->event.dcc_voice_data.data);
+ break;
+
+ case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+ case GG_EVENT_PUBDIR50_READ:
+ case GG_EVENT_PUBDIR50_WRITE:
+ gg_pubdir50_free(e->event.pubdir50);
+ break;
+
+ case GG_EVENT_USERLIST:
+ free(e->event.userlist.reply);
+ break;
+
+ case GG_EVENT_IMAGE_REPLY:
+ free(e->event.image_reply.filename);
+ free(e->event.image_reply.image);
+ break;
+ }
+
+ free(e);
+}
+
+/*
+ * gg_image_queue_remove()
+ *
+ * usuwa z kolejki dany wpis.
+ *
+ * - s - sesja
+ * - q - kolejka
+ * - freeq - czy zwolniæ kolejkê
+ *
+ * 0/-1
+ */
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
+{
+ if (!s || !q) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (s->images == q)
+ s->images = q->next;
+ else {
+ struct gg_image_queue *qq;
+
+ for (qq = s->images; qq; qq = qq->next) {
+ if (qq->next == q) {
+ qq->next = q->next;
+ break;
+ }
+ }
+ }
+
+ if (freeq) {
+ free(q->image);
+ free(q->filename);
+ free(q);
+ }
+
+ return 0;
+}
+
+/*
+ * gg_image_queue_parse() // funkcja wewnêtrzna
+ *
+ * parsuje przychodz±cy pakiet z obrazkiem.
+ *
+ * - e - opis zdarzenia
+ * -
+ */
+static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender)
+{
+ struct gg_msg_image_reply *i = (void*) p;
+ struct gg_image_queue *q, *qq;
+
+ if (!p || !sess || !e) {
+ errno = EFAULT;
+ return;
+ }
+
+ /* znajd¼ dany obrazek w kolejce danej sesji */
+
+ for (qq = sess->images, q = NULL; qq; qq = qq->next) {
+ if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
+ q = qq;
+ break;
+ }
+ }
+
+ if (!q) {
+ gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
+ return;
+ }
+
+ if (p[0] == 0x05) {
+ int i, ok = 0;
+
+ q->done = 0;
+
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+
+ /* sprawd¼, czy mamy tekst zakoñczony \0 */
+
+ for (i = 0; i < len; i++) {
+ if (!p[i]) {
+ ok = 1;
+ break;
+ }
+ }
+
+ if (!ok) {
+ gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+ return;
+ }
+
+ if (!(q->filename = strdup(p))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+ return;
+ }
+
+ len -= strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+ }
+
+ if (q->done + len > q->size)
+ len = q->size - q->done;
+
+ memcpy(q->image + q->done, p, len);
+ q->done += len;
+
+ /* je¶li skoñczono odbieraæ obrazek, wygeneruj zdarzenie */
+
+ if (q->done >= q->size) {
+ e->type = GG_EVENT_IMAGE_REPLY;
+ e->event.image_reply.sender = sender;
+ e->event.image_reply.size = q->size;
+ e->event.image_reply.crc32 = q->crc32;
+ e->event.image_reply.filename = q->filename;
+ e->event.image_reply.image = q->image;
+
+ gg_image_queue_remove(sess, q, 0);
+
+ free(q);
+ }
+}
+
+/*
+ * gg_handle_recv_msg() // funkcja wewnêtrzna
+ *
+ * obs³uguje pakiet z przychodz±c± wiadomo¶ci±, rozbijaj±c go na dodatkowe
+ * struktury (konferencje, kolorki) w razie potrzeby.
+ *
+ * - h - nag³ówek pakietu
+ * - e - opis zdarzenia
+ *
+ * 0, -1.
+ */
+static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess)
+{
+ struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header));
+ char *p, *packet_end = (char*) r + h->length;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e);
+
+ if (!r->seq && !r->msgclass) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
+ e->type = GG_EVENT_NONE;
+ return 0;
+ }
+
+ for (p = (char*) r + sizeof(*r); *p; p++) {
+ if (*p == 0x02 && p == packet_end - 1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
+ break;
+ }
+ if (p >= packet_end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n");
+ goto malformed;
+ }
+ }
+
+ p++;
+
+ /* przeanalizuj dodatkowe opcje */
+ while (p < packet_end) {
+ switch (*p) {
+ case 0x01: /* konferencja */
+ {
+ struct gg_msg_recipients *m = (void*) p;
+ uint32_t i, count;
+
+ p += sizeof(*m);
+
+ if (p > packet_end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n");
+ goto malformed;
+ }
+
+ count = gg_fix32(m->count);
+
+ if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n");
+ goto malformed;
+ }
+
+ if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n");
+ goto fail;
+ }
+
+ for (i = 0; i < count; i++, p += sizeof(uint32_t)) {
+ uint32_t u;
+ memcpy(&u, p, sizeof(uint32_t));
+ e->event.msg.recipients[i] = gg_fix32(u);
+ }
+
+ e->event.msg.recipients_count = count;
+
+ break;
+ }
+
+ case 0x02: /* richtext */
+ {
+ uint16_t len;
+ char *buf;
+
+ if (p + 3 > packet_end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&len, p + 1, sizeof(uint16_t));
+ len = gg_fix16(len);
+
+ if (!(buf = malloc(len))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n");
+ goto fail;
+ }
+
+ p += 3;
+
+ if (p + len > packet_end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+ free(buf);
+ goto malformed;
+ }
+
+ memcpy(buf, p, len);
+
+ e->event.msg.formats = buf;
+ e->event.msg.formats_length = len;
+
+ p += len;
+
+ break;
+ }
+
+ case 0x04: /* image_request */
+ {
+ struct gg_msg_image_request *i = (void*) p;
+
+ if (p + sizeof(*i) > packet_end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n");
+ goto malformed;
+ }
+
+ e->event.image_request.sender = gg_fix32(r->sender);
+ e->event.image_request.size = gg_fix32(i->size);
+ e->event.image_request.crc32 = gg_fix32(i->crc32);
+
+ e->type = GG_EVENT_IMAGE_REQUEST;
+
+ return 0;
+ }
+
+ case 0x05: /* image_reply */
+ case 0x06:
+ {
+ struct gg_msg_image_reply *rep = (void*) p;
+
+ if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
+
+ /* pusta odpowied¼ - klient po drugiej stronie nie ma ¿±danego obrazka */
+
+ e->type = GG_EVENT_IMAGE_REPLY;
+ e->event.image_reply.sender = gg_fix32(r->sender);
+ e->event.image_reply.size = 0;
+ e->event.image_reply.crc32 = gg_fix32(rep->crc32);
+ e->event.image_reply.filename = NULL;
+ e->event.image_reply.image = NULL;
+ return 0;
+
+ } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
+
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n");
+ goto malformed;
+ }
+
+ rep->size = gg_fix32(rep->size);
+ rep->crc32 = gg_fix32(rep->crc32);
+ gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender));
+
+ return 0;
+ }
+
+ default:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p);
+ p = packet_end;
+ }
+ }
+ }
+
+ e->type = GG_EVENT_MSG;
+ e->event.msg.msgclass = gg_fix32(r->msgclass);
+ e->event.msg.sender = gg_fix32(r->sender);
+ e->event.msg.time = gg_fix32(r->time);
+ e->event.msg.message = strdup((char*) r + sizeof(*r));
+
+ return 0;
+
+malformed:
+ e->type = GG_EVENT_NONE;
+
+ free(e->event.msg.recipients);
+ free(e->event.msg.formats);
+
+ return 0;
+
+fail:
+ free(e->event.msg.recipients);
+ free(e->event.msg.formats);
+ return -1;
+}
+
+/*
+ * gg_watch_fd_connected() // funkcja wewnêtrzna
+ *
+ * patrzy na gniazdo, odbiera pakiet i wype³nia strukturê zdarzenia.
+ *
+ * - sess - struktura opisuj±ca sesjê
+ * - e - opis zdarzenia
+ *
+ * 0, -1.
+ */
+static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e)
+{
+ struct gg_header *h = NULL;
+ char *p;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (!(h = gg_recv_packet(sess))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail;
+ }
+
+ p = (char*) h + sizeof(struct gg_header);
+
+ switch (h->type) {
+ case GG_RECV_MSG:
+ {
+ if (h->length >= sizeof(struct gg_recv_msg))
+ if (gg_handle_recv_msg(h, e, sess))
+ goto fail;
+
+ break;
+ }
+
+ case GG_NOTIFY_REPLY:
+ {
+ struct gg_notify_reply *n = (void*) p;
+ unsigned int count, i;
+ char *tmp;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ if (h->length < sizeof(*n)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n");
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) {
+ e->type = GG_EVENT_NOTIFY_DESCR;
+
+ if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+ e->event.notify_descr.notify[1].uin = 0;
+ memcpy(e->event.notify_descr.notify, p, sizeof(*n));
+ e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin);
+ e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status);
+ e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port);
+
+ count = h->length - sizeof(*n);
+ if (!(tmp = malloc(count + 1))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+ memcpy(tmp, p + sizeof(*n), count);
+ tmp[count] = 0;
+ e->event.notify_descr.descr = tmp;
+
+ } else {
+ e->type = GG_EVENT_NOTIFY;
+
+ if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ memcpy(e->event.notify, p, h->length);
+ count = h->length / sizeof(*n);
+ e->event.notify[count].uin = 0;
+
+ for (i = 0; i < count; i++) {
+ e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin);
+ e->event.notify[i].status = gg_fix32(e->event.notify[i].status);
+ e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port);
+ }
+ }
+
+ break;
+ }
+
+ case GG_STATUS:
+ {
+ struct gg_status *s = (void*) p;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ if (h->length >= sizeof(*s)) {
+ e->type = GG_EVENT_STATUS;
+ memcpy(&e->event.status, p, sizeof(*s));
+ e->event.status.uin = gg_fix32(e->event.status.uin);
+ e->event.status.status = gg_fix32(e->event.status.status);
+ if (h->length > sizeof(*s)) {
+ int len = h->length - sizeof(*s);
+ char *buf = malloc(len + 1);
+ if (buf) {
+ memcpy(buf, p + sizeof(*s), len);
+ buf[len] = 0;
+ }
+ e->event.status.descr = buf;
+ } else
+ e->event.status.descr = NULL;
+ }
+
+ break;
+ }
+
+ case GG_NOTIFY_REPLY60:
+ {
+ struct gg_notify_reply60 *n = (void*) p;
+ unsigned int length = h->length, i = 0;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ e->type = GG_EVENT_NOTIFY60;
+ e->event.notify60 = malloc(sizeof(*e->event.notify60));
+
+ if (!e->event.notify60) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ e->event.notify60[0].uin = 0;
+
+ while (length >= sizeof(struct gg_notify_reply60)) {
+ uin_t uin = gg_fix32(n->uin);
+ char *tmp;
+
+ e->event.notify60[i].uin = uin & 0x00ffffff;
+ e->event.notify60[i].status = n->status;
+ e->event.notify60[i].remote_ip = n->remote_ip;
+ e->event.notify60[i].remote_port = gg_fix16(n->remote_port);
+ e->event.notify60[i].version = n->version;
+ e->event.notify60[i].image_size = n->image_size;
+ e->event.notify60[i].descr = NULL;
+ e->event.notify60[i].time = 0;
+
+ if (uin & 0x40000000)
+ e->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x08000000)
+ e->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
+
+ if (GG_S_D(n->status)) {
+ unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60));
+
+ if (descr_len < length) {
+ if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ goto fail;
+ }
+
+ memcpy(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
+ e->event.notify60[i].descr[descr_len] = 0;
+
+ /* XXX czas */
+ }
+
+ length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
+ } else {
+ length -= sizeof(struct gg_notify_reply60);
+ n = (void*) ((char*) n + sizeof(struct gg_notify_reply60));
+ }
+
+ if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n");
+ free(e->event.notify60);
+ goto fail;
+ }
+
+ e->event.notify60 = (void*) tmp;
+ e->event.notify60[++i].uin = 0;
+ }
+
+ break;
+ }
+
+ case GG_STATUS60:
+ {
+ struct gg_status60 *s = (void*) p;
+ uint32_t uin;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
+
+ if (h->length < sizeof(*s))
+ break;
+
+ uin = gg_fix32(s->uin);
+
+ e->type = GG_EVENT_STATUS60;
+ e->event.status60.uin = uin & 0x00ffffff;
+ e->event.status60.status = s->status;
+ e->event.status60.remote_ip = s->remote_ip;
+ e->event.status60.remote_port = gg_fix16(s->remote_port);
+ e->event.status60.version = s->version;
+ e->event.status60.image_size = s->image_size;
+ e->event.status60.descr = NULL;
+ e->event.status60.time = 0;
+
+ if (uin & 0x40000000)
+ e->event.status60.version |= GG_HAS_AUDIO_MASK;
+ if (uin & 0x08000000)
+ e->event.status60.version |= GG_ERA_OMNIX_MASK;
+
+ if (h->length > sizeof(*s)) {
+ int len = h->length - sizeof(*s);
+ char *buf = malloc(len + 1);
+
+ if (buf) {
+ memcpy(buf, (char*) p + sizeof(*s), len);
+ buf[len] = 0;
+ }
+
+ e->event.status60.descr = buf;
+
+ if (len > 4 && p[h->length - 5] == 0) {
+ uint32_t t;
+ memcpy(&t, p + h->length - 4, sizeof(uint32_t));
+ e->event.status60.time = gg_fix32(t);
+ }
+ }
+
+ break;
+ }
+
+ case GG_SEND_MSG_ACK:
+ {
+ struct gg_send_msg_ack *s = (void*) p;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+
+ if (h->length < sizeof(*s))
+ break;
+
+ e->type = GG_EVENT_ACK;
+ e->event.ack.status = gg_fix32(s->status);
+ e->event.ack.recipient = gg_fix32(s->recipient);
+ e->event.ack.seq = gg_fix32(s->seq);
+
+ break;
+ }
+
+ case GG_PONG:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+
+ e->type = GG_EVENT_PONG;
+ sess->last_pong = time(NULL);
+
+ break;
+ }
+
+ case GG_DISCONNECTING:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
+ e->type = GG_EVENT_DISCONNECT;
+ break;
+ }
+
+ case GG_PUBDIR50_REPLY:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
+ if (gg_pubdir50_handle_reply(e, p, h->length) == -1)
+ goto fail;
+ break;
+ }
+
+ case GG_USERLIST_REPLY:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+
+ if (h->length < 1)
+ break;
+
+ /* je¶li odpowied¼ na eksport, wywo³aj zdarzenie tylko
+ * gdy otrzymano wszystkie odpowiedzi */
+ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
+ if (--sess->userlist_blocks)
+ break;
+
+ p[0] = GG_USERLIST_PUT_REPLY;
+ }
+
+ if (h->length > 1) {
+ char *tmp;
+ unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0;
+
+ gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len);
+
+ if (!(tmp = realloc(sess->userlist_reply, len + h->length))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n");
+ free(sess->userlist_reply);
+ sess->userlist_reply = NULL;
+ goto fail;
+ }
+
+ sess->userlist_reply = tmp;
+ sess->userlist_reply[len + h->length - 1] = 0;
+ memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
+ }
+
+ if (p[0] == GG_USERLIST_GET_MORE_REPLY)
+ break;
+
+ e->type = GG_EVENT_USERLIST;
+ e->event.userlist.type = p[0];
+ e->event.userlist.reply = sess->userlist_reply;
+ sess->userlist_reply = NULL;
+
+ break;
+ }
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+ }
+
+ free(h);
+ return 0;
+
+fail:
+ free(h);
+ return -1;
+}
+
+/*
+ * gg_watch_fd()
+ *
+ * funkcja, któr± nale¿y wywo³aæ, gdy co¶ siê stanie z obserwowanym
+ * deskryptorem. zwraca klientowi informacjê o tym, co siê dzieje.
+ *
+ * - sess - opis sesji
+ *
+ * wska¼nik do struktury gg_event, któr± trzeba zwolniæ pó¼niej
+ * za pomoc± gg_event_free(). jesli rodzaj zdarzenia jest równy
+ * GG_EVENT_NONE, nale¿y je zignorowaæ. je¶li zwróci³o NULL,
+ * sta³o siê co¶ niedobrego -- albo zabrak³o pamiêci albo zerwa³o
+ * po³±czenie.
+ */
+struct gg_event *gg_watch_fd(struct gg_session *sess)
+{
+ struct gg_event *e;
+ int res = 0;
+ int port = 0;
+ int errno2 = 0;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (!(e = (void*) calloc(1, sizeof(*e)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
+ return NULL;
+ }
+
+ e->type = GG_EVENT_NONE;
+
+ switch (sess->state) {
+ case GG_STATE_RESOLVING:
+ {
+ struct in_addr addr;
+ int failed = 0;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+
+ if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ failed = 1;
+ errno2 = errno;
+ }
+
+ close(sess->fd);
+ sess->fd = -1;
+
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+ waitpid(sess->pid, NULL, 0);
+ sess->pid = -1;
+#else
+ if (sess->resolver) {
+ pthread_cancel(*((pthread_t*) sess->resolver));
+ free(sess->resolver);
+ sess->resolver = NULL;
+ }
+#endif
+
+ if (failed) {
+ errno = errno2;
+ goto fail_resolving;
+ }
+
+ /* je¶li jeste¶my w resolverze i mamy ustawiony port
+ * proxy, znaczy, ¿e resolvowali¶my proxy. zatem
+ * wpiszmy jego adres. */
+ if (sess->proxy_port)
+ sess->proxy_addr = addr.s_addr;
+
+ /* zapiszmy sobie adres huba i adres serwera (do
+ * bezpo¶redniego po³±czenia, je¶li hub le¿y)
+ * z resolvera. */
+ if (sess->proxy_addr && sess->proxy_port)
+ port = sess->proxy_port;
+ else {
+ sess->server_addr = sess->hub_addr = addr.s_addr;
+ port = GG_APPMSG_PORT;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port);
+
+ /* ³±czymy siê albo z hubem, albo z proxy, zale¿nie
+ * od tego, co resolvowali¶my. */
+ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) {
+ /* je¶li w trybie asynchronicznym gg_connect()
+ * zwróci b³±d, nie ma sensu próbowaæ dalej. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+
+ /* je¶li podano serwer i ³±czmy siê przez proxy,
+ * jest to bezpo¶rednie po³±czenie, inaczej jest
+ * do huba. */
+ sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+ case GG_STATE_CONNECTING_HUB:
+ {
+ char buf[1024], *client, *auth;
+ int res = 0, res_size = sizeof(res);
+ const char *host, *appmsg;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n");
+
+ /* je¶li asynchroniczne, sprawdzamy, czy nie wyst±pi³
+ * przypadkiem jaki¶ b³±d. */
+ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* no tak, nie uda³o siê po³±czyæ z proxy. nawet
+ * nie próbujemy dalej. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res));
+ close(sess->fd);
+
+ if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) {
+ /* przy asynchronicznych, gg_connect()
+ * zwraca -1 przy b³êdach socket(),
+ * ioctl(), braku routingu itd. dlatego
+ * nawet nie próbujemy dalej. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n");
+
+ if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+ goto fail_connecting;
+ }
+
+ if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
+ host = "http://" GG_APPMSG_HOST;
+ else
+ host = "";
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl)
+ appmsg = "appmsg3.asp";
+ else
+#endif
+ appmsg = "appmsg2.asp";
+
+ auth = gg_proxy_auth();
+
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Pragma: no-cache\r\n"
+ "%s"
+ "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : "");
+
+ if (auth)
+ free(auth);
+
+ free(client);
+
+ /* zwolnij pamiêæ po wersji klienta. */
+ if (sess->client_version) {
+ free(sess->client_version);
+ sess->client_version = NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf);
+
+ /* zapytanie jest krótkie, wiêc zawsze zmie¶ci siê
+ * do bufora gniazda. je¶li write() zwróci mniej,
+ * sta³o siê co¶ z³ego. */
+ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_WRITING;
+ sess->state = GG_STATE_IDLE;
+ close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ sess->state = GG_STATE_READING_DATA;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+ case GG_STATE_READING_DATA:
+ {
+ char buf[1024], *tmp, *host;
+ int port = GG_DEFAULT_PORT;
+ struct in_addr addr;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n");
+
+ /* czytamy liniê z gniazda i obcinamy \r\n. */
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+ gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf);
+
+ /* sprawdzamy, czy wszystko w porz±dku. */
+ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n");
+
+ close(sess->fd);
+
+ /* je¶li otrzymali¶my jakie¶ dziwne informacje,
+ * próbujemy siê ³±czyæ z pominiêciem huba. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* trudno. nie wysz³o. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ sess->port = GG_DEFAULT_PORT;
+
+ /* ³±czymy siê na port 8074 huba. */
+ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+ sess->port = GG_HTTPS_PORT;
+
+ /* ³±czymy siê na port 443. */
+ if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+ }
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ /* ignorujemy resztê nag³ówka. */
+ while (strcmp(buf, "\r\n") && strcmp(buf, ""))
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+
+ /* czytamy pierwsz± liniê danych. */
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+
+ /* je¶li pierwsza liczba w linii nie jest równa zeru,
+ * oznacza to, ¿e mamy wiadomo¶æ systemow±. */
+ if (atoi(buf)) {
+ char tmp[1024], *foo, *sysmsg_buf = NULL;
+ int len = 0;
+
+ while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) {
+ if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+ break;
+ }
+
+ sysmsg_buf = foo;
+
+ if (!len)
+ strcpy(sysmsg_buf, tmp);
+ else
+ strcat(sysmsg_buf, tmp);
+
+ len += strlen(tmp);
+ }
+
+ e->type = GG_EVENT_MSG;
+ e->event.msg.msgclass = atoi(buf);
+ e->event.msg.sender = 0;
+ e->event.msg.message = sysmsg_buf;
+ }
+
+ close(sess->fd);
+
+ gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+ /* analizujemy otrzymane dane. */
+ tmp = buf;
+
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ host = tmp;
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ *tmp = 0;
+
+ if ((tmp = strchr(host, ':'))) {
+ *tmp = 0;
+ port = atoi(tmp + 1);
+ }
+
+ if (!strcmp(host, "notoperating")) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+ sess->fd = -1;
+ goto fail_unavailable;
+ }
+
+ addr.s_addr = inet_addr(host);
+ sess->server_addr = addr.s_addr;
+
+ if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
+ /* je¶li mamy proxy, ³±czymy siê z nim. */
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* nie wysz³o? trudno. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ sess->port = port;
+
+ /* ³±czymy siê z w³a¶ciwym serwerem. */
+ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+ sess->port = GG_HTTPS_PORT;
+
+ /* nie wysz³o? próbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawiod³a?
+ * w takim razie zwijamy manatki. */
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+ }
+
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+ case GG_STATE_CONNECTING_GG:
+ {
+ int res = 0, res_size = sizeof(res);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n");
+
+ /* je¶li wyst±pi³ b³±d podczas ³±czenia siê... */
+ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* je¶li nie uda³o siê po³±czenie z proxy,
+ * nie mamy czego próbowaæ wiêcej. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+
+ close(sess->fd);
+ sess->fd = -1;
+
+#ifdef ETIMEDOUT
+ if (sess->timeout == 0)
+ errno = ETIMEDOUT;
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ /* je¶li logujemy siê po TLS, nie próbujemy
+ * siê ³±czyæ ju¿ z niczym innym w przypadku
+ * b³êdu. nie do¶æ, ¿e nie ma sensu, to i
+ * trzeba by siê bawiæ w tworzenie na nowo
+ * SSL i SSL_CTX. */
+
+ if (sess->ssl) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+#endif
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res));
+
+ sess->port = GG_HTTPS_PORT;
+
+ /* próbujemy na port 443. */
+ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_connecting;
+ }
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
+ if (gg_proxy_http_only)
+ sess->proxy_port = 0;
+
+ /* je¶li mamy proxy, wy¶lijmy zapytanie. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ char buf[100], *auth = gg_proxy_auth();
+ struct in_addr addr;
+
+ if (sess->server_addr)
+ addr.s_addr = sess->server_addr;
+ else
+ addr.s_addr = sess->hub_addr;
+
+ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf);
+
+ /* wysy³amy zapytanie. jest ono na tyle krótkie,
+ * ¿e musi siê zmie¶ciæ w buforze gniazda. je¶li
+ * write() zawiedzie, sta³o siê co¶ z³ego. */
+ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ if (auth)
+ free(auth);
+ goto fail_connecting;
+ }
+
+ if (auth) {
+ gg_debug(GG_DEBUG_MISC, "// %s", auth);
+ if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ free(auth);
+ goto fail_connecting;
+ }
+
+ free(auth);
+ }
+
+ if (write(sess->fd, "\r\n", 2) < 2) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ goto fail_connecting;
+ }
+ }
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl) {
+ SSL_set_fd(sess->ssl, sess->fd);
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ int res;
+ X509 *peer;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+ if ((res = SSL_connect(sess->ssl)) <= 0) {
+ int err = SSL_get_error(sess->ssl, res);
+
+ if (res == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ if (err == SSL_ERROR_WANT_READ) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else {
+ char buf[1024];
+
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl));
+
+ peer = SSL_get_peer_certificate(sess->ssl);
+
+ if (!peer)
+ gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
+ else {
+ char buf[1024];
+
+ X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf);
+
+ X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
+ }
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ case GG_STATE_READING_KEY:
+ {
+ struct gg_header *h;
+ struct gg_welcome *w;
+ struct gg_login60 l;
+ unsigned int hash;
+ unsigned char *password = sess->password;
+ int ret;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n");
+
+ memset(&l, 0, sizeof(l));
+ l.dunno2 = 0xbe;
+
+ /* XXX bardzo, bardzo, bardzo g³upi pomys³ na pozbycie
+ * siê tekstu wrzucanego przez proxy. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ char buf[100];
+
+ strcpy(buf, "");
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf);
+
+ while (strcmp(buf, "")) {
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+ if (strcmp(buf, ""))
+ gg_debug(GG_DEBUG_MISC, "// %s\n", buf);
+ }
+
+ /* XXX niech czeka jeszcze raz w tej samej
+ * fazie. g³upio, ale dzia³a. */
+ sess->proxy_port = 0;
+
+ break;
+ }
+
+ /* czytaj pierwszy pakiet. */
+ if (!(h = gg_recv_packet(sess))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_READING;
+ sess->state = GG_STATE_IDLE;
+ errno2 = errno;
+ close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ break;
+ }
+
+ if (h->type != GG_WELCOME) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+ free(h);
+ close(sess->fd);
+ sess->fd = -1;
+ errno = EINVAL;
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_INVALID;
+ sess->state = GG_STATE_IDLE;
+ break;
+ }
+
+ w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
+ w->key = gg_fix32(w->key);
+
+ hash = gg_login_hash(password, w->key);
+
+ gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash);
+
+ free(h);
+
+ free(sess->password);
+ sess->password = NULL;
+
+ {
+ struct in_addr dcc_ip;
+ dcc_ip.s_addr = gg_dcc_ip;
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip));
+ }
+
+ if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
+ struct sockaddr_in sin;
+ int sin_len = sizeof(sin);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+
+ if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr));
+ l.local_ip = sin.sin_addr.s_addr;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+ l.local_ip = 0;
+ }
+ } else
+ l.local_ip = gg_dcc_ip;
+
+ l.uin = gg_fix32(sess->uin);
+ l.hash = gg_fix32(hash);
+ l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL);
+ l.version = gg_fix32(sess->protocol_version);
+ l.local_port = gg_fix16(gg_dcc_port);
+ l.image_size = sess->image_size;
+
+ if (sess->external_addr && sess->external_port > 1023) {
+ l.external_ip = sess->external_addr;
+ l.external_port = gg_fix16(sess->external_port);
+ }
+
+ gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n");
+ ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL);
+
+ free(sess->initial_descr);
+ sess->initial_descr = NULL;
+
+ if (ret == -1) {
+ gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+ errno2 = errno;
+ close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_WRITING;
+ sess->state = GG_STATE_IDLE;
+ break;
+ }
+
+ sess->state = GG_STATE_READING_REPLY;
+
+ break;
+ }
+
+ case GG_STATE_READING_REPLY:
+ {
+ struct gg_header *h;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+
+ if (!(h = gg_recv_packet(sess))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno));
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_READING;
+ sess->state = GG_STATE_IDLE;
+ errno2 = errno;
+ close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ break;
+ }
+
+ if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
+ e->type = GG_EVENT_CONN_SUCCESS;
+ sess->state = GG_STATE_CONNECTED;
+ sess->timeout = -1;
+ sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL;
+ free(h);
+ break;
+ }
+
+ if (h->type == GG_LOGIN_FAILED) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
+ e->event.failure = GG_FAILURE_PASSWORD;
+ errno = EACCES;
+ } else if (h->type == GG_DISCONNECTING) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
+ e->event.failure = GG_FAILURE_INTRUDER;
+ errno = EACCES;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+ e->event.failure = GG_FAILURE_INVALID;
+ errno = EINVAL;
+ }
+
+ e->type = GG_EVENT_CONN_FAILED;
+ sess->state = GG_STATE_IDLE;
+ errno2 = errno;
+ close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ free(h);
+
+ break;
+ }
+
+ case GG_STATE_CONNECTED:
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+
+ sess->last_event = time(NULL);
+
+ if ((res = gg_watch_fd_connected(sess, e)) == -1) {
+
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno));
+
+ if (errno == EAGAIN) {
+ e->type = GG_EVENT_NONE;
+ res = 0;
+ } else
+ res = -1;
+ }
+ break;
+ }
+ }
+
+done:
+ if (res == -1) {
+ free(e);
+ e = NULL;
+ }
+
+ return e;
+
+fail_connecting:
+ if (sess->fd != -1) {
+ errno2 = errno;
+ close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ }
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ sess->state = GG_STATE_IDLE;
+ goto done;
+
+fail_resolving:
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_RESOLVING;
+ sess->state = GG_STATE_IDLE;
+ goto done;
+
+fail_unavailable:
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_UNAVAILABLE;
+ sess->state = GG_STATE_IDLE;
+ goto done;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/http.c b/kopete/protocols/gadu/libgadu/http.c
new file mode 100644
index 00000000..77ebb319
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/http.c
@@ -0,0 +1,522 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "libgadu-config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+# include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "compat.h"
+#include "libgadu.h"
+
+/*
+ * gg_http_connect() // funkcja pomocnicza
+ *
+ * rozpoczyna po³±czenie po http.
+ *
+ * - hostname - adres serwera
+ * - port - port serwera
+ * - async - asynchroniczne po³±czenie
+ * - method - metoda http (GET, POST, cokolwiek)
+ * - path - ¶cie¿ka do zasobu (musi byæ poprzedzona ,,/'')
+ * - header - nag³ówek zapytania plus ewentualne dane dla POST
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y
+ * zwolniæ funkcj± gg_http_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
+{
+ struct gg_http *h;
+
+ if (!hostname || !port || !method || !path || !header) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (!(h = malloc(sizeof(*h))))
+ return NULL;
+ memset(h, 0, sizeof(*h));
+
+ h->async = async;
+ h->port = port;
+ h->fd = -1;
+ h->type = GG_SESSION_HTTP;
+
+ if (gg_proxy_enabled) {
+ char *auth = gg_proxy_auth();
+
+ h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
+ method, hostname, port, path, (auth) ? auth :
+ "", header);
+ hostname = gg_proxy_host;
+ h->port = port = gg_proxy_port;
+
+ if (auth)
+ free(auth);
+ } else {
+ h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
+ method, path, header);
+ }
+
+ if (!h->query) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
+ free(h);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
+
+ if (async) {
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+ if (gg_resolve(&h->fd, &h->pid, hostname)) {
+#else
+ if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) {
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
+ gg_http_free(h);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
+
+ h->state = GG_STATE_RESOLVING;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ struct in_addr *hn, a;
+
+ if (!(hn = gg_gethostbyname(hostname))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
+ gg_http_free(h);
+ errno = ENOENT;
+ return NULL;
+ } else {
+ a.s_addr = hn->s_addr;
+ free(hn);
+ }
+
+ if (!(h->fd = gg_connect(&a, port, 0)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_http_free(h);
+ return NULL;
+ }
+
+ h->state = GG_STATE_CONNECTING;
+
+ while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1)
+ break;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
+ gg_http_free(h);
+ return NULL;
+ }
+ }
+
+ h->callback = gg_http_watch_fd;
+ h->destroy = gg_http_free;
+
+ return h;
+}
+
+#define gg_http_error(x) \
+ close(h->fd); \
+ h->fd = -1; \
+ h->state = GG_STATE_ERROR; \
+ h->error = x; \
+ return 0;
+
+/*
+ * gg_http_watch_fd()
+ *
+ * przy asynchronicznej obs³udze HTTP funkcjê t± nale¿y wywo³aæ, je¶li
+ * zmieni³o siê co¶ na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisuj±ca po³±czenie
+ *
+ * je¶li wszystko posz³o dobrze to 0, inaczej -1. po³±czenie bêdzie
+ * zakoñczone, je¶li h->state == GG_STATE_PARSING. je¶li wyst±pi jaki¶
+ * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error.
+ */
+int gg_http_watch_fd(struct gg_http *h)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
+
+ if (!h) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_RESOLVING) {
+ struct in_addr a;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
+
+ if (read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) {
+ gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
+ gg_http_error(GG_ERROR_RESOLVING);
+ }
+
+ close(h->fd);
+ h->fd = -1;
+
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+ waitpid(h->pid, NULL, 0);
+#else
+ if (h->resolver) {
+ pthread_cancel(*((pthread_t *) h->resolver));
+ free(h->resolver);
+ h->resolver = NULL;
+ }
+#endif
+
+ gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
+
+ if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_http_error(GG_ERROR_CONNECTING);
+ }
+
+ h->state = GG_STATE_CONNECTING;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_CONNECTING) {
+ int res = 0;
+ unsigned int res_size = sizeof(res);
+
+ if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno));
+ close(h->fd);
+ h->fd = -1;
+ h->state = GG_STATE_ERROR;
+ h->error = GG_ERROR_CONNECTING;
+ if (res)
+ errno = res;
+ return 0;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
+
+ h->state = GG_STATE_SENDING_QUERY;
+ }
+
+ if (h->state == GG_STATE_SENDING_QUERY) {
+ int res;
+
+ if ((res = write(h->fd, h->query, strlen(h->query))) < 1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno);
+ gg_http_error(GG_ERROR_WRITING);
+ }
+
+ if (res < strlen(h->query)) {
+ gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res);
+
+ memmove(h->query, h->query + res, strlen(h->query) - res + 1);
+ h->state = GG_STATE_SENDING_QUERY;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query));
+ free(h->query);
+ h->query = NULL;
+
+ h->state = GG_STATE_READING_HEADER;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_READING_HEADER) {
+ char buf[1024], *tmp;
+ int res;
+
+ if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (!res) {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
+
+ if (!(tmp = realloc(h->header, h->header_size + res + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ h->header = tmp;
+
+ memcpy(h->header + h->header_size, buf, res);
+ h->header_size += res;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
+
+ h->header[h->header_size] = 0;
+
+ if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
+ int sep_len = (*tmp == '\r') ? 4 : 2;
+ unsigned int left;
+ char *line;
+
+ left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len);
+
+ gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left);
+
+ /* HTTP/1.1 200 OK */
+ if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+ gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_CONNECTING);
+ }
+
+ h->body_size = 0;
+ line = h->header;
+ *tmp = 0;
+
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+ while (line) {
+ if (!strncasecmp(line, "Content-length: ", 16)) {
+ h->body_size = atoi(line + 16);
+ }
+ line = strchr(line, '\n');
+ if (line)
+ line++;
+ }
+
+ if (h->body_size <= 0) {
+ gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
+ h->body_size = left;
+ }
+
+ if (left > h->body_size) {
+ gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
+ h->body_size = left;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
+
+ if (!(h->body = malloc(h->body_size + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1);
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (left) {
+ memcpy(h->body, tmp + sep_len, left);
+ h->body_done = left;
+ }
+
+ h->body[left] = 0;
+
+ h->state = GG_STATE_READING_DATA;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_READING_DATA) {
+ char buf[1024];
+ int res;
+
+ if ((res = read(h->fd, buf, sizeof(buf))) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (!res) {
+ if (h->body_done >= h->body_size) {
+ gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
+ h->state = GG_STATE_PARSING;
+ close(h->fd);
+ h->fd = -1;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size);
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ return 0;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
+
+ if (h->body_done + res > h->body_size) {
+ char *tmp;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size);
+
+ if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1);
+ free(h->body);
+ h->body = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ h->body = tmp;
+ h->body_size = h->body_done + res;
+ }
+
+ h->body[h->body_done + res] = 0;
+ memcpy(h->body + h->body_done, buf, res);
+ h->body_done += res;
+
+ gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
+
+ return 0;
+ }
+
+ if (h->fd != -1)
+ close(h->fd);
+
+ h->fd = -1;
+ h->state = GG_STATE_ERROR;
+ h->error = 0;
+
+ return -1;
+}
+
+#undef gg_http_error
+
+/*
+ * gg_http_stop()
+ *
+ * je¶li po³±czenie jest w trakcie, przerywa je. nie zwalnia h->data.
+ *
+ * - h - struktura opisuj±ca po³±czenie
+ */
+void gg_http_stop(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
+ return;
+
+ if (h->fd != -1)
+ close(h->fd);
+ h->fd = -1;
+}
+
+/*
+ * gg_http_free_fields() // funkcja wewnêtrzna
+ *
+ * zwalnia pola struct gg_http, ale nie zwalnia samej struktury.
+ */
+void gg_http_free_fields(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+
+ if (h->query) {
+ free(h->query);
+ h->query = NULL;
+ }
+
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+}
+
+/*
+ * gg_http_free()
+ *
+ * próbuje zamkn±æ po³±czenie i zwalnia pamiêæ po nim.
+ *
+ * - h - struktura, któr± nale¿y zlikwidowaæ
+ */
+void gg_http_free(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ gg_http_stop(h);
+ gg_http_free_fields(h);
+ free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/libgadu-config.h.in b/kopete/protocols/gadu/libgadu/libgadu-config.h.in
new file mode 100644
index 00000000..dc4fb435
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/libgadu-config.h.in
@@ -0,0 +1,30 @@
+/* Local libgadu configuration. */
+
+#ifndef __GG_LIBGADU_CONFIG_H
+#define __GG_LIBGADU_CONFIG_H
+
+/* Defined if libgadu was compiled for bigendian machine. */
+#undef __GG_LIBGADU_BIGENDIAN
+
+/* Defined if libgadu was compiled and linked with pthread support. */
+#define __GG_LIBGADU_HAVE_PTHREAD
+
+/* Defined if this machine has C99-compiliant vsnprintf(). */
+#undef __GG_LIBGADU_HAVE_C99_VSNPRINTF
+
+/* Defined if this machine has va_copy(). */
+#undef __GG_LIBGADU_HAVE_VA_COPY
+
+/* Defined if this machine has __va_copy(). */
+#undef __GG_LIBGADU_HAVE___VA_COPY
+
+/* Defined if this machine supports long long. */
+#undef __GG_LIBGADU_HAVE_LONG_LONG
+
+/* Defined if libgadu was compiled and linked with TLS support. */
+#undef __GG_LIBGADU_HAVE_OPENSSL
+
+/* Include file containing uintXX_t declarations. */
+#include <inttypes.h>
+
+#endif /* __GG_LIBGADU_CONFIG_H */
diff --git a/kopete/protocols/gadu/libgadu/libgadu.c b/kopete/protocols/gadu/libgadu/libgadu.c
new file mode 100644
index 00000000..47b687f0
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/libgadu.c
@@ -0,0 +1,1818 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Wo¼ny <speedy@ziew.org>
+ * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliñski <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+
+#include "libgadu-config.h"
+
+#include <errno.h>
+#include <netdb.h>
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+# include <pthread.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+# include <openssl/err.h>
+# include <openssl/rand.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+
+int gg_debug_level = 0;
+void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
+
+int gg_dcc_port = 0;
+unsigned long gg_dcc_ip = 0;
+
+unsigned long gg_local_ip = 0;
+/*
+ * zmienne opisuj±ce parametry proxy http.
+ */
+char *gg_proxy_host = NULL;
+int gg_proxy_port = 0;
+int gg_proxy_enabled = 0;
+int gg_proxy_http_only = 0;
+char *gg_proxy_username = NULL;
+char *gg_proxy_password = NULL;
+
+#ifndef lint
+static char rcsid[]
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+= "$Id$";
+#endif
+
+/*
+ * gg_libgadu_version()
+ *
+ * zwraca wersjê libgadu.
+ *
+ * - brak
+ *
+ * wersja libgadu.
+ */
+const char *gg_libgadu_version()
+{
+ return GG_LIBGADU_VERSION;
+}
+
+/*
+ * gg_fix32()
+ *
+ * zamienia kolejno¶æ bajtów w liczbie 32-bitowej tak, by odpowiada³a
+ * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ * - x - liczba do zamiany
+ *
+ * liczba z odpowiedni± kolejno¶ci± bajtów.
+ */
+uint32_t gg_fix32(uint32_t x)
+{
+#ifndef __GG_LIBGADU_BIGENDIAN
+ return x;
+#else
+ return (uint32_t)
+ (((x & (uint32_t) 0x000000ffU) << 24) |
+ ((x & (uint32_t) 0x0000ff00U) << 8) |
+ ((x & (uint32_t) 0x00ff0000U) >> 8) |
+ ((x & (uint32_t) 0xff000000U) >> 24));
+#endif
+}
+
+/*
+ * gg_fix16()
+ *
+ * zamienia kolejno¶æ bajtów w liczbie 16-bitowej tak, by odpowiada³a
+ * kolejno¶ci bajtów w protokole GG. ze wzglêdu na LE-owo¶æ serwera,
+ * zamienia tylko na maszynach BE-wych.
+ *
+ * - x - liczba do zamiany
+ *
+ * liczba z odpowiedni± kolejno¶ci± bajtów.
+ */
+uint16_t gg_fix16(uint16_t x)
+{
+#ifndef __GG_LIBGADU_BIGENDIAN
+ return x;
+#else
+ return (uint16_t)
+ (((x & (uint16_t) 0x00ffU) << 8) |
+ ((x & (uint16_t) 0xff00U) >> 8));
+#endif
+}
+
+/*
+ * gg_login_hash() // funkcja wewnêtrzna
+ *
+ * liczy hash z has³a i danego seeda.
+ *
+ * - password - has³o do hashowania
+ * - seed - warto¶æ podana przez serwer
+ *
+ * hash.
+ */
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
+{
+ unsigned int x, y, z;
+
+ y = seed;
+
+ for (x = 0; *password; password++) {
+ x = (x & 0xffffff00) | *password;
+ y ^= x;
+ y += x;
+ x <<= 8;
+ y ^= x;
+ x <<= 8;
+ y -= x;
+ x <<= 8;
+ y ^= x;
+
+ z = y & 0x1F;
+ y = (y << z) | (y >> (32 - z));
+ }
+
+ return y;
+}
+
+/*
+ * gg_resolve() // funkcja wewnêtrzna
+ *
+ * tworzy potok, forkuje siê i w drugim procesie zaczyna resolvowaæ
+ * podanego hosta. zapisuje w sesji deskryptor potoku. je¶li co¶ tam
+ * bêdzie gotowego, znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li
+ * nie znajdzie, zwraca INADDR_NONE.
+ *
+ * - fd - wska¼nik gdzie wrzuciæ deskryptor
+ * - pid - gdzie wrzuciæ pid procesu potomnego
+ * - hostname - nazwa hosta do zresolvowania
+ *
+ * 0, -1.
+ */
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+ int pipes[2], res;
+ struct in_addr a;
+ int errno2;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname);
+
+ if (!fd || !pid) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (pipe(pipes) == -1)
+ return -1;
+
+ if ((res = fork()) == -1) {
+ errno2 = errno;
+ close(pipes[0]);
+ close(pipes[1]);
+ errno = errno2;
+ return -1;
+ }
+
+ if (!res) {
+ close(pipes[0]);
+
+ if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+ struct in_addr *hn;
+
+ if (!(hn = gg_gethostbyname(hostname)))
+ a.s_addr = INADDR_NONE;
+ else {
+ a.s_addr = hn->s_addr;
+ free(hn);
+ }
+ }
+
+ write(pipes[1], &a, sizeof(a));
+
+ exit(0);
+ }
+
+ close(pipes[1]);
+
+ *fd = pipes[0];
+ *pid = res;
+
+ return 0;
+}
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+
+struct gg_resolve_pthread_data {
+ char *hostname;
+ int fd;
+};
+
+static void *gg_resolve_pthread_thread(void *arg)
+{
+ struct gg_resolve_pthread_data *d = arg;
+ struct in_addr a;
+
+ pthread_detach(pthread_self());
+
+ if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) {
+ struct in_addr *hn;
+
+ if (!(hn = gg_gethostbyname(d->hostname)))
+ a.s_addr = INADDR_NONE;
+ else {
+ a.s_addr = hn->s_addr;
+ free(hn);
+ }
+ }
+
+ write(d->fd, &a, sizeof(a));
+ close(d->fd);
+
+ free(d->hostname);
+ d->hostname = NULL;
+
+ free(d);
+
+ pthread_exit(NULL);
+
+ return NULL; /* ¿eby kompilator nie marudzi³ */
+}
+
+/*
+ * gg_resolve_pthread() // funkcja wewnêtrzna
+ *
+ * tworzy potok, nowy w±tek i w nim zaczyna resolvowaæ podanego hosta.
+ * zapisuje w sesji deskryptor potoku. je¶li co¶ tam bêdzie gotowego,
+ * znaczy, ¿e mo¿na wczytaæ struct in_addr. je¶li nie znajdzie, zwraca
+ * INADDR_NONE.
+ *
+ * - fd - wska¼nik do zmiennej przechowuj±cej desktyptor resolvera
+ * - resolver - wska¼nik do wska¼nika resolvera
+ * - hostname - nazwa hosta do zresolvowania
+ *
+ * 0, -1.
+ */
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+ struct gg_resolve_pthread_data *d = NULL;
+ pthread_t *tmp;
+ int pipes[2], new_errno;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname);
+
+ if (!resolver || !fd || !hostname) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (!(tmp = malloc(sizeof(pthread_t)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n");
+ return -1;
+ }
+
+ if (pipe(pipes) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno));
+ free(tmp);
+ return -1;
+ }
+
+ if (!(d = malloc(sizeof(*d)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ d->hostname = NULL;
+
+ if (!(d->hostname = strdup(hostname))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n");
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ d->fd = pipes[1];
+
+ if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n");
+ new_errno = errno;
+ goto cleanup;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp);
+
+ *resolver = tmp;
+
+ *fd = pipes[0];
+
+ return 0;
+
+cleanup:
+ if (d) {
+ free(d->hostname);
+ free(d);
+ }
+
+ close(pipes[0]);
+ close(pipes[1]);
+
+ free(tmp);
+
+ errno = new_errno;
+
+ return -1;
+}
+
+#endif
+
+/*
+ * gg_read() // funkcja pomocnicza
+ *
+ * czyta z gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy
+ * po³±czenie zwyk³e czy TLS.
+ *
+ * - sess - sesja,
+ * - buf - bufor,
+ * - length - ilo¶æ bajtów,
+ *
+ * takie same warto¶ci jak read().
+ */
+int gg_read(struct gg_session *sess, char *buf, int length)
+{
+ int res;
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl) {
+ int err;
+
+ res = SSL_read(sess->ssl, buf, length);
+
+ if (res < 0) {
+ err = SSL_get_error(sess->ssl, res);
+
+ if (err == SSL_ERROR_WANT_READ)
+ errno = EAGAIN;
+
+ return -1;
+ }
+ } else
+#endif
+ res = read(sess->fd, buf, length);
+
+ return res;
+}
+
+/*
+ * gg_write() // funkcja pomocnicza
+ *
+ * zapisuje do gniazda okre¶lon± ilo¶æ bajtów. bierze pod uwagê, czy mamy
+ * po³±czenie zwyk³e czy TLS.
+ *
+ * - sess - sesja,
+ * - buf - bufor,
+ * - length - ilo¶æ bajtów,
+ *
+ * takie same warto¶ci jak write().
+ */
+int gg_write(struct gg_session *sess, const char *buf, int length)
+{
+ int res = 0;
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl) {
+ int err;
+
+ res = SSL_write(sess->ssl, buf, length);
+
+ if (res < 0) {
+ err = SSL_get_error(sess->ssl, res);
+
+ if (err == SSL_ERROR_WANT_WRITE)
+ errno = EAGAIN;
+
+ return -1;
+ }
+ } else
+#endif
+ {
+ int written = 0;
+
+ while (written < length) {
+ res = write(sess->fd, buf + written, length - written);
+
+ if (res == -1) {
+ if (errno == EAGAIN)
+ continue;
+ else
+ break;
+ } else {
+ written += res;
+ res = written;
+ }
+ }
+ }
+
+ return res;
+}
+
+/*
+ * gg_recv_packet() // funkcja wewnêtrzna
+ *
+ * odbiera jeden pakiet i zwraca wska¼nik do niego. pamiêæ po nim
+ * nale¿y zwolniæ za pomoc± free().
+ *
+ * - sess - opis sesji
+ *
+ * w przypadku b³êdu NULL, kod b³êdu w errno. nale¿y zwróciæ uwagê, ¿e gdy
+ * po³±czenie jest nieblokuj±ce, a kod b³êdu wynosi EAGAIN, nie uda³o siê
+ * odczytaæ ca³ego pakietu i nie nale¿y tego traktowaæ jako b³±d.
+ */
+void *gg_recv_packet(struct gg_session *sess)
+{
+ struct gg_header h;
+ char *buf = NULL;
+ int ret = 0;
+ unsigned int offset, size = 0;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (sess->recv_left < 1) {
+ if (sess->header_buf) {
+ memcpy(&h, sess->header_buf, sess->header_done);
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done);
+ free(sess->header_buf);
+ sess->header_buf = NULL;
+ } else
+ sess->header_done = 0;
+
+ while (sess->header_done < sizeof(h)) {
+ ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret);
+
+ if (!ret) {
+ errno = ECONNRESET;
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
+ return NULL;
+ }
+
+ if (ret == -1) {
+ if (errno == EINTR) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n");
+ continue;
+ }
+
+ if (errno == EAGAIN) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n");
+
+ if (!(sess->header_buf = malloc(sess->header_done))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n");
+ return NULL;
+ }
+
+ memcpy(sess->header_buf, &h, sess->header_done);
+
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+
+ return NULL;
+ }
+
+ sess->header_done += ret;
+
+ }
+
+ h.type = gg_fix32(h.type);
+ h.length = gg_fix32(h.length);
+ } else
+ memcpy(&h, sess->recv_buf, sizeof(h));
+
+ /* jakie¶ sensowne limity na rozmiar pakietu */
+ if (h.length > 65535) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length);
+ errno = ERANGE;
+ return NULL;
+ }
+
+ if (sess->recv_left > 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n");
+ size = sess->recv_left;
+ offset = sess->recv_done;
+ buf = sess->recv_buf;
+ } else {
+ if (!(buf = malloc(sizeof(h) + h.length + 1))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
+ return NULL;
+ }
+
+ memcpy(buf, &h, sizeof(h));
+
+ offset = 0;
+ size = h.length;
+ }
+
+ while (size > 0) {
+ ret = gg_read(sess, buf + sizeof(h) + offset, size);
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret);
+ if (!ret) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
+ errno = ECONNRESET;
+ return NULL;
+ }
+ if (ret > -1 && ret <= size) {
+ offset += ret;
+ size -= ret;
+ } else if (ret == -1) {
+ int errno2 = errno;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
+ errno = errno2;
+
+ if (errno == EAGAIN) {
+ gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size);
+ sess->recv_buf = buf;
+ sess->recv_left = size;
+ sess->recv_done = offset;
+ return NULL;
+ }
+ if (errno != EINTR) {
+ free(buf);
+ return NULL;
+ }
+ }
+ }
+
+ sess->recv_left = 0;
+
+ if ((gg_debug_level & GG_DEBUG_DUMP)) {
+ unsigned int i;
+
+ gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type);
+ for (i = 0; i < sizeof(h) + h.length; i++)
+ gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]);
+ gg_debug(GG_DEBUG_DUMP, "\n");
+ }
+
+ return buf;
+}
+
+/*
+ * gg_send_packet() // funkcja wewnêtrzna
+ *
+ * konstruuje pakiet i wysy³a go do serwera.
+ *
+ * - sock - deskryptor gniazda
+ * - type - typ pakietu
+ * - payload_1 - pierwsza czê¶æ pakietu
+ * - payload_length_1 - d³ugo¶æ pierwszej czê¶ci
+ * - payload_2 - druga czê¶æ pakietu
+ * - payload_length_2 - d³ugo¶æ drugiej czê¶ci
+ * - ... - kolejne czê¶ci pakietu i ich d³ugo¶ci
+ * - NULL - koñcowym parametr (konieczny!)
+ *
+ * je¶li siê powiod³o, zwraca 0, w przypadku b³êdu -1. je¶li errno == ENOMEM,
+ * zabrak³o pamiêci. inaczej by³ b³±d przy wysy³aniu pakietu. dla errno == 0
+ * nie wys³ano ca³ego pakietu.
+ */
+int gg_send_packet(struct gg_session *sess, int type, ...)
+{
+ struct gg_header *h;
+ char *tmp;
+ unsigned int tmp_length;
+ void *payload;
+ unsigned int payload_length;
+ va_list ap;
+ int res;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type);
+
+ tmp_length = sizeof(struct gg_header);
+
+ if (!(tmp = malloc(tmp_length))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
+ return -1;
+ }
+
+ va_start(ap, type);
+
+ payload = va_arg(ap, void *);
+
+ while (payload) {
+ char *tmp2;
+
+ payload_length = va_arg(ap, unsigned int);
+
+ if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n");
+ free(tmp);
+ va_end(ap);
+ return -1;
+ }
+
+ tmp = tmp2;
+
+ memcpy(tmp + tmp_length, payload, payload_length);
+ tmp_length += payload_length;
+
+ payload = va_arg(ap, void *);
+ }
+
+ va_end(ap);
+
+ h = (struct gg_header*) tmp;
+ h->type = gg_fix32(type);
+ h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
+
+ if ((gg_debug_level & GG_DEBUG_DUMP)) {
+ unsigned int i;
+
+ gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+ for (i = 0; i < tmp_length; ++i)
+ gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]);
+ gg_debug(GG_DEBUG_DUMP, "\n");
+ }
+
+ if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) {
+ gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno));
+ free(tmp);
+ return -1;
+ }
+
+ free(tmp);
+ return 0;
+}
+
+/*
+ * gg_session_callback() // funkcja wewnêtrzna
+ *
+ * wywo³ywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje
+ * do gg_session->event jego wynik.
+ */
+static int gg_session_callback(struct gg_session *s)
+{
+ if (!s) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1;
+}
+
+/*
+ * gg_login()
+ *
+ * rozpoczyna procedurê ³±czenia siê z serwerem. resztê obs³uguje siê przez
+ * gg_watch_fd().
+ *
+ * UWAGA! program musi obs³u¿yæ SIGCHLD, je¶li ³±czy siê asynchronicznie,
+ * ¿eby poprawnie zamkn±æ proces resolvera.
+ *
+ * - p - struktura opisuj±ca pocz±tkowy stan. wymagane pola: uin,
+ * password
+ *
+ * w przypadku b³êdu NULL, je¶li idzie dobrze (async) albo posz³o
+ * dobrze (sync), zwróci wska¼nik do zaalokowanej struct gg_session.
+ */
+struct gg_session *gg_login(const struct gg_login_params *p)
+{
+ struct gg_session *sess = NULL;
+ char *hostname;
+ int port;
+
+ if (!p) {
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
+ errno = EFAULT;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
+
+ if (!(sess = malloc(sizeof(struct gg_session)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
+ goto fail;
+ }
+
+ memset(sess, 0, sizeof(struct gg_session));
+
+ if (!p->password || !p->uin) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
+ errno = EFAULT;
+ goto fail;
+ }
+
+ if (!(sess->password = strdup(p->password))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
+ goto fail;
+ }
+
+ if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
+ goto fail;
+ }
+
+ sess->uin = p->uin;
+ sess->state = GG_STATE_RESOLVING;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ sess->async = p->async;
+ sess->type = GG_SESSION_GG;
+ sess->initial_status = p->status;
+ sess->callback = gg_session_callback;
+ sess->destroy = gg_free_session;
+ sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT);
+ sess->server_addr = p->server_addr;
+ sess->external_port = p->external_port;
+ sess->external_addr = p->external_addr;
+ sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+ if (p->era_omnix)
+ sess->protocol_version |= GG_ERA_OMNIX_MASK;
+ if (p->has_audio)
+ sess->protocol_version |= GG_HAS_AUDIO_MASK;
+ sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL;
+ sess->last_sysmsg = p->last_sysmsg;
+ sess->image_size = p->image_size;
+ sess->pid = -1;
+
+ if (p->tls == 1) {
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ char buf[1024];
+
+ OpenSSL_add_ssl_algorithms();
+
+ if (!RAND_status()) {
+ char rdata[1024];
+ struct {
+ time_t time;
+ void *ptr;
+ } rstruct;
+
+ time(&rstruct.time);
+ rstruct.ptr = (void *) &rstruct;
+
+ RAND_seed((void *) rdata, sizeof(rdata));
+ RAND_seed((void *) &rstruct, sizeof(rstruct));
+ }
+
+ sess->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
+
+ if (!sess->ssl_ctx) {
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf);
+ goto fail;
+ }
+
+ SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+ sess->ssl = SSL_new(sess->ssl_ctx);
+
+ if (!sess->ssl) {
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf);
+ goto fail;
+ }
+#else
+ gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
+#endif
+ }
+
+ if (gg_proxy_enabled) {
+ hostname = gg_proxy_host;
+ sess->proxy_port = port = gg_proxy_port;
+ } else {
+ hostname = GG_APPMSG_HOST;
+ port = GG_APPMSG_PORT;
+ }
+
+ if (!p->async) {
+ struct in_addr a;
+
+ if (!p->server_addr || !p->server_port) {
+ if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) {
+ struct in_addr *hn;
+
+ if (!(hn = gg_gethostbyname(hostname))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname);
+ goto fail;
+ } else {
+ a.s_addr = hn->s_addr;
+ free(hn);
+ }
+ }
+ } else {
+ a.s_addr = p->server_addr;
+ port = p->server_port;
+ }
+
+ sess->hub_addr = a.s_addr;
+
+ if (gg_proxy_enabled)
+ sess->proxy_addr = a.s_addr;
+
+ if ((sess->fd = gg_connect(&a, port, 0)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail;
+ }
+
+ if (p->server_addr && p->server_port)
+ sess->state = GG_STATE_CONNECTING_GG;
+ else
+ sess->state = GG_STATE_CONNECTING_HUB;
+
+ while (sess->state != GG_STATE_CONNECTED) {
+ struct gg_event *e;
+
+ if (!(e = gg_watch_fd(sess))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n");
+ goto fail;
+ }
+
+ if (e->type == GG_EVENT_CONN_FAILED) {
+ errno = EACCES;
+ gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n");
+ gg_event_free(e);
+ goto fail;
+ }
+
+ gg_event_free(e);
+ }
+
+ return sess;
+ }
+
+ if (!sess->server_addr || gg_proxy_enabled) {
+#ifndef __GG_LIBGADU_HAVE_PTHREAD
+ if (gg_resolve(&sess->fd, &sess->pid, hostname)) {
+#else
+ if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) {
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail;
+ }
+ } else {
+ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail;
+ }
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ }
+
+ return sess;
+
+fail:
+ if (sess) {
+ if (sess->password)
+ free(sess->password);
+ if (sess->initial_descr)
+ free(sess->initial_descr);
+ free(sess);
+ }
+
+ return NULL;
+}
+
+/*
+ * gg_free_session()
+ *
+ * próbuje zamkn±æ po³±czenia i zwalnia pamiêæ zajmowan± przez sesjê.
+ *
+ * - sess - opis sesji
+ */
+void gg_free_session(struct gg_session *sess)
+{
+ if (!sess)
+ return;
+
+ /* XXX dopisaæ zwalnianie i zamykanie wszystkiego, co mog³o zostaæ */
+
+ if (sess->password)
+ free(sess->password);
+
+ if (sess->initial_descr)
+ free(sess->initial_descr);
+
+ if (sess->client_version)
+ free(sess->client_version);
+
+ if (sess->header_buf)
+ free(sess->header_buf);
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl)
+ SSL_free(sess->ssl);
+
+ if (sess->ssl_ctx)
+ SSL_CTX_free(sess->ssl_ctx);
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+ if (sess->resolver) {
+ pthread_cancel(*((pthread_t*) sess->resolver));
+ free(sess->resolver);
+ sess->resolver = NULL;
+ }
+#else
+ if (sess->pid != -1) {
+ kill(sess->pid, SIGTERM);
+ waitpid(sess->pid, NULL, WNOHANG);
+ }
+#endif
+
+ if (sess->fd != -1)
+ close(sess->fd);
+
+ while (sess->images)
+ gg_image_queue_remove(sess, sess->images, 1);
+
+ free(sess);
+}
+
+/*
+ * gg_change_status()
+ *
+ * zmienia status u¿ytkownika. przydatne do /away i /busy oraz /quit.
+ *
+ * - sess - opis sesji
+ * - status - nowy status u¿ytkownika
+ *
+ * 0, -1.
+ */
+int gg_change_status(struct gg_session *sess, int status)
+{
+ struct gg_new_status p;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ p.status = gg_fix32(status);
+
+ sess->status = status;
+
+ return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL);
+}
+
+/*
+ * gg_change_status_descr()
+ *
+ * zmienia status u¿ytkownika na opisowy.
+ *
+ * - sess - opis sesji
+ * - status - nowy status u¿ytkownika
+ * - descr - opis statusu
+ *
+ * 0, -1.
+ */
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
+{
+ struct gg_new_status p;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
+
+ if (!sess || !descr) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ p.status = gg_fix32(status);
+
+ sess->status = status;
+
+ return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL);
+}
+
+/*
+ * gg_change_status_descr_time()
+ *
+ * zmienia status u¿ytkownika na opisowy z godzin± powrotu.
+ *
+ * - sess - opis sesji
+ * - status - nowy status u¿ytkownika
+ * - descr - opis statusu
+ * - time - czas w formacie uniksowym
+ *
+ * 0, -1.
+ */
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+{
+ struct gg_new_status p;
+ uint32_t newtime;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time);
+
+ if (!sess || !descr || !time) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ p.status = gg_fix32(status);
+
+ sess->status = status;
+
+ newtime = gg_fix32(time);
+
+ return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL);
+}
+
+/*
+ * gg_logoff()
+ *
+ * wylogowuje u¿ytkownika i zamyka po³±czenie, ale nie zwalnia pamiêci.
+ *
+ * - sess - opis sesji
+ */
+void gg_logoff(struct gg_session *sess)
+{
+ if (!sess)
+ return;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+
+ if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK))
+ gg_change_status(sess, GG_STATUS_NOT_AVAIL);
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ if (sess->ssl)
+ SSL_shutdown(sess->ssl);
+#endif
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+ if (sess->resolver) {
+ pthread_cancel(*((pthread_t*) sess->resolver));
+ free(sess->resolver);
+ sess->resolver = NULL;
+ }
+#else
+ if (sess->pid != -1) {
+ kill(sess->pid, SIGTERM);
+ waitpid(sess->pid, NULL, WNOHANG);
+ sess->pid = -1;
+ }
+#endif
+
+ if (sess->fd != -1) {
+ shutdown(sess->fd, SHUT_RDWR);
+ close(sess->fd);
+ sess->fd = -1;
+ }
+}
+
+/*
+ * gg_image_request()
+ *
+ * wysy³a ¿±danie wys³ania obrazka o podanych parametrach.
+ *
+ * - sess - opis sesji
+ * - recipient - numer adresata
+ * - size - rozmiar obrazka
+ * - crc32 - suma kontrolna obrazka
+ *
+ * 0/-1
+ */
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
+{
+ struct gg_send_msg s;
+ struct gg_msg_image_request r;
+ char dummy = 0;
+ int res;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+ r.flag = 0x04;
+ r.size = gg_fix32(size);
+ r.crc32 = gg_fix32(crc32);
+
+ res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
+
+ if (!res) {
+ struct gg_image_queue *q = malloc(sizeof(*q));
+ char *buf;
+
+ if (!q) {
+ gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+ return -1;
+ }
+
+ buf = malloc(size);
+ if (size && !buf)
+ {
+ gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
+ free(q);
+ return -1;
+ }
+
+ memset(q, 0, sizeof(*q));
+
+ q->sender = recipient;
+ q->size = size;
+ q->crc32 = crc32;
+ q->image = buf;
+
+ if (!sess->images)
+ sess->images = q;
+ else {
+ struct gg_image_queue *qq;
+
+ for (qq = sess->images; qq->next; qq = qq->next)
+ ;
+
+ qq->next = q;
+ }
+ }
+
+ return res;
+}
+
+/*
+ * gg_image_reply()
+ *
+ * wysy³a ¿±dany obrazek.
+ *
+ * - sess - opis sesji
+ * - recipient - numer adresata
+ * - filename - nazwa pliku
+ * - image - bufor z obrazkiem
+ * - size - rozmiar obrazka
+ *
+ * 0/-1
+ */
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
+{
+ struct gg_msg_image_reply *r;
+ struct gg_send_msg s;
+ const char *tmp;
+ char buf[1910];
+ int res = -1;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
+
+ if (!sess || !filename || !image) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* wytnij ¶cie¿ki, zostaw tylko nazwê pliku */
+ while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
+ filename = tmp + 1;
+
+ if (strlen(filename) < 1 || strlen(filename) > 1024) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+ buf[0] = 0;
+ r = (void*) &buf[1];
+
+ r->flag = 0x05;
+ r->size = gg_fix32(size);
+ r->crc32 = gg_fix32(gg_crc32(0, image, size));
+
+ while (size > 0) {
+ int buflen, chunklen;
+
+ /* \0 + struct gg_msg_image_reply */
+ buflen = sizeof(struct gg_msg_image_reply) + 1;
+
+ /* w pierwszym kawa³ku jest nazwa pliku */
+ if (r->flag == 0x05) {
+ strcpy(buf + buflen, filename);
+ buflen += strlen(filename) + 1;
+ }
+
+ chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size;
+
+ memcpy(buf + buflen, image, chunklen);
+ size -= chunklen;
+ image += chunklen;
+
+ res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL);
+
+ if (res == -1)
+ break;
+
+ r->flag = 0x06;
+ }
+
+ return res;
+}
+
+/*
+ * gg_send_message_ctcp()
+ *
+ * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer
+ * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomo¶ci
+ * - recipient - numer adresata
+ * - message - tre¶æ wiadomo¶ci
+ * - message_len - d³ugo¶æ
+ *
+ * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu.
+ */
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
+{
+ struct gg_send_msg s;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(msgclass);
+
+ return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
+}
+
+/*
+ * gg_send_message()
+ *
+ * wysy³a wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer
+ * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomo¶ci
+ * - recipient - numer adresata
+ * - message - tre¶æ wiadomo¶ci
+ *
+ * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu.
+ */
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message);
+
+ return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0);
+}
+
+/*
+ * gg_send_message_richtext()
+ *
+ * wysy³a kolorow± wiadomo¶æ do innego u¿ytkownika. zwraca losowy numer
+ * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomo¶ci
+ * - recipient - numer adresata
+ * - message - tre¶æ wiadomo¶ci
+ * - format - informacje o formatowaniu
+ * - formatlen - d³ugo¶æ informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu.
+ */
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+ struct gg_send_msg s;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!message) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ if (!sess->seq)
+ sess->seq = 0x01740000 | (rand() & 0xffff);
+ s.seq = gg_fix32(sess->seq);
+ s.msgclass = gg_fix32(msgclass);
+ sess->seq += (rand() % 0x300) + 0x300;
+
+ if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, format, formatlen, NULL) == -1)
+ return -1;
+
+ return gg_fix32(s.seq);
+}
+
+/*
+ * gg_send_message_confer()
+ *
+ * wysy³a wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca losowy numer
+ * sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomo¶ci
+ * - recipients_count - ilo¶æ adresatów
+ * - recipients - numerki adresatów
+ * - message - tre¶æ wiadomo¶ci
+ *
+ * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu.
+ */
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message);
+
+ return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0);
+}
+
+/*
+ * gg_send_message_confer_richtext()
+ *
+ * wysy³a kolorow± wiadomo¶æ do kilku u¿ytkownikow (konferencja). zwraca
+ * losowy numer sekwencyjny, który mo¿na zignorowaæ albo wykorzystaæ do
+ * potwierdzenia.
+ *
+ * - sess - opis sesji
+ * - msgclass - rodzaj wiadomo¶ci
+ * - recipients_count - ilo¶æ adresatów
+ * - recipients - numerki adresatów
+ * - message - tre¶æ wiadomo¶ci
+ * - format - informacje o formatowaniu
+ * - formatlen - d³ugo¶æ informacji o formatowaniu
+ *
+ * numer sekwencyjny wiadomo¶ci lub -1 w przypadku b³êdu.
+ */
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+ struct gg_send_msg s;
+ struct gg_msg_recipients r;
+ int i, j, k;
+ uin_t *recps;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ r.flag = 0x01;
+ r.count = gg_fix32(recipients_count - 1);
+
+ if (!sess->seq)
+ sess->seq = 0x01740000 | (rand() & 0xffff);
+ s.seq = gg_fix32(sess->seq);
+ s.msgclass = gg_fix32(msgclass);
+
+ recps = malloc(sizeof(uin_t) * recipients_count);
+ if (!recps)
+ return -1;
+
+ for (i = 0; i < recipients_count; i++) {
+
+ s.recipient = gg_fix32(recipients[i]);
+
+ for (j = 0, k = 0; j < recipients_count; j++)
+ if (recipients[j] != recipients[i]) {
+ recps[k] = gg_fix32(recipients[j]);
+ k++;
+ }
+
+ if (!i)
+ sess->seq += (rand() % 0x300) + 0x300;
+
+ if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen(message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) {
+ free(recps);
+ return -1;
+ }
+ }
+
+ free(recps);
+
+ return gg_fix32(s.seq);
+}
+
+/*
+ * gg_ping()
+ *
+ * wysy³a do serwera pakiet ping.
+ *
+ * - sess - opis sesji
+ *
+ * 0, -1.
+ */
+int gg_ping(struct gg_session *sess)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ return gg_send_packet(sess, GG_PING, NULL);
+}
+
+/*
+ * gg_notify_ex()
+ *
+ * wysy³a serwerowi listê kontaktów (wraz z odpowiadaj±cymi im typami userów),
+ * dziêki czemu wie, czyj stan nas interesuje.
+ *
+ * - sess - opis sesji
+ * - userlist - wska¼nik do tablicy numerów
+ * - types - wska¼nik do tablicy typów u¿ytkowników
+ * - count - ilo¶æ numerków
+ *
+ * 0, -1.
+ */
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
+{
+ struct gg_notify *n;
+ uin_t *u;
+ char *t;
+ int i, res = 0;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!userlist || !count)
+ return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+
+ while (count > 0) {
+ int part_count, packet_type;
+
+ if (count > 400) {
+ part_count = 400;
+ packet_type = GG_NOTIFY_FIRST;
+ } else {
+ part_count = count;
+ packet_type = GG_NOTIFY_LAST;
+ }
+
+ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+ return -1;
+
+ for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) {
+ n[i].uin = gg_fix32(*u);
+ n[i].dunno1 = *t;
+ }
+
+ if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
+ free(n);
+ res = -1;
+ break;
+ }
+
+ count -= part_count;
+ userlist += part_count;
+ types += part_count;
+
+ free(n);
+ }
+
+ return res;
+}
+
+/*
+ * gg_notify()
+ *
+ * wysy³a serwerowi listê kontaktów, dziêki czemu wie, czyj stan nas
+ * interesuje.
+ *
+ * - sess - opis sesji
+ * - userlist - wska¼nik do tablicy numerów
+ * - count - ilo¶æ numerków
+ *
+ * 0, -1.
+ */
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
+{
+ struct gg_notify *n;
+ uin_t *u;
+ int i, res = 0;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!userlist || !count)
+ return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+
+ while (count > 0) {
+ int part_count, packet_type;
+
+ if (count > 400) {
+ part_count = 400;
+ packet_type = GG_NOTIFY_FIRST;
+ } else {
+ part_count = count;
+ packet_type = GG_NOTIFY_LAST;
+ }
+
+ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+ return -1;
+
+ for (u = userlist, i = 0; i < part_count; u++, i++) {
+ n[i].uin = gg_fix32(*u);
+ n[i].dunno1 = GG_USER_NORMAL;
+ }
+
+ if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
+ res = -1;
+ free(n);
+ break;
+ }
+
+ free(n);
+
+ userlist += part_count;
+ count -= part_count;
+ }
+
+ return res;
+}
+
+/*
+ * gg_add_notify_ex()
+ *
+ * dodaje do listy kontaktów dany numer w trakcie po³±czenia.
+ * dodawanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html)
+ *
+ * - sess - opis sesji
+ * - uin - numer
+ * - type - typ
+ *
+ * 0, -1.
+ */
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+ struct gg_add_remove a;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ a.uin = gg_fix32(uin);
+ a.dunno1 = type;
+
+ return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/*
+ * gg_add_notify()
+ *
+ * dodaje do listy kontaktów dany numer w trakcie po³±czenia.
+ *
+ * - sess - opis sesji
+ * - uin - numer
+ *
+ * 0, -1.
+ */
+int gg_add_notify(struct gg_session *sess, uin_t uin)
+{
+ return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/*
+ * gg_remove_notify_ex()
+ *
+ * usuwa z listy kontaktów w trakcie po³±czenia.
+ * usuwanemu u¿ytkownikowi okre¶lamy jego typ (patrz protocol.html)
+ *
+ * - sess - opis sesji
+ * - uin - numer
+ * - type - typ
+ *
+ * 0, -1.
+ */
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+ struct gg_add_remove a;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ a.uin = gg_fix32(uin);
+ a.dunno1 = type;
+
+ return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
+}
+
+/*
+ * gg_remove_notify()
+ *
+ * usuwa z listy kontaktów w trakcie po³±czenia.
+ *
+ * - sess - opis sesji
+ * - uin - numer
+ *
+ * 0, -1.
+ */
+int gg_remove_notify(struct gg_session *sess, uin_t uin)
+{
+ return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/*
+ * gg_userlist_request()
+ *
+ * wysy³a ¿±danie/zapytanie listy kontaktów na serwerze.
+ *
+ * - sess - opis sesji
+ * - type - rodzaj zapytania/¿±dania
+ * - request - tre¶æ zapytania/¿±dania (mo¿e byæ NULL)
+ *
+ * 0, -1
+ */
+int gg_userlist_request(struct gg_session *sess, char type, const char *request)
+{
+ int len;
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!request) {
+ sess->userlist_blocks = 1;
+ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
+ }
+
+ len = strlen(request);
+
+ sess->userlist_blocks = 0;
+
+ while (len > 2047) {
+ sess->userlist_blocks++;
+
+ if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
+ return -1;
+
+ if (type == GG_USERLIST_PUT)
+ type = GG_USERLIST_PUT_MORE;
+
+ request += 2047;
+ len -= 2047;
+ }
+
+ sess->userlist_blocks++;
+
+ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/libgadu.h b/kopete/protocols/gadu/libgadu/libgadu.h
new file mode 100644
index 00000000..18588500
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/libgadu.h
@@ -0,0 +1,1310 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. Wo¼ny <speedy@ziew.org>
+ * Arkadiusz Mi¶kiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliñski <chilek@chilan.com>
+ * Piotr Wysocki <wysek@linux.bydg.org>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef __GG_LIBGADU_H
+#define __GG_LIBGADU_H
+
+#ifdef __cplusplus
+#ifdef _WIN32
+#pragma pack(push, 1)
+#endif
+extern "C" {
+#endif
+
+#include <libgadu-config.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+#include <openssl/ssl.h>
+#endif
+
+/*
+ * typedef uin_t
+ *
+ * typ reprezentuj±cy numer osoby.
+ */
+typedef uint32_t uin_t;
+
+/*
+ * ogólna struktura opisuj±ca ró¿ne sesje. przydatna w klientach.
+ */
+#define gg_common_head(x) \
+ int fd; /* podgl±dany deskryptor */ \
+ int check; /* sprawdzamy zapis czy odczyt */ \
+ int state; /* aktualny stan maszynki */ \
+ int error; /* kod b³êdu dla GG_STATE_ERROR */ \
+ int type; /* rodzaj sesji */ \
+ int id; /* identyfikator */ \
+ int timeout; /* sugerowany timeout w sekundach */ \
+ int (*callback)(x*); /* callback przy zmianach */ \
+ void (*destroy)(x*); /* funkcja niszczenia */
+
+struct gg_common {
+ gg_common_head(struct gg_common)
+};
+
+struct gg_image_queue;
+
+/*
+ * struct gg_session
+ *
+ * struktura opisuj±ca dan± sesjê. tworzona przez gg_login(), zwalniana
+ * przez gg_free_session().
+ */
+struct gg_session {
+ gg_common_head(struct gg_session)
+
+ int async; /* czy po³±czenie jest asynchroniczne */
+ int pid; /* pid procesu resolvera */
+ int port; /* port, z którym siê ³±czymy */
+ int seq; /* numer sekwencyjny ostatniej wiadomo¶ci */
+ int last_pong; /* czas otrzymania ostatniego ping/pong */
+ int last_event; /* czas otrzymania ostatniego pakietu */
+
+ struct gg_event *event; /* zdarzenie po ->callback() */
+
+ uint32_t proxy_addr; /* adres proxy, keszowany */
+ uint16_t proxy_port; /* port proxy */
+
+ uint32_t hub_addr; /* adres huba po resolvniêciu */
+ uint32_t server_addr; /* adres serwera, od huba */
+
+ uint32_t client_addr; /* adres klienta */
+ uint16_t client_port; /* port, na którym klient s³ucha */
+
+ uint32_t external_addr; /* adres zewnetrzny klienta */
+ uint16_t external_port; /* port zewnetrzny klienta */
+
+ uin_t uin; /* numerek klienta */
+ char *password; /* i jego has³o. zwalniane automagicznie */
+
+ int initial_status; /* pocz±tkowy stan klienta */
+ int status; /* aktualny stan klienta */
+
+ char *recv_buf; /* bufor na otrzymywane pakiety */
+ int recv_done; /* ile ju¿ wczytano do bufora */
+ int recv_left; /* i ile jeszcze trzeba wczytaæ */
+
+ int protocol_version; /* wersja u¿ywanego protoko³u */
+ char *client_version; /* wersja u¿ywanego klienta */
+ int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */
+
+ char *initial_descr; /* pocz±tkowy opis stanu klienta */
+
+ void *resolver; /* wska¼nik na informacje resolvera */
+
+ char *header_buf; /* bufor na pocz±tek nag³ówka */
+ unsigned int header_done;/* ile ju¿ mamy */
+
+#ifdef __GG_LIBGADU_HAVE_OPENSSL
+ SSL *ssl; /* sesja TLS */
+ SSL_CTX *ssl_ctx; /* kontekst sesji? */
+#else
+ void *ssl; /* zachowujemy ABI */
+ void *ssl_ctx;
+#endif
+
+ int image_size; /* maksymalny rozmiar obrazków w KiB */
+
+ char *userlist_reply; /* fragment odpowiedzi listy kontaktów */
+
+ int userlist_blocks; /* na ile kawa³ków podzielono listê kontaktów */
+
+ struct gg_image_queue *images; /* aktualnie wczytywane obrazki */
+};
+
+/*
+ * struct gg_http
+ *
+ * ogólna struktura opisuj±ca stan wszystkich operacji HTTP. tworzona
+ * przez gg_http_connect(), zwalniana przez gg_http_free().
+ */
+struct gg_http {
+ gg_common_head(struct gg_http)
+
+ int async; /* czy po³±czenie asynchroniczne */
+ int pid; /* pid procesu resolvera */
+ int port; /* port, z którym siê ³±czymy */
+
+ char *query; /* bufor zapytania http */
+ char *header; /* bufor nag³ówka */
+ int header_size; /* rozmiar wczytanego nag³ówka */
+ char *body; /* bufor otrzymanych informacji */
+ unsigned int body_size; /* oczekiwana ilo¶æ informacji */
+
+ void *data; /* dane danej operacji http */
+
+ char *user_data; /* dane u¿ytkownika, nie s± zwalniane przez gg_http_free() */
+
+ void *resolver; /* wska¼nik na informacje resolvera */
+
+ unsigned int body_done; /* ile ju¿ tre¶ci odebrano? */
+};
+
+#ifdef __GNUC__
+#define GG_PACKED __attribute__ ((packed))
+#else
+#define GG_PACKED
+#endif
+
+#define GG_MAX_PATH 276
+
+/*
+ * struct gg_file_info
+ *
+ * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbêdnej przy
+ * wysy³aniu plików.
+ */
+struct gg_file_info {
+ uint32_t mode; /* dwFileAttributes */
+ uint32_t ctime[2]; /* ftCreationTime */
+ uint32_t atime[2]; /* ftLastAccessTime */
+ uint32_t mtime[2]; /* ftLastWriteTime */
+ uint32_t size_hi; /* nFileSizeHigh */
+ uint32_t size; /* nFileSizeLow */
+ uint32_t reserved0; /* dwReserved0 */
+ uint32_t reserved1; /* dwReserved1 */
+ unsigned char filename[GG_MAX_PATH - 14]; /* cFileName */
+ unsigned char short_filename[14]; /* cAlternateFileName */
+} GG_PACKED;
+
+/*
+ * struct gg_dcc
+ *
+ * struktura opisuj±ca nas³uchuj±ce gniazdo po³±czeñ miêdzy klientami.
+ * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free().
+ */
+struct gg_dcc {
+ gg_common_head(struct gg_dcc)
+
+ struct gg_event *event; /* opis zdarzenia */
+
+ int active; /* czy to my siê ³±czymy? */
+ int port; /* port, na którym siedzi */
+ uin_t uin; /* uin klienta */
+ uin_t peer_uin; /* uin drugiej strony */
+ int file_fd; /* deskryptor pliku */
+ unsigned int offset; /* offset w pliku */
+ unsigned int chunk_size;/* rozmiar kawa³ka */
+ unsigned int chunk_offset;/* offset w aktualnym kawa³ku */
+ struct gg_file_info file_info;
+ /* informacje o pliku */
+ int established; /* po³±czenie ustanowione */
+ char *voice_buf; /* bufor na pakiet po³±czenia g³osowego */
+ int incoming; /* po³±czenie przychodz±ce */
+ char *chunk_buf; /* bufor na kawa³ek danych */
+ uint32_t remote_addr; /* adres drugiej strony */
+ uint16_t remote_port; /* port drugiej strony */
+};
+
+/*
+ * enum gg_session_t
+ *
+ * rodzaje sesji.
+ */
+enum gg_session_t {
+ GG_SESSION_GG = 1, /* po³±czenie z serwerem gg */
+ GG_SESSION_HTTP, /* ogólna sesja http */
+ GG_SESSION_SEARCH, /* szukanie */
+ GG_SESSION_REGISTER, /* rejestrowanie */
+ GG_SESSION_REMIND, /* przypominanie has³a */
+ GG_SESSION_PASSWD, /* zmiana has³a */
+ GG_SESSION_CHANGE, /* zmiana informacji o sobie */
+ GG_SESSION_DCC, /* ogólne po³±czenie DCC */
+ GG_SESSION_DCC_SOCKET, /* nas³uchuj±cy socket */
+ GG_SESSION_DCC_SEND, /* wysy³anie pliku */
+ GG_SESSION_DCC_GET, /* odbieranie pliku */
+ GG_SESSION_DCC_VOICE, /* rozmowa g³osowa */
+ GG_SESSION_USERLIST_GET, /* pobieranie userlisty */
+ GG_SESSION_USERLIST_PUT, /* wysy³anie userlisty */
+ GG_SESSION_UNREGISTER, /* usuwanie konta */
+ GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */
+ GG_SESSION_TOKEN, /* pobieranie tokenu */
+
+ GG_SESSION_USER0 = 256, /* zdefiniowana dla u¿ytkownika */
+ GG_SESSION_USER1, /* j.w. */
+ GG_SESSION_USER2, /* j.w. */
+ GG_SESSION_USER3, /* j.w. */
+ GG_SESSION_USER4, /* j.w. */
+ GG_SESSION_USER5, /* j.w. */
+ GG_SESSION_USER6, /* j.w. */
+ GG_SESSION_USER7 /* j.w. */
+};
+
+/*
+ * enum gg_state_t
+ *
+ * opisuje stan asynchronicznej maszyny.
+ */
+enum gg_state_t {
+ /* wspólne */
+ GG_STATE_IDLE = 0, /* nie powinno wyst±piæ. */
+ GG_STATE_RESOLVING, /* wywo³a³ gethostbyname() */
+ GG_STATE_CONNECTING, /* wywo³a³ connect() */
+ GG_STATE_READING_DATA, /* czeka na dane http */
+ GG_STATE_ERROR, /* wyst±pi³ b³±d. kod w x->error */
+
+ /* gg_session */
+ GG_STATE_CONNECTING_HUB, /* wywo³a³ connect() na huba */
+ GG_STATE_CONNECTING_GG, /* wywo³a³ connect() na serwer */
+ GG_STATE_READING_KEY, /* czeka na klucz */
+ GG_STATE_READING_REPLY, /* czeka na odpowied¼ */
+ GG_STATE_CONNECTED, /* po³±czy³ siê */
+
+ /* gg_http */
+ GG_STATE_SENDING_QUERY, /* wysy³a zapytanie http */
+ GG_STATE_READING_HEADER, /* czeka na nag³ówek http */
+ GG_STATE_PARSING, /* przetwarza dane */
+ GG_STATE_DONE, /* skoñczy³ */
+
+ /* gg_dcc */
+ GG_STATE_LISTENING, /* czeka na po³±czenia */
+ GG_STATE_READING_UIN_1, /* czeka na uin peera */
+ GG_STATE_READING_UIN_2, /* czeka na swój uin */
+ GG_STATE_SENDING_ACK, /* wysy³a potwierdzenie dcc */
+ GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */
+ GG_STATE_READING_REQUEST, /* czeka na komendê */
+ GG_STATE_SENDING_REQUEST, /* wysy³a komendê */
+ GG_STATE_SENDING_FILE_INFO, /* wysy³a informacje o pliku */
+ GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */
+ GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */
+ GG_STATE_SENDING_FILE_ACK, /* wysy³a potwierdzenie pliku */
+ GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
+ GG_STATE_SENDING_FILE_HEADER, /* wysy³a nag³ówek pliku */
+ GG_STATE_READING_FILE_HEADER, /* czeka na nag³ówek */
+ GG_STATE_GETTING_FILE, /* odbiera plik */
+ GG_STATE_SENDING_FILE, /* wysy³a plik */
+ GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */
+ GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */
+ GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */
+ GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */
+ GG_STATE_SENDING_VOICE_ACK, /* wysy³a potwierdzenie voip */
+ GG_STATE_SENDING_VOICE_REQUEST, /* wysy³a ¿±danie voip */
+ GG_STATE_READING_TYPE, /* czeka na typ po³±czenia */
+
+ /* nowe. bez sensu jest to API. */
+ GG_STATE_TLS_NEGOTIATION /* negocjuje po³±czenie TLS */
+};
+
+/*
+ * enum gg_check_t
+ *
+ * informuje, co proces klienta powinien sprawdziæ na deskryptorze danego
+ * po³±czenia.
+ */
+enum gg_check_t {
+ GG_CHECK_NONE = 0, /* nic. nie powinno wyst±piæ */
+ GG_CHECK_WRITE = 1, /* sprawdzamy mo¿liwo¶æ zapisu */
+ GG_CHECK_READ = 2 /* sprawdzamy mo¿liwo¶æ odczytu */
+};
+
+/*
+ * struct gg_login_params
+ *
+ * parametry gg_login(). przeniesiono do struktury, ¿eby unikn±æ problemów
+ * z ci±g³ymi zmianami API, gdy dodano co¶ nowego do protoko³u.
+ */
+struct gg_login_params {
+ uin_t uin; /* numerek */
+ char *password; /* has³o */
+ int async; /* asynchroniczne sockety? */
+ int status; /* pocz±tkowy status klienta */
+ char *status_descr; /* opis statusu */
+ uint32_t server_addr; /* adres serwera gg */
+ uint16_t server_port; /* port serwera gg */
+ uint32_t client_addr; /* adres dcc klienta */
+ uint16_t client_port; /* port dcc klienta */
+ int protocol_version; /* wersja protoko³u */
+ char *client_version; /* wersja klienta */
+ int has_audio; /* czy ma d¼wiêk? */
+ int last_sysmsg; /* ostatnia wiadomo¶æ systemowa */
+ uint32_t external_addr; /* adres widziany na zewnatrz */
+ uint16_t external_port; /* port widziany na zewnatrz */
+ int tls; /* czy ³±czymy po TLS? */
+ int image_size; /* maksymalny rozmiar obrazka w KiB */
+ int era_omnix; /* czy udawaæ klienta era omnix? */
+
+ char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych,
+ * ¿eby z dodaniem parametru nie
+ * zmienia³ siê rozmiar struktury */
+};
+
+struct gg_session *gg_login(const struct gg_login_params *p);
+void gg_free_session(struct gg_session *sess);
+void gg_logoff(struct gg_session *sess);
+int gg_change_status(struct gg_session *sess, int status);
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
+int gg_ping(struct gg_session *sess);
+int gg_userlist_request(struct gg_session *sess, char type, const char *request);
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
+
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
+
+struct gg_image_queue {
+ uin_t sender; /* nadawca obrazka */
+ uint32_t size; /* rozmiar */
+ uint32_t crc32; /* suma kontrolna */
+ char *filename; /* nazwa pliku */
+ char *image; /* bufor z obrazem */
+ uint32_t done; /* ile ju¿ wczytano */
+
+ struct gg_image_queue *next; /* nastêpny na li¶cie */
+};
+
+/*
+ * enum gg_event_t
+ *
+ * rodzaje zdarzeñ.
+ */
+enum gg_event_t {
+ GG_EVENT_NONE = 0, /* nic siê nie wydarzy³o */
+ GG_EVENT_MSG, /* otrzymano wiadomo¶æ */
+ GG_EVENT_NOTIFY, /* kto¶ siê pojawi³ */
+ GG_EVENT_NOTIFY_DESCR, /* kto¶ siê pojawi³ z opisem */
+ GG_EVENT_STATUS, /* kto¶ zmieni³ stan */
+ GG_EVENT_ACK, /* potwierdzenie wys³ania wiadomo¶ci */
+ GG_EVENT_PONG, /* pakiet pong */
+ GG_EVENT_CONN_FAILED, /* po³±czenie siê nie uda³o */
+ GG_EVENT_CONN_SUCCESS, /* po³±czenie siê powiod³o */
+ GG_EVENT_DISCONNECT, /* serwer zrywa po³±czenie */
+
+ GG_EVENT_DCC_NEW, /* nowe po³±czenie miêdzy klientami */
+ GG_EVENT_DCC_ERROR, /* b³±d po³±czenia miêdzy klientami */
+ GG_EVENT_DCC_DONE, /* zakoñczono po³±czenie */
+ GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */
+ GG_EVENT_DCC_CALLBACK, /* klient siê po³±czy³ na ¿±danie */
+ GG_EVENT_DCC_NEED_FILE_INFO, /* nale¿y wype³niæ file_info */
+ GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */
+ GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */
+ GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy g³osowej */
+
+ GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */
+ GG_EVENT_PUBDIR50_READ, /* odczytano w³asne dane z katalogu */
+ GG_EVENT_PUBDIR50_WRITE, /* wpisano w³asne dane do katalogu */
+
+ GG_EVENT_STATUS60, /* kto¶ zmieni³ stan w GG 6.0 */
+ GG_EVENT_NOTIFY60, /* kto¶ siê pojawi³ w GG 6.0 */
+ GG_EVENT_USERLIST, /* odpowied¼ listy kontaktów w GG 6.0 */
+ GG_EVENT_IMAGE_REQUEST, /* pro¶ba o wys³anie obrazka GG 6.0 */
+ GG_EVENT_IMAGE_REPLY, /* podes³any obrazek GG 6.0 */
+ GG_EVENT_DCC_ACK /* potwierdzenie transmisji */
+};
+
+#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
+
+/*
+ * enum gg_failure_t
+ *
+ * okre¶la powód nieudanego po³±czenia.
+ */
+enum gg_failure_t {
+ GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */
+ GG_FAILURE_CONNECTING, /* nie mo¿na siê po³±czyæ */
+ GG_FAILURE_INVALID, /* serwer zwróci³ nieprawid³owe dane */
+ GG_FAILURE_READING, /* zerwano po³±czenie podczas odczytu */
+ GG_FAILURE_WRITING, /* zerwano po³±czenie podczas zapisu */
+ GG_FAILURE_PASSWORD, /* nieprawid³owe has³o */
+ GG_FAILURE_404, /* XXX nieu¿ywane */
+ GG_FAILURE_TLS, /* b³±d negocjacji TLS */
+ GG_FAILURE_NEED_EMAIL, /* serwer roz³±czy³ nas z pro¶b± o zmianê emaila */
+ GG_FAILURE_INTRUDER, /* za du¿o prób po³±czenia siê z nieprawid³owym has³em */
+ GG_FAILURE_UNAVAILABLE /* serwery s± wy³±czone */
+};
+
+/*
+ * enum gg_error_t
+ *
+ * okre¶la rodzaj b³êdu wywo³anego przez dan± operacjê. nie zawiera
+ * przesadnie szczegó³owych informacji o powodzie b³êdu, by nie komplikowaæ
+ * obs³ugi b³êdów. je¶li wymagana jest wiêksza dok³adno¶æ, nale¿y sprawdziæ
+ * zawarto¶æ zmiennej errno.
+ */
+enum gg_error_t {
+ GG_ERROR_RESOLVING = 1, /* b³±d znajdowania hosta */
+ GG_ERROR_CONNECTING, /* b³±d ³aczenia siê */
+ GG_ERROR_READING, /* b³±d odczytu */
+ GG_ERROR_WRITING, /* b³±d wysy³ania */
+
+ GG_ERROR_DCC_HANDSHAKE, /* b³±d negocjacji */
+ GG_ERROR_DCC_FILE, /* b³±d odczytu/zapisu pliku */
+ GG_ERROR_DCC_EOF, /* plik siê skoñczy³? */
+ GG_ERROR_DCC_NET, /* b³±d wysy³ania/odbierania */
+ GG_ERROR_DCC_REFUSED /* po³±czenie odrzucone przez usera */
+};
+
+/*
+ * struktury dotycz±ce wyszukiwania w GG 5.0. NIE NALE¯Y SIÊ DO NICH
+ * ODWO£YWAÆ BEZPO¦REDNIO! do dostêpu do nich s³u¿± funkcje gg_pubdir50_*()
+ */
+struct gg_pubdir50_entry {
+ int num;
+ char *field;
+ char *value;
+};
+
+struct gg_pubdir50_s {
+ int count;
+ uin_t next;
+ int type;
+ uint32_t seq;
+ struct gg_pubdir50_entry *entries;
+ int entries_count;
+};
+
+/*
+ * typedef gg_pubdir_50_t
+ *
+ * typ opisuj±cy zapytanie lub wynik zapytania katalogu publicznego
+ * z protoko³u GG 5.0. nie nale¿y siê odwo³ywaæ bezpo¶rednio do jego
+ * pól -- s³u¿± do tego funkcje gg_pubdir50_*()
+ */
+typedef struct gg_pubdir50_s *gg_pubdir50_t;
+
+/*
+ * struct gg_event
+ *
+ * struktura opisuj±ca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub
+ * z gg_dcc_watch_fd()
+ */
+struct gg_event {
+ int type; /* rodzaj zdarzenia -- gg_event_t */
+ union { /* @event */
+ struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów -- GG_EVENT_NOTIFY */
+
+ enum gg_failure_t failure; /* b³±d po³±czenia -- GG_EVENT_FAILURE */
+
+ struct gg_dcc *dcc_new; /* nowe po³±czenie bezpo¶rednie -- GG_EVENT_DCC_NEW */
+
+ int dcc_error; /* b³±d po³±czenia bezpo¶redniego -- GG_EVENT_DCC_ERROR */
+
+ gg_pubdir50_t pubdir50; /* wynik operacji zwi±zanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */
+
+ struct { /* @msg odebrano wiadomo¶æ -- GG_EVENT_MSG */
+ uin_t sender; /* numer nadawcy */
+ int msgclass; /* klasa wiadomo¶ci */
+ time_t time; /* czas nadania */
+ unsigned char *message; /* tre¶æ wiadomo¶ci */
+
+ int recipients_count; /* ilo¶æ odbiorców konferencji */
+ uin_t *recipients; /* odbiorcy konferencji */
+
+ int formats_length; /* d³ugo¶æ informacji o formatowaniu tekstu */
+ void *formats; /* informacje o formatowaniu tekstu */
+ } msg;
+
+ struct { /* @notify_descr informacje o li¶cie kontaktów z opisami stanu -- GG_EVENT_NOTIFY_DESCR */
+ struct gg_notify_reply *notify; /* informacje o li¶cie kontaktów */
+ char *descr; /* opis stanu */
+ } notify_descr;
+
+ struct { /* @status zmiana stanu -- GG_EVENT_STATUS */
+ uin_t uin; /* numer */
+ uint32_t status; /* nowy stan */
+ char *descr; /* opis stanu */
+ } status;
+
+ struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */
+ uin_t uin; /* numer */
+ int status; /* nowy stan */
+ uint32_t remote_ip; /* adres ip */
+ uint16_t remote_port; /* port */
+ int version; /* wersja klienta */
+ int image_size; /* maksymalny rozmiar grafiki w KiB */
+ char *descr; /* opis stanu */
+ time_t time; /* czas powrotu */
+ } status60;
+
+ struct { /* @notify60 informacja o li¶cie kontaktów -- GG_EVENT_NOTIFY60 */
+ uin_t uin; /* numer */
+ int status; /* stan */
+ uint32_t remote_ip; /* adres ip */
+ uint16_t remote_port; /* port */
+ int version; /* wersja klienta */
+ int image_size; /* maksymalny rozmiar grafiki w KiB */
+ char *descr; /* opis stanu */
+ time_t time; /* czas powrotu */
+ } *notify60;
+
+ struct { /* @ack potwierdzenie wiadomo¶ci -- GG_EVENT_ACK */
+ uin_t recipient; /* numer odbiorcy */
+ int status; /* stan dorêczenia wiadomo¶ci */
+ int seq; /* numer sekwencyjny wiadomo¶ci */
+ } ack;
+
+ struct { /* @dcc_voice_data otrzymano dane d¼wiêkowe -- GG_EVENT_DCC_VOICE_DATA */
+ uint8_t *data; /* dane d¼wiêkowe */
+ int length; /* ilo¶æ danych d¼wiêkowych */
+ } dcc_voice_data;
+
+ struct { /* @userlist odpowied¼ listy kontaktów serwera */
+ char type; /* rodzaj odpowiedzi */
+ char *reply; /* tre¶æ odpowiedzi */
+ } userlist;
+
+ struct { /* @image_request pro¶ba o obrazek */
+ uin_t sender; /* nadawca pro¶by */
+ uint32_t size; /* rozmiar obrazka */
+ uint32_t crc32; /* suma kontrolna */
+ } image_request;
+
+ struct { /* @image_reply odpowied¼ z obrazkiem */
+ uin_t sender; /* nadawca odpowiedzi */
+ uint32_t size; /* rozmiar obrazka */
+ uint32_t crc32; /* suma kontrolna */
+ char *filename; /* nazwa pliku */
+ char *image; /* bufor z obrazkiem */
+ } image_reply;
+ } event;
+};
+
+struct gg_event *gg_watch_fd(struct gg_session *sess);
+void gg_event_free(struct gg_event *e);
+#define gg_free_event gg_event_free
+
+/*
+ * funkcje obs³ugi listy kontaktów.
+ */
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_add_notify(struct gg_session *sess, uin_t uin);
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_remove_notify(struct gg_session *sess, uin_t uin);
+
+/*
+ * funkcje obs³ugi http.
+ */
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
+int gg_http_watch_fd(struct gg_http *h);
+void gg_http_stop(struct gg_http *h);
+void gg_http_free(struct gg_http *h);
+void gg_http_free_fields(struct gg_http *h);
+#define gg_free_http gg_http_free
+
+/*
+ * struktury opisuj±ca kryteria wyszukiwania dla gg_search(). nieaktualne,
+ * zast±pione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI.
+ */
+struct gg_search_request {
+ int active;
+ unsigned int start;
+ char *nickname;
+ char *first_name;
+ char *last_name;
+ char *city;
+ int gender;
+ int min_birth;
+ int max_birth;
+ char *email;
+ char *phone;
+ uin_t uin;
+};
+
+struct gg_search {
+ int count;
+ struct gg_search_result *results;
+};
+
+struct gg_search_result {
+ uin_t uin;
+ char *first_name;
+ char *last_name;
+ char *nickname;
+ int born;
+ int gender;
+ char *city;
+ int active;
+};
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+/*
+ * funkcje wyszukiwania.
+ */
+struct gg_http *gg_search(const struct gg_search_request *r, int async);
+int gg_search_watch_fd(struct gg_http *f);
+void gg_free_search(struct gg_http *f);
+#define gg_search_free gg_free_search
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start);
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start);
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start);
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start);
+void gg_search_request_free(struct gg_search_request *r);
+
+/*
+ * funkcje obs³ugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje
+ * zachowuj± pewien poziom abstrakcji, ¿eby unikn±æ zmian ABI przy zmianach
+ * w protokole.
+ *
+ * NIE NALE¯Y SIÊ ODWO£YWAÆ DO PÓL gg_pubdir50_t BEZPO¦REDNIO!
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
+gg_pubdir50_t gg_pubdir50_new(int type);
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
+int gg_pubdir50_type(gg_pubdir50_t res);
+int gg_pubdir50_count(gg_pubdir50_t res);
+uin_t gg_pubdir50_next(gg_pubdir50_t res);
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
+void gg_pubdir50_free(gg_pubdir50_t res);
+
+#define GG_PUBDIR50_UIN "FmNumber"
+#define GG_PUBDIR50_STATUS "FmStatus"
+#define GG_PUBDIR50_FIRSTNAME "firstname"
+#define GG_PUBDIR50_LASTNAME "lastname"
+#define GG_PUBDIR50_NICKNAME "nickname"
+#define GG_PUBDIR50_BIRTHYEAR "birthyear"
+#define GG_PUBDIR50_CITY "city"
+#define GG_PUBDIR50_GENDER "gender"
+#define GG_PUBDIR50_GENDER_FEMALE "1"
+#define GG_PUBDIR50_GENDER_MALE "2"
+#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
+#define GG_PUBDIR50_GENDER_SET_MALE "1"
+#define GG_PUBDIR50_ACTIVE "ActiveOnly"
+#define GG_PUBDIR50_ACTIVE_TRUE "1"
+#define GG_PUBDIR50_START "fmstart"
+#define GG_PUBDIR50_FAMILYNAME "familyname"
+#define GG_PUBDIR50_FAMILYCITY "familycity"
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length);
+
+/*
+ * struct gg_pubdir
+ *
+ * operacje na katalogu publicznym.
+ */
+struct gg_pubdir {
+ int success; /* czy siê uda³o */
+ uin_t uin; /* otrzymany numerek. 0 je¶li b³±d */
+};
+
+/* ogólne funkcje, nie powinny byæ u¿ywane */
+int gg_pubdir_watch_fd(struct gg_http *f);
+void gg_pubdir_free(struct gg_http *f);
+#define gg_free_pubdir gg_pubdir_free
+
+struct gg_token {
+ int width; /* szeroko¶æ obrazka */
+ int height; /* wysoko¶æ obrazka */
+ int length; /* ilo¶æ znaków w tokenie */
+ char *tokenid; /* id tokenu */
+};
+
+/* funkcje dotycz±ce tokenów */
+struct gg_http *gg_token(int async);
+int gg_token_watch_fd(struct gg_http *h);
+void gg_token_free(struct gg_http *h);
+
+/* rejestracja nowego numerka */
+struct gg_http *gg_register(const char *email, const char *password, int async);
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async);
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#define gg_register_watch_fd gg_pubdir_watch_fd
+#define gg_register_free gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async);
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async);
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#define gg_unregister_watch_fd gg_pubdir_watch_fd
+#define gg_unregister_free gg_pubdir_free
+
+/* przypomnienie has³a e-mailem */
+struct gg_http *gg_remind_passwd(uin_t uin, int async);
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async);
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_remind_passwd_free gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+
+/* zmiana has³a */
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async);
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async);
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async);
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#define gg_change_passwd_free gg_pubdir_free
+#define gg_free_change_passwd gg_pubdir_free
+
+/*
+ * struct gg_change_info_request
+ *
+ * opis ¿±dania zmiany informacji w katalogu publicznym.
+ */
+struct gg_change_info_request {
+ char *first_name; /* imiê */
+ char *last_name; /* nazwisko */
+ char *nickname; /* pseudonim */
+ char *email; /* email */
+ int born; /* rok urodzenia */
+ int gender; /* p³eæ */
+ char *city; /* miasto */
+};
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city);
+void gg_change_info_request_free(struct gg_change_info_request *r);
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async);
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+/*
+ * funkcje dotycz±ce listy kontaktów na serwerze.
+ */
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async);
+int gg_userlist_get_watch_fd(struct gg_http *f);
+void gg_userlist_get_free(struct gg_http *f);
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async);
+int gg_userlist_put_watch_fd(struct gg_http *f);
+void gg_userlist_put_free(struct gg_http *f);
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async);
+int gg_userlist_remove_watch_fd(struct gg_http *f);
+void gg_userlist_remove_free(struct gg_http *f);
+
+
+
+/*
+ * funkcje dotycz±ce komunikacji miêdzy klientami.
+ */
+extern int gg_dcc_port; /* port, na którym nas³uchuje klient */
+extern unsigned long gg_dcc_ip; /* adres, na którym nas³uchuje klient */
+
+int gg_dcc_request(struct gg_session *sess, uin_t uin);
+
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+void gg_dcc_set_type(struct gg_dcc *d, int type);
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
+
+#define GG_DCC_VOICE_FRAME_LENGTH 195
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326
+
+struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
+#define gg_dcc_socket_free gg_free_dcc
+#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
+
+void gg_dcc_free(struct gg_dcc *c);
+#define gg_free_dcc gg_dcc_free
+
+/*
+ * je¶li chcemy sobie podebugowaæ, wystarczy ustawiæ `gg_debug_level'.
+ * niestety w miarê przybywania wpisów `gg_debug(...)' nie chcia³o mi
+ * siê ustawiaæ odpowiednich leveli, wiêc wiêkszo¶æ sz³a do _MISC.
+ */
+extern int gg_debug_level; /* poziom debugowania. mapa bitowa sta³ych GG_DEBUG_* */
+
+/*
+ * mo¿na podaæ wska¼nik do funkcji obs³uguj±cej wywo³ania gg_debug().
+ * nieoficjalne, nieudokumentowane, mo¿e siê zmieniæ. je¶li kto¶ jest
+ * zainteresowany, niech da znaæ na ekg-devel.
+ */
+extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+
+/*
+ * mo¿na podaæ plik, do którego bêd± zapisywane teksty z gg_debug().
+ */
+extern FILE *gg_debug_file;
+
+#define GG_DEBUG_NET 1
+#define GG_DEBUG_TRAFFIC 2
+#define GG_DEBUG_DUMP 4
+#define GG_DEBUG_FUNCTION 8
+#define GG_DEBUG_MISC 16
+
+#ifdef GG_DEBUG_DISABLE
+#define gg_debug(x, y...) do { } while(0)
+#else
+void gg_debug(int level, const char *format, ...);
+#endif
+
+const char *gg_libgadu_version(void);
+
+/*
+ * konfiguracja http proxy.
+ */
+extern int gg_proxy_enabled; /* w³±cza obs³ugê proxy */
+extern char *gg_proxy_host; /* okre¶la adres serwera proxy */
+extern int gg_proxy_port; /* okre¶la port serwera proxy */
+extern char *gg_proxy_username; /* okre¶la nazwê u¿ytkownika przy autoryzacji serwera proxy */
+extern char *gg_proxy_password; /* okre¶la has³o u¿ytkownika przy autoryzacji serwera proxy */
+extern int gg_proxy_http_only; /* w³±cza obs³ugê proxy wy³±cznie dla us³ug HTTP */
+
+
+/*
+ * adres, z którego ¶lemy pakiety (np ³±czymy siê z serwerem)
+ * u¿ywany przy gg_connect()
+ */
+extern unsigned long gg_local_ip;
+/*
+ * -------------------------------------------------------------------------
+ * poni¿ej znajduj± siê wewnêtrzne sprawy biblioteki. zwyk³y klient nie
+ * powinien ich w ogóle ruszaæ, bo i nie ma po co. wszystko mo¿na za³atwiæ
+ * procedurami wy¿szego poziomu, których definicje znajduj± siê na pocz±tku
+ * tego pliku.
+ * -------------------------------------------------------------------------
+ */
+
+#ifdef __GG_LIBGADU_HAVE_PTHREAD
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
+#endif
+
+#ifdef _WIN32
+int gg_thread_socket(int thread_id, int socket);
+#endif
+
+int gg_resolve(int *fd, int *pid, const char *hostname);
+
+#ifdef __GNUC__
+char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+#else
+char *gg_saprintf(const char *format, ...);
+#endif
+
+char *gg_vsaprintf(const char *format, va_list ap);
+
+#define gg_alloc_sprintf gg_saprintf
+
+char *gg_get_line(char **ptr);
+
+int gg_connect(void *addr, int port, int async);
+struct in_addr *gg_gethostbyname(const char *hostname);
+char *gg_read_line(int sock, char *buf, int length);
+void gg_chomp(char *line);
+char *gg_urlencode(const char *str);
+int gg_http_hash(const char *format, ...);
+int gg_read(struct gg_session *sess, char *buf, int length);
+int gg_write(struct gg_session *sess, const char *buf, int length);
+void *gg_recv_packet(struct gg_session *sess);
+int gg_send_packet(struct gg_session *sess, int type, ...);
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed);
+uint32_t gg_fix32(uint32_t x);
+uint16_t gg_fix16(uint16_t x);
+#define fix16 gg_fix16
+#define fix32 gg_fix32
+char *gg_proxy_auth(void);
+char *gg_base64_encode(const char *buf);
+char *gg_base64_decode(const char *buf);
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq);
+
+#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
+#define GG_APPMSG_PORT 80
+#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
+#define GG_PUBDIR_PORT 80
+#define GG_REGISTER_HOST "register.gadu-gadu.pl"
+#define GG_REGISTER_PORT 80
+#define GG_REMIND_HOST "retr.gadu-gadu.pl"
+#define GG_REMIND_PORT 80
+
+#define GG_DEFAULT_PORT 8074
+#define GG_HTTPS_PORT 443
+#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
+
+#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158"
+#define GG_DEFAULT_PROTOCOL_VERSION 0x24
+#define GG_DEFAULT_TIMEOUT 30
+#define GG_HAS_AUDIO_MASK 0x40000000
+#define GG_ERA_OMNIX_MASK 0x04000000
+#define GG_LIBGADU_VERSION "CVS"
+
+#define GG_DEFAULT_DCC_PORT 1550
+
+struct gg_header {
+ uint32_t type; /* typ pakietu */
+ uint32_t length; /* d³ugo¶æ reszty pakietu */
+} GG_PACKED;
+
+#define GG_WELCOME 0x0001
+#define GG_NEED_EMAIL 0x0014
+
+struct gg_welcome {
+ uint32_t key; /* klucz szyfrowania has³a */
+} GG_PACKED;
+
+#define GG_LOGIN 0x000c
+
+struct gg_login {
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash has³a */
+ uint32_t status; /* status na dzieñ dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym s³ucham */
+} GG_PACKED;
+
+#define GG_LOGIN_EXT 0x0013
+
+struct gg_login_ext {
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash has³a */
+ uint32_t status; /* status na dzieñ dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym s³ucham */
+ uint32_t external_ip; /* zewnêtrzny adres ip */
+ uint16_t external_port; /* zewnêtrzny port */
+} GG_PACKED;
+
+#define GG_LOGIN60 0x0015
+
+struct gg_login60 {
+ uint32_t uin; /* mój numerek */
+ uint32_t hash; /* hash has³a */
+ uint32_t status; /* status na dzieñ dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t local_ip; /* mój adres ip */
+ uint16_t local_port; /* port, na którym s³ucham */
+ uint32_t external_ip; /* zewnêtrzny adres ip */
+ uint16_t external_port; /* zewnêtrzny port */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno2; /* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN_OK 0x0003
+
+#define GG_LOGIN_FAILED 0x0009
+
+#define GG_PUBDIR50_REQUEST 0x0014
+
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+struct gg_pubdir50_request {
+ uint8_t type; /* GG_PUBDIR50_* */
+ uint32_t seq; /* czas wys³ania zapytania */
+} GG_PACKED;
+
+#define GG_PUBDIR50_REPLY 0x000e
+
+struct gg_pubdir50_reply {
+ uint8_t type; /* GG_PUBDIR50_* */
+ uint32_t seq; /* czas wys³ania zapytania */
+} GG_PACKED;
+
+#define GG_NEW_STATUS 0x0002
+
+#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostêpny */
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostêpny z opisem (4.8) */
+#define GG_STATUS_AVAIL 0x0002 /* dostêpny */
+#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostêpny z opisem (4.9) */
+#define GG_STATUS_BUSY 0x0003 /* zajêty */
+#define GG_STATUS_BUSY_DESCR 0x0005 /* zajêty z opisem (4.8) */
+#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */
+#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */
+#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */
+
+#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */
+
+#define GG_STATUS_DESCR_MAXSIZE 70
+
+/*
+ * makra do ³atwego i szybkiego sprawdzania stanu.
+ */
+
+/* GG_S_F() tryb tylko dla znajomych */
+#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
+
+/* GG_S() stan bez uwzglêdnienia trybu tylko dla znajomych */
+#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK)
+
+/* GG_S_A() dostêpny */
+#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_NA() niedostêpny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+
+/* GG_S_B() zajêty */
+#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+
+/* GG_S_I() niewidoczny */
+#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_D() stan opisowy */
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_BL() blokowany lub blokuj±cy */
+#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
+
+struct gg_new_status {
+ uint32_t status; /* na jaki zmieniæ? */
+} GG_PACKED;
+
+#define GG_NOTIFY_FIRST 0x000f
+#define GG_NOTIFY_LAST 0x0010
+
+#define GG_NOTIFY 0x0010
+
+struct gg_notify {
+ uint32_t uin; /* numerek danej osoby */
+ uint8_t dunno1; /* rodzaj wpisu w li¶cie */
+} GG_PACKED;
+
+#define GG_USER_OFFLINE 0x01 /* bêdziemy niewidoczni dla u¿ytkownika */
+#define GG_USER_NORMAL 0x03 /* zwyk³y u¿ytkownik */
+#define GG_USER_BLOCKED 0x04 /* zablokowany u¿ytkownik */
+
+#define GG_LIST_EMPTY 0x0012
+
+#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */
+
+struct gg_notify_reply {
+ uint32_t uin; /* numerek */
+ uint32_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym s³ucha klient */
+ uint32_t version; /* wersja klienta */
+ uint16_t dunno2; /* znowu port? */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY60 0x0011
+
+struct gg_notify_reply60 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym s³ucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+} GG_PACKED;
+
+#define GG_STATUS60 0x000f
+
+struct gg_status60 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym s³ucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+} GG_PACKED;
+
+#define GG_ADD_NOTIFY 0x000d
+#define GG_REMOVE_NOTIFY 0x000e
+
+struct gg_add_remove {
+ uint32_t uin; /* numerek */
+ uint8_t dunno1; /* bitmapa */
+} GG_PACKED;
+
+#define GG_STATUS 0x0002
+
+struct gg_status {
+ uint32_t uin; /* numerek */
+ uint32_t status; /* nowy stan */
+} GG_PACKED;
+
+#define GG_SEND_MSG 0x000b
+
+#define GG_CLASS_QUEUED 0x0001
+#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
+#define GG_CLASS_MSG 0x0004
+#define GG_CLASS_CHAT 0x0008
+#define GG_CLASS_CTCP 0x0010
+#define GG_CLASS_ACK 0x0020
+#define GG_CLASS_EXT GG_CLASS_ACK /* kompatybilno¶æ wstecz */
+
+#define GG_MSG_MAXSIZE 2000
+
+struct gg_send_msg {
+ uint32_t recipient;
+ uint32_t seq;
+ uint32_t msgclass;
+} GG_PACKED;
+
+struct gg_msg_richtext {
+ uint8_t flag;
+ uint16_t length;
+} GG_PACKED;
+
+struct gg_msg_richtext_format {
+ uint16_t position;
+ uint8_t font;
+} GG_PACKED;
+
+struct gg_msg_richtext_image {
+ uint16_t unknown1;
+ uint32_t size;
+ uint32_t crc32;
+} GG_PACKED;
+
+#define GG_FONT_BOLD 0x01
+#define GG_FONT_ITALIC 0x02
+#define GG_FONT_UNDERLINE 0x04
+#define GG_FONT_COLOR 0x08
+#define GG_FONT_IMAGE 0x80
+
+struct gg_msg_richtext_color {
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+} GG_PACKED;
+
+struct gg_msg_recipients {
+ uint8_t flag;
+ uint32_t count;
+} GG_PACKED;
+
+struct gg_msg_image_request {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+} GG_PACKED;
+
+struct gg_msg_image_reply {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+ /* char filename[]; */
+ /* char image[]; */
+} GG_PACKED;
+
+#define GG_SEND_MSG_ACK 0x0005
+
+#define GG_ACK_BLOCKED 0x0001
+#define GG_ACK_DELIVERED 0x0002
+#define GG_ACK_QUEUED 0x0003
+#define GG_ACK_MBOXFULL 0x0004
+#define GG_ACK_NOT_DELIVERED 0x0006
+
+struct gg_send_msg_ack {
+ uint32_t status;
+ uint32_t recipient;
+ uint32_t seq;
+} GG_PACKED;
+
+#define GG_RECV_MSG 0x000a
+
+struct gg_recv_msg {
+ uint32_t sender;
+ uint32_t seq;
+ uint32_t time;
+ uint32_t msgclass;
+} GG_PACKED;
+
+#define GG_PING 0x0008
+
+#define GG_PONG 0x0007
+
+#define GG_DISCONNECTING 0x000b
+
+#define GG_USERLIST_REQUEST 0x0016
+
+#define GG_USERLIST_PUT 0x00
+#define GG_USERLIST_PUT_MORE 0x01
+#define GG_USERLIST_GET 0x02
+
+struct gg_userlist_request {
+ uint8_t type;
+} GG_PACKED;
+
+#define GG_USERLIST_REPLY 0x0010
+
+#define GG_USERLIST_PUT_REPLY 0x00
+#define GG_USERLIST_PUT_MORE_REPLY 0x02
+#define GG_USERLIST_GET_REPLY 0x06
+#define GG_USERLIST_GET_MORE_REPLY 0x04
+
+struct gg_userlist_reply {
+ uint8_t type;
+} GG_PACKED;
+
+/*
+ * pakiety, sta³e, struktury dla DCC
+ */
+
+struct gg_dcc_tiny_packet {
+ uint8_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_small_packet {
+ uint32_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_big_packet {
+ uint32_t type; /* rodzaj pakietu */
+ uint32_t dunno1; /* niewiadoma */
+ uint32_t dunno2; /* niewiadoma */
+} GG_PACKED;
+
+/*
+ * póki co, nie znamy dok³adnie protoko³u. nie wiemy, co czemu odpowiada.
+ * nazwy s± niepowa¿ne i tymczasowe.
+ */
+#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */
+#define GG_DCC_HAVE_FILE 0x0001 /* wiêc mu damy */
+#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */
+#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */
+#define GG_DCC_CATCH_FILE 0x0002 /* wysy³amy plik */
+
+#define GG_DCC_FILEATTR_READONLY 0x0020
+
+#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */
+#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */
+#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */
+#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */
+
+#ifdef __cplusplus
+}
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+#endif
+
+#endif /* __GG_LIBGADU_H */
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/pubdir.c b/kopete/protocols/gadu/libgadu/pubdir.c
new file mode 100644
index 00000000..7ed545ff
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/pubdir.c
@@ -0,0 +1,689 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libgadu.h"
+
+/*
+ * gg_register3()
+ *
+ * rozpoczyna rejestracjê u¿ytkownika protoko³em GG 6.0. wymaga wcze¶niejszego
+ * pobrania tokenu za pomoc± funkcji gg_token().
+ *
+ * - email - adres e-mail klienta
+ * - password - has³o klienta
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto¶æ tokenu
+ * - async - po³±czenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ
+ * funkcj± gg_register_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query;
+
+ if (!email || !password || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __pwd = gg_urlencode(password);
+ __email = gg_urlencode(email);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__pwd || !__email || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n");
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u",
+ __pwd, __email, __tokenid, __tokenval,
+ gg_http_hash("ss", email, password));
+
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ if (!form) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n");
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_REGISTER;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+/*
+ * gg_unregister3()
+ *
+ * usuwa konto u¿ytkownika z serwera protoko³em GG 6.0
+ *
+ * - uin - numerek GG
+ * - password - has³o klienta
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto¶æ tokenu
+ * - async - po³±czenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ
+ * funkcj± gg_unregister_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query;
+
+ if (!password || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __pwd = gg_saprintf("%ld", random());
+ __fmpwd = gg_urlencode(password);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n");
+ free(__pwd);
+ free(__fmpwd);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval, gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd));
+
+ free(__fmpwd);
+ free(__pwd);
+ free(__tokenid);
+ free(__tokenval);
+
+ if (!form) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n");
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_UNREGISTER;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+/*
+ * gg_change_passwd4()
+ *
+ * wysy³a ¿±danie zmiany has³a zgodnie z protoko³em GG 6.0. wymaga
+ * wcze¶niejszego pobrania tokenu za pomoc± funkcji gg_token().
+ *
+ * - uin - numer
+ * - email - adres e-mail
+ * - passwd - stare has³o
+ * - newpasswd - nowe has³o
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto¶æ tokenu
+ * - async - po³±czenie asynchroniczne
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ
+ * funkcj± gg_change_passwd_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval;
+
+ if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __fmpwd = gg_urlencode(passwd);
+ __pwd = gg_urlencode(newpasswd);
+ __email = gg_urlencode(email);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd)))) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ return NULL;
+ }
+
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_PASSWD;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+/*
+ * gg_remind_passwd3()
+ *
+ * wysy³a ¿±danie przypomnienia has³a e-mailem.
+ *
+ * - uin - numer
+ * - email - adres e-mail taki, jak ten zapisany na serwerze
+ * - async - po³±czenie asynchroniczne
+ * - tokenid - identyfikator tokenu
+ * - tokenval - warto¶æ tokenu
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ
+ * funkcj± gg_remind_passwd_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *form, *query, *__tokenid, *__tokenval, *__email;
+
+ if (!tokenid || !tokenval || !email) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+ __email = gg_urlencode(email);
+
+ if (!__tokenid || !__tokenval || !__email) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+ return NULL;
+ }
+
+ if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+ return NULL;
+ }
+
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+
+ gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REMIND_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_REMIND;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+/*
+ * gg_pubdir_watch_fd()
+ *
+ * przy asynchronicznych operacjach na katalogu publicznym nale¿y wywo³ywaæ
+ * tê funkcjê przy zmianach na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisuj±ca po³±czenie
+ *
+ * je¶li wszystko posz³o dobrze to 0, inaczej -1. operacja bêdzie
+ * zakoñczona, je¶li h->state == GG_STATE_DONE. je¶li wyst±pi jaki¶
+ * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error.
+ */
+int gg_pubdir_watch_fd(struct gg_http *h)
+{
+ struct gg_pubdir *p;
+ char *tmp;
+
+ if (!h) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_ERROR) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (h->state != GG_STATE_PARSING)
+ return 0;
+
+ h->state = GG_STATE_DONE;
+
+ if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n");
+ return -1;
+ }
+
+ p->success = 0;
+ p->uin = 0;
+
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
+
+ if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
+ p->success = 1;
+ p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
+ } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
+ p->success = 1;
+ if (tmp[7] == ':')
+ p->uin = strtol(tmp + 8, NULL, 0);
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin);
+ } else
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n");
+
+ return 0;
+}
+
+/*
+ * gg_pubdir_free()
+ *
+ * zwalnia pamiêæ po efektach operacji na katalogu publicznym.
+ *
+ * - h - zwalniana struktura
+ */
+void gg_pubdir_free(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ free(h->data);
+ gg_http_free(h);
+}
+
+/*
+ * gg_token()
+ *
+ * pobiera z serwera token do autoryzacji zak³adania konta, usuwania
+ * konta i zmiany has³a.
+ *
+ * zaalokowana struct gg_http, któr± po¼niej nale¿y zwolniæ
+ * funkcj± gg_token_free(), albo NULL je¶li wyst±pi³ b³±d.
+ */
+struct gg_http *gg_token(int async)
+{
+ struct gg_http *h;
+ const char *query;
+
+ query = "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: 0\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n";
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+ return NULL;
+ }
+
+ h->type = GG_SESSION_TOKEN;
+
+ h->callback = gg_token_watch_fd;
+ h->destroy = gg_token_free;
+
+ if (!async)
+ gg_token_watch_fd(h);
+
+ return h;
+}
+
+/*
+ * gg_token_watch_fd()
+ *
+ * przy asynchronicznych operacjach zwi±zanych z tokenem nale¿y wywo³ywaæ
+ * tê funkcjê przy zmianach na obserwowanym deskryptorze.
+ *
+ * - h - struktura opisuj±ca po³±czenie
+ *
+ * je¶li wszystko posz³o dobrze to 0, inaczej -1. operacja bêdzie
+ * zakoñczona, je¶li h->state == GG_STATE_DONE. je¶li wyst±pi jaki¶
+ * b³±d, to bêdzie tam GG_STATE_ERROR i odpowiedni kod b³êdu w h->error.
+ */
+int gg_token_watch_fd(struct gg_http *h)
+{
+ if (!h) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_ERROR) {
+ gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> token, http failure\n");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (h->state != GG_STATE_PARSING)
+ return 0;
+
+ /* je¶li h->data jest puste, to ¶ci±gali¶my tokenid i url do niego,
+ * ale je¶li co¶ tam jest, to znaczy, ¿e mamy drugi etap polegaj±cy
+ * na pobieraniu tokenu. */
+ if (!h->data) {
+ int width, height, length;
+ char *url = NULL, *tokenid = NULL, *path, *headers;
+ const char *host;
+ struct gg_http *h2;
+ struct gg_token *t;
+
+ gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body);
+
+ if (h->body && (!(url = malloc(strlen(h->body))) || !(tokenid = malloc(strlen(h->body))))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n");
+ free(url);
+ return -1;
+ }
+
+ if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) {
+ gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n");
+ free(url);
+ free(tokenid);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* dostali¶my tokenid i wszystkie niezbêdne informacje,
+ * wiêc pobierzmy obrazek z tokenem */
+
+ if (strncmp(url, "http://", 7)) {
+ path = gg_saprintf("%s?tokenid=%s", url, tokenid);
+ host = GG_REGISTER_HOST;
+ } else {
+ char *slash = strchr(url + 7, '/');
+
+ if (slash) {
+ path = gg_saprintf("%s?tokenid=%s", slash, tokenid);
+ *slash = 0;
+ host = url + 7;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n");
+ free(url);
+ free(tokenid);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (!path) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+ free(url);
+ free(tokenid);
+ return -1;
+ }
+
+ if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+ free(path);
+ free(url);
+ free(tokenid);
+ return -1;
+ }
+
+ if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+ free(headers);
+ free(url);
+ free(path);
+ free(tokenid);
+ return -1;
+ }
+
+ free(headers);
+ free(path);
+ free(url);
+
+ memcpy(h, h2, sizeof(struct gg_http));
+ free(h2);
+
+ h->type = GG_SESSION_TOKEN;
+
+ h->callback = gg_token_watch_fd;
+ h->destroy = gg_token_free;
+
+ if (!h->async)
+ gg_token_watch_fd(h);
+
+ if (!(h->data = t = malloc(sizeof(struct gg_token)))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n");
+ free(tokenid);
+ return -1;
+ }
+
+ t->width = width;
+ t->height = height;
+ t->length = length;
+ t->tokenid = tokenid;
+ } else {
+ /* obrazek mamy w h->body */
+ h->state = GG_STATE_DONE;
+ }
+
+ return 0;
+}
+
+/*
+ * gg_token_free()
+ *
+ * zwalnia pamiêæ po efektach pobierania tokenu.
+ *
+ * - h - zwalniana struktura
+ */
+void gg_token_free(struct gg_http *h)
+{
+ struct gg_token *t;
+
+ if (!h)
+ return;
+
+ if ((t = h->data))
+ free(t->tokenid);
+
+ free(h->data);
+ gg_http_free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/libgadu/pubdir50.c b/kopete/protocols/gadu/libgadu/pubdir50.c
new file mode 100644
index 00000000..6ef285e7
--- /dev/null
+++ b/kopete/protocols/gadu/libgadu/pubdir50.c
@@ -0,0 +1,467 @@
+/* $Id$ */
+
+/*
+ * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program 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 program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libgadu.h"
+
+/*
+ * gg_pubdir50_new()
+ *
+ * tworzy now± zmienn± typu gg_pubdir50_t.
+ *
+ * zaalokowana zmienna lub NULL w przypadku braku pamiêci.
+ */
+gg_pubdir50_t gg_pubdir50_new(int type)
+{
+ gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
+
+ if (!res) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
+ return NULL;
+ }
+
+ memset(res, 0, sizeof(struct gg_pubdir50_s));
+
+ res->type = type;
+
+ return res;
+}
+
+/*
+ * gg_pubdir50_add_n() // funkcja wewnêtrzna
+ *
+ * funkcja dodaje lub zastêpuje istniej±ce pole do zapytania lub odpowiedzi.
+ *
+ * - req - wska¼nik opisu zapytania,
+ * - num - numer wyniku (0 dla zapytania),
+ * - field - nazwa pola,
+ * - value - warto¶æ pola,
+ *
+ * 0/-1
+ */
+static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
+{
+ struct gg_pubdir50_entry *tmp = NULL, *entry;
+ char *dupfield, *dupvalue;
+ int i;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
+
+ if (!(dupvalue = strdup(value))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < req->entries_count; i++) {
+ if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
+ continue;
+
+ free(req->entries[i].value);
+ req->entries[i].value = dupvalue;
+
+ return 0;
+ }
+
+ if (!(dupfield = strdup(field))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ free(dupvalue);
+ return -1;
+ }
+
+ if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ free(dupfield);
+ free(dupvalue);
+ return -1;
+ }
+
+ req->entries = tmp;
+
+ entry = &req->entries[req->entries_count];
+ entry->num = num;
+ entry->field = dupfield;
+ entry->value = dupvalue;
+
+ req->entries_count++;
+
+ return 0;
+}
+
+/*
+ * gg_pubdir50_add()
+ *
+ * funkcja dodaje pole do zapytania.
+ *
+ * - req - wska¼nik opisu zapytania,
+ * - field - nazwa pola,
+ * - value - warto¶æ pola,
+ *
+ * 0/-1
+ */
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
+{
+ return gg_pubdir50_add_n(req, 0, field, value);
+}
+
+/*
+ * gg_pubdir50_seq_set()
+ *
+ * ustawia numer sekwencyjny zapytania.
+ *
+ * - req - zapytanie,
+ * - seq - nowy numer sekwencyjny.
+ *
+ * 0/-1.
+ */
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
+
+ if (!req) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ req->seq = seq;
+
+ return 0;
+}
+
+/*
+ * gg_pubdir50_free()
+ *
+ * zwalnia pamiêæ po zapytaniu lub rezultacie szukania u¿ytkownika.
+ *
+ * - s - zwalniana zmienna,
+ */
+void gg_pubdir50_free(gg_pubdir50_t s)
+{
+ int i;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < s->entries_count; i++) {
+ free(s->entries[i].field);
+ free(s->entries[i].value);
+ }
+
+ free(s->entries);
+ free(s);
+}
+
+/*
+ * gg_pubdir50()
+ *
+ * wysy³a zapytanie katalogu publicznego do serwera.
+ *
+ * - sess - sesja,
+ * - req - zapytanie.
+ *
+ * numer sekwencyjny wyszukiwania lub 0 w przypadku b³êdu.
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
+{
+ int i, size = 5;
+ uint32_t res;
+ char *buf, *p;
+ struct gg_pubdir50_request *r;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+
+ if (!sess || !req) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+ errno = EFAULT;
+ return 0;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+ errno = ENOTCONN;
+ return 0;
+ }
+
+ for (i = 0; i < req->entries_count; i++) {
+ /* wyszukiwanie bierze tylko pierwszy wpis */
+ if (req->entries[i].num)
+ continue;
+
+ size += strlen(req->entries[i].field) + 1;
+ size += strlen(req->entries[i].value) + 1;
+ }
+
+ if (!(buf = malloc(size))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+ return 0;
+ }
+
+ r = (struct gg_pubdir50_request*) buf;
+ res = time(NULL);
+ r->type = req->type;
+ r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL));
+ req->seq = gg_fix32(r->seq);
+
+ for (i = 0, p = buf + 5; i < req->entries_count; i++) {
+ if (req->entries[i].num)
+ continue;
+
+ strcpy(p, req->entries[i].field);
+ p += strlen(p) + 1;
+
+ strcpy(p, req->entries[i].value);
+ p += strlen(p) + 1;
+ }
+
+ if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
+ res = 0;
+
+ free(buf);
+
+ return res;
+}
+
+/*
+ * gg_pubdir50_handle_reply() // funkcja wewnêtrzna
+ *
+ * analizuje przychodz±cy pakiet odpowiedzi i zapisuje wynik w struct gg_event.
+ *
+ * - e - opis zdarzenia
+ * - packet - zawarto¶æ pakietu odpowiedzi
+ * - length - d³ugo¶æ pakietu odpowiedzi
+ *
+ * 0/-1
+ */
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+ const char *end = packet + length, *p;
+ struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet;
+ gg_pubdir50_t res;
+ int num = 0;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length);
+
+ if (!e || !packet) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (length < 5) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(res = gg_pubdir50_new(r->type))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
+ return -1;
+ }
+
+ e->event.pubdir50 = res;
+
+ res->seq = gg_fix32(r->seq);
+
+ switch (res->type) {
+ case GG_PUBDIR50_READ:
+ e->type = GG_EVENT_PUBDIR50_READ;
+ break;
+
+ case GG_PUBDIR50_WRITE:
+ e->type = GG_EVENT_PUBDIR50_WRITE;
+ break;
+
+ default:
+ e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
+ break;
+ }
+
+ /* brak wyników? */
+ if (length == 5)
+ return 0;
+
+ /* pomiñ pocz±tek odpowiedzi */
+ p = packet + 5;
+
+ while (p < end) {
+ const char *field, *value;
+
+ field = p;
+
+ /* sprawd¼, czy nie mamy podzia³u na kolejne pole */
+ if (!*field) {
+ num++;
+ field++;
+ }
+
+ value = NULL;
+
+ for (p = field; p < end; p++) {
+ /* je¶li mamy koniec tekstu... */
+ if (!*p) {
+ /* ...i jeszcze nie mieli¶my warto¶ci pola to
+ * wiemy, ¿e po tym zerze jest warto¶æ... */
+ if (!value)
+ value = p + 1;
+ else
+ /* ...w przeciwym wypadku koniec
+ * warto¶ci i mo¿emy wychodziæ
+ * grzecznie z pêtli */
+ break;
+ }
+ }
+
+ /* sprawd¼my, czy pole nie wychodzi poza pakiet, ¿eby nie
+ * mieæ segfaultów, je¶li serwer przestanie zakañczaæ pakietów
+ * przez \0 */
+
+ if (p == end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
+ goto failure;
+ }
+
+ p++;
+
+ /* je¶li dostali¶my namier na nastêpne wyniki, to znaczy ¿e
+ * mamy koniec wyników i nie jest to kolejna osoba. */
+ if (!strcasecmp(field, "nextstart")) {
+ res->next = atoi(value);
+ num--;
+ } else {
+ if (gg_pubdir50_add_n(res, num, field, value) == -1)
+ goto failure;
+ }
+ }
+
+ res->count = num + 1;
+
+ return 0;
+
+failure:
+ gg_pubdir50_free(res);
+ return -1;
+}
+
+/*
+ * gg_pubdir50_get()
+ *
+ * pobiera informacjê z rezultatu wyszukiwania.
+ *
+ * - res - rezultat wyszukiwania,
+ * - num - numer odpowiedzi,
+ * - field - nazwa pola (wielko¶æ liter nie ma znaczenia).
+ *
+ * warto¶æ pola lub NULL, je¶li nie znaleziono.
+ */
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
+{
+ char *value = NULL;
+ int i;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
+
+ if (!res || num < 0 || !field) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ for (i = 0; i < res->entries_count; i++) {
+ if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
+ value = res->entries[i].value;
+ break;
+ }
+ }
+
+ return value;
+}
+
+/*
+ * gg_pubdir50_count()
+ *
+ * zwraca ilo¶æ wyników danego zapytania.
+ *
+ * - res - odpowied¼
+ *
+ * ilo¶æ lub -1 w przypadku b³êdu.
+ */
+int gg_pubdir50_count(gg_pubdir50_t res)
+{
+ return (!res) ? -1 : res->count;
+}
+
+/*
+ * gg_pubdir50_type()
+ *
+ * zwraca rodzaj zapytania lub odpowiedzi.
+ *
+ * - res - zapytanie lub odpowied¼
+ *
+ * ilo¶æ lub -1 w przypadku b³êdu.
+ */
+int gg_pubdir50_type(gg_pubdir50_t res)
+{
+ return (!res) ? -1 : res->type;
+}
+
+/*
+ * gg_pubdir50_next()
+ *
+ * zwraca numer, od którego nale¿y rozpocz±æ kolejne wyszukiwanie, je¶li
+ * zale¿y nam na kolejnych wynikach.
+ *
+ * - res - odpowied¼
+ *
+ * numer lub -1 w przypadku b³êdu.
+ */
+uin_t gg_pubdir50_next(gg_pubdir50_t res)
+{
+ return (!res) ? (unsigned) -1 : res->next;
+}
+
+/*
+ * gg_pubdir50_seq()
+ *
+ * zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ *
+ * - res - zapytanie lub odpowied¼
+ *
+ * numer lub -1 w przypadku b³êdu.
+ */
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
+{
+ return (!res) ? (unsigned) -1 : res->seq;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/kopete/protocols/gadu/ui/Makefile.am b/kopete/protocols/gadu/ui/Makefile.am
new file mode 100644
index 00000000..12ee702a
--- /dev/null
+++ b/kopete/protocols/gadu/ui/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libgaduui.la
+
+libgaduui_la_SOURCES = gaduadd.ui gadusearch.ui gadueditaccountui.ui gaduawayui.ui gaduregisteraccountui.ui \
+ empty.cpp
+
+EXTRA_DIST = gaduadd.ui gadusearch.ui gadueditaccountui.ui gaduawayui.ui gaduregisteraccountui.ui \
+ empty.cpp
diff --git a/kopete/protocols/gadu/ui/empty.cpp b/kopete/protocols/gadu/ui/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/gadu/ui/empty.cpp
diff --git a/kopete/protocols/gadu/ui/gaduadd.ui b/kopete/protocols/gadu/ui/gaduadd.ui
new file mode 100644
index 00000000..f8d673b6
--- /dev/null
+++ b/kopete/protocols/gadu/ui/gaduadd.ui
@@ -0,0 +1,360 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GaduAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GaduAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>394</width>
+ <height>340</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout39</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Gadu-Gadu &amp;UIN:</string>
+ </property>
+ <property name="textFormat">
+ <enum>AutoText</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the Gadu-Gadu account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the Gadu-Gadu account you would like to add. This should be in the form of a number (no decimals, no spaces). This field is mandatory.</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine">
+ <property name="name">
+ <cstring>addEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the Gadu-Gadu account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the Gadu-Gadu account you would like to add. This should be in the form of a number (no decimals, no spaces). This field is mandatory.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: 1234567)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Forename:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fornameEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The forename of the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The forename (first name) of the contact you wish to add. Optionally this may include a middle name.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Surname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>snameEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The surname of the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The surname (last name) of the contact you wish to add.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>N&amp;ickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A nickname for the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A nickname for the contact you wish to add.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Email address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_4_2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Telephone number:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>fornameEdit_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The forename of the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The forename (first name) of the contact you wish to add. Optionally this may include a middle name.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>snameEdit_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The surname of the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The surname (last name) of the contact you wish to add.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>nickEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A nickname for the contact you wish to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A nickname for the contact you wish to add.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>emailEdit_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>telephoneEdit_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>E-Mail address for this contact.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>notAFriend_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Offline to contact when you set "&amp;Just for friends"</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check if you want to exclude this contact from the "Just for friends" status mode.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check if you want to exclude this contact from the "Just for friends" status mode.</string>
+ </property>
+ </widget>
+ <widget class="QListView" row="3" column="0">
+ <column>
+ <property name="text">
+ <string>Group</string>
+ </property>
+ <property name="clickable">
+ <bool>false</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <item>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>groups</cstring>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>krestrictedline.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/gadu/ui/gaduawayui.ui b/kopete/protocols/gadu/ui/gaduawayui.ui
new file mode 100644
index 00000000..3e475bce
--- /dev/null
+++ b/kopete/protocols/gadu/ui/gaduawayui.ui
@@ -0,0 +1,194 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GaduAwayUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GaduAwayUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>332</width>
+ <height>188</height>
+ </rect>
+ </property>
+ <property name="backgroundOrigin">
+ <enum>WidgetOrigin</enum>
+ </property>
+ <property name="caption">
+ <string>Away Dialog</string>
+ </property>
+ <property name="focusPolicy">
+ <enum>TabFocus</enum>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>statusGroup_</cstring>
+ </property>
+ <property name="title">
+ <string>Status</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Choose status, by default present status is selected.
+So all you need to do is just to type in your description.
+Choosing Offline status will disconnect you, with given description.</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>onlineButton_</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;nline</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>4</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Set your status to Online.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Set your status to Online, indicating that you are available to chat with anyone who wishes.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>awayButton_</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Busy</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>5</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Set your status to busy.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Set your status to busy, indicating that you may should not be bothered with trivial chat, and may not be able to reply immediately.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>invisibleButton_</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Invisible</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>22</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Set status to invisible, which will hide your presence from other users.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Set status to invisible, which will hide your presence from other users (who will see you as offline). However you may still chat, and see the online presence of others.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>offlineButton_</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;ffline</string>
+ </property>
+ <property name="buttonGroupId">
+ <number>21</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Choose this status to disconnect with description entered below.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Choose this status to disconnect with description entered below.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout278</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>textEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Description of your status.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Description of your status (up to 70 characters).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>textEdit_</cstring>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="maxLength">
+ <number>70</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Description of your status.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Description of your status (up to 70 characters).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>textEdit_</tabstop>
+ <tabstop>onlineButton_</tabstop>
+ <tabstop>awayButton_</tabstop>
+ <tabstop>invisibleButton_</tabstop>
+ <tabstop>offlineButton_</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/gadu/ui/gadueditaccountui.ui b/kopete/protocols/gadu/ui/gadueditaccountui.ui
new file mode 100644
index 00000000..01edaa08
--- /dev/null
+++ b/kopete/protocols/gadu/ui/gadueditaccountui.ui
@@ -0,0 +1,873 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GaduAccountEditUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GaduAccountEditUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>580</width>
+ <height>390</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - Gadu-Gadu</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget4</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </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>groupBox63</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>layout8</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Gadu-Gadu &amp;UIN:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>loginEdit_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of your Gadu-Gadu account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of your Gadu-Gadu account. This should be in the form of a number (no decimals, no spaces).</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>loginEdit_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maxLength">
+ <number>16</number>
+ </property>
+ <property name="edited">
+ <bool>true</bool>
+ </property>
+ <property name="dragEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of your Gadu-Gadu account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of your Gadu-Gadu account. This should be in the form of a number (no decimals, no spaces).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget">
+ <property name="name">
+ <cstring>passwordWidget_</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>autoLoginCheck_</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></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>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the Gadu-Gadu network, you will need a Gadu-Gadu account.&lt;br&gt;&lt;br&gt;
+If you do not currently have an account, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>registerNew</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Re&amp;gister New Account</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>A&amp;ccount Preferences</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>160</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox64</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>dccCheck_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Use direct connections (DCC)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout65</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Use protocol encr&amp;yption (SSL):</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>useTls_</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Whether or not you want to enable SSL encrypted communication with the server.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Whether or not you want to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>If Available</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Required</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Do Not Use</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>useTls_</cstring>
+ </property>
+ <property name="autoCompletion">
+ <bool>false</bool>
+ </property>
+ <property name="duplicatesEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Whether or not you want to enable SSL encrypted communication with the server.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Whether or not you want to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>cacheServersCheck__</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>C&amp;ache server information</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Cache connection information for each server connected to in case the main load-balancing server fails.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option is used whenever the primary Gadu-Gadu load-balancing server fails. If this is checked, Kopete will try to connect to the actual servers directly using cached information about them. This prevents connection errors when the main load-balancing server does not answer. In practice it only helps very rarely.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>ignoreCheck_</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Ignore people off your contact list</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>U&amp;ser Information</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>connectLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;You must be connected to change your Personal Information.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>userInformation</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>User Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>uiSurnamea</cstring>
+ </property>
+ <property name="text">
+ <string>Surname:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3</cstring>
+ </property>
+ <property name="text">
+ <string>Your nick name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Gender:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Year of birth:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiSurname</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>nickName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string></string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Female</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Male</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>uiGender</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiYOB</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiCity</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_5_3_2_4_2</cstring>
+ </property>
+ <property name="text">
+ <string>Values below are going to be used in search, but will not appear in results.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer15_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3_2_4</cstring>
+ </property>
+ <property name="text">
+ <string>Maiden name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5_3_2_4_3</cstring>
+ </property>
+ <property name="text">
+ <string>City of origin:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiMeiden</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uiOrgin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;File Transfer</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>dcc</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="title">
+ <string>Global DCC Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;qt&gt;&lt;p align="center"&gt;&lt;font color="#ff0000"&gt;These options affect &lt;b&gt;all&lt;/b&gt; Gadu-Gadu accounts.&lt;/font&gt;&lt;/p&gt;&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionOverrideDCC</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Override default configuration</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout32</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Local &amp;IP address /</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ipAddress</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>dccPort</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KRestrictedLine">
+ <property name="name">
+ <cstring>ipAddress</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>0.0.0.0</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>dccPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>8010</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <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>180</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>
+ </hbox>
+</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>
+<connections>
+ <connection>
+ <sender>optionOverrideDCC</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>dccPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideDCC</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>ipAddress</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideDCC</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2_3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideDCC</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget4</tabstop>
+ <tabstop>loginEdit_</tabstop>
+ <tabstop>autoLoginCheck_</tabstop>
+ <tabstop>registerNew</tabstop>
+ <tabstop>cacheServersCheck__</tabstop>
+ <tabstop>dccCheck_</tabstop>
+ <tabstop>useTls_</tabstop>
+ <tabstop>optionOverrideDCC</tabstop>
+ <tabstop>ipAddress</tabstop>
+ <tabstop>dccPort</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>krestrictedline.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/gadu/ui/gaduregisteraccountui.ui b/kopete/protocols/gadu/ui/gaduregisteraccountui.ui
new file mode 100644
index 00000000..5a0e475e
--- /dev/null
+++ b/kopete/protocols/gadu/ui/gaduregisteraccountui.ui
@@ -0,0 +1,423 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GaduRegisterAccountUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GaduRegisterAccountUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>376</width>
+ <height>394</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Register Account - Gadu-Gadu</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>pixmapEmailAddress</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>labelPasswordVerify</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Repeat pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>valuePasswordVerify</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A confirmation of the password you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A confirmation of the password you would like to use for this account.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="2">
+ <property name="name">
+ <cstring>valuePassword</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The password you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The password you would like to use for this account.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="2">
+ <property name="name">
+ <cstring>valueEmailAddress</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your E-mail address.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The E-mail address you would like to use to register this account.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>pixmapVerificationSequence</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>labelEmailAddress</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;E-Mail address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>valueEmailAddress</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your E-mail address.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The E-mail address you would like to use to register this account.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>pixmapPasswordVerify</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>labelVerificationSequence</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Verification sequence:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>valueVerificationSequence</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The text from the image below.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The text from the image below. This is used to prevent abusive automated registration scripts.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="2">
+ <property name="name">
+ <cstring>valueVerificationSequence</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The text from the image below.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The text from the image below. This is used to prevent abusive automated registration scripts.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>pixmapPassword</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>labelPassword</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>valuePassword</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The password you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The password you would like to use for this account.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="2">
+ <property name="name">
+ <cstring>valuePasswordVerify</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A confirmation of the password you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A confirmation of the password you would like to use for this account.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layoutImageCenter</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacerImageLeft</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>23</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>pixmapToken</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>20</horstretch>
+ <verstretch>13</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>256</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>256</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="backgroundMode">
+ <enum>PaletteForeground</enum>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>255</green>
+ <blue>255</blue>
+ </color>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Gadu-Gadu registration token.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This field contains an image with number that you need to type into the &lt;b&gt;Verification Sequence&lt;/b&gt; field above.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacerImageRight</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>22</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelInstructions</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;Type the letters and numbers shown in the image above into the &lt;b&gt;Verification Sequence&lt;/b&gt; field. This is used to prevent automated registration abuse.&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacerMiddle</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <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>
+<tabstops>
+ <tabstop>valueEmailAddress</tabstop>
+ <tabstop>valuePassword</tabstop>
+ <tabstop>valuePasswordVerify</tabstop>
+ <tabstop>valueVerificationSequence</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/gadu/ui/gadusearch.ui b/kopete/protocols/gadu/ui/gadusearch.ui
new file mode 100644
index 00000000..ac215b1a
--- /dev/null
+++ b/kopete/protocols/gadu/ui/gadusearch.ui
@@ -0,0 +1,692 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>GaduPublicDirectory</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GaduPublicDirectory</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>570</width>
+ <height>386</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QWidgetStack" row="0" column="0">
+ <property name="name">
+ <cstring>pubsearch</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup2</cstring>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout38</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout36</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout31</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout29</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1a</cstring>
+ </property>
+ <property name="text">
+ <string>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2a</cstring>
+ </property>
+ <property name="text">
+ <string>Surname:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3a</cstring>
+ </property>
+ <property name="text">
+ <string>Nick:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3_2a</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout30</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>nameS</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>surname</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>nick</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>cityS</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout34</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2_2a</cstring>
+ </property>
+ <property name="text">
+ <string>Age from:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>ageFrom</cstring>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="buttonSymbols">
+ <enum>PlusMinus</enum>
+ </property>
+ <property name="maxValue">
+ <number>120</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>to:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>ageTo</cstring>
+ </property>
+ <property name="cursor">
+ <cursor>0</cursor>
+ </property>
+ <property name="buttonSymbols">
+ <enum>PlusMinus</enum>
+ </property>
+ <property name="maxValue">
+ <number>120</number>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>297</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout32</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4a</cstring>
+ </property>
+ <property name="text">
+ <string>Gender:</string>
+ </property>
+ <property name="textFormat">
+ <enum>PlainText</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string></string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Male</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Female</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>gender</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>TabFocus</enum>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout37</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>uin_static</cstring>
+ </property>
+ <property name="text">
+ <string>User number:</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine">
+ <property name="name">
+ <cstring>UIN</cstring>
+ </property>
+ <property name="maxLength">
+ <number>32</number>
+ </property>
+ <property name="echoMode">
+ <enum>Normal</enum>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>radioByUin</cstring>
+ </property>
+ <property name="text">
+ <string>Request information about user:</string>
+ </property>
+ <property name="autoRepeat">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>radioByData</cstring>
+ </property>
+ <property name="autoMask">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Search by specified data:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0">
+ <property name="name">
+ <cstring>layout35</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>onlyOnline</cstring>
+ </property>
+ <property name="text">
+ <string>Lookup only those that are currently online</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>224</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer row="4" column="0">
+ <property name="name">
+ <cstring>spacer5</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>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>1</number>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Nick Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Age</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>City</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>UIN</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>false</bool>
+ </property>
+ </column>
+ <item>
+ <property name="text">
+ <string>12</string>
+ </property>
+ <property name="text">
+ <string>DONT_TRANSLATE</string>
+ </property>
+ <property name="text">
+ <string>DONT_TRANSL</string>
+ </property>
+ <property name="text">
+ <string>999</string>
+ </property>
+ <property name="text">
+ <string>DONT_TRANSL</string>
+ </property>
+ <property name="text">
+ <string>245324956234</string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>listFound</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>512</width>
+ <height>256</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>640</width>
+ <height>512</height>
+ </size>
+ </property>
+ <property name="lineWidth">
+ <number>2</number>
+ </property>
+ <property name="resizePolicy">
+ <enum>AutoOneFit</enum>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Extended</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showSortIndicator">
+ <bool>true</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>NoColumn</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>false</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="autoOpen">
+ <bool>false</bool>
+ </property>
+ <property name="dropVisualizer">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="499">789c8590cf6ac3300cc6ef790a13dd4259d37414c6e823acec58183be85fd21dda42d71ec6d8bb57929d50b6c28463f4fba4cf913d6fd2f6f52535f3eaf38ce70f4ebcc3536ae4b2df7fbdbdafbfab7ab14ab69ed2a29e55f543e2b4391ed473b01cda08c7de9127544765796cc3288e7d275dbb74c4829aab43603f7a29a362ae7246afc70c39004a5234434084488a0686a179923543f42f3698fa90984990a63e1389894455a7f3008818544004217bdd695d117e60d19054c49650d1c22b7e07d5d1ebffb08e61b0e6db996d0a4421fd6fe63f77bbfb06bfdeeae7b9ba0259af7424</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>radioByData</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>UIN</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByData</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>uin_static</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>ageFrom</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>ageTo</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cityS</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>gender</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nameS</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>surname</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2_2a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_3_2a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>nick</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_4a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>radioByUin</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_3a</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>nameS</tabstop>
+ <tabstop>surname</tabstop>
+ <tabstop>nick</tabstop>
+ <tabstop>cityS</tabstop>
+ <tabstop>gender</tabstop>
+ <tabstop>UIN</tabstop>
+ <tabstop>ageFrom</tabstop>
+ <tabstop>ageTo</tabstop>
+ <tabstop>onlyOnline</tabstop>
+ <tabstop>listFound</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>krestrictedline.h</includehint>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <wstephenson@novell.com> 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 foo@novell.com
+ {
+ 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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#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 <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef 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 <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 <will@stevello.free-online.co.uk>
+
+ Blocking status taken from MSN
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <will@stevello.free-online.co.uk>
+
+ Blocking status taken from MSN
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <will@stevello.free-online.co.uk>
+ 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <will@stevello.free-online.co.uk>
+ 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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-Email=will@stevello.free-online.co.uk
+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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+
+#include "client.h"
+#include "tasks/chatcountstask.h"
+#include "tasks/chatpropertiestask.h"
+#include "tasks/searchchattask.h"
+
+#include "chatroommanager.h"
+
+ChatroomManager::ChatroomManager( Client * parent, const char *name)
+ : QObject(parent, name), m_client( parent ), m_replace( false )
+{
+}
+
+ChatroomManager::~ChatroomManager()
+{
+}
+
+void ChatroomManager::updateRooms()
+{
+ getChatrooms( !m_rooms.isEmpty() );
+}
+
+GroupWise::ChatroomMap ChatroomManager::rooms()
+{
+ return m_rooms;
+}
+
+void ChatroomManager::getChatrooms( bool refresh )
+{
+ m_replace = !refresh;
+ SearchChatTask * sct = new SearchChatTask( m_client->rootTask() );
+ sct->search( ( refresh ? SearchChatTask::SinceLastSearch : SearchChatTask::FetchAll ) );
+ connect( sct, SIGNAL( finished() ), SLOT( slotGotChatroomList() ) );
+ sct->go( true );
+}
+
+void ChatroomManager::slotGotChatroomList()
+{
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ SearchChatTask * sct = (SearchChatTask *)sender();
+ if ( sct )
+ {
+ if ( m_replace )
+ m_rooms.clear();
+
+ QValueList<ChatroomSearchResult> roomsFound = sct->results();
+ QValueList<ChatroomSearchResult>::Iterator it = roomsFound.begin();
+ const QValueList<ChatroomSearchResult>::Iterator end = roomsFound.end();
+ for ( ; it != end; ++it )
+ {
+ GroupWise::Chatroom c( *it );
+ m_rooms.insert( c.displayName, c );
+ }
+ }
+ emit updated();
+}
+
+void ChatroomManager::updateCounts()
+{
+ ChatCountsTask * cct = new ChatCountsTask( m_client->rootTask() );
+ connect( cct, SIGNAL( finished() ), SLOT( slotGotChatCounts() ) );
+ cct->go( true );
+}
+
+void ChatroomManager::slotGotChatCounts()
+{
+ ChatCountsTask * cct = (ChatCountsTask *)sender();
+ if ( cct )
+ {
+ QMap< QString, int > newCounts = cct->results();
+ QMap< QString, int >::iterator it = newCounts.begin();
+ const QMap< QString, int >::iterator end = newCounts.end();
+
+ for ( ; it != end; ++it )
+ if ( m_rooms.contains( it.key() ) )
+ m_rooms[ it.key() ].participantsCount = it.data();
+ }
+ emit updated();
+}
+
+void ChatroomManager::requestProperties( const QString & displayName )
+{
+ if ( 0 /*m_rooms.contains( displayName ) */)
+ emit gotProperties( m_rooms[ displayName ] );
+ else
+ {
+ ChatPropertiesTask * cpt = new ChatPropertiesTask( m_client->rootTask() );
+ cpt->setChat( displayName );
+ connect ( cpt, SIGNAL( finished() ), SLOT( slotGotChatProperties() ) );
+ cpt->go( true );
+ }
+}
+
+void ChatroomManager::slotGotChatProperties()
+{
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << endl;
+ ChatPropertiesTask * cpt = (ChatPropertiesTask *)sender();
+ if ( cpt )
+ {
+ Chatroom room = m_rooms[ cpt->m_chat ];
+ room.displayName = cpt->m_chat;
+ room.ownerDN = cpt->m_ownerDn;
+ room.description = cpt->m_description;
+ room.disclaimer = cpt->m_disclaimer;
+ room.query = cpt->m_query;
+ room.archive = ( cpt->m_archive == "0" );
+ room.maxUsers = cpt->m_maxUsers.toInt();
+ room.topic = cpt->m_topic;
+ room.creatorDN = cpt->m_creatorDn;
+ room.createdOn = cpt->m_creationTime;
+ room.acl = cpt->m_aclEntries;
+ room.chatRights = cpt->m_rights;
+ m_rooms.insert( room.displayName, room );
+ emit gotProperties( room );
+ }
+}
+
+#include "chatroommanager.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/chatroommanager.h b/kopete/protocols/groupwise/libgroupwise/chatroommanager.h
new file mode 100644
index 00000000..4d0e888b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/chatroommanager.h
@@ -0,0 +1,66 @@
+/*
+ Kopete Groupwise Protocol
+ chatroommanager.h - tracks our knowledge of server side chatrooms
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATROOMMANAGER_H
+#define CHATROOMMANAGER_H
+
+#include <qobject.h>
+
+#include "gwchatrooms.h"
+
+class Client;
+
+/**
+ * Keeps a record of the server side chatrooms
+ * @author SUSE Linux Products GmbH
+ */
+class ChatroomManager : public QObject
+{
+ Q_OBJECT
+ public:
+ ChatroomManager( Client * client, const char *name = 0);
+ ~ChatroomManager();
+ GroupWise::ChatroomMap rooms();
+ void requestProperties( const QString & displayName );
+ void updateRooms();
+ void updateCounts();
+ signals:
+ void gotProperties( const GroupWise::Chatroom & );
+ void updated();
+ protected:
+ void getChatrooms( bool refresh );
+ protected slots:
+ /**
+ * Used to initialise the list of chatrooms in response to a SearchChatTask.
+ */
+ void slotGotChatroomList();
+ /**
+ * Used to update the user counts of chatrooms.
+ */
+ void slotGotChatCounts();
+ /**
+ * Get the properties of a specific room.
+ */
+ void slotGotChatProperties();
+ private:
+ Client * m_client;
+ GroupWise::ChatroomMap m_rooms;
+ bool m_replace;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/client.cpp b/kopete/protocols/groupwise/libgroupwise/client.cpp
new file mode 100644
index 00000000..274a9ea8
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/client.cpp
@@ -0,0 +1,541 @@
+/*
+ Kopete Groupwise Protocol
+ client.cpp - The main interface for the Groupwise protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+ (c) 2008 Novell, Inc.
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "chatroommanager.h"
+#include "gwclientstream.h"
+#include "privacymanager.h"
+#include "requestfactory.h"
+#include "task.h"
+#include "tasks/conferencetask.h"
+#include "tasks/connectiontask.h"
+#include "tasks/createconferencetask.h"
+#include "tasks/getdetailstask.h"
+#include "tasks/getstatustask.h"
+#include "tasks/joinconferencetask.h"
+#include "tasks/keepalivetask.h"
+#include "tasks/leaveconferencetask.h"
+#include "tasks/logintask.h"
+#include "tasks/rejectinvitetask.h"
+#include "tasks/sendinvitetask.h"
+#include "tasks/sendmessagetask.h"
+#include "tasks/setstatustask.h"
+#include "tasks/statustask.h"
+#include "tasks/typingtask.h"
+#include "userdetailsmanager.h"
+#include "client.h"
+
+class Client::ClientPrivate
+{
+public:
+ ClientPrivate() {}
+
+ ClientStream *stream;
+ int id_seed;
+ Task *root;
+ QString host, user, userDN, pass;
+ QString osname, tzname, clientName, clientVersion;
+ uint port;
+/* int tzoffset;*/
+ bool active;
+ RequestFactory * requestFactory;
+ ChatroomManager * chatroomMgr;
+ UserDetailsManager * userDetailsMgr;
+ PrivacyManager * privacyMgr;
+ uint protocolVersion;
+ QValueList<GroupWise::CustomStatus> customStatuses;
+ QTimer * keepAliveTimer;
+};
+
+Client::Client(QObject *par, uint protocolVersion )
+:QObject(par, "groupwiseclient")
+{
+ d = new ClientPrivate;
+/* d->tzoffset = 0;*/
+ d->active = false;
+ d->osname = "N/A";
+ d->clientName = "N/A";
+ d->clientVersion = "0.0";
+ d->id_seed = 0xaaaa;
+ d->root = new Task(this, true);
+ d->chatroomMgr = 0;
+ d->requestFactory = new RequestFactory;
+ d->userDetailsMgr = new UserDetailsManager( this, "userdetailsmgr" );
+ d->privacyMgr = new PrivacyManager( this, "privacymgr" );
+ d->stream = 0;
+ d->protocolVersion = protocolVersion;
+ // Sends regular keepalives so the server knows we are still running
+ d->keepAliveTimer = new QTimer( this );
+ connect( d->keepAliveTimer, SIGNAL( timeout() ), SLOT( sendKeepAlive() ) );
+}
+
+Client::~Client()
+{
+ delete d->root;
+ delete d->requestFactory;
+ delete d->userDetailsMgr;
+ delete d;
+}
+
+void Client::connectToServer( ClientStream *s, const NovellDN &server, bool auth )
+{
+ d->stream = s;
+ //connect(d->stream, SIGNAL(connected()), SLOT(streamConnected()));
+ //connect(d->stream, SIGNAL(handshaken()), SLOT(streamHandshaken()));
+ connect(d->stream, SIGNAL(error(int)), SLOT(streamError(int)));
+ //connect(d->stream, SIGNAL(sslCertificateReady(const QSSLCert &)), SLOT(streamSSLCertificateReady(const QSSLCert &)));
+ connect(d->stream, SIGNAL(readyRead()), SLOT(streamReadyRead()));
+ //connect(d->stream, SIGNAL(closeFinished()), SLOT(streamCloseFinished()));
+
+ d->stream->connectToServer(server, auth);
+}
+
+void Client::setOSName(const QString &name)
+{
+ d->osname = name;
+}
+
+void Client::setClientName(const QString &s)
+{
+ d->clientName = s;
+}
+
+void Client::setClientVersion(const QString &s)
+{
+ d->clientVersion = s;
+}
+
+void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass )
+{
+ d->host = host;
+ d->port = port;
+ d->user = userId;
+ d->pass = pass;
+
+ initialiseEventTasks();
+
+ LoginTask * login = new LoginTask( d->root );
+
+ connect( login, SIGNAL( gotMyself( const GroupWise::ContactDetails & ) ),
+ this, SIGNAL( accountDetailsReceived( const GroupWise::ContactDetails & ) ) );
+
+ connect( login, SIGNAL( gotFolder( const FolderItem & ) ),
+ this, SIGNAL( folderReceived( const FolderItem & ) ) );
+
+ connect( login, SIGNAL( gotContact( const ContactItem & ) ),
+ this, SIGNAL( contactReceived( const ContactItem & ) ) );
+
+ connect( login, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ),
+ this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) ) ;
+
+ connect( login, SIGNAL( gotPrivacySettings( bool, bool, const QStringList &, const QStringList & ) ),
+ privacyManager(), SLOT( slotGotPrivacySettings( bool, bool, const QStringList &, const QStringList & ) ) );
+
+ connect( login, SIGNAL( gotCustomStatus( const GroupWise::CustomStatus & ) ),
+ SLOT( lt_gotCustomStatus( const GroupWise::CustomStatus & ) ) );
+
+ connect( login, SIGNAL( gotKeepalivePeriod( int ) ), SLOT( lt_gotKeepalivePeriod( int ) ) );
+
+ connect( login, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
+
+ login->initialise();
+ login->go( true );
+
+ d->active = true;
+}
+
+void Client::close()
+{
+ debug( "Client::close()" );
+ d->keepAliveTimer->stop();
+ if(d->stream) {
+ d->stream->disconnect(this);
+ d->stream->close();
+ d->stream = 0;
+ }
+}
+
+QString Client::host()
+{
+ return d->host;
+}
+
+int Client::port()
+{
+ return d->port;
+}
+
+QValueList<GroupWise::CustomStatus> Client::customStatuses()
+{
+ return d->customStatuses;
+}
+
+void Client::initialiseEventTasks()
+{
+ // The StatusTask handles incoming status changes
+ StatusTask * st = new StatusTask( d->root ); // FIXME - add an additional EventRoot?
+ connect( st, SIGNAL( gotStatus( const QString &, Q_UINT16, const QString & ) ), SIGNAL( statusReceived( const QString &, Q_UINT16, const QString & ) ) );
+ // The ConferenceTask handles incoming conference events, messages, joins, leaves, etc
+ ConferenceTask * ct = new ConferenceTask( d->root );
+ connect( ct, SIGNAL( message( const ConferenceEvent & ) ), SLOT( ct_messageReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( typing( const ConferenceEvent & ) ), SIGNAL( contactTyping( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( notTyping( const ConferenceEvent & ) ), SIGNAL( contactNotTyping( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( joined( const ConferenceEvent & ) ), SIGNAL( conferenceJoinNotifyReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( left( const ConferenceEvent & ) ), SIGNAL( conferenceLeft( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( invited( const ConferenceEvent & ) ), SIGNAL( invitationReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( otherInvited( const ConferenceEvent & ) ), SIGNAL( inviteNotifyReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( invitationDeclined( const ConferenceEvent & ) ), SIGNAL( invitationDeclined( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( closed( const ConferenceEvent & ) ), SIGNAL( conferenceClosed( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( autoReply( const ConferenceEvent & ) ), SIGNAL( autoReplyReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( broadcast( const ConferenceEvent & ) ), SIGNAL( broadcastReceived( const ConferenceEvent & ) ) );
+ connect( ct, SIGNAL( systemBroadcast( const ConferenceEvent & ) ), SIGNAL( systemBroadcastReceived( const ConferenceEvent & ) ) );
+
+
+ // The ConnectionTask handles incoming connection events
+ ConnectionTask* cont = new ConnectionTask( d->root );
+ connect( cont, SIGNAL( connectedElsewhere() ), SIGNAL( connectedElsewhere() ) );
+}
+
+void Client::setStatus( GroupWise::Status status, const QString & reason, const QString & autoReply )
+{
+ debug( QString("Setting status to %1").arg( status ) );;
+ SetStatusTask * sst = new SetStatusTask( d->root );
+ sst->status( status, reason, autoReply );
+ connect( sst, SIGNAL( finished() ), this, SLOT( sst_statusChanged() ) );
+ sst->go( true );
+ // TODO: set status change in progress flag
+}
+
+void Client::requestStatus( const QString & userDN )
+{
+ GetStatusTask * gst = new GetStatusTask( d->root );
+ gst->userDN( userDN );
+ connect( gst, SIGNAL( gotStatus( const QString &, Q_UINT16, const QString & ) ), SIGNAL( statusReceived( const QString &, Q_UINT16, const QString & ) ) );
+ gst->go( true );
+}
+
+void Client::sendMessage( const QStringList & addresseeDNs, const OutgoingMessage & message )
+{
+ SendMessageTask * smt = new SendMessageTask( d->root );
+ smt->message( addresseeDNs, message );
+ connect( smt, SIGNAL( finished() ), SLOT( smt_messageSent() ) );
+ smt->go( true );
+}
+
+void Client::sendTyping( const GroupWise::ConferenceGuid & conferenceGuid, bool typing )
+{
+ TypingTask * tt = new TypingTask( d->root );
+ tt->typing( conferenceGuid, typing );
+ tt->go( true );
+}
+
+void Client::createConference( const int clientId )
+{
+ QStringList dummy;
+ createConference( clientId, dummy );
+}
+
+void Client::createConference( const int clientId, const QStringList & participants )
+{
+ CreateConferenceTask * cct = new CreateConferenceTask( d->root );
+ cct->conference( clientId, participants );
+ connect( cct, SIGNAL( finished() ), SLOT( cct_conferenceCreated() ) );
+ cct->go( true );
+}
+void Client::requestDetails( const QStringList & userDNs )
+{
+ GetDetailsTask * gdt = new GetDetailsTask( d->root );
+ gdt->userDNs( userDNs );
+ connect( gdt, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ),
+ this, SIGNAL( contactUserDetailsReceived( const GroupWise::ContactDetails & ) ) );
+ gdt->go( true );
+}
+
+void Client::joinConference( const GroupWise::ConferenceGuid & guid )
+{
+ JoinConferenceTask * jct = new JoinConferenceTask( d->root );
+ jct->join( guid );
+ connect( jct, SIGNAL( finished() ), SLOT( jct_joinConfCompleted() ) );
+ jct->go( true );
+}
+
+void Client::rejectInvitation( const GroupWise::ConferenceGuid & guid )
+{
+ RejectInviteTask * rit = new RejectInviteTask ( d->root );
+ rit->reject( guid );
+ // we don't do anything with the results of this task
+ rit->go( true );
+}
+
+void Client::leaveConference( const GroupWise::ConferenceGuid & guid )
+{
+ LeaveConferenceTask * lct = new LeaveConferenceTask( d->root );
+ lct->leave( guid );
+ //connect( lct, SIGNAL( finished() ), SLOT( lct_leftConference() ) );
+ lct->go( true );
+}
+
+void Client::sendInvitation( const GroupWise::ConferenceGuid & guid, const QString & dn, const GroupWise::OutgoingMessage & message )
+{
+ SendInviteTask * sit = new SendInviteTask( d->root );
+ QStringList invitees( dn );
+ sit->invite( guid, dn, message );
+ sit->go( true );
+}
+
+// SLOTS //
+void Client::streamError( int error )
+{
+ debug( QString( "CLIENT ERROR (Error %1)" ).arg( error ) );
+}
+
+void Client::streamReadyRead()
+{
+ debug( "CLIENT STREAM READY READ" );
+ // take the incoming transfer and distribute it to the task tree
+ Transfer * transfer = d->stream->read();
+ distribute( transfer );
+}
+
+void Client::lt_loginFinished()
+{
+ debug( "Client::lt_loginFinished()" );
+ const LoginTask * lt = (LoginTask *)sender();
+ if ( lt->success() )
+ {
+ debug( "Client::lt_loginFinished() LOGIN SUCCEEDED" );
+ // set our initial status
+ SetStatusTask * sst = new SetStatusTask( d->root );
+ sst->status( GroupWise::Available, QString::null, QString::null );
+ sst->go( true );
+ emit loggedIn();
+ // fetch details for any privacy list items that aren't in our contact list.
+ // There is a chicken-and-egg case regarding this: We need the privacy before reading the contact list so
+ // blocked contacts are shown as blocked. But we need not fetch user details for the privacy lists
+ // before reading the contact list, as many privacy items' details are already in the contact list
+ privacyManager()->getDetailsForPrivacyLists();
+ }
+ else
+ {
+ debug( "Client::lt_loginFinished() LOGIN FAILED" );
+ emit loginFailed();
+ }
+ // otherwise client should disconnect and signal failure that way??
+}
+
+void Client::sst_statusChanged()
+{
+ const SetStatusTask * sst = (SetStatusTask *)sender();
+ if ( sst->success() )
+ {
+ emit ourStatusChanged( sst->requestedStatus(), sst->awayMessage(), sst->autoReply() );
+ }
+}
+
+void Client::ct_messageReceived( const ConferenceEvent & messageEvent )
+{
+ debug( "parsing received message's RTF" );
+ ConferenceEvent transformedEvent = messageEvent;
+ RTF2HTML parser;
+ QString rtf = messageEvent.message;
+ if ( !rtf.isEmpty() )
+ transformedEvent.message = parser.Parse( rtf.latin1(), "" );
+
+ // fixes for RTF to HTML conversion problems
+ // we can drop these once the server reenables the sending of unformatted text
+ // redundant linebreak at the end of the message
+ QRegExp rx(" </span> </span> </span><br>$");
+ transformedEvent.message.replace( rx, "</span></span></span>" );
+ // missing linebreak after first line of an encrypted message
+ QRegExp ry("-----BEGIN PGP MESSAGE----- </span> </span> </span>");
+ transformedEvent.message.replace( ry, "-----BEGIN PGP MESSAGE-----</span></span></span><br/>" );
+
+ emit messageReceived( transformedEvent );
+}
+
+void Client::cct_conferenceCreated()
+{
+ const CreateConferenceTask * cct = ( CreateConferenceTask * )sender();
+ if ( cct->success() )
+ {
+ emit conferenceCreated( cct->clientConfId(), cct->conferenceGUID() );
+ }
+ else
+ {
+ emit conferenceCreationFailed( cct->clientConfId(), cct->statusCode() );
+ }
+}
+
+void Client::jct_joinConfCompleted()
+{
+ const JoinConferenceTask * jct = ( JoinConferenceTask * )sender();
+#ifdef LIBGW_DEBUG
+ debug( QString( "Joined conference %1, participants are: " ).arg( jct->guid() ) );
+ QStringList parts = jct->participants();
+ for ( QStringList::Iterator it = parts.begin(); it != parts.end(); ++it )
+ debug( QString( " - %1" ).arg(*it) );
+ debug( "invitees are: " );
+ QStringList invitees = jct->invitees();
+ for ( QStringList::Iterator it = invitees.begin(); it != invitees.end(); ++it )
+ debug( QString( " - %1" ).arg(*it) );
+#endif
+ emit conferenceJoined( jct->guid(), jct->participants(), jct->invitees() );
+}
+
+void Client::lt_gotCustomStatus( const GroupWise::CustomStatus & custom )
+{
+ d->customStatuses.append( custom );
+}
+
+// INTERNALS //
+
+QString Client::userId()
+{
+ return d->user;
+}
+
+void Client::setUserDN( const QString & userDN )
+{
+ d->userDN = userDN;
+}
+
+QString Client::userDN()
+{
+ return d->userDN;
+}
+
+QString Client::password()
+{
+ return d->pass;
+}
+
+QString Client::userAgent()
+{
+ return QString::fromLatin1( "%1/%2 (%3)" ).arg( d->clientName, d->clientVersion, d->osname );
+}
+
+QCString Client::ipAddress()
+{
+ // TODO: remove hardcoding
+ return "10.10.11.103";
+}
+
+void Client::distribute( Transfer * transfer )
+{
+ if( !rootTask()->take( transfer ) )
+ debug( "CLIENT: root task refused transfer" );
+ // at this point the transfer is no longer needed
+ delete transfer;
+}
+
+void Client::send( Request * request )
+{
+ debug( "CLIENT::send()" );
+ if( !d->stream )
+ {
+ debug( "CLIENT - NO STREAM TO SEND ON!");
+ return;
+ }
+// QString out = request.toString();
+// debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+// xmlOutgoing(out);
+
+ d->stream->write( request );
+}
+
+void Client::debug( const QString &str )
+{
+#ifdef LIBGW_USE_KDEBUG
+ kdDebug( GROUPWISE_DEBUG_LIBGW ) << "debug: " << str << endl;
+#else
+ qDebug( "CLIENT: %s\n", str.ascii() );
+#endif
+}
+
+QString Client::genUniqueId()
+{
+ QString s;
+ s.sprintf("a%x", d->id_seed);
+ d->id_seed += 0x10;
+ return s;
+}
+
+PrivacyManager * Client::privacyManager()
+{
+ return d->privacyMgr;
+}
+
+RequestFactory * Client::requestFactory()
+{
+ return d->requestFactory;
+}
+
+UserDetailsManager * Client::userDetailsManager()
+{
+ return d->userDetailsMgr;
+}
+
+Task * Client::rootTask()
+{
+ return d->root;
+}
+
+uint Client::protocolVersion() const
+{
+ return d->protocolVersion;
+}
+
+ChatroomManager * Client::chatroomManager()
+{
+ if ( !d->chatroomMgr )
+ d->chatroomMgr = new ChatroomManager( this, "chatroommgr" );
+ return d->chatroomMgr;
+}
+
+void Client::lt_gotKeepalivePeriod( int period )
+{
+ d->keepAliveTimer->start( period * 60 * 1000 );
+}
+
+void Client::sendKeepAlive()
+{
+ KeepAliveTask * kat = new KeepAliveTask( d->root );
+ kat->setup();
+ kat->go( true );
+}
+
+void Client::smt_messageSent()
+{
+ const SendMessageTask * smt = ( SendMessageTask * )sender();
+ if ( smt->success() )
+ {
+ debug( "message sent OK" );
+ }
+ else
+ {
+ debug( "message sending failed!" );
+ emit messageSendingFailed();
+ }
+}
+
+#include "client.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/client.h b/kopete/protocols/groupwise/libgroupwise/client.h
new file mode 100644
index 00000000..102b0c27
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/client.h
@@ -0,0 +1,401 @@
+/*
+ Kopete Groupwise Protocol
+ client.h - The main interface for the Groupwise protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LIBGW_CLIENT_H
+#define LIBGW_CLIENT_H
+
+#include <qstring.h>
+
+#include "gwclientstream.h"
+#include "gwerror.h"
+#include "rtf2html.h"
+#include "transfer.h"
+
+class ChatroomManager;
+class PrivacyManager;
+class RequestFactory;
+class UserDetailsManager;
+class Task;
+
+using namespace GroupWise;
+
+class Client : public QObject
+{
+Q_OBJECT
+
+ public:
+
+ /*************
+ EXTERNAL API
+ *************/
+
+ Client( QObject *parent = 0, uint protocolVersion = 2 );
+ ~Client();
+ void setOSName( const QString &name );
+ void setClientName( const QString &s );
+ void setClientVersion( const QString &s );
+ void setUserDN( const QString & userDN );
+ /**
+ * Start a connection to the server using the supplied @ref ClientStream.
+ * This is only a transport layer connection.
+ * Needed for protocol action P1.
+ * @param s initialised client stream to use for the connection.
+ * @param server the server to connect to - but this is also set on the connector used to construct the clientstream??
+ * @param auth needed for jabber protocol layer only?
+ */
+ void connectToServer( ClientStream *s, const NovellDN &server, bool auth=true );
+
+ /**
+ * Login to the GroupWise server using the supplied credentials
+ * Protocol action P1, needed for all
+ * @param host - probably could obtain this back from the connector - used for outgoing tasks to determine destination
+ * @param user The user name to log in as.
+fd * @param password
+ */
+ void start( const QString &host, const uint port, const QString &userId, const QString &pass );
+
+ /**
+ * Logout and disconnect
+ * Protocol action P4 void distribute(const QDomElement &);
+
+ */
+ void close();
+
+ /**
+ * Accessors needed for login
+ */
+ QString host();
+ int port();
+
+ /**
+ * Set the user's presence on the server
+ * Protocol action P2
+ * @param status status enum
+ * @param reason custom status name for away statuses
+ * @param autoReply auto reply message for use in this status
+ */
+ void setStatus( GroupWise::Status status, const QString & reason, const QString & autoReply );
+
+ /**
+ * Send a message
+ * Protocol action P10
+ * @param message contains the text and the recipient.
+ */
+ void sendMessage( const QStringList & addresseeDNs, const OutgoingMessage & message );
+
+ /**
+ * Send a typing notification
+ * Protocol action P11
+ * @param conference The conference where the typing took place.
+ * @param typing True if the user is now typing, false otherwise.
+ */
+ void sendTyping( const ConferenceGuid & conferenceGuid, bool typing );
+
+ /**
+ * Request details for one or more users, for example, if we receive a message from someone who isn't on our contact list
+ * @param userDNs A list of one or more user's DNs to fetch details for
+ */
+ void requestDetails( const QStringList & userDNs );
+
+ /**
+ * Request the status of a single user, for example, if they have messaged us and are not on our contact list
+ */
+ void requestStatus( const QString & userDN );
+
+ /**
+ * Add a contact to the contact list
+ * Protocol action P12
+ */
+
+ /**
+ * Remove a contact from the contact list
+ * Protocol action P13
+ */
+
+ /**
+ * Instantiate a conference on the server
+ * Protocol action P5
+ */
+ void createConference( const int clientId );
+ /**
+ * Overloaded version of the above to create a conference with a supplied list of invitees
+ */
+ void createConference( const int clientId, const QStringList & participants );
+
+ /**
+ * Join a conference, accepting an invitation
+ * Protocol action P7
+ */
+ void joinConference( const ConferenceGuid & guid );
+
+ /**
+ * Reject a conference invitation
+ * Protocol action P8
+ */
+ void rejectInvitation( const ConferenceGuid & guid );
+
+ /**
+ * Leave a conference, notifying
+ * Protocol action P6
+ */
+ void leaveConference( const ConferenceGuid & guid );
+
+ /**
+ * Send an invitation to join a conference
+ * Protocol action P9
+ */
+ void sendInvitation( const ConferenceGuid & guid, const QString & dn, const GroupWise::OutgoingMessage & message );
+ /*************
+ INTERNAL (FOR USE BY TASKS) METHODS
+ *************/
+ /**
+ * Send an outgoing request to the server
+ */
+ void send( Request *request );
+ /**
+ * Print a debug statement
+ */
+ void debug( const QString &str );
+
+ /**
+ * The protocol version of the Client
+ */
+ uint protocolVersion() const;
+ /**
+ * Generate a unique ID for Tasks.
+ */
+ QString genUniqueId();
+
+ /**
+ * The current user's user ID
+ */
+ QString userId();
+
+ /**
+ * The current user's DN
+ */
+ QString userDN();
+ /**
+ * The current user's password
+ */
+ QString password();
+
+ /**
+ * User agent details for this host
+ */
+ QString userAgent();
+
+ /**
+ * Host's IP address
+ */
+ QCString ipAddress();
+
+ /**
+ * Obtain the list of custom statuses stored on the server
+ */
+ QValueList<GroupWise::CustomStatus> customStatuses();
+
+ /**
+ * Get a reference to the RequestFactory for this Client.
+ * Used by Tasks to generate Requests with an ascending sequence of transaction IDs
+ * for this connection
+ */
+ RequestFactory * requestFactory();
+
+ /**
+ * Get a reference to the ChatroomManager for this Client.
+ * This is constructed the first time this function is called. Used to manipulate chat rooms on the server.
+ */
+ ChatroomManager * chatroomManager();
+
+ /**
+ * Get a reference to the UserDetailsManager for this Client.
+ * Used to track known user details and issue new details requests
+ */
+ UserDetailsManager * userDetailsManager();
+ /**
+ * Get a reference to the PrivacyManager for this Client.
+ * Used to track and manipulate server side privacy settings
+ */
+ PrivacyManager * privacyManager();
+ /**
+ * Access the root Task for this client, so tasks may be added to it.
+ */
+ Task* rootTask();
+
+ signals:
+ /** CONNECTION EVENTS */
+ /**
+ * Notifies that the login process has succeeded.
+ */
+ void loggedIn();
+ void loginFailed();
+ /**
+ * Notifies tasks and account so they can react properly
+ */
+ void disconnected();
+ /**
+ * We were disconnected because we connected elsewhere
+ */
+ void connectedElsewhere();
+
+ /** STATUS AND METADATA EVENTS */
+ /**
+ * We've just got the user's own details from the server.
+ */
+ void accountDetailsReceived( const GroupWise::ContactDetails & );
+ /**
+ * We've just found out about a folder from the server.
+ */
+ void folderReceived( const FolderItem & );
+ /**
+ * We've just found out about a folder from the server.
+ */
+ void contactReceived( const ContactItem & );
+ /**
+ * We've just received a contact's metadata from the server.
+ */
+ void contactUserDetailsReceived( const GroupWise::ContactDetails & );
+ /**
+ * A remote contact changed status
+ */
+ void statusReceived( const QString & contactId, Q_UINT16 status, const QString & statusText );
+ /**
+ * Our status changed on the server
+ */
+ void ourStatusChanged( GroupWise::Status status, const QString & statusText, const QString & autoReply );
+
+ /** CONFERENCE (MANAGEMENT) EVENTS */
+ /**
+ * Notify that we've just received a message. Sender may not be on our contact list
+ */
+ void messageReceived( const ConferenceEvent & );
+ /**
+ * Notify that we've received an auto reply. This Event does not contain any rtf, unlike a normal message.
+ */
+ void autoReplyReceived( const ConferenceEvent & );
+ /**
+ * A conference was successfully created on the server
+ */
+ void conferenceCreated( const int clientId, const GroupWise::ConferenceGuid & guid );
+ /**
+ * A third party was invited to join a chat. They may not be on our contact list.
+ */
+ void inviteNotifyReceived( const ConferenceEvent & );
+ /**
+ * We were invited to join a chat. The inviter may not be on our contact list
+ */
+ void invitationReceived( const ConferenceEvent & );
+ /**
+ * Someone joined a chat. They may not be on our contact list if it is a group chat
+ * and they were invited to join the chat prior to our being invited to join and joining
+ */
+ void conferenceJoinNotifyReceived( const ConferenceEvent & );
+ /**
+ * Someone left a conference. This may close a conference, see @ref conferenceClosed.
+ */
+ void conferenceLeft( const ConferenceEvent & );
+ /**
+ * Someone declined an invitation to join a conference. This may close a conference, see @ref conferenceClosed.
+ */
+ void invitationDeclined( const ConferenceEvent & );
+ /**
+ * A conference was closed by the server. This occurs if we are the only participant and there
+ * are no outstanding invitations.
+ */
+ void conferenceClosed( const ConferenceEvent & );
+ /**
+ * We joined a conference.
+ */
+ void conferenceJoined( const GroupWise::ConferenceGuid &, const QStringList &, const QStringList & );
+ /**
+ * We received an "is typing" event in a conference
+ */
+ void contactTyping( const ConferenceEvent & );
+ /**
+ * We received an "is not typing event" in a conference
+ */
+ void contactNotTyping( const ConferenceEvent & );
+ /**
+ * An attempt to create a conference failed.
+ */
+ void conferenceCreationFailed( const int clientId, const int error );
+ /**
+ * We received a temporary contact related to a conference
+ */
+ void tempContactReceived( const GroupWise::ContactDetails & );
+ /**
+ * We received a broadcast message
+ */
+ void broadcastReceived( const ConferenceEvent & );
+ /**
+ * We received a system broadcast
+ */
+ void systemBroadcastReceived ( const ConferenceEvent & );
+ /** CONTACT LIST MANAGEMENT EVENTS */
+ /** TBD! */
+ void messageSendingFailed();
+ protected:
+ /**
+ * Instantiate all the event handling tasks
+ */
+ void initialiseEventTasks();
+ protected slots:
+ // INTERNAL, FOR USE BY TASKS' finished() SIGNALS //
+ void lt_loginFinished();
+ void sst_statusChanged();
+ void cct_conferenceCreated();
+ /**
+ * Transforms an RTF message into an HTML message and emits messageReceived()
+ */
+ void ct_messageReceived( const ConferenceEvent & );
+ void jct_joinConfCompleted();
+ /**
+ * Receive a custom status during login and record it
+ */
+ void lt_gotCustomStatus( const GroupWise::CustomStatus & );
+ /**
+ * Notify us of the keepalive period contained in the login response
+ */
+ void lt_gotKeepalivePeriod( int );
+
+ /**
+ * Used by the client stream to notify errors to upper layers.
+ */
+ void streamError( int error );
+
+ /**
+ * The client stream has data ready to read.
+ */
+ void streamReadyRead();
+
+ /**
+ * sendout a 'ping' keepalive message so that the server does not disconnect us
+ */
+ void sendKeepAlive();
+ void smt_messageSent();
+
+ private:
+ void distribute( Transfer *transfer );
+ class ClientPrivate;
+ ClientPrivate* d;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/connector.cpp b/kopete/protocols/groupwise/libgroupwise/connector.cpp
new file mode 100644
index 00000000..55e866ee
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/connector.cpp
@@ -0,0 +1,73 @@
+/*
+ Kopete Groupwise Protocol
+ connector.cpp - the Groupwise socket connector
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connector.h"
+
+Connector::Connector(QObject *parent)
+:QObject(parent)
+{
+ setUseSSL(false);
+ setPeerAddressNone();
+}
+
+Connector::~Connector()
+{
+}
+
+bool Connector::useSSL() const
+{
+ return ssl;
+}
+
+bool Connector::havePeerAddress() const
+{
+ return haveaddr;
+}
+
+QHostAddress Connector::peerAddress() const
+{
+ return addr;
+}
+
+Q_UINT16 Connector::peerPort() const
+{
+ return port;
+}
+
+void Connector::setUseSSL(bool b)
+{
+ ssl = b;
+}
+
+void Connector::setPeerAddressNone()
+{
+ haveaddr = false;
+ addr = QHostAddress();
+ port = 0;
+}
+
+void Connector::setPeerAddress(const QHostAddress &_addr, Q_UINT16 _port)
+{
+ haveaddr = true;
+ addr = _addr;
+ port = _port;
+}
+
+#include "connector.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/connector.h b/kopete/protocols/groupwise/libgroupwise/connector.h
new file mode 100644
index 00000000..1cae3426
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/connector.h
@@ -0,0 +1,61 @@
+/*
+ Kopete Groupwise Protocol
+ connector.h - the Groupwise socket connector
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LIBGW_CONNECTOR_H
+#define LIBGW_CONNECTOR_H
+
+#include <qhostaddress.h>
+#include <qobject.h>
+
+class ByteStream;
+
+class Connector : public QObject
+{
+ Q_OBJECT
+public:
+ Connector(QObject *parent=0);
+ virtual ~Connector();
+
+ virtual void connectToServer(const QString &server)=0;
+ virtual ByteStream *stream() const=0;
+ virtual void done()=0;
+
+ bool useSSL() const;
+ bool havePeerAddress() const;
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void connected();
+ void error();
+
+protected:
+ void setUseSSL(bool b);
+ void setPeerAddressNone();
+ void setPeerAddress(const QHostAddress &addr, Q_UINT16 port);
+
+private:
+ bool ssl;
+ bool haveaddr;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp
new file mode 100644
index 00000000..1e15287e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/coreprotocol.cpp
@@ -0,0 +1,507 @@
+/*
+ Kopete Groupwise Protocol
+ coreprotocol.h- the core GroupWise protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+ url_escape_string from Gaim src/protocols/novell/nmconn.c
+ Copyright (c) 2004 Novell, Inc. All Rights Reserved
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <string.h>
+#include <iostream>
+#include <stdlib.h>
+
+#include <qdatastream.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+
+
+#include <kdebug.h>
+#include <kurl.h>
+
+#include "eventprotocol.h"
+#include "eventtransfer.h"
+#include "gwerror.h"
+#include "gwfield.h"
+#include "request.h"
+#include "response.h"
+#include "responseprotocol.h"
+
+#include "coreprotocol.h"
+
+#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
+#define GW_URLVAR_TAG "&tag="
+#define GW_URLVAR_METHOD "&cmd="
+#define GW_URLVAR_VAL "&val="
+#define GW_URLVAR_TYPE "&type="
+
+//#define GW_COREPROTOCOL_DEBUG
+
+QCString
+url_escape_string( const char *src)
+{
+ uint escape = 0;
+ const char *p;
+ uint q;
+ //char *encoded = NULL;
+ int ch;
+
+ static const char hex_table[17] = "0123456789abcdef";
+
+ if (src == NULL) {
+ return QCString();
+ }
+
+ /* Find number of chars to escape */
+ for (p = src; *p != '\0'; p++) {
+ ch = (uchar) *p;
+ if (!NO_ESCAPE(ch)) {
+ escape++;
+ }
+ }
+
+ QCString encoded((p - src) + (escape * 2) + 1);
+
+ /* Escape the string */
+ for (p = src, q = 0; *p != '\0'; p++) {
+ ch = (uchar) * p;
+ if (NO_ESCAPE(ch)) {
+ if (ch != 0x20) {
+ encoded.insert( q, (char)ch );
+ q++;
+ } else {
+ encoded.insert( q, '+' );
+ q++;
+ }
+ } else {
+ encoded.insert( q, '%' );
+ q++;
+
+ encoded.insert( q, hex_table[ch >> 4] );
+ q++;
+
+ encoded.insert( q, hex_table[ch & 15] );
+ q++;
+ }
+ }
+ encoded.insert( q, '\0' );
+
+ return encoded;
+}
+
+CoreProtocol::CoreProtocol() : QObject()
+{
+ m_eventProtocol = new EventProtocol( this, "eventprotocol" );
+ m_responseProtocol = new ResponseProtocol( this, "responseprotocol" );
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+int CoreProtocol::state()
+{
+ return m_state;
+}
+
+void CoreProtocol::debug( const QString &str )
+{
+#ifdef LIBGW_USE_KDEBUG
+ kdDebug( 14191 ) << "debug: " << str << endl;
+#else
+ qDebug( "GW RAW PROTO: %s\n", str.ascii() );
+#endif
+}
+
+void CoreProtocol::addIncomingData( const QByteArray & incomingBytes )
+{
+// store locally
+ debug( "CoreProtocol::addIncomingData()");
+ int oldsize = m_in.size();
+ m_in.resize( oldsize + incomingBytes.size() );
+ memcpy( m_in.data() + oldsize, incomingBytes.data(), incomingBytes.size() );
+ m_state = Available;
+ // convert every event in the chunk to a Transfer, signalling it back to the clientstream
+
+ int parsedBytes = 0;
+ int transferCount = 0;
+ // while there is data left in the input buffer, and we are able to parse something out of it
+ while ( m_in.size() && ( parsedBytes = wireToTransfer( m_in ) ) )
+ {
+ transferCount++;
+ debug( QString( "CoreProtocol::addIncomingData() - parsed transfer #%1 in chunk" ).arg( transferCount ) );
+ int size = m_in.size();
+ if ( parsedBytes < size )
+ {
+ debug( " - more data in chunk!" );
+ // copy the unparsed bytes into a new qbytearray and replace m_in with that
+ QByteArray remainder( size - parsedBytes );
+ memcpy( remainder.data(), m_in.data() + parsedBytes, remainder.size() );
+ m_in = remainder;
+ }
+ else
+ m_in.truncate( 0 );
+ }
+ if ( m_state == NeedMore )
+ debug( " - message was incomplete, waiting for more..." );
+ if ( m_eventProtocol->state() == EventProtocol::OutOfSync )
+ {
+ debug( " - protocol thinks it's out of sync, discarding the rest of the buffer and hoping the server regains sync soon..." );
+ m_in.truncate( 0 );
+ }
+ debug( " - done processing chunk" );
+}
+
+Transfer* CoreProtocol::incomingTransfer()
+{
+ debug( "CoreProtocol::incomingTransfer()" );
+ if ( m_state == Available )
+ {
+ debug( " - got a transfer" );
+ m_state = NoData;
+ return m_inTransfer;
+ m_inTransfer = 0;
+ }
+ else
+ {
+ debug( " - no milk today." );
+ return 0;
+ }
+}
+
+void cp_dump( const QByteArray &bytes )
+{
+#ifdef LIBGW_DEBUG
+ CoreProtocol::debug( QString( "contains: %1 bytes" ).arg( bytes.count() ) );
+ for ( uint i = 0; i < bytes.count(); ++i )
+ {
+ printf( "%02x ", bytes[ i ] );
+ }
+ printf( "\n" );
+#else
+ Q_UNUSED(bytes);
+#endif
+}
+
+void CoreProtocol::outgoingTransfer( Request* outgoing )
+{
+ debug( "CoreProtocol::outgoingTransfer()" );
+ // Convert the outgoing data into wire format
+ Request * request = dynamic_cast<Request *>( outgoing );
+ Field::FieldList fields = request->fields();
+ if ( fields.isEmpty() )
+ {
+ debug( " CoreProtocol::outgoingTransfer: Transfer contained no fields, it must be a ping.");
+/* m_error = NMERR_BAD_PARM;
+ return;*/
+ }
+ // Append field containing transaction id
+ Field::SingleField * fld = new Field::SingleField( NM_A_SZ_TRANSACTION_ID, NMFIELD_METHOD_VALID,
+ 0, NMFIELD_TYPE_UTF8, request->transactionId() );
+ fields.append( fld );
+
+ // convert to QByteArray
+ QByteArray bytesOut;
+ QTextStream dout( bytesOut, IO_WriteOnly );
+ dout.setEncoding( QTextStream::Latin1 );
+ //dout.setByteOrder( QDataStream::LittleEndian );
+
+ // strip out any embedded host and port in the command string
+ QCString command, host, port;
+ if ( request->command().section( ':', 0, 0 ) == "login" )
+ {
+ command = "login";
+ host = request->command().section( ':', 1, 1 ).ascii();
+ port = request->command().section( ':', 2, 2 ).ascii();
+ debug( QString( "Host: %1 Port: %2" ).arg( host.data() ).arg( port.data() ) );
+ }
+ else
+ command = request->command().ascii();
+
+ // add the POST
+ dout << "POST /";
+ dout << command;
+ dout << " HTTP/1.0\r\n";
+
+ // if a login, add Host arg
+ if ( command == "login" )
+ {
+ dout << "Host: ";
+ dout << host; //FIXME: Get this from somewhere else!!
+ dout << ":" << port << "\r\n\r\n";
+ }
+ else
+ dout << "\r\n";
+
+ debug( QString( "data out: %1" ).arg( bytesOut.data() ) );
+
+ emit outgoingData( bytesOut );
+ // now convert
+ fieldsToWire( fields );
+ delete request;
+ delete fld;
+ return;
+}
+
+void CoreProtocol::fieldsToWire( Field::FieldList fields, int depth )
+{
+ debug( "CoreProtocol::fieldsToWire()");
+ int subFieldCount = 0;
+
+ // TODO: consider constructing this as a QStringList and then join()ing it.
+ Field::FieldListIterator it;
+ Field::FieldListIterator end = fields.end();
+ Field::FieldBase* field;
+ for ( it = fields.begin(); it != end ; ++it )
+ {
+ field = *it;
+ //debug( " - writing a field" );
+ QByteArray bytesOut;
+ QDataStream dout( bytesOut, IO_WriteOnly );
+ dout.setByteOrder( QDataStream::LittleEndian );
+
+ // these fields are ignored by Gaim's novell
+ if ( field->type() == NMFIELD_TYPE_BINARY || field->method() == NMFIELD_METHOD_IGNORE )
+ continue;
+
+ // GAIM writes these tags to the secure socket separately - if we can't connect, check here
+ // NM Protocol 1 writes them in an apparently arbitrary order
+ // tag
+ //dout.writeRawBytes( GW_URLVAR_TAG, sizeof( GW_URLVAR_TAG ) );
+ //dout << field->tag();
+
+ // method
+ //dout.writeRawBytes( GW_URLVAR_METHOD, sizeof( GW_URLVAR_METHOD ) );
+ // char methodChar = encode_method( field->method() );
+ //dout << (Q_UINT8)methodChar;
+
+ // value
+ //dout.writeRawBytes( GW_URLVAR_VAL, sizeof( GW_URLVAR_VAL ) );
+
+ char valString[ NMFIELD_MAX_STR_LENGTH ];
+ switch ( field->type() )
+ {
+ case NMFIELD_TYPE_UTF8: // Field contains UTF-8
+ case NMFIELD_TYPE_DN: // Field contains a Distinguished Name
+ {
+ //debug( " - it's a single string" );
+ const Field::SingleField *sField = static_cast<const Field::SingleField*>( field );
+// QString encoded = KURL::encode_string( sField->value().toString(), 106 /* UTF-8 */);
+// encoded.replace( "%20", "+" );
+// dout << encoded.ascii();
+
+ snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%s", url_escape_string( sField->value().toString().utf8() ).data() );
+ //dout << sField->value().toString().ascii();
+ break;
+ }
+ case NMFIELD_TYPE_ARRAY: // Field contains a field array
+ case NMFIELD_TYPE_MV: // Field contains a multivalue
+ {
+ //debug( " - it's a multi" );
+ const Field::MultiField *mField = static_cast<const Field::MultiField*>( field );
+ subFieldCount = mField->fields().count(); // determines if we have a subarray to send after this field
+ //dout << QString::number( subFieldCount ).ascii();
+ snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%u", subFieldCount );
+ break;
+ }
+ default: // Field contains a numeric value
+ {
+ //debug( " - it's a number" );
+ const Field::SingleField *sField = static_cast<const Field::SingleField*>( field );
+ //dout << QString::number( sField->value().toInt() ).ascii();
+ snprintf( valString, NMFIELD_MAX_STR_LENGTH, "%u", sField->value().toInt() );
+ }
+ }
+
+ // type
+ //dout.writeRawBytes( GW_URLVAR_TYPE, sizeof( GW_URLVAR_TYPE ) );
+
+ //dout << QString::number( field->type() ).ascii();
+ QCString typeString;
+ typeString.setNum( field->type() );
+ QCString outgoing = GW_URLVAR_TAG + field->tag()
+ + GW_URLVAR_METHOD + (char)encode_method( field->method() )
+ + GW_URLVAR_VAL + (const char *)valString
+ + GW_URLVAR_TYPE + typeString;
+
+ debug( QString( "CoreProtocol::fieldsToWire - outgoing data: %1" ).arg( outgoing.data() ) );
+ dout.writeRawBytes( outgoing.data(), outgoing.length() );
+ // write what we have so far, we may be calling this function recursively
+ //kdDebug( 14999 ) << k_funcinfo << "writing \'" << bout << "\'" << endl;
+ //debug( " - signalling data" );
+ emit outgoingData( bytesOut );
+
+ // write fields of subarray, if that's what the current field is
+ if ( subFieldCount > 0 &&
+ ( field->type() == NMFIELD_TYPE_ARRAY || field->type() == NMFIELD_TYPE_MV ) )
+ {
+ const Field::MultiField *mField = static_cast<const Field::MultiField*>( field );
+ fieldsToWire( mField->fields(), depth + 1 );
+ }
+ //debug( " - field done" );
+ }
+ if ( depth == 0 ) // this call to the function was not recursive, so the entire request has been sent at this point
+ {
+ // very important, don't send put the \r\n on the wire as a string or it will be preceded with the string length and 0 terminated, which the server reads as a request to disconnect.
+ QByteArray bytesOut;
+ QDataStream dout( bytesOut, IO_WriteOnly );
+ dout.setByteOrder( QDataStream::LittleEndian );
+ dout.writeRawBytes( "\r\n", 2 );
+ emit outgoingData( bytesOut );
+ debug( "CoreProtocol::fieldsToWire - request completed" );
+ }
+ //debug( " - method done" );
+}
+
+int CoreProtocol::wireToTransfer( const QByteArray& wire )
+{
+ // processing incoming data and reassembling it into transfers
+ // may be an event or a response
+ uint bytesParsed = 0;
+ m_din = new QDataStream( wire, IO_ReadOnly );
+ m_din->setByteOrder( QDataStream::LittleEndian );
+
+ // look at first four bytes and decide what to do with the chunk
+ Q_UINT32 val;
+ if ( okToProceed() )
+ {
+ *m_din >> val;
+
+ // if is 'HTTP', it's a Response. PTTH it is after endian conversion
+ if ( !qstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) ||
+ !qstrncmp( (const char *)&val, "PTTH", strlen( "PTTH" ) )
+ ) {
+ Transfer * t = m_responseProtocol->parse( wire, bytesParsed );
+ if ( t )
+ {
+ m_inTransfer = t;
+ debug( "CoreProtocol::wireToTransfer() - got a RESPONSE " );
+
+ m_state = Available;
+ emit incomingData();
+ }
+ else
+ bytesParsed = 0;
+ }
+ else // otherwise -> Event code
+ {
+ debug( QString( "CoreProtocol::wireToTransfer() - looks like an EVENT: %1, length %2" ).arg( val ).arg( wire.size() ) );
+ Transfer * t = m_eventProtocol->parse( wire, bytesParsed );
+ if ( t )
+ {
+ m_inTransfer = t;
+ debug( QString( "CoreProtocol::wireToTransfer() - got an EVENT: %1, parsed: %2" ).arg( val ).arg( bytesParsed ) );
+ m_state = Available;
+ emit incomingData();
+ }
+ else
+ {
+ debug( "CoreProtocol::wireToTransfer() - EventProtocol was unable to parse it" );
+ bytesParsed = 0;
+ }
+ }
+ }
+ delete m_din;
+ return bytesParsed;
+}
+
+void CoreProtocol::reset()
+{
+ m_in.resize( 0 );
+}
+
+QChar CoreProtocol::encode_method( Q_UINT8 method )
+{
+ QChar str;
+
+ switch (method) {
+ case NMFIELD_METHOD_EQUAL:
+ str = 'G';
+ break;
+ case NMFIELD_METHOD_UPDATE:
+ str = 'F';
+ break;
+ case NMFIELD_METHOD_GTE:
+ str = 'E';
+ break;
+ case NMFIELD_METHOD_LTE:
+ str = 'D';
+ break;
+ case NMFIELD_METHOD_NE:
+ str = 'C';
+ break;
+ case NMFIELD_METHOD_EXIST:
+ str = 'B';
+ break;
+ case NMFIELD_METHOD_NOTEXIST:
+ str = 'A';
+ break;
+ case NMFIELD_METHOD_SEARCH:
+ str = '9';
+ break;
+ case NMFIELD_METHOD_MATCHBEGIN:
+ str = '8';
+ break;
+ case NMFIELD_METHOD_MATCHEND:
+ str = '7';
+ break;
+ case NMFIELD_METHOD_NOT_ARRAY:
+ str = '6';
+ break;
+ case NMFIELD_METHOD_OR_ARRAY:
+ str = '5';
+ break;
+ case NMFIELD_METHOD_AND_ARRAY:
+ str = '4';
+ break;
+ case NMFIELD_METHOD_DELETE_ALL:
+ str = '3';
+ break;
+ case NMFIELD_METHOD_DELETE:
+ str = '2';
+ break;
+ case NMFIELD_METHOD_ADD:
+ str = '1';
+ break;
+ default: /* NMFIEL D_METHOD_VALID */
+ str = '0';
+ break;
+ }
+
+ return str;
+}
+
+void CoreProtocol::slotOutgoingData( const QCString &out )
+{
+ debug( QString( "CoreProtocol::slotOutgoingData() %1" ).arg( out ) );
+}
+
+bool CoreProtocol::okToProceed()
+{
+ if ( m_din )
+ {
+ if ( m_din->atEnd() )
+ {
+ m_state = NeedMore;
+ debug( "CoreProtocol::okToProceed() - Server message ended prematurely!" );
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+#include "coreprotocol.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/coreprotocol.h b/kopete/protocols/groupwise/libgroupwise/coreprotocol.h
new file mode 100644
index 00000000..4cd30b88
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/coreprotocol.h
@@ -0,0 +1,202 @@
+/*
+ Kopete Groupwise Protocol
+ coreprotocol.h- the core GroupWise protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_CORE_PROTOCOL_H
+#define GW_CORE_PROTOCOL_H
+
+#include <qcstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+#include "gwfield.h"
+
+class EventProtocol;
+class ResponseProtocol;
+class Request;
+class Transfer;
+
+/**
+ * This class handles transforming data between structured high level messages and encoded bytes that are sent
+ * and received over the network.
+ *
+ * 0) FIELD ARRAYS
+ * ---------------
+ * This is relevant to both input and output handling.
+ * Requests (out) and Responses (in) are messages containing, after a HTTP header, a series of 'Fields'.
+ * A message may contain a flat series of Fields, or each Field may mark the start of a nested array of more Fields.
+ * In this case the Field's value is the length of the following nested array.
+ * The length of the top level Field series is not given. The message ends when there are no more Fields expected as part of a nested array,
+ * and is marked by a terminator.
+ * The encoding used for Fields differs for Requests and Responses, and is described below.
+ *
+ * 1) INPUT
+ * --------
+ * The input functionality is a finite state machine that processes the stream of data from the GroupWise server.
+ * Since the server may arbitrarily truncate or run together protocol level messages, we buffer the incoming data stream,
+ * parsing it into individual messages that are removed from the buffer and passed back to the ClientStream, which propagates
+ * them to higher layers.
+ *
+ * Incoming data may be in either of two formats; a Response or an Event.
+ * All binary data is Little Endian on the network.
+ *
+ * 1.1) INPUT MESSAGE 'SPECIES'
+ *
+ * 1.1.1) Events
+ *
+ * Events are independently occuring notifications generated by the server or by the activity of other users.
+ * Events are represented on the wire in binary format:
+ *
+ * BYTE 1
+ * 0 8 6....
+ * AAAABBBBCCCCCCCCC....DDDDDDDD.....
+ * AAAA is a UINT32 giving the type of event
+ * BBBB is a UINT32 giving the length of the event source,
+ * CCCC... is the event source, a UTF8 encoded string, which is observed to be zero terminated
+ * DDDD... is event dependent binary data, which frequently consists of the conference the event relates to,
+ * conference flags describing the logging, chat security and closed status, and message data.
+ *
+ * As the DDDD portion is irregularly structured, it must be processed knowing the semantics of the event type.
+ * See the @ref EventProtocol documentation.
+ *
+ * Event message data is always a UINT32 giving the message length, then a message in RTF format.
+ * The message length may be zero.
+ *
+ * 1.1.2) Responses
+ * Responses are the server's response to client Requests. Each Request generates one Response. Requests and Responses are regularly structured
+ * and can be parsed/generated without any knowledge of their content.
+ * Responses consist of text/line oriented standard HTTP headers, followed by a binary payload. The payload is a series of Fields as described above,
+ * and the terminator following the last field is a null (0x0) byte.
+ *
+ * TODO: Add Field structure format: type, tag, method, flags, and value. see ResponseProtocol::readFields() for reference if this is incomplete.
+ *
+ * 1.3) INPUT PROCESSING IMPLEMENTATION
+ * CoreProtocol input handling operates on an event driven basis. It starts processing when it receives data via @ref addIncomingData(),
+ * and emits @ref incomingData() as each complete message is parsed in off the wire.
+ * Each call to addIncomingData() may result in zero or more incomingData() signals
+ *
+ * 2) REQUESTS
+ * -----------
+ * The output functionality is an encoding function that transforms outgoing Requests into the wire request format
+ * - a HTTP POST made up of the request operation type as the path, followed by a series of (repeated) variables that form the arguments.
+ * Order of the arguments is significant!
+ * Argument values are URL-encoded with spaces encoded as + rather than %20.
+ * The terminator used is a CRLF pair ("\r\n").
+ * HTTP headers are only used in a login operation, where they contain a Host: hostname:port line.
+ * Headers are separated from the arguments by a blank line (only CRLF) as usual.
+ *
+ * 3) USER MESSAGE BODY TEXT REPRESENTATION
+ * -----------------------------------
+ * Message text sent by users (found in both Requests and Events) is generally formatted as Rich Text Format.
+ * Text portions of the RTF may be be encoded in
+ * any of three ways -
+ * ascii text,
+ * latin1 as hexadecimal,
+ * escaped unicode code points (encoded/escaped as \uUNICODEVALUE?, with or without a space between the end of the unicode value and the ? )
+ * Outgoing messages may contain rich text, and additionally the plain text encoded as UTF8, but this plain payload is apparently ignored by the server
+ *
+ */
+class CoreProtocol : public QObject
+{
+Q_OBJECT
+public:
+ enum State { NeedMore, Available, NoData };
+
+ CoreProtocol();
+
+ virtual ~CoreProtocol();
+ /**
+ * Debug output
+ */
+ static void debug(const QString &str);
+
+ /**
+ * Reset the protocol, clear buffers
+ */
+ void reset();
+
+ /**
+ * Accept data from the network, and buffer it into a useful message
+ * @param incomingBytes Raw data in wire format.
+ */
+ void addIncomingData( const QByteArray& incomingBytes );
+
+ /**
+ * @return the incoming transfer or 0 if none is available.
+ */
+ Transfer* incomingTransfer();
+
+ /**
+ * Convert a request into an outgoing transfer
+ * emits @ref outgoingData() with each part of the transfer
+ */
+ void outgoingTransfer( Request* outgoing );
+
+ /**
+ * Get the state of the protocol
+ */
+ int state();
+
+signals:
+ /**
+ * Emitted as the core protocol converts fields to wire ready data
+ */
+ void outgoingData( const QByteArray& );
+ /**
+ * Emitted when there is incoming data, parsed into a Transfer
+ */
+ void incomingData();
+protected slots:
+ /**
+ * Just a debug method to test emitting to the socket, atm - should go to the ClientStream
+ */
+ void slotOutgoingData( const QCString & );
+
+protected:
+ /**
+ * Check that there is data to read, and set the protocol's state if there isn't any.
+ */
+ bool okToProceed();
+ /**
+ * Convert incoming wire data into a Transfer object and queue it
+ * @return number of bytes from the input that were parsed into a Transfer
+ */
+ int wireToTransfer( const QByteArray& wire );
+ /**
+ * Convert fields to a wire representation. Emits outgoingData as each field is written.
+ * Calls itself recursively to process nested fields, hence
+ * @param depth Current depth of recursion. Don't use this parameter yourself!
+ */
+ void fieldsToWire( Field::FieldList fields, int depth = 0 );
+ /**
+ * encodes a method number (usually supplied as a #defined symbol) to a char
+ */
+ QChar encode_method( Q_UINT8 method );
+private:
+ QByteArray m_in; // buffer containing unprocessed bytes we received
+ QDataStream* m_din; // contains the packet currently being parsed
+ int m_error;
+ Transfer* m_inTransfer; // the transfer that is being received
+ int m_state; // represents the protocol's overall state
+ EventProtocol* m_eventProtocol;
+ ResponseProtocol * m_responseProtocol;
+};
+
+#endif
+
diff --git a/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp
new file mode 100644
index 00000000..05320676
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/eventprotocol.cpp
@@ -0,0 +1,216 @@
+/*
+ Kopete Groupwise Protocol
+ eventprotocol.cpp - reads the protocol used by GroupWise for signalling Events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qbuffer.h>
+
+#include "gwerror.h"
+
+#include "eventtransfer.h"
+#include "eventprotocol.h"
+
+using namespace GroupWise;
+
+EventProtocol::EventProtocol(QObject *parent, const char *name)
+ : InputProtocolBase(parent, name)
+{
+}
+
+EventProtocol::~EventProtocol()
+{
+}
+
+Transfer * EventProtocol::parse( const QByteArray & wire, uint& bytes )
+{
+ m_bytes = 0;
+ //m_din = new QDataStream( wire, IO_ReadOnly );
+ QBuffer inBuf( wire );
+ inBuf.open( IO_ReadOnly);
+ m_din.setDevice( &inBuf );
+ m_din.setByteOrder( QDataStream::LittleEndian );
+ Q_UINT32 type;
+
+ if ( !okToProceed() )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ // read the event type
+ m_din >> type;
+ m_bytes += sizeof( Q_UINT32 );
+
+ debug( QString( "EventProtocol::parse() Reading event of type %1" ).arg( type ) );
+ if ( type > Stop )
+ {
+ debug( QString ( "EventProtocol::parse() - found unexpected event type %1 - assuming out of sync" ).arg( type ) );
+ m_state = OutOfSync;
+ return 0;
+ }
+
+ // read the event source
+ QString source;
+ if ( !readString( source ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+
+ // now create an event object
+ //HACK: lowercased DN
+ EventTransfer * tentative = new EventTransfer( type, source.lower(), QDateTime::currentDateTime() );
+
+ // add any additional data depending on the type of event
+ // Note: if there are any errors in the way the data is read below, we will soon be OutOfSync
+ QString statusText;
+ QString guid;
+ Q_UINT16 status;
+ Q_UINT32 flags;
+ QString message;
+
+ switch ( type )
+ {
+ case StatusChange: //103 - STATUS + STATUSTEXT
+ if ( !okToProceed() )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ m_din >> status;
+ m_bytes += sizeof( Q_UINT16 );
+ if ( !readString( statusText ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ debug( QString( "got status: %1").arg( status ) );
+ tentative->setStatus( status );
+ debug( QString( "tentative status: %1").arg( tentative->status() ) );
+ tentative->setStatusText( statusText );
+ break;
+ case ConferenceJoined: // 106 - GUID + FLAGS
+ case ConferenceLeft: // 107
+ if ( !readString( guid ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setGuid( guid );
+ if ( !readFlags( flags ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setFlags( flags );
+ break;
+ case UndeliverableStatus: //102 - GUID
+ case ConferenceClosed: //105
+ case ConferenceInviteNotify://118
+ case ConferenceReject: //119
+ case UserTyping: //112
+ case UserNotTyping: //113
+ if ( !readString( guid ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setGuid( guid );
+ break;
+ case ReceiveAutoReply: //121 - GUID + FLAGS + MESSAGE
+ case ReceiveMessage: //108
+ // guid
+ if ( !readString( guid ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setGuid( guid );
+ // flags
+ if ( !readFlags( flags ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setFlags( flags );
+ // message
+ if ( !readString( message ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setMessage( message );
+ break;
+ case ConferenceInvite: //117 GUID + MESSAGE
+ // guid
+ if ( !readString( guid ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setGuid( guid );
+ // message
+ if ( !readString( message ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setMessage( message );
+ break;
+ case UserDisconnect: //114 (NOTHING)
+ case ServerDisconnect: //115
+ // nothing else to read
+ break;
+ case InvalidRecipient: //101
+ case ContactAdd: //104
+ case ReceiveFile: //109
+ case ConferenceRename: //116
+ // unhandled because unhandled in Gaim
+ break;
+ /* GW7 */
+ case ReceivedBroadcast: //122
+ case ReceivedSystemBroadcast: //123
+ // message
+ if ( !readString( message ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ tentative->setMessage( message );
+ break;
+ default:
+ debug( QString( "EventProtocol::parse() - found unexpected event type %1" ).arg( type ) );
+ break;
+ }
+ // if we got this far, the parse succeeded, return the Transfer
+ m_state = Success;
+ //delete m_din;
+ bytes = m_bytes;
+ m_din.unsetDevice();
+ return tentative;
+}
+
+bool EventProtocol::readFlags( Q_UINT32 &flags)
+{
+ if ( okToProceed() )
+ {
+ m_din >> flags;
+ m_bytes += sizeof( Q_UINT32 );
+ return true;
+ }
+ return false;
+}
+
+#include "eventprotocol.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/eventprotocol.h b/kopete/protocols/groupwise/libgroupwise/eventprotocol.h
new file mode 100644
index 00000000..4f6d94e5
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/eventprotocol.h
@@ -0,0 +1,130 @@
+/*
+ Kopete Groupwise Protocol
+ eventprotocol.h - reads the protocol used by GroupWise for signalling Events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_EVENTPROTOCOL_H
+#define GW_EVENTPROTOCOL_H
+
+#include "inputprotocolbase.h"
+
+class EventTransfer;
+/**
+ * This class converts incoming event data into EventTransfer objects. Since it requires knowledge of the binary event format, which
+ * differs for each event type, it is implemented as a separate class. See also @ref CoreProtocol, which detects event messages in the
+ * data stream and hands them to this class for processing.
+ * Event Types
+ *
+@author SUSE AG
+ Ablauf:
+ CoreProtocol receives data in addIncomingData, and passes to wireToTransfer.
+ wireToTransfer detects an event.
+ Passes whole chunk to EventProtocol ( as QByteArray )
+ In to EventProtocol - QByteArray
+ Returned from EventProtocol - EventTransfer *, bytes read, set state?
+ EventProtocol tries to parse data into eventTransfer
+ If not complete, sets state to NeedMore and returns 0
+ If complete, returns number of bytes read for the event
+ If bytes less than length of chunk, CoreProtocol::addIncomingData places the unread bytes back in m_in and calls wireToTransfer again.
+ if ResponseProtocol or EventProtocol set state to NeedMore, don't call wireToTransfer again.
+
+ What event dependent data does EventTransfer contain?
+
+ What if some events contain EXTRA bytes off the end that we don't know about? Then we will put those bytes back on the buffer, and try and parse those as the start of a new message!!! Need to react SAFELY then.
+
+ What event dependent binary data does each event type contain?
+
+ All Events contain an event code, and a source ( a DN )
+ NOTHANDLED indicates that there is no further data and we don't handle events of that type, because they are not sent by the server
+ NONE indicates there is no further data
+ STATUSTEXT, GUID, MESSAGE indicate a string encoded in the usual GroupWise binary string encoding: a UINT32 containing the string length in little-endian, followed by the string itself, as UTF-8 encoded unicode. The string length value includes a terminating NUL, so when converting to a QString, subtract one from the string length.
+ FLAGS contains a UINT32 containing the server's flags for this conference. See gwerror.h for the possible values and meanings of these flags. Only Logging has been observed in practice.
+
+ All events are timestamped with the local time on receipt.
+
+ From gwerror.h:
+ enum Event { InvalidRecipient = 101,
+ NOTHANDLED
+ UndeliverableStatus = 102,
+ NOTHANDLED *
+ StatusChange = 103,
+ Q_UINT16 STATUS
+ STATUSTEXT
+ ContactAdd = 104,
+ NOTHANDLED
+ ConferenceClosed = 105,
+ GUID
+ ConferenceJoined = 106,
+ GUID
+ FLAGS
+ ConferenceLeft = 107,
+ GUID
+ FLAGS
+ ReceiveMessage = 108,
+ GUID
+ FLAGS
+ MESSAGE
+ ReceiveFile = 109,
+ NOTHANDLED
+ UserTyping = 112,
+ GUID
+ UserNotTyping = 113,
+ GUID
+ UserDisconnect = 114,
+ NONE
+ ServerDisconnect = 115,
+ NONE
+ ConferenceRename = 116,
+ NOTHANDLED
+ ConferenceInvite = 117,
+ GUID
+ MESSAGE
+ ConferenceInviteNotify = 118,
+ GUID
+ ConferenceReject = 119,
+ GUID
+ ReceiveAutoReply = 121,
+ GUID
+ FLAGS
+ MESSAGE
+ Start = InvalidRecipient,
+ Stop = ReceiveAutoReply
+ };
+ Therefore we have GUID, FLAGS, MESSAGE, STATUS, STATUSTEXT. All transfers have TYPE and SOURCE, and a TIMESTAMP is added on receipt.
+*/
+
+class EventProtocol : public InputProtocolBase
+{
+Q_OBJECT
+public:
+ EventProtocol(QObject *parent = 0, const char *name = 0);
+ ~EventProtocol();
+ /**
+ * Attempt to parse the supplied data into an @ref EventTransfer object.
+ * The exact state of the parse attempt can be read using @ref state.
+ * @param rawData The unparsed data.
+ * @param bytes An integer used to return the number of bytes read.
+ * @return A pointer to an EventTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object.
+ */
+ Transfer * parse( const QByteArray &, uint & bytes );
+protected:
+ /**
+ * Reads a conference's flags
+ */
+ bool readFlags( Q_UINT32 &flags);
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp b/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp
new file mode 100644
index 00000000..06178f21
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/eventtransfer.cpp
@@ -0,0 +1,145 @@
+/*
+ eventtransfer.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "eventtransfer.h"
+
+EventTransfer::EventTransfer( const Q_UINT32 eventType, const QString & source, QDateTime timeStamp )
+ : Transfer(), m_eventType( eventType ), m_source( source ), m_timeStamp( timeStamp )
+{
+ m_contentFlags |= ( EventType | Source | TimeStamp );
+}
+
+
+EventTransfer::~EventTransfer()
+{
+}
+
+// query contents
+
+bool EventTransfer::hasEventType()
+{
+ return ( m_contentFlags & EventType );
+}
+
+bool EventTransfer::hasSource()
+{
+ return ( m_contentFlags & Source );
+}
+
+bool EventTransfer::hasTimeStamp()
+{
+ return ( m_contentFlags & TimeStamp );
+}
+
+bool EventTransfer::hasGuid()
+{
+ return ( m_contentFlags & Guid );
+}
+
+bool EventTransfer::hasFlags()
+{
+ return ( m_contentFlags & Flags );
+}
+
+bool EventTransfer::hasMessage()
+{
+ return ( m_contentFlags & Message );
+}
+
+bool EventTransfer::hasStatus()
+{
+ return ( m_contentFlags & Status );
+}
+
+bool EventTransfer::hasStatusText()
+{
+ return ( m_contentFlags & StatusText );
+}
+
+// accessors
+
+int EventTransfer::eventType()
+{
+ return m_eventType;
+}
+
+QString EventTransfer::source()
+{
+ return m_source;
+}
+
+QDateTime EventTransfer::timeStamp()
+{
+ return m_timeStamp;
+}
+
+GroupWise::ConferenceGuid EventTransfer::guid()
+{
+ return m_guid;
+}
+
+Q_UINT32 EventTransfer::flags()
+{
+ return m_flags;
+}
+
+QString EventTransfer::message()
+{
+ return m_message;
+}
+
+Q_UINT16 EventTransfer::status()
+{
+ return m_status;
+}
+
+QString EventTransfer::statusText()
+{
+ return m_statusText;
+}
+
+// mutators
+void EventTransfer::setGuid( const GroupWise::ConferenceGuid & guid )
+{
+ m_contentFlags |= Guid;
+ m_guid = guid;
+}
+
+void EventTransfer::setFlags( const Q_UINT32 flags )
+{
+ m_contentFlags |= Flags;
+ m_flags = flags;
+}
+
+void EventTransfer::setMessage( const QString & message )
+{
+ m_contentFlags |= Message;
+ m_message = message;
+}
+
+void EventTransfer::setStatus( const Q_UINT16 inStatus )
+{
+ m_contentFlags |= Status;
+ m_status = inStatus;
+}
+
+void EventTransfer::setStatusText( const QString & statusText )
+{
+ m_contentFlags |= StatusText;
+ m_statusText = statusText;
+}
+
diff --git a/kopete/protocols/groupwise/libgroupwise/eventtransfer.h b/kopete/protocols/groupwise/libgroupwise/eventtransfer.h
new file mode 100644
index 00000000..335d4593
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/eventtransfer.h
@@ -0,0 +1,110 @@
+/*
+ eventtransfer.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_EVENTTRANSFER_H
+#define GW_EVENTTRANSFER_H
+
+#include <qcstring.h>
+#include <qdatetime.h>
+
+#include "gwerror.h"
+
+#include "transfer.h"
+
+namespace Event {
+
+}
+
+/**
+ * Transfer representing an event, a message generated by the server in response to external stimulus
+ * This class can contain varying data items depending on the type of event.
+ * You can query which data is present before trying to access it
+ * @author Kopete Developers
+ */
+class EventTransfer : public Transfer
+{
+public:
+ /**
+ * Flags describing the possible contents of an event transfer
+ */
+ enum Contents { EventType = 0x00000001,
+ Source = 0x00000002,
+ TimeStamp = 0x00000004,
+ Guid = 0x00000008,
+ Flags = 0x00000010,
+ Message = 0x00000020,
+ Status = 0x00000040,
+ StatusText = 0x00000080 };
+ /**
+ * Constructor
+ * @param eventType the event code describing the event, see @refGroupWise::Event.
+ * @param source the user generating the event.
+ * @param timeStamp the time at which the event was received.
+ */
+ EventTransfer( const Q_UINT32 eventType, const QString & source, QDateTime timeStamp );
+ ~EventTransfer();
+ /**
+ * Access the bitmask that describes the transfer's contents. Use @ref Contents to determine what it contains
+ */
+ Q_UINT32 contents();
+ /**
+ * Convenience accessors to see what the transfer contains
+ */
+ bool hasEventType();
+ bool hasSource();
+ bool hasTimeStamp();
+ bool hasGuid();
+ bool hasFlags();
+ bool hasMessage();
+ bool hasStatus();
+ bool hasStatusText();
+
+ /**
+ * Accessors for the transfer's contents
+ */
+ TransferType type() { return Transfer::EventTransfer; }
+ int eventType();
+ QString source();
+ QDateTime timeStamp();
+ GroupWise::ConferenceGuid guid();
+ Q_UINT32 flags();
+ QString message();
+ Q_UINT16 status();
+ QString statusText();
+
+ /**
+ * Mutators to set the transfer's contents
+ */
+ void setGuid( const GroupWise::ConferenceGuid & guid );
+ void setFlags( const Q_UINT32 flags );
+ void setMessage( const QString & message );
+ void setStatus( const Q_UINT16 status );
+ void setStatusText( const QString & statusText);
+
+private:
+ Q_UINT32 m_contentFlags;
+ int m_eventType;
+ QString m_source;
+ QDateTime m_timeStamp;
+ GroupWise::ConferenceGuid m_guid;
+ Q_UINT32 m_flags;
+ QString m_message;
+ Q_UINT16 m_status;
+ QString m_statusText;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h b/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h
new file mode 100644
index 00000000..207531bb
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwchatrooms.h
@@ -0,0 +1,78 @@
+/*
+ Kopete Groupwise Protocol
+ gwchatrooms.h - Data types for group chat
+
+ Copyright (c) 2005 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdatetime.h>
+#include <qvaluelist.h>
+
+#ifndef GROUPWISE_CHATROOMS_H
+#define GROUPWISE_CHATROOMS_H
+
+namespace GroupWise
+{
+
+class ChatContact
+{
+ public:
+ QString dn;
+ uint chatRights;
+};
+typedef QValueList<GroupWise::ChatContact> ChatContactList;
+
+struct ChatroomSearchResult
+{
+ QString name;
+ QString ownerDN;
+ uint participants;
+};
+
+
+class Chatroom
+{
+ public:
+ enum UserStatus { Participating, NotParticipating };
+ enum Rights { Read = 1, Write = 2, Modify = 4, Moderator = 8, Owner = 16 };
+ QString creatorDN;
+ QString description;
+ QString disclaimer;
+ QString displayName;
+ QString objectId;
+ QString ownerDN;
+ QString query;
+ QString topic;
+ bool archive;
+ uint maxUsers;
+ uint chatRights;
+ UserStatus userStatus;
+ QDateTime createdOn;
+ uint participantsCount;
+ // haveParticipants, Acl, Invites indicate if we have obtained these lists from the server, so we can tell 'not fetched list' and 'fetched empty list' apart.
+ bool haveParticipants;
+ ChatContactList participants;
+ bool haveAcl;
+ ChatContactList acl;
+ bool haveInvites;
+ ChatContactList invites;
+
+ Chatroom() { archive = false; maxUsers = 0; chatRights = 0; participantsCount = 0; haveParticipants = false; haveAcl = false; haveInvites = false; }
+ Chatroom( ChatroomSearchResult csr ) { archive = false; maxUsers = 0; chatRights = 0; participantsCount = csr.participants; haveParticipants = false; haveAcl = false; haveInvites = false; ownerDN = csr.ownerDN; displayName = csr.name; }
+};
+
+typedef QValueList<Chatroom> ChatroomList;
+typedef QMap<QString, Chatroom> ChatroomMap;
+}
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp b/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp
new file mode 100644
index 00000000..7d58de93
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwclientstream.cpp
@@ -0,0 +1,606 @@
+/*
+ gwclientstream.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+ encode_method from Gaim src/protocols/novell/nmconn.c
+ Copyright (c) 2004 Novell, Inc. All Rights Reserved
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+//#include<qtextstream.h>
+//#include<qguardedptr.h>
+// #include<qca.h>
+// #include<stdlib.h>
+// #include"bytestream.h"
+// #include"base64.h"
+// #include"hash.h"
+// #include"simplesasl.h"
+// #include"securestream.h"
+// #include"protocol.h"
+
+#include <qapplication.h> // for qdebug
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qptrqueue.h>
+#include <qtimer.h>
+
+#include "bytestream.h"
+#include "connector.h"
+#include "coreprotocol.h"
+#include "request.h"
+#include "securestream.h"
+#include "tlshandler.h"
+
+//#include "iostream.h"
+
+#include "gwclientstream.h"
+
+//#define LIBGW_DEBUG 1
+
+void cs_dump( const QByteArray &bytes );
+
+enum {
+ Idle,
+ Connecting,
+ WaitVersion,
+ WaitTLS,
+ NeedParams,
+ Active,
+ Closing
+};
+
+enum {
+ Client,
+ Server
+};
+
+class ClientStream::Private
+{
+public:
+ Private()
+ {
+ conn = 0;
+ bs = 0;
+ ss = 0;
+ tlsHandler = 0;
+ tls = 0;
+// sasl = 0;
+ in.setAutoDelete(true);
+
+ allowPlain = false;
+ mutualAuth = false;
+ haveLocalAddr = false;
+/* minimumSSF = 0;
+ maximumSSF = 0;*/
+ doBinding = true;
+
+ in_rrsig = false;
+
+ reset();
+ }
+ void reset()
+ {
+ state = Idle;
+ notify = 0;
+ newTransfers = false;
+// sasl_ssf = 0;
+ tls_warned = false;
+ using_tls = false;
+ }
+
+ NovellDN id;
+ QString server;
+ bool oldOnly;
+ bool allowPlain, mutualAuth;
+ bool haveLocalAddr;
+ QHostAddress localAddr;
+ Q_UINT16 localPort;
+// int minimumSSF, maximumSSF;
+// QString sasl_mech;
+ bool doBinding;
+
+ bool in_rrsig;
+
+ Connector *conn;
+ ByteStream *bs;
+ TLSHandler *tlsHandler;
+ QCA::TLS *tls;
+// QCA::SASL *sasl;
+ SecureStream *ss;
+ CoreProtocol client;
+ //CoreProtocol srv;
+
+ QString defRealm;
+
+ int mode;
+ int state;
+ int notify;
+ bool newTransfers;
+// int sasl_ssf;
+ bool tls_warned, using_tls;
+ bool doAuth;
+
+// QStringList sasl_mechlist;
+
+ int errCond;
+ QString errText;
+
+ QPtrQueue<Transfer> in;
+
+ QTimer noopTimer; // probably not needed
+ int noop_time;
+};
+
+ClientStream::ClientStream(Connector *conn, TLSHandler *tlsHandler, QObject *parent)
+:Stream(parent)
+{
+ d = new Private;
+ d->mode = Client;
+ d->conn = conn;
+ connect( d->conn, SIGNAL(connected()), SLOT(cr_connected()) );
+ connect( d->conn, SIGNAL(error()), SLOT(cr_error()) );
+ connect( &d->client, SIGNAL( outgoingData( const QByteArray& ) ), SLOT ( cp_outgoingData( const QByteArray & ) ) );
+ connect( &d->client, SIGNAL( incomingData() ), SLOT ( cp_incomingData() ) );
+
+ d->noop_time = 0;
+ connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop()));
+
+ d->tlsHandler = tlsHandler; // all the extra stuff happening in the larger ctor happens at connect time :)
+}
+
+ClientStream::~ClientStream()
+{
+ reset();
+ delete d;
+}
+
+void ClientStream::reset(bool all)
+{
+ d->reset();
+ d->noopTimer.stop();
+
+ // delete securestream
+ delete d->ss;
+ d->ss = 0;
+
+ // reset sasl
+// delete d->sasl;
+// d->sasl = 0;
+
+ // client
+ if(d->mode == Client) {
+ // reset tls
+ if(d->tlsHandler)
+ d->tlsHandler->reset();
+
+ // reset connector
+ if(d->bs) {
+ d->bs->close();
+ d->bs = 0;
+ }
+ d->conn->done();
+
+ // reset state machine
+ d->client.reset();
+ }
+ if(all)
+ d->in.clear();
+}
+
+// Jid ClientStream::jid() const
+// {
+// return d->jid;
+// }
+
+void ClientStream::connectToServer(const NovellDN &id, bool auth)
+{
+ reset(true);
+ d->state = Connecting;
+ d->id = id;
+ d->doAuth = auth;
+ d->server = d->id.server;
+
+ d->conn->connectToServer( d->server );
+}
+
+void ClientStream::continueAfterWarning()
+{
+ if(d->state == WaitVersion) {
+ // if we don't have TLS yet, then we're never going to get it
+ if(!d->tls_warned && !d->using_tls) {
+ d->tls_warned = true;
+ d->state = WaitTLS;
+ emit warning(WarnNoTLS);
+ return;
+ }
+ d->state = Connecting;
+ processNext();
+ }
+ else if(d->state == WaitTLS) {
+ d->state = Connecting;
+ processNext();
+ }
+}
+
+void ClientStream::accept()
+{
+/* d->srv.host = d->server;
+ processNext();*/
+}
+
+bool ClientStream::isActive() const
+{
+ return (d->state != Idle);
+}
+
+bool ClientStream::isAuthenticated() const
+{
+ return (d->state == Active);
+}
+
+// void ClientStream::setPassword(const QString &s)
+// {
+// if(d->client.old) {
+// d->client.setPassword(s);
+// }
+// else {
+// if(d->sasl)
+// d->sasl->setPassword(s);
+// }
+// }
+
+// void ClientStream::setRealm(const QString &s)
+// {
+// if(d->sasl)
+// d->sasl->setRealm(s);
+// }
+
+void ClientStream::continueAfterParams()
+{
+/* if(d->state == NeedParams) {
+ d->state = Connecting;
+ if(d->client.old) {
+ processNext();
+ }
+ else {
+ if(d->sasl)
+ d->sasl->continueAfterParams();
+ }
+ }*/
+}
+
+void ClientStream::setNoopTime(int mills)
+{
+ d->noop_time = mills;
+
+ if(d->state != Active)
+ return;
+
+ if(d->noop_time == 0) {
+ d->noopTimer.stop();
+ return;
+ }
+ d->noopTimer.start(d->noop_time);
+}
+
+void ClientStream::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->haveLocalAddr = true;
+ d->localAddr = addr;
+ d->localPort = port;
+}
+
+int ClientStream::errorCondition() const
+{
+ return d->errCond;
+}
+
+QString ClientStream::errorText() const
+{
+ return d->errText;
+}
+
+// QDomElement ClientStream::errorAppSpec() const
+// {
+// return d->errAppSpec;cr_error
+// }
+
+// bool ClientStream::old() const
+// {
+// return d->client.old;
+// }
+
+void ClientStream::close()
+{
+ if(d->state == Active) {
+ d->state = Closing;
+// d->client.shutdown();
+ processNext();
+ }
+ else if(d->state != Idle && d->state != Closing) {
+ reset();
+ }
+}
+
+void ClientStream::setAllowPlain(bool b)
+{
+ d->allowPlain = b;
+}
+
+void ClientStream::setRequireMutualAuth(bool b)
+{
+ d->mutualAuth = b;
+}
+
+// void ClientStream::setSSFRange(int low, int high)
+// {
+// d->minimumSSF = low;
+// d->maximumSSF = high;
+// }
+
+// void ClientStream::setOldOnly(bool b)
+// {
+// d->oldOnly = b;
+// }
+
+bool ClientStream::transfersAvailable() const
+{
+ return ( !d->in.isEmpty() );
+}
+
+Transfer * ClientStream::read()
+{
+ if(d->in.isEmpty())
+ return 0; //first from queue...
+ else
+ return d->in.dequeue();
+}
+
+void ClientStream::write( Request *request )
+{
+ // pass to CoreProtocol for transformation into wire format
+ d->client.outgoingTransfer( request );
+}
+
+void cs_dump( const QByteArray &bytes )
+{
+//#define GW_CLIENTSTREAM_DEBUG 1
+#ifdef GW_CLIENTSTREAM_DEBUG
+ CoreProtocol::debug( QString( "contains: %1 bytes " ).arg( bytes.count() ) );
+ uint count = 0;
+ while ( count < bytes.count() )
+ {
+ int dword = 0;
+ for ( int i = 0; i < 8; ++i )
+ {
+ if ( count + i < bytes.count() )
+ printf( "%02x ", bytes[ count + i ] );
+ else
+ printf( " " );
+ if ( i == 3 )
+ printf( " " );
+ }
+ printf(" | ");
+ dword = 0;
+ for ( int i = 0; i < 8; ++i )
+ {
+ if ( count + i < bytes.count() )
+ {
+ int j = bytes [ count + i ];
+ if ( j >= 0x20 && j <= 0x7e )
+ printf( "%2c ", j );
+ else
+ printf( "%2c ", '.' );
+ }
+ else
+ printf( " " );
+ if ( i == 3 )
+ printf( " " );
+ }
+ printf( "\n" );
+ count += 8;
+ }
+ printf( "\n" );
+#else
+ Q_UNUSED( bytes );
+#endif
+}
+
+void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes )
+{
+ // take formatted bytes from CoreProtocol and put them on the wire
+#ifdef LIBGW_DEBUG
+ CoreProtocol::debug( "ClientStream::cp_outgoingData:" );
+ cs_dump( outgoingBytes );
+#endif
+ d->ss->write( outgoingBytes );
+}
+
+void ClientStream::cp_incomingData()
+{
+ CoreProtocol::debug( "ClientStream::cp_incomingData:" );
+ Transfer * incoming = d->client.incomingTransfer();
+ if ( incoming )
+ {
+ CoreProtocol::debug( " - got a new transfer" );
+ d->in.enqueue( incoming );
+ d->newTransfers = true;
+ emit doReadyRead();
+ }
+ else
+ CoreProtocol::debug( QString( " - client signalled incomingData but none was available, state is: %1" ).arg( d->client.state() ) );
+}
+
+void ClientStream::cr_connected()
+{
+ d->bs = d->conn->stream();
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+
+ QByteArray spare = d->bs->read();
+
+ d->ss = new SecureStream(d->bs);
+ connect(d->ss, SIGNAL(readyRead()), SLOT(ss_readyRead()));
+ connect(d->ss, SIGNAL(bytesWritten(int)), SLOT(ss_bytesWritten(int)));
+ connect(d->ss, SIGNAL(tlsHandshaken()), SLOT(ss_tlsHandshaken()));
+ connect(d->ss, SIGNAL(tlsClosed()), SLOT(ss_tlsClosed()));
+ connect(d->ss, SIGNAL(error(int)), SLOT(ss_error(int)));
+
+ //d->client.startDialbackOut("andbit.net", "im.pyxa.org");
+ //d->client.startServerOut(d->server);
+
+// d->client.startClientOut(d->jid, d->oldOnly, d->conn->useSSL(), d->doAuth);
+// d->client.setAllowTLS(d->tlsHandler ? true: false);
+// d->client.setAllowBind(d->doBinding);
+// d->client.setAllowPlain(d->allowPlain);
+
+ /*d->client.jid = d->jid;
+ d->client.server = d->server;
+ d->client.allowPlain = d->allowPlain;
+ d->client.oldOnly = d->oldOnly;
+ d->client.sasl_mech = d->sasl_mech;
+ d->client.doTLS = d->tlsHandler ? true: false;
+ d->client.doBinding = d->doBinding;*/
+
+ QGuardedPtr<QObject> self = this;
+ emit connected();
+ if(!self)
+ return;
+
+ // immediate SSL?
+ if(d->conn->useSSL()) {
+ CoreProtocol::debug( "CLIENTSTREAM: cr_connected(), starting TLS" );
+ d->using_tls = true;
+ d->ss->startTLSClient(d->tlsHandler, d->server, spare);
+ }
+ else {
+/* d->client.addIncomingData(spare);
+ processNext();*/
+ }
+}
+
+void ClientStream::cr_error()
+{
+ reset();
+ emit error(ErrConnection);
+}
+
+void ClientStream::bs_connectionClosed()
+{
+ reset();
+ emit connectionClosed();
+}
+
+void ClientStream::bs_delayedCloseFinished()
+{
+ // we don't care about this (we track all important data ourself)
+}
+
+void ClientStream::bs_error(int)
+{
+ // TODO
+}
+
+void ClientStream::ss_readyRead()
+{
+ QByteArray a;
+ a = d->ss->read();
+
+#ifdef LIBGW_DEBUG
+ QCString cs(a.data(), a.size()+1);
+ CoreProtocol::debug( QString( "ClientStream: ss_readyRead() recv: %1 bytes" ).arg( a.size() ) );
+ cs_dump( a );
+#endif
+
+ d->client.addIncomingData(a);
+/* if(d->notify & CoreProtocol::NRecv) { */
+ //processNext();
+}
+
+void ClientStream::ss_bytesWritten(int bytes)
+{
+#ifdef LIBGW_DEBUG
+ CoreProtocol::debug( QString( "ClientStream::ss_bytesWritten: %1 bytes written" ).arg( bytes ) );
+#else
+ Q_UNUSED( bytes );
+#endif
+}
+
+void ClientStream::ss_tlsHandshaken()
+{
+ QGuardedPtr<QObject> self = this;
+ emit securityLayerActivated(LayerTLS);
+ if(!self)
+ return;
+ processNext();
+}
+
+void ClientStream::ss_tlsClosed()
+{
+ CoreProtocol::debug( "ClientStream::ss_tlsClosed()" );
+ reset();
+ emit connectionClosed();
+}
+
+void ClientStream::ss_error(int x)
+{
+ CoreProtocol::debug( QString( "ClientStream::ss_error() x=%1 ").arg( x ) );
+ if(x == SecureStream::ErrTLS) {
+ reset();
+ d->errCond = TLSFail;
+ emit error(ErrTLS);
+ }
+ else {
+ reset();
+ emit error(ErrSecurityLayer);
+ }
+}
+
+void ClientStream::srvProcessNext()
+{
+}
+
+void ClientStream::doReadyRead()
+{
+ //QGuardedPtr<QObject> self = this;
+ emit readyRead();
+ //if(!self)
+ // return;
+ //d->in_rrsig = false;
+}
+
+void ClientStream::processNext()
+{
+ if( !d->in.isEmpty() ) {
+ //d->in_rrsig = true;
+ QTimer::singleShot(0, this, SLOT(doReadyRead()));
+ }
+}
+
+bool ClientStream::handleNeed()
+{
+ return false;
+}
+
+
+void ClientStream::doNoop()
+{
+}
+
+void ClientStream::handleError()
+{
+}
+
+#include "gwclientstream.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/gwclientstream.h b/kopete/protocols/groupwise/libgroupwise/gwclientstream.h
new file mode 100644
index 00000000..5ae5ec8c
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwclientstream.h
@@ -0,0 +1,185 @@
+/*
+ gwclientstream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_CLIENTSTREAM_H
+#define GW_CLIENTSTREAM_H
+
+#include <qca.h>
+
+#include "gwfield.h"
+#include "stream.h"
+
+// forward defines
+class ByteStream;
+class Connector;
+class Request;
+class TLSHandler;
+
+typedef struct NovellDN
+{
+ QString dn;
+ QString server;
+};
+
+class ClientStream : public Stream
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up
+ ErrNeg, // Negotiation error, see condition
+ ErrTLS, // TLS error, see condition
+ ErrAuth, // Auth error, see condition
+ ErrSecurityLayer, // broken SASL security layer
+ ErrBind // Resource binding error
+ };
+ enum Warning {
+/*# WarnOldVersion, // server uses older XMPP/Jabber "0.9" protocol // can be customised for novell versions*/
+ WarnNoTLS // there is no chance for TLS at this point
+ };
+ enum NegCond {
+ HostGone, // host no longer hosted
+ HostUnknown, // unknown host
+ RemoteConnectionFailed, // unable to connect to a required remote resource
+ SeeOtherHost, // a 'redirect', see errorText() for other host
+ UnsupportedVersion // unsupported XMPP version
+ };
+ enum TLSCond {
+ TLSStart, // server rejected STARTTLS
+ TLSFail // TLS failed, ask TLSHandler-subclass what's up
+ };
+ enum SecurityLayer {
+ LayerTLS,
+ LayerSASL
+ };
+ enum AuthCond {
+ GenericAuthError, // all-purpose "can't login" error
+ NoMech, // No appropriate auth mech available
+ BadProto, // Bad SASL auth protocol
+ BadServ, // Server failed mutual auth
+ EncryptionRequired, // can't use mech without TLS
+/*# InvalidAuthzid, // bad input JID // need to change this to novell DN*/
+ InvalidMech, // bad mechanism
+ InvalidRealm, // bad realm
+ MechTooWeak, // can't use mech with this authzid
+ NotAuthorized, // bad user, bad password, bad creditials
+ TemporaryAuthFailure // please try again later!
+ };
+ enum BindCond {
+ BindNotAllowed, // not allowed to bind a resource
+ BindConflict // resource in-use
+ };
+
+ ClientStream(Connector *conn, TLSHandler *tlsHandler=0, QObject *parent=0);
+ ~ClientStream();
+
+ void connectToServer(const NovellDN &id, bool auth=true);
+ void accept(); // server
+ bool isActive() const;
+ bool isAuthenticated() const;
+
+ // login params
+ void setUsername(const QString &s);
+ void setPassword(const QString &s);
+ void setRealm(const QString &s);
+ void continueAfterParams();
+
+ // security options (old protocol only uses the first !)
+ void setAllowPlain(bool);
+ void setRequireMutualAuth(bool);
+ void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ void close();
+
+ /**
+ * Are there any messages waiting to be read
+ */
+ bool transfersAvailable() const;
+ /**
+ * Read a message received from the server
+ */
+ Transfer * read();
+
+ /**
+ * Send a message to the server
+ */
+ void write( Request * request );
+
+ int errorCondition() const;
+ QString errorText() const;
+// # QDomElement errorAppSpec() const; // redondo
+
+ // extrahttp://bugs.kde.org/show_bug.cgi?id=85158
+/*# void writeDirect(const QString &s); // must be for debug testing*/
+ void setNoopTime(int mills);
+
+signals:
+ void connected();
+ void securityLayerActivated(int);
+ //void needAuthParams(bool user, bool pass, bool realm);
+ void authenticated(); // this signal is ordinarily emitted in processNext
+ void warning(int);
+// # void incomingXml(const QString &s); // signals emitted in processNext but don't seem to go anywhere...
+// # void outgoingXml(const QString &s); //
+// void readyRead(); //signals that there is a transfer ready to be read - defined in stream
+public slots:
+ void continueAfterWarning();
+
+private slots:
+ void cr_connected();
+ void cr_error();
+ /**
+ * collects wire ready outgoing data from the core protocol and sends
+ */
+ void cp_outgoingData( const QByteArray& );
+ /**
+ * collects parsed incoming data as a transfer from the core protocol and queues
+ */
+ void cp_incomingData();
+
+ void bs_connectionClosed();
+ void bs_delayedCloseFinished();
+ void bs_error(int); // server only
+
+ void ss_readyRead();
+ void ss_bytesWritten(int);
+ void ss_tlsHandshaken();
+ void ss_tlsClosed();
+ void ss_error(int);
+
+ void doNoop();
+ void doReadyRead();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool all=false);
+ void processNext();
+ bool handleNeed();
+ void handleError();
+ void srvProcessNext();
+
+ /**
+ * convert internal method representation to wire
+ */
+ static char* encode_method(Q_UINT8 method);
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/gwerror.cpp b/kopete/protocols/groupwise/libgroupwise/gwerror.cpp
new file mode 100644
index 00000000..5338cea0
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwerror.cpp
@@ -0,0 +1,276 @@
+/*
+ gwerror.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2007 Novell, Inc http://www.novell.com/linux
+
+ Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <klocale.h>
+
+#include "gwerror.h"
+
+QString GroupWise::errorCodeToString( int errorCode )
+{
+ QString errorString;
+ switch ( errorCode )
+ {
+#if 0
+ case NMERR_ACCESS_DENIED:
+ errorString = i18n( "Access denied" );
+ break;
+ case NMERR_NOT_SUPPORTED:
+ errorString = i18n( "Not supported" );
+ break;
+ case NMERR_PASSWORD_EXPIRED:
+ errorString = i18n( "Password expired" );
+ break;
+ case NMERR_PASSWORD_INVALID:
+ errorString = i18n( "Invalid password" );
+ break;
+ case NMERR_USER_NOT_FOUND:
+ errorString = i18n( "User not found" );
+ break;
+ case NMERR_ATTRIBUTE_NOT_FOUND:
+ errorString = i18n( "Attribute not found" );
+ break;
+ case NMERR_USER_DISABLED:
+ errorString = i18n( "User is disabled" );
+ break;
+ case NMERR_DIRECTORY_FAILURE:
+ errorString = i18n( "Directory failure" );
+ break;
+ case NMERR_HOST_NOT_FOUND:
+ errorString = i18n( "Host not found" );
+ break;
+ case NMERR_ADMIN_LOCKED:
+ errorString = i18n( "Locked by admin" );
+ break;
+ case NMERR_DUPLICATE_PARTICIPANT:
+ errorString = i18n( "Duplicate participant" );
+ break;
+ case NMERR_SERVER_BUSY:
+ errorString = i18n( "Server busy" );
+ break;
+ case NMERR_OBJECT_NOT_FOUND:
+ errorString = i18n( "Object not found" );
+ break;
+ case NMERR_DIRECTORY_UPDATE:
+ errorString = i18n( "Directory update" );
+ break;
+ case NMERR_DUPLICATE_FOLDER:
+ errorString = i18n( "Duplicate folder" );
+ break;
+ case NMERR_DUPLICATE_CONTACT:
+ errorString = i18n( "Contact list entry already exists" );
+ break;
+ case NMERR_USER_NOT_ALLOWED:
+ errorString = i18n( "User not allowed" );
+ break;
+ case NMERR_TOO_MANY_CONTACTS:
+ errorString = i18n( "Too many contacts" );
+ break;
+ case NMERR_CONFERENCE_NOT_FOUND_2:
+ errorString = i18n( "Conference not found" );
+ break;
+ case NMERR_TOO_MANY_FOLDERS:
+ errorString = i18n( "Too many folders" );
+ break;
+ case NMERR_SERVER_PROTOCOL:
+ errorString = i18n( "Server protocol error" );
+ break;
+ case NMERR_CONVERSATION_INVITE:
+ errorString = i18n( "Conversation invitation error" );
+ break;
+ case NMERR_USER_BLOCKED:
+ errorString = i18n( "User is blocked" );
+ break;
+ case NMERR_MASTER_ARCHIVE_MISSING:
+ errorString = i18n( "Master archive is missing" );
+ break;
+ case NMERR_PASSWORD_EXPIRED_2:
+ errorString = i18n( "Expired password in use" );
+ break;
+ case NMERR_CREDENTIALS_MISSING:
+ errorString = i18n( "Credentials missing" );
+ break;
+ case NMERR_AUTHENTICATION_FAILED:
+ errorString = i18n( "Authentication failed" );
+ break;
+ case NMERR_EVAL_CONNECTION_LIMIT:
+ errorString = i18n( "Eval connection limit" );
+ break;
+ case MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION:
+ errorString = i18n( "Unsupported client version" );
+ break;
+ case MSGPRES_ERR_DUPLICATE_CHAT:
+ errorString = i18n( "A duplicate chat was found" );
+ break;
+ case MSGPRES_ERR_CHAT_NOT_FOUND:
+ errorString = i18n( "Chat not found" );
+ break;
+ case MSGPRES_ERR_INVALID_NAME:
+ errorString = i18n( "Invalid chat name" );
+ break;
+ case MSGPRES_ERR_CHAT_ACTIVE:
+ errorString = i18n( "The chat is active" );
+ break;
+ case MSGPRES_ERR_CHAT_BUSY:
+ errorString = i18n( "Chat is busy; try again" );
+ break;
+ case MSGPRES_ERR_REQUEST_TOO_SOON:
+ errorString = i18n( "Tried request too soon after another; try again" );
+ break;
+ case MSGPRES_ERR_CHAT_NOT_ACTIVE:
+ errorString = i18n( "Server's chat subsystem is not active" );
+ break;
+ case MSGPRES_ERR_INVALID_CHAT_UPDATE:
+ errorString = i18n( "The chat update request is invalid" );
+ break;
+ case MSGPRES_ERR_DIRECTORY_MISMATCH:
+ errorString = i18n( "Write failed due to directory mismatch" );
+ break;
+ case MSGPRES_ERR_RECIPIENT_TOO_OLD:
+ errorString = i18n( "Recipient's client version is too old" );
+ break;
+ case MSGPRES_ERR_CHAT_NO_LONGER_VALID:
+ errorString = i18n( "Chat has been removed from server" );
+ break;
+ default:
+ errorString = i18n("Unrecognized error code: %s").arg( errorCode );
+#else
+ case NMERR_ACCESS_DENIED:
+ errorString = "Access denied";
+ break;
+ case NMERR_NOT_SUPPORTED:
+ errorString = "Not supported";
+ break;
+ case NMERR_PASSWORD_EXPIRED:
+ errorString = "Password expired";
+ break;
+ case NMERR_PASSWORD_INVALID:
+ errorString = "Invalid password";
+ break;
+ case NMERR_USER_NOT_FOUND:
+ errorString = "User not found";
+ break;
+ case NMERR_ATTRIBUTE_NOT_FOUND:
+ errorString = "Attribute not found";
+ break;
+ case NMERR_USER_DISABLED:
+ errorString = "User is disabled";
+ break;
+ case NMERR_DIRECTORY_FAILURE:
+ errorString = "Directory failure";
+ break;
+ case NMERR_HOST_NOT_FOUND:
+ errorString = "Host not found";
+ break;
+ case NMERR_ADMIN_LOCKED:
+ errorString = "Locked by admin";
+ break;
+ case NMERR_DUPLICATE_PARTICIPANT:
+ errorString = "Duplicate participant";
+ break;
+ case NMERR_SERVER_BUSY:
+ errorString = "Server busy";
+ break;
+ case NMERR_OBJECT_NOT_FOUND:
+ errorString = "Object not found";
+ break;
+ case NMERR_DIRECTORY_UPDATE:
+ errorString = "Directory update";
+ break;
+ case NMERR_DUPLICATE_FOLDER:
+ errorString = "Duplicate folder";
+ break;
+ case NMERR_DUPLICATE_CONTACT:
+ errorString = "Contact list entry already exists";
+ break;
+ case NMERR_USER_NOT_ALLOWED:
+ errorString = "User not allowed";
+ break;
+ case NMERR_TOO_MANY_CONTACTS:
+ errorString = "Too many contacts";
+ break;
+ case NMERR_CONFERENCE_NOT_FOUND_2:
+ errorString = "Conference not found";
+ break;
+ case NMERR_TOO_MANY_FOLDERS:
+ errorString = "Too many folders";
+ break;
+ case NMERR_SERVER_PROTOCOL:
+ errorString = "Server protocol error";
+ break;
+ case NMERR_CONVERSATION_INVITE:
+ errorString = "Conversation invitation error";
+ break;
+ case NMERR_USER_BLOCKED:
+ errorString = "User is blocked";
+ break;
+ case NMERR_MASTER_ARCHIVE_MISSING:
+ errorString = "Master archive is missing";
+ break;
+ case NMERR_PASSWORD_EXPIRED_2:
+ errorString = "Expired password in use";
+ break;
+ case NMERR_CREDENTIALS_MISSING:
+ errorString = "Credentials missing";
+ break;
+ case NMERR_AUTHENTICATION_FAILED:
+ errorString = "Authentication failed";
+ break;
+ case NMERR_EVAL_CONNECTION_LIMIT:
+ errorString = "Eval connection limit";
+ break;
+ case MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION:
+ errorString = "Unsupported client version";
+ break;
+ case MSGPRES_ERR_DUPLICATE_CHAT:
+ errorString = "A duplicate chat was found";
+ break;
+ case MSGPRES_ERR_CHAT_NOT_FOUND:
+ errorString = "Chat not found";
+ break;
+ case MSGPRES_ERR_INVALID_NAME:
+ errorString = "Invalid chat name";
+ break;
+ case MSGPRES_ERR_CHAT_ACTIVE:
+ errorString = "The chat is active";
+ break;
+ case MSGPRES_ERR_CHAT_BUSY:
+ errorString = "Chat is busy; try again";
+ break;
+ case MSGPRES_ERR_REQUEST_TOO_SOON:
+ errorString = "Tried request too soon after another; try again";
+ break;
+ case MSGPRES_ERR_CHAT_NOT_ACTIVE:
+ errorString = "Server's chat subsystem is not active";
+ break;
+ case MSGPRES_ERR_INVALID_CHAT_UPDATE:
+ errorString = "The chat update request is invalid";
+ break;
+ case MSGPRES_ERR_DIRECTORY_MISMATCH:
+ errorString = "Write failed due to directory mismatch";
+ break;
+ case MSGPRES_ERR_RECIPIENT_TOO_OLD:
+ errorString = "Recipient's client version is too old";
+ break;
+ case MSGPRES_ERR_CHAT_NO_LONGER_VALID:
+ errorString = "Chat has been removed from server";
+ break;
+ default:
+ errorString = QString("Unrecognized error code: %s").arg( errorCode );
+#endif
+ }
+ return errorString;
+} \ No newline at end of file
diff --git a/kopete/protocols/groupwise/libgroupwise/gwerror.h b/kopete/protocols/groupwise/libgroupwise/gwerror.h
new file mode 100644
index 00000000..5300f788
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwerror.h
@@ -0,0 +1,241 @@
+/*
+ gwerror.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004-2007 Novell, Inc http://www.novell.com/linux
+
+ Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_ERROR_H
+#define GW_ERROR_H
+
+#include <qdatetime.h>
+#include <qglobal.h>
+#include <qmap.h>
+#include <qstring.h>
+
+typedef Q_UINT16 NMERR_T;
+#define GROUPWISE_DEBUG_GLOBAL 14190
+#define GROUPWISE_DEBUG_LIBGW 14191
+#define GROUPWISE_DEBUG_RAW 14192
+
+#define BLANK_GUID "[00000000-00000000-00000000-0000-0000]"
+#define CONF_GUID_END 27
+
+//#define LIBGW_DEBUG 1
+#define LIBGW_USE_KDEBUG 1
+
+namespace GroupWise
+{
+ enum Status { Unknown = 0,
+ Offline = 1,
+ Available = 2,
+ Busy = 3,
+ Away = 4,
+ AwayIdle = 5,
+ Invalid = 6
+ };
+
+ enum Error { None = 0,
+ ErrorBase = 0x2000L,
+ BadParm,
+ TCPWrite,
+ TCPRead,
+ Protocol,
+ ServerRedirect,
+ ConferenceNotFound,
+ ConferenceNotInstantiated,
+ FolderExists
+ };
+
+ enum Event { InvalidRecipient = 101,
+ UndeliverableStatus = 102,
+ StatusChange = 103,
+ ContactAdd = 104,
+ ConferenceClosed = 105,
+ ConferenceJoined = 106,
+ ConferenceLeft = 107,
+ ReceiveMessage = 108,
+ ReceiveFile = 109,
+ UserTyping = 112,
+ UserNotTyping = 113,
+ UserDisconnect = 114,
+ ServerDisconnect = 115,
+ ConferenceRename = 116,
+ ConferenceInvite = 117,
+ ConferenceInviteNotify = 118,
+ ConferenceReject = 119,
+ ReceiveAutoReply = 121,
+ Start = InvalidRecipient,
+ /* Event codes >= 122 are new in GW7 protocol */
+ ReceivedBroadcast = 122,
+ ReceivedSystemBroadcast = 123,
+ ConferenceAttribUpdate = 128,
+ ConferenceTopicChanged = 129,
+ ChatroomNameChanged = 130,
+ ConferenceRightsChanged = 131,
+ ConferenceRemoved = 132, /* you were kicked */
+ ChatOwnerChanged = 133,
+ Stop = ChatOwnerChanged
+
+ };
+
+ enum ConferenceFlags { Logging = 0x00000001,
+ Secure = 0x00000002,
+ Closed = 0x10000000
+ };
+
+ QString errorCodeToString( int errorCode );
+
+ // helpful structs used to pass data between the client library and the application using it
+ class ConferenceGuid : public QString
+ {
+ public:
+ ConferenceGuid();
+ ConferenceGuid( const QString & string );
+ ~ConferenceGuid();
+ };
+
+ bool operator==( const ConferenceGuid & g1, const ConferenceGuid & g2 );
+ bool operator==( const QString & s, const ConferenceGuid & g );
+ bool operator==( const ConferenceGuid & g, const QString & s );
+
+ struct ConferenceEvent
+ {
+ Event type;
+ ConferenceGuid guid;
+ QString user;
+ QDateTime timeStamp;
+ Q_UINT32 flags;
+ QString message;
+ };
+
+ struct FolderItem
+ {
+ uint id;
+ uint sequence;
+ uint parentId;
+ QString name;
+ };
+
+ struct ContactItem
+ {
+ uint id;
+ uint parentId;
+ uint sequence;
+ QString dn;
+ QString displayName;
+ };
+
+ struct ContactDetails
+ {
+ QString cn,
+ dn,
+ givenName,
+ surname,
+ fullName,
+ awayMessage,
+ authAttribute;
+ int status;
+ bool archive;
+ QMap< QString, QString > properties;
+ };
+
+ struct OutgoingMessage
+ {
+ ConferenceGuid guid;
+ QString message;
+ QString rtfMessage;
+ };
+
+ struct UserSearchQueryTerm
+ {
+ QString field;
+ QString argument;
+ int operation;
+ };
+
+ struct CustomStatus
+ {
+ GroupWise::Status status;
+ QString name;
+ QString autoReply;
+ };
+}
+
+// temporary typedef pending implementation
+
+// #define NMERR_BASE 0x2000L
+// #define NM_OK 0L
+// #define NMERR_BAD_PARM (NMERR_BASE + 0x0001)
+// #define NMERR_TCP_WRITE (NMERR_BASE + 0x0002)
+// #define NMERR_TCP_READ (NMERR_BASE + 0x0003)
+// #define NMERR_PROTOCOL (NMERR_BASE + 0x0004)
+// #define NMERR_SERVER_REDIRECT (NMERR_BASE + 0x0005)
+// #define NMERR_CONFERENCE_NOT_FOUND (NMERR_BASE + 0x0006)
+// #define NMERR_CONFERENCE_NOT_INSTANTIATED (NMERR_BASE + 0x0007)
+// #define NMERR_FOLDER_EXISTS (NMERR_BASE + 0x0008)
+
+/* Errors that are returned from the server */
+#define NMERR_SERVER_BASE 0xD100L
+#define NMERR_ACCESS_DENIED (NMERR_SERVER_BASE + 0x0006)
+#define NMERR_NOT_SUPPORTED (NMERR_SERVER_BASE + 0x000A)
+#define NMERR_PASSWORD_EXPIRED (NMERR_SERVER_BASE + 0x000B)
+#define NMERR_PASSWORD_INVALID (NMERR_SERVER_BASE + 0x000C)
+#define NMERR_USER_NOT_FOUND (NMERR_SERVER_BASE + 0x000D)
+#define NMERR_ATTRIBUTE_NOT_FOUND (NMERR_SERVER_BASE + 0x000E)
+#define NMERR_USER_DISABLED (NMERR_SERVER_BASE + 0x0010)
+#define NMERR_DIRECTORY_FAILURE (NMERR_SERVER_BASE + 0x0011)
+#define NMERR_HOST_NOT_FOUND (NMERR_SERVER_BASE + 0x0019)
+#define NMERR_ADMIN_LOCKED (NMERR_SERVER_BASE + 0x001C)
+#define NMERR_DUPLICATE_PARTICIPANT (NMERR_SERVER_BASE + 0x001F)
+#define NMERR_SERVER_BUSY (NMERR_SERVER_BASE + 0x0023)
+#define NMERR_OBJECT_NOT_FOUND (NMERR_SERVER_BASE + 0x0024)
+#define NMERR_DIRECTORY_UPDATE (NMERR_SERVER_BASE + 0x0025)
+#define NMERR_DUPLICATE_FOLDER (NMERR_SERVER_BASE + 0x0026)
+#define NMERR_DUPLICATE_CONTACT (NMERR_SERVER_BASE + 0x0027)
+#define NMERR_USER_NOT_ALLOWED (NMERR_SERVER_BASE + 0x0028)
+#define NMERR_TOO_MANY_CONTACTS (NMERR_SERVER_BASE + 0x0029)
+#define NMERR_CONFERENCE_NOT_FOUND_2 (NMERR_SERVER_BASE + 0x002B)
+#define NMERR_TOO_MANY_FOLDERS (NMERR_SERVER_BASE + 0x002C)
+#define NMERR_SERVER_PROTOCOL (NMERR_SERVER_BASE + 0x0030)
+#define NMERR_CONVERSATION_INVITE (NMERR_SERVER_BASE + 0x0035)
+#define NMERR_USER_BLOCKED (NMERR_SERVER_BASE + 0x0039)
+#define NMERR_MASTER_ARCHIVE_MISSING (NMERR_SERVER_BASE + 0x003A)
+#define NMERR_PASSWORD_EXPIRED_2 (NMERR_SERVER_BASE + 0x0042)
+#define NMERR_CREDENTIALS_MISSING (NMERR_SERVER_BASE + 0x0046)
+#define NMERR_AUTHENTICATION_FAILED (NMERR_SERVER_BASE + 0x0049)
+#define NMERR_EVAL_CONNECTION_LIMIT (NMERR_SERVER_BASE + 0x004A)
+
+/* Error codes that are new in GW7 */
+#define MSGPRES_ERR_UNSUPPORTED_CLIENT_VERSION (NMERR_SERVER_BASE + 0x004B) // This version of the client is not supported.
+#define MSGPRES_ERR_DUPLICATE_CHAT (NMERR_SERVER_BASE + 0x0051) // A duplicate chat was found.
+#define MSGPRES_ERR_CHAT_NOT_FOUND (NMERR_SERVER_BASE + 0x0052) // The chat was not found.
+#define MSGPRES_ERR_INVALID_NAME (NMERR_SERVER_BASE + 0x0053) // The chat name is not valid.
+#define MSGPRES_ERR_CHAT_ACTIVE (NMERR_SERVER_BASE + 0x0054) // Cannot delete an active chat.
+#define MSGPRES_ERR_INSUF_CONV_RIGHTS (NMERR_SERVER_BASE + 0x0055) // Insufficient conversation rights to perform an action.
+#define MSGPRES_ERR_CHAT_BUSY (NMERR_SERVER_BASE + 0x0056) // Chat is busy; try again.
+#define MSGPRES_ERR_REQUEST_TOO_SOON (NMERR_SERVER_BASE + 0x0057) // Tried a request too soon after another one; try again.
+#define MSGPRES_INFO_NO_LIST_CHANGE (NMERR_SERVER_BASE + 0x0058) // The chat list has not changed since the last search.
+#define MSGPRES_ERR_CHAT_NOT_ACTIVE (NMERR_SERVER_BASE + 0x0059) // The chat subsystem is not active!
+#define MSGPRES_ERR_INVALID_CHAT_UPDATE (NMERR_SERVER_BASE + 0x005A) // The chat update request is invalid.
+#define MSGPRES_ERR_DIRECTORY_MISMATCH (NMERR_SERVER_BASE + 0x005B) // Write failed due to directory mismatch.
+#define MSGPRES_ERR_RECIPIENT_TOO_OLD (NMERR_SERVER_BASE + 0x005C) // The recipient's client version is too old.
+#define MSGPRES_ERR_CHAT_NO_LONGER_VALID (NMERR_SERVER_BASE + 0x005D) // The chat has been removed from the server.
+
+/* protocol version capabilities */
+#define CMSGPRES_GW_6_5 2
+#define CMSGPRES_SUPPORTS_NO_DETAILS_ON_LOGIN 3
+#define CMSGPRES_SUPPORTS_BROADCAST 4
+#define CMSGPRES_SUPPORTS_CHAT 5
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/gwfield.cpp b/kopete/protocols/groupwise/libgroupwise/gwfield.cpp
new file mode 100644
index 00000000..e0d3c5db
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwfield.cpp
@@ -0,0 +1,223 @@
+/*
+ gwfield.cpp - Fields used for Request/Response data in GroupWise
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcstring.h>
+
+#include "gwerror.h"
+
+#ifdef LIBGW_USE_KDEBUG
+ #include <kdebug.h>
+#endif
+
+#include "gwfield.h"
+#include <iostream>
+
+using namespace Field;
+using namespace std;
+
+/* === FieldList ==================================================== */
+FieldList::~FieldList()
+{
+}
+
+FieldListIterator FieldList::find( QCString tag )
+{
+ FieldListIterator it = begin();
+ return find( it, tag );
+}
+
+FieldListIterator FieldList::find( FieldListIterator &it, QCString tag )
+{
+ FieldListIterator theEnd = end();
+ //cout << "FieldList::find() looking for " << tag.data() << endl;
+ for ( ; it != theEnd; ++it )
+ {
+ //cout << " - on " << (*it)->tag().data() << endl;
+ if ( (*it)->tag() == tag )
+ break;
+ }
+ return it;
+}
+
+int FieldList::findIndex( QCString tag )
+{
+ FieldListIterator it = begin();
+ FieldListIterator theEnd = end();
+ int index = 0;
+ for ( ; it != theEnd; ++it, ++index )
+ if ( (*it)->tag() == tag )
+ return index;
+
+ return -1;
+}
+
+void FieldList::dump( bool recursive, int offset )
+{
+ const FieldListIterator myEnd = end();
+ if ( !offset )
+ kdDebug( GROUPWISE_DEBUG_LIBGW ) << k_funcinfo << ( recursive ? ", recursively" : ", non-recursive" ) << endl;
+ for( FieldListIterator it = begin(); it != myEnd; ++it )
+ {
+ QString s;
+ s.fill(' ', offset*2 );
+ s.append( (*it)->tag() );
+ SingleField * sf;
+ if ( ( sf = dynamic_cast<SingleField*>( *it ) ) )
+ {
+ s.append( " :" );
+ s.append( sf->value().toString() );
+ }
+ kdDebug( GROUPWISE_DEBUG_LIBGW ) << s << endl;
+ if ( recursive )
+ {
+ MultiField * mf;
+ if ( ( mf = dynamic_cast<MultiField*>( *it ) ) )
+ mf->fields().dump( recursive, offset+1 );
+ }
+ }
+}
+
+void FieldList::purge()
+{
+ Field::FieldListIterator it = begin();
+ Field::FieldListIterator theEnd = end();
+ int index = 0;
+ for ( ; it != theEnd; ++it, ++index )
+ delete *it;
+}
+
+// THIS IS AN ATTEMPT TO HIDE THE POLYMORPHISM INSIDE THE LIST
+// HOWEVER IT FAILS BECAUSE WE NEED BOTH THE ITERATOR AND THE CASTED Single|MultiField it points to
+
+SingleField * FieldList::findSingleField( QCString tag )
+{
+ FieldListIterator it = begin();
+ return findSingleField( it, tag );
+}
+
+SingleField * FieldList::findSingleField( FieldListIterator &it, QCString tag )
+{
+ FieldListIterator found = find( it, tag );
+ if ( found == end() )
+ return 0;
+ else
+ return dynamic_cast<SingleField *>( *found );
+}
+
+MultiField * FieldList::findMultiField( QCString tag )
+{
+ FieldListIterator it = begin();
+ return findMultiField( it, tag );
+}
+
+MultiField * FieldList::findMultiField( FieldListIterator &it, QCString tag )
+{
+ FieldListIterator found = find( it, tag );
+ if ( found == end() )
+ return 0;
+ else
+ return dynamic_cast<MultiField *>( *found );
+}
+
+
+/* === FieldBase ========================================================= */
+
+FieldBase::FieldBase( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type )
+: m_tag( tag ), m_method( method ), m_flags( flags ), m_type( type )
+{
+
+}
+
+QCString FieldBase::tag() const
+{
+ return m_tag;
+}
+
+Q_UINT8 FieldBase::method() const
+{
+ return m_method;
+}
+
+Q_UINT8 FieldBase::flags() const
+{
+ return m_flags;
+}
+
+Q_UINT8 FieldBase::type() const
+{
+ return m_type;
+}
+
+void FieldBase::setFlags( const Q_UINT8 flags )
+{
+ m_flags = flags;
+}
+
+/* === SingleField ========================================================= */
+
+SingleField::SingleField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, QVariant value )
+: FieldBase( tag, method, flags, type ), m_value( value )
+{
+}
+
+SingleField::SingleField( QCString tag, Q_UINT8 flags, Q_UINT8 type, QVariant value )
+: FieldBase( tag, NMFIELD_METHOD_VALID, flags, type ), m_value( value )
+{
+}
+
+SingleField::~SingleField()
+{
+}
+
+void SingleField::setValue( const QVariant v )
+{
+ m_value = v;
+}
+
+QVariant SingleField::value() const
+{
+ return m_value;
+}
+
+/* === MultiField ========================================================= */
+
+MultiField::MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, FieldList fields )
+: FieldBase( tag, method, flags, type ), m_fields( fields )
+{
+}
+
+MultiField::MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type )
+: FieldBase( tag, method, flags, type )
+{
+}
+
+MultiField::~MultiField()
+{
+ m_fields.purge();
+}
+
+FieldList MultiField::fields() const
+{
+ return m_fields;
+}
+
+void MultiField::setFields( FieldList fields )
+{
+ m_fields = fields;
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/gwfield.h b/kopete/protocols/groupwise/libgroupwise/gwfield.h
new file mode 100644
index 00000000..9362b5ad
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwfield.h
@@ -0,0 +1,275 @@
+/*
+ gwfield.h - Fields used for Request/Response data in GroupWise
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Testbed
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GWFIELD_H
+#define GWFIELD_H
+
+/* Field types */
+/* Comments: ^1 not used ^2 ignored ^3 apparently only used in _field_to_string for debug */
+/* Otherwise: widely used */
+#define NMFIELD_TYPE_INVALID 0
+/* ^1 */
+#define NMFIELD_TYPE_NUMBER 1
+/* ^1 */
+#define NMFIELD_TYPE_BINARY 2
+/* ^2? */
+#define NMFIELD_TYPE_BYTE 3
+/* ^3 */
+#define NMFIELD_TYPE_UBYTE 4
+/* ^3 */
+#define NMFIELD_TYPE_WORD 5
+/* ^3 */
+#define NMFIELD_TYPE_UWORD 6
+/* ^3 */
+#define NMFIELD_TYPE_DWORD 7
+/* ^3 */
+#define NMFIELD_TYPE_UDWORD 8
+/*WILLNOTE used in nm_send_login ( build ID ) and nm_send_message ( message type = 0 ) */
+#define NMFIELD_TYPE_ARRAY 9
+#define NMFIELD_TYPE_UTF8 10
+#define NMFIELD_TYPE_BOOL 11
+/* ^3 */
+#define NMFIELD_TYPE_MV 12
+#define NMFIELD_TYPE_DN 13
+
+/* Field methods */
+#define NMFIELD_METHOD_VALID 0
+#define NMFIELD_METHOD_IGNORE 1
+#define NMFIELD_METHOD_DELETE 2
+#define NMFIELD_METHOD_DELETE_ALL 3
+#define NMFIELD_METHOD_EQUAL 4
+#define NMFIELD_METHOD_ADD 5
+#define NMFIELD_METHOD_UPDATE 6
+#define NMFIELD_METHOD_GTE 10
+#define NMFIELD_METHOD_LTE 12
+#define NMFIELD_METHOD_NE 14
+#define NMFIELD_METHOD_EXIST 15
+#define NMFIELD_METHOD_NOTEXIST 16
+#define NMFIELD_METHOD_SEARCH 17
+#define NMFIELD_METHOD_MATCHBEGIN 19
+#define NMFIELD_METHOD_MATCHEND 20
+#define NMFIELD_METHOD_NOT_ARRAY 40
+#define NMFIELD_METHOD_OR_ARRAY 41
+#define NMFIELD_METHOD_AND_ARRAY 42
+
+/* Attribute Names (field tags) */
+#define NM_A_IP_ADDRESS "nnmIPAddress"
+#define NM_A_PORT "nnmPort"
+#define NM_A_FA_FOLDER "NM_A_FA_FOLDER"
+#define NM_A_FA_CONTACT "NM_A_FA_CONTACT"
+#define NM_A_FA_CONVERSATION "NM_A_FA_CONVERSATION"
+#define NM_A_FA_MESSAGE "NM_A_FA_MESSAGE"
+#define NM_A_FA_CONTACT_LIST "NM_A_FA_CONTACT_LIST"
+#define NM_A_FA_RESULTS "NM_A_FA_RESULTS"
+#define NM_A_FA_INFO_DISPLAY_ARRAY "NM_A_FA_INFO_DISPLAY_ARRAY"
+#define NM_A_FA_USER_DETAILS "NM_A_FA_USER_DETAILS"
+#define NM_A_SZ_OBJECT_ID "NM_A_SZ_OBJECT_ID"
+#define NM_A_SZ_PARENT_ID "NM_A_SZ_PARENT_ID"
+#define NM_A_SZ_SEQUENCE_NUMBER "NM_A_SZ_SEQUENCE_NUMBER"
+#define NM_A_SZ_TYPE "NM_A_SZ_TYPE"
+#define NM_A_SZ_STATUS "NM_A_SZ_STATUS"
+#define NM_A_SZ_STATUS_TEXT "NM_A_SZ_STATUS_TEXT"
+#define NM_A_SZ_DN "NM_A_SZ_DN"
+#define NM_A_SZ_DISPLAY_NAME "NM_A_SZ_DISPLAY_NAME"
+#define NM_A_SZ_USERID "NM_A_SZ_USERID"
+#define NM_A_SZ_CREDENTIALS "NM_A_SZ_CREDENTIALS"
+#define NM_A_SZ_MESSAGE_BODY "NM_A_SZ_MESSAGE_BODY"
+#define NM_A_SZ_MESSAGE_TEXT "NM_A_SZ_MESSAGE_TEXT"
+#define NM_A_UD_MESSAGE_TYPE "NM_A_UD_MESSAGE_TYPE"
+#define NM_A_FA_PARTICIPANTS "NM_A_FA_PARTICIPANTS"
+#define NM_A_FA_INVITES "NM_A_FA_INVITES"
+#define NM_A_FA_EVENT "NM_A_FA_EVENT"
+#define NM_A_UD_COUNT "NM_A_UD_COUNT"
+#define NM_A_UD_DATE "NM_A_UD_DATE"
+#define NM_A_UD_EVENT "NM_A_UD_EVENT"
+#define NM_A_B_NO_CONTACTS "NM_A_B_NO_CONTACTS"
+#define NM_A_B_NO_CUSTOMS "NM_A_B_NO_CUSTOMS"
+#define NM_A_B_NO_PRIVACY "NM_A_B_NO_PRIVACY"
+#define NM_A_B_ONLY_MODIFIED "NM_A_B_ONLY_MODIFIED"
+#define NM_A_UW_STATUS "NM_A_UW_STATUS"
+#define NM_A_UD_OBJECT_ID "NM_A_UD_OBJECT_ID"
+#define NM_A_SZ_TRANSACTION_ID "NM_A_SZ_TRANSACTION_ID"
+#define NM_A_SZ_RESULT_CODE "NM_A_SZ_RESULT_CODE"
+#define NM_A_UD_BUILD "NM_A_UD_BUILD"
+#define NM_A_SZ_AUTH_ATTRIBUTE "NM_A_SZ_AUTH_ATTRIBUTE"
+#define NM_A_UD_KEEPALIVE "NM_A_UD_KEEPALIVE"
+#define NM_A_SZ_USER_AGENT "NM_A_SZ_USER_AGENT"
+#define NM_A_BLOCKING "nnmBlocking"
+#define NM_A_BLOCKING_DENY_LIST "nnmBlockingDenyList"
+#define NM_A_BLOCKING_ALLOW_LIST "nnmBlockingAllowList"
+#define NM_A_SZ_BLOCKING_ALLOW_ITEM "NM_A_SZ_BLOCKING_ALLOW_ITEM"
+#define NM_A_SZ_BLOCKING_DENY_ITEM "NM_A_SZ_BLOCKING_DENY_ITEM"
+#define NM_A_LOCKED_ATTR_LIST "nnmLockedAttrList"
+#define NM_A_SZ_DEPARTMENT "OU"
+#define NM_A_SZ_TITLE "Title"
+// GW7
+#define NM_A_FA_CUSTOM_STATUSES "NM_A_FA_CUSTOM_STATUSES"
+#define NM_A_FA_STATUS "NM_A_FA_STATUS"
+#define NM_A_UD_QUERY_COUNT "NM_A_UD_QUERY_COUNT"
+#define NM_A_FA_CHAT "NM_A_FA_CHAT"
+#define NM_A_DISPLAY_NAME "nnmDisplayName"
+#define NM_A_CHAT_OWNER_DN "nnmChatOwnerDN"
+#define NM_A_UD_PARTICIPANTS "NM_A_UD_PARTICIPANTS"
+#define NM_A_DESCRIPTION "nnmDescription"
+#define NM_A_DISCLAIMER "nnmDisclaimer"
+#define NM_A_QUERY "nnmQuery"
+#define NM_A_ARCHIVE "nnmArchive"
+#define NM_A_MAX_USERS "nnmMaxUsers"
+#define NM_A_SZ_TOPIC "NM_A_SZ_TOPIC"
+#define NM_A_FA_CHAT_ACL "NM_A_FA_CHAT_ACL"
+#define NM_A_FA_CHAT_ACL_ENTRY "NM_A_FA_CHAT_ACL_ENTRY"
+#define NM_A_SZ_ACCESS_FLAGS "NM_A_SZ_ACCESS_FLAGS"
+#define NM_A_CHAT_CREATOR_DN "nnmCreatorDN"
+#define NM_A_CREATION_TIME "nnmCreationTime"
+#define NM_A_UD_CHAT_RIGHTS "NM_A_UD_CHAT_RIGHTS"
+
+#define NM_PROTOCOL_VERSION 5
+#define NM_FIELD_TRUE "1"
+#define NM_FIELD_FALSE "0"
+
+#define NMFIELD_MAX_STR_LENGTH 32768
+
+#include <qglobal.h>
+#include <qobject.h>
+#include <qvariant.h>
+#include <qvaluelist.h>
+
+/**
+ * Fields are typed units of information interchanged between the groupwise server and its clients.
+ * In this implementation Fields are assumed to have a straight data flow from a Task to a socket and vice versa,
+ * so the @ref Task::take() is responsible for deleting incoming Fields and the netcode is responsible for
+ * deleting outgoing Fields.
+ */
+
+namespace Field
+{
+ /**
+ * Abstract base class of all field types
+ */
+ class FieldBase
+ {
+ public:
+ FieldBase() {}
+ FieldBase( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type );
+ virtual ~FieldBase() {}
+ QCString tag() const;
+ Q_UINT8 method() const;
+ Q_UINT8 flags() const;
+ Q_UINT8 type() const;
+ void setFlags( const Q_UINT8 flags );
+ protected:
+ QCString m_tag;
+ Q_UINT8 m_method;
+ Q_UINT8 m_flags;
+ Q_UINT8 m_type; // doch needed
+ };
+
+ typedef QValueListIterator<FieldBase *> FieldListIterator;
+ typedef QValueListConstIterator<FieldBase *> FieldListConstIterator;
+ class SingleField;
+ class MultiField;
+
+ class FieldList : public QValueList<FieldBase *>
+ {
+ public:
+ /**
+ * Destructor - doesn't delete the fields because FieldLists are passed by value
+ */
+ virtual ~FieldList();
+ /**
+ * Locate the first occurrence of a given field in the list. Same semantics as QValueList::find().
+ * @param tag The tag name of the field to search for.
+ * @return An iterator pointing to the first occurrence found, or end() if none was found.
+ */
+ FieldListIterator find( QCString tag );
+ /**
+ * Locate the first occurrence of a given field in the list, starting at the supplied iterator
+ * @param tag The tag name of the field to search for.
+ * @param it An iterator within the list, to start searching from.
+ * @return An iterator pointing to the first occurrence found, or end() if none was found.
+ */
+ FieldListIterator find( FieldListIterator &it, QCString tag );
+ /**
+ * Get the index of the first occurrence of tag, or -1 if not found
+ */
+ int findIndex( QCString tag );
+ /**
+ * Debug function, dumps to stdout
+ */
+ void dump( bool recursive = false, int offset = 0 );
+ /**
+ * Delete the contents of the list
+ */
+ void purge();
+ /**
+ * Utility functions for finding the first instance of a tag
+ * @return 0 if no field of the right tag and type was found.
+ */
+ SingleField * findSingleField( QCString tag );
+ MultiField * findMultiField( QCString tag );
+ protected:
+ SingleField * findSingleField( FieldListIterator &it, QCString tag );
+ MultiField * findMultiField( FieldListIterator &it, QCString tag );
+
+ };
+
+ /**
+ * This class is responsible for storing all Groupwise single value field types, eg
+ * NMFIELD_TYPE_INVALID, NMFIELD_TYPE_NUMBER, NMFIELD_TYPE_BINARY, NMFIELD_TYPE_BYTE
+ * NMFIELD_TYPE_UBYTE, NMFIELD_TYPE_DWORD, NMFIELD_TYPE_UDWORD, NMFIELD_TYPE_UTF8, NMFIELD_TYPE_BOOL
+ * NMFIELD_TYPE_DN
+ */
+ class SingleField : public FieldBase
+ {
+ public:
+ /**
+ * Single field constructor
+ */
+ SingleField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, QVariant value );
+ /**
+ * Convenience constructor for NMFIELD_METHOD_VALID fields
+ */
+ SingleField( QCString tag, Q_UINT8 flags, Q_UINT8 type, QVariant value );
+ ~SingleField();
+ void setValue( const QVariant v );
+ QVariant value() const;
+ private:
+ QVariant m_value;
+ };
+
+ /**
+ * This class is responsible for storing multi-value GroupWise field types, eg
+ * NMFIELD_TYPE_ARRAY, NMFIELD_TYPE_MV
+ */
+ class MultiField : public FieldBase
+ {
+ public:
+ MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type );
+ MultiField( QCString tag, Q_UINT8 method, Q_UINT8 flags, Q_UINT8 type, FieldList fields );
+ ~MultiField();
+ FieldList fields() const;
+ void setFields( FieldList );
+ private:
+ FieldList m_fields; // nb implicitly shared, copy-on-write - is there a case where this is bad?
+ };
+
+}
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp b/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp
new file mode 100644
index 00000000..4ea25779
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/gwglobal.cpp
@@ -0,0 +1,39 @@
+/*
+ gwglobal.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwerror.h"
+
+namespace GroupWise
+{
+ ConferenceGuid::ConferenceGuid() {}
+ ConferenceGuid::ConferenceGuid( const QString & string ) : QString( string ) {}
+
+ ConferenceGuid::~ConferenceGuid() {}
+
+ bool operator==( const ConferenceGuid & g1, const ConferenceGuid & g2 )
+ {
+ return g1.left( CONF_GUID_END ) == g2.left( CONF_GUID_END );
+ }
+ bool operator==( const QString & s, const ConferenceGuid & g )
+ {
+ return s.left( CONF_GUID_END ) == g.left( CONF_GUID_END );
+ }
+ bool operator==( const ConferenceGuid & g, const QString & s )
+ {
+ return s.left( CONF_GUID_END ) == g.left( CONF_GUID_END );
+ }
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp
new file mode 100644
index 00000000..30627a81
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.cpp
@@ -0,0 +1,112 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include "gwerror.h"
+
+#include "gwfield.h"
+#include "inputprotocolbase.h"
+
+InputProtocolBase::InputProtocolBase(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+}
+
+
+InputProtocolBase::~InputProtocolBase()
+{
+}
+
+void InputProtocolBase::debug( const QString &str )
+{
+#ifdef LIBGW_USE_KDEBUG
+ kdDebug( 14191 ) << "debug: " << str << endl;
+#else
+ qDebug( "GW RAW PROTO: %s\n", str.ascii() );
+#endif
+}
+
+uint InputProtocolBase::state() const
+{
+ return m_state;
+}
+
+bool InputProtocolBase::readString( QString &message )
+{
+ uint len;
+ QCString rawData;
+ if ( !safeReadBytes( rawData, len ) )
+ return false;
+ message = QString::fromUtf8( rawData.data(), len - 1 );
+ return true;
+}
+
+
+bool InputProtocolBase::okToProceed()
+{
+ if ( m_din.device() )
+ {
+ if ( m_din.atEnd() )
+ {
+ m_state = NeedMore;
+ debug( "InputProtocol::okToProceed() - Server message ended prematurely!" );
+ }
+ else
+ return true;
+ }
+ return false;
+}
+
+bool InputProtocolBase::safeReadBytes( QCString & data, uint & len )
+{
+ // read the length of the bytes
+ Q_UINT32 val;
+ if ( !okToProceed() )
+ return false;
+ m_din >> val;
+ m_bytes += sizeof( Q_UINT32 );
+ if ( val > NMFIELD_MAX_STR_LENGTH )
+ return false;
+ //qDebug( "EventProtocol::safeReadBytes() - expecting %i bytes", val );
+ QCString temp( val );
+ if ( val != 0 )
+ {
+ if ( !okToProceed() )
+ return false;
+ // if the server splits packets here we are in trouble,
+ // as there is no way to see how much data was actually read
+ m_din.readRawBytes( temp.data(), val );
+ // the rest of the string will be filled with FF,
+ // so look for that in the last position instead of \0
+ // this caused a crash - guessing that temp.length() is set to the number of bytes actually read...
+ // if ( (Q_UINT8)( * ( temp.data() + ( temp.length() - 1 ) ) ) == 0xFF )
+ if ( temp.length() < ( val - 1 ) )
+ {
+ debug( QString( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %1 bytes out of %2" ).arg( temp.length() ).arg( val ) );
+ m_state = NeedMore;
+ return false;
+ }
+ }
+ data = temp;
+ len = val;
+ m_bytes += val;
+ return true;
+}
+
+#include "inputprotocolbase.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h
new file mode 100644
index 00000000..efd2979f
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/inputprotocolbase.h
@@ -0,0 +1,78 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef INPUTPROTOCOLBASE_H
+#define INPUTPROTOCOLBASE_H
+
+#include <qobject.h>
+
+class Transfer;
+/**
+Defines a basic interface for protocols dealing with input from the GroupWise server.
+
+@author Kopete Developers
+*/
+class InputProtocolBase : public QObject
+{
+Q_OBJECT
+public:
+ enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError };
+ InputProtocolBase(QObject *parent = 0, const char *name = 0);
+ ~InputProtocolBase();
+
+ /**
+ * Debug output
+ */
+ static void debug(const QString &str);
+
+ /**
+ * Returns a value describing the state of the object.
+ * If the object is given data to parse that does not begin with a recognised event code,
+ * it will become OutOfSync, to indicate that the input data probably contains leftover data not processed during a previous parse.
+ */
+ uint state() const;
+ /**
+ * Attempt to parse the supplied data into a Transfer object
+ * @param bytes this will be set to the number of bytes that were successfully parsed. It is no indication of the success of the whole procedure
+ * @return On success, a Transfer object that the caller is responsible for deleting. It will be either an EventTransfer or a Response, delete as appropriate. On failure, returns 0.
+ */
+ virtual Transfer * parse( const QByteArray &, uint & bytes ) = 0 ;
+protected:
+ /**
+ * Reads an arbitrary string
+ * updates the bytes parsed counter
+ */
+ bool readString( QString &message );
+ /**
+ * Check that there is data to read, and set the protocol's state if there isn't any.
+ */
+ bool okToProceed();
+ /**
+ * read a Q_UINT32 giving the number of following bytes, then a string of that length
+ * updates the bytes parsed counter
+ * @return false if the string was broken or there was no data available at all
+ */
+ bool safeReadBytes( QCString & data, uint & len );
+
+protected:
+ uint m_state;
+ uint m_bytes;
+ QDataStream m_din;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp b/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp
new file mode 100644
index 00000000..3d42207b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/privacymanager.cpp
@@ -0,0 +1,251 @@
+/*
+ Kopete Groupwise Protocol
+ privacymanager.cpp - stores the user's privacy information and maintains it on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "tasks/privacyitemtask.h"
+#include "userdetailsmanager.h"
+
+#include "privacymanager.h"
+
+PrivacyManager::PrivacyManager( Client * client, const char *name)
+ : QObject(client, name), m_client( client )
+{
+}
+
+PrivacyManager::~PrivacyManager()
+{
+}
+
+bool PrivacyManager::defaultAllow()
+{
+ return !m_defaultDeny;
+}
+
+bool PrivacyManager::defaultDeny()
+{
+ return m_defaultDeny;
+}
+
+QStringList PrivacyManager::allowList()
+{
+ return m_allowList;
+}
+
+QStringList PrivacyManager::denyList()
+{
+ return m_denyList;
+}
+
+bool PrivacyManager::isPrivacyLocked()
+{
+ return m_locked;
+}
+
+bool PrivacyManager::isBlocked( const QString & dn )
+{
+ if ( m_defaultDeny )
+ return !m_allowList.contains( dn );
+ else
+ return m_denyList.contains( dn );
+}
+
+void PrivacyManager::setAllow( const QString & dn )
+{
+ if ( m_defaultDeny )
+ {
+ if ( !m_allowList.contains( dn ) )
+ addAllow( dn );
+ }
+ else
+ {
+ if ( m_denyList.contains( dn ) )
+ removeDeny( dn );
+ }
+}
+
+void PrivacyManager::setDeny( const QString & dn )
+{
+ if ( m_defaultDeny )
+ {
+ if ( m_allowList.contains( dn ) )
+ removeAllow( dn );
+ }
+ else
+ {
+ if ( !m_denyList.contains( dn ) )
+ addDeny( dn );
+ }
+}
+
+
+void PrivacyManager::setDefaultAllow( bool allow )
+{
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->defaultPolicy( !allow );
+ connect( pit, SIGNAL( finished() ), SLOT( slotDefaultPolicyChanged() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::setDefaultDeny( bool deny )
+{
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->defaultPolicy( deny);
+ connect( pit, SIGNAL( finished() ), SLOT( slotDefaultPolicyChanged() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::addAllow( const QString & dn )
+{
+ // start off a CreatePrivacyItemTask
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->allow( dn );
+ connect( pit, SIGNAL( finished() ), SLOT( slotAllowAdded() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::addDeny( const QString & dn )
+{
+ // start off a CreatePrivacyItemTask
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->deny( dn );
+ connect( pit, SIGNAL( finished() ), SLOT( slotDenyAdded() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::removeAllow( const QString & dn )
+{
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->removeAllow( dn );
+ connect( pit, SIGNAL( finished() ), SLOT( slotAllowRemoved() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::removeDeny( const QString & dn )
+{
+ // start off a CreatePrivacyItemTask
+ PrivacyItemTask * pit = new PrivacyItemTask( m_client->rootTask() );
+ pit->removeDeny( dn );
+ connect( pit, SIGNAL( finished() ), SLOT( slotDenyRemoved() ) );
+ pit->go( true );
+}
+
+void PrivacyManager::setPrivacy( bool defaultIsDeny, const QStringList & allowList, const QStringList & denyList )
+{
+ if ( defaultIsDeny != m_defaultDeny )
+ setDefaultDeny( defaultIsDeny );
+ // find the DNs no longer in the allow list
+ QStringList allowsToRemove = difference( m_allowList, allowList );
+ // find the DNs no longer in the deny list
+ QStringList denysToRemove = difference( m_denyList, denyList );
+ // find the DNs new in the allow list
+ QStringList allowsToAdd = difference( allowList, m_allowList );
+ // find the DNs new in the deny list
+ QStringList denysToAdd = difference( denyList, m_denyList );
+
+ QStringList::ConstIterator end = allowsToRemove.end();
+ for ( QStringList::ConstIterator it = allowsToRemove.begin(); it != end; ++it )
+ removeAllow( *it );
+ end = denysToRemove.end();
+ for ( QStringList::ConstIterator it = denysToRemove.begin(); it != end; ++it )
+ removeDeny( *it );
+ end = allowsToAdd.end();
+ for ( QStringList::ConstIterator it = allowsToAdd.begin(); it != end; ++it )
+ addAllow( *it );
+ end = denysToAdd.end();
+ for ( QStringList::ConstIterator it = denysToAdd.begin(); it != end; ++it )
+ addDeny( *it );
+}
+
+void PrivacyManager::slotGotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList )
+{
+ m_locked = locked;
+ m_defaultDeny = defaultDeny;
+ m_allowList = allowList;
+ m_denyList = denyList;
+}
+
+void PrivacyManager::getDetailsForPrivacyLists()
+{
+ if ( !m_allowList.isEmpty() )
+ {
+ m_client->userDetailsManager()->requestDetails( m_allowList );
+ }
+ if ( !m_denyList.isEmpty() )
+ m_client->userDetailsManager()->requestDetails( m_denyList );
+}
+
+void PrivacyManager::slotDefaultPolicyChanged()
+{
+ PrivacyItemTask * pit = ( PrivacyItemTask * )sender();
+ if ( pit->success() )
+ m_defaultDeny = pit->defaultDeny();
+}
+
+void PrivacyManager::slotAllowAdded()
+{
+ PrivacyItemTask * pit = ( PrivacyItemTask * )sender();
+ if ( pit->success() )
+ {
+ m_allowList.append( pit->dn() );
+ emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) );
+ }
+}
+
+void PrivacyManager::slotDenyAdded()
+{
+ PrivacyItemTask * pit = ( PrivacyItemTask * )sender();
+ if ( pit->success() )
+ {
+ m_denyList.append( pit->dn() );
+ emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) );
+ }
+}
+
+void PrivacyManager::slotAllowRemoved()
+{
+ PrivacyItemTask * pit = ( PrivacyItemTask * )sender();
+ if ( pit->success() )
+ {
+ m_allowList.remove( pit->dn() );
+ emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) );
+ }
+}
+
+void PrivacyManager::slotDenyRemoved()
+{
+ PrivacyItemTask * pit = ( PrivacyItemTask * )sender();
+ if ( pit->success() )
+ {
+ m_denyList.remove( pit->dn() );
+ emit privacyChanged( pit->dn(), isBlocked( pit->dn() ) );
+ }
+}
+
+QStringList PrivacyManager::difference( const QStringList & lhs, const QStringList & rhs )
+{
+ QStringList diff;
+ const QStringList::ConstIterator lhsEnd = lhs.end();
+ const QStringList::ConstIterator rhsEnd = rhs.end();
+ for ( QStringList::ConstIterator lhsIt = lhs.begin(); lhsIt != lhsEnd; ++lhsIt )
+ {
+ if ( rhs.find( *lhsIt ) == rhsEnd )
+ diff.append( *lhsIt );
+ }
+ return diff;
+}
+#include "privacymanager.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/privacymanager.h b/kopete/protocols/groupwise/libgroupwise/privacymanager.h
new file mode 100644
index 00000000..102c2b0a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/privacymanager.h
@@ -0,0 +1,88 @@
+/*
+ Kopete Groupwise Protocol
+ privacymanager.cpp - stores the user's privacy information and maintains it on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef PRIVACYMANAGER_H
+#define PRIVACYMANAGER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class Client;
+
+/**
+Keeps a record of the server side privacy allow and deny lists, default policy and whether the user is allowed to change privacy settings
+
+@author SUSE AG
+*/
+class PrivacyManager : public QObject
+{
+Q_OBJECT
+public:
+ PrivacyManager( Client * client, const char *name = 0);
+ ~PrivacyManager();
+ // accessors
+ bool isBlocked( const QString & dn );
+ QStringList allowList();
+ QStringList denyList();
+ bool isPrivacyLocked();
+ bool defaultDeny();
+ bool defaultAllow();
+ // mutators
+ void setDefaultAllow( bool allow );
+ void setDefaultDeny( bool deny );
+ void setAllow( const QString & dn );
+ void setDeny( const QString & dn );
+ void getDetailsForPrivacyLists();
+ // change everything at once
+ void setPrivacy( bool defaultIsDeny, const QStringList & allowList, const QStringList & denyList );
+
+signals:
+ void privacyChanged( const QString &dn, bool allowed );
+public slots:
+ /**
+ * Used to initialise the privacy manager using the server side privacy list
+ */
+ void slotGotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList );
+protected:
+ void addAllow( const QString & dn );
+ void addDeny( const QString & dn );
+ void removeAllow( const QString & dn );
+ void removeDeny( const QString & dn );
+ /**
+ * A set difference function
+ * @param lhs The set of strings to be subtracted from
+ * @param rhs The set of string to subtract
+ * @return The difference between the two sets
+ */
+ QStringList difference( const QStringList & lhs, const QStringList & rhs );
+protected slots:
+ // Receive the results of Tasks manipulating the privacy lists
+ void slotDefaultPolicyChanged();
+ void slotAllowAdded();
+ void slotDenyAdded();
+ void slotAllowRemoved();
+ void slotDenyRemoved();
+private:
+ Client * m_client;
+ bool m_locked;
+ bool m_defaultDeny;
+ QStringList m_allowList;
+ QStringList m_denyList;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/COPYING b/kopete/protocols/groupwise/libgroupwise/qca/COPYING
new file mode 100644
index 00000000..b1e3f5a2
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/INSTALL b/kopete/protocols/groupwise/libgroupwise/qca/INSTALL
new file mode 100644
index 00000000..8dd34099
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/INSTALL
@@ -0,0 +1,12 @@
+Installing QCA
+--------------
+
+Installation should be straightforward:
+
+ ./configure
+ make
+ make install
+
+NOTE: You may also need to run '/sbin/ldconfig' or a similar tool to
+ get the new library files recognized by the system. If you are
+ using Linux, just run it for good measure.
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am b/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am
new file mode 100644
index 00000000..af437a64
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/README b/kopete/protocols/groupwise/libgroupwise/qca/README
new file mode 100644
index 00000000..0641713a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/README
@@ -0,0 +1,29 @@
+Qt Cryptographic Architecture
+-----------------------------
+Version: API v1.0, Plugin v1
+Author: Justin Karneges <justin@affinix.com>
+Date: September 10th 2003
+
+This library provides an easy API for the following features:
+
+ SSL/TLS
+ X509
+ SASL
+ RSA
+ Hashing (SHA1, MD5)
+ Ciphers (BlowFish, 3DES, AES)
+
+Functionality is supplied via plugins. This is useful for avoiding
+dependence on a particular crypto library and makes upgrading easier,
+as there is no need to recompile your application when adding or
+upgrading a crypto plugin. Also, by pushing crypto functionality into
+plugins, your application is free of legal issues, such as export
+regulation.
+
+And of course, you get a very simple crypto API for Qt, where you can
+do things like:
+
+ QString hash = QCA::SHA1::hashToString(blockOfData);
+
+Have fun!
+
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/TODO b/kopete/protocols/groupwise/libgroupwise/qca/TODO
new file mode 100644
index 00000000..bc8247e0
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/TODO
@@ -0,0 +1,6 @@
+* plugins: thread safety ?
+
+* dsa
+* diffie-hellman
+* entropy
+
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am b/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am
new file mode 100644
index 00000000..b7ae1bb4
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/src/Makefile.am
@@ -0,0 +1,8 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libqca.la
+INCLUDES = $(all_includes)
+
+libqca_la_SOURCES = \
+ qca.cpp
+libqca_la_LIBADD = -lqt-mt
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp
new file mode 100644
index 00000000..9edb0fb3
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.cpp
@@ -0,0 +1,1486 @@
+/*
+ * qca.cpp - Qt Cryptographic Architecture
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include"qca.h"
+
+#include<qptrlist.h>
+#include<qdir.h>
+#include<qfileinfo.h>
+#include<qstringlist.h>
+#include<qlibrary.h>
+#include<qtimer.h>
+#include<qhostaddress.h>
+#include<qapplication.h>
+#include<qguardedptr.h>
+#include<stdlib.h>
+#include"qcaprovider.h"
+
+#if defined(Q_OS_WIN32)
+#define PLUGIN_EXT "dll"
+#elif defined(Q_OS_MAC)
+#define PLUGIN_EXT "dylib"
+#else
+#define PLUGIN_EXT "so"
+#endif
+
+using namespace QCA;
+
+class ProviderItem
+{
+public:
+ QCAProvider *p;
+ QString fname;
+
+ static ProviderItem *load(const QString &fname)
+ {
+ QLibrary *lib = new QLibrary(fname);
+ if(!lib->load()) {
+ delete lib;
+ return 0;
+ }
+ void *s = lib->resolve("createProvider");
+ if(!s) {
+ delete lib;
+ return 0;
+ }
+ QCAProvider *(*createProvider)() = (QCAProvider *(*)())s;
+ QCAProvider *p = createProvider();
+ if(!p) {
+ delete lib;
+ return 0;
+ }
+ ProviderItem *i = new ProviderItem(lib, p);
+ i->fname = fname;
+ return i;
+ }
+
+ static ProviderItem *fromClass(QCAProvider *p)
+ {
+ ProviderItem *i = new ProviderItem(0, p);
+ return i;
+ }
+
+ ~ProviderItem()
+ {
+ delete p;
+ delete lib;
+ }
+
+ void ensureInit()
+ {
+ if(init_done)
+ return;
+ init_done = true;
+ p->init();
+ }
+
+private:
+ QLibrary *lib;
+ bool init_done;
+
+ ProviderItem(QLibrary *_lib, QCAProvider *_p)
+ {
+ lib = _lib;
+ p = _p;
+ init_done = false;
+ }
+};
+
+static QPtrList<ProviderItem> providerList;
+static bool qca_init = false;
+
+static bool plugin_have(const QString &fname)
+{
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it) {
+ if(i->fname == fname)
+ return true;
+ }
+ return false;
+}
+
+static void plugin_scan()
+{
+ QStringList dirs = QApplication::libraryPaths();
+ for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+ QDir libpath(*it);
+ QDir dir(libpath.filePath("crypto"));
+ if(!dir.exists())
+ continue;
+
+ QStringList list = dir.entryList();
+ for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ QFileInfo fi(dir.filePath(*it));
+ if(fi.isDir())
+ continue;
+ if(fi.extension() != PLUGIN_EXT)
+ continue;
+ QString fname = fi.filePath();
+
+ // don't load the same plugin again!
+ if(plugin_have(fname))
+ continue;
+ //printf("f=[%s]\n", fname.latin1());
+
+ ProviderItem *i = ProviderItem::load(fname);
+ if(!i)
+ continue;
+ if(i->p->qcaVersion() != QCA_PLUGIN_VERSION) {
+ delete i;
+ continue;
+ }
+
+ providerList.append(i);
+ }
+ }
+}
+
+static void plugin_addClass(QCAProvider *p)
+{
+ ProviderItem *i = ProviderItem::fromClass(p);
+ providerList.prepend(i);
+}
+
+static void plugin_unloadall()
+{
+ providerList.clear();
+}
+
+static int plugin_caps()
+{
+ int caps = 0;
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it)
+ caps |= i->p->capabilities();
+ return caps;
+}
+
+QString QCA::arrayToHex(const QByteArray &a)
+{
+ QString out;
+ for(int n = 0; n < (int)a.size(); ++n) {
+ QString str;
+ str.sprintf("%02x", (uchar)a[n]);
+ out.append(str);
+ }
+ return out;
+}
+
+QByteArray QCA::hexToArray(const QString &str)
+{
+ QByteArray out(str.length() / 2);
+ int at = 0;
+ for(int n = 0; n + 1 < (int)str.length(); n += 2) {
+ uchar a = str[n];
+ uchar b = str[n+1];
+ uchar c = ((a & 0x0f) << 4) + (b & 0x0f);
+ out[at++] = c;
+ }
+ return out;
+}
+
+void QCA::init()
+{
+ if(qca_init)
+ return;
+ qca_init = true;
+ providerList.setAutoDelete(true);
+}
+
+bool QCA::isSupported(int capabilities)
+{
+ init();
+
+ int caps = plugin_caps();
+ if(caps & capabilities)
+ return true;
+
+ // ok, try scanning for new stuff
+ plugin_scan();
+ caps = plugin_caps();
+ if(caps & capabilities)
+ return true;
+
+ return false;
+}
+
+void QCA::insertProvider(QCAProvider *p)
+{
+ plugin_addClass(p);
+}
+
+void QCA::unloadAllPlugins()
+{
+ plugin_unloadall();
+}
+
+static void *getContext(int cap)
+{
+ init();
+
+ // this call will also trip a scan for new plugins if needed
+ if(!QCA::isSupported(cap))
+ return 0;
+
+ QPtrListIterator<ProviderItem> it(providerList);
+ for(ProviderItem *i; (i = it.current()); ++it) {
+ if(i->p->capabilities() & cap) {
+ i->ensureInit();
+ return i->p->context(cap);
+ }
+ }
+ return 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Hash
+//----------------------------------------------------------------------------
+class Hash::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ c->reset();
+ }
+
+ QCA_HashContext *c;
+};
+
+Hash::Hash(QCA_HashContext *c)
+{
+ d = new Private;
+ d->c = c;
+}
+
+Hash::Hash(const Hash &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Hash & Hash::operator=(const Hash &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ return *this;
+}
+
+Hash::~Hash()
+{
+ delete d;
+}
+
+void Hash::clear()
+{
+ d->reset();
+}
+
+void Hash::update(const QByteArray &a)
+{
+ d->c->update(a.data(), a.size());
+}
+
+QByteArray Hash::final()
+{
+ QByteArray buf;
+ d->c->final(&buf);
+ return buf;
+}
+
+
+//----------------------------------------------------------------------------
+// Cipher
+//----------------------------------------------------------------------------
+class Cipher::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ dir = Encrypt;
+ key.resize(0);
+ iv.resize(0);
+ err = false;
+ }
+
+ QCA_CipherContext *c;
+ int dir;
+ int mode;
+ QByteArray key, iv;
+ bool err;
+};
+
+Cipher::Cipher(QCA_CipherContext *c, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+{
+ d = new Private;
+ d->c = c;
+ reset(dir, mode, key, iv, pad);
+}
+
+Cipher::Cipher(const Cipher &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Cipher & Cipher::operator=(const Cipher &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ d->dir = from.d->dir;
+ d->mode = from.d->mode;
+ d->key = from.d->key.copy();
+ d->iv = from.d->iv.copy();
+ d->err = from.d->err;
+ return *this;
+}
+
+Cipher::~Cipher()
+{
+ delete d;
+}
+
+QByteArray Cipher::dyn_generateKey(int size) const
+{
+ QByteArray buf;
+ if(size != -1)
+ buf.resize(size);
+ else
+ buf.resize(d->c->keySize());
+ if(!d->c->generateKey(buf.data(), size))
+ return QByteArray();
+ return buf;
+}
+
+QByteArray Cipher::dyn_generateIV() const
+{
+ QByteArray buf(d->c->blockSize());
+ if(!d->c->generateIV(buf.data()))
+ return QByteArray();
+ return buf;
+}
+
+void Cipher::reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+{
+ d->reset();
+
+ d->dir = dir;
+ d->mode = mode;
+ d->key = key.copy();
+ d->iv = iv.copy();
+ if(!d->c->setup(d->dir, d->mode, d->key.isEmpty() ? 0: d->key.data(), d->key.size(), d->iv.isEmpty() ? 0 : d->iv.data(), pad)) {
+ d->err = true;
+ return;
+ }
+}
+
+bool Cipher::update(const QByteArray &a)
+{
+ if(d->err)
+ return false;
+
+ if(!a.isEmpty()) {
+ if(!d->c->update(a.data(), a.size())) {
+ d->err = true;
+ return false;
+ }
+ }
+ return true;
+}
+
+QByteArray Cipher::final(bool *ok)
+{
+ if(ok)
+ *ok = false;
+ if(d->err)
+ return QByteArray();
+
+ QByteArray out;
+ if(!d->c->final(&out)) {
+ d->err = true;
+ return QByteArray();
+ }
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+
+//----------------------------------------------------------------------------
+// SHA1
+//----------------------------------------------------------------------------
+SHA1::SHA1()
+:Hash((QCA_HashContext *)getContext(CAP_SHA1))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// SHA256
+//----------------------------------------------------------------------------
+SHA256::SHA256()
+:Hash((QCA_HashContext *)getContext(CAP_SHA256))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// MD5
+//----------------------------------------------------------------------------
+MD5::MD5()
+:Hash((QCA_HashContext *)getContext(CAP_MD5))
+{
+}
+
+
+//----------------------------------------------------------------------------
+// BlowFish
+//----------------------------------------------------------------------------
+BlowFish::BlowFish(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_BlowFish), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// TripleDES
+//----------------------------------------------------------------------------
+TripleDES::TripleDES(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_TripleDES), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// AES128
+//----------------------------------------------------------------------------
+AES128::AES128(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_AES128), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// AES256
+//----------------------------------------------------------------------------
+AES256::AES256(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad)
+:Cipher((QCA_CipherContext *)getContext(CAP_AES256), dir, mode, key, iv, pad)
+{
+}
+
+
+//----------------------------------------------------------------------------
+// RSAKey
+//----------------------------------------------------------------------------
+class RSAKey::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ QCA_RSAKeyContext *c;
+};
+
+RSAKey::RSAKey()
+{
+ d = new Private;
+ d->c = (QCA_RSAKeyContext *)getContext(CAP_RSA);
+}
+
+RSAKey::RSAKey(const RSAKey &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+RSAKey & RSAKey::operator=(const RSAKey &from)
+{
+ delete d->c;
+ d->c = from.d->c->clone();
+ return *this;
+}
+
+RSAKey::~RSAKey()
+{
+ delete d;
+}
+
+bool RSAKey::isNull() const
+{
+ return d->c->isNull();
+}
+
+bool RSAKey::havePublic() const
+{
+ return d->c->havePublic();
+}
+
+bool RSAKey::havePrivate() const
+{
+ return d->c->havePrivate();
+}
+
+QByteArray RSAKey::toDER(bool publicOnly) const
+{
+ QByteArray out;
+ if(!d->c->toDER(&out, publicOnly))
+ return QByteArray();
+ return out;
+}
+
+bool RSAKey::fromDER(const QByteArray &a)
+{
+ return d->c->createFromDER(a.data(), a.size());
+}
+
+QString RSAKey::toPEM(bool publicOnly) const
+{
+ QByteArray out;
+ if(!d->c->toPEM(&out, publicOnly))
+ return QByteArray();
+
+ QCString cs;
+ cs.resize(out.size()+1);
+ memcpy(cs.data(), out.data(), out.size());
+ return QString::fromLatin1(cs);
+}
+
+bool RSAKey::fromPEM(const QString &str)
+{
+ QCString cs = str.latin1();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return d->c->createFromPEM(a.data(), a.size());
+}
+
+bool RSAKey::fromNative(void *p)
+{
+ return d->c->createFromNative(p);
+}
+
+bool RSAKey::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ QByteArray out;
+ if(!d->c->encrypt(a, &out, oaep))
+ return false;
+ *b = out;
+ return true;
+}
+
+bool RSAKey::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ QByteArray out;
+ if(!d->c->decrypt(a, &out, oaep))
+ return false;
+ *b = out;
+ return true;
+}
+
+bool RSAKey::generate(unsigned int bits)
+{
+ return d->c->generate(bits);
+}
+
+
+//----------------------------------------------------------------------------
+// RSA
+//----------------------------------------------------------------------------
+RSA::RSA()
+{
+}
+
+RSA::~RSA()
+{
+}
+
+RSAKey RSA::key() const
+{
+ return v_key;
+}
+
+void RSA::setKey(const RSAKey &k)
+{
+ v_key = k;
+}
+
+bool RSA::encrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ if(v_key.isNull())
+ return false;
+ return v_key.encrypt(a, b, oaep);
+}
+
+bool RSA::decrypt(const QByteArray &a, QByteArray *b, bool oaep) const
+{
+ if(v_key.isNull())
+ return false;
+ return v_key.decrypt(a, b, oaep);
+}
+
+RSAKey RSA::generateKey(unsigned int bits)
+{
+ RSAKey k;
+ k.generate(bits);
+ return k;
+}
+
+
+//----------------------------------------------------------------------------
+// Cert
+//----------------------------------------------------------------------------
+class Cert::Private
+{
+public:
+ Private()
+ {
+ c = 0;
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ QCA_CertContext *c;
+};
+
+Cert::Cert()
+{
+ d = new Private;
+ // crash because this is returning 0
+ d->c = (QCA_CertContext *)getContext(CAP_X509);
+}
+
+Cert::Cert(const Cert &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+Cert & Cert::operator=(const Cert &from)
+{
+ delete d->c;
+ if ( from.d->c )
+ d->c = from.d->c->clone();
+ else
+ d->c = 0;
+ return *this;
+}
+
+Cert::~Cert()
+{
+ delete d;
+}
+
+void Cert::fromContext(QCA_CertContext *ctx)
+{
+ delete d->c;
+ d->c = ctx;
+}
+
+bool Cert::isNull() const
+{
+ return d->c->isNull();
+}
+
+QString Cert::commonName() const
+{
+ CertProperties props = subject();
+ return props["CN"];
+}
+
+QString Cert::serialNumber() const
+{
+ return d->c->serialNumber();
+}
+
+QString Cert::subjectString() const
+{
+ return d->c->subjectString();
+}
+
+QString Cert::issuerString() const
+{
+ return d->c->issuerString();
+}
+
+CertProperties Cert::subject() const
+{
+ QValueList<QCA_CertProperty> list = d->c->subject();
+ CertProperties props;
+ for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
+ props[(*it).var] = (*it).val;
+ return props;
+}
+
+CertProperties Cert::issuer() const
+{
+ QValueList<QCA_CertProperty> list = d->c->issuer();
+ CertProperties props;
+ for(QValueList<QCA_CertProperty>::ConstIterator it = list.begin(); it != list.end(); ++it)
+ props[(*it).var] = (*it).val;
+ return props;
+}
+
+QDateTime Cert::notBefore() const
+{
+ return d->c->notBefore();
+}
+
+QDateTime Cert::notAfter() const
+{
+ return d->c->notAfter();
+}
+
+QByteArray Cert::toDER() const
+{
+ QByteArray out;
+ if(!d->c->toDER(&out))
+ return QByteArray();
+ return out;
+}
+
+bool Cert::fromDER(const QByteArray &a)
+{
+ return d->c->createFromDER(a.data(), a.size());
+}
+
+QString Cert::toPEM() const
+{
+ QByteArray out;
+ if(!d->c->toPEM(&out))
+ return QByteArray();
+
+ QCString cs;
+ cs.resize(out.size()+1);
+ memcpy(cs.data(), out.data(), out.size());
+ return QString::fromLatin1(cs);
+}
+
+bool Cert::fromPEM(const QString &str)
+{
+ QCString cs = str.latin1();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return d->c->createFromPEM(a.data(), a.size());
+}
+
+
+//----------------------------------------------------------------------------
+// TLS
+//----------------------------------------------------------------------------
+class TLS::Private
+{
+public:
+ Private()
+ {
+ c = (QCA_TLSContext *)getContext(CAP_TLS);
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void reset()
+ {
+ handshaken = false;
+ closing = false;
+ in.resize(0);
+ out.resize(0);
+ from_net.resize(0);
+ to_net.resize(0);
+ host = "";
+ hostMismatch = false;
+ // this causes the crash, because the Cert ctor is setting a null context
+ cert = Cert();
+ bytesEncoded = 0;
+ tryMore = false;
+ }
+
+ void appendArray(QByteArray *a, const QByteArray &b)
+ {
+ int oldsize = a->size();
+ a->resize(oldsize + b.size());
+ memcpy(a->data() + oldsize, b.data(), b.size());
+ }
+
+ Cert cert;
+ QCA_TLSContext *c;
+ QByteArray in, out, to_net, from_net;
+ int bytesEncoded;
+ bool tryMore;
+ bool handshaken;
+ QString host;
+ bool hostMismatch;
+ bool closing;
+
+ Cert ourCert;
+ RSAKey ourKey;
+ QPtrList<QCA_CertContext> store;
+};
+
+TLS::TLS(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+}
+
+TLS::~TLS()
+{
+ delete d;
+}
+
+void TLS::setCertificate(const Cert &cert, const RSAKey &key)
+{
+ d->ourCert = cert;
+ d->ourKey = key;
+}
+
+void TLS::setCertificateStore(const QPtrList<Cert> &store)
+{
+ // convert the cert list into a context list
+ d->store.clear();
+ QPtrListIterator<Cert> it(store);
+ for(Cert *cert; (cert = it.current()); ++it)
+ d->store.append(cert->d->c);
+}
+
+void TLS::reset()
+{
+ d->reset();
+}
+
+bool TLS::startClient(const QString &host)
+{
+ d->reset();
+ d->host = host;
+
+ if(!d->c->startClient(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
+ return false;
+ QTimer::singleShot(0, this, SLOT(update()));
+ return true;
+}
+
+bool TLS::startServer()
+{
+ d->reset();
+
+ if(!d->c->startServer(d->store, *d->ourCert.d->c, *d->ourKey.d->c))
+ return false;
+ QTimer::singleShot(0, this, SLOT(update()));
+ return true;
+}
+
+void TLS::close()
+{
+ if(!d->handshaken || d->closing)
+ return;
+
+ d->closing = true;
+ QTimer::singleShot(0, this, SLOT(update()));
+}
+
+bool TLS::isHandshaken() const
+{
+ return d->handshaken;
+}
+
+void TLS::write(const QByteArray &a)
+{
+ d->appendArray(&d->out, a);
+ update();
+}
+
+QByteArray TLS::read()
+{
+ QByteArray a = d->in.copy();
+ d->in.resize(0);
+ return a;
+}
+
+void TLS::writeIncoming(const QByteArray &a)
+{
+ d->appendArray(&d->from_net, a);
+ update();
+}
+
+QByteArray TLS::readOutgoing()
+{
+ QByteArray a = d->to_net.copy();
+ d->to_net.resize(0);
+ return a;
+}
+
+QByteArray TLS::readUnprocessed()
+{
+ QByteArray a = d->from_net.copy();
+ d->from_net.resize(0);
+ return a;
+}
+
+const Cert & TLS::peerCertificate() const
+{
+ return d->cert;
+}
+
+int TLS::certificateValidityResult() const
+{
+ if(d->hostMismatch)
+ return QCA::TLS::HostMismatch;
+ else
+ return d->c->validityResult();
+}
+
+void TLS::update()
+{
+ bool force_read = false;
+ bool eof = false;
+ bool done = false;
+ QGuardedPtr<TLS> self = this;
+
+ if(d->closing) {
+ QByteArray a;
+ int r = d->c->shutdown(d->from_net, &a);
+ d->from_net.resize(0);
+ if(r == QCA_TLSContext::Error) {
+ reset();
+ error(ErrHandshake);
+ return;
+ }
+ if(r == QCA_TLSContext::Success) {
+ d->from_net = d->c->unprocessed().copy();
+ done = true;
+ }
+ d->appendArray(&d->to_net, a);
+ }
+ else {
+ if(!d->handshaken) {
+ QByteArray a;
+ int r = d->c->handshake(d->from_net, &a);
+ d->from_net.resize(0);
+ if(r == QCA_TLSContext::Error) {
+ reset();
+ error(ErrHandshake);
+ return;
+ }
+ d->appendArray(&d->to_net, a);
+ if(r == QCA_TLSContext::Success) {
+ QCA_CertContext *cc = d->c->peerCertificate();
+ if(cc && !d->host.isEmpty() && d->c->validityResult() == QCA::TLS::Valid) {
+ if(!cc->matchesAddress(d->host))
+ d->hostMismatch = true;
+ }
+ d->cert.fromContext(cc);
+ d->handshaken = true;
+ handshaken();
+ if(!self)
+ return;
+
+ // there is a teeny tiny possibility that incoming data awaits. let us get it.
+ force_read = true;
+ }
+ }
+
+ if(d->handshaken) {
+ if(!d->out.isEmpty() || d->tryMore) {
+ d->tryMore = false;
+ QByteArray a;
+ int enc;
+ bool more = false;
+ bool ok = d->c->encode(d->out, &a, &enc);
+ eof = d->c->eof();
+ if(ok && enc < (int)d->out.size())
+ more = true;
+ d->out.resize(0);
+ if(!eof) {
+ if(!ok) {
+ reset();
+ error(ErrCrypt);
+ return;
+ }
+ d->bytesEncoded += enc;
+ if(more)
+ d->tryMore = true;
+ d->appendArray(&d->to_net, a);
+ }
+ }
+ if(!d->from_net.isEmpty() || force_read) {
+ QByteArray a, b;
+ bool ok = d->c->decode(d->from_net, &a, &b);
+ eof = d->c->eof();
+ d->from_net.resize(0);
+ if(!ok) {
+ reset();
+ error(ErrCrypt);
+ return;
+ }
+ d->appendArray(&d->in, a);
+ d->appendArray(&d->to_net, b);
+ }
+
+ if(!d->in.isEmpty()) {
+ readyRead();
+ if(!self)
+ return;
+ }
+ }
+ }
+
+ if(!d->to_net.isEmpty()) {
+ int bytes = d->bytesEncoded;
+ d->bytesEncoded = 0;
+ readyReadOutgoing(bytes);
+ if(!self)
+ return;
+ }
+
+ if(eof) {
+ close();
+ if(!self)
+ return;
+ return;
+ }
+
+ if(d->closing && done) {
+ reset();
+ closed();
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// SASL
+//----------------------------------------------------------------------------
+QString saslappname = "qca";
+class SASL::Private
+{
+public:
+ Private()
+ {
+ c = (QCA_SASLContext *)getContext(CAP_SASL);
+ }
+
+ ~Private()
+ {
+ delete c;
+ }
+
+ void setSecurityProps()
+ {
+ c->setSecurityProps(noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual, ssfmin, ssfmax, ext_authid, ext_ssf);
+ }
+
+ // security opts
+ bool noPlain, noActive, noDict, noAnon, reqForward, reqCreds, reqMutual;
+ int ssfmin, ssfmax;
+ QString ext_authid;
+ int ext_ssf;
+
+ bool tried;
+ QCA_SASLContext *c;
+ QHostAddress localAddr, remoteAddr;
+ int localPort, remotePort;
+ QByteArray stepData;
+ bool allowCSF;
+ bool first, server;
+
+ QByteArray inbuf, outbuf;
+};
+
+SASL::SASL(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ reset();
+}
+
+SASL::~SASL()
+{
+ delete d;
+}
+
+void SASL::setAppName(const QString &name)
+{
+ saslappname = name;
+}
+
+void SASL::reset()
+{
+ d->localPort = -1;
+ d->remotePort = -1;
+
+ d->noPlain = false;
+ d->noActive = false;
+ d->noDict = false;
+ d->noAnon = false;
+ d->reqForward = false;
+ d->reqCreds = false;
+ d->reqMutual = false;
+ d->ssfmin = 0;
+ d->ssfmax = 0;
+ d->ext_authid = "";
+ d->ext_ssf = 0;
+
+ d->inbuf.resize(0);
+ d->outbuf.resize(0);
+
+ d->c->reset();
+}
+
+int SASL::errorCondition() const
+{
+ return d->c->errorCond();
+}
+
+void SASL::setAllowPlain(bool b)
+{
+ d->noPlain = !b;
+}
+
+void SASL::setAllowAnonymous(bool b)
+{
+ d->noAnon = !b;
+}
+
+void SASL::setAllowActiveVulnerable(bool b)
+{
+ d->noActive = !b;
+}
+
+void SASL::setAllowDictionaryVulnerable(bool b)
+{
+ d->noDict = !b;
+}
+
+void SASL::setRequireForwardSecrecy(bool b)
+{
+ d->reqForward = b;
+}
+
+void SASL::setRequirePassCredentials(bool b)
+{
+ d->reqCreds = b;
+}
+
+void SASL::setRequireMutualAuth(bool b)
+{
+ d->reqMutual = b;
+}
+
+void SASL::setMinimumSSF(int x)
+{
+ d->ssfmin = x;
+}
+
+void SASL::setMaximumSSF(int x)
+{
+ d->ssfmax = x;
+}
+
+void SASL::setExternalAuthID(const QString &authid)
+{
+ d->ext_authid = authid;
+}
+
+void SASL::setExternalSSF(int x)
+{
+ d->ext_ssf = x;
+}
+
+void SASL::setLocalAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->localAddr = addr;
+ d->localPort = port;
+}
+
+void SASL::setRemoteAddr(const QHostAddress &addr, Q_UINT16 port)
+{
+ d->remoteAddr = addr;
+ d->remotePort = port;
+}
+
+bool SASL::startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst)
+{
+ QCA_SASLHostPort la, ra;
+ if(d->localPort != -1) {
+ la.addr = d->localAddr;
+ la.port = d->localPort;
+ }
+ if(d->remotePort != -1) {
+ ra.addr = d->remoteAddr;
+ ra.port = d->remotePort;
+ }
+
+ d->allowCSF = allowClientSendFirst;
+ d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
+ d->setSecurityProps();
+
+ if(!d->c->clientStart(mechlist))
+ return false;
+ d->first = true;
+ d->server = false;
+ d->tried = false;
+ QTimer::singleShot(0, this, SLOT(tryAgain()));
+ return true;
+}
+
+bool SASL::startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist)
+{
+ QCA_SASLHostPort la, ra;
+ if(d->localPort != -1) {
+ la.addr = d->localAddr;
+ la.port = d->localPort;
+ }
+ if(d->remotePort != -1) {
+ ra.addr = d->remoteAddr;
+ ra.port = d->remotePort;
+ }
+
+ d->c->setCoreProps(service, host, d->localPort != -1 ? &la : 0, d->remotePort != -1 ? &ra : 0);
+ d->setSecurityProps();
+
+ if(!d->c->serverStart(realm, mechlist, saslappname))
+ return false;
+ d->first = true;
+ d->server = true;
+ d->tried = false;
+ return true;
+}
+
+void SASL::putServerFirstStep(const QString &mech)
+{
+ int r = d->c->serverFirstStep(mech, 0);
+ handleServerFirstStep(r);
+}
+
+void SASL::putServerFirstStep(const QString &mech, const QByteArray &clientInit)
+{
+ int r = d->c->serverFirstStep(mech, &clientInit);
+ handleServerFirstStep(r);
+}
+
+void SASL::handleServerFirstStep(int r)
+{
+ if(r == QCA_SASLContext::Success)
+ authenticated();
+ else if(r == QCA_SASLContext::Continue)
+ nextStep(d->c->result());
+ else if(r == QCA_SASLContext::AuthCheck)
+ tryAgain();
+ else
+ error(ErrAuth);
+}
+
+void SASL::putStep(const QByteArray &stepData)
+{
+ d->stepData = stepData.copy();
+ tryAgain();
+}
+
+void SASL::setUsername(const QString &user)
+{
+ d->c->setClientParams(&user, 0, 0, 0);
+}
+
+void SASL::setAuthzid(const QString &authzid)
+{
+ d->c->setClientParams(0, &authzid, 0, 0);
+}
+
+void SASL::setPassword(const QString &pass)
+{
+ d->c->setClientParams(0, 0, &pass, 0);
+}
+
+void SASL::setRealm(const QString &realm)
+{
+ d->c->setClientParams(0, 0, 0, &realm);
+}
+
+void SASL::continueAfterParams()
+{
+ tryAgain();
+}
+
+void SASL::continueAfterAuthCheck()
+{
+ tryAgain();
+}
+
+void SASL::tryAgain()
+{
+ int r;
+
+ if(d->server) {
+ if(!d->tried) {
+ r = d->c->nextStep(d->stepData);
+ d->tried = true;
+ }
+ else {
+ r = d->c->tryAgain();
+ }
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::Continue) {
+ d->tried = false;
+ nextStep(d->c->result());
+ return;
+ }
+ else if(r == QCA_SASLContext::AuthCheck) {
+ authCheck(d->c->username(), d->c->authzid());
+ return;
+ }
+ }
+ else {
+ if(d->first) {
+ if(!d->tried) {
+ r = d->c->clientFirstStep(d->allowCSF);
+ d->tried = true;
+ }
+ else
+ r = d->c->tryAgain();
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::NeedParams) {
+ //d->tried = false;
+ QCA_SASLNeedParams np = d->c->clientParamsNeeded();
+ needParams(np.user, np.authzid, np.pass, np.realm);
+ return;
+ }
+
+ QString mech = d->c->mech();
+ const QByteArray *clientInit = d->c->clientInit();
+
+ d->first = false;
+ d->tried = false;
+ clientFirstStep(mech, clientInit);
+ }
+ else {
+ if(!d->tried) {
+ r = d->c->nextStep(d->stepData);
+ d->tried = true;
+ }
+ else
+ r = d->c->tryAgain();
+
+ if(r == QCA_SASLContext::Error) {
+ error(ErrAuth);
+ return;
+ }
+ else if(r == QCA_SASLContext::NeedParams) {
+ //d->tried = false;
+ QCA_SASLNeedParams np = d->c->clientParamsNeeded();
+ needParams(np.user, np.authzid, np.pass, np.realm);
+ return;
+ }
+ d->tried = false;
+ //else if(r == QCA_SASLContext::Continue) {
+ nextStep(d->c->result());
+ // return;
+ //}
+ }
+ }
+
+ if(r == QCA_SASLContext::Success)
+ authenticated();
+ else if(r == QCA_SASLContext::Error)
+ error(ErrAuth);
+}
+
+int SASL::ssf() const
+{
+ return d->c->security();
+}
+
+void SASL::write(const QByteArray &a)
+{
+ QByteArray b;
+ if(!d->c->encode(a, &b)) {
+ error(ErrCrypt);
+ return;
+ }
+ int oldsize = d->outbuf.size();
+ d->outbuf.resize(oldsize + b.size());
+ memcpy(d->outbuf.data() + oldsize, b.data(), b.size());
+ readyReadOutgoing(a.size());
+}
+
+QByteArray SASL::read()
+{
+ QByteArray a = d->inbuf.copy();
+ d->inbuf.resize(0);
+ return a;
+}
+
+void SASL::writeIncoming(const QByteArray &a)
+{
+ QByteArray b;
+ if(!d->c->decode(a, &b)) {
+ error(ErrCrypt);
+ return;
+ }
+ int oldsize = d->inbuf.size();
+ d->inbuf.resize(oldsize + b.size());
+ memcpy(d->inbuf.data() + oldsize, b.data(), b.size());
+ readyRead();
+}
+
+QByteArray SASL::readOutgoing()
+{
+ QByteArray a = d->outbuf.copy();
+ d->outbuf.resize(0);
+ return a;
+}
+
+#include "qca.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h
new file mode 100644
index 00000000..e7cd1609
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qca.h
@@ -0,0 +1,466 @@
+/*
+ * qca.h - Qt Cryptographic Architecture
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef QCA_H
+#define QCA_H
+
+#include<qstring.h>
+#include<qcstring.h>
+#include<qdatetime.h>
+#include<qmap.h>
+#include<qptrlist.h>
+#include<qobject.h>
+
+#ifdef Q_OS_WIN32
+# ifndef QCA_STATIC
+# ifdef QCA_MAKEDLL
+# define QCA_EXPORT __declspec(dllexport)
+# else
+# define QCA_EXPORT __declspec(dllimport)
+# endif
+# endif
+#endif
+#ifndef QCA_EXPORT
+#define QCA_EXPORT
+#endif
+
+#ifdef Q_OS_WIN32
+# ifdef QCA_PLUGIN_DLL
+# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllexport)
+# else
+# define QCA_PLUGIN_EXPORT extern "C" __declspec(dllimport)
+# endif
+#endif
+#ifndef QCA_PLUGIN_EXPORT
+#define QCA_PLUGIN_EXPORT extern "C"
+#endif
+
+class QHostAddress;
+class QStringList;
+
+class QCAProvider;
+class QCA_HashContext;
+class QCA_CipherContext;
+class QCA_CertContext;
+
+namespace QCA
+{
+ enum {
+ CAP_SHA1 = 0x0001,
+ CAP_SHA256 = 0x0002,
+ CAP_MD5 = 0x0004,
+ CAP_BlowFish = 0x0008,
+ CAP_TripleDES = 0x0010,
+ CAP_AES128 = 0x0020,
+ CAP_AES256 = 0x0040,
+ CAP_RSA = 0x0080,
+ CAP_X509 = 0x0100,
+ CAP_TLS = 0x0200,
+ CAP_SASL = 0x0400
+ };
+
+ enum {
+ CBC = 0x0001,
+ CFB = 0x0002
+ };
+
+ enum {
+ Encrypt = 0x0001,
+ Decrypt = 0x0002
+ };
+
+ QCA_EXPORT void init();
+ QCA_EXPORT bool isSupported(int capabilities);
+ QCA_EXPORT void insertProvider(QCAProvider *);
+ QCA_EXPORT void unloadAllPlugins();
+
+ QCA_EXPORT QString arrayToHex(const QByteArray &);
+ QCA_EXPORT QByteArray hexToArray(const QString &);
+
+ class QCA_EXPORT Hash
+ {
+ public:
+ Hash(const Hash &);
+ Hash & operator=(const Hash &);
+ ~Hash();
+
+ void clear();
+ void update(const QByteArray &a);
+ QByteArray final();
+
+ protected:
+ Hash(QCA_HashContext *);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ template <class T>
+ class QCA_EXPORT HashStatic
+ {
+ public:
+ HashStatic<T>() {}
+
+ static QByteArray hash(const QByteArray &a)
+ {
+ T obj;
+ obj.update(a);
+ return obj.final();
+ }
+
+ static QByteArray hash(const QCString &cs)
+ {
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return hash(a);
+ }
+
+ static QString hashToString(const QByteArray &a)
+ {
+ return arrayToHex(hash(a));
+ }
+
+ static QString hashToString(const QCString &cs)
+ {
+ return arrayToHex(hash(cs));
+ }
+ };
+
+ class QCA_EXPORT Cipher
+ {
+ public:
+ Cipher(const Cipher &);
+ Cipher & operator=(const Cipher &);
+ ~Cipher();
+
+ QByteArray dyn_generateKey(int size=-1) const;
+ QByteArray dyn_generateIV() const;
+ void reset(int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad=true);
+ bool update(const QByteArray &a);
+ QByteArray final(bool *ok=0);
+
+ protected:
+ Cipher(QCA_CipherContext *, int dir, int mode, const QByteArray &key, const QByteArray &iv, bool pad);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ template <class T>
+ class QCA_EXPORT CipherStatic
+ {
+ public:
+ CipherStatic<T>() {}
+
+ static QByteArray generateKey(int size=-1)
+ {
+ T obj;
+ return obj.dyn_generateKey(size);
+ }
+
+ static QByteArray generateIV()
+ {
+ T obj;
+ return obj.dyn_generateIV();
+ }
+ };
+
+ class QCA_EXPORT SHA1 : public Hash, public HashStatic<SHA1>
+ {
+ public:
+ SHA1();
+ };
+
+ class QCA_EXPORT SHA256 : public Hash, public HashStatic<SHA256>
+ {
+ public:
+ SHA256();
+ };
+
+ class QCA_EXPORT MD5 : public Hash, public HashStatic<MD5>
+ {
+ public:
+ MD5();
+ };
+
+ class QCA_EXPORT BlowFish : public Cipher, public CipherStatic<BlowFish>
+ {
+ public:
+ BlowFish(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT TripleDES : public Cipher, public CipherStatic<TripleDES>
+ {
+ public:
+ TripleDES(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT AES128 : public Cipher, public CipherStatic<AES128>
+ {
+ public:
+ AES128(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class QCA_EXPORT AES256 : public Cipher, public CipherStatic<AES256>
+ {
+ public:
+ AES256(int dir=Encrypt, int mode=CBC, const QByteArray &key=QByteArray(), const QByteArray &iv=QByteArray(), bool pad=true);
+ };
+
+ class RSA;
+ class QCA_EXPORT RSAKey
+ {
+ public:
+ RSAKey();
+ RSAKey(const RSAKey &from);
+ RSAKey & operator=(const RSAKey &from);
+ ~RSAKey();
+
+ bool isNull() const;
+ bool havePublic() const;
+ bool havePrivate() const;
+
+ QByteArray toDER(bool publicOnly=false) const;
+ bool fromDER(const QByteArray &a);
+
+ QString toPEM(bool publicOnly=false) const;
+ bool fromPEM(const QString &);
+
+ // only call if you know what you are doing
+ bool fromNative(void *);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class RSA;
+ friend class TLS;
+ bool encrypt(const QByteArray &a, QByteArray *out, bool oaep) const;
+ bool decrypt(const QByteArray &a, QByteArray *out, bool oaep) const;
+ bool generate(unsigned int bits);
+ };
+
+ class QCA_EXPORT RSA
+ {
+ public:
+ RSA();
+ ~RSA();
+
+ RSAKey key() const;
+ void setKey(const RSAKey &);
+
+ bool encrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const;
+ bool decrypt(const QByteArray &a, QByteArray *out, bool oaep=false) const;
+
+ static RSAKey generateKey(unsigned int bits);
+
+ private:
+ RSAKey v_key;
+ };
+
+ typedef QMap<QString, QString> CertProperties;
+ class QCA_EXPORT Cert
+ {
+ public:
+ Cert();
+ Cert(const Cert &);
+ Cert & operator=(const Cert &);
+ ~Cert();
+
+ bool isNull() const;
+
+ QString commonName() const;
+ QString serialNumber() const;
+ QString subjectString() const;
+ QString issuerString() const;
+ CertProperties subject() const;
+ CertProperties issuer() const;
+ QDateTime notBefore() const;
+ QDateTime notAfter() const;
+
+ QByteArray toDER() const;
+ bool fromDER(const QByteArray &a);
+
+ QString toPEM() const;
+ bool fromPEM(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class TLS;
+ void fromContext(QCA_CertContext *);
+ };
+
+ class QCA_EXPORT TLS : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Validity {
+ NoCert,
+ Valid,
+ HostMismatch,
+ Rejected,
+ Untrusted,
+ SignatureFailed,
+ InvalidCA,
+ InvalidPurpose,
+ SelfSigned,
+ Revoked,
+ PathLengthExceeded,
+ Expired,
+ Unknown
+ };
+ enum Error { ErrHandshake, ErrCrypt };
+
+ TLS(QObject *parent=0);
+ ~TLS();
+
+ void setCertificate(const Cert &cert, const RSAKey &key);
+ void setCertificateStore(const QPtrList<Cert> &store); // note: store must persist
+
+ void reset();
+ bool startClient(const QString &host="");
+ bool startServer();
+ void close();
+ bool isHandshaken() const;
+
+ // plain (application side)
+ void write(const QByteArray &a);
+ QByteArray read();
+
+ // encoded (socket side)
+ void writeIncoming(const QByteArray &a);
+ QByteArray readOutgoing();
+ QByteArray readUnprocessed();
+
+ // cert related
+ const Cert & peerCertificate() const;
+ int certificateValidityResult() const;
+
+ signals:
+ void handshaken();
+ void readyRead();
+ void readyReadOutgoing(int plainBytes);
+ void closed();
+ void error(int);
+
+ private slots:
+ void update();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class QCA_EXPORT SASL : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrAuth, ErrCrypt };
+ enum ErrorCond {
+ NoMech,
+ BadProto,
+ BadServ,
+ BadAuth,
+ NoAuthzid,
+ TooWeak,
+ NeedEncrypt,
+ Expired,
+ Disabled,
+ NoUser,
+ RemoteUnavail
+ };
+ SASL(QObject *parent=0);
+ ~SASL();
+
+ static void setAppName(const QString &name);
+
+ void reset();
+ int errorCondition() const;
+
+ // options
+ void setAllowPlain(bool);
+ void setAllowAnonymous(bool);
+ void setAllowActiveVulnerable(bool);
+ void setAllowDictionaryVulnerable(bool);
+ void setRequireForwardSecrecy(bool);
+ void setRequirePassCredentials(bool);
+ void setRequireMutualAuth(bool);
+
+ void setMinimumSSF(int);
+ void setMaximumSSF(int);
+ void setExternalAuthID(const QString &authid);
+ void setExternalSSF(int);
+
+ void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+ void setRemoteAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ // initialize
+ bool startClient(const QString &service, const QString &host, const QStringList &mechlist, bool allowClientSendFirst=true);
+ bool startServer(const QString &service, const QString &host, const QString &realm, QStringList *mechlist);
+
+ // authentication
+ void putStep(const QByteArray &stepData);
+ void putServerFirstStep(const QString &mech);
+ void putServerFirstStep(const QString &mech, const QByteArray &clientInit);
+ void setUsername(const QString &user);
+ void setAuthzid(const QString &auth);
+ void setPassword(const QString &pass);
+ void setRealm(const QString &realm);
+ void continueAfterParams();
+ void continueAfterAuthCheck();
+
+ // security layer
+ int ssf() const;
+ void write(const QByteArray &a);
+ QByteArray read();
+ void writeIncoming(const QByteArray &a);
+ QByteArray readOutgoing();
+
+ signals:
+ // for authentication
+ void clientFirstStep(const QString &mech, const QByteArray *clientInit);
+ void nextStep(const QByteArray &stepData);
+ void needParams(bool user, bool authzid, bool pass, bool realm);
+ void authCheck(const QString &user, const QString &authzid);
+ void authenticated();
+
+ // for security layer
+ void readyRead();
+ void readyReadOutgoing(int plainBytes);
+
+ // error
+ void error(int);
+
+ private slots:
+ void tryAgain();
+
+ private:
+ class Private;
+ Private *d;
+
+ void handleServerFirstStep(int r);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h b/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h
new file mode 100644
index 00000000..a7f1805b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qca/src/qcaprovider.h
@@ -0,0 +1,191 @@
+/*
+ * qcaprovider.h - QCA Plugin API
+ * Copyright (C) 2003 Justin Karneges
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef QCAPROVIDER_H
+#define QCAPROVIDER_H
+
+#include<qglobal.h>
+#include<qstring.h>
+#include<qdatetime.h>
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"qca.h"
+
+#define QCA_PLUGIN_VERSION 1
+
+class QCAProvider
+{
+public:
+ QCAProvider() {}
+ virtual ~QCAProvider() {}
+
+ virtual void init()=0;
+ virtual int qcaVersion() const=0;
+ virtual int capabilities() const=0;
+ virtual void *context(int cap)=0;
+};
+
+class QCA_HashContext
+{
+public:
+ virtual ~QCA_HashContext() {}
+
+ virtual QCA_HashContext *clone()=0;
+ virtual void reset()=0;
+ virtual void update(const char *in, unsigned int len)=0;
+ virtual void final(QByteArray *out)=0;
+};
+
+class QCA_CipherContext
+{
+public:
+ virtual ~QCA_CipherContext() {}
+
+ virtual QCA_CipherContext *clone()=0;
+ virtual int keySize()=0;
+ virtual int blockSize()=0;
+ virtual bool generateKey(char *out, int keysize=-1)=0;
+ virtual bool generateIV(char *out)=0;
+
+ virtual bool setup(int dir, int mode, const char *key, int keysize, const char *iv, bool pad)=0;
+ virtual bool update(const char *in, unsigned int len)=0;
+ virtual bool final(QByteArray *out)=0;
+};
+
+class QCA_RSAKeyContext
+{
+public:
+ virtual ~QCA_RSAKeyContext() {}
+
+ virtual QCA_RSAKeyContext *clone() const=0;
+ virtual bool isNull() const=0;
+ virtual bool havePublic() const=0;
+ virtual bool havePrivate() const=0;
+ virtual bool createFromDER(const char *in, unsigned int len)=0;
+ virtual bool createFromPEM(const char *in, unsigned int len)=0;
+ virtual bool createFromNative(void *in)=0;
+ virtual bool generate(unsigned int bits)=0;
+ virtual bool toDER(QByteArray *out, bool publicOnly)=0;
+ virtual bool toPEM(QByteArray *out, bool publicOnly)=0;
+
+ virtual bool encrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+ virtual bool decrypt(const QByteArray &in, QByteArray *out, bool oaep)=0;
+};
+
+struct QCA_CertProperty
+{
+ QString var;
+ QString val;
+};
+
+class QCA_CertContext
+{
+public:
+ virtual ~QCA_CertContext() {}
+
+ virtual QCA_CertContext *clone() const=0;
+ virtual bool isNull() const=0;
+ virtual bool createFromDER(const char *in, unsigned int len)=0;
+ virtual bool createFromPEM(const char *in, unsigned int len)=0;
+ virtual bool toDER(QByteArray *out)=0;
+ virtual bool toPEM(QByteArray *out)=0;
+
+ virtual QString serialNumber() const=0;
+ virtual QString subjectString() const=0;
+ virtual QString issuerString() const=0;
+ virtual QValueList<QCA_CertProperty> subject() const=0;
+ virtual QValueList<QCA_CertProperty> issuer() const=0;
+ virtual QDateTime notBefore() const=0;
+ virtual QDateTime notAfter() const=0;
+ virtual bool matchesAddress(const QString &realHost) const=0;
+};
+
+class QCA_TLSContext
+{
+public:
+ enum Result { Success, Error, Continue };
+ virtual ~QCA_TLSContext() {}
+
+ virtual void reset()=0;
+ virtual bool startClient(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+ virtual bool startServer(const QPtrList<QCA_CertContext> &store, const QCA_CertContext &cert, const QCA_RSAKeyContext &key)=0;
+
+ virtual int handshake(const QByteArray &in, QByteArray *out)=0;
+ virtual int shutdown(const QByteArray &in, QByteArray *out)=0;
+ virtual bool encode(const QByteArray &plain, QByteArray *to_net, int *encoded)=0;
+ virtual bool decode(const QByteArray &from_net, QByteArray *plain, QByteArray *to_net)=0;
+ virtual bool eof() const=0;
+ virtual QByteArray unprocessed()=0;
+
+ virtual QCA_CertContext *peerCertificate() const=0;
+ virtual int validityResult() const=0;
+};
+
+struct QCA_SASLHostPort
+{
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+struct QCA_SASLNeedParams
+{
+ bool user, authzid, pass, realm;
+};
+
+class QCA_SASLContext
+{
+public:
+ enum Result { Success, Error, NeedParams, AuthCheck, Continue };
+ virtual ~QCA_SASLContext() {}
+
+ // common
+ virtual void reset()=0;
+ virtual void setCoreProps(const QString &service, const QString &host, QCA_SASLHostPort *local, QCA_SASLHostPort *remote)=0;
+ virtual void setSecurityProps(bool noPlain, bool noActive, bool noDict, bool noAnon, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int ssfMax, const QString &_ext_authid, int _ext_ssf)=0;
+ virtual int security() const=0;
+ virtual int errorCond() const=0;
+
+ // init / first step
+ virtual bool clientStart(const QStringList &mechlist)=0;
+ virtual int clientFirstStep(bool allowClientSendFirst)=0;
+ virtual bool serverStart(const QString &realm, QStringList *mechlist, const QString &name)=0;
+ virtual int serverFirstStep(const QString &mech, const QByteArray *in)=0;
+
+ // get / set params
+ virtual QCA_SASLNeedParams clientParamsNeeded() const=0;
+ virtual void setClientParams(const QString *user, const QString *authzid, const QString *pass, const QString *realm)=0;
+ virtual QString username() const=0;
+ virtual QString authzid() const=0;
+
+ // continue steps
+ virtual int nextStep(const QByteArray &in)=0;
+ virtual int tryAgain()=0;
+
+ // results
+ virtual QString mech() const=0;
+ virtual const QByteArray *clientInit() const=0;
+ virtual QByteArray result() const=0;
+
+ // security layer
+ virtual bool encode(const QByteArray &in, QByteArray *out)=0;
+ virtual bool decode(const QByteArray &in, QByteArray *out)=0;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp
new file mode 100644
index 00000000..366f2afa
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.cpp
@@ -0,0 +1,122 @@
+/*
+ qcatlshandler.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+
+#include "qca.h"
+
+#include "qcatlshandler.h"
+
+class QCATLSHandler::Private
+{
+public:
+ QCA::TLS *tls;
+ int state, err;
+};
+
+QCATLSHandler::QCATLSHandler(QCA::TLS *parent)
+:TLSHandler(parent)
+{
+ d = new Private;
+ d->tls = parent;
+ connect(d->tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+ connect(d->tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+ connect(d->tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+ connect(d->tls, SIGNAL(closed()), SLOT(tls_closed()));
+ connect(d->tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+ d->state = 0;
+ d->err = -1;
+}
+
+QCATLSHandler::~QCATLSHandler()
+{
+ delete d;
+}
+
+QCA::TLS *QCATLSHandler::tls() const
+{
+ return d->tls;
+}
+
+int QCATLSHandler::tlsError() const
+{
+ return d->err;
+}
+
+void QCATLSHandler::reset()
+{
+ d->tls->reset();
+ d->state = 0;
+}
+
+void QCATLSHandler::startClient(const QString &host)
+{
+ d->state = 0;
+ d->err = -1;
+ if(!d->tls->startClient(host))
+ QTimer::singleShot(0, this, SIGNAL(fail()));
+}
+
+void QCATLSHandler::write(const QByteArray &a)
+{
+ d->tls->write(a);
+}
+
+void QCATLSHandler::writeIncoming(const QByteArray &a)
+{
+ d->tls->writeIncoming(a);
+}
+
+void QCATLSHandler::continueAfterHandshake()
+{
+ if(d->state == 2) {
+ success();
+ d->state = 3;
+ }
+}
+
+void QCATLSHandler::tls_handshaken()
+{
+ d->state = 2;
+ tlsHandshaken();
+}
+
+void QCATLSHandler::tls_readyRead()
+{
+ readyRead(d->tls->read());
+}
+
+void QCATLSHandler::tls_readyReadOutgoing(int plainBytes)
+{
+ readyReadOutgoing(d->tls->readOutgoing(), plainBytes);
+}
+
+void QCATLSHandler::tls_closed()
+{
+ closed();
+}
+
+void QCATLSHandler::tls_error(int x)
+{
+ d->err = x;
+ d->state = 0;
+ fail();
+}
+
+#include "qcatlshandler.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h
new file mode 100644
index 00000000..a550d54b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/qcatlshandler.h
@@ -0,0 +1,61 @@
+/*
+ qcatlshandler.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GWQCATLSHANDLER_H
+#define GWQCATLSHANDLER_H
+
+//#include <qtimer.h>
+#include "tlshandler.h"
+
+class QCA::TLS;
+
+class QCATLSHandler : public TLSHandler
+{
+ Q_OBJECT
+public:
+ QCATLSHandler(QCA::TLS *parent);
+ ~QCATLSHandler();
+
+ QCA::TLS *tls() const;
+ int tlsError() const;
+
+ void reset();
+ void startClient(const QString &host);
+ void write(const QByteArray &a);
+ void writeIncoming(const QByteArray &a);
+
+signals:
+ void tlsHandshaken();
+
+public slots:
+ void continueAfterHandshake();
+
+private slots:
+ void tls_handshaken();
+ void tls_readyRead();
+ void tls_readyReadOutgoing(int);
+ void tls_closed();
+ void tls_error(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/request.cpp b/kopete/protocols/groupwise/libgroupwise/request.cpp
new file mode 100644
index 00000000..508bf6a0
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/request.cpp
@@ -0,0 +1,34 @@
+/*
+ request.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "request.h"
+
+Request::Request( const int transactionId, const QString &command )
+: UserTransfer(transactionId), m_command( command )
+{
+}
+
+Request::~Request()
+{
+}
+
+QString Request::command()
+{
+ return m_command;
+}
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/request.h b/kopete/protocols/groupwise/libgroupwise/request.h
new file mode 100644
index 00000000..85a55e8a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/request.h
@@ -0,0 +1,40 @@
+/*
+ request.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef LIBGW_REQUEST_H
+#define LIBGW_REQUEST_H
+
+#include "usertransfer.h"
+
+/**
+ * Represents a client generated request to the server
+ * Create with @ref RequestFactory::request().
+ * @author Kopete Developers
+*/
+class Request : public UserTransfer
+{
+friend class RequestFactory;
+
+public:
+ ~Request( );
+ QString command();
+ TransferType type() { return Transfer::RequestTransfer; }
+private:
+ Request( const int transactionId, const QString &command );
+ QString m_command;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp b/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp
new file mode 100644
index 00000000..6387370f
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/requestfactory.cpp
@@ -0,0 +1,37 @@
+/*
+ requestfactory.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "request.h"
+
+#include "requestfactory.h"
+
+#define GW_REQUESTFACTORY_FIRST_TID 1
+RequestFactory::RequestFactory()
+: m_nextTransaction( GW_REQUESTFACTORY_FIRST_TID )
+{
+}
+
+RequestFactory::~RequestFactory()
+{
+}
+
+Request* RequestFactory::request( const QString &command )
+{
+ return new Request( m_nextTransaction++, command );
+}
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/requestfactory.h b/kopete/protocols/groupwise/libgroupwise/requestfactory.h
new file mode 100644
index 00000000..e4ec073e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/requestfactory.h
@@ -0,0 +1,44 @@
+/*
+ requestfactory.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef REQUESTFACTORY_H
+#define REQUESTFACTORY_H
+
+#include <qstring.h>
+
+class Request;
+
+/**
+ * Factory for obtaining @ref Request instances.
+ * @author Kopete Developers
+ */
+class RequestFactory{
+public:
+ RequestFactory();
+ ~RequestFactory();
+
+ /**
+ * Obtain a new @ref Request instance
+ * The consumer is responsible for deleting this
+ * TODO: Allow the user to provide the fields for the request in this call
+ */
+ Request * request( const QString &request);
+private:
+ int m_nextTransaction;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/response.cpp b/kopete/protocols/groupwise/libgroupwise/response.cpp
new file mode 100644
index 00000000..837c7810
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/response.cpp
@@ -0,0 +1,34 @@
+/*
+ response.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qapplication.h>
+
+#include "response.h"
+
+Response::Response( int transactionId, int resultCode, Field::FieldList fields )
+: UserTransfer( transactionId ), m_resultCode( resultCode )
+{
+ setFields( fields );
+}
+
+Response::~Response()
+{
+}
+
+int Response::resultCode() const
+{
+ return m_resultCode;
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/response.h b/kopete/protocols/groupwise/libgroupwise/response.h
new file mode 100644
index 00000000..8f4fb970
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/response.h
@@ -0,0 +1,39 @@
+/*
+ response.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_RESPONSE_H
+#define GW_RESPONSE_H
+
+#include "usertransfer.h"
+
+/**
+ * Represents the server's reply to a client generated request
+ * @author Kopete Developers
+*/
+class Response: public UserTransfer
+{
+public:
+ Response( int transactionId, int resultCode, Field::FieldList fields );
+ virtual ~Response( );
+
+ TransferType type() { return Transfer::ResponseTransfer; }
+ int resultCode() const;
+private:
+ int m_resultCode;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp b/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp
new file mode 100644
index 00000000..6784fd15
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/responseprotocol.cpp
@@ -0,0 +1,314 @@
+/*
+ Kopete Groupwise Protocol
+ responseprotocol.cpp - Protocol used for reading incoming GroupWise Responses
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qbuffer.h>
+
+#include "response.h"
+
+#include "responseprotocol.h"
+
+ResponseProtocol::ResponseProtocol(QObject* parent, const char* name): InputProtocolBase(parent, name)
+{
+}
+
+
+ResponseProtocol::~ResponseProtocol()
+{
+}
+
+Transfer * ResponseProtocol::parse( const QByteArray & wire, uint & bytes )
+{
+ m_bytes = 0;
+ m_collatingFields.clear();
+ //m_din = new QDataStream( wire, IO_ReadOnly );
+ QBuffer inBuf( wire );
+ inBuf.open( IO_ReadOnly);
+ m_din.setDevice( &inBuf );
+ m_din.setByteOrder( QDataStream::LittleEndian );
+
+ // check that this begins with a HTTP (is a response)
+ Q_UINT32 val;
+ m_din >> val;
+ m_bytes += sizeof( Q_UINT32 );
+
+ Q_ASSERT( qstrncmp( (const char *)&val, "HTTP", strlen( "HTTP" ) ) == 0 );
+
+ // read rest of HTTP header and look for a 301 redirect.
+ QCString headerFirst;
+ if ( !readGroupWiseLine( headerFirst ) )
+ return 0;
+ // pull out the HTTP return code
+ int firstSpace = headerFirst.find( ' ' );
+ QString rtnField = headerFirst.mid( firstSpace, headerFirst.find( ' ', firstSpace + 1 ) );
+ bool ok = true;
+ int rtnCode;
+ int packetState = -1;
+ rtnCode = rtnField.toInt( &ok );
+ debug( "CoreProtocol::readResponse() got HTTP return code " );
+ // read rest of header
+ QStringList headerRest;
+ QCString line;
+ while ( line != "\r\n" )
+ {
+ if ( !readGroupWiseLine( line ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ headerRest.append( line );
+ debug( QString( "- read header line - (%1) : %2" ).arg( line.length() ).arg( line.data() ) );
+ }
+ debug( "ResponseProtocol::readResponse() header finished" );
+ // if it's a redirect, set flag
+ if ( ok && rtnCode == 301 )
+ {
+ debug( "- server redirect " );
+ packetState = ServerRedirect;
+ m_din.unsetDevice();
+ return 0;
+ }
+ // other header processing ( 500! )
+ if ( ok && rtnCode == 500 )
+ {
+ debug( QString( "- server error %1" ).arg( rtnCode ) );
+ packetState = ServerError;
+ m_din.unsetDevice();
+ return 0;
+ }
+ if ( ok && rtnCode == 404 )
+ {
+ debug( QString( "- server error %1" ).arg( rtnCode ) );
+ packetState = ServerError;
+ m_din.unsetDevice();
+ return 0;
+ }
+ if ( m_din.atEnd() )
+ {
+ debug( "- no fields" );
+ packetState = ProtocolError;
+ m_din.unsetDevice();
+ return 0;
+ }
+
+ // read fields
+ if ( !readFields( -1 ) )
+ {
+ m_din.unsetDevice();
+ return 0;
+ }
+ // find transaction id field and create Response object if nonzero
+ int tId = 0;
+ int resultCode = 0;
+ Field::FieldListIterator it;
+ Field::FieldListIterator end = m_collatingFields.end();
+ it = m_collatingFields.find( NM_A_SZ_TRANSACTION_ID );
+ if ( it != end )
+ {
+ Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it );
+ if ( sf )
+ {
+ tId = sf->value().toInt();
+ debug( QString( "ResponseProtocol::readResponse() - transaction ID is %1" ).arg( tId ) );
+ m_collatingFields.remove( it );
+ delete sf;
+ }
+ }
+ it = m_collatingFields.find( NM_A_SZ_RESULT_CODE );
+ if ( it != end )
+ {
+ Field::SingleField * sf = dynamic_cast<Field::SingleField*>( *it );
+ if ( sf )
+ {
+ resultCode = sf->value().toInt();
+ debug( QString( "ResponseProtocol::readResponse() - result code is %1" ).arg( resultCode ) );
+ m_collatingFields.remove( it );
+ delete sf;
+ }
+ }
+ // append to inQueue
+ if ( tId )
+ {
+ debug( QString( "ResponseProtocol::readResponse() - setting state Available, got %1 fields in base array" ).arg(m_collatingFields.count() ) );
+ packetState = Available;
+ bytes = m_bytes;
+ m_din.unsetDevice();
+ return new Response( tId, resultCode, m_collatingFields );
+ }
+ else
+ {
+ debug( "- WARNING - NO TRANSACTION ID FOUND!" );
+ m_state = ProtocolError;
+ m_din.unsetDevice();
+ m_collatingFields.purge();
+ return 0;
+ }
+}
+
+bool ResponseProtocol::readFields( int fieldCount, Field::FieldList * list )
+{
+ // build a list of fields.
+ // If there is already a list of fields stored in m_collatingFields,
+ // the list we're reading on this iteration must be a nested list
+ // so when we're done reading it, add it to the MultiList element
+ // that is the last element in the top list in m_collatingFields.
+ // if we find the beginning of a new nested list, push the current list onto m_collatingFields
+ debug( "ResponseProtocol::readFields()" );
+ if ( fieldCount > 0 )
+ debug( QString( "reading %1 fields" ).arg( fieldCount ) );
+ Field::FieldList currentList;
+ while ( fieldCount != 0 ) // prevents bad input data from ruining our day
+ {
+ // the field being read
+ // read field
+ Q_UINT8 type, method;
+ Q_UINT32 val;
+ QCString tag;
+ // read uint8 type
+ if ( !okToProceed() )
+ {
+ currentList.purge();
+ return false;
+ }
+ m_din >> type;
+ m_bytes += sizeof( Q_UINT8 );
+ // if type is 0 SOMETHING_INVALID, we're at the end of the fields
+ if ( type == 0 ) /*&& m_din->atEnd() )*/
+ {
+ debug( "- end of field list" );
+ m_packetState = FieldsRead;
+ // do something to indicate we're done
+ break;
+ }
+ // read uint8 method
+ if ( !okToProceed() )
+ {
+ currentList.purge();
+ return false;
+ }
+ m_din >> method;
+ m_bytes += sizeof( Q_UINT8 );
+ // read tag and length
+ if ( !safeReadBytes( tag, val ) )
+ {
+ currentList.purge();
+ return false;
+ }
+
+ debug( QString( "- type: %1, method: %2, tag: %3," ).arg( type ).arg( method ).arg( tag.data() ) );
+ // if multivalue or array
+ if ( type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY )
+ {
+ // read length uint32
+ if ( !okToProceed() )
+ {
+ currentList.purge();
+ return false;
+ }
+ m_din >> val;
+ m_bytes += sizeof( Q_UINT32 );
+
+ // create multifield
+ debug( QString( " multi field containing: %1" ).arg( val ) );
+ Field::MultiField* m = new Field::MultiField( tag, method, 0, type );
+ currentList.append( m );
+ if ( !readFields( val, &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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RESPONSEPROTOCOL_H
+#define RESPONSEPROTOCOL_H
+
+#include "gwerror.h"
+#include "gwfield.h"
+
+#include "inputprotocolbase.h"
+
+/**
+Handles the parsing of incoming Response messages
+
+@author Kopete Developers
+*/
+class ResponseProtocol : public InputProtocolBase
+{
+Q_OBJECT
+public:
+ /**
+ * Describes the current state of the protocol
+ */
+ enum State { NeedMore, Available, ServerError, ServerRedirect, ReadingEvent, NoData };
+
+ /**
+ * Describes the parsing of the last received packet
+ */
+ enum PacketState { FieldsRead, ProtocolError };
+
+ ResponseProtocol(QObject* parent, const char* name);
+ ~ResponseProtocol();
+ /**
+ * Attempt to parse the supplied data into an @ref Response object.
+ * The exact state of the parse attempt can be read using @ref state.
+ * @param rawData The unparsed data.
+ * @param bytes An integer used to return the number of bytes read.
+ * @return A pointer to an Response object if successfull, otherwise 0. The caller is responsible for deleting this object.
+ */
+ Transfer * parse( const QByteArray &, uint & bytes );
+protected:
+ /**
+ * read a line ending in \r\n, including the \r\n
+ */
+ bool readGroupWiseLine( QCString & );
+ /**
+ * Read in a response
+ */
+ bool readResponse();
+ /**
+ * Parse received fields and store in m_collatingFields
+ */
+ bool readFields( int fieldCount, Field::FieldList * list = 0 );
+private:
+ // fields from a packet being parsed, before it has been completely received
+ //QValueStack<Field::FieldList> m_collatingFields;
+ Field::FieldList m_collatingFields;
+ int m_packetState; // represents the state of the parsing of the last incoming data received
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/rtf.cc b/kopete/protocols/groupwise/libgroupwise/rtf.cc
new file mode 100644
index 00000000..eb5da80e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/rtf.cc
@@ -0,0 +1,2532 @@
+#line 2 "rtf.cc"
+
+#line 4 "rtf.cc"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE rtfrestart(rtfin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int rtfleng;
+
+extern FILE *rtfin, *rtfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up rtftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up rtftext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via rtfrestart()), so that the user can continue scanning by
+ * just pointing rtfin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when rtftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int rtfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow rtfwrap()'s to do buffer switches
+ * instead of setting up a fresh rtfin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void rtfrestart (FILE *input_file );
+void rtf_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE rtf_create_buffer (FILE *file,int size );
+void rtf_delete_buffer (YY_BUFFER_STATE b );
+void rtf_flush_buffer (YY_BUFFER_STATE b );
+void rtfpush_buffer_state (YY_BUFFER_STATE new_buffer );
+void rtfpop_buffer_state (void );
+
+static void rtfensure_buffer_stack (void );
+static void rtf_load_buffer_state (void );
+static void rtf_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER rtf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE rtf_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE rtf_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE rtf_scan_bytes (yyconst char *bytes,int len );
+
+void *rtfalloc (yy_size_t );
+void *rtfrealloc (void *,yy_size_t );
+void rtffree (void * );
+
+#define yy_new_buffer rtf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ rtfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ rtf_create_buffer(rtfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ rtfensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ rtf_create_buffer(rtfin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+typedef unsigned char YY_CHAR;
+
+FILE *rtfin = (FILE *) 0, *rtfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int rtflineno;
+
+int rtflineno = 1;
+
+extern char *rtftext;
+#define yytext_ptr rtftext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up rtftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ rtfleng = (size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 10
+#define YY_END_OF_BUFFER 11
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[33] =
+ { 0,
+ 0, 0, 11, 8, 8, 9, 9, 1, 2, 8,
+ 0, 0, 5, 3, 5, 0, 0, 5, 5, 5,
+ 0, 6, 5, 7, 5, 5, 5, 4, 5, 5,
+ 5, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 1, 1, 4, 1, 1, 1, 5, 1,
+ 1, 1, 1, 1, 1, 1, 1, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 1, 1, 7,
+ 1, 8, 9, 1, 10, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 1, 12, 1, 1, 1, 1, 10, 10, 10, 10,
+
+ 10, 10, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 13, 11, 11, 11,
+ 11, 11, 14, 1, 15, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[16] =
+ { 0,
+ 1, 1, 2, 1, 1, 2, 3, 4, 1, 2,
+ 2, 3, 2, 3, 3
+ } ;
+
+static yyconst flex_int16_t yy_base[37] =
+ { 0,
+ 0, 14, 45, 0, 0, 39, 25, 59, 59, 0,
+ 38, 0, 2, 59, 14, 0, 3, 59, 16, 21,
+ 25, 59, 28, 59, 38, 23, 19, 59, 17, 12,
+ 5, 59, 47, 51, 1, 55
+ } ;
+
+static yyconst flex_int16_t yy_def[37] =
+ { 0,
+ 33, 33, 32, 34, 34, 32, 32, 32, 32, 34,
+ 32, 32, 35, 32, 35, 36, 32, 32, 32, 32,
+ 36, 32, 32, 32, 32, 32, 25, 32, 25, 25,
+ 25, 0, 32, 32, 32, 32
+ } ;
+
+static yyconst flex_int16_t yy_nxt[75] =
+ { 0,
+ 32, 5, 13, 32, 18, 17, 6, 19, 22, 17,
+ 19, 7, 22, 8, 9, 5, 18, 31, 18, 20,
+ 6, 19, 30, 18, 29, 7, 23, 8, 9, 12,
+ 18, 28, 24, 25, 13, 13, 14, 15, 14, 14,
+ 26, 16, 11, 27, 32, 32, 28, 4, 4, 4,
+ 4, 10, 10, 32, 10, 21, 21, 21, 3, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32
+ } ;
+
+static yyconst flex_int16_t yy_chk[75] =
+ { 0,
+ 0, 1, 35, 0, 13, 12, 1, 13, 17, 12,
+ 31, 1, 17, 1, 1, 2, 15, 30, 19, 15,
+ 2, 19, 29, 20, 27, 2, 20, 2, 2, 7,
+ 23, 26, 21, 23, 7, 7, 7, 7, 7, 7,
+ 25, 11, 6, 25, 3, 0, 25, 33, 33, 33,
+ 33, 34, 34, 0, 34, 36, 36, 36, 32, 32,
+ 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ 32, 32, 32, 32
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int rtf_flex_debug;
+int rtf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *rtftext;
+#line 1 "rtf.ll"
+#line 2 "rtf.ll"
+/*
+ rtf.ll - A simple RTF Parser (Flex code)
+
+ Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+update rtf.cc:
+flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll
+sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc
+rm -f lex.yy.c
+
+*/
+
+#define UP 1
+#define DOWN 2
+#define CMD 3
+#define TXT 4
+#define HEX 5
+#define IMG 6
+#define UNICODE_CHAR 7
+#define SKIP 8
+#define SLASH 9
+#define S_TXT 10
+
+#define YY_NEVER_INTERACTIVE 1
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_MAIN 0
+
+#line 505 "rtf.cc"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int rtfwrap (void );
+#else
+extern int rtfwrap (void );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( rtftext, rtfleng, 1, rtfout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( rtfin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( rtfin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, rtfin))==0 && ferror(rtfin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(rtfin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int rtflex (void);
+
+#define YY_DECL int rtflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after rtftext and rtfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 46 "rtf.ll"
+
+
+#line 657 "rtf.cc"
+
+ if ( (yy_init) )
+ {
+ (yy_init) = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! rtfin )
+ rtfin = stdin;
+
+ if ( ! rtfout )
+ rtfout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ rtfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ rtf_create_buffer(rtfin,YY_BUF_SIZE );
+ }
+
+ rtf_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of rtftext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 33 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 59 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 48 "rtf.ll"
+{ return UP; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 49 "rtf.ll"
+{ return DOWN; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 50 "rtf.ll"
+{ return SLASH; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 51 "rtf.ll"
+{ return UNICODE_CHAR; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 52 "rtf.ll"
+{ return CMD; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 53 "rtf.ll"
+{ return HEX; }
+ YY_BREAK
+case 7:
+/* rule 7 can match eol */
+YY_RULE_SETUP
+#line 54 "rtf.ll"
+{ return IMG; }
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 55 "rtf.ll"
+{ return TXT; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 56 "rtf.ll"
+{ return TXT; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 57 "rtf.ll"
+ECHO;
+ YY_BREAK
+#line 792 "rtf.cc"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed rtfin at a new source and called
+ * rtflex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = rtfin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( rtfwrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * rtftext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of rtflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ rtfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ rtfrestart(rtfin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 33 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 33 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 32);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ rtfrestart(rtfin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( rtfwrap( ) )
+ return EOF;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve rtftext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void rtfrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ rtfensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ rtf_create_buffer(rtfin,YY_BUF_SIZE );
+ }
+
+ rtf_init_buffer(YY_CURRENT_BUFFER,input_file );
+ rtf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void rtf_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * rtfpop_buffer_state();
+ * rtfpush_buffer_state(new_buffer);
+ */
+ rtfensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ rtf_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (rtfwrap()) processing, but the only time this flag
+ * is looked at is after rtfwrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void rtf_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ rtfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE rtf_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) rtfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in rtf_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) rtfalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in rtf_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ rtf_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with rtf_create_buffer()
+ *
+ */
+ void rtf_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ rtffree((void *) b->yy_ch_buf );
+
+ rtffree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a rtfrestart() or at EOF.
+ */
+ static void rtf_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ rtf_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then rtf_init_buffer was _probably_
+ * called from rtfrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void rtf_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ rtf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void rtfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ rtfensure_buffer_stack();
+
+ /* This block is copied from rtf_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from rtf_switch_to_buffer. */
+ rtf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void rtfpop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ rtf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ rtf_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void rtfensure_buffer_stack (void)
+{
+ int num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)rtfalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)rtfrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE rtf_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) rtfalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in rtf_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ rtf_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to rtflex() will
+ * scan from a @e copy of @a str.
+ * @param str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * rtf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE rtf_scan_string (yyconst char * yy_str )
+{
+
+ return rtf_scan_bytes(yy_str,strlen(yy_str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to rtflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE rtf_scan_bytes (yyconst char * bytes, int len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) rtfalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in rtf_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = rtf_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in rtf_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up rtftext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ rtftext[rtfleng] = (yy_hold_char); \
+ (yy_c_buf_p) = rtftext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ rtfleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int rtfget_lineno (void)
+{
+
+ return rtflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *rtfget_in (void)
+{
+ return rtfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *rtfget_out (void)
+{
+ return rtfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int rtfget_leng (void)
+{
+ return rtfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *rtfget_text (void)
+{
+ return rtftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void rtfset_lineno (int line_number )
+{
+
+ rtflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see rtf_switch_to_buffer
+ */
+void rtfset_in (FILE * in_str )
+{
+ rtfin = in_str ;
+}
+
+void rtfset_out (FILE * out_str )
+{
+ rtfout = out_str ;
+}
+
+int rtfget_debug (void)
+{
+ return rtf_flex_debug;
+}
+
+void rtfset_debug (int bdebug )
+{
+ rtf_flex_debug = bdebug ;
+}
+
+/* rtflex_destroy is for both reentrant and non-reentrant scanners. */
+int rtflex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ rtf_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ rtfpop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ rtffree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *rtfalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *rtfrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void rtffree (void * ptr )
+{
+ free( (char *) ptr ); /* see rtfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+#line 57 "rtf.ll"
+
+
+
+#include "rtf2html.h"
+
+void ParStyle::clearFormatting()
+{
+ // For now, do nothing.
+ // dir is not a formatting item.
+}
+
+QString RTF2HTML::quoteString(const QString &_str, quoteMode mode)
+{
+ QString str = _str;
+ str.replace(QRegExp("&"), "&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 <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+update rtf.cc:
+flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll
+sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc
+rm -f lex.yy.c
+
+*/
+
+#define UP 1
+#define DOWN 2
+#define CMD 3
+#define TXT 4
+#define HEX 5
+#define IMG 6
+#define UNICODE_CHAR 7
+#define SKIP 8
+#define SLASH 9
+#define S_TXT 10
+
+#define YY_NEVER_INTERACTIVE 1
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_MAIN 0
+
+%}
+
+%option nounput
+%option nostack
+%option prefix="rtf"
+
+%%
+
+"{" { return UP; }
+"}" { return DOWN; }
+"\\"[\\\{\}] { return SLASH; }
+"\\u"[0-9]{3,7}[ ]?"?" { return UNICODE_CHAR; }
+"\\"[A-Za-z]+[0-9]*[ ]? { return CMD; }
+"\\'"[0-9A-Fa-f][0-9A-Fa-f] { return HEX; }
+"<##"[^>]+">" { return IMG; }
+[^\\{}<]+ { return TXT; }
+. { return TXT; }
+%%
+
+#include "rtf2html.h"
+
+void ParStyle::clearFormatting()
+{
+ // For now, do nothing.
+ // dir is not a formatting item.
+}
+
+QString RTF2HTML::quoteString(const QString &_str, quoteMode mode)
+{
+ QString str = _str;
+ str.replace(QRegExp("&"), "&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 <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RTF2HTML_H
+#define RTF2HTML_H
+
+#include <qstring.h>
+#include <stdio.h>
+
+#include <qtextcodec.h>
+#include <qcolor.h>
+#include <qregexp.h>
+#include <kdebug.h>
+
+#include <vector>
+#include <stack>
+#include <string>
+#include <stdarg.h>
+
+using namespace std;
+
+struct FontDef
+{
+ int charset;
+ string taggedName;
+ string nonTaggedName;
+};
+
+class RTF2HTML;
+
+enum TagEnum
+{
+ TAG_ALL = 0,
+ TAG_FONT_SIZE,
+ TAG_FONT_COLOR,
+ TAG_FONT_FAMILY,
+ TAG_BG_COLOR,
+ TAG_BOLD,
+ TAG_ITALIC,
+ TAG_UNDERLINE
+};
+
+class ParStyle
+{
+public:
+ ParStyle() { dir = DirLTR; }
+ void clearFormatting();
+
+public:
+ enum {DirLTR, DirRTL} dir;
+};
+
+class Level
+{
+public:
+ Level(RTF2HTML *_p);
+ Level(const Level&);
+ void setText(const char* str);
+ void setFontTbl() { m_bFontTbl = true; }
+ void setColors() { m_bColors = true; resetColors(); }
+ void setRed(unsigned char val) { setColor(val, &m_nRed); }
+ void setGreen(unsigned char val) { setColor(val, &m_nGreen); }
+ void setBlue(unsigned char val) { setColor(val, &m_nBlue); }
+ void setFont(unsigned nFont);
+ void setEncoding(unsigned nFont);
+ void setFontName();
+ void setFontColor(unsigned short color);
+ void setFontBgColor(unsigned short color);
+ void setFontSizeHalfPoints(unsigned short sizeInHalfPoints);
+ void setFontSize(unsigned short sizeInPoints);
+ void setBold(bool);
+ void setItalic(bool);
+ void setUnderline(bool);
+ void startParagraph();
+ bool isParagraphOpen() const;
+ void clearParagraphFormatting();
+ void setParagraphDirLTR();
+ void setParagraphDirRTL();
+ void addLineBreak();
+ void flush();
+ void reset();
+ void resetTag(TagEnum tag);
+protected:
+ string text;
+ void Init();
+ RTF2HTML *p;
+ void resetColors() { m_nRed = m_nGreen = m_nBlue = 0; m_bColorInit = false; }
+ void setColor(unsigned char val, unsigned char *p)
+ { *p = val; m_bColorInit=true; }
+
+ // Marks the position in m_tags where this level begun.
+ unsigned m_nTagsStartPos;
+
+ // True when parsing the fonts table
+ bool m_bFontTbl;
+ // True when parsing the colors table.
+ bool m_bColors;
+ // True when inside a 'fname' block.
+ bool m_bFontName;
+ // False until we get the tagged font name.
+ bool m_bTaggedFontNameOk;
+
+ unsigned char m_nRed;
+ unsigned char m_nGreen;
+ unsigned char m_nBlue;
+ bool m_bColorInit;
+ unsigned m_nFont; // 1-based
+ unsigned m_nEncoding;
+ unsigned m_nFontColor; // 1-based
+ unsigned m_nFontSize;
+ unsigned m_nFontBgColor; // 1-based
+ bool m_bBold;
+ bool m_bItalic;
+ bool m_bUnderline;
+};
+
+class OutTag
+{
+public:
+ OutTag(TagEnum _tag, unsigned _param) : tag(_tag), param(_param) {}
+ TagEnum tag;
+ unsigned param;
+};
+
+enum quoteMode
+{
+ quoteHTML,
+ quoteXML,
+ quoteNOBR
+};
+
+class RTF2HTML
+{
+ friend class Level;
+
+public:
+ RTF2HTML();
+ QString Parse(const char *rtf, const char *encoding);
+
+ // Paragraph-specific functions:
+
+ QString quoteString(const QString &_str, quoteMode mode = quoteHTML);
+ // Appends a string with formatting into the paragraph buffer.
+ void PrintUnquoted(const char *str, ...);
+ // Quotes and appends a string to the paragraph buffer.
+ void PrintQuoted(const QString &str);
+ // Writes down the tags from oTags into the paragraph buffer.
+ void FlushOutTags();
+ // Retrieves the top not-yet-written tag.
+ OutTag* getTopOutTag(TagEnum tagType);
+ // Writes down the paragraph buffer and resets the paragraph state.
+ void FlushParagraph();
+
+ // Document-wide functions:
+
+ void PutTag(TagEnum n)
+ {
+ tags.push(n);
+ }
+
+protected:
+
+// Paragraph members
+
+ // True if the paragraph was opened explicitly.
+ bool bExplicitParagraph;
+ // The paragraph's HTML buffer.
+ QString sParagraph;
+ // Defines the paragraph's formatting.
+ ParStyle parStyle;
+ // Tags which weren't yet printed out.
+ vector<OutTag> oTags;
+
+// Document members
+
+ // The document HTML buffer.
+ QString s;
+ // Fonts table.
+ vector<FontDef> fonts;
+ // Colors table.
+ vector<QColor> colors;
+ // Stack of tags (across all levels, not just current level)
+ stack<TagEnum> tags;
+
+// RTF parser internals
+
+ const char *rtf_ptr;
+ const char *encoding;
+ Level cur_level;
+ stack<Level> levels;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/safedelete.cpp b/kopete/protocols/groupwise/libgroupwise/safedelete.cpp
new file mode 100644
index 00000000..703e8ed3
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/safedelete.cpp
@@ -0,0 +1,139 @@
+/*
+ safedelete.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "safedelete.h"
+
+#include <qtimer.h>
+
+//----------------------------------------------------------------------------
+// SafeDelete
+//----------------------------------------------------------------------------
+SafeDelete::SafeDelete()
+{
+ lock = 0;
+}
+
+SafeDelete::~SafeDelete()
+{
+ if(lock)
+ lock->dying();
+}
+
+void SafeDelete::deleteLater(QObject *o)
+{
+ if(!lock)
+ deleteSingle(o);
+ else
+ list.append(o);
+}
+
+void SafeDelete::unlock()
+{
+ lock = 0;
+ deleteAll();
+}
+
+void SafeDelete::deleteAll()
+{
+ if(list.isEmpty())
+ return;
+
+ QObjectListIt it(list);
+ for(QObject *o; (o = it.current()); ++it)
+ deleteSingle(o);
+ list.clear();
+}
+
+void SafeDelete::deleteSingle(QObject *o)
+{
+#if QT_VERSION < 0x030000
+ // roll our own QObject::deleteLater()
+ SafeDeleteLater *sdl = SafeDeleteLater::ensureExists();
+ sdl->deleteItLater(o);
+#else
+ o->deleteLater();
+#endif
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLock
+//----------------------------------------------------------------------------
+SafeDeleteLock::SafeDeleteLock(SafeDelete *sd)
+{
+ own = false;
+ if(!sd->lock) {
+ _sd = sd;
+ _sd->lock = this;
+ }
+ else
+ _sd = 0;
+}
+
+SafeDeleteLock::~SafeDeleteLock()
+{
+ if(_sd) {
+ _sd->unlock();
+ if(own)
+ delete _sd;
+ }
+}
+
+void SafeDeleteLock::dying()
+{
+ _sd = new SafeDelete(*_sd);
+ own = true;
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLater
+//----------------------------------------------------------------------------
+SafeDeleteLater *SafeDeleteLater::self = 0;
+
+SafeDeleteLater *SafeDeleteLater::ensureExists()
+{
+ if(!self)
+ new SafeDeleteLater();
+ return self;
+}
+
+SafeDeleteLater::SafeDeleteLater()
+{
+ list.setAutoDelete(true);
+ self = this;
+ QTimer::singleShot(0, this, SLOT(explode()));
+}
+
+SafeDeleteLater::~SafeDeleteLater()
+{
+ list.clear();
+ self = 0;
+}
+
+void SafeDeleteLater::deleteItLater(QObject *o)
+{
+ list.append(o);
+}
+
+void SafeDeleteLater::explode()
+{
+ delete this;
+}
+
+#include "safedelete.moc"
+
diff --git a/kopete/protocols/groupwise/libgroupwise/safedelete.h b/kopete/protocols/groupwise/libgroupwise/safedelete.h
new file mode 100644
index 00000000..e8215c06
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/safedelete.h
@@ -0,0 +1,79 @@
+/*
+ gwclientstream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+#include<qobject.h>
+#include<qobjectlist.h>
+
+class SafeDelete;
+class SafeDeleteLock
+{
+public:
+ SafeDeleteLock(SafeDelete *sd);
+ ~SafeDeleteLock();
+
+private:
+ SafeDelete *_sd;
+ bool own;
+ friend class SafeDelete;
+ void dying();
+};
+
+class SafeDelete
+{
+public:
+ SafeDelete();
+ ~SafeDelete();
+
+ void deleteLater(QObject *o);
+
+ // same as QObject::deleteLater()
+ static void deleteSingle(QObject *o);
+
+private:
+ QObjectList list;
+ void deleteAll();
+
+ friend class SafeDeleteLock;
+ SafeDeleteLock *lock;
+ void unlock();
+};
+
+class SafeDeleteLater : public QObject
+{
+ Q_OBJECT
+public:
+ static SafeDeleteLater *ensureExists();
+ void deleteItLater(QObject *o);
+
+private slots:
+ void explode();
+
+private:
+ SafeDeleteLater();
+ ~SafeDeleteLater();
+
+ QObjectList list;
+ friend class SafeDelete;
+ static SafeDeleteLater *self;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/securestream.cpp b/kopete/protocols/groupwise/libgroupwise/securestream.cpp
new file mode 100644
index 00000000..583be03e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/securestream.cpp
@@ -0,0 +1,542 @@
+/*
+ securestream.h - Kopete Groupwise Protocol
+ Combines a ByteStream with TLS and SASL
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+/*
+ Note: SecureStream depends on the underlying security layers to signal
+ plain-to-encrypted results immediately (as opposed to waiting for the
+ event loop) so that the user cannot add/remove security layers during
+ this conversion moment. QCA::TLS and QCA::SASL behave as expected,
+ but future layers might not.
+*/
+
+#include<qguardedptr.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+
+#include"securestream.h"
+
+//----------------------------------------------------------------------------
+// LayerTracker
+//----------------------------------------------------------------------------
+LayerTracker::LayerTracker()
+{
+ p = 0;
+}
+
+void LayerTracker::reset()
+{USE_TLSHANDLER
+ p = 0;
+ list.clear();
+}
+
+void LayerTracker::addPlain(int plain)
+{
+ p += plain;
+}
+
+void LayerTracker::specifyEncoded(int encoded, int plain)
+{
+ // can't specify more bytes than we have
+ if(plain > p)
+ plain = p;
+ p -= plain;
+ Item i;
+ i.plain = plain;
+ i.encoded = encoded;
+ list += i;
+}
+
+int LayerTracker::finished(int encoded)
+{
+ int plain = 0;
+ for(QValueList<Item>::Iterator it = list.begin(); it != list.end();) {
+ Item &i = *it;
+
+ // not enough?
+ if(encoded < i.encoded) {
+ i.encoded -= encoded;
+ break;
+ }
+
+ encoded -= i.encoded;
+ plain += i.plain;
+ it = list.remove(it);
+ }
+ return plain;
+}
+
+//----------------------------------------------------------------------------
+// SecureStream
+//----------------------------------------------------------------------------
+
+SecureLayer::SecureLayer(QCA::TLS *t)
+{
+ type = TLS;
+ p.tls = t;
+ init();
+ connect(p.tls, SIGNAL(handshaken()), SLOT(tls_handshaken()));
+ connect(p.tls, SIGNAL(readyRead()), SLOT(tls_readyRead()));
+ connect(p.tls, SIGNAL(readyReadOutgoing(int)), SLOT(tls_readyReadOutgoing(int)));
+ connect(p.tls, SIGNAL(closed()), SLOT(tls_closed()));
+ connect(p.tls, SIGNAL(error(int)), SLOT(tls_error(int)));
+}
+
+SecureLayer::SecureLayer(QCA::SASL *s)
+{
+ type = SASL;
+ p.sasl = s;
+ init();
+ connect(p.sasl, SIGNAL(readyRead()), SLOT(sasl_readyRead()));
+ connect(p.sasl, SIGNAL(readyReadOutgoing(int)), SLOT(sasl_readyReadOutgoing(int)));
+ connect(p.sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+}
+
+#ifdef USE_TLSHANDLER
+SecureLayer::SecureLayer(TLSHandler *t)
+{
+ type = TLSH;
+ p.tlsHandler = t;
+ init();
+ connect(p.tlsHandler, SIGNAL(success()), SLOT(tlsHandler_success()));
+ connect(p.tlsHandler, SIGNAL(fail()), SLOT(tlsHandler_fail()));
+ connect(p.tlsHandler, SIGNAL(closed()), SLOT(tlsHandler_closed()));
+ connect(p.tlsHandler, SIGNAL(readyRead(const QByteArray &)), SLOT(tlsHandler_readyRead(const QByteArray &)));
+ connect(p.tlsHandler, SIGNAL(readyReadOutgoing(const QByteArray &, int)), SLOT(tlsHandler_readyReadOutgoing(const QByteArray &, int)));
+}
+#endif
+
+void SecureLayer::init()
+{
+ tls_done = false;
+ prebytes = 0;
+}
+
+void SecureLayer::write(const QByteArray &a)
+{
+ layer.addPlain(a.size());
+ switch(type) {
+ case TLS: { p.tls->write(a); break; }
+ case SASL: { p.sasl->write(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->write(a); break; }
+#endif
+ }
+}
+
+void SecureLayer::writeIncoming(const QByteArray &a)
+{
+ switch(type) {
+ case TLS: { p.tls->writeIncoming(a); break; }
+ case SASL: { p.sasl->writeIncoming(a); break; }
+#ifdef USE_TLSHANDLER
+ case TLSH: { p.tlsHandler->writeIncoming(a); break; }
+#endif
+ }
+}
+
+int SecureLayer::finished(int plain)
+{
+ int written = 0;
+
+ // deal with prebytes (bytes sent prior to this security layer)
+ if(prebytes > 0) {
+ if(prebytes >= plain) {
+ written += plain;
+ prebytes -= plain;
+ plain = 0;
+ }
+ else {
+ written += prebytes;
+ plain -= prebytes;
+ prebytes = 0;
+ }
+ }
+
+ // put remainder into the layer tracker
+ if(type == SASL || tls_done)
+ written += layer.finished(plain);
+
+ return written;
+}
+
+void SecureLayer::tls_handshaken()
+{
+ tls_done = true;
+ tlsHandshaken();
+}
+
+void SecureLayer::tls_readyRead()
+{
+ QByteArray a = p.tls->read();
+ readyRead(a);
+}
+
+void SecureLayer::tls_readyReadOutgoing(int plainBytes)
+{
+ QByteArray a = p.tls->readOutgoing();
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+
+void SecureLayer::tls_closed()
+{
+ QByteArray a = p.tls->readUnprocessed();
+ tlsClosed(a);
+}
+
+void SecureLayer::tls_error(int x)
+{
+ error(x);
+}
+
+void SecureLayer::sasl_readyRead()
+{
+ QByteArray a = p.sasl->read();
+ readyRead(a);
+}
+
+void SecureLayer::sasl_readyReadOutgoing(int plainBytes)
+{
+ QByteArray a = p.sasl->readOutgoing();
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+
+void SecureLayer::sasl_error(int x)
+{
+ error(x);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureLayer::tlsHandler_success()
+{
+ tls_done = true;
+ tlsHandshaken();
+}
+
+void SecureLayer::tlsHandler_fail()
+{
+ error(0);
+}
+
+void SecureLayer::tlsHandler_closed()
+{
+ tlsClosed(QByteArray());
+}
+
+void SecureLayer::tlsHandler_readyRead(const QByteArray &a)
+{
+ readyRead(a);
+}
+
+void SecureLayer::tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
+{
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+}
+#endif
+
+class SecureStream::Private
+{
+public:
+ ByteStream *bs;
+ QPtrList<SecureLayer> layers;
+ int pending;
+ int errorCode;
+ bool active;
+ bool topInProgress;
+
+ bool haveTLS() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::TLS
+#ifdef USE_TLSHANDLER
+ || s->type == SecureLayer::TLSH
+#endif
+ ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool haveSASL() const
+ {
+ QPtrListIterator<SecureLayer> it(layers);
+ for(SecureLayer *s; (s = it.current()); ++it) {
+ if(s->type == SecureLayer::SASL)
+ return true;
+ }
+ return false;
+ }
+};
+
+SecureStream::SecureStream(ByteStream *s)
+:ByteStream(0)
+{
+ d = new Private;
+
+ d->bs = s;
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+
+ d->layers.setAutoDelete(true);
+ d->pending = 0;
+ d->active = true;
+ d->topInProgress = false;
+}
+
+SecureStream::~SecureStream()
+{
+ delete d;
+}
+
+void SecureStream::linkLayer(QObject *s)
+{
+ connect(s, SIGNAL(tlsHandshaken()), SLOT(layer_tlsHandshaken()));
+ connect(s, SIGNAL(tlsClosed(const QByteArray &)), SLOT(layer_tlsClosed(const QByteArray &)));
+ connect(s, SIGNAL(readyRead(const QByteArray &)), SLOT(layer_readyRead(const QByteArray &)));
+ connect(s, SIGNAL(needWrite(const QByteArray &)), SLOT(layer_needWrite(const QByteArray &)));
+ connect(s, SIGNAL(error(int)), SLOT(layer_error(int)));
+}
+
+int SecureStream::calcPrebytes() const
+{
+ int x = 0;
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ x += s->prebytes;
+ return (d->pending - x);
+}
+
+void SecureStream::startTLSClient(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::startTLSServer(QCA::TLS *t, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ insertData(spare);
+}
+
+void SecureStream::setLayerSASL(QCA::SASL *sasl, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveSASL())
+ return;
+
+ SecureLayer *s = new SecureLayer(sasl);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+
+ insertData(spare);
+}
+
+#ifdef USE_TLSHANDLER
+void SecureStream::startTLSClient(TLSHandler *t, const QString &server, const QByteArray &spare)
+{
+ if(!d->active || d->topInProgress || d->haveTLS())
+ return;
+
+ SecureLayer *s = new SecureLayer(t);
+ s->prebytes = calcPrebytes();
+ linkLayer(s);
+ d->layers.append(s);
+ d->topInProgress = true;
+
+ // unlike QCA::TLS, TLSHandler has no return value
+ s->p.tlsHandler->startClient(server);
+
+ insertData(spare);
+}
+#endif
+
+void SecureStream::closeTLS()
+{
+ SecureLayer *s = d->layers.getLast();
+ if(s) {
+ if(s->type == SecureLayer::TLS)
+ s->p.tls->close();
+ }
+}
+
+int SecureStream::errorCode() const
+{
+ return d->errorCode;
+}
+
+bool SecureStream::isOpen() const
+{
+ return d->active;
+}
+
+void SecureStream::write(const QByteArray &a)
+{
+ if(!isOpen())
+ return;
+
+ d->pending += a.size();
+
+ // send to the last layer
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+int SecureStream::bytesToWrite() const
+{
+ return d->pending;
+}
+
+void SecureStream::bs_readyRead()
+{
+ QByteArray a = d->bs->read();
+
+ // send to the first layer
+ SecureLayer *s = d->layers.getFirst();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::bs_bytesWritten(int bytes)
+{
+ QPtrListIterator<SecureLayer> it(d->layers);
+ for(SecureLayer *s; (s = it.current()); ++it)
+ bytes = s->finished(bytes);
+
+ if(bytes > 0) {
+ d->pending -= bytes;
+ bytesWritten(bytes);
+ }
+}
+
+void SecureStream::layer_tlsHandshaken()
+{
+ d->topInProgress = false;
+ tlsHandshaken();
+}
+
+void SecureStream::layer_tlsClosed(const QByteArray &)
+{
+ d->active = false;
+ d->layers.clear();
+ tlsClosed();
+}
+
+void SecureStream::layer_readyRead(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass upwards
+ ++it;
+ s = it.current();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+}
+
+void SecureStream::layer_needWrite(const QByteArray &a)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ QPtrListIterator<SecureLayer> it(d->layers);
+ while(it.current() != s)
+ ++it;
+
+ // pass downwards
+ --it;
+ s = it.current();
+ if(s)
+ s->write(a);
+ else
+ writeRawData(a);
+}
+
+void SecureStream::layer_error(int x)
+{
+ SecureLayer *s = (SecureLayer *)sender();
+ int type = s->type;
+ d->errorCode = x;
+ d->active = false;
+ d->layers.clear();
+ if(type == SecureLayer::TLS)
+ error(ErrTLS);
+ else if(type == SecureLayer::SASL)
+ error(ErrSASL);
+#ifdef USE_TLSHANDLER
+ else if(type == SecureLayer::TLSH)
+ error(ErrTLS);
+#endif
+}
+
+void SecureStream::insertData(const QByteArray &a)
+{
+ if(!a.isEmpty()) {
+ SecureLayer *s = d->layers.getLast();
+ if(s)
+ s->writeIncoming(a);
+ else
+ incomingData(a);
+ }
+}
+
+void SecureStream::writeRawData(const QByteArray &a)
+{
+ d->bs->write(a);
+}
+
+void SecureStream::incomingData(const QByteArray &a)
+{
+ appendRead(a);
+ //qDebug( "SecureStream::incomingData() got %i bytes ", a.size() );
+
+ if(bytesAvailable())
+ readyRead();
+}
+
+#include "securestream.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/securestream.h b/kopete/protocols/groupwise/libgroupwise/securestream.h
new file mode 100644
index 00000000..36999b14
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/securestream.h
@@ -0,0 +1,156 @@
+/*
+ securestream.h - Kopete Groupwise Protocol
+ Combines a ByteStream with TLS and SASL
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SECURESTREAM_H
+#define SECURESTREAM_H
+
+#include<qca.h>
+#include "tlshandler.h"
+#include"bytestream.h"
+
+#define USE_TLSHANDLER
+
+#ifdef USE_TLSHANDLER
+ class TLSHandler;
+#endif
+
+class SecureStream : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrTLS = ErrCustom, ErrSASL };
+ SecureStream(ByteStream *s);
+ ~SecureStream();
+
+ void startTLSClient(QCA::TLS *t, const QByteArray &spare=QByteArray());
+ void startTLSServer(QCA::TLS *t, const QByteArray &spare=QByteArray());
+ void setLayerSASL(QCA::SASL *s, const QByteArray &spare=QByteArray());
+#ifdef USE_TLSHANDLER
+ void startTLSClient(TLSHandler *t, const QString &server, const QByteArray &spare=QByteArray());
+#endif
+
+ void closeTLS();
+ int errorCode() const;
+
+ // reimplemented
+ bool isOpen() const;
+ void write(const QByteArray &);
+ int bytesToWrite() const;
+
+signals:
+ void tlsHandshaken();
+ void tlsClosed();
+
+private slots:
+ void bs_readyRead();
+ void bs_bytesWritten(int);
+
+ void layer_tlsHandshaken();
+ void layer_tlsClosed(const QByteArray &);
+ void layer_readyRead(const QByteArray &);
+ void layer_needWrite(const QByteArray &);
+ void layer_error(int);
+
+private:
+ void linkLayer(QObject *);
+ int calcPrebytes() const;
+ void insertData(const QByteArray &a);
+ void writeRawData(const QByteArray &a);
+ void incomingData(const QByteArray &a);
+
+ class Private;
+ Private *d;
+};
+
+class LayerTracker
+{
+public:
+ struct Item
+ {
+ int plain;
+ int encoded;
+ };
+USE_TLSHANDLER
+ LayerTracker();
+
+ void reset();
+ void addPlain(int plain);
+ void specifyEncoded(int encoded, int plain);
+ int finished(int encoded);
+
+ int p;
+ QValueList<Item> list;
+};
+
+
+class SecureLayer : public QObject
+{
+ Q_OBJECT
+public:
+ SecureLayer(QCA::TLS *t);
+ SecureLayer(QCA::SASL *s);
+#ifdef USE_TLSHANDLER
+ SecureLayer(TLSHandler *t);
+#endif
+ void init();
+ void write(const QByteArray &a);
+ void writeIncoming(const QByteArray &a);
+ int finished(int plain);
+
+ enum { TLS, SASL, TLSH };
+ int type;
+ union {
+ QCA::TLS *tls;
+ QCA::SASL *sasl;
+#ifdef USE_TLSHANDLER
+ TLSHandler *tlsHandler;
+#endif
+ } p;
+ LayerTracker layer;
+ bool tls_done;
+ int prebytes;
+
+signals:
+ void tlsHandshaken();
+ void tlsClosed(const QByteArray &);
+ void readyRead(const QByteArray &);
+ void needWrite(const QByteArray &);
+ void error(int);
+
+private slots:
+ void tls_handshaken();
+ void tls_readyRead();
+ void tls_readyReadOutgoing(int plainBytes);
+ void tls_closed();
+ void tls_error(int x);
+ void sasl_readyRead();
+ void sasl_readyReadOutgoing(int plainBytes);
+ void sasl_error(int x);
+#ifdef USE_TLSHANDLER
+ void tlsHandler_success();
+ void tlsHandler_fail();
+ void tlsHandler_closed();
+ void tlsHandler_readyRead(const QByteArray &a);
+ void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes);
+#endif
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/stream.cpp b/kopete/protocols/groupwise/libgroupwise/stream.cpp
new file mode 100644
index 00000000..5817f4c3
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/stream.cpp
@@ -0,0 +1,31 @@
+/*
+ stream.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "stream.h"
+
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+#include "stream.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/stream.h b/kopete/protocols/groupwise/libgroupwise/stream.h
new file mode 100644
index 00000000..37a63652
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/stream.h
@@ -0,0 +1,92 @@
+/*
+ stream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdom.h>
+#include "qobject.h"
+
+#include "gwerror.h"
+#include "gwfield.h"
+#include "request.h"
+
+#ifndef GW_STREAM_H
+#define GW_STREAM_H
+
+
+class Stream : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 };
+ enum StreamCond {
+ GenericStreamError,
+ Conflict,
+ ConnectionTimeout,
+ InternalServerError,
+ InvalidFrom,
+/*# InvalidXml, // not required*/
+ PolicyViolation,
+ ResourceConstraint,
+ SystemShutdown
+ };
+
+ Stream(QObject *parent=0);
+ virtual ~Stream();
+
+ virtual void close()=0;
+ virtual int errorCondition() const=0;
+ virtual QString errorText() const=0;
+ //virtual QDomElement errorAppSpec() const=0;
+
+ /**
+ * Are there any messages waiting to be read
+ */
+ virtual bool transfersAvailable() const = 0; // adapt to messages
+ /**
+ * Read a message received from the server
+ */
+ virtual Transfer * read() = 0;
+
+ /**
+ * Send a message to the server
+ */
+ virtual void write( Request *request) = 0; // ", ends up on a send queue, by a very roundabout way, see analysis at bottom of
+
+// # virtual bool stanzaAvailable() const=0;
+// # virtual Stanza read()=0;
+// # virtual void write(const Stanza &s)=0;
+
+// # virtual QDomDocument & doc() const=0;
+// # virtual QString baseNS() const=0;
+// # virtual bool old() const=0;
+
+// # Stanza createStanza(Stanza::Kind k, const Jid &to="", const QString &type="", const QString &id="");
+// # Stanza createStanza(const QDomElement &e);
+
+// static QString xmlToString(const static XmlProtocol *foo = 0;
+//QDomElement &e, bool clip=false);
+
+signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+// void stanzaWritten();
+ void error(int);
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/task.cpp b/kopete/protocols/groupwise/libgroupwise/task.cpp
new file mode 100644
index 00000000..786bf36b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/task.cpp
@@ -0,0 +1,268 @@
+/*
+ task.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+
+#include "client.h"
+#include "gwfield.h"
+#include "request.h"
+#include "safedelete.h"
+
+#include "task.h"
+
+class Task::TaskPrivate
+{
+public:
+ TaskPrivate() {}
+
+ QString id;
+ bool success;
+ int statusCode;
+ QString statusString;
+ Client *client;
+ bool insignificant, deleteme, autoDelete;
+ bool done;
+ Transfer * transfer;
+};
+
+Task::Task(Task *parent)
+:QObject(parent)
+{
+ init();
+ d->transfer = 0;
+ d->client = parent->client();
+ d->id = client()->genUniqueId();
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::Task(Client *parent, bool)
+:QObject(0)
+{
+ init();
+
+ d->client = parent;
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::~Task()
+{
+ delete d;
+}
+
+void Task::init()
+{
+ d = new TaskPrivate;
+ d->success = false;
+ d->insignificant = false;
+ d->deleteme = false;
+ d->autoDelete = false;
+ d->done = false;
+ d->transfer = 0;
+ d->statusCode = 0;
+}
+
+Task *Task::parent() const
+{
+ return (Task *)QObject::parent();
+}
+
+Client *Task::client() const
+{
+ return d->client;
+}
+
+Transfer * Task::transfer() const
+{
+ return d->transfer;
+}
+
+void Task::setTransfer( Transfer * transfer )
+{
+ d->transfer = transfer;
+}
+
+QString Task::id() const
+{
+ return d->id;
+}
+
+bool Task::success() const
+{
+ return d->success;
+}
+
+int Task::statusCode() const
+{
+ return d->statusCode;
+}
+
+const QString & Task::statusString() const
+{
+ return d->statusString;
+}
+
+void Task::go(bool autoDelete)
+{
+ d->autoDelete = autoDelete;
+
+ onGo();
+}
+
+bool Task::take( Transfer * transfer)
+{
+ const QObjectList *p = children();
+ if(!p)
+ return false;
+
+ // pass along the transfer to our children
+ QObjectListIt it(*p);
+ Task *t;
+ for(; it.current(); ++it) {
+ QObject *obj = it.current();
+ if(!obj->inherits("Task"))
+ continue;
+
+ t = static_cast<Task*>(obj);
+
+ if(t->take( transfer ))
+ {
+ client()->debug( QString( "Transfer ACCEPTED by: %1" ).arg( t->className() ) );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Task::safeDelete()
+{
+ if(d->deleteme)
+ return;
+
+ d->deleteme = true;
+ if(!d->insignificant)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+ client()->debug( "ERROR: calling default NULL onGo() for this task, you should reimplement this!");
+}
+
+void Task::onDisconnect()
+{
+ if(!d->done) {
+ d->success = false;
+ d->statusCode = ErrDisc;
+ d->statusString = tr("Disconnected");
+
+ // delay this so that tasks that react don't block the shutdown
+ QTimer::singleShot(0, this, SLOT(done()));
+ }
+}
+
+void Task::send( Request * request )
+{
+ client()->send( request );
+}
+
+void Task::setSuccess(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = true;
+ d->statusCode = code;
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::setError(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = false;
+ d->statusCode = code;
+ if ( str.isEmpty() )
+ d->statusString = GroupWise::errorCodeToString( code );
+ else
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::done()
+{
+ debug("Task::done()");
+ if(d->done || d->insignificant)
+ return;
+ d->done = true;
+
+ if(d->deleteme || d->autoDelete)
+ d->deleteme = true;
+
+ d->insignificant = true;
+ debug("emitting finished");
+ finished();
+ d->insignificant = false;
+
+ if(d->deleteme)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::clientDisconnected()
+{
+ onDisconnect();
+}
+
+// void Task::debug(const char *fmt, ...)
+// {
+// char *buf;
+// QString str;
+// int size = 1024;
+// int r;
+//
+// do {
+// buf = new char[size];
+// va_list ap;
+// va_start(ap, fmt);
+// r = vsnprintf(buf, size, fmt, ap);
+// va_end(ap);
+//
+// if(r != -1)
+// str = QString(buf);
+//
+// delete [] buf;
+//
+// size *= 2;
+// } while(r == -1);
+//
+// debug(str);
+// }
+
+void Task::debug(const QString &str)
+{
+ client()->debug(QString("%1: ").arg(className()) + str);
+}
+
+bool Task::forMe( const Transfer * transfer ) const
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+#include "task.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/task.h b/kopete/protocols/groupwise/libgroupwise/task.h
new file mode 100644
index 00000000..0a34cafa
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/task.h
@@ -0,0 +1,97 @@
+/*
+ task.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_TASK_H
+#define GW_TASK_H
+
+#include <qobject.h>
+
+#include "gwerror.h"
+#include "gwfield.h"
+#include "transfer.h"
+
+class Client;
+class Request;
+
+class Task : public QObject
+{
+ Q_OBJECT
+public:
+ enum { ErrDisc };
+ Task(Task *parent);
+ Task( Client *, bool isRoot );
+ virtual ~Task();
+
+ Task *parent() const;
+ Client *client() const;
+ Transfer *transfer() const;
+
+ QString id() const;
+
+ bool success() const;
+ int statusCode() const;
+ const QString & statusString() const;
+
+ void go( bool autoDelete=false );
+ /**
+ * Allows a task to examine an incoming Transfer and decide whether to 'take' it
+ * for further processing.
+ */
+ virtual bool take( Transfer* transfer );
+ void safeDelete();
+
+signals:
+ void finished();
+
+protected:
+ virtual void onGo();
+ virtual void onDisconnect();
+ void send( Request * request );
+ void setSuccess( int code=0, const QString &str="" );
+ /**
+ * If an empty string is passed, this sets the error string based on the error code using GroupWise::errorCodeToString
+ */
+ void setError( int code=0, const QString &str="" );
+// void debug( const char *, ... );
+ void debug( const QString & );
+ /**
+ * Used in take() to check if the offered transfer is for this Task
+ * @return true if this Task should take the Transfer. Default impl always returns false.
+ */
+ virtual bool forMe( const Transfer * transfer ) const;
+ /**
+ * Creates a transfer with the given command and field list
+ */
+ void createTransfer( const QString & command, const Field::FieldList fields );
+ /**
+ * Direct setter for Tasks which don't have any fields
+ */
+ void setTransfer( Transfer * transfer );
+private slots:
+ void clientDisconnected();
+ void done();
+
+private:
+ void init();
+
+ class TaskPrivate;
+ TaskPrivate *d;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am
new file mode 100644
index 00000000..cf966ca2
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = -I$(top_srcdir)/protocols/groupwise/libgroupwise/qca/src \
+ -I$(srcdir)/../../libgroupwise/ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src \
+ $(all_includes)
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libgroupwise_tasks.la
+
+libgroupwise_tasks_la_SOURCES = requesttask.cpp eventtask.cpp logintask.cpp \
+ setstatustask.cpp statustask.cpp conferencetask.cpp createconferencetask.cpp \
+ sendmessagetask.cpp getdetailstask.cpp getstatustask.cpp typingtask.cpp connectiontask.cpp \
+ sendinvitetask.cpp joinconferencetask.cpp leaveconferencetask.cpp rejectinvitetask.cpp \
+ keepalivetask.cpp createcontacttask.cpp modifycontactlisttask.cpp createfoldertask.cpp \
+ movecontacttask.cpp updateitemtask.cpp createcontactinstancetask.cpp deleteitemtask.cpp \
+ updatefoldertask.cpp updatecontacttask.cpp pollsearchresultstask.cpp privacyitemtask.cpp \
+ needfoldertask.cpp searchchattask.cpp searchusertask.cpp searchusertask.h \
+ getchatsearchresultstask.cpp chatcountstask.cpp chatpropertiestask.cpp joinchattask.cpp
+noinst_HEADERS = requesttask.h eventtask.h logintask.h setstatustask.h \
+ statustask.h conferencetask.h createconferencetask.h sendmessagetask.h \
+ getdetailstask.h getstatustask.h typingtask.h connectiontask.h sendinvitetask.h \
+ joinconferencetask.h leaveconferencetask.h rejectinvitetask.h createcontacttask.h \
+ modifycontactlisttask.h createfoldertask.h movecontacttask.h updateitemtask.h deleteitemtask.h \
+ updatefoldertask.h updatecontacttask.h pollsearchresultstask.h privacyitemtask.h \
+ needfoldertask.h searchchattask.h getchatsearchresultstask.h searchusertask.h \
+ chatcountstask.h joinchattask.h
+
+
+libgroupwise_tasks_la_LDFLAGS = -no-undefined $(all_libraries)
+libgroupwise_tasks_la_LIBADD = $(LIB_QT)
+
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp
new file mode 100644
index 00000000..9e9837f7
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.cpp
@@ -0,0 +1,87 @@
+/*
+ Kopete Groupwise Protocol
+ ChatCountsTask.cpp - Task to update chatroom participant counts
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qmap.h>
+#include <kdebug.h>
+
+#include "gwfield.h"
+#include "response.h"
+
+#include "chatcountstask.h"
+
+using namespace GroupWise;
+
+ChatCountsTask::ChatCountsTask(Task* parent): RequestTask(parent)
+{
+ Field::FieldList lst;
+ createTransfer( "chatcounts", lst );
+}
+
+
+ChatCountsTask::~ChatCountsTask()
+{
+}
+
+bool ChatCountsTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ setError( response->resultCode() );
+ return true;
+ }
+
+ Field::FieldList responseFields = response->fields();
+ Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS );
+ if ( !resultsArray )
+ {
+ setError( Protocol );
+ return true;
+ }
+ Field::FieldList counts = resultsArray->fields();
+ const Field::FieldListIterator end = counts.end();
+ for ( Field::FieldListIterator it = counts.find( NM_A_FA_CHAT );
+ it != end;
+ it = counts.find( ++it, NM_A_FA_CHAT ) )
+ {
+ Field::MultiField * mf = static_cast<Field::MultiField *>( *it );
+ Field::FieldList chat = mf->fields();
+ QString roomName;
+ int participants;
+ // read the supplied fields, set metadata and status.
+ Field::SingleField * sf;
+ if ( ( sf = chat.findSingleField ( NM_A_DISPLAY_NAME ) ) )
+ roomName = sf->value().toString();
+ if ( ( sf = chat.findSingleField ( NM_A_UD_PARTICIPANTS ) ) )
+ participants = sf->value().toInt();
+
+ m_results.insert( roomName, participants );
+ }
+ return true;
+}
+
+QMap< QString, int > ChatCountsTask::results()
+{
+ return m_results;
+}
+
+#include "chatcountstask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h
new file mode 100644
index 00000000..c80a219a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatcountstask.h
@@ -0,0 +1,49 @@
+/*
+ Kopete Groupwise Protocol
+ chatcountstask.cpp - Task to update chatroom participant counts
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATCOUNTSTASK_H
+#define CHATCOUNTSTASK_H
+
+#include <qvaluelist.h>
+
+#include "gwerror.h"
+#include "gwfield.h"
+
+#include "requesttask.h"
+
+/**
+Get the current number of users in each chat on the server
+
+@author SUSE Linux Products GmbH
+ */
+class ChatCountsTask : public RequestTask
+{
+ Q_OBJECT
+ public:
+ ChatCountsTask(Task* parent);
+ ~ChatCountsTask();
+ bool take( Transfer * transfer );
+ /**
+ * Contains a list of all the chatrooms that have participants on the server. If a chatroom exists but is empty, this task does not return a result, so update the participants count to 0.
+ */
+ QMap< QString, int > results();
+ private:
+ QMap< QString, int > m_results;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp
new file mode 100644
index 00000000..66b2da42
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.cpp
@@ -0,0 +1,139 @@
+/*
+ Kopete Groupwise Protocol
+ ChatPropertiesTask.cpp - Task to update chatroom participant counts
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include "gwfield.h"
+#include "response.h"
+
+#include "chatpropertiestask.h"
+
+using namespace GroupWise;
+
+ChatPropertiesTask::ChatPropertiesTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+ChatPropertiesTask::~ChatPropertiesTask()
+{
+}
+
+void ChatPropertiesTask::setChat( const QString &displayName )
+{
+ Field::FieldList lst;
+ m_chat = displayName;
+ lst.append( new Field::SingleField( NM_A_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_chat ) );
+ createTransfer( "chatproperties", lst );
+}
+
+bool ChatPropertiesTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ setError( response->resultCode() );
+ return true;
+ }
+
+ Field::FieldList responseFields = response->fields();
+ Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_CHAT );
+ if ( !resultsArray )
+ {
+ setError( Protocol );
+ return true;
+ }
+
+ Field::FieldList lst = resultsArray->fields();
+ const Field::FieldListIterator end = lst.end();
+ for ( Field::FieldListIterator it = lst.begin();
+ it != end;
+ ++it )
+ {
+ Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it );
+ if ( sf )
+ {
+ if ( sf->tag() == NM_A_DISPLAY_NAME )
+ continue;
+ else if ( sf->tag() == NM_A_CHAT_OWNER_DN )
+ m_ownerDn = sf->value().toString();
+ else if ( sf->tag() == NM_A_CHAT_CREATOR_DN )
+ m_creatorDn= sf->value().toString();
+ else if ( sf->tag() == NM_A_DESCRIPTION )
+ m_description = sf->value().toString();
+ else if ( sf->tag() == NM_A_DISCLAIMER )
+ m_disclaimer = sf->value().toString();
+ else if ( sf->tag() == NM_A_QUERY )
+ m_query = sf->value().toString();
+ else if ( sf->tag() == NM_A_ARCHIVE )
+ m_archive = sf->value().toString();
+ else if ( sf->tag() == NM_A_SZ_TOPIC )
+ m_topic = sf->value().toString();
+ else if ( sf->tag() == NM_A_CREATION_TIME )
+ m_creationTime.setTime_t( sf->value().toInt() );
+ else if ( sf->tag() == NM_A_UD_CHAT_RIGHTS )
+ m_rights = sf->value().toInt();
+
+ }
+ else
+ {
+ Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it );
+ if ( mf )
+ {
+ if ( mf->tag() == NM_A_FA_CHAT_ACL )
+ {
+ Field::FieldList acl = mf->fields();
+ const Field::FieldListIterator aclEnd = acl.end();
+ for ( Field::FieldListIterator aclIt = acl.begin();
+ aclIt != aclEnd;
+ ++aclIt )
+ {
+ Field::MultiField * aclEntryFields = dynamic_cast<Field::MultiField *>( *aclIt );
+ if ( aclEntryFields )
+ {
+ ChatContact entry;
+ Field::FieldList entryFields = aclEntryFields->fields();
+ Field::SingleField * sf;
+ if ( ( sf = entryFields.findSingleField ( NM_A_SZ_DN ) ) )
+ entry.dn = sf->value().toString();
+ if ( ( sf = entryFields.findSingleField ( NM_A_SZ_ACCESS_FLAGS ) ) )
+ entry.chatRights = sf->value().toInt();
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "got acl entry: " << entry.dn << ", " << entry.chatRights << endl;
+ m_aclEntries.append( entry );
+ }
+
+ }
+ }
+ }
+ }
+ }
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << "Got chatroom properties: " << m_chat << " : " << m_ownerDn << ", " << m_description << ", " << m_disclaimer << ", " << m_query << ", " << m_archive << ", " << m_topic << ", " << m_creatorDn << ", " << m_creationTime.toString() << ", " << m_rights << endl;
+ finished();
+ return true;
+}
+
+QValueList< ChatContact > ChatPropertiesTask::aclEntries()
+{
+ return m_aclEntries;
+}
+
+#include "chatpropertiestask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h
new file mode 100644
index 00000000..c9f890dd
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/chatpropertiestask.h
@@ -0,0 +1,64 @@
+/*
+ Kopete Groupwise Protocol
+ chatcountstask.cpp - Task to update chatroom participant counts
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATPROPERTIESTASK_H
+#define CHATPROPERTIESTASK_H
+
+#include <qdatetime.h>
+#include <qvaluelist.h>
+#include "gwchatrooms.h"
+#include "gwerror.h"
+#include "gwfield.h"
+
+#include "requesttask.h"
+
+/**
+Get the current number of users in each chat on the server
+
+@author SUSE Linux Products GmbH
+ */
+class ChatPropertiesTask : public RequestTask
+{
+ Q_OBJECT
+ public:
+ ChatPropertiesTask(Task* parent);
+ ~ChatPropertiesTask();
+ /**
+ * Specify which chatroom to get properties for
+ */
+ void setChat( const QString & );
+ bool take( Transfer * transfer );
+ /**
+ * Contains a list of the ACL entries for the specified chatroom
+ */
+ QValueList< GroupWise::ChatContact > aclEntries();
+ QString m_chat;
+ QString m_ownerDn;
+ QString m_description;
+ QString m_disclaimer;
+ QString m_query;
+ QString m_archive;
+ QString m_maxUsers;
+ QString m_topic;
+ QString m_creatorDn;
+ QDateTime m_creationTime;
+ uint m_rights;
+ QValueList< GroupWise::ChatContact > m_aclEntries;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp
new file mode 100644
index 00000000..9773a622
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.cpp
@@ -0,0 +1,230 @@
+/*
+ Kopete Groupwise Protocol
+ conferencetask.cpp - Event Handling task responsible for all conference related events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "userdetailsmanager.h"
+
+#include "conferencetask.h"
+
+ConferenceTask::ConferenceTask( Task* parent )
+ : EventTask( parent )
+{
+ // register all the events that this task monitors
+ registerEvent( GroupWise::ConferenceClosed );
+ registerEvent( GroupWise::ConferenceJoined );
+ registerEvent( GroupWise::ConferenceLeft );
+ registerEvent( GroupWise::ReceiveMessage );
+ registerEvent( GroupWise::UserTyping );
+ registerEvent( GroupWise::UserNotTyping );
+ registerEvent( GroupWise::ConferenceInvite );
+ registerEvent( GroupWise::ConferenceInviteNotify );
+ registerEvent( GroupWise::ConferenceReject );
+ registerEvent( GroupWise::ReceiveAutoReply );
+ // GW7
+ registerEvent( GroupWise::ReceivedBroadcast );
+ registerEvent( GroupWise::ReceivedSystemBroadcast );
+
+ // listen to the UserDetailsManager telling us that user details are available
+ connect( client()->userDetailsManager(), SIGNAL( gotContactDetails( const GroupWise::ContactDetails & ) ),
+ SLOT( slotReceiveUserDetails( const GroupWise::ContactDetails & ) ) );
+}
+
+
+ConferenceTask::~ConferenceTask()
+{
+}
+
+void ConferenceTask::dumpConferenceEvent( ConferenceEvent & evt )
+{
+ client()->debug( QString( "Conference Event - guid: %1 user: %2 timestamp: %3:%4:%5" ).arg
+ ( evt.guid ).arg( evt.user.ascii() ).arg( evt.timeStamp.time().hour() ).arg
+ ( evt.timeStamp.time().minute() ).arg( evt.timeStamp.time().second() ) );
+ client()->debug( QString( " flags: %1" ).arg( evt.flags, 8 ) );
+}
+
+bool ConferenceTask::take( Transfer * transfer )
+{
+ EventTransfer * incomingEvent;
+ if ( forMe( transfer, incomingEvent ) )
+ {
+ client()->debug( "Got a conference event:" );
+ ConferenceEvent event;
+ event.type = (GroupWise::Event)( incomingEvent->eventType() );
+ event.timeStamp = incomingEvent->timeStamp();
+ event.user = incomingEvent->source();
+ event.flags = 0;
+ Q_ASSERT( incomingEvent->hasGuid() );
+ event.guid = incomingEvent->guid();
+
+ switch ( event.type )
+ {
+ case GroupWise::ConferenceClosed:
+ // extra debug - we never see these events, against spec.
+ client()->debug( "********************" );
+ client()->debug( "* ConferenceClosed *" );
+ client()->debug( "* ConferenceClosed *" );
+ client()->debug( "* ConferenceClosed *" );
+ client()->debug( "********************" );
+ emit closed( event );
+ break;
+ case GroupWise::ConferenceJoined:
+ Q_ASSERT( incomingEvent->hasFlags() );
+ event.flags = incomingEvent->flags();
+ client()->debug( "ConferenceJoined" );
+ if ( !queueWhileAwaitingData( event ) )
+ emit joined( event );
+ break;
+ case GroupWise::ConferenceLeft:
+ Q_ASSERT( incomingEvent->hasFlags() );
+ event.flags = incomingEvent->flags();
+ client()->debug( "ConferenceLeft" );
+ emit left( event );
+ break;
+ case GroupWise::ReceiveMessage:
+ Q_ASSERT( incomingEvent->hasFlags() );
+ event.flags = incomingEvent->flags();
+ Q_ASSERT( incomingEvent->hasMessage() );
+ event.message = incomingEvent->message();
+ client()->debug( "ReceiveMessage" );
+ client()->debug( QString( "message: %1" ).arg( event.message ) );
+ if ( !queueWhileAwaitingData( event ) )
+ emit message( event );
+ break;
+ case GroupWise::UserTyping:
+ client()->debug( "UserTyping" );
+ emit typing( event );
+ break;
+ case GroupWise::UserNotTyping:
+ client()->debug( "UserNotTyping" );
+ emit notTyping( event );
+ break;
+ case GroupWise::ConferenceInvite:
+ Q_ASSERT( incomingEvent->hasMessage() );
+ event.message = incomingEvent->message();
+ client()->debug( "ConferenceInvite" );
+ client()->debug( QString( "message: %1" ).arg( event.message ) );
+ if ( !queueWhileAwaitingData( event ) )
+ emit invited( event );
+ break;
+ case GroupWise::ConferenceInviteNotify:
+ client()->debug( "ConferenceInviteNotify" );
+ if ( !queueWhileAwaitingData( event ) )
+ emit otherInvited( event );
+ break;
+ case GroupWise::ConferenceReject:
+ client()->debug( "ConferenceReject" );
+ if ( !queueWhileAwaitingData( event ) )
+ emit invitationDeclined( event );
+ break;
+ case GroupWise::ReceiveAutoReply:
+ Q_ASSERT( incomingEvent->hasFlags() );
+ event.flags = incomingEvent->flags();
+ Q_ASSERT( incomingEvent->hasMessage() );
+ event.message = incomingEvent->message();
+ client()->debug( "ReceiveAutoReply" );
+ client()->debug( QString( "message: %1" ).arg( event.message.ascii() ) );
+ emit autoReply( event );
+ break;
+ case GroupWise::ReceivedBroadcast:
+ Q_ASSERT( incomingEvent->hasMessage() );
+ event.message = incomingEvent->message();
+ client()->debug( "ReceivedBroadCast" );
+ client()->debug( QString( "message: %1" ).arg( event.message ) );
+ if ( !queueWhileAwaitingData( event ) )
+ emit broadcast( event );
+ break;
+ case GroupWise::ReceivedSystemBroadcast:
+ Q_ASSERT( incomingEvent->hasMessage() );
+ event.message = incomingEvent->message();
+ client()->debug( "ReceivedSystemBroadCast" );
+ client()->debug( QString( "message: %1" ).arg( event.message ) );
+ emit systemBroadcast( event );
+ break;
+ default:
+ client()->debug( QString( "WARNING: didn't handle registered event %1, on conference %2" ).arg( incomingEvent->eventType() ).arg( event.guid.ascii() ) );
+ }
+ dumpConferenceEvent( event );
+
+ return true;
+ }
+ return false;
+}
+
+void ConferenceTask::slotReceiveUserDetails( const GroupWise::ContactDetails & details )
+{
+ client()->debug( "ConferenceTask::slotReceiveUserDetails()" );
+
+ // dequeue any events which are deliverable now we have these details
+ QValueListIterator< ConferenceEvent > end = m_pendingEvents.end();
+ QValueListIterator< ConferenceEvent > it = m_pendingEvents.begin();
+ while ( it != end )
+ {
+ QValueListIterator< ConferenceEvent > current = it;
+ ++it;
+ // if the details relate to event, try again to handle it
+ if ( details.dn == (*current).user )
+ {
+ client()->debug( QString( " - got details for event involving %1" ).arg( (*current).user ) );
+ switch ( (*current).type )
+ {
+ case GroupWise::ConferenceJoined:
+ client()->debug( "ConferenceJoined" );
+ emit joined( *current );
+ break;
+ case GroupWise::ReceiveMessage:
+ client()->debug( "ReceiveMessage" );
+ emit message( *current );
+ break;
+ case GroupWise::ConferenceInvite:
+ client()->debug( "ConferenceInvite" );
+ emit invited( *current );
+ break;
+ case GroupWise::ConferenceInviteNotify:
+ client()->debug( "ConferenceInviteNotify" );
+ emit otherInvited( *current );
+ break;
+ default:
+ client()->debug( "Queued an event while waiting for more data, but didn't write a handler for the dequeue!" );
+ }
+ m_pendingEvents.remove( current );
+ client()->debug( QString( "Event handled - now %1 pending events" ).arg
+ ( (uint)m_pendingEvents.count() ) );
+ }
+ }
+}
+
+
+bool ConferenceTask::queueWhileAwaitingData( const ConferenceEvent & event )
+{
+ if ( client()->userDetailsManager()->known( event.user ) )
+ {
+ client()->debug( "ConferenceTask::queueWhileAwaitingData() - source is known!" );
+ return false;
+ }
+ else
+ {
+ client()->debug( QString( "ConferenceTask::queueWhileAwaitingData() - queueing event involving %1" ).arg( event.user ) );
+ client()->userDetailsManager()->requestDetails( event.user );
+ m_pendingEvents.append( event );
+ return true;
+ }
+}
+
+#include "conferencetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h
new file mode 100644
index 00000000..42f4fc2b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/conferencetask.h
@@ -0,0 +1,74 @@
+/*
+ Kopete Groupwise Protocol
+ conferencetask.h - Event Handling task responsible for all conference related events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CONFERENCETASK_H
+#define CONFERENCETASK_H
+
+#include "gwerror.h"
+#include "eventtask.h"
+
+/**
+ * This Task is responsible for handling all conference related events, and signalling them up to @ref GroupWiseAccount
+ * Implementation note: It would be fit the model more cleanly to have each of these in their own Task, but the amount
+ * of code they share is quite large, and the differences in the way each event uses it are small
+ * @author SUSE AG
+ */
+
+using namespace GroupWise;
+
+class ConferenceTask : public EventTask
+{
+Q_OBJECT
+public:
+ ConferenceTask( Task* parent );
+ ~ConferenceTask();
+ bool take( Transfer * transfer );
+signals:
+ void typing( const ConferenceEvent & );
+ void notTyping( const ConferenceEvent & );
+ void joined( const ConferenceEvent & );
+ void left( const ConferenceEvent &);
+ void invited( const ConferenceEvent & );
+ void otherInvited( const ConferenceEvent & );
+ void invitationDeclined( const ConferenceEvent & );
+ void closed( const ConferenceEvent & );
+ void message( const ConferenceEvent &);
+ void autoReply( const ConferenceEvent & );
+ // GW7
+ void broadcast( const ConferenceEvent &);
+ void systemBroadcast( const ConferenceEvent &);
+protected slots:
+ void slotReceiveUserDetails( const GroupWise::ContactDetails & );
+protected:
+ Q_UINT32 readFlags( QDataStream & din );
+ QString readMessage( QDataStream & din );
+ /**
+ * Checks to see if we need more data from the client before we can propagate this event
+ * and queues the event if so
+ * @return whether the event was queued pending more data
+ */
+ bool queueWhileAwaitingData( const ConferenceEvent & event );
+ void dumpConferenceEvent( ConferenceEvent & evt );
+private:
+ // A list of events which are waiting for more data from the server before they can be exposed to the client
+ QValueList< ConferenceEvent > m_pendingEvents;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp
new file mode 100644
index 00000000..3d041208
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.cpp
@@ -0,0 +1,55 @@
+/*
+ Kopete Groupwise Protocol
+ connectiontask.cpp - Event Handling task responsible for all connection related events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "client.h"
+
+#include "connectiontask.h"
+
+ConnectionTask::ConnectionTask(Task* parent): EventTask(parent)
+{
+ registerEvent( GroupWise::UserDisconnect );
+ registerEvent( GroupWise::ServerDisconnect );
+}
+
+
+ConnectionTask::~ConnectionTask()
+{
+}
+
+bool ConnectionTask::take( Transfer * transfer )
+{
+ EventTransfer * incomingEvent;
+ if ( forMe( transfer, incomingEvent ) )
+ {
+ client()->debug( "Got a connection event:" );
+ switch ( incomingEvent->eventType() )
+ {
+ case GroupWise::UserDisconnect:
+ emit connectedElsewhere();
+ break;
+ case GroupWise::ServerDisconnect:
+ emit serverDisconnect();
+ break;
+ }
+ return true;
+ }
+ return false;
+}
+
+#include "connectiontask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h
new file mode 100644
index 00000000..95df34f9
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/connectiontask.h
@@ -0,0 +1,43 @@
+/*
+ Kopete Groupwise Protocol
+ connectiontask.h - Event Handling task responsible for all connection related events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CONNECTIONTASK_H
+#define CONNECTIONTASK_H
+
+#include "eventtask.h"
+
+/**
+This task monitors connection related events, currently 'connected elsewhere' disconnects and server disconnect notification.
+
+@author Kopete Developers
+*/
+class ConnectionTask : public EventTask
+{
+Q_OBJECT
+public:
+ ConnectionTask(Task* parent);
+ ~ConnectionTask();
+ bool take( Transfer * transfer );
+signals:
+ void connectedElsewhere();
+ void serverDisconnect();
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp
new file mode 100644
index 00000000..8be16888
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.cpp
@@ -0,0 +1,85 @@
+/*
+ Kopete Groupwise Protocol
+ createconferencetask.cpp - Request task that creates conferences on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "response.h"
+
+
+#include "createconferencetask.h"
+
+CreateConferenceTask::CreateConferenceTask(Task* parent): RequestTask(parent), m_confId( 0 ), m_guid( BLANK_GUID )
+{
+
+}
+
+CreateConferenceTask::~CreateConferenceTask()
+{
+}
+
+void CreateConferenceTask::conference( const int confId, const QStringList &participants )
+{
+ m_confId = confId;
+ Field::FieldList lst, tmp;
+ // list containing blank GUID
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, m_guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ // series of participants (may be empty )
+ QValueListConstIterator<QString> end = participants.end();
+ for ( QValueListConstIterator<QString> it = participants.begin(); it != end; ++it )
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) );
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, client()->userDN() ) );
+ createTransfer( "createconf", lst );
+}
+
+bool CreateConferenceTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+
+ // if the createconf was successful, read the GUID and store it
+ Field::FieldList responseFields = response->fields();
+ if ( response->resultCode() == GroupWise::None )
+ {
+ Field::MultiField * listField = responseFields.findMultiField( NM_A_FA_CONVERSATION );
+ Field::FieldList guidList = listField->fields();
+ Field::SingleField * guidField = guidList.findSingleField( NM_A_SZ_OBJECT_ID );
+ m_guid = guidField->value().toString();
+ setSuccess();
+ }
+ else
+ setError( response->resultCode() );
+ return true;
+
+}
+
+GroupWise::ConferenceGuid CreateConferenceTask::conferenceGUID() const
+{
+ return m_guid;
+}
+
+int CreateConferenceTask::clientConfId() const
+{
+ return m_confId;
+}
+
+#include "createconferencetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h
new file mode 100644
index 00000000..48d5702e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createconferencetask.h
@@ -0,0 +1,54 @@
+/*
+ Kopete Groupwise Protocol
+ createconferencetask.h - Request task that creates conferences on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CREATECONFERENCETASK_H
+#define CREATECONFERENCETASK_H
+
+#include "requesttask.h"
+
+/**
+This task is responsible for creating a conference at the server, and confirming that the server allowed the conference to be created.
+
+@author SUSE AG
+*/
+class CreateConferenceTask : public RequestTask
+{
+Q_OBJECT
+public:
+ CreateConferenceTask(Task* parent);
+ ~CreateConferenceTask();
+ /**
+ * Set up a create conference request
+ * @param confId The client-unique conference Id.
+ * @param participants A list of Novell DNs of the people taking part in the conference.
+ */
+ void conference( const int confId, const QStringList &participants );
+ bool take( Transfer * transfer );
+ int clientConfId() const;
+ GroupWise::ConferenceGuid conferenceGUID() const;
+
+signals:
+ void created( const GroupWise::ConferenceGuid & guid );
+private:
+ int m_confId; // the conference id given us before making the request
+ ConferenceGuid m_guid;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp
new file mode 100644
index 00000000..832b5900
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.cpp
@@ -0,0 +1,97 @@
+/*
+ Kopete Groupwise Protocol
+ createcontactinstancetask.h - Request Task that creates an instance of a contact on the server side contact list
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "client.h"
+
+#include "createcontactinstancetask.h"
+
+CreateContactInstanceTask::CreateContactInstanceTask(Task* parent) : NeedFolderTask(parent)
+{
+ // make the client tell the client app (Kopete) when we receive a contact
+ connect( this, SIGNAL( gotContactAdded( const ContactItem & ) ), client(), SIGNAL( contactReceived( const ContactItem & ) ) );
+}
+
+CreateContactInstanceTask::~CreateContactInstanceTask()
+{
+}
+
+void CreateContactInstanceTask::contactFromUserId( const QString & userId, const QString & displayName, const int parentFolder )
+{
+ contact( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, userId ), displayName, parentFolder );
+}
+
+void CreateContactInstanceTask::contactFromUserIdAndFolder( const QString & userId, const QString & displayName, const int folderSequence, const QString & folderDisplayName )
+{
+ // record the user details
+ m_userId = userId;
+ m_displayName = displayName;
+ // record the folder details
+ m_folderSequence = folderSequence;
+ m_folderDisplayName = folderDisplayName;
+}
+
+void CreateContactInstanceTask::contactFromDN( const QString & dn, const QString & displayName, const int parentFolder )
+{
+ contact( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, dn ), displayName, parentFolder );
+}
+
+void CreateContactInstanceTask::contactFromDNAndFolder( const QString & dn, const QString & displayName, const int folderSequence, const QString & folderDisplayName )
+{
+ // record the user details
+ m_dn = dn;
+ m_displayName = displayName;
+ // record the folder details
+ m_folderSequence = folderSequence;
+ m_folderDisplayName = folderDisplayName;
+}
+
+void CreateContactInstanceTask::contact( Field::SingleField * id, const QString & displayName, const int parentFolder )
+{
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentFolder ) ) );
+ // this is either a user Id or a DN
+ lst.append( id );
+ if ( displayName.isEmpty() ) // fallback so that the contact is created
+ lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, m_dn ) );
+ else
+ lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, displayName ) );
+ createTransfer( "createcontact", lst );
+}
+
+void CreateContactInstanceTask::onGo()
+{
+ // are we creating a folder first or can we just proceed as normal?
+ if ( m_folderDisplayName.isEmpty() )
+ RequestTask::onGo();
+ else // create the folder, when the folder has been created, onFolderCreated gets called and creates the contact
+ createFolder();
+}
+
+void CreateContactInstanceTask::onFolderCreated()
+{
+ // now the folder exists, perform the requested type of contact instance creation
+ if ( m_userId.isEmpty() )
+ contact( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, m_dn ), m_displayName, m_folderId );
+ else
+ contact( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, m_userId ), m_displayName, m_folderId );
+ // send the transfer immediately
+ RequestTask::onGo();
+}
+
+#include "createcontactinstancetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h
new file mode 100644
index 00000000..d6be5933
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontactinstancetask.h
@@ -0,0 +1,54 @@
+/*
+ Kopete Groupwise Protocol
+ createcontactinstancetask.h - Request Task that creates an instance of a contact on the server side contact list
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CreateContactInstanceTask_H
+#define CreateContactInstanceTask_H
+
+#include "needfoldertask.h"
+
+/**
+Creates a contact on the server. The response to this action is handled by its parent
+
+@author SUSE AG
+*/
+class CreateContactInstanceTask : public NeedFolderTask
+{
+Q_OBJECT
+public:
+ CreateContactInstanceTask(Task* parent);
+ ~CreateContactInstanceTask();
+ /**
+ * Sets up the request message.
+ */
+ void contactFromUserId( const QString & userId, const QString & displayName, const int parentFolder );
+ void contactFromDN( const QString & dn, const QString & displayName, const int parentFolder );
+ void contactFromUserIdAndFolder( const QString & userId, const QString & displayName, const int folderSequence, const QString & folderDisplayName );
+ void contactFromDNAndFolder( const QString & dn, const QString & displayName, const int folderSequence, const QString & folderDisplayName );
+ void onGo();
+protected:
+ void contact( Field::SingleField * id, const QString & displayName, const int parentFolder );
+ void onFolderCreated();
+private:
+ QString m_userId;
+ QString m_dn;
+ QString m_displayName;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp
new file mode 100644
index 00000000..aac16042
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.cpp
@@ -0,0 +1,144 @@
+/*
+ Kopete Groupwise Protocol
+ createcontacttask.cpp - high level task responsible for creating both a contact and any folders it belongs to locally, on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "createfoldertask.h"
+#include "createcontactinstancetask.h"
+
+#include "createcontacttask.h"
+
+CreateContactTask::CreateContactTask(Task* parent): Task(parent)
+{
+}
+
+CreateContactTask::~CreateContactTask()
+{
+}
+
+QString CreateContactTask::userId()
+{
+ return m_userId;
+}
+
+QString CreateContactTask::dn()
+{
+ return m_dn;
+}
+
+QString CreateContactTask::displayName()
+{
+ return m_displayName;
+}
+
+bool CreateContactTask::take( Transfer * transfer )
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+void CreateContactTask::contactFromUserId( const QString & userId, const QString & displayName, const int firstSeqNo, const QValueList< FolderItem > folders, bool topLevel )
+{
+ m_userId = userId;
+ m_displayName = displayName;
+ m_firstSequenceNumber = firstSeqNo;
+ m_folders = folders;
+ m_topLevel = topLevel;
+}
+
+void CreateContactTask::onGo()
+{
+ client()->debug( "CreateContactTask::onGo() - Welcome to the Create Contact Task Show!");
+ QValueList<FolderItem>::ConstIterator it = m_folders.begin();
+ const QValueList<FolderItem>::ConstIterator end = m_folders.end();
+
+ // create contacts on the server
+ for ( ; it != end; ++it )
+ {
+ client()->debug( QString( " - contact is in folder %1 with id %2" ).arg( (*it).name ).arg( (*it).id ) );
+ CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() );
+ // the add contact action may cause other contacts' sequence numbers to change
+ // CreateContactInstanceTask signals these changes, so we propagate the signal via the Client, to the GroupWiseAccount
+ // This updates our local versions of those contacts using the same mechanism by which they are updated at login.
+ connect( ccit, SIGNAL( gotContactAdded( const ContactItem & ) ), SLOT( slotContactAdded( const ContactItem & ) ) );
+ connect( ccit, SIGNAL( finished() ), SLOT( slotCheckContactInstanceCreated() ) );
+ if ( (*it).id == 0 ) // caller asserts that this isn't on the server...
+ {
+ ccit->contactFromDNAndFolder( m_userId, m_displayName, m_firstSequenceNumber++, ( *it ).name );
+ }
+ else
+ ccit->contactFromDN( m_userId, m_displayName, (*it).id );
+
+ ccit->go( true );
+ }
+
+ if ( m_topLevel )
+ {
+ client()->debug( " - contact is in top level folder " );
+ CreateContactInstanceTask * ccit = new CreateContactInstanceTask( client()->rootTask() );
+ connect( ccit, SIGNAL( gotContactAdded( const ContactItem & ) ), SLOT( slotContactAdded( const ContactItem & ) ) );
+ connect( ccit, SIGNAL( finished() ), SLOT( slotCheckContactInstanceCreated() ) );
+ ccit->contactFromDN( m_userId, m_displayName, 0 );
+ ccit->go( true );
+ }
+ client()->debug( "CreateContactTask::onGo() - DONE" );
+}
+
+void CreateContactTask::slotContactAdded( const ContactItem & addedContact )
+{
+ client()->debug( "CreateContactTask::slotContactAdded()" );
+ // as each contact instance has been added on the server,
+ // remove the folderitem it belongs in.
+ // once the list is empty, we have been successful
+
+ if ( addedContact.displayName != m_displayName )
+ {
+ client()->debug( " - addedContact is not the one we were trying to add, ignoring it ( Account will update it )" );
+ return;
+ }
+ client()->debug( QString( "CreateContactTask::slotContactAdded() - Contact Instance %1 was created on the server, with objectId %2 in folder %3" ).arg
+ ( addedContact.displayName ).arg( addedContact.id ).arg( addedContact.parentId ) );
+
+ if ( m_dn.isEmpty() )
+ m_dn = addedContact.dn;
+
+
+ if ( !m_folders.isEmpty() )
+ m_folders.pop_back();
+
+ // clear the topLevel flag once the corresponding server side entry has been successfully created
+ if ( addedContact.parentId == 0 )
+ m_topLevel = false;
+
+ if ( m_folders.isEmpty() && !m_topLevel )
+ {
+ client()->debug( "CreateContactTask::slotContactAdded() - All contacts were created on the server, we're finished!" );
+ setSuccess();
+ }
+}
+void CreateContactTask::slotCheckContactInstanceCreated()
+{
+ CreateContactInstanceTask * ccit = ( CreateContactInstanceTask * )sender();
+ if ( !ccit->success() )
+ {
+ setError( ccit->statusCode(), ccit->statusString() );
+ }
+}
+
+#include "createcontacttask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h
new file mode 100644
index 00000000..a9e4ab06
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createcontacttask.h
@@ -0,0 +1,88 @@
+/*
+ Kopete Groupwise Protocol
+ createcontacttask.cpp - high level task responsible for creating both a contact and any folders it belongs to locally, on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CREATECONTACTTASK_H
+#define CREATECONTACTTASK_H
+
+#include <qvaluelist.h>
+
+#include "gwerror.h"
+
+#include "task.h"
+
+using namespace GroupWise;
+
+/**
+ Creates a contact on the server, as well as any folders that do not exist on the server, and add the contact to those folders.
+ This is a meta-task to suit Kopete. If you maintain your own copy of the server side contact list and follow the server's
+ contact semantics (contact instances rather than contacts in the contact list), you can just use CreateContactInstanceTask.
+ This task causes the @ref Client to emit folderReceived() and contactReceived() as the task proceeds. Kopete processes these
+ signals as usual, because it created the contact optimistically, before invoking this task.
+
+ The finished() signal indicates the whole procedure has completed and the sender can be queried for success as usual
+@author SUSE AG
+*/
+class CreateContactTask : public Task
+{
+Q_OBJECT
+public:
+ CreateContactTask(Task* parent);
+ ~CreateContactTask();
+ /**
+ * Get the userId of the contact just created
+ */
+ QString userId();
+ /**
+ * Get the DN of the contact just created
+ */
+ QString dn();
+ QString displayName();
+
+ /**
+ * Sets up the task.
+ * @param userId the user Id of the contact to create
+ * @param displayName the display name we should give to this contact
+ * @param firstSeqNo Used to create the folders - the first unused folder sequence number we know of
+ * @param folders A list of folders that the contact should belong to - any folders that do not exist on the server should have a objectId of 0, and will be created
+ * @param topLevel is the folder also in the top level folder?
+ */
+ void contactFromUserId( const QString & userId, const QString & displayName, const int firstSeqNo, const QValueList< FolderItem > folders, bool topLevel );
+ //void contactFromDN( const QString & dn, const QString & displayName, const int parentFolder );
+ /**
+ * This task doesn't do any I/O itself, so this take prints an error and returns false;
+ */
+ bool take( Transfer * );
+ /**
+ * Starts off the whole process
+ */
+ void onGo();
+protected slots:
+ void slotContactAdded( const ContactItem & );
+ void slotCheckContactInstanceCreated();
+private:
+ int m_firstSequenceNumber;
+ QString m_userId;
+ QString m_dn;
+ QString m_displayName;
+ QValueList< FolderItem > m_folders;
+ bool m_topLevel;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp
new file mode 100644
index 00000000..c7e9933b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.cpp
@@ -0,0 +1,41 @@
+/*
+ Kopete Groupwise Protocol
+ createfoldertask.h - Request Task for creating a single folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "createfoldertask.h"
+
+CreateFolderTask::CreateFolderTask(Task* parent): ModifyContactListTask(parent)
+{
+}
+
+
+CreateFolderTask::~CreateFolderTask()
+{
+}
+
+void CreateFolderTask::folder( const int parentId, const int sequence, const QString & displayName )
+{
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentId ) ) );
+ lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, displayName ) );
+ lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, QString::number( sequence ) ) );
+ createTransfer( "createfolder", lst );
+}
+
+#include "createfoldertask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h
new file mode 100644
index 00000000..f3c6ebb9
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/createfoldertask.h
@@ -0,0 +1,40 @@
+/*
+ Kopete Groupwise Protocol
+ createfoldertask.h - Request Task for creating a single folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CREATEFOLDERTASK_H
+#define CREATEFOLDERTASK_H
+
+#include "modifycontactlisttask.h"
+
+/**
+Creates a folder on the server
+
+@author SUSE AG
+*/
+class CreateFolderTask : public ModifyContactListTask
+{
+Q_OBJECT
+public:
+ CreateFolderTask(Task* parent);
+ ~CreateFolderTask();
+ void folder( const int parentId, const int sequence, const QString & displayName );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp
new file mode 100644
index 00000000..89480d10
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.cpp
@@ -0,0 +1,46 @@
+/*
+ Kopete Groupwise Protocol
+ deleteitemtask.cpp - Delete a contact or folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "deleteitemtask.h"
+
+DeleteItemTask::DeleteItemTask(Task* parent): ModifyContactListTask(parent)
+{
+}
+
+
+DeleteItemTask::~DeleteItemTask()
+{
+}
+
+void DeleteItemTask::item( const int parentFolder, const int objectId )
+{
+ if ( objectId == 0 )
+ {
+ setError( 1, "Can't delete the root folder" );
+ return;
+ }
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( parentFolder ) ) );
+ // this is either a user Id or a DN
+ lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( objectId ) ) );
+ createTransfer( "deletecontact", lst );
+}
+
+#include "deleteitemtask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h
new file mode 100644
index 00000000..f249c2f5
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/deleteitemtask.h
@@ -0,0 +1,38 @@
+/*
+ Kopete Groupwise Protocol
+ deleteitemtask.h - Delete a contact or folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DELETEITEMTASK_H
+#define DELETEITEMTASK_H
+
+#include "modifycontactlisttask.h"
+
+/**
+@author SUSE AG
+*/
+class DeleteItemTask : public ModifyContactListTask
+{
+Q_OBJECT
+public:
+ DeleteItemTask(Task* parent);
+ ~DeleteItemTask();
+ void item( const int parentFolder, const int objectId );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp
new file mode 100644
index 00000000..c6bd2d85
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.cpp
@@ -0,0 +1,48 @@
+/*
+ Kopete Groupwise Protocol
+ eventtask.cpp - Ancestor of all Event Handling tasks
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwfield.h"
+#include "eventtask.h"
+
+EventTask::EventTask( Task * parent )
+: Task( parent )
+{
+}
+
+void EventTask::registerEvent( GroupWise::Event e )
+{
+ m_eventCodes.append( e );
+}
+
+bool EventTask::forMe( Transfer * transfer, EventTransfer*& event ) const
+{
+ // see if we can down-cast transfer to an EventTransfer
+ /*EventTransfer * */
+ event = dynamic_cast<EventTransfer *>(transfer);
+ if ( event )
+ {
+ // see if we are supposed to handle this kind of event
+ // consider speeding this up by having 1 handler per event
+ return ( m_eventCodes.find( event->eventType() ) != m_eventCodes.end() );
+ }
+ return false;
+}
+
+#include "eventtask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h
new file mode 100644
index 00000000..50b84ac5
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/eventtask.h
@@ -0,0 +1,43 @@
+/*
+ Kopete Groupwise Protocol
+ eventtask.h - Ancestor of all Event Handling tasks
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_EVENTTASK_H
+#define GW_EVENTTASK_H
+
+#include <qvaluelist.h>
+
+#include "eventtransfer.h"
+#include "task.h"
+
+class Transfer;
+
+class EventTask : public Task
+{
+Q_OBJECT
+ public:
+ EventTask( Task *parent );
+ protected:
+ bool forMe( Transfer * transfer, EventTransfer *& event ) const;
+ void registerEvent( GroupWise::Event e );
+ private:
+ QValueList<int> m_eventCodes;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp
new file mode 100644
index 00000000..fe1d61f9
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.cpp
@@ -0,0 +1,122 @@
+/*
+ Kopete Groupwise Protocol
+ getchatsearchresultstask.cpp - Poll the server to see if it has processed our search yet.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include "gwfield.h"
+#include "response.h"
+
+#include "logintask.h"
+
+#include "getchatsearchresultstask.h"
+
+using namespace GroupWise;
+
+GetChatSearchResultsTask::GetChatSearchResultsTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+GetChatSearchResultsTask::~GetChatSearchResultsTask()
+{
+}
+
+void GetChatSearchResultsTask::poll( int queryHandle )
+{
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_UD_OBJECT_ID, 0, NMFIELD_TYPE_UDWORD, queryHandle ) );
+ lst.append( new Field::SingleField( NM_A_UD_QUERY_COUNT, 0, NMFIELD_TYPE_UDWORD, 10 ) );
+ createTransfer( "getchatsearchresults", lst );
+}
+
+bool GetChatSearchResultsTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ setError( response->resultCode() );
+ return true;
+ }
+
+ // look for the status code
+ Field::FieldList responseFields = response->fields();
+ Field::SingleField * sf = responseFields.findSingleField( NM_A_UW_STATUS );
+ m_queryStatus = (SearchResultCode)sf->value().toInt();
+
+ Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS );
+ if ( !resultsArray )
+ {
+ setError( Protocol );
+ return true;
+ }
+ Field::FieldList matches = resultsArray->fields();
+ const Field::FieldListIterator end = matches.end();
+ for ( Field::FieldListIterator it = matches.find( NM_A_FA_CHAT );
+ it != end;
+ it = matches.find( ++it, NM_A_FA_CHAT ) )
+ {
+ Field::MultiField * mf = static_cast<Field::MultiField *>( *it );
+ Field::FieldList chat = mf->fields();
+ GroupWise::ChatroomSearchResult cd = extractChatDetails( chat );
+ m_results.append( cd );
+ }
+
+ if ( m_queryStatus != DataRetrieved )
+ setError( m_queryStatus );
+ else
+ {
+ kdDebug ( GROUPWISE_DEBUG_GLOBAL ) << " we won!" << endl;
+ setSuccess( m_queryStatus );
+ }
+ return true;
+}
+
+QValueList< GroupWise::ChatroomSearchResult > GetChatSearchResultsTask::results()
+{
+ return m_results;
+}
+
+int GetChatSearchResultsTask::queryStatus()
+{
+ return m_queryStatus;
+}
+
+GroupWise::ChatroomSearchResult GetChatSearchResultsTask::extractChatDetails( Field::FieldList & fields )
+{
+ ChatroomSearchResult csr;
+ csr.participants = 0;
+ // read the supplied fields, set metadata and status.
+ Field::SingleField * sf;
+ if ( ( sf = fields.findSingleField ( NM_A_DISPLAY_NAME ) ) )
+ csr.name = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( NM_A_CHAT_OWNER_DN ) ) )
+ csr.ownerDN = sf->value().toString().lower(); // HACK: lowercased DN
+ if ( ( sf = fields.findSingleField ( NM_A_UD_PARTICIPANTS ) ) )
+ csr.participants = sf->value().toInt();
+
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << csr.name << ", " << csr.ownerDN << ", " << csr.participants << endl;
+ return csr;
+}
+
+#include "getchatsearchresultstask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h
new file mode 100644
index 00000000..31db19ed
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getchatsearchresultstask.h
@@ -0,0 +1,52 @@
+/*
+ Kopete Groupwise Protocol
+ getchatsearchresultstask.h - Poll the server once to see if it has processed our chatroom search yet.
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATSEARCHRESULTSTASK_H
+#define CHATSEARCHRESULTSTASK_H
+
+#include <qvaluelist.h>
+
+#include "gwchatrooms.h"
+
+#include "requesttask.h"
+
+/**
+Search results are polled on the server, using the search handle returned by the server with the original query. This is a single poll request, which if successful, will retrieve the results. Otherwise, it will set a status code, so the SearchChatTask can decide whether to poll again.
+
+@author SUSE Linux Products GmbH
+ */
+class GetChatSearchResultsTask : public RequestTask
+{
+ Q_OBJECT
+ public:
+ enum SearchResultCode { Completed=2, Cancelled=4, Error=5, GettingData=8, DataRetrieved=9 };
+ GetChatSearchResultsTask(Task* parent);
+ ~GetChatSearchResultsTask();
+ void poll( int queryHandle);
+ bool take( Transfer * transfer );
+ int queryStatus();
+ QValueList< GroupWise::ChatroomSearchResult > results();
+ private:
+ GroupWise::ChatroomSearchResult extractChatDetails( Field::FieldList & fields );
+ SearchResultCode m_queryStatus;
+ QValueList< GroupWise::ChatroomSearchResult > m_results;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp
new file mode 100644
index 00000000..0b37efb4
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.cpp
@@ -0,0 +1,136 @@
+/*
+ Kopete Groupwise Protocol
+ getdetailstask.cpp - fetch a contact's details from the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "response.h"
+#include "userdetailsmanager.h"
+
+#include "getdetailstask.h"
+
+using namespace GroupWise;
+
+GetDetailsTask::GetDetailsTask( Task * parent )
+ : RequestTask( parent )
+{
+}
+
+
+GetDetailsTask::~GetDetailsTask()
+{
+}
+
+void GetDetailsTask::userDNs( const QStringList & userDNs )
+{
+ Field::FieldList lst;
+ for ( QStringList::ConstIterator it = userDNs.begin(); it != userDNs.end(); ++it )
+ {
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, *it ) );
+ }
+ createTransfer( "getdetails", lst );
+}
+
+bool GetDetailsTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+
+ Field::FieldList detailsFields = response->fields();
+ // parse received details and signal like billio
+ Field::MultiField * container = 0;
+ Field::FieldListIterator end = detailsFields.end();
+ for ( Field::FieldListIterator it = detailsFields.find( NM_A_FA_RESULTS );
+ it != end;
+ it = detailsFields.find( ++it, NM_A_FA_RESULTS ) )
+ {
+ container = static_cast<Field::MultiField *>( *it );
+ ContactDetails cd = extractUserDetails( container );
+ emit gotContactUserDetails( cd );
+ }
+
+ return true;
+}
+
+ContactDetails GetDetailsTask::extractUserDetails(Field::MultiField * details )
+{
+ ContactDetails cd;
+ cd.status = GroupWise::Invalid;
+ cd.archive = false;
+ Field::FieldList fields = details->fields();
+ // TODO: not sure what this means, ask Mike
+ Field::SingleField * sf;
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) )
+ cd.authAttribute = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) )
+ cd.dn =sf->value().toString().lower(); // HACK: lowercased DN
+ if ( ( sf = fields.findSingleField ( "CN" ) ) )
+ cd.cn = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Given Name" ) ) )
+ cd.givenName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Surname" ) ) )
+ cd.surname = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) )
+ cd.archive = ( sf->value().toInt() == 1 );
+ if ( ( sf = fields.findSingleField ( "Full Name" ) ) )
+ cd.fullName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) )
+ cd.status = sf->value().toInt();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) )
+ cd.awayMessage = sf->value().toString();
+ Field::MultiField * mf;
+ QMap< QString, QString > propMap;
+ if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) )
+ {
+ Field::FieldList fl = mf->fields();
+ const Field::FieldListIterator end = fl.end();
+ for ( Field::FieldListIterator it = fl.begin(); it != end; ++it )
+ {
+ Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it );
+ if ( propField ) {
+ QString propName = propField->tag();
+ QString propValue = propField->value().toString();
+ propMap.insert( propName, propValue );
+ } else {
+ Field::MultiField * mf2;
+ if ( ( mf2 = dynamic_cast<Field::MultiField *>( *it ) ) ) {
+ Field::FieldList fl2 = mf2->fields();
+ const Field::FieldListIterator end = fl2.end();
+ for ( Field::FieldListIterator it2 = fl2.begin(); it2 != end; ++it2 )
+ {
+ propField = dynamic_cast<Field::SingleField *>( *it2 );
+ if ( propField ) {
+ QString propName = propField->tag();
+ QString propValue = propField->value().toString();
+ propMap.insert( propName, propValue );
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( !propMap.empty() )
+ {
+ cd.properties = propMap;
+ }
+ return cd;
+}
+#include "getdetailstask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h
new file mode 100644
index 00000000..d263f50b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getdetailstask.h
@@ -0,0 +1,49 @@
+/*
+ Kopete Groupwise Protocol
+ getdetailstask.h - fetch a contact's details from the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GETDETAILSTASK_H
+#define GETDETAILSTASK_H
+
+#include "gwerror.h"
+#include "requesttask.h"
+
+/**
+This task fetches the details for a set of user IDs from the server. Sometimes we get an event that only has a DN, and we need other details before showing the event to the user.
+
+@author SUSE AG
+*/
+using namespace GroupWise;
+
+class GetDetailsTask : public RequestTask
+{
+Q_OBJECT
+public:
+ GetDetailsTask( Task * parent );
+ ~GetDetailsTask();
+ bool take( Transfer * transfer );
+ void userDNs( const QStringList & userDNs );
+signals:
+ void gotContactUserDetails( const GroupWise::ContactDetails & );
+protected:
+ GroupWise::ContactDetails extractUserDetails( Field::MultiField * details );
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp
new file mode 100644
index 00000000..dde055a6
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.cpp
@@ -0,0 +1,72 @@
+/*
+ Kopete Groupwise Protocol
+ getstatustask.cpp - fetch a contact's details from the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "response.h"
+
+#include "getstatustask.h"
+
+GetStatusTask::GetStatusTask(Task* parent): RequestTask(parent)
+{
+}
+
+GetStatusTask::~GetStatusTask()
+{
+}
+
+void GetStatusTask::userDN( const QString & dn )
+{
+ m_userDN = dn;
+ // set up Transfer
+ Field::FieldList lst;
+ // changed from USERID to DN as per Gaim/GWIM
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, m_userDN ) );
+ createTransfer( "getstatus", lst );
+}
+
+bool GetStatusTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+
+ Field::FieldList responseFields = response->fields();
+ responseFields.dump( true );
+ // parse received details and signal like billio
+ Field::SingleField * sf = 0;
+ Q_UINT16 status;
+ sf = responseFields.findSingleField( NM_A_SZ_STATUS );
+ if ( sf )
+ {
+ // As of Sept 2004 the server always responds with 2 (Available) here, even if the sender is not
+ // This must be because the sender is not on our contact list but has sent us a message.
+ // TODO: Check that the change to sending DNs above has fixed this problem.
+ status = sf->value().toInt();
+ // unfortunately getstatus doesn't give us an away message so we pass QString::null here
+ emit gotStatus( m_userDN, status, QString::null );
+ setSuccess();
+ }
+ else
+ setError();
+ return true;
+}
+
+#include "getstatustask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h
new file mode 100644
index 00000000..59422342
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/getstatustask.h
@@ -0,0 +1,44 @@
+/*
+ Kopete Groupwise Protocol
+ getstatustask.h - fetch a contact's details from the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GETSTATUSTASK_H
+#define GETSTATUSTASK_H
+
+#include "requesttask.h"
+
+/**
+ * Request the status for a specific contact (e.g. one who's not on our contact list)
+ * @author SUSE AG
+*/
+class GetStatusTask : public RequestTask
+{
+Q_OBJECT
+public:
+ GetStatusTask(Task* parent);
+ ~GetStatusTask();
+ void userDN( const QString & dn );
+ bool take( Transfer * transfer );
+signals:
+ void gotStatus( const QString & contactId, Q_UINT16 status, const QString & statusText );
+private:
+ QString m_userDN;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.cpp
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/gwtasklogin.h
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp
new file mode 100644
index 00000000..4e9e4f57
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.cpp
@@ -0,0 +1,131 @@
+/*
+ Kopete Groupwise Protocol
+ joinchattask.cpp - Join a Chat on the server, after having been invited.
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwerror.h"
+#include "client.h"
+#include "response.h"
+#include "userdetailsmanager.h"
+
+#include "joinchattask.h"
+
+JoinChatTask::JoinChatTask(Task* parent): RequestTask(parent)
+{
+}
+
+JoinChatTask::~JoinChatTask()
+{
+}
+
+void JoinChatTask::join( const QString & displayName )
+{
+ m_displayName = displayName;
+ Field::FieldList lst, tmp;
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, displayName ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ createTransfer( "joinchat", lst );
+}
+
+bool JoinChatTask::take( Transfer * transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ client()->debug( "JoinChatTask::take()" );
+ Response * response = dynamic_cast<Response *>( transfer );
+ Field::FieldList responseFields = response->fields();
+ // if the request was successful
+ if ( response->resultCode() == GroupWise::None )
+ {
+ // extract the list of participants and store them
+ Field::MultiField * participants = responseFields.findMultiField( NM_A_FA_CONTACT_LIST );
+ if ( participants )
+ {
+ Field::SingleField * contact = 0;
+ Field::FieldList contactList = participants->fields();
+ const Field::FieldListIterator end = contactList.end();
+ for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN );
+ it != end;
+ it = contactList.find( ++it, NM_A_SZ_DN ) )
+ {
+ contact = static_cast<Field::SingleField *>( *it );
+ if ( contact )
+ {
+ // HACK: lowercased DN
+ QString dn = contact->value().toString().lower();
+ m_participants.append( dn );
+ // need to ask for details for these contacts
+ }
+ }
+ }
+ else
+ setError( GroupWise::Protocol );
+
+ // now, extract the list of pending invites and store them
+ Field::MultiField * invitees = responseFields.findMultiField( NM_A_FA_RESULTS );
+ if ( invitees )
+ {
+ Field::SingleField * contact = 0;
+ Field::FieldList contactList = invitees->fields();
+ const Field::FieldListIterator end = contactList.end();
+ for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN );
+ it != end;
+ it = contactList.find( ++it, NM_A_SZ_DN ) )
+ {
+ contact = static_cast<Field::SingleField *>( *it );
+ if ( contact )
+ {
+ // HACK: lowercased DN
+ QString dn = contact->value().toString().lower();
+ m_invitees.append( dn );
+ // need to ask for details for these contacts
+ if ( !client()->userDetailsManager()->known( dn ) )
+ ; // don't request details for chatrooms, there could be too many
+ }
+ }
+ }
+ else
+ setError( GroupWise::Protocol );
+
+ client()->debug( "JoinChatTask::finished()" );
+ finished();
+ }
+ else
+ setError( response->resultCode() );
+ return true;
+ }
+ else
+ return false;
+}
+
+QStringList JoinChatTask::participants() const
+{
+ return m_participants;
+}
+
+QStringList JoinChatTask::invitees() const
+{
+ return m_invitees;
+}
+
+QString JoinChatTask::displayName() const
+{
+ return m_displayName;
+}
+
+#include "joinchattask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h
new file mode 100644
index 00000000..a7cc4119
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinchattask.h
@@ -0,0 +1,52 @@
+/*
+ Kopete Groupwise Protocol
+ joinchattask.h - Join a chatroom on the server, after having been invited.
+
+ Copyright (c) 2004 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef JOINCHATTASK_H
+#define JOINCHATTASK_H
+
+#include "requesttask.h"
+
+using namespace GroupWise;
+
+/**
+Sends Join Conference messages when the user accepts an invitation
+
+@author SUSE Linux Products GmbH
+ */
+
+class JoinChatTask : public RequestTask
+{
+ Q_OBJECT
+ public:
+ JoinChatTask(Task* parent);
+ ~JoinChatTask();
+ void join( const QString & displayName );
+ bool take( Transfer * transfer );
+ QStringList participants() const;
+ QStringList invitees() const;
+ QString displayName() const;
+ private:
+ ConferenceGuid m_displayName;
+ QStringList m_participants;
+ QStringList m_invitees;
+ QStringList m_unknowns;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp
new file mode 100644
index 00000000..c2cf0f02
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.cpp
@@ -0,0 +1,175 @@
+/*
+ Kopete Groupwise Protocol
+ joinconferencetask.cpp - Join a conference on the server, after having been invited.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwerror.h"
+#include "client.h"
+#include "response.h"
+#include "userdetailsmanager.h"
+
+#include "joinconferencetask.h"
+
+JoinConferenceTask::JoinConferenceTask(Task* parent): RequestTask(parent)
+{
+}
+
+JoinConferenceTask::~JoinConferenceTask()
+{
+}
+
+void JoinConferenceTask::join( const GroupWise::ConferenceGuid & guid )
+{
+ m_guid = guid;
+ Field::FieldList lst, tmp;
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ createTransfer( "joinconf", lst );
+}
+
+bool JoinConferenceTask::take( Transfer * transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ client()->debug( "JoinConferenceTask::take()" );
+ Response * response = dynamic_cast<Response *>( transfer );
+ Field::FieldList responseFields = response->fields();
+ // if the request was successful
+ if ( response->resultCode() == GroupWise::None )
+ {
+ // extract the list of participants and store them
+ Field::MultiField * participants = responseFields.findMultiField( NM_A_FA_CONTACT_LIST );
+ if ( participants )
+ {
+ Field::SingleField * contact = 0;
+ Field::FieldList contactList = participants->fields();
+ const Field::FieldListIterator end = contactList.end();
+ for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN );
+ it != end;
+ it = contactList.find( ++it, NM_A_SZ_DN ) )
+ {
+ contact = static_cast<Field::SingleField *>( *it );
+ if ( contact )
+ {
+ // HACK: lowercased DN
+ QString dn = contact->value().toString().lower();
+ m_participants.append( dn );
+ // need to ask for details for these contacts
+ if ( !client()->userDetailsManager()->known( dn ) )
+ m_unknowns.append( dn );
+ }
+ }
+ }
+ else
+ setError( GroupWise::Protocol );
+
+ // now, extract the list of pending invites and store them
+ Field::MultiField * invitees = responseFields.findMultiField( NM_A_FA_RESULTS );
+ if ( invitees )
+ {
+ Field::SingleField * contact = 0;
+ Field::FieldList contactList = invitees->fields();
+ const Field::FieldListIterator end = contactList.end();
+ for ( Field::FieldListIterator it = contactList.find( NM_A_SZ_DN );
+ it != end;
+ it = contactList.find( ++it, NM_A_SZ_DN ) )
+ {
+ contact = static_cast<Field::SingleField *>( *it );
+ if ( contact )
+ {
+ // HACK: lowercased DN
+ QString dn = contact->value().toString().lower();
+ m_invitees.append( dn );
+ // need to ask for details for these contacts
+ if ( !client()->userDetailsManager()->known( dn ) )
+ m_unknowns.append( dn );
+ }
+ }
+ }
+ else
+ setError( GroupWise::Protocol );
+
+ if ( m_unknowns.empty() ) // ready to chat
+ {
+ client()->debug( "JoinConferenceTask::finished()" );
+ finished();
+ }
+ else // need to get some more details first
+ {
+ client()->debug( "JoinConferenceTask::slotReceiveUserDetails(), requesting details" );
+ connect( client()->userDetailsManager(),
+ SIGNAL( gotContactDetails( const GroupWise::ContactDetails & ) ),
+ SLOT( slotReceiveUserDetails( const GroupWise::ContactDetails & ) ) );
+ client()->userDetailsManager()->requestDetails( m_unknowns );
+ }
+ }
+ else
+ setError( response->resultCode() );
+ return true;
+ }
+ else
+ return false;
+}
+
+void JoinConferenceTask::slotReceiveUserDetails( const ContactDetails & details )
+{
+ client()->debug( QString( "JoinConferenceTask::slotReceiveUserDetails() - got %1" ).arg( details.dn ) );
+ QStringList::Iterator it = m_unknowns.begin();
+ QStringList::Iterator end = m_unknowns.end();
+ while( it != end )
+ {
+ QString current = *it;
+ ++it;
+ client()->debug( QString( " - can we remove %1?" ).arg(current ) );
+ if ( current == details.dn )
+ {
+ client()->debug( " - it's gone!" );
+ m_unknowns.remove( current );
+ break;
+ }
+ }
+ client()->debug( QString( " - now %1 unknowns").arg( m_unknowns.count() ) );
+ if ( m_unknowns.empty() )
+ {
+ client()->debug( " - finished()" );
+ finished();
+ }
+// would be better to count the number of received details and listen to the getdetails task's error signal.
+// else
+// {
+// client()->debug( " - ERROR - we requested details for the list of chat participants/invitees, but the server did not send us all the details! - setting finished() anyway, so the chat can take place." );
+// finished();
+// }
+}
+
+QStringList JoinConferenceTask::participants() const
+{
+ return m_participants;
+}
+
+QStringList JoinConferenceTask::invitees() const
+{
+ return m_invitees;
+}
+
+GroupWise::ConferenceGuid JoinConferenceTask::guid() const
+{
+ return m_guid;
+}
+
+#include "joinconferencetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h
new file mode 100644
index 00000000..68316147
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/joinconferencetask.h
@@ -0,0 +1,54 @@
+/*
+ Kopete Groupwise Protocol
+ joinconferencetask.h - Join a conference on the server, after having been invited.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef JOINCONFERENCETASK_H
+#define JOINCONFERENCETASK_H
+
+#include "requesttask.h"
+
+using namespace GroupWise;
+
+/**
+Sends Join Conference messages when the user accepts an invitation
+
+@author SUSE AG
+*/
+
+class JoinConferenceTask : public RequestTask
+{
+Q_OBJECT
+public:
+ JoinConferenceTask(Task* parent);
+ ~JoinConferenceTask();
+ void join( const ConferenceGuid & guid );
+ bool take( Transfer * transfer );
+ QStringList participants() const;
+ QStringList invitees() const;
+ ConferenceGuid guid() const;
+public slots:
+ void slotReceiveUserDetails( const GroupWise::ContactDetails & details );
+private:
+ ConferenceGuid m_guid;
+ QStringList m_participants;
+ QStringList m_invitees;
+ QStringList m_unknowns;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp
new file mode 100644
index 00000000..ac84ac2b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.cpp
@@ -0,0 +1,42 @@
+/*
+ Kopete Groupwise Protocol
+ keepalivetask.cpp - Send keepalive pings to the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+ (c) 2006 Novell, Inc.
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "request.h"
+#include "requestfactory.h"
+#include "keepalivetask.h"
+
+KeepAliveTask::KeepAliveTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+KeepAliveTask::~KeepAliveTask()
+{
+}
+
+void KeepAliveTask::setup()
+{
+ Field::FieldList lst;
+ createTransfer( "ping", lst );
+}
+
+#include "keepalivetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h
new file mode 100644
index 00000000..04f9a352
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/keepalivetask.h
@@ -0,0 +1,38 @@
+/*
+ Kopete Groupwise Protocol
+ keepalivetask.h - Send keepalive pings to the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KEEPALIVETASK_H
+#define KEEPALIVETASK_H
+
+#include "requesttask.h"
+
+/**
+@author Kopete Developers
+*/
+class KeepAliveTask : public RequestTask
+{
+Q_OBJECT
+public:
+ KeepAliveTask(Task* parent);
+ ~KeepAliveTask();
+ void setup();
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp
new file mode 100644
index 00000000..d2d58b83
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.cpp
@@ -0,0 +1,40 @@
+/*
+ Kopete Groupwise Protocol
+ leaveconferencetask.cpp - Tell the server we are leaving a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "leaveconferencetask.h"
+
+LeaveConferenceTask::LeaveConferenceTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+LeaveConferenceTask::~LeaveConferenceTask()
+{
+}
+
+void LeaveConferenceTask::leave( const GroupWise::ConferenceGuid & guid )
+{
+ Field::FieldList lst, tmp;
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ createTransfer( "leaveconf", lst );
+}
+
+#include "leaveconferencetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h
new file mode 100644
index 00000000..65ebe540
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/leaveconferencetask.h
@@ -0,0 +1,40 @@
+/*
+ Kopete Groupwise Protocol
+ leaveconferencetask.h - Tell the server we are leaving a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LEAVECONFERENCETASK_H
+#define LEAVECONFERENCETASK_H
+
+#include "requesttask.h"
+
+/**
+Tells the server that you are leaving a conference (closed the chatwindow)
+
+@author SUSE AG
+*/
+class LeaveConferenceTask : public RequestTask
+{
+Q_OBJECT
+public:
+ LeaveConferenceTask(Task* parent);
+ ~LeaveConferenceTask();
+ void leave( const GroupWise::ConferenceGuid & guid );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp
new file mode 100644
index 00000000..1f679a6c
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.cpp
@@ -0,0 +1,360 @@
+/*
+ Kopete Groupwise Protocol
+ logintask.cpp - Send our credentials to the server and process the contact list and privacy details that it returns.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "response.h"
+#include "privacymanager.h"
+#include "userdetailsmanager.h"
+
+#include "logintask.h"
+
+LoginTask::LoginTask( Task * parent )
+ : RequestTask( parent )
+{
+}
+
+LoginTask::~LoginTask()
+{
+}
+
+void LoginTask::initialise()
+{
+ QString command = QString::fromLatin1("login:%1:%2").arg( client()->host() ).arg( client()->port() );
+
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, client()->userId() ) );
+ lst.append( new Field::SingleField( NM_A_SZ_CREDENTIALS, 0, NMFIELD_TYPE_UTF8, client()->password() ) );
+ lst.append( new Field::SingleField( NM_A_SZ_USER_AGENT, 0, NMFIELD_TYPE_UTF8, client()->userAgent() ) );
+ lst.append( new Field::SingleField( NM_A_UD_BUILD, 0, NMFIELD_TYPE_UDWORD, client()->protocolVersion() ) );
+ lst.append( new Field::SingleField( NM_A_IP_ADDRESS, 0, NMFIELD_TYPE_UTF8, client()->ipAddress() ) );
+ createTransfer( command, lst );
+}
+
+bool LoginTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ setError( response->resultCode() );
+ return true;
+ }
+ response->fields().dump( true );
+
+ // read in myself()'s metadata fields and emit signal
+ Field::FieldList loginResponseFields = response->fields();
+
+ ContactDetails cd = extractUserDetails( loginResponseFields );
+ emit gotMyself( cd );
+
+ // read the privacy settings first, because this affects all contacts' apparent status
+ extractPrivacy( loginResponseFields );
+
+ extractCustomStatuses( loginResponseFields );
+
+ // CREATE CONTACT LIST
+ // locate contact list
+ Field::MultiField * contactList = loginResponseFields.findMultiField( NM_A_FA_CONTACT_LIST );
+ if ( contactList )
+ {
+ Field::FieldList contactListFields = contactList->fields();
+ Field::MultiField * container;
+ // read folders
+ for ( Field::FieldListIterator it = contactListFields.find( NM_A_FA_FOLDER );
+ it != contactListFields.end();
+ it = contactListFields.find( ++it, NM_A_FA_FOLDER ) )
+ {
+ container = static_cast<Field::MultiField *>( *it );
+ extractFolder( container );
+ }
+
+ // read contacts
+ for ( Field::FieldListIterator it = contactListFields.find( NM_A_FA_CONTACT );
+ it != contactListFields.end();
+ it = contactListFields.find( ++it, NM_A_FA_CONTACT ) )
+ {
+ container = static_cast<Field::MultiField *>( *it );
+ extractContact( container );
+ }
+ }
+
+ extractKeepalivePeriod( loginResponseFields );
+
+ setSuccess();
+
+ return true;
+}
+
+void LoginTask::extractFolder( Field::MultiField * folderContainer )
+{
+ FolderItem folder;
+ Field::SingleField * current;
+ Field::FieldList fl = folderContainer->fields();
+ // object id
+ current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
+ folder.id = current->value().toInt();
+ // sequence number
+ current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
+ folder.sequence = current->value().toInt();
+ // name
+ current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
+ folder.name = current->value().toString();
+ // parent
+ current = fl.findSingleField( NM_A_SZ_PARENT_ID );
+ folder.parentId = current->value().toInt();
+
+ client()->debug( QString( "Got folder: %1, obj: %2, parent: %3, seq: %3." ).arg( folder.name ).arg( folder.id ).arg( folder.parentId ).arg( folder.sequence ) );
+ // tell the world about it
+ emit gotFolder( folder );
+}
+
+void LoginTask::extractContact( Field::MultiField * contactContainer )
+{
+ if ( contactContainer->tag() != NM_A_FA_CONTACT )
+ return;
+ ContactItem contact;
+ Field::SingleField * current;
+ Field::FieldList fl = contactContainer->fields();
+ // sequence number, object and parent IDs are a numeric values but are stored as strings...
+ current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
+ contact.id = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_PARENT_ID );
+ contact.parentId = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
+ contact.sequence = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
+ contact.displayName = current->value().toString();
+ current = fl.findSingleField( NM_A_SZ_DN );
+ contact.dn = current->value().toString().lower();
+ emit gotContact( contact );
+ Field::MultiField * details = fl.findMultiField( NM_A_FA_USER_DETAILS );
+ if ( details ) // not all contact list contacts have these
+ {
+ Field::FieldList detailsFields = details->fields();
+ ContactDetails cd = extractUserDetails( detailsFields );
+ if ( cd.dn.isEmpty() )
+ cd.dn = contact.dn;
+ // tell the UserDetailsManager that we have this contact's details
+ client()->userDetailsManager()->addDetails( cd );
+ emit gotContactUserDetails( cd );
+ }
+}
+
+ContactDetails LoginTask::extractUserDetails( Field::FieldList & fields )
+{
+ ContactDetails cd;
+ cd.status = GroupWise::Invalid;
+ cd.archive = false;
+ // read the supplied fields, set metadata and status.
+ Field::SingleField * sf;
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) )
+ cd.authAttribute = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) )
+ cd.dn = sf->value().toString().lower(); // HACK: lowercased DN
+ if ( ( sf = fields.findSingleField ( "CN" ) ) )
+ cd.cn = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Given Name" ) ) )
+ cd.givenName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Surname" ) ) )
+ cd.surname = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Full Name" ) ) )
+ cd.fullName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) )
+ cd.archive = ( sf->value().toInt() == 1 );
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) )
+ cd.status = sf->value().toInt();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) )
+ cd.awayMessage = sf->value().toString();
+ Field::MultiField * mf;
+ QMap< QString, QString > propMap;
+ if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) )
+ {
+ Field::FieldList fl = mf->fields();
+ const Field::FieldListIterator end = fl.end();
+ for ( Field::FieldListIterator it = fl.begin(); it != end; ++it )
+ {
+ Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it );
+ if ( propField )
+ {
+ QString propName = propField->tag();
+ QString propValue = propField->value().toString();
+ propMap.insert( propName, propValue );
+ }
+ else
+ {
+ Field::MultiField * propList = dynamic_cast<Field::MultiField*>( *it );
+ if ( propList )
+ {
+ // Hello A Nagappan. GW gave us a multiple field where we previously got a single field
+ QString parentName = propList->tag();
+ Field::FieldList propFields = propList->fields();
+ const Field::FieldListIterator end = propFields.end();
+ for ( Field::FieldListIterator it = propFields.begin(); it != end; ++it )
+ {
+ propField = dynamic_cast<Field::SingleField *>( *it );
+ if ( propField /*&& propField->tag() == parentName */)
+ {
+ QString propValue = propField->value().toString();
+ QString contents = propMap[ propField->tag() ];
+ if ( !contents.isEmpty() )
+ contents.append( ", " );
+ contents.append( propField->value().toString());
+ propMap.insert( propField->tag(), contents );
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( !propMap.empty() )
+ {
+ cd.properties = propMap;
+ }
+ return cd;
+}
+
+void LoginTask::extractPrivacy( Field::FieldList & fields )
+{
+ bool privacyLocked = false;
+ bool defaultDeny = false;
+ QStringList allowList;
+ QStringList denyList;
+ // read blocking
+ // may be a single field or may be an array
+ Field::FieldListIterator it = fields.find( NM_A_LOCKED_ATTR_LIST );
+ if ( it != fields.end() )
+ {
+ if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) )
+ {
+ if ( sf->value().toString().find( NM_A_BLOCKING ) )
+ privacyLocked = true;
+ }
+ else if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) )
+ {
+ Field::FieldList fl = mf->fields();
+ for ( Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it )
+ {
+ if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) )
+ {
+ if ( sf->tag() == NM_A_BLOCKING )
+ {
+ privacyLocked = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // read default privacy policy
+ Field::SingleField * sf = fields.findSingleField( NM_A_BLOCKING );
+ if ( sf )
+ defaultDeny = ( sf->value().toInt() != 0 );
+
+
+ // read deny list
+ denyList = readPrivacyItems( NM_A_BLOCKING_DENY_LIST, fields );
+ // read allow list
+ allowList = readPrivacyItems( NM_A_BLOCKING_ALLOW_LIST, fields );
+ emit gotPrivacySettings( privacyLocked, defaultDeny, allowList, denyList );
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << "locked is " << privacyLocked << ", default is " << defaultDeny << "\nallow list is: " << allowList << "\ndeny list is: " << denyList << endl;
+}
+
+QStringList LoginTask::readPrivacyItems( const QCString & tag, Field::FieldList & fields )
+{
+ QStringList items;
+
+ Field::FieldListIterator it = fields.find( tag );
+ if ( it != fields.end() )
+ {
+ if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) )
+ {
+ items.append( sf->value().toString().lower() );
+ }
+ else if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) )
+ {
+ Field::FieldList fl = mf->fields();
+ for ( Field::FieldListIterator it = fl.begin(); it != fl.end(); ++it )
+ {
+ if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) )
+ {
+ items.append( sf->value().toString().lower() );
+ }
+ }
+ }
+ }
+ return items;
+}
+
+void LoginTask::extractCustomStatuses( Field::FieldList & fields )
+{
+ Field::FieldListIterator it = fields.find( NM_A_FA_CUSTOM_STATUSES );
+ if ( it != fields.end() )
+ {
+ if ( Field::MultiField * mf = dynamic_cast<Field::MultiField *>( *it ) )
+ {
+ Field::FieldList fl = mf->fields();
+ for ( Field::FieldListIterator custStatIt = fl.begin(); custStatIt != fl.end(); ++custStatIt )
+ {
+ Field::MultiField * mf2 = dynamic_cast<Field::MultiField *>( *custStatIt );
+ if ( mf2 && ( mf2->tag() == NM_A_FA_STATUS ) )
+ {
+ GroupWise::CustomStatus custom;
+ Field::FieldList fl2 = mf2->fields();
+ for ( Field::FieldListIterator custContentIt = fl2.begin(); custContentIt != fl2.end(); ++custContentIt )
+ {
+ if ( Field::SingleField * sf3 = dynamic_cast<Field::SingleField *>( *custContentIt ) )
+ {
+ if ( sf3->tag() == NM_A_SZ_TYPE )
+ custom.status = (GroupWise::Status)sf3->value().toInt();
+ else if ( sf3->tag() == NM_A_SZ_DISPLAY_NAME )
+ custom.name = sf3->value().toString();
+ else if ( sf3->tag() == NM_A_SZ_MESSAGE_BODY )
+ custom.autoReply = sf3->value().toString();
+ }
+ }
+ emit gotCustomStatus( custom );
+ }
+ }
+ }
+ }
+}
+
+void LoginTask::extractKeepalivePeriod( Field::FieldList & fields )
+{
+ Field::FieldListIterator it = fields.find( NM_A_UD_KEEPALIVE );
+ if ( it != fields.end() )
+ {
+ if ( Field::SingleField * sf = dynamic_cast<Field::SingleField *>( *it ) )
+ {
+ bool ok;
+ int period = sf->value().toInt( &ok );
+ if ( ok )
+ {
+ emit gotKeepalivePeriod( period );
+ }
+ }
+ }
+}
+
+#include "logintask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h
new file mode 100644
index 00000000..0b2acdfd
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/logintask.h
@@ -0,0 +1,64 @@
+/*
+ Kopete Groupwise Protocol
+ logintask.h - Send our credentials to the server and process the contact list and privacy details that it returns.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LOGINTASK_H
+#define LOGINTASK_H
+
+#include "requesttask.h"
+
+using namespace GroupWise;
+
+/**
+@author Kopete Developers
+*/
+class LoginTask : public RequestTask
+{
+Q_OBJECT
+public:
+ LoginTask( Task * parent );
+ ~LoginTask();
+ /**
+ * Get the login fields ready to go
+ */
+ void initialise();
+ /**
+ * Only accepts the contactlist that comes back from the server,
+ * processes it and notifies the client of the contactlist
+ */
+ bool take( Transfer * transfer );
+protected:
+ void extractFolder( Field::MultiField * folderContainer );
+ void extractContact( Field::MultiField * contactContainer );
+ ContactDetails extractUserDetails( Field::FieldList & fields );
+ void extractPrivacy( Field::FieldList & fields );
+ QStringList readPrivacyItems( const QCString & tag, Field::FieldList & fields );
+ void extractCustomStatuses( Field::FieldList & fields );
+ void extractKeepalivePeriod( Field::FieldList & fields );
+signals:
+ void gotMyself( const GroupWise::ContactDetails & );
+ void gotFolder( const FolderItem & );
+ void gotContact( const ContactItem & );
+ void gotContactUserDetails( const GroupWise::ContactDetails & );
+ void gotPrivacySettings( bool locked, bool defaultDeny, const QStringList & allowList, const QStringList & denyList );
+ void gotCustomStatus( const GroupWise::CustomStatus & );
+ void gotKeepalivePeriod( int );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp
new file mode 100644
index 00000000..10233a18
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.cpp
@@ -0,0 +1,139 @@
+/*
+ Kopete Groupwise Protocol
+ modifycontactlisttask.cpp - Ancestor of all tasks that change the server side contact list.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "response.h"
+#include "gwerror.h"
+#include "modifycontactlisttask.h"
+
+ModifyContactListTask::ModifyContactListTask(Task* parent): RequestTask(parent)
+{
+}
+
+ModifyContactListTask::~ModifyContactListTask()
+{
+}
+
+bool ModifyContactListTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ client()->debug( "ModifyContactListTask::take()" );
+
+ // scan the contact list received
+ // emit each add and delete as a signal
+ Field::FieldList fl = response->fields();
+ fl.dump( true );
+ Field::FieldListIterator it = fl.begin();
+ Field::FieldListIterator end = fl.end();
+ Field::MultiField * current = fl.findMultiField( NM_A_FA_RESULTS );
+ if ( current )
+ fl = current->fields();
+ current = fl.findMultiField( NM_A_FA_CONTACT_LIST );
+ if ( current )
+ {
+ Field::FieldList contactList = current->fields();
+ Field::FieldListIterator cursor = contactList.begin();
+ const Field::FieldListIterator end = contactList.end();
+ while ( cursor != end )
+ {
+ Field::MultiField * mf = dynamic_cast< Field::MultiField * >( *cursor );
+ if ( mf->tag() == NM_A_FA_CONTACT )
+ {
+ // contact change
+ processContactChange( mf );
+ }
+ else if ( mf->tag() == NM_A_FA_FOLDER )
+ {
+ // folder change
+ processFolderChange( mf );
+ }
+ ++cursor;
+ }
+ }
+ // TODO: call virtual here to read any fields after the contact list...
+ if ( response->resultCode() == GroupWise::None )
+ setSuccess();
+ else
+ setError( response->resultCode() );
+ return true;
+}
+
+void ModifyContactListTask::processContactChange( Field::MultiField * container )
+{
+ if ( !( container->method() == NMFIELD_METHOD_ADD
+ || container->method() == NMFIELD_METHOD_DELETE ) )
+ return;
+
+ client()->debug( "ModifyContactListTask::processContactChange()" );
+ Field::SingleField * current;
+ Field::FieldList fl = container->fields();
+ ContactItem contact;
+ current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
+ contact.id = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_PARENT_ID );
+ contact.parentId = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
+ contact.sequence = current->value().toInt();
+ current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
+ contact.displayName = current->value().toString();
+ current = fl.findSingleField( NM_A_SZ_DN );
+ contact.dn = current->value().toString();
+
+ if ( container->method() == NMFIELD_METHOD_ADD )
+ emit gotContactAdded( contact );
+ else if ( container->method() == NMFIELD_METHOD_DELETE )
+ emit gotContactDeleted( contact );
+}
+
+void ModifyContactListTask::processFolderChange( Field::MultiField * container )
+{
+ if ( !( container->method() == NMFIELD_METHOD_ADD
+ || container->method() == NMFIELD_METHOD_DELETE ) )
+ return;
+
+ client()->debug( "ModifyContactListTask::processFolderChange()" );
+ FolderItem folder;
+ Field::SingleField * current;
+ Field::FieldList fl = container->fields();
+ // object id
+ current = fl.findSingleField( NM_A_SZ_OBJECT_ID );
+ folder.id = current->value().toInt();
+ // sequence number
+ current = fl.findSingleField( NM_A_SZ_SEQUENCE_NUMBER );
+ folder.sequence = current->value().toInt();
+ // name
+ current = fl.findSingleField( NM_A_SZ_DISPLAY_NAME );
+ folder.name = current->value().toString();
+ // parent
+ current = fl.findSingleField( NM_A_SZ_PARENT_ID );
+ folder.parentId = current->value().toInt();
+ if ( container->method() == NMFIELD_METHOD_ADD )
+ emit gotFolderAdded( folder );
+ else if ( container->method() == NMFIELD_METHOD_DELETE )
+ emit gotFolderDeleted( folder );
+
+}
+
+
+#include "modifycontactlisttask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h
new file mode 100644
index 00000000..2f5a4939
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/modifycontactlisttask.h
@@ -0,0 +1,51 @@
+/*
+ Kopete Groupwise Protocol
+ modifycontactlisttask.h - Ancestor of all tasks that change the server side contact list.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MODIFYCONTACTLISTTASK_H
+#define MODIFYCONTACTLISTTASK_H
+
+#include "requesttask.h"
+
+/**
+This is the parent of all tasks that manipulate the contact list. The server responds to each one in the same way, and this task contains a take() to process this response.
+
+@author SUSE AG
+*/
+
+using namespace GroupWise;
+
+class ModifyContactListTask : public RequestTask
+{
+Q_OBJECT
+public:
+ ModifyContactListTask(Task* parent);
+ ~ModifyContactListTask();
+ bool take( Transfer * transfer );
+signals:
+ void gotFolderAdded( const FolderItem &);
+ void gotFolderDeleted( const FolderItem & );
+ void gotContactAdded( const ContactItem & );
+ void gotContactDeleted( const ContactItem & );
+private:
+ void processFolderChange( Field::MultiField * container );
+ void processContactChange( Field::MultiField * container );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp
new file mode 100644
index 00000000..713315ee
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.cpp
@@ -0,0 +1,83 @@
+/*
+ Kopete Groupwise Protocol
+ movecontacttask.cpp - Move a contact between folders on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+
+#include "movecontacttask.h"
+
+MoveContactTask::MoveContactTask(Task* parent): NeedFolderTask(parent)
+{
+ // make the client tell the client app (Kopete) when we receive a contact
+ connect( this, SIGNAL( gotContactAdded( const ContactItem & ) ), client(), SIGNAL( contactReceived( const ContactItem & ) ) );
+}
+
+
+MoveContactTask::~MoveContactTask()
+{
+}
+
+void MoveContactTask::moveContact( const ContactItem & contact, const int newParent )
+{
+ Field::FieldList lst;
+ // TODO: - write a contact_item_to_fields method and factor duplicate code like this out
+ Field::FieldList contactFields;
+ contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, contact.id ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, contact.parentId ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, contact.sequence ) );
+ if ( !contact.dn.isNull() )
+ contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, contact.dn ) );
+ if ( !contact.displayName.isNull() )
+ contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, contact.displayName ) );
+ Field::FieldList contactList;
+ contactList.append(
+ new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, contactFields ) );
+
+ lst.append( new Field::MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contactList ) );
+
+ lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, "-1" ) );
+ lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( newParent ) ) );
+ createTransfer( "movecontact", lst );
+}
+
+void MoveContactTask::moveContactToNewFolder( const ContactItem & contact, const int newSequenceNumber, const QString & folderDisplayName )
+{
+ client()->debug("MoveContactTask::moveContactToNewFolder()" );
+ m_folderSequence = newSequenceNumber;
+ m_folderDisplayName = folderDisplayName;
+ m_contactToMove = contact;
+
+}
+
+void MoveContactTask::onGo()
+{
+ // are we creating a folder first or can we just proceed as normal?
+ if ( m_folderDisplayName.isEmpty() )
+ RequestTask::onGo();
+ else // create the folder, when the folder has been created, onFolderCreated gets called and creates the contact
+ createFolder();
+}
+
+void MoveContactTask::onFolderCreated()
+{
+ client()->debug("MoveContactTask::onFolderCreated()" );
+ moveContact( m_contactToMove, m_folderId );
+ RequestTask::onGo();
+}
+#include "movecontacttask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h
new file mode 100644
index 00000000..f423981a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/movecontacttask.h
@@ -0,0 +1,49 @@
+/*
+ Kopete Groupwise Protocol
+ movecontacttask.h - Move a contact between folders on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MOVECONTACTTASK_H
+#define MOVECONTACTTASK_H
+
+#include "needfoldertask.h"
+
+/**
+Moves a contact between folders on the server
+
+@author SUSE AG
+*/
+class MoveContactTask : public NeedFolderTask
+{
+Q_OBJECT
+public:
+ MoveContactTask(Task* parent);
+ ~MoveContactTask();
+ void moveContact( const ContactItem & contact, const int newParent );
+ void moveContactToNewFolder( const ContactItem & contact, const int newSequenceNumber, const QString & folderDisplayName );
+ void onGo();
+protected:
+ void onFolderCreated();
+private:
+ int m_targetFolder;
+ QString m_dn;
+ QString m_displayName;
+ ContactItem m_contactToMove;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp
new file mode 100644
index 00000000..810326ee
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.cpp
@@ -0,0 +1,58 @@
+//
+// C++ Implementation: %{MODULE}
+//
+// Description:
+//
+//
+// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include "client.h"
+#include "tasks/createcontactinstancetask.h"
+#include "tasks/createfoldertask.h"
+
+#include "needfoldertask.h"
+
+NeedFolderTask::NeedFolderTask(Task* parent): ModifyContactListTask(parent)
+{
+}
+
+NeedFolderTask::~NeedFolderTask()
+{
+}
+
+void NeedFolderTask::createFolder()
+{
+ CreateFolderTask * cct = new CreateFolderTask( client()->rootTask() );
+ cct->folder( 0, m_folderSequence, m_folderDisplayName );
+ connect( cct, SIGNAL( gotFolderAdded( const FolderItem & ) ), client(), SIGNAL( folderReceived( const FolderItem & ) ) );
+ connect( cct, SIGNAL( gotFolderAdded( const FolderItem & ) ), SLOT( slotFolderAdded( const FolderItem & ) ) );
+ connect( cct, SIGNAL( finished() ), SLOT( slotFolderTaskFinished() ) );
+ cct->go( true );
+}
+
+void NeedFolderTask::slotFolderAdded( const FolderItem & addedFolder )
+{
+ // if this is the folder we were trying to create
+ if ( m_folderDisplayName == addedFolder.name )
+ {
+ client()->debug( QString( "NeedFolderTask::slotFolderAdded() - Folder %1 was created on the server, now has objectId %2" ).arg( addedFolder.name ).arg( addedFolder.id ) );
+ m_folderId = addedFolder.id;
+ }
+}
+
+void NeedFolderTask::slotFolderTaskFinished()
+{
+ CreateFolderTask *cct = ( CreateFolderTask* )sender();
+ if ( cct->success() )
+ {
+ // call our child class's action to be performed
+ onFolderCreated();
+ }
+ else
+ setError( 1, "Folder creation failed" );
+}
+
+#include "needfoldertask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h
new file mode 100644
index 00000000..8d6278df
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/needfoldertask.h
@@ -0,0 +1,39 @@
+//
+// C++ Interface: %{MODULE}
+//
+// Description:
+//
+//
+// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef NEEDFOLDERTASK_H
+#define NEEDFOLDERTASK_H
+
+#include "modifycontactlisttask.h"
+
+/**
+This Task is the ancestor of Tasks that may need to create a folder on the server before they can carry out their own operation.
+
+@author Kopete Developers
+*/
+class NeedFolderTask : public ModifyContactListTask
+{
+Q_OBJECT
+public:
+ NeedFolderTask(Task* parent);
+ ~NeedFolderTask();
+ void createFolder();
+ virtual void onFolderCreated() = 0;
+protected slots:
+ void slotFolderAdded( const FolderItem & );
+ void slotFolderTaskFinished();
+protected:
+ int m_folderSequence;
+ int m_folderId;
+ QString m_folderDisplayName;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp
new file mode 100644
index 00000000..772a0888
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.cpp
@@ -0,0 +1,185 @@
+/*
+ Kopete Groupwise Protocol
+ pollsearchresultstask.cpp - Poll the server to see if it has processed our search yet.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwfield.h"
+#include "response.h"
+
+#include "logintask.h"
+
+#include "pollsearchresultstask.h"
+
+using namespace GroupWise;
+
+PollSearchResultsTask::PollSearchResultsTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+PollSearchResultsTask::~PollSearchResultsTask()
+{
+}
+
+void PollSearchResultsTask::poll( const QString & queryHandle )
+{
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, queryHandle ) );
+ createTransfer( "getresults", lst );
+}
+
+bool PollSearchResultsTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ setError( response->resultCode() );
+ return true;
+ }
+
+ // look for the status code
+ Field::FieldList responseFields = response->fields();
+ Field::SingleField * sf = responseFields.findSingleField( NM_A_SZ_STATUS );
+ m_queryStatus = sf->value().toInt();
+
+ Field::MultiField * resultsArray = responseFields.findMultiField( NM_A_FA_RESULTS );
+ if ( !resultsArray )
+ {
+ setError( Protocol );
+ return true;
+ }
+ Field::FieldList matches = resultsArray->fields();
+ const Field::FieldListIterator end = matches.end();
+ for ( Field::FieldListIterator it = matches.find( NM_A_FA_CONTACT );
+ it != end;
+ it = matches.find( ++it, NM_A_FA_CONTACT ) )
+ {
+ Field::MultiField * mf = static_cast<Field::MultiField *>( *it );
+ Field::FieldList contact = mf->fields();
+ GroupWise::ContactDetails cd = extractUserDetails( contact );
+ m_results.append( cd );
+ }
+
+ // first field: NM_A_SZ_STATUS contains
+ #define SEARCH_PENDING 0
+ #define SEARCH_INPROGRESS 1
+ #define SEARCH_COMPLETED 2
+ #define SEARCH_TIMEOUT 3
+ #define SEARCH_CANCELLED 4
+ #define SEARCH_ERROR 5
+ // set a status code if needed
+ // followed by NM_A_FA_RESULTS, looks like a getdetails
+ // add an accessor to get at the results list of ContactItems, probably
+
+ if ( m_queryStatus != 2 )
+ setError( m_queryStatus );
+ else
+ setSuccess( m_queryStatus );
+ return true;
+}
+
+QValueList< GroupWise::ContactDetails > PollSearchResultsTask::results()
+{
+ return m_results;
+}
+
+int PollSearchResultsTask::queryStatus()
+{
+ return m_queryStatus;
+}
+
+GroupWise::ContactDetails PollSearchResultsTask::extractUserDetails( Field::FieldList & fields )
+{
+ ContactDetails cd;
+ cd.status = GroupWise::Invalid;
+ cd.archive = false;
+ // read the supplied fields, set metadata and status.
+ Field::SingleField * sf;
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_AUTH_ATTRIBUTE ) ) )
+ cd.authAttribute = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_DN ) ) )
+ cd.dn =sf->value().toString().lower(); // HACK: lowercased DN
+ if ( ( sf = fields.findSingleField ( "CN" ) ) )
+ cd.cn = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Given Name" ) ) )
+ cd.givenName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Surname" ) ) )
+ cd.surname = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "Full Name" ) ) )
+ cd.fullName = sf->value().toString();
+ if ( ( sf = fields.findSingleField ( "nnmArchive" ) ) )
+ cd.archive = ( sf->value().toInt() == 1 );
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_STATUS ) ) )
+ cd.status = sf->value().toInt();
+ if ( ( sf = fields.findSingleField ( NM_A_SZ_MESSAGE_BODY ) ) )
+ cd.awayMessage = sf->value().toString();
+ Field::MultiField * mf;
+ QMap< QString, QString > propMap;
+ if ( ( mf = fields.findMultiField ( NM_A_FA_INFO_DISPLAY_ARRAY ) ) )
+ {
+ Field::FieldList fl = mf->fields();
+ const Field::FieldListIterator end = fl.end();
+ for ( Field::FieldListIterator it = fl.begin(); it != end; ++it )
+ {
+ // assumes each property only present once
+ // check in logintask.cpp and if it's a multi field,
+ // get the value of this instance, check if it's already in the property map and append if found.
+ Field::SingleField * propField = dynamic_cast<Field::SingleField *>( *it );
+ if ( propField )
+ {
+ QString propName = propField->tag();
+ QString propValue = propField->value().toString();
+ propMap.insert( propName, propValue );
+ }
+ else
+ {
+ Field::MultiField * propList = dynamic_cast<Field::MultiField*>( *it );
+ if ( propList )
+ {
+ QString parentName = propList->tag();
+ Field::FieldList propFields = propList->fields();
+ const Field::FieldListIterator end = propFields.end();
+ for ( Field::FieldListIterator it = propFields.begin(); it != end; ++it )
+ {
+ propField = dynamic_cast<Field::SingleField *>( *it );
+ if ( propField )
+ {
+ QString propValue = propField->value().toString();
+ QString contents = propMap[ propField->tag() ];
+ if ( !contents.isEmpty() )
+ contents.append( ", " );
+ contents.append( propField->value().toString());
+ propMap.insert( propField->tag(), contents );
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( !propMap.empty() )
+ {
+ cd.properties = propMap;
+ }
+ return cd;
+}
+
+#include "pollsearchresultstask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h
new file mode 100644
index 00000000..11f810c0
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/pollsearchresultstask.h
@@ -0,0 +1,52 @@
+/*
+ Kopete Groupwise Protocol
+ pollsearchresultstask.h - Poll the server once to see if it has processed our search yet.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef POLLSEARCHRESULTSTASK_H
+#define POLLSEARCHRESULTSTASK_H
+
+#include <qvaluelist.h>
+
+#include "gwerror.h"
+
+#include "requesttask.h"
+
+/**
+Search results are polled on the server, using the search handle supplied by the client with the original query. This is a single poll request, which if successful, will retrieve the results. Otherwise, it will set a status code, so the ContactSearchTask can decide whether to poll again.
+
+@author SUSE AG
+*/
+class PollSearchResultsTask : public RequestTask
+{
+Q_OBJECT
+public:
+ enum SearchResultCode { Pending=0, InProgess=1, Completed=2, TimeOut=3, Cancelled=4, Error=5 };
+ PollSearchResultsTask(Task* parent);
+ ~PollSearchResultsTask();
+ void poll( const QString & queryHandle);
+ bool take( Transfer * transfer );
+ int queryStatus();
+ QValueList< GroupWise::ContactDetails > results();
+GroupWise::ContactDetails extractUserDetails( Field::FieldList & fields );
+private:
+ int m_queryStatus;
+ QValueList< GroupWise::ContactDetails > m_results;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp
new file mode 100644
index 00000000..003a6d60
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.cpp
@@ -0,0 +1,82 @@
+/*
+ Kopete Groupwise Protocol
+ privacyitemtask.cpp - Add an entry to the server side deny or allow lists
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "privacyitemtask.h"
+
+PrivacyItemTask::PrivacyItemTask( Task* parent) : RequestTask( parent )
+{
+}
+
+PrivacyItemTask::~PrivacyItemTask()
+{
+}
+
+QString PrivacyItemTask::dn() const
+{
+ return m_dn;
+}
+
+bool PrivacyItemTask::defaultDeny() const
+{
+ return m_default;
+}
+
+void PrivacyItemTask::allow( const QString & dn )
+{
+ m_dn = dn;
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_BLOCKING_ALLOW_ITEM, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_UTF8, dn ) );
+ createTransfer( "createblock", lst );
+}
+
+void PrivacyItemTask::deny( const QString & dn )
+{
+ m_dn = dn;
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_BLOCKING_DENY_ITEM, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_UTF8, dn ) );
+ createTransfer( "createblock", lst );
+}
+
+void PrivacyItemTask::removeAllow( const QString & dn )
+{
+ m_dn = dn;
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_BLOCKING_ALLOW_LIST, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_UTF8, dn ) );
+ createTransfer( "updateblocks", lst );
+
+}
+
+void PrivacyItemTask::removeDeny( const QString & dn )
+{
+ m_dn = dn;
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_BLOCKING_DENY_LIST, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_UTF8, dn ) );
+ createTransfer( "updateblocks", lst );
+}
+
+void PrivacyItemTask::defaultPolicy( bool defaultDeny )
+{
+ m_default = defaultDeny;
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_BLOCKING, NMFIELD_METHOD_UPDATE, 0, NMFIELD_TYPE_UTF8, ( defaultDeny ? "1" :"0" ) ) );
+ createTransfer( "updateblocks", lst );
+}
+
+#include "privacyitemtask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h
new file mode 100644
index 00000000..809cb7a4
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/privacyitemtask.h
@@ -0,0 +1,50 @@
+/*
+ Kopete Groupwise Protocol
+ privacyitemtask.h - Add an entry to the server side deny or allow lists
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef PRIVACYITEMTASK_H
+#define PRIVACYITEMTASK_H
+
+#include "requesttask.h"
+
+/**
+Adds a contact to the server side allow or deny lists
+
+@author SUSE AG
+*/
+class PrivacyItemTask : public RequestTask
+{
+Q_OBJECT
+public:
+ PrivacyItemTask( Task* parent);
+ ~PrivacyItemTask();
+ void allow( const QString & dn );
+ void deny( const QString & dn );
+ void removeAllow( const QString & dn );
+ void removeDeny( const QString & dn );
+ void defaultPolicy( bool defaultDeny );
+ QString dn() const;
+ bool defaultDeny() const;
+ // void contacts( const QStringList & contacts );
+private:
+ bool m_default;
+ QString m_dn;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp
new file mode 100644
index 00000000..2b252ff5
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.cpp
@@ -0,0 +1,39 @@
+/*
+ Kopete Groupwise Protocol
+ rejectinvitetask.cpp - Decline an invitation to chat
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "rejectinvitetask.h"
+
+RejectInviteTask::RejectInviteTask(Task* parent): RequestTask(parent)
+{
+}
+
+RejectInviteTask::~RejectInviteTask()
+{
+}
+
+void RejectInviteTask::reject( const GroupWise::ConferenceGuid & guid )
+{
+ Field::FieldList lst, tmp;
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ createTransfer( "rejectconf", lst );
+}
+
+#include "rejectinvitetask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h
new file mode 100644
index 00000000..b82f4e77
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/rejectinvitetask.h
@@ -0,0 +1,41 @@
+/*
+ Kopete Groupwise Protocol
+ rejectinvitetask.h - Decline an invitation to chat
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef REJECTINVITETASK_H
+#define REJECTINVITETASK_H
+
+#include "requesttask.h"
+
+/**
+Used to reject an invitation to join a conference
+
+@author SUSE AG
+*/
+class RejectInviteTask : public RequestTask
+{
+Q_OBJECT
+public:
+ RejectInviteTask(Task* parent);
+ ~RejectInviteTask();
+ void reject( const GroupWise::ConferenceGuid & guid );
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp
new file mode 100644
index 00000000..3788bb6e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.cpp
@@ -0,0 +1,76 @@
+/*
+ Kopete Groupwise Protocol
+ requesttask.cpp - Ancestor of all tasks that carry out a user request
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwfield.h"
+#include "client.h"
+#include "request.h"
+#include "response.h"
+#include "requestfactory.h"
+
+#include "requesttask.h"
+
+RequestTask::RequestTask( Task * parent )
+: Task( parent )
+{
+}
+
+bool RequestTask::forMe( Transfer * transfer ) const
+{
+ // see if we can down-cast transfer to a Response
+ Response * theResponse = dynamic_cast<Response *>(transfer);
+ return (theResponse && theResponse->transactionId() == m_transactionId );
+}
+
+void RequestTask::createTransfer( const QString & command, const Field::FieldList & fields )
+{
+ Request * request = client()->requestFactory()->request( command );
+ m_transactionId = request->transactionId();
+ request->setFields( fields );
+ Task::setTransfer( request );
+}
+
+void RequestTask::onGo()
+{
+ if ( transfer() )
+ {
+ client()->debug( QString( "%1::onGo() - sending %2 fields" ).arg( className() ).arg( static_cast<Request *>( transfer() )->command() ) );
+ send( static_cast<Request *>( transfer() ) );
+ }
+ else
+ client()->debug( "RequestTask::onGo() - called prematurely, no transfer set." );
+}
+
+bool RequestTask::take( Transfer * transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ client()->debug( "RequestTask::take() - Default take() Accepting transaction ack, taking no further action" );
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( response->resultCode() == GroupWise::None )
+ setSuccess();
+ else
+ setError( response->resultCode() );
+ return true;
+ }
+ else
+ return false;
+}
+
+#include "requesttask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h
new file mode 100644
index 00000000..30ee57ed
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/requesttask.h
@@ -0,0 +1,42 @@
+/*
+ Kopete Groupwise Protocol
+ requesttask.h - Ancestor of all tasks that carry out a user request
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_REQUESTTASK_H
+#define GW_REQUESTTASK_H
+
+#include "task.h"
+
+class Transfer;
+
+class RequestTask : public Task
+{
+Q_OBJECT
+ public:
+ RequestTask( Task *parent );
+ bool take( Transfer * transfer );
+ virtual void onGo();
+ protected:
+ bool forMe( Transfer * transfer ) const;
+ void createTransfer( const QString & command, const Field::FieldList & fields );
+ private:
+ int m_transactionId;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp
new file mode 100644
index 00000000..4ee35549
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.cpp
@@ -0,0 +1,127 @@
+/*
+ Kopete Groupwise Protocol
+ searchchattask.cpp - high level search for users on the server - spawns PollSearchResultsTasks
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdatetime.h>
+#include <qtimer.h>
+
+#include "client.h"
+#include "gwerror.h"
+#include "gwfield.h"
+#include "response.h"
+
+#include "getchatsearchresultstask.h"
+
+#include "searchchattask.h"
+
+
+// the delay we allow the server to initially do the search
+#define GW_POLL_INITIAL_DELAY 1000
+// the maximum number of times to poll the server
+#define GW_POLL_MAXIMUM 5
+// the frequency between subsequent polls
+#define GW_POLL_FREQUENCY_MS 8000
+
+using namespace GroupWise;
+
+SearchChatTask::SearchChatTask(Task* parent): RequestTask(parent), m_polls( 0 )
+{
+}
+
+
+SearchChatTask::~SearchChatTask()
+{
+}
+
+void SearchChatTask::search( SearchType type )
+{
+ Field::FieldList lst;
+ // object Id identifies the search for later reference
+ lst.append( new Field::SingleField( NM_A_B_ONLY_MODIFIED, 0, NMFIELD_TYPE_BOOL, ( type == FetchAll ? 0 : 1 ) ) );
+ createTransfer( "chatsearch", lst );
+}
+
+bool SearchChatTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got return code in response << " << response->resultCode() << endl;
+ setError( response->resultCode() );
+ return true;
+ }
+ Field::FieldList responseFields = response->fields();
+ Field::SingleField * sf = responseFields.findSingleField( NM_A_UD_OBJECT_ID );
+ m_objectId = sf->value().toInt();
+
+ // now start the results poll timer
+ QTimer::singleShot( GW_POLL_INITIAL_DELAY, this, SLOT( slotPollForResults() ) );
+ return true;
+}
+
+void SearchChatTask::slotPollForResults()
+{
+ //create a PollSearchResultsTask
+ GetChatSearchResultsTask * gcsrt = new GetChatSearchResultsTask( client()->rootTask() );
+ gcsrt->poll( m_objectId );
+ connect( gcsrt, SIGNAL( finished() ), SLOT( slotGotPollResults() ) );
+ gcsrt->go( true );
+}
+
+void SearchChatTask::slotGotPollResults()
+{
+ GetChatSearchResultsTask * gcsrt = (GetChatSearchResultsTask *)sender();
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "status code is " << gcsrt->queryStatus() << endl;
+ m_polls++;
+ switch ( gcsrt->queryStatus() )
+ {
+ case GetChatSearchResultsTask::GettingData:
+ if ( m_polls < GW_POLL_MAXIMUM ) // restart timer
+ QTimer::singleShot( GW_POLL_FREQUENCY_MS, this, SLOT( slotPollForResults() ) );
+ else
+ setSuccess( gcsrt->statusCode() );
+ break;
+ case GetChatSearchResultsTask::DataRetrieved:
+ // got some results, there may be more.
+ m_results += gcsrt->results();
+ QTimer::singleShot( 0, this, SLOT( slotPollForResults() ) );
+ break;
+ case GetChatSearchResultsTask::Completed:
+ m_results += gcsrt->results();
+ setSuccess();
+ break;
+ case GetChatSearchResultsTask::Cancelled:
+ setError(gcsrt->statusCode() );
+ break;
+ case GetChatSearchResultsTask::Error:
+ setError( gcsrt->statusCode() );
+ break;
+ }
+}
+
+QValueList< GroupWise::ChatroomSearchResult > SearchChatTask::results()
+{
+ return m_results;
+}
+
+#include "searchchattask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h
new file mode 100644
index 00000000..2f24e075
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchchattask.h
@@ -0,0 +1,66 @@
+/*
+ Kopete Groupwise Protocol
+ searchchattask.h - search for chatrooms on the server - spawns PollSearchResultsTasks
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SEARCHCHATTASK_H
+#define SEARCHCHATTASK_H
+
+#include "gwerror.h"
+
+#include "requesttask.h"
+
+class QTimer;
+
+/**
+This Task searches for chatrooms on the server
+
+@author SUSE Linux Products GmbH
+ */
+class SearchChatTask : public RequestTask
+{
+ Q_OBJECT
+ public:
+ enum SearchType { FetchAll=0, SinceLastSearch };
+
+ SearchChatTask(Task* parent);
+
+ ~SearchChatTask();
+ /**
+ * Create the search query
+ */
+ void search( SearchType type );
+ /**
+ * If the query was accepted, start a timer to poll for results using PollSearchResultsTask
+ */
+ virtual bool take( Transfer * transfer );
+ /**
+ * Access the results of the search
+ */
+ QValueList< GroupWise::ChatroomSearchResult > results();
+ protected slots:
+ void slotPollForResults();
+ void slotGotPollResults();
+ private:
+ QTimer * m_resultsPollTimer;
+ QValueList< GroupWise::ChatroomSearchResult > m_results;
+ int m_polls;
+ int m_objectId; // used to identify our query to the server, so we can poll for its results
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp
new file mode 100644
index 00000000..cd199ad8
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.cpp
@@ -0,0 +1,137 @@
+/*
+ Kopete Groupwise Protocol
+ searchusertask.cpp - high level search for users on the server - spawns PollSearchResultsTasks
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdatetime.h>
+#include <qtimer.h>
+
+#include "client.h"
+#include "gwerror.h"
+#include "gwfield.h"
+#include "response.h"
+
+#include "pollsearchresultstask.h"
+
+#include "searchusertask.h"
+
+// the delay we allow the server to initially do the search
+#define GW_POLL_INITIAL_DELAY 1000
+// the maximum number of times to poll the server
+#define GW_POLL_MAXIMUM 5
+// the frequency between subsequent polls
+#define GW_POLL_FREQUENCY_MS 8000
+
+using namespace GroupWise;
+
+SearchUserTask::SearchUserTask(Task* parent): RequestTask(parent), m_polls( 0 )
+{
+}
+
+
+SearchUserTask::~SearchUserTask()
+{
+}
+
+void SearchUserTask::search( const QValueList<UserSearchQueryTerm> & query )
+{
+ m_queryHandle = QString::number( QDateTime::currentDateTime().toTime_t () );
+ Field::FieldList lst;
+ if ( query.isEmpty() )
+ {
+ setError( 1, "no query terms" );
+ return;
+ }
+ // object Id identifies the search for later reference
+ lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, m_queryHandle ) );
+ QValueList<UserSearchQueryTerm>::ConstIterator it = query.begin();
+ const QValueList<UserSearchQueryTerm>::ConstIterator end = query.end();
+ for ( ; it != end; ++it )
+ {
+ Field::SingleField * fld = new Field::SingleField( (*it).field.ascii(), (*it).operation, 0, NMFIELD_TYPE_UTF8, (*it).argument );
+ lst.append( fld );
+ }
+ //lst.append( new Field::SingleField( "Given Name", 0, NMFIELD_TYPE_UTF8, [ NMFIELD_METHOD_EQUAL | NMFIELD_METHOD_MATCHBEGIN | NMFIELD_METHOD_MATCHEND | NMFIELD_METHOD_SEARCH ], searchTerm );
+ // Or "Surname", NM_A_SZ_USERID, NM_A_SZ_TITLE, NM_A_SZ_DEPARTMENT in other fields
+
+ createTransfer( "createsearch", lst );
+}
+
+bool SearchUserTask::take( Transfer * transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+ Response * response = dynamic_cast<Response *>( transfer );
+ if ( !response )
+ return false;
+ if ( response->resultCode() )
+ {
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "got return code in response << " << response->resultCode() << endl;
+ setError( response->resultCode() );
+ return true;
+ }
+ // now start the results poll timer
+ QTimer::singleShot( GW_POLL_INITIAL_DELAY, this, SLOT( slotPollForResults() ) );
+ return true;
+}
+
+void SearchUserTask::slotPollForResults()
+{
+ //create a PollSearchResultsTask
+ PollSearchResultsTask * psrt = new PollSearchResultsTask( client()->rootTask() );
+ psrt->poll( m_queryHandle );
+ connect( psrt, SIGNAL( finished() ), SLOT( slotGotPollResults() ) );
+ psrt->go( true );
+}
+
+void SearchUserTask::slotGotPollResults()
+{
+ PollSearchResultsTask * psrt = (PollSearchResultsTask *)sender();
+ kdDebug( GROUPWISE_DEBUG_GLOBAL ) << k_funcinfo << "status code is " << psrt->queryStatus() << endl;
+ m_polls++;
+ switch ( psrt->queryStatus() )
+ {
+ case PollSearchResultsTask::Pending:
+ case PollSearchResultsTask::InProgess:
+ if ( m_polls < GW_POLL_MAXIMUM ) // restart timer
+ QTimer::singleShot( GW_POLL_FREQUENCY_MS, this, SLOT( slotPollForResults() ) );
+ else
+ setSuccess( psrt->statusCode() );
+ break;
+ case PollSearchResultsTask::Completed:
+ m_results = psrt->results();
+ setSuccess();
+ break;
+ case PollSearchResultsTask::Cancelled:
+ setError(psrt->statusCode() );
+ break;
+ case PollSearchResultsTask::Error:
+ setError( psrt->statusCode() );
+ break;
+ case PollSearchResultsTask::TimeOut:
+ setError( psrt->statusCode() );
+ break;
+ }
+}
+
+QValueList< GroupWise::ContactDetails > SearchUserTask::results()
+{
+ return m_results;
+}
+
+#include "searchusertask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h
new file mode 100644
index 00000000..28c09b02
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/searchusertask.h
@@ -0,0 +1,63 @@
+/*
+ Kopete Groupwise Protocol
+ searchusertask.h - high level search for users on the server - spawns PollSearchResultsTasks
+
+ Copyright (c) 2005 SUSE Linux Products GmbH http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SEARCHUSERTASK_H
+#define SEARCHUSERTASK_H
+
+#include "requesttask.h"
+
+class QTimer;
+
+/**
+This Task performs user searching on the server
+
+@author SUSE AG
+*/
+class SearchUserTask : public RequestTask
+{
+Q_OBJECT
+public:
+ SearchUserTask(Task* parent);
+
+ ~SearchUserTask();
+ /**
+ * Create the search query
+ * @param query a list of search terms
+ */
+ void search( const QValueList<GroupWise::UserSearchQueryTerm> & query);
+ /**
+ * If the query was accepted, start a timer to poll for results using PollSearchResultsTask
+ */
+ virtual bool take( Transfer * transfer );
+ /**
+ * Access the results of the search
+ */
+ QValueList< GroupWise::ContactDetails > results();
+protected slots:
+ void slotPollForResults();
+ void slotGotPollResults();
+private:
+ QString m_queryHandle; // used to identify our query to the server, so we can poll for its results
+ QTimer * m_resultsPollTimer;
+ QValueList< GroupWise::ContactDetails > m_results;
+ int m_polls;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp
new file mode 100644
index 00000000..b3a9614f
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.cpp
@@ -0,0 +1,42 @@
+/*
+ Kopete Groupwise Protocol
+ sendinvitetask.cpp - invites someone to join a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "sendinvitetask.h"
+
+SendInviteTask::SendInviteTask(Task* parent): RequestTask(parent)
+{
+}
+
+SendInviteTask::~SendInviteTask()
+{
+}
+
+void SendInviteTask::invite( const GroupWise::ConferenceGuid & guid, const QStringList & invitees, const GroupWise::OutgoingMessage & msg)
+{
+ Field::FieldList lst, tmp;
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ QValueListConstIterator<QString> end = invitees.end();
+ for ( QValueListConstIterator<QString> it = invitees.begin(); it != end; ++it )
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) );
+ if ( !msg.message.isEmpty() )
+ lst.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, msg.message ) );
+ createTransfer( "sendinvite", lst );
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h
new file mode 100644
index 00000000..c8cf1d9b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendinvitetask.h
@@ -0,0 +1,43 @@
+/*
+ Kopete Groupwise Protocol
+ sendinvitetask.h - invites someone to join a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SENDINVITETASK_H
+#define SENDINVITETASK_H
+
+#include "gwerror.h"
+
+#include "requesttask.h"
+
+/**
+This sends an invitation to a conference
+
+@author SUSE AG
+*/
+class SendInviteTask : public RequestTask
+{
+public:
+ SendInviteTask(Task* parent);
+ ~SendInviteTask();
+ void invite( const GroupWise::ConferenceGuid & guid, const QStringList & invitees, const GroupWise::OutgoingMessage & msg );
+private:
+ QString m_confId;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp
new file mode 100644
index 00000000..290b9d9b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.cpp
@@ -0,0 +1,51 @@
+/*
+ Kopete Groupwise Protocol
+ sendmessagetask.cpp - sends a message to a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "sendmessagetask.h"
+
+SendMessageTask::SendMessageTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+SendMessageTask::~SendMessageTask()
+{
+}
+
+void SendMessageTask::message( const QStringList & recipientDNList, const OutgoingMessage & msg )
+{
+ // Assumes the conference is instantiated, unlike Gaim's nm_send_message
+ Field::FieldList lst, tmp, msgBodies;
+ // list containing GUID
+ tmp.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, msg.guid ) );
+ lst.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, tmp ) );
+ msgBodies.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, msg.rtfMessage ) );
+ // message body type indicator / separator?
+ msgBodies.append( new Field::SingleField( NM_A_UD_MESSAGE_TYPE, 0, NMFIELD_TYPE_UDWORD, 0 ) );
+ // message body plaintext
+ msgBodies.append( new Field::SingleField( NM_A_SZ_MESSAGE_TEXT, 0, NMFIELD_TYPE_UTF8, msg.message ) );
+ // list containing message bodies
+ lst.append( new Field::MultiField( NM_A_FA_MESSAGE, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, msgBodies ) );
+ // series of participants (may be empty )
+ QValueListConstIterator<QString> end = recipientDNList.end();
+ for ( QValueListConstIterator<QString> it = recipientDNList.begin(); it != end; ++it )
+ lst.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_DN, *it ) );
+ createTransfer( "sendmessage", lst );
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h
new file mode 100644
index 00000000..f45e491f
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/sendmessagetask.h
@@ -0,0 +1,41 @@
+/*
+ Kopete Groupwise Protocol
+ sendmessagetask.h - sends a message to a conference
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SENDMESSAGETASK_H
+#define SENDMESSAGETASK_H
+
+#include "client.h"
+#include "requesttask.h"
+
+/**
+Sends messages to a particular conference on the server
+
+@author SUSE AG
+*/
+class SendMessageTask : public RequestTask
+{
+public:
+ SendMessageTask(Task* parent);
+ ~SendMessageTask();
+
+ void message( const QStringList & recipientDNList, const OutgoingMessage & msg );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp
new file mode 100644
index 00000000..0744ff8a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.cpp
@@ -0,0 +1,69 @@
+/*
+ Kopete Groupwise Protocol
+ setstatustask.cpp - Sets our status on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "setstatustask.h"
+
+using namespace GroupWise;
+
+SetStatusTask::SetStatusTask(Task* parent): RequestTask(parent)
+{
+}
+
+SetStatusTask::~SetStatusTask()
+{
+}
+
+void SetStatusTask::status( Status newStatus, const QString &awayMessage, const QString &autoReply )
+{
+ if ( newStatus > GroupWise::Invalid )
+ {
+ setError( 1, "Invalid Status" );
+ return;
+ }
+
+ m_status = newStatus;
+ m_awayMessage = awayMessage;
+ m_autoReply = autoReply;
+
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_STATUS, 0, NMFIELD_TYPE_UTF8, QString::number( newStatus ) ) );
+ if ( !awayMessage.isNull() )
+ lst.append( new Field::SingleField( NM_A_SZ_STATUS_TEXT, 0, NMFIELD_TYPE_UTF8, awayMessage ) );
+ if ( !autoReply.isNull() )
+ lst.append( new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, autoReply ) );
+ createTransfer( "setstatus", lst );
+}
+
+Status SetStatusTask::requestedStatus() const
+{
+ return m_status;
+}
+
+QString SetStatusTask::awayMessage() const
+{
+ return m_awayMessage;
+}
+
+QString SetStatusTask::autoReply() const
+{
+ return m_autoReply;
+}
+
+#include "setstatustask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h
new file mode 100644
index 00000000..2d3c53d7
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/setstatustask.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Groupwise Protocol
+ setstatustask.h - Sets our status on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SETSTATUSTASK_H
+#define SETSTATUSTASK_H
+
+#include "gwerror.h"
+#include "requesttask.h"
+
+/**
+@author Kopete Developers
+*/
+class SetStatusTask : public RequestTask
+{
+Q_OBJECT
+public:
+ SetStatusTask(Task* parent);
+ ~SetStatusTask();
+ void status( GroupWise::Status newStatus, const QString &awayMessage, const QString &autoReply );
+ GroupWise::Status requestedStatus() const;
+ QString awayMessage() const;
+ QString autoReply() const;
+private:
+ GroupWise::Status m_status;
+ QString m_awayMessage;
+ QString m_autoReply;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp
new file mode 100644
index 00000000..8f8eccd4
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.cpp
@@ -0,0 +1,47 @@
+/*
+ Kopete Groupwise Protocol
+ statustask.cpp - Event handling task responsible for status change events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+
+#include "statustask.h"
+
+StatusTask::StatusTask(Task* parent): EventTask(parent)
+{
+ registerEvent( GroupWise::StatusChange );
+}
+
+StatusTask::~StatusTask()
+{
+}
+
+bool StatusTask::take( Transfer * transfer )
+{
+ EventTransfer * event;
+ if ( forMe( transfer, event ) )
+ {
+ client()->debug( "Got a status change!" );
+ client()->debug( QString( "%1 changed status to %2, message: %3" ).arg( event->source() ).arg( event->status() ).arg( event->statusText() ) );
+ emit gotStatus( event->source().lower(), event->status(), event->statusText() );
+ return true;
+ }
+ else
+ return false;
+}
+#include "statustask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h
new file mode 100644
index 00000000..8e4994ff
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/statustask.h
@@ -0,0 +1,40 @@
+/*
+ Kopete Groupwise Protocol
+ statustask.h - Event handling task responsible for status change events
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef STATUSTASK_H
+#define STATUSTASK_H
+
+#include "eventtask.h"
+
+/**
+@author Kopete Developers
+*/
+class StatusTask : public EventTask
+{
+Q_OBJECT
+public:
+ StatusTask(Task* parent);
+ ~StatusTask();
+ bool take( Transfer * transfer );
+signals:
+ void gotStatus( const QString & contactId, Q_UINT16 status, const QString & statusText );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am
new file mode 100644
index 00000000..6a10925b
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/tests/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES = -I$(top_srcdir)/protocols/groupwise/libgroupwise/qca/src -I$(srcdir)/../../libgroupwise/ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src $(all_includes)
+METASOURCES = AUTO
+noinst_PROGRAMS = task_take_test
+
+task_take_test_LDADD = -lqt-mt ../../libgwtest.la
+
+task_take_test_SOURCES = task_take_test.cpp
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp
new file mode 100644
index 00000000..140e851f
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/tests/task_take_test.cpp
@@ -0,0 +1,18 @@
+//
+// C++ Implementation: task_take_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+//#include "requesttask.h"
+
+int main()
+{
+ // balls, root task requires client, will test in situ instead
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp
new file mode 100644
index 00000000..b835c525
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.cpp
@@ -0,0 +1,44 @@
+/*
+ Kopete Groupwise Protocol
+ typingtask.cpp - sends typing notifications to the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+//#include "eventtransfer.h"
+
+#include "typingtask.h"
+
+TypingTask::TypingTask(Task* parent): RequestTask(parent)
+{
+}
+
+
+TypingTask::~TypingTask()
+{
+}
+
+void TypingTask::typing( const GroupWise::ConferenceGuid & conferenceGuid, const bool typing )
+{
+ Field::FieldList typingNotification, outgoingList;
+ typingNotification.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, conferenceGuid ) );
+ typingNotification.append( new Field::SingleField( NM_A_SZ_TYPE, 0, NMFIELD_TYPE_UTF8,
+ QString::number( typing ? GroupWise::UserTyping : GroupWise::UserNotTyping ) ) );
+ outgoingList.append( new Field::MultiField( NM_A_FA_CONVERSATION, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, typingNotification ) );
+ createTransfer( "sendtyping", outgoingList );
+}
+
+#include "typingtask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h
new file mode 100644
index 00000000..4f4da1cd
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/typingtask.h
@@ -0,0 +1,41 @@
+/*
+ Kopete Groupwise Protocol
+ typingtask.h - sends typing notifications to the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TYPINGTASK_H
+#define TYPINGTASK_H
+
+#include "requesttask.h"
+
+/**
+ Notifies the server that we are typing or are no longer typing in a particular conversation
+
+@author Kopete Developers
+*/
+class TypingTask : public RequestTask
+{
+Q_OBJECT
+
+public:
+ TypingTask(Task* parent);
+ ~TypingTask();
+ void typing( const GroupWise::ConferenceGuid & guid, const bool typing );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp
new file mode 100644
index 00000000..d8c1a68a
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.cpp
@@ -0,0 +1,76 @@
+/*
+ Kopete Groupwise Protocol
+ updatecontacttask.cpp - rename a contact on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "gwfield.h"
+
+#include "updatecontacttask.h"
+
+using namespace GroupWise;
+
+UpdateContactTask::UpdateContactTask(Task* parent): UpdateItemTask(parent)
+{
+}
+
+
+UpdateContactTask::~UpdateContactTask()
+{
+}
+
+QString UpdateContactTask::displayName()
+{
+ return m_name;
+}
+
+void UpdateContactTask::renameContact( const QString & newName, const QValueList<ContactItem> & contactInstances )
+{
+ m_name = newName;
+ // build a list of delete, add fields that removes each instance on the server and then readds it with the new name
+ Field::FieldList lst;
+ const QValueList<ContactItem>::ConstIterator end = contactInstances.end();
+ for( QValueList<ContactItem>::ConstIterator it = contactInstances.begin(); it != end; ++it )
+ {
+ Field::FieldList contactFields;
+ contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, (*it).id ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, (*it).parentId ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, (*it).sequence ) );
+ if ( !(*it).dn.isNull() )
+ contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, (*it).dn ) );
+ if ( !(*it).displayName.isNull() )
+ contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, (*it).displayName ) );
+ lst.append(
+ new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, contactFields ) );
+ }
+ for( QValueList<ContactItem>::ConstIterator it = contactInstances.begin(); it != end; ++it )
+ {
+ Field::FieldList contactFields;
+ contactFields.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, (*it).id ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, (*it).parentId ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, (*it).sequence ) );
+ if ( !(*it).dn.isNull() )
+ contactFields.append( new Field::SingleField( NM_A_SZ_DN, 0, NMFIELD_TYPE_UTF8, (*it).dn ) );
+ contactFields.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, newName ) );
+ lst.append(
+ new Field::MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_ARRAY, contactFields ) );
+ }
+ //lst.dump( true );
+ UpdateItemTask::item( lst );
+}
+
+#include "updatecontacttask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h
new file mode 100644
index 00000000..7e6ac899
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatecontacttask.h
@@ -0,0 +1,44 @@
+/*
+ Kopete Groupwise Protocol
+ updatecontacttask.h - rename a contact on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef UPDATECONTACTTASK_H
+#define UPDATECONTACTTASK_H
+
+#include "gwerror.h"
+
+#include "updateitemtask.h"
+
+/**
+ * Renames a contact on the server
+ * @author Kopete Developers
+ */
+class UpdateContactTask : public UpdateItemTask
+{
+Q_OBJECT
+public:
+ UpdateContactTask(Task* parent);
+ ~UpdateContactTask();
+ void renameContact( const QString& newName, const QValueList<GroupWise::ContactItem> & contactInstances );
+ QString displayName();
+private:
+ QString m_name;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp
new file mode 100644
index 00000000..fef5d2fe
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.cpp
@@ -0,0 +1,59 @@
+/*
+ Kopete Groupwise Protocol
+ updatefoldertask.cpp - rename a folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "gwfield.h"
+
+#include "updatefoldertask.h"
+
+UpdateFolderTask::UpdateFolderTask(Task* parent): UpdateItemTask(parent)
+{
+}
+
+UpdateFolderTask::~UpdateFolderTask()
+{
+}
+
+void UpdateFolderTask::renameFolder( const QString & newName, const GroupWise::FolderItem & existing )
+{
+ Field::FieldList lst;
+ // add the old version of the folder, marked delete
+ lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_DELETE, 0, NMFIELD_TYPE_ARRAY, folderToFields( existing) ) );
+
+ GroupWise::FolderItem renamed = existing;
+ renamed.name = newName;
+ // add the new version of the folder, marked add
+ lst.append( new Field::MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_ADD, 0, NMFIELD_TYPE_ARRAY, folderToFields( renamed ) ) );
+ // let our parent class package it up as a contactlist in a transfer
+ UpdateItemTask::item( lst );
+}
+
+Field::FieldList UpdateFolderTask::folderToFields( const GroupWise::FolderItem & folder )
+{
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, folder.id ) );
+ lst.append( new Field::SingleField( NM_A_SZ_PARENT_ID, 0, NMFIELD_TYPE_UTF8, 0 ) );
+ lst.append( new Field::SingleField( NM_A_SZ_TYPE, 0, NMFIELD_TYPE_UTF8, 1 ) );
+ lst.append( new Field::SingleField( NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_TYPE_UTF8, folder.sequence ) );
+ if ( !folder.name.isEmpty() )
+ lst.append( new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, folder.name ) );
+ return lst;
+}
+
+#include "updatefoldertask.moc"
+
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h
new file mode 100644
index 00000000..230bd563
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updatefoldertask.h
@@ -0,0 +1,42 @@
+/*
+ Kopete Groupwise Protocol
+ updatefoldertask.h - rename a folder on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef UPDATEFOLDERTASK_H
+#define UPDATEFOLDERTASK_H
+
+#include <updateitemtask.h>
+
+/**
+Renames a folder on the server
+
+@author Kopete Developers
+*/
+class UpdateFolderTask : public UpdateItemTask
+{
+Q_OBJECT
+public:
+ UpdateFolderTask(Task* parent);
+ ~UpdateFolderTask();
+ void renameFolder( const QString & newName, const GroupWise::FolderItem & existing );
+protected:
+ Field::FieldList folderToFields( const GroupWise::FolderItem & folder );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp
new file mode 100644
index 00000000..1af4ef12
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.cpp
@@ -0,0 +1,39 @@
+/*
+ Kopete Groupwise Protocol
+ updateitemtask.cpp - ancestor for tasks that rename objects on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "updateitemtask.h"
+
+UpdateItemTask::UpdateItemTask( Task* parent) : RequestTask( parent )
+{
+}
+
+
+UpdateItemTask::~UpdateItemTask()
+{
+}
+
+void UpdateItemTask::item( Field::FieldList updateItemFields )
+{
+ Field::FieldList lst;
+ lst.append( new Field::MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, updateItemFields ) );
+ createTransfer( "updateitem", lst );
+}
+
+#include "updateitemtask.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h
new file mode 100644
index 00000000..a087d276
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tasks/updateitemtask.h
@@ -0,0 +1,40 @@
+/*
+ Kopete Groupwise Protocol
+ updateitemtask.h - ancestor for tasks that rename objects on the server
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef UPDATEITEMTASK_H
+#define UPDATEITEMTASK_H
+
+#include "requesttask.h"
+
+/**
+Rename a folder or contact on the server. In future may be used for changing the order of folders or contacts relative to one another, but this is not supported by Kopete yet.
+
+@author SUSE AG
+*/
+class UpdateItemTask : public RequestTask
+{
+Q_OBJECT
+public:
+ UpdateItemTask( Task* parent );
+ ~UpdateItemTask();
+ void item( Field::FieldList updateItemFields );
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am b/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am
new file mode 100644
index 00000000..33a603ad
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES = -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise/qca/src \
+ -I$(top_srcdir)/kopete/protocols/groupwise/libgroupwise \
+ -I$(top_srcdir)/kopete/protocols/groupwise \
+ $(all_includes)
+METASOURCES = AUTO
+noinst_PROGRAMS = clientstream_test field_test coreprotocol_test client_test
+coreprotocol_test_LDFLAGS = -no-undefined $(all_libraries)
+coreprotocol_test_SOURCES = coreprotocol_test.cpp
+coreprotocol_test_LDADD = \
+ ../libgwtest.la -lqt-mt
+field_test_LDFLAGS = -no-undefined $(all_libraries)
+field_test_SOURCES = field_test.cpp
+field_test_LDADD = \
+ ../libgwtest.la -lqt-mt
+
+clientstream_test_SOURCES = clientstream_test.cpp
+clientstream_test_LDADD = -lqt-mt \
+ ../../kopete_groupwise.la
+
+client_test_SOURCES = client_test.cpp
+client_test_LDADD = ../../../../protocols/groupwise/kopete_groupwise.la \
+ ../libgwtest.la -lqt-mt
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp
new file mode 100644
index 00000000..22f92282
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/client_test.cpp
@@ -0,0 +1,10 @@
+#include "client.h"
+#include "task.h"
+
+int main()
+{
+ Client c;
+ Task rootTask( &c, true );
+
+ return 0;
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp
new file mode 100644
index 00000000..bbd10ee8
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.cpp
@@ -0,0 +1,107 @@
+#include "clientstream_test.h"
+
+ClientStreamTest::ClientStreamTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ //myConnector->setOptHostPort( "localhost", 8300 );
+ myConnector->setOptHostPort( "reiser.suse.de", 8300 );
+ myConnector->setOptSSL( true );
+ Q_ASSERT( QCA::isSupported(QCA::CAP_TLS) );
+ myTLS = new QCA::TLS;
+ myTLSHandler = new QCATLSHandler( myTLS );
+ myTestObject = new ClientStream( myConnector, myTLSHandler, 0);
+ // notify when the transport layer is connected
+ connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ // it's necessary to catch this signal and tell the TLS handler to proceed, even if we don't check cert validity
+ connect( myTLSHandler, SIGNAL(tlsHandshaken()), SLOT(slotTLSHandshaken()) );
+ // notify and start sending
+ connect( myTestObject, SIGNAL( securityLayerActivated(int) ), SLOT( slotSend(int) ) );
+ connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) );
+
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+}
+
+ClientStreamTest::~ClientStreamTest()
+{
+ delete myTestObject;
+ delete myTLSHandler;
+ delete myTLS;
+ delete myConnector;
+}
+
+void ClientStreamTest::slotDoTest()
+{
+ NovellDN dn;
+ dn.dn = "maeuschen";
+ dn.server = "reiser.suse.de";
+ // connect to server
+ qDebug( "connecting to server ");
+ myTestObject->connectToServer( dn, true ); // fine up to here...
+}
+
+void ClientStreamTest::slotConnected()
+{
+ qDebug( "connection is up");
+}
+
+void ClientStreamTest::slotWarning(int warning)
+{
+ qDebug( "warning: %i", warning);
+}
+
+void ClientStreamTest::slotsend(int layer)
+{
+ qDebug( "security layer is up: %i", layer);
+ RequestFactory testFactory;
+ // we're not connecting...
+ qDebug( "sending request" );
+ // send a request
+ QCString command("login");
+ Request * firstRequest = testFactory.request( command );
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_USERID, 0, NMFIELD_TYPE_UTF8, "maeuschen" ) );
+ lst.append( new Field::SingleField( NM_A_SZ_CREDENTIALS, 0, NMFIELD_TYPE_UTF8, "maeuschen" ) );
+ lst.append( new Field::SingleField( NM_A_SZ_USER_AGENT, 0, NMFIELD_TYPE_UTF8, "libgroupwise/0.1 (linux, 2.6.5-7.97-smp)" ) );
+ lst.append( new Field::SingleField( NM_A_UD_BUILD, 0, NMFIELD_TYPE_UDWORD, 2 ) );
+ lst.append( new Field::SingleField( NM_A_IP_ADDRESS, 0, NMFIELD_TYPE_UTF8, "10.10.11.103" ) );
+ firstRequest->setFields( lst );
+ myTestObject->write( firstRequest );
+ qDebug( "done");
+}
+
+void ClientStreamTest::slotTLSHandshaken()
+{
+ qDebug( "TLS handshake complete" );
+ int validityResult = myTLS->certificateValidityResult ();
+
+ if( validityResult == QCA::TLS::Valid )
+ {
+ qDebug( "Certificate is valid, continuing.");
+ // valid certificate, continue
+ myTLSHandler->continueAfterHandshake ();
+ }
+ else
+ {
+ qDebug( "Certificate is not valid, continuing" );
+ // certificate is not valid, query the user
+ /* if(handleTLSWarning (validityResult, server (), myself()->contactId ()) == KMessageBox::Continue)
+ {*/
+ myTLSHandler->continueAfterHandshake ();
+ /* }
+ else
+ {
+ disconnect ( Kopete::Account::Manual );
+ }*/
+ }
+
+}
+int main(int argc, char ** argv)
+{
+ ClientStreamTest a( argc, argv );
+ a.exec();
+ return 0;
+}
+
+#include "clientstream_test.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h
new file mode 100644
index 00000000..2c77f4e1
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/clientstream_test.h
@@ -0,0 +1,57 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef clientstream_test_h
+#define clientstream_test_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "gwclientstream.h"
+#include "gwconnector.h"
+#include <qca.h>
+#include "qcatlshandler.h"
+#include "requestfactory.h"
+#include "request.h"
+#include "usertransfer.h"
+
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class ClientStreamTest : public QApplication
+{
+Q_OBJECT
+public:
+ ClientStreamTest(int argc, char ** argv);
+
+ ~ClientStreamTest();
+
+public slots:
+ void slotDoTest();
+
+ void slotConnected();
+
+ void slotWarning(int warning);
+
+ void slotsend(int layer);
+ void slotTLSHandshaken();
+
+private:
+ KNetworkConnector *myConnector;
+ QCA::TLS *myTLS;
+ QCATLSHandler *myTLSHandler;
+ ClientStream *myTestObject;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp
new file mode 100644
index 00000000..d1de6084
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/coreprotocol_test.cpp
@@ -0,0 +1,30 @@
+//
+// C++ Implementation: coreprotocol_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "requestfactory.h"
+#include "request.h"
+#include "usertransfer.h"
+
+#include "coreprotocol.h"
+
+int main()
+{
+ CoreProtocol testObject;
+ RequestFactory testFactory;
+ QCString command("login");
+ Request * firstRequest = testFactory.request( command );
+ Field::FieldList lst;
+ lst.append( new Field::SingleField( NM_A_SZ_USERID, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_UTF8, "blah@fasel.org" ) );
+ firstRequest->setFields( lst );
+ testObject.outgoingTransfer( firstRequest );
+ return 0;
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp b/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp
new file mode 100644
index 00000000..eec3f1f2
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tests/field_test.cpp
@@ -0,0 +1,154 @@
+#include "gwfield.h"
+#include <stdio.h>
+
+static Field::FieldList fl;
+
+void buildList();
+void buildFakeContactList();
+void extractFields( Field::FieldList );
+
+int main()
+{
+ buildFakeContactList();
+ // look for a field in the list
+/* if ( fl.find( NM_A_FA_MESSAGE ) != fl.end() )
+ printf( "Found a field, where there was supposed to be one :)\n" );
+ else
+ printf( "Didn't find a field, where there was supposed to be one :(\n" );
+ Field::FieldListIterator it;
+ if ( (it = fl.find( NM_A_SZ_OBJECT_ID ) ) != fl.end() )
+ printf( "Found a field, where there was NOT supposed to be one :(\n" );
+ else
+ printf( "Didn't find a field, where there wasn't supposed to be one :)\n" );*/
+ //printf( "%i\n", static_cast<Field::MultiField*>(*it) );
+ // dump the list
+ fl.dump( true );
+
+ printf( "\nNow testing find routines.\n");
+ // find the field containing the contact list
+ Field::MultiField * clf = dynamic_cast< Field::MultiField * >( *(fl.find( NM_A_FA_CONTACT_LIST ) ) );
+ if ( clf )
+ {
+ Field::FieldList cl = clf->fields();
+ // look for a folder in the list
+ Field::FieldListIterator it = cl.find( NM_A_FA_FOLDER );
+ if ( it != cl.end() )
+ printf( "Found the first folder :)\n");
+ else
+ printf( "Didn't find the first folder, where did it go? :(\n");
+
+ printf( "Looking for a second folder :)\n");
+ it = cl.find( ++it, NM_A_FA_FOLDER );
+ if ( it == cl.end() )
+ printf( "Didn't find a second folder :)\n" );
+ else
+ printf( "Found a second folder, now did that get there? :(\n");
+ }
+ else
+ printf( "Didn't find the contact list, where did it go? :(\n");
+
+ //extractFields( fl );
+ return 0;
+}
+// test Field subclasses by creating various FieldLists and recovering the data
+
+void buildList()
+{
+ // STRUCTURE
+ // fl - top list
+ // sf - faust quote
+ // mf - Multifield - participants, containing
+ // nl - nested list
+ // sf - contactlist (empty field array)
+ // sf - message body
+
+ Field::SingleField* sf = new Field::SingleField( NM_A_FA_MESSAGE, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "Da steh ich nun, ich armer Tor! Und bin so klug als wie zuvor..." ) );
+ fl.append( sf );
+ sf = new Field::SingleField( NM_A_SZ_TRANSACTION_ID, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "maeuschen" ) );
+ fl.append( sf );
+ // nested list
+ Field::FieldList nl;
+ sf = new Field::SingleField( NM_A_SZ_STATUS, 0, NMFIELD_TYPE_UDWORD, 123 );
+ nl.append( sf );
+ sf = new Field::SingleField( NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_TYPE_UTF8, QString::fromLatin1( "bla bla" ) );
+ nl.append( sf );
+ Field::MultiField* mf = new Field::MultiField( NM_A_FA_PARTICIPANTS, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY );
+ mf->setFields( nl );
+ fl.append( mf );
+
+/* Field::SingleField * ext = sf;
+ printf( "tag: %s flags: %i type: %i value: %s\n", ext->tag().data(), ext->flags(), ext->type(), ext->value().toString().ascii() );*/
+}
+
+void buildFakeContactList()
+{
+ using namespace Field;
+
+ FieldList contactlist;
+ // add a few contacts
+ {
+ const char* names[] = { "apple", "banana", "cherry", "damson", "elderberry", "framboise" };
+ for ( int i = 0; i < 6; i ++ )
+ {
+ FieldList contact;
+ Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( i ) );
+ contact.append( sf );
+ sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, names[i] );
+ contact.append( sf );
+ MultiField* mf = new MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contact );
+ contactlist.append( mf );
+ }
+ }
+ // add a folder
+ {
+ FieldList folder;
+ Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( 1 ) );
+ folder.append( sf );
+ sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, "buddies" );
+ folder.append( sf );
+ MultiField* mf = new MultiField( NM_A_FA_FOLDER, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, folder );
+ contactlist.append( mf );
+ }
+ // add some more contacts
+ {
+ const char* names[] = { "aardvark", "boar", "cat" };
+ for ( int i = 0; i < 3; i ++ )
+ {
+ FieldList contact;
+ Field::SingleField* sf = new Field::SingleField( NM_A_SZ_OBJECT_ID, 0, NMFIELD_TYPE_UTF8, QString::number( i ) );
+ contact.append( sf );
+ sf = new Field::SingleField( NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_TYPE_UTF8, names[i] );
+ contact.append( sf );
+ MultiField* mf = new MultiField( NM_A_FA_CONTACT, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contact );
+ contactlist.append( mf );
+ }
+ }
+
+
+ MultiField * cl = new MultiField( NM_A_FA_CONTACT_LIST, NMFIELD_METHOD_VALID, 0, NMFIELD_TYPE_ARRAY, contactlist );
+ fl.append( cl );
+}
+
+void extractFields( Field::FieldList l )
+{
+ Field::FieldListIterator it;
+ printf ("iterating over %i fields\n", l.count() );
+ for ( it = l.begin(); it != l.end() ; ++it )
+ {
+ printf ("field\n");
+ Field::SingleField * ext = dynamic_cast<Field::SingleField *>( *it );
+ if ( ext )
+ printf( "tag: %s flags: %i type: %i value: %s\n", ext->tag().data(), ext->flags(), ext->type(), ext->value().toString().ascii() );
+ else
+ {
+ Field::MultiField* mf = dynamic_cast<Field::MultiField *>( *it );
+ if ( mf )
+ {
+ printf( "found a multi value field\n" );
+ extractFields( mf->fields() );
+ }
+ }
+ }
+
+ printf ("done\n");
+}
diff --git a/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp b/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp
new file mode 100644
index 00000000..290dba36
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tlshandler.cpp
@@ -0,0 +1,31 @@
+/*
+ tlshandler.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "tlshandler.h"
+
+TLSHandler::TLSHandler(QObject *parent)
+:QObject(parent)
+{
+}
+
+TLSHandler::~TLSHandler()
+{
+}
+
+#include "tlshandler.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/tlshandler.h b/kopete/protocols/groupwise/libgroupwise/tlshandler.h
new file mode 100644
index 00000000..61c8fe7d
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/tlshandler.h
@@ -0,0 +1,51 @@
+/*
+ tlshandler.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GWTLSHANDLER_H
+#define GWTLSHANDLER_H
+
+#include <qobject.h>
+//#include<qstring.h>
+//#include<qhostaddress.h>
+//#include<qstring.h>
+//#include<qcstring.h>
+//#include<qxml.h>
+//#include<qdom.h>
+
+class TLSHandler : public QObject
+{
+ Q_OBJECT
+public:
+ TLSHandler(QObject *parent=0);
+ virtual ~TLSHandler();
+
+ virtual void reset()=0;
+ virtual void startClient(const QString &host)=0;
+ virtual void write(const QByteArray &a)=0;
+ virtual void writeIncoming(const QByteArray &a)=0;
+
+signals:
+ void success();
+ void fail();
+ void closed();
+ void readyRead(const QByteArray &a);
+ void readyReadOutgoing(const QByteArray &a, int plainBytes);
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/transfer.cpp b/kopete/protocols/groupwise/libgroupwise/transfer.cpp
new file mode 100644
index 00000000..366deed0
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/transfer.cpp
@@ -0,0 +1,30 @@
+/*
+ transfer.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qapplication.h>
+
+#include "transfer.h"
+
+Transfer::Transfer()
+{
+}
+
+
+Transfer::~Transfer()
+{
+}
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/transfer.h b/kopete/protocols/groupwise/libgroupwise/transfer.h
new file mode 100644
index 00000000..b46f81ea
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/transfer.h
@@ -0,0 +1,34 @@
+/*
+ transfer.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSFER_H
+#define TRANSFER_H
+
+/**
+@author Kopete Developers
+*/
+class Transfer{
+public:
+ enum TransferType { EventTransfer, RequestTransfer, ResponseTransfer };
+ Transfer();
+ virtual ~Transfer();
+
+ virtual TransferType type() = 0;
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/transferbase.cpp b/kopete/protocols/groupwise/libgroupwise/transferbase.cpp
new file mode 100644
index 00000000..6864ea48
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/transferbase.cpp
@@ -0,0 +1,29 @@
+/*
+ transferbase.cpp - Base class of all I/O transfers
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "transferbase.h"
+
+TransferBase::TransferBase()
+{
+}
+
+
+TransferBase::~TransferBase()
+{
+}
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/transferbase.h b/kopete/protocols/groupwise/libgroupwise/transferbase.h
new file mode 100644
index 00000000..de7a688d
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/transferbase.h
@@ -0,0 +1,32 @@
+/*
+ transferbase.h - Base class of all I/O transfers
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSFERBASE_H
+#define TRANSFERBASE_H
+
+/**
+@author Kopete Developers
+*/
+class TransferBase{
+public:
+ TransferBase();
+
+ ~TransferBase();
+
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp
new file mode 100644
index 00000000..2527968e
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.cpp
@@ -0,0 +1,129 @@
+/*
+ userdetailsmanager.cpp - Storage of all user details seen during this session
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+#include "tasks/getdetailstask.h"
+
+#include "userdetailsmanager.h"
+
+UserDetailsManager::UserDetailsManager( Client * parent, const char *name)
+ : QObject(parent, name), m_client( parent )
+{
+}
+
+UserDetailsManager::~UserDetailsManager()
+{
+}
+
+void UserDetailsManager::dump( const QStringList & list )
+{
+ for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it )
+ {
+ m_client->debug( QString( " - %1" ).arg (*it) );
+ }
+}
+
+bool UserDetailsManager::known( const QString & dn )
+{
+ if ( dn == m_client->userDN() )
+ return true;
+ // TODO: replace with m_detailsMap.contains( dn );
+ QStringList::Iterator found = m_detailsMap.keys().find( dn );
+ // we always know the local user's details, so don't look them up
+ return ( found !=m_detailsMap.keys().end() );
+}
+
+ContactDetails UserDetailsManager::details( const QString & dn )
+{
+ return m_detailsMap[ dn ];
+}
+
+QStringList UserDetailsManager::knownDNs()
+{
+ return m_detailsMap.keys();
+}
+
+void UserDetailsManager::addDetails( const ContactDetails & details )
+{
+ //qDebug( "UserDetailsManager::addContact, got %s, we now know: ", details.dn.ascii() );
+ m_detailsMap.insert( details.dn, details );
+/* QStringList keys = m_detailsMap.keys();
+ dump( keys );
+ qDebug( "UserDetailsManager::addContact, pending: " );
+ dump( m_pendingDNs );*/
+}
+
+void UserDetailsManager::removeContact( const QString & dn )
+{
+ m_detailsMap.remove( dn );
+}
+
+void UserDetailsManager::requestDetails( const QStringList & dnList, bool onlyUnknown )
+{
+ // build a list of DNs that are not already subject to a pending request
+ QStringList requestList;
+ QValueListConstIterator<QString> end = dnList.end();
+ for ( QValueListConstIterator<QString> it = dnList.begin(); it != end; ++it )
+ {
+ // don't request our own details
+ if ( *it == m_client->userDN() )
+ break;
+ // don't request details we already have unless the caller specified this
+ if ( onlyUnknown && known( *it ) )
+ break;
+ QStringList::Iterator found = m_pendingDNs.find( *it );
+ if ( found == m_pendingDNs.end() )
+ {
+ m_client->debug( QString( "UserDetailsManager::requestDetails - including %1" ).arg( (*it) ) );
+ requestList.append( *it );
+ m_pendingDNs.append( *it );
+ }
+ }
+ if ( !requestList.empty() )
+ {
+ GetDetailsTask * gdt = new GetDetailsTask( m_client->rootTask() );
+ gdt->userDNs( requestList );
+ connect( gdt, SIGNAL( gotContactUserDetails( const GroupWise::ContactDetails & ) ),
+ SLOT( slotReceiveContactDetails( const GroupWise::ContactDetails & ) ) );
+ // TODO: connect to gdt's finished() signal, check for failures, expand gdt to maintain a list of not found DNs?
+ gdt->go( true );
+ }
+ else
+ {
+ m_client->debug( "UserDetailsManager::requestDetails - all requested contacts are already available or pending" );
+ }
+}
+
+void UserDetailsManager::requestDetails( const QString & dn, bool onlyUnknown )
+{
+ m_client->debug( QString( "UserDetailsManager::requestDetails for %1" ).arg( dn ) );
+ QStringList list;
+ list.append( dn );
+ requestDetails( list, onlyUnknown );
+}
+
+void UserDetailsManager::slotReceiveContactDetails( const GroupWise::ContactDetails & details )
+{
+ m_client->debug( "UserDetailsManager::slotReceiveContactDetails()" );
+ m_pendingDNs.remove( details.dn );
+ /*client()->userDetailsManager()->*/
+ addDetails( details );
+ //emit temporaryContact( details );
+ emit gotContactDetails( details );
+}
+
+#include "userdetailsmanager.moc"
diff --git a/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h
new file mode 100644
index 00000000..4e9b6022
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/userdetailsmanager.h
@@ -0,0 +1,84 @@
+/*
+ userdetailsmanager.h - Storage of all user details seen during this session
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef USERDETAILSMANAGER_H
+#define USERDETAILSMANAGER_H
+
+#include <qmap.h>
+#include <qobject.h>
+#include <qstringlist.h>
+
+#include "gwerror.h"
+class Client;
+
+/**
+Several client event handling processes require that a contact's details are available before exposing the event to the user. This class is responsible for issuing details requests, tracking which users the client already has received details for, and signalling when details have been received. The manager allows multiple interleaved get details requests to be replaced by a single request.
+
+@author SUSE AG
+*/
+
+class UserDetailsManager : public QObject
+{
+Q_OBJECT
+public:
+ UserDetailsManager( Client * parent, const char *name = 0);
+ ~UserDetailsManager();
+ /**
+ * List of DNs that we have already received details for
+ */
+ QStringList knownDNs();
+ /**
+ * Check if we have details for a single DN
+ */
+ bool known( const QString &dn );
+ /**
+ * Get details for a given DN
+ */
+ ContactDetails details( const QString &dn );
+ /**
+ * Add a ContactDetails object to our cache.
+ * This SHOULD be called when receiving details in contactlist receive and manipulation, to prevent unnecessary additional requests.
+ */
+ void addDetails( const GroupWise::ContactDetails & details );
+ /**
+ * Remove a contact from the list of known DNs. This MUST be performed when a client removes a DN from its local contact list,
+ * otherwise new events from this DN will not receive user details.
+ */
+ void removeContact( const QString & dn );
+ /**
+ * Explicitly request details for a set of contacts from the server.
+ * Will signal @ref gotContactUserDetails for each one when they are available.
+ */
+ void requestDetails( const QStringList & dnList, bool onlyUnknown = true );
+ /**
+ * Explicitly request a contact's details from the server. Will signal @ref gotContactUserDetails when they are available.
+ */
+ void requestDetails( const QString & dn, bool onlyUnknown = true );
+
+signals:
+ void gotContactDetails( const GroupWise::ContactDetails & );
+protected slots:
+ void slotReceiveContactDetails( const GroupWise::ContactDetails & );
+protected:
+ void dump( const QStringList & list );
+private:
+ QStringList m_pendingDNs; // a list of DNs that have pending requests
+ Client * m_client;
+ QMap< QString, GroupWise::ContactDetails > m_detailsMap;
+};
+
+#endif
diff --git a/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp b/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp
new file mode 100644
index 00000000..85f0f395
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/usertransfer.cpp
@@ -0,0 +1,46 @@
+/*
+ usertransfer.cpp - Ancestor of In- or outgoing Transfers (Requests and Response)
+ initated by the user.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "usertransfer.h"
+
+UserTransfer::UserTransfer( int transactionId )
+{
+ m_transactionId = transactionId;
+}
+
+UserTransfer::~UserTransfer()
+{
+ m_fields.purge();
+}
+
+void UserTransfer::setFields( Field::FieldList fields )
+{
+ m_fields = fields;
+}
+
+int UserTransfer::transactionId()
+{
+ return m_transactionId;
+}
+
+Field::FieldList UserTransfer::fields()
+{
+ return m_fields;
+}
+
+
diff --git a/kopete/protocols/groupwise/libgroupwise/usertransfer.h b/kopete/protocols/groupwise/libgroupwise/usertransfer.h
new file mode 100644
index 00000000..d4d30dbc
--- /dev/null
+++ b/kopete/protocols/groupwise/libgroupwise/usertransfer.h
@@ -0,0 +1,45 @@
+/*
+ usertransfer.h - Ancestor of In- or outgoing Transfers (Requests and Response)
+ initated by the user.
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef USERTRANSFER_H
+#define USERTRANSFER_H
+
+#include "gwfield.h"
+
+#include "transfer.h"
+
+/**
+ * Represents transfers of data in response to a user action, either outgoing Requests, or incoming Responses
+ * @author Kopete Developers
+ */
+class UserTransfer : public Transfer
+{
+public:
+ UserTransfer( int transactionId );
+ virtual ~UserTransfer();
+ int transactionId();
+ Field::FieldList fields();
+ void setFields( Field::FieldList fields );
+
+private:
+ int m_transactionId;
+ Field::FieldList m_fields;
+
+};
+
+#endif
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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <will@stevello.free-online.co.uk>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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 <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * 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>
diff --git a/kopete/protocols/irc/Makefile.am b/kopete/protocols/irc/Makefile.am
new file mode 100644
index 00000000..7bd37813
--- /dev/null
+++ b/kopete/protocols/irc/Makefile.am
@@ -0,0 +1,40 @@
+METASOURCES = AUTO
+
+SUBDIRS = icons libkirc ui
+AM_CPPFLAGS = -I$(srcdir)/ui $(KOPETE_INCLUDES) \
+ -I./ui \
+ -I$(srcdir)/libkirc \
+ $(all_includes)
+kde_module_LTLIBRARIES = kopete_irc.la
+
+kopete_irc_la_SOURCES = \
+ ircaccount.cpp \
+ ircaddcontactpage.cpp \
+ ircchannelcontact.cpp \
+ irccontact.cpp \
+ ircguiclient.cpp \
+ ircprotocol.cpp \
+ ircservercontact.cpp \
+ ircsignalhandler.cpp \
+ irctransferhandler.cpp \
+ ircusercontact.cpp \
+ irccontactmanager.cpp \
+ kcodecaction.cpp \
+ ksparser.cpp
+
+kopete_irc_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+kopete_irc_la_LIBADD = ../../libkopete/libkopete.la \
+ ./ui/libkopeteircui.la \
+ ./libkirc/libkirc.la \
+ $(LIB_KIO)
+
+service_DATA = kopete_irc.desktop irc.protocol
+servicedir = $(kde_servicesdir)
+
+xmldata_DATA = ircnetworks.xml
+xmldatadir = $(kde_datadir)/kopete
+
+EXTRA_DIST = $(xmldata_DATA)
+
+mydatadir = $(kde_datadir)/kopete
+mydata_DATA = ircchatui.rc
diff --git a/kopete/protocols/irc/icons/Makefile.am b/kopete/protocols/irc/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/protocols/irc/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_away.png b/kopete/protocols/irc/icons/cr16-action-irc_away.png
new file mode 100644
index 00000000..d7572e1f
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_away.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_channel.png b/kopete/protocols/irc/icons/cr16-action-irc_channel.png
new file mode 100644
index 00000000..0353e7dc
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_channel.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng b/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng
new file mode 100644
index 00000000..486102e4
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_connecting.mng
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_normal.png b/kopete/protocols/irc/icons/cr16-action-irc_normal.png
new file mode 100644
index 00000000..b7a3cc24
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_normal.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_online.png b/kopete/protocols/irc/icons/cr16-action-irc_online.png
new file mode 100644
index 00000000..c5678d1e
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_online.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_op.png b/kopete/protocols/irc/icons/cr16-action-irc_op.png
new file mode 100644
index 00000000..6b14cf14
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_op.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_server.png b/kopete/protocols/irc/icons/cr16-action-irc_server.png
new file mode 100644
index 00000000..b7a3cc24
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_server.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-action-irc_voice.png b/kopete/protocols/irc/icons/cr16-action-irc_voice.png
new file mode 100644
index 00000000..6a9b5aaf
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-action-irc_voice.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr16-app-irc_protocol.png b/kopete/protocols/irc/icons/cr16-app-irc_protocol.png
new file mode 100644
index 00000000..c5678d1e
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr16-app-irc_protocol.png
Binary files differ
diff --git a/kopete/protocols/irc/icons/cr32-app-irc_protocol.png b/kopete/protocols/irc/icons/cr32-app-irc_protocol.png
new file mode 100644
index 00000000..f2747b49
--- /dev/null
+++ b/kopete/protocols/irc/icons/cr32-app-irc_protocol.png
Binary files differ
diff --git a/kopete/protocols/irc/irc.protocol b/kopete/protocols/irc/irc.protocol
new file mode 100644
index 00000000..f57982bb
--- /dev/null
+++ b/kopete/protocols/irc/irc.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=kopete %u
+protocol=irc
+input=none
+output=none
+helper=true
+listing=
+reading=false
+writing=false
+makedir=false
+deleting=false
+Icon=irc_normal
diff --git a/kopete/protocols/irc/ircaccount.cpp b/kopete/protocols/irc/ircaccount.cpp
new file mode 100644
index 00000000..1a1bf75f
--- /dev/null
+++ b/kopete/protocols/irc/ircaccount.cpp
@@ -0,0 +1,904 @@
+/*
+ ircaccount.cpp - IRC Account
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003-2004 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2003-2005 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircaccount.h"
+#include "irccontact.h"
+#include "irccontactmanager.h"
+#include "ircprotocol.h"
+
+#include "ircservercontact.h"
+#include "ircchannelcontact.h"
+#include "ircusercontact.h"
+
+#include "channellistdialog.h"
+
+#include "kircengine.h"
+
+#include "kopeteaccountmanager.h"
+#include "kopeteaway.h"
+#include "kopeteawayaction.h"
+#include "kopetecommandhandler.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+#include "kopeteview.h"
+#include "kopetepassword.h"
+
+#include <kaction.h>
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kcompletionbox.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kinputdialog.h>
+#include <klineedit.h>
+#include <klineeditdlg.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kpopupmenu.h>
+
+#include <qlayout.h>
+#include <qtimer.h>
+
+const QString IRCAccount::CONFIG_CODECMIB = QString::fromLatin1("Codec");
+const QString IRCAccount::CONFIG_NETWORKNAME = QString::fromLatin1("NetworkName");
+const QString IRCAccount::CONFIG_NICKNAME = QString::fromLatin1("NickName");
+const QString IRCAccount::CONFIG_USERNAME = QString::fromLatin1("UserName");
+const QString IRCAccount::CONFIG_REALNAME = QString::fromLatin1("RealName");
+
+IRCAccount::IRCAccount(IRCProtocol *protocol, const QString &accountId, const QString &autoChan, const QString& netName, const QString &nickName)
+ : Kopete::PasswordedAccount(protocol, accountId, 0, true), autoConnect( autoChan ), commandSource(0)
+{
+ m_manager = 0L;
+ m_channelList = 0L;
+ m_network = 0L;
+
+ triedAltNick = false;
+
+ m_contactManager = 0;
+ m_engine = new KIRC::Engine(this);
+
+ QMap< QString, QString> replies = customCtcpReplies();
+ for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it )
+ m_engine->addCustomCtcp( it.key(), it.data() );
+
+ QString version=i18n("Kopete IRC Plugin %1 [http://kopete.kde.org]").arg(kapp->aboutData()->version());
+ m_engine->setVersionString( version );
+
+ QObject::connect(m_engine, SIGNAL(successfullyChangedNick(const QString &, const QString &)),
+ this, SLOT(successfullyChangedNick(const QString &, const QString &)));
+
+ QObject::connect(m_engine, SIGNAL(incomingFailedServerPassword()),
+ this, SLOT(slotFailedServerPassword()));
+
+ QObject::connect(m_engine, SIGNAL(incomingNickInUse(const QString &)),
+ this, SLOT(slotNickInUseAlert( const QString &)) );
+
+ QObject::connect(m_engine, SIGNAL(incomingFailedNickOnLogin(const QString &)),
+ this, SLOT(slotNickInUse( const QString &)) );
+
+ QObject::connect(m_engine, SIGNAL(incomingJoinedChannel(const QString &, const QString &)),
+ this, SLOT(slotJoinedUnknownChannel(const QString &, const QString &)));
+
+ QObject::connect(m_engine, SIGNAL(incomingCtcpReply(const QString &, const QString &, const QString &)),
+ this, SLOT( slotNewCtcpReply(const QString&, const QString &, const QString &)));
+
+ QObject::connect(m_engine, SIGNAL(statusChanged(KIRC::Engine::Status)),
+ this, SLOT(engineStatusChanged(KIRC::Engine::Status)));
+
+ QObject::connect(m_engine, SIGNAL(incomingServerLoadTooHigh()),
+ this, SLOT(slotServerBusy()));
+
+ QObject::connect(m_engine, SIGNAL(incomingNoSuchNickname(const QString &)),
+ this, SLOT(slotNoSuchNickname(const QString &)));
+
+ mAwayAction = new Kopete::AwayAction ( i18n("Set Away"),
+ m_protocol->m_UserStatusAway.iconFor( this ), 0, this,
+ SLOT(slotGoAway( const QString & )), this );
+
+ currentHost = 0;
+
+ KConfigGroup *config = configGroup();
+
+ QString networkName = netName;
+ if (networkName.isNull())
+ networkName = config->readEntry(CONFIG_NETWORKNAME);
+
+ if (!nickName.isNull())
+ setNickName(nickName);
+ else
+ mNickName = config->readEntry(CONFIG_NICKNAME);
+
+ QString codecMib = config->readEntry(CONFIG_CODECMIB);
+ // int codecMib = config->readNumEntry(CONFIG_CODECMIB, UTF-8);
+
+ m_serverNotices = (MessageDestination)config->readNumEntry( "ServerNotices", ServerWindow );
+ m_serverMessages = (MessageDestination)config->readNumEntry( "ServerMessages", ServerWindow );
+ m_informationReplies = (MessageDestination)config->readNumEntry( "InformationReplies", ActiveWindow );
+ m_errorMessages = (MessageDestination)config->readNumEntry( "ErrorMessages", ActiveWindow );
+ autoShowServerWindow = config->readBoolEntry( "AutoShowServerWindow", false );
+
+ if( !codecMib.isEmpty() )
+ {
+ mCodec = QTextCodec::codecForMib( codecMib.toInt() );
+ m_engine->setDefaultCodec( mCodec );
+ }
+ else
+ mCodec = 0;
+
+ QString m_accountId = this->accountId();
+ if( networkName.isEmpty() && QRegExp( "[^#+&\\s]+@[\\w-\\.]+:\\d+" ).exactMatch( m_accountId ) )
+ {
+ kdDebug(14120) << "Creating account from " << m_accountId << endl;
+
+ mNickName = m_accountId.section('@',0,0);
+ QString serverInfo = m_accountId.section('@',1);
+ QString hostName = serverInfo.section(':',0,0);
+
+ for( QDictIterator<IRCNetwork> it( m_protocol->networks() ); it.current(); ++it )
+ {
+ IRCNetwork *net = it.current();
+ for( QValueList<IRCHost*>::iterator it2 = net->hosts.begin(); it2 != net->hosts.end(); ++it2 )
+ {
+ if( (*it2)->host == hostName )
+ {
+ setNetwork(net->name);
+ break;
+ }
+ }
+
+ if( !networkName.isEmpty() )
+ break;
+ }
+
+ if( networkName.isEmpty() )
+ {
+ /* Could not find this host. Add it to the networks structure */
+
+ m_network = new IRCNetwork;
+ m_network->name = i18n("Temporary Network - %1").arg( hostName );
+ m_network->description = i18n("Network imported from previous version of Kopete, or an IRC URI");
+
+ IRCHost *host = new IRCHost;
+ host->host = hostName;
+ host->port = serverInfo.section(':',1).toInt();
+ if( !password().cachedValue().isEmpty() )
+ host->password = password().cachedValue();
+ host->ssl = false;
+
+ m_network->hosts.append( host );
+ m_protocol->addNetwork( m_network );
+
+ config->writeEntry(CONFIG_NETWORKNAME, m_network->name);
+ config->writeEntry(CONFIG_NICKNAME, mNickName);
+ }
+ }
+ else if( !networkName.isEmpty() )
+ {
+ setNetwork(networkName);
+ }
+ else
+ {
+ kdError() << "No network name defined, and could not import network information from ID" << endl;
+ }
+
+ m_engine->setUserName(userName());
+ m_engine->setRealName(realName());
+
+ m_contactManager = new IRCContactManager(mNickName, this);
+ setMyself( m_contactManager->mySelf() );
+ setAccountLabel( QString::fromLatin1("%1@%2").arg(mNickName,networkName) );
+ m_myServer = m_contactManager->myServer();
+
+ m_joinChannelAction = new KAction ( i18n("Join Channel..."), QString::null, 0, this,
+ SLOT(slotJoinChannel()), this);
+ m_searchChannelAction = new KAction ( i18n("Search Channels..."), QString::null, 0, this,
+ SLOT(slotSearchChannels()), this);
+}
+
+IRCAccount::~IRCAccount()
+{
+ if (m_engine->isConnected())
+ m_engine->quit(i18n("Plugin Unloaded"), true);
+}
+
+void IRCAccount::slotNickInUse( const QString &nick )
+{
+ QString altNickName = altNick();
+ if( triedAltNick || altNickName.isEmpty() )
+ {
+ QString newNick = KInputDialog::getText(
+ i18n("IRC Plugin"),
+ i18n("The nickname %1 is already in use. Please enter an alternate nickname:").arg(nick),
+ nick);
+
+ if (newNick.isNull())
+ disconnect();
+ else
+ m_engine->nick(newNick);
+ }
+ else
+ {
+ triedAltNick = true;
+ m_engine->nick(altNickName);
+ }
+}
+
+void IRCAccount::slotNickInUseAlert( const QString &nick )
+{
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("The nickname %1 is already in use").arg(nick), i18n("IRC Plugin"));
+}
+
+void IRCAccount::setAltNick( const QString &altNick )
+{
+ configGroup()->writeEntry(QString::fromLatin1( "altNick" ), altNick);
+}
+
+const QString IRCAccount::altNick() const
+{
+ return configGroup()->readEntry(QString::fromLatin1("altNick"));
+}
+
+void IRCAccount::setAutoShowServerWindow( bool show )
+{
+ autoShowServerWindow = show;
+ configGroup()->writeEntry(QString::fromLatin1( "AutoShowServerWindow" ), autoShowServerWindow);
+}
+
+const QString IRCAccount::networkName() const
+{
+ if( m_network )
+ return m_network->name;
+ else
+ return i18n("Unknown");
+}
+
+void IRCAccount::setUserName( const QString &userName )
+{
+ m_engine->setUserName(userName);
+ configGroup()->writeEntry(CONFIG_USERNAME, userName);
+}
+
+const QString IRCAccount::userName() const
+{
+ return configGroup()->readEntry(CONFIG_USERNAME);
+}
+
+void IRCAccount::setRealName( const QString &userName )
+{
+ m_engine->setRealName(userName);
+ configGroup()->writeEntry(CONFIG_REALNAME, userName);
+}
+
+const QString IRCAccount::realName() const
+{
+ return configGroup()->readEntry(CONFIG_REALNAME);
+}
+
+void IRCAccount::setNetwork( const QString &network )
+{
+ IRCNetwork *net = m_protocol->networks()[ network ];
+ if( net )
+ {
+ m_network = net;
+ configGroup()->writeEntry(CONFIG_NETWORKNAME, network);
+ setAccountLabel(network);
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox(
+ Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("<qt>The network associated with this account, <b>%1</b>, no longer exists. Please"
+ " ensure that the account has a valid network. The account will not be enabled until you do so.</qt>").arg(network),
+ i18n("Problem Loading %1").arg( accountId() ), 0 );
+ }
+}
+
+void IRCAccount::setNickName( const QString &nick )
+{
+ mNickName = nick;
+ configGroup()->writeEntry(CONFIG_NICKNAME, mNickName);
+
+ if( mySelf() )
+ mySelf()->setNickName( mNickName );
+}
+
+// FIXME: Possible null pointer usage here
+void IRCAccount::setCodec( QTextCodec *codec )
+{
+ mCodec = codec;
+ configGroup()->writeEntry(CONFIG_CODECMIB, codec->mibEnum());
+
+ if( mCodec )
+ m_engine->setDefaultCodec( mCodec );
+}
+
+QTextCodec *IRCAccount::codec() const
+{
+ return mCodec;
+}
+
+// FIXME: Move this to a dictionnary
+void IRCAccount::setDefaultPart( const QString &defaultPart )
+{
+ configGroup()->writeEntry( QString::fromLatin1( "defaultPart" ), defaultPart );
+}
+
+// FIXME: Move this to a dictionnary
+void IRCAccount::setDefaultQuit( const QString &defaultQuit )
+{
+ configGroup()->writeEntry( QString::fromLatin1( "defaultQuit" ), defaultQuit );
+}
+
+// FIXME: Move this to a dictionnary
+const QString IRCAccount::defaultPart() const
+{
+ QString partMsg = configGroup()->readEntry(QString::fromLatin1("defaultPart"));
+ if( partMsg.isEmpty() )
+ return QString::fromLatin1("Kopete %1 : http://kopete.kde.org").arg( kapp->aboutData()->version() );
+ return partMsg;
+}
+
+const QString IRCAccount::defaultQuit() const
+{
+ QString quitMsg = configGroup()->readEntry(QString::fromLatin1("defaultQuit"));
+ if( quitMsg.isEmpty() )
+ return QString::fromLatin1("Kopete %1 : http://kopete.kde.org").arg(kapp->aboutData()->version());
+ return quitMsg;
+}
+
+void IRCAccount::setCustomCtcpReplies( const QMap< QString, QString > &replies ) const
+{
+ QStringList val;
+ for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it )
+ {
+ m_engine->addCustomCtcp( it.key(), it.data() );
+ val.append( QString::fromLatin1("%1=%2").arg( it.key() ).arg( it.data() ) );
+ }
+
+ configGroup()->writeEntry( "CustomCtcp", val );
+}
+
+const QMap< QString, QString > IRCAccount::customCtcpReplies() const
+{
+ QMap< QString, QString > replies;
+ QStringList replyList;
+
+ replyList = configGroup()->readListEntry( "CustomCtcp" );
+
+ for( QStringList::Iterator it = replyList.begin(); it != replyList.end(); ++it )
+ replies[ (*it).section('=', 0, 0 ) ] = (*it).section('=', 1 );
+
+ return replies;
+}
+
+void IRCAccount::setConnectCommands( const QStringList &commands ) const
+{
+ configGroup()->writeEntry( "ConnectCommands", commands );
+}
+
+const QStringList IRCAccount::connectCommands() const
+{
+ return configGroup()->readListEntry( "ConnectCommands" );
+}
+
+void IRCAccount::setMessageDestinations( int serverNotices, int serverMessages,
+ int informationReplies, int errorMessages )
+{
+ KConfigGroup *config = configGroup();
+ config->writeEntry( "ServerNotices", serverNotices );
+ config->writeEntry( "ServerMessages", serverMessages );
+ config->writeEntry( "InformationReplies", informationReplies );
+ config->writeEntry( "ErrorMessages", errorMessages );
+
+ m_serverNotices = (MessageDestination)serverNotices;
+ m_serverMessages = (MessageDestination)serverMessages;
+ m_informationReplies = (MessageDestination)informationReplies;
+ m_errorMessages = (MessageDestination)errorMessages;
+}
+
+KActionMenu *IRCAccount::actionMenu()
+{
+ QString menuTitle = QString::fromLatin1( " %1 <%2> " ).arg( accountId() ).arg( myself()->onlineStatus().description() );
+
+ KActionMenu *mActionMenu = Kopete::Account::actionMenu();
+
+ m_joinChannelAction->setEnabled( isConnected() );
+ m_searchChannelAction->setEnabled( isConnected() );
+
+ mActionMenu->popupMenu()->insertSeparator();
+ mActionMenu->insert(m_joinChannelAction);
+ mActionMenu->insert(m_searchChannelAction);
+ mActionMenu->insert( new KAction ( i18n("Show Server Window"), QString::null, 0, this, SLOT(slotShowServerWindow()), mActionMenu ) );
+
+ if( m_engine->isConnected() && m_engine->useSSL() )
+ {
+ mActionMenu->insert( new KAction ( i18n("Show Security Information"), "", 0, m_engine,
+ SLOT(showInfoDialog()), mActionMenu ) );
+ }
+
+ return mActionMenu;
+}
+
+void IRCAccount::connectWithPassword(const QString &password)
+{
+ //TODO: honor the initial status
+
+ if( m_engine->isConnected() )
+ {
+ if( isAway() )
+ setAway( false );
+ }
+ else if( m_engine->isDisconnected() )
+ {
+ if( m_network )
+ {
+ QValueList<IRCHost*> &hosts = m_network->hosts;
+ if( hosts.count() == 0 )
+ {
+ KMessageBox::queuedMessageBox(
+ Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("<qt>The network associated with this account, <b>%1</b>, has no valid hosts. Please ensure that the account has a valid network.</qt>").arg(m_network->name),
+ i18n("Network is Empty"), 0 );
+ }
+ else if( currentHost == hosts.count() )
+ {
+ KMessageBox::queuedMessageBox(
+ Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("<qt>Kopete could not connect to any of the servers in the network associated with this account (<b>%1</b>). Please try again later.</qt>").arg(m_network->name),
+ i18n("Network is Unavailable"), 0 );
+
+ currentHost = 0;
+ }
+ else
+ {
+ // if prefer SSL is set, sort by SSL first
+ if (configGroup()->readBoolEntry("PreferSSL"))
+ {
+ typedef QValueList<IRCHost*> IRCHostList;
+ IRCHostList sslFirst;
+ IRCHostList::iterator it;
+ for ( it = hosts.begin(); it != hosts.end(); ++it )
+ {
+ if ( (*it)->ssl == true )
+ {
+ sslFirst.append( *it );
+ it = hosts.remove( it );
+ }
+ }
+ for ( it = hosts.begin(); it != hosts.end(); ++it )
+ sslFirst.append( *it );
+
+ hosts = sslFirst;
+ }
+
+ IRCHost *host = hosts[ currentHost++ ];
+ myServer()->appendMessage( i18n("Connecting to %1...").arg( host->host ) );
+ if( host->ssl )
+ myServer()->appendMessage( i18n("Using SSL") );
+
+ m_engine->setPassword(password);
+ m_engine->connectToServer( host->host, host->port, mNickName, host->ssl );
+ }
+ }
+ else
+ {
+ kdWarning() << "No network defined!" << endl;
+ }
+ }
+}
+
+void IRCAccount::engineStatusChanged(KIRC::Engine::Status newStatus)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ mySelf()->updateStatus();
+
+ switch (newStatus)
+ {
+ case KIRC::Engine::Idle:
+ // Do nothing.
+ break;
+ case KIRC::Engine::Connecting:
+ {
+ if( autoShowServerWindow )
+ myServer()->startChat();
+ break;
+ }
+ case KIRC::Engine::Authentifying:
+ break;
+ case KIRC::Engine::Connected:
+ {
+ //Reset the host so re-connection will start over at first server
+ currentHost = 0;
+ m_contactManager->addToNotifyList( m_engine->nickName() );
+
+ // HACK! See bug #85200 for details. Some servers still cannot accept commands
+ // after the 001 is sent, you need to wait until all the init junk is done.
+ // Unfortunatly, there is no way for us to know when it is done (it could be
+ // spewing out any number of replies), so just try delaying it
+ QTimer::singleShot( 250, this, SLOT( slotPerformOnConnectCommands() ) );
+ }
+ break;
+ case KIRC::Engine::Closing:
+ triedAltNick = false;
+// mySelf()->setOnlineStatus( m_protocol->m_UserStatusOffline );
+ m_contactManager->removeFromNotifyList( m_engine->nickName() );
+
+// if (m_contactManager && !autoConnect.isNull())
+// Kopete::AccountManager::self()->removeAccount( this );
+ break;
+ case KIRC::Engine::AuthentifyingFailed:
+ break;
+ case KIRC::Engine::Timeout:
+ //Try next server
+ connect();
+ break;
+ case KIRC::Engine::Disconnected:
+ break;
+ }
+}
+
+void IRCAccount::slotPerformOnConnectCommands()
+{
+ Kopete::ChatSession *manager = myServer()->manager(Kopete::Contact::CanCreate);
+ if (!manager)
+ return;
+
+ if (!autoConnect.isEmpty())
+ Kopete::CommandHandler::commandHandler()->processMessage( QString::fromLatin1("/join %1").arg(autoConnect), manager);
+
+ QStringList commands(connectCommands());
+ for (QStringList::Iterator it=commands.begin(); it != commands.end(); ++it)
+ Kopete::CommandHandler::commandHandler()->processMessage(*it, manager);
+}
+
+void IRCAccount::slotJoinedUnknownChannel(const QString &channel, const QString &nick)
+{
+ if ( nick.lower() == m_contactManager->mySelf()->nickName().lower() )
+ {
+ m_contactManager->findChannel(channel)->join();
+ }
+}
+
+void IRCAccount::disconnect()
+{
+ quit();
+}
+
+void IRCAccount::slotServerBusy()
+{
+ KMessageBox::queuedMessageBox(
+ Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("The IRC server is currently too busy to respond to this request."),
+ i18n("Server is Busy"), 0
+ );
+}
+
+void IRCAccount::slotSearchChannels()
+{
+ if( !m_channelList )
+ {
+ m_channelList = new ChannelListDialog( m_engine,
+ i18n("Channel List for %1").arg( m_engine->currentHost() ), this,
+ SLOT( slotJoinNamedChannel( const QString & ) ) );
+ }
+ else
+ m_channelList->clear();
+
+ m_channelList->show();
+}
+
+void IRCAccount::listChannels()
+{
+ slotSearchChannels();
+ m_channelList->search();
+}
+
+void IRCAccount::quit( const QString &quitMessage )
+{
+ kdDebug(14120) << "Quitting IRC: " << quitMessage << endl;
+
+ if( quitMessage.isNull() || quitMessage.isEmpty() )
+ m_engine->quit( defaultQuit() );
+ else
+ m_engine->quit( quitMessage );
+}
+
+void IRCAccount::setAway(bool isAway, const QString &awayMessage)
+{
+ kdDebug(14120) << k_funcinfo << isAway << " " << awayMessage << endl;
+ if(m_engine->isConnected())
+ {
+ static_cast<IRCUserContact *>( myself() )->setAway( isAway );
+ engine()->away(isAway, awayMessage);
+ }
+}
+
+/*
+ * Ask for server password, and reconnect
+ */
+void IRCAccount::slotFailedServerPassword()
+{
+ // JLN
+ password().setWrong();
+ connect();
+}
+void IRCAccount::slotGoAway( const QString &reason )
+{
+ setAway( true, reason );
+}
+
+void IRCAccount::slotShowServerWindow()
+{
+ m_myServer->startChat();
+}
+
+bool IRCAccount::isConnected()
+{
+// return ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline );
+ return m_engine->isConnected();
+}
+
+void IRCAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason )
+{
+ if ( status.status() == Kopete::OnlineStatus::Online &&
+ myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline )
+ connect();
+ else if (status.status() == Kopete::OnlineStatus::Online &&
+ myself()->onlineStatus().status() == Kopete::OnlineStatus::Away )
+ setAway( false );
+ else if ( status.status() == Kopete::OnlineStatus::Offline )
+ disconnect();
+ else if ( status.status() == Kopete::OnlineStatus::Away )
+ slotGoAway( reason );
+}
+
+
+void IRCAccount::successfullyChangedNick(const QString &oldnick, const QString &newnick)
+{
+ kdDebug(14120) << k_funcinfo << "Changing nick to " << newnick << endl;
+ mNickName = newnick;
+ mySelf()->setNickName( mNickName );
+ m_contactManager->removeFromNotifyList( oldnick );
+ m_contactManager->addToNotifyList( newnick );
+}
+
+bool IRCAccount::createContact( const QString &contactId, Kopete::MetaContact *m )
+{
+ kdDebug(14120) << k_funcinfo << contactManager() << endl;
+ IRCContact *c;
+
+ if( !m )
+ {//This should NEVER happen
+ m = new Kopete::MetaContact();
+ Kopete::ContactList::self()->addMetaContact(m);
+ }
+
+ if( contactId == mNickName )
+ {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("\"You are not allowed to add yourself to your contact list."), i18n("IRC Plugin")
+ );
+
+ return false;
+ }
+ else if (contactId.startsWith(QString::fromLatin1("#")))
+ {
+ c = static_cast<IRCContact*>(contactManager()->findChannel(contactId, m));
+ }
+ else
+ {
+ m_contactManager->addToNotifyList( contactId );
+ c = static_cast<IRCContact*>(contactManager()->findUser(contactId, m));
+ }
+
+ if( c->metaContact() != m )
+ {//This should NEVER happen
+ Kopete::MetaContact *old = c->metaContact();
+ c->setMetaContact( m );
+ Kopete::ContactPtrList children = old->contacts();
+ if (children.isEmpty())
+ Kopete::ContactList::self()->removeMetaContact( old );
+ }
+ else if( c->metaContact()->isTemporary() )
+ m->setTemporary(false);
+
+ return true;
+}
+
+void IRCAccount::slotJoinNamedChannel(const QString &chan)
+{
+ contactManager()->findChannel(chan)->startChat();
+}
+
+void IRCAccount::setCurrentCommandSource( Kopete::ChatSession *session )
+{
+ commandSource = session;
+}
+
+Kopete::ChatSession *IRCAccount::currentCommandSource()
+{
+ return commandSource;
+}
+
+void IRCAccount::slotJoinChannel()
+{
+ if (!isConnected())
+ return;
+
+ QStringList chans = configGroup()->readListEntry( "Recent Channel list" );
+ //kdDebug(14120) << "Recent channel list from config: " << chans << endl;
+
+ KLineEditDlg dlg(
+ i18n("Please enter name of the channel you want to join:"),
+ QString::null,
+ Kopete::UI::Global::mainWidget()
+ );
+
+ KCompletion comp;
+ comp.insertItems( chans );
+
+ dlg.lineEdit()->setCompletionObject( &comp );
+ dlg.lineEdit()->setCompletionMode( KGlobalSettings::CompletionPopup );
+
+ while( true )
+ {
+ if( dlg.exec() != QDialog::Accepted )
+ break;
+
+ QString chan = dlg.text();
+ if( chan.isNull() )
+ break;
+
+ if( KIRC::Entity::isChannel( chan ) )
+ {
+ contactManager()->findChannel( chan )->startChat();
+
+ // push the joined channel to first in list
+ chans.remove( chan );
+ chans.prepend( chan );
+
+ configGroup()->writeEntry( "Recent Channel list", chans );
+ break;
+ }
+
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.").arg(chan),
+ i18n("IRC Plugin")
+ );
+ }
+}
+
+void IRCAccount::slotNewCtcpReply(const QString &type, const QString &/*target*/, const QString &messageReceived)
+{
+ appendMessage( i18n("CTCP %1 REPLY: %2").arg(type).arg(messageReceived), InfoReply );
+}
+
+void IRCAccount::slotNoSuchNickname( const QString &nick )
+{
+ if( KIRC::Entity::isChannel(nick) )
+ appendMessage( i18n("The channel \"%1\" does not exist").arg(nick), UnknownReply );
+ else
+ appendMessage( i18n("The nickname \"%1\" does not exist").arg(nick), UnknownReply );
+}
+
+void IRCAccount::appendMessage( const QString &message, MessageType type )
+{
+ // TODO: Impliment a UI where people can pick multiple destinations
+ // for a message type, and make codethis handle it
+
+ MessageDestination destination;
+
+ switch( type )
+ {
+ case ConnectReply:
+ destination = m_serverMessages;
+ break;
+ case InfoReply:
+ destination = m_informationReplies;
+ break;
+ case NoticeReply:
+ destination = m_serverNotices;
+ break;
+ case ErrorReply:
+ destination = m_errorMessages;
+ break;
+ case UnknownReply:
+ default:
+ destination = ActiveWindow;
+ break;
+ }
+
+ if( destination == ActiveWindow )
+ {
+ KopeteView *activeView = Kopete::ChatSessionManager::self()->activeView();
+ if( activeView && activeView->msgManager()->account() == this )
+ {
+ Kopete::ChatSession *manager = activeView->msgManager();
+ Kopete::Message msg( manager->myself(), manager->members(), message,
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW );
+ activeView->appendMessage(msg);
+ }
+ }
+
+ if( destination == AnonymousWindow )
+ {
+ //TODO: Create an anonymous window??? What will this mean...
+ }
+
+ if( destination == ServerWindow )
+ {
+ myServer()->appendMessage(message);
+ }
+
+ if( destination == KNotify )
+ {
+ KNotifyClient::event(
+ Kopete::UI::Global::mainWidget()->winId(), QString::fromLatin1("irc_event"), message
+ );
+ }
+}
+
+IRCUserContact *IRCAccount::mySelf() const
+{
+ return static_cast<IRCUserContact *>( myself() );
+}
+
+IRCServerContact *IRCAccount::myServer() const
+{
+ return m_myServer;
+}
+
+IRCContact *IRCAccount::getContact(const QString &name, Kopete::MetaContact *metac)
+{
+ return getContact(m_engine->getEntity(name), metac);
+}
+
+IRCContact *IRCAccount::getContact(KIRC::EntityPtr entity, Kopete::MetaContact *metac)
+{
+ IRCContact *contact = 0;
+
+#ifdef __GNUC__
+ #warning Do the search code here.
+#endif
+
+ if (!contact)
+ {
+#ifdef __GNUC__
+ #warning Make a temporary meta contact if metac is null
+#endif
+ contact = new IRCContact(this, entity, metac);
+ m_contacts.append(contact);
+ }
+
+ QObject::connect(contact, SIGNAL(destroyed(IRCContact *)), SLOT(destroyed(IRCContact *)));
+ return contact;
+}
+
+void IRCAccount::destroyed(IRCContact *contact)
+{
+ m_contacts.remove(contact);
+}
+
+#include "ircaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ircaccount.h b/kopete/protocols/irc/ircaccount.h
new file mode 100644
index 00000000..e5917360
--- /dev/null
+++ b/kopete/protocols/irc/ircaccount.h
@@ -0,0 +1,248 @@
+/*
+ ircaccount.h - IRC Account
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCACCOUNT_H
+#define IRCACCOUNT_H
+
+#include "ircprotocol.h"
+
+#include "kircengine.h"
+
+#include "kopetepasswordedaccount.h"
+
+#include <kdialogbase.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+class ChannelListDialog;
+
+class IRCContact;
+class IRCChannelContact;
+class IRCContactManager;
+class IRCServerContact;
+class IRCProtocol;
+class IRCUserContact;
+
+namespace Kopete
+{
+class AwayAction;
+class Contact;
+class Message;
+class ChatSession;
+class MetaContact;
+}
+
+class KAction;
+class KActionMenu;
+
+struct IRCHost
+{
+ QString host;
+ uint port;
+ QString password;
+ bool ssl;
+};
+
+struct IRCNetwork
+{
+ QString name;
+ QString description;
+ QValueList<IRCHost*> hosts;
+};
+
+class IRCAccount
+ : public Kopete::PasswordedAccount
+{
+ friend class IRCEditAccountWidget;
+ friend class IRCProtocolHandler;
+
+ Q_OBJECT
+
+public:
+ static const QString CONFIG_CODECMIB;
+ static const QString CONFIG_NETWORKNAME;
+ static const QString CONFIG_NICKNAME;
+ static const QString CONFIG_USERNAME;
+ static const QString CONFIG_REALNAME;
+
+ enum MessageType
+ {
+ ConnectReply = 1,
+ InfoReply = 2,
+ NoticeReply = 4,
+ ErrorReply = 8,
+ UnknownReply = 16,
+ Default = 32
+ };
+
+ enum MessageDestination
+ {
+ ActiveWindow = 1,
+ ServerWindow = 2,
+ AnonymousWindow = 3,
+ KNotify = 4,
+ Ignore = 5
+ };
+
+ IRCAccount(IRCProtocol *p, const QString &accountid, const QString &autoConnect = QString::null,
+ const QString& networkName = QString::null, const QString &nickName = QString::null);
+ virtual ~IRCAccount();
+
+ void setNickName( const QString & );
+
+ void setAutoShowServerWindow( bool show );
+
+ void setAltNick( const QString & );
+ const QString altNick() const;
+
+ void setUserName( const QString & );
+ const QString userName() const;
+
+ void setRealName( const QString & );
+ const QString realName() const;
+
+ const QStringList connectCommands() const;
+
+ void setConnectCommands( const QStringList & ) const;
+
+ void setDefaultPart( const QString & );
+
+ void setNetwork( const QString & );
+
+ void setDefaultQuit( const QString & );
+
+ void setCodec( QTextCodec *codec );
+
+ void setMessageDestinations( int serverNotices, int serverMessages,
+ int informationReplies, int errorMessages );
+
+ QTextCodec *codec() const;
+
+ const QString defaultPart() const;
+
+ const QString defaultQuit() const;
+
+ const QString networkName() const;
+
+ QMap< QString, QString > customCtcp() const;
+
+ void setCustomCtcpReplies( const QMap< QString, QString > &replys ) const;
+
+ const QMap<QString, QString> customCtcpReplies() const;
+
+ void setCurrentCommandSource( Kopete::ChatSession *session );
+
+ Kopete::ChatSession *currentCommandSource();
+
+ IRCContact *getContact(const QString &name, Kopete::MetaContact *metac=0);
+ IRCContact *getContact(KIRC::EntityPtr entity, Kopete::MetaContact *metac=0);
+
+public slots:
+
+ virtual KActionMenu *actionMenu();
+
+ virtual void setAway( bool isAway, const QString &awayMessage = QString::null );
+
+ virtual bool isConnected();
+
+ /** Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+
+ // Returns the KIRC engine instance
+ KIRC::Engine *engine() const { return m_engine; }
+
+ // Returns the IRCProtocol instance for contacts
+ IRCProtocol *protocol() const { return m_protocol; }
+
+ IRCContactManager *contactManager() const { return m_contactManager; }
+
+ // Returns the Kopete::Contact of the user
+ IRCUserContact *mySelf() const;
+
+ // Returns the Kopete::Contact of the server of the user
+ IRCServerContact *myServer() const;
+
+ void successfullyChangedNick(const QString &, const QString &);
+
+ virtual void connectWithPassword( const QString & );
+ virtual void disconnect();
+
+ void quit( const QString &quitMessage = QString::null );
+
+ void listChannels();
+
+ void appendMessage( const QString &message, MessageType type = Default );
+
+protected:
+ virtual bool createContact( const QString &contactId, Kopete::MetaContact *parentContact ) ;
+
+private slots:
+ void engineStatusChanged(KIRC::Engine::Status newStatus);
+
+ void destroyed(IRCContact *contact);
+
+ void slotFailedServerPassword();
+ void slotGoAway( const QString &reason );
+ void slotJoinNamedChannel( const QString &channel );
+ void slotJoinChannel();
+ void slotShowServerWindow();
+ void slotNickInUse( const QString &nick );
+ void slotNickInUseAlert( const QString &nick );
+ void slotServerBusy();
+ void slotNoSuchNickname( const QString &nick );
+ void slotSearchChannels();
+ void slotNewCtcpReply(const QString &type, const QString &target, const QString &messageReceived);
+ void slotJoinedUnknownChannel( const QString &channel, const QString &nick );
+ void slotPerformOnConnectCommands();
+
+private:
+ Kopete::ChatSession *m_manager;
+ QString mNickName;
+ Kopete::AwayAction *mAwayAction;
+ bool triedAltNick;
+ bool autoShowServerWindow;
+ QString autoConnect;
+
+ KIRC::Engine *m_engine;
+ IRCNetwork *m_network;
+ uint currentHost;
+ QTextCodec *mCodec;
+
+ MessageDestination m_serverNotices;
+ MessageDestination m_serverMessages;
+ MessageDestination m_informationReplies;
+ MessageDestination m_errorMessages;
+
+ ChannelListDialog *m_channelList;
+
+ QValueList<IRCContact *> m_contacts;
+ IRCContactManager *m_contactManager;
+ IRCServerContact *m_myServer;
+
+ QMap< QString, QString > m_customCtcp;
+ Kopete::ChatSession *commandSource;
+
+ KAction *m_joinChannelAction;
+ KAction *m_searchChannelAction;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ircaddcontactpage.cpp b/kopete/protocols/irc/ircaddcontactpage.cpp
new file mode 100644
index 00000000..db4ca3b2
--- /dev/null
+++ b/kopete/protocols/irc/ircaddcontactpage.cpp
@@ -0,0 +1,83 @@
+/*
+ ircaddcontactpage.cpp - IRC Add Contact Widget
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircadd.h"
+#include "ircaddcontactpage.h"
+#include "channellist.h"
+
+#include "kircengine.h"
+
+#include "ircaccount.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qframe.h>
+#include <qtabwidget.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+IRCAddContactPage::IRCAddContactPage( QWidget *parent, IRCAccount *a ) : AddContactPage(parent, 0)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ ircdata = new ircAddUI(this);
+ mSearch = new ChannelList( (QWidget*)ircdata->hbox, a->engine() );
+ mAccount = a;
+
+ connect( mSearch, SIGNAL( channelSelected( const QString & ) ),
+ this, SLOT( slotChannelSelected( const QString & ) ) );
+
+ connect( mSearch, SIGNAL( channelDoubleClicked( const QString & ) ),
+ this, SLOT( slotChannelDoubleClicked( const QString & ) ) );
+}
+
+IRCAddContactPage::~IRCAddContactPage()
+{
+}
+
+void IRCAddContactPage::slotChannelSelected( const QString &channel )
+{
+ ircdata->addID->setText( channel );
+}
+
+void IRCAddContactPage::slotChannelDoubleClicked( const QString &channel )
+{
+ ircdata->addID->setText( channel );
+ ircdata->tabWidget3->setCurrentPage(0);
+}
+
+bool IRCAddContactPage::apply(Kopete::Account *account , Kopete::MetaContact *m)
+{
+ QString name = ircdata->addID->text();
+ return account->addContact(name, m, Kopete::Account::ChangeKABC );
+}
+
+bool IRCAddContactPage::validateData()
+{
+ QString name = ircdata->addID->text();
+ if (name.isEmpty() == true)
+ {
+ KMessageBox::sorry(this, i18n("<qt>You need to specify a channel to join, or query to open.</qt>"), i18n("You Must Specify a Channel"));
+ return false;
+ }
+ return true;
+}
+
+#include "ircaddcontactpage.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ircaddcontactpage.h b/kopete/protocols/irc/ircaddcontactpage.h
new file mode 100644
index 00000000..c6b897ff
--- /dev/null
+++ b/kopete/protocols/irc/ircaddcontactpage.h
@@ -0,0 +1,61 @@
+/*
+ ircaddcontactpage.h - IRC Add Contact Widget
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCADDCONTACTPAGE_H
+#define IRCADDCONTACTPAGE_H
+
+#include "addcontactpage.h"
+
+class ircAddUI;
+namespace Kopete { class MetaContact; }
+class IRCAccount;
+class QListViewItem;
+class ChannelList;
+
+/**
+ *@author Nick Betcher <nbetcher@kde.org>
+ */
+class IRCAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ IRCAddContactPage(QWidget *parent=0, IRCAccount* account = 0);
+ ~IRCAddContactPage();
+ ircAddUI *ircdata;
+
+public slots:
+ virtual bool apply(Kopete::Account *account , Kopete::MetaContact *m);
+
+private slots:
+ virtual bool validateData();
+ void slotChannelSelected( const QString &channel );
+ void slotChannelDoubleClicked( const QString &channel );
+private:
+ IRCAccount *mAccount;
+ ChannelList *mSearch;
+};
+
+#endif
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ircchannelcontact.cpp b/kopete/protocols/irc/ircchannelcontact.cpp
new file mode 100644
index 00000000..cc99acf3
--- /dev/null
+++ b/kopete/protocols/irc/ircchannelcontact.cpp
@@ -0,0 +1,749 @@
+/*
+ ircchannelcontact.cpp - IRC Channel Contact
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "irccontactmanager.h"
+#include "ircchannelcontact.h"
+#include "ircusercontact.h"
+#include "ircservercontact.h"
+#include "ircaccount.h"
+#include "ircprotocol.h"
+
+#include "kopeteview.h"
+#include "kopeteuiglobal.h"
+#include "kcodecaction.h"
+#include "kopetemetacontact.h"
+#include "kopetestdaction.h"
+#include "kopetechatsessionmanager.h"
+
+#include <kdebug.h>
+#include <krun.h>
+#include <kinputdialog.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+
+#include <qtimer.h>
+
+//This is the number of nicknames we will process concurrently when joining a channel
+//Lower numbers ensure less GUI blocking, but take marginally longer to complete.
+//Higher numbers are absolute fastest, but block GUI until all members are added
+#define NICK_BATCH_LENGTH 1
+
+IRCChannelContact::IRCChannelContact(IRCContactManager *contactManager, const QString &channel, Kopete::MetaContact *metac)
+ : IRCContact(contactManager, channel, metac, "irc_channel")
+{
+ KIRC::Engine *engine = kircEngine();
+
+ mInfoTimer = new QTimer( this );
+ QObject::connect(mInfoTimer, SIGNAL(timeout()), this, SLOT( slotUpdateInfo() ) );
+
+ QObject::connect(engine, SIGNAL(incomingUserIsAway(const QString &, const QString &)),
+ this, SLOT(slotIncomingUserIsAway(const QString &, const QString &)));
+
+ QObject::connect(engine, SIGNAL(incomingListedChan(const QString &, uint, const QString &)),
+ this, SLOT(slotChannelListed(const QString &, uint, const QString &)));
+
+ actionJoin = 0L;
+ actionModeT = new KToggleAction(i18n("Only Operators Can Change &Topic"), 0, this, SLOT(slotModeChanged()), this );
+ actionModeN = new KToggleAction(i18n("&No Outside Messages"), 0, this, SLOT(slotModeChanged()), this );
+ actionModeS = new KToggleAction(i18n("&Secret"), 0, this, SLOT(slotModeChanged()), this );
+ actionModeM = new KToggleAction(i18n("&Moderated"), 0, this, SLOT(slotModeChanged()), this );
+ actionModeI = new KToggleAction(i18n("&Invite Only"), 0, this, SLOT(slotModeChanged()), this );
+ actionHomePage = 0L;
+
+ updateStatus();
+}
+
+IRCChannelContact::~IRCChannelContact()
+{
+}
+
+void IRCChannelContact::slotUpdateInfo()
+{
+ /** This woudl be nice, but it generates server errors too often
+
+ if( !manager(Kopete::Contact::CannotCreate) && onlineStatus() == m_protocol->m_ChannelStatusOnline )
+ kircEngine()->writeMessage( QString::fromLatin1("LIST %1").arg(m_nickName) );
+ else
+ setProperty( QString::fromLatin1("channelMembers"), i18n("Members"), manager()->members().count() );
+
+ */
+ KIRC::Engine *engine = kircEngine();
+
+ if (manager(Kopete::Contact::CannotCreate))
+ {
+ setProperty(m_protocol->propChannelMembers, manager()->members().count());
+ engine->writeMessage(QString::fromLatin1("WHO %1").arg(m_nickName));
+ }
+ else
+ {
+ removeProperty(m_protocol->propChannelMembers);
+ removeProperty(m_protocol->propChannelTopic);
+ }
+
+ mInfoTimer->start( 45000, true );
+}
+
+void IRCChannelContact::slotChannelListed( const QString &channel, uint members, const QString &topic )
+{
+ if (!manager(Kopete::Contact::CannotCreate) &&
+ onlineStatus() == m_protocol->m_ChannelStatusOnline &&
+ channel.lower() == m_nickName.lower())
+ {
+ mTopic = topic;
+ setProperty(m_protocol->propChannelMembers, members);
+ setProperty(m_protocol->propChannelTopic, topic);
+ }
+}
+
+void IRCChannelContact::toggleOperatorActions(bool enabled)
+{
+ if (enabled) {
+ actionTopic->setEnabled(true);
+ } else if (modeEnabled('t')) {
+ actionTopic->setEnabled(false);
+ }
+
+ actionModeT->setEnabled(enabled);
+ actionModeN->setEnabled(enabled);
+ actionModeS->setEnabled(enabled);
+ actionModeM->setEnabled(enabled);
+ actionModeI->setEnabled(enabled);
+}
+
+void IRCChannelContact::slotOnlineStatusChanged(Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus)
+{
+ Q_UNUSED(oldStatus);
+
+ if (c == account()->myself()) {
+ if (status.internalStatus() & IRCProtocol::Operator) {
+ kdDebug(14120) << k_funcinfo << "WE NOW HAVE OP STATUS" << endl;
+ toggleOperatorActions(true);
+ } else {
+ kdDebug(14120) << k_funcinfo << "WE NOW dont HAVE OP STATUS" << endl;
+ toggleOperatorActions(false);
+ }
+ }
+}
+
+void IRCChannelContact::updateStatus()
+{
+ KIRC::Engine::Status status = kircEngine()->status();
+ switch (status)
+ {
+ case KIRC::Engine::Idle:
+ case KIRC::Engine::Connecting:
+ case KIRC::Engine::Authentifying:
+ setOnlineStatus(m_protocol->m_ChannelStatusOffline);
+ break;
+ case KIRC::Engine::Connected:
+ case KIRC::Engine::Closing:
+ setOnlineStatus(m_protocol->m_ChannelStatusOnline);
+ break;
+ default:
+ setOnlineStatus(m_protocol->m_StatusUnknown);
+ }
+}
+
+void IRCChannelContact::chatSessionDestroyed()
+{
+ if (manager(Kopete::Contact::CannotCreate))
+ {
+ part();
+ Kopete::ContactPtrList contacts = manager()->members();
+
+ // remove all the users on the channel
+ for (Kopete::Contact *c = contacts.first(); c; c = contacts.next())
+ {
+ if (c->metaContact()->isTemporary() &&
+ !static_cast<IRCContact*>(c)->isChatting(manager()))
+ c->deleteLater();
+ }
+ }
+
+ IRCContact::chatSessionDestroyed();
+}
+
+void IRCChannelContact::initConversation()
+{
+ kircEngine()->join(m_nickName, password());
+}
+
+void IRCChannelContact::slotConnectedToServer()
+{
+ setOnlineStatus(m_protocol->m_ChannelStatusOnline);
+ if (manager(Kopete::Contact::CannotCreate))
+ kircEngine()->join(m_nickName, password());
+}
+
+void IRCChannelContact::namesList(const QStringList &nicknames)
+{
+ mInfoTimer->stop();
+ mJoinedNicks += nicknames;
+ slotAddNicknames();
+}
+
+void IRCChannelContact::endOfNames()
+{
+ setMode(QString::null);
+ slotUpdateInfo();
+}
+
+void IRCChannelContact::slotAddNicknames()
+{
+ if( !manager(Kopete::Contact::CannotCreate) || mJoinedNicks.isEmpty())
+ {
+ return;
+ }
+
+ IRCAccount *account = ircAccount();
+
+ for( uint i = 0; !mJoinedNicks.isEmpty() && i < NICK_BATCH_LENGTH; ++i )
+ {
+ // Pick a nick from the front of the list.
+
+ QString nickToAdd = mJoinedNicks.front();
+ QChar firstChar = nickToAdd[0];
+ if( firstChar == '@' || firstChar == '%' || firstChar == '+' )
+ nickToAdd = nickToAdd.remove(0, 1);
+
+ IRCUserContact *user;
+
+ if ( nickToAdd.lower() != account->mySelf()->nickName().lower() )
+ {
+ //kdDebug(14120) << k_funcinfo << m_nickName << " nick to add: " << nickToAdd << endl;
+
+ user = account->contactManager()->findUser(nickToAdd);
+
+ // If the user is already present in some channel, dont flip the status
+ // back to online, because the other channels listen to
+ // onlineStatusChanged() emits, and they would adjust their statuses.
+
+ if (account->contactManager()->findChannelsByMember(user).isEmpty()) {
+ //kdDebug(14120) << k_funcinfo << "Setting nick ONLINE" << endl;
+ user->setOnlineStatus(m_protocol->m_UserStatusOnline);
+ }
+ }
+ else
+ {
+ // Handling my nick in the list.
+ user = account->mySelf();
+ }
+
+ Kopete::OnlineStatus status;
+ if ( firstChar == '@' || firstChar == '%' )
+ status = m_protocol->m_UserStatusOp;
+ else if( firstChar == '+')
+ status = m_protocol->m_UserStatusVoice;
+ else
+ status = user->onlineStatus();
+
+ if( user != account->mySelf() )
+ manager()->addContact(user , status, true);
+ else
+ manager()->setContactOnlineStatus(user, status);
+
+ mJoinedNicks.pop_front();
+ }
+
+ QTimer::singleShot( 0, this, SLOT( slotAddNicknames() ) );
+}
+
+void IRCChannelContact::channelTopic(const QString &topic)
+{
+ mTopic = topic;
+ setProperty( m_protocol->propChannelTopic, mTopic );
+ manager()->setDisplayName(caption());
+
+ if (mTopic.isEmpty()) {
+ Kopete::Message msg((Kopete::Contact*)this, mMyself,
+ i18n("Topic for %1 is set empty.").arg(m_nickName),
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW);
+ appendMessage(msg);
+ } else {
+ Kopete::Message msg((Kopete::Contact*)this, mMyself,
+ i18n("Topic for %1 is %2").arg(m_nickName).arg(mTopic),
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW);
+ appendMessage(msg);
+ }
+}
+
+void IRCChannelContact::channelHomePage(const QString &url)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+ setProperty( m_protocol->propHomepage, url );
+}
+
+void IRCChannelContact::join()
+{
+ if (!manager(Kopete::Contact::CannotCreate) &&
+ onlineStatus().status() == Kopete::OnlineStatus::Online)
+ {
+ kdDebug() << k_funcinfo << "My nickname:" << m_nickName << endl;
+ kdDebug() << k_funcinfo << "My manager:" << manager(Kopete::Contact::CannotCreate) << endl;
+ if( manager(Kopete::Contact::CannotCreate) )
+ kdDebug() << k_funcinfo << "My view:" << manager(Kopete::Contact::CannotCreate)->view(false) << endl;
+ startChat();
+ }
+
+ if (manager()) {
+ connect(manager(),
+ SIGNAL(onlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus &)),
+ SLOT(slotOnlineStatusChanged(Kopete::Contact *, const Kopete::OnlineStatus &,
+ const Kopete::OnlineStatus &)));
+ }
+}
+
+void IRCChannelContact::partAction()
+{
+ if (manager())
+ manager()->view()->closeView();
+}
+
+void IRCChannelContact::part()
+{
+ if (manager() && !kircEngine()->isDisconnected())
+ kircEngine()->part(m_nickName, ircAccount()->defaultPart());
+}
+
+void IRCChannelContact::slotIncomingUserIsAway( const QString &nick, const QString & )
+{
+ IRCAccount *account = ircAccount();
+
+ if( nick.lower() == account->mySelf()->nickName().lower() )
+ {
+ IRCUserContact *c = account->mySelf();
+ if (manager() && manager()->members().contains(c))
+ {
+ Kopete::OnlineStatus status = manager()->contactOnlineStatus(c);
+ if (status == m_protocol->m_UserStatusOp)
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOpAway );
+ else if (status == m_protocol->m_UserStatusOpAway)
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOp);
+ else if (status == m_protocol->m_UserStatusVoice)
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusVoiceAway);
+ else if (status == m_protocol->m_UserStatusVoiceAway)
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusVoice);
+ else if (status == m_protocol->m_UserStatusAway)
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusOnline);
+ else
+ manager()->setContactOnlineStatus(c, m_protocol->m_UserStatusAway);
+ }
+ }
+}
+
+void IRCChannelContact::userJoinedChannel(const QString &nickname)
+{
+ IRCAccount *account = ircAccount();
+
+ if (nickname.lower() == account->mySelf()->nickName().lower())
+ {
+ kdDebug() << k_funcinfo << "Me:" << this << endl;
+ kdDebug() << k_funcinfo << "My nickname:" << m_nickName << endl;
+ kdDebug() << k_funcinfo << "My manager:" << manager(Kopete::Contact::CannotCreate) << endl;
+
+ if (manager(Kopete::Contact::CannotCreate))
+ kdDebug() << k_funcinfo << "My view:" << manager(Kopete::Contact::CannotCreate)->view(false) << endl;
+
+ Kopete::Message msg((Kopete::Contact *)this, mMyself,
+ i18n("You have joined channel %1").arg(m_nickName),
+ Kopete::Message::Internal, Kopete::Message::PlainText,
+ CHAT_VIEW);
+ msg.setImportance( Kopete::Message::Low); //set the importance manualy to low
+ appendMessage(msg);
+ }
+ else
+ {
+ // If we have lag or huge channels, we might receive a JOIN after we have left a channel.
+ if (!manager())
+ return;
+
+ IRCUserContact *contact = account->contactManager()->findUser( nickname );
+ contact->setOnlineStatus( m_protocol->m_UserStatusOnline );
+ manager()->addContact((Kopete::Contact *)contact, true);
+ Kopete::Message msg((Kopete::Contact *)this, mMyself,
+ i18n("User <b>%1</b> joined channel %2").arg(nickname).arg(m_nickName),
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW);
+ msg.setImportance( Kopete::Message::Low); //set the importance manualy to low
+ manager()->appendMessage(msg);
+ }
+}
+
+void IRCChannelContact::userPartedChannel(const QString &nickname,const QString &reason)
+{
+ IRCAccount *account = ircAccount();
+
+ if (nickname.lower() != account->engine()->nickName().lower())
+ {
+ Kopete::Contact *c = locateUser( nickname );
+ if ( c )
+ {
+ manager()->removeContact( c, Kopete::Message::unescape(reason) );
+ if( c->metaContact()->isTemporary() && !static_cast<IRCContact*>(c)->isChatting( manager(Kopete::Contact::CannotCreate) ) )
+ c->deleteLater();
+ }
+ }
+}
+
+void IRCChannelContact::userKicked(const QString &nick, const QString &nickKicked, const QString &reason)
+{
+ IRCAccount *account = ircAccount();
+
+ if( nickKicked.lower() != account->engine()->nickName().lower() )
+ {
+ Kopete::Contact *c = locateUser( nickKicked );
+ if (c)
+ {
+ QString r;
+
+ if ((reason != nick) && (reason != nickKicked)) {
+ r = i18n( "%1 was kicked by %2. Reason: %3" ).arg(nickKicked, nick, reason);
+ } else {
+ r = i18n( "%1 was kicked by %2." ).arg(nickKicked, nick);
+ }
+
+ manager()->removeContact( c, r );
+ Kopete::Message msg( this, mMyself, r,
+ Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW);
+ msg.setImportance(Kopete::Message::Low);
+ appendMessage(msg);
+
+ if( c->metaContact()->isTemporary() &&
+ !static_cast<IRCContact*>(c)->isChatting( manager() ) )
+ c->deleteLater();
+ }
+ }
+ else
+ {
+ QString r;
+
+ if ((reason != nick) && (reason != nickKicked)) {
+ r = i18n( "You were kicked from %1 by %2. Reason: %3" ).arg(m_nickName, nickKicked, reason);
+ } else {
+ r = i18n( "You were kicked from %1 by %2." ).arg(m_nickName, nickKicked);
+ }
+
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), r, i18n("IRC Plugin"));
+ manager()->view()->closeView();
+ }
+}
+
+void IRCChannelContact::setTopic(const QString &topic)
+{
+ IRCAccount *account = ircAccount();
+
+ if (manager(Kopete::Contact::CannotCreate))
+ {
+ if( manager()->contactOnlineStatus( manager()->myself() ) ==
+ m_protocol->m_UserStatusOp || !modeEnabled('t') )
+ {
+ bool okPressed = true;
+ QString newTopic = topic;
+ if( newTopic.isNull() )
+ newTopic = KInputDialog::getText( i18n("New Topic"), i18n("Enter the new topic:"),
+ Kopete::Message::unescape(mTopic), &okPressed, 0L );
+
+ if( okPressed )
+ {
+ mTopic = newTopic;
+ kircEngine()->topic(m_nickName, newTopic);
+ }
+ }
+ else
+ {
+ Kopete::Message msg(account->myServer(), manager()->members(),
+ i18n("You must be a channel operator on %1 to do that.").arg(m_nickName),
+ Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW);
+ manager()->appendMessage(msg);
+ }
+ }
+}
+
+void IRCChannelContact::topicChanged(const QString &nick, const QString &newtopic)
+{
+ IRCAccount *account = ircAccount();
+
+ mTopic = newtopic;
+ setProperty( m_protocol->propChannelTopic, mTopic );
+ manager()->setDisplayName( caption() );
+ Kopete::Message msg(account->myServer(), mMyself,
+ i18n("%1 has changed the topic to: %2").arg(nick).arg(newtopic),
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW);
+ msg.setImportance(Kopete::Message::Low); //set the importance manualy to low
+ appendMessage(msg);
+}
+
+void IRCChannelContact::topicUser(const QString &nick, const QDateTime &time)
+{
+ IRCAccount *account = ircAccount();
+
+ Kopete::Message msg(account->myServer(), mMyself,
+ i18n("Topic set by %1 at %2").arg(nick).arg(
+ KGlobal::locale()->formatDateTime(time, true)
+ ), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW);
+ msg.setImportance(Kopete::Message::Low); //set the importance manualy to low
+ appendMessage(msg);
+}
+
+void IRCChannelContact::incomingModeChange( const QString &nick, const QString &mode )
+{
+ Kopete::Message msg(this, mMyself, i18n("%1 sets mode %2 on %3").arg(nick).arg(mode).arg(m_nickName), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW);
+ msg.setImportance( Kopete::Message::Low); //set the importance manualy to low
+ appendMessage(msg);
+
+ bool inParams = false;
+ bool modeEnabled = false;
+ QString params = QString::null;
+ for( uint i=0; i < mode.length(); i++ )
+ {
+ switch( mode[i] )
+ {
+ case '+':
+ modeEnabled = true;
+ break;
+
+ case '-':
+ modeEnabled = false;
+ break;
+
+ case ' ':
+ inParams = true;
+ break;
+ default:
+ if( inParams )
+ params.append( mode[i] );
+ else
+ toggleMode( mode[i], modeEnabled, false );
+ break;
+ }
+ }
+}
+
+void IRCChannelContact::incomingChannelMode( const QString &mode,
+ const QString &/*params*/ )
+{
+ for( uint i=1; i < mode.length(); i++ )
+ {
+ if( mode[i] != 'l' && mode[i] != 'k' )
+ toggleMode( mode[i], true, false );
+ }
+}
+
+void IRCChannelContact::setMode(const QString &mode)
+{
+ if (manager(Kopete::Contact::CannotCreate))
+ kircEngine()->mode(m_nickName, mode);
+}
+
+void IRCChannelContact::slotModeChanged()
+{
+ toggleMode( 't', actionModeT->isChecked(), true );
+ toggleMode( 'n', actionModeN->isChecked(), true );
+ toggleMode( 's', actionModeS->isChecked(), true );
+ toggleMode( 'm', actionModeM->isChecked(), true );
+ toggleMode( 'i', actionModeI->isChecked(), true );
+}
+
+void IRCChannelContact::failedChanBanned()
+{
+ manager()->deleteLater();
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("<qt>You can not join %1 because you have been banned.</qt>").arg(m_nickName),
+ i18n("IRC Plugin") );
+}
+
+void IRCChannelContact::failedChanInvite()
+{
+ manager()->deleteLater();
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("<qt>You can not join %1 because it is set to invite only, and no one has invited you.</qt>").arg(m_nickName), i18n("IRC Plugin") );
+}
+
+void IRCChannelContact::failedChanFull()
+{
+ manager()->deleteLater();
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n("<qt>You can not join %1 because it has reached its user limit.</qt>").arg(m_nickName),
+ i18n("IRC Plugin") );
+}
+
+void IRCChannelContact::failedChankey()
+{
+ bool ok;
+ QString diaPassword = KInputDialog::getText( i18n( "IRC Plugin" ),
+ i18n( "Please enter key for channel %1: ").arg(m_nickName),
+ QString::null,
+ &ok );
+
+ if ( !ok )
+ manager()->deleteLater();
+ else
+ {
+ setPassword(diaPassword);
+ kircEngine()->join(m_nickName, password());
+ }
+}
+
+void IRCChannelContact::toggleMode( QChar mode, bool enabled, bool update )
+{
+ if( manager(Kopete::Contact::CannotCreate) )
+ {
+ switch( mode )
+ {
+ case 't':
+ actionModeT->setChecked( enabled );
+
+ // If someones sets +t and we're not channel operators, disable the action.
+ if (enabled && !(manager()->contactOnlineStatus(ircAccount()->myself()).internalStatus() & IRCProtocol::Operator)) {
+ actionTopic->setEnabled( false );
+ } else {
+ actionTopic->setEnabled( true );
+ }
+ break;
+ case 'n':
+ actionModeN->setChecked( enabled );
+ break;
+ case 's':
+ actionModeS->setChecked( enabled );
+ break;
+ case 'm':
+ actionModeM->setChecked( enabled );
+ break;
+ case 'i':
+ actionModeI->setChecked( enabled );
+ break;
+ }
+ }
+
+ if( update )
+ {
+ if( modeMap[mode] != enabled )
+ {
+ if( enabled )
+ setMode( QString::fromLatin1("+") + mode );
+ else
+ setMode( QString::fromLatin1("-") + mode );
+ }
+ }
+
+ modeMap[mode] = enabled;
+}
+
+bool IRCChannelContact::modeEnabled( QChar mode, QString *value )
+{
+ if( !value )
+ return modeMap[mode];
+
+ return false;
+}
+
+QPtrList<KAction> *IRCChannelContact::customContextMenuActions()
+{
+ QPtrList<KAction> *mCustomActions = new QPtrList<KAction>();
+ if( !actionJoin )
+ {
+ actionJoin = new KAction(i18n("&Join"), 0, this, SLOT(join()), this, "actionJoin");
+ actionPart = new KAction(i18n("&Part"), 0, this, SLOT(partAction()), this, "actionPart");
+ actionTopic = new KAction(i18n("Change &Topic..."), 0, this, SLOT(setTopic()), this, "actionTopic");
+ actionModeMenu = new KActionMenu(i18n("Channel Modes"), 0, this, "actionModeMenu");
+
+ if( !property(m_protocol->propHomepage).value().isNull() )
+ {
+ actionHomePage = new KAction( i18n("Visit &Homepage"), 0, this,
+ SLOT(slotHomepage()), this, "actionHomepage");
+ }
+ else if( actionHomePage )
+ {
+ delete actionHomePage;
+ }
+
+ actionModeMenu->insert( actionModeT );
+ actionModeMenu->insert( actionModeN );
+ actionModeMenu->insert( actionModeS );
+ actionModeMenu->insert( actionModeM );
+ actionModeMenu->insert( actionModeI );
+ actionModeMenu->setEnabled( true );
+
+ codecAction = new KCodecAction( i18n("&Encoding"), 0, this, "selectcharset" );
+ connect( codecAction, SIGNAL( activated( const QTextCodec * ) ),
+ this, SLOT( setCodec( const QTextCodec *) ) );
+ codecAction->setCodec( codec() );
+ }
+
+ mCustomActions->append( actionJoin );
+ mCustomActions->append( actionPart );
+ mCustomActions->append( actionTopic );
+ mCustomActions->append( actionModeMenu );
+ mCustomActions->append( codecAction );
+ if( actionHomePage )
+ mCustomActions->append( actionHomePage );
+
+ bool isOperator = manager(Kopete::Contact::CannotCreate) &&
+ (manager()->contactOnlineStatus(ircAccount()->myself()).internalStatus() & IRCProtocol::Operator);
+
+ actionJoin->setEnabled( !manager(Kopete::Contact::CannotCreate) );
+ actionPart->setEnabled( manager(Kopete::Contact::CannotCreate) );
+ actionTopic->setEnabled( manager(Kopete::Contact::CannotCreate) && ( !modeEnabled('t') || isOperator ) );
+
+ toggleOperatorActions(isOperator);
+
+ return mCustomActions;
+}
+
+void IRCChannelContact::slotHomepage()
+{
+ QString homePage = property(m_protocol->propHomepage).value().toString();
+ if( !homePage.isEmpty() )
+ {
+ new KRun( KURL( homePage ), 0, false);
+ }
+}
+
+const QString IRCChannelContact::caption() const
+{
+ QString cap = QString::fromLatin1("%1 @ %2").arg(m_nickName).arg(kircEngine()->currentHost());
+ if(!mTopic.isEmpty())
+ cap.append( QString::fromLatin1(" - %1").arg(Kopete::Message::unescape(mTopic)) );
+
+ return cap;
+}
+
+void IRCChannelContact::privateMessage(IRCContact *from, IRCContact *to, const QString &message)
+{
+ if(to == this)
+ {
+ Kopete::Message msg(from, manager()->members(), message, Kopete::Message::Inbound,
+ Kopete::Message::RichText, CHAT_VIEW);
+ appendMessage(msg);
+ }
+}
+
+void IRCChannelContact::newAction(const QString &from, const QString &action)
+{
+ IRCAccount *account = ircAccount();
+
+ IRCUserContact *f = account->contactManager()->findUser(from);
+ Kopete::Message::MessageDirection dir =
+ (f == account->mySelf()) ? Kopete::Message::Outbound : Kopete::Message::Inbound;
+ Kopete::Message msg(f, manager()->members(), action, dir, Kopete::Message::RichText,
+ CHAT_VIEW, Kopete::Message::TypeAction);
+ appendMessage(msg);
+}
+
+#include "ircchannelcontact.moc"
diff --git a/kopete/protocols/irc/ircchannelcontact.h b/kopete/protocols/irc/ircchannelcontact.h
new file mode 100644
index 00000000..15a72e17
--- /dev/null
+++ b/kopete/protocols/irc/ircchannelcontact.h
@@ -0,0 +1,156 @@
+/*
+ ircchannelcontact.h - IRC Channel Contact
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCCHANNELCONTACT_H
+#define IRCCHANNELCONTACT_H
+
+#include "irccontact.h"
+
+class KActionCollection;
+class KAction;
+class KActionMenu;
+class KCodecAction;
+class KToggleAction;
+
+namespace Kopete { class MetaContact; }
+namespace Kopete { class ChatSession; }
+namespace Kopete { class Message; }
+class KopeteView;
+
+class IRCAccount;
+class IRCContactManager;
+
+/**
+ * @author Jason Keirstead <jason@keirstead.org>
+ *
+ * This class is the @ref Kopete::Contact object representing IRC Channels, not users.
+ * It is derived from IRCContact where much of its functionality is shared with @ref IRCUserContact.
+ */
+class IRCChannelContact
+ : public IRCContact
+{
+ friend class IRCSignalMapper;
+
+ Q_OBJECT
+
+public:
+ IRCChannelContact(IRCContactManager *, const QString &channel, Kopete::MetaContact *metac);
+ ~IRCChannelContact();
+
+ /**
+ * Returns the current topic for this channel.
+ */
+ const QString &topic() const { return mTopic; };
+
+ /* Set password for a channel */
+ void setPassword(const QString &password) { mPassword = password; }
+ /* Get password for a channel */
+ const QString &password() const { return mPassword; }
+
+ /**
+ * Returns if a mode is enabled for this channel.
+ * @param mode The mode you want to check ( 't', 'n', etc. )
+ * @param value This is a pointer to a QString which is set to
+ * the value of the mode if it has one. Example, the mode 'l' or
+ * the mode 'k'. If the mode has no such value then the pointer
+ * is always returned null.
+ */
+ bool modeEnabled( QChar mode, QString *value = 0 );
+
+ // Kopete::Contact stuff
+ virtual QPtrList<KAction> *customContextMenuActions();
+ virtual const QString caption() const;
+
+ //Methods handled by the signal mapper
+ void userJoinedChannel(const QString &user);
+ void userPartedChannel(const QString &user, const QString &reason);
+ void userKicked(const QString &nick, const QString &nickKicked, const QString &reason);
+ void channelTopic(const QString &topic);
+ void channelHomePage(const QString &url);
+ void topicChanged(const QString &nick, const QString &newtopic);
+ void topicUser(const QString &nick, const QDateTime &time);
+ void namesList(const QStringList &nicknames);
+ void endOfNames();
+ void incomingModeChange(const QString &nick, const QString &mode);
+ void incomingChannelMode(const QString &mode, const QString &params );
+ void failedChankey();
+ void failedChanBanned();
+ void failedChanInvite();
+ void failedChanFull();
+ void newAction(const QString &from, const QString &action);
+
+public slots:
+ void updateStatus();
+
+ /**
+ * Sets the topic of this channel
+ * @param topic The topic you want set
+ */
+ void setTopic( const QString &topic = QString::null );
+
+ /**
+ * Sets or unsets a mode on this channel
+ * @param mode The full text of the mode change you want performed
+ */
+ void setMode( const QString &mode = QString::null );
+
+ void part();
+ void partAction();
+ void join();
+
+protected slots:
+ void chatSessionDestroyed();
+
+ virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message);
+ virtual void initConversation();
+
+private slots:
+ void slotIncomingUserIsAway( const QString &nick, const QString &reason );
+ void slotModeChanged();
+ void slotAddNicknames();
+ void slotConnectedToServer();
+ void slotUpdateInfo();
+ void slotHomepage();
+ void slotChannelListed(const QString &channel, uint members, const QString &topic);
+ void slotOnlineStatusChanged(Kopete::Contact *c, const Kopete::OnlineStatus &status, const Kopete::OnlineStatus &oldStatus);
+
+private:
+ KAction *actionJoin;
+ KAction *actionPart;
+ KAction *actionTopic;
+ KAction *actionHomePage;
+ KActionMenu *actionModeMenu;
+ KCodecAction *codecAction;
+
+ KToggleAction *actionModeT; // Only Operators Can Change Topic
+ KToggleAction *actionModeN; // No Outside Messages
+ KToggleAction *actionModeS; // Secret
+ KToggleAction *actionModeI; // Invite Only
+ KToggleAction *actionModeM; // Moderated
+
+ QString mTopic;
+ QString mPassword;
+ QStringList mJoinedNicks;
+ QMap<QString, bool> modeMap;
+ QTimer *mInfoTimer;
+
+ void toggleMode( QChar mode, bool enabled, bool update );
+ void toggleOperatorActions( bool enabled );
+};
+
+#endif
diff --git a/kopete/protocols/irc/ircchatui.rc b/kopete/protocols/irc/ircchatui.rc
new file mode 100644
index 00000000..9c1b9dbb
--- /dev/null
+++ b/kopete/protocols/irc/ircchatui.rc
@@ -0,0 +1,10 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="26" name="kopetechatwindow">
+ <MenuBar>
+ <Menu name="irc" >
+ <text>IRC</text>
+ <ActionList name="irccontactactionlist" />
+ </Menu>
+ </MenuBar>
+
+</kpartgui>
diff --git a/kopete/protocols/irc/irccontact.cpp b/kopete/protocols/irc/irccontact.cpp
new file mode 100644
index 00000000..64f89322
--- /dev/null
+++ b/kopete/protocols/irc/irccontact.cpp
@@ -0,0 +1,425 @@
+/*
+ irccontact.cpp - IRC Contact
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qregexp.h>
+
+#include <qtimer.h>
+#include <qtextcodec.h>
+
+#include "ircaccount.h"
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+#include "kopeteview.h"
+#include "ircusercontact.h"
+#include "irccontact.h"
+#include "ircprotocol.h"
+#include "ircservercontact.h"
+#include "irccontactmanager.h"
+#include "ksparser.h"
+
+IRCContact::IRCContact(IRCAccount *account, KIRC::EntityPtr entity, Kopete::MetaContact *metac, const QString& icon)
+ : Kopete::Contact(account, entity->name(), metac, icon),
+ m_chatSession(0)
+{
+}
+
+IRCContact::IRCContact(IRCContactManager *contactManager, const QString &nick, Kopete::MetaContact *metac, const QString& icon)
+ : Kopete::Contact(contactManager->account(), nick, metac, icon),
+ m_nickName(nick),
+ m_chatSession(0)
+{
+ KIRC::Engine *engine = kircEngine();
+
+ // Contact list display name
+ setProperty( Kopete::Global::Properties::self()->nickName(), m_nickName );
+
+ // IRCContactManager stuff
+ QObject::connect(contactManager, SIGNAL(privateMessage(IRCContact *, IRCContact *, const QString &)),
+ this, SLOT(privateMessage(IRCContact *, IRCContact *, const QString &)));
+
+ // Kopete::ChatSessionManager stuff
+ mMyself.append( static_cast<Kopete::Contact*>( this ) );
+
+ // KIRC stuff
+ QObject::connect(engine, SIGNAL(incomingNickChange(const QString &, const QString &)),
+ this, SLOT( slotNewNickChange(const QString&, const QString&)));
+ QObject::connect(engine, SIGNAL(successfullyChangedNick(const QString &, const QString &)),
+ this, SLOT(slotNewNickChange(const QString &, const QString &)));
+ QObject::connect(engine, SIGNAL(incomingQuitIRC(const QString &, const QString &)),
+ this, SLOT( slotUserDisconnected(const QString&, const QString&)));
+
+ QObject::connect(engine, SIGNAL(statusChanged(KIRC::Engine::Status)),
+ this, SLOT(updateStatus()));
+
+ engine->setCodec( m_nickName, codec() );
+}
+
+IRCContact::~IRCContact()
+{
+// kdDebug(14120) << k_funcinfo << m_nickName << endl;
+ if (metaContact() && metaContact()->isTemporary() && !isChatting(m_chatSession))
+ metaContact()->deleteLater();
+
+ emit destroyed(this);
+}
+
+IRCAccount *IRCContact::ircAccount() const
+{
+ return static_cast<IRCAccount *>(account());
+}
+
+KIRC::Engine *IRCContact::kircEngine() const
+{
+ return ircAccount()->engine();
+}
+
+bool IRCContact::isReachable()
+{
+ if (onlineStatus().status() != Kopete::OnlineStatus::Offline &&
+ onlineStatus().status() != Kopete::OnlineStatus::Unknown)
+ return true;
+
+ return false;
+}
+
+const QString IRCContact::caption() const
+{
+ return QString::null;
+}
+/*
+const QString IRCContact::formatedName() const
+{
+ return QString::null;
+}
+*/
+void IRCContact::updateStatus()
+{
+}
+
+void IRCContact::privateMessage(IRCContact *, IRCContact *, const QString &)
+{
+}
+
+void IRCContact::setCodec(const QTextCodec *codec)
+{
+ kircEngine()->setCodec(m_nickName, codec);
+ metaContact()->setPluginData(m_protocol, QString::fromLatin1("Codec"), QString::number(codec->mibEnum()));
+}
+
+const QTextCodec *IRCContact::codec()
+{
+ QString codecId = metaContact()->pluginData(m_protocol, QString::fromLatin1("Codec"));
+ QTextCodec *codec = ircAccount()->codec();
+
+ if( !codecId.isEmpty() )
+ {
+ bool test = true;
+ uint mib = codecId.toInt(&test);
+ if (test)
+ codec = QTextCodec::codecForMib(mib);
+ else
+ codec = QTextCodec::codecForName(codecId.latin1());
+ }
+
+ if( !codec )
+ return kircEngine()->codec();
+
+ return codec;
+}
+
+Kopete::ChatSession *IRCContact::manager(Kopete::Contact::CanCreateFlags canCreate)
+{
+ IRCAccount *account = ircAccount();
+ KIRC::Engine *engine = kircEngine();
+
+ if (canCreate == Kopete::Contact::CanCreate && !m_chatSession)
+ {
+ if( engine->status() == KIRC::Engine::Idle && dynamic_cast<IRCServerContact*>(this) == 0 )
+ account->connect();
+
+ m_chatSession = Kopete::ChatSessionManager::self()->create(account->myself(), mMyself, account->protocol());
+ m_chatSession->setDisplayName(caption());
+
+ QObject::connect(m_chatSession, SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession *)),
+ this, SLOT(slotSendMsg(Kopete::Message&, Kopete::ChatSession *)));
+ QObject::connect(m_chatSession, SIGNAL(closing(Kopete::ChatSession *)),
+ this, SLOT(chatSessionDestroyed()));
+
+ initConversation();
+ }
+
+ return m_chatSession;
+}
+
+void IRCContact::chatSessionDestroyed()
+{
+ m_chatSession = 0;
+
+ if (metaContact()->isTemporary() && !isChatting())
+ deleteLater();
+}
+
+void IRCContact::slotUserDisconnected(const QString &user, const QString &reason)
+{
+ if (m_chatSession)
+ {
+ QString nickname = user.section('!', 0, 0);
+ Kopete::Contact *c = locateUser( nickname );
+ if ( c )
+ {
+ m_chatSession->removeContact(c, i18n("Quit: \"%1\" ").arg(reason), Kopete::Message::RichText);
+ c->setOnlineStatus(m_protocol->m_UserStatusOffline);
+ }
+ }
+}
+
+void IRCContact::setNickName( const QString &nickname )
+{
+ kdDebug(14120) << k_funcinfo << m_nickName << " changed to " << nickname << endl;
+ m_nickName = nickname;
+ Kopete::Contact::setNickName( nickname );
+}
+
+void IRCContact::slotNewNickChange(const QString &oldnickname, const QString &newnickname)
+{
+ IRCAccount *account = ircAccount();
+
+ IRCContact *user = static_cast<IRCContact*>( locateUser(oldnickname) );
+ if( user )
+ {
+ user->setNickName( newnickname );
+
+ //If the user is in our contact list, then change the notify list nickname
+ if (!user->metaContact()->isTemporary())
+ {
+ account->contactManager()->removeFromNotifyList( oldnickname );
+ account->contactManager()->addToNotifyList( newnickname );
+ }
+ }
+}
+
+void IRCContact::slotSendMsg(Kopete::Message &message, Kopete::ChatSession *)
+{
+ QString htmlString = message.escapedBody();
+
+ // Messages we get with RichText enabled:
+ //
+ // Hello world in bold and color:
+ // <span style="font-weight:600;color:#403897">Hello World</span>
+ //
+ // Two-liner in color:
+ // <span style="color:#403897">Hello<br />World</span>
+
+ if (htmlString.find(QString::fromLatin1("</span")) > -1)
+ {
+ QRegExp findTags( QString::fromLatin1("<span style=\"(.*)\">(.*)</span>") );
+ findTags.setMinimal( true );
+ int pos = 0;
+
+ while (pos >= 0)
+ {
+ pos = findTags.search(htmlString);
+ if (pos > -1)
+ {
+ QString styleHTML = findTags.cap(1);
+ QString replacement = findTags.cap(2);
+ QStringList styleAttrs = QStringList::split(';', styleHTML);
+
+ for (QStringList::Iterator attrPair = styleAttrs.begin(); attrPair != styleAttrs.end(); ++attrPair)
+ {
+ QString attribute = (*attrPair).section(':',0,0);
+ QString value = (*attrPair).section(':',1);
+
+ if( attribute == QString::fromLatin1("color") )
+ {
+ int ircColor = KSParser::colorForHTML( value );
+ if( ircColor > -1 )
+ replacement.prepend( QString( QChar(0x03) ).append( QString::number(ircColor) ) ).append( QChar( 0x03 ) );
+ }
+ else if( attribute == QString::fromLatin1("font-weight") &&
+ value == QString::fromLatin1("600") ) {
+ // Bolding
+ replacement.prepend( QChar(0x02) ).append( QChar(0x02) );
+ }
+ else if( attribute == QString::fromLatin1("text-decoration") &&
+ value == QString::fromLatin1("underline") ) {
+ replacement.prepend( QChar(31) ).append( QChar(31) );
+ }
+ }
+
+ htmlString = htmlString.left( pos ) + replacement + htmlString.mid( pos + findTags.matchedLength() );
+ }
+ }
+ }
+
+ htmlString = Kopete::Message::unescape(htmlString);
+
+ QStringList messages = QStringList::split( '\n', htmlString );
+
+ for( QStringList::Iterator it = messages.begin(); it != messages.end(); ++it )
+ {
+ // Dont use the resulting string(s). The problem is that we'd have to parse them
+ // back to format that would be suitable for appendMessage().
+ //
+ // TODO: If the given message was plaintext, we could easily show what was
+ // actually sent.
+
+ sendMessage(*it);
+ }
+
+ if (message.requestedPlugin() != CHAT_VIEW) {
+ Kopete::Message msg(message.from(), message.to(), message.escapedBody(), message.direction(),
+ Kopete::Message::RichText, CHAT_VIEW, message.type());
+
+ msg.setBg(QColor());
+ msg.setFg(QColor());
+
+ appendMessage(msg);
+ } else {
+ // Lets not modify the given message object.
+ Kopete::Message msg = message;
+ msg.setBg(QColor());
+ appendMessage(msg);
+ }
+
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+QStringList IRCContact::sendMessage( const QString &msg )
+{
+ QStringList messages;
+
+ QString newMessage = msg;
+
+ // IRC limits the message size to 512 characters. So split the given
+ // message into pieces.
+ //
+ // This can of course give nasty results, but most of us dont write
+ // that long lines anyway ;-)... And this is how other clients also
+ // seem to behave.
+
+ int l = 500 - m_nickName.length();
+
+ do {
+ messages.append(newMessage.mid(0, l));
+ newMessage.remove(0, l);
+ } while (!newMessage.isEmpty());
+
+ for (QStringList::const_iterator it = messages.begin();
+ it != messages.end(); ++it)
+ kircEngine()->privmsg(m_nickName, *it);
+
+ return messages;
+}
+
+Kopete::Contact *IRCContact::locateUser(const QString &nick)
+{
+ IRCAccount *account = ircAccount();
+
+ if (m_chatSession)
+ {
+ if( nick == account->mySelf()->nickName() )
+ return account->mySelf();
+ else
+ {
+ Kopete::ContactPtrList mMembers = m_chatSession->members();
+ for (Kopete::Contact *it = mMembers.first(); it; it = mMembers.next())
+ {
+ if (static_cast<IRCContact*>(it)->nickName() == nick)
+ return it;
+ }
+ }
+ }
+ return 0;
+}
+
+bool IRCContact::isChatting(const Kopete::ChatSession *avoid) const
+{
+ IRCAccount *account = ircAccount();
+
+ if (!account)
+ return false;
+
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ for (QValueList<Kopete::ChatSession*>::Iterator it= sessions.begin(); it!=sessions.end() ; ++it)
+ {
+ if( (*it) != avoid && (*it)->account() == account &&
+ (*it)->members().contains(this) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void IRCContact::deleteContact()
+{
+ kdDebug(14120) << k_funcinfo << m_nickName << endl;
+
+ delete m_chatSession;
+
+ if (!isChatting())
+ {
+ kdDebug(14120) << k_funcinfo << "will delete " << m_nickName << endl;
+ Kopete::Contact::deleteContact();
+ }
+ else
+ {
+ metaContact()->removeContact(this);
+ Kopete::MetaContact *m = new Kopete::MetaContact();
+ m->setTemporary(true);
+ setMetaContact(m);
+ }
+}
+
+void IRCContact::appendMessage(Kopete::Message &msg)
+{
+ manager(Kopete::Contact::CanCreate)->appendMessage(msg);
+}
+
+KopeteView *IRCContact::view()
+{
+ if (m_chatSession)
+ return m_chatSession->view(false);
+ return 0L;
+}
+void IRCContact::serialize(QMap<QString, QString> & /*serializedData*/, QMap<QString, QString> &addressBookData)
+{
+ // write the
+ addressBookData[ protocol()->addressBookIndexField() ] = ( contactId() + QChar(0xE120) + account()->accountId() );
+}
+
+void IRCContact::receivedMessage( KIRC::Engine::ServerMessageType type,
+ const KIRC::EntityPtr &from,
+ const KIRC::EntityPtrList &to,
+ const QString &msg)
+{
+ if (to.contains(m_entity))
+ {
+ IRCContact *fromContact = ircAccount()->getContact(from);
+ Kopete::Message message(fromContact, manager()->members(), msg, Kopete::Message::Inbound,
+ Kopete::Message::RichText, CHAT_VIEW);
+ appendMessage(message);
+ }
+}
+
+#include "irccontact.moc"
diff --git a/kopete/protocols/irc/irccontact.h b/kopete/protocols/irc/irccontact.h
new file mode 100644
index 00000000..058315fb
--- /dev/null
+++ b/kopete/protocols/irc/irccontact.h
@@ -0,0 +1,153 @@
+/*
+ irccontact.h - IRC Contact
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCCONTACT_H
+#define IRCCONTACT_H
+
+#include "kircengine.h"
+#include "kircentity.h"
+
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+
+#include <qptrlist.h>
+#include <qmap.h>
+
+class IRCProtocol;
+class IRCAccount;
+class IRCContactManager;
+
+namespace KIRC
+{
+class Engine;
+}
+
+namespace Kopete
+{
+class ChatSession;
+class MetaContact;
+}
+
+class KopeteView;
+
+class QTextCodec;
+
+/**
+ * @author Jason Keirstead <jason@keirstead.org>
+ * @author Michel Hermier <michel.hermier@wanadoo.fr>
+ *
+ * This class is the base class for @ref IRCUserContact and @ref IRCChannelContact.
+ * Common routines and signal connections that are required for both types of
+ * contacts reside here, to avoid code duplication between these two classes.
+ */
+class IRCContact
+ : public Kopete::Contact
+{
+ Q_OBJECT
+
+public:
+ IRCContact(IRCAccount *account, KIRC::EntityPtr entity, Kopete::MetaContact *metac, const QString& icon = QString::null);
+ IRCContact(IRCContactManager *contactManager, const QString &nick, Kopete::MetaContact *metac, const QString& icon = QString::null);
+ virtual ~IRCContact();
+
+ IRCAccount *ircAccount() const;
+ KIRC::Engine *kircEngine() const;
+
+ /**
+ * Sets the nickname of this contact. The nickname is distinct from the displayName
+ * in case trackNameChanges is disabled.
+ */
+ void setNickName(const QString &nickname);
+
+ /**
+ * Returns the nickname / channel name
+ */
+ const QString &nickName() const { return m_nickName; }
+
+ /**
+ * This function attempts to find the nickname specified within the current chat
+ * session. Returns a pointer to that IRCUserContact, or 0L if the user does not
+ * exist in this session. More useful for channels. Calling IRCChannelContact::locateUser()
+ * for example tells you if a user is in a certain channel.
+ */
+ Kopete::Contact *locateUser( const QString &nickName );
+
+ virtual bool isReachable();
+
+ /**
+ * return true if the contact is in a chat. false if the contact is in no chats
+ * that loop over all manager, and checks the presence of the user
+ */
+ bool isChatting( const Kopete::ChatSession *avoid = 0L ) const;
+
+ virtual const QString caption() const;
+// virtual const QString formatedName() const;
+
+ virtual Kopete::ChatSession *manager(Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate);
+
+ virtual void appendMessage( Kopete::Message & );
+
+ const QTextCodec *codec();
+
+ KopeteView *view();
+
+ /**
+ * We serialise the contactId and the server group in 'contactId'
+ * so that other IRC programs reading this from KAddressBook have a chance of figuring
+ * which server the contact relates to
+ */
+ virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData );
+
+signals:
+ void destroyed(IRCContact *self);
+
+public slots:
+ void setCodec( const QTextCodec *codec );
+ virtual void updateStatus();
+
+protected slots:
+ virtual void slotSendMsg(Kopete::Message &message, Kopete::ChatSession *);
+ QStringList sendMessage( const QString &msg );
+
+ virtual void chatSessionDestroyed();
+
+ void slotNewNickChange( const QString &oldnickname, const QString &newnickname);
+ void slotUserDisconnected( const QString &nickname, const QString &reason);
+
+ virtual void deleteContact();
+ virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message);
+ virtual void initConversation() {};
+
+ void receivedMessage( KIRC::Engine::ServerMessageType type,
+ const KIRC::EntityPtr &from,
+ const KIRC::EntityPtrList &to,
+ const QString &msg);
+
+protected:
+ KIRC::EntityPtr m_entity;
+
+ QString m_nickName;
+ Kopete::ChatSession *m_chatSession;
+
+ QPtrList<Kopete::Contact> mMyself;
+ Kopete::Message::MessageDirection execDir;
+};
+
+#endif
diff --git a/kopete/protocols/irc/irccontactmanager.cpp b/kopete/protocols/irc/irccontactmanager.cpp
new file mode 100644
index 00000000..7808668b
--- /dev/null
+++ b/kopete/protocols/irc/irccontactmanager.cpp
@@ -0,0 +1,297 @@
+/*
+ irccontactmanager.cpp - Manager of IRC Contacts
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircusercontact.h"
+#include "ircaccount.h"
+#include "irccontactmanager.h"
+#include "ircprotocol.h"
+#include "ircsignalhandler.h"
+
+#include "ircservercontact.h"
+#include "ircchannelcontact.h"
+
+#include "kircengine.h"
+
+#include <kopeteaccountmanager.h>
+#include <kopetemetacontact.h>
+#include <kopetecontactlist.h>
+#include <kopeteview.h>
+
+#include <kconfig.h>
+#include <kstandarddirs.h>
+
+#include <qtimer.h>
+
+IRCContactManager::IRCContactManager(const QString &nickName, IRCAccount *account, const char *name)
+ : QObject(account, name),
+ m_channels( QDict<IRCChannelContact>( 17, false ) ),
+ m_users( QDict<IRCUserContact>( 577, false ) ),
+ m_account( account )
+{
+ m_mySelf = findUser(nickName);
+
+ Kopete::MetaContact *m = new Kopete::MetaContact();
+// m->setTemporary( true );
+ m_myServer = new IRCServerContact(this, account->networkName(), m);
+
+ QObject::connect(account->engine(), SIGNAL(incomingMessage(const QString &, const QString &, const QString &)),
+ this, SLOT(slotNewMessage(const QString &, const QString &, const QString &)));
+
+ QObject::connect(account->engine(), SIGNAL(incomingPrivMessage(const QString &, const QString &, const QString &)),
+ this, SLOT(slotNewPrivMessage(const QString &, const QString &, const QString &)));
+
+ QObject::connect(account->engine(), SIGNAL(incomingNickChange(const QString &, const QString &)),
+ this, SLOT( slotNewNickChange(const QString&, const QString&)));
+
+ QObject::connect(account->engine(), SIGNAL(successfullyChangedNick(const QString &, const QString &)),
+ this, SLOT( slotNewNickChange(const QString &, const QString &)));
+
+ QObject::connect(account->engine(), SIGNAL(incomingUserOnline(const QString &)),
+ this, SLOT( slotIsonRecieved()));
+
+ QObject::connect(Kopete::ContactList::self(), SIGNAL(metaContactAdded( Kopete::MetaContact * )),
+ this, SLOT( slotContactAdded( Kopete::MetaContact* )));
+
+ socketTimeout = 15000;
+ QString timeoutPath = locate( "config", "kioslaverc" );
+ if( !timeoutPath.isEmpty() )
+ {
+ KConfig config( timeoutPath );
+ socketTimeout = config.readNumEntry( "ReadTimeout", 15 ) * 1000;
+ }
+
+ m_NotifyTimer = new QTimer(this);
+ QObject::connect(m_NotifyTimer, SIGNAL(timeout()),
+ this, SLOT(checkOnlineNotifyList()));
+ m_NotifyTimer->start(30000); // check online every 30sec
+
+ new IRCSignalHandler(this);
+}
+
+void IRCContactManager::slotNewNickChange(const QString &oldnick, const QString &newnick)
+{
+ IRCUserContact *c = m_users[ oldnick ];
+ if( c )
+ {
+ m_users.insert(newnick, c);
+ m_users.remove(oldnick);
+ }
+}
+
+void IRCContactManager::slotNewMessage(const QString &originating, const QString &channel, const QString &message)
+{
+ IRCContact *from = findUser(originating);
+ IRCChannelContact *to = findChannel(channel);
+ emit privateMessage(from, to, message);
+}
+
+void IRCContactManager::slotNewPrivMessage(const QString &originating, const QString &user, const QString &message)
+{
+ IRCContact *from = findUser(originating);
+ IRCUserContact *to = findUser(user);
+ emit privateMessage(from, to, message);
+}
+
+void IRCContactManager::unregister(Kopete::Contact *contact)
+{
+ unregisterChannel(contact, true);
+ unregisterUser(contact, true);
+}
+
+QValueList<IRCChannelContact*> IRCContactManager::findChannelsByMember( IRCUserContact *contact )
+{
+ QValueList<IRCChannelContact*> retVal;
+ for( QDictIterator<IRCChannelContact> it(m_channels); it.current(); ++it )
+ {
+ if( it.current()->manager(Kopete::Contact::CannotCreate) )
+ {
+ if( contact == m_mySelf )
+ retVal.push_back( it.current() );
+ else
+ {
+ bool c = true;
+
+ Kopete::ContactPtrList members = it.current()->manager()->members();
+ for( QPtrListIterator<Kopete::Contact> it2( members ); c && it2.current(); ++it2 )
+ {
+ if( it2.current() == contact )
+ {
+ retVal.push_back( it.current() );
+ c = false;
+ }
+ }
+ }
+ }
+ }
+
+ return retVal;
+}
+
+IRCChannelContact *IRCContactManager::findChannel(const QString &name, Kopete::MetaContact *m)
+{
+ IRCChannelContact *channel = m_channels[ name ];
+
+ if ( !channel )
+ {
+ if( !m )
+ {
+ m = new Kopete::MetaContact();
+ m->setTemporary( true );
+ }
+
+ channel = new IRCChannelContact(this, name, m);
+ m_channels.insert( name, channel );
+ QObject::connect(channel, SIGNAL(contactDestroyed(Kopete::Contact *)),
+ this, SLOT(unregister(Kopete::Contact *)));
+ }
+
+ return channel;
+}
+
+IRCChannelContact *IRCContactManager::existChannel( const QString &channel ) const
+{
+ return m_channels[ channel ];
+}
+
+void IRCContactManager::unregisterChannel(Kopete::Contact *contact, bool force )
+{
+ IRCChannelContact *channel = (IRCChannelContact*)contact;
+ if( force || (
+ channel!=0 &&
+ !channel->isChatting() &&
+ channel->metaContact()->isTemporary() ) )
+ {
+ m_channels.remove( channel->nickName() );
+ }
+}
+
+IRCUserContact *IRCContactManager::findUser(const QString &name, Kopete::MetaContact *m)
+{
+ IRCUserContact *user = m_users[name.section('!', 0, 0)];
+
+ if ( !user )
+ {
+ if( !m )
+ {
+ m = new Kopete::MetaContact();
+ m->setTemporary( true );
+ }
+
+ user = new IRCUserContact(this, name, m);
+ m_users.insert( name, user );
+ QObject::connect(user, SIGNAL(contactDestroyed(Kopete::Contact *)),
+ this, SLOT(unregister(Kopete::Contact *)));
+ }
+
+ return user;
+}
+
+IRCUserContact *IRCContactManager::existUser( const QString &user ) const
+{
+ return m_users[user];
+}
+
+IRCContact *IRCContactManager::findContact( const QString &id, Kopete::MetaContact *m )
+{
+ if( KIRC::Entity::isChannel(id) )
+ return findChannel( id, m );
+ else
+ return findUser( id, m );
+}
+
+IRCContact *IRCContactManager::existContact( const KIRC::Engine *engine, const QString &id )
+{
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( IRCProtocol::protocol() );
+ QDictIterator<Kopete::Account> it(accounts);
+ for( ; it.current(); ++it )
+ {
+ IRCAccount *account = (IRCAccount *)it.current();
+ if( account && account->engine() == engine )
+ return account->contactManager()->existContact(id);
+ }
+ return 0L;
+}
+
+IRCContact *IRCContactManager::existContact( const QString &id ) const
+{
+ if( KIRC::Entity::isChannel(id) )
+ return existChannel( id );
+ else
+ return existUser( id );
+}
+
+void IRCContactManager::unregisterUser(Kopete::Contact *contact, bool force )
+{
+ IRCUserContact *user = (IRCUserContact *)contact;
+ if( force || (
+ user!=0 &&
+ user!=mySelf() &&
+ !user->isChatting() &&
+ user->metaContact()->isTemporary() ) )
+ {
+ m_users.remove( user->nickName() );
+ }
+}
+
+void IRCContactManager::slotContactAdded( Kopete::MetaContact *contact )
+{
+ for( QPtrListIterator<Kopete::Contact> it( contact->contacts() ); it.current(); ++it )
+ {
+ if( it.current()->account() == m_account )
+ {
+ addToNotifyList( static_cast<IRCContact*>( it.current() )->nickName() );
+ }
+ }
+}
+
+void IRCContactManager::addToNotifyList(const QString &nick)
+{
+ if (!m_NotifyList.contains(nick.lower()))
+ {
+ m_NotifyList.append(nick);
+ checkOnlineNotifyList();
+ }
+}
+
+void IRCContactManager::removeFromNotifyList(const QString &nick)
+{
+ if (m_NotifyList.contains(nick.lower()))
+ m_NotifyList.remove(nick.lower());
+}
+
+void IRCContactManager::checkOnlineNotifyList()
+{
+ if( m_account->engine()->isConnected() )
+ {
+ isonRecieved = false;
+ m_account->engine()->ison( m_NotifyList );
+ //QTimer::singleShot( socketTimeout, this, SLOT( slotIsonTimeout() ) );
+ }
+}
+
+void IRCContactManager::slotIsonRecieved()
+{
+ isonRecieved = true;
+}
+
+void IRCContactManager::slotIsonTimeout()
+{
+ if( !isonRecieved )
+ m_account->engine()->quit("", true);
+}
+
+#include "irccontactmanager.moc"
diff --git a/kopete/protocols/irc/irccontactmanager.h b/kopete/protocols/irc/irccontactmanager.h
new file mode 100644
index 00000000..4a8ae05f
--- /dev/null
+++ b/kopete/protocols/irc/irccontactmanager.h
@@ -0,0 +1,117 @@
+/*
+ irccontactmanager.h - Manager of IRC Contacts
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCCONTACTMANAGER_H
+#define IRCCONTACTMANAGER_H
+
+#include <qdict.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class IRCContact;
+class IRCAccount;
+
+class IRCServerContact;
+class IRCChannelContact;
+class IRCUserContact;
+
+namespace KIRC
+{
+class Engine;
+}
+
+namespace Kopete
+{
+class Contact;
+class MetaContact;
+}
+
+class KopeteView;
+
+class QTimer;
+
+/**
+ * @author Michel Hermier <michel.hermier@wanadoo.fr>
+ *
+ * This class is the repository for all the reference of the @ref IRCContact childs.
+ * It manage the life cycle of all the @ref IRCServerContact, @ref IRCChannelContact and @ref IRCUserContact objects for the given account.
+ */
+class IRCContactManager
+ : public QObject
+{
+ Q_OBJECT
+
+ public:
+ IRCContactManager(const QString &nickName, IRCAccount *account, const char *name=0);
+
+ IRCAccount *account() const { return m_account; }
+
+ IRCServerContact *myServer() const { return m_myServer; }
+ IRCUserContact *mySelf() const { return m_mySelf; }
+
+ IRCChannelContact *findChannel(const QString &channel, Kopete::MetaContact *m=0);
+ IRCChannelContact *existChannel(const QString &channel) const;
+
+ IRCUserContact *findUser(const QString &nick, Kopete::MetaContact *m=0);
+ IRCUserContact *existUser(const QString &nick) const;
+
+ IRCContact *findContact(const QString &nick, Kopete::MetaContact *m=0);
+ IRCContact *existContact( const QString &id ) const;
+
+ QValueList<IRCChannelContact*> findChannelsByMember( IRCUserContact *contact );
+
+ static IRCContact *existContact(const KIRC::Engine *engine, const QString &nick);
+
+ public slots:
+ void unregister(Kopete::Contact *contact);
+ void unregisterUser(Kopete::Contact *contact, bool force = false );
+ void unregisterChannel(Kopete::Contact *contact, bool force = false );
+
+ void addToNotifyList(const QString &nick);
+ void removeFromNotifyList(const QString &nick);
+ void checkOnlineNotifyList();
+
+ signals:
+ void privateMessage(IRCContact *from, IRCContact *to, const QString &message);
+
+ private slots:
+ void slotNewMessage(const QString &originating, const QString &channel, const QString &message);
+ void slotNewPrivMessage(const QString &originating, const QString &, const QString &message);
+ void slotIsonRecieved();
+ void slotIsonTimeout();
+ void slotNewNickChange(const QString &oldnick, const QString &newnick);
+ void slotContactAdded( Kopete::MetaContact *contact );
+
+ private:
+ QDict<IRCChannelContact> m_channels;
+ QDict<IRCUserContact> m_users;
+
+ IRCAccount *m_account;
+ IRCServerContact *m_myServer;
+ IRCUserContact *m_mySelf;
+
+ QStringList m_NotifyList;
+ QTimer *m_NotifyTimer;
+ bool isonRecieved;
+ int socketTimeout;
+
+ static const QRegExp isChannel;
+};
+
+#endif
+
diff --git a/kopete/protocols/irc/ircguiclient.cpp b/kopete/protocols/irc/ircguiclient.cpp
new file mode 100644
index 00000000..b4c36973
--- /dev/null
+++ b/kopete/protocols/irc/ircguiclient.cpp
@@ -0,0 +1,100 @@
+/*
+ ircguiclient.cpp
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <klocale.h>
+
+#include <kdeversion.h>
+#if KDE_IS_VERSION( 3, 1, 90 )
+ #include <kactioncollection.h>
+#else
+// ------------------------------------------------------------
+// TODO: UGLY HACK, remove when we drop KDE 3.1 compatibility
+#ifdef KDE_NO_COMPAT
+#undef KDE_NO_COMPAT
+#include <kaction.h>
+#define KDE_NO_COMPAT 1
+#endif
+// ------------------------------------------------------------
+#endif
+
+#include <qptrlist.h>
+#include <kdebug.h>
+#include <qdom.h>
+
+#include "kopetechatsession.h"
+#include "kcodecaction.h"
+#include "ircguiclient.h"
+#include "ircaccount.h"
+#include "irccontact.h"
+
+IRCGUIClient::IRCGUIClient( Kopete::ChatSession *parent ) : QObject(parent) , KXMLGUIClient(parent)
+{
+ Kopete::ContactPtrList members = parent->members();
+ if( members.count() > 0 )
+ {
+ m_user = static_cast<IRCContact*>( members.first() );
+
+ /***
+ FIXME: Why doesn't this work???? Have to use DOM hack below now...
+
+ setXMLFile("ircchatui.rc");
+
+ unplugActionList( "irccontactactionlist" );
+ QPtrList<KAction> *actions = m_user->customContextMenuActions( parent );
+ plugActionList( "irccontactactionlist", *actions );
+ delete actions;
+ */
+
+ setXMLFile("ircchatui.rc");
+
+ QDomDocument doc = domDocument();
+ QDomNode menu = doc.documentElement().firstChild().firstChild();
+ QPtrList<KAction> *actions = m_user->customContextMenuActions( parent );
+ if( actions )
+ {
+ for( KAction *a = actions->first(); a; a = actions->next() )
+ {
+ actionCollection()->insert( a );
+ QDomElement newNode = doc.createElement( "Action" );
+ newNode.setAttribute( "name", a->name() );
+ menu.appendChild( newNode );
+ }
+ }
+ else
+ {
+ kdDebug(14120) << k_funcinfo << "Actions == 0" << endl;
+ }
+
+ delete actions;
+
+ setDOMDocument( doc );
+ }
+ else
+ {
+ kdDebug(14120) << k_funcinfo << "Members == 0" << endl;
+ }
+}
+
+IRCGUIClient::~IRCGUIClient()
+{
+}
+
+void IRCGUIClient::slotSelectCodec( const QTextCodec *codec )
+{
+ m_user->setCodec( codec );
+}
+
+#include "ircguiclient.moc"
diff --git a/kopete/protocols/irc/ircguiclient.h b/kopete/protocols/irc/ircguiclient.h
new file mode 100644
index 00000000..b81aa632
--- /dev/null
+++ b/kopete/protocols/irc/ircguiclient.h
@@ -0,0 +1,42 @@
+/*
+ ircguiclient.h
+
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef IRCGUICLIENT_H
+#define IRCGUICLIENT_H
+
+#include <qobject.h>
+#include <kxmlguiclient.h>
+
+namespace Kopete { class ChatSession; }
+class IRCContact;
+
+/**
+ *@author Jason Keirstead
+ */
+class IRCGUIClient : public QObject , public KXMLGUIClient
+{
+ Q_OBJECT
+ public:
+ IRCGUIClient( Kopete::ChatSession *parent = 0 );
+ ~IRCGUIClient();
+
+ private slots:
+ void slotSelectCodec( const QTextCodec *codec );
+
+ private:
+ IRCContact *m_user;
+};
+
+#endif
diff --git a/kopete/protocols/irc/ircnetworks.xml b/kopete/protocols/irc/ircnetworks.xml
new file mode 100644
index 00000000..c743e9e0
--- /dev/null
+++ b/kopete/protocols/irc/ircnetworks.xml
@@ -0,0 +1,1463 @@
+<!DOCTYPE irc-networks>
+<networks>
+ <network>
+ <name>AnyNet</name>
+ <description>AnyNet</description>
+ <servers>
+ <server>
+ <host>irc.anynet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>IRCNet</name>
+ <description>IRCNet</description>
+ <servers>
+ <server>
+ <host>eu.ircnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ircd.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>au.ircnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.stealth.net</host>
+ <port>6660</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>KewlNet</name>
+ <description>KewlNet</description>
+ <servers>
+ <server>
+ <host>irc.kewl.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>la.defense.fr.eu.kewl.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>nanterre.fr.eu.kewl.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>TrekLink</name>
+ <description>TrekLink</description>
+ <servers>
+ <server>
+ <host>neutron.treklink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.treklink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>SlashNET</name>
+ <description>SlashNET</description>
+ <servers>
+ <server>
+ <host>irc.slashnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>area51.slashnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>moo.slashnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>radon.slashnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>devnull.slashnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>NeverNET</name>
+ <description>NeverNET</description>
+ <servers>
+ <server>
+ <host>irc.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>imagine.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>dimension.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>universe.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>wayland.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>forte.nevernet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>CoolChat</name>
+ <description>CoolChat</description>
+ <servers>
+ <server>
+ <host>irc.coolchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>unix.coolchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>south.coolchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>toronto.coolchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>UnderNet</name>
+ <description>UnderNet</description>
+ <servers>
+ <server>
+ <host>us.undernet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>eu.undernet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>MagicStar</name>
+ <description>MagicStar</description>
+ <servers>
+ <server>
+ <host>irc.magicstar.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>PTNet, UNI</name>
+ <description>PTNet, UNI</description>
+ <servers>
+ <server>
+ <host>irc.PTNet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>rccn.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uevora.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>umoderna.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ist.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>aaum.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uc.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ualg.ptnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>madinfo.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>isep.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ua.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ipg.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>isec.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>utad.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>iscte.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ubi.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>FDFNet</name>
+ <description>FDFNet</description>
+ <servers>
+ <server>
+ <host>irc.fdfnet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.eu.fdfnet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>FEFNet</name>
+ <description>FEFNet</description>
+ <servers>
+ <server>
+ <host>irc.fef.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.villagenet.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ggn.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.vendetta.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>KrushNet.Org</name>
+ <description>KrushNet.Org</description>
+ <servers>
+ <server>
+ <host>Jeffersonville.IN.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Auckland.NZ.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Hastings.NZ.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Seattle-R.WA.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Minneapolis.MN.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Cullowhee.NC.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Asheville-R.NC.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>San-Antonio.TX.US.KrushNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AfterNET</name>
+ <description>AfterNET</description>
+ <servers>
+ <server>
+ <host>irc.afternet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ic5.eu.afternet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>baltimore.md.us.afternet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>boston.afternet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>DragonLynk</name>
+ <description>DragonLynk</description>
+ <servers>
+ <server>
+ <host>irc.dragonlynk.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>EFNet</name>
+ <description>EFNet</description>
+ <servers>
+ <server>
+ <host>us.rr.efnet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.arcti.ca</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>eu.rr.efnet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>au.rr.efnet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.efnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.light.se</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.stanford.edu</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.solidstreaming.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>IrcLink</name>
+ <description>IrcLink</description>
+ <servers>
+ <server>
+ <host>irc.irclink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Alesund.no.eu.irclink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Oslo.no.eu.irclink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>frogn.no.eu.irclink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>tonsberg.no.eu.irclink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AstroLINK.Org</name>
+ <description>AstroLINK.Org</description>
+ <servers>
+ <server>
+ <host>irc.astrolink.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>GalaxyNet</name>
+ <description>GalaxyNet</description>
+ <servers>
+ <server>
+ <host>sprynet.us.galaxynet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>atlanta.ga.us.galaxynet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.galaxynet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>SceneNet</name>
+ <description>SceneNet</description>
+ <servers>
+ <server>
+ <host>irc.scene.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.eu.scene.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.us.scene.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>EUIrc</name>
+ <description>EUIrc</description>
+ <servers>
+ <server>
+ <host>irc.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ham.de.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ber.de.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ffm.de.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.bre.de.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.hes.de.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.vie.at.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.inn.at.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.bas.ch.euirc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>RebelChat</name>
+ <description>RebelChat</description>
+ <servers>
+ <server>
+ <host>irc.rebelchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>interquad.rebelchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>rebel.rebelchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>bigcove.rebelchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>ARCNet</name>
+ <description>ARCNet</description>
+ <servers>
+ <server>
+ <host>se1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us2.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us3.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ca1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>de1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>de3.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ch1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>be1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>nl3.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk2.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk3.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>fr1.arcnet.vapor.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>Librenet</name>
+ <description>Librenet</description>
+ <servers>
+ <server>
+ <host>irc.librenet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>famipow.fr.librenet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ielf.fr.librenet.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>SubCultNet</name>
+ <description>SubCultNet</description>
+ <servers>
+ <server>
+ <host>irc.subcult.ch</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.phuncrew.ch</host>
+ <port>6668</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.mgz.ch</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>freenode</name>
+ <description>freenode, a service by Peer-directed Projects Center</description>
+ <servers>
+ <server>
+ <host>irc.kde.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>chat.freenode.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>chat.us.freenode.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>chat.eu.freenode.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>chat.au.freenode.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>OFTC</name>
+ <description>The Open and Free Technology Community</description>
+ <servers>
+ <server>
+ <host>irc.oftc.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ircs.oftc.net</host>
+ <port>9999</port>
+ <useSSL>true</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>XWorld</name>
+ <description>XWorld</description>
+ <servers>
+ <server>
+ <host>Buffalo.NY.US.XWorld.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Minneapolis.MN.US.Xworld.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>PalmSprings.CA.US.XWorld.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Quebec.QC.CA.XWorld.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Rochester.NY.US.XWorld.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Bayern.DE.EU.XWorld.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Chicago.IL.US.XWorld.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>ChatNet</name>
+ <description>ChatNet</description>
+ <servers>
+ <server>
+ <host>US.ChatNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>EU.ChatNet.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>Neohorizon</name>
+ <description>Neohorizon</description>
+ <servers>
+ <server>
+ <host>irc.nhn.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>QChat.net</name>
+ <description>QChat.net</description>
+ <servers>
+ <server>
+ <host>irc.qchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>StarChat</name>
+ <description>StarChat</description>
+ <servers>
+ <server>
+ <host>irc.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>galatea.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>stargate.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>powerzone.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>utopia.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>cairns.starchat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>Infinity-IRC.org</name>
+ <description>Infinity-IRC.org</description>
+ <servers>
+ <server>
+ <host>Atlanta.GA.US.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Babylon.NY.US.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Dewspeak.TX.US.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Sunshine.Ca.US.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>MNC.MD.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>IRC.Infinity-IRC.Org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>HabberNet</name>
+ <description>HabberNet</description>
+ <servers>
+ <server>
+ <host>irc.habber.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>Mellorien</name>
+ <description>Mellorien</description>
+ <servers>
+ <server>
+ <host>Irc.mellorien.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.mellorien.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>eu.mellorien.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>DwarfStarNet</name>
+ <description>DwarfStarNet</description>
+ <servers>
+ <server>
+ <host>IRC.dwarfstar.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>US.dwarfstar.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>EU.dwarfstar.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>AU.dwarfstar.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>ChatJunkiesNet</name>
+ <description>ChatJunkiesNet</description>
+ <servers>
+ <server>
+ <host>irc.xchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.xchat.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>OtherNet</name>
+ <description>OtherNet</description>
+ <servers>
+ <server>
+ <host>irc.othernet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AxeNet</name>
+ <description>AxeNet</description>
+ <servers>
+ <server>
+ <host>irc.axenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>angel.axenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>energy.axenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>python.axenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>unsecurity.org</name>
+ <description>unsecurity.org</description>
+ <servers>
+ <server>
+ <host>irc.unsecurity.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>wc.unsecurity.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>thegift.unsecurity.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>sysgate.unsecurity.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>DALNet</name>
+ <description>DALNet</description>
+ <servers>
+ <server>
+ <host>irc.dal.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.eu.dal.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AustNet</name>
+ <description>AustNet</description>
+ <servers>
+ <server>
+ <host>us.austnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ca.austnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>au.austnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>PTNet, ISP's</name>
+ <description>PTNet, ISP's</description>
+ <servers>
+ <server>
+ <host>irc.PTNet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>rccn.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>EUnet.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>madinfo.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>netc2.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>netc1.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>teleweb.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>netway.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>telepac1.ptnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>services.ptnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>esoterica.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ip-hub.ptnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>telepac1.ptnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>nortenet.PTnet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>NixHelpNet</name>
+ <description>NixHelpNet</description>
+ <servers>
+ <server>
+ <host>irc.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk2.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uk3.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>nl.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>ca.ld.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.co.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.ca.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>us.pa.nixhelp.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>Gamma Force</name>
+ <description>Gamma Force</description>
+ <servers>
+ <server>
+ <host>irc.gammaforce.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>sphinx.or.us.gammaforce.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>monolith.ok.us.gammaforce.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>PTlink</name>
+ <description>PTlink</description>
+ <servers>
+ <server>
+ <host>irc.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>dark.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>uc.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>kungfoo.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>matrix.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>illusion.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>Cibercultura.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>aaia.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>gaesi.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>BuBix.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>montijo.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>queima.PTlink.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>OzNet</name>
+ <description>OzNet</description>
+ <servers>
+ <server>
+ <host>sydney.oz.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>melbourne.oz.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>FoxChat</name>
+ <description>FoxChat</description>
+ <servers>
+ <server>
+ <host>irc.FoxChat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ac6.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>beastie.ac6.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>wild.FoxChat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>roadkill.FoxChat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>slick.FoxChat.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AzzurraNet</name>
+ <description>AzzurraNet</description>
+ <servers>
+ <server>
+ <host>irc.bitchx.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.jnet.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.net36.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.noflyzone.net</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.swappoint.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.azzurra.com</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.leonet.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.libero.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.estranet.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.filmaker.it</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+ <network>
+ <name>AbleNET</name>
+ <description>AbleNET</description>
+ <servers>
+ <server>
+ <host>california.ablenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>amazon.ablenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>agora.ablenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>extreme.ablenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ <server>
+ <host>irc.ablenet.org</host>
+ <port>6667</port>
+ <useSSL>false</useSSL>
+ </server>
+ </servers>
+ </network>
+</networks>
diff --git a/kopete/protocols/irc/ircprotocol.cpp b/kopete/protocols/irc/ircprotocol.cpp
new file mode 100644
index 00000000..176c74d7
--- /dev/null
+++ b/kopete/protocols/irc/ircprotocol.cpp
@@ -0,0 +1,1241 @@
+/*
+ ircprotocol - IRC Protocol
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircaccount.h"
+#include "ircprotocol.h"
+#include "ksparser.h"
+
+#include "ircaddcontactpage.h"
+#include "ircchannelcontact.h"
+#include "irccontactmanager.h"
+
+#include "networkconfig.h"
+#include "channellist.h"
+#include "ircguiclient.h"
+#include "ircusercontact.h"
+#include "irceditaccountwidget.h"
+#include "irctransferhandler.h"
+
+#include "kircengine.h"
+
+#include "kopeteaccountmanager.h"
+#include "kopetecommandhandler.h"
+#include "kopeteglobal.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteview.h"
+#include "kopeteuiglobal.h"
+
+#undef KDE_NO_COMPAT
+#include <kaction.h>
+#include <kcharsets.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kglobal.h>
+#include <kinputdialog.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kuser.h>
+
+#include <qcheckbox.h>
+#include <qdom.h>
+#include <qfile.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qspinbox.h>
+#include <qvalidator.h>
+
+#include <dom/html_element.h>
+#include <unistd.h>
+
+typedef KGenericFactory<IRCProtocol> IRCProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_irc, IRCProtocolFactory( "kopete_irc" ) )
+
+IRCProtocol *IRCProtocol::s_protocol = 0L;
+
+IRCProtocolHandler::IRCProtocolHandler() : Kopete::MimeTypeHandler( false )
+{
+ registerAsProtocolHandler( QString::fromLatin1("irc") );
+}
+
+void IRCProtocolHandler::handleURL( const KURL &url ) const
+{
+ kdDebug(14120) << url << endl;
+ if( !url.isValid() )
+ return;
+
+ unsigned short port = url.port();
+ if( port == 0 )
+ port = 6667;
+
+ QString chan = url.url().section('/',3);
+ if( chan.isEmpty() )
+ return;
+
+ KUser user( getuid() );
+ QString accountId = QString::fromLatin1("%1@%2:%3").arg(
+ user.loginName(),
+ url.host(),
+ QString::number(port)
+ );
+
+ kdDebug(14120) << accountId << endl;
+
+ IRCAccount *newAccount = new IRCAccount( IRCProtocol::protocol(), accountId, chan );
+ newAccount->setNickName( user.loginName() );
+ newAccount->setUserName( user.loginName() );
+ newAccount->connect();
+}
+
+IRCProtocol::IRCProtocol( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Protocol( IRCProtocolFactory::instance(), parent, name ),
+
+ m_ServerStatusOnline(Kopete::OnlineStatus::Online,
+ 100, this, OnlineServer, QString::null, i18n("Online")),
+ m_ServerStatusOffline(Kopete::OnlineStatus::Offline,
+ 90, this, OfflineServer, QString::null, i18n("Offline")),
+
+ m_ChannelStatusOnline(Kopete::OnlineStatus::Online,
+ 80, this, OnlineChannel, QString::null, i18n("Online")),
+ m_ChannelStatusOffline(Kopete::OnlineStatus::Offline,
+ 70, this, OfflineChannel, QString::null, i18n("Offline")),
+
+ m_UserStatusOpVoice(Kopete::OnlineStatus::Online,
+ 60, this, Online | Operator | Voiced, QStringList::split(' ',"irc_voice irc_op"), i18n("Op")),
+ m_UserStatusOpVoiceAway(Kopete::OnlineStatus::Away,
+ 55, this, Online | Operator | Voiced | Away,
+ QStringList::split(' ',"irc_voice irc_op contact_away_overlay"), i18n("Away")),
+
+ m_UserStatusOp(Kopete::OnlineStatus::Online,
+ 50, this, Online | Operator, "irc_op", i18n("Op")),
+ m_UserStatusOpAway(Kopete::OnlineStatus::Away,
+ 45, this, Online | Operator | Away,
+ QStringList::split(' ',"irc_op contact_away_overlay"), i18n("Away")),
+
+ m_UserStatusVoice(Kopete::OnlineStatus::Online,
+ 40, this, Online | Voiced, "irc_voice", i18n("Voice")),
+ m_UserStatusVoiceAway(Kopete::OnlineStatus::Away,
+ 35, this, Online | Voiced | Away,
+ QStringList::split(' ',"irc_voice contact_away_overlay"), i18n("Away")),
+
+ m_UserStatusOnline(Kopete::OnlineStatus::Online,
+ 25, this, Online, QString::null, i18n("Online"), i18n("Online"), Kopete::OnlineStatusManager::Online),
+
+ m_UserStatusAway(Kopete::OnlineStatus::Away,
+ 2, this, Online | Away, "contact_away_overlay",
+ i18n("Away"), i18n("Away"), Kopete::OnlineStatusManager::Away),
+ m_UserStatusConnecting(Kopete::OnlineStatus::Connecting,
+ 1, this, Connecting, "irc_connecting", i18n("Connecting")),
+ m_UserStatusOffline(Kopete::OnlineStatus::Offline,
+ 0, this, Offline, QString::null, i18n("Offline"), i18n("Offline"), Kopete::OnlineStatusManager::Offline),
+
+ m_StatusUnknown(Kopete::OnlineStatus::Unknown,
+ 999, this, 999, "status_unknown", i18n("Status not available")),
+
+ propChannelTopic(QString::fromLatin1("channelTopic"), i18n("Topic"), QString::null, false, true ),
+ propChannelMembers(QString::fromLatin1("channelMembers"), i18n("Members")),
+ propHomepage(QString::fromLatin1("homePage"), i18n("Home Page")),
+ propLastSeen(Kopete::Global::Properties::self()->lastSeen()),
+ propUserInfo(QString::fromLatin1("userInfo"), i18n("IRC User")),
+ propServer(QString::fromLatin1("ircServer"), i18n("IRC Server")),
+ propChannels( QString::fromLatin1("ircChannels"), i18n("IRC Channels")),
+ propHops(QString::fromLatin1("ircHops"), i18n("IRC Hops")),
+ propFullName(QString::fromLatin1("FormattedName"), i18n("Full Name")),
+ propIsIdentified(QString::fromLatin1("identifiedUser"), i18n("User Is Authenticated"))
+{
+// kdDebug(14120) << k_funcinfo << endl;
+
+ s_protocol = this;
+
+ //m_status = m_unknownStatus = m_Unknown;
+
+ addAddressBookField("messaging/irc", Kopete::Plugin::MakeIndexField);
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("raw"),
+ SLOT( slotRawCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /raw <text> - Sends the text in raw form to the server."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quote"),
+ SLOT( slotQuoteCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /quote <text> - Sends the text in quoted form to the server."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ctcp"),
+ SLOT( slotCtcpCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /ctcp <nick> <message> - Send the CTCP message to nick<action>."), 2 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ping"),
+ SLOT( slotPingCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /ping <nickname> - Alias for /CTCP <nickname> PING."), 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("motd"),
+ SLOT( slotMotdCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /motd [<server>] - Shows the message of the day for the current or the given server.") );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("list"),
+ SLOT( slotListCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /list - List the public channels on the server.") );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("join"),
+ SLOT( slotJoinCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /join <#channel 1> [<password>] - Joins the specified channel."), 1, 2 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("topic"),
+ SLOT( slotTopicCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /topic [<topic>] - Sets and/or displays the topic for the active channel.") );
+
+ //FIXME: Update help text
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whois"),
+ SLOT( slotWhoisCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /whois <nickname> - Display whois info on this user."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("whowas"),
+ SLOT( slotWhoWasCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /whowas <nickname> - Display whowas info on this user."), 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("who"),
+ SLOT( slotWhoCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /who <nickname|channel> - Display who info on this user/channel."), 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("query"),
+ SLOT( slotQueryCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /query <nickname> [<message>] - Open a private chat with this user."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("mode"),
+ SLOT( slotModeCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /mode <channel> <modes> - Set modes on the given channel."), 2 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("nick"),
+ SLOT( slotNickCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /nick <nickname> - Change your nickname to the given one."), 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("me"),
+ SLOT( slotMeCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /me <action> - Do something."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ame"),
+ SLOT( slotAllMeCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /ame <action> - Do something in every open chat."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("kick"),
+ SLOT( slotKickCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /kick <nickname> [<reason>] - Kick someone from the channel (requires operator status).")
+ , 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("ban"),
+ SLOT( slotBanCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /ban <mask> - Add someone to this channel's ban list. (requires operator status)."),
+ 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("bannick"),
+ QString::fromLatin1("ban %1!*@*"),
+ i18n("USAGE: /bannick <nickname> - Add someone to this channel's ban list. Uses the hostmask nickname!*@* (requires operator status)."), Kopete::CommandHandler::SystemAlias, 1, 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("op"),
+ SLOT( slotOpCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /op <nickname 1> [<nickname 2> <...>] - Give channel operator status to someone (requires operator status)."),
+ 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("deop"),
+ SLOT( slotDeopCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /deop <nickname> [<nickname 2> <...>]- Remove channel operator status from someone (requires operator status)."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("voice"),
+ SLOT( slotVoiceCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /voice <nickname> [<nickname 2> <...>]- Give channel voice status to someone (requires operator status)."),
+ 1);
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("devoice"),
+ SLOT( slotDevoiceCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /devoice <nickname> [<nickname 2> <...>]- Remove channel voice status from someone (requires operator status)."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("quit"),
+ SLOT( slotQuitCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /quit [<reason>] - Disconnect from IRC, optionally leaving a message.") );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("part"),
+ SLOT( slotPartCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /part [<reason>] - Part from a channel, optionally leaving a message.") );
+
+ Kopete::CommandHandler::commandHandler()->registerCommand( this, QString::fromLatin1("invite"),
+ SLOT( slotInviteCommand( const QString &, Kopete::ChatSession*) ),
+ i18n("USAGE: /invite <nickname> [<channel>] - Invite a user to join a channel."), 1 );
+
+ Kopete::CommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("j"),
+ QString::fromLatin1("join %1"),
+ i18n("USAGE: /j <#channel 1> [<password>] - Alias for JOIN."), Kopete::CommandHandler::SystemAlias,
+ 1, 2 );
+
+ Kopete::CommandHandler::commandHandler()->registerAlias( this, QString::fromLatin1("msg"),
+ QString::fromLatin1("query %s"),
+ i18n("USAGE: /msg <nickname> [<message>] - Alias for QUERY <nickname> <message>."), Kopete::CommandHandler::SystemAlias, 1 );
+
+ QObject::connect( Kopete::ChatSessionManager::self(), SIGNAL(aboutToDisplay(Kopete::Message &)),
+ this, SLOT(slotMessageFilter(Kopete::Message &)) );
+
+ QObject::connect( Kopete::ChatSessionManager::self(), SIGNAL( viewCreated( KopeteView* ) ),
+ this, SLOT( slotViewCreated( KopeteView* ) ) );
+
+ setCapabilities( Kopete::Protocol::RichBFormatting | Kopete::Protocol::RichUFormatting | Kopete::Protocol::RichColor );
+
+ netConf = 0L;
+
+ slotReadNetworks();
+
+ m_protocolHandler = new IRCProtocolHandler();
+
+ IRCTransferHandler::self(); // Initiate the transfer handling system.
+}
+
+IRCProtocol * IRCProtocol::protocol()
+{
+ return s_protocol;
+}
+
+IRCProtocol::~IRCProtocol()
+{
+ delete m_protocolHandler;
+}
+
+const Kopete::OnlineStatus IRCProtocol::statusLookup( IRCStatus status ) const
+{
+ kdDebug(14120) << k_funcinfo << "Looking up status for " << status << endl;
+
+ switch( status )
+ {
+ case Offline:
+ return m_UserStatusOffline;
+ case Connecting:
+ return m_UserStatusConnecting;
+
+ // Regular user
+ case Online:
+ return m_UserStatusOnline;
+ case Online | Away:
+ return m_UserStatusAway;
+
+ // Voiced
+ case Online | Voiced:
+ return m_UserStatusVoice;
+ case Online | Away | Voiced:
+ return m_UserStatusVoiceAway;
+
+ // Operator
+ case Online | Operator:
+ return m_UserStatusOp;
+ case Online | Away | Operator:
+ return m_UserStatusOpAway;
+ case Online | Operator | Voiced:
+ return m_UserStatusOpVoice;
+ case Online | Operator | Voiced | Away:
+ return m_UserStatusOpVoiceAway;
+
+ // Server
+ case OnlineServer:
+ return m_ServerStatusOnline;
+ case OfflineServer:
+ return m_ServerStatusOffline;
+
+ // Channel
+ case OnlineChannel:
+ return m_ChannelStatusOnline;
+ case OfflineChannel:
+ return m_ChannelStatusOffline;
+
+ default:
+ return m_StatusUnknown;
+ }
+}
+
+void IRCProtocol::slotViewCreated( KopeteView *view )
+{
+ if( view->msgManager()->protocol() == this )
+ new IRCGUIClient( view->msgManager() );
+}
+
+void IRCProtocol::slotMessageFilter( Kopete::Message &msg )
+{
+ if( msg.from()->protocol() == this )
+ {
+ QString messageText = msg.escapedBody();
+
+ //Add right click for channels, only replace text not in HTML tags
+ messageText.replace( QRegExp( QString::fromLatin1("(?![^<]+>)(#[^#\\s]+)(?![^<]+>)") ), QString::fromLatin1("<span class=\"KopeteLink\" type=\"IRCChannel\">\\1</span>") );
+
+ msg.setBody( messageText, Kopete::Message::RichText );
+ }
+}
+
+QPtrList<KAction> *IRCProtocol::customChatWindowPopupActions( const Kopete::Message &m, DOM::Node &n )
+{
+ DOM::HTMLElement e = n;
+
+ //isNull checks that the cast was successful
+ if( !e.isNull() && !m.to().isEmpty() )
+ {
+ activeNode = n;
+ activeAccount = static_cast<IRCAccount*>( m.from()->account() );
+ if( e.getAttribute( QString::fromLatin1("type") ) == QString::fromLatin1("IRCChannel") )
+ return activeAccount->contactManager()->findChannel(
+ e.innerText().string() )->customContextMenuActions();
+ }
+
+ return 0L;
+}
+
+AddContactPage *IRCProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account)
+{
+ return new IRCAddContactPage(parent,static_cast<IRCAccount*>(account));
+}
+
+KopeteEditAccountWidget *IRCProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new IRCEditAccountWidget(this, static_cast<IRCAccount*>(account),parent);
+}
+
+Kopete::Account *IRCProtocol::createNewAccount(const QString &accountId)
+{
+ return new IRCAccount( this, accountId );
+}
+
+Kopete::Contact *IRCProtocol::deserializeContact( Kopete::MetaContact *metaContact, const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ QString contactId = serializedData[ "contactId" ];
+ QString displayName = serializedData[ "displayName" ];
+
+ if( displayName.isEmpty() )
+ displayName = contactId;
+
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
+ if( !accounts.isEmpty() )
+ {
+ Kopete::Account *a = accounts[ serializedData[ "accountId" ] ];
+ if( a )
+ {
+ a->addContact( contactId, metaContact );
+ return a->contacts()[contactId];
+ }
+ else
+ kdDebug(14120) << k_funcinfo << serializedData[ "accountId" ] << " was a contact's account,"
+ " but we don't have it in the accounts list" << endl;
+ }
+ else
+ kdDebug(14120) << k_funcinfo << "No accounts loaded!" << endl;
+
+ return 0;
+}
+
+void IRCProtocol::slotRawCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ IRCAccount *account = static_cast<IRCAccount*>( manager->account() );
+
+ if (!args.isEmpty())
+ {
+ account->engine()->writeRawMessage(args);
+ }
+ else
+ {
+ account->appendMessage(i18n("You must enter some text to send to the server."),
+ IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotQuoteCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ IRCAccount *account = static_cast<IRCAccount*>( manager->account() );
+
+ if( !args.isEmpty() )
+ {
+ account->engine()->writeMessage( args );
+ }
+ else
+ {
+ account->appendMessage(i18n("You must enter some text to send to the server."),
+ IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotCtcpCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ if( !args.isEmpty() )
+ {
+ QString user = args.section( ' ', 0, 0 );
+ QString message = args.section( ' ', 1 );
+ static_cast<IRCAccount*>( manager->account() )->engine()->writeCtcpQueryMessage( user, QString::null, message );
+ }
+}
+
+void IRCProtocol::slotMotdCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ static_cast<IRCAccount*>( manager->account() )->engine()->motd(argsList.front());
+}
+
+void IRCProtocol::slotPingCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments(args);
+ static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_ping(argsList.front());
+}
+
+void IRCProtocol::slotListCommand( const QString &/*args*/, Kopete::ChatSession *manager )
+{
+ static_cast<IRCAccount*>( manager->account() )->listChannels();
+}
+
+void IRCProtocol::slotTopicCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ Kopete::ContactPtrList members = manager->members();
+ IRCChannelContact *chan = dynamic_cast<IRCChannelContact*>( members.first() );
+ if( chan )
+ {
+ if( !args.isEmpty() )
+ chan->setTopic( args );
+ else
+ {
+ static_cast<IRCAccount*>(manager->account())->engine()->
+ writeRawMessage(QString::fromLatin1("TOPIC %1").arg(chan->nickName()));
+ }
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be in a channel to use this command."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotJoinCommand( const QString &arg, Kopete::ChatSession *manager )
+{
+ QStringList args = Kopete::CommandHandler::parseArguments( arg );
+ if( KIRC::Entity::isChannel(args[0]) )
+ {
+ IRCChannelContact *chan = static_cast<IRCAccount*>( manager->account() )->contactManager()->findChannel( args[0] );
+ if( args.count() == 2 )
+ chan->setPassword( args[1] );
+ static_cast<IRCAccount*>( manager->account() )->engine()->join(args[0], chan->password());
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.")
+ .arg(args[0]), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotInviteCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ IRCChannelContact *c = 0L;
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+
+ if( argsList.count() > 1 )
+ {
+ if( KIRC::Entity::isChannel(argsList[1]) )
+ {
+ c = static_cast<IRCAccount*>( manager->account() )->contactManager()->
+ findChannel( argsList[1] );
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("\"%1\" is an invalid channel. Channels must start with '#', '!', '+', or '&'.")
+ .arg(argsList[1]), IRCAccount::ErrorReply );
+ }
+ }
+ else
+ {
+ Kopete::ContactPtrList members = manager->members();
+ c = dynamic_cast<IRCChannelContact*>( members.first() );
+ }
+
+ if( c && c->manager()->contactOnlineStatus( manager->myself() ) == m_UserStatusOp )
+ {
+ static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
+ QString::fromLatin1("INVITE %1 %2").arg( argsList[0] ).
+ arg( c->nickName() )
+ );
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotQueryCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QString user = args.section( ' ', 0, 0 );
+ QString rest = args.section( ' ', 1 );
+
+ if( !KIRC::Entity::isChannel(user) )
+ {
+ IRCUserContact *c = static_cast<IRCAccount*>( manager->account() )->
+ contactManager()->findUser( user );
+ c->startChat();
+ if( !rest.isEmpty() )
+ {
+ Kopete::Message msg( c->manager()->myself(), c->manager()->members(), rest,
+ Kopete::Message::Outbound, Kopete::Message::PlainText, CHAT_VIEW);
+ c->manager()->sendMessage(msg);
+ }
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("\"%1\" is an invalid nickname. Nicknames must not start with '#','!','+', or '&'.").arg(user),
+ IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotWhoisCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ static_cast<IRCAccount*>( manager->account() )->engine()->whois( args );
+ static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
+}
+
+void IRCProtocol::slotWhoCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
+ QString::fromLatin1("WHO %1").arg( argsList.first() ) );
+ static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
+}
+
+void IRCProtocol::slotWhoWasCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ static_cast<IRCAccount*>( manager->account() )->engine()->writeMessage(
+ QString::fromLatin1("WHOWAS %1").arg( argsList.first() ) );
+ static_cast<IRCAccount*>( manager->account() )->setCurrentCommandSource( manager );
+}
+
+void IRCProtocol::slotQuitCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ static_cast<IRCAccount*>( manager->account() )->quit( args );
+}
+
+void IRCProtocol::slotNickCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ static_cast<IRCAccount*>( manager->account() )->engine()->nick( argsList.front() );
+}
+
+void IRCProtocol::slotModeCommand(const QString &args, Kopete::ChatSession *manager)
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ static_cast<IRCAccount*>( manager->account() )->engine()->mode( argsList.front(),
+ args.section( QRegExp(QString::fromLatin1("\\s+")), 1 ) );
+}
+
+void IRCProtocol::slotMeCommand(const QString &args, Kopete::ChatSession *manager)
+{
+ Kopete::ContactPtrList members = manager->members();
+ static_cast<IRCAccount*>( manager->account() )->engine()->CtcpRequest_action(
+ static_cast<const IRCContact*>(members.first())->nickName(), args
+ );
+}
+
+void IRCProtocol::slotAllMeCommand(const QString &args, Kopete::ChatSession *)
+{
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+
+ for( QValueList<Kopete::ChatSession*>::iterator it = sessions.begin(); it != sessions.end(); ++it )
+ {
+ Kopete::ChatSession *session = *it;
+ if( session->protocol() == this )
+ slotMeCommand(args, session);
+ }
+}
+
+void IRCProtocol::slotKickCommand(const QString &args, Kopete::ChatSession *manager)
+{
+ if (manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp)
+ {
+ QRegExp spaces(QString::fromLatin1("\\s+"));
+ QString nick = args.section( spaces, 0, 0);
+ QString reason = args.section( spaces, 1);
+ Kopete::ContactPtrList members = manager->members();
+ QString channel = static_cast<IRCContact*>( members.first() )->nickName();
+ if (KIRC::Entity::isChannel(channel))
+ static_cast<IRCAccount*>(manager->account())->engine()->kick(nick, channel, reason);
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotBanCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ if( manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp )
+ {
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ Kopete::ContactPtrList members = manager->members();
+ IRCChannelContact *chan = static_cast<IRCChannelContact*>( members.first() );
+ if( chan && chan->locateUser( argsList.front() ) )
+ chan->setMode( QString::fromLatin1("+b %1").arg( argsList.front() ) );
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotPartCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ QStringList argsList = Kopete::CommandHandler::parseArguments(args);
+ Kopete::ContactPtrList members = manager->members();
+ IRCChannelContact *chan = static_cast<IRCChannelContact*>(members.first());
+
+ if (chan)
+ {
+ if(!args.isEmpty())
+ static_cast<IRCAccount*>(manager->account())->engine()->part(chan->nickName(), args);
+ else
+ chan->part();
+ if( manager->view() )
+ manager->view()->closeView(true);
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be in a channel to use this command."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::slotOpCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ simpleModeChange( args, manager, QString::fromLatin1("+o") );
+}
+
+void IRCProtocol::slotDeopCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ simpleModeChange( args, manager, QString::fromLatin1("-o") );
+}
+
+void IRCProtocol::slotVoiceCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ simpleModeChange( args, manager, QString::fromLatin1("+v") );
+}
+
+void IRCProtocol::slotDevoiceCommand( const QString &args, Kopete::ChatSession *manager )
+{
+ simpleModeChange( args, manager, QString::fromLatin1("-v") );
+}
+
+void IRCProtocol::simpleModeChange( const QString &args, Kopete::ChatSession *manager, const QString &mode )
+{
+ if( manager->contactOnlineStatus( manager->myself() ) == m_UserStatusOp )
+ {
+ QStringList argsList = Kopete::CommandHandler::parseArguments( args );
+ Kopete::ContactPtrList members = manager->members();
+ IRCChannelContact *chan = static_cast<IRCChannelContact*>( members.first() );
+ if( chan )
+ {
+ for( QStringList::iterator it = argsList.begin(); it != argsList.end(); ++it )
+ {
+ if( chan->locateUser( *it ) )
+ chan->setMode( QString::fromLatin1("%1 %2").arg( mode ).arg( *it ) );
+ }
+ }
+ }
+ else
+ {
+ static_cast<IRCAccount*>( manager->account() )->appendMessage(
+ i18n("You must be a channel operator to perform this operation."), IRCAccount::ErrorReply );
+ }
+}
+
+void IRCProtocol::editNetworks( const QString &networkName )
+{
+ if( !netConf )
+ {
+ netConf = new NetworkConfig( Kopete::UI::Global::mainWidget(), "network_config", true );
+ netConf->host->setValidator( new QRegExpValidator( QString::fromLatin1("^[\\w-\\.]*$"), netConf ) );
+ netConf->upButton->setIconSet( SmallIconSet( "up" ) );
+ netConf->downButton->setIconSet( SmallIconSet( "down" ) );
+
+ connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ connect( netConf, SIGNAL( accepted() ), this, SLOT( slotSaveNetworkConfig() ) );
+ connect( netConf, SIGNAL( rejected() ), this, SLOT( slotReadNetworks() ) );
+ connect( netConf->upButton, SIGNAL( clicked() ), this, SLOT( slotMoveServerUp() ) );
+ connect( netConf->downButton, SIGNAL( clicked() ), this, SLOT( slotMoveServerDown() ) );
+ connect( netConf->removeNetwork, SIGNAL( clicked() ), this, SLOT( slotDeleteNetwork() ) );
+ connect( netConf->removeHost, SIGNAL( clicked() ), this, SLOT( slotDeleteHost() ) );
+ connect( netConf->newHost, SIGNAL( clicked() ), this, SLOT( slotNewHost() ) );
+ connect( netConf->newNetwork, SIGNAL( clicked() ), this, SLOT( slotNewNetwork() ) );
+ connect( netConf->renameNetwork, SIGNAL( clicked() ), this, SLOT( slotRenameNetwork() ) );
+ connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
+ connect( netConf->networkList, SIGNAL( doubleClicked ( QListBoxItem * )), SLOT(slotRenameNetwork()));
+
+ }
+
+ disconnect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
+ disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+
+ netConf->networkList->clear();
+
+ for( QDictIterator<IRCNetwork> it( m_networks ); it.current(); ++it )
+ {
+ IRCNetwork *net = it.current();
+ netConf->networkList->insertItem( net->name );
+ }
+
+ netConf->networkList->sort();
+
+ connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+
+ if( !networkName.isEmpty() )
+ netConf->networkList->setSelected( netConf->networkList->findItem( networkName ), true );
+
+ //slotUpdateNetworkConfig(); // unnecessary, setSelected emits selectionChanged
+
+ netConf->show();
+}
+
+void IRCProtocol::slotUpdateNetworkConfig()
+{
+ // update the data structure of the previous selection from the UI
+ storeCurrentNetwork();
+
+ // update the UI from the data for the current selection
+ IRCNetwork *net = m_networks[ netConf->networkList->currentText() ];
+ if( net )
+ {
+ netConf->description->setText( net->description );
+ netConf->hostList->clear();
+
+ for( QValueList<IRCHost*>::iterator it = net->hosts.begin(); it != net->hosts.end(); ++it )
+ netConf->hostList->insertItem( (*it)->host + QString::fromLatin1(":") + QString::number((*it)->port) );
+
+ // prevent nested event loop crash
+ disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ netConf->hostList->setSelected( 0, true );
+ slotUpdateNetworkHostConfig();
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ }
+
+ // record the current selection
+ m_uiCurrentNetworkSelection = netConf->networkList->currentText();
+}
+
+void IRCProtocol::storeCurrentNetwork()
+{
+ if ( !m_uiCurrentNetworkSelection.isEmpty() )
+ {
+ IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
+ if ( net )
+ {
+ net->description = netConf->description->text(); // crash on 2nd dialog show here!
+ }
+ else
+ kdDebug( 14120 ) << m_uiCurrentNetworkSelection << " was already gone from the cache!" << endl;
+ }
+}
+
+void IRCProtocol::storeCurrentHost()
+{
+ if ( !m_uiCurrentHostSelection.isEmpty() )
+ {
+ IRCHost *host = m_hosts[ m_uiCurrentHostSelection ];
+ if ( host )
+ {
+ host->host = netConf->host->text();
+ host->password = netConf->password->text();
+ host->port = netConf->port->text().toInt();
+ host->ssl = netConf->useSSL->isChecked();
+ }
+ }
+}
+
+void IRCProtocol::slotHostPortChanged( int value )
+{
+ QString entryText = m_uiCurrentHostSelection + QString::fromLatin1(":") + QString::number( value );
+ // changeItem causes a take() and insert, and we don't want a selectionChanged() signal that sets all this off again.
+ disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ netConf->hostList->changeItem( entryText, netConf->hostList->currentItem() );
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+}
+
+void IRCProtocol::slotUpdateNetworkHostConfig()
+{
+ storeCurrentHost();
+
+ if ( netConf->hostList->selectedItem() )
+ {
+ m_uiCurrentHostSelection = netConf->hostList->currentText().section(':', 0, 0);
+ IRCHost *host = m_hosts[ m_uiCurrentHostSelection ];
+
+ if( host )
+ {
+ netConf->host->setText( host->host );
+ netConf->password->setText( host->password );
+ disconnect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
+ netConf->port->setValue( host->port );
+ connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
+ netConf->useSSL->setChecked( host->ssl );
+
+ netConf->upButton->setEnabled( netConf->hostList->currentItem() > 0 );
+ netConf->downButton->setEnabled( netConf->hostList->currentItem() < (int)( netConf->hostList->count() - 1 ) );
+ }
+ }
+ else
+ {
+ m_uiCurrentHostSelection = QString();
+ disconnect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
+ netConf->host->clear();
+ netConf->password->clear();
+ netConf->port->setValue( 6667 );
+ netConf->useSSL->setChecked( false );
+ connect( netConf->port, SIGNAL( valueChanged( int ) ), this, SLOT( slotHostPortChanged( int ) ) );
+ }
+}
+
+void IRCProtocol::slotDeleteNetwork()
+{
+ QString network = netConf->networkList->currentText();
+ if( KMessageBox::warningContinueCancel(
+ Kopete::UI::Global::mainWidget(), i18n("<qt>Are you sure you want to delete the network <b>%1</b>?<br>"
+ "Any accounts which use this network will have to be modified.</qt>")
+ .arg(network), i18n("Deleting Network"),
+ KGuiItem(i18n("&Delete Network"),"editdelete"), QString::fromLatin1("AskIRCDeleteNetwork") ) == KMessageBox::Continue )
+ {
+ disconnect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
+ disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ IRCNetwork *net = m_networks[ network ];
+ for( QValueList<IRCHost*>::iterator it = net->hosts.begin(); it != net->hosts.end(); ++it )
+ {
+ m_hosts.remove( (*it)->host );
+ delete (*it);
+ }
+ m_networks.remove( network );
+ delete net;
+ netConf->networkList->removeItem( netConf->networkList->currentItem() );
+ connect( netConf->networkList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkConfig() ) );
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ slotUpdateNetworkHostConfig();
+
+ }
+}
+
+void IRCProtocol::slotDeleteHost()
+{
+ QString hostName = netConf->host->text();
+ if ( KMessageBox::warningContinueCancel(
+ Kopete::UI::Global::mainWidget(), i18n("<qt>Are you sure you want to delete the host <b>%1</b>?</qt>")
+ .arg(hostName), i18n("Deleting Host"),
+ KGuiItem(i18n("&Delete Host"),"editdelete"), QString::fromLatin1("AskIRCDeleteHost")) == KMessageBox::Continue )
+ {
+ IRCHost *host = m_hosts[ hostName ];
+ if ( host )
+ {
+ disconnect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+ QString entryText = host->host + QString::fromLatin1(":") + QString::number(host->port);
+ QListBoxItem * justAdded = netConf->hostList->findItem( entryText );
+ netConf->hostList->removeItem( netConf->hostList->index( justAdded ) );
+ connect( netConf->hostList, SIGNAL( selectionChanged() ), this, SLOT( slotUpdateNetworkHostConfig() ) );
+
+ // remove from network as well
+ IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
+ net->hosts.remove( host );
+
+ m_hosts.remove( host->host );
+ delete host;
+ }
+ }
+}
+
+void IRCProtocol::slotNewNetwork()
+{
+ // create a new network struct
+ IRCNetwork *net = new IRCNetwork;
+ // give it the name of 'New Network' (incrementing number if needed)
+ QString netName = QString::fromLatin1( "New Network" );
+ if ( m_networks.find( netName ) )
+ {
+ int newIdx = 1;
+ do {
+ netName = QString::fromLatin1( "New Network #%1" ).arg( newIdx++ );
+ }
+ while ( m_networks.find( netName ) && newIdx < 100 );
+ if ( newIdx == 100 ) // pathological case
+ return;
+ }
+ net->name = netName;
+ // and add it to the networks dict and list
+ m_networks.insert( net->name, net );
+ netConf->networkList->insertItem( net->name );
+ QListBoxItem * justAdded = netConf->networkList->findItem( net->name );
+ netConf->networkList->setSelected( justAdded, true );
+ netConf->networkList->setBottomItem( netConf->networkList->index( justAdded ) );
+}
+
+void IRCProtocol::slotNewHost()
+{
+ // create a new host
+ IRCHost *host = new IRCHost;
+ // prompt for a name
+ bool ok;
+ QString name = KInputDialog::getText(
+ i18n("New Host"),
+ i18n("Enter the hostname of the new server:"),
+ QString::null, &ok, Kopete::UI::Global::mainWidget() );
+ if ( ok )
+ {
+ // dupe check
+ if ( m_hosts[ name ] )
+ {
+ KMessageBox::sorry(netConf, i18n( "A host already exists with that name" ) );
+ return;
+ }
+ // set defaults on others
+ host->host = name;
+ host->port = 6667;
+ host->ssl = false;
+ // add it to the dict
+ m_hosts.insert( host->host, host );
+ // add it to the network!
+ IRCNetwork *net = m_networks[ netConf->networkList->currentText() ];
+ net->hosts.append( host );
+ // add it to the gui
+ QString entryText = host->host + QString::fromLatin1(":") + QString::number(host->port);
+ netConf->hostList->insertItem( entryText );
+ // select it in the gui
+ QListBoxItem * justAdded = netConf->hostList->findItem( entryText );
+ netConf->hostList->setSelected( justAdded, true );
+ //netConf->hostList->setBottomItem( netConf->hostList->index( justAdded ) );
+ }
+}
+
+void IRCProtocol::slotRenameNetwork()
+{
+ IRCNetwork *net = m_networks[ m_uiCurrentNetworkSelection ];
+ if ( net )
+ {
+ bool ok;
+ // popup up a dialog containing the current name
+ QString name = KInputDialog::getText(
+ i18n("Rename Network"),
+ i18n("Enter the new name for this network:"),
+ m_uiCurrentNetworkSelection, &ok,
+ Kopete::UI::Global::mainWidget() );
+ if ( ok )
+ {
+ if ( m_uiCurrentNetworkSelection != name )
+ {
+ // dupe check
+ if ( m_networks[ name ] )
+ {
+ KMessageBox::sorry(netConf, i18n( "A network already exists with that name" ) );
+ return;
+ }
+
+ net->name = name;
+ // dict
+ m_networks.remove( m_uiCurrentNetworkSelection );
+ m_networks.insert( net->name, net );
+ // ui
+ int idx = netConf->networkList->index( netConf->networkList->findItem( m_uiCurrentNetworkSelection ) );
+ m_uiCurrentNetworkSelection = net->name;
+ netConf->networkList->changeItem( net->name, idx ); // changes the selection!!!
+ netConf->networkList->sort();
+ }
+ }
+ }
+}
+
+void IRCProtocol::addNetwork( IRCNetwork *network )
+{
+ m_networks.insert( network->name, network );
+ slotSaveNetworkConfig();
+}
+
+void IRCProtocol::slotSaveNetworkConfig()
+{
+ // store any changes in the UI
+ storeCurrentNetwork();
+ kdDebug( 14120 ) << k_funcinfo << m_uiCurrentHostSelection << endl;
+ storeCurrentHost();
+
+ QDomDocument doc("irc-networks");
+ QDomNode root = doc.appendChild( doc.createElement("networks") );
+
+ for( QDictIterator<IRCNetwork> it( m_networks ); it.current(); ++it )
+ {
+ IRCNetwork *net = it.current();
+
+ QDomNode networkNode = root.appendChild( doc.createElement("network") );
+ QDomNode nameNode = networkNode.appendChild( doc.createElement("name") );
+ nameNode.appendChild( doc.createTextNode( net->name ) );
+
+ QDomNode descNode = networkNode.appendChild( doc.createElement("description") );
+ descNode.appendChild( doc.createTextNode( net->description ) );
+
+ QDomNode serversNode = networkNode.appendChild( doc.createElement("servers") );
+
+ for( QValueList<IRCHost*>::iterator it2 = net->hosts.begin(); it2 != net->hosts.end(); ++it2 )
+ {
+ QDomNode serverNode = serversNode.appendChild( doc.createElement("server") );
+
+ QDomNode hostNode = serverNode.appendChild( doc.createElement("host") );
+ hostNode.appendChild( doc.createTextNode( (*it2)->host ) );
+
+ QDomNode portNode = serverNode.appendChild( doc.createElement("port" ) );
+ portNode.appendChild( doc.createTextNode( QString::number( (*it2)->port ) ) );
+
+ QDomNode sslNode = serverNode.appendChild( doc.createElement("useSSL") );
+ sslNode.appendChild( doc.createTextNode( (*it2)->ssl ? "true" : "false" ) );
+ }
+ }
+
+// kdDebug(14121) << k_funcinfo << doc.toString(4) << endl;
+ QFile xmlFile( locateLocal( "appdata", "ircnetworks.xml" ) );
+
+ if (xmlFile.open(IO_WriteOnly))
+ {
+ QTextStream stream(&xmlFile);
+ stream << doc.toString(4);
+ xmlFile.close();
+ }
+ else
+ kdDebug(14121) << k_funcinfo << "Failed to save the Networks definition file" << endl;
+
+ if (netConf)
+ emit networkConfigUpdated( netConf->networkList->currentText() );
+}
+
+void IRCProtocol::slotReadNetworks()
+{
+ m_networks.clear();
+ m_hosts.clear();
+
+ QFile xmlFile( locate( "appdata", "ircnetworks.xml" ) );
+ xmlFile.open( IO_ReadOnly );
+
+ QDomDocument doc;
+ doc.setContent( &xmlFile );
+ QDomElement networkNode = doc.documentElement().firstChild().toElement();
+ while( !networkNode.isNull () )
+ {
+ IRCNetwork *net = new IRCNetwork;
+
+ QDomElement networkChild = networkNode.firstChild().toElement();
+ while( !networkChild.isNull() )
+ {
+ if( networkChild.tagName() == "name" )
+ net->name = networkChild.text();
+ else if( networkChild.tagName() == "description" )
+ net->description = networkChild.text();
+ else if( networkChild.tagName() == "servers" )
+ {
+ QDomElement server = networkChild.firstChild().toElement();
+ while( !server.isNull() )
+ {
+ IRCHost *host = new IRCHost;
+
+ QDomElement serverChild = server.firstChild().toElement();
+ while( !serverChild.isNull() )
+ {
+ if( serverChild.tagName() == "host" )
+ host->host = serverChild.text();
+ else if( serverChild.tagName() == "port" )
+ host->port = serverChild.text().toInt();
+ else if( serverChild.tagName() == "useSSL" )
+ host->ssl = ( serverChild.text() == "true" );
+
+ serverChild = serverChild.nextSibling().toElement();
+ }
+
+ net->hosts.append( host );
+ m_hosts.insert( host->host, host );
+ server = server.nextSibling().toElement();
+ }
+ }
+ networkChild = networkChild.nextSibling().toElement();
+ }
+
+ m_networks.insert( net->name, net );
+ networkNode = networkNode.nextSibling().toElement();
+ }
+
+ xmlFile.close();
+}
+
+void IRCProtocol::slotMoveServerUp()
+{
+ IRCHost *selectedHost = m_hosts[ netConf->hostList->currentText().section(':', 0, 0) ];
+ IRCNetwork *selectedNetwork = m_networks[ netConf->networkList->currentText() ];
+
+ if( !selectedNetwork || !selectedHost )
+ return;
+
+ QValueList<IRCHost*>::iterator pos = selectedNetwork->hosts.find( selectedHost );
+ if( pos != selectedNetwork->hosts.begin() )
+ {
+ QValueList<IRCHost*>::iterator lastPos = pos;
+ lastPos--;
+ selectedNetwork->hosts.insert( lastPos, selectedHost );
+ selectedNetwork->hosts.remove( pos );
+ }
+
+ unsigned int currentPos = netConf->hostList->currentItem();
+ if( currentPos > 0 )
+ {
+ netConf->hostList->removeItem( currentPos );
+ QString entryText = selectedHost->host + QString::fromLatin1(":") + QString::number( selectedHost->port );
+ netConf->hostList->insertItem( entryText, --currentPos );
+ netConf->hostList->setSelected( currentPos, true );
+ }
+}
+
+void IRCProtocol::slotMoveServerDown()
+{
+ IRCHost *selectedHost = m_hosts[ netConf->hostList->currentText().section(':', 0, 0) ];
+ IRCNetwork *selectedNetwork = m_networks[ netConf->networkList->currentText() ];
+
+ if( !selectedNetwork || !selectedHost )
+ return;
+
+ QValueList<IRCHost*>::iterator pos = selectedNetwork->hosts.find( selectedHost );
+ if( *pos != selectedNetwork->hosts.back() )
+ {
+ QValueList<IRCHost*>::iterator nextPos = selectedNetwork->hosts.remove( pos );
+ selectedNetwork->hosts.insert( ++nextPos, selectedHost );
+ }
+
+ unsigned int currentPos = netConf->hostList->currentItem();
+ if( currentPos < ( netConf->hostList->count() - 1 ) )
+ {
+ netConf->hostList->removeItem( currentPos );
+ QString entryText = selectedHost->host + QString::fromLatin1(":") + QString::number( selectedHost->port );
+ netConf->hostList->insertItem( entryText, ++currentPos );
+ netConf->hostList->setSelected( currentPos, true );
+ }
+}
+
+
+
+#include "ircprotocol.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/irc/ircprotocol.h b/kopete/protocols/irc/ircprotocol.h
new file mode 100644
index 00000000..2a1700e5
--- /dev/null
+++ b/kopete/protocols/irc/ircprotocol.h
@@ -0,0 +1,228 @@
+/*
+ ircprotocol.h - IRC Protocol
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCPROTOCOL_H
+#define IRCPROTOCOL_H
+
+#include "kopeteonlinestatus.h"
+#include "kopeteprotocol.h"
+#include "kopetecontactproperty.h"
+#include "kopetemimetypehandler.h"
+
+#include <dom/dom_node.h>
+#include <qdict.h>
+
+#define m_protocol (IRCProtocol::protocol())
+
+namespace Kopete
+{
+class Account;
+class MetaContact;
+}
+
+class AddContactPage;
+
+class EditAccountWidget;
+class IRCAccount;
+
+class QStringList;
+class QWidget;
+class KopeteView;
+
+class IRCNetwork;
+class IRCHost;
+class NetworkConfig;
+
+class IRCProtocolHandler : public Kopete::MimeTypeHandler
+{
+ public:
+
+ IRCProtocolHandler();
+
+ void handleURL( const KURL &url ) const;
+};
+
+static const QString CHAT_VIEW( QString::fromLatin1("kopete_chatwindow") );
+
+/**
+ * @author Nick Betcher <nbetcher@kde.org>
+ */
+class IRCProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ enum IRCStatus
+ {
+ Offline = 1, //! An offline user.
+ Connecting = 2, //! User that is connecting.
+ Away = 4, //! User that is away. May be regular user, voiced user or (server) operator.
+ Online = 8, //! This user is online.
+ Voiced = 16, //! This user is voiced.
+ Operator = 32, //! This user is a channel operator.
+ ServerOperator = 1024, //! This user is a server operator.
+ OfflineChannel = 4096, //! This channel is offline.
+ OnlineChannel = 8192, //! This channel is online.
+ OfflineServer = 16384, //! This server is offline.
+ OnlineServer = 32768 //! This server is online.
+ };
+
+ IRCProtocol( QObject *parent, const char *name, const QStringList &args );
+ ~IRCProtocol();
+
+ /** Kopete::Protocol reimplementation */
+ virtual AddContactPage *createAddContactWidget(QWidget *parent, Kopete::Account *account);
+
+ /**
+ * Deserialize contact data
+ */
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData, const QMap<QString, QString> &addressBookData );
+
+ virtual KopeteEditAccountWidget* createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+
+ virtual Kopete::Account* createNewAccount(const QString &accountId);
+
+ virtual QPtrList<KAction> *customChatWindowPopupActions( const Kopete::Message &, DOM::Node & );
+
+ static IRCProtocol *protocol();
+
+ /**
+ * Maps the given IRC status to Kopete::OnlineStatus.
+ */
+ const Kopete::OnlineStatus statusLookup( IRCStatus status ) const;
+
+ const Kopete::OnlineStatus m_ServerStatusOnline;
+ const Kopete::OnlineStatus m_ServerStatusOffline;
+
+ const Kopete::OnlineStatus m_ChannelStatusOnline;
+ const Kopete::OnlineStatus m_ChannelStatusOffline;
+
+ const Kopete::OnlineStatus m_UserStatusOpVoice;
+ const Kopete::OnlineStatus m_UserStatusOpVoiceAway;
+ const Kopete::OnlineStatus m_UserStatusOp;
+ const Kopete::OnlineStatus m_UserStatusOpAway;
+ const Kopete::OnlineStatus m_UserStatusVoice;
+ const Kopete::OnlineStatus m_UserStatusVoiceAway;
+ const Kopete::OnlineStatus m_UserStatusOnline;
+ const Kopete::OnlineStatus m_UserStatusAway;
+ const Kopete::OnlineStatus m_UserStatusConnecting;
+ const Kopete::OnlineStatus m_UserStatusOffline;
+
+ const Kopete::OnlineStatus m_StatusUnknown;
+
+ // irc channnel-contact properties
+ const Kopete::ContactPropertyTmpl propChannelTopic;
+ const Kopete::ContactPropertyTmpl propChannelMembers;
+ const Kopete::ContactPropertyTmpl propHomepage;
+
+ // irc user-contact properties
+ const Kopete::ContactPropertyTmpl propLastSeen;
+ const Kopete::ContactPropertyTmpl propUserInfo;
+ const Kopete::ContactPropertyTmpl propServer;
+ const Kopete::ContactPropertyTmpl propChannels;
+ const Kopete::ContactPropertyTmpl propHops;
+ const Kopete::ContactPropertyTmpl propFullName;
+ const Kopete::ContactPropertyTmpl propIsIdentified;
+
+ bool commandInProgress(){ return m_commandInProgress; }
+ void setCommandInProgress( bool ip ) { m_commandInProgress = ip; }
+
+ QDict<IRCNetwork> &networks(){ return m_networks; }
+ void addNetwork( IRCNetwork *network );
+
+ void editNetworks( const QString &networkName = QString::null );
+
+signals:
+ void networkConfigUpdated( const QString &selectedNetwork );
+
+private slots:
+ // FIXME: All the code for managing the networks list should be in another class - Will
+ void slotUpdateNetworkConfig();
+ void slotUpdateNetworkHostConfig();
+ void slotMoveServerUp();
+ void slotMoveServerDown();
+ void slotSaveNetworkConfig();
+ void slotReadNetworks();
+ void slotDeleteNetwork();
+ void slotDeleteHost();
+ void slotNewNetwork();
+ void slotRenameNetwork();
+ void slotNewHost();
+ void slotHostPortChanged( int value );
+ // end of network list specific code
+
+ void slotMessageFilter( Kopete::Message &msg );
+
+ void slotRawCommand( const QString &args, Kopete::ChatSession *manager );
+ void slotQuoteCommand( const QString &args, Kopete::ChatSession *manager );
+ void slotCtcpCommand( const QString &args, Kopete::ChatSession *manager );
+ void slotPingCommand( const QString &args, Kopete::ChatSession *manager );
+
+ void slotMotdCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotListCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotTopicCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotJoinCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotNickCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotWhoisCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotWhoWasCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotWhoCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotMeCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotAllMeCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotModeCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotQueryCommand( const QString &args, Kopete::ChatSession *manager);
+
+ void slotKickCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotBanCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotOpCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotDeopCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotVoiceCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotDevoiceCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotQuitCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotPartCommand( const QString &args, Kopete::ChatSession *manager);
+ void slotInviteCommand( const QString &args, Kopete::ChatSession *manager);
+
+ void slotViewCreated( KopeteView * );
+
+private:
+ static IRCProtocol *s_protocol;
+
+ void simpleModeChange( const QString &, Kopete::ChatSession *, const QString &mode );
+
+ // FIXME: All the code for managing the networks list should be in another class - Will
+ void storeCurrentNetwork();
+ void storeCurrentHost();
+
+ NetworkConfig *netConf;
+ QString m_uiCurrentNetworkSelection;
+ QString m_uiCurrentHostSelection;
+ // end of network list specific code
+
+ DOM::Node activeNode;
+ IRCAccount *activeAccount;
+
+ bool m_commandInProgress;
+
+ QDict<IRCNetwork> m_networks;
+ QDict<IRCHost> m_hosts;
+ IRCProtocolHandler *m_protocolHandler;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ircservercontact.cpp b/kopete/protocols/irc/ircservercontact.cpp
new file mode 100644
index 00000000..3ca21643
--- /dev/null
+++ b/kopete/protocols/irc/ircservercontact.cpp
@@ -0,0 +1,220 @@
+/*
+ ircservercontact.cpp - IRC Server Contact
+
+ Copyright (c) 2003 by Michel Hermier <mhermier@kde.org>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircusercontact.h"
+#include "ircservercontact.h"
+#include "ircaccount.h"
+#include "ircprotocol.h"
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qtimer.h>
+
+IRCServerContact::IRCServerContact(IRCContactManager *contactManager, const QString &servername, Kopete::MetaContact *m)
+ : IRCContact(contactManager, servername, m, "irc_server")
+{
+ KIRC::Engine *engine = kircEngine();
+
+ QObject::connect(engine, SIGNAL(internalError(KIRC::Engine::Error, KIRC::Message &)),
+ this, SLOT(engineInternalError(KIRC::Engine::Error, KIRC::Message &)));
+/*
+ //FIXME: Have some kind of a debug option for raw input/ouput display??
+ QObject::connect(engine, SIGNAL(sentMessage(KIRC::Message &)),
+ this, SLOT(engineSentMessage(KIRC::Message &)));
+ QObject::connect(engine, SIGNAL(receivedMessage(KIRC::Message &)),
+ this, SLOT(engineReceivedMessage(KIRC::Message &)));
+*/
+
+ QObject::connect(engine, SIGNAL(incomingNotice(const QString &, const QString &)),
+ this, SLOT(slotIncomingNotice(const QString &, const QString &)));
+
+ QObject::connect(engine, SIGNAL(incomingCannotSendToChannel(const QString &, const QString &)),
+ this, SLOT(slotCannotSendToChannel(const QString &, const QString &)));
+
+ QObject::connect(engine, SIGNAL(incomingUnknown(const QString &)),
+ this, SLOT(slotIncomingUnknown(const QString &)));
+
+ QObject::connect(engine, SIGNAL(incomingConnectString(const QString &)),
+ this, SLOT(slotIncomingConnect(const QString &)));
+
+ QObject::connect(engine, SIGNAL(incomingMotd(const QString &)),
+ this, SLOT(slotIncomingMotd(const QString &)));
+
+ QObject::connect(Kopete::ChatSessionManager::self(), SIGNAL(viewCreated(KopeteView*)),
+ this, SLOT(slotViewCreated(KopeteView*)) );
+
+ updateStatus();
+}
+
+void IRCServerContact::updateStatus()
+{
+ KIRC::Engine::Status status = kircEngine()->status();
+ switch( status )
+ {
+ case KIRC::Engine::Idle:
+ case KIRC::Engine::Connecting:
+ if( m_chatSession )
+ m_chatSession->setDisplayName( caption() );
+ setOnlineStatus( m_protocol->m_ServerStatusOffline );
+ break;
+
+ case KIRC::Engine::Authentifying:
+ case KIRC::Engine::Connected:
+ case KIRC::Engine::Closing:
+ // should make some extra check here
+ setOnlineStatus( m_protocol->m_ServerStatusOnline );
+ break;
+
+ default:
+ setOnlineStatus( m_protocol->m_StatusUnknown );
+ }
+}
+
+const QString IRCServerContact::caption() const
+{
+ return i18n("%1 @ %2").arg(ircAccount()->mySelf()->nickName() ).arg(
+ kircEngine()->currentHost().isEmpty() ? ircAccount()->networkName() : kircEngine()->currentHost()
+ );
+}
+
+void IRCServerContact::engineInternalError(KIRC::Engine::Error engineError, KIRC::Message &ircmsg)
+{
+ QString error;
+ switch (engineError)
+ {
+ case KIRC::Engine::ParsingFailed:
+ error = i18n("KIRC Error - Parse error: ");
+ break;
+ case KIRC::Engine::UnknownCommand:
+ error = i18n("KIRC Error - Unknown command: ");
+ break;
+ case KIRC::Engine::UnknownNumericReply:
+ error = i18n("KIRC Error - Unknown numeric reply: ");
+ break;
+ case KIRC::Engine::InvalidNumberOfArguments:
+ error = i18n("KIRC Error - Invalid number of arguments: ");
+ break;
+ case KIRC::Engine::MethodFailed:
+ error = i18n("KIRC Error - Method failed: ");
+ break;
+ default:
+ error = i18n("KIRC Error - Unknown error: ");
+ }
+
+ ircAccount()->appendMessage(error + QString(ircmsg.raw()), IRCAccount::ErrorReply);
+}
+
+void IRCServerContact::slotSendMsg(Kopete::Message &, Kopete::ChatSession *manager)
+{
+ manager->messageSucceeded();
+ Kopete::Message msg( manager->myself(), manager->members(),
+ i18n("You can not talk to the server, you can only issue commands here. Type /help for supported commands."), Kopete::Message::Internal, Kopete::Message::PlainText, CHAT_VIEW);
+ manager->appendMessage(msg);
+}
+
+void IRCServerContact::appendMessage( const QString &message )
+{
+ Kopete::ContactPtrList members;
+ members.append( this );
+ Kopete::Message msg( this, members, message, Kopete::Message::Internal,
+ Kopete::Message::RichText, CHAT_VIEW );
+ appendMessage(msg);
+}
+
+void IRCServerContact::slotIncomingNotice( const QString &orig, const QString &notice )
+{
+ if (orig.isEmpty()) {
+ // Prefix missing.
+ // NOTICE AUTH :*** Checking Ident
+
+ ircAccount()->appendMessage(i18n("NOTICE from %1: %2").arg(kircEngine()->currentHost(), notice),
+ IRCAccount::NoticeReply);
+
+ } else {
+ // :Global!service@rizon.net NOTICE foobar :[Logon News - Oct 12 2005] Due to growing problems ...
+ // :somenick!~fooobar@somehostname.fi NOTICE foobar :hello
+
+ if (orig.contains('!')) {
+ ircAccount()->appendMessage(i18n("NOTICE from %1 (%2): %3").arg(
+ orig.section('!', 0, 0),
+ orig.section('!', 1, 1),
+ notice),
+ IRCAccount::NoticeReply);
+ } else {
+ ircAccount()->appendMessage(i18n("NOTICE from %1: %2").arg(
+ orig, notice), IRCAccount::NoticeReply);
+ }
+ }
+}
+
+void IRCServerContact::slotIncomingUnknown(const QString &message)
+{
+ ircAccount()->appendMessage(message, IRCAccount::UnknownReply);
+}
+
+void IRCServerContact::slotIncomingConnect(const QString &message)
+{
+ ircAccount()->appendMessage(message, IRCAccount::ConnectReply);
+}
+
+void IRCServerContact::slotIncomingMotd(const QString &message)
+{
+ ircAccount()->appendMessage(message, IRCAccount::InfoReply);
+}
+
+void IRCServerContact::slotCannotSendToChannel(const QString &channel, const QString &message)
+{
+ ircAccount()->appendMessage(QString::fromLatin1("%1: %2").arg(channel).arg(message), IRCAccount::ErrorReply);
+}
+
+void IRCServerContact::appendMessage(Kopete::Message &msg)
+{
+ msg.setImportance( Kopete::Message::Low ); //to don't distrub the user
+
+ if (m_chatSession && m_chatSession->view(false))
+ m_chatSession->appendMessage(msg);
+/*
+// disable the buffering for now: cause a memleak since we don't made it a *fixed size fifo*
+ else
+ mMsgBuffer.append( msg );
+*/
+}
+
+void IRCServerContact::slotDumpMessages()
+{
+ if (!mMsgBuffer.isEmpty())
+ {
+ manager()->appendMessage( mMsgBuffer.front() );
+ mMsgBuffer.pop_front();
+ QTimer::singleShot( 0, this, SLOT( slotDumpMessages() ) );
+ }
+}
+
+void IRCServerContact::slotViewCreated( KopeteView *v )
+{
+ kdDebug(14121) << k_funcinfo << "Created: " << v << ", mgr: " << v->msgManager() << ", Mine: " << m_chatSession << endl;
+ if (m_chatSession && v->msgManager() == m_chatSession)
+ QTimer::singleShot(500, this, SLOT(slotDumpMessages()));
+}
+
+#include "ircservercontact.moc"
diff --git a/kopete/protocols/irc/ircservercontact.h b/kopete/protocols/irc/ircservercontact.h
new file mode 100644
index 00000000..1ca1475b
--- /dev/null
+++ b/kopete/protocols/irc/ircservercontact.h
@@ -0,0 +1,80 @@
+/*
+ ircservercontact.h - IRC User Contact
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCSERVERCONTACT_H
+#define IRCSERVERCONTACT_H
+
+#include "irccontact.h"
+
+#include "kircengine.h"
+
+#include "kopetechatsessionmanager.h"
+
+#include <qvaluelist.h>
+#include <qstringlist.h>
+
+class KActionCollection;
+class KAction;
+class KActionMenu;
+class KopeteView;
+
+class IRCContactManager;
+class IRCChannelContact;
+
+/**
+ * @author Michel Hermier <michel.hermier@wanadoo.fr>
+ *
+ * This class is the @ref Kopete::Contact object representing IRC Servers.
+ * It is derrived from @ref IRCContact where much of its functionality is shared with @ref IRCChannelContact and @ref IRCUserContact.
+ */
+class IRCServerContact
+ : public IRCContact
+{
+ Q_OBJECT
+
+ public:
+ // This class provides a Kopete::Contact for each server of a given IRC connection.
+ IRCServerContact(IRCContactManager *, const QString &servername, Kopete::MetaContact *mc);
+
+ virtual const QString caption() const;
+
+ virtual void appendMessage(Kopete::Message &);
+ void appendMessage( const QString &message );
+
+ protected slots:
+ void engineInternalError(KIRC::Engine::Error error, KIRC::Message &ircmsg);
+ virtual void slotSendMsg(Kopete::Message &message, Kopete::ChatSession *);
+
+ private slots:
+ virtual void updateStatus();
+ void slotViewCreated( KopeteView* );
+ void slotDumpMessages();
+
+ void slotIncomingUnknown( const QString &message );
+ void slotIncomingConnect( const QString &message );
+ void slotIncomingMotd( const QString &motd );
+ void slotIncomingNotice( const QString &orig, const QString &notice );
+ void slotCannotSendToChannel( const QString &channel, const QString &msg );
+
+ private:
+ QValueList<Kopete::Message> mMsgBuffer;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/irc/ircsignalhandler.cpp b/kopete/protocols/irc/ircsignalhandler.cpp
new file mode 100644
index 00000000..5bfab0cc
--- /dev/null
+++ b/kopete/protocols/irc/ircsignalhandler.cpp
@@ -0,0 +1,173 @@
+
+/*
+ ircsignalhandler.cpp - Maps signals from the IRC engine to contacts
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircusercontact.h"
+#include "ircchannelcontact.h"
+#include "ircsignalhandler.h"
+
+#include "kircengine.h"
+
+IRCSignalHandler::IRCSignalHandler(IRCContactManager *m)
+ : QObject(m),
+ manager(m)
+{
+ KIRC::Engine *m_engine = static_cast<IRCAccount*>( manager->mySelf()->account() )->engine();
+
+ //Channel Connections to ourself
+ QObject::connect(m_engine, SIGNAL(incomingNamesList(const QString &, const QStringList &)),
+ this, SLOT(slotNamesList(const QString &, const QStringList &)));
+
+ QObject::connect(m_engine, SIGNAL(incomingEndOfNames(const QString &)),
+ this, SLOT(slotEndOfNames(const QString &)));
+
+ QObject::connect(m_engine, SIGNAL(incomingTopicUser(const QString &, const QString &, const QDateTime &)),
+ this, SLOT(slotTopicUser(const QString&,const QString&,const QDateTime&)));
+
+ //Channel String mappings
+ map<IRCChannelContact>( m, SIGNAL(incomingFailedChankey(const QString &)),
+ &IRCChannelContact::failedChankey );
+
+ map<IRCChannelContact>( m, SIGNAL(incomingFailedChanFull(const QString &)),
+ &IRCChannelContact::failedChanInvite );
+
+ map<IRCChannelContact>( m, SIGNAL(incomingFailedChanInvite(const QString &)),
+ &IRCChannelContact::failedChanInvite );
+
+ map<IRCChannelContact>( m, SIGNAL(incomingFailedChanBanned(const QString &)),
+ &IRCChannelContact::failedChanBanned );
+
+ mapSingle<IRCChannelContact>( m, SIGNAL(incomingJoinedChannel(const QString &, const QString &)),
+ &IRCChannelContact::userJoinedChannel );
+
+ mapSingle<IRCChannelContact>( m, SIGNAL(incomingExistingTopic(const QString &, const QString &)),
+ &IRCChannelContact::channelTopic );
+
+ mapSingle<IRCChannelContact>( m, SIGNAL(incomingChannelHomePage(const QString &, const QString &)),
+ &IRCChannelContact::channelHomePage );
+
+ mapDouble<IRCChannelContact>( m,
+ SIGNAL(incomingPartedChannel(const QString &, const QString &,const QString &)),
+ &IRCChannelContact::userPartedChannel );
+
+ mapDouble<IRCChannelContact>( m,
+ SIGNAL(incomingTopicChange(const QString &, const QString &,const QString &)),
+ &IRCChannelContact::topicChanged );
+
+ mapDouble<IRCChannelContact>( m,
+ SIGNAL(incomingChannelModeChange(const QString &, const QString &,const QString &)),
+ &IRCChannelContact::incomingModeChange );
+
+ mapDouble<IRCChannelContact>( m,
+ SIGNAL(incomingChannelMode(const QString &, const QString &,const QString &)),
+ &IRCChannelContact::incomingChannelMode );
+
+ mapTriple<IRCChannelContact>( m,
+ SIGNAL(incomingKick(const QString &, const QString &,const QString &,const QString &)),
+ &IRCChannelContact::userKicked );
+
+ //User connections to ourself
+ QObject::connect(m_engine, SIGNAL(incomingWhoIsIdle(const QString &, unsigned long )),
+ this, SLOT(slotNewWhoIsIdle(const QString &, unsigned long )));
+
+ QObject::connect(m_engine, SIGNAL(incomingWhoReply(const QString &, const QString &, const QString &,
+ const QString &, const QString &, bool, const QString &, uint, const QString & )),
+ this, SLOT( slotNewWhoReply(const QString &, const QString &, const QString &, const QString &,
+ const QString &, bool, const QString &, uint, const QString &)));
+
+ //User signal mappings
+ map<IRCUserContact>( m, SIGNAL(incomingUserOnline( const QString & )), &IRCUserContact::userOnline );
+
+ map<IRCUserContact>( m, SIGNAL(incomingWhoIsOperator( const QString & )), &IRCUserContact::newWhoIsOperator );
+
+ map<IRCUserContact>( m, SIGNAL(incomingWhoIsIdentified( const QString & )), &IRCUserContact::newWhoIsIdentified );
+
+ map<IRCUserContact>( m, SIGNAL(incomingEndOfWhois( const QString & )), &IRCUserContact::whoIsComplete );
+
+ map<IRCUserContact>( m, SIGNAL(incomingEndOfWhoWas( const QString & )), &IRCUserContact::whoWasComplete );
+
+ mapSingle<IRCUserContact>( m, SIGNAL(incomingUserIsAway( const QString &, const QString & )),
+ &IRCUserContact::incomingUserIsAway );
+
+ mapSingle<IRCUserContact>( m, SIGNAL(incomingWhoIsChannels( const QString &, const QString & )),
+ &IRCUserContact::newWhoIsChannels );
+
+ mapDouble<IRCUserContact>( m,
+ SIGNAL(incomingWhoIsServer(const QString &, const QString &, const QString &)),
+ &IRCUserContact::newWhoIsServer );
+
+ mapDouble<IRCUserContact>( m,
+ SIGNAL(incomingPrivAction(const QString &, const QString &, const QString &)),
+ &IRCUserContact::newAction );
+
+ mapDouble<IRCChannelContact>( m,
+ SIGNAL(incomingAction(const QString &, const QString &, const QString &)),
+ &IRCChannelContact::newAction );
+
+ mapTriple<IRCUserContact>( m,
+ SIGNAL(incomingWhoIsUser(const QString &, const QString &, const QString &, const QString &)),
+ &IRCUserContact::newWhoIsUser );
+
+ mapTriple<IRCUserContact>( m,
+ SIGNAL(incomingWhoWasUser(const QString &, const QString &, const QString &, const QString &)),
+ &IRCUserContact::newWhoIsUser );
+}
+
+IRCSignalHandler::~IRCSignalHandler()
+{
+ //Delete our mapping pointers
+ for( QValueList<IRCSignalMappingBase*>::iterator it = mappings.begin(); it != mappings.end(); ++it )
+ delete *it;
+}
+
+void IRCSignalHandler::slotNamesList( const QString &chan, const QStringList &list )
+{
+ IRCChannelContact *c = manager->existChannel( chan );
+ if( c )
+ c->namesList( list );
+}
+
+void IRCSignalHandler::slotEndOfNames( const QString &chan )
+{
+ IRCChannelContact *c = manager->existChannel( chan );
+ if ( c )
+ c->endOfNames();
+}
+
+void IRCSignalHandler::slotTopicUser(const QString &chan, const QString &user,const QDateTime &time)
+{
+ IRCChannelContact *c = manager->existChannel( chan );
+ if( c )
+ c->topicUser( user, time );
+}
+
+void IRCSignalHandler::slotNewWhoIsIdle(const QString &nick, unsigned long val )
+{
+ IRCUserContact *c = manager->findUser( nick );
+ if( c )
+ c->newWhoIsIdle( val );
+}
+
+void IRCSignalHandler::slotNewWhoReply(const QString &nick, const QString &arg1, const QString &arg2,
+ const QString &arg3, const QString &arg4, bool arg5, const QString &arg6, uint arg7, const QString &arg8 )
+{
+ IRCUserContact *c = manager->findUser( nick );
+ if( c )
+ c->newWhoReply( arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 );
+}
+
+#include "ircsignalhandler.moc"
diff --git a/kopete/protocols/irc/ircsignalhandler.h b/kopete/protocols/irc/ircsignalhandler.h
new file mode 100644
index 00000000..a87f814c
--- /dev/null
+++ b/kopete/protocols/irc/ircsignalhandler.h
@@ -0,0 +1,334 @@
+
+/*
+ ircsignalhandler.h - Maps signals from the IRC engine to contacts
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _IRC_SIGNAL_HANDLER_H
+#define _IRC_SIGNAL_HANDLER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+
+#include <kdebug.h>
+
+#include "ircaccount.h"
+#include "irccontactmanager.h"
+
+/***
+* IRC Signal handler. Mapps a KIRC engine signal to the right contact. Avoids
+* Having a signal connected to 500+ slots where only one is valid, instead
+* uses the contact dictionary.
+*
+* Warning: This file has a lot of black magic in it. Avoid it if
+* you don't want your eyes to bleed. More below...
+*
+* Define some templated classes and methods to map a KIRC signal to the
+* right contact. Having these templates greatly cuts down *A LOT* on the amount of
+* code that would need to be in the signal mapper, at the expense of some readability.
+*
+* There are four IRCSignalMapping classes, one each for signals with 0, 1, 2,
+* and 3 arguments ( plus the contact ID ). The classes take the signal, look
+* up the contact it is for, and call the function passed into the class by the
+* mapping function.
+*
+* Since QObjects cannot be inside templates, the QMember classes that connect
+* to the slots are seperate.
+*/
+
+/*** Pre-declare mapping types for the QObjects **/
+struct IRCSignalMappingBase{};
+
+struct IRCSignalMappingT : IRCSignalMappingBase
+{
+ virtual void exec( const QString & ) = 0;
+ virtual ~IRCSignalMappingT() {};
+};
+
+struct IRCSignalMappingSingleT : IRCSignalMappingBase
+{
+ virtual void exec( const QString &, const QString & ) = 0;
+ virtual ~IRCSignalMappingSingleT() {};
+};
+
+struct IRCSignalMappingDoubleT : IRCSignalMappingBase
+{
+ virtual void exec( const QString &, const QString &, const QString & ) = 0;
+ virtual ~IRCSignalMappingDoubleT() {};
+};
+
+struct IRCSignalMappingTripleT : IRCSignalMappingBase
+{
+ virtual void exec( const QString &, const QString &, const QString &, const QString & ) = 0;
+ virtual ~IRCSignalMappingTripleT() {};
+};
+
+/***
+QObject members, these connect to the KIRC signals and call
+the Mapping functions when they emit.
+**/
+
+class QMember : public QObject
+{
+ Q_OBJECT
+
+ public:
+ QMember( IRCSignalMappingT *m, QObject *p ) : QObject( p ), mapping( m ){};
+
+ public slots:
+ void slotEmit( const QString &id )
+ {
+ //kdDebug(14120) << k_funcinfo << id << endl;
+ mapping->exec(id);
+ }
+
+ private:
+ IRCSignalMappingT *mapping;
+};
+
+class QMemberSingle : public QObject
+{
+ Q_OBJECT
+
+ public:
+ QMemberSingle( IRCSignalMappingSingleT *m, QObject *p ) : QObject( p ), mapping( m ){}
+
+ public slots:
+ void slotEmit( const QString &id, const QString &arg )
+ {
+ //kdDebug(14120) << k_funcinfo << id << " : " << arg << endl;
+ mapping->exec(id,arg);
+ }
+
+ private:
+ IRCSignalMappingSingleT *mapping;
+};
+
+class QMemberDouble : public QObject
+{
+ Q_OBJECT
+
+ public:
+ QMemberDouble( IRCSignalMappingDoubleT *m, QObject *p ) : QObject( p ), mapping( m ){}
+
+ public slots:
+ void slotEmit( const QString &id, const QString &arg, const QString &arg2 )
+ {
+ //kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << endl;
+ mapping->exec(id,arg,arg2);
+ }
+
+ private:
+ IRCSignalMappingDoubleT *mapping;
+};
+
+class QMemberTriple : public QObject
+{
+ Q_OBJECT
+
+ public:
+ QMemberTriple( IRCSignalMappingTripleT *m, QObject *p ) : QObject( p ), mapping( m ){}
+
+ public slots:
+ void slotEmit( const QString &id, const QString &arg, const QString &arg2, const QString &arg3 )
+ {
+ //kdDebug(14120) << k_funcinfo << id << " : " << arg << " : " << arg2 << " : " << arg3 << endl;
+ mapping->exec(id,arg,arg2,arg3);
+ }
+
+ private:
+ IRCSignalMappingTripleT *mapping;
+};
+
+/***
+Mapping classes. These contain pointers to the functions to call. We first
+look up the right contact in the contact manager's dictionary, and then
+call the method
+**/
+
+template <class TClass>
+class IRCSignalMapping : public IRCSignalMappingT
+{
+ public:
+ IRCSignalMapping( IRCContactManager *mgr, const char * /*signal*/,
+ void (TClass::*m)() ) : manager(mgr), method(m){}
+
+ void exec( const QString &id )
+ {
+ TClass *c = (TClass*)manager->findContact( id );
+ if( c )
+ {
+ void (TClass::*tmp)() = (void (TClass::*)())method;
+ (*c.*tmp)();
+ }
+ }
+
+ private:
+ IRCContactManager *manager;
+ void (TClass::*method)();
+};
+
+template <class TClass>
+class IRCSignalMappingSingle : public IRCSignalMappingSingleT
+{
+ public:
+ IRCSignalMappingSingle<TClass>( IRCContactManager *mgr, const char * /*signal*/,
+ void (TClass::*m)(const QString&) ) : manager(mgr), method(m){}
+
+ void exec( const QString &id, const QString &arg )
+ {
+ TClass *c = (TClass*)manager->findContact( id );
+ if( c )
+ {
+ void (TClass::*tmp)(const QString&) = (void (TClass::*)(const QString&))method;
+ (*c.*tmp)( arg );
+ }
+ }
+
+ private:
+ IRCContactManager *manager;
+ void (TClass::*method)(const QString &);
+};
+
+template <class TClass>
+class IRCSignalMappingDouble : public IRCSignalMappingDoubleT
+{
+ public:
+ IRCSignalMappingDouble<TClass>( IRCContactManager *mgr, const char * /*signal*/,
+ void (TClass::*m)(const QString&,const QString&) ) : manager(mgr), method(m){}
+
+ void exec( const QString &id,const QString &arg, const QString &arg2 )
+ {
+ TClass *c = (TClass*)manager->findContact( id );
+ if( c )
+ {
+ void (TClass::*tmp)(const QString&,const QString&) =
+ (void (TClass::*)(const QString&,const QString&))method;
+ (*c.*tmp)(arg,arg2);
+ }
+ }
+
+ private:
+ IRCContactManager *manager;
+ void (TClass::*method)(const QString &,const QString &);
+};
+
+template <class TClass>
+class IRCSignalMappingTriple : public IRCSignalMappingTripleT
+{
+ public:
+ IRCSignalMappingTriple<TClass>( IRCContactManager *mgr, const char * /*signal*/,
+ void (TClass::*m)(const QString&,const QString&,const QString&) )
+ : manager(mgr), method(m){}
+
+ void exec( const QString &id,const QString&arg, const QString &arg2, const QString &arg3 )
+ {
+ TClass *c = (TClass*)manager->findContact( id );
+ if( c )
+ {
+ void (TClass::*tmp)(const QString&,const QString&,const QString&) =
+ (void (TClass::*)(const QString&,const QString&,const QString&))method;
+ (*c.*tmp)(arg,arg2,arg3);
+ }
+ }
+
+ private:
+ IRCContactManager *manager;
+ void (TClass::*method)(const QString &,const QString &,const QString &);
+};
+
+class IRCSignalHandler : public QObject
+{
+ Q_OBJECT
+
+ public:
+ IRCSignalHandler( IRCContactManager *manager );
+ ~IRCSignalHandler();
+
+ private slots:
+
+ /****
+ Slots for signals with non-QString types
+ */
+
+ //Channel contact slots
+ void slotNamesList( const QString &, const QStringList & );
+ void slotEndOfNames( const QString & );
+ void slotTopicUser( const QString &, const QString&, const QDateTime &);
+
+ //User contact slots
+ void slotNewWhoIsIdle(const QString &, unsigned long );
+ void slotNewWhoReply(const QString &, const QString &, const QString &, const QString &,
+ const QString &, bool , const QString &, uint , const QString & );
+
+ private:
+ IRCContactManager *manager;
+ QValueList<IRCSignalMappingBase*> mappings;
+
+ /****
+ Signal mapping functions
+ */
+
+ template <class TClass>
+ inline void map( IRCContactManager *m, const char* signal, void (TClass::*method)() )
+ {
+ IRCSignalMappingT *mapping = new IRCSignalMapping<TClass>( m, signal, method );
+ mappings.append(mapping);
+ QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
+ new QMember( mapping, this),
+ SLOT( slotEmit( const QString &) )
+ );
+ }
+
+ template <class TClass>
+ inline void mapSingle( IRCContactManager *m,
+ const char* signal, void (TClass::*method)(const QString&) )
+ {
+ IRCSignalMappingSingleT *mapping = new IRCSignalMappingSingle<TClass>( m, signal, method );
+ mappings.append(mapping);
+ QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
+ new QMemberSingle( mapping, this),
+ SLOT( slotEmit( const QString &, const QString &) )
+ );
+ }
+
+ template <class TClass>
+ inline void mapDouble( IRCContactManager *m,
+ const char* signal, void (TClass::*method)(const QString&,const QString&) )
+ {
+ IRCSignalMappingDoubleT *mapping = new IRCSignalMappingDouble<TClass>( m, signal, method );
+ mappings.append(mapping);
+ QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
+ new QMemberDouble( mapping, this),
+ SLOT( slotEmit( const QString &, const QString &,const QString &) )
+ );
+ }
+
+ template <class TClass>
+ inline void mapTriple( IRCContactManager *m,
+ const char* signal,
+ void (TClass::*method)(const QString&,const QString &, const QString &) )
+ {
+ IRCSignalMappingTripleT *mapping = new IRCSignalMappingTriple<TClass>( m, signal, method );
+ mappings.append(mapping);
+ QObject::connect( static_cast<IRCAccount*>( m->mySelf()->account() )->engine(), signal,
+ new QMemberTriple( mapping, this),
+ SLOT( slotEmit( const QString &, const QString &,const QString &,const QString &) )
+ );
+ }
+};
+
+#endif
diff --git a/kopete/protocols/irc/irctransferhandler.cpp b/kopete/protocols/irc/irctransferhandler.cpp
new file mode 100644
index 00000000..4715679e
--- /dev/null
+++ b/kopete/protocols/irc/irctransferhandler.cpp
@@ -0,0 +1,183 @@
+/*
+ irctransferhandler.cpp - IRC transfer.
+
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+
+#include <kopetetransfermanager.h>
+
+#include "libkirc/kirctransfer.h"
+#include "libkirc/kirctransferhandler.h"
+
+#include "kopetemetacontact.h"
+#include "irccontact.h"
+#include "irccontactmanager.h"
+
+#include "irctransferhandler.h"
+
+IRCTransferHandler *IRCTransferHandler::self()
+{
+ static IRCTransferHandler sm_self;
+ return &sm_self;
+}
+
+KIRC::TransferHandler *IRCTransferHandler::handler()
+{
+ return KIRC::TransferHandler::self();
+}
+
+IRCTransferHandler::IRCTransferHandler()
+{
+ connect(handler(), SIGNAL(transferCreated(KIRC::Transfer *)),
+ this, SLOT(transferCreated(KIRC::Transfer *)));
+
+ connect(Kopete::TransferManager::transferManager(), SIGNAL(accepted(Kopete::Transfer *, const QString &)),
+ this, SLOT(transferAccepted(Kopete::Transfer *, const QString&)));
+ connect( Kopete::TransferManager::transferManager(), SIGNAL(refused(const Kopete::FileTransferInfo &)),
+ this, SLOT(transferRefused(const Kopete::FileTransferInfo &)));
+}
+
+void IRCTransferHandler::transferCreated(KIRC::Transfer *t)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ IRCContact *contact = IRCContactManager::existContact(t->engine(), t->nick());
+ QString fileName = t->fileName();
+ unsigned long fileSize = t->fileSize();
+
+ if(!contact)
+ {
+ kdDebug(14120) << k_funcinfo << "Trying to create transfer for a non existing contact(" << t->nick() << ")." << endl;
+ return;
+ }
+
+ switch(t->type())
+ {
+// case KIRC::Transfer::Chat:
+ case KIRC::Transfer::FileOutgoing:
+ {
+ Kopete::Transfer *kt = Kopete::TransferManager::transferManager()->addTransfer(
+ contact, fileName, fileSize, contact->metaContact()->displayName(),
+ Kopete::FileTransferInfo::Outgoing);
+ connectKopeteTransfer(kt, t);
+ }
+ break;
+ case KIRC::Transfer::FileIncoming:
+ {
+ int ID = Kopete::TransferManager::transferManager()->askIncomingTransfer(
+ contact , fileName, fileSize);
+ m_idMap.insert(ID, t);
+ }
+ break;
+ default:
+ kdDebug(14120) << k_funcinfo << "Unknown transfer type" << endl;
+ t->deleteLater();
+ }
+}
+
+void IRCTransferHandler::transferAccepted(Kopete::Transfer *kt, const QString &file)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ KIRC::Transfer *t = getKIRCTransfer(kt->info());
+ if(t)
+ {
+ t->setFileName(file);
+ connectKopeteTransfer(kt, t);
+ }
+}
+void IRCTransferHandler::transferRefused(const Kopete::FileTransferInfo &info)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ KIRC::Transfer *t = getKIRCTransfer(info);
+ if(t)
+ {
+ t->deleteLater();
+ }
+}
+
+void IRCTransferHandler::connectKopeteTransfer(Kopete::Transfer *kt, KIRC::Transfer *t)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ if(kt && t)
+ {
+ switch(t->type())
+ {
+// case KIRC::Transfer::Chat:
+ case KIRC::Transfer::FileOutgoing:
+ case KIRC::Transfer::FileIncoming:
+ connect(t , SIGNAL(fileSizeAcknowledge(unsigned int)),
+ kt, SLOT(slotProcessed(unsigned int)));
+ break;
+ default:
+ kdDebug(14120) << k_funcinfo << "Unknown transfer connections for type" << endl;
+ t->deleteLater();
+ return;
+ }
+
+ connect(t , SIGNAL(complete()),
+ kt, SLOT(slotComplete()));
+
+// connect(kt , SIGNAL(transferCanceled()),
+// t, SLOT(abort()));
+// connect(kt, SIGNAL(destroyed()),
+// t, SLOT(slotKopeteTransferDestroyed()));
+
+ connect(kt, SIGNAL(result(KIO::Job *)),
+ this , SLOT(kioresult(KIO::Job *)));
+
+ t->initiate();
+ }
+}
+
+void IRCTransferHandler::kioresult(KIO::Job *job)
+{
+ Kopete::Transfer *kt= (Kopete::Transfer *)job; // FIXME: move to *_cast
+ if(!kt)
+ {
+ kdDebug(14120) << k_funcinfo << "Kopete::Transfer not found from kio:" << job << endl;
+ return;
+ }
+
+ switch(kt->error())
+ {
+ case 0: // 0 means no error
+ break;
+ case KIO::ERR_USER_CANCELED:
+ kdDebug(14120) << k_funcinfo << "User canceled transfer." << endl;
+ // KIO::buildErrorString form error don't provide a result string ...
+// if (t->)
+// kt->userAbort(i18n("User canceled transfer."));
+// else
+// kt->userAbort(i18n("User canceled transfer for file:%1").arg(t->fileName()));
+ break;
+ default:
+ kdDebug(14120) << k_funcinfo << "Transfer halted:" << kt->error() << endl;
+// kt->userAbort(KIO::buildErrorString(kt->error(), kt->fileName()));
+ break;
+ }
+}
+
+KIRC::Transfer *IRCTransferHandler::getKIRCTransfer(const Kopete::FileTransferInfo &info)
+{
+ KIRC::Transfer *t = m_idMap[info.transferId()];
+ m_idMap.remove(info.transferId());
+ return t;
+}
+
+#include "irctransferhandler.moc"
diff --git a/kopete/protocols/irc/irctransferhandler.h b/kopete/protocols/irc/irctransferhandler.h
new file mode 100644
index 00000000..17c419ae
--- /dev/null
+++ b/kopete/protocols/irc/irctransferhandler.h
@@ -0,0 +1,65 @@
+/*
+ irctransferhandler.h - IRC transfer.
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCTRANSFERHANDLER_H
+#define IRCTRANSFERHANDLER_H
+
+#include <qintdict.h>
+
+#include <kopetetransfermanager.h>
+
+namespace Kopete
+{
+ class Transfer;
+}
+
+namespace KIRC
+{
+class Transfer;
+class TransferHandler;
+}
+
+class IRCTransferHandler
+ : public QObject
+{
+ Q_OBJECT
+
+public:
+ static IRCTransferHandler *self();
+
+private slots:
+ void transferCreated(KIRC::Transfer *);
+ void transferAccepted(Kopete::Transfer *kt, const QString&file);
+ void transferRefused(const Kopete::FileTransferInfo &info);
+
+ void kioresult(KIO::Job *job);
+
+private:
+ IRCTransferHandler();
+
+ void connectKopeteTransfer(Kopete::Transfer *kt, KIRC::Transfer *t);
+
+ /* warning: After calling this method the KIRC::Transfer is removed from the m_idMap.
+ */
+ KIRC::Transfer *getKIRCTransfer(const Kopete::FileTransferInfo &info);
+
+ KIRC::TransferHandler *handler();
+
+ QIntDict<KIRC::Transfer> m_idMap;
+};
+
+#endif
diff --git a/kopete/protocols/irc/ircusercontact.cpp b/kopete/protocols/irc/ircusercontact.cpp
new file mode 100644
index 00000000..dc9dcbf2
--- /dev/null
+++ b/kopete/protocols/irc/ircusercontact.cpp
@@ -0,0 +1,734 @@
+/*
+ ircusercontact.cpp - IRC User Contact
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ircusercontact.h"
+#include "ircservercontact.h"
+#include "ircchannelcontact.h"
+#include "irccontactmanager.h"
+#include "ircaccount.h"
+#include "ircprotocol.h"
+#include "kcodecaction.h"
+
+#include "kopetemetacontact.h"
+#include "kopeteview.h"
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+
+#include <qtimer.h>
+
+IRCUserContact::IRCUserContact(IRCContactManager *contactManager, const QString &nickname, Kopete::MetaContact *m )
+ : IRCContact(contactManager, nickname, m ),
+ actionCtcpMenu(0L)
+{
+ setFileCapable(true);
+
+ mOnlineTimer = new QTimer( this );
+
+ QObject::connect(mOnlineTimer, SIGNAL(timeout()), this, SLOT( slotUserOffline() ) );
+
+ QObject::connect(kircEngine(), SIGNAL(incomingChannelModeChange(const QString&, const QString&, const QString&)),
+ this, SLOT(slotIncomingModeChange(const QString&,const QString&, const QString&)));
+
+ mInfo.isOperator = false;
+ mInfo.isIdentified = false;
+ mInfo.idle = 0;
+ mInfo.hops = 0;
+ mInfo.away = false;
+ mInfo.online = metaContact()->isTemporary();
+
+ updateStatus();
+}
+
+void IRCUserContact::updateStatus()
+{
+ //kdDebug(14120) << k_funcinfo << endl;
+
+ Kopete::OnlineStatus newStatus;
+
+ switch (kircEngine()->status())
+ {
+ case KIRC::Engine::Idle:
+ newStatus = m_protocol->m_UserStatusOffline;
+ break;
+
+ case KIRC::Engine::Connecting:
+ case KIRC::Engine::Authentifying:
+ if (this == ircAccount()->mySelf())
+ newStatus = m_protocol->m_UserStatusConnecting;
+ else
+ newStatus = m_protocol->m_UserStatusOffline;
+ break;
+
+ case KIRC::Engine::Connected:
+ case KIRC::Engine::Closing:
+ if (mInfo.away)
+ newStatus = m_protocol->m_UserStatusAway;
+ else if (mInfo.online)
+ newStatus = m_protocol->m_UserStatusOnline;
+ break;
+
+ default:
+ newStatus = m_protocol->m_StatusUnknown;
+ }
+
+ // Try hard not to emit several onlineStatusChanged() signals.
+ bool onlineStatusChanged = false;
+
+
+ /* The away status is global, so if the user goes away, we must set
+ * the new status on all channels.
+ */
+
+
+ // This may not be created yet ( for myself() on startup )
+ if( ircAccount()->contactManager() )
+ {
+ QValueList<IRCChannelContact*> channels = ircAccount()->contactManager()->findChannelsByMember(this);
+
+ for( QValueList<IRCChannelContact*>::iterator it = channels.begin(); it != channels.end(); ++it )
+ {
+ IRCChannelContact *channel = *it;
+ Kopete::OnlineStatus currentStatus = channel->manager()->contactOnlineStatus(this);
+
+ //kdDebug(14120) << k_funcinfo << "iterating channel " << channel->nickName() << " internal status: " << currentStatus.internalStatus() << endl;
+
+ if( currentStatus.internalStatus() >= IRCProtocol::Online )
+ {
+ onlineStatusChanged = true;
+
+ if( !(currentStatus.internalStatus() & IRCProtocol::Away) && newStatus == m_protocol->m_UserStatusAway )
+ {
+ setOnlineStatus( newStatus );
+ //kdDebug(14120) << k_funcinfo << "was NOT away, but is now, channel " << channel->nickName() << endl;
+ adjustInternalOnlineStatusBits(channel, IRCProtocol::Away, AddBits);
+ }
+ else if( (currentStatus.internalStatus() & IRCProtocol::Away) && newStatus == m_protocol->m_UserStatusOnline )
+ {
+ setOnlineStatus( newStatus );
+ //kdDebug(14120) << k_funcinfo << "was away, but not anymore, channel " << channel->nickName() << endl;
+ adjustInternalOnlineStatusBits(channel, IRCProtocol::Away, RemoveBits);
+
+ }
+ else if( newStatus.internalStatus() < IRCProtocol::Away )
+ {
+ //kdDebug(14120) << k_funcinfo << "offline or connecting?" << endl;
+ channel->manager()->setContactOnlineStatus( this, newStatus );
+ }
+ }
+ }
+ }
+
+ if (!onlineStatusChanged) {
+ //kdDebug(14120) << k_funcinfo << "setting status at last" << endl;
+ setOnlineStatus( newStatus );
+ }
+}
+
+void IRCUserContact::sendFile(const KURL &sourceURL, const QString&, unsigned int)
+{
+ QString filePath;
+
+ //If the file location is null, then get it from a file open dialog
+ if( !sourceURL.isValid() )
+ filePath = KFileDialog::getOpenFileName(QString::null, "*", 0l , i18n("Kopete File Transfer"));
+ else
+ filePath = sourceURL.path(-1);
+
+ kdDebug(14120) << k_funcinfo << "File chosen to send:" << filePath << endl;
+
+ if (!filePath.isEmpty())
+ kircEngine()->CtcpRequest_dcc( m_nickName, filePath, 0, KIRC::Transfer::FileOutgoing);
+}
+
+void IRCUserContact::slotUserOffline()
+{
+ mInfo.online = false;
+ mInfo.away = false;
+
+ updateStatus();
+
+ if( !metaContact()->isTemporary() )
+ kircEngine()->writeMessage( QString::fromLatin1("WHOWAS %1").arg(m_nickName) );
+
+ removeProperty( m_protocol->propUserInfo );
+ removeProperty( m_protocol->propServer );
+ removeProperty( m_protocol->propChannels );
+}
+
+void IRCUserContact::setAway(bool isAway)
+{
+ //kdDebug(14120) << k_funcinfo << isAway << endl;
+
+ mInfo.away = isAway;
+ updateStatus();
+}
+
+void IRCUserContact::incomingUserIsAway(const QString &reason)
+{
+ if( manager( Kopete::Contact::CannotCreate ) )
+ {
+ Kopete::Message msg( (Kopete::Contact*)ircAccount()->myServer(), mMyself,
+ i18n("%1 is away (%2)").arg( m_nickName ).arg( reason ),
+ Kopete::Message::Internal, Kopete::Message::RichText, CHAT_VIEW );
+ manager(Kopete::Contact::CanCreate)->appendMessage(msg);
+ }
+}
+
+void IRCUserContact::userOnline()
+{
+ mInfo.online = true;
+ updateStatus();
+ if (this != ircAccount()->mySelf() && !metaContact()->isTemporary() && ircAccount()->isConnected())
+ {
+ mOnlineTimer->start( 45000, true );
+ ircAccount()->setCurrentCommandSource(0);
+ kircEngine()->whois(m_nickName);
+ }
+
+ removeProperty( m_protocol->propLastSeen );
+}
+
+void IRCUserContact::slotUserInfo()
+{
+ if (isChatting())
+ {
+ ircAccount()->setCurrentCommandSource(manager());
+ kircEngine()->whois(m_nickName);
+ }
+}
+
+const QString IRCUserContact::caption() const
+{
+ return i18n("%1 @ %2").arg(m_nickName).arg(kircEngine()->currentHost());
+}
+
+void IRCUserContact::slotOp()
+{
+ contactMode( QString::fromLatin1("+o") );
+}
+
+void IRCUserContact::slotDeop()
+{
+ contactMode( QString::fromLatin1("-o") );
+}
+
+void IRCUserContact::slotVoice()
+{
+ contactMode( QString::fromLatin1("+v") );
+}
+
+void IRCUserContact::slotDevoice()
+{
+ contactMode( QString::fromLatin1("-v") );
+}
+
+void IRCUserContact::slotBanHost()
+{
+ // MODE #foofoofoo +b *!*@host.domain.net
+
+ if (mInfo.hostName.isEmpty()) {
+ if (kircEngine()->isConnected()) {
+ kircEngine()->whois(m_nickName);
+ QTimer::singleShot( 750, this, SLOT( slotBanHostOnce() ) );
+ }
+ } else {
+ slotBanHostOnce();
+ }
+}
+void IRCUserContact::slotBanHostOnce()
+{
+ if (mInfo.hostName.isEmpty())
+ return;
+
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+
+ kircEngine()->mode(channelName, QString::fromLatin1("+b *!*@%1").arg(mInfo.hostName));
+}
+
+void IRCUserContact::slotBanUserHost()
+{
+ // MODE #foofoofoo +b *!*user@host.domain.net
+
+ if (mInfo.hostName.isEmpty()) {
+ if (kircEngine()->isConnected()) {
+ kircEngine()->whois(m_nickName);
+ QTimer::singleShot( 750, this, SLOT( slotBanUserHostOnce() ) );
+ }
+ } else {
+ slotBanUserHostOnce();
+ }
+}
+void IRCUserContact::slotBanUserHostOnce()
+{
+ if (mInfo.hostName.isEmpty())
+ return;
+
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+
+ kircEngine()->mode(channelName, QString::fromLatin1("+b *!*%1@%2").arg(mInfo.userName, mInfo.hostName));
+}
+
+void IRCUserContact::slotBanDomain()
+{
+ // MODE #foofoofoo +b *!*@*.domain.net
+
+ if (mInfo.hostName.isEmpty()) {
+ if (kircEngine()->isConnected()) {
+ kircEngine()->whois(m_nickName);
+ QTimer::singleShot( 750, this, SLOT( slotBanDomainOnce() ) );
+ }
+ } else {
+ slotBanDomainOnce();
+ }
+}
+void IRCUserContact::slotBanDomainOnce()
+{
+ if (mInfo.hostName.isEmpty())
+ return;
+
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+
+ QString domain = mInfo.hostName.section('.', 1);
+
+ kircEngine()->mode(channelName, QString::fromLatin1("+b *!*@*.%1").arg(domain));
+}
+
+void IRCUserContact::slotBanUserDomain()
+{
+ // MODE #foofoofoo +b *!*user@*.domain.net
+
+ if (mInfo.hostName.isEmpty()) {
+ if (kircEngine()->isConnected()) {
+ kircEngine()->whois(m_nickName);
+ QTimer::singleShot( 750, this, SLOT( slotBanUserDomainOnce() ) );
+ }
+ } else {
+ slotBanUserDomainOnce();
+ }
+}
+void IRCUserContact::slotBanUserDomainOnce()
+{
+ if (mInfo.hostName.isEmpty())
+ return;
+
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+
+ QString domain = mInfo.hostName.section('.', 1);
+
+ kircEngine()->mode(channelName, QString::fromLatin1("+b *!*%1@*.%2").arg(mInfo.userName, domain));
+}
+
+void IRCUserContact::slotKick()
+{
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+ kircEngine()->kick(m_nickName, channelName, QString::null);
+}
+
+void IRCUserContact::contactMode(const QString &mode)
+{
+ Kopete::ContactPtrList members = mActiveManager->members();
+ QString channelName = static_cast<IRCContact*>(members.first())->nickName();
+ kircEngine()->mode(channelName, QString::fromLatin1("%1 %2").arg(mode).arg(m_nickName));
+}
+
+void IRCUserContact::slotCtcpPing()
+{
+ kircEngine()->CtcpRequest_ping(m_nickName);
+}
+
+void IRCUserContact::slotCtcpVersion()
+{
+ kircEngine()->CtcpRequest_version(m_nickName);
+}
+
+void IRCUserContact::newWhoIsUser(const QString &username, const QString &hostname, const QString &realname)
+{
+ mInfo.channels.clear();
+ mInfo.userName = username;
+ mInfo.hostName = hostname;
+ mInfo.realName = realname;
+
+ if( onlineStatus().status() == Kopete::OnlineStatus::Offline )
+ {
+ setProperty( m_protocol->propUserInfo, QString::fromLatin1("%1@%2")
+ .arg(mInfo.userName).arg(mInfo.hostName) );
+ setProperty( m_protocol->propServer, mInfo.serverName );
+ setProperty( m_protocol->propFullName, mInfo.realName );
+ }
+}
+
+void IRCUserContact::newWhoIsServer(const QString &servername, const QString &serverinfo)
+{
+ mInfo.serverName = servername;
+ if( metaContact()->isTemporary() || onlineStatus().status() == Kopete::OnlineStatus::Online
+ || onlineStatus().status() == Kopete::OnlineStatus::Away )
+ mInfo.serverInfo = serverinfo;
+ else
+ {
+ //kdDebug(14120)<< "Setting last online: " << serverinfo << endl;
+
+ // Try to convert first, since server can return depending if
+ // user is online or not:
+ //
+ // 312 mynick othernick localhost.localdomain :FooNet Server
+ // 312 mynick othernick localhost.localdomain :Thu Jun 16 21:00:36 2005
+
+ QDateTime lastSeen = QDateTime::fromString( serverinfo );
+ if( lastSeen.isValid() )
+ setProperty( m_protocol->propLastSeen, lastSeen );
+ }
+}
+
+void IRCUserContact::newWhoIsIdle(unsigned long idle)
+{
+ mInfo.idle = idle;
+}
+
+void IRCUserContact::newWhoIsOperator()
+{
+ mInfo.isOperator = true;
+}
+
+void IRCUserContact::newWhoIsIdentified()
+{
+ mInfo.isIdentified = true;
+ setProperty( m_protocol->propIsIdentified, i18n("True") );
+}
+
+void IRCUserContact::newWhoIsChannels(const QString &channel)
+{
+ mInfo.channels.append( channel );
+}
+
+void IRCUserContact::whoIsComplete()
+{
+ Kopete::ChatSession *commandSource = ircAccount()->currentCommandSource();
+
+ updateInfo();
+
+ if( isChatting() && commandSource &&
+ commandSource == manager(Kopete::Contact::CannotCreate) )
+ {
+ //User info
+ QString msg = i18n("%1 is (%2@%3): %4<br/>")
+ .arg(m_nickName)
+ .arg(mInfo.userName)
+ .arg(mInfo.hostName)
+ .arg(mInfo.realName);
+
+ if( mInfo.isIdentified )
+ msg += i18n("%1 is authenticated with NICKSERV<br/>").arg(m_nickName);
+
+ if( mInfo.isOperator )
+ msg += i18n("%1 is an IRC operator<br/>").arg(m_nickName);
+
+ //Channels
+ msg += i18n("on channels %1<br/>").arg(mInfo.channels.join(" ; "));
+
+ //Server
+ msg += i18n("on IRC via server %1 ( %2 )<br/>").arg(mInfo.serverName).arg(mInfo.serverInfo);
+
+ //Idle
+ QString idleTime = formattedIdleTime();
+ msg += i18n("idle: %2<br/>").arg( idleTime.isEmpty() ? QString::number(0) : idleTime );
+
+ //End
+ ircAccount()->appendMessage(msg, IRCAccount::InfoReply );
+ ircAccount()->setCurrentCommandSource(0);
+ }
+}
+
+void IRCUserContact::whoWasComplete()
+{
+ if( isChatting() && ircAccount()->currentCommandSource() == manager() )
+ {
+ //User info
+ QString msg = i18n("%1 was (%2@%3): %4\n")
+ .arg(m_nickName)
+ .arg(mInfo.userName)
+ .arg(mInfo.hostName)
+ .arg(mInfo.realName);
+
+ msg += i18n("Last Online: %1\n").arg(
+ KGlobal::locale()->formatDateTime(
+ property( m_protocol->propLastSeen ).value().toDateTime()
+ )
+ );
+
+ ircAccount()->appendMessage(msg, IRCAccount::InfoReply );
+ ircAccount()->setCurrentCommandSource(0);
+ }
+}
+
+QString IRCUserContact::formattedName() const
+{
+ return mInfo.realName;
+}
+
+void IRCUserContact::updateInfo()
+{
+ setProperty( m_protocol->propUserInfo, QString::fromLatin1("%1@%2")
+ .arg(mInfo.userName).arg(mInfo.hostName) );
+ setProperty( m_protocol->propServer, mInfo.serverName );
+ setProperty( m_protocol->propChannels, mInfo.channels.join(" ") );
+ setProperty( m_protocol->propHops, QString::number(mInfo.hops) );
+ setProperty( m_protocol->propFullName, mInfo.realName );
+
+ setIdleTime( mInfo.idle );
+
+ mInfo.lastUpdate = QTime::currentTime();
+}
+
+void IRCUserContact::newWhoReply( const QString &channel, const QString &user, const QString &host,
+ const QString &server, bool away, const QString &flags, uint hops, const QString &realName )
+{
+ if( !mInfo.channels.contains( channel ) )
+ mInfo.channels.append( channel );
+
+ mInfo.userName = user;
+ mInfo.hostName = host;
+ mInfo.serverName = server;
+ mInfo.flags = flags;
+ mInfo.hops = hops;
+ mInfo.realName = realName;
+
+ setAway(away);
+
+ updateInfo();
+
+ if( isChatting() && ircAccount()->currentCommandSource() == manager() )
+ {
+ ircAccount()->setCurrentCommandSource(0);
+ }
+}
+
+QPtrList<KAction> *IRCUserContact::customContextMenuActions( Kopete::ChatSession *manager )
+{
+ if( manager )
+ {
+ QPtrList<KAction> *mCustomActions = new QPtrList<KAction> ();
+ mActiveManager = manager;
+ Kopete::ContactPtrList members = mActiveManager->members();
+ IRCChannelContact *isChannel = dynamic_cast<IRCChannelContact*>( members.first() );
+
+ if( !actionCtcpMenu )
+ {
+ actionCtcpMenu = new KActionMenu(i18n("C&TCP"), 0, this );
+ actionCtcpMenu->insert( new KAction(i18n("&Version"), 0, this,
+ SLOT(slotCtcpVersion()), actionCtcpMenu) );
+ actionCtcpMenu->insert( new KAction(i18n("&Ping"), 0, this,
+ SLOT(slotCtcpPing()), actionCtcpMenu) );
+
+ actionModeMenu = new KActionMenu(i18n("&Modes"), 0, this, "actionModeMenu");
+ actionModeMenu->insert( new KAction(i18n("&Op"), 0, this,
+ SLOT(slotOp()), actionModeMenu, "actionOp") );
+ actionModeMenu->insert( new KAction(i18n("&Deop"), 0, this,
+ SLOT(slotDeop()), actionModeMenu, "actionDeop") );
+ actionModeMenu->insert( new KAction(i18n("&Voice"), 0, this,
+ SLOT(slotVoice()), actionModeMenu, "actionVoice") );
+ actionModeMenu->insert( new KAction(i18n("Devoice"), 0, this,
+ SLOT(slotDevoice()), actionModeMenu, "actionDevoice") );
+ actionModeMenu->setEnabled( false );
+
+ actionKick = new KAction(i18n("&Kick"), 0, this, SLOT(slotKick()), this);
+ actionKick->setEnabled( false );
+
+ actionBanMenu = new KActionMenu(i18n("&Ban"), 0, this, "actionBanMenu");
+ actionBanMenu->insert( new KAction(i18n("Host (*!*@host.domain.net)"), 0, this,
+ SLOT(slotBanHost()), actionBanMenu ) );
+ actionBanMenu->insert( new KAction(i18n("Domain (*!*@*.domain.net)"), 0, this,
+ SLOT(slotBanDomain()), actionBanMenu ) );
+ actionBanMenu->insert( new KAction(i18n("User@Host (*!*user@host.domain.net)"), 0, this,
+ SLOT(slotBanUserHost()), actionBanMenu ) );
+ actionBanMenu->insert( new KAction(i18n("User@Domain (*!*user@*.domain.net)"), 0, this,
+ SLOT(slotBanUserDomain()), actionBanMenu ) );
+ actionBanMenu->setEnabled( false );
+
+ codecAction = new KCodecAction( i18n("&Encoding"), 0, this, "selectcharset" );
+ connect( codecAction, SIGNAL( activated( const QTextCodec * ) ),
+ this, SLOT( setCodec( const QTextCodec *) ) );
+ codecAction->setCodec( codec() );
+ }
+
+ mCustomActions->append( actionCtcpMenu );
+ mCustomActions->append( actionModeMenu );
+ mCustomActions->append( actionKick );
+ mCustomActions->append( actionBanMenu );
+ mCustomActions->append( codecAction );
+
+ if( isChannel )
+ {
+ bool isOperator = ( manager->contactOnlineStatus( account()->myself() ).internalStatus() & IRCProtocol::Operator );
+ actionModeMenu->setEnabled(isOperator);
+ actionBanMenu->setEnabled(isOperator);
+ actionKick->setEnabled(isOperator);
+ }
+
+ return mCustomActions;
+ }
+
+ mActiveManager = 0L;
+
+ return 0L;
+}
+
+void IRCUserContact::slotIncomingModeChange( const QString &channel, const QString &, const QString &mode_ )
+{
+ kdDebug(14120) << k_funcinfo << "channel: " << channel << " mode: " << mode_ << endl;
+
+ IRCChannelContact *chan = ircAccount()->contactManager()->findChannel( channel );
+
+ if( !chan->locateUser( m_nickName ) )
+ return;
+
+ // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o kakkonen
+ // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o-o foobar001 kakkonen
+ // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +oo kakkonen foobar001
+ // :foobar_!~fooobar@dhcp.inet.fi MODE #foofoofoo2 +o-ov foobar001 kakkonen foobar001
+ //
+ // irssi manual example: /MODE #channel +nto-o+v nick1 nick2 nick3
+
+ QStringList users = QStringList::split(' ', mode_);
+ users.pop_front();
+
+ const QString mode = mode_.section(' ', 0, 0);
+
+ bitAdjustment adjMode = RemoveBits;
+ QStringList::iterator user = users.begin();
+
+ //kdDebug(14120) << "me: " << m_nickName << " users: " << users << " mode: " << mode << endl;
+
+ for( uint i=0; i < mode.length(); i++ )
+ {
+ switch( mode[i] )
+ {
+ case '+':
+ adjMode = AddBits;
+ break;
+
+ case '-':
+ adjMode = RemoveBits;
+ break;
+
+ default:
+ //kdDebug(14120) << "got " << mode[i] << ", user: " << *user << endl;
+
+ if (mode[i] == 'o') {
+ if (user == users.end())
+ return;
+
+ if ((*user).lower() == m_nickName.lower())
+ adjustInternalOnlineStatusBits(chan, IRCProtocol::Operator, adjMode);
+
+ ++user;
+ }
+ else if (mode[i] == 'v') {
+ if (user == users.end())
+ return;
+
+ if ((*user).lower() == m_nickName.lower())
+ adjustInternalOnlineStatusBits(chan, IRCProtocol::Voiced, adjMode);
+
+ ++user;
+ }
+
+ break;
+ }
+ }
+}
+
+
+/* Remove or add the given bits for the given channel from the current internal online status.
+ *
+ * You could fiddle with bits like IRCProtocol::Operator, IRCProtocol::Voiced, etc.
+ */
+
+void IRCUserContact::adjustInternalOnlineStatusBits(IRCChannelContact *channel, unsigned statusAdjustment, bitAdjustment adj)
+{
+ Kopete::OnlineStatus currentStatus = channel->manager()->contactOnlineStatus(this);
+ Kopete::OnlineStatus newStatus;
+
+ if (adj == RemoveBits) {
+
+ // If the bit is not set in the current internal status, stop here.
+ if ((currentStatus.internalStatus() & ~statusAdjustment) == currentStatus.internalStatus())
+ return;
+
+ newStatus = m_protocol->statusLookup(
+ (IRCProtocol::IRCStatus)(currentStatus.internalStatus() & ~statusAdjustment)
+ );
+
+ } else if (adj == AddBits) {
+
+ // If the bit is already set in the current internal status, stop here.
+ if ((currentStatus.internalStatus() | statusAdjustment) == currentStatus.internalStatus())
+ return;
+
+ newStatus = m_protocol->statusLookup(
+ (IRCProtocol::IRCStatus)(currentStatus.internalStatus() | statusAdjustment)
+ );
+
+ }
+
+ channel->manager()->setContactOnlineStatus(this, newStatus);
+}
+
+void IRCUserContact::privateMessage(IRCContact *from, IRCContact *to, const QString &message)
+{
+ if (to == this)
+ {
+ if(to==account()->myself())
+ {
+ Kopete::Message msg(from, from->manager(Kopete::Contact::CanCreate)->members(), message,
+ Kopete::Message::Inbound, Kopete::Message::RichText, CHAT_VIEW);
+ from->appendMessage(msg);
+ }
+ else
+ {
+ kdDebug(14120) << "IRC Server error: Received a private message for " << to->nickName() << ":" << message << endl;
+ // emit/call something on main ircservercontact
+ }
+ }
+}
+
+void IRCUserContact::newAction(const QString &to, const QString &action)
+{
+ IRCAccount *account = ircAccount();
+
+ IRCContact *t = account->contactManager()->findUser(to);
+
+ Kopete::Message::MessageDirection dir =
+ (this == account->mySelf()) ? Kopete::Message::Outbound : Kopete::Message::Inbound;
+ Kopete::Message msg(this, t, action, dir, Kopete::Message::RichText,
+ CHAT_VIEW, Kopete::Message::TypeAction);
+
+ //Either this is from me to a guy, or from a guy to me. Either way its a PM
+ if (dir == Kopete::Message::Outbound)
+ t->appendMessage(msg);
+ else
+ appendMessage(msg);
+}
+
+#include "ircusercontact.moc"
diff --git a/kopete/protocols/irc/ircusercontact.h b/kopete/protocols/irc/ircusercontact.h
new file mode 100644
index 00000000..3373fa9c
--- /dev/null
+++ b/kopete/protocols/irc/ircusercontact.h
@@ -0,0 +1,146 @@
+/*
+ ircusercontact.h - IRC User Contact
+
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef IRCUSERCONTACT_H
+#define IRCUSERCONTACT_H
+
+#include "kopetechatsessionmanager.h"
+#include "irccontact.h"
+#include "kopeteonlinestatus.h"
+
+class QTimer;
+
+class KActionCollection;
+class KAction;
+class KActionMenu;
+class KCodecAction;
+
+class IRCContactManager;
+class IRCChannelContact;
+
+struct IRCUserInfo
+{
+ QString userName;
+ QString hostName;
+ QString realName;
+ QString serverName;
+ QString serverInfo;
+ QString flags;
+ QStringList channels;
+ unsigned long idle;
+ bool isOperator;
+ bool isIdentified;
+ bool away;
+ bool online;
+ uint hops;
+ QDateTime lastOnline;
+ QTime lastUpdate;
+};
+
+/**
+ * @author Jason Keirstead <jason@keirstead.org
+ *
+ * This class is the @ref Kopete::Contact object representing IRC Users, not channels.
+ * It is derrived from IRCContact where much of its functionality is shared with @ref IRCChannelContact.
+ */
+class IRCUserContact : public IRCContact
+{
+ Q_OBJECT
+
+public:
+ // This class provides a Kopete::Contact for each user on the channel.
+ IRCUserContact(IRCContactManager *, const QString &nickname, Kopete::MetaContact *mc);
+
+ // Kopete::Contact stuff
+ virtual QPtrList<KAction> *customContextMenuActions( Kopete::ChatSession *manager );
+ virtual const QString caption() const;
+
+ void setAway(bool isAway);
+
+ QString formattedName() const;
+
+ //Methods handled by the signal mapper
+ void incomingUserIsAway(const QString &message );
+ void userOnline();
+ void newAction( const QString &from, const QString &action );
+ void newWhoIsUser(const QString &username, const QString &hostname, const QString &realname);
+ void newWhoIsServer(const QString &server, const QString &serverInfo);
+ void newWhoIsOperator();
+ void newWhoIsIdentified();
+ void newWhoIsIdle(unsigned long seconds);
+ void newWhoIsChannels(const QString &channel);
+ void whoIsComplete();
+ void whoWasComplete();
+ void newWhoReply( const QString &channel, const QString &user, const QString &host,
+ const QString &server, bool away, const QString &flags, uint hops,
+ const QString &realName );
+
+public slots:
+ /** \brief Updates online status for channels based on current internal status.
+ */
+ virtual void updateStatus();
+
+ virtual void sendFile(const KURL &sourceURL, const QString&, unsigned int);
+
+protected slots:
+ virtual void privateMessage(IRCContact *from, IRCContact *to, const QString &message);
+
+private slots:
+ void slotOp();
+ void slotDeop();
+ void slotVoice();
+ void slotDevoice();
+ void slotCtcpPing();
+ void slotCtcpVersion();
+ void slotBanHost();
+ void slotBanUserHost();
+ void slotBanDomain();
+ void slotBanUserDomain();
+ void slotKick();
+ void slotUserOffline();
+
+ void slotBanHostOnce();
+ void slotBanUserHostOnce();
+ void slotBanDomainOnce();
+ void slotBanUserDomainOnce();
+
+ virtual void slotUserInfo();
+
+ //This can't be handled by the contact manager since
+ void slotIncomingModeChange(const QString &nick, const QString &channel, const QString &mode);
+
+private:
+ enum bitAdjustment { RemoveBits, AddBits };
+ void adjustInternalOnlineStatusBits(IRCChannelContact *channel, unsigned statusAdjustment, bitAdjustment adj);
+
+ void contactMode(const QString &mode);
+ void updateInfo();
+
+ KActionMenu *actionModeMenu;
+ KActionMenu *actionCtcpMenu;
+ KAction *actionKick;
+ KActionMenu *actionBanMenu;
+ KCodecAction *codecAction;
+ Kopete::ChatSession *mActiveManager;
+ QTimer *mOnlineTimer;
+ IRCUserInfo mInfo;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
diff --git a/kopete/protocols/irc/kcodecaction.cpp b/kopete/protocols/irc/kcodecaction.cpp
new file mode 100644
index 00000000..e32a1787
--- /dev/null
+++ b/kopete/protocols/irc/kcodecaction.cpp
@@ -0,0 +1,87 @@
+/*
+ kcodecaction.cpp
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <kcharsets.h>
+
+#include "kcodecaction.h"
+
+KCodecAction::KCodecAction( const QString &text, const KShortcut &cut,
+ QObject *parent, const char *name ) : KSelectAction( text, "", cut, parent, name )
+{
+ QObject::connect( this, SIGNAL( activated( const QString & ) ),
+ this, SLOT( slotActivated( const QString & ) ) );
+
+ setItems( KCodecAction::supportedEncodings() );
+}
+
+void KCodecAction::slotActivated( const QString & text )
+{
+ /* text is something like "Western European ( iso-8859-1 )", but we must give
+ * codecForName() only the "iso-8859-1" part.
+ */
+ QString encoding = KGlobal::charsets()->encodingForName(text);
+
+ emit activated( KGlobal::charsets()->codecForName(encoding) );
+}
+
+void KCodecAction::setCodec( const QTextCodec *codec )
+{
+ QStringList items = this->items();
+ int i = 0;
+ for (QStringList::ConstIterator it = items.begin(), end = items.end(); it != end; ++it, ++i) {
+ QString encoding = KGlobal::charsets()->encodingForName(*it);
+
+ if (KGlobal::charsets()->codecForName(encoding)->mibEnum() == codec->mibEnum()) {
+ setCurrentItem(i);
+ break;
+ }
+ }
+}
+
+/* Create a list of supported encodings, and keep only one of each encoding
+ * mime name.
+ *
+ * This piece of code from kdepim/kmail/kmmsgbase.cpp
+ */
+
+QStringList KCodecAction::supportedEncodings(bool usAscii)
+{
+ QStringList encodingNames = KGlobal::charsets()->availableEncodingNames();
+ QStringList encodings;
+ QMap<QString, bool> mimeNames;
+
+ for (QStringList::ConstIterator it = encodingNames.begin();
+ it != encodingNames.end(); ++it)
+ {
+ QTextCodec *codec = KGlobal::charsets()->codecForName(*it);
+ QString mimeName = (codec) ? QString(codec->mimeName()).lower() : (*it);
+ if (mimeNames.find(mimeName) == mimeNames.end())
+ {
+ encodings.append(KGlobal::charsets()->languageForEncoding(*it)
+ + " ( " + mimeName + " )");
+ mimeNames.insert(mimeName, true);
+ }
+ }
+
+ encodings.sort();
+ if (usAscii) encodings.prepend(KGlobal::charsets()
+ ->languageForEncoding("us-ascii") + " ( us-ascii )");
+ return encodings;
+}
+
+#include "kcodecaction.moc"
diff --git a/kopete/protocols/irc/kcodecaction.h b/kopete/protocols/irc/kcodecaction.h
new file mode 100644
index 00000000..93f9d6c1
--- /dev/null
+++ b/kopete/protocols/irc/kcodecaction.h
@@ -0,0 +1,47 @@
+/*
+ kcodecaction.h
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef KCODECACTION_H
+#define KCODECACTION_H
+
+#include <kdeversion.h>
+#include <qintdict.h>
+
+#if KDE_IS_VERSION( 3, 1, 90 )
+ #include <kactionclasses.h>
+#else
+ #include <kaction.h>
+#endif
+
+class KCodecAction : public KSelectAction
+{
+ Q_OBJECT
+ public:
+ KCodecAction( const QString &text, const KShortcut &cut = KShortcut(),
+ QObject *parent = 0, const char *name = 0 );
+
+ void setCodec( const QTextCodec *codec );
+
+ static QStringList supportedEncodings( bool usAscii = false );
+
+ signals:
+ void activated( const QTextCodec * );
+
+ private slots:
+ void slotActivated( const QString & );
+};
+
+#endif
diff --git a/kopete/protocols/irc/kopete_irc.desktop b/kopete/protocols/irc/kopete_irc.desktop
new file mode 100644
index 00000000..6e3cf144
--- /dev/null
+++ b/kopete/protocols/irc/kopete_irc.desktop
@@ -0,0 +1,79 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=irc_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_irc
+X-Kopete-Messaging-Protocol=messaging/irc
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_irc
+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=IRC
+Name[bn]=আই-আর-সি
+Name[hi]=आईआरसी
+Name[ne]=आइआरसी
+Comment=Protocol to connect to IRC
+Comment[ar]=البروتوكل سيتصل بـ IRC
+Comment[be]=Пратакол IRC
+Comment[bg]=Протокол за връзка Ñ IRC
+Comment[bn]=আই-আর-সিতে সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh IRC
+Comment[bs]=IRC protokol
+Comment[ca]=Protocol per a connectar-se a l'IRC
+Comment[cs]=Protokol k připojení k IRC
+Comment[cy]=Protocol i gysylltu ag IRC
+Comment[da]=Protokol til at forbinde til IRC
+Comment[de]=Protokoll zur Verbindung mit IRC
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο IRC
+Comment[es]=Protocolo de conexión al IRC
+Comment[et]=Protokoll ühendumiseks IRC-ga
+Comment[eu]=IRC-ra konektatzeko protokoloa
+Comment[fa]=قرار داد برای اتصال به IRC
+Comment[fi]=Yhteyskäytänötö IRC-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur IRC
+Comment[ga]=Prótacal chun ceangal le IRC
+Comment[gl]=Protocolo para conectar a IRC
+Comment[he]=פרוטוקול התחברות ל- IRC
+Comment[hi]=आईआरसी से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na IRC
+Comment[hu]=Protokoll az IRC használatához
+Comment[is]=Samskiptamáti til að tengjast IRC
+Comment[it]=Protocollo per connessione a IRC
+Comment[ja]=IRC ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=IRC-თáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=IRC-ге қоÑылу протоколы
+Comment[km]=ពិធីការ​​ភ្ជាប់​ទៅ IRC
+Comment[lt]=Protokolas prisijungimui prie IRC
+Comment[mk]=Протокол за поврзување на IRC
+Comment[nb]=Protokoll for å koble til IRC
+Comment[nds]=Protokoll för't Tokoppeln na IRC
+Comment[ne]=आइआरसी मा जडान गरà¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor Internet Relay Chat (IRC)
+Comment[nn]=Protokoll for å kopla til IRC
+Comment[pl]=Protokół połączenia z serwerem IRC
+Comment[pt]=Protocolo para ligar ao IRC
+Comment[pt_BR]=Protocolo de conexão ao IRC
+Comment[ro]=Protocol de conectare la IRC
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº IRC
+Comment[sk]=Protokol pre pripojenie k IRC
+Comment[sl]=Protokol za povezavo na IRC
+Comment[sr]=Протокол за повезивање на IRC
+Comment[sr@Latn]=Protokol za povezivanje na IRC
+Comment[sv]=Protokoll för att ansluta till IRC
+Comment[ta]=IRC உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба IRC
+Comment[tr]=IRC'ye bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· IRC
+Comment[uz]=IRC bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=IRC билан алоқа ўрнатиш учун протокол
+Comment[wa]=Protocole po s' raloyî so les canås IRC
+Comment[zh_CN]=连接到 IRC åè®®
+Comment[zh_HK]=用來連接至 IRC 的通訊å”定
+Comment[zh_TW]=連線到 IRC çš„å”定
+
diff --git a/kopete/protocols/irc/ksparser.cpp b/kopete/protocols/irc/ksparser.cpp
new file mode 100644
index 00000000..c101a79e
--- /dev/null
+++ b/kopete/protocols/irc/ksparser.cpp
@@ -0,0 +1,265 @@
+/* This file is part of ksirc
+ Copyright (c) 2001 Malte Starostik <malte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/*
+Color parser code courtesy of ksirc <http://www.kde.org>
+Modified by Jason Keirstead <jason@keirstead.org>
+*/
+
+#include <knotifyclient.h>
+#include <kdebug.h>
+#include <qbuffer.h>
+#include <qdatastream.h>
+#include <qstylesheet.h>
+#include "ksparser.h"
+#include <stdlib.h>
+
+KSParser KSParser::m_parser;
+
+const QColor KSParser::IRC_Colors[17]=
+{
+ Qt::white,
+ Qt::black,
+ Qt::darkBlue,
+ Qt::darkGreen,
+ Qt::red,
+ Qt::darkRed,
+ Qt::darkMagenta,
+ Qt::darkYellow,
+ Qt::yellow,
+ Qt::green,
+ Qt::darkCyan,
+ Qt::cyan,
+ Qt::blue,
+ Qt::magenta,
+ Qt::darkGray,
+ Qt::gray,
+ QColor() // default invalid color, must be the last
+};
+
+const QRegExp KSParser::sm_colorsModeRegexp("(\\d{1,2})(?:,(\\d{1,2}))?");
+
+template <typename _TYPE_>
+ inline void swap(_TYPE_ &o1, _TYPE_ &o2)
+{
+ _TYPE_ tmp = o1;
+ o1 = o2;
+ o2 = tmp;
+}
+
+KSParser::KSParser()
+{
+ kdDebug(14120) << k_funcinfo << endl;
+}
+
+KSParser::~KSParser()
+{
+ kdDebug(14120) << k_funcinfo << endl;
+}
+
+/* NOTE: If thread corruption are seen simply ad a qlock here */
+QCString KSParser::parse(const QCString &message)
+{
+ return m_parser._parse(message);
+}
+
+QCString KSParser::_parse(const QCString &message)
+{
+ QCString data( message.size() * 2 );
+ QBuffer buff( data );
+ buff.open( IO_WriteOnly );
+
+ m_tags.clear();
+ m_attributes.clear();
+
+ QRegExp colorsModeRegexp(sm_colorsModeRegexp);
+
+ // should be set to the current default colors ....
+ QColor fgColor; /*KopeteMesage::fg().name()*/
+ QColor bgColor; /*KopeteMesage::bg().name()*/
+
+ uint chars = 0;
+ for(uint i = 0; i < message.length(); ++i)
+ {
+ const QChar &cur = message[i];
+ QString toAppend;
+
+ switch (cur)
+ {
+ case 0x02: //Bold: ^B
+ toAppend= toggleTag("b");
+ break;
+ case 0x03: //Color code: ^C
+ if (colorsModeRegexp.search(message, i+1) == (int)i+1)
+ {
+ i += colorsModeRegexp.matchedLength(); // + 1 will be added by ++
+ QString tagStyle;
+
+ fgColor = ircColor(colorsModeRegexp.cap(1));
+ bgColor = ircColor(colorsModeRegexp.cap(2));
+
+ toAppend = pushColorTag(fgColor, bgColor);
+ }
+ else
+ {
+ toAppend = popTag(QString::fromLatin1("span"));
+ }
+ break;
+ case 0x07: //System bell: ^G
+ KNotifyClient::beep( QString::fromLatin1("IRC beep event received in a message") );
+ break;
+ case '\t': // 0x09
+ toAppend = QString::fromLatin1("&nbsp;&nbsp;&nbsp;&nbsp;");
+ break;
+ case '\n': // 0x0D
+ toAppend= QString::fromLatin1("<br/>");
+ break;
+ case 0x0D: // Italics: ^N
+ toAppend = toggleTag("i");
+ break;
+ case 0x0F: //Plain Text, close all tags: ^O
+ toAppend = popAll();
+ break;
+ // case 0x12: // Reverse original text colors: ^R
+ // break;
+ case 0x16: //Invert Colors: ^V
+ swap(fgColor, bgColor);
+ toAppend = pushColorTag(fgColor, bgColor);
+ break;
+ case 0x1F: //Underline
+ toAppend = toggleTag("u");
+ break;
+ case '<':
+ toAppend = QString::fromLatin1("&lt;");
+ break;
+ case '>':
+ toAppend = QString::fromLatin1("&gt;");
+ break;
+ default:
+ if (cur < QChar(' ')) // search for control characters
+ toAppend = QString::fromLatin1("&lt;%1&gt;").arg(cur, 2, 16).upper();
+ else
+ toAppend = QStyleSheet::escape(cur);
+ }
+
+ chars += toAppend.length();
+ buff.writeBlock( toAppend.latin1(), toAppend.length() );
+ }
+
+ QString toAppend = popAll();
+ chars += toAppend.length();
+ buff.writeBlock( toAppend.latin1(), toAppend.length() );
+
+ // Make sure we have enough room for NULL character.
+ if (data.size() < chars+1)
+ data.resize(chars+1);
+
+ data[chars] = '\0';
+
+ return data;
+}
+
+QString KSParser::pushTag(const QString &tag, const QString &attributes)
+{
+ QString res;
+ m_tags.push(tag);
+ if(!m_attributes.contains(tag))
+ m_attributes.insert(tag, attributes);
+ else if(!attributes.isEmpty())
+ m_attributes.replace(tag, attributes);
+ res.append("<" + tag);
+ if(!m_attributes[tag].isEmpty())
+ res.append(" " + m_attributes[tag]);
+ return res + ">";
+}
+
+QString KSParser::pushColorTag(const QColor &fgColor, const QColor &bgColor)
+{
+ QString tagStyle;
+
+ if (fgColor.isValid())
+ tagStyle += QString::fromLatin1("color:%1;").arg(fgColor.name());
+ if (bgColor.isValid())
+ tagStyle += QString::fromLatin1("background-color:%1;").arg(bgColor.name());
+
+ if(!tagStyle.isEmpty())
+ tagStyle = QString::fromLatin1("style=\"%1\"").arg(tagStyle);
+
+ return pushTag(QString::fromLatin1("span"), tagStyle);;
+}
+
+QString KSParser::popTag(const QString &tag)
+{
+ if (!m_tags.contains(tag))
+ return QString::null;
+
+ QString res;
+ QValueStack<QString> savedTags;
+ while(m_tags.top() != tag)
+ {
+ savedTags.push(m_tags.pop());
+ res.append("</" + savedTags.top() + ">");
+ }
+ res.append("</" + m_tags.pop() + ">");
+ m_attributes.remove(tag);
+ while(!savedTags.isEmpty())
+ res.append(pushTag(savedTags.pop()));
+ return res;
+}
+
+QString KSParser::toggleTag(const QString &tag)
+{
+ return m_attributes.contains(tag)?popTag(tag):pushTag(tag);
+}
+
+QString KSParser::popAll()
+{
+ QString res;
+ while(!m_tags.isEmpty())
+ res.append("</" + m_tags.pop() + ">");
+ m_attributes.clear();
+ return res;
+}
+
+QColor KSParser::ircColor(const QString &color)
+{
+ bool success;
+ unsigned int intColor = color.toUInt(&success);
+
+ if (success)
+ return ircColor(intColor);
+ else
+ return QColor();
+}
+
+QColor KSParser::ircColor(unsigned int color)
+{
+ unsigned int maxcolor = sizeof(IRC_Colors)/sizeof(QColor);
+ return color<=maxcolor?IRC_Colors[color]:IRC_Colors[maxcolor];
+}
+
+int KSParser::colorForHTML(const QString &html)
+{
+ QColor color(html);
+ for(uint i=0; i<sizeof(IRC_Colors)/sizeof(QColor); i++)
+ {
+ if(IRC_Colors[i] == color)
+ return i;
+ }
+ return -1;
+}
diff --git a/kopete/protocols/irc/ksparser.h b/kopete/protocols/irc/ksparser.h
new file mode 100644
index 00000000..dda7b7c1
--- /dev/null
+++ b/kopete/protocols/irc/ksparser.h
@@ -0,0 +1,56 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+#ifndef __ksparser_h__
+#define __ksparser_h__
+
+#include <qcolor.h>
+#include <qmap.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qvaluestack.h>
+
+/*
+ * Helper class to parse IRC color/style codes and convert them to
+ * richtext. The parser maintains an internal stack of the styles
+ * applied because the IRC message could contain sequences as
+ * (bold)Hello (red)World(endbold)! (blue)blue text
+ * which needs to be converted to
+ * <b>Hello </b><font color="red"><b>World</b>! </font><font color="blue">blue text</font>
+ * to get correctly nested tags. (malte)
+ */
+class KSParser
+{
+public:
+ static QCString parse(const QCString &);
+ static int colorForHTML( const QString &html );
+
+ static QColor ircColor(const QString &color);
+ static QColor ircColor(unsigned int color);
+
+ ~KSParser();
+private:
+ KSParser();
+
+ QCString _parse(const QCString &);
+ QString pushTag(const QString &, const QString & = QString::null);
+ QString pushColorTag(const QColor &fgColor, const QColor &bgColor);
+ QString popTag(const QString &);
+ QString toggleTag(const QString &);
+ QString popAll();
+
+private:
+ static KSParser m_parser;
+ static const QColor IRC_Colors[17];
+ static const QRegExp sm_colorsModeRegexp;
+
+ QValueStack<QString> m_tags;
+ QMap<QString, QString> m_attributes;
+};
+
+#endif
+
+
diff --git a/kopete/protocols/irc/libkirc/Makefile.am b/kopete/protocols/irc/libkirc/Makefile.am
new file mode 100644
index 00000000..e2ebe543
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/Makefile.am
@@ -0,0 +1,20 @@
+KDE_OPTIONS = nofinal
+noinst_LTLIBRARIES = libkirc.la
+
+libkirc_la_SOURCES = \
+ kircengine.cpp \
+ kircengine_commands.cpp \
+ kircengine_ctcp.cpp \
+ kircengine_numericreplies.cpp \
+ kircentity.cpp \
+ kircmessage.cpp \
+ kircmessageredirector.cpp \
+ kirctransfer.cpp \
+ kirctransferhandler.cpp \
+ kirctransferserver.cpp \
+ ksslsocket.cpp
+libkirc_la_LDFLAGS = -no-undefined $(KDE_PLUGIN) $(all_libraries)
+libkirc_la_LIBADD = $(LIB_KIO)
+
+AM_CPPFLAGS = -I$(top_srcdir)/kopete/protocols/irc $(KOPETE_INCLUDES) $(all_includes)
+METASOURCES = AUTO
diff --git a/kopete/protocols/irc/libkirc/kircengine.cpp b/kopete/protocols/irc/libkirc/kircengine.cpp
new file mode 100644
index 00000000..5b70d5fc
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircengine.cpp
@@ -0,0 +1,497 @@
+/*
+ kirc.cpp - IRC Client
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kircengine.h"
+#include "ksslsocket.h"
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kextsock.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include <qtextcodec.h>
+#include <qtimer.h>
+
+//Needed for getuid / getpwuid
+#include <unistd.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <kopetemessage.h>
+
+#ifndef KIRC_SSL_SUPPORT
+#define KIRC_SSL_SUPPORT
+#endif
+
+using namespace KIRC;
+
+// FIXME: Remove slotConnected() and error(int errCode) while going to KNetwork namespace
+
+/* Please note that the regular expression "[\\r\\n]*$" is used in a QString::replace statement many times.
+ * This gets rid of trailing \r\n, \r, \n, and \n\r characters.
+ */
+const QRegExp Engine::m_RemoveLinefeeds( QString::fromLatin1("[\\r\\n]*$") );
+
+Engine::Engine(QObject *parent, const char *name)
+ : QObject(parent, QString::fromLatin1("[KIRC::Engine]%1").arg(name).latin1()),
+ m_status(Idle),
+ m_FailedNickOnLogin(false),
+ m_useSSL(false),
+ m_commands(101, false),
+// m_numericCommands(101),
+ m_ctcpQueries(17, false),
+ m_ctcpReplies(17, false),
+ codecs(577,false)
+{
+ setUserName(QString::null);
+
+ m_commands.setAutoDelete(true);
+ m_ctcpQueries.setAutoDelete(true);
+ m_ctcpReplies.setAutoDelete(true);
+
+ bindCommands();
+ bindNumericReplies();
+ bindCtcp();
+
+ m_VersionString = QString::fromLatin1("Anonymous client using the KIRC engine.");
+ m_UserString = QString::fromLatin1("Response not supplied by user.");
+ m_SourceString = QString::fromLatin1("Unknown client, known source.");
+
+ defaultCodec = QTextCodec::codecForMib(106); // UTF8 mib is 106
+ kdDebug(14120) << "Setting default engine codec, " << defaultCodec->name() << endl;
+
+ m_sock = 0L;
+}
+
+Engine::~Engine()
+{
+ kdDebug(14120) << k_funcinfo << m_Host << endl;
+ quit("KIRC Deleted", true);
+ if( m_sock )
+ delete m_sock;
+}
+
+void Engine::setUseSSL( bool useSSL )
+{
+ kdDebug(14120) << k_funcinfo << useSSL << endl;
+
+ if( !m_sock || useSSL != m_useSSL )
+ {
+ if( m_sock )
+ delete m_sock;
+
+ m_useSSL = useSSL;
+
+
+ if( m_useSSL )
+ {
+ #ifdef KIRC_SSL_SUPPORT
+ m_sock = new KSSLSocket;
+ m_sock->setSocketFlags( KExtendedSocket::inetSocket );
+
+ connect(m_sock, SIGNAL(certificateAccepted()), SLOT(slotConnected()));
+ connect(m_sock, SIGNAL(certificateRejected()), SLOT(slotConnectionClosed()));
+ connect(m_sock, SIGNAL(sslFailure()), SLOT(slotConnectionClosed()));
+ }
+ else
+ #else
+ kdWarning(14120) << "You tried to use SSL, but this version of Kopete was"
+ " not compiled with IRC SSL support. A normal IRC connection will be attempted." << endl;
+ }
+ #endif
+ {
+ m_sock = new KExtendedSocket;
+ m_sock->setSocketFlags( KExtendedSocket::inputBufferedSocket | KExtendedSocket::inetSocket );
+
+ connect(m_sock, SIGNAL(connectionSuccess()), SLOT(slotConnected()));
+ connect(m_sock, SIGNAL(connectionFailed(int)), SLOT(error(int)));
+ }
+
+ connect(m_sock, SIGNAL(closed(int)), SLOT(slotConnectionClosed()));
+ connect(m_sock, SIGNAL(readyRead()), SLOT(slotReadyRead()));
+ }
+}
+
+void Engine::setStatus(Engine::Status status)
+{
+ kdDebug(14120) << k_funcinfo << status << endl;
+
+ if (m_status == status)
+ return;
+
+// Engine::Status oldStatus = m_status;
+ m_status = status;
+ emit statusChanged(status);
+
+ switch (m_status)
+ {
+ case Idle:
+ // Do nothing.
+ break;
+ case Connecting:
+ // Do nothing.
+ break;
+ case Authentifying:
+ m_sock->enableRead(true);
+
+ // If password is given for this server, send it now, and don't expect a reply
+ if (!(password()).isEmpty())
+ pass(password());
+
+ user(m_Username, 0, m_realName);
+ nick(m_Nickname);
+
+ break;
+ case Connected:
+ // Do nothing.
+ break;
+ case Closing:
+ m_sock->close();
+ m_sock->reset();
+ setStatus(Idle);
+ break;
+ case AuthentifyingFailed:
+ setStatus(Closing);
+ break;
+ case Timeout:
+ setStatus(Closing);
+ break;
+ case Disconnected:
+ setStatus(Closing);
+ break;
+ }
+}
+
+void Engine::connectToServer(const QString &host, Q_UINT16 port, const QString &nickname, bool useSSL )
+{
+ setUseSSL(useSSL);
+
+ m_Nickname = nickname;
+ m_Host = host;
+ m_Port = port;
+
+ kdDebug(14120) << "Trying to connect to server " << m_Host << ":" << m_Port << endl;
+ kdDebug(14120) << "Sock status: " << m_sock->socketStatus() << endl;
+
+ if( !m_sock->setAddress(m_Host, m_Port) )
+ kdDebug(14120) << k_funcinfo << "setAddress failed. Status: " << m_sock->socketStatus() << endl;
+
+ if( m_sock->startAsyncConnect() == 0 )
+ {
+ kdDebug(14120) << k_funcinfo << "Success!. Status: " << m_sock->socketStatus() << endl;
+ setStatus(Connecting);
+ }
+ else
+ {
+ kdDebug(14120) << k_funcinfo << "Failed. Status: " << m_sock->socketStatus() << endl;
+ setStatus(Disconnected);
+ }
+}
+
+void Engine::slotConnected()
+{
+ setStatus(Authentifying);
+}
+
+void Engine::slotConnectionClosed()
+{
+ setStatus(Disconnected);
+}
+
+void Engine::error(int errCode)
+{
+ kdDebug(14120) << k_funcinfo << "Socket error: " << errCode << endl;
+ if (m_sock->socketStatus () != KExtendedSocket::connecting)
+ {
+ // Connection in progress.. This is a signal fired wrong
+ setStatus(Disconnected);
+ }
+}
+
+void Engine::setVersionString(const QString &newString)
+{
+ m_VersionString = newString;
+ m_VersionString.remove(m_RemoveLinefeeds);
+}
+
+void Engine::setUserString(const QString &newString)
+{
+ m_UserString = newString;
+ m_UserString.remove(m_RemoveLinefeeds);
+}
+
+void Engine::setSourceString(const QString &newString)
+{
+ m_SourceString = newString;
+ m_SourceString.remove(m_RemoveLinefeeds);
+}
+
+void Engine::setUserName(const QString &newName)
+{
+ if(newName.isEmpty())
+ m_Username = QString::fromLatin1(getpwuid(getuid())->pw_name);
+ else
+ m_Username = newName;
+ m_Username.remove(m_RemoveLinefeeds);
+}
+
+void Engine::setRealName(const QString &newName)
+{
+ if(newName.isEmpty())
+ m_realName = QString::fromLatin1(getpwuid(getuid())->pw_gecos);
+ else
+ m_realName = newName;
+ m_realName.remove(m_RemoveLinefeeds);
+}
+
+bool Engine::_bind(QDict<KIRC::MessageRedirector> &dict,
+ QString command, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage)
+{
+// FIXME: Force upper case.
+// FIXME: Force number format.
+
+ MessageRedirector *mr = dict[command];
+
+ if (!mr)
+ {
+ mr = new MessageRedirector(this, minArgs, maxArgs, helpMessage);
+ dict.replace(command, mr);
+ }
+
+ return mr->connect(object, member);
+}
+
+bool Engine::bind(const QString &command, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage)
+{
+ return _bind(m_commands, command, object, member,
+ minArgs, maxArgs, helpMessage);
+}
+
+bool Engine::bind(int id, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage)
+{
+ return _bind(m_commands, QString::number(id), object, member,
+ minArgs, maxArgs, helpMessage);
+}
+
+bool Engine::bindCtcpQuery(const QString &command, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage)
+{
+ return _bind(m_ctcpQueries, command, object, member,
+ minArgs, maxArgs, helpMessage);
+}
+
+bool Engine::bindCtcpReply(const QString &command, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage)
+{
+ return _bind(m_ctcpReplies, command, object, member,
+ minArgs, maxArgs, helpMessage);
+}
+
+/* Message will be send as passed.
+ */
+void Engine::writeRawMessage(const QString &rawMsg)
+{
+ Message::writeRawMessage(this, defaultCodec, rawMsg);
+}
+
+/* Message will be quoted before beeing send.
+ */
+void Engine::writeMessage(const QString &msg, const QTextCodec *codec)
+{
+ Message::writeMessage(this, codec ? codec : defaultCodec, msg);
+}
+
+void Engine::writeMessage(const QString &command, const QStringList &args, const QString &suffix, const QTextCodec *codec)
+{
+ Message::writeMessage(this, codec ? codec : defaultCodec, command, args, suffix );
+}
+
+void Engine::writeCtcpMessage(const QString &command, const QString &to, const QString &ctcpMessage)
+{
+ Message::writeCtcpMessage(this, defaultCodec, command, to, ctcpMessage);
+}
+
+void Engine::writeCtcpMessage(const QString &command, const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix, bool )
+{
+ QString nick = Entity::userNick(to);
+
+ Message::writeCtcpMessage(this, codecForNick( nick ), command, nick, suffix,
+ ctcpCommand, ctcpArgs, ctcpSuffix );
+}
+
+void Engine::slotReadyRead()
+{
+ // This condition is buggy when the peer server
+ // close the socket unexpectedly
+ bool parseSuccess;
+
+ if (m_sock->socketStatus() == KExtendedSocket::connected && m_sock->canReadLine())
+ {
+ Message msg = Message::parse(this, defaultCodec, &parseSuccess);
+ if (parseSuccess)
+ {
+ emit receivedMessage(msg);
+
+ KIRC::MessageRedirector *mr;
+ if (msg.isNumeric())
+// mr = m_numericCommands[ msg.command().toInt() ];
+ // we do this conversion because some dummy servers sends 1 instead of 001
+ // numbers are stored as "1" instead of "001" to make convertion faster (no 0 pading).
+ mr = m_commands[ QString::number(msg.command().toInt()) ];
+ else
+ mr = m_commands[ msg.command() ];
+
+ if (mr)
+ {
+ QStringList errors = mr->operator()(msg);
+
+ if (!errors.isEmpty())
+ {
+ kdDebug(14120) << "Method error for line:" << msg.raw() << endl;
+ emit internalError(MethodFailed, msg);
+ }
+ }
+ else if (msg.isNumeric())
+ {
+ kdWarning(14120) << "Unknown IRC numeric reply for line:" << msg.raw() << endl;
+ emit incomingUnknown(msg.raw());
+ }
+ else
+ {
+ kdWarning(14120) << "Unknown IRC command for line:" << msg.raw() << endl;
+ emit internalError(UnknownCommand, msg);
+ }
+ }
+ else
+ {
+ emit incomingUnknown(msg.raw());
+ emit internalError(ParsingFailed, msg);
+ }
+
+ QTimer::singleShot( 0, this, SLOT( slotReadyRead() ) );
+ }
+
+ if(m_sock->socketStatus() != KExtendedSocket::connected)
+ error();
+}
+
+const QTextCodec *Engine::codecForNick( const QString &nick ) const
+{
+ if( nick.isEmpty() )
+ return defaultCodec;
+
+ QTextCodec *codec = codecs[ nick ];
+ kdDebug(14120) << nick << " has codec " << codec << endl;
+
+ if( !codec )
+ return defaultCodec;
+ else
+ return codec;
+}
+
+void Engine::showInfoDialog()
+{
+ if( m_useSSL )
+ {
+ static_cast<KSSLSocket*>( m_sock )->showInfoDialog();
+ }
+}
+
+/*
+ * The ctcp commands seems to follow the same message behaviours has normal IRC command.
+ * (Only missing the \n\r final characters)
+ * So applying the same parsing rules to the messages.
+ */
+bool Engine::invokeCtcpCommandOfMessage(const QDict<MessageRedirector> &map, Message &msg)
+{
+ if(msg.hasCtcpMessage() && msg.ctcpMessage().isValid())
+ {
+ Message &ctcpMsg = msg.ctcpMessage();
+
+ MessageRedirector *mr = map[ctcpMsg.command()];
+ if (mr)
+ {
+ QStringList errors = mr->operator()(msg);
+
+ if (errors.isEmpty())
+ return true;
+
+ kdDebug(14120) << "Method error for line:" << ctcpMsg.raw() << endl;
+ writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(),
+ QString::fromLatin1("%1 internal error(s)").arg(errors.size()));
+ }
+ else
+ {
+ kdDebug(14120) << "Unknow IRC/CTCP command for line:" << ctcpMsg.raw() << endl;
+ // Don't send error message on unknown CTCP command
+ // None of the client send it, and it makes the client as infected by virus for IRC network scanners
+ // writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(), "Unknown CTCP command");
+
+ emit incomingUnknownCtcp(msg.ctcpRaw());
+ }
+ }
+ else
+ {
+ kdDebug(14120) << "Message do not embed a CTCP message:" << msg.raw();
+ }
+ return false;
+}
+
+EntityPtr Engine::getEntity(const QString &name)
+{
+ Entity *entity = 0;
+
+ #pragma warning Do the searching code here.
+
+ if (!entity)
+ {
+ entity = new Entity(name);
+ m_entities.append(entity);
+ }
+
+ connect(entity, SIGNAL(destroyed(KIRC::Entity *)), SLOT(destroyed(KIRC::Entity *)));
+ return EntityPtr(entity);
+}
+
+void Engine::destroyed(KIRC::Entity *entity)
+{
+ m_entities.remove(entity);
+}
+
+void Engine::ignoreMessage(KIRC::Message &/*msg*/)
+{
+}
+
+void Engine::emitSuffix(KIRC::Message &msg)
+{
+ emit receivedMessage(InfoMessage, m_server, m_server, msg.suffix());
+}
+
+#include "kircengine.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/irc/libkirc/kircengine.h b/kopete/protocols/irc/libkirc/kircengine.h
new file mode 100644
index 00000000..50cb8f49
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircengine.h
@@ -0,0 +1,532 @@
+/*
+ kircengine.h - IRC Client
+
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCENGINE_H
+#define KIRCENGINE_H
+
+#include "kircentity.h"
+#include "kircmessage.h"
+#include "kircmessageredirector.h"
+#include "kirctransfer.h"
+
+#include <kdeversion.h>
+
+// FIXME: Move the following kdedebug class to the *.cpp.
+#include <kdebug.h>
+#if KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 )
+#include <kdebugclasses.h>
+#endif
+
+#include <qdatetime.h>
+#include <qdict.h>
+#include <qintdict.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+class QRegExp;
+
+namespace KIRC
+{
+
+/**
+ * @author Nick Betcher <nbetcher@kde.org>
+ * @author Michel Hermier <michel.hermier@wanadoo.fr>
+ * @author Jason Keirstead <jason@keirstead.org>
+ */
+class Engine
+ : public QObject
+{
+ Q_OBJECT
+
+// Q_PROPERTY(QUrl serverURL READ serverURL WRITE setServerURL)
+
+// Extracted from the base of the serverURL.
+// Q_PROPERTY(bool useSSL);
+// Q_PROPERTY(QString user READ user);
+// Q_PROPERTY(QString password);
+// Q_PROPERTY(QString host READ host);
+// Q_PROPERTY(int port READ host);
+
+// Extracted from the query of the serverURL.
+// Q_PROPERTY(bool reqsPasswd);
+// Q_PROPERTY(QString name); // real name
+// Q_PROPERTY(QStringList nickList READ nickList WRITE setNickList)
+// Q_PROPERTY(QString nick READ nick)
+// Q_PROPERTY(QStringList portList)
+
+ Q_ENUMS(Status)
+
+public:
+ enum Error
+ {
+ ParsingFailed,
+ UnknownCommand,
+ UnknownNumericReply,
+ InvalidNumberOfArguments,
+ MethodFailed
+ };
+
+ enum Status
+ {
+ Idle,
+ Connecting,
+ Authentifying,
+ Connected,
+ Closing,
+ AuthentifyingFailed,
+ Timeout,
+ Disconnected
+ };
+
+ enum ServerMessageType
+ {
+ ErrorMessage = -1,
+ PrivateMessage,
+ InfoMessage,
+
+ MessageOfTheDayMessage,
+ MessageOfTheDayCondensedMessage
+ };
+
+ Engine( QObject *parent = 0, const char* name = 0 );
+ ~Engine();
+
+// QString nick() const;
+// QStringList nickList() const;
+// void setNickList(const QStringList& nickList);
+
+// QUrl serverURL() const;
+// bool setServerURL(const QUrl &url);
+
+ inline const QString &currentHost() const
+ { return m_Host; };
+
+ inline Q_UINT16 currentPort()
+ { return m_Port; }
+
+ inline const QString &nickName() const
+ { return m_Nickname; };
+
+ inline const QString &password() const
+ { return m_Passwd; }
+
+ inline void setPassword(const QString &passwd)
+ { m_Passwd = passwd; };
+
+ inline const QString &userName() const
+ { return m_Username; }
+
+ void setUserName(const QString &newName);
+
+ void setRealName(const QString &newName);
+ inline const QString &realName() const
+ { return m_realName; }
+
+ inline const bool reqsPassword() const
+ { return m_ReqsPasswd; };
+
+ inline void setReqsPassword(bool b)
+ { m_ReqsPasswd = b; };
+
+ const bool useSSL() const { return m_useSSL; };
+ void setUseSSL( bool useSSL );
+
+ inline const QTextCodec *codec() const
+ { return defaultCodec; };
+
+ const QTextCodec *codecForNick( const QString &nick ) const;
+
+ inline void setDefaultCodec( QTextCodec* codec )
+ { defaultCodec = codec; };
+
+ void setVersionString(const QString &versionString);
+ void setUserString(const QString &userString);
+ void setSourceString(const QString &sourceString);
+ void connectToServer(const QString &host, Q_UINT16 port, const QString &nickname, bool useSSL = false);
+
+ KExtendedSocket *socket()
+ { return m_sock; };
+
+ inline KIRC::Engine::Status status() const
+ { return m_status; }
+
+ inline bool isDisconnected() const
+ { return m_status == Disconnected || m_status == Idle; }
+
+ inline bool isConnected() const
+ { return m_status == Connected; }
+
+ inline void setCodec( const QString &nick, const QTextCodec *codec )
+ { codecs.replace( nick, codec ); }
+
+ /* Custom CTCP replies handling */
+ inline QString &customCtcp( const QString &s )
+ { return customCtcpMap[s]; }
+
+ inline void addCustomCtcp( const QString &ctcp, const QString &reply )
+ { customCtcpMap[ ctcp.lower() ] = reply; }
+
+ KIRC::EntityPtr getEntity(const QString &name);
+
+public slots:
+ //Message output
+ void writeRawMessage(const QString &message);
+
+ void writeMessage(const QString &message, const QTextCodec *codec = 0 );
+ void writeMessage(const QString &command, const QStringList &args,
+ const QString &suffix = QString::null, const QTextCodec *codec = 0);
+
+ void writeCtcpMessage(const QString &command, const QString &to, const QString &ctcpMessage);
+
+ void writeCtcpMessage(const QString &command, const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix = QString::null,
+ bool emitRepliedCtcp = true);
+
+ inline void writeCtcpQueryMessage(const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null,
+ bool emitRepliedCtcp = true)
+ { return writeCtcpMessage("PRIVMSG", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); }
+
+ inline void writeCtcpReplyMessage(const QString &to, const QString &ctcpMessage)
+ { writeCtcpMessage("NOTICE", to, ctcpMessage); }
+
+ inline void writeCtcpReplyMessage(const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null,
+ bool emitRepliedCtcp = true)
+ { return writeCtcpMessage("NOTICE", to, suffix, ctcpCommand, ctcpArgs, ctcpSuffix, emitRepliedCtcp); }
+
+ inline void writeCtcpErrorMessage(const QString &to, const QString &ctcpLine, const QString &errorMsg,
+ bool emitRepliedCtcp=true)
+ { return writeCtcpReplyMessage(to, QString::null, "ERRMSG", ctcpLine, errorMsg, emitRepliedCtcp); }
+
+ bool bind(const QString &command, QObject *object, const char *member,
+ int minArgs = KIRC::MessageRedirector::Unknown,
+ int maxArgs = KIRC::MessageRedirector::Unknown,
+ const QString &helpMessage = QString::null);
+
+ bool bind(int id, QObject *object, const char *member,
+ int minArgs = KIRC::MessageRedirector::Unknown,
+ int maxArgs = KIRC::MessageRedirector::Unknown,
+ const QString &helpMessage = QString::null);
+
+ bool bindCtcpQuery(const QString &command, QObject *object, const char *member,
+ int minArgs = KIRC::MessageRedirector::Unknown,
+ int maxArgs = KIRC::MessageRedirector::Unknown,
+ const QString &helpMessage = QString::null);
+
+ bool bindCtcpReply(const QString &command, QObject *object, const char *member,
+ int minArgs = KIRC::MessageRedirector::Unknown,
+ int maxArgs = KIRC::MessageRedirector::Unknown,
+ const QString &helpMessage = QString::null);
+
+
+ void away(bool isAway, const QString &awayMessage = QString::null);
+ void ison(const QStringList &nickList);
+ void join(const QString &name, const QString &key);
+ void kick(const QString &user, const QString &channel, const QString &reason);
+ void list();
+ void mode(const QString &target, const QString &mode);
+ void motd(const QString &server = QString::null);
+ void nick(const QString &newNickname);
+ void notice(const QString &target, const QString &message);
+ void part(const QString &name, const QString &reason);
+ void pass(const QString &password);
+ void privmsg(const QString &contact, const QString &message);
+
+ /**
+ * Send a quit message for the given reason.
+ * If now is set to true the connection is closed and no event message is sent.
+ * Therefore setting now to true should only be used while destroying the object.
+ */
+ void quit(const QString &reason, bool now=false);
+
+ void topic(const QString &channel, const QString &topic);
+ void user(const QString &newUsername, const QString &hostname, const QString &newRealname);
+ void user(const QString &newUsername, Q_UINT8 mode, const QString &newRealname);
+ void whois(const QString &user);
+
+
+ /* CTCP commands */
+ void CtcpRequestCommand(const QString &contact, const QString &command);
+ void CtcpRequest_action(const QString &contact, const QString &message);
+ void CtcpRequest_dcc(const QString &, const QString &, unsigned int port, KIRC::Transfer::Type type);
+ void CtcpRequest_ping(const QString &target);
+ void CtcpRequest_version(const QString &target);
+
+public slots:
+ void showInfoDialog();
+
+signals:
+ void statusChanged(KIRC::Engine::Status newStatus);
+ void internalError(KIRC::Engine::Error, KIRC::Message &);
+
+ void receivedMessage(KIRC::Message &);
+
+ /**
+ * Emit a received message.
+ * The received message could have been translated to your locale.
+ *
+ * @param type the message type.
+ * @param from the originator of the message.
+ * @param to is the list of entities that are related to this message.
+ * @param msg the message (usually translated).
+ *
+ * @note Most of the following numeric messages should be deprecated, and call this method instead.
+ * Most of the methods, using it, update KIRC::Entities.
+ * Lists based messages are sent via dedicated API, therefore they don't use this.
+ */
+ // @param args the args to apply to this message.
+ void receivedMessage( KIRC::Engine::ServerMessageType type,
+ const KIRC::EntityPtr &from,
+ const KIRC::EntityPtrList &to,
+ const QString &msg);
+
+ void successfullyChangedNick(const QString &, const QString &);
+
+ //ServerContact Signals
+ void incomingMotd(const QString &motd);
+ void incomingNotice(const QString &originating, const QString &message);
+ void incomingHostInfo(const QString &servername, const QString &version,
+ const QString &userModes, const QString &channelModes);
+ void incomingYourHostInfo(const QString &servername, const QString &version,
+ const QString &userModes, const QString &channelModes);
+ void incomingConnectString(const QString &clients);
+
+ //Channel Contact Signals
+ void incomingMessage(const QString &originating, const QString &target, const QString &message);
+ void incomingTopicChange(const QString &, const QString &, const QString &);
+ void incomingExistingTopic(const QString &, const QString &);
+ void incomingTopicUser(const QString &channel, const QString &user, const QDateTime &time);
+ void incomingJoinedChannel(const QString &channel,const QString &nick);
+ void incomingPartedChannel(const QString &channel,const QString &nick, const QString &reason);
+ void incomingNamesList(const QString &channel, const QStringList &nicknames);
+ void incomingEndOfNames(const QString &channel);
+ void incomingChannelMode(const QString &channel, const QString &mode, const QString &params);
+ void incomingCannotSendToChannel(const QString &channel, const QString &message);
+ void incomingChannelModeChange(const QString &channel, const QString &nick, const QString &mode);
+ void incomingChannelHomePage(const QString &channel, const QString &url);
+
+ //Contact Signals
+ void incomingPrivMessage(const QString &, const QString &, const QString &);
+ void incomingQuitIRC(const QString &user, const QString &reason);
+ void incomingUserModeChange(const QString &nick, const QString &mode);
+ void incomingNoSuchNickname(const QString &nick);
+
+ // CTCP Signals
+// void action(const QString &from, const QString &to, const QString &message);
+ void incomingAction(const QString &channel, const QString &originating, const QString &message);
+ void incomingPrivAction(const QString &target, const QString &originating, const QString &message);
+
+ //Response Signals
+ void incomingUserOnline(const QString &nick);
+ void incomingWhoIsUser(const QString &nickname, const QString &username,
+ const QString &hostname, const QString &realname);
+ void incomingWhoWasUser(const QString &nickname, const QString &username,
+ const QString &hostname, const QString &realname);
+ void incomingWhoIsServer(const QString &nickname, const QString &server, const QString &serverInfo);
+ void incomingWhoIsOperator(const QString &nickname);
+ void incomingWhoIsIdentified(const QString &nickname);
+ void incomingWhoIsChannels(const QString &nickname, const QString &channel);
+ void incomingWhoIsIdle(const QString &nickname, unsigned long seconds); /* 317 */
+ void incomingSignOnTime(const QString &nickname, unsigned long seconds); /* 317 */
+ void incomingEndOfWhois(const QString &nickname);
+ void incomingEndOfWhoWas(const QString &nickname);
+
+ void incomingWhoReply( const QString &nick, const QString &channel, const QString &user, const QString &host,
+ const QString &server,bool away, const QString &flag, uint hops, const QString &realName );
+
+ void incomingEndOfWho( const QString &query );
+
+ //Error Message Signals
+ void incomingServerLoadTooHigh();
+ void incomingNickInUse(const QString &usingNick);
+ void incomingNickChange(const QString &, const QString &);
+ void incomingFailedServerPassword();
+ void incomingFailedChankey(const QString &);
+ void incomingFailedChanBanned(const QString &);
+ void incomingFailedChanInvite(const QString &);
+ void incomingFailedChanFull(const QString &);
+ void incomingFailedNickOnLogin(const QString &);
+ void incomingNoNickChan(const QString &);
+ void incomingWasNoNick(const QString &);
+
+ //General Signals
+ void incomingUnknown(const QString &);
+ void incomingUnknownCtcp(const QString &);
+ void incomingKick(const QString &channel, const QString &nick,
+ const QString &nickKicked, const QString &reason);
+
+ void incomingUserIsAway(const QString &nick, const QString &awayMessage);
+ void incomingListedChan(const QString &chan, uint users, const QString &topic);
+ void incomingEndOfList();
+
+ void incomingCtcpReply(const QString &type, const QString &target, const QString &messageReceived);
+
+private slots:
+ void destroyed(KIRC::Entity *entity);
+
+ void slotReadyRead();
+
+ void slotConnected();
+ void slotConnectionClosed();
+ void error(int errCode = 0);
+
+ void ignoreMessage(KIRC::Message &msg);
+ void emitSuffix(KIRC::Message &);
+
+ void error(KIRC::Message &msg);
+ void join(KIRC::Message &msg);
+ void kick(KIRC::Message &msg);
+ void mode(KIRC::Message &msg);
+ void nick(KIRC::Message &msg);
+ void notice(KIRC::Message &msg);
+ void part(KIRC::Message &msg);
+ void ping(KIRC::Message &msg);
+ void pong(KIRC::Message &msg);
+ void privmsg(KIRC::Message &msg);
+// void squit(KIRC::Message &msg);
+ void quit(KIRC::Message &msg);
+ void topic(KIRC::Message &msg);
+
+ void numericReply_001(KIRC::Message &msg);
+ void numericReply_002(KIRC::Message &msg);
+ void numericReply_003(KIRC::Message &msg);
+ void numericReply_004(KIRC::Message &msg);
+ void numericReply_005(KIRC::Message &msg);
+ void numericReply_250(KIRC::Message &msg);
+ void numericReply_251(KIRC::Message &msg);
+ void numericReply_252(KIRC::Message &msg);
+ void numericReply_253(KIRC::Message &msg);
+ void numericReply_254(KIRC::Message &msg);
+ void numericReply_255(KIRC::Message &msg);
+ void numericReply_263(KIRC::Message &msg);
+ void numericReply_265(KIRC::Message &msg);
+ void numericReply_266(KIRC::Message &msg);
+ void numericReply_301(KIRC::Message &msg);
+ void numericReply_303(KIRC::Message &msg);
+// void numericReply_305(KIRC::Message &msg);
+// void numericReply_306(KIRC::Message &msg);
+ void numericReply_307(KIRC::Message &msg);
+ void numericReply_311(KIRC::Message &msg);
+ void numericReply_312(KIRC::Message &msg);
+ void numericReply_313(KIRC::Message &msg);
+ void numericReply_314(KIRC::Message &msg);
+ void numericReply_315(KIRC::Message &msg);
+ void numericReply_317(KIRC::Message &msg);
+ void numericReply_318(KIRC::Message &msg);
+ void numericReply_319(KIRC::Message &msg);
+ void numericReply_320(KIRC::Message &msg);
+ void numericReply_322(KIRC::Message &msg);
+ void numericReply_323(KIRC::Message &msg);
+ void numericReply_324(KIRC::Message &msg);
+ void numericReply_328(KIRC::Message &msg);
+ void numericReply_329(KIRC::Message &msg);
+ void numericReply_331(KIRC::Message &msg);
+ void numericReply_332(KIRC::Message &msg);
+ void numericReply_333(KIRC::Message &msg);
+ void numericReply_352(KIRC::Message &msg);
+ void numericReply_353(KIRC::Message &msg);
+ void numericReply_366(KIRC::Message &msg);
+ void numericReply_369(KIRC::Message &msg);
+ void numericReply_372(KIRC::Message &msg);
+// void numericReply_376(KIRC::Message &msg);
+
+ void numericReply_401(KIRC::Message &msg);
+ void numericReply_406(KIRC::Message &msg);
+ void numericReply_422(KIRC::Message &msg);
+ void numericReply_433(KIRC::Message &msg);
+ void numericReply_464(KIRC::Message &msg);
+ void numericReply_471(KIRC::Message &msg);
+ void numericReply_473(KIRC::Message &msg);
+ void numericReply_474(KIRC::Message &msg);
+ void numericReply_475(KIRC::Message &msg);
+
+
+ void CtcpQuery_action(KIRC::Message &msg);
+ void CtcpQuery_clientinfo(KIRC::Message &msg);
+ void CtcpQuery_finger(KIRC::Message &msg);
+ void CtcpQuery_dcc(KIRC::Message &msg);
+ void CtcpQuery_ping(KIRC::Message &msg);
+ void CtcpQuery_source(KIRC::Message &msg);
+ void CtcpQuery_time(KIRC::Message &msg);
+ void CtcpQuery_userinfo(KIRC::Message &msg);
+ void CtcpQuery_version(KIRC::Message &msg);
+
+ void CtcpReply_errmsg(KIRC::Message &msg);
+ void CtcpReply_ping(KIRC::Message &msg);
+ void CtcpReply_version(KIRC::Message &msg);
+
+private:
+ void bindCommands();
+ void bindNumericReplies();
+ void bindCtcp();
+
+ void setStatus(KIRC::Engine::Status status);
+ bool invokeCtcpCommandOfMessage(const QDict<KIRC::MessageRedirector> &map, KIRC::Message &message);
+
+ /*
+ * Methods that handles all the bindings creations.
+ * This methods is used by all the bind(...) methods.
+ */
+ bool _bind(QDict<KIRC::MessageRedirector> &dict,
+ QString command, QObject *object, const char *member,
+ int minArgs, int maxArgs, const QString &helpMessage);
+
+ //Static regexes
+ static const QRegExp m_RemoveLinefeeds;
+
+ KIRC::Engine::Status m_status;
+ QString m_Host;
+ Q_UINT16 m_Port;
+
+// QUrl serverURL;
+// QUrl currentServerURL;
+ QString m_Nickname;
+ QString m_Username;
+ QString m_realName;
+ QString m_Passwd;
+ bool m_ReqsPasswd;
+ bool m_FailedNickOnLogin;
+ bool m_useSSL;
+
+ QValueList<KIRC::Entity *> m_entities;
+ KIRC::EntityPtr m_server;
+ KIRC::EntityPtr m_self;
+
+ QString m_VersionString;
+ QString m_UserString;
+ QString m_SourceString;
+ QString m_PendingNick;
+
+ QDict<KIRC::MessageRedirector> m_commands;
+// QIntDict<KIRC::MessageRedirector> m_numericCommands;
+ QDict<KIRC::MessageRedirector> m_ctcpQueries;
+ QDict<KIRC::MessageRedirector> m_ctcpReplies;
+
+ QMap<QString, QString> customCtcpMap;
+ QDict<QTextCodec> codecs;
+ QTextCodec *defaultCodec;
+
+ KExtendedSocket *m_sock;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/kircengine_commands.cpp b/kopete/protocols/irc/libkirc/kircengine_commands.cpp
new file mode 100644
index 00000000..0a0f9002
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircengine_commands.cpp
@@ -0,0 +1,312 @@
+/*
+ kirc_commands.h - IRC Client
+
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kircengine.h"
+
+#include <kextsock.h>
+
+#include <qtimer.h>
+
+using namespace KIRC;
+
+void Engine::bindCommands()
+{
+ bind("ERROR", this, SLOT(error(KIRC::Message &)), 0, 0);
+ bind("JOIN", this, SLOT(join(KIRC::Message &)), 0, 1);
+ bind("KICK", this, SLOT(kick(KIRC::Message &)), 2, 2);
+ bind("NICK", this, SLOT(nick(KIRC::Message &)), 0, 0);
+ bind("MODE", this, SLOT(mode(KIRC::Message &)), 1, 1);
+ bind("NOTICE", this, SLOT(notice(KIRC::Message &)), 1, 1);
+ bind("PART", this, SLOT(part(KIRC::Message &)), 1, 1);
+ bind("PING", this, SLOT(ping(KIRC::Message &)), 0, 0);
+ bind("PONG", this, SLOT(pong(KIRC::Message &)), 0, 0);
+ bind("PRIVMSG", this, SLOT(privmsg(KIRC::Message &)), 1, 1);
+ bind("QUIT", this, SLOT(quit(KIRC::Message &)), 0, 0);
+// bind("SQUIT", this, SLOT(squit(KIRC::Message &)), 1, 1);
+ bind("TOPIC", this, SLOT(topic(KIRC::Message &)), 1, 1);
+}
+
+void Engine::away(bool isAway, const QString &awayMessage)
+{
+ if(isAway)
+ if( !awayMessage.isEmpty() )
+ writeMessage("AWAY", QString::null, awayMessage);
+ else
+ writeMessage("AWAY", QString::null, QString::fromLatin1("I'm away."));
+ else
+ writeMessage("AWAY", QString::null);
+}
+
+// FIXME: Really handle this message
+void Engine::error(Message &)
+{
+ setStatus(Closing);
+}
+
+void Engine::ison(const QStringList &nickList)
+{
+ if (!nickList.isEmpty())
+ {
+ QString statement = QString::fromLatin1("ISON");
+ for (QStringList::ConstIterator it = nickList.begin(); it != nickList.end(); ++it)
+ {
+ if ((statement.length()+(*it).length())>509) // 512(max buf)-2("\r\n")-1(<space separator>)
+ {
+ writeMessage(statement);
+ statement = QString::fromLatin1("ISON ") + (*it);
+ }
+ else
+ statement.append(QChar(' ') + (*it));
+ }
+ writeMessage(statement);
+ }
+}
+
+void Engine::join(const QString &name, const QString &key)
+{
+ QStringList args(name);
+ if ( !key.isNull() )
+ args << key;
+
+ writeMessage("JOIN", args);
+}
+
+void Engine::join(Message &msg)
+{
+ /* RFC say: "( <channel> *( "," <channel> ) [ <key> *( "," <key> ) ] ) / "0""
+ * suspected: ":<channel> *(" "/"," <channel>)"
+ * assumed ":<channel>"
+ * This is the response of someone joining a channel.
+ * Remember that this will be emitted when *you* /join a room for the first time */
+
+ if (msg.argsSize()==1)
+ emit incomingJoinedChannel(Kopete::Message::unescape(msg.arg(0)), msg.nickFromPrefix());
+ else
+ emit incomingJoinedChannel(Kopete::Message::unescape(msg.suffix()), msg.nickFromPrefix());
+}
+
+void Engine::kick(const QString &user, const QString &channel, const QString &reason)
+{
+ writeMessage("KICK", QStringList(channel) << user << reason);
+}
+
+void Engine::kick(Message &msg)
+{
+ /* The given user is kicked.
+ * "<channel> *( "," <channel> ) <user> *( "," <user> ) [<comment>]"
+ */
+ emit incomingKick( Kopete::Message::unescape(msg.arg(0)), msg.nickFromPrefix(), msg.arg(1), msg.suffix());
+}
+
+void Engine::mode(const QString &target, const QString &mode)
+{
+ writeMessage("MODE", QStringList(target) << mode);
+}
+
+void Engine::mode(Message &msg)
+{
+ /* Change the mode of a user.
+ * "<nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )"
+ */
+ QStringList args = msg.args();
+ args.pop_front();
+ if( Entity::isChannel( msg.arg(0) ) )
+ emit incomingChannelModeChange( msg.arg(0), msg.nickFromPrefix(), args.join(" "));
+ else
+ emit incomingUserModeChange( msg.nickFromPrefix(), args.join(" "));
+}
+
+void Engine::nick(const QString &newNickname)
+{
+ m_PendingNick = newNickname;
+ writeMessage("NICK", newNickname);
+}
+
+void Engine::nick(Message &msg)
+{
+ /* Nick name of a user changed
+ * "<nickname>" */
+ QString oldNick = msg.prefix().section('!', 0, 0);
+ QString newNick = msg.suffix();
+
+ if( codecs[ oldNick ] )
+ {
+ QTextCodec *c = codecs[ oldNick ];
+ codecs.remove( oldNick );
+ codecs.insert( newNick, c );
+ }
+
+ if (oldNick.lower() == m_Nickname.lower())
+ {
+ emit successfullyChangedNick(oldNick, msg.suffix());
+ m_Nickname = msg.suffix();
+ }
+ else
+ emit incomingNickChange(oldNick, msg.suffix());
+}
+
+void Engine::part(const QString &channel, const QString &reason)
+{
+ /* This will part a channel with 'reason' as the reason for parting
+ */
+ writeMessage("PART", channel, reason);
+}
+
+void Engine::part(Message &msg)
+{
+ /* This signal emits when a user parts a channel
+ * "<channel> *( "," <channel> ) [ <Part Message> ]"
+ */
+ kdDebug(14120) << "User parting" << endl;
+ emit incomingPartedChannel(msg.arg(0), msg.nickFromPrefix(), msg.suffix());
+}
+
+void Engine::pass(const QString &password)
+{
+ writeMessage("PASS", password);
+}
+
+void Engine::ping(Message &msg)
+{
+ writeMessage("PONG", msg.arg(0), msg.suffix());
+}
+
+void Engine::pong(Message &/*msg*/)
+{
+}
+
+void Engine::quit(const QString &reason, bool /*now*/)
+{
+ kdDebug(14120) << k_funcinfo << reason << endl;
+
+ if (isDisconnected())
+ return;
+
+ if (isConnected())
+ writeMessage("QUIT", QString::null, reason);
+
+ setStatus(Closing);
+}
+
+void Engine::quit(Message &msg)
+{
+ /* This signal emits when a user quits irc.
+ */
+ kdDebug(14120) << "User quiting" << endl;
+ emit incomingQuitIRC(msg.prefix(), msg.suffix());
+}
+
+void Engine::user(const QString &newUserName, const QString &hostname, const QString &newRealName)
+{
+ /* RFC1459: "<username> <hostname> <servername> <realname>"
+ * The USER command is used at the beginning of connection to specify
+ * the username, hostname and realname of a new user.
+ * hostname is usualy set to "127.0.0.1" */
+ m_Username = newUserName;
+ m_realName = newRealName;
+
+ writeMessage("USER", QStringList(m_Username) << hostname << m_Host, m_realName);
+}
+
+void Engine::user(const QString &newUserName, Q_UINT8 mode, const QString &newRealName)
+{
+ /* RFC2812: "<user> <mode> <unused> <realname>"
+ * mode is a numeric value (from a bit mask).
+ * 0x00 normal
+ * 0x04 request +w
+ * 0x08 request +i */
+ m_Username = newUserName;
+ m_realName = newRealName;
+
+ writeMessage("USER", QStringList(m_Username) << QString::number(mode) << QChar('*'), m_realName);
+}
+
+void Engine::topic(const QString &channel, const QString &topic)
+{
+ writeMessage("TOPIC", channel, topic);
+}
+
+void Engine::topic(Message &msg)
+{
+ /* The topic of a channel changed. emit the channel, new topic, and the person who changed it.
+ * "<channel> [ <topic> ]"
+ */
+ emit incomingTopicChange(msg.arg(0), msg.nickFromPrefix(), msg.suffix());
+}
+
+void Engine::list()
+{
+ writeMessage("LIST", QString::null);
+}
+
+void Engine::motd(const QString &server)
+{
+ writeMessage("MOTD", server);
+}
+
+void Engine::privmsg(const QString &contact, const QString &message)
+{
+ writeMessage("PRIVMSG", contact, message, codecForNick( contact ) );
+}
+
+void Engine::privmsg(Message &msg)
+{
+ /* This is a signal that indicates there is a new message.
+ * This can be either from a channel or from a specific user. */
+ Message m = msg;
+ if (!m.suffix().isEmpty())
+ {
+ QString user = m.arg(0);
+ QString message = m.suffix();
+ const QTextCodec *codec = codecForNick( user );
+ if (codec != defaultCodec) {
+ m.decodeAgain( codec );
+ message = m.suffix();
+ }
+ if (Entity::isChannel(user))
+ emit incomingMessage(m.nickFromPrefix(), Kopete::Message::unescape(m.arg(0)), message );
+ else
+ emit incomingPrivMessage(m.nickFromPrefix(), Kopete::Message::unescape(m.arg(0)), message );
+// emit receivedMessage(PrivateMessage, msg.entityFrom(), msg.entityTo(), message);
+ }
+
+ if( m.hasCtcpMessage() )
+ {
+ invokeCtcpCommandOfMessage(m_ctcpQueries, m);
+ }
+}
+
+void Engine::notice(const QString &target, const QString &message)
+{
+ writeMessage("NOTICE", target, message);
+}
+
+void Engine::notice(Message &msg)
+{
+ if(!msg.suffix().isEmpty())
+ emit incomingNotice(msg.prefix(), msg.suffix());
+
+ if(msg.hasCtcpMessage())
+ invokeCtcpCommandOfMessage(m_ctcpReplies, msg);
+}
+
+void Engine::whois(const QString &user)
+{
+ writeMessage("WHOIS", user);
+}
diff --git a/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp b/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp
new file mode 100644
index 00000000..db1903f3
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircengine_ctcp.cpp
@@ -0,0 +1,351 @@
+/*
+ kirc_ctcp.h - IRC Client
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "config.h"
+
+#include "kircengine.h"
+#include "kirctransferhandler.h"
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <kextsock.h>
+
+#include <qfileinfo.h>
+#include <qregexp.h>
+
+using namespace KIRC;
+
+void Engine::bindCtcp()
+{
+ bindCtcpQuery("ACTION", this, SLOT(CtcpQuery_action(KIRC::Message &)),
+ -1, -1);
+ bindCtcpQuery("CLIENTINFO", this, SLOT(CtcpQuery_clientinfo(KIRC::Message &)),
+ -1, 1);
+ bindCtcpQuery("DCC", this, SLOT(CtcpQuery_dcc(KIRC::Message &)),
+ 4, 5);
+ bindCtcpQuery("FINGER", this, SLOT(CtcpQuery_finger(KIRC::Message &)),
+ -1, 0);
+ bindCtcpQuery("PING", this, SLOT(CtcpQuery_ping(KIRC::Message &)),
+ 1, 1);
+ bindCtcpQuery("SOURCE", this, SLOT(CtcpQuery_source(KIRC::Message &)),
+ -1, 0);
+ bindCtcpQuery("TIME", this, SLOT(CtcpQuery_time(KIRC::Message &)),
+ -1, 0);
+ bindCtcpQuery("USERINFO", this, SLOT(CtcpQuery_userinfo(KIRC::Message &)),
+ -1, 0);
+ bindCtcpQuery("VERSION", this, SLOT(CtcpQuery_version(KIRC::Message &)),
+ -1, 0);
+
+ bindCtcpReply("ERRMSG", this, SLOT(CtcpReply_errmsg(KIRC::Message &)),
+ 1, -1);
+ bindCtcpReply("PING", this, SLOT(CtcpReply_ping(KIRC::Message &)),
+ 1, 1, "");
+ bindCtcpReply("VERSION", this, SLOT(CtcpReply_version(KIRC::Message &)),
+ -1, -1, "");
+}
+
+// Normal order for a ctcp command:
+// CtcpRequest_*
+// CtcpQuery_*
+// CtcpReply_* (if any)
+
+/* Generic ctcp commnd for the /ctcp trigger */
+void Engine::CtcpRequestCommand(const QString &contact, const QString &command)
+{
+ if(m_status == Connected)
+ {
+ writeCtcpQueryMessage(contact, QString::null, command);
+// emit ctcpCommandMessage( contact, command );
+ }
+}
+
+void Engine::CtcpRequest_action(const QString &contact, const QString &message)
+{
+ if(m_status == Connected)
+ {
+ writeCtcpQueryMessage(contact, QString::null, "ACTION", message);
+
+ if( Entity::isChannel(contact) )
+ emit incomingAction(Kopete::Message::unescape(contact), Kopete::Message::unescape(m_Nickname), message);
+ else
+ emit incomingPrivAction(Kopete::Message::unescape(m_Nickname), Kopete::Message::unescape(contact), message);
+ }
+}
+
+void Engine::CtcpQuery_action(Message &msg)
+{
+ QString target = msg.arg(0);
+ if (target[0] == '#' || target[0] == '!' || target[0] == '&')
+ emit incomingAction(target, msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw());
+ else
+ emit incomingPrivAction(msg.nickFromPrefix(), Kopete::Message::unescape(target), msg.ctcpMessage().ctcpRaw());
+}
+
+/*
+NO REPLY EXIST FOR THE CTCP ACTION COMMAND !
+bool Engine::CtcpReply_action(Message &msg)
+{
+}
+*/
+
+// FIXME: the API can now answer to help commands.
+void Engine::CtcpQuery_clientinfo(Message &msg)
+{
+ QString clientinfo = customCtcpMap[ QString::fromLatin1("clientinfo") ];
+
+ if (clientinfo.isNull())
+ clientinfo = QString::fromLatin1("The following commands are supported, but "
+ "without sub-command help: VERSION, CLIENTINFO, USERINFO, TIME, SOURCE, PING,"
+ "ACTION.");
+
+ writeCtcpReplyMessage( msg.nickFromPrefix(), QString::null,
+ msg.ctcpMessage().command(), QString::null, clientinfo);
+}
+
+void Engine::CtcpRequest_dcc(const QString &nickname, const QString &fileName, uint port, Transfer::Type type)
+{
+ if( m_status != Connected ||
+ m_sock->localAddress() == 0 ||
+ m_sock->localAddress()->nodeName().isNull())
+ return;
+
+ switch(type)
+ {
+ case Transfer::Chat:
+ {
+ writeCtcpQueryMessage(nickname, QString::null,
+ QString::fromLatin1("DCC"),
+ QStringList(QString::fromLatin1("CHAT")) << QString::fromLatin1("chat") <<
+ m_sock->localAddress()->nodeName() << QString::number(port)
+ );
+ break;
+ }
+
+ case Transfer::FileOutgoing:
+ {
+ QFileInfo file(fileName);
+ QString noWhiteSpace = file.fileName();
+ if (noWhiteSpace.contains(' ') > 0)
+ noWhiteSpace.replace(QRegExp("\\s+"), "_");
+
+ TransferServer *server = TransferHandler::self()->createServer(this, nickname, type, fileName, file.size());
+
+ QString ip = m_sock->localAddress()->nodeName();
+ QString ipNumber = QString::number( ntohl( inet_addr( ip.latin1() ) ) );
+
+ kdDebug(14120) << "Starting DCC file outgoing transfer." << endl;
+
+ writeCtcpQueryMessage(nickname, QString::null,
+ QString::fromLatin1("DCC"),
+ QStringList(QString::fromLatin1("SEND")) << noWhiteSpace << ipNumber <<
+ QString::number(server->port()) << QString::number(file.size())
+ );
+ break;
+ }
+
+ case Transfer::FileIncoming:
+ case Transfer::Unknown:
+ default:
+ break;
+ }
+}
+
+void Engine::CtcpQuery_dcc(Message &msg)
+{
+ Message &ctcpMsg = msg.ctcpMessage();
+ QString dccCommand = ctcpMsg.arg(0).upper();
+
+ if (dccCommand == QString::fromLatin1("CHAT"))
+ {
+// if(ctcpMsg.argsSize()!=4) return false;
+
+ /* DCC CHAT type longip port
+ *
+ * type = Either Chat or Talk, but almost always Chat these days
+ * longip = 32-bit Internet address of originator's machine
+ * port = Port on which the originator is waitng for a DCC chat
+ */
+ bool okayHost, okayPort;
+ // should ctctMsg.arg(1) be tested?
+ QHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost));
+ unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort);
+ if (okayHost && okayPort)
+ {
+ kdDebug(14120) << "Starting DCC chat window." << endl;
+ TransferHandler::self()->createClient(
+ this, msg.nickFromPrefix(),
+ address, port,
+ Transfer::Chat );
+ }
+ }
+ else if (dccCommand == QString::fromLatin1("SEND"))
+ {
+// if(ctcpMsg.argsSize()!=5) return false;
+
+ /* DCC SEND (filename) (longip) (port) (filesize)
+ *
+ * filename = Name of file being sent
+ * longip = 32-bit Internet address of originator's machine
+ * port = Port on which the originator is waiitng for a DCC chat
+ * filesize = Size of file being sent
+ */
+ bool okayHost, okayPort, okaySize;
+// QFileInfo realfile(msg.arg(1));
+ QHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost));
+ unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort);
+ unsigned int size = ctcpMsg.arg(4).toUInt(&okaySize);
+ if (okayHost && okayPort && okaySize)
+ {
+ kdDebug(14120) << "Starting DCC send file transfert for file:" << ctcpMsg.arg(1) << endl;
+ TransferHandler::self()->createClient(
+ this, msg.nickFromPrefix(),
+ address, port,
+ Transfer::FileIncoming,
+ ctcpMsg.arg(1), size );
+ }
+ }
+// else
+// ((MessageRedirector *)sender())->error("Unknow dcc command");
+}
+
+/*
+NO REPLY EXIST FOR THE CTCP DCC COMMAND !
+bool Engine::CtcpReply_dcc(Message &msg)
+{
+}
+*/
+
+void Engine::CtcpReply_errmsg(Message &)
+{
+ // should emit one signal
+}
+
+void Engine::CtcpQuery_finger( Message &)
+{
+ // To be implemented
+}
+
+void Engine::CtcpRequest_ping(const QString &target)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+
+ timeval time;
+ if (gettimeofday(&time, 0) == 0)
+ {
+ QString timeReply;
+
+ if( Entity::isChannel(target) )
+ timeReply = QString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec);
+ else
+ timeReply = QString::number( time.tv_sec );
+
+ writeCtcpQueryMessage( target, QString::null, "PING", timeReply);
+ }
+// else
+// ((MessageRedirector *)sender())->error("failed to get current time");
+}
+
+void Engine::CtcpQuery_ping(Message &msg)
+{
+ writeCtcpReplyMessage( msg.nickFromPrefix(), QString::null,
+ msg.ctcpMessage().command(), msg.ctcpMessage().arg(0));
+}
+
+void Engine::CtcpReply_ping(Message &msg)
+{
+ timeval time;
+ if (gettimeofday(&time, 0) == 0)
+ {
+ // FIXME: the time code is wrong for usec
+ QString timeReply = QString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec);
+ double newTime = timeReply.toDouble();
+ double oldTime = msg.suffix().section(' ',0, 0).toDouble();
+ double difference = newTime - oldTime;
+ QString diffString;
+
+ if (difference < 1)
+ {
+ diffString = QString::number(difference);
+ diffString.remove((diffString.find('.') -1), 2);
+ diffString.truncate(3);
+ diffString.append("milliseconds");
+ }
+ else
+ {
+ diffString = QString::number(difference);
+ QString seconds = diffString.section('.', 0, 0);
+ QString millSec = diffString.section('.', 1, 1);
+ millSec.remove(millSec.find('.'), 1);
+ millSec.truncate(3);
+ diffString = QString::fromLatin1("%1 seconds, %2 milliseconds").arg(seconds).arg(millSec);
+ }
+
+ emit incomingCtcpReply(QString::fromLatin1("PING"), msg.nickFromPrefix(), diffString);
+ }
+// else
+// ((MessageRedirector *)sender())->error("failed to get current time");
+}
+
+void Engine::CtcpQuery_source(Message &msg)
+{
+ writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null,
+ msg.ctcpMessage().command(), m_SourceString);
+}
+
+void Engine::CtcpQuery_time(Message &msg)
+{
+ writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null,
+ msg.ctcpMessage().command(), QDateTime::currentDateTime().toString(),
+ QString::null, false);
+}
+
+void Engine::CtcpQuery_userinfo(Message &msg)
+{
+ QString userinfo = customCtcpMap[ QString::fromLatin1("userinfo") ];
+
+ if (userinfo.isNull())
+ userinfo = m_UserString;
+
+ writeCtcpReplyMessage(msg.nickFromPrefix(), QString::null,
+ msg.ctcpMessage().command(), QString::null, userinfo);
+}
+
+void Engine::CtcpRequest_version(const QString &target)
+{
+ writeCtcpQueryMessage(target, QString::null, "VERSION");
+}
+
+void Engine::CtcpQuery_version(Message &msg)
+{
+ QString response = customCtcpMap[ QString::fromLatin1("version") ];
+ kdDebug(14120) << "Version check: " << response << endl;
+
+ if (response.isNull())
+ response = m_VersionString;
+
+ writeCtcpReplyMessage(msg.nickFromPrefix(),
+ msg.ctcpMessage().command() + " " + response);
+}
+
+void Engine::CtcpReply_version(Message &msg)
+{
+ emit incomingCtcpReply(msg.ctcpMessage().command(), msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw());
+}
diff --git a/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp b/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp
new file mode 100644
index 00000000..c47b8b05
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircengine_numericreplies.cpp
@@ -0,0 +1,570 @@
+
+/*
+ kircnumericreplies.cpp - IRC Client
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+ Copyright (c) 2002 by Nick Betcher <nbetcher@kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kircengine.h"
+
+#include <qtimer.h>
+
+using namespace KIRC;
+
+/* IMPORTANT NOTE:
+ * Numeric replies always have the current nick or * as first argmuent.
+ * NOTE: * means undefined in most (all ?) of the cases.
+ */
+
+void Engine::bindNumericReplies()
+{
+ bind(1, this, SLOT(numericReply_001(KIRC::Message &)), 1, 1);
+ bind(2, this, SLOT(numericReply_002(KIRC::Message &)), 1, 1);
+ bind(3, this, SLOT(numericReply_003(KIRC::Message &)), 1, 1);
+ bind(4, this, SLOT(numericReply_004(KIRC::Message &)), 5, 5);
+ bind(5, this, SLOT(numericReply_004(KIRC::Message &)), 1, 1);
+
+ bind(250, this, SLOT(numericReply_250(KIRC::Message &)));
+ bind(251, this, SLOT(numericReply_251(KIRC::Message &)));
+ bind(252, this, SLOT(numericReply_252(KIRC::Message &)), 2, 2);
+ bind(253, this, SLOT(numericReply_253(KIRC::Message &)), 2, 2);
+ bind(254, this, SLOT(numericReply_254(KIRC::Message &)), 2, 2);
+ bind(255, this, SLOT(numericReply_255(KIRC::Message &)), 1, 1); // incomingConnectString
+
+ bind(263, this, SLOT(numericReply_263(KIRC::Message &))); // incomingServerLoadTooHigh
+ bind(265, this, SLOT(numericReply_265(KIRC::Message &)));
+ bind(266, this, SLOT(numericReply_266(KIRC::Message &)));
+
+ bind(301, this, SLOT(numericReply_301(KIRC::Message &)), 2, 2);
+ bind(303, this, SLOT(numericReply_303(KIRC::Message &)), 1, 1);
+ bind(305, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 ); // You are no longer marked as away
+ bind(306, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 ); // You are marked as away
+ bind(307, this, SLOT(numericReply_307(KIRC::Message &)), 1, 1);
+ bind(311, this, SLOT(numericReply_311(KIRC::Message &)), 5, 5);
+ bind(312, this, SLOT(numericReply_312(KIRC::Message &)), 3, 3);
+ bind(313, this, SLOT(numericReply_313(KIRC::Message &)), 2, 2);
+ bind(314, this, SLOT(numericReply_314(KIRC::Message &)), 5, 5);
+ bind(315, this, SLOT(numericReply_315(KIRC::Message &)), 2, 2);
+ bind(317, this, SLOT(numericReply_317(KIRC::Message &)), 3, 4);
+ bind(318, this, SLOT(numericReply_318(KIRC::Message &)), 2, 2);
+ bind(319, this, SLOT(numericReply_319(KIRC::Message &)), 2, 2);
+ bind(320, this, SLOT(numericReply_320(KIRC::Message &)), 2, 2);
+ bind(321, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0 );
+ bind(322, this, SLOT(numericReply_322(KIRC::Message &)), 3, 3);
+ bind(323, this, SLOT(numericReply_323(KIRC::Message &)), 1, 1);
+ bind(324, this, SLOT(numericReply_324(KIRC::Message &)), 2, 4);
+ bind(328, this, SLOT(numericReply_328(KIRC::Message &)), 2, 2);
+ bind(329, this, SLOT(numericReply_329(KIRC::Message &)), 3, 3);
+ bind(330, this, SLOT(ignoreMessage(KIRC::Message &)), 0, 0); // ???
+ bind(331, this, SLOT(numericReply_331(KIRC::Message &)), 2, 2);
+ bind(332, this, SLOT(numericReply_332(KIRC::Message &)), 2, 2);
+ bind(333, this, SLOT(numericReply_333(KIRC::Message &)), 4, 4);
+ bind(352, this, SLOT(numericReply_352(KIRC::Message &)), 5, 10);
+ bind(353, this, SLOT(numericReply_353(KIRC::Message &)), 3, 3);
+ bind(366, this, SLOT(numericReply_366(KIRC::Message &)), 2, 2);
+ bind(369, this, SLOT(numericReply_369(KIRC::Message &)), 2, 2);
+ bind(372, this, SLOT(numericReply_372(KIRC::Message &)), 1, 1);
+ bind(375, this, SLOT(ignoreMessage(KIRC::Message&)), 0, 0 );
+ bind(376, this, SLOT(ignoreMessage(KIRC::Message&)), 0, 0 );
+
+ bind(401, this, SLOT(numericReply_401(KIRC::Message &)), 2, 2); // incomingNoNickChan
+// bind(404, this, SLOT(numericReply_404(KIRC::Message &)), 2, 2); // incomingCannotSendToChannel
+ bind(406, this, SLOT(numericReply_406(KIRC::Message &)), 2, 2); // incomingWasNoNick
+ bind(422, this, SLOT(numericReply_422(KIRC::Message &)), 1, 1);
+ bind(433, this, SLOT(numericReply_433(KIRC::Message &)), 2, 2);
+// bind(442, this, SLOT(numericReply_442(KIRC::Message &)), 2, 2); // incomingCannotSendToChannel
+ bind(464, this, SLOT(numericReply_464(KIRC::Message &)), 1, 1);
+ bind(471, this, SLOT(numericReply_471(KIRC::Message &)), 2, 2);
+ bind(473, this, SLOT(numericReply_473(KIRC::Message &)), 2, 2);
+ bind(474, this, SLOT(numericReply_474(KIRC::Message &)), 2, 2);
+ bind(475, this, SLOT(numericReply_475(KIRC::Message &)), 2, 2);
+
+ //Freenode seems to use this for a non-RFC compliant purpose, as does Unreal
+ bind(477, this, SLOT(emitSuffix(KIRC::Message&)),0,0);
+}
+
+/* 001: "Welcome to the Internet Relay Network <nick>!<user>@<host>"
+ * Gives a welcome message in the form of:
+ */
+void Engine::numericReply_001(Message &msg)
+{
+ kdDebug(14121) << k_funcinfo << endl;
+
+ if (m_FailedNickOnLogin)
+ {
+ // this is if we had a "Nickname in use" message when connecting and we set another nick.
+ // This signal emits that the nick was accepted and we are now logged in
+ emit successfullyChangedNick(m_Nickname, m_PendingNick);
+ m_Nickname = m_PendingNick;
+ m_FailedNickOnLogin = false;
+ }
+
+ /* At this point we are connected and the server is ready for us to being taking commands
+ * although the MOTD comes *after* this.
+ */
+ emitSuffix(msg);
+
+ setStatus(Connected);
+}
+
+/* 002: ":Your host is <servername>, running version <ver>"
+ * Gives information about the host. The given informations are close to 004.
+ */
+void Engine::numericReply_002(Message &msg)
+{
+ emitSuffix(msg);
+}
+
+/* 003: "This server was created <date>"
+ * Gives the date that this server was created.
+ * NOTE: This is useful for determining the uptime of the server).
+ */
+void Engine::numericReply_003(Message &msg)
+{
+ emitSuffix(msg);
+}
+
+/* 004: "<servername> <version> <available user modes> <available channel modes>"
+ * Gives information about the servername, version, available modes, etc.
+ */
+void Engine::numericReply_004(Message &msg)
+{
+ emit incomingHostInfo(msg.arg(1),msg.arg(2),msg.arg(3),msg.arg(4));
+}
+
+/* 005:
+ * Gives capability information. TODO: This is important!
+ */
+void Engine::numericReply_005(Message &msg)
+{
+ emit incomingConnectString( msg.toString() );
+}
+
+/* 250: ":Highest connection count: <integer> (<integer> clients)
+ * (<integer> since server was (re)started)"
+ * Tells connections statistics about the server for the uptime activity.
+ * NOT IN RFC1459 NOR RFC2812
+ */
+void Engine::numericReply_250(Message &msg)
+{
+ emit incomingConnectString( msg.suffix() );
+}
+
+/* 251: ":There are <integer> users and <integer> services on <integer> servers"
+ * Tells how many user there are on all the different servers in the form of:
+ */
+void Engine::numericReply_251(Message &msg)
+{
+ emit incomingConnectString( msg.suffix() );
+}
+/* 252: "<integer> :operator(s) online"
+ * Issues a number of operators on the server in the form of:
+ */
+void Engine::numericReply_252(Message &msg)
+{
+ emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() );
+}
+
+/* 253: "<integer> :unknown connection(s)"
+ * Tells how many unknown connections the server has in the form of:
+ */
+void Engine::numericReply_253(Message &msg)
+{
+ emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() );
+}
+
+/* Tells how many total channels there are on this network in the form of:
+ * "<integer> :channels formed" */
+void Engine::numericReply_254(Message &msg)
+{
+ emit incomingConnectString( msg.arg(1) + ' ' + msg.suffix() );
+}
+
+/* 255: ":I have <integer> clients and <integer> servers"
+ * Tells how many clients and servers *this* server handles.
+ */
+void Engine::numericReply_255(Message &msg)
+{
+ emit incomingConnectString( msg.suffix() );
+}
+
+/* 263:
+ * Server is too busy.
+ */
+void Engine::numericReply_263(Message &)
+{
+ emit incomingServerLoadTooHigh();
+}
+
+/* 265: ":Current local users: <integer> Max: <integer>"
+ * Tells statistics about the current local server state.
+ * NOT IN RFC2812
+ */
+void Engine::numericReply_265(Message &msg)
+{
+ emit incomingConnectString( msg.suffix() );
+}
+
+/* 266: ":Current global users: <integer> Max: <integer>"
+ * Tells statistics about the current global(the whole irc server chain) server state:
+ */
+void Engine::numericReply_266(Message &msg)
+{
+ emit incomingConnectString( msg.suffix() );
+}
+
+/* 301: "<nick> :<away message>"
+ */
+void Engine::numericReply_301(Message &msg)
+{
+ emit incomingUserIsAway(Kopete::Message::unescape(msg.arg(1)), msg.suffix());
+}
+
+/* 303: ":*1<nick> *(" " <nick> )"
+ */
+void Engine::numericReply_303(Message &msg)
+{
+ QStringList nicks = QStringList::split(QRegExp(QChar(' ')), msg.suffix());
+ for(QStringList::Iterator it = nicks.begin(); it != nicks.end(); ++it)
+ {
+ if (!(*it).stripWhiteSpace().isEmpty())
+ emit incomingUserOnline(Kopete::Message::unescape(*it));
+ }
+}
+
+/* 305: ":You are no longer marked as being away"
+ */
+// void Engine::numericReply_305(Message &msg)
+// {
+// }
+
+
+/* 306: ":You have been marked as being away"
+ */
+// void Engine::numericReply_306(Message &msg)
+// {
+// }
+
+/* 307: ":is a registered nick"
+ * DALNET: Indicates that this user is identified with NICSERV.
+ */
+void Engine::numericReply_307(Message & /*msg*/)
+{
+// emit incomingWhoiIsUserNickIsRegistered(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 311: "<nick> <user> <host> * :<real name>"
+ * Show info about a user (part of a /whois) in the form of:
+ */
+void Engine::numericReply_311(Message &msg)
+{
+ emit incomingWhoIsUser(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3), msg.suffix());
+}
+
+/* 312: "<nick> <server> :<server info>"
+ * Show info about a server (part of a /whois).
+ */
+void Engine::numericReply_312(Message &msg)
+{
+ emit incomingWhoIsServer(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.suffix());
+}
+
+/* 313: "<nick> :is an IRC operator"
+ * Show info about an operator (part of a /whois).
+ */
+void Engine::numericReply_313(Message & /*msg*/)
+{
+}
+
+/* 314: "<nick> <user> <host> * :<real name>"
+ * Show WHOWAS Info
+ */
+void Engine::numericReply_314(Message &msg)
+{
+ emit incomingWhoWasUser(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3), msg.suffix());
+}
+
+void Engine::numericReply_315(Message &msg)
+{
+ emit incomingEndOfWho(Kopete::Message::unescape(msg.arg(1)));
+}
+
+void Engine::numericReply_317(Message &msg)
+{
+ /* RFC say: "<nick> <integer> :seconds idle"
+ * Some servers say: "<nick> <integer> <integer> :seconds idle, signon time"
+ * Show info about someone who is idle (part of a /whois) in the form of:
+ */
+ emit incomingWhoIsIdle(Kopete::Message::unescape(msg.arg(1)), msg.arg(2).toULong());
+ if (msg.argsSize()==4)
+ emit incomingSignOnTime(Kopete::Message::unescape(msg.arg(1)),msg.arg(3).toULong());
+}
+
+/* 318: "<nick>{<space><realname>} :End of /WHOIS list"
+ * End of WHOIS for a given nick.
+ */
+void Engine::numericReply_318(Message &msg)
+{
+ emit incomingEndOfWhois(Kopete::Message::unescape(msg.arg(1)));
+}
+
+void Engine::numericReply_319(Message &msg)
+{
+ /* Show info a channel a user is logged in (part of a /whois) in the form of:
+ * "<nick> :{[@|+]<channel><space>}"
+ */
+ emit incomingWhoIsChannels(Kopete::Message::unescape(msg.arg(1)), msg.suffix());
+}
+
+/* 320:
+ * Indicates that this user is identified with NICSERV on FREENODE.
+ */
+void Engine::numericReply_320(Message &msg)
+{
+ emit incomingWhoIsIdentified(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 321: "<channel> :Users Name" ("Channel :Users Name")
+ * RFC1459: Declared.
+ * RFC2812: Obsoleted.
+ */
+
+/* 322: "<channel> <# visible> :<topic>"
+ * Received one channel from the LIST command.
+ */
+void Engine::numericReply_322(Message &msg)
+{
+ //kdDebug(14120) << k_funcinfo << "Listed " << msg.arg(1) << endl;
+
+ emit incomingListedChan(Kopete::Message::unescape(msg.arg(1)), msg.arg(2).toUInt(), msg.suffix());
+}
+
+/* 323: ":End of LIST"
+ * End of the LIST command.
+ */
+void Engine::numericReply_323(Message &)
+{
+ emit incomingEndOfList();
+}
+
+/* 324: "<channel> <mode> <mode params>"
+ */
+void Engine::numericReply_324(Message &msg)
+{
+ emit incomingChannelMode(Kopete::Message::unescape(msg.arg(1)), msg.arg(2), msg.arg(3));
+}
+
+/* 328: "<channel> <mode> <mode params>"
+ */
+void Engine::numericReply_328(Message &msg)
+{
+ kdDebug(14120) << k_funcinfo << endl;
+ emit incomingChannelHomePage(Kopete::Message::unescape(msg.arg(1)), msg.suffix());
+}
+
+/* 329: "%s %lu"
+ * NOTE: What is the meaning of this arguments. DAL-ircd say it's a RPL_CREATIONTIME
+ * NOT IN RFC1459 NOR RFC2812
+ */
+void Engine::numericReply_329( Message &)
+{
+}
+
+/* 331: "<channel> :No topic is set"
+ * Gives the existing topic for a channel after a join.
+ */
+void Engine::numericReply_331( Message &)
+{
+// emit incomingExistingTopic(msg.arg(1), suffix);
+}
+
+/* 332: "<channel> :<topic>"
+ * Gives the existing topic for a channel after a join.
+ */
+void Engine::numericReply_332(Message &msg)
+{
+ emit incomingExistingTopic(Kopete::Message::unescape(msg.arg(1)), msg.suffix());
+}
+
+/* 333:
+ * Gives the nickname and time who changed the topic
+ */
+void Engine::numericReply_333( Message &msg )
+{
+ kdDebug(14120) << k_funcinfo << endl;
+ QDateTime d;
+ d.setTime_t( msg.arg(3).toLong() );
+ emit incomingTopicUser( Kopete::Message::unescape(msg.arg(1)), Kopete::Message::unescape(msg.arg(2)), d );
+}
+
+/* 352:
+ * WHO Reply
+ *
+ * "<channel> <user> <host> <server> <nick> ("H" / "G") ["*"] [("@" / "+")] :<hopcount> <real name>"
+ *
+ * :efnet.cs.hut.fi 352 userNick #foobar username some.host.name efnet.cs.hut.fi someNick H :0 foobar
+ * :efnet.cs.hut.fi 352 userNick #foobar ~fooobar other.hostname irc.dkom.at anotherNick G+ :3 Unknown
+ */
+void Engine::numericReply_352(Message &msg)
+{
+ emit incomingWhoReply(
+ Kopete::Message::unescape(msg.arg(5)), // nick name
+ Kopete::Message::unescape(msg.arg(1)), // channel name
+ msg.arg(2), // user name
+ msg.arg(3), // host name
+ msg.arg(4), // server name
+ msg.arg(6)[0] != 'H', // G=away (true), H=not away (false)
+ msg.arg(7), // @ (op), + (voiced)
+ msg.suffix().section(' ', 0, 1 ).toUInt(), // hopcount
+ msg.suffix().section(' ', 1 ) // real name
+ );
+}
+
+
+/* 353:
+ * NAMES list
+ */
+void Engine::numericReply_353(Message &msg)
+{
+ emit incomingNamesList(Kopete::Message::unescape(msg.arg(2)), QStringList::split(' ', msg.suffix()));
+}
+
+/* 366: "<channel> :End of NAMES list"
+ * Gives a signal to indicate that the NAMES list has ended for channel.
+ */
+void Engine::numericReply_366(Message &msg)
+{
+ emit incomingEndOfNames(msg.arg(1));
+}
+
+/* 369:
+ * End of WHOWAS Request
+ */
+void Engine::numericReply_369(Message & /*msg*/)
+{
+}
+
+/* 372: ":- <text>"
+ * Part of the MOTD.
+ */
+void Engine::numericReply_372(Message &msg)
+{
+ emit incomingMotd(msg.suffix());
+}
+
+/* 375: ":- <server> Message of the day - "
+ * Beginging the motd. This isn't emitted because the MOTD is sent out line by line.
+ */
+
+/* 376: ":End of MOTD command"
+ * End of the motd.
+ */
+
+/* 401: "<nickname> :No such nick/channel"
+ * Gives a signal to indicate that the command issued failed because the person/channel not being on IRC.
+ * - Used to indicate the nickname parameter supplied to a command is currently unused.
+ */
+void Engine::numericReply_401(Message &msg)
+{
+ emit incomingNoSuchNickname( Kopete::Message::unescape(msg.arg(1)) );
+}
+
+/* 406: "<nickname> :There was no such nickname"
+ * Like case 401, but when there *was* no such nickname.
+ */
+void Engine::numericReply_406(Message &msg)
+{
+ emit incomingNoSuchNickname( Kopete::Message::unescape(msg.arg(1)) );
+}
+
+/* 422: ":MOTD File is missing"
+ *
+ * Server's MOTD file could not be opened by the server.
+ */
+void Engine::numericReply_422(Message &msg)
+{
+ emit incomingMotd(msg.suffix());
+}
+
+/* 433: "<nick> :Nickname is already in use"
+ * Tells us that our nickname is already in use.
+ */
+void Engine::numericReply_433(Message &msg)
+{
+ if(m_status == Authentifying)
+ {
+ // This tells us that our nickname is, but we aren't logged in.
+ // This differs because the server won't send us a response back telling us our nick changed
+ // (since we aren't logged in).
+ m_FailedNickOnLogin = true;
+ emit incomingFailedNickOnLogin(Kopete::Message::unescape(msg.arg(1)));
+ }
+ else
+ {
+ // And this is the signal for if someone is trying to use the /nick command or such when already logged in,
+ // but it's already in use
+ emit incomingNickInUse(Kopete::Message::unescape(msg.arg(1)));
+ }
+}
+
+/* 464: ":Password Incorrect"
+ * Bad server password
+ */
+void Engine::numericReply_464(Message &/*msg*/)
+{
+ /* Server need pass.. Call disconnect*/
+ emit incomingFailedServerPassword();
+}
+
+/* 471:
+ * Channel is Full
+ */
+void Engine::numericReply_471(Message &msg)
+{
+ emit incomingFailedChanFull(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 473:
+ * Invite Only.
+ */
+void Engine::numericReply_473(Message &msg)
+{
+ emit incomingFailedChanInvite(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 474:
+ * Banned.
+ */
+void Engine::numericReply_474(Message &msg)
+{
+ emit incomingFailedChanBanned(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 475:
+ * Wrong Chan-key.
+ */
+void Engine::numericReply_475(Message &msg)
+{
+ emit incomingFailedChankey(Kopete::Message::unescape(msg.arg(1)));
+}
+
+/* 477: "<channel> :You need a registered nick to join that channel."
+ * Available on DALNET servers only ?
+ */
+// void Engine::numericReply_477(Message &msg)
+// {
+// emit incomingChannelNeedRegistration(msg.arg(2), msg.suffix());
+// }
diff --git a/kopete/protocols/irc/libkirc/kircentity.cpp b/kopete/protocols/irc/libkirc/kircentity.cpp
new file mode 100644
index 00000000..6aa6fd55
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircentity.cpp
@@ -0,0 +1,132 @@
+/*
+ kircentity.cpp - IRC Client
+
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kircengine.h"
+#include "kircentity.h"
+
+#include <kdebug.h>
+
+using namespace KIRC;
+using namespace KNetwork;
+
+/**
+ * Match a possible user definition:
+ * nick!user@host
+ * where user and host are optionnal.
+ * NOTE: If changes are done to the regexp string, update also the sm_userStrictRegExp regexp string.
+ */
+const QRegExp Entity::sm_userRegExp(QString::fromLatin1("^([^\\s,:!@]+)(?:(?:!([^\\s,:!@]+))?(?:@([^\\s,!@]+)))?$"));
+
+/**
+ * Regexp to match strictly the complete user definition:
+ * nick!user@host
+ * NOTE: If changes are done to the regexp string, update also the sm_userRegExp regexp string.
+ */
+const QRegExp Entity::sm_userStrictRegExp(QString::fromLatin1("^([^\\s,:!@]+)!([^\\s,:!@]+)@([^\\s,:!@]+)$"));
+
+const QRegExp Entity::sm_channelRegExp( QString::fromLatin1("^[#!+&][^\\s,]+$") );
+
+Entity::Entity(const QString &, const Type type)
+ : QObject(0, "KIRC::Entity"),
+ m_type(type)
+{
+// rename(name, type);
+}
+
+Entity::~Entity()
+{
+ emit destroyed(this);
+}
+
+QString Entity::name() const
+{
+ return m_name;
+}
+
+QString Entity::host() const
+{
+ switch(m_type)
+ {
+// case Unknown:
+ case Server:
+ return m_name;
+// case Channel:
+ case Service:
+ case User:
+ return userHost();
+ default:
+ kdDebug(14121) << k_funcinfo << "No host defined for type:" << m_type;
+ return QString::null;
+ }
+}
+
+KIRC::Entity::Type Entity::type() const
+{
+ return m_type;
+}
+
+KIRC::Entity::Type Entity::guessType()
+{
+ m_type = guessType(m_name);
+ return m_type;
+}
+
+// FIXME: Implement me
+KIRC::Entity::Type Entity::guessType(const QString &)
+{
+ return Unknown;
+}
+
+QString Entity::userNick() const
+{
+ return userNick(m_name);
+}
+
+QString Entity::userNick(const QString &s)
+{
+ return userInfo(s, 1);
+}
+
+QString Entity::userName() const
+{
+ return userName(m_name);
+}
+
+QString Entity::userName(const QString &s)
+{
+ return userInfo(s, 2);
+}
+
+QString Entity::userHost() const
+{
+ return userHost(m_name);
+}
+
+QString Entity::userHost(const QString &s)
+{
+ return userInfo(s, 3);
+}
+
+QString Entity::userInfo(const QString &s, int num)
+{
+ QRegExp userRegExp(sm_userRegExp);
+ userRegExp.search(s);
+ return userRegExp.cap(num);
+}
+
+#include "kircentity.moc"
+
diff --git a/kopete/protocols/irc/libkirc/kircentity.h b/kopete/protocols/irc/libkirc/kircentity.h
new file mode 100644
index 00000000..c9336439
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircentity.h
@@ -0,0 +1,128 @@
+/*
+ kircentity.h - IRC Client
+
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCENTITY_H
+#define KIRCENTITY_H
+
+#include <kdeversion.h>
+#include <kresolver.h>
+#include <ksharedptr.h>
+
+#include <qobject.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qvaluelist.h>
+
+namespace KIRC
+{
+
+class Engine;
+
+class Entity
+ : public QObject,
+ public KShared
+{
+ Q_OBJECT
+
+public:
+ typedef enum Type
+ {
+ Unknown,
+ Server,
+ Channel,
+ Service,
+ User
+ };
+
+ Entity(const QString &name, const Type type = Unknown);
+ virtual ~Entity();
+
+ QString name() const;
+ QString host() const;
+
+ KIRC::Entity::Type type() const;
+ KIRC::Entity::Type guessType();
+ static KIRC::Entity::Type guessType(const QString &name);
+
+ // FIXME: Remove these is* functions ... They are duplicate with the ::guessType(const QString&)
+ inline static bool isUser( const QString &s )
+ { return sm_userRegExp.exactMatch(s); };
+ inline bool isChannel()
+ { return isChannel(m_name); };
+ inline static bool isChannel( const QString &s )
+ { return sm_channelRegExp.exactMatch(s); };
+
+ QString userNick() const;
+ static QString userNick(const QString &s);
+
+ QString userName() const;
+ static QString userName(const QString &s);
+
+ QString userHost() const;
+ static QString userHost(const QString &s);
+
+signals:
+ void destroyed(KIRC::Entity *self);
+
+private:
+
+ static QString userInfo(const QString &s, int num_cap);
+
+ static const QRegExp sm_userRegExp;
+ static const QRegExp sm_userStrictRegExp;
+ static const QRegExp sm_channelRegExp;
+
+ KIRC::Entity::Type m_type;
+ QString m_name;
+
+ // peer ip address if the entity is a User.
+ QString m_address;
+};
+
+class EntityPtr
+ : public KSharedPtr<KIRC::Entity>
+{
+public:
+ EntityPtr(KIRC::Entity *entity = 0)
+ : KSharedPtr<KIRC::Entity>(entity)
+ { }
+
+ EntityPtr(const KIRC::EntityPtr &entity)
+ : KSharedPtr<KIRC::Entity>(entity)
+ { }
+};
+
+class EntityPtrList
+ : public QValueList<EntityPtr>
+{
+public:
+ EntityPtrList()
+ { }
+
+ EntityPtrList(const EntityPtr &entity)
+ {
+ append(entity);
+ }
+
+ EntityPtrList(const QValueList<EntityPtr> &list)
+ : QValueList<EntityPtr>(list)
+ { }
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/kircmessage.cpp b/kopete/protocols/irc/libkirc/kircmessage.cpp
new file mode 100644
index 00000000..f1a5b61f
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircmessage.cpp
@@ -0,0 +1,370 @@
+/*
+ kircmessage.cpp - IRC Client
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete engineelopers <kopete-engineel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kircengine.h"
+#include "kircmessage.h"
+
+// FIXME: Remove the following dependencies.
+#include "kopetemessage.h"
+#include "ksparser.h"
+
+#include <kdebug.h>
+#include <kextsock.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+using namespace KIRC;
+
+#ifndef _IRC_STRICTNESS_
+QRegExp Message::m_IRCNumericCommand("^\\d{1,3}$");
+
+// TODO: This regexp parsing is no good. It's slower than it needs to be, and
+// is not codec-safe since QString requires a codec. NEed to parse this with
+// our own parsing class that operates on the raw QCStrings
+QRegExp Message::m_IRCCommandType1(
+ "^(?::([^ ]+) )?([A-Za-z]+|\\d{1,3})((?: [^ :][^ ]*)*) ?(?: :(.*))?$");
+ // Extra end arg space check -------------------------^
+#else // _IRC_STRICTNESS_
+QRegExp Message::m_IRCNumericCommand("^\\d{3,3}$");
+
+QRegExp Message::m_IRCCommandType1(
+ "^(?::([^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){0,13})(?: :(.*))?$");
+QRegExp Message::m_IRCCommandType2(
+ "^(?::[[^ ]+) )?([A-Za-z]+|\\d{3,3})((?: [^ :][^ ]*){14,14})(?: (.*))?$");
+#endif // _IRC_STRICTNESS_
+
+Message::Message()
+ : m_ctcpMessage(0)
+{
+}
+
+Message::Message(const Message &obj)
+ : m_ctcpMessage(0)
+{
+ m_raw = obj.m_raw;
+
+ m_prefix = obj.m_prefix;
+ m_command = obj.m_command;
+ m_args = obj.m_args;
+ m_suffix = obj.m_suffix;
+
+ m_ctcpRaw = obj.m_ctcpRaw;
+
+ if (obj.m_ctcpMessage)
+ m_ctcpMessage = new Message(obj.m_ctcpMessage);
+}
+
+Message::Message(const Message *obj)
+ : m_ctcpMessage(0)
+{
+ m_raw = obj->m_raw;
+
+ m_prefix = obj->m_prefix;
+ m_command = obj->m_command;
+ m_args = obj->m_args;
+ m_suffix = obj->m_suffix;
+
+ m_ctcpRaw = obj->m_ctcpRaw;
+
+ if (obj->m_ctcpMessage)
+ m_ctcpMessage = new Message(obj->m_ctcpMessage);
+}
+
+Message::~Message()
+{
+ if (m_ctcpMessage)
+ delete m_ctcpMessage;
+}
+
+void Message::writeRawMessage(Engine *engine, const QTextCodec *codec, const QString &str)
+{
+ // FIXME: Really handle this
+ if (!engine->socket())
+ {
+ kdDebug(14121) << k_funcinfo << "Not connected while attempting to write:" << str << endl;
+ return;
+ }
+
+ QString txt = str + QString::fromLatin1("\r\n");
+
+ QCString s(codec->fromUnicode(txt));
+ kdDebug(14120) << "Message is " << s.length() << " chars" << endl;
+ // FIXME: Should check the amount of data really writen.
+ int wrote = engine->socket()->writeBlock(s.data(), s.length());
+
+ kdDebug(14121) << QString::fromLatin1("(%1 bytes) >> %2").arg(wrote).arg(str) << endl;
+}
+
+void Message::writeMessage(Engine *engine, const QTextCodec *codec, const QString &message)
+{
+ writeRawMessage(engine, codec, quote(message));
+}
+
+void Message::writeMessage(Engine *engine, const QTextCodec *codec,
+ const QString &command, const QStringList &args, const QString &suffix)
+{
+ QString msg = command;
+
+ if (!args.isEmpty())
+ msg += QChar(' ') + args.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here
+
+ if (!suffix.isNull())
+ msg = msg.stripWhiteSpace() + QString::fromLatin1(" :") + suffix;
+
+ writeMessage(engine, codec, msg);
+}
+
+void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec,
+ const QString &command, const QString&to,
+ const QString &ctcpMessage)
+{
+ writeMessage(engine, codec, command, to, QChar(0x01) + ctcpQuote(ctcpMessage) + QChar(0x01));
+}
+
+void Message::writeCtcpMessage(Engine *engine, const QTextCodec *codec,
+ const QString &command, const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs, const QString &ctcpSuffix )
+{
+ QString ctcpMsg = ctcpCommand;
+
+ if (!ctcpArgs.isEmpty())
+ ctcpMsg += QChar(' ') + ctcpArgs.join(QChar(' ')).stripWhiteSpace(); // some extra check should be done here
+
+ if (!ctcpSuffix.isNull())
+ ctcpMsg += QString::fromLatin1(" :") + ctcpSuffix;
+
+ writeMessage(engine, codec, command, to, suffix + QChar(0x01) + ctcpQuote(ctcpMsg) + QChar(0x01));
+}
+
+Message Message::parse(Engine *engine, const QTextCodec *codec, bool *parseSuccess)
+{
+ if (parseSuccess)
+ *parseSuccess=false;
+
+ if (engine->socket()->canReadLine())
+ {
+ QCString raw(engine->socket()->bytesAvailable()+1);
+ Q_LONG length = engine->socket()->readLine(raw.data(), raw.count());
+
+ if( length > -1 )
+ {
+ raw.resize( length );
+
+ // Remove trailing '\r\n' or '\n'.
+ //
+ // Some servers send '\n' instead of '\r\n' that the RFCs say they should be sending.
+
+ if (length > 1 && raw.at(length-2) == '\n') {
+ raw.at(length-2) = '\0';
+ }
+ if (length > 2 && raw.at(length-3) == '\r') {
+ raw.at(length-3) = '\0';
+ }
+
+ kdDebug(14121) << "<< " << raw << endl;
+
+ Message msg;
+ if(matchForIRCRegExp(raw, codec, msg))
+ {
+ if(parseSuccess)
+ *parseSuccess = true;
+ }
+ else
+ {
+ kdDebug(14120) << k_funcinfo << "Unmatched line: \"" << raw << "\"" << endl;
+ }
+
+ return msg;
+ }
+ else
+ kdWarning(14121) << k_funcinfo << "Failed to read a line while canReadLine returned true!" << endl;
+ }
+
+ return Message();
+}
+
+QString Message::quote(const QString &str)
+{
+ QString tmp = str;
+ QChar q('\020');
+ tmp.replace(q, q+QString(q));
+ tmp.replace(QChar('\r'), q+QString::fromLatin1("r"));
+ tmp.replace(QChar('\n'), q+QString::fromLatin1("n"));
+ tmp.replace(QChar('\0'), q+QString::fromLatin1("0"));
+ return tmp;
+}
+
+// FIXME: The unquote system is buggy.
+QString Message::unquote(const QString &str)
+{
+ QString tmp = str;
+
+ char b[3] = { 020, 020, '\0' };
+ const char b2[2] = { 020, '\0' };
+
+ tmp.replace( b, b2 );
+ b[1] = 'r';
+ tmp.replace( b, "\r");
+ b[1] = 'n';
+ tmp.replace( b, "\n");
+ b[1] = '0';
+ tmp.replace( b, "\0");
+
+ return tmp;
+}
+
+QString Message::ctcpQuote(const QString &str)
+{
+ QString tmp = str;
+ tmp.replace( QChar('\\'), QString::fromLatin1("\\\\"));
+ tmp.replace( (char)1, QString::fromLatin1("\\1"));
+ return tmp;
+}
+
+QString Message::ctcpUnquote(const QString &str)
+{
+ QString tmp = str;
+ tmp.replace("\\\\", "\\");
+ tmp.replace("\\1", "\1" );
+ return tmp;
+}
+
+bool Message::matchForIRCRegExp(const QCString &line, const QTextCodec *codec, Message &message)
+{
+ if(matchForIRCRegExp(m_IRCCommandType1, codec, line, message))
+ return true;
+#ifdef _IRC_STRICTNESS_
+ if(!matchForIRCRegExp(m_IRCCommandType2, codec, line, message))
+ return true;
+#endif // _IRC_STRICTNESS_
+ return false;
+}
+
+// FIXME: remove the decodeStrings calls or update them.
+// FIXME: avoid the recursive call, it make the ctcp command unquoted twice (wich is wrong, but valid in most of the cases)
+bool Message::matchForIRCRegExp(QRegExp &regexp, const QTextCodec *codec, const QCString &line, Message &msg )
+{
+ if( regexp.exactMatch( codec->toUnicode(line) ) )
+ {
+ msg.m_raw = line;
+ msg.m_prefix = unquote(regexp.cap(1));
+ msg.m_command = unquote(regexp.cap(2));
+ msg.m_args = QStringList::split(' ', regexp.cap(3));
+
+ QCString suffix = codec->fromUnicode(unquote(regexp.cap(4)));
+ if (!suffix.isNull() && suffix.length() > 0)
+ {
+ QCString ctcpRaw;
+ if (extractCtcpCommand(suffix, ctcpRaw))
+ {
+ msg.m_ctcpRaw = codec->toUnicode(ctcpRaw);
+
+ msg.m_ctcpMessage = new Message();
+ msg.m_ctcpMessage->m_raw = codec->fromUnicode(ctcpUnquote(msg.m_ctcpRaw));
+
+ int space = ctcpRaw.find(' ');
+ if (!matchForIRCRegExp(msg.m_ctcpMessage->m_raw, codec, *msg.m_ctcpMessage))
+ {
+ QCString command;
+ if (space > 0)
+ command = ctcpRaw.mid(0, space).upper();
+ else
+ command = ctcpRaw.upper();
+ msg.m_ctcpMessage->m_command =
+ Kopete::Message::decodeString( KSParser::parse(command), codec );
+ }
+
+ if (space > 0)
+ msg.m_ctcpMessage->m_ctcpRaw =
+ Kopete::Message::decodeString( KSParser::parse(ctcpRaw.mid(space)), codec );
+ }
+
+ msg.m_suffix = Kopete::Message::decodeString( KSParser::parse(suffix), codec );
+ }
+ else
+ msg.m_suffix = QString::null;
+ return true;
+ }
+ return false;
+}
+
+void Message::decodeAgain( const QTextCodec *codec )
+{
+ matchForIRCRegExp(m_raw, codec, *this);
+}
+
+// FIXME: there are missing parts
+QString Message::toString() const
+{
+ if( !isValid() )
+ return QString::null;
+
+ QString msg = m_command;
+ for (QStringList::ConstIterator it = m_args.begin(); it != m_args.end(); ++it)
+ msg += QChar(' ') + *it;
+ if (!m_suffix.isNull())
+ msg += QString::fromLatin1(" :") + m_suffix;
+
+ return msg;
+}
+
+bool Message::isNumeric() const
+{
+ return m_IRCNumericCommand.exactMatch(m_command);
+}
+
+bool Message::isValid() const
+{
+// This could/should be more complex but the message validity is tested durring the parsing
+// So this is enougth as we don't allow the editing the content.
+ return !m_command.isEmpty();
+}
+
+/* Return true if the given string is a special command string
+ * (i.e start and finish with the ascii code \001), and the given
+ * string is splited to get the first part of the message and fill the ctcp command.
+ * FIXME: The code currently only match for a textual message or a ctcp message not both mixed as it can be (even if very rare).
+ */
+bool Message::extractCtcpCommand(QCString &message, QCString &ctcpline)
+{
+ uint len = message.length();
+
+ if( message[0] == 1 && message[len-1] == 1 )
+ {
+ ctcpline = message.mid(1,len-2);
+ message.truncate(0);
+
+ return true;
+ }
+
+ return false;
+}
+
+void Message::dump() const
+{
+ kdDebug(14120) << "Raw:" << m_raw << endl
+ << "Prefix:" << m_prefix << endl
+ << "Command:" << m_command << endl
+ << "Args:" << m_args << endl
+ << "Suffix:" << m_suffix << endl
+ << "CtcpRaw:" << m_ctcpRaw << endl;
+ if(m_ctcpMessage)
+ {
+ kdDebug(14120) << "Contains CTCP Message:" << endl;
+ m_ctcpMessage->dump();
+ }
+}
diff --git a/kopete/protocols/irc/libkirc/kircmessage.h b/kopete/protocols/irc/libkirc/kircmessage.h
new file mode 100644
index 00000000..e37f3fb2
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircmessage.h
@@ -0,0 +1,198 @@
+/*
+ kircmessage.h - IRC Client
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCMESSAGE_H
+#define KIRCMESSAGE_H
+
+#include "kircentity.h"
+
+#include <kbufferedio.h>
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+#include <qregexp.h>
+
+#include <kopetemessage.h>
+
+// Uncoment this if you want a really rfc compliant message handling.
+// This is due to some changes of the message encoding with 14 arguments.(not very frequent :)
+// #define _IRC_STRICTNESS_
+
+namespace KIRC
+{
+
+class Engine;
+
+class Message
+{
+public:
+ /** \brief Sends the message as-is to the server.
+ */
+ static void writeRawMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str);
+
+ static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec, const QString &str);
+
+ static void writeMessage(KIRC::Engine *engine, const QTextCodec *codec,
+ const QString &command, const QStringList &args, const QString &suffix);
+
+ static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec,
+ const QString &command, const QString &to,
+ const QString &ctcpMessage);
+
+ static void writeCtcpMessage(KIRC::Engine *engine, const QTextCodec *codec,
+ const QString &command, const QString &to, const QString &suffix,
+ const QString &ctcpCommand, const QStringList &ctcpArgs = QStringList(), const QString &ctcpSuffix = QString::null );
+
+ Message();
+ Message(const KIRC::Message &obj);
+ Message(const KIRC::Message *obj);
+
+ ~Message();
+
+ inline const QString nickFromPrefix() const
+ { return Kopete::Message::unescape(KIRC::Entity::userNick(m_prefix)); }
+
+ QString toString() const;
+
+ /** \brief Returns true if the message command is numeric.
+ */
+ bool isNumeric() const;
+
+ /** \brief Message is valid if it was parsed correctly.
+ */
+ bool isValid() const;
+
+ /** \brief Writes internal message information about this message through kdDebug().
+ */
+ void dump() const;
+
+ /** \brief Re-decodes the message with given codec.
+ */
+ void decodeAgain( const QTextCodec *codec );
+
+ /** \brief The whole message as received.
+ */
+ inline const QCString &raw() const
+ { return m_raw; }
+
+ /** \brief Prefix of this message.
+ *
+ * Returns the prefix of the message. Note that it can be empty.
+ *
+ * Prefix is the server name or the nick name of the sender.
+ *
+ * message = [ ":" prefix SPACE ] command [ params ] crlf
+ * prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
+ */
+ inline const QString &prefix() const
+ { return m_prefix; }
+
+ /** \brief The command part of this message.
+ *
+ * Returns the command of this message. Can be numerical.
+ *
+ * Examples: "MODE", "PRIVMSG", 303, 001, ...
+ */
+ inline const QString &command() const
+ { return m_command; }
+
+ /** \brief The number of command arguments this message contains.
+ */
+ inline size_t argsSize() const
+ { return m_args.size(); }
+
+ /** \brief i:th command argument.
+ */
+ inline const QString &arg(size_t i) const
+ { return m_args[i]; }
+
+ /** \brief All command arguments.
+ */
+ inline const QStringList &args() const
+ { return m_args; }
+
+ /** \brief Message suffix.
+ */
+ inline const QString &suffix() const
+ { return m_suffix; }
+ inline const QString &ctcpRaw() const
+ { return m_ctcpRaw; }
+
+ inline bool hasCtcpMessage() const
+ { return m_ctcpMessage!=0; }
+ inline class KIRC::Message &ctcpMessage() const
+ { return *m_ctcpMessage; }
+
+ static KIRC::Message parse(KIRC::Engine *engine, const QTextCodec *codec, bool *parseSuccess=0);
+
+private:
+ /**
+ * Contains the low level dequoted message.
+ */
+ QCString m_raw;
+
+ /**
+ * Contains the completely dequoted prefix.
+ */
+ QString m_prefix;
+ /**
+ * Contains the completely dequoted command.
+ */
+ QString m_command;
+ /**
+ * Contains the completely dequoted args.
+ */
+ QStringList m_args;
+ /**
+ * Contains the completely dequoted suffix.
+ */
+ QString m_suffix;
+
+ /**
+ * If it is a message contains the completely dequoted rawCtcpLine.
+ * If it is a ctcp message contains the completely dequoted rawCtcpArgsLine.
+ */
+ QString m_ctcpRaw;
+
+ // low level quoting, message quoting
+ static QString quote(const QString &str);
+ static QString unquote(const QString &str);
+
+ // ctcp level quoting
+ static QString ctcpQuote(const QString &str);
+ static QString ctcpUnquote(const QString &str);
+
+ static bool extractCtcpCommand(QCString &str, QCString &ctcpline);
+
+ static bool matchForIRCRegExp(const QCString &line, const QTextCodec *codec, KIRC::Message &message);
+ static bool matchForIRCRegExp(QRegExp &regexp, const QTextCodec *codec, const QCString &line, KIRC::Message &message);
+
+ class KIRC::Message *m_ctcpMessage;
+
+ static QRegExp m_IRCCommandType1;
+ #ifdef _IRC_STRICTNESS_
+ static QRegExp m_IRCCommandType2;
+ #endif // _IRC_STRICTNESS_
+
+ static QRegExp m_IRCNumericCommand;
+};
+
+}
+
+#endif // KIRCMESSAGE_H
diff --git a/kopete/protocols/irc/libkirc/kircmessageredirector.cpp b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp
new file mode 100644
index 00000000..49194ce0
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircmessageredirector.cpp
@@ -0,0 +1,97 @@
+/*
+ kircmessageredirector.cpp - IRC Client
+
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "kircengine.h"
+#include "kircmessage.h"
+#include "kircmessageredirector.h"
+
+using namespace KIRC;
+
+MessageRedirector::MessageRedirector(KIRC::Engine *engine,
+ int argsSize_min, int argsSize_max, const QString &helpMessage)
+ : QObject(engine, "KIRC::MessageRedirector"),
+ m_argsSize_min(argsSize_min),
+ m_argsSize_max(argsSize_max),
+ m_helpMessage(helpMessage)
+{
+}
+
+bool MessageRedirector::connect(QObject *object, const char *member)
+{
+ return QObject::connect(this, SIGNAL(redirect(KIRC::Message &)),
+ object, member);
+}
+
+QStringList MessageRedirector::operator () (Message &msg)
+{
+ m_errors.clear();
+
+// if (m_connectedObjects == 0)
+// m_errors.append(i18n("Internal error: no more connected object, triggered by:")+msg);
+
+ if (checkValidity(msg))
+ emit redirect(msg);
+
+ return m_errors;
+}
+
+QString MessageRedirector::helpMessage()
+{
+ return m_helpMessage;
+}
+
+void MessageRedirector::error(QString &message)
+{
+ m_errors.append(message);
+}
+
+bool MessageRedirector::checkValidity(const Message &msg)
+{
+ bool success = true;
+ int argsSize = msg.argsSize();
+
+ if (m_argsSize_min >= 0 && argsSize < m_argsSize_min)
+ {
+// m_errors.append(i18n("Not enougth arguments in message:")+msg);
+ success = false;
+ }
+
+#ifdef _IRC_STRICTNESS_
+ if (m_argsSize_max >= 0 && argsSize > m_argsSize_max)
+ {
+// m_errors.append(i18n("Too many arguments in message:")+msg);
+ success = false;
+ }
+#endif
+/*
+ if ( msg.isNumeric() &&
+ ( msg.argsSize() > 0 && (
+ msg.arg(0) == m_Nickname ||
+ msg.arg(0) == m_PendingNick ||
+ msg.arg(0) == QString::fromLatin1("*")
+ )
+ )
+ )
+ {
+// m_errors.append(i18n("Too many arguments in message:")+msg);
+ success = false;
+ }
+*/
+ return success;
+}
+
+#include "kircmessageredirector.moc"
diff --git a/kopete/protocols/irc/libkirc/kircmessageredirector.h b/kopete/protocols/irc/libkirc/kircmessageredirector.h
new file mode 100644
index 00000000..f87a2af6
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kircmessageredirector.h
@@ -0,0 +1,86 @@
+/*
+ kircmessageredirector.h - IRC Client
+
+ Copyright (c) 2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRC_MESSAGEREDIRECTOR_H
+#define KIRC_MESSAGEREDIRECTOR_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+namespace KIRC
+{
+
+class Engine;
+
+class Message;
+
+class MessageRedirector
+ : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum {
+ Unknown = -1,
+ Unlimited = -2
+ };
+
+ MessageRedirector(KIRC::Engine *engine,
+ int argsSize_min = KIRC::MessageRedirector::Unknown,
+ int argsSize_max = KIRC::MessageRedirector::Unknown,
+ const QString &helpMessage = QString::null);
+
+ /**
+ * Connects the given object member signal/slot to this message redirector.
+ * The member signal slot should be looking like:
+ * SIGNAL(mysignal(KIRC::Message &msg))
+ * or
+ * SIGNAL(myslot(KIRC::Message &msg))
+ */
+ bool connect(QObject *object, const char *member);
+
+ /**
+ * Attempt to send the message.
+ * @return a not empty QStringList on errors or no slots connected.
+ * The returned string list contains all the errors.
+ */
+ QStringList operator()(KIRC::Message &msg);
+
+ void error(QString &errorMessage);
+
+ QString helpMessage();
+
+signals:
+ void redirect(KIRC::Message &);
+
+private:
+ /**
+ * Check that the given message as the correct number of args
+ * and do some message format checks.
+ */
+ bool checkValidity(const KIRC::Message &msg);
+
+ QStringList m_errors;
+
+ int m_argsSize_min;
+ int m_argsSize_max;
+ QString m_helpMessage;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/kirctransfer.cpp b/kopete/protocols/irc/libkirc/kirctransfer.cpp
new file mode 100644
index 00000000..2466d6a9
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransfer.cpp
@@ -0,0 +1,365 @@
+/*
+ kirctransfer.cpp - IRC transfer.
+
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <kextsock.h>
+#include <klocale.h>
+
+#include <qfile.h>
+#include <qtimer.h>
+
+#include "kirctransfer.h"
+
+using namespace KIRC;
+
+Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress
+ Type type,
+ QObject *parent, const char *name )
+ : QObject( parent, name ),
+ m_engine(engine), m_nick(nick),
+ m_type(type), m_socket(0),
+ m_initiated(false),
+ m_file(0), m_fileName(QString::null), m_fileSize(0), m_fileSizeCur(0), m_fileSizeAck(0),
+ m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0)
+{
+}
+
+Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize, // put this in a QVariant ?
+ QObject *parent, const char *name )
+ : QObject( parent, name ),
+ m_engine(engine), m_nick(nick),
+ m_type(type), m_socket(0),
+ m_initiated(false),
+ m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0),
+ m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0)
+{
+}
+
+Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress
+ QHostAddress hostAdress, Q_UINT16 port, // put this in a QVariant ?
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize, // put this in a QVariant ?
+ QObject *parent, const char *name )
+ : QObject( parent, name ),
+ m_engine(engine), m_nick(nick),
+ m_type(type), m_socket(0),
+ m_initiated(false),
+ m_file(0), m_fileName(fileName), m_fileSize(fileSize), m_fileSizeCur(0), m_fileSizeAck(0),
+ m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0)
+{
+ setSocket(new KExtendedSocket(hostAdress.toString(), port));
+}
+/*
+Transfer::Transfer( Engine *engine, QString nick,// QString nick_peer_adress
+ Transfer::Type type, QVariant properties,
+ QObject *parent, const char *name )
+ : QObject( parent, name ),
+ m_engine(engine), m_nick(nick),
+ m_type(type), m_socket(properties[socket]),
+ m_initiated(false),
+ m_file(0), m_fileName(properties[fileName]), m_fileSize(properties[fileSize]), m_fileSizeCur(0), m_fileSizeAck(0),
+ m_receivedBytes(0), m_receivedBytesLimit(0), m_sentBytes(0), m_sentBytesLimit(0)
+{
+ if(!properites["socket"].isNull())
+ setSocket(properites["socket"]);
+ else if(!properites["hostAddress"].isNull() && !properites["hostPort"].isNull())
+ setSocket(new KExtendedSocket(properites["hostAddress"], properites["hostPort"]));
+
+ connect(this, SIGNAL(complete()),
+ this, SLOT(closeSocket()));
+
+ connect(this, SIGNAL(abort(QString)),
+ this, SLOT(closeSocket()));
+}
+*/
+Transfer::~Transfer()
+{
+ closeSocket();
+ // m_file is automatically closed on destroy.
+}
+
+Transfer::Status Transfer::status() const
+{
+ if(m_socket)
+ {
+// return (Transfer::Status)m_socket->socketStatus();
+ return Connected;
+ }
+ return Error_NoSocket;
+}
+
+void Transfer::slotError( int error )
+{
+ // Connection in progress.. This is a signal fired wrong
+ if (m_socket->socketStatus () != KExtendedSocket::connecting)
+ {
+ abort(KExtendedSocket::strError(m_socket->status(), m_socket->systemError()));
+// closeSocket();
+ }
+}
+
+bool Transfer::initiate()
+{
+ QTimer *timer = 0;
+
+ if(m_initiated)
+ {
+ kdDebug(14121) << k_funcinfo << "Transfer allready initiated" << endl;
+ return false;
+ }
+
+ if(!m_socket)
+ {
+ kdDebug(14121) << k_funcinfo << "Socket not set" << endl;
+ return false;
+ }
+
+ m_initiated = true;
+
+ m_file.setName(m_fileName);
+
+ connect(this, SIGNAL(complete()),
+ this, SLOT(closeSocket()));
+ connect(this, SIGNAL(abort(QString)),
+ this, SLOT(closeSocket()));
+
+// connect(m_socket, SIGNAL(connectionClosed()),
+// this, SLOT(slotConnectionClosed()));
+// connect(m_socket, SIGNAL(delayedCloseFinished()),
+// this, SLOT(slotConnectionClosed()));
+ connect(m_socket, SIGNAL(error(int)), // FIXME: connection failed: No such signal KExtendedSocket::error(int)
+ this, SLOT(slotError(int)));
+
+ switch( m_type )
+ {
+ case Chat:
+ kdDebug(14121) << k_funcinfo << "Stting up a chat." << endl;
+ connect(m_socket, SIGNAL(readyRead()),
+ this, SLOT(readyReadFileIncoming()));
+ break;
+ case FileIncoming:
+ kdDebug(14121) << k_funcinfo << "Stting up an incoming file transfer." << endl;
+ m_file.open(IO_WriteOnly);
+ connect(m_socket, SIGNAL(readyRead()),
+ this, SLOT(readyReadFileIncoming()));
+ break;
+ case FileOutgoing:
+ kdDebug(14121) << k_funcinfo << "Stting up an outgoing file transfer." << endl;
+ m_file.open(IO_ReadOnly);
+ connect(m_socket, SIGNAL(readyRead()),
+ this, SLOT(readyReadFileOutgoing()));
+// timer = new QTimer(this);
+// connect(timer, SIGNAL(timeout()),
+// this, SLOT(writeFileOutgoing()));
+// timer->start(1000, false);
+ writeFileOutgoing(); // send a first packet.
+ break;
+ default:
+ kdDebug(14121) << k_funcinfo << "Closing transfer: Unknown extra initiation for type:" << m_type << endl;
+ m_socket->close();
+ return false;
+ break;
+ }
+
+// if(status()==Idle)
+ if(m_socket->status()==KExtendedSocket::nothing)
+ m_socket->connect();
+
+ m_socket->enableRead(true);
+ m_socket->enableWrite(true);
+
+ m_socketDataStream.setDevice(m_socket);
+
+ // I wonder if calling this is really necessary
+ // As far as I understand, buffer (socket buffer at least) should be flushed while event-looping.
+ // But I'm not really sure of this, so I force the flush.
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()),
+ this, SLOT(flush()));
+ timer->start(1000, FALSE); // flush the streams at every seconds
+
+ return true;
+}
+
+bool Transfer::setSocket( KExtendedSocket *socket )
+{
+ if (!m_socket)
+ {
+ m_socket = socket;
+ return true;
+ }
+ else
+ kdDebug(14121) << k_funcinfo << "Socket allready set" << endl;
+ return false;
+}
+
+void Transfer::closeSocket()
+{
+ if(m_socket)
+ {
+ m_socket->close();
+// m_socket->reset();
+ m_socket->deleteLater();
+ }
+ m_socket = 0;
+}
+
+/*
+ * This slot ensure that all the stream are flushed.
+ * This slot is called periodically internaly.
+ */
+ void Transfer::flush()
+{
+ /*
+ * Enure the incoming file content in case of a crash.
+ */
+ if(m_file.isOpen() && m_file.isWritable())
+ m_file.flush();
+
+ /*
+ * Ensure that non interactive streams outputs (i.e file transfer acknowledge by example)
+ * are sent (Don't stay in a local buffer).
+ */
+ if(m_socket && status() == Connected)
+ m_socket->flush();
+}
+
+void Transfer::userAbort(QString msg)
+{
+ emit abort(msg);
+}
+
+void Transfer::setCodec( QTextCodec *codec )
+{
+ switch( m_type )
+ {
+ case Chat:
+ m_socket_textStream.setCodec( codec );
+ break;
+ default:
+// operation not permitted on this type.
+ break;
+ }
+}
+
+void Transfer::writeLine( const QString &line )
+{
+ switch( m_type )
+ {
+ case Chat:
+// m_socket.flush();
+ break;
+ default:
+// operation not permitted on this type.
+ break;
+ }
+}
+
+void Transfer::readyReadLine()
+{
+ if( m_socket->canReadLine() )
+ {
+ QString msg = m_socket_textStream.readLine();
+ emit readLine(msg);
+ }
+}
+
+void Transfer::readyReadFileIncoming()
+{
+ kdDebug(14121) << k_funcinfo << endl;
+
+ m_bufferLength = m_socket->readBlock(m_buffer, sizeof(m_buffer));
+
+ if(m_bufferLength > 0)
+ {
+ int written = m_file.writeBlock(m_buffer, m_bufferLength);
+ if(m_bufferLength == written)
+ {
+ m_fileSizeCur += written;
+ m_fileSizeAck = m_fileSizeCur;
+ m_socketDataStream << m_fileSizeAck;
+ checkFileTransferEnd(m_fileSizeAck);
+ return;
+ }
+ else
+ // Something bad happened while writting.
+ abort(m_file.errorString());
+ }
+ else if(m_bufferLength == -1)
+ abort("Error while reading socket.");
+}
+
+void Transfer::readyReadFileOutgoing()
+{
+ kdDebug(14121) << k_funcinfo << "Available bytes:" << m_socket->bytesAvailable() << endl;
+
+ bool hadData = false;
+ Q_UINT32 fileSizeAck = 0;
+
+// if (m_socket->bytesAvailable() >= sizeof(fileSizeAck)) // BUGGY: bytesAvailable() that allways return 0 on unbuffered sockets.
+ {
+ m_socketDataStream >> fileSizeAck;
+ hadData = true;
+ }
+
+ if (hadData)
+ {
+ checkFileTransferEnd(fileSizeAck);
+ writeFileOutgoing();
+ }
+}
+
+void Transfer::writeFileOutgoing()
+{
+ kdDebug(14121) << k_funcinfo << endl;
+
+ if (m_fileSizeAck < m_fileSize)
+ {
+ m_bufferLength = m_file.readBlock(m_buffer, sizeof(m_buffer));
+ if (m_bufferLength > 0)
+ {
+ Q_UINT32 read = m_socket->writeBlock(m_buffer, m_bufferLength); // should check written == read
+
+// if(read != m_buffer_length)
+// buffer is not cleared still
+
+ m_fileSizeCur += read;
+// m_socket->flush(); // Should think on using this
+ emit fileSizeCurrent( m_fileSizeCur );
+ }
+ else if(m_bufferLength == -1)
+ abort("Error while reading file.");
+ }
+}
+
+void Transfer::checkFileTransferEnd(Q_UINT32 fileSizeAck)
+{
+ kdDebug(14121) << k_funcinfo << "Acknowledged:" << fileSizeAck << endl;
+
+ m_fileSizeAck = fileSizeAck;
+ emit fileSizeAcknowledge(m_fileSizeAck);
+
+ if(m_fileSizeAck > m_fileSize)
+ abort(i18n("Acknowledge size is greater than the expected file size"));
+
+ if(m_fileSizeAck == m_fileSize)
+ emit complete();
+}
+
+#include "kirctransfer.moc"
diff --git a/kopete/protocols/irc/libkirc/kirctransfer.h b/kopete/protocols/irc/libkirc/kirctransfer.h
new file mode 100644
index 00000000..3453f5cb
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransfer.h
@@ -0,0 +1,191 @@
+/*
+ kirctransfer.h - DCC Handler
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCTRANSFER_H
+#define KIRCTRANSFER_H
+
+#include <qdatastream.h>
+#include <qfile.h>
+#include <qhostaddress.h>
+#include <qobject.h>
+#include <qtextstream.h>
+
+class KExtendedSocket;
+
+class QFile;
+class QTextCodec;
+
+namespace KIRC
+{
+class Engine;
+
+class Transfer
+ : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Type {
+ Unknown,
+ Chat,
+ FileOutgoing,
+ FileIncoming
+ };
+
+ enum Status {
+ Error_NoSocket = -2,
+ Error = -1,
+ Idle = 0,
+ HostLookup,
+ Connecting,
+ Connected,
+ Closed
+ };
+public:
+ Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress
+ Type type = Unknown,
+ QObject *parent = 0L, const char *name = 0L );
+
+ Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress,
+ QHostAddress peer_address, Q_UINT16 peer_port,
+ Transfer::Type type,
+ QObject *parent = 0L, const char *name = 0L );
+
+ Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize,
+ QObject *parent = 0L, const char *name = 0L );
+
+ Transfer( KIRC::Engine *engine, QString nick,// QString nick_peer_adress,
+ QHostAddress peer_address, Q_UINT16 peer_port,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize,
+ QObject *parent = 0L, const char *name = 0L );
+/*
+ For a file transfer properties are:
+
+ KExntendedSocket *socket
+ or
+ QHostAddress peerAddress
+ Q_UINT16 peerPort
+ for determining the socket.
+
+ QString fileName
+ Q_UINT32 fileSize
+ for detemining the file propeties.
+*//*
+ Transfer( KIRC *engine, QString nick,// QString nick_peer_adress,
+ Transfer::Type type, QVariant properties,
+ QObject *parent = 0L, const char *name = 0L );
+*/
+ ~Transfer();
+
+ KIRC::Engine *engine() const
+ { return m_engine; }
+ QString nick() const
+ { return m_nick; }
+ Type type() const
+ { return m_type; }
+ Status status() const;
+
+ /* Start the transfer.
+ * If not connected connect to client.
+ * Allow receiving/emitting data.
+ */
+ bool initiate();
+
+ QString fileName() const
+ { return m_fileName; }
+ /* Change the file name.
+ */
+ void setFileName(QString fileName)
+ { m_fileName = fileName; }
+ unsigned long fileSize() const
+ { return m_fileSize; }
+public slots:
+ bool setSocket( KExtendedSocket *socket );
+ void closeSocket();
+
+ void setCodec( QTextCodec *codec );
+ void writeLine( const QString &msg );
+
+ void flush();
+
+ void userAbort(QString);
+
+signals:
+ void readLine( const QString &msg );
+
+ void fileSizeCurrent( unsigned int );
+ void fileSizeAcknowledge( unsigned int );
+
+// void received(Q_UINT32);
+// void sent(Q_UINT32);
+
+ void abort(QString);
+
+ /* Emited when the transfer is complete.
+ * Usually it means that the file transfer has successfully finished.
+ */
+ void complete();
+
+protected slots:
+ void slotError(int);
+
+ void readyReadLine();
+
+ void readyReadFileIncoming();
+
+ void writeFileOutgoing();
+ void readyReadFileOutgoing();
+
+protected:
+// void emitSignals();
+ void checkFileTransferEnd( Q_UINT32 fileSizeAck );
+
+ KIRC::Engine * m_engine;
+ QString m_nick;
+
+ Type m_type;
+ KExtendedSocket *m_socket;
+ bool m_initiated;
+
+ // Text member data
+ QTextStream m_socket_textStream;
+// QTextCodec * m_socket_codec;
+
+ // File member data
+ QFile m_file;
+ QString m_fileName;
+ Q_UINT32 m_fileSize;
+ Q_UINT32 /*usize_t*/ m_fileSizeCur;
+ Q_UINT32 /*usize_t*/ m_fileSizeAck;
+ QDataStream m_socketDataStream;
+ char m_buffer[1024];
+ int m_bufferLength;
+
+ // Data transfer measures
+ Q_UINT32 m_receivedBytes;
+ Q_UINT32 m_receivedBytesLimit;
+
+ Q_UINT32 m_sentBytes;
+ Q_UINT32 m_sentBytesLimit;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/kirctransferhandler.cpp b/kopete/protocols/irc/libkirc/kirctransferhandler.cpp
new file mode 100644
index 00000000..3fa73dff
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransferhandler.cpp
@@ -0,0 +1,97 @@
+/*
+ kirctransferhandler.cpp - DCC Handler
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kextsock.h>
+
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+
+#include "kirctransferserver.h"
+
+#include "kirctransferhandler.h"
+
+using namespace KIRC;
+
+TransferHandler *TransferHandler::self()
+{
+ static TransferHandler sm_self;
+ return &sm_self;
+}
+
+TransferServer *TransferHandler::server()
+{
+ if( m_server )
+// server( m_default_server_port, m_default_server_backlog );
+ server( 0, 1 );
+ return m_server;
+}
+
+TransferServer *TransferHandler::server( Q_UINT16 port, int backlog )
+{
+// if( m_server )
+// m_server->terminate();
+ TransferServer *m_server = new TransferServer( port, backlog, this );
+
+ // here connect the slots of the server
+
+ return m_server;
+}
+
+TransferServer *TransferHandler::createServer(Engine *engine, QString m_userName,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize)
+{
+ TransferServer *server = new TransferServer(engine, m_userName, type, fileName, fileSize, this);
+ transferServerCreated(server);
+ return server;
+}
+
+Transfer *TransferHandler::createClient(
+ Engine *engine, QString nick,// QString nick_peer_adress,
+ QHostAddress peer_address, Q_UINT16 peer_port,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize )
+{
+ Transfer *client = new Transfer(
+ engine, nick,// QString nick_peer_adress,
+ peer_address, peer_port,
+ type,
+ fileName, fileSize,
+ this );
+ transferCreated(client);
+ return client;
+}
+
+/*
+File *DCCHandler::openFile( QString file, int mode = IO_ReadWrite )
+{
+ QFile *file = new QFile(filename);
+ if (!file->open(mode))
+ {
+ delete file;
+ file = 0L;
+ }
+ return file;
+}
+*/
+
+#include "kirctransferhandler.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/irc/libkirc/kirctransferhandler.h b/kopete/protocols/irc/libkirc/kirctransferhandler.h
new file mode 100644
index 00000000..81774c02
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransferhandler.h
@@ -0,0 +1,79 @@
+/*
+ kirctransferhandler.h - DCC Handler
+
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCTRANSFERHANDLER_H
+#define KIRCTRANSFERHANDLER_H
+
+#include <qhostaddress.h>
+
+#include "kirctransfer.h"
+#include "kirctransferserver.h"
+
+class QFile;
+class QTextCodec;
+
+class KExtendedSocket;
+
+namespace KIRC
+{
+
+class TransferHandler
+ : public QObject
+{
+ Q_OBJECT
+
+public:
+ static TransferHandler *self();
+
+ TransferServer *server();
+ TransferServer *server( Q_UINT16 port, int backlog = 1 );
+
+ TransferServer *createServer(KIRC::Engine *engine, QString m_userName,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize);
+
+ Transfer *createClient(
+ KIRC::Engine *engine, QString nick,// QString nick_peer_adress,
+ QHostAddress peer_address, Q_UINT16 peer_port,
+ Transfer::Type type,
+ QString file = QString::null, Q_UINT32 fileSize = 0 );
+
+// void registerServer( DCCServer * );
+// QPtrList<DCCServer> getRegisteredServers();
+// static QPtrList<DCCServer> getAllRegisteredServers();
+// void unregisterServer( DCCServer * );
+
+// void registerClient( DCCClient * );
+// QPtrList<DCCClient> getRegisteredClients();
+// static QPtrList<DCCClient> getAllRegisteredClients();
+// void unregisterClient( DCCClient * );
+
+signals:
+ void transferServerCreated(KIRC::TransferServer *server);
+ void transferCreated(KIRC::Transfer *transfer);
+
+private:
+// TransferHandler();
+
+ TransferServer *m_server;
+// QPtrList<TransferServer> m_servers;
+// QPtrList<Transfer> m_clients;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/kirctransferserver.cpp b/kopete/protocols/irc/libkirc/kirctransferserver.cpp
new file mode 100644
index 00000000..96cc66fb
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransferserver.cpp
@@ -0,0 +1,154 @@
+/*
+ kirctransfer.cpp - IRC transfer.
+
+ Copyright (c) 2003 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <kextsock.h>
+
+#include "kirctransferhandler.h"
+
+#include "kirctransferserver.h"
+
+using namespace KIRC;
+
+/*
+TransferServer::TransferServer( QObject *parent, const char *name )
+ : QObject( parent, name ),
+ m_socket( 0 ),
+ m_port( 0 ),
+ m_backlog( 1 )
+{
+}
+*/
+TransferServer::TransferServer(Q_UINT16 port, int backlog, QObject *parent, const char *name)
+ : QObject( parent, name ),
+ m_socket( 0 ),
+ m_port( port ),
+ m_backlog( backlog )
+{
+}
+
+TransferServer::TransferServer(Engine *engine, QString nick,// QString nick_peer_adress,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize,
+ QObject *parent, const char *name)
+ : QObject( parent, name ),
+ m_socket(0),
+ m_port(0),
+ m_backlog(1),
+ m_engine(engine),
+ m_nick(nick),
+ m_type(type),
+ m_fileName(fileName),
+ m_fileSize(fileSize)
+{
+ initServer();
+}
+
+TransferServer::~TransferServer()
+{
+ if (m_socket)
+ delete m_socket;
+}
+
+bool TransferServer::initServer()
+{
+ if (!m_socket)
+ {
+ QObject::connect(this, SIGNAL(incomingNewTransfer(Transfer *)),
+ TransferHandler::self(), SIGNAL(transferCreated(Transfer *)));
+
+ m_socket = new KExtendedSocket();
+
+// m_socket->setHost(m_socket->localAddress()->nodeName());
+ if (!m_socket->setPort(m_port))
+ kdDebug(14120) << k_funcinfo << "Failed to set port to" << m_port << endl;
+ m_socket->setSocketFlags(KExtendedSocket::noResolve
+ |KExtendedSocket::passiveSocket
+ |KExtendedSocket::inetSocket );
+
+ if (!m_socket->setTimeout(2*60)) // FIXME: allow configuration of this.
+ kdDebug(14120) << k_funcinfo << "Failed to set timeout." << endl;
+
+ QObject::connect(m_socket, SIGNAL(readyAccept()),
+ this, SLOT(readyAccept()));
+ QObject::connect(m_socket, SIGNAL(connectionFailed(int)),
+ this, SLOT(connectionFailed(int)));
+
+ m_socket->listen(m_backlog);
+ m_socket->setBlockingMode(true);
+
+ const KInetSocketAddress *localAddress = static_cast<const KInetSocketAddress *>(m_socket->localAddress());
+ if (!localAddress)
+ {
+ kdDebug(14120) << k_funcinfo << "Not a KInetSocketAddress." << endl;
+ deleteLater();
+ return false;
+ }
+
+ m_port = localAddress->port();
+ }
+ return (m_socket->socketStatus() != KExtendedSocket::error);
+}
+
+bool TransferServer::initServer( Q_UINT16 port, int backlog )
+{
+ if (m_socket)
+ {
+ m_port = port;
+ m_backlog = backlog;
+ }
+ return initServer();
+}
+
+void TransferServer::readyAccept()
+{
+ KExtendedSocket *socket;
+ m_socket->accept( socket );
+ Transfer *transfer = new Transfer(m_engine, m_nick, m_type, m_fileName, m_fileSize);
+ transfer->setSocket(socket);
+ transfer->initiate();
+ emit incomingNewTransfer(transfer);
+}
+
+void TransferServer::connectionFailed(int error)
+{
+ if (error!=0)
+ {
+ kdDebug(14120) << k_funcinfo << "Connection failed with " << m_nick << endl;
+ deleteLater();
+ }
+}
+/*
+void Transfer::initClient()
+{
+ if(!m_socket)
+ {
+ connect(m_socket, SIGNAL(connectionClosed()),
+ this, SLOT(slotConnectionClosed()));
+ connect(m_socket, SIGNAL(delayedCloseFinished()),
+ this, SLOT(slotConnectionClosed()));
+ connect(m_socket, SIGNAL(error(int)),
+ this, SLOT(slotError(int)));
+ connect(m_socket, SIGNAL(readyRead()),
+ this, SLOT(readyReadFileOut));
+
+ m_socket->enableRead( true );
+ m_socket->enableWrite( true );
+ }
+}
+*/
+#include "kirctransferserver.moc"
diff --git a/kopete/protocols/irc/libkirc/kirctransferserver.h b/kopete/protocols/irc/libkirc/kirctransferserver.h
new file mode 100644
index 00000000..8ac016ef
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/kirctransferserver.h
@@ -0,0 +1,81 @@
+/*
+ kirctransfer.h - DCC Handler
+
+ Copyright (c) 2003-2004 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef KIRCTRANSFERSERVER_H
+#define KIRCTRANSFERSERVER_H
+
+#include "kirctransfer.h"
+
+#include <qobject.h>
+
+class KExtendedSocket;
+
+class QFile;
+class QTextCodec;
+
+namespace KIRC
+{
+
+class TransferServer
+ : public QObject
+{
+ Q_OBJECT
+
+public:
+// TransferServer(QObject *parent = 0, const char *name = 0);
+ TransferServer(Q_UINT16 port, int backlog = 1, QObject *parent = 0, const char *name = 0);
+ TransferServer(KIRC::Engine *engine, QString nick,// QString nick_peer_adress,
+ Transfer::Type type,
+ QString fileName, Q_UINT32 fileSize,
+ QObject *parent = 0, const char *name = 0);
+
+ ~TransferServer();
+
+ int port()
+ { return m_port; }
+
+protected:
+ bool initServer();
+ bool initServer( Q_UINT16 port, int backlog = 1 );
+
+signals:
+ void incomingNewTransfer(Transfer *transfer);
+
+protected slots:
+ void readyAccept();
+ void connectionFailed(int error);
+
+private:
+ KExtendedSocket * m_socket;
+ Q_UINT16 m_port;
+ int m_backlog;
+
+ // The following will be deprecated ...
+ KIRC::Engine * m_engine;
+ QString m_nick;
+ Transfer::Type m_type;
+ QString m_fileName;
+ Q_UINT32 m_fileSize;
+ // by
+ // QPtrList<Transfer> m_pendingTransfers;
+ // QPtrList<Transfer> m_activeTransfers;
+
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/irc/libkirc/ksslsocket.cpp b/kopete/protocols/irc/libkirc/ksslsocket.cpp
new file mode 100644
index 00000000..fb2d5161
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/ksslsocket.cpp
@@ -0,0 +1,458 @@
+/*
+ ksslsocket.cpp - KDE SSL Socket
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qsocketnotifier.h>
+
+#include <dcopclient.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kssl.h>
+#include <ksslinfodlg.h>
+#include <ksslpeerinfo.h>
+#include <ksslcertchain.h>
+#include <ksslcertificatecache.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+
+#include "ksslsocket.h"
+
+struct KSSLSocketPrivate
+{
+ mutable KSSL *kssl;
+ KSSLCertificateCache *cc;
+ DCOPClient *dcc;
+ QMap<QString,QString> metaData;
+ QSocketNotifier *socketNotifier;
+};
+
+KSSLSocket::KSSLSocket() : KExtendedSocket()
+{
+ d = new KSSLSocketPrivate;
+ d->kssl = 0;
+ d->dcc = KApplication::kApplication()->dcopClient();
+ d->cc = new KSSLCertificateCache;
+ d->cc->reload();
+
+ //No blocking
+ setBlockingMode(false);
+
+ //Connect internal slots
+ QObject::connect( this, SIGNAL(connectionSuccess()), this, SLOT(slotConnected()) );
+ QObject::connect( this, SIGNAL(closed(int)), this, SLOT(slotDisconnected()) );
+ QObject::connect( this, SIGNAL(connectionFailed(int)), this, SLOT(slotDisconnected()));
+}
+
+KSSLSocket::~KSSLSocket()
+{
+ //Close connection
+ closeNow();
+
+ if( d->kssl )
+ {
+ d->kssl->close();
+ delete d->kssl;
+ }
+
+ delete d->cc;
+
+ delete d;
+}
+
+Q_LONG KSSLSocket::readBlock( char* data, Q_ULONG maxLen )
+{
+ //Re-implemented because KExtSocket doesn't use this when not in buffered mode
+ Q_LONG retval = consumeReadBuffer(maxLen, data);
+
+ if( retval == 0 )
+ {
+ if (sockfd == -1)
+ return 0;
+
+ retval = -1;
+ }
+
+ return retval;
+}
+
+int KSSLSocket::peekBlock( char* data, uint maxLen )
+{
+ //Re-implemented because KExtSocket doesn't use this when not in buffered mode
+ if( socketStatus() < connected )
+ return -2;
+
+ if( sockfd == -1 )
+ return -2;
+
+ return consumeReadBuffer(maxLen, data, false);
+}
+
+Q_LONG KSSLSocket::writeBlock( const char* data, Q_ULONG len )
+{
+ return d->kssl->write( data, len );
+}
+
+int KSSLSocket::bytesAvailable() const
+{
+ if( socketStatus() < connected )
+ return -2;
+
+ //Re-implemented because KExtSocket doesn't use this when not in buffered mode
+ return KBufferedIO::bytesAvailable();
+}
+
+void KSSLSocket::slotReadData()
+{
+ kdDebug(14120) << k_funcinfo << d->kssl->pending() << endl;
+ QByteArray buff(512);
+ int bytesRead = d->kssl->read( buff.data(), 512 );
+
+ //Fill the read buffer
+ feedReadBuffer( bytesRead, buff.data() );
+ emit readyRead();
+}
+
+void KSSLSocket::slotConnected()
+{
+ if (!KSSL::doesSSLWork()) {
+ kdError(14120) << k_funcinfo << "SSL not functional!" << endl;
+
+ closeNow();
+ emit sslFailure();
+ return;
+ }
+
+ delete d->kssl;
+ d->kssl = new KSSL();
+
+ if (d->kssl->connect( sockfd ) != 1) {
+ kdError(14120) << k_funcinfo << "SSL connect() failed." << endl;
+
+ closeNow();
+ emit sslFailure();
+ return;
+ }
+
+ //Disconnect the KExtSocket notifier slot, we use our own
+ QObject::disconnect( readNotifier(), SIGNAL(activated( int )),
+ this, SLOT(socketActivityRead()) );
+
+ QObject::connect( readNotifier(), SIGNAL(activated( int )),
+ this, SLOT(slotReadData()) );
+
+ readNotifier()->setEnabled(true);
+
+ if (verifyCertificate() != 1) {
+ closeNow();
+ emit certificateRejected();
+ return;
+ }
+
+ emit certificateAccepted();
+}
+
+void KSSLSocket::slotDisconnected()
+{
+ kdDebug(14120) << k_funcinfo << "Disconnected" << endl;
+
+ if( readNotifier() )
+ readNotifier()->setEnabled(false);
+
+ delete d->kssl;
+ d->kssl = 0L;
+}
+
+void KSSLSocket::showInfoDialog()
+{
+ if( socketStatus() == connected )
+ {
+ if (!d->dcc->isApplicationRegistered("kio_uiserver"))
+ {
+ KApplication::startServiceByDesktopPath("kio_uiserver.desktop",QStringList());
+ }
+
+ QByteArray data, ignore;
+ QCString ignoretype;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << "irc://" + peerAddress()->pretty() + ":" + port() << d->metaData;
+ d->dcc->call("kio_uiserver", "UIServer",
+ "showSSLInfoDialog(QString,KIO::MetaData)", data, ignoretype, ignore);
+ }
+}
+
+void KSSLSocket::setMetaData( const QString &key, const QVariant &data )
+{
+ QVariant v = data;
+ d->metaData[key] = v.asString();
+}
+
+bool KSSLSocket::hasMetaData( const QString &key )
+{
+ return d->metaData.contains(key);
+}
+
+QString KSSLSocket::metaData( const QString &key )
+{
+ if( d->metaData.contains(key) )
+ return d->metaData[key];
+ return QString::null;
+}
+
+/*
+I basically copied the below from tcpKIO::SlaveBase.hpp, with some modificaions and formatting.
+
+ * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net
+ * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
+ * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
+*/
+
+int KSSLSocket::messageBox( KIO::SlaveBase::MessageBoxType type, const QString &text, const QString &caption,
+ const QString &buttonYes, const QString &buttonNo )
+{
+ kdDebug(14120) << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo << endl;
+ QByteArray data, result;
+ QCString returnType;
+ QDataStream arg(data, IO_WriteOnly);
+ arg << (int)1 << (int)type << text << caption << buttonYes << buttonNo;
+
+ if (!d->dcc->isApplicationRegistered("kio_uiserver"))
+ {
+ KApplication::startServiceByDesktopPath("kio_uiserver.desktop",QStringList());
+ }
+
+ d->dcc->call("kio_uiserver", "UIServer",
+ "messageBox(int,int,QString,QString,QString,QString)", data, returnType, result);
+
+ if( returnType == "int" )
+ {
+ int res;
+ QDataStream r(result, IO_ReadOnly);
+ r >> res;
+ return res;
+ }
+ else
+ return 0; // communication failure
+}
+
+
+// Returns 0 for failed verification, -1 for rejected cert and 1 for ok
+int KSSLSocket::verifyCertificate()
+{
+ int rc = 0;
+ bool permacache = false;
+ bool _IPmatchesCN = false;
+ int result;
+ bool doAddHost = false;
+ QString ourHost = host();
+ QString ourIp = peerAddress()->pretty();
+
+ QString theurl = "irc://" + ourHost + ":" + port();
+
+ if (!d->cc)
+ d->cc = new KSSLCertificateCache;
+
+ KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
+
+ KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
+
+ _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
+
+ if (!_IPmatchesCN)
+ {
+ ksvl << KSSLCertificate::InvalidHost;
+ }
+
+ KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
+ if (!ksvl.isEmpty())
+ ksv = ksvl.first();
+
+ /* Setting the various bits of meta-info that will be needed. */
+ setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
+ setMetaData("ssl_cipher_desc", d->kssl->connectionInfo().getCipherDescription());
+ setMetaData("ssl_cipher_version", d->kssl->connectionInfo().getCipherVersion());
+ setMetaData("ssl_cipher_used_bits", QString::number(d->kssl->connectionInfo().getCipherUsedBits()));
+ setMetaData("ssl_cipher_bits", QString::number(d->kssl->connectionInfo().getCipherBits()));
+ setMetaData("ssl_peer_ip", ourIp );
+
+ QString errorStr;
+ for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
+ it != ksvl.end(); ++it)
+ {
+ errorStr += QString::number(*it)+":";
+ }
+
+ setMetaData("ssl_cert_errors", errorStr);
+ setMetaData("ssl_peer_certificate", pc.toString());
+
+ if (pc.chain().isValid() && pc.chain().depth() > 1)
+ {
+ QString theChain;
+ QPtrList<KSSLCertificate> chain = pc.chain().getChain();
+ for (KSSLCertificate *c = chain.first(); c; c = chain.next())
+ {
+ theChain += c->toString();
+ theChain += "\n";
+ }
+ setMetaData("ssl_peer_chain", theChain);
+ }
+ else
+ {
+ setMetaData("ssl_peer_chain", "");
+ }
+
+ setMetaData("ssl_cert_state", QString::number(ksv));
+
+ if (ksv == KSSLCertificate::Ok)
+ {
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ }
+
+ // Since we're the parent, we need to teach the child.
+ setMetaData("ssl_parent_ip", ourIp );
+ setMetaData("ssl_parent_cert", pc.toString());
+
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp = d->cc->getPolicyByCertificate(pc);
+
+ // - validation code
+ if (ksv != KSSLCertificate::Ok)
+ {
+ if( cp == KSSLCertificateCache::Unknown || cp == KSSLCertificateCache::Ambiguous)
+ {
+ cp = KSSLCertificateCache::Prompt;
+ }
+ else
+ {
+ // A policy was already set so let's honor that.
+ permacache = d->cc->isPermanent(pc);
+ }
+
+ if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept)
+ {
+ cp = KSSLCertificateCache::Prompt;
+ }
+
+ // Precondition: cp is one of Reject, Accept or Prompt
+ switch (cp)
+ {
+ case KSSLCertificateCache::Accept:
+ rc = 1;
+ break;
+
+ case KSSLCertificateCache::Reject:
+ rc = -1;
+ break;
+
+ case KSSLCertificateCache::Prompt:
+ {
+ do
+ {
+ if (ksv == KSSLCertificate::InvalidHost)
+ {
+ QString msg = i18n("The IP address of the host %1 "
+ "does not match the one the "
+ "certificate was issued to.");
+ result = messageBox( KIO::SlaveBase::WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ }
+ else
+ {
+ QString msg = i18n("The server certificate failed the "
+ "authenticity test (%1).");
+ result = messageBox( KIO::SlaveBase::WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ }
+
+ if (result == KMessageBox::Yes)
+ {
+ showInfoDialog();
+ }
+ }
+ while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No)
+ {
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ doAddHost = true;
+ result = messageBox( KIO::SlaveBase::WarningYesNo,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ i18n("&Forever"),
+ i18n("&Current Sessions Only"));
+ if (result == KMessageBox::Yes)
+ permacache = true;
+ else
+ permacache = false;
+ }
+ else
+ {
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ }
+
+ break;
+ }
+ default:
+ kdDebug(14120) << "SSL error in cert code."
+ << "Please report this to kopete-devel@kde.org."
+ << endl;
+ break;
+ }
+ }
+
+ // - cache the results
+ d->cc->addCertificate(pc, cp, permacache);
+ if (doAddHost)
+ d->cc->addHost(pc, ourHost);
+
+
+ if (rc == -1)
+ return rc;
+
+
+ kdDebug(14120) << "SSL connection information follows:" << endl
+ << "+-----------------------------------------------" << endl
+ << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
+ << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
+ << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
+ << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
+ << " of " << d->kssl->connectionInfo().getCipherBits()
+ << " bits used." << endl
+ << "| PEER:" << endl
+ << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
+ << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
+ << "| Validation: " << (int)ksv << endl
+ << "| Certificate matches IP: " << _IPmatchesCN << endl
+ << "+-----------------------------------------------"
+ << endl;
+
+ // sendMetaData(); Do not call this function!!
+ return rc;
+}
+
+
+#include "ksslsocket.moc"
diff --git a/kopete/protocols/irc/libkirc/ksslsocket.h b/kopete/protocols/irc/libkirc/ksslsocket.h
new file mode 100644
index 00000000..692d5288
--- /dev/null
+++ b/kopete/protocols/irc/libkirc/ksslsocket.h
@@ -0,0 +1,68 @@
+
+#ifndef _K_SSL_SOCKET_H_
+#define _K_SSL_SOCKET_H_
+
+/*
+ ksslsocket.h - KDE SSL Socket
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvariant.h>
+#include <kextsock.h>
+#include <kio/slavebase.h>
+
+class KSSLSocketPrivate;
+
+class KSSLSocket : public KExtendedSocket
+{
+ Q_OBJECT
+
+ public:
+ KSSLSocket();
+ ~KSSLSocket();
+
+ Q_LONG readBlock( char* data, Q_ULONG maxLen );
+ Q_LONG writeBlock( const char* data, Q_ULONG len );
+ int peekBlock( char *data, uint maxLen );
+ int bytesAvailable() const;
+
+ void showInfoDialog();
+
+ signals:
+ void sslFailure();
+ void certificateAccepted();
+ void certificateRejected();
+
+ private slots:
+ void slotConnected();
+ void slotDisconnected();
+ void slotReadData();
+
+ private:
+ int verifyCertificate();
+ int messageBox( KIO::SlaveBase::MessageBoxType type, const QString &text,
+ const QString &caption, const QString &buttonYes, const QString &buttonNo );
+
+
+ //Copied frm tcpslavebase to simply integrating their dialog function
+ void setMetaData( const QString &, const QVariant & );
+ bool hasMetaData( const QString & );
+ QString metaData( const QString & );
+
+ KSSLSocketPrivate *d;
+};
+
+#endif
diff --git a/kopete/protocols/irc/ui/Makefile.am b/kopete/protocols/irc/ui/Makefile.am
new file mode 100644
index 00000000..854a7398
--- /dev/null
+++ b/kopete/protocols/irc/ui/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkopeteircui.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/..\
+ -I$(srcdir)/../libkirc \
+ $(all_includes)
+
+
+libkopeteircui_la_SOURCES = ircadd.ui empty.cpp irceditaccountwidget.cpp \
+ irceditaccount.ui channellist.cpp channellistdialog.cpp networkconfig.ui
+EXTRA_DIST = ircadd.ui ircprefs.ui empty.cpp
diff --git a/kopete/protocols/irc/ui/channellist.cpp b/kopete/protocols/irc/ui/channellist.cpp
new file mode 100644
index 00000000..5c66ede0
--- /dev/null
+++ b/kopete/protocols/irc/ui/channellist.cpp
@@ -0,0 +1,346 @@
+/*
+ channellist.cpp - IRC Channel Search Widget
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "channellist.h"
+
+#include "kircengine.h"
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <qvariant.h>
+#include <qlabel.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qsimplerichtext.h>
+#include <qstyle.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+#include <klistview.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qtimer.h>
+#include <qspinbox.h>
+#include <qwhatsthis.h>
+
+class ChannelListItem : public KListViewItem
+{
+ public:
+ ChannelListItem( KListView *parent, QString arg1, QString arg2, QString arg3 );
+ virtual int compare( QListViewItem *i, int col, bool ascending ) const;
+ virtual void paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align );
+
+ private:
+ KListView *parentList;
+};
+
+ChannelListItem::ChannelListItem( KListView *parent, QString arg1, QString arg2, QString arg3 ) :
+ KListViewItem( parent, parent->lastItem() ), parentList( parent )
+{
+ setText(0, arg1);
+ setText(1, arg2);
+ setText(2, arg3);
+}
+
+int ChannelListItem::compare( QListViewItem *i, int col, bool ascending ) const
+{
+ if( col == 1 )
+ {
+ if( text(1).toUInt() < i->text(1).toUInt() )
+ return -1;
+ else if ( text(1).toUInt() == i->text(1).toUInt() )
+ return 0;
+ else
+ return 1;
+ }
+ else
+ return QListViewItem::compare( i, col, ascending );
+}
+
+void ChannelListItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int align )
+{
+ QPixmap back( width, height() );
+ QPainter paint( &back );
+ //KListViewItem::paintCell( &paint, cg, column, width, align );
+ // PASTED FROM KLISTVIEWITEM:
+ // set the alternate cell background colour if necessary
+ QColorGroup _cg = cg;
+ if (isAlternate())
+ if (listView()->viewport()->backgroundMode()==Qt::FixedColor)
+ _cg.setColor(QColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground());
+ else
+ _cg.setColor(QColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground());
+ // PASTED FROM QLISTVIEWITEM
+ {
+ QPainter *p = &paint;
+
+ QListView *lv = listView();
+ if ( !lv )
+ return;
+ QFontMetrics fm( p->fontMetrics() );
+
+ // any text we render is done by the Components, not by this class, so make sure we've nothing to write
+ QString t;
+
+ // removed text truncating code from Qt - we do that differently, further on
+
+ int marg = lv->itemMargin();
+ int r = marg;
+ // const QPixmap * icon = pixmap( column );
+
+ const BackgroundMode bgmode = lv->viewport()->backgroundMode();
+ const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode );
+
+ if ( _cg.brush( crole ) != lv->colorGroup().brush( crole ) )
+ p->fillRect( 0, 0, width, height(), _cg.brush( crole ) );
+ else
+ {
+ // all copied from QListView::paintEmptyArea
+
+ //lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) );
+ QStyleOption opt( lv->sortColumn(), 0 ); // ### hack; in 3.1, add a property in QListView and QHeader
+ QStyle::SFlags how = QStyle::Style_Default;
+ if ( lv->isEnabled() )
+ how |= QStyle::Style_Enabled;
+
+ lv->style().drawComplexControl( QStyle::CC_ListView,
+ p, lv, QRect( 0, 0, width, height() ), lv->colorGroup(),
+ how, QStyle::SC_ListView, QStyle::SC_None,
+ opt );
+ }
+
+
+
+ if ( isSelected() &&
+ (column == 0 || lv->allColumnsShowFocus()) ) {
+ p->fillRect( r - marg, 0, width - r + marg, height(),
+ _cg.brush( QColorGroup::Highlight ) );
+ // removed text pen setting code from Qt
+ }
+
+ // removed icon drawing code from Qt
+
+ // draw the tree gubbins
+ if ( multiLinesEnabled() && column == 0 && isOpen() && childCount() ) {
+ int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin();
+ textheight = QMAX( textheight, QApplication::globalStrut().height() );
+ if ( textheight % 2 > 0 )
+ textheight++;
+ if ( textheight < height() ) {
+ int w = lv->treeStepSize() / 2;
+ lv->style().drawComplexControl( QStyle::CC_ListView, p, lv,
+ QRect( 0, textheight, w + 1, height() - textheight + 1 ), _cg,
+ lv->isEnabled() ? QStyle::Style_Enabled : QStyle::Style_Default,
+ QStyle::SC_ListViewExpand,
+ (uint)QStyle::SC_All, QStyleOption( this ) );
+ }
+ }
+ }
+ // END OF PASTE
+
+
+ //do you see a better way to tell the TextComponent we are selected ? - Olivier 2004-09-02
+ if ( isSelected() )
+ _cg.setColor(QColorGroup::Text , _cg.highlightedText() );
+
+ QSimpleRichText myrichtext( text(column), paint.font() );
+ myrichtext.draw( &paint, 0, 0, paint.window(), _cg );
+
+ paint.end();
+ p->drawPixmap( 0, 0, back );
+}
+
+ChannelList::ChannelList( QWidget* parent, KIRC::Engine *engine )
+ : QWidget( parent ), m_engine( engine )
+{
+ ChannelListLayout = new QVBoxLayout( this, 11, 6, "ChannelListLayout");
+
+ layout72_2 = new QHBoxLayout( 0, 0, 6, "layout72_2");
+
+ textLabel1_2 = new QLabel( this, "textLabel1_2" );
+ layout72_2->addWidget( textLabel1_2 );
+
+ channelSearch = new QLineEdit( this, "channelSearch" );
+ layout72_2->addWidget( channelSearch );
+
+ numUsers = new QSpinBox( 0, 32767, 1, this, "num_users" );
+ numUsers->setSuffix( i18n(" members") );
+ layout72_2->addWidget( numUsers );
+
+ mSearchButton = new QPushButton( this, "mSearchButton" );
+ layout72_2->addWidget( mSearchButton );
+ ChannelListLayout->addLayout( layout72_2 );
+
+ mChannelList = new KListView( this, "mChannelList" );
+ mChannelList->addColumn( i18n( "Channel" ) );
+ mChannelList->addColumn( i18n( "Users" ) );
+ mChannelList->header()->setResizeEnabled( FALSE, mChannelList->header()->count() - 1 );
+ mChannelList->addColumn( i18n( "Topic" ) );
+ mChannelList->setAllColumnsShowFocus( TRUE );
+ mChannelList->setShowSortIndicator( TRUE );
+ ChannelListLayout->addWidget( mChannelList );
+
+ clearWState( WState_Polished );
+
+ textLabel1_2->setText( i18n( "Search for:" ) );
+ QToolTip::add( textLabel1_2, i18n( "You may search for channels on the IRC server for a text string entered here." ) );
+ QToolTip::add( numUsers, i18n( "Channels returned must have at least this many members." ) );
+ QWhatsThis::add( numUsers, i18n( "Channels returned must have at least this many members." ) );
+ QWhatsThis::add( textLabel1_2, i18n( "You may search for channels on the IRC server for a text string entered here. For instance, you may type 'linux' to find channels that have something to do with linux." ) );
+ QToolTip::add( channelSearch, i18n( "You may search for channels on the IRC server for a text string entered here." ) );
+ QWhatsThis::add( channelSearch, i18n( "You may search for channels on the IRC server for a text string entered here. For instance, you may type 'linux' to find channels that have something to do with linux." ) );
+ mSearchButton->setText( i18n( "S&earch" ) );
+ QToolTip::add( mSearchButton, i18n( "Perform a channel search." ) );
+ QWhatsThis::add( mSearchButton, i18n( "Perform a channel search. Please be patient, as this can be slow depending on the number of channels on the server." ) );
+ QToolTip::add( mChannelList, i18n( "Double click on a channel to select it." ) );
+ mChannelList->header()->setLabel( 0, i18n( "Channel" ) );
+ mChannelList->header()->setLabel( 1, i18n( "Users" ) );
+ mChannelList->header()->setLabel( 2, i18n( "Topic" ) );
+
+ // signals and slots connections
+ connect( mChannelList, SIGNAL( doubleClicked(QListViewItem*) ),
+ this, SLOT( slotItemDoubleClicked(QListViewItem*) ) );
+
+ connect( mSearchButton, SIGNAL( clicked() ), this, SLOT( search() ) );
+
+ connect( mChannelList, SIGNAL( selectionChanged( QListViewItem*) ), this,
+ SLOT( slotItemSelected( QListViewItem *) ) );
+
+ connect( m_engine, SIGNAL( incomingListedChan( const QString &, uint, const QString & ) ),
+ this, SLOT( slotChannelListed( const QString &, uint, const QString & ) ) );
+
+ connect( m_engine, SIGNAL( incomingEndOfList() ), this, SLOT( slotListEnd() ) );
+
+ connect( m_engine, SIGNAL( statusChanged(KIRC::Engine::Status) ),
+ this, SLOT( slotStatusChanged(KIRC::Engine::Status) ) );
+
+ show();
+}
+
+void ChannelList::slotItemDoubleClicked( QListViewItem *i )
+{
+ emit channelDoubleClicked( i->text(0) );
+}
+
+void ChannelList::slotItemSelected( QListViewItem *i )
+{
+ emit channelSelected( i->text(0) );
+}
+
+void ChannelList::slotStatusChanged(KIRC::Engine::Status newStatus)
+{
+ switch(newStatus) {
+ case KIRC::Engine::Connected:
+ this->reset();
+ break;
+ case KIRC::Engine::Disconnected:
+ if (mSearching) {
+ KMessageBox::queuedMessageBox(
+ this, KMessageBox::Error,
+ i18n("You have been disconnected from the IRC server."),
+ i18n("Disconnected"), 0
+ );
+ }
+
+ slotListEnd();
+ break;
+ default:
+ break;
+ }
+}
+
+void ChannelList::reset()
+{
+ channelCache.clear();
+ clear();
+}
+
+void ChannelList::clear()
+{
+ mChannelList->clear();
+ channelSearch->clear();
+ channelSearch->setFocus();
+}
+
+void ChannelList::search()
+{
+ if( m_engine->isConnected() || !channelCache.isEmpty() )
+ {
+ mChannelList->clear();
+ mChannelList->setSorting( -1 );
+ mSearchButton->setEnabled(false);
+ mSearch = channelSearch->text();
+ mSearching = true;
+ mUsers = numUsers->value();
+
+ if( channelCache.isEmpty() )
+ m_engine->list();
+ else
+ {
+ cacheIterator = channelCache.begin();
+ slotSearchCache();
+ }
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox(
+ this, KMessageBox::Error,
+ i18n("You must be connected to the IRC server to perform a channel listing."),
+ i18n("Not Connected"), 0
+ );
+ }
+}
+
+void ChannelList::slotChannelListed( const QString &channel, uint users, const QString &topic )
+{
+ checkSearchResult( channel, users, topic );
+ channelCache.insert( channel, QPair< uint, QString >( users, topic ) );
+}
+
+void ChannelList::checkSearchResult( const QString &channel, uint users, const QString &topic )
+{
+ if( ( mUsers == 0 || mUsers <= users ) &&
+ ( mSearch.isEmpty() || channel.contains( mSearch, false ) || topic.contains( mSearch, false ) )
+ )
+ {
+ new ChannelListItem( mChannelList, channel, QString::number(users), topic );
+ }
+}
+
+void ChannelList::slotSearchCache()
+{
+ if( cacheIterator != channelCache.end() )
+ {
+ checkSearchResult( cacheIterator.key(), cacheIterator.data().first, cacheIterator.data().second );
+ ++cacheIterator;
+ QTimer::singleShot( 0, this, SLOT( slotSearchCache() ) );
+ }
+ else
+ {
+ slotListEnd();
+ }
+}
+
+void ChannelList::slotListEnd()
+{
+ mChannelList->setSorting(0, true);
+ mSearchButton->setEnabled(true);
+ mSearching = false;
+}
+
+#include "channellist.moc"
diff --git a/kopete/protocols/irc/ui/channellist.h b/kopete/protocols/irc/ui/channellist.h
new file mode 100644
index 00000000..c6f435a0
--- /dev/null
+++ b/kopete/protocols/irc/ui/channellist.h
@@ -0,0 +1,80 @@
+ /*
+ channellist.h - IRC Channel Search Widget
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHANNELLIST_H
+#define CHANNELLIST_H
+
+#include <qwidget.h>
+#include <qmap.h>
+#include <qpair.h>
+
+#include "kircengine.h"
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QGridLayout;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class KListView;
+class QSpinBox;
+class QListViewItem;
+
+class ChannelList
+ : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ChannelList( QWidget *parent, KIRC::Engine *engine );
+
+ public slots:
+ void search();
+ void reset();
+ void clear();
+
+ signals:
+ void channelDoubleClicked( const QString &channel );
+ void channelSelected( const QString &channel );
+
+ private slots:
+ void slotItemDoubleClicked( QListViewItem * i );
+ void slotItemSelected( QListViewItem * i );
+ void slotChannelListed( const QString & channel, uint users, const QString & topic );
+ void slotListEnd();
+ void slotSearchCache();
+ void slotStatusChanged( KIRC::Engine::Status );
+
+ private:
+ void checkSearchResult( const QString & channel, uint users, const QString & topic );
+
+ QLabel* textLabel1_2;
+ QLineEdit* channelSearch;
+ QSpinBox* numUsers;
+ QPushButton* mSearchButton;
+ KListView* mChannelList;
+ QVBoxLayout* ChannelListLayout;
+ QHBoxLayout* layout72_2;
+ KIRC::Engine *m_engine;
+ bool mSearching;
+ QString mSearch;
+ uint mUsers;
+ QMap< QString, QPair< uint, QString > > channelCache;
+ QMap< QString, QPair< uint, QString > >::const_iterator cacheIterator;
+};
+
+#endif
diff --git a/kopete/protocols/irc/ui/channellistdialog.cpp b/kopete/protocols/irc/ui/channellistdialog.cpp
new file mode 100644
index 00000000..46128730
--- /dev/null
+++ b/kopete/protocols/irc/ui/channellistdialog.cpp
@@ -0,0 +1,61 @@
+/*
+ channellistdialog.cpp - IRC Channel Search Dialog
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+ Copyright (c) 2005 by Michel Hermier <michel.hermier@wanadoo.fr>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "channellistdialog.h"
+
+#include "kircengine.h"
+
+#include "kopeteuiglobal.h"
+
+#include "qlayout.h"
+
+ChannelListDialog::ChannelListDialog(KIRC::Engine *engine, const QString &caption, QObject *target, const char* slotJoinChan)
+ : KDialogBase(Kopete::UI::Global::mainWidget(), "channel_list_widget", false, caption, Close)
+{
+ m_engine = engine;
+ m_list = new ChannelList( this, engine );
+
+ connect( m_list, SIGNAL( channelDoubleClicked( const QString & ) ),
+ target, slotJoinChan );
+
+ connect( m_list, SIGNAL( channelDoubleClicked( const QString & ) ),
+ this, SLOT( slotChannelDoubleClicked( const QString & ) ) );
+
+ new QHBoxLayout( m_list, 0, spacingHint() );
+
+ setInitialSize( QSize( 500, 400 ) );
+ setMainWidget( m_list );
+ show();
+}
+
+void ChannelListDialog::clear()
+{
+ m_list->clear();
+}
+
+void ChannelListDialog::search()
+{
+ m_list->search();
+}
+
+void ChannelListDialog::slotChannelDoubleClicked( const QString & )
+{
+ close();
+}
+
+#include "channellistdialog.moc"
diff --git a/kopete/protocols/irc/ui/channellistdialog.h b/kopete/protocols/irc/ui/channellistdialog.h
new file mode 100644
index 00000000..2bb85f5b
--- /dev/null
+++ b/kopete/protocols/irc/ui/channellistdialog.h
@@ -0,0 +1,45 @@
+ /*
+ channellist.h - IRC Channel Search Widget
+
+ Copyright (c) 2004 by Jason Keirstead <jason@keirstead.org>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHANNELLISTDIALOG_H
+#define CHANNELLISTDIALOG_H
+
+#include "channellist.h"
+
+#include "kdialogbase.h"
+
+class ChannelListDialog
+ : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ChannelListDialog(KIRC::Engine *engine, const QString &caption, QObject *target, const char* slotJoinChan);
+
+ void clear();
+
+ void search();
+
+ private slots:
+ void slotChannelDoubleClicked( const QString & );
+
+ private:
+ KIRC::Engine *m_engine;
+ ChannelList *m_list;
+};
+
+#endif
diff --git a/kopete/protocols/irc/ui/empty.cpp b/kopete/protocols/irc/ui/empty.cpp
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/kopete/protocols/irc/ui/empty.cpp
@@ -0,0 +1 @@
+
diff --git a/kopete/protocols/irc/ui/ircadd.ui b/kopete/protocols/irc/ui/ircadd.ui
new file mode 100644
index 00000000..f1025112
--- /dev/null
+++ b/kopete/protocols/irc/ui/ircadd.ui
@@ -0,0 +1,163 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ircAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ircAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>389</width>
+ <height>350</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget3</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Add Contact</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout70</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>N&amp;ickname/channel to add:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The name of the IRC contact or channel you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of the IRC contact or channel you would like to add. You may type simply the text of a person's nickname, or you may type a channel name, preceded by a pound sign ('#').</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The name of the IRC contact or channel you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The name of the IRC contact or channel you would like to add. You may type simply the text of a person's nickname, or you may type a channel name, preceded by a pound sign ('#')</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: joe_bob or #somechannel)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer25</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>110</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Search Channels</string>
+ </attribute>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QHBox">
+ <property name="name">
+ <cstring>hbox</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>QHBox</class>
+ <header>qhbox.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="4462">789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>addID</tabstop>
+ <tabstop>tabWidget3</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/irc/ui/irceditaccount.ui b/kopete/protocols/irc/ui/irceditaccount.ui
new file mode 100644
index 00000000..682e9be9
--- /dev/null
+++ b/kopete/protocols/irc/ui/irceditaccount.ui
@@ -0,0 +1,1022 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>IRCEditAccountBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>IRCEditAccountBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>689</width>
+ <height>528</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>440</width>
+ <height>575</height>
+ </size>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>B&amp;asic Setup</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <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>150</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; Most IRC servers do not require a password, and only a nickname is required to connect&lt;/p&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox59</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Account Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>N&amp;ickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mNickName</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the name that everyone will see everytime you say something</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Alternate ni&amp;ckname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mAltNickname</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When the nickname is already in use when connecting, this name will be used instead</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>mNickName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>This is the name that everyone will see everytime you say something</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The alias you would like to use on IRC. You may change this once online with the /nick command.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>mAltNickname</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>When the nickname is already in use when connecting, this name will be used instead</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>When the nickname is already in use when connecting, this name will be used instead</string>
+ </property>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mPasswordWidget</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>m_realNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Real name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_realNameLineEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mUserName</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>mUserName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="echoMode">
+ <enum>Normal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>m_realNameLineEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="echoMode">
+ <enum>Normal</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The username you would prefer to use on IRC, if your system does not have identd support.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The username you would prefer to use on IRC, if your system does not have identd support. Leave blank to use your system account name.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Connection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>description</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>161</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout20</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>network</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>editButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Edit...</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>392</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Network:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>network</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>preferSSL</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Prefer SSL-based connections</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>autoConnect</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout25</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Default &amp;charset:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>charset</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>charset</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>141</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Default Messages</string>
+ </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;Part message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>partMessage</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Quit message:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>quitMessage</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>partMessage</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The message you want people to see when you part a channel without giving a reason. Leave this field blank to use the Kopete default message.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The message you want people to see when you part a channel without giving a reason. Leave this field blank to use the Kopete default message.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>quitMessage</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The message you want people to see when you disconnect from IRC without giving a reason. Leave this field blank to use the Kopete default message.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The message you want people to see when you disconnect from IRC without giving a reason. Leave this field blank to use the Kopete default message.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer72</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>150</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>A&amp;dvanced Configuration</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox7</cstring>
+ </property>
+ <property name="title">
+ <string>Message Destinations</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>autoShowAnonWindows</cstring>
+ </property>
+ <property name="text">
+ <string>Auto-show anonymous windows</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>autoShowServerWindow</cstring>
+ </property>
+ <property name="text">
+ <string>Auto-show the server window</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Server messages:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel4_3</cstring>
+ </property>
+ <property name="text">
+ <string>Server notices:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>Active Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Server Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Anonymous Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>KNotify</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ignore</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>serverNotices</cstring>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>Active Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Server Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Anonymous Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>KNotify</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ignore</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>serverMessages</cstring>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>Error messages:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>Active Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Server Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Anonymous Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>KNotify</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ignore</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>informationReplies</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Information replies:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="1">
+ <item>
+ <property name="text">
+ <string>Active Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Server Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Anonymous Window</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>KNotify</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ignore</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>errorMessages</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>130</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Custom CTCP Replies</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>CTCP</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Reply</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>ctcpList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>2</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>false</bool>
+ </property>
+ <property name="defaultRenameAction">
+ <enum>Accept</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>You can use this dialog to add custom replies for when people send CTCP requests to you. You can also use this dialog to override the built-in replies for VERSION, USERINFO, and CLIENTINFO.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout153</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;CTCP:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>newCTCP</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>newCTCP</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Reply:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>newReply</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>newReply</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>addReply</cstring>
+ </property>
+ <property name="text">
+ <string>Add Repl&amp;y</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox60</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>130</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Run Following Commands on Connect</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout29</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>commandEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>addButton</cstring>
+ </property>
+ <property name="text">
+ <string>Add Co&amp;mmand</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KListView" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Command</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>commandList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>2</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="defaultRenameAction">
+ <enum>Accept</enum>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsRenameable">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Any commands added here will be run as soon as you are connected to the IRC server.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Any commands added here will be run as soon as you are connected to the IRC server.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ </widget>
+ </hbox>
+</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="826">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000030149444154388db59531681b5718c77f0e377c070e3c810a3a70e0041eac51852e0a19e45134830a1d9a4c69a04bc8928e990a693a640e1d0c8642b08742321894c1507991b484c890902bb8701a047760c3bd21701fe4201dde49b6a41a32b8df72dcbbeffdbefffbbfefbd5b1b0c07cce266ebe667ae2006c3c1dada0cdc3be87d6e6c35b0d692a409d9c7ec8b20d65ae29398d19b1114e7e3de4ce98b3f5e10dc0053cf0951b4506496e1b964bf7ce6c585d9054c62d01d617ca48be0596553cf496d8f2c8b01c5f795fc93904e85ec4c01a152857a5d9175d0b2805c872080f18595ccc1499a10a225d4e2fbc2877786fe81253ab6c04c8d106e09db5d43ab0d146e5c64d1a23938fb98a185cea1c33eecfd9eba49eb427dcb201e245365f2b7b2fb5b4a3a31dcb927178afe07d86901df870fefa4842aed6f6b74ba42e52b4014d580e1eb9cbd9d94de7e4aad16d2f9be02d805f0b5e532f927a1ffcacea1777f122a8105b164a7c25faf323a5d9f1f1fd600e1e5bec59e2d4b5c7ef5209d0ad17b8b31864e57c0b3e0815ac3ee33253ab664a770ff5185d1a1cb8d2267d3e58aa1dc7d2508cbe597d0e74fdd269aaaf0f52d414c4ea3e9762c996869e42560d7a72e41c4799a2586e74f95e8d8151481fa86efbe7b3398ac58b1a2b8527589f15451ad303ac2293542ad6648a796278f13a27185e4c4754310facb98c53a79e19a3fdc1426ff28c3d7399d1f7cb25343eb96106cf83c790ce9c4f2eb831855c55485663327992eb6dc8a6259874ed700b0b793323cccb9ffa842b30d6133e3e75fea989ac15a8b16ca76b746b0b92278d919774c5b6d48a78697fb29bbcf52468742a32120909c24e899ce67beed5be2db01e22d1e9485bb620e47f9ee9e606a21bd3f5d3744c7e7c54d55e87443867d8b554515ac5db4620e8e4f62263170fd1cdee90aad7640141992891b0f367c9adfe4049bb07d3b7022bd8c687c0978f46684ee084150b65ac1fcca94591b7a90a496e4c095164fb016a2b192a497795cc0f84817aebe25f7bf70ccc54a575c555c03f78ffa5fc0570d1f0c076bff0232285a09643cc7ce0000000049454e44ae426082</data>
+ </image>
+</images>
+<tabstops>
+ <tabstop>tabWidget2</tabstop>
+ <tabstop>mNickName</tabstop>
+ <tabstop>mAltNickname</tabstop>
+ <tabstop>mUserName</tabstop>
+ <tabstop>m_realNameLineEdit</tabstop>
+ <tabstop>network</tabstop>
+ <tabstop>editButton</tabstop>
+ <tabstop>preferSSL</tabstop>
+ <tabstop>autoConnect</tabstop>
+ <tabstop>charset</tabstop>
+ <tabstop>partMessage</tabstop>
+ <tabstop>quitMessage</tabstop>
+ <tabstop>serverMessages</tabstop>
+ <tabstop>serverNotices</tabstop>
+ <tabstop>informationReplies</tabstop>
+ <tabstop>errorMessages</tabstop>
+ <tabstop>autoShowServerWindow</tabstop>
+ <tabstop>autoShowAnonWindows</tabstop>
+ <tabstop>ctcpList</tabstop>
+ <tabstop>newCTCP</tabstop>
+ <tabstop>newReply</tabstop>
+ <tabstop>addReply</tabstop>
+ <tabstop>commandList</tabstop>
+ <tabstop>commandEdit</tabstop>
+ <tabstop>addButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/irc/ui/irceditaccountwidget.cpp b/kopete/protocols/irc/ui/irceditaccountwidget.cpp
new file mode 100644
index 00000000..4a1e6ed3
--- /dev/null
+++ b/kopete/protocols/irc/ui/irceditaccountwidget.cpp
@@ -0,0 +1,282 @@
+/*
+ irceditaccountwidget.cpp - IRC Account Widget
+
+ Copyright (c) 2005 by Tommi Rantala <tommi.rantala@cs.helsinki.fi>
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
+ Kopete (c) 2003-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "irceditaccountwidget.h"
+
+#include "ircaccount.h"
+#include "ircusercontact.h"
+#include "ircprotocol.h"
+#include "kcodecaction.h"
+
+#include "kircengine.h"
+
+#include "kopetepasswordwidget.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <kdebug.h>
+#include <kextsock.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kcharsets.h>
+
+#include <qlabel.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <qconnection.h>
+#include <qvalidator.h>
+#include <qcombobox.h>
+#include <qlistbox.h>
+#include <qlineedit.h>
+
+IRCEditAccountWidget::IRCEditAccountWidget(IRCProtocol *proto, IRCAccount *ident, QWidget *parent, const char * )
+ : IRCEditAccountBase(parent), KopeteEditAccountWidget(ident)
+{
+ mProtocol = proto;
+
+ // default charset/encoding for new accounts: utf-8, see http://www.iana.org/assignments/character-sets
+ int currentCodec = 106;
+
+ if( account() )
+ {
+ QString nickName = account()->mySelf()->nickName();
+ QString serverInfo = account()->accountId();
+
+ mNickName->setText( nickName );
+ mAltNickname->setText( account()->altNick() );
+ mUserName->setText( account()->userName() );
+ m_realNameLineEdit->setText( account()->realName() );
+
+ partMessage->setText( account()->defaultPart() );
+ quitMessage->setText( account()->defaultQuit() );
+ if( account()->codec() )
+ currentCodec = account()->codec()->mibEnum();
+
+ mPasswordWidget->load ( &account()->password() );
+
+ preferSSL->setChecked(account()->configGroup()->readBoolEntry("PreferSSL"));
+ autoShowServerWindow->setChecked( account()->configGroup()->readBoolEntry("AutoShowServerWindow") );
+ autoConnect->setChecked( static_cast<Kopete::Account*>(account())->excludeConnect() );
+
+ KConfigGroup *config = account()->configGroup();
+
+ serverNotices->setCurrentItem( config->readNumEntry( "ServerNotices", IRCAccount::ServerWindow ) - 1 );
+ serverMessages->setCurrentItem( config->readNumEntry( "ServerMessages", IRCAccount::ServerWindow ) - 1 );
+ informationReplies->setCurrentItem( config->readNumEntry( "InformationReplies", IRCAccount::ActiveWindow ) - 1 );
+ errorMessages->setCurrentItem( config->readNumEntry( "ErrorMessages", IRCAccount::ActiveWindow ) - 1 );
+
+ QStringList cmds = account()->connectCommands();
+ for( QStringList::Iterator i = cmds.begin(); i != cmds.end(); ++i )
+ new QListViewItem( commandList, *i );
+
+ const QMap< QString, QString > replies = account()->customCtcpReplies();
+ for( QMap< QString, QString >::ConstIterator it = replies.begin(); it != replies.end(); ++it )
+ new QListViewItem( ctcpList, it.key(), it.data() );
+ }
+
+ mUserName->setValidator( new QRegExpValidator( QString::fromLatin1("^[^\\s]*$"), mUserName ) );
+ mNickName->setValidator( new QRegExpValidator( QString::fromLatin1("^[^#+&][^\\s]*$"), mNickName ) );
+ mAltNickname->setValidator( new QRegExpValidator( QString::fromLatin1("^[^#+&][^\\s]*$"), mAltNickname ) );
+
+ charset->insertStringList( KCodecAction::supportedEncodings() );
+
+ for (int i = 0; i < charset->count(); ++i) {
+ QString encoding = KGlobal::charsets()->encodingForName(charset->text(i));
+
+ if (KGlobal::charsets()->codecForName(encoding)->mibEnum() == currentCodec) {
+ charset->setCurrentItem( i );
+ break;
+ }
+ }
+
+ connect( commandList, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ),
+ this, SLOT( slotCommandContextMenu( KListView *, QListViewItem *, const QPoint & ) ) );
+
+ connect( ctcpList, SIGNAL( contextMenu( KListView *, QListViewItem *, const QPoint & ) ),
+ this, SLOT( slotCtcpContextMenu( KListView *, QListViewItem *, const QPoint & ) ) );
+
+ connect( addButton, SIGNAL( clicked() ), this, SLOT( slotAddCommand() ) );
+ connect( editButton, SIGNAL( clicked() ), this, SLOT(slotEditNetworks() ) );
+ connect( addReply, SIGNAL( clicked() ), this, SLOT( slotAddCtcp() ) );
+
+ connect( network, SIGNAL( activated( const QString & ) ),
+ this, SLOT( slotUpdateNetworkDescription( const QString &) ) );
+
+ connect( IRCProtocol::protocol(), SIGNAL( networkConfigUpdated( const QString & ) ),
+ this, SLOT( slotUpdateNetworks( const QString & ) ) );
+
+ slotUpdateNetworks( QString::null );
+}
+
+IRCEditAccountWidget::~IRCEditAccountWidget()
+{
+}
+
+IRCAccount *IRCEditAccountWidget::account ()
+{
+ return dynamic_cast<IRCAccount *>(KopeteEditAccountWidget::account () );
+}
+
+void IRCEditAccountWidget::slotUpdateNetworks( const QString & selectedNetwork )
+{
+ network->clear();
+
+ uint i = 0;
+ QStringList keys;
+ for( QDictIterator<IRCNetwork> it( IRCProtocol::protocol()->networks() ); it.current(); ++it )
+ keys.append( it.currentKey() );
+
+ keys.sort();
+
+ QStringList::Iterator end = keys.end();
+ for( QStringList::Iterator it = keys.begin(); it != end; ++it )
+ {
+ IRCNetwork * current = IRCProtocol::protocol()->networks()[*it];
+ network->insertItem( current->name );
+ if ( ( account() && account()->networkName() == current->name ) || current->name == selectedNetwork )
+ {
+ network->setCurrentItem( i );
+ description->setText( current->description );
+ }
+ ++i;
+ }
+}
+
+void IRCEditAccountWidget::slotEditNetworks()
+{
+ IRCProtocol::protocol()->editNetworks( network->currentText() );
+}
+
+void IRCEditAccountWidget::slotUpdateNetworkDescription( const QString &network )
+{
+ description->setText(
+ IRCProtocol::protocol()->networks()[ network ]->description
+ );
+}
+
+void IRCEditAccountWidget::slotCommandContextMenu( KListView *, QListViewItem *item, const QPoint &p )
+{
+ QPopupMenu popup;
+ popup.insertItem( i18n("Remove Command"), 1 );
+ if( popup.exec( p ) == 1 )
+ delete item;
+}
+
+void IRCEditAccountWidget::slotCtcpContextMenu( KListView *, QListViewItem *item, const QPoint &p )
+{
+ QPopupMenu popup;
+ popup.insertItem( i18n("Remove CTCP Reply"), 1 );
+ if( popup.exec( p ) == 1 )
+ delete item;
+}
+
+void IRCEditAccountWidget::slotAddCommand()
+{
+ if ( !commandEdit->text().isEmpty() )
+ {
+ new QListViewItem( commandList, commandEdit->text() );
+ commandEdit->clear();
+ }
+}
+
+void IRCEditAccountWidget::slotAddCtcp()
+{
+ if ( !newCTCP->text().isEmpty() && !newReply->text().isEmpty() )
+ {
+ new QListViewItem( ctcpList, newCTCP->text(), newReply->text() );
+ newCTCP->clear();
+ newReply->clear();
+ }
+}
+
+QString IRCEditAccountWidget::generateAccountId( const QString &network )
+{
+ KConfig *config = KGlobal::config();
+ QString nextId = network;
+
+ uint accountNumber = 1;
+ while( config->hasGroup( QString("Account_%1_%2").arg( m_protocol->pluginId() ).arg( nextId ) ) )
+ {
+ nextId = QString::fromLatin1("%1_%2").arg( network ).arg( ++accountNumber );
+ }
+ kdDebug( 14120 ) << k_funcinfo << " ID IS: " << nextId << endl;
+ return nextId;
+}
+
+Kopete::Account *IRCEditAccountWidget::apply()
+{
+ QString nickName = mNickName->text();
+ QString networkName = network->currentText();
+
+ if( !account() )
+ {
+ setAccount( new IRCAccount( mProtocol, generateAccountId(networkName), QString::null, networkName, nickName ) );
+
+ }
+ else
+ {
+ account()->setNickName( nickName );
+ account()->setNetwork( networkName );
+ }
+
+ mPasswordWidget->save( &account()->password() );
+
+ account()->setAltNick( mAltNickname->text() );
+ account()->setUserName( mUserName->text() );
+ account()->setRealName( m_realNameLineEdit->text() );
+ account()->setDefaultPart( partMessage->text() );
+ account()->setDefaultQuit( quitMessage->text() );
+ account()->setAutoShowServerWindow( autoShowServerWindow->isChecked() );
+ account()->setExcludeConnect( autoConnect->isChecked() );
+ account()->setMessageDestinations( serverNotices->currentItem() + 1, serverMessages->currentItem() + 1,
+ informationReplies->currentItem() + 1, errorMessages->currentItem() + 1
+ );
+
+ account()->configGroup()->writeEntry("PreferSSL", preferSSL->isChecked());
+
+ QStringList cmds;
+ for( QListViewItem *i = commandList->firstChild(); i; i = i->nextSibling() )
+ cmds.append( i->text(0) );
+
+ QMap< QString, QString > replies;
+ for( QListViewItem *i = ctcpList->firstChild(); i; i = i->nextSibling() )
+ replies[ i->text(0) ] = i->text(1);
+
+ account()->setCustomCtcpReplies( replies );
+ account()->setConnectCommands( cmds );
+
+ KCharsets *c = KGlobal::charsets();
+ account()->setCodec( c->codecForName( c->encodingForName( charset->currentText() ) ) );
+
+ return account();
+}
+
+
+bool IRCEditAccountWidget::validateData()
+{
+ if( mNickName->text().isEmpty() )
+ KMessageBox::sorry(this, i18n("<qt>You must enter a nickname.</qt>"), i18n("Kopete"));
+ else
+ return true;
+
+ return false;
+}
+
+#include "irceditaccountwidget.moc"
diff --git a/kopete/protocols/irc/ui/irceditaccountwidget.h b/kopete/protocols/irc/ui/irceditaccountwidget.h
new file mode 100644
index 00000000..365acaf3
--- /dev/null
+++ b/kopete/protocols/irc/ui/irceditaccountwidget.h
@@ -0,0 +1,60 @@
+/*
+ irceditaccountwidget.h - IRC Account Widget
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+
+#ifndef IRCEDITACCOUNTWIDEGET_H
+#define IRCEDITACCOUNTWIDEGET_H
+
+#include "editaccountwidget.h"
+#include "irceditaccount.h"
+
+class IRCProtocol;
+class IRCAccount;
+class KListView;
+class QListViewItem;
+
+class IRCEditAccountWidget : public IRCEditAccountBase, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+
+ public:
+ IRCEditAccountWidget(IRCProtocol *proto, IRCAccount *, QWidget *parent=0, const char *name=0);
+ ~IRCEditAccountWidget();
+
+ IRCAccount *account();
+ virtual bool validateData();
+ virtual Kopete::Account *apply();
+
+ private slots:
+ void slotCommandContextMenu( KListView*, QListViewItem*, const QPoint & );
+ void slotCtcpContextMenu( KListView*, QListViewItem*, const QPoint & );
+ void slotAddCommand();
+ void slotAddCtcp();
+ void slotEditNetworks();
+ void slotUpdateNetworks( const QString & );
+ void slotUpdateNetworkDescription( const QString & );
+
+ private:
+ void readNetworks();
+ QString generateAccountId( const QString &network );
+
+ IRCProtocol *mProtocol;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/irc/ui/networkconfig.ui b/kopete/protocols/irc/ui/networkconfig.ui
new file mode 100644
index 00000000..d1000e37
--- /dev/null
+++ b/kopete/protocols/irc/ui/networkconfig.ui
@@ -0,0 +1,382 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>NetworkConfig</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>NetworkConfig</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>670</width>
+ <height>468</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Network Configuration</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="4" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>description</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Description:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>description</cstring>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="2" column="3" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="margin">
+ <number>4</number>
+ </property>
+ <property name="title">
+ <string>Host Con&amp;figuration</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QListBox" row="0" column="0" rowspan="3" colspan="4">
+ <property name="name">
+ <cstring>hostList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IRC servers associated with this network</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IRC servers associated with this network. Use the up and down buttons to alter the order in which connections are attempted.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>password</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Most IRC servers do not require a password</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Por&amp;t:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>port</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>port</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65536</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>6667</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>password</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Host:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>host</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>host</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>useSSL</cstring>
+ </property>
+ <property name="text">
+ <string>Use SS&amp;L</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this to enable SSL for this connection</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="6" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>removeHost</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="6" column="2">
+ <property name="name">
+ <cstring>newHost</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;New...</string>
+ </property>
+ </widget>
+ <spacer row="6" column="0" rowspan="1" colspan="2">
+ <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>210</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="2" column="4">
+ <property name="name">
+ <cstring>downButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Move this server down</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Move this server down in connection attempt priority</string>
+ </property>
+ </widget>
+ <spacer row="0" column="4">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>151</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="1" column="4">
+ <property name="name">
+ <cstring>upButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Move this server up</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Move this server up in connection attempt priority</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QPushButton" row="3" column="6">
+ <property name="name">
+ <cstring>cancelButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="5">
+ <property name="name">
+ <cstring>saveButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Save</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="0">
+ <property name="name">
+ <cstring>newNetwork</cstring>
+ </property>
+ <property name="text">
+ <string>Ne&amp;w</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="0" column="0" rowspan="3" colspan="3">
+ <property name="name">
+ <cstring>networkList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer row="3" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>260</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>renameNetwork</cstring>
+ </property>
+ <property name="text">
+ <string>Rena&amp;me...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="2">
+ <property name="name">
+ <cstring>removeNetwork</cstring>
+ </property>
+ <property name="text">
+ <string>Remo&amp;ve</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>NetworkConfig</receiver>
+ <slot>reject()</slot>
+ </connection>
+ <connection>
+ <sender>saveButton</sender>
+ <signal>clicked()</signal>
+ <receiver>NetworkConfig</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>networkList</tabstop>
+ <tabstop>newNetwork</tabstop>
+ <tabstop>renameNetwork</tabstop>
+ <tabstop>removeNetwork</tabstop>
+ <tabstop>description</tabstop>
+ <tabstop>hostList</tabstop>
+ <tabstop>upButton</tabstop>
+ <tabstop>downButton</tabstop>
+ <tabstop>host</tabstop>
+ <tabstop>port</tabstop>
+ <tabstop>password</tabstop>
+ <tabstop>useSSL</tabstop>
+ <tabstop>newHost</tabstop>
+ <tabstop>removeHost</tabstop>
+ <tabstop>saveButton</tabstop>
+ <tabstop>cancelButton</tabstop>
+</tabstops>
+<signals>
+ <signal>accepted()</signal>
+ <signal>rejected()</signal>
+</signals>
+<slots>
+ <slot access="protected">accept()</slot>
+ <slot access="protected">reject()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/irc/ui/networkconfig.ui.h b/kopete/protocols/irc/ui/networkconfig.ui.h
new file mode 100644
index 00000000..7716e75f
--- /dev/null
+++ b/kopete/protocols/irc/ui/networkconfig.ui.h
@@ -0,0 +1,26 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+
+
+
+
+void NetworkConfig::accept()
+{
+ emit accepted();
+ QDialog::accept();
+}
+
+
+void NetworkConfig::reject()
+{
+ emit rejected();
+ QDialog::reject();
+}
diff --git a/kopete/protocols/jabber/Makefile.am b/kopete/protocols/jabber/Makefile.am
new file mode 100644
index 00000000..ce462f74
--- /dev/null
+++ b/kopete/protocols/jabber/Makefile.am
@@ -0,0 +1,67 @@
+if include_jingle
+JINGLE=jingle
+JINGLE_LIBS=jingle/libkopetejabberjingle.la
+JINGLE_INCLUDES=-I$(srcdir)/jingle -I$(top_builddir)/kopete/protocols/jabber/jingle
+endif
+
+METASOURCES = AUTO
+SUBDIRS = ui icons libiris $(JINGLE) . kioslave
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/libiris/iris/include \
+ -I$(srcdir)/libiris/iris/xmpp-im \
+ -I$(srcdir)/libiris/iris/jabber \
+ -I$(srcdir)/libiris/qca/src \
+ -I$(srcdir)/libiris/cutestuff/util \
+ -I$(srcdir)/libiris/cutestuff/network \
+ -I$(srcdir)/ui \
+ -I./ui \
+ $(all_includes) $(JINGLE_INCLUDES)
+
+noinst_LTLIBRARIES = libjabberclient.la
+libjabberclient_la_SOURCES = \
+ jabberclient.cpp \
+ jabberconnector.cpp \
+ jabberbytestream.cpp
+
+kde_module_LTLIBRARIES = kopete_jabber.la
+
+kopete_jabber_la_SOURCES = \
+ jabberprotocol.cpp \
+ jabberaccount.cpp \
+ jabberresource.cpp \
+ jabberresourcepool.cpp \
+ jabberbasecontact.cpp \
+ jabbercontact.cpp \
+ jabbergroupcontact.cpp \
+ jabbergroupmembercontact.cpp \
+ jabbercontactpool.cpp \
+ jabberformtranslator.cpp \
+ jabberformlineedit.cpp \
+ jabberchatsession.cpp \
+ jabbergroupchatmanager.cpp \
+ jabberfiletransfer.cpp \
+ jabbercapabilitiesmanager.cpp\
+ jabbertransport.cpp\
+ jabberbookmarks.cpp
+
+kopete_jabber_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_jabber_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la \
+ ui/libkopetejabberui.la \
+ libiris/iris/include/libiris.la \
+ libiris/iris/jabber/libiris_jabber.la \
+ libiris/iris/xmpp-core/libiris_xmpp_core.la \
+ libiris/iris/xmpp-im/libiris_xmpp_im.la \
+ libiris/qca/src/libqca.la \
+ libiris/cutestuff/network/libcutestuff_network.la \
+ libiris/cutestuff/util/libcutestuff_util.la \
+ libjabberclient.la \
+ $(JINGLE_LIBS)
+
+service_DATA = kopete_jabber.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_jabber
+mydata_DATA = jabberchatui.rc
+
+noinst_HEADERS = jabberresourcepool.h jabbercontact.h jabbergroupcontact.h \
+ jabberclient.h
diff --git a/kopete/protocols/jabber/TODO b/kopete/protocols/jabber/TODO
new file mode 100644
index 00000000..64da5133
--- /dev/null
+++ b/kopete/protocols/jabber/TODO
@@ -0,0 +1,27 @@
+TODO for Jabber:
+
+- implement support for transports/agents
+- support all message types (chat/ticker/etc)
+- add a button for server defaults
+- port dialogs to KDialogBase
+- support different icons for contacts from servers with broken connections etc.
+- show (i.e. with a QToolTip) the subscription status: both, to, from
+- add "querying..." feedback while waiting for vCard
+- clean up class names in the ui directory, no real scheme there right now
+- if a contact subscribed to you, it is being added as a real contact,
+ should either be added as temporary or not at all
+- provide better feedback for dialogs querying the server
+- support avatars and idle times for tooltips
+- when trying to register an account, try to display the actual server error
+ message
+- clean up JabberAddContactPage (needs rewrite)
+- support advanced auth methods
+- subclass TLS to make use of KDE classes
+- allow SSL fallback with setOptProbe
+- support account deletion
+- factor out client backend to single class JabberClient
+- make vCard dialog better, maybe use KIMProxy somehow
+- allow fetching vCard from "auth user?" dialog
+- allow adding file transfer reasons
+- support resuming
+- support addAddressBookField
diff --git a/kopete/protocols/jabber/icons/Makefile.am b/kopete/protocols/jabber/icons/Makefile.am
new file mode 100644
index 00000000..56681824
--- /dev/null
+++ b/kopete/protocols/jabber/icons/Makefile.am
@@ -0,0 +1 @@
+KDE_ICON=AUTO \ No newline at end of file
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_away.png b/kopete/protocols/jabber/icons/cr16-action-jabber_away.png
new file mode 100644
index 00000000..a6727e71
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_away.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_chatty.png b/kopete/protocols/jabber/icons/cr16-action-jabber_chatty.png
new file mode 100644
index 00000000..baca2e22
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_chatty.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_connecting.mng b/kopete/protocols/jabber/icons/cr16-action-jabber_connecting.mng
new file mode 100644
index 00000000..3098ca1f
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_connecting.mng
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_group.png b/kopete/protocols/jabber/icons/cr16-action-jabber_group.png
new file mode 100644
index 00000000..0240ab6e
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_group.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_invisible.png b/kopete/protocols/jabber/icons/cr16-action-jabber_invisible.png
new file mode 100644
index 00000000..279f1397
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_invisible.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_na.png b/kopete/protocols/jabber/icons/cr16-action-jabber_na.png
new file mode 100644
index 00000000..b1aa91af
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_na.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_offline.png b/kopete/protocols/jabber/icons/cr16-action-jabber_offline.png
new file mode 100644
index 00000000..5e473faa
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_offline.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_online.png b/kopete/protocols/jabber/icons/cr16-action-jabber_online.png
new file mode 100644
index 00000000..48dd715e
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_online.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_original.png b/kopete/protocols/jabber/icons/cr16-action-jabber_original.png
new file mode 100644
index 00000000..3a8e5042
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_original.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_raw.png b/kopete/protocols/jabber/icons/cr16-action-jabber_raw.png
new file mode 100644
index 00000000..7b170c19
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_raw.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_serv_off.png b/kopete/protocols/jabber/icons/cr16-action-jabber_serv_off.png
new file mode 100644
index 00000000..8d5005e7
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_serv_off.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_serv_on.png b/kopete/protocols/jabber/icons/cr16-action-jabber_serv_on.png
new file mode 100644
index 00000000..378afc5c
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_serv_on.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-action-jabber_xa.png b/kopete/protocols/jabber/icons/cr16-action-jabber_xa.png
new file mode 100644
index 00000000..347a4753
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-action-jabber_xa.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_aim.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_aim.png
new file mode 100644
index 00000000..32aea50a
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_aim.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_gadu.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_gadu.png
new file mode 100644
index 00000000..85c7ee81
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_gadu.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_http-ws.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_http-ws.png
new file mode 100644
index 00000000..71da6df4
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_http-ws.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_icq.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_icq.png
new file mode 100644
index 00000000..77df20aa
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_icq.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_irc.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_irc.png
new file mode 100644
index 00000000..c4283af4
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_irc.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_msn.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_msn.png
new file mode 100644
index 00000000..2a349148
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_msn.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_qq.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_qq.png
new file mode 100644
index 00000000..0dd883dd
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_qq.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_sms.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_sms.png
new file mode 100644
index 00000000..eeb212a3
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_sms.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_smtp.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_smtp.png
new file mode 100644
index 00000000..bebd2e69
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_smtp.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_tlen.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_tlen.png
new file mode 100644
index 00000000..d4421eed
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_tlen.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_yahoo.png b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_yahoo.png
new file mode 100644
index 00000000..6f1bb81d
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_gateway_yahoo.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr16-app-jabber_protocol.png b/kopete/protocols/jabber/icons/cr16-app-jabber_protocol.png
new file mode 100644
index 00000000..48dd715e
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr16-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr32-app-jabber_protocol.png b/kopete/protocols/jabber/icons/cr32-app-jabber_protocol.png
new file mode 100644
index 00000000..9d091b13
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr32-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/cr48-app-jabber_protocol.png b/kopete/protocols/jabber/icons/cr48-app-jabber_protocol.png
new file mode 100644
index 00000000..0964749c
--- /dev/null
+++ b/kopete/protocols/jabber/icons/cr48-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_away.png b/kopete/protocols/jabber/icons/hi16-action-jabber_away.png
new file mode 100644
index 00000000..b3959b1a
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_away.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_chatty.png b/kopete/protocols/jabber/icons/hi16-action-jabber_chatty.png
new file mode 100644
index 00000000..8fd5a24b
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_chatty.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_connecting.mng b/kopete/protocols/jabber/icons/hi16-action-jabber_connecting.mng
new file mode 100644
index 00000000..5fabdd4c
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_connecting.mng
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_group.png b/kopete/protocols/jabber/icons/hi16-action-jabber_group.png
new file mode 100644
index 00000000..fe2062a9
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_group.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_invisible.png b/kopete/protocols/jabber/icons/hi16-action-jabber_invisible.png
new file mode 100644
index 00000000..e1a30342
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_invisible.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_na.png b/kopete/protocols/jabber/icons/hi16-action-jabber_na.png
new file mode 100644
index 00000000..d4950ec0
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_na.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_offline.png b/kopete/protocols/jabber/icons/hi16-action-jabber_offline.png
new file mode 100644
index 00000000..199b75ed
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_offline.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_online.png b/kopete/protocols/jabber/icons/hi16-action-jabber_online.png
new file mode 100644
index 00000000..f3566eab
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_online.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_original.png b/kopete/protocols/jabber/icons/hi16-action-jabber_original.png
new file mode 100644
index 00000000..4613cb67
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_original.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_raw.png b/kopete/protocols/jabber/icons/hi16-action-jabber_raw.png
new file mode 100644
index 00000000..7b170c19
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_raw.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_serv_off.png b/kopete/protocols/jabber/icons/hi16-action-jabber_serv_off.png
new file mode 100644
index 00000000..822b70fb
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_serv_off.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_serv_on.png b/kopete/protocols/jabber/icons/hi16-action-jabber_serv_on.png
new file mode 100644
index 00000000..b123c82e
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_serv_on.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-action-jabber_xa.png b/kopete/protocols/jabber/icons/hi16-action-jabber_xa.png
new file mode 100644
index 00000000..e6a17e7c
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-action-jabber_xa.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi16-app-jabber_protocol.png b/kopete/protocols/jabber/icons/hi16-app-jabber_protocol.png
new file mode 100644
index 00000000..f3566eab
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi16-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi32-app-jabber_protocol.png b/kopete/protocols/jabber/icons/hi32-app-jabber_protocol.png
new file mode 100644
index 00000000..2cb3ef57
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi32-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/icons/hi48-app-jabber_protocol.png b/kopete/protocols/jabber/icons/hi48-app-jabber_protocol.png
new file mode 100644
index 00000000..7d4cf34b
--- /dev/null
+++ b/kopete/protocols/jabber/icons/hi48-app-jabber_protocol.png
Binary files differ
diff --git a/kopete/protocols/jabber/jabberaccount.cpp b/kopete/protocols/jabber/jabberaccount.cpp
new file mode 100644
index 00000000..785e9c53
--- /dev/null
+++ b/kopete/protocols/jabber/jabberaccount.cpp
@@ -0,0 +1,1752 @@
+
+/***************************************************************************
+ jabberaccount.cpp - core Jabber account class
+ -------------------
+ begin : Sat M??? 8 2003
+ copyright : (C) 2003 by Till Gerken <till@tantalo.net>
+ Based on JabberProtocol by Daniel Stone <dstone@kde.org>
+ and Till Gerken <till@tantalo.net>.
+ copyright : (C) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (C) 2001-2003 Kopete developers
+ <kopete-devel@kde.org>.
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "im.h"
+#include "filetransfer.h"
+#include "xmpp.h"
+#include "xmpp_tasks.h"
+#include "qca.h"
+#include "bsocket.h"
+
+#include "jabberaccount.h"
+#include "jabberbookmarks.h"
+
+#include <time.h>
+
+#include <qstring.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <ksocketbase.h>
+#include <kpassdlg.h>
+#include <kinputdialog.h>
+
+#include "kopetepassword.h"
+#include "kopeteawayaction.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+#include "kopetegroup.h"
+#include "kopetecontactlist.h"
+#include "kopeteaccountmanager.h"
+#include "contactaddednotifydialog.h"
+
+#include "jabberconnector.h"
+#include "jabberclient.h"
+#include "jabberprotocol.h"
+#include "jabberresourcepool.h"
+#include "jabbercontactpool.h"
+#include "jabberfiletransfer.h"
+#include "jabbercontact.h"
+#include "jabbergroupcontact.h"
+#include "jabbercapabilitiesmanager.h"
+#include "jabbertransport.h"
+#include "dlgjabbersendraw.h"
+#include "dlgjabberservices.h"
+#include "dlgjabberchatjoin.h"
+
+#include <sys/utsname.h>
+
+#ifdef SUPPORT_JINGLE
+#include "voicecaller.h"
+#include "jinglevoicecaller.h"
+
+// NOTE: Disabled for 0.12, will develop them futher in KDE4
+// #include "jinglesessionmanager.h"
+// #include "jinglesession.h"
+// #include "jinglevoicesession.h"
+#include "jinglevoicesessiondialog.h"
+#endif
+
+#define KOPETE_CAPS_NODE "http://kopete.kde.org/jabber/caps"
+
+
+
+JabberAccount::JabberAccount (JabberProtocol * parent, const QString & accountId, const char *name)
+ :Kopete::PasswordedAccount ( parent, accountId, 0, name )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Instantiating new account " << accountId << endl;
+
+ m_protocol = parent;
+
+ m_jabberClient = 0L;
+
+ m_resourcePool = 0L;
+ m_contactPool = 0L;
+#ifdef SUPPORT_JINGLE
+ m_voiceCaller = 0L;
+ //m_jingleSessionManager = 0L; // NOTE: Disabled for 0.12
+#endif
+ m_bookmarks = new JabberBookmarks(this);
+ m_removing=false;
+ m_notifiedUserCannotBindTransferPort = false;
+ // add our own contact to the pool
+ JabberContact *myContact = contactPool()->addContact ( XMPP::RosterItem ( accountId ), Kopete::ContactList::self()->myself(), false );
+ setMyself( myContact );
+
+ QObject::connect(Kopete::ContactList::self(), SIGNAL( globalIdentityChanged(const QString&, const QVariant& ) ), SLOT( slotGlobalIdentityChanged(const QString&, const QVariant& ) ) );
+
+ m_initialPresence = XMPP::Status ( "", "", 5, true );
+
+}
+
+JabberAccount::~JabberAccount ()
+{
+ disconnect ( Kopete::Account::Manual );
+
+ // Remove this account from Capabilities manager.
+ protocol()->capabilitiesManager()->removeAccount( this );
+
+ cleanup ();
+
+ QMap<QString,JabberTransport*> tranposrts_copy=m_transports;
+ QMap<QString,JabberTransport*>::Iterator it;
+ for ( it = tranposrts_copy.begin(); it != tranposrts_copy.end(); ++it )
+ delete it.data();
+}
+
+void JabberAccount::cleanup ()
+{
+
+ delete m_jabberClient;
+
+ m_jabberClient = 0L;
+
+ delete m_resourcePool;
+ m_resourcePool = 0L;
+
+ delete m_contactPool;
+ m_contactPool = 0L;
+
+#ifdef SUPPORT_JINGLE
+ delete m_voiceCaller;
+ m_voiceCaller = 0L;
+
+// delete m_jingleSessionManager;
+// m_jingleSessionManager = 0L;
+#endif
+}
+
+void JabberAccount::setS5BServerPort ( int port )
+{
+
+ if ( !m_jabberClient )
+ {
+ return;
+ }
+
+ if ( !m_jabberClient->setS5BServerPort ( port ) && !m_notifiedUserCannotBindTransferPort)
+ {
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (), KMessageBox::Sorry,
+ i18n ( "Could not bind Jabber file transfer manager to local port. Please check if the file transfer port is already in use or choose another port in the account settings." ),
+ i18n ( "Failed to start Jabber File Transfer Manager" ) );
+ m_notifiedUserCannotBindTransferPort = true;
+ }
+
+}
+
+KActionMenu *JabberAccount::actionMenu ()
+{
+ KActionMenu *m_actionMenu = Kopete::Account::actionMenu();
+
+ m_actionMenu->popupMenu()->insertSeparator();
+
+ KAction *action;
+
+ action = new KAction (i18n ("Join Groupchat..."), "jabber_group", 0, this, SLOT (slotJoinNewChat ()), this, "actionJoinChat");
+ m_actionMenu->insert(action);
+ action->setEnabled( isConnected() );
+
+ action = m_bookmarks->bookmarksAction( m_bookmarks );
+ m_actionMenu->insert(action);
+ action->setEnabled( isConnected() );
+
+
+ m_actionMenu->popupMenu()->insertSeparator();
+
+ action = new KAction ( i18n ("Services..."), "jabber_serv_on", 0,
+ this, SLOT ( slotGetServices () ), this, "actionJabberServices");
+ action->setEnabled( isConnected() );
+ m_actionMenu->insert ( action );
+
+ action = new KAction ( i18n ("Send Raw Packet to Server..."), "mail_new", 0,
+ this, SLOT ( slotSendRaw () ), this, "actionJabberSendRaw") ;
+ action->setEnabled( isConnected() );
+ m_actionMenu->insert ( action );
+
+ action = new KAction ( i18n ("Edit User Info..."), "identity", 0,
+ this, SLOT ( slotEditVCard () ), this, "actionEditVCard") ;
+ action->setEnabled( isConnected() );
+ m_actionMenu->insert ( action );
+
+
+ return m_actionMenu;
+
+}
+
+JabberResourcePool *JabberAccount::resourcePool ()
+{
+
+ if ( !m_resourcePool )
+ m_resourcePool = new JabberResourcePool ( this );
+
+ return m_resourcePool;
+
+}
+
+JabberContactPool *JabberAccount::contactPool ()
+{
+
+ if ( !m_contactPool )
+ m_contactPool = new JabberContactPool ( this );
+
+ return m_contactPool;
+
+}
+
+bool JabberAccount::createContact (const QString & contactId, Kopete::MetaContact * metaContact)
+{
+
+ // collect all group names
+ QStringList groupNames;
+ Kopete::GroupList groupList = metaContact->groups();
+ for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
+ groupNames += group->displayName();
+
+ XMPP::Jid jid ( contactId );
+ XMPP::RosterItem item ( jid );
+ item.setName ( metaContact->displayName () );
+ item.setGroups ( groupNames );
+
+ // this contact will be created with the "dirty" flag set
+ // (it will get reset if the contact appears in the roster during connect)
+ JabberContact *contact = contactPool()->addContact ( item, metaContact, true );
+
+ return ( contact != 0 );
+
+}
+
+void JabberAccount::errorConnectFirst ()
+{
+
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Error,
+ i18n ("Please connect first."), i18n ("Jabber Error") );
+
+}
+
+void JabberAccount::errorConnectionLost ()
+{
+ disconnected( Kopete::Account::ConnectionReset );
+}
+
+bool JabberAccount::isConnecting ()
+{
+
+ XMPP::Jid jid ( myself()->contactId () );
+
+ // see if we are currently trying to connect
+ return resourcePool()->bestResource ( jid ).status().show () == QString("connecting");
+
+}
+
+void JabberAccount::connectWithPassword ( const QString &password )
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "called" << endl;
+
+ /* Cancel connection process if no password has been supplied. */
+ if ( password.isEmpty () )
+ {
+ disconnect ( Kopete::Account::Manual );
+ return;
+ }
+
+ /* Don't do anything if we are already connected. */
+ if ( isConnected () )
+ return;
+
+ // instantiate new client backend or clean up old one
+ if ( !m_jabberClient )
+ {
+ m_jabberClient = new JabberClient;
+
+ QObject::connect ( m_jabberClient, SIGNAL ( csDisconnected () ), this, SLOT ( slotCSDisconnected () ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( csError ( int ) ), this, SLOT ( slotCSError ( int ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( tlsWarning ( int ) ), this, SLOT ( slotHandleTLSWarning ( int ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( error ( JabberClient::ErrorCode ) ), this, SLOT ( slotClientError ( JabberClient::ErrorCode ) ) );
+
+ QObject::connect ( m_jabberClient, SIGNAL ( subscription ( const XMPP::Jid &, const QString & ) ),
+ this, SLOT ( slotSubscription ( const XMPP::Jid &, const QString & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( rosterRequestFinished ( bool ) ),
+ this, SLOT ( slotRosterRequestFinished ( bool ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( newContact ( const XMPP::RosterItem & ) ),
+ this, SLOT ( slotContactUpdated ( const XMPP::RosterItem & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( contactUpdated ( const XMPP::RosterItem & ) ),
+ this, SLOT ( slotContactUpdated ( const XMPP::RosterItem & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( contactDeleted ( const XMPP::RosterItem & ) ),
+ this, SLOT ( slotContactDeleted ( const XMPP::RosterItem & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( resourceAvailable ( const XMPP::Jid &, const XMPP::Resource & ) ),
+ this, SLOT ( slotResourceAvailable ( const XMPP::Jid &, const XMPP::Resource & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( resourceUnavailable ( const XMPP::Jid &, const XMPP::Resource & ) ),
+ this, SLOT ( slotResourceUnavailable ( const XMPP::Jid &, const XMPP::Resource & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( messageReceived ( const XMPP::Message & ) ),
+ this, SLOT ( slotReceivedMessage ( const XMPP::Message & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( incomingFileTransfer () ),
+ this, SLOT ( slotIncomingFileTransfer () ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( groupChatJoined ( const XMPP::Jid & ) ),
+ this, SLOT ( slotGroupChatJoined ( const XMPP::Jid & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( groupChatLeft ( const XMPP::Jid & ) ),
+ this, SLOT ( slotGroupChatLeft ( const XMPP::Jid & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( groupChatPresence ( const XMPP::Jid &, const XMPP::Status & ) ),
+ this, SLOT ( slotGroupChatPresence ( const XMPP::Jid &, const XMPP::Status & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( groupChatError ( const XMPP::Jid &, int, const QString & ) ),
+ this, SLOT ( slotGroupChatError ( const XMPP::Jid &, int, const QString & ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( debugMessage ( const QString & ) ),
+ this, SLOT ( slotClientDebugMessage ( const QString & ) ) );
+ }
+ else
+ {
+ m_jabberClient->disconnect ();
+ }
+
+ // we need to use the old protocol for now
+ m_jabberClient->setUseXMPP09 ( true );
+
+ // set SSL flag (this should be converted to forceTLS when using the new protocol)
+ m_jabberClient->setUseSSL ( configGroup()->readBoolEntry ( "UseSSL", false ) );
+
+ // override server and port (this should be dropped when using the new protocol and no direct SSL)
+ m_jabberClient->setOverrideHost ( true, server (), port () );
+
+ // allow plaintext password authentication or not?
+ m_jabberClient->setAllowPlainTextPassword ( configGroup()->readBoolEntry ( "AllowPlainTextPassword", false ) );
+
+ // enable file transfer (if empty, IP will be set after connection has been established)
+ KGlobal::config()->setGroup ( "Jabber" );
+ m_jabberClient->setFileTransfersEnabled ( true, KGlobal::config()->readEntry ( "LocalIP" ) );
+ setS5BServerPort ( KGlobal::config()->readNumEntry ( "LocalPort", 8010 ) );
+
+ //
+ // Determine system name
+ //
+ if ( !configGroup()->readBoolEntry ( "HideSystemInfo", false ) )
+ {
+ struct utsname utsBuf;
+
+ uname (&utsBuf);
+
+ m_jabberClient->setClientName ("Kopete");
+ m_jabberClient->setClientVersion (kapp->aboutData ()->version ());
+ m_jabberClient->setOSName (QString ("%1 %2").arg (utsBuf.sysname, 1).arg (utsBuf.release, 2));
+ }
+
+ // Set caps node information
+ m_jabberClient->setCapsNode(KOPETE_CAPS_NODE);
+ m_jabberClient->setCapsVersion(kapp->aboutData()->version());
+
+ // Set Disco Identity information
+ DiscoItem::Identity identity;
+ identity.category = "client";
+ identity.type = "pc";
+ identity.name = "Kopete";
+ m_jabberClient->setDiscoIdentity(identity);
+
+ //BEGIN TIMEZONE INFORMATION
+ //
+ // Set timezone information (code from Psi)
+ // Copyright (C) 2001-2003 Justin Karneges
+ //
+ time_t x;
+ time(&x);
+ char str[256];
+ char fmt[32];
+ int timezoneOffset;
+ QString timezoneString;
+
+ strcpy ( fmt, "%z" );
+ strftime ( str, 256, fmt, localtime ( &x ) );
+
+ if ( strcmp ( fmt, str ) )
+ {
+ QString s = str;
+ if ( s.at ( 0 ) == '+' )
+ s.remove ( 0, 1 );
+ s.truncate ( s.length () - 2 );
+ timezoneOffset = s.toInt();
+ }
+
+ strcpy ( fmt, "%Z" );
+ strftime ( str, 256, fmt, localtime ( &x ) );
+
+ if ( strcmp ( fmt, str ) )
+ timezoneString = str;
+ //END of timezone code
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Determined timezone " << timezoneString << " with UTC offset " << timezoneOffset << " hours." << endl;
+
+ m_jabberClient->setTimeZone ( timezoneString, timezoneOffset );
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Connecting to Jabber server " << server() << ":" << port() << endl;
+
+ setPresence( XMPP::Status ("connecting", "", 0, true) );
+
+ switch ( m_jabberClient->connect ( XMPP::Jid ( accountId () + QString("/") + resource () ), password ) )
+ {
+ case JabberClient::NoTLS:
+ // no SSL support, at the connecting stage this means the problem is client-side
+ 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 ("Jabber SSL Error"));
+ break;
+
+ case JabberClient::Ok:
+ default:
+ // everything alright!
+
+ break;
+ }
+
+}
+
+void JabberAccount::slotClientDebugMessage ( const QString &msg )
+{
+
+ kdDebug (JABBER_DEBUG_PROTOCOL) << k_funcinfo << msg << endl;
+
+}
+
+bool JabberAccount::handleTLSWarning ( JabberClient *jabberClient, int warning )
+{
+ QString validityString, code;
+
+ QString server = jabberClient->jid().domain ();
+ QString accountId = jabberClient->jid().bare ();
+
+ 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("<qt><p>The certificate of server %1 could not be validated for account %2: %3</p><p>Do you want to continue?</p></qt>").
+ arg(server, accountId, validityString),
+ i18n("Jabber Connection Certificate Problem"),
+ KStdGuiItem::cont(),
+ QString("KopeteTLSWarning") + server + code) == KMessageBox::Continue );
+
+}
+
+void JabberAccount::slotHandleTLSWarning ( int validityResult )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Handling TLS warning..." << endl;
+
+ if ( handleTLSWarning ( m_jabberClient, validityResult ) )
+ {
+ // resume stream
+ m_jabberClient->continueAfterTLSWarning ();
+ }
+ else
+ {
+ // disconnect stream
+ disconnect ( Kopete::Account::Manual );
+ }
+
+}
+
+void JabberAccount::slotClientError ( JabberClient::ErrorCode errorCode )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Handling client error..." << endl;
+
+ switch ( errorCode )
+ {
+ case JabberClient::NoTLS:
+ default:
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (), KMessageBox::Error,
+ i18n ("An encrypted connection with the Jabber server could not be established."),
+ i18n ("Jabber Connection Error"));
+ disconnect ( Kopete::Account::Manual );
+ break;
+ }
+
+}
+
+void JabberAccount::slotConnected ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Connected to Jabber server." << endl;
+
+#ifdef SUPPORT_JINGLE
+ if(!m_voiceCaller)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Creating Jingle Voice caller..." << endl;
+ m_voiceCaller = new JingleVoiceCaller( this );
+ QObject::connect(m_voiceCaller,SIGNAL(incoming(const Jid&)),this,SLOT(slotIncomingVoiceCall( const Jid& )));
+ m_voiceCaller->initialize();
+ }
+
+
+
+#if 0
+ if(!m_jingleSessionManager)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Creating Jingle Session Manager..." << endl;
+ m_jingleSessionManager = new JingleSessionManager( this );
+ QObject::connect(m_jingleSessionManager, SIGNAL(incomingSession(const QString &, JingleSession *)), this, SLOT(slotIncomingJingleSession(const QString &, JingleSession *)));
+ }
+#endif
+
+ // Set caps extensions
+ m_jabberClient->client()->addExtension("voice-v1", Features(QString("http://www.google.com/xmpp/protocol/voice/v1")));
+#endif
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Requesting roster..." << endl;
+ m_jabberClient->requestRoster ();
+}
+
+void JabberAccount::slotRosterRequestFinished ( bool success )
+{
+
+ if ( success )
+ {
+ // the roster was imported successfully, clear
+ // all "dirty" items from the contact list
+ contactPool()->cleanUp ();
+ }
+
+ /* Since we are online now, set initial presence. Don't do this
+ * before the roster request or we will receive presence
+ * information before we have updated our roster with actual
+ * contacts from the server! (Iris won't forward presence
+ * information in that case either). */
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Setting initial presence..." << endl;
+ setPresence ( m_initialPresence );
+
+}
+
+void JabberAccount::slotIncomingFileTransfer ()
+{
+
+ // delegate the work to a file transfer object
+ new JabberFileTransfer ( this, client()->fileTransferManager()->takeIncoming () );
+
+}
+
+void JabberAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason)
+{
+ XMPP::Status xmppStatus = m_protocol->kosToStatus( status, reason);
+
+ if( status.status() == Kopete::OnlineStatus::Offline )
+ {
+ xmppStatus.setIsAvailable( false );
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "CROSS YOUR FINGERS! THIS IS GONNA BE WILD" << endl;
+ disconnect (Manual, xmppStatus);
+ return;
+ }
+
+ if( isConnecting () )
+ {
+ return;
+ }
+
+
+ if ( !isConnected () )
+ {
+ // we are not connected yet, so connect now
+ m_initialPresence = xmppStatus;
+ connect ( status );
+ }
+ else
+ {
+ setPresence ( xmppStatus );
+ }
+}
+
+void JabberAccount::disconnect ( Kopete::Account::DisconnectReason reason )
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "disconnect() called" << endl;
+
+ if (isConnected ())
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Still connected, closing connection..." << endl;
+ /* Tell backend class to disconnect. */
+ m_jabberClient->disconnect ();
+ }
+
+ // make sure that the connection animation gets stopped if we're still
+ // in the process of connecting
+ setPresence ( XMPP::Status ("", "", 0, false) );
+ m_initialPresence = XMPP::Status ("", "", 5, true);
+
+ /* FIXME:
+ * We should delete the JabberClient instance here,
+ * but active timers in Iris prevent us from doing so.
+ * (in a failed connection attempt, these timers will
+ * try to access an already deleted object).
+ * Instead, the instance will lurk until the next
+ * connection attempt.
+ */
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Disconnected." << endl;
+
+ disconnected ( reason );
+}
+
+void JabberAccount::disconnect( Kopete::Account::DisconnectReason reason, XMPP::Status &status )
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "disconnect( reason, status ) called" << endl;
+
+ if (isConnected ())
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Still connected, closing connection..." << endl;
+ /* Tell backend class to disconnect. */
+ m_jabberClient->disconnect (status);
+ }
+
+ // make sure that the connection animation gets stopped if we're still
+ // in the process of connecting
+ setPresence ( status );
+
+ /* FIXME:
+ * We should delete the JabberClient instance here,
+ * but active timers in Iris prevent us from doing so.
+ * (in a failed connection attempt, these timers will
+ * try to access an already deleted object).
+ * Instead, the instance will lurk until the next
+ * connection attempt.
+ */
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Disconnected." << endl;
+
+ Kopete::Account::disconnected ( reason );
+}
+
+void JabberAccount::disconnect ()
+{
+ disconnect ( Manual );
+}
+
+void JabberAccount::slotConnect ()
+{
+ connect ();
+}
+
+void JabberAccount::slotDisconnect ()
+{
+ disconnect ( Kopete::Account::Manual );
+}
+
+void JabberAccount::slotCSDisconnected ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Disconnected from Jabber server." << endl;
+
+ /*
+ * We should delete the JabberClient instance here,
+ * but timers etc prevent us from doing so. Iris does
+ * not like to be deleted from a slot.
+ */
+
+ /* It seems that we don't get offline notifications when going offline
+ * with the protocol, so clear all resources manually. */
+ resourcePool()->clear();
+
+}
+
+void JabberAccount::handleStreamError (int streamError, int streamCondition, int connectorCode, const QString &server, Kopete::Account::DisconnectReason &errorClass)
+{
+ QString errorText;
+ QString errorCondition;
+
+ errorClass = Kopete::Account::InvalidHost;
+
+ /*
+ * Display error to user.
+ * FIXME: for unknown errors, maybe add error codes?
+ */
+ switch(streamError)
+ {
+ case XMPP::Stream::ErrParse:
+ errorClass = Kopete::Account::Unknown;
+ errorText = i18n("Malformed packet received.");
+ break;
+
+ case XMPP::Stream::ErrProtocol:
+ errorClass = Kopete::Account::Unknown;
+ errorText = i18n("There was an unrecoverable error in the protocol.");
+ break;
+
+ case XMPP::Stream::ErrStream:
+ switch(streamCondition)
+ {
+ case XMPP::Stream::GenericStreamError:
+ errorCondition = i18n("Generic stream error (sorry, I do not have a more-detailed reason)");
+ break;
+ case XMPP::Stream::Conflict:
+ // FIXME: need a better error message here
+ errorCondition = i18n("There was a conflict in the information received.");
+ break;
+ case XMPP::Stream::ConnectionTimeout:
+ errorCondition = i18n("The stream timed out.");
+ break;
+ case XMPP::Stream::InternalServerError:
+ errorCondition = i18n("Internal server error.");
+ break;
+ case XMPP::Stream::InvalidFrom:
+ errorCondition = i18n("Stream packet received from an invalid address.");
+ break;
+ case XMPP::Stream::InvalidXml:
+ errorCondition = i18n("Malformed stream packet received.");
+ break;
+ case XMPP::Stream::PolicyViolation:
+ // FIXME: need a better error message here
+ errorCondition = i18n("Policy violation in the protocol stream.");
+ break;
+ case XMPP::Stream::ResourceConstraint:
+ // FIXME: need a better error message here
+ errorCondition = i18n("Resource constraint.");
+ break;
+ case XMPP::Stream::SystemShutdown:
+ // FIXME: need a better error message here
+ errorCondition = i18n("System shutdown.");
+ break;
+ default:
+ errorCondition = i18n("Unknown reason.");
+ break;
+ }
+
+ errorText = i18n("There was an error in the protocol stream: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrConnection:
+ switch(connectorCode)
+ {
+ case KNetwork::KSocketBase::LookupFailure:
+ errorClass = Kopete::Account::InvalidHost;
+ errorCondition = i18n("Host not found.");
+ break;
+ case KNetwork::KSocketBase::AddressInUse:
+ errorCondition = i18n("Address is already in use.");
+ break;
+ case KNetwork::KSocketBase::AlreadyCreated:
+ errorCondition = i18n("Cannot recreate the socket.");
+ break;
+ case KNetwork::KSocketBase::AlreadyBound:
+ errorCondition = i18n("Cannot bind the socket again.");
+ break;
+ case KNetwork::KSocketBase::AlreadyConnected:
+ errorCondition = i18n("Socket is already connected.");
+ break;
+ case KNetwork::KSocketBase::NotConnected:
+ errorCondition = i18n("Socket is not connected.");
+ break;
+ case KNetwork::KSocketBase::NotBound:
+ errorCondition = i18n("Socket is not bound.");
+ break;
+ case KNetwork::KSocketBase::NotCreated:
+ errorCondition = i18n("Socket has not been created.");
+ break;
+ case KNetwork::KSocketBase::WouldBlock:
+ errorCondition = i18n("Socket operation would block. You should not see this error, please use \"Report Bug\" from the Help menu.");
+ break;
+ case KNetwork::KSocketBase::ConnectionRefused:
+ errorCondition = i18n("Connection refused.");
+ break;
+ case KNetwork::KSocketBase::ConnectionTimedOut:
+ errorCondition = i18n("Connection timed out.");
+ break;
+ case KNetwork::KSocketBase::InProgress:
+ errorCondition = i18n("Connection attempt already in progress.");
+ break;
+ case KNetwork::KSocketBase::NetFailure:
+ errorCondition = i18n("Network failure.");
+ break;
+ case KNetwork::KSocketBase::NotSupported:
+ errorCondition = i18n("Operation is not supported.");
+ break;
+ case KNetwork::KSocketBase::Timeout:
+ errorCondition = i18n("Socket timed out.");
+ break;
+ default:
+ errorClass = Kopete::Account::ConnectionReset;
+ //errorCondition = i18n("Sorry, something unexpected happened that I do not know more about.");
+ break;
+ }
+ if(!errorCondition.isEmpty())
+ errorText = i18n("There was a connection error: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrNeg:
+ switch(streamCondition)
+ {
+ case XMPP::ClientStream::HostUnknown:
+ // FIXME: need a better error message here
+ errorCondition = i18n("Unknown host.");
+ break;
+ case XMPP::ClientStream::RemoteConnectionFailed:
+ // FIXME: need a better error message here
+ errorCondition = i18n("Could not connect to a required remote resource.");
+ break;
+ case XMPP::ClientStream::SeeOtherHost:
+ errorCondition = i18n("It appears we have been redirected to another server; I do not know how to handle this.");
+ break;
+ case XMPP::ClientStream::UnsupportedVersion:
+ errorCondition = i18n("Unsupported protocol version.");
+ break;
+ default:
+ errorCondition = i18n("Unknown error.");
+ break;
+ }
+
+ errorText = i18n("There was a negotiation error: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrTLS:
+ switch(streamCondition)
+ {
+ case XMPP::ClientStream::TLSStart:
+ errorCondition = i18n("Server rejected our request to start the TLS handshake.");
+ break;
+ case XMPP::ClientStream::TLSFail:
+ errorCondition = i18n("Failed to establish a secure connection.");
+ break;
+ default:
+ errorCondition = i18n("Unknown error.");
+ break;
+ }
+
+ errorText = i18n("There was a Transport Layer Security (TLS) error: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrAuth:
+ switch(streamCondition)
+ {
+ case XMPP::ClientStream::GenericAuthError:
+ errorCondition = i18n("Login failed with unknown reason.");
+ break;
+ case XMPP::ClientStream::NoMech:
+ errorCondition = i18n("No appropriate authentication mechanism available.");
+ break;
+ case XMPP::ClientStream::BadProto:
+ errorCondition = i18n("Bad SASL authentication protocol.");
+ break;
+ case XMPP::ClientStream::BadServ:
+ errorCondition = i18n("Server failed mutual authentication.");
+ break;
+ case XMPP::ClientStream::EncryptionRequired:
+ errorCondition = i18n("Encryption is required but not present.");
+ break;
+ case XMPP::ClientStream::InvalidAuthzid:
+ errorCondition = i18n("Invalid user ID.");
+ break;
+ case XMPP::ClientStream::InvalidMech:
+ errorCondition = i18n("Invalid mechanism.");
+ break;
+ case XMPP::ClientStream::InvalidRealm:
+ errorCondition = i18n("Invalid realm.");
+ break;
+ case XMPP::ClientStream::MechTooWeak:
+ errorCondition = i18n("Mechanism too weak.");
+ break;
+ case XMPP::ClientStream::NotAuthorized:
+ errorCondition = i18n("Wrong credentials supplied. (check your user ID and password)");
+ break;
+ case XMPP::ClientStream::TemporaryAuthFailure:
+ errorCondition = i18n("Temporary failure, please try again later.");
+ break;
+ default:
+ errorCondition = i18n("Unknown error.");
+ break;
+ }
+
+ errorText = i18n("There was an error authenticating with the server: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrSecurityLayer:
+ switch(streamCondition)
+ {
+ case XMPP::ClientStream::LayerTLS:
+ errorCondition = i18n("Transport Layer Security (TLS) problem.");
+ break;
+ case XMPP::ClientStream::LayerSASL:
+ errorCondition = i18n("Simple Authentication and Security Layer (SASL) problem.");
+ break;
+ default:
+ errorCondition = i18n("Unknown error.");
+ break;
+ }
+
+ errorText = i18n("There was an error in the security layer: %1").arg(errorCondition);
+ break;
+
+ case XMPP::ClientStream::ErrBind:
+ switch(streamCondition)
+ {
+ case XMPP::ClientStream::BindNotAllowed:
+ errorCondition = i18n("No permission to bind the resource.");
+ break;
+ case XMPP::ClientStream::BindConflict:
+ errorCondition = i18n("The resource is already in use.");
+ break;
+ default:
+ errorCondition = i18n("Unknown error.");
+ break;
+ }
+
+ errorText = i18n("Could not bind a resource: %1").arg(errorCondition);
+ break;
+
+ default:
+ errorText = i18n("Unknown error.");
+ break;
+ }
+
+ /*
+ * This mustn't be queued as otherwise the reconnection
+ * API will attempt to reconnect, queueing another
+ * error until memory is exhausted.
+ */
+ if(!errorText.isEmpty())
+ KMessageBox::error (Kopete::UI::Global::mainWidget (),
+ errorText,
+ i18n("Connection problem with Jabber server %1").arg(server));
+
+
+}
+
+void JabberAccount::slotCSError ( int error )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Error in stream signalled." << endl;
+
+ if ( ( error == XMPP::ClientStream::ErrAuth )
+ && ( client()->clientStream()->errorCondition () == XMPP::ClientStream::NotAuthorized ) )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Incorrect password, retrying." << endl;
+ disconnect(Kopete::Account::BadPassword);
+ }
+ else
+ {
+ Kopete::Account::DisconnectReason errorClass = Kopete::Account::Unknown;
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Disconnecting." << endl;
+
+ // display message to user
+ if(!m_removing) //when removing the account, connection errors are normal.
+ handleStreamError (error, client()->clientStream()->errorCondition (), client()->clientConnector()->errorCode (), server (), errorClass);
+
+ disconnect ( errorClass );
+
+ /* slotCSDisconnected will not be called*/
+ resourcePool()->clear();
+ }
+
+}
+
+/* Set presence (usually called by dialog widget). */
+void JabberAccount::setPresence ( const XMPP::Status &status )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Status: " << status.show () << ", Reason: " << status.status () << endl;
+
+ // fetch input status
+ XMPP::Status newStatus = status;
+
+ // TODO: Check if Caps is enabled
+ // Send entity capabilities
+ if( client() )
+ {
+ newStatus.setCapsNode( client()->capsNode() );
+ newStatus.setCapsVersion( client()->capsVersion() );
+ newStatus.setCapsExt( client()->capsExt() );
+ }
+
+ // make sure the status gets the correct priority
+ newStatus.setPriority ( configGroup()->readNumEntry ( "Priority", 5 ) );
+
+ XMPP::Jid jid ( myself()->contactId () );
+ XMPP::Resource newResource ( resource (), newStatus );
+
+ // update our resource in the resource pool
+ resourcePool()->addResource ( jid, newResource );
+
+ // make sure that we only consider our own resource locally
+ resourcePool()->lockToResource ( jid, newResource );
+
+ /*
+ * Unless we are in the connecting status, send a presence packet to the server
+ */
+ if(status.show () != QString("connecting") )
+ {
+ /*
+ * Make sure we are actually connected before sending out a packet.
+ */
+ if (isConnected())
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Sending new presence to the server." << endl;
+
+ XMPP::JT_Presence * task = new XMPP::JT_Presence ( client()->rootTask ());
+
+ task->pres ( newStatus );
+ task->go ( true );
+ }
+ else
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "We were not connected, presence update aborted." << endl;
+ }
+ }
+
+}
+
+void JabberAccount::slotSendRaw ()
+{
+ /* Check if we're connected. */
+ if ( !isConnected () )
+ {
+ errorConnectFirst ();
+ return;
+ }
+
+ new dlgJabberSendRaw ( client (), Kopete::UI::Global::mainWidget());
+
+}
+
+void JabberAccount::slotSubscription (const XMPP::Jid & jid, const QString & type)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << jid.full () << ", " << type << endl;
+
+ if (type == "subscribe")
+ {
+ /*
+ * A user wants to subscribe to our presence.
+ */
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << jid.full () << " is asking for authorization to subscribe." << endl;
+
+ // Is the user already in our contact list?
+ JabberBaseContact *contact = contactPool()->findExactMatch( jid );
+ Kopete::MetaContact *metaContact=0L;
+ if(contact)
+ metaContact=contact->metaContact();
+
+ int hideFlags=Kopete::UI::ContactAddedNotifyDialog::InfoButton;
+ if( metaContact && !metaContact->isTemporary() )
+ hideFlags |= Kopete::UI::ContactAddedNotifyDialog::AddCheckBox | Kopete::UI::ContactAddedNotifyDialog::AddGroupBox ;
+
+ Kopete::UI::ContactAddedNotifyDialog *dialog=
+ new Kopete::UI::ContactAddedNotifyDialog( jid.full() ,QString::null,this, hideFlags );
+ QObject::connect(dialog,SIGNAL(applyClicked(const QString&)),
+ this,SLOT(slotContactAddedNotifyDialogClosed(const QString& )));
+ dialog->show();
+ }
+ else if (type == "unsubscribed")
+ {
+ /*
+ * Someone else removed our authorization to see them.
+ */
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << jid.full() << " revoked our presence authorization" << endl;
+
+ XMPP::JT_Roster *task;
+
+ switch (KMessageBox::warningYesNo (Kopete::UI::Global::mainWidget(),
+ i18n
+ ("The Jabber user %1 removed %2's subscription to them. "
+ "This account will no longer be able to view their online/offline status. "
+ "Do you want to delete the contact?").
+ arg (jid.full(), 1).arg (accountId(), 2), i18n ("Notification"), KStdGuiItem::del(), i18n("Keep")))
+ {
+
+ case KMessageBox::Yes:
+ /*
+ * Delete this contact from our roster.
+ */
+ task = new XMPP::JT_Roster ( client()->rootTask ());
+
+ task->remove (jid);
+ task->go (true);
+
+ break;
+
+ default:
+ /*
+ * We want to leave the contact in our contact list.
+ * In this case, we need to delete all the resources
+ * we have for it, as the Jabber server won't signal us
+ * that the contact is offline now.
+ */
+ resourcePool()->removeAllResources ( jid );
+ break;
+
+ }
+ }
+}
+
+void JabberAccount::slotContactAddedNotifyDialogClosed( const QString & contactid )
+{ // the dialog that asked the authorisation is closed. (it was shown in slotSubscrition)
+
+ XMPP::JT_Presence *task;
+ XMPP::Jid jid(contactid);
+
+ const Kopete::UI::ContactAddedNotifyDialog *dialog =
+ dynamic_cast<const Kopete::UI::ContactAddedNotifyDialog *>(sender());
+ if(!dialog || !isConnected())
+ return;
+
+ if ( dialog->authorized() )
+ {
+ /*
+ * Authorize user.
+ */
+
+ task = new XMPP::JT_Presence ( client()->rootTask () );
+ task->sub ( jid, "subscribed" );
+ task->go ( true );
+ }
+ else
+ {
+ /*
+ * Reject subscription.
+ */
+ task = new XMPP::JT_Presence ( client()->rootTask () );
+ task->sub ( jid, "unsubscribed" );
+ task->go ( true );
+ }
+
+
+ if(dialog->added())
+ {
+ Kopete::MetaContact *parentContact=dialog->addContact();
+ if(parentContact)
+ {
+ QStringList groupNames;
+ Kopete::GroupList groupList = parentContact->groups();
+ for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
+ groupNames += group->displayName();
+
+ XMPP::RosterItem item;
+// XMPP::Jid jid ( contactId );
+
+ item.setJid ( jid );
+ item.setName ( parentContact->displayName() );
+ item.setGroups ( groupNames );
+
+ // add the new contact to our roster.
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( client()->rootTask () );
+
+ rosterTask->set ( item.jid(), item.name(), item.groups() );
+ rosterTask->go ( true );
+
+ // send a subscription request.
+ XMPP::JT_Presence *presenceTask = new XMPP::JT_Presence ( client()->rootTask () );
+
+ presenceTask->sub ( jid, "subscribe" );
+ presenceTask->go ( true );
+ }
+ }
+}
+
+
+
+void JabberAccount::slotContactUpdated (const XMPP::RosterItem & item)
+{
+
+ /**
+ * Subscription types are: Both, From, To, Remove, None.
+ * Both: Both sides have authed each other, each side
+ * can see each other's presence
+ * From: The other side can see us.
+ * To: We can see the other side. (implies we are
+ * authed)
+ * Remove: Other side revoked our subscription request.
+ * Not to be handled here.
+ * None: No subscription.
+ *
+ * Regardless of the subscription type, we have to add
+ * a roster item here.
+ */
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New roster item " << item.jid().full () << " (Subscription: " << item.subscription().toString () << ")" << endl;
+
+ /*
+ * See if the contact need to be added, according to the criterias of
+ * JEP-0162: Best Practices for Roster and Subscription Management
+ * http://www.jabber.org/jeps/jep-0162.html#contacts
+ */
+ bool need_to_add=false;
+ if(item.subscription().type() == XMPP::Subscription::Both || item.subscription().type() == XMPP::Subscription::To)
+ need_to_add = true;
+ else if( !item.ask().isEmpty() )
+ need_to_add = true;
+ else if( !item.name().isEmpty() || !item.groups().isEmpty() )
+ need_to_add = true;
+
+ /*
+ * See if the contact is already on our contact list
+ */
+ Kopete::Contact *c= contactPool()->findExactMatch( item.jid() );
+
+ if( c && c == c->Kopete::Contact::account()->myself() ) //don't use JabberBaseContact::account() which return alwaus the JabberAccount, and not the transport
+ {
+ // don't let remove the gateway contact, eh!
+ need_to_add = true;
+ }
+
+ if(need_to_add)
+ {
+ Kopete::MetaContact *metaContact=0L;
+ if (!c)
+ {
+ /*
+ * No metacontact has been found which contains a contact with this ID,
+ * so add a new metacontact to the list.
+ */
+ metaContact = new Kopete::MetaContact ();
+ QStringList groups = item.groups ();
+
+ // add this metacontact to all groups the contact is a member of
+ for (QStringList::Iterator it = groups.begin (); it != groups.end (); ++it)
+ metaContact->addToGroup (Kopete::ContactList::self ()->findGroup (*it));
+
+ // put it onto contact list
+ Kopete::ContactList::self ()->addMetaContact ( metaContact );
+ }
+ else
+ {
+ metaContact=c->metaContact();
+ //TODO: syncronize groups
+ }
+
+ /*
+ * Add / update the contact in our pool. In case the contact is already there,
+ * it will be updated. In case the contact is not in the meta contact yet, it
+ * will be added to it.
+ * The "dirty" flag is false here, because we just received the contact from
+ * the server's roster. As such, it is now a synchronized entry.
+ */
+ JabberContact *contact = contactPool()->addContact ( item, metaContact, false );
+
+ /*
+ * Set authorization property
+ */
+ if ( !item.ask().isEmpty () )
+ {
+ contact->setProperty ( protocol()->propAuthorizationStatus, i18n ( "Waiting for authorization" ) );
+ }
+ else
+ {
+ contact->removeProperty ( protocol()->propAuthorizationStatus );
+ }
+ }
+ else if(c) //we don't need to add it, and it is in the contactlist
+ {
+ Kopete::MetaContact *metaContact=c->metaContact();
+ if(metaContact->isTemporary())
+ return;
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << c->contactId() <<
+ " is on the contactlist while it shouldn't. we are removing it. - " << c << endl;
+ delete c;
+ if(metaContact->contacts().isEmpty())
+ Kopete::ContactList::self()->removeMetaContact( metaContact );
+ }
+
+}
+
+void JabberAccount::slotContactDeleted (const XMPP::RosterItem & item)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Deleting contact " << item.jid().full () << endl;
+
+ // since the contact instance will get deleted here, the GUI should be updated
+ contactPool()->removeContact ( item.jid () );
+
+}
+
+void JabberAccount::slotReceivedMessage (const XMPP::Message & message)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New message from " << message.from().full () << endl;
+
+ JabberBaseContact *contactFrom;
+
+ if ( message.type() == "groupchat" )
+ {
+ // this is a group chat message, forward it to the group contact
+ // (the one without resource name)
+ XMPP::Jid jid ( message.from().userHost () );
+
+ // try to locate an exact match in our pool first
+ contactFrom = contactPool()->findExactMatch ( jid );
+
+ /**
+ * If there was no exact match, something is really messed up.
+ * We can't receive group chat messages from rooms that we are
+ * not a member of and if the room contact vanished somehow,
+ * we're in deep trouble.
+ */
+ if ( !contactFrom )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "WARNING: Received a groupchat message but couldn't find room contact. Ignoring message." << endl;
+ return;
+ }
+ }
+ else
+ {
+ // try to locate an exact match in our pool first
+ contactFrom = contactPool()->findExactMatch ( message.from () );
+
+ if ( !contactFrom )
+ {
+ // we have no exact match, try a broader search
+ contactFrom = contactPool()->findRelevantRecipient ( message.from () );
+ }
+
+ // see if we found the contact in our pool
+ if ( !contactFrom )
+ {
+ // eliminate the resource from this contact,
+ // otherwise we will add the contact with the
+ // resource to our list
+ // NOTE: This is a stupid way to do it, but
+ // message.from().setResource("") had no
+ // effect. Iris bug?
+ XMPP::Jid jid ( message.from().userHost () );
+
+ // the contact is not in our pool, add it as a temporary contact
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << jid.full () << " is unknown to us, creating temporary contact." << endl;
+
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+
+ metaContact->setTemporary (true);
+
+ contactFrom = contactPool()->addContact ( XMPP::RosterItem ( jid ), metaContact, false );
+
+ Kopete::ContactList::self ()->addMetaContact (metaContact);
+ }
+ }
+
+ // pass the message on to the contact
+ contactFrom->handleIncomingMessage (message);
+
+}
+
+void JabberAccount::slotJoinNewChat ()
+{
+
+ if (!isConnected ())
+ {
+ errorConnectFirst ();
+ return;
+ }
+
+ dlgJabberChatJoin *joinDialog = new dlgJabberChatJoin ( this, Kopete::UI::Global::mainWidget () );
+ joinDialog->show ();
+
+}
+
+void JabberAccount::slotGroupChatJoined (const XMPP::Jid & jid)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Joined group chat " << jid.full () << endl;
+
+ // Create new meta contact that holds the group chat contact.
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+
+ metaContact->setTemporary ( true );
+
+ // Create a groupchat contact for this room
+ JabberGroupContact *groupContact = dynamic_cast<JabberGroupContact *>( contactPool()->addGroupContact ( XMPP::RosterItem ( jid ), true, metaContact, false ) );
+
+ if(groupContact)
+ {
+ // Add the groupchat contact to the meta contact.
+ //metaContact->addContact ( groupContact );
+
+ Kopete::ContactList::self ()->addMetaContact ( metaContact );
+ }
+ else
+ delete metaContact;
+
+ /**
+ * Add an initial resource for this contact to the pool. We need
+ * to do this to be able to lock the group status to our own presence.
+ * Our own presence will be updated right after this method returned
+ * by slotGroupChatPresence(), since the server will signal our own
+ * presence back to us.
+ */
+ resourcePool()->addResource ( XMPP::Jid ( jid.userHost () ), XMPP::Resource ( jid.resource () ) );
+
+ // lock the room to our own status
+ resourcePool()->lockToResource ( XMPP::Jid ( jid.userHost () ), jid.resource () );
+
+ m_bookmarks->insertGroupChat(jid);
+}
+
+void JabberAccount::slotGroupChatLeft (const XMPP::Jid & jid)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo "Left groupchat " << jid.full () << endl;
+
+ // remove group contact from list
+ Kopete::Contact *contact =
+ Kopete::ContactList::self()->findContact( protocol()->pluginId() , accountId() , jid.userHost() );
+
+ if ( contact )
+ {
+ Kopete::MetaContact *metaContact= contact->metaContact();
+ if( metaContact && metaContact->isTemporary() )
+ Kopete::ContactList::self()->removeMetaContact ( metaContact );
+ else
+ contact->deleteLater();
+ }
+
+ // now remove it from our pool, which should clean up all subcontacts as well
+ contactPool()->removeContact ( XMPP::Jid ( jid.userHost () ) );
+
+}
+
+void JabberAccount::slotGroupChatPresence (const XMPP::Jid & jid, const XMPP::Status & status)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Received groupchat presence for room " << jid.full () << endl;
+
+ // fetch room contact (the one without resource)
+ JabberGroupContact *groupContact = dynamic_cast<JabberGroupContact *>( contactPool()->findExactMatch ( XMPP::Jid ( jid.userHost () ) ) );
+
+ if ( !groupContact )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "WARNING: Groupchat presence signalled, but we don't have a room contact?" << endl;
+ return;
+ }
+
+ if ( !status.isAvailable () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << jid.full () << " has become unavailable, removing from room" << endl;
+
+ // remove the resource from the pool
+ resourcePool()->removeResource ( jid, XMPP::Resource ( jid.resource (), status ) );
+
+ // the person has become unavailable, remove it
+ groupContact->removeSubContact ( XMPP::RosterItem ( jid ) );
+ }
+ else
+ {
+ // add a resource for this contact to the pool (existing resources will be updated)
+ resourcePool()->addResource ( jid, XMPP::Resource ( jid.resource (), status ) );
+
+ // make sure the contact exists in the room (if it exists already, it won't be added twice)
+ groupContact->addSubContact ( XMPP::RosterItem ( jid ) );
+ }
+
+}
+
+void JabberAccount::slotGroupChatError (const XMPP::Jid &jid, int error, const QString &reason)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Group chat error - room " << jid.full () << " had error " << error << " (" << reason << ")" << endl;
+
+ switch (error)
+ {
+ case JabberClient::InvalidPasswordForMUC:
+ {
+ QCString password;
+ int result = KPasswordDialog::getPassword(password, i18n("A password is required to join the room %1.").arg(jid.node()));
+ if (result == KPasswordDialog::Accepted)
+ m_jabberClient->joinGroupChat(jid.domain(), jid.node(), jid.resource(), password);
+ }
+ break;
+
+ case JabberClient::NicknameConflict:
+ {
+ bool ok;
+ QString nickname = KInputDialog::getText(i18n("Error trying to join %1 : nickname %2 is already in use").arg(jid.node(), jid.resource()),
+ i18n("Give your nickname"),
+ QString(),
+ &ok);
+ if (ok)
+ {
+ m_jabberClient->joinGroupChat(jid.domain(), jid.node(), nickname);
+ }
+ }
+ break;
+
+ case JabberClient::BannedFromThisMUC:
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Error,
+ i18n ("You can't join the room %1 because you were banned").arg(jid.node()),
+ i18n ("Jabber Group Chat") );
+ break;
+
+ case JabberClient::MaxUsersReachedForThisMuc:
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Error,
+ i18n ("You can't join the room %1 because the maximum users has been reached").arg(jid.node()),
+ i18n ("Jabber Group Chat") );
+ break;
+
+ default:
+ {
+ QString detailedReason = reason.isEmpty () ? i18n ( "No reason given by the server" ) : reason;
+
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Error,
+ i18n ("There was an error processing your request for group chat %1. (Reason: %2, Code %3)").arg ( jid.full (), detailedReason, QString::number ( error ) ),
+ i18n ("Jabber Group Chat") );
+ }
+ }
+}
+
+void JabberAccount::slotResourceAvailable (const XMPP::Jid & jid, const XMPP::Resource & resource)
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New resource available for " << jid.full() << endl;
+
+ resourcePool()->addResource ( jid, resource );
+
+}
+
+void JabberAccount::slotResourceUnavailable (const XMPP::Jid & jid, const XMPP::Resource & resource)
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Resource now unavailable for " << jid.full () << endl;
+
+ resourcePool()->removeResource ( jid, resource );
+
+}
+
+void JabberAccount::slotEditVCard ()
+{
+ static_cast<JabberContact *>( myself() )->slotUserInfo ();
+}
+
+void JabberAccount::slotGlobalIdentityChanged (const QString &key, const QVariant &value)
+{
+ // Check if this account is excluded from Global Identity.
+ if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) )
+ {
+ JabberContact *jabberMyself = static_cast<JabberContact *>( myself() );
+ if( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ QString oldNick = jabberMyself->property( protocol()->propNickName ).value().toString();
+ QString newNick = value.toString();
+
+ if( newNick != oldNick && isConnected() )
+ {
+ jabberMyself->setProperty( protocol()->propNickName, newNick );
+ jabberMyself->slotSendVCard();
+ }
+ }
+ if( key == Kopete::Global::Properties::self()->photo().key() )
+ {
+ if( isConnected() )
+ {
+ jabberMyself->setPhoto( value.toString() );
+ jabberMyself->slotSendVCard();
+ }
+ }
+ }
+}
+
+const QString JabberAccount::resource () const
+{
+
+ return configGroup()->readEntry ( "Resource", "Kopete" );
+
+}
+
+const QString JabberAccount::server () const
+{
+
+ return configGroup()->readEntry ( "Server" );
+
+}
+
+const int JabberAccount::port () const
+{
+
+ return configGroup()->readNumEntry ( "Port", 5222 );
+
+}
+
+void JabberAccount::slotGetServices ()
+{
+ dlgJabberServices *dialog = new dlgJabberServices (this);
+
+ dialog->show ();
+ dialog->raise ();
+}
+
+void JabberAccount::slotIncomingVoiceCall( const Jid &jid )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+#ifdef SUPPORT_JINGLE
+ if(voiceCaller())
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Showing voice dialog." << endl;
+ JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( jid, voiceCaller() );
+ voiceDialog->show();
+ }
+#else
+ Q_UNUSED(jid);
+#endif
+}
+
+// void JabberAccount::slotIncomingJingleSession( const QString &sessionType, JingleSession *session )
+// {
+// #ifdef SUPPORT_JINGLE
+// if(sessionType == "http://www.google.com/session/phone")
+// {
+// QString from = ((XMPP::Jid)session->peers().first()).full();
+// //KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, QString("Received a voice session invitation from %1.").arg(from) );
+// JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( static_cast<JingleVoiceSession*>(session) );
+// voiceDialog->show();
+// }
+// #else
+// Q_UNUSED( sessionType );
+// Q_UNUSED( session );
+// #endif
+// }
+
+
+void JabberAccount::addTransport( JabberTransport * tr, const QString &jid )
+{
+ m_transports.insert(jid,tr);
+}
+
+void JabberAccount::removeTransport( const QString &jid )
+{
+ m_transports.remove(jid);
+}
+
+bool JabberAccount::removeAccount( )
+{
+ if(!m_removing)
+ {
+ int result=KMessageBox::warningYesNoCancel( Kopete::UI::Global::mainWidget () ,
+ i18n( "Do you want to also unregister \"%1\" from the Jabber server ?\n"
+ "If you unregister, all your contact list may be removed on the server,"
+ "And you will never be able to connect to this account with any client").arg( accountLabel() ),
+ i18n("Unregister"),
+ KGuiItem(i18n( "Remove and Unregister" ), "editdelete"),
+ KGuiItem(i18n( "Remove from kopete only"), "edittrash"),
+ QString(), KMessageBox::Notify | KMessageBox::Dangerous );
+ if(result == KMessageBox::Cancel)
+ {
+ return false;
+ }
+ else if(result == KMessageBox::Yes)
+ {
+ if (!isConnected())
+ {
+ errorConnectFirst ();
+ return false;
+ }
+
+ XMPP::JT_Register *task = new XMPP::JT_Register ( client()->rootTask () );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotUnregisterFinished ) );
+ task->unreg ();
+ task->go ( true );
+ m_removing=true;
+ // from my experiment, not all server reply us with a response. it simply dosconnect
+ // so after one seconde, we will force to remove the account
+ QTimer::singleShot(1111, this, SLOT(slotUnregisterFinished()));
+
+ return false; //the account will be removed when the task will be finished
+ }
+ }
+
+ //remove transports from config file.
+ QMap<QString,JabberTransport*> tranposrts_copy=m_transports;
+ QMap<QString,JabberTransport*>::Iterator it;
+ for ( it = tranposrts_copy.begin(); it != tranposrts_copy.end(); ++it )
+ {
+ (*it)->jabberAccountRemoved();
+ }
+ return true;
+}
+
+void JabberAccount::slotUnregisterFinished( )
+{
+ const XMPP::JT_Register * task = dynamic_cast<const XMPP::JT_Register *>(sender ());
+
+ if ( task && ! task->success ())
+ {
+ KMessageBox::queuedMessageBox ( 0L, KMessageBox::Error,
+ i18n ("An error occured when trying to remove the account:\n%1").arg(task->statusString()),
+ i18n ("Jabber Account Unregistration"));
+ m_removing=false;
+ return;
+ }
+ if(m_removing) //it may be because this is now the timer.
+ Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
+}
+
+
+
+
+
+#include "jabberaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/jabberaccount.h b/kopete/protocols/jabber/jabberaccount.h
new file mode 100644
index 00000000..3731b590
--- /dev/null
+++ b/kopete/protocols/jabber/jabberaccount.h
@@ -0,0 +1,309 @@
+
+/***************************************************************************
+ jabberaccount.h - core Jabber account class
+ -------------------
+ begin : Sat Mar 8 2003
+ copyright : (C) 2003 by Till Gerken <till@tantalo.net>
+ Based on JabberProtocol by Daniel Stone <dstone@kde.org>
+ and Till Gerken <till@tantalo.net>.
+ copyright : (C) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (C) 2001-2003 Kopete developers <kopete-devel@kde.org>.
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBERACCOUNT_H
+#define JABBERACCOUNT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// we need these for type reasons
+#include <kopetepasswordedaccount.h>
+#include <kopeteonlinestatus.h>
+#include <im.h>
+#include "jabberclient.h"
+
+class QString;
+class QStringList;
+class KActionMenu;
+class JabberResourcePool;
+class JabberContact;
+class JabberContactPool;
+class JabberProtocol;
+class JabberTransport;
+class JabberBookmarks;
+
+namespace Kopete { class MetaContact; }
+
+#ifdef SUPPORT_JINGLE
+//class JingleSessionManager;
+//class JingleSession;
+class VoiceCaller;
+#endif
+
+
+/* @author Daniel Stone, Till Gerken */
+
+class JabberAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+
+public:
+ JabberAccount (JabberProtocol * parent, const QString & accountID, const char *name = 0L);
+ ~JabberAccount ();
+
+ /* Returns the action menu for this account. */
+ virtual KActionMenu *actionMenu ();
+
+ /* Return the resource of the client */
+ const QString resource () const;
+ const QString server () const;
+ const int port () const;
+
+ JabberResourcePool *resourcePool ();
+ JabberContactPool *contactPool ();
+
+ /* to get the protocol from the account */
+ JabberProtocol *protocol () const
+ {
+ return m_protocol;
+ }
+
+ JabberClient *client () const
+ {
+ return m_jabberClient;
+ }
+
+#ifdef SUPPORT_JINGLE
+ VoiceCaller *voiceCaller() const
+ {
+ return m_voiceCaller;
+ }
+
+// JingleSessionManager *sessionManager() const
+// {
+// return m_jingleSessionManager;
+// }
+#endif
+
+ // change the default S5B server port
+ void setS5BServerPort ( int port );
+
+ /* Tells the user to connect first before they can do whatever it is
+ * that they want to do. */
+ void errorConnectFirst ();
+
+ /* Tells the user that the connection was lost while we waited for
+ * an answer of him. */
+ void errorConnectionLost ();
+
+ /*
+ * Handle TLS warnings. Displays a dialog and returns the user's choice.
+ * Parameters: Warning code from QCA::TLS
+ * Automatically resumes the stream if wanted.
+ */
+ /**
+ * Handle a TLS warning. Displays a dialog and returns if the
+ * stream can be continued or not.
+ * @param client JabberClient instance
+ * @param warning Warning code from QCA::TLS
+ * @return True if stream can be resumed.
+ */
+ static bool handleTLSWarning ( JabberClient *client, int warning );
+
+ /*
+ * Handle stream errors. Displays a dialog and returns.
+ */
+ static void handleStreamError (int streamError, int streamCondition, int connectorCode, const QString &server, Kopete::Account::DisconnectReason &errorClass);
+
+ const QMap<QString, JabberTransport *> &transports()
+ { return m_transports; }
+
+
+ /**
+ * called when the account is removed in the config ui
+ */
+ virtual bool removeAccount();
+
+public slots:
+ /* Connects to the server. */
+ void connectWithPassword ( const QString &password );
+
+ /* Disconnects from the server. */
+ void disconnect ();
+
+ /* Disconnect with a reason */
+ void disconnect ( Kopete::Account::DisconnectReason reason );
+
+ /* Disconnect with a reason, and status */
+ void disconnect( Kopete::Account::DisconnectReason reason, XMPP::Status &status );
+ /* Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+
+ void addTransport( JabberTransport *tr , const QString &jid);
+ void removeTransport( const QString &jid );
+
+
+protected:
+ /**
+ * Create a new contact in the specified metacontact
+ *
+ * You shouldn't ever call this method yourself, For adding contacts see @ref addContact()
+ *
+ * This method is called by @ref Kopete::Account::addContact() in this method, you should
+ * simply create the new custom @ref Kopete::Contact in the given metacontact. You should
+ * NOT add the contact to the server here as this method gets only called when synchronizing
+ * the contact list on disk with the one in memory. As such, all created contacts from this
+ * method should have the "dirty" flag set.
+ *
+ * This method should simply be used to intantiate the new contact, everything else
+ * (updating the GUI, parenting to meta contact, etc.) is being taken care of.
+ *
+ * @param contactId The unique ID for this protocol
+ * @param parentContact The metacontact to add this contact to
+ */
+ virtual bool createContact (const QString & contactID, Kopete::MetaContact * parentContact);
+
+
+
+private:
+ JabberProtocol *m_protocol;
+
+ // backend for this account
+ JabberClient *m_jabberClient;
+
+ JabberResourcePool *m_resourcePool;
+ JabberContactPool *m_contactPool;
+
+#ifdef SUPPORT_JINGLE
+ VoiceCaller *m_voiceCaller;
+ //JingleSessionManager *m_jingleSessionManager;
+#endif
+
+ JabberBookmarks *m_bookmarks;
+
+ /* Set up our actions for the status menu. */
+ void initActions ();
+
+ void cleanup ();
+
+ /* Initial presence to set after connecting. */
+ XMPP::Status m_initialPresence;
+
+ /**
+ * Sets our own presence. Updates our resource in the
+ * resource pool and sends a presence packet to the server.
+ */
+ void setPresence ( const XMPP::Status &status );
+
+ /**
+ * Returns if a connection attempt is currently in progress.
+ */
+ bool isConnecting ();
+
+ QMap<QString, JabberTransport *>m_transports;
+
+ /* used in removeAccount() */
+ bool m_removing;
+ /* keep track if we told the user we were not able to bind the
+ jabber transfer port, to avoid popup insanity */
+ bool m_notifiedUserCannotBindTransferPort;
+private slots:
+ /* Connects to the server. */
+ void slotConnect ();
+
+ /* Disconnects from the server. */
+ void slotDisconnect ();
+
+ // handle a TLS warning
+ void slotHandleTLSWarning ( int validityResult );
+
+ // handle client errors
+ void slotClientError ( JabberClient::ErrorCode errorCode );
+
+ // we are connected to the server
+ void slotConnected ();
+
+ /* Called from Psi: tells us when we've been disconnected from the server. */
+ void slotCSDisconnected ();
+
+ /* Called from Psi: alerts us to a protocol error. */
+ void slotCSError (int);
+
+ /* Called from Psi: roster request finished */
+ void slotRosterRequestFinished ( bool success );
+
+ /* Called from Psi: incoming file transfer */
+ void slotIncomingFileTransfer ();
+
+ /* Called from Psi: debug messages from the backend. */
+ void slotClientDebugMessage (const QString &msg);
+
+ /* Sends a raw message to the server (use with caution) */
+ void slotSendRaw ();
+
+ /* Slots for handling group chats. */
+ void slotJoinNewChat ();
+ void slotGroupChatJoined ( const XMPP::Jid &jid );
+ void slotGroupChatLeft ( const XMPP::Jid &jid );
+ void slotGroupChatPresence ( const XMPP::Jid &jid, const XMPP::Status &status );
+ void slotGroupChatError ( const XMPP::Jid &jid, int error, const QString &reason );
+
+ /* Incoming subscription request. */
+ void slotSubscription ( const XMPP::Jid &jid, const QString &type );
+
+ /* the dialog that asked to add the contact was closed (that dialog is shown in slotSubscription) */
+ void slotContactAddedNotifyDialogClosed(const QString& contactid);
+
+ /**
+ * A new item appeared in our roster, synch it with the
+ * contact list.
+ * (or the contact has been updated
+ */
+ void slotContactUpdated ( const XMPP::RosterItem & );
+
+ /**
+ * An item has been deleted from our roster,
+ * delete it from our contact pool.
+ */
+ void slotContactDeleted ( const XMPP::RosterItem & );
+
+
+ /* Someone on our contact list had (another) resource come online. */
+ void slotResourceAvailable ( const XMPP::Jid &, const XMPP::Resource & );
+
+ /* Someone on our contact list had (another) resource go offline. */
+ void slotResourceUnavailable ( const XMPP::Jid &, const XMPP::Resource & );
+
+ /* Displays a new message. */
+ void slotReceivedMessage ( const XMPP::Message & );
+
+ /* Gets the user's vCard from the server for editing. */
+ void slotEditVCard ();
+
+ /* Get the services list from the server for management. */
+ void slotGetServices ();
+
+ /* Update the myself information if the global identity changes. */
+ void slotGlobalIdentityChanged( const QString &key, const QVariant &value );
+
+ /* we received a voice invitation */
+ void slotIncomingVoiceCall(const Jid&);
+
+ /* the unregister task finished */
+ void slotUnregisterFinished();
+
+ //void slotIncomingJingleSession(const QString &sessionType, JingleSession *session);
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberbasecontact.cpp b/kopete/protocols/jabber/jabberbasecontact.cpp
new file mode 100644
index 00000000..56cc1de4
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbasecontact.cpp
@@ -0,0 +1,676 @@
+ /*
+ * jabbercontact.cpp - Base class for the Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kstandarddirs.h>
+#include <qtimer.h>
+#include <qimage.h>
+#include <qregexp.h>
+#include <kmessagebox.h>
+#include <kio/netaccess.h>
+
+
+#include <kopetegroup.h>
+#include <kopetecontactlist.h>
+
+#include "jabberbasecontact.h"
+
+#include "xmpp_tasks.h"
+
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberresource.h"
+#include "jabberresourcepool.h"
+#include "kopetemetacontact.h"
+#include "kopetemessage.h"
+#include "kopeteuiglobal.h"
+#include "jabbertransport.h"
+#include "dlgjabbervcard.h"
+
+
+/**
+ * JabberBaseContact constructor
+ */
+JabberBaseContact::JabberBaseContact (const XMPP::RosterItem &rosterItem, Kopete::Account *account, Kopete::MetaContact * mc, const QString &legacyId)
+ : Kopete::Contact (account, legacyId.isEmpty() ? rosterItem.jid().full() : legacyId , mc )
+{
+ setDontSync ( false );
+
+ JabberTransport *t=transport();
+ m_account= t ? t->account() : static_cast<JabberAccount *>(Kopete::Contact::account());
+
+
+ // take roster item and update display name
+ updateContact ( rosterItem );
+
+}
+
+
+JabberProtocol *JabberBaseContact::protocol ()
+{
+
+ return static_cast<JabberProtocol *>(Kopete::Contact::protocol ());
+}
+
+
+JabberTransport * JabberBaseContact::transport( )
+{
+ return dynamic_cast<JabberTransport*>(Kopete::Contact::account());
+}
+
+
+/* Return if we are reachable (defaults to true because
+ we can send on- and offline, only return false if the
+ account itself is offline, too) */
+bool JabberBaseContact::isReachable ()
+{
+ if (account()->isConnected())
+ return true;
+
+ return false;
+
+}
+
+void JabberBaseContact::updateContact ( const XMPP::RosterItem & item )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Synchronizing local copy of " << contactId() << " with information received from server. (name='" << item.name() << "' groups='" << item.groups() << "')"<< endl;
+
+ mRosterItem = item;
+
+ // if we don't have a meta contact yet, stop processing here
+ if ( !metaContact () )
+ return;
+
+ /*
+ * We received the information from the server, as such,
+ * don't attempt to synch while we update our local copy.
+ */
+ setDontSync ( true );
+
+ // The myself contact is not in the roster on server, ignore this code
+ // because the myself MetaContact displayname become the latest
+ // Jabber acccount jid.
+ if( metaContact() != Kopete::ContactList::self()->myself() )
+ {
+ // only update the alias if its not empty
+ if ( !item.name().isEmpty () && item.name() != item.jid().bare() )
+ {
+ QString newName = item.name ();
+ QString oldName = metaContact()->displayName();
+ Kopete::Contact *source=metaContact()->displayNameSourceContact();
+// kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "setting display name of " << contactId () << " to " << newName << endl;
+ metaContact()->setDisplayName ( newName );
+ //automatically set to custom source if the source is to this contact.
+ if( metaContact()->displayNameSource()==Kopete::MetaContact::SourceContact && newName != oldName && ( source == this || source == 0L ) )
+ metaContact()->setDisplayNameSource( Kopete::MetaContact::SourceCustom );
+ }
+ }
+
+ /*
+ * Set the contact's subscription status
+ */
+ switch ( item.subscription().type () )
+ {
+ case XMPP::Subscription::None:
+ setProperty ( protocol()->propSubscriptionStatus,
+ i18n ( "You cannot see each others' status." ) );
+ break;
+ case XMPP::Subscription::To:
+ setProperty ( protocol()->propSubscriptionStatus,
+ i18n ( "You can see this contact's status but they cannot see your status." ) );
+ break;
+ case XMPP::Subscription::From:
+ setProperty ( protocol()->propSubscriptionStatus,
+ i18n ( "This contact can see your status but you cannot see their status." ) );
+ break;
+ case XMPP::Subscription::Both:
+ setProperty ( protocol()->propSubscriptionStatus,
+ i18n ( "You can see each others' status." ) );
+ break;
+ }
+
+ if( !metaContact()->isTemporary() )
+ {
+ /*
+ * In this method, as opposed to KC::syncGroups(),
+ * the group list from the server is authoritative.
+ * As such, we need to find a list of all groups
+ * that the meta contact resides in but does not
+ * reside in on the server anymore, as well as all
+ * groups that the meta contact does not reside in,
+ * but resides in on the server.
+ * Then, we'll have to synchronize the KMC using
+ * that information.
+ */
+ Kopete::GroupList groupsToRemoveFrom, groupsToAddTo;
+
+ // find all groups our contact is in but that are not in the server side roster
+ for ( unsigned i = 0; i < metaContact()->groups().count (); i++ )
+ {
+ if ( item.groups().find ( metaContact()->groups().at(i)->displayName () ) == item.groups().end () )
+ groupsToRemoveFrom.append ( metaContact()->groups().at ( i ) );
+ }
+
+ // now find all groups that are in the server side roster but not in the local group
+ for ( unsigned i = 0; i < item.groups().count (); i++ )
+ {
+ bool found = false;
+ for ( unsigned j = 0; j < metaContact()->groups().count (); j++)
+ {
+ if ( metaContact()->groups().at(j)->displayName () == *item.groups().at(i) )
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ groupsToAddTo.append ( Kopete::ContactList::self()->findGroup ( *item.groups().at(i) ) );
+ }
+ }
+
+ /*
+ * Special case: if we don't add the contact to any group and the
+ * list of groups to remove from contains the top level group, we
+ * risk removing the contact from the visible contact list. In this
+ * case, we need to make sure at least the top level group stays.
+ */
+ if ( ( groupsToAddTo.count () == 0 ) && ( groupsToRemoveFrom.contains ( Kopete::Group::topLevel () ) ) )
+ {
+ groupsToRemoveFrom.remove ( Kopete::Group::topLevel () );
+ }
+
+ for ( Kopete::Group *group = groupsToRemoveFrom.first (); group; group = groupsToRemoveFrom.next () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Removing " << contactId() << " from group " << group->displayName () << endl;
+ metaContact()->removeFromGroup ( group );
+ }
+
+ for ( Kopete::Group *group = groupsToAddTo.first (); group; group = groupsToAddTo.next () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Adding " << contactId() << " to group " << group->displayName () << endl;
+ metaContact()->addToGroup ( group );
+ }
+ }
+
+ /*
+ * Enable updates for the server again.
+ */
+ setDontSync ( false );
+
+ //can't do it now because it's called from contructor at a point some virtual function are not available
+ QTimer::singleShot(0, this, SLOT(reevaluateStatus()));
+
+}
+
+void JabberBaseContact::updateResourceList ()
+{
+ /*
+ * Set available resources.
+ * This is a bit more complicated: We need to generate
+ * all images dynamically from the KOS icons and store
+ * them into the mime factory, then plug them into
+ * the richtext.
+ */
+ JabberResourcePool::ResourceList resourceList;
+ account()->resourcePool()->findResources ( rosterItem().jid() , resourceList );
+
+ if ( resourceList.isEmpty () )
+ {
+ removeProperty ( protocol()->propAvailableResources );
+ return;
+ }
+
+ QString resourceListStr = "<table cellspacing=\"0\">";
+
+ for ( JabberResourcePool::ResourceList::iterator it = resourceList.begin (); it != resourceList.end (); ++it )
+ {
+ // icon, resource name and priority
+ resourceListStr += QString ( "<tr><td><img src=\"kopete-onlinestatus-icon:%1\" /> <b>%2</b> (Priority: %3)</td></tr>" ).
+ arg ( protocol()->resourceToKOS((*it)->resource()).mimeSourceFor ( account () ),
+ (*it)->resource().name (), QString::number ( (*it)->resource().priority () ) );
+
+ // client name, version, OS
+ if ( !(*it)->clientName().isEmpty () )
+ {
+ resourceListStr += QString ( "<tr><td>%1: %2 (%3)</td></tr>" ).
+ arg ( i18n ( "Client" ), (*it)->clientName (), (*it)->clientSystem () );
+ }
+
+ // Supported features
+#if 0 //disabled because it's just an ugly and long list of incomprehensible namespaces to the user
+ QStringList supportedFeatures = (*it)->features().list();
+ QStringList::ConstIterator featuresIt, featuresItEnd = supportedFeatures.constEnd();
+ if( !supportedFeatures.empty() )
+ resourceListStr += QString( "<tr><td>Supported Features:" );
+ for( featuresIt = supportedFeatures.constBegin(); featuresIt != featuresItEnd; ++featuresIt )
+ {
+ XMPP::Features tempFeature(*featuresIt);
+ resourceListStr += QString("\n<br>");
+ if ( tempFeature.id() > XMPP::Features::FID_None )
+ resourceListStr += tempFeature.name() + QString(" (");
+ resourceListStr += *featuresIt;
+ if ( tempFeature.id() > Features::FID_None )
+ resourceListStr += QString(")");
+ }
+ if( !supportedFeatures.empty() )
+ resourceListStr += QString( "</td></tr>" );
+#endif
+
+ // resource timestamp
+ resourceListStr += QString ( "<tr><td>%1: %2</td></tr>" ).
+ arg ( i18n ( "Timestamp" ), KGlobal::locale()->formatDateTime ( (*it)->resource().status().timeStamp(), true, true ) );
+
+ // message, if any
+ if ( !(*it)->resource().status().status().stripWhiteSpace().isEmpty () )
+ {
+ resourceListStr += QString ( "<tr><td>%1: %2</td></tr>" ).
+ arg (
+ i18n ( "Message" ),
+ Kopete::Message::escape( (*it)->resource().status().status () )
+ );
+ }
+ }
+
+ resourceListStr += "</table>";
+
+ setProperty ( protocol()->propAvailableResources, resourceListStr );
+}
+
+void JabberBaseContact::reevaluateStatus ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Determining new status for " << contactId () << endl;
+
+ Kopete::OnlineStatus status;
+ XMPP::Resource resource = account()->resourcePool()->bestResource ( mRosterItem.jid () );
+
+ status = protocol()->resourceToKOS ( resource );
+
+
+ /* Add some icon to show the subscription */
+ if( ( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::From)
+ && inherits ( "JabberContact" ) && metaContact() != Kopete::ContactList::self()->myself() && account()->isConnected() )
+ {
+ status = Kopete::OnlineStatus(status.status() ,
+ status.weight() ,
+ protocol() ,
+ status.internalStatus() | 0x0100,
+ status.overlayIcons() + QStringList("status_unknown_overlay") , //FIXME: find better icon
+ status.description() );
+ }
+
+
+ updateResourceList ();
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "New status for " << contactId () << " is " << status.description () << endl;
+ setOnlineStatus ( status );
+
+ /*
+ * Set away message property.
+ * We just need to read it from the current resource.
+ */
+ if ( !resource.status ().status ().isEmpty () )
+ {
+ setProperty ( protocol()->propAwayMessage, resource.status().status () );
+ }
+ else
+ {
+ removeProperty ( protocol()->propAwayMessage );
+ }
+
+}
+
+QString JabberBaseContact::fullAddress ()
+{
+
+ XMPP::Jid jid = rosterItem().jid();
+
+ if ( jid.resource().isEmpty () )
+ {
+ jid.setResource ( account()->resourcePool()->bestResource ( jid ).name () );
+ }
+
+ return jid.full ();
+
+}
+
+XMPP::Jid JabberBaseContact::bestAddress ()
+{
+
+ // see if we are subscribed with a preselected resource
+ if ( !mRosterItem.jid().resource().isEmpty () )
+ {
+ // we have a preselected resource, so return our default full address
+ return mRosterItem.jid ();
+ }
+
+ // construct address out of user@host and current best resource
+ XMPP::Jid jid = mRosterItem.jid ();
+ jid.setResource ( account()->resourcePool()->bestResource( mRosterItem.jid() ).name () );
+
+ return jid;
+
+}
+
+void JabberBaseContact::setDontSync ( bool flag )
+{
+
+ mDontSync = flag;
+
+}
+
+bool JabberBaseContact::dontSync ()
+{
+
+ return mDontSync;
+
+}
+
+void JabberBaseContact::serialize (QMap < QString, QString > &serializedData, QMap < QString, QString > & /* addressBookData */ )
+{
+
+ // Contact id and display name are already set for us, only add the rest
+ serializedData["JID"] = mRosterItem.jid().full();
+
+ serializedData["groups"] = mRosterItem.groups ().join (QString::fromLatin1 (","));
+}
+
+void JabberBaseContact::slotUserInfo( )
+{
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ // Update the vCard
+ //slotGetTimedVCard();
+
+ new dlgJabberVCard ( account(), this, Kopete::UI::Global::mainWidget () );
+}
+
+void JabberBaseContact::setPropertiesFromVCard ( const XMPP::VCard &vCard )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Updating vCard for " << contactId () << endl;
+
+ // update vCard cache timestamp if this is not a temporary contact
+ if ( metaContact() && !metaContact()->isTemporary () )
+ {
+ setProperty ( protocol()->propVCardCacheTimeStamp, QDateTime::currentDateTime().toString ( Qt::ISODate ) );
+ }
+
+
+ /*
+ * Set the nickname property.
+ * but ignore it if we are in a groupchat, or it will clash with the normal nickname
+ */
+ if(inherits ( "JabberContact" ))
+ {
+ if ( !vCard.nickName().isEmpty () )
+ {
+ setProperty ( protocol()->propNickName, vCard.nickName () );
+ }
+ else
+ {
+ removeProperty ( protocol()->propNickName );
+ }
+ }
+
+ /**
+ * Kopete does not allow a modification of the "full name"
+ * property. However, some vCards specify only the full name,
+ * some specify only first and last name.
+ * Due to these inconsistencies, if first and last name don't
+ * exist, it is attempted to parse the full name.
+ */
+
+ // remove all properties first
+ removeProperty ( protocol()->propFirstName );
+ removeProperty ( protocol()->propLastName );
+ removeProperty ( protocol()->propFullName );
+
+ if ( !vCard.fullName().isEmpty () && vCard.givenName().isEmpty () && vCard.familyName().isEmpty () )
+ {
+ QString lastName = vCard.fullName().section ( ' ', 0, -1 );
+ QString firstName = vCard.fullName().left(vCard.fullName().length () - lastName.length ()).stripWhiteSpace ();
+
+ setProperty ( protocol()->propFirstName, firstName );
+ setProperty ( protocol()->propLastName, lastName );
+ }
+ else
+ {
+ if ( !vCard.givenName().isEmpty () )
+ setProperty ( protocol()->propFirstName, vCard.givenName () );
+
+ if ( !vCard.familyName().isEmpty () )
+ setProperty ( protocol()->propLastName, vCard.familyName () );
+ }
+ if( !vCard.fullName().isEmpty() )
+ setProperty ( protocol()->propFullName, vCard.fullName() );
+
+ /*
+ * Set the general information
+ */
+ removeProperty( protocol()->propJid );
+ removeProperty( protocol()->propBirthday );
+ removeProperty( protocol()->propTimezone );
+ removeProperty( protocol()->propHomepage );
+
+ setProperty( protocol()->propJid, vCard.jid() );
+
+ if( !vCard.bdayStr().isEmpty () )
+ setProperty( protocol()->propBirthday, vCard.bdayStr() );
+ if( !vCard.timezone().isEmpty () )
+ setProperty( protocol()->propTimezone, vCard.timezone() );
+ if( !vCard.url().isEmpty () )
+ setProperty( protocol()->propHomepage, vCard.url() );
+
+ /*
+ * Set the work information.
+ */
+ removeProperty( protocol()->propCompanyName );
+ removeProperty( protocol()->propCompanyDepartement );
+ removeProperty( protocol()->propCompanyPosition );
+ removeProperty( protocol()->propCompanyRole );
+
+ if( !vCard.org().name.isEmpty() )
+ setProperty( protocol()->propCompanyName, vCard.org().name );
+ if( !vCard.org().unit.join(",").isEmpty() )
+ setProperty( protocol()->propCompanyDepartement, vCard.org().unit.join(",")) ;
+ if( !vCard.title().isEmpty() )
+ setProperty( protocol()->propCompanyPosition, vCard.title() );
+ if( !vCard.role().isEmpty() )
+ setProperty( protocol()->propCompanyRole, vCard.role() );
+
+ /*
+ * Set the about information
+ */
+ removeProperty( protocol()->propAbout );
+
+ if( !vCard.desc().isEmpty() )
+ setProperty( protocol()->propAbout, vCard.desc() );
+
+
+ /*
+ * Set the work and home addresses information
+ */
+ removeProperty( protocol()->propWorkStreet );
+ removeProperty( protocol()->propWorkExtAddr );
+ removeProperty( protocol()->propWorkPOBox );
+ removeProperty( protocol()->propWorkCity );
+ removeProperty( protocol()->propWorkPostalCode );
+ removeProperty( protocol()->propWorkCountry );
+
+ removeProperty( protocol()->propHomeStreet );
+ removeProperty( protocol()->propHomeExtAddr );
+ removeProperty( protocol()->propHomePOBox );
+ removeProperty( protocol()->propHomeCity );
+ removeProperty( protocol()->propHomePostalCode );
+ removeProperty( protocol()->propHomeCountry );
+
+ for(XMPP::VCard::AddressList::const_iterator it = vCard.addressList().begin(); it != vCard.addressList().end(); it++)
+ {
+ XMPP::VCard::Address address = (*it);
+
+ if(address.work)
+ {
+ setProperty( protocol()->propWorkStreet, address.street );
+ setProperty( protocol()->propWorkExtAddr, address.extaddr );
+ setProperty( protocol()->propWorkPOBox, address.pobox );
+ setProperty( protocol()->propWorkCity, address.locality );
+ setProperty( protocol()->propWorkPostalCode, address.pcode );
+ setProperty( protocol()->propWorkCountry, address.country );
+ }
+ else
+ if(address.home)
+ {
+ setProperty( protocol()->propHomeStreet, address.street );
+ setProperty( protocol()->propHomeExtAddr, address.extaddr );
+ setProperty( protocol()->propHomePOBox, address.pobox );
+ setProperty( protocol()->propHomeCity, address.locality );
+ setProperty( protocol()->propHomePostalCode, address.pcode );
+ setProperty( protocol()->propHomeCountry, address.country );
+ }
+ }
+
+
+ /*
+ * Delete emails first, they might not be present
+ * in the vCard at all anymore.
+ */
+ removeProperty ( protocol()->propEmailAddress );
+ removeProperty ( protocol()->propWorkEmailAddress );
+
+ /*
+ * Set the home and work email information.
+ */
+ XMPP::VCard::EmailList::const_iterator emailEnd = vCard.emailList().end ();
+ for(XMPP::VCard::EmailList::const_iterator it = vCard.emailList().begin(); it != emailEnd; ++it)
+ {
+ XMPP::VCard::Email email = (*it);
+
+ if(email.work)
+ {
+ if( !email.userid.isEmpty() )
+ setProperty ( protocol()->propWorkEmailAddress, email.userid );
+ }
+ else
+ if(email.home)
+ {
+ if( !email.userid.isEmpty() )
+ setProperty ( protocol()->propEmailAddress, email.userid );
+ }
+ }
+
+ /*
+ * Delete phone number properties first as they might have
+ * been unset during an update and are not present in
+ * the vCard at all anymore.
+ */
+ removeProperty ( protocol()->propPrivatePhone );
+ removeProperty ( protocol()->propPrivateMobilePhone );
+ removeProperty ( protocol()->propWorkPhone );
+ removeProperty ( protocol()->propWorkMobilePhone );
+
+ /*
+ * Set phone numbers. Note that if a mobile phone number
+ * is specified, it's assigned to the private mobile
+ * phone number property. This might not be the desired
+ * behavior for all users.
+ */
+ XMPP::VCard::PhoneList::const_iterator phoneEnd = vCard.phoneList().end ();
+ for(XMPP::VCard::PhoneList::const_iterator it = vCard.phoneList().begin(); it != phoneEnd; ++it)
+ {
+ XMPP::VCard::Phone phone = (*it);
+
+ if(phone.work)
+ {
+ setProperty ( protocol()->propWorkPhone, phone.number );
+ }
+ else
+ if(phone.fax)
+ {
+ setProperty ( protocol()->propPhoneFax, phone.number);
+ }
+ else
+ if(phone.cell)
+ {
+ setProperty ( protocol()->propPrivateMobilePhone, phone.number );
+ }
+ else
+ if(phone.home)
+ {
+ setProperty ( protocol()->propPrivatePhone, phone.number );
+ }
+
+ }
+
+ /*
+ * Set photo/avatar property.
+ */
+ removeProperty( protocol()->propPhoto );
+
+ QImage contactPhoto;
+ QString fullJid = mRosterItem.jid().full();
+ QString finalPhotoPath = locateLocal("appdata", "jabberphotos/" + fullJid.replace(QRegExp("[./~]"),"-") +".png");
+
+ // photo() is a QByteArray
+ if ( !vCard.photo().isEmpty() )
+ {
+ kdDebug( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Contact has a photo embedded into his vCard." << endl;
+
+ // QImage is used to save to disk in PNG later.
+ contactPhoto = QImage( vCard.photo() );
+ }
+ // Contact photo is a URI.
+ else if( !vCard.photoURI().isEmpty() )
+ {
+ QString tempPhotoPath = 0;
+
+ // Downalod photo from URI.
+ if( !KIO::NetAccess::download( vCard.photoURI(), tempPhotoPath, 0) )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget (), KMessageBox::Sorry, i18n( "Downloading of Jabber contact photo failed!" ) );
+ return;
+ }
+
+ kdDebug( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Contact photo is a URI." << endl;
+
+ contactPhoto = QImage( tempPhotoPath );
+
+ KIO::NetAccess::removeTempFile( tempPhotoPath );
+ }
+
+ // Save the image to the disk, then set the property.
+ if( !contactPhoto.isNull() && contactPhoto.save(finalPhotoPath, "PNG") )
+ {
+ kdDebug( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Setting photo for contact: " << fullJid << endl;
+ setProperty( protocol()->propPhoto, finalPhotoPath );
+ }
+
+}
+
+
+
+
+#include "jabberbasecontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/jabberbasecontact.h b/kopete/protocols/jabber/jabberbasecontact.h
new file mode 100644
index 00000000..7ba5c3fb
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbasecontact.h
@@ -0,0 +1,185 @@
+ /*
+ * jabbercontact.h - Base class for the Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERBASECONTACT_H
+#define JABBERBASECONTACT_H
+
+#include "kopetecontact.h"
+#include "xmpp.h"
+#include "im.h"
+
+class dlgJabberVCard;
+class JabberProtocol;
+class JabberAccount;
+class JabberResource;
+class JabberTransport;
+namespace Kopete { class MetaContact; }
+namespace XMPP { class VCard; }
+
+class JabberBaseContact : public Kopete::Contact
+{
+
+Q_OBJECT
+friend class JabberAccount; /* Friends can touch each other's private parts. */
+
+public:
+
+ /**
+ * @param legacyId is the contactId of the contact if != Jid
+ */
+ JabberBaseContact (const XMPP::RosterItem &rosterItem,
+ Kopete::Account *account, Kopete::MetaContact * mc,
+ const QString &legacyId=QString());
+
+ /********************************************************************
+ *
+ * Kopete::Contact reimplementation start
+ *
+ ********************************************************************/
+
+ /**
+ * Return the protocol instance associated with this contact
+ */
+ JabberProtocol *protocol ();
+
+ /**
+ * Return the account instance associated with this contact
+ */
+ JabberAccount *account () const { return m_account; };
+
+ /**
+ * return the transport if any, or null
+ */
+ JabberTransport *transport();
+
+ /**
+ * Return if the contact is reachable (this is true if the account
+ * is online)
+ */
+ virtual bool isReachable ();
+
+ /**
+ * Create custom context menu items for the contact
+ * FIXME: implement manager version here?
+ */
+ virtual QPtrList<KAction> *customContextMenuActions () = 0;
+
+ /**
+ * Serialize contact
+ */
+ virtual void serialize (QMap < QString, QString > &serializedData, QMap < QString, QString > &addressBookData);
+
+ /**
+ * Update contact if a roster item has been
+ * received for it. (used during login)
+ */
+ void updateContact ( const XMPP::RosterItem &item );
+
+ /**
+ * Deal with an incoming message for this contact.
+ */
+ virtual void handleIncomingMessage ( const XMPP::Message &message ) = 0;
+
+ /**
+ * Update the resource property of the
+ * contact, listing all available resources.
+ */
+ void updateResourceList ();
+
+ /**
+ * Return current full address.
+ * Uses bestResource() if no presubscribed
+ * address exists.
+ */
+ QString fullAddress ();
+
+ /**
+ * Set the dontSync flag for this contact.
+ * If this flag is set, calls to @ref sync will
+ * be ignored. This is required if the contact
+ * has been moved between groups on the server
+ * after we logged in and we try to update our
+ * local contact list. Since libkopete can only
+ * handle one group update at a time, moving
+ * between groups requires to operations which
+ * each in turn would cause a call to sync(),
+ * overwriting the change that is being carried
+ * out. (besides causing unnecessary traffic)
+ * This is avoided by setting the dontSync flag
+ * while synchronizing the local copy.
+ */
+ void setDontSync ( bool flag );
+
+ /**
+ * Return the status of the dontSync flag.
+ * See @ref setDontSync for a full description.
+ */
+ bool dontSync ();
+
+ /**
+ * return the roster item of the contact.
+ * to get the jid, use rosterItem().jid().full() don't use contactId as it is not the same with transport
+ */
+ XMPP::RosterItem rosterItem() const { return mRosterItem; }
+
+ /**
+ * Reads a vCard object and updates the contact's
+ * properties accordingly.
+ */
+ void setPropertiesFromVCard ( const XMPP::VCard &vCard );
+
+
+public slots:
+
+ /**
+ * Retrieve a vCard for the contact
+ */
+ virtual void slotUserInfo ();
+
+
+ /**
+ * Re-evaluate online status. Gets called
+ * whenever a resource is added, removed, or
+ * changed in the resource pool.
+ */
+ void reevaluateStatus ();
+
+protected:
+ /**
+ * Construct best address out of
+ * eventually preselected resource
+ * (due to subscription) and best
+ * available resource.
+ */
+ XMPP::Jid bestAddress ();
+
+ /**
+ * This will simply cache all
+ * relevant data for this contact.
+ */
+ XMPP::RosterItem mRosterItem;
+
+private:
+ bool mDontSync;
+ JabberAccount *m_account;
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/jabberbookmarks.cpp b/kopete/protocols/jabber/jabberbookmarks.cpp
new file mode 100644
index 00000000..720705b2
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbookmarks.cpp
@@ -0,0 +1,149 @@
+ /*
+ Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#include "jabberbookmarks.h"
+#include "jabberaccount.h"
+
+#include <kopetecontact.h>
+
+
+#include <kdebug.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include "xmpp_tasks.h"
+
+
+JabberBookmarks::JabberBookmarks(JabberAccount *parent) : QObject(parent) , m_account(parent)
+{
+ connect( m_account , SIGNAL( isConnectedChanged() ) , this , SLOT( accountConnected() ) );
+}
+
+void JabberBookmarks::accountConnected()
+{
+ if(!m_account->isConnected())
+ return;
+
+ XMPP::JT_PrivateStorage * task = new XMPP::JT_PrivateStorage ( m_account->client()->rootTask ());
+ task->get( "storage" , "storage:bookmarks" );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotReceivedBookmarks() ) );
+ task->go ( true );
+}
+
+void JabberBookmarks::slotReceivedBookmarks( )
+{
+ XMPP::JT_PrivateStorage * task = (XMPP::JT_PrivateStorage*)(sender());
+ m_storage=QDomDocument("storage");
+ m_conferencesJID.clear();
+ if(task->success())
+ {
+ QDomElement storage_e=task->element();
+ if(!storage_e.isNull() && storage_e.tagName() == "storage")
+ {
+ storage_e=m_storage.importNode(storage_e,true).toElement();
+ m_storage.appendChild(storage_e);
+
+ for(QDomNode n = storage_e.firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == "conference")
+ {
+ QString jid=i.attribute("jid");
+ QString password;
+ for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement e = n.toElement();
+ if(e.isNull())
+ continue;
+ else if(e.tagName() == "nick")
+ jid+="/"+e.text();
+ else if(e.tagName() == "password")
+ password=e.text();
+
+ }
+ m_conferencesJID += jid;
+ if(i.attribute("autojoin") == "true")
+ {
+ XMPP::Jid x_jid(jid);
+ QString nick=x_jid.resource();
+ if(nick.isEmpty())
+ nick=m_account->myself()->nickName();
+
+ if(password.isEmpty())
+ m_account->client()->joinGroupChat(x_jid.host() , x_jid.user() , nick );
+ else
+ m_account->client()->joinGroupChat(x_jid.host() , x_jid.user() , nick , password);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void JabberBookmarks::insertGroupChat(const XMPP::Jid &jid)
+{
+ if(m_conferencesJID.contains(jid.full()) || !m_account->isConnected())
+ {
+ return;
+ }
+
+ QDomElement storage_e=m_storage.documentElement();
+ if(storage_e.isNull())
+ {
+ storage_e=m_storage.createElement("storage");
+ m_storage.appendChild(storage_e);
+ storage_e.setAttribute("xmlns","storage:bookmarks");
+ }
+
+ QDomElement conference=m_storage.createElement("conference");
+ storage_e.appendChild(conference);
+ conference.setAttribute("jid",jid.userHost());
+ QDomElement nick=m_storage.createElement("nick");
+ conference.appendChild(nick);
+ nick.appendChild(m_storage.createTextNode(jid.resource()));
+ QDomElement name=m_storage.createElement("name");
+ conference.appendChild(name);
+ name.appendChild(m_storage.createTextNode(jid.full()));
+
+
+ XMPP::JT_PrivateStorage * task = new XMPP::JT_PrivateStorage ( m_account->client()->rootTask ());
+ task->set( storage_e );
+ task->go ( true );
+
+ m_conferencesJID += jid.full();
+}
+
+KAction * JabberBookmarks::bookmarksAction(QObject *parent)
+{
+ KSelectAction *groupchatBM = new KSelectAction( i18n("Groupchat bookmark") , "jabber_group" , 0 , parent , "actionBookMark" );
+ groupchatBM->setItems(m_conferencesJID);
+ QObject::connect(groupchatBM, SIGNAL(activated (const QString&)) , this, SLOT(slotJoinChatBookmark(const QString&)));
+ return groupchatBM;
+}
+
+void JabberBookmarks::slotJoinChatBookmark( const QString & _jid )
+{
+ if(!m_account->isConnected())
+ return;
+ XMPP::Jid jid(_jid);
+ m_account->client()->joinGroupChat( jid.host() , jid.user() , jid.resource() );
+}
+
+
+
+#include "jabberbookmarks.moc"
+
diff --git a/kopete/protocols/jabber/jabberbookmarks.h b/kopete/protocols/jabber/jabberbookmarks.h
new file mode 100644
index 00000000..826d15e2
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbookmarks.h
@@ -0,0 +1,68 @@
+ /*
+
+ Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef JABBERBOOKMARKS_H
+#define JABBERBOOKMARKS_H
+
+#include <qobject.h>
+#include <qdom.h>
+#include <qstringlist.h>
+
+namespace XMPP { class Jid; }
+class JabberAccount;
+class JabberProtocol;
+
+class KAction;
+
+/**
+ * This is a class that hanlde the bookmark collection (JEP-0048)
+ * There is one instance of that class by accounts.
+ * @author Olivier Goffart
+ */
+class JabberBookmarks : public QObject
+{
+ Q_OBJECT
+ public:
+ /**
+ * Constructor
+ */
+ JabberBookmarks(JabberAccount *parent);
+ ~JabberBookmarks(){}
+
+ /**
+ * update or create en entry with the given jid.
+ * the jid ressource is the nickname
+ */
+ void insertGroupChat(const XMPP::Jid &jid);
+
+ /**
+ * return an action that will be added in the jabber popup menu
+ */
+ KAction *bookmarksAction(QObject * parent);
+ private slots:
+ void accountConnected();
+ void slotReceivedBookmarks();
+ void slotJoinChatBookmark(const QString&);
+
+
+ private:
+ JabberAccount *m_account;
+ QDomDocument m_storage;
+ QStringList m_conferencesJID;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberbytestream.cpp b/kopete/protocols/jabber/jabberbytestream.cpp
new file mode 100644
index 00000000..2f0f5c80
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbytestream.cpp
@@ -0,0 +1,156 @@
+
+/***************************************************************************
+ jabberbytestream.cpp - Byte Stream for Jabber
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 <qobject.h>
+#include <kdebug.h>
+#include "jabberbytestream.h"
+#include <kbufferedsocket.h>
+#include <kresolver.h>
+#include "jabberprotocol.h"
+
+JabberByteStream::JabberByteStream ( QObject *parent, const char */*name*/ )
+ : ByteStream ( parent )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Instantiating new Jabber 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 JabberByteStream::connect ( QString host, QString service )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Connecting to " << host << ", service " << service << endl;
+
+ mClosing = false;
+
+ return socket()->connect ( host, service );
+
+}
+
+bool JabberByteStream::isOpen () const
+{
+
+ // determine if socket is open
+ return socket()->isOpen ();
+
+}
+
+void JabberByteStream::close ()
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Closing stream." << endl;
+
+ // close the socket and set flag that we are closing it ourselves
+ mClosing = true;
+ socket()->close();
+
+}
+
+int JabberByteStream::tryWrite ()
+{
+
+ // send all data from the buffers to the socket
+ QByteArray writeData = takeWrite();
+ socket()->writeBlock ( writeData.data (), writeData.size () );
+
+ return writeData.size ();
+
+}
+
+KNetwork::KBufferedSocket *JabberByteStream::socket () const
+{
+
+ return mSocket;
+
+}
+
+JabberByteStream::~JabberByteStream ()
+{
+
+ delete mSocket;
+
+}
+
+void JabberByteStream::slotConnected ()
+{
+
+ emit connected ();
+
+}
+
+void JabberByteStream::slotConnectionClosed ()
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Socket has been closed." << endl;
+
+ // depending on who closed the socket, emit different signals
+ if ( !mClosing )
+ {
+ emit connectionClosed ();
+ }
+ else
+ {
+ emit delayedCloseFinished ();
+ }
+
+ mClosing = false;
+
+}
+
+void JabberByteStream::slotReadyRead ()
+{
+
+ // stuff all available data into our buffers
+ QByteArray readBuffer ( socket()->bytesAvailable () );
+
+ socket()->readBlock ( readBuffer.data (), readBuffer.size () );
+
+ appendRead ( readBuffer );
+
+ emit readyRead ();
+
+}
+
+void JabberByteStream::slotBytesWritten ( int bytes )
+{
+
+ emit bytesWritten ( bytes );
+
+}
+
+void JabberByteStream::slotError ( int code )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Socket error " << code << endl;
+
+ emit error ( code );
+
+}
+
+#include "jabberbytestream.moc"
diff --git a/kopete/protocols/jabber/jabberbytestream.h b/kopete/protocols/jabber/jabberbytestream.h
new file mode 100644
index 00000000..97e1ceeb
--- /dev/null
+++ b/kopete/protocols/jabber/jabberbytestream.h
@@ -0,0 +1,65 @@
+
+/***************************************************************************
+ jabberbytestream.h - Byte Stream for Jabber
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 JABBERBYTESTREAM_H
+#define JABBERBYTESTREAM_H
+
+#include <bytestream.h>
+#include <kbufferedsocket.h>
+
+
+/**
+@author Kopete Developers
+*/
+class JabberByteStream : public ByteStream
+{
+
+Q_OBJECT
+
+public:
+ JabberByteStream ( QObject *parent = 0, const char *name = 0 );
+
+ ~JabberByteStream ();
+
+ 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/jabber/jabbercapabilitiesmanager.cpp b/kopete/protocols/jabber/jabbercapabilitiesmanager.cpp
new file mode 100644
index 00000000..e570d241
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercapabilitiesmanager.cpp
@@ -0,0 +1,656 @@
+ /*
+ jabbercapabilitiesmanager.cpp - Manage entity capabilities(JEP-0115).
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ Imported from caps.cpp from Psi:
+ Copyright (C) 2005 Remko Troncon
+
+ *************************************************************************
+ * *
+ * 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 "jabbercapabilitiesmanager.h"
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+#include <qpair.h>
+#include <qdom.h>
+#include <qtextstream.h>
+
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+#include <xmpp_tasks.h>
+
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+
+using namespace XMPP;
+
+//BEGIN Capabilities
+JabberCapabilitiesManager::Capabilities::Capabilities()
+{}
+
+JabberCapabilitiesManager::Capabilities::Capabilities(const QString& node, const QString& version, const QString& extensions)
+ : m_node(node), m_version(version), m_extensions(extensions)
+{}
+
+const QString& JabberCapabilitiesManager::Capabilities::node() const
+{
+ return m_node;
+}
+
+const QString& JabberCapabilitiesManager::Capabilities::version() const
+{
+ return m_version;
+}
+
+const QString& JabberCapabilitiesManager::Capabilities::extensions() const
+{
+ return m_extensions;
+}
+
+JabberCapabilitiesManager::CapabilitiesList JabberCapabilitiesManager::Capabilities::flatten() const
+{
+ CapabilitiesList capsList;
+ capsList.append( Capabilities(node(), version(), version()) );
+
+ QStringList extensionList = QStringList::split(" ",extensions());
+ QStringList::ConstIterator it, itEnd = extensionList.constEnd();
+ for(it = extensionList.constBegin(); it != itEnd; ++it)
+ {
+ capsList.append( Capabilities(node(),version(),*it) );
+ }
+
+ return capsList;
+}
+
+bool JabberCapabilitiesManager::Capabilities::operator==(const Capabilities &other) const
+{
+ return (node() == other.node() && version() == other.version() && extensions() == other.extensions());
+}
+
+bool JabberCapabilitiesManager::Capabilities::operator!=(const Capabilities &other) const
+{
+ return !((*this) == other);
+}
+
+bool JabberCapabilitiesManager::Capabilities::operator<(const Capabilities &other) const
+{
+ return (node() != other.node() ? node() < other.node() :
+ (version() != other.version() ? version() < other.version() :
+ extensions() < other.extensions()));
+}
+//END Capabilities
+
+//BEGIN CapabilitiesInformation
+JabberCapabilitiesManager::CapabilitiesInformation::CapabilitiesInformation()
+ : m_discovered(false), m_pendingRequests(0)
+{
+ updateLastSeen();
+}
+
+const QStringList& JabberCapabilitiesManager::CapabilitiesInformation::features() const
+{
+ return m_features;
+}
+
+const DiscoItem::Identities& JabberCapabilitiesManager::CapabilitiesInformation::identities() const
+{
+ return m_identities;
+}
+
+QStringList JabberCapabilitiesManager::CapabilitiesInformation::jids() const
+{
+ QStringList jids;
+
+ QValueList<QPair<QString,JabberAccount*> >::ConstIterator it = m_jids.constBegin(), itEnd = m_jids.constEnd();
+ for( ; it != itEnd; ++it)
+ {
+ QString jid( (*it).first );
+ if( !jids.contains(jid) )
+ jids.push_back(jid);
+ }
+
+ return jids;
+}
+
+bool JabberCapabilitiesManager::CapabilitiesInformation::discovered() const
+{
+ return m_discovered;
+}
+
+int JabberCapabilitiesManager::CapabilitiesInformation::pendingRequests() const
+{
+ return m_pendingRequests;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::reset()
+{
+ m_features.clear();
+ m_identities.clear();
+ m_discovered = false;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::removeAccount(JabberAccount *account)
+{
+ QValueList<QPair<QString,JabberAccount*> >::Iterator it = m_jids.begin();
+ while( it != m_jids.end() )
+ {
+ if( (*it).second == account)
+ {
+ QValueList<QPair<QString,JabberAccount*> >::Iterator otherIt = it;
+ it++;
+ m_jids.remove(otherIt);
+ }
+ else
+ {
+ it++;
+ }
+ }
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::addJid(const Jid& jid, JabberAccount* account)
+{
+ QPair<QString,JabberAccount*> jidAccountPair(jid.full(),account);
+
+ if( !m_jids.contains(jidAccountPair) )
+ {
+ m_jids.push_back(jidAccountPair);
+ updateLastSeen();
+ }
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::removeJid(const Jid& jid)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Unregistering " << QString(jid.full()).replace('%',"%%") << endl;
+
+ QValueList<QPair<QString,JabberAccount*> >::Iterator it = m_jids.begin();
+ while( it != m_jids.end() )
+ {
+ if( (*it).first == jid.full() )
+ {
+ QValueList<QPair<QString,JabberAccount*> >::Iterator otherIt = it;
+ it++;
+ m_jids.remove(otherIt);
+ }
+ else
+ {
+ it++;
+ }
+ }
+}
+
+QPair<Jid,JabberAccount*> JabberCapabilitiesManager::CapabilitiesInformation::nextJid(const Jid& jid, const Task* t)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Looking for next JID" << endl;
+
+ QValueList<QPair<QString,JabberAccount*> >::ConstIterator it = m_jids.constBegin(), itEnd = m_jids.constEnd();
+ for( ; it != itEnd; ++it)
+ {
+ if( (*it).first == jid.full() && (*it).second->client()->rootTask() == t)
+ {
+ it++;
+ if (it == itEnd)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No more JIDs" << endl;
+
+ return QPair<Jid,JabberAccount*>(Jid(),0L);
+ }
+ else if( (*it).second->isConnected() )
+ {
+ //qDebug("caps.cpp: Account isn't active");
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Account isn't connected." << endl;
+
+ return QPair<Jid,JabberAccount*>( (*it).first,(*it).second );
+ }
+ }
+ }
+ return QPair<Jid,JabberAccount*>(Jid(),0L);
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::setDiscovered(bool value)
+{
+ m_discovered = value;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::setPendingRequests(int pendingRequests)
+{
+ m_pendingRequests = pendingRequests;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::setIdentities(const DiscoItem::Identities& identities)
+{
+ m_identities = identities;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::setFeatures(const QStringList& featureList)
+{
+ m_features = featureList;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::updateLastSeen()
+{
+ m_lastSeen = QDate::currentDate();
+}
+
+QDomElement JabberCapabilitiesManager::CapabilitiesInformation::toXml(QDomDocument *doc) const
+{
+ QDomElement info = doc->createElement("info");
+ //info.setAttribute("last-seen",lastSeen_.toString(Qt::ISODate));
+
+ // Identities
+ DiscoItem::Identities::ConstIterator discoIt = m_identities.constBegin(), discoItEnd = m_identities.constEnd();
+ for( ; discoIt != discoItEnd; ++discoIt )
+ {
+ QDomElement identity = doc->createElement("identity");
+ identity.setAttribute("category",(*discoIt).category);
+ identity.setAttribute("name",(*discoIt).name);
+ identity.setAttribute("type",(*discoIt).type);
+ info.appendChild(identity);
+ }
+
+ // Features
+ QStringList::ConstIterator featuresIt = m_features.constBegin(), featuresItEnd = m_features.constEnd();
+ for( ; featuresIt != featuresItEnd; ++featuresIt )
+ {
+ QDomElement feature = doc->createElement("feature");
+ feature.setAttribute("node",*featuresIt);
+ info.appendChild(feature);
+ }
+
+ return info;
+}
+
+void JabberCapabilitiesManager::CapabilitiesInformation::fromXml(const QDomElement &element)
+{
+ if( element.tagName() != "info")
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Invalid info element" << endl;
+ return;
+ }
+
+ //if (!e.attribute("last-seen").isEmpty())
+ // lastSeen_ = QDate::fromString(e.attribute("last-seen"),Qt::ISODate);
+
+ for(QDomNode node = element.firstChild(); !node.isNull(); node = node.nextSibling())
+ {
+ QDomElement infoElement = node.toElement();
+ if( infoElement.isNull() )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Null element" << endl;
+ continue;
+ }
+
+ if( infoElement.tagName() == "identity")
+ {
+ DiscoItem::Identity id;
+ id.category = infoElement.attribute("category");
+ id.name = infoElement.attribute("name");
+ id.type = infoElement.attribute("type");
+ m_identities += id;
+ }
+ else if( infoElement.tagName() == "feature" )
+ {
+ m_features += infoElement.attribute("node");
+ }
+ else
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Unknown element" << endl;
+ }
+
+ m_discovered = true;
+ }
+}
+//END CapabilitiesInformation
+
+//BEGIN Private(d-ptr)
+class JabberCapabilitiesManager::Private
+{
+public:
+ Private()
+ {}
+
+ // Map a full jid to a capabilities
+ QMap<QString,Capabilities> jidCapabilitiesMap;
+ // Map a capabilities to its detail information
+ QMap<Capabilities,CapabilitiesInformation> capabilitiesInformationMap;
+};
+//END Private(d-ptr)
+
+JabberCapabilitiesManager::JabberCapabilitiesManager()
+ : d(new Private)
+{
+}
+
+JabberCapabilitiesManager::~JabberCapabilitiesManager()
+{
+ saveInformation();
+ delete d;
+}
+
+void JabberCapabilitiesManager::removeAccount(JabberAccount *account)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing account " << account->accountId() << endl;
+
+ QValueList<CapabilitiesInformation> info = d->capabilitiesInformationMap.values();
+
+ QValueList<CapabilitiesInformation>::Iterator it, itEnd = info.end();
+ for(it = info.begin(); it != info.end(); ++it)
+ {
+ (*it).removeAccount(account);
+ }
+}
+
+void JabberCapabilitiesManager::updateCapabilities(JabberAccount *account, const XMPP::Jid &jid, const XMPP::Status &status )
+{
+ if( !account->client() || !account->client()->rootTask() )
+ return;
+
+
+ // Do don't anything if the jid correspond to the account's JabberClient jid.
+ // false means that we don't check for resources.
+ if( jid.compare(account->client()->jid(), false) )
+ return;
+
+ QString node = status.capsNode(), version = status.capsVersion(), extensions = status.capsExt();
+ Capabilities capabilities( node, version, extensions );
+
+ // Check if the capabilities was really updated(i.e the content is different)
+ if( d->jidCapabilitiesMap[jid.full()] != capabilities)
+ {
+ // Unregister from all old caps nodes
+ // FIXME: We should only unregister & register from changed nodes
+ CapabilitiesList oldCaps = d->jidCapabilitiesMap[jid.full()].flatten();
+ CapabilitiesList::Iterator oldCapsIt = oldCaps.begin(), oldCapsItEnd = oldCaps.end();
+ for( ; oldCapsIt != oldCapsItEnd; ++oldCapsIt)
+ {
+ if( (*oldCapsIt) != Capabilities() )
+ {
+ d->capabilitiesInformationMap[*oldCapsIt].removeJid(jid);
+ }
+ }
+
+ // Check if the jid has caps in his presence message.
+ if( !status.capsNode().isEmpty() && !status.capsVersion().isEmpty() )
+ {
+ // Register with all new caps nodes
+ d->jidCapabilitiesMap[jid.full()] = capabilities;
+ CapabilitiesList caps = capabilities.flatten();
+ CapabilitiesList::Iterator newCapsIt = caps.begin(), newCapsItEnd = caps.end();
+ for( ; newCapsIt != newCapsItEnd; ++newCapsIt )
+ {
+ d->capabilitiesInformationMap[*newCapsIt].addJid(jid,account);
+ }
+
+ emit capabilitiesChanged(jid);
+
+ // Register new caps and check if we need to discover features
+ newCapsIt = caps.begin();
+ for( ; newCapsIt != newCapsItEnd; ++newCapsIt )
+ {
+ if( !d->capabilitiesInformationMap[*newCapsIt].discovered() && d->capabilitiesInformationMap[*newCapsIt].pendingRequests() == 0 )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << QString("Sending disco request to %1, node=%2").arg(QString(jid.full()).replace('%',"%%")).arg(node + "#" + (*newCapsIt).extensions()) << endl;
+
+ d->capabilitiesInformationMap[*newCapsIt].setPendingRequests(1);
+ requestDiscoInfo(account, jid, node + "#" + (*newCapsIt).extensions());
+ }
+ }
+ }
+ else
+ {
+ // Remove all caps specifications
+ kdDebug(JABBER_DEBUG_GLOBAL) << QString("Illegal caps info from %1: node=%2, ver=%3").arg(QString(jid.full()).replace('%',"%%")).arg(node).arg(version) << endl;
+
+ d->jidCapabilitiesMap.remove( jid.full() );
+ }
+ }
+ else
+ {
+ // Add to the list of jids
+ CapabilitiesList caps = capabilities.flatten();
+ CapabilitiesList::Iterator capsIt = caps.begin(), capsItEnd = caps.end();
+ for( ; capsIt != capsItEnd; ++capsIt)
+ {
+ d->capabilitiesInformationMap[*capsIt].addJid(jid,account);
+ }
+ }
+}
+
+void JabberCapabilitiesManager::requestDiscoInfo(JabberAccount *account, const Jid& jid, const QString& node)
+{
+ if( !account->client()->rootTask() )
+ return;
+
+ JT_DiscoInfo *discoInfo = new JT_DiscoInfo(account->client()->rootTask());
+ connect(discoInfo, SIGNAL(finished()), SLOT(discoRequestFinished()));
+ discoInfo->get(jid, node);
+ //pending_++;
+ //timer_.start(REQUEST_TIMEOUT,true);
+ discoInfo->go(true);
+}
+
+void JabberCapabilitiesManager::discoRequestFinished()
+{
+ JT_DiscoInfo *discoInfo = (JT_DiscoInfo*)sender();
+ if (!discoInfo)
+ return;
+
+ DiscoItem item = discoInfo->item();
+ Jid jid = discoInfo->jid();
+ kdDebug(JABBER_DEBUG_GLOBAL) << QString("Disco response from %1, node=%2, success=%3").arg(QString(jid.full()).replace('%',"%%")).arg(discoInfo->node()).arg(discoInfo->success()) << endl;
+
+ QStringList tokens = QStringList::split("#",discoInfo->node());
+
+ // Update features
+ Q_ASSERT(tokens.count() == 2);
+ QString node = tokens[0];
+ QString extensions = tokens[1];
+
+ Capabilities jidCapabilities = d->jidCapabilitiesMap[jid.full()];
+ if( jidCapabilities.node() == node )
+ {
+ Capabilities capabilities(node, jidCapabilities.version(), extensions);
+
+ if( discoInfo->success() )
+ {
+ // Save identities & features
+ d->capabilitiesInformationMap[capabilities].setIdentities(item.identities());
+ d->capabilitiesInformationMap[capabilities].setFeatures(item.features().list());
+ d->capabilitiesInformationMap[capabilities].setPendingRequests(0);
+ d->capabilitiesInformationMap[capabilities].setDiscovered(true);
+
+ // Save(Cache) information
+ saveInformation();
+
+ // Notify affected jids.
+ QStringList jids = d->capabilitiesInformationMap[capabilities].jids();
+ QStringList::ConstIterator jidsIt = jids.constBegin(), jidsItEnd = jids.constEnd();
+ for( ; jidsIt != jidsItEnd; ++jidsItEnd )
+ {
+ emit capabilitiesChanged(*jidsIt);
+ }
+ }
+ else
+ {
+ QPair<Jid,JabberAccount*> jidAccountPair = d->capabilitiesInformationMap[capabilities].nextJid(jid,discoInfo->parent());
+ if( jidAccountPair.second )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << QString("Falling back on %1.").arg(QString(jidAccountPair.first.full()).replace('%',"%%")) << endl;
+ requestDiscoInfo( jidAccountPair.second, jidAccountPair.first, discoInfo->node() );
+ }
+ else
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << "No valid disco request avalable." << endl;
+ d->capabilitiesInformationMap[capabilities].setPendingRequests(0);
+ }
+ }
+ }
+ else
+ kdDebug(JABBER_DEBUG_GLOBAL) << QString("Current client node '%1' does not match response '%2'").arg(jidCapabilities.node()).arg(node) << endl;
+
+ //for (unsigned int i = 0; i < item.features().list().count(); i++)
+ // printf(" Feature: %s\n",item.features().list()[i].latin1());
+
+ // Check pending requests
+// pending_ = (pending_ > 0 ? pending_-1 : 0);
+// if (!pending_) {
+// timer_.stop();
+// updatePendingJIDs();
+// }
+}
+
+void JabberCapabilitiesManager::loadCachedInformation()
+{
+ QString capsFileName;
+ capsFileName = locateLocal("appdata", QString::fromUtf8("jabber-capabilities-cache.xml"));
+
+ // Load settings
+ QDomDocument doc;
+ QFile cacheFile(capsFileName);
+ if( !cacheFile.open(IO_ReadOnly) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Could not open the Capabilities cache from disk." << endl;
+ return;
+ }
+ if( !doc.setContent(&cacheFile) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Could not set the Capabilities cache from file." << endl;
+ return;
+ }
+ cacheFile.close();
+
+ QDomElement caps = doc.documentElement();
+ if( caps.tagName() != "capabilities" )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Invalid capabilities element." << endl;
+ return;
+ }
+
+ QDomNode node;
+ for(node = caps.firstChild(); !node.isNull(); node = node.nextSibling())
+ {
+ QDomElement element = node.toElement();
+ if( element.isNull() )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Found a null element." << endl;
+ continue;
+ }
+
+ if( element.tagName() == "info" )
+ {
+ CapabilitiesInformation info;
+ info.fromXml(element);
+ Capabilities entityCaps( element.attribute("node"),element.attribute("ver"),element.attribute("ext") );
+ d->capabilitiesInformationMap[entityCaps] = info;
+ }
+ else
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Unknow element" << endl;
+ }
+ }
+}
+
+bool JabberCapabilitiesManager::capabilitiesEnabled(const Jid &jid) const
+{
+ return d->jidCapabilitiesMap.contains( jid.full() );
+}
+
+XMPP::Features JabberCapabilitiesManager::features(const Jid& jid) const
+{
+ QStringList featuresList;
+
+ if( capabilitiesEnabled(jid) )
+ {
+ CapabilitiesList capabilitiesList = d->jidCapabilitiesMap[jid.full()].flatten();
+ CapabilitiesList::ConstIterator capsIt = capabilitiesList.constBegin(), capsItEnd = capabilitiesList.constEnd();
+ for( ; capsIt != capsItEnd; ++capsIt)
+ {
+ featuresList += d->capabilitiesInformationMap[*capsIt].features();
+ }
+ }
+
+ return Features(featuresList);
+}
+
+QString JabberCapabilitiesManager::clientName(const Jid& jid) const
+{
+ if( capabilitiesEnabled(jid) )
+ {
+ Capabilities caps = d->jidCapabilitiesMap[jid.full()];
+ QString name = d->capabilitiesInformationMap[Capabilities(caps.node(),caps.version(),caps.version())].identities().first().name;
+
+ // Try to be intelligent about the name
+ /*if (name.isEmpty()) {
+ name = cs.node();
+ if (name.startsWith("http://"))
+ name = name.right(name.length() - 7);
+
+ if (name.startsWith("www."))
+ name = name.right(name.length() - 4);
+
+ int cut_pos = name.find(".");
+ if (cut_pos != -1) {
+ name = name.left(cut_pos);
+ }
+ }*/
+
+ return name;
+ }
+ else
+ {
+ return QString();
+ }
+}
+
+QString JabberCapabilitiesManager::clientVersion(const Jid& jid) const
+{
+ return (capabilitiesEnabled(jid) ? d->jidCapabilitiesMap[jid.full()].version() : QString());
+}
+
+void JabberCapabilitiesManager::saveInformation()
+{
+ QString capsFileName;
+ capsFileName = locateLocal("appdata", QString::fromUtf8("jabber-capabilities-cache.xml"));
+
+ // Generate XML
+ QDomDocument doc;
+ QDomElement capabilities = doc.createElement("capabilities");
+ doc.appendChild(capabilities);
+ QMap<Capabilities,CapabilitiesInformation>::ConstIterator it = d->capabilitiesInformationMap.constBegin(), itEnd = d->capabilitiesInformationMap.constEnd();
+ for( ; it != itEnd; ++it )
+ {
+ QDomElement info = it.data().toXml(&doc);
+ info.setAttribute("node",it.key().node());
+ info.setAttribute("ver",it.key().version());
+ info.setAttribute("ext",it.key().extensions());
+ capabilities.appendChild(info);
+ }
+
+ // Save
+ QFile capsFile(capsFileName);
+ if( !capsFile.open(IO_WriteOnly) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Error while opening Capabilities cache file." << endl;
+ return;
+ }
+
+ QTextStream textStream;
+ textStream.setDevice(&capsFile);
+ textStream.setEncoding(QTextStream::UnicodeUTF8);
+ textStream << doc.toString();
+ textStream.unsetDevice();
+ capsFile.close();
+}
+
+#include "jabbercapabilitiesmanager.moc"
diff --git a/kopete/protocols/jabber/jabbercapabilitiesmanager.h b/kopete/protocols/jabber/jabbercapabilitiesmanager.h
new file mode 100644
index 00000000..3010479f
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercapabilitiesmanager.h
@@ -0,0 +1,211 @@
+ /*
+ jabbercapabilitiesmanager.h - Manage entity capabilities(JEP-0115) pool.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ Imported from caps.cpp from Psi:
+ Copyright (C) 2005 Remko Troncon
+
+ *************************************************************************
+ * *
+ * 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 JABBERCAPABILITIESMANAGER_H
+#define JABBERCAPABILITIESMANAGER_H
+
+#include <qobject.h>
+#include <im.h>
+#include <xmpp.h>
+
+using namespace XMPP;
+
+class JabberAccount;
+
+/**
+ * @brief Manage Jabber entity capabilities (JEP-0115)
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ * @author Remko Troncon
+ */
+class JabberCapabilitiesManager : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct
+ */
+ JabberCapabilitiesManager();
+ ~JabberCapabilitiesManager();
+
+ /**
+ * Load cached information from local file.
+ */
+ void loadCachedInformation();
+
+ /**
+ * Check if the jid support Entity capabitilies.
+ * @param jid JID to check.
+ * @return true if the jid support entity capabitilies.
+ */
+ bool capabilitiesEnabled(const Jid& jid) const;
+
+ /**
+ * Remove account from manager.
+ */
+ void removeAccount(JabberAccount *account);
+
+ /**
+ * Return the features supported for the JID.
+ */
+ XMPP::Features features(const Jid& jid) const;
+ /**
+ * Return the client name for the current JID.
+ */
+ QString clientName(const Jid& jid) const;
+ /**
+ * Return the client version for the current JID.
+ */
+ QString clientVersion(const Jid& jid) const;
+
+signals:
+ void capabilitiesChanged(const XMPP::Jid &jid);
+
+public slots:
+ /**
+ * Update if necessary the capabities for the JID passed in args.
+ * Caps are received in Presence messages so that's why we are
+ * passing a XMPP::Status object.
+ *
+ * @param jid JID that capabilities was updated.
+ * @param status The XMPP::Status that contain the caps.
+ */
+ void updateCapabilities(JabberAccount *account, const XMPP::Jid &jid, const XMPP::Status &status);
+
+private slots:
+ /**
+ * @brief Called when a reply to disco#info request was received.
+ * If the result was succesful, the resulting features are recorded in the
+ * features database for the requested node, and all the affected jids are
+ * put in the queue for update notification.
+ * If the result was unsuccesful, another jid with the same capabilities is
+ * selected and sent a disco#info query.
+ */
+ void discoRequestFinished();
+
+private:
+ /**
+ * @brief Sends a disco#info request to a given node of a jid through an account.
+ * When the request is finished, the discoRequestFinished() slot is called.
+ *
+ * @param account The account through which to send the disco request.
+ * @param jid The target entity's JID
+ * @param node The target disco#info node
+ */
+ void requestDiscoInfo(JabberAccount *account, const Jid& jid, const QString& node);
+
+ /**
+ * Save capabilities information to disk.
+ */
+ void saveInformation();
+
+ class Capabilities;
+ typedef QValueList<Capabilities> CapabilitiesList;
+ /**
+ * @brief A class representing an entity capability specification.
+ * An entity capability is a combination of a node, a version, and a set of
+ * extensions.
+ */
+ class Capabilities
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ Capabilities();
+ /**
+ * Define capabilities.
+ * @param node the node
+ * @param version the version
+ * @param extensions the list of extensions (separated by spaces)
+ */
+ Capabilities(const QString &node, const QString &version, const QString &extensions);
+ /**
+ * Returns the node of the capabilities specification.
+ */
+ const QString& node() const;
+ /**
+ * @brief Returns the version of the capabilities specification.
+ */
+ const QString& version() const;
+ /**
+ * @brief Returns the extensions of the capabilities specification.
+ */
+ const QString& extensions() const;
+ /**
+ * \brief Flattens the caps specification into the set of 'simple' specifications.
+ * A 'simple' specification is a specification with exactly one extension,
+ * or with the version number as the extension.
+ *
+ * Example: A caps specification with node=http://psi-im.org, version=0.10,
+ * and ext='achat vchat' would be expanded into the following list of specs:
+ * node=http://psi-im.org, ver=0.10, ext=0.10
+ * node=http://psi-im.org, ver=0.10, ext=achat
+ * node=http://psi-im.org, ver=0.10, ext=vchat
+ */
+ CapabilitiesList flatten() const;
+
+ bool operator==(const Capabilities&) const;
+ bool operator!=(const Capabilities&) const;
+ bool operator<(const Capabilities&) const;
+
+ private:
+ QString m_node, m_version, m_extensions;
+ };
+
+ class CapabilitiesInformation
+ {
+ public:
+ CapabilitiesInformation();
+ const QStringList& features() const;
+ const DiscoItem::Identities& identities() const;
+ QStringList jids() const;
+ bool discovered() const;
+ int pendingRequests() const;
+
+ void reset();
+ void removeAccount(JabberAccount* acc);
+ void removeJid(const Jid&);
+ void addJid(const Jid&, JabberAccount*);
+ QPair<Jid,JabberAccount*> nextJid(const Jid&, const Task*);
+
+ void setDiscovered(bool);
+ void setPendingRequests(int);
+ void setIdentities(const DiscoItem::Identities&);
+ void setFeatures(const QStringList&);
+
+ QDomElement toXml(QDomDocument *) const;
+ void fromXml(const QDomElement&);
+
+ protected:
+ void updateLastSeen();
+
+ private:
+ bool m_discovered;
+ int m_pendingRequests;
+ QStringList m_features;
+ DiscoItem::Identities m_identities;
+ QValueList<QPair<QString,JabberAccount*> > m_jids;
+ QDate m_lastSeen;
+ };
+
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberchatsession.cpp b/kopete/protocols/jabber/jabberchatsession.cpp
new file mode 100644
index 00000000..faa6f950
--- /dev/null
+++ b/kopete/protocols/jabber/jabberchatsession.cpp
@@ -0,0 +1,357 @@
+/*
+ jabberchatsession.cpp - Jabber Chat Session
+
+ Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "jabberchatsession.h"
+
+#include <qptrlist.h>
+#include <qlabel.h>
+#include <qimage.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qiconset.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include "kopetechatsessionmanager.h"
+#include "kopetemessage.h"
+#include "kopeteviewplugin.h"
+#include "kopeteview.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabbercontact.h"
+#include "jabberresource.h"
+#include "jabberresourcepool.h"
+#include "kioslave/jabberdisco.h"
+
+
+JabberChatSession::JabberChatSession ( JabberProtocol *protocol, const JabberBaseContact *user,
+ Kopete::ContactPtrList others, const QString &resource, const char *name )
+ : Kopete::ChatSession ( user, others, protocol, name )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "New message manager for " << user->contactId () << endl;
+
+ // make sure Kopete knows about this instance
+ Kopete::ChatSessionManager::self()->registerChatSession ( this );
+
+ connect ( this, SIGNAL ( messageSent ( Kopete::Message &, Kopete::ChatSession * ) ),
+ this, SLOT ( slotMessageSent ( Kopete::Message &, Kopete::ChatSession * ) ) );
+
+ connect ( this, SIGNAL ( myselfTyping ( bool ) ), this, SLOT ( slotSendTypingNotification ( bool ) ) );
+
+ connect ( this, SIGNAL ( onlineStatusChanged(Kopete::Contact*, const Kopete::OnlineStatus&, const Kopete::OnlineStatus& ) ), this, SLOT ( slotUpdateDisplayName () ) );
+
+ // check if the user ID contains a hardwired resource,
+ // we'll have to use that one in that case
+ XMPP::Jid jid = user->rosterItem().jid() ;
+
+ mResource = jid.resource().isEmpty () ? resource : jid.resource ();
+ slotUpdateDisplayName ();
+
+#ifdef SUPPORT_JINGLE
+ KAction *jabber_voicecall = new KAction( i18n("Voice call" ), "voicecall", 0, members().getFirst(), SLOT(voiceCall ()), actionCollection(), "jabber_voicecall" );
+
+ setInstance(protocol->instance());
+ jabber_voicecall->setEnabled( false );
+
+
+ Kopete::ContactPtrList chatMembers = members ();
+ if ( chatMembers.first () )
+ {
+ // Check if the current contact support Voice calls, also honour lock by default.
+ // FIXME: we should use the active ressource
+ JabberResource *bestResource = account()->resourcePool()-> bestJabberResource( static_cast<JabberBaseContact*>(chatMembers.first())->rosterItem().jid() );
+ if( bestResource && bestResource->features().canVoice() )
+ {
+ jabber_voicecall->setEnabled( true );
+ }
+ }
+
+#endif
+
+ new KAction( i18n( "Send File" ), "attach", 0, this, SLOT( slotSendFile() ), actionCollection(), "jabberSendFile" );
+
+ setXMLFile("jabberchatui.rc");
+
+}
+
+JabberChatSession::~JabberChatSession( )
+{
+ JabberAccount * a = dynamic_cast<JabberAccount *>(Kopete::ChatSession::account ());
+ if( !a ) //When closing kopete, the account is partially destroyed already, dynamic_cast return 0
+ return;
+ if ( a->configGroup()->readBoolEntry ("SendEvents", true) &&
+ a->configGroup()->readBoolEntry ("SendGoneEvent", true) )
+ sendNotification( XMPP::GoneEvent );
+}
+
+
+void JabberChatSession::slotUpdateDisplayName ()
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ Kopete::ContactPtrList chatMembers = members ();
+
+ // make sure we do have members in the chat
+ if ( !chatMembers.first () )
+ return;
+
+ XMPP::Jid jid = static_cast<JabberBaseContact*>(chatMembers.first())->rosterItem().jid();
+
+ if ( !mResource.isEmpty () )
+ jid.setResource ( mResource );
+
+ QString statusText = i18n("a contact's online status in parenthesis.", " (%1)")
+ .arg( chatMembers.first()->onlineStatus().description() );
+ if ( jid.resource().isEmpty () )
+ setDisplayName ( chatMembers.first()->metaContact()->displayName () + statusText );
+ else
+ setDisplayName ( chatMembers.first()->metaContact()->displayName () + "/" + jid.resource () + statusText );
+
+}
+
+const JabberBaseContact *JabberChatSession::user () const
+{
+
+ return static_cast<const JabberBaseContact *>(Kopete::ChatSession::myself());
+
+}
+
+JabberAccount *JabberChatSession::account () const
+{
+
+ return static_cast<JabberAccount *>(Kopete::ChatSession::account ());
+
+}
+
+const QString &JabberChatSession::resource () const
+{
+
+ return mResource;
+
+}
+
+void JabberChatSession::appendMessage ( Kopete::Message &msg, const QString &fromResource )
+{
+
+ mResource = fromResource;
+
+ slotUpdateDisplayName ();
+ Kopete::ChatSession::appendMessage ( msg );
+
+ // We send the notifications for Delivered and Displayed events. More granular management
+ // (ie.: send Displayed event when it is really displayed)
+ // of these events would require changes in the chatwindow API.
+
+ if ( account()->configGroup()->readBoolEntry ("SendEvents", true) )
+ {
+ if ( account()->configGroup()->readBoolEntry ("SendDeliveredEvent", true) )
+ {
+ sendNotification( XMPP::DeliveredEvent );
+ }
+
+ if ( account()->configGroup()->readBoolEntry ("SendDisplayedEvent", true) )
+ {
+ sendNotification( XMPP::DisplayedEvent );
+ }
+ }
+}
+
+void JabberChatSession::sendNotification( XMPP::MsgEvent event )
+{
+ if ( !account()->isConnected () )
+ return;
+
+ JabberContact *contact;
+ QPtrListIterator<Kopete::Contact> listIterator ( members () );
+
+ while ( ( contact = dynamic_cast<JabberContact*>( listIterator.current () ) ) != 0 )
+ {
+ ++listIterator;
+ if ( contact->isContactRequestingEvent( event ) )
+ {
+ // create JID for the recipient
+ XMPP::Jid toJid = contact->rosterItem().jid();
+
+ // set resource properly if it has been selected already
+ if ( !resource().isEmpty () )
+ toJid.setResource ( resource () );
+
+ XMPP::Message message;
+
+ message.setFrom ( account()->client()->jid() );
+ message.setTo ( toJid );
+ message.setEventId ( contact->lastReceivedMessageId () );
+ // store composing event depending on state
+ message.addEvent ( event );
+
+ if (view() && view()->plugin()->pluginId() == "kopete_emailwindow" )
+ {
+ message.setType ( "normal" );
+ }
+ else
+ {
+ message.setType ( "chat" );
+ }
+
+
+ // send message
+ account()->client()->sendMessage ( message );
+ }
+ }
+}
+
+void JabberChatSession::slotSendTypingNotification ( bool typing )
+{
+ if ( !account()->configGroup()->readBoolEntry ("SendEvents", true)
+ || !account()->configGroup()->readBoolEntry("SendComposingEvent", true) )
+ return;
+
+ // create JID for us as sender
+ XMPP::Jid fromJid = static_cast<const JabberBaseContact*>(myself())->rosterItem().jid();
+ fromJid.setResource ( account()->configGroup()->readEntry( "Resource", QString::null ) );
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Sending out typing notification (" << typing << ") to all chat members." << endl;
+
+ typing ? sendNotification( ComposingEvent ) : sendNotification( CancelEvent );
+}
+
+void JabberChatSession::slotMessageSent ( Kopete::Message &message, Kopete::ChatSession * )
+{
+
+ if( account()->isConnected () )
+ {
+ XMPP::Message jabberMessage;
+ JabberBaseContact *recipient = static_cast<JabberBaseContact*>(message.to().first());
+
+ jabberMessage.setFrom ( account()->client()->jid() );
+
+
+ XMPP::Jid toJid = recipient->rosterItem().jid();
+
+ if( !resource().isEmpty () )
+ toJid.setResource ( resource() );
+
+ jabberMessage.setTo ( toJid );
+
+ jabberMessage.setSubject ( message.subject () );
+ jabberMessage.setTimeStamp ( message.timestamp () );
+
+ if ( message.plainBody().find ( "-----BEGIN PGP MESSAGE-----" ) != -1 )
+ {
+ /*
+ * This message is encrypted, so we need to set
+ * a fake body indicating that this is an encrypted
+ * message (for clients not implementing this
+ * functionality) and then generate the encrypted
+ * payload out of the old message body.
+ */
+
+ // please don't translate the following string
+ jabberMessage.setBody ( i18n ( "This message is encrypted." ) );
+
+ QString encryptedBody = message.plainBody ();
+
+ // remove PGP header and footer from message
+ encryptedBody.truncate ( encryptedBody.length () - QString("-----END PGP MESSAGE-----").length () - 2 );
+ encryptedBody = encryptedBody.right ( encryptedBody.length () - encryptedBody.find ( "\n\n" ) - 2 );
+
+ // assign payload to message
+ jabberMessage.setXEncrypted ( encryptedBody );
+ }
+ else
+ {
+ // this message is not encrypted
+ jabberMessage.setBody ( message.plainBody ());
+ if (message.format() == Kopete::Message::RichText)
+ {
+ JabberResource *bestResource = account()->resourcePool()->bestJabberResource(toJid);
+ if( bestResource && bestResource->features().canXHTML() )
+ {
+ QString xhtmlBody = message.escapedBody();
+
+ // According to JEP-0071 8.9 it is only RECOMMANDED to replace \n with <br/>
+ // which mean that some implementation (gaim 2 beta) may still think that \n are linebreak.
+ // and considered the fact that KTextEditor generate a well indented XHTML, we need to remove all \n from it
+ // see Bug 121627
+ // Anyway, theses client that do like that are *WRONG* considreded the example of jep-71 where there are lot of
+ // linebreak that are not interpreted. - Olivier 2006-31-03
+ xhtmlBody.replace("\n","");
+
+ //&nbsp; is not a valid XML entity
+ xhtmlBody.replace("&nbsp;" , "&#160;");
+
+ xhtmlBody="<p "+ message.getHtmlStyleAttribute() +">"+ xhtmlBody +"</p>";
+ jabberMessage.setXHTMLBody ( xhtmlBody );
+ }
+ }
+ }
+
+ // determine type of the widget and set message type accordingly
+ // "kopete_emailwindow" is the default email Kopete::ViewPlugin. If other email plugins
+ // become available, either jabber will have to provide its own selector or libkopete will need
+ // a better way of categorising view plugins.
+
+ // FIXME: the view() is a speedy way to solve BUG:108389. A better solution is to be found
+ // but I don't want to introduce a new bug during the bug hunt ;-).
+ if (view() && view()->plugin()->pluginId() == "kopete_emailwindow" )
+ {
+ jabberMessage.setType ( "normal" );
+ }
+ else
+ {
+ jabberMessage.setType ( "chat" );
+ }
+
+ // add request for all notifications
+ jabberMessage.addEvent( OfflineEvent );
+ jabberMessage.addEvent( ComposingEvent );
+ jabberMessage.addEvent( DeliveredEvent );
+ jabberMessage.addEvent( DisplayedEvent );
+
+
+ // send the message
+ account()->client()->sendMessage ( jabberMessage );
+
+ // append the message to the manager
+ Kopete::ChatSession::appendMessage ( message );
+
+ // tell the manager that we sent successfully
+ messageSucceeded ();
+ }
+ else
+ {
+ account()->errorConnectFirst ();
+
+ // FIXME: there is no messageFailed() yet,
+ // but we need to stop the animation etc.
+ messageSucceeded ();
+ }
+
+}
+
+ void JabberChatSession::slotSendFile()
+ {
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<JabberContact *>(contacts.first())->sendFile();
+ }
+
+#include "jabberchatsession.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; replace-tabs off; space-indent off;
diff --git a/kopete/protocols/jabber/jabberchatsession.h b/kopete/protocols/jabber/jabberchatsession.h
new file mode 100644
index 00000000..66b4c63b
--- /dev/null
+++ b/kopete/protocols/jabber/jabberchatsession.h
@@ -0,0 +1,103 @@
+/*
+ jabbermessagemanager.h - Jabber Message Manager
+
+ Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef JABBERCHATSESSION_H
+#define JABBERCHATSESSION_H
+
+#include "kopetechatsession.h"
+
+#include "im.h"
+
+class JabberProtocol;
+class JabberAccount;
+class JabberBaseContact;
+namespace Kopete { class Message; }
+class QString;
+
+
+/**
+ * @author Till Gerken
+ */
+class JabberChatSession : public Kopete::ChatSession
+{
+ Q_OBJECT
+
+public:
+ JabberChatSession ( JabberProtocol *protocol, const JabberBaseContact *user,
+ Kopete::ContactPtrList others, const QString &resource = "",
+ const char *name = 0 );
+
+ ~JabberChatSession();
+
+ /**
+ * @brief Get the local user in the session
+ * @return the local user in the session, same as account()->myself()
+ */
+ const JabberBaseContact *user () const;
+
+ /**
+ * @brief get the account
+ * @return the account
+ */
+ JabberAccount *account() const ;
+
+ /**
+ * @brief Return the resource this manager is currently associated with.
+ * @return currently associated resource
+ */
+ const QString &resource () const;
+
+public slots:
+ /**
+ * Show a message to the chatwindow, or append it to the queue.
+ * This is the function protocols HAVE TO call for both incoming and outgoing messages
+ * if the message must be showed in the chatwindow
+ *
+ * This is an overloaded version of the original implementation which
+ * also accepts a resource the message originates from. The message manager
+ * will set its own resource to the resource the message was received from.
+ * See @ref JabberBaseContact::manager() about how to deal with instantiating
+ * new message managers for messages not originating from the same resource
+ * a manager already exists for.
+ */
+ void appendMessage ( Kopete::Message &msg, const QString &fromResource );
+
+private slots:
+ void slotSendTypingNotification ( bool typing );
+ void slotMessageSent ( Kopete::Message &message, Kopete::ChatSession *kmm );
+
+ /**
+ * Re-generate the display name
+ */
+ void slotUpdateDisplayName ();
+
+ void slotSendFile();
+
+private:
+ /**
+ * Send a notification (XMPP::MsgEvent) to the members of the chatsession.
+ * SlotSendTypingNotification uses it.
+ */
+ void sendNotification( XMPP::MsgEvent event );
+
+ QString mResource;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/jabber/jabberchatui.rc b/kopete/protocols/jabber/jabberchatui.rc
new file mode 100644
index 00000000..9db9abe2
--- /dev/null
+++ b/kopete/protocols/jabber/jabberchatui.rc
@@ -0,0 +1,19 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="11" name="kopete_jabber_chat">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="jabber_voicecall" />
+ <Action name="jabberSendFile" />
+ </Menu>
+ </MenuBar>
+
+
+ <ToolBar name="statusToolBar">
+ <Action name="jabberDisplayPicture" />
+ <Action name="jabber_voicecall" />
+ <Action name="jabberSendFile" />
+ </ToolBar>
+
+
+</kpartgui> \ No newline at end of file
diff --git a/kopete/protocols/jabber/jabberclient.cpp b/kopete/protocols/jabber/jabberclient.cpp
new file mode 100644
index 00000000..b8e05d48
--- /dev/null
+++ b/kopete/protocols/jabber/jabberclient.cpp
@@ -0,0 +1,1137 @@
+
+/***************************************************************************
+ jabberclient.cpp - Generic Jabber Client Class
+ -------------------
+ begin : Sat May 25 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+ (C) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (C) 2001-2006 Kopete developers
+ <kopete-devel@kde.org>.
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "jabberclient.h"
+
+#include <qtimer.h>
+#include <qregexp.h>
+
+#include <qca.h>
+#include <bsocket.h>
+#include <filetransfer.h>
+#include <xmpp_tasks.h>
+
+#include "jabberconnector.h"
+
+#define JABBER_PENALTY_TIME 2
+
+class JabberClient::Private
+{
+public:
+ Private()
+ : jabberClient(0L), jabberClientStream(0L), jabberClientConnector(0L), jabberTLS(0L), jabberTLSHandler(0L)
+ {}
+ ~Private()
+ {
+ if ( jabberClient )
+ {
+ jabberClient->close ();
+ }
+
+ delete jabberClient;
+ delete jabberClientStream;
+ delete jabberClientConnector;
+ delete jabberTLSHandler;
+ delete jabberTLS;
+ }
+
+ // connection details
+ XMPP::Jid jid;
+ QString password;
+
+ // XMPP backend
+ XMPP::Client *jabberClient;
+ XMPP::ClientStream *jabberClientStream;
+ JabberConnector *jabberClientConnector;
+ QCA::TLS *jabberTLS;
+ XMPP::QCATLSHandler *jabberTLSHandler;
+
+ // ignore TLS warnings
+ bool ignoreTLSWarnings;
+
+ // current S5B server instance
+ static XMPP::S5BServer *s5bServer;
+ // address list being handled by the S5B server instance
+ static QStringList s5bAddressList;
+ // port of S5B server
+ static int s5bServerPort;
+
+ // local IP address
+ QString localAddress;
+
+ // whether TLS (or direct SSL in case of the old protocol) should be used
+ bool forceTLS;
+
+ // whether direct SSL connections should be used
+ bool useSSL;
+
+ // use XMPP 1.0 or the older protocol version
+ bool useXMPP09;
+
+ // whether SSL support should be probed in case the old protocol is used
+ bool probeSSL;
+
+ // override the default server name and port (only pre-XMPP 1.0)
+ bool overrideHost;
+ QString server;
+ int port;
+
+ // allow transmission of plaintext passwords
+ bool allowPlainTextPassword;
+
+ // enable file transfers
+ bool fileTransfersEnabled;
+
+ // current penalty time
+ int currentPenaltyTime;
+
+ // client information
+ QString clientName, clientVersion, osName;
+
+ // timezone information
+ QString timeZoneName;
+ int timeZoneOffset;
+
+ // Caps(JEP-0115: Entity Capabilities) information
+ QString capsNode, capsVersion;
+ DiscoItem::Identity discoIdentity;
+};
+
+XMPP::S5BServer *JabberClient::Private::s5bServer = 0L;
+QStringList JabberClient::Private::s5bAddressList;
+int JabberClient::Private::s5bServerPort = 8010;
+
+JabberClient::JabberClient ()
+{
+ d = new Private();
+
+ cleanUp ();
+
+ // initiate penalty timer
+ QTimer::singleShot ( JABBER_PENALTY_TIME * 1000, this, SLOT ( slotUpdatePenaltyTime () ) );
+
+}
+
+JabberClient::~JabberClient ()
+{
+ delete d;
+}
+
+void JabberClient::cleanUp ()
+{
+ if ( d->jabberClient )
+ {
+ d->jabberClient->close ();
+ }
+
+ delete d->jabberClient;
+ delete d->jabberClientStream;
+ delete d->jabberClientConnector;
+ delete d->jabberTLSHandler;
+ delete d->jabberTLS;
+
+ d->jabberClient = 0L;
+ d->jabberClientStream = 0L;
+ d->jabberClientConnector = 0L;
+ d->jabberTLSHandler = 0L;
+ d->jabberTLS = 0L;
+
+ d->currentPenaltyTime = 0;
+
+ d->jid = XMPP::Jid ();
+ d->password = QString::null;
+
+ setForceTLS ( false );
+ setUseSSL ( false );
+ setUseXMPP09 ( false );
+ setProbeSSL ( false );
+
+ setOverrideHost ( false );
+
+ setAllowPlainTextPassword ( true );
+
+ setFileTransfersEnabled ( false );
+ setS5BServerPort ( 8010 );
+
+ setClientName ( QString::null );
+ setClientVersion ( QString::null );
+ setOSName ( QString::null );
+
+ setTimeZone ( "UTC", 0 );
+
+ setIgnoreTLSWarnings ( false );
+
+}
+
+void JabberClient::slotUpdatePenaltyTime ()
+{
+
+ if ( d->currentPenaltyTime >= JABBER_PENALTY_TIME )
+ d->currentPenaltyTime -= JABBER_PENALTY_TIME;
+ else
+ d->currentPenaltyTime = 0;
+
+ QTimer::singleShot ( JABBER_PENALTY_TIME * 1000, this, SLOT ( slotUpdatePenaltyTime () ) );
+
+}
+
+void JabberClient::setIgnoreTLSWarnings ( bool flag )
+{
+
+ d->ignoreTLSWarnings = flag;
+
+}
+
+bool JabberClient::ignoreTLSWarnings ()
+{
+
+ return d->ignoreTLSWarnings;
+
+}
+
+bool JabberClient::setS5BServerPort ( int port )
+{
+
+ d->s5bServerPort = port;
+
+ if ( fileTransfersEnabled () )
+ {
+ return s5bServer()->start ( port );
+ }
+
+ return true;
+
+}
+
+int JabberClient::s5bServerPort () const
+{
+
+ return d->s5bServerPort;
+
+}
+
+XMPP::S5BServer *JabberClient::s5bServer ()
+{
+
+ if ( !d->s5bServer )
+ {
+ d->s5bServer = new XMPP::S5BServer ();
+ QObject::connect ( d->s5bServer, SIGNAL ( destroyed () ), this, SLOT ( slotS5BServerGone () ) );
+
+ /*
+ * Try to start the server at the default port here.
+ * We have no way of notifying the caller of an error.
+ * However, since the caller will usually also
+ * use setS5BServerPort() to ensure the correct
+ * port, we can return an error code there.
+ */
+ if ( fileTransfersEnabled () )
+ {
+ s5bServer()->start ( d->s5bServerPort );
+ }
+ }
+
+ return d->s5bServer;
+
+}
+
+void JabberClient::slotS5BServerGone ()
+{
+
+ d->s5bServer = 0L;
+
+ if ( d->jabberClient )
+ d->jabberClient->s5bManager()->setServer( 0L );
+
+}
+
+void JabberClient::addS5BServerAddress ( const QString &address )
+{
+ QStringList newList;
+
+ d->s5bAddressList.append ( address );
+
+ // now filter the list without dupes
+ for ( QStringList::Iterator it = d->s5bAddressList.begin (); it != d->s5bAddressList.end (); ++it )
+ {
+ if ( !newList.contains ( *it ) )
+ newList.append ( *it );
+ }
+
+ s5bServer()->setHostList ( newList );
+
+}
+
+void JabberClient::removeS5BServerAddress ( const QString &address )
+{
+ QStringList newList;
+
+ QStringList::iterator it = d->s5bAddressList.find ( address );
+ if ( it != d->s5bAddressList.end () )
+ {
+ d->s5bAddressList.remove ( it );
+ }
+
+ if ( d->s5bAddressList.isEmpty () )
+ {
+ delete d->s5bServer;
+ d->s5bServer = 0L;
+ }
+ else
+ {
+ // now filter the list without dupes
+ for ( QStringList::Iterator it = d->s5bAddressList.begin (); it != d->s5bAddressList.end (); ++it )
+ {
+ if ( !newList.contains ( *it ) )
+ newList.append ( *it );
+ }
+
+ s5bServer()->setHostList ( newList );
+ }
+
+}
+
+void JabberClient::setForceTLS ( bool flag )
+{
+
+ d->forceTLS = flag;
+
+}
+
+bool JabberClient::forceTLS () const
+{
+
+ return d->forceTLS;
+
+}
+
+void JabberClient::setUseSSL ( bool flag )
+{
+
+ d->useSSL = flag;
+
+}
+
+bool JabberClient::useSSL () const
+{
+
+ return d->useSSL;
+
+}
+
+void JabberClient::setUseXMPP09 ( bool flag )
+{
+
+ d->useXMPP09 = flag;
+
+}
+
+bool JabberClient::useXMPP09 () const
+{
+
+ return d->useXMPP09;
+
+}
+
+void JabberClient::setProbeSSL ( bool flag )
+{
+
+ d->probeSSL = flag;
+
+}
+
+bool JabberClient::probeSSL () const
+{
+
+ return d->probeSSL;
+
+}
+
+void JabberClient::setOverrideHost ( bool flag, const QString &server, int port )
+{
+
+ d->overrideHost = flag;
+ d->server = server;
+ d->port = port;
+
+}
+
+bool JabberClient::overrideHost () const
+{
+
+ return d->overrideHost;
+
+}
+
+void JabberClient::setAllowPlainTextPassword ( bool flag )
+{
+
+ d->allowPlainTextPassword = flag;
+
+}
+
+bool JabberClient::allowPlainTextPassword () const
+{
+
+ return d->allowPlainTextPassword;
+
+}
+
+void JabberClient::setFileTransfersEnabled ( bool flag, const QString &localAddress )
+{
+
+ d->fileTransfersEnabled = flag;
+ d->localAddress = localAddress;
+
+}
+
+QString JabberClient::localAddress () const
+{
+
+ return d->localAddress;
+
+}
+
+bool JabberClient::fileTransfersEnabled () const
+{
+
+ return d->fileTransfersEnabled;
+
+}
+
+void JabberClient::setClientName ( const QString &clientName )
+{
+
+ d->clientName = clientName;
+
+}
+
+QString JabberClient::clientName () const
+{
+
+ return d->clientName;
+
+}
+
+void JabberClient::setClientVersion ( const QString &clientVersion )
+{
+
+ d->clientVersion = clientVersion;
+
+}
+
+QString JabberClient::clientVersion () const
+{
+
+ return d->clientVersion;
+
+}
+
+void JabberClient::setOSName ( const QString &osName )
+{
+
+ d->osName = osName;
+
+}
+
+QString JabberClient::osName () const
+{
+
+ return d->osName;
+
+}
+
+void JabberClient::setCapsNode( const QString &capsNode )
+{
+ d->capsNode = capsNode;
+}
+
+QString JabberClient::capsNode() const
+{
+ return d->capsNode;
+}
+
+void JabberClient::setCapsVersion( const QString &capsVersion )
+{
+ d->capsVersion = capsVersion;
+}
+
+QString JabberClient::capsVersion() const
+{
+ return d->capsVersion;
+}
+
+QString JabberClient::capsExt() const
+{
+ if(d->jabberClient)
+ {
+ return d->jabberClient->capsExt();
+ }
+
+ return QString();
+}
+void JabberClient::setDiscoIdentity( DiscoItem::Identity identity )
+{
+ d->discoIdentity = identity;
+}
+
+DiscoItem::Identity JabberClient::discoIdentity() const
+{
+ return d->discoIdentity;
+}
+
+void JabberClient::setTimeZone ( const QString &timeZoneName, int timeZoneOffset )
+{
+
+ d->timeZoneName = timeZoneName;
+ d->timeZoneOffset = timeZoneOffset;
+
+}
+
+QString JabberClient::timeZoneName () const
+{
+
+ return d->timeZoneName;
+
+}
+
+int JabberClient::timeZoneOffset () const
+{
+
+ return d->timeZoneOffset;
+
+}
+
+int JabberClient::getPenaltyTime ()
+{
+
+ int currentTime = d->currentPenaltyTime;
+
+ d->currentPenaltyTime += JABBER_PENALTY_TIME;
+
+ return currentTime;
+
+}
+
+XMPP::Client *JabberClient::client () const
+{
+
+ return d->jabberClient;
+
+}
+
+XMPP::ClientStream *JabberClient::clientStream () const
+{
+
+ return d->jabberClientStream;
+
+}
+
+JabberConnector *JabberClient::clientConnector () const
+{
+
+ return d->jabberClientConnector;
+
+}
+
+XMPP::Task *JabberClient::rootTask () const
+{
+
+ if ( client () )
+ {
+ return client()->rootTask ();
+ }
+ else
+ {
+ return 0l;
+ }
+
+}
+
+XMPP::FileTransferManager *JabberClient::fileTransferManager () const
+{
+
+ if ( client () )
+ {
+ return client()->fileTransferManager ();
+ }
+ else
+ {
+ return 0L;
+ }
+
+}
+
+XMPP::Jid JabberClient::jid () const
+{
+
+ return d->jid;
+
+}
+
+JabberClient::ErrorCode JabberClient::connect ( const XMPP::Jid &jid, const QString &password, bool auth )
+{
+ /*
+ * Close any existing connection.
+ */
+ if ( d->jabberClient )
+ {
+ d->jabberClient->close ();
+ }
+
+ d->jid = jid;
+ d->password = password;
+
+ /*
+ * Return an error if we should force TLS but it's not available.
+ */
+ if ( ( forceTLS () || useSSL () || probeSSL () ) && !QCA::isSupported ( QCA::CAP_TLS ) )
+ {
+ return NoTLS;
+ }
+
+ /*
+ * Instantiate connector, responsible for dealing with the socket.
+ * This class uses KDE's socket code, which in turn makes use of
+ * the global proxy settings.
+ */
+ d->jabberClientConnector = new JabberConnector;
+
+ d->jabberClientConnector->setOptSSL ( useSSL () );
+
+ if ( useXMPP09 () )
+ {
+ if ( overrideHost () )
+ {
+ d->jabberClientConnector->setOptHostPort ( d->server, d->port );
+ }
+
+ d->jabberClientConnector->setOptProbe ( probeSSL () );
+
+ }
+
+ /*
+ * Setup authentication layer
+ */
+ if ( QCA::isSupported ( QCA::CAP_TLS ) )
+ {
+ d->jabberTLS = new QCA::TLS;
+ d->jabberTLSHandler = new XMPP::QCATLSHandler ( d->jabberTLS );
+
+ {
+ using namespace XMPP;
+ QObject::connect ( d->jabberTLSHandler, SIGNAL ( tlsHandshaken() ), this, SLOT ( slotTLSHandshaken () ) );
+ }
+
+ QPtrList<QCA::Cert> certStore;
+ d->jabberTLS->setCertificateStore ( certStore );
+ }
+
+ /*
+ * Instantiate client stream which handles the network communication by referring
+ * to a connector (proxying etc.) and a TLS handler (security layer)
+ */
+ d->jabberClientStream = new XMPP::ClientStream ( d->jabberClientConnector, d->jabberTLSHandler );
+
+ {
+ using namespace XMPP;
+ QObject::connect ( d->jabberClientStream, SIGNAL ( needAuthParams(bool, bool, bool) ),
+ this, SLOT ( slotCSNeedAuthParams (bool, bool, bool) ) );
+ QObject::connect ( d->jabberClientStream, SIGNAL ( authenticated () ),
+ this, SLOT ( slotCSAuthenticated () ) );
+ QObject::connect ( d->jabberClientStream, SIGNAL ( connectionClosed () ),
+ this, SLOT ( slotCSDisconnected () ) );
+ QObject::connect ( d->jabberClientStream, SIGNAL ( delayedCloseFinished () ),
+ this, SLOT ( slotCSDisconnected () ) );
+ QObject::connect ( d->jabberClientStream, SIGNAL ( warning (int) ),
+ this, SLOT ( slotCSWarning (int) ) );
+ QObject::connect ( d->jabberClientStream, SIGNAL ( error (int) ),
+ this, SLOT ( slotCSError (int) ) );
+ }
+
+ d->jabberClientStream->setOldOnly ( useXMPP09 () );
+
+ /*
+ * Initiate anti-idle timer (will be triggered every 55 seconds).
+ */
+ d->jabberClientStream->setNoopTime ( 55000 );
+
+ /*
+ * Allow plaintext password authentication or not?
+ */
+ d->jabberClientStream->setAllowPlain( allowPlainTextPassword () );
+
+ /*
+ * Setup client layer.
+ */
+ d->jabberClient = new XMPP::Client ( this );
+
+ /*
+ * Enable file transfer (IP and server will be set after connection
+ * has been established.
+ */
+ if ( fileTransfersEnabled () )
+ {
+ d->jabberClient->setFileTransferEnabled ( true );
+
+ {
+ using namespace XMPP;
+ QObject::connect ( d->jabberClient->fileTransferManager(), SIGNAL ( incomingReady() ),
+ this, SLOT ( slotIncomingFileTransfer () ) );
+ }
+ }
+
+ /* This should only be done here to connect the signals, otherwise it is a
+ * bad idea.
+ */
+ {
+ using namespace XMPP;
+ QObject::connect ( d->jabberClient, SIGNAL ( subscription (const Jid &, const QString &) ),
+ this, SLOT ( slotSubscription (const Jid &, const QString &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( rosterRequestFinished ( bool, int, const QString & ) ),
+ this, SLOT ( slotRosterRequestFinished ( bool, int, const QString & ) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( rosterItemAdded (const RosterItem &) ),
+ this, SLOT ( slotNewContact (const RosterItem &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( rosterItemUpdated (const RosterItem &) ),
+ this, SLOT ( slotContactUpdated (const RosterItem &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( rosterItemRemoved (const RosterItem &) ),
+ this, SLOT ( slotContactDeleted (const RosterItem &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( resourceAvailable (const Jid &, const Resource &) ),
+ this, SLOT ( slotResourceAvailable (const Jid &, const Resource &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( resourceUnavailable (const Jid &, const Resource &) ),
+ this, SLOT ( slotResourceUnavailable (const Jid &, const Resource &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( messageReceived (const Message &) ),
+ this, SLOT ( slotReceivedMessage (const Message &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( groupChatJoined (const Jid &) ),
+ this, SLOT ( slotGroupChatJoined (const Jid &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( groupChatLeft (const Jid &) ),
+ this, SLOT ( slotGroupChatLeft (const Jid &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( groupChatPresence (const Jid &, const Status &) ),
+ this, SLOT ( slotGroupChatPresence (const Jid &, const Status &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( groupChatError (const Jid &, int, const QString &) ),
+ this, SLOT ( slotGroupChatError (const Jid &, int, const QString &) ) );
+ //QObject::connect ( d->jabberClient, SIGNAL (debugText (const QString &) ),
+ // this, SLOT ( slotPsiDebug (const QString &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( xmlIncoming(const QString& ) ),
+ this, SLOT ( slotIncomingXML (const QString &) ) );
+ QObject::connect ( d->jabberClient, SIGNAL ( xmlOutgoing(const QString& ) ),
+ this, SLOT ( slotOutgoingXML (const QString &) ) );
+ }
+
+ d->jabberClient->setClientName ( clientName () );
+ d->jabberClient->setClientVersion ( clientVersion () );
+ d->jabberClient->setOSName ( osName () );
+
+ // Set caps information
+ d->jabberClient->setCapsNode( capsNode() );
+ d->jabberClient->setCapsVersion( capsVersion() );
+
+ // Set Disco Identity
+ d->jabberClient->setIdentity( discoIdentity() );
+
+ d->jabberClient->setTimeZone ( timeZoneName (), timeZoneOffset () );
+
+ d->jabberClient->connectToServer ( d->jabberClientStream, jid, auth );
+
+ return Ok;
+
+}
+
+void JabberClient::disconnect ()
+{
+
+ if ( d->jabberClient )
+ {
+ d->jabberClient->close ();
+ }
+ else
+ {
+ cleanUp ();
+ }
+
+}
+
+void JabberClient::disconnect( XMPP::Status &reason )
+{
+ if ( d->jabberClient )
+ {
+ if ( d->jabberClientStream->isActive() )
+ {
+ XMPP::JT_Presence *pres = new JT_Presence(rootTask());
+ reason.setIsAvailable( false );
+ pres->pres( reason );
+ pres->go();
+
+ d->jabberClientStream->close();
+ d->jabberClient->close();
+ }
+ }
+ else
+ {
+ cleanUp();
+ }
+}
+
+bool JabberClient::isConnected () const
+{
+
+ if ( d->jabberClient )
+ {
+ return d->jabberClient->isActive ();
+ }
+
+ return false;
+
+}
+
+void JabberClient::joinGroupChat ( const QString &host, const QString &room, const QString &nick )
+{
+
+ client()->groupChatJoin ( host, room, nick );
+
+}
+
+void JabberClient::joinGroupChat ( const QString &host, const QString &room, const QString &nick, const QString &password )
+{
+
+ client()->groupChatJoin ( host, room, nick, password );
+
+}
+
+void JabberClient::leaveGroupChat ( const QString &host, const QString &room )
+{
+
+ client()->groupChatLeave ( host, room );
+
+}
+
+void JabberClient::setGroupChatStatus( const QString & host, const QString & room, const XMPP::Status & status )
+{
+ client()->groupChatSetStatus( host, room, status);
+}
+
+void JabberClient::changeGroupChatNick( const QString & host, const QString & room, const QString & nick, const XMPP::Status & status )
+{
+ client()->groupChatChangeNick( host, room, nick, status );
+}
+
+
+void JabberClient::sendMessage ( const XMPP::Message &message )
+{
+
+ client()->sendMessage ( message );
+
+}
+
+void JabberClient::send ( const QString &packet )
+{
+
+ client()->send ( packet );
+
+}
+
+void JabberClient::requestRoster ()
+{
+
+ client()->rosterRequest ();
+
+}
+
+void JabberClient::slotPsiDebug ( const QString & _msg )
+{
+ QString msg = _msg;
+
+ msg = msg.replace( QRegExp( "<password>[^<]*</password>\n" ), "<password>[Filtered]</password>\n" );
+ msg = msg.replace( QRegExp( "<digest>[^<]*</digest>\n" ), "<digest>[Filtered]</digest>\n" );
+
+ emit debugMessage ( "Psi: " + msg );
+
+}
+
+void JabberClient::slotIncomingXML ( const QString & _msg )
+{
+ QString msg = _msg;
+
+ msg = msg.replace( QRegExp( "<password>[^<]*</password>\n" ), "<password>[Filtered]</password>\n" );
+ msg = msg.replace( QRegExp( "<digest>[^<]*</digest>\n" ), "<digest>[Filtered]</digest>\n" );
+
+ emit debugMessage ( "XML IN: " + msg );
+
+}
+
+void JabberClient::slotOutgoingXML ( const QString & _msg )
+{
+ QString msg = _msg;
+
+ msg = msg.replace( QRegExp( "<password>[^<]*</password>\n" ), "<password>[Filtered]</password>\n" );
+ msg = msg.replace( QRegExp( "<digest>[^<]*</digest>\n" ), "<digest>[Filtered]</digest>\n" );
+
+ emit debugMessage ( "XML OUT: " + msg );
+
+}
+
+void JabberClient::slotTLSHandshaken ()
+{
+
+ emit debugMessage ( "TLS handshake done, testing certificate validity..." );
+
+ // FIXME: in the future, this should be handled by KDE, not QCA
+ int validityResult = d->jabberTLS->certificateValidityResult ();
+
+ if ( validityResult == QCA::TLS::Valid )
+ {
+ emit debugMessage ( "Certificate is valid, continuing." );
+
+ // valid certificate, continue
+ d->jabberTLSHandler->continueAfterHandshake ();
+ }
+ else
+ {
+ emit debugMessage ( "Certificate is not valid, asking user what to do next." );
+
+ // certificate is not valid, query the user
+ if ( ignoreTLSWarnings () )
+ {
+ emit debugMessage ( "We are supposed to ignore TLS warnings, continuing." );
+ d->jabberTLSHandler->continueAfterHandshake ();
+ }
+
+ emit tlsWarning ( validityResult );
+ }
+
+}
+
+void JabberClient::continueAfterTLSWarning ()
+{
+
+ if ( d->jabberTLSHandler )
+ {
+ d->jabberTLSHandler->continueAfterHandshake ();
+ }
+
+}
+
+void JabberClient::slotCSNeedAuthParams ( bool user, bool pass, bool realm )
+{
+ emit debugMessage ( "Sending auth credentials..." );
+
+ if ( user )
+ {
+ d->jabberClientStream->setUsername ( jid().node () );
+ }
+
+ if ( pass )
+ {
+ d->jabberClientStream->setPassword ( d->password );
+ }
+
+ if ( realm )
+ {
+ d->jabberClientStream->setRealm ( jid().domain () );
+ }
+
+ d->jabberClientStream->continueAfterParams ();
+
+}
+
+void JabberClient::slotCSAuthenticated ()
+{
+ emit debugMessage ( "Connected to Jabber server." );
+
+ /*
+ * Determine local IP address.
+ * FIXME: This is ugly!
+ */
+ if ( localAddress().isEmpty () )
+ {
+ // code for Iris-type bytestreams
+ ByteStream *irisByteStream = d->jabberClientConnector->stream();
+ if ( irisByteStream->inherits ( "BSocket" ) || irisByteStream->inherits ( "XMPP::BSocket" ) )
+ {
+ d->localAddress = ( (BSocket *)irisByteStream )->address().toString ();
+ }
+
+ // code for the KDE-type bytestream
+ JabberByteStream *kdeByteStream = dynamic_cast<JabberByteStream*>(d->jabberClientConnector->stream());
+ if ( kdeByteStream )
+ {
+ d->localAddress = kdeByteStream->socket()->localAddress().nodeName ();
+ }
+ }
+
+ if ( fileTransfersEnabled () )
+ {
+ // setup file transfer
+ addS5BServerAddress ( localAddress () );
+ d->jabberClient->s5bManager()->setServer ( s5bServer () );
+ }
+
+ // start the client operation
+ d->jabberClient->start ( jid().domain (), jid().node (), d->password, jid().resource () );
+
+ emit connected ();
+}
+
+void JabberClient::slotCSDisconnected ()
+{
+
+ /* FIXME:
+ * We should delete the XMPP::Client instance here,
+ * but timers etc prevent us from doing so. (Psi does
+ * not like to be deleted from a slot).
+ */
+
+ emit debugMessage ( "Disconnected, freeing up file transfer port..." );
+
+ // delete local address from S5B server
+ removeS5BServerAddress ( localAddress () );
+
+ emit csDisconnected ();
+
+}
+
+void JabberClient::slotCSWarning ( int warning )
+{
+
+ emit debugMessage ( "Client stream warning." );
+
+ /*
+ * FIXME: process all other warnings
+ */
+ switch ( warning )
+ {
+ //case XMPP::ClientStream::WarnOldVersion:
+ case XMPP::ClientStream::WarnNoTLS:
+ if ( forceTLS () )
+ {
+ disconnect ();
+ emit error ( NoTLS );
+ return;
+ }
+ break;
+ }
+
+ d->jabberClientStream->continueAfterWarning ();
+
+}
+
+void JabberClient::slotCSError ( int error )
+{
+
+ emit debugMessage ( "Client stream error." );
+
+ emit csError ( error );
+
+}
+
+void JabberClient::slotRosterRequestFinished ( bool success, int /*statusCode*/, const QString &/*statusString*/ )
+{
+
+ emit rosterRequestFinished ( success );
+
+}
+
+void JabberClient::slotIncomingFileTransfer ()
+{
+
+ emit incomingFileTransfer ();
+
+}
+
+void JabberClient::slotNewContact ( const XMPP::RosterItem &item )
+{
+
+ emit newContact ( item );
+
+}
+
+void JabberClient::slotContactDeleted ( const RosterItem &item )
+{
+
+ emit contactDeleted ( item );
+
+}
+
+void JabberClient::slotContactUpdated ( const RosterItem &item )
+{
+
+ emit contactUpdated ( item );
+
+}
+
+void JabberClient::slotResourceAvailable ( const Jid &jid, const Resource &resource )
+{
+
+ emit resourceAvailable ( jid, resource );
+
+}
+
+void JabberClient::slotResourceUnavailable ( const Jid &jid, const Resource &resource )
+{
+
+ emit resourceUnavailable ( jid, resource );
+
+}
+
+void JabberClient::slotReceivedMessage ( const Message &message )
+{
+
+ emit messageReceived ( message );
+
+}
+
+void JabberClient::slotGroupChatJoined ( const Jid &jid )
+{
+
+ emit groupChatJoined ( jid );
+
+}
+
+void JabberClient::slotGroupChatLeft ( const Jid &jid )
+{
+
+ emit groupChatLeft ( jid );
+
+}
+
+void JabberClient::slotGroupChatPresence ( const Jid &jid, const Status &status)
+{
+
+ emit groupChatPresence ( jid, status );
+
+}
+
+void JabberClient::slotGroupChatError ( const Jid &jid, int error, const QString &reason)
+{
+
+ emit groupChatError ( jid, error, reason );
+
+}
+
+void JabberClient::slotSubscription ( const Jid &jid, const QString &type )
+{
+
+ emit subscription ( jid, type );
+
+}
+
+
+#include "jabberclient.moc"
diff --git a/kopete/protocols/jabber/jabberclient.h b/kopete/protocols/jabber/jabberclient.h
new file mode 100644
index 00000000..7cd33e02
--- /dev/null
+++ b/kopete/protocols/jabber/jabberclient.h
@@ -0,0 +1,600 @@
+
+/***************************************************************************
+ jabberclient.h - Generic Jabber Client Class
+ -------------------
+ begin : Sat May 25 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+ (C) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (C) 2001-2006 Kopete developers
+ <kopete-devel@kde.org>.
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBERCLIENT_H
+#define JABBERCLIENT_H
+
+#include <qobject.h>
+
+// include these because of namespace reasons
+#include <im.h>
+#include <xmpp.h>
+#include <s5b.h>
+
+using namespace XMPP;
+
+class JabberConnector;
+
+/**
+ * This class provides an interface to the Iris subsystem. The goal is to
+ * abstract the Iris layer and manage it via a single, simple to use class.
+ * By default, @ref JabberClient will attempt to establish a connection
+ * using XMPP 1.0. This means that apart from the JID and password, no
+ * further details are necessary to connect. The server and port will be
+ * determined using a SRV lookup. If TLS is possible (meaning, the TLS
+ * plugin is available and the server supports TLS) it will automatically
+ * be used. Otherwise, a non-encrypted connection will be established.
+ * If XMPP 1.0 is not possible, the connection will fall back to the old
+ * protocol. By default, this connection is not encrypted. You can, however,
+ * use @ref setUseSSL to immediately attempt an SSL connection. This is
+ * most useful if you want to establish an SSL connection to a non-standard
+ * port, in which case you will also have to use @ref setOverrideHost. In case
+ * XMPP 1.0 does not work, an automatic attempt to connect to the standard port
+ * 5223 with SSL can be made with @ref setProbeSSL. If the attempt is not
+ * sucessful, the connection will fall back to an unencrypted attempt
+ * at port 5222.
+ * @brief Provides a Jabber client
+ * @author Till Gerken
+ */
+class JabberClient : public QObject
+{
+
+Q_OBJECT
+
+public:
+ /**
+ * Error codes indicating problems during operation.
+ */
+ enum ErrorCode
+ {
+ Ok, /** No error. */
+ InvalidPassword, /** Password used to connect to the server was incorrect. */
+ AlreadyConnected, /** A new connection was attempted while the previous one hasn't been closed. */
+ NoTLS, /** Use of TLS has been forced (see @ref forceTLS) but TLS is not available, either server- or client-side. */
+ InvalidPasswordForMUC = 401, /** A password is require to enter on this Multi-User Chat */
+ NicknameConflict = 409, /** There is already someone with that nick connected to the Multi-User Chat */
+ BannedFromThisMUC = 403, /** You can't join this Multi-User Chat because you were bannished */
+ MaxUsersReachedForThisMuc = 503 /** You can't join this Multi-User Chat because it is full */
+ };
+
+ JabberClient();
+ ~JabberClient();
+
+ /**
+ * Connect to a Jabber server.
+ * @param jid JID to connect to.
+ * @param password Password to authenticate with.
+ * @param auth True if authentication should be done, false if not.
+ */
+ ErrorCode connect ( const XMPP::Jid &jid, const QString &password, bool auth = true );
+
+ /**
+ * Disconnect from Jabber server.
+ */
+ void disconnect ();
+
+ /**
+ * Disconnect from Jabber server with reason
+ * @param reason The reason for disconnecting
+ */
+ void disconnect (XMPP::Status &reason);
+
+ /**
+ * Returns if this instance is connected to a server.
+ */
+ bool isConnected () const;
+
+ /**
+ * Returns the JID associated with this instance.
+ */
+ XMPP::Jid jid () const;
+
+ /**
+ * Set flag to ignore TLS warnings. If TLS
+ * warnings are not ignored, the class will emit
+ * @ref tlsWarning and wait for the user to
+ * call @ref continueAfterTLSWarning or
+ * @ref disconnect. Default is false.
+ */
+ void setIgnoreTLSWarnings ( bool flag );
+ /**
+ * Return if TLS warnings are being ignored.
+ */
+ bool ignoreTLSWarnings ();
+
+ /**
+ * Continue after a @ref tlsWarning signal.
+ */
+ void continueAfterTLSWarning ();
+
+ /**
+ * Set the port on which the S5B server should listen.
+ * This is only taken into account if @ref setFileTransfersEnabled
+ * is set to true.
+ * @return True if port could be bound, false if not.
+ */
+ bool setS5BServerPort ( int port );
+ /**
+ * Returns the port the S5B server listens on.
+ */
+ int s5bServerPort () const;
+
+ /**
+ * Force the use of TLS. If TLS connections are forced,
+ * unencrypted connections will not be established.
+ * Default is false.
+ */
+ void setForceTLS ( bool flag );
+ /**
+ * Returns if TLS connections are forced.
+ */
+ bool forceTLS () const;
+
+ /**
+ * Force direct SSL connection, also for the
+ * handshake. This is only useful if you know
+ * the server supports it or you want to use
+ * a non-standard port, in which case @ref setOverrideHost
+ * will be useful. Default is false.
+ */
+ void setUseSSL ( bool flag );
+ /**
+ * Returns if an SSL connection attempt should be made.
+ */
+ bool useSSL () const;
+
+ /**
+ * Use only the old protocol (pre-XMPP 1.0). This should only
+ * be used with servers not supporting XMPP 1.0 or with servers
+ * that have a broken login procedure. Default is false. If
+ * a connection attempt is not possible, Iris will automatically
+ * fall back to the old protocol.
+ */
+ void setUseXMPP09 ( bool flag );
+ /**
+ * Returns if the old protocol should be used.
+ */
+ bool useXMPP09 () const;
+
+ /**
+ * Probe port 5223 if an SSL connection is possible. If
+ * a connection is not possible, an unencrypted connection
+ * will be attempted at port 5222. This is only meaningful
+ * if @ref useXMPP09 is true. Default is false.
+ */
+ void setProbeSSL ( bool flag );
+ /**
+ * Returns if SSL support will be probed.
+ */
+ bool probeSSL () const;
+
+ /**
+ * Override the name and port of the server to connect to.
+ * This only has an effect if the old protocol (@ref useXMPP09)
+ * has been enabled. Default is false.
+ */
+ void setOverrideHost ( bool flag, const QString &server = "", int port = 5222 );
+ /**
+ * Returns if the server name and port are overridden.
+ */
+ bool overrideHost () const;
+
+ /**
+ * Allow the transmission of a plain text password. If digested
+ * passwords are supported by the server, they will still be preferred.
+ * Defaults to true.
+ */
+ void setAllowPlainTextPassword ( bool flag );
+ /**
+ * Returns if plain text passwords are allowed.
+ */
+ bool allowPlainTextPassword () const;
+
+ /**
+ * Enable file transfers. Default is false.
+ * @param flag Whether to enable file transfers.
+ * @param localAddress Local address to receive file transfers at. Will be determined automatically if not specified.
+ */
+ void setFileTransfersEnabled ( bool flag, const QString &localAddress = QString::null );
+
+ /**
+ * Returns the address of the local interface.
+ */
+ QString localAddress () const;
+
+ /**
+ * Returns if file transfers are enabled.
+ */
+ bool fileTransfersEnabled () const;
+
+ /**
+ * Set client name.
+ */
+ void setClientName ( const QString &clientName );
+ /**
+ * Return client name.
+ */
+ QString clientName () const;
+
+ /**
+ * Set client version.
+ */
+ void setClientVersion ( const QString &clientVersion );
+ /**
+ * Return client version.
+ */
+ QString clientVersion () const;
+
+ /**
+ * Set operating system name.
+ */
+ void setOSName ( const QString &osName );
+ /**
+ * Return operating system name.
+ */
+ QString osName () const;
+
+ /**
+ * Set the caps(JEP-0115: Entity capabilities) node name.
+ * @param node Node name.
+ */
+ void setCapsNode( const QString &capsNode );
+ /**
+ * Return the caps node name for this client.
+ * @return the caps node name.
+ */
+ QString capsNode() const;
+
+ /**
+ * Set the caps(JEP-0115: Entity capabilities) node version.
+ * @param capsVersion the node version.
+ */
+ void setCapsVersion( const QString &capsVersion );
+ /**
+ * Return the caps version for this client.
+ * @return the caps version.
+ */
+ QString capsVersion() const;
+
+ /**
+ * Return the caps extension list for this client.
+ * @return A string containing all extensions separated by space.
+ */
+ QString capsExt() const;
+
+ /**
+ * Set the disco Identity information for this client.
+ * Create a Disco identity like this:
+ * @code
+ * DiscoItem::Identity identity;
+ * identity.category = "client";
+ * identity.type = "pc";
+ * identity.name = "Kopete";
+ * @endcode
+ *
+ * @param identity DiscoItem::Identity for the client.
+ */
+ void setDiscoIdentity(DiscoItem::Identity identity);
+ /**
+ * Get the disco Identity information for this client.
+ * @return the DiscoItem::Identity for this client.
+ */
+ DiscoItem::Identity discoIdentity() const;
+
+ /**
+ * Set timezone information. Default is UTC.
+ */
+ void setTimeZone ( const QString &timeZoneName, int timeZoneOffset );
+ /**
+ * Return timezone name.
+ */
+ QString timeZoneName () const;
+ /**
+ * Return timezone offset.
+ */
+ int timeZoneOffset () const;
+
+ /**
+ * This method can be used to implement a penalty
+ * system when a lot of queries need to be sent to the
+ * server. Using the time returned by this method,
+ * the caller can determine a delay until the next
+ * operation in the queue can be carried out.
+ * @brief Return current penalty time in seconds.
+ */
+ int getPenaltyTime ();
+
+ /**
+ * Return the XMPP client instance.
+ */
+ XMPP::Client *client () const;
+
+ /**
+ * Return client stream instance.
+ */
+ XMPP::ClientStream *clientStream () const;
+
+ /**
+ * Return client connector instance.
+ */
+ JabberConnector *clientConnector () const;
+
+ /**
+ * Get the root task for this connection.
+ * You need this instance for every task
+ * you want to start.
+ */
+ XMPP::Task *rootTask () const;
+
+ /**
+ * Returns the file transfer manager
+ * instance that deals with current file
+ * transfers.
+ */
+ XMPP::FileTransferManager *fileTransferManager () const;
+
+ /**
+ * Join a group chat.
+ * @param host Node to join the room at.
+ * @param room Name of room to join.
+ * @param nick Nick name you want to join with.
+ */
+ void joinGroupChat ( const QString &host, const QString &room, const QString &nick );
+
+ /**
+ * Join a group chat that require a password.
+ * @param host Node to join the room at.
+ * @param room Name of room to join.
+ * @param nick Nick name you want to join with.
+ * @param password The password to join the room.
+ */
+ void joinGroupChat ( const QString &host, const QString &room, const QString &nick, const QString &password );
+
+ /**
+ * Leave a group chat.
+ * @param host Node to leave room at.
+ * @param room Name of room to leave.
+ */
+ void leaveGroupChat ( const QString &host, const QString &room );
+
+ /**
+ * change the status of a group chat
+ */
+ void setGroupChatStatus(const QString &host, const QString &room, const XMPP::Status &);
+ /**
+ * change the nick in a group chat
+ */
+ void changeGroupChatNick(const QString &host, const QString &room, const QString &nick, const XMPP::Status &status =XMPP::Status());
+
+ /**
+ * Send a message.
+ */
+ void sendMessage ( const XMPP::Message &message );
+
+ /**
+ * Send raw packet to the server.
+ */
+ void send ( const QString &packet );
+
+ /**
+ * Request the roster from the Jabber server.
+ */
+ void requestRoster ();
+
+signals:
+ /**
+ * Connected successfully.
+ */
+ void connected ();
+
+ /**
+ * Client stream authenticated. This
+ * signal is emitted when the socket
+ * connection has been successfully
+ * established, before sending the login
+ * packet.
+ */
+ void csAuthenticated ();
+
+ /**
+ * Client stream error.
+ */
+ void csError ( int error );
+
+ /**
+ * Client stream was disconnected.
+ */
+ void csDisconnected ();
+
+ /**
+ * TLS problem encountered.
+ */
+ void tlsWarning ( int validityResult );
+
+ /**
+ * A new file transfer needs to be handled.
+ * The file transfer can be dealt with by
+ * querying the file transfer manager from
+ * @ref client.
+ */
+ void incomingFileTransfer ();
+
+ /**
+ * Fatal error has been encountered,
+ * further operations are not possible.
+ */
+ void error ( JabberClient::ErrorCode code );
+
+ /**
+ * Roster has been transmitted and processed.
+ */
+ void rosterRequestFinished ( bool success );
+
+ /**
+ * A new contact appeared on the roster.
+ */
+ void newContact ( const XMPP::RosterItem &item );
+
+ /**
+ * A contact has been removed from the roster.
+ */
+ void contactDeleted ( const XMPP::RosterItem &item );
+
+ /**
+ * A roster item has changed.
+ */
+ void contactUpdated ( const XMPP::RosterItem &item );
+
+ /**
+ * New resource is available for a contact.
+ */
+ void resourceAvailable ( const XMPP::Jid &jid, const XMPP::Resource &resource );
+
+ /**
+ * An existing resource has been removed.
+ */
+ void resourceUnavailable ( const XMPP::Jid &jid, const XMPP::Resource &resource );
+
+ /**
+ * A new message has been received.
+ */
+ void messageReceived ( const XMPP::Message &message );
+
+ /**
+ * Group chat has been joined.
+ */
+ void groupChatJoined ( const XMPP::Jid &jid );
+
+ /**
+ * Group chat has been left.
+ */
+ void groupChatLeft ( const XMPP::Jid &jid );
+
+ /**
+ * A presence to a group chat has been signalled.
+ */
+ void groupChatPresence ( const XMPP::Jid &jid, const XMPP::Status &status );
+
+ /**
+ * An error was encountered joining or processing a group chat.
+ */
+ void groupChatError ( const XMPP::Jid &jid, int error, const QString &reason );
+
+ /**
+ * New subscription request.
+ */
+ void subscription ( const XMPP::Jid &jid, const QString &type );
+
+ /**
+ * Dispatches a debug message. Debug messages
+ * include incoming and outgoing XML packets
+ * as well as internal status messages.
+ */
+ void debugMessage ( const QString &message );
+
+private:
+ class Private;
+ Private *d;
+
+ /**
+ * Delete all member classes and reset the class to a predefined state.
+ */
+ void cleanUp ();
+
+ /**
+ * Return current instance of the S5B server.
+ */
+ XMPP::S5BServer *s5bServer ();
+ /**
+ * Add an address that the S5B server should handle.
+ */
+ void addS5BServerAddress ( const QString &address );
+ /**
+ * Remove an address that the S5B server currently handles.
+ */
+ void removeS5BServerAddress ( const QString &address );
+
+private slots:
+ /* S5B server object has been destroyed. */
+ void slotS5BServerGone ();
+
+ /* update the penalty timer */
+ void slotUpdatePenaltyTime ();
+
+ /* Login if the connection was OK. */
+ void slotCSNeedAuthParams (bool user, bool pass, bool realm);
+
+ /* Called from Psi: tells us when we're logged in OK. */
+ void slotCSAuthenticated ();
+
+ /* Called from Psi: tells us when we've been disconnected from the server. */
+ void slotCSDisconnected ();
+
+ /* Called from Psi: alerts us to a protocol warning. */
+ void slotCSWarning (int);
+
+ /* Called from Psi: alerts us to a protocol error. */
+ void slotCSError (int);
+
+ /* Called from Psi: report certificate status */
+ void slotTLSHandshaken ();
+
+ /* Called from Psi: roster request finished */
+ void slotRosterRequestFinished ( bool success, int statusCode, const QString &statusString );
+
+ /* Called from Psi: incoming file transfer */
+ void slotIncomingFileTransfer ();
+
+ /* A new item appeared in our roster */
+ void slotNewContact (const RosterItem &);
+
+ /* An item has been deleted from our roster. */
+ void slotContactDeleted (const RosterItem &);
+
+ /* Update a contact's details. */
+ void slotContactUpdated (const RosterItem &);
+
+ /* Someone on our contact list had (another) resource come online. */
+ void slotResourceAvailable (const Jid &, const Resource &);
+
+ /* Someone on our contact list had a resource go offline. */
+ void slotResourceUnavailable (const Jid &, const Resource &);
+
+ /* Incoming message. */
+ void slotReceivedMessage (const Message &);
+
+ /* Called from Psi: debug messages from the backend. */
+ void slotPsiDebug (const QString & msg);
+ void slotIncomingXML (const QString &msg);
+ void slotOutgoingXML (const QString &msg);
+
+ /* Slots for handling group chats. */
+ void slotGroupChatJoined (const Jid & jid);
+ void slotGroupChatLeft (const Jid & jid);
+ void slotGroupChatPresence (const Jid & jid, const Status & status);
+ void slotGroupChatError (const Jid & jid, int error, const QString & reason);
+
+ /* Incoming subscription request. */
+ void slotSubscription (const Jid & jid, const QString & type);
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberconnector.cpp b/kopete/protocols/jabber/jabberconnector.cpp
new file mode 100644
index 00000000..2359dd69
--- /dev/null
+++ b/kopete/protocols/jabber/jabberconnector.cpp
@@ -0,0 +1,132 @@
+
+/***************************************************************************
+ jabberconnector.cpp - Socket Connector for Jabber
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 <kdebug.h>
+#include "jabberconnector.h"
+#include "jabberbytestream.h"
+#include "jabberprotocol.h"
+
+JabberConnector::JabberConnector ( QObject *parent, const char */*name*/ )
+ : XMPP::Connector ( parent )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "New Jabber connector." << endl;
+
+ mErrorCode = KNetwork::KSocketBase::NoError;
+
+ mByteStream = new JabberByteStream ( this );
+
+ connect ( mByteStream, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) );
+ connect ( mByteStream, SIGNAL ( error ( int ) ), this, SLOT ( slotError ( int ) ) );
+
+}
+
+JabberConnector::~JabberConnector ()
+{
+
+ delete mByteStream;
+
+}
+
+void JabberConnector::connectToServer ( const QString &server )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Initiating connection to " << server << endl;
+
+ /*
+ * 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 JabberConnector::slotConnected ()
+{
+ kdDebug ( JABBER_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 JabberConnector::slotError ( int code )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Error detected: " << code << endl;
+
+ mErrorCode = code;
+ emit error ();
+
+}
+
+int JabberConnector::errorCode ()
+{
+
+ return mErrorCode;
+
+}
+
+ByteStream *JabberConnector::stream () const
+{
+
+ return mByteStream;
+
+}
+
+void JabberConnector::done ()
+{
+
+ mByteStream->close ();
+
+}
+
+void JabberConnector::setOptHostPort ( const QString &host, Q_UINT16 port )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl;
+
+ mHost = host;
+ mPort = port;
+
+}
+
+void JabberConnector::setOptSSL ( bool ssl )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Setting SSL to " << ssl << endl;
+
+ setUseSSL ( ssl );
+
+}
+
+void JabberConnector::setOptProbe ( bool )
+{
+ // FIXME: Implement this.
+}
+
+#include "jabberconnector.moc"
diff --git a/kopete/protocols/jabber/jabberconnector.h b/kopete/protocols/jabber/jabberconnector.h
new file mode 100644
index 00000000..6fbc4167
--- /dev/null
+++ b/kopete/protocols/jabber/jabberconnector.h
@@ -0,0 +1,65 @@
+
+/***************************************************************************
+ jabberconnector.cpp - Socket Connector for Jabber
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 JABBERCONNECTOR_H
+#define JABBERCONNECTOR_H
+
+#include <xmpp.h>
+#include "jabberbytestream.h"
+
+class ByteStream;
+class KResolverEntry;
+
+/**
+@author Till Gerken
+*/
+class JabberConnector : public XMPP::Connector
+{
+
+Q_OBJECT
+
+public:
+ JabberConnector ( QObject *parent = 0, const char *name = 0 );
+
+ ~JabberConnector ();
+
+ void connectToServer ( const QString &server );
+ ByteStream *stream () const;
+ void done ();
+
+ void setOptHostPort ( const QString &host, Q_UINT16 port );
+ void setOptSSL ( bool );
+ void setOptProbe ( bool );
+
+ int errorCode ();
+
+private slots:
+ void slotConnected ();
+ void slotError ( int );
+
+private:
+ QString mHost;
+ Q_UINT16 mPort;
+ int mErrorCode;
+
+ JabberByteStream *mByteStream;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabbercontact.cpp b/kopete/protocols/jabber/jabbercontact.cpp
new file mode 100644
index 00000000..c8589e1e
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercontact.cpp
@@ -0,0 +1,1328 @@
+ /*
+ * jabbercontact.cpp - Regular Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabbercontact.h"
+
+#include "xmpp_tasks.h"
+#include "im.h"
+
+#include <qtimer.h>
+#include <qdatetime.h>
+#include <qstylesheet.h>
+#include <qimage.h>
+#include <qregexp.h>
+#include <qbuffer.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kinputdialog.h>
+#include <kopeteview.h>
+
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteaccountmanager.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabberchatsession.h"
+#include "jabberresource.h"
+#include "jabberresourcepool.h"
+#include "jabberfiletransfer.h"
+#include "jabbertransport.h"
+#include "dlgjabbervcard.h"
+
+#ifdef SUPPORT_JINGLE
+// #include "jinglesessionmanager.h"
+// #include "jinglevoicesession.h"
+#include "jinglevoicesessiondialog.h"
+#endif
+
+/**
+ * JabberContact constructor
+ */
+JabberContact::JabberContact (const XMPP::RosterItem &rosterItem, Kopete::Account *_account, Kopete::MetaContact * mc, const QString &legacyId)
+ : JabberBaseContact ( rosterItem, _account, mc, legacyId) , mDiscoDone(false), m_syncTimer(0L)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << contactId() << " is created - " << this << endl;
+ // this contact is able to transfer files
+ setFileCapable ( true );
+
+ /*
+ * Catch when we're going online for the first time to
+ * update our properties from a vCard. (properties are
+ * not available during startup, so we need to read
+ * them later - this also serves as a random update
+ * feature)
+ * Note: The only time account->myself() could be a
+ * NULL pointer is if this contact here is the myself()
+ * instance itself. Since in that case we wouldn't
+ * get updates at all, we need to treat that as a
+ * special case.
+ */
+
+ mVCardUpdateInProgress = false;
+
+ if ( !account()->myself () )
+ {
+ // this contact is a regular contact
+ connect ( this,
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckVCard () ) );
+ }
+ else
+ {
+ // this contact is the myself instance
+ connect ( account()->myself (),
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckVCard () ) );
+
+ connect ( account()->myself (),
+ SIGNAL ( onlineStatusChanged ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ),
+ this, SLOT ( slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
+
+ /*
+ * Trigger update once if we're already connected for contacts
+ * that are being added while we are online.
+ */
+ if ( account()->myself()->onlineStatus().isDefinitelyOnline() )
+ {
+ slotGetTimedVCard ();
+ }
+ }
+
+ mRequestOfflineEvent = false;
+ mRequestDisplayedEvent = false;
+ mRequestDeliveredEvent = false;
+ mRequestComposingEvent = false;
+ mRequestGoneEvent = false;
+}
+
+
+
+JabberContact::~JabberContact()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << contactId() << " is destroyed - " << this << endl;
+}
+
+QPtrList<KAction> *JabberContact::customContextMenuActions ()
+{
+
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+
+ KActionMenu *actionAuthorization = new KActionMenu ( i18n ("Authorization"), "connect_established", this, "jabber_authorization");
+
+ KAction *resendAuthAction, *requestAuthAction, *removeAuthAction;
+
+ resendAuthAction = new KAction (i18n ("(Re)send Authorization To"), "mail_forward", 0,
+ this, SLOT (slotSendAuth ()), actionAuthorization, "actionSendAuth");
+ resendAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::To || mRosterItem.subscription().type() == XMPP::Subscription::None );
+ actionAuthorization->insert(resendAuthAction);
+
+ requestAuthAction = new KAction (i18n ("(Re)request Authorization From"), "mail_reply", 0,
+ this, SLOT (slotRequestAuth ()), actionAuthorization, "actionRequestAuth");
+ requestAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::From || mRosterItem.subscription().type() == XMPP::Subscription::None );
+ actionAuthorization->insert(requestAuthAction);
+
+ removeAuthAction = new KAction (i18n ("Remove Authorization From"), "mail_delete", 0,
+ this, SLOT (slotRemoveAuth ()), actionAuthorization, "actionRemoveAuth");
+ removeAuthAction->setEnabled( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From );
+ actionAuthorization->insert(removeAuthAction);
+
+ KActionMenu *actionSetAvailability = new KActionMenu (i18n ("Set Availability"), "kopeteavailable", this, "jabber_online");
+
+ actionSetAvailability->insert(new KAction (i18n ("Online"), protocol()->JabberKOSOnline.iconFor(this),
+ 0, this, SLOT (slotStatusOnline ()), actionSetAvailability, "actionOnline"));
+ actionSetAvailability->insert(new KAction (i18n ("Free to Chat"), protocol()->JabberKOSChatty.iconFor(this),
+ 0, this, SLOT (slotStatusChatty ()), actionSetAvailability, "actionChatty"));
+ actionSetAvailability->insert(new KAction (i18n ("Away"), protocol()->JabberKOSAway.iconFor(this),
+ 0, this, SLOT (slotStatusAway ()), actionSetAvailability, "actionAway"));
+ actionSetAvailability->insert(new KAction (i18n ("Extended Away"), protocol()->JabberKOSXA.iconFor(this),
+ 0, this, SLOT (slotStatusXA ()), actionSetAvailability, "actionXA"));
+ actionSetAvailability->insert(new KAction (i18n ("Do Not Disturb"), protocol()->JabberKOSDND.iconFor(this),
+ 0, this, SLOT (slotStatusDND ()), actionSetAvailability, "actionDND"));
+ actionSetAvailability->insert(new KAction (i18n ("Invisible"), protocol()->JabberKOSInvisible.iconFor(this),
+ 0, this, SLOT (slotStatusInvisible ()), actionSetAvailability, "actionInvisible"));
+
+ KActionMenu *actionSelectResource = new KActionMenu (i18n ("Select Resource"), "connect_no", this, "actionSelectResource");
+
+ // if the contact is online, display the resources we have for it,
+ // otherwise disable the menu
+ if (onlineStatus ().status () == Kopete::OnlineStatus::Offline)
+ {
+ actionSelectResource->setEnabled ( false );
+ }
+ else
+ {
+ QStringList items;
+ XMPP::ResourceList availableResources;
+
+ int activeItem = 0, i = 1;
+ const XMPP::Resource lockedResource = account()->resourcePool()->lockedResource ( mRosterItem.jid () );
+
+ // put default resource first
+ items.append (i18n ("Automatic (best/default resource)"));
+
+ account()->resourcePool()->findResources ( mRosterItem.jid (), availableResources );
+
+ XMPP::ResourceList::const_iterator resourcesEnd = availableResources.end ();
+ for ( XMPP::ResourceList::const_iterator it = availableResources.begin(); it != resourcesEnd; ++it, i++)
+ {
+ items.append ( (*it).name() );
+
+ if ( (*it).name() == lockedResource.name() )
+ activeItem = i;
+ }
+
+ // now go through the string list and add the resources with their icons
+ i = 0;
+ QStringList::const_iterator itemsEnd = items.end ();
+ for(QStringList::const_iterator it = items.begin(); it != itemsEnd; ++it)
+ {
+ if( i == activeItem )
+ {
+ actionSelectResource->insert ( new KAction( ( *it ), "button_ok", 0, this, SLOT( slotSelectResource() ),
+ actionSelectResource, QString::number( i ).latin1() ) );
+ }
+ else
+ {
+ /*
+ * Select icon, using bestResource() without lock for the automatic entry
+ * and the resources' respective status icons for the rest.
+ */
+ QIconSet iconSet ( !i ?
+ protocol()->resourceToKOS ( account()->resourcePool()->bestResource ( mRosterItem.jid(), false ) ).iconFor ( account () ) : protocol()->resourceToKOS ( *availableResources.find(*it) ).iconFor ( account () ));
+
+ actionSelectResource->insert ( new KAction( ( *it ), iconSet, 0, this, SLOT( slotSelectResource() ),
+ actionSelectResource, QString::number( i ).latin1() ) );
+ }
+
+ i++;
+ }
+
+ }
+
+ actionCollection->append( actionAuthorization );
+ actionCollection->append( actionSetAvailability );
+ actionCollection->append( actionSelectResource );
+
+
+#ifdef SUPPORT_JINGLE
+ KAction *actionVoiceCall = new KAction (i18n ("Voice call"), "voicecall", 0, this, SLOT (voiceCall ()), this, "jabber_voicecall");
+ actionVoiceCall->setEnabled( false );
+
+ actionCollection->append( actionVoiceCall );
+
+ // Check if the current contact support Voice calls, also honour lock by default.
+ JabberResource *bestResource = account()->resourcePool()->bestJabberResource( mRosterItem.jid() );
+ if( bestResource && bestResource->features().canVoice() )
+ {
+ actionVoiceCall->setEnabled( true );
+ }
+#endif
+
+ return actionCollection;
+}
+
+void JabberContact::handleIncomingMessage (const XMPP::Message & message)
+{
+ QString viewPlugin;
+ Kopete::Message *newMessage = 0L;
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Received Message Type:" << message.type () << endl;
+
+ // fetch message manager
+ JabberChatSession *mManager = manager ( message.from().resource (), Kopete::Contact::CanCreate );
+
+ // evaluate notifications
+ if ( message.type () != "error" )
+ {
+ if (!message.invite().isEmpty())
+ {
+ QString room=message.invite();
+ QString originalBody=message.body().isEmpty() ? QString() :
+ i18n( "The original message is : <i>\" %1 \"</i><br>" ).arg(QStyleSheet::escape(message.body()));
+ QString mes=i18n("<qt><i>%1</i> invited you to join the conference <b>%2</b><br>%3<br>"
+ "If you want to accept and join, just <b>enter your nickname</b> and press ok<br>"
+ "If you want to decline, press cancel</qt>")
+ .arg(message.from().full(), room , originalBody);
+
+ bool ok=false;
+ QString futureNewNickName = KInputDialog::getText( i18n( "Invited to a conference - Jabber Plugin" ),
+ mes, QString() , &ok , (mManager ? dynamic_cast<QWidget*>(mManager->view(false)) : 0) );
+ if ( !ok || !account()->isConnected() || futureNewNickName.isEmpty() )
+ return;
+
+ XMPP::Jid roomjid(room);
+ account()->client()->joinGroupChat( roomjid.host() , roomjid.user() , futureNewNickName );
+ return;
+ }
+ else if (message.body().isEmpty())
+ // Then here could be event notifications
+ {
+ if (message.containsEvent ( XMPP::CancelEvent ) )
+ mManager->receivedTypingMsg ( this, false );
+ else if (message.containsEvent ( XMPP::ComposingEvent ) )
+ mManager->receivedTypingMsg ( this, true );
+ else if (message.containsEvent ( XMPP::DisplayedEvent ) )
+ mManager->receivedEventNotification ( i18n("Message has been displayed") );
+ else if (message.containsEvent ( XMPP::DeliveredEvent ) )
+ mManager->receivedEventNotification ( i18n("Message has been delivered") );
+ else if (message.containsEvent ( XMPP::OfflineEvent ) )
+ {
+ mManager->receivedEventNotification( i18n("Message stored on the server, contact offline") );
+ }
+ else if (message.containsEvent ( XMPP::GoneEvent ) )
+ {
+ if(mManager->view( Kopete::Contact::CannotCreate ))
+ { //show an internal message if the user has not already closed his window
+ Kopete::Message m=Kopete::Message ( this, mManager->members(),
+ i18n("%1 has ended their participation in the chat session.").arg(metaContact()->displayName()),
+ Kopete::Message::Internal );
+ m.setImportance(Kopete::Message::Low);
+ mManager->view()->appendMessage ( m ); //use KopeteView::AppendMessage to bypass notifications
+ }
+ }
+ }
+ else
+ // Then here could be event notification requests
+ {
+ mRequestComposingEvent = message.containsEvent ( XMPP::ComposingEvent );
+ mRequestOfflineEvent = message.containsEvent ( XMPP::OfflineEvent );
+ mRequestDeliveredEvent = message.containsEvent ( XMPP::DeliveredEvent );
+ mRequestDisplayedEvent = message.containsEvent ( XMPP::DisplayedEvent);
+ mRequestGoneEvent= message.containsEvent ( XMPP::GoneEvent);
+ }
+ }
+
+ /**
+ * Don't display empty messages, these were most likely just carrying
+ * event notifications or other payload.
+ */
+ if ( message.body().isEmpty () && message.urlList().isEmpty () && message.xHTMLBody().isEmpty() && !message.xencrypted() )
+ return;
+
+ // determine message type
+ if (message.type () == "chat")
+ viewPlugin = "kopete_chatwindow";
+ else
+ viewPlugin = "kopete_emailwindow";
+
+ Kopete::ContactPtrList contactList;
+ contactList.append ( account()->myself () );
+
+ // check for errors
+ if ( message.type () == "error" )
+ {
+ newMessage = new Kopete::Message( message.timeStamp (), this, contactList,
+ i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"").
+ arg ( message.body () ).arg ( message.error().text ),
+ message.subject(), Kopete::Message::Inbound, Kopete::Message::PlainText, viewPlugin );
+ }
+ else
+ {
+ // store message id for outgoing notifications
+ mLastReceivedMessageId = message.id ();
+
+ // retrieve and reformat body
+ QString body = message.body ();
+ QString xHTMLBody;
+ if( !message.xencrypted().isEmpty () )
+ {
+ body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n");
+ }
+ else
+ {
+ xHTMLBody = message.xHTMLBody ();
+ }
+
+ // convert XMPP::Message into Kopete::Message
+ if (!xHTMLBody.isEmpty()) {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Received a xHTML message" << endl;
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList, xHTMLBody,
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::RichText, viewPlugin );
+ }
+ else if ( !body.isEmpty () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Received a plain text message" << endl;
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList, body,
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::PlainText, viewPlugin );
+ }
+ }
+
+ // append message to (eventually new) manager and preselect the originating resource
+ if ( newMessage )
+ {
+ mManager->appendMessage ( *newMessage, message.from().resource () );
+
+ delete newMessage;
+ }
+
+ // append URLs as separate messages
+ if ( !message.urlList().isEmpty () )
+ {
+ /*
+ * We need to copy it here because Iris returns a copy
+ * and we can't work with a returned copy in a for() loop.
+ */
+ XMPP::UrlList urlList = message.urlList();
+
+ for ( XMPP::UrlList::const_iterator it = urlList.begin (); it != urlList.end (); ++it )
+ {
+ QString description = (*it).desc().isEmpty() ? (*it).url() : QStyleSheet::escape ( (*it).desc() );
+ QString url = (*it).url ();
+
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList,
+ QString ( "<a href=\"%1\">%2</a>" ).arg ( url, description ),
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::RichText, viewPlugin );
+
+ mManager->appendMessage ( *newMessage, message.from().resource () );
+
+ delete newMessage;
+ }
+ }
+
+}
+
+void JabberContact::slotCheckVCard ()
+{
+ QDateTime cacheDate;
+ Kopete::ContactProperty cacheDateString = property ( protocol()->propVCardCacheTimeStamp );
+
+ // don't do anything while we are offline
+ if ( !account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ return;
+ }
+
+ if(!mDiscoDone)
+ {
+ if(transport()) //no need to disco if this is a legacy contact
+ mDiscoDone = true;
+ else if(!rosterItem().jid().node().isEmpty())
+ mDiscoDone = true; //contact with an @ are not transport for sure
+ else
+ {
+ mDiscoDone = true; //or it will happen twice, we don't want that.
+ //disco to see if it's not a transport
+ XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask());
+ QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished()));
+ jt->get(rosterItem().jid(), QString());
+ jt->go(true);
+ }
+ }
+
+
+ // avoid warning if key does not exist in configuration file
+ if ( cacheDateString.isNull () )
+ cacheDate = QDateTime::currentDateTime().addDays ( -2 );
+ else
+ cacheDate = QDateTime::fromString ( cacheDateString.value().toString (), Qt::ISODate );
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Cached vCard data for " << contactId () << " from " << cacheDate.toString () << endl;
+
+ if ( !mVCardUpdateInProgress && ( cacheDate.addDays ( 1 ) < QDateTime::currentDateTime () ) )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Scheduling update." << endl;
+
+ mVCardUpdateInProgress = true;
+
+ // current data is older than 24 hours, request a new one
+ QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetTimedVCard () ) );
+ }
+
+}
+
+void JabberContact::slotGetTimedVCard ()
+{
+ mVCardUpdateInProgress = false;
+
+ // check if we are still connected - eventually we lost our connection in the meantime
+ if ( !account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ // we are not connected, discard this update
+ return;
+ }
+
+ if(!mDiscoDone)
+ {
+ if(transport()) //no need to disco if this is a legacy contact
+ mDiscoDone = true;
+ else if(!rosterItem().jid().node().isEmpty())
+ mDiscoDone = true; //contact with an @ are not transport for sure
+ else
+ {
+ //disco to see if it's not a transport
+ XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account()->client()->rootTask());
+ QObject::connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished()));
+ jt->get(rosterItem().jid(), QString());
+ jt->go(true);
+ }
+ }
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting vCard for " << contactId () << " from update timer." << endl;
+
+ mVCardUpdateInProgress = true;
+
+ // request vCard
+ XMPP::JT_VCard *task = new XMPP::JT_VCard ( account()->client()->rootTask () );
+ // signal to ourselves when the vCard data arrived
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotGotVCard () ) );
+ task->get ( mRosterItem.jid () );
+ task->go ( true );
+
+}
+
+void JabberContact::slotGotVCard ()
+{
+
+ XMPP::JT_VCard * vCard = (XMPP::JT_VCard *) sender ();
+
+ // update timestamp of last vCard retrieval
+ if ( metaContact() && !metaContact()->isTemporary () )
+ {
+ setProperty ( protocol()->propVCardCacheTimeStamp, QDateTime::currentDateTime().toString ( Qt::ISODate ) );
+ }
+
+ mVCardUpdateInProgress = false;
+
+ if ( !vCard->success() )
+ {
+ /*
+ * A vCard for the user does not exist or the
+ * request was unsuccessful or incomplete.
+ * The timestamp was already updated when
+ * requesting the vCard, so it's safe to
+ * just do nothing here.
+ */
+ return;
+ }
+
+ setPropertiesFromVCard ( vCard->vcard () );
+
+}
+
+void JabberContact::slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
+{
+
+ /*
+ * Checking the last activity only makes sense if a contact is offline.
+ * So, this check should only be done in the following cases:
+ * - Kopete goes online for the first time and this contact is offline, or
+ * - Kopete is already online and this contact went offline.
+ *
+ * Since Kopete already takes care of maintaining the lastSeen property
+ * if the contact changes its state while we are online, we don't need
+ * to query its activity after we are already connected.
+ */
+
+ if ( onlineStatus().isDefinitelyOnline () )
+ {
+ // Kopete already deals with lastSeen if the contact is online
+ return;
+ }
+
+ if ( ( oldStatus.status () == Kopete::OnlineStatus::Connecting ) && newStatus.isDefinitelyOnline () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Scheduling request for last activity for " << mRosterItem.jid().bare () << endl;
+
+ QTimer::singleShot ( account()->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetTimedLastActivity () ) );
+ }
+
+}
+
+void JabberContact::slotGetTimedLastActivity ()
+{
+ /*
+ * We have been called from @ref slotCheckLastActivity.
+ * We could have lost our connection in the meantime,
+ * so make sure we are online. Additionally, the contact
+ * itself could have gone online, so make sure it is
+ * still offline. (otherwise the last seen property is
+ * maintained by Kopete)
+ */
+
+ if ( onlineStatus().isDefinitelyOnline () )
+ {
+ // Kopete already deals with setting lastSeen if the contact is online
+ return;
+ }
+
+ if ( account()->myself()->onlineStatus().isDefinitelyOnline () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting last activity from timer for " << mRosterItem.jid().bare () << endl;
+
+ XMPP::JT_GetLastActivity *task = new XMPP::JT_GetLastActivity ( account()->client()->rootTask () );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotGotLastActivity () ) );
+ task->get ( mRosterItem.jid () );
+ task->go ( true );
+ }
+
+}
+
+void JabberContact::slotGotLastActivity ()
+{
+ XMPP::JT_GetLastActivity *task = (XMPP::JT_GetLastActivity *) sender ();
+
+ if ( task->success () )
+ {
+ setProperty ( protocol()->propLastSeen, QDateTime::currentDateTime().addSecs ( -task->seconds () ) );
+ if( !task->message().isEmpty() )
+ {
+ setProperty( protocol()->propAwayMessage, task->message() );
+ }
+ }
+
+}
+
+void JabberContact::slotSendVCard()
+{
+ XMPP::VCard vCard;
+ XMPP::VCard::AddressList addressList;
+ XMPP::VCard::EmailList emailList;
+ XMPP::VCard::PhoneList phoneList;
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ // General information
+ vCard.setNickName (property(protocol()->propNickName).value().toString());
+ vCard.setFullName (property(protocol()->propFullName).value().toString());
+ vCard.setJid (property(protocol()->propJid).value().toString());
+ vCard.setBdayStr (property(protocol()->propBirthday).value().toString());
+ vCard.setTimezone (property(protocol()->propTimezone).value().toString());
+ vCard.setUrl (property(protocol()->propHomepage).value().toString());
+
+ // home address tab
+ XMPP::VCard::Address homeAddress;
+
+ homeAddress.home = true;
+ homeAddress.street = property(protocol()->propHomeStreet).value().toString();
+ homeAddress.extaddr = property(protocol()->propHomeExtAddr).value().toString();
+ homeAddress.pobox = property(protocol()->propHomePOBox).value().toString();
+ homeAddress.locality = property(protocol()->propHomeCity).value().toString();
+ homeAddress.pcode = property(protocol()->propHomePostalCode).value().toString();
+ homeAddress.country = property(protocol()->propHomeCountry).value().toString();
+
+ // work address tab
+ XMPP::VCard::Address workAddress;
+
+ workAddress.work = true;
+ workAddress.street = property(protocol()->propWorkStreet).value().toString();
+ workAddress.extaddr = property(protocol()->propWorkExtAddr).value().toString();
+ workAddress.pobox = property(protocol()->propWorkPOBox).value().toString();
+ workAddress.locality = property(protocol()->propWorkCity).value().toString();
+ workAddress.pcode = property(protocol()->propWorkPostalCode).value().toString();
+ workAddress.country = property(protocol()->propWorkCountry).value().toString();
+
+ addressList.append(homeAddress);
+ addressList.append(workAddress);
+
+ vCard.setAddressList(addressList);
+
+ // home email
+ XMPP::VCard::Email homeEmail;
+
+ homeEmail.home = true;
+ homeEmail.userid = property(protocol()->propEmailAddress).value().toString();
+
+ // work email
+ XMPP::VCard::Email workEmail;
+
+ workEmail.work = true;
+ workEmail.userid = property(protocol()->propWorkEmailAddress).value().toString();
+
+ emailList.append(homeEmail);
+ emailList.append(workEmail);
+
+ vCard.setEmailList(emailList);
+
+ // work information tab
+ XMPP::VCard::Org org;
+ org.name = property(protocol()->propCompanyName).value().toString();
+ org.unit = QStringList::split(",", property(protocol()->propCompanyDepartement).value().toString());
+ vCard.setOrg(org);
+ vCard.setTitle (property(protocol()->propCompanyPosition).value().toString());
+ vCard.setRole (property(protocol()->propCompanyRole).value().toString());
+
+ // phone numbers tab
+ XMPP::VCard::Phone phoneHome;
+ phoneHome.home = true;
+ phoneHome.number = property(protocol()->propPrivatePhone).value().toString();
+
+ XMPP::VCard::Phone phoneWork;
+ phoneWork.work = true;
+ phoneWork.number = property(protocol()->propWorkPhone).value().toString();
+
+ XMPP::VCard::Phone phoneFax;
+ phoneFax.fax = true;
+ phoneFax.number = property(protocol()->propPhoneFax).value().toString();
+
+ XMPP::VCard::Phone phoneCell;
+ phoneCell.cell = true;
+ phoneCell.number = property(protocol()->propPrivateMobilePhone).value().toString();
+
+ phoneList.append(phoneHome);
+ phoneList.append(phoneWork);
+ phoneList.append(phoneFax);
+ phoneList.append(phoneCell);
+
+ vCard.setPhoneList(phoneList);
+
+ // about tab
+ vCard.setDesc(property(protocol()->propAbout).value().toString());
+
+ // Set contact photo as a binary value (if he has set a photo)
+ if( hasProperty( protocol()->propPhoto.key() ) )
+ {
+ QString photoPath = property( protocol()->propPhoto ).value().toString();
+ QImage image( photoPath );
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ image.save( &buffer, "PNG" );
+ vCard.setPhoto( ba );
+ }
+
+ vCard.setVersion("3.0");
+ vCard.setProdId("Kopete");
+
+ XMPP::JT_VCard *task = new XMPP::JT_VCard (account()->client()->rootTask ());
+ // signal to ourselves when the vCard data arrived
+ QObject::connect (task, SIGNAL (finished ()), this, SLOT (slotSentVCard ()));
+ task->set (vCard);
+ task->go (true);
+}
+
+void JabberContact::setPhoto( const QString &photoPath )
+{
+ QImage contactPhoto(photoPath);
+ QString newPhotoPath = photoPath;
+ if(contactPhoto.width() > 96 || contactPhoto.height() > 96)
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ // Scale and crop the picture.
+ contactPhoto = contactPhoto.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 96, 96);
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 96, 96);
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+ else if (contactPhoto.width() < 32 || contactPhoto.height() < 32)
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ // Scale and crop the picture.
+ contactPhoto = contactPhoto.smoothScale( 32, 32, QImage::ScaleMin );
+ // crop image if not square
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, 32, 32);
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, 32, 32);
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+ else if (contactPhoto.width() != contactPhoto.height())
+ {
+ // Save image to a new location if the image isn't the correct format.
+ QString newLocation( locateLocal( "appdata", "jabberphotos/"+ KURL(photoPath).fileName().lower() ) );
+
+ if(contactPhoto.width() < contactPhoto.height())
+ contactPhoto = contactPhoto.copy((contactPhoto.width()-contactPhoto.height())/2, 0, contactPhoto.height(), contactPhoto.height());
+ else if (contactPhoto.width() > contactPhoto.height())
+ contactPhoto = contactPhoto.copy(0, (contactPhoto.height()-contactPhoto.width())/2, contactPhoto.height(), contactPhoto.height());
+
+ // Use the cropped/scaled image now.
+ if(!contactPhoto.save(newLocation, "PNG"))
+ newPhotoPath = QString::null;
+ else
+ newPhotoPath = newLocation;
+ }
+
+ setProperty( protocol()->propPhoto, newPhotoPath );
+}
+
+void JabberContact::slotSentVCard ()
+{
+
+}
+
+void JabberContact::slotChatSessionDeleted ( QObject *sender )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Message manager deleted, collecting the pieces..." << endl;
+
+ JabberChatSession *manager = static_cast<JabberChatSession *>(sender);
+
+ mManagers.remove ( mManagers.find ( manager ) );
+
+}
+
+JabberChatSession *JabberContact::manager ( Kopete::ContactPtrList chatMembers, Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << endl;
+
+ Kopete::ChatSession *_manager = Kopete::ChatSessionManager::self()->findChatSession ( account()->myself(), chatMembers, protocol() );
+ JabberChatSession *manager = dynamic_cast<JabberChatSession*>( _manager );
+
+ /*
+ * If we didn't find a message manager for this contact,
+ * instantiate a new one if we are allowed to. (otherwise return 0)
+ */
+ if ( !manager && canCreate )
+ {
+ XMPP::Jid jid = rosterItem().jid();
+
+ /*
+ * If we have no hardwired JID, set any eventually
+ * locked resource as preselected resource.
+ * If there is no locked resource, the resource field
+ * will stay empty.
+ */
+ if ( jid.resource().isEmpty () )
+ jid.setResource ( account()->resourcePool()->lockedResource ( jid ).name () );
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No manager found, creating a new one with resource '" << jid.resource () << "'" << endl;
+
+ manager = new JabberChatSession ( protocol(), static_cast<JabberBaseContact *>(account()->myself()), chatMembers, jid.resource () );
+ connect ( manager, SIGNAL ( destroyed ( QObject * ) ), this, SLOT ( slotChatSessionDeleted ( QObject * ) ) );
+ mManagers.append ( manager );
+ }
+
+ return manager;
+
+}
+
+Kopete::ChatSession *JabberContact::manager ( Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << endl;
+
+ Kopete::ContactPtrList chatMembers;
+ chatMembers.append ( this );
+
+ return manager ( chatMembers, canCreate );
+
+}
+
+JabberChatSession *JabberContact::manager ( const QString &resource, Kopete::Contact::CanCreateFlags canCreate )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "called, canCreate: " << canCreate << ", Resource: '" << resource << "'" << endl;
+
+ /*
+ * First of all, see if we already have a manager matching
+ * the requested resource or if there are any managers with
+ * an empty resource.
+ */
+ if ( !resource.isEmpty () )
+ {
+ for ( JabberChatSession *mManager = mManagers.first (); mManager; mManager = mManagers.next () )
+ {
+ if ( mManager->resource().isEmpty () || ( mManager->resource () == resource ) )
+ {
+ // we found a matching manager, return this one
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Found an existing message manager for this resource." << endl;
+ return mManager;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No manager found for this resource, creating a new one." << endl;
+
+ /*
+ * If we have come this far, we were either supposed to create
+ * a manager with a preselected resource but have found
+ * no available manager. (not even one with an empty resource)
+ * This means, we will have to create a new one with a
+ * preselected resource.
+ */
+ Kopete::ContactPtrList chatmembers;
+ chatmembers.append ( this );
+ JabberChatSession *manager = new JabberChatSession ( protocol(),
+ static_cast<JabberBaseContact *>(account()->myself()),
+ chatmembers, resource );
+ connect ( manager, SIGNAL ( destroyed ( QObject * ) ), this, SLOT ( slotChatSessionDeleted ( QObject * ) ) );
+ mManagers.append ( manager );
+
+ return manager;
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Resource is empty, grabbing first available manager." << endl;
+
+ /*
+ * The resource is empty, so just return first available manager.
+ */
+ return dynamic_cast<JabberChatSession *>( manager ( canCreate ) );
+
+}
+
+void JabberContact::deleteContact ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing user " << contactId () << endl;
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ /*
+ * Follow the recommendation of
+ * JEP-0162: Best Practices for Roster and Subscription Management
+ * http://www.jabber.org/jeps/jep-0162.html#removal
+ */
+
+ bool remove_from_roster=false;
+
+ if( mRosterItem.subscription().type() == XMPP::Subscription::Both || mRosterItem.subscription().type() == XMPP::Subscription::From )
+ {
+ int result = KMessageBox::questionYesNoCancel (Kopete::UI::Global::mainWidget(),
+ i18n ( "Do you also want to remove the authorization from user %1 to see your status?" ).
+ arg ( mRosterItem.jid().bare () ), i18n ("Notification"),
+ KStdGuiItem::del (), i18n("Keep"), "JabberRemoveAuthorizationOnDelete" );
+ if(result == KMessageBox::Yes )
+ remove_from_roster = true;
+ else if( result == KMessageBox::Cancel)
+ return;
+ }
+ else if( mRosterItem.subscription().type() == XMPP::Subscription::None || mRosterItem.subscription().type() == XMPP::Subscription::To )
+ remove_from_roster = true;
+
+ if( remove_from_roster )
+ {
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+ rosterTask->remove ( mRosterItem.jid () );
+ rosterTask->go ( true );
+ }
+ else
+ {
+ sendSubscription("unsubscribe");
+
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+ rosterTask->set ( mRosterItem.jid (), QString() , QStringList() );
+ rosterTask->go (true);
+ }
+
+}
+
+void JabberContact::sync ( unsigned int )
+{
+ // if we are offline or this is a temporary contact or we should not synch, don't bother
+ if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () || metaContact() == Kopete::ContactList::self()->myself() )
+ return;
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << contactId () /*<< " - " <<kdBacktrace()*/ << endl;
+
+ if(!m_syncTimer)
+ {
+ m_syncTimer=new QTimer(this);
+ connect(m_syncTimer, SIGNAL(timeout()) , this , SLOT(slotDelayedSync()));
+ }
+ m_syncTimer->start(2*1000,true);
+ /*
+ the sync operation is delayed, because when we are doing a move to group operation,
+ kopete first add the contact to the group, then removes it.
+ Theses two operations should anyway be done in only one pass.
+
+ if there is two jabber contact in one metacontact, this may result in an infinite change of
+ groups between theses two contacts, and the server is being flooded.
+ */
+}
+
+void JabberContact::slotDelayedSync( )
+{
+ m_syncTimer->deleteLater();
+ m_syncTimer=0L;
+ // if we are offline or this is a temporary contact or we should not synch, don't bother
+ if ( dontSync () || !account()->isConnected () || metaContact()->isTemporary () )
+ return;
+
+ bool changed=metaContact()->displayName() != mRosterItem.name();
+
+
+ QStringList groups;
+ Kopete::GroupList groupList = metaContact ()->groups ();
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Synchronizing contact " << contactId () << endl;
+
+ for ( Kopete::Group * g = groupList.first (); g; g = groupList.next () )
+ {
+ if ( g->type () != Kopete::Group::TopLevel )
+ groups += g->displayName ();
+ }
+
+ if(mRosterItem.groups() != groups)
+ {
+ changed=true;
+ mRosterItem.setGroups ( groups );
+ }
+
+ if(!changed)
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "contact has not changed, abort sync" << endl;
+ return;
+ }
+
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+
+ rosterTask->set ( mRosterItem.jid (), metaContact()->displayName (), mRosterItem.groups () );
+ rosterTask->go (true);
+
+}
+
+void JabberContact::sendFile ( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ // if the file location is null, then get it from a file open dialog
+ if ( !sourceURL.isValid () )
+ filePath = KFileDialog::getOpenFileName( QString::null , "*", 0L, i18n ( "Kopete File Transfer" ) );
+ else
+ filePath = sourceURL.path(-1);
+
+ QFile file ( filePath );
+
+ if ( file.exists () )
+ {
+ // send the file
+ new JabberFileTransfer ( account (), this, filePath );
+ }
+
+}
+
+
+void JabberContact::slotSendAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "(Re)send auth " << contactId () << endl;
+
+ sendSubscription ("subscribed");
+
+}
+
+void JabberContact::slotRequestAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "(Re)request auth " << contactId () << endl;
+
+ sendSubscription ("subscribe");
+
+}
+
+void JabberContact::slotRemoveAuth ()
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Remove auth " << contactId () << endl;
+
+ sendSubscription ("unsubscribed");
+
+}
+
+void JabberContact::sendSubscription ( const QString& subType )
+{
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () );
+
+ task->sub ( mRosterItem.jid().full (), subType );
+ task->go ( true );
+
+}
+
+void JabberContact::slotSelectResource ()
+{
+ int currentItem = QString ( static_cast<const KAction *>( sender() )->name () ).toUInt ();
+
+ /*
+ * Warn the user if there is already an active chat window.
+ * The resource selection will only apply for newly opened
+ * windows.
+ */
+ if ( manager ( Kopete::Contact::CannotCreate ) != 0 )
+ {
+ KMessageBox::queuedMessageBox ( Kopete::UI::Global::mainWidget (),
+ KMessageBox::Information,
+ i18n ("You have preselected a resource for contact %1, "
+ "but you still have open chat windows for this contact. "
+ "The preselected resource will only apply to newly opened "
+ "chat windows.").arg ( contactId () ),
+ i18n ("Jabber Resource Selector") );
+ }
+
+ if (currentItem == 0)
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing active resource, trusting bestResource()." << endl;
+
+ account()->resourcePool()->removeLock ( rosterItem().jid() );
+ }
+ else
+ {
+ QString selectedResource = static_cast<const KAction *>(sender())->text();
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Moving to resource " << selectedResource << endl;
+
+ account()->resourcePool()->lockToResource ( rosterItem().jid() , XMPP::Resource ( selectedResource ) );
+ }
+
+}
+
+void JabberContact::sendPresence ( const XMPP::Status status )
+{
+
+ if ( !account()->isConnected () )
+ {
+ account()->errorConnectFirst ();
+ return;
+ }
+
+ XMPP::Status newStatus = status;
+
+ // honour our priority
+ if(newStatus.isAvailable())
+ newStatus.setPriority ( account()->configGroup()->readNumEntry ( "Priority", 5 ) );
+
+ XMPP::JT_Presence * task = new XMPP::JT_Presence ( account()->client()->rootTask () );
+
+ task->pres ( bestAddress (), newStatus);
+ task->go ( true );
+
+}
+
+
+void JabberContact::slotStatusOnline ()
+{
+
+ XMPP::Status status;
+ status.setShow("");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusChatty ()
+{
+
+ XMPP::Status status;
+ status.setShow ("chat");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusAway ()
+{
+
+ XMPP::Status status;
+ status.setShow ("away");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusXA ()
+{
+
+ XMPP::Status status;
+ status.setShow ("xa");
+
+ sendPresence ( status );
+
+}
+
+void JabberContact::slotStatusDND ()
+{
+
+ XMPP::Status status;
+ status.setShow ("dnd");
+
+ sendPresence ( status );
+
+
+}
+
+void JabberContact::slotStatusInvisible ()
+{
+
+ XMPP::Status status;
+ status.setIsAvailable( false );
+
+ sendPresence ( status );
+
+}
+
+bool JabberContact::isContactRequestingEvent( XMPP::MsgEvent event )
+{
+ if ( event == OfflineEvent )
+ return mRequestOfflineEvent;
+ else if ( event == DeliveredEvent )
+ return mRequestDeliveredEvent;
+ else if ( event == DisplayedEvent )
+ return mRequestDisplayedEvent;
+ else if ( event == ComposingEvent )
+ return mRequestComposingEvent;
+ else if ( event == CancelEvent )
+ return mRequestComposingEvent;
+ else if ( event == GoneEvent )
+ return mRequestGoneEvent;
+ else
+ return false;
+}
+
+QString JabberContact::lastReceivedMessageId () const
+{
+ return mLastReceivedMessageId;
+}
+
+void JabberContact::voiceCall( )
+{
+#ifdef SUPPORT_JINGLE
+ Jid jid = mRosterItem.jid();
+
+ // It's honour lock by default.
+ JabberResource *bestResource = account()->resourcePool()->bestJabberResource( jid );
+ if( bestResource )
+ {
+ if( jid.resource().isEmpty() )
+ {
+ // If the jid resource is empty, get the JID from best resource for this contact.
+ jid = bestResource->jid();
+ }
+
+ // Check if the voice caller exist and the current resource support voice.
+ if( account()->voiceCaller() && bestResource->features().canVoice() )
+ {
+ JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog( jid, account()->voiceCaller() );
+ voiceDialog->show();
+ voiceDialog->start();
+ }
+#if 0
+ if( account()->sessionManager() && bestResource->features().canVoice() )
+ {
+ JingleVoiceSession *session = static_cast<JingleVoiceSession*>(account()->sessionManager()->createSession("http://www.google.com/session/phone", jid));
+
+ JingleVoiceSessionDialog *voiceDialog = new JingleVoiceSessionDialog(session);
+ voiceDialog->show();
+ voiceDialog->start();
+ }
+#endif
+ }
+ else
+ {
+ // Shouldn't never go there.
+ }
+#endif
+}
+
+void JabberContact::slotDiscoFinished( )
+{
+ mDiscoDone = true;
+ JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
+
+ bool is_transport=false;
+ QString tr_type;
+
+ if ( jt->success() )
+ {
+ QValueList<XMPP::DiscoItem::Identity> identities = jt->item().identities();
+ QValueList<XMPP::DiscoItem::Identity>::Iterator it;
+ for ( it = identities.begin(); it != identities.end(); ++it )
+ {
+ XMPP::DiscoItem::Identity ident=*it;
+ if(ident.category == "gateway")
+ {
+ is_transport=true;
+ tr_type=ident.type;
+ //name=ident.name;
+
+ break; //(we currently only support gateway)
+ }
+ else if (ident.category == "service")
+ {
+ //The ApaSMSAgent is reporting itself as service (instead of gateway) which is broken.
+ //we anyway support it. See bug 127811
+ if(ident.type == "sms")
+ {
+ is_transport=true;
+ tr_type=ident.type;
+ }
+ }
+ }
+ }
+
+ if(is_transport && !transport())
+ { //ok, we are not a contact, we are a transport....
+
+ XMPP::RosterItem ri = rosterItem();
+ Kopete::MetaContact *mc=metaContact();
+ JabberAccount *parentAccount=account();
+ Kopete::OnlineStatus status=onlineStatus();
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << ri.jid().full() << " is not a contact but a gateway - " << this << endl;
+
+ if( Kopete::AccountManager::self()->findAccount( protocol()->pluginId() , account()->accountId() + "/" + ri.jid().bare() ) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "oops, transport already exists, abort operation " << endl;
+ return;
+ }
+
+ delete this; //we are not a contact i said !
+
+ if(mc->contacts().count() == 0)
+ Kopete::ContactList::self()->removeMetaContact( mc );
+
+ //we need to create the transport when 'this' is already deleted, so transport->myself() will not conflict with it
+ JabberTransport *transport = new JabberTransport( parentAccount , ri , tr_type );
+ if(!Kopete::AccountManager::self()->registerAccount( transport ))
+ return;
+ transport->myself()->setOnlineStatus( status ); //push back the online status
+ return;
+ }
+}
+
+
+
+#include "jabbercontact.moc"
diff --git a/kopete/protocols/jabber/jabbercontact.h b/kopete/protocols/jabber/jabbercontact.h
new file mode 100644
index 00000000..a7a3b024
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercontact.h
@@ -0,0 +1,266 @@
+ /*
+ * jabbercontact.cpp - Regular Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERCONTACT_H
+#define JABBERCONTACT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "jabberbasecontact.h"
+#include "xmpp_vcard.h"
+
+#include "kopetechatsession.h" // needed for silly Kopete::ContactPtrList
+
+class JabberChatSession;
+class QTimer;
+
+class JabberContact : public JabberBaseContact
+{
+
+Q_OBJECT
+
+public:
+
+ JabberContact (const XMPP::RosterItem &rosterItem,
+ Kopete::Account *account, Kopete::MetaContact * mc, const QString &legacyId = QString());
+
+ ~JabberContact();
+
+ /**
+ * Create custom context menu items for the contact
+ * FIXME: implement manager version here?
+ */
+ QPtrList<KAction> *customContextMenuActions ();
+
+ /**
+ * Start a rename request.
+ */
+ void rename ( const QString &newName );
+
+ /**
+ * Deal with an incoming message for this contact.
+ */
+ void handleIncomingMessage ( const XMPP::Message &message );
+
+ /**
+ * Create a message manager for this contact.
+ * This variant is a pure single-contact version and
+ * not suitable for groupchat, as it only looks for
+ * managers with ourselves in the contact list.
+ */
+ Kopete::ChatSession *manager ( Kopete::Contact::CanCreateFlags );
+
+
+ bool isContactRequestingEvent( XMPP::MsgEvent event );
+
+ QString lastReceivedMessageId () const;
+
+public slots:
+
+ /**
+ * Remove this contact from the roster
+ */
+ void deleteContact ();
+
+ /**
+ * Sync Groups with server
+ *
+ * operations are alctually performed in sloDelayedSync()
+ */
+ void sync(unsigned int);
+
+ /**
+ * This is the JabberContact level slot for sending files.
+ *
+ * @param sourceURL The actual KURL of the file you are sending
+ * @param fileName (Optional) An alternate name for the file - what the
+ * receiver will see
+ * @param fileSize (Optional) Size of the file being sent. Used when sending
+ * a nondeterminate file size (such as over a socket)
+ */
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+ /**
+ * Update the vCard on the server.
+ * @todo is that still used ?
+ */
+ void slotSendVCard();
+
+ /**
+ * Set contact photo.
+ * @param path Path to the photo.
+ */
+ void setPhoto(const QString &photoPath);
+
+ /**
+ * this will start a voice call to the contact
+ */
+ void voiceCall();
+
+private slots:
+
+ /**
+ * Send type="subscribed" to contact
+ */
+ void slotSendAuth ();
+
+ /**
+ * Send type="subscribe" to contact
+ */
+ void slotRequestAuth ();
+
+ /**
+ * Send type="unsubscribed" to contact
+ */
+ void slotRemoveAuth ();
+
+ /**
+ * Change this contact's status
+ */
+ void slotStatusOnline ();
+ void slotStatusChatty ();
+ void slotStatusAway ();
+ void slotStatusXA ();
+ void slotStatusDND ();
+ void slotStatusInvisible ();
+
+ /**
+ * Select a new resource for the contact
+ */
+ void slotSelectResource ();
+
+ void slotChatSessionDeleted ( QObject *sender );
+
+ /**
+ * Check if cached vCard is recent.
+ * Triggered as soon as Kopete changes its online state.
+ */
+ void slotCheckVCard ();
+
+ /**
+ * Triggered from a timer, requests the vCard.
+ * Timer is initiated by @ref slotCheckVCard.
+ */
+ void slotGetTimedVCard ();
+
+ /**
+ * Passes vCard on to parsing function.
+ */
+ void slotGotVCard ();
+
+ /**
+ * Get information about last activity of the contact.
+ * Triggered as soon as Kopete goes online or the contact goes offline.
+ */
+ void slotCheckLastActivity ( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & );
+
+ /**
+ * Triggered from a timer, requests last activity information.
+ * Timer is initiated by @ref slotCheckLastActivity.
+ */
+ void slotGetTimedLastActivity ();
+
+ /**
+ * Updates activity information.
+ */
+ void slotGotLastActivity ();
+
+ /**
+ * Display a error message if the vCard sent was unsuccesful.
+ */
+ void slotSentVCard();
+
+ /**
+ * The service discovery on that contact is finished
+ */
+ void slotDiscoFinished();
+
+ /**
+ * actually perform operations of sync() with a delay.
+ * slot received by the syncTimer.
+ */
+ void slotDelayedSync();
+private:
+
+ /**
+ * Create a message manager for this contact.
+ * This variant is a pure single-contact version and
+ * not suitable for groupchat, as it only looks for
+ * managers with ourselves in the contact list.
+ * Additionally to the version above, this one adds
+ * a resource constraint that has to be matched by
+ * the manager. If a new manager is created, the given
+ * resource is preselected.
+ */
+ JabberChatSession *manager ( const QString &resource, Kopete::Contact::CanCreateFlags );
+
+ /**
+ * Create a message manager for this contact.
+ * This version is suitable for group chat as it
+ * looks for a message manager with a given
+ * list of contacts as members.
+ */
+ JabberChatSession *manager ( Kopete::ContactPtrList chatMembers, Kopete::Contact::CanCreateFlags );
+
+ /**
+ * Sends subscription messages.
+ */
+ void sendSubscription (const QString& subType);
+
+ /**
+ * Sends a presence packet to this contact
+ */
+ void sendPresence ( const XMPP::Status status );
+
+ /**
+ * This variable keeps a list of message managers.
+ * It is required to locate message managers by
+ * resource name, if one account is interacting
+ * with several resources of the same contact
+ * at the same time. Note that this does *not*
+ * apply to group chats, so this variable
+ * only contains classes of type JabberChatSession.
+ * The casts in manager() and slotChatSessionDeleted()
+ * are thus legal.
+ */
+ QPtrList<JabberChatSession> mManagers;
+
+ /**
+ * Indicates whether the vCard is currently
+ * being updated or not.
+ */
+ bool mVCardUpdateInProgress :1;
+
+ bool mRequestComposingEvent :1;
+ bool mRequestOfflineEvent :1;
+ bool mRequestDisplayedEvent :1;
+ bool mRequestDeliveredEvent :1;
+ bool mRequestGoneEvent :1;
+ /**
+ * tell if the disco#info has been done for this contact.
+ */
+ bool mDiscoDone :1;
+
+ QString mLastReceivedMessageId;
+ QTimer *m_syncTimer;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabbercontactpool.cpp b/kopete/protocols/jabber/jabbercontactpool.cpp
new file mode 100644
index 00000000..736c6045
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercontactpool.cpp
@@ -0,0 +1,355 @@
+ /*
+ * jabbercontactpool.cpp
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabbercontactpool.h"
+
+#include <qptrlist.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kopeteaccountmanager.h>
+#include <kopetecontactlist.h>
+#include "kopeteuiglobal.h"
+#include "jabberprotocol.h"
+#include "jabberbasecontact.h"
+#include "jabbercontact.h"
+#include "jabbergroupcontact.h"
+#include "jabbergroupmembercontact.h"
+#include "jabberresourcepool.h"
+#include "jabberaccount.h"
+#include "jabbertransport.h"
+
+JabberContactPool::JabberContactPool ( JabberAccount *account )
+{
+
+ // automatically delete all contacts in the pool upon removal
+ mPool.setAutoDelete (true);
+
+ mAccount = account;
+
+}
+
+JabberContactPool::~JabberContactPool ()
+{
+}
+
+JabberContactPoolItem *JabberContactPool::findPoolItem ( const XMPP::RosterItem &contact )
+{
+
+ // see if the contact already exists
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().full().lower() == contact.jid().full().lower() )
+ {
+ return mContactItem;
+ }
+ }
+
+ return 0;
+
+}
+
+JabberContact *JabberContactPool::addContact ( const XMPP::RosterItem &contact, Kopete::MetaContact *metaContact, bool dirty )
+{
+ // see if the contact already exists
+ JabberContactPoolItem *mContactItem = findPoolItem ( contact );
+ if ( mContactItem)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating existing contact " << contact.jid().full() << " - " << mContactItem->contact() << endl;
+
+ // It exists, update it.
+ mContactItem->contact()->updateContact ( contact );
+ mContactItem->setDirty ( dirty );
+
+ JabberContact *retval = dynamic_cast<JabberContact *>(mContactItem->contact ());
+
+ if ( !retval )
+ {
+ KMessageBox::error ( Kopete::UI::Global::mainWidget (),
+ "Fatal error in the Jabber contact pool. Please restart Kopete and submit a debug log "
+ "of your session to http://bugs.kde.org.",
+ "Fatal Jabber Error" );
+ }
+
+ return retval;
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Adding new contact " << contact.jid().full() << endl;
+
+ JabberTransport *transport=0l;
+ QString legacyId;
+ //find if the contact should be added to a transport.
+ if(mAccount->transports().contains(contact.jid().domain()))
+ {
+ transport=mAccount->transports()[contact.jid().domain()];
+ legacyId=transport->legacyId( contact.jid() );
+ }
+
+ // create new contact instance and add it to the dictionary
+ JabberContact *newContact = new JabberContact ( contact, transport ? (Kopete::Account*)transport : (Kopete::Account*)mAccount, metaContact , legacyId );
+ JabberContactPoolItem *newContactItem = new JabberContactPoolItem ( newContact );
+ connect ( newContact, SIGNAL ( contactDestroyed ( Kopete::Contact * ) ), this, SLOT ( slotContactDestroyed ( Kopete::Contact * ) ) );
+ newContactItem->setDirty ( dirty );
+ mPool.append ( newContactItem );
+
+ return newContact;
+
+}
+
+JabberBaseContact *JabberContactPool::addGroupContact ( const XMPP::RosterItem &contact, bool roomContact, Kopete::MetaContact *metaContact, bool dirty )
+{
+
+ XMPP::RosterItem mContact ( roomContact ? contact.jid().userHost () : contact.jid().full() );
+
+ // see if the contact already exists
+ JabberContactPoolItem *mContactItem = findPoolItem ( mContact );
+ if ( mContactItem)
+ {
+ if(mContactItem->contact()->inherits(roomContact ?
+ (const char*)("JabberGroupContact") : (const char*)("JabberGroupMemberContact") ) )
+ {
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating existing contact " << mContact.jid().full() << endl;
+
+ // It exists, update it.
+ mContactItem->contact()->updateContact ( mContact );
+ mContactItem->setDirty ( dirty );
+
+ //we must tell to the originating function that no new contact has been added
+ return 0L;//mContactItem->contact ();
+ }
+ else
+ {
+ //this happen if we receive a MUC invitaiton: when the invitaiton is received, it's from the muc itself
+ //and then kopete will create a temporary contact for it. but it will not be a good contact.
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Bad contact will be removed and re-added " << mContact.jid().full() << endl;
+ Kopete::MetaContact *old_mc=mContactItem->contact()->metaContact();
+ delete mContactItem->contact();
+ mContactItem = 0L;
+ if(old_mc->contacts().isEmpty() && old_mc!=metaContact)
+ {
+ Kopete::ContactList::self()->removeMetaContact( old_mc );
+ }
+
+ }
+
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Adding new contact " << mContact.jid().full() << endl;
+
+ // create new contact instance and add it to the dictionary
+ JabberBaseContact *newContact;
+
+ if ( roomContact )
+ newContact = new JabberGroupContact ( contact, mAccount, metaContact );
+ else
+ newContact = new JabberGroupMemberContact ( contact, mAccount, metaContact );
+
+ JabberContactPoolItem *newContactItem = new JabberContactPoolItem ( newContact );
+
+ connect ( newContact, SIGNAL ( contactDestroyed ( Kopete::Contact * ) ), this, SLOT ( slotContactDestroyed ( Kopete::Contact * ) ) );
+
+ newContactItem->setDirty ( dirty );
+ mPool.append ( newContactItem );
+
+ return newContact;
+
+}
+
+void JabberContactPool::removeContact ( const XMPP::Jid &jid )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing contact " << jid.full() << endl;
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().full().lower() == jid.full().lower() )
+ {
+ /*
+ * The following deletion will cause slotContactDestroyed()
+ * to be called, which will clean the up the list.
+ */
+ if(mContactItem->contact())
+ {
+ Kopete::MetaContact *mc=mContactItem->contact()->metaContact();
+ delete mContactItem->contact ();
+ if(mc && mc->contacts().isEmpty())
+ {
+ Kopete::ContactList::self()->removeMetaContact(mc) ;
+ }
+ }
+ return;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "WARNING: No match found!" << endl;
+
+}
+
+void JabberContactPool::slotContactDestroyed ( Kopete::Contact *contact )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Contact deleted, collecting the pieces..." << endl;
+
+ JabberBaseContact *jabberContact = static_cast<JabberBaseContact *>( contact );
+ //WARNING this ptr is not usable, we are in the Kopete::Contact destructor
+
+ // remove contact from the pool
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact() == jabberContact )
+ {
+ mPool.remove ();
+ break;
+ }
+ }
+
+ // delete all resources for it
+ if(contact->account()==(Kopete::Account*)(mAccount))
+ mAccount->resourcePool()->removeAllResources ( XMPP::Jid ( contact->contactId() ) );
+ else
+ {
+ //this is a legacy contact. we have no way to get the real Jid at this point, we can only guess it.
+ QString contactId= contact->contactId().replace('@','%') + "@" + contact->account()->myself()->contactId();
+ mAccount->resourcePool()->removeAllResources ( XMPP::Jid ( contactId ) ) ;
+ }
+
+}
+
+void JabberContactPool::clear ()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Clearing the contact pool." << endl;
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ /*
+ * The following deletion will cause slotContactDestroyed()
+ * to be called, which will clean the up the list.
+ * NOTE: this is a very inefficient way to clear the list
+ */
+ delete mContactItem->contact ();
+ }
+
+}
+
+void JabberContactPool::setDirty ( const XMPP::Jid &jid, bool dirty )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Setting flag for " << jid.full() << " to " << dirty << endl;
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().full().lower() == jid.full().lower() )
+ {
+ mContactItem->setDirty ( dirty );
+ return;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "WARNING: No match found!" << endl;
+
+}
+
+void JabberContactPool::cleanUp ()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Cleaning dirty items from contact pool." << endl;
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->dirty () )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing dirty contact " << mContactItem->contact()->contactId () << endl;
+
+ /*
+ * The following deletion will cause slotContactDestroyed()
+ * to be called, which will clean the up the list.
+ */
+ delete mContactItem->contact ();
+ }
+ }
+
+}
+
+JabberBaseContact *JabberContactPool::findExactMatch ( const XMPP::Jid &jid )
+{
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().full().lower () == jid.full().lower () )
+ {
+ return mContactItem->contact ();
+ }
+ }
+
+ return 0L;
+
+}
+
+JabberBaseContact *JabberContactPool::findRelevantRecipient ( const XMPP::Jid &jid )
+{
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().full().lower () == jid.userHost().lower () )
+ {
+ return mContactItem->contact ();
+ }
+ }
+
+ return 0L;
+
+}
+
+QPtrList<JabberBaseContact> JabberContactPool::findRelevantSources ( const XMPP::Jid &jid )
+{
+ QPtrList<JabberBaseContact> list;
+
+ for(JabberContactPoolItem *mContactItem = mPool.first (); mContactItem; mContactItem = mPool.next ())
+ {
+ if ( mContactItem->contact()->rosterItem().jid().userHost().lower () == jid.userHost().lower () )
+ {
+ list.append ( mContactItem->contact () );
+ }
+ }
+
+ return list;
+
+}
+
+JabberContactPoolItem::JabberContactPoolItem ( JabberBaseContact *contact )
+{
+ mDirty = true;
+ mContact = contact;
+}
+
+JabberContactPoolItem::~JabberContactPoolItem ()
+{
+}
+
+void JabberContactPoolItem::setDirty ( bool dirty )
+{
+ mDirty = dirty;
+}
+
+bool JabberContactPoolItem::dirty ()
+{
+ return mDirty;
+}
+
+JabberBaseContact *JabberContactPoolItem::contact ()
+{
+ return mContact;
+}
+
+#include "jabbercontactpool.moc"
diff --git a/kopete/protocols/jabber/jabbercontactpool.h b/kopete/protocols/jabber/jabbercontactpool.h
new file mode 100644
index 00000000..6582f64c
--- /dev/null
+++ b/kopete/protocols/jabber/jabbercontactpool.h
@@ -0,0 +1,124 @@
+ /*
+ * jabbercontactpool.h
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERCONTACTPOOL_H
+#define JABBERCONTACTPOOL_H
+
+#include <qobject.h>
+#include <im.h>
+
+namespace Kopete { class MetaContact; }
+namespace Kopete { class Contact; }
+class JabberContactPoolItem;
+class JabberBaseContact;
+class JabberContact;
+class JabberGroupContact;
+class JabberAccount;
+class JabberTransport;
+
+/**
+ * @author Till Gerken <till@tantalo.net>
+ */
+class JabberContactPool : public QObject
+{
+
+Q_OBJECT
+
+public:
+ /**
+ * Default constructor
+ */
+ JabberContactPool ( JabberAccount *account );
+
+ /**
+ * Default destructor
+ */
+ ~JabberContactPool();
+
+ /**
+ * Add a contact to the pool
+ */
+ JabberContact *addContact ( const XMPP::RosterItem &contact, Kopete::MetaContact *metaContact, bool dirty = true );
+ JabberBaseContact *addGroupContact ( const XMPP::RosterItem &contact, bool roomContact, Kopete::MetaContact *metaContact, bool dirty = true );
+
+ /**
+ * Remove a contact from the pool
+ */
+ void removeContact ( const XMPP::Jid &jid );
+
+ /**
+ * Remove all contacts from the pool
+ */
+ void clear ();
+
+ /**
+ * Sets the "dirty" flag for a certain contact
+ */
+ void setDirty ( const XMPP::Jid &jid, bool dirty );
+
+ /**
+ * Remove all dirty elements from the pool
+ * (used after connecting to delete removed items from the roster)
+ */
+ void cleanUp ();
+
+ /**
+ * Find an exact match in the pool by full JID.
+ */
+ JabberBaseContact *findExactMatch ( const XMPP::Jid &jid );
+
+ /**
+ * Find a relevant recipient for a given JID.
+ * This will match user@domain for a given user@domain/resource,
+ * but NOT user@domain/resource for a given user@domain.
+ */
+ JabberBaseContact *findRelevantRecipient ( const XMPP::Jid &jid );
+
+ /**
+ * Find relevant sources for a given JID.
+ * This will match user@domain/resource for a given user@domain.
+ */
+ QPtrList<JabberBaseContact> findRelevantSources ( const XMPP::Jid &jid );
+
+private slots:
+ void slotContactDestroyed ( Kopete::Contact *contact );
+
+private:
+ JabberContactPoolItem *findPoolItem ( const XMPP::RosterItem &contact );
+
+ QPtrList<JabberContactPoolItem> mPool;
+ JabberAccount *mAccount;
+
+};
+
+class JabberContactPoolItem : QObject
+{
+Q_OBJECT
+public:
+ JabberContactPoolItem ( JabberBaseContact *contact );
+ ~JabberContactPoolItem ();
+
+ void setDirty ( bool dirty );
+ bool dirty ();
+ JabberBaseContact *contact ();
+
+private:
+ bool mDirty;
+ JabberBaseContact *mContact;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberfiletransfer.cpp b/kopete/protocols/jabber/jabberfiletransfer.cpp
new file mode 100644
index 00000000..fde5b105
--- /dev/null
+++ b/kopete/protocols/jabber/jabberfiletransfer.cpp
@@ -0,0 +1,326 @@
+ /*
+ * jabberfiletransfer.cpp
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include <kdebug.h>
+#include <im.h>
+#include <xmpp.h>
+#include "jabberfiletransfer.h"
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetetransfermanager.h"
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+#include "jabberclient.h"
+#include "jabbercontactpool.h"
+#include "jabberbasecontact.h"
+#include "jabbercontact.h"
+
+JabberFileTransfer::JabberFileTransfer ( JabberAccount *account, XMPP::FileTransfer *incomingTransfer )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "New incoming transfer from " << incomingTransfer->peer().full () << ", filename " << incomingTransfer->fileName () << ", size " << QString::number ( incomingTransfer->fileSize () ) << endl;
+
+ mAccount = account;
+ mXMPPTransfer = incomingTransfer;
+
+ // try to locate an exact match in our pool first
+ JabberBaseContact *contact = mAccount->contactPool()->findExactMatch ( mXMPPTransfer->peer () );
+
+ if ( !contact )
+ {
+ // we have no exact match, try a broader search
+ contact = mAccount->contactPool()->findRelevantRecipient ( mXMPPTransfer->peer () );
+ }
+
+ if ( !contact )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No matching local contact found, creating a new one." << endl;
+
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+
+ metaContact->setTemporary (true);
+
+ contact = mAccount->contactPool()->addContact ( mXMPPTransfer->peer (), metaContact, false );
+
+ Kopete::ContactList::self ()->addMetaContact ( metaContact );
+ }
+
+ connect ( Kopete::TransferManager::transferManager (), SIGNAL ( accepted ( Kopete::Transfer *, const QString & ) ),
+ this, SLOT ( slotIncomingTransferAccepted ( Kopete::Transfer *, const QString & ) ) );
+ connect ( Kopete::TransferManager::transferManager (), SIGNAL ( refused ( const Kopete::FileTransferInfo & ) ),
+ this, SLOT ( slotTransferRefused ( const Kopete::FileTransferInfo & ) ) );
+
+ initializeVariables ();
+
+ mTransferId = Kopete::TransferManager::transferManager()->askIncomingTransfer ( contact,
+ mXMPPTransfer->fileName (),
+ mXMPPTransfer->fileSize (),
+ mXMPPTransfer->description () );
+
+}
+
+JabberFileTransfer::JabberFileTransfer ( JabberAccount *account, JabberBaseContact *contact, const QString &file )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "New outgoing transfer for " << contact->contactId() << ": " << file << endl;
+
+ mAccount = account;
+ mLocalFile.setName ( file );
+ mLocalFile.open ( IO_ReadOnly );
+
+ mKopeteTransfer = Kopete::TransferManager::transferManager()->addTransfer ( contact,
+ mLocalFile.name (),
+ mLocalFile.size (),
+ contact->contactId (),
+ Kopete::FileTransferInfo::Outgoing );
+
+ connect ( mKopeteTransfer, SIGNAL ( result ( KIO::Job * ) ), this, SLOT ( slotTransferResult () ) );
+
+ mXMPPTransfer = mAccount->client()->fileTransferManager()->createTransfer ();
+
+ initializeVariables ();
+
+ connect ( mXMPPTransfer, SIGNAL ( connected () ), this, SLOT ( slotOutgoingConnected () ) );
+ connect ( mXMPPTransfer, SIGNAL ( bytesWritten ( int ) ), this, SLOT ( slotOutgoingBytesWritten ( int ) ) );
+ connect ( mXMPPTransfer, SIGNAL ( error ( int ) ), this, SLOT ( slotTransferError ( int ) ) );
+
+ mXMPPTransfer->sendFile ( XMPP::Jid ( contact->fullAddress () ), KURL(file).fileName (), mLocalFile.size (), "" );
+
+}
+
+JabberFileTransfer::~JabberFileTransfer ()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Destroying Jabber file transfer object." << endl;
+
+ mLocalFile.close ();
+
+ mXMPPTransfer->close ();
+ delete mXMPPTransfer;
+
+}
+
+void JabberFileTransfer::initializeVariables ()
+{
+
+ mTransferId = -1;
+ mBytesTransferred = 0;
+ mBytesToTransfer = 0;
+ mXMPPTransfer->setProxy ( XMPP::Jid ( mAccount->configGroup()->readEntry ( "ProxyJID" ) ) );
+
+}
+
+void JabberFileTransfer::slotIncomingTransferAccepted ( Kopete::Transfer *transfer, const QString &fileName )
+{
+
+ if ( (long)transfer->info().transferId () != mTransferId )
+ return;
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Accepting transfer for " << mXMPPTransfer->peer().full () << endl;
+
+ mKopeteTransfer = transfer;
+ mLocalFile.setName ( fileName );
+
+ bool couldOpen = false;
+ Q_LLONG offset = 0;
+ Q_LLONG length = 0;
+
+ mBytesTransferred = 0;
+ mBytesToTransfer = mXMPPTransfer->fileSize ();
+
+ if ( mXMPPTransfer->rangeSupported () && mLocalFile.exists () )
+ {
+ KGuiItem resumeButton ( i18n ( "&Resume" ) );
+ KGuiItem overwriteButton ( i18n ( "Over&write" ) );
+
+ switch ( KMessageBox::questionYesNoCancel ( Kopete::UI::Global::mainWidget (),
+ i18n ( "The file %1 already exists, do you want to resume or overwrite it?" ).arg ( fileName ),
+ i18n ( "File Exists: %1" ).arg ( fileName ),
+ resumeButton, overwriteButton ) )
+ {
+ case KMessageBox::Yes: // resume
+ couldOpen = mLocalFile.open ( IO_ReadWrite );
+ if ( couldOpen )
+ {
+ offset = mLocalFile.size ();
+ length = mXMPPTransfer->fileSize () - offset;
+ mBytesTransferred = offset;
+ mBytesToTransfer = length;
+ mLocalFile.at ( mLocalFile.size () );
+ }
+ break;
+
+ case KMessageBox::No: // overwrite
+ couldOpen = mLocalFile.open ( IO_WriteOnly );
+ break;
+
+ default: // cancel
+ deleteLater ();
+ return;
+ }
+ }
+ else
+ {
+ // overwrite by default
+ couldOpen = mLocalFile.open ( IO_WriteOnly );
+ }
+
+ if ( !couldOpen )
+ {
+ transfer->slotError ( KIO::ERR_COULD_NOT_WRITE, fileName );
+
+ deleteLater ();
+ }
+ else
+ {
+ connect ( mKopeteTransfer, SIGNAL ( result ( KIO::Job * ) ), this, SLOT ( slotTransferResult () ) );
+ connect ( mXMPPTransfer, SIGNAL ( readyRead ( const QByteArray& ) ), this, SLOT ( slotIncomingDataReady ( const QByteArray & ) ) );
+ connect ( mXMPPTransfer, SIGNAL ( error ( int ) ), this, SLOT ( slotTransferError ( int ) ) );
+ mXMPPTransfer->accept ( offset, length );
+ }
+
+}
+
+void JabberFileTransfer::slotTransferRefused ( const Kopete::FileTransferInfo &transfer )
+{
+
+ if ( (long)transfer.transferId () != mTransferId )
+ return;
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Local user refused transfer from " << mXMPPTransfer->peer().full () << endl;
+
+ deleteLater ();
+
+}
+
+void JabberFileTransfer::slotTransferResult ()
+{
+
+ if ( mKopeteTransfer->error () == KIO::ERR_USER_CANCELED )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Transfer with " << mXMPPTransfer->peer().full () << " has been canceled." << endl;
+ mXMPPTransfer->close ();
+ deleteLater ();
+ }
+
+}
+
+void JabberFileTransfer::slotTransferError ( int errorCode )
+{
+
+ switch ( errorCode )
+ {
+ case XMPP::FileTransfer::ErrReject:
+ // user rejected the transfer request
+ mKopeteTransfer->slotError ( KIO::ERR_ACCESS_DENIED,
+ mXMPPTransfer->peer().full () );
+ break;
+
+ case XMPP::FileTransfer::ErrNeg:
+ // unable to negotiate a suitable connection for the file transfer with the user
+ mKopeteTransfer->slotError ( KIO::ERR_COULD_NOT_LOGIN,
+ mXMPPTransfer->peer().full () );
+ break;
+
+ case XMPP::FileTransfer::ErrConnect:
+ // could not connect to the user
+ mKopeteTransfer->slotError ( KIO::ERR_COULD_NOT_CONNECT,
+ mXMPPTransfer->peer().full () );
+ break;
+
+ case XMPP::FileTransfer::ErrStream:
+ // data stream was disrupted, probably cancelled
+ mKopeteTransfer->slotError ( KIO::ERR_CONNECTION_BROKEN,
+ mXMPPTransfer->peer().full () );
+ break;
+
+ default:
+ // unknown error
+ mKopeteTransfer->slotError ( KIO::ERR_UNKNOWN,
+ mXMPPTransfer->peer().full () );
+ break;
+ }
+
+ deleteLater ();
+
+}
+
+void JabberFileTransfer::slotIncomingDataReady ( const QByteArray &data )
+{
+
+ mBytesTransferred += data.size ();
+ mBytesToTransfer -= data.size ();
+
+ mKopeteTransfer->slotProcessed ( mBytesTransferred );
+
+ mLocalFile.writeBlock ( data );
+
+ if ( mBytesToTransfer <= 0 )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Transfer from " << mXMPPTransfer->peer().full () << " done." << endl;
+
+ mKopeteTransfer->slotComplete ();
+
+ deleteLater ();
+ }
+
+}
+
+void JabberFileTransfer::slotOutgoingConnected ()
+{
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Outgoing data connection is open." << endl;
+
+ mBytesTransferred = mXMPPTransfer->offset ();
+ mLocalFile.at ( mXMPPTransfer->offset () );
+ mBytesToTransfer = ( mXMPPTransfer->fileSize () > mXMPPTransfer->length () ) ? mXMPPTransfer->length () : mXMPPTransfer->fileSize ();
+
+ slotOutgoingBytesWritten ( 0 );
+
+}
+
+void JabberFileTransfer::slotOutgoingBytesWritten ( int nrWritten )
+{
+
+ mBytesTransferred += nrWritten;
+ mBytesToTransfer -= nrWritten;
+
+ mKopeteTransfer->slotProcessed ( mBytesTransferred );
+
+ if ( mBytesToTransfer )
+ {
+ int nrToWrite = mXMPPTransfer->dataSizeNeeded ();
+
+ QByteArray readBuffer ( nrToWrite );
+
+ mLocalFile.readBlock ( readBuffer.data (), nrToWrite );
+
+ mXMPPTransfer->writeFileData ( readBuffer );
+ }
+ else
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Transfer to " << mXMPPTransfer->peer().full () << " done." << endl;
+
+ mKopeteTransfer->slotComplete ();
+
+ deleteLater ();
+ }
+
+}
+
+#include "jabberfiletransfer.moc"
diff --git a/kopete/protocols/jabber/jabberfiletransfer.h b/kopete/protocols/jabber/jabberfiletransfer.h
new file mode 100644
index 00000000..01ba99e1
--- /dev/null
+++ b/kopete/protocols/jabber/jabberfiletransfer.h
@@ -0,0 +1,74 @@
+ /*
+ * jabberfiletransfer.h
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERFILETRANSFER_H
+#define JABBERFILETRANSFER_H
+
+#include <qobject.h>
+#include <filetransfer.h>
+
+class QString;
+class JabberAccount;
+namespace Kopete { class Transfer; }
+namespace Kopete { class FileTransferInfo; }
+class JabberBaseContact;
+
+class JabberFileTransfer : public QObject
+{
+
+Q_OBJECT
+
+public:
+ /**
+ * Constructor for an incoming transfer
+ */
+ JabberFileTransfer ( JabberAccount *account, XMPP::FileTransfer *incomingTransfer );
+
+ /**
+ * Constructor for an outgoing transfer
+ */
+ JabberFileTransfer ( JabberAccount *account, JabberBaseContact *contact, const QString &file );
+
+ ~JabberFileTransfer ();
+
+private slots:
+ void slotIncomingTransferAccepted ( Kopete::Transfer *transfer, const QString &fileName );
+ void slotTransferRefused ( const Kopete::FileTransferInfo &transfer );
+ void slotTransferResult ();
+ void slotTransferError ( int errorCode );
+
+ void slotOutgoingConnected ();
+ void slotOutgoingBytesWritten ( int nrWritten );
+
+ void slotIncomingDataReady ( const QByteArray &data );
+
+private:
+ void initializeVariables ();
+
+ JabberAccount *mAccount;
+ XMPP::FileTransfer *mXMPPTransfer;
+ Kopete::Transfer *mKopeteTransfer;
+ QFile mLocalFile;
+ int mTransferId;
+ Q_LLONG mBytesTransferred;
+ Q_LLONG mBytesToTransfer;
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
diff --git a/kopete/protocols/jabber/jabberformlineedit.cpp b/kopete/protocols/jabber/jabberformlineedit.cpp
new file mode 100644
index 00000000..04187b20
--- /dev/null
+++ b/kopete/protocols/jabber/jabberformlineedit.cpp
@@ -0,0 +1,58 @@
+ /*
+ * jabberformlineedit.cpp
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabberformlineedit.h"
+
+JabberFormLineEdit::JabberFormLineEdit (const int type, const QString & realName, const QString & value, QWidget * parent, const char *name):QLineEdit (value,
+ parent,
+ name)
+{
+
+ fieldType = type;
+ fieldName = realName;
+
+}
+
+void JabberFormLineEdit::slotGatherData (XMPP::Form & form)
+{
+
+ form += XMPP::FormField (fieldName, text ());
+
+}
+
+JabberFormLineEdit::~JabberFormLineEdit ()
+{
+}
+
+JabberFormPasswordEdit::JabberFormPasswordEdit (const int type, const QString & realName, const QString & value, QWidget * parent, const char *name):KPasswordEdit(parent, name)
+{
+
+ setText(value);
+ fieldType = type;
+ fieldName = realName;
+
+}
+
+void JabberFormPasswordEdit::slotGatherData (XMPP::Form & form)
+{
+
+ form += XMPP::FormField (fieldName, password());
+
+}
+
+
+#include "jabberformlineedit.moc"
diff --git a/kopete/protocols/jabber/jabberformlineedit.h b/kopete/protocols/jabber/jabberformlineedit.h
new file mode 100644
index 00000000..770bab39
--- /dev/null
+++ b/kopete/protocols/jabber/jabberformlineedit.h
@@ -0,0 +1,59 @@
+ /*
+ * jabberformlineedit.h
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERFORMLINEEDIT_H
+#define JABBERFORMLINEEDIT_H
+
+#include <qwidget.h>
+#include <qlineedit.h>
+#include <kpassdlg.h>
+
+#include "xmpp_tasks.h"
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class JabberFormLineEdit:public QLineEdit
+{
+
+ Q_OBJECT public:
+ JabberFormLineEdit (const int type, const QString & realName, const QString & value, QWidget * parent = 0, const char *name = 0);
+ ~JabberFormLineEdit ();
+
+ public slots:void slotGatherData (XMPP::Form & form);
+
+ private:
+ int fieldType;
+ QString fieldName;
+
+};
+
+class JabberFormPasswordEdit:public KPasswordEdit
+{
+
+ Q_OBJECT public:
+ JabberFormPasswordEdit(const int type, const QString & realName, const QString & value, QWidget * parent = 0, const char *name = 0);
+
+ public slots:void slotGatherData (XMPP::Form & form);
+
+ private:
+ int fieldType;
+ QString fieldName;
+
+};
+#endif
diff --git a/kopete/protocols/jabber/jabberformtranslator.cpp b/kopete/protocols/jabber/jabberformtranslator.cpp
new file mode 100644
index 00000000..fe6ec230
--- /dev/null
+++ b/kopete/protocols/jabber/jabberformtranslator.cpp
@@ -0,0 +1,91 @@
+ /*
+ * jabberformtranslator.cpp
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include <qlabel.h>
+
+#include <kdebug.h>
+
+#include "jabberformlineedit.h"
+#include "jabberformtranslator.h"
+
+JabberFormTranslator::JabberFormTranslator (const XMPP::Form & form, QWidget * parent, const char *name):QWidget (parent, name)
+{
+ /* Copy basic form values. */
+ privForm.setJid (form.jid ());
+ privForm.setInstructions (form.instructions ());
+ privForm.setKey (form.key ());
+
+ emptyForm = privForm;
+
+ /* Add instructions to layout. */
+ QVBoxLayout *innerLayout = new QVBoxLayout (this, 0, 4);
+
+ QLabel *label = new QLabel (form.instructions (), this, "InstructionLabel");
+ label->setAlignment (int (QLabel::WordBreak | QLabel::AlignVCenter));
+ label->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed, true);
+ label->show ();
+
+ innerLayout->addWidget (label, 0);
+
+ QGridLayout *formLayout = new QGridLayout (innerLayout, form.count (), 2);
+
+ int row = 1;
+ XMPP::Form::const_iterator formEnd = form.end ();
+ for (XMPP::Form::const_iterator it = form.begin (); it != formEnd; ++it, ++row)
+ {
+ kdDebug (14130) << "[JabberFormTranslator] Adding field realName()==" <<
+ (*it).realName () << ", fieldName()==" << (*it).fieldName () << " to the dialog" << endl;
+
+ label = new QLabel ((*it).fieldName (), this, (*it).fieldName ().latin1 ());
+ formLayout->addWidget (label, row, 0);
+ label->show ();
+
+ QLineEdit *edit;
+ if ((*it).type() == XMPP::FormField::password)
+ {
+ edit = new JabberFormPasswordEdit((*it).type (), (*it).realName (), (*it).value (), this);
+ }
+ else
+ {
+ edit = new JabberFormLineEdit ((*it).type (), (*it).realName (),
+ (*it).value (), this);
+ }
+ formLayout->addWidget (edit, row, 1);
+ edit->show ();
+
+ connect (this, SIGNAL (gatherData (XMPP::Form &)), edit, SLOT (slotGatherData (XMPP::Form &)));
+ }
+
+ innerLayout->addStretch ();
+}
+
+XMPP::Form & JabberFormTranslator::resultData ()
+{
+ // clear form data
+ privForm = emptyForm;
+
+ // let all line edit fields write into our form
+ emit gatherData (privForm);
+
+ return privForm;
+}
+
+JabberFormTranslator::~JabberFormTranslator ()
+{
+}
+
+#include "jabberformtranslator.moc"
diff --git a/kopete/protocols/jabber/jabberformtranslator.h b/kopete/protocols/jabber/jabberformtranslator.h
new file mode 100644
index 00000000..d9cf4044
--- /dev/null
+++ b/kopete/protocols/jabber/jabberformtranslator.h
@@ -0,0 +1,49 @@
+ /*
+ * jabberformtranslator.h
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERFORMTRANSLATOR_H
+#define JABBERFORMTRANSLATOR_H
+
+#include <qwidget.h>
+#include <qlayout.h>
+
+#include "xmpp_tasks.h"
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class JabberFormTranslator:public QWidget
+{
+
+Q_OBJECT
+
+public:
+ JabberFormTranslator (const XMPP::Form & form, QWidget * parent = 0, const char *name = 0);
+ ~JabberFormTranslator ();
+
+ XMPP::Form & resultData ();
+
+signals:
+ void gatherData (XMPP::Form & form);
+
+private:
+ XMPP::Form emptyForm, privForm;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabbergroupchatmanager.cpp b/kopete/protocols/jabber/jabbergroupchatmanager.cpp
new file mode 100644
index 00000000..7686ba8c
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupchatmanager.cpp
@@ -0,0 +1,163 @@
+/*
+ jabbergroupchatmanager.cpp - Jabber Message Manager for group chats
+
+ Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "jabbergroupchatmanager.h"
+
+#include <qptrlist.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabbercontact.h"
+
+JabberGroupChatManager::JabberGroupChatManager ( JabberProtocol *protocol, const JabberBaseContact *user,
+ Kopete::ContactPtrList others, XMPP::Jid roomJid, const char *name )
+ : Kopete::ChatSession ( user, others, protocol, name )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "New message manager for " << user->contactId () << endl;
+
+ mRoomJid = roomJid;
+
+ setMayInvite( true );
+
+ // make sure Kopete knows about this instance
+ Kopete::ChatSessionManager::self()->registerChatSession ( this );
+
+ connect ( this, SIGNAL ( messageSent ( Kopete::Message &, Kopete::ChatSession * ) ),
+ this, SLOT ( slotMessageSent ( Kopete::Message &, Kopete::ChatSession * ) ) );
+
+ updateDisplayName ();
+}
+
+JabberGroupChatManager::~JabberGroupChatManager()
+{
+}
+
+void JabberGroupChatManager::updateDisplayName ()
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ setDisplayName ( mRoomJid.full () );
+
+}
+
+const JabberBaseContact *JabberGroupChatManager::user () const
+{
+
+ return static_cast<const JabberBaseContact *>(Kopete::ChatSession::myself());
+
+}
+
+JabberAccount *JabberGroupChatManager::account () const
+{
+
+ return user()->account();
+
+}
+
+void JabberGroupChatManager::slotMessageSent ( Kopete::Message &message, Kopete::ChatSession * )
+{
+
+ if( account()->isConnected () )
+ {
+ XMPP::Message jabberMessage;
+
+ jabberMessage.setFrom ( account()->client()->jid() );
+
+
+ XMPP::Jid toJid ( mRoomJid );
+
+ jabberMessage.setTo ( toJid );
+
+ jabberMessage.setSubject ( message.subject () );
+ jabberMessage.setTimeStamp ( message.timestamp () );
+
+ if ( message.plainBody().find ( "-----BEGIN PGP MESSAGE-----" ) != -1 )
+ {
+ /*
+ * This message is encrypted, so we need to set
+ * a fake body indicating that this is an encrypted
+ * message (for clients not implementing this
+ * functionality) and then generate the encrypted
+ * payload out of the old message body.
+ */
+
+ // please don't translate the following string
+ jabberMessage.setBody ( i18n ( "This message is encrypted." ) );
+
+ QString encryptedBody = message.plainBody ();
+
+ // remove PGP header and footer from message
+ encryptedBody.truncate ( encryptedBody.length () - QString("-----END PGP MESSAGE-----").length () - 2 );
+ encryptedBody = encryptedBody.right ( encryptedBody.length () - encryptedBody.find ( "\n\n" ) - 2 );
+
+ // assign payload to message
+ jabberMessage.setXEncrypted ( encryptedBody );
+ }
+ else
+ {
+ // this message is not encrypted
+ jabberMessage.setBody ( message.plainBody () );
+ }
+
+ jabberMessage.setType ( "groupchat" );
+
+ // send the message
+ account()->client()->sendMessage ( jabberMessage );
+
+ // tell the manager that we sent successfully
+ messageSucceeded ();
+ }
+ else
+ {
+ account()->errorConnectFirst ();
+
+ // FIXME: there is no messageFailed() yet,
+ // but we need to stop the animation etc.
+ messageSucceeded ();
+ }
+}
+
+void JabberGroupChatManager::inviteContact( const QString & contactId )
+{
+ if( account()->isConnected () )
+ {
+ //NOTE: this is the obsolete, NOT RECOMMANDED protocol.
+ // iris doesn't implement groupchat yet
+ XMPP::Message jabberMessage;
+ jabberMessage.setFrom ( account()->client()->jid() );
+ jabberMessage.setTo ( contactId );
+ jabberMessage.setInvite( mRoomJid.userHost() );
+ jabberMessage.setBody( i18n("You have been invited to %1").arg( mRoomJid.userHost() ) );
+
+ // send the message
+ account()->client()->sendMessage ( jabberMessage );
+ }
+ else
+ {
+ account()->errorConnectFirst ();
+ }
+}
+
+
+#include "jabbergroupchatmanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/jabber/jabbergroupchatmanager.h b/kopete/protocols/jabber/jabbergroupchatmanager.h
new file mode 100644
index 00000000..96c689d0
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupchatmanager.h
@@ -0,0 +1,78 @@
+/*
+ jabbergroupchatmanager.h - Jabber Message Manager for group chats
+
+ Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef JABBERGROUPCHATMANAGER_H
+#define JABBERGROUPCHATMANAGER_H
+
+#include "kopetechatsession.h"
+#include "xmpp.h"
+
+class JabberProtocol;
+class JabberAccount;
+class JabberBaseContact;
+namespace Kopete { class Message; }
+class QString;
+
+/**
+ * @author Till Gerken
+ */
+class JabberGroupChatManager : public Kopete::ChatSession
+{
+ Q_OBJECT
+
+public:
+ JabberGroupChatManager ( JabberProtocol *protocol, const JabberBaseContact *user,
+ Kopete::ContactPtrList others, XMPP::Jid roomJid, const char *name = 0 );
+
+ ~JabberGroupChatManager();
+
+ /**
+ * @brief Get the local user in the session
+ * @return the local user in the session, same as account()->myself()
+ */
+ const JabberBaseContact *user () const;
+
+ /**
+ * @brief get the account
+ * @return the account
+ */
+ JabberAccount *account() const ;
+
+ /**
+ * Re-generate the display name
+ */
+ void updateDisplayName ();
+
+ /**
+ * reimplemented from Kopete::ChatSession
+ * called when a contact is droped in the window
+ */
+ virtual void inviteContact(const QString &contactId);
+
+private slots:
+ void slotMessageSent ( Kopete::Message &message, Kopete::ChatSession *kmm );
+
+
+
+private:
+ XMPP::Jid mRoomJid;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/jabber/jabbergroupcontact.cpp b/kopete/protocols/jabber/jabbergroupcontact.cpp
new file mode 100644
index 00000000..83d69ab9
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupcontact.cpp
@@ -0,0 +1,378 @@
+ /*
+ * jabbercontact.cpp - Regular Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart @ kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabbergroupcontact.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <kinputdialog.h>
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabberfiletransfer.h"
+#include "jabbergroupchatmanager.h"
+#include "jabbergroupmembercontact.h"
+#include "jabbercontactpool.h"
+#include "kopetemetacontact.h"
+#include "xmpp_tasks.h"
+
+/**
+ * JabberGroupContact constructor
+ */
+JabberGroupContact::JabberGroupContact (const XMPP::RosterItem &rosterItem, JabberAccount *account, Kopete::MetaContact * mc)
+ : JabberBaseContact ( XMPP::RosterItem ( rosterItem.jid().userHost () ), account, mc) , mNick( rosterItem.jid().resource() )
+{
+ setIcon( "jabber_group" );
+
+ // initialize here, we need it set before we instantiate the manager below
+ mManager = 0;
+
+ setFileCapable ( false );
+
+ /**
+ * Add our own nick as first subcontact (we need to do that here
+ * because we need to set this contact as myself() of the message
+ * manager).
+ */
+ mSelfContact = addSubContact ( rosterItem );
+
+ /**
+ * Instantiate a new message manager without members.
+ */
+ mManager = new JabberGroupChatManager ( protocol (), mSelfContact,
+ Kopete::ContactPtrList (), XMPP::Jid ( rosterItem.jid().userHost () ) );
+
+ connect ( mManager, SIGNAL ( closing ( Kopete::ChatSession* ) ), this, SLOT ( slotChatSessionDeleted () ) );
+
+ connect ( account->myself() , SIGNAL(onlineStatusChanged( Kopete::Contact*, const Kopete::OnlineStatus&, const Kopete::OnlineStatus& ) ) ,
+ this , SLOT(slotStatusChanged() ) ) ;
+
+ /**
+ * FIXME: The first contact in the list of the message manager
+ * needs to be our own contact. This is a flaw in the Kopete
+ * API because it can't deal with group chat properly.
+ * If we are alone in a room, we are myself() already and members()
+ * is empty. This makes at least the history plugin crash.
+ */
+ mManager->addContact ( this );
+
+
+
+ /**
+ * Let's construct the window:
+ * otherwise, the ref count of maznager is equal to zero.
+ * and if we receive a message before the window is shown,
+ * it will be deleted and we will be out of the channel
+ * In all case, there are no reason to don't show it.
+ */
+ mManager->view( true , "kopete_chatwindow" );
+}
+
+JabberGroupContact::~JabberGroupContact ()
+{
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << endl;
+
+ if(mManager)
+ {
+ mManager->deleteLater();
+ }
+
+ for ( Kopete::Contact *contact = mContactList.first (); contact; contact = mContactList.next () )
+ {
+ /*if(mManager)
+ mManager->removeContact( contact );*/
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Deleting KC " << contact->contactId () << endl;
+ contact->deleteLater();
+ }
+
+ for ( Kopete::MetaContact *metaContact = mMetaContactList.first (); metaContact; metaContact = mMetaContactList.next () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Deleting KMC " << metaContact->metaContactId () << endl;
+ metaContact->deleteLater();
+ }
+}
+
+QPtrList<KAction> *JabberGroupContact::customContextMenuActions ()
+{
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+
+ KAction *actionSetNick = new KAction (i18n ("Change nick name"), 0, 0, this, SLOT (slotChangeNick()), this, "jabber_changenick");
+ actionCollection->append( actionSetNick );
+
+ return actionCollection;
+}
+
+Kopete::ChatSession *JabberGroupContact::manager ( Kopete::Contact::CanCreateFlags canCreate )
+{
+ if(!mManager && canCreate == Kopete::Contact::CanCreate)
+ {
+ kdWarning (JABBER_DEBUG_GLOBAL) << k_funcinfo << "somehow, the chat manager was removed, and the contact is still there" << endl;
+ mManager = new JabberGroupChatManager ( protocol (), mSelfContact,
+ Kopete::ContactPtrList (), XMPP::Jid ( rosterItem().jid().userHost() ) );
+
+ mManager->addContact ( this );
+
+ connect ( mManager, SIGNAL ( closing ( Kopete::ChatSession* ) ), this, SLOT ( slotChatSessionDeleted () ) );
+
+ //if we have to recreate the manager, we probably have to connect again to the chat.
+ slotStatusChanged();
+ }
+ return mManager;
+
+}
+
+void JabberGroupContact::handleIncomingMessage (const XMPP::Message & message)
+{
+ // message type is always chat in a groupchat
+ QString viewType = "kopete_chatwindow";
+ Kopete::Message *newMessage = 0L;
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Received a message" << endl;
+
+ /**
+ * Don't display empty messages, these were most likely just carrying
+ * event notifications or other payload.
+ */
+ if ( message.body().isEmpty () )
+ return;
+
+ manager(CanCreate); //force to create mManager
+
+ Kopete::ContactPtrList contactList = manager()->members();
+
+ // check for errors
+ if ( message.type () == "error" )
+ {
+ newMessage = new Kopete::Message( message.timeStamp (), this, contactList,
+ i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"").
+ arg ( message.body () ).arg ( message.error().text ),
+ message.subject(), Kopete::Message::Inbound, Kopete::Message::PlainText, viewType );
+ }
+ else
+ {
+ // retrieve and reformat body
+ QString body = message.body ();
+
+ if( !message.xencrypted().isEmpty () )
+ {
+ body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n");
+ }
+
+ // locate the originating contact
+ JabberBaseContact *subContact = account()->contactPool()->findExactMatch ( message.from () );
+
+ if ( !subContact )
+ {
+ kdWarning (JABBER_DEBUG_GLOBAL) << k_funcinfo << "the contact is not in the list : " << message.from().full()<< endl;
+
+ /**
+ * We couldn't find the contact for this message. That most likely means
+ * that it originated from a history backlog or something similar and
+ * the sending person is not in the channel anymore. We need to create
+ * a new contact for this which does not show up in the manager.
+ */
+ subContact = addSubContact ( XMPP::RosterItem ( message.from () ), false );
+ }
+
+ // convert XMPP::Message into Kopete::Message
+ newMessage = new Kopete::Message ( message.timeStamp (), subContact, contactList, body,
+ message.subject (),
+ subContact != mManager->myself() ? Kopete::Message::Inbound : Kopete::Message::Outbound,
+ Kopete::Message::PlainText, viewType );
+ }
+
+ // append message to manager
+ mManager->appendMessage ( *newMessage );
+
+ delete newMessage;
+
+}
+
+JabberBaseContact *JabberGroupContact::addSubContact ( const XMPP::RosterItem &rosterItem, bool addToManager )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Adding new subcontact " << rosterItem.jid().full () << " to room " << mRosterItem.jid().full () << endl;
+
+ // see if this contact already exists, skip creation otherwise
+ JabberBaseContact *subContact = dynamic_cast<JabberGroupMemberContact *>( account()->contactPool()->findExactMatch ( rosterItem.jid () ) );
+
+ if ( subContact )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Contact already exists, not adding again." << endl;
+ return subContact;
+ }
+
+ // Create new meta contact that holds the group chat contact.
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact ();
+ metaContact->setTemporary ( true );
+ mMetaContactList.append ( metaContact );
+
+ // now add contact to the pool, no dirty flag
+ subContact = account()->contactPool()->addGroupContact ( rosterItem, false, metaContact, false );
+
+ /**
+ * Add the contact to our message manager first. We need
+ * to check the pointer for validity, because this method
+ * gets called from the constructor, where the manager
+ * does not exist yet.
+ */
+ if ( mManager && addToManager )
+ mManager->addContact ( subContact );
+
+ // now, add the contact also to our own list
+ mContactList.append ( subContact );
+
+ connect(subContact , SIGNAL(contactDestroyed(Kopete::Contact*)) , this , SLOT(slotSubContactDestroyed(Kopete::Contact*)));
+
+ return subContact;
+
+}
+
+void JabberGroupContact::removeSubContact ( const XMPP::RosterItem &rosterItem )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Removing subcontact " << rosterItem.jid().full () << " from room " << mRosterItem.jid().full () << endl;
+
+ // make sure that subcontacts are only removed from the room contact, which has no resource
+ if ( !mRosterItem.jid().resource().isEmpty () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "WARNING: Trying to remove subcontact from subcontact!" << endl;
+ return;
+ }
+
+ // find contact in the pool
+ JabberGroupMemberContact *subContact = dynamic_cast<JabberGroupMemberContact *>( account()->contactPool()->findExactMatch ( rosterItem.jid () ) );
+
+ if ( !subContact )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "WARNING: Subcontact couldn't be located!" << endl;
+ return;
+ }
+
+ if(mManager && subContact->contactId() == mManager->myself()->contactId() )
+ {
+ //HACK WORKAROUND FIXME KDE4
+ //impossible to remove myself, or we will die
+ //subContact->setNickName( mNick ); //this is even worse than nothing
+ return;
+ }
+
+ // remove the contact from the message manager first
+ if(mManager)
+ mManager->removeContact ( subContact );
+
+ // remove the contact's meta contact from our internal list
+ mMetaContactList.remove ( subContact->metaContact () );
+
+ // remove the contact from our internal list
+ mContactList.remove ( subContact );
+
+ // delete the meta contact first
+ delete subContact->metaContact ();
+
+ // finally, delete the contact itself from the pool
+ account()->contactPool()->removeContact ( rosterItem.jid () );
+
+}
+
+void JabberGroupContact::sendFile ( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ // if the file location is null, then get it from a file open dialog
+ if ( !sourceURL.isValid () )
+ filePath = KFileDialog::getOpenFileName( QString::null , "*", 0L, i18n ( "Kopete File Transfer" ) );
+ else
+ filePath = sourceURL.path(-1);
+
+ QFile file ( filePath );
+
+ if ( file.exists () )
+ {
+ // send the file
+ new JabberFileTransfer ( account (), this, filePath );
+ }
+
+}
+
+void JabberGroupContact::slotChatSessionDeleted ()
+{
+
+ mManager = 0;
+
+ if ( account()->isConnected () )
+ {
+ account()->client()->leaveGroupChat ( mRosterItem.jid().host (), mRosterItem.jid().user () );
+ }
+
+ //deleteLater(); //we will be deleted later when the the account will know we have left
+
+}
+
+void JabberGroupContact::slotStatusChanged( )
+{
+ if( !account()->isConnected() )
+ {
+ //we need to remove all contact, because when we connect again, we will not receive the notificaion they are gone.
+ QPtrList<Kopete::Contact> copy_contactlist=mContactList;
+ for ( Kopete::Contact *contact = copy_contactlist.first (); contact; contact = copy_contactlist.next () )
+ {
+ removeSubContact( XMPP::Jid(contact->contactId()) );
+ }
+ return;
+ }
+
+
+ if( !isOnline() )
+ {
+ //HACK WORKAROUND XMPP::client->d->groupChatList must contains us.
+ account()->client()->joinGroupChat( rosterItem().jid().host() , rosterItem().jid().user() , mNick );
+ }
+
+ //TODO: away message
+ XMPP::Status newStatus = account()->protocol()->kosToStatus( account()->myself()->onlineStatus() );
+ account()->client()->setGroupChatStatus( rosterItem().jid().host() , rosterItem().jid().user() , newStatus );
+}
+
+void JabberGroupContact::slotChangeNick( )
+{
+
+ bool ok;
+ QString futureNewNickName = KInputDialog::getText( i18n( "Change nickanme - Jabber Plugin" ),
+ i18n( "Please enter the new nick name you want to have on the room <i>%1</i>" ).arg(rosterItem().jid().userHost()),
+ mNick, &ok );
+ if ( !ok || !account()->isConnected())
+ return;
+
+ mNick=futureNewNickName;
+
+ XMPP::Status status = account()->protocol()->kosToStatus( account()->myself()->onlineStatus() );
+ account()->client()->changeGroupChatNick( rosterItem().jid().host() , rosterItem().jid().user() , mNick , status);
+
+}
+
+void JabberGroupContact::slotSubContactDestroyed( Kopete::Contact * deadContact )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "cleaning dead subcontact " << deadContact->contactId() << " from room " << mRosterItem.jid().full () << endl;
+
+ mMetaContactList.remove ( deadContact->metaContact () );
+ mContactList.remove ( deadContact );
+
+}
+
+#include "jabbergroupcontact.moc"
diff --git a/kopete/protocols/jabber/jabbergroupcontact.h b/kopete/protocols/jabber/jabbergroupcontact.h
new file mode 100644
index 00000000..c157b823
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupcontact.h
@@ -0,0 +1,107 @@
+ /*
+ * jabbercontact.cpp - Kopete Jabber protocol groupchat contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERGROUPCONTACT_H
+#define JABBERGROUPCONTACT_H
+
+#include "jabberbasecontact.h"
+
+namespace Kopete { class MetaContact; }
+class JabberGroupChatManager;
+
+class JabberGroupContact : public JabberBaseContact
+{
+
+Q_OBJECT
+
+public:
+
+ JabberGroupContact (const XMPP::RosterItem &rosterItem,
+ JabberAccount *account, Kopete::MetaContact * mc);
+
+ ~JabberGroupContact ();
+
+ /**
+ * Create custom context menu items for the contact
+ * FIXME: implement manager version here?
+ */
+ QPtrList<KAction> *customContextMenuActions ();
+
+ /**
+ * Deal with an incoming message for this contact.
+ */
+ void handleIncomingMessage ( const XMPP::Message &message );
+
+ /**
+ * Add a contact to this room.
+ */
+ JabberBaseContact *addSubContact ( const XMPP::RosterItem &rosterItem, bool addToManager = true );
+
+ /**
+ * Remove a contact from this room.
+ */
+ void removeSubContact ( const XMPP::RosterItem &rosterItem );
+
+ Kopete::ChatSession *manager ( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CannotCreate );
+
+public slots:
+
+ /**
+ * This is the JabberContact level slot for sending files.
+ *
+ * @param sourceURL The actual KURL of the file you are sending
+ * @param fileName (Optional) An alternate name for the file - what the
+ * receiver will see
+ * @param fileSize (Optional) Size of the file being sent. Used when sending
+ * a nondeterminate file size (such as over a socket)
+ */
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+private slots:
+
+ /**
+ * Catch a dying message manager and leave the room.
+ */
+ void slotChatSessionDeleted ();
+
+ /**
+ * When our own status change, we need to manually send the presence.
+ */
+ void slotStatusChanged();
+
+ /**
+ * ask the user to change the nick, and change it
+ */
+ void slotChangeNick();
+
+ /**
+ * a subcontact has been destroyed (may happen when closing kopete)
+ */
+ void slotSubContactDestroyed(Kopete::Contact*);
+
+private:
+
+ QPtrList<Kopete::Contact> mContactList;
+ QPtrList<Kopete::MetaContact> mMetaContactList;
+
+ JabberGroupChatManager *mManager;
+ JabberBaseContact *mSelfContact;
+ QString mNick;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabbergroupmembercontact.cpp b/kopete/protocols/jabber/jabbergroupmembercontact.cpp
new file mode 100644
index 00000000..2e86b898
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupmembercontact.cpp
@@ -0,0 +1,168 @@
+ /*
+ * jabbergroupmembercontact.cpp - Regular Kopete Jabber protocol contact
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabbergroupmembercontact.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberfiletransfer.h"
+#include "jabbergroupchatmanager.h"
+#include "jabberchatsession.h"
+#include "jabbercontactpool.h"
+#include "kopetemetacontact.h"
+
+/**
+ * JabberGroupMemberContact constructor
+ */
+JabberGroupMemberContact::JabberGroupMemberContact (const XMPP::RosterItem &rosterItem,
+ JabberAccount *account, Kopete::MetaContact * mc)
+ : JabberBaseContact ( rosterItem, account, mc)
+{
+
+ mc->setDisplayName ( rosterItem.jid().resource() );
+ setNickName ( rosterItem.jid().resource() );
+
+ setFileCapable ( true );
+
+ mManager = 0;
+
+}
+
+/**
+ * JabberGroupMemberContact destructor
+ */
+JabberGroupMemberContact::~JabberGroupMemberContact ()
+{
+ if(mManager)
+ {
+ mManager->deleteLater();
+ }
+}
+
+QPtrList<KAction> *JabberGroupMemberContact::customContextMenuActions ()
+{
+
+ return 0;
+
+}
+
+Kopete::ChatSession *JabberGroupMemberContact::manager ( Kopete::Contact::CanCreateFlags canCreate )
+{
+
+ if ( mManager )
+ return mManager;
+
+ if ( !mManager && !canCreate )
+ return 0;
+
+ Kopete::ContactPtrList chatMembers;
+ chatMembers.append ( this );
+
+ /*
+ * FIXME: We might have to use the group chat contact here instead of
+ * the global myself() instance for a correct representation.
+ */
+ mManager = new JabberChatSession ( protocol(), static_cast<JabberBaseContact *>(account()->myself()), chatMembers );
+ connect ( mManager, SIGNAL ( destroyed ( QObject * ) ), this, SLOT ( slotChatSessionDeleted () ) );
+
+ return mManager;
+
+}
+
+void JabberGroupMemberContact::slotChatSessionDeleted ()
+{
+
+ mManager = 0;
+
+}
+
+void JabberGroupMemberContact::handleIncomingMessage ( const XMPP::Message &message )
+{
+ // message type is always chat in a groupchat
+ QString viewType = "kopete_chatwindow";
+ Kopete::Message *newMessage = 0L;
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Received Message Type:" << message.type () << endl;
+
+ /**
+ * Don't display empty messages, these were most likely just carrying
+ * event notifications or other payload.
+ */
+ if ( message.body().isEmpty () )
+ return;
+
+ Kopete::ChatSession *kmm = manager( Kopete::Contact::CanCreate );
+ if(!kmm)
+ return;
+ Kopete::ContactPtrList contactList = kmm->members();
+
+ // check for errors
+ if ( message.type () == "error" )
+ {
+ newMessage = new Kopete::Message( message.timeStamp (), this, contactList,
+ i18n("Your message could not be delivered: \"%1\", Reason: \"%2\"").
+ arg ( message.body () ).arg ( message.error().text ),
+ message.subject(), Kopete::Message::Inbound, Kopete::Message::PlainText, viewType );
+ }
+ else
+ {
+ // retrieve and reformat body
+ QString body = message.body ();
+
+ if( !message.xencrypted().isEmpty () )
+ {
+ body = QString ("-----BEGIN PGP MESSAGE-----\n\n") + message.xencrypted () + QString ("\n-----END PGP MESSAGE-----\n");
+ }
+
+ // convert XMPP::Message into Kopete::Message
+ newMessage = new Kopete::Message ( message.timeStamp (), this, contactList, body,
+ message.subject (), Kopete::Message::Inbound,
+ Kopete::Message::PlainText, viewType );
+ }
+
+ // append message to manager
+ kmm->appendMessage ( *newMessage );
+
+ delete newMessage;
+
+}
+
+void JabberGroupMemberContact::sendFile ( const KURL &sourceURL, const QString &/*fileName*/, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ // if the file location is null, then get it from a file open dialog
+ if ( !sourceURL.isValid () )
+ filePath = KFileDialog::getOpenFileName( QString::null , "*", 0L, i18n ( "Kopete File Transfer" ) );
+ else
+ filePath = sourceURL.path(-1);
+
+ QFile file ( filePath );
+
+ if ( file.exists () )
+ {
+ // send the file
+ new JabberFileTransfer ( account (), this, filePath );
+ }
+
+}
+
+
+#include "jabbergroupmembercontact.moc"
diff --git a/kopete/protocols/jabber/jabbergroupmembercontact.h b/kopete/protocols/jabber/jabbergroupmembercontact.h
new file mode 100644
index 00000000..d4ec5b06
--- /dev/null
+++ b/kopete/protocols/jabber/jabbergroupmembercontact.h
@@ -0,0 +1,80 @@
+ /*
+ * jabbergroupmembercontact.cpp - Kopete Jabber protocol groupchat contact (member)
+ *
+ * Copyright (c) 2002-2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERGROUPMEMBERCONTACT_H
+#define JABBERGROUPMEMBERCONTACT_H
+
+#include "jabberbasecontact.h"
+
+namespace Kopete { class MetaContact; }
+class JabberGroupChatManager;
+class JabberChatSession;
+
+class JabberGroupMemberContact : public JabberBaseContact
+{
+
+Q_OBJECT
+
+public:
+
+ JabberGroupMemberContact (const XMPP::RosterItem &rosterItem,
+ JabberAccount *account, Kopete::MetaContact * mc);
+
+ ~JabberGroupMemberContact ();
+
+ /**
+ * Create custom context menu items for the contact
+ * FIXME: implement manager version here?
+ */
+ QPtrList<KAction> *customContextMenuActions ();
+
+ /**
+ * Return message manager for this instance.
+ */
+ Kopete::ChatSession *manager ( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CannotCreate );
+
+ /**
+ * Deal with incoming messages.
+ */
+ void handleIncomingMessage ( const XMPP::Message &message );
+
+public slots:
+
+ /**
+ * This is the JabberContact level slot for sending files.
+ *
+ * @param sourceURL The actual KURL of the file you are sending
+ * @param fileName (Optional) An alternate name for the file - what the
+ * receiver will see
+ * @param fileSize (Optional) Size of the file being sent. Used when sending
+ * a nondeterminate file size (such as over a socket)
+ */
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+private slots:
+ /**
+ * Catch a dying message manager
+ */
+ void slotChatSessionDeleted ();
+
+private:
+ JabberChatSession *mManager;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberprotocol.cpp b/kopete/protocols/jabber/jabberprotocol.cpp
new file mode 100644
index 00000000..ea2e8039
--- /dev/null
+++ b/kopete/protocols/jabber/jabberprotocol.cpp
@@ -0,0 +1,345 @@
+ /*
+ * jabberprotocol.cpp - Base class for the Kopete Jabber protocol
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ * Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <klineeditdlg.h>
+
+#include <qapplication.h>
+#include <qcursor.h>
+#include <qmap.h>
+#include <qtimer.h>
+#include <qpixmap.h>
+#include <qstringlist.h>
+
+#include "im.h"
+#include "xmpp.h"
+
+#include <sys/utsname.h>
+
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopetechatsession.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteaway.h"
+#include "kopeteglobal.h"
+#include "kopeteprotocol.h"
+#include "kopeteplugin.h"
+#include "kopeteaccountmanager.h"
+#include "addcontactpage.h"
+#include "kopetecommandhandler.h"
+
+#include "jabbercontact.h"
+#include "jabberaddcontactpage.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabbereditaccountwidget.h"
+#include "jabbercapabilitiesmanager.h"
+#include "jabbertransport.h"
+#include "dlgjabbersendraw.h"
+#include "dlgjabberservices.h"
+#include "dlgjabberchatjoin.h"
+#include "dlgjabberregister.h"
+
+JabberProtocol *JabberProtocol::protocolInstance = 0;
+
+typedef KGenericFactory<JabberProtocol> JabberProtocolFactory;
+
+K_EXPORT_COMPONENT_FACTORY( kopete_jabber, JabberProtocolFactory( "kopete_jabber" ) )
+
+JabberProtocol::JabberProtocol (QObject * parent, const char *name, const QStringList &)
+: Kopete::Protocol( JabberProtocolFactory::instance(), parent, name ),
+ JabberKOSChatty(Kopete::OnlineStatus::Online, 100, this, JabberFreeForChat, "jabber_chatty", i18n ("Free for Chat"), i18n ("Free for Chat"), Kopete::OnlineStatusManager::FreeForChat, Kopete::OnlineStatusManager::HasAwayMessage ),
+ JabberKOSOnline(Kopete::OnlineStatus::Online, 90, this, JabberOnline, QString::null, i18n ("Online"), i18n ("Online"), Kopete::OnlineStatusManager::Online, Kopete::OnlineStatusManager::HasAwayMessage ),
+ JabberKOSAway(Kopete::OnlineStatus::Away, 80, this, JabberAway, "contact_away_overlay", i18n ("Away"), i18n ("Away"), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage),
+ JabberKOSXA(Kopete::OnlineStatus::Away, 70, this, JabberXA, "contact_xa_overlay", i18n ("Extended Away"), i18n ("Extended Away"), 0, Kopete::OnlineStatusManager::HasAwayMessage),
+ JabberKOSDND(Kopete::OnlineStatus::Away, 60, this, JabberDND, "contact_busy_overlay", i18n ("Do not Disturb"), i18n ("Do not Disturb"), Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage),
+ JabberKOSOffline(Kopete::OnlineStatus::Offline, 50, this, JabberOffline, QString::null, i18n ("Offline") ,i18n ("Offline"), Kopete::OnlineStatusManager::Offline, Kopete::OnlineStatusManager::HasAwayMessage ),
+ JabberKOSInvisible(Kopete::OnlineStatus::Invisible, 40, this, JabberInvisible, "contact_invisible_overlay", i18n ("Invisible") ,i18n ("Invisible"), Kopete::OnlineStatusManager::Invisible),
+ JabberKOSConnecting(Kopete::OnlineStatus::Connecting, 30, this, JabberConnecting, "jabber_connecting", i18n("Connecting")),
+ propLastSeen(Kopete::Global::Properties::self()->lastSeen()),
+ propAwayMessage(Kopete::Global::Properties::self()->awayMessage()),
+ propFirstName(Kopete::Global::Properties::self()->firstName()),
+ propLastName(Kopete::Global::Properties::self()->lastName()),
+ propFullName(Kopete::Global::Properties::self()->fullName()),
+ propEmailAddress(Kopete::Global::Properties::self()->emailAddress()),
+ propPrivatePhone(Kopete::Global::Properties::self()->privatePhone()),
+ propPrivateMobilePhone(Kopete::Global::Properties::self()->privateMobilePhone()),
+ propWorkPhone(Kopete::Global::Properties::self()->workPhone()),
+ propWorkMobilePhone(Kopete::Global::Properties::self()->workMobilePhone()),
+ propNickName(Kopete::Global::Properties::self()->nickName()),
+ propSubscriptionStatus("jabberSubscriptionStatus", i18n ("Subscription"), QString::null, true, false),
+ propAuthorizationStatus("jabberAuthorizationStatus", i18n ("Authorization Status"), QString::null, true, false),
+ propAvailableResources("jabberAvailableResources", i18n ("Available Resources"), "jabber_chatty", false, true),
+ propVCardCacheTimeStamp("jabberVCardCacheTimeStamp", i18n ("vCard Cache Timestamp"), QString::null, true, false, true),
+ propPhoto(Kopete::Global::Properties::self()->photo()),
+ propJid("jabberVCardJid", i18n("Jabber ID"), QString::null, true, false),
+ propBirthday("jabberVCardBirthday", i18n("Birthday"), QString::null, true, false),
+ propTimezone("jabberVCardTimezone", i18n("Timezone"), QString::null, true, false),
+ propHomepage("jabberVCardHomepage", i18n("Homepage"), QString::null, true, false),
+ propCompanyName("jabberVCardCompanyName", i18n("Company name"), QString::null, true, false),
+ propCompanyDepartement("jabberVCardCompanyDepartement", i18n("Company Departement"), QString::null, true, false),
+ propCompanyPosition("jabberVCardCompanyPosition", i18n("Company Position"), QString::null, true, false),
+ propCompanyRole("jabberVCardCompanyRole", i18n("Company Role"), QString::null, true, false),
+ propWorkStreet("jabberVCardWorkStreet", i18n("Work Street"), QString::null, true, false),
+ propWorkExtAddr("jabberVCardWorkExtAddr", i18n("Work Extra Address"), QString::null, true, false),
+ propWorkPOBox("jabberVCardWorkPOBox", i18n("Work PO Box"), QString::null, true, false),
+ propWorkCity("jabberVCardWorkCity", i18n("Work City"), QString::null, true, false),
+ propWorkPostalCode("jabberVCardWorkPostalCode", i18n("Work Postal Code"), QString::null, true, false),
+ propWorkCountry("jabberVCardWorkCountry", i18n("Work Country"), QString::null, true, false),
+ propWorkEmailAddress("jabberVCardWorkEmailAddress", i18n("Work Email Address"), QString::null, true, false),
+ propHomeStreet("jabberVCardHomeStreet", i18n("Home Street"), QString::null, true, false),
+ propHomeExtAddr("jabberVCardHomeExt", i18n("Home Extra Address"), QString::null, true, false),
+ propHomePOBox("jabberVCardHomePOBox", i18n("Home PO Box"), QString::null, true, false),
+ propHomeCity("jabberVCardHomeCity", i18n("Home City"), QString::null, true, false),
+ propHomePostalCode("jabberVCardHomePostalCode", i18n("Home Postal Code"), QString::null, true, false),
+ propHomeCountry("jabberVCardHomeCountry", i18n("Home Country"), QString::null, true, false),
+ propPhoneFax("jabberVCardPhoneFax", i18n("Fax"), QString::null, true, false),
+ propAbout("jabberVCardAbout", i18n("About"), QString::null, true, false)
+
+{
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << "[JabberProtocol] Loading ..." << endl;
+
+ /* This is meant to be a singleton, so we will check if we have
+ * been loaded before. */
+ if (protocolInstance)
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << "[JabberProtocol] Warning: Protocol already " << "loaded, not initializing again." << endl;
+ return;
+ }
+
+ protocolInstance = this;
+
+ addAddressBookField ("messaging/xmpp", Kopete::Plugin::MakeIndexField);
+ setCapabilities(Kopete::Protocol::FullRTF|Kopete::Protocol::CanSendOffline);
+
+ // Init the Entity Capabilities manager.
+ capsManager = new JabberCapabilitiesManager;
+ capsManager->loadCachedInformation();
+}
+
+JabberProtocol::~JabberProtocol ()
+{
+ //disconnectAll();
+
+ delete capsManager;
+ capsManager = 0L;
+
+ /* make sure that the next attempt to load Jabber
+ * re-initializes the protocol class. */
+ protocolInstance = 0L;
+}
+
+
+
+AddContactPage *JabberProtocol::createAddContactWidget (QWidget * parent, Kopete::Account * i)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << "[Jabber Protocol] Create Add Contact Widget\n" << endl;
+ return new JabberAddContactPage (i, parent);
+}
+
+KopeteEditAccountWidget *JabberProtocol::createEditAccountWidget (Kopete::Account * account, QWidget * parent)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << "[Jabber Protocol] Edit Account Widget\n" << endl;
+ JabberAccount *ja=dynamic_cast < JabberAccount * >(account);
+ if(ja || !account)
+ return new JabberEditAccountWidget (this,ja , parent);
+ else
+ {
+ JabberTransport *transport = dynamic_cast < JabberTransport * >(account);
+ if(!transport)
+ return 0L;
+ dlgJabberRegister *registerDialog = new dlgJabberRegister (transport->account(), transport->myself()->contactId());
+ registerDialog->show ();
+ registerDialog->raise ();
+ return 0l; //we make ourself our own dialog, not an editAccountWidget.
+ }
+}
+
+Kopete::Account *JabberProtocol::createNewAccount (const QString & accountId)
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << "[Jabber Protocol] Create New Account. ID: " << accountId << "\n" << endl;
+ if( Kopete::AccountManager::self()->findAccount( pluginId() , accountId ) )
+ return 0L; //the account may already exist if greated just above
+
+ int slash=accountId.find('/');
+ if(slash>=0)
+ {
+ QString realAccountId=accountId.left(slash);
+ JabberAccount *realAccount=dynamic_cast<JabberAccount*>(Kopete::AccountManager::self()->findAccount( pluginId() , realAccountId ));
+ if(!realAccount) //if it doesn't exist yet, create it
+ {
+ realAccount = new JabberAccount( this, realAccountId );
+ if(!Kopete::AccountManager::self()->registerAccount( realAccount ) )
+ return 0L;
+ }
+ if(!realAccount)
+ return 0L;
+ return new JabberTransport( realAccount , accountId );
+ }
+ else
+ {
+ return new JabberAccount (this, accountId);
+ }
+}
+
+Kopete::OnlineStatus JabberProtocol::resourceToKOS ( const XMPP::Resource &resource )
+{
+
+ // update to offline by default
+ Kopete::OnlineStatus status = JabberKOSOffline;
+
+ if ( !resource.status().isAvailable () )
+ {
+ // resource is offline
+ status = JabberKOSOffline;
+ }
+ else
+ {
+ if (resource.status ().show ().isEmpty ())
+ {
+ if (resource.status ().isInvisible ())
+ {
+ status = JabberKOSInvisible;
+ }
+ else
+ {
+ status = JabberKOSOnline;
+ }
+ }
+ else
+ if (resource.status ().show () == "chat")
+ {
+ status = JabberKOSChatty;
+ }
+ else if (resource.status ().show () == "away")
+ {
+ status = JabberKOSAway;
+ }
+ else if (resource.status ().show () == "xa")
+ {
+ status = JabberKOSXA;
+ }
+ else if (resource.status ().show () == "dnd")
+ {
+ status = JabberKOSDND;
+ }
+ else if (resource.status ().show () == "online")
+ { // the ApaSMSAgent sms gateway report status as "online" even if it's not in the RFC 3921 § 2.2.2.1
+ // See Bug 129059
+ status = JabberKOSOnline;
+ }
+ else if (resource.status ().show () == "connecting")
+ { // this is for kopete internals
+ status = JabberKOSConnecting;
+ }
+ else
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Unknown status <show>" << resource.status ().show () << "</show> for contact. One of your contact is probably using a broken client, ask him to report a bug" << endl;
+ }
+ }
+
+ return status;
+
+}
+
+JabberCapabilitiesManager *JabberProtocol::capabilitiesManager()
+{
+ return capsManager;
+}
+
+JabberProtocol *JabberProtocol::protocol ()
+{
+ // return current instance
+ return protocolInstance;
+}
+
+Kopete::Contact *JabberProtocol::deserializeContact (Kopete::MetaContact * metaContact,
+ const QMap < QString, QString > &serializedData, const QMap < QString, QString > & /* addressBookData */ )
+{
+// kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Deserializing data for metacontact " << metaContact->displayName () << "\n" << endl;
+
+ QString contactId = serializedData["contactId"];
+ QString displayName = serializedData["displayName"];
+ QString accountId = serializedData["accountId"];
+ QString jid = serializedData["JID"];
+
+ QDict < Kopete::Account > accounts = Kopete::AccountManager::self ()->accounts (this);
+ Kopete::Account *account = accounts[accountId];
+
+ if (!account)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "WARNING: Account for contact does not exist, skipping." << endl;
+ return 0;
+ }
+
+ JabberTransport *transport = dynamic_cast<JabberTransport*>(account);
+ if( transport )
+ transport->account()->addContact ( jid.isEmpty() ? contactId : jid , metaContact);
+ else
+ account->addContact (contactId, metaContact);
+ return account->contacts()[contactId];
+}
+
+XMPP::Status JabberProtocol::kosToStatus( const Kopete::OnlineStatus & status , const QString & message )
+{
+ XMPP::Status xmppStatus ( "", message );
+
+ if( status.status() == Kopete::OnlineStatus::Offline )
+ {
+ xmppStatus.setIsAvailable( false );
+ }
+
+ switch ( status.internalStatus () )
+ {
+ case JabberProtocol::JabberFreeForChat:
+ xmppStatus.setShow ( "chat" );
+ break;
+
+ case JabberProtocol::JabberOnline:
+ xmppStatus.setShow ( "" );
+ break;
+
+ case JabberProtocol::JabberAway:
+ xmppStatus.setShow ( "away" );
+ break;
+
+ case JabberProtocol::JabberXA:
+ xmppStatus.setShow ( "xa" );
+ break;
+
+ case JabberProtocol::JabberDND:
+ xmppStatus.setShow ( "dnd" );
+ break;
+
+ case JabberProtocol::JabberInvisible:
+ xmppStatus.setIsInvisible ( true );
+ break;
+ }
+ return xmppStatus;
+}
+
+#include "jabberprotocol.moc"
diff --git a/kopete/protocols/jabber/jabberprotocol.h b/kopete/protocols/jabber/jabberprotocol.h
new file mode 100644
index 00000000..798aafb4
--- /dev/null
+++ b/kopete/protocols/jabber/jabberprotocol.h
@@ -0,0 +1,164 @@
+ /*
+ * jabberprotocol.h - Base class for the Kopete Jabber protocol
+ *
+ * Copyright (c) 2002-2003 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2002 by Daniel Stone <dstone@kde.org>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERPROTOCOL_H
+#define JABBERPROTOCOL_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qmovie.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+
+#include "kopetecontact.h"
+#include "kopetecontactproperty.h"
+#include "kopetemetacontact.h"
+#include "kopeteonlinestatus.h"
+#include "addcontactpage.h"
+
+#define JABBER_DEBUG_GLOBAL 14130
+#define JABBER_DEBUG_PROTOCOL 14131
+
+namespace XMPP
+{
+ class Resource;
+ class Status;
+}
+
+class JabberContact;
+class dlgJabberStatus;
+class dlgJabberSendRaw;
+class JabberCapabilitiesManager;
+
+class JabberProtocol:public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Object constructor and destructor
+ */
+ JabberProtocol (QObject * parent, const char *name, const QStringList &);
+ ~JabberProtocol ();
+
+ /**
+ * Creates the "add contact" dialog specific to this protocol
+ */
+ virtual AddContactPage *createAddContactWidget (QWidget * parent, Kopete::Account * i);
+ virtual KopeteEditAccountWidget *createEditAccountWidget (Kopete::Account * account, QWidget * parent);
+ virtual Kopete::Account *createNewAccount (const QString & accountId);
+
+ /**
+ * Deserialize contact data
+ */
+ virtual Kopete::Contact *deserializeContact (Kopete::MetaContact * metaContact,
+ const QMap < QString, QString > &serializedData, const QMap < QString, QString > &addressBookData);
+
+ enum OnlineStatus { JabberOnline, JabberFreeForChat, JabberAway, JabberXA, JabberDND,
+ JabberOffline, JabberInvisible, JabberConnecting };
+
+ const Kopete::OnlineStatus JabberKOSChatty;
+ const Kopete::OnlineStatus JabberKOSOnline;
+ const Kopete::OnlineStatus JabberKOSAway;
+ const Kopete::OnlineStatus JabberKOSXA;
+ const Kopete::OnlineStatus JabberKOSDND;
+ const Kopete::OnlineStatus JabberKOSOffline;
+ const Kopete::OnlineStatus JabberKOSInvisible;
+ const Kopete::OnlineStatus JabberKOSConnecting;
+
+ const Kopete::ContactPropertyTmpl propLastSeen;
+ const Kopete::ContactPropertyTmpl propAwayMessage;
+ const Kopete::ContactPropertyTmpl propFirstName;
+ const Kopete::ContactPropertyTmpl propLastName;
+ const Kopete::ContactPropertyTmpl propFullName;
+ const Kopete::ContactPropertyTmpl propEmailAddress;
+ const Kopete::ContactPropertyTmpl propPrivatePhone;
+ const Kopete::ContactPropertyTmpl propPrivateMobilePhone;
+ const Kopete::ContactPropertyTmpl propWorkPhone;
+ const Kopete::ContactPropertyTmpl propWorkMobilePhone;
+ const Kopete::ContactPropertyTmpl propNickName;
+ const Kopete::ContactPropertyTmpl propSubscriptionStatus;
+ const Kopete::ContactPropertyTmpl propAuthorizationStatus;
+ const Kopete::ContactPropertyTmpl propAvailableResources;
+ const Kopete::ContactPropertyTmpl propVCardCacheTimeStamp;
+ const Kopete::ContactPropertyTmpl propPhoto;
+ // extra properties to match with vCard
+ const Kopete::ContactPropertyTmpl propJid;
+ const Kopete::ContactPropertyTmpl propBirthday;
+ const Kopete::ContactPropertyTmpl propTimezone;
+ const Kopete::ContactPropertyTmpl propHomepage;
+ const Kopete::ContactPropertyTmpl propCompanyName;
+ const Kopete::ContactPropertyTmpl propCompanyDepartement;
+ const Kopete::ContactPropertyTmpl propCompanyPosition;
+ const Kopete::ContactPropertyTmpl propCompanyRole;
+ const Kopete::ContactPropertyTmpl propWorkStreet;
+ const Kopete::ContactPropertyTmpl propWorkExtAddr;
+ const Kopete::ContactPropertyTmpl propWorkPOBox;
+ const Kopete::ContactPropertyTmpl propWorkCity;
+ const Kopete::ContactPropertyTmpl propWorkPostalCode;
+ const Kopete::ContactPropertyTmpl propWorkCountry;
+ const Kopete::ContactPropertyTmpl propWorkEmailAddress;
+ const Kopete::ContactPropertyTmpl propHomeStreet;
+ const Kopete::ContactPropertyTmpl propHomeExtAddr;
+ const Kopete::ContactPropertyTmpl propHomePOBox;
+ const Kopete::ContactPropertyTmpl propHomeCity;
+ const Kopete::ContactPropertyTmpl propHomePostalCode;
+ const Kopete::ContactPropertyTmpl propHomeCountry;
+ const Kopete::ContactPropertyTmpl propPhoneFax;
+ const Kopete::ContactPropertyTmpl propAbout;
+
+ /**
+ * This returns our protocol instance
+ */
+ static JabberProtocol *protocol ();
+
+ /**
+ * Return whether the protocol supports offline messages.
+ */
+ bool canSendOffline() const { return true; }
+
+ /**
+ * Convert an XMPP::Resource status to a Kopete::OnlineStatus
+ */
+ Kopete::OnlineStatus resourceToKOS ( const XMPP::Resource &resource );
+
+ /**
+ * Convert an online status to a XMPP::Status
+ */
+ XMPP::Status kosToStatus( const Kopete::OnlineStatus & status, const QString& message=QString() );
+
+ /**
+ * Return the Entity Capabilities(JEP-0115) manager instance.
+ */
+ JabberCapabilitiesManager *capabilitiesManager();
+
+private:
+ /*
+ * Singleton instance of our protocol class
+ */
+ static JabberProtocol *protocolInstance;
+
+ /**
+ * Unique Instance of the Entity Capabilities(JEP-0115) manager for Kopete Jabber plugin.
+ */
+ JabberCapabilitiesManager *capsManager;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabberresource.cpp b/kopete/protocols/jabber/jabberresource.cpp
new file mode 100644
index 00000000..e74a0fa9
--- /dev/null
+++ b/kopete/protocols/jabber/jabberresource.cpp
@@ -0,0 +1,171 @@
+ /*
+ * jabberresource.cpp
+ *
+ * Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include "jabberresource.h"
+
+// Qt includes
+#include <qtimer.h>
+
+// KDE includes
+#include <kdebug.h>
+
+// libiris includes
+#include <im.h>
+#include <xmpp_tasks.h>
+
+// Kopete includes
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabbercapabilitiesmanager.h"
+
+class JabberResource::Private
+{
+public:
+ Private( JabberAccount *t_account, const XMPP::Jid &t_jid, const XMPP::Resource &t_resource )
+ : account(t_account), jid(t_jid), resource(t_resource), capsEnabled(false)
+ {
+ // Make sure the resource is always set.
+ jid.setResource(resource.name());
+ }
+
+ JabberAccount *account;
+ XMPP::Jid jid;
+ XMPP::Resource resource;
+
+ QString clientName, clientSystem;
+ XMPP::Features supportedFeatures;
+ bool capsEnabled;
+};
+
+JabberResource::JabberResource ( JabberAccount *account, const XMPP::Jid &jid, const XMPP::Resource &resource )
+ : d( new Private(account, jid, resource) )
+{
+ d->capsEnabled = account->protocol()->capabilitiesManager()->capabilitiesEnabled(jid);
+
+ if ( account->isConnected () )
+ {
+ QTimer::singleShot ( account->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetTimedClientVersion () ) );
+ if(!d->capsEnabled)
+ {
+ QTimer::singleShot ( account->client()->getPenaltyTime () * 1000, this, SLOT ( slotGetDiscoCapabilties () ) );
+ }
+ }
+}
+
+JabberResource::~JabberResource ()
+{
+ delete d;
+}
+
+const XMPP::Jid &JabberResource::jid () const
+{
+ return d->jid;
+}
+
+const XMPP::Resource &JabberResource::resource () const
+{
+ return d->resource;
+}
+
+void JabberResource::setResource ( const XMPP::Resource &resource )
+{
+ d->resource = resource;
+
+ // Check if the caps are now available.
+ d->capsEnabled = d->account->protocol()->capabilitiesManager()->capabilitiesEnabled(d->jid);
+
+ emit updated( this );
+}
+
+const QString &JabberResource::clientName () const
+{
+ return d->clientName;
+}
+
+const QString &JabberResource::clientSystem () const
+{
+ return d->clientSystem;
+}
+
+XMPP::Features JabberResource::features() const
+{
+ if(d->capsEnabled)
+ {
+ return d->account->protocol()->capabilitiesManager()->features(d->jid);
+ }
+ else
+ {
+ return d->supportedFeatures;
+ }
+}
+
+void JabberResource::slotGetTimedClientVersion ()
+{
+ if ( d->account->isConnected () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting client version for " << d->jid.full () << endl;
+
+ // request client version
+ XMPP::JT_ClientVersion *task = new XMPP::JT_ClientVersion ( d->account->client()->rootTask () );
+ // signal to ourselves when the vCard data arrived
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotGotClientVersion () ) );
+ task->get ( d->jid );
+ task->go ( true );
+ }
+}
+
+void JabberResource::slotGotClientVersion ()
+{
+ XMPP::JT_ClientVersion *clientVersion = (XMPP::JT_ClientVersion *) sender ();
+
+ if ( clientVersion->success () )
+ {
+ d->clientName = clientVersion->name () + " " + clientVersion->version ();
+ d->clientSystem = clientVersion->os ();
+
+ emit updated ( this );
+ }
+}
+
+void JabberResource:: slotGetDiscoCapabilties ()
+{
+ if ( d->account->isConnected () )
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Requesting Client Features for " << d->jid.full () << endl;
+
+ XMPP:: JT_DiscoInfo *task = new XMPP::JT_DiscoInfo ( d->account->client()->rootTask () );
+ // Retrive features when service discovery is done.
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT (slotGotDiscoCapabilities () ) );
+ task->get ( d->jid);
+ task->go ( true );
+ }
+}
+
+void JabberResource::slotGotDiscoCapabilities ()
+{
+ XMPP::JT_DiscoInfo *discoInfo = (XMPP::JT_DiscoInfo *) sender ();
+
+ if ( discoInfo->success () )
+ {
+ d->supportedFeatures = discoInfo->item().features();
+
+ emit updated ( this );
+ }
+}
+
+#include "jabberresource.moc"
diff --git a/kopete/protocols/jabber/jabberresource.h b/kopete/protocols/jabber/jabberresource.h
new file mode 100644
index 00000000..7b398c09
--- /dev/null
+++ b/kopete/protocols/jabber/jabberresource.h
@@ -0,0 +1,86 @@
+ /*
+ * jabberresource.h
+ *
+ * Copyright (c) 2005-2006 by Michaël Larouche <michael.larouche@kdemail.net>
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ *
+ * Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERRESOURCE_H
+#define JABBERRESOURCE_H
+
+/**
+ * Container class for a contact's resource
+ */
+
+#include <qobject.h>
+#include <qstring.h>
+
+class JabberAccount;
+
+namespace XMPP
+{
+class Resource;
+class Jid;
+class Features;
+}
+
+class JabberResource : public QObject
+{
+Q_OBJECT
+
+public:
+ /**
+ * Create a new Jabber resource.
+ */
+ JabberResource (JabberAccount *account, const XMPP::Jid &jid, const XMPP::Resource &resource);
+ ~JabberResource ();
+
+ const XMPP::Jid &jid() const;
+ const XMPP::Resource &resource() const;
+
+ void setResource ( const XMPP::Resource &resource );
+
+ /**
+ * Return the client name for this resource.
+ * @return the client name
+ */
+ const QString &clientName () const;
+ /**
+ * Return the client system for this resource.
+ * @return the client system.
+ */
+ const QString &clientSystem () const;
+
+ /**
+ * Get the available features for this resource.
+ */
+ XMPP::Features features() const;
+
+signals:
+ void updated ( JabberResource * );
+
+private slots:
+ void slotGetTimedClientVersion ();
+ void slotGotClientVersion ();
+ void slotGetDiscoCapabilties ();
+ void slotGotDiscoCapabilities ();
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
diff --git a/kopete/protocols/jabber/jabberresourcepool.cpp b/kopete/protocols/jabber/jabberresourcepool.cpp
new file mode 100644
index 00000000..9d953ce6
--- /dev/null
+++ b/kopete/protocols/jabber/jabberresourcepool.cpp
@@ -0,0 +1,394 @@
+ /*
+ * jabberresourcepool.cpp
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#include <qptrlist.h>
+#include <kdebug.h>
+
+#include "jabberresourcepool.h"
+#include "jabberresource.h"
+#include "jabbercontactpool.h"
+#include "jabberbasecontact.h"
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+#include "jabbercapabilitiesmanager.h"
+
+/**
+ * This resource will be returned if no other resource
+ * for a given JID can be found. It's an empty offline
+ * resource.
+ */
+XMPP::Resource JabberResourcePool::EmptyResource ( "", XMPP::Status ( "", "", 0, false ) );
+
+class JabberResourcePool::Private
+{
+public:
+ Private(JabberAccount *pAccount)
+ : account(pAccount)
+ {
+ // automatically delete all resources in the pool upon removal
+ pool.setAutoDelete(true);
+ }
+
+ QPtrList<JabberResource> pool;
+ QPtrList<JabberResource> lockList;
+
+ /**
+ * Pointer to the JabberAccount instance.
+ */
+ JabberAccount *account;
+};
+
+JabberResourcePool::JabberResourcePool ( JabberAccount *account )
+ : d(new Private(account))
+{}
+
+JabberResourcePool::~JabberResourcePool ()
+{
+ delete d;
+}
+
+void JabberResourcePool::slotResourceDestroyed (QObject *sender)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Resource has been destroyed, collecting the pieces." << endl;
+
+ JabberResource *oldResource = static_cast<JabberResource *>(sender);
+
+ // remove this resource from the lock list if it existed
+ d->lockList.remove ( oldResource );
+}
+
+void JabberResourcePool::slotResourceUpdated ( JabberResource *resource )
+{
+ QPtrList<JabberBaseContact> list = d->account->contactPool()->findRelevantSources ( resource->jid () );
+
+ for(JabberBaseContact *mContact = list.first (); mContact; mContact = list.next ())
+ {
+ mContact->updateResourceList ();
+ }
+
+ // Update capabilities
+ if( !resource->resource().status().capsNode().isEmpty() )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating capabilities for JID: " << resource->jid().full() << endl;
+ d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, resource->jid(), resource->resource().status() );
+ }
+}
+
+void JabberResourcePool::notifyRelevantContacts ( const XMPP::Jid &jid )
+{
+ QPtrList<JabberBaseContact> list = d->account->contactPool()->findRelevantSources ( jid );
+
+ for(JabberBaseContact *mContact = list.first (); mContact; mContact = list.next ())
+ {
+ mContact->reevaluateStatus ();
+ }
+}
+
+void JabberResourcePool::addResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
+{
+ // see if the resource already exists
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( (mResource->jid().userHost().lower() == jid.userHost().lower()) && (mResource->resource().name().lower() == resource.name().lower()) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating existing resource " << resource.name() << " for " << jid.userHost() << endl;
+
+ // It exists, update it. Don't do a "lazy" update by deleting
+ // it here and readding it with new parameters later on,
+ // any possible lockings to this resource will get lost.
+ mResource->setResource ( resource );
+
+ // we still need to notify the contact in case the status
+ // of this resource changed
+ notifyRelevantContacts ( jid );
+
+ return;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Adding new resource " << resource.name() << " for " << jid.userHost() << endl;
+
+ // Update initial capabilities if available.
+ // Called before creating JabberResource so JabberResource wouldn't ask for disco information.
+ if( !resource.status().capsNode().isEmpty() )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Initial update of capabilities for JID: " << jid.full() << endl;
+ d->account->protocol()->capabilitiesManager()->updateCapabilities( d->account, jid, resource.status() );
+ }
+
+ // create new resource instance and add it to the dictionary
+ JabberResource *newResource = new JabberResource(d->account, jid, resource);
+ connect ( newResource, SIGNAL ( destroyed (QObject *) ), this, SLOT ( slotResourceDestroyed (QObject *) ) );
+ connect ( newResource, SIGNAL ( updated (JabberResource *) ), this, SLOT ( slotResourceUpdated (JabberResource *) ) );
+ d->pool.append ( newResource );
+
+ // send notifications out to the relevant contacts that
+ // a new resource is available for them
+ notifyRelevantContacts ( jid );
+}
+
+void JabberResourcePool::removeResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing resource " << resource.name() << " from " << jid.userHost() << endl;
+
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( (mResource->jid().userHost().lower() == jid.userHost().lower()) && (mResource->resource().name().lower() == resource.name().lower()) )
+ {
+ d->pool.remove ();
+ notifyRelevantContacts ( jid );
+ return;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "WARNING: No match found!" << endl;
+}
+
+void JabberResourcePool::removeAllResources ( const XMPP::Jid &jid )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing all resources for " << jid.userHost() << endl;
+
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( mResource->jid().userHost().lower() == jid.userHost().lower() )
+ {
+ // only remove preselected resource in case there is one
+ if ( jid.resource().isEmpty () || ( jid.resource().lower () == mResource->resource().name().lower () ) )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing resource " << jid.userHost() << "/" << mResource->resource().name () << endl;
+ d->pool.remove ();
+ }
+ }
+ }
+}
+
+void JabberResourcePool::clear ()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Clearing the resource pool." << endl;
+
+ /*
+ * Since many contacts can have multiple resources, we can't simply delete
+ * each resource and trigger a notification upon each deletion. This would
+ * cause lots of status updates in the GUI and create unnecessary flicker
+ * and API traffic. Instead, collect all JIDs, clear the dictionary
+ * and then notify all JIDs after the resources have been deleted.
+ */
+
+ QStringList jidList;
+
+ for ( JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next () )
+ {
+ jidList += mResource->jid().full ();
+ }
+
+ /*
+ * Since mPool has autodeletion enabled, this will cause all
+ * items to be deleted. The lock list will be cleaned automatically.
+ */
+ d->pool.clear ();
+
+ /*
+ * Now go through the list of JIDs and notify each contact
+ * of its status change
+ */
+ for ( QStringList::Iterator it = jidList.begin (); it != jidList.end (); ++it )
+ {
+ notifyRelevantContacts ( XMPP::Jid ( *it ) );
+ }
+
+}
+
+void JabberResourcePool::lockToResource ( const XMPP::Jid &jid, const XMPP::Resource &resource )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Locking " << jid.full() << " to " << resource.name() << endl;
+
+ // remove all existing locks first
+ removeLock ( jid );
+
+ // find the resource in our dictionary that matches
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( (mResource->jid().userHost().lower() == jid.full().lower()) && (mResource->resource().name().lower() == resource.name().lower()) )
+ {
+ d->lockList.append ( mResource );
+ return;
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "WARNING: No match found!" << endl;
+}
+
+void JabberResourcePool::removeLock ( const XMPP::Jid &jid )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing resource lock for " << jid.userHost() << endl;
+
+ // find the resource in our dictionary that matches
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( (mResource->jid().userHost().lower() == jid.userHost().lower()) )
+ {
+ d->lockList.remove (mResource);
+ }
+ }
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "No locks found." << endl;
+}
+
+JabberResource *JabberResourcePool::lockedJabberResource( const XMPP::Jid &jid )
+{
+ // check if the JID already carries a resource, then we will have to use that one
+ if ( !jid.resource().isEmpty () )
+ {
+ // we are subscribed to a JID, find the according resource in the pool
+ for ( JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next () )
+ {
+ if ( ( mResource->jid().userHost().lower () == jid.userHost().lower () ) && ( mResource->resource().name () == jid.resource () ) )
+ {
+ return mResource;
+ }
+ }
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "WARNING: No resource found in pool, returning as offline." << endl;
+
+ return 0L;
+ }
+
+ // see if we have a locked resource
+ for(JabberResource *mResource = d->lockList.first (); mResource; mResource = d->lockList.next ())
+ {
+ if ( mResource->jid().userHost().lower() == jid.userHost().lower() )
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Current lock for " << jid.userHost () << " is '" << mResource->resource().name () << "'" << endl;
+ return mResource;
+ }
+ }
+
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "No lock available for " << jid.userHost () << endl;
+
+ // there's no locked resource, return an empty resource
+ return 0L;
+}
+
+const XMPP::Resource &JabberResourcePool::lockedResource ( const XMPP::Jid &jid )
+{
+ JabberResource *resource = lockedJabberResource( jid );
+ return (resource) ? resource->resource() : EmptyResource;
+}
+
+JabberResource *JabberResourcePool::bestJabberResource( const XMPP::Jid &jid, bool honourLock )
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Determining best resource for " << jid.full () << endl;
+
+ if ( honourLock )
+ {
+ // if we are locked to a certain resource, always return that one
+ JabberResource *mResource = lockedJabberResource ( jid );
+ if ( mResource )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "We have a locked resource '" << mResource->resource().name () << "' for " << jid.full () << endl;
+ return mResource;
+ }
+ }
+
+ JabberResource *bestResource = 0L;
+ JabberResource *currentResource = 0L;
+
+ for(currentResource = d->pool.first (); currentResource; currentResource = d->pool.next ())
+ {
+ // make sure we are only looking up resources for the specified JID
+ if ( currentResource->jid().userHost().lower() != jid.userHost().lower() )
+ {
+ continue;
+ }
+
+ // take first resource if no resource has been chosen yet
+ if(!bestResource)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Taking '" << currentResource->resource().name () << "' as first available resource." << endl;
+
+ bestResource = currentResource;
+ continue;
+ }
+
+ if(currentResource->resource().priority() > bestResource->resource().priority())
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Using '" << currentResource->resource().name () << "' due to better priority." << endl;
+
+ // got a better match by priority
+ bestResource = currentResource;
+ }
+ else
+ {
+ if(currentResource->resource().priority() == bestResource->resource().priority())
+ {
+ if(currentResource->resource().status().timeStamp() > bestResource->resource().status().timeStamp())
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Using '" << currentResource->resource().name () << "' due to better timestamp." << endl;
+
+ // got a better match by timestamp (priorities are equal)
+ bestResource = currentResource;
+ }
+ }
+ }
+ }
+
+ return (bestResource) ? bestResource : 0L;
+}
+
+const XMPP::Resource &JabberResourcePool::bestResource ( const XMPP::Jid &jid, bool honourLock )
+{
+ JabberResource *bestResource = bestJabberResource( jid, honourLock);
+ return (bestResource) ? bestResource->resource() : EmptyResource;
+}
+
+//TODO: Find Resources based on certain Features.
+void JabberResourcePool::findResources ( const XMPP::Jid &jid, JabberResourcePool::ResourceList &resourceList )
+{
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( mResource->jid().userHost().lower() == jid.userHost().lower() )
+ {
+ // we found a resource for the JID, let's see if the JID already contains a resource
+ if ( !jid.resource().isEmpty() && ( jid.resource().lower() != mResource->resource().name().lower() ) )
+ // the JID contains a resource but it's not the one we have in the dictionary,
+ // thus we have to ignore this resource
+ continue;
+
+ resourceList.append ( mResource );
+ }
+ }
+}
+
+void JabberResourcePool::findResources ( const XMPP::Jid &jid, XMPP::ResourceList &resourceList )
+{
+ for(JabberResource *mResource = d->pool.first (); mResource; mResource = d->pool.next ())
+ {
+ if ( mResource->jid().userHost().lower() == jid.userHost().lower() )
+ {
+ // we found a resource for the JID, let's see if the JID already contains a resource
+ if ( !jid.resource().isEmpty() && ( jid.resource().lower() != mResource->resource().name().lower() ) )
+ // the JID contains a resource but it's not the one we have in the dictionary,
+ // thus we have to ignore this resource
+ continue;
+
+ resourceList.append ( mResource->resource () );
+ }
+ }
+}
+
+#include "jabberresourcepool.moc"
diff --git a/kopete/protocols/jabber/jabberresourcepool.h b/kopete/protocols/jabber/jabberresourcepool.h
new file mode 100644
index 00000000..a6cefcde
--- /dev/null
+++ b/kopete/protocols/jabber/jabberresourcepool.h
@@ -0,0 +1,129 @@
+ /*
+ * jabberresourcepool.h
+ *
+ * Copyright (c) 2004 by Till Gerken <till@tantalo.net>
+ * Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+ *
+ * Kopete (c) by the Kopete developers <kopete-devel@kde.org>
+ *
+ * *************************************************************************
+ * * *
+ * * This program is free software; you can redistribute it and/or modify *
+ * * it under the terms of the GNU General Public License as published by *
+ * * the Free Software Foundation; either version 2 of the License, or *
+ * * (at your option) any later version. *
+ * * *
+ * *************************************************************************
+ */
+
+#ifndef JABBERRESOURCEPOOL_H
+#define JABBERRESOURCEPOOL_H
+
+#include <qobject.h>
+#include <im.h>
+
+class JabberResource;
+class JabberAccount;
+
+/**
+ * @author Till Gerken <till@tantalo.net>
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class JabberResourcePool : public QObject
+{
+ Q_OBJECT
+public:
+ static XMPP::Resource EmptyResource;
+
+ typedef QPtrList<JabberResource> ResourceList;
+
+ /**
+ * Default constructor
+ */
+ JabberResourcePool ( JabberAccount *account );
+
+ /**
+ * Default destructor
+ */
+ ~JabberResourcePool();
+
+ /**
+ * Notify all relevant contacts in case
+ * a resource has been added, updated or removed.
+ */
+ void notifyRelevantContacts ( const XMPP::Jid &jid );
+
+ /**
+ * Add a resource to the pool
+ */
+ void addResource ( const XMPP::Jid &jid, const XMPP::Resource &resource );
+
+ /**
+ * Remove a resource from the pool
+ */
+ void removeResource ( const XMPP::Jid &jid, const XMPP::Resource &resource );
+
+ /**
+ * Remove all resources for a given address from the pool
+ * NOTE: Since this method is mainly used for housekeeping,
+ * it does NOT notify any contacts.
+ */
+ void removeAllResources ( const XMPP::Jid &jid );
+
+ /**
+ * Remove all resources from the pool
+ */
+ void clear ();
+
+ /**
+ * Lock to a certain resource
+ */
+ void lockToResource ( const XMPP::Jid &jid, const XMPP::Resource &resource );
+
+ /**
+ * Remove a resource lock
+ */
+ void removeLock ( const XMPP::Jid &jid );
+
+ /**
+ * Return the JabberResource instance for the locked resource, if any.
+ */
+ JabberResource *lockedJabberResource( const XMPP::Jid &jid );
+
+ /**
+ * Return currently locked resource, if any
+ */
+ const XMPP::Resource &lockedResource ( const XMPP::Jid &jid );
+
+ /**
+ * Return a usable JabberResource for a given JID.
+ *
+ * @param jid Jid to look for the best resource.
+ * @param honourLock Honour the resource locked by the user.
+ *
+ * @return a JabberResource instance.
+ */
+ JabberResource *bestJabberResource( const XMPP::Jid &jid, bool honourLock = true );
+
+ /**
+ * Return usable resource for a given JID
+ * Matches by userHost(), honours locks for a JID by default
+ */
+ const XMPP::Resource &bestResource ( const XMPP::Jid &jid, bool honourLock = true );
+
+ /**
+ * Find all resources that exist for a given JID
+ */
+ void findResources ( const XMPP::Jid &jid, JabberResourcePool::ResourceList &resourceList );
+ void findResources ( const XMPP::Jid &jid, XMPP::ResourceList &resourceList );
+
+private slots:
+ void slotResourceDestroyed ( QObject *sender );
+ void slotResourceUpdated ( JabberResource *resource );
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jabbertransport.cpp b/kopete/protocols/jabber/jabbertransport.cpp
new file mode 100644
index 00000000..e7a8e7d3
--- /dev/null
+++ b/kopete/protocols/jabber/jabbertransport.cpp
@@ -0,0 +1,345 @@
+ /*
+
+ Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#include "jabbertransport.h"
+#include "jabbercontact.h"
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+#include "jabbercontactpool.h"
+#include "jabberchatsession.h"
+
+#include <kopeteaccountmanager.h>
+#include <kopetecontact.h>
+#include <kopetecontactlist.h>
+
+#include <kopeteversion.h>
+
+
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+#include "xmpp_tasks.h"
+
+JabberTransport::JabberTransport (JabberAccount * parentAccount, const XMPP::RosterItem & item, const QString& gateway_type)
+ : Kopete::Account ( parentAccount->protocol(), parentAccount->accountId()+"/"+ item.jid().bare() )
+{
+ m_status=Creating;
+ m_account = parentAccount;
+ m_account->addTransport( this,item.jid().bare() );
+
+ JabberContact *myContact = m_account->contactPool()->addContact ( item , Kopete::ContactList::self()->myself(), false );
+ setMyself( myContact );
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << accountId() <<" transport created: myself: " << myContact << endl;
+
+ setColor( account()->color() );
+
+#if KOPETE_IS_VERSION(0,11,51) //setCustomIcon is new in kopete 0.12
+ QString cIcon;
+ if(gateway_type=="msn")
+ cIcon="jabber_gateway_msn";
+ else if(gateway_type=="icq")
+ cIcon="jabber_gateway_icq";
+ else if(gateway_type=="aim")
+ cIcon="jabber_gateway_aim";
+ else if(gateway_type=="yahoo")
+ cIcon="jabber_gateway_yahoo";
+ else if(gateway_type=="sms")
+ cIcon="jabber_gateway_sms";
+ else if(gateway_type=="gadu-gadu")
+ cIcon="jabber_gateway_gadu";
+ else if(gateway_type=="smtp")
+ cIcon="jabber_gateway_smtp";
+ else if(gateway_type=="http-ws")
+ cIcon="jabber_gateway_http-ws";
+ else if(gateway_type=="qq")
+ cIcon="jabber_gateway_qq";
+ else if(gateway_type=="tlen")
+ cIcon="jabber_gateway_tlen";
+ else if(gateway_type=="irc") //NOTE: this is not official
+ cIcon="irc_protocol";
+
+ if( !cIcon.isEmpty() )
+ setCustomIcon( cIcon );
+#endif
+
+ configGroup()->writeEntry("GatewayJID" , item.jid().full() );
+
+ QTimer::singleShot(0, this, SLOT(eatContacts()));
+
+ m_status=Normal;
+}
+
+JabberTransport::JabberTransport( JabberAccount * parentAccount, const QString & _accountId )
+ : Kopete::Account ( parentAccount->protocol(), _accountId )
+{
+ m_status=Creating;
+ m_account = parentAccount;
+
+ const QString contactJID_s = configGroup()->readEntry("GatewayJID");
+
+ if(contactJID_s.isEmpty())
+ {
+ kdError(JABBER_DEBUG_GLOBAL) << k_funcinfo << _accountId <<": GatewayJID is empty: MISCONFIGURATION (have you used Kopete 0.12 beta ?)" << endl;
+ }
+
+ XMPP::Jid contactJID= XMPP::Jid( contactJID_s );
+
+ m_account->addTransport( this, contactJID.bare() );
+
+ JabberContact *myContact = m_account->contactPool()->addContact ( contactJID , Kopete::ContactList::self()->myself(), false );
+ setMyself( myContact );
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << accountId() <<" transport created: myself: " << myContact << endl;
+
+ m_status=Normal;
+}
+
+
+
+
+JabberTransport::~JabberTransport ()
+{
+ m_account->removeTransport( myself()->contactId() );
+}
+
+KActionMenu *JabberTransport::actionMenu ()
+{
+ KActionMenu *menu = new KActionMenu( accountId(), myself()->onlineStatus().iconFor( this ), this );
+ QString nick = myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString();
+
+ menu->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ),
+ nick.isNull() ? accountLabel() : i18n( "%2 <%1>" ).arg( accountLabel(), nick )
+ );
+
+ QPtrList<KAction> *customActions = myself()->customContextMenuActions( );
+ if( customActions && !customActions->isEmpty() )
+ {
+ menu->popupMenu()->insertSeparator();
+
+ for( KAction *a = customActions->first(); a; a = customActions->next() )
+ a->plug( menu->popupMenu() );
+ }
+ delete customActions;
+
+ return menu;
+
+/* KActionMenu *m_actionMenu = Kopete::Account::actionMenu();
+
+ m_actionMenu->popupMenu()->insertSeparator();
+
+ m_actionMenu->insert(new KAction (i18n ("Join Groupchat..."), "jabber_group", 0,
+ this, SLOT (slotJoinNewChat ()), this, "actionJoinChat"));
+
+ m_actionMenu->popupMenu()->insertSeparator();
+
+ m_actionMenu->insert ( new KAction ( i18n ("Services..."), "jabber_serv_on", 0,
+ this, SLOT ( slotGetServices () ), this, "actionJabberServices") );
+
+ m_actionMenu->insert ( new KAction ( i18n ("Send Raw Packet to Server..."), "mail_new", 0,
+ this, SLOT ( slotSendRaw () ), this, "actionJabberSendRaw") );
+
+ m_actionMenu->insert ( new KAction ( i18n ("Edit User Info..."), "identity", 0,
+ this, SLOT ( slotEditVCard () ), this, "actionEditVCard") );
+
+ return m_actionMenu;*/
+}
+
+
+bool JabberTransport::createContact (const QString & contactId, Kopete::MetaContact * metaContact)
+{
+#if 0 //TODO
+ // collect all group names
+ QStringList groupNames;
+ Kopete::GroupList groupList = metaContact->groups();
+ for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
+ groupNames += group->displayName();
+
+ XMPP::Jid jid ( contactId );
+ XMPP::RosterItem item ( jid );
+ item.setName ( metaContact->displayName () );
+ item.setGroups ( groupNames );
+
+ // this contact will be created with the "dirty" flag set
+ // (it will get reset if the contact appears in the roster during connect)
+ JabberContact *contact = contactPool()->addContact ( item, metaContact, true );
+
+ return ( contact != 0 );
+#endif
+ return false;
+}
+
+
+void JabberTransport::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason)
+{
+#if 0
+ if( status.status() == Kopete::OnlineStatus::Offline )
+ {
+ disconnect( Kopete::Account::Manual );
+ return;
+ }
+
+ if( isConnecting () )
+ {
+ errorConnectionInProgress ();
+ return;
+ }
+
+ XMPP::Status xmppStatus ( "", reason );
+
+ switch ( status.internalStatus () )
+ {
+ case JabberProtocol::JabberFreeForChat:
+ xmppStatus.setShow ( "chat" );
+ break;
+
+ case JabberProtocol::JabberOnline:
+ xmppStatus.setShow ( "" );
+ break;
+
+ case JabberProtocol::JabberAway:
+ xmppStatus.setShow ( "away" );
+ break;
+
+ case JabberProtocol::JabberXA:
+ xmppStatus.setShow ( "xa" );
+ break;
+
+ case JabberProtocol::JabberDND:
+ xmppStatus.setShow ( "dnd" );
+ break;
+
+ case JabberProtocol::JabberInvisible:
+ xmppStatus.setIsInvisible ( true );
+ break;
+ }
+
+ if ( !isConnected () )
+ {
+ // we are not connected yet, so connect now
+ m_initialPresence = xmppStatus;
+ connect ();
+ }
+ else
+ {
+ setPresence ( xmppStatus );
+ }
+#endif
+}
+
+JabberProtocol * JabberTransport::protocol( ) const
+{
+ return m_account->protocol();
+}
+
+bool JabberTransport::removeAccount( )
+{
+ if(m_status == Removing || m_status == AccountRemoved)
+ return true; //so it can be deleted
+
+ if (!account()->isConnected())
+ {
+ account()->errorConnectFirst ();
+ return false;
+ }
+
+ m_status = Removing;
+ XMPP::JT_Register *task = new XMPP::JT_Register ( m_account->client()->rootTask () );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( removeAllContacts() ) );
+
+ //JabberContact *my=static_cast<JabberContact*>(myself());
+ task->unreg ( myself()->contactId() );
+ task->go ( true );
+ return false; //delay the removal
+}
+
+void JabberTransport::removeAllContacts( )
+{
+// XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();
+
+/* if ( ! task->success ())
+ KMessageBox::queuedMessageBox ( 0L, KMessageBox::Error,
+ i18n ("An error occured when trying to remove the transport:\n%1").arg(task->statusString()),
+ i18n ("Jabber Service Unregistration"));
+ */ //we don't really care, we remove everithing anyway.
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "delete all contacts of the transport"<< endl;
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for( ; it.current(); ++it )
+ {
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( account()->client()->rootTask () );
+ rosterTask->remove ( static_cast<JabberBaseContact*>(it.current())->rosterItem().jid() );
+ rosterTask->go ( true );
+ }
+ m_status = Removing; //in theory that's already our status
+ Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
+}
+
+QString JabberTransport::legacyId( const XMPP::Jid & jid )
+{
+ if(jid.node().isEmpty())
+ return QString();
+ QString node = jid.node();
+ return node.replace("%","@");
+}
+
+void JabberTransport::jabberAccountRemoved( )
+{
+ m_status = AccountRemoved;
+ Kopete::AccountManager::self()->removeAccount( this ); //this will delete this
+}
+
+void JabberTransport::eatContacts( )
+{
+ /*
+ * "Gateway Contact Eating" (c)(r)(tm)(g)(o)(f)
+ * this comes directly from my mind into the kopete code.
+ * principle: - the transport is hungry
+ * - it will eat contacts which belong to him
+ * - the contact will die
+ * - a new contact will born, with the same characteristics, but owned by the transport
+ * - Olivier 2006-01-17 -
+ */
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+ QDict<Kopete::Contact> cts=account()->contacts();
+ QDictIterator<Kopete::Contact> it( cts );
+ for( ; it.current(); ++it )
+ {
+ JabberContact *contact=dynamic_cast<JabberContact*>(it.current());
+ if( contact && !contact->transport() && contact->rosterItem().jid().domain() == myself()->contactId() && contact != account()->myself())
+ {
+ XMPP::RosterItem item=contact->rosterItem();
+ Kopete::MetaContact *mc=contact->metaContact();
+ Kopete::OnlineStatus status = contact->onlineStatus();
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << item.jid().full() << " will be soon eat - " << contact << endl;
+ delete contact;
+ Kopete::Contact *c2=account()->contactPool()->addContact( item , mc , false ); //not sure this is false;
+ if(c2)
+ c2->setOnlineStatus( status ); //put back the old status
+ }
+ }
+}
+
+
+
+#include "jabbertransport.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/jabbertransport.h b/kopete/protocols/jabber/jabbertransport.h
new file mode 100644
index 00000000..b26fd9c0
--- /dev/null
+++ b/kopete/protocols/jabber/jabbertransport.h
@@ -0,0 +1,138 @@
+ /*
+
+ Copyright (c) 2006 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef JABBERTRANSPORT_H
+#define JABBERTRANSPORT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+
+#include <kopeteaccount.h>
+
+
+namespace XMPP {
+ class Jid;
+ class RosterItem;
+}
+class JabberAccount;
+class JabberProtocol;
+
+/**
+ * this class handle a jabber gateway
+ * @author Olivier Goffart */
+
+class JabberTransport : public Kopete::Account
+{
+ Q_OBJECT
+
+public:
+ /**
+ * constructor called when the transport is created by info from server (i.e not when loading kopete)
+ * @param parentAccount is the parent jabber account.
+ * @param item is the roster item of the gateway
+ * @param gateway_type eg: "msn" or "icq" only used when the account is not loaded from config file for determining the icon
+ */
+ JabberTransport (JabberAccount * parentAccount, const XMPP::RosterItem &item, const QString& gateway_type=QString());
+
+ /**
+ * constructor called when the transport is loaded from config
+ * @param parentAccount is the parent jabber account.
+ * @param accountId is the accountId
+ */
+ JabberTransport (JabberAccount * parentAccount, const QString &accountId );
+
+ ~JabberTransport ();
+
+ /** Returns the action menu for this account. */
+ virtual KActionMenu *actionMenu ();
+
+ /** the parent account */
+ JabberAccount *account() const
+ { return m_account; }
+
+ /* to get the protocol from the account */
+ JabberProtocol *protocol () const;
+
+ void connect( const Kopete::OnlineStatus& ) {}
+ virtual void disconnect( ) {}
+
+ /**
+ * called when the account is removed in the config ui
+ * will remove the subscription
+ */
+ virtual bool removeAccount();
+
+
+ enum TransportStatus { Normal , Creating, Removing , AccountRemoved };
+ TransportStatus transportStatus() { return m_status; };
+
+ /**
+ * return the legacyId conrresponding to the jid
+ * example: jhon%msn.com@msn.foojabber.org -> jhon@msn.com
+ */
+ QString legacyId( const XMPP::Jid &jid );
+
+public slots:
+
+ /* Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+
+ /**
+ * the account has been unregistered.
+ * loop over all contact and remove them
+ */
+ void removeAllContacts();
+
+ /**
+ * the JabberAccount has been removed from Kopete, remove this account also
+ */
+ void jabberAccountRemoved();
+
+ /**
+ * "eat" all contact in the account that have the same domain as us.
+ */
+ void eatContacts();
+
+protected:
+ /**
+ * Create a new contact in the specified metacontact
+ *
+ * You shouldn't ever call this method yourself, For adding contacts see @ref addContact()
+ *
+ * This method is called by @ref Kopete::Account::addContact() in this method, you should
+ * simply create the new custom @ref Kopete::Contact in the given metacontact. You should
+ * NOT add the contact to the server here as this method gets only called when synchronizing
+ * the contact list on disk with the one in memory. As such, all created contacts from this
+ * method should have the "dirty" flag set.
+ *
+ * This method should simply be used to intantiate the new contact, everything else
+ * (updating the GUI, parenting to meta contact, etc.) is being taken care of.
+ *
+ * @param contactId The unique ID for this protocol
+ * @param parentContact The metacontact to add this contact to
+ */
+ virtual bool createContact (const QString & contactID, Kopete::MetaContact * parentContact);
+
+private:
+ JabberAccount *m_account;
+ TransportStatus m_status;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/DESIGN b/kopete/protocols/jabber/jingle/DESIGN
new file mode 100644
index 00000000..b1cbd666
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/DESIGN
@@ -0,0 +1,121 @@
+Voice Use cases:
+----------------
+
+In JabberAccount:
+-Account is connected:
+* Init the JingleSessionManager (accessible via account()->jingleSessionManager())
+* Add voice extension to client features.
+* Connect to incomingSession(const QString &sessionType, JingleSession *session) signal in JabberAccount.
+
+-On incoming session
+* Create and show VoiceConversationDialog.
+* VoiceConversationDialog will handle the communcation between the user and the session.
+
+In JabberContact:
+-User select "Start voice conversation..."
+* Get the best resource that support voice. If no compatible resource is found, show a message box.
+* Create a JingleVoiceSession using JingleVoiceSessionManager.
+* Create VoiceConversationDialog
+* and VoiceConversationDialog will handle the communication between the user and the session.
+
+In VoiceConversationDialog:
+-Incoming voice session
+* Accept the session call JingleVoiceSession::accept();
+* Decline the session call JingleVoiceSession::decline();
+
+-Accepted voice session
+* Change GUI to "Voice session in progress."
+
+-On declining voice session or terminating a session.
+* Remove JingleVoiceSession from JingleVoiceSessionManager.
+* Close the dialog.
+
+===================================================================================================
+Design with future in mind. Only voice session type is available today, but others will come.
+
+A session is a connection between two or multiple peers.
+A session do not handle multiple "calls"(or whatever it called depending of the context). That's will be job of SessionManager
+A sesson has a myself user and others users, all identified by their full JID. (maybe their JabberBaseContact or JabberResource object ?)
+
+-Maybe use the Channel pattern, where Session will hold one or multiple Channels. Think for voice+video for example. ?
+
+All manager classes must be unique for each account.
+
+JidList = QValueList<XMPP::Jid> or QStringList if QValueList<XMPP::Jid> doesn't work.
+
+JingleSession and derivated are created by the Manager class.
+
+SessionType is the XML Namespace of the session type (ex: http://jabber.org/protocol/sessions/audio)
+
+JingleSessionManager
+--------------------
+Manage Jingle sessions.
+-Manage global (maybe static ?)objects shared by all sessions (cricket::BasicPortAllocator, cricket::SessionManager).
+
+Has a JingleWatchSessionTask(derived from XMPP::Task) that check for incoming session in JingleSessionManager, that check the session type,
+create the right JingleSession subclass, then emit the required signal. This bypass libjingle to have a better
+control on incoming session request and avoid using multiple Manager for each session type.
+
+JingleSessionManager manage the JingleSession pointers. Do not delete it in user classes.
+
+* JingleSessionManager(JabberAccount *)
+
+* public slots:
+* JingleSession *createSession(const QString &sessionType, const JidList &peers);
+* void removeSession(JingleSession *);
+
+signals:
+* void incomingSession(const QString &sessionType, JingleSession *session);
+
+JingleSession
+-------------
+Base class for Jingle session. A session is a
+
+* JingleSession(JingleSessionManager *manager, const JidList &peers);
+
+* XMPP::Jid &myself(); // account()->client()->jid();
+* JidList &peers();
+* JabberAccount *account();
+* JingleSessionManager *manager();
+
+// Start the negociation phase.
+* virtual void start() = 0;
+// Send the IQ stanza with action "accept"
+* virtual void accept() = 0;
+// Send the IQ stanza with action "
+* virtual void decline() = 0;
+* virtual void terminate() = 0;
+
+// Return Session XML namespace
+* virtual QString sessionType() = 0;
+
+protected slots:
+ void sendStanza(const QString &stanza) { account()->client->send(stanza);
+
+signals:
+ void accepted();
+ void declined();
+ void terminated();
+
+JingleVoiceSession : public JingleSession
+------------------
+Define a VoIP voice session between two peers(for the moment).
+Hold the PhoneSessionClient object.
+
+connect(account()->client(),SIGNAL(xmlIncoming(const QString&)),SLOT(receiveStanza(const QString&)));
+
+
+private slots:
+ void receiveStanza(const QString &stanza);
+
+VoiceConversationDialog
+-----------------------
+* VoiceConversationDialog(JingleVoiceSession *)
+VoiceConversationDialog will handle the communcation between the user and a session.
+Should auto-delete when closed.
+It can:
+-Accept a voice session.
+-Decline a voice session.
+-Terminate a voice session(or hang-up).
+
+It is the Action menu that can start a session.
diff --git a/kopete/protocols/jabber/jingle/Makefile.am b/kopete/protocols/jabber/jingle/Makefile.am
new file mode 100644
index 00000000..553be0d7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/Makefile.am
@@ -0,0 +1,28 @@
+SUBDIRS = libjingle
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/../libiris/iris/include \
+ -I$(srcdir)/../libiris/iris/xmpp-im \
+ -I$(srcdir)/../libiris/iris/jabber \
+ -I$(srcdir)/../libiris/qca/src \
+ -I$(srcdir)/../libiris/cutestuff/util \
+ -I$(srcdir)/libjingle \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopetejabberjingle.la
+
+# libkopetejabberjingle_la_SOURCES = jinglevoicecaller.cpp \
+# jinglewatchsessiontask.cpp jinglesession.cpp jinglevoicesession.cpp jinglesessionmanager.cpp \
+# jinglevoicesessiondialogbase.ui jinglevoicesessiondialog.cpp
+
+libkopetejabberjingle_la_SOURCES = jinglevoicecaller.cpp jinglevoicesessiondialogbase.ui jinglevoicesessiondialog.cpp
+
+libkopetejabberjingle_la_LIBADD = libjingle/talk/session/phone/libcricketsessionphone.la \
+ libjingle/talk/p2p/client/libcricketp2pclient.la \
+ libjingle/talk/p2p/base/libcricketp2pbase.la \
+ libjingle/talk/xmpp/libcricketxmpp.la \
+ libjingle/talk/xmllite/libcricketxmllite.la \
+ libjingle/talk/base/libcricketbase.la \
+ libjingle/talk/third_party/mediastreamer/libmediastreamer.la \
+ $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) $(ALSA_LIBS)
diff --git a/kopete/protocols/jabber/jingle/configure.in.bot b/kopete/protocols/jabber/jingle/configure.in.bot
new file mode 100644
index 00000000..f30595e6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/configure.in.bot
@@ -0,0 +1,16 @@
+if test "$with_jingle" = yes; then
+ echo ""
+ echo Supported Jabber Jingle voice Codecs for Kopete:
+ echo Speex: $speex_found
+ echo iLBC: $ilbc_found
+ echo MULAW: yes
+else
+ echo ""
+ echo "You have disabled Jabber Jingle voice support or you are missing required libraries required to compile it."
+ echo "Jingle is a new Jabber standard that define a signaling protocol via XMPP for peer-to-peer applications."
+ echo "Jingle audio is compatible with the Google Talk voice service."
+ echo ""
+ echo "Required Jingle dependencies are listed on this page:"
+ echo "http://wiki.kde.org/tiki-index.php?page=Kopete+Jabber+Jingle"
+ all_tests=bad
+fi
diff --git a/kopete/protocols/jabber/jingle/configure.in.in b/kopete/protocols/jabber/jingle/configure.in.in
new file mode 100644
index 00000000..ee4db3fa
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/configure.in.in
@@ -0,0 +1,87 @@
+AC_DEFINE(PRODUCTION_BUILD, 1, [Build as a production build])
+AC_DEFINE(PRODUCTION, 1, [Build as a production build])
+AC_DEFINE(POSIX, 1, [If we're using configure, we're on POSIX])
+
+# Check if the user want Jabber Jingle voice support
+AC_ARG_ENABLE(jingle, [ --enable-jingle enable Jabber Jingle voice support ], with_jingle=$enableval, with_jingle=no)
+
+# Here we go
+HAVE_EXPAT=no
+AC_CHECK_LIB(expat, XML_ParserCreate, HAVE_EXPAT="yes")
+if test "x$HAVE_EXPAT" = xyes ; then
+ EXPAT_LIBS="-lexpat"
+ AC_SUBST(EXPAT_LIBS)
+else
+ with_jingle=no
+ AC_MSG_WARN([Expat is required to build Jabber Jingle voice support. You can get it from http://expat.sourceforge.net/])
+fi
+
+AC_CHECK_HEADERS(alsa/asoundlib.h,
+ [AC_CHECK_LIB(asound, snd_pcm_open,
+ [ALSA_LIBS="-lasound" ; AC_DEFINE(__ALSA_ENABLED__,1,[Defined when alsa support is enabled]) ])
+ ]
+)
+AC_SUBST(ALSA_LIBS)
+
+# We test for GLIB in protocols/configure.in.in
+if test x$have_glib = xno; then
+ with_jingle=no
+fi
+
+PKG_CHECK_MODULES(ORTP, ortp, enable_ortp=yes, enable_ortp=no)
+if test x$enable_ortp = xno ; then
+ with_jingle=no
+ AC_MSG_WARN([oRTP is required to build Jabber Jingle voice support. You can get it from http://www.linphone.org/ortp/])
+fi
+AC_SUBST(ORTP_CFLAGS)
+AC_SUBST(ORTP_LIBS)
+
+AC_ARG_WITH( speex,
+ [ --with-speex Set prefix where speex lib can be found (ex:/usr, /usr/local) [default=/usr] ],
+ [ speex_prefix=${withval}],[ speex_prefix="/usr" ])
+
+PKG_CHECK_MODULES(SPEEX, speex, speex_found=yes, speex_found=no)
+AC_CHECK_HEADERS(speex.h,[AC_CHECK_LIB(speex,speex_encode_int,speex_found=yes,speex_found=no)],speex_found=no)
+AC_CHECK_HEADERS(speex/speex.h,[AC_CHECK_LIB(speex,speex_encode_int,speex_found=yes,speex_found=no)],speex_found=no)
+
+if test x$speex_found = xno; then
+ AC_MSG_WARN([Could not find a libspeex version that have the speex_encode_int() function. Please install libspeex=1.0.5 or libspeex>=1.1.6 from http://www.speex.org/])
+else
+ SPEEX_CFLAGS="$SPEEX_CFLAGS -I${speex_prefix}/include -I${speex_prefix}/include/speex"
+ AC_SUBST(SPEEX_CFLAGS)
+ AC_SUBST(SPEEX_LIBS)
+ AC_DEFINE(HAVE_SPEEX,1,[Speex codec is enabled])
+fi
+
+
+# dnl only accept speex>=1.1.6 or 1.0.5 (the versions that have speex_encode_int )
+# AC_ARG_WITH( speex,
+# [ --with-speex Set prefix where speex lib can be found (ex:/usr, /usr/local) [default=/usr] ],
+# [ speex_prefix=${withval}],[ speex_prefix="/usr" ])
+#
+# AC_CHECK_HEADERS(speex.h,[AC_CHECK_LIB(speex,speex_encode_int,speex_found=yes,speex_found=no)
+# ],speex_found=no)
+#
+# if test "$speex_found" = "no" ; then
+# AC_MSG_WARN([Could not find a libspeex version that have the speex_encode_int() function. Please install libspeex=1.0.5 or libspeex>=1.1.6 from http://www.speex.org/])
+# else
+# SPEEX_CFLAGS=" -I${speex_prefix}/include -I${speex_prefix}/include/speex"
+# SPEEX_LIBS="-L${speex_prefix}/lib -lspeex -lm"
+# CPPFLAGS_save=$CPPFLAGS
+# CPPFLAGS=$SPEEX_CFLAGS
+# LDFLAGS_save=$LDFLAGS
+# LDFLAGS=$SPEEX_LIBS
+# AC_DEFINE(HAVE_SPEEX,1,[has speex])
+# fi
+#
+# AC_SUBST(SPEEX_CFLAGS)
+# AC_SUBST(SPEEX_LIBS)
+# CPPFLAGS=$CPPFLAGS_save
+# LDFLAGS=$LDFLAGS_save
+ilbc_found="no"
+
+AM_CONDITIONAL(include_jingle, test "$with_jingle" = "yes")
+
+if test "$with_jingle" = "yes" ; then
+ AC_DEFINE(SUPPORT_JINGLE,1,[Jingle support is enabled])
+fi
diff --git a/kopete/protocols/jabber/jingle/jinglesession.cpp b/kopete/protocols/jabber/jingle/jinglesession.cpp
new file mode 100644
index 00000000..6c370fca
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglesession.cpp
@@ -0,0 +1,72 @@
+/*
+ jinglesession.h - Define a Jingle session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "jinglesession.h"
+
+#include <kdebug.h>
+
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+
+class JingleSession::Private
+{
+public:
+ Private(JabberAccount *t_account, const JidList &t_peers)
+ : peers(t_peers), account(t_account)
+ {}
+
+ XMPP::Jid myself;
+ JidList peers;
+ JabberAccount *account;
+};
+
+JingleSession::JingleSession(JabberAccount *account, const JidList &peers)
+ : QObject(account, 0), d(new Private(account, peers))
+{
+ d->myself = account->client()->jid();
+}
+
+JingleSession::~JingleSession()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+ delete d;
+}
+
+const XMPP::Jid &JingleSession::myself() const
+{
+ return d->myself;
+}
+
+const JingleSession::JidList &JingleSession::peers() const
+{
+ return d->peers;
+}
+
+JingleSession::JidList &JingleSession::peers()
+{
+ return d->peers;
+}
+JabberAccount *JingleSession::account()
+{
+ return d->account;
+}
+
+void JingleSession::sendStanza(const QString &stanza)
+{
+ account()->client()->send( stanza );
+}
+
+#include "jinglesession.moc"
diff --git a/kopete/protocols/jabber/jingle/jinglesession.h b/kopete/protocols/jabber/jingle/jinglesession.h
new file mode 100644
index 00000000..00c192bd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglesession.h
@@ -0,0 +1,94 @@
+/*
+ jinglesession.h - Define a Jingle session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef JINGLESESSION_H
+#define JINGLESESSION_H
+
+#include <qobject.h>
+#include <qstring.h>
+
+#include <xmpp.h> // XMPP::Jid
+#include <qvaluelist.h>
+
+class JabberAccount;
+/**
+ * @brief Base class for peer-to-peer session that use Jingle signaling
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class JingleSession : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QValueList<XMPP::Jid> JidList;
+
+ JingleSession(JabberAccount *account, const JidList &peers);
+ virtual ~JingleSession();
+
+ /**
+ * Return the JabberAccount associated with this session.
+ */
+ JabberAccount *account();
+
+ const XMPP::Jid &myself() const;
+ const JidList &peers() const;
+ JidList &peers();
+
+ /**
+ * Return the type of session(ex: voice, video, games)
+ * Note that you must return the XML namespace that define
+ * the session: ex:(http://jabber.org/protocol/jingle/sessions/audio)
+ */
+ virtual QString sessionType() = 0;
+
+public slots:
+ /**
+ * @brief Start a session with the give JID.
+ * You should begin the negociation here.
+ */
+ virtual void start() = 0;
+ /**
+ * @brief Acept a session request.
+ */
+ virtual void accept() = 0;
+ /**
+ * @brief Decline a session request.
+ */
+ virtual void decline() = 0;
+ /**
+ * @brief Terminate a Jingle session.
+ */
+ virtual void terminate() = 0;
+
+protected slots:
+ void sendStanza(const QString &stanza);
+
+signals:
+ /**
+ * Session is started(negocation and connection test are done).
+ */
+ void sessionStarted();
+
+ void accepted();
+ void declined();
+ void terminated();
+
+private:
+ class Private;
+ Private *d;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/jinglesessionmanager.cpp b/kopete/protocols/jabber/jingle/jinglesessionmanager.cpp
new file mode 100644
index 00000000..aeec2889
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglesessionmanager.cpp
@@ -0,0 +1,205 @@
+/*
+ jinglesessionmanager.cpp - Manage Jingle sessions.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+// libjingle before everything else to not clash with Qt
+#define POSIX
+#include "talk/xmpp/constants.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/socketaddress.h"
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/sessionsendtask.h"
+
+
+#include "jinglesessionmanager.h"
+
+//#include "jinglesession.h"
+#include "jinglevoicesession.h"
+
+#include "jinglewatchsessiontask.h"
+
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+
+#include <kdebug.h>
+
+#define JINGLE_NS "http://www.google.com/session"
+#define JINGLE_VOICE_SESSION_NS "http://www.google.com/session/phone"
+
+//BEGIN JingleSessionManager::SlotsProxy
+class JingleSessionManager;
+class JingleSessionManager::SlotsProxy : public sigslot::has_slots<>
+{
+public:
+ SlotsProxy(JingleSessionManager *parent)
+ : sessionManager(parent)
+ {}
+
+ void OnSignalingRequest()
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Requesting Jingle signaling." << endl;
+ sessionManager->cricketSessionManager()->OnSignalingReady();
+ }
+
+
+private:
+ JingleSessionManager *sessionManager;
+};
+
+//END JingleSessionManager::SlotsProxy
+
+//BEGIN JingleSessionManager::Private
+class JingeSession;
+class JingleSessionManager::Private
+{
+public:
+ Private(JabberAccount *t_account)
+ : account(t_account), watchSessionTask(0L)
+ {}
+
+ ~Private()
+ {
+ delete networkManager;
+ delete portAllocator;
+ delete sessionThread;
+ delete cricketSessionManager;
+ }
+
+ JabberAccount *account;
+ QValueList<JingleSession*> sessionList;
+ JingleWatchSessionTask *watchSessionTask;
+
+ cricket::NetworkManager *networkManager;
+ cricket::BasicPortAllocator *portAllocator;
+ cricket::Thread *sessionThread;
+ cricket::SessionManager *cricketSessionManager;
+};
+//END JingleSessionManager::Private
+
+JingleSessionManager::JingleSessionManager(JabberAccount *account)
+ : QObject(account, 0), d(new Private(account))
+{
+ // Create slots proxy for libjingle
+ slotsProxy = new SlotsProxy(this);
+
+ // Create watch incoming session task.
+ d->watchSessionTask = new JingleWatchSessionTask(account->client()->rootTask());
+ connect(d->watchSessionTask, SIGNAL(watchSession(const QString &, const QString &)), this, SLOT(slotIncomingSession(const QString &, const QString &)));
+
+ // Create global cricket variables common to all sessions.
+ // Seed random generation with the JID of the account.
+ QString accountJid = account->client()->jid().full();
+ cricket::InitRandom( accountJid.ascii(), accountJid.length() );
+
+ // Create the libjingle NetworkManager that manager local network connections
+ d->networkManager = new cricket::NetworkManager();
+
+ // Init the port allocator(select best ports) with the Google STUN server to help.
+ cricket::SocketAddress *googleStunAddress = new cricket::SocketAddress("64.233.167.126", 19302);
+ // TODO: Define a relay server.
+ d->portAllocator = new cricket::BasicPortAllocator(d->networkManager, googleStunAddress, 0L);
+
+ // Create the Session manager that manager peer-to-peer sessions.
+ d->sessionThread = new cricket::Thread();
+ d->cricketSessionManager = new cricket::SessionManager(d->portAllocator, d->sessionThread);
+ d->cricketSessionManager->SignalRequestSignaling.connect(slotsProxy, &JingleSessionManager::SlotsProxy::OnSignalingRequest);
+ d->cricketSessionManager->OnSignalingReady();
+
+ d->sessionThread->Start();
+}
+
+JingleSessionManager::~JingleSessionManager()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Cleaning up Jingle sessions." << endl;
+ QValueList<JingleSession*>::Iterator it, itEnd = d->sessionList.end();
+ for(it = d->sessionList.begin(); it != itEnd; ++it)
+ {
+ JingleSession *deletedSession = *it;
+ if( deletedSession )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "deleting a session." << endl;
+ delete deletedSession;
+ }
+ }
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Done Cleaning up Jingle sessions." << endl;
+
+ delete d;
+}
+
+cricket::SessionManager *JingleSessionManager::cricketSessionManager()
+{
+ return d->cricketSessionManager;
+}
+
+JabberAccount *JingleSessionManager::account()
+{
+ return d->account;
+}
+
+JingleSession *JingleSessionManager::createSession(const QString &sessionType, const JidList &peers)
+{
+ JingleSession *newSession = 0L;
+
+ if(sessionType == JINGLE_VOICE_SESSION_NS)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Creating a voice session" << endl;
+ newSession = new JingleVoiceSession(account(), peers);
+ }
+
+ if(newSession)
+ d->sessionList.append(newSession);
+
+ return newSession;
+}
+
+JingleSession *JingleSessionManager::createSession(const QString &sessionType, const XMPP::Jid &user)
+{
+ JingleSessionManager::JidList jidList;
+ jidList.append(user);
+
+ return createSession(sessionType, jidList);
+}
+
+void JingleSessionManager::removeSession(JingleSession *session)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Removing a jingle session." << endl;
+
+ d->sessionList.remove(session);
+ delete session;
+}
+
+void JingleSessionManager::slotIncomingSession(const QString &sessionType, const QString &initiator)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Incoming session: " << sessionType << ". Initiator: " << initiator << endl;
+
+ JingleSession *newSession = createSession(sessionType, XMPP::Jid(initiator));
+ emit incomingSession(sessionType, newSession);
+}
+
+#include "jinglesessionmanager.moc"
diff --git a/kopete/protocols/jabber/jingle/jinglesessionmanager.h b/kopete/protocols/jabber/jingle/jinglesessionmanager.h
new file mode 100644
index 00000000..06951c2f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglesessionmanager.h
@@ -0,0 +1,89 @@
+/*
+ jinglesessionmanager.h - Manage Jingle sessions.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef JINGLESESSIONMANAGER_H
+#define JINGLESESSIONMANAGER_H
+
+#include <xmpp.h>
+#include <im.h>
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+namespace cricket
+{
+ class SessionManager;
+}
+
+class JingleSession;
+class JingleVoiceSession;
+class JabberAccount;
+
+/**
+ * @brief Manage Jingle sessions.
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class JingleSessionManager : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QValueList<XMPP::Jid> JidList;
+
+ JingleSessionManager(JabberAccount *account);
+ ~JingleSessionManager();
+
+ /**
+ * Get the (single) instance of the cricket session manager.
+ */
+ cricket::SessionManager *cricketSessionManager();
+
+ /**
+ * Return the JabberAccount associated with this session manager.
+ */
+ JabberAccount *account();
+
+public slots:
+ /**
+ * Create a new Jingle session. Returned pointer is managed by this class.
+ * @param sessionType the session you want to create. You must pass its XML namespace(ex: http://jabber.org/protocol/sessions/audio)
+ * @param peers Lists of participants of the session.
+ */
+ JingleSession *createSession(const QString &sessionType, const JidList &peers);
+ /**
+ * Override method that create a session for a one-to-one session.
+ * It behave like createSession method.
+ * @param sessionType the sesion you want to create. You must pass its XML namespace(ex: http://jabber.org/protocol/sessions/audio)
+ * @param user The JID of the user you want to begin a session with.
+ */
+ JingleSession *createSession(const QString &sessionType, const XMPP::Jid &user);
+
+ void removeSession(JingleSession *session);
+
+signals:
+ void incomingSession(const QString &sessionType, JingleSession *session);
+
+private slots:
+ void slotIncomingSession(const QString &sessionType, const QString &initiator);
+
+private:
+ class Private;
+ Private *d;
+
+ class SlotsProxy;
+ SlotsProxy *slotsProxy;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp
new file mode 100644
index 00000000..3ad6a89a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicecaller.cpp
@@ -0,0 +1,376 @@
+
+#define POSIX //FIXME
+#include "talk/xmpp/constants.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/socketaddress.h"
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/sessionsendtask.h"
+
+
+
+#include <qstring.h>
+#include <qdom.h>
+
+
+
+#include "im.h"
+#include "xmpp.h"
+#include "xmpp_xmlcommon.h"
+#include "jinglevoicecaller.h"
+#include "jabberprotocol.h"
+
+// Should change in the future
+#define JINGLE_NS "http://www.google.com/session"
+
+#include "jabberaccount.h"
+#include <kdebug.h>
+#define qDebug( X ) kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << X << endl
+#define qWarning( X ) kdWarning() <<k_funcinfo<< X << endl
+
+// ----------------------------------------------------------------------------
+
+class JingleIQResponder : public XMPP::Task
+{
+public:
+ JingleIQResponder(XMPP::Task *);
+ ~JingleIQResponder();
+
+ bool take(const QDomElement &);
+};
+
+/**
+ * \class JingleIQResponder
+ * \brief A task that responds to jingle candidate queries with an empty reply.
+ */
+
+JingleIQResponder::JingleIQResponder(Task *parent) :Task(parent)
+{
+}
+
+JingleIQResponder::~JingleIQResponder()
+{
+}
+
+bool JingleIQResponder::take(const QDomElement &e)
+{
+ if(e.tagName() != "iq")
+ return false;
+
+ QDomElement first = e.firstChild().toElement();
+ if (!first.isNull() && first.attribute("xmlns") == JINGLE_NS) {
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ send(iq);
+ return true;
+ }
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * \brief A class for handling signals from libjingle.
+ */
+class JingleClientSlots : public sigslot::has_slots<>
+{
+public:
+ JingleClientSlots(JingleVoiceCaller *voiceCaller);
+
+ void callCreated(cricket::Call *call);
+ void callDestroyed(cricket::Call *call);
+ void sendStanza(cricket::SessionClient*, const buzz::XmlElement *stanza);
+ void requestSignaling();
+ void stateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state);
+
+private:
+ JingleVoiceCaller* voiceCaller_;
+};
+
+
+JingleClientSlots::JingleClientSlots(JingleVoiceCaller *voiceCaller) : voiceCaller_(voiceCaller)
+{
+}
+
+void JingleClientSlots::callCreated(cricket::Call *call)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+ call->SignalSessionState.connect(this, &JingleClientSlots::stateChanged);
+}
+
+void JingleClientSlots::callDestroyed(cricket::Call *call)
+{
+ qDebug("JingleClientSlots: Call destroyed");
+ Jid jid(call->sessions()[0]->remote_address().c_str());
+ if (voiceCaller_->calling(jid)) {
+ qDebug(QString("Removing unterminated call to %1").arg(jid.full()));
+ voiceCaller_->removeCall(jid);
+ emit voiceCaller_->terminated(jid);
+ }
+}
+
+void JingleClientSlots::sendStanza(cricket::SessionClient*, const buzz::XmlElement *stanza)
+{
+ QString st(stanza->Str().c_str());
+ st.replace("cli:iq","iq");
+ st.replace(":cli=","=");
+ fprintf(stderr,"bling\n");
+ voiceCaller_->sendStanza(st.latin1());
+ fprintf(stderr,"blong\n");
+ fprintf(stderr,"Sending stanza \n%s\n\n",st.latin1());
+}
+
+void JingleClientSlots::requestSignaling()
+{
+ voiceCaller_->session_manager_->OnSignalingReady();
+}
+
+void JingleClientSlots::stateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state)
+{
+ qDebug(QString("jinglevoicecaller.cpp: State changed (%1)").arg(state));
+ // Why is c_str() stuff needed to make it compile on OS X ?
+ Jid jid(session->remote_address().c_str());
+
+ if (state == cricket::Session::STATE_INIT) { }
+ else if (state == cricket::Session::STATE_SENTINITIATE) {
+ voiceCaller_->registerCall(jid,call);
+ }
+ else if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
+ voiceCaller_->registerCall(jid,call);
+ emit voiceCaller_->incoming(jid);
+ }
+ else if (state == cricket::Session::STATE_SENTACCEPT) { }
+ else if (state == cricket::Session::STATE_RECEIVEDACCEPT) {
+ emit voiceCaller_->accepted(jid);
+ }
+ else if (state == cricket::Session::STATE_SENTMODIFY) { }
+ else if (state == cricket::Session::STATE_RECEIVEDMODIFY) {
+ qWarning(QString("jinglevoicecaller.cpp: RECEIVEDMODIFY not implemented yet (was from %1)").arg(jid.full()));
+ }
+ else if (state == cricket::Session::STATE_SENTREJECT) { }
+ else if (state == cricket::Session::STATE_RECEIVEDREJECT) {
+ voiceCaller_->removeCall(jid);
+ emit voiceCaller_->rejected(jid);
+ }
+ else if (state == cricket::Session::STATE_SENTREDIRECT) { }
+ else if (state == cricket::Session::STATE_SENTTERMINATE) {
+ voiceCaller_->removeCall(jid);
+ emit voiceCaller_->terminated(jid);
+ }
+ else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) {
+ voiceCaller_->removeCall(jid);
+ emit voiceCaller_->terminated(jid);
+ }
+ else if (state == cricket::Session::STATE_INPROGRESS) {
+ emit voiceCaller_->in_progress(jid);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+/**
+ * \class JingleVoiceCaller
+ * \brief A Voice Calling implementation using libjingle.
+ */
+
+JingleVoiceCaller::JingleVoiceCaller(PsiAccount* acc) : VoiceCaller(acc)
+{
+ initialized_ = false;
+}
+
+void JingleVoiceCaller::initialize()
+{
+ if (initialized_)
+ return;
+
+ QString jid = ((ClientStream&) account()->client()->client()->stream()).jid().full();
+ qDebug(QString("jinglevoicecaller.cpp: Creating new caller for %1").arg(jid));
+ if (jid.isEmpty()) {
+ qWarning("jinglevoicecaller.cpp: Empty JID");
+ return;
+ }
+
+ buzz::Jid j(jid.ascii());
+ cricket::InitRandom(j.Str().c_str(),j.Str().size());
+
+ // Global variables
+ if (!socket_server_) {
+ socket_server_ = new cricket::PhysicalSocketServer();
+ cricket::Thread *t = new cricket::Thread((cricket::PhysicalSocketServer*)(socket_server_));
+ cricket::ThreadManager::SetCurrent(t);
+ t->Start();
+ thread_ = t;
+
+ stun_addr_ = new cricket::SocketAddress("64.233.167.126",19302);
+ network_manager_ = new cricket::NetworkManager();
+ port_allocator_ = new cricket::BasicPortAllocator((cricket::NetworkManager*)(network_manager_), (cricket::SocketAddress*)(stun_addr_), /* relay server */ NULL);
+ }
+
+ // Session manager
+ session_manager_ = new cricket::SessionManager((cricket::PortAllocator*)(port_allocator_), thread_);
+ slots_ = new JingleClientSlots(this);
+ session_manager_->SignalRequestSignaling.connect(slots_, &JingleClientSlots::requestSignaling);
+ session_manager_->OnSignalingReady();
+
+ // Phone Client
+ phone_client_ = new cricket::PhoneSessionClient(j, (cricket::SessionManager*)(session_manager_));
+ phone_client_->SignalCallCreate.connect(slots_, &JingleClientSlots::callCreated);
+ phone_client_->SignalCallDestroy.connect(slots_, &JingleClientSlots::callDestroyed);
+ phone_client_->SignalSendStanza.connect(slots_, &JingleClientSlots::sendStanza);
+
+ // IQ Responder
+ new JingleIQResponder(account()->client()->rootTask());
+
+ // Listen to incoming packets
+ connect(account()->client()->client(),SIGNAL(xmlIncoming(const QString&)),SLOT(receiveStanza(const QString&)));
+
+ initialized_ = true;
+}
+
+
+void JingleVoiceCaller::deinitialize()
+{
+ if (!initialized_)
+ return;
+
+ // Stop listening to incoming packets
+ disconnect(account()->client(),SIGNAL(xmlIncoming(const QString&)),this,SLOT(receiveStanza(const QString&)));
+
+ // Disconnect signals (is this needed)
+ //phone_client_->SignalCallCreate.disconnect(slots_);
+ //phone_client_->SignalSendStanza.disconnect(slots_);
+
+ // Delete objects
+ delete phone_client_;
+ delete session_manager_;
+ delete slots_;
+
+ initialized_ = false;
+}
+
+
+JingleVoiceCaller::~JingleVoiceCaller()
+{
+}
+
+bool JingleVoiceCaller::calling(const Jid& jid)
+{
+ return calls_.contains(jid.full());
+}
+
+void JingleVoiceCaller::call(const Jid& jid)
+{
+ qDebug(QString("jinglevoicecaller.cpp: Calling %1").arg(jid.full()));
+ cricket::Call *c = ((cricket::PhoneSessionClient*)(phone_client_))->CreateCall();
+ c->InitiateSession(buzz::Jid(jid.full().ascii()));
+ phone_client_->SetFocus(c);
+}
+
+void JingleVoiceCaller::accept(const Jid& j)
+{
+ qDebug("jinglevoicecaller.cpp: Accepting call");
+ cricket::Call* call = calls_[j.full()];
+ if (call != NULL) {
+ call->AcceptSession(call->sessions()[0]);
+ phone_client_->SetFocus(call);
+ }
+}
+
+void JingleVoiceCaller::reject(const Jid& j)
+{
+ qDebug("jinglevoicecaller.cpp: Rejecting call");
+ cricket::Call* call = calls_[j.full()];
+ if (call != NULL) {
+ call->RejectSession(call->sessions()[0]);
+ calls_.remove(j.full());
+ }
+}
+
+void JingleVoiceCaller::terminate(const Jid& j)
+{
+ qDebug(QString("jinglevoicecaller.cpp: Terminating call to %1").arg(j.full()));
+ cricket::Call* call = calls_[j.full()];
+ if (call != NULL) {
+ call->Terminate();
+ calls_.remove(j.full());
+ }
+}
+
+void JingleVoiceCaller::sendStanza(const char* stanza)
+{
+ account()->client()->send(QString(stanza));
+}
+
+void JingleVoiceCaller::registerCall(const Jid& jid, cricket::Call* call)
+{
+ qDebug("jinglevoicecaller.cpp: Registering call\n");
+ kdDebug(14000) << k_funcinfo << jid.full() << endl;
+ if (!calls_.contains(jid.full())) {
+ calls_[jid.full()] = call;
+ }
+// else {
+// qWarning("jinglevoicecaller.cpp: Auto-rejecting call because another call is currently open");
+// call->RejectSession(call->sessions()[0]);
+// }
+}
+
+void JingleVoiceCaller::removeCall(const Jid& j)
+{
+ qDebug(QString("JingleVoiceCaller: Removing call to %1").arg(j.full()));
+ calls_.remove(j.full());
+}
+
+void JingleVoiceCaller::receiveStanza(const QString& stanza)
+{
+ QDomDocument doc;
+ doc.setContent(stanza);
+
+ // Check if it is offline presence from an open chat
+ if (doc.documentElement().tagName() == "presence") {
+ Jid from = Jid(doc.documentElement().attribute("from"));
+ QString type = doc.documentElement().attribute("type");
+ if (type == "unavailable" && calls_.contains(from.full())) {
+ qDebug("JingleVoiceCaller: User went offline without closing a call.");
+ removeCall(from);
+ emit terminated(from);
+ }
+ return;
+ }
+
+ // Check if the packet is destined for libjingle.
+ // We could use Session::IsClientStanza to check this, but this one crashes
+ // for some reason.
+ QDomNode n = doc.documentElement().firstChild();
+ bool ok = false;
+ while (!n.isNull() && !ok) {
+ QDomElement e = n.toElement();
+ if (!e.isNull() && e.attribute("xmlns") == JINGLE_NS) {
+ ok = true;
+ }
+ n = n.nextSibling();
+ }
+
+ // Spread the word
+ if (ok) {
+ qDebug(QString("jinglevoicecaller.cpp: Handing down %1").arg(stanza));
+ buzz::XmlElement *e = buzz::XmlElement::ForStr(stanza.ascii());
+ phone_client_->OnIncomingStanza(e);
+ }
+}
+
+cricket::SocketServer* JingleVoiceCaller::socket_server_ = NULL;
+cricket::Thread* JingleVoiceCaller::thread_ = NULL;
+cricket::NetworkManager* JingleVoiceCaller::network_manager_ = NULL;
+cricket::BasicPortAllocator* JingleVoiceCaller::port_allocator_ = NULL;
+cricket::SocketAddress* JingleVoiceCaller::stun_addr_ = NULL;
diff --git a/kopete/protocols/jabber/jingle/jinglevoicecaller.h b/kopete/protocols/jabber/jingle/jinglevoicecaller.h
new file mode 100644
index 00000000..4448d530
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicecaller.h
@@ -0,0 +1,72 @@
+#define PsiAccount JabberAccount
+
+#ifndef JINGLEVOICECALLER_H
+#define JINGLEVOICECALLER_H
+
+#include <qmap.h>
+
+#include "im.h"
+#include "voicecaller.h"
+
+using namespace XMPP;
+
+
+class PsiAccount;
+
+namespace cricket {
+ class SocketServer;
+ class Thread;
+ class NetworkManager;
+ class BasicPortAllocator;
+ class SessionManager;
+ class PhoneSessionClient;
+ class Call;
+ class SocketAddress;
+}
+
+class JingleClientSlots;
+class JingleCallSlots;
+
+
+class JingleVoiceCaller : public VoiceCaller
+{
+ Q_OBJECT
+
+ friend class JingleClientSlots;
+
+public:
+ JingleVoiceCaller(PsiAccount* account);
+ ~JingleVoiceCaller();
+
+ virtual bool calling(const Jid&);
+
+ virtual void initialize();
+ virtual void deinitialize();
+
+ virtual void call(const Jid&);
+ virtual void accept(const Jid&);
+ virtual void reject(const Jid&);
+ virtual void terminate(const Jid&);
+
+protected:
+ void sendStanza(const char*);
+ void registerCall(const Jid&, cricket::Call*);
+ void removeCall(const Jid&);
+
+protected slots:
+ void receiveStanza(const QString&);
+
+private:
+ bool initialized_;
+ static cricket::SocketServer *socket_server_;
+ static cricket::Thread *thread_;
+ static cricket::NetworkManager *network_manager_;
+ static cricket::BasicPortAllocator *port_allocator_;
+ static cricket::SocketAddress *stun_addr_;
+ cricket::SessionManager *session_manager_;
+ cricket::PhoneSessionClient *phone_client_;
+ JingleClientSlots *slots_;
+ QMap<QString,cricket::Call*> calls_;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesession.cpp b/kopete/protocols/jabber/jingle/jinglevoicesession.cpp
new file mode 100644
index 00000000..09019ce4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesession.cpp
@@ -0,0 +1,333 @@
+/*
+ jinglevoicesession.cpp - Define a Jingle voice session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+// libjingle before everything else to not clash with Qt
+#define POSIX
+#include "talk/xmpp/constants.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/thread.h"
+#include "talk/base/socketaddress.h"
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/sessionsendtask.h"
+
+#include "jinglevoicesession.h"
+#include "jinglesessionmanager.h"
+
+// Qt includes
+#include <qdom.h>
+
+// KDE includes
+#include <kdebug.h>
+
+// Kopete Jabber includes
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+
+#include <xmpp.h>
+#include <xmpp_xmlcommon.h>
+
+#define JINGLE_NS "http://www.google.com/session"
+#define JINGLE_VOICE_SESSION_NS "http://www.google.com/session/phone"
+
+static bool hasPeer(const JingleVoiceSession::JidList &jidList, const XMPP::Jid &peer)
+{
+ JingleVoiceSession::JidList::ConstIterator it, itEnd = jidList.constEnd();
+ for(it = jidList.constBegin(); it != itEnd; ++it)
+ {
+ if( (*it).compare(peer, true) )
+ return true;
+ }
+
+ return false;
+}
+//BEGIN SlotsProxy
+/**
+ * This class is used to receive signals from libjingle,
+ * which is are not compatible with Qt signals.
+ * So it's a proxy between JingeVoiceSession(qt)<->linjingle class.
+ */
+class JingleVoiceSession::SlotsProxy : public sigslot::has_slots<>
+{
+public:
+ SlotsProxy(JingleVoiceSession *parent)
+ : voiceSession(parent)
+ {}
+
+ void OnCallCreated(cricket::Call* call)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "SlotsProxy: CallCreated." << endl;
+
+ call->SignalSessionState.connect(this, &JingleVoiceSession::SlotsProxy::PhoneSessionStateChanged);
+ voiceSession->setCall(call);
+ }
+
+ void PhoneSessionStateChanged(cricket::Call *call, cricket::Session *session, cricket::Session::State state)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "State changed: " << state << endl;
+
+ XMPP::Jid jid(session->remote_address().c_str());
+
+ // Do nothing if the session do not contain a peers.
+ //if( !voiceSession->peers().contains(jid) )
+ if( !hasPeer(voiceSession->peers(), jid) )
+ return;
+
+ if (state == cricket::Session::STATE_INIT)
+ {}
+ else if (state == cricket::Session::STATE_SENTINITIATE)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDINITIATE)
+ {
+ voiceSession->setCall(call);
+ }
+ else if (state == cricket::Session::STATE_SENTACCEPT)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDACCEPT)
+ {
+ emit voiceSession->accepted();
+ }
+ else if (state == cricket::Session::STATE_SENTMODIFY)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDMODIFY)
+ {
+ //qWarning(QString("jinglevoicecaller.cpp: RECEIVEDMODIFY not implemented yet (was from %1)").arg(jid.full()));
+ }
+ else if (state == cricket::Session::STATE_SENTREJECT)
+ {}
+ else if (state == cricket::Session::STATE_RECEIVEDREJECT)
+ {
+ emit voiceSession->declined();
+ }
+ else if (state == cricket::Session::STATE_SENTREDIRECT)
+ {}
+ else if (state == cricket::Session::STATE_SENTTERMINATE)
+ {
+ emit voiceSession->terminated();
+ }
+ else if (state == cricket::Session::STATE_RECEIVEDTERMINATE)
+ {
+ emit voiceSession->terminated();
+ }
+ else if (state == cricket::Session::STATE_INPROGRESS)
+ {
+ emit voiceSession->sessionStarted();
+ }
+ }
+
+ void OnSendingStanza(cricket::SessionClient*, const buzz::XmlElement *buzzStanza)
+ {
+ QString irisStanza(buzzStanza->Str().c_str());
+ irisStanza.replace("cli:iq","iq");
+ irisStanza.replace(":cli=","=");
+
+ voiceSession->sendStanza(irisStanza);
+ }
+private:
+ JingleVoiceSession *voiceSession;
+};
+//END SlotsProxy
+
+//BEGIN JingleIQResponder
+class JingleVoiceSession::JingleIQResponder : public XMPP::Task
+{
+public:
+ JingleIQResponder(XMPP::Task *);
+ ~JingleIQResponder();
+
+ bool take(const QDomElement &);
+};
+
+/**
+ * \class JingleIQResponder
+ * \brief A task that responds to jingle candidate queries with an empty reply.
+ */
+
+JingleVoiceSession::JingleIQResponder::JingleIQResponder(Task *parent) :Task(parent)
+{
+}
+
+JingleVoiceSession::JingleIQResponder::~JingleIQResponder()
+{
+}
+
+bool JingleVoiceSession::JingleIQResponder::take(const QDomElement &e)
+{
+ if(e.tagName() != "iq")
+ return false;
+
+ QDomElement first = e.firstChild().toElement();
+ if (!first.isNull() && first.attribute("xmlns") == JINGLE_NS) {
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ send(iq);
+ return true;
+ }
+
+ return false;
+}
+//END JingleIQResponder
+
+class JingleVoiceSession::Private
+{
+public:
+ Private()
+ : phoneSessionClient(0L), currentCall(0L)
+ {}
+
+ ~Private()
+ {
+ if(currentCall)
+ currentCall->Terminate();
+
+ delete currentCall;
+ }
+
+ cricket::PhoneSessionClient *phoneSessionClient;
+ cricket::Call* currentCall;
+};
+
+JingleVoiceSession::JingleVoiceSession(JabberAccount *account, const JidList &peers)
+ : JingleSession(account, peers), d(new Private)
+{
+ slotsProxy = new SlotsProxy(this);
+
+ buzz::Jid buzzJid( account->client()->jid().full().ascii() );
+
+ // Create the phone(voice) session.
+ d->phoneSessionClient = new cricket::PhoneSessionClient( buzzJid, account->sessionManager()->cricketSessionManager() );
+
+ d->phoneSessionClient->SignalSendStanza.connect(slotsProxy, &JingleVoiceSession::SlotsProxy::OnSendingStanza);
+ d->phoneSessionClient->SignalCallCreate.connect(slotsProxy, &JingleVoiceSession::SlotsProxy::OnCallCreated);
+
+ // Listen to incoming packets
+ connect(account->client()->client(), SIGNAL(xmlIncoming(const QString&)), this, SLOT(receiveStanza(const QString&)));
+
+ new JingleIQResponder(account->client()->rootTask());
+}
+
+JingleVoiceSession::~JingleVoiceSession()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << endl;
+ delete slotsProxy;
+ delete d;
+}
+
+QString JingleVoiceSession::sessionType()
+{
+ return QString(JINGLE_VOICE_SESSION_NS);
+}
+
+void JingleVoiceSession::start()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Starting a voice session..." << endl;
+ d->currentCall = d->phoneSessionClient->CreateCall();
+
+ QString firstPeerJid = ((XMPP::Jid)peers().first()).full();
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "With peer: " << firstPeerJid << endl;
+ d->currentCall->InitiateSession( buzz::Jid(firstPeerJid.ascii()) );
+
+ d->phoneSessionClient->SetFocus(d->currentCall);
+}
+
+void JingleVoiceSession::accept()
+{
+ if(d->currentCall)
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Accepting a voice session..." << endl;
+
+ d->currentCall->AcceptSession(d->currentCall->sessions()[0]);
+ d->phoneSessionClient->SetFocus(d->currentCall);
+ }
+}
+
+void JingleVoiceSession::decline()
+{
+ if(d->currentCall)
+ {
+ d->currentCall->RejectSession(d->currentCall->sessions()[0]);
+ }
+}
+
+void JingleVoiceSession::terminate()
+{
+ if(d->currentCall)
+ {
+ d->currentCall->Terminate();
+ }
+}
+
+void JingleVoiceSession::setCall(cricket::Call *call)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Updating cricket::call object." << endl;
+ d->currentCall = call;
+ d->phoneSessionClient->SetFocus(d->currentCall);
+}
+
+void JingleVoiceSession::receiveStanza(const QString &stanza)
+{
+ QDomDocument doc;
+ doc.setContent(stanza);
+
+ // Check if it is offline presence from an open chat
+ if( doc.documentElement().tagName() == "presence" )
+ {
+ XMPP::Jid from = XMPP::Jid(doc.documentElement().attribute("from"));
+ QString type = doc.documentElement().attribute("type");
+ if( type == "unavailable" && hasPeer(peers(), from) )
+ {
+ //qDebug("JingleVoiceCaller: User went offline without closing a call.");
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "User went offline without closing a call." << endl;
+ emit terminated();
+ }
+ return;
+ }
+
+ // Check if the packet is destined for libjingle.
+ // We could use Session::IsClientStanza to check this, but this one crashes
+ // for some reason.
+ QDomNode node = doc.documentElement().firstChild();
+ bool ok = false;
+ while( !node.isNull() && !ok )
+ {
+ QDomElement element = node.toElement();
+ if( !element.isNull() && element.attribute("xmlns") == JINGLE_NS)
+ {
+ ok = true;
+ }
+ node = node.nextSibling();
+ }
+
+ // Spread the word
+ if( ok )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Handing down buzz::stanza" << endl;
+ buzz::XmlElement *e = buzz::XmlElement::ForStr(stanza.ascii());
+ d->phoneSessionClient->OnIncomingStanza(e);
+ }
+}
+
+#include "jinglevoicesession.moc"
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesession.h b/kopete/protocols/jabber/jingle/jinglevoicesession.h
new file mode 100644
index 00000000..e70d3b54
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesession.h
@@ -0,0 +1,70 @@
+/*
+ jinglevoicesession.h - Define a Jingle voice session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef JINGLEVOICESESSION_H
+#define JINGLEVOICESESSION_H
+
+#include <jinglesession.h>
+
+#include <xmpp.h> // XMPP::Jid
+#include <qvaluelist.h>
+
+namespace cricket
+{
+ class Call;
+}
+
+class JabberAccount;
+class JingleSession;
+
+/**
+ * Implement a Jingle voice peer-to-peer session that is compatible with Google Talk voice offering.
+ *
+ * @author Michaël Larouche
+*/
+class JingleVoiceSession : public JingleSession
+{
+ Q_OBJECT
+public:
+ typedef QValueList<XMPP::Jid> JidList;
+
+ JingleVoiceSession(JabberAccount *account, const JidList &peers);
+ virtual ~JingleVoiceSession();
+
+ virtual QString sessionType();
+
+public slots:
+ virtual void accept();
+ virtual void decline();
+ virtual void start();
+ virtual void terminate();
+
+protected slots:
+ void receiveStanza(const QString &stanza);
+
+private:
+ void setCall(cricket::Call *call);
+
+ class Private;
+ Private *d;
+
+ class SlotsProxy;
+ SlotsProxy *slotsProxy;
+
+ class JingleIQResponder;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.cpp b/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.cpp
new file mode 100644
index 00000000..9fb61274
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.cpp
@@ -0,0 +1,208 @@
+/*
+ jinglevoicesessiondialog.cpp - GUI for a voice session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "jinglevoicesessiondialog.h"
+
+// Qt includes
+#include <qlabel.h>
+#include <qpixmap.h>
+#include <qimage.h>
+
+// Jingle includes
+// #include "jinglevoicesession.h"
+// #include "jinglesessionmanager.h"
+#include "voicecaller.h"
+
+// KDE includes
+#include <klocale.h>
+#include <kpushbutton.h>
+
+// Kopete includes
+#include "jabberaccount.h"
+#include "jabbercontact.h"
+#include "jabbercontactpool.h"
+
+#include "kopeteglobal.h"
+#include "kopetemetacontact.h"
+
+using namespace XMPP;
+
+JingleVoiceSessionDialog::JingleVoiceSessionDialog(const Jid &peerJid, VoiceCaller *caller, QWidget *parent, const char *name)
+ : JingleVoiceSessionDialogBase(parent, name), m_session(caller), m_peerJid(peerJid), m_sessionState(Incoming)
+{
+ QString contactJid = m_peerJid.full();
+ setCaption( i18n("Voice session with %1").arg(contactJid) );
+
+ connect(buttonAccept, SIGNAL(clicked()), this, SLOT(slotAcceptClicked()));
+ connect(buttonDecline, SIGNAL(clicked()), this, SLOT(slotDeclineClicked()));
+ connect(buttonTerminate, SIGNAL(clicked()), this, SLOT(slotTerminateClicked()));
+
+// NOTE: Disabled for 0.12
+#if 0
+ connect(m_session, SIGNAL(sessionStarted()), this, SLOT(sessionStarted()));
+ connect(m_session, SIGNAL(accepted()), this, SLOT(sessionAccepted()));
+ connect(m_session, SIGNAL(declined()), this, SLOT(sessionDeclined()));
+ connect(m_session, SIGNAL(terminated()), this, SLOT(sessionTerminated()));
+#endif
+ connect(m_session, SIGNAL(accepted( const Jid & )), this, SLOT( sessionAccepted(const Jid &) ));
+ connect(m_session, SIGNAL(in_progress( const Jid & )), this, SLOT( sessionStarted(const Jid &) ));
+ connect(m_session, SIGNAL(rejected( const Jid& )), this, SLOT( sessionDeclined(const Jid &) ));
+ connect(m_session, SIGNAL(terminated( const Jid& )), this, SLOT( sessionTerminated(const Jid &) ));
+
+ // Find JabberContact for the peer and fill this dialog with contact information.
+ JabberContact *peerContact = static_cast<JabberContact*>( m_session->account()->contactPool()->findRelevantRecipient( m_peerJid ) );
+ if( peerContact )
+ {
+ setContactInformation( peerContact );
+ }
+
+ labelSessionStatus->setText( i18n("Incoming Session...") );
+ buttonAccept->setEnabled(true);
+ buttonDecline->setEnabled(true);
+}
+
+JingleVoiceSessionDialog::~JingleVoiceSessionDialog()
+{
+ //m_session->account()->sessionManager()->removeSession(m_session);
+}
+
+void JingleVoiceSessionDialog::setContactInformation(JabberContact *contact)
+{
+ if( contact->metaContact() )
+ {
+ labelDisplayName->setText( contact->metaContact()->displayName() );
+ labelContactPhoto->setPixmap( QPixmap(contact->metaContact()->photo()) );
+ }
+ else
+ {
+ labelDisplayName->setText( contact->nickName() );
+ labelDisplayName->setPixmap( QPixmap(contact->property(Kopete::Global::Properties::self()->photo()).value().toString()) );
+ }
+}
+
+void JingleVoiceSessionDialog::start()
+{
+ labelSessionStatus->setText( i18n("Waiting for other peer...") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(true);
+ //m_session->start();
+ m_session->call( m_peerJid );
+ m_sessionState = Waiting;
+}
+
+void JingleVoiceSessionDialog::slotAcceptClicked()
+{
+ labelSessionStatus->setText( i18n("Session accepted.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(true);
+
+ //m_session->accept();
+ m_session->accept( m_peerJid );
+ m_sessionState = Accepted;
+}
+
+void JingleVoiceSessionDialog::slotDeclineClicked()
+{
+ labelSessionStatus->setText( i18n("Session declined.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(false);
+
+ //m_session->decline();
+ m_session->reject( m_peerJid );
+ m_sessionState = Declined;
+ finalize();
+}
+
+void JingleVoiceSessionDialog::slotTerminateClicked()
+{
+ labelSessionStatus->setText( i18n("Session terminated.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(false);
+
+ //m_session->terminate();
+ m_session->terminate( m_peerJid );
+ m_sessionState = Terminated;
+ finalize();
+ close();
+}
+
+void JingleVoiceSessionDialog::sessionStarted(const Jid &jid)
+{
+ if( m_peerJid.compare(jid) )
+ {
+ labelSessionStatus->setText( i18n("Session in progress.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(true);
+ m_sessionState = Started;
+ }
+}
+
+void JingleVoiceSessionDialog::sessionAccepted(const Jid &jid)
+{
+ if( m_peerJid.compare(jid) )
+ {
+ labelSessionStatus->setText( i18n("Session accepted.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(true);
+ m_sessionState = Accepted;
+ }
+}
+
+void JingleVoiceSessionDialog::sessionDeclined(const Jid &jid)
+{
+ if( m_peerJid.compare(jid) )
+ {
+ labelSessionStatus->setText( i18n("Session declined.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(false);
+ m_sessionState = Declined;
+ }
+}
+
+void JingleVoiceSessionDialog::sessionTerminated(const Jid &jid)
+{
+ if( m_peerJid.compare(jid) )
+ {
+ labelSessionStatus->setText( i18n("Session terminated.") );
+ buttonAccept->setEnabled(false);
+ buttonDecline->setEnabled(false);
+ buttonTerminate->setEnabled(false);
+ m_sessionState = Terminated;
+ }
+}
+
+void JingleVoiceSessionDialog::reject()
+{
+ finalize();
+ QDialog::reject();
+}
+
+void JingleVoiceSessionDialog::finalize()
+{
+ disconnect(m_session, SIGNAL(accepted( const Jid & )), this, SLOT( sessionAccepted(const Jid &) ));
+ disconnect(m_session, SIGNAL(in_progress( const Jid & )), this, SLOT( sessionStarted(const Jid &) ));
+ disconnect(m_session, SIGNAL(rejected( const Jid& )), this, SLOT( sessionDeclined(const Jid &) ));
+ disconnect(m_session, SIGNAL(terminated( const Jid& )), this, SLOT( sessionTerminated(const Jid &) ));
+}
+
+#include "jinglevoicesessiondialog.moc"
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.h b/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.h
new file mode 100644
index 00000000..29d0c091
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesessiondialog.h
@@ -0,0 +1,66 @@
+/*
+ jinglevoicesessiondialog.h - GUI for a voice session.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef JINGLEVOICESESSIONDIALOG_H
+#define JINGLEVOICESESSIONDIALOG_H
+
+#include "jinglevoicesessiondialogbase.h"
+
+#include <im.h>
+#include <xmpp.h>
+
+using namespace XMPP;
+
+class JabberContact;
+class VoiceCaller;
+
+class JingleVoiceSessionDialog : public JingleVoiceSessionDialogBase
+{
+ Q_OBJECT
+public:
+ enum SessionState { Incoming, Waiting, Accepted, Declined, Started, Terminated };
+
+ JingleVoiceSessionDialog(const Jid &peerJid, VoiceCaller *caller, QWidget *parent = 0, const char *name = 0);
+ ~JingleVoiceSessionDialog();
+
+public slots:
+ void start();
+
+protected slots:
+ void reject();
+
+protected:
+ void finalize();
+
+private slots:
+ void slotAcceptClicked();
+ void slotDeclineClicked();
+ void slotTerminateClicked();
+
+ void sessionStarted(const Jid &jid);
+ void sessionAccepted(const Jid &jid);
+ void sessionDeclined(const Jid &jid);
+ void sessionTerminated(const Jid &jid);
+
+private:
+ void setContactInformation(JabberContact *contact);
+
+ VoiceCaller *m_session;
+ Jid m_peerJid;
+ SessionState m_sessionState;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/jinglevoicesessiondialogbase.ui b/kopete/protocols/jabber/jingle/jinglevoicesessiondialogbase.ui
new file mode 100644
index 00000000..0dc9ab35
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglevoicesessiondialogbase.ui
@@ -0,0 +1,369 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>JingleVoiceSessionDialogBase</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>JingleVoiceSessionDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>329</width>
+ <height>188</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>JabberVoiceSessionDialogBase</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout8</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Voice session with:</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelContactPhoto</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>128</width>
+ <height>128</height>
+ </size>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout7</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelDisplayName</cstring>
+ </property>
+ <property name="text">
+ <string>Contact displayname</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer12</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <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>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonAccept</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Accep&amp;t</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonDecline</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Decline</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonTerminate</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Termi&amp;nate</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Current status:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelSessionStatus</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Session status</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/jingle/jinglewatchsessiontask.cpp b/kopete/protocols/jabber/jingle/jinglewatchsessiontask.cpp
new file mode 100644
index 00000000..fc7de053
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglewatchsessiontask.cpp
@@ -0,0 +1,75 @@
+/*
+ jingleswatchsessiontask.cpp - Watch for incoming Jingle sessions.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "jinglewatchsessiontask.h"
+
+#include <kdebug.h>
+
+#include "jabberprotocol.h"
+
+#define JINGLE_NS "http://www.google.com/session"
+
+JingleWatchSessionTask::JingleWatchSessionTask(XMPP::Task *parent)
+ : Task(parent)
+{}
+
+JingleWatchSessionTask::~JingleWatchSessionTask()
+{}
+
+//NOTE: This task watch for pre-JEP session.
+bool JingleWatchSessionTask::take(const QDomElement &element)
+{
+ if(element.tagName() != "iq")
+ return false;
+
+ QString sessionType, initiator;
+
+ QDomElement first = element.firstChild().toElement();
+ if( !first.isNull() && first.attribute("xmlns") == JINGLE_NS && first.tagName() == "session" )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Checking for incoming sesssion." << endl;
+ initiator = first.attribute("initiator");
+
+ // Only proceed initiate type Jingle XMPP call.
+ if( first.attribute("type") != QString::fromUtf8("initiate") )
+ return false;
+
+ int nodeIndex;
+
+ QDomNodeList nodeList = first.childNodes();
+ // Do not check first child
+ for(nodeIndex = 0; nodeIndex < nodeList.length(); nodeIndex++)
+ {
+ QDomElement nodeElement = nodeList.item(nodeIndex).toElement();
+ if(nodeElement.tagName() == "description")
+ {
+ sessionType = nodeElement.attribute("xmlns");
+ }
+ }
+
+ if( !initiator.isEmpty() && !sessionType.isEmpty() )
+ {
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Emmiting incoming sesssion." << endl;
+ emit watchSession(sessionType, initiator);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#include "jinglewatchsessiontask.moc" \ No newline at end of file
diff --git a/kopete/protocols/jabber/jingle/jinglewatchsessiontask.h b/kopete/protocols/jabber/jingle/jinglewatchsessiontask.h
new file mode 100644
index 00000000..99b76661
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/jinglewatchsessiontask.h
@@ -0,0 +1,39 @@
+/*
+ jingleswatchsessiontask.h - Watch for incoming Jingle sessions.
+
+ Copyright (c) 2006 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2001-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef JINGLEWATCHSESSIONTASK_H
+#define JINGLEWATCHSESSIONTASK_H
+
+#include <xmpp_tasks.h>
+
+/**
+ * This task watch for incoming Jingle session and notify manager.
+ * It is declared in the header to be "moc"-able.
+ */
+class JingleWatchSessionTask : public XMPP::Task
+{
+ Q_OBJECT
+public:
+ JingleWatchSessionTask(XMPP::Task *parent);
+ ~JingleWatchSessionTask();
+
+ bool take(const QDomElement &element);
+
+signals:
+ void watchSession(const QString &sessionType, const QString &initiator);
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/AUTHORS b/kopete/protocols/jabber/jingle/libjingle/AUTHORS
new file mode 100644
index 00000000..e491a9e7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/AUTHORS
@@ -0,0 +1 @@
+Google Inc.
diff --git a/kopete/protocols/jabber/jingle/libjingle/COPYING b/kopete/protocols/jabber/jingle/libjingle/COPYING
new file mode 100644
index 00000000..d58182b1
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/COPYING
@@ -0,0 +1,25 @@
+Copyright (c) 2004--2005, Google Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
+WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/kopete/protocols/jabber/jingle/libjingle/ChangeLog b/kopete/protocols/jabber/jingle/libjingle/ChangeLog
new file mode 100644
index 00000000..6d15ac52
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/ChangeLog
@@ -0,0 +1,4 @@
+Libjingle
+
+0.1.0 - Dec 15 2005
+ - Initial release
diff --git a/kopete/protocols/jabber/jingle/libjingle/INSTALL b/kopete/protocols/jabber/jingle/libjingle/INSTALL
new file mode 100644
index 00000000..a4b34144
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/INSTALL
@@ -0,0 +1,229 @@
+Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
+Foundation, Inc.
+
+ This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=PATH' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the `--target=TYPE' option to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+will cause the specified gcc to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/Makefile.am
new file mode 100644
index 00000000..164f7058
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/Makefile.am
@@ -0,0 +1,4 @@
+SUBDIRS=talk
+
+dist-hook:
+ sed -i -f talk/sanitize.sed `find $(distdir) -type f` \ No newline at end of file
diff --git a/kopete/protocols/jabber/jingle/libjingle/NEWS b/kopete/protocols/jabber/jingle/libjingle/NEWS
new file mode 100644
index 00000000..1694c754
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/NEWS
@@ -0,0 +1 @@
+* Initial source release
diff --git a/kopete/protocols/jabber/jingle/libjingle/README b/kopete/protocols/jabber/jingle/libjingle/README
new file mode 100644
index 00000000..ec130b36
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/README
@@ -0,0 +1,59 @@
+Libjingle
+
+Libjingle is a set of components provided by Google to interoperate with Google
+Talk's peer-to-peer and voice capabilities. This package will create several
+static libraries you may link to your project as needed.
+
+-talk - No source files in talk/, just these subdirectories
+|-base - Contains basic low-level portable utility functions for
+| things like threads and sockets
+|-p2p - The P2P stack
+ |-base - Base p2p functionality
+ |-client - Hooks to tie it into XMPP
+|-session - Signaling
+ |-phone - Signaling code specific to making phone calls
+|-third_party - Components that aren't ours
+ |-mediastreamer - Media components for dealing with sound hardware and
+ | voice codecs
+|-xmllite - XML parser
+|-xmpp - XMPP engine
+
+In addition, this package contains two examples in talk/examples which
+illustrate the basic concepts of how the provided classes work.
+
+The xmllite component of libjingle depends on expat. You can download expat
+from http://expat.sourceforge.net/.
+
+mediastreamer, the media components used by the example applications depend on
+the oRTP and iLBC components from linphone, which can be found at
+http://www.linphone.org. Linphone, in turn depends on GLib, which can be found
+at http://www.gtk.org. This GLib dependency should be removed in future
+releases.
+
+Building Libjingle
+
+Once the dependencies are installed, run ./configure. ./configure will return
+an error if it failed to locate the proper dependencies. If ./configure
+succeeds, run 'make' to build the components and examples.
+
+When the build is complete, you can run the call example from
+talk/examples/call. This will ask you for your GMail username and your GMail
+auth cookie. Your GMail auth cookie is the GX cookie from mail.google.com
+found in your web browser.
+
+Relay Server
+
+Libjingle will also build a relay server that may be used to relay traffic
+when a direct peer-to-peer connection could not be established. The relay
+server will build in talk/p2p/base/relayserver and will listen on UDP
+ports 5000 and 5001. See the Libjingle Developer Guide at
+http://code.google.com/apis/talk/index.html for information about configuring
+a client to use this relay server.
+
+STUN Server
+
+Lastly, Libjingle builds a STUN server which implements the STUN protocol for
+Simple Traversal of UDP over NAT. The STUN server is built as
+talk/p2p/base/stunserver and listens on UDP port 7000. See the Libjingle
+Developer Guide at http://code.google.com/apis/talk/index.html for information
+about configuring a client to use this STUN server.
diff --git a/kopete/protocols/jabber/jingle/libjingle/libjingle.pro b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro
new file mode 100644
index 00000000..53c8e293
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/libjingle.pro
@@ -0,0 +1,142 @@
+TEMPLATE = lib
+CONFIG += staticlib
+CONFIG += debug
+
+target.extra = true
+
+exists(../../conf.pri) {
+ include(../../conf.pri)
+}
+
+JINGLE_CPP = .
+INCLUDEPATH += $$JINGLE_CPP $$JINGLE_CPP/talk/third_party/mediastreamer
+DEFINES += POSIX
+OBJECTS_DIR = $$JINGLE_CPP/.obj
+
+# Base
+SOURCES += \
+ $$JINGLE_CPP/talk/base/asyncpacketsocket.cc \
+ $$JINGLE_CPP/talk/base/asynctcpsocket.cc \
+ $$JINGLE_CPP/talk/base/asyncudpsocket.cc \
+ $$JINGLE_CPP/talk/base/base64.cc \
+ $$JINGLE_CPP/talk/base/bytebuffer.cc \
+ $$JINGLE_CPP/talk/base/md5c.c \
+ $$JINGLE_CPP/talk/base/messagequeue.cc \
+ $$JINGLE_CPP/talk/base/network.cc \
+ $$JINGLE_CPP/talk/base/physicalsocketserver.cc \
+ $$JINGLE_CPP/talk/base/socketadapters.cc \
+ $$JINGLE_CPP/talk/base/socketaddress.cc \
+ $$JINGLE_CPP/talk/base/task.cc \
+ $$JINGLE_CPP/talk/base/taskrunner.cc \
+ $$JINGLE_CPP/talk/base/thread.cc \
+ $$JINGLE_CPP/talk/base/time.cc
+
+# Not needed ?
+#$$JINGLE_CPP/talk/base/socketaddresspair.cc \
+#$$JINGLE_CPP/talk/base/host.cc \
+
+# P2P Base
+SOURCES += \
+ $$JINGLE_CPP/talk/p2p/base/helpers.cc \
+ $$JINGLE_CPP/talk/p2p/base/p2psocket.cc \
+ $$JINGLE_CPP/talk/p2p/base/port.cc \
+ $$JINGLE_CPP/talk/p2p/base/relayport.cc \
+ $$JINGLE_CPP/talk/p2p/base/session.cc \
+ $$JINGLE_CPP/talk/p2p/base/sessionmanager.cc \
+ $$JINGLE_CPP/talk/p2p/base/socketmanager.cc \
+ $$JINGLE_CPP/talk/p2p/base/stun.cc \
+ $$JINGLE_CPP/talk/p2p/base/stunport.cc \
+ $$JINGLE_CPP/talk/p2p/base/stunrequest.cc \
+ $$JINGLE_CPP/talk/p2p/base/tcpport.cc \
+ $$JINGLE_CPP/talk/p2p/base/udpport.cc
+
+# P2P Client
+SOURCES += \
+ $$JINGLE_CPP/talk/p2p/client/basicportallocator.cc \
+ $$JINGLE_CPP/talk/p2p/client/sessionclient.cc \
+ $$JINGLE_CPP/talk/p2p/client/socketmonitor.cc
+
+
+# XMLLite
+SOURCES += \
+ $$JINGLE_CPP/talk/xmllite/qname.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlbuilder.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlconstants.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlelement.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlnsstack.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlparser.cc \
+ $$JINGLE_CPP/talk/xmllite/xmlprinter.cc
+
+# XMPP
+SOURCES += \
+ $$JINGLE_CPP/talk/xmpp/constants.cc \
+ $$JINGLE_CPP/talk/xmpp/jid.cc \
+ $$JINGLE_CPP/talk/xmpp/saslmechanism.cc \
+ $$JINGLE_CPP/talk/xmpp/xmppclient.cc \
+ $$JINGLE_CPP/talk/xmpp/xmppengineimpl.cc \
+ $$JINGLE_CPP/talk/xmpp/xmppengineimpl_iq.cc \
+ $$JINGLE_CPP/talk/xmpp/xmpplogintask.cc \
+ $$JINGLE_CPP/talk/xmpp/xmppstanzaparser.cc \
+ $$JINGLE_CPP/talk/xmpp/xmpptask.cc
+
+# Session
+SOURCES += \
+ $$JINGLE_CPP/talk/session/phone/call.cc \
+ $$JINGLE_CPP/talk/session/phone/audiomonitor.cc \
+ $$JINGLE_CPP/talk/session/phone/phonesessionclient.cc \
+ $$JINGLE_CPP/talk/session/phone/channelmanager.cc \
+ $$JINGLE_CPP/talk/session/phone/linphonemediaengine.cc \
+ $$JINGLE_CPP/talk/session/phone/voicechannel.cc
+
+#contains(DEFINES, HAVE_PORTAUDIO) {
+# SOURCES += \
+# $$JINGLE_CPP/talk/session/phone/portaudiomediaengine.cc
+#}
+
+
+# Mediastreamer
+SOURCES += \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/audiostream.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/ms.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msAlawdec.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msAlawenc.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msbuffer.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mscodec.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mscopy.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msfdispatcher.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msfifo.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msfilter.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msilbcdec.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msilbcenc.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msMUlawdec.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msMUlawenc.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msnosync.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msossread.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msosswrite.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msqdispatcher.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msqueue.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msread.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msringplayer.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msrtprecv.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msrtpsend.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mssoundread.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mssoundwrite.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msspeexdec.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/msspeexenc.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mssync.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mstimer.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/mswrite.c \
+ $$JINGLE_CPP/talk/third_party/mediastreamer/sndcard.c
+
+contains(DEFINES, HAVE_ALSA_ASOUNDLIB_H) {
+ SOURCES += $$JINGLE_CPP/talk/third_party/mediastreamer/alsacard.c
+}
+
+contains(DEFINES, HAVE_PORTAUDIO) {
+ SOURCES += $$JINGLE_CPP/talk/third_party/mediastreamer/portaudiocard.c
+}
+
+#$$JINGLE_CPP/talk/third_party/mediastreamer/osscard.c \
+#$$JINGLE_CPP/talk/third_party/mediastreamer/jackcard.c \
+#$$JINGLE_CPP/talk/third_party/mediastreamer/hpuxsndcard.c \
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am
new file mode 100644
index 00000000..2a845dc0
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=base p2p xmllite xmpp session third_party
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am
new file mode 100644
index 00000000..2921049a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/Makefile.am
@@ -0,0 +1,62 @@
+## Does not compile with final
+KDE_OPTIONS = nofinal
+
+libcricketbase_la_SOURCES = socketaddress.cc \
+ jtime.cc \
+ asyncudpsocket.cc \
+ messagequeue.cc \
+ thread.cc \
+ physicalsocketserver.cc \
+ bytebuffer.cc \
+ asyncpacketsocket.cc \
+ network.cc \
+ asynctcpsocket.cc \
+ socketadapters.cc \
+ md5c.c \
+ base64.cc \
+ task.cc \
+ taskrunner.cc \
+ host.cc \
+ socketaddresspair.cc
+
+noinst_HEADERS = asyncfile.h \
+ common.h \
+ asyncpacketsocket.h \
+ socketfactory.h \
+ asyncsocket.h \
+ socket.h \
+ asynctcpsocket.h \
+ linked_ptr.h \
+ asyncudpsocket.h \
+ logging.h \
+ socketserver.h \
+ base64.h \
+ md5.h \
+ stl_decl.h \
+ basicdefs.h \
+ messagequeue.h \
+ basictypes.h \
+ stringutils.h \
+ bytebuffer.h \
+ task.h \
+ byteorder.h \
+ taskrunner.h \
+ criticalsection.h \
+ network.h \
+ thread.h \
+ jtime.h \
+ physicalsocketserver.h \
+ proxyinfo.h \
+ host.h \
+ scoped_ptr.h \
+ sigslot.h \
+ winping.h \
+ socketadapters.h \
+ socketaddress.h \
+ host.h \
+ socketaddresspair.h
+
+AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../.. -I$(top_builddir) $(all_includes)
+noinst_LTLIBRARIES = libcricketbase.la
+DEFAULT_INCLUDES = -I$(srcdir)/../..
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h
new file mode 100644
index 00000000..0faac9ea
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncfile.h
@@ -0,0 +1,56 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef CRICKET_BASE_ASYNCFILEH_H__
+#define CRICKET_BASE_ASYNCFILEH_H__
+
+#include "talk/base/sigslot.h"
+
+namespace cricket {
+
+// Provides the ability to perform file I/O asynchronously.
+// TODO: Create a common base class with AsyncSocket.
+class AsyncFile {
+public:
+ virtual ~AsyncFile() {}
+
+ // Determines whether the file will receive read events.
+ virtual bool readable() = 0;
+ virtual void set_readable(bool value) = 0;
+
+ // Determines whether the file will receive write events.
+ virtual bool writable() = 0;
+ virtual void set_writable(bool value) = 0;
+
+ sigslot::signal1<AsyncFile*> SignalReadEvent;
+ sigslot::signal1<AsyncFile*> SignalWriteEvent;
+ sigslot::signal2<AsyncFile*,int> SignalCloseEvent;
+};
+
+} // namespace cricket
+
+#endif // CRICKET_BASE_ASYNCFILEH_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc
new file mode 100644
index 00000000..10cfa617
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.cc
@@ -0,0 +1,83 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/asyncpacketsocket.h"
+
+namespace cricket {
+
+AsyncPacketSocket::AsyncPacketSocket(AsyncSocket* socket) : socket_(socket) {
+}
+
+AsyncPacketSocket::~AsyncPacketSocket() {
+ delete socket_;
+}
+
+SocketAddress AsyncPacketSocket::GetLocalAddress() const {
+ return socket_->GetLocalAddress();
+}
+
+SocketAddress AsyncPacketSocket::GetRemoteAddress() const {
+ return socket_->GetRemoteAddress();
+}
+
+int AsyncPacketSocket::Bind(const SocketAddress& addr) {
+ return socket_->Bind(addr);
+}
+
+int AsyncPacketSocket::Connect(const SocketAddress& addr) {
+ return socket_->Connect(addr);
+}
+
+int AsyncPacketSocket::Send(const void *pv, size_t cb) {
+ return socket_->Send(pv, cb);
+}
+
+int AsyncPacketSocket::SendTo(
+ const void *pv, size_t cb, const SocketAddress& addr) {
+ return socket_->SendTo(pv, cb, addr);
+}
+
+int AsyncPacketSocket::Close() {
+ return socket_->Close();
+}
+
+int AsyncPacketSocket::SetOption(Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int AsyncPacketSocket::GetError() const {
+ return socket_->GetError();
+}
+
+void AsyncPacketSocket::SetError(int error) {
+ return socket_->SetError(error);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h
new file mode 100644
index 00000000..b5119c8d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncpacketsocket.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASYNCPACKETSOCKET_H__
+#define __ASYNCPACKETSOCKET_H__
+
+#include "talk/base/asyncsocket.h"
+
+namespace cricket {
+
+// Provides the ability to receive packets asynchronously. Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncPacketSocket : public sigslot::has_slots<> {
+public:
+ AsyncPacketSocket(AsyncSocket* socket);
+ virtual ~AsyncPacketSocket();
+
+ // Relevant socket methods:
+ virtual SocketAddress GetLocalAddress() const;
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Bind(const SocketAddress& addr);
+ virtual int Connect(const SocketAddress& addr);
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+ virtual int Close();
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError() const;
+ virtual void SetError(int error);
+
+ // Emitted each time a packet is read.
+ sigslot::signal4<const char*, size_t, const SocketAddress&, AsyncPacketSocket*> SignalReadPacket;
+
+protected:
+ AsyncSocket* socket_;
+};
+
+} // namespace cricket
+
+#endif // __ASYNCPACKETSOCKET_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h
new file mode 100644
index 00000000..d6404232
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncsocket.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASYNCSOCKET_H__
+#define __ASYNCSOCKET_H__
+
+#include "talk/base/sigslot.h"
+#include "talk/base/socket.h"
+
+namespace cricket {
+
+// Provides the ability to perform socket I/O asynchronously.
+class AsyncSocket : public Socket, public sigslot::has_slots<> {
+public:
+ virtual ~AsyncSocket() {}
+
+ sigslot::signal1<AsyncSocket*> SignalReadEvent; // ready to read
+ sigslot::signal1<AsyncSocket*> SignalWriteEvent; // ready to write
+ sigslot::signal1<AsyncSocket*> SignalConnectEvent; // connected
+ sigslot::signal2<AsyncSocket*,int> SignalCloseEvent; // closed
+ // TODO: error
+};
+
+class AsyncSocketAdapter : public AsyncSocket {
+public:
+ AsyncSocketAdapter(Socket * socket) : socket_(socket) {
+ }
+ AsyncSocketAdapter(AsyncSocket * socket) : socket_(socket) {
+ socket->SignalConnectEvent.connect(this, &AsyncSocketAdapter::OnConnectEvent);
+ socket->SignalReadEvent.connect(this, &AsyncSocketAdapter::OnReadEvent);
+ socket->SignalWriteEvent.connect(this, &AsyncSocketAdapter::OnWriteEvent);
+ socket->SignalCloseEvent.connect(this, &AsyncSocketAdapter::OnCloseEvent);
+ }
+ virtual ~AsyncSocketAdapter() { delete socket_; }
+
+ virtual SocketAddress GetLocalAddress() const { return socket_->GetLocalAddress(); }
+ virtual SocketAddress GetRemoteAddress() const { return socket_->GetRemoteAddress(); }
+
+ virtual int Bind(const SocketAddress& addr) { return socket_->Bind(addr); }
+ virtual int Connect(const SocketAddress& addr) { return socket_->Connect(addr); }
+ virtual int Send(const void *pv, size_t cb) { return socket_->Send(pv, cb); }
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) { return socket_->SendTo(pv, cb, addr); }
+ virtual int Recv(void *pv, size_t cb) { return socket_->Recv(pv, cb); }
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) { return socket_->RecvFrom(pv, cb, paddr); }
+ virtual int Listen(int backlog) { return socket_->Listen(backlog); }
+ virtual Socket *Accept(SocketAddress *paddr) { return socket_->Accept(paddr); }
+ virtual int Close() { return socket_->Close(); }
+ virtual int GetError() const { return socket_->GetError(); }
+ virtual void SetError(int error) { return socket_->SetError(error); }
+
+ virtual ConnState GetState() const { return socket_->GetState(); }
+
+ virtual int EstimateMTU(uint16* mtu) { return socket_->EstimateMTU(mtu); }
+ virtual int SetOption(Option opt, int value) { return socket_->SetOption(opt, value); }
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket) { SignalConnectEvent(this); }
+ virtual void OnReadEvent(AsyncSocket * socket) { SignalReadEvent(this); }
+ virtual void OnWriteEvent(AsyncSocket * socket) { SignalWriteEvent(this); }
+ virtual void OnCloseEvent(AsyncSocket * socket, int err) { SignalCloseEvent(this, err); }
+
+ Socket * socket_;
+};
+
+} // namespace cricket
+
+#endif // __ASYNCSOCKET_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc
new file mode 100644
index 00000000..6d4697a6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.cc
@@ -0,0 +1,197 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const size_t MAX_PACKET_SIZE = 64 * 1024;
+
+typedef uint16 PacketLength;
+const size_t PKT_LEN_SIZE = sizeof(PacketLength);
+
+const size_t BUF_SIZE = MAX_PACKET_SIZE + PKT_LEN_SIZE;
+
+AsyncTCPSocket::AsyncTCPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket), insize_(BUF_SIZE), inpos_(0), outsize_(BUF_SIZE), outpos_(0) {
+ inbuf_ = new char[insize_];
+ outbuf_ = new char[outsize_];
+
+ ASSERT(socket_ != NULL);
+ socket_->SignalConnectEvent.connect(this, &AsyncTCPSocket::OnConnectEvent);
+ socket_->SignalReadEvent.connect(this, &AsyncTCPSocket::OnReadEvent);
+ socket_->SignalWriteEvent.connect(this, &AsyncTCPSocket::OnWriteEvent);
+ socket_->SignalCloseEvent.connect(this, &AsyncTCPSocket::OnCloseEvent);
+}
+
+AsyncTCPSocket::~AsyncTCPSocket() {
+ delete [] inbuf_;
+ delete [] outbuf_;
+}
+
+int AsyncTCPSocket::Send(const void *pv, size_t cb) {
+ if (cb > MAX_PACKET_SIZE) {
+ socket_->SetError(EMSGSIZE);
+ return -1;
+ }
+
+ // If we are blocking on send, then silently drop this packet
+ if (outpos_)
+ return static_cast<int>(cb);
+
+ PacketLength pkt_len = HostToNetwork16(static_cast<PacketLength>(cb));
+ memcpy(outbuf_, &pkt_len, PKT_LEN_SIZE);
+ memcpy(outbuf_ + PKT_LEN_SIZE, pv, cb);
+ outpos_ = PKT_LEN_SIZE + cb;
+
+ int res = Flush();
+ if (res <= 0) {
+ // drop packet if we made no progress
+ outpos_ = 0;
+ return res;
+ }
+
+ // We claim to have sent the whole thing, even if we only sent partial
+ return static_cast<int>(cb);
+}
+
+int AsyncTCPSocket::SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ if (addr == GetRemoteAddress())
+ return Send(pv, cb);
+
+ ASSERT(false);
+ socket_->SetError(ENOTCONN);
+ return -1;
+}
+
+int AsyncTCPSocket::SendRaw(const void * pv, size_t cb) {
+ if (outpos_ + cb > outsize_) {
+ socket_->SetError(EMSGSIZE);
+ return -1;
+ }
+
+ memcpy(outbuf_ + outpos_, pv, cb);
+ outpos_ += cb;
+
+ return Flush();
+}
+
+void AsyncTCPSocket::ProcessInput(char * data, size_t& len) {
+ SocketAddress remote_addr(GetRemoteAddress());
+
+ while (true) {
+ if (len < PKT_LEN_SIZE)
+ return;
+
+ PacketLength pkt_len;
+ memcpy(&pkt_len, data, PKT_LEN_SIZE);
+ pkt_len = NetworkToHost16(pkt_len);
+
+ if (len < PKT_LEN_SIZE + pkt_len)
+ return;
+
+ SignalReadPacket(data + PKT_LEN_SIZE, pkt_len, remote_addr, this);
+
+ len -= PKT_LEN_SIZE + pkt_len;
+ if (len > 0) {
+ memmove(data, data + PKT_LEN_SIZE + pkt_len, len);
+ }
+ }
+}
+
+int AsyncTCPSocket::Flush() {
+ int res = socket_->Send(outbuf_, outpos_);
+ if (res <= 0) {
+ return res;
+ }
+ if (static_cast<size_t>(res) <= outpos_) {
+ outpos_ -= res;
+ } else {
+ ASSERT(false);
+ return -1;
+ }
+ if (outpos_ > 0) {
+ memmove(outbuf_, outbuf_ + res, outpos_);
+ }
+ return res;
+}
+
+void AsyncTCPSocket::OnConnectEvent(AsyncSocket* socket) {
+ SignalConnect(this);
+}
+
+void AsyncTCPSocket::OnReadEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+
+ int len = socket_->Recv(inbuf_ + inpos_, insize_ - inpos_);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ LOG(INFO) << "recvfrom: " << errno << " " << std::strerror(errno);
+ return;
+ }
+
+ inpos_ += len;
+
+ ProcessInput(inbuf_, inpos_);
+
+ if (inpos_ >= insize_) {
+ LOG(INFO) << "input buffer overflow";
+ ASSERT(false);
+ inpos_ = 0;
+ }
+}
+
+void AsyncTCPSocket::OnWriteEvent(AsyncSocket* socket) {
+ ASSERT(socket == socket_);
+
+ if (outpos_ > 0) {
+ Flush();
+ }
+}
+
+void AsyncTCPSocket::OnCloseEvent(AsyncSocket* socket, int error) {
+ SignalClose(this, error);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h
new file mode 100644
index 00000000..e93e5e7d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asynctcpsocket.h
@@ -0,0 +1,68 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASYNCTCPSOCKET_H__
+#define __ASYNCTCPSOCKET_H__
+
+#include "talk/base/asyncpacketsocket.h"
+
+namespace cricket {
+
+// Simulates UDP semantics over TCP. Send and Recv packet sizes
+// are preserved, and drops packets silently on Send, rather than
+// buffer them in user space.
+class AsyncTCPSocket : public AsyncPacketSocket {
+public:
+ AsyncTCPSocket(AsyncSocket* socket);
+ virtual ~AsyncTCPSocket();
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+
+ sigslot::signal1<AsyncTCPSocket*> SignalConnect;
+ sigslot::signal2<AsyncTCPSocket*,int> SignalClose;
+
+protected:
+ int SendRaw(const void * pv, size_t cb);
+ virtual void ProcessInput(char * data, size_t& len);
+
+private:
+ char* inbuf_, * outbuf_;
+ size_t insize_, inpos_, outsize_, outpos_;
+
+ int Flush();
+
+ // Called by the underlying socket
+ void OnConnectEvent(AsyncSocket* socket);
+ void OnReadEvent(AsyncSocket* socket);
+ void OnWriteEvent(AsyncSocket* socket);
+ void OnCloseEvent(AsyncSocket* socket, int error);
+};
+
+} // namespace cricket
+
+#endif // __ASYNCSTCPOCKET_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc
new file mode 100644
index 00000000..5b8c2466
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.cc
@@ -0,0 +1,83 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/logging.h"
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const int BUF_SIZE = 64 * 1024;
+
+AsyncUDPSocket::AsyncUDPSocket(AsyncSocket* socket) : AsyncPacketSocket(socket) {
+ size_ = BUF_SIZE;
+ buf_ = new char[size_];
+
+ assert(socket_);
+ // The socket should start out readable but not writable.
+ socket_->SignalReadEvent.connect(this, &AsyncUDPSocket::OnReadEvent);
+}
+
+AsyncUDPSocket::~AsyncUDPSocket() {
+ delete [] buf_;
+}
+
+void AsyncUDPSocket::OnReadEvent(AsyncSocket* socket) {
+ assert(socket == socket_);
+
+ SocketAddress remote_addr;
+ int len = socket_->RecvFrom(buf_, size_, &remote_addr);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ PLOG(LS_ERROR, socket_->GetError()) << "recvfrom";
+ return;
+ }
+
+ // TODO: Make sure that we got all of the packet. If we did not, then we
+ // should resize our buffer to be large enough.
+
+ SignalReadPacket(buf_, (size_t)len, remote_addr, this);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h
new file mode 100644
index 00000000..7fac7713
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/asyncudpsocket.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASYNCUDPSOCKET_H__
+#define __ASYNCUDPSOCKET_H__
+
+#include "talk/base/asyncpacketsocket.h"
+#include "talk/base/socketfactory.h"
+
+namespace cricket {
+
+// Provides the ability to receive packets asynchronously. Sends are not
+// buffered since it is acceptable to drop packets under high load.
+class AsyncUDPSocket : public AsyncPacketSocket {
+public:
+ AsyncUDPSocket(AsyncSocket* socket);
+ virtual ~AsyncUDPSocket();
+
+private:
+ char* buf_;
+ size_t size_;
+
+ // Called when the underlying socket is ready to be read from.
+ void OnReadEvent(AsyncSocket* socket);
+};
+
+// Creates a new socket for sending asynchronous UDP packets using an
+// asynchronous socket from the given factory.
+inline AsyncUDPSocket* CreateAsyncUDPSocket(SocketFactory* factory) {
+ return new AsyncUDPSocket(factory->CreateAsyncSocket(SOCK_DGRAM));
+}
+
+} // namespace cricket
+
+#endif // __ASYNCSUDPOCKET_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc
new file mode 100644
index 00000000..e0ec1b90
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.cc
@@ -0,0 +1,194 @@
+
+//*********************************************************************
+//* Base64 - a simple base64 encoder and decoder.
+//*
+//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
+//*
+//* This code may be freely used for any purpose, either personal
+//* or commercial, provided the authors copyright notice remains
+//* intact.
+//*
+//* Enhancements by Stanley Yamane:
+//* o reverse lookup table for the decode function
+//* o reserve string buffer space in advance
+//*
+//*********************************************************************
+
+#include "talk/base/base64.h"
+
+using namespace std;
+
+static const char fillchar = '=';
+static const string::size_type np = string::npos;
+
+const string Base64::Base64Table(
+ // 0000000000111111111122222222223333333333444444444455555555556666
+ // 0123456789012345678901234567890123456789012345678901234567890123
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
+
+// Decode Table gives the index of any valid base64 character in the Base64 table]
+// 65 == A, 97 == a, 48 == 0, 43 == +, 47 == /
+
+ // 0 1 2 3 4 5 6 7 8 9
+const string::size_type Base64::DecodeTable[] = {
+ np,np,np,np,np,np,np,np,np,np, // 0 - 9
+ np,np,np,np,np,np,np,np,np,np, //10 -19
+ np,np,np,np,np,np,np,np,np,np, //20 -29
+ np,np,np,np,np,np,np,np,np,np, //30 -39
+ np,np,np,62,np,np,np,63,52,53, //40 -49
+ 54,55,56,57,58,59,60,61,np,np, //50 -59
+ np,np,np,np,np, 0, 1, 2, 3, 4, //60 -69
+ 5, 6, 7, 8, 9,10,11,12,13,14, //70 -79
+ 15,16,17,18,19,20,21,22,23,24, //80 -89
+ 25,np,np,np,np,np,np,26,27,28, //90 -99
+ 29,30,31,32,33,34,35,36,37,38, //100 -109
+ 39,40,41,42,43,44,45,46,47,48, //110 -119
+ 49,50,51,np,np,np,np,np,np,np, //120 -129
+ np,np,np,np,np,np,np,np,np,np, //130 -139
+ np,np,np,np,np,np,np,np,np,np, //140 -149
+ np,np,np,np,np,np,np,np,np,np, //150 -159
+ np,np,np,np,np,np,np,np,np,np, //160 -169
+ np,np,np,np,np,np,np,np,np,np, //170 -179
+ np,np,np,np,np,np,np,np,np,np, //180 -189
+ np,np,np,np,np,np,np,np,np,np, //190 -199
+ np,np,np,np,np,np,np,np,np,np, //200 -209
+ np,np,np,np,np,np,np,np,np,np, //210 -219
+ np,np,np,np,np,np,np,np,np,np, //220 -229
+ np,np,np,np,np,np,np,np,np,np, //230 -239
+ np,np,np,np,np,np,np,np,np,np, //240 -249
+ np,np,np,np,np,np //250 -256
+};
+
+string Base64::encodeFromArray(const char * data, size_t len) {
+ size_t i;
+ char c;
+ string ret;
+
+ ret.reserve(len * 2);
+
+ for (i = 0; i < len; ++i)
+ {
+ c = (data[i] >> 2) & 0x3f;
+ ret.append(1, Base64Table[c]);
+ c = (data[i] << 4) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 4) & 0x0f;
+
+ ret.append(1, Base64Table[c]);
+ if (i < len)
+ {
+ c = (data[i] << 2) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 6) & 0x03;
+
+ ret.append(1, Base64Table[c]);
+ }
+ else
+ {
+ ++i;
+ ret.append(1, fillchar);
+ }
+
+ if (i < len)
+ {
+ c = data[i] & 0x3f;
+ ret.append(1, Base64Table[c]);
+ }
+ else
+ {
+ ret.append(1, fillchar);
+ }
+ }
+
+ return(ret);
+}
+
+
+string Base64::encode(const string& data)
+{
+ string::size_type i;
+ char c;
+ string::size_type len = data.length();
+ string ret;
+
+ ret.reserve(len * 2);
+
+ for (i = 0; i < len; ++i)
+ {
+ c = (data[i] >> 2) & 0x3f;
+ ret.append(1, Base64Table[c]);
+ c = (data[i] << 4) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 4) & 0x0f;
+
+ ret.append(1, Base64Table[c]);
+ if (i < len)
+ {
+ c = (data[i] << 2) & 0x3f;
+ if (++i < len)
+ c |= (data[i] >> 6) & 0x03;
+
+ ret.append(1, Base64Table[c]);
+ }
+ else
+ {
+ ++i;
+ ret.append(1, fillchar);
+ }
+
+ if (i < len)
+ {
+ c = data[i] & 0x3f;
+ ret.append(1, Base64Table[c]);
+ }
+ else
+ {
+ ret.append(1, fillchar);
+ }
+ }
+
+ return(ret);
+}
+
+string Base64::decode(const string& data)
+{
+ string::size_type i;
+ char c;
+ char c1;
+ string::size_type len = data.length();
+ string ret;
+
+ ret.reserve(len);
+
+ for (i = 0; i < len; ++i)
+ {
+ c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ ++i;
+ c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ c = (c << 2) | ((c1 >> 4) & 0x3);
+ ret.append(1, c);
+ if (++i < len)
+ {
+ c = data[i];
+ if (fillchar == c)
+ break;
+
+ c = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
+ ret.append(1, c1);
+ }
+
+ if (++i < len)
+ {
+ c1 = data[i];
+ if (fillchar == c1)
+ break;
+
+ c1 = static_cast<char>(DecodeTable[static_cast<unsigned char>(data[i])]);
+ c = ((c << 6) & 0xc0) | c1;
+ ret.append(1, c);
+ }
+ }
+
+ return(ret);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h
new file mode 100644
index 00000000..4622b329
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/base64.h
@@ -0,0 +1,29 @@
+
+//*********************************************************************
+//* C_Base64 - a simple base64 encoder and decoder.
+//*
+//* Copyright (c) 1999, Bob Withers - bwit@pobox.com
+//*
+//* This code may be freely used for any purpose, either personal
+//* or commercial, provided the authors copyright notice remains
+//* intact.
+//*********************************************************************
+
+#ifndef Base64_H
+#define Base64_H
+
+#include <string>
+using std::string; // comment if your compiler doesn't use namespaces
+
+class Base64
+{
+public:
+ static string encode(const string & data);
+ static string decode(const string & data);
+ static string encodeFromArray(const char * data, size_t len);
+private:
+ static const string Base64Table;
+ static const string::size_type DecodeTable[];
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h
new file mode 100644
index 00000000..171bc9f9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/basicdefs.h
@@ -0,0 +1,53 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BASICDEFS_H__
+#define __BASICDEFS_H__
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+
+// Used in GUI when referring to the product name (& Version Resource Product Name)
+#define PRODUCT_NAME "Google Talk"
+
+// Used in GUI when referring to the publisher of the product
+#define COMPANY_NAME "Google"
+
+// Used in filenames, directories, registry key names, etc to refer to the product
+#define DIRECTORY_NAME "Google Talk"
+
+// Used in URLs, registry values, etc, where we prefer not to use a space
+#define PRODUCT_SIGNATURE "googletalk"
+
+// Used whenever we do HTTP
+#define USERAGENT_STRING "Google Talk"
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+#endif // __BASICDEFS_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h
new file mode 100644
index 00000000..ea393c09
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/basictypes.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BASICTYPES_H__
+#define __BASICTYPES_H__
+
+#ifdef COMPILER_MSVC
+typedef __int64 int64;
+#else
+typedef long long int64;
+#endif /* COMPILER_MSVC */
+typedef long int32;
+typedef short int16;
+typedef char int8;
+
+#ifdef COMPILER_MSVC
+typedef unsigned __int64 uint64;
+typedef __int64 int64;
+#else
+typedef unsigned long long uint64;
+typedef long long int64;
+#endif /* COMPILER_MSVC */
+typedef unsigned long uint32;
+typedef unsigned short uint16;
+typedef unsigned char uint8;
+
+#ifdef WIN32
+typedef int socklen_t;
+#endif
+
+namespace cricket {
+ template<class T> inline T _min(T a, T b) { return (a > b) ? b : a; }
+ template<class T> inline T _max(T a, T b) { return (a < b) ? b : a; }
+}
+
+// A macro to disallow the evil copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_EVIL_CONSTRUCTORS(TypeName)
+
+#ifndef UNUSED
+#define UNUSED(x) Unused(static_cast<const void *>(&x))
+#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y))
+#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z))
+#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a))
+#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b))
+inline void Unused(const void *) { }
+#endif // UNUSED
+
+#endif // __BASICTYPES_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc
new file mode 100644
index 00000000..067f50ed
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.cc
@@ -0,0 +1,165 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/basictypes.h"
+#include "talk/base/bytebuffer.h"
+#include "talk/base/byteorder.h"
+#include <algorithm>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcpy;
+}
+#endif
+
+namespace cricket {
+
+const int DEFAULT_SIZE = 4096;
+
+ByteBuffer::ByteBuffer() {
+ start_ = 0;
+ end_ = 0;
+ size_ = DEFAULT_SIZE;
+ bytes_ = new char[size_];
+}
+
+ByteBuffer::ByteBuffer(const char* bytes, size_t len) {
+ start_ = 0;
+ end_ = len;
+ size_ = len;
+ bytes_ = new char[size_];
+ memcpy(bytes_, bytes, end_);
+}
+
+ByteBuffer::ByteBuffer(const char* bytes) {
+ start_ = 0;
+ end_ = strlen(bytes);
+ size_ = end_;
+ bytes_ = new char[size_];
+ memcpy(bytes_, bytes, end_);
+}
+
+ByteBuffer::~ByteBuffer() {
+ delete bytes_;
+}
+
+bool ByteBuffer::ReadUInt8(uint8& val) {
+ return ReadBytes(reinterpret_cast<char*>(&val), 1);
+}
+
+bool ByteBuffer::ReadUInt16(uint16& val) {
+ uint16 v;
+ if (!ReadBytes(reinterpret_cast<char*>(&v), 2)) {
+ return false;
+ } else {
+ val = NetworkToHost16(v);
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadUInt32(uint32& val) {
+ uint32 v;
+ if (!ReadBytes(reinterpret_cast<char*>(&v), 4)) {
+ return false;
+ } else {
+ val = NetworkToHost32(v);
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadString(std::string& val, size_t len) {
+ if (len > Length()) {
+ return false;
+ } else {
+ val.append(bytes_ + start_, len);
+ start_ += len;
+ return true;
+ }
+}
+
+bool ByteBuffer::ReadBytes(char* val, size_t len) {
+ if (len > Length()) {
+ return false;
+ } else {
+ memcpy(val, bytes_ + start_, len);
+ start_ += len;
+ return true;
+ }
+}
+
+void ByteBuffer::WriteUInt8(uint8 val) {
+ WriteBytes(reinterpret_cast<const char*>(&val), 1);
+}
+
+void ByteBuffer::WriteUInt16(uint16 val) {
+ uint16 v = HostToNetwork16(val);
+ WriteBytes(reinterpret_cast<const char*>(&v), 2);
+}
+
+void ByteBuffer::WriteUInt32(uint32 val) {
+ uint32 v = HostToNetwork32(val);
+ WriteBytes(reinterpret_cast<const char*>(&v), 4);
+}
+
+void ByteBuffer::WriteString(const std::string& val) {
+ WriteBytes(val.c_str(), val.size());
+}
+
+void ByteBuffer::WriteBytes(const char* val, size_t len) {
+ if (Length() + len > Capacity())
+ Resize(Length() + len);
+
+ memcpy(bytes_ + end_, val, len);
+ end_ += len;
+}
+
+void ByteBuffer::Resize(size_t size) {
+ if (size > size_)
+ size = _max(size, 3 * size_ / 2);
+
+ size_t len = _min(end_ - start_, size);
+ char* new_bytes = new char[size];
+ memcpy(new_bytes, bytes_ + start_, len);
+ delete [] bytes_;
+
+ start_ = 0;
+ end_ = len;
+ size_ = size;
+ bytes_ = new_bytes;
+}
+
+void ByteBuffer::Shift(size_t size) {
+ if (size > Length())
+ return;
+
+ end_ = Length() - size;
+ memmove(bytes_, bytes_ + start_ + size, end_);
+ start_ = 0;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h
new file mode 100644
index 00000000..b0a52344
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/bytebuffer.h
@@ -0,0 +1,71 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BYTEBUFFER_H__
+#define __BYTEBUFFER_H__
+
+#include "talk/base/basictypes.h"
+#include <string>
+
+namespace cricket {
+
+class ByteBuffer {
+public:
+ ByteBuffer();
+ ByteBuffer(const char* bytes, size_t len);
+ ByteBuffer(const char* bytes); // uses strlen
+ ~ByteBuffer();
+
+ const char* Data() const { return bytes_ + start_; }
+ size_t Length() { return end_ - start_; }
+ size_t Capacity() { return size_ - start_; }
+
+ bool ReadUInt8(uint8& val);
+ bool ReadUInt16(uint16& val);
+ bool ReadUInt32(uint32& val);
+ bool ReadString(std::string& val, size_t len); // append to val
+ bool ReadBytes(char* val, size_t len);
+
+ void WriteUInt8(uint8 val);
+ void WriteUInt16(uint16 val);
+ void WriteUInt32(uint32 val);
+ void WriteString(const std::string& val);
+ void WriteBytes(const char* val, size_t len);
+
+ void Resize(size_t size);
+ void Shift(size_t size);
+
+private:
+ char* bytes_;
+ size_t size_;
+ size_t start_;
+ size_t end_;
+};
+
+} // namespace cricket
+
+#endif // __BYTEBUFFER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h
new file mode 100644
index 00000000..4b70f47e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/byteorder.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BYTEORDER_H__
+#define __BYTEORDER_H__
+
+#include "talk/base/basictypes.h"
+
+#ifdef POSIX
+extern "C" {
+#include <arpa/inet.h>
+}
+#endif
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
+namespace cricket {
+
+inline uint16 HostToNetwork16(uint16 n) {
+ return htons(n);
+}
+
+inline uint32 HostToNetwork32(uint32 n) {
+ return htonl(n);
+}
+
+inline uint16 NetworkToHost16(uint16 n) {
+ return ntohs(n);
+}
+
+inline uint32 NetworkToHost32(uint32 n) {
+ return ntohl(n);
+}
+
+} // namespace cricket
+
+#endif // __BYTEORDER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h
new file mode 100644
index 00000000..b21be2f1
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/common.h
@@ -0,0 +1,231 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _common_h_
+#define _common_h_
+
+#if defined(_MSC_VER)
+// warning C4355: 'this' : used in base member initializer list
+#pragma warning(disable:4355)
+#endif
+
+#if defined(ENABLE_DEBUG_MALLOC) && !defined(ENABLE_DEBUG)
+#define ENABLE_DEBUG 1
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// General Utilities
+//////////////////////////////////////////////////////////////////////
+
+#ifndef UNUSED
+#define UNUSED(x) Unused(static_cast<const void *>(&x))
+#define UNUSED2(x,y) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y))
+#define UNUSED3(x,y,z) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z))
+#define UNUSED4(x,y,z,a) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a))
+#define UNUSED5(x,y,z,a,b) Unused(static_cast<const void *>(&x)); Unused(static_cast<const void *>(&y)); Unused(static_cast<const void *>(&z)); Unused(static_cast<const void *>(&a)); Unused(static_cast<const void *>(&b))
+inline void Unused(const void *) { }
+#endif // UNUSED
+
+#define ARRAY_SIZE(x) (static_cast<int>((sizeof(x)/sizeof(x[0]))))
+
+/////////////////////////////////////////////////////////////////////////////
+// std:min/std:max on msvc
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0
+
+#undef min
+#undef max
+
+namespace std {
+
+
+ /**
+ * @brief This does what you think it does.
+ * @param a A thing of arbitrary type.
+ * @param b Another thing of arbitrary type.
+ * @return The lesser of the parameters.
+ *
+ * This is the simple classic generic implementation. It will work on
+ * temporary expressions, since they are only evaluated once, unlike a
+ * preprocessor macro.
+ */
+ template<typename _Tp>
+ inline const _Tp&
+ min(const _Tp& __a, const _Tp& __b)
+ {
+ //return __b < __a ? __b : __a;
+ if (__b < __a) return __b; return __a;
+ }
+
+ /**
+ * @brief This does what you think it does.
+ * @param a A thing of arbitrary type.
+ * @param b Another thing of arbitrary type.
+ * @return The greater of the parameters.
+ *
+ * This is the simple classic generic implementation. It will work on
+ * temporary expressions, since they are only evaluated once, unlike a
+ * preprocessor macro.
+ */
+ template<typename _Tp>
+ inline const _Tp&
+ max(const _Tp& __a, const _Tp& __b)
+ {
+ //return __a < __b ? __b : __a;
+ if (__a < __b) return __b; return __a;
+ }
+
+ /**
+ * @brief This does what you think it does.
+ * @param a A thing of arbitrary type.
+ * @param b Another thing of arbitrary type.
+ * @param comp A @link s20_3_3_comparisons comparison functor@endlink.
+ * @return The lesser of the parameters.
+ *
+ * This will work on temporary expressions, since they are only evaluated
+ * once, unlike a preprocessor macro.
+ */
+ template<typename _Tp, typename _Compare>
+ inline const _Tp&
+ min(const _Tp& __a, const _Tp& __b, _Compare __comp)
+ {
+ //return __comp(__b, __a) ? __b : __a;
+ if (__comp(__b, __a)) return __b; return __a;
+ }
+
+ /**
+ * @brief This does what you think it does.
+ * @param a A thing of arbitrary type.
+ * @param b Another thing of arbitrary type.
+ * @param comp A @link s20_3_3_comparisons comparison functor@endlink.
+ * @return The greater of the parameters.
+ *
+ * This will work on temporary expressions, since they are only evaluated
+ * once, unlike a preprocessor macro.
+ */
+ template<typename _Tp, typename _Compare>
+ inline const _Tp&
+ max(const _Tp& __a, const _Tp& __b, _Compare __comp)
+ {
+ //return __comp(__a, __b) ? __b : __a;
+ if (__comp(__a, __b)) return __b; return __a;
+ }
+
+}
+
+#endif // _MSC_VER <= 1200
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Assertions
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef ENABLE_DEBUG
+
+namespace buzz {
+
+// Break causes the debugger to stop executing, or the program to abort
+void Break();
+
+// LogAssert writes information about an assertion to the log
+void LogAssert(const char * function, const char * file, int line, const char * expression);
+
+inline void Assert(bool result, const char * function, const char * file, int line, const char * expression) {
+ if (!result) {
+ LogAssert(function, file, line, expression);
+ Break();
+ }
+}
+
+}; // namespace buzz
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#define __FUNCTION__ ""
+#endif
+
+#define ASSERT(x) buzz::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+#define VERIFY(x) buzz::Assert((x),__FUNCTION__,__FILE__,__LINE__,#x)
+
+#else // !ENABLE_DEBUG
+
+#define ASSERT(x) (void)0
+#define VERIFY(x) (void)(x)
+
+#endif // !ENABLE_DEBUG
+
+#define COMPILE_TIME_ASSERT(expr) char CTA_UNIQUE_NAME[expr]
+#define CTA_UNIQUE_NAME MAKE_NAME(__LINE__)
+#define CTA_MAKE_NAME(line) MAKE_NAME2(line)
+#define CTA_MAKE_NAME2(line) constraint_ ## line
+
+//////////////////////////////////////////////////////////////////////
+// Memory leak tracking
+//////////////////////////////////////////////////////////////////////
+
+#include <sys/types.h>
+
+#ifdef ENABLE_DEBUG_MALLOC
+
+namespace buzz {
+
+void * DebugAllocate(size_t size, const char * fname = 0, int line = 0);
+void DebugDeallocate(void * ptr, const char * fname = 0, int line = 0);
+bool LeakCheck();
+bool LeakCheckU();
+void LeakMarkBaseline();
+void LeakClearBaseline();
+
+}; // namespace buzz
+
+inline void * operator new(size_t size, const char * fname, int line) { return buzz::DebugAllocate(size, fname, line); }
+inline void operator delete(void * ptr, const char * fname, int line) { buzz::DebugDeallocate(ptr, fname, line); }
+
+#if !(defined(TRACK_ARRAY_ALLOC_PROBLEM) && \
+ defined(_MSC_VER) && _MSC_VER <= 1200) // 1200 == VC++ 6.0
+
+inline void * operator new[](size_t size, const char * fname, int line) { return buzz::DebugAllocate(size, fname, line); }
+inline void operator delete[](void * ptr, const char * fname, int line) { buzz::DebugDeallocate(ptr, fname, line); }
+
+#endif // TRACK_ARRAY_ALLOC_PROBLEM
+
+
+// If you put "#define new TRACK_NEW" in your .cc file after all includes, it should track the calling function name
+
+#define TRACK_NEW new(__FILE__,__LINE__)
+#define TRACK_DEL delete(__FILE__,__LINE__)
+
+#else // !ENABLE_DEBUG_MALLOC
+
+#define TRACK_NEW new
+#define TRACK_DEL delete
+
+#endif // !ENABLE_DEBUG_MALLOC
+
+//////////////////////////////////////////////////////////////////////
+
+#endif // _common_h_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h
new file mode 100644
index 00000000..b75ad5c7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/criticalsection.h
@@ -0,0 +1,120 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _criticalsection_h_
+#define _criticalsection_h_
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+#ifdef POSIX
+#include <pthread.h>
+#endif
+
+#ifdef _DEBUG
+#define CS_TRACK_OWNER 1
+#endif // _DEBUG
+
+#if CS_TRACK_OWNER
+#define TRACK_OWNER(x) x
+#else // !CS_TRACK_OWNER
+#define TRACK_OWNER(x)
+#endif // !CS_TRACK_OWNER
+
+namespace cricket {
+
+#ifdef WIN32
+class CriticalSection {
+public:
+ CriticalSection() {
+ InitializeCriticalSection(&crit_);
+ // Windows docs say 0 is not a valid thread id
+ TRACK_OWNER(thread_ = 0);
+ }
+ ~CriticalSection() {
+ DeleteCriticalSection(&crit_);
+ }
+ void Enter() {
+ EnterCriticalSection(&crit_);
+ TRACK_OWNER(thread_ = GetCurrentThreadId());
+ }
+ void Leave() {
+ TRACK_OWNER(thread_ = 0);
+ LeaveCriticalSection(&crit_);
+ }
+
+#if CS_TRACK_OWNER
+ bool CurrentThreadIsOwner() const { return thread_ == GetCurrentThreadId(); }
+#endif // CS_TRACK_OWNER
+
+private:
+ CRITICAL_SECTION crit_;
+ TRACK_OWNER(DWORD thread_); // The section's owning thread id
+};
+#endif // WIN32
+
+#ifdef POSIX
+class CriticalSection {
+public:
+ CriticalSection() {
+ pthread_mutexattr_t mutex_attribute;
+ pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mutex_, &mutex_attribute);
+ }
+ ~CriticalSection() {
+ pthread_mutex_destroy(&mutex_);
+ }
+ void Enter() {
+ pthread_mutex_lock(&mutex_);
+ }
+ void Leave() {
+ pthread_mutex_unlock(&mutex_);
+ }
+private:
+ pthread_mutex_t mutex_;
+};
+#endif // POSIX
+
+// CritScope, for serializing exection through a scope
+
+class CritScope {
+public:
+ CritScope(CriticalSection *pcrit) {
+ pcrit_ = pcrit;
+ pcrit_->Enter();
+ }
+ ~CritScope() {
+ pcrit_->Leave();
+ }
+private:
+ CriticalSection *pcrit_;
+};
+
+} // namespace cricket
+
+#endif // _criticalsection_h_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc
new file mode 100644
index 00000000..7b7490d9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.cc
@@ -0,0 +1,99 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/base/network.h"
+#include "talk/base/socket.h"
+#include <string>
+#include <iostream>
+#include <cassert>
+#include <errno.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+ using ::exit;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <sys/utsname.h>
+}
+#endif // POSIX
+
+namespace {
+
+void FatalError(const std::string& name, int err) {
+ PLOG(LERROR, err) << name;
+ std::exit(1);
+}
+
+}
+
+namespace cricket {
+
+#ifdef POSIX
+std::string GetHostName() {
+ struct utsname nm;
+ if (uname(&nm) < 0)
+ FatalError("uname", errno);
+ return std::string(nm.nodename);
+}
+#endif
+
+#ifdef WIN32
+std::string GetHostName() {
+ // TODO: fix this
+ return "cricket";
+}
+#endif
+
+// Records information about the local host.
+Host* gLocalHost = 0;
+
+const Host& LocalHost() {
+ if (!gLocalHost) {
+ std::vector<Network*>* networks = new std::vector<Network*>;
+ NetworkManager::CreateNetworks(*networks);
+#ifdef WIN32
+ // This is sort of problematic... one part of the code (the unittests) wants
+ // 127.0.0.1 to be present and another part (port allocators) don't. Right
+ // now, they use different APIs, so we can have different behavior. But
+ // there is something wrong with this.
+ networks->push_back(new Network("localhost",
+ SocketAddress::StringToIP("127.0.0.1")));
+#endif
+ gLocalHost = new Host(GetHostName(), networks);
+ assert(gLocalHost->networks().size() > 0);
+ }
+
+ return *gLocalHost;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h
new file mode 100644
index 00000000..16f31a78
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/host.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HOST_H__
+#define __HOST_H__
+
+#include "talk/base/network.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+// Provides information about a host in the network.
+class Host {
+public:
+ Host(const std::string& name, std::vector<Network*>* networks)
+ : name_(name), networks_(networks) { }
+
+ const std::string& name() const { return name_; }
+ const std::vector<Network*>& networks() const { return *networks_; }
+
+private:
+ std::string name_;
+ std::vector<Network*>* networks_;
+};
+
+// Returns a reference to the description of the local host.
+const Host& LocalHost();
+
+// Returns the name of the local host.
+std::string GetHostName();
+
+} // namespace cricket
+
+#endif // __HOST_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc
new file mode 100644
index 00000000..5befe9fd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.cc
@@ -0,0 +1,77 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/jtime.h"
+#include <iostream>
+#include <cstdlib>
+#include <cstring>
+
+namespace cricket {
+
+#ifdef POSIX
+#include <sys/time.h>
+uint32 Time() {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+uint32 Time() {
+ return GetTickCount();
+}
+#endif
+
+bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier) {
+ if (earlier <= later) {
+ return ((earlier <= middle) && (middle <= later));
+ } else {
+ return !((later < middle) && (middle < earlier));
+ }
+}
+
+int32 TimeDiff(uint32 later, uint32 earlier) {
+ uint32 LAST = 0xFFFFFFFF;
+ uint32 HALF = 0x80000000;
+ if (TimeIsBetween(earlier + HALF, later, earlier)) {
+ if (earlier <= later) {
+ return static_cast<long>(later - earlier);
+ } else {
+ return static_cast<long>(later + (LAST - earlier) + 1);
+ }
+ } else {
+ if (later <= earlier) {
+ return -static_cast<long>(earlier - later);
+ } else {
+ return -static_cast<long>(earlier + (LAST - later) + 1);
+ }
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h
new file mode 100644
index 00000000..d7dff0fb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/jtime.h
@@ -0,0 +1,47 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __JTIME_H__
+#define __JTIME_H__
+
+#include "talk/base/basictypes.h"
+
+namespace cricket {
+
+// Returns the current time in milliseconds.
+uint32 Time();
+
+// TODO: Delete this old version.
+#define GetMillisecondCount Time
+
+// Comparisons between time values, which can wrap around.
+bool TimeIsBetween(uint32 later, uint32 middle, uint32 earlier);
+int32 TimeDiff(uint32 later, uint32 earlier);
+
+} // namespace cricket
+
+#endif // __TIME_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h
new file mode 100644
index 00000000..94b62ad3
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/linked_ptr.h
@@ -0,0 +1,138 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * linked_ptr - simple reference linked pointer
+ * (like reference counting, just using a linked list of the references
+ * instead of their count.)
+ *
+ * The implementation stores three pointers for every linked_ptr, but
+ * does not allocate anything on the free store.
+ */
+
+#ifndef _LINKED_PTR_H_
+#define _LINKED_PTR_H_
+
+/* For ANSI-challenged compilers, you may want to #define
+ * NO_MEMBER_TEMPLATES, explicit or mutable */
+#define NO_MEMBER_TEMPLATES
+
+template <class X> class linked_ptr
+{
+public:
+
+#ifndef NO_MEMBER_TEMPLATES
+# define TEMPLATE_FUNCTION template <class Y>
+ TEMPLATE_FUNCTION friend class linked_ptr<Y>;
+#else
+# define TEMPLATE_FUNCTION
+ typedef X Y;
+#endif
+
+ typedef X element_type;
+
+ explicit linked_ptr(X* p = 0) throw()
+ : itsPtr(p) {itsPrev = itsNext = this;}
+ ~linked_ptr()
+ {release();}
+ linked_ptr(const linked_ptr& r) throw()
+ {acquire(r);}
+ linked_ptr& operator=(const linked_ptr& r)
+ {
+ if (this != &r) {
+ release();
+ acquire(r);
+ }
+ return *this;
+ }
+
+#ifndef NO_MEMBER_TEMPLATES
+ template <class Y> friend class linked_ptr<Y>;
+ template <class Y> linked_ptr(const linked_ptr<Y>& r) throw()
+ {acquire(r);}
+ template <class Y> linked_ptr& operator=(const linked_ptr<Y>& r)
+ {
+ if (this != &r) {
+ release();
+ acquire(r);
+ }
+ return *this;
+ }
+#endif // NO_MEMBER_TEMPLATES
+
+ X& operator*() const throw() {return *itsPtr;}
+ X* operator->() const throw() {return itsPtr;}
+ X* get() const throw() {return itsPtr;}
+ bool unique() const throw() {return itsPrev ? itsPrev==this : true;}
+
+private:
+ X* itsPtr;
+ mutable const linked_ptr* itsPrev;
+ mutable const linked_ptr* itsNext;
+
+ void acquire(const linked_ptr& r) throw()
+ { // insert this to the list
+ itsPtr = r.itsPtr;
+ itsNext = r.itsNext;
+ itsNext->itsPrev = this;
+ itsPrev = &r;
+#ifndef mutable
+ r.itsNext = this;
+#else // for ANSI-challenged compilers
+ (const_cast<linked_ptr<X>*>(&r))->itsNext = this;
+#endif
+ }
+
+#ifndef NO_MEMBER_TEMPLATES
+ template <class Y> void acquire(const linked_ptr<Y>& r) throw()
+ { // insert this to the list
+ itsPtr = r.itsPtr;
+ itsNext = r.itsNext;
+ itsNext->itsPrev = this;
+ itsPrev = &r;
+#ifndef mutable
+ r.itsNext = this;
+#else // for ANSI-challenged compilers
+ (const_cast<linked_ptr<X>*>(&r))->itsNext = this;
+#endif
+ }
+#endif // NO_MEMBER_TEMPLATES
+
+ void release()
+ { // erase this from the list, delete if unique
+ if (unique()) delete itsPtr;
+ else {
+ itsPrev->itsNext = itsNext;
+ itsNext->itsPrev = itsPrev;
+ itsPrev = itsNext = 0;
+ }
+ itsPtr = 0;
+ }
+};
+
+#endif // LINKED_PTR_H
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h
new file mode 100644
index 00000000..500386ed
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/logging.h
@@ -0,0 +1,222 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// LOG(...) an ostream target that can be used to send formatted
+// output to a variety of logging targets, such as debugger console, stderr,
+// file, or any StreamInterface.
+// The severity level passed as the first argument to the the LOGging
+// functions is used as a filter, to limit the verbosity of the logging.
+// Static members of LogMessage documented below are used to control the
+// verbosity and target of the output.
+// There are several variations on the LOG macro which facilitate logging
+// of common error conditions, detailed below.
+
+#ifndef TALK_BASE_LOGGING_H__
+#define TALK_BASE_LOGGING_H__
+
+#include <sstream>
+#include "talk/base/basictypes.h"
+class StreamInterface;
+
+///////////////////////////////////////////////////////////////////////////////
+// ConstantLabel can be used to easily generate string names from constant
+// values. This can be useful for logging descriptive names of error messages.
+// Usage:
+// const ConstantLabel LIBRARY_ERRORS[] = {
+// KLABEL(SOME_ERROR),
+// KLABEL(SOME_OTHER_ERROR),
+// ...
+// LASTLABEL
+// }
+//
+// int err = LibraryFunc();
+// LOG(LS_ERROR) << "LibraryFunc returned: "
+// << ErrorName(err, LIBRARY_ERRORS);
+
+struct ConstantLabel { int value; const char * label; };
+#define KLABEL(x) { x, #x }
+#define TLABEL(x,y) { x, y }
+#define LASTLABEL { 0, 0 }
+
+const char * FindLabel(int value, const ConstantLabel entries[]);
+std::string ErrorName(int err, const ConstantLabel * err_table);
+
+//////////////////////////////////////////////////////////////////////
+
+// Note that the non-standard LoggingSeverity aliases exist because they are
+// still in broad use. The meanings of the levels are:
+// LS_SENSITIVE: Information which should only be logged with the consent
+// of the user, due to privacy concerns.
+// LS_VERBOSE: This level is for data which we do not want to appear in the
+// normal debug log, but should appear in diagnostic logs.
+// LS_INFO: Chatty level used in debugging for all sorts of things, the default
+// in debug builds.
+// LS_WARNING: Something that may warrant investigation.
+// LS_ERROR: Something that should not have occurred.
+enum LoggingSeverity { LS_SENSITIVE, LS_VERBOSE, LS_INFO, LS_WARNING, LS_ERROR,
+ INFO = LS_INFO,
+ WARNING = LS_WARNING,
+ LERROR = LS_ERROR };
+
+// LogErrorContext assists in interpreting the meaning of an error value.
+// ERRCTX_ERRNO: the value was read from global 'errno'
+// ERRCTX_HRESULT: the value is a Windows HRESULT
+enum LogErrorContext { ERRCTX_NONE, ERRCTX_ERRNO, ERRCTX_HRESULT };
+
+// If LOGGING is not explicitly defined, default to enabled in debug mode
+#if !defined(LOGGING)
+#if defined(_DEBUG) && !defined(NDEBUG)
+#define LOGGING 1
+#else
+#define LOGGING 0
+#endif
+#endif // !defined(LOGGING)
+
+#if LOGGING
+
+#define LOG(sev) \
+ if (LogMessage::Loggable(sev)) \
+ LogMessage(__FILE__, __LINE__, sev).stream()
+
+// PLOG and LOG_ERR attempt to provide a string description of an errno derived
+// error. LOG_ERR reads errno directly, so care must be taken to call it before
+// errno is reset.
+#define PLOG(sev, err) \
+ if (LogMessage::Loggable(sev)) \
+ LogMessage(__FILE__, __LINE__, sev, ERRCTX_ERRNO, err).stream()
+#define LOG_ERR(sev) \
+ if (LogMessage::Loggable(sev)) \
+ LogMessage(__FILE__, __LINE__, sev, ERRCTX_ERRNO, errno).stream()
+
+// LOG_GLE(M) attempt to provide a string description of the HRESULT returned
+// by GetLastError. The second variant allows searching of a dll's string
+// table for the error description.
+#ifdef WIN32
+#define LOG_GLE(sev) \
+ if (LogMessage::Loggable(sev)) \
+ LogMessage(__FILE__, __LINE__, sev, ERRCTX_HRESULT, GetLastError()).stream()
+#define LOG_GLEM(sev, mod) \
+ if (LogMessage::Loggable(sev)) \
+ LogMessage(__FILE__, __LINE__, sev, ERRCTX_HRESULT, GetLastError(), mod) \
+ .stream()
+#endif // WIN32
+
+// TODO: Add an "assert" wrapper that logs in the same manner.
+
+#else // !LOGGING
+
+// Hopefully, the compiler will optimize away some of this code.
+// Note: syntax of "1 ? (void)0 : LogMessage" was causing errors in g++,
+// converted to "while (false)"
+#define LOG(sev) \
+ while (false) LogMessage(NULL, 0, sev).stream()
+#define PLOG(sev, err) \
+ while (false) LogMessage(NULL, 0, sev, ERRCTX_ERRNO, 0).stream()
+#define LOG_ERR(sev) \
+ while (false) LogMessage(NULL, 0, sev, ERRCTX_ERRNO, 0).stream()
+#ifdef WIN32
+#define LOG_GLE(sev) \
+ while (false) LogMessage(NULL, 0, sev, ERRCTX_HRESULT, 0).stream()
+#define LOG_GLEM(sev, mod) \
+ while (false) LogMessage(NULL, 0, sev, ERRCTX_HRESULT, 0).stream()
+#endif // WIN32
+
+#endif // !LOGGING
+
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, LoggingSeverity sev,
+ LogErrorContext err_ctx = ERRCTX_NONE, int err = 0,
+ const char* module = NULL);
+ ~LogMessage();
+
+ static inline bool Loggable(LoggingSeverity sev) { return (sev >= min_sev_); }
+ std::ostream& stream() { return print_stream_; }
+
+ enum { NO_LOGGING = LS_ERROR + 1 };
+
+ // These are attributes which apply to all logging channels
+ // LogContext: Display the file and line number of the message
+ static void LogContext(int min_sev);
+ // LogThreads: Display the thread identifier of the current thread
+ static void LogThreads(bool on = true);
+ // LogTimestamps: Display the elapsed time of the program
+ static void LogTimestamps(bool on = true);
+
+ // Timestamps begin with program execution, but can be reset with this
+ // function for measuring the duration of an activity, or to synchronize
+ // timestamps between multiple instances.
+ static void ResetTimestamps();
+
+ // These are the available logging channels
+ // Debug: Debug console on Windows, otherwise stderr
+ static void LogToDebug(int min_sev);
+ static int GetLogToDebug() { return dbg_sev_; }
+ // Stream: Any non-blocking stream interface. LogMessage takes ownership of
+ // the stream.
+ static void LogToStream(StreamInterface* stream, int min_sev);
+ static int GetLogToStream() { return stream_sev_; }
+
+ // Testing against MinLogSeverity allows code to avoid potentially expensive
+ // logging operations by pre-checking the logging level.
+ static int GetMinLogSeverity() { return min_sev_; }
+
+ private:
+ // These assist in formatting some parts of the debug output.
+ static const char* Describe(LoggingSeverity sev);
+ static const char* DescribeFile(const char* file);
+
+ // The ostream that buffers the formatted message before output
+ std::ostringstream print_stream_;
+
+ // The severity level of this message
+ LoggingSeverity severity_;
+
+ // String data generated in the constructor, that should be appended to
+ // the message before output.
+ std::string extra_;
+
+ // dbg_sev_ and stream_sev_ are the thresholds for those output targets
+ // min_sev_ is the minimum (most verbose) of those levels, and is used
+ // as a short-circuit in the logging macros to identify messages that won't
+ // be logged.
+ // ctx_sev_ is the minimum level at which file context is displayed
+ static int min_sev_, dbg_sev_, stream_sev_, ctx_sev_;
+
+ // The output stream, if any
+ static StreamInterface * stream_;
+
+ // Flags for formatting options
+ static bool thread_, timestamp_;
+
+ // The timestamp at which logging started.
+ static uint32 start_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+#endif // TALK_BASE_LOGGING_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h
new file mode 100644
index 00000000..c2e22cc5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5.h
@@ -0,0 +1,45 @@
+/*
+ * This is the header file for the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ *
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef long unsigned int uint32;
+typedef struct MD5Context MD5_CTX;
+
+#define md5byte unsigned char
+
+struct MD5Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ uint32 in[16];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /* !MD5_H */
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c
new file mode 100644
index 00000000..eb2c034d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/md5c.c
@@ -0,0 +1,256 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h> /* for memcpy() */
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void
+MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void
+MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void
+MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = (unsigned char*)(ctx->in) + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void
+MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc
new file mode 100644
index 00000000..f10489f7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.cc
@@ -0,0 +1,321 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/messagequeue.h"
+#include "talk/base/physicalsocketserver.h"
+
+#ifdef POSIX
+extern "C" {
+#include <sys/time.h>
+}
+#endif
+
+namespace cricket {
+
+//------------------------------------------------------------------
+// MessageQueueManager
+
+MessageQueueManager* MessageQueueManager::instance_;
+
+MessageQueueManager* MessageQueueManager::Instance() {
+ // Note: This is not thread safe, but it is first called before threads are
+ // spawned.
+ if (!instance_)
+ instance_ = new MessageQueueManager;
+ return instance_;
+}
+
+MessageQueueManager::MessageQueueManager() {
+}
+
+MessageQueueManager::~MessageQueueManager() {
+}
+
+void MessageQueueManager::Add(MessageQueue *message_queue) {
+ CritScope cs(&crit_);
+ message_queues_.push_back(message_queue);
+}
+
+void MessageQueueManager::Remove(MessageQueue *message_queue) {
+ CritScope cs(&crit_);
+ std::vector<MessageQueue *>::iterator iter;
+ iter = std::find(message_queues_.begin(), message_queues_.end(), message_queue);
+ if (iter != message_queues_.end())
+ message_queues_.erase(iter);
+}
+
+void MessageQueueManager::Clear(MessageHandler *handler) {
+ CritScope cs(&crit_);
+ std::vector<MessageQueue *>::iterator iter;
+ for (iter = message_queues_.begin(); iter != message_queues_.end(); iter++)
+ (*iter)->Clear(handler);
+}
+
+//------------------------------------------------------------------
+// MessageQueue
+
+MessageQueue::MessageQueue(SocketServer* ss)
+ : ss_(ss), new_ss(false), fStop_(false), fPeekKeep_(false) {
+ if (!ss_) {
+ new_ss = true;
+ ss_ = new PhysicalSocketServer();
+ }
+ MessageQueueManager::Instance()->Add(this);
+}
+
+MessageQueue::~MessageQueue() {
+ Clear(NULL);
+ if (new_ss)
+ delete ss_;
+ MessageQueueManager::Instance()->Remove(this);
+}
+
+void MessageQueue::set_socketserver(SocketServer* ss) {
+ if (new_ss)
+ delete ss_;
+ new_ss = false;
+ ss_ = ss;
+}
+
+void MessageQueue::Stop() {
+ fStop_ = true;
+ ss_->WakeUp();
+}
+
+bool MessageQueue::IsStopping() {
+ return fStop_;
+}
+
+void MessageQueue::Restart() {
+ fStop_ = false;
+}
+
+bool MessageQueue::Peek(Message *pmsg, int cmsWait) {
+ if (fStop_)
+ return false;
+ if (fPeekKeep_) {
+ *pmsg = msgPeek_;
+ return true;
+ }
+ if (!Get(pmsg, cmsWait))
+ return false;
+ msgPeek_ = *pmsg;
+ fPeekKeep_ = true;
+ return true;
+}
+
+bool MessageQueue::Get(Message *pmsg, int cmsWait) {
+ // Force stopping
+
+ if (fStop_)
+ return false;
+
+ // Return and clear peek if present
+ // Always return the peek if it exists so there is Peek/Get symmetry
+
+ if (fPeekKeep_) {
+ *pmsg = msgPeek_;
+ fPeekKeep_ = false;
+ return true;
+ }
+
+ // Get w/wait + timer scan / dispatch + socket / event multiplexer dispatch
+
+ int cmsTotal = cmsWait;
+ int cmsElapsed = 0;
+ uint32 msStart = GetMillisecondCount();
+ uint32 msCurrent = msStart;
+ while (!fStop_) {
+ // Check for sent messages
+
+ ReceiveSends();
+
+ // Check queues
+
+ int cmsDelayNext = -1;
+ {
+ CritScope cs(&crit_);
+
+ // Check for delayed messages that have been triggered
+ // Calc the next trigger too
+
+ while (!dmsgq_.empty()) {
+ if (msCurrent < dmsgq_.top().msTrigger_) {
+ cmsDelayNext = dmsgq_.top().msTrigger_ - msCurrent;
+ break;
+ }
+ msgq_.push(dmsgq_.top().msg_);
+ dmsgq_.pop();
+ }
+
+ // Check for posted events
+
+ if (!msgq_.empty()) {
+ *pmsg = msgq_.front();
+ msgq_.pop();
+ return true;
+ }
+ }
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == -1) {
+ cmsNext = cmsDelayNext;
+ } else {
+ cmsNext = cmsTotal - cmsElapsed;
+ if (cmsNext < 0)
+ cmsNext = 0;
+ if (cmsDelayNext != -1 && cmsDelayNext < cmsNext)
+ cmsNext = cmsDelayNext;
+ }
+
+ // Wait and multiplex in the meantime
+ ss_->Wait(cmsNext, true);
+
+ // If the specified timeout expired, return
+
+ msCurrent = GetMillisecondCount();
+ cmsElapsed = msCurrent - msStart;
+ if (cmsWait != -1) {
+ if (cmsElapsed >= cmsWait)
+ return false;
+ }
+ }
+ return false;
+}
+
+void MessageQueue::ReceiveSends() {
+}
+
+void MessageQueue::Post(MessageHandler *phandler, uint32 id,
+ MessageData *pdata) {
+ // Keep thread safe
+ // Add the message to the end of the queue
+ // Signal for the multiplexer to return
+
+ CritScope cs(&crit_);
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ msgq_.push(msg);
+ ss_->WakeUp();
+}
+
+void MessageQueue::PostDelayed(int cmsDelay, MessageHandler *phandler,
+ uint32 id, MessageData *pdata) {
+ // Keep thread safe
+ // Add to the priority queue. Gets sorted soonest first.
+ // Signal for the multiplexer to return.
+
+ CritScope cs(&crit_);
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ dmsgq_.push(DelayedMessage(cmsDelay, &msg));
+ ss_->WakeUp();
+}
+
+int MessageQueue::GetDelay() {
+ CritScope cs(&crit_);
+
+ if (!msgq_.empty())
+ return 0;
+
+ if (!dmsgq_.empty()) {
+ int delay = dmsgq_.top().msTrigger_ - GetMillisecondCount();
+ if (delay < 0)
+ delay = 0;
+ return delay;
+ }
+
+ return -1;
+}
+
+void MessageQueue::Clear(MessageHandler *phandler, uint32 id) {
+ CritScope cs(&crit_);
+
+ // Remove messages with phandler
+
+ if (fPeekKeep_) {
+ if (phandler == NULL || msgPeek_.phandler == phandler) {
+ if (id == (uint32)-1 || msgPeek_.message_id == id) {
+ delete msgPeek_.pdata;
+ fPeekKeep_ = false;
+ }
+ }
+ }
+
+ // Remove from ordered message queue
+
+ size_t c = msgq_.size();
+ while (c-- != 0) {
+ Message msg = msgq_.front();
+ msgq_.pop();
+ if (phandler != NULL && msg.phandler != phandler) {
+ msgq_.push(msg);
+ } else {
+ if (id == (uint32)-1 || msg.message_id == id) {
+ delete msg.pdata;
+ } else {
+ msgq_.push(msg);
+ }
+ }
+ }
+
+ // Remove from priority queue. Not directly iterable, so use this approach
+
+ std::queue<DelayedMessage> dmsgs;
+ while (!dmsgq_.empty()) {
+ DelayedMessage dmsg = dmsgq_.top();
+ dmsgq_.pop();
+ if (phandler != NULL && dmsg.msg_.phandler != phandler) {
+ dmsgs.push(dmsg);
+ } else {
+ if (id == (uint32)-1 || dmsg.msg_.message_id == id) {
+ delete dmsg.msg_.pdata;
+ } else {
+ dmsgs.push(dmsg);
+ }
+ }
+ }
+ while (!dmsgs.empty()) {
+ dmsgq_.push(dmsgs.front());
+ dmsgs.pop();
+ }
+}
+
+void MessageQueue::Dispatch(Message *pmsg) {
+ pmsg->phandler->OnMessage(pmsg);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h
new file mode 100644
index 00000000..2a9cbed6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/messagequeue.h
@@ -0,0 +1,164 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MESSAGEQUEUE_H__
+#define __MESSAGEQUEUE_H__
+
+#include "talk/base/basictypes.h"
+#include "talk/base/criticalsection.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/jtime.h"
+#include <vector>
+#include <queue>
+#include <algorithm>
+
+namespace cricket {
+
+struct Message;
+class MessageQueue;
+class MessageHandler;
+
+// MessageQueueManager does cleanup of of message queues
+
+class MessageQueueManager {
+public:
+ static MessageQueueManager* Instance();
+
+ void Add(MessageQueue *message_queue);
+ void Remove(MessageQueue *message_queue);
+ void Clear(MessageHandler *handler);
+
+private:
+ MessageQueueManager();
+ ~MessageQueueManager();
+
+ static MessageQueueManager* instance_;
+ std::vector<MessageQueue *> message_queues_;
+ CriticalSection crit_;
+};
+
+// Messages get dispatched to a MessageHandler
+
+class MessageHandler {
+public:
+ virtual ~MessageHandler() {
+ MessageQueueManager::Instance()->Clear(this);
+ }
+
+ virtual void OnMessage(Message *pmsg) = 0;
+};
+
+// Derive from this for specialized data
+// App manages lifetime, except when messages are purged
+
+class MessageData {
+public:
+ MessageData() {}
+ virtual ~MessageData() {}
+};
+
+template <class arg1_type>
+class TypedMessageData : public MessageData {
+public:
+ TypedMessageData(arg1_type data) {
+ data_ = data;
+ }
+ arg1_type data() {
+ return data_;
+ }
+private:
+ arg1_type data_;
+};
+
+// No destructor
+
+struct Message {
+ Message() {
+ memset(this, 0, sizeof(*this));
+ }
+ MessageHandler *phandler;
+ uint32 message_id;
+ MessageData *pdata;
+};
+
+// DelayedMessage goes into a priority queue, sorted by trigger time
+
+class DelayedMessage {
+public:
+ DelayedMessage(int cmsDelay, Message *pmsg) {
+ cmsDelay_ = cmsDelay;
+ msTrigger_ = GetMillisecondCount() + cmsDelay;
+ msg_ = *pmsg;
+ }
+
+ bool operator< (const DelayedMessage& dmsg) const {
+ return dmsg.msTrigger_ < msTrigger_;
+ }
+
+ int cmsDelay_; // for debugging
+ uint32 msTrigger_;
+ Message msg_;
+};
+
+class MessageQueue {
+public:
+ MessageQueue(SocketServer* ss = 0);
+ virtual ~MessageQueue();
+
+ SocketServer* socketserver() { return ss_; }
+ void set_socketserver(SocketServer* ss);
+
+ // Once the queue is stopped, all calls to Get/Peek will return false.
+ virtual void Stop();
+ virtual bool IsStopping();
+ virtual void Restart();
+
+ virtual bool Get(Message *pmsg, int cmsWait = -1);
+ virtual bool Peek(Message *pmsg, int cmsWait = 0);
+ virtual void Post(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL);
+ virtual void PostDelayed(int cmsDelay, MessageHandler *phandler,
+ uint32 id = 0, MessageData *pdata = NULL);
+ virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1);
+ virtual void Dispatch(Message *pmsg);
+ virtual void ReceiveSends();
+ virtual int GetDelay();
+
+protected:
+ SocketServer* ss_;
+ bool new_ss;
+ bool fStop_;
+ bool fPeekKeep_;
+ Message msgPeek_;
+ std::queue<Message> msgq_;
+ std::priority_queue<DelayedMessage> dmsgq_;
+ CriticalSection crit_;
+};
+
+} // namespace cricket
+
+#endif // __MESSAGEQUEUE_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc
new file mode 100644
index 00000000..21b3a08f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.cc
@@ -0,0 +1,382 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/base/network.h"
+#include "talk/base/socket.h" // this includes something that makes windows happy
+#include "talk/base/jtime.h"
+#include "talk/base/basicdefs.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cfloat>
+#include <cmath>
+#include <sstream>
+
+#ifdef POSIX
+extern "C" {
+#include <sys/utsname.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <errno.h>
+}
+#endif // POSIX
+
+#ifdef WIN32
+#include <Iphlpapi.h>
+#endif
+
+namespace {
+
+const double kAlpha = 0.5; // weight for data infinitely far in the past
+const double kHalfLife = 2000; // half life of exponential decay (in ms)
+const double kLog2 = 0.693147180559945309417;
+const double kLambda = kLog2 / kHalfLife;
+
+// assume so-so quality unless data says otherwise
+const double kDefaultQuality = cricket::QUALITY_FAIR;
+
+typedef std::map<std::string,std::string> StrMap;
+
+void BuildMap(const StrMap& map, std::string& str) {
+ str.append("{");
+ bool first = true;
+ for (StrMap::const_iterator i = map.begin(); i != map.end(); ++i) {
+ if (!first) str.append(",");
+ str.append(i->first);
+ str.append("=");
+ str.append(i->second);
+ first = false;
+ }
+ str.append("}");
+}
+
+void ParseCheck(std::istringstream& ist, char ch) {
+ if (ist.get() != ch)
+ LOG(LERROR) << "Expecting '" << ch << "'";
+}
+
+std::string ParseString(std::istringstream& ist) {
+ std::string str;
+ int count = 0;
+ while (ist) {
+ char ch = ist.peek();
+ if ((count == 0) && ((ch == '=') || (ch == ',') || (ch == '}'))) {
+ break;
+ } else if (ch == '{') {
+ count += 1;
+ } else if (ch == '}') {
+ count -= 1;
+ if (count < 0)
+ LOG(LERROR) << "mismatched '{' and '}'";
+ }
+ str.append(1, static_cast<char>(ist.get()));
+ }
+ return str;
+}
+
+void ParseMap(const std::string& str, StrMap& map) {
+ if (str.size() == 0)
+ return;
+ std::istringstream ist(str);
+ ParseCheck(ist, '{');
+ for (;;) {
+ std::string key = ParseString(ist);
+ ParseCheck(ist, '=');
+ std::string val = ParseString(ist);
+ map[key] = val;
+ if (ist.peek() == ',')
+ ist.get();
+ else
+ break;
+ }
+ ParseCheck(ist, '}');
+ if (ist.rdbuf()->in_avail() != 0)
+ LOG(LERROR) << "Unexpected characters at end";
+}
+
+#if 0
+const std::string TEST_MAP0_IN = "";
+const std::string TEST_MAP0_OUT = "{}";
+const std::string TEST_MAP1 = "{a=12345}";
+const std::string TEST_MAP2 = "{a=12345,b=67890}";
+const std::string TEST_MAP3 = "{a=12345,b=67890,c=13579}";
+const std::string TEST_MAP4 = "{a={d=12345,e=67890}}";
+const std::string TEST_MAP5 = "{a={d=12345,e=67890},b=67890}";
+const std::string TEST_MAP6 = "{a=12345,b={d=12345,e=67890}}";
+const std::string TEST_MAP7 = "{a=12345,b={d=12345,e=67890},c=13579}";
+
+class MyTest {
+public:
+ MyTest() {
+ test(TEST_MAP0_IN, TEST_MAP0_OUT);
+ test(TEST_MAP1, TEST_MAP1);
+ test(TEST_MAP2, TEST_MAP2);
+ test(TEST_MAP3, TEST_MAP3);
+ test(TEST_MAP4, TEST_MAP4);
+ test(TEST_MAP5, TEST_MAP5);
+ test(TEST_MAP6, TEST_MAP6);
+ test(TEST_MAP7, TEST_MAP7);
+ }
+ void test(const std::string& input, const std::string& exp_output) {
+ StrMap map;
+ ParseMap(input, map);
+ std::string output;
+ BuildMap(map, output);
+ LOG(INFO) << " ******** " << (output == exp_output);
+ }
+};
+
+static MyTest myTest;
+#endif
+
+template <typename T>
+std::string ToString(T val) {
+ std::ostringstream ost;
+ ost << val;
+ return ost.str();
+}
+
+template <typename T>
+T FromString(std::string str) {
+ std::istringstream ist(str);
+ T val;
+ ist >> val;
+ return val;
+}
+
+}
+
+namespace cricket {
+
+#ifdef POSIX
+void NetworkManager::CreateNetworks(std::vector<Network*>& networks) {
+ int fd;
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ PLOG(LERROR, errno) << "socket";
+ return;
+ }
+
+ struct ifconf ifc;
+ ifc.ifc_len = 64 * sizeof(struct ifreq);
+ ifc.ifc_buf = new char[ifc.ifc_len];
+
+ if (ioctl(fd, SIOCGIFCONF, &ifc) < 0) {
+ PLOG(LERROR, errno) << "ioctl";
+ return;
+ }
+ assert(ifc.ifc_len < static_cast<int>(64 * sizeof(struct ifreq)));
+
+ struct ifreq* ptr = reinterpret_cast<struct ifreq*>(ifc.ifc_buf);
+ struct ifreq* end =
+ reinterpret_cast<struct ifreq*>(ifc.ifc_buf + ifc.ifc_len);
+
+ while (ptr < end) {
+ struct sockaddr_in* inaddr =
+ reinterpret_cast<struct sockaddr_in*>(&ptr->ifr_ifru.ifru_addr);
+ if (inaddr->sin_family == AF_INET) {
+ uint32 ip = ntohl(inaddr->sin_addr.s_addr);
+ networks.push_back(new Network(std::string(ptr->ifr_name), ip));
+ }
+#ifdef _SIZEOF_ADDR_IFREQ
+ ptr = reinterpret_cast<struct ifreq*>(
+ reinterpret_cast<char*>(ptr) + _SIZEOF_ADDR_IFREQ(*ptr));
+#else
+ ptr++;
+#endif
+ }
+
+ delete [] ifc.ifc_buf;
+ close(fd);
+}
+#endif
+
+#ifdef WIN32
+void NetworkManager::CreateNetworks(std::vector<Network*>& networks) {
+ IP_ADAPTER_INFO info_temp;
+ ULONG len = 0;
+
+ if (GetAdaptersInfo(&info_temp, &len) != ERROR_BUFFER_OVERFLOW)
+ return;
+ IP_ADAPTER_INFO *infos = new IP_ADAPTER_INFO[len];
+ if (GetAdaptersInfo(infos, &len) != NO_ERROR)
+ return;
+
+ int count = 0;
+ for (IP_ADAPTER_INFO *info = infos; info != NULL; info = info->Next) {
+ if (info->Type == MIB_IF_TYPE_LOOPBACK)
+ continue;
+ if (strcmp(info->IpAddressList.IpAddress.String, "0.0.0.0") == 0)
+ continue;
+
+ // In production, don't transmit the network name because of
+ // privacy concerns. Transmit a number instead.
+
+ std::string name;
+#if defined(PRODUCTION)
+ std::ostringstream ost;
+ ost << count;
+ name = ost.str();
+ count++;
+#else
+ name = info->Description;
+#endif
+
+ networks.push_back(new Network(name,
+ SocketAddress::StringToIP(info->IpAddressList.IpAddress.String)));
+ }
+
+ delete infos;
+}
+#endif
+
+void NetworkManager::GetNetworks(std::vector<Network*>& result) {
+ std::vector<Network*> list;
+ CreateNetworks(list);
+
+ for (uint32 i = 0; i < list.size(); ++i) {
+ NetworkMap::iterator iter = networks_.find(list[i]->name());
+
+ Network* network;
+ if (iter == networks_.end()) {
+ network = list[i];
+ } else {
+ network = iter->second;
+ network->set_ip(list[i]->ip());
+ delete list[i];
+ }
+
+ networks_[network->name()] = network;
+ result.push_back(network);
+ }
+}
+
+std::string NetworkManager::GetState() {
+ StrMap map;
+ for (NetworkMap::iterator i = networks_.begin(); i != networks_.end(); ++i)
+ map[i->first] = i->second->GetState();
+
+ std::string str;
+ BuildMap(map, str);
+ return str;
+}
+
+void NetworkManager::SetState(std::string str) {
+ StrMap map;
+ ParseMap(str, map);
+
+ for (StrMap::iterator i = map.begin(); i != map.end(); ++i) {
+ std::string name = i->first;
+ std::string state = i->second;
+
+ Network* network = new Network(name, 0);
+ network->SetState(state);
+ networks_[name] = network;
+ }
+}
+
+Network::Network(const std::string& name, uint32 ip)
+ : name_(name), ip_(ip), uniform_numerator_(0), uniform_denominator_(0),
+ exponential_numerator_(0), exponential_denominator_(0),
+ quality_(kDefaultQuality) {
+
+ last_data_time_ = Time();
+
+ // TODO: seed the historical data with one data point based on the link speed
+ // metric from XP (4.0 if < 50, 3.0 otherwise).
+}
+
+void Network::StartSession(NetworkSession* session) {
+ assert(std::find(sessions_.begin(), sessions_.end(), session) == sessions_.end());
+ sessions_.push_back(session);
+}
+
+void Network::StopSession(NetworkSession* session) {
+ SessionList::iterator iter = std::find(sessions_.begin(), sessions_.end(), session);
+ if (iter != sessions_.end())
+ sessions_.erase(iter);
+}
+
+void Network::EstimateQuality() {
+ uint32 now = Time();
+
+ // Add new data points for the current time.
+ for (uint32 i = 0; i < sessions_.size(); ++i) {
+ if (sessions_[i]->HasQuality())
+ AddDataPoint(now, sessions_[i]->GetCurrentQuality());
+ }
+
+ // Construct the weighted average using both uniform and exponential weights.
+
+ double exp_shift = exp(-kLambda * (now - last_data_time_));
+ double numerator = uniform_numerator_ + exp_shift * exponential_numerator_;
+ double denominator = uniform_denominator_ + exp_shift * exponential_denominator_;
+
+ if (denominator < DBL_EPSILON)
+ quality_ = kDefaultQuality;
+ else
+ quality_ = numerator / denominator;
+}
+
+void Network::AddDataPoint(uint32 time, double quality) {
+ uniform_numerator_ += kAlpha * quality;
+ uniform_denominator_ += kAlpha;
+
+ double exp_shift = exp(-kLambda * (time - last_data_time_));
+ exponential_numerator_ = (1 - kAlpha) * quality + exp_shift * exponential_numerator_;
+ exponential_denominator_ = (1 - kAlpha) + exp_shift * exponential_denominator_;
+
+ last_data_time_ = time;
+}
+
+std::string Network::GetState() {
+ StrMap map;
+ map["lt"] = ToString<uint32>(last_data_time_);
+ map["un"] = ToString<double>(uniform_numerator_);
+ map["ud"] = ToString<double>(uniform_denominator_);
+ map["en"] = ToString<double>(exponential_numerator_);
+ map["ed"] = ToString<double>(exponential_denominator_);
+
+ std::string str;
+ BuildMap(map, str);
+ return str;
+}
+
+void Network::SetState(std::string str) {
+ StrMap map;
+ ParseMap(str, map);
+
+ last_data_time_ = FromString<uint32>(map["lt"]);
+ uniform_numerator_ = FromString<double>(map["un"]);
+ uniform_denominator_ = FromString<double>(map["ud"]);
+ exponential_numerator_ = FromString<double>(map["en"]);
+ exponential_denominator_ = FromString<double>(map["ed"]);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h
new file mode 100644
index 00000000..2cc9128a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/network.h
@@ -0,0 +1,136 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __NETWORK_H__
+#define __NETWORK_H__
+
+#include "talk/base/basictypes.h"
+
+#include <deque>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+class Network;
+class NetworkSession;
+
+// Keeps track of the available network interfaces over time so that quality
+// information can be aggregated and recorded.
+class NetworkManager {
+public:
+
+ // Updates and returns the current list of networks available on this machine.
+ // This version will make sure that repeated calls return the same object for
+ // a given network, so that quality is tracked appropriately.
+ void GetNetworks(std::vector<Network*>& networks);
+
+ // Reads and writes the state of the quality database in a string format.
+ std::string GetState();
+ void SetState(std::string str);
+
+ // Creates a network object for each network available on the machine.
+ static void CreateNetworks(std::vector<Network*>& networks);
+
+private:
+ typedef std::map<std::string,Network*> NetworkMap;
+
+ NetworkMap networks_;
+};
+
+// Represents a Unix-type network interface, with a name and single address.
+// It also includes the ability to track and estimate quality.
+class Network {
+public:
+ Network(const std::string& name, uint32 ip);
+
+ // Returns the OS name of this network. This is considered the primary key
+ // that identifies each network.
+ const std::string& name() const { return name_; }
+
+ // Identifies the current IP address used by this network.
+ uint32 ip() const { return ip_; }
+ void set_ip(uint32 ip) { ip_ = ip; }
+
+ // Updates the list of sessions that are ongoing.
+ void StartSession(NetworkSession* session);
+ void StopSession(NetworkSession* session);
+
+ // Re-computes the estimate of near-future quality based on the information
+ // as of this exact moment.
+ void EstimateQuality();
+
+ // Returns the current estimate of the near-future quality of connections
+ // that use this local interface.
+ double quality() { return quality_; }
+
+private:
+ typedef std::vector<NetworkSession*> SessionList;
+
+ std::string name_;
+ uint32 ip_;
+ SessionList sessions_;
+ double uniform_numerator_;
+ double uniform_denominator_;
+ double exponential_numerator_;
+ double exponential_denominator_;
+ uint32 last_data_time_;
+ double quality_;
+
+ // Updates the statistics maintained to include the given estimate.
+ void AddDataPoint(uint32 time, double quality);
+
+ // Converts the internal state to and from a string. This is used to record
+ // quality information into a permanent store.
+ void SetState(std::string str);
+ std::string GetState();
+
+ friend class NetworkManager;
+};
+
+// Represents a session that is in progress using a particular network and can
+// provide data about the quality of the network at any given moment.
+class NetworkSession {
+public:
+ // Determines whether this session has an estimate at this moment. We will
+ // only call GetCurrentQuality when this returns true.
+ virtual bool HasQuality() = 0;
+
+ // Returns an estimate of the quality at this exact moment. The result should
+ // be a MOS (mean opinion score) value.
+ virtual float GetCurrentQuality() = 0;
+
+};
+
+const double QUALITY_BAD = 3.0;
+const double QUALITY_FAIR = 3.35;
+const double QUALITY_GOOD = 3.7;
+
+} // namespace cricket
+
+#endif // __NETWORK_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc
new file mode 100644
index 00000000..91d2daad
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.cc
@@ -0,0 +1,1117 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <cassert>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+}
+#endif
+
+#include "talk/base/basictypes.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/physicalsocketserver.h"
+#include "talk/base/jtime.h"
+#include "talk/base/winping.h"
+
+#ifdef __linux
+#define IP_MTU 14 // Until this is integrated from linux/in.h to netinet/in.h
+#endif // __linux
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#undef SetPort
+
+#include <algorithm>
+#include <iostream>
+
+class WinsockInitializer {
+public:
+ WinsockInitializer() {
+ WSADATA wsaData;
+ WORD wVersionRequested = MAKEWORD(1, 0);
+ err_ = WSAStartup(wVersionRequested, &wsaData);
+ }
+ ~WinsockInitializer() {
+ WSACleanup();
+ }
+ int error() {
+ return err_;
+ }
+private:
+ int err_;
+};
+WinsockInitializer g_winsockinit;
+#endif
+
+namespace cricket {
+
+const int kfRead = 0x0001;
+const int kfWrite = 0x0002;
+const int kfConnect = 0x0004;
+const int kfClose = 0x0008;
+
+
+// Standard MTUs
+const uint16 PACKET_MAXIMUMS[] = {
+ 65535, // Theoretical maximum, Hyperchannel
+ 32000, // Nothing
+ 17914, // 16Mb IBM Token Ring
+ 8166, // IEEE 802.4
+ //4464, // IEEE 802.5 (4Mb max)
+ 4352, // FDDI
+ //2048, // Wideband Network
+ 2002, // IEEE 802.5 (4Mb recommended)
+ //1536, // Expermental Ethernet Networks
+ //1500, // Ethernet, Point-to-Point (default)
+ 1492, // IEEE 802.3
+ 1006, // SLIP, ARPANET
+ //576, // X.25 Networks
+ //544, // DEC IP Portal
+ //512, // NETBIOS
+ 508, // IEEE 802/Source-Rt Bridge, ARCNET
+ 296, // Point-to-Point (low delay)
+ 68, // Official minimum
+ 0, // End of list marker
+};
+
+const uint32 IP_HEADER_SIZE = 20;
+const uint32 ICMP_HEADER_SIZE = 8;
+
+class PhysicalSocket : public AsyncSocket {
+public:
+ PhysicalSocket(PhysicalSocketServer* ss, SOCKET s = INVALID_SOCKET)
+ : ss_(ss), s_(s), enabled_events_(0), error_(0),
+ state_((s == INVALID_SOCKET) ? CS_CLOSED : CS_CONNECTED) {
+ if (s != INVALID_SOCKET)
+ enabled_events_ = kfRead | kfWrite;
+ }
+
+ virtual ~PhysicalSocket() {
+ Close();
+ }
+
+ // Creates the underlying OS socket (same as the "socket" function).
+ virtual bool Create(int type) {
+ Close();
+ s_ = ::socket(AF_INET, type, 0);
+ UpdateLastError();
+ enabled_events_ = kfRead | kfWrite;
+ return s_ != INVALID_SOCKET;
+ }
+
+ SocketAddress GetLocalAddress() const {
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getsockname(s_, (struct sockaddr*)&addr, &addrlen);
+ assert(addrlen == sizeof(addr));
+ if (result >= 0) {
+ return SocketAddress(NetworkToHost32(addr.sin_addr.s_addr),
+ NetworkToHost16(addr.sin_port));
+ } else {
+ return SocketAddress();
+ }
+ }
+
+ SocketAddress GetRemoteAddress() const {
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(addr);
+ int result = ::getpeername(s_, (struct sockaddr*)&addr, &addrlen);
+ assert(addrlen == sizeof(addr));
+ if (result >= 0) {
+ return SocketAddress(
+ NetworkToHost32(addr.sin_addr.s_addr),
+ NetworkToHost16(addr.sin_port));
+ } else {
+ assert(errno == ENOTCONN);
+ return SocketAddress();
+ }
+ }
+
+ int Bind(const SocketAddress& addr) {
+ struct sockaddr_in saddr;
+ IP2SA(&addr, &saddr);
+ int err = ::bind(s_, (struct sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ return err;
+ }
+
+ int Connect(const SocketAddress& addr) {
+ // TODO: Implicit creation is required to reconnect...
+ // ...but should we make it more explicit?
+ if ((s_ == INVALID_SOCKET) && !Create(SOCK_STREAM))
+ return SOCKET_ERROR;
+ SocketAddress addr2(addr);
+ if (addr2.IsUnresolved()) {
+ LOG(INFO) << "Resolving addr in PhysicalSocket::Connect";
+ addr2.Resolve(); // TODO: Do this async later?
+ }
+ struct sockaddr_in saddr;
+ IP2SA(&addr2, &saddr);
+ int err = ::connect(s_, (struct sockaddr*)&saddr, sizeof(saddr));
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Connect(" << addr2.ToString() << ") Ret: " << err << " Error: " << error_;
+ if (err == 0) {
+ state_ = CS_CONNECTED;
+ } else if (IsBlockingError(error_)) {
+ state_ = CS_CONNECTING;
+ enabled_events_ |= kfConnect;
+ }
+ return err;
+ }
+
+ int GetError() const {
+ return error_;
+ }
+
+ void SetError(int error) {
+ error_ = error;
+ }
+
+ ConnState GetState() const {
+ return state_;
+ }
+
+ int SetOption(Option opt, int value) {
+ assert(opt == OPT_DONTFRAGMENT);
+#ifdef WIN32
+ value = (value == 0) ? 0 : 1;
+ return ::setsockopt(
+ s_, IPPROTO_IP, IP_DONTFRAGMENT, reinterpret_cast<char*>(&value),
+ sizeof(value));
+#endif
+#ifdef __linux
+ value = (value == 0) ? IP_PMTUDISC_DONT : IP_PMTUDISC_DO;
+ return ::setsockopt(
+ s_, IPPROTO_IP, IP_MTU_DISCOVER, &value, sizeof(value));
+#endif
+#ifdef OSX
+ // This is not possible on OSX.
+ return -1;
+#endif
+ }
+
+ int Send(const void *pv, size_t cb) {
+ int sent = ::send(s_, reinterpret_cast<const char *>(pv), (int)cb, 0);
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Send(" << cb << ") Ret: " << sent << " Error: " << error_;
+ ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false
+ if ((sent < 0) && IsBlockingError(error_)) {
+ enabled_events_ |= kfWrite;
+ }
+ return sent;
+ }
+
+ int SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ struct sockaddr_in saddr;
+ IP2SA(&addr, &saddr);
+ int sent = ::sendto(
+ s_, (const char *)pv, (int)cb, 0, (struct sockaddr*)&saddr,
+ sizeof(saddr));
+ UpdateLastError();
+ ASSERT(sent <= static_cast<int>(cb)); // We have seen minidumps where this may be false
+ if ((sent < 0) && IsBlockingError(error_)) {
+ enabled_events_ |= kfWrite;
+ }
+ return sent;
+ }
+
+ int Recv(void *pv, size_t cb) {
+ int received = ::recv(s_, (char *)pv, (int)cb, 0);
+ UpdateLastError();
+ if ((received >= 0) || IsBlockingError(error_)) {
+ enabled_events_ |= kfRead;
+ }
+ return received;
+ }
+
+ int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ struct sockaddr saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ int received = ::recvfrom(s_, (char *)pv, (int)cb, 0, &saddr, &cbAddr);
+ UpdateLastError();
+ if ((received >= 0) && (paddr != NULL))
+ SA2IP(&saddr, paddr);
+ if ((received >= 0) || IsBlockingError(error_)) {
+ enabled_events_ |= kfRead;
+ }
+ return received;
+ }
+
+ int Listen(int backlog) {
+ int err = ::listen(s_, backlog);
+ UpdateLastError();
+ if (err == 0)
+ state_ = CS_CONNECTING;
+ return err;
+ }
+
+ Socket* Accept(SocketAddress *paddr) {
+ struct sockaddr saddr;
+ socklen_t cbAddr = sizeof(saddr);
+ SOCKET s = ::accept(s_, &saddr, &cbAddr);
+ UpdateLastError();
+ if (s == INVALID_SOCKET)
+ return NULL;
+ if (paddr != NULL)
+ SA2IP(&saddr, paddr);
+ return ss_->WrapSocket(s);
+ }
+
+ int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+ int err = ::closesocket(s_);
+ UpdateLastError();
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] Close() Ret: " << err << " Error: " << error_;
+ s_ = INVALID_SOCKET;
+ state_ = CS_CLOSED;
+ enabled_events_ = 0;
+ return err;
+ }
+
+ int EstimateMTU(uint16* mtu) {
+ SocketAddress addr = GetRemoteAddress();
+ if (addr.IsAny()) {
+ error_ = ENOTCONN;
+ return -1;
+ }
+
+#ifdef WIN32
+
+ WinPing ping;
+ if (!ping.IsValid()) {
+ error_ = EINVAL; // can't think of a better error ID
+ return -1;
+ }
+
+ for (int level = 0; PACKET_MAXIMUMS[level + 1] > 0; ++level) {
+ int32 size = PACKET_MAXIMUMS[level] - IP_HEADER_SIZE - ICMP_HEADER_SIZE;
+ if (ping.Ping(addr.ip(), size, 0, 1, false) != WinPing::PING_TOO_LARGE) {
+ *mtu = PACKET_MAXIMUMS[level];
+ return 0;
+ }
+ }
+
+ assert(false);
+ return 0;
+
+#endif // WIN32
+
+#ifdef __linux
+
+ int value;
+ socklen_t vlen = sizeof(value);
+ int err = getsockopt(s_, IPPROTO_IP, IP_MTU, &value, &vlen);
+ if (err < 0) {
+ UpdateLastError();
+ return err;
+ }
+
+ assert((0 <= value) && (value <= 65536));
+ *mtu = uint16(value);
+ return 0;
+
+#endif // __linux
+
+ // TODO: OSX support
+ }
+
+ SocketServer* socketserver() { return ss_; }
+
+protected:
+ PhysicalSocketServer* ss_;
+ SOCKET s_;
+ uint32 enabled_events_;
+ int error_;
+ ConnState state_;
+
+ void UpdateLastError() {
+#ifdef WIN32
+ error_ = WSAGetLastError();
+#endif
+#ifdef POSIX
+ error_ = errno;
+#endif
+ }
+
+ void IP2SA(const SocketAddress *paddr, struct sockaddr_in *psaddr) {
+ memset(psaddr, 0, sizeof(*psaddr));
+ psaddr->sin_family = AF_INET;
+ psaddr->sin_port = HostToNetwork16(paddr->port());
+ if (paddr->ip() == 0)
+ psaddr->sin_addr.s_addr = INADDR_ANY;
+ else
+ psaddr->sin_addr.s_addr = HostToNetwork32(paddr->ip());
+ }
+
+ void SA2IP(const struct sockaddr *psaddr, SocketAddress *paddr) {
+ const struct sockaddr_in *psaddr_in =
+ reinterpret_cast<const struct sockaddr_in*>(psaddr);
+ paddr->SetIP(NetworkToHost32(psaddr_in->sin_addr.s_addr));
+ paddr->SetPort(NetworkToHost16(psaddr_in->sin_port));
+ }
+};
+
+#ifdef POSIX
+class Dispatcher {
+public:
+ virtual uint32 GetRequestedEvents() = 0;
+ virtual void OnPreEvent(uint32 ff) = 0;
+ virtual void OnEvent(uint32 ff, int err) = 0;
+ virtual int GetDescriptor() = 0;
+};
+
+class EventDispatcher : public Dispatcher {
+public:
+ EventDispatcher(PhysicalSocketServer* ss) : ss_(ss), fSignaled_(false) {
+ if (pipe(afd_) < 0)
+ LOG(LERROR) << "pipe failed";
+ ss_->Add(this);
+ }
+
+ virtual ~EventDispatcher() {
+ ss_->Remove(this);
+ close(afd_[0]);
+ close(afd_[1]);
+ }
+
+ virtual void Signal() {
+ CritScope cs(&crit_);
+ if (!fSignaled_) {
+ uint8 b = 0;
+ if (write(afd_[1], &b, sizeof(b)) < 0)
+ LOG(LERROR) << "write failed";
+ fSignaled_ = true;
+ }
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return kfRead;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ // It is not possible to perfectly emulate an auto-resetting event with
+ // pipes. This simulates it by resetting before the event is handled.
+
+ CritScope cs(&crit_);
+ if (fSignaled_) {
+ uint8 b;
+ read(afd_[0], &b, sizeof(b));
+ fSignaled_ = false;
+ }
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ assert(false);
+ }
+
+ virtual int GetDescriptor() {
+ return afd_[0];
+ }
+
+private:
+ PhysicalSocketServer *ss_;
+ int afd_[2];
+ bool fSignaled_;
+ CriticalSection crit_;
+};
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+public:
+ SocketDispatcher(PhysicalSocketServer *ss) : PhysicalSocket(ss) {
+ ss_->Add(this);
+ }
+ SocketDispatcher(SOCKET s, PhysicalSocketServer *ss) : PhysicalSocket(ss, s) {
+ ss_->Add(this);
+ }
+
+ virtual ~SocketDispatcher() {
+ ss_->Remove(this);
+ }
+
+ bool Initialize() {
+ fcntl(s_, F_SETFL, fcntl(s_, F_GETFL, 0) | O_NONBLOCK);
+ return true;
+ }
+
+ virtual bool Create(int type) {
+ // Change the socket to be non-blocking.
+ if (!PhysicalSocket::Create(type))
+ return false;
+
+ return Initialize();
+ }
+
+ virtual int GetDescriptor() {
+ return s_;
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return enabled_events_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ if ((ff & kfRead) != 0) {
+ enabled_events_ &= ~kfRead;
+ SignalReadEvent(this);
+ }
+ if ((ff & kfWrite) != 0) {
+ enabled_events_ &= ~kfWrite;
+ SignalWriteEvent(this);
+ }
+ if ((ff & kfConnect) != 0) {
+ enabled_events_ &= ~kfConnect;
+ SignalConnectEvent(this);
+ }
+ if ((ff & kfClose) != 0)
+ SignalCloseEvent(this, err);
+ }
+};
+
+class FileDispatcher: public Dispatcher, public AsyncFile {
+public:
+ FileDispatcher(int fd, PhysicalSocketServer *ss) : ss_(ss), fd_(fd) {
+ set_readable(true);
+
+ ss_->Add(this);
+
+ fcntl(fd_, F_SETFL, fcntl(fd_, F_GETFL, 0) | O_NONBLOCK);
+ }
+
+ virtual ~FileDispatcher() {
+ ss_->Remove(this);
+ }
+
+ SocketServer* socketserver() { return ss_; }
+
+ virtual int GetDescriptor() {
+ return fd_;
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return flags_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ if ((ff & kfRead) != 0)
+ SignalReadEvent(this);
+ if ((ff & kfWrite) != 0)
+ SignalWriteEvent(this);
+ if ((ff & kfClose) != 0)
+ SignalCloseEvent(this, err);
+ }
+
+ virtual bool readable() {
+ return (flags_ & kfRead) != 0;
+ }
+
+ virtual void set_readable(bool value) {
+ flags_ = value ? (flags_ | kfRead) : (flags_ & ~kfRead);
+ }
+
+ virtual bool writable() {
+ return (flags_ & kfWrite) != 0;
+ }
+
+ virtual void set_writable(bool value) {
+ flags_ = value ? (flags_ | kfWrite) : (flags_ & ~kfWrite);
+ }
+
+private:
+ PhysicalSocketServer* ss_;
+ int fd_;
+ int flags_;
+};
+
+AsyncFile* PhysicalSocketServer::CreateFile(int fd) {
+ return new FileDispatcher(fd, this);
+}
+
+#endif // POSIX
+
+#ifdef WIN32
+class Dispatcher {
+public:
+ virtual uint32 GetRequestedEvents() = 0;
+ virtual void OnPreEvent(uint32 ff) = 0;
+ virtual void OnEvent(uint32 ff, int err) = 0;
+ virtual WSAEVENT GetWSAEvent() = 0;
+ virtual SOCKET GetSocket() = 0;
+ virtual bool CheckSignalClose() = 0;
+};
+
+uint32 FlagsToEvents(uint32 events) {
+ uint32 ffFD = FD_CLOSE | FD_ACCEPT;
+ if (events & kfRead)
+ ffFD |= FD_READ;
+ if (events & kfWrite)
+ ffFD |= FD_WRITE;
+ if (events & kfConnect)
+ ffFD |= FD_CONNECT;
+ return ffFD;
+}
+
+class EventDispatcher : public Dispatcher {
+public:
+ EventDispatcher(PhysicalSocketServer *ss) : ss_(ss) {
+ if (hev_ = WSACreateEvent()) {
+ ss_->Add(this);
+ }
+ }
+
+ ~EventDispatcher() {
+ if (hev_ != NULL) {
+ ss_->Remove(this);
+ WSACloseEvent(hev_);
+ hev_ = NULL;
+ }
+ }
+
+ virtual void Signal() {
+ if (hev_ != NULL)
+ WSASetEvent(hev_);
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return 0;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ WSAResetEvent(hev_);
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ }
+
+ virtual WSAEVENT GetWSAEvent() {
+ return hev_;
+ }
+
+ virtual SOCKET GetSocket() {
+ return INVALID_SOCKET;
+ }
+
+ virtual bool CheckSignalClose() { return false; }
+
+private:
+ PhysicalSocketServer* ss_;
+ WSAEVENT hev_;
+};
+
+class SocketDispatcher : public Dispatcher, public PhysicalSocket {
+public:
+ static int next_id_;
+ int id_;
+ bool signal_close_;
+ int signal_err_;
+
+ SocketDispatcher(PhysicalSocketServer* ss) : PhysicalSocket(ss), id_(0), signal_close_(false) {
+ }
+ SocketDispatcher(SOCKET s, PhysicalSocketServer* ss) : PhysicalSocket(ss, s), id_(0), signal_close_(false) {
+ }
+
+ virtual ~SocketDispatcher() {
+ Close();
+ }
+
+ bool Initialize() {
+ assert(s_ != INVALID_SOCKET);
+ // Must be a non-blocking
+ u_long argp = 1;
+ ioctlsocket(s_, FIONBIO, &argp);
+ ss_->Add(this);
+ return true;
+ }
+
+ virtual bool Create(int type) {
+ // Create socket
+ if (!PhysicalSocket::Create(type))
+ return false;
+
+ if (!Initialize())
+ return false;
+
+ do { id_ = ++next_id_; } while (id_ == 0);
+ return true;
+ }
+
+ virtual int Close() {
+ if (s_ == INVALID_SOCKET)
+ return 0;
+
+ id_ = 0;
+ signal_close_ = false;
+ ss_->Remove(this);
+ return PhysicalSocket::Close();
+ }
+
+ virtual uint32 GetRequestedEvents() {
+ return enabled_events_;
+ }
+
+ virtual void OnPreEvent(uint32 ff) {
+ if ((ff & kfConnect) != 0)
+ state_ = CS_CONNECTED;
+ }
+
+ virtual void OnEvent(uint32 ff, int err) {
+ int cache_id = id_;
+ if ((ff & kfRead) != 0) {
+ enabled_events_ &= ~kfRead;
+ SignalReadEvent(this);
+ }
+ if (((ff & kfWrite) != 0) && (id_ == cache_id)) {
+ enabled_events_ &= ~kfWrite;
+ SignalWriteEvent(this);
+ }
+ if (((ff & kfConnect) != 0) && (id_ == cache_id)) {
+ enabled_events_ &= ~kfConnect;
+ SignalConnectEvent(this);
+ }
+ if (((ff & kfClose) != 0) && (id_ == cache_id)) {
+ //LOG(INFO) << "SOCK[" << static_cast<int>(s_) << "] OnClose() Error: " << err;
+ signal_close_ = true;
+ signal_err_ = err;
+ }
+ }
+
+ virtual WSAEVENT GetWSAEvent() {
+ return WSA_INVALID_EVENT;
+ }
+
+ virtual SOCKET GetSocket() {
+ return s_;
+ }
+
+ virtual bool CheckSignalClose() {
+ if (!signal_close_)
+ return false;
+
+ char ch;
+ if (recv(s_, &ch, 1, MSG_PEEK) > 0)
+ return false;
+
+ signal_close_ = false;
+ SignalCloseEvent(this, signal_err_);
+ return true;
+ }
+};
+
+int SocketDispatcher::next_id_ = 0;
+
+#endif // WIN32
+
+// Sets the value of a boolean value to false when signaled.
+class Signaler : public EventDispatcher {
+public:
+ Signaler(PhysicalSocketServer* ss, bool* pf)
+ : EventDispatcher(ss), pf_(pf) {
+ }
+ virtual ~Signaler() { }
+
+ void OnEvent(uint32 ff, int err) {
+ if (pf_)
+ *pf_ = false;
+ }
+
+private:
+ bool *pf_;
+};
+
+PhysicalSocketServer::PhysicalSocketServer() : fWait_(false),
+ last_tick_tracked_(0), last_tick_dispatch_count_(0) {
+ signal_wakeup_ = new Signaler(this, &fWait_);
+}
+
+PhysicalSocketServer::~PhysicalSocketServer() {
+ delete signal_wakeup_;
+}
+
+void PhysicalSocketServer::WakeUp() {
+ signal_wakeup_->Signal();
+}
+
+Socket* PhysicalSocketServer::CreateSocket(int type) {
+ PhysicalSocket* socket = new PhysicalSocket(this);
+ if (socket->Create(type)) {
+ return socket;
+ } else {
+ delete socket;
+ return 0;
+ }
+}
+
+AsyncSocket* PhysicalSocketServer::CreateAsyncSocket(int type) {
+ SocketDispatcher* dispatcher = new SocketDispatcher(this);
+ if (dispatcher->Create(type)) {
+ return dispatcher;
+ } else {
+ delete dispatcher;
+ return 0;
+ }
+}
+
+AsyncSocket* PhysicalSocketServer::WrapSocket(SOCKET s) {
+ SocketDispatcher* dispatcher = new SocketDispatcher(s, this);
+ if (dispatcher->Initialize()) {
+ return dispatcher;
+ } else {
+ delete dispatcher;
+ return 0;
+ }
+}
+
+void PhysicalSocketServer::Add(Dispatcher *pdispatcher) {
+ CritScope cs(&crit_);
+ dispatchers_.push_back(pdispatcher);
+}
+
+void PhysicalSocketServer::Remove(Dispatcher *pdispatcher) {
+ CritScope cs(&crit_);
+ dispatchers_.erase(std::remove(dispatchers_.begin(), dispatchers_.end(), pdispatcher), dispatchers_.end());
+}
+
+#ifdef POSIX
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io) {
+ // Calculate timing information
+
+ struct timeval *ptvWait = NULL;
+ struct timeval tvWait;
+ struct timeval tvStop;
+ if (cmsWait != -1) {
+ // Calculate wait timeval
+ tvWait.tv_sec = cmsWait / 1000;
+ tvWait.tv_usec = (cmsWait % 1000) * 1000;
+ ptvWait = &tvWait;
+
+ // Calculate when to return in a timeval
+ gettimeofday(&tvStop, NULL);
+ tvStop.tv_sec += tvWait.tv_sec;
+ tvStop.tv_usec += tvWait.tv_usec;
+ if (tvStop.tv_usec >= 1000000) {
+ tvStop.tv_usec -= 1000000;
+ tvStop.tv_sec += 1;
+ }
+ }
+
+ // Zero all fd_sets. Don't need to do this inside the loop since
+ // select() zeros the descriptors not signaled
+
+ fd_set fdsRead;
+ FD_ZERO(&fdsRead);
+ fd_set fdsWrite;
+ FD_ZERO(&fdsWrite);
+
+ fWait_ = true;
+
+ while (fWait_) {
+ int fdmax = -1;
+ {
+ CritScope cr(&crit_);
+ for (unsigned i = 0; i < dispatchers_.size(); i++) {
+ // Query dispatchers for read and write wait state
+
+ Dispatcher *pdispatcher = dispatchers_[i];
+ assert(pdispatcher);
+ if (!process_io && (pdispatcher != signal_wakeup_))
+ continue;
+ int fd = pdispatcher->GetDescriptor();
+ if (fd > fdmax)
+ fdmax = fd;
+ uint32 ff = pdispatcher->GetRequestedEvents();
+ if (ff & kfRead)
+ FD_SET(fd, &fdsRead);
+ if (ff & (kfWrite | kfConnect))
+ FD_SET(fd, &fdsWrite);
+ }
+ }
+
+ // Wait then call handlers as appropriate
+ // < 0 means error
+ // 0 means timeout
+ // > 0 means count of descriptors ready
+ int n = select(fdmax + 1, &fdsRead, &fdsWrite, NULL, ptvWait);
+
+ // If error, return error
+ // todo: do something intelligent
+
+ if (n < 0)
+ return false;
+
+ // If timeout, return success
+
+ if (n == 0)
+ return true;
+
+ // We have signaled descriptors
+
+ {
+ CritScope cr(&crit_);
+ for (unsigned i = 0; i < dispatchers_.size(); i++) {
+ Dispatcher *pdispatcher = dispatchers_[i];
+ int fd = pdispatcher->GetDescriptor();
+ uint32 ff = 0;
+ if (FD_ISSET(fd, &fdsRead)) {
+ FD_CLR(fd, &fdsRead);
+ ff |= kfRead;
+ }
+ if (FD_ISSET(fd, &fdsWrite)) {
+ FD_CLR(fd, &fdsWrite);
+ if (pdispatcher->GetRequestedEvents() & kfConnect) {
+ ff |= kfConnect;
+ } else {
+ ff |= kfWrite;
+ }
+ }
+ if (ff != 0) {
+ pdispatcher->OnPreEvent(ff);
+ pdispatcher->OnEvent(ff, 0);
+ }
+ }
+ }
+
+ // Recalc the time remaining to wait. Doing it here means it doesn't get
+ // calced twice the first time through the loop
+
+ if (cmsWait != -1) {
+ ptvWait->tv_sec = 0;
+ ptvWait->tv_usec = 0;
+ struct timeval tvT;
+ gettimeofday(&tvT, NULL);
+ if (tvStop.tv_sec >= tvT.tv_sec) {
+ ptvWait->tv_sec = tvStop.tv_sec - tvT.tv_sec;
+ ptvWait->tv_usec = tvStop.tv_usec - tvT.tv_usec;
+ if (ptvWait->tv_usec < 0) {
+ ptvWait->tv_usec += 1000000;
+ ptvWait->tv_sec -= 1;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+#endif // POSIX
+
+#ifdef WIN32
+bool PhysicalSocketServer::Wait(int cmsWait, bool process_io)
+{
+ int cmsTotal = cmsWait;
+ int cmsElapsed = 0;
+ uint32 msStart = GetMillisecondCount();
+
+#if LOGGING
+ if (last_tick_dispatch_count_ == 0) {
+ last_tick_tracked_ = msStart;
+ }
+#endif
+
+ WSAEVENT socket_ev = WSACreateEvent();
+
+ fWait_ = true;
+ while (fWait_) {
+ std::vector<WSAEVENT> events;
+ std::vector<Dispatcher *> event_owners;
+
+ events.push_back(socket_ev);
+
+ {
+ CritScope cr(&crit_);
+ for (size_t i = 0; i < dispatchers_.size(); ++i) {
+ Dispatcher * disp = dispatchers_[i];
+ if (!process_io && (disp != signal_wakeup_))
+ continue;
+ SOCKET s = disp->GetSocket();
+ if (disp->CheckSignalClose()) {
+ // We just signalled close, don't poll this socket
+ } else if (s != INVALID_SOCKET) {
+ WSAEventSelect(s, events[0], FlagsToEvents(disp->GetRequestedEvents()));
+ } else {
+ events.push_back(disp->GetWSAEvent());
+ event_owners.push_back(disp);
+ }
+ }
+ }
+
+ // Which is shorter, the delay wait or the asked wait?
+
+ int cmsNext;
+ if (cmsWait == -1) {
+ cmsNext = cmsWait;
+ } else {
+ cmsNext = cmsTotal - cmsElapsed;
+ if (cmsNext < 0)
+ cmsNext = 0;
+ }
+
+ // Wait for one of the events to signal
+ DWORD dw = WSAWaitForMultipleEvents(static_cast<DWORD>(events.size()), &events[0], false, cmsNext, false);
+
+#if 0 // LOGGING
+ // we track this information purely for logging purposes.
+ last_tick_dispatch_count_++;
+ if (last_tick_dispatch_count_ >= 1000) {
+ uint32 now = GetMillisecondCount();
+ LOG(INFO) << "PhysicalSocketServer took " << TimeDiff(now, last_tick_tracked_) << "ms for 1000 events";
+
+ // If we get more than 1000 events in a second, we are spinning badly
+ // (normally it should take about 8-20 seconds).
+ assert(TimeDiff(now, last_tick_tracked_) > 1000);
+
+ last_tick_tracked_ = now;
+ last_tick_dispatch_count_ = 0;
+ }
+#endif
+
+ // Failed?
+ // todo: need a better strategy than this!
+
+ if (dw == WSA_WAIT_FAILED) {
+ int error = WSAGetLastError();
+ assert(false);
+ WSACloseEvent(socket_ev);
+ return false;
+ }
+
+ // Timeout?
+
+ if (dw == WSA_WAIT_TIMEOUT) {
+ WSACloseEvent(socket_ev);
+ return true;
+ }
+
+ // Figure out which one it is and call it
+
+ {
+ CritScope cr(&crit_);
+ int index = dw - WSA_WAIT_EVENT_0;
+ if (index > 0) {
+ --index; // The first event is the socket event
+ event_owners[index]->OnPreEvent(0);
+ event_owners[index]->OnEvent(0, 0);
+ } else if (process_io) {
+ for (size_t i = 0; i < dispatchers_.size(); ++i) {
+ Dispatcher * disp = dispatchers_[i];
+ SOCKET s = disp->GetSocket();
+ if (s == INVALID_SOCKET)
+ continue;
+
+ WSANETWORKEVENTS wsaEvents;
+ int err = WSAEnumNetworkEvents(s, events[0], &wsaEvents);
+ if (err == 0) {
+
+#if LOGGING
+ {
+ if ((wsaEvents.lNetworkEvents & FD_READ) && wsaEvents.iErrorCode[FD_READ_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_READ_BIT error " << wsaEvents.iErrorCode[FD_READ_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_WRITE) && wsaEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_WRITE_BIT error " << wsaEvents.iErrorCode[FD_WRITE_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_CONNECT) && wsaEvents.iErrorCode[FD_CONNECT_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_CONNECT_BIT error " << wsaEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_ACCEPT) && wsaEvents.iErrorCode[FD_ACCEPT_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_ACCEPT_BIT error " << wsaEvents.iErrorCode[FD_ACCEPT_BIT];
+ }
+ if ((wsaEvents.lNetworkEvents & FD_CLOSE) && wsaEvents.iErrorCode[FD_CLOSE_BIT] != 0) {
+ LOG(WARNING) << "PhysicalSocketServer got FD_CLOSE_BIT error " << wsaEvents.iErrorCode[FD_CLOSE_BIT];
+ }
+ }
+#endif
+ uint32 ff = 0;
+ int errcode = 0;
+ if (wsaEvents.lNetworkEvents & FD_READ)
+ ff |= kfRead;
+ if (wsaEvents.lNetworkEvents & FD_WRITE)
+ ff |= kfWrite;
+ if (wsaEvents.lNetworkEvents & FD_CONNECT) {
+ if (wsaEvents.iErrorCode[FD_CONNECT_BIT] == 0) {
+ ff |= kfConnect;
+ } else {
+ // TODO: Decide whether we want to signal connect, but with an error code
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+ }
+ if (wsaEvents.lNetworkEvents & FD_ACCEPT)
+ ff |= kfRead;
+ if (wsaEvents.lNetworkEvents & FD_CLOSE) {
+ ff |= kfClose;
+ errcode = wsaEvents.iErrorCode[FD_CLOSE_BIT];
+ }
+ if (ff != 0) {
+ disp->OnPreEvent(ff);
+ disp->OnEvent(ff, errcode);
+ }
+ }
+ }
+ }
+
+ // Reset the network event until new activity occurs
+ WSAResetEvent(socket_ev);
+ }
+
+ // Break?
+
+ if (!fWait_)
+ break;
+ cmsElapsed = GetMillisecondCount() - msStart;
+ if (cmsWait != -1) {
+ if (cmsElapsed >= cmsWait)
+ break;
+ }
+ }
+
+ // Done
+
+ WSACloseEvent(socket_ev);
+ return true;
+}
+#endif // WIN32
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h
new file mode 100644
index 00000000..305b64d9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/physicalsocketserver.h
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PHYSICALSOCKETSERVER_H__
+#define __PHYSICALSOCKETSERVER_H__
+
+#include "talk/base/asyncfile.h"
+#include "talk/base/socketserver.h"
+#include "talk/base/criticalsection.h"
+#include <vector>
+
+#ifdef POSIX
+typedef int SOCKET;
+#endif // POSIX
+
+namespace cricket {
+
+class Dispatcher;
+class Signaler;
+
+// A socket server that provides the real sockets of the underlying OS.
+class PhysicalSocketServer : public SocketServer {
+public:
+ PhysicalSocketServer();
+ virtual ~PhysicalSocketServer();
+
+ // SocketFactory:
+ virtual Socket* CreateSocket(int type);
+ virtual AsyncSocket* CreateAsyncSocket(int type);
+
+ // Internal Factory for Accept
+ AsyncSocket* WrapSocket(SOCKET s);
+
+ // SocketServer:
+ virtual bool Wait(int cms, bool process_io);
+ virtual void WakeUp();
+
+ void Add(Dispatcher* dispatcher);
+ void Remove(Dispatcher* dispatcher);
+
+#ifdef POSIX
+ AsyncFile* CreateFile(int fd);
+#endif
+
+private:
+ std::vector<Dispatcher*> dispatchers_;
+ Signaler* signal_wakeup_;
+ CriticalSection crit_;
+ bool fWait_;
+ uint32 last_tick_tracked_;
+ int last_tick_dispatch_count_;
+};
+
+} // namespace cricket
+
+#endif // __PHYSICALSOCKETSERVER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h
new file mode 100644
index 00000000..1bd817b9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/proxyinfo.h
@@ -0,0 +1,52 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PROXYINFO_H__
+#define __PROXYINFO_H__
+
+#include <string>
+#include "talk/base/socketaddress.h"
+// TODO: move xmpppassword into base
+#include "talk/xmpp/xmpppassword.h"
+
+namespace cricket {
+
+enum ProxyType { PROXY_NONE, PROXY_HTTPS, PROXY_SOCKS5, PROXY_UNKNOWN };
+const char * ProxyToString(ProxyType proxy);
+
+struct ProxyInfo {
+ ProxyType type;
+ SocketAddress address;
+ std::string username;
+ buzz::XmppPassword password;
+
+ ProxyInfo() : type(PROXY_NONE) { }
+};
+
+} // namespace cricket
+
+#endif // __PROXYINFO_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h
new file mode 100644
index 00000000..0470ff83
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/scoped_ptr.h
@@ -0,0 +1,259 @@
+#ifndef SCOPED_PTR_H
+#define SCOPED_PTR_H
+
+// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
+// Copyright (c) 2001, 2002 Peter Dimov
+//
+// Permission to copy, use, modify, sell and distribute this software
+// is granted provided this copyright notice appears in all copies.
+// This software is provided "as is" without express or implied
+// warranty, and with no claim as to its suitability for any purpose.
+//
+// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
+//
+
+// scoped_ptr mimics a built-in pointer except that it guarantees deletion
+// of the object pointed to, either on destruction of the scoped_ptr or via
+// an explicit reset(). scoped_ptr is a simple solution for simple needs;
+// use shared_ptr or std::auto_ptr if your needs are more complex.
+
+// scoped_ptr_malloc added in by Google. When one of
+// these goes out of scope, instead of doing a delete or delete[], it
+// calls free(). scoped_ptr_malloc<char> is likely to see much more
+// use than any other specializations.
+
+// release() added in by Google. Use this to conditionally
+// transfer ownership of a heap-allocated object to the caller, usually on
+// method success.
+
+#include <cstddef> // for std::ptrdiff_t
+#include <assert.h> // for assert
+#include <stdlib.h> // for free() decl
+
+#ifdef _WIN32
+namespace std { using ::ptrdiff_t; };
+#endif // _WIN32
+
+namespace buzz {
+
+template <typename T>
+class scoped_ptr {
+ private:
+
+ T* ptr;
+
+ scoped_ptr(scoped_ptr const &);
+ scoped_ptr & operator=(scoped_ptr const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_ptr(T* p = 0): ptr(p) {}
+
+ ~scoped_ptr() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete ptr;
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ delete ptr;
+ ptr = p;
+ }
+ }
+
+ T& operator*() const {
+ assert(ptr != 0);
+ return *ptr;
+ }
+
+ T* operator->() const {
+ assert(ptr != 0);
+ return ptr;
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_ptr & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete ptr;
+ ptr = 0;
+ }
+ return &ptr;
+ }
+
+ T** use() {
+ return &ptr;
+ }
+};
+
+template<typename T> inline
+void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
+ a.swap(b);
+}
+
+
+
+
+// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
+// is guaranteed, either on destruction of the scoped_array or via an explicit
+// reset(). Use shared_array or std::vector if your needs are more complex.
+
+template<typename T>
+class scoped_array {
+ private:
+
+ T* ptr;
+
+ scoped_array(scoped_array const &);
+ scoped_array & operator=(scoped_array const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_array(T* p = 0) : ptr(p) {}
+
+ ~scoped_array() {
+ typedef char type_must_be_complete[sizeof(T)];
+ delete[] ptr;
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ delete [] ptr;
+ ptr = p;
+ }
+ }
+
+ T& operator[](std::ptrdiff_t i) const {
+ assert(ptr != 0);
+ assert(i >= 0);
+ return ptr[i];
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_array & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ delete [] ptr;
+ ptr = 0;
+ }
+ return &ptr;
+ }
+};
+
+template<class T> inline
+void swap(scoped_array<T>& a, scoped_array<T>& b) {
+ a.swap(b);
+}
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the function used to free the object.
+
+template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc {
+ private:
+
+ T* ptr;
+
+ scoped_ptr_malloc(scoped_ptr_malloc const &);
+ scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
+
+ public:
+
+ typedef T element_type;
+
+ explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
+
+ ~scoped_ptr_malloc() {
+ typedef char type_must_be_complete[sizeof(T)];
+ FF(static_cast<void*>(ptr));
+ }
+
+ void reset(T* p = 0) {
+ typedef char type_must_be_complete[sizeof(T)];
+
+ if (ptr != p) {
+ FF(static_cast<void*>(ptr));
+ ptr = p;
+ }
+ }
+
+ T& operator*() const {
+ assert(ptr != 0);
+ return *ptr;
+ }
+
+ T* operator->() const {
+ assert(ptr != 0);
+ return ptr;
+ }
+
+ T* get() const {
+ return ptr;
+ }
+
+ void swap(scoped_ptr_malloc & b) {
+ T* tmp = b.ptr;
+ b.ptr = ptr;
+ ptr = tmp;
+ }
+
+ T* release() {
+ T* tmp = ptr;
+ ptr = 0;
+ return tmp;
+ }
+
+ T** accept() {
+ if (ptr) {
+ FF(static_cast<void*>(ptr));
+ ptr = 0;
+ }
+ return &ptr;
+ }
+};
+
+template<typename T, void (*FF)(void*)> inline
+void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) {
+ a.swap(b);
+}
+
+}
+
+using buzz::scoped_ptr;
+
+#endif // #ifndef SCOPED_PTR_H
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h
new file mode 100644
index 00000000..446516b8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/sigslot.h
@@ -0,0 +1,2700 @@
+// sigslot.h: Signal/Slot classes
+//
+// Written by Sarah Thompson (sarah@telergy.com) 2002.
+//
+// License: Public domain. You are free to use this code however you like, with the proviso that
+// the author takes on no responsibility or liability for any use.
+//
+// QUICK DOCUMENTATION
+//
+// (see also the full documentation at http://sigslot.sourceforge.net/)
+//
+// #define switches
+// SIGSLOT_PURE_ISO - Define this to force ISO C++ compliance. This also disables
+// all of the thread safety support on platforms where it is
+// available.
+//
+// SIGSLOT_USE_POSIX_THREADS - Force use of Posix threads when using a C++ compiler other than
+// gcc on a platform that supports Posix threads. (When using gcc,
+// this is the default - use SIGSLOT_PURE_ISO to disable this if
+// necessary)
+//
+// SIGSLOT_DEFAULT_MT_POLICY - Where thread support is enabled, this defaults to multi_threaded_global.
+// Otherwise, the default is single_threaded. #define this yourself to
+// override the default. In pure ISO mode, anything other than
+// single_threaded will cause a compiler error.
+//
+// PLATFORM NOTES
+//
+// Win32 - On Win32, the WIN32 symbol must be #defined. Most mainstream
+// compilers do this by default, but you may need to define it
+// yourself if your build environment is less standard. This causes
+// the Win32 thread support to be compiled in and used automatically.
+//
+// Unix/Linux/BSD, etc. - If you're using gcc, it is assumed that you have Posix threads
+// available, so they are used automatically. You can override this
+// (as under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
+// something other than gcc but still want to use Posix threads, you
+// need to #define SIGSLOT_USE_POSIX_THREADS.
+//
+// ISO C++ - If none of the supported platforms are detected, or if
+// SIGSLOT_PURE_ISO is defined, all multithreading support is turned off,
+// along with any code that might cause a pure ISO C++ environment to
+// complain. Before you ask, gcc -ansi -pedantic won't compile this
+// library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
+// errors that aren't really there. If you feel like investigating this,
+// please contact the author.
+//
+//
+// THREADING MODES
+//
+// single_threaded - Your program is assumed to be single threaded from the point of view
+// of signal/slot usage (i.e. all objects using signals and slots are
+// created and destroyed from a single thread). Behaviour if objects are
+// destroyed concurrently is undefined (i.e. you'll get the occasional
+// segmentation fault/memory exception).
+//
+// multi_threaded_global - Your program is assumed to be multi threaded. Objects using signals and
+// slots can be safely created and destroyed from any thread, even when
+// connections exist. In multi_threaded_global mode, this is achieved by a
+// single global mutex (actually a critical section on Windows because they
+// are faster). This option uses less OS resources, but results in more
+// opportunities for contention, possibly resulting in more context switches
+// than are strictly necessary.
+//
+// multi_threaded_local - Behaviour in this mode is essentially the same as multi_threaded_global,
+// except that each signal, and each object that inherits has_slots, all
+// have their own mutex/critical section. In practice, this means that
+// mutex collisions (and hence context switches) only happen if they are
+// absolutely essential. However, on some platforms, creating a lot of
+// mutexes can slow down the whole OS, so use this option with care.
+//
+// USING THE LIBRARY
+//
+// See the full documentation at http://sigslot.sourceforge.net/
+//
+//
+
+#ifndef SIGSLOT_H__
+#define SIGSLOT_H__
+
+#include <set>
+#include <list>
+
+// On our copy of sigslot.h, we force single threading
+#define SIGSLOT_PURE_ISO
+
+#if defined(SIGSLOT_PURE_ISO) || (!defined(WIN32) && !defined(__GNUG__) && !defined(SIGSLOT_USE_POSIX_THREADS))
+# define _SIGSLOT_SINGLE_THREADED
+#elif defined(WIN32)
+# define _SIGSLOT_HAS_WIN32_THREADS
+# include <windows.h>
+#elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
+# define _SIGSLOT_HAS_POSIX_THREADS
+# include <pthread.h>
+#else
+# define _SIGSLOT_SINGLE_THREADED
+#endif
+
+#ifndef SIGSLOT_DEFAULT_MT_POLICY
+# ifdef _SIGSLOT_SINGLE_THREADED
+# define SIGSLOT_DEFAULT_MT_POLICY single_threaded
+# else
+# define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
+# endif
+#endif
+
+
+namespace sigslot {
+
+ class single_threaded
+ {
+ public:
+ single_threaded()
+ {
+ ;
+ }
+
+ virtual ~single_threaded()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ ;
+ }
+
+ virtual void unlock()
+ {
+ ;
+ }
+ };
+
+#ifdef _SIGSLOT_HAS_WIN32_THREADS
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ static bool isinitialised = false;
+
+ if(!isinitialised)
+ {
+ InitializeCriticalSection(get_critsec());
+ isinitialised = true;
+ }
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(get_critsec());
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(get_critsec());
+ }
+
+ private:
+ CRITICAL_SECTION* get_critsec()
+ {
+ static CRITICAL_SECTION g_critsec;
+ return &g_critsec;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ InitializeCriticalSection(&m_critsec);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ DeleteCriticalSection(&m_critsec);
+ }
+
+ virtual void lock()
+ {
+ EnterCriticalSection(&m_critsec);
+ }
+
+ virtual void unlock()
+ {
+ LeaveCriticalSection(&m_critsec);
+ }
+
+ private:
+ CRITICAL_SECTION m_critsec;
+ };
+#endif // _SIGSLOT_HAS_WIN32_THREADS
+
+#ifdef _SIGSLOT_HAS_POSIX_THREADS
+ // The multi threading policies only get compiled in if they are enabled.
+ class multi_threaded_global
+ {
+ public:
+ multi_threaded_global()
+ {
+ pthread_mutex_init(get_mutex(), NULL);
+ }
+
+ multi_threaded_global(const multi_threaded_global&)
+ {
+ ;
+ }
+
+ virtual ~multi_threaded_global()
+ {
+ ;
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(get_mutex());
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(get_mutex());
+ }
+
+ private:
+ pthread_mutex_t* get_mutex()
+ {
+ static pthread_mutex_t g_mutex;
+ return &g_mutex;
+ }
+ };
+
+ class multi_threaded_local
+ {
+ public:
+ multi_threaded_local()
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ multi_threaded_local(const multi_threaded_local&)
+ {
+ pthread_mutex_init(&m_mutex, NULL);
+ }
+
+ virtual ~multi_threaded_local()
+ {
+ pthread_mutex_destroy(&m_mutex);
+ }
+
+ virtual void lock()
+ {
+ pthread_mutex_lock(&m_mutex);
+ }
+
+ virtual void unlock()
+ {
+ pthread_mutex_unlock(&m_mutex);
+ }
+
+ private:
+ pthread_mutex_t m_mutex;
+ };
+#endif // _SIGSLOT_HAS_POSIX_THREADS
+
+ template<class mt_policy>
+ class lock_block
+ {
+ public:
+ mt_policy *m_mutex;
+
+ lock_block(mt_policy *mtx)
+ : m_mutex(mtx)
+ {
+ m_mutex->lock();
+ }
+
+ ~lock_block()
+ {
+ m_mutex->unlock();
+ }
+ };
+
+ template<class mt_policy>
+ class has_slots;
+
+ template<class mt_policy>
+ class _connection_base0
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit() = 0;
+ virtual _connection_base0* clone() = 0;
+ virtual _connection_base0* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _connection_base1
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type) = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* clone() = 0;
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _connection_base2
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type) = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone() = 0;
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection_base3
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type) = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0;
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _connection_base4
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type) = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone() = 0;
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _connection_base5
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type) = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone() = 0;
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _connection_base6
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type) = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone() = 0;
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection_base7
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type) = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone() = 0;
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _connection_base8
+ {
+ public:
+ virtual has_slots<mt_policy>* getdest() const = 0;
+ virtual void emit(arg1_type, arg2_type, arg3_type, arg4_type, arg5_type,
+ arg6_type, arg7_type, arg8_type) = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone() = 0;
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
+ };
+
+ template<class mt_policy>
+ class _signal_base : public mt_policy
+ {
+ public:
+ virtual void slot_disconnect(has_slots<mt_policy>* pslot) = 0;
+ virtual void slot_duplicate(const has_slots<mt_policy>* poldslot, has_slots<mt_policy>* pnewslot) = 0;
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class has_slots : public mt_policy
+ {
+ private:
+ typedef typename std::set<_signal_base<mt_policy> *> sender_set;
+ typedef typename sender_set::const_iterator const_iterator;
+
+ public:
+ has_slots()
+ {
+ ;
+ }
+
+ has_slots(const has_slots& hs)
+ : mt_policy(hs)
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = hs.m_senders.begin();
+ const_iterator itEnd = hs.m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_duplicate(&hs, this);
+ m_senders.insert(*it);
+ ++it;
+ }
+ }
+
+ void signal_connect(_signal_base<mt_policy>* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.insert(sender);
+ }
+
+ void signal_disconnect(_signal_base<mt_policy>* sender)
+ {
+ lock_block<mt_policy> lock(this);
+ m_senders.erase(sender);
+ }
+
+ virtual ~has_slots()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ const_iterator it = m_senders.begin();
+ const_iterator itEnd = m_senders.end();
+
+ while(it != itEnd)
+ {
+ (*it)->slot_disconnect(this);
+ ++it;
+ }
+
+ m_senders.erase(m_senders.begin(), m_senders.end());
+ }
+
+ private:
+ sender_set m_senders;
+ };
+
+ template<class mt_policy>
+ class _signal_base0 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base0<mt_policy> *> connections_list;
+
+ _signal_base0()
+ {
+ ;
+ }
+
+ _signal_base0(const _signal_base0& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ ~_signal_base0()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class mt_policy>
+ class _signal_base1 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base1<arg1_type, mt_policy> *> connections_list;
+
+ _signal_base1()
+ {
+ ;
+ }
+
+ _signal_base1(const _signal_base1<arg1_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base1()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy>
+ class _signal_base2 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base2<arg1_type, arg2_type, mt_policy> *>
+ connections_list;
+
+ _signal_base2()
+ {
+ ;
+ }
+
+ _signal_base2(const _signal_base2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base2()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _signal_base3 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *>
+ connections_list;
+
+ _signal_base3()
+ {
+ ;
+ }
+
+ _signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base3()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy>
+ class _signal_base4 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy> *> connections_list;
+
+ _signal_base4()
+ {
+ ;
+ }
+
+ _signal_base4(const _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base4()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy>
+ class _signal_base5 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy> *> connections_list;
+
+ _signal_base5()
+ {
+ ;
+ }
+
+ _signal_base5(const _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base5()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy>
+ class _signal_base6 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy> *> connections_list;
+
+ _signal_base6()
+ {
+ ;
+ }
+
+ _signal_base6(const _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base6()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _signal_base7 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy> *> connections_list;
+
+ _signal_base7()
+ {
+ ;
+ }
+
+ _signal_base7(const _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base7()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy>
+ class _signal_base8 : public _signal_base<mt_policy>
+ {
+ public:
+ typedef std::list<_connection_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> *>
+ connections_list;
+
+ _signal_base8()
+ {
+ ;
+ }
+
+ _signal_base8(const _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base<mt_policy>(s)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = s.m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = s.m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_connect(this);
+ m_connected_slots.push_back((*it)->clone());
+
+ ++it;
+ }
+ }
+
+ void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == oldtarget)
+ {
+ m_connected_slots.push_back((*it)->duplicate(newtarget));
+ }
+
+ ++it;
+ }
+ }
+
+ ~_signal_base8()
+ {
+ disconnect_all();
+ }
+
+ void disconnect_all()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ (*it)->getdest()->signal_disconnect(this);
+ delete *it;
+
+ ++it;
+ }
+
+ m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
+ }
+
+#ifdef _DEBUG
+ bool connected(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+ if ((*it)->getdest() == pclass)
+ return true;
+ it = itNext;
+ }
+ return false;
+ }
+#endif
+
+ void disconnect(has_slots<mt_policy>* pclass)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ if((*it)->getdest() == pclass)
+ {
+ delete *it;
+ m_connected_slots.erase(it);
+ pclass->signal_disconnect(this);
+ return;
+ }
+
+ ++it;
+ }
+ }
+
+ void slot_disconnect(has_slots<mt_policy>* pslot)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::iterator it = m_connected_slots.begin();
+ typename connections_list::iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ typename connections_list::iterator itNext = it;
+ ++itNext;
+
+ if((*it)->getdest() == pslot)
+ {
+ m_connected_slots.erase(it);
+ // delete *it;
+ }
+
+ it = itNext;
+ }
+ }
+
+ protected:
+ connections_list m_connected_slots;
+ };
+
+
+ template<class dest_type, class mt_policy>
+ class _connection0 : public _connection_base0<mt_policy>
+ {
+ public:
+ _connection0()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection0(dest_type* pobject, void (dest_type::*pmemfun)())
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base0<mt_policy>* clone()
+ {
+ return new _connection0<dest_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base0<mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection0<dest_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit()
+ {
+ (m_pobject->*m_pmemfun)();
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)();
+ };
+
+ template<class dest_type, class arg1_type, class mt_policy>
+ class _connection1 : public _connection_base1<arg1_type, mt_policy>
+ {
+ public:
+ _connection1()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection1(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* clone()
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base1<arg1_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection1<dest_type, arg1_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1)
+ {
+ (m_pobject->*m_pmemfun)(a1);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class mt_policy>
+ class _connection2 : public _connection_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ _connection2()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection2(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* clone()
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base2<arg1_type, arg2_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection2<dest_type, arg1_type, arg2_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy>
+ class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ _connection3()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone()
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class mt_policy>
+ class _connection4 : public _connection_base4<arg1_type, arg2_type,
+ arg3_type, arg4_type, mt_policy>
+ {
+ public:
+ _connection4()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection4(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* clone()
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection4<dest_type, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3,
+ arg4_type a4)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type,
+ arg4_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class mt_policy>
+ class _connection5 : public _connection_base5<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ _connection5()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection5(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* clone()
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection5<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class mt_policy>
+ class _connection6 : public _connection_base6<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ _connection6()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection6(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* clone()
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection6<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type, class mt_policy>
+ class _connection7 : public _connection_base7<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ _connection7()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection7(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, arg7_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* clone()
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection7<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type);
+ };
+
+ template<class dest_type, class arg1_type, class arg2_type, class arg3_type,
+ class arg4_type, class arg5_type, class arg6_type, class arg7_type,
+ class arg8_type, class mt_policy>
+ class _connection8 : public _connection_base8<arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ _connection8()
+ {
+ m_pobject = NULL;
+ m_pmemfun = NULL;
+ }
+
+ _connection8(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ m_pobject = pobject;
+ m_pmemfun = pmemfun;
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* clone()
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(*this);
+ }
+
+ virtual _connection_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
+ {
+ return new _connection8<dest_type, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
+ }
+
+ virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ (m_pobject->*m_pmemfun)(a1, a2, a3, a4, a5, a6, a7, a8);
+ }
+
+ virtual has_slots<mt_policy>* getdest() const
+ {
+ return m_pobject;
+ }
+
+ private:
+ dest_type* m_pobject;
+ void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type);
+ };
+
+ template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal0 : public _signal_base0<mt_policy>
+ {
+ public:
+ typedef _signal_base0<mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal0()
+ {
+ ;
+ }
+
+ signal0(const signal0<mt_policy>& s)
+ : _signal_base0<mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)())
+ {
+ lock_block<mt_policy> lock(this);
+ _connection0<desttype, mt_policy>* conn =
+ new _connection0<desttype, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+
+ void operator()()
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit();
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal1 : public _signal_base1<arg1_type, mt_policy>
+ {
+ public:
+ typedef _signal_base1<arg1_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal1()
+ {
+ ;
+ }
+
+ signal1(const signal1<arg1_type, mt_policy>& s)
+ : _signal_base1<arg1_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection1<desttype, arg1_type, mt_policy>* conn =
+ new _connection1<desttype, arg1_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal2 : public _signal_base2<arg1_type, arg2_type, mt_policy>
+ {
+ public:
+ typedef _signal_base2<arg1_type, arg2_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal2()
+ {
+ ;
+ }
+
+ signal2(const signal2<arg1_type, arg2_type, mt_policy>& s)
+ : _signal_base2<arg1_type, arg2_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>* conn = new
+ _connection2<desttype, arg1_type, arg2_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>
+ {
+ public:
+ typedef _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal3()
+ {
+ ;
+ }
+
+ signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
+ : _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn =
+ new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass,
+ pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal4 : public _signal_base4<arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>
+ {
+ public:
+ typedef _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal4()
+ {
+ ;
+ }
+
+ signal4(const signal4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>& s)
+ : _signal_base4<arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection4<desttype, arg1_type, arg2_type, arg3_type, arg4_type, mt_policy>*
+ conn = new _connection4<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal5 : public _signal_base5<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, mt_policy>
+ {
+ public:
+ typedef _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal5()
+ {
+ ;
+ }
+
+ signal5(const signal5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>& s)
+ : _signal_base5<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection5<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, mt_policy>* conn = new _connection5<desttype, arg1_type, arg2_type,
+ arg3_type, arg4_type, arg5_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5);
+
+ it = itNext;
+ }
+ }
+ };
+
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal6 : public _signal_base6<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>
+ {
+ public:
+ typedef _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type, arg5_type, arg6_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal6()
+ {
+ ;
+ }
+
+ signal6(const signal6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>& s)
+ : _signal_base6<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection6<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, mt_policy>* conn =
+ new _connection6<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal7 : public _signal_base7<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>
+ {
+ public:
+ typedef _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal7()
+ {
+ ;
+ }
+
+ signal7(const signal7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>& s)
+ : _signal_base7<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection7<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, mt_policy>* conn =
+ new _connection7<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7);
+
+ it = itNext;
+ }
+ }
+ };
+
+ template<class arg1_type, class arg2_type, class arg3_type, class arg4_type,
+ class arg5_type, class arg6_type, class arg7_type, class arg8_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
+ class signal8 : public _signal_base8<arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>
+ {
+ public:
+ typedef _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy> base;
+ typedef typename base::connections_list connections_list;
+ using base::m_connected_slots;
+
+ signal8()
+ {
+ ;
+ }
+
+ signal8(const signal8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>& s)
+ : _signal_base8<arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>(s)
+ {
+ ;
+ }
+
+ template<class desttype>
+ void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
+ arg2_type, arg3_type, arg4_type, arg5_type, arg6_type,
+ arg7_type, arg8_type))
+ {
+ lock_block<mt_policy> lock(this);
+ _connection8<desttype, arg1_type, arg2_type, arg3_type, arg4_type,
+ arg5_type, arg6_type, arg7_type, arg8_type, mt_policy>* conn =
+ new _connection8<desttype, arg1_type, arg2_type, arg3_type,
+ arg4_type, arg5_type, arg6_type, arg7_type,
+ arg8_type, mt_policy>(pclass, pmemfun);
+ m_connected_slots.push_back(conn);
+ pclass->signal_connect(this);
+ }
+
+ void emit(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+
+ void operator()(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4,
+ arg5_type a5, arg6_type a6, arg7_type a7, arg8_type a8)
+ {
+ lock_block<mt_policy> lock(this);
+ typename connections_list::const_iterator itNext, it = m_connected_slots.begin();
+ typename connections_list::const_iterator itEnd = m_connected_slots.end();
+
+ while(it != itEnd)
+ {
+ itNext = it;
+ ++itNext;
+
+ (*it)->emit(a1, a2, a3, a4, a5, a6, a7, a8);
+
+ it = itNext;
+ }
+ }
+ };
+
+}; // namespace sigslot
+
+#endif // SIGSLOT_H__
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h
new file mode 100644
index 00000000..d4a49d96
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socket.h
@@ -0,0 +1,158 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _socket_h_
+#define _socket_h_
+
+#include "talk/base/basictypes.h"
+#include "talk/base/socketaddress.h"
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <errno.h>
+#endif
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+// Rather than converting errors into a private namespace,
+// Reuse the POSIX socket api errors. Note this depends on
+// Win32 compatibility.
+
+#ifdef WIN32
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define EINPROGRESS WSAEINPROGRESS
+#define EALREADY WSAEALREADY
+#define ENOTSOCK WSAENOTSOCK
+#define EDESTADDRREQ WSAEDESTADDRREQ
+#define EMSGSIZE WSAEMSGSIZE
+#define EPROTOTYPE WSAEPROTOTYPE
+#define ENOPROTOOPT WSAENOPROTOOPT
+#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
+#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
+#define EOPNOTSUPP WSAEOPNOTSUPP
+#define EPFNOSUPPORT WSAEPFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#define EADDRINUSE WSAEADDRINUSE
+#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define ENETDOWN WSAENETDOWN
+#define ENETUNREACH WSAENETUNREACH
+#define ENETRESET WSAENETRESET
+#define ECONNABORTED WSAECONNABORTED
+#define ECONNRESET WSAECONNRESET
+#define ENOBUFS WSAENOBUFS
+#define EISCONN WSAEISCONN
+#define ENOTCONN WSAENOTCONN
+#define ESHUTDOWN WSAESHUTDOWN
+#define ETOOMANYREFS WSAETOOMANYREFS
+#define ETIMEDOUT WSAETIMEDOUT
+#define ECONNREFUSED WSAECONNREFUSED
+#define ELOOP WSAELOOP
+#undef ENAMETOOLONG // remove errno.h's definition
+#define ENAMETOOLONG WSAENAMETOOLONG
+#define EHOSTDOWN WSAEHOSTDOWN
+#define EHOSTUNREACH WSAEHOSTUNREACH
+#undef ENOTEMPTY // remove errno.h's definition
+#define ENOTEMPTY WSAENOTEMPTY
+#define EPROCLIM WSAEPROCLIM
+#define EUSERS WSAEUSERS
+#define EDQUOT WSAEDQUOT
+#define ESTALE WSAESTALE
+#define EREMOTE WSAEREMOTE
+#undef EACCES
+#define EACCES WSAEACCES
+#endif // WIN32
+
+#ifdef POSIX
+#define INVALID_SOCKET (-1)
+#define SOCKET_ERROR (-1)
+#define closesocket(s) close(s)
+#endif // POSIX
+
+namespace cricket {
+
+inline bool IsBlockingError(int e) {
+ return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
+}
+
+// General interface for the socket implementations of various networks. The
+// methods match those of normal UNIX sockets very closely.
+class Socket {
+public:
+ virtual ~Socket() {}
+
+ // Returns the address to which the socket is bound. If the socket is not
+ // bound, then the any-address is returned.
+ virtual SocketAddress GetLocalAddress() const = 0;
+
+ // Returns the address to which the socket is connected. If the socket is
+ // not connected, then the any-address is returned.
+ virtual SocketAddress GetRemoteAddress() const = 0;
+
+ virtual int Bind(const SocketAddress& addr) = 0;
+ virtual int Connect(const SocketAddress& addr) = 0;
+ virtual int Send(const void *pv, size_t cb) = 0;
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr) = 0;
+ virtual int Recv(void *pv, size_t cb) = 0;
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr) = 0;
+ virtual int Listen(int backlog) = 0;
+ virtual Socket *Accept(SocketAddress *paddr) = 0;
+ virtual int Close() = 0;
+ virtual int GetError() const = 0;
+ virtual void SetError(int error) = 0;
+ inline bool IsBlocking() const { return IsBlockingError(GetError()); }
+
+ enum ConnState {
+ CS_CLOSED,
+ CS_CONNECTING,
+ CS_CONNECTED
+ };
+ virtual ConnState GetState() const = 0;
+
+ // Fills in the given uint16 with the current estimate of the MTU along the
+ // path to the address to which this socket is connected.
+ virtual int EstimateMTU(uint16* mtu) = 0;
+
+ enum Option {
+ OPT_DONTFRAGMENT
+ };
+ virtual int SetOption(Option opt, int value) = 0;
+
+protected:
+ Socket() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(Socket);
+};
+
+} // namespace cricket
+
+#endif // _socket_h_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc
new file mode 100644
index 00000000..049e923c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.cc
@@ -0,0 +1,1130 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+
+#include <time.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#include <wininet.h> // HTTP_STATUS_PROXY_AUTH_REQ
+#define SECURITY_WIN32
+#include <security.h>
+#endif
+
+#include <cassert>
+
+#include "talk/base/base64.h"
+#include "talk/base/basicdefs.h"
+#include "talk/base/bytebuffer.h"
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/md5.h"
+#include "talk/base/socketadapters.h"
+#include "talk/base/stringutils.h"
+
+#include <errno.h>
+
+
+#ifdef WIN32
+#include "talk/base/sec_buffer.h"
+#endif // WIN32
+
+namespace cricket {
+
+#ifdef WIN32
+extern const ConstantLabel SECURITY_ERRORS[];
+#endif
+
+BufferedReadAdapter::BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size)
+ : AsyncSocketAdapter(socket), buffer_size_(buffer_size), data_len_(0), buffering_(false) {
+ buffer_ = new char[buffer_size_];
+}
+
+BufferedReadAdapter::~BufferedReadAdapter() {
+ delete [] buffer_;
+}
+
+int BufferedReadAdapter::Send(const void *pv, size_t cb) {
+ if (buffering_) {
+ // TODO: Spoof error better; Signal Writeable
+ socket_->SetError(EWOULDBLOCK);
+ return -1;
+ }
+ return AsyncSocketAdapter::Send(pv, cb);
+}
+
+int BufferedReadAdapter::Recv(void *pv, size_t cb) {
+ if (buffering_) {
+ socket_->SetError(EWOULDBLOCK);
+ return -1;
+ }
+
+ size_t read = 0;
+
+ if (data_len_) {
+ read = _min(cb, data_len_);
+ memcpy(pv, buffer_, read);
+ data_len_ -= read;
+ if (data_len_ > 0) {
+ memmove(buffer_, buffer_ + read, data_len_);
+ }
+ pv = static_cast<char *>(pv) + read;
+ cb -= read;
+ }
+
+ // FIX: If cb == 0, we won't generate another read event
+
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res < 0)
+ return res;
+
+ return res + static_cast<int>(read);
+}
+
+void BufferedReadAdapter::BufferInput(bool on) {
+ buffering_ = on;
+}
+
+void BufferedReadAdapter::OnReadEvent(AsyncSocket * socket) {
+ assert(socket == socket_);
+
+ if (!buffering_) {
+ AsyncSocketAdapter::OnReadEvent(socket);
+ return;
+ }
+
+ if (data_len_ >= buffer_size_) {
+ LOG(INFO) << "Input buffer overflow";
+ assert(false);
+ data_len_ = 0;
+ }
+
+ int len = socket_->Recv(buffer_ + data_len_, buffer_size_ - data_len_);
+ if (len < 0) {
+ // TODO: Do something better like forwarding the error to the user.
+ LOG(INFO) << "Recv: " << errno << " " << std::strerror(errno);
+ return;
+ }
+
+ data_len_ += len;
+
+ ProcessInput(buffer_, data_len_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8 SSL_SERVER_HELLO[] = {
+ 22,3,1,0,74,2,0,0,70,3,1,66,133,69,167,39,169,93,160,
+ 179,197,231,83,218,72,43,63,198,90,202,137,193,88,82,
+ 161,120,60,91,23,70,0,133,63,32,14,211,6,114,91,91,
+ 27,95,21,172,19,249,136,83,157,155,232,61,123,12,48,
+ 50,110,56,77,162,117,87,65,108,52,92,0,4,0
+};
+
+const char SSL_CLIENT_HELLO[] = {
+ -128,70,1,3,1,0,45,0,0,0,16,1,0,-128,3,0,-128,7,0,-64,6,0,64,2,0,
+ -128,4,0,-128,0,0,4,0,-2,-1,0,0,10,0,-2,-2,0,0,9,0,0,100,0,0,98,0,
+ 0,3,0,0,6,31,23,12,-90,47,0,120,-4,70,85,46,-79,-125,57,-15,-22
+};
+
+AsyncSSLSocket::AsyncSSLSocket(AsyncSocket* socket) : BufferedReadAdapter(socket, 1024) {
+}
+
+int AsyncSSLSocket::Connect(const SocketAddress& addr) {
+ // Begin buffering before we connect, so that there isn't a race condition between
+ // potential senders and receiving the OnConnectEvent signal
+ BufferInput(true);
+ return BufferedReadAdapter::Connect(addr);
+}
+
+void AsyncSSLSocket::OnConnectEvent(AsyncSocket * socket) {
+ assert(socket == socket_);
+
+ // TODO: we could buffer output too...
+ int res = DirectSend(SSL_CLIENT_HELLO, sizeof(SSL_CLIENT_HELLO));
+ assert(res == sizeof(SSL_CLIENT_HELLO));
+}
+
+void AsyncSSLSocket::ProcessInput(char * data, size_t& len) {
+ if (len < sizeof(SSL_SERVER_HELLO))
+ return;
+
+ if (memcmp(SSL_SERVER_HELLO, data, sizeof(SSL_SERVER_HELLO)) != 0) {
+ Close();
+ SignalCloseEvent(this, 0); // TODO: error code?
+ return;
+ }
+
+ len -= sizeof(SSL_SERVER_HELLO);
+ if (len > 0) {
+ memmove(data, data + sizeof(SSL_SERVER_HELLO), len);
+ }
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define TEST_DIGEST 0
+#if TEST_DIGEST
+/*
+const char * const DIGEST_CHALLENGE =
+ "Digest realm=\"testrealm@host.com\","
+ " qop=\"auth,auth-int\","
+ " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
+ " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
+const char * const DIGEST_METHOD = "GET";
+const char * const DIGEST_URI =
+ "/dir/index.html";;
+const char * const DIGEST_CNONCE =
+ "0a4f113b";
+const char * const DIGEST_RESPONSE =
+ "6629fae49393a05397450978507c4ef1";
+//user_ = "Mufasa";
+//pass_ = "Circle Of Life";
+*/
+const char * const DIGEST_CHALLENGE =
+ "Digest realm=\"Squid proxy-caching web server\","
+ " nonce=\"Nny4QuC5PwiSDixJ\","
+ " qop=\"auth\","
+ " stale=false";
+const char * const DIGEST_URI =
+ "/";
+const char * const DIGEST_CNONCE =
+ "6501d58e9a21cee1e7b5fec894ded024";
+const char * const DIGEST_RESPONSE =
+ "edffcb0829e755838b073a4a42de06bc";
+#endif
+
+static std::string MD5(const std::string& data) {
+ MD5_CTX ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, const_cast<unsigned char *>(reinterpret_cast<const unsigned char *>(data.data())), static_cast<unsigned int>(data.size()));
+ unsigned char digest[16];
+ MD5Final(digest, &ctx);
+ std::string hex_digest;
+ const char HEX[] = "0123456789abcdef";
+ for (int i=0; i<16; ++i) {
+ hex_digest += HEX[digest[i] >> 4];
+ hex_digest += HEX[digest[i] & 0xf];
+ }
+ return hex_digest;
+}
+
+static std::string Quote(const std::string& str) {
+ std::string result;
+ result.push_back('"');
+ for (size_t i=0; i<str.size(); ++i) {
+ if ((str[i] == '"') || (str[i] == '\\'))
+ result.push_back('\\');
+ result.push_back(str[i]);
+ }
+ result.push_back('"');
+ return result;
+}
+
+#ifdef WIN32
+struct NegotiateAuthContext : public AsyncHttpsProxySocket::AuthContext {
+ CredHandle cred;
+ CtxtHandle ctx;
+ size_t steps;
+ bool specified_credentials;
+
+ NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
+ : AuthContext(auth), cred(c1), ctx(c2), steps(0), specified_credentials(false) { }
+
+ virtual ~NegotiateAuthContext() {
+ DeleteSecurityContext(&ctx);
+ FreeCredentialsHandle(&cred);
+ }
+};
+#endif // WIN32
+
+AsyncHttpsProxySocket::AuthResult
+AsyncHttpsProxySocket::Authenticate(const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const buzz::XmppPassword& password,
+ AuthContext *& context, std::string& response, std::string& auth_method) {
+#if TEST_DIGEST
+ challenge = DIGEST_CHALLENGE;
+ len = strlen(challenge);
+#endif
+
+ std::map<std::string, std::string> args;
+ ParseAuth(challenge, len, auth_method, args);
+
+ if (context && (context->auth_method != auth_method))
+ return AR_IGNORE;
+
+ // BASIC
+ if (stricmp(auth_method.c_str(), "basic") == 0) {
+ if (context)
+ return AR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return AR_CREDENTIALS; // Missing credentials
+
+ context = new AuthContext(auth_method);
+
+ // TODO: convert sensitive to a secure buffer that gets securely deleted
+ //std::string decoded = username + ":" + password;
+ size_t len = username.size() + password.GetLength() + 2;
+ char * sensitive = new char[len];
+ size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ password.CopyTo(sensitive + pos, true);
+
+ response = auth_method;
+ response.append(" ");
+ // TODO: create a sensitive-source version of Base64::encode
+ response.append(Base64::encode(sensitive));
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ return AR_RESPONSE;
+ }
+
+ // DIGEST
+ if (stricmp(auth_method.c_str(), "digest") == 0) {
+ if (context)
+ return AR_CREDENTIALS; // Bad credentials
+ if (username.empty())
+ return AR_CREDENTIALS; // Missing credentials
+
+ context = new AuthContext(auth_method);
+
+ std::string cnonce, ncount;
+#if TEST_DIGEST
+ method = DIGEST_METHOD;
+ uri = DIGEST_URI;
+ cnonce = DIGEST_CNONCE;
+#else
+ char buffer[256];
+ sprintf(buffer, "%d", time(0));
+ cnonce = MD5(buffer);
+#endif
+ ncount = "00000001";
+
+ // TODO: convert sensitive to be secure buffer
+ //std::string A1 = username + ":" + args["realm"] + ":" + password;
+ size_t len = username.size() + args["realm"].size() + password.GetLength() + 3;
+ char * sensitive = new char[len]; // A1
+ size_t pos = strcpyn(sensitive, len, username.data(), username.size());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ pos += strcpyn(sensitive + pos, len - pos, args["realm"].c_str());
+ pos += strcpyn(sensitive + pos, len - pos, ":");
+ password.CopyTo(sensitive + pos, true);
+
+ std::string A2 = method + ":" + uri;
+ std::string middle;
+ if (args.find("qop") != args.end()) {
+ args["qop"] = "auth";
+ middle = args["nonce"] + ":" + ncount + ":" + cnonce + ":" + args["qop"];
+ } else {
+ middle = args["nonce"];
+ }
+ std::string HA1 = MD5(sensitive);
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ std::string HA2 = MD5(A2);
+ std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
+
+#if TEST_DIGEST
+ assert(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0);
+#endif
+
+ std::stringstream ss;
+ ss << auth_method;
+ ss << " username=" << Quote(username);
+ ss << ", realm=" << Quote(args["realm"]);
+ ss << ", nonce=" << Quote(args["nonce"]);
+ ss << ", uri=" << Quote(uri);
+ if (args.find("qop") != args.end()) {
+ ss << ", qop=" << args["qop"];
+ ss << ", nc=" << ncount;
+ ss << ", cnonce=" << Quote(cnonce);
+ }
+ ss << ", response=\"" << dig_response << "\"";
+ if (args.find("opaque") != args.end()) {
+ ss << ", opaque=" << Quote(args["opaque"]);
+ }
+ response = ss.str();
+ return AR_RESPONSE;
+ }
+
+#ifdef WIN32
+#if 1
+ bool want_negotiate = (stricmp(auth_method.c_str(), "negotiate") == 0);
+ bool want_ntlm = (stricmp(auth_method.c_str(), "ntlm") == 0);
+ // SPNEGO & NTLM
+ if (want_negotiate || want_ntlm) {
+ const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
+ char out_buf[MAX_MESSAGE], spn[MAX_SPN];
+
+#if 0 // Requires funky windows versions
+ DWORD len = MAX_SPN;
+ if (DsMakeSpn("HTTP", server.IPAsString().c_str(), NULL, server.port(), 0, &len, spn) != ERROR_SUCCESS) {
+ LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) - DsMakeSpn failed";
+ return AR_IGNORE;
+ }
+#else
+ sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
+#endif
+
+ SecBuffer out_sec;
+ out_sec.pvBuffer = out_buf;
+ out_sec.cbBuffer = sizeof(out_buf);
+ out_sec.BufferType = SECBUFFER_TOKEN;
+
+ SecBufferDesc out_buf_desc;
+ out_buf_desc.ulVersion = 0;
+ out_buf_desc.cBuffers = 1;
+ out_buf_desc.pBuffers = &out_sec;
+
+ const ULONG NEG_FLAGS_DEFAULT =
+ //ISC_REQ_ALLOCATE_MEMORY
+ ISC_REQ_CONFIDENTIALITY
+ //| ISC_REQ_EXTENDED_ERROR
+ //| ISC_REQ_INTEGRITY
+ | ISC_REQ_REPLAY_DETECT
+ | ISC_REQ_SEQUENCE_DETECT
+ //| ISC_REQ_STREAM
+ //| ISC_REQ_USE_SUPPLIED_CREDS
+ ;
+
+ TimeStamp lifetime;
+ SECURITY_STATUS ret = S_OK;
+ ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
+
+ bool specify_credentials = !username.empty();
+ size_t steps = 0;
+
+ //uint32 now = cricket::Time();
+
+ NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context);
+ if (neg) {
+ const size_t max_steps = 10;
+ if (++neg->steps >= max_steps) {
+ LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries";
+ return AR_ERROR;
+ }
+ steps = neg->steps;
+
+ std::string decoded_challenge = Base64::decode(args[""]);
+ if (!decoded_challenge.empty()) {
+ SecBuffer in_sec;
+ in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data());
+ in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size());
+ in_sec.BufferType = SECBUFFER_TOKEN;
+
+ SecBufferDesc in_buf_desc;
+ in_buf_desc.ulVersion = 0;
+ in_buf_desc.cBuffers = 1;
+ in_buf_desc.pBuffers = &in_sec;
+
+ ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
+ //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return AR_ERROR;
+ }
+ } else if (neg->specified_credentials) {
+ // Try again with default credentials
+ specify_credentials = false;
+ delete context;
+ context = neg = 0;
+ } else {
+ return AR_CREDENTIALS;
+ }
+ }
+
+ if (!neg) {
+ unsigned char userbuf[256], passbuf[256], domainbuf[16];
+ SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
+ if (specify_credentials) {
+ memset(&auth_id, 0, sizeof(auth_id));
+ size_t len = password.GetLength()+1;
+ char * sensitive = new char[len];
+ password.CopyTo(sensitive, true);
+ std::string::size_type pos = username.find('\\');
+ if (pos == std::string::npos) {
+ auth_id.UserLength = static_cast<unsigned long>(
+ _min(sizeof(userbuf) - 1, username.size()));
+ memcpy(userbuf, username.c_str(), auth_id.UserLength);
+ userbuf[auth_id.UserLength] = 0;
+ auth_id.DomainLength = 0;
+ domainbuf[auth_id.DomainLength] = 0;
+ auth_id.PasswordLength = static_cast<unsigned long>(
+ _min(sizeof(passbuf) - 1, password.GetLength()));
+ memcpy(passbuf, sensitive, auth_id.PasswordLength);
+ passbuf[auth_id.PasswordLength] = 0;
+ } else {
+ auth_id.UserLength = static_cast<unsigned long>(
+ _min(sizeof(userbuf) - 1, username.size() - pos - 1));
+ memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
+ userbuf[auth_id.UserLength] = 0;
+ auth_id.DomainLength = static_cast<unsigned long>(
+ _min(sizeof(domainbuf) - 1, pos));
+ memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
+ domainbuf[auth_id.DomainLength] = 0;
+ auth_id.PasswordLength = static_cast<unsigned long>(
+ _min(sizeof(passbuf) - 1, password.GetLength()));
+ memcpy(passbuf, sensitive, auth_id.PasswordLength);
+ passbuf[auth_id.PasswordLength] = 0;
+ }
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ auth_id.User = userbuf;
+ auth_id.Domain = domainbuf;
+ auth_id.Password = passbuf;
+ auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+ pauth_id = &auth_id;
+ LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials";
+ } else {
+ LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
+ }
+
+ CredHandle cred;
+ ret = AcquireCredentialsHandleA(0, want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A, SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
+ //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << cricket::TimeDiff(cricket::Time(), now);
+ if (ret != SEC_E_OK) {
+ LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ return AR_IGNORE;
+ }
+
+ //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
+
+ CtxtHandle ctx;
+ ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
+ //LOG(INFO) << "$$$ InitializeSecurityContext @ " << cricket::TimeDiff(cricket::Time(), now);
+ if (FAILED(ret)) {
+ LOG(LS_ERROR) << "InitializeSecurityContext returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ FreeCredentialsHandle(&cred);
+ return AR_IGNORE;
+ }
+
+ assert(!context);
+ context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
+ neg->specified_credentials = specify_credentials;
+ neg->steps = steps;
+ }
+
+ if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
+ ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
+ //LOG(INFO) << "$$$ CompleteAuthToken @ " << cricket::TimeDiff(cricket::Time(), now);
+ LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
+ << ErrorName(ret, SECURITY_ERRORS);
+ if (FAILED(ret)) {
+ return AR_ERROR;
+ }
+ }
+
+ //LOG(INFO) << "$$$ NEGOTIATE took " << cricket::TimeDiff(cricket::Time(), now) << "ms";
+
+ std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
+ response = auth_method;
+ response.append(" ");
+ response.append(Base64::encode(decoded));
+ return AR_RESPONSE;
+ }
+#endif
+#endif // WIN32
+
+ return AR_IGNORE;
+}
+
+inline bool end_of_name(size_t pos, size_t len, const char * data) {
+ if (pos >= len)
+ return true;
+ if (isspace(data[pos]))
+ return true;
+ // The reason for this complexity is that some non-compliant auth schemes (like Negotiate)
+ // use base64 tokens in the challenge instead of name=value. This could confuse us when the
+ // base64 ends in equal signs.
+ if ((pos+1 < len) && (data[pos] == '=') && !isspace(data[pos+1]) && (data[pos+1] != '='))
+ return true;
+ return false;
+}
+
+void AsyncHttpsProxySocket::ParseAuth(const char * data, size_t len, std::string& method, std::map<std::string,std::string>& args) {
+ size_t pos = 0;
+ while ((pos < len) && isspace(data[pos])) ++pos;
+ size_t start = pos;
+ while ((pos < len) && !isspace(data[pos])) ++pos;
+ method.assign(data + start, data + pos);
+ while (pos < len) {
+ while ((pos < len) && isspace(data[pos])) ++pos;
+ if (pos >= len)
+ return;
+
+ start = pos;
+ while (!end_of_name(pos, len, data)) ++pos;
+ //while ((pos < len) && !isspace(data[pos]) && (data[pos] != '=')) ++pos;
+ std::string name(data + start, data + pos), value;
+
+ if ((pos < len) && (data[pos] == '=')) {
+ ++pos; // Skip '='
+ // Check if quoted value
+ if ((pos < len) && (data[pos] == '"')) {
+ while (++pos < len) {
+ if (data[pos] == '"') {
+ ++pos;
+ break;
+ }
+ if ((data[pos] == '\\') && (pos + 1 < len))
+ ++pos;
+ value.append(1, data[pos]);
+ }
+ } else {
+ while ((pos < len) && !isspace(data[pos]) && (data[pos] != ','))
+ value.append(1, data[pos++]);
+ }
+ } else {
+ value = name;
+ name.clear();
+ }
+
+ args.insert(std::make_pair(name, value));
+ if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
+ }
+}
+
+AsyncHttpsProxySocket::AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const buzz::XmppPassword& password)
+ : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password),
+ state_(PS_ERROR), context_(0) {
+}
+
+AsyncHttpsProxySocket::~AsyncHttpsProxySocket() {
+ delete context_;
+}
+
+int AsyncHttpsProxySocket::Connect(const SocketAddress& addr) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::Connect(" << proxy_.ToString() << ")";
+ dest_ = addr;
+ if (dest_.port() != 80) {
+ BufferInput(true);
+ }
+ return BufferedReadAdapter::Connect(proxy_);
+}
+
+SocketAddress AsyncHttpsProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+int AsyncHttpsProxySocket::Close() {
+ headers_.clear();
+ state_ = PS_ERROR;
+ delete context_;
+ context_ = 0;
+ return BufferedReadAdapter::Close();
+}
+
+void AsyncHttpsProxySocket::OnConnectEvent(AsyncSocket * socket) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnConnectEvent";
+ // TODO: Decide whether tunneling or not should be explicitly set,
+ // or indicated by destination port (as below)
+ if (dest_.port() == 80) {
+ state_ = PS_TUNNEL;
+ BufferedReadAdapter::OnConnectEvent(socket);
+ return;
+ }
+ SendRequest();
+}
+
+void AsyncHttpsProxySocket::OnCloseEvent(AsyncSocket * socket, int err) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket::OnCloseEvent(" << err << ")";
+ if ((state_ == PS_WAIT_CLOSE) && (err == 0)) {
+ state_ = PS_ERROR;
+ Connect(dest_);
+ } else {
+ BufferedReadAdapter::OnCloseEvent(socket, err);
+ }
+}
+
+void AsyncHttpsProxySocket::ProcessInput(char * data, size_t& len) {
+ size_t start = 0;
+ for (size_t pos = start; (state_ < PS_TUNNEL) && (pos < len); ) {
+ if (state_ == PS_SKIP_BODY) {
+ size_t consume = _min(len - pos, content_length_);
+ pos += consume;
+ start = pos;
+ content_length_ -= consume;
+ if (content_length_ == 0) {
+ EndResponse();
+ }
+ continue;
+ }
+
+ if (data[pos++] != '\n')
+ continue;
+
+ size_t len = pos - start - 1;
+ if ((len > 0) && (data[start + len - 1] == '\r'))
+ --len;
+
+ data[start + len] = 0;
+ ProcessLine(data + start, len);
+ start = pos;
+ }
+
+ len -= start;
+ if (len > 0) {
+ memmove(data, data + start, len);
+ }
+
+ if (state_ != PS_TUNNEL)
+ return;
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this); // TODO: signal this??
+}
+
+void AsyncHttpsProxySocket::SendRequest() {
+ std::stringstream ss;
+ ss << "CONNECT " << dest_.ToString() << " HTTP/1.0\r\n";
+ ss << "User-Agent: " USERAGENT_STRING "\r\n";
+ ss << "Host: " << dest_.IPAsString() << "\r\n";
+ ss << "Content-Length: 0\r\n";
+ ss << "Proxy-Connection: Keep-Alive\r\n";
+ ss << headers_;
+ ss << "\r\n";
+ std::string str = ss.str();
+ DirectSend(str.c_str(), str.size());
+ state_ = PS_LEADER;
+ expect_close_ = true;
+ content_length_ = 0;
+ headers_.clear();
+
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket >> " << str;
+}
+
+void AsyncHttpsProxySocket::ProcessLine(char * data, size_t len) {
+ LOG(LS_VERBOSE) << "AsyncHttpsProxySocket << " << data;
+
+ if (len == 0) {
+ if (state_ == PS_TUNNEL_HEADERS) {
+ state_ = PS_TUNNEL;
+ } else if (state_ == PS_ERROR_HEADERS) {
+ Error(defer_error_);
+ return;
+ } else if (state_ == PS_SKIP_HEADERS) {
+ if (content_length_) {
+ state_ = PS_SKIP_BODY;
+ } else {
+ EndResponse();
+ return;
+ }
+ } else {
+ static bool report = false;
+ if (!unknown_mechanisms_.empty() && !report) {
+ report = true;
+ std::string msg(
+ "Unable to connect to the Google Talk service due to an incompatibility "
+ "with your proxy.\r\nPlease help us resolve this issue by submitting the "
+ "following information to us using our technical issue submission form "
+ "at:\r\n\r\n"
+ "http://www.google.com/support/talk/bin/request.py\r\n\r\n"
+ "We apologize for the inconvenience.\r\n\r\n"
+ "Information to submit to Google: "
+ );
+ //std::string msg("Please report the following information to foo@bar.com:\r\nUnknown methods: ");
+ msg.append(unknown_mechanisms_);
+#ifdef WIN32
+ MessageBoxA(0, msg.c_str(), "Oops!", MB_OK);
+#endif
+#ifdef POSIX
+ //TODO: Raise a signal or something so the UI can be separated.
+ LOG(LS_ERROR) << "Oops!\n\n" << msg;
+#endif
+ }
+ // Unexpected end of headers
+ Error(0);
+ return;
+ }
+ } else if (state_ == PS_LEADER) {
+ uint32 code;
+ if (sscanf(data, "HTTP/%*lu.%*lu %lu", &code) != 1) {
+ Error(0);
+ return;
+ }
+ switch (code) {
+ case 200:
+ // connection good!
+ state_ = PS_TUNNEL_HEADERS;
+ return;
+#if defined(HTTP_STATUS_PROXY_AUTH_REQ) && (HTTP_STATUS_PROXY_AUTH_REQ != 407)
+#error Wrong code for HTTP_STATUS_PROXY_AUTH_REQ
+#endif
+ case 407: // HTTP_STATUS_PROXY_AUTH_REQ
+ state_ = PS_AUTHENTICATE;
+ return;
+ default:
+ defer_error_ = 0;
+ state_ = PS_ERROR_HEADERS;
+ return;
+ }
+ } else if ((state_ == PS_AUTHENTICATE) && (strnicmp(data, "Proxy-Authenticate:", 19) == 0)) {
+ std::string response, auth_method;
+ switch (Authenticate(data + 19, len - 19, proxy_, "CONNECT", "/", user_, pass_, context_, response, auth_method)) {
+ case AR_IGNORE:
+ LOG(LS_VERBOSE) << "Ignoring Proxy-Authenticate: " << auth_method;
+ if (!unknown_mechanisms_.empty())
+ unknown_mechanisms_.append(", ");
+ unknown_mechanisms_.append(auth_method);
+ break;
+ case AR_RESPONSE:
+ headers_ = "Proxy-Authorization: ";
+ headers_.append(response);
+ headers_.append("\r\n");
+ state_ = PS_SKIP_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case AR_CREDENTIALS:
+ defer_error_ = EACCES;
+ state_ = PS_ERROR_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ case AR_ERROR:
+ defer_error_ = 0;
+ state_ = PS_ERROR_HEADERS;
+ unknown_mechanisms_.clear();
+ break;
+ }
+ } else if (strnicmp(data, "Content-Length:", 15) == 0) {
+ content_length_ = strtoul(data + 15, 0, 0);
+ } else if (strnicmp(data, "Proxy-Connection: Keep-Alive", 28) == 0) {
+ expect_close_ = false;
+ /*
+ } else if (strnicmp(data, "Connection: close", 17) == 0) {
+ expect_close_ = true;
+ */
+ }
+}
+
+void AsyncHttpsProxySocket::EndResponse() {
+ if (!expect_close_) {
+ SendRequest();
+ return;
+ }
+
+ // No point in waiting for the server to close... let's close now
+ // TODO: Refactor out PS_WAIT_CLOSE
+ state_ = PS_WAIT_CLOSE;
+ BufferedReadAdapter::Close();
+ OnCloseEvent(this, 0);
+}
+
+void AsyncHttpsProxySocket::Error(int error) {
+ BufferInput(false);
+ Close();
+ SetError(error);
+ SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AsyncSocksProxySocket::AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const buzz::XmppPassword& password)
+ : BufferedReadAdapter(socket, 1024), proxy_(proxy), user_(username), pass_(password),
+ state_(SS_ERROR) {
+}
+
+int AsyncSocksProxySocket::Connect(const SocketAddress& addr) {
+ dest_ = addr;
+ BufferInput(true);
+ return BufferedReadAdapter::Connect(proxy_);
+}
+
+SocketAddress AsyncSocksProxySocket::GetRemoteAddress() const {
+ return dest_;
+}
+
+void AsyncSocksProxySocket::OnConnectEvent(AsyncSocket * socket) {
+ SendHello();
+}
+
+void AsyncSocksProxySocket::ProcessInput(char * data, size_t& len) {
+ assert(state_ < SS_TUNNEL);
+
+ ByteBuffer response(data, len);
+
+ if (state_ == SS_HELLO) {
+ uint8 ver, method;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(method))
+ return;
+
+ if (ver != 5) {
+ Error(0);
+ return;
+ }
+
+ if (method == 0) {
+ SendConnect();
+ } else if (method == 2) {
+ SendAuth();
+ } else {
+ Error(0);
+ return;
+ }
+ } else if (state_ == SS_AUTH) {
+ uint8 ver, status;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(status))
+ return;
+
+ if ((ver != 1) || (status != 0)) {
+ Error(EACCES);
+ return;
+ }
+
+ SendConnect();
+ } else if (state_ == SS_CONNECT) {
+ uint8 ver, rep, rsv, atyp;
+ if (!response.ReadUInt8(ver) ||
+ !response.ReadUInt8(rep) ||
+ !response.ReadUInt8(rsv) ||
+ !response.ReadUInt8(atyp))
+ return;
+
+ if ((ver != 5) || (rep != 0)) {
+ Error(0);
+ return;
+ }
+
+ uint16 port;
+ if (atyp == 1) {
+ uint32 addr;
+ if (!response.ReadUInt32(addr) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+ } else if (atyp == 3) {
+ uint8 len;
+ std::string addr;
+ if (!response.ReadUInt8(len) ||
+ !response.ReadString(addr, len) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on " << addr << ":" << port;
+ } else if (atyp == 4) {
+ std::string addr;
+ if (!response.ReadString(addr, 16) ||
+ !response.ReadUInt16(port))
+ return;
+ LOG(LS_VERBOSE) << "Bound on <IPV6>:" << port;
+ } else {
+ Error(0);
+ return;
+ }
+
+ state_ = SS_TUNNEL;
+ }
+
+ // Consume parsed data
+ len = response.Length();
+ memcpy(data, response.Data(), len);
+
+ if (state_ != SS_TUNNEL)
+ return;
+
+ bool remainder = (len > 0);
+ BufferInput(false);
+ SignalConnectEvent(this);
+
+ // FIX: if SignalConnect causes the socket to be destroyed, we are in trouble
+ if (remainder)
+ SignalReadEvent(this); // TODO: signal this??
+}
+
+void AsyncSocksProxySocket::SendHello() {
+ ByteBuffer request;
+ request.WriteUInt8(5); // Socks Version
+ if (user_.empty()) {
+ request.WriteUInt8(1); // Authentication Mechanisms
+ request.WriteUInt8(0); // No authentication
+ } else {
+ request.WriteUInt8(2); // Authentication Mechanisms
+ request.WriteUInt8(0); // No authentication
+ request.WriteUInt8(2); // Username/Password
+ }
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_HELLO;
+}
+
+void AsyncSocksProxySocket::SendAuth() {
+ ByteBuffer request;
+ request.WriteUInt8(1); // Negotiation Version
+ request.WriteUInt8(static_cast<uint8>(user_.size()));
+ request.WriteString(user_); // Username
+ request.WriteUInt8(static_cast<uint8>(pass_.GetLength()));
+ size_t len = pass_.GetLength() + 1;
+ char * sensitive = new char[len];
+ pass_.CopyTo(sensitive, true);
+ request.WriteString(sensitive); // Password
+ memset(sensitive, 0, len);
+ delete [] sensitive;
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_AUTH;
+}
+
+void AsyncSocksProxySocket::SendConnect() {
+ ByteBuffer request;
+ request.WriteUInt8(5); // Socks Version
+ request.WriteUInt8(1); // CONNECT
+ request.WriteUInt8(0); // Reserved
+ if (dest_.IsUnresolved()) {
+ std::string hostname = dest_.IPAsString();
+ request.WriteUInt8(3); // DOMAINNAME
+ request.WriteUInt8(static_cast<uint8>(hostname.size()));
+ request.WriteString(hostname); // Destination Hostname
+ } else {
+ request.WriteUInt8(1); // IPV4
+ request.WriteUInt32(dest_.ip()); // Destination IP
+ }
+ request.WriteUInt16(dest_.port()); // Destination Port
+ DirectSend(request.Data(), request.Length());
+ state_ = SS_CONNECT;
+}
+
+void AsyncSocksProxySocket::Error(int error) {
+ state_ = SS_ERROR;
+ BufferInput(false);
+ Close();
+ SetError(EACCES);
+ SignalCloseEvent(this, error);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+LoggingAdapter::LoggingAdapter(AsyncSocket* socket, LoggingSeverity level,
+ const char * label)
+ : AsyncSocketAdapter(socket), level_(level)
+{
+ label_.append("[");
+ label_.append(label);
+ label_.append("]");
+}
+
+int
+LoggingAdapter::Send(const void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Send(pv, cb);
+ if (res > 0)
+ LogMultiline(false, static_cast<const char *>(pv), res);
+ return res;
+}
+
+int
+LoggingAdapter::SendTo(const void *pv, size_t cb, const SocketAddress& addr) {
+ int res = AsyncSocketAdapter::SendTo(pv, cb, addr);
+ if (res > 0)
+ LogMultiline(false, static_cast<const char *>(pv), res);
+ return res;
+}
+
+int
+LoggingAdapter::Recv(void *pv, size_t cb) {
+ int res = AsyncSocketAdapter::Recv(pv, cb);
+ if (res > 0)
+ LogMultiline(true, static_cast<const char *>(pv), res);
+ return res;
+}
+
+int
+LoggingAdapter::RecvFrom(void *pv, size_t cb, SocketAddress *paddr) {
+ int res = AsyncSocketAdapter::RecvFrom(pv, cb, paddr);
+ if (res > 0)
+ LogMultiline(true, static_cast<const char *>(pv), res);
+ return res;
+}
+
+void
+LoggingAdapter::OnConnectEvent(AsyncSocket * socket) {
+ LOG(level_) << label_ << " Connected";
+ AsyncSocketAdapter::OnConnectEvent(socket);
+}
+
+void
+LoggingAdapter::OnCloseEvent(AsyncSocket * socket, int err) {
+ LOG(level_) << label_ << " Closed with error: " << err;
+ AsyncSocketAdapter::OnCloseEvent(socket, err);
+}
+
+void
+LoggingAdapter::LogMultiline(bool input, const char * data, size_t len) {
+ const char * direction = (input ? " << " : " >> ");
+ std::string str(data, len);
+ while (!str.empty()) {
+ std::string::size_type pos = str.find('\n');
+ std::string substr = str;
+ if (pos == std::string::npos) {
+ substr = str;
+ str.clear();
+ } else if ((pos > 0) && (str[pos-1] == '\r')) {
+ substr = str.substr(0, pos - 1);
+ str = str.substr(pos + 1);
+ } else {
+ substr = str.substr(0, pos);
+ str = str.substr(pos + 1);
+ }
+
+ // Filter out any private data
+ std::string::size_type pos_private = substr.find("Email");
+ if (pos_private == std::string::npos) {
+ pos_private = substr.find("Passwd");
+ }
+ if (pos_private == std::string::npos) {
+ LOG(level_) << label_ << direction << substr;
+ } else {
+ LOG(level_) << label_ << direction << "## TEXT REMOVED ##";
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h
new file mode 100644
index 00000000..1c65aa79
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketadapters.h
@@ -0,0 +1,181 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKETADAPTERS_H__
+#define __SOCKETADAPTERS_H__
+
+#include <map>
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/logging.h"
+#include "talk/xmpp/xmpppassword.h" // TODO: move xmpppassword to base
+
+namespace cricket {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class BufferedReadAdapter : public AsyncSocketAdapter {
+public:
+ BufferedReadAdapter(AsyncSocket* socket, size_t buffer_size);
+ virtual ~BufferedReadAdapter();
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int Recv(void *pv, size_t cb);
+
+protected:
+ int DirectSend(const void *pv, size_t cb) { return AsyncSocketAdapter::Send(pv, cb); }
+
+ void BufferInput(bool on = true);
+ virtual void ProcessInput(char * data, size_t& len) = 0;
+
+ virtual void OnReadEvent(AsyncSocket * socket);
+
+private:
+ char * buffer_;
+ size_t buffer_size_, data_len_;
+ bool buffering_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSSLSocket : public BufferedReadAdapter {
+public:
+ AsyncSSLSocket(AsyncSocket* socket);
+
+ virtual int Connect(const SocketAddress& addr);
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void ProcessInput(char * data, size_t& len);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncHttpsProxySocket : public BufferedReadAdapter {
+public:
+ AsyncHttpsProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const buzz::XmppPassword& password);
+ virtual ~AsyncHttpsProxySocket();
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+ virtual int Close();
+
+ struct AuthContext {
+ std::string auth_method;
+ AuthContext(const std::string& auth) : auth_method(auth) { }
+ virtual ~AuthContext() { }
+ };
+
+ // 'context' is used by this function to record information between calls.
+ // Start by passing a null pointer, then pass the same pointer each additional
+ // call. When the authentication attempt is finished, delete the context.
+ enum AuthResult { AR_RESPONSE, AR_IGNORE, AR_CREDENTIALS, AR_ERROR };
+ static AuthResult Authenticate(const char * challenge, size_t len,
+ const SocketAddress& server,
+ const std::string& method, const std::string& uri,
+ const std::string& username, const buzz::XmppPassword& password,
+ AuthContext *& context, std::string& response, std::string& auth_method);
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void OnCloseEvent(AsyncSocket * socket, int err);
+ virtual void ProcessInput(char * data, size_t& len);
+
+ void SendRequest();
+ void ProcessLine(char * data, size_t len);
+ void EndResponse();
+ void Error(int error);
+
+ static void ParseAuth(const char * data, size_t len, std::string& method, std::map<std::string,std::string>& args);
+
+private:
+ SocketAddress proxy_, dest_;
+ std::string user_, headers_;
+ buzz::XmppPassword pass_;
+ size_t content_length_;
+ int defer_error_;
+ bool expect_close_;
+ enum ProxyState { PS_LEADER, PS_AUTHENTICATE, PS_SKIP_HEADERS, PS_ERROR_HEADERS, PS_TUNNEL_HEADERS, PS_SKIP_BODY, PS_TUNNEL, PS_WAIT_CLOSE, PS_ERROR } state_;
+ AuthContext * context_;
+ std::string unknown_mechanisms_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AsyncSocksProxySocket : public BufferedReadAdapter {
+public:
+ AsyncSocksProxySocket(AsyncSocket* socket, const SocketAddress& proxy,
+ const std::string& username, const buzz::XmppPassword& password);
+
+ virtual int Connect(const SocketAddress& addr);
+ virtual SocketAddress GetRemoteAddress() const;
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void ProcessInput(char * data, size_t& len);
+
+ void SendHello();
+ void SendConnect();
+ void SendAuth();
+ void Error(int error);
+
+private:
+ SocketAddress proxy_, dest_;
+ std::string user_;
+ buzz::XmppPassword pass_;
+ enum SocksState { SS_HELLO, SS_AUTH, SS_CONNECT, SS_TUNNEL, SS_ERROR } state_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class LoggingAdapter : public AsyncSocketAdapter {
+public:
+ LoggingAdapter(AsyncSocket* socket, LoggingSeverity level,
+ const char * label);
+
+ virtual int Send(const void *pv, size_t cb);
+ virtual int SendTo(const void *pv, size_t cb, const SocketAddress& addr);
+ virtual int Recv(void *pv, size_t cb);
+ virtual int RecvFrom(void *pv, size_t cb, SocketAddress *paddr);
+
+protected:
+ virtual void OnConnectEvent(AsyncSocket * socket);
+ virtual void OnCloseEvent(AsyncSocket * socket, int err);
+
+private:
+ void LogMultiline(bool input, const char * data, size_t len);
+
+ LoggingSeverity level_;
+ std::string label_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // namespace cricket
+
+#endif // __SOCKETADAPTERS_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc
new file mode 100644
index 00000000..f0228fbd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.cc
@@ -0,0 +1,267 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/socketaddress.h"
+#include "talk/base/byteorder.h"
+#include "talk/base/logging.h"
+#include <cstring>
+#include <sstream>
+#include <cassert>
+
+#ifdef WIN32
+#undef SetPort
+int inet_aton(const char * cp, struct in_addr * inp) {
+ inp->s_addr = inet_addr(cp);
+ return (inp->s_addr == INADDR_NONE) ? 0 : 1;
+}
+#endif // WIN32
+
+#ifdef POSIX
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#ifdef _DEBUG
+#define DISABLE_DNS 0
+#else // !_DEBUG
+#define DISABLE_DNS 0
+#endif // !_DEBUG
+
+namespace cricket {
+
+SocketAddress::SocketAddress() {
+ Zero();
+}
+
+SocketAddress::SocketAddress(const std::string& hostname, int port, bool use_dns) {
+ Zero();
+ SetIP(hostname, use_dns);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(uint32 ip, int port) {
+ Zero();
+ SetIP(ip);
+ SetPort(port);
+}
+
+SocketAddress::SocketAddress(const SocketAddress& addr) {
+ Zero();
+ this->operator=(addr);
+}
+
+void SocketAddress::Zero() {
+ ip_ = 0;
+ port_ = 0;
+}
+
+SocketAddress& SocketAddress::operator =(const SocketAddress& addr) {
+ hostname_ = addr.hostname_;
+ ip_ = addr.ip_;
+ port_ = addr.port_;
+ return *this;
+}
+
+void SocketAddress::SetIP(uint32 ip) {
+ hostname_.clear();
+ ip_ = ip;
+}
+
+bool SocketAddress::SetIP(const std::string& hostname, bool use_dns) {
+ hostname_ = hostname;
+ ip_ = 0;
+ return Resolve(true, use_dns);
+}
+
+void SocketAddress::SetResolvedIP(uint32 ip) {
+ ip_ = ip;
+}
+
+void SocketAddress::SetPort(int port) {
+ assert((0 <= port) && (port < 65536));
+ port_ = port;
+}
+
+uint32 SocketAddress::ip() const {
+ return ip_;
+}
+
+uint16 SocketAddress::port() const {
+ return port_;
+}
+
+std::string SocketAddress::IPAsString() const {
+ if (!hostname_.empty())
+ return hostname_;
+ return IPToString(ip_);
+}
+
+std::string SocketAddress::PortAsString() const {
+ std::ostringstream ost;
+ ost << port_;
+ return ost.str();
+}
+
+std::string SocketAddress::ToString() const {
+ std::ostringstream ost;
+ ost << IPAsString();
+ ost << ":";
+ ost << port();
+ return ost.str();
+}
+
+bool SocketAddress::IsAny() const {
+ return (ip_ == 0);
+}
+
+bool SocketAddress::IsLocalIP() const {
+ return (ip_ >> 24) == 127;
+}
+
+bool SocketAddress::IsPrivateIP() const {
+ return ((ip_ >> 24) == 127) ||
+ ((ip_ >> 24) == 10) ||
+ ((ip_ >> 20) == ((172 << 4) | 1)) ||
+ ((ip_ >> 16) == ((192 << 8) | 168));
+}
+
+bool SocketAddress::IsUnresolved() const {
+ return IsAny() && !hostname_.empty();
+}
+
+bool SocketAddress::Resolve(bool force, bool use_dns) {
+ if (hostname_.empty()) {
+ // nothing to resolve
+ } else if (!force && !IsAny()) {
+ // already resolved
+ } else if (uint32 ip = StringToIP(hostname_, use_dns)) {
+ ip_ = ip;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+bool SocketAddress::operator ==(const SocketAddress& addr) const {
+ return EqualIPs(addr) && EqualPorts(addr);
+}
+
+bool SocketAddress::operator <(const SocketAddress& addr) const {
+ if (ip_ < addr.ip_)
+ return true;
+ else if (addr.ip_ < ip_)
+ return false;
+
+ // We only check hostnames if both IPs are zero. This matches EqualIPs()
+ if (addr.ip_ == 0) {
+ if (hostname_ < addr.hostname_)
+ return true;
+ else if (addr.hostname_ < hostname_)
+ return false;
+ }
+
+ return port_ < addr.port_;
+}
+
+bool SocketAddress::EqualIPs(const SocketAddress& addr) const {
+ return (ip_ == addr.ip_) && ((ip_ != 0) || (hostname_ == addr.hostname_));
+}
+
+bool SocketAddress::EqualPorts(const SocketAddress& addr) const {
+ return (port_ == addr.port_);
+}
+
+size_t SocketAddress::Hash() const {
+ size_t h = 0;
+ h ^= ip_;
+ h ^= port_ | (port_ << 16);
+ return h;
+}
+
+size_t SocketAddress::Size_() const {
+ return sizeof(ip_) + sizeof(port_);
+}
+
+void SocketAddress::Write_(char* buf, int len) const {
+ // TODO: Depending on how this is used, we may want/need to write hostname
+ assert((size_t)len >= Size_());
+ reinterpret_cast<uint32*>(buf)[0] = ip_;
+ buf += sizeof(ip_);
+ reinterpret_cast<uint16*>(buf)[0] = port_;
+}
+
+void SocketAddress::Read_(const char* buf, int len) {
+ assert((size_t)len >= Size_());
+ ip_ = reinterpret_cast<const uint32*>(buf)[0];
+ buf += sizeof(ip_);
+ port_ = reinterpret_cast<const uint16*>(buf)[0];
+}
+
+std::string SocketAddress::IPToString(uint32 ip) {
+ std::ostringstream ost;
+ ost << ((ip >> 24) & 0xff);
+ ost << '.';
+ ost << ((ip >> 16) & 0xff);
+ ost << '.';
+ ost << ((ip >> 8) & 0xff);
+ ost << '.';
+ ost << ((ip >> 0) & 0xff);
+ return ost.str();
+}
+
+uint32 SocketAddress::StringToIP(const std::string& hostname, bool use_dns) {
+ uint32 ip = 0;
+ in_addr addr;
+ if (inet_aton(hostname.c_str(), &addr) != 0) {
+ ip = NetworkToHost32(addr.s_addr);
+ } else if (use_dns) {
+ // Note: this is here so we can spot spurious DNS resolutions for a while
+ LOG(INFO) << "=== DNS RESOLUTION (" << hostname << ") ===";
+#if DISABLE_DNS
+ LOG(WARNING) << "*** DNS DISABLED ***";
+#if WIN32
+ WSASetLastError(WSAHOST_NOT_FOUND);
+#endif // WIN32
+#endif // DISABLE_DNS
+ if (hostent * pHost = gethostbyname(hostname.c_str())) {
+ ip = NetworkToHost32(*reinterpret_cast<uint32 *>(pHost->h_addr_list[0]));
+ } else {
+#if WIN32
+ LOG(LS_ERROR) << "gethostbyname error: " << WSAGetLastError();
+#else
+ LOG(LS_ERROR) << "gethostbyname error: " << strerror(h_errno);
+#endif
+ }
+ LOG(INFO) << hostname << " resolved to " << IPToString(ip);
+ }
+ return ip;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h
new file mode 100644
index 00000000..b8a165d3
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddress.h
@@ -0,0 +1,154 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKETADDRESS_H__
+#define __SOCKETADDRESS_H__
+
+#include "talk/base/basictypes.h"
+#include <string>
+#undef SetPort
+
+namespace cricket {
+
+// Records an IP address and port, which are 32 and 16 bit integers,
+// respectively, both in <b>host byte-order</b>.
+class SocketAddress {
+public:
+ // Creates a missing / unknown address.
+ SocketAddress();
+
+ // Creates the address with the given host and port. If use_dns is true,
+ // the hostname will be immediately resolved to an IP (which may block for
+ // several seconds if DNS is not available). Alternately, set use_dns to
+ // false, and then call Resolve() to complete resolution later, or use
+ // SetResolvedIP to set the IP explictly.
+ SocketAddress(const std::string& hostname, int port = 0, bool use_dns = true);
+
+ // Creates the address with the given IP and port.
+ SocketAddress(uint32 ip, int port);
+
+ // Creates a copy of the given address.
+ SocketAddress(const SocketAddress& addr);
+
+ // Replaces our address with the given one.
+ SocketAddress& operator =(const SocketAddress& addr);
+
+ // Changes the IP of this address to the given one, and clears the hostname.
+ void SetIP(uint32 ip);
+
+ // Changes the hostname of this address to the given one.
+ // Calls Resolve and returns the result.
+ bool SetIP(const std::string& hostname, bool use_dns = true);
+
+ // Sets the IP address while retaining the hostname. Useful for bypassing
+ // DNS for a pre-resolved IP.
+ void SetResolvedIP(uint32 ip);
+
+ // Changes the port of this address to the given one.
+ void SetPort(int port);
+
+ // Returns the IP address.
+ uint32 ip() const;
+
+ // Returns the port part of this address.
+ uint16 port() const;
+
+ // Returns the IP address in dotted form.
+ std::string IPAsString() const;
+
+ // Returns the port as a string
+ std::string PortAsString() const;
+
+ // Returns a display version of the IP/port.
+ std::string ToString() const;
+
+ // Determines whether this represents a missing / any address.
+ bool IsAny() const;
+
+ // Synomym for missing / any.
+ bool IsNil() const { return IsAny(); }
+
+ // Determines whether the IP address refers to the local host, i.e. within
+ // the range 127.0.0.0/8.
+ bool IsLocalIP() const;
+
+ // Determines whether the IP address is in one of the private ranges:
+ // 127.0.0.0/8 10.0.0.0/8 192.168.0.0/16 172.16.0.0/12.
+ bool IsPrivateIP() const;
+
+ // Determines whether the hostname has been resolved to an IP
+ bool IsUnresolved() const;
+
+ // Attempt to resolve a hostname to IP address.
+ // Returns false if resolution is required but failed.
+ // 'force' will cause re-resolution of hostname.
+ //
+ bool Resolve(bool force = false, bool use_dns = true);
+
+ // Determines whether this address is identical to the given one.
+ bool operator ==(const SocketAddress& addr) const;
+
+ // Compares based on IP and then port.
+ bool operator <(const SocketAddress& addr) const;
+
+ // Determines whether this address has the same IP as the one given.
+ bool EqualIPs(const SocketAddress& addr) const;
+
+ // Deteremines whether this address has the same port as the one given.
+ bool EqualPorts(const SocketAddress& addr) const;
+
+ // Hashes this address into a small number.
+ size_t Hash() const;
+
+ // Returns the size of this address when written.
+ size_t Size_() const;
+
+ // Writes this address into the given buffer.
+ void Write_(char* buf, int len) const;
+
+ // Reads this address from the given buffer.
+ void Read_(const char* buf, int len);
+
+ // Converts the IP address given in compact form into dotted form.
+ static std::string IPToString(uint32 ip);
+
+ // Converts the IP address given in dotted form into compact form.
+ // Without 'use_dns', only dotted names (A.B.C.D) are resolved.
+ static uint32 StringToIP(const std::string& str, bool use_dns = true);
+
+private:
+ std::string hostname_;
+ uint32 ip_;
+ uint16 port_;
+
+ // Initializes the address to missing / any.
+ void Zero();
+};
+
+} // namespace cricket
+
+#endif // __SOCKETADDRESS_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc
new file mode 100644
index 00000000..2166be09
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.cc
@@ -0,0 +1,58 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/socketaddresspair.h"
+
+namespace cricket {
+
+SocketAddressPair::SocketAddressPair(
+ const SocketAddress& src, const SocketAddress& dest)
+ : src_(src), dest_(dest) {
+}
+
+
+bool SocketAddressPair::operator ==(const SocketAddressPair& p) const {
+ return (src_ == p.src_) && (dest_ == p.dest_);
+}
+
+bool SocketAddressPair::operator <(const SocketAddressPair& p) const {
+ if (src_ < p.src_)
+ return true;
+ if (p.src_ < src_)
+ return false;
+ if (dest_ < p.dest_)
+ return true;
+ if (p.dest_ < dest_)
+ return false;
+ return false;
+}
+
+size_t SocketAddressPair::Hash() const {
+ return src_.Hash() ^ dest_.Hash();
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h
new file mode 100644
index 00000000..098bafdb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketaddresspair.h
@@ -0,0 +1,58 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKETADDRESSPAIR_H__
+#define __SOCKETADDRESSPAIR_H__
+
+#include "talk/base/socketaddress.h"
+
+namespace cricket {
+
+// Records a pair (source,destination) of socket addresses. The two addresses
+// identify a connection between two machines. (For UDP, this "connection" is
+// not maintained explicitly in a socket.)
+class SocketAddressPair {
+public:
+ SocketAddressPair() {}
+ SocketAddressPair(const SocketAddress& srs, const SocketAddress& dest);
+
+ const SocketAddress& source() const { return src_; }
+ const SocketAddress& destination() const { return dest_; }
+
+ bool operator ==(const SocketAddressPair& r) const;
+ bool operator <(const SocketAddressPair& r) const;
+
+ size_t Hash() const;
+
+private:
+ SocketAddress src_;
+ SocketAddress dest_;
+};
+
+} // namespace cricket
+
+#endif // __SOCKETADDRESSPAIR_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h
new file mode 100644
index 00000000..67386160
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketfactory.h
@@ -0,0 +1,50 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKETFACTORY_H__
+#define __SOCKETFACTORY_H__
+
+#include "talk/base/socket.h"
+#include "talk/base/asyncsocket.h"
+
+namespace cricket {
+
+class SocketFactory {
+public:
+
+ // Returns a new socket for blocking communication. The type can be
+ // SOCK_DGRAM and SOCK_STREAM.
+ virtual Socket* CreateSocket(int type) = 0;
+
+ // Returns a new socket for nonblocking communication. The type can be
+ // SOCK_DGRAM and SOCK_STREAM.
+ virtual AsyncSocket* CreateAsyncSocket(int type) = 0;
+};
+
+} // namespace cricket
+
+#endif // __SOCKETFACTORY_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h
new file mode 100644
index 00000000..d0e7a22a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/socketserver.h
@@ -0,0 +1,53 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SOCKETSERVER_H__
+#define __SOCKETSERVER_H__
+
+#include "talk/base/socketfactory.h"
+
+namespace cricket {
+
+// Provides the ability to wait for activity on a set of sockets. The Thread
+// class provides a nice wrapper on a socket server.
+//
+// The server is also a socket factory. The sockets it creates will be
+// notified of asynchronous I/O from this server's Wait method.
+class SocketServer : public SocketFactory {
+public:
+
+ // Performs I/O or sleeps for the given number of milliseconds.
+ // If process_io is false, just sleeps until WakeUp.
+ virtual bool Wait(int cms, bool process_io) = 0;
+
+ // Causes the current wait (if one is in progress) to wake up.
+ virtual void WakeUp() = 0;
+};
+
+} // namespace cricket
+
+#endif // __SOCKETSERVER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h
new file mode 100644
index 00000000..9c2506f1
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/stl_decl.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _STL_DECL_H
+#define _STL_DECL_H
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0
+#pragma warning(disable:4786)
+#endif
+
+#include <sys/types.h>
+
+namespace std {
+ template <class Key> struct hash;
+ template <class Key> struct equal_to;
+ template <class Key> struct less;
+ template <class T> class allocator;
+ template <class Key, class Val,
+ class Compare,
+ class Alloc> class map;
+ template <class T, class Alloc> class vector;
+ template <class T, class Alloc> class list;
+ template <class T, class Alloc> class slist;
+ template <class T, class Alloc, size_t BufSiz> class deque;
+ template <class T, class Sequence> class stack;
+ template <class T, class Sequence> class queue;
+ template <class T, class Sequence, class Compare> class priority_queue;
+ template <class T1, class T2> struct pair;
+ template <class Key, class Compare, class Alloc> class set;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Workaround declaration problem with defaults
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200 // 1200 == VC++ 6.0
+
+#define STD_MAP(T1, T2) \
+ std::map<T1 , T2, std::less<T1>, std::allocator<T2> >
+
+#define STD_VECTOR(T1) \
+ std::vector<T1, std::allocator<T1> >
+
+#define STD_SET(T1) \
+ std::set<T1, std::less<T1>, std::allocator<T1> >
+
+#else
+
+#define STD_MAP(T1, T2) \
+ std::map<T1, T2, std::less<T1>, std::allocator<std::pair<const T1, T2 > > >
+
+#define STD_VECTOR(T1) \
+ std::vector<T1, std::allocator<T1> >
+
+#define STD_SET(T1) \
+ std::set<T1, std::less<T1>, std::allocator<T1> >
+
+#endif
+
+
+#endif // _STL_DECL_H
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h
new file mode 100644
index 00000000..a23132dd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/stringutils.h
@@ -0,0 +1,266 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STRINGUTILS_H__
+#define __STRINGUTILS_H__
+
+#include <cctype>
+#include <cstdarg>
+#include <cstdio>
+#ifdef WIN32
+#include <wchar.h>
+#endif // WIN32
+
+#include <string>
+
+///////////////////////////////////////////////////////////////////////////////
+// Rename a bunch of common string functions so they are consistent across
+// platforms and between char and wchar_t variants.
+// Here is the full list of functions that are unified:
+// strlen, strcmp, stricmp, strncmp, strnicmp
+// strchr, vsnprintf, strtoul, tolowercase
+// tolowercase is like tolower, but not compatible with end-of-file value
+// Note that the wchar_t versions are not available on Linux
+///////////////////////////////////////////////////////////////////////////////
+
+inline char tolowercase(char c) {
+ return static_cast<char>(tolower(c));
+}
+
+#ifdef WIN32
+
+inline size_t strlen(const wchar_t* s) {
+ return wcslen(s);
+}
+inline int strcmp(const wchar_t* s1, const wchar_t* s2) {
+ return wcscmp(s1, s2);
+}
+inline int stricmp(const wchar_t* s1, const wchar_t* s2) {
+ return wcsicmp(s1, s2);
+}
+inline int strncmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+ return wcsncmp(s1, s2, n);
+}
+inline int strnicmp(const wchar_t* s1, const wchar_t* s2, size_t n) {
+ return wcsnicmp(s1, s2, n);
+}
+inline const wchar_t* strchr(const wchar_t* s, wchar_t c) {
+ return wcschr(s, c);
+}
+inline int vsnprintf(char* buf, size_t n, const char* fmt, va_list args) {
+ return _vsnprintf(buf, n, fmt, args);
+}
+inline int vsnprintf(wchar_t* buf, size_t n, const wchar_t* fmt, va_list args) {
+ return _vsnwprintf(buf, n, fmt, args);
+}
+inline unsigned long strtoul(const wchar_t* snum, wchar_t** end, int base) {
+ return wcstoul(snum, end, base);
+}
+inline wchar_t tolowercase(wchar_t c) {
+ return static_cast<wchar_t>(towlower(c));
+}
+
+#endif // WIN32
+
+#ifdef POSIX
+
+inline int stricmp(const char* s1, const char* s2) {
+ return strcasecmp(s1, s2);
+}
+inline int strnicmp(const char* s1, const char* s2, size_t n) {
+ return strncasecmp(s1, s2, n);
+}
+
+#endif // POSIX
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits simplifies porting string functions to be CTYPE-agnostic
+///////////////////////////////////////////////////////////////////////////////
+
+namespace cricket {
+
+const size_t SIZE_UNKNOWN = static_cast<size_t>(-1);
+
+template<class CTYPE>
+struct Traits {
+ // STL string type
+ //typedef XXX string;
+ // Null-terminated string
+ //inline static const CTYPE* empty_str();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// String utilities which work with char or wchar_t
+///////////////////////////////////////////////////////////////////////////////
+
+template<class CTYPE>
+inline const CTYPE* nonnull(const CTYPE* str, const CTYPE* def_str = NULL) {
+ return str ? str : (def_str ? def_str : Traits<CTYPE>::empty_str());
+}
+
+template<class CTYPE>
+const CTYPE* strchr(const CTYPE* str, const CTYPE* chs) {
+ for (size_t i=0; str[i]; ++i) {
+ for (size_t j=0; chs[j]; ++j) {
+ if (str[i] == chs[j]) {
+ return str + i;
+ }
+ }
+ }
+ return 0;
+}
+
+template<class CTYPE>
+const CTYPE* strchrn(const CTYPE* str, size_t slen, CTYPE ch) {
+ for (size_t i=0; i<slen && str[i]; ++i) {
+ if (str[i] == ch) {
+ return str + i;
+ }
+ }
+ return 0;
+}
+
+template<class CTYPE>
+size_t strlenn(const CTYPE* buffer, size_t buflen) {
+ size_t bufpos = 0;
+ while (buffer[bufpos] && (bufpos < buflen)) {
+ ++bufpos;
+ }
+ return bufpos;
+}
+
+template<class CTYPE>
+size_t strcpyn(CTYPE* buffer, size_t buflen,
+ const CTYPE* source, size_t srclen = SIZE_UNKNOWN) {
+ if (buflen <= 0)
+ return 0;
+
+ if (srclen == SIZE_UNKNOWN) {
+ srclen = strlenn(source, buflen - 1);
+ } else if (srclen >= buflen) {
+ srclen = buflen - 1;
+ }
+ memcpy(buffer, source, srclen * sizeof(CTYPE));
+ buffer[srclen] = 0;
+ return srclen;
+}
+
+// Safe versions of snprintf and vsnprintf that always null-terminate
+
+template<class CTYPE>
+size_t sprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format, ...) {
+ va_list args;
+ va_start(args, format);
+ size_t len = vsprintfn(buffer, buflen, format, args);
+ va_end(args);
+ return len;
+}
+
+template<class CTYPE>
+size_t vsprintfn(CTYPE* buffer, size_t buflen, const CTYPE* format,
+ va_list args) {
+ int len = vsnprintf(buffer, buflen, format, args);
+ if ((len < 0) || (static_cast<size_t>(len) >= buflen)) {
+ len = static_cast<int>(buflen - 1);
+ buffer[len] = 0;
+ }
+ return len;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Allow safe comparing and copying ascii (not UTF-8) with both wide and
+// non-wide character strings.
+///////////////////////////////////////////////////////////////////////////////
+
+inline int asccmp(const char* s1, const char* s2) {
+ return strcmp(s1, s2);
+}
+inline int ascicmp(const char* s1, const char* s2) {
+ return stricmp(s1, s2);
+}
+inline int ascncmp(const char* s1, const char* s2, size_t n) {
+ return strncmp(s1, s2, n);
+}
+inline int ascnicmp(const char* s1, const char* s2, size_t n) {
+ return strnicmp(s1, s2, n);
+}
+inline size_t asccpyn(char* buffer, size_t buflen,
+ const char* source, size_t srclen = SIZE_UNKNOWN) {
+ return strcpyn(buffer, buflen, source, srclen);
+}
+
+#ifdef WIN32
+
+typedef wchar_t(*CharacterTransformation)(wchar_t);
+inline wchar_t identity(wchar_t c) { return c; }
+int ascii_string_compare(const wchar_t* s1, const char* s2, size_t n,
+ CharacterTransformation transformation);
+
+inline int asccmp(const wchar_t* s1, const char* s2) {
+ return ascii_string_compare(s1, s2, static_cast<size_t>(-1), identity);
+}
+inline int ascicmp(const wchar_t* s1, const char* s2) {
+ return ascii_string_compare(s1, s2, static_cast<size_t>(-1), tolowercase);
+}
+inline int ascncmp(const wchar_t* s1, const char* s2, size_t n) {
+ return ascii_string_compare(s1, s2, n, identity);
+}
+inline int ascnicmp(const wchar_t* s1, const char* s2, size_t n) {
+ return ascii_string_compare(s1, s2, n, tolowercase);
+}
+size_t asccpyn(wchar_t* buffer, size_t buflen,
+ const char* source, size_t srclen = SIZE_UNKNOWN);
+
+#endif // WIN32
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<char> specializations
+///////////////////////////////////////////////////////////////////////////////
+
+template<>
+struct Traits<char> {
+ typedef std::string string;
+ inline static const char* empty_str() { return ""; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Traits<wchar_t> specializations (Windows only, currently)
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef WIN32
+
+template<>
+struct Traits<wchar_t> {
+ typedef std::wstring string;
+ inline static const wchar_t* Traits<wchar_t>::empty_str() { return L""; }
+};
+
+#endif // WIN32
+
+} // namespace cricket
+
+#endif // __STRINGUTILS_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc
new file mode 100644
index 00000000..a5a94941
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.cc
@@ -0,0 +1,238 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "task.h"
+#include "taskrunner.h"
+
+#include <algorithm>
+
+namespace buzz {
+
+Task::Task(Task * parent) :
+ state_(STATE_INIT),
+ parent_(parent),
+ blocked_(false),
+ done_(false),
+ aborted_(false),
+ busy_(false),
+ error_(false),
+ child_error_(false),
+ start_time_(0) {
+ runner_ = ((parent == NULL) ? (TaskRunner *)this : parent->GetRunner());
+ if (parent_ != NULL) {
+ parent_->AddChild(this);
+ }
+}
+
+unsigned long long
+Task::CurrentTime() {
+ return runner_->CurrentTime();
+}
+
+unsigned long long
+Task::ElapsedTime() {
+ return CurrentTime() - start_time_;
+}
+
+void
+Task::Start() {
+ if (state_ != STATE_INIT)
+ return;
+ GetRunner()->StartTask(this);
+ start_time_ = CurrentTime();
+}
+
+void
+Task::Step() {
+ if (done_) {
+#ifdef DEBUG
+ // we do not know how !blocked_ happens when done_ - should be impossible.
+ // But it causes problems, so in retail build, we force blocked_, and
+ // under debug we assert.
+ assert(blocked_);
+#else
+ blocked_ = true;
+#endif
+ return;
+ }
+
+ // Async Error() was called
+ if (error_) {
+ done_ = true;
+ state_ = STATE_ERROR;
+ blocked_ = true;
+// obsolete - an errored task is not considered done now
+// SignalDone();
+ Stop();
+ return;
+ }
+
+ busy_ = true;
+ int new_state = Process(state_);
+ busy_ = false;
+
+ if (aborted_) {
+ Abort(true); // no need to wake because we're awake
+ return;
+ }
+
+ if (new_state == STATE_BLOCKED) {
+ blocked_ = true;
+ }
+ else {
+ state_ = new_state;
+ blocked_ = false;
+ }
+
+ if (new_state == STATE_DONE) {
+ done_ = true;
+ }
+ else if (new_state == STATE_ERROR) {
+ done_ = true;
+ error_ = true;
+ }
+
+ if (done_) {
+// obsolete - call this yourself
+// SignalDone();
+ Stop();
+ blocked_ = true;
+ }
+}
+
+void
+Task::Abort(bool nowake) {
+ if (aborted_ || done_)
+ return;
+ aborted_ = true;
+ if (!busy_) {
+ done_ = true;
+ blocked_ = true;
+ error_ = true;
+ Stop();
+ if (!nowake)
+ Wake(); // to self-delete
+ }
+}
+
+void
+Task::Wake() {
+ if (done_)
+ return;
+ if (blocked_) {
+ blocked_ = false;
+ GetRunner()->WakeTasks();
+ }
+}
+
+void
+Task::Error() {
+ if (error_ || done_)
+ return;
+ error_ = true;
+ Wake();
+}
+
+std::string
+Task::GetStateName(int state) const {
+ static const std::string STR_BLOCKED("BLOCKED");
+ static const std::string STR_INIT("INIT");
+ static const std::string STR_START("START");
+ static const std::string STR_DONE("DONE");
+ static const std::string STR_ERROR("ERROR");
+ static const std::string STR_RESPONSE("RESPONSE");
+ static const std::string STR_HUH("??");
+ switch (state) {
+ case STATE_BLOCKED: return STR_BLOCKED;
+ case STATE_INIT: return STR_INIT;
+ case STATE_START: return STR_START;
+ case STATE_DONE: return STR_DONE;
+ case STATE_ERROR: return STR_ERROR;
+ case STATE_RESPONSE: return STR_RESPONSE;
+ }
+ return STR_HUH;
+}
+
+int Task::Process(int state) {
+ switch (state) {
+ case STATE_INIT:
+ return STATE_START;
+ case STATE_START:
+ return ProcessStart();
+ case STATE_RESPONSE:
+ return ProcessResponse();
+ case STATE_DONE:
+ case STATE_ERROR:
+ return STATE_BLOCKED;
+ }
+ return STATE_ERROR;
+}
+
+void
+Task::AddChild(Task * child) {
+ children_.insert(child);
+}
+
+bool
+Task::AllChildrenDone() {
+ for (ChildSet::iterator it = children_.begin(); it != children_.end(); ++it) {
+ if (!(*it)->IsDone())
+ return false;
+ }
+ return true;
+}
+
+bool
+Task::AnyChildError() {
+ return child_error_;
+}
+
+void
+Task::AbortAllChildren() {
+ if (children_.size() > 0) {
+ ChildSet copy = children_;
+ for (ChildSet::iterator it = copy.begin(); it != copy.end(); ++it) {
+ (*it)->Abort(true); // Note we do not wake
+ }
+ }
+}
+
+void
+Task::Stop() {
+ AbortAllChildren(); // No need to wake because we're either awake or in abort
+ parent_->OnChildStopped(this);
+}
+
+void
+Task::OnChildStopped(Task * child) {
+ if (child->HasError())
+ child_error_ = true;
+ children_.erase(child);
+}
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h
new file mode 100644
index 00000000..5a486198
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/task.h
@@ -0,0 +1,186 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TASK_H_
+#define _TASK_H_
+
+#include <vector>
+#include <string>
+
+#include "talk/base/sigslot.h"
+
+/////////////////////////////////////////////////////////////////////
+//
+// TASK
+//
+/////////////////////////////////////////////////////////////////////
+//
+// Task is a state machine infrastructure. States are pushed forward by
+// pushing forwards a TaskRunner that holds on to all Tasks. The purpose
+// of Task is threefold:
+//
+// (1) It manages ongoing work on the UI thread. Multitasking without
+// threads, keeping it easy, keeping it real. :-) It does this by
+// organizing a set of states for each task. When you return from your
+// Process*() function, you return an integer for the next state. You do
+// not go onto the next state yourself. Every time you enter a state,
+// you check to see if you can do anything yet. If not, you return
+// STATE_BLOCKED. If you _could_ do anything, do not return
+// STATE_BLOCKED - even if you end up in the same state, return
+// STATE_mysamestate. When you are done, return STATE_DONE and then the
+// task will self-delete sometimea afterwards.
+//
+// (2) It helps you avoid all those reentrancy problems when you chain
+// too many triggers on one thread. Basically if you want to tell a task
+// to process something for you, you feed your task some information and
+// then you Wake() it. Don't tell it to process it right away. If it
+// might be working on something as you send it infomration, you may want
+// to have a queue in the task.
+//
+// (3) Finally it helps manage parent tasks and children. If a parent
+// task gets aborted, all the children tasks are too. The nice thing
+// about this, for example, is if you have one parent task that
+// represents, say, and Xmpp connection, then you can spawn a whole bunch
+// of infinite lifetime child tasks and now worry about cleaning them up.
+// When the parent task goes to STATE_DONE, the task engine will make
+// sure all those children are aborted and get deleted.
+//
+// Notice that Task has a few built-in states, e.g.,
+//
+// STATE_INIT - the task isn't running yet
+// STATE_START - the task is in its first state
+// STATE_RESPONSE - the task is in its second state
+// STATE_DONE - the task is done
+//
+// STATE_ERROR - indicates an error - we should audit the error code in
+// light of any usage of it to see if it should be improved. When I
+// first put down the task stuff I didn't have a good sense of what was
+// needed for Abort and Error, and now the subclasses of Task will ground
+// the design in a stronger way.
+//
+// STATE_NEXT - the first undefined state number. (like WM_USER) - you
+// can start defining more task states there.
+//
+// When you define more task states, just override Process(int state) and
+// add your own switch statement. If you want to delegate to
+// Task::Process, you can effectively delegate to its switch statement.
+// No fancy method pointers or such - this is all just pretty low tech,
+// easy to debug, and fast.
+//
+
+namespace buzz {
+
+class TaskRunner;
+
+// A task executes a sequence of steps
+
+class Task;
+class RootTask;
+
+class Task {
+public:
+ Task(Task * parent);
+ virtual ~Task() {}
+
+ void Start();
+ void Step();
+ int GetState() const { return state_; }
+ bool HasError() const { return (GetState() == STATE_ERROR); }
+ bool Blocked() const { return blocked_; }
+ bool IsDone() const { return done_; }
+ unsigned long long ElapsedTime();
+ virtual void Poll() {}
+
+ Task * GetParent() { return parent_; }
+ TaskRunner * GetRunner() { return runner_; }
+ virtual Task * GetParent(int code) { return parent_->GetParent(code); }
+
+ // Called from outside to stop task without any more callbacks
+ void Abort(bool nowake = false);
+
+ // For managing children
+ bool AllChildrenDone();
+ bool AnyChildError();
+
+
+protected:
+
+ enum {
+ STATE_BLOCKED = -1,
+ STATE_INIT = 0,
+ STATE_START = 1,
+ STATE_DONE = 2,
+ STATE_ERROR = 3,
+ STATE_RESPONSE = 4,
+ STATE_NEXT = 5, // Subclasses which need more states start here and higher
+ };
+
+ // Called inside the task to signal that the task may be unblocked
+ void Wake();
+
+ // Called inside to advise that the task should wake and signal an error
+ void Error();
+
+ unsigned long long CurrentTime();
+
+ virtual std::string GetStateName(int state) const;
+ virtual int Process(int state);
+ virtual void Stop();
+ virtual int ProcessStart() = 0;
+ virtual int ProcessResponse() { return STATE_DONE; }
+
+ // for managing children (if any)
+ void AddChild(Task * child);
+ void AbortAllChildren();
+
+private:
+ void Done();
+ void OnChildStopped(Task * child);
+
+ int state_;
+ Task * parent_;
+ TaskRunner * runner_;
+ bool blocked_;
+ bool done_;
+ bool aborted_;
+ bool busy_;
+ bool error_;
+ bool child_error_;
+ unsigned long long start_time_;
+
+ // for managing children
+ typedef std::set<Task *> ChildSet;
+ ChildSet children_;
+
+};
+
+
+
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc
new file mode 100644
index 00000000..b5ecc55e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.cc
@@ -0,0 +1,92 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "taskrunner.h"
+#include "task.h"
+#include <algorithm>
+
+
+namespace buzz {
+
+TaskRunner::~TaskRunner() {
+ // this kills and deletes children silently!
+ AbortAllChildren();
+ RunTasks();
+}
+
+void
+TaskRunner::StartTask(Task * task) {
+ tasks_.push_back(task);
+ WakeTasks();
+}
+
+void
+TaskRunner::RunTasks() {
+ // Running continues until all tasks are Blocked (ok for a small # of tasks)
+ if (tasks_running_) {
+ return; // don't reenter
+ }
+
+ tasks_running_ = true;
+
+ int did_run = true;
+ while (did_run) {
+ did_run = false;
+ // use indexing instead of iterators because tasks_ may grow
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ while (!tasks_[i]->Blocked()) {
+ tasks_[i]->Step();
+ did_run = true;
+ }
+ }
+ }
+ // Tasks are deleted when running has paused
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ if (tasks_[i]->IsDone()) {
+ Task* task = tasks_[i];
+ delete task;
+ tasks_[i] = NULL;
+ }
+ }
+ // Finally, remove nulls
+ tasks_.erase(std::remove(tasks_.begin(), tasks_.end(), (Task *)NULL), tasks_.end());
+
+ tasks_running_ = false;
+}
+
+void
+TaskRunner::PollTasks() {
+ // every task gets hit once with a poll - they wake if needed
+ for (size_t i = 0; i < tasks_.size(); ++i) {
+ if (!tasks_[i]->IsDone()) {
+ tasks_[i]->Poll();
+ }
+ }
+}
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h
new file mode 100644
index 00000000..eab16eb9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/taskrunner.h
@@ -0,0 +1,64 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TASKRUNNER_H_
+#define _TASKRUNNER_H_
+
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/base/task.h"
+
+
+namespace buzz {
+
+
+class Task;
+
+class TaskRunner : public Task, public sigslot::has_slots<> {
+public:
+ TaskRunner() : Task(NULL), tasks_running_(false) {}
+ virtual ~TaskRunner();
+
+ virtual void WakeTasks() = 0;
+ virtual unsigned long long CurrentTime() = 0 ;
+
+ void StartTask(Task * task);
+ void RunTasks();
+ void PollTasks();
+
+ // dummy state machine - never run.
+ virtual int ProcessStart() { return STATE_DONE; }
+
+private:
+ std::vector<Task *> tasks_;
+ bool tasks_running_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc
new file mode 100644
index 00000000..8f18a992
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.cc
@@ -0,0 +1,273 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef POSIX
+extern "C" {
+#include <sys/time.h>
+}
+#endif
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#include "talk/base/jtime.h"
+
+namespace cricket {
+
+ThreadManager g_thmgr;
+
+#ifdef POSIX
+pthread_key_t ThreadManager::key_;
+
+ThreadManager::ThreadManager() {
+ pthread_key_create(&key_, NULL);
+ main_thread_ = new Thread();
+ SetCurrent(main_thread_);
+}
+
+ThreadManager::~ThreadManager() {
+ pthread_key_delete(key_);
+ delete main_thread_;
+}
+
+Thread *ThreadManager::CurrentThread() {
+ return (Thread *)pthread_getspecific(key_);
+}
+
+void ThreadManager::SetCurrent(Thread *thread) {
+ pthread_setspecific(key_, thread);
+}
+#endif
+
+#ifdef WIN32
+DWORD ThreadManager::key_;
+
+ThreadManager::ThreadManager() {
+ key_ = TlsAlloc();
+ main_thread_ = new Thread();
+ SetCurrent(main_thread_);
+}
+
+ThreadManager::~ThreadManager() {
+ TlsFree(key_);
+ delete main_thread_;
+}
+
+Thread *ThreadManager::CurrentThread() {
+ return (Thread *)TlsGetValue(key_);
+}
+
+void ThreadManager::SetCurrent(Thread *thread) {
+ TlsSetValue(key_, thread);
+}
+#endif
+
+void ThreadManager::Add(Thread *thread) {
+ CritScope cs(&crit_);
+ threads_.push_back(thread);
+}
+
+void ThreadManager::Remove(Thread *thread) {
+ CritScope cs(&crit_);
+ threads_.erase(std::remove(threads_.begin(), threads_.end(), thread), threads_.end());
+}
+
+Thread::Thread(SocketServer* ss) : MessageQueue(ss) {
+ g_thmgr.Add(this);
+ started_ = false;
+ has_sends_ = false;
+}
+
+Thread::~Thread() {
+ Stop();
+ Clear(NULL);
+ g_thmgr.Remove(this);
+}
+
+#ifdef POSIX
+void Thread::Start() {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_create(&thread_, &attr, PreLoop, this);
+ started_ = true;
+}
+
+void Thread::Join() {
+ if (started_) {
+ void *pv;
+ pthread_join(thread_, &pv);
+ }
+}
+#endif
+
+#ifdef WIN32
+void Thread::Start() {
+ thread_ = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PreLoop, this, 0, NULL);
+ started_ = true;
+}
+
+void Thread::Join() {
+ if (started_) {
+ WaitForSingleObject(thread_, INFINITE);
+ CloseHandle(thread_);
+ started_ = false;
+ }
+}
+#endif
+
+void *Thread::PreLoop(void *pv) {
+ Thread *thread = (Thread *)pv;
+ ThreadManager::SetCurrent(thread);
+ thread->Loop();
+ return NULL;
+}
+
+void Thread::Loop(int cmsLoop) {
+ uint32 msEnd;
+ if (cmsLoop != -1)
+ msEnd = GetMillisecondCount() + cmsLoop;
+ int cmsNext = cmsLoop;
+
+ while (true) {
+ Message msg;
+ if (!Get(&msg, cmsNext))
+ return;
+ Dispatch(&msg);
+
+ if (cmsLoop != -1) {
+ uint32 msCur = GetMillisecondCount();
+ if (msCur >= msEnd)
+ return;
+ cmsNext = msEnd - msCur;
+ }
+ }
+}
+
+void Thread::Stop() {
+ MessageQueue::Stop();
+ Join();
+}
+
+void Thread::Send(MessageHandler *phandler, uint32 id, MessageData *pdata) {
+ // Sent messages are sent to the MessageHandler directly, in the context
+ // of "thread", like Win32 SendMessage. If in the right context,
+ // call the handler directly.
+
+ Message msg;
+ msg.phandler = phandler;
+ msg.message_id = id;
+ msg.pdata = pdata;
+ if (IsCurrent()) {
+ phandler->OnMessage(&msg);
+ return;
+ }
+
+ AutoThread thread;
+ Thread *current_thread = Thread::Current();
+ ASSERT(current_thread != NULL); // AutoThread ensures this
+
+ crit_.Enter();
+ bool ready = false;
+ _SendMessage smsg;
+ smsg.thread = current_thread;
+ smsg.msg = msg;
+ smsg.ready = &ready;
+ sendlist_.push_back(smsg);
+ has_sends_ = true;
+ crit_.Leave();
+
+ // Wait for a reply
+
+ ss_->WakeUp();
+ while (!ready) {
+ current_thread->ReceiveSends();
+ current_thread->socketserver()->Wait(-1, false);
+ }
+}
+
+void Thread::ReceiveSends() {
+ // Before entering critical section, check boolean.
+
+ if (!has_sends_)
+ return;
+
+ // Receive a sent message. Cleanup scenarios:
+ // - thread sending exits: We don't allow this, since thread can exit
+ // only via Join, so Send must complete.
+ // - thread receiving exits: Wakeup/set ready in Thread::Clear()
+ // - object target cleared: Wakeup/set ready in Thread::Clear()
+ crit_.Enter();
+ while (!sendlist_.empty()) {
+ _SendMessage smsg = sendlist_.front();
+ sendlist_.pop_front();
+ crit_.Leave();
+ smsg.msg.phandler->OnMessage(&smsg.msg);
+ crit_.Enter();
+ *smsg.ready = true;
+ smsg.thread->socketserver()->WakeUp();
+ }
+ has_sends_ = false;
+ crit_.Leave();
+}
+
+void Thread::Clear(MessageHandler *phandler, uint32 id) {
+ CritScope cs(&crit_);
+
+ // Remove messages on sendlist_ with phandler
+ // Object target cleared: remove from send list, wakeup/set ready
+ // if sender not NULL.
+
+ std::list<_SendMessage>::iterator iter = sendlist_.begin();
+ while (iter != sendlist_.end()) {
+ _SendMessage smsg = *iter;
+ if (phandler == NULL || smsg.msg.phandler == phandler) {
+ if (id == (uint32)-1 || smsg.msg.message_id == id) {
+ iter = sendlist_.erase(iter);
+ *smsg.ready = true;
+ smsg.thread->socketserver()->WakeUp();
+ continue;
+ }
+ }
+ ++iter;
+ }
+
+ MessageQueue::Clear(phandler, id);
+}
+
+AutoThread::AutoThread(SocketServer* ss) : Thread(ss) {
+ if (!ThreadManager::CurrentThread()) {
+ ThreadManager::SetCurrent(this);
+ }
+}
+
+AutoThread::~AutoThread() {
+ if (ThreadManager::CurrentThread() == this) {
+ ThreadManager::SetCurrent(NULL);
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h
new file mode 100644
index 00000000..56c23384
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/thread.h
@@ -0,0 +1,141 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __THREAD_H__
+#define __THREAD_H__
+
+#include "talk/base/messagequeue.h"
+
+#include <algorithm>
+#include <list>
+#include <vector>
+
+#ifdef POSIX
+#include <pthread.h>
+#endif
+
+#ifdef WIN32
+#include "talk/base/win32.h"
+#endif
+
+namespace cricket {
+
+class Thread;
+
+class ThreadManager {
+public:
+ ThreadManager();
+ ~ThreadManager();
+
+ static Thread *CurrentThread();
+ static void SetCurrent(Thread *thread);
+ void Add(Thread *thread);
+ void Remove(Thread *thread);
+
+private:
+ Thread *main_thread_;
+ std::vector<Thread *> threads_;
+ CriticalSection crit_;
+
+#ifdef POSIX
+ static pthread_key_t key_;
+#endif
+
+#ifdef WIN32
+ static DWORD key_;
+#endif
+};
+
+class Thread;
+
+struct _SendMessage {
+ _SendMessage() {}
+ Thread *thread;
+ Message msg;
+ bool *ready;
+};
+
+class Thread : public MessageQueue {
+public:
+ Thread(SocketServer* ss = 0);
+ virtual ~Thread();
+
+ static inline Thread* Current() {
+ return ThreadManager::CurrentThread();
+ }
+ inline bool IsCurrent() const {
+ return (ThreadManager::CurrentThread() == this);
+ }
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Loop(int cms = -1);
+ virtual void Send(MessageHandler *phandler, uint32 id = 0,
+ MessageData *pdata = NULL);
+
+ // From MessageQueue
+ virtual void Clear(MessageHandler *phandler, uint32 id = (uint32)-1);
+ virtual void ReceiveSends();
+
+#ifdef WIN32
+ HANDLE GetHandle() {
+ return thread_;
+ }
+#endif
+
+private:
+ static void *PreLoop(void *pv);
+ void Join();
+
+ std::list<_SendMessage> sendlist_;
+ bool started_;
+ bool has_sends_;
+
+#ifdef POSIX
+ pthread_t thread_;
+#endif
+
+#ifdef WIN32
+ HANDLE thread_;
+#endif
+
+ friend class ThreadManager;
+};
+
+// AutoThread automatically installs itself at construction
+// uninstalls at destruction, if a Thread object is
+// _not already_ associated with the current OS thread.
+
+class AutoThread : public Thread {
+public:
+ AutoThread(SocketServer* ss = 0);
+ virtual ~AutoThread();
+};
+
+} // namespace cricket
+
+#endif // __THREAD_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h b/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h
new file mode 100644
index 00000000..99ba2fdc
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/base/winping.h
@@ -0,0 +1,101 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _WINPING_H_
+#define _WINPING_H_
+
+#ifdef WIN32
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "talk/base/basictypes.h"
+
+#include <winsock2.h>
+#define _WINSOCKAPI_
+#include <windows.h>
+#undef SetPort
+
+// This class wraps a Win32 API for doing ICMP pinging. This API, unlike the
+// the normal socket APIs (as implemented on Win9x), will return an error if
+// an ICMP packet with the dont-fragment bit set is too large. This means this
+// class can be used to detect the MTU to a given address.
+
+typedef struct ip_option_information {
+ UCHAR Ttl; // Time To Live
+ UCHAR Tos; // Type Of Service
+ UCHAR Flags; // IP header flags
+ UCHAR OptionsSize; // Size in bytes of options data
+ PUCHAR OptionsData; // Pointer to options data
+} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
+
+typedef HANDLE (WINAPI *PIcmpCreateFile)();
+
+typedef BOOL (WINAPI *PIcmpCloseHandle)(HANDLE icmp_handle);
+
+typedef DWORD (WINAPI *PIcmpSendEcho)(
+ HANDLE IcmpHandle,
+ ULONG DestinationAddress,
+ LPVOID RequestData,
+ WORD RequestSize,
+ PIP_OPTION_INFORMATION RequestOptions,
+ LPVOID ReplyBuffer,
+ DWORD ReplySize,
+ DWORD Timeout);
+
+class WinPing {
+public:
+ WinPing();
+ ~WinPing();
+
+ // Determines whether the class was initialized correctly.
+ bool IsValid() { return valid_; }
+
+ // Attempts to send a ping with the given parameters.
+ enum PingResult { PING_FAIL, PING_TOO_LARGE, PING_TIMEOUT, PING_SUCCESS };
+ PingResult Ping(
+ uint32 ip, uint32 data_size, uint32 timeout_millis, uint8 ttl,
+ bool allow_fragments);
+
+private:
+ HMODULE dll_;
+ HANDLE hping_;
+ PIcmpCreateFile create_;
+ PIcmpCloseHandle close_;
+ PIcmpSendEcho send_;
+ char* data_;
+ uint32 dlen_;
+ char* reply_;
+ uint32 rlen_;
+ bool valid_;
+};
+
+#endif // WIN32
+
+#endif // _WINPING_H_
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am
new file mode 100644
index 00000000..43b0edb5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=login call
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am
new file mode 100644
index 00000000..81cf9345
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/Makefile.am
@@ -0,0 +1,16 @@
+bin_PROGRAMS = call
+call_CXXFLAGS = $(AM_CXXFLAGS)
+call_SOURCES = call_main.cc callclient.cc console.cc presencepushtask.cc presenceouttask.cc
+noinst_HEADERS = callclient.h console.h presenceouttask.h presencepushtask.h status.h
+call_LDADD = \
+ $(srcdir)/../../../talk/examples/login/libcricketexampleslogin.la \
+ $(srcdir)/../../../talk/session/phone/libcricketsessionphone.la \
+ $(srcdir)/../../../talk/p2p/client/libcricketp2pclient.la \
+ $(srcdir)/../../../talk/p2p/base/libcricketp2pbase.la \
+ $(srcdir)/../../../talk/xmpp/libcricketxmpp.la \
+ $(srcdir)/../../../talk/xmllite/libcricketxmllite.la \
+ $(srcdir)/../../../talk/base/libcricketbase.la \
+ $(srcdir)/../../../talk/third_party/mediastreamer/libmediastreamer.la \
+ $(EXPAT_LIBS) $(ORTP_LIBS) -lpthread $(ILBC_LIBS) $(SPEEX_LIBS) $(GLIB_LIBS) -lasound
+AM_CPPFLAGS = -DPOSIX
+DEFAULT_INCLUDES = -I$(srcdir)/../../.. \ No newline at end of file
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro
new file mode 100644
index 00000000..ccf0638b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call.pro
@@ -0,0 +1,19 @@
+TEMPLATE = app
+INCLUDEPATH = ../../..
+DEFINES += POSIX
+
+include(../../../../../conf.pri)
+
+# Input
+SOURCES += \
+ call_main.cc \
+ callclient.cc \
+ console.cc \
+ presenceouttask.cc \
+ presencepushtask.cc \
+ ../login/xmppauth.cc \
+ ../login/xmpppump.cc \
+ ../login/xmppsocket.cc \
+ ../login/xmppthread.cc
+
+LIBS += ../../../liblibjingle.a
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc
new file mode 100644
index 00000000..1a965326
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/call_main.cc
@@ -0,0 +1,62 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include "talk/examples/login/xmppauth.h"
+#include "talk/examples/call/callclient.h"
+#include "talk/examples/call/console.h"
+
+void GetString(const char* desc, char* out) {
+ printf("%s: ", desc);
+ fflush(stdout);
+ scanf("%s", out);
+}
+
+int main(int argc, char **argv) {
+ // TODO: Make this into a console task
+ char username[256], auth_cookie[256];
+ GetString("Username", username);
+ GetString("Auth Cookie", auth_cookie);
+
+ printf("Logging in as %s@gmail.com\n", username);
+
+ // We will run the console and the XMPP client on the main thread. The
+ // CallClient maintains a separate worker thread for voice.
+
+ cricket::PhysicalSocketServer ss;
+ cricket::Thread main_thread(&ss);
+ cricket::ThreadManager::SetCurrent(&main_thread);
+
+ InitConsole(&ss);
+ XmppPump pump;
+ CallClient client(pump.client());
+
+ buzz::XmppClientSettings xcs;
+ xcs.set_user(username);
+ xcs.set_host("gmail.com");
+ xcs.set_use_tls(false);
+ xcs.set_auth_cookie(auth_cookie);
+ xcs.set_server(cricket::SocketAddress("talk.google.com", 5222));
+ pump.DoLogin(xcs, new XmppSocket(false), new XmppAuth());
+
+ main_thread.Loop();
+
+ return 0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc
new file mode 100644
index 00000000..c8c28310
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.cc
@@ -0,0 +1,390 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <string>
+#include <vector>
+
+#include "talk/xmpp/constants.h"
+#include "talk/base/thread.h"
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/session/receiver.h"
+#include "talk/session/sessionsendtask.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/examples/call/callclient.h"
+#include "talk/examples/call/console.h"
+#include "talk/examples/call/presencepushtask.h"
+#include "talk/examples/call/presenceouttask.h"
+
+namespace {
+
+const char* CALL_COMMANDS =
+"Available commands:\n"
+"\n"
+" hangup Ends the call.\n"
+" mute Stops sending voice.\n"
+" unmute Re-starts sending voice.\n"
+"";
+
+class CallTask: public ConsoleTask, public sigslot::has_slots<> {
+public:
+ CallTask(CallClient* call_client, const buzz::Jid& jid, cricket::Call* call)
+ : call_client_(call_client), jid_(jid), call_(call) {
+ }
+
+ virtual ~CallTask() {}
+
+ virtual void Start() {
+ call_client_->phone_client()->SignalCallDestroy.connect(
+ this, &CallTask::OnCallDestroy);
+ if (!call_) {
+ call_ = call_client_->phone_client()->CreateCall();
+ call_->SignalSessionState.connect(this, &CallTask::OnSessionState);
+ session_ = call_->InitiateSession(jid_);
+ }
+ call_client_->phone_client()->SetFocus(call_);
+ }
+
+ virtual std::string GetPrompt() { return jid_.node(); }
+
+ virtual void ProcessLine(const std::string& line) {
+ std::vector<std::string> words;
+ ParseLine(line, &words);
+
+ if ((words.size() == 1) && (words[0] == "hangup")) {
+ call_->Terminate();
+ SignalDone(this);
+ } else if ((words.size() == 1) && (words[0] == "mute")) {
+ call_->Mute(true);
+ } else if ((words.size() == 1) && (words[0] == "unmute")) {
+ call_->Mute(false);
+ } else {
+ console()->Print(CALL_COMMANDS);
+ }
+ }
+
+private:
+ CallClient* call_client_;
+ buzz::Jid jid_;
+ cricket::Call* call_;
+ cricket::Session* session_;
+
+ void OnCallDestroy(cricket::Call* call) {
+ if (call == call_) {
+ console()->Print("call destroyed");
+ SignalDone(this);
+ }
+ }
+
+ void OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state) {
+ if (state == cricket::Session::STATE_SENTINITIATE) {
+ console()->Print("calling...");
+ } else if (state == cricket::Session::STATE_RECEIVEDACCEPT) {
+ console()->Print("call answered");
+ } else if (state == cricket::Session::STATE_RECEIVEDREJECT) {
+ console()->Print("call not answered");
+ SignalDone(this);
+ } else if (state == cricket::Session::STATE_INPROGRESS) {
+ console()->Print("call in progress");
+ } else if (state == cricket::Session::STATE_RECEIVEDTERMINATE) {
+ console()->Print("other side hung up");
+ SignalDone(this);
+ }
+ }
+};
+
+const char* RECEIVE_COMMANDS =
+"Available commands:\n"
+"\n"
+" accept Accepts the incoming call and switches to it.\n"
+" reject Rejects the incoming call and stays with the current call.\n"
+"";
+
+class ReceiveTask: public ConsoleTask {
+public:
+ ReceiveTask(CallClient* call_client,
+ const buzz::Jid& jid,
+ cricket::Call* call)
+ : call_client_(call_client), jid_(jid), call_(call) {
+ }
+
+ virtual std::string GetPrompt() { return jid_.node(); }
+
+ virtual void ProcessLine(const std::string& line) {
+ std::vector<std::string> words;
+ ParseLine(line, &words);
+
+ if ((words.size() == 1) && (words[0] == "accept")) {
+ assert(call_->sessions().size() == 1);
+ call_->AcceptSession(call_->sessions()[0]);
+ Console()->Push(new CallTask(call_client_, jid_, call_));
+ SignalDone(this);
+ } else if ((words.size() == 1) && (words[0] == "reject")) {
+ call_->RejectSession(call_->sessions()[0]);
+ SignalDone(this);
+ } else {
+ console()->Print(RECEIVE_COMMANDS);
+ }
+ }
+
+private:
+ CallClient* call_client_;
+ buzz::Jid jid_;
+ cricket::Call* call_;
+};
+
+const char* CONSOLE_COMMANDS =
+"Available commands:\n"
+"\n"
+" roster Prints the online friends from your roster.\n"
+" call <name> Initiates a call to the friend with the given name.\n"
+" quit Quits the application.\n"
+"";
+
+class CallConsoleTask: public ConsoleTask {
+public:
+ CallConsoleTask(CallClient* call_client) : call_client_(call_client) {}
+ virtual ~CallConsoleTask() {}
+
+ virtual std::string GetPrompt() { return "console"; }
+
+ virtual void ProcessLine(const std::string& line) {
+ std::vector<std::string> words;
+ ParseLine(line, &words);
+
+ if ((words.size() == 1) && (words[0] == "quit")) {
+ SignalDone(this);
+ } else if ((words.size() == 1) && (words[0] == "roster")) {
+ call_client_->PrintRoster();
+ } else if ((words.size() == 2) && (words[0] == "call")) {
+ call_client_->MakeCallTo(words[1]);
+ } else {
+ console()->Print(CONSOLE_COMMANDS);
+ }
+ }
+
+private:
+ CallClient* call_client_;
+};
+
+const char* DescribeStatus(buzz::Status::Show show, const std::string& desc) {
+ switch (show) {
+ case buzz::Status::SHOW_XA: return desc.c_str();
+ case buzz::Status::SHOW_ONLINE: return "online";
+ case buzz::Status::SHOW_AWAY: return "away";
+ case buzz::Status::SHOW_DND: return "do not disturb";
+ case buzz::Status::SHOW_CHAT: return "ready to chat";
+ delault: return "offline";
+ }
+}
+
+} // namespace
+
+CallClient::CallClient(buzz::XmppClient* xmpp_client)
+ : xmpp_client_(xmpp_client), roster_(new RosterMap) {
+ xmpp_client_->SignalStateChange.connect(this, &CallClient::OnStateChange);
+ Console()->Push(new CallConsoleTask(this));
+}
+
+CallClient::~CallClient() {
+ delete roster_;
+}
+
+const std::string CallClient::strerror(buzz::XmppEngine::Error err) {
+ switch (err) {
+ case buzz::XmppEngine::ERROR_NONE:
+ return "";
+ case buzz::XmppEngine::ERROR_XML:
+ return "Malformed XML or encoding error";
+ case buzz::XmppEngine::ERROR_STREAM:
+ return "XMPP stream error";
+ case buzz::XmppEngine::ERROR_VERSION:
+ return "XMPP version error";
+ case buzz::XmppEngine::ERROR_UNAUTHORIZED:
+ return "User is not authorized (Confirm your GX cookie at mail.google.com)";
+ case buzz::XmppEngine::ERROR_TLS:
+ return "TLS could not be negotiated";
+ case buzz::XmppEngine::ERROR_AUTH:
+ return "Authentication could not be negotiated";
+ case buzz::XmppEngine::ERROR_BIND:
+ return "Resource or session binding could not be negotiated";
+ case buzz::XmppEngine::ERROR_CONNECTION_CLOSED:
+ return "Connection closed by output handler.";
+ case buzz::XmppEngine::ERROR_DOCUMENT_CLOSED:
+ return "Closed by </stream:stream>";
+ case buzz::XmppEngine::ERROR_SOCKET:
+ return "Socket error";
+ }
+}
+
+void CallClient::OnStateChange(buzz::XmppEngine::State state) {
+ switch (state) {
+ case buzz::XmppEngine::STATE_START:
+ Console()->Print("connecting...");
+ break;
+
+ case buzz::XmppEngine::STATE_OPENING:
+ Console()->Print("logging in...");
+ break;
+
+ case buzz::XmppEngine::STATE_OPEN:
+ Console()->Print("logged in...");
+ InitPhone();
+ InitPresence();
+ break;
+
+ case buzz::XmppEngine::STATE_CLOSED:
+ buzz::XmppEngine::Error error = xmpp_client_->GetError();
+ Console()->Print("logged out..." + strerror(error));
+ exit(0);
+ }
+}
+
+void CallClient::InitPhone() {
+ std::string client_unique = xmpp_client_->jid().Str();
+ cricket::InitRandom(client_unique.c_str(), client_unique.size());
+
+ worker_thread_ = new cricket::Thread();
+
+ network_manager_ = new cricket::NetworkManager();
+
+ cricket::SocketAddress *stun_addr = new cricket::SocketAddress("64.233.167.126", 19302);
+ port_allocator_ = new cricket::BasicPortAllocator(network_manager_, stun_addr, NULL);
+
+ session_manager_ = new cricket::SessionManager(
+ port_allocator_, worker_thread_);
+ session_manager_->SignalRequestSignaling.connect(
+ this, &CallClient::OnRequestSignaling);
+ session_manager_->OnSignalingReady();
+
+ phone_client_ = new cricket::PhoneSessionClient(
+ xmpp_client_->jid(),session_manager_);
+ phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate);
+ phone_client_->SignalSendStanza.connect(this, &CallClient::OnSendStanza);
+
+ receiver_ = new cricket::Receiver(xmpp_client_, phone_client_);
+ receiver_->Start();
+
+ worker_thread_->Start();
+}
+
+void CallClient::OnRequestSignaling() {
+ session_manager_->OnSignalingReady();
+}
+
+void CallClient::OnCallCreate(cricket::Call* call) {
+ call->SignalSessionState.connect(this, &CallClient::OnSessionState);
+}
+
+void CallClient::OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state) {
+ if (state == cricket::Session::STATE_RECEIVEDINITIATE) {
+ buzz::Jid jid(session->remote_address());
+ Console()->Printf("Incoming call from '%s'", jid.Str().c_str());
+ Console()->Push(new ReceiveTask(this, jid, call));
+ }
+}
+
+void CallClient::OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza) {
+ cricket::SessionSendTask* sender =
+ new cricket::SessionSendTask(xmpp_client_, phone_client_);
+ sender->Send(stanza);
+ sender->Start();
+}
+
+void CallClient::InitPresence() {
+ presence_push_ = new buzz::PresencePushTask(xmpp_client_);
+ presence_push_->SignalStatusUpdate.connect(
+ this, &CallClient::OnStatusUpdate);
+ presence_push_->Start();
+
+ buzz::Status my_status;
+ my_status.set_jid(xmpp_client_->jid());
+ my_status.set_available(true);
+ my_status.set_invisible(false);
+ my_status.set_show(buzz::Status::SHOW_ONLINE);
+ my_status.set_priority(0);
+ my_status.set_know_capabilities(true);
+ my_status.set_phone_capability(true);
+ my_status.set_is_google_client(true);
+ my_status.set_version("1.0.0.66");
+
+ buzz::PresenceOutTask* presence_out_ =
+ new buzz::PresenceOutTask(xmpp_client_);
+ presence_out_->Send(my_status);
+ presence_out_->Start();
+}
+
+void CallClient::OnStatusUpdate(const buzz::Status& status) {
+ RosterItem item;
+ item.jid = status.jid();
+ item.show = status.show();
+ item.status = status.status();
+
+ std::string key = item.jid.Str();
+
+ if (status.available() && status.phone_capability()) {
+ Console()->Printf("Adding to roster: %s", key.c_str());
+ (*roster_)[key] = item;
+ } else {
+ Console()->Printf("Removing from roster: %s", key.c_str());
+ RosterMap::iterator iter = roster_->find(key);
+ if (iter != roster_->end())
+ roster_->erase(iter);
+ }
+}
+
+void CallClient::PrintRoster() {
+ Console()->Printf("Roster contains %d callable", roster_->size());
+ RosterMap::iterator iter = roster_->begin();
+ while (iter != roster_->end()) {
+ Console()->Printf("%s - %s",
+ iter->second.jid.BareJid().Str().c_str(),
+ DescribeStatus(iter->second.show, iter->second.status));
+ iter++;
+ }
+}
+
+void CallClient::MakeCallTo(const std::string& name) {
+ bool found = false;
+ buzz::Jid found_jid;
+
+ RosterMap::iterator iter = roster_->begin();
+ while (iter != roster_->end()) {
+ if (iter->second.jid.node() == name) {
+ found = true;
+ found_jid = iter->second.jid;
+ break;
+ }
+ ++iter;
+ }
+
+ if (found) {
+ Console()->Printf("Found online friend '%s'", found_jid.Str().c_str());
+ Console()->Push(new CallTask(this, found_jid, NULL));
+ } else {
+ Console()->Printf("Could not find online friend '%s'", name.c_str());
+ }
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h
new file mode 100644
index 00000000..2400b7db
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/callclient.h
@@ -0,0 +1,88 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
+#define CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
+
+#include <map>
+#include <string>
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/examples/call/status.h"
+
+namespace buzz {
+class PresencePushTask;
+class Status;
+}
+
+namespace cricket {
+class Thread;
+class NetworkManager;
+class PortAllocator;
+class PhoneSessionClient;
+class Receiver;
+class Call;
+}
+
+struct RosterItem {
+ buzz::Jid jid;
+ buzz::Status::Show show;
+ std::string status;
+};
+
+class CallClient: public sigslot::has_slots<> {
+public:
+ CallClient(buzz::XmppClient* xmpp_client);
+ ~CallClient();
+
+ cricket::PhoneSessionClient* phone_client() const { return phone_client_; }
+
+ void PrintRoster();
+ void MakeCallTo(const std::string& name);
+
+private:
+ typedef std::map<std::string,RosterItem> RosterMap;
+
+ buzz::XmppClient* xmpp_client_;
+ cricket::Thread* worker_thread_;
+ cricket::NetworkManager* network_manager_;
+ cricket::PortAllocator* port_allocator_;
+ cricket::SessionManager* session_manager_;
+ cricket::PhoneSessionClient* phone_client_;
+ cricket::Receiver* receiver_;
+ buzz::PresencePushTask* presence_push_;
+ RosterMap* roster_;
+
+ void OnStateChange(buzz::XmppEngine::State state);
+
+ void InitPhone();
+ void OnRequestSignaling();
+ void OnCallCreate(cricket::Call* call);
+ const std::string strerror(buzz::XmppEngine::Error err);
+ void OnSessionState(cricket::Call* call,
+ cricket::Session* session,
+ cricket::Session::State state);
+ void OnSendStanza(cricket::SessionClient *client, const buzz::XmlElement* stanza);
+
+ void InitPresence();
+ void OnStatusUpdate(const buzz::Status& status);
+};
+
+#endif // CRICKET_EXAMPLES_CALL_CALLCLIENT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc
new file mode 100644
index 00000000..4150f281
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.cc
@@ -0,0 +1,196 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+}
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdarg>
+
+#include "talk/examples/call/console.h"
+
+namespace {
+
+void PError(const char* desc) {
+ perror(desc);
+ exit(1);
+}
+
+CConsole* gConsole = NULL;
+
+const uint32 MSG_UPDATE = 1;
+
+} // namespace
+
+void InitConsole(cricket::PhysicalSocketServer* ss) {
+ assert(gConsole == NULL);
+ assert(ss);
+ gConsole = new CConsole(ss);
+}
+
+CConsole* Console() {
+ assert(gConsole);
+ return gConsole;
+}
+
+CConsole::CConsole(cricket::PhysicalSocketServer* ss)
+ : prompting_(false), prompt_dirty_(false) {
+ stdin_ = ss->CreateFile(0);
+ stdin_->SignalReadEvent.connect(this, &CConsole::OnReadInput);
+
+ tasks_ = new std::vector<ConsoleTask*>;
+}
+
+CConsole::~CConsole() {
+ delete stdin_;
+ delete tasks_;
+}
+
+void CConsole::Push(ConsoleTask* task) {
+ task->set_console(this);
+ task->SignalDone.connect(this, &CConsole::OnTaskDone);
+ tasks_->push_back(task);
+ task->Start();
+ UpdatePrompt();
+}
+
+void CConsole::Remove(ConsoleTask* task) {
+ int index = -1;
+ for (size_t i = 0; i < tasks_->size(); ++i) {
+ if ((*tasks_)[i] == task)
+ index = i;
+ }
+
+ assert(index >= 0);
+ tasks_->erase(tasks_->begin() + index);
+ if (static_cast<int>(tasks_->size()) == index)
+ UpdatePrompt();
+
+ delete task;
+
+ if (tasks_->size() == 0)
+ exit(0);
+}
+
+void CConsole::Print(const char* str) {
+ if (prompting_)
+ printf("\r");
+ printf("%s\n", str);
+ prompting_ = false;
+ UpdatePrompt();
+}
+
+void CConsole::Print(const std::string& str) {
+ Print(str.c_str());
+}
+
+void CConsole::Printf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ char buf[4096];
+ int size = vsnprintf(buf, sizeof(buf), format, ap);
+ assert(size >= 0);
+ assert(size < static_cast<int>(sizeof(buf)));
+ buf[size] = '\0';
+ Print(buf);
+
+ va_end(ap);
+}
+
+void CConsole::OnTaskDone(ConsoleTask* task) {
+ Remove(task);
+}
+
+void CConsole::OnReadInput(cricket::AsyncFile* file) {
+ assert(file == stdin_);
+
+ char buf[4096];
+ int size = read(0, buf, sizeof(buf));
+ if (size < 0)
+ PError("read");
+
+ prompting_ = (buf[size-1] != '\n');
+
+ int start = 0;
+ for (int i = 0; i < size; ++i) {
+ if (buf[i] == '\n') {
+ std::string line = input_;
+ line.append(buf + start, i + 1 - start);
+ input_.clear();
+
+ assert(tasks_->size() > 0);
+ tasks_->back()->ProcessLine(line);
+
+ start = i + 1;
+ }
+ }
+
+ input_.append(buf + start, size - start);
+}
+
+void CConsole::OnMessage(cricket::Message* pmsg) {
+ assert(pmsg->message_id == MSG_UPDATE);
+ assert(tasks_->size() > 0);
+ if (prompting_)
+ printf("\n");
+ printf("%s: %s", tasks_->back()->GetPrompt().c_str(), input_.c_str());
+ fflush(stdout);
+ prompting_ = true;
+ prompt_dirty_ = false;
+}
+
+void CConsole::UpdatePrompt() {
+ if (!prompt_dirty_) {
+ prompt_dirty_ = true;
+ cricket::Thread::Current()->Post(this, MSG_UPDATE);
+ }
+}
+
+void ConsoleTask::ParseLine(const std::string& line,
+ std::vector<std::string>* words) {
+ assert(line.size() > 0);
+ assert(line[line.size() - 1] == '\n');
+
+ int start = -1;
+ int state = 0;
+ for (int index = 0; index <= static_cast<int>(line.size()); ++index) {
+ if (state == 0) {
+ if (!isspace(line[index])) {
+ start = index;
+ state = 1;
+ }
+ } else {
+ assert(state == 1);
+ assert(start >= 0);
+ if (isspace(line[index])) {
+ std::string word(line, start, index - start);
+ words->push_back(word);
+ start = -1;
+ state = 0;
+ }
+ }
+ }
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h
new file mode 100644
index 00000000..aca229b9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/console.h
@@ -0,0 +1,82 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef CRICKET_EXAMPLES_CALL_CONSOLE_H__
+#define CRICKET_EXAMPLES_CALL_CONSOLE_H__
+
+#include <string>
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/base/physicalsocketserver.h"
+
+class CConsole;
+
+class ConsoleTask {
+public:
+ ConsoleTask() : console_(NULL) {}
+ virtual ~ConsoleTask() {}
+
+ CConsole* console() const { return console_; }
+ void set_console(CConsole* console) { console_ = console; }
+
+ virtual void Start() {}
+ virtual std::string GetPrompt() = 0;
+ virtual void ProcessLine(const std::string& line) = 0; // includes newline
+
+ sigslot::signal1<ConsoleTask*> SignalDone;
+
+protected:
+ void ParseLine(const std::string& line, std::vector<std::string>* words);
+
+private:
+ CConsole* console_;
+};
+
+class CConsole: public cricket::MessageHandler, public sigslot::has_slots<> {
+public:
+ CConsole(cricket::PhysicalSocketServer* ss);
+ ~CConsole();
+
+ void Push(ConsoleTask* task);
+ void Remove(ConsoleTask* task);
+
+ // final newline should not be included
+ void Print(const char* str);
+ void Print(const std::string& str);
+ void Printf(const char* format, ...);
+
+private:
+ cricket::AsyncFile* stdin_;
+ std::vector<ConsoleTask*>* tasks_;
+ std::string input_;
+ bool prompting_;
+ bool prompt_dirty_;
+
+ void OnTaskDone(ConsoleTask* task);
+ void OnReadInput(cricket::AsyncFile* file);
+ void OnMessage(cricket::Message* pmsg);
+ void UpdatePrompt();
+};
+
+void InitConsole(cricket::PhysicalSocketServer* ss);
+CConsole* Console();
+
+#endif // CRICKET_EXAMPLES_CALL_CONSOLE_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc
new file mode 100644
index 00000000..3ecdb420
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.cc
@@ -0,0 +1,148 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sstream>
+#include <time.h>
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/examples/call/presenceouttask.h"
+
+namespace buzz {
+
+// string helper functions -----------------------------------------------------
+template <class T> static
+bool FromString(const std::string& s,
+ T * t) {
+ std::istringstream iss(s);
+ return !(iss>>*t).fail();
+}
+
+template <class T> static
+bool ToString(const T &t,
+ std::string* s) {
+ std::ostringstream oss;
+ oss << t;
+ *s = oss.str();
+ return !oss.fail();
+}
+
+XmppReturnStatus
+PresenceOutTask::Send(const Status & s) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ stanza_.reset(TranslateStatus(s));
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+PresenceOutTask::SendDirected(const Jid & j, const Status & s) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = TranslateStatus(s);
+ presence->AddAttr(QN_TO, j.Str());
+ stanza_.reset(presence);
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus PresenceOutTask::SendProbe(const Jid & jid) {
+ if (GetState() != STATE_INIT)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement * presence = new XmlElement(QN_PRESENCE);
+ presence->AddAttr(QN_TO, jid.Str());
+ presence->AddAttr(QN_TYPE, "probe");
+
+ stanza_.reset(presence);
+ return XMPP_RETURN_OK;
+}
+
+int
+PresenceOutTask::ProcessStart() {
+ if (SendStanza(stanza_.get()) != XMPP_RETURN_OK)
+ return STATE_ERROR;
+ return STATE_DONE;
+}
+
+XmlElement *
+PresenceOutTask::TranslateStatus(const Status & s) {
+ XmlElement * result = new XmlElement(QN_PRESENCE);
+ if (!s.available()) {
+ result->AddAttr(QN_TYPE, STR_UNAVAILABLE);
+ }
+ else {
+ if (s.invisible()) {
+ result->AddAttr(QN_TYPE, STR_INVISIBLE);
+ }
+
+ if (s.show() != Status::SHOW_ONLINE && s.show() != Status::SHOW_OFFLINE) {
+ result->AddElement(new XmlElement(QN_SHOW));
+ switch (s.show()) {
+ default:
+ result->AddText(STR_SHOW_AWAY, 1);
+ break;
+ case Status::SHOW_XA:
+ result->AddText(STR_SHOW_XA, 1);
+ break;
+ case Status::SHOW_DND:
+ result->AddText(STR_SHOW_DND, 1);
+ break;
+ case Status::SHOW_CHAT:
+ result->AddText(STR_SHOW_CHAT, 1);
+ break;
+ }
+ }
+
+ result->AddElement(new XmlElement(QN_STATUS));
+ result->AddText(s.status(), 1);
+
+ std::string pri;
+ ToString(s.priority(), &pri);
+
+ result->AddElement(new XmlElement(QN_PRIORITY));
+ result->AddText(pri, 1);
+
+ if (s.know_capabilities() && s.is_google_client()) {
+ result->AddElement(new XmlElement(QN_CAPS_C, true));
+ result->AddAttr(QN_NODE, GOOGLE_CLIENT_NODE, 1);
+ result->AddAttr(QN_VER, s.version(), 1);
+ result->AddAttr(QN_EXT, s.phone_capability() ? "voice-v1" : "", 1);
+ }
+
+ // Put the delay mark on the presence according to JEP-0091
+ {
+ result->AddElement(new XmlElement(kQnDelayX, true));
+
+ // This here is why we *love* the C runtime
+ time_t current_time_seconds;
+ time(&current_time_seconds);
+ struct tm* current_time = gmtime(&current_time_seconds);
+ char output[256];
+ strftime(output, ARRAY_SIZE(output), "%Y%m%dT%H:%M:%S", current_time);
+ result->AddAttr(kQnStamp, output, 1);
+ }
+
+ }
+
+ return result;
+}
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h
new file mode 100644
index 00000000..868bda59
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presenceouttask.h
@@ -0,0 +1,46 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PRESENCEOUTTASK_H_
+#define _PRESENCEOUTTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/examples/call/status.h"
+
+namespace buzz {
+
+class PresenceOutTask : public XmppTask {
+public:
+ PresenceOutTask(Task * parent) : XmppTask(parent) {}
+ virtual ~PresenceOutTask() {}
+
+ XmppReturnStatus Send(const Status & s);
+ XmppReturnStatus SendDirected(const Jid & j, const Status & s);
+ XmppReturnStatus SendProbe(const Jid& jid);
+
+ virtual int ProcessStart();
+private:
+ XmlElement * TranslateStatus(const Status & s);
+ scoped_ptr<XmlElement> stanza_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc
new file mode 100644
index 00000000..d0543b99
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.cc
@@ -0,0 +1,172 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "talk/examples/call/presencepushtask.h"
+#include "talk/xmpp/constants.h"
+#include <sstream>
+
+
+namespace buzz {
+
+// string helper functions -----------------------------------------------------
+template <class T> static
+bool FromString(const std::string& s,
+ T * t) {
+ std::istringstream iss(s);
+ return !(iss>>*t).fail();
+}
+
+template <class T> static
+bool ToString(const T &t,
+ std::string* s) {
+ std::ostringstream oss;
+ oss << t;
+ *s = oss.str();
+ return !oss.fail();
+}
+
+static bool
+IsXmlSpace(int ch) {
+ return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
+}
+
+static bool
+ListContainsToken(const std::string & list, const std::string & token) {
+ size_t i = list.find(token);
+ if (i == std::string::npos || token.empty())
+ return false;
+ bool boundary_before = (i == 0 || IsXmlSpace(list[i - 1]));
+ bool boundary_after = (i == list.length() - token.length() || IsXmlSpace(list[i + token.length()]));
+ return boundary_before && boundary_after;
+}
+
+
+bool
+PresencePushTask::HandleStanza(const XmlElement * stanza) {
+ if (stanza->Name() != QN_PRESENCE)
+ return false;
+ if (stanza->HasAttr(QN_TYPE) && stanza->Attr(QN_TYPE) != STR_UNAVAILABLE)
+ return false;
+ QueueStanza(stanza);
+ return true;
+}
+
+static bool IsUtf8FirstByte(int c) {
+ return (((c)&0x80)==0) || // is single byte
+ ((unsigned char)((c)-0xc0)<0x3e); // or is lead byte
+}
+
+int
+PresencePushTask::ProcessStart() {
+ const XmlElement * stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+ Status s;
+
+ s.set_jid(Jid(stanza->Attr(QN_FROM)));
+
+ if (stanza->Attr(QN_TYPE) == STR_UNAVAILABLE) {
+ s.set_available(false);
+ SignalStatusUpdate(s);
+ }
+ else {
+ s.set_available(true);
+ const XmlElement * status = stanza->FirstNamed(QN_STATUS);
+ if (status != NULL) {
+ s.set_status(status->BodyText());
+
+ // Truncate status messages longer than 300 bytes
+ if (s.status().length() > 300) {
+ size_t len = 300;
+
+ // Be careful not to split legal utf-8 chars in half
+ while (!IsUtf8FirstByte(s.status()[len]) && len > 0) {
+ len -= 1;
+ }
+ std::string truncated(s.status(), 0, len);
+ s.set_status(truncated);
+ }
+ }
+
+ const XmlElement * priority = stanza->FirstNamed(QN_PRIORITY);
+ if (priority != NULL) {
+ int pri;
+ if (FromString(priority->BodyText(), &pri)) {
+ s.set_priority(pri);
+ }
+ }
+
+ const XmlElement * show = stanza->FirstNamed(QN_SHOW);
+ if (show == NULL || show->FirstChild() == NULL) {
+ s.set_show(Status::SHOW_ONLINE);
+ }
+ else {
+ if (show->BodyText() == "away") {
+ s.set_show(Status::SHOW_AWAY);
+ }
+ else if (show->BodyText() == "xa") {
+ s.set_show(Status::SHOW_XA);
+ }
+ else if (show->BodyText() == "dnd") {
+ s.set_show(Status::SHOW_DND);
+ }
+ else if (show->BodyText() == "chat") {
+ s.set_show(Status::SHOW_CHAT);
+ }
+ else {
+ s.set_show(Status::SHOW_ONLINE);
+ }
+ }
+
+ const XmlElement * caps = stanza->FirstNamed(QN_CAPS_C);
+ if (caps != NULL) {
+ std::string node = caps->Attr(QN_NODE);
+ std::string ver = caps->Attr(QN_VER);
+ std::string exts = caps->Attr(QN_EXT);
+
+ s.set_know_capabilities(true);
+
+ if (node == GOOGLE_CLIENT_NODE) {
+ s.set_is_google_client(true);
+ s.set_version(ver);
+ if (ListContainsToken(exts, "voice-v1")) {
+ s.set_phone_capability(true);
+ }
+ }
+ }
+
+ const XmlElement* delay = stanza->FirstNamed(kQnDelayX);
+ if (delay != NULL) {
+ // Ideally we would parse this according to the Psuedo ISO-8601 rules
+ // that are laid out in JEP-0082:
+ // http://www.jabber.org/jeps/jep-0082.html
+ std::string stamp = delay->Attr(kQnStamp);
+ s.set_sent_time(stamp);
+ }
+
+ SignalStatusUpdate(s);
+ }
+
+ return STATE_START;
+}
+
+
+}
+
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h
new file mode 100644
index 00000000..45bc9020
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/presencepushtask.h
@@ -0,0 +1,44 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PRESENCEPUSHTASK_H_
+#define _PRESENCEPUSHTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/base/sigslot.h"
+#include "talk/examples/call/status.h"
+
+namespace buzz {
+
+class PresencePushTask : public XmppTask {
+
+public:
+ PresencePushTask(Task * parent) : XmppTask(parent, XmppEngine::HL_TYPE) {}
+ virtual int ProcessStart();
+ sigslot::signal1<const Status &>SignalStatusUpdate;
+
+protected:
+ virtual bool HandleStanza(const XmlElement * stanza);
+};
+
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h
new file mode 100644
index 00000000..a1e76f62
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/call/status.h
@@ -0,0 +1,213 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+
+#define GOOGLE_CLIENT_NODE "http://www.google.com/xmpp/client/caps"
+
+namespace buzz {
+
+class Status {
+public:
+ Status() :
+ pri_(0),
+ show_(SHOW_NONE),
+ available_(false),
+ invisible_(false),
+ e_code_(0),
+ phone_capability_(false),
+ know_capabilities_(false),
+ is_google_client_(false),
+ feedback_probation_(false) {};
+
+ ~Status() {}
+
+ // These are arranged in "priority order", i.e., if we see
+ // two statuses at the same priority but with different Shows,
+ // we will show the one with the highest show in the following
+ // order.
+ enum Show {
+ SHOW_NONE = 0,
+ SHOW_INVISIBLE = 1,
+ SHOW_OFFLINE = 2,
+ SHOW_XA = 3,
+ SHOW_AWAY = 4,
+ SHOW_DND = 5,
+ SHOW_ONLINE = 6,
+ SHOW_CHAT = 7,
+ };
+
+ const Jid & jid() const { return jid_; }
+ int priority() const { return pri_; }
+ Show show() const { return show_; }
+ const std::string & status() const { return status_; }
+ bool available() const { return available_ ; }
+ bool invisible() const { return invisible_; }
+ int error_code() const { return e_code_; }
+ const std::string & error_string() const { return e_str_; }
+ bool know_capabilities() const { return know_capabilities_; }
+ bool phone_capability() const { return phone_capability_; }
+ bool is_google_client() const { return is_google_client_; }
+ const std::string & version() const { return version_; }
+ bool feedback_probation() const { return feedback_probation_; }
+ const std::string& sent_time() const { return sent_time_; }
+
+ void set_jid(const Jid & jid) { jid_ = jid; }
+ void set_priority(int pri) { pri_ = pri; }
+ void set_show(Show show) { show_ = show; }
+ void set_status(const std::string & status) { status_ = status; }
+ void set_available(bool a) { available_ = a; }
+ void set_invisible(bool i) { invisible_ = i; }
+ void set_error(int e_code, const std::string e_str)
+ { e_code_ = e_code; e_str_ = e_str; }
+ void set_know_capabilities(bool f) { know_capabilities_ = f; }
+ void set_phone_capability(bool f) { phone_capability_ = f; }
+ void set_is_google_client(bool f) { is_google_client_ = f; }
+ void set_version(const std::string & v) { version_ = v; }
+ void set_feedback_probation(bool f) { feedback_probation_ = f; }
+ void set_sent_time(const std::string& time) { sent_time_ = time; }
+
+ void UpdateWith(const Status & new_value) {
+ if (!new_value.know_capabilities()) {
+ bool k = know_capabilities();
+ bool i = is_google_client();
+ bool p = phone_capability();
+ std::string v = version();
+
+ *this = new_value;
+
+ set_know_capabilities(k);
+ set_is_google_client(i);
+ set_phone_capability(p);
+ set_version(v);
+ }
+ else {
+ *this = new_value;
+ }
+ }
+
+ bool HasQuietStatus() const {
+ if (status_.empty())
+ return false;
+ return !(QuietStatus().empty());
+ }
+
+ // Knowledge of other clients' silly automatic status strings -
+ // Don't show these.
+ std::string QuietStatus() const {
+ if (jid_.resource().find("Psi") != std::string::npos) {
+ if (status_ == "Online" ||
+ status_.find("Auto Status") != std::string::npos)
+ return STR_EMPTY;
+ }
+ if (jid_.resource().find("Gaim") != std::string::npos) {
+ if (status_ == "Sorry, I ran out for a bit!")
+ return STR_EMPTY;
+ }
+ return TrimStatus(status_);
+ }
+
+ std::string ExplicitStatus() const {
+ std::string result = QuietStatus();
+ if (result.empty()) {
+ result = ShowStatus();
+ }
+ return result;
+ }
+
+ std::string ShowStatus() const {
+ std::string result;
+ if (!available()) {
+ result = "Offline";
+ }
+ else {
+ switch (show()) {
+ case SHOW_AWAY:
+ case SHOW_XA:
+ result = "Idle";
+ break;
+ case SHOW_DND:
+ result = "Busy";
+ break;
+ case SHOW_CHAT:
+ result = "Chatty";
+ break;
+ default:
+ result = "Available";
+ break;
+ }
+ }
+ return result;
+ }
+
+ static std::string TrimStatus(const std::string & st) {
+ std::string s(st);
+ int j = 0;
+ bool collapsing = true;
+ for (unsigned int i = 0; i < s.length(); i+= 1) {
+ if (s[i] <= ' ' && s[i] >= 0) {
+ if (collapsing) {
+ continue;
+ }
+ else {
+ s[j] = ' ';
+ j += 1;
+ collapsing = true;
+ }
+ }
+ else {
+ s[j] = s[i];
+ j += 1;
+ collapsing = false;
+ }
+ }
+ if (collapsing && j > 0) {
+ j -= 1;
+ }
+ s.erase(j, s.length());
+ return s;
+ }
+
+private:
+ Jid jid_;
+ int pri_;
+ Show show_;
+ std::string status_;
+ bool available_;
+ bool invisible_;
+ int e_code_;
+ std::string e_str_;
+ bool feedback_probation_;
+
+ // capabilities (valid only if know_capabilities_
+ bool know_capabilities_;
+ bool phone_capability_;
+ bool is_google_client_;
+ std::string version_;
+
+ std::string sent_time_; // from the jabber:x:delay element
+};
+
+}
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am
new file mode 100644
index 00000000..16164fb7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/Makefile.am
@@ -0,0 +1,15 @@
+noinst_LTLIBRARIES= libcricketexampleslogin.la
+libcricketexampleslogin_la_SOURCES = xmppsocket.cc \
+ xmppauth.cc \
+ xmppthread.cc \
+ xmpppump.cc
+noinst_HEADERS = xmppauth.h xmpppump.h xmppsocket.h xmppthread.h
+bin_PROGRAMS = login
+login_CXXFLAGS = $(AM_CXXFLAGS)
+login_SOURCES = login_main.cc xmppsocket.cc xmppthread.cc xmpppump.cc xmppauth.cc
+login_LDADD = $(srcdir)/../../../talk/xmpp/libcricketxmpp.la \
+ $(srcdir)/../../../talk/xmllite/libcricketxmllite.la \
+ $(srcdir)/../../../talk/base/libcricketbase.la \
+ $(EXPAT_LIBS) -lpthread
+AM_CPPFLAGS = -DPOSIX
+DEFAULT_INCLUDES = -I$(srcdir)/../../..
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc
new file mode 100644
index 00000000..2939c79f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/login_main.cc
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/thread.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include <iostream>
+
+int main(int argc, char **argv) {
+ printf("Auth Cookie: ");
+ fflush(stdout);
+
+ char auth_cookie[256];
+ scanf("%s", auth_cookie);
+
+ char username[256];
+ scanf("%s", username);
+
+ // Start xmpp on a different thread
+ XmppThread thread;
+ thread.Start();
+
+ buzz::XmppClientSettings xcs;
+ xcs.set_user(username);
+ xcs.set_host("gmail.com");
+ xcs.set_use_tls(false);
+ xcs.set_auth_cookie(auth_cookie);
+ xcs.set_server(cricket::SocketAddress("talk.google.com", 5222));
+ thread.Login(xcs);
+
+ // Use main thread for console input
+ std::string line;
+ while (std::getline(std::cin, line)) {
+ if (line == "quit")
+ break;
+ }
+ return 0;
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc
new file mode 100644
index 00000000..66191f12
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.cc
@@ -0,0 +1,93 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <algorithm>
+#include "talk/examples/login/xmppauth.h"
+#include "talk/xmpp/saslcookiemechanism.h"
+#include "talk/xmpp/saslplainmechanism.h"
+
+XmppAuth::XmppAuth() : done_(false), error_(false) {
+}
+
+XmppAuth::~XmppAuth() {
+}
+
+void XmppAuth::StartPreXmppAuth(const buzz::Jid & jid,
+ const cricket::SocketAddress & server,
+ const buzz::XmppPassword & pass,
+ const std::string & auth_cookie) {
+ jid_ = jid;
+ passwd_ = pass;
+ auth_cookie_ = auth_cookie;
+ error_ = auth_cookie.empty();
+ done_ = true;
+
+ SignalAuthDone();
+}
+
+std::string XmppAuth::ChooseBestSaslMechanism(
+ const std::vector<std::string> & mechanisms,
+ bool encrypted) {
+ std::vector<std::string>::const_iterator it;
+
+ // a token is the weakest auth - 15s, service-limited, so prefer it.
+ it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-TOKEN");
+ if (it != mechanisms.end())
+ return "X-GOOGLE-TOKEN";
+
+ // a cookie is the next weakest - 14 days
+ it = std::find(mechanisms.begin(), mechanisms.end(), "X-GOOGLE-COOKIE");
+ if (it != mechanisms.end())
+ return "X-GOOGLE-COOKIE";
+
+ // never pass @google.com passwords without encryption!!
+ if (!encrypted && (jid_.domain() == "google.com"))
+ return "";
+
+ // as a last resort, use plain authentication
+ if (jid_.domain() != "google.com") {
+ it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN");
+ if (it != mechanisms.end())
+ return "PLAIN";
+ }
+
+ // No good mechanism found
+ return "";
+}
+
+buzz::SaslMechanism* XmppAuth::CreateSaslMechanism(
+ const std::string & mechanism) {
+ if (mechanism == "X-GOOGLE-TOKEN") {
+ return new buzz::SaslCookieMechanism(mechanism, jid_.Str(), auth_cookie_);
+ //} else if (mechanism == "X-GOOGLE-COOKIE") {
+ // return new buzz::SaslCookieMechanism(mechanism, jid.Str(), sid_);
+ } else if (mechanism == "PLAIN") {
+ return new buzz::SaslPlainMechanism(jid_, passwd_);
+ } else {
+ return NULL;
+ }
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h
new file mode 100644
index 00000000..57743f90
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppauth.h
@@ -0,0 +1,71 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPAUTH_H_
+#define _XMPPAUTH_H_
+
+#include <vector>
+
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/saslhandler.h"
+#include "talk/xmpp/prexmppauth.h"
+#include "talk/xmpp/xmpppassword.h"
+
+class XmppAuth: public buzz::PreXmppAuth {
+public:
+ XmppAuth();
+ virtual ~XmppAuth();
+
+ virtual void StartPreXmppAuth(const buzz::Jid & jid,
+ const cricket::SocketAddress & server,
+ const buzz::XmppPassword & pass,
+ const std::string & auth_cookie);
+
+ virtual bool IsAuthDone() { return done_; }
+ virtual bool IsAuthorized() { return !error_; }
+ virtual bool HadError() { return error_; }
+ virtual buzz::CaptchaChallenge GetCaptchaChallenge() {
+ return buzz::CaptchaChallenge();
+ }
+ virtual std::string GetAuthCookie() { return auth_cookie_; }
+
+ virtual std::string ChooseBestSaslMechanism(
+ const std::vector<std::string> & mechanisms,
+ bool encrypted);
+
+ virtual buzz::SaslMechanism * CreateSaslMechanism(
+ const std::string & mechanism);
+
+private:
+ buzz::Jid jid_;
+ buzz::XmppPassword passwd_;
+ std::string auth_cookie_;
+ bool done_, error_;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc
new file mode 100644
index 00000000..7d966fb3
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.cc
@@ -0,0 +1,73 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/examples/login/xmpppump.h"
+#include "talk/examples/login/xmppauth.h"
+
+XmppPump::XmppPump(XmppPumpNotify * notify) {
+ state_ = buzz::XmppEngine::STATE_NONE;
+ notify_ = notify;
+ client_ = new buzz::XmppClient(this); // NOTE: deleted by TaskRunner
+}
+
+void XmppPump::DoLogin(const buzz::XmppClientSettings & xcs,
+ buzz::AsyncSocket* socket,
+ buzz::PreXmppAuth* auth) {
+ OnStateChange(buzz::XmppEngine::STATE_START);
+ client_->SignalStateChange.connect(this, &XmppPump::OnStateChange);
+ client_->Connect(xcs, socket, auth);
+ client_->Start();
+}
+
+void XmppPump::DoDisconnect() {
+ client_->Disconnect();
+ OnStateChange(buzz::XmppEngine::STATE_CLOSED);
+}
+
+void XmppPump::OnStateChange(buzz::XmppEngine::State state) {
+ if (state_ == state)
+ return;
+ state_ = state;
+ if (notify_ != NULL)
+ notify_->OnStateChange(state);
+}
+
+void XmppPump::WakeTasks() {
+ cricket::Thread::Current()->Post(this);
+}
+
+unsigned long long XmppPump::CurrentTime() {
+ return cricket::Time();
+}
+
+void XmppPump::OnMessage(cricket::Message *pmsg) {
+ RunTasks();
+}
+
+buzz::XmppReturnStatus XmppPump::SendStanza(const buzz::XmlElement *stanza) {
+ return client_->SendStanza(stanza);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h
new file mode 100644
index 00000000..fd6f88bb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmpppump.h
@@ -0,0 +1,73 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPPUMP_H_
+#define _XMPPPUMP_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/base/taskrunner.h"
+#include "talk/base/thread.h"
+#include "talk/base/jtime.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+
+// Simple xmpp pump
+
+class XmppPumpNotify {
+public:
+ virtual void OnStateChange(buzz::XmppEngine::State state) = 0;
+};
+
+class XmppPump : public cricket::MessageHandler, public buzz::TaskRunner {
+public:
+ XmppPump(XmppPumpNotify * notify = NULL);
+
+ buzz::XmppClient *client() { return client_; }
+
+ void DoLogin(const buzz::XmppClientSettings & xcs,
+ buzz::AsyncSocket* socket,
+ buzz::PreXmppAuth* auth);
+ void DoDisconnect();
+
+ void OnStateChange(buzz::XmppEngine::State state);
+
+ void WakeTasks();
+
+ unsigned long long CurrentTime();
+
+ void OnMessage(cricket::Message *pmsg);
+
+ buzz::XmppReturnStatus SendStanza(const buzz::XmlElement *stanza);
+
+private:
+ buzz::XmppClient *client_;
+ buzz::XmppEngine::State state_;
+ XmppPumpNotify *notify_;
+};
+
+#endif // _XMPPPUMP_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc
new file mode 100644
index 00000000..33aabf3e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.cc
@@ -0,0 +1,144 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include "talk/base/basicdefs.h"
+#include "talk/base/logging.h"
+#include "talk/base/thread.h"
+#ifdef FEATURE_ENABLE_SSL
+#include "talk/base/schanneladapter.h"
+#endif
+#include "xmppsocket.h"
+
+XmppSocket::XmppSocket(bool tls) : tls_(tls) {
+ cricket::Thread* pth = cricket::Thread::Current();
+ cricket::AsyncSocket* socket =
+ pth->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+#ifdef FEATURE_ENABLE_SSL
+ if (tls_)
+ socket = new cricket::SChannelAdapter(socket);
+#endif
+ cricket_socket_ = socket;
+ cricket_socket_->SignalReadEvent.connect(this, &XmppSocket::OnReadEvent);
+ cricket_socket_->SignalWriteEvent.connect(this, &XmppSocket::OnWriteEvent);
+ cricket_socket_->SignalConnectEvent.connect(this,
+ &XmppSocket::OnConnectEvent);
+ state_ = buzz::AsyncSocket::STATE_CLOSED;
+}
+
+XmppSocket::~XmppSocket() {
+ Close();
+ delete cricket_socket_;
+}
+
+void XmppSocket::OnReadEvent(cricket::AsyncSocket * socket) {
+ SignalRead();
+}
+
+void XmppSocket::OnWriteEvent(cricket::AsyncSocket * socket) {
+ // Write bytes if there are any
+ while (buffer_.Length() != 0) {
+ int written = cricket_socket_->Send(buffer_.Data(), buffer_.Length());
+ if (written > 0) {
+ buffer_.Shift(written);
+ continue;
+ }
+ if (!cricket_socket_->IsBlocking())
+ LOG(LS_ERROR) << "Send error: " << cricket_socket_->GetError();
+ return;
+ }
+}
+
+void XmppSocket::OnConnectEvent(cricket::AsyncSocket * socket) {
+#if defined(FEATURE_ENABLE_SSL)
+ if (state_ == buzz::AsyncSocket::STATE_TLS_CONNECTING) {
+ state_ = buzz::AsyncSocket::STATE_TLS_OPEN;
+ SignalSSLConnected();
+ OnWriteEvent(cricket_socket_);
+ return;
+ }
+#endif // !defined(FEATURE_ENABLE_SSL)
+ state_ = buzz::AsyncSocket::STATE_OPEN;
+ SignalConnected();
+}
+
+buzz::AsyncSocket::State XmppSocket::state() {
+ return state_;
+}
+
+buzz::AsyncSocket::Error XmppSocket::error() {
+ return buzz::AsyncSocket::ERROR_NONE;
+}
+
+bool XmppSocket::Connect(const cricket::SocketAddress& addr) {
+ if (cricket_socket_->Connect(addr) < 0) {
+ return cricket_socket_->IsBlocking();
+ }
+ return true;
+}
+
+bool XmppSocket::Read(char * data, size_t len, size_t* len_read) {
+ int read = cricket_socket_->Recv(data, len);
+ if (read > 0) {
+ *len_read = (size_t)read;
+ return true;
+ }
+ return false;
+}
+
+bool XmppSocket::Write(const char * data, size_t len) {
+ buffer_.WriteBytes(data, len);
+ OnWriteEvent(cricket_socket_);
+ return true;
+}
+
+bool XmppSocket::Close() {
+ if (state_ != buzz::AsyncSocket::STATE_OPEN)
+ return false;
+ if (cricket_socket_->Close() == 0) {
+ state_ = buzz::AsyncSocket::STATE_CLOSED;
+ SignalClosed();
+ return true;
+ }
+ return false;
+}
+
+bool XmppSocket::StartTls(const std::string & domainname) {
+#if defined(FEATURE_ENABLE_SSL)
+ if (!tls_)
+ return false;
+ cricket::SChannelAdapter * ssl =
+ static_cast<cricket::SChannelAdapter *>(cricket_socket_);
+ ssl->set_ignore_bad_cert(true);
+ if (ssl->StartSSL(domainname.c_str(), false) != 0)
+ return false;
+ state_ = buzz::AsyncSocket::STATE_TLS_CONNECTING;
+ return true;
+#else // !defined(FEATURE_ENABLE_SSL)
+ return false;
+#endif // !defined(FEATURE_ENABLE_SSL)
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h
new file mode 100644
index 00000000..f6c53d7d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppsocket.h
@@ -0,0 +1,63 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPSOCKET_H_
+#define _XMPPSOCKET_H_
+
+#include "talk/base/asyncsocket.h"
+#include "talk/base/bytebuffer.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/asyncsocket.h"
+
+extern cricket::AsyncSocket* cricket_socket_;
+
+class XmppSocket : public buzz::AsyncSocket, public sigslot::has_slots<> {
+public:
+ XmppSocket(bool tls);
+ ~XmppSocket();
+
+ virtual buzz::AsyncSocket::State state();
+ virtual buzz::AsyncSocket::Error error();
+
+ virtual bool Connect(const cricket::SocketAddress& addr);
+ virtual bool Read(char * data, size_t len, size_t* len_read);
+ virtual bool Write(const char * data, size_t len);
+ virtual bool Close();
+ virtual bool StartTls(const std::string & domainname);
+
+private:
+ void OnReadEvent(cricket::AsyncSocket * socket);
+ void OnWriteEvent(cricket::AsyncSocket * socket);
+ void OnConnectEvent(cricket::AsyncSocket * socket);
+
+ cricket::AsyncSocket * cricket_socket_;
+ buzz::AsyncSocket::State state_;
+ cricket::ByteBuffer buffer_;
+ bool tls_;
+};
+
+#endif // _XMPPSOCKET_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc
new file mode 100644
index 00000000..7a1b2a13
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.cc
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/examples/login/xmppthread.h"
+#include "talk/examples/login/xmppauth.h"
+
+namespace {
+
+const uint32 MSG_LOGIN = 1;
+const uint32 MSG_DISCONNECT = 2;
+
+struct LoginData: public cricket::MessageData {
+ LoginData(const buzz::XmppClientSettings& s) : xcs(s) {}
+ virtual ~LoginData() {}
+
+ buzz::XmppClientSettings xcs;
+};
+
+} // namespace
+
+XmppThread::XmppThread() {
+ pump_ = new XmppPump(this);
+}
+
+XmppThread::~XmppThread() {
+ delete pump_;
+}
+
+void XmppThread::Loop(int cms) {
+ Thread::Loop(cms);
+}
+
+void XmppThread::Login(const buzz::XmppClientSettings& xcs) {
+ Post(this, MSG_LOGIN, new LoginData(xcs));
+}
+
+void XmppThread::Disconnect() {
+ Post(this, MSG_DISCONNECT);
+}
+
+void XmppThread::OnStateChange(buzz::XmppEngine::State state) {
+}
+
+void XmppThread::OnMessage(cricket::Message* pmsg) {
+ if (pmsg->message_id == MSG_LOGIN) {
+ assert(pmsg->pdata);
+ LoginData* data = reinterpret_cast<LoginData*>(pmsg->pdata);
+ pump_->DoLogin(data->xcs, new XmppSocket(false), new XmppAuth());
+ delete data;
+ } else if (pmsg->message_id == MSG_DISCONNECT) {
+ pump_->DoDisconnect();
+ } else {
+ assert(false);
+ }
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h
new file mode 100644
index 00000000..533a2376
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/examples/login/xmppthread.h
@@ -0,0 +1,57 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPTHREAD_H_
+#define _XMPPTHREAD_H_
+
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/base/thread.h"
+#include "talk/examples/login/xmpppump.h"
+#include "talk/examples/login/xmppsocket.h"
+#include <iostream>
+
+class XmppThread:
+ public cricket::Thread, XmppPumpNotify, cricket::MessageHandler {
+public:
+ XmppThread();
+ ~XmppThread();
+
+ buzz::XmppClient* client() { return pump_->client(); }
+
+ void Loop(int cms);
+
+ void Login(const buzz::XmppClientSettings & xcs);
+ void Disconnect();
+
+private:
+ XmppPump* pump_;
+
+ void OnStateChange(buzz::XmppEngine::State state);
+ void OnMessage(cricket::Message* pmsg);
+};
+
+#endif // _XMPPTHREAD_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am
new file mode 100644
index 00000000..c935a6bd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=base client
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am
new file mode 100644
index 00000000..6cc30f15
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/Makefile.am
@@ -0,0 +1,47 @@
+## Does not compile with final
+KDE_OPTIONS = nofinal
+
+libcricketp2pbase_la_SOURCES = stun.cc \
+ port.cc \
+ udpport.cc \
+ tcpport.cc \
+ helpers.cc \
+ sessionmanager.cc \
+ session.cc \
+ p2psocket.cc \
+ relayport.cc \
+ stunrequest.cc \
+ stunport.cc \
+ socketmanager.cc
+
+noinst_HEADERS = candidate.h \
+ portallocator.h \
+ relayport.h \
+ session.h \
+ sessionmessage.h \
+ stunport.h \
+ tcpport.h \
+ helpers.h \
+ port.h \
+ sessionid.h \
+ socketmanager.h \
+ stunrequest.h \
+ udpport.h \
+ p2psocket.h \
+ sessiondescription.h \
+ sessionmanager.h \
+ stun.h \
+ relayserver.h \
+ stunserver.h
+
+AM_CPPFLAGS = -DPOSIX $(all_includes) -I$(srcdir)/../../..
+
+bin_PROGRAMS = relayserver stunserver
+relayserver_SOURCES = relayserver.cc relayserver_main.cc
+relayserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread
+stunserver_SOURCES = stunserver.cc stunserver_main.cc
+stunserver_LDADD = ../../base/libcricketbase.la libcricketp2pbase.la -lpthread
+
+noinst_LTLIBRARIES = libcricketp2pbase.la
+
+DEFAULT_INCLUDES = -I$(srcdir)/../../..
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h
new file mode 100644
index 00000000..c2f974b8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/candidate.h
@@ -0,0 +1,118 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CANDIDATE_H_
+#define _CANDIDATE_H_
+
+#include <string>
+#include <sstream>
+#include "talk/base/socketaddress.h"
+
+namespace cricket {
+
+// Candidate for ICE based connection discovery.
+
+class Candidate {
+public:
+
+ const std::string & name() const { return name_; }
+ void set_name(const std::string & name) { name_ = name; }
+
+ const std::string & protocol() const { return protocol_; }
+ void set_protocol(const std::string & protocol) { protocol_ = protocol; }
+
+ const SocketAddress & address() const { return address_; }
+ void set_address(const SocketAddress & address) { address_ = address; }
+
+ const float preference() const { return preference_; }
+ void set_preference(const float preference) { preference_ = preference; }
+ const std::string preference_str() const {
+ std::ostringstream ost;
+ ost << preference_;
+ return ost.str();
+ }
+ void set_preference_str(const std::string & preference) {
+ std::istringstream ist(preference);
+ ist >> preference_;
+ }
+
+ const std::string & username() const { return username_; }
+ void set_username(const std::string & username) { username_ = username; }
+
+ const std::string & password() const { return password_; }
+ void set_password(const std::string & password) { password_ = password; }
+
+ const std::string & type() const { return type_; }
+ void set_type(const std::string & type) { type_ = type; }
+
+ const std::string & network_name() const { return network_name_; }
+ void set_network_name(const std::string & network_name) {
+ network_name_ = network_name;
+ }
+
+ // Candidates in a new generation replace those in the old generation.
+ uint32 generation() const { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+ const std::string generation_str() const {
+ std::ostringstream ost;
+ ost << generation_;
+ return ost.str();
+ }
+ void set_generation_str(const std::string& str) {
+ std::istringstream ist(str);
+ ist >> generation_;
+ }
+
+ // Determines whether this candidate is equivalent to the given one.
+ bool IsEquivalent(const Candidate& c) const {
+ // We ignore the network name, since that is just debug information, and
+ // the preference, since that should be the same if the rest is (and it's
+ // a float so equality checking is always worrisome).
+ return (name_ == c.name_) &&
+ (protocol_ == c.protocol_) &&
+ (address_ == c.address_) &&
+ (username_ == c.username_) &&
+ (password_ == c.password_) &&
+ (type_ == c.type_) &&
+ (generation_ == c.generation_);
+ }
+
+private:
+ std::string name_;
+ std::string protocol_;
+ SocketAddress address_;
+ float preference_;
+ std::string username_;
+ std::string password_;
+ std::string type_;
+ std::string network_name_;
+ uint32 generation_;
+};
+
+} // namespace cricket
+
+#endif // _CANDIDATE_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc
new file mode 100644
index 00000000..83e02a27
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.cc
@@ -0,0 +1,129 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/helpers.h"
+#include "talk/base/jtime.h"
+#include <cstdlib>
+#include <cassert>
+
+// TODO: Change this implementation to use OpenSSL's RAND_bytes. That will
+// give cryptographically random values on all platforms.
+
+#ifdef WIN32
+#include <time.h>
+#include <windows.h>
+#endif
+
+namespace cricket {
+
+static long g_seed = 1L;
+
+int GetRandom() {
+ return ((g_seed = g_seed * 214013L + 2531011L) >> 16) & 0x7fff;
+}
+
+void SetRandomSeed(unsigned long seed)
+{
+ g_seed = (long)seed;
+}
+
+static bool s_initrandom;
+
+void InitRandom(const char *client_unique, size_t len) {
+ s_initrandom = true;
+
+ // Hash this string - unique per client
+
+ uint32 hash = 0;
+ if (client_unique != NULL) {
+ for (int i = 0; i < (int)len; i++)
+ hash = ((hash << 2) + hash) + client_unique[i];
+ }
+
+ // Now initialize the seed against a high resolution
+ // counter
+
+#ifdef WIN32
+ LARGE_INTEGER big;
+ QueryPerformanceCounter(&big);
+ SetRandomSeed(big.LowPart ^ hash);
+#else
+ SetRandomSeed(Time() ^ hash);
+#endif
+}
+
+const char BASE64[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
+// Generates a random string of the given length. We generate base64 values so
+// that they will be printable, though that's not necessary.
+
+std::string CreateRandomString(int len) {
+ // Random number generator should of been initialized!
+ assert(s_initrandom);
+ if (!s_initrandom)
+ InitRandom(0, 0);
+
+ std::string str;
+ for (int i = 0; i < len; i++)
+#if defined(_MSC_VER) && _MSC_VER < 1300
+ str.insert(str.end(), BASE64[GetRandom() & 63]);
+#else
+ str.push_back(BASE64[GetRandom() & 63]);
+#endif
+ return str;
+}
+
+uint32 CreateRandomId() {
+ uint8 b1 = (uint8)(GetRandom() & 255);
+ uint8 b2 = (uint8)(GetRandom() & 255);
+ uint8 b3 = (uint8)(GetRandom() & 255);
+ uint8 b4 = (uint8)(GetRandom() & 255);
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+}
+
+bool IsBase64Char(char ch) {
+ return (('A' <= ch) && (ch <= 'Z')) ||
+ (('a' <= ch) && (ch <= 'z')) ||
+ (('0' <= ch) && (ch <= '9')) ||
+ (ch == '+') || (ch == '/');
+}
+
+bool IsBase64Encoded(const std::string& str) {
+ for (size_t i = 0; i < str.size(); ++i) {
+ if (!IsBase64Char(str.at(i)))
+ return false;
+ }
+ return true;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h
new file mode 100644
index 00000000..1c8dfa7f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/helpers.h
@@ -0,0 +1,51 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HELPERS_H__
+#define __HELPERS_H__
+
+#include "talk/base/basictypes.h"
+#include <string>
+
+namespace cricket {
+
+// srand initializer
+void InitRandom(const char *client_unique, size_t len);
+
+// Generates a (cryptographically) random string of the given length.
+std::string CreateRandomString(int length);
+
+// Generates a random id
+uint32 CreateRandomId();
+
+// Determines whether the given string consists entirely of valid base64
+// encoded characters.
+bool IsBase64Encoded(const std::string& str);
+
+} // namespace cricket
+
+#endif // __HELPERS_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc
new file mode 100644
index 00000000..eb53efeb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.cc
@@ -0,0 +1,910 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Description of the P2PSocket class in P2PSocket.h
+//
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include <iostream>
+#include <cassert>
+#include "talk/base/logging.h"
+#include "talk/p2p/base/p2psocket.h"
+#include <errno.h>
+namespace {
+
+// messages for queuing up work for ourselves
+const uint32 MSG_SORT = 1;
+const uint32 MSG_PING = 2;
+const uint32 MSG_ALLOCATE = 3;
+
+// When the socket is unwritable, we will use 10 Kbps (ignoring IP+UDP headers)
+// for pinging. When the socket is writable, we will use only 1 Kbps because
+// we don't want to degrade the quality on a modem. These numbers should work
+// well on a 28.8K modem, which is the slowest connection on which the voice
+// quality is reasonable at all.
+static const uint32 PING_PACKET_SIZE = 60 * 8;
+static const uint32 WRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 1000; // 480ms
+static const uint32 UNWRITABLE_DELAY = 1000 * PING_PACKET_SIZE / 10000;// 50ms
+
+// If there is a current writable connection, then we will also try hard to
+// make sure it is pinged at this rate.
+static const uint32 MAX_CURRENT_WRITABLE_DELAY = 900; // 2*WRITABLE_DELAY - bit
+
+// The minimum improvement in MOS that justifies a switch.
+static const double kMinImprovement = 10;
+
+// Amount of time that we wait when *losing* writability before we try doing
+// another allocation.
+static const int kAllocateDelay = 1 * 1000; // 1 second
+
+// We will try creating a new allocator from scratch after a delay of this
+// length without becoming writable (or timing out).
+static const int kAllocatePeriod = 20 * 1000; // 20 seconds
+
+cricket::Port::CandidateOrigin GetOrigin(cricket::Port* port,
+ cricket::Port* origin_port) {
+ if (!origin_port)
+ return cricket::Port::ORIGIN_MESSAGE;
+ else if (port == origin_port)
+ return cricket::Port::ORIGIN_THIS_PORT;
+ else
+ return cricket::Port::ORIGIN_OTHER_PORT;
+}
+
+// Compares two connections based only on static information about them.
+int CompareConnectionCandidates(cricket::Connection* a,
+ cricket::Connection* b) {
+ // Combine local and remote preferences
+ assert(a->local_candidate().preference() == a->port()->preference());
+ assert(b->local_candidate().preference() == b->port()->preference());
+ double a_pref = a->local_candidate().preference()
+ * a->remote_candidate().preference();
+ double b_pref = b->local_candidate().preference()
+ * b->remote_candidate().preference();
+
+ // Now check combined preferences. Lower values get sorted last.
+ if (a_pref > b_pref)
+ return 1;
+ if (a_pref < b_pref)
+ return -1;
+
+ return 0;
+}
+
+// Compare two connections based on their writability and static preferences.
+int CompareConnections(cricket::Connection *a, cricket::Connection *b) {
+ // Sort based on write-state. Better states have lower values.
+ if (a->write_state() < b->write_state())
+ return 1;
+ if (a->write_state() > b->write_state())
+ return -1;
+
+ // Compare the candidate information.
+ return CompareConnectionCandidates(a, b);
+}
+
+// Wraps the comparison connection into a less than operator that puts higher
+// priority writable connections first.
+class ConnectionCompare {
+public:
+ bool operator()(const cricket::Connection *ca,
+ const cricket::Connection *cb) {
+ cricket::Connection* a = const_cast<cricket::Connection*>(ca);
+ cricket::Connection* b = const_cast<cricket::Connection*>(cb);
+
+ // Compare first on writability and static preferences.
+ int cmp = CompareConnections(a, b);
+ if (cmp > 0)
+ return true;
+ if (cmp < 0)
+ return false;
+
+ // Otherwise, sort based on latency estimate.
+ return a->rtt() < b->rtt();
+
+ // Should we bother checking for the last connection that last received
+ // data? It would help rendezvous on the connection that is also receiving
+ // packets.
+ //
+ // TODO: Yes we should definitely do this. The TCP protocol gains
+ // efficiency by being used bidirectionally, as opposed to two separate
+ // unidirectional streams. This test should probably occur before
+ // comparison of local prefs (assuming combined prefs are the same). We
+ // need to be careful though, not to bounce back and forth with both sides
+ // trying to rendevous with the other.
+ }
+};
+
+// Determines whether we should switch between two connections, based first on
+// static preferences and then (if those are equal) on latency estimates.
+bool ShouldSwitch(cricket::Connection* a_conn, cricket::Connection* b_conn) {
+ if (a_conn == b_conn)
+ return false;
+
+ if ((a_conn == NULL) || (b_conn == NULL)) // don't think the latter should happen
+ return true;
+
+ int prefs_cmp = CompareConnections(a_conn, b_conn);
+ if (prefs_cmp < 0)
+ return true;
+ if (prefs_cmp > 0)
+ return false;
+
+ return b_conn->rtt() <= a_conn->rtt() + kMinImprovement;
+}
+
+} // unnamed namespace
+
+namespace cricket {
+
+P2PSocket::P2PSocket(const std::string &name, PortAllocator *allocator)
+: worker_thread_(Thread::Current()), name_(name), allocator_(allocator),
+ error_(0), state_(STATE_CONNECTING), waiting_for_signaling_(false),
+ best_connection_(NULL), pinging_started_(false), sort_dirty_(false),
+ was_writable_(false), was_timed_out_(true) {
+}
+
+P2PSocket::~P2PSocket() {
+ assert(worker_thread_ == Thread::Current());
+
+ thread()->Clear(this);
+
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
+ delete allocator_sessions_[i];
+}
+
+// Add the allocator session to our list so that we know which sessions
+// are still active.
+void P2PSocket::AddAllocatorSession(PortAllocatorSession* session) {
+ session->set_generation(static_cast<uint32>(allocator_sessions_.size()));
+ allocator_sessions_.push_back(session);
+
+ // We now only want to apply new candidates that we receive to the ports
+ // created by this new session because these are replacing those of the
+ // previous sessions.
+ ports_.clear();
+
+ session->SignalPortReady.connect(this, &P2PSocket::OnPortReady);
+ session->SignalCandidatesReady.connect(this, &P2PSocket::OnCandidatesReady);
+ session->GetInitialPorts();
+ if (pinging_started_)
+ session->StartGetAllPorts();
+}
+
+// Go into the state of processing candidates, and running in general
+void P2PSocket::StartProcessingCandidates() {
+ assert(worker_thread_ == Thread::Current());
+
+ // Kick off an allocator session
+ OnAllocate();
+
+ // Start pinging as the ports come in.
+ thread()->Post(this, MSG_PING);
+}
+
+// Reset the socket, clear up any previous allocations and start over
+void P2PSocket::Reset() {
+ assert(worker_thread_ == Thread::Current());
+
+ // Get rid of all the old allocators. This should clean up everything.
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i)
+ delete allocator_sessions_[i];
+
+ allocator_sessions_.clear();
+ ports_.clear();
+ connections_.clear();
+ best_connection_ = NULL;
+
+ // Forget about all of the candidates we got before.
+ remote_candidates_.clear();
+
+ // Revert to the connecting state.
+ set_state(STATE_CONNECTING);
+
+ // Reinitialize the rest of our state.
+ waiting_for_signaling_ = false;
+ pinging_started_ = false;
+ sort_dirty_ = false;
+ was_writable_ = false;
+ was_timed_out_ = true;
+
+ // Start a new allocator.
+ OnAllocate();
+
+ // Start pinging as the ports come in.
+ thread()->Clear(this);
+ thread()->Post(this, MSG_PING);
+}
+
+// A new port is available, attempt to make connections for it
+void P2PSocket::OnPortReady(PortAllocatorSession *session, Port* port) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Set in-effect options on the new port
+ for (OptionMap::const_iterator it = options_.begin(); it != options_.end(); ++it) {
+ int val = port->SetOption(it->first, it->second);
+ if (val < 0) {
+ LOG(WARNING) << "SetOption(" << it->first << ", " << it->second << ") failed: " << port->GetError();
+ }
+ }
+
+ // Remember the ports and candidates, and signal that candidates are ready.
+ // The session will handle this, and send an initiate/accept/modify message
+ // if one is pending.
+
+ ports_.push_back(port);
+ port->SignalUnknownAddress.connect(this, &P2PSocket::OnUnknownAddress);
+ port->SignalDestroyed.connect(this, &P2PSocket::OnPortDestroyed);
+
+ // Attempt to create a connection from this new port to all of the remote
+ // candidates that we were given so far.
+
+ std::vector<RemoteCandidate>::iterator iter;
+ for (iter = remote_candidates_.begin(); iter != remote_candidates_.end();
+ ++iter)
+ CreateConnection(port, *iter, iter->origin_port(), false);
+
+ SortConnections();
+}
+
+// A new candidate is available, let listeners know
+void P2PSocket::OnCandidatesReady(PortAllocatorSession *session,
+ const std::vector<Candidate>& candidates) {
+ SignalCandidatesReady(this, candidates);
+}
+
+// Handle stun packets
+void P2PSocket::OnUnknownAddress(Port *port,
+ const SocketAddress &address,
+ StunMessage *stun_msg,
+ const std::string &remote_username) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Port has received a valid stun packet from an address that no Connection
+ // is currently available for. See if the remote user name is in the remote
+ // candidate list. If it isn't return error to the stun request.
+
+ const Candidate *candidate = NULL;
+ std::vector<RemoteCandidate>::iterator it;
+ for (it = remote_candidates_.begin(); it != remote_candidates_.end(); ++it) {
+ if ((*it).username() == remote_username) {
+ candidate = &(*it);
+ break;
+ }
+ }
+ if (candidate == NULL) {
+ // Don't know about this username, the request is bogus
+ // This sometimes happens if a binding response comes in before the ACCEPT
+ // message. It is totally valid; the retry state machine will try again.
+
+ port->SendBindingErrorResponse(stun_msg, address,
+ STUN_ERROR_STALE_CREDENTIALS, STUN_ERROR_REASON_STALE_CREDENTIALS);
+ delete stun_msg;
+ return;
+ }
+
+ // Check for connectivity to this address. Create connections
+ // to this address across all local ports. First, add this as a new remote
+ // address
+
+ Candidate new_remote_candidate = *candidate;
+ new_remote_candidate.set_address(address);
+ //new_remote_candidate.set_protocol(port->protocol());
+
+ // This remote username exists. Now create connections using this candidate,
+ // and resort
+
+ if (CreateConnections(new_remote_candidate, port, true)) {
+ // Send the pinger a successful stun response.
+ port->SendBindingResponse(stun_msg, address);
+
+ // Update the list of connections since we just added another. We do this
+ // after sending the response since it could (in principle) delete the
+ // connection in question.
+ SortConnections();
+ } else {
+ // Hopefully this won't occur, because changing a destination address
+ // shouldn't cause a new connection to fail
+ assert(false);
+ port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR,
+ STUN_ERROR_REASON_SERVER_ERROR);
+ }
+
+ delete stun_msg;
+}
+
+// We received a candidate from the other side, make connections so we
+// can try to use these remote candidates with our local candidates.
+void P2PSocket::AddRemoteCandidates(
+ const std::vector<Candidate> &remote_candidates) {
+ assert(worker_thread_ == Thread::Current());
+
+ // The remote candidates have come in. Remember them and start to establish
+ // connections
+
+ std::vector<Candidate>::const_iterator it;
+ for (it = remote_candidates.begin(); it != remote_candidates.end(); ++it)
+ CreateConnections(*it, NULL, false);
+
+ // Resort the connections
+
+ SortConnections();
+}
+
+// Creates connections from all of the ports that we care about to the given
+// remote candidate. The return value is true iff we created a connection from
+// the origin port.
+bool P2PSocket::CreateConnections(const Candidate &remote_candidate,
+ Port* origin_port,
+ bool readable) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Add a new connection for this candidate to every port that allows such a
+ // connection (i.e., if they have compatible protocols) and that does not
+ // already have a connection to an equivalent candidate. We must be careful
+ // to make sure that the origin port is included, even if it was pruned,
+ // since that may be the only port that can create this connection.
+
+ bool created = false;
+
+ std::vector<Port *>::reverse_iterator it;
+ for (it = ports_.rbegin(); it != ports_.rend(); ++it) {
+ if (CreateConnection(*it, remote_candidate, origin_port, readable)) {
+ if (*it == origin_port)
+ created = true;
+ }
+ }
+
+ if ((origin_port != NULL) &&
+ find(ports_.begin(), ports_.end(), origin_port) == ports_.end()) {
+ if (CreateConnection(origin_port, remote_candidate, origin_port, readable))
+ created = true;
+ }
+
+ // Remember this remote candidate so that we can add it to future ports.
+ RememberRemoteCandidate(remote_candidate, origin_port);
+
+ return created;
+}
+
+// Setup a connection object for the local and remote candidate combination.
+// And then listen to connection object for changes.
+bool P2PSocket::CreateConnection(Port* port,
+ const Candidate& remote_candidate,
+ Port* origin_port,
+ bool readable) {
+ // Look for an existing connection with this remote address. If one is not
+ // found, then we can create a new connection for this address.
+ Connection* connection = port->GetConnection(remote_candidate.address());
+ if (connection != NULL) {
+ // It is not legal to try to change any of the parameters of an existing
+ // connection; however, the other side can send a duplicate candidate.
+ if (!remote_candidate.IsEquivalent(connection->remote_candidate())) {
+ LOG(INFO) << "Attempt to change a remote candidate";
+ return false;
+ }
+ } else {
+ Port::CandidateOrigin origin = GetOrigin(port, origin_port);
+ connection = port->CreateConnection(remote_candidate, origin);
+ if (!connection)
+ return false;
+
+ connections_.push_back(connection);
+ connection->SignalReadPacket.connect(this, &P2PSocket::OnReadPacket);
+ connection->SignalStateChange.connect(this, &P2PSocket::OnConnectionStateChange);
+ connection->SignalDestroyed.connect(this, &P2PSocket::OnConnectionDestroyed);
+ }
+
+ // If we are readable, it is because we are creating this in response to a
+ // ping from the other side. This will cause the state to become readable.
+ if (readable)
+ connection->ReceivedPing();
+
+ return true;
+}
+
+// Maintain our remote candidate list, adding this new remote one.
+void P2PSocket::RememberRemoteCandidate(const Candidate& remote_candidate,
+ Port* origin_port) {
+ // Remove any candidates whose generation is older than this one. The
+ // presence of a new generation indicates that the old ones are not useful.
+ uint32 i = 0;
+ while (i < remote_candidates_.size()) {
+ if (remote_candidates_[i].generation() < remote_candidate.generation()) {
+ remote_candidates_.erase(remote_candidates_.begin() + i);
+ LOG(INFO) << "Pruning candidate from old generation: "
+ << remote_candidates_[i].address().ToString();
+
+ } else {
+ i += 1;
+ }
+ }
+
+ // Make sure this candidate is not a duplicate.
+ for (uint32 i = 0; i < remote_candidates_.size(); ++i) {
+ if (remote_candidates_[i].IsEquivalent(remote_candidate)) {
+ LOG(INFO) << "Duplicate candidate: "
+ << remote_candidate.address().ToString();
+ return;
+ }
+ }
+
+ // Try this candidate for all future ports.
+ remote_candidates_.push_back(RemoteCandidate(remote_candidate, origin_port));
+
+ // We have some candidates from the other side, we are now serious about
+ // this connection. Let's do the StartGetAllPorts thing.
+ if (!pinging_started_) {
+ pinging_started_ = true;
+ for (size_t i = 0; i < allocator_sessions_.size(); ++i) {
+ if (!allocator_sessions_[i]->IsGettingAllPorts())
+ allocator_sessions_[i]->StartGetAllPorts();
+ }
+ }
+}
+
+// Send data to the other side, using our best connection
+int P2PSocket::Send(const char *data, size_t len) {
+ // This can get called on any thread that is convenient to write from!
+ if (best_connection_ == NULL) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = best_connection_->Send(data, len);
+ if (sent <= 0) {
+ assert(sent < 0);
+ error_ = best_connection_->GetError();
+ }
+ return sent;
+}
+
+// Monitor connection states
+void P2PSocket::UpdateConnectionStates() {
+ uint32 now = Time();
+
+ // We need to copy the list of connections since some may delete themselves
+ // when we call UpdateState.
+ for (uint32 i = 0; i < connections_.size(); ++i)
+ connections_[i]->UpdateState(now);
+}
+
+// Prepare for best candidate sorting
+void P2PSocket::RequestSort() {
+ if (!sort_dirty_) {
+ worker_thread_->Post(this, MSG_SORT);
+ sort_dirty_ = true;
+ }
+}
+
+// Sort the available connections to find the best one. We also monitor
+// the number of available connections and the current state so that we
+// can possibly kick off more allocators (for more connections).
+void P2PSocket::SortConnections() {
+ assert(worker_thread_ == Thread::Current());
+
+ // Make sure the connection states are up-to-date since this affects how they
+ // will be sorted.
+ UpdateConnectionStates();
+
+ // Any changes after this point will require a re-sort.
+ sort_dirty_ = false;
+
+ // Get a list of the networks that we are using.
+ std::set<Network*> networks;
+ for (uint32 i = 0; i < connections_.size(); ++i)
+ networks.insert(connections_[i]->port()->network());
+
+ // Find the best alternative connection by sorting. It is important to note
+ // that amongst equal preference, writable connections, this will choose the
+ // one whose estimated latency is lowest. So it is the only one that we
+ // need to consider switching to.
+
+ ConnectionCompare cmp;
+ std::stable_sort(connections_.begin(), connections_.end(), cmp);
+ Connection* top_connection = NULL;
+ if (connections_.size() > 0)
+ top_connection = connections_[0];
+
+ // If necessary, switch to the new choice.
+ if (ShouldSwitch(best_connection_, top_connection))
+ SwitchBestConnectionTo(top_connection);
+
+ // We can prune any connection for which there is a writable connection on
+ // the same network with better or equal prefences. We leave those with
+ // better preference just in case they become writable later (at which point,
+ // we would prune out the current best connection). We leave connections on
+ // other networks because they may not be using the same resources and they
+ // may represent very distinct paths over which we can switch.
+ std::set<Network*>::iterator network;
+ for (network = networks.begin(); network != networks.end(); ++network) {
+ Connection* primier = GetBestConnectionOnNetwork(*network);
+ if (!primier || (primier->write_state() != Connection::STATE_WRITABLE))
+ continue;
+
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if ((connections_[i] != primier) &&
+ (connections_[i]->port()->network() == *network) &&
+ (CompareConnectionCandidates(primier, connections_[i]) >= 0)) {
+ connections_[i]->Prune();
+ }
+ }
+ }
+
+ // Count the number of connections in the various states.
+
+ int writable = 0;
+ int write_connect = 0;
+ int write_timeout = 0;
+
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ switch (connections_[i]->write_state()) {
+ case Connection::STATE_WRITABLE:
+ ++writable;
+ break;
+ case Connection::STATE_WRITE_CONNECT:
+ ++write_connect;
+ break;
+ case Connection::STATE_WRITE_TIMEOUT:
+ ++write_timeout;
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ if (writable > 0) {
+ HandleWritable();
+ } else if (write_connect > 0) {
+ HandleNotWritable();
+ } else {
+ HandleAllTimedOut();
+ }
+
+ // Notify of connection state change
+ SignalConnectionMonitor(this);
+}
+
+// Track the best connection, and let listeners know
+void P2PSocket::SwitchBestConnectionTo(Connection* conn) {
+ best_connection_ = conn;
+ if (best_connection_)
+ SignalConnectionChanged(this,
+ best_connection_->remote_candidate().address());
+}
+
+// We checked the status of our connections and we had at least one that
+// was writable, go into the writable state.
+void P2PSocket::HandleWritable() {
+ //
+ // One or more connections writable!
+ //
+ if (state_ != STATE_WRITABLE) {
+ for (uint32 i = 0; i < allocator_sessions_.size(); ++i) {
+ if (allocator_sessions_[i]->IsGettingAllPorts()) {
+ allocator_sessions_[i]->StopGetAllPorts();
+ }
+ }
+
+ // Stop further allocations.
+ thread()->Clear(this, MSG_ALLOCATE);
+ }
+
+ // We're writable, obviously we aren't timed out
+ was_writable_ = true;
+ was_timed_out_ = false;
+ set_state(STATE_WRITABLE);
+}
+
+// We checked the status of our connections and we didn't have any that
+// were writable, go into the connecting state (kick off a new allocator
+// session).
+void P2PSocket::HandleNotWritable() {
+ //
+ // No connections are writable but not timed out!
+ //
+ if (was_writable_) {
+ // If we were writable, let's kick off an allocator session immediately
+ was_writable_ = false;
+ OnAllocate();
+ }
+
+ // We were connecting, obviously not ALL timed out.
+ was_timed_out_ = false;
+ set_state(STATE_CONNECTING);
+}
+
+// We checked the status of our connections and not only weren't they writable
+// but they were also timed out, we really need a new allocator.
+void P2PSocket::HandleAllTimedOut() {
+ //
+ // No connections... all are timed out!
+ //
+ if (!was_timed_out_) {
+ // We weren't timed out before, so kick off an allocator now (we'll still
+ // be in the fully timed out state until the allocator actually gives back
+ // new ports)
+ OnAllocate();
+ }
+
+ // NOTE: we start was_timed_out_ in the true state so that we don't get
+ // another allocator created WHILE we are in the process of building up
+ // our first allocator.
+ was_timed_out_ = true;
+ was_writable_ = false;
+ set_state(STATE_CONNECTING);
+}
+
+// If we have a best connection, return it, otherwise return top one in the
+// list (later we will mark it best).
+Connection* P2PSocket::GetBestConnectionOnNetwork(Network* network) {
+ // If the best connection is on this network, then it wins.
+ if (best_connection_ && (best_connection_->port()->network() == network))
+ return best_connection_;
+
+ // Otherwise, we return the top-most in sorted order.
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (connections_[i]->port()->network() == network)
+ return connections_[i];
+ }
+
+ return NULL;
+}
+
+// Handle any queued up requests
+void P2PSocket::OnMessage(Message *pmsg) {
+ if (pmsg->message_id == MSG_SORT)
+ OnSort();
+ else if (pmsg->message_id == MSG_PING)
+ OnPing();
+ else if (pmsg->message_id == MSG_ALLOCATE)
+ OnAllocate();
+ else
+ assert(false);
+}
+
+// Handle queued up sort request
+void P2PSocket::OnSort() {
+ // Resort the connections based on the new statistics.
+ SortConnections();
+}
+
+// Handle queued up ping request
+void P2PSocket::OnPing() {
+ // Make sure the states of the connections are up-to-date (since this affects
+ // which ones are pingable).
+ UpdateConnectionStates();
+
+ // Find the oldest pingable connection and have it do a ping.
+ Connection* conn = FindNextPingableConnection();
+ if (conn)
+ conn->Ping(Time());
+
+ // Post ourselves a message to perform the next ping.
+ uint32 delay = (state_ == STATE_WRITABLE) ? WRITABLE_DELAY : UNWRITABLE_DELAY;
+ thread()->PostDelayed(delay, this, MSG_PING);
+}
+
+// Is the connection in a state for us to even consider pinging the other side?
+bool P2PSocket::IsPingable(Connection* conn) {
+ // An unconnected connection cannot be written to at all, so pinging is out
+ // of the question.
+ if (!conn->connected())
+ return false;
+
+ if (state_ == STATE_WRITABLE) {
+ // If we are writable, then we only want to ping connections that could be
+ // better than this one, i.e., the ones that were not pruned.
+ return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT);
+ } else {
+ // If we are not writable, then we need to try everything that might work.
+ // This includes both connections that do not have write timeout as well as
+ // ones that do not have read timeout. A connection could be readable but
+ // be in write-timeout if we pruned it before. Since the other side is
+ // still pinging it, it very well might still work.
+ return (conn->write_state() != Connection::STATE_WRITE_TIMEOUT) ||
+ (conn->read_state() != Connection::STATE_READ_TIMEOUT);
+ }
+}
+
+// Returns the next pingable connection to ping. This will be the oldest
+// pingable connection unless we have a writable connection that is past the
+// maximum acceptable ping delay.
+Connection* P2PSocket::FindNextPingableConnection() {
+ uint32 now = Time();
+ if (best_connection_ &&
+ (best_connection_->write_state() == Connection::STATE_WRITABLE) &&
+ (best_connection_->last_ping_sent()
+ + MAX_CURRENT_WRITABLE_DELAY <= now)) {
+ return best_connection_;
+ }
+
+ Connection* oldest_conn = NULL;
+ uint32 oldest_time = 0xFFFFFFFF;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (IsPingable(connections_[i])) {
+ if (connections_[i]->last_ping_sent() < oldest_time) {
+ oldest_time = connections_[i]->last_ping_sent();
+ oldest_conn = connections_[i];
+ }
+ }
+ }
+ return oldest_conn;
+}
+
+// return the number of "pingable" connections
+uint32 P2PSocket::NumPingableConnections() {
+ uint32 count = 0;
+ for (uint32 i = 0; i < connections_.size(); ++i) {
+ if (IsPingable(connections_[i]))
+ count += 1;
+ }
+ return count;
+}
+
+// When a connection's state changes, we need to figure out who to use as
+// the best connection again. It could have become usable, or become unusable.
+void P2PSocket::OnConnectionStateChange(Connection *connection) {
+ assert(worker_thread_ == Thread::Current());
+
+ // We have to unroll the stack before doing this because we may be changing
+ // the state of connections while sorting.
+ RequestSort();
+}
+
+// When a connection is removed, edit it out, and then update our best
+// connection.
+void P2PSocket::OnConnectionDestroyed(Connection *connection) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Remove this connection from the list.
+ std::vector<Connection*>::iterator iter =
+ find(connections_.begin(), connections_.end(), connection);
+ assert(iter != connections_.end());
+ connections_.erase(iter);
+
+ LOG(INFO) << "Removed connection from p2p socket: "
+ << static_cast<int>(connections_.size()) << " remaining";
+
+ // If this is currently the best connection, then we need to pick a new one.
+ // The call to SortConnections will pick a new one. It looks at the current
+ // best connection in order to avoid switching between fairly similar ones.
+ // Since this connection is no longer an option, we can just set best to NULL
+ // and re-choose a best assuming that there was no best connection.
+ if (best_connection_ == connection) {
+ SwitchBestConnectionTo(NULL);
+ RequestSort();
+ }
+}
+
+// When a port is destroyed remove it from our list of ports to use for
+// connection attempts.
+void P2PSocket::OnPortDestroyed(Port* port) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Remove this port from the list (if we didn't drop it already).
+ std::vector<Port*>::iterator iter = find(ports_.begin(), ports_.end(), port);
+ if (iter != ports_.end())
+ ports_.erase(iter);
+
+ LOG(INFO) << "Removed port from p2p socket: "
+ << static_cast<int>(ports_.size()) << " remaining";
+}
+
+// We data is available, let listeners know
+void P2PSocket::OnReadPacket(Connection *connection,
+ const char *data, size_t len) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Let the client know of an incoming packet
+
+ SignalReadPacket(this, data, len);
+}
+
+// return socket name
+const std::string &P2PSocket::name() const {
+ return name_;
+}
+
+// return socket error value
+int P2PSocket::GetError() {
+ return error_;
+}
+
+// return a reference to the list of connections
+const std::vector<Connection *>& P2PSocket::connections() {
+ return connections_;
+}
+
+// Set options on ourselves is simply setting options on all of our available
+// port objects.
+int P2PSocket::SetOption(Socket::Option opt, int value) {
+ OptionMap::iterator it = options_.find(opt);
+ if (it == options_.end()) {
+ options_.insert(std::make_pair(opt, value));
+ } else if (it->second == value) {
+ return 0;
+ } else {
+ it->second = value;
+ }
+
+ for (uint32 i = 0; i < ports_.size(); ++i) {
+ int val = ports_[i]->SetOption(opt, value);
+ if (val < 0) {
+ // Because this also occurs deferred, probably no point in reporting an error
+ LOG(WARNING) << "SetOption(" << opt << ", " << value << ") failed: " << ports_[i]->GetError();
+ }
+ }
+ return 0;
+}
+
+// returns the current state
+P2PSocket::State P2PSocket::state() {
+ return state_;
+}
+
+// Set the current state, and let listeners know when it changes
+void P2PSocket::set_state(P2PSocket::State state) {
+ assert(worker_thread_ == Thread::Current());
+ if (state != state_) {
+ state_ = state;
+ SignalState(this, state);
+ }
+}
+
+// Time for a new allocator, lets make sure we have a signalling channel
+// to communicate candidates through first.
+void P2PSocket::OnAllocate() {
+ // Allocation timer went off
+ waiting_for_signaling_ = true;
+ SignalRequestSignaling();
+}
+
+// When the signalling channel is ready, we can really kick off the allocator
+void P2PSocket::OnSignalingReady() {
+ if (waiting_for_signaling_) {
+ waiting_for_signaling_ = false;
+ AddAllocatorSession(allocator_->CreateSession(name_));
+ thread()->PostDelayed(kAllocatePeriod, this, MSG_ALLOCATE);
+ }
+}
+
+// return the current best connection writable state.
+bool P2PSocket::writable() {
+ assert(worker_thread_ == Thread::Current());
+
+ if (best_connection_ == NULL)
+ return false;
+ return best_connection_->write_state() == Connection::STATE_WRITABLE;
+}
+
+// return the worker thread
+Thread *P2PSocket::thread() {
+ return worker_thread_;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h
new file mode 100644
index 00000000..32eb1f6d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/p2psocket.h
@@ -0,0 +1,164 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// P2PSocket wraps up the state management of the connection between two
+// P2P clients. Clients have candidate ports for connecting, and connections
+// which are combinations of candidates from each end (Alice and Bob each
+// have candidates, one candidate from Alice and one candidate from Bob are
+// used to make a connection, repeat to make many connections).
+//
+// When all of the available connections become invalid (non-writable), we
+// kick off a process of determining more candidates and more connections.
+//
+#ifndef _CRICKET_P2P_BASE_P2PSOCKET_H_
+#define _CRICKET_P2P_BASE_P2PSOCKET_H_
+
+#include <vector>
+#include <string>
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/portallocator.h"
+
+namespace cricket {
+
+// Adds the port on which the candidate originated.
+class RemoteCandidate: public Candidate {
+ public:
+ RemoteCandidate(const Candidate& c, Port* origin_port)
+ : Candidate(c), origin_port_(origin_port) {}
+
+ Port* origin_port() { return origin_port_; }
+
+ private:
+ Port* origin_port_;
+};
+
+// P2PSocket manages the candidates and connection process to keep two P2P
+// clients connected to each other.
+class P2PSocket : public MessageHandler, public sigslot::has_slots<> {
+ public:
+ enum State {
+ STATE_CONNECTING = 0, // establishing writability
+ STATE_WRITABLE, // connected - ready for writing
+ };
+
+ P2PSocket(const std::string &name, PortAllocator *allocator);
+ virtual ~P2PSocket();
+
+ // Typically SocketManager calls these
+
+ const std::string &name() const;
+ void StartProcessingCandidates();
+ void AddRemoteCandidates(const std::vector<Candidate> &remote_candidates);
+ void OnSignalingReady();
+ void Reset();
+
+ // Typically the Session Client calls these
+
+ int Send(const char *data, size_t len);
+ int SetOption(Socket::Option opt, int value);
+ int GetError();
+
+ State state();
+ bool writable();
+ const std::vector<Connection *>& connections();
+ Connection* best_connection() { return best_connection_; }
+ Thread *thread();
+
+ sigslot::signal2<P2PSocket *, State> SignalState;
+ sigslot::signal0<> SignalRequestSignaling;
+ sigslot::signal3<P2PSocket *, const char *, size_t> SignalReadPacket;
+ sigslot::signal2<P2PSocket *, const SocketAddress &> SignalConnectionChanged;
+ sigslot::signal2<P2PSocket *, const std::vector<Candidate>&>
+ SignalCandidatesReady;
+ sigslot::signal1<P2PSocket *> SignalConnectionMonitor;
+
+ // Handler for internal messages.
+ virtual void OnMessage(Message *pmsg);
+
+ private:
+ void set_state(State state);
+ void UpdateConnectionStates();
+ void RequestSort();
+ void SortConnections();
+ void SwitchBestConnectionTo(Connection* conn);
+ void HandleWritable();
+ void HandleNotWritable();
+ void HandleAllTimedOut();
+ Connection* GetBestConnectionOnNetwork(Network* network);
+ bool CreateConnections(const Candidate &remote_candidate, Port* origin_port,
+ bool readable);
+ bool CreateConnection(Port* port, const Candidate& remote_candidate,
+ Port* origin_port, bool readable);
+ void RememberRemoteCandidate(const Candidate& remote_candidate,
+ Port* origin_port);
+ void OnUnknownAddress(Port *port, const SocketAddress &addr,
+ StunMessage *stun_msg,
+ const std::string &remote_username);
+ void OnPortReady(PortAllocatorSession *session, Port* port);
+ void OnCandidatesReady(PortAllocatorSession *session,
+ const std::vector<Candidate>& candidates);
+ void OnConnectionStateChange(Connection *connection);
+ void OnConnectionDestroyed(Connection *connection);
+ void OnPortDestroyed(Port* port);
+ void OnAllocate();
+ void OnReadPacket(Connection *connection, const char *data, size_t len);
+ void OnSort();
+ void OnPing();
+ bool IsPingable(Connection* conn);
+ Connection* FindNextPingableConnection();
+ uint32 NumPingableConnections();
+ PortAllocatorSession* allocator_session() {
+ return allocator_sessions_.back();
+ }
+ void AddAllocatorSession(PortAllocatorSession* session);
+
+ Thread *worker_thread_;
+ State state_;
+ bool waiting_for_signaling_;
+ int error_;
+ std::string name_;
+ PortAllocator *allocator_;
+ std::vector<PortAllocatorSession*> allocator_sessions_;
+ std::vector<Port *> ports_;
+ std::vector<Connection *> connections_;
+ Connection *best_connection_;
+ std::vector<RemoteCandidate> remote_candidates_;
+ bool pinging_started_; // indicates whether StartGetAllCandidates has been called
+ bool sort_dirty_; // indicates whether another sort is needed right now
+ bool was_writable_;
+ bool was_timed_out_;
+ typedef std::map<Socket::Option, int> OptionMap;
+ OptionMap options_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(P2PSocket);
+};
+
+} // namespace cricket
+
+#endif // _CRICKET_P2P_BASE_P2PSOCKET_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc
new file mode 100644
index 00000000..14549b5b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.cc
@@ -0,0 +1,869 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/base/socketadapters.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/base/scoped_ptr.h"
+#include <errno.h>
+#include <algorithm>
+#include <iostream>
+#include <cassert>
+#include <vector>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcmp;
+}
+#endif
+
+namespace {
+
+// The length of time we wait before timing out readability on a connection.
+const uint32 CONNECTION_READ_TIMEOUT = 30 * 1000; // 30 seconds
+
+// The length of time we wait before timing out writability on a connection.
+const uint32 CONNECTION_WRITE_TIMEOUT = 15 * 1000; // 15 seconds
+
+// The length of time we wait before we become unwritable.
+const uint32 CONNECTION_WRITE_CONNECT_TIMEOUT = 5 * 1000; // 5 seconds
+
+// The number of pings that must fail to respond before we become unwritable.
+const uint32 CONNECTION_WRITE_CONNECT_FAILURES = 5;
+
+// This is the length of time that we wait for a ping response to come back.
+const int CONNECTION_RESPONSE_TIMEOUT = 5 * 1000; // 5 seconds
+
+// Determines whether we have seen at least the given maximum number of
+// pings fail to have a response.
+inline bool TooManyFailures(
+ const std::vector<uint32>& pings_since_last_response,
+ uint32 maximum_failures,
+ uint32 rtt_estimate,
+ uint32 now) {
+
+ // If we haven't sent that many pings, then we can't have failed that many.
+ if (pings_since_last_response.size() < maximum_failures)
+ return false;
+
+ // Check if the window in which we would expect a response to the ping has
+ // already elapsed.
+ return pings_since_last_response[maximum_failures - 1] + rtt_estimate < now;
+}
+
+// Determines whether we have gone too long without seeing any response.
+inline bool TooLongWithoutResponse(
+ const std::vector<uint32>& pings_since_last_response,
+ uint32 maximum_time,
+ uint32 now) {
+
+ if (pings_since_last_response.size() == 0)
+ return false;
+
+ return pings_since_last_response[0] + maximum_time < now;
+}
+
+// We will restrict RTT estimates (when used for determining state) to be
+// within a reasonable range.
+const uint32 MINIMUM_RTT = 100; // 0.1 seconds
+const uint32 MAXIMUM_RTT = 3000; // 3 seconds
+
+// When we don't have any RTT data, we have to pick something reasonable. We
+// use a large value just in case the connection is really slow.
+const uint32 DEFAULT_RTT = MAXIMUM_RTT;
+
+// Computes our estimate of the RTT given the current estimate and the number
+// of data points on which it is based.
+inline uint32 ConservativeRTTEstimate(uint32 rtt, uint32 rtt_data_points) {
+ if (rtt_data_points == 0)
+ return DEFAULT_RTT;
+ else
+ return cricket::_max(MINIMUM_RTT, cricket::_min(MAXIMUM_RTT, 2 * rtt));
+}
+
+// Weighting of the old rtt value to new data.
+const int RTT_RATIO = 3; // 3 : 1
+
+// The delay before we begin checking if this port is useless.
+const int kPortTimeoutDelay = 30 * 1000; // 30 seconds
+
+const uint32 MSG_CHECKTIMEOUT = 1;
+const uint32 MSG_DELETE = 1;
+
+}
+
+namespace cricket {
+
+static const char * const PROTO_NAMES[PROTO_LAST+1] = { "udp", "tcp", "ssltcp" };
+
+const char * ProtoToString(ProtocolType proto) {
+ return PROTO_NAMES[proto];
+}
+
+bool StringToProto(const char * value, ProtocolType& proto) {
+ for (size_t i=0; i<=PROTO_LAST; ++i) {
+ if (strcmp(PROTO_NAMES[i], value) == 0) {
+ proto = static_cast<ProtocolType>(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+ProxyInfo Port::proxy_;
+
+Port::Port(Thread* thread, const std::string& type, SocketFactory* factory,
+ Network* network)
+ : thread_(thread), factory_(factory), type_(type), network_(network),
+ preference_(-1), lifetime_(LT_PRESTART) {
+
+ if (factory_ == NULL)
+ factory_ = thread_->socketserver();
+
+ set_username_fragment(CreateRandomString(16));
+ set_password(CreateRandomString(16));
+}
+
+Port::~Port() {
+ // Delete all of the remaining connections. We copy the list up front
+ // because each deletion will cause it to be modified.
+
+ std::vector<Connection*> list;
+
+ AddressMap::iterator iter = connections_.begin();
+ while (iter != connections_.end()) {
+ list.push_back(iter->second);
+ ++iter;
+ }
+
+ for (uint32 i = 0; i < list.size(); i++)
+ delete list[i];
+}
+
+Connection* Port::GetConnection(const SocketAddress& remote_addr) {
+ AddressMap::const_iterator iter = connections_.find(remote_addr);
+ if (iter != connections_.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+void Port::set_username_fragment(const std::string& username_fragment) {
+ username_frag_ = username_fragment;
+}
+
+void Port::set_password(const std::string& password) {
+ password_ = password;
+}
+
+void Port::add_address(const SocketAddress& address, const std::string& protocol, bool final) {
+ Candidate c;
+ c.set_name(name_);
+ c.set_type(type_);
+ c.set_protocol(protocol);
+ c.set_address(address);
+ c.set_preference(preference_);
+ c.set_username(username_frag_);
+ c.set_password(password_);
+ c.set_network_name(network_->name());
+ c.set_generation(generation_);
+ candidates_.push_back(c);
+
+ if (final)
+ SignalAddressReady(this);
+}
+
+void Port::AddConnection(Connection* conn) {
+ connections_[conn->remote_candidate().address()] = conn;
+ conn->SignalDestroyed.connect(this, &Port::OnConnectionDestroyed);
+ SignalConnectionCreated(this, conn);
+}
+
+void Port::OnReadPacket(
+ const char* data, size_t size, const SocketAddress& addr) {
+
+ // If this is an authenticated STUN request, then signal unknown address and
+ // send back a proper binding response.
+ StunMessage* msg;
+ std::string remote_username;
+ if (!GetStunMessage(data, size, addr, msg, remote_username)) {
+ LOG(LERROR) << "Received non-STUN packet from unknown address: "
+ << addr.ToString();
+ } else if (!msg) {
+ // STUN message handled already
+ } else if (msg->type() == STUN_BINDING_REQUEST) {
+ SignalUnknownAddress(this, addr, msg, remote_username);
+ } else {
+ LOG(LERROR) << "Received unexpected STUN message type (" << msg->type()
+ << ") from unknown address: " << addr.ToString();
+ delete msg;
+ }
+}
+
+void Port::SendBindingRequest(Connection* conn) {
+
+ // Construct the request message.
+
+ StunMessage request;
+ request.SetType(STUN_BINDING_REQUEST);
+ request.SetTransactionID(CreateRandomString(16));
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ std::string username = conn->remote_candidate().username();
+ username.append(username_frag_);
+ username_attr->CopyBytes(username.c_str(), (uint16)username.size());
+ request.AddAttribute(username_attr);
+
+ // Send the request message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ ByteBuffer buf;
+ request.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), conn->remote_candidate().address(), false);
+}
+
+bool Port::GetStunMessage(const char* data, size_t size,
+ const SocketAddress& addr, StunMessage *& msg,
+ std::string& remote_username) {
+ // NOTE: This could clearly be optimized to avoid allocating any memory.
+ // However, at the data rates we'll be looking at on the client side,
+ // this probably isn't worth worrying about.
+
+ msg = 0;
+
+ // Parse the request message. If the packet is not a complete and correct
+ // STUN message, then ignore it.
+ buzz::scoped_ptr<StunMessage> stun_msg(new StunMessage());
+ ByteBuffer buf(data, size);
+ if (!stun_msg->Read(&buf) || (buf.Length() > 0)) {
+ return false;
+ }
+
+ // The packet must include a username that either begins or ends with our
+ // fragment. It should begin with our fragment if it is a request and it
+ // should end with our fragment if it is a response.
+ const StunByteStringAttribute* username_attr =
+ stun_msg->GetByteString(STUN_ATTR_USERNAME);
+
+ int remote_frag_len = (username_attr ? username_attr->length() : 0);
+ remote_frag_len -= static_cast<int>(username_frag_.size());
+
+ if (stun_msg->type() == STUN_BINDING_REQUEST) {
+ if ((remote_frag_len < 0)
+ || (std::memcmp(username_attr->bytes(),
+ username_frag_.c_str(), username_frag_.size()) != 0)) {
+ LOG(LERROR) << "Received STUN request with bad username";
+ SendBindingErrorResponse(stun_msg.get(), addr, STUN_ERROR_BAD_REQUEST,
+ STUN_ERROR_REASON_BAD_REQUEST);
+ return true;
+ }
+
+ remote_username.assign(username_attr->bytes() + username_frag_.size(),
+ username_attr->bytes() + username_attr->length());
+ } else if ((stun_msg->type() == STUN_BINDING_RESPONSE)
+ || (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE)) {
+ if ((remote_frag_len < 0)
+ || (std::memcmp(username_attr->bytes() + remote_frag_len,
+ username_frag_.c_str(), username_frag_.size()) != 0)) {
+ LOG(LERROR) << "Received STUN response with bad username";
+ // Do not send error response to a response
+ return true;
+ }
+
+ remote_username.assign(username_attr->bytes(),
+ username_attr->bytes() + remote_frag_len);
+
+ if (stun_msg->type() == STUN_BINDING_ERROR_RESPONSE) {
+ if (const StunErrorCodeAttribute* error_code = stun_msg->GetErrorCode()) {
+ LOG(LERROR) << "Received STUN binding error:"
+ << " class=" << error_code->error_class()
+ << " number=" << error_code->number()
+ << " reason='" << error_code->reason() << "'";
+ // Return message to allow error-specific processing
+ } else {
+ LOG(LERROR) << "Received STUN error response with no error code";
+ // Drop corrupt message
+ return true;
+ }
+ }
+ } else {
+ LOG(LERROR) << "Received STUN packet with invalid type: "
+ << stun_msg->type();
+ return true;
+ }
+
+ // Return the STUN message found.
+ msg = stun_msg.release();
+ return true;
+}
+
+void Port::SendBindingResponse(
+ StunMessage* request, const SocketAddress& addr) {
+
+ assert(request->type() == STUN_BINDING_REQUEST);
+
+ // Retrieve the username from the request.
+ const StunByteStringAttribute* username_attr =
+ request->GetByteString(STUN_ATTR_USERNAME);
+ assert(username_attr);
+
+ // Fill in the response message.
+
+ StunMessage response;
+ response.SetType(STUN_BINDING_RESPONSE);
+ response.SetTransactionID(request->transaction_id());
+
+ StunByteStringAttribute* username2_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username2_attr->CopyBytes(username_attr->bytes(), username_attr->length());
+ response.AddAttribute(username2_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetPort(addr.port());
+ addr_attr->SetIP(addr.ip());
+ response.AddAttribute(addr_attr);
+
+ // Send the response message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ ByteBuffer buf;
+ response.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), addr, false);
+
+ // The fact that we received a successful request means that this connection
+ // (if one exists) should now be readable.
+ Connection* conn = GetConnection(addr);
+ assert(conn);
+ if (conn)
+ conn->ReceivedPing();
+}
+
+void Port::SendBindingErrorResponse(
+ StunMessage* request, const SocketAddress& addr, int error_code,
+ const std::string& reason) {
+
+ assert(request->type() == STUN_BINDING_REQUEST);
+
+ // Retrieve the username from the request. If it didn't have one, we
+ // shouldn't be responding at all.
+ const StunByteStringAttribute* username_attr =
+ request->GetByteString(STUN_ATTR_USERNAME);
+ assert(username_attr);
+
+ // Fill in the response message.
+
+ StunMessage response;
+ response.SetType(STUN_BINDING_ERROR_RESPONSE);
+ response.SetTransactionID(request->transaction_id());
+
+ StunByteStringAttribute* username2_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username2_attr->CopyBytes(username_attr->bytes(), username_attr->length());
+ response.AddAttribute(username2_attr);
+
+ StunErrorCodeAttribute* error_attr = StunAttribute::CreateErrorCode();
+ error_attr->SetErrorCode(error_code);
+ error_attr->SetReason(reason);
+ response.AddAttribute(error_attr);
+
+ // Send the response message.
+ // NOTE: If we wanted to, this is where we would add the HMAC.
+ ByteBuffer buf;
+ response.Write(&buf);
+ SendTo(buf.Data(), buf.Length(), addr, false);
+}
+
+AsyncPacketSocket * Port::CreatePacketSocket(ProtocolType proto) {
+ if (proto == PROTO_UDP) {
+ return new AsyncUDPSocket(factory_->CreateAsyncSocket(SOCK_DGRAM));
+ } else if ((proto == PROTO_TCP) || (proto == PROTO_SSLTCP)) {
+ AsyncSocket * socket = factory_->CreateAsyncSocket(SOCK_STREAM);
+ switch (proxy().type) {
+ case PROXY_NONE:
+ break;
+ case PROXY_SOCKS5:
+ socket = new AsyncSocksProxySocket(socket, proxy().address, proxy().username, proxy().password);
+ break;
+ case PROXY_HTTPS:
+ default:
+ socket = new AsyncHttpsProxySocket(socket, proxy().address, proxy().username, proxy().password);
+ break;
+ }
+ if (proto == PROTO_SSLTCP) {
+ socket = new AsyncSSLSocket(socket);
+ }
+ return new AsyncTCPSocket(socket);
+ } else {
+ LOG(INFO) << "Unknown protocol: " << proto;
+ return 0;
+ }
+}
+
+void Port::OnMessage(Message *pmsg) {
+ assert(pmsg->message_id == MSG_CHECKTIMEOUT);
+ assert(lifetime_ == LT_PRETIMEOUT);
+ lifetime_ = LT_POSTTIMEOUT;
+ CheckTimeout();
+}
+
+void Port::Start() {
+ // The port sticks around for a minimum lifetime, after which
+ // we destroy it when it drops to zero connections.
+ if (lifetime_ == LT_PRESTART) {
+ lifetime_ = LT_PRETIMEOUT;
+ thread_->PostDelayed(kPortTimeoutDelay, this, MSG_CHECKTIMEOUT);
+ } else {
+ LOG(WARNING) << "Port restart attempted";
+ }
+}
+
+void Port::OnConnectionDestroyed(Connection* conn) {
+ AddressMap::iterator iter = connections_.find(conn->remote_candidate().address());
+ assert(iter != connections_.end());
+ connections_.erase(iter);
+
+ CheckTimeout();
+}
+
+void Port::CheckTimeout() {
+ // If this port has no connections, then there's no reason to keep it around.
+ // When the connections time out (both read and write), they will delete
+ // themselves, so if we have any connections, they are either readable or
+ // writable (or still connecting).
+ if ((lifetime_ == LT_POSTTIMEOUT) && connections_.empty()) {
+ LOG(INFO) << "Destroying port: " << name_ << "-" << type_;
+ SignalDestroyed(this);
+ delete this;
+ }
+}
+
+// A ConnectionRequest is a simple STUN ping used to determine writability.
+class ConnectionRequest : public StunRequest {
+public:
+ ConnectionRequest(Connection* connection) : connection_(connection) {
+ }
+
+ virtual ~ConnectionRequest() {
+ }
+
+ virtual void Prepare(StunMessage* request) {
+ request->SetType(STUN_BINDING_REQUEST);
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ std::string username = connection_->remote_candidate().username();
+ username.append(connection_->port()->username_fragment());
+ username_attr->CopyBytes(username.c_str(), (uint16)username.size());
+ request->AddAttribute(username_attr);
+ }
+
+ virtual void OnResponse(StunMessage* response) {
+ connection_->OnConnectionRequestResponse(response, Elapsed());
+ }
+
+ virtual void OnErrorResponse(StunMessage* response) {
+ connection_->OnConnectionRequestErrorResponse(response, Elapsed());
+ }
+
+ virtual void OnTimeout() {
+ }
+
+ virtual int GetNextDelay() {
+ // Each request is sent only once. After a single delay , the request will
+ // time out.
+ timeout_ = true;
+ return CONNECTION_RESPONSE_TIMEOUT;
+ }
+
+private:
+ Connection* connection_;
+};
+
+//
+// Connection
+//
+
+Connection::Connection(Port* port, size_t index, const Candidate& remote_candidate)
+ : requests_(port->thread()), port_(port), local_candidate_index_(index),
+ remote_candidate_(remote_candidate), read_state_(STATE_READ_TIMEOUT),
+ write_state_(STATE_WRITE_CONNECT), connected_(true), pruned_(false),
+ rtt_(0), rtt_data_points_(0), last_ping_sent_(0), last_ping_received_(0),
+ recv_total_bytes_(0), recv_bytes_second_(0),
+ last_recv_bytes_second_time_((uint32)-1), last_recv_bytes_second_calc_(0),
+ sent_total_bytes_(0), sent_bytes_second_(0),
+ last_sent_bytes_second_time_((uint32)-1), last_sent_bytes_second_calc_(0) {
+
+ // Wire up to send stun packets
+ requests_.SignalSendPacket.connect(this, &Connection::OnSendStunPacket);
+}
+
+Connection::~Connection() {
+}
+
+const Candidate& Connection::local_candidate() const {
+ if (local_candidate_index_ < port_->candidates().size())
+ return port_->candidates()[local_candidate_index_];
+ assert(false);
+ static Candidate foo;
+ return foo;
+}
+
+void Connection::set_read_state(ReadState value) {
+ ReadState old_value = read_state_;
+ read_state_ = value;
+ if (value != old_value) {
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_write_state(WriteState value) {
+ WriteState old_value = write_state_;
+ write_state_ = value;
+ if (value != old_value) {
+ SignalStateChange(this);
+ CheckTimeout();
+ }
+}
+
+void Connection::set_connected(bool value) {
+ bool old_value = connected_;
+ connected_ = value;
+
+ // When connectedness is turned off, this connection is done.
+ if (old_value && !value)
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void Connection::OnSendStunPacket(const void* data, size_t size) {
+ port_->SendTo(data, size, remote_candidate_.address(), false);
+}
+
+void Connection::OnReadPacket(const char* data, size_t size) {
+ StunMessage* msg;
+ std::string remote_username;
+ const SocketAddress& addr(remote_candidate_.address());
+ if (!port_->GetStunMessage(data, size, addr, msg, remote_username)) {
+ // The packet did not parse as a valid STUN message
+
+ // If this connection is readable, then pass along the packet.
+ if (read_state_ == STATE_READABLE) {
+ // readable means data from this address is acceptable
+ // Send it on!
+
+ recv_total_bytes_ += size;
+ SignalReadPacket(this, data, size);
+
+ // If timed out sending writability checks, start up again
+ if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT))
+ set_write_state(STATE_WRITE_CONNECT);
+ } else {
+ // Not readable means the remote address hasn't send a valid
+ // binding request yet.
+
+ LOG(WARNING) << "Received non-STUN packet from an unreadable connection.";
+ }
+ } else if (!msg) {
+ // The packet was STUN, but was already handled
+ } else if (remote_username != remote_candidate_.username()) {
+ // Not destined this connection
+ LOG(LERROR) << "Received STUN packet on wrong address.";
+ if (msg->type() == STUN_BINDING_REQUEST) {
+ port_->SendBindingErrorResponse(msg, addr, STUN_ERROR_BAD_REQUEST,
+ STUN_ERROR_REASON_BAD_REQUEST);
+ }
+ delete msg;
+ } else {
+ // The packet is STUN, with the current username
+ // If this is a STUN request, then update the readable bit and respond.
+ // If this is a STUN response, then update the writable bit.
+
+ switch (msg->type()) {
+ case STUN_BINDING_REQUEST:
+ // Incoming, validated stun request from remote peer.
+ // This call will also set the connection readable.
+
+ port_->SendBindingResponse(msg, addr);
+
+ // If timed out sending writability checks, start up again
+ if (!pruned_ && (write_state_ == STATE_WRITE_TIMEOUT))
+ set_write_state(STATE_WRITE_CONNECT);
+ break;
+
+ case STUN_BINDING_RESPONSE:
+ case STUN_BINDING_ERROR_RESPONSE:
+ // Response from remote peer. Does it match request sent?
+ // This doesn't just check, it makes callbacks if transaction
+ // id's match
+ requests_.CheckResponse(msg);
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ // Done with the message; delete
+
+ delete msg;
+ }
+}
+
+void Connection::Prune() {
+ pruned_ = true;
+ requests_.Clear();
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void Connection::Destroy() {
+ set_read_state(STATE_READ_TIMEOUT);
+ set_write_state(STATE_WRITE_TIMEOUT);
+}
+
+void Connection::UpdateState(uint32 now) {
+ // Check the readable state.
+ //
+ // Since we don't know how many pings the other side has attempted, the best
+ // test we can do is a simple window.
+
+ if ((read_state_ == STATE_READABLE) &&
+ (last_ping_received_ + CONNECTION_READ_TIMEOUT <= now)) {
+ set_read_state(STATE_READ_TIMEOUT);
+ }
+
+ // Check the writable state. (The order of these checks is important.)
+ //
+ // Before becoming unwritable, we allow for a fixed number of pings to fail
+ // (i.e., receive no response). We also have to give the response time to
+ // get back, so we include a conservative estimate of this.
+ //
+ // Before timing out writability, we give a fixed amount of time. This is to
+ // allow for changes in network conditions.
+
+ uint32 rtt = ConservativeRTTEstimate(rtt_, rtt_data_points_);
+
+ if ((write_state_ == STATE_WRITABLE) &&
+ TooManyFailures(pings_since_last_response_,
+ CONNECTION_WRITE_CONNECT_FAILURES,
+ rtt,
+ now) &&
+ TooLongWithoutResponse(pings_since_last_response_,
+ CONNECTION_WRITE_CONNECT_TIMEOUT,
+ now)) {
+ set_write_state(STATE_WRITE_CONNECT);
+ }
+
+ if ((write_state_ == STATE_WRITE_CONNECT) &&
+ TooLongWithoutResponse(pings_since_last_response_,
+ CONNECTION_WRITE_TIMEOUT,
+ now)) {
+ set_write_state(STATE_WRITE_TIMEOUT);
+ }
+}
+
+void Connection::Ping(uint32 now) {
+ assert(connected_);
+ last_ping_sent_ = now;
+ pings_since_last_response_.push_back(now);
+ requests_.Send(new ConnectionRequest(this));
+}
+
+void Connection::ReceivedPing() {
+ last_ping_received_ = Time();
+ set_read_state(STATE_READABLE);
+}
+
+void Connection::OnConnectionRequestResponse(StunMessage *response, uint32 rtt) {
+ // We have a potentially valid reply from the remote address.
+ // The packet must include a username that ends with our fragment,
+ // since it is a response.
+
+ // Check exact message type
+ bool valid = true;
+ if (response->type() != STUN_BINDING_RESPONSE)
+ valid = false;
+
+ // Must have username attribute
+ const StunByteStringAttribute* username_attr =
+ response->GetByteString(STUN_ATTR_USERNAME);
+ if (valid) {
+ if (!username_attr) {
+ LOG(LERROR) << "Received likely STUN packet with no username";
+ valid = false;
+ }
+ }
+
+ // Length must be at least the size of our fragment (actually, should
+ // be bigger since our fragment is at the end!)
+ if (valid) {
+ if (username_attr->length() <= port_->username_fragment().size()) {
+ LOG(LERROR) << "Received likely STUN packet with short username";
+ valid = false;
+ }
+ }
+
+ // Compare our fragment with the end of the username - must be exact match
+ if (valid) {
+ std::string username_fragment = port_->username_fragment();
+ int offset = (int)(username_attr->length() - username_fragment.size());
+ if (std::memcmp(username_attr->bytes() + offset,
+ username_fragment.c_str(), username_fragment.size()) != 0) {
+ LOG(LERROR) << "Received STUN response with bad username";
+ valid = false;
+ }
+ }
+
+ if (valid) {
+ // Valid response. If we're not already, become writable. We may be
+ // bringing a pruned connection back to life, but if we don't really want
+ // it, we can always prune it again.
+ set_write_state(STATE_WRITABLE);
+
+ pings_since_last_response_.clear();
+ rtt_ = (RTT_RATIO * rtt_ + rtt) / (RTT_RATIO + 1);
+ rtt_data_points_ += 1;
+ }
+}
+
+void Connection::OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt) {
+ const StunErrorCodeAttribute* error = response->GetErrorCode();
+ uint32 error_code = error ? error->error_code() : STUN_ERROR_GLOBAL_FAILURE;
+
+ if ((error_code == STUN_ERROR_UNKNOWN_ATTRIBUTE)
+ || (error_code == STUN_ERROR_SERVER_ERROR)
+ || (error_code == STUN_ERROR_UNAUTHORIZED)) {
+ // Recoverable error, retry
+ } else if (error_code == STUN_ERROR_STALE_CREDENTIALS) {
+ // Race failure, retry
+ } else {
+ // This is not a valid connection.
+ set_connected(false);
+ }
+}
+
+void Connection::CheckTimeout() {
+ // If both read and write have timed out, then this connection can contribute
+ // no more to p2p socket unless at some later date readability were to come
+ // back. However, we gave readability a long time to timeout, so at this
+ // point, it seems fair to get rid of this connectoin.
+ if ((read_state_ == STATE_READ_TIMEOUT) &&
+ (write_state_ == STATE_WRITE_TIMEOUT)) {
+ port_->thread()->Post(this, MSG_DELETE);
+ }
+}
+
+void Connection::OnMessage(Message *pmsg) {
+ assert(pmsg->message_id == MSG_DELETE);
+
+ LOG(INFO) << "Destroying connection: from "
+ << local_candidate().address().ToString()
+ << " to " << remote_candidate_.address().ToString();
+
+ SignalDestroyed(this);
+ delete this;
+}
+
+size_t Connection::recv_bytes_second() {
+ // Snapshot bytes / second calculator
+
+ uint32 current_time = Time();
+ if (last_recv_bytes_second_time_ != (uint32)-1) {
+ int delta = TimeDiff(current_time, last_recv_bytes_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds_time = delta - fraction_time;
+ int fraction_bytes = (int)(recv_total_bytes_ - last_recv_bytes_second_calc_) * fraction_time / delta;
+ recv_bytes_second_ = (recv_total_bytes_ - last_recv_bytes_second_calc_ - fraction_bytes) * seconds_time / delta;
+ last_recv_bytes_second_time_ = current_time - fraction_time;
+ last_recv_bytes_second_calc_ = recv_total_bytes_ - fraction_bytes;
+ }
+ }
+ if (last_recv_bytes_second_time_ == (uint32)-1) {
+ last_recv_bytes_second_time_ = current_time;
+ last_recv_bytes_second_calc_ = recv_total_bytes_;
+ }
+
+ return recv_bytes_second_;
+}
+
+size_t Connection::recv_total_bytes() {
+ return recv_total_bytes_;
+}
+
+size_t Connection::sent_bytes_second() {
+ // Snapshot bytes / second calculator
+
+ uint32 current_time = Time();
+ if (last_sent_bytes_second_time_ != (uint32)-1) {
+ int delta = TimeDiff(current_time, last_sent_bytes_second_time_);
+ if (delta >= 1000) {
+ int fraction_time = delta % 1000;
+ int seconds_time = delta - fraction_time;
+ int fraction_bytes = (int)(sent_total_bytes_ - last_sent_bytes_second_calc_) * fraction_time / delta;
+ sent_bytes_second_ = (sent_total_bytes_ - last_sent_bytes_second_calc_ - fraction_bytes) * seconds_time / delta;
+ last_sent_bytes_second_time_ = current_time - fraction_time;
+ last_sent_bytes_second_calc_ = sent_total_bytes_ - fraction_bytes;
+ }
+ }
+ if (last_sent_bytes_second_time_ == (uint32)-1) {
+ last_sent_bytes_second_time_ = current_time;
+ last_sent_bytes_second_calc_ = sent_total_bytes_;
+ }
+
+ return sent_bytes_second_;
+}
+
+size_t Connection::sent_total_bytes() {
+ return sent_total_bytes_;
+}
+
+ProxyConnection::ProxyConnection(Port* port, size_t index, const Candidate& candidate)
+ : Connection(port, index, candidate), error_(0) {
+}
+
+int ProxyConnection::Send(const void* data, size_t size) {
+ if (write_state() != STATE_WRITABLE) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ int sent = port_->SendTo(data, size, remote_candidate_.address(), true);
+ if (sent <= 0) {
+ assert(sent < 0);
+ error_ = port_->GetError();
+ } else {
+ sent_total_bytes_ += sent;
+ }
+ return sent;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h
new file mode 100644
index 00000000..c22fad28
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/port.h
@@ -0,0 +1,367 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __PORT_H__
+#define __PORT_H__
+
+#include "talk/base/network.h"
+#include "talk/base/socketaddress.h"
+#include "talk/base/proxyinfo.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/stun.h"
+#include "talk/p2p/base/stunrequest.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace cricket {
+
+class Connection;
+class AsyncPacketSocket;
+
+enum ProtocolType { PROTO_UDP, PROTO_TCP, PROTO_SSLTCP, PROTO_LAST = PROTO_SSLTCP };
+const char * ProtoToString(ProtocolType proto);
+bool StringToProto(const char * value, ProtocolType& proto);
+
+struct ProtocolAddress {
+ SocketAddress address;
+ ProtocolType proto;
+
+ ProtocolAddress(const SocketAddress& a, ProtocolType p) : address(a), proto(p) { }
+};
+
+// Represents a local communication mechanism that can be used to create
+// connections to similar mechanisms of the other client. Subclasses of this
+// one add support for specific mechanisms like local UDP ports.
+class Port: public MessageHandler, public sigslot::has_slots<> {
+public:
+ Port(Thread* thread, const std::string &type, SocketFactory* factory,
+ Network* network);
+ virtual ~Port();
+
+ // The thread on which this port performs its I/O.
+ Thread* thread() { return thread_; }
+
+ // The factory used to create the sockets of this port.
+ SocketFactory* socket_factory() const { return factory_; }
+ void set_socket_factory(SocketFactory* factory) { factory_ = factory; }
+
+ // Each port is identified by a name (for debugging purposes).
+ const std::string& name() const { return name_; }
+ void set_name(const std::string& name) { name_ = name; }
+
+ // In order to establish a connection to this Port (so that real data can be
+ // sent through), the other side must send us a STUN binding request that is
+ // authenticated with this username and password.
+ const std::string& username_fragment() const { return username_frag_; }
+ const std::string& password() const { return password_; }
+
+ // A value in [0,1] that indicates the preference for this port versus other
+ // ports on this client. (Larger indicates more preference.)
+ float preference() const { return preference_; }
+ void set_preference(float preference) { preference_ = preference; }
+
+ // Identifies the port type.
+ //const std::string& protocol() const { return proto_; }
+ const std::string& type() const { return type_; }
+
+ // Identifies network that this port was allocated on.
+ Network* network() { return network_; }
+
+ // Identifies the generation that this port was created in.
+ uint32 generation() { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+
+ // PrepareAddress will attempt to get an address for this port that other
+ // clients can send to. It may take some time before the address is read.
+ // Once it is ready, we will send SignalAddressReady.
+ virtual void PrepareAddress() = 0;
+ sigslot::signal1<Port*> SignalAddressReady;
+ //const SocketAddress& address() const { return address_; }
+
+ // Provides all of the above information in one handy object.
+ const std::vector<Candidate>& candidates() const { return candidates_; }
+
+ // Returns a map containing all of the connections of this port, keyed by the
+ // remote address.
+ typedef std::map<SocketAddress, Connection*> AddressMap;
+ const AddressMap& connections() { return connections_; }
+
+ // Returns the connection to the given address or NULL if none exists.
+ Connection* GetConnection(const SocketAddress& remote_addr);
+
+ // Creates a new connection to the given address.
+ enum CandidateOrigin { ORIGIN_THIS_PORT, ORIGIN_OTHER_PORT, ORIGIN_MESSAGE };
+ virtual Connection* CreateConnection(const Candidate& remote_candidate, CandidateOrigin origin) = 0;
+
+ // Called each time a connection is created.
+ sigslot::signal2<Port*, Connection*> SignalConnectionCreated;
+
+ // Sends the given packet to the given address, provided that the address is
+ // that of a connection or an address that has sent to us already.
+ virtual int SendTo(
+ const void* data, size_t size, const SocketAddress& addr, bool payload) = 0;
+
+ // Indicates that we received a successful STUN binding request from an
+ // address that doesn't correspond to any current connection. To turn this
+ // into a real connection, call CreateConnection.
+ sigslot::signal4<Port*, const SocketAddress&, StunMessage*, const std::string&> SignalUnknownAddress;
+
+ // Sends a response message (normal or error) to the given request. One of
+ // these methods should be called as a response to SignalUnknownAddress.
+ // NOTE: You MUST call CreateConnection BEFORE SendBindingResponse.
+ void SendBindingResponse(StunMessage* request, const SocketAddress& addr);
+ void SendBindingErrorResponse(
+ StunMessage* request, const SocketAddress& addr, int error_code,
+ const std::string& reason);
+
+ // Indicates that errors occurred when performing I/O.
+ sigslot::signal2<Port*, int> SignalReadError;
+ sigslot::signal2<Port*, int> SignalWriteError;
+
+ // Functions on the underlying socket(s).
+ virtual int SetOption(Socket::Option opt, int value) = 0;
+ virtual int GetError() = 0;
+
+ static void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; }
+ static const ProxyInfo& proxy() { return proxy_; }
+
+ AsyncPacketSocket * CreatePacketSocket(ProtocolType proto);
+
+ virtual void OnMessage(Message *pmsg);
+
+ // Indicates to the port that its official use has now begun. This will
+ // start the timer that checks to see if the port is being used.
+ void Start();
+
+ // Signaled when this port decides to delete itself because it no longer has
+ // any usefulness.
+ sigslot::signal1<Port*> SignalDestroyed;
+
+protected:
+ Thread* thread_;
+ SocketFactory* factory_;
+ std::string type_;
+ Network* network_;
+ uint32 generation_;
+ std::string name_;
+ std::string username_frag_;
+ std::string password_;
+ float preference_;
+ std::vector<Candidate> candidates_;
+ AddressMap connections_;
+ enum Lifetime { LT_PRESTART, LT_PRETIMEOUT, LT_POSTTIMEOUT } lifetime_;
+
+ // Fills in the username fragment and password. These will be initially set
+ // in the constructor to random values. Subclasses can override, though.
+ void set_username_fragment(const std::string& username_fragment);
+ void set_password(const std::string& password);
+
+ // Fills in the local address of the port.
+ void add_address(const SocketAddress& address, const std::string& protocol, bool final = true);
+
+ // Adds the given connection to the list. (Deleting removes them.)
+ void AddConnection(Connection* conn);
+
+ // Called when a packet is received from an unknown address that is not
+ // currently a connection. If this is an authenticated STUN binding request,
+ // then we will signal the client.
+ void OnReadPacket(const char* data, size_t size, const SocketAddress& addr);
+
+ // Constructs a STUN binding request for the given connection and sends it.
+ void SendBindingRequest(Connection* conn);
+
+ // If the given data comprises a complete and correct STUN message then the
+ // return value is true, otherwise false. If the message username corresponds
+ // with this port's username fragment, msg will contain the parsed STUN
+ // message. Otherwise, the function may send a STUN response internally.
+ // remote_username contains the remote fragment of the STUN username.
+ bool GetStunMessage(const char* data, size_t size, const SocketAddress& addr,
+ StunMessage *& msg, std::string& remote_username);
+
+ friend class Connection;
+
+private:
+ // Called when one of our connections deletes itself.
+ void OnConnectionDestroyed(Connection* conn);
+
+ // Checks if this port is useless, and hence, should be destroyed.
+ void CheckTimeout();
+
+ static ProxyInfo proxy_;
+};
+
+// Represents a communication link between a port on the local client and a
+// port on the remote client.
+class Connection : public MessageHandler, public sigslot::has_slots<> {
+public:
+ virtual ~Connection();
+
+ // The local port where this connection sends and receives packets.
+ Port* port() { return port_; }
+ const Port* port() const { return port_; }
+
+ // Returns the description of the local port
+ virtual const Candidate& local_candidate() const;
+
+ // Returns the description of the remote port to which we communicate.
+ const Candidate& remote_candidate() const { return remote_candidate_; }
+
+ enum ReadState {
+ STATE_READABLE = 0, // we have received pings recently
+ STATE_READ_TIMEOUT = 1 // we haven't received pings in a while
+ };
+
+ ReadState read_state() const { return read_state_; }
+
+ enum WriteState {
+ STATE_WRITABLE = 0, // we have received ping responses recently
+ STATE_WRITE_CONNECT = 1, // we have had a few ping failures
+ STATE_WRITE_TIMEOUT = 2 // we have had a large number of ping failures
+ };
+
+ WriteState write_state() const { return write_state_; }
+
+ // Determines whether the connection has finished connecting. This can only
+ // be false for TCP connections.
+ bool connected() const { return connected_; }
+
+ // Estimate of the round-trip time over this connection.
+ uint32 rtt() const { return rtt_; }
+
+ size_t sent_total_bytes();
+ size_t sent_bytes_second();
+ size_t recv_total_bytes();
+ size_t recv_bytes_second();
+ sigslot::signal1<Connection*> SignalStateChange;
+
+ // Sent when the connection has decided that it is no longer of value. It
+ // will delete itself immediately after this call.
+ sigslot::signal1<Connection*> SignalDestroyed;
+
+ // The connection can send and receive packets asynchronously. This matches
+ // the interface of AsyncPacketSocket, which may use UDP or TCP under the covers.
+ virtual int Send(const void* data, size_t size) = 0;
+
+ // Error if Send() returns < 0
+ virtual int GetError() = 0;
+
+ sigslot::signal3<Connection*, const char*, size_t> SignalReadPacket;
+
+ // Called when a packet is received on this connection.
+ void OnReadPacket(const char* data, size_t size);
+
+ // Called when a connection is determined to be no longer useful to us. We
+ // still keep it around in case the other side wants to use it. But we can
+ // safely stop pinging on it and we can allow it to time out if the other
+ // side stops using it as well.
+ bool pruned() { return pruned_; }
+ void Prune();
+
+ // Makes the connection go away.
+ void Destroy();
+
+ // Checks that the state of this connection is up-to-date. The argument is
+ // the current time, which is compared against various timeouts.
+ void UpdateState(uint32 now);
+
+ // Called when this connection should try checking writability again.
+ uint32 last_ping_sent() { return last_ping_sent_; }
+ void Ping(uint32 now);
+
+ // Called whenever a valid ping is received on this connection. This is
+ // public because the connection intercepts the first ping for us.
+ void ReceivedPing();
+
+protected:
+ Port* port_;
+ size_t local_candidate_index_;
+ Candidate remote_candidate_;
+ ReadState read_state_;
+ WriteState write_state_;
+ bool connected_;
+ bool pruned_;
+ StunRequestManager requests_;
+ uint32 rtt_;
+ uint32 rtt_data_points_;
+ uint32 last_ping_sent_; // last time we sent a ping to the other side
+ uint32 last_ping_received_; // last time we received a ping from the other side
+ std::vector<uint32> pings_since_last_response_;
+
+ size_t recv_total_bytes_;
+ size_t recv_bytes_second_;
+ uint32 last_recv_bytes_second_time_;
+ size_t last_recv_bytes_second_calc_;
+
+ size_t sent_total_bytes_;
+ size_t sent_bytes_second_;
+ uint32 last_sent_bytes_second_time_;
+ size_t last_sent_bytes_second_calc_;
+
+ // Callbacks from ConnectionRequest
+ void OnConnectionRequestResponse(StunMessage *response, uint32 rtt);
+ void OnConnectionRequestErrorResponse(StunMessage *response, uint32 rtt);
+
+ // Called back when StunRequestManager has a stun packet to send
+ void OnSendStunPacket(const void* data, size_t size);
+
+ // Constructs a new connection to the given remote port.
+ Connection(Port* port, size_t index, const Candidate& candidate);
+
+ // Changes the state and signals if necessary.
+ void set_read_state(ReadState value);
+ void set_write_state(WriteState value);
+ void set_connected(bool value);
+
+ // Checks if this connection is useless, and hence, should be destroyed.
+ void CheckTimeout();
+
+ void OnMessage(Message *pmsg);
+
+ friend class Port;
+ friend class ConnectionRequest;
+};
+
+// ProxyConnection defers all the interesting work to the port
+
+class ProxyConnection : public Connection {
+public:
+ ProxyConnection(Port* port, size_t index, const Candidate& candidate);
+
+ virtual int Send(const void* data, size_t size);
+ virtual int GetError() { return error_; }
+
+private:
+ int error_;
+};
+
+} // namespace cricket
+
+#endif // __PORT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h
new file mode 100644
index 00000000..3246f29f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/portallocator.h
@@ -0,0 +1,91 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PORTALLOCATOR_H_
+#define _PORTALLOCATOR_H_
+
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/port.h"
+#include <string>
+#undef SetPort
+
+namespace cricket {
+
+// PortAllocator is responsible for allocating Port types for a given
+// P2PSocket. It also handles port freeing.
+//
+// Clients can override this class to control port allocation, including
+// what kinds of ports are allocated.
+
+class PortAllocatorSession : public sigslot::has_slots<> {
+public:
+ // Prepares an initial set of ports to try.
+ virtual void GetInitialPorts() = 0;
+
+ // Starts and stops the flow of additional ports to try.
+ virtual void StartGetAllPorts() = 0;
+ virtual void StopGetAllPorts() = 0;
+ virtual bool IsGettingAllPorts() = 0;
+
+ sigslot::signal2<PortAllocatorSession*, Port*> SignalPortReady;
+ sigslot::signal2<PortAllocatorSession*, const std::vector<Candidate>&> SignalCandidatesReady;
+
+ uint32 generation() { return generation_; }
+ void set_generation(uint32 generation) { generation_ = generation; }
+
+private:
+ uint32 generation_;
+};
+
+const uint32 PORTALLOCATOR_DISABLE_UDP = 0x01;
+const uint32 PORTALLOCATOR_DISABLE_STUN = 0x02;
+const uint32 PORTALLOCATOR_DISABLE_RELAY = 0x04;
+const uint32 PORTALLOCATOR_DISABLE_TCP = 0x08;
+const uint32 PORTALLOCATOR_ENABLE_SHAKER = 0x10;
+
+const uint32 kDefaultPortAllocatorFlags = 0;
+
+class PortAllocator {
+public:
+ PortAllocator() : flags_(kDefaultPortAllocatorFlags) {}
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name) = 0;
+
+ uint32 flags() const { return flags_; }
+ void set_flags(uint32 flags) { flags_ = flags; }
+
+ const ProxyInfo& proxy() const { return proxy_; }
+ void set_proxy(const ProxyInfo& proxy) { proxy_ = proxy; }
+
+protected:
+ uint32 flags_;
+ ProxyInfo proxy_;
+};
+
+} // namespace cricket
+
+#endif // _PORTALLOCATOR_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc
new file mode 100644
index 00000000..4ba12be3
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.cc
@@ -0,0 +1,640 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/base/asynctcpsocket.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/helpers.h"
+#include <iostream>
+#include <cassert>
+#ifdef OSX
+#include <errno.h>
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const int KEEPALIVE_DELAY = 10 * 60 * 1000;
+const int RETRY_DELAY = 50; // 50ms, from ICE spec
+const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
+
+const uint32 MSG_DISPOSE_SOCKET = 100; // needs to be more than ID used by Port
+typedef TypedMessageData<AsyncPacketSocket *> DisposeSocketData;
+
+class AsyncTCPSocket;
+
+// Manages a single connection to the relayserver. We aim to use each
+// connection for only a specific destination address so that we can avoid
+// wrapping every packet in a STUN send / data indication.
+class RelayEntry : public sigslot::has_slots<> {
+public:
+ RelayEntry(RelayPort* port, const SocketAddress& ext_addr, const SocketAddress& local_addr);
+ ~RelayEntry();
+
+ RelayPort* port() { return port_; }
+
+ const SocketAddress& address() { return ext_addr_; }
+ void set_address(const SocketAddress& addr) { ext_addr_ = addr; }
+
+ AsyncPacketSocket* socket() { return socket_; }
+
+ bool connected() { return connected_; }
+ void set_connected(bool connected) { connected_ = connected; }
+
+ bool locked() { return locked_; }
+
+ // Returns the last error on the socket of this entry.
+ int GetError() { return socket_->GetError(); }
+
+ // Sends the STUN requests to the server to initiate this connection.
+ void Connect();
+
+ // Called when this entry becomes connected. The address given is the one
+ // exposed to the outside world on the relay server.
+ void OnConnect(const SocketAddress& mapped_addr);
+
+ // Sends a packet to the given destination address using the socket of this
+ // entry. This will wrap the packet in STUN if necessary.
+ int SendTo(const void* data, size_t size, const SocketAddress& addr);
+
+ // Schedules a keep-alive allocate request.
+ void ScheduleKeepAlive();
+
+ void SetServerIndex(size_t sindex) { server_index_ = sindex; }
+ size_t ServerIndex() const { return server_index_; }
+
+ // Try a different server address
+ void HandleConnectFailure();
+
+private:
+ RelayPort* port_;
+ SocketAddress ext_addr_, local_addr_;
+ size_t server_index_;
+ AsyncPacketSocket* socket_;
+ bool connected_;
+ bool locked_;
+ StunRequestManager requests_;
+
+ // Called when a TCP connection is established or fails
+ void OnSocketConnect(AsyncTCPSocket* socket);
+ void OnSocketClose(AsyncTCPSocket* socket, int error);
+
+ // Called when a packet is received on this socket.
+ void OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+ // Called on behalf of a StunRequest to write data to the socket. This is
+ // already STUN intended for the server, so no wrapping is necessary.
+ void OnSendPacket(const void* data, size_t size);
+
+ // Sends the given data on the socket to the server with no wrapping. This
+ // returns the number of bytes written or -1 if an error occurred.
+ int SendPacket(const void* data, size_t size);
+};
+
+// Handles an allocate request for a particular RelayEntry.
+class AllocateRequest : public StunRequest {
+public:
+ AllocateRequest(RelayEntry* entry);
+ virtual ~AllocateRequest() {}
+
+ virtual void Prepare(StunMessage* request);
+
+ virtual int GetNextDelay();
+
+ virtual void OnResponse(StunMessage* response);
+ virtual void OnErrorResponse(StunMessage* response);
+ virtual void OnTimeout();
+
+private:
+ RelayEntry* entry_;
+ uint32 start_time_;
+};
+
+const std::string RELAY_PORT_TYPE("relay");
+
+RelayPort::RelayPort(
+ Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& local_addr, const std::string& username,
+ const std::string& password, const std::string& magic_cookie)
+ : Port(thread, RELAY_PORT_TYPE, factory, network), local_addr_(local_addr),
+ ready_(false), magic_cookie_(magic_cookie), error_(0) {
+
+ entries_.push_back(new RelayEntry(this, SocketAddress(), local_addr_));
+
+ set_username_fragment(username);
+ set_password(password);
+
+ if (magic_cookie_.size() == 0)
+ magic_cookie_.append(STUN_MAGIC_COOKIE_VALUE, 4);
+}
+
+RelayPort::~RelayPort() {
+ for (unsigned i = 0; i < entries_.size(); ++i)
+ delete entries_[i];
+ thread_->Clear(this);
+}
+
+void RelayPort::AddServerAddress(const ProtocolAddress& addr) {
+ // Since HTTP proxies usually only allow 443, let's up the priority on PROTO_SSLTCP
+ if ((addr.proto == PROTO_SSLTCP)
+ && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) {
+ server_addr_.push_front(addr);
+ } else {
+ server_addr_.push_back(addr);
+ }
+}
+
+void RelayPort::AddExternalAddress(const ProtocolAddress& addr) {
+ std::string proto_name = ProtoToString(addr.proto);
+ for (std::vector<Candidate>::const_iterator it = candidates().begin(); it != candidates().end(); ++it) {
+ if ((it->address() == addr.address) && (it->protocol() == proto_name)) {
+ LOG(INFO) << "Redundant relay address: " << proto_name << " @ " << addr.address.ToString();
+ return;
+ }
+ }
+ add_address(addr.address, proto_name, false);
+}
+
+void RelayPort::SetReady() {
+ if (!ready_) {
+ ready_ = true;
+ SignalAddressReady(this);
+ }
+}
+
+const ProtocolAddress * RelayPort::ServerAddress(size_t index) const {
+ if ((index >= 0) && (index < server_addr_.size()))
+ return &server_addr_[index];
+ return 0;
+}
+
+bool RelayPort::HasMagicCookie(const char* data, size_t size) {
+ if (size < 24 + magic_cookie_.size()) {
+ return false;
+ } else {
+ return 0 == std::memcmp(data + 24,
+ magic_cookie_.c_str(),
+ magic_cookie_.size());
+ }
+}
+
+void RelayPort::PrepareAddress() {
+ // We initiate a connect on the first entry. If this completes, it will fill
+ // in the server address as the address of this port.
+ assert(entries_.size() == 1);
+ entries_[0]->Connect();
+ ready_ = false;
+}
+
+Connection* RelayPort::CreateConnection(const Candidate& address, CandidateOrigin origin) {
+ // We only create connections to non-udp sockets if they are incoming on this port
+ if ((address.protocol() != "udp") && (origin != ORIGIN_THIS_PORT))
+ return 0;
+
+ // We don't support loopback on relays
+ if (address.type() == type())
+ return 0;
+
+ size_t index = 0;
+ for (size_t i = 0; i < candidates().size(); ++i) {
+ const Candidate& local = candidates()[i];
+ if (local.protocol() == address.protocol()) {
+ index = i;
+ break;
+ }
+ }
+
+ Connection * conn = new ProxyConnection(this, index, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int RelayPort::SendTo(const void* data,
+ size_t size,
+ const SocketAddress& addr, bool payload) {
+
+ // Try to find an entry for this specific address. Note that the first entry
+ // created was not given an address initially, so it can be set to the first
+ // address that comes along.
+
+ RelayEntry* entry = 0;
+
+ for (unsigned i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->address().IsAny() && payload) {
+ entry = entries_[i];
+ entry->set_address(addr);
+ break;
+ } else if (entries_[i]->address() == addr) {
+ entry = entries_[i];
+ break;
+ }
+ }
+
+ // If we did not find one, then we make a new one. This will not be useable
+ // until it becomes connected, however.
+ if (!entry && payload) {
+ entry = new RelayEntry(this, addr, local_addr_);
+ if (!entries_.empty()) {
+ // Use the same port to connect to relay server
+ entry->SetServerIndex(entries_[0]->ServerIndex());
+ }
+ entry->Connect();
+ entries_.push_back(entry);
+ }
+
+ // If the entry is connected, then we can send on it (though wrapping may
+ // still be necessary). Otherwise, we can't yet use this connection, so we
+ // default to the first one.
+ if (!entry || !entry->connected()) {
+ assert(!entries_.empty());
+ entry = entries_[0];
+ if (!entry->connected()) {
+ error_ = EWOULDBLOCK;
+ return SOCKET_ERROR;
+ }
+ }
+
+ // Send the actual contents to the server using the usual mechanism.
+ int sent = entry->SendTo(data, size, addr);
+ if (sent <= 0) {
+ assert(sent < 0);
+ error_ = entry->GetError();
+ return SOCKET_ERROR;
+ }
+
+ // The caller of the function is expecting the number of user data bytes,
+ // rather than the size of the packet.
+ return (int)size;
+}
+
+void RelayPort::OnMessage(Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_DISPOSE_SOCKET: {
+ DisposeSocketData * data = static_cast<DisposeSocketData *>(pmsg->pdata);
+ delete data->data();
+ delete data;
+ break; }
+ default:
+ Port::OnMessage(pmsg);
+ }
+}
+
+int RelayPort::SetOption(Socket::Option opt, int value) {
+ int result = 0;
+ for (unsigned i = 0; i < entries_.size(); ++i) {
+ if (entries_[i]->socket()->SetOption(opt, value) < 0) {
+ result = -1;
+ error_ = entries_[i]->socket()->GetError();
+ }
+ }
+ options_.push_back(OptionValue(opt, value));
+ return result;
+}
+
+int RelayPort::GetError() {
+ return error_;
+}
+
+void RelayPort::OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr) {
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+void RelayPort::DisposeSocket(AsyncPacketSocket * socket) {
+ thread_->Post(this, MSG_DISPOSE_SOCKET, new DisposeSocketData(socket));
+}
+
+RelayEntry::RelayEntry(RelayPort* port, const SocketAddress& ext_addr,
+ const SocketAddress& local_addr)
+ : port_(port), ext_addr_(ext_addr), local_addr_(local_addr), server_index_(0),
+ socket_(0), connected_(false), locked_(false), requests_(port->thread()) {
+
+ requests_.SignalSendPacket.connect(this, &RelayEntry::OnSendPacket);
+}
+
+RelayEntry::~RelayEntry() {
+ delete socket_;
+}
+
+void RelayEntry::Connect() {
+ assert(socket_ == 0);
+ const ProtocolAddress * ra = port()->ServerAddress(server_index_);
+ if (!ra) {
+ LOG(INFO) << "Out of relay server connections";
+ return;
+ }
+
+ LOG(INFO) << "Connecting to relay via " << ProtoToString(ra->proto) << " @ " << ra->address.ToString();
+
+ socket_ = port_->CreatePacketSocket(ra->proto);
+ assert(socket_ != 0);
+
+ socket_->SignalReadPacket.connect(this, &RelayEntry::OnReadPacket);
+ if (socket_->Bind(local_addr_) < 0)
+ LOG(INFO) << "bind: " << std::strerror(socket_->GetError());
+
+ for (unsigned i = 0; i < port_->options().size(); ++i)
+ socket_->SetOption(port_->options()[i].first, port_->options()[i].second);
+
+ if ((ra->proto == PROTO_TCP) || (ra->proto == PROTO_SSLTCP)) {
+ AsyncTCPSocket * tcp = static_cast<AsyncTCPSocket *>(socket_);
+ tcp->SignalClose.connect(this, &RelayEntry::OnSocketClose);
+ tcp->SignalConnect.connect(this, &RelayEntry::OnSocketConnect);
+ tcp->Connect(ra->address);
+ } else {
+ requests_.Send(new AllocateRequest(this));
+ }
+}
+
+void RelayEntry::OnConnect(const SocketAddress& mapped_addr) {
+ ProtocolType proto = PROTO_UDP;
+ LOG(INFO) << "Relay allocate succeeded: " << ProtoToString(proto) << " @ " << mapped_addr.ToString();
+ connected_ = true;
+
+ port_->AddExternalAddress(ProtocolAddress(mapped_addr, proto));
+ port_->SetReady();
+}
+
+int RelayEntry::SendTo(const void* data,
+ size_t size,
+ const SocketAddress& addr) {
+
+ // If this connection is locked to the address given, then we can send the
+ // packet with no wrapper.
+ if (locked_ && (ext_addr_ == addr))
+ return SendPacket(data, size);
+
+ // Otherwise, we must wrap the given data in a STUN SEND request so that we
+ // can communicate the destination address to the server.
+ //
+ // Note that we do not use a StunRequest here. This is because there is
+ // likely no reason to resend this packet. If it is late, we just drop it.
+ // The next send to this address will try again.
+
+ StunMessage request;
+ request.SetType(STUN_SEND_REQUEST);
+ request.SetTransactionID(CreateRandomString(16));
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(port_->magic_cookie().c_str(),
+ (uint16)port_->magic_cookie().size());
+ request.AddAttribute(magic_cookie_attr);
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username_attr->CopyBytes(port_->username_fragment().c_str(),
+ (uint16)port_->username_fragment().size());
+ request.AddAttribute(username_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_DESTINATION_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(addr.ip());
+ addr_attr->SetPort(addr.port());
+ request.AddAttribute(addr_attr);
+
+ // Attempt to lock
+ if (ext_addr_ == addr) {
+ StunUInt32Attribute* options_attr =
+ StunAttribute::CreateUInt32(STUN_ATTR_OPTIONS);
+ options_attr->SetValue(0x1);
+ request.AddAttribute(options_attr);
+ }
+
+ StunByteStringAttribute* data_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_DATA);
+ data_attr->CopyBytes(data, (uint16)size);
+ request.AddAttribute(data_attr);
+
+ // TODO: compute the HMAC.
+
+ ByteBuffer buf;
+ request.Write(&buf);
+
+ return SendPacket(buf.Data(), buf.Length());
+}
+
+void RelayEntry::ScheduleKeepAlive() {
+ requests_.SendDelayed(new AllocateRequest(this), KEEPALIVE_DELAY);
+}
+
+void RelayEntry::HandleConnectFailure() {
+ //if (GetMillisecondCount() - start_time_ > RETRY_TIMEOUT)
+ // return;
+ //ScheduleKeepAlive();
+
+ connected_ = false;
+ port()->DisposeSocket(socket_);
+ socket_ = 0;
+ server_index_ += 1;
+ Connect();
+}
+
+void RelayEntry::OnSocketConnect(AsyncTCPSocket* socket) {
+ assert(socket == socket_);
+ LOG(INFO) << "relay tcp connected to " << socket->GetRemoteAddress().ToString();
+ requests_.Send(new AllocateRequest(this));
+}
+
+void RelayEntry::OnSocketClose(AsyncTCPSocket* socket, int error) {
+ assert(socket == socket_);
+ PLOG(LERROR, error) << "relay tcp connect failed";
+ HandleConnectFailure();
+}
+
+void RelayEntry::OnReadPacket(const char* data,
+ size_t size,
+ const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ //assert(remote_addr == port_->server_addr()); TODO: are we worried about this?
+
+ // If the magic cookie is not present, then this is an unwrapped packet sent
+ // by the server, The actual remote address is the one we recorded.
+ if (!port_->HasMagicCookie(data, size)) {
+ if (locked_) {
+ port_->OnReadPacket(data, size, ext_addr_);
+ } else {
+ LOG(WARNING) << "Dropping packet: entry not locked";
+ }
+ return;
+ }
+
+ ByteBuffer buf(data, size);
+ StunMessage msg;
+ if (!msg.Read(&buf)) {
+ LOG(INFO) << "Incoming packet was not STUN";
+ return;
+ }
+
+ // The incoming packet should be a STUN ALLOCATE response, SEND response, or
+ // DATA indication.
+ if (requests_.CheckResponse(&msg)) {
+ return;
+ } else if (msg.type() == STUN_SEND_RESPONSE) {
+ if (const StunUInt32Attribute* options_attr = msg.GetUInt32(STUN_ATTR_OPTIONS)) {
+ if (options_attr->value() & 0x1) {
+ locked_ = true;
+ }
+ }
+ return;
+ } else if (msg.type() != STUN_DATA_INDICATION) {
+ LOG(INFO) << "Received BAD stun type from server: " << msg.type()
+ ;
+ return;
+ }
+
+ // This must be a data indication.
+
+ const StunAddressAttribute* addr_attr =
+ msg.GetAddress(STUN_ATTR_SOURCE_ADDRESS2);
+ if (!addr_attr) {
+ LOG(INFO) << "Data indication has no source address";
+ return;
+ } else if (addr_attr->family() != 1) {
+ LOG(INFO) << "Source address has bad family";
+ return;
+ }
+
+ SocketAddress remote_addr2(addr_attr->ip(), addr_attr->port());
+
+ const StunByteStringAttribute* data_attr = msg.GetByteString(STUN_ATTR_DATA);
+ if (!data_attr) {
+ LOG(INFO) << "Data indication has no data";
+ return;
+ }
+
+ // Process the actual data and remote address in the normal manner.
+ port_->OnReadPacket(data_attr->bytes(), data_attr->length(), remote_addr2);
+}
+
+void RelayEntry::OnSendPacket(const void* data, size_t size) {
+ SendPacket(data, size);
+}
+
+int RelayEntry::SendPacket(const void* data, size_t size) {
+ const ProtocolAddress * ra = port_->ServerAddress(server_index_);
+ if (!ra) {
+ socket_->SetError(ENOTCONN);
+ return SOCKET_ERROR;
+ }
+ int sent = socket_->SendTo(data, size, ra->address);
+ if (sent <= 0) {
+ LOG(LS_VERBOSE) << "sendto: " << std::strerror(socket_->GetError());
+ assert(sent < 0);
+ }
+ return sent;
+}
+
+AllocateRequest::AllocateRequest(RelayEntry* entry) : entry_(entry) {
+ start_time_ = GetMillisecondCount();
+}
+
+void AllocateRequest::Prepare(StunMessage* request) {
+ request->SetType(STUN_ALLOCATE_REQUEST);
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(
+ entry_->port()->magic_cookie().c_str(),
+ (uint16)entry_->port()->magic_cookie().size());
+ request->AddAttribute(magic_cookie_attr);
+
+ StunByteStringAttribute* username_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_USERNAME);
+ username_attr->CopyBytes(
+ entry_->port()->username_fragment().c_str(),
+ (uint16)entry_->port()->username_fragment().size());
+ request->AddAttribute(username_attr);
+}
+
+int AllocateRequest::GetNextDelay() {
+ int delay = 100 * _max(1 << count_, 2);
+ count_ += 1;
+ if (count_ == 5)
+ timeout_ = true;
+ return delay;
+}
+
+void AllocateRequest::OnResponse(StunMessage* response) {
+ const StunAddressAttribute* addr_attr =
+ response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ if (!addr_attr) {
+ LOG(INFO) << "Allocate response missing mapped address.";
+ } else if (addr_attr->family() != 1) {
+ LOG(INFO) << "Mapped address has bad family";
+ } else {
+ SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ entry_->OnConnect(addr);
+ }
+
+ // We will do a keep-alive regardless of whether this request suceeds.
+ // This should have almost no impact on network usage.
+ entry_->ScheduleKeepAlive();
+}
+
+void AllocateRequest::OnErrorResponse(StunMessage* response) {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ LOG(INFO) << "Bad allocate response error code";
+ } else {
+ LOG(INFO) << "Allocate error response:"
+ << " code=" << static_cast<int>(attr->error_code())
+ << " reason='" << attr->reason() << "'";
+ }
+
+ if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)
+ entry_->ScheduleKeepAlive();
+}
+
+void AllocateRequest::OnTimeout() {
+ LOG(INFO) << "Allocate request timed out";
+ entry_->HandleConnectFailure();
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h
new file mode 100644
index 00000000..7cfdc015
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayport.h
@@ -0,0 +1,93 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RELAYPORT_H__
+#define __RELAYPORT_H__
+
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/stunrequest.h"
+#include <vector>
+
+namespace cricket {
+
+extern const std::string RELAY_PORT_TYPE;
+class RelayEntry;
+
+// Communicates using an allocated port on the relay server.
+class RelayPort: public Port {
+public:
+ RelayPort(
+ Thread* thread, SocketFactory* factory, Network*,
+ const SocketAddress& local_addr,
+ const std::string& username, const std::string& password,
+ const std::string& magic_cookie);
+ virtual ~RelayPort();
+
+ void AddServerAddress(const ProtocolAddress& addr);
+ void AddExternalAddress(const ProtocolAddress& addr);
+
+ typedef std::pair<Socket::Option, int> OptionValue;
+ const std::vector<OptionValue>& options() const { return options_; }
+
+ const std::string& magic_cookie() const { return magic_cookie_; }
+ bool HasMagicCookie(const char* data, size_t size);
+
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError();
+
+ const ProtocolAddress * ServerAddress(size_t index) const;
+
+ void DisposeSocket(AsyncPacketSocket * socket);
+
+protected:
+ void SetReady();
+
+ virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload);
+ virtual void OnMessage(Message *pmsg);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr);
+
+private:
+ friend class RelayEntry;
+
+ SocketAddress local_addr_;
+ std::deque<ProtocolAddress> server_addr_;
+ bool ready_;
+ std::vector<RelayEntry*> entries_;
+ std::vector<OptionValue> options_;
+ std::string magic_cookie_;
+ int error_;
+};
+
+} // namespace cricket
+
+#endif // __RELAYPORT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc
new file mode 100644
index 00000000..bb52a1d5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.cc
@@ -0,0 +1,657 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/p2p/base/relayserver.h"
+#include "talk/p2p/base/helpers.h"
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+// By default, we require a ping every 90 seconds.
+const int MAX_LIFETIME = 15 * 60 * 1000;
+
+// The number of bytes in each of the usernames we use.
+const uint32 USERNAME_LENGTH = 16;
+
+// Calls SendTo on the given socket and logs any bad results.
+void Send(AsyncPacketSocket* socket, const char* bytes, size_t size,
+ const SocketAddress& addr) {
+ int result = socket->SendTo(bytes, size, addr);
+ if (result < int(size)) {
+ std::cerr << "SendTo wrote only " << result << " of " << int(size)
+ << " bytes" << std::endl;
+ } else if (result < 0) {
+ std::cerr << "SendTo: " << std::strerror(errno) << std::endl;
+ }
+}
+
+// Sends the given STUN message on the given socket.
+void SendStun(const StunMessage& msg,
+ AsyncPacketSocket* socket,
+ const SocketAddress& addr) {
+ ByteBuffer buf;
+ msg.Write(&buf);
+ Send(socket, buf.Data(), buf.Length(), addr);
+}
+
+// Constructs a STUN error response and sends it on the given socket.
+void SendStunError(const StunMessage& msg, AsyncPacketSocket* socket,
+ const SocketAddress& remote_addr, int error_code,
+ const char* error_desc, const std::string& magic_cookie) {
+
+ StunMessage err_msg;
+ err_msg.SetType(GetStunErrorResponseType(msg.type()));
+ err_msg.SetTransactionID(msg.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ if (magic_cookie.size() == 0)
+ magic_cookie_attr->CopyBytes(cricket::STUN_MAGIC_COOKIE_VALUE, 4);
+ else
+ magic_cookie_attr->CopyBytes(magic_cookie.c_str(), magic_cookie.size());
+ err_msg.AddAttribute(magic_cookie_attr);
+
+ StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
+ err_code->SetErrorClass(error_code / 100);
+ err_code->SetNumber(error_code % 100);
+ err_code->SetReason(error_desc);
+ err_msg.AddAttribute(err_code);
+
+ SendStun(err_msg, socket, remote_addr);
+}
+
+RelayServer::RelayServer(Thread* thread) : thread_(thread) {
+}
+
+RelayServer::~RelayServer() {
+ for (unsigned i = 0; i < internal_sockets_.size(); i++)
+ delete internal_sockets_[i];
+ for (unsigned i = 0; i < external_sockets_.size(); i++)
+ delete external_sockets_[i];
+}
+
+void RelayServer::AddInternalSocket(AsyncPacketSocket* socket) {
+ assert(internal_sockets_.end() ==
+ std::find(internal_sockets_.begin(), internal_sockets_.end(), socket));
+ internal_sockets_.push_back(socket);
+ socket->SignalReadPacket.connect(this, &RelayServer::OnInternalPacket);
+}
+
+void RelayServer::RemoveInternalSocket(AsyncPacketSocket* socket) {
+ SocketList::iterator iter =
+ std::find(internal_sockets_.begin(), internal_sockets_.end(), socket);
+ assert(iter != internal_sockets_.end());
+ internal_sockets_.erase(iter);
+ socket->SignalReadPacket.disconnect(this);
+}
+
+void RelayServer::AddExternalSocket(AsyncPacketSocket* socket) {
+ assert(external_sockets_.end() ==
+ std::find(external_sockets_.begin(), external_sockets_.end(), socket));
+ external_sockets_.push_back(socket);
+ socket->SignalReadPacket.connect(this, &RelayServer::OnExternalPacket);
+}
+
+void RelayServer::RemoveExternalSocket(AsyncPacketSocket* socket) {
+ SocketList::iterator iter =
+ std::find(external_sockets_.begin(), external_sockets_.end(), socket);
+ assert(iter != external_sockets_.end());
+ external_sockets_.erase(iter);
+ socket->SignalReadPacket.disconnect(this);
+}
+
+void RelayServer::OnInternalPacket(
+ const char* bytes, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+
+ // Get the address of the connection we just received on.
+ SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
+ assert(!ap.destination().IsAny());
+
+ // If this did not come from an existing connection, it should be a STUN
+ // allocate request.
+ ConnectionMap::iterator piter = connections_.find(ap);
+ if (piter == connections_.end()) {
+ HandleStunAllocate(bytes, size, ap, socket);
+ return;
+ }
+
+ RelayServerConnection* int_conn = piter->second;
+
+ // Handle STUN requests to the server itself.
+ if (int_conn->binding()->HasMagicCookie(bytes, size)) {
+ HandleStun(int_conn, bytes, size);
+ return;
+ }
+
+ // Otherwise, this is a non-wrapped packet that we are to forward. Make sure
+ // that this connection has been locked. (Otherwise, we would not know what
+ // address to forward to.)
+ if (!int_conn->locked()) {
+ std::cerr << "Dropping packet: connection not locked" << std::endl;
+ return;
+ }
+
+ // Forward this to the destination address into the connection.
+ RelayServerConnection* ext_conn = int_conn->binding()->GetExternalConnection(
+ int_conn->default_destination());
+ if (ext_conn) {
+ // TODO: Check the HMAC.
+ ext_conn->Send(bytes, size);
+ } else {
+ // This happens very often and is not an error.
+ //std::cerr << "Dropping packet: no external connection" << std::endl;
+ }
+}
+
+void RelayServer::OnExternalPacket(
+ const char* bytes, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+
+ // Get the address of the connection we just received on.
+ SocketAddressPair ap(remote_addr, socket->GetLocalAddress());
+ assert(!ap.destination().IsAny());
+
+ // If this connection already exists, then forward the traffic.
+ ConnectionMap::iterator piter = connections_.find(ap);
+ if (piter != connections_.end()) {
+ // TODO: Check the HMAC.
+ RelayServerConnection* ext_conn = piter->second;
+ RelayServerConnection* int_conn =
+ ext_conn->binding()->GetInternalConnection(
+ ext_conn->addr_pair().source());
+ assert(int_conn);
+ int_conn->Send(bytes, size, ext_conn->addr_pair().source());
+ return;
+ }
+
+ // The first packet should always be a STUN / TURN packet. If it isn't, then
+ // we should just ignore this packet.
+ StunMessage msg;
+ ByteBuffer buf = ByteBuffer(bytes, size);
+ if (!msg.Read(&buf)) {
+ std::cerr << "Dropping packet: first packet not STUN" << std::endl;
+ return;
+ }
+
+ // The initial packet should have a username (which identifies the binding).
+ const StunByteStringAttribute* username_attr =
+ msg.GetByteString(STUN_ATTR_USERNAME);
+ if (!username_attr) {
+ std::cerr << "Dropping packet: no username" << std::endl;
+ return;
+ }
+
+ uint32 length = _min(uint32(username_attr->length()), USERNAME_LENGTH);
+ std::string username(username_attr->bytes(), length);
+ // TODO: Check the HMAC.
+
+ // The binding should already be present.
+ BindingMap::iterator biter = bindings_.find(username);
+ if (biter == bindings_.end()) {
+ // TODO: Turn this back on. This is the sign of a client bug.
+ //std::cerr << "Dropping packet: no binding with username" << std::endl;
+ return;
+ }
+
+ // Add this authenticted connection to the binding.
+ RelayServerConnection* ext_conn =
+ new RelayServerConnection(biter->second, ap, socket);
+ ext_conn->binding()->AddExternalConnection(ext_conn);
+ AddConnection(ext_conn);
+
+ // We always know where external packets should be forwarded, so we can lock
+ // them from the beginning.
+ ext_conn->Lock();
+
+ // Send this message on the appropriate internal connection.
+ RelayServerConnection* int_conn = ext_conn->binding()->GetInternalConnection(
+ ext_conn->addr_pair().source());
+ assert(int_conn);
+ int_conn->Send(bytes, size, ext_conn->addr_pair().source());
+}
+
+bool RelayServer::HandleStun(
+ const char* bytes, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket, std::string* username, StunMessage* msg) {
+
+ // Parse this into a stun message.
+ ByteBuffer buf = ByteBuffer(bytes, size);
+ if (!msg->Read(&buf)) {
+ SendStunError(*msg, socket, remote_addr, 400, "Bad Request", "");
+ return false;
+ }
+
+ // The initial packet should have a username (which identifies the binding).
+ const StunByteStringAttribute* username_attr =
+ msg->GetByteString(STUN_ATTR_USERNAME);
+ if (!username_attr) {
+ SendStunError(*msg, socket, remote_addr, 432, "Missing Username", "");
+ return false;
+ }
+
+ // Record the username if requested.
+ if (username)
+ username->append(username_attr->bytes(), username_attr->length());
+
+ // TODO: Check for unknown attributes (<= 0x7fff)
+
+ return true;
+}
+
+void RelayServer::HandleStunAllocate(
+ const char* bytes, size_t size, const SocketAddressPair& ap,
+ AsyncPacketSocket* socket) {
+
+ // Make sure this is a valid STUN request.
+ StunMessage request;
+ std::string username;
+ if (!HandleStun(bytes, size, ap.source(), socket, &username, &request))
+ return;
+
+ // Make sure this is a an allocate request.
+ if (request.type() != STUN_ALLOCATE_REQUEST) {
+ SendStunError(request,
+ socket,
+ ap.source(),
+ 600,
+ "Operation Not Supported",
+ "");
+ return;
+ }
+
+ // TODO: Check the HMAC.
+
+ // Find or create the binding for this username.
+
+ RelayServerBinding* binding;
+
+ BindingMap::iterator biter = bindings_.find(username);
+ if (biter != bindings_.end()) {
+
+ binding = biter->second;
+
+ } else {
+
+ // NOTE: In the future, bindings will be created by the bot only. This
+ // else-branch will then disappear.
+
+ // Compute the appropriate lifetime for this binding.
+ uint32 lifetime = MAX_LIFETIME;
+ const StunUInt32Attribute* lifetime_attr =
+ request.GetUInt32(STUN_ATTR_LIFETIME);
+ if (lifetime_attr)
+ lifetime = _min(lifetime, lifetime_attr->value() * 1000);
+
+ binding = new RelayServerBinding(this, username, "0", lifetime);
+ binding->SignalTimeout.connect(this, &RelayServer::OnTimeout);
+ bindings_[username] = binding;
+
+ std::cout << "Added new binding: " << bindings_.size() << " total" << std::endl;
+ }
+
+ // Add this connection to the binding. It starts out unlocked.
+ RelayServerConnection* int_conn =
+ new RelayServerConnection(binding, ap, socket);
+ binding->AddInternalConnection(int_conn);
+ AddConnection(int_conn);
+
+ // Now that we have a connection, this other method takes over.
+ HandleStunAllocate(int_conn, request);
+}
+
+void RelayServer::HandleStun(
+ RelayServerConnection* int_conn, const char* bytes, size_t size) {
+
+ // Make sure this is a valid STUN request.
+ StunMessage request;
+ std::string username;
+ if (!HandleStun(bytes, size, int_conn->addr_pair().source(),
+ int_conn->socket(), &username, &request))
+ return;
+
+ // Make sure the username is the one were were expecting.
+ if (username != int_conn->binding()->username()) {
+ int_conn->SendStunError(request, 430, "Stale Credentials");
+ return;
+ }
+
+ // TODO: Check the HMAC.
+
+ // Send this request to the appropriate handler.
+ if (request.type() == STUN_SEND_REQUEST)
+ HandleStunSend(int_conn, request);
+ else if (request.type() == STUN_ALLOCATE_REQUEST)
+ HandleStunAllocate(int_conn, request);
+ else
+ int_conn->SendStunError(request, 600, "Operation Not Supported");
+}
+
+void RelayServer::HandleStunAllocate(
+ RelayServerConnection* int_conn, const StunMessage& request) {
+
+ // Create a response message that includes an address with which external
+ // clients can communicate.
+
+ StunMessage response;
+ response.SetType(STUN_ALLOCATE_RESPONSE);
+ response.SetTransactionID(request.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
+ int_conn->binding()->magic_cookie().size());
+ response.AddAttribute(magic_cookie_attr);
+
+ size_t index = rand() % external_sockets_.size();
+ SocketAddress ext_addr = external_sockets_[index]->GetLocalAddress();
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(ext_addr.ip());
+ addr_attr->SetPort(ext_addr.port());
+ response.AddAttribute(addr_attr);
+
+ StunUInt32Attribute* res_lifetime_attr =
+ StunAttribute::CreateUInt32(STUN_ATTR_LIFETIME);
+ res_lifetime_attr->SetValue(int_conn->binding()->lifetime() / 1000);
+ response.AddAttribute(res_lifetime_attr);
+
+ // TODO: Support transport-prefs (preallocate RTCP port).
+ // TODO: Support bandwidth restrictions.
+ // TODO: Add message integrity check.
+
+ // Send a response to the caller.
+ int_conn->SendStun(response);
+}
+
+void RelayServer::HandleStunSend(
+ RelayServerConnection* int_conn, const StunMessage& request) {
+
+ const StunAddressAttribute* addr_attr =
+ request.GetAddress(STUN_ATTR_DESTINATION_ADDRESS);
+ if (!addr_attr) {
+ int_conn->SendStunError(request, 400, "Bad Request");
+ return;
+ }
+
+ const StunByteStringAttribute* data_attr =
+ request.GetByteString(STUN_ATTR_DATA);
+ if (!data_attr) {
+ int_conn->SendStunError(request, 400, "Bad Request");
+ return;
+ }
+
+ SocketAddress ext_addr(addr_attr->ip(), addr_attr->port());
+ RelayServerConnection* ext_conn =
+ int_conn->binding()->GetExternalConnection(ext_addr);
+ if (!ext_conn) {
+ // This happens very often and is not an error.
+ //std::cerr << "Dropping packet: no external connection" << std::endl;
+ return;
+ }
+
+ ext_conn->Send(data_attr->bytes(), data_attr->length());
+
+ const StunUInt32Attribute* options_attr =
+ request.GetUInt32(STUN_ATTR_OPTIONS);
+ if (options_attr && (options_attr->value() & 0x01 != 0)) {
+ int_conn->set_default_destination(ext_addr);
+ int_conn->Lock();
+
+ StunMessage response;
+ response.SetType(STUN_SEND_RESPONSE);
+ response.SetTransactionID(request.transaction_id());
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(int_conn->binding()->magic_cookie().c_str(),
+ int_conn->binding()->magic_cookie().size());
+ response.AddAttribute(magic_cookie_attr);
+
+ StunUInt32Attribute* options2_attr =
+ StunAttribute::CreateUInt32(cricket::STUN_ATTR_OPTIONS);
+ options2_attr->SetValue(0x01);
+ response.AddAttribute(options2_attr);
+
+ int_conn->SendStun(response);
+ }
+}
+
+void RelayServer::AddConnection(RelayServerConnection* conn) {
+ assert(connections_.find(conn->addr_pair()) == connections_.end());
+ connections_[conn->addr_pair()] = conn;
+}
+
+void RelayServer::RemoveConnection(RelayServerConnection* conn) {
+ ConnectionMap::iterator iter = connections_.find(conn->addr_pair());
+ assert(iter != connections_.end());
+ connections_.erase(iter);
+}
+
+void RelayServer::RemoveBinding(RelayServerBinding* binding) {
+ BindingMap::iterator iter = bindings_.find(binding->username());
+ assert(iter != bindings_.end());
+ bindings_.erase(iter);
+
+ std::cout << "Removed a binding: " << bindings_.size() << " remaining" << std::endl;
+}
+
+void RelayServer::OnTimeout(RelayServerBinding* binding) {
+ // This call will result in all of the necessary clean-up.
+ delete binding;
+}
+
+RelayServerConnection::RelayServerConnection(
+ RelayServerBinding* binding, const SocketAddressPair& addrs,
+ AsyncPacketSocket* socket)
+ : binding_(binding), addr_pair_(addrs), socket_(socket), locked_(false) {
+
+ // The creation of a new connection constitutes a use of the binding.
+ binding_->NoteUsed();
+}
+
+RelayServerConnection::~RelayServerConnection() {
+ // Remove this connection from the server's map (if it exists there).
+ binding_->server()->RemoveConnection(this);
+}
+
+void RelayServerConnection::Send(const char* data, size_t size) {
+ // Note that the binding has been used again.
+ binding_->NoteUsed();
+
+ cricket::Send(socket_, data, size, addr_pair_.source());
+}
+
+void RelayServerConnection::Send(
+ const char* data, size_t size, const SocketAddress& from_addr) {
+ // If the from address is known to the client, we don't need to send it.
+ if (locked() && (from_addr == default_dest_)) {
+ Send(data, size);
+ return;
+ }
+
+ // Wrap the given data in a data-indication packet.
+
+ StunMessage msg;
+ msg.SetType(STUN_DATA_INDICATION);
+ msg.SetTransactionID("0000000000000000");
+
+ StunByteStringAttribute* magic_cookie_attr =
+ StunAttribute::CreateByteString(cricket::STUN_ATTR_MAGIC_COOKIE);
+ magic_cookie_attr->CopyBytes(binding_->magic_cookie().c_str(),
+ binding_->magic_cookie().size());
+ msg.AddAttribute(magic_cookie_attr);
+
+ StunAddressAttribute* addr_attr =
+ StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS2);
+ addr_attr->SetFamily(1);
+ addr_attr->SetIP(from_addr.ip());
+ addr_attr->SetPort(from_addr.port());
+ msg.AddAttribute(addr_attr);
+
+ StunByteStringAttribute* data_attr =
+ StunAttribute::CreateByteString(STUN_ATTR_DATA);
+ assert(size <= 65536);
+ data_attr->CopyBytes(data, uint16(size));
+ msg.AddAttribute(data_attr);
+
+ SendStun(msg);
+}
+
+void RelayServerConnection::SendStun(const StunMessage& msg) {
+ // Note that the binding has been used again.
+ binding_->NoteUsed();
+
+ cricket::SendStun(msg, socket_, addr_pair_.source());
+}
+
+void RelayServerConnection::SendStunError(
+ const StunMessage& request, int error_code, const char* error_desc) {
+ // An error does not indicate use. If no legitimate use off the binding
+ // occurs, we want it to be cleaned up even if errors are still occuring.
+
+ cricket::SendStunError(
+ request, socket_, addr_pair_.source(), error_code, error_desc,
+ binding_->magic_cookie());
+}
+
+void RelayServerConnection::Lock() {
+ locked_ = true;
+}
+
+void RelayServerConnection::Unlock() {
+ locked_ = false;
+}
+
+// IDs used for posted messages:
+const uint32 MSG_LIFETIME_TIMER = 1;
+
+RelayServerBinding::RelayServerBinding(
+ RelayServer* server, const std::string& username,
+ const std::string& password, uint32 lifetime)
+ : server_(server), username_(username), password_(password),
+ lifetime_(lifetime) {
+
+ // For now, every connection uses the standard magic cookie value.
+ magic_cookie_.append(
+ reinterpret_cast<const char*>(STUN_MAGIC_COOKIE_VALUE), 4);
+
+ // Initialize the last-used time to now.
+ NoteUsed();
+
+ // Set the first timeout check.
+ server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
+}
+
+RelayServerBinding::~RelayServerBinding() {
+ // Clear the outstanding timeout check.
+ server_->thread()->Clear(this);
+
+ // Clean up all of the connections.
+ for (size_t i = 0; i < internal_connections_.size(); ++i)
+ delete internal_connections_[i];
+ for (size_t i = 0; i < external_connections_.size(); ++i)
+ delete external_connections_[i];
+
+ // Remove this binding from the server's map.
+ server_->RemoveBinding(this);
+}
+
+void RelayServerBinding::AddInternalConnection(RelayServerConnection* conn) {
+ internal_connections_.push_back(conn);
+}
+
+void RelayServerBinding::AddExternalConnection(RelayServerConnection* conn) {
+ external_connections_.push_back(conn);
+}
+
+void RelayServerBinding::NoteUsed() {
+ last_used_ = GetMillisecondCount();
+}
+
+bool RelayServerBinding::HasMagicCookie(const char* bytes, size_t size) const {
+ if (size < 24 + magic_cookie_.size()) {
+ return false;
+ } else {
+ return 0 == std::memcmp(
+ bytes + 24, magic_cookie_.c_str(), magic_cookie_.size());
+ }
+}
+
+RelayServerConnection* RelayServerBinding::GetInternalConnection(
+ const SocketAddress& ext_addr) {
+
+ // Look for an internal connection that is locked to this address.
+ for (size_t i = 0; i < internal_connections_.size(); ++i) {
+ if (internal_connections_[i]->locked() &&
+ (ext_addr == internal_connections_[i]->default_destination()))
+ return internal_connections_[i];
+ }
+
+ // If one was not found, we send to the first connection.
+ assert(internal_connections_.size() > 0);
+ return internal_connections_[0];
+}
+
+RelayServerConnection* RelayServerBinding::GetExternalConnection(
+ const SocketAddress& ext_addr) {
+ for (size_t i = 0; i < external_connections_.size(); ++i) {
+ if (ext_addr == external_connections_[i]->addr_pair().source())
+ return external_connections_[i];
+ }
+ return 0;
+}
+
+void RelayServerBinding::OnMessage(Message *pmsg) {
+ if (pmsg->message_id == MSG_LIFETIME_TIMER) {
+ assert(!pmsg->pdata);
+
+ // If the lifetime timeout has been exceeded, then send a signal.
+ // Otherwise, just keep waiting.
+ if (GetMillisecondCount() >= last_used_ + lifetime_) {
+ SignalTimeout(this);
+ } else {
+ server_->thread()->PostDelayed(lifetime_, this, MSG_LIFETIME_TIMER);
+ }
+
+ } else {
+ assert(false);
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h
new file mode 100644
index 00000000..01dc3678
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.h
@@ -0,0 +1,210 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RELAYSERVER_H__
+#define __RELAYSERVER_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/socketaddresspair.h"
+#include "talk/base/thread.h"
+#include "talk/base/jtime.h"
+#include "talk/p2p/base/stun.h"
+
+#include <string>
+#include <vector>
+#include <map>
+
+namespace cricket {
+
+class RelayServerBinding;
+class RelayServerConnection;
+
+// Relays traffic between connections to the server that are "bound" together.
+// All connections created with the same username/password are bound together.
+class RelayServer : public sigslot::has_slots<> {
+public:
+ // Creates a server, which will use this thread to post messages to itself.
+ RelayServer(Thread* thread);
+ ~RelayServer();
+
+ Thread* thread() { return thread_; }
+
+ // Updates the set of sockets that the server uses to talk to "internal"
+ // clients. These are clients that do the "port allocations".
+ void AddInternalSocket(AsyncPacketSocket* socket);
+ void RemoveInternalSocket(AsyncPacketSocket* socket);
+
+ // Updates the set of sockets that the server uses to talk to "external"
+ // clients. These are the clients that do not do allocations. They do not
+ // know that these addresses represent a relay server.
+ void AddExternalSocket(AsyncPacketSocket* socket);
+ void RemoveExternalSocket(AsyncPacketSocket* socket);
+
+private:
+ typedef std::vector<AsyncPacketSocket*> SocketList;
+ typedef std::map<std::string,RelayServerBinding*> BindingMap;
+ typedef std::map<SocketAddressPair,RelayServerConnection*> ConnectionMap;
+
+ Thread* thread_;
+ SocketList internal_sockets_;
+ SocketList external_sockets_;
+ BindingMap bindings_;
+ ConnectionMap connections_;
+
+ // Called when a packet is received by the server on one of its sockets.
+ void OnInternalPacket(
+ const char* bytes, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+ void OnExternalPacket(
+ const char* bytes, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+ // Processes the relevant STUN request types from the client.
+ bool HandleStun(const char* bytes, size_t size,
+ const SocketAddress& remote_addr, AsyncPacketSocket* socket,
+ std::string* username, StunMessage* msg);
+ void HandleStunAllocate(const char* bytes, size_t size,
+ const SocketAddressPair& ap,
+ AsyncPacketSocket* socket);
+ void HandleStun(RelayServerConnection* int_conn, const char* bytes,
+ size_t size);
+ void HandleStunAllocate(RelayServerConnection* int_conn,
+ const StunMessage& msg);
+ void HandleStunSend(RelayServerConnection* int_conn, const StunMessage& msg);
+
+ // Adds/Removes the a connection or binding.
+ void AddConnection(RelayServerConnection* conn);
+ void RemoveConnection(RelayServerConnection* conn);
+ void RemoveBinding(RelayServerBinding* binding);
+
+ // Called when the timer for checking lifetime times out.
+ void OnTimeout(RelayServerBinding* binding);
+
+ friend class RelayServerConnection;
+ friend class RelayServerBinding;
+};
+
+// Maintains information about a connection to the server. Each connection is
+// part of one and only one binding.
+class RelayServerConnection {
+public:
+ RelayServerConnection(RelayServerBinding* binding,
+ const SocketAddressPair& addrs,
+ AsyncPacketSocket* socket);
+ ~RelayServerConnection();
+
+ RelayServerBinding* binding() { return binding_; }
+ AsyncPacketSocket* socket() { return socket_; }
+
+ // Returns a pair where the source is the remote address and the destination
+ // is the local address.
+ const SocketAddressPair& addr_pair() { return addr_pair_; }
+
+ // Sends a packet to the connected client. If an address is provided, then
+ // we make sure the internal client receives it, wrapping if necessary.
+ void Send(const char* data, size_t size);
+ void Send(const char* data, size_t size, const SocketAddress& ext_addr);
+
+ // Sends a STUN message to the connected client with no wrapping.
+ void SendStun(const StunMessage& msg);
+ void SendStunError(const StunMessage& request, int code, const char* desc);
+
+ // A locked connection is one for which we know the intended destination of
+ // any raw packet received.
+ bool locked() const { return locked_; }
+ void Lock();
+ void Unlock();
+
+ // Records the address that raw packets should be forwarded to (for internal
+ // packets only; for external, we already know where they go).
+ const SocketAddress& default_destination() const { return default_dest_; }
+ void set_default_destination(const SocketAddress& addr) {
+ default_dest_ = addr;
+ }
+
+private:
+ RelayServerBinding* binding_;
+ SocketAddressPair addr_pair_;
+ AsyncPacketSocket* socket_;
+ bool locked_;
+ SocketAddress default_dest_;
+};
+
+// Records a set of internal and external connections that we relay between,
+// or in other words, that are "bound" together.
+class RelayServerBinding : public MessageHandler {
+public:
+ RelayServerBinding(
+ RelayServer* server, const std::string& username,
+ const std::string& password, uint32 lifetime);
+ virtual ~RelayServerBinding();
+
+ RelayServer* server() { return server_; }
+ uint32 lifetime() { return lifetime_; }
+ const std::string& username() { return username_; }
+ const std::string& password() { return password_; }
+ const std::string& magic_cookie() { return magic_cookie_; }
+
+ // Adds/Removes a connection into the binding.
+ void AddInternalConnection(RelayServerConnection* conn);
+ void AddExternalConnection(RelayServerConnection* conn);
+
+ // We keep track of the use of each binding. If we detect that it was not
+ // used for longer than the lifetime, then we send a signal.
+ void NoteUsed();
+ sigslot::signal1<RelayServerBinding*> SignalTimeout;
+
+ // Determines whether the given packet has the magic cookie present (in the
+ // right place).
+ bool HasMagicCookie(const char* bytes, size_t size) const;
+
+ // Determines the connection to use to send packets to or from the given
+ // external address.
+ RelayServerConnection* GetInternalConnection(const SocketAddress& ext_addr);
+ RelayServerConnection* GetExternalConnection(const SocketAddress& ext_addr);
+
+ // MessageHandler:
+ void OnMessage(Message *pmsg);
+
+private:
+ RelayServer* server_;
+
+ std::string username_;
+ std::string password_;
+ std::string magic_cookie_;
+
+ std::vector<RelayServerConnection*> internal_connections_;
+ std::vector<RelayServerConnection*> external_connections_;
+
+ uint32 lifetime_;
+ uint32 last_used_;
+ // TODO: bandwidth
+};
+
+} // namespace cricket
+
+#endif // __RELAYSERVER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro
new file mode 100644
index 00000000..41bc6b63
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+INCLUDEPATH = ../../..
+DEFINES += POSIX
+
+include(../../../../../conf.pri)
+
+# Input
+SOURCES += \
+ relayserver.cc \
+ relayserver_main.cc \
+ ../../base/host.cc \
+ ../../base/socketaddresspair.cc
+
+LIBS += ../../../liblibjingle.a
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc
new file mode 100644
index 00000000..5f624f37
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/relayserver_main.cc
@@ -0,0 +1,75 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/host.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/relayserver.h"
+#include <iostream>
+#include <assert.h>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace cricket;
+
+int main(int argc, char **argv) {
+ if (argc != 1) {
+ std::cerr << "usage: relayserver" << std::endl;
+ return 1;
+ }
+
+ assert(LocalHost().networks().size() >= 2);
+ SocketAddress int_addr(LocalHost().networks()[1]->ip(), 5000);
+ SocketAddress ext_addr(LocalHost().networks()[1]->ip(), 5001);
+
+ Thread *pthMain = Thread::Current();
+
+ AsyncUDPSocket* int_socket = CreateAsyncUDPSocket(pthMain->socketserver());
+ if (int_socket->Bind(int_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ AsyncUDPSocket* ext_socket = CreateAsyncUDPSocket(pthMain->socketserver());
+ if (ext_socket->Bind(ext_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ RelayServer server(pthMain);
+ server.AddInternalSocket(int_socket);
+ server.AddExternalSocket(ext_socket);
+
+ std::cout << "Listening internally at " << int_addr.ToString() << std::endl;
+ std::cout << "Listening externally at " << ext_addr.ToString() << std::endl;
+
+ pthMain->Loop();
+ return 0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc
new file mode 100644
index 00000000..73873338
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.cc
@@ -0,0 +1,421 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/common.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/p2p/base/session.h"
+
+namespace cricket {
+
+const uint32 MSG_TIMEOUT = 1;
+const uint32 MSG_ERROR = 2;
+const uint32 MSG_STATE = 3;
+
+Session::Session(SessionManager *session_manager, const std::string &name,
+ const SessionID& id) {
+ session_manager_ = session_manager;
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ name_ = name;
+ id_ = id;
+ error_ = ERROR_NONE;
+ state_ = STATE_INIT;
+ initiator_ = false;
+ description_ = NULL;
+ remote_description_ = NULL;
+ socket_manager_ = new SocketManager(session_manager_);
+ socket_manager_->SignalCandidatesReady.connect(this, &Session::OnCandidatesReady);
+ socket_manager_->SignalNetworkError.connect(this, &Session::OnNetworkError);
+ socket_manager_->SignalState.connect(this, &Session::OnSocketState);
+ socket_manager_->SignalRequestSignaling.connect(this, &Session::OnRequestSignaling);
+}
+
+Session::~Session() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ delete description_;
+ delete remote_description_;
+ delete socket_manager_;
+ session_manager_->signaling_thread()->Clear(this);
+}
+
+P2PSocket *Session::CreateSocket(const std::string &name) {
+ return socket_manager_->CreateSocket(name);
+}
+
+void Session::DestroySocket(P2PSocket *socket) {
+ socket_manager_->DestroySocket(socket);
+}
+
+void Session::OnCandidatesReady(const std::vector<Candidate>& candidates) {
+ SendSessionMessage(SessionMessage::TYPE_CANDIDATES, NULL, &candidates, NULL);
+}
+
+void Session::OnNetworkError() {
+ // Socket manager is experiencing a network error trying to allocate
+ // network resources (usually port allocation)
+
+ set_error(ERROR_NETWORK);
+}
+
+void Session::OnSocketState() {
+ // If the call is not in progress, then we don't care about writability.
+ // We have separate timers for making sure we transition back to the in-
+ // progress state in time.
+ if (state_ != STATE_INPROGRESS)
+ return;
+
+ // Put the timer into the write state. This is called when the state changes,
+ // so we will restart the timer each time we lose writability.
+ if (socket_manager_->writable()) {
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ } else {
+ session_manager_->signaling_thread()->PostDelayed(
+ session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+ }
+}
+
+void Session::OnRequestSignaling() {
+ SignalRequestSignaling();
+}
+
+void Session::OnSignalingReady() {
+ socket_manager_->OnSignalingReady();
+}
+
+void Session::SendSessionMessage(SessionMessage::Type type,
+ const SessionDescription* description,
+ const std::vector<Candidate>* candidates,
+ SessionMessage::Cookie* redirect_cookie) {
+ SessionMessage m;
+ m.set_type(type);
+ m.set_to(remote_address_);
+ m.set_name(name_);
+ m.set_description(description);
+ m.set_session_id(id_);
+ if (candidates)
+ m.set_candidates(*candidates);
+ m.set_redirect_target(redirect_target_);
+ m.set_redirect_cookie(redirect_cookie);
+ SignalOutgoingMessage(this, m);
+}
+
+bool Session::Initiate(const std::string &to, const SessionDescription *description) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Only from STATE_INIT
+ if (state_ != STATE_INIT)
+ return false;
+
+ // Setup for signaling. Initiate is asynchronous. It occurs once the address
+ // candidates are ready.
+ initiator_ = true;
+ remote_address_ = to;
+ description_ = description;
+ SendSessionMessage(SessionMessage::TYPE_INITIATE, description, NULL, NULL);
+ set_state(Session::STATE_SENTINITIATE);
+
+ // Let the socket manager know we now want the candidates
+ socket_manager_->StartProcessingCandidates();
+
+ // Start the session timeout
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+ return true;
+}
+
+bool Session::Accept(const SessionDescription *description) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Only if just received initiate
+ if (state_ != STATE_RECEIVEDINITIATE)
+ return false;
+
+ // Setup for signaling. Accept is asynchronous. It occurs once the address
+ // candidates are ready.
+ initiator_ = false;
+ description_ = description;
+ SendSessionMessage(SessionMessage::TYPE_ACCEPT, description, NULL, NULL);
+ set_state(Session::STATE_SENTACCEPT);
+
+ return true;
+}
+
+bool Session::Modify(const SessionDescription *description) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Only if session already STATE_INPROGRESS
+ if (state_ != STATE_INPROGRESS)
+ return false;
+
+ // Modify is asynchronous. It occurs once the address candidates are ready.
+ // Either side can send a modify. It is only valid in an already accepted
+ // session.
+ description_ = description;
+ SendSessionMessage(SessionMessage::TYPE_MODIFY, description, NULL, NULL);
+ set_state(Session::STATE_SENTMODIFY);
+
+ // Start the session timeout
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+ return true;
+}
+
+bool Session::Redirect(const std::string& target) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Redirect is sent in response to an initiate or modify, to redirect the
+ // request
+ if (state_ != STATE_RECEIVEDINITIATE)
+ return false;
+
+ initiator_ = false;
+ redirect_target_ = target;
+ SendSessionMessage(SessionMessage::TYPE_REDIRECT, NULL, NULL, NULL);
+
+ // A redirect puts us in the same state as reject. It just sends a different
+ // kind of reject message, if you like.
+ set_state(STATE_SENTREDIRECT);
+
+ return true;
+}
+
+bool Session::Reject() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Reject is sent in response to an initiate or modify, to reject the
+ // request
+ if (state_ != STATE_RECEIVEDINITIATE && state_ != STATE_RECEIVEDMODIFY)
+ return false;
+
+ initiator_ = false;
+ SendSessionMessage(SessionMessage::TYPE_REJECT, NULL, NULL, NULL);
+ set_state(STATE_SENTREJECT);
+
+ return true;
+}
+
+bool Session::Terminate() {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // Either side can terminate, at any time.
+ if (state_ == STATE_SENTTERMINATE && state_ != STATE_RECEIVEDTERMINATE)
+ return false;
+
+ // But we don't need to terminate if we already rejected. The other client
+ // already knows that we're done with this session.
+ if (state_ != STATE_SENTREDIRECT)
+ SendSessionMessage(SessionMessage::TYPE_TERMINATE, NULL, NULL, NULL);
+
+ set_state(STATE_SENTTERMINATE);
+
+ return true;
+}
+
+void Session::OnIncomingError(const SessionMessage &m) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ // If a candidate message errors out or gets dropped for some reason we
+ // ignore the error.
+ if (m.type() != SessionMessage::TYPE_CANDIDATES) {
+ set_error(ERROR_RESPONSE);
+ }
+}
+
+void Session::OnIncomingMessage(const SessionMessage &m) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+
+ switch (m.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ remote_description_ = m.description();
+ remote_address_ = m.from();
+ name_ = m.name();
+ initiator_ = false;
+ set_state(STATE_RECEIVEDINITIATE);
+
+ // Let the socket manager know we now want the initial candidates
+ socket_manager_->StartProcessingCandidates();
+ break;
+
+ case SessionMessage::TYPE_ACCEPT:
+ remote_description_ = m.description();
+ set_state(STATE_RECEIVEDACCEPT);
+ break;
+
+ case SessionMessage::TYPE_MODIFY:
+ remote_description_ = m.description();
+ set_state(STATE_RECEIVEDMODIFY);
+ break;
+
+ case SessionMessage::TYPE_CANDIDATES:
+ socket_manager_->AddRemoteCandidates(m.candidates());
+ break;
+
+ case SessionMessage::TYPE_REJECT:
+ set_state(STATE_RECEIVEDREJECT);
+ break;
+
+ case SessionMessage::TYPE_REDIRECT:
+ OnRedirectMessage(m);
+ break;
+
+ case SessionMessage::TYPE_TERMINATE:
+ set_state(STATE_RECEIVEDTERMINATE);
+ break;
+ }
+}
+
+void Session::OnRedirectMessage(const SessionMessage &m) {
+ ASSERT(state_ == STATE_SENTINITIATE);
+ if (state_ != STATE_SENTINITIATE)
+ return;
+
+ ASSERT(m.redirect_target().size() != 0);
+ remote_address_ = m.redirect_target();
+
+ SendSessionMessage(SessionMessage::TYPE_INITIATE, description_, NULL,
+ m.redirect_cookie()->Copy());
+
+ // Restart the session timeout.
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ session_manager_->signaling_thread()->PostDelayed(session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
+
+ // Reset all of the sockets back into the initial state.
+ socket_manager_->ResetSockets();
+}
+
+Session::State Session::state() {
+ return state_;
+}
+
+void Session::set_state(State state) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (state != state_) {
+ state_ = state;
+ SignalState(this, state);
+ session_manager_->signaling_thread()->Post(this, MSG_STATE);
+ }
+}
+
+Session::Error Session::error() {
+ return error_;
+}
+
+void Session::set_error(Error error) {
+ ASSERT(session_manager_->signaling_thread()->IsCurrent());
+ if (error != error_) {
+ error_ = error;
+ SignalError(this, error);
+ session_manager_->signaling_thread()->Post(this, MSG_ERROR);
+ }
+}
+
+const std::string &Session::name() {
+ return name_;
+}
+
+const std::string &Session::remote_address() {
+ return remote_address_;
+}
+
+bool Session::initiator() {
+ return initiator_;
+}
+
+const SessionID& Session::id() {
+ return id_;
+}
+
+const SessionDescription *Session::description() {
+ return description_;
+}
+
+const SessionDescription *Session::remote_description() {
+ return remote_description_;
+}
+
+SessionManager *Session::session_manager() {
+ return session_manager_;
+}
+
+void Session::OnMessage(Message *pmsg) {
+ switch(pmsg->message_id) {
+ case MSG_TIMEOUT:
+ // Session timeout has occured. Check to see if the session is still trying
+ // to signal. If so, the session has timed out.
+ // The Sockets have their own timeout for connectivity.
+ set_error(ERROR_TIME);
+ break;
+
+ case MSG_ERROR:
+ switch (error_) {
+ case ERROR_RESPONSE:
+ // This state could be reached if we get an error in response to an IQ
+ // or if the network is so slow we time out on an individual IQ exchange.
+ // In either case, Terminate (send more messages) and ignore the likely
+ // cascade of more errors.
+
+ // fall through
+ case ERROR_NETWORK:
+ case ERROR_TIME:
+ // Time ran out - no response
+ Terminate();
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case MSG_STATE:
+ switch (state_) {
+ case STATE_SENTACCEPT:
+ case STATE_RECEIVEDACCEPT:
+ set_state(STATE_INPROGRESS);
+ session_manager_->signaling_thread()->Clear(this, MSG_TIMEOUT);
+ OnSocketState(); // Update the writability timeout state.
+ break;
+
+ case STATE_SENTREJECT:
+ case STATE_SENTREDIRECT:
+ case STATE_RECEIVEDREJECT:
+ Terminate();
+ break;
+
+ case STATE_SENTTERMINATE:
+ case STATE_RECEIVEDTERMINATE:
+ session_manager_->DestroySession(this);
+ break;
+
+ default:
+ // explicitly ignoring some states here
+ break;
+ }
+ break;
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h
new file mode 100644
index 00000000..1414a375
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/session.h
@@ -0,0 +1,140 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSION_H_
+#define _SESSION_H_
+
+#include "talk/base/socketaddress.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/sessionmessage.h"
+#include "talk/p2p/base/socketmanager.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/p2p/base/port.h"
+#include <string>
+
+namespace cricket {
+
+class SessionManager;
+class SocketManager;
+
+// A specific Session created by the SessionManager
+// A Session manages signaling for session setup and tear down, and connectivity
+// with P2PSockets
+
+class Session : public MessageHandler, public sigslot::has_slots<> {
+public:
+ enum State {
+ STATE_INIT = 0,
+ STATE_SENTINITIATE, // sent initiate, waiting for Accept or Reject
+ STATE_RECEIVEDINITIATE, // received an initiate. Call Accept or Reject
+ STATE_SENTACCEPT, // sent accept. begin connectivity establishment
+ STATE_RECEIVEDACCEPT, // received accept. begin connectivity establishment
+ STATE_SENTMODIFY, // sent modify, waiting for Accept or Reject
+ STATE_RECEIVEDMODIFY, // received modify, call Accept or Reject
+ STATE_SENTREJECT, // sent reject after receiving initiate
+ STATE_RECEIVEDREJECT, // received reject after sending initiate
+ STATE_SENTREDIRECT, // sent direct after receiving initiate
+ STATE_SENTTERMINATE, // sent terminate (any time / either side)
+ STATE_RECEIVEDTERMINATE, // received terminate (any time / either side)
+ STATE_INPROGRESS, // session accepted and in progress
+ };
+
+ enum Error {
+ ERROR_NONE = 0, // no error
+ ERROR_TIME, // no response to signaling
+ ERROR_RESPONSE, // error during signaling
+ ERROR_NETWORK, // network error, could not allocate network resources
+ };
+
+ Session(SessionManager *session_manager, const std::string &name, const SessionID& id);
+ ~Session();
+
+ // From MessageHandler
+ void OnMessage(Message *pmsg);
+
+ P2PSocket *CreateSocket(const std::string & name);
+ void DestroySocket(P2PSocket *socket);
+
+ bool Initiate(const std::string &to, const SessionDescription *description);
+ bool Accept(const SessionDescription *description);
+ bool Modify(const SessionDescription *description);
+ bool Reject();
+ bool Redirect(const std::string& target);
+ bool Terminate();
+
+ SessionManager *session_manager();
+ const std::string &name();
+ const std::string &remote_address();
+ bool initiator();
+ const SessionID& id();
+ const SessionDescription *description();
+ const SessionDescription *remote_description();
+
+ State state();
+ Error error();
+
+ void OnSignalingReady();
+ void OnIncomingMessage(const SessionMessage &m);
+ void OnIncomingError(const SessionMessage &m);
+
+ sigslot::signal2<Session *, State> SignalState;
+ sigslot::signal2<Session *, Error> SignalError;
+ sigslot::signal2<Session *, const SessionMessage &> SignalOutgoingMessage;
+ sigslot::signal0<> SignalRequestSignaling;
+
+private:
+ void SendSessionMessage(SessionMessage::Type type,
+ const SessionDescription* description,
+ const std::vector<Candidate>* candidates,
+ SessionMessage::Cookie* redirect_cookie);
+ void OnCandidatesReady(const std::vector<Candidate>& candidates);
+ void OnNetworkError();
+ void OnSocketState();
+ void OnRequestSignaling();
+ void OnRedirectMessage(const SessionMessage &m);
+
+ void set_state(State state);
+ void set_error(Error error);
+
+ bool initiator_;
+ SessionManager *session_manager_;
+ SessionID id_;
+ SocketManager *socket_manager_;
+ std::string name_;
+ std::string remote_address_;
+ const SessionDescription *description_;
+ const SessionDescription *remote_description_;
+ std::string redirect_target_;
+ State state_;
+ Error error_;
+ CriticalSection crit_;
+};
+
+} // namespace cricket
+
+#endif // _SESSION_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h
new file mode 100644
index 00000000..28b70845
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessiondescription.h
@@ -0,0 +1,42 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONDESCRIPTION_H_
+#define _SESSIONDESCRIPTION_H_
+
+namespace cricket {
+
+// The client overrides this with whatever
+
+class SessionDescription {
+public:
+ virtual ~SessionDescription() {}
+};
+
+} // namespace cricket
+
+#endif // _SESSIONDESCRIPTION_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h
new file mode 100644
index 00000000..a12535c0
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionid.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONID_H_
+#define _SESSIONID_H_
+
+#include "talk/base/basictypes.h"
+#include <string>
+#include <sstream>
+
+namespace cricket {
+
+// Each session is identified by a pair (from,id), where id is only
+// assumed to be unique to the machine identified by from.
+class SessionID {
+public:
+ SessionID() : id_str_("0") {
+ }
+ SessionID(const std::string& initiator, uint32 id)
+ : initiator_(initiator) {
+ set_id(id);
+ }
+ SessionID(const SessionID& sid)
+ : id_str_(sid.id_str_), initiator_(sid.initiator_) {
+ }
+
+ void set_id(uint32 id) {
+ std::stringstream st;
+ st << id;
+ st >> id_str_;
+ }
+ const std::string id_str() const {
+ return id_str_;
+ }
+ void set_id_str(const std::string &id_str) {
+ id_str_ = id_str;
+ }
+
+ const std::string &initiator() const {
+ return initiator_;
+ }
+ void set_initiator(const std::string &initiator) {
+ initiator_ = initiator;
+ }
+
+ bool operator <(const SessionID& sid) const {
+ int r = initiator_.compare(sid.initiator_);
+ if (r == 0)
+ r = id_str_.compare(sid.id_str_);
+ return r < 0;
+ }
+
+ bool operator ==(const SessionID& sid) const {
+ return (id_str_ == sid.id_str_) && (initiator_ == sid.initiator_);
+ }
+
+ SessionID& operator =(const SessionID& sid) {
+ id_str_ = sid.id_str_;
+ initiator_ = sid.initiator_;
+ return *this;
+ }
+
+private:
+ std::string id_str_;
+ std::string initiator_;
+};
+
+} // namespace cricket
+
+#endif // _SESSIONID_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc
new file mode 100644
index 00000000..4c1c09d9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.cc
@@ -0,0 +1,173 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/common.h"
+#include "talk/p2p/base/helpers.h"
+#include "sessionmanager.h"
+
+namespace cricket {
+
+SessionManager::SessionManager(PortAllocator *allocator, Thread *worker) {
+ allocator_ = allocator;
+ signaling_thread_ = Thread::Current();
+ if (worker == NULL) {
+ worker_thread_ = Thread::Current();
+ } else {
+ worker_thread_ = worker;
+ }
+ timeout_ = 50;
+}
+
+SessionManager::~SessionManager() {
+ // Note: Session::Terminate occurs asynchronously, so it's too late to
+ // delete them now. They better be all gone.
+ ASSERT(session_map_.empty());
+ //TerminateAll();
+}
+
+Session *SessionManager::CreateSession(const std::string &name, const std::string& initiator) {
+ return CreateSession(name, SessionID(initiator, CreateRandomId()), false);
+}
+
+Session *SessionManager::CreateSession(const std::string &name, const SessionID& id, bool received_initiate) {
+ Session *session = new Session(this, name, id);
+ session_map_[session->id()] = session;
+ session->SignalRequestSignaling.connect(this, &SessionManager::OnRequestSignaling);
+ SignalSessionCreate(session, received_initiate);
+ return session;
+}
+
+void SessionManager::DestroySession(Session *session) {
+ if (session != NULL) {
+ std::map<SessionID, Session *>::iterator it = session_map_.find(session->id());
+ if (it != session_map_.end()) {
+ SignalSessionDestroy(session);
+ session_map_.erase(it);
+ delete session;
+ }
+ }
+}
+
+Session *SessionManager::GetSession(const SessionID& id) {
+ // If the id isn't present, the [] operator will make a NULL entry
+ std::map<SessionID, Session *>::iterator it = session_map_.find(id);
+ if (it != session_map_.end())
+ return (*it).second;
+ return NULL;
+}
+
+void SessionManager::TerminateAll() {
+ while (session_map_.begin() != session_map_.end()) {
+ Session *session = session_map_.begin()->second;
+ session->Terminate();
+ }
+}
+
+void SessionManager::OnIncomingError(const SessionMessage &m) {
+ // Incoming signaling error. This means, as the result of trying
+ // to send message m, and error was generated. In all cases, a
+ // session should already exist
+
+ Session *session;
+ switch (m.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ case SessionMessage::TYPE_ACCEPT:
+ case SessionMessage::TYPE_MODIFY:
+ case SessionMessage::TYPE_CANDIDATES:
+ case SessionMessage::TYPE_REJECT:
+ case SessionMessage::TYPE_TERMINATE:
+ session = GetSession(m.session_id());
+ break;
+
+ default:
+ return;
+ }
+
+ if (session != NULL)
+ session->OnIncomingError(m);
+
+}
+
+void SessionManager::OnIncomingMessage(const SessionMessage &m) {
+ // In the case of an incoming initiate, there is no session yet, and one needs to be created.
+ // The other cases have sessions already.
+
+ Session *session;
+ switch (m.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ session = CreateSession(m.name(), m.session_id(), true);
+ break;
+
+ case SessionMessage::TYPE_ACCEPT:
+ case SessionMessage::TYPE_MODIFY:
+ case SessionMessage::TYPE_CANDIDATES:
+ case SessionMessage::TYPE_REJECT:
+ case SessionMessage::TYPE_REDIRECT:
+ case SessionMessage::TYPE_TERMINATE:
+ session = GetSession(m.session_id());
+ break;
+
+ default:
+ return;
+ }
+
+ if (session != NULL)
+ session->OnIncomingMessage(m);
+}
+
+void SessionManager::OnSignalingReady() {
+ for (std::map<SessionID, Session *>::iterator it = session_map_.begin();
+ it != session_map_.end(); ++it) {
+ it->second->OnSignalingReady();
+ }
+}
+
+void SessionManager::OnRequestSignaling() {
+ SignalRequestSignaling();
+}
+
+PortAllocator *SessionManager::port_allocator() const {
+ return allocator_;
+}
+
+Thread *SessionManager::worker_thread() const {
+ return worker_thread_;
+}
+
+Thread *SessionManager::signaling_thread() const {
+ return signaling_thread_;
+}
+
+int SessionManager::session_timeout() {
+ return timeout_;
+}
+
+void SessionManager::set_session_timeout(int timeout) {
+ timeout_ = timeout;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h
new file mode 100644
index 00000000..5ce0e4c5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmanager.h
@@ -0,0 +1,86 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONMANAGER_H_
+#define _SESSIONMANAGER_H_
+
+#include "talk/base/thread.h"
+#include "talk/p2p/base/portallocator.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessionmessage.h"
+#include "talk/base/sigslot.h"
+
+#include <string>
+#include <utility>
+#include <map>
+
+namespace cricket {
+
+class Session;
+
+// SessionManager manages session instances
+
+class SessionManager : public sigslot::has_slots<> {
+public:
+ SessionManager(PortAllocator *allocator, Thread *worker_thread = NULL);
+ virtual ~SessionManager();
+
+ Session *CreateSession(const std::string &name, const std::string& initiator);
+ void DestroySession(Session *session);
+ Session *GetSession(const SessionID& id);
+ void TerminateAll();
+ void OnIncomingMessage(const SessionMessage &m);
+ void OnIncomingError(const SessionMessage &m);
+ void OnSignalingReady();
+
+ PortAllocator *port_allocator() const;
+ Thread *worker_thread() const;
+ Thread *signaling_thread() const;
+ int session_timeout();
+ void set_session_timeout(int timeout);
+
+ sigslot::signal2<Session *, bool> SignalSessionCreate;
+ sigslot::signal1<Session *> SignalSessionDestroy;
+
+ // Note: you can connect this directly to OnSignalingReady(), if a signalling
+ // check is not required.
+ sigslot::signal0<> SignalRequestSignaling;
+
+private:
+ Session *CreateSession(const std::string &name, const SessionID& id, bool received_initiate);
+ void OnRequestSignaling();
+
+ int timeout_;
+ Thread *worker_thread_;
+ Thread *signaling_thread_;
+ PortAllocator *allocator_;
+ std::map<SessionID, Session *> session_map_;
+};
+
+} // namespace cricket
+
+#endif // _SESSIONMANAGER_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h
new file mode 100644
index 00000000..fc1b0323
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/sessionmessage.h
@@ -0,0 +1,133 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONMESSAGE_H_
+#define _SESSIONMESSAGE_H_
+
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionid.h"
+#include "talk/base/basictypes.h"
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace cricket {
+
+class SessionMessage {
+public:
+ enum Type {
+ TYPE_INITIATE = 0, // Initiate message
+ TYPE_ACCEPT, // Accept message
+ TYPE_MODIFY, // Modify message
+ TYPE_CANDIDATES, // Candidates message
+ TYPE_REJECT, // Reject message
+ TYPE_REDIRECT, // Reject message
+ TYPE_TERMINATE, // Terminate message
+ };
+
+ class Cookie {
+ public:
+ virtual ~Cookie() {}
+
+ // Returns a copy of this cookie.
+ virtual Cookie* Copy() = 0;
+ };
+
+ Type type() const {
+ return type_;
+ }
+ void set_type(Type type) {
+ type_ = type;
+ }
+ const SessionID& session_id() const {
+ return id_;
+ }
+ SessionID& session_id() {
+ return id_;
+ }
+ void set_session_id(const SessionID& id) {
+ id_ = id;
+ }
+ const std::string &from() const {
+ return from_;
+ }
+ void set_from(const std::string &from) {
+ from_ = from;
+ }
+ const std::string &to() const {
+ return to_;
+ }
+ void set_to(const std::string &to) {
+ to_ = to;
+ }
+ const std::string &name() const {
+ return name_;
+ }
+ void set_name(const std::string &name) {
+ name_ = name;
+ }
+ const std::string &redirect_target() const {
+ return redirect_target_;
+ }
+ void set_redirect_target(const std::string &redirect_target) {
+ redirect_target_ = redirect_target;
+ }
+ Cookie *redirect_cookie() const {
+ return redirect_cookie_;
+ }
+ void set_redirect_cookie(Cookie* redirect_cookie) {
+ redirect_cookie_ = redirect_cookie;
+ }
+ const SessionDescription *description() const {
+ return description_;
+ }
+ void set_description(const SessionDescription *description) {
+ description_ = description;
+ }
+ const std::vector<Candidate> &candidates() const {
+ return candidates_;
+ }
+ void set_candidates(const std::vector<Candidate> &candidates) {
+ candidates_ = candidates;
+ }
+
+private:
+ Type type_;
+ SessionID id_;
+ std::string from_;
+ std::string to_;
+ std::string name_;
+ const SessionDescription *description_;
+ std::vector<Candidate> candidates_;
+ std::string redirect_target_;
+ Cookie* redirect_cookie_;
+};
+
+} // namespace cricket
+
+#endif // _SESSIONMESSAGE_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc
new file mode 100644
index 00000000..2f0d67b8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.cc
@@ -0,0 +1,273 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "socketmanager.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_CREATESOCKET = 1;
+const uint32 MSG_DESTROYSOCKET = 2;
+const uint32 MSG_ONSIGNALINGREADY = 3;
+const uint32 MSG_CANDIDATESREADY = 4;
+const uint32 MSG_ADDREMOTECANDIDATES = 5;
+const uint32 MSG_ONREQUESTSIGNALING = 6;
+const uint32 MSG_RESETSOCKETS = 7;
+
+struct CreateParams {
+ CreateParams() {}
+ P2PSocket *socket;
+ std::string name;
+};
+
+SocketManager::SocketManager(SessionManager *session_manager) {
+ session_manager_ = session_manager;
+ candidates_requested_ = false;
+ writable_ = false;
+}
+
+SocketManager::~SocketManager() {
+ assert(Thread::Current() == session_manager_->signaling_thread());
+
+ // Are the sockets destroyed? If not, destroy them
+
+ critSM_.Enter();
+ while (sockets_.size() != 0) {
+ P2PSocket *socket = sockets_[0];
+ critSM_.Leave();
+ DestroySocket(socket);
+ critSM_.Enter();
+ }
+ critSM_.Leave();
+
+ // Clear queues
+
+ session_manager_->signaling_thread()->Clear(this);
+ session_manager_->worker_thread()->Clear(this);
+}
+
+P2PSocket *SocketManager::CreateSocket(const std::string &name) {
+ // Can occur on any thread
+ CreateParams params;
+ params.name = name;
+ params.socket = NULL;
+ TypedMessageData<CreateParams *> data(&params);
+ session_manager_->worker_thread()->Send(this, MSG_CREATESOCKET, &data);
+ return data.data()->socket;
+}
+
+P2PSocket *SocketManager::CreateSocket_w(const std::string &name) {
+ // Only on worker thread
+ assert(Thread::Current() == session_manager_->worker_thread());
+ CritScope cs(&critSM_);
+ P2PSocket *socket = new P2PSocket(name, session_manager_->port_allocator());
+ socket->SignalCandidatesReady.connect(this, &SocketManager::OnCandidatesReady);
+ socket->SignalState.connect(this, &SocketManager::OnSocketState);
+ socket->SignalRequestSignaling.connect(this, &SocketManager::OnRequestSignaling);
+ sockets_.push_back(socket);
+ socket->StartProcessingCandidates();
+ return socket;
+}
+
+void SocketManager::DestroySocket(P2PSocket *socket) {
+ // Can occur on any thread
+ TypedMessageData<P2PSocket *> data(socket);
+ session_manager_->worker_thread()->Send(this, MSG_DESTROYSOCKET, &data);
+}
+
+void SocketManager::DestroySocket_w(P2PSocket *socket) {
+ // Only on worker thread
+ assert(Thread::Current() == session_manager_->worker_thread());
+
+ // Only if socket exists
+ CritScope cs(&critSM_);
+ std::vector<P2PSocket *>::iterator it;
+ it = std::find(sockets_.begin(), sockets_.end(), socket);
+ if (it == sockets_.end())
+ return;
+ sockets_.erase(it);
+ delete socket;
+}
+
+void SocketManager::StartProcessingCandidates() {
+ // Only on signaling thread
+ assert(Thread::Current() == session_manager_->signaling_thread());
+
+ // When sockets are created, their candidates are requested.
+ // When the candidates are ready, the client is signaled
+ // on the signaling thread
+ candidates_requested_ = true;
+ session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY);
+}
+
+void SocketManager::OnSignalingReady() {
+ session_manager_->worker_thread()->Post(this, MSG_ONSIGNALINGREADY);
+}
+
+void SocketManager::OnSignalingReady_w() {
+ // Only on worker thread
+ assert(Thread::Current() == session_manager_->worker_thread());
+ for (uint32 i = 0; i < sockets_.size(); ++i) {
+ sockets_[i]->OnSignalingReady();
+ }
+}
+
+void SocketManager::OnCandidatesReady(
+ P2PSocket *socket, const std::vector<Candidate>& candidates) {
+ // Only on worker thread
+ assert(Thread::Current() == session_manager_->worker_thread());
+
+ // Remember candidates
+ CritScope cs(&critSM_);
+ std::vector<Candidate>::const_iterator it;
+ for (it = candidates.begin(); it != candidates.end(); it++)
+ candidates_.push_back(*it);
+
+ // If candidates requested, tell signaling thread
+ if (candidates_requested_)
+ session_manager_->signaling_thread()->Post(this, MSG_CANDIDATESREADY);
+}
+
+void SocketManager::ResetSockets() {
+ assert(Thread::Current() == session_manager_->signaling_thread());
+ session_manager_->worker_thread()->Post(this, MSG_RESETSOCKETS);
+}
+
+void SocketManager::ResetSockets_w() {
+ assert(Thread::Current() == session_manager_->worker_thread());
+
+ for (size_t i = 0; i < sockets_.size(); ++i)
+ sockets_[i]->Reset();
+}
+
+void SocketManager::OnSocketState(P2PSocket* socket, P2PSocket::State state) {
+ assert(Thread::Current() == session_manager_->worker_thread());
+
+ bool writable = false;
+ for (uint32 i = 0; i < sockets_.size(); ++i)
+ if (sockets_[i]->writable())
+ writable = true;
+
+ if (writable_ != writable) {
+ writable_ = writable;
+ SignalState();
+ }
+}
+
+void SocketManager::OnRequestSignaling() {
+ assert(Thread::Current() == session_manager_->worker_thread());
+ session_manager_->signaling_thread()->Post(this, MSG_ONREQUESTSIGNALING);
+}
+
+
+void SocketManager::AddRemoteCandidates(const std::vector<Candidate> &remote_candidates) {
+ assert(Thread::Current() == session_manager_->signaling_thread());
+ TypedMessageData<std::vector<Candidate> > *data = new TypedMessageData<std::vector<Candidate> >(remote_candidates);
+ session_manager_->worker_thread()->Post(this, MSG_ADDREMOTECANDIDATES, data);
+}
+
+void SocketManager::AddRemoteCandidates_w(const std::vector<Candidate> &remote_candidates) {
+ assert(Thread::Current() == session_manager_->worker_thread());
+
+ // Local and remote candidates now exist, so connectivity checking can
+ // commence. Tell the P2PSockets about the remote candidates.
+ // Group candidates by socket name
+
+ CritScope cs(&critSM_);
+ std::vector<P2PSocket *>::iterator it_socket;
+ for (it_socket = sockets_.begin(); it_socket != sockets_.end(); it_socket++) {
+ // Create a vector of remote candidates for each socket
+ std::string name = (*it_socket)->name();
+ std::vector<Candidate> candidate_bundle;
+ std::vector<Candidate>::const_iterator it_candidate;
+ for (it_candidate = remote_candidates.begin(); it_candidate != remote_candidates.end(); it_candidate++) {
+ if ((*it_candidate).name() == name)
+ candidate_bundle.push_back(*it_candidate);
+ }
+ if (candidate_bundle.size() != 0)
+ (*it_socket)->AddRemoteCandidates(candidate_bundle);
+ }
+}
+
+void SocketManager::OnMessage(Message *message) {
+ switch (message->message_id) {
+ case MSG_CREATESOCKET:
+ {
+ assert(Thread::Current() == session_manager_->worker_thread());
+ TypedMessageData<CreateParams *> *params = static_cast<TypedMessageData<CreateParams *> *>(message->pdata);
+ params->data()->socket = CreateSocket_w(params->data()->name);
+ }
+ break;
+
+ case MSG_DESTROYSOCKET:
+ {
+ assert(Thread::Current() == session_manager_->worker_thread());
+ TypedMessageData<P2PSocket *> *data = static_cast<TypedMessageData<P2PSocket *> *>(message->pdata);
+ DestroySocket_w(data->data());
+ }
+ break;
+
+ case MSG_ONSIGNALINGREADY:
+ assert(Thread::Current() == session_manager_->worker_thread());
+ OnSignalingReady_w();
+ break;
+
+ case MSG_ONREQUESTSIGNALING:
+ assert(Thread::Current() == session_manager_->signaling_thread());
+ SignalRequestSignaling();
+ break;
+
+ case MSG_CANDIDATESREADY:
+ assert(Thread::Current() == session_manager_->signaling_thread());
+ if (candidates_requested_) {
+ CritScope cs(&critSM_);
+ if (candidates_.size() > 0) {
+ SignalCandidatesReady(candidates_);
+ candidates_.clear();
+ }
+ }
+ break;
+
+ case MSG_ADDREMOTECANDIDATES:
+ {
+ assert(Thread::Current() == session_manager_->worker_thread());
+ TypedMessageData<const std::vector<Candidate> > *data = static_cast<TypedMessageData<const std::vector<Candidate> > *>(message->pdata);
+ AddRemoteCandidates_w(data->data());
+ delete data;
+ }
+ break;
+
+ case MSG_RESETSOCKETS:
+ ResetSockets_w();
+ break;
+ }
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h
new file mode 100644
index 00000000..3ca1cf74
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/socketmanager.h
@@ -0,0 +1,101 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SOCKETMANAGER_H_
+#define _SOCKETMANAGER_H_
+
+#include "talk/base/criticalsection.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/candidate.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/p2p/base/socketmanager.h"
+
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+class SessionManager;
+
+// Manages P2PSocket creation/destruction/readiness.
+// Provides thread separation between session and sockets.
+// This allows session to execute on the signaling thread,
+// and sockets to execute on the worker thread, if desired,
+// which is good for some media types (audio/video for example).
+
+class SocketManager : public MessageHandler, public sigslot::has_slots<> {
+public:
+ SocketManager(SessionManager *session_manager);
+ virtual ~SocketManager();
+
+ // Determines whether any of the created sockets are currently writable.
+ bool writable() { return writable_; }
+
+ P2PSocket *CreateSocket(const std::string & name);
+ void DestroySocket(P2PSocket *socket);
+
+ // Start discovering local candidates
+ void StartProcessingCandidates();
+
+ // Adds the given candidates that were sent by the remote side.
+ void AddRemoteCandidates(const std::vector<Candidate>& candidates);
+
+ // signaling channel is up, ready to transmit candidates as they are discovered
+ void OnSignalingReady();
+
+ // Put all of the sockets back into the initial state.
+ void ResetSockets();
+
+ sigslot::signal1<const std::vector<Candidate>&> SignalCandidatesReady;
+ sigslot::signal0<> SignalNetworkError;
+ sigslot::signal0<> SignalState;
+ sigslot::signal0<> SignalRequestSignaling;
+
+private:
+ P2PSocket *CreateSocket_w(const std::string &name);
+ void DestroySocket_w(P2PSocket *socket);
+ void OnSignalingReady_w();
+ void AddRemoteCandidates_w(const std::vector<Candidate> &candidates);
+ virtual void OnMessage(Message *message);
+ void OnCandidatesReady(P2PSocket *socket, const std::vector<Candidate>&);
+ void OnSocketState(P2PSocket* socket, P2PSocket::State state);
+ void OnRequestSignaling(void);
+ void ResetSockets_w();
+
+ SessionManager *session_manager_;
+ std::vector<Candidate> candidates_;
+ CriticalSection critSM_;
+ std::vector<P2PSocket *> sockets_;
+ bool candidates_requested_;
+ bool writable_;
+};
+
+}
+
+#endif // _SOCKETMANAGER_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc
new file mode 100644
index 00000000..6a22b238
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.cc
@@ -0,0 +1,576 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/logging.h"
+#include "talk/p2p/base/stun.h"
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::memcpy;
+}
+#endif
+
+namespace cricket {
+
+const std::string STUN_ERROR_REASON_BAD_REQUEST = "BAD REQUEST";
+const std::string STUN_ERROR_REASON_UNAUTHORIZED = "UNAUTHORIZED";
+const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE = "UNKNOWN ATTRIBUTE";
+const std::string STUN_ERROR_REASON_STALE_CREDENTIALS = "STALE CREDENTIALS";
+const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE = "INTEGRITY CHECK FAILURE";
+const std::string STUN_ERROR_REASON_MISSING_USERNAME = "MISSING USERNAME";
+const std::string STUN_ERROR_REASON_USE_TLS = "USE TLS";
+const std::string STUN_ERROR_REASON_SERVER_ERROR = "SERVER ERROR";
+const std::string STUN_ERROR_REASON_GLOBAL_FAILURE = "GLOBAL FAILURE";
+
+StunMessage::StunMessage() : type_(0), length_(0),
+ transaction_id_("0000000000000000") {
+ assert(transaction_id_.size() == 16);
+ attrs_ = new std::vector<StunAttribute*>();
+}
+
+StunMessage::~StunMessage() {
+ for (unsigned i = 0; i < attrs_->size(); i++)
+ delete (*attrs_)[i];
+ delete attrs_;
+}
+
+void StunMessage::SetTransactionID(const std::string& str) {
+ assert(str.size() == 16);
+ transaction_id_ = str;
+}
+
+void StunMessage::AddAttribute(StunAttribute* attr) {
+ attrs_->push_back(attr);
+ length_ += attr->length() + 4;
+}
+
+const StunAddressAttribute*
+StunMessage::GetAddress(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ return reinterpret_cast<const StunAddressAttribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunUInt32Attribute*
+StunMessage::GetUInt32(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ return reinterpret_cast<const StunUInt32Attribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunByteStringAttribute*
+StunMessage::GetByteString(StunAttributeType type) const {
+ switch (type) {
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ case STUN_ATTR_DATA:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return reinterpret_cast<const StunByteStringAttribute*>(GetAttribute(type));
+
+ default:
+ assert(0);
+ return 0;
+ }
+}
+
+const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
+ return reinterpret_cast<const StunErrorCodeAttribute*>(
+ GetAttribute(STUN_ATTR_ERROR_CODE));
+}
+
+const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
+ return reinterpret_cast<const StunUInt16ListAttribute*>(
+ GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
+}
+
+const StunTransportPrefsAttribute* StunMessage::GetTransportPrefs() const {
+ return reinterpret_cast<const StunTransportPrefsAttribute*>(
+ GetAttribute(STUN_ATTR_TRANSPORT_PREFERENCES));
+}
+
+const StunAttribute* StunMessage::GetAttribute(StunAttributeType type) const {
+ for (unsigned i = 0; i < attrs_->size(); i++) {
+ if ((*attrs_)[i]->type() == type)
+ return (*attrs_)[i];
+ }
+ return 0;
+}
+
+bool StunMessage::Read(ByteBuffer* buf) {
+ if (!buf->ReadUInt16(type_))
+ return false;
+
+ if (!buf->ReadUInt16(length_))
+ return false;
+
+ std::string transaction_id;
+ if (!buf->ReadString(transaction_id, 16))
+ return false;
+ assert(transaction_id.size() == 16);
+ transaction_id_ = transaction_id;
+
+ if (length_ > buf->Length())
+ return false;
+
+ attrs_->resize(0);
+
+ size_t rest = buf->Length() - length_;
+ while (buf->Length() > rest) {
+ uint16 attr_type, attr_length;
+ if (!buf->ReadUInt16(attr_type))
+ return false;
+ if (!buf->ReadUInt16(attr_length))
+ return false;
+
+ StunAttribute* attr = StunAttribute::Create(attr_type, attr_length);
+ if (!attr || !attr->Read(buf))
+ return false;
+
+ attrs_->push_back(attr);
+ }
+
+ if (buf->Length() != rest) {
+ // fixme: shouldn't be doing this
+ LOG(LERROR) << "wrong message length"
+ << " (" << (int)rest << " != " << (int)buf->Length() << ")";
+ return false;
+ }
+
+ return true;
+}
+
+void StunMessage::Write(ByteBuffer* buf) const {
+ buf->WriteUInt16(type_);
+ buf->WriteUInt16(length_);
+ buf->WriteString(transaction_id_);
+
+ for (unsigned i = 0; i < attrs_->size(); i++) {
+ buf->WriteUInt16((*attrs_)[i]->type());
+ buf->WriteUInt16((*attrs_)[i]->length());
+ (*attrs_)[i]->Write(buf);
+ }
+}
+
+StunAttribute::StunAttribute(uint16 type, uint16 length)
+ : type_(type), length_(length) {
+}
+
+StunAttribute* StunAttribute::Create(uint16 type, uint16 length) {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ if (length != StunAddressAttribute::SIZE)
+ return 0;
+ return new StunAddressAttribute(type);
+
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ if (length != StunUInt32Attribute::SIZE)
+ return 0;
+ return new StunUInt32Attribute(type);
+
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return (length % 4 == 0) ? new StunByteStringAttribute(type, length) : 0;
+
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ return (length == 20) ? new StunByteStringAttribute(type, length) : 0;
+
+ case STUN_ATTR_DATA:
+ return new StunByteStringAttribute(type, length);
+
+ case STUN_ATTR_ERROR_CODE:
+ if (length < StunErrorCodeAttribute::MIN_SIZE)
+ return 0;
+ return new StunErrorCodeAttribute(type, length);
+
+ case STUN_ATTR_UNKNOWN_ATTRIBUTES:
+ return (length % 2 == 0) ? new StunUInt16ListAttribute(type, length) : 0;
+
+ case STUN_ATTR_TRANSPORT_PREFERENCES:
+ if ((length != StunTransportPrefsAttribute::SIZE1) &&
+ (length != StunTransportPrefsAttribute::SIZE2))
+ return 0;
+ return new StunTransportPrefsAttribute(type, length);
+
+ default:
+ return 0;
+ }
+}
+
+StunAddressAttribute* StunAttribute::CreateAddress(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_MAPPED_ADDRESS:
+ case STUN_ATTR_RESPONSE_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS:
+ case STUN_ATTR_CHANGED_ADDRESS:
+ case STUN_ATTR_REFLECTED_FROM:
+ case STUN_ATTR_ALTERNATE_SERVER:
+ case STUN_ATTR_DESTINATION_ADDRESS:
+ case STUN_ATTR_SOURCE_ADDRESS2:
+ return new StunAddressAttribute(type);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunUInt32Attribute* StunAttribute::CreateUInt32(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_CHANGE_REQUEST:
+ case STUN_ATTR_LIFETIME:
+ case STUN_ATTR_BANDWIDTH:
+ case STUN_ATTR_OPTIONS:
+ return new StunUInt32Attribute(type);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunByteStringAttribute* StunAttribute::CreateByteString(uint16 type) {
+ switch (type) {
+ case STUN_ATTR_USERNAME:
+ case STUN_ATTR_PASSWORD:
+ case STUN_ATTR_MESSAGE_INTEGRITY:
+ case STUN_ATTR_DATA:
+ case STUN_ATTR_MAGIC_COOKIE:
+ return new StunByteStringAttribute(type, 0);
+
+ default:
+ assert(false);
+ return 0;
+ }
+}
+
+StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
+ return new StunErrorCodeAttribute(
+ STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
+}
+
+StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
+ return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
+}
+
+StunTransportPrefsAttribute* StunAttribute::CreateTransportPrefs() {
+ return new StunTransportPrefsAttribute(
+ STUN_ATTR_TRANSPORT_PREFERENCES, StunTransportPrefsAttribute::SIZE1);
+}
+
+StunAddressAttribute::StunAddressAttribute(uint16 type)
+ : StunAttribute(type, SIZE), family_(0), port_(0), ip_(0) {
+}
+
+bool StunAddressAttribute::Read(ByteBuffer* buf) {
+ uint8 dummy;
+ if (!buf->ReadUInt8(dummy))
+ return false;
+ if (!buf->ReadUInt8(family_))
+ return false;
+ if (!buf->ReadUInt16(port_))
+ return false;
+ if (!buf->ReadUInt32(ip_))
+ return false;
+ return true;
+}
+
+void StunAddressAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt8(0);
+ buf->WriteUInt8(family_);
+ buf->WriteUInt16(port_);
+ buf->WriteUInt32(ip_);
+}
+
+StunUInt32Attribute::StunUInt32Attribute(uint16 type)
+ : StunAttribute(type, SIZE), bits_(0) {
+}
+
+bool StunUInt32Attribute::GetBit(int index) const {
+ assert((0 <= index) && (index < 32));
+ return static_cast<bool>((bits_ >> index) & 0x1);
+}
+
+void StunUInt32Attribute::SetBit(int index, bool value) {
+ assert((0 <= index) && (index < 32));
+ bits_ &= ~(1 << index);
+ bits_ |= value ? (1 << index) : 0;
+}
+
+bool StunUInt32Attribute::Read(ByteBuffer* buf) {
+ if (!buf->ReadUInt32(bits_))
+ return false;
+ return true;
+}
+
+void StunUInt32Attribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32(bits_);
+}
+
+StunByteStringAttribute::StunByteStringAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length), bytes_(0) {
+}
+
+StunByteStringAttribute::~StunByteStringAttribute() {
+ delete [] bytes_;
+}
+
+void StunByteStringAttribute::SetBytes(char* bytes, uint16 length) {
+ delete [] bytes_;
+ bytes_ = bytes;
+ SetLength(length);
+}
+
+void StunByteStringAttribute::CopyBytes(const char* bytes) {
+ CopyBytes(bytes, (uint16)strlen(bytes));
+}
+
+void StunByteStringAttribute::CopyBytes(const void* bytes, uint16 length) {
+ char* new_bytes = new char[length];
+ std::memcpy(new_bytes, bytes, length);
+ SetBytes(new_bytes, length);
+}
+
+uint8 StunByteStringAttribute::GetByte(int index) const {
+ assert(bytes_);
+ assert((0 <= index) && (index < length()));
+ return static_cast<uint8>(bytes_[index]);
+}
+
+void StunByteStringAttribute::SetByte(int index, uint8 value) {
+ assert(bytes_);
+ assert((0 <= index) && (index < length()));
+ bytes_[index] = value;
+}
+
+bool StunByteStringAttribute::Read(ByteBuffer* buf) {
+ bytes_ = new char[length()];
+ if (!buf->ReadBytes(bytes_, length()))
+ return false;
+ return true;
+}
+
+void StunByteStringAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteBytes(bytes_, length());
+}
+
+StunErrorCodeAttribute::StunErrorCodeAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length), class_(0), number_(0) {
+}
+
+StunErrorCodeAttribute::~StunErrorCodeAttribute() {
+}
+
+void StunErrorCodeAttribute::SetErrorCode(uint32 code) {
+ class_ = (uint8)((code >> 8) & 0x7);
+ number_ = (uint8)(code & 0xff);
+}
+
+void StunErrorCodeAttribute::SetReason(const std::string& reason) {
+ SetLength(MIN_SIZE + (uint16)reason.size());
+ reason_ = reason;
+}
+
+bool StunErrorCodeAttribute::Read(ByteBuffer* buf) {
+ uint32 val;
+ if (!buf->ReadUInt32(val))
+ return false;
+
+ if ((val >> 11) != 0)
+ LOG(LERROR) << "error-code bits not zero";
+
+ SetErrorCode(val);
+
+ if (!buf->ReadString(reason_, length() - 4))
+ return false;
+
+ return true;
+}
+
+void StunErrorCodeAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32(error_code());
+ buf->WriteString(reason_);
+}
+
+StunUInt16ListAttribute::StunUInt16ListAttribute(uint16 type, uint16 length)
+ : StunAttribute(type, length) {
+ attr_types_ = new std::vector<uint16>();
+}
+
+StunUInt16ListAttribute::~StunUInt16ListAttribute() {
+ delete attr_types_;
+}
+
+size_t StunUInt16ListAttribute::Size() const {
+ return attr_types_->size();
+}
+
+uint16 StunUInt16ListAttribute::GetType(int index) const {
+ return (*attr_types_)[index];
+}
+
+void StunUInt16ListAttribute::SetType(int index, uint16 value) {
+ (*attr_types_)[index] = value;
+}
+
+void StunUInt16ListAttribute::AddType(uint16 value) {
+ attr_types_->push_back(value);
+ SetLength((uint16)attr_types_->size() * 2);
+}
+
+bool StunUInt16ListAttribute::Read(ByteBuffer* buf) {
+ for (int i = 0; i < length() / 2; i++) {
+ uint16 attr;
+ if (!buf->ReadUInt16(attr))
+ return false;
+ attr_types_->push_back(attr);
+ }
+ return true;
+}
+
+void StunUInt16ListAttribute::Write(ByteBuffer* buf) const {
+ for (unsigned i = 0; i < attr_types_->size(); i++)
+ buf->WriteUInt16((*attr_types_)[i]);
+}
+
+StunTransportPrefsAttribute::StunTransportPrefsAttribute(
+ uint16 type, uint16 length)
+ : StunAttribute(type, length), preallocate_(false), prefs_(0), addr_(0) {
+}
+
+StunTransportPrefsAttribute::~StunTransportPrefsAttribute() {
+ delete addr_;
+}
+
+void StunTransportPrefsAttribute::SetPreallocateAddress(
+ StunAddressAttribute* addr) {
+ if (!addr) {
+ preallocate_ = false;
+ addr_ = 0;
+ SetLength(SIZE1);
+ } else {
+ preallocate_ = true;
+ addr_ = addr;
+ SetLength(SIZE2);
+ }
+}
+
+bool StunTransportPrefsAttribute::Read(ByteBuffer* buf) {
+ uint32 val;
+ if (!buf->ReadUInt32(val))
+ return false;
+
+ if ((val >> 3) != 0)
+ LOG(LERROR) << "transport-preferences bits not zero";
+
+ preallocate_ = static_cast<bool>((val >> 2) & 0x1);
+ prefs_ = (uint8)(val & 0x3);
+
+ if (preallocate_ && (prefs_ == 3))
+ LOG(LERROR) << "transport-preferences imcompatible P and Typ";
+
+ if (!preallocate_) {
+ if (length() != StunUInt32Attribute::SIZE)
+ return false;
+ } else {
+ if (length() != StunUInt32Attribute::SIZE + StunAddressAttribute::SIZE)
+ return false;
+
+ addr_ = new StunAddressAttribute(STUN_ATTR_SOURCE_ADDRESS);
+ addr_->Read(buf);
+ }
+
+ return true;
+}
+
+void StunTransportPrefsAttribute::Write(ByteBuffer* buf) const {
+ buf->WriteUInt32((preallocate_ ? 4 : 0) | prefs_);
+
+ if (preallocate_)
+ addr_->Write(buf);
+}
+
+StunMessageType GetStunResponseType(StunMessageType request_type) {
+ switch (request_type) {
+ case STUN_SHARED_SECRET_REQUEST:
+ return STUN_SHARED_SECRET_RESPONSE;
+ case STUN_ALLOCATE_REQUEST:
+ return STUN_ALLOCATE_RESPONSE;
+ case STUN_SEND_REQUEST:
+ return STUN_SEND_RESPONSE;
+ default:
+ return STUN_BINDING_RESPONSE;
+ }
+}
+
+StunMessageType GetStunErrorResponseType(StunMessageType request_type) {
+ switch (request_type) {
+ case STUN_SHARED_SECRET_REQUEST:
+ return STUN_SHARED_SECRET_ERROR_RESPONSE;
+ case STUN_ALLOCATE_REQUEST:
+ return STUN_ALLOCATE_ERROR_RESPONSE;
+ case STUN_SEND_REQUEST:
+ return STUN_SEND_ERROR_RESPONSE;
+ default:
+ return STUN_BINDING_ERROR_RESPONSE;
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h
new file mode 100644
index 00000000..27a8e4be
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stun.h
@@ -0,0 +1,364 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUN_H__
+#define __STUN_H__
+
+// This file contains classes for dealing with the STUN and TURN protocols.
+// Both protocols use the same wire format.
+
+#include "talk/base/basictypes.h"
+#include "talk/base/bytebuffer.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+// These are the types of STUN & TURN messages as of last check.
+enum StunMessageType {
+ STUN_BINDING_REQUEST = 0x0001,
+ STUN_BINDING_RESPONSE = 0x0101,
+ STUN_BINDING_ERROR_RESPONSE = 0x0111,
+ STUN_SHARED_SECRET_REQUEST = 0x0002,
+ STUN_SHARED_SECRET_RESPONSE = 0x0102,
+ STUN_SHARED_SECRET_ERROR_RESPONSE = 0x0112,
+ STUN_ALLOCATE_REQUEST = 0x0003,
+ STUN_ALLOCATE_RESPONSE = 0x0103,
+ STUN_ALLOCATE_ERROR_RESPONSE = 0x0113,
+ STUN_SEND_REQUEST = 0x0004,
+ STUN_SEND_RESPONSE = 0x0104,
+ STUN_SEND_ERROR_RESPONSE = 0x0114,
+ STUN_DATA_INDICATION = 0x0115
+};
+
+// These are the types of attributes defined in STUN & TURN. Next to each is
+// the name of the class (T is StunTAttribute) that implements that type.
+enum StunAttributeType {
+ STUN_ATTR_MAPPED_ADDRESS = 0x0001, // Address
+ STUN_ATTR_RESPONSE_ADDRESS = 0x0002, // Address
+ STUN_ATTR_CHANGE_REQUEST = 0x0003, // UInt32
+ STUN_ATTR_SOURCE_ADDRESS = 0x0004, // Address
+ STUN_ATTR_CHANGED_ADDRESS = 0x0005, // Address
+ STUN_ATTR_USERNAME = 0x0006, // ByteString, multiple of 4 bytes
+ STUN_ATTR_PASSWORD = 0x0007, // ByteString, multiple of 4 bytes
+ STUN_ATTR_MESSAGE_INTEGRITY = 0x0008, // ByteString, 20 bytes
+ STUN_ATTR_ERROR_CODE = 0x0009, // ErrorCode
+ STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000a, // UInt16List
+ STUN_ATTR_REFLECTED_FROM = 0x000b, // Address
+ STUN_ATTR_TRANSPORT_PREFERENCES = 0x000c, // TransportPrefs
+ STUN_ATTR_LIFETIME = 0x000d, // UInt32
+ STUN_ATTR_ALTERNATE_SERVER = 0x000e, // Address
+ STUN_ATTR_MAGIC_COOKIE = 0x000f, // ByteString, 4 bytes
+ STUN_ATTR_BANDWIDTH = 0x0010, // UInt32
+ STUN_ATTR_DESTINATION_ADDRESS = 0x0011, // Address
+ STUN_ATTR_SOURCE_ADDRESS2 = 0x0012, // Address
+ STUN_ATTR_DATA = 0x0013, // ByteString
+ STUN_ATTR_OPTIONS = 0x8001 // UInt32
+};
+
+enum StunErrorCodes {
+ STUN_ERROR_BAD_REQUEST = 400,
+ STUN_ERROR_UNAUTHORIZED = 401,
+ STUN_ERROR_UNKNOWN_ATTRIBUTE = 420,
+ STUN_ERROR_STALE_CREDENTIALS = 430,
+ STUN_ERROR_INTEGRITY_CHECK_FAILURE = 431,
+ STUN_ERROR_MISSING_USERNAME = 432,
+ STUN_ERROR_USE_TLS = 433,
+ STUN_ERROR_SERVER_ERROR = 500,
+ STUN_ERROR_GLOBAL_FAILURE = 600
+};
+
+extern const std::string STUN_ERROR_REASON_BAD_REQUEST;
+extern const std::string STUN_ERROR_REASON_UNAUTHORIZED;
+extern const std::string STUN_ERROR_REASON_UNKNOWN_ATTRIBUTE;
+extern const std::string STUN_ERROR_REASON_STALE_CREDENTIALS;
+extern const std::string STUN_ERROR_REASON_INTEGRITY_CHECK_FAILURE;
+extern const std::string STUN_ERROR_REASON_MISSING_USERNAME;
+extern const std::string STUN_ERROR_REASON_USE_TLS;
+extern const std::string STUN_ERROR_REASON_SERVER_ERROR;
+extern const std::string STUN_ERROR_REASON_GLOBAL_FAILURE;
+
+class StunAttribute;
+class StunAddressAttribute;
+class StunUInt32Attribute;
+class StunByteStringAttribute;
+class StunErrorCodeAttribute;
+class StunUInt16ListAttribute;
+class StunTransportPrefsAttribute;
+
+// Records a complete STUN/TURN message. Each message consists of a type and
+// any number of attributes. Each attribute is parsed into an instance of an
+// appropriate class (see above). The Get* methods will return instances of
+// that attribute class.
+class StunMessage {
+public:
+ StunMessage();
+ ~StunMessage();
+
+ StunMessageType type() const { return static_cast<StunMessageType>(type_); }
+ uint16 length() const { return length_; }
+ const std::string& transaction_id() const { return transaction_id_; }
+
+ void SetType(StunMessageType type) { type_ = type; }
+ void SetTransactionID(const std::string& str);
+
+ const StunAddressAttribute* GetAddress(StunAttributeType type) const;
+ const StunUInt32Attribute* GetUInt32(StunAttributeType type) const;
+ const StunByteStringAttribute* GetByteString(StunAttributeType type) const;
+ const StunErrorCodeAttribute* GetErrorCode() const;
+ const StunUInt16ListAttribute* GetUnknownAttributes() const;
+ const StunTransportPrefsAttribute* GetTransportPrefs() const;
+
+ void AddAttribute(StunAttribute* attr);
+
+ // Parses the STUN/TURN packet in the given buffer and records it here. The
+ // return value indicates whether this was successful.
+ bool Read(ByteBuffer* buf);
+
+ // Writes this object into a STUN/TURN packet. Return value is true if
+ // successful.
+ void Write(ByteBuffer* buf) const;
+
+private:
+ uint16 type_;
+ uint16 length_;
+ std::string transaction_id_;
+ std::vector<StunAttribute*>* attrs_;
+
+ const StunAttribute* GetAttribute(StunAttributeType type) const;
+};
+
+// Base class for all STUN/TURN attributes.
+class StunAttribute {
+public:
+ virtual ~StunAttribute() {}
+
+ StunAttributeType type() const {
+ return static_cast<StunAttributeType>(type_);
+ }
+ uint16 length() const { return length_; }
+
+ // Reads the body (not the type or length) for this type of attribute from
+ // the given buffer. Return value is true if successful.
+ virtual bool Read(ByteBuffer* buf) = 0;
+
+ // Writes the body (not the type or length) to the given buffer. Return
+ // value is true if successful.
+ virtual void Write(ByteBuffer* buf) const = 0;
+
+ // Creates an attribute object with the given type and len.
+ static StunAttribute* Create(uint16 type, uint16 length);
+
+ // Creates an attribute object with the given type and smallest length.
+ static StunAddressAttribute* CreateAddress(uint16 type);
+ static StunUInt32Attribute* CreateUInt32(uint16 type);
+ static StunByteStringAttribute* CreateByteString(uint16 type);
+ static StunErrorCodeAttribute* CreateErrorCode();
+ static StunUInt16ListAttribute* CreateUnknownAttributes();
+ static StunTransportPrefsAttribute* CreateTransportPrefs();
+
+protected:
+ StunAttribute(uint16 type, uint16 length);
+
+ void SetLength(uint16 length) { length_ = length; }
+
+private:
+ uint16 type_;
+ uint16 length_;
+};
+
+// Implements STUN/TURN attributes that record an Internet address.
+class StunAddressAttribute : public StunAttribute {
+public:
+ StunAddressAttribute(uint16 type);
+
+#if (_MSC_VER < 1300)
+ enum { SIZE = 8 };
+#else
+ static const uint16 SIZE = 8;
+#endif
+
+ uint8 family() const { return family_; }
+ uint16 port() const { return port_; }
+ uint32 ip() const { return ip_; }
+
+ void SetFamily(uint8 family) { family_ = family; }
+ void SetIP(uint32 ip) { ip_ = ip; }
+ void SetPort(uint16 port) { port_ = port; }
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ uint8 family_;
+ uint16 port_;
+ uint32 ip_;
+};
+
+// Implements STUN/TURN attributs that record a 32-bit integer.
+class StunUInt32Attribute : public StunAttribute {
+public:
+ StunUInt32Attribute(uint16 type);
+
+#if (_MSC_VER < 1300)
+ enum { SIZE = 4 };
+#else
+ static const uint16 SIZE = 4;
+#endif
+
+ uint32 value() const { return bits_; }
+
+ void SetValue(uint32 bits) { bits_ = bits; }
+
+ bool GetBit(int index) const;
+ void SetBit(int index, bool value);
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ uint32 bits_;
+};
+
+// Implements STUN/TURN attributs that record an arbitrary byte string
+class StunByteStringAttribute : public StunAttribute {
+public:
+ StunByteStringAttribute(uint16 type, uint16 length);
+ ~StunByteStringAttribute();
+
+ const char* bytes() const { return bytes_; }
+
+ void SetBytes(char* bytes, uint16 length);
+
+ void CopyBytes(const char* bytes); // uses strlen
+ void CopyBytes(const void* bytes, uint16 length);
+
+ uint8 GetByte(int index) const;
+ void SetByte(int index, uint8 value);
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ char* bytes_;
+};
+
+// Implements STUN/TURN attributs that record an error code.
+class StunErrorCodeAttribute : public StunAttribute {
+public:
+ StunErrorCodeAttribute(uint16 type, uint16 length);
+ ~StunErrorCodeAttribute();
+
+#if (_MSC_VER < 1300)
+ enum { MIN_SIZE = 4 };
+#else
+ static const uint16 MIN_SIZE = 4;
+#endif
+
+ uint32 error_code() const { return (class_ << 8) | number_; }
+ uint8 error_class() const { return class_; }
+ uint8 number() const { return number_; }
+ const std::string& reason() const { return reason_; }
+
+ void SetErrorCode(uint32 code);
+ void SetErrorClass(uint8 eclass) { class_ = eclass; }
+ void SetNumber(uint8 number) { number_ = number; }
+ void SetReason(const std::string& reason);
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ uint8 class_;
+ uint8 number_;
+ std::string reason_;
+};
+
+// Implements STUN/TURN attributs that record a list of attribute names.
+class StunUInt16ListAttribute : public StunAttribute {
+public:
+ StunUInt16ListAttribute(uint16 type, uint16 length);
+ ~StunUInt16ListAttribute();
+
+ size_t Size() const;
+ uint16 GetType(int index) const;
+ void SetType(int index, uint16 value);
+ void AddType(uint16 value);
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ std::vector<uint16>* attr_types_;
+};
+
+// Implements the TURN TRANSPORT-PREFS attribute, which provides information
+// about the ports to allocate.
+class StunTransportPrefsAttribute : public StunAttribute {
+public:
+ StunTransportPrefsAttribute(uint16 type, uint16 length);
+ ~StunTransportPrefsAttribute();
+
+#if (_MSC_VER < 1300)
+ enum { SIZE1 = 4, SIZE2 = 12 };
+#else
+ static const uint16 SIZE1 = 4;
+ static const uint16 SIZE2 = 12;
+#endif
+
+ bool preallocate() const { return preallocate_; }
+ uint8 preference_type() const { return prefs_; }
+ const StunAddressAttribute* address() const { return addr_; }
+
+ void SetPreferenceType(uint8 prefs) { prefs_ = prefs; }
+
+ // Sets the preallocate address to the given value, or if 0 is given, it sets
+ // to not preallocate.
+ void SetPreallocateAddress(StunAddressAttribute* addr);
+
+ bool Read(ByteBuffer* buf);
+ void Write(ByteBuffer* buf) const;
+
+private:
+ bool preallocate_;
+ uint8 prefs_;
+ StunAddressAttribute* addr_;
+};
+
+// The special MAGIC-COOKIE attribute is used to distinguish TURN packets from
+// other kinds of traffic.
+const char STUN_MAGIC_COOKIE_VALUE[] = { 0x72, char(0xc6), 0x4b, char(0xc6) };
+
+// Returns the (successful) response type for the given request type.
+StunMessageType GetStunResponseType(StunMessageType request_type);
+
+// Returns the error response type for the given request type.
+StunMessageType GetStunErrorResponseType(StunMessageType request_type);
+
+} // namespace cricket
+
+#endif // __STUN_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc
new file mode 100644
index 00000000..6d1dc6b1
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.cc
@@ -0,0 +1,171 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/helpers.h"
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const int KEEPALIVE_DELAY = 10 * 1000; // 10 seconds - sort timeouts
+const int RETRY_DELAY = 50; // 50ms, from ICE spec
+const uint32 RETRY_TIMEOUT = 50 * 1000; // ICE says 50 secs
+
+// Handles a binding request sent to the STUN server.
+class StunPortBindingRequest : public StunRequest {
+public:
+ StunPortBindingRequest(StunPort* port) : port_(port) {
+ start_time_ = GetMillisecondCount();
+ }
+
+ virtual ~StunPortBindingRequest() {
+ }
+
+ virtual void Prepare(StunMessage* request) {
+ request->SetType(STUN_BINDING_REQUEST);
+ }
+
+ virtual void OnResponse(StunMessage* response) {
+ const StunAddressAttribute* addr_attr =
+ response->GetAddress(STUN_ATTR_MAPPED_ADDRESS);
+ if (!addr_attr) {
+ LOG(LERROR) << "Binding response missing mapped address.";
+ } else if (addr_attr->family() != 1) {
+ LOG(LERROR) << "Binding address has bad family";
+ } else {
+ SocketAddress addr(addr_attr->ip(), addr_attr->port());
+ if (port_->candidates().empty())
+ port_->add_address(addr, "udp");
+ }
+
+ // We will do a keep-alive regardless of whether this request suceeds.
+ // This should have almost no impact on network usage.
+ port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY);
+ }
+
+ virtual void OnErrorResponse(StunMessage* response) {
+ const StunErrorCodeAttribute* attr = response->GetErrorCode();
+ if (!attr) {
+ LOG(LERROR) << "Bad allocate response error code";
+ } else {
+ LOG(LERROR) << "Binding error response:"
+ << " class=" << attr->error_class()
+ << " number=" << attr->number()
+ << " reason='" << attr->reason() << "'";
+ }
+
+ if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)
+ port_->requests_.SendDelayed(new StunPortBindingRequest(port_), KEEPALIVE_DELAY);
+ }
+
+ virtual void OnTimeout() {
+ LOG(LERROR) << "Binding request timed out";
+ if (GetMillisecondCount() - start_time_ <= RETRY_TIMEOUT)
+ port_->requests_.SendDelayed(new StunPortBindingRequest(port_), RETRY_DELAY);
+ }
+
+private:
+ uint32 start_time_;
+ StunPort* port_;
+};
+
+const std::string STUN_PORT_TYPE("stun");
+
+StunPort::StunPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& local_addr,
+ const SocketAddress& server_addr)
+ : UDPPort(thread, STUN_PORT_TYPE, factory, network),
+ server_addr_(server_addr), requests_(thread), error_(0) {
+
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ socket_->SignalReadPacket.connect(this, &StunPort::OnReadPacket);
+ if (socket_->Bind(local_addr) < 0)
+ PLOG(LERROR, socket_->GetError()) << "bind";
+
+ requests_.SignalSendPacket.connect(this, &StunPort::OnSendPacket);
+}
+
+StunPort::~StunPort() {
+ delete socket_;
+}
+
+void StunPort::PrepareAddress() {
+ requests_.Send(new StunPortBindingRequest(this));
+}
+
+int StunPort::SendTo(
+ const void* data, size_t size, const SocketAddress& addr, bool payload) {
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0)
+ error_ = socket_->GetError();
+ return sent;
+}
+
+int StunPort::SetOption(Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int StunPort::GetError() {
+ return error_;
+}
+
+void StunPort::OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+
+ // Look for a response to a binding request.
+ if (requests_.CheckResponse(data, size))
+ return;
+
+ // Process this data packet in the normal manner.
+ UDPPort::OnReadPacket(data, size, remote_addr);
+}
+
+void StunPort::OnSendPacket(const void* data, size_t size) {
+ if (socket_->SendTo(data, size, server_addr_) < 0)
+ PLOG(LERROR, socket_->GetError()) << "sendto";
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h
new file mode 100644
index 00000000..f042ae14
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunport.h
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNPORT_H__
+#define __STUNPORT_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/stunrequest.h"
+
+namespace cricket {
+
+extern const std::string STUN_PORT_TYPE;
+
+// Communicates using the address on the outside of a NAT.
+class StunPort : public UDPPort {
+public:
+ StunPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& local_addr, const SocketAddress& server_addr);
+ virtual ~StunPort();
+
+ virtual void PrepareAddress();
+
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload);
+
+ void OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+private:
+ AsyncPacketSocket* socket_;
+ SocketAddress server_addr_;
+ StunRequestManager requests_;
+ int error_;
+
+ friend class StunPortBindingRequest;
+
+ // Sends STUN requests to the server.
+ void OnSendPacket(const void* data, size_t size);
+};
+
+} // namespace cricket
+
+#endif // __STUNPORT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc
new file mode 100644
index 00000000..14d64735
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.cc
@@ -0,0 +1,198 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/p2p/base/stunrequest.h"
+#include "talk/p2p/base/helpers.h"
+#include <iostream>
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_STUN_SEND = 1;
+
+const int MAX_SENDS = 9;
+const int DELAY_UNIT = 100; // 100 milliseconds
+const int DELAY_MAX_FACTOR = 16;
+
+StunRequestManager::StunRequestManager(Thread* thread) : thread_(thread) {
+}
+
+StunRequestManager::~StunRequestManager() {
+ while (requests_.begin() != requests_.end()) {
+ StunRequest *request = requests_.begin()->second;
+ requests_.erase(requests_.begin());
+ delete request;
+ }
+}
+
+void StunRequestManager::Send(StunRequest* request) {
+ SendDelayed(request, 0);
+}
+
+void StunRequestManager::SendDelayed(StunRequest* request, int delay) {
+ request->set_manager(this);
+ assert(requests_.find(request->id()) == requests_.end());
+ requests_[request->id()] = request;
+ thread_->PostDelayed(delay, request, MSG_STUN_SEND, NULL);
+}
+
+void StunRequestManager::Remove(StunRequest* request) {
+ assert(request->manager() == this);
+ RequestMap::iterator iter = requests_.find(request->id());
+ if (iter != requests_.end()) {
+ assert(iter->second == request);
+ requests_.erase(iter);
+ thread_->Clear(request);
+ }
+}
+
+void StunRequestManager::Clear() {
+ std::vector<StunRequest*> requests;
+ for (RequestMap::iterator i = requests_.begin(); i != requests_.end(); ++i)
+ requests.push_back(i->second);
+
+ for (uint32 i = 0; i < requests.size(); ++i)
+ Remove(requests[i]);
+}
+
+bool StunRequestManager::CheckResponse(StunMessage* msg) {
+ RequestMap::iterator iter = requests_.find(msg->transaction_id());
+ if (iter == requests_.end())
+ return false;
+
+ StunRequest* request = iter->second;
+ if (msg->type() == GetStunResponseType(request->type())) {
+ request->OnResponse(msg);
+ } else if (msg->type() == GetStunErrorResponseType(request->type())) {
+ request->OnErrorResponse(msg);
+ } else {
+ LOG(LERROR) << "Received response with wrong type: " << msg->type()
+ << " (expecting " << GetStunResponseType(request->type()) << ")";
+ return false;
+ }
+
+ delete request;
+ return true;
+}
+
+bool StunRequestManager::CheckResponse(const char* data, size_t size) {
+ // Check the appropriate bytes of the stream to see if they match the
+ // transaction ID of a response we are expecting.
+
+ if (size < 20)
+ return false;
+
+ std::string id;
+ id.append(data + 4, 16);
+
+ RequestMap::iterator iter = requests_.find(id);
+ if (iter == requests_.end())
+ return false;
+
+ // Parse the STUN message and continue processing as usual.
+
+ ByteBuffer buf(data, size);
+ StunMessage msg;
+ if (!msg.Read(&buf))
+ return false;
+
+ return CheckResponse(&msg);
+}
+
+StunRequest::StunRequest()
+ : manager_(0), id_(CreateRandomString(16)), msg_(0), count_(0),
+ timeout_(false), tstamp_(0) {
+}
+
+StunRequest::StunRequest(StunMessage* request)
+ : manager_(0), id_(request->transaction_id()), msg_(request),
+ count_(0), timeout_(false) {
+}
+
+StunRequest::~StunRequest() {
+ assert(manager_ != NULL);
+ if (manager_) {
+ manager_->Remove(this);
+ manager_->thread_->Clear(this);
+ }
+ delete msg_;
+}
+
+const StunMessageType StunRequest::type() {
+ assert(msg_);
+ return msg_->type();
+}
+
+void StunRequest::set_manager(StunRequestManager* manager) {
+ assert(!manager_);
+ manager_ = manager;
+}
+
+void StunRequest::OnMessage(Message* pmsg) {
+ assert(manager_);
+ assert(pmsg->message_id == MSG_STUN_SEND);
+
+ if (!msg_) {
+ msg_ = new StunMessage();
+ msg_->SetTransactionID(id_);
+ Prepare(msg_);
+ assert(msg_->transaction_id() == id_);
+ }
+
+ if (timeout_) {
+ OnTimeout();
+ delete this;
+ return;
+ }
+
+ tstamp_ = GetMillisecondCount();
+
+ ByteBuffer buf;
+ msg_->Write(&buf);
+ manager_->SignalSendPacket(buf.Data(), buf.Length());
+
+ int delay = GetNextDelay();
+ manager_->thread_->PostDelayed(delay, this, MSG_STUN_SEND, NULL);
+}
+
+uint32 StunRequest::Elapsed() const {
+ return (GetMillisecondCount() - tstamp_);
+}
+
+int StunRequest::GetNextDelay() {
+ int delay = DELAY_UNIT * _min(1 << count_, DELAY_MAX_FACTOR);
+ count_ += 1;
+ if (count_ == MAX_SENDS)
+ timeout_ = true;
+ return delay;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h
new file mode 100644
index 00000000..86acff91
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunrequest.h
@@ -0,0 +1,126 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNREQUESTMANAGER_H__
+#define __STUNREQUESTMANAGER_H__
+
+#include "talk/base/sigslot.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/stun.h"
+#include <map>
+#include <string>
+
+namespace cricket {
+
+class StunRequest;
+
+// Manages a set of STUN requests, sending and resending until we receive a
+// response or determine that the request has timed out.
+class StunRequestManager {
+public:
+ StunRequestManager(Thread* thread);
+ ~StunRequestManager();
+
+ // Starts sending the given request (perhaps after a delay).
+ void Send(StunRequest* request);
+ void SendDelayed(StunRequest* request, int delay);
+
+ // Removes a stun request that was added previously. This will happen
+ // automatically when a request succeeds, fails, or times out.
+ void Remove(StunRequest* request);
+
+ // Removes all stun requests that were added previously.
+ void Clear();
+
+ // Determines whether the given message is a response to one of the
+ // outstanding requests, and if so, processes it appropriately.
+ bool CheckResponse(StunMessage* msg);
+ bool CheckResponse(const char* data, size_t size);
+
+ // Raised when there are bytes to be sent.
+ sigslot::signal2<const void*, size_t> SignalSendPacket;
+
+private:
+ typedef std::map<std::string, StunRequest*> RequestMap;
+
+ Thread* thread_;
+ RequestMap requests_;
+
+ friend class StunRequest;
+};
+
+// Represents an individual request to be sent. The STUN message can either be
+// constructed beforehand or built on demand.
+class StunRequest : public MessageHandler {
+public:
+ StunRequest();
+ StunRequest(StunMessage* request);
+ virtual ~StunRequest();
+
+ // The manager handling this request (if it has been scheduled for sending).
+ StunRequestManager* manager() { return manager_; }
+
+ // Returns the transaction ID of this request.
+ const std::string& id() { return id_; }
+
+ // Returns the STUN type of the request message.
+ const StunMessageType type();
+
+ // Handles messages for sending and timeout.
+ void OnMessage(Message* pmsg);
+
+ // Time elapsed since last send (in ms)
+ uint32 Elapsed() const;
+
+protected:
+ int count_;
+ bool timeout_;
+
+ // Fills in the actual request to be sent. Note that the transaction ID will
+ // already be set and cannot be changed.
+ virtual void Prepare(StunMessage* request) {}
+
+ // Called when the message receives a response or times out.
+ virtual void OnResponse(StunMessage* response) {}
+ virtual void OnErrorResponse(StunMessage* response) {}
+ virtual void OnTimeout() {}
+ virtual int GetNextDelay();
+
+private:
+ StunRequestManager* manager_;
+ std::string id_;
+ StunMessage* msg_;
+ uint32 tstamp_;
+
+ void set_manager(StunRequestManager* manager);
+
+ friend class StunRequestManager;
+};
+
+} // namespace cricket
+
+#endif // __STUNREQUESTMANAGER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc
new file mode 100644
index 00000000..6e4f6b66
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.cc
@@ -0,0 +1,160 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/bytebuffer.h"
+#include "talk/p2p/base/stunserver.h"
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+StunServer::StunServer(AsyncUDPSocket* socket) : socket_(socket) {
+ socket_->SignalReadPacket.connect(this, &StunServer::OnPacket);
+}
+
+StunServer::~StunServer() {
+ socket_->SignalReadPacket.disconnect(this);
+}
+
+void StunServer::OnPacket(
+ const char* buf, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+
+ // TODO: If appropriate, look for the magic cookie before parsing.
+
+ // Parse the STUN message.
+ ByteBuffer bbuf(buf, size);
+ StunMessage msg;
+ if (!msg.Read(&bbuf)) {
+ SendErrorResponse(msg, remote_addr, 400, "Bad Request");
+ return;
+ }
+
+ // TODO: If this is UDP, then we shouldn't allow non-fully-parsed messages.
+
+ // TODO: If unknown non-optiional (<= 0x7fff) attributes are found, send a
+ // 420 "Unknown Attribute" response.
+
+ // TODO: Check that a message-integrity attribute was given (or send 401
+ // "Unauthorized"). Check that a username attribute was given (or send
+ // 432 "Missing Username"). Look up the username and password. If it
+ // is missing or the HMAC is wrong, send 431 "Integrity Check Failure".
+
+ // Send the message to the appropriate handler function.
+ switch (msg.type()) {
+ case STUN_BINDING_REQUEST:
+ OnBindingRequest(&msg, remote_addr);
+ return;
+
+ case STUN_ALLOCATE_REQUEST:
+ OnAllocateRequest(&msg, remote_addr);
+ return;
+
+ default:
+ SendErrorResponse(msg, remote_addr, 600, "Operation Not Supported");
+ }
+}
+
+void StunServer::OnBindingRequest(
+ StunMessage* msg, const SocketAddress& remote_addr) {
+ StunMessage response;
+ response.SetType(STUN_BINDING_RESPONSE);
+ response.SetTransactionID(msg->transaction_id());
+
+ // Tell the user the address that we received their request from.
+ StunAddressAttribute* mapped_addr =
+ StunAttribute::CreateAddress(STUN_ATTR_MAPPED_ADDRESS);
+ mapped_addr->SetFamily(1);
+ mapped_addr->SetPort(remote_addr.port());
+ mapped_addr->SetIP(remote_addr.ip());
+ response.AddAttribute(mapped_addr);
+
+ // Tell the user the address that we are sending the response from.
+ SocketAddress local_addr = socket_->GetLocalAddress();
+ StunAddressAttribute* source_addr =
+ StunAttribute::CreateAddress(STUN_ATTR_SOURCE_ADDRESS);
+ source_addr->SetFamily(1);
+ source_addr->SetPort(local_addr.port());
+ source_addr->SetIP(local_addr.ip());
+ response.AddAttribute(source_addr);
+
+ // TODO: Add username and message-integrity.
+
+ // TODO: Add changed-address. (Keep information about three other servers.)
+
+ SendResponse(response, remote_addr);
+}
+
+void StunServer::OnAllocateRequest(
+ StunMessage* msg, const SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::OnSharedSecretRequest(
+ StunMessage* msg, const SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::OnSendRequest(StunMessage* msg, const SocketAddress& addr) {
+ SendErrorResponse(*msg, addr, 600, "Operation Not Supported");
+}
+
+void StunServer::SendErrorResponse(
+ const StunMessage& msg, const SocketAddress& addr, int error_code,
+ const char* error_desc) {
+
+ StunMessage err_msg;
+ err_msg.SetType(GetStunErrorResponseType(msg.type()));
+ err_msg.SetTransactionID(msg.transaction_id());
+
+ StunErrorCodeAttribute* err_code = StunAttribute::CreateErrorCode();
+ err_code->SetErrorClass(error_code / 100);
+ err_code->SetNumber(error_code % 100);
+ err_code->SetReason(error_desc);
+ err_msg.AddAttribute(err_code);
+
+ SendResponse(err_msg, addr);
+}
+
+void StunServer::SendResponse(
+ const StunMessage& msg, const SocketAddress& addr) {
+
+ ByteBuffer buf;
+ msg.Write(&buf);
+
+ // TODO: Allow response addr attribute if sent from another stun server.
+
+ if (socket_->SendTo(buf.Data(), buf.Length(), addr) < 0)
+ std::cerr << "sendto: " << std::strerror(errno) << std::endl;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h
new file mode 100644
index 00000000..3043645d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.h
@@ -0,0 +1,73 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __STUNSERVER_H__
+#define __STUNSERVER_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/stun.h"
+
+namespace cricket {
+
+const int STUN_SERVER_PORT = 3478;
+
+class StunServer : public sigslot::has_slots<> {
+public:
+ // Creates a STUN server, which will listen on the given socket.
+ StunServer(AsyncUDPSocket* socket);
+
+ // Removes the STUN server from the socket, but does not delete the socket.
+ ~StunServer();
+
+protected:
+
+ // Slot for AsyncSocket.PacketRead:
+ void OnPacket(
+ const char* buf, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+ // Handlers for the different types of STUN/TURN requests:
+ void OnBindingRequest(StunMessage* msg, const SocketAddress& addr);
+ void OnAllocateRequest(StunMessage* msg, const SocketAddress& addr);
+ void OnSharedSecretRequest(StunMessage* msg, const SocketAddress& addr);
+ void OnSendRequest(StunMessage* msg, const SocketAddress& addr);
+
+ // Sends an error response to the given message back to the user.
+ void SendErrorResponse(
+ const StunMessage& msg, const SocketAddress& addr, int error_code,
+ const char* error_desc);
+
+ // Sends the given message to the appropriate destination.
+ void SendResponse(const StunMessage& msg, const SocketAddress& addr);
+
+private:
+ AsyncUDPSocket* socket_;
+};
+
+} // namespace cricket
+
+#endif // __STUNSERVER_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro
new file mode 100644
index 00000000..dce92ec4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver.pro
@@ -0,0 +1,14 @@
+TEMPLATE = app
+INCLUDEPATH = ../../..
+DEFINES += POSIX
+
+include(../../../../../conf.pri)
+
+# Input
+SOURCES += \
+ stunserver.cc \
+ stunserver_main.cc \
+ ../../base/host.cc #\
+# ../../base/socketaddresspair.cc
+
+LIBS += ../../../liblibjingle.a
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc
new file mode 100644
index 00000000..bd8a96e5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/stunserver_main.cc
@@ -0,0 +1,66 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/host.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/base/stunserver.h"
+#include <iostream>
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+using namespace cricket;
+
+int main(int argc, char* argv[]) {
+ if (argc != 1) {
+ std::cerr << "usage: stunserver" << std::endl;
+ return 1;
+ }
+
+ SocketAddress server_addr(LocalHost().networks()[1]->ip(), 7000);
+
+ Thread *pthMain = Thread::Current();
+
+ AsyncUDPSocket* server_socket = CreateAsyncUDPSocket(pthMain->socketserver());
+ if (server_socket->Bind(server_addr) < 0) {
+ std::cerr << "bind: " << std::strerror(errno) << std::endl;
+ return 1;
+ }
+
+ StunServer* server = new StunServer(server_socket);
+
+ std::cout << "Listening at " << server_addr.ToString() << std::endl;
+
+ pthMain->Loop();
+
+ delete server;
+ delete server_socket;
+ return 0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc
new file mode 100644
index 00000000..a2d2adc6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.cc
@@ -0,0 +1,250 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/p2p/base/tcpport.h"
+#include "talk/base/logging.h"
+#ifdef WIN32
+#include "talk/base/winfirewall.h"
+#endif // WIN32
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+#ifdef WIN32
+static WinFirewall win_firewall;
+#endif // WIN32
+
+TCPPort::TCPPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& address)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) {
+ incoming_only_ = (address.port() != 0);
+ socket_ = thread->socketserver()->CreateAsyncSocket(SOCK_STREAM);
+ socket_->SignalReadEvent.connect(this, &TCPPort::OnAcceptEvent);
+ if (socket_->Bind(address) < 0)
+ LOG(INFO) << "bind: " << std::strerror(socket_->GetError());
+}
+
+TCPPort::~TCPPort() {
+ delete socket_;
+}
+
+Connection* TCPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) {
+ // We only support TCP protocols
+ if ((address.protocol() != "tcp") && (address.protocol() != "ssltcp"))
+ return 0;
+
+ // We can't accept TCP connections incoming on other ports
+ if (origin == ORIGIN_OTHER_PORT)
+ return 0;
+
+ // Check if we are allowed to make outgoing TCP connections
+ if (incoming_only_ && (origin == ORIGIN_MESSAGE))
+ return 0;
+
+ // We don't know how to act as an ssl server yet
+ if ((address.protocol() == "ssltcp") && (origin == ORIGIN_THIS_PORT))
+ return 0;
+
+ TCPConnection* conn = 0;
+ if (AsyncTCPSocket * socket = GetIncoming(address.address(), true)) {
+ socket->SignalReadPacket.disconnect(this);
+ conn = new TCPConnection(this, address, socket);
+ } else {
+ conn = new TCPConnection(this, address);
+ }
+ AddConnection(conn);
+ return conn;
+}
+
+void TCPPort::PrepareAddress() {
+ assert(socket_);
+
+ bool allow_listen = true;
+#ifdef WIN32
+ if (win_firewall.Initialize()) {
+ char module_path[MAX_PATH + 1] = { 0 };
+ ::GetModuleFileNameA(NULL, module_path, MAX_PATH);
+ if (win_firewall.Enabled() && !win_firewall.Authorized(module_path)) {
+ allow_listen = false;
+ }
+ }
+#endif // WIN32
+ if (allow_listen) {
+ if (socket_->Listen(5) < 0)
+ LOG(INFO) << "listen: " << std::strerror(socket_->GetError());
+ } else {
+ LOG(INFO) << "not listening due to firewall restrictions";
+ }
+ // Note: We still add the address, since otherwise the remote side won't recognize
+ // our incoming TCP connections.
+ add_address(socket_->GetLocalAddress(), "tcp");
+}
+
+int TCPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) {
+ AsyncTCPSocket * socket = 0;
+
+ if (TCPConnection * conn = static_cast<TCPConnection*>(GetConnection(addr))) {
+ socket = conn->socket();
+ } else {
+ socket = GetIncoming(addr);
+ }
+ if (!socket) {
+ LOG(INFO) << "Unknown destination for SendTo: " << addr.ToString();
+ return -1; // TODO: Set error_
+ }
+
+ //LOG(INFO) << "TCPPort::SendTo(" << size << ", " << addr.ToString() << ")";
+
+ int sent = socket->Send(data, size);
+ if (sent < 0)
+ error_ = socket->GetError();
+ return sent;
+}
+
+int TCPPort::SetOption(Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int TCPPort::GetError() {
+ assert(socket_);
+ return error_;
+}
+
+void TCPPort::OnAcceptEvent(AsyncSocket* socket) {
+ assert(socket == socket_);
+
+ Incoming incoming;
+ AsyncSocket * newsocket = static_cast<AsyncSocket *>(socket->Accept(&incoming.addr));
+ if (!newsocket) {
+ // TODO: Do something better like forwarding the error to the user.
+ LOG(INFO) << "accept: " << socket_->GetError() << " " << std::strerror(socket_->GetError());
+ return;
+ }
+ incoming.socket = new AsyncTCPSocket(newsocket);
+ incoming.socket->SignalReadPacket.connect(this, &TCPPort::OnReadPacket);
+
+ LOG(INFO) << "accepted incoming connection from " << incoming.addr.ToString();
+ incoming_.push_back(incoming);
+
+ // Prime a read event in case data is waiting
+ newsocket->SignalReadEvent(newsocket);
+}
+
+AsyncTCPSocket * TCPPort::GetIncoming(const SocketAddress& addr, bool remove) {
+ AsyncTCPSocket * socket = 0;
+ for (std::list<Incoming>::iterator it = incoming_.begin(); it != incoming_.end(); ++it) {
+ if (it->addr == addr) {
+ socket = it->socket;
+ if (remove)
+ incoming_.erase(it);
+ break;
+ }
+ }
+ return socket;
+}
+
+void TCPPort::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ Port::OnReadPacket(data, size, remote_addr);
+}
+
+TCPConnection::TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket)
+ : Connection(port, 0, candidate), socket_(socket), error_(0) {
+ bool outgoing = (socket_ == 0);
+ if (outgoing) {
+ socket_ = static_cast<AsyncTCPSocket *>(port->CreatePacketSocket(
+ (candidate.protocol() == "ssltcp") ? PROTO_SSLTCP : PROTO_TCP));
+ }
+ socket_->SignalReadPacket.connect(this, &TCPConnection::OnReadPacket);
+ socket_->SignalClose.connect(this, &TCPConnection::OnClose);
+ if (outgoing) {
+ connected_ = false;
+ socket_->SignalConnect.connect(this, &TCPConnection::OnConnect);
+ socket_->Connect(candidate.address());
+ LOG(INFO) << "Connecting to " << candidate.address().ToString();
+ }
+}
+
+TCPConnection::~TCPConnection() {
+}
+
+int TCPConnection::Send(const void* data, size_t size) {
+ if (write_state() != STATE_WRITABLE)
+ return 0;
+
+ int sent = socket_->Send(data, size);
+ if (sent < 0) {
+ error_ = socket_->GetError();
+ } else {
+ sent_total_bytes_ += sent;
+ }
+ return sent;
+}
+
+int TCPConnection::GetError() {
+ return error_;
+}
+
+TCPPort* TCPConnection::tcpport() {
+ return static_cast<TCPPort*>(port_);
+}
+
+void TCPConnection::OnConnect(AsyncTCPSocket* socket) {
+ assert(socket == socket_);
+ LOG(INFO) << "tcp connected to " << socket->GetRemoteAddress().ToString();
+ set_connected(true);
+}
+
+void TCPConnection::OnClose(AsyncTCPSocket* socket, int error) {
+ assert(socket == socket_);
+ LOG(INFO) << "tcp closed with error: " << error;
+ set_connected(false);
+}
+
+void TCPConnection::OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ Connection::OnReadPacket(data, size);
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h
new file mode 100644
index 00000000..f6a9beb7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/tcpport.h
@@ -0,0 +1,116 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __TCPPORT_H__
+#define __TCPPORT_H__
+
+#include <list>
+#include "talk/base/asynctcpsocket.h"
+#include "talk/p2p/base/port.h"
+
+namespace cricket {
+
+class TCPConnection;
+
+extern const std::string LOCAL_PORT_TYPE; // type of TCP ports
+
+// Communicates using a local TCP port.
+//
+// This class is designed to allow subclasses to take advantage of the
+// connection management provided by this class. A subclass should take of all
+// packet sending and preparation, but when a packet is received, it should
+// call this TCPPort::OnReadPacket (3 arg) to dispatch to a connection.
+class TCPPort : public Port {
+public:
+ TCPPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& address);
+ virtual ~TCPPort();
+
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual void PrepareAddress();
+
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ // Handles sending using the local TCP socket.
+ virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload);
+
+ // Creates TCPConnection for incoming sockets
+ void OnAcceptEvent(AsyncSocket* socket);
+
+ AsyncSocket* socket() { return socket_; }
+
+private:
+ bool incoming_only_;
+ AsyncSocket* socket_;
+ int error_;
+
+ struct Incoming {
+ SocketAddress addr;
+ AsyncTCPSocket * socket;
+ };
+ std::list<Incoming> incoming_;
+
+ AsyncTCPSocket * GetIncoming(const SocketAddress& addr, bool remove = false);
+
+ // Receives packet signal from the local TCP Socket.
+ void OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+ friend class TCPConnection;
+};
+
+class TCPConnection : public Connection {
+public:
+ // Connection is outgoing unless socket is specified
+ TCPConnection(TCPPort* port, const Candidate& candidate, AsyncTCPSocket* socket = 0);
+ virtual ~TCPConnection();
+
+ virtual int Send(const void* data, size_t size);
+ virtual int GetError();
+
+ AsyncTCPSocket * socket() { return socket_; }
+
+private:
+ TCPPort* tcpport();
+ AsyncTCPSocket* socket_;
+ bool connected_;
+ int error_;
+
+ void OnConnect(AsyncTCPSocket* socket);
+ void OnClose(AsyncTCPSocket* socket, int error);
+ void OnReadPacket(const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+
+ friend class TCPPort;
+};
+
+} // namespace cricket
+
+#endif // __TCPPORT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc
new file mode 100644
index 00000000..fabbb25b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.cc
@@ -0,0 +1,117 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/logging.h"
+#include "talk/p2p/base/udpport.h"
+#include <iostream>
+#include <cassert>
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+namespace std {
+ using ::strerror;
+}
+#endif
+
+#ifdef POSIX
+extern "C" {
+#include <errno.h>
+}
+#endif // POSIX
+
+namespace cricket {
+
+const std::string LOCAL_PORT_TYPE("local");
+
+UDPPort::UDPPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& address)
+ : Port(thread, LOCAL_PORT_TYPE, factory, network), error_(0) {
+ socket_ = CreatePacketSocket(PROTO_UDP);
+ socket_->SignalReadPacket.connect(this, &UDPPort::OnReadPacketSlot);
+ if (socket_->Bind(address) < 0)
+ PLOG(LERROR, socket_->GetError()) << "bind";
+}
+
+UDPPort::UDPPort(Thread* thread, const std::string &type,
+ SocketFactory* factory, Network* network)
+ : Port(thread, type, factory, network), socket_(0), error_(0) {
+}
+
+UDPPort::~UDPPort() {
+ delete socket_;
+}
+
+void UDPPort::PrepareAddress() {
+ assert(socket_);
+ add_address(socket_->GetLocalAddress(), "udp");
+}
+
+Connection* UDPPort::CreateConnection(const Candidate& address, CandidateOrigin origin) {
+ if (address.protocol() != "udp")
+ return 0;
+
+ Connection * conn = new ProxyConnection(this, 0, address);
+ AddConnection(conn);
+ return conn;
+}
+
+int UDPPort::SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload) {
+ assert(socket_);
+ int sent = socket_->SendTo(data, size, addr);
+ if (sent < 0)
+ error_ = socket_->GetError();
+ return sent;
+}
+
+int UDPPort::SetOption(Socket::Option opt, int value) {
+ return socket_->SetOption(opt, value);
+}
+
+int UDPPort::GetError() {
+ assert(socket_);
+ return error_;
+}
+
+void UDPPort::OnReadPacketSlot(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket) {
+ assert(socket == socket_);
+ OnReadPacket(data, size, remote_addr);
+}
+
+void UDPPort::OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr) {
+ if (Connection* conn = GetConnection(remote_addr)) {
+ conn->OnReadPacket(data, size);
+ } else {
+ Port::OnReadPacket(data, size, remote_addr);
+ }
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h
new file mode 100644
index 00000000..4bcd113e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/base/udpport.h
@@ -0,0 +1,81 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __UDPPORT_H__
+#define __UDPPORT_H__
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/p2p/base/port.h"
+
+namespace cricket {
+
+extern const std::string LOCAL_PORT_TYPE; // type of UDP ports
+
+// Communicates using a local UDP port.
+//
+// This class is designed to allow subclasses to take advantage of the
+// connection management provided by this class. A subclass should take of all
+// packet sending and preparation, but when a packet is received, it should
+// call this UDPPort::OnReadPacket (3 arg) to dispatch to a connection.
+class UDPPort : public Port {
+public:
+ UDPPort(Thread* thread, SocketFactory* factory, Network* network,
+ const SocketAddress& address);
+ virtual ~UDPPort();
+
+ virtual void PrepareAddress();
+ virtual Connection* CreateConnection(const Candidate& address, CandidateOrigin origin);
+
+ virtual int SetOption(Socket::Option opt, int value);
+ virtual int GetError();
+
+protected:
+ UDPPort(Thread* thread, const std::string &type, SocketFactory* factory,
+ Network* network);
+
+ // Handles sending using the local UDP socket.
+ virtual int SendTo(const void* data, size_t size, const SocketAddress& addr, bool payload);
+
+ // Dispatches the given packet to the port or connection as appropriate.
+ void OnReadPacket(
+ const char* data, size_t size, const SocketAddress& remote_addr);
+
+ AsyncPacketSocket* socket() { return socket_; }
+
+private:
+ AsyncPacketSocket* socket_;
+ int error_;
+
+ // Receives packet signal from the local UDP Socket.
+ void OnReadPacketSlot(
+ const char* data, size_t size, const SocketAddress& remote_addr,
+ AsyncPacketSocket* socket);
+};
+
+} // namespace cricket
+
+#endif // __UDPPORT_H__
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am
new file mode 100644
index 00000000..2bdd95ff
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/Makefile.am
@@ -0,0 +1,11 @@
+libcricketp2pclient_la_SOURCES = sessionclient.cc \
+ basicportallocator.cc \
+ socketmonitor.cc
+
+noinst_HEADERS = basicportallocator.h \
+ sessionclient.h \
+ socketmonitor.h
+
+AM_CPPFLAGS = -I$(srcdir)/../../.. -DLINUX -DPOSIX -DINTERNAL_BUILD
+
+noinst_LTLIBRARIES = libcricketp2pclient.la
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc
new file mode 100644
index 00000000..5192595c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.cc
@@ -0,0 +1,667 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/base/host.h"
+#include "talk/base/logging.h"
+#include "talk/p2p/client/basicportallocator.h"
+#include "talk/p2p/base/port.h"
+#include "talk/p2p/base/udpport.h"
+#include "talk/p2p/base/tcpport.h"
+#include "talk/p2p/base/stunport.h"
+#include "talk/p2p/base/relayport.h"
+#include "talk/p2p/base/helpers.h"
+#include <cassert>
+
+namespace {
+
+const uint32 MSG_CONFIG_START = 1;
+const uint32 MSG_CONFIG_READY = 2;
+const uint32 MSG_ALLOCATE = 3;
+const uint32 MSG_ALLOCATION_PHASE = 4;
+const uint32 MSG_SHAKE = 5;
+
+const uint32 ALLOCATE_DELAY = 250;
+const uint32 ALLOCATION_STEP_DELAY = 1 * 1000;
+
+const int PHASE_UDP = 0;
+const int PHASE_RELAY = 1;
+const int PHASE_TCP = 2;
+const int PHASE_SSLTCP = 3;
+const int kNumPhases = 4;
+
+const float PREF_LOCAL_UDP = 1.0f;
+const float PREF_LOCAL_STUN = 0.9f;
+const float PREF_LOCAL_TCP = 0.8f;
+const float PREF_RELAY = 0.5f;
+
+const float RELAY_PRIMARY_PREF_MODIFIER = 0.0f; // modifiers of the above constants
+const float RELAY_BACKUP_PREF_MODIFIER = -0.2f;
+
+
+// Returns the phase in which a given local candidate (or rather, the port that
+// gave rise to that local candidate) would have been created.
+int LocalCandidateToPhase(const cricket::Candidate& candidate) {
+ cricket::ProtocolType proto;
+ bool result = cricket::StringToProto(candidate.protocol().c_str(), proto);
+ if (result) {
+ if (candidate.type() == cricket::LOCAL_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_UDP;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ default: assert(false);
+ }
+ } else if (candidate.type() == cricket::STUN_PORT_TYPE) {
+ return PHASE_UDP;
+ } else if (candidate.type() == cricket::RELAY_PORT_TYPE) {
+ switch (proto) {
+ case cricket::PROTO_UDP: return PHASE_RELAY;
+ case cricket::PROTO_TCP: return PHASE_TCP;
+ case cricket::PROTO_SSLTCP: return PHASE_SSLTCP;
+ default: assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ } else {
+ assert(false);
+ }
+ return PHASE_UDP; // reached only with assert failure
+}
+
+const int SHAKE_MIN_DELAY = 45 * 1000; // 45 seconds
+const int SHAKE_MAX_DELAY = 90 * 1000; // 90 seconds
+
+int ShakeDelay() {
+ int range = SHAKE_MAX_DELAY - SHAKE_MIN_DELAY + 1;
+ return SHAKE_MIN_DELAY + cricket::CreateRandomId() % range;
+}
+
+}
+
+namespace cricket {
+
+// Performs the allocation of ports, in a sequenced (timed) manner, for a given
+// network and IP address.
+class AllocationSequence: public MessageHandler {
+public:
+ AllocationSequence(BasicPortAllocatorSession* session,
+ Network* network,
+ PortConfiguration* config);
+ ~AllocationSequence();
+
+ // Determines whether this sequence is operating on an equivalent network
+ // setup to the one given.
+ bool IsEquivalent(Network* network);
+
+ // Starts and stops the sequence. When started, it will continue allocating
+ // new ports on its own timed schedule.
+ void Start();
+ void Stop();
+
+ // MessageHandler:
+ void OnMessage(Message* msg);
+
+ void EnableProtocol(ProtocolType proto);
+ bool ProtocolEnabled(ProtocolType proto) const;
+
+private:
+ BasicPortAllocatorSession* session_;
+ Network* network_;
+ uint32 ip_;
+ PortConfiguration* config_;
+ bool running_;
+ int step_;
+ int step_of_phase_[kNumPhases];
+
+ typedef std::vector<ProtocolType> ProtocolList;
+ ProtocolList protocols_;
+
+ void CreateUDPPorts();
+ void CreateTCPPorts();
+ void CreateStunPorts();
+ void CreateRelayPorts();
+};
+
+
+// BasicPortAllocator
+
+BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager)
+ : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(NULL), relay_address_(NULL) {
+}
+
+BasicPortAllocator::BasicPortAllocator(NetworkManager* network_manager, SocketAddress* stun_address, SocketAddress *relay_address)
+ : network_manager_(network_manager), best_writable_phase_(-1), stun_address_(stun_address), relay_address_(relay_address) {
+}
+
+BasicPortAllocator::~BasicPortAllocator() {
+}
+
+int BasicPortAllocator::best_writable_phase() const {
+ // If we are configured with an HTTP proxy, the best bet is to use the relay
+ if ((best_writable_phase_ == -1)
+ && ((proxy().type == PROXY_HTTPS) || (proxy().type == PROXY_UNKNOWN))) {
+ return PHASE_RELAY;
+ }
+ return best_writable_phase_;
+}
+
+PortAllocatorSession *BasicPortAllocator::CreateSession(const std::string &name) {
+ return new BasicPortAllocatorSession(this, name, stun_address_, relay_address_);
+}
+
+void BasicPortAllocator::AddWritablePhase(int phase) {
+ if ((best_writable_phase_ == -1) || (phase < best_writable_phase_))
+ best_writable_phase_ = phase;
+}
+
+// BasicPortAllocatorSession
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name)
+ : allocator_(allocator), name_(name), network_thread_(NULL),
+ config_thread_(NULL), allocation_started_(false), running_(false),
+ stun_address_(NULL), relay_address_(NULL) {
+}
+
+BasicPortAllocatorSession::BasicPortAllocatorSession(
+ BasicPortAllocator *allocator,
+ const std::string &name,
+ SocketAddress *stun_address,
+ SocketAddress *relay_address)
+ : allocator_(allocator), name_(name), network_thread_(NULL),
+ config_thread_(NULL), allocation_started_(false), running_(false),
+ stun_address_(stun_address), relay_address_(relay_address) {
+}
+
+BasicPortAllocatorSession::~BasicPortAllocatorSession() {
+ if (config_thread_ != NULL)
+ config_thread_->Clear(this);
+ if (network_thread_ != NULL)
+ network_thread_->Clear(this);
+
+ std::vector<PortData>::iterator it;
+ for (it = ports_.begin(); it != ports_.end(); it++)
+ delete it->port;
+
+ for (uint32 i = 0; i < configs_.size(); ++i)
+ delete configs_[i];
+
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ delete sequences_[i];
+}
+
+void BasicPortAllocatorSession::GetInitialPorts() {
+ network_thread_ = Thread::Current();
+ if (!config_thread_)
+ config_thread_ = network_thread_;
+
+ config_thread_->Post(this, MSG_CONFIG_START);
+
+ if (allocator()->flags() & PORTALLOCATOR_ENABLE_SHAKER)
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+void BasicPortAllocatorSession::StartGetAllPorts() {
+ assert(Thread::Current() == network_thread_);
+ running_ = true;
+ if (allocation_started_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Start();
+ for (size_t i = 0; i < ports_.size(); ++i)
+ ports_[i].port->Start();
+}
+
+void BasicPortAllocatorSession::StopGetAllPorts() {
+ assert(Thread::Current() == network_thread_);
+ running_ = false;
+ network_thread_->Clear(this, MSG_ALLOCATE);
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ sequences_[i]->Stop();
+}
+
+void BasicPortAllocatorSession::OnMessage(Message *message) {
+ switch (message->message_id) {
+ case MSG_CONFIG_START:
+ assert(Thread::Current() == config_thread_);
+ GetPortConfigurations();
+ break;
+
+ case MSG_CONFIG_READY:
+ assert(Thread::Current() == network_thread_);
+ OnConfigReady(static_cast<PortConfiguration*>(message->pdata));
+ break;
+
+ case MSG_ALLOCATE:
+ assert(Thread::Current() == network_thread_);
+ OnAllocate();
+ break;
+
+ case MSG_SHAKE:
+ assert(Thread::Current() == network_thread_);
+ OnShake();
+ break;
+
+ default:
+ assert(false);
+ }
+}
+
+void BasicPortAllocatorSession::GetPortConfigurations() {
+ PortConfiguration* config = NULL;
+ if (stun_address_ != NULL)
+ config = new PortConfiguration(*stun_address_,
+ CreateRandomString(16),
+ CreateRandomString(16),
+ "");
+ PortConfiguration::PortList ports;
+ if (relay_address_ != NULL) {
+ ports.push_back(ProtocolAddress(*relay_address_, PROTO_UDP));
+ config->AddRelay(ports, RELAY_PRIMARY_PREF_MODIFIER);
+ }
+
+ ConfigReady(config);
+}
+
+void BasicPortAllocatorSession::ConfigReady(PortConfiguration* config) {
+ network_thread_->Post(this, MSG_CONFIG_READY, config);
+}
+
+// Adds a configuration to the list.
+void BasicPortAllocatorSession::OnConfigReady(PortConfiguration* config) {
+ if (config)
+ configs_.push_back(config);
+
+ AllocatePorts();
+}
+
+void BasicPortAllocatorSession::AllocatePorts() {
+ assert(Thread::Current() == network_thread_);
+
+ if (allocator_->proxy().type != PROXY_NONE)
+ Port::set_proxy(allocator_->proxy());
+
+ network_thread_->Post(this, MSG_ALLOCATE);
+}
+
+// For each network, see if we have a sequence that covers it already. If not,
+// create a new sequence to create the appropriate ports.
+void BasicPortAllocatorSession::OnAllocate() {
+ std::vector<Network*> networks;
+ allocator_->network_manager()->GetNetworks(networks);
+
+ for (uint32 i = 0; i < networks.size(); ++i) {
+ if (HasEquivalentSequence(networks[i]))
+ continue;
+
+ PortConfiguration* config = NULL;
+ if (configs_.size() > 0)
+ config = configs_.back();
+
+ AllocationSequence* sequence =
+ new AllocationSequence(this, networks[i], config);
+ if (running_)
+ sequence->Start();
+
+ sequences_.push_back(sequence);
+ }
+
+ allocation_started_ = true;
+ if (running_)
+ network_thread_->PostDelayed(ALLOCATE_DELAY, this, MSG_ALLOCATE);
+}
+
+bool BasicPortAllocatorSession::HasEquivalentSequence(Network* network) {
+ for (uint32 i = 0; i < sequences_.size(); ++i)
+ if (sequences_[i]->IsEquivalent(network))
+ return true;
+ return false;
+}
+
+void BasicPortAllocatorSession::AddAllocatedPort(Port* port,
+ AllocationSequence * seq,
+ float pref,
+ bool prepare_address) {
+ if (!port)
+ return;
+
+ port->set_name(name_);
+ port->set_preference(pref);
+ port->set_generation(generation());
+ PortData data;
+ data.port = port;
+ data.sequence = seq;
+ data.ready = false;
+ ports_.push_back(data);
+ port->SignalAddressReady.connect(this, &BasicPortAllocatorSession::OnAddressReady);
+ port->SignalConnectionCreated.connect(this, &BasicPortAllocatorSession::OnConnectionCreated);
+ port->SignalDestroyed.connect(this, &BasicPortAllocatorSession::OnPortDestroyed);
+ if (prepare_address)
+ port->PrepareAddress();
+ if (running_)
+ port->Start();
+}
+
+void BasicPortAllocatorSession::OnAddressReady(Port *port) {
+ assert(Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator it = std::find(ports_.begin(), ports_.end(), port);
+ assert(it != ports_.end());
+ assert(!it->ready);
+ it->ready = true;
+ SignalPortReady(this, port);
+
+ // Only accumulate the candidates whose protocol has been enabled
+ std::vector<Candidate> candidates;
+ const std::vector<Candidate>& potentials = port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (it->sequence->ProtocolEnabled(pvalue)) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto) {
+ std::vector<Candidate> candidates;
+ for (std::vector<PortData>::iterator it = ports_.begin(); it != ports_.end(); ++it) {
+ if (!it->ready || (it->sequence != seq))
+ continue;
+
+ const std::vector<Candidate>& potentials = it->port->candidates();
+ for (size_t i=0; i<potentials.size(); ++i) {
+ ProtocolType pvalue;
+ if (!StringToProto(potentials[i].protocol().c_str(), pvalue))
+ continue;
+ if (pvalue == proto) {
+ candidates.push_back(potentials[i]);
+ }
+ }
+ }
+ if (!candidates.empty()) {
+ SignalCandidatesReady(this, candidates);
+ }
+}
+
+void BasicPortAllocatorSession::OnPortDestroyed(Port* port) {
+ assert(Thread::Current() == network_thread_);
+ std::vector<PortData>::iterator iter =
+ find(ports_.begin(), ports_.end(), port);
+ assert(iter != ports_.end());
+ ports_.erase(iter);
+
+ LOG(INFO) << "Removed port from allocator: "
+ << static_cast<int>(ports_.size()) << " remaining";
+}
+
+void BasicPortAllocatorSession::OnConnectionCreated(Port* port, Connection* conn) {
+ conn->SignalStateChange.connect(this, &BasicPortAllocatorSession::OnConnectionStateChange);
+}
+
+void BasicPortAllocatorSession::OnConnectionStateChange(Connection* conn) {
+ if (conn->write_state() == Connection::STATE_WRITABLE)
+ allocator_->AddWritablePhase(LocalCandidateToPhase(conn->local_candidate()));
+}
+
+void BasicPortAllocatorSession::OnShake() {
+ LOG(INFO) << ">>>>> SHAKE <<<<< >>>>> SHAKE <<<<< >>>>> SHAKE <<<<<";
+
+ std::vector<Port*> ports;
+ std::vector<Connection*> connections;
+
+ for (size_t i = 0; i < ports_.size(); ++i) {
+ if (ports_[i].ready)
+ ports.push_back(ports_[i].port);
+ }
+
+ for (size_t i = 0; i < ports.size(); ++i) {
+ Port::AddressMap::const_iterator iter;
+ for (iter = ports[i]->connections().begin();
+ iter != ports[i]->connections().end();
+ ++iter) {
+ connections.push_back(iter->second);
+ }
+ }
+
+ LOG(INFO) << ">>>>> Destroying " << (int)ports.size() << " ports and "
+ << (int)connections.size() << " connections";
+
+ for (size_t i = 0; i < connections.size(); ++i)
+ connections[i]->Destroy();
+
+ if (running_ || (ports.size() > 0) || (connections.size() > 0))
+ network_thread_->PostDelayed(ShakeDelay(), this, MSG_SHAKE);
+}
+
+// AllocationSequence
+
+AllocationSequence::AllocationSequence(BasicPortAllocatorSession* session,
+ Network* network,
+ PortConfiguration* config)
+ : session_(session), network_(network), ip_(network->ip()), config_(config),
+ running_(false), step_(0) {
+
+ // All of the phases up until the best-writable phase so far run in step 0.
+ // The other phases follow sequentially in the steps after that. If there is
+ // no best-writable so far, then only phase 0 occurs in step 0.
+ int last_phase_in_step_zero =
+ _max(0, session->allocator()->best_writable_phase());
+ for (int phase = 0; phase < kNumPhases; ++phase)
+ step_of_phase_[phase] = _max(0, phase - last_phase_in_step_zero);
+
+ // Immediately perform phase 0.
+ OnMessage(NULL);
+}
+
+AllocationSequence::~AllocationSequence() {
+ session_->network_thread()->Clear(this);
+}
+
+bool AllocationSequence::IsEquivalent(Network* network) {
+ return (network == network_) && (ip_ == network->ip());
+}
+
+void AllocationSequence::Start() {
+ running_ = true;
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::Stop() {
+ running_ = false;
+ session_->network_thread()->Clear(this, MSG_ALLOCATION_PHASE);
+}
+
+void AllocationSequence::OnMessage(Message* msg) {
+ assert(Thread::Current() == session_->network_thread());
+ if (msg)
+ assert(msg->message_id == MSG_ALLOCATION_PHASE);
+
+ // Perform all of the phases in the current step.
+ for (int phase = 0; phase < kNumPhases; phase++) {
+ if (step_of_phase_[phase] != step_)
+ continue;
+
+ switch (phase) {
+ case PHASE_UDP:
+ LOG(INFO) << "Phase=UDP Step=" << step_;
+ CreateUDPPorts();
+ CreateStunPorts();
+ EnableProtocol(PROTO_UDP);
+ break;
+
+ case PHASE_RELAY:
+ LOG(INFO) << "Phase=RELAY Step=" << step_;
+ CreateRelayPorts();
+ break;
+
+ case PHASE_TCP:
+ LOG(INFO) << "Phase=TCP Step=" << step_;
+ CreateTCPPorts();
+ EnableProtocol(PROTO_TCP);
+ break;
+
+ case PHASE_SSLTCP:
+ LOG(INFO) << "Phase=SSLTCP Step=" << step_;
+ EnableProtocol(PROTO_SSLTCP);
+ break;
+
+ default:
+ // Nothing else we can do.
+ return;
+ }
+ }
+
+ // TODO: use different delays for each stage
+ step_ += 1;
+ if (running_) {
+ session_->network_thread()->PostDelayed(ALLOCATION_STEP_DELAY,
+ this,
+ MSG_ALLOCATION_PHASE);
+ }
+}
+
+void AllocationSequence::EnableProtocol(ProtocolType proto) {
+ if (!ProtocolEnabled(proto)) {
+ protocols_.push_back(proto);
+ session_->OnProtocolEnabled(this, proto);
+ }
+}
+
+bool AllocationSequence::ProtocolEnabled(ProtocolType proto) const {
+ for (ProtocolList::const_iterator it = protocols_.begin(); it != protocols_.end(); ++it) {
+ if (*it == proto)
+ return true;
+ }
+ return false;
+}
+
+void AllocationSequence::CreateUDPPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_UDP)
+ return;
+
+ Port* port = new UDPPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_UDP);
+}
+
+void AllocationSequence::CreateTCPPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_TCP)
+ return;
+
+ Port* port = new TCPPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0));
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_TCP);
+}
+
+void AllocationSequence::CreateStunPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_STUN)
+ return;
+
+ if (!config_ || config_->stun_address.IsAny())
+ return;
+
+ Port* port = new StunPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0), config_->stun_address);
+ session_->AddAllocatedPort(port, this, PREF_LOCAL_STUN);
+}
+
+void AllocationSequence::CreateRelayPorts() {
+ if (session_->allocator()->flags() & PORTALLOCATOR_DISABLE_RELAY)
+ return;
+
+ if (!config_)
+ return;
+
+ PortConfiguration::RelayList::const_iterator relay;
+ for (relay = config_->relays.begin();
+ relay != config_->relays.end();
+ ++relay) {
+
+ RelayPort *port = new RelayPort(session_->network_thread(), NULL, network_,
+ SocketAddress(ip_, 0),
+ config_->username, config_->password,
+ config_->magic_cookie);
+ // Note: We must add the allocated port before we add addresses because
+ // the latter will create candidates that need name and preference
+ // settings. However, we also can't prepare the address (normally
+ // done by AddAllocatedPort) until we have these addresses. So we
+ // wait to do that until below.
+ session_->AddAllocatedPort(port, this, PREF_RELAY + relay->pref_modifier, false);
+
+ // Add the addresses of this protocol.
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay->ports.begin();
+ relay_port != relay->ports.end();
+ ++relay_port) {
+ port->AddServerAddress(*relay_port);
+ port->AddExternalAddress(*relay_port);
+ }
+
+ // Start fetching an address for this port.
+ port->PrepareAddress();
+ }
+}
+
+// PortConfiguration
+
+PortConfiguration::PortConfiguration(const SocketAddress& sa,
+ const std::string& un,
+ const std::string& pw,
+ const std::string& mc)
+ : stun_address(sa), username(un), password(pw), magic_cookie(mc) {
+}
+
+void PortConfiguration::AddRelay(const PortList& ports, float pref_modifier) {
+ RelayServer relay;
+ relay.ports = ports;
+ relay.pref_modifier = pref_modifier;
+ relays.push_back(relay);
+}
+
+bool PortConfiguration::SupportsProtocol(
+ const PortConfiguration::RelayServer& relay, ProtocolType type) {
+ PortConfiguration::PortList::const_iterator relay_port;
+ for (relay_port = relay.ports.begin();
+ relay_port != relay.ports.end();
+ ++relay_port) {
+ if (relay_port->proto == type)
+ return true;
+ }
+ return false;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h
new file mode 100644
index 00000000..0f7b96b4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/basicportallocator.h
@@ -0,0 +1,172 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BASICPORTALLOCATOR_H_
+#define _BASICPORTALLOCATOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/network.h"
+#include "talk/p2p/base/portallocator.h"
+#include <string>
+#include <vector>
+
+namespace cricket {
+
+class BasicPortAllocator: public PortAllocator {
+public:
+ BasicPortAllocator(NetworkManager* network_manager);
+ BasicPortAllocator(NetworkManager* network_manager, SocketAddress *stun_server, SocketAddress *relay_server);
+ virtual ~BasicPortAllocator();
+
+ NetworkManager* network_manager() { return network_manager_; }
+
+ // Returns the best (highest preference) phase that has produced a port that
+ // produced a writable connection. If no writable connections have been
+ // produced, this returns -1.
+ int best_writable_phase() const;
+
+ virtual PortAllocatorSession *CreateSession(const std::string &name);
+
+ // Called whenever a connection becomes writable with the argument being the
+ // phase that the corresponding port was created in.
+ void AddWritablePhase(int phase);
+
+private:
+ NetworkManager* network_manager_;
+ SocketAddress* stun_address_;
+ SocketAddress* relay_address_;
+ int best_writable_phase_;
+};
+
+struct PortConfiguration;
+class AllocationSequence;
+
+class BasicPortAllocatorSession: public PortAllocatorSession, public MessageHandler {
+public:
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name);
+ BasicPortAllocatorSession(BasicPortAllocator *allocator,
+ const std::string &name,
+ SocketAddress *stun_address,
+ SocketAddress *relay_address);
+ ~BasicPortAllocatorSession();
+
+ BasicPortAllocator* allocator() { return allocator_; }
+ const std::string& name() const { return name_; }
+ Thread* network_thread() { return network_thread_; }
+
+ Thread* config_thread() { return config_thread_; }
+ void set_config_thread(Thread* thread) { config_thread_ = thread; }
+
+ virtual void GetInitialPorts();
+ virtual void StartGetAllPorts();
+ virtual void StopGetAllPorts();
+ virtual bool IsGettingAllPorts() { return running_; }
+
+protected:
+ // Starts the process of getting the port configurations.
+ virtual void GetPortConfigurations();
+
+ // Adds a port configuration that is now ready. Once we have one for each
+ // network (or a timeout occurs), we will start allocating ports.
+ void ConfigReady(PortConfiguration* config);
+
+ // MessageHandler. Can be overriden if message IDs do not conflict.
+ virtual void OnMessage(Message *message);
+
+private:
+ void OnConfigReady(PortConfiguration* config);
+ void OnConfigTimeout();
+ void AllocatePorts();
+ void OnAllocate();
+ bool HasEquivalentSequence(Network* network);
+ void AddAllocatedPort(Port* port, AllocationSequence * seq, float pref, bool prepare_address = true);
+ void OnAddressReady(Port *port);
+ void OnProtocolEnabled(AllocationSequence * seq, ProtocolType proto);
+ void OnPortDestroyed(Port* port);
+ void OnConnectionCreated(Port* port, Connection* conn);
+ void OnConnectionStateChange(Connection* conn);
+ void OnShake();
+
+ BasicPortAllocator *allocator_;
+ std::string name_;
+ Thread* network_thread_;
+ Thread* config_thread_;
+ bool configuration_done_;
+ bool allocation_started_;
+ bool running_; // set when StartGetAllPorts is called
+ std::vector<PortConfiguration*> configs_;
+ std::vector<AllocationSequence*> sequences_;
+ SocketAddress *stun_address_;
+ SocketAddress *relay_address_;
+
+ struct PortData {
+ Port * port;
+ AllocationSequence * sequence;
+ bool ready;
+
+ bool operator==(Port * rhs) const { return (port == rhs); }
+ };
+ std::vector<PortData> ports_;
+
+ friend class AllocationSequence;
+};
+
+// Records configuration information useful in creating ports.
+struct PortConfiguration: public MessageData {
+ SocketAddress stun_address;
+ std::string username;
+ std::string password;
+ std::string magic_cookie;
+
+ typedef std::vector<ProtocolAddress> PortList;
+ struct RelayServer {
+ PortList ports;
+ float pref_modifier; // added to the protocol modifier to get the
+ // preference for this particular server
+ };
+
+ typedef std::vector<RelayServer> RelayList;
+ RelayList relays;
+
+ PortConfiguration(const SocketAddress& stun_address,
+ const std::string& username,
+ const std::string& password,
+ const std::string& magic_cookie);
+
+ // Adds another relay server, with the given ports and modifier, to the list.
+ void AddRelay(const PortList& ports, float pref_modifier);
+
+ // Determines whether the given relay server supports the given protocol.
+ static bool SupportsProtocol(const PortConfiguration::RelayServer& relay,
+ ProtocolType type);
+};
+
+} // namespace cricket
+
+#endif // _BASICPORTALLOCATOR_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc
new file mode 100644
index 00000000..09b38a52
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.cc
@@ -0,0 +1,545 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if defined(_MSC_VER) && _MSC_VER < 1300
+#pragma warning(disable:4786)
+#endif
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/base/logging.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/xmlprinter.h"
+#include <iostream>
+#undef SetPort
+
+namespace {
+
+// We only allow usernames to be this many characters or fewer.
+const size_t kMaxUsernameSize = 16;
+
+}
+
+namespace cricket {
+
+#if 0
+>>>>>>
+<iq from="..." to="..." type="set" id="27">
+ <session xmlns="http://www.google.com/session" type="initiate" id="Dr45JU8A34DF" initiator="...">
+ <description xmlns="http://www.whoever.com/whatever">
+ ...
+ </description>
+ </session>
+</iq>
+
+<<<<<<
+<iq from="..." to="..." type="result" id="27"/>
+
+>>>>>>
+<iq from="..." to="..." type="set" id="28">
+ <session xmlns="http://www.google.com/session" type="candidates" id="Dr45JU8A34DF" initiator="...">
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ <candidate name="rtp" address="X.X.X.X" port="NNN" username="asdf" password="asdf" preference="1.0" type="udp" network="bleh"/>
+ </session>
+</iq>>
+
+<<<<<<
+<iq from="..." to="..." type="result" id="28"/>
+
+#endif
+
+const std::string NS_GOOGLESESSION("http://www.google.com/session");
+const buzz::QName QN_GOOGLESESSION_SESSION(true, NS_GOOGLESESSION, "session");
+const buzz::QName QN_GOOGLESESSION_CANDIDATE(true, NS_GOOGLESESSION, "candidate");
+const buzz::QName QN_GOOGLESESSION_TARGET(true, NS_GOOGLESESSION, "target");
+const buzz::QName QN_GOOGLESESSION_COOKIE(true, NS_GOOGLESESSION, "cookie");
+const buzz::QName QN_GOOGLESESSION_REGARDING(true, NS_GOOGLESESSION, "regarding");
+
+const buzz::QName QN_TYPE(true, buzz::STR_EMPTY, "type");
+const buzz::QName QN_ID(true, buzz::STR_EMPTY, "id");
+const buzz::QName QN_INITIATOR(true, buzz::STR_EMPTY, "initiator");
+const buzz::QName QN_NAME(true, buzz::STR_EMPTY, "name");
+const buzz::QName QN_PORT(true, buzz::STR_EMPTY, "port");
+const buzz::QName QN_NETWORK(true, buzz::STR_EMPTY, "network");
+const buzz::QName QN_GENERATION(true, buzz::STR_EMPTY, "generation");
+const buzz::QName QN_ADDRESS(true, buzz::STR_EMPTY, "address");
+const buzz::QName QN_USERNAME(true, buzz::STR_EMPTY, "username");
+const buzz::QName QN_PASSWORD(true, buzz::STR_EMPTY, "password");
+const buzz::QName QN_PREFERENCE(true, buzz::STR_EMPTY, "preference");
+const buzz::QName QN_PROTOCOL(true, buzz::STR_EMPTY, "protocol");
+const buzz::QName QN_KEY(true, buzz::STR_EMPTY, "key");
+
+class XmlCookie: public SessionMessage::Cookie {
+public:
+ XmlCookie(const buzz::XmlElement* elem)
+ : elem_(new buzz::XmlElement(*elem)) {
+ }
+
+ virtual ~XmlCookie() {
+ delete elem_;
+ }
+
+ const buzz::XmlElement* elem() const { return elem_; }
+
+ virtual Cookie* Copy() {
+ return new XmlCookie(elem_);
+ }
+
+private:
+ buzz::XmlElement* elem_;
+};
+
+SessionClient::SessionClient(SessionManager *session_manager) {
+ session_manager_ = session_manager;
+ session_manager_->SignalSessionCreate.connect(this, &SessionClient::OnSessionCreateSlot);
+ session_manager_->SignalSessionDestroy.connect(this, &SessionClient::OnSessionDestroySlot);
+}
+
+SessionClient::~SessionClient() {
+}
+
+void SessionClient::OnSessionCreateSlot(Session *session, bool received_initiate) {
+ // Does this session belong to this session client?
+ if (session->name() == GetSessionDescriptionName()) {
+ session->SignalOutgoingMessage.connect(this, &SessionClient::OnOutgoingMessage);
+ OnSessionCreate(session, received_initiate);
+ }
+}
+
+void SessionClient::OnSessionDestroySlot(Session *session) {
+ if (session->name() == GetSessionDescriptionName()) {
+ session->SignalOutgoingMessage.disconnect(this);
+ OnSessionDestroy(session);
+ }
+}
+
+bool SessionClient::IsClientStanza(const buzz::XmlElement *stanza) {
+ // Is it a IQ set stanza?
+ if (stanza->Name() != buzz::QN_IQ)
+ return false;
+ if (stanza->Attr(buzz::QN_TYPE) != buzz::STR_SET)
+ return false;
+
+ // Make sure it has the right child element
+ const buzz::XmlElement* element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ if (element == NULL)
+ return false;
+
+ // Is it one of the allowed types?
+ std::string type;
+ if (element->HasAttr(QN_TYPE)) {
+ type = element->Attr(QN_TYPE);
+ if (type != "initiate" && type != "accept" && type != "modify" &&
+ type != "candidates" && type != "reject" && type != "redirect" &&
+ type != "terminate") {
+ return false;
+ }
+ }
+
+ // Does this client own the session description namespace?
+ buzz::QName qn_session_desc(GetSessionDescriptionName(), "description");
+ const buzz::XmlElement* description = element->FirstNamed(qn_session_desc);
+ if (type == "initiate" || type == "accept" || type == "modify") {
+ if (description == NULL)
+ return false;
+ } else {
+ if (description != NULL)
+ return false;
+ }
+
+ // It's good
+ return true;
+}
+
+void SessionClient::OnIncomingStanza(const buzz::XmlElement *stanza) {
+ SessionMessage message;
+ if (!ParseIncomingMessage(stanza, message))
+ return;
+
+ session_manager_->OnIncomingMessage(message);
+}
+
+void SessionClient::OnFailedSend(const buzz::XmlElement *original_stanza,
+ const buzz::XmlElement *failure_stanza) {
+ SessionMessage message;
+ if (!ParseIncomingMessage(original_stanza, message))
+ return;
+
+ // Note the from/to represents the *original* stanza and not the from/to
+ // on any return path
+ session_manager_->OnIncomingError(message);
+}
+
+bool SessionClient::ParseIncomingMessage(const buzz::XmlElement *stanza,
+ SessionMessage& message) {
+ // Parse stanza into SessionMessage
+ const buzz::XmlElement* element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ std::string type = element->Attr(QN_TYPE);
+ if (type == "initiate" || type == "accept" || type == "modify") {
+ ParseInitiateAcceptModify(stanza, message);
+ } else if (type == "candidates") {
+ ParseCandidates(stanza, message);
+ } else if (type == "reject" || type == "terminate") {
+ ParseRejectTerminate(stanza, message);
+ } else if (type == "redirect") {
+ ParseRedirect(stanza, message);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void SessionClient::ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message) {
+ if (stanza->HasAttr(buzz::QN_FROM))
+ message.set_from(stanza->Attr(buzz::QN_FROM));
+ if (stanza->HasAttr(buzz::QN_TO))
+ message.set_to(stanza->Attr(buzz::QN_TO));
+
+ const buzz::XmlElement *element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ if (element->HasAttr(QN_ID))
+ message.session_id().set_id_str(element->Attr(QN_ID));
+
+ if (element->HasAttr(QN_INITIATOR))
+ message.session_id().set_initiator(element->Attr(QN_INITIATOR));
+
+ std::string type = element->Attr(QN_TYPE);
+ if (type == "initiate") {
+ message.set_type(SessionMessage::TYPE_INITIATE);
+ } else if (type == "accept") {
+ message.set_type(SessionMessage::TYPE_ACCEPT);
+ } else if (type == "modify") {
+ message.set_type(SessionMessage::TYPE_MODIFY);
+ } else if (type == "candidates") {
+ message.set_type(SessionMessage::TYPE_CANDIDATES);
+ } else if (type == "reject") {
+ message.set_type(SessionMessage::TYPE_REJECT);
+ } else if (type == "redirect") {
+ message.set_type(SessionMessage::TYPE_REDIRECT);
+ } else if (type == "terminate") {
+ message.set_type(SessionMessage::TYPE_TERMINATE);
+ } else {
+ assert(false);
+ }
+}
+
+void SessionClient::ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+
+ // Parse session description
+ const buzz::XmlElement *session
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ buzz::QName qn_session_desc(GetSessionDescriptionName(), "description");
+ const buzz::XmlElement* desc_elem = session->FirstNamed(qn_session_desc);
+ const SessionDescription *description = NULL;
+ if (desc_elem)
+ description = CreateSessionDescription(desc_elem);
+ message.set_name(GetSessionDescriptionName());
+ message.set_description(description);
+}
+
+void SessionClient::ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+
+ // Parse candidates and session description
+ std::vector<Candidate> candidates;
+ const buzz::XmlElement *element
+ = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+ const buzz::XmlElement *child = element->FirstElement();
+ while (child != NULL) {
+ if (child->Name() == QN_GOOGLESESSION_CANDIDATE) {
+ Candidate candidate;
+ if (ParseCandidate(child, &candidate))
+ candidates.push_back(candidate);
+ }
+ child = child->NextElement();
+ }
+ message.set_name(GetSessionDescriptionName());
+ message.set_candidates(candidates);
+}
+
+void SessionClient::ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Reject and terminate are very simple
+ ParseHeader(stanza, message);
+}
+
+bool SessionClient::ParseCandidate(const buzz::XmlElement *child,
+ Candidate* candidate) {
+ // Check for all of the required attributes.
+ if (!child->HasAttr(QN_NAME) ||
+ !child->HasAttr(QN_ADDRESS) ||
+ !child->HasAttr(QN_PORT) ||
+ !child->HasAttr(QN_USERNAME) ||
+ !child->HasAttr(QN_PREFERENCE) ||
+ !child->HasAttr(QN_PROTOCOL) ||
+ !child->HasAttr(QN_GENERATION)) {
+ LOG(LERROR) << "Candidate missing required attribute";
+ return false;
+ }
+
+ SocketAddress address;
+ address.SetIP(child->Attr(QN_ADDRESS));
+ std::istringstream ist(child->Attr(QN_PORT));
+ int port;
+ ist >> port;
+ address.SetPort(port);
+
+ if (address.IsAny()) {
+ LOG(LERROR) << "Candidate has address 0";
+ return false;
+ }
+
+ // Always disallow addresses that refer to the local host.
+ if (address.IsLocalIP()) {
+ LOG(LERROR) << "Candidate has local IP address";
+ return false;
+ }
+
+ // Disallow all ports below 1024, except for 80 and 443 on public addresses.
+ if (port < 1024) {
+ if ((port != 80) && (port != 443)) {
+ LOG(LERROR) << "Candidate has port below 1024, not 80 or 443";
+ return false;
+ }
+ if (address.IsPrivateIP()) {
+ LOG(LERROR) << "Candidate has port of 80 or 443 with private IP address";
+ return false;
+ }
+ }
+
+ candidate->set_name(child->Attr(QN_NAME));
+ candidate->set_address(address);
+ candidate->set_username(child->Attr(QN_USERNAME));
+ candidate->set_preference_str(child->Attr(QN_PREFERENCE));
+ candidate->set_protocol(child->Attr(QN_PROTOCOL));
+ candidate->set_generation_str(child->Attr(QN_GENERATION));
+
+ // Check that the username is not too long and does not use any bad chars.
+ if (candidate->username().size() > kMaxUsernameSize) {
+ LOG(LERROR) << "Candidate username is too long";
+ return false;
+ }
+ if (!IsBase64Encoded(candidate->username())) {
+ LOG(LERROR) << "Candidate username has non-base64 encoded characters";
+ return false;
+ }
+
+ // Look for the non-required attributes.
+ if (child->HasAttr(QN_PASSWORD))
+ candidate->set_password(child->Attr(QN_PASSWORD));
+ if (child->HasAttr(QN_TYPE))
+ candidate->set_type(child->Attr(QN_TYPE));
+ if (child->HasAttr(QN_NETWORK))
+ candidate->set_network_name(child->Attr(QN_NETWORK));
+
+ return true;
+}
+
+void SessionClient::ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message) {
+ // Pull the standard header pieces out
+ ParseHeader(stanza, message);
+ const buzz::XmlElement *session = stanza->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Parse the target and cookie.
+
+ const buzz::XmlElement* target = session->FirstNamed(QN_GOOGLESESSION_TARGET);
+ if (target)
+ message.set_redirect_target(target->Attr(QN_NAME));
+
+ const buzz::XmlElement* cookie = session->FirstNamed(QN_GOOGLESESSION_COOKIE);
+ if (cookie)
+ message.set_redirect_cookie(new XmlCookie(cookie));
+}
+
+void SessionClient::OnOutgoingMessage(Session *session, const SessionMessage &message) {
+ // Translate the message into an XMPP stanza
+
+ buzz::XmlElement *result = NULL;
+ switch (message.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ case SessionMessage::TYPE_ACCEPT:
+ case SessionMessage::TYPE_MODIFY:
+ result = TranslateInitiateAcceptModify(message);
+ break;
+
+ case SessionMessage::TYPE_CANDIDATES:
+ result = TranslateCandidates(message);
+ break;
+
+ case SessionMessage::TYPE_REJECT:
+ case SessionMessage::TYPE_TERMINATE:
+ result = TranslateRejectTerminate(message);
+ break;
+
+ case SessionMessage::TYPE_REDIRECT:
+ result = TranslateRedirect(message);
+ break;
+ }
+
+ // Send the stanza. Note that SessionClient is passing on ownership
+ // of result.
+ if (result != NULL) {
+ SignalSendStanza(this, result);
+ }
+}
+
+buzz::XmlElement *SessionClient::TranslateHeader(const SessionMessage &message) {
+ buzz::XmlElement *result = new buzz::XmlElement(buzz::QN_IQ);
+ result->AddAttr(buzz::QN_TO, message.to());
+ result->AddAttr(buzz::QN_TYPE, buzz::STR_SET);
+ buzz::XmlElement *session = new buzz::XmlElement(QN_GOOGLESESSION_SESSION, true);
+ result->AddElement(session);
+ switch (message.type()) {
+ case SessionMessage::TYPE_INITIATE:
+ session->AddAttr(QN_TYPE, "initiate");
+ break;
+ case SessionMessage::TYPE_ACCEPT:
+ session->AddAttr(QN_TYPE, "accept");
+ break;
+ case SessionMessage::TYPE_MODIFY:
+ session->AddAttr(QN_TYPE, "modify");
+ break;
+ case SessionMessage::TYPE_CANDIDATES:
+ session->AddAttr(QN_TYPE, "candidates");
+ break;
+ case SessionMessage::TYPE_REJECT:
+ session->AddAttr(QN_TYPE, "reject");
+ break;
+ case SessionMessage::TYPE_REDIRECT:
+ session->AddAttr(QN_TYPE, "redirect");
+ break;
+ case SessionMessage::TYPE_TERMINATE:
+ session->AddAttr(QN_TYPE, "terminate");
+ break;
+ }
+ session->AddAttr(QN_ID, message.session_id().id_str());
+ session->AddAttr(QN_INITIATOR, message.session_id().initiator());
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateCandidate(const Candidate &candidate) {
+ buzz::XmlElement *result = new buzz::XmlElement(QN_GOOGLESESSION_CANDIDATE);
+ result->AddAttr(QN_NAME, candidate.name());
+ result->AddAttr(QN_ADDRESS, candidate.address().IPAsString());
+ result->AddAttr(QN_PORT, candidate.address().PortAsString());
+ result->AddAttr(QN_USERNAME, candidate.username());
+ result->AddAttr(QN_PASSWORD, candidate.password());
+ result->AddAttr(QN_PREFERENCE, candidate.preference_str());
+ result->AddAttr(QN_PROTOCOL, candidate.protocol());
+ result->AddAttr(QN_TYPE, candidate.type());
+ result->AddAttr(QN_NETWORK, candidate.network_name());
+ result->AddAttr(QN_GENERATION, candidate.generation_str());
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateInitiateAcceptModify(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Candidates
+ assert(message.candidates().size() == 0);
+
+ // Session Description
+ buzz::XmlElement* description = TranslateSessionDescription(message.description());
+ assert(description->Name().LocalPart() == "description");
+ assert(description->Name().Namespace() == GetSessionDescriptionName());
+ session->AddElement(description);
+
+ if (message.redirect_cookie() != NULL) {
+ const buzz::XmlElement* cookie =
+ reinterpret_cast<XmlCookie*>(message.redirect_cookie())->elem();
+ for (const buzz::XmlElement* elem = cookie->FirstElement(); elem; elem = elem->NextElement())
+ session->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateCandidates(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ // Candidates
+ std::vector<Candidate>::const_iterator it;
+ for (it = message.candidates().begin(); it != message.candidates().end(); it++)
+ session->AddElement(TranslateCandidate(*it));
+
+ return result;
+}
+
+buzz::XmlElement *SessionClient::TranslateRejectTerminate(const SessionMessage &message) {
+ // These messages are simple, and only have a header
+ return TranslateHeader(message);
+}
+
+buzz::XmlElement *SessionClient::TranslateRedirect(const SessionMessage &message) {
+ // Header info common to all message types
+ buzz::XmlElement *result = TranslateHeader(message);
+ buzz::XmlElement *session = result->FirstNamed(QN_GOOGLESESSION_SESSION);
+
+ assert(message.candidates().size() == 0);
+ assert(message.description() == NULL);
+
+ assert(message.redirect_target().size() > 0);
+ buzz::XmlElement* target = new buzz::XmlElement(QN_GOOGLESESSION_TARGET);
+ target->AddAttr(QN_NAME, message.redirect_target());
+ session->AddElement(target);
+
+ buzz::XmlElement* cookie = new buzz::XmlElement(QN_GOOGLESESSION_COOKIE);
+ session->AddElement(cookie);
+
+ // If the message does not have a redirect cookie, then this is a redirect
+ // initiated by us. We will automatically add a regarding cookie.
+ if (message.redirect_cookie() == NULL) {
+ buzz::XmlElement* regarding = new buzz::XmlElement(QN_GOOGLESESSION_REGARDING);
+ regarding->AddAttr(QN_NAME, GetJid().BareJid().Str());
+ cookie->AddElement(regarding);
+ } else {
+ const buzz::XmlElement* cookie_elem =
+ reinterpret_cast<const XmlCookie*>(message.redirect_cookie())->elem();
+ const buzz::XmlElement* elem;
+ for (elem = cookie_elem->FirstElement(); elem; elem = elem->NextElement())
+ cookie->AddElement(new buzz::XmlElement(*elem));
+ }
+
+ return result;
+}
+
+SessionManager *SessionClient::session_manager() {
+ return session_manager_;
+}
+
+} // namespace cricket
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h
new file mode 100644
index 00000000..69a18422
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/sessionclient.h
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SESSIONCLIENT_H_
+#define _SESSIONCLIENT_H_
+
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/p2p/base/sessionmessage.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/jid.h"
+namespace cricket {
+
+// Generic XMPP session client. This class knows how to translate
+// a SessionMessage to and from XMPP stanzas. The SessionDescription
+// is a custom description implemented by the client.
+
+// This class knows how to talk to the session manager, however the
+// session manager doesn't have knowledge of a particular SessionClient.
+
+class SessionClient : public sigslot::has_slots<> {
+public:
+ SessionClient(SessionManager *psm);
+ virtual ~SessionClient();
+
+ // Call this method to determine if a stanza is for this session client
+ bool IsClientStanza(const buzz::XmlElement *stanza);
+
+ // Call this method to deliver a stanza to this session client
+ void OnIncomingStanza(const buzz::XmlElement *stanza);
+
+ // Call this whenever an error is recieved in response to an outgoing
+ // session IQ. Include the original stanza and any failure stanza. If
+ // the failure is due to a time out, the failure_stanza should be NULL
+ void OnFailedSend(const buzz::XmlElement* original_stanza,
+ const buzz::XmlElement* failure_stanza);
+
+ SessionManager *session_manager();
+
+ // Implement this method for stanza sending
+ sigslot::signal2<SessionClient*, const buzz::XmlElement*> SignalSendStanza;
+
+protected:
+ // Override these to know when sessions belonging to this client create/destroy
+
+ virtual void OnSessionCreate(Session * /*session*/, bool /*received_initiate*/) {}
+ virtual void OnSessionDestroy(Session * /*session*/) {}
+
+ // Implement these methods for a custom session description
+ virtual const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element) = 0;
+ virtual buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description) = 0;
+ virtual const std::string &GetSessionDescriptionName() = 0;
+ virtual const buzz::Jid &GetJid() const = 0;
+
+ SessionManager *session_manager_;
+
+private:
+ void OnSessionCreateSlot(Session *session, bool received_initiate);
+ void OnSessionDestroySlot(Session *session);
+ void OnOutgoingMessage(Session *session, const SessionMessage &message);
+ void ParseHeader(const buzz::XmlElement *stanza, SessionMessage &message);
+ bool ParseCandidate(const buzz::XmlElement *child, Candidate* candidate);
+ bool ParseIncomingMessage(const buzz::XmlElement *stanza,
+ SessionMessage& message);
+ void ParseInitiateAcceptModify(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseCandidates(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseRejectTerminate(const buzz::XmlElement *stanza, SessionMessage &message);
+ void ParseRedirect(const buzz::XmlElement *stanza, SessionMessage &message);
+ buzz::XmlElement *TranslateHeader(const SessionMessage &message);
+ buzz::XmlElement *TranslateCandidate(const Candidate &candidate);
+ buzz::XmlElement *TranslateInitiateAcceptModify(const SessionMessage &message);
+ buzz::XmlElement *TranslateCandidates(const SessionMessage &message);
+ buzz::XmlElement *TranslateRejectTerminate(const SessionMessage &message);
+ buzz::XmlElement *TranslateRedirect(const SessionMessage &message);
+
+};
+
+} // namespace cricket
+
+#endif // _SESSIONCLIENT_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc
new file mode 100644
index 00000000..dd9fa67c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.cc
@@ -0,0 +1,149 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "socketmonitor.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+SocketMonitor::SocketMonitor(P2PSocket *socket, Thread *monitor_thread) {
+ socket_ = socket;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+SocketMonitor::~SocketMonitor() {
+ socket_->thread()->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void SocketMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 250)
+ rate_ = 250;
+ socket_->thread()->Post(this, MSG_MONITOR_START);
+}
+
+void SocketMonitor::Stop() {
+ socket_->thread()->Post(this, MSG_MONITOR_STOP);
+}
+
+void SocketMonitor::OnMessage(Message *message) {
+ CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(Thread::Current() == socket_->thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ socket_->SignalConnectionMonitor.connect(this, &SocketMonitor::OnConnectionMonitor);
+ PollSocket(true);
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(Thread::Current() == socket_->thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ socket_->SignalConnectionMonitor.disconnect(this);
+ socket_->thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(Thread::Current() == socket_->thread());
+ PollSocket(true);
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(Thread::Current() == monitoring_thread_);
+ std::vector<ConnectionInfo> infos = connection_infos_;
+ crit_.Leave();
+ SignalUpdate(this, infos);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void SocketMonitor::OnConnectionMonitor(P2PSocket *socket) {
+ CritScope cs(&crit_);
+ if (monitoring_)
+ PollSocket(false);
+}
+
+void SocketMonitor::PollSocket(bool poll) {
+ CritScope cs(&crit_);
+ assert(Thread::Current() == socket_->thread());
+
+ // Gather connection infos
+
+ connection_infos_.clear();
+ const std::vector<Connection *> &connections = socket_->connections();
+ std::vector<Connection *>::const_iterator it;
+ for (it = connections.begin(); it != connections.end(); it++) {
+ Connection *connection = *it;
+ ConnectionInfo info;
+ info.best_connection = socket_->best_connection() == connection;
+ info.readable = connection->read_state() == Connection::STATE_READABLE;
+ info.writable = connection->write_state() == Connection::STATE_WRITABLE;
+ info.timeout = connection->write_state() == Connection::STATE_WRITE_TIMEOUT;
+ info.new_connection = false; // connection->new_connection();
+ info.rtt = connection->rtt();
+ info.sent_total_bytes = connection->sent_total_bytes();
+ info.sent_bytes_second = connection->sent_bytes_second();
+ info.recv_total_bytes = connection->recv_total_bytes();
+ info.recv_bytes_second = connection->recv_bytes_second();
+ info.local_candidate = connection->local_candidate();
+ info.remote_candidate = connection->remote_candidate();
+ info.est_quality = connection->port()->network()->quality();
+ info.key = reinterpret_cast<void *>(connection);
+ connection_infos_.push_back(info);
+ }
+
+ // Signal the monitoring thread, start another poll timer
+
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ if (poll)
+ socket_->thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+P2PSocket *SocketMonitor::socket() {
+ return socket_;
+}
+
+Thread *SocketMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h
new file mode 100644
index 00000000..549e90b6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/p2p/client/socketmonitor.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SOCKETMONITOR_H_
+#define _SOCKETMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+struct ConnectionInfo {
+ bool best_connection;
+ bool writable;
+ bool readable;
+ bool timeout;
+ bool new_connection;
+ size_t rtt;
+ size_t sent_total_bytes;
+ size_t sent_bytes_second;
+ size_t recv_total_bytes;
+ size_t recv_bytes_second;
+ Candidate local_candidate;
+ Candidate remote_candidate;
+ double est_quality;
+ void *key;
+};
+
+class SocketMonitor : public MessageHandler, public sigslot::has_slots<> {
+public:
+ SocketMonitor(P2PSocket *socket, Thread *monitor_thread);
+ ~SocketMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ P2PSocket *socket();
+ Thread *monitor_thread();
+
+ sigslot::signal2<SocketMonitor *, const std::vector<ConnectionInfo> &> SignalUpdate;
+
+protected:
+ void OnMessage(Message *message);
+ void OnConnectionMonitor(P2PSocket *socket);
+ void PollSocket(bool poll);
+
+ std::vector<ConnectionInfo> connection_infos_;
+ P2PSocket *socket_;
+ Thread *monitoring_thread_;
+ CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _SOCKETMONITOR_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am
new file mode 100644
index 00000000..6cfc5b24
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/Makefile.am
@@ -0,0 +1,3 @@
+noinst_HEADERS = receiver.h sessionsendtask.h
+SUBDIRS = phone
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am
new file mode 100644
index 00000000..b2acbf81
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/Makefile.am
@@ -0,0 +1,18 @@
+libcricketsessionphone_la_SOURCES = audiomonitor.cc \
+ channelmanager.cc \
+ voicechannel.cc \
+ call.cc \
+ phonesessionclient.cc \
+ linphonemediaengine.cc
+
+noinst_HEADERS = audiomonitor.h \
+ channelmanager.h \
+ linphonemediaengine.h \
+ mediaengine.h \
+ phonesessionclient.h \
+ voicechannel.h \
+ call.h \
+ mediachannel.h
+
+AM_CPPFLAGS = -DPOSIX $(ORTP_CFLAGS) $(ILBC_CFLAGS) -I$(srcdir)/../../../talk/third_party/mediastreamer -I$(srcdir)/../../.. $(GLIB_CFLAGS) $(SPEEX_CFLAGS)
+noinst_LTLIBRARIES = libcricketsessionphone.la
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc
new file mode 100644
index 00000000..c1b63d1b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.cc
@@ -0,0 +1,119 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/voicechannel.h"
+#include <cassert>
+
+namespace cricket {
+
+const uint32 MSG_MONITOR_POLL = 1;
+const uint32 MSG_MONITOR_START = 2;
+const uint32 MSG_MONITOR_STOP = 3;
+const uint32 MSG_MONITOR_SIGNAL = 4;
+
+AudioMonitor::AudioMonitor(VoiceChannel *voice_channel, Thread *monitor_thread) {
+ voice_channel_ = voice_channel;
+ monitoring_thread_ = monitor_thread;
+ monitoring_ = false;
+}
+
+AudioMonitor::~AudioMonitor() {
+ voice_channel_->worker_thread()->Clear(this);
+ monitoring_thread_->Clear(this);
+}
+
+void AudioMonitor::Start(int milliseconds) {
+ rate_ = milliseconds;
+ if (rate_ < 100)
+ rate_ = 100;
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_START);
+}
+
+void AudioMonitor::Stop() {
+ voice_channel_->worker_thread()->Post(this, MSG_MONITOR_STOP);
+}
+
+void AudioMonitor::OnMessage(Message *message) {
+ CritScope cs(&crit_);
+
+ switch (message->message_id) {
+ case MSG_MONITOR_START:
+ assert(Thread::Current() == voice_channel_->worker_thread());
+ if (!monitoring_) {
+ monitoring_ = true;
+ PollVoiceChannel();
+ }
+ break;
+
+ case MSG_MONITOR_STOP:
+ assert(Thread::Current() == voice_channel_->worker_thread());
+ if (monitoring_) {
+ monitoring_ = false;
+ voice_channel_->worker_thread()->Clear(this);
+ }
+ break;
+
+ case MSG_MONITOR_POLL:
+ assert(Thread::Current() == voice_channel_->worker_thread());
+ PollVoiceChannel();
+ break;
+
+ case MSG_MONITOR_SIGNAL:
+ {
+ assert(Thread::Current() == monitoring_thread_);
+ AudioInfo info = audio_info_;
+ crit_.Leave();
+ SignalUpdate(this, audio_info_);
+ crit_.Enter();
+ }
+ break;
+ }
+}
+
+void AudioMonitor::PollVoiceChannel() {
+ CritScope cs(&crit_);
+ assert(Thread::Current() == voice_channel_->worker_thread());
+
+ // Gather connection infos
+ audio_info_.input_level = voice_channel_->GetInputLevel_w();
+ audio_info_.output_level = voice_channel_->GetOutputLevel_w();
+
+ // Signal the monitoring thread, start another poll timer
+ monitoring_thread_->Post(this, MSG_MONITOR_SIGNAL);
+ voice_channel_->worker_thread()->PostDelayed(rate_, this, MSG_MONITOR_POLL);
+}
+
+VoiceChannel *AudioMonitor::voice_channel() {
+ return voice_channel_;
+}
+
+Thread *AudioMonitor::monitor_thread() {
+ return monitoring_thread_;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h
new file mode 100644
index 00000000..96b95bd7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/audiomonitor.h
@@ -0,0 +1,73 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_PHONE_AUDIOMONITOR_H_
+#define _CRICKET_PHONE_AUDIOMONITOR_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/base/port.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+
+struct AudioInfo {
+ int input_level;
+ int output_level;
+};
+
+class AudioMonitor : public MessageHandler, public sigslot::has_slots<> {
+public:
+ AudioMonitor(VoiceChannel* voice_channel, Thread *monitor_thread);
+ ~AudioMonitor();
+
+ void Start(int cms);
+ void Stop();
+
+ VoiceChannel* voice_channel();
+ Thread *monitor_thread();
+
+ sigslot::signal2<AudioMonitor*, const AudioInfo&> SignalUpdate;
+
+protected:
+ void OnMessage(Message *message);
+ void PollVoiceChannel();
+
+ AudioInfo audio_info_;
+ VoiceChannel* voice_channel_;
+ Thread* monitoring_thread_;
+ CriticalSection crit_;
+ uint32 rate_;
+ bool monitoring_;
+};
+
+}
+
+#endif // _CRICKET_PHONE_AUDIOMONITOR_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc
new file mode 100644
index 00000000..31b12e92
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.cc
@@ -0,0 +1,258 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/thread.h"
+#include "talk/p2p/base/helpers.h"
+#include "talk/session/phone/call.h"
+
+namespace cricket {
+
+const uint32 MSG_CHECKAUTODESTROY = 1;
+
+Call::Call(PhoneSessionClient *session_client) : muted_(false) {
+ session_client_ = session_client;
+ id_ = CreateRandomId();
+}
+
+Call::~Call() {
+ while (sessions_.begin() != sessions_.end()) {
+ Session *session = sessions_[0];
+ RemoveSession(session);
+ session_client_->session_manager()->DestroySession(session);
+ }
+ Thread::Current()->Clear(this);
+}
+
+Session *Call::InitiateSession(const buzz::Jid &jid) {
+ Session *session = session_client_->CreateSession(this);
+ AddSession(session);
+ session->Initiate(jid.Str(), session_client_->CreateOfferSessionDescription());
+ return session;
+}
+
+void Call::AcceptSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Accept(session_client_->CreateAcceptSessionDescription(session->remote_description()));
+}
+
+void Call::RedirectSession(Session *session, const buzz::Jid &to) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Redirect(to.Str());
+}
+
+void Call::RejectSession(Session *session) {
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ assert(it != sessions_.end());
+ if (it != sessions_.end())
+ session->Reject();
+}
+
+void Call::TerminateSession(Session *session) {
+ assert(std::find(sessions_.begin(), sessions_.end(), session) != sessions_.end());
+ std::vector<Session *>::iterator it;
+ it = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it != sessions_.end())
+ (*it)->Terminate();
+}
+
+void Call::Terminate() {
+ // There may be more than one session to terminate
+ std::vector<Session *>::iterator it = sessions_.begin();
+ for (it = sessions_.begin(); it != sessions_.end(); it++)
+ TerminateSession(*it);
+}
+
+void Call::OnMessage(Message *message) {
+ switch (message->message_id) {
+ case MSG_CHECKAUTODESTROY:
+ // If no more sessions for this call, delete it
+ if (sessions_.size() == 0)
+ session_client_->DestroyCall(this);
+ break;
+ }
+}
+
+const std::vector<Session *> &Call::sessions() {
+ return sessions_;
+}
+
+void Call::AddSession(Session *session) {
+ // Add session to list, create voice channel for this session
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+
+ VoiceChannel *channel = session_client_->channel_manager()->CreateVoiceChannel(session);
+ channel_map_[session->id()] = channel;
+
+ // If this call has the focus, enable this channel
+ if (session_client_->GetFocus() == this)
+ channel->Enable(true);
+
+ // Signal client
+ SignalAddSession(this, session);
+}
+
+void Call::RemoveSession(Session *session) {
+ // Remove session from list
+ std::vector<Session *>::iterator it_session;
+ it_session = std::find(sessions_.begin(), sessions_.end(), session);
+ if (it_session == sessions_.end())
+ return;
+ sessions_.erase(it_session);
+
+ // Destroy session channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = it_channel->second;
+ channel_map_.erase(it_channel);
+ session_client_->channel_manager()->DestroyVoiceChannel(channel);
+ }
+
+ // Signal client
+ SignalRemoveSession(this, session);
+
+ // The call auto destroys when the lass session is removed
+ Thread::Current()->Post(this, MSG_CHECKAUTODESTROY);
+}
+
+VoiceChannel* Call::GetChannel(Session* session) {
+ std::map<SessionID, VoiceChannel *>::iterator it = channel_map_.find(session->id());
+ assert(it != channel_map_.end());
+ return it->second;
+}
+
+void Call::EnableChannels(bool enable) {
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Enable(enable);
+ }
+}
+
+void Call::Mute(bool mute) {
+ muted_ = mute;
+ std::vector<Session *>::iterator it;
+ for (it = sessions_.begin(); it != sessions_.end(); it++) {
+ VoiceChannel *channel = channel_map_[(*it)->id()];
+ if (channel != NULL)
+ channel->Mute(mute);
+ }
+}
+
+void Call::Join(Call *call, bool enable) {
+ while (call->sessions_.size() != 0) {
+ // Move session
+ Session *session = call->sessions_[0];
+ call->sessions_.erase(call->sessions_.begin());
+ sessions_.push_back(session);
+ session->SignalState.connect(this, &Call::OnSessionState);
+ session->SignalError.connect(this, &Call::OnSessionError);
+
+ // Move channel
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = call->channel_map_.find(session->id());
+ if (it_channel != call->channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ call->channel_map_.erase(it_channel);
+ channel_map_[session->id()] = channel;
+ channel->Enable(enable);
+ }
+ }
+}
+
+void Call::StartConnectionMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalConnectionMonitor.connect(this, &Call::OnConnectionMonitor);
+ channel->StartConnectionMonitor(cms);
+ }
+}
+
+void Call::StopConnectionMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopConnectionMonitor();
+ channel->SignalConnectionMonitor.disconnect(this);
+ }
+}
+
+void Call::StartAudioMonitor(Session *session, int cms) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->SignalAudioMonitor.connect(this, &Call::OnAudioMonitor);
+ channel->StartAudioMonitor(cms);
+ }
+}
+
+void Call::StopAudioMonitor(Session *session) {
+ std::map<SessionID, VoiceChannel *>::iterator it_channel;
+ it_channel = channel_map_.find(session->id());
+ if (it_channel != channel_map_.end()) {
+ VoiceChannel *channel = (*it_channel).second;
+ channel->StopAudioMonitor();
+ channel->SignalAudioMonitor.disconnect(this);
+ }
+}
+
+
+void Call::OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, channel->session(), infos);
+}
+
+void Call::OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info) {
+ SignalAudioMonitor(this, channel->session(), info);
+}
+
+uint32 Call::id() {
+ return id_;
+}
+
+void Call::OnSessionState(Session *session, Session::State state) {
+ SignalSessionState(this, session, state);
+}
+
+void Call::OnSessionError(Session *session, Session::Error error) {
+ SignalSessionError(this, session, error);
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h
new file mode 100644
index 00000000..209e13c9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/call.h
@@ -0,0 +1,97 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CALL_H_
+#define _CALL_H_
+
+#include "talk/base/messagequeue.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/xmpp/jid.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/audiomonitor.h"
+
+#include <map>
+#include <vector>
+
+namespace cricket {
+
+class PhoneSessionClient;
+
+class Call : public MessageHandler, public sigslot::has_slots<> {
+public:
+ Call(PhoneSessionClient *session_client);
+ ~Call();
+
+ Session *InitiateSession(const buzz::Jid &jid);
+ void AcceptSession(Session *session);
+ void RedirectSession(Session *session, const buzz::Jid &to);
+ void RejectSession(Session *session);
+ void TerminateSession(Session *session);
+ void Terminate();
+ void StartConnectionMonitor(Session *session, int cms);
+ void StopConnectionMonitor(Session *session);
+ void StartAudioMonitor(Session *session, int cms);
+ void StopAudioMonitor(Session *session);
+ void Mute(bool mute);
+
+ const std::vector<Session *> &sessions();
+ uint32 id();
+ bool muted() const { return muted_; }
+
+ sigslot::signal2<Call *, Session *> SignalAddSession;
+ sigslot::signal2<Call *, Session *> SignalRemoveSession;
+ sigslot::signal3<Call *, Session *, Session::State> SignalSessionState;
+ sigslot::signal3<Call *, Session *, Session::Error> SignalSessionError;
+ sigslot::signal3<Call *, Session *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor;
+ sigslot::signal3<Call *, Session *, const AudioInfo&> SignalAudioMonitor;
+
+private:
+ void OnMessage(Message *message);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionError(Session *session, Session::Error error);
+ void AddSession(Session *session);
+ void RemoveSession(Session *session);
+ void EnableChannels(bool enable);
+ void Join(Call *call, bool enable);
+ void OnConnectionMonitor(VoiceChannel *channel, const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitor(VoiceChannel *channel, const AudioInfo& info);
+ VoiceChannel* GetChannel(Session* session);
+
+ uint32 id_;
+ PhoneSessionClient *session_client_;
+ std::vector<Session *> sessions_;
+ std::map<SessionID, VoiceChannel *> channel_map_;
+ bool muted_;
+
+ friend class PhoneSessionClient;
+};
+
+}
+
+#endif // _CALL_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc
new file mode 100644
index 00000000..98634b12
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.cc
@@ -0,0 +1,203 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_GIPS
+#include "talk/session/phone/gipsmediaengine.h"
+#else
+#include "talk/session/phone/linphonemediaengine.h"
+#endif
+#include "channelmanager.h"
+#include <cassert>
+#include <iostream>
+namespace cricket {
+
+const uint32 MSG_CREATEVOICECHANNEL = 1;
+const uint32 MSG_DESTROYVOICECHANNEL = 2;
+const uint32 MSG_SETAUDIOOPTIONS = 3;
+
+ChannelManager::ChannelManager(Thread *worker_thread) {
+#ifdef HAVE_GIPS
+ media_engine_ = new GipsMediaEngine();
+#else
+ media_engine_ = new LinphoneMediaEngine();
+#endif
+ worker_thread_ = worker_thread;
+ initialized_ = false;
+ Init();
+}
+
+ChannelManager::~ChannelManager() {
+ Exit();
+}
+
+MediaEngine *ChannelManager::media_engine() {
+ return media_engine_;
+}
+
+bool ChannelManager::Init() {
+ initialized_ = media_engine_->Init();
+ return initialized_;
+}
+
+void ChannelManager::Exit() {
+ if (!initialized_)
+ return;
+
+ // Need to destroy the voice channels
+
+ while (true) {
+ crit_.Enter();
+ VoiceChannel *channel = NULL;
+ if (channels_.begin() != channels_.end())
+ channel = channels_[0];
+ crit_.Leave();
+ if (channel == NULL)
+ break;
+ delete channel;
+ }
+ media_engine_->Terminate();
+}
+
+struct CreateParams {
+ Session *session;
+ VoiceChannel *channel;
+};
+
+VoiceChannel *ChannelManager::CreateVoiceChannel(Session *session) {
+ CreateParams params;
+ params.session = session;
+ params.channel = NULL;
+ TypedMessageData<CreateParams *> data(&params);
+ worker_thread_->Send(this, MSG_CREATEVOICECHANNEL, &data);
+ return params.channel;
+}
+
+VoiceChannel *ChannelManager::CreateVoiceChannel_w(Session *session) {
+ CritScope cs(&crit_);
+
+ // This is ok to alloc from a thread other than the worker thread
+ assert(initialized_);
+ MediaChannel *channel = media_engine_->CreateChannel();
+ if (channel == NULL)
+ return NULL;
+
+ VoiceChannel *voice_channel = new VoiceChannel(this, session, channel);
+ channels_.push_back(voice_channel);
+ return voice_channel;
+}
+
+void ChannelManager::DestroyVoiceChannel(VoiceChannel *voice_channel) {
+ TypedMessageData<VoiceChannel *> data(voice_channel);
+ worker_thread_->Send(this, MSG_DESTROYVOICECHANNEL, &data);
+}
+
+void ChannelManager::DestroyVoiceChannel_w(VoiceChannel *voice_channel) {
+ CritScope cs(&crit_);
+ // Destroy voice channel.
+ assert(initialized_);
+ std::vector<VoiceChannel *>::iterator it = std::find(channels_.begin(),
+ channels_.end(), voice_channel);
+ assert(it != channels_.end());
+ if (it == channels_.end())
+ return;
+
+ channels_.erase(it);
+ MediaChannel *channel = voice_channel->channel();
+ delete voice_channel;
+ delete channel;
+}
+
+void ChannelManager::SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ AudioOptions options;
+ options.auto_gain_control = auto_gain_control;
+ options.wave_in_device = wave_in_device;
+ options.wave_out_device = wave_out_device;
+ TypedMessageData<AudioOptions> data(options);
+ worker_thread_->Send(this, MSG_SETAUDIOOPTIONS, &data);
+}
+
+void ChannelManager::SetAudioOptions_w(AudioOptions options) {
+ assert(worker_thread_ == Thread::Current());
+
+ // Set auto gain control on
+ if (media_engine_->SetAudioOptions(options.auto_gain_control?MediaEngine::AUTO_GAIN_CONTROL:0) != 0) {
+ // TODO: We need to log these failures.
+ }
+
+ // Set the audio devices
+ // This will fail if audio is already playing. Stop all of the media
+ // start it up again after changing the setting.
+ {
+ CritScope cs(&crit_);
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->PauseMedia_w();
+ }
+
+ if (media_engine_->SetSoundDevices(options.wave_in_device, options.wave_out_device) == -1) {
+ // TODO: We need to log these failures.
+ }
+
+ for (VoiceChannels::iterator it = channels_.begin();
+ it < channels_.end();
+ ++it) {
+ (*it)->UnpauseMedia_w();
+ }
+ }
+}
+
+Thread *ChannelManager::worker_thread() {
+ return worker_thread_;
+}
+
+void ChannelManager::OnMessage(Message *message) {
+ switch (message->message_id) {
+ case MSG_CREATEVOICECHANNEL:
+ {
+ TypedMessageData<CreateParams *> *data = static_cast<TypedMessageData<CreateParams *> *>(message->pdata);
+ data->data()->channel = CreateVoiceChannel_w(data->data()->session);
+ }
+ break;
+
+ case MSG_DESTROYVOICECHANNEL:
+ {
+ TypedMessageData<VoiceChannel *> *data = static_cast<TypedMessageData<VoiceChannel *> *>(message->pdata);
+ DestroyVoiceChannel_w(data->data());
+ }
+ break;
+ case MSG_SETAUDIOOPTIONS:
+ {
+ TypedMessageData<AudioOptions> *data = static_cast<TypedMessageData<AudioOptions> *>(message->pdata);
+ SetAudioOptions_w(data->data());
+ }
+ break;
+ }
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h
new file mode 100644
index 00000000..7200f75e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/channelmanager.h
@@ -0,0 +1,81 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CHANNELMANAGER_H_
+#define _CHANNELMANAGER_H_
+
+#include "talk/base/thread.h"
+#include "talk/base/criticalsection.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/mediaengine.h"
+#include <vector>
+
+namespace cricket {
+
+class VoiceChannel;
+
+class ChannelManager : public MessageHandler {
+public:
+ ChannelManager(Thread *worker_thread);
+ ~ChannelManager();
+
+ VoiceChannel *CreateVoiceChannel(Session *session);
+ void DestroyVoiceChannel(VoiceChannel *voice_channel);
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device);
+
+ MediaEngine *media_engine();
+ Thread *worker_thread();
+
+private:
+ VoiceChannel *CreateVoiceChannel_w(Session *session);
+ void DestroyVoiceChannel_w(VoiceChannel *voice_channel);
+ void OnMessage(Message *message);
+ bool Init();
+ void Exit();
+
+ struct AudioOptions {
+ bool auto_gain_control;
+ int wave_in_device;
+ int wave_out_device;
+ };
+ void SetAudioOptions_w(AudioOptions options);
+
+ Thread *worker_thread_;
+ MediaEngine *media_engine_;
+ bool initialized_;
+ CriticalSection crit_;
+
+ typedef std::vector<VoiceChannel*> VoiceChannels;
+ VoiceChannels channels_;
+};
+
+}
+
+#endif // _CHANNELMANAGER_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
new file mode 100644
index 00000000..7d2305dc
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.cc
@@ -0,0 +1,170 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+extern "C" {
+#include "talk/third_party/mediastreamer/mediastream.h"
+#ifdef HAVE_ILBC
+#include "talk/third_party/mediastreamer/msilbcdec.h"
+#endif
+#ifdef HAVE_SPEEX
+#include "talk/third_party/mediastreamer/msspeexdec.h"
+#endif
+}
+#include <ortp/ortp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include "talk/session/phone/linphonemediaengine.h"
+
+using namespace cricket;
+
+void *thread_function(void *data)
+{
+ LinphoneMediaChannel *mc =(LinphoneMediaChannel*) data;
+ while (mc->dying() == false) {
+ MediaChannel::NetworkInterface *iface = mc->network_interface();
+ char *buf[2048];
+ int len;
+ len = read(mc->fd(), buf, sizeof(buf));
+ if (iface && (mc->mute()==FALSE))
+ iface->SendPacket(buf, len);
+ }
+}
+
+LinphoneMediaChannel::LinphoneMediaChannel() {
+ pt_ = 102;
+ dying_ = false;
+ pthread_attr_t attr;
+ audio_stream_ = NULL;
+
+ struct sockaddr_in sockaddr;
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ sockaddr.sin_port = htons(3000);
+ fd_ = socket(PF_INET, SOCK_DGRAM, 0);
+ fcntl(fd_, F_SETFL, 0, O_NONBLOCK);
+ bind (fd_,(struct sockaddr*)&sockaddr, sizeof(sockaddr));
+
+ pthread_attr_init(&attr);
+ pthread_create(&thread_, &attr, &thread_function, this);
+}
+
+LinphoneMediaChannel::~LinphoneMediaChannel() {
+ dying_ = true;
+ pthread_join(thread_, NULL);
+ audio_stream_stop(audio_stream_);
+ close(fd_);
+}
+
+void LinphoneMediaChannel::SetCodec(const char *codec) {
+ if (!strcmp(codec, "iLBC"))
+ pt_ = 102;
+ else if (!strcmp(codec, "speex"))
+ pt_ = 110;
+ else
+ pt_ = 0;
+ if (audio_stream_)
+ audio_stream_stop(audio_stream_);
+ audio_stream_ = audio_stream_start(&av_profile, 2000, "127.0.0.1", 3000, pt_, 250);
+}
+
+void LinphoneMediaChannel::OnPacketReceived(const void *data, int len) {
+ struct sockaddr_in sockaddr;
+ sockaddr.sin_family = AF_INET;
+ struct hostent *host = gethostbyname("localhost");
+ memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length);
+ sockaddr.sin_port = htons(2000);
+
+ char buf[2048];
+ memcpy(buf, data, len);
+
+ if (buf[1] == pt_) {
+ } else if (buf[1] == 13) {
+ } else if (buf[1] == 102) {
+ SetCodec("iLBC");
+ } else if (buf[1] == 110) {
+ SetCodec("speex");
+ } else if (buf[1] == 0) {
+ SetCodec("PCMU");
+ }
+
+ if (play_ && buf[1] != 13)
+ sendto(fd_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+}
+
+void LinphoneMediaChannel::SetPlayout(bool playout) {
+ play_ = playout;
+}
+
+void LinphoneMediaChannel::SetSend(bool send) {
+ mute_ = !send;
+}
+
+float LinphoneMediaChannel::GetCurrentQuality() {}
+int LinphoneMediaChannel::GetOutputLevel() {}
+
+LinphoneMediaEngine::LinphoneMediaEngine() {}
+LinphoneMediaEngine::~LinphoneMediaEngine() {}
+
+static void null_log_handler(const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data) {
+}
+
+bool LinphoneMediaEngine::Init() {
+ g_log_set_handler("MediaStreamer", G_LOG_LEVEL_MASK, null_log_handler, NULL);
+ g_log_set_handler("oRTP", G_LOG_LEVEL_MASK, null_log_handler, NULL);
+ g_log_set_handler("oRTP-stats", G_LOG_LEVEL_MASK, null_log_handler, NULL);
+ ortp_init();
+ ms_init();
+
+#ifdef HAVE_SPEEX
+ ms_speex_codec_init();
+ rtp_profile_set_payload(&av_profile, 110, &speex_wb);
+ codecs_.push_back(Codec(110, "speex", 8));
+#endif
+
+#ifdef HAVE_ILBC
+ ms_ilbc_codec_init();
+ rtp_profile_set_payload(&av_profile, 102, &payload_type_ilbc);
+ codecs_.push_back(Codec(102, "iLBC", 4));
+#endif
+
+ rtp_profile_set_payload(&av_profile, 0, &pcmu8000);
+ codecs_.push_back(Codec(0, "PCMU", 2));
+
+return true;
+}
+
+void LinphoneMediaEngine::Terminate() {
+
+}
+
+MediaChannel *LinphoneMediaEngine::CreateChannel() {
+ return new LinphoneMediaChannel();
+}
+
+int LinphoneMediaEngine::SetAudioOptions(int options) {}
+int LinphoneMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device) {}
+
+float LinphoneMediaEngine::GetCurrentQuality() {}
+int LinphoneMediaEngine::GetInputLevel() {}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h
new file mode 100644
index 00000000..ee16d2ee
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/linphonemediaengine.h
@@ -0,0 +1,75 @@
+/*
+ * Jingle call example
+ * Copyright 2004--2005, Google Inc.
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// LinphoneMediaEngine is a Linphone implementation of MediaEngine
+
+#ifndef TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+#define TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
+
+extern "C" {
+#include "talk/third_party/mediastreamer/mediastream.h"
+}
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+class LinphoneMediaChannel : public MediaChannel {
+ public:
+ LinphoneMediaChannel();
+ virtual ~LinphoneMediaChannel();
+ virtual void SetCodec(const char *codec);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+ virtual float GetCurrentQuality();
+ virtual int GetOutputLevel();
+ int fd() {return fd_;}
+ bool mute() {return mute_;}
+ bool dying() {return dying_;}
+ private:
+ AudioStream *audio_stream_;
+ pthread_t thread_;
+ int fd_;
+ int pt_;
+ bool dying_;
+ bool mute_;
+ bool play_;
+};
+
+class LinphoneMediaEngine : public MediaEngine {
+ public:
+ LinphoneMediaEngine();
+ ~LinphoneMediaEngine();
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual float GetCurrentQuality();
+ virtual int GetInputLevel();
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_LINPHONEMEDIAENGINE_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h
new file mode 100644
index 00000000..db2f9654
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediachannel.h
@@ -0,0 +1,55 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TALK_SESSION_PHONE_MEDIACHANNEL_H_
+#define TALK_SESSION_PHONE_MEDIACHANNEL_H_
+
+namespace cricket {
+
+class MediaChannel {
+ public:
+ class NetworkInterface {
+ public:
+ virtual void SendPacket(const void *data, size_t len) = 0;
+ };
+ MediaChannel() {network_interface_ = NULL;}
+ virtual ~MediaChannel() {};
+ void SetInterface(NetworkInterface *iface) {network_interface_ = iface;}
+ virtual void SetCodec(const char *codec) = 0;
+ virtual void OnPacketReceived(const void *data, int len) = 0;
+ virtual void SetPlayout(bool playout) = 0;
+ virtual void SetSend(bool send) = 0;
+ virtual float GetCurrentQuality() = 0;
+ virtual int GetOutputLevel() = 0;
+ NetworkInterface *network_interface() {return network_interface_;}
+ protected:
+ NetworkInterface *network_interface_;
+};
+
+}; // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIACHANNEL_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h
new file mode 100644
index 00000000..fa07d2ec
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/mediaengine.h
@@ -0,0 +1,95 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// MediaEngine is an abstraction of a media engine which can be subclassed
+// to support different media componentry backends.
+
+#ifndef TALK_SESSION_PHONE_MEDIAENGINE_H_
+#define TALK_SESSION_PHONE_MEDIAENGINE_H_
+
+#include <string>
+#include <vector>
+#include "mediachannel.h"
+
+namespace cricket {
+
+class MediaEngine {
+ public:
+
+ struct Codec {
+ int id;
+ std::string name;
+ int preference;
+ // Creates a codec with the given parameters.
+ Codec(int pt, const std::string& nm, int pr) : id(pt), name(nm), preference(pr) {}
+ // Ranks codecs by their preferences.
+ bool operator <(const Codec& c) const { return preference > c.preference; }
+ };
+
+ // Bitmask flags for options that may be supported by the media engine implementation
+ enum MediaEngineOptions {
+ AUTO_GAIN_CONTROL = 1 << 1,
+ };
+
+ MediaEngine() {}
+
+ // Initialize
+ virtual bool Init() = 0;
+ virtual void Terminate() = 0;
+ virtual MediaChannel *CreateChannel() = 0;
+
+ virtual int SetAudioOptions(int options) = 0;
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device) = 0;
+ virtual int GetInputLevel() = 0;
+
+ std::vector<Codec> &codecs() { return codecs_; }
+
+ bool FindCodec(const char* codec) {
+ for (std::vector<Codec>::iterator i = codecs_.begin(); i < codecs_.end(); i++) {
+ if ((*i).name == codec)
+ return true;
+ }
+ return false;
+ }
+
+ bool GetCodecPreference (const char *codec, int & preference) {
+ for (std::vector<Codec>::iterator i = codecs_.begin(); i < codecs_.end(); i++) {
+ if ((*i).name == codec) {
+ preference = (*i).preference;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected:
+ std::vector<Codec> codecs_;
+};
+
+} // namespace cricket
+
+#endif // TALK_SESSION_PHONE_MEDIAENGINE_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc
new file mode 100644
index 00000000..d8a31df2
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.cc
@@ -0,0 +1,267 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/logging.h"
+#include "talk/session/receiver.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/xmllite/qname.h"
+namespace {
+
+const std::string NS_PHONE("http://www.google.com/session/phone");
+const std::string NS_EMPTY("");
+
+const buzz::QName QN_PHONE_DESCRIPTION(true, NS_PHONE, "description");
+const buzz::QName QN_PHONE_PAYLOADTYPE(true, NS_PHONE, "payload-type");
+const buzz::QName QN_PHONE_PAYLOADTYPE_ID(true, NS_EMPTY, "id");
+const buzz::QName QN_PHONE_PAYLOADTYPE_NAME(true, NS_EMPTY, "name");
+
+}
+
+namespace cricket {
+
+PhoneSessionClient::PhoneSessionClient(const buzz::Jid& jid,
+ SessionManager *manager) : jid_(jid), SessionClient(manager) {
+
+ // No call to start, and certainly no call with focus
+ focus_call_ = NULL;
+
+ // Start up the channel manager on a worker thread
+ channel_manager_ = new ChannelManager(session_manager_->worker_thread());
+}
+
+PhoneSessionClient::~PhoneSessionClient() {
+ // Destroy all calls
+ std::map<uint32, Call *>::iterator it;
+ while (calls_.begin() != calls_.end()) {
+ std::map<uint32, Call *>::iterator it = calls_.begin();
+ DestroyCall((*it).second);
+ }
+
+ // Delete channel manager. This will wait for the channels to exit
+ delete channel_manager_;
+}
+
+const std::string &PhoneSessionClient::GetSessionDescriptionName() {
+ return NS_PHONE;
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateOfferSessionDescription() {
+ PhoneSessionDescription* session_desc = new PhoneSessionDescription();
+
+ MediaEngine *me = channel_manager_->media_engine();
+ std::vector<MediaEngine::Codec> codecs = me->codecs();
+ std::vector<MediaEngine::Codec>::iterator i;
+ for (i = codecs.begin(); i < codecs.end(); i++)
+ session_desc->AddCodec(*i);
+
+ session_desc->Sort();
+ return session_desc;
+}
+
+PhoneSessionDescription* PhoneSessionClient::CreateAcceptSessionDescription(const SessionDescription* offer) {
+ const PhoneSessionDescription* offer_desc =
+ static_cast<const PhoneSessionDescription*>(offer);
+ PhoneSessionDescription* accept_desc = new PhoneSessionDescription();
+ std::vector<MediaEngine::Codec> codecs = channel_manager_->media_engine()->codecs();
+ std::vector<MediaEngine::Codec>::iterator iter;
+ for (unsigned int i = 0; i < offer_desc->codecs().size(); ++i) {
+ for (iter = codecs.begin(); iter < codecs.end(); iter++) {
+ if ((*iter).name == offer_desc->codecs()[i].name)
+ accept_desc->AddCodec(*iter);
+ }
+ }
+
+ accept_desc->Sort();
+ return accept_desc;
+}
+
+bool PhoneSessionClient::FindMediaCodec(MediaEngine* me,
+ const PhoneSessionDescription* desc,
+ const char** codec) {
+ for (size_t i = 0; i < desc->codecs().size(); ++i) {
+ if (me->FindCodec(desc->codecs()[i].name.c_str()))
+ *codec = desc->codecs()[i].name.c_str();
+ return true;
+ }
+
+ return false;
+}
+
+const SessionDescription *PhoneSessionClient::CreateSessionDescription(const buzz::XmlElement *element) {
+ PhoneSessionDescription* desc = new PhoneSessionDescription();
+
+ const buzz::XmlElement* payload_type = element->FirstNamed(QN_PHONE_PAYLOADTYPE);
+ int num_payload_types = 0;
+
+ while (payload_type) {
+ if (payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_ID) &&
+ payload_type->HasAttr(QN_PHONE_PAYLOADTYPE_NAME)) {
+ int id = atoi(payload_type->Attr(QN_PHONE_PAYLOADTYPE_ID).c_str());
+ int pref = 0;
+ std::string name = payload_type->Attr(QN_PHONE_PAYLOADTYPE_NAME);
+ desc->AddCodec(MediaEngine::Codec(id, name, 0));
+ }
+
+ payload_type = payload_type->NextNamed(QN_PHONE_PAYLOADTYPE);
+ num_payload_types += 1;
+ }
+
+ // For backward compatability, we can assume the other client is (an old
+ // version of Talk) if it has no payload types at all.
+ if (num_payload_types == 0) {
+ desc->AddCodec(MediaEngine::Codec(103, "ISAC", 1));
+ desc->AddCodec(MediaEngine::Codec(0, "PCMU", 0));
+ }
+
+ return desc;
+}
+
+buzz::XmlElement *PhoneSessionClient::TranslateSessionDescription(const SessionDescription *_session_desc) {
+ const PhoneSessionDescription* session_desc =
+ static_cast<const PhoneSessionDescription*>(_session_desc);
+ buzz::XmlElement* description = new buzz::XmlElement(QN_PHONE_DESCRIPTION, true);
+
+ for (size_t i = 0; i < session_desc->codecs().size(); ++i) {
+ buzz::XmlElement* payload_type = new buzz::XmlElement(QN_PHONE_PAYLOADTYPE, true);
+
+ char buf[32];
+ sprintf(buf, "%d", session_desc->codecs()[i].id);
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_ID, buf);
+
+ payload_type->AddAttr(QN_PHONE_PAYLOADTYPE_NAME,
+ session_desc->codecs()[i].name.c_str());
+
+ description->AddElement(payload_type);
+ }
+
+ return description;
+}
+
+Call *PhoneSessionClient::CreateCall() {
+ Call *call = new Call(this);
+ calls_[call->id()] = call;
+ SignalCallCreate(call);
+ return call;
+}
+
+void PhoneSessionClient::OnSessionCreate(Session *session, bool received_initiate) {
+ if (received_initiate) {
+ session->SignalState.connect(this, &PhoneSessionClient::OnSessionState);
+
+ Call *call = CreateCall();
+ session_map_[session->id()] = call;
+ call->AddSession(session);
+ }
+}
+
+void PhoneSessionClient::OnSessionState(Session *session, Session::State state) {
+ if (state == Session::STATE_RECEIVEDINITIATE) {
+ // If our accept would have no codecs, then we must reject this call.
+ PhoneSessionDescription* accept_desc =
+ CreateAcceptSessionDescription(session->remote_description());
+ if (accept_desc->codecs().size() == 0) {
+ // TODO: include an error description with the rejection.
+ session->Reject();
+ }
+ delete accept_desc;
+ }
+}
+
+void PhoneSessionClient::DestroyCall(Call *call) {
+ // Change focus away, signal destruction
+
+ if (call == focus_call_)
+ SetFocus(NULL);
+ SignalCallDestroy(call);
+
+ // Remove it from calls_ map and delete
+
+ std::map<uint32, Call *>::iterator it = calls_.find(call->id());
+ if (it != calls_.end())
+ calls_.erase(it);
+
+ delete call;
+}
+
+void PhoneSessionClient::OnSessionDestroy(Session *session) {
+ // Find the call this session is in, remove it
+
+ std::map<SessionID, Call *>::iterator it = session_map_.find(session->id());
+ assert(it != session_map_.end());
+ if (it != session_map_.end()) {
+ Call *call = (*it).second;
+ session_map_.erase(it);
+ call->RemoveSession(session);
+ }
+}
+
+Call *PhoneSessionClient::GetFocus() {
+ return focus_call_;
+}
+
+void PhoneSessionClient::SetFocus(Call *call) {
+ Call *old_focus_call = focus_call_;
+ if (focus_call_ != call) {
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(false);
+ focus_call_ = call;
+ if (focus_call_ != NULL)
+ focus_call_->EnableChannels(true);
+ SignalFocus(focus_call_, old_focus_call);
+ }
+}
+
+void PhoneSessionClient::JoinCalls(Call *call_to_join, Call *call) {
+ // Move all sessions from call to call_to_join, delete call.
+ // If call_to_join has focus, added sessions should have enabled channels.
+
+ if (focus_call_ == call)
+ SetFocus(NULL);
+ call_to_join->Join(call, focus_call_ == call_to_join);
+ DestroyCall(call);
+}
+
+Session *PhoneSessionClient::CreateSession(Call *call) {
+ Session *session = session_manager_->CreateSession(
+ GetSessionDescriptionName(), jid().Str());
+ session_map_[session->id()] = call;
+ return session;
+}
+
+ChannelManager *PhoneSessionClient::channel_manager() {
+ return channel_manager_;
+}
+
+const buzz::Jid &PhoneSessionClient::jid() const {
+ return jid_;
+}
+
+const buzz::Jid &PhoneSessionClient::GetJid() const {
+ return jid_;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h
new file mode 100644
index 00000000..150bf34b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/phonesessionclient.h
@@ -0,0 +1,122 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PHONESESSIONCLIENT_H_
+#define _PHONESESSIONCLIENT_H_
+
+#include "talk/session/phone/call.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/base/sigslot.h"
+#include "talk/base/messagequeue.h"
+#include "talk/base/thread.h"
+#include "talk/p2p/client/sessionclient.h"
+#include "talk/p2p/base/sessionmanager.h"
+#include "talk/p2p/base/session.h"
+#include "talk/p2p/base/sessiondescription.h"
+#include "talk/xmpp/xmppclient.h"
+#include <map>
+
+namespace cricket {
+
+class Call;
+class PhoneSessionDescription;
+
+class PhoneSessionClient : public SessionClient {
+public:
+ PhoneSessionClient(const buzz::Jid& jid, SessionManager *manager);
+ ~PhoneSessionClient();
+
+ const buzz::Jid &jid() const;
+
+ Call *CreateCall();
+ void DestroyCall(Call *call);
+
+ Call *GetFocus();
+ void SetFocus(Call *call);
+
+ void JoinCalls(Call *call_to_join, Call *call);
+
+ void SetAudioOptions(bool auto_gain_control, int wave_in_device,
+ int wave_out_device) {
+ if (channel_manager_)
+ channel_manager_->SetAudioOptions(auto_gain_control, wave_in_device,
+ wave_out_device);
+ }
+
+ sigslot::signal2<Call *, Call *> SignalFocus;
+ sigslot::signal1<Call *> SignalCallCreate;
+ sigslot::signal1<Call *> SignalCallDestroy;
+
+ PhoneSessionDescription* CreateOfferSessionDescription();
+ PhoneSessionDescription* CreateAcceptSessionDescription(const SessionDescription* offer);
+
+ // Returns our preference for the given codec.
+ static int GetMediaCodecPreference(const char* name);
+
+ // Returns the name of the first codec in the description that
+ // is found. Return value is false if none was found.
+ static bool FindMediaCodec(MediaEngine* gips,
+ const PhoneSessionDescription* desc,
+ const char **codec);
+
+private:
+ void OnSessionCreate(Session *session, bool received_initiate);
+ void OnSessionState(Session *session, Session::State state);
+ void OnSessionDestroy(Session *session);
+ const SessionDescription *CreateSessionDescription(const buzz::XmlElement *element);
+ buzz::XmlElement *TranslateSessionDescription(const SessionDescription *description);
+ const std::string &GetSessionDescriptionName();
+ const buzz::Jid &GetJid() const;
+ Session *CreateSession(Call *call);
+ ChannelManager *channel_manager();
+
+ buzz::Jid jid_;
+ Call *focus_call_;
+ ChannelManager *channel_manager_;
+ std::map<uint32, Call *> calls_;
+ std::map<SessionID, Call *> session_map_;
+
+ friend class Call;
+};
+
+class PhoneSessionDescription: public SessionDescription {
+public:
+ // Returns the list of codecs sorted by our preference.
+ const std::vector<MediaEngine::Codec>& codecs() const { return codecs_; }
+
+ // Adds another codec to the list.
+ void AddCodec(const MediaEngine::Codec& codec) { codecs_.push_back(codec); }
+ // Sorts the list of codecs by preference.
+ void Sort() { /* std::stable_sort(codecs_.begin(), codecs_.end());*/ }
+
+private:
+ std::vector<MediaEngine::Codec> codecs_;
+};
+
+}
+
+#endif // _PHONESESSIONCLIENT_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc
new file mode 100644
index 00000000..b65c9a20
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.cc
@@ -0,0 +1,331 @@
+#include <portaudio.h>
+#include <ortp/ortp.h>
+#include <speex.h>
+
+// Socket stuff
+#ifndef _WIN32
+#ifdef INET6
+#include <netdb.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+#else
+#include <winsock32.h>
+#endif
+
+#include "talk/session/phone/mediaengine.h"
+#include "talk/session/phone/portaudiomediaengine.h"
+
+// Engine settings
+#define ENGINE_BUFFER_SIZE 2048
+
+// PortAudio settings
+#define FRAMES_PER_BUFFER 256
+#define SAMPLE_RATE 1
+
+// Speex settings
+//#define SPEEX_QUALITY 8
+
+// ORTP settings
+#define MAX_RTP_SIZE 1500 // From mediastreamer
+
+
+// -----------------------------------------------------------------------------
+
+static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *channel_p )
+{
+ PortAudioMediaChannel* channel = (PortAudioMediaChannel*) channel_p;
+ channel->readOutput((float*) outputBuffer, framesPerBuffer);
+ channel->writeInput((float*) inputBuffer, framesPerBuffer);
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+PortAudioMediaChannel::PortAudioMediaChannel() : mute_(false), play_(false), stream_(NULL), out_buffer_(NULL), in_buffer_(NULL), speex_frame_(NULL)
+{
+ // Initialize buffers
+ out_buffer_ = new float[ENGINE_BUFFER_SIZE];
+ out_buffer_read_ = out_buffer_write_ = (float*) out_buffer_;
+ out_buffer_end_ = (float*) out_buffer_ + ENGINE_BUFFER_SIZE;
+ in_buffer_ = new float[ENGINE_BUFFER_SIZE];
+ in_buffer_read_ = in_buffer_write_ = (float*) in_buffer_;
+ in_buffer_end_ = (float*) in_buffer_ + ENGINE_BUFFER_SIZE;
+
+ // Initialize PortAudio
+ int err = Pa_OpenDefaultStream(&stream_, 1, 1, paFloat32, SAMPLE_RATE, FRAMES_PER_BUFFER, 0, portAudioCallback, this );
+ if (err != paNoError)
+ fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err));
+
+ // Initialize Speex
+ speex_bits_init(&speex_bits_);
+ speex_enc_state_ = speex_encoder_init(&speex_nb_mode);
+ speex_dec_state_ = speex_decoder_init(&speex_nb_mode);
+ speex_decoder_ctl(speex_dec_state_, SPEEX_GET_FRAME_SIZE, &speex_frame_size_);
+ speex_frame_ = new float[speex_frame_size_];
+
+ // int quality = SPEEX_QUALITY;
+ // speex_encoder_ctl(state, SPEEX_SET_QUALITY, &quality);
+
+ // Initialize ORTP socket
+ struct sockaddr_in sockaddr;
+ sockaddr.sin_family = AF_INET;
+ sockaddr.sin_addr.s_addr = INADDR_ANY;
+ sockaddr.sin_port = htons(3000);
+ rtp_socket_ = socket(PF_INET, SOCK_DGRAM, 0);
+ fcntl(rtp_socket_, F_SETFL, 0, O_NONBLOCK);
+ bind (rtp_socket_,(struct sockaddr*)&sockaddr, sizeof(sockaddr));
+
+ // Initialize ORTP Session
+ rtp_session_ = rtp_session_new(RTP_SESSION_SENDRECV);
+ rtp_session_max_buf_size_set(rtp_session_, MAX_RTP_SIZE);
+ rtp_session_set_profile(rtp_session_, &av_profile);
+ rtp_session_set_local_addr(rtp_session_, "127.0.0.1", 2000);
+ rtp_session_set_remote_addr(rtp_session_, "127.0.0.1", 3000);
+ rtp_session_set_scheduling_mode(rtp_session_, 0);
+ rtp_session_set_blocking_mode(rtp_session_, 0);
+ rtp_session_set_payload_type(rtp_session_, 110);
+ rtp_session_set_jitter_compensation(rtp_session_, 250);
+ rtp_session_enable_adaptive_jitter_compensation(rtp_session_, TRUE);
+ rtp_timestamp_ = 0;
+ //rtp_session_signal_connect(rtp_session_, "telephone-event", (RtpCallback) ortpTelephoneCallback,this);
+}
+
+PortAudioMediaChannel::~PortAudioMediaChannel()
+{
+ if (stream_) {
+ Pa_CloseStream(stream_);
+ }
+
+ // Clean up other allocated pointers
+
+ close(rtp_socket_);
+}
+
+void PortAudioMediaChannel::SetCodec(const char *codec)
+{
+ if (strcmp(codec, "speex"))
+ printf("Unsupported codec: %s\n", codec);
+}
+
+void PortAudioMediaChannel::OnPacketReceived(const void *data, int len)
+{
+ struct sockaddr_in sockaddr;
+ sockaddr.sin_family = AF_INET;
+ struct hostent *host = gethostbyname("localhost");
+ memcpy(&sockaddr.sin_addr.s_addr, host->h_addr, host->h_length);
+ sockaddr.sin_port = htons(2000);
+
+ char buf[2048];
+ memcpy(buf, data, len);
+
+ // Pass packet on to ORTP
+ if (play_) {
+ sendto(rtp_socket_, buf, len, 0, (struct sockaddr*)&sockaddr, sizeof(sockaddr));
+ }
+}
+
+void PortAudioMediaChannel::SetPlayout(bool playout)
+{
+ if (!stream_)
+ return;
+
+ if (play_ && !playout) {
+ int err = Pa_StopStream(stream_);
+ if (err != paNoError) {
+ fprintf(stderr, "Error stopping PortAudio stream: %s\n", Pa_GetErrorText(err));
+ return;
+ }
+ play_ = false;
+ }
+ else if (!play_ && playout) {
+ int err = Pa_StartStream(stream_);
+ if (err != paNoError) {
+ fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err));
+ return;
+ }
+ play_ = true;
+ }
+}
+
+void PortAudioMediaChannel::SetSend(bool send)
+{
+ mute_ = !send;
+}
+
+
+float PortAudioMediaChannel::GetCurrentQuality()
+{
+ return 0;
+}
+
+int PortAudioMediaChannel::GetOutputLevel()
+{
+ return 0;
+}
+
+void PortAudioMediaChannel::readOutput(float* buf, int len)
+{
+ //readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len);
+
+ // Receive a packet (if there is one)
+ mblk_t *mp;
+ mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_);
+ while (mp != NULL) {
+ gint in_len = mp->b_cont->b_wptr-mp->b_cont->b_rptr;
+
+ // Decode speex stream
+ speex_bits_read_from(&speex_bits_,mp->b_cont->b_rptr, in_len);
+ speex_decode(speex_dec_state_, &speex_bits_, speex_frame_);
+ writeBuffer(out_buffer_, out_buffer_read_, &out_buffer_write_, out_buffer_end_, speex_frame_, speex_frame_size_);
+ rtp_timestamp_++;
+ mp = rtp_session_recvm_with_ts(rtp_session_,rtp_timestamp_);
+ }
+
+ // Read output
+ readBuffer(out_buffer_, &out_buffer_read_, out_buffer_write_, out_buffer_end_, buf, len);
+}
+
+void PortAudioMediaChannel::writeInput(float* buf, int len)
+{
+ //writeBuffer(in_buffer_, in_buffer_read_, &in_buffer_write_, in_buffer_end_, buf, len);
+}
+
+
+void PortAudioMediaChannel::readBuffer(float* buffer, float** buffer_read_p, float*buffer_write, float* buffer_end, float* target_buffer, int target_len)
+{
+ float *end, *tmp, *buffer_read = *buffer_read_p;
+ int remaining;
+
+ // First phase
+ tmp = buffer_read + target_len;
+ if (buffer_write < buffer_read && tmp > buffer_end) {
+ end = buffer_end;
+ remaining = tmp - buffer_end;
+ }
+ else {
+ end = (tmp > buffer_write ? buffer_write : tmp);
+ remaining = 0;
+ }
+
+ while (buffer_read < end) {
+ *target_buffer++ = *buffer_read++;
+ }
+
+ // Second phase
+ if (remaining > 0) {
+ buffer_read = buffer;
+ tmp = buffer_read + remaining;
+ end = (tmp > buffer_write ? buffer_write : tmp);
+ while (buffer_read < end) {
+ *target_buffer++ = *buffer_read++;
+ }
+ }
+
+ // Finish up
+ *buffer_read_p = buffer_read;
+}
+
+void PortAudioMediaChannel::writeBuffer(float* buffer, float* buffer_read, float**buffer_write_p, float* buffer_end, float* source_buffer, int source_len)
+{
+ float *end, *tmp, *buffer_write = *buffer_write_p;
+ int remaining;
+
+ // First phase
+ tmp = buffer_write + source_len;
+ if (buffer_write > buffer_read) {
+ if (tmp > buffer_end) {
+ end = buffer_end;
+ remaining = tmp - buffer_end;
+ }
+ else {
+ end = tmp;
+ remaining = 0;
+ }
+ }
+ else {
+ if (tmp > buffer_read) {
+ printf("Warning: Dropping frame(s)\n");
+ end = buffer_read;
+ remaining = 0;
+ }
+ else {
+ end = tmp;
+ remaining = 0;
+ }
+ }
+
+ while (buffer_write < end) {
+ *buffer_write++ = *source_buffer++;
+ }
+
+ // Second phase
+ if (remaining > 0) {
+ buffer_write = buffer;
+ tmp = buffer_write + remaining;
+ if (tmp > buffer_read) {
+ printf("Warning: Dropping frame(s)\n");
+ end = buffer_read;
+ }
+ else {
+ end = tmp;
+ }
+ while (buffer_write < end) {
+ *buffer_write++ = *source_buffer++;
+ }
+ }
+
+ // Finish up
+ *buffer_write_p = buffer_write;
+}
+
+// -----------------------------------------------------------------------------
+
+PortAudioMediaEngine::PortAudioMediaEngine()
+{
+}
+
+PortAudioMediaEngine::~PortAudioMediaEngine()
+{
+ Pa_Terminate();
+}
+
+bool PortAudioMediaEngine::Init()
+{
+ ortp_init();
+
+ int err = Pa_Initialize();
+ if (err != paNoError) {
+ fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err));
+ return false;
+ }
+
+ // Speex
+ rtp_profile_set_payload(&av_profile, 110, &speex_wb);
+ codecs_.push_back(Codec(110, "speex", 8));
+
+ return true;
+}
+
+void PortAudioMediaEngine::Terminate()
+{
+}
+
+
+cricket::MediaChannel* PortAudioMediaEngine::CreateChannel()
+{
+ return new PortAudioMediaChannel();
+}
+
+int PortAudioMediaEngine::SetAudioOptions(int options)
+{
+}
+
+int PortAudioMediaEngine::SetSoundDevices(int wave_in_device, int wave_out_device)
+{
+}
+
+int PortAudioMediaEngine::GetInputLevel()
+{
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h
new file mode 100644
index 00000000..95c39a1a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/portaudiomediaengine.h
@@ -0,0 +1,69 @@
+#ifndef PORTAUDIOMEDIAENGINE_H
+#define PORTAUDIOMEDIAENGINE_H
+
+#include <portaudio.h>
+#include <speex.h>
+#include <ortp/ortp.h>
+
+#include "talk/session/phone/mediaengine.h"
+
+class PortAudioMediaChannel : public cricket::MediaChannel
+{
+public:
+ PortAudioMediaChannel();
+ virtual ~PortAudioMediaChannel();
+ virtual void SetCodec(const char *codec);
+ virtual void OnPacketReceived(const void *data, int len);
+
+ virtual void SetPlayout(bool playout);
+ virtual void SetSend(bool send);
+
+ virtual float GetCurrentQuality();
+ virtual int GetOutputLevel();
+
+ void readOutput(float*, int);
+ void writeInput(float*, int);
+
+protected:
+ void readBuffer(float*, float**, float*, float*, float*, int);
+ void writeBuffer(float*, float*, float**, float*, float*, int);
+
+private:
+ bool mute_;
+ bool play_;
+ PortAudioStream* stream_;
+
+ // Buffers
+ float *out_buffer_, *out_buffer_read_, *out_buffer_write_, *out_buffer_end_;
+ float *in_buffer_, *in_buffer_read_, *in_buffer_write_, *in_buffer_end_;
+
+ // Speex
+ SpeexBits speex_bits_;
+ void *speex_enc_state_, *speex_dec_state_;
+ float *speex_frame_;
+ int speex_frame_size_;
+
+ // ORTP
+ int rtp_socket_;
+ RtpSession* rtp_session_;
+ int rtp_timestamp_;
+};
+
+
+class PortAudioMediaEngine : public cricket::MediaEngine
+{
+public:
+ PortAudioMediaEngine();
+ ~PortAudioMediaEngine();
+ virtual bool Init();
+ virtual void Terminate();
+
+ virtual cricket::MediaChannel *CreateChannel();
+
+ virtual int SetAudioOptions(int options);
+ virtual int SetSoundDevices(int wave_in_device, int wave_out_device);
+
+ virtual int GetInputLevel();
+};
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc
new file mode 100644
index 00000000..58e1db60
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.cc
@@ -0,0 +1,331 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/session/phone/voicechannel.h"
+#include "talk/session/phone/channelmanager.h"
+#include "talk/session/phone/phonesessionclient.h"
+#include "talk/base/logging.h"
+#include <cassert>
+#undef SetPort
+
+namespace {
+
+// Delay before quality estimate is meaningful.
+uint32 kQualityDelay = 5000; // in ms
+
+}
+
+namespace cricket {
+
+VoiceChannel::VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel) {
+ channel_manager_ = manager;
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ channel_ = channel;
+ session_ = session;
+ socket_monitor_ = NULL;
+ audio_monitor_ = NULL;
+ socket_ = session_->CreateSocket("rtp");
+ socket_->SignalState.connect(this, &VoiceChannel::OnSocketState);
+ socket_->SignalReadPacket.connect(this, &VoiceChannel::OnSocketRead);
+ channel->SetInterface(this);
+ enabled_ = false;
+ paused_ = false;
+ socket_writable_ = false;
+ muted_ = false;
+ LOG(INFO) << "Created voice channel";
+ start_time_ = 0xFFFFFFFF - kQualityDelay;
+
+ session->SignalState.connect(this, &VoiceChannel::OnSessionState);
+ OnSessionState(session, session->state());
+}
+
+VoiceChannel::~VoiceChannel() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ enabled_ = false;
+ ChangeState();
+ delete socket_monitor_;
+ delete audio_monitor_;
+ Thread::Current()->Clear(this);
+ if (socket_ != NULL)
+ session_->DestroySocket(socket_);
+ LOG(INFO) << "Destroyed voice channel";
+}
+
+void VoiceChannel::OnMessage(Message *pmsg) {
+ switch (pmsg->message_id) {
+ case MSG_ENABLE:
+ EnableMedia_w();
+ break;
+
+ case MSG_DISABLE:
+ DisableMedia_w();
+ break;
+
+ case MSG_MUTE:
+ MuteMedia_w();
+ break;
+
+ case MSG_UNMUTE:
+ UnmuteMedia_w();
+ break;
+
+ case MSG_SETSENDCODEC:
+ SetSendCodec_w();
+ break;
+ }
+}
+
+void VoiceChannel::Enable(bool enable) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this, enable ? MSG_ENABLE : MSG_DISABLE);
+}
+
+void VoiceChannel::Mute(bool mute) {
+ // Can be called from thread other than worker thread
+ channel_manager_->worker_thread()->Post(this, mute ? MSG_MUTE : MSG_UNMUTE);
+}
+
+MediaChannel * VoiceChannel::channel() {
+ return channel_;
+}
+
+void VoiceChannel::OnSessionState(Session* session, Session::State state) {
+ if ((state == Session::STATE_RECEIVEDACCEPT) ||
+ (state == Session::STATE_RECEIVEDINITIATE)) {
+ channel_manager_->worker_thread()->Post(this, MSG_SETSENDCODEC);
+ }
+}
+
+void VoiceChannel::SetSendCodec_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+
+ const PhoneSessionDescription* desc =
+ static_cast<const PhoneSessionDescription*>(session()->remote_description());
+
+ const char *codec = NULL;
+
+ if (desc->codecs().size() > 0)
+ PhoneSessionClient::FindMediaCodec(channel_manager_->media_engine(), desc, &codec);
+
+ // The other client should have returned one of the codecs that we offered.
+ // If they could not, they should have rejected the session. So, if we get
+ // into this state, we're dealing with a bad client, so we may as well just
+ // pick the mostt common format there is: payload type zero.
+ if (codec == NULL)
+ codec = "PCMU";
+
+ channel_->SetCodec(codec);
+}
+
+void VoiceChannel::OnSocketState(P2PSocket *socket, P2PSocket::State state) {
+ switch (state) {
+ case P2PSocket::STATE_WRITABLE:
+ SocketWritable_w();
+ break;
+
+ default:
+ SocketNotWritable_w();
+ break;
+ }
+}
+
+void VoiceChannel::OnSocketRead(P2PSocket *socket, const char *data, size_t len) {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ // OnSocketRead gets called from P2PSocket; now pass data to MediaEngine
+ channel_->OnPacketReceived(data, (int)len);
+}
+
+void VoiceChannel::SendPacket(const void *data, size_t len) {
+ // SendPacket gets called from MediaEngine; send to socket
+ // MediaEngine will call us on a random thread. The Send operation on the socket is
+ // special in that it can handle this.
+ socket_->Send(static_cast<const char *>(data), len);
+}
+
+void VoiceChannel::ChangeState() {
+ if (paused_ || !enabled_ || !socket_writable_) {
+ channel_->SetPlayout(false);
+ channel_->SetSend(false);
+ } else {
+ if (muted_) {
+ channel_->SetSend(false);
+ channel_->SetPlayout(true);
+ } else {
+ channel_->SetSend(true);
+ channel_->SetPlayout(true);
+ }
+ }
+}
+
+void VoiceChannel::PauseMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ assert(!paused_);
+
+ LOG(INFO) << "Voice channel paused";
+ paused_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnpauseMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ assert(paused_);
+
+ LOG(INFO) << "Voice channel unpaused";
+ paused_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::EnableMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel enabled";
+ enabled_ = true;
+ start_time_ = Time();
+ ChangeState();
+}
+
+void VoiceChannel::DisableMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (!enabled_)
+ return;
+
+ LOG(INFO) << "Voice channel disabled";
+ enabled_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::MuteMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (muted_)
+ return;
+
+ LOG(INFO) << "Voice channel muted";
+ muted_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::UnmuteMedia_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (!muted_)
+ return;
+
+ LOG(INFO) << "Voice channel unmuted";
+ muted_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::SocketWritable_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (socket_writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket writable";
+ socket_writable_ = true;
+ ChangeState();
+}
+
+void VoiceChannel::SocketNotWritable_w() {
+ assert(channel_manager_->worker_thread() == Thread::Current());
+ if (!socket_writable_)
+ return;
+
+ LOG(INFO) << "Voice channel socket not writable";
+ socket_writable_ = false;
+ ChangeState();
+}
+
+void VoiceChannel::StartConnectionMonitor(int cms) {
+ delete socket_monitor_;
+ socket_monitor_ = new SocketMonitor(socket_, Thread::Current());
+ socket_monitor_
+ ->SignalUpdate.connect(this, &VoiceChannel::OnConnectionMonitorUpdate);
+ socket_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopConnectionMonitor() {
+ if (socket_monitor_ != NULL) {
+ socket_monitor_->Stop();
+ socket_monitor_->SignalUpdate.disconnect(this);
+ delete socket_monitor_;
+ socket_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnConnectionMonitorUpdate(SocketMonitor *monitor,
+ const std::vector<ConnectionInfo> &infos) {
+ SignalConnectionMonitor(this, infos);
+}
+
+void VoiceChannel::StartAudioMonitor(int cms) {
+ delete audio_monitor_;
+ audio_monitor_ = new AudioMonitor(this, Thread::Current());
+ audio_monitor_
+ ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate);
+ audio_monitor_->Start(cms);
+}
+
+void VoiceChannel::StopAudioMonitor() {
+ if (audio_monitor_ != NULL) {
+ audio_monitor_ ->Stop();
+ audio_monitor_ ->SignalUpdate.disconnect(this);
+ delete audio_monitor_ ;
+ audio_monitor_ = NULL;
+ }
+}
+
+void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor *monitor,
+ const AudioInfo& info) {
+ SignalAudioMonitor(this, info);
+}
+
+Session *VoiceChannel::session() {
+ return session_;
+}
+
+bool VoiceChannel::HasQuality() {
+ return Time() >= start_time_ + kQualityDelay;
+}
+
+float VoiceChannel::GetCurrentQuality() {
+ return channel_->GetCurrentQuality();
+}
+
+int VoiceChannel::GetInputLevel_w() {
+ return channel_manager_->media_engine()->GetInputLevel();
+}
+
+int VoiceChannel::GetOutputLevel_w() {
+ return channel_->GetOutputLevel();
+}
+
+Thread* VoiceChannel::worker_thread() {
+ return channel_manager_->worker_thread();
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h
new file mode 100644
index 00000000..4cfa0b11
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/phone/voicechannel.h
@@ -0,0 +1,129 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _VOICECHANNEL_H_
+#define _VOICECHANNEL_H_
+
+#include "talk/base/asyncudpsocket.h"
+#include "talk/base/network.h"
+#include "talk/base/sigslot.h"
+#include "talk/p2p/client/socketmonitor.h"
+#include "talk/p2p/base/p2psocket.h"
+#include "talk/p2p/base/session.h"
+#include "talk/session/phone/audiomonitor.h"
+#include "talk/session/phone/mediaengine.h"
+
+namespace cricket {
+
+const uint32 MSG_ENABLE = 1;
+const uint32 MSG_DISABLE = 2;
+const uint32 MSG_MUTE = 3;
+const uint32 MSG_UNMUTE = 4;
+const uint32 MSG_SETSENDCODEC = 5;
+
+class ChannelManager;
+
+class VoiceChannel
+ : public MessageHandler, public sigslot::has_slots<>,
+ public NetworkSession, public MediaChannel::NetworkInterface {
+ public:
+ VoiceChannel(ChannelManager *manager, Session *session, MediaChannel *channel);
+ ~VoiceChannel();
+
+ void Enable(bool enable);
+ void Mute(bool mute);
+ MediaChannel *channel();
+ Session *session();
+
+ // Monitoring
+
+ void StartConnectionMonitor(int cms);
+ void StopConnectionMonitor();
+ sigslot::signal2<VoiceChannel *, const std::vector<ConnectionInfo> &> SignalConnectionMonitor;
+
+ void StartAudioMonitor(int cms);
+ void StopAudioMonitor();
+ sigslot::signal2<VoiceChannel *, const AudioInfo&> SignalAudioMonitor;
+ Thread* worker_thread();
+
+ // Pausing so that the ChannelManager can change the audio devices. These
+ // should only be called from the worker thread
+ void PauseMedia_w();
+ void UnpauseMedia_w();
+
+ int GetInputLevel_w();
+ int GetOutputLevel_w();
+
+ // Gives a quality estimate to the network quality manager.
+ virtual bool HasQuality();
+ virtual float GetCurrentQuality();
+
+ // MediaEngine calls this
+ virtual void SendPacket(const void *data, size_t len);
+
+private:
+ void ChangeState();
+ void EnableMedia_w();
+ void DisableMedia_w();
+ void MuteMedia_w();
+ void UnmuteMedia_w();
+ void SocketWritable_w();
+ void SocketNotWritable_w();
+
+ void OnConnectionMonitorUpdate(SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos);
+ void OnAudioMonitorUpdate(AudioMonitor *monitor, const AudioInfo& info);
+
+ // From MessageHandler
+
+ void OnMessage(Message *pmsg);
+
+ // Setting the send codec based on the remote description.
+ void OnSessionState(Session* session, Session::State state);
+ void SetSendCodec_w();
+
+ // From P2PSocket
+
+ void OnSocketState(P2PSocket *socket, P2PSocket::State state);
+ void OnSocketRead(P2PSocket *socket, const char *data, size_t len);
+
+
+ bool enabled_;
+ bool paused_;
+ bool socket_writable_;
+ bool muted_;
+ MediaChannel *channel_;
+ Session *session_;
+ P2PSocket *socket_;
+ ChannelManager *channel_manager_;
+ SocketMonitor *socket_monitor_;
+ AudioMonitor *audio_monitor_;
+ uint32 start_time_;
+};
+
+}
+
+#endif // _VOICECHANNEL_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h
new file mode 100644
index 00000000..a5326893
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/receiver.h
@@ -0,0 +1,72 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RECEIVER_H_
+#define _RECEIVER_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/p2p/client/sessionclient.h"
+
+namespace cricket {
+
+class Receiver : public buzz::XmppTask {
+public:
+ Receiver(Task *parent, SessionClient *session_client)
+ : buzz::XmppTask(parent, buzz::XmppEngine::HL_TYPE) {
+ session_client_ = session_client;
+ }
+
+ virtual int ProcessStart() {
+ const buzz::XmlElement *stanza = NextStanza();
+ if (stanza == NULL)
+ return STATE_BLOCKED;
+ session_client_->OnIncomingStanza(stanza);
+
+ // Respond right away to the sender to let them know that we received
+ // this IQ
+ buzz::XmlElement * result = MakeIqResult(stanza);
+ SendStanza(result);
+
+ return STATE_START;
+ }
+
+protected:
+ virtual bool HandleStanza(const buzz::XmlElement *stanza) {
+ if (!session_client_->IsClientStanza(stanza))
+ return false;
+ QueueStanza(stanza);
+ return true;
+ }
+
+private:
+ SessionClient *session_client_;
+};
+
+}
+
+#endif // _RECEIVER_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h b/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h
new file mode 100644
index 00000000..9dc5384c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/session/sessionsendtask.h
@@ -0,0 +1,111 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_PHONE_SESSIONSENDTASK_H_
+#define _CRICKET_PHONE_SESSIONSENDTASK_H_
+
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmpptask.h"
+#include "talk/p2p/client/sessionclient.h"
+
+namespace cricket {
+
+// The job of this task is to send an IQ stanza out (after stamping it with
+// an ID attribute) and then wait for a response. If not response happens
+// within 5 seconds, it will signal failure on a SessionClient. If an error
+// happens it will also signal failure. If, however, the send succeeds this
+// task will quietly go away.
+
+// It is safe for this to hold on to the session client. In the case where
+// the xmpp client goes away, this task will automatically be aborted. The
+// session_client is guaranteed to outlive the xmpp session.
+class SessionSendTask : public buzz::XmppTask {
+public:
+ SessionSendTask(Task *parent, SessionClient *session_client)
+ : buzz::XmppTask(parent, buzz::XmppEngine::HL_SINGLE),
+ session_client_(session_client),
+ timed_out_(false) {
+ }
+
+ void Send(const buzz::XmlElement* stanza) {
+ assert(stanza_.get() == NULL);
+ stanza_.reset(new buzz::XmlElement(*stanza));
+ stanza_->SetAttr(buzz::QN_ID, task_id());
+ }
+
+protected:
+ // This gets called by the task runner every 500 msec
+ virtual void Poll() {
+ if (ElapsedTime() > (15 * 1000 * 10000)) { // 15 secs
+ timed_out_ = true;
+ Wake();
+ }
+ }
+
+ virtual int ProcessStart() {
+ SendStanza(stanza_.get());
+ return STATE_RESPONSE;
+ }
+
+ virtual int ProcessResponse() {
+ if (timed_out_) {
+ session_client_->OnFailedSend(stanza_.get(), NULL);
+ return STATE_DONE;
+ }
+
+ const buzz::XmlElement* next = NextStanza();
+ if (next == NULL)
+ return STATE_BLOCKED;
+
+ if (next->Attr(buzz::QN_TYPE) == "result") {
+ return STATE_DONE;
+ } else {
+ session_client_->OnFailedSend(stanza_.get(), next);
+ return STATE_DONE;
+ }
+ }
+
+ virtual bool HandleStanza(const buzz::XmlElement *stanza) {
+ if (!MatchResponseIq(stanza, buzz::Jid(stanza_->Attr(buzz::QN_TO)), task_id()))
+ return false;
+ if (stanza->Attr(buzz::QN_TYPE) == "result" ||
+ stanza->Attr(buzz::QN_TYPE) == "error") {
+ QueueStanza(stanza);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ SessionClient *session_client_;
+ buzz::scoped_ptr<buzz::XmlElement> stanza_;
+ bool timed_out_;
+};
+
+}
+
+#endif // _CRICKET_PHONE_SESSIONSENDTASK_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am
new file mode 100644
index 00000000..3186245a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS=mediastreamer
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am
new file mode 100644
index 00000000..268a52fe
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.am
@@ -0,0 +1,92 @@
+EXTRA_DIST=Makefile.ms
+noinst_LTLIBRARIES = libmediastreamer.la
+libmediastreamer_la_SOURCES=msfilter.c msfilter.h msutils.h waveheader.h\
+ mscodec.c mscodec.h \
+ mssoundread.c mssoundread.h \
+ mssoundwrite.c mssoundwrite.h \
+ msbuffer.c msbuffer.h \
+ msqueue.c msqueue.h \
+ msfifo.c msfifo.h \
+ ms.c ms.h\
+ mssync.c mssync.h \
+ msnosync.c msnosync.h \
+ msread.c msread.h \
+ mswrite.c mswrite.h \
+ mscopy.c mscopy.h \
+ msosswrite.c msosswrite.h \
+ msossread.c msossread.h \
+ msringplayer.c msringplayer.h \
+ msrtprecv.c msrtprecv.h \
+ msrtpsend.c msrtpsend.h \
+ msAlawenc.c msAlawenc.h g711common.h \
+ msAlawdec.c msAlawdec.h g711common.h \
+ msMUlawenc.c msMUlawenc.h g711common.h \
+ msMUlawdec.c msMUlawdec.h g711common.h \
+ mstimer.c mstimer.h \
+ msqdispatcher.c msqdispatcher.h \
+ msfdispatcher.c msfdispatcher.h \
+ sndcard.c sndcard.h \
+ osscard.c osscard.h\
+ hpuxsndcard.c \
+ alsacard.c alsacard.h \
+ jackcard.c jackcard.h \
+ audiostream.c mediastream.h \
+ msspeexenc.c msspeexenc.h msspeexdec.c msspeexdec.h \
+ msilbcdec.c msilbcdec.h msilbcenc.c msilbcenc.h
+
+noinst_HEADERS = affine.h \
+ msAlawenc.h \
+ msfdispatcher.h \
+ msilbcdec.h \
+ msnosync.h \
+ msringplayer.h \
+ msspeexdec.h \
+ msutils.h \
+ waveheader.h \
+ alsacard.h \
+ msavdecoder.h \
+ msfifo.h \
+ msilbcenc.h \
+ msossread.h \
+ msrtprecv.h \
+ msspeexenc.h \
+ msv4l.h \
+ g711common.h \
+ msavencoder.h \
+ msfilter.h \
+ msLPC10decoder.h \
+ msosswrite.h \
+ msrtpsend.h \
+ mssync.h \
+ msvideosource.h \
+ jackcard.h \
+ msbuffer.h \
+ msGSMdecoder.h \
+ msLPC10encoder.h \
+ msqdispatcher.h \
+ mssdlout.h \
+ mstimer.h \
+ mswrite.h \
+ mediastream.h \
+ mscodec.h \
+ msGSMencoder.h \
+ msMUlawdec.h \
+ msqueue.h \
+ mssoundread.h \
+ mstruespeechdecoder.h \
+ osscard.h \
+ msAlawdec.h \
+ mscopy.h \
+ ms.h \
+ msMUlawenc.h \
+ msread.h \
+ mssoundwrite.h \
+ mstruespeechencoder.h \
+ sndcard.h
+
+
+libmediastreamer_la_LIBADD= $(GLIB_LIBS) $(ORTP_LIBS) $(SPEEX_LIBS)
+
+AM_CFLAGS=$(GLIB_CFLAGS) -DG_LOG_DOMAIN=\"MediaStreamer\" $(ORTP_CFLAGS) $(IPV6_CFLAGS) $(ILBC_CFLAGS) $(SPEEX_CFLAGS)
+
+INCLUDES= -I$(srcdir)/../../.. $(ORTP_CFLAGS)
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms
new file mode 100644
index 00000000..8b7427c3
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/Makefile.ms
@@ -0,0 +1,34 @@
+
+OBJEXT=o
+AR = ar
+RANLIB = ranlib
+DEFS= -DG_LOG_DOMAIN=\"MediaStreamer\"
+INCLUDES=-I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include/ \
+ -I../gsmlib/ -I../lpc10-1.5 -I../oRTP
+COMPILE= gcc $(DEFS) $(INCLUDES)
+LIBTOOL=libtool
+LDFLAGS=-L/usr/local/lib/ -lglib-1.3 -lgthread-1.3 -lpthread
+LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+
+libmediastreamer_a_OBJECTS = msfilter.$(OBJEXT) msbuffer.$(OBJEXT) \
+msqueue.$(OBJEXT) msfifo.$(OBJEXT) ms.$(OBJEXT) mssync.$(OBJEXT) \
+msnosync.$(OBJEXT) msread.$(OBJEXT) mswrite.$(OBJEXT) mscopy.$(OBJEXT) \
+msv4lsource.$(OBJEXT) msoss.$(OBJEXT) msosswrite.$(OBJEXT) \
+msossread.$(OBJEXT) msringplayer.$(OBJEXT) msGSMencoder.$(OBJEXT) \
+msGSMdecoder.$(OBJEXT) msLPC10encoder.$(OBJEXT) \
+msLPC10decoder.$(OBJEXT)
+
+all: libmediastreamer.a mstest
+
+
+.c.o:
+ $(COMPILE) -c $<
+
+libmediastreamer.a: $(libmediastreamer_a_OBJECTS)
+ -rm -f libmediastreamer.a
+ $(AR) cru libmediastreamer.a $(libmediastreamer_a_OBJECTS)
+ $(RANLIB) libmediastreamer.a
+
+
+mstest: test.o libmediastreamer.a
+ gcc -o mstest test.o libmediastreamer.a $(LDFLAGS) -Wl,-rpath /usr/local/lib
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README
new file mode 100644
index 00000000..1309f534
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/README
@@ -0,0 +1,3 @@
+Mediastreamer is the library that handle all media operations: rtp streaming
+from file, from soundcard, with codec transcoding, and vice-versa;-).
+And also video streaming in the future.
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h
new file mode 100644
index 00000000..620fdc9d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/affine.h
@@ -0,0 +1,43 @@
+/*
+ * affine.h -- Affine Transforms for 2d objects
+ * Copyright (C) 2002 Charles Yates <charles.yates@pandora.be>
+ * Portions Copyright (C) 2003 Dan Dennedy <dan@dennedy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _AFFINE_H
+#define _AFFINE_H
+
+#include <math.h>
+
+/** Affine transforms for 2d image manipulation. Current provides shearing and
+ rotating support.
+*/
+
+typedef struct {
+ double matrix[2][2];
+} affine_transform_t;
+
+void affine_transform_init( affine_transform_t *this );
+void affine_transform_rotate( affine_transform_t *this, double angle );
+void affine_transform_shear( affine_transform_t *this, double shear );
+void affine_transform_scale( affine_transform_t *this, double sx, double sy );
+double affine_transform_mapx( affine_transform_t *this, int x, int y );
+double affine_transform_mapy( affine_transform_t *this, int x, int y );
+void affine_scale( const unsigned char *src, unsigned char *dest, int src_width, int src_height, int dest_width, int dest_height, int bpp );
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c
new file mode 100644
index 00000000..c240aa72
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.c
@@ -0,0 +1,640 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "alsacard.h"
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+
+static gchar *over_pcmdev=NULL;
+
+#include "msossread.h"
+#include "msosswrite.h"
+
+#include <signal.h>
+
+int __alsa_card_write(AlsaCard *obj,char *buf,int size);
+
+int alsa_set_params(AlsaCard *obj, int rw, int bits, int stereo, int rate)
+{
+ snd_pcm_hw_params_t *hwparams=NULL;
+ snd_pcm_sw_params_t *swparams=NULL;
+ snd_pcm_t *pcm_handle;
+ gint dir,exact_value;
+ gint channels;
+ gint fsize=0;
+ gint periods=8;
+ gint periodsize=256;
+ gint err;
+ int format;
+
+ if (rw) {
+ pcm_handle=obj->write_handle;
+ }
+ else pcm_handle=obj->read_handle;
+
+ /* Allocate the snd_pcm_hw_params_t structure on the stack. */
+ snd_pcm_hw_params_alloca(&hwparams);
+
+ /* Init hwparams with full configuration space */
+ if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
+ g_warning("alsa_set_params: Cannot configure this PCM device.\n");
+ return(-1);
+ }
+
+ if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
+ g_warning("alsa_set_params: Error setting access.\n");
+ return(-1);
+ }
+ /* Set sample format */
+#ifdef WORDS_BIGENDIAN
+ format=SND_PCM_FORMAT_S16_BE;
+#else
+ format=SND_PCM_FORMAT_S16_LE;
+#endif
+ if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) {
+ g_warning("alsa_set_params: Error setting format.\n");
+ return(-1);
+ }
+ /* Set number of channels */
+ if (stereo) channels=2;
+ else channels=1;
+ if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) {
+ g_warning("alsa_set_params: Error setting channels.\n");
+ return(-1);
+ }
+ /* Set sample rate. If the exact rate is not supported */
+ /* by the hardware, use nearest possible rate. */
+ exact_value=rate;
+ dir=0;
+ if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_value, &dir))<0){
+ g_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err));
+ return -1;
+ }
+ if (dir != 0) {
+ g_warning("alsa_set_params: The rate %d Hz is not supported by your hardware.\n "
+ "==> Using %d Hz instead.\n", rate, exact_value);
+ }
+ /* choose greater period size when rate is high */
+ periodsize=periodsize*(rate/8000);
+
+ /* Set buffer size (in frames). The resulting latency is given by */
+ /* latency = periodsize * periods / (rate * bytes_per_frame) */
+ /*
+ fsize=periodsize * periods;
+ exact_value=fsize;
+ if ((err=snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams,&exact_value)) < 0) {
+ g_warning("alsa_set_params: Error setting buffer size:%s",snd_strerror(err));
+ return(-1);
+ }
+ if (fsize!= exact_value) {
+ g_warning("alsa_set_params: The buffer size %d is not supported by your hardware.\n "
+ "==> Using %d instead.\n", fsize, exact_value);
+ }
+ */
+ /* set period size */
+ exact_value=periodsize;
+ dir=0;
+ if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_value, &dir) < 0) {
+ g_warning("alsa_set_params: Error setting period size.\n");
+ return(-1);
+ }
+ if (dir != 0) {
+ g_warning("alsa_set_params: The period size %d is not supported by your hardware.\n "
+ "==> Using %d instead.\n", periodsize, exact_value);
+ }
+ periodsize=exact_value;
+ /* Set number of periods. Periods used to be called fragments. */
+ exact_value=periods;
+ dir=0;
+ if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_value, &dir) < 0) {
+ g_warning("alsa_set_params: Error setting periods.\n");
+ return(-1);
+ }
+ if (dir != 0) {
+ g_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n "
+ "==> Using %d instead.\n", periods, exact_value);
+ }
+ /* Apply HW parameter settings to */
+ /* PCM device and prepare device */
+ if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
+ g_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err));
+ return(-1);
+ }
+ /*prepare sw params */
+ if (rw){
+ snd_pcm_sw_params_alloca(&swparams);
+ snd_pcm_sw_params_current(pcm_handle, swparams);
+ if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){
+ g_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err));
+ return -1;
+ }
+ if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){
+ g_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err));
+ return(-1);
+ }
+ }
+ obj->frame_size=channels*(bits/8);
+ SND_CARD(obj)->bsize=periodsize*obj->frame_size;
+ /* //SND_CARD(obj)->bsize=4096; */
+ obj->frames=periodsize;
+ g_message("alsa_set_params: blocksize=%i.",SND_CARD(obj)->bsize);
+ return SND_CARD(obj)->bsize;
+}
+
+int alsa_card_open_r(AlsaCard *obj,int bits,int stereo,int rate)
+{
+ int bsize;
+ int err;
+ snd_pcm_t *pcm_handle;
+ gchar *pcmdev;
+ if (over_pcmdev!=NULL) pcmdev=over_pcmdev;
+ else pcmdev=obj->pcmdev;
+
+ if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) {
+ g_warning("alsa_card_open_r: Error opening PCM device %s\n",obj->pcmdev );
+ return -1;
+ }
+ g_return_val_if_fail(pcm_handle!=NULL,-1);
+ obj->read_handle=pcm_handle;
+ if ((bsize=alsa_set_params(obj,0,bits,stereo,rate))<0){
+ snd_pcm_close(pcm_handle);
+ obj->read_handle=NULL;
+ return -1;
+ }
+ obj->readbuf=g_malloc0(bsize);
+
+ err=snd_pcm_start(obj->read_handle);
+ if (err<0){
+ g_warning("Cannot start read pcm: %s", snd_strerror(err));
+ }
+ obj->readpos=0;
+ SND_CARD(obj)->bsize=bsize;
+ SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED;
+ return 0;
+}
+
+int alsa_card_open_w(AlsaCard *obj,int bits,int stereo,int rate)
+{
+ int err,bsize;
+ snd_pcm_t *pcm_handle;
+ gchar *pcmdev;
+ if (over_pcmdev!=NULL) pcmdev=over_pcmdev;
+ else pcmdev=obj->pcmdev;
+
+ if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) {
+ g_warning("alsa_card_open_w: Error opening PCM device %s\n", obj->pcmdev);
+ return -1;
+ }
+ obj->write_handle=pcm_handle;
+ if ((bsize=alsa_set_params(obj,1,bits,stereo,rate))<0){
+ snd_pcm_close(pcm_handle);
+ obj->write_handle=NULL;
+ return -1;
+ }
+ obj->writebuf=g_malloc0(bsize);
+
+ obj->writepos=0;
+ SND_CARD(obj)->bsize=bsize;
+ SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED;
+ return 0;
+}
+
+
+void alsa_card_set_blocking_mode(AlsaCard *obj, gboolean yesno){
+ if (obj->read_handle!=NULL) snd_pcm_nonblock(obj->read_handle,!yesno);
+ if (obj->write_handle!=NULL) snd_pcm_nonblock(obj->write_handle,!yesno);
+}
+
+void alsa_card_close_r(AlsaCard *obj)
+{
+ if (obj->read_handle!=NULL){
+ snd_pcm_close(obj->read_handle);
+ obj->read_handle=NULL;
+ g_free(obj->readbuf);
+ obj->readbuf=NULL;
+ }
+}
+
+void alsa_card_close_w(AlsaCard *obj)
+{
+ if (obj->write_handle!=NULL){
+ snd_pcm_close(obj->write_handle);
+ obj->write_handle=NULL;
+ g_free(obj->writebuf);
+ obj->writebuf=NULL;
+ }
+}
+
+int alsa_card_probe(AlsaCard *obj,int bits,int stereo,int rate)
+{
+ int ret;
+ ret=alsa_card_open_w(obj,bits,stereo,rate);
+ if (ret<0) return -1;
+ ret=SND_CARD(obj)->bsize;
+ alsa_card_close_w(obj);
+ return ret;
+}
+
+
+void alsa_card_destroy(AlsaCard *obj)
+{
+ snd_card_uninit(SND_CARD(obj));
+ g_free(obj->pcmdev);
+ if (obj->readbuf!=0) g_free(obj->readbuf);
+ if (obj->writebuf!=0) g_free(obj->writebuf);
+}
+
+gboolean alsa_card_can_read(AlsaCard *obj)
+{
+ int frames;
+ g_return_val_if_fail(obj->read_handle!=NULL,0);
+ if (obj->readpos!=0) return TRUE;
+ if ( frames=snd_pcm_avail_update(obj->read_handle)>=obj->frames) return 1;
+ /* //g_message("frames=%i",frames); */
+ return 0;
+}
+
+
+
+int __alsa_card_read(AlsaCard *obj,char *buf,int bsize)
+{
+ int err;
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set,SIGALRM);
+ sigprocmask(SIG_BLOCK,&set,NULL);
+ err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size);
+ if (err<0) {
+ if (err!=-EPIPE){
+ g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
+ }
+ snd_pcm_prepare(obj->read_handle);
+ err=snd_pcm_readi(obj->read_handle,buf,bsize/obj->frame_size);
+ if (err<0) g_warning("alsa_card_read: snd_pcm_readi() failed:%s.",snd_strerror(err));
+ }
+ sigprocmask(SIG_UNBLOCK,&set,NULL);
+ return err*obj->frame_size;
+}
+
+int alsa_card_read(AlsaCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+ g_return_val_if_fail(obj->read_handle!=NULL,-1);
+ if (size<bsize){
+ gint canread=MIN(bsize-obj->readpos,size);
+
+ if (obj->readpos==0){
+ err=__alsa_card_read(obj,obj->readbuf,bsize);
+ }
+
+ memcpy(buf,&obj->readbuf[obj->readpos],canread);
+ obj->readpos+=canread;
+ if (obj->readpos>=bsize) obj->readpos=0;
+ return canread;
+ }else{
+ err=__alsa_card_read(obj,buf,size);
+ return err;
+ }
+
+}
+
+int __alsa_card_write(AlsaCard *obj,char *buf,int size)
+{
+ int err;
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set,SIGALRM);
+ sigprocmask(SIG_BLOCK,&set,NULL);
+ if ((err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size))<0){
+ if (err!=-EPIPE){
+ g_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err));
+ }
+ snd_pcm_prepare(obj->write_handle);
+ err=snd_pcm_writei(obj->write_handle,buf,size/obj->frame_size);
+ if (err<0) g_warning("alsa_card_write: Error writing sound buffer (size=%i):%s",size,snd_strerror(err));
+
+ }
+ sigprocmask(SIG_UNBLOCK,&set,NULL);
+ return err;
+}
+
+int alsa_card_write(AlsaCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+ g_return_val_if_fail(obj->write_handle!=NULL,-1);
+ if (size<bsize){
+ gint canwrite;
+
+ canwrite=MIN(bsize-obj->writepos,size);
+ memcpy(&obj->writebuf[obj->writepos],buf,canwrite);
+ obj->writepos+=canwrite;
+ if (obj->writepos>=bsize){
+ err=__alsa_card_write(obj,obj->writebuf,bsize);
+ obj->writepos=0;
+ }
+ return canwrite;
+ }else{
+ return __alsa_card_write(obj,buf,bsize);
+ }
+}
+
+snd_mixer_t *alsa_mixer_open(AlsaCard *obj){
+ snd_mixer_t *mixer=NULL;
+ int err;
+ err=snd_mixer_open(&mixer,0);
+ if (err<0){
+ g_warning("Could not open alsa mixer: %s",snd_strerror(err));
+ return NULL;
+ }
+ if ((err = snd_mixer_attach (mixer, obj->mixdev)) < 0){
+ g_warning("Could not attach mixer to card: %s",snd_strerror(err));
+ snd_mixer_close(mixer);
+ return NULL;
+ }
+ if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){
+ g_warning("snd_mixer_selem_register: %s",snd_strerror(err));
+ snd_mixer_close(mixer);
+ return NULL;
+ }
+ if ((err = snd_mixer_load (mixer)) < 0){
+ g_warning("snd_mixer_load: %s",snd_strerror(err));
+ snd_mixer_close(mixer);
+ return NULL;
+ }
+ obj->mixer=mixer;
+ return mixer;
+}
+
+void alsa_mixer_close(AlsaCard *obj){
+ snd_mixer_close(obj->mixer);
+ obj->mixer=NULL;
+}
+
+typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction;
+
+static gint get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){
+ long value=0;
+ const char *elemname;
+ snd_mixer_elem_t *elem;
+ int err;
+ long sndMixerPMin;
+ long sndMixerPMax;
+ long newvol;
+ elem=snd_mixer_first_elem(mixer);
+ while (elem!=NULL){
+ elemname=snd_mixer_selem_get_name(elem);
+ /* //g_message("Found alsa mixer element %s.",elemname); */
+ if (strcmp(elemname,name)==0){
+ switch (action){
+ case CAPTURE:
+ if (snd_mixer_selem_has_capture_volume(elem)){
+ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
+ err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol);
+ newvol-=sndMixerPMin;
+ value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
+ if (err<0) g_warning("Could not get capture volume for %s:%s",name,snd_strerror(err));
+ /* //else g_message("Succesfully get capture level for %s.",elemname); */
+ break;
+ }
+ break;
+ case PLAYBACK:
+ if (snd_mixer_selem_has_playback_volume(elem)){
+ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
+ err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol);
+ newvol-=sndMixerPMin;
+ value=(100*newvol)/(sndMixerPMax-sndMixerPMin);
+ if (err<0) g_warning("Could not get playback volume for %s:%s",name,snd_strerror(err));
+ /* //else g_message("Succesfully get playback level for %s.",elemname); */
+ break;
+ }
+ break;
+ case CAPTURE_SWITCH:
+
+ break;
+ }
+ }
+ elem=snd_mixer_elem_next(elem);
+ }
+
+ return value;
+}
+
+
+static void set_mixer_element(snd_mixer_t *mixer,const char *name, gint level,MixerAction action){
+ const char *elemname;
+ snd_mixer_elem_t *elem;
+ int tmp;
+ long sndMixerPMin;
+ long sndMixerPMax;
+ long newvol;
+
+ elem=snd_mixer_first_elem(mixer);
+
+ while (elem!=NULL){
+ elemname=snd_mixer_selem_get_name(elem);
+ /* //g_message("Found alsa mixer element %s.",elemname); */
+ if (strcmp(elemname,name)==0){
+ switch(action){
+ case CAPTURE:
+ if (snd_mixer_selem_has_capture_volume(elem)){
+ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
+ newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
+ snd_mixer_selem_set_capture_volume_all(elem,newvol);
+ /* //g_message("Succesfully set capture level for %s.",elemname); */
+ return;
+ }
+ break;
+ case PLAYBACK:
+ if (snd_mixer_selem_has_playback_volume(elem)){
+ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax);
+ newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin;
+ snd_mixer_selem_set_playback_volume_all(elem,newvol);
+ /* //g_message("Succesfully set playback level for %s.",elemname); */
+ return;
+ }
+ break;
+ case CAPTURE_SWITCH:
+ if (snd_mixer_selem_has_capture_switch(elem)){
+ snd_mixer_selem_set_capture_switch_all(elem,level);
+ /* //g_message("Succesfully set capture switch for %s.",elemname); */
+ }
+ break;
+ case PLAYBACK_SWITCH:
+ if (snd_mixer_selem_has_playback_switch(elem)){
+ snd_mixer_selem_set_playback_switch_all(elem,level);
+ /* //g_message("Succesfully set capture switch for %s.",elemname); */
+ }
+ break;
+
+ }
+ }
+ elem=snd_mixer_elem_next(elem);
+ }
+
+ return ;
+}
+
+
+void alsa_card_set_level(AlsaCard *obj,gint way,gint a)
+{
+ snd_mixer_t *mixer;
+ mixer=alsa_mixer_open(obj);
+ if (mixer==NULL) return ;
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ set_mixer_element(mixer,"Master",a,PLAYBACK);
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ set_mixer_element(mixer,"Capture",a,CAPTURE);
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ set_mixer_element(mixer,"PCM",a,PLAYBACK);
+ break;
+ default:
+ g_warning("oss_card_set_level: unsupported command.");
+ }
+ alsa_mixer_close(obj);
+}
+
+gint alsa_card_get_level(AlsaCard *obj,gint way)
+{
+ snd_mixer_t *mixer;
+ gint value;
+ mixer=alsa_mixer_open(obj);
+ if (mixer==NULL) return 0;
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ value=get_mixer_element(mixer,"Master",PLAYBACK);
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ value=get_mixer_element(mixer,"Capture",CAPTURE);
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ value=get_mixer_element(mixer,"PCM",PLAYBACK);
+ break;
+ default:
+ g_warning("oss_card_set_level: unsupported command.");
+ }
+ alsa_mixer_close(obj);
+ return value;
+}
+
+void alsa_card_set_source(AlsaCard *obj,int source)
+{
+ snd_mixer_t *mixer;
+ mixer=alsa_mixer_open(obj);
+ if (mixer==NULL) return;
+ switch (source){
+ case 'm':
+ set_mixer_element(mixer,"Mic",1,CAPTURE_SWITCH);
+ set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
+ break;
+ case 'l':
+ set_mixer_element(mixer,"Line",1,CAPTURE_SWITCH);
+ set_mixer_element(mixer,"Capture",1,CAPTURE_SWITCH);
+ break;
+ }
+}
+
+MSFilter *alsa_card_create_read_filter(AlsaCard *card)
+{
+ MSFilter *f=ms_oss_read_new();
+ ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
+ return f;
+}
+
+MSFilter *alsa_card_create_write_filter(AlsaCard *card)
+{
+ MSFilter *f=ms_oss_write_new();
+ ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
+ return f;
+}
+
+
+SndCard * alsa_card_new(gint devid)
+{
+ AlsaCard * obj;
+ SndCard *base;
+ int err;
+ gchar *name=NULL;
+
+ /* carefull: this is an alsalib call despite its name! */
+ err=snd_card_get_name(devid,&name);
+ if (err<0) {
+ return NULL;
+ }
+ obj= g_new0(AlsaCard,1);
+ base= SND_CARD(obj);
+ snd_card_init(base);
+
+ base->card_name=g_strdup_printf("%s (Advanced Linux Sound Architecture)",name);
+ base->_probe=(SndCardOpenFunc)alsa_card_probe;
+ base->_open_r=(SndCardOpenFunc)alsa_card_open_r;
+ base->_open_w=(SndCardOpenFunc)alsa_card_open_w;
+ base->_can_read=(SndCardPollFunc)alsa_card_can_read;
+ base->_set_blocking_mode=(SndCardSetBlockingModeFunc)alsa_card_set_blocking_mode;
+ base->_read=(SndCardIOFunc)alsa_card_read;
+ base->_write=(SndCardIOFunc)alsa_card_write;
+ base->_close_r=(SndCardCloseFunc)alsa_card_close_r;
+ base->_close_w=(SndCardCloseFunc)alsa_card_close_w;
+ base->_set_rec_source=(SndCardMixerSetRecSourceFunc)alsa_card_set_source;
+ base->_set_level=(SndCardMixerSetLevelFunc)alsa_card_set_level;
+ base->_get_level=(SndCardMixerGetLevelFunc)alsa_card_get_level;
+ base->_destroy=(SndCardDestroyFunc)alsa_card_destroy;
+ base->_create_read_filter=(SndCardCreateFilterFunc)alsa_card_create_read_filter;
+ base->_create_write_filter=(SndCardCreateFilterFunc)alsa_card_create_write_filter;
+
+
+ obj->pcmdev=g_strdup_printf("plughw:%i,0",devid);
+ obj->mixdev=g_strdup_printf("hw:%i",devid);
+ obj->readbuf=NULL;
+ obj->writebuf=NULL;
+ return base;
+}
+
+
+gint alsa_card_manager_init(SndCardManager *m, gint index)
+{
+ gint devindex;
+ gint i;
+ gint found=0;
+ gchar *name=NULL;
+ for(devindex=0;index<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){
+ if (snd_card_get_name(devindex,&name)==0){
+ g_message("Found ALSA device: %s",name);
+ m->cards[index]=alsa_card_new(devindex);
+ m->cards[index]->index=index;
+ found++;
+ index++;
+ }
+ }
+ return found;
+}
+
+void alsa_card_manager_set_default_pcm_device(const gchar *pcmdev){
+ if (over_pcmdev!=NULL){
+ g_free(over_pcmdev);
+ }
+ over_pcmdev=g_strdup(pcmdev);
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h
new file mode 100644
index 00000000..df3372fb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/alsacard.h
@@ -0,0 +1,50 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <config.h>
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+
+#include "sndcard.h"
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#include <alsa/asoundlib.h>
+struct _AlsaCard
+{
+ SndCard parent;
+ gchar *pcmdev;
+ gchar *mixdev;
+ snd_pcm_t *read_handle;
+ snd_pcm_t *write_handle;
+ gint frame_size;
+ gint frames;
+ gchar *readbuf;
+ gint readpos;
+ gchar *writebuf;
+ gint writepos;
+ snd_mixer_t *mixer;
+};
+
+typedef struct _AlsaCard AlsaCard;
+
+SndCard *alsa_card_new(gint dev_id);
+gint alsa_card_manager_init(SndCardManager *m, gint index);
+void alsa_card_manager_set_default_pcm_device(const gchar *pcmdev);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c
new file mode 100644
index 00000000..f4ff4867
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/audiostream.c
@@ -0,0 +1,343 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mediastream.h"
+#ifdef INET6
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <netdb.h>
+#endif
+
+
+#define MAX_RTP_SIZE 1500
+
+/* this code is not part of the library itself, it is part of the mediastream program */
+void audio_stream_free(AudioStream *stream)
+{
+ RtpSession *s;
+ RtpSession *destroyed=NULL;
+ if (stream->rtprecv!=NULL) {
+ s=ms_rtp_recv_get_session(MS_RTP_RECV(stream->rtprecv));
+ if (s!=NULL){
+ destroyed=s;
+ rtp_session_destroy(s);
+ }
+ ms_filter_destroy(stream->rtprecv);
+ }
+ if (stream->rtpsend!=NULL) {
+ s=ms_rtp_send_get_session(MS_RTP_SEND(stream->rtpsend));
+ if (s!=NULL){
+ if (s!=destroyed)
+ rtp_session_destroy(s);
+ }
+ ms_filter_destroy(stream->rtpsend);
+ }
+ if (stream->soundread!=NULL) ms_filter_destroy(stream->soundread);
+ if (stream->soundwrite!=NULL) ms_filter_destroy(stream->soundwrite);
+ if (stream->encoder!=NULL) ms_filter_destroy(stream->encoder);
+ if (stream->decoder!=NULL) ms_filter_destroy(stream->decoder);
+ if (stream->timer!=NULL) ms_sync_destroy(stream->timer);
+ g_free(stream);
+}
+
+static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'};
+
+static void on_dtmf_received(RtpSession *s,gint dtmf,gpointer user_data)
+{
+ AudioStream *stream=(AudioStream*)user_data;
+ if (dtmf>15){
+ g_warning("Unsupported telephone-event type.");
+ return;
+ }
+ g_message("Receiving dtmf %c.",dtmf_tab[dtmf]);
+ if (stream!=NULL){
+ if (strcmp(stream->soundwrite->klass->name,"OssWrite")==0)
+ ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf_tab[dtmf]);
+ }
+}
+
+static void on_timestamp_jump(RtpSession *s,guint32* ts, gpointer user_data)
+{
+ g_warning("The remote sip-phone has send data with a future timestamp: %u,"
+ "resynchronising session.",*ts);
+ rtp_session_reset(s);
+}
+
+static const char *ip4local="0.0.0.0";
+static const char *ip6local="::";
+
+const char *get_local_addr_for(const char *remote)
+{
+ const char *ret;
+#ifdef INET6
+ char num[8];
+ struct addrinfo hints, *res0;
+ int err;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ err = getaddrinfo(remote,"8000", &hints, &res0);
+ if (err!=0) {
+ g_warning ("get_local_addr_for: %s", gai_strerror(err));
+ return ip4local;
+ }
+ ret=(res0->ai_addr->sa_family==AF_INET6) ? ip6local : ip4local;
+ freeaddrinfo(res0);
+#else
+ ret=ip4local;
+#endif
+ return ret;
+}
+
+void create_duplex_rtpsession(RtpProfile *profile, int locport,char *remip,int remport,
+ int payload,int jitt_comp,
+ RtpSession **recvsend){
+ RtpSession *rtpr;
+ rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
+ rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
+ rtp_session_set_profile(rtpr,profile);
+ rtp_session_set_local_addr(rtpr,get_local_addr_for(remip),locport);
+ if (remport>0) rtp_session_set_remote_addr(rtpr,remip,remport);
+ rtp_session_set_scheduling_mode(rtpr,0);
+ rtp_session_set_blocking_mode(rtpr,0);
+ rtp_session_set_payload_type(rtpr,payload);
+ rtp_session_set_jitter_compensation(rtpr,jitt_comp);
+ rtp_session_enable_adaptive_jitter_compensation(rtpr,TRUE);
+ /*rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL);*/
+ *recvsend=rtpr;
+}
+
+void create_rtp_sessions(RtpProfile *profile, int locport,char *remip,int remport,
+ int payload,int jitt_comp,
+ RtpSession **recv, RtpSession **send){
+ RtpSession *rtps,*rtpr;
+ PayloadType *pt;
+ /* creates two rtp filters to recv send streams (remote part)*/
+
+ rtps=rtp_session_new(RTP_SESSION_SENDONLY);
+ rtp_session_max_buf_size_set(rtps,MAX_RTP_SIZE);
+ rtp_session_set_profile(rtps,profile);
+#ifdef INET6
+ rtp_session_set_local_addr(rtps,"::",locport+2);
+#else
+ rtp_session_set_local_addr(rtps,"0.0.0.0",locport+2);
+#endif
+ rtp_session_set_remote_addr(rtps,remip,remport);
+ rtp_session_set_scheduling_mode(rtps,0);
+ rtp_session_set_blocking_mode(rtps,0);
+ rtp_session_set_payload_type(rtps,payload);
+ rtp_session_set_jitter_compensation(rtps,jitt_comp);
+
+ rtpr=rtp_session_new(RTP_SESSION_RECVONLY);
+ rtp_session_max_buf_size_set(rtpr,MAX_RTP_SIZE);
+ rtp_session_set_profile(rtpr,profile);
+#ifdef INET6
+ rtp_session_set_local_addr(rtpr,"::",locport);
+#else
+ rtp_session_set_local_addr(rtpr,"0.0.0.0",locport);
+#endif
+ rtp_session_set_scheduling_mode(rtpr,0);
+ rtp_session_set_blocking_mode(rtpr,0);
+ rtp_session_set_payload_type(rtpr,payload);
+ rtp_session_set_jitter_compensation(rtpr,jitt_comp);
+ rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,NULL);
+ rtp_session_signal_connect(rtpr,"timestamp_jump",(RtpCallback)on_timestamp_jump,NULL);
+ *recv=rtpr;
+ *send=rtps;
+
+}
+
+
+AudioStream * audio_stream_start_full(RtpProfile *profile, int locport,char *remip,int remport,
+ int payload,int jitt_comp, gchar *infile, gchar *outfile, SndCard *playcard, SndCard *captcard)
+{
+ AudioStream *stream=g_new0(AudioStream,1);
+ RtpSession *rtps,*rtpr;
+ PayloadType *pt;
+
+ /* //create_rtp_sessions(profile,locport,remip,remport,payload,jitt_comp,&rtpr,&rtps); */
+
+ create_duplex_rtpsession(profile,locport,remip,remport,payload,jitt_comp,&rtpr);
+ rtp_session_signal_connect(rtpr,"telephone-event",(RtpCallback)on_dtmf_received,(gpointer)stream);
+ rtps=rtpr;
+
+ stream->recv_session = rtpr;
+ stream->send_session = rtps;
+ stream->rtpsend=ms_rtp_send_new();
+ ms_rtp_send_set_session(MS_RTP_SEND(stream->rtpsend),rtps);
+ stream->rtprecv=ms_rtp_recv_new();
+ ms_rtp_recv_set_session(MS_RTP_RECV(stream->rtprecv),rtpr);
+
+
+ /* creates the local part */
+ if (infile==NULL) stream->soundread=snd_card_create_read_filter(captcard);
+ else stream->soundread=ms_read_new(infile);
+ if (outfile==NULL) stream->soundwrite=snd_card_create_write_filter(playcard);
+ else stream->soundwrite=ms_write_new(outfile);
+
+ /* creates the couple of encoder/decoder */
+ pt=rtp_profile_get_payload(profile,payload);
+ if (pt==NULL){
+ g_error("audiostream.c: undefined payload type.");
+ return NULL;
+ }
+ stream->encoder=ms_encoder_new_with_string_id(pt->mime_type);
+ stream->decoder=ms_decoder_new_with_string_id(pt->mime_type);
+ if ((stream->encoder==NULL) || (stream->decoder==NULL)){
+ /* big problem: we have not a registered codec for this payload...*/
+ audio_stream_free(stream);
+ g_error("mediastream.c: No decoder available for payload %i.",payload);
+ return NULL;
+ }
+ /* give the sound filters some properties */
+ ms_filter_set_property(stream->soundread,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
+ ms_filter_set_property(stream->soundwrite,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
+
+ /* give the encoder/decoder some parameters*/
+ ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
+ ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate);
+ ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FREQ,&pt->clock_rate);
+ ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_BITRATE,&pt->normal_bitrate);
+
+ ms_filter_set_property(stream->encoder,MS_FILTER_PROPERTY_FMTP, (void*)pt->fmtp);
+ ms_filter_set_property(stream->decoder,MS_FILTER_PROPERTY_FMTP,(void*)pt->fmtp);
+ /* create the synchronisation source */
+ stream->timer=ms_timer_new();
+
+ /* and then connect all */
+ ms_filter_add_link(stream->soundread,stream->encoder);
+ ms_filter_add_link(stream->encoder,stream->rtpsend);
+ ms_filter_add_link(stream->rtprecv,stream->decoder);
+ ms_filter_add_link(stream->decoder,stream->soundwrite);
+
+ ms_sync_attach(stream->timer,stream->soundread);
+ ms_sync_attach(stream->timer,stream->rtprecv);
+
+ /* and start */
+ ms_start(stream->timer);
+
+ return stream;
+}
+
+static int defcard=0;
+
+void audio_stream_set_default_card(int cardindex){
+ defcard=cardindex;
+}
+
+AudioStream * audio_stream_start_with_files(RtpProfile *prof,int locport,char *remip,
+ int remport,int profile,int jitt_comp,gchar *infile, gchar*outfile)
+{
+ return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,infile,outfile,NULL,NULL);
+}
+
+AudioStream * audio_stream_start(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp)
+{
+ SndCard *sndcard;
+ sndcard=snd_card_manager_get_card(snd_card_manager,defcard);
+ return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,sndcard,sndcard);
+}
+
+AudioStream *audio_stream_start_with_sndcards(RtpProfile *prof,int locport,char *remip,int remport,int profile,int jitt_comp,SndCard *playcard, SndCard *captcard)
+{
+ g_return_val_if_fail(playcard!=NULL,NULL);
+ g_return_val_if_fail(captcard!=NULL,NULL);
+ return audio_stream_start_full(prof,locport,remip,remport,profile,jitt_comp,NULL,NULL,playcard,captcard);
+}
+
+void audio_stream_set_rtcp_information(AudioStream *st, const char *cname){
+ if (st->send_session!=NULL){
+ rtp_session_set_source_description(st->send_session,cname,NULL,NULL,NULL, NULL,"linphone",
+ "This is free software (GPL) !");
+ }
+}
+
+void audio_stream_stop(AudioStream * stream)
+{
+
+ ms_stop(stream->timer);
+ ortp_global_stats_display();
+ ms_sync_detach(stream->timer,stream->soundread);
+ ms_sync_detach(stream->timer,stream->rtprecv);
+
+ ms_filter_remove_links(stream->soundread,stream->encoder);
+ ms_filter_remove_links(stream->encoder,stream->rtpsend);
+ ms_filter_remove_links(stream->rtprecv,stream->decoder);
+ ms_filter_remove_links(stream->decoder,stream->soundwrite);
+
+ audio_stream_free(stream);
+}
+
+RingStream * ring_start(gchar *file,gint interval,SndCard *sndcard)
+{
+ return ring_start_with_cb(file,interval,sndcard,NULL,NULL);
+}
+
+RingStream * ring_start_with_cb(gchar *file,gint interval,SndCard *sndcard, MSFilterNotifyFunc func,gpointer user_data)
+{
+ RingStream *stream;
+ int tmp;
+ g_return_val_if_fail(sndcard!=NULL,NULL);
+ stream=g_new0(RingStream,1);
+ stream->source=ms_ring_player_new(file,interval);
+ if (stream->source==NULL) {
+ g_warning("Could not create ring player. Probably the ring file (%s) does not exist.",file);
+ return NULL;
+ }
+ if (func!=NULL) ms_filter_set_notify_func(MS_FILTER(stream->source),func,user_data);
+ stream->sndwrite=snd_card_create_write_filter(sndcard);
+ ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_FREQ,&tmp);
+ ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_FREQ,&tmp);
+ ms_filter_get_property(stream->source,MS_FILTER_PROPERTY_CHANNELS,&tmp);
+ ms_filter_set_property(stream->sndwrite,MS_FILTER_PROPERTY_CHANNELS,&tmp);
+ stream->timer=ms_timer_new();
+ ms_filter_add_link(stream->source,stream->sndwrite);
+ ms_sync_attach(stream->timer,stream->source);
+ ms_start(stream->timer);
+ return stream;
+}
+
+void ring_stop(RingStream *stream)
+{
+ ms_stop(stream->timer);
+ ms_sync_detach(stream->timer,stream->source);
+ ms_sync_destroy(stream->timer);
+ ms_filter_remove_links(stream->source,stream->sndwrite);
+ ms_filter_destroy(stream->source);
+ ms_filter_destroy(stream->sndwrite);
+ g_free(stream);
+}
+
+/* returns the latency in samples if the audio device with id dev_id is openable in full duplex mode, else 0 */
+gint test_audio_dev(int dev_id)
+{
+ gint err;
+ SndCard *sndcard=snd_card_manager_get_card(snd_card_manager,dev_id);
+ if (sndcard==NULL) return -1;
+ err=snd_card_probe(sndcard,16,0,8000);
+ return err; /* return latency in number of sample */
+}
+
+gint audio_stream_send_dtmf(AudioStream *stream, gchar dtmf)
+{
+ ms_rtp_send_dtmf(MS_RTP_SEND(stream->rtpsend), dtmf);
+ ms_oss_write_play_dtmf(MS_OSS_WRITE(stream->soundwrite),dtmf);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h
new file mode 100644
index 00000000..3f5ad16f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/g711common.h
@@ -0,0 +1,171 @@
+/*
+ * PCM - A-Law conversion
+ * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ * Wrapper for linphone Codec class by Simon Morlat <simon.morlat@free.fr>
+ */
+
+static inline int val_seg(int val)
+{
+ int r = 0;
+ val >>= 7;
+ if (val & 0xf0) {
+ val >>= 4;
+ r += 4;
+ }
+ if (val & 0x0c) {
+ val >>= 2;
+ r += 2;
+ }
+ if (val & 0x02)
+ r += 1;
+ return r;
+}
+
+/*
+ * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
+ *
+ * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
+ *
+ * Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 0000000wxyza 000wxyz
+ * 0000001wxyza 001wxyz
+ * 000001wxyzab 010wxyz
+ * 00001wxyzabc 011wxyz
+ * 0001wxyzabcd 100wxyz
+ * 001wxyzabcde 101wxyz
+ * 01wxyzabcdef 110wxyz
+ * 1wxyzabcdefg 111wxyz
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+
+static inline unsigned char s16_to_alaw(int pcm_val)
+{
+ int mask;
+ int seg;
+ unsigned char aval;
+
+ if (pcm_val >= 0) {
+ mask = 0xD5;
+ } else {
+ mask = 0x55;
+ pcm_val = -pcm_val;
+ if (pcm_val > 0x7fff)
+ pcm_val = 0x7fff;
+ }
+
+ if (pcm_val < 256)
+ aval = pcm_val >> 4;
+ else {
+ /* Convert the scaled magnitude to segment number. */
+ seg = val_seg(pcm_val);
+ aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
+ }
+ return aval ^ mask;
+}
+
+/*
+ * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+static inline int alaw_to_s16(unsigned char a_val)
+{
+ int t;
+ int seg;
+
+ a_val ^= 0x55;
+ t = a_val & 0x7f;
+ if (t < 16)
+ t = (t << 4) + 8;
+ else {
+ seg = (t >> 4) & 0x07;
+ t = ((t & 0x0f) << 4) + 0x108;
+ t <<= seg -1;
+ }
+ return ((a_val & 0x80) ? t : -t);
+}
+/*
+ * s16_to_ulaw() - Convert a linear PCM value to u-law
+ *
+ * In order to simplify the encoding process, the original linear magnitude
+ * is biased by adding 33 which shifts the encoding range from (0 - 8158) to
+ * (33 - 8191). The result can be seen in the following encoding table:
+ *
+ * Biased Linear Input Code Compressed Code
+ * ------------------------ ---------------
+ * 00000001wxyza 000wxyz
+ * 0000001wxyzab 001wxyz
+ * 000001wxyzabc 010wxyz
+ * 00001wxyzabcd 011wxyz
+ * 0001wxyzabcde 100wxyz
+ * 001wxyzabcdef 101wxyz
+ * 01wxyzabcdefg 110wxyz
+ * 1wxyzabcdefgh 111wxyz
+ *
+ * Each biased linear code has a leading 1 which identifies the segment
+ * number. The value of the segment number is equal to 7 minus the number
+ * of leading 0's. The quantization interval is directly available as the
+ * four bits wxyz. * The trailing bits (a - h) are ignored.
+ *
+ * Ordinarily the complement of the resulting code word is used for
+ * transmission, and so the code word is complemented before it is returned.
+ *
+ * For further information see John C. Bellamy's Digital Telephony, 1982,
+ * John Wiley & Sons, pps 98-111 and 472-476.
+ */
+
+static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */
+{
+ int mask;
+ int seg;
+ unsigned char uval;
+
+ if (pcm_val < 0) {
+ pcm_val = 0x84 - pcm_val;
+ mask = 0x7f;
+ } else {
+ pcm_val += 0x84;
+ mask = 0xff;
+ }
+ if (pcm_val > 0x7fff)
+ pcm_val = 0x7fff;
+
+ /* Convert the scaled magnitude to segment number. */
+ seg = val_seg(pcm_val);
+
+ /*
+ * Combine the sign, segment, quantization bits;
+ * and complement the code word.
+ */
+ uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
+ return uval ^ mask;
+}
+
+/*
+ * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM
+ *
+ * First, a biased linear code is derived from the code word. An unbiased
+ * output can then be obtained by subtracting 33 from the biased code.
+ *
+ * Note that this function expects to be passed the complement of the
+ * original code word. This is in keeping with ISDN conventions.
+ */
+static inline int ulaw_to_s16(unsigned char u_val)
+{
+ int t;
+
+ /* Complement to obtain normal u-law value. */
+ u_val = ~u_val;
+
+ /*
+ * Extract and bias the quantization bits. Then
+ * shift up by the segment number and subtract out the bias.
+ */
+ t = ((u_val & 0x0f) << 3) + 0x84;
+ t <<= (u_val & 0x70) >> 4;
+
+ return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84));
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c
new file mode 100644
index 00000000..8210e29d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/hpuxsndcard.c
@@ -0,0 +1,301 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "sndcard.h"
+#include "osscard.h"
+
+#ifdef HAVE_SYS_AUDIO_H
+#include <sys/audio.h>
+
+
+#include "msossread.h"
+#include "msosswrite.h"
+
+#include <errno.h>
+#include <fcntl.h>
+
+
+int hpuxsnd_open(HpuxSndCard *obj, int bits,int stereo, int rate)
+{
+ int fd;
+ int p=0,cond=0;
+ int i=0;
+ int min_size=0,blocksize=512;
+ /* do a quick non blocking open to be sure that we are not going to be blocked here
+ for the eternity */
+ fd=open(obj->dev_name,O_RDWR|O_NONBLOCK);
+ if (fd<0) return -EWOULDBLOCK;
+ close(fd);
+ /* open the device */
+ fd=open(obj->dev_name,O_RDWR);
+
+ g_return_val_if_fail(fd>0,-errno);
+
+ ioctl(fd,AUDIO_RESET,0);
+ ioctl(fd,AUDIO_SET_SAMPLE_RATE,rate);
+ ioctl(fd,AUDIO_SET_CHANNELS,stereo);
+ p=AUDIO_FORMAT_LINEAR16BIT;
+ ioctl(fd,AUDIO_SET_DATA_FORMAT,p);
+ /* ioctl(fd,AUDIO_GET_RXBUFSIZE,&min_size); does not work ? */
+ min_size=2048;
+
+ g_message("dsp blocksize is %i.",min_size);
+ obj->fd=fd;
+ obj->readpos=0;
+ obj->writepos=0;
+ SND_CARD(obj)->bits=bits;
+ SND_CARD(obj)->stereo=stereo;
+ SND_CARD(obj)->rate=rate;
+ SND_CARD(obj)->bsize=min_size;
+ return fd;
+}
+
+int hpux_snd_card_probe(HpuxSndCard *obj,int bits,int stereo,int rate)
+{
+ return 2048;
+}
+
+
+int hpux_snd_card_open(HpuxSndCard *obj,int bits,int stereo,int rate)
+{
+ int fd;
+ obj->ref++;
+ if (obj->fd==0){
+ fd=hpuxsnd_open(obj,bits,stereo,rate);
+ if (fd<0) {
+ obj->fd=0;
+ obj->ref--;
+ return -1;
+ }
+ }
+ SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED;
+ return 0;
+}
+
+void hpux_snd_card_close(HpuxSndCard *obj)
+{
+ int i;
+ obj->ref--;
+ if (obj->ref==0) {
+ close(obj->fd);
+ obj->fd=0;
+ SND_CARD(obj)->flags&=~SND_CARD_FLAGS_OPENED;
+
+ }
+}
+
+void hpux_snd_card_destroy(HpuxSndCard *obj)
+{
+ snd_card_uninit(SND_CARD(obj));
+ g_free(obj->dev_name);
+ g_free(obj->mixdev_name);
+}
+
+gboolean hpux_snd_card_can_read(HpuxSndCard *obj)
+{
+ struct timeval tout={0,0};
+ int err;
+ fd_set fdset;
+ FD_ZERO(&fdset);
+ FD_SET(obj->fd,&fdset);
+ err=select(obj->fd+1,&fdset,NULL,NULL,&tout);
+ if (err>0) return TRUE;
+ else return FALSE;
+}
+
+int hpux_snd_card_read(HpuxSndCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+ if (size<bsize){
+ gint canread=MIN(bsize-obj->readpos,size);
+ if (obj->readbuf==NULL) obj->readbuf=g_malloc0(bsize);
+ if (obj->readpos==0){
+ err=read(obj->fd,obj->readbuf,bsize);
+ if (err<0) {
+ g_warning("hpux_snd_card_read: read() failed:%s.",strerror(errno));
+ return -1;
+ }
+ }
+
+ memcpy(buf,&obj->readbuf[obj->readpos],canread);
+ obj->readpos+=canread;
+ if (obj->readpos>=bsize) obj->readpos=0;
+ return canread;
+ }else{
+ err=read(obj->fd,buf,size);
+ if (err<0) {
+ g_warning("hpux_snd_card_read: read-2() failed:%s.",strerror(errno));
+ }
+ return err;
+ }
+
+}
+
+int hpux_snd_card_write(HpuxSndCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+ if (size<bsize){
+ gint canwrite=MIN(bsize-obj->writepos,size);
+ if (obj->writebuf==NULL) obj->writebuf=g_malloc0(bsize);
+
+ memcpy(&obj->writebuf[obj->writepos],buf,canwrite);
+ obj->writepos+=canwrite;
+ if (obj->writepos>=bsize){
+ err=write(obj->fd,obj->writebuf,bsize);
+ }
+ return canwrite;
+ }else{
+ return write(obj->fd,buf,bsize);
+ }
+}
+
+#define SND_CARD_LEVEL_TO_HPUX_LEVEL(a) (((a)*2) - 100)
+#define HPUX_LEVEL_TO_SND_CARD_LEVEL(a) (((a)+200)/2)
+void hpux_snd_card_set_level(HpuxSndCard *obj,gint way,gint a)
+{
+ struct audio_gain gain;
+ int error,mix_fd;
+
+ g_return_if_fail(obj->mixdev_name!=NULL);
+ memset(&gain,0,sizeof(struct audio_gain));
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ gain.cgain[0].monitor_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ gain.cgain[1].monitor_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ gain.cgain[0].receive_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ gain.cgain[1].receive_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ gain.cgain[0].transmit_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ gain.cgain[1].transmit_gain=SND_CARD_LEVEL_TO_HPUX_LEVEL(a);
+ break;
+ default:
+ g_warning("hpux_snd_card_set_level: unsupported command.");
+ return;
+ }
+ gain.channel_mask=AUDIO_CHANNEL_RIGHT|AUDIO_CHANNEL_LEFT;
+ mix_fd = open(obj->mixdev_name, O_WRONLY);
+ g_return_if_fail(mix_fd>0);
+ error=ioctl(mix_fd,AUDIO_SET_GAINS,&gain);
+ if (error<0){
+ g_warning("hpux_snd_card_set_level: Could not set gains: %s",strerror(errno));
+ }
+ close(mix_fd);
+}
+
+gint hpux_snd_card_get_level(HpuxSndCard *obj,gint way)
+{
+ struct audio_gain gain;
+ int p=0,mix_fd,error;
+ g_return_if_fail(obj->mixdev_name!=NULL);
+
+ gain.channel_mask=AUDIO_CHANNEL_RIGHT|AUDIO_CHANNEL_LEFT;
+ mix_fd = open(obj->mixdev_name, O_RDONLY);
+ g_return_if_fail(mix_fd>0);
+ error=ioctl(mix_fd,AUDIO_GET_GAINS,&gain);
+ if (error<0){
+ g_warning("hpux_snd_card_set_level: Could not get gains: %s",strerror(errno));
+ }
+ close(mix_fd);
+
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ p=gain.cgain[0].monitor_gain;
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ p=gain.cgain[0].receive_gain;
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ p=gain.cgain[0].transmit_gain;
+ break;
+ default:
+ g_warning("hpux_snd_card_get_level: unsupported command.");
+ return -1;
+ }
+ return HPUX_LEVEL_TO_SND_CARD_LEVEL(p);
+}
+
+void hpux_snd_card_set_source(HpuxSndCard *obj,int source)
+{
+ gint p=0;
+ gint mix_fd;
+ gint error=0;
+ g_return_if_fail(obj->mixdev_name!=NULL);
+
+ mix_fd=open("/dev/audio",O_WRONLY);
+ g_return_if_fail(mix_fd>0);
+ switch(source){
+ case 'm':
+ error=ioctl(mix_fd,AUDIO_SET_INPUT,AUDIO_IN_MIKE);
+ break;
+ case 'l':
+ error=ioctl(mix_fd,AUDIO_SET_INPUT,AUDIO_IN_LINE);
+ break;
+ default:
+ g_warning("hpux_snd_card_set_source: unsupported source.");
+ }
+ close(mix_fd);
+}
+
+MSFilter *hpux_snd_card_create_read_filter(HpuxSndCard *card)
+{
+ MSFilter *f=ms_oss_read_new();
+ ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
+ return f;
+}
+
+MSFilter *hpux_snd_card_create_write_filter(HpuxSndCard *card)
+{
+ MSFilter *f=ms_oss_write_new();
+ ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
+ return f;
+}
+
+
+SndCard * hpux_snd_card_new(char *devname, char *mixdev_name)
+{
+ HpuxSndCard * obj= g_new0(HpuxSndCard,1);
+ SndCard *base= SND_CARD(obj);
+ snd_card_init(base);
+ obj->dev_name=g_strdup(devname);
+ obj->mixdev_name=g_strdup( mixdev_name);
+ base->card_name=g_strdup(devname);
+ base->_probe=(SndCardOpenFunc)hpux_snd_card_probe;
+ base->_open_r=(SndCardOpenFunc)hpux_snd_card_open;
+ base->_open_w=(SndCardOpenFunc)hpux_snd_card_open;
+ base->_can_read=(SndCardPollFunc)hpux_snd_card_can_read;
+ base->_read=(SndCardIOFunc)hpux_snd_card_read;
+ base->_write=(SndCardIOFunc)hpux_snd_card_write;
+ base->_close_r=(SndCardCloseFunc)hpux_snd_card_close;
+ base->_close_w=(SndCardCloseFunc)hpux_snd_card_close;
+ base->_set_rec_source=(SndCardMixerSetRecSourceFunc)hpux_snd_card_set_source;
+ base->_set_level=(SndCardMixerSetLevelFunc)hpux_snd_card_set_level;
+ base->_get_level=(SndCardMixerGetLevelFunc)hpux_snd_card_get_level;
+ base->_destroy=(SndCardDestroyFunc)hpux_snd_card_destroy;
+ base->_create_read_filter=(SndCardCreateFilterFunc)hpux_snd_card_create_read_filter;
+ base->_create_write_filter=(SndCardCreateFilterFunc)hpux_snd_card_create_write_filter;
+ return base;
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c
new file mode 100644
index 00000000..b929cce9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.c
@@ -0,0 +1,574 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+
+ JACK support
+ Copyright (C) 2004 Tobias Gehrig tobias@gehrig.tk
+*/
+
+#include "jackcard.h"
+
+#ifdef __JACK_ENABLED__
+
+#include "msossread.h"
+#include "msosswrite.h"
+
+#include <signal.h>
+
+#define READBUFFERSIZE 524288
+#define WRITEBUFFERSIZE 524288
+#define BSIZE 512
+
+/**
+ * jack_shutdown:
+ * @arg:
+ *
+ * This is the shutdown callback for this JACK application.
+ * It is called by JACK if the server ever shuts down or
+ * decides to disconnect the client.
+ *
+ */
+void
+jack_shutdown (void *arg)
+{
+ JackCard* obj = (JackCard*) arg;
+
+ obj->jack_running = FALSE;
+ obj->jack_active = FALSE;
+ obj->read.port = NULL;
+ if (obj->read.open)
+ obj->read.init = TRUE;
+ obj->write.port = NULL;
+ if (obj->write.open)
+ obj->write.init = TRUE;
+}
+
+int samplerate(jack_nframes_t rate, void *arg)
+{
+ JackCard* obj = (JackCard*) arg;
+ int error;
+
+ obj->rate = rate;
+ if (obj->read.open) {
+ obj->read.data.src_ratio = (double)obj->read.rate / (double)obj->rate;
+ obj->read.data.input_frames = (long)((double)obj->read.frames/obj->read.data.src_ratio);
+ g_free(obj->read.data.data_in);
+ obj->read.data.data_in = malloc(obj->read.data.input_frames*sizeof(float));
+ if (obj->read.src_state)
+ if ((error = src_set_ratio(obj->read.src_state, obj->read.data.src_ratio)) != 0)
+ g_warning("Error while resetting the write samplerate: %s", src_strerror(error));
+ }
+ if (obj->write.open) {
+ obj->write.data.src_ratio = (double)obj->rate / (double)obj->write.rate;
+ obj->write.data.output_frames = (long)((double)obj->write.frames*obj->write.data.src_ratio);
+ g_free(obj->write.data.data_out);
+ obj->write.data.data_out = malloc(obj->write.data.output_frames*sizeof(float));
+ if (obj->write.src_state)
+ if ((error = src_set_ratio(obj->write.src_state, obj->write.data.src_ratio)) != 0)
+ g_warning("Error while resetting the write samplerate: %s", src_strerror(error));
+ }
+ return 0;
+}
+
+/*
+ * The process callback for this JACK application.
+ * It is called by JACK at the appropriate times.
+ * @nframes :
+ * @arg :
+ */
+int
+process (jack_nframes_t nframes, void *arg)
+{
+ JackCard* obj = (JackCard*) arg;
+ sample_t *out;
+ sample_t *in;
+
+ if (obj->clear && !obj->write.can_process) {
+ out = (sample_t *) jack_port_get_buffer (obj->write.port, nframes);
+ memset (out, 0, nframes * sizeof(sample_t));
+ obj->clear = FALSE;
+ }
+
+ if (!obj->can_process)
+ return 0;
+
+ if(obj->read.can_process) {
+ in = (sample_t *) jack_port_get_buffer (obj->read.port, nframes);
+ jack_ringbuffer_write (obj->read.buffer, (void *) in, sizeof(sample_t) * nframes);
+ }
+
+ if (obj->write.can_process) {
+ out = (sample_t *) jack_port_get_buffer (obj->write.port, nframes);
+ memset (out, 0, nframes * sizeof(sample_t));
+ if (obj->clear && jack_ringbuffer_read_space(obj->write.buffer) == 0) {
+ obj->write.can_process = FALSE;
+ if (!obj->read.open)
+ obj->can_process = FALSE;
+ obj->clear = FALSE;
+ return 0;
+ }
+ jack_ringbuffer_read (obj->write.buffer, (void *) out, sizeof(sample_t) * nframes);
+ }
+ return 0;
+}
+
+int jack_init(JackCard* obj)
+{
+ char* client_name;
+ int error;
+
+ if (!obj->jack_running) {
+ obj->client = NULL;
+ client_name = g_strdup_printf("linphone-%u", g_random_int());
+ if ((obj->client = jack_client_new (client_name)) == NULL) {
+ g_warning("cannot create jack client");
+ g_free(client_name);
+ return -1;
+ }
+ g_message("Found Jack Daemon");
+ g_free(client_name);
+
+ /* tell the JACK server to call `process()' whenever
+ there is work to be done.
+ */
+ jack_set_process_callback (obj->client, process, obj);
+
+ /* tell the JACK server to call `jack_shutdown()' if
+ it ever shuts down, either entirely, or if it
+ just decides to stop calling us.
+ */
+ jack_on_shutdown (obj->client, jack_shutdown, obj);
+ jack_set_sample_rate_callback (obj->client, samplerate, obj);
+ obj->rate = jack_get_sample_rate (obj->client);
+ if (obj->rate == 0) {
+ g_warning ("rate is 0???");
+ if (jack_client_close(obj->client) != 0)
+ g_warning("could not close client");
+ return -1;
+ }
+ obj->buffer_size = jack_get_buffer_size(obj->client);
+ obj->jack_running = TRUE;
+ }
+
+ if (!obj->jack_active) {
+ if (jack_activate (obj->client)) {
+ g_warning("cannot activate jack client");
+ return -1;
+ } else obj->jack_active = TRUE;
+ }
+
+ if (obj->read.init) {
+ if (!obj->read.port && (obj->read.port = jack_port_register (obj->client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0))==NULL) {
+ g_warning("error while trying to register input port");
+ return -1;
+ }
+ if (!obj->read.phys_ports && (obj->read.phys_ports = jack_get_ports (obj->client, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == NULL) {
+ g_warning("Cannot find any physical capture ports\n");
+ jack_port_unregister(obj->client, obj->read.port);
+ obj->read.port = NULL;
+ return -1;
+ }
+ if (!jack_port_connected(obj->read.port))
+ if ((error = jack_connect (obj->client, obj->read.phys_ports[0], jack_port_name (obj->read.port))) != 0) {
+ g_warning("cannot connect input ports: %s -> %s\n", jack_port_name (obj->read.port), obj->read.phys_ports[0]);
+ if (error == EEXIST) g_warning("connection already made");
+ else {
+ jack_port_unregister(obj->client, obj->read.port);
+ obj->read.port = NULL;
+ return -1;
+ }
+ }
+ obj->read.init = FALSE;
+ }
+
+ if (obj->write.init) {
+ if (!obj->write.port && (obj->write.port = jack_port_register (obj->client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0))==NULL) {
+ g_warning("error while trying to register output port");
+ return -1;
+ }
+ if (!obj->write.phys_ports && (obj->write.phys_ports = jack_get_ports (obj->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) {
+ g_warning("Cannot find any physical playback ports\n");
+ jack_port_unregister(obj->client, obj->write.port);
+ obj->write.port = NULL;
+ return -1;
+ }
+ if (!jack_port_connected(obj->write.port)) {
+ if ((error = jack_connect (obj->client, jack_port_name (obj->write.port), obj->write.phys_ports[0])) != 0) {
+ g_warning("cannot connect output ports: %s -> %s\n", jack_port_name (obj->write.port), obj->write.phys_ports[0]);
+ if (error == EEXIST) g_warning("connection already made");
+ else {
+ jack_port_unregister(obj->client, obj->write.port);
+ obj->write.port = NULL;
+ return -1;
+ }
+ }
+ if ((error = jack_connect (obj->client, jack_port_name (obj->write.port), obj->write.phys_ports[1])) != 0) {
+ g_warning("cannot connect output ports: %s -> %s\n", jack_port_name (obj->write.port), obj->write.phys_ports[1]);
+ if (error == EEXIST) g_warning("connection already made");
+ else {
+ jack_port_unregister(obj->client, obj->write.port);
+ obj->write.port = NULL;
+ return -1;
+ }
+ }
+ }
+ obj->write.init = FALSE;
+ }
+ return 0;
+}
+
+int jack_card_open_r(JackCard *obj,int bits,int stereo,int rate)
+{
+ int channels = stereo + 1, bsize, error;
+ obj->read.init = TRUE;
+ if (jack_init(obj) != 0) return -1;
+
+ obj->read.rate = rate;
+ obj->sample_size = bits / 8;
+ obj->frame_size = channels * obj->sample_size;
+ bsize = BSIZE;
+ obj->read.frames = bsize / 2;
+ SND_CARD(obj)->bsize = bsize;
+ SND_CARD(obj)->flags |= SND_CARD_FLAGS_OPENED;
+ obj->read.channels = channels;
+ if ((obj->read.src_state = src_new (SRC_SINC_FASTEST, channels, &error)) == NULL)
+ g_warning("Error while initializing the samplerate converter: %s", src_strerror(error));
+ obj->read.data.src_ratio = (double)rate / (double)obj->rate;
+ obj->read.data.input_frames = (long)((double)obj->read.frames/obj->read.data.src_ratio);
+ obj->read.data.data_in = malloc(obj->read.data.input_frames*sizeof(float));
+ obj->read.data.data_out = malloc(obj->read.frames*sizeof(float));
+ obj->read.data.end_of_input = 0;
+ if (!obj->read.buffer)
+ obj->read.buffer = jack_ringbuffer_create(READBUFFERSIZE);
+ obj->read.can_process = TRUE;
+ obj->can_process = TRUE;
+ obj->read.open = TRUE;
+ obj->read.init = FALSE;
+ return 0;
+}
+
+int jack_card_open_w(JackCard *obj,int bits,int stereo,int rate)
+{
+ int channels = stereo + 1, bsize, err;
+ obj->write.init = TRUE;
+ if (jack_init(obj) != 0) return -1;
+
+ obj->write.rate = rate;
+ obj->sample_size = bits / 8;
+ obj->frame_size = channels * obj->sample_size;
+ bsize = BSIZE;
+ obj->write.frames = bsize / 2;
+ SND_CARD(obj)->bsize = bsize;
+ SND_CARD(obj)->flags |= SND_CARD_FLAGS_OPENED;
+ obj->write.channels = channels;
+ if ((obj->write.src_state = src_new (SRC_SINC_FASTEST, channels, &err)) == NULL)
+ g_warning("Error while initializing the samplerate converter: %s", src_strerror(err));
+ obj->write.data.src_ratio = (double)obj->rate / (double)rate;
+ obj->write.data.data_in = malloc(obj->write.frames*sizeof(float));
+ obj->write.data.end_of_input = 0;
+ obj->write.data.output_frames = (long)((double)obj->write.frames*obj->write.data.src_ratio);
+ obj->write.data.data_out = malloc(obj->write.data.output_frames*sizeof(float));
+ if (!obj->write.buffer)
+ obj->write.buffer = jack_ringbuffer_create(WRITEBUFFERSIZE);
+ obj->write.can_process = TRUE;
+ obj->can_process = TRUE;
+ obj->write.open = TRUE;
+ obj->write.init = FALSE;
+ return 0;
+}
+
+void jack_card_set_blocking_mode(JackCard *obj, gboolean yesno)
+{
+}
+
+void jack_card_close_r(JackCard *obj)
+{
+ obj->read.open = FALSE;
+ obj->read.init = FALSE;
+ obj->read.can_process = FALSE;
+ if (!obj->write.open)
+ obj->can_process = FALSE;
+ if (obj->read.src_state)
+ obj->read.src_state = src_delete (obj->read.src_state);
+ g_free(obj->read.data.data_in);
+ g_free(obj->read.data.data_out);
+}
+
+void jack_card_close_w(JackCard *obj)
+{
+ obj->write.open = FALSE;
+ obj->write.init = FALSE;
+ obj->clear = TRUE;
+ if (!obj->jack_running) {
+ obj->write.can_process = FALSE;
+ obj->can_process = FALSE;
+ }
+ if (obj->write.src_state)
+ obj->write.src_state = src_delete (obj->write.src_state);
+ g_free(obj->write.data.data_in);
+ g_free(obj->write.data.data_out);
+}
+
+int jack_card_probe(JackCard *obj,int bits,int stereo,int rate)
+{
+ if (obj->jack_running) return BSIZE;
+ else if (jack_init(obj) == 0) return BSIZE;
+ else return -1;
+}
+
+void jack_card_destroy(JackCard *obj)
+{
+ if (obj->jack_running) jack_client_close (obj->client);
+ snd_card_uninit(SND_CARD(obj));
+ if (obj->read.buffer) {
+ jack_ringbuffer_free(obj->read.buffer);
+ obj->read.buffer = NULL;
+ }
+ if (obj->write.buffer) {
+ jack_ringbuffer_free(obj->write.buffer);
+ obj->write.buffer = NULL;
+ }
+ if (obj->read.phys_ports) {
+ g_free(obj->read.phys_ports);
+ obj->read.phys_ports = NULL;
+ }
+ if (obj->write.phys_ports) {
+ g_free(obj->write.phys_ports);
+ obj->write.phys_ports = NULL;
+ }
+}
+
+gboolean jack_card_can_read(JackCard *obj)
+{
+ g_return_val_if_fail(obj->read.buffer!=NULL,0);
+ if (jack_ringbuffer_read_space(obj->read.buffer)>=(long)((double)obj->read.frames/obj->read.data.src_ratio)*sizeof(sample_t)) return TRUE;
+ else return FALSE;
+}
+
+int jack_card_read(JackCard *obj,char *buf,int size)
+{
+ size_t bytes, can_read, i;
+ int error;
+ float norm, value;
+
+ g_return_val_if_fail((obj->read.buffer!=NULL)&&(obj->read.src_state!=NULL),-1);
+ if (jack_init(obj) != 0) return -1;
+ size /= 2;
+ can_read = MIN(size, obj->read.frames);
+ // can_read = MIN(((long)((double)can_read / obj->read.data.src_ratio))*sizeof(sample_t), jack_ringbuffer_read_space(obj->read.buffer));
+ can_read = ((long)((double)can_read / obj->read.data.src_ratio))*sizeof(sample_t);
+ obj->read.can_process = FALSE;
+ bytes = jack_ringbuffer_read (obj->read.buffer, (void *)obj->read.data.data_in, can_read);
+ obj->read.can_process = TRUE;
+ obj->read.data.input_frames = bytes / sizeof(sample_t);
+ can_read = MIN(size, obj->read.frames);
+ obj->read.data.output_frames = can_read;
+ if ((error = src_process(obj->read.src_state, &(obj->read.data))) != 0)
+ g_warning("error while samplerate conversion. error: %s", src_strerror(error));
+ norm = obj->read.level*obj->level*(float)0x8000;
+ for (i=0; i < obj->read.data.output_frames_gen; i++) {
+ value = obj->read.data.data_out[i]*norm;
+ if (value >= 32767.0)
+ ((short*)buf)[i] = 32767;
+ else if (value <= -32768.0)
+ ((short*)buf)[i] = -32768;
+ else
+ ((short*)buf)[i] = (short)value;
+ }
+ bytes = obj->read.data.output_frames_gen * 2;
+ return bytes;
+}
+
+int jack_card_write(JackCard *obj,char *buf,int size)
+{
+ size_t bytes, can_write, i;
+ int error;
+ float norm;
+
+ g_return_val_if_fail((obj->write.buffer!=NULL)&&(obj->write.src_state!=NULL),-1);
+ if (jack_init(obj) != 0) return -1;
+ size /= 2;
+ can_write = MIN(size, obj->write.frames);
+ norm = obj->write.level*obj->level/(float)0x8000;
+ for (i=0; i<can_write; i++) {
+ obj->write.data.data_in[i] = (float)((short*)buf)[i]*norm;
+ }
+ obj->write.data.input_frames = can_write;
+ if ((error = src_process(obj->write.src_state, &(obj->write.data))) != 0)
+ g_warning("error while samplerate conversion. error: %s", src_strerror(error));
+ obj->write.can_process = FALSE;
+ bytes = jack_ringbuffer_write (obj->write.buffer, (void *) obj->write.data.data_out, sizeof(sample_t)*obj->write.data.output_frames_gen);
+ obj->write.can_process = TRUE;
+ return bytes;
+}
+
+void jack_card_set_level(JackCard *obj,gint way,gint a)
+{
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ obj->level = (float)a / 100.0;
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ obj->read.level = (float)a / 100.0;
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ obj->write.level = (float)a / 100.0;
+ break;
+ default:
+ g_warning("jack_card_set_level: unsupported command.");
+ }
+}
+
+gint jack_card_get_level(JackCard *obj,gint way)
+{
+ gint value = 0;
+
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ value = (gint)(obj->level*100.0);
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ value = (gint)(obj->read.level*100.0);
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ value = (gint)(obj->write.level*100.0);
+ break;
+ default:
+ g_warning("jack_card_get_level: unsupported command.");
+ }
+ return value;
+}
+
+void jack_card_set_source(JackCard *obj,int source)
+{
+}
+
+MSFilter *jack_card_create_read_filter(JackCard *card)
+{
+ MSFilter *f=ms_oss_read_new();
+ ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
+ return f;
+}
+
+MSFilter *jack_card_create_write_filter(JackCard *card)
+{
+ MSFilter *f=ms_oss_write_new();
+ ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
+ return f;
+}
+SndCard * jack_card_new(jack_client_t *client)
+{
+ JackCard * obj;
+ SndCard *base;
+
+ obj= g_new0(JackCard,1);
+
+ if (!client) return NULL;
+ obj->client = client;
+ obj->jack_running = TRUE;
+ obj->jack_active = FALSE;
+ obj->can_process = FALSE;
+ obj->clear = TRUE;
+ obj->write.can_process = FALSE;
+ obj->write.open = FALSE;
+ obj->write.init = TRUE;
+ obj->write.port = NULL;
+ obj->write.phys_ports = NULL;
+ obj->write.buffer = NULL;
+ obj->read.can_process = FALSE;
+ obj->read.open = FALSE;
+ obj->read.init = TRUE;
+ obj->read.port = NULL;
+ obj->read.phys_ports = NULL;
+ obj->read.buffer = NULL;
+
+ /* tell the JACK server to call `process()' whenever
+ there is work to be done.
+ */
+ jack_set_process_callback (client, process, obj);
+
+ /* tell the JACK server to call `jack_shutdown()' if
+ it ever shuts down, either entirely, or if it
+ just decides to stop calling us.
+ */
+ jack_on_shutdown (client, jack_shutdown, obj);
+
+ jack_set_sample_rate_callback (client, samplerate, obj);
+
+ obj->rate = jack_get_sample_rate (client);
+ obj->buffer_size = jack_get_buffer_size(obj->client);
+
+ jack_init(obj);
+
+ base= SND_CARD(obj);
+ snd_card_init(base);
+
+#ifdef HAVE_GLIB
+ base->card_name=g_strdup_printf("JACK client");
+#else
+ base->card_name=malloc(100);
+ snprintf(base->card_name, 100, "JACK client");
+#endif
+
+ base->_probe=(SndCardOpenFunc)jack_card_probe;
+ base->_open_r=(SndCardOpenFunc)jack_card_open_r;
+ base->_open_w=(SndCardOpenFunc)jack_card_open_w;
+ base->_can_read=(SndCardPollFunc)jack_card_can_read;
+ base->_set_blocking_mode=(SndCardSetBlockingModeFunc)jack_card_set_blocking_mode;
+ base->_read=(SndCardIOFunc)jack_card_read;
+ base->_write=(SndCardIOFunc)jack_card_write;
+ base->_close_r=(SndCardCloseFunc)jack_card_close_r;
+ base->_close_w=(SndCardCloseFunc)jack_card_close_w;
+ base->_set_rec_source=(SndCardMixerSetRecSourceFunc)jack_card_set_source;
+ base->_set_level=(SndCardMixerSetLevelFunc)jack_card_set_level;
+ base->_get_level=(SndCardMixerGetLevelFunc)jack_card_get_level;
+ base->_destroy=(SndCardDestroyFunc)jack_card_destroy;
+ base->_create_read_filter=(SndCardCreateFilterFunc)jack_card_create_read_filter;
+ base->_create_write_filter=(SndCardCreateFilterFunc)jack_card_create_write_filter;
+
+ obj->read.buffer=NULL;
+ obj->write.buffer=NULL;
+ obj->buffer_size = 0;
+ obj->level = 1.0;
+ obj->write.level = 1.0;
+ obj->read.level = 1.0;
+
+ return base;
+}
+
+
+gint jack_card_manager_init(SndCardManager *m, gint index)
+{
+ jack_client_t *client = NULL;
+ char* client_name;
+
+ client_name=g_strdup_printf("linphone-%u", g_random_int());
+ if ((client = jack_client_new (client_name))!= NULL)
+ {
+ g_message("Found Jack Daemon");
+ g_free(client_name);
+ m->cards[index]=jack_card_new(client);
+ m->cards[index]->index=index;
+ return 1;
+ } else {
+ g_free(client_name);
+ return 0;
+ }
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h
new file mode 100644
index 00000000..33ec46dc
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/jackcard.h
@@ -0,0 +1,81 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+
+ JACK support
+ Copyright (C) 2004 Tobias Gehrig tobias@gehrig.tk
+*/
+
+#ifndef JACK_CARD_H
+#define JACK_CARD_H
+
+#include <config.h>
+
+#ifdef __JACK_ENABLED__
+
+#include "sndcard.h"
+
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include <samplerate.h>
+
+typedef jack_default_audio_sample_t sample_t;
+
+typedef struct {
+ jack_port_t *port;
+ const char **phys_ports;
+ float level;
+ jack_ringbuffer_t *buffer;
+ gint channels;
+ gint rate;
+ SRC_STATE* src_state;
+ SRC_DATA data;
+ size_t frames;
+ gboolean can_process;
+ gboolean open;
+ gboolean init;
+} jackcard_mode_t;
+
+struct _JackCard
+{
+ SndCard parent;
+
+ jack_client_t *client;
+ gboolean jack_running;
+ gboolean jack_active;
+ float level;
+ jack_nframes_t buffer_size;
+ gint sample_size;
+ gint frame_size;
+ gint rate;
+ gboolean can_process;
+ gboolean clear;
+
+ jackcard_mode_t read, write;
+};
+
+typedef struct _JackCard JackCard;
+
+SndCard * jack_card_new(jack_client_t *client);
+
+gint jack_card_manager_init(SndCardManager *m, gint index);
+
+#endif
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h
new file mode 100644
index 00000000..3ccbab69
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mediastream.h
@@ -0,0 +1,130 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MEDIASTREAM_H
+#define MEDIASTREAM_H
+
+#include "msrtprecv.h"
+#include "msrtpsend.h"
+#include "ms.h"
+#include "msosswrite.h"
+#include "msossread.h"
+#include "msread.h"
+#include "mswrite.h"
+#include "mstimer.h"
+#include "mscodec.h"
+#ifdef HAVE_SPEEX
+#include "msspeexdec.h"
+#endif
+#include "msringplayer.h"
+
+
+struct _AudioStream
+{
+ MSSync *timer;
+ RtpSession *send_session;
+ RtpSession *recv_session;
+ MSFilter *soundread;
+ MSFilter *soundwrite;
+ MSFilter *encoder;
+ MSFilter *decoder;
+ MSFilter *rtprecv;
+ MSFilter *rtpsend;
+};
+
+
+typedef struct _AudioStream AudioStream;
+
+struct _RingStream
+{
+ MSSync *timer;
+ MSFilter *source;
+ MSFilter *sndwrite;
+};
+
+typedef struct _RingStream RingStream;
+
+/* start a thread that does sampling->encoding->rtp_sending|rtp_receiving->decoding->playing */
+AudioStream *audio_stream_start (RtpProfile * prof, int locport, char *remip,
+ int remport, int profile, int jitt_comp);
+
+AudioStream *audio_stream_start_with_sndcards(RtpProfile * prof, int locport, char *remip4,
+ int remport, int profile, int jitt_comp, SndCard *playcard, SndCard *captcard);
+
+AudioStream *audio_stream_start_with_files (RtpProfile * prof, int locport,
+ char *remip4, int remport,
+ int profile, int jitt_comp,
+ gchar * infile, gchar * outfile);
+void audio_stream_set_rtcp_information(AudioStream *st, const char *cname);
+
+
+/* stop the above process*/
+void audio_stream_stop (AudioStream * stream);
+
+RingStream *ring_start (gchar * file, gint interval, SndCard *sndcard);
+RingStream *ring_start_with_cb(gchar * file, gint interval, SndCard *sndcard, MSFilterNotifyFunc func,gpointer user_data);
+void ring_stop (RingStream * stream);
+
+/* returns the latency in samples if the audio device with id dev_id is openable in full duplex mode, else 0 */
+gint test_audio_dev (int dev_id);
+
+/* send a dtmf */
+gint audio_stream_send_dtmf (AudioStream * stream, gchar dtmf);
+
+void audio_stream_set_default_card(int cardindex);
+
+
+#ifdef VIDEO_ENABLED
+
+/*****************
+ Video Support
+ *****************/
+
+
+
+struct _VideoStream
+{
+ MSSync *timer;
+ RtpSession *send_session;
+ RtpSession *recv_session;
+ MSFilter *source;
+ MSFilter *output;
+ MSFilter *encoder;
+ MSFilter *decoder;
+ MSFilter *rtprecv;
+ MSFilter *rtpsend;
+ gboolean show_local;
+};
+
+
+typedef struct _VideoStream VideoStream;
+
+VideoStream *video_stream_start(RtpProfile *profile, int locport, char *remip4, int remport,
+ int payload, int jitt_comp, gboolean show_local, const gchar *source, const gchar *device);
+void video_stream_set_rtcp_information(VideoStream *st, const char *cname);
+void video_stream_stop (VideoStream * stream);
+
+VideoStream * video_preview_start(const gchar *source, const gchar *device);
+void video_preview_stop(VideoStream *stream);
+
+#endif
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c
new file mode 100644
index 00000000..cfcafa33
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.c
@@ -0,0 +1,342 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ms.h"
+#include "sndcard.h"
+#include "mscodec.h"
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef VIDEO_ENABLED
+extern void ms_video_source_register_all();
+#endif
+#ifdef HAVE_ILBC
+extern void ms_ilbc_codec_init();
+#endif
+
+/**
+ * ms_init:
+ *
+ *
+ * Initialize the mediastreamer. This must be the first function called in a program
+ * using the mediastreamer library.
+ *
+ *
+ */
+void ms_init()
+{
+ if (!g_thread_supported()) g_thread_init (NULL);
+#ifdef HAVE_GLIB
+ if (!g_module_supported()){
+ g_error("GModule is not supported.");
+ }
+#endif
+ /* initialize the oss subsystem */
+ snd_card_manager_init(snd_card_manager);
+ /* register the statically linked codecs */
+ ms_codec_register_all();
+#ifdef VIDEO_ENABLED
+ ms_video_source_register_all();
+#endif
+#ifdef HAVE_ILBC
+ ms_ilbc_codec_init();
+#endif
+}
+
+
+static gint compare(gconstpointer a, gconstpointer b)
+{
+ MSFilter *f1=(MSFilter*)a,*f2=(MSFilter*)b;
+ if (f1->klass<f2->klass) return -1;
+ if (f1->klass==f2->klass) return 0;
+ /* if f1->klass>f2->klass ....*/
+ return 1;
+}
+
+static GList *g_list_append_if_new(GList *l,gpointer data)
+{
+ GList *res=l;
+ if (g_list_find(res,data)==NULL)
+ res=g_list_append(res,data);
+ return(res);
+}
+
+static GList *get_nexts(MSFilter *f,GList *l)
+{
+ int i;
+ MSFifo *fifo;
+ MSQueue *q;
+ GList *res=l;
+
+ /* check fifos*/
+ for (i=0;i <f->klass->max_foutputs;i++)
+ {
+ fifo=f->outfifos[i];
+ if (fifo!=NULL) res=g_list_append_if_new(res,(gpointer)fifo->next_data);
+ }
+ /* check queues*/
+ for (i=0;i <f->klass->max_qoutputs;i++)
+ {
+ q=f->outqueues[i];
+ if (q!=NULL) res=g_list_append_if_new(res,(gpointer)q->next_data);
+ }
+ return(res);
+}
+
+/* compile graphs attached to a sync source*/
+int ms_compile(MSSync *sync)
+{
+ int i;
+ GList *list1=NULL,*list2=NULL,*elem;
+ GList *proc_chain=NULL;
+ MSFilter *f;
+
+ /* first free the old list if we are just updating*/
+ if (sync->execution_list!=NULL) g_list_free(sync->execution_list);
+ /* get the list of filters attached to this sync*/
+ for (i=0;i<sync->filters;i++)
+ {
+ /* //printf("found filter !\n"); */
+ list1=g_list_append(list1,sync->attached_filters[i]);
+ }
+ /* find the processing chain */
+ while (list1!=NULL)
+ {
+ list2=NULL;
+ /* sort the list by types of filter*/
+ list1=g_list_sort(list1,compare);
+ /* save into the processing chain list*/
+ /* //printf("list1 :%i elements\n",g_list_length(list1)); */
+ proc_chain=g_list_concat(proc_chain,list1);
+ /* get all following filters. They are appended to list2*/
+ elem=list1;
+ while (elem!=NULL)
+ {
+ f=(MSFilter*)(elem->data);
+ /* check if filter 's status */
+ if (f->klass->attributes & FILTER_CAN_SYNC)
+ {
+ sync->samples_per_tick=0;
+ }
+ list2=get_nexts(f,list2);
+ elem=g_list_next(elem);
+ }
+ list1=list2;
+ }
+ sync->execution_list=proc_chain;
+ sync->flags&=~MS_SYNC_NEED_UPDATE;
+ ms_trace("%i filters successfully compiled in a processing chain.",g_list_length(sync->execution_list));
+ return 0;
+}
+
+/*execute the processing chain attached to a sync source. It is called as a thread by ms_main()*/
+void *ms_thread_run(void *sync_ptr)
+{
+ MSSync *sync=(MSSync*) sync_ptr;
+ GList *filter;
+ MSFilter *f;
+
+
+ ms_sync_lock(sync);
+ while(sync->run)
+ {
+ /* //g_message("sync->run=%i",sync->run); */
+ if (sync->samples_per_tick==0) ms_sync_suspend(sync);
+ if (sync->flags & MS_SYNC_NEED_UPDATE){
+ ms_compile(sync);
+ ms_sync_setup(sync);
+ }
+ filter=sync->execution_list;
+ ms_sync_unlock(sync);
+ /* //ms_trace("Calling synchronisation"); */
+ ms_sync_synchronize(sync);
+ while(filter!=NULL)
+ {
+ f=(MSFilter*)filter->data;
+ if (MS_FILTER_GET_CLASS(f)->attributes & FILTER_IS_SOURCE)
+ {
+ /* execute it once */
+ ms_trace("Running source filter %s.",f->klass->name);
+ ms_filter_process(f);
+ }
+ else
+ {
+ /* make the filter process its input data until it has no more */
+ while ( ms_filter_fifos_have_data(f) || ms_filter_queues_have_data(f) )
+ {
+ ms_trace("Running filter %s.",f->klass->name);
+ ms_filter_process(f);
+ }
+ }
+ filter=g_list_next(filter);
+ }
+ ms_sync_lock(sync);
+ }
+ g_cond_signal(sync->stop_cond); /* signal that the sync thread has finished */
+ ms_sync_unlock(sync);
+ g_message("Mediastreamer processing thread is exiting.");
+ return NULL;
+}
+
+/* stop the processing chain attached to a sync source.*/
+void ms_thread_stop(MSSync *sync)
+{
+ if (sync->thread!=NULL)
+ {
+ if (sync->samples_per_tick==0)
+ {
+ /* to wakeup the thread */
+ /* //g_cond_signal(sync->thread_cond); */
+ }
+ g_mutex_lock(sync->lock);
+ sync->run=0;
+ sync->thread=NULL;
+ g_cond_wait(sync->stop_cond,sync->lock);
+ g_mutex_unlock(sync->lock);
+ }
+ /* //g_message("ms_thread_stop() finished."); */
+}
+
+/**
+ * ms_start:
+ * @sync: A synchronisation source to be started.
+ *
+ * Starts a thread that will shedule all processing chains attached to the synchronisation source @sync.
+ *
+ *
+ */
+void ms_start(MSSync *sync)
+{
+ if (sync->run==1) return; /*already running*/
+ ms_compile(sync);
+ ms_sync_setup(sync);
+ /* this is to avoid race conditions, for example:
+ ms_start(sync);
+ ms_oss_write_start(ossw);
+ here tge ossw filter need to be compiled to run ms_oss_write_start()
+ */
+ ms_trace("ms_start: creating new thread.");
+ sync->run=1;
+ sync->thread=g_thread_create((GThreadFunc)ms_thread_run,(gpointer)sync,TRUE,NULL);
+ if (sync->thread==NULL){
+ g_warning("Could not create thread !");
+ }
+}
+
+/**
+ * ms_stop:
+ * @sync: A synchronisation source to be stopped.
+ *
+ * Stop the thread that was sheduling the processing chains attached to the synchronisation source @sync.
+ * The processing chains are kept unchanged, no object is freed. The synchronisation source can be restarted using ms_start().
+ *
+ *
+ */
+void ms_stop(MSSync *sync)
+{
+ ms_thread_stop(sync);
+ ms_sync_unsetup(sync);
+}
+
+
+gint ms_load_plugin(gchar *path)
+{
+#ifdef HAVE_GLIB
+ g_module_open(path,0);
+#endif
+ return 0;
+}
+
+gchar * ms_proc_get_param(gchar *parameter)
+{
+ gchar *file;
+ int fd;
+ int err,len;
+ gchar *p,*begin,*end;
+ gchar *ret;
+ fd=open("/proc/cpuinfo",O_RDONLY);
+ if (fd<0){
+ g_warning("Could not open /proc/cpuinfo.");
+ return NULL;
+ }
+ file=g_malloc(1024);
+ err=read(fd,file,1024);
+ file[err-1]='\0';
+ /* find the parameter */
+ p=strstr(file,parameter);
+ if (p==NULL){
+ /* parameter not found */
+ g_free(file);
+ return NULL;
+ }
+ /* find the following ':' */
+ p=strchr(p,':');
+ if (p==NULL){
+ g_free(file);
+ return NULL;
+ }
+ /* find the value*/
+ begin=p+2;
+ end=strchr(begin,'\n');
+ if (end==NULL) end=strchr(begin,'\0');
+ len=end-begin+1;
+ ret=g_malloc(len+1);
+ snprintf(ret,len,"%s",begin);
+ /* //printf("%s=%s\n",parameter,ret); */
+ g_free(file);
+ return ret;
+}
+
+gint ms_proc_get_type()
+{
+ static int proc_type=0;
+ gchar *value;
+ if (proc_type==0){
+ value=ms_proc_get_param("cpu family");
+ if (value!=NULL) {
+ proc_type=atoi(value);
+ g_free(value);
+ }else return -1;
+ }
+ return proc_type;
+}
+
+gint ms_proc_get_speed()
+{
+ char *value;
+ static int proc_speed=0;
+ if (proc_speed==0){
+ value=ms_proc_get_param("cpu MHz");
+ if (value!=NULL){
+ proc_speed=atoi(value);
+ g_free(value);
+ }else return -1;
+ }
+ /* //printf("proc_speed=%i\n",proc_speed); */
+ return proc_speed;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h
new file mode 100644
index 00000000..51c69b96
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/ms.h
@@ -0,0 +1,81 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MS_H
+#define MS_H
+#include "msfilter.h"
+#include "mssync.h"
+
+
+void ms_init();
+
+/* compile graphs attached to a sync source*/
+int ms_compile(MSSync *source);
+
+
+/* stop the processing chain attached to a sync source.*/
+void ms_thread_stop(MSSync *sync);
+
+
+/**
+ * function_name:ms_thread_run
+ * @sync: The synchronization source for all the set of graphs to run.
+ *
+ * Execute the processing chain attached to a sync source. This function loops indefinitely.
+ * The media streamer programmer can choose to execute this function directly, or to call ms_start(),
+ * that will start a thread for the synchronisation source.
+ *
+ * Returns: no return value.
+ */
+void *ms_thread_run(void *sync);
+
+
+/**
+ * function_name:ms_start
+ * @sync: A synchronisation source to be started.
+ *
+ * Starts a thread that will shedule all processing chains attached to the synchronisation source @sync.
+ *
+ * Returns: no return value.
+ */
+void ms_start(MSSync *sync);
+
+
+/**
+ * function_name:ms_stop
+ * @sync: A synchronisation source to be stopped.
+ *
+ * Stop the thread that was sheduling the processing chains attached to the synchronisation source @sync.
+ * The processing chains are kept unchanged, no object is freed. The synchronisation source can be restarted using ms_start().
+ *
+ * Returns: no return value.
+ */
+void ms_stop(MSSync *sync);
+
+
+gchar * ms_proc_get_param(gchar *parameter);
+gint ms_proc_get_type();
+gint ms_proc_get_speed();
+
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c
new file mode 100644
index 00000000..70cc906e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.c
@@ -0,0 +1,132 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <msAlawdec.h>
+#include <g711common.h>
+
+extern MSFilter * ms_ALAWencoder_new(void);
+
+MSCodecInfo ALAWinfo={
+ {
+ "ALAW codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_ALAWencoder_new,
+ "This is the classic A-law codec. Good quality, but only usable with high speed network connections."
+ },
+ ms_ALAWencoder_new,
+ ms_ALAWdecoder_new,
+ 320,
+ 160,
+ 64000,
+ 8000,
+ 8,
+ "PCMA",
+ 1,
+ 1,
+};
+
+static MSALAWDecoderClass *ms_ALAWdecoder_class=NULL;
+
+MSFilter * ms_ALAWdecoder_new(void)
+{
+ MSALAWDecoder *r;
+
+ r=g_new(MSALAWDecoder,1);
+ ms_ALAWdecoder_init(r);
+ if (ms_ALAWdecoder_class==NULL)
+ {
+ ms_ALAWdecoder_class=g_new(MSALAWDecoderClass,1);
+ ms_ALAWdecoder_class_init(ms_ALAWdecoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ALAWdecoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_ALAWdecoder_init(MSALAWDecoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->r_mingran=ALAW_DECODER_RMAXGRAN;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSALAWDECODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSALAWDECODER_MAX_INPUTS);
+
+}
+
+void ms_ALAWdecoder_class_init(MSALAWDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ALAWDecoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ALAWinfo;
+ MS_FILTER_CLASS(klass)->max_finputs=MSALAWDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSALAWDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=ALAW_DECODER_RMAXGRAN;
+ MS_FILTER_CLASS(klass)->w_maxgran=ALAW_DECODER_WMAXGRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ALAWdecoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ALAWdecoder_process;
+}
+
+void ms_ALAWdecoder_process(MSALAWDecoder *r)
+{
+ MSFifo *fi,*fo;
+ int inlen,outlen;
+ gchar *s,*d;
+ int i;
+ /* process output fifos, but there is only one for this class of filter*/
+
+ /* this is the simplest process function design:
+ the filter declares a r_mingran of ALAW_DECODER_RMAXGRAN, so the mediastreamer's
+ scheduler will call the process function each time there is ALAW_DECODER_RMAXGRAN
+ bytes to read in the input fifo. If there is more, then it will call it several
+ time in order to the fifo to be completetly processed.
+ This is very simple, but not very efficient because of the multiple call function
+ of MSFilterProcessFunc that may happen.
+ The MSAlawEncoder implements another design; see it.
+ */
+
+ fi=r->f_inputs[0];
+ fo=r->f_outputs[0];
+ g_return_if_fail(fi!=NULL);
+ g_return_if_fail(fo!=NULL);
+
+ inlen=ms_fifo_get_read_ptr(fi,ALAW_DECODER_RMAXGRAN,(void**)&s);
+ if (s==NULL) return;
+ outlen=ms_fifo_get_write_ptr(fo,ALAW_DECODER_WMAXGRAN,(void**)&d);
+ if (d!=NULL)
+ {
+ for(i=0;i<ALAW_DECODER_RMAXGRAN;i++)
+ {
+ ((gint16*)d)[i]=alaw_to_s16( (unsigned char) s[i]);
+ }
+ }
+ else g_warning("MSALAWDecoder: Discarding samples !!");
+
+}
+
+
+
+void ms_ALAWdecoder_destroy( MSALAWDecoder *obj)
+{
+ g_free(obj);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h
new file mode 100644
index 00000000..7db4c750
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawdec.h
@@ -0,0 +1,65 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSALAWDECODER_H
+#define MSALAWDECODER_H
+
+#include <msfilter.h>
+#include <mscodec.h>
+
+/*this is the class that implements a ALAWdecoder filter*/
+
+#define MSALAWDECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSALAWDecoder
+{
+ /* the MSALAWDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSALAWDecoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSALAWDECODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSALAWDECODER_MAX_INPUTS];
+} MSALAWDecoder;
+
+typedef struct _MSALAWDecoderClass
+{
+ /* the MSALAWDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSALAWDecoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSALAWDecoderClass;
+
+/* PUBLIC */
+#define MS_ALAWDECODER(filter) ((MSALAWDecoder*)(filter))
+#define MS_ALAWDECODER_CLASS(klass) ((MSALAWDecoderClass*)(klass))
+MSFilter * ms_ALAWdecoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_ALAWdecoder_init(MSALAWDecoder *r);
+void ms_ALAWdecoder_class_init(MSALAWDecoderClass *klass);
+void ms_ALAWdecoder_destroy( MSALAWDecoder *obj);
+void ms_ALAWdecoder_process(MSALAWDecoder *r);
+
+/* tuning parameters :*/
+#define ALAW_DECODER_WMAXGRAN 320
+#define ALAW_DECODER_RMAXGRAN 160
+
+extern MSCodecInfo ALAWinfo;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c
new file mode 100644
index 00000000..fd1f9abe
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.c
@@ -0,0 +1,124 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msAlawenc.h"
+#include "g711common.h"
+
+extern MSCodecInfo ALAWinfo;
+
+static MSALAWEncoderClass *ms_ALAWencoder_class=NULL;
+
+MSFilter * ms_ALAWencoder_new(void)
+{
+ MSALAWEncoder *r;
+
+ r=g_new(MSALAWEncoder,1);
+ ms_ALAWencoder_init(r);
+ if (ms_ALAWencoder_class==NULL)
+ {
+ ms_ALAWencoder_class=g_new(MSALAWEncoderClass,1);
+ ms_ALAWencoder_class_init(ms_ALAWencoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ALAWencoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_ALAWencoder_init(MSALAWEncoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->r_mingran=ALAW_ENCODER_RMAXGRAN; /* the filter can be called as soon as there is
+ something to process */
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSALAWENCODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSALAWENCODER_MAX_INPUTS);
+
+}
+
+void ms_ALAWencoder_class_init(MSALAWEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ALAWEncoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ALAWinfo;
+ MS_FILTER_CLASS(klass)->max_finputs=MSALAWENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSALAWENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=ALAW_ENCODER_RMAXGRAN;
+ MS_FILTER_CLASS(klass)->w_maxgran=ALAW_ENCODER_WMAXGRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ALAWencoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ALAWencoder_process;
+}
+
+void ms_ALAWencoder_process(MSALAWEncoder *r)
+{
+ MSFifo *fi,*fo;
+ int inlen,outlen;
+ gchar *s,*d;
+ int i;
+ /* process output fifos, but there is only one for this class of filter*/
+
+ /* this is the sophisticated design of the process function:
+ Here the filter declares that it can be called as soon as there is something
+ to read on the input fifo by setting r_mingran=0.
+ Then it ask for the fifo to get as many data as possible by calling:
+ inlen=ms_fifo_get_read_ptr(fi,0,(void**)&s);
+ This avoid multiple call to the process function to process all data available
+ on the input fifo... but the writing of the process function is a bit
+ more difficult, because althoug ms_fifo_get_read_ptr() returns N bytes,
+ we cannot ask ms_fifo_get_write_ptr to return N bytes if
+ N>MS_FILTER_CLASS(klass)->w_maxgran. This is forbidden by the MSFifo
+ mechanism.
+ This is an open issue.
+ For the moment what is done here is that ms_fifo_get_write_ptr() is called
+ several time with its maximum granularity in order to try to write the output.
+ ...
+ One solution:
+ -create a new function ms_fifo_get_rw_ptr(fifo1,p1, fifo2,p2) to
+ return the number of bytes able to being processed according to the input
+ and output fifo, and their respective data pointers
+ */
+
+
+ fi=r->f_inputs[0];
+ fo=r->f_outputs[0];
+
+ inlen=ms_fifo_get_read_ptr(fi,ALAW_ENCODER_RMAXGRAN,(void**)&s);
+ if (s==NULL) return;
+ outlen=ms_fifo_get_write_ptr(fo,ALAW_ENCODER_WMAXGRAN,(void**)&d);
+ if (d!=NULL)
+ {
+ for(i=0;i<ALAW_ENCODER_WMAXGRAN;i++)
+ {
+ d[i]=s16_to_alaw( *((gint16*)s) );
+ s+=2;
+ }
+ }
+ else g_warning("MSALAWDecoder: Discarding samples !!");
+
+}
+
+
+
+void ms_ALAWencoder_destroy( MSALAWEncoder *obj)
+{
+ g_free(obj);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h
new file mode 100644
index 00000000..608a9884
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msAlawenc.h
@@ -0,0 +1,64 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSALAWENCODER_H
+#define MSALAWENCODER_H
+
+#include "mscodec.h"
+
+
+/*this is the class that implements a ALAWencoder filter*/
+
+#define MSALAWENCODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSALAWEncoder
+{
+ /* the MSALAWEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSALAWEncoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSALAWENCODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSALAWENCODER_MAX_INPUTS];
+} MSALAWEncoder;
+
+typedef struct _MSALAWEncoderClass
+{
+ /* the MSALAWEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSALAWEncoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSALAWEncoderClass;
+
+/* PUBLIC */
+#define MS_ALAWENCODER(filter) ((MSALAWEncoder*)(filter))
+#define MS_ALAWENCODER_CLASS(klass) ((MSALAWEncoderClass*)(klass))
+MSFilter * ms_ALAWencoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_ALAWencoder_init(MSALAWEncoder *r);
+void ms_ALAWencoder_class_init(MSALAWEncoderClass *klass);
+void ms_ALAWencoder_destroy( MSALAWEncoder *obj);
+void ms_ALAWencoder_process(MSALAWEncoder *r);
+
+/* tuning parameters :*/
+#define ALAW_ENCODER_WMAXGRAN 160
+#define ALAW_ENCODER_RMAXGRAN 320
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h
new file mode 100644
index 00000000..e73fb332
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMdecoder.h
@@ -0,0 +1,64 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSGSMDECODER_H
+#define MSGSMDECODER_H
+
+#include <msfilter.h>
+#include <mscodec.h>
+#include <gsm.h>
+
+/*this is the class that implements a GSMdecoder filter*/
+
+#define MSGSMDECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSGSMDecoder
+{
+ /* the MSGSMDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSGSMDecoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSGSMDECODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSGSMDECODER_MAX_INPUTS];
+ gsm gsm_handle;
+} MSGSMDecoder;
+
+typedef struct _MSGSMDecoderClass
+{
+ /* the MSGSMDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSGSMDecoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSGSMDecoderClass;
+
+/* PUBLIC */
+#define MS_GSMDECODER(filter) ((MSGSMDecoder*)(filter))
+#define MS_GSMDECODER_CLASS(klass) ((MSGSMDecoderClass*)(klass))
+MSFilter * ms_GSMdecoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_GSMdecoder_init(MSGSMDecoder *r);
+void ms_GSMdecoder_class_init(MSGSMDecoderClass *klass);
+void ms_GSMdecoder_destroy( MSGSMDecoder *obj);
+void ms_GSMdecoder_process(MSGSMDecoder *r);
+
+extern MSCodecInfo GSMinfo;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h
new file mode 100644
index 00000000..2deae387
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msGSMencoder.h
@@ -0,0 +1,61 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSGSMENCODER_H
+#define MSGSMENCODER_H
+
+#include "msfilter.h"
+#include <gsm.h>
+
+/*this is the class that implements a GSMencoder filter*/
+
+#define MSGSMENCODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSGSMEncoder
+{
+ /* the MSGSMEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSGSMEncoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSGSMENCODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSGSMENCODER_MAX_INPUTS];
+ gsm gsm_handle;
+} MSGSMEncoder;
+
+typedef struct _MSGSMEncoderClass
+{
+ /* the MSGSMEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSGSMEncoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSGSMEncoderClass;
+
+/* PUBLIC */
+#define MS_GSMENCODER(filter) ((MSGSMEncoder*)(filter))
+#define MS_GSMENCODER_CLASS(klass) ((MSGSMEncoderClass*)(klass))
+MSFilter * ms_GSMencoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_GSMencoder_init(MSGSMEncoder *r);
+void ms_GSMencoder_class_init(MSGSMEncoderClass *klass);
+void ms_GSMencoder_destroy( MSGSMEncoder *obj);
+void ms_GSMencoder_process(MSGSMEncoder *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h
new file mode 100644
index 00000000..59d9deca
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10decoder.h
@@ -0,0 +1,64 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSLPC10DECODER_H
+#define MSLPC10DECODER_H
+
+#include <msfilter.h>
+#include <mscodec.h>
+#include <lpc10.h>
+
+/*this is the class that implements a LPC10decoder filter*/
+
+#define MSLPC10DECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSLPC10Decoder
+{
+ /* the MSLPC10Decoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSLPC10Decoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSLPC10DECODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSLPC10DECODER_MAX_INPUTS];
+ struct lpc10_decoder_state *lpc10_dec;
+} MSLPC10Decoder;
+
+typedef struct _MSLPC10DecoderClass
+{
+ /* the MSLPC10Decoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSLPC10Decoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSLPC10DecoderClass;
+
+/* PUBLIC */
+#define MS_LPC10DECODER(filter) ((MSLPC10Decoder*)(filter))
+#define MS_LPC10DECODER_CLASS(klass) ((MSLPC10DecoderClass*)(klass))
+MSFilter * ms_LPC10decoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_LPC10decoder_init(MSLPC10Decoder *r);
+void ms_LPC10decoder_class_init(MSLPC10DecoderClass *klass);
+void ms_LPC10decoder_destroy( MSLPC10Decoder *obj);
+void ms_LPC10decoder_process(MSLPC10Decoder *r);
+
+extern MSCodecInfo LPC10info;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h
new file mode 100644
index 00000000..4db16436
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msLPC10encoder.h
@@ -0,0 +1,74 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSLPC10ENCODER_H
+#define MSLPC10ENCODER_H
+
+#include "mscodec.h"
+
+
+int
+read_16bit_samples(gint16 int16samples[], float speech[], int n);
+
+int
+write_16bit_samples(gint16 int16samples[], float speech[], int n);
+
+void
+write_bits(unsigned char *data, gint32 *bits, int len);
+
+int
+read_bits(unsigned char *data, gint32 *bits, int len);
+
+
+/*this is the class that implements a LPC10encoder filter*/
+
+#define MSLPC10ENCODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSLPC10Encoder
+{
+ /* the MSLPC10Encoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSLPC10Encoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSLPC10ENCODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSLPC10ENCODER_MAX_INPUTS];
+ struct lpc10_encoder_state *lpc10_enc;
+} MSLPC10Encoder;
+
+typedef struct _MSLPC10EncoderClass
+{
+ /* the MSLPC10Encoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSLPC10Encoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSLPC10EncoderClass;
+
+/* PUBLIC */
+#define MS_LPC10ENCODER(filter) ((MSLPC10Encoder*)(filter))
+#define MS_LPC10ENCODER_CLASS(klass) ((MSLPC10EncoderClass*)(klass))
+MSFilter * ms_LPC10encoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_LPC10encoder_init(MSLPC10Encoder *r);
+void ms_LPC10encoder_class_init(MSLPC10EncoderClass *klass);
+void ms_LPC10encoder_destroy( MSLPC10Encoder *obj);
+void ms_LPC10encoder_process(MSLPC10Encoder *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c
new file mode 100644
index 00000000..500f2389
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.c
@@ -0,0 +1,130 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <msMUlawdec.h>
+#include <g711common.h>
+
+extern MSFilter * ms_MULAWencoder_new(void);
+
+MSCodecInfo MULAWinfo={
+ {
+ "MULAW codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_MULAWencoder_new,
+ "This is the classic Mu-law codec. Good quality, but only usable with high speed network connections."
+ },
+ ms_MULAWencoder_new,
+ ms_MULAWdecoder_new,
+ 320,
+ 160,
+ 64000,
+ 8000,
+ 0,
+ "PCMU",
+ 1,
+ 1
+};
+
+static MSMULAWDecoderClass *ms_MULAWdecoder_class=NULL;
+
+MSFilter * ms_MULAWdecoder_new(void)
+{
+ MSMULAWDecoder *r;
+
+ r=g_new(MSMULAWDecoder,1);
+ ms_MULAWdecoder_init(r);
+ if (ms_MULAWdecoder_class==NULL)
+ {
+ ms_MULAWdecoder_class=g_new(MSMULAWDecoderClass,1);
+ ms_MULAWdecoder_class_init(ms_MULAWdecoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_MULAWdecoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_MULAWdecoder_init(MSMULAWDecoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->r_mingran=MULAW_DECODER_RMAXGRAN;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSMULAWDECODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSMULAWDECODER_MAX_INPUTS);
+
+}
+
+void ms_MULAWdecoder_class_init(MSMULAWDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"MULAWDecoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&MULAWinfo;
+ MS_FILTER_CLASS(klass)->max_finputs=MSMULAWDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSMULAWDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=MULAW_DECODER_RMAXGRAN;
+ MS_FILTER_CLASS(klass)->w_maxgran=MULAW_DECODER_WMAXGRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_MULAWdecoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_MULAWdecoder_process;
+}
+
+void ms_MULAWdecoder_process(MSMULAWDecoder *r)
+{
+ MSFifo *fi,*fo;
+ int inlen,outlen;
+ gchar *s,*d;
+ int i;
+ /* process output fifos, but there is only one for this class of filter*/
+
+ /* this is the simplest process function design:
+ the filter declares a r_mingran of MULAW_DECODER_RMAXGRAN, so the mediastreamer's
+ scheduler will call the process function each time there is MULAW_DECODER_RMAXGRAN
+ bytes to read in the input fifo. If there is more, then it will call it several
+ time in order to the fifo to be completetly processed.
+ This is very simple, but not very efficient because of the multiple call function
+ of MSFilterProcessFunc that may happen.
+ The MSAlawEncoder implements another design; see it.
+ */
+
+ fi=r->f_inputs[0];
+ fo=r->f_outputs[0];
+
+ inlen=ms_fifo_get_read_ptr(fi,MULAW_DECODER_RMAXGRAN,(void**)&s);
+ if (s==NULL) g_error("ms_MULAWdecoder_process: internal error.");
+ outlen=ms_fifo_get_write_ptr(fo,MULAW_DECODER_WMAXGRAN,(void**)&d);
+ if (d!=NULL)
+ {
+ for(i=0;i<MULAW_DECODER_RMAXGRAN;i++)
+ {
+ *((gint16*)d)=ulaw_to_s16( (unsigned char) s[i]);
+ d+=2;
+ }
+ }
+ else g_warning("MSMULAWDecoder: Discarding samples !!");
+}
+
+
+
+void ms_MULAWdecoder_destroy( MSMULAWDecoder *obj)
+{
+ g_free(obj);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h
new file mode 100644
index 00000000..c135d21d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawdec.h
@@ -0,0 +1,66 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSMULAWDECODER_H
+#define MSMULAWDECODER_H
+
+#include <msfilter.h>
+#include <mscodec.h>
+
+/*this is the class that implements a MULAWdecoder filter*/
+
+#define MSMULAWDECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSMULAWDecoder
+{
+ /* the MSMULAWDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSMULAWDecoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSMULAWDECODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSMULAWDECODER_MAX_INPUTS];
+} MSMULAWDecoder;
+
+typedef struct _MSMULAWDecoderClass
+{
+ /* the MSMULAWDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSMULAWDecoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSMULAWDecoderClass;
+
+/* PUBLIC */
+#define MS_MULAWDECODER(filter) ((MSMULAWDecoder*)(filter))
+#define MS_MULAWDECODER_CLASS(klass) ((MSMULAWDecoderClass*)(klass))
+MSFilter * ms_MULAWdecoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_MULAWdecoder_init(MSMULAWDecoder *r);
+void ms_MULAWdecoder_class_init(MSMULAWDecoderClass *klass);
+void ms_MULAWdecoder_destroy( MSMULAWDecoder *obj);
+void ms_MULAWdecoder_process(MSMULAWDecoder *r);
+
+/* tuning parameters :*/
+#define MULAW_DECODER_WMAXGRAN 320
+#define MULAW_DECODER_RMAXGRAN 160
+
+extern MSCodecInfo MULAWinfo;
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c
new file mode 100644
index 00000000..2f740d89
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.c
@@ -0,0 +1,99 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msMUlawenc.h"
+#include "g711common.h"
+
+extern MSCodecInfo MULAWinfo;
+
+static MSMULAWEncoderClass *ms_MULAWencoder_class=NULL;
+
+MSFilter * ms_MULAWencoder_new(void)
+{
+ MSMULAWEncoder *r;
+
+ r=g_new(MSMULAWEncoder,1);
+ ms_MULAWencoder_init(r);
+ if (ms_MULAWencoder_class==NULL)
+ {
+ ms_MULAWencoder_class=g_new(MSMULAWEncoderClass,1);
+ ms_MULAWencoder_class_init(ms_MULAWencoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_MULAWencoder_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_MULAWencoder_init(MSMULAWEncoder *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->r_mingran=MULAW_ENCODER_RMAXGRAN; /* the filter can be called as soon as there is
+ something to process */
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSMULAWENCODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSMULAWENCODER_MAX_INPUTS);
+
+}
+
+void ms_MULAWencoder_class_init(MSMULAWEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"MULAWEncoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&MULAWinfo;
+ MS_FILTER_CLASS(klass)->max_finputs=MSMULAWENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSMULAWENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=MULAW_ENCODER_RMAXGRAN;
+ MS_FILTER_CLASS(klass)->w_maxgran=MULAW_ENCODER_WMAXGRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_MULAWencoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_MULAWencoder_process;
+}
+
+void ms_MULAWencoder_process(MSMULAWEncoder *r)
+{
+ MSFifo *fi,*fo;
+ int inlen,outlen;
+ gchar *s,*d;
+ int i;
+ /* process output fifos, but there is only one for this class of filter*/
+
+ fi=r->f_inputs[0];
+ fo=r->f_outputs[0];
+ inlen=ms_fifo_get_read_ptr(fi,MULAW_ENCODER_RMAXGRAN,(void**)&s);
+ outlen=ms_fifo_get_write_ptr(fo,MULAW_ENCODER_WMAXGRAN,(void**)&d);
+ if (d!=NULL)
+ {
+ for(i=0;i<MULAW_ENCODER_WMAXGRAN;i++)
+ {
+ d[i]=s16_to_ulaw( *((gint16*)s) );
+ s+=2;
+ }
+ }
+ else g_warning("MSMULAWDecoder: Discarding samples !!");
+}
+
+
+
+void ms_MULAWencoder_destroy( MSMULAWEncoder *obj)
+{
+ g_free(obj);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h
new file mode 100644
index 00000000..52f76661
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msMUlawenc.h
@@ -0,0 +1,63 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSMULAWENCODER_H
+#define MSMULAWENCODER_H
+
+#include "mscodec.h"
+
+
+/*this is the class that implements a MULAWencoder filter*/
+
+#define MSMULAWENCODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSMULAWEncoder
+{
+ /* the MSMULAWEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSMULAWEncoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSMULAWENCODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSMULAWENCODER_MAX_INPUTS];
+} MSMULAWEncoder;
+
+typedef struct _MSMULAWEncoderClass
+{
+ /* the MSMULAWEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSMULAWEncoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSMULAWEncoderClass;
+
+/* PUBLIC */
+#define MS_MULAWENCODER(filter) ((MSMULAWEncoder*)(filter))
+#define MS_MULAWENCODER_CLASS(klass) ((MSMULAWEncoderClass*)(klass))
+MSFilter * ms_MULAWencoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_MULAWencoder_init(MSMULAWEncoder *r);
+void ms_MULAWencoder_class_init(MSMULAWEncoderClass *klass);
+void ms_MULAWencoder_destroy( MSMULAWEncoder *obj);
+void ms_MULAWencoder_process(MSMULAWEncoder *r);
+
+/* tuning parameters :*/
+#define MULAW_ENCODER_WMAXGRAN 160
+#define MULAW_ENCODER_RMAXGRAN 320
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h
new file mode 100644
index 00000000..e7c880be
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavdecoder.h
@@ -0,0 +1,87 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSAVDECODER_H
+#define MSAVDECODER_H
+
+#include "msfilter.h"
+
+
+#include <avcodec.h>
+
+/*this is the class that implements a AVdecoder filter*/
+
+#define MSAVDECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+struct _MSAVDecoder
+{
+ /* the MSAVDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSAVDecoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSQueue *q_inputs[MSAVDECODER_MAX_INPUTS];
+ MSQueue *q_outputs[MSAVDECODER_MAX_INPUTS];
+ AVCodec *av_codec; /*the AVCodec from which this MSFilter is related */
+ AVCodecContext av_context; /* the context of the AVCodec */
+ gint av_opened;
+ int output_pix_fmt;
+ int width;
+ int height;
+ int skip_gob;
+ unsigned char buf_compressed[100000];
+ int buf_size;
+ MSBuffer *obufwrap; /* alternate buffer, when format change is needed*/
+};
+
+typedef struct _MSAVDecoder MSAVDecoder;
+
+struct _MSAVDecoderClass
+{
+ /* the MSAVDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSAVDecoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSAVDecoderClass MSAVDecoderClass;
+
+/* PUBLIC */
+#define MS_AVDECODER(filter) ((MSAVDecoder*)(filter))
+#define MS_AVDECODER_CLASS(klass) ((MSAVDecoderClass*)(klass))
+
+MSFilter *ms_h263_decoder_new();
+MSFilter *ms_mpeg_decoder_new();
+MSFilter *ms_mpeg4_decoder_new();
+MSFilter * ms_AVdecoder_new_with_codec(enum CodecID codec_id);
+
+gint ms_AVdecoder_set_format(MSAVDecoder *dec, gchar *fmt);
+void ms_AVdecoder_set_width(MSAVDecoder *av,gint w);
+void ms_AVdecoder_set_height(MSAVDecoder *av,gint h);
+
+/* FOR INTERNAL USE*/
+void ms_AVdecoder_init(MSAVDecoder *r, AVCodec *codec);
+void ms_AVdecoder_uninit(MSAVDecoder *enc);
+void ms_AVdecoder_class_init(MSAVDecoderClass *klass);
+void ms_AVdecoder_destroy( MSAVDecoder *obj);
+void ms_AVdecoder_process(MSAVDecoder *r);
+
+void ms_AVCodec_init();
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h
new file mode 100644
index 00000000..6fe5cad4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msavencoder.h
@@ -0,0 +1,90 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSAVENCODER_H
+#define MSAVENCODER_H
+
+#include "msfilter.h"
+#include "mscodec.h"
+#include <avcodec.h>
+
+/*this is the class that implements a AVencoder filter*/
+
+#define MSAVENCODER_MAX_INPUTS 1 /* max output per filter*/
+#define MSAVENCODER_MAX_OUTPUTS 2
+
+struct _MSAVEncoder
+{
+ /* the MSAVEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSAVEncoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSQueue *q_inputs[MSAVENCODER_MAX_INPUTS];
+ MSQueue *q_outputs[MSAVENCODER_MAX_OUTPUTS];
+ AVCodec *av_codec; /*the AVCodec from which this MSFilter is related */
+ AVCodecContext av_context; /* the context of the AVCodec */
+ gint input_pix_fmt;
+ gint av_opened;
+ MSBuffer *comp_buf;
+ MSBuffer *yuv_buf;
+};
+
+typedef struct _MSAVEncoder MSAVEncoder;
+/* MSAVEncoder always outputs planar YUV and accept any incoming format you should setup using
+ ms_AVencoder_set_format()
+q_outputs[0] is the compressed video stream output
+q_outputs[1] is a YUV planar buffer of the image it receives in input.
+*/
+
+
+struct _MSAVEncoderClass
+{
+ /* the MSAVEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSAVEncoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSAVEncoderClass MSAVEncoderClass;
+
+/* PUBLIC */
+#define MS_AVENCODER(filter) ((MSAVEncoder*)(filter))
+#define MS_AVENCODER_CLASS(klass) ((MSAVEncoderClass*)(klass))
+
+MSFilter *ms_h263_encoder_new();
+MSFilter *ms_mpeg_encoder_new();
+MSFilter *ms_mpeg4_encoder_new();
+MSFilter * ms_AVencoder_new_with_codec(enum CodecID codec_id, MSCodecInfo *info);
+
+gint ms_AVencoder_set_format(MSAVEncoder *enc, gchar *fmt);
+
+#define ms_AVencoder_set_width(av,w) (av)->av_context.width=(w)
+#define ms_AVencoder_set_height(av,h) (av)->av_context.height=(h)
+#define ms_AVencoder_set_bit_rate(av,r) (av)->av_context.bit_rate=(r)
+
+void ms_AVencoder_set_frame_rate(MSAVEncoder *enc, gint frame_rate, gint frame_rate_base);
+
+/* FOR INTERNAL USE*/
+void ms_AVencoder_init(MSAVEncoder *r, AVCodec *codec);
+void ms_AVencoder_uninit(MSAVEncoder *enc);
+void ms_AVencoder_class_init(MSAVEncoderClass *klass);
+void ms_AVencoder_destroy( MSAVEncoder *obj);
+void ms_AVencoder_process(MSAVEncoder *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c
new file mode 100644
index 00000000..4ca3c925
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.c
@@ -0,0 +1,94 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msbuffer.h"
+#include "msutils.h"
+#include <string.h>
+
+
+
+MSBuffer * ms_buffer_new(guint32 size)
+{
+ MSBuffer *buf;
+ buf=(MSBuffer*)g_malloc(sizeof(MSBuffer)+size);
+ buf->ref_count=0;
+ buf->size=size;
+ ms_trace("ms_buffer_new: Allocating buffer of %i bytes.",size);
+ /* allocate the data buffer: there is a lot of optmisation that can be done by using a pool of cached buffers*/
+ buf->buffer=((char*)(buf))+sizeof(MSBuffer); /* to avoid to do two allocations,
+ buffer info and buffer are contigous.*/
+ buf->flags=MS_BUFFER_CONTIGUOUS;
+ return(buf);
+}
+
+MSBuffer *ms_buffer_alloc(gint flags)
+{
+ MSBuffer *buf;
+ buf=(MSBuffer*)g_malloc(sizeof(MSBuffer));
+ buf->ref_count=0;
+ buf->size=0;
+ buf->buffer=NULL;
+ buf->flags=0;
+ return(buf);
+}
+
+
+void ms_buffer_destroy(MSBuffer *buf)
+{
+ if (buf->flags & MS_BUFFER_CONTIGUOUS){
+ g_free(buf);
+ }
+ else {
+ g_free(buf->buffer);
+ g_free(buf);
+ }
+}
+
+MSMessage *ms_message_alloc()
+{
+ MSMessage *m=g_malloc(sizeof(MSMessage));
+ memset(m,0,sizeof(MSMessage));
+ return m;
+}
+
+MSMessage *ms_message_new(gint size)
+{
+ MSMessage *m=ms_message_alloc();
+ MSBuffer *buf=ms_buffer_new(size);
+ ms_message_set_buf(m,buf);
+ return m;
+}
+
+void ms_message_destroy(MSMessage *m)
+{
+ /* the buffer is freed if its ref_count goes to zero */
+ if (m->buffer!=NULL){
+ m->buffer->ref_count--;
+ if (m->buffer->ref_count==0) ms_buffer_destroy(m->buffer);
+ }
+ g_free(m);
+}
+
+MSMessage * ms_message_dup(MSMessage *m)
+{
+ MSMessage *msg=ms_message_alloc();
+ ms_message_set_buf(msg,m->buffer);
+ return msg;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h
new file mode 100644
index 00000000..f96b35a7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msbuffer.h
@@ -0,0 +1,75 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSBUFFER_H
+#define MSBUFFER_H
+#include <config.h>
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#else
+#include <uglib.h>
+#endif
+
+
+#define MS_BUFFER_LARGE 4092
+
+
+typedef struct _MSBuffer
+{
+ gchar *buffer;
+ guint32 size;
+ guint16 ref_count;
+ guint16 flags;
+#define MS_BUFFER_CONTIGUOUS (1)
+}MSBuffer;
+
+MSBuffer * ms_buffer_new(guint32 size);
+void ms_buffer_destroy(MSBuffer *buf);
+
+struct _MSMessage
+{
+ MSBuffer *buffer; /* points to a MSBuffer */
+ void *data; /*points to buffer->buffer */
+ guint32 size; /* the size of the buffer to read in data. It may not be the
+ physical size (I mean buffer->buffer->size */
+ struct _MSMessage *next;
+ struct _MSMessage *prev; /* MSMessage are queued into MSQueues */
+};
+
+typedef struct _MSMessage MSMessage;
+
+
+MSBuffer *ms_buffer_alloc(gint flags);
+MSMessage *ms_message_new(gint size);
+
+#define ms_message_set_buf(m,b) do { (b)->ref_count++; (m)->buffer=(b); (m)->data=(b)->buffer; (m)->size=(b)->size; }while(0)
+#define ms_message_unset_buf(m) do { (m)->buffer->ref_count--; (m)->buffer=NULL; (m)->size=0; (m)->data=NULL; } while(0)
+
+#define ms_message_size(m) (m)->size
+void ms_message_destroy(MSMessage *m);
+
+MSMessage * ms_message_dup(MSMessage *m);
+
+/* allocate a single message without buffer */
+MSMessage *ms_message_alloc();
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c
new file mode 100644
index 00000000..dafa1e87
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.c
@@ -0,0 +1,250 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mscodec.h"
+#include "msMUlawdec.h"
+
+#ifdef TRUESPEECH
+extern MSCodecInfo TrueSpeechinfo;
+#endif
+
+#ifdef VIDEO_ENABLED
+extern void ms_AVCodec_init();
+#endif
+
+#define UDP_HDR_SZ 8
+#define RTP_HDR_SZ 12
+#define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/
+
+
+
+
+/* register all statically linked codecs */
+void ms_codec_register_all()
+{
+/*// ms_filter_register(MS_FILTER_INFO(&GSMinfo));
+ // ms_filter_register(MS_FILTER_INFO(&LPC10info));*/
+ ms_filter_register(MS_FILTER_INFO(&MULAWinfo));
+#ifdef TRUESPEECH
+ ms_filter_register(MS_FILTER_INFO(&TrueSpeechinfo));
+#endif
+#ifdef VIDEO_ENABLED
+ ms_AVCodec_init();
+#endif
+
+}
+
+/* returns a list of MSCodecInfo */
+GList * ms_codec_get_all_audio()
+{
+ GList *audio_codecs=NULL;
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if (info->type==MS_FILTER_AUDIO_CODEC){
+ audio_codecs=g_list_append(audio_codecs,info);
+ }
+ elem=g_list_next(elem);
+ }
+ return audio_codecs;
+}
+
+
+MSCodecInfo * ms_audio_codec_info_get(gchar *name)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ( (info->type==MS_FILTER_AUDIO_CODEC) ){
+ MSCodecInfo *codinfo=(MSCodecInfo *)info;
+ if (strcmp(codinfo->description,name)==0){
+ return MS_CODEC_INFO(info);
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSCodecInfo * ms_video_codec_info_get(gchar *name)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ( (info->type==MS_FILTER_VIDEO_CODEC) ){
+ MSCodecInfo *codinfo=(MSCodecInfo *)info;
+ if (strcmp(codinfo->description,name)==0){
+ return MS_CODEC_INFO(info);
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+/* returns a list of MSCodecInfo */
+GList * ms_codec_get_all_video()
+{
+ GList *video_codecs=NULL;
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if (info->type==MS_FILTER_VIDEO_CODEC){
+ video_codecs=g_list_append(video_codecs,info);
+ }
+ elem=g_list_next(elem);
+ }
+ return video_codecs;
+}
+
+MSFilter * ms_encoder_new(gchar *name)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (strcmp(info->name,name)==0){
+ return codinfo->encoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSFilter * ms_decoder_new(gchar *name)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (strcmp(info->name,name)==0){
+ return codinfo->decoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSFilter * ms_encoder_new_with_pt(gint pt)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (codinfo->pt==pt){
+ return codinfo->encoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSFilter * ms_decoder_new_with_pt(gint pt)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (codinfo->pt==pt){
+ return codinfo->decoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSFilter * ms_decoder_new_with_string_id(gchar *id)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (strcasecmp(codinfo->description,id)==0){
+ return codinfo->decoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+
+MSFilter * ms_encoder_new_with_string_id(gchar *id)
+{
+ GList *elem=filter_list;
+ MSFilterInfo *info;
+ while (elem!=NULL)
+ {
+ info=(MSFilterInfo *)elem->data;
+ if ((info->type==MS_FILTER_AUDIO_CODEC) || (info->type==MS_FILTER_VIDEO_CODEC)){
+ MSCodecInfo *codinfo=(MSCodecInfo *)elem->data;
+ if (strcasecmp(codinfo->description,id)==0){
+ return codinfo->encoder();
+ }
+ }
+ elem=g_list_next(elem);
+ }
+ return NULL;
+}
+/* return 0 if codec can be used with bandwidth, -1 else*/
+int ms_codec_is_usable(MSCodecInfo *codec,double bandwidth)
+{
+ double codec_band;
+ double npacket;
+ double packet_size;
+
+ if (((MSFilterInfo*)codec)->type==MS_FILTER_AUDIO_CODEC)
+ {
+ /* calculate the total bandwdith needed by codec (including headers for rtp, udp, ip)*/
+ /* number of packet per second*/
+ npacket=2.0*(double)(codec->rate)/(double)(codec->fr_size);
+ packet_size=(double)(codec->dt_size)+UDP_HDR_SZ+RTP_HDR_SZ+IP4_HDR_SZ;
+ codec_band=packet_size*8.0*npacket;
+ }
+ else return -1;
+ return(codec_band<bandwidth);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h
new file mode 100644
index 00000000..6c6847d8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscodec.h
@@ -0,0 +1,67 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSCODEC_H
+#define MSCODEC_H
+
+#include "msfilter.h"
+
+struct _MSCodecInfo
+{
+ MSFilterInfo info;
+ MSFilterNewFunc encoder;
+ MSFilterNewFunc decoder;
+ gint fr_size; /* size in char of the uncompressed frame */
+ gint dt_size; /* size in char of the compressed frame */
+ gint bitrate; /* the minimum bit rate in bits/second */
+ gint rate; /*frequency */
+ gint pt; /* the payload type number associated with this codec*/
+ gchar *description; /* a rtpmap field to describe the codec */
+ guint is_usable:1; /* linphone set this flag to remember if it can use this codec considering the total bandwidth*/
+ guint is_selected:1; /* linphone (user) set this flag if he allows this codec to be used*/
+};
+
+typedef struct _MSCodecInfo MSCodecInfo;
+
+MSFilter * ms_encoder_new(gchar *name);
+MSFilter * ms_decoder_new(gchar *name);
+
+MSFilter * ms_encoder_new_with_pt(gint pt);
+MSFilter * ms_decoder_new_with_pt(gint pt);
+
+MSFilter * ms_encoder_new_with_string_id(gchar *id);
+MSFilter * ms_decoder_new_with_string_id(gchar *id);
+
+/* return 0 if codec can be used with bandwidth, -1 else*/
+int ms_codec_is_usable(MSCodecInfo *codec,double bandwidth);
+
+GList * ms_codec_get_all_audio();
+
+GList * ms_codec_get_all_video();
+
+MSCodecInfo * ms_audio_codec_info_get(gchar *name);
+MSCodecInfo * ms_video_codec_info_get(gchar *name);
+
+/* register all statically linked codecs */
+void ms_codec_register_all();
+
+#define MS_CODEC_INFO(codinfo) ((MSCodecInfo*)codinfo)
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c
new file mode 100644
index 00000000..3040b2e2
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.c
@@ -0,0 +1,96 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mscopy.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+
+static MSCopyClass *ms_copy_class=NULL;
+
+MSFilter * ms_copy_new(void)
+{
+ MSCopy *r;
+
+ r=g_new(MSCopy,1);
+ ms_copy_init(r);
+ if (ms_copy_class==NULL)
+ {
+ ms_copy_class=g_new(MSCopyClass,1);
+ ms_copy_class_init(ms_copy_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_copy_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_copy_init(MSCopy *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->r_mingran=MSCOPY_DEF_GRAN;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSCOPY_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSCOPY_MAX_INPUTS);
+}
+
+void ms_copy_class_init(MSCopyClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"fifocopier");
+ MS_FILTER_CLASS(klass)->max_finputs=MSCOPY_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSCOPY_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=MSCOPY_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->w_maxgran=MSCOPY_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_copy_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_copy_process;
+}
+
+void ms_copy_process(MSCopy *r)
+{
+ MSFifo *fi,*fo;
+ int err1;
+ gint gran=MS_FILTER(r)->klass->r_maxgran;
+ void *s,*d;
+
+ /* process output fifos, but there is only one for this class of filter*/
+
+ fi=r->f_inputs[0];
+ fo=r->f_outputs[0];
+ if (fi!=NULL)
+ {
+ err1=ms_fifo_get_read_ptr(fi,gran,&s);
+ if (err1>0) err1=ms_fifo_get_write_ptr(fo,gran,&d);
+ if (err1>0)
+ {
+ memcpy(d,s,gran);
+ }
+ }
+}
+
+void ms_copy_destroy( MSCopy *obj)
+{
+ g_free(obj);
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h
new file mode 100644
index 00000000..2b5749b9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mscopy.h
@@ -0,0 +1,61 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSCOPY_H
+#define MSCOPY_H
+
+#include "msfilter.h"
+
+
+/*this is the class that implements a copy filter*/
+
+#define MSCOPY_MAX_INPUTS 1 /* max output per filter*/
+
+#define MSCOPY_DEF_GRAN 64 /* the default granularity*/
+
+typedef struct _MSCopy
+{
+ /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSCOPY_MAX_INPUTS];
+ MSFifo *f_outputs[MSCOPY_MAX_INPUTS];
+} MSCopy;
+
+typedef struct _MSCopyClass
+{
+ /* the MSCopy derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSCopyClass;
+
+/* PUBLIC */
+#define MS_COPY(filter) ((MSCopy*)(filter))
+#define MS_COPY_CLASS(klass) ((MSCopyClass*)(klass))
+MSFilter * ms_copy_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_copy_init(MSCopy *r);
+void ms_copy_class_init(MSCopyClass *klass);
+void ms_copy_destroy( MSCopy *obj);
+void ms_copy_process(MSCopy *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c
new file mode 100644
index 00000000..692bbb7b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.c
@@ -0,0 +1,94 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 dispatcher 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 "msfdispatcher.h"
+
+static MSFdispatcherClass *ms_fdispatcher_class=NULL;
+
+MSFilter * ms_fdispatcher_new(void)
+{
+ MSFdispatcher *obj;
+ obj=g_malloc(sizeof(MSFdispatcher));
+ if (ms_fdispatcher_class==NULL){
+ ms_fdispatcher_class=g_malloc(sizeof(MSFdispatcherClass));
+ ms_fdispatcher_class_init(ms_fdispatcher_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_fdispatcher_class);
+ ms_fdispatcher_init(obj);
+ return MS_FILTER(obj);
+}
+
+
+void ms_fdispatcher_init(MSFdispatcher *obj)
+{
+ ms_filter_init(MS_FILTER(obj));
+ MS_FILTER(obj)->infifos=obj->f_inputs;
+ MS_FILTER(obj)->outfifos=obj->f_outputs;
+ MS_FILTER(obj)->r_mingran=MS_FDISPATCHER_DEF_GRAN;
+ memset(obj->f_inputs,0,sizeof(MSFifo*)*MS_FDISPATCHER_MAX_INPUTS);
+ memset(obj->f_outputs,0,sizeof(MSFifo*)*MS_FDISPATCHER_MAX_OUTPUTS);
+}
+
+
+
+void ms_fdispatcher_class_init(MSFdispatcherClass *klass)
+{
+ MSFilterClass *parent_class=MS_FILTER_CLASS(klass);
+ ms_filter_class_init(parent_class);
+ ms_filter_class_set_name(parent_class,"fdispatcher");
+ parent_class->max_finputs=MS_FDISPATCHER_MAX_INPUTS;
+ parent_class->max_foutputs=MS_FDISPATCHER_MAX_OUTPUTS;
+ parent_class->r_maxgran=MS_FDISPATCHER_DEF_GRAN;
+ parent_class->w_maxgran=MS_FDISPATCHER_DEF_GRAN;
+ parent_class->destroy=(MSFilterDestroyFunc)ms_fdispatcher_destroy;
+ parent_class->process=(MSFilterProcessFunc)ms_fdispatcher_process;
+}
+
+
+void ms_fdispatcher_destroy( MSFdispatcher *obj)
+{
+ g_free(obj);
+}
+
+void ms_fdispatcher_process(MSFdispatcher *obj)
+{
+ gint i;
+ MSFifo *inf=obj->f_inputs[0];
+
+
+ if (inf!=NULL){
+ void *s,*d;
+ /* dispatch fifos */
+ while ( ms_fifo_get_read_ptr(inf,MS_FDISPATCHER_DEF_GRAN,&s) >0 ){
+ for (i=0;i<MS_FDISPATCHER_MAX_OUTPUTS;i++){
+ MSFifo *outf=obj->f_outputs[i];
+
+ if (outf!=NULL)
+ {
+ ms_fifo_get_write_ptr(outf,MS_FDISPATCHER_DEF_GRAN,&d);
+ if (d!=NULL) memcpy(d,s,MS_FDISPATCHER_DEF_GRAN);
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h
new file mode 100644
index 00000000..b1b457df
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfdispatcher.h
@@ -0,0 +1,61 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 dispatcher 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 MSFDISPATCHER_H
+#define MSFDISPATCHER_H
+
+#include "msfilter.h"
+
+
+/*this is the class that implements a fdispatcher filter*/
+
+#define MS_FDISPATCHER_MAX_INPUTS 1
+#define MS_FDISPATCHER_MAX_OUTPUTS 5
+#define MS_FDISPATCHER_DEF_GRAN 64 /* the default granularity*/
+
+typedef struct _MSFdispatcher
+{
+ /* the MSFdispatcher derivates from MSFilter, so the MSFilter object MUST be the first of the MSFdispatcher object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MS_FDISPATCHER_MAX_INPUTS];
+ MSFifo *f_outputs[MS_FDISPATCHER_MAX_OUTPUTS];
+} MSFdispatcher;
+
+typedef struct _MSFdispatcherClass
+{
+ /* the MSFdispatcher derivates from MSFilter, so the MSFilter class MUST be the first of the MSFdispatcher class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSFdispatcherClass;
+
+/* PUBLIC */
+#define MS_FDISPATCHER(filter) ((MSFdispatcher*)(filter))
+#define MS_FDISPATCHER_CLASS(klass) ((MSFdispatcherClass*)(klass))
+MSFilter * ms_fdispatcher_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_fdispatcher_init(MSFdispatcher *r);
+void ms_fdispatcher_class_init(MSFdispatcherClass *klass);
+void ms_fdispatcher_destroy( MSFdispatcher *obj);
+void ms_fdispatcher_process(MSFdispatcher *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c
new file mode 100644
index 00000000..7e783c24
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.c
@@ -0,0 +1,168 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <errno.h>
+#include <string.h>
+#include "msutils.h"
+#include "msfifo.h"
+
+MSFifo * ms_fifo_new(MSBuffer *buf, gint r_gran, gint w_gran, gint r_offset, gint w_offset)
+{
+ MSFifo *fifo;
+ gint saved_offset=MAX(r_gran+r_offset,w_offset);
+
+ g_return_val_if_fail(saved_offset<=(buf->size),NULL);
+ fifo=g_malloc(sizeof(MSFifo));
+ fifo->buffer=buf;
+ fifo->r_gran=r_gran;
+ fifo->w_gran=w_gran;
+ fifo->begin=fifo->wr_ptr=fifo->rd_ptr=buf->buffer+saved_offset;
+ fifo->readsize=0;
+ fifo->size=fifo->writesize=buf->size-saved_offset;
+ fifo->saved_offset= saved_offset;
+ fifo->r_end=fifo->w_end=buf->buffer+buf->size;
+ fifo->pre_end=fifo->w_end-saved_offset;
+ buf->ref_count++;
+ fifo->prev_data=NULL;
+ fifo->next_data=NULL;
+ ms_trace("fifo base=%x, begin=%x, end=%x, saved_offset=%i, size=%i"
+ ,fifo->buffer->buffer,fifo->begin,fifo->w_end,fifo->saved_offset,fifo->size);
+ return(fifo);
+}
+
+MSFifo * ms_fifo_new_with_buffer(gint r_gran, gint w_gran, gint r_offset, gint w_offset,
+ gint min_fifo_size)
+{
+ MSFifo *fifo;
+ MSBuffer *buf;
+ gint saved_offset=MAX(r_gran+r_offset,w_offset);
+ gint fifo_size;
+ gint tmp;
+ if (min_fifo_size==0) min_fifo_size=w_gran;
+
+ /* we must allocate a fifo with a size multiple of min_fifo_size,
+ with a saved_offset */
+ if (min_fifo_size>MS_BUFFER_LARGE)
+ fifo_size=(min_fifo_size) + saved_offset;
+ else fifo_size=(6*min_fifo_size) + saved_offset;
+ buf=ms_buffer_new(fifo_size);
+ fifo=ms_fifo_new(buf,r_gran,w_gran,r_offset,w_offset);
+ ms_trace("fifo_size=%i",fifo_size);
+ return(fifo);
+}
+
+void ms_fifo_destroy( MSFifo *fifo)
+{
+ g_free(fifo);
+}
+
+void ms_fifo_destroy_with_buffer(MSFifo *fifo)
+{
+ ms_buffer_destroy(fifo->buffer);
+ ms_fifo_destroy(fifo);
+}
+
+gint ms_fifo_get_read_ptr(MSFifo *fifo, gint bsize, void **ret_ptr)
+{
+ gchar *rnext;
+
+ *ret_ptr=NULL;
+ /* //ms_trace("ms_fifo_get_read_ptr: entering.");*/
+ g_return_val_if_fail(bsize<=fifo->r_gran,-EINVAL);
+
+ if (bsize>fifo->readsize)
+ {
+ ms_trace("Not enough data: bsize=%i, readsize=%i",bsize,fifo->readsize);
+ return (-ENODATA);
+ }
+
+ rnext=fifo->rd_ptr+bsize;
+ if (rnext<=fifo->r_end){
+
+ *ret_ptr=fifo->rd_ptr;
+ fifo->rd_ptr=rnext;
+ }else{
+ int unread=fifo->r_end-fifo->rd_ptr;
+ *ret_ptr=fifo->begin-unread;
+ memcpy(fifo->buffer->buffer,fifo->r_end-fifo->saved_offset,fifo->saved_offset);
+ fifo->rd_ptr=(char*)(*ret_ptr) + bsize;
+ fifo->r_end=fifo->w_end; /* this is important ! */
+ ms_trace("moving read ptr to %x",fifo->rd_ptr);
+
+ }
+ /* update write size*/
+ fifo->writesize+=bsize;
+ fifo->readsize-=bsize;
+ return bsize;
+}
+
+
+void ms_fifo_update_write_ptr(MSFifo *fifo, gint written){
+ gint reserved=fifo->wr_ptr-fifo->prev_wr_ptr;
+ gint unwritten;
+ g_return_if_fail(reserved>=0);
+ unwritten=reserved-written;
+ g_return_if_fail(unwritten>=0);
+ /* fix readsize and writesize */
+ fifo->readsize-=unwritten;
+ fifo->writesize+=unwritten;
+ fifo->wr_ptr+=written;
+}
+
+gint ms_fifo_get_write_ptr(MSFifo *fifo, gint bsize, void **ret_ptr)
+{
+ gchar *wnext;
+
+ *ret_ptr=NULL;
+ /* //ms_trace("ms_fifo_get_write_ptr: Entering.");*/
+ g_return_val_if_fail(bsize<=fifo->w_gran,-EINVAL);
+ if (bsize>fifo->writesize)
+ {
+ ms_trace("Not enough space: bsize=%i, writesize=%i",bsize,fifo->writesize);
+ *ret_ptr=NULL;
+ return(-ENODATA);
+ }
+ wnext=fifo->wr_ptr+bsize;
+ if (wnext<=fifo->w_end){
+ *ret_ptr=fifo->wr_ptr;
+ fifo->wr_ptr=wnext;
+ }else{
+ *ret_ptr=fifo->begin;
+ fifo->r_end=fifo->wr_ptr;
+ fifo->wr_ptr=fifo->begin+bsize;
+ ms_trace("moving write ptr to %x",fifo->wr_ptr);
+ }
+ fifo->prev_wr_ptr=*ret_ptr;
+ /* update readsize*/
+ fifo->readsize+=bsize;
+ fifo->writesize-=bsize;
+ /* //ms_trace("ms_fifo_get_write_ptr: readsize=%i, writesize=%i",fifo->readsize,fifo->writesize);*/
+ return bsize;
+}
+
+gint ms_fifo_get_rw_ptr(MSFifo *f1,void **p1,gint minsize1,
+ MSFifo *f2,void **p2,gint minsize2)
+{
+ gint rbsize,wbsize;
+
+ rbsize=MIN(f1->readsize,(f1->pre_end-f1->rd_ptr));
+ wbsize=MIN(f2->writesize,(f2->w_end-f2->wr_ptr));
+ return 0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h
new file mode 100644
index 00000000..fde1bece
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfifo.h
@@ -0,0 +1,73 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+*/
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#else
+#include "glist.h"
+#endif
+#include "msbuffer.h"
+
+typedef struct _MSFifo
+{
+ gint r_gran; /*maximum granularity for reading*/
+ gint w_gran; /*maximum granularity for writing*/
+ gchar * rd_ptr; /* read pointer on the position where there is something to read on the MSBuffer */
+ guint32 readsize;
+ gchar * wr_ptr;
+ gchar * prev_wr_ptr;
+ guint32 writesize; /* write pointer on the position where it is possible to write on the MSBuffer */
+ gchar * begin; /* rd_ptr et wr_ptr must all be >=begin*/
+ guint32 size; /* the length of the fifo, but this may not be equal to buffer->size*/
+ guint32 saved_offset;
+ gchar * pre_end; /* the end of the buffer that is copied at the begginning when we wrap around*/
+ gchar * w_end; /* when a wr ptr is expected to exceed end_offset,
+ it must be wrapped around to go at the beginning of the buffer. This is the end of the buffer*/
+ gchar * r_end; /* this is the last position written at the end of the fifo. If a read ptr is expected to
+ exceed this pointer, it must be put at the begginning of the buffer */
+ void *prev_data; /*user data, usually the writing MSFilter*/
+ void *next_data; /* user data, usually the reading MSFilter */
+ MSBuffer *buffer;
+} MSFifo;
+
+/* constructor*/
+/* r_gran: max granularity for reading (in number of bytes)*/
+/* w_gran: max granularity for writing (in number of bytes)*/
+/* r_offset: number of bytes that are kept available behind read pointer (for recursive filters)*/
+/* w_offset: number of bytes that are kept available behind write pointer (for recursive filters)*/
+/* buf is a MSBuffer that should be compatible with the above parameter*/
+MSFifo * ms_fifo_new(MSBuffer *buf, gint r_gran, gint w_gran, gint r_offset, gint w_offset);
+
+/*does the same that ms_fifo_new(), but also allocate a compatible buffer automatically*/
+MSFifo * ms_fifo_new_with_buffer(gint r_gran, gint w_gran, gint r_offset, gint w_offset, gint min_buffer_size);
+
+void ms_fifo_destroy( MSFifo *fifo);
+
+void ms_fifo_destroy_with_buffer(MSFifo *fifo);
+
+/* get data to read */
+gint ms_fifo_get_read_ptr(MSFifo *fifo, gint bsize, void **ret_ptr);
+
+/* get a buffer to write*/
+gint ms_fifo_get_write_ptr(MSFifo *fifo, gint bsize, void **ret_ptr);
+
+/* in case the buffer got by ms_fifo_get_write_ptr() could not be filled completely, you must
+tell it by using this function */
+void ms_fifo_update_write_ptr(MSFifo *fifo, gint written);
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c
new file mode 100644
index 00000000..c67e9f0e
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.c
@@ -0,0 +1,537 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <errno.h>
+#include "msfilter.h"
+
+
+
+void ms_filter_init(MSFilter *filter)
+{
+ filter->finputs=0;
+ filter->foutputs=0;
+ filter->qinputs=0;
+ filter->qoutputs=0;
+ filter->infifos=NULL;
+ filter->outfifos=NULL;
+ filter->inqueues=NULL;
+ filter->outqueues=NULL;
+ filter->lock=g_mutex_new();
+ filter->min_fifo_size=0x7fff;
+ filter->notify_event=NULL;
+ filter->userdata=NULL;
+}
+
+void ms_filter_uninit(MSFilter *filter)
+{
+ g_mutex_free(filter->lock);
+}
+
+void ms_filter_class_init(MSFilterClass *filterclass)
+{
+ filterclass->name=NULL;
+ filterclass->max_finputs=0;
+ filterclass->max_foutputs=0;
+ filterclass->max_qinputs=0;
+ filterclass->max_qoutputs=0;
+ filterclass->r_maxgran=0;
+ filterclass->w_maxgran=0;
+ filterclass->r_offset=0;
+ filterclass->w_offset=0;
+ filterclass->set_property=NULL;
+ filterclass->get_property=NULL;
+ filterclass->setup=NULL;
+ filterclass->unsetup=NULL;
+ filterclass->process=NULL;
+ filterclass->destroy=NULL;
+ filterclass->attributes=0;
+ filterclass->ref_count=0;
+}
+
+/* find output queue */
+gint find_oq(MSFilter *m1,MSQueue *oq)
+{
+ gint i;
+
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++){
+ if (m1->outqueues[i]==oq) return i;
+ }
+
+ return -1;
+}
+
+/* find input queue */
+gint find_iq(MSFilter *m1,MSQueue *iq)
+{
+ gint i;
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qinputs;i++){
+ if (m1->inqueues[i]==iq) return i;
+ }
+ return -1;
+}
+
+/* find output fifo */
+gint find_of(MSFilter *m1,MSFifo *of)
+{
+ gint i;
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++){
+ if (m1->outfifos[i]==of) return i;
+ }
+
+ return -1;
+}
+
+/* find input fifo */
+gint find_if(MSFilter *m1,MSFifo *inf)
+{
+ gint i;
+
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_finputs;i++){
+ if (m1->infifos[i]==inf) return i;
+ }
+
+ return -1;
+}
+
+#define find_free_iq(_m1) find_iq(_m1,NULL)
+#define find_free_oq(_m1) find_oq(_m1,NULL)
+#define find_free_if(_m1) find_if(_m1,NULL)
+#define find_free_of(_m1) find_of(_m1,NULL)
+
+int ms_filter_add_link(MSFilter *m1, MSFilter *m2)
+{
+ gint m1_q=-1;
+ gint m1_f=-1;
+ gint m2_q=-1;
+ gint m2_f=-1;
+ /* determine the type of link we can add */
+ m1_q=find_free_oq(m1);
+ m1_f=find_free_of(m1);
+ m2_q=find_free_iq(m2);
+ m2_f=find_free_if(m2);
+ if ((m1_q!=-1) && (m2_q!=-1)){
+ /* link with queues */
+ ms_trace("m1_q=%i , m2_q=%i",m1_q,m2_q);
+ return ms_filter_link(m1,m1_q,m2,m2_q,LINK_QUEUE);
+ }
+ if ((m1_f!=-1) && (m2_f!=-1)){
+ /* link with queues */
+ ms_trace("m1_f=%i , m2_f=%i",m1_f,m2_f);
+ return ms_filter_link(m1,m1_f,m2,m2_f,LINK_FIFO);
+ }
+ g_warning("ms_filter_add_link: could not link.");
+ return -1;
+}
+/**
+ * ms_filter_link:
+ * @m1: A #MSFilter object.
+ * @pin1: The pin number on @m1.
+ * @m2: A #MSFilter object.
+ * @pin2: The pin number on @m2.
+ * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS.
+ *
+ * This function links two MSFilter object between them. It must be used to make chains of filters.
+ * All data outgoing from pin1 of m1 will go to the input pin2 of m2.
+ * The way to communicate can be fifos or queues, depending of the nature of the filters. Filters can have
+ * multiple queue pins and multiple fifo pins, but most of them have only one queue input/output or only one
+ * fifo input/output. Fifos are usally used by filters doing audio processing, while queues are used by filters doing
+ * video processing.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, int linktype)
+{
+ MSQueue *q;
+ MSFifo *fifo;
+
+ g_message("ms_filter_add_link: %s,%i -> %s,%i",m1->klass->name,pin1,m2->klass->name,pin2);
+ switch(linktype)
+ {
+ case LINK_QUEUE:
+ /* Are filter m1 and m2 able to accept more queues connections ?*/
+ g_return_val_if_fail(m1->qoutputs<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EMLINK);
+ g_return_val_if_fail(m2->qinputs<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EMLINK);
+ /* Are filter m1 and m2 valid with their inputs and outputs ?*/
+ g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT);
+ g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT);
+ /* are the requested pins exists ?*/
+ g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL);
+ g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL);
+ /* are the requested pins free ?*/
+ g_return_val_if_fail(m1->outqueues[pin1]==NULL,-EBUSY);
+ g_return_val_if_fail(m2->inqueues[pin2]==NULL,-EBUSY);
+
+ q=ms_queue_new();
+ m1->outqueues[pin1]=m2->inqueues[pin2]=q;
+ m1->qoutputs++;
+ m2->qinputs++;
+ q->prev_data=(void*)m1;
+ q->next_data=(void*)m2;
+ break;
+ case LINK_FIFO:
+ /* Are filter m1 and m2 able to accept more fifo connections ?*/
+ g_return_val_if_fail(m1->foutputs<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EMLINK);
+ g_return_val_if_fail(m2->finputs<MS_FILTER_GET_CLASS(m2)->max_finputs,-EMLINK);
+ /* Are filter m1 and m2 valid with their inputs and outputs ?*/
+ g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT);
+ g_return_val_if_fail(m2->infifos!=NULL,-EFAULT);
+ /* are the requested pins exists ?*/
+ g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL);
+ g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL);
+ /* are the requested pins free ?*/
+ g_return_val_if_fail(m1->outfifos[pin1]==NULL,-EBUSY);
+ g_return_val_if_fail(m2->infifos[pin2]==NULL,-EBUSY);
+
+ if (MS_FILTER_GET_CLASS(m1)->attributes & FILTER_IS_SOURCE)
+ {
+ /* configure min_fifo_size */
+ fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran,
+ MS_FILTER_GET_CLASS(m1)->w_maxgran,
+ MS_FILTER_GET_CLASS(m2)->r_offset,
+ MS_FILTER_GET_CLASS(m1)->w_offset,
+ MS_FILTER_GET_CLASS(m1)->w_maxgran);
+ m2->min_fifo_size=MS_FILTER_GET_CLASS(m1)->w_maxgran;
+ }
+ else
+ {
+ gint next_size;
+ ms_trace("ms_filter_add_link: min_fifo_size=%i",m1->min_fifo_size);
+ fifo=ms_fifo_new_with_buffer(MS_FILTER_GET_CLASS(m2)->r_maxgran,
+ MS_FILTER_GET_CLASS(m1)->w_maxgran,
+ MS_FILTER_GET_CLASS(m2)->r_offset,
+ MS_FILTER_GET_CLASS(m1)->w_offset,
+ m1->min_fifo_size);
+ if (MS_FILTER_GET_CLASS(m2)->r_maxgran>0){
+ next_size=(m1->min_fifo_size*
+ (MS_FILTER_GET_CLASS(m2)->w_maxgran)) /
+ (MS_FILTER_GET_CLASS(m2)->r_maxgran);
+ }else next_size=m1->min_fifo_size;
+ ms_trace("ms_filter_add_link: next_size=%i",next_size);
+ m2->min_fifo_size=next_size;
+ }
+
+
+ m1->outfifos[pin1]=m2->infifos[pin2]=fifo;
+ m1->foutputs++;
+ m2->finputs++;
+ fifo->prev_data=(void*)m1;
+ fifo->next_data=(void*)m2;
+ break;
+ }
+ return 0;
+}
+/**
+ * ms_filter_unlink:
+ * @m1: A #MSFilter object.
+ * @pin1: The pin number on @m1.
+ * @m2: A #MSFilter object.
+ * @pin2: The pin number on @m2.
+ * @linktype: Type of connection, it may be #LINK_QUEUE, #LINK_FIFOS.
+ *
+ * Unlink @pin1 of filter @m1 from @pin2 of filter @m2. @linktype specifies what type of connection is removed.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype)
+{
+ switch(linktype)
+ {
+ case LINK_QUEUE:
+ /* Are filter m1 and m2 valid with their inputs and outputs ?*/
+ g_return_val_if_fail(m1->outqueues!=NULL,-EFAULT);
+ g_return_val_if_fail(m2->inqueues!=NULL,-EFAULT);
+ /* are the requested pins exists ?*/
+ g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_qoutputs,-EINVAL);
+ g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_qinputs,-EINVAL);
+ /* are the requested pins busy ?*/
+ g_return_val_if_fail(m1->outqueues[pin1]!=NULL,-ENOENT);
+ g_return_val_if_fail(m2->inqueues[pin2]!=NULL,-ENOENT);
+ /* are the two pins connected together ?*/
+ g_return_val_if_fail(m1->outqueues[pin1]==m2->inqueues[pin2],-EINVAL);
+
+ ms_queue_destroy(m1->outqueues[pin1]);
+ m1->outqueues[pin1]=m2->inqueues[pin2]=NULL;
+ m1->qoutputs--;
+ m2->qinputs--;
+
+ break;
+ case LINK_FIFO:
+ /* Are filter m1 and m2 valid with their inputs and outputs ?*/
+ g_return_val_if_fail(m1->outfifos!=NULL,-EFAULT);
+ g_return_val_if_fail(m2->infifos!=NULL,-EFAULT);
+ /* are the requested pins exists ?*/
+ g_return_val_if_fail(pin1<MS_FILTER_GET_CLASS(m1)->max_foutputs,-EINVAL);
+ g_return_val_if_fail(pin2<MS_FILTER_GET_CLASS(m2)->max_finputs,-EINVAL);
+ /* are the requested pins busy ?*/
+ g_return_val_if_fail(m1->outfifos[pin1]!=NULL,-ENOENT);
+ g_return_val_if_fail(m2->infifos[pin2]!=NULL,-ENOENT);
+ /* are the two pins connected together ?*/
+ g_return_val_if_fail(m1->outfifos[pin1]==m2->infifos[pin2],-EINVAL);
+ ms_fifo_destroy_with_buffer(m1->outfifos[pin1]);
+ m1->outfifos[pin1]=m2->infifos[pin2]=NULL;
+ m1->foutputs--;
+ m2->finputs--;
+ break;
+ }
+ return 0;
+}
+
+/**
+ *ms_filter_remove_links:
+ *@m1: a filter
+ *@m2: another filter.
+ *
+ * Removes all links between m1 and m2.
+ *
+ *Returns: 0 if one more link have been removed, -1 if not.
+**/
+gint ms_filter_remove_links(MSFilter *m1, MSFilter *m2)
+{
+ int i,j;
+ int removed=-1;
+ MSQueue *qo;
+ MSFifo *fo;
+ /* takes all outputs of m1, and removes the one that goes to m2 */
+ if (m1->outqueues!=NULL){
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_qoutputs;i++)
+ {
+ qo=m1->outqueues[i];
+ if (qo!=NULL){
+ MSFilter *rmf;
+ /* test if the queue connects to m2 */
+ rmf=(MSFilter*)qo->next_data;
+ if (rmf==m2){
+ j=find_iq(rmf,qo);
+ if (j==-1) g_error("Could not find input queue: impossible case.");
+ ms_filter_unlink(m1,i,m2,j,LINK_QUEUE);
+ removed=0;
+ }
+ }
+ }
+ }
+ if (m1->outfifos!=NULL){
+ for (i=0;i<MS_FILTER_GET_CLASS(m1)->max_foutputs;i++)
+ {
+ fo=m1->outfifos[i];
+ if (fo!=NULL){
+ MSFilter *rmf;
+ /* test if the queue connects to m2 */
+ rmf=(MSFilter*)fo->next_data;
+ if (rmf==m2){
+ j=find_if(rmf,fo);
+ if (j==-1) g_error("Could not find input fifo: impossible case.");
+ ms_filter_unlink(m1,i,m2,j,LINK_FIFO);
+ removed=0;
+ }
+ }
+ }
+ }
+ return removed;
+}
+
+/**
+ * ms_filter_fifos_have_data:
+ * @f: a #MSFilter object.
+ *
+ * Tells if the filter has enough data in its input fifos in order to be executed succesfully.
+ *
+ * Returns: 1 if it can be executed, 0 else.
+ */
+gint ms_filter_fifos_have_data(MSFilter *f)
+{
+ gint i,j;
+ gint max_inputs=f->klass->max_finputs;
+ gint con_inputs=f->finputs;
+ MSFifo *fifo;
+ /* test fifos */
+ for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++)
+ {
+ fifo=f->infifos[i];
+ if (fifo!=NULL)
+ {
+ j++;
+ if (fifo->readsize==0) return 0;
+ if (fifo->readsize>=f->r_mingran) return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * ms_filter_queues_have_data:
+ * @f: a #MSFilter object.
+ *
+ * Tells if the filter has enough data in its input queues in order to be executed succesfully.
+ *
+ * Returns: 1 if it can be executed, 0 else.
+ */
+gint ms_filter_queues_have_data(MSFilter *f)
+{
+ gint i,j;
+ gint max_inputs=f->klass->max_qinputs;
+ gint con_inputs=f->qinputs;
+ MSQueue *q;
+ /* test queues */
+ for(i=0,j=0; (i<max_inputs) && (j<con_inputs);i++)
+ {
+ q=f->inqueues[i];
+ if (q!=NULL)
+ {
+ j++;
+ if (ms_queue_can_get(q)) return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+void ms_filter_destroy(MSFilter *f)
+{
+ /* first check if the filter is disconnected from any others */
+ g_return_if_fail(f->finputs==0);
+ g_return_if_fail(f->foutputs==0);
+ g_return_if_fail(f->qinputs==0);
+ g_return_if_fail(f->qoutputs==0);
+ f->klass->destroy(f);
+}
+
+GList *filter_list=NULL;
+
+void ms_filter_register(MSFilterInfo *info)
+{
+ gpointer tmp;
+ tmp=g_list_find(filter_list,info);
+ if (tmp==NULL) filter_list=g_list_append(filter_list,(gpointer)info);
+}
+
+void ms_filter_unregister(MSFilterInfo *info)
+{
+ filter_list=g_list_remove(filter_list,(gpointer)info);
+}
+
+static gint compare_names(gpointer info, gpointer name)
+{
+ MSFilterInfo *i=(MSFilterInfo*) info;
+ return (strcmp(i->name,name));
+}
+
+MSFilterInfo * ms_filter_get_by_name(const gchar *name)
+{
+ GList *elem=g_list_find_custom(filter_list,
+ (gpointer)name,(GCompareFunc)compare_names);
+ if (elem!=NULL){
+ return (MSFilterInfo*)elem->data;
+ }
+ return NULL;
+}
+
+
+
+MSFilter * ms_filter_new_with_name(const gchar *name)
+{
+ MSFilterInfo *info=ms_filter_get_by_name(name);
+ if (info!=NULL) return info->constructor();
+ g_warning("ms_filter_new_with_name: no filter named %s found.",name);
+ return NULL;
+}
+
+
+/* find the first codec in the left part of the stream */
+MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type)
+{
+ MSFilter *tmp=f;
+ MSFilterInfo *info;
+
+ if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL)){
+ tmp=(MSFilter*) tmp->infifos[0]->prev_data;
+ while(1){
+ info=MS_FILTER_GET_CLASS(tmp)->info;
+ if (info!=NULL){
+ if ( (info->type==type) ){
+ return tmp;
+ }
+ }
+ if ((tmp->infifos!=NULL) && (tmp->infifos[0]!=NULL))
+ tmp=(MSFilter*) tmp->infifos[0]->prev_data;
+ else break;
+ }
+ }
+ tmp=f;
+ if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL)){
+ tmp=(MSFilter*) tmp->inqueues[0]->prev_data;
+ while(1){
+
+ info=MS_FILTER_GET_CLASS(tmp)->info;
+ if (info!=NULL){
+ if ( (info->type==type)){
+ return tmp;
+ }
+ }else g_warning("ms_filter_search_upstream_by_type: filter %s has no info."
+ ,MS_FILTER_GET_CLASS(tmp)->name);
+ if ((tmp->inqueues!=NULL) && (tmp->inqueues[0]!=NULL))
+ tmp=(MSFilter*) tmp->inqueues[0]->prev_data;
+ else break;
+ }
+ }
+ return NULL;
+}
+
+
+int ms_filter_set_property(MSFilter *f, MSFilterProperty prop,void *value)
+{
+ if (f->klass->set_property!=NULL){
+ return f->klass->set_property(f,prop,value);
+ }
+ return 0;
+}
+
+int ms_filter_get_property(MSFilter *f, MSFilterProperty prop,void *value)
+{
+ if (f->klass->get_property!=NULL){
+ return f->klass->get_property(f,prop,value);
+ }
+ return -1;
+}
+
+void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata)
+{
+ filter->notify_event=func;
+ filter->userdata=userdata;
+}
+
+void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg)
+{
+ if (filter->notify_event!=NULL){
+ filter->notify_event(filter,event,arg,filter->userdata);
+ }
+}
+
+void swap_buffer(gchar *buffer, gint len)
+{
+ int i;
+ gchar tmp;
+ for (i=0;i<len;i+=2){
+ tmp=buffer[i];
+ buffer[i]=buffer[i+1];
+ buffer[i+1]=tmp;
+ }
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h
new file mode 100644
index 00000000..71ec81ad
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msfilter.h
@@ -0,0 +1,201 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSFILTER_H
+#define MSFILTER_H
+
+#include <config.h>
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#include <gmodule.h>
+#else
+#undef VERSION
+#undef PACKAGE
+#include <uglib.h>
+#endif
+
+#include <string.h>
+#include "msutils.h"
+#include "msfifo.h"
+#include "msqueue.h"
+
+struct _MSFilter;
+/*this is the abstract object and class for all filter types*/
+typedef gint (*MSFilterNotifyFunc)(struct _MSFilter*, gint event, gpointer arg, gpointer userdata);
+
+struct _MSFilter
+{
+ struct _MSFilterClass *klass;
+ GMutex *lock;
+ guchar finputs; /* number of connected fifo inputs*/
+ guchar foutputs; /* number of connected fifo outputs*/
+ guchar qinputs; /* number of connected queue inputs*/
+ guchar qoutputs; /* number of connected queue outputs*/
+ gint min_fifo_size; /* set when linking*/
+ gint r_mingran; /* read minimum granularity (for fifos).
+ It can be zero so that the filter can accept any size of reading data*/
+ MSFifo **infifos; /*pointer to a table of pointer to input fifos*/
+ MSFifo **outfifos; /*pointer to a table of pointer to output fifos*/
+ MSQueue **inqueues; /*pointer to a table of pointer to input queues*/
+ MSQueue **outqueues; /*pointer to a table of pointer to output queues*/
+ MSFilterNotifyFunc notify_event;
+ gpointer userdata;
+};
+
+typedef struct _MSFilter MSFilter;
+
+typedef enum{
+ MS_FILTER_PROPERTY_FREQ, /* value is int */
+ MS_FILTER_PROPERTY_BITRATE, /*value is int */
+ MS_FILTER_PROPERTY_CHANNELS,/*value is int */
+ MS_FILTER_PROPERTY_FMTP /* value is string */
+}MSFilterProperty;
+
+#define MS_FILTER_PROPERTY_STRING_MAX_SIZE 256
+
+typedef MSFilter * (*MSFilterNewFunc)(void);
+typedef void (*MSFilterProcessFunc)(MSFilter *);
+typedef void (*MSFilterDestroyFunc)(MSFilter *);
+typedef int (*MSFilterPropertyFunc)(MSFilter *,int ,void*);
+typedef void (*MSFilterSetupFunc)(MSFilter *, void *); /*2nd arg is the sync */
+
+typedef struct _MSFilterClass
+{
+ struct _MSFilterInfo *info; /*pointer to a filter_info */
+ gchar *name;
+ guchar max_finputs; /* maximum number of fifo inputs*/
+ guchar max_foutputs; /* maximum number of fifo outputs*/
+ guchar max_qinputs; /* maximum number of queue inputs*/
+ guchar max_qoutputs; /* maximum number of queue outputs*/
+ gint r_maxgran; /* read maximum granularity (for fifos)*/
+ gint w_maxgran; /* write maximum granularity (for fifos)*/
+ gint r_offset; /* size of kept samples behind read pointer (for fifos)*/
+ gint w_offset; /* size of kept samples behind write pointer (for fifos)*/
+ MSFilterPropertyFunc set_property;
+ MSFilterPropertyFunc get_property;
+ MSFilterSetupFunc setup; /* called when attaching to sync */
+ void (*process)(MSFilter *filter);
+ MSFilterSetupFunc unsetup; /* called when detaching from sync */
+ void (*destroy)(MSFilter *filter);
+ guint attributes;
+#define FILTER_HAS_FIFOS (0x0001)
+#define FILTER_HAS_QUEUES (0x0001<<1)
+#define FILTER_IS_SOURCE (0x0001<<2)
+#define FILTER_IS_SINK (0x0001<<3)
+#define FILTER_CAN_SYNC (0x0001<<4)
+ guint ref_count; /*number of object using the class*/
+} MSFilterClass;
+
+
+
+#define MS_FILTER(obj) ((MSFilter*)obj)
+#define MS_FILTER_CLASS(klass) ((MSFilterClass*)klass)
+#define MS_FILTER_GET_CLASS(obj) ((MSFilterClass*)((MS_FILTER(obj)->klass)))
+
+void ms_filter_class_init(MSFilterClass *filterclass);
+void ms_filter_init(MSFilter *filter);
+
+#define ms_filter_class_set_attr(filter,flag) ((filter)->attributes|=(flag))
+#define ms_filter_class_unset_attr(filter,flag) ((filter)->attributes&=~(flag))
+
+#define ms_filter_class_set_name(__klass,__name) (__klass)->name=g_strdup((__name))
+#define ms_filter_class_set_info(_klass,_info) (_klass)->info=(_info)
+/* public*/
+
+#define ms_filter_process(filter) ((filter)->klass->process((filter)))
+
+#define ms_filter_lock(filter) g_mutex_lock((filter)->lock)
+#define ms_filter_unlock(filter) g_mutex_unlock((filter)->lock)
+/* low level connect functions */
+int ms_filter_link(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2, gint linktype);
+int ms_filter_unlink(MSFilter *m1, gint pin1, MSFilter *m2,gint pin2,gint linktype);
+
+/* high level connect functions */
+int ms_filter_add_link(MSFilter *m1, MSFilter *m2);
+int ms_filter_remove_links(MSFilter *m1, MSFilter *m2);
+
+void ms_filter_set_notify_func(MSFilter* filter,MSFilterNotifyFunc func, gpointer userdata);
+void ms_filter_notify_event(MSFilter *filter,gint event, gpointer arg);
+
+int ms_filter_set_property(MSFilter *f,MSFilterProperty property, void *value);
+int ms_filter_get_property(MSFilter *f,MSFilterProperty property, void *value);
+
+
+gint ms_filter_fifos_have_data(MSFilter *f);
+gint ms_filter_queues_have_data(MSFilter *f);
+
+void ms_filter_uninit(MSFilter *obj);
+void ms_filter_destroy(MSFilter *f);
+
+#define ms_filter_get_mingran(f) ((f)->r_mingran)
+#define ms_filter_set_mingran(f,gran) ((f)->r_mingran=(gran))
+
+#define LINK_DEFAULT 0
+#define LINK_FIFO 1
+#define LINK_QUEUE 2
+
+
+#define MSFILTER_VERSION(a,b,c) (((a)<<2)|((b)<<1)|(c))
+
+enum _MSFilterType
+{
+ MS_FILTER_DISK_IO,
+ MS_FILTER_AUDIO_CODEC,
+ MS_FILTER_VIDEO_CODEC,
+ MS_FILTER_NET_IO,
+ MS_FILTER_VIDEO_IO,
+ MS_FILTER_AUDIO_IO,
+ MS_FILTER_OTHER
+};
+
+typedef enum _MSFilterType MSFilterType;
+
+
+/* find the first codec in the left part of the stream */
+MSFilter * ms_filter_search_upstream_by_type(MSFilter *f,MSFilterType type);
+
+struct _MSFilterInfo
+{
+ gchar *name;
+ gint version;
+ MSFilterType type;
+ MSFilterNewFunc constructor;
+ char *description; /*some textual information*/
+};
+
+typedef struct _MSFilterInfo MSFilterInfo;
+
+void ms_filter_register(MSFilterInfo *finfo);
+void ms_filter_unregister(MSFilterInfo *finfo);
+MSFilterInfo * ms_filter_get_by_name(const gchar *name);
+
+MSFilter * ms_filter_new_with_name(const gchar *name);
+
+
+
+extern GList *filter_list;
+#define MS_FILTER_INFO(obj) ((MSFilterInfo*)obj)
+
+void swap_buffer(gchar *buffer, gint len);
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c
new file mode 100644
index 00000000..b2dfff98
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.c
@@ -0,0 +1,194 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <config.h>
+
+#ifdef HAVE_ILBC
+
+
+#include "msilbcdec.h"
+#include "msilbcenc.h"
+#include "mscodec.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+
+
+extern MSFilter * ms_ilbc_encoder_new(void);
+
+MSCodecInfo ilbc_info={
+ {
+ "iLBC codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_ilbc_encoder_new,
+ "A speech codec suitable for robust voice communication over IP"
+ },
+ ms_ilbc_encoder_new,
+ ms_ilbc_decoder_new,
+ 0, /* not applicable, 2 modes */
+ 0, /* not applicable, 2 modes */
+ 15200,
+ 8000,
+ 97,
+ "iLBC",
+ 1,
+ 1,
+};
+
+
+void ms_ilbc_codec_init()
+{
+ ms_filter_register(MS_FILTER_INFO(&ilbc_info));
+}
+
+
+
+static MSILBCDecoderClass *ms_ilbc_decoder_class=NULL;
+
+MSFilter * ms_ilbc_decoder_new(void)
+{
+ MSILBCDecoder *r;
+
+ r=g_new(MSILBCDecoder,1);
+ ms_ilbc_decoder_init(r);
+ if (ms_ilbc_decoder_class==NULL)
+ {
+ ms_ilbc_decoder_class=g_new(MSILBCDecoderClass,1);
+ ms_ilbc_decoder_class_init(ms_ilbc_decoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ilbc_decoder_class);
+ return(MS_FILTER(r));
+}
+
+
+int ms_ilbc_decoder_set_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FMTP:
+ if (value == NULL) return 0;
+ if (strstr(value,"ptime=20")!=NULL) obj->ms_per_frame=20;
+ else if (strstr(value,"ptime=30")!=NULL) obj->ms_per_frame=30;
+ else g_warning("Unrecognized fmtp parameter for ilbc encoder!");
+ break;
+ }
+ return 0;
+}
+int ms_ilbc_decoder_get_property(MSILBCDecoder *obj, MSFilterProperty prop, char *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FMTP:
+ if (obj->ms_per_frame==20) strncpy(value,"ptime=20",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
+ if (obj->ms_per_frame==30) strncpy(value,"ptime=30",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
+ break;
+ }
+ return 0;
+}
+
+void ms_ilbc_decoder_setup(MSILBCDecoder *r)
+{
+ MSFilterClass *klass = NULL;
+ switch (r->ms_per_frame) {
+ case 20:
+ r->samples_per_frame = BLOCKL_20MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;
+ break;
+ case 30:
+ r->samples_per_frame = BLOCKL_30MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_30MS;
+ break;
+ default:
+ g_error("ms_ilbc_decoder_setup: Bad value for ptime (%i)",r->ms_per_frame);
+ }
+ g_message("Using ilbc decoder with %i ms frames mode.",r->ms_per_frame);
+ initDecode(&r->ilbc_dec, r->ms_per_frame /* ms frames */, /* user enhancer */ 0);
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_ilbc_decoder_init(MSILBCDecoder *r)
+{
+ /* default bitrate */
+ r->bitrate = 15200;
+ r->ms_per_frame = 30;
+ r->samples_per_frame = BLOCKL_20MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;
+
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->inqueues=r->q_inputs;
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ memset(r->q_inputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS);
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSILBCDECODER_MAX_INPUTS);
+}
+
+void ms_ilbc_decoder_class_init(MSILBCDecoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ILBCDec");
+ MS_FILTER_CLASS(klass)->max_qinputs=MSILBCDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSILBCDECODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->w_maxgran= ILBC_MAX_SAMPLES_PER_FRAME*2;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ilbc_decoder_destroy;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ilbc_decoder_set_property;
+ MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ilbc_decoder_get_property;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ilbc_decoder_setup;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ilbc_decoder_process;
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ilbc_info;
+}
+
+void ms_ilbc_decoder_process(MSILBCDecoder *r)
+{
+ MSFifo *fo;
+ MSQueue *qi;
+ int err1;
+ void *dst=NULL;
+ float speech[ILBC_MAX_SAMPLES_PER_FRAME];
+ MSMessage *m;
+
+ qi=r->q_inputs[0];
+ fo=r->f_outputs[0];
+ m=ms_queue_get(qi);
+
+ ms_fifo_get_write_ptr(fo, r->samples_per_frame*2, &dst);
+ if (dst!=NULL){
+ if (m->data!=NULL){
+ if (m->size<r->bytes_per_compressed_frame) {
+ g_warning("Invalid ilbc frame ?");
+ }
+ iLBC_decode(speech, m->data, &r->ilbc_dec, /* mode */1);
+ }else{
+ iLBC_decode(speech,NULL, &r->ilbc_dec,0);
+ }
+ ilbc_write_16bit_samples((gint16*)dst, speech, r->samples_per_frame);
+ }
+ ms_message_destroy(m);
+}
+
+void ms_ilbc_decoder_uninit(MSILBCDecoder *obj)
+{
+}
+
+void ms_ilbc_decoder_destroy( MSILBCDecoder *obj)
+{
+ ms_ilbc_decoder_uninit(obj);
+ g_free(obj);
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h
new file mode 100644
index 00000000..c219aabe
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcdec.h
@@ -0,0 +1,72 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSILBCDECODER_H
+#define MSILBCDECODER_H
+
+#include <msfilter.h>
+#include <mscodec.h>
+#include <iLBC_decode.h>
+
+/*this is the class that implements a ILBCdecoder filter*/
+
+#define MSILBCDECODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSILBCDecoder
+{
+ /* the MSILBCDecoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSILBCDecoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSQueue *q_inputs[MSILBCDECODER_MAX_INPUTS];
+ MSFifo *f_outputs[MSILBCDECODER_MAX_INPUTS];
+ iLBC_Dec_Inst_t ilbc_dec;
+ int bitrate;
+ int ms_per_frame;
+ int samples_per_frame;
+ int bytes_per_compressed_frame;
+} MSILBCDecoder;
+
+typedef struct _MSILBCDecoderClass
+{
+ /* the MSILBCDecoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSILBCDecoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSILBCDecoderClass;
+
+/* PUBLIC */
+
+/* call this before if don't load the plugin dynamically */
+void ms_ilbc_codec_init();
+
+#define MS_ILBCDECODER(filter) ((MSILBCDecoder*)(filter))
+#define MS_ILBCDECODER_CLASS(klass) ((MSILBCDecoderClass*)(klass))
+MSFilter * ms_ilbc_decoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_ilbc_decoder_init(MSILBCDecoder *r);
+void ms_ilbc_decoder_class_init(MSILBCDecoderClass *klass);
+void ms_ilbc_decoder_destroy( MSILBCDecoder *obj);
+void ms_ilbc_decoder_process(MSILBCDecoder *r);
+
+extern MSCodecInfo ilbc_info;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c
new file mode 100644
index 00000000..76d8b648
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.c
@@ -0,0 +1,244 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <config.h>
+
+#ifdef HAVE_ILBC
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "msilbcenc.h"
+
+
+extern MSCodecInfo ilbc_info;
+
+/* The return value of each of these calls is the same as that
+ returned by fread/fwrite, which should be the number of samples
+ successfully read/written, not the number of bytes. */
+
+int
+ilbc_read_16bit_samples(gint16 int16samples[], float speech[], int n)
+{
+ int i;
+
+ /* Convert 16 bit integer samples to floating point values in the
+ range [-1,+1]. */
+
+ for (i = 0; i < n; i++) {
+ speech[i] = int16samples[i];
+ }
+
+ return (n);
+}
+
+
+
+int
+ilbc_write_16bit_samples(gint16 int16samples[], float speech[], int n)
+{
+ int i;
+ float real_sample;
+
+ /* Convert floating point samples in range [-1,+1] to 16 bit
+ integers. */
+ for (i = 0; i < n; i++) {
+ float dtmp=speech[i];
+ if (dtmp<MIN_SAMPLE)
+ dtmp=MIN_SAMPLE;
+ else if (dtmp>MAX_SAMPLE)
+ dtmp=MAX_SAMPLE;
+ int16samples[i] = (short) dtmp;
+ }
+ return (n);
+}
+
+/*
+
+Write the bits in bits[0] through bits[len-1] to file f, in "packed"
+format.
+
+bits is expected to be an array of len integer values, where each
+integer is 0 to represent a 0 bit, and any other value represents a 1
+bit. This bit string is written to the file f in the form of several
+8 bit characters. If len is not a multiple of 8, then the last
+character is padded with 0 bits -- the padding is in the least
+significant bits of the last byte. The 8 bit characters are "filled"
+in order from most significant bit to least significant.
+
+*/
+
+void
+ilbc_write_bits(unsigned char *data, unsigned char *bits, int nbytes)
+{
+ memcpy(data, bits, nbytes);
+}
+
+
+
+/*
+
+Read bits from file f into bits[0] through bits[len-1], in "packed"
+format.
+
+*/
+
+int
+ilbc_read_bits(unsigned char *data, unsigned char *bits, int nbytes)
+{
+
+ memcpy(bits, data, nbytes);
+
+ return (nbytes);
+}
+
+
+
+
+static MSILBCEncoderClass *ms_ilbc_encoder_class=NULL;
+
+MSFilter * ms_ilbc_encoder_new(void)
+{
+ MSILBCEncoder *r;
+
+ r=g_new(MSILBCEncoder,1);
+ ms_ilbc_encoder_init(r);
+ if (ms_ilbc_encoder_class==NULL)
+ {
+ ms_ilbc_encoder_class=g_new(MSILBCEncoderClass,1);
+ ms_ilbc_encoder_class_init(ms_ilbc_encoder_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ilbc_encoder_class);
+ return(MS_FILTER(r));
+}
+
+
+int ms_ilbc_encoder_set_property(MSILBCEncoder *obj, MSFilterProperty prop, char *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FMTP:
+ if (value == NULL) return 0;
+ if (strstr(value,"ptime=20")!=NULL) obj->ms_per_frame=20;
+ else if (strstr(value,"ptime=30")!=NULL) obj->ms_per_frame=30;
+ else g_warning("Unrecognized fmtp parameter for ilbc encoder!");
+ break;
+ }
+ return 0;
+}
+
+
+int ms_ilbc_encoder_get_property(MSILBCEncoder *obj, MSFilterProperty prop, char *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FMTP:
+ if (obj->ms_per_frame==20) strncpy(value,"ptime=20",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
+ if (obj->ms_per_frame==30) strncpy(value,"ptime=30",MS_FILTER_PROPERTY_STRING_MAX_SIZE);
+ break;
+ }
+ return 0;
+}
+
+void ms_ilbc_encoder_setup(MSILBCEncoder *r)
+{
+ MSFilterClass *klass = NULL;
+ switch (r->ms_per_frame) {
+ case 20:
+ r->samples_per_frame = BLOCKL_20MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;
+ break;
+ case 30:
+ r->samples_per_frame = BLOCKL_30MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_30MS;
+ break;
+ default:
+ g_error("Bad bitrate value (%i) for ilbc encoder!", r->ms_per_frame);
+ break;
+ }
+ MS_FILTER(r)->r_mingran= (r->samples_per_frame * 2);
+ g_message("Using ilbc encoder with %i ms frames mode.",r->ms_per_frame);
+ initEncode(&r->ilbc_enc, r->ms_per_frame /* ms frames */);
+}
+
+/* FOR INTERNAL USE*/
+void ms_ilbc_encoder_init(MSILBCEncoder *r)
+{
+ /* default bitrate */
+ r->bitrate = 15200;
+ r->ms_per_frame = 20;
+ r->samples_per_frame = BLOCKL_20MS;
+ r->bytes_per_compressed_frame = NO_OF_BYTES_20MS;
+
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->outqueues=r->q_outputs;
+ MS_FILTER(r)->r_mingran= (r->samples_per_frame * 2);
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSILBCENCODER_MAX_INPUTS);
+ memset(r->q_outputs,0,sizeof(MSFifo*)*MSILBCENCODER_MAX_INPUTS);
+}
+
+void ms_ilbc_encoder_class_init(MSILBCEncoderClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ILBCEnc");
+ MS_FILTER_CLASS(klass)->max_finputs=MSILBCENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSILBCENCODER_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=ILBC_MAX_SAMPLES_PER_FRAME*2;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ilbc_encoder_set_property;
+ MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ilbc_encoder_get_property;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ilbc_encoder_setup;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ilbc_encoder_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ilbc_encoder_process;
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&ilbc_info;
+}
+
+void ms_ilbc_encoder_process(MSILBCEncoder *r)
+{
+ MSFifo *fi;
+ MSQueue *qo;
+ MSMessage *m;
+ void *src=NULL;
+ float speech[ILBC_MAX_SAMPLES_PER_FRAME];
+
+ /* process output fifos, but there is only one for this class of filter*/
+
+ qo=r->q_outputs[0];
+ fi=r->f_inputs[0];
+ ms_fifo_get_read_ptr(fi,r->samples_per_frame*2,&src);
+ if (src==NULL) {
+ g_warning( "src=%p\n", src);
+ return;
+ }
+ m=ms_message_new(r->bytes_per_compressed_frame);
+
+ ilbc_read_16bit_samples((gint16*)src, speech, r->samples_per_frame);
+ iLBC_encode((unsigned char *)m->data, speech, &r->ilbc_enc);
+ ms_queue_put(qo,m);
+}
+
+void ms_ilbc_encoder_uninit(MSILBCEncoder *obj)
+{
+}
+
+void ms_ilbc_encoder_destroy( MSILBCEncoder *obj)
+{
+ ms_ilbc_encoder_uninit(obj);
+ g_free(obj);
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h
new file mode 100644
index 00000000..bd8f3bf5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msilbcenc.h
@@ -0,0 +1,84 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSILBCENCODER_H
+#define MSILBCENCODER_H
+
+#include "mscodec.h"
+#include <iLBC_encode.h>
+
+#define ILBC_BITS_IN_COMPRESSED_FRAME 400
+
+int
+ilbc_read_16bit_samples(gint16 int16samples[], float speech[], int n);
+
+int
+ilbc_write_16bit_samples(gint16 int16samples[], float speech[], int n);
+
+void
+ilbc_write_bits(unsigned char *data, unsigned char *bits, int nbytes);
+
+int
+ilbc_read_bits(unsigned char *data, unsigned char *bits, int nbytes);
+
+
+/*this is the class that implements a ILBCencoder filter*/
+
+#define MSILBCENCODER_MAX_INPUTS 1 /* max output per filter*/
+
+
+typedef struct _MSILBCEncoder
+{
+ /* the MSILBCEncoder derivates from MSFilter, so the MSFilter object MUST be the first of the MSILBCEncoder object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSILBCENCODER_MAX_INPUTS];
+ MSQueue *q_outputs[MSILBCENCODER_MAX_INPUTS];
+ iLBC_Enc_Inst_t ilbc_enc;
+ int ilbc_encoded_bytes;
+ int bitrate;
+ int ms_per_frame;
+ int samples_per_frame;
+ int bytes_per_compressed_frame;
+} MSILBCEncoder;
+
+typedef struct _MSILBCEncoderClass
+{
+ /* the MSILBCEncoder derivates from MSFilter, so the MSFilter class MUST be the first of the MSILBCEncoder class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSILBCEncoderClass;
+
+/* PUBLIC */
+#define MS_ILBCENCODER(filter) ((MSILBCEncoder*)(filter))
+#define MS_ILBCENCODER_CLASS(klass) ((MSILBCEncoderClass*)(klass))
+MSFilter * ms_ilbc_encoder_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_ilbc_encoder_init(MSILBCEncoder *r);
+void ms_ilbc_encoder_class_init(MSILBCEncoderClass *klass);
+void ms_ilbc_encoder_destroy( MSILBCEncoder *obj);
+void ms_ilbc_encoder_process(MSILBCEncoder *r);
+
+#define ILBC_MAX_BYTES_PER_COMPRESSED_FRAME NO_OF_BYTES_30MS
+#define ILBC_MAX_SAMPLES_PER_FRAME BLOCKL_30MS
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c
new file mode 100644
index 00000000..af5141c0
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.c
@@ -0,0 +1,82 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msnosync.h"
+
+static MSNoSyncClass *ms_nosync_class=NULL;
+
+void ms_nosync_init(MSNoSync *sync)
+{
+ ms_sync_init(MS_SYNC(sync));
+ MS_SYNC(sync)->attached_filters=sync->filters;
+ memset(sync->filters,0,MSNOSYNC_MAX_FILTERS*sizeof(MSFilter*));
+ MS_SYNC(sync)->samples_per_tick=160;
+ sync->started=0;
+}
+
+void ms_nosync_class_init(MSNoSyncClass *klass)
+{
+ ms_sync_class_init(MS_SYNC_CLASS(klass));
+ MS_SYNC_CLASS(klass)->max_filters=MSNOSYNC_MAX_FILTERS;
+ MS_SYNC_CLASS(klass)->synchronize=(MSSyncSyncFunc)ms_nosync_synchronize;
+ MS_SYNC_CLASS(klass)->destroy=(MSSyncDestroyFunc)ms_nosync_destroy;
+ /* no need to overload these function*/
+ MS_SYNC_CLASS(klass)->attach=ms_sync_attach_generic;
+ MS_SYNC_CLASS(klass)->detach=ms_sync_detach_generic;
+}
+
+void ms_nosync_destroy(MSNoSync *nosync)
+{
+ g_free(nosync);
+}
+
+/* the synchronization function that does nothing*/
+void ms_nosync_synchronize(MSNoSync *nosync)
+{
+ gint32 time;
+ if (nosync->started==0){
+ gettimeofday(&nosync->start,NULL);
+ nosync->started=1;
+ }
+ gettimeofday(&nosync->current,NULL);
+ MS_SYNC(nosync)->ticks++;
+ /* update the time, we are supposed to work at 8000 Hz */
+ time=((nosync->current.tv_sec-nosync->start.tv_sec)*1000) +
+ ((nosync->current.tv_usec-nosync->start.tv_usec)/1000);
+ MS_SYNC(nosync)->time=time;
+ return;
+}
+
+
+MSSync *ms_nosync_new()
+{
+ MSNoSync *nosync;
+
+ nosync=g_malloc(sizeof(MSNoSync));
+ ms_nosync_init(nosync);
+ if (ms_nosync_class==NULL)
+ {
+ ms_nosync_class=g_new(MSNoSyncClass,1);
+ ms_nosync_class_init(ms_nosync_class);
+ }
+ MS_SYNC(nosync)->klass=MS_SYNC_CLASS(ms_nosync_class);
+ return(MS_SYNC(nosync));
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h
new file mode 100644
index 00000000..eef52d45
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msnosync.h
@@ -0,0 +1,60 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mssync.h"
+
+#include <sys/time.h>
+#define MSNOSYNC_MAX_FILTERS 10
+
+/* MSNoSync derivates from MSSync base class*/
+
+typedef struct _MSNoSync
+{
+ /* the MSSync must be the first field of the object in order to the object mechanism to work*/
+ MSSync sync;
+ MSFilter *filters[MSNOSYNC_MAX_FILTERS];
+ int started;
+ struct timeval start,current;
+} MSNoSync;
+
+
+typedef struct _MSNoSyncClass
+{
+ /* the MSSyncClass must be the first field of the class in order to the class mechanism to work*/
+ MSSyncClass parent_class;
+} MSNoSyncClass;
+
+
+/*private*/
+
+void ms_nosync_init(MSNoSync *sync);
+void ms_nosync_class_init(MSNoSyncClass *sync);
+
+void ms_nosync_destroy(MSNoSync *nosync);
+void ms_nosync_synchronize(MSNoSync *nosync);
+
+/*public*/
+
+/* casts a MSSync object into a MSNoSync */
+#define MS_NOSYNC(sync) ((MSNoSync*)(sync))
+/* casts a MSSync class into a MSNoSync class */
+#define MS_NOSYNC_CLASS(klass) ((MSNoSyncClass*)(klass))
+
+MSSync *ms_nosync_new();
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c
new file mode 100644
index 00000000..2486c736
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.c
@@ -0,0 +1,148 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msossread.h"
+#include "mssync.h"
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+MSFilterInfo oss_read_info={
+ "OSS read",
+ 0,
+ MS_FILTER_AUDIO_IO,
+ ms_oss_read_new,
+ NULL
+};
+
+static MSOssReadClass *msossreadclass=NULL;
+
+MSFilter * ms_oss_read_new()
+{
+ MSOssRead *w;
+
+ if (msossreadclass==NULL)
+ {
+ msossreadclass=g_new(MSOssReadClass,1);
+ ms_oss_read_class_init( msossreadclass );
+ }
+
+ w=g_new(MSOssRead,1);
+ MS_FILTER(w)->klass=MS_FILTER_CLASS(msossreadclass);
+ ms_oss_read_init(w);
+
+ return(MS_FILTER(w));
+}
+
+/* FOR INTERNAL USE*/
+void ms_oss_read_init(MSOssRead *w)
+{
+ ms_sound_read_init(MS_SOUND_READ(w));
+ MS_FILTER(w)->outfifos=w->f_outputs;
+ MS_FILTER(w)->outfifos[0]=NULL;
+ w->devid=0;
+ w->sndcard=NULL;
+ w->freq=8000;
+}
+
+gint ms_oss_read_set_property(MSOssRead *f,MSFilterProperty prop, void *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ f->freq=((gint*)value)[0];
+ break;
+ }
+ return 0;
+}
+void ms_oss_read_class_init(MSOssReadClass *klass)
+{
+ ms_sound_read_class_init(MS_SOUND_READ_CLASS(klass));
+ MS_FILTER_CLASS(klass)->max_foutputs=1; /* one fifo output only */
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_oss_read_setup;
+ MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_oss_read_stop;
+ MS_FILTER_CLASS(klass)->process= (MSFilterProcessFunc)ms_oss_read_process;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_oss_read_set_property;
+ MS_FILTER_CLASS(klass)->destroy= (MSFilterDestroyFunc)ms_oss_read_destroy;
+ MS_FILTER_CLASS(klass)->w_maxgran=MS_OSS_READ_MAX_GRAN;
+ MS_FILTER_CLASS(klass)->info=&oss_read_info;
+ MS_SOUND_READ_CLASS(klass)->set_device=(gint (*)(MSSoundRead*,gint))ms_oss_read_set_device;
+ MS_SOUND_READ_CLASS(klass)->start=(void (*)(MSSoundRead*))ms_oss_read_start;
+ MS_SOUND_READ_CLASS(klass)->stop=(void (*)(MSSoundRead*))ms_oss_read_stop;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"OssRead");
+ /* //ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_CAN_SYNC|FILTER_IS_SOURCE); */
+}
+
+void ms_oss_read_destroy( MSOssRead *obj)
+{
+ g_free(obj);
+}
+
+void ms_oss_read_process(MSOssRead *f)
+{
+ MSFifo *fifo;
+ char *p;
+ fifo=f->f_outputs[0];
+
+ g_return_if_fail(f->sndcard!=NULL);
+ g_return_if_fail(f->gran>0);
+
+ if (snd_card_can_read(f->sndcard)){
+ int got;
+ ms_fifo_get_write_ptr(fifo,f->gran,(void**)&p);
+ g_return_if_fail(p!=NULL);
+ got=snd_card_read(f->sndcard,p,f->gran);
+ if (got>=0 && got!=f->gran) ms_fifo_update_write_ptr(fifo,got);
+ }
+}
+
+
+void ms_oss_read_start(MSOssRead *r)
+{
+ g_return_if_fail(r->devid!=-1);
+ r->sndcard=snd_card_manager_get_card(snd_card_manager,r->devid);
+ g_return_if_fail(r->sndcard!=NULL);
+ /* open the device for an audio telephony signal with minimum latency */
+ snd_card_open_r(r->sndcard,16,0,r->freq);
+ r->gran=(512*r->freq)/8000;
+
+}
+
+void ms_oss_read_stop(MSOssRead *w)
+{
+ g_return_if_fail(w->devid!=-1);
+ g_return_if_fail(w->sndcard!=NULL);
+ snd_card_close_r(w->sndcard);
+ w->sndcard=NULL;
+}
+
+
+void ms_oss_read_setup(MSOssRead *f, MSSync *sync)
+{
+ f->sync=sync;
+ ms_oss_read_start(f);
+}
+
+
+gint ms_oss_read_set_device(MSOssRead *r,gint devid)
+{
+ r->devid=devid;
+ return 0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h
new file mode 100644
index 00000000..89d5a40b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msossread.h
@@ -0,0 +1,77 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSOSSREAD_H
+#define MSOSSREAD_H
+
+#include "mssoundread.h"
+#include "sndcard.h"
+#include "mssync.h"
+
+
+/*this is the class that implements oss writing sink filter*/
+
+#define MS_OSS_READ_MAX_INPUTS 1 /* max output per filter*/
+
+#define MS_OSS_READ_MAX_GRAN (512*2) /* the maximum granularity*/
+
+struct _MSOssRead
+{
+ /* the MSOssRead derivates from MSSoundRead so the MSSoundRead object MUST be the first of the MSOssRead object
+ in order to the object mechanism to work*/
+ MSSoundRead filter;
+ MSFifo *f_outputs[MS_OSS_READ_MAX_INPUTS];
+ MSSync *sync;
+ SndCard *sndcard;
+ gint freq;
+ gint devid; /* the sound device id it depends on*/
+ gint gran;
+ gint flags;
+#define START_REQUESTED 1
+#define STOP_REQUESTED 2
+};
+
+typedef struct _MSOssRead MSOssRead;
+
+struct _MSOssReadClass
+{
+ /* the MSOssRead derivates from MSSoundRead, so the MSSoundRead class MUST be the first of the MSOssRead class
+ in order to the class mechanism to work*/
+ MSSoundReadClass parent_class;
+};
+
+typedef struct _MSOssReadClass MSOssReadClass;
+
+/* PUBLIC */
+#define MS_OSS_READ(filter) ((MSOssRead*)(filter))
+#define MS_OSS_READ_CLASS(klass) ((MSOssReadClass*)(klass))
+MSFilter * ms_oss_read_new(void);
+gint ms_oss_read_set_device(MSOssRead *w,gint devid);
+void ms_oss_read_start(MSOssRead *w);
+void ms_oss_read_stop(MSOssRead *w);
+
+/* FOR INTERNAL USE*/
+void ms_oss_read_init(MSOssRead *r);
+void ms_oss_read_class_init(MSOssReadClass *klass);
+void ms_oss_read_destroy( MSOssRead *obj);
+void ms_oss_read_process(MSOssRead *f);
+void ms_oss_read_setup(MSOssRead *f, MSSync *sync);
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c
new file mode 100644
index 00000000..cc86cd6b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.c
@@ -0,0 +1,247 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msosswrite.h"
+#include "mssync.h"
+#include <unistd.h>
+#include <math.h>
+
+MSFilterInfo oss_write_info={
+ "OSS write",
+ 0,
+ MS_FILTER_OTHER,
+ ms_oss_write_new,
+ NULL
+};
+
+
+static MSOssWriteClass *msosswriteclass=NULL;
+
+MSFilter * ms_oss_write_new()
+{
+ MSOssWrite *w;
+
+ if (msosswriteclass==NULL)
+ {
+ msosswriteclass=g_new(MSOssWriteClass,1);
+ ms_oss_write_class_init( msosswriteclass );
+ }
+ w=g_new(MSOssWrite,1);
+ MS_FILTER(w)->klass=MS_FILTER_CLASS(msosswriteclass);
+ ms_oss_write_init(w);
+ return(MS_FILTER(w));
+}
+
+/* FOR INTERNAL USE*/
+void ms_oss_write_init(MSOssWrite *w)
+{
+ ms_sound_write_init(MS_SOUND_WRITE(w));
+ MS_FILTER(w)->infifos=w->f_inputs;
+ MS_FILTER(w)->infifos[0]=NULL;
+ MS_FILTER(w)->r_mingran=512; /* very few cards can do that...*/
+ w->devid=0;
+ w->sndcard=NULL;
+ w->freq=8000;
+ w->channels=1;
+ w->dtmf_time=-1;
+}
+
+gint ms_oss_write_set_property(MSOssWrite *f,MSFilterProperty prop, void *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ f->freq=((gint*)value)[0];
+ break;
+ case MS_FILTER_PROPERTY_CHANNELS:
+ f->channels=((gint*)value)[0];
+ break;
+ }
+ return 0;
+}
+
+void ms_oss_write_class_init(MSOssWriteClass *klass)
+{
+ ms_sound_write_class_init(MS_SOUND_WRITE_CLASS(klass));
+ MS_FILTER_CLASS(klass)->max_finputs=1; /* one fifo input only */
+ MS_FILTER_CLASS(klass)->r_maxgran=MS_OSS_WRITE_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->process= (MSFilterProcessFunc)ms_oss_write_process;
+ MS_FILTER_CLASS(klass)->destroy= (MSFilterDestroyFunc)ms_oss_write_destroy;
+ MS_FILTER_CLASS(klass)->setup= (MSFilterSetupFunc)ms_oss_write_setup;
+ MS_FILTER_CLASS(klass)->unsetup= (MSFilterSetupFunc)ms_oss_write_stop;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_oss_write_set_property;
+ MS_FILTER_CLASS(klass)->info=&oss_write_info;
+ MS_SOUND_WRITE_CLASS(klass)->set_device=(gint (*)(MSSoundWrite*,gint))ms_oss_write_set_device;
+ MS_SOUND_WRITE_CLASS(klass)->start=(void (*)(MSSoundWrite*))ms_oss_write_start;
+ MS_SOUND_WRITE_CLASS(klass)->stop=(void (*)(MSSoundWrite*))ms_oss_write_stop;
+ MS_SOUND_WRITE_CLASS(klass)->set_level=(void (*)(MSSoundWrite*, gint))ms_oss_write_set_level;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"OssWrite");
+}
+
+void ms_oss_write_destroy( MSOssWrite *obj)
+{
+
+ g_free(obj);
+}
+
+void ms_oss_write_process(MSOssWrite *f)
+{
+ MSFifo *fifo;
+ void *p;
+ int i;
+ gint gran=ms_filter_get_mingran(MS_FILTER(f));
+
+ /* always consume something */
+ fifo=f->f_inputs[0];
+ ms_fifo_get_read_ptr(fifo,gran,&p);
+ if (p==NULL) {
+ g_warning("Not enough data: gran=%i.",gran);
+ return;
+ }
+ g_return_if_fail(f->sndcard!=NULL);
+ if (f->dtmf_time!=-1){
+ gint16 *buf=(gint16*)p;
+ /* generate a DTMF*/
+ for(i=0;i<gran/2;i++){
+ buf[i]=(gint16)(10000.0*sin(2*M_PI*(double)f->dtmf_time*f->lowfreq));
+ buf[i]+=(gint16)(10000.0*sin(2*M_PI*(double)f->dtmf_time*f->highfreq));
+ f->dtmf_time++;
+ /* //printf("buf[%i]=%i\n",i,buf[i]); */
+ }
+ if (f->dtmf_time>f->dtmf_duration) f->dtmf_time=-1; /*finished*/
+ }
+ snd_card_write(f->sndcard,p,gran);
+}
+
+void ms_oss_write_start(MSOssWrite *w)
+{
+ gint bsize;
+ g_return_if_fail(w->devid!=-1);
+ w->sndcard=snd_card_manager_get_card(snd_card_manager,w->devid);
+ g_return_if_fail(w->sndcard!=NULL);
+ /* open the device for an audio telephony signal with minimum latency */
+ snd_card_open_w(w->sndcard,16,w->channels==2,w->freq);
+ w->bsize=snd_card_get_bsize(w->sndcard);
+ /* //MS_FILTER(w)->r_mingran=w->bsize; */
+ /* //ms_sync_set_samples_per_tick(MS_FILTER(w)->sync,bsize); */
+}
+
+void ms_oss_write_stop(MSOssWrite *w)
+{
+ g_return_if_fail(w->devid!=-1);
+ g_return_if_fail(w->sndcard!=NULL);
+ snd_card_close_w(w->sndcard);
+ w->sndcard=NULL;
+}
+
+void ms_oss_write_set_level(MSOssWrite *w,gint a)
+{
+
+}
+
+gint ms_oss_write_set_device(MSOssWrite *w, gint devid)
+{
+ w->devid=devid;
+ return 0;
+}
+
+void ms_oss_write_setup(MSOssWrite *r)
+{
+ /* //g_message("starting MSOssWrite.."); */
+ ms_oss_write_start(r);
+}
+
+
+
+void ms_oss_write_play_dtmf(MSOssWrite *w, char dtmf){
+
+ w->dtmf_duration=0.1*w->freq;
+ switch(dtmf){
+ case '0':
+ w->lowfreq=941;
+ w->highfreq=1336;
+ break;
+ case '1':
+ w->lowfreq=697;
+ w->highfreq=1209;
+ break;
+ case '2':
+ w->lowfreq=697;
+ w->highfreq=1336;
+ break;
+ case '3':
+ w->lowfreq=697;
+ w->highfreq=1477;
+ break;
+ case '4':
+ w->lowfreq=770;
+ w->highfreq=1209;
+ break;
+ case '5':
+ w->lowfreq=770;
+ w->highfreq=1336;
+ break;
+ case '6':
+ w->lowfreq=770;
+ w->highfreq=1477;
+ break;
+ case '7':
+ w->lowfreq=852;
+ w->highfreq=1209;
+ break;
+ case '8':
+ w->lowfreq=852;
+ w->highfreq=1336;
+ break;
+ case '9':
+ w->lowfreq=852;
+ w->highfreq=1477;
+ break;
+ case '*':
+ w->lowfreq=941;
+ w->highfreq=1209;
+ break;
+ case '#':
+ w->lowfreq=941;
+ w->highfreq=1477;
+ break;
+ case 'A':
+ w->lowfreq=697;
+ w->highfreq=1633;
+ break;
+ case 'B':
+ w->lowfreq=770;
+ w->highfreq=1633;
+ break;
+ case 'C':
+ w->lowfreq=852;
+ w->highfreq=1633;
+ break;
+ case 'D':
+ w->lowfreq=941;
+ w->highfreq=1633;
+ break;
+ default:
+ g_warning("Not a dtmf key.");
+ return;
+ }
+ w->lowfreq=w->lowfreq/w->freq;
+ w->highfreq=w->highfreq/w->freq;
+ w->dtmf_time=0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h
new file mode 100644
index 00000000..d4775341
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msosswrite.h
@@ -0,0 +1,78 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSOSSWRITE_H
+#define MSOSSWRITE_H
+
+#include "mssoundwrite.h"
+#include "sndcard.h"
+
+/*this is the class that implements oss writing sink filter*/
+
+#define MS_OSS_WRITE_MAX_INPUTS 1 /* max output per filter*/
+
+#define MS_OSS_WRITE_DEF_GRAN (512*2) /* the default granularity*/
+
+struct _MSOssWrite
+{
+ /* the MSOssWrite derivates from MSSoundWrite, so the MSSoundWrite object MUST be the first of the MSOssWrite object
+ in order to the object mechanism to work*/
+ MSSoundWrite filter;
+ MSFifo *f_inputs[MS_OSS_WRITE_MAX_INPUTS];
+ gint devid; /* the sound device id it depends on*/
+ SndCard *sndcard;
+ gint bsize;
+ gint freq;
+ gint channels;
+ gdouble lowfreq;
+ gdouble highfreq;
+ gint dtmf_time;
+ gint dtmf_duration;
+};
+
+typedef struct _MSOssWrite MSOssWrite;
+
+struct _MSOssWriteClass
+{
+ /* the MSOssWrite derivates from MSSoundWrite, so the MSSoundWrite class MUST be the first of the MSOssWrite class
+ in order to the class mechanism to work*/
+ MSSoundWriteClass parent_class;
+};
+
+typedef struct _MSOssWriteClass MSOssWriteClass;
+
+/* PUBLIC */
+#define MS_OSS_WRITE(filter) ((MSOssWrite*)(filter))
+#define MS_OSS_WRITE_CLASS(klass) ((MSOssWriteClass*)(klass))
+MSFilter * ms_oss_write_new(void);
+gint ms_oss_write_set_device(MSOssWrite *w,gint devid);
+void ms_oss_write_start(MSOssWrite *w);
+void ms_oss_write_stop(MSOssWrite *w);
+void ms_oss_write_set_level(MSOssWrite *w, gint level);
+void ms_oss_write_play_dtmf(MSOssWrite *w, char dtmf);
+
+/* FOR INTERNAL USE*/
+void ms_oss_write_init(MSOssWrite *r);
+void ms_oss_write_setup(MSOssWrite *r);
+void ms_oss_write_class_init(MSOssWriteClass *klass);
+void ms_oss_write_destroy( MSOssWrite *obj);
+void ms_oss_write_process(MSOssWrite *f);
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c
new file mode 100644
index 00000000..6bd073b9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.c
@@ -0,0 +1,91 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 dispatcher 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 "msqdispatcher.h"
+
+static MSQdispatcherClass *ms_qdispatcher_class=NULL;
+
+MSFilter * ms_qdispatcher_new(void)
+{
+ MSQdispatcher *obj;
+ obj=g_malloc(sizeof(MSQdispatcher));
+ if (ms_qdispatcher_class==NULL){
+ ms_qdispatcher_class=g_malloc0(sizeof(MSQdispatcherClass));
+ ms_qdispatcher_class_init(ms_qdispatcher_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_qdispatcher_class);
+ ms_qdispatcher_init(obj);
+ return MS_FILTER(obj);
+}
+
+
+void ms_qdispatcher_init(MSQdispatcher *obj)
+{
+ ms_filter_init(MS_FILTER(obj));
+
+ MS_FILTER(obj)->inqueues=obj->q_inputs;
+ MS_FILTER(obj)->outqueues=obj->q_outputs;
+ memset(obj->q_inputs,0,sizeof(MSQueue*)*MS_QDISPATCHER_MAX_INPUTS);
+ memset(obj->q_outputs,0,sizeof(MSQueue*)*MS_QDISPATCHER_MAX_OUTPUTS);
+}
+
+
+
+void ms_qdispatcher_class_init(MSQdispatcherClass *klass)
+{
+ MSFilterClass *parent_class=MS_FILTER_CLASS(klass);
+ ms_filter_class_init(parent_class);
+ ms_filter_class_set_name(parent_class,"qdispatcher");
+ parent_class->max_qinputs=MS_QDISPATCHER_MAX_INPUTS;
+ parent_class->max_qoutputs=MS_QDISPATCHER_MAX_OUTPUTS;
+
+ parent_class->destroy=(MSFilterDestroyFunc)ms_qdispatcher_destroy;
+ parent_class->process=(MSFilterProcessFunc)ms_qdispatcher_process;
+}
+
+
+void ms_qdispatcher_destroy( MSQdispatcher *obj)
+{
+ g_free(obj);
+}
+
+void ms_qdispatcher_process(MSQdispatcher *obj)
+{
+ gint i;
+ MSQueue *inq=obj->q_inputs[0];
+
+ if (inq!=NULL){
+ MSQueue *outq;
+ MSMessage *m1,*m2;
+ while ( (m1=ms_queue_get(inq))!=NULL){
+ /* dispatch incoming messages to output queues */
+ for (i=0;i<MS_QDISPATCHER_MAX_OUTPUTS;i++){
+ outq=obj->q_outputs[i];
+ if (outq!=NULL){
+ m2=ms_message_dup(m1);
+ ms_queue_put(outq,m2);
+ }
+ }
+ ms_message_destroy(m1);
+ }
+ }
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h
new file mode 100644
index 00000000..3b0c566d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqdispatcher.h
@@ -0,0 +1,60 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 dispatcher 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 MSQDISPATCHER_H
+#define MSQDISPATCHER_H
+
+#include "msfilter.h"
+
+
+/*this is the class that implements a qdispatcher filter*/
+
+#define MS_QDISPATCHER_MAX_INPUTS 1
+#define MS_QDISPATCHER_MAX_OUTPUTS 5
+
+typedef struct _MSQdispatcher
+{
+ /* the MSQdispatcher derivates from MSFilter, so the MSFilter object MUST be the first of the MSQdispatcher object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSQueue *q_inputs[MS_QDISPATCHER_MAX_INPUTS];
+ MSQueue *q_outputs[MS_QDISPATCHER_MAX_OUTPUTS];
+} MSQdispatcher;
+
+typedef struct _MSQdispatcherClass
+{
+ /* the MSQdispatcher derivates from MSFilter, so the MSFilter class MUST be the first of the MSQdispatcher class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSQdispatcherClass;
+
+/* PUBLIC */
+#define MS_QDISPATCHER(filter) ((MSQdispatcher*)(filter))
+#define MS_QDISPATCHER_CLASS(klass) ((MSQdispatcherClass*)(klass))
+MSFilter * ms_qdispatcher_new(void);
+
+/* FOR INTERNAL USE*/
+void ms_qdispatcher_init(MSQdispatcher *r);
+void ms_qdispatcher_class_init(MSQdispatcherClass *klass);
+void ms_qdispatcher_destroy( MSQdispatcher *obj);
+void ms_qdispatcher_process(MSQdispatcher *r);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c
new file mode 100644
index 00000000..46368956
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.c
@@ -0,0 +1,56 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msqueue.h"
+#include <string.h>
+
+MSQueue * ms_queue_new()
+{
+ MSQueue *q=g_malloc(sizeof(MSQueue));
+ memset(q,0,sizeof(MSQueue));
+ return q;
+}
+
+MSMessage *ms_queue_get(MSQueue *q)
+{
+ MSMessage *b=q->last;
+ if (b==NULL) return NULL;
+ q->last=b->prev;
+ if (b->prev==NULL) q->first=NULL; /* it was the only element of the queue*/
+ q->size--;
+ b->next=b->prev=NULL;
+ return(b);
+}
+
+void ms_queue_put(MSQueue *q, MSMessage *m)
+{
+ MSMessage *mtmp=q->first;
+ g_return_if_fail(m!=NULL);
+ q->first=m;
+ m->next=mtmp;
+ if (mtmp!=NULL)
+ {
+ mtmp->prev=m;
+ }
+ else q->last=m; /* it was the first element of the q */
+ q->size++;
+}
+
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h
new file mode 100644
index 00000000..73ab8d8d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msqueue.h
@@ -0,0 +1,49 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSQUEUE_H
+#define MSQUEUE_H
+
+#include "msbuffer.h"
+
+/* for the moment these are stupid queues limited to one element*/
+
+typedef struct _MSQueue
+{
+ MSMessage *first;
+ MSMessage *last;
+ gint size;
+ void *prev_data; /*user data, usually the writting filter*/
+ void *next_data; /* user data, usually the reading filter*/
+}MSQueue;
+
+
+MSQueue * ms_queue_new();
+
+MSMessage *ms_queue_get(MSQueue *q);
+
+void ms_queue_put(MSQueue *q, MSMessage *m);
+
+#define ms_queue_can_get(q) ( (q)->size!=0 )
+
+#define ms_queue_destroy(q) g_free(q)
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c
new file mode 100644
index 00000000..6f0ec99d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.c
@@ -0,0 +1,182 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msread.h"
+#include "mssync.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+
+static MSReadClass *ms_read_class=NULL;
+
+MSFilter * ms_read_new(char *name)
+{
+ MSRead *r;
+ int fd=-1;
+
+ r=g_new(MSRead,1);
+ ms_read_init(r);
+ if (ms_read_class==NULL)
+ {
+ ms_read_class=g_new(MSReadClass,1);
+ ms_read_class_init(ms_read_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_read_class);
+ r->fd=-1;
+ if (name!=NULL) ms_read_open(r,name);
+ return(MS_FILTER(r));
+}
+
+
+
+gint ms_read_open(MSRead *r, gchar *name)
+{
+ gint fd;
+ fd=open(name,O_RDONLY);
+ if (fd<0) {
+ r->fd=-1;
+ g_warning("ms_read_new: cannot open %s : %s",name,strerror(errno));
+ return -1;
+ }
+ r->fd=fd;
+ if (strstr(name,".wav")!=NULL){
+ /* skip the header */
+ lseek(fd,20,SEEK_SET);
+#ifdef WORDS_BIGENDIAN
+ r->need_swap=1;
+#else
+ r->need_swap=0;
+#endif
+ }
+ r->state=MS_READ_STATE_STARTED;
+ return 0;
+}
+
+/* FOR INTERNAL USE*/
+void ms_read_init(MSRead *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->outfifos=r->foutputs;
+ MS_FILTER(r)->outqueues=r->qoutputs;
+ memset(r->foutputs,0,sizeof(MSFifo*)*MSREAD_MAX_OUTPUTS);
+ memset(r->qoutputs,0,sizeof(MSQueue*)*MSREAD_MAX_OUTPUTS);
+ r->fd=-1;
+ r->gran=320;
+ r->state=MS_READ_STATE_STOPPED;
+ r->need_swap=0;
+ r->rate=8000;
+}
+
+gint ms_read_set_property(MSRead *f,MSFilterProperty prop, void *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ f->rate=((gint*)value)[0];
+ break;
+ }
+ return 0;
+}
+
+void ms_read_class_init(MSReadClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"dskreader");
+ ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE);
+ MS_FILTER_CLASS(klass)->max_foutputs=MSREAD_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSREAD_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->w_maxgran=MSREAD_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_read_destroy;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_read_setup;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_read_process;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_read_set_property;
+}
+
+void ms_read_process(MSRead *r)
+{
+ MSFifo *f;
+ MSQueue *q;
+ MSMessage *msg=NULL;
+ int err;
+ gint gran=r->gran;
+ void *p;
+
+ f=r->foutputs[0];
+ if ((f!=NULL) && (r->state==MS_READ_STATE_STARTED))
+ {
+ ms_fifo_get_write_ptr(f,gran,&p);
+ if (p!=NULL)
+ {
+ err=read(r->fd,p,gran);
+ if (err<0)
+ {
+ /* temp: */
+ g_warning("ms_read_process: failed to read: %s.\n",strerror(errno));
+ }
+ else if (err<gran){
+ ms_trace("ms_read_process: end of file.");
+ ms_filter_notify_event(MS_FILTER(r),MS_READ_EVENT_EOF,NULL);
+ r->state=MS_READ_STATE_STOPPED;
+ close(r->fd);
+ r->fd=-1;
+ }
+ if (r->need_swap) swap_buffer(p,gran);
+ }
+ }
+ /* process output queues*/
+ q=r->qoutputs[0];
+ if ((q!=NULL) && (r->fd>0))
+ {
+ msg=ms_message_new(r->gran);
+ err=read(r->fd,msg->data,r->gran);
+ if (err>0){
+ msg->size=err;
+ ms_queue_put(q,msg);
+ if (r->need_swap) swap_buffer(msg->data,r->gran);
+ }else{
+ ms_filter_notify_event(MS_FILTER(r),MS_READ_EVENT_EOF,NULL);
+ ms_trace("End of file reached.");
+ r->state=MS_READ_STATE_STOPPED;
+ }
+ }
+}
+
+void ms_read_destroy( MSRead *obj)
+{
+ if (obj->fd!=0) close(obj->fd);
+ g_free(obj);
+}
+
+gint ms_read_close(MSRead *obj)
+{
+ if (obj->fd!=0) {
+ close(obj->fd);
+ obj->fd=-1;
+ obj->state=MS_READ_STATE_STOPPED;
+ }
+}
+
+
+void ms_read_setup(MSRead *r, MSSync *sync)
+{
+ r->sync=sync;
+ r->gran=(r->rate*sync->interval/1000)*2;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h
new file mode 100644
index 00000000..93177f38
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msread.h
@@ -0,0 +1,80 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSREAD_H
+#define MSREAD_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+/*this is the class that implements file reading source filter*/
+
+#define MSREAD_MAX_OUTPUTS 1 /* max output per filter*/
+
+#define MSREAD_DEF_GRAN 640 /* the default granularity*/
+
+typedef enum{
+ MS_READ_STATE_STARTED,
+ MS_READ_STATE_STOPPED,
+ MS_READ_STATE_EOF
+}MSReadState;
+
+typedef struct _MSRead
+{
+ /* the MSRead derivates from MSFilter, so the MSFilter object MUST be the first of the MSRead object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *foutputs[MSREAD_MAX_OUTPUTS];
+ MSQueue *qoutputs[MSREAD_MAX_OUTPUTS];
+ MSSync *sync;
+ gint rate;
+ gint fd; /* the file descriptor of the file being read*/
+ gint gran; /*granularity*/ /* for use with queues */
+ gint need_swap;
+ gint state;
+} MSRead;
+
+typedef struct _MSReadClass
+{
+ /* the MSRead derivates from MSFilter, so the MSFilter class MUST be the first of the MSRead class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSReadClass;
+
+/* PUBLIC */
+#define MS_READ(filter) ((MSRead*)(filter))
+#define MS_READ_CLASS(klass) ((MSReadClass*)(klass))
+MSFilter * ms_read_new(char *name);
+/* set the granularity for reading file on disk */
+#define ms_read_set_bufsize(filter,sz) (filter)->gran=(sz)
+
+/* FOR INTERNAL USE*/
+void ms_read_init(MSRead *r);
+void ms_read_class_init(MSReadClass *klass);
+void ms_read_destroy( MSRead *obj);
+void ms_read_process(MSRead *r);
+void ms_read_setup(MSRead *r, MSSync *sync);
+
+typedef enum{
+ MS_READ_EVENT_EOF /* end of file */
+} MSReadEvent;
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c
new file mode 100644
index 00000000..fb2006e8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.c
@@ -0,0 +1,246 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msringplayer.h"
+#include "mssync.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <string.h>
+#include <errno.h>
+
+#include "waveheader.h"
+
+#define WAVE_HEADER_OFFSET sizeof(wave_header_t)
+
+enum { PLAY_RING, PLAY_SILENCE};
+
+static int supported_freq[6]={8000,11025,16000,22050,32000,44100};
+
+gint freq_is_supported(gint freq){
+ int i;
+ for (i=0;i<6;i++){
+ if (abs(supported_freq[i]-freq)<50) return supported_freq[i];
+ }
+ return 0;
+}
+
+static MSRingPlayerClass *ms_ring_player_class=NULL;
+
+/**
+ * ms_ring_player_new:
+ * @name: The path to the 16-bit 8khz raw file to be played as a ring.
+ * @seconds: The number of seconds that separates two rings.
+ *
+ * Allocates a new MSRingPlayer object.
+ *
+ *
+ * Returns: a pointer the the object, NULL if name could not be open.
+ */
+MSFilter * ms_ring_player_new(char *name, gint seconds)
+{
+ MSRingPlayer *r;
+ int fd=-1;
+
+ if ((name!=NULL) && (strlen(name)!=0))
+ {
+ fd=open(name,O_RDONLY);
+ if (fd<0)
+ {
+ g_warning("ms_ring_player_new: failed to open %s.\n",name);
+ return NULL;
+ }
+
+ }else {
+ g_warning("ms_ring_player_new: Bad file name");
+ return NULL;
+ }
+
+ r=g_new(MSRingPlayer,1);
+ ms_ring_player_init(r);
+ if (ms_ring_player_class==NULL)
+ {
+ ms_ring_player_class=g_new(MSRingPlayerClass,1);
+ ms_ring_player_class_init(ms_ring_player_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_ring_player_class);
+
+ r->fd=fd;
+ r->silence=seconds;
+ r->freq=8000;
+ if (strstr(name,".wav")!=NULL){
+ wave_header_t header;
+ int freq,freq2;
+ /* read the header */
+ read(fd,&header,sizeof(wave_header_t));
+ freq=wave_header_get_rate(&header);
+ if ((freq2=freq_is_supported(freq))>0){
+ r->freq=freq2;
+ }else {
+ g_warning("Unsupported sampling rate %i",freq);
+ r->freq=8000;
+ }
+ r->channel=wave_header_get_channel(&header);
+ lseek(fd,WAVE_HEADER_OFFSET,SEEK_SET);
+#ifdef WORDS_BIGENDIAN
+ r->need_swap=1;
+#else
+ r->need_swap=0;
+#endif
+ }
+ ms_ring_player_set_property(r, MS_FILTER_PROPERTY_FREQ,&r->freq);
+ r->state=PLAY_RING;
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_ring_player_init(MSRingPlayer *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->outfifos=r->foutputs;
+ MS_FILTER(r)->outqueues=r->qoutputs;
+ memset(r->foutputs,0,sizeof(MSFifo*)*MS_RING_PLAYER_MAX_OUTPUTS);
+ memset(r->qoutputs,0,sizeof(MSQueue*)*MS_RING_PLAYER_MAX_OUTPUTS);
+ r->fd=-1;
+ r->current_pos=0;
+ r->need_swap=0;
+ r->sync=NULL;
+}
+
+gint ms_ring_player_set_property(MSRingPlayer *f,MSFilterProperty prop, void *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ f->rate=((gint*)value)[0]*2;
+ f->silence_bytes=f->silence*f->rate;
+ if (f->sync!=NULL)
+ f->gran=(f->rate*f->sync->interval/1000)*2;
+ break;
+ }
+ return 0;
+}
+
+gint ms_ring_player_get_property(MSRingPlayer *f,MSFilterProperty prop, void *value)
+{
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ ((gint*)value)[0]=f->freq;
+
+ break;
+ case MS_FILTER_PROPERTY_CHANNELS:
+ ((gint*)value)[0]=f->channel;
+ break;
+ }
+ return 0;
+}
+
+gint ms_ring_player_get_sample_freq(MSRingPlayer *obj){
+ return obj->freq;
+}
+
+
+void ms_ring_player_class_init(MSRingPlayerClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"ringplay");
+ ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE);
+ MS_FILTER_CLASS(klass)->max_foutputs=MS_RING_PLAYER_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->max_qoutputs=MS_RING_PLAYER_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->w_maxgran=MS_RING_PLAYER_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_ring_player_setup;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_ring_player_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_ring_player_process;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_ring_player_set_property;
+ MS_FILTER_CLASS(klass)->get_property=(MSFilterPropertyFunc)ms_ring_player_get_property;
+}
+
+void ms_ring_player_process(MSRingPlayer *r)
+{
+ MSFifo *f;
+ gint err;
+ gint processed=0;
+ gint gran=r->gran;
+ char *p;
+
+ g_return_if_fail(gran>0);
+ /* process output fifos*/
+
+ f=r->foutputs[0];
+ ms_fifo_get_write_ptr(f,gran,(void**)&p);
+ g_return_if_fail(p!=NULL);
+ for (processed=0;processed<gran;){
+ switch(r->state){
+ case PLAY_RING:
+ err=read(r->fd,&p[processed],gran-processed);
+ if (err<0)
+ {
+ memset(&p[processed],0,gran-processed);
+ processed=gran;
+ g_warning("ms_ring_player_process: failed to read: %s.\n",strerror(errno));
+ return;
+ }
+ else if (err<gran)
+ {/* end of file */
+
+ r->current_pos=r->silence_bytes;
+ lseek(r->fd,WAVE_HEADER_OFFSET,SEEK_SET);
+ r->state=PLAY_SILENCE;
+ ms_filter_notify_event(MS_FILTER(r),MS_RING_PLAYER_END_OF_RING_EVENT,NULL);
+ }
+ if (r->need_swap) swap_buffer(&p[processed],err);
+ processed+=err;
+ break;
+ case PLAY_SILENCE:
+ err=gran-processed;
+ if (r->current_pos>err){
+ memset(&p[processed],0,err);
+ r->current_pos-=gran;
+ processed=gran;
+ }else{
+ memset(&p[processed],0,r->current_pos);
+ processed+=r->current_pos;
+ r->state=PLAY_RING;
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * ms_ring_player_destroy:
+ * @obj: A valid MSRingPlayer object.
+ *
+ * Destroy a MSRingPlayer object.
+ *
+ *
+ */
+
+void ms_ring_player_destroy( MSRingPlayer *obj)
+{
+ if (obj->fd!=0) close(obj->fd);
+ g_free(obj);
+}
+
+void ms_ring_player_setup(MSRingPlayer *r,MSSync *sync)
+{
+ r->sync=sync;
+ r->gran=(r->rate*r->sync->interval/1000)*r->channel;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h
new file mode 100644
index 00000000..1f5e67da
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msringplayer.h
@@ -0,0 +1,81 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSRINGPLAYER_H
+#define MSRINGPLAYER_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+
+/*this is the class that implements file reading source filter*/
+
+#define MS_RING_PLAYER_MAX_OUTPUTS 1 /* max output per filter*/
+
+#define MS_RING_PLAYER_DEF_GRAN 8192 /* the default granularity*/
+
+#define MS_RING_PLAYER_END_OF_RING_EVENT 1
+
+struct _MSRingPlayer
+{
+ /* the MSRingPlayer derivates from MSFilter, so the MSFilter object MUST be the first of the MSRingPlayer object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *foutputs[MS_RING_PLAYER_MAX_OUTPUTS];
+ MSQueue *qoutputs[MS_RING_PLAYER_MAX_OUTPUTS];\
+ MSSync *sync;
+ gint gran;
+ gint freq;
+ gint rate;
+ gint channel; /* number of interleaved channels */
+ gint silence; /* silence time between each ring, in seconds */
+ gint state;
+ gint fd; /* the file descriptor of the file being read*/
+ gint silence_bytes; /*silence in number of bytes between each ring */
+ gint current_pos;
+ gint need_swap;
+};
+
+typedef struct _MSRingPlayer MSRingPlayer;
+
+struct _MSRingPlayerClass
+{
+ /* the MSRingPlayer derivates from MSFilter, so the MSFilter class MUST be the first of the MSRingPlayer class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSRingPlayerClass MSRingPlayerClass;
+
+/* PUBLIC */
+#define MS_RING_PLAYER(filter) ((MSRingPlayer*)(filter))
+#define MS_RING_PLAYER_CLASS(klass) ((MSRingPlayerClass*)(klass))
+MSFilter * ms_ring_player_new(char *name, gint seconds);
+gint ms_ring_player_get_sample_freq(MSRingPlayer *obj);
+
+
+/* FOR INTERNAL USE*/
+void ms_ring_player_init(MSRingPlayer *r);
+void ms_ring_player_class_init(MSRingPlayerClass *klass);
+void ms_ring_player_destroy( MSRingPlayer *obj);
+void ms_ring_player_process(MSRingPlayer *r);
+#define ms_ring_player_set_bufsize(filter,sz) (filter)->gran=(sz)
+void ms_ring_player_setup(MSRingPlayer *r,MSSync *sync);
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c
new file mode 100644
index 00000000..9b82e939
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.c
@@ -0,0 +1,163 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msrtprecv.h"
+
+
+/* some utilities to convert mblk_t to MSMessage and vice-versa */
+MSMessage *msgb_2_ms_message(mblk_t* mp){
+ MSMessage *msg;
+ MSBuffer *msbuf;
+ if (mp->b_datap->ref_count!=1) return NULL; /* cannot handle properly non-unique buffers*/
+ /* create a MSBuffer using the mblk_t buffer */
+ msg=ms_message_alloc();
+ msbuf=ms_buffer_alloc(0);
+ msbuf->buffer=mp->b_datap->db_base;
+ msbuf->size=(char*)mp->b_datap->db_lim-(char*)mp->b_datap->db_base;
+ ms_message_set_buf(msg,msbuf);
+ msg->size=mp->b_wptr-mp->b_rptr;
+ msg->data=mp->b_rptr;
+ /* free the mblk_t */
+ g_free(mp->b_datap);
+ g_free(mp);
+ return msg;
+}
+
+
+static MSRtpRecvClass *ms_rtp_recv_class=NULL;
+
+MSFilter * ms_rtp_recv_new(void)
+{
+ MSRtpRecv *r;
+
+ r=g_new(MSRtpRecv,1);
+ ms_rtp_recv_init(r);
+ if (ms_rtp_recv_class==NULL)
+ {
+ ms_rtp_recv_class=g_new0(MSRtpRecvClass,1);
+ ms_rtp_recv_class_init(ms_rtp_recv_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_rtp_recv_class);
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_rtp_recv_init(MSRtpRecv *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->outfifos=r->f_outputs;
+ MS_FILTER(r)->outqueues=r->q_outputs;
+ memset(r->f_outputs,0,sizeof(MSFifo*)*MSRTPRECV_MAX_OUTPUTS);
+ memset(r->q_outputs,0,sizeof(MSFifo*)*MSRTPRECV_MAX_OUTPUTS);
+ r->rtpsession=NULL;
+ r->stream_started=0;
+}
+
+void ms_rtp_recv_class_init(MSRtpRecvClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"RTPRecv");
+ MS_FILTER_CLASS(klass)->max_qoutputs=MSRTPRECV_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->max_foutputs=MSRTPRECV_MAX_OUTPUTS;
+ MS_FILTER_CLASS(klass)->w_maxgran=MSRTPRECV_DEF_GRAN;
+ ms_filter_class_set_attr(MS_FILTER_CLASS(klass),FILTER_IS_SOURCE);
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_rtp_recv_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_rtp_recv_process;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_rtp_recv_setup;
+}
+
+void ms_rtp_recv_process(MSRtpRecv *r)
+{
+ MSFifo *fo;
+ MSQueue *qo;
+ MSSync *sync= r->sync;
+ void *d;
+ mblk_t *mp;
+ gint len;
+ gint gran=ms_sync_get_samples_per_tick(MS_SYNC(sync));
+
+ if (r->rtpsession==NULL) return;
+ /* process output fifo and output queue*/
+ fo=r->f_outputs[0];
+ if (fo!=NULL)
+ {
+ while( (mp=rtp_session_recvm_with_ts(r->rtpsession,r->prev_ts))!=NULL) {
+ /* try to get rtp packets and paste them to the output fifo */
+ r->stream_started=1;
+ len=mp->b_cont->b_wptr-mp->b_cont->b_rptr;
+ ms_fifo_get_write_ptr(fo,len,&d);
+ if (d!=NULL){
+ memcpy(d,mp->b_cont->b_rptr,len);
+ }else ms_warning("ms_rtp_recv_process: no space on output fifo !");
+ freemsg(mp);
+ }
+ r->prev_ts+=gran;
+
+ }
+ qo=r->q_outputs[0];
+ if (qo!=NULL)
+ {
+ guint32 clock;
+ gint got=0;
+ /* we are connected with queues (surely for video)*/
+ /* use the sync system time to compute a timestamp */
+ PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type);
+ if (pt==NULL) {
+ ms_warning("ms_rtp_recv_process(): NULL RtpPayload- skipping.");
+ return;
+ }
+ clock=(guint32)(((double)sync->time*(double)pt->clock_rate)/1000.0);
+ /*g_message("Querying packet with timestamp %u",clock);*/
+ /* get rtp packet, and send them through the output queue */
+ while ( (mp=rtp_session_recvm_with_ts(r->rtpsession,clock))!=NULL ){
+ MSMessage *msg;
+ mblk_t *mdata;
+ /*g_message("Got packet with timestamp %u",clock);*/
+ got++;
+ r->stream_started=1;
+ mdata=mp->b_cont;
+ freeb(mp);
+ msg=msgb_2_ms_message(mdata);
+ ms_queue_put(qo,msg);
+ }
+ }
+}
+
+void ms_rtp_recv_destroy( MSRtpRecv *obj)
+{
+ g_free(obj);
+}
+
+RtpSession * ms_rtp_recv_set_session(MSRtpRecv *obj,RtpSession *session)
+{
+ RtpSession *old=obj->rtpsession;
+ obj->rtpsession=session;
+ obj->prev_ts=0;
+ return old;
+}
+
+
+void ms_rtp_recv_setup(MSRtpRecv *r,MSSync *sync)
+{
+ r->sync=sync;
+ r->stream_started=0;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h
new file mode 100644
index 00000000..8c2c2ed7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtprecv.h
@@ -0,0 +1,80 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSRTPRECV_H
+#define MSRTPRECV_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+/* because of a conflict between config.h from oRTP and config.h from linphone:*/
+#undef PACKAGE
+#undef VERSION
+#include <ortp/ortp.h>
+
+/*this is the class that implements a copy filter*/
+
+#define MSRTPRECV_MAX_OUTPUTS 1 /* max output per filter*/
+
+#define MSRTPRECV_DEF_GRAN 4096 /* the default granularity*/
+
+struct _MSRtpRecv
+{
+ /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_outputs[MSRTPRECV_MAX_OUTPUTS];
+ MSQueue *q_outputs[MSRTPRECV_MAX_OUTPUTS];
+ MSSync *sync;
+ RtpSession *rtpsession;
+ guint32 prev_ts;
+ gint stream_started;
+};
+
+typedef struct _MSRtpRecv MSRtpRecv;
+
+struct _MSRtpRecvClass
+{
+ /* the MSCopy derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSRtpRecvClass MSRtpRecvClass;
+
+/* PUBLIC */
+#define MS_RTP_RECV(filter) ((MSRtpRecv*)(filter))
+#define MS_RTP_RECV_CLASS(klass) ((MSRtpRecvClass*)(klass))
+MSFilter * ms_rtp_recv_new(void);
+RtpSession * ms_rtp_recv_set_session(MSRtpRecv *obj,RtpSession *session);
+#define ms_rtp_recv_unset_session(obj) (ms_rtp_recv_set_session((obj),NULL))
+#define ms_rtp_recv_get_session(obj) ((obj)->rtpsession)
+
+
+
+/* FOR INTERNAL USE*/
+void ms_rtp_recv_init(MSRtpRecv *r);
+void ms_rtp_recv_class_init(MSRtpRecvClass *klass);
+void ms_rtp_recv_destroy( MSRtpRecv *obj);
+void ms_rtp_recv_process(MSRtpRecv *r);
+void ms_rtp_recv_setup(MSRtpRecv *r,MSSync *sync);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c
new file mode 100644
index 00000000..cfcb6b34
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.c
@@ -0,0 +1,211 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "msrtpsend.h"
+#include <ortp/telephonyevents.h>
+#include "mssync.h"
+#include "mscodec.h"
+
+
+
+static MSRtpSendClass *ms_rtp_send_class=NULL;
+
+MSFilter * ms_rtp_send_new(void)
+{
+ MSRtpSend *r;
+
+ r=g_new(MSRtpSend,1);
+
+ if (ms_rtp_send_class==NULL)
+ {
+ ms_rtp_send_class=g_new(MSRtpSendClass,1);
+ ms_rtp_send_class_init(ms_rtp_send_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_rtp_send_class);
+ ms_rtp_send_init(r);
+ return(MS_FILTER(r));
+}
+
+
+void ms_rtp_send_init(MSRtpSend *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->inqueues=r->q_inputs;
+ MS_FILTER(r)->r_mingran=MSRTPSEND_DEF_GRAN;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSRTPSEND_MAX_INPUTS);
+ memset(r->q_inputs,0,sizeof(MSFifo*)*MSRTPSEND_MAX_INPUTS);
+ r->rtpsession=NULL;
+ r->ts=0;
+ r->ts_inc=0;
+ r->flags=0;
+ r->delay=0;
+}
+
+void ms_rtp_send_class_init(MSRtpSendClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"RTPSend");
+ MS_FILTER_CLASS(klass)->max_qinputs=MSRTPSEND_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_finputs=MSRTPSEND_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=MSRTPSEND_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_rtp_send_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_rtp_send_process;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_rtp_send_setup;
+}
+
+void ms_rtp_send_set_timing(MSRtpSend *r, guint32 ts_inc, gint payload_size)
+{
+ r->ts_inc=ts_inc;
+ r->packet_size=payload_size;
+ if (r->ts_inc!=0) r->flags|=RTPSEND_CONFIGURED;
+ else r->flags&=~RTPSEND_CONFIGURED;
+ MS_FILTER(r)->r_mingran=payload_size;
+ /*g_message("ms_rtp_send_set_timing: ts_inc=%i",ts_inc);*/
+}
+
+guint32 get_new_timestamp(MSRtpSend *r,guint32 synctime)
+{
+ guint32 clockts;
+ /* use the sync system time to compute a timestamp */
+ PayloadType *pt=rtp_profile_get_payload(r->rtpsession->profile,r->rtpsession->payload_type);
+ g_return_val_if_fail(pt!=NULL,0);
+ clockts=(guint32)(((double)synctime * (double)pt->clock_rate)/1000.0);
+ ms_trace("ms_rtp_send_process: sync->time=%i clock=%i",synctime,clockts);
+ if (r->flags & RTPSEND_CONFIGURED){
+ if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(clockts,r->ts+(2*r->ts_inc) )){
+ r->ts=clockts;
+ }
+ else r->ts+=r->ts_inc;
+ }else{
+ r->ts=clockts;
+ }
+ return r->ts;
+}
+
+
+void ms_rtp_send_process(MSRtpSend *r)
+{
+ MSFifo *fi;
+ MSQueue *qi;
+ MSSync *sync= r->sync;
+ int gran=ms_sync_get_samples_per_tick(sync);
+ guint32 ts;
+ void *s;
+ guint skip;
+ guint32 synctime=sync->time;
+
+ g_return_if_fail(gran>0);
+ if (r->rtpsession==NULL) return;
+
+ ms_filter_lock(MS_FILTER(r));
+ skip=r->delay!=0;
+ if (skip) r->delay--;
+ /* process output fifo and output queue*/
+ fi=r->f_inputs[0];
+ if (fi!=NULL)
+ {
+ ts=get_new_timestamp(r,synctime);
+ /* try to read r->packet_size bytes and send them in a rtp packet*/
+ ms_fifo_get_read_ptr(fi,r->packet_size,&s);
+ if (!skip){
+ rtp_session_send_with_ts(r->rtpsession,s,r->packet_size,ts);
+ ms_trace("len=%i, ts=%i ",r->packet_size,ts);
+ }
+ }
+ qi=r->q_inputs[0];
+ if (qi!=NULL)
+ {
+ MSMessage *msg;
+ /* read a MSMessage and send it through the network*/
+ while ( (msg=ms_queue_get(qi))!=NULL){
+ ts=get_new_timestamp(r,synctime);
+ if (!skip) {
+ /*g_message("Sending packet with ts=%u",ts);*/
+ rtp_session_send_with_ts(r->rtpsession,msg->data,msg->size,ts);
+
+ }
+ ms_message_destroy(msg);
+ }
+ }
+ ms_filter_unlock(MS_FILTER(r));
+}
+
+void ms_rtp_send_destroy( MSRtpSend *obj)
+{
+ g_free(obj);
+}
+
+RtpSession * ms_rtp_send_set_session(MSRtpSend *obj,RtpSession *session)
+{
+ RtpSession *old=obj->rtpsession;
+ obj->rtpsession=session;
+ obj->ts=0;
+ obj->ts_inc=0;
+ return old;
+}
+
+void ms_rtp_send_setup(MSRtpSend *r, MSSync *sync)
+{
+ MSFilter *codec;
+ MSCodecInfo *info;
+ r->sync=sync;
+ codec=ms_filter_search_upstream_by_type(MS_FILTER(r),MS_FILTER_AUDIO_CODEC);
+ if (codec==NULL) codec=ms_filter_search_upstream_by_type(MS_FILTER(r),MS_FILTER_VIDEO_CODEC);
+ if (codec==NULL){
+ g_warning("ms_rtp_send_setup: could not find upstream codec.");
+ return;
+ }
+ info=MS_CODEC_INFO(codec->klass->info);
+ if (info->info.type==MS_FILTER_AUDIO_CODEC){
+ int ts_inc=info->fr_size/2;
+ int psize=info->dt_size;
+ if (ts_inc==0){
+ /* dont'use the normal frame size: this is a variable frame size codec */
+ /* use the MS_FILTER(codec)->r_mingran */
+ ts_inc=MS_FILTER(codec)->r_mingran/2;
+ psize=0;
+ }
+ ms_rtp_send_set_timing(r,ts_inc,psize);
+ }
+}
+
+gint ms_rtp_send_dtmf(MSRtpSend *r, gchar dtmf)
+{
+ gint res;
+
+ if (r->rtpsession==NULL) return -1;
+ if (rtp_session_telephone_events_supported(r->rtpsession)==-1){
+ g_warning("ERROR : telephone events not supported.\n");
+ return -1;
+ }
+
+ ms_filter_lock(MS_FILTER(r));
+ g_message("Sending DTMF.");
+ res=rtp_session_send_dtmf(r->rtpsession, dtmf, r->ts);
+ if (res==0){
+ /* //r->ts+=r->ts_inc; */
+ r->delay+=2;
+ }else g_warning("Could not send dtmf.");
+
+ ms_filter_unlock(MS_FILTER(r));
+
+ return res;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h
new file mode 100644
index 00000000..b70f4e55
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msrtpsend.h
@@ -0,0 +1,85 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSRTPSEND_H
+#define MSRTPSEND_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+#undef PACKAGE
+#undef VERSION
+#include <ortp/ortp.h>
+
+
+/*this is the class that implements a sending through rtp filter*/
+
+#define MSRTPSEND_MAX_INPUTS 1 /* max input per filter*/
+
+#define MSRTPSEND_DEF_GRAN 4096/* the default granularity*/
+
+struct _MSRtpSend
+{
+ /* the MSCopy derivates from MSFilter, so the MSFilter object MUST be the first of the MSCopy object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSRTPSEND_MAX_INPUTS];
+ MSQueue *q_inputs[MSRTPSEND_MAX_INPUTS];
+ MSSync *sync;
+ RtpSession *rtpsession;
+ guint32 ts;
+ guint32 ts_inc; /* the timestamp increment */
+ gint packet_size;
+ guint flags;
+ guint delay; /* number of _proccess call which must be skipped */
+#define RTPSEND_CONFIGURED (1)
+};
+
+typedef struct _MSRtpSend MSRtpSend;
+
+struct _MSRtpSendClass
+{
+ /* the MSRtpSend derivates from MSFilter, so the MSFilter class MUST be the first of the MSCopy class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSRtpSendClass MSRtpSendClass;
+
+/* PUBLIC */
+#define MS_RTP_SEND(filter) ((MSRtpSend*)(filter))
+#define MS_RTP_SEND_CLASS(klass) ((MSRtpSendClass*)(klass))
+MSFilter * ms_rtp_send_new(void);
+RtpSession * ms_rtp_send_set_session(MSRtpSend *obj,RtpSession *session);
+#define ms_rtp_send_unset_session(obj) (ms_rtp_send_set_session((obj),NULL))
+#define ms_rtp_send_get_session(obj) ((obj)->rtpsession)
+void ms_rtp_send_set_timing(MSRtpSend *r, guint32 ts_inc, gint payload_size);
+gint ms_rtp_send_dtmf(MSRtpSend *r, gchar dtmf);
+
+
+/* FOR INTERNAL USE*/
+void ms_rtp_send_init(MSRtpSend *r);
+void ms_rtp_send_class_init(MSRtpSendClass *klass);
+void ms_rtp_send_destroy( MSRtpSend *obj);
+void ms_rtp_send_process(MSRtpSend *r);
+void ms_rtp_send_setup(MSRtpSend *r, MSSync *sync);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h
new file mode 100644
index 00000000..fd6ec547
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssdlout.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * mssdlout.h
+ *
+ * Mon Jul 11 16:18:55 2005
+ * Copyright 2005 Simon Morlat
+ * Email simon dot morlat at linphone dot org
+ ****************************************************************************/
+
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 mssdlout_h
+#define mssdlout_h
+
+#include "msfilter.h"
+
+#include <SDL/SDL.h>
+#include <SDL/SDL_video.h>
+
+struct _MSSdlOut
+{
+ MSFilter parent;
+ MSQueue *input[2];
+ gint width,height;
+ const gchar *format;
+ SDL_Surface *screen;
+ SDL_Overlay *overlay;
+ MSMessage *oldinm1;
+ gboolean use_yuv;
+};
+
+
+typedef struct _MSSdlOut MSSdlOut;
+
+struct _MSSdlOutClass
+{
+ MSFilterClass parent_class;
+};
+
+typedef struct _MSSdlOutClass MSSdlOutClass;
+
+MSFilter * ms_sdl_out_new(void);
+void ms_sdl_out_set_format(MSSdlOut *obj, const char *fmt);
+
+#define MS_SDL_OUT(obj) ((MSSdlOut*)obj)
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c
new file mode 100644
index 00000000..3803b018
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.c
@@ -0,0 +1,39 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+
+ */
+
+#include "mssoundread.h"
+
+
+void ms_sound_read_init(MSSoundRead *w)
+{
+ ms_filter_init(MS_FILTER(w));
+
+}
+
+void ms_sound_read_class_init(MSSoundReadClass *klass)
+{
+ int i;
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ MS_FILTER_CLASS(klass)->max_foutputs=1; /* one fifo output only */
+
+ ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_IS_SOURCE);
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h
new file mode 100644
index 00000000..7f2cab93
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundread.h
@@ -0,0 +1,80 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSSOUNDREAD_H
+#define MSSOUNDREAD_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+
+
+struct _MSSoundRead
+{
+ /* the MSOssRead derivates from MSFilter, so the MSFilter object MUST be the first of the MSOssRead object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+};
+
+typedef struct _MSSoundRead MSSoundRead;
+
+struct _MSSoundReadClass
+{
+ /* the MSOssRead derivates from MSFilter, so the MSFilter class MUST be the first of the MSOssRead class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+ gint (*set_device)(MSSoundRead *, gint devid);
+ void (*start)(MSSoundRead *);
+ void (*stop)(MSSoundRead*);
+ void (*set_level)(MSSoundRead *, gint a);
+};
+
+typedef struct _MSSoundReadClass MSSoundReadClass;
+
+/* PUBLIC */
+#define MS_SOUND_READ(filter) ((MSSoundRead*)(filter))
+#define MS_SOUND_READ_CLASS(klass) ((MSSoundReadClass*)(klass))
+
+static inline int ms_sound_read_set_device(MSSoundRead *r,gint devid)
+{
+ return MS_SOUND_READ_CLASS( MS_FILTER(r)->klass )->set_device(r,devid);
+}
+
+static inline void ms_sound_read_start(MSSoundRead *r)
+{
+ MS_SOUND_READ_CLASS( MS_FILTER(r)->klass )->start(r);
+}
+
+static inline void ms_sound_read_stop(MSSoundRead *w)
+{
+ MS_SOUND_READ_CLASS( MS_FILTER(w)->klass )->stop(w);
+}
+
+static inline void ms_sound_read_set_level(MSSoundRead *w,gint a)
+{
+ MS_SOUND_READ_CLASS( MS_FILTER(w)->klass )->set_level(w,a);
+}
+
+/* FOR INTERNAL USE*/
+void ms_sound_read_init(MSSoundRead *r);
+void ms_sound_read_class_init(MSSoundReadClass *klass);
+
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c
new file mode 100644
index 00000000..9c5879f4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.c
@@ -0,0 +1,39 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+
+ */
+
+#include "mssoundwrite.h"
+
+
+void ms_sound_write_init(MSSoundWrite *w)
+{
+ ms_filter_init(MS_FILTER(w));
+
+}
+
+void ms_sound_write_class_init(MSSoundWriteClass *klass)
+{
+ int i;
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ MS_FILTER_CLASS(klass)->max_finputs=1; /* one fifo output only */
+
+ ms_filter_class_set_attr( MS_FILTER_CLASS(klass),FILTER_IS_SINK);
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h
new file mode 100644
index 00000000..e6d79874
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssoundwrite.h
@@ -0,0 +1,80 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSSOUNDWRITE_H
+#define MSSOUNDWRITE_H
+
+#include "msfilter.h"
+#include "mssync.h"
+
+
+
+struct _MSSoundWrite
+{
+ /* the MSOssWrite derivates from MSFilter, so the MSFilter object MUST be the first of the MSOssWrite object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+};
+
+typedef struct _MSSoundWrite MSSoundWrite;
+
+struct _MSSoundWriteClass
+{
+ /* the MSOssWrite derivates from MSFilter, so the MSFilter class MUST be the first of the MSOssWrite class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+ gint (*set_device)(MSSoundWrite *, gint devid);
+ void (*start)(MSSoundWrite *);
+ void (*stop)(MSSoundWrite*);
+ void (*set_level)(MSSoundWrite *, gint a);
+};
+
+typedef struct _MSSoundWriteClass MSSoundWriteClass;
+
+/* PUBLIC */
+#define MS_SOUND_WRITE(filter) ((MSSoundWrite*)(filter))
+#define MS_SOUND_WRITE_CLASS(klass) ((MSSoundWriteClass*)(klass))
+
+static inline int ms_sound_write_set_device(MSSoundWrite *r,gint devid)
+{
+ return MS_SOUND_WRITE_CLASS( MS_FILTER(r)->klass )->set_device(r,devid);
+}
+
+static inline void ms_sound_write_start(MSSoundWrite *r)
+{
+ MS_SOUND_WRITE_CLASS( MS_FILTER(r)->klass )->start(r);
+}
+
+static inline void ms_sound_write_stop(MSSoundWrite *w)
+{
+ MS_SOUND_WRITE_CLASS( MS_FILTER(w)->klass )->stop(w);
+}
+
+static inline void ms_sound_write_set_level(MSSoundWrite *w,gint a)
+{
+ MS_SOUND_WRITE_CLASS( MS_FILTER(w)->klass )->set_level(w,a);
+}
+
+/* FOR INTERNAL USE*/
+void ms_sound_write_init(MSSoundWrite *r);
+void ms_sound_write_class_init(MSSoundWriteClass *klass);
+
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c
new file mode 100644
index 00000000..b91ca360
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.c
@@ -0,0 +1,218 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <config.h>
+
+#ifdef HAVE_SPEEX
+
+#include "msspeexdec.h"
+
+#ifdef HAVE_GLIB
+#include <gmodule.h>
+#endif
+
+extern MSFilter * ms_speex_enc_new();
+
+MSCodecInfo speex_info=
+{
+ {
+ "Speex codec",
+ 0,
+ MS_FILTER_AUDIO_CODEC,
+ ms_speex_dec_new,
+ "A high quality variable bit-rate codec from Jean Marc Valin and David Rowe."
+ },
+ ms_speex_enc_new,
+ ms_speex_dec_new,
+ 0, /*frame size */
+ 0,
+ 8000, /*minimal bitrate */
+ -1, /* sampling frequency */
+ 110, /* payload type */
+ "speex",
+ 1,
+ 1
+};
+
+
+
+void ms_speex_codec_init()
+{
+
+ ms_filter_register(MS_FILTER_INFO(&speex_info));
+ /* //ms_filter_register(MS_FILTER_INFO(&speex_lbr_info)); */
+}
+
+#ifdef HAVE_GLIB
+gchar * g_module_check_init(GModule *module)
+{
+ ms_speex_codec_init();
+
+ return NULL;
+}
+#else
+gchar * g_module_check_init()
+{
+ ms_speex_codec_init();
+
+ return NULL;
+}
+#endif
+
+static MSSpeexDecClass * ms_speex_dec_class=NULL;
+/* //static MSSpeexDecClass * ms_speexnb_dec_class=NULL; */
+
+MSFilter * ms_speex_dec_new()
+{
+ MSSpeexDec *obj=g_new(MSSpeexDec,1);
+
+ if (ms_speex_dec_class==NULL){
+ ms_speex_dec_class=g_new(MSSpeexDecClass,1);
+ ms_speex_dec_class_init(ms_speex_dec_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_speex_dec_class);
+
+ ms_speex_dec_init(obj);
+ return MS_FILTER(obj);
+}
+
+void ms_speex_dec_init(MSSpeexDec *obj)
+{
+ ms_filter_init(MS_FILTER(obj));
+ obj->initialized=0;
+ MS_FILTER(obj)->outfifos=obj->outf;
+ MS_FILTER(obj)->inqueues=obj->inq;
+ obj->outf[0]=NULL;
+ obj->inq[0]=NULL;
+ obj->frequency=8000; /*default value */
+
+}
+
+void ms_speex_dec_init_core(MSSpeexDec *obj,const SpeexMode *mode)
+{
+ int pf=1;
+
+ obj->speex_state=speex_decoder_init(mode);
+ speex_bits_init(&obj->bits);
+ /* enable the perceptual post filter */
+ speex_decoder_ctl(obj->speex_state,SPEEX_SET_PF, &pf);
+
+ speex_mode_query(mode, SPEEX_MODE_FRAME_SIZE, &obj->frame_size);
+
+ obj->initialized=1;
+}
+
+int ms_speex_dec_set_property(MSSpeexDec *obj, MSFilterProperty prop, int *value)
+{
+ if (obj->initialized){
+ /* we are called when speex is running !! forbid that! */
+ ms_warning("ms_speex_dec_set_property: cannot call this function when running!");
+ return -1;
+ }
+ switch(prop){
+ case MS_FILTER_PROPERTY_FREQ:
+ obj->frequency=value[0];
+ break;
+ }
+ return 0;
+}
+
+void ms_speex_dec_setup(MSSpeexDec *obj)
+{
+ const SpeexMode *mode;
+ g_message("Speex decoder setup: freq=%i",obj->frequency);
+ if ( obj->frequency< 16000) mode=&speex_nb_mode;
+ else mode=&speex_wb_mode;
+ ms_speex_dec_init_core(obj,mode);
+}
+
+void ms_speex_dec_unsetup(MSSpeexDec *obj)
+{
+ ms_speex_dec_uninit_core(obj);
+}
+
+void ms_speex_dec_class_init(MSSpeexDecClass *klass)
+{
+ gint frame_size=0;
+
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ /* use the largest frame size to configure fifos */
+ speex_mode_query(&speex_wb_mode, SPEEX_MODE_FRAME_SIZE, &frame_size);
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_speex_dec_process;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_speex_dec_setup;
+ MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_speex_dec_unsetup;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_speex_dec_destroy;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_speex_dec_set_property;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"SpeexDecoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&speex_info;
+ MS_FILTER_CLASS(klass)->max_foutputs=1;
+ MS_FILTER_CLASS(klass)->max_qinputs=1;
+ MS_FILTER_CLASS(klass)->w_maxgran=frame_size*2;
+ ms_trace("ms_speex_dec_class_init: w_maxgran is %i.",MS_FILTER_CLASS(klass)->w_maxgran);
+}
+
+void ms_speex_dec_uninit_core(MSSpeexDec *obj)
+{
+ speex_decoder_destroy(obj->speex_state);
+ obj->initialized=0;
+}
+
+void ms_speex_dec_uninit(MSSpeexDec *obj)
+{
+
+}
+
+void ms_speex_dec_destroy(MSSpeexDec *obj)
+{
+ ms_speex_dec_uninit(obj);
+ g_free(obj);
+}
+
+void ms_speex_dec_process(MSSpeexDec *obj)
+{
+ MSFifo *outf=obj->outf[0];
+ MSQueue *inq=obj->inq[0];
+ gint16 *output;
+ gint gran=obj->frame_size*2;
+ gint i;
+ MSMessage *m;
+
+ g_return_if_fail(inq!=NULL);
+ g_return_if_fail(outf!=NULL);
+
+ m=ms_queue_get(inq);
+ g_return_if_fail(m!=NULL);
+ speex_bits_reset(&obj->bits);
+ ms_fifo_get_write_ptr(outf,gran,(void**)&output);
+ g_return_if_fail(output!=NULL);
+ if (m->data!=NULL){
+
+ speex_bits_read_from(&obj->bits,m->data,m->size);
+ /* decode */
+ speex_decode_int(obj->speex_state,&obj->bits,(short*)output);
+ }else{
+ /* we have a missing packet */
+ speex_decode_int(obj->speex_state,NULL,(short*)output);
+ }
+ ms_message_destroy(m);
+
+}
+
+#endif /* HAVE_SPEEX */
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h
new file mode 100644
index 00000000..d4e745fe
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexdec.h
@@ -0,0 +1,69 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSSPEEXDEC_H
+#define MSSPEEXDEC_H
+
+#include <mscodec.h>
+#include <speex.h>
+
+struct _MSSpeexDec
+{
+ MSFilter parent;
+ MSQueue *inq[1]; /* speex has an input q because it can be variable bit rate */
+ MSFifo *outf[1];
+ void *speex_state;
+ SpeexBits bits;
+ int frequency;
+ int frame_size;
+ int initialized;
+};
+
+typedef struct _MSSpeexDec MSSpeexDec;
+
+
+struct _MSSpeexDecClass
+{
+ MSFilterClass parent;
+};
+
+typedef struct _MSSpeexDecClass MSSpeexDecClass;
+
+
+#define MS_SPEEX_DEC(o) ((MSSpeexDec*)(o))
+#define MS_SPEEX_DEC_CLASS(o) ((MSSpeexDecClass*)(o))
+
+/* call this before if don't load the plugin dynamically */
+void ms_speex_codec_init();
+
+/* mediastreamer compliant constructor */
+MSFilter * ms_speex_dec_new();
+
+void ms_speex_dec_init(MSSpeexDec *obj);
+void ms_speex_dec_init_core(MSSpeexDec *obj,const SpeexMode *mode);
+void ms_speex_dec_class_init(MSSpeexDecClass *klass);
+void ms_speex_dec_uninit(MSSpeexDec *obj);
+void ms_speex_dec_uninit_core(MSSpeexDec *obj);
+
+void ms_speex_dec_process(MSSpeexDec *obj);
+void ms_speex_dec_destroy(MSSpeexDec *obj);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c
new file mode 100644
index 00000000..abf976e6
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.c
@@ -0,0 +1,192 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 <config.h>
+
+#ifdef HAVE_SPEEX
+
+#include "msspeexenc.h"
+#include "ms.h"
+extern MSCodecInfo speex_info;
+
+static MSSpeexEncClass * ms_speex_enc_class=NULL;
+
+MSFilter * ms_speex_enc_new()
+{
+ MSSpeexEnc *obj=g_new(MSSpeexEnc,1);
+
+ if (ms_speex_enc_class==NULL){
+ ms_speex_enc_class=g_new(MSSpeexEncClass,1);
+ ms_speex_enc_class_init(ms_speex_enc_class);
+ }
+ MS_FILTER(obj)->klass=MS_FILTER_CLASS(ms_speex_enc_class);
+ ms_speex_enc_init(MS_SPEEX_ENC(obj));
+ return MS_FILTER(obj);
+}
+
+void ms_speex_enc_init(MSSpeexEnc *obj)
+{
+ ms_filter_init(MS_FILTER(obj));
+ MS_FILTER(obj)->infifos=obj->inf;
+ MS_FILTER(obj)->outqueues=obj->outq;
+ obj->inf[0]=NULL;
+ obj->outq[0]=NULL;
+ obj->frequency=8000;
+ obj->bitrate=30000;
+ obj->initialized=0;
+}
+
+void ms_speex_enc_init_core(MSSpeexEnc *obj,const SpeexMode *mode, gint bitrate)
+{
+ int proc_type, proc_speed;
+ gchar *proc_vendor;
+ int tmp;
+ int frame_size;
+
+ obj->speex_state=speex_encoder_init(mode);
+ speex_bits_init(&obj->bits);
+
+ if (bitrate>0) {
+ bitrate++;
+ speex_encoder_ctl(obj->speex_state, SPEEX_SET_BITRATE, &bitrate);
+ g_message("Setting speex output bitrate less or equal than %i",bitrate-1);
+ }
+
+ proc_speed=ms_proc_get_speed();
+ proc_vendor=ms_proc_get_param("vendor_id");
+ if (proc_speed<0 || proc_vendor==NULL){
+ g_warning("Can't guess processor features: setting speex encoder to its lowest complexity.");
+ tmp=1;
+ speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp);
+ }else if ((proc_speed!=-1) && (proc_speed<200)){
+ g_warning("A cpu speed less than 200 Mhz is not enough: let's reduce the complexity of the speex codec.");
+ tmp=1;
+ speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp);
+ }else if (proc_vendor!=NULL) {
+ if (strncmp(proc_vendor,"GenuineIntel",strlen("GenuineIntel"))==0){
+ proc_type=ms_proc_get_type();
+ if (proc_type==5){
+ g_warning("A pentium I is not enough fast for speex codec in normal mode: let's reduce its complexity.");
+ tmp=1;
+ speex_encoder_ctl(obj->speex_state,SPEEX_SET_COMPLEXITY,&tmp);
+ }
+ }
+ g_free(proc_vendor);
+ }
+ /* guess the used input frame size */
+ speex_mode_query(mode, SPEEX_MODE_FRAME_SIZE, &frame_size);
+ MS_FILTER(obj)->r_mingran=frame_size*2;
+ ms_trace("ms_speex_init: using frame size of %i.",MS_FILTER(obj)->r_mingran);
+
+ obj->initialized=1;
+}
+
+/* must be called before the encoder is running*/
+int ms_speex_enc_set_property(MSSpeexEnc *obj,int property,int *value)
+{
+ if (obj->initialized){
+ /* we are called when speex is running !! forbid that! */
+ ms_warning("ms_speex_enc_set_property: cannot call this function when running!");
+ return -1;
+ }
+ switch(property){
+ case MS_FILTER_PROPERTY_FREQ:
+ obj->frequency=value[0];
+ break;
+ case MS_FILTER_PROPERTY_BITRATE: /* to specify max bitrate */
+ obj->bitrate=value[0];
+ break;
+ }
+ return 0;
+}
+
+void ms_speex_enc_setup(MSSpeexEnc *obj)
+{
+ const SpeexMode *mode;
+ int quality;
+ g_message("Speex encoder setup: freq=%i",obj->frequency);
+ if ( obj->frequency< 16000) mode=&speex_nb_mode;
+ else mode=&speex_wb_mode;
+ ms_speex_enc_init_core(obj,mode,obj->bitrate);
+
+}
+
+void ms_speex_enc_unsetup(MSSpeexEnc *obj)
+{
+ ms_speex_enc_uninit_core(obj);
+}
+
+void ms_speex_enc_class_init(MSSpeexEncClass *klass)
+{
+ gint frame_size=0;
+
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ /* we take the larger (wb) frame size */
+ speex_mode_query(&speex_wb_mode, SPEEX_MODE_FRAME_SIZE, &frame_size);
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_speex_enc_process;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_speex_enc_destroy;
+ MS_FILTER_CLASS(klass)->setup=(MSFilterSetupFunc)ms_speex_enc_setup;
+ MS_FILTER_CLASS(klass)->unsetup=(MSFilterSetupFunc)ms_speex_enc_unsetup;
+ MS_FILTER_CLASS(klass)->set_property=(MSFilterPropertyFunc)ms_speex_enc_set_property;
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"SpeexEncoder");
+ MS_FILTER_CLASS(klass)->info=(MSFilterInfo*)&speex_info;
+ MS_FILTER_CLASS(klass)->max_finputs=1;
+ MS_FILTER_CLASS(klass)->max_qoutputs=1;
+ MS_FILTER_CLASS(klass)->r_maxgran=frame_size*2;
+ ms_trace("ms_speex_enc_class_init: r_maxgran is %i.",MS_FILTER_CLASS(klass)->r_maxgran);
+}
+
+void ms_speex_enc_uninit_core(MSSpeexEnc *obj)
+{
+ if (obj->initialized){
+ speex_encoder_destroy(obj->speex_state);
+ obj->initialized=0;
+ }
+}
+
+void ms_speex_enc_destroy(MSSpeexEnc *obj)
+{
+ ms_speex_enc_uninit_core(obj);
+ g_free(obj);
+}
+
+void ms_speex_enc_process(MSSpeexEnc *obj)
+{
+ MSFifo *inf=obj->inf[0];
+ MSQueue *outq=obj->outq[0];
+ gint16 *input;
+ gint gran=MS_FILTER(obj)->r_mingran;
+ gint i;
+ MSMessage *m;
+
+ g_return_if_fail(inf!=NULL);
+ g_return_if_fail(outq!=NULL);
+
+ ms_fifo_get_read_ptr(inf,gran,(void**)&input);
+ g_return_if_fail(input!=NULL);
+ /* encode */
+ speex_bits_reset(&obj->bits);
+ speex_encode_int(obj->speex_state,(short*)input,&obj->bits);
+ m=ms_message_new(speex_bits_nbytes(&obj->bits));
+ m->size=speex_bits_write(&obj->bits,m->data,m->size);
+ ms_queue_put(outq,m);
+}
+
+#endif /* HAVE_SPEEX */
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h
new file mode 100644
index 00000000..41655b9f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msspeexenc.h
@@ -0,0 +1,66 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSSPEEXENC_H
+#define MSSPEEXENC_H
+
+#include <mscodec.h>
+#include <speex.h>
+
+struct _MSSpeexEnc
+{
+ MSFilter parent;
+ MSFifo *inf[1];
+ MSQueue *outq[1]; /* speex has an output q because it can be variable bit rate */
+ void *speex_state;
+ SpeexBits bits;
+ int frequency;
+ int bitrate;
+ int initialized;
+};
+
+typedef struct _MSSpeexEnc MSSpeexEnc;
+
+
+struct _MSSpeexEncClass
+{
+ MSFilterClass parent;
+};
+
+typedef struct _MSSpeexEncClass MSSpeexEncClass;
+
+
+#define MS_SPEEX_ENC(o) ((MSSpeexEnc*)(o))
+#define MS_SPEEX_ENC_CLASS(o) ((MSSpeexEncClass*)(o))
+
+/* generic constructor */
+MSFilter * ms_speex_enc_new();
+
+void ms_speex_enc_init_core(MSSpeexEnc *obj,const SpeexMode *mode, gint quality);
+void ms_speex_enc_uninit_core(MSSpeexEnc *obj);
+void ms_speex_enc_init(MSSpeexEnc *obj);
+void ms_speex_enc_class_init(MSSpeexEncClass *klass);
+
+
+void ms_speex_enc_process(MSSpeexEnc *obj);
+void ms_speex_enc_destroy(MSSpeexEnc *obj);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c
new file mode 100644
index 00000000..7656211b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.c
@@ -0,0 +1,193 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mssync.h"
+#include <errno.h>
+
+/* TODO:
+ -define an uninit function that free the mutex
+*/
+
+/**
+ * function_name:ms_sync_get_bytes_per_tick
+ * @sync: A #MSSync object.
+ *
+ * Returns the number of bytes per tick. This is a usefull information for sources, so
+ * that they can know how much data they must deliver each time they are called.
+ *
+ */
+
+/* private */
+void ms_sync_init(MSSync *sync)
+{
+ sync->klass=NULL;
+ sync->lock=g_mutex_new();
+ sync->thread_cond=g_cond_new();
+ sync->stop_cond=g_cond_new();
+ sync->attached_filters=NULL;
+ sync->execution_list=NULL;
+ sync->filters=0;
+ sync->run=0;
+ sync->flags=0;
+ sync->samples_per_tick=0;
+ sync->ticks=0;
+ sync->time=0;
+ sync->thread=NULL;
+}
+
+void ms_sync_class_init(MSSyncClass *klass)
+{
+ klass->max_filters=0;
+ klass->synchronize=NULL;
+ klass->attach=ms_sync_attach_generic;
+ klass->detach=ms_sync_detach_generic;
+ klass->destroy=NULL;
+}
+
+/* public*/
+
+
+/**
+ * ms_sync_attach:
+ * @sync: A #MSSync object.
+ * @f: A #MSFilter object.
+ *
+ * Attach a chain of filters to a synchronisation source @sync. Filter @f must be the first filter of the processing chain.
+ * In order to be run, each chain of filter must be attached to a synchronisation source, that will be responsible for scheduling
+ * the processing. Multiple chains can be attached to a single synchronisation.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_sync_attach(MSSync *sync,MSFilter *f)
+{
+ gint err;
+ ms_sync_lock(sync);
+ err=sync->klass->attach(sync,f);
+ ms_sync_update(sync);
+ ms_sync_unlock(sync);
+ return(err);
+}
+
+int ms_sync_attach_generic(MSSync *sync,MSFilter *f)
+{
+ int i;
+ /* //printf("attr: %i\n",f->klass->attributes); */
+ g_return_val_if_fail(f->klass->attributes & FILTER_IS_SOURCE,-EINVAL);
+ g_return_val_if_fail(sync->attached_filters!=NULL,-EFAULT);
+
+
+ /* find a free place to attach*/
+ for (i=0;i<sync->klass->max_filters;i++)
+ {
+ if (sync->attached_filters[i]==NULL)
+ {
+ sync->attached_filters[i]=f;
+ sync->filters++;
+ ms_trace("Filter succesfully attached to sync.");
+ return 0;
+ }
+ }
+ g_warning("No more link on sync !");
+ return(-EMLINK);
+}
+
+/**
+ * ms_sync_detach:
+ * @sync: A #MSSync object.
+ * @f: A #MSFilter object.
+ *
+ * Dettach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain.
+ * The processing chain will no more be executed.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_sync_detach(MSSync *sync,MSFilter *f)
+{
+ gint err;
+ ms_sync_lock(sync);
+ err=sync->klass->detach(sync,f);
+ ms_sync_update(sync);
+ ms_sync_unlock(sync);
+ return(err);
+}
+
+int ms_sync_detach_generic(MSSync *sync,MSFilter *f)
+{
+ int i;
+ g_return_val_if_fail(f->klass->attributes & FILTER_IS_SOURCE,-EINVAL);
+ g_return_val_if_fail(sync->attached_filters!=NULL,-EFAULT);
+ for (i=0;i<sync->filters;i++)
+ {
+ if (sync->attached_filters[i]==f)
+ {
+ sync->attached_filters[i]=NULL;
+ sync->filters--;
+ return 0;
+ }
+ }
+ return(-EMLINK);
+}
+
+void ms_sync_set_samples_per_tick(MSSync *sync,gint size)
+{
+ if (sync->samples_per_tick==0)
+ {
+ sync->samples_per_tick=size;
+ g_cond_signal(sync->thread_cond);
+ }
+ else sync->samples_per_tick=size;
+}
+
+/* call the setup func of each filter attached to the graph */
+void ms_sync_setup(MSSync *sync)
+{
+ GList *elem=sync->execution_list;
+ MSFilter *f;
+ while(elem!=NULL){
+ f=(MSFilter*)elem->data;
+ if (f->klass->setup!=NULL){
+ f->klass->setup(f,sync);
+ }
+ elem=g_list_next(elem);
+ }
+}
+
+/* call the unsetup func of each filter attached to the graph */
+void ms_sync_unsetup(MSSync *sync)
+{
+ GList *elem=sync->execution_list;
+ MSFilter *f;
+ while(elem!=NULL){
+ f=(MSFilter*)elem->data;
+ if (f->klass->unsetup!=NULL){
+ f->klass->unsetup(f,sync);
+ }
+ elem=g_list_next(elem);
+ }
+}
+
+
+int ms_sync_uninit(MSSync *sync)
+{
+ g_mutex_free(sync->lock);
+ g_cond_free(sync->thread_cond);
+ g_cond_free(sync->stop_cond);
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h
new file mode 100644
index 00000000..012c068f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mssync.h
@@ -0,0 +1,136 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MS_SYNC_H
+#define MS_SYNC_H
+
+
+#include "msfilter.h"
+
+struct _MSSync
+{
+ struct _MSSyncClass *klass;
+ GMutex *lock;
+ MSFilter **attached_filters; /* pointer to a table of pointer of filters*/
+ GList *execution_list; /* the list of filters to be executed. This is filled with compilation */
+ gint filters; /*number of filters attached to the sync */
+ gint run; /* flag to indicate whether the sync must be run or not */
+ GThread * thread; /* the thread ressource if this sync is run by a thread*/
+ GCond *thread_cond;
+ GCond *stop_cond;
+ guint32 flags;
+ gint interval; /* in miliseconds*/
+#define MS_SYNC_NEED_UPDATE (0x0001) /* a modification has occured in the processing chains
+ attached to this sync; so the execution list has to be updated */
+ guint samples_per_tick; /* number of bytes produced by sources of the processing chains*/
+ guint32 ticks;
+ guint32 time; /* a time since the start of the sync expressed in milisec*/
+};
+
+typedef struct _MSSync MSSync;
+
+typedef void (*MSSyncDestroyFunc)(MSSync*);
+typedef void (*MSSyncSyncFunc)(MSSync*);
+typedef int (*MSSyncAttachFunc)(MSSync*,MSFilter*);
+typedef int (*MSSyncDetachFunc)(MSSync*,MSFilter*);
+
+typedef struct _MSSyncClass
+{
+ gint max_filters; /* the maximum number of filters that can be attached to this sync*/
+ MSSyncSyncFunc synchronize;
+ MSSyncDestroyFunc destroy;
+ MSSyncAttachFunc attach;
+ MSSyncDetachFunc detach;
+} MSSyncClass;
+
+/* private */
+void ms_sync_init(MSSync *sync);
+void ms_sync_class_init(MSSyncClass *klass);
+
+int ms_sync_attach_generic(MSSync *sync,MSFilter *f);
+int ms_sync_detach_generic(MSSync *sync,MSFilter *f);
+
+/* public*/
+
+#define MS_SYNC(sync) ((MSSync*)(sync))
+#define MS_SYNC_CLASS(klass) ((MSSyncClass*)(klass))
+
+#define ms_sync_synchronize(_sync) \
+do \
+{ \
+ MSSync *__sync=_sync; \
+ __sync->ticks++; \
+ ((__sync)->klass->synchronize((__sync))); \
+}while(0)
+
+void ms_sync_setup(MSSync *sync);
+
+void ms_sync_unsetup(MSSync *sync);
+
+#define ms_sync_update(sync) (sync)->flags|=MS_SYNC_NEED_UPDATE
+
+#define ms_sync_get_samples_per_tick(sync) ((sync)->samples_per_tick)
+
+void ms_sync_set_samples_per_tick(MSSync *sync,gint size);
+
+#define ms_sync_get_tick_count(sync) ((sync)->ticks)
+
+#define ms_sync_suspend(sync) g_cond_wait((sync)->thread_cond,(sync)->lock)
+
+#define ms_sync_lock(sync) g_mutex_lock((sync)->lock)
+
+#define ms_sync_unlock(sync) g_mutex_unlock((sync)->lock)
+
+#define ms_sync_trylock(sync) g_mutex_trylock((sync)->lock)
+
+/**
+ * function_name:ms_sync_attach
+ * @sync: A #MSSync object.
+ * @f: A #MSFilter object.
+ *
+ * Attach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_sync_attach(MSSync *sync,MSFilter *f);
+
+/**
+ * ms_sync_detach:
+ * @sync: A #MSSync object.
+ * @f: A #MSFilter object.
+ *
+ * Dettach a chain of filters to a synchronisation source. Filter @f must be the first filter of the processing chain.
+ * The processing chain will no more be executed.
+ *
+ * Returns: 0 if successfull, a negative value reprensenting the errno.h error.
+ */
+int ms_sync_detach(MSSync *sync,MSFilter *f);
+
+int ms_sync_uninit(MSSync *sync);
+
+#define ms_sync_start(sync) ms_start((sync))
+#define ms_sync_stop(sync) ms_stop((sync))
+
+
+/*destroy*/
+#define ms_sync_destroy(sync) (sync)->klass->destroy((sync))
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c
new file mode 100644
index 00000000..29b81d3c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.c
@@ -0,0 +1,114 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mstimer.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+
+static MSTimerClass *ms_timer_class=NULL;
+
+
+void ms_timer_init(MSTimer *sync)
+{
+ ms_sync_init(MS_SYNC(sync));
+ MS_SYNC(sync)->attached_filters=sync->filters;
+ memset(sync->filters,0,MSTIMER_MAX_FILTERS*sizeof(MSFilter*));
+ MS_SYNC(sync)->samples_per_tick=160;
+ ms_timer_set_interval(sync,20);
+ sync->state=MS_TIMER_STOPPED;
+}
+
+void ms_timer_class_init(MSTimerClass *klass)
+{
+ ms_sync_class_init(MS_SYNC_CLASS(klass));
+ MS_SYNC_CLASS(klass)->max_filters=MSTIMER_MAX_FILTERS;
+ MS_SYNC_CLASS(klass)->synchronize=(MSSyncSyncFunc)ms_timer_synchronize;
+ MS_SYNC_CLASS(klass)->destroy=(MSSyncDestroyFunc)ms_timer_destroy;
+ /* no need to overload these function*/
+ MS_SYNC_CLASS(klass)->attach=ms_sync_attach_generic;
+ MS_SYNC_CLASS(klass)->detach=ms_sync_detach_generic;
+}
+
+void ms_timer_destroy(MSTimer *timer)
+{
+ g_free(timer);
+}
+
+
+void ms_timer_synchronize(MSTimer *timer)
+{
+ /* //printf("ticks=%i \n",MS_SYNC(timer)->ticks); */
+ if (timer->state==MS_TIMER_STOPPED){
+ timer->state=MS_TIMER_RUNNING;
+ gettimeofday(&timer->orig,NULL);
+ timer->sync.time=0;
+ }
+ else {
+ gint32 diff,time;
+ struct timeval tv,cur;
+
+ gettimeofday(&cur,NULL);
+ time=((cur.tv_usec-timer->orig.tv_usec)/1000 ) + ((cur.tv_sec-timer->orig.tv_sec)*1000 );
+ if ( (diff=time-timer->sync.time)>50){
+ g_warning("Must catchup %i miliseconds.",diff);
+ }
+ while((diff = timer->sync.time-time) > 0)
+ {
+ tv.tv_sec = diff/1000;
+ tv.tv_usec = (diff%1000)*1000;
+ select(0,NULL,NULL,NULL,&tv);
+ gettimeofday(&cur,NULL);
+ time=((cur.tv_usec-timer->orig.tv_usec)/1000 ) + ((cur.tv_sec-timer->orig.tv_sec)*1000 );
+ }
+ }
+ timer->sync.time+=timer->milisec;
+ return;
+}
+
+
+MSSync *ms_timer_new()
+{
+ MSTimer *timer;
+
+ timer=g_malloc(sizeof(MSTimer));
+ ms_timer_init(timer);
+ if (ms_timer_class==NULL)
+ {
+ ms_timer_class=g_new(MSTimerClass,1);
+ ms_timer_class_init(ms_timer_class);
+ }
+ MS_SYNC(timer)->klass=MS_SYNC_CLASS(ms_timer_class);
+ return(MS_SYNC(timer));
+}
+
+void ms_timer_set_interval(MSTimer *timer, int milisec)
+{
+
+ MS_SYNC(timer)->ticks=0;
+ MS_SYNC(timer)->interval=milisec;
+ timer->interval.tv_sec=milisec/1000;
+ timer->interval.tv_usec=(milisec % 1000)*1000;
+ timer->milisec=milisec;
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h
new file mode 100644
index 00000000..5c7e8ede
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstimer.h
@@ -0,0 +1,68 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSTIMER_H
+#define MSTIMER_H
+
+#include "mssync.h"
+#include <sys/time.h>
+
+#define MSTIMER_MAX_FILTERS 10
+
+/* MSTimer derivates from MSSync base class*/
+
+typedef struct _MSTimer
+{
+ /* the MSSync must be the first field of the object in order to the object mechanism to work*/
+ MSSync sync;
+ MSFilter *filters[MSTIMER_MAX_FILTERS];
+ gint milisec; /* the interval */
+ struct timeval interval;
+ struct timeval orig;
+ gint state;
+} MSTimer;
+
+
+typedef struct _MSTimerClass
+{
+ /* the MSSyncClass must be the first field of the class in order to the class mechanism to work*/
+ MSSyncClass parent_class;
+} MSTimerClass;
+
+
+/*private*/
+#define MS_TIMER_RUNNING 1
+#define MS_TIMER_STOPPED 0
+void ms_timer_init(MSTimer *sync);
+void ms_timer_class_init(MSTimerClass *sync);
+
+void ms_timer_destroy(MSTimer *timer);
+void ms_timer_synchronize(MSTimer *timer);
+
+/*public*/
+void ms_timer_set_interval(MSTimer *timer, gint milisec);
+
+/* casts a MSSync object into a MSTimer */
+#define MS_TIMER(sync) ((MSTimer*)(sync))
+/* casts a MSSync class into a MSTimer class */
+#define MS_TIMER_CLASS(klass) ((MSTimerClass*)(klass))
+
+MSSync *ms_timer_new();
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h
new file mode 100644
index 00000000..62477436
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechdecoder.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2003 Robert W. Brewer <rbrewer at op.net>
+
+ 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 MSTRUESPEECHDECODER_H
+#define MSTRUESPEECHDECODER_H
+
+#include "msfilter.h"
+#include "mstruespeechencoder.h"
+
+
+
+typedef struct _MSTrueSpeechDecoder
+{
+ /* the MSTrueSpeechDecoder derives from MSFilter, so the MSFilter
+ object MUST be the first of the MSTrueSpeechDecoder object
+ in order for the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT];
+ MSFifo *f_outputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT];
+ Win32Codec* codec;
+} MSTrueSpeechDecoder;
+
+typedef struct _MSTrueSpeechDecoderClass
+{
+ /* the MSTrueSpeechDecoder derives from MSFilter,
+ so the MSFilter class MUST be the first of the MSTrueSpechDecoder
+ class
+ in order for the class mechanism to work*/
+ MSFilterClass parent_class;
+ Win32CodecDriver* driver;
+} MSTrueSpeechDecoderClass;
+
+/* PUBLIC */
+#define MS_TRUESPEECHDECODER(filter) ((MSTrueSpechMDecoder*)(filter))
+#define MS_TRUESPEECHDECODER_CLASS(klass) ((MSTrueSpeechDecoderClass*)(klass))
+MSFilter * ms_truespeechdecoder_new(void);
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h
new file mode 100644
index 00000000..04e40bb8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mstruespeechencoder.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2003 Robert W. Brewer <rbrewer at op.net>
+
+ 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 MSTRUESPEECHENCODER_H
+#define MSTRUESPEECHENCODER_H
+
+#include "msfilter.h"
+#include <win32codec.h>
+
+
+#define MS_TRUESPEECH_CODEC_MAX_IN_OUT 1 /* max inputs/outputs per filter*/
+
+#define TRUESPEECH_FORMAT_TAG 0x22
+#define TRUESPEECH_DLL "tssoft32.acm"
+
+typedef struct _MSTrueSpeechEncoder
+{
+ /* the MSTrueSpeechEncoder derives from MSFilter, so the MSFilter
+ object MUST be the first of the MSTrueSpeechEncoder object
+ in order for the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT];
+ MSFifo *f_outputs[MS_TRUESPEECH_CODEC_MAX_IN_OUT];
+ Win32Codec* codec;
+} MSTrueSpeechEncoder;
+
+typedef struct _MSTrueSpeechEncoderClass
+{
+ /* the MSTrueSpeechEncoder derives from MSFilter,
+ so the MSFilter class MUST be the first of the MSTrueSpechEncoder
+ class
+ in order for the class mechanism to work*/
+ MSFilterClass parent_class;
+ Win32CodecDriver* driver;
+} MSTrueSpeechEncoderClass;
+
+/* PUBLIC */
+#define MS_TRUESPEECHENCODER(filter) ((MSTrueSpechMEncoder*)(filter))
+#define MS_TRUESPEECHENCODER_CLASS(klass) ((MSTrueSpeechEncoderClass*)(klass))
+MSFilter * ms_truespeechencoder_new(void);
+
+/* for internal use only */
+WAVEFORMATEX* ms_truespeechencoder_wf_create();
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h
new file mode 100644
index 00000000..012b87d8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msutils.h
@@ -0,0 +1,61 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSUTILS_H
+#define MSUTILS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#else
+#include <uglib.h>
+#endif
+#include <errno.h>
+
+#ifndef ENODATA
+/* this is for freeBSD .*/
+#define ENODATA EWOULDBLOCK
+#endif
+
+#ifdef MS_DEBUG
+
+#define ms_trace g_message
+
+#else
+
+#define ms_trace(...)
+#endif
+
+#define ms_warning g_warning
+#define ms_error g_error
+
+#define VIDEO_SIZE_CIF_W 352
+#define VIDEO_SIZE_CIF_H 288
+#define VIDEO_SIZE_QCIF_W 176
+#define VIDEO_SIZE_QCIF_H 144
+#define VIDEO_SIZE_4CIF_W 704
+#define VIDEO_SIZE_4CIF_H 576
+#define VIDEO_SIZE_MAX_W VIDEO_SIZE_4CIF_W
+#define VIDEO_SIZE_MAX_H VIDEO_SIZE_4CIF_H
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h
new file mode 100644
index 00000000..e19ac9ea
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msv4l.h
@@ -0,0 +1,96 @@
+ /*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSV4L_H
+#define MSV4L_H
+
+#include <msvideosource.h>
+#include <sys/types.h>
+#include <linux/videodev.h>
+
+struct _MSV4l
+{
+ MSVideoSource parent;
+ int fd;
+ char *device;
+ struct video_capability cap;
+ struct video_channel channel;
+ struct video_window win;
+ struct video_picture pict;
+ struct video_mmap vmap;
+ struct video_mbuf vmbuf;
+ struct video_capture vcap;
+ gint bsize;
+ gint use_mmap;
+ gint frame;
+ guint query_frame;
+ gchar *mmapdbuf; /* the mmap'd buffer */
+ MSBuffer img[VIDEO_MAX_FRAME]; /* the buffer wrappers used for mmaps */
+ gint width; /* the capture image size - can be cropped to output size */
+ gint height;
+ MSBuffer *allocdbuf; /* the buffer allocated for read() and mire */
+ gint count;
+ MSBuffer *image_grabbed;
+ GCond *cond;
+ GCond *stopcond;
+ GThread *v4lthread;
+ gboolean grab_image;
+ gboolean thread_run;
+ gboolean thread_exited;
+};
+
+typedef struct _MSV4l MSV4l;
+
+
+struct _MSV4lClass
+{
+ MSVideoSourceClass parent_class;
+
+};
+
+typedef struct _MSV4lClass MSV4lClass;
+
+
+/* PUBLIC API */
+#define MS_V4L(v) ((MSV4l*)(v))
+#define MS_V4L_CLASS(k) ((MSV4lClass*)(k))
+MSFilter * ms_v4l_new();
+
+void ms_v4l_start(MSV4l *obj);
+void ms_v4l_stop(MSV4l *obj);
+int ms_v4l_set_device(MSV4l *f, const gchar *device);
+gint ms_v4l_get_width(MSV4l *v4l);
+gint ms_v4l_get_height(MSV4l *v4l);
+void ms_v4l_set_size(MSV4l *v4l, gint w, gint h);
+
+/* PRIVATE API */
+void ms_v4l_init(MSV4l *obj);
+void ms_v4l_class_init(MSV4lClass *klass);
+int v4l_configure(MSV4l *f);
+
+void v4l_process(MSV4l *obj);
+
+void ms_v4l_uninit(MSV4l *obj);
+
+void ms_v4l_destroy(MSV4l *obj);
+
+extern MSFilterInfo v4l_info;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h
new file mode 100644
index 00000000..9a27f836
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/msvideosource.h
@@ -0,0 +1,74 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSVIDEOSOURCE_H
+#define MSVIDEOSOURCE_H
+
+
+#include "msfilter.h"
+
+/* this is the video input abstract class */
+
+#define MSVIDEOSOURCE_MAX_OUTPUTS 1 /* max output per filter*/
+
+typedef struct _MSVideoSource
+{
+ /* the MSVideoSource derivates from MSFilter, so the MSFilter object MUST be the first of the MSVideoSource object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSQueue *outputs[MSVIDEOSOURCE_MAX_OUTPUTS];
+ gchar *dev_name;
+ gint width, height;
+ gchar *format;
+ gint frame_rate;
+ gint frame_rate_base;
+} MSVideoSource;
+
+typedef struct _MSVideoSourceClass
+{
+ /* the MSVideoSource derivates from MSFilter, so the MSFilter class MUST be the first of the MSVideoSource class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+ gint (*set_device)(MSVideoSource *s, const gchar *name);
+ void (*start)(MSVideoSource *s);
+ void (*stop)(MSVideoSource *s);
+ void (*set_size)(MSVideoSource *s, gint width, gint height);
+ void (*set_frame_rate)(MSVideoSource *s, gint frame_rate, gint frame_rate_base);
+} MSVideoSourceClass;
+
+/* PUBLIC */
+void ms_video_source_register_all();
+int ms_video_source_set_device(MSVideoSource *f, const gchar *device);
+gchar* ms_video_source_get_device_name(MSVideoSource *f);
+void ms_video_source_start(MSVideoSource *f);
+void ms_video_source_stop(MSVideoSource *f);
+void ms_video_source_set_size(MSVideoSource *f, gint width, gint height);
+void ms_video_source_set_frame_rate(MSVideoSource *f, gint frame_rate, gint frame_rate_base);
+gchar* ms_video_source_get_format(MSVideoSource *f);
+
+#define MS_VIDEO_SOURCE(obj) ((MSVideoSource*)(obj))
+#define MS_VIDEO_SOURCE_CLASS(klass) ((MSVideoSourceClass*)(klass))
+
+
+/* FOR INTERNAL USE*/
+void ms_video_source_init(MSVideoSource *f);
+void ms_video_source_class_init(MSVideoSourceClass *klass);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c
new file mode 100644
index 00000000..178e294c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.c
@@ -0,0 +1,121 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "mswrite.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+
+static MSWriteClass *ms_write_class=NULL;
+
+MSFilter * ms_write_new(char *name)
+{
+ MSWrite *r;
+ int fd=-1;
+
+ r=g_new(MSWrite,1);
+ ms_write_init(r);
+ if (ms_write_class==NULL)
+ {
+ ms_write_class=g_new(MSWriteClass,1);
+ ms_write_class_init(ms_write_class);
+ }
+ MS_FILTER(r)->klass=MS_FILTER_CLASS(ms_write_class);
+ if ((name!=NULL) && (strlen(name)!=0))
+ {
+ fd=open(name,O_WRONLY | O_CREAT | O_TRUNC,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (fd<0) g_error("ms_write_new: failed to open %s.\n",name);
+ }
+ r->fd=fd;
+ return(MS_FILTER(r));
+}
+
+
+/* FOR INTERNAL USE*/
+void ms_write_init(MSWrite *r)
+{
+ ms_filter_init(MS_FILTER(r));
+ MS_FILTER(r)->infifos=r->f_inputs;
+ MS_FILTER(r)->inqueues=r->q_inputs;
+ MS_FILTER(r)->r_mingran=MSWRITE_MIN_GRAN;
+ memset(r->f_inputs,0,sizeof(MSFifo*)*MSWRITE_MAX_INPUTS);
+ memset(r->q_inputs,0,sizeof(MSQueue*)*MSWRITE_MAX_INPUTS);
+ r->fd=-1;
+}
+
+void ms_write_class_init(MSWriteClass *klass)
+{
+ ms_filter_class_init(MS_FILTER_CLASS(klass));
+ ms_filter_class_set_name(MS_FILTER_CLASS(klass),"dskwriter");
+ MS_FILTER_CLASS(klass)->max_finputs=MSWRITE_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->max_qinputs=MSWRITE_MAX_INPUTS;
+ MS_FILTER_CLASS(klass)->r_maxgran=MSWRITE_DEF_GRAN;
+ MS_FILTER_CLASS(klass)->destroy=(MSFilterDestroyFunc)ms_write_destroy;
+ MS_FILTER_CLASS(klass)->process=(MSFilterProcessFunc)ms_write_process;
+}
+
+void ms_write_process(MSWrite *r)
+{
+ MSFifo *f;
+ MSQueue *q;
+ MSMessage *buf=NULL;
+ int i,j,err1,err2;
+ gint gran=ms_filter_get_mingran(MS_FILTER(r));
+ void *p;
+
+ /* process output fifos*/
+ for (i=0,j=0;(i<MS_FILTER(r)->klass->max_finputs)&&(j<MS_FILTER(r)->finputs);i++)
+ {
+ f=r->f_inputs[i];
+ if (f!=NULL)
+ {
+ if ( (err1=ms_fifo_get_read_ptr(f,gran,&p))>0 )
+ {
+
+ err2=write(r->fd,p,gran);
+ if (err2<0) g_warning("ms_write_process: failed to write: %s.\n",strerror(errno));
+ }
+ j++;
+ }
+ }
+ /* process output queues*/
+ for (i=0,j=0;(i<MS_FILTER(r)->klass->max_qinputs)&&(j<MS_FILTER(r)->qinputs);i++)
+ {
+ q=r->q_inputs[i];
+ if (q!=NULL)
+ {
+ while ( (buf=ms_queue_get(q))!=NULL ){
+ write(r->fd,buf->data,buf->size);
+ j++;
+ ms_message_destroy(buf);
+ }
+ }
+ }
+}
+
+void ms_write_destroy( MSWrite *obj)
+{
+ if (obj->fd!=0) close(obj->fd);
+ g_free(obj);
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h
new file mode 100644
index 00000000..cd766d10
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/mswrite.h
@@ -0,0 +1,63 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 MSWRITE_H
+#define MSWRITE_H
+
+#include "msfilter.h"
+
+
+/*this is the class that implements writing reading sink filter*/
+
+#define MSWRITE_MAX_INPUTS 1 /* max output per filter*/
+
+#define MSWRITE_DEF_GRAN 512 /* the default granularity*/
+#define MSWRITE_MIN_GRAN 64
+
+typedef struct _MSWrite
+{
+ /* the MSWrite derivates from MSFilter, so the MSFilter object MUST be the first of the MSWrite object
+ in order to the object mechanism to work*/
+ MSFilter filter;
+ MSFifo *f_inputs[MSWRITE_MAX_INPUTS];
+ MSQueue *q_inputs[MSWRITE_MAX_INPUTS];
+ gint fd; /* the file descriptor of the file being written*/
+} MSWrite;
+
+typedef struct _MSWriteClass
+{
+ /* the MSWrite derivates from MSFilter, so the MSFilter class MUST be the first of the MSWrite class
+ in order to the class mechanism to work*/
+ MSFilterClass parent_class;
+} MSWriteClass;
+
+/* PUBLIC */
+#define MS_WRITE(filter) ((MSWrite*)(filter))
+#define MS_WRITE_CLASS(klass) ((MSWriteClass*)(klass))
+MSFilter * ms_write_new(char *name);
+
+/* FOR INTERNAL USE*/
+void ms_write_init(MSWrite *r);
+void ms_write_class_init(MSWriteClass *klass);
+void ms_write_destroy( MSWrite *obj);
+void ms_write_process(MSWrite *r);
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c
new file mode 100644
index 00000000..636c5792
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.c
@@ -0,0 +1,495 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "osscard.h"
+
+#include "msossread.h"
+#include "msosswrite.h"
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <sys/soundcard.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#if 0
+void * oss_thread(OssCard *obj)
+{
+ gint i;
+ gint err;
+ g_message("oss_thread: starting **********");
+ while(1){
+ for(i=0;i<OSS_CARD_BUFFERS;i++){
+ g_mutex_lock(obj->lock);
+ if (obj->ref==0){
+ g_cond_signal(obj->cond);
+ g_mutex_unlock(obj->lock);
+ g_thread_exit(NULL);
+ }
+ g_mutex_unlock(obj->lock);
+ obj->readindex=i;
+
+ err=read(obj->fd,obj->readbuf[i],SND_CARD(obj)->bsize);
+ if (err<0) g_warning("oss_thread: read() error:%s.",strerror(errno));
+ obj->writeindex=i;
+ write(obj->fd,obj->writebuf[i],SND_CARD(obj)->bsize);
+ memset(obj->writebuf[i],0,SND_CARD(obj)->bsize);
+ }
+ }
+}
+#endif
+int oss_open(OssCard *obj, int bits,int stereo, int rate)
+{
+ int fd;
+ int p=0,cond=0;
+ int i=0;
+ int min_size=0,blocksize=512;
+ int err;
+
+ //g_message("opening sound device");
+ fd=open(obj->dev_name,O_RDWR|O_NONBLOCK);
+ if (fd<0) return -EWOULDBLOCK;
+ /* unset nonblocking mode */
+ /* We wanted non blocking open but now put it back to normal ; thanks Xine !*/
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL)&~O_NONBLOCK);
+
+ /* reset is maybe not needed but takes time*/
+ /*ioctl(fd, SNDCTL_DSP_RESET, 0); */
+
+
+#ifdef WORDS_BIGENDIAN
+ p=AFMT_U16_BE;
+#else
+ p=AFMT_U16_LE;
+#endif
+
+ err=ioctl(fd,SNDCTL_DSP_SETFMT,&p);
+ if (err<0){
+ g_warning("oss_open: can't set sample format:%s.",strerror(errno));
+ }
+
+
+ p = bits; /* 16 bits */
+ err=ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p);
+ if (err<0){
+ g_warning("oss_open: can't set sample size to %i:%s.",bits,strerror(errno));
+ }
+
+ p = rate; /* rate in khz*/
+ err=ioctl(fd, SNDCTL_DSP_SPEED, &p);
+ if (err<0){
+ g_warning("oss_open: can't set sample rate to %i:%s.",rate,strerror(errno));
+ }
+
+ p = stereo; /* stereo or not */
+ err=ioctl(fd, SNDCTL_DSP_STEREO, &p);
+ if (err<0){
+ g_warning("oss_open: can't set mono/stereo mode:%s.",strerror(errno));
+ }
+
+ if (rate==16000) blocksize=4096; /* oss emulation is not very good at 16khz */
+ else blocksize=blocksize*(rate/8000);
+ ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
+
+ /* try to subdivide BLKSIZE to reach blocksize if necessary */
+ if (min_size>blocksize)
+ {
+ cond=1;
+ p=min_size/blocksize;
+ while(cond)
+ {
+ i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p);
+ //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno);
+ if ((i==0) || (p==1)) cond=0;
+ else p=p/2;
+ }
+ }
+ ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
+ if (min_size>blocksize)
+ {
+ g_warning("dsp block size set to %i.",min_size);
+ }else{
+ /* no need to access the card with less latency than needed*/
+ min_size=blocksize;
+ }
+
+ g_message("dsp blocksize is %i.",min_size);
+
+ /* start recording !!! Alex */
+ {
+ int fl,res;
+
+ fl=PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT;
+ res=ioctl(fd, SNDCTL_DSP_SETTRIGGER, &fl);
+ if (res<0) g_warning("OSS_TRIGGER: %s",strerror(errno));
+ }
+
+ obj->fd=fd;
+ obj->readpos=0;
+ obj->writepos=0;
+ SND_CARD(obj)->bits=bits;
+ SND_CARD(obj)->stereo=stereo;
+ SND_CARD(obj)->rate=rate;
+ SND_CARD(obj)->bsize=min_size;
+ return fd;
+}
+
+int oss_card_probe(OssCard *obj,int bits,int stereo,int rate)
+{
+
+ int fd;
+ int p=0,cond=0;
+ int i=0;
+ int min_size=0,blocksize=512;
+
+ if (obj->fd>0) return SND_CARD(obj)->bsize;
+ fd=open(obj->dev_name,O_RDWR|O_NONBLOCK);
+ if (fd<0) {
+ g_warning("oss_card_probe: can't open %s: %s.",obj->dev_name,strerror(errno));
+ return -1;
+ }
+ ioctl(fd, SNDCTL_DSP_RESET, 0);
+
+ p = bits; /* 16 bits */
+ ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &p);
+
+ p = stereo; /* number of channels */
+ ioctl(fd, SNDCTL_DSP_CHANNELS, &p);
+
+ p = rate; /* rate in khz*/
+ ioctl(fd, SNDCTL_DSP_SPEED, &p);
+
+ ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
+
+ /* try to subdivide BLKSIZE to reach blocksize if necessary */
+ if (min_size>blocksize)
+ {
+ cond=1;
+ p=min_size/blocksize;
+ while(cond)
+ {
+ i=ioctl(fd, SNDCTL_DSP_SUBDIVIDE, &p);
+ //printf("SUB_DIVIDE said error=%i,errno=%i\n",i,errno);
+ if ((i==0) || (p==1)) cond=0;
+ else p=p/2;
+ }
+ }
+ ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &min_size);
+ if (min_size>blocksize)
+ {
+ g_warning("dsp block size set to %i.",min_size);
+ }else{
+ /* no need to access the card with less latency than needed*/
+ min_size=blocksize;
+ }
+ close(fd);
+ return min_size;
+}
+
+
+int oss_card_open(OssCard *obj,int bits,int stereo,int rate)
+{
+ int fd;
+ obj->ref++;
+ if (obj->fd==0){
+ fd=oss_open(obj,bits,stereo,rate);
+ if (fd<0) {
+ obj->fd=0;
+ obj->ref--;
+ return -1;
+ }
+ }
+
+ obj->readbuf=g_malloc0(SND_CARD(obj)->bsize);
+ obj->writebuf=g_malloc0(SND_CARD(obj)->bsize);
+
+ SND_CARD(obj)->flags|=SND_CARD_FLAGS_OPENED;
+ return 0;
+}
+
+void oss_card_close(OssCard *obj)
+{
+ int i;
+ obj->ref--;
+ if (obj->ref==0) {
+ close(obj->fd);
+ obj->fd=0;
+ SND_CARD(obj)->flags&=~SND_CARD_FLAGS_OPENED;
+ g_free(obj->readbuf);
+ obj->readbuf=NULL;
+ g_free(obj->writebuf);
+ obj->writebuf=NULL;
+
+ }
+}
+
+void oss_card_destroy(OssCard *obj)
+{
+ snd_card_uninit(SND_CARD(obj));
+ g_free(obj->dev_name);
+ g_free(obj->mixdev_name);
+ if (obj->readbuf!=NULL) g_free(obj->readbuf);
+ if (obj->writebuf!=NULL) g_free(obj->writebuf);
+}
+
+gboolean oss_card_can_read(OssCard *obj)
+{
+ struct timeval tout={0,0};
+ int err;
+ fd_set fdset;
+ if (obj->readpos!=0) return TRUE;
+ FD_ZERO(&fdset);
+ FD_SET(obj->fd,&fdset);
+ err=select(obj->fd+1,&fdset,NULL,NULL,&tout);
+ if (err>0) return TRUE;
+ else return FALSE;
+}
+
+int oss_card_read(OssCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+ if (size<bsize){
+ gint canread=MIN(bsize-obj->readpos,size);
+ if (obj->readpos==0){
+ err=read(obj->fd,obj->readbuf,bsize);
+ if (err<0) {
+ g_warning("oss_card_read: read() failed:%s.",strerror(errno));
+ return -1;
+ }
+ }
+
+ memcpy(buf,&obj->readbuf[obj->readpos],canread);
+ obj->readpos+=canread;
+ if (obj->readpos>=bsize) obj->readpos=0;
+ return canread;
+ }else{
+ err=read(obj->fd,buf,size);
+ if (err<0) {
+ g_warning("oss_card_read: read-2() failed:%s.",strerror(errno));
+ }
+ return err;
+ }
+
+}
+
+int oss_card_write(OssCard *obj,char *buf,int size)
+{
+ int err;
+ gint bsize=SND_CARD(obj)->bsize;
+
+ if (size<bsize){
+ gint canwrite;
+ canwrite=MIN(bsize-obj->writepos,size);
+ memcpy(&obj->writebuf[obj->writepos],buf,canwrite);
+ obj->writepos+=canwrite;
+ if (obj->writepos>=bsize){
+ err=write(obj->fd,obj->writebuf,bsize);
+ obj->writepos=0;
+ }
+ return canwrite;
+ }else{
+ return write(obj->fd,buf,bsize);
+ }
+}
+
+void oss_card_set_level(OssCard *obj,gint way,gint a)
+{
+ int p,mix_fd;
+ int osscmd;
+ g_return_if_fail(obj->mixdev_name!=NULL);
+#ifdef HAVE_SYS_SOUNDCARD_H
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ osscmd=SOUND_MIXER_VOLUME;
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ osscmd=SOUND_MIXER_IGAIN;
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ osscmd=SOUND_MIXER_PCM;
+ break;
+ default:
+ g_warning("oss_card_set_level: unsupported command.");
+ return;
+ }
+ p=(((int)a)<<8 | (int)a);
+ mix_fd = open(obj->mixdev_name, O_WRONLY);
+ ioctl(mix_fd,MIXER_WRITE(osscmd), &p);
+ close(mix_fd);
+#endif
+}
+
+gint oss_card_get_level(OssCard *obj,gint way)
+{
+ int p=0,mix_fd;
+ int osscmd;
+ g_return_if_fail(obj->mixdev_name!=NULL);
+#ifdef HAVE_SYS_SOUNDCARD_H
+ switch(way){
+ case SND_CARD_LEVEL_GENERAL:
+ osscmd=SOUND_MIXER_VOLUME;
+ break;
+ case SND_CARD_LEVEL_INPUT:
+ osscmd=SOUND_MIXER_IGAIN;
+ break;
+ case SND_CARD_LEVEL_OUTPUT:
+ osscmd=SOUND_MIXER_PCM;
+ break;
+ default:
+ g_warning("oss_card_get_level: unsupported command.");
+ return -1;
+ }
+ mix_fd = open(obj->mixdev_name, O_RDONLY);
+ ioctl(mix_fd,MIXER_READ(SOUND_MIXER_VOLUME), &p);
+ close(mix_fd);
+#endif
+ return p>>8;
+}
+
+void oss_card_set_source(OssCard *obj,int source)
+{
+ gint p=0;
+ gint mix_fd;
+ g_return_if_fail(obj->mixdev_name!=NULL);
+#ifdef HAVE_SYS_SOUNDCARD_H
+ if (source == 'c')
+ p = 1 << SOUND_MIXER_CD;
+ if (source == 'l')
+ p = 1 << SOUND_MIXER_LINE;
+ if (source == 'm')
+ p = 1 << SOUND_MIXER_MIC;
+
+
+ mix_fd = open(obj->mixdev_name, O_WRONLY);
+ ioctl(mix_fd, SOUND_MIXER_WRITE_RECSRC, &p);
+ close(mix_fd);
+#endif
+}
+
+MSFilter *oss_card_create_read_filter(OssCard *card)
+{
+ MSFilter *f=ms_oss_read_new();
+ ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
+ return f;
+}
+
+MSFilter *oss_card_create_write_filter(OssCard *card)
+{
+ MSFilter *f=ms_oss_write_new();
+ ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
+ return f;
+}
+
+
+SndCard * oss_card_new(char *devname, char *mixdev_name)
+{
+ OssCard * obj= g_new0(OssCard,1);
+ SndCard *base= SND_CARD(obj);
+ snd_card_init(base);
+ obj->dev_name=g_strdup(devname);
+ obj->mixdev_name=g_strdup( mixdev_name);
+#ifdef HAVE_GLIB
+ base->card_name=g_strdup_printf("%s (Open Sound System)",devname);
+#else
+ base->card_name=malloc(100);
+ snprintf(base->card_name, 100, "%s (Open Sound System)",devname);
+#endif
+ base->_probe=(SndCardOpenFunc)oss_card_probe;
+ base->_open_r=(SndCardOpenFunc)oss_card_open;
+ base->_open_w=(SndCardOpenFunc)oss_card_open;
+ base->_can_read=(SndCardPollFunc)oss_card_can_read;
+ base->_read=(SndCardIOFunc)oss_card_read;
+ base->_write=(SndCardIOFunc)oss_card_write;
+ base->_close_r=(SndCardCloseFunc)oss_card_close;
+ base->_close_w=(SndCardCloseFunc)oss_card_close;
+ base->_set_rec_source=(SndCardMixerSetRecSourceFunc)oss_card_set_source;
+ base->_set_level=(SndCardMixerSetLevelFunc)oss_card_set_level;
+ base->_get_level=(SndCardMixerGetLevelFunc)oss_card_get_level;
+ base->_destroy=(SndCardDestroyFunc)oss_card_destroy;
+ base->_create_read_filter=(SndCardCreateFilterFunc)oss_card_create_read_filter;
+ base->_create_write_filter=(SndCardCreateFilterFunc)oss_card_create_write_filter;
+ return base;
+}
+
+#define DSP_NAME "/dev/dsp"
+#define MIXER_NAME "/dev/mixer"
+
+gint oss_card_manager_init(SndCardManager *manager, gint tabindex)
+{
+ gchar *devname;
+ gchar *mixername;
+ gint devindex=0;
+ gint found=0;
+
+ /* search for /dev/dsp and /dev/mixer */
+#ifdef HAVE_GLIB
+ if (g_file_test(DSP_NAME,G_FILE_TEST_EXISTS)){
+ tabindex++;
+ devindex++;
+ manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME);
+ manager->cards[0]->index=0;
+ found++;
+ g_message("Found /dev/dsp.");
+ }
+ for (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){
+ devname=g_strdup_printf("%s%i",DSP_NAME,devindex);
+ mixername=g_strdup_printf("%s%i",MIXER_NAME,devindex);
+ if (g_file_test(devname,G_FILE_TEST_EXISTS)){
+ manager->cards[tabindex]=oss_card_new(devname,mixername);
+ manager->cards[tabindex]->index=tabindex;
+ tabindex++;
+ found++;
+ }
+ g_free(devname);
+ g_free(mixername);
+ }
+#else
+ if (access(DSP_NAME,F_OK)==0){
+ tabindex++;
+ devindex++;
+ manager->cards[0]=oss_card_new(DSP_NAME,MIXER_NAME);
+ manager->cards[0]->index=0;
+ found++;
+ g_message("Found /dev/dsp.");
+ }
+ for (;tabindex<MAX_SND_CARDS && devindex<MAX_SND_CARDS ;devindex++){
+ devname=malloc(100);
+ snprintf(devname, 100, "%s%i",DSP_NAME,devindex);
+ mixername=malloc(100);
+ snprintf(mixername, 100, "%s%i",MIXER_NAME,devindex);
+
+ if (access(devname,F_OK)==0){
+ manager->cards[tabindex]=oss_card_new(devname,mixername);
+ manager->cards[tabindex]->index=tabindex;
+ tabindex++;
+ found++;
+ }
+ g_free(devname);
+ g_free(mixername);
+ }
+#endif
+ if (tabindex==0) g_warning("No sound cards found !");
+ return found;
+}
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h
new file mode 100644
index 00000000..30b96c23
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/osscard.h
@@ -0,0 +1,47 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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
+*/
+/* An implementation of SndCard : the OssCard */
+
+#ifndef OSS_CARD_H
+#define OSS_CARD_H
+
+#include "sndcard.h"
+
+#define OSS_CARD_BUFFERS 3
+struct _OssCard
+{
+ SndCard parent;
+ gchar *dev_name; /* /dev/dsp0 for example */
+ gchar *mixdev_name; /* /dev/mixer0 for example */
+ gint fd; /* the file descriptor of the open soundcard, 0 if not open*/
+ gint ref;
+ gchar *readbuf;
+ gint readpos;
+ gchar *writebuf;
+ gint writepos;
+};
+
+typedef struct _OssCard OssCard;
+
+SndCard * oss_card_new(char *devname, char *mixdev_name);
+
+typedef OssCard HpuxSndCard;
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c
new file mode 100644
index 00000000..9570b905
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.c
@@ -0,0 +1,315 @@
+/*
+ Copyright (C) 2005 Remko Troncon
+
+ 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 <stdio.h>
+#include <portaudio.h>
+#include <stdlib.h>
+
+#include "portaudiocard.h"
+#include "msossread.h"
+#include "msosswrite.h"
+
+// Settings
+#define BUFFER_SIZE 2048
+
+// PortAudio settings
+#define FRAMES_PER_BUFFER 256
+
+
+// -----------------------------------------------------------------------------
+
+int readBuffer(char* buffer, char** buffer_read_p, char*buffer_write, char* buffer_end, char* target_buffer, int target_len)
+{
+ char *end, *tmp, *buffer_read = *buffer_read_p;
+ size_t remaining, len;
+ int read = 0;
+
+ // First phase
+ tmp = buffer_read + target_len;
+ if (buffer_write < buffer_read) {
+ if (tmp > buffer_end) {
+ end = buffer_end;
+ remaining = tmp - buffer_end;
+ }
+ else {
+ end = tmp;
+ remaining = 0;
+ }
+ }
+ else {
+ end = (tmp >= buffer_write ? buffer_write : tmp);
+ remaining = 0;
+ }
+ //printf("end: %p\n",end);
+
+ // Copy the data
+ len = end - buffer_read;
+ memcpy(target_buffer, buffer_read, len);
+ buffer_read += len;
+ target_buffer += len;
+ read += len;
+
+ // Second phase
+ if (remaining > 0) {
+ buffer_read = buffer;
+ tmp = buffer_read + remaining;
+ len = (tmp > buffer_write ? buffer_write : tmp) - buffer_read;
+ memcpy(target_buffer, buffer_read, len);
+ buffer_read += len;
+ read += len;
+ }
+
+ // Finish up
+ *buffer_read_p = buffer_read;
+
+ return read;
+}
+
+int writeBuffer(char* buffer, char* buffer_read, char** buffer_write_p, char* buffer_end, char* source_buffer, int source_len)
+{
+ char *end, *tmp, *buffer_write = *buffer_write_p;
+ size_t remaining, len;
+ int written = 0;
+
+ // First phase
+ tmp = buffer_write + source_len;
+ if (buffer_write >= buffer_read) {
+ if (tmp > buffer_end) {
+ end = buffer_end;
+ remaining = tmp - buffer_end;
+ }
+ else {
+ end = tmp;
+ remaining = 0;
+ }
+ }
+ else {
+ if (tmp > buffer_read) {
+ printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read);
+ end = buffer_read;
+ remaining = 0;
+ }
+ else {
+ end = tmp;
+ remaining = 0;
+ }
+ }
+
+ len = end - buffer_write;
+ memcpy(buffer_write, source_buffer, len);
+ buffer_write += len;
+ source_buffer += len;
+ written += len;
+
+ // Second phase
+ if (remaining > 0) {
+ buffer_write = buffer;
+ tmp = buffer_write + remaining;
+ if (tmp > buffer_read) {
+ printf("Warning: Dropping frame(s) %p %p\n", tmp, buffer_read);
+ end = buffer_read;
+ }
+ else {
+ end = tmp;
+ }
+
+ len = end - buffer_write;
+ memcpy(buffer_write, source_buffer, len);
+ buffer_write += len;
+ written += len;
+ }
+
+ // Finish up
+ *buffer_write_p = buffer_write;
+ return written;
+}
+
+// -----------------------------------------------------------------------------
+
+static int portAudioCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *card_p )
+{
+ PortAudioCard* card = (PortAudioCard*) card_p;
+
+ size_t len = framesPerBuffer * Pa_GetSampleSize(paInt16);
+ //printf("PA::readBuffer begin %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len);
+ readBuffer(card->out_buffer,&card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, outputBuffer, len);
+ //printf("PA::readBuffer end %p %p %p %p %d\n",card->out_buffer,card->out_buffer_read,card->out_buffer_write,card->out_buffer_end, len);
+ writeBuffer(card->in_buffer,card->in_buffer_read,&card->in_buffer_write,card->in_buffer_end, inputBuffer, len);
+ return 0;
+}
+
+// -----------------------------------------------------------------------------
+
+int portaudio_card_probe(PortAudioCard *obj, int bits, int stereo, int rate)
+{
+ return FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16);
+}
+
+
+int portaudio_card_open_r(PortAudioCard *obj,int bits,int stereo,int rate)
+{
+ fprintf(stderr,"Opening PortAudio card\n");
+
+ int err;
+ err = Pa_OpenDefaultStream(&obj->stream, 1, 1, paInt16, rate, FRAMES_PER_BUFFER, 0, portAudioCallback, obj);
+ if (err != paNoError) {
+ fprintf(stderr, "Error creating a PortAudio stream: %s\n", Pa_GetErrorText(err));
+ return -1;
+ }
+
+ err = Pa_StartStream(obj->stream);
+ if (err != paNoError) {
+ fprintf(stderr, "Error starting PortAudio stream: %s\n", Pa_GetErrorText(err));
+ Pa_CloseStream(obj->stream);
+ obj->stream = NULL;
+ return -1;
+ }
+
+ SND_CARD(obj)->bits = 16;
+ SND_CARD(obj)->stereo = 0;
+ SND_CARD(obj)->rate = rate;
+ // Should this be multiplied by Pa_GetMinNumBuffers(FRAMES_PER_BUFFER,sampleRate) ?
+ SND_CARD(obj)->bsize = FRAMES_PER_BUFFER * (SND_CARD(obj)->stereo ? 2 : 1) * Pa_GetSampleSize(paInt16);
+
+ return 0;
+
+}
+
+void portaudio_card_close_r(PortAudioCard *obj)
+{
+ fprintf(stderr, "Closing PortAudio card\n");
+ if (obj->stream) {
+ Pa_StopStream(obj->stream);
+ Pa_CloseStream(obj->stream);
+ obj->stream = NULL;
+ }
+}
+
+int portaudio_card_open_w(PortAudioCard *obj,int bits,int stereo,int rate)
+{
+}
+
+void portaudio_card_close_w(PortAudioCard *obj)
+{
+}
+
+void portaudio_card_destroy(PortAudioCard *obj)
+{
+ snd_card_uninit(SND_CARD(obj));
+ free(obj->in_buffer);
+ free(obj->out_buffer);
+}
+
+gboolean portaudio_card_can_read(PortAudioCard *obj)
+{
+ return obj->in_buffer_read != obj->in_buffer_write;
+}
+
+int portaudio_card_read(PortAudioCard *obj,char *buf,int size)
+{
+ //printf("read begin %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size);
+ return readBuffer(obj->in_buffer,&obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, buf, size);
+ //printf("read end %p %p %p %p %d\n",obj->in_buffer,obj->in_buffer_read,obj->in_buffer_write,obj->in_buffer_end, size);
+}
+
+int portaudio_card_write(PortAudioCard *obj,char *buf,int size)
+{
+ //printf("writeBuffer begin %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size);
+ return writeBuffer(obj->out_buffer,obj->out_buffer_read,&obj->out_buffer_write,obj->out_buffer_end, buf, size);
+ //printf("writeBuffer end %p %p %p %p %d\n",obj->out_buffer,obj->out_buffer_read,obj->out_buffer_write,obj->out_buffer_end, size);
+}
+
+void portaudio_card_set_level(PortAudioCard *obj,gint way,gint a)
+{
+}
+
+gint portaudio_card_get_level(PortAudioCard *obj,gint way)
+{
+ return 0;
+}
+
+void portaudio_card_set_source(PortAudioCard *obj,int source)
+{
+}
+
+MSFilter *portaudio_card_create_read_filter(PortAudioCard *card)
+{
+ MSFilter *f=ms_oss_read_new();
+ ms_oss_read_set_device(MS_OSS_READ(f),SND_CARD(card)->index);
+ return f;
+}
+
+MSFilter *portaudio_card_create_write_filter(PortAudioCard *card)
+{
+ MSFilter *f=ms_oss_write_new();
+ ms_oss_write_set_device(MS_OSS_WRITE(f),SND_CARD(card)->index);
+ return f;
+}
+
+
+SndCard* portaudio_card_new()
+{
+ // Basic stuff
+ PortAudioCard* obj= g_new0(PortAudioCard,1);
+ SndCard* base= SND_CARD(obj);
+ snd_card_init(base);
+ base->card_name=g_strdup_printf("PortAudio Card");
+ base->_probe=(SndCardOpenFunc)portaudio_card_probe;
+ base->_open_r=(SndCardOpenFunc)portaudio_card_open_r;
+ base->_open_w=(SndCardOpenFunc)portaudio_card_open_w;
+ base->_can_read=(SndCardPollFunc)portaudio_card_can_read;
+ base->_read=(SndCardIOFunc)portaudio_card_read;
+ base->_write=(SndCardIOFunc)portaudio_card_write;
+ base->_close_r=(SndCardCloseFunc)portaudio_card_close_r;
+ base->_close_w=(SndCardCloseFunc)portaudio_card_close_w;
+ base->_set_rec_source=(SndCardMixerSetRecSourceFunc)portaudio_card_set_source;
+ base->_set_level=(SndCardMixerSetLevelFunc)portaudio_card_set_level;
+ base->_get_level=(SndCardMixerGetLevelFunc)portaudio_card_get_level;
+ base->_destroy=(SndCardDestroyFunc)portaudio_card_destroy;
+ base->_create_read_filter=(SndCardCreateFilterFunc)portaudio_card_create_read_filter;
+ base->_create_write_filter=(SndCardCreateFilterFunc)portaudio_card_create_write_filter;
+
+ // Initialize stream
+ obj->stream = NULL;
+
+ // Initialize buffers
+ obj->out_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
+ obj->out_buffer_read = obj->out_buffer_write = obj->out_buffer;
+ obj->out_buffer_end = obj->out_buffer + BUFFER_SIZE;
+ obj->in_buffer = (char*) malloc(sizeof(char)*BUFFER_SIZE);
+ obj->in_buffer_read = obj->in_buffer_write = obj->in_buffer;
+ obj->in_buffer_end = obj->in_buffer + BUFFER_SIZE;
+
+ return base;
+}
+
+gint portaudio_card_manager_init(SndCardManager *manager, gint tabindex)
+{
+ // Initialize portaudio lib
+ int err = Pa_Initialize();
+ if (err != paNoError) {
+ fprintf(stderr,"Error initializing PortAudio: %s\n",Pa_GetErrorText(err));
+ return 0;
+ }
+
+ // Create new card
+ manager->cards[0]=portaudio_card_new();
+ manager->cards[0]->index=0;
+
+ return 1;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h
new file mode 100644
index 00000000..cbaa7982
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/portaudiocard.h
@@ -0,0 +1,35 @@
+/*
+ Copyright (C) 2005 Remko Troncon
+
+ 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
+*/
+/* An implementation of SndCard : the OssCard */
+
+#ifndef PORTAUDIO_CARD_H
+#define PORTAUDIO_CARD_H
+
+#include "sndcard.h"
+
+typedef struct _PortAudioCard
+{
+ SndCard parent;
+ PortAudioStream* stream;
+ char *out_buffer, *out_buffer_read, *out_buffer_write, *out_buffer_end;
+ char *in_buffer, *in_buffer_read, *in_buffer_write, *in_buffer_end;
+} PortAudioCard;
+
+gint portaudio_card_manager_init(SndCardManager *manager, gint tabindex);
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c
new file mode 100644
index 00000000..3a0f5d9a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.c
@@ -0,0 +1,209 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 "sndcard.h"
+#include "msfilter.h"
+
+void snd_card_init(SndCard *obj)
+{
+ memset(obj,0,sizeof(SndCard));
+}
+
+void snd_card_uninit(SndCard *obj)
+{
+ if (obj->card_name!=NULL) g_free(obj->card_name);
+}
+
+const gchar *snd_card_get_identifier(SndCard *obj)
+{
+ return obj->card_name;
+}
+
+int snd_card_open_r(SndCard *obj, int bits, int stereo, int rate)
+{
+ g_return_val_if_fail(obj->_open_r!=NULL,-1);
+ g_message("Opening sound card [%s] in capture mode with stereo=%i,rate=%i,bits=%i",obj->card_name,stereo,rate,bits);
+ return obj->_open_r(obj,bits,stereo,rate);
+}
+int snd_card_open_w(SndCard *obj, int bits, int stereo, int rate)
+{
+ g_return_val_if_fail(obj->_open_w!=NULL,-1);
+ g_message("Opening sound card [%s] in playback mode with stereo=%i,rate=%i,bits=%i",obj->card_name,stereo,rate,bits);
+ return obj->_open_w(obj,bits,stereo,rate);
+}
+
+gboolean snd_card_can_read(SndCard *obj){
+ g_return_val_if_fail(obj->_can_read!=NULL,-1);
+ return obj->_can_read(obj);
+}
+
+void snd_card_set_blocking_mode(SndCard *obj,gboolean yesno){
+ g_return_if_fail(obj->_set_blocking_mode!=NULL);
+ obj->_set_blocking_mode(obj,yesno);
+}
+
+int snd_card_read(SndCard *obj,char *buffer,int size)
+{
+ g_return_val_if_fail(obj->_read!=NULL,-1);
+ return obj->_read(obj,buffer,size);
+}
+int snd_card_write(SndCard *obj,char *buffer,int size)
+{
+ g_return_val_if_fail(obj->_write!=NULL,-1);
+ return obj->_write(obj,buffer,size);
+}
+
+int snd_card_get_bsize(SndCard *obj)
+{
+ if (obj->flags & SND_CARD_FLAGS_OPENED){
+ return obj->bsize;
+ }
+ return -1;
+}
+
+void snd_card_close_r(SndCard *obj)
+{
+ g_return_if_fail(obj->_close_r!=NULL);
+ g_message("Closing reading channel of soundcard.");
+ obj->_close_r(obj);
+}
+
+void snd_card_close_w(SndCard *obj)
+{
+ g_return_if_fail(obj->_close_w!=NULL);
+ g_message("Closing writing channel of soundcard.");
+ obj->_close_w(obj);
+}
+
+gint snd_card_probe(SndCard *obj,int bits, int stereo, int rate)
+{
+ g_return_val_if_fail(obj->_probe!=NULL,-1);
+ return obj->_probe(obj,bits,stereo,rate);
+}
+
+void snd_card_set_rec_source(SndCard *obj, int source)
+{
+ g_return_if_fail(obj->_set_rec_source!=NULL);
+ obj->_set_rec_source(obj,source);
+}
+
+void snd_card_set_level(SndCard *obj, int way, int level)
+{
+ g_return_if_fail(obj->_set_level!=NULL);
+ obj->_set_level(obj,way,level);
+}
+
+gint snd_card_get_level(SndCard *obj,int way)
+{
+ g_return_val_if_fail(obj->_get_level!=NULL,-1);
+ return obj->_get_level(obj,way);
+}
+
+
+MSFilter * snd_card_create_read_filter(SndCard *obj)
+{
+ g_return_val_if_fail(obj->_create_read_filter!=NULL,NULL);
+ return obj->_create_read_filter(obj);
+}
+MSFilter * snd_card_create_write_filter(SndCard *obj)
+{
+ g_return_val_if_fail(obj->_create_write_filter!=NULL,NULL);
+ return obj->_create_write_filter(obj);
+}
+
+
+#ifdef HAVE_SYS_AUDIO_H
+gint sys_audio_manager_init(SndCardManager *manager, gint index)
+{
+ /* this is a quick shortcut, as multiple soundcards on HPUX does not happen
+ very often... */
+ manager->cards[index]=hpux_snd_card_new("/dev/audio","/dev/audio");
+ return 1;
+}
+
+#endif
+
+#include "osscard.h"
+#include "alsacard.h"
+#include "jackcard.h"
+
+void snd_card_manager_init(SndCardManager *manager)
+{
+ gint index=0;
+ gint tmp=0;
+ memset(manager,0,sizeof(SndCardManager));
+ #ifdef HAVE_SYS_SOUNDCARD_H
+ tmp=oss_card_manager_init(manager,index);
+ index+=tmp;
+ if (index>=MAX_SND_CARDS) return;
+ #endif
+ #ifdef __ALSA_ENABLED__
+ tmp=alsa_card_manager_init(manager,index);
+ index+=tmp;
+ if (index>=MAX_SND_CARDS) return;
+ #endif
+ #ifdef __JACK_ENABLED__
+ tmp=jack_card_manager_init(manager,index);
+ index+=tmp;
+ if (index>=MAX_SND_CARDS) return;
+ #endif
+ #ifdef HAVE_PORTAUDIO
+ tmp=portaudio_card_manager_init(manager,index);
+ index+=tmp;
+ if (index>=MAX_SND_CARDS) return;
+ #endif
+ #ifdef HAVE_SYS_AUDIO_H
+ tmp=sys_audio_manager_init(manager,index);
+ index+=tmp;
+ #endif
+}
+
+
+
+
+
+SndCard * snd_card_manager_get_card(SndCardManager *manager,int index)
+{
+ g_return_val_if_fail(index>=0,NULL);
+ g_return_val_if_fail(index<MAX_SND_CARDS,NULL);
+ if (index>MAX_SND_CARDS) return NULL;
+ return manager->cards[index];
+}
+
+SndCard * snd_card_manager_get_card_with_string(SndCardManager *manager,const char *cardname,int *index)
+{
+ int i;
+ for (i=0;i<MAX_SND_CARDS;i++){
+ gchar *card_name;
+ if (manager->cards[i]==NULL) continue;
+ card_name=manager->cards[i]->card_name;
+ if (card_name==NULL) continue;
+ if (strcmp(card_name,cardname)==0){
+ *index=i;
+ return manager->cards[i];
+ }
+ }
+ g_warning("No card %s found.",cardname);
+ return NULL;
+}
+
+SndCardManager _snd_card_manager;
+SndCardManager *snd_card_manager=&_snd_card_manager;
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h
new file mode 100644
index 00000000..d84757fd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/sndcard.h
@@ -0,0 +1,143 @@
+/*
+ The mediastreamer library aims at providing modular media processing and I/O
+ for linphone, but also for any telephony application.
+ Copyright (C) 2001 Simon MORLAT simon.morlat@linphone.org
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.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 SNDCARD_H
+#define SNDCARD_H
+
+#undef PACKAGE
+#undef VERSION
+#include <config.h>
+#undef PACKAGE
+#undef VERSION
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#else
+#include <uglib.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* the base class for all soundcards: SndCard */
+struct _SndCard;
+
+typedef int (*SndCardOpenFunc)(struct _SndCard*,int, int, int);
+typedef void (*SndCardSetBlockingModeFunc)(struct _SndCard*, gboolean );
+typedef void (*SndCardCloseFunc)(struct _SndCard*);
+typedef gint (*SndCardIOFunc)(struct _SndCard*,char *,int);
+typedef void (*SndCardDestroyFunc)(struct _SndCard*);
+typedef gboolean (*SndCardPollFunc)(struct _SndCard*);
+typedef gint (*SndCardMixerGetLevelFunc)(struct _SndCard*,gint);
+typedef void (*SndCardMixerSetRecSourceFunc)(struct _SndCard*,gint);
+typedef void (*SndCardMixerSetLevelFunc)(struct _SndCard*,gint ,gint);
+typedef struct _MSFilter * (*SndCardCreateFilterFunc)(struct _SndCard *);
+
+struct _SndCard
+{
+ gchar *card_name; /* SB16 PCI for example */
+ gint index;
+ gint bsize;
+ gint rate;
+ gint stereo;
+ gint bits;
+ gint flags;
+#define SND_CARD_FLAGS_OPENED 1
+ SndCardOpenFunc _probe;
+ SndCardOpenFunc _open_r;
+ SndCardOpenFunc _open_w;
+ SndCardSetBlockingModeFunc _set_blocking_mode;
+ SndCardPollFunc _can_read;
+ SndCardIOFunc _read;
+ SndCardIOFunc _write;
+ SndCardCloseFunc _close_r;
+ SndCardCloseFunc _close_w;
+ SndCardMixerGetLevelFunc _get_level;
+ SndCardMixerSetLevelFunc _set_level;
+ SndCardMixerSetRecSourceFunc _set_rec_source;
+ SndCardCreateFilterFunc _create_read_filter;
+ SndCardCreateFilterFunc _create_write_filter;
+ SndCardDestroyFunc _destroy;
+};
+
+
+typedef struct _SndCard SndCard;
+
+void snd_card_init(SndCard *obj);
+void snd_card_uninit(SndCard *obj);
+gint snd_card_probe(SndCard *obj, int bits, int stereo, int rate);
+int snd_card_open_r(SndCard *obj, int bits, int stereo, int rate);
+int snd_card_open_w(SndCard *obj, int bits, int stereo, int rate);
+int snd_card_get_bsize(SndCard *obj);
+gboolean snd_card_can_read(SndCard *obj);
+int snd_card_read(SndCard *obj,char *buffer,int size);
+int snd_card_write(SndCard *obj,char *buffer,int size);
+void snd_card_set_blocking_mode(SndCard *obj,gboolean yesno);
+void snd_card_close_r(SndCard *obj);
+void snd_card_close_w(SndCard *obj);
+
+void snd_card_set_rec_source(SndCard *obj, int source); /* source='l' or 'm'*/
+void snd_card_set_level(SndCard *obj, int way, int level);
+gint snd_card_get_level(SndCard *obj,int way);
+
+const gchar *snd_card_get_identifier(SndCard *obj);
+
+struct _MSFilter * snd_card_create_read_filter(SndCard *sndcard);
+struct _MSFilter * snd_card_create_write_filter(SndCard *sndcard);
+
+
+#define SND_CARD_LEVEL_GENERAL 1
+#define SND_CARD_LEVEL_INPUT 2
+#define SND_CARD_LEVEL_OUTPUT 3
+
+
+int snd_card_destroy(SndCard *obj);
+
+#define SND_CARD(obj) ((SndCard*)(obj))
+
+
+
+
+/* SndCardManager */
+
+#define MAX_SND_CARDS 20
+
+
+struct _SndCardManager
+{
+ SndCard *cards[MAX_SND_CARDS];
+};
+
+typedef struct _SndCardManager SndCardManager;
+
+void snd_card_manager_init(SndCardManager *manager);
+SndCard * snd_card_manager_get_card(SndCardManager *manager,int index);
+SndCard * snd_card_manager_get_card_with_string(SndCardManager *manager,const char *cardname,int *index);
+
+extern SndCardManager *snd_card_manager;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h
new file mode 100644
index 00000000..6768d8f8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/third_party/mediastreamer/waveheader.h
@@ -0,0 +1,111 @@
+/*
+linphone
+Copyright (C) 2000 Simon MORLAT (simon.morlat@free.fr)
+
+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.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+/* the following code was taken from a free software utility that I don't remember the name. */
+/* sorry */
+
+
+
+#include <ms.h>
+#ifndef waveheader_h
+#define waveheader_h
+
+typedef struct uint16scheme
+{
+ unsigned char lo_byte;
+ unsigned char hi_byte;
+} uint16scheme_t;
+
+typedef struct uint32scheme
+{
+ guint16 lo_int;
+ guint16 hi_int;
+} uint32scheme_t;
+
+
+/* all integer in wav header must be read in least endian order */
+inline guint16 _readuint16(guint16 a)
+{
+ guint16 res;
+ uint16scheme_t *tmp1=(uint16scheme_t*)&a;
+
+ ((uint16scheme_t *)(&res))->lo_byte=tmp1->hi_byte;
+ ((uint16scheme_t *)(&res))->hi_byte=tmp1->lo_byte;
+ return res;
+}
+
+inline guint32 _readuint32(guint32 a)
+{
+ guint32 res;
+ uint32scheme_t *tmp1=(uint32scheme_t*)&a;
+
+ ((uint32scheme_t *)(&res))->lo_int=_readuint16(tmp1->hi_int);
+ ((uint32scheme_t *)(&res))->hi_int=_readuint16(tmp1->lo_int);
+ return res;
+}
+
+#ifdef WORDS_BIGENDIAN
+#define le_uint32(a) (_readuint32((a)))
+#define le_uint16(a) (_readuint16((a)))
+#define le_int16(a) ( (gint16) _readuint16((guint16)((a))) )
+#else
+#define le_uint32(a) (a)
+#define le_uint16(a) (a)
+#define le_int16(a) (a)
+#endif
+
+typedef struct _riff_t {
+ char riff[4] ; /* "RIFF" (ASCII characters) */
+ guint32 len ; /* Length of package (binary, little endian) */
+ char wave[4] ; /* "WAVE" (ASCII characters) */
+} riff_t;
+
+/* The FORMAT chunk */
+
+typedef struct _format_t {
+ char fmt[4] ; /* "fmt_" (ASCII characters) */
+ guint32 len ; /* length of FORMAT chunk (always 0x10) */
+ guint16 que ; /* Always 0x01 */
+ guint16 channel ; /* Channel numbers (0x01 = mono, 0x02 = stereo) */
+ guint32 rate ; /* Sample rate (binary, in Hz) */
+ guint32 bps ; /* Bytes Per Second */
+ guint16 bpsmpl ; /* bytes per sample: 1 = 8 bit Mono,
+ 2 = 8 bit Stereo/16 bit Mono,
+ 4 = 16 bit Stereo */
+ guint16 bitpspl ; /* bits per sample */
+} format_t;
+
+/* The DATA chunk */
+
+typedef struct _data_t {
+ char data[4] ; /* "data" (ASCII characters) */
+ int len ; /* length of data */
+} data_t;
+
+typedef struct _wave_header_t
+{
+ riff_t riff_chunk;
+ format_t format_chunk;
+ data_t data_chunk;
+} wave_header_t;
+
+#define wave_header_get_rate(header) le_uint32((header)->format_chunk.rate)
+#define wave_header_get_channel(header) le_uint16((header)->format_chunk.channel)
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am
new file mode 100644
index 00000000..1e7abcfd
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/Makefile.am
@@ -0,0 +1,18 @@
+libcricketxmllite_la_SOURCES = qname.cc \
+ xmlbuilder.cc \
+ xmlconstants.cc \
+ xmlelement.cc \
+ xmlnsstack.cc \
+ xmlparser.cc \
+ xmlprinter.cc
+
+noinst_HEADERS = qname.h \
+ xmlbuilder.h \
+ xmlconstants.h \
+ xmlelement.h \
+ xmlnsstack.h \
+ xmlparser.h \
+ xmlprinter.h
+AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../..
+
+noinst_LTLIBRARIES = libcricketxmllite.la
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc
new file mode 100644
index 00000000..626cfa96
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.cc
@@ -0,0 +1,167 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlconstants.h"
+
+//#define new TRACK_NEW
+
+namespace buzz {
+
+static int QName_Hash(const std::string & ns, const char * local) {
+ int result = ns.size() * 101;
+ while (*local) {
+ result *= 19;
+ result += *local;
+ local += 1;
+ }
+ return result;
+}
+
+static const int bits = 9;
+static QName::Data * get_qname_table() {
+ static QName::Data qname_table[1 << bits];
+ return qname_table;
+}
+
+static QName::Data *
+AllocateOrFind(const std::string & ns, const char * local) {
+ int index = QName_Hash(ns, local);
+ int increment = index >> (bits - 1) | 1;
+ QName::Data * qname_table = get_qname_table();
+ for (;;) {
+ index &= ((1 << bits) - 1);
+ if (!qname_table[index].Occupied()) {
+ return new QName::Data(ns, local);
+ }
+ if (qname_table[index].localPart_ == local &&
+ qname_table[index].namespace_ == ns) {
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ index += increment;
+ }
+}
+
+static QName::Data *
+Add(const std::string & ns, const char * local) {
+ int index = QName_Hash(ns, local);
+ int increment = index >> (bits - 1) | 1;
+ QName::Data * qname_table = get_qname_table();
+ for (;;) {
+ index &= ((1 << bits) - 1);
+ if (!qname_table[index].Occupied()) {
+ qname_table[index].namespace_ = ns;
+ qname_table[index].localPart_ = local;
+ qname_table[index].AddRef(); // AddRef twice so it's never deleted
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ if (qname_table[index].localPart_ == local &&
+ qname_table[index].namespace_ == ns) {
+ qname_table[index].AddRef();
+ return qname_table + index;
+ }
+ index += increment;
+ }
+}
+
+QName::~QName() {
+ data_->Release();
+}
+
+QName::QName() : data_(QN_EMPTY.data_) {
+ data_->AddRef();
+}
+
+QName::QName(bool add, const std::string & ns, const char * local) :
+ data_(add ? Add(ns, local) : AllocateOrFind(ns, local)) {}
+
+QName::QName(bool add, const std::string & ns, const std::string & local) :
+ data_(add ? Add(ns, local.c_str()) : AllocateOrFind(ns, local.c_str())) {}
+
+QName::QName(const std::string & ns, const char * local) :
+ data_(AllocateOrFind(ns, local)) {}
+
+static std::string
+QName_LocalPart(const std::string & name) {
+ size_t i = name.rfind(':');
+ if (i == std::string::npos)
+ return name;
+ return name.substr(i + 1);
+}
+
+static std::string
+QName_Namespace(const std::string & name) {
+ size_t i = name.rfind(':');
+ if (i == std::string::npos)
+ return STR_EMPTY;
+ return name.substr(0, i);
+}
+
+QName::QName(const std::string & mergedOrLocal) :
+ data_(AllocateOrFind(QName_Namespace(mergedOrLocal),
+ QName_LocalPart(mergedOrLocal).c_str())) {}
+
+std::string
+QName::Merged() const {
+ if (data_->namespace_ == STR_EMPTY)
+ return data_->localPart_;
+
+ std::string result(data_->namespace_);
+ result.reserve(result.length() + 1 + data_->localPart_.length());
+ result += ':';
+ result += data_->localPart_;
+ return result;
+}
+
+bool
+QName::operator==(const QName & other) const {
+ return other.data_ == data_ ||
+ data_->localPart_ == other.data_->localPart_ &&
+ data_->namespace_ == other.data_->namespace_;
+}
+
+int
+QName::Compare(const QName & other) const {
+ if (data_ == other.data_)
+ return 0;
+
+ int result = data_->localPart_.compare(other.data_->localPart_);
+ if (result)
+ return result;
+
+ return data_->namespace_.compare(other.data_->namespace_);
+}
+
+}
+
+
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h
new file mode 100644
index 00000000..b1bcec61
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/qname.h
@@ -0,0 +1,87 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _qname_h_
+#define _qname_h_
+
+#include <string>
+
+namespace buzz {
+
+
+class QName
+{
+public:
+ explicit QName();
+ QName(const QName & qname) : data_(qname.data_) { data_->AddRef(); }
+ explicit QName(bool add, const std::string & ns, const char * local);
+ explicit QName(bool add, const std::string & ns, const std::string & local);
+ explicit QName(const std::string & ns, const char * local);
+ explicit QName(const std::string & mergedOrLocal);
+ QName & operator=(const QName & qn) {
+ qn.data_->AddRef();
+ data_->Release();
+ data_ = qn.data_;
+ return *this;
+ }
+ ~QName();
+
+ const std::string & Namespace() const { return data_->namespace_; }
+ const std::string & LocalPart() const { return data_->localPart_; }
+ std::string Merged() const;
+ int Compare(const QName & other) const;
+ bool operator==(const QName & other) const;
+ bool operator!=(const QName & other) const { return !operator==(other); }
+ bool operator<(const QName & other) const { return Compare(other) < 0; }
+
+ class Data {
+ public:
+ Data(const std::string & ns, const std::string & local) :
+ refcount_(1),
+ namespace_(ns),
+ localPart_(local) {}
+
+ Data() : refcount_(0) {}
+
+ std::string namespace_;
+ std::string localPart_;
+ void AddRef() { refcount_++; }
+ void Release() { if (!--refcount_) { delete this; } }
+ bool Occupied() { return !!refcount_; }
+
+ private:
+ int refcount_;
+ };
+
+private:
+ Data * data_;
+};
+
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc
new file mode 100644
index 00000000..313c4013
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.cc
@@ -0,0 +1,151 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <vector>
+#include <set>
+#include <expat.h>
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlbuilder.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+XmlBuilder::XmlBuilder() :
+ pelCurrent_(NULL),
+ pelRoot_(NULL),
+ pvParents_(new std::vector<XmlElement *>()) {
+}
+
+void
+XmlBuilder::Reset() {
+ pelRoot_.reset();
+ pelCurrent_ = NULL;
+ pvParents_->clear();
+}
+
+XmlElement *
+XmlBuilder::BuildElement(XmlParseContext * pctx,
+ const char * name, const char ** atts) {
+ QName tagName(pctx->ResolveQName(name, false));
+ if (tagName == QN_EMPTY)
+ return NULL;
+
+ XmlElement * pelNew = new XmlElement(tagName);
+
+ if (!*atts)
+ return pelNew;
+
+ std::set<QName> seenNonlocalAtts;
+
+ while (*atts) {
+ QName attName(pctx->ResolveQName(*atts, true));
+ if (attName == QN_EMPTY) {
+ delete pelNew;
+ return NULL;
+ }
+
+ // verify that namespaced names are unique
+ if (!attName.Namespace().empty()) {
+ if (seenNonlocalAtts.count(attName)) {
+ delete pelNew;
+ return NULL;
+ }
+ seenNonlocalAtts.insert(attName);
+ }
+
+ pelNew->AddAttr(attName, std::string(*(atts + 1)));
+ atts += 2;
+ }
+
+ return pelNew;
+}
+
+void
+XmlBuilder::StartElement(XmlParseContext * pctx,
+ const char * name, const char ** atts) {
+ XmlElement * pelNew = BuildElement(pctx, name, atts);
+ if (pelNew == NULL) {
+ pctx->RaiseError(XML_ERROR_SYNTAX);
+ return;
+ }
+
+ if (!pelCurrent_) {
+ pelCurrent_ = pelNew;
+ pelRoot_.reset(pelNew);
+ pvParents_->push_back(NULL);
+ } else {
+ pelCurrent_->AddElement(pelNew);
+ pvParents_->push_back(pelCurrent_);
+ pelCurrent_ = pelNew;
+ }
+}
+
+void
+XmlBuilder::EndElement(XmlParseContext * pctx, const char * name) {
+ UNUSED(pctx);
+ UNUSED(name);
+ pelCurrent_ = pvParents_->back();
+ pvParents_->pop_back();
+}
+
+void
+XmlBuilder::CharacterData(XmlParseContext * pctx,
+ const char * text, int len) {
+ UNUSED(pctx);
+ if (pelCurrent_) {
+ pelCurrent_->AddParsedText(text, len);
+ }
+}
+
+void
+XmlBuilder::Error(XmlParseContext * pctx, XML_Error err) {
+ UNUSED(pctx);
+ UNUSED(err);
+ pelRoot_.reset(NULL);
+ pelCurrent_ = NULL;
+ pvParents_->clear();
+}
+
+XmlElement *
+XmlBuilder::CreateElement() {
+ return pelRoot_.release();
+}
+
+XmlElement *
+XmlBuilder::BuiltElement() {
+ return pelRoot_.get();
+}
+
+XmlBuilder::~XmlBuilder() {
+}
+
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h
new file mode 100644
index 00000000..b5b1be59
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlbuilder.h
@@ -0,0 +1,79 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlbuilder_h_
+#define _xmlbuilder_h_
+
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stl_decl.h"
+#include "talk/xmllite/xmlparser.h"
+
+#ifdef OSX
+#include "talk/third_party/expat/expat.h"
+#else
+#include <expat.h>
+#endif
+
+namespace buzz {
+
+class XmlElement;
+class XmlParseContext;
+
+
+class XmlBuilder : public XmlParseHandler {
+public:
+ XmlBuilder();
+
+ static XmlElement * BuildElement(XmlParseContext * pctx,
+ const char * name, const char ** atts);
+ virtual void StartElement(XmlParseContext * pctx,
+ const char * name, const char ** atts);
+ virtual void EndElement(XmlParseContext * pctx, const char * name);
+ virtual void CharacterData(XmlParseContext * pctx,
+ const char * text, int len);
+ virtual void Error(XmlParseContext * pctx, XML_Error);
+ virtual ~XmlBuilder();
+
+ void Reset();
+
+ // Take ownership of the built element; second call returns NULL
+ XmlElement * CreateElement();
+
+ // Peek at the built element without taking ownership
+ XmlElement * BuiltElement();
+
+private:
+ XmlElement * pelCurrent_;
+ scoped_ptr<XmlElement> pelRoot_;
+ scoped_ptr<std::vector<XmlElement *, std::allocator<XmlElement *> > >
+ pvParents_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc
new file mode 100644
index 00000000..503f832f
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.cc
@@ -0,0 +1,65 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xmlconstants.h"
+
+using namespace buzz;
+
+const std::string & XmlConstants::str_empty() {
+ static const std::string str_empty_;
+ return str_empty_;
+}
+
+const std::string & XmlConstants::ns_xml() {
+ static const std::string ns_xml_("http://www.w3.org/XML/1998/namespace");
+ return ns_xml_;
+}
+
+const std::string & XmlConstants::ns_xmlns() {
+ static const std::string ns_xmlns_("http://www.w3.org/2000/xmlns/");
+ return ns_xmlns_;
+}
+
+const std::string & XmlConstants::str_xmlns() {
+ static const std::string str_xmlns_("xmlns");
+ return str_xmlns_;
+}
+
+const std::string & XmlConstants::str_xml() {
+ static const std::string str_xml_("xml");
+ return str_xml_;
+}
+
+const std::string & XmlConstants::str_version() {
+ static const std::string str_version_("version");
+ return str_version_;
+}
+
+const std::string & XmlConstants::str_encoding() {
+ static const std::string str_encoding_("encoding");
+ return str_encoding_;
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h
new file mode 100644
index 00000000..8514d6f4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlconstants.h
@@ -0,0 +1,61 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Because global constant initialization order is undefined
+// globals cannot depend on other objects to be instantiated.
+// This class creates string objects within static methods
+// such that globals may refer to these constants by the
+// accessor function and they are guaranteed to be initialized.
+
+#ifndef TALK_XMLLITE_CONSTANTS_H_
+#define TALK_XMLLITE_CONSTANTS_H_
+
+#include <string>
+
+#define STR_EMPTY XmlConstants::str_empty()
+#define NS_XML XmlConstants::ns_xml()
+#define NS_XMLNS XmlConstants::ns_xmlns()
+#define STR_XMLNS XmlConstants::str_xmlns()
+#define STR_XML XmlConstants::str_xml()
+#define STR_VERSION XmlConstants::str_version()
+#define STR_ENCODING XmlConstants::str_encoding()
+namespace buzz {
+
+class XmlConstants {
+ public:
+ static const std::string & str_empty();
+ static const std::string & ns_xml();
+ static const std::string & ns_xmlns();
+ static const std::string & str_xmlns();
+ static const std::string & str_xml();
+ static const std::string & str_version();
+ static const std::string & str_encoding();
+};
+
+}
+
+#endif // TALK_XMLLITE_CONSTANTS_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc
new file mode 100644
index 00000000..d3619a92
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.cc
@@ -0,0 +1,491 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlbuilder.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmllite/xmlconstants.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+const QName QN_EMPTY(true, STR_EMPTY, STR_EMPTY);
+const QName QN_XMLNS(true, STR_EMPTY, STR_XMLNS);
+
+
+XmlChild::~XmlChild() {
+}
+
+bool
+XmlText::IsTextImpl() const {
+ return true;
+}
+
+XmlElement *
+XmlText::AsElementImpl() const {
+ return NULL;
+}
+
+XmlText *
+XmlText::AsTextImpl() const {
+ return const_cast<XmlText *>(this);
+}
+
+void
+XmlText::SetText(const std::string & text) {
+ text_ = text;
+}
+
+void
+XmlText::AddParsedText(const char * buf, int len) {
+ text_.append(buf, len);
+}
+
+void
+XmlText::AddText(const std::string & text) {
+ text_ += text;
+}
+
+XmlText::~XmlText() {
+}
+
+XmlElement::XmlElement(const QName & name) :
+ name_(name),
+ pFirstAttr_(NULL),
+ pLastAttr_(NULL),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+}
+
+XmlElement::XmlElement(const XmlElement & elt) :
+ XmlChild(),
+ name_(elt.name_),
+ pFirstAttr_(NULL),
+ pLastAttr_(NULL),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+
+ // copy attributes
+ XmlAttr * pAttr;
+ XmlAttr ** ppLastAttr = &pFirstAttr_;
+ XmlAttr * newAttr = NULL;
+ for (pAttr = elt.pFirstAttr_; pAttr; pAttr = pAttr->NextAttr()) {
+ newAttr = new XmlAttr(*pAttr);
+ *ppLastAttr = newAttr;
+ ppLastAttr = &(newAttr->pNextAttr_);
+ }
+ pLastAttr_ = newAttr;
+
+ // copy children
+ XmlChild * pChild;
+ XmlChild ** ppLast = &pFirstChild_;
+ XmlChild * newChild = NULL;
+
+ for (pChild = elt.pFirstChild_; pChild; pChild = pChild->NextChild()) {
+ if (pChild->IsText()) {
+ newChild = new XmlText(*(pChild->AsText()));
+ } else {
+ newChild = new XmlElement(*(pChild->AsElement()));
+ }
+ *ppLast = newChild;
+ ppLast = &(newChild->pNextChild_);
+ }
+ pLastChild_ = newChild;
+
+}
+
+XmlElement::XmlElement(const QName & name, bool useDefaultNs) :
+ name_(name),
+ pFirstAttr_(useDefaultNs ? new XmlAttr(QN_XMLNS, name.Namespace()) : NULL),
+ pLastAttr_(pFirstAttr_),
+ pFirstChild_(NULL),
+ pLastChild_(NULL) {
+}
+
+bool
+XmlElement::IsTextImpl() const {
+ return false;
+}
+
+XmlElement *
+XmlElement::AsElementImpl() const {
+ return const_cast<XmlElement *>(this);
+}
+
+XmlText *
+XmlElement::AsTextImpl() const {
+ return NULL;
+}
+
+const std::string &
+XmlElement::BodyText() const {
+ if (pFirstChild_ && pFirstChild_->IsText() && pLastChild_ == pFirstChild_) {
+ return pFirstChild_->AsText()->Text();
+ }
+
+ return STR_EMPTY;
+}
+
+void
+XmlElement::SetBodyText(const std::string & text) {
+ if (text == STR_EMPTY) {
+ ClearChildren();
+ } else if (pFirstChild_ == NULL) {
+ AddText(text);
+ } else if (pFirstChild_->IsText() && pLastChild_ == pFirstChild_) {
+ pFirstChild_->AsText()->SetText(text);
+ } else {
+ ClearChildren();
+ AddText(text);
+ }
+}
+
+const QName &
+XmlElement::FirstElementName() const {
+ const XmlElement * element = FirstElement();
+ if (element == NULL)
+ return QN_EMPTY;
+ return element->Name();
+}
+
+XmlAttr *
+XmlElement::FirstAttr() {
+ return pFirstAttr_;
+}
+
+const std::string &
+XmlElement::Attr(const QName & name) const {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ return pattr->value_;
+ }
+ return STR_EMPTY;
+}
+
+bool
+XmlElement::HasAttr(const QName & name) const {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ return true;
+ }
+ return false;
+}
+
+void
+XmlElement::SetAttr(const QName & name, const std::string & value) {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ break;
+ }
+ if (!pattr) {
+ pattr = new XmlAttr(name, value);
+ if (pLastAttr_)
+ pLastAttr_->pNextAttr_ = pattr;
+ else
+ pFirstAttr_ = pattr;
+ pLastAttr_ = pattr;
+ return;
+ }
+ pattr->value_ = value;
+}
+
+void
+XmlElement::ClearAttr(const QName & name) {
+ XmlAttr * pattr;
+ XmlAttr *pLastAttr = NULL;
+ for (pattr = pFirstAttr_; pattr; pattr = pattr->pNextAttr_) {
+ if (pattr->name_ == name)
+ break;
+ pLastAttr = pattr;
+ }
+ if (!pattr)
+ return;
+ if (!pLastAttr)
+ pFirstAttr_ = pattr->pNextAttr_;
+ else
+ pLastAttr->pNextAttr_ = pattr->pNextAttr_;
+ if (pLastAttr_ == pattr)
+ pLastAttr_ = pLastAttr;
+ delete pattr;
+}
+
+XmlChild *
+XmlElement::FirstChild() {
+ return pFirstChild_;
+}
+
+XmlElement *
+XmlElement::FirstElement() {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText())
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextElement() {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText())
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::FirstWithNamespace(const std::string & ns) {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextWithNamespace(const std::string & ns) {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name().Namespace() == ns)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::FirstNamed(const QName & name) {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+XmlElement *
+XmlElement::NextNamed(const QName & name) {
+ XmlChild * pChild;
+ for (pChild = pNextChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement();
+ }
+ return NULL;
+}
+
+const std::string &
+XmlElement::TextNamed(const QName & name) const {
+ XmlChild * pChild;
+ for (pChild = pFirstChild_; pChild; pChild = pChild->pNextChild_) {
+ if (!pChild->IsText() && pChild->AsElement()->Name() == name)
+ return pChild->AsElement()->BodyText();
+ }
+ return STR_EMPTY;
+}
+
+void
+XmlElement::InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNext) {
+ if (pPredecessor == NULL) {
+ pNext->pNextChild_ = pFirstChild_;
+ pFirstChild_ = pNext;
+ }
+ else {
+ pNext->pNextChild_ = pPredecessor->pNextChild_;
+ pPredecessor->pNextChild_ = pNext;
+ }
+}
+
+void
+XmlElement::RemoveChildAfter(XmlChild * pPredecessor) {
+ XmlChild * pNext;
+
+ if (pPredecessor == NULL) {
+ pNext = pFirstChild_;
+ pFirstChild_ = pNext->pNextChild_;
+ }
+ else {
+ pNext = pPredecessor->pNextChild_;
+ pPredecessor->pNextChild_ = pNext->pNextChild_;
+ }
+
+ if (pLastChild_ == pNext)
+ pLastChild_ = pPredecessor;
+
+ delete pNext;
+}
+
+void
+XmlElement::AddAttr(const QName & name, const std::string & value) {
+ ASSERT(!HasAttr(name));
+
+ XmlAttr ** pprev = pLastAttr_ ? &(pLastAttr_->pNextAttr_) : &pFirstAttr_;
+ pLastAttr_ = (*pprev = new XmlAttr(name, value));
+}
+
+void
+XmlElement::AddAttr(const QName & name, const std::string & value,
+ int depth) {
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddAttr(name, value);
+}
+
+void
+XmlElement::AddParsedText(const char * cstr, int len) {
+ if (len == 0)
+ return;
+
+ if (pLastChild_ && pLastChild_->IsText()) {
+ pLastChild_->AsText()->AddParsedText(cstr, len);
+ return;
+ }
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = new XmlText(cstr, len);
+}
+
+void
+XmlElement::AddText(const std::string & text) {
+ if (text == STR_EMPTY)
+ return;
+
+ if (pLastChild_ && pLastChild_->IsText()) {
+ pLastChild_->AsText()->AddText(text);
+ return;
+ }
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = new XmlText(text);
+}
+
+void
+XmlElement::AddText(const std::string & text, int depth) {
+ // note: the first syntax is ambigious for msvc 6
+ // XmlElement * pel(this);
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddText(text);
+}
+
+void
+XmlElement::AddElement(XmlElement *pelChild) {
+ if (pelChild == NULL)
+ return;
+
+ XmlChild ** pprev = pLastChild_ ? &(pLastChild_->pNextChild_) : &pFirstChild_;
+ pLastChild_ = *pprev = pelChild;
+ pelChild->pNextChild_ = NULL;
+}
+
+void
+XmlElement::AddElement(XmlElement *pelChild, int depth) {
+ XmlElement * element = this;
+ while (depth--) {
+ element = element->pLastChild_->AsElement();
+ }
+ element->AddElement(pelChild);
+}
+
+void
+XmlElement::ClearNamedChildren(const QName & name) {
+ XmlChild * prev_child = NULL;
+ XmlChild * next_child;
+ XmlChild * child;
+ for (child = FirstChild(); child; child = next_child) {
+ next_child = child->NextChild();
+ if (!child->IsText() && child->AsElement()->Name() == name)
+ {
+ RemoveChildAfter(prev_child);
+ continue;
+ }
+ prev_child = child;
+ }
+}
+
+void
+XmlElement::ClearChildren() {
+ XmlChild * pchild;
+ for (pchild = pFirstChild_; pchild; ) {
+ XmlChild * pToDelete = pchild;
+ pchild = pchild->pNextChild_;
+ delete pToDelete;
+ }
+ pFirstChild_ = pLastChild_ = NULL;
+}
+
+std::string
+XmlElement::Str() const {
+ std::stringstream ss;
+ Print(&ss, NULL, 0);
+ return ss.str();
+}
+
+XmlElement *
+XmlElement::ForStr(const std::string & str) {
+ XmlBuilder builder;
+ XmlParser::ParseXml(&builder, str);
+ return builder.CreateElement();
+}
+
+void
+XmlElement::Print(
+ std::ostream * pout, std::string xmlns[], int xmlnsCount) const {
+ XmlPrinter::PrintXml(pout, this, xmlns, xmlnsCount);
+}
+
+XmlElement::~XmlElement() {
+ XmlAttr * pattr;
+ for (pattr = pFirstAttr_; pattr; ) {
+ XmlAttr * pToDelete = pattr;
+ pattr = pattr->pNextAttr_;
+ delete pToDelete;
+ }
+
+ XmlChild * pchild;
+ for (pchild = pFirstChild_; pchild; ) {
+ XmlChild * pToDelete = pchild;
+ pchild = pchild->pNextChild_;
+ delete pToDelete;
+ }
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h
new file mode 100644
index 00000000..06545d89
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlelement.h
@@ -0,0 +1,231 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlelement_h_
+#define _xmlelement_h_
+
+#include <iosfwd>
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+extern const QName QN_EMPTY;
+extern const QName QN_XMLNS;
+
+
+class XmlChild;
+class XmlText;
+class XmlElement;
+class XmlAttr;
+
+class XmlChild {
+friend class XmlElement;
+
+public:
+
+ XmlChild * NextChild() { return pNextChild_; }
+ const XmlChild * NextChild() const { return pNextChild_; }
+
+ bool IsText() const { return IsTextImpl(); }
+
+ XmlElement * AsElement() { return AsElementImpl(); }
+ const XmlElement * AsElement() const { return AsElementImpl(); }
+
+ XmlText * AsText() { return AsTextImpl(); }
+ const XmlText * AsText() const { return AsTextImpl(); }
+
+
+protected:
+
+ XmlChild() :
+ pNextChild_(NULL) {
+ }
+
+ virtual bool IsTextImpl() const = 0;
+ virtual XmlElement * AsElementImpl() const = 0;
+ virtual XmlText * AsTextImpl() const = 0;
+
+
+ virtual ~XmlChild();
+
+private:
+ XmlChild(const XmlChild & noimpl);
+
+ XmlChild * pNextChild_;
+
+};
+
+class XmlText : public XmlChild {
+public:
+ explicit XmlText(const std::string & text) :
+ XmlChild(),
+ text_(text) {
+ }
+ explicit XmlText(const XmlText & t) :
+ XmlChild(),
+ text_(t.text_) {
+ }
+ explicit XmlText(const char * cstr, size_t len) :
+ XmlChild(),
+ text_(cstr, len) {
+ }
+ virtual ~XmlText();
+
+ const std::string & Text() const { return text_; }
+ void SetText(const std::string & text);
+ void AddParsedText(const char * buf, int len);
+ void AddText(const std::string & text);
+
+protected:
+ virtual bool IsTextImpl() const;
+ virtual XmlElement * AsElementImpl() const;
+ virtual XmlText * AsTextImpl() const;
+
+private:
+ std::string text_;
+};
+
+class XmlAttr {
+friend class XmlElement;
+
+public:
+ XmlAttr * NextAttr() const { return pNextAttr_; }
+ const QName & Name() const { return name_; }
+ const std::string & Value() const { return value_; }
+
+private:
+ explicit XmlAttr(const QName & name, const std::string & value) :
+ pNextAttr_(NULL),
+ name_(name),
+ value_(value) {
+ }
+ explicit XmlAttr(const XmlAttr & att) :
+ pNextAttr_(NULL),
+ name_(att.name_),
+ value_(att.value_) {
+ }
+
+ XmlAttr * pNextAttr_;
+ QName name_;
+ std::string value_;
+};
+
+class XmlElement : public XmlChild {
+public:
+ explicit XmlElement(const QName & name);
+ explicit XmlElement(const QName & name, bool useDefaultNs);
+ explicit XmlElement(const XmlElement & elt);
+
+ virtual ~XmlElement();
+
+ const QName & Name() const { return name_; }
+
+ const std::string & BodyText() const;
+ void SetBodyText(const std::string & text);
+
+ const QName & FirstElementName() const;
+
+ XmlAttr * FirstAttr();
+ const XmlAttr * FirstAttr() const
+ { return const_cast<XmlElement *>(this)->FirstAttr(); }
+
+ //! Attr will return STR_EMPTY if the attribute isn't there:
+ //! use HasAttr to test presence of an attribute.
+ const std::string & Attr(const QName & name) const;
+ bool HasAttr(const QName & name) const;
+ void SetAttr(const QName & name, const std::string & value);
+ void ClearAttr(const QName & name);
+
+ XmlChild * FirstChild();
+ const XmlChild * FirstChild() const
+ { return const_cast<XmlElement *>(this)->FirstChild(); }
+
+ XmlElement * FirstElement();
+ const XmlElement * FirstElement() const
+ { return const_cast<XmlElement *>(this)->FirstElement(); }
+
+ XmlElement * NextElement();
+ const XmlElement * NextElement() const
+ { return const_cast<XmlElement *>(this)->NextElement(); }
+
+ XmlElement * FirstWithNamespace(const std::string & ns);
+ const XmlElement * FirstWithNamespace(const std::string & ns) const
+ { return const_cast<XmlElement *>(this)->FirstWithNamespace(ns); }
+
+ XmlElement * NextWithNamespace(const std::string & ns);
+ const XmlElement * NextWithNamespace(const std::string & ns) const
+ { return const_cast<XmlElement *>(this)->NextWithNamespace(ns); }
+
+ XmlElement * FirstNamed(const QName & name);
+ const XmlElement * FirstNamed(const QName & name) const
+ { return const_cast<XmlElement *>(this)->FirstNamed(name); }
+
+ XmlElement * NextNamed(const QName & name);
+ const XmlElement * NextNamed(const QName & name) const
+ { return const_cast<XmlElement *>(this)->NextNamed(name); }
+
+ const std::string & TextNamed(const QName & name) const;
+
+ void InsertChildAfter(XmlChild * pPredecessor, XmlChild * pNewChild);
+ void RemoveChildAfter(XmlChild * pPredecessor);
+
+ void AddParsedText(const char * buf, int len);
+ void AddText(const std::string & text);
+ void AddText(const std::string & text, int depth);
+ void AddElement(XmlElement * pelChild);
+ void AddElement(XmlElement * pelChild, int depth);
+ void AddAttr(const QName & name, const std::string & value);
+ void AddAttr(const QName & name, const std::string & value, int depth);
+ void ClearNamedChildren(const QName & name);
+ void ClearChildren();
+
+ static XmlElement * ForStr(const std::string & str);
+ std::string Str() const;
+
+ void Print(std::ostream * pout, std::string xmlns[], int xmlnsCount) const;
+
+protected:
+ virtual bool IsTextImpl() const;
+ virtual XmlElement * AsElementImpl() const;
+ virtual XmlText * AsTextImpl() const;
+
+private:
+ QName name_;
+ XmlAttr * pFirstAttr_;
+ XmlAttr * pLastAttr_;
+ XmlChild * pFirstChild_;
+ XmlChild * pLastChild_;
+
+};
+
+
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc
new file mode 100644
index 00000000..4dcb6490
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.cc
@@ -0,0 +1,205 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+XmlnsStack::XmlnsStack() :
+ pxmlnsStack_(new std::vector<std::string>),
+ pxmlnsDepthStack_(new std::vector<size_t>) {
+}
+
+XmlnsStack::~XmlnsStack() {}
+
+void
+XmlnsStack::PushFrame() {
+ pxmlnsDepthStack_->push_back(pxmlnsStack_->size());
+}
+
+void
+XmlnsStack::PopFrame() {
+ size_t prev_size = pxmlnsDepthStack_->back();
+ pxmlnsDepthStack_->pop_back();
+ if (prev_size < pxmlnsStack_->size()) {
+ pxmlnsStack_->erase(pxmlnsStack_->begin() + prev_size,
+ pxmlnsStack_->end());
+ }
+}
+const std::pair<std::string, bool> NS_NOT_FOUND(STR_EMPTY, false);
+const std::pair<std::string, bool> EMPTY_NS_FOUND(STR_EMPTY, true);
+const std::pair<std::string, bool> XMLNS_DEFINITION_FOUND(NS_XMLNS, true);
+
+const std::string *
+XmlnsStack::NsForPrefix(const std::string & prefix) {
+ if (prefix.length() >= 3 &&
+ (prefix[0] == 'x' || prefix[0] == 'X') &&
+ (prefix[1] == 'm' || prefix[1] == 'M') &&
+ (prefix[2] == 'l' || prefix[2] == 'L')) {
+ if (prefix == "xml")
+ return &(NS_XML);
+ if (prefix == "xmlns")
+ return &(NS_XMLNS);
+ return NULL;
+ }
+
+ std::vector<std::string>::iterator pos;
+ for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+ pos -= 2;
+ if (*pos == prefix)
+ return &(*(pos + 1));
+ }
+
+ if (prefix == STR_EMPTY)
+ return &(STR_EMPTY); // default namespace
+
+ return NULL; // none found
+}
+
+bool
+XmlnsStack::PrefixMatchesNs(const std::string & prefix, const std::string & ns) {
+ const std::string * match = NsForPrefix(prefix);
+ if (match == NULL)
+ return false;
+ return (*match == ns);
+}
+
+std::pair<std::string, bool>
+XmlnsStack::PrefixForNs(const std::string & ns, bool isattr) {
+ if (ns == NS_XML)
+ return std::make_pair(std::string("xml"), true);
+ if (ns == NS_XMLNS)
+ return std::make_pair(std::string("xmlns"), true);
+ if (isattr ? ns == STR_EMPTY : PrefixMatchesNs(STR_EMPTY, ns))
+ return std::make_pair(STR_EMPTY, true);
+
+ std::vector<std::string>::iterator pos;
+ for (pos = pxmlnsStack_->end(); pos > pxmlnsStack_->begin(); ) {
+ pos -= 2;
+ if (*(pos + 1) == ns &&
+ (!isattr || !pos->empty()) && PrefixMatchesNs(*pos, ns))
+ return std::make_pair(*pos, true);
+ }
+
+ return std::make_pair(STR_EMPTY, false); // none found
+}
+
+std::string
+XmlnsStack::FormatQName(const QName & name, bool isAttr) {
+ std::string prefix(PrefixForNs(name.Namespace(), isAttr).first);
+ if (prefix == STR_EMPTY)
+ return name.LocalPart();
+ else
+ return prefix + ':' + name.LocalPart();
+}
+
+void
+XmlnsStack::AddXmlns(const std::string & prefix, const std::string & ns) {
+ pxmlnsStack_->push_back(prefix);
+ pxmlnsStack_->push_back(ns);
+}
+
+void
+XmlnsStack::RemoveXmlns() {
+ pxmlnsStack_->pop_back();
+ pxmlnsStack_->pop_back();
+}
+
+static bool IsAsciiLetter(char ch) {
+ return ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z'));
+}
+
+static std::string AsciiLower(const std::string & s) {
+ std::string result(s);
+ size_t i;
+ for (i = 0; i < result.length(); i++) {
+ if (result[i] >= 'A' && result[i] <= 'Z')
+ result[i] += 'a' - 'A';
+ }
+ return result;
+}
+
+static std::string SuggestPrefix(const std::string & ns) {
+ size_t len = ns.length();
+ size_t i = ns.find_last_of('.');
+ if (i != std::string::npos && len - i <= 4 + 1)
+ len = i; // chop off ".html" or ".xsd" or ".?{0,4}"
+ size_t last = len;
+ while (last > 0) {
+ last -= 1;
+ if (IsAsciiLetter(ns[last])) {
+ size_t first = last;
+ last += 1;
+ while (first > 0) {
+ if (!IsAsciiLetter(ns[first - 1]))
+ break;
+ first -= 1;
+ }
+ if (last - first > 4)
+ last = first + 3;
+ std::string candidate(AsciiLower(ns.substr(first, last - first)));
+ if (candidate.find("xml") != 0)
+ return candidate;
+ break;
+ }
+ }
+ return "ns";
+}
+
+
+std::pair<std::string, bool>
+XmlnsStack::AddNewPrefix(const std::string & ns, bool isAttr) {
+ if (PrefixForNs(ns, isAttr).second)
+ return std::make_pair(STR_EMPTY, false);
+
+ std::string base(SuggestPrefix(ns));
+ std::string result(base);
+ int i = 2;
+ while (NsForPrefix(result) != NULL) {
+ std::stringstream ss;
+ ss << base;
+ ss << (i++);
+ ss >> result;
+ }
+ AddXmlns(result, ns);
+ return std::make_pair(result, true);
+}
+
+void XmlnsStack::Reset() {
+ pxmlnsStack_->clear();
+ pxmlnsDepthStack_->clear();
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h
new file mode 100644
index 00000000..299ec1ce
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlnsstack.h
@@ -0,0 +1,62 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlnsstack_h_
+#define _xmlnsstack_h_
+
+#include <string>
+#include "talk/base/scoped_ptr.h"
+#include "talk/base/stl_decl.h"
+#include "talk/xmllite/qname.h"
+
+namespace buzz {
+
+class XmlnsStack {
+public:
+ XmlnsStack();
+ ~XmlnsStack();
+
+ void AddXmlns(const std::string & prefix, const std::string & ns);
+ void RemoveXmlns();
+ void PushFrame();
+ void PopFrame();
+ void Reset();
+
+ const std::string * NsForPrefix(const std::string & prefix);
+ bool PrefixMatchesNs(const std::string & prefix, const std::string & ns);
+ std::pair<std::string, bool> PrefixForNs(const std::string & ns, bool isAttr);
+ std::pair<std::string, bool> AddNewPrefix(const std::string & ns, bool isAttr);
+ std::string FormatQName(const QName & name, bool isAttr);
+
+private:
+
+ scoped_ptr<std::vector<std::string, std::allocator<std::string> > > pxmlnsStack_;
+ scoped_ptr<std::vector<size_t, std::allocator<size_t> > > pxmlnsDepthStack_;
+};
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc
new file mode 100644
index 00000000..f2b56778
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.cc
@@ -0,0 +1,250 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <string>
+#include <vector>
+#include <iostream>
+#include <expat.h>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/base/common.h"
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+
+#include <expat.h>
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+
+static void
+StartElementCallback(void * userData, const char *name, const char **atts) {
+ (static_cast<XmlParser *>(userData))->ExpatStartElement(name, atts);
+}
+
+static void
+EndElementCallback(void * userData, const char *name) {
+ (static_cast<XmlParser *>(userData))->ExpatEndElement(name);
+}
+
+static void
+CharacterDataCallback(void * userData, const char *text, int len) {
+ (static_cast<XmlParser *>(userData))->ExpatCharacterData(text, len);
+}
+
+static void
+XmlDeclCallback(void * userData, const char * ver, const char * enc, int st) {
+ (static_cast<XmlParser *>(userData))->ExpatXmlDecl(ver, enc, st);
+}
+
+XmlParser::XmlParser(XmlParseHandler *pxph) :
+ context_(this), pxph_(pxph), sentError_(false) {
+ expat_ = XML_ParserCreate(NULL);
+ XML_SetUserData(expat_, this);
+ XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
+ XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
+ XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
+}
+
+void
+XmlParser::Reset() {
+ if (!XML_ParserReset(expat_, NULL)) {
+ XML_ParserFree(expat_);
+ expat_ = XML_ParserCreate(NULL);
+ }
+ XML_SetUserData(expat_, this);
+ XML_SetElementHandler(expat_, StartElementCallback, EndElementCallback);
+ XML_SetCharacterDataHandler(expat_, CharacterDataCallback);
+ XML_SetXmlDeclHandler(expat_, XmlDeclCallback);
+ context_.Reset();
+ sentError_ = false;
+}
+
+static bool
+XmlParser_StartsWithXmlns(const char *name) {
+ return name[0] == 'x' &&
+ name[1] == 'm' &&
+ name[2] == 'l' &&
+ name[3] == 'n' &&
+ name[4] == 's';
+}
+
+void
+XmlParser::ExpatStartElement(const char *name, const char **atts) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+ const char **att;
+ context_.StartElement();
+ for (att = atts; *att; att += 2) {
+ if (XmlParser_StartsWithXmlns(*att)) {
+ if ((*att)[5] == '\0') {
+ context_.StartNamespace("", *(att + 1));
+ }
+ else if ((*att)[5] == ':') {
+ if (**(att + 1) == '\0') {
+ // In XML 1.0 empty namespace illegal with prefix (not in 1.1)
+ context_.RaiseError(XML_ERROR_SYNTAX);
+ return;
+ }
+ context_.StartNamespace((*att) + 6, *(att + 1));
+ }
+ }
+ }
+ pxph_->StartElement(&context_, name, atts);
+}
+
+void
+XmlParser::ExpatEndElement(const char *name) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+ context_.EndElement();
+ pxph_->EndElement(&context_, name);
+}
+
+void
+XmlParser::ExpatCharacterData(const char *text, int len) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+ pxph_->CharacterData(&context_, text, len);
+}
+
+void
+XmlParser::ExpatXmlDecl(const char * ver, const char * enc, int standalone) {
+ if (context_.RaisedError() != XML_ERROR_NONE)
+ return;
+
+ if (ver && std::string("1.0") != ver) {
+ context_.RaiseError(XML_ERROR_SYNTAX);
+ return;
+ }
+
+ if (standalone == 0) {
+ context_.RaiseError(XML_ERROR_SYNTAX);
+ return;
+ }
+
+ if (enc && !((enc[0] == 'U' || enc[0] == 'u') &&
+ (enc[1] == 'T' || enc[1] == 't') &&
+ (enc[2] == 'F' || enc[2] == 'f') &&
+ enc[3] == '-' && enc[4] =='8')) {
+ context_.RaiseError(XML_ERROR_INCORRECT_ENCODING);
+ return;
+ }
+
+}
+
+bool
+XmlParser::Parse(const char *data, size_t len, bool isFinal) {
+ if (sentError_)
+ return false;
+
+ if (XML_Parse(expat_, data, static_cast<int>(len), isFinal) != XML_STATUS_OK)
+ context_.RaiseError(XML_GetErrorCode(expat_));
+
+ if (context_.RaisedError() != XML_ERROR_NONE) {
+ sentError_ = true;
+ pxph_->Error(&context_, context_.RaisedError());
+ return false;
+ }
+
+ return true;
+}
+
+XmlParser::~XmlParser() {
+ XML_ParserFree(expat_);
+}
+
+void
+XmlParser::ParseXml(XmlParseHandler *pxph, std::string text) {
+ XmlParser parser(pxph);
+ parser.Parse(text.c_str(), text.length(), true);
+}
+
+XmlParser::ParseContext::ParseContext(XmlParser *parser) :
+ parser_(parser),
+ xmlnsstack_(),
+ raised_(XML_ERROR_NONE) {
+}
+
+void
+XmlParser::ParseContext::StartNamespace(const char *prefix, const char *ns) {
+ xmlnsstack_.AddXmlns(
+ *prefix ? std::string(prefix) : STR_EMPTY,
+// ns == NS_CLIENT ? NS_CLIENT :
+// ns == NS_ROSTER ? NS_ROSTER :
+// ns == NS_GR ? NS_GR :
+ std::string(ns));
+}
+
+void
+XmlParser::ParseContext::StartElement() {
+ xmlnsstack_.PushFrame();
+}
+
+void
+XmlParser::ParseContext::EndElement() {
+ xmlnsstack_.PopFrame();
+}
+
+QName
+XmlParser::ParseContext::ResolveQName(const char *qname, bool isAttr) {
+ const char *c;
+ for (c = qname; *c; ++c) {
+ if (*c == ':') {
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(std::string(qname, c - qname));
+ if (result == NULL)
+ return QN_EMPTY;
+ const char * localname = c + 1;
+ return QName(*result, localname);
+ }
+ }
+ if (isAttr) {
+ return QName(STR_EMPTY, qname);
+ }
+
+ const std::string * result;
+ result = xmlnsstack_.NsForPrefix(STR_EMPTY);
+ if (result == NULL)
+ return QN_EMPTY;
+
+ return QName(*result, qname);
+}
+
+void
+XmlParser::ParseContext::Reset() {
+ xmlnsstack_.Reset();
+ raised_ = XML_ERROR_NONE;
+}
+
+XmlParser::ParseContext::~ParseContext() {
+}
+
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h
new file mode 100644
index 00000000..760802e4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlparser.h
@@ -0,0 +1,108 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlparser_h_
+#define _xmlparser_h_
+
+#include <string>
+#include "talk/xmllite/xmlnsstack.h"
+#include <expat.h>
+
+struct XML_ParserStruct;
+typedef struct XML_ParserStruct * XML_Parser;
+
+namespace buzz {
+
+class XmlParseHandler;
+class XmlParseContext;
+class XmlParser;
+
+class XmlParseContext {
+public:
+ virtual QName ResolveQName(const char * qname, bool isAttr) = 0;
+ virtual void RaiseError(XML_Error err) = 0;
+};
+
+class XmlParseHandler {
+public:
+ virtual void StartElement(XmlParseContext * pctx,
+ const char * name, const char ** atts) = 0;
+ virtual void EndElement(XmlParseContext * pctx,
+ const char * name) = 0;
+ virtual void CharacterData(XmlParseContext * pctx,
+ const char * text, int len) = 0;
+ virtual void Error(XmlParseContext * pctx,
+ XML_Error errorCode) = 0;
+};
+
+class XmlParser {
+public:
+ static void ParseXml(XmlParseHandler * pxph, std::string text);
+
+ explicit XmlParser(XmlParseHandler * pxph);
+ bool Parse(const char * data, size_t len, bool isFinal);
+ void Reset();
+ virtual ~XmlParser();
+
+ // expat callbacks
+ void ExpatStartElement(const char * name, const char ** atts);
+ void ExpatEndElement(const char * name);
+ void ExpatCharacterData(const char * text, int len);
+ void ExpatXmlDecl(const char * ver, const char * enc, int standalone);
+
+private:
+
+ class ParseContext : public XmlParseContext {
+ public:
+ ParseContext(XmlParser * parser);
+ virtual ~ParseContext();
+ virtual QName ResolveQName(const char * qname, bool isAttr);
+ virtual void RaiseError(XML_Error err) { if (!raised_) raised_ = err; }
+ XML_Error RaisedError() { return raised_; }
+ void Reset();
+
+ void StartElement();
+ void EndElement();
+ void StartNamespace(const char * prefix, const char * ns);
+
+ private:
+ const XmlParser * parser_;
+ XmlnsStack xmlnsstack_;
+ XML_Error raised_;
+ };
+
+ ParseContext context_;
+ XML_Parser expat_;
+ XmlParseHandler * pxph_;
+ bool sentError_;
+
+
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc
new file mode 100644
index 00000000..892e2ebb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.cc
@@ -0,0 +1,190 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/stl_decl.h"
+#include <string>
+#include <iostream>
+#include <vector>
+#include <sstream>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmllite/xmlnsstack.h"
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+class XmlPrinterImpl {
+public:
+ XmlPrinterImpl(std::ostream * pout,
+ const std::string * const xmlns, int xmlnsCount);
+ void PrintElement(const XmlElement * element);
+ void PrintQuotedValue(const std::string & text);
+ void PrintBodyText(const std::string & text);
+
+private:
+ std::ostream *pout_;
+ XmlnsStack xmlnsStack_;
+};
+
+void
+XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element) {
+ PrintXml(pout, element, NULL, 0);
+}
+
+void
+XmlPrinter::PrintXml(std::ostream * pout, const XmlElement * element,
+ const std::string * const xmlns, int xmlnsCount) {
+ XmlPrinterImpl printer(pout, xmlns, xmlnsCount);
+ printer.PrintElement(element);
+}
+
+XmlPrinterImpl::XmlPrinterImpl(std::ostream * pout,
+ const std::string * const xmlns, int xmlnsCount) :
+ pout_(pout),
+ xmlnsStack_() {
+ int i;
+ for (i = 0; i < xmlnsCount; i += 2) {
+ xmlnsStack_.AddXmlns(xmlns[i], xmlns[i + 1]);
+ }
+}
+
+void
+XmlPrinterImpl::PrintElement(const XmlElement * element) {
+ xmlnsStack_.PushFrame();
+
+ // first go through attrs of pel to add xmlns definitions
+ const XmlAttr * pattr;
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ if (pattr->Name() == QN_XMLNS)
+ xmlnsStack_.AddXmlns(STR_EMPTY, pattr->Value());
+ else if (pattr->Name().Namespace() == NS_XMLNS)
+ xmlnsStack_.AddXmlns(pattr->Name().LocalPart(),
+ pattr->Value());
+ }
+
+ // then go through qnames to make sure needed xmlns definitons are added
+ std::vector<std::string> newXmlns;
+ std::pair<std::string, bool> prefix;
+ prefix = xmlnsStack_.AddNewPrefix(element->Name().Namespace(), false);
+ if (prefix.second) {
+ newXmlns.push_back(prefix.first);
+ newXmlns.push_back(element->Name().Namespace());
+ }
+
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ prefix = xmlnsStack_.AddNewPrefix(pattr->Name().Namespace(), true);
+ if (prefix.second) {
+ newXmlns.push_back(prefix.first);
+ newXmlns.push_back(element->Name().Namespace());
+ }
+ }
+
+ // print the element name
+ *pout_ << '<' << xmlnsStack_.FormatQName(element->Name(), false);
+
+ // and the attributes
+ for (pattr = element->FirstAttr(); pattr; pattr = pattr->NextAttr()) {
+ *pout_ << ' ' << xmlnsStack_.FormatQName(pattr->Name(), true) << "=\"";
+ PrintQuotedValue(pattr->Value());
+ *pout_ << '"';
+ }
+
+ // and the extra xmlns declarations
+ std::vector<std::string>::iterator i(newXmlns.begin());
+ while (i < newXmlns.end()) {
+ if (*i == STR_EMPTY)
+ *pout_ << " xmlns=\"" << *(i + 1) << '"';
+ else
+ *pout_ << " xmlns:" << *i << "=\"" << *(i + 1) << '"';
+ i += 2;
+ }
+
+ // now the children
+ const XmlChild * pchild = element->FirstChild();
+
+ if (pchild == NULL)
+ *pout_ << "/>";
+ else {
+ *pout_ << '>';
+ while (pchild) {
+ if (pchild->IsText())
+ PrintBodyText(pchild->AsText()->Text());
+ else
+ PrintElement(pchild->AsElement());
+ pchild = pchild->NextChild();
+ }
+ *pout_ << "</" << xmlnsStack_.FormatQName(element->Name(), false) << '>';
+ }
+
+ xmlnsStack_.PopFrame();
+}
+
+void
+XmlPrinterImpl::PrintQuotedValue(const std::string & text) {
+ size_t safe = 0;
+ for (;;) {
+ size_t unsafe = text.find_first_of("<>&\"", safe);
+ if (unsafe == std::string::npos)
+ unsafe = text.length();
+ *pout_ << text.substr(safe, unsafe - safe);
+ if (unsafe == text.length())
+ return;
+ switch (text[unsafe]) {
+ case '<': *pout_ << "&lt;"; break;
+ case '>': *pout_ << "&gt;"; break;
+ case '&': *pout_ << "&amp;"; break;
+ case '"': *pout_ << "&quot;"; break;
+ }
+ safe = unsafe + 1;
+ if (safe == text.length())
+ return;
+ }
+}
+
+void
+XmlPrinterImpl::PrintBodyText(const std::string & text) {
+ size_t safe = 0;
+ for (;;) {
+ size_t unsafe = text.find_first_of("<>&", safe);
+ if (unsafe == std::string::npos)
+ unsafe = text.length();
+ *pout_ << text.substr(safe, unsafe - safe);
+ if (unsafe == text.length())
+ return;
+ switch (text[unsafe]) {
+ case '<': *pout_ << "&lt;"; break;
+ case '>': *pout_ << "&gt;"; break;
+ case '&': *pout_ << "&amp;"; break;
+ }
+ safe = unsafe + 1;
+ if (safe == text.length())
+ return;
+ }
+}
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h
new file mode 100644
index 00000000..96900d0d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmllite/xmlprinter.h
@@ -0,0 +1,49 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmlprinter_h_
+#define _xmlprinter_h_
+
+#include <iosfwd>
+#include <string>
+#include "talk/base/scoped_ptr.h"
+
+namespace buzz {
+
+class XmlElement;
+
+class XmlPrinter {
+public:
+ static void PrintXml(std::ostream * pout, const XmlElement * pelt);
+
+ static void PrintXml(std::ostream * pout, const XmlElement * pelt,
+ const std::string * const xmlns, int xmlnsCount);
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am
new file mode 100644
index 00000000..527f7053
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/Makefile.am
@@ -0,0 +1,34 @@
+## Does not compile with final
+KDE_OPTIONS = nofinal
+
+libcricketxmpp_la_SOURCES = constants.cc \
+ jid.cc \
+ saslmechanism.cc \
+ xmppclient.cc \
+ xmppengineimpl.cc \
+ xmppengineimpl_iq.cc \
+ xmpplogintask.cc \
+ xmppstanzaparser.cc \
+ xmpptask.cc
+
+noinst_HEADERS = asyncsocket.h \
+ prexmppauth.h \
+ saslhandler.h \
+ xmpplogintask.h \
+ jid.h \
+ saslmechanism.h \
+ xmppclient.h \
+ xmpppassword.h \
+ constants.h \
+ saslplainmechanism.h \
+ xmppclientsettings.h \
+ xmppstanzaparser.h \
+ xmppengine.h \
+ xmpptask.h \
+ plainsaslhandler.h \
+ saslcookiemechanism.h \
+ xmppengineimpl.h
+
+
+AM_CPPFLAGS = -DPOSIX -I$(srcdir)/../..
+noinst_LTLIBRARIES = libcricketxmpp.la
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h
new file mode 100644
index 00000000..fd91929b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/asyncsocket.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _ASYNCSOCKET_H_
+#define _ASYNCSOCKET_H_
+
+#include "talk/base/sigslot.h"
+
+namespace cricket {
+ class SocketAddress;
+}
+
+namespace buzz {
+
+class AsyncSocket {
+public:
+ enum State {
+ STATE_CLOSED = 0, //!< Socket is not open.
+ STATE_CLOSING, //!< Socket is closing but can have buffered data
+ STATE_CONNECTING, //!< In the process of
+ STATE_OPEN, //!< Socket is connected
+#if defined(FEATURE_ENABLE_SSL)
+ STATE_TLS_CONNECTING, //!< Establishing TLS connection
+ STATE_TLS_OPEN, //!< TLS connected
+#endif
+ };
+
+ enum Error {
+ ERROR_NONE = 0, //!< No error
+ ERROR_WINSOCK, //!< Winsock error
+ ERROR_DNS, //!< Couldn't resolve host name
+ ERROR_WRONGSTATE, //!< Call made while socket is in the wrong state
+#if defined(FEATURE_ENABLE_SSL)
+ ERROR_SSL, //!< Something went wrong with OpenSSL
+#endif
+ };
+
+ virtual ~AsyncSocket() {}
+ virtual State state() = 0;
+ virtual Error error() = 0;
+
+ virtual bool Connect(const cricket::SocketAddress& addr) = 0;
+ virtual bool Read(char * data, size_t len, size_t* len_read) = 0;
+ virtual bool Write(const char * data, size_t len) = 0;
+ virtual bool Close() = 0;
+#if defined(FEATURE_ENABLE_SSL)
+ // We allow matching any passed domain.
+ // If both names are passed as empty, we do not require a match.
+ virtual bool StartTls(const std::string & domainname) = 0;
+#endif
+
+ sigslot::signal0<> SignalConnected;
+ sigslot::signal0<> SignalSSLConnected;
+ sigslot::signal0<> SignalClosed;
+ sigslot::signal0<> SignalRead;
+ sigslot::signal0<> SignalError;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc
new file mode 100644
index 00000000..b2c833f7
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.cc
@@ -0,0 +1,331 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include "talk/base/basicdefs.h"
+#include "talk/xmllite/xmlconstants.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+namespace buzz {
+
+const Jid JID_EMPTY(STR_EMPTY);
+
+const std::string & Constants::ns_client() {
+ static const std::string ns_client_("jabber:client");
+ return ns_client_;
+}
+
+const std::string & Constants::ns_server() {
+ static const std::string ns_server_("jabber:server");
+ return ns_server_;
+}
+
+const std::string & Constants::ns_stream() {
+ static const std::string ns_stream_("http://etherx.jabber.org/streams");
+ return ns_stream_;
+}
+
+const std::string & Constants::ns_xstream() {
+ static const std::string ns_xstream_("urn:ietf:params:xml:ns:xmpp-streams");
+ return ns_xstream_;
+}
+
+const std::string & Constants::ns_tls() {
+ static const std::string ns_tls_("urn:ietf:params:xml:ns:xmpp-tls");
+ return ns_tls_;
+}
+
+const std::string & Constants::ns_sasl() {
+ static const std::string ns_sasl_("urn:ietf:params:xml:ns:xmpp-sasl");
+ return ns_sasl_;
+}
+
+const std::string & Constants::ns_bind() {
+ static const std::string ns_bind_("urn:ietf:params:xml:ns:xmpp-bind");
+ return ns_bind_;
+}
+
+const std::string & Constants::ns_dialback() {
+ static const std::string ns_dialback_("jabber:server:dialback");
+ return ns_dialback_;
+}
+
+const std::string & Constants::ns_session() {
+ static const std::string ns_session_("urn:ietf:params:xml:ns:xmpp-session");
+ return ns_session_;
+}
+
+const std::string & Constants::ns_stanza() {
+ static const std::string ns_stanza_("urn:ietf:params:xml:ns:xmpp-stanzas");
+ return ns_stanza_;
+}
+
+const std::string & Constants::ns_privacy() {
+ static const std::string ns_privacy_("jabber:iq:privacy");
+ return ns_privacy_;
+}
+
+const std::string & Constants::ns_roster() {
+ static const std::string ns_roster_("jabber:iq:roster");
+ return ns_roster_;
+}
+
+const std::string & Constants::ns_vcard() {
+ static const std::string ns_vcard_("vcard-temp");
+ return ns_vcard_;
+}
+
+const std::string & Constants::str_client() {
+ static const std::string str_client_("client");
+ return str_client_;
+}
+
+const std::string & Constants::str_server() {
+ static const std::string str_server_("server");
+ return str_server_;
+}
+
+const std::string & Constants::str_stream() {
+ static const std::string str_stream_("stream");
+ return str_stream_;
+}
+
+const std::string STR_GET("get");
+const std::string STR_SET("set");
+const std::string STR_RESULT("result");
+const std::string STR_ERROR("error");
+
+const std::string STR_FROM("from");
+const std::string STR_TO("to");
+const std::string STR_BOTH("both");
+const std::string STR_REMOVE("remove");
+
+const std::string STR_UNAVAILABLE("unavailable");
+const std::string STR_INVISIBLE("invisible");
+
+const std::string STR_GOOGLE_COM("google.com");
+const std::string STR_GMAIL_COM("gmail.com");
+const std::string STR_GOOGLEMAIL_COM("googlemail.com");
+const std::string STR_DEFAULT_DOMAIN("default.talk.google.com");
+const std::string STR_X("x");
+
+const QName QN_STREAM_STREAM(true, NS_STREAM, STR_STREAM);
+const QName QN_STREAM_FEATURES(true, NS_STREAM, "features");
+const QName QN_STREAM_ERROR(true, NS_STREAM, "error");
+
+const QName QN_XSTREAM_BAD_FORMAT(true, NS_XSTREAM, "bad-format");
+const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX(true, NS_XSTREAM, "bad-namespace-prefix");
+const QName QN_XSTREAM_CONFLICT(true, NS_XSTREAM, "conflict");
+const QName QN_XSTREAM_CONNECTION_TIMEOUT(true, NS_XSTREAM, "connection-timeout");
+const QName QN_XSTREAM_HOST_GONE(true, NS_XSTREAM, "host-gone");
+const QName QN_XSTREAM_HOST_UNKNOWN(true, NS_XSTREAM, "host-unknown");
+const QName QN_XSTREAM_IMPROPER_ADDRESSIING(true, NS_XSTREAM, "improper-addressing");
+const QName QN_XSTREAM_INTERNAL_SERVER_ERROR(true, NS_XSTREAM, "internal-server-error");
+const QName QN_XSTREAM_INVALID_FROM(true, NS_XSTREAM, "invalid-from");
+const QName QN_XSTREAM_INVALID_ID(true, NS_XSTREAM, "invalid-id");
+const QName QN_XSTREAM_INVALID_NAMESPACE(true, NS_XSTREAM, "invalid-namespace");
+const QName QN_XSTREAM_INVALID_XML(true, NS_XSTREAM, "invalid-xml");
+const QName QN_XSTREAM_NOT_AUTHORIZED(true, NS_XSTREAM, "not-authorized");
+const QName QN_XSTREAM_POLICY_VIOLATION(true, NS_XSTREAM, "policy-violation");
+const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED(true, NS_XSTREAM, "remote-connection-failed");
+const QName QN_XSTREAM_RESOURCE_CONSTRAINT(true, NS_XSTREAM, "resource-constraint");
+const QName QN_XSTREAM_RESTRICTED_XML(true, NS_XSTREAM, "restricted-xml");
+const QName QN_XSTREAM_SEE_OTHER_HOST(true, NS_XSTREAM, "see-other-host");
+const QName QN_XSTREAM_SYSTEM_SHUTDOWN(true, NS_XSTREAM, "system-shutdown");
+const QName QN_XSTREAM_UNDEFINED_CONDITION(true, NS_XSTREAM, "undefined-condition");
+const QName QN_XSTREAM_UNSUPPORTED_ENCODING(true, NS_XSTREAM, "unsupported-encoding");
+const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE(true, NS_XSTREAM, "unsupported-stanza-type");
+const QName QN_XSTREAM_UNSUPPORTED_VERSION(true, NS_XSTREAM, "unsupported-version");
+const QName QN_XSTREAM_XML_NOT_WELL_FORMED(true, NS_XSTREAM, "xml-not-well-formed");
+const QName QN_XSTREAM_TEXT(true, NS_XSTREAM, "text");
+
+const QName QN_TLS_STARTTLS(true, NS_TLS, "starttls");
+const QName QN_TLS_REQUIRED(true, NS_TLS, "required");
+const QName QN_TLS_PROCEED(true, NS_TLS, "proceed");
+const QName QN_TLS_FAILURE(true, NS_TLS, "failure");
+
+const QName QN_SASL_MECHANISMS(true, NS_SASL, "mechanisms");
+const QName QN_SASL_MECHANISM(true, NS_SASL, "mechanism");
+const QName QN_SASL_AUTH(true, NS_SASL, "auth");
+const QName QN_SASL_CHALLENGE(true, NS_SASL, "challenge");
+const QName QN_SASL_RESPONSE(true, NS_SASL, "response");
+const QName QN_SASL_ABORT(true, NS_SASL, "abort");
+const QName QN_SASL_SUCCESS(true, NS_SASL, "success");
+const QName QN_SASL_FAILURE(true, NS_SASL, "failure");
+const QName QN_SASL_ABORTED(true, NS_SASL, "aborted");
+const QName QN_SASL_INCORRECT_ENCODING(true, NS_SASL, "incorrect-encoding");
+const QName QN_SASL_INVALID_AUTHZID(true, NS_SASL, "invalid-authzid");
+const QName QN_SASL_INVALID_MECHANISM(true, NS_SASL, "invalid-mechanism");
+const QName QN_SASL_MECHANISM_TOO_WEAK(true, NS_SASL, "mechanism-too-weak");
+const QName QN_SASL_NOT_AUTHORIZED(true, NS_SASL, "not-authorized");
+const QName QN_SASL_TEMPORARY_AUTH_FAILURE(true, NS_SASL, "temporary-auth-failure");
+
+const QName QN_DIALBACK_RESULT(true, NS_DIALBACK, "result");
+const QName QN_DIALBACK_VERIFY(true, NS_DIALBACK, "verify");
+
+const QName QN_STANZA_BAD_REQUEST(true, NS_STANZA, "bad-request");
+const QName QN_STANZA_CONFLICT(true, NS_STANZA, "conflict");
+const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED(true, NS_STANZA, "feature-not-implemented");
+const QName QN_STANZA_FORBIDDEN(true, NS_STANZA, "forbidden");
+const QName QN_STANZA_GONE(true, NS_STANZA, "gone");
+const QName QN_STANZA_INTERNAL_SERVER_ERROR(true, NS_STANZA, "internal-server-error");
+const QName QN_STANZA_ITEM_NOT_FOUND(true, NS_STANZA, "item-not-found");
+const QName QN_STANZA_JID_MALFORMED(true, NS_STANZA, "jid-malformed");
+const QName QN_STANZA_NOT_ACCEPTABLE(true, NS_STANZA, "not-acceptable");
+const QName QN_STANZA_NOT_ALLOWED(true, NS_STANZA, "not-allowed");
+const QName QN_STANZA_PAYMENT_REQUIRED(true, NS_STANZA, "payment-required");
+const QName QN_STANZA_RECIPIENT_UNAVAILABLE(true, NS_STANZA, "recipient-unavailable");
+const QName QN_STANZA_REDIRECT(true, NS_STANZA, "redirect");
+const QName QN_STANZA_REGISTRATION_REQUIRED(true, NS_STANZA, "registration-required");
+const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND(true, NS_STANZA, "remote-server-not-found");
+const QName QN_STANZA_REMOTE_SERVER_TIMEOUT(true, NS_STANZA, "remote-server-timeout");
+const QName QN_STANZA_RESOURCE_CONSTRAINT(true, NS_STANZA, "resource-constraint");
+const QName QN_STANZA_SERVICE_UNAVAILABLE(true, NS_STANZA, "service-unavailable");
+const QName QN_STANZA_SUBSCRIPTION_REQUIRED(true, NS_STANZA, "subscription-required");
+const QName QN_STANZA_UNDEFINED_CONDITION(true, NS_STANZA, "undefined-condition");
+const QName QN_STANZA_UNEXPECTED_REQUEST(true, NS_STANZA, "unexpected-request");
+const QName QN_STANZA_TEXT(true, NS_STANZA, "text");
+
+const QName QN_BIND_BIND(true, NS_BIND, "bind");
+const QName QN_BIND_RESOURCE(true, NS_BIND, "resource");
+const QName QN_BIND_JID(true, NS_BIND, "jid");
+
+const QName QN_MESSAGE(true, NS_CLIENT, "message");
+const QName QN_BODY(true, NS_CLIENT, "body");
+const QName QN_SUBJECT(true, NS_CLIENT, "subject");
+const QName QN_THREAD(true, NS_CLIENT, "thread");
+const QName QN_PRESENCE(true, NS_CLIENT, "presence");
+const QName QN_SHOW(true, NS_CLIENT, "show");
+const QName QN_STATUS(true, NS_CLIENT, "status");
+const QName QN_LANG(true, NS_CLIENT, "lang");
+const QName QN_PRIORITY(true, NS_CLIENT, "priority");
+const QName QN_IQ(true, NS_CLIENT, "iq");
+const QName QN_ERROR(true, NS_CLIENT, "error");
+
+const QName QN_SERVER_MESSAGE(true, NS_SERVER, "message");
+const QName QN_SERVER_BODY(true, NS_SERVER, "body");
+const QName QN_SERVER_SUBJECT(true, NS_SERVER, "subject");
+const QName QN_SERVER_THREAD(true, NS_SERVER, "thread");
+const QName QN_SERVER_PRESENCE(true, NS_SERVER, "presence");
+const QName QN_SERVER_SHOW(true, NS_SERVER, "show");
+const QName QN_SERVER_STATUS(true, NS_SERVER, "status");
+const QName QN_SERVER_LANG(true, NS_SERVER, "lang");
+const QName QN_SERVER_PRIORITY(true, NS_SERVER, "priority");
+const QName QN_SERVER_IQ(true, NS_SERVER, "iq");
+const QName QN_SERVER_ERROR(true, NS_SERVER, "error");
+
+const QName QN_SESSION_SESSION(true, NS_SESSION, "session");
+
+const QName QN_PRIVACY_QUERY(true, NS_PRIVACY, "query");
+const QName QN_PRIVACY_ACTIVE(true, NS_PRIVACY, "active");
+const QName QN_PRIVACY_DEFAULT(true, NS_PRIVACY, "default");
+const QName QN_PRIVACY_LIST(true, NS_PRIVACY, "list");
+const QName QN_PRIVACY_ITEM(true, NS_PRIVACY, "item");
+const QName QN_PRIVACY_IQ(true, NS_PRIVACY, "iq");
+const QName QN_PRIVACY_MESSAGE(true, NS_PRIVACY, "message");
+const QName QN_PRIVACY_PRESENCE_IN(true, NS_PRIVACY, "presence-in");
+const QName QN_PRIVACY_PRESENCE_OUT(true, NS_PRIVACY, "presence-out");
+
+const QName QN_ROSTER_QUERY(true, NS_ROSTER, "query");
+const QName QN_ROSTER_ITEM(true, NS_ROSTER, "item");
+const QName QN_ROSTER_GROUP(true, NS_ROSTER, "group");
+
+const QName QN_VCARD_QUERY(true, NS_VCARD, "vCard");
+const QName QN_VCARD_FN(true, NS_VCARD, "FN");
+
+const QName QN_XML_LANG(true, NS_XML, "lang");
+
+const std::string STR_TYPE("type");
+const std::string STR_ID("id");
+const std::string STR_NAME("name");
+const std::string STR_JID("jid");
+const std::string STR_SUBSCRIPTION("subscription");
+const std::string STR_ASK("ask");
+
+const QName QN_ENCODING(true, STR_EMPTY, STR_ENCODING);
+const QName QN_VERSION(true, STR_EMPTY, STR_VERSION);
+const QName QN_TO(true, STR_EMPTY, "to");
+const QName QN_FROM(true, STR_EMPTY, "from");
+const QName QN_TYPE(true, STR_EMPTY, "type");
+const QName QN_ID(true, STR_EMPTY, "id");
+const QName QN_CODE(true, STR_EMPTY, "code");
+const QName QN_NAME(true, STR_EMPTY, "name");
+const QName QN_VALUE(true, STR_EMPTY, "value");
+const QName QN_ACTION(true, STR_EMPTY, "action");
+const QName QN_ORDER(true, STR_EMPTY, "order");
+const QName QN_MECHANISM(true, STR_EMPTY, "mechanism");
+const QName QN_ASK(true, STR_EMPTY, "ask");
+const QName QN_JID(true, STR_EMPTY, "jid");
+const QName QN_SUBSCRIPTION(true, STR_EMPTY, "subscription");
+const QName QN_SOURCE(true, STR_EMPTY, "source");
+
+const QName QN_XMLNS_CLIENT(true, NS_XMLNS, STR_CLIENT);
+const QName QN_XMLNS_SERVER(true, NS_XMLNS, STR_SERVER);
+const QName QN_XMLNS_STREAM(true, NS_XMLNS, STR_STREAM);
+
+// Presence
+const std::string STR_SHOW_AWAY("away");
+const std::string STR_SHOW_CHAT("chat");
+const std::string STR_SHOW_DND("dnd");
+const std::string STR_SHOW_XA("xa");
+
+// Subscription
+const std::string STR_SUBSCRIBE("subscribe");
+const std::string STR_SUBSCRIBED("subscribed");
+const std::string STR_UNSUBSCRIBE("unsubscribe");
+const std::string STR_UNSUBSCRIBED("unsubscribed");
+
+
+// JEP 0030
+const QName QN_NODE(true, STR_EMPTY, "node");
+const QName QN_CATEGORY(true, STR_EMPTY, "category");
+const QName QN_VAR(true, STR_EMPTY, "var");
+const std::string NS_DISCO_INFO("http://jabber.org/protocol/disco#info");
+const std::string NS_DISCO_ITEMS("http://jabber.org/protocol/disco#items");
+const QName QN_DISCO_INFO_QUERY(true, NS_DISCO_INFO, "query");
+const QName QN_DISCO_IDENTITY(true, NS_DISCO_INFO, "identity");
+const QName QN_DISCO_FEATURE(true, NS_DISCO_INFO, "feature");
+
+const QName QN_DISCO_ITEMS_QUERY(true, NS_DISCO_ITEMS, "query");
+const QName QN_DISCO_ITEM(true, NS_DISCO_ITEMS, "item");
+
+
+// JEP 0115
+const std::string NS_CAPS("http://jabber.org/protocol/caps");
+const QName QN_CAPS_C(true, NS_CAPS, "c");
+const QName QN_VER(true, STR_EMPTY, "ver");
+const QName QN_EXT(true, STR_EMPTY, "ext");
+
+// JEP 0091 Delayed Delivery
+const std::string kNSDelay("jabber:x:delay");
+const QName kQnDelayX(true, kNSDelay, "x");
+const QName kQnStamp(true, STR_EMPTY, "stamp");
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h
new file mode 100644
index 00000000..b05af965
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/constants.h
@@ -0,0 +1,300 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
+#define _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
+
+#include <string>
+#include "talk/xmllite/qname.h"
+#include "talk/xmpp/jid.h"
+
+
+#define NS_CLIENT Constants::ns_client()
+#define NS_SERVER Constants::ns_server()
+#define NS_STREAM Constants::ns_stream()
+#define NS_XSTREAM Constants::ns_xstream()
+#define NS_TLS Constants::ns_tls()
+#define NS_SASL Constants::ns_sasl()
+#define NS_BIND Constants::ns_bind()
+#define NS_DIALBACK Constants::ns_dialback()
+#define NS_SESSION Constants::ns_session()
+#define NS_STANZA Constants::ns_stanza()
+#define NS_PRIVACY Constants::ns_privacy()
+#define NS_ROSTER Constants::ns_roster()
+#define NS_VCARD Constants::ns_vcard()
+#define STR_CLIENT Constants::str_client()
+#define STR_SERVER Constants::str_server()
+#define STR_STREAM Constants::str_stream()
+
+namespace buzz {
+
+extern const Jid JID_EMPTY;
+
+class Constants {
+ public:
+ static const std::string & ns_client();
+ static const std::string & ns_server();
+ static const std::string & ns_stream();
+ static const std::string & ns_xstream();
+ static const std::string & ns_tls();
+ static const std::string & ns_sasl();
+ static const std::string & ns_bind();
+ static const std::string & ns_dialback();
+ static const std::string & ns_session();
+ static const std::string & ns_stanza();
+ static const std::string & ns_privacy();
+ static const std::string & ns_roster();
+ static const std::string & ns_vcard();
+
+ static const std::string & str_client();
+ static const std::string & str_server();
+ static const std::string & str_stream();
+};
+
+extern const std::string STR_GET;
+extern const std::string STR_SET;
+extern const std::string STR_RESULT;
+extern const std::string STR_ERROR;
+
+extern const std::string STR_FROM;
+extern const std::string STR_TO;
+extern const std::string STR_BOTH;
+extern const std::string STR_REMOVE;
+
+extern const std::string STR_MESSAGE;
+extern const std::string STR_BODY;
+extern const std::string STR_PRESENCE;
+extern const std::string STR_STATUS;
+extern const std::string STR_SHOW;
+extern const std::string STR_PRIOIRTY;
+extern const std::string STR_IQ;
+
+extern const std::string STR_TYPE;
+extern const std::string STR_NAME;
+extern const std::string STR_ID;
+extern const std::string STR_JID;
+extern const std::string STR_SUBSCRIPTION;
+extern const std::string STR_ASK;
+extern const std::string STR_X;
+extern const std::string STR_GOOGLE_COM;
+extern const std::string STR_GMAIL_COM;
+extern const std::string STR_GOOGLEMAIL_COM;
+extern const std::string STR_DEFAULT_DOMAIN;
+
+extern const std::string STR_UNAVAILABLE;
+extern const std::string STR_INVISIBLE;
+
+extern const QName QN_STREAM_STREAM;
+extern const QName QN_STREAM_FEATURES;
+extern const QName QN_STREAM_ERROR;
+
+extern const QName QN_XSTREAM_BAD_FORMAT;
+extern const QName QN_XSTREAM_BAD_NAMESPACE_PREFIX;
+extern const QName QN_XSTREAM_CONFLICT;
+extern const QName QN_XSTREAM_CONNECTION_TIMEOUT;
+extern const QName QN_XSTREAM_HOST_GONE;
+extern const QName QN_XSTREAM_HOST_UNKNOWN;
+extern const QName QN_XSTREAM_IMPROPER_ADDRESSIING;
+extern const QName QN_XSTREAM_INTERNAL_SERVER_ERROR;
+extern const QName QN_XSTREAM_INVALID_FROM;
+extern const QName QN_XSTREAM_INVALID_ID;
+extern const QName QN_XSTREAM_INVALID_NAMESPACE;
+extern const QName QN_XSTREAM_INVALID_XML;
+extern const QName QN_XSTREAM_NOT_AUTHORIZED;
+extern const QName QN_XSTREAM_POLICY_VIOLATION;
+extern const QName QN_XSTREAM_REMOTE_CONNECTION_FAILED;
+extern const QName QN_XSTREAM_RESOURCE_CONSTRAINT;
+extern const QName QN_XSTREAM_RESTRICTED_XML;
+extern const QName QN_XSTREAM_SEE_OTHER_HOST;
+extern const QName QN_XSTREAM_SYSTEM_SHUTDOWN;
+extern const QName QN_XSTREAM_UNDEFINED_CONDITION;
+extern const QName QN_XSTREAM_UNSUPPORTED_ENCODING;
+extern const QName QN_XSTREAM_UNSUPPORTED_STANZA_TYPE;
+extern const QName QN_XSTREAM_UNSUPPORTED_VERSION;
+extern const QName QN_XSTREAM_XML_NOT_WELL_FORMED;
+extern const QName QN_XSTREAM_TEXT;
+
+extern const QName QN_TLS_STARTTLS;
+extern const QName QN_TLS_REQUIRED;
+extern const QName QN_TLS_PROCEED;
+extern const QName QN_TLS_FAILURE;
+
+extern const QName QN_SASL_MECHANISMS;
+extern const QName QN_SASL_MECHANISM;
+extern const QName QN_SASL_AUTH;
+extern const QName QN_SASL_CHALLENGE;
+extern const QName QN_SASL_RESPONSE;
+extern const QName QN_SASL_ABORT;
+extern const QName QN_SASL_SUCCESS;
+extern const QName QN_SASL_FAILURE;
+extern const QName QN_SASL_ABORTED;
+extern const QName QN_SASL_INCORRECT_ENCODING;
+extern const QName QN_SASL_INVALID_AUTHZID;
+extern const QName QN_SASL_INVALID_MECHANISM;
+extern const QName QN_SASL_MECHANISM_TOO_WEAK;
+extern const QName QN_SASL_NOT_AUTHORIZED;
+extern const QName QN_SASL_TEMPORARY_AUTH_FAILURE;
+
+extern const QName QN_DIALBACK_RESULT;
+extern const QName QN_DIALBACK_VERIFY;
+
+extern const QName QN_STANZA_BAD_REQUEST;
+extern const QName QN_STANZA_CONFLICT;
+extern const QName QN_STANZA_FEATURE_NOT_IMPLEMENTED;
+extern const QName QN_STANZA_FORBIDDEN;
+extern const QName QN_STANZA_GONE;
+extern const QName QN_STANZA_INTERNAL_SERVER_ERROR;
+extern const QName QN_STANZA_ITEM_NOT_FOUND;
+extern const QName QN_STANZA_JID_MALFORMED;
+extern const QName QN_STANZA_NOT_ACCEPTABLE;
+extern const QName QN_STANZA_NOT_ALLOWED;
+extern const QName QN_STANZA_PAYMENT_REQUIRED;
+extern const QName QN_STANZA_RECIPIENT_UNAVAILABLE;
+extern const QName QN_STANZA_REDIRECT;
+extern const QName QN_STANZA_REGISTRATION_REQUIRED;
+extern const QName QN_STANZA_REMOTE_SERVER_NOT_FOUND;
+extern const QName QN_STANZA_REMOTE_SERVER_TIMEOUT;
+extern const QName QN_STANZA_RESOURCE_CONSTRAINT;
+extern const QName QN_STANZA_SERVICE_UNAVAILABLE;
+extern const QName QN_STANZA_SUBSCRIPTION_REQUIRED;
+extern const QName QN_STANZA_UNDEFINED_CONDITION;
+extern const QName QN_STANZA_UNEXPECTED_REQUEST;
+extern const QName QN_STANZA_TEXT;
+
+extern const QName QN_BIND_BIND;
+extern const QName QN_BIND_RESOURCE;
+extern const QName QN_BIND_JID;
+
+extern const QName QN_MESSAGE;
+extern const QName QN_BODY;
+extern const QName QN_SUBJECT;
+extern const QName QN_THREAD;
+extern const QName QN_PRESENCE;
+extern const QName QN_SHOW;
+extern const QName QN_STATUS;
+extern const QName QN_LANG;
+extern const QName QN_PRIORITY;
+extern const QName QN_IQ;
+extern const QName QN_ERROR;
+
+extern const QName QN_SERVER_MESSAGE;
+extern const QName QN_SERVER_BODY;
+extern const QName QN_SERVER_SUBJECT;
+extern const QName QN_SERVER_THREAD;
+extern const QName QN_SERVER_PRESENCE;
+extern const QName QN_SERVER_SHOW;
+extern const QName QN_SERVER_STATUS;
+extern const QName QN_SERVER_LANG;
+extern const QName QN_SERVER_PRIORITY;
+extern const QName QN_SERVER_IQ;
+extern const QName QN_SERVER_ERROR;
+
+extern const QName QN_SESSION_SESSION;
+
+extern const QName QN_PRIVACY_QUERY;
+extern const QName QN_PRIVACY_ACTIVE;
+extern const QName QN_PRIVACY_DEFAULT;
+extern const QName QN_PRIVACY_LIST;
+extern const QName QN_PRIVACY_ITEM;
+extern const QName QN_PRIVACY_IQ;
+extern const QName QN_PRIVACY_MESSAGE;
+extern const QName QN_PRIVACY_PRESENCE_IN;
+extern const QName QN_PRIVACY_PRESENCE_OUT;
+
+extern const QName QN_ROSTER_QUERY;
+extern const QName QN_ROSTER_ITEM;
+extern const QName QN_ROSTER_GROUP;
+
+extern const QName QN_VCARD_QUERY;
+extern const QName QN_VCARD_FN;
+
+extern const QName QN_XML_LANG;
+
+extern const QName QN_ENCODING;
+extern const QName QN_VERSION;
+extern const QName QN_TO;
+extern const QName QN_FROM;
+extern const QName QN_TYPE;
+extern const QName QN_ID;
+extern const QName QN_CODE;
+extern const QName QN_NAME;
+extern const QName QN_VALUE;
+extern const QName QN_ACTION;
+extern const QName QN_ORDER;
+extern const QName QN_MECHANISM;
+extern const QName QN_ASK;
+extern const QName QN_JID;
+extern const QName QN_SUBSCRIPTION;
+
+
+extern const QName QN_XMLNS_CLIENT;
+extern const QName QN_XMLNS_SERVER;
+extern const QName QN_XMLNS_STREAM;
+
+// Presence
+extern const std::string STR_SHOW_AWAY;
+extern const std::string STR_SHOW_CHAT;
+extern const std::string STR_SHOW_DND;
+extern const std::string STR_SHOW_XA;
+
+// Subscription
+extern const std::string STR_SUBSCRIBE;
+extern const std::string STR_SUBSCRIBED;
+extern const std::string STR_UNSUBSCRIBE;
+extern const std::string STR_UNSUBSCRIBED;
+
+
+// JEP 0030
+extern const QName QN_NODE;
+extern const QName QN_CATEGORY;
+extern const QName QN_VAR;
+extern const std::string NS_DISCO_INFO;
+extern const std::string NS_DISCO_ITEMS;
+
+extern const QName QN_DISCO_INFO_QUERY;
+extern const QName QN_DISCO_IDENTITY;
+extern const QName QN_DISCO_FEATURE;
+
+extern const QName QN_DISCO_ITEMS_QUERY;
+extern const QName QN_DISCO_ITEM;
+
+
+// JEP 0115
+extern const std::string NS_CAPS;
+extern const QName QN_CAPS_C;
+extern const QName QN_VER;
+extern const QName QN_EXT;
+
+
+// JEP 0091 Delayed Delivery
+extern const std::string kNSDelay;
+extern const QName kQnDelayX;
+extern const QName kQnStamp;
+
+}
+
+#endif // _CRICKET_XMPP_XMPPLIB_BUZZ_CONSTANTS_H_
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc
new file mode 100644
index 00000000..b742e03a
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.cc
@@ -0,0 +1,477 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+extern "C" {
+#include <ctype.h>
+}
+#include <string>
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/constants.h"
+#include "talk/base/common.h"
+#include <algorithm>
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+static int AsciiToLower(int x) {
+ return (x <= 'Z' && x >= 'A') ? (x + ('a' - 'A')) : x;
+}
+
+Jid::Jid() : data_(NULL) {
+}
+
+Jid::Jid(bool is_special, const std::string & special) {
+ data_ = is_special ? new Data(special, STR_EMPTY, STR_EMPTY) : NULL;
+}
+
+Jid::Jid(const std::string & jid_string) {
+ if (jid_string == STR_EMPTY) {
+ data_ = NULL;
+ return;
+ }
+
+ // First find the slash and slice of that part
+ size_t slash = jid_string.find('/');
+ std::string resource_name = (slash == std::string::npos ? STR_EMPTY :
+ jid_string.substr(slash + 1));
+
+ // Now look for the node
+ std::string node_name;
+ size_t at = jid_string.find('@');
+ size_t domain_begin;
+ if (at < slash && at != std::string::npos) {
+ node_name = jid_string.substr(0, at);
+ domain_begin = at + 1;
+ } else {
+ domain_begin = 0;
+ }
+
+ // Now take what is left as the domain
+ size_t domain_length =
+ ( slash == std::string::npos
+ ? jid_string.length() - domain_begin
+ : slash - domain_begin);
+
+ // avoid allocating these constants repeatedly
+ std::string domain_name;
+
+ if (domain_length == 9 && jid_string.find("gmail.com", domain_begin) == domain_begin) {
+ domain_name = STR_GMAIL_COM;
+ }
+ else if (domain_length == 14 && jid_string.find("googlemail.com", domain_begin) == domain_begin) {
+ domain_name = STR_GOOGLEMAIL_COM;
+ }
+ else if (domain_length == 10 && jid_string.find("google.com", domain_begin) == domain_begin) {
+ domain_name = STR_GOOGLE_COM;
+ }
+ else {
+ domain_name = jid_string.substr(domain_begin, domain_length);
+ }
+
+ // If the domain is empty we have a non-valid jid and we should empty
+ // everything else out
+ if (domain_name.empty()) {
+ data_ = NULL;
+ return;
+ }
+
+ bool valid_node;
+ std::string validated_node = prepNode(node_name,
+ node_name.begin(), node_name.end(), &valid_node);
+ bool valid_domain;
+ std::string validated_domain = prepDomain(domain_name,
+ domain_name.begin(), domain_name.end(), &valid_domain);
+ bool valid_resource;
+ std::string validated_resource = prepResource(resource_name,
+ resource_name.begin(), resource_name.end(), &valid_resource);
+
+ if (!valid_node || !valid_domain || !valid_resource) {
+ data_ = NULL;
+ return;
+ }
+
+ data_ = new Data(validated_node, validated_domain, validated_resource);
+}
+
+Jid::Jid(const std::string & node_name,
+ const std::string & domain_name,
+ const std::string & resource_name) {
+ if (domain_name.empty()) {
+ data_ = NULL;
+ return;
+ }
+
+ bool valid_node;
+ std::string validated_node = prepNode(node_name,
+ node_name.begin(), node_name.end(), &valid_node);
+ bool valid_domain;
+ std::string validated_domain = prepDomain(domain_name,
+ domain_name.begin(), domain_name.end(), &valid_domain);
+ bool valid_resource;
+ std::string validated_resource = prepResource(resource_name,
+ resource_name.begin(), resource_name.end(), &valid_resource);
+
+ if (!valid_node || !valid_domain || !valid_resource) {
+ data_ = NULL;
+ return;
+ }
+
+ data_ = new Data(validated_node, validated_domain, validated_resource);
+}
+
+std::string Jid::Str() const {
+ if (!IsValid())
+ return STR_EMPTY;
+
+ std::string ret;
+
+ if (!data_->node_name_.empty())
+ ret = data_->node_name_ + "@";
+
+ ASSERT(data_->domain_name_ != STR_EMPTY);
+ ret += data_->domain_name_;
+
+ if (!data_->resource_name_.empty())
+ ret += "/" + data_->resource_name_;
+
+ return ret;
+}
+
+bool
+Jid::IsValid() const {
+ return data_ != NULL && !data_->domain_name_.empty();
+}
+
+bool
+Jid::IsBare() const {
+ return IsValid() &&
+ data_->resource_name_.empty();
+}
+
+bool
+Jid::IsFull() const {
+ return IsValid() &&
+ !data_->resource_name_.empty();
+}
+
+Jid
+Jid::BareJid() const {
+ if (!IsValid())
+ return Jid();
+ if (!IsFull())
+ return *this;
+ return Jid(data_->node_name_, data_->domain_name_, STR_EMPTY);
+}
+
+#if 0
+void
+Jid::set_node(const std::string & node_name) {
+ data_->node_name_ = node_name;
+}
+void
+Jid::set_domain(const std::string & domain_name) {
+ data_->domain_name_ = domain_name;
+}
+void
+Jid::set_resource(const std::string & res_name) {
+ data_->resource_name_ = res_name;
+}
+#endif
+
+bool
+Jid::BareEquals(const Jid & other) const {
+ return (other.data_ == data_ ||
+ data_ != NULL &&
+ other.data_ != NULL &&
+ other.data_->node_name_ == data_->node_name_ &&
+ other.data_->domain_name_ == data_->domain_name_);
+}
+
+bool
+Jid::operator==(const Jid & other) const {
+ return (other.data_ == data_ ||
+ data_ != NULL &&
+ other.data_ != NULL &&
+ other.data_->node_name_ == data_->node_name_ &&
+ other.data_->domain_name_ == data_->domain_name_ &&
+ other.data_->resource_name_ == data_->resource_name_);
+}
+
+int
+Jid::Compare(const Jid & other) const {
+ if (other.data_ == data_)
+ return 0;
+ if (data_ == NULL)
+ return -1;
+ if (other.data_ == NULL)
+ return 1;
+
+ int compare_result;
+ compare_result = data_->node_name_.compare(other.data_->node_name_);
+ if (0 != compare_result)
+ return compare_result;
+ compare_result = data_->domain_name_.compare(other.data_->domain_name_);
+ if (0 != compare_result)
+ return compare_result;
+ compare_result = data_->resource_name_.compare(other.data_->resource_name_);
+ return compare_result;
+}
+
+
+// --- JID parsing code: ---
+
+// Checks and normalizes the node part of a JID.
+std::string
+Jid::prepNode(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ char ch = *i;
+ if (ch <= 0x7F) {
+ result += prepNodeAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement the correct stringprep protocol for these
+ result += tolower(ch);
+ }
+ if (!char_valid) {
+ return STR_EMPTY;
+ }
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+
+// Returns the appropriate mapping for an ASCII character in a node.
+char
+Jid::prepNodeAscii(char ch, bool *valid) {
+ *valid = true;
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ return (char)(ch + ('a' - 'A'));
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case ' ': case '&': case '/': case ':': case '<': case '>': case '@':
+ case '\"': case '\'':
+ case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+
+// Checks and normalizes the resource part of a JID.
+std::string
+Jid::prepResource(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ char ch = *i;
+ if (ch <= 0x7F) {
+ result += prepResourceAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement the correct stringprep protocol for these
+ result += ch;
+ }
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+// Returns the appropriate mapping for an ASCII character in a resource.
+char
+Jid::prepResourceAscii(char ch, bool *valid) {
+ *valid = true;
+ switch (ch) {
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+// Checks and normalizes the domain part of a JID.
+std::string
+Jid::prepDomain(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, bool *valid) {
+ *valid = false;
+ std::string result;
+
+ // TODO: if the domain contains a ':', then we should parse it
+ // as an IPv6 address rather than giving an error about illegal domain.
+ prepDomain(str, start, end, &result, valid);
+ if (!*valid) {
+ return STR_EMPTY;
+ }
+
+ if (result.length() > 1023) {
+ return STR_EMPTY;
+ }
+ *valid = true;
+ return result;
+}
+
+
+// Checks and normalizes an IDNA domain.
+void
+Jid::prepDomain(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, std::string *buf, bool *valid) {
+ *valid = false;
+ std::string::const_iterator last = start;
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool label_valid = true;
+ char ch = *i;
+ switch (ch) {
+ case 0x002E:
+#if 0 // FIX: This isn't UTF-8-aware.
+ case 0x3002:
+ case 0xFF0E:
+ case 0xFF61:
+#endif
+ prepDomainLabel(str, last, i, buf, &label_valid);
+ *buf += '.';
+ last = i + 1;
+ break;
+ }
+ if (!label_valid) {
+ return;
+ }
+ }
+ prepDomainLabel(str, last, end, buf, valid);
+}
+
+// Checks and normalizes a domain label.
+void
+Jid::prepDomainLabel(const std::string str, std::string::const_iterator start,
+ std::string::const_iterator end, std::string *buf, bool *valid) {
+ *valid = false;
+
+ int startLen = buf->length();
+ for (std::string::const_iterator i = start; i < end; i++) {
+ bool char_valid = true;
+ char ch = *i;
+ if (ch <= 0x7F) {
+ *buf += prepDomainLabelAscii(ch, &char_valid);
+ }
+ else {
+ // TODO: implement ToASCII for these
+ *buf += ch;
+ }
+ if (!char_valid) {
+ return;
+ }
+ }
+
+ int count = buf->length() - startLen;
+ if (count == 0) {
+ return;
+ }
+ else if (count > 63) {
+ return;
+ }
+
+ // Is this check needed? See comment in prepDomainLabelAscii.
+ if ((*buf)[startLen] == '-') {
+ return;
+ }
+ if ((*buf)[buf->length() - 1] == '-') {
+ return;
+ }
+ *valid = true;
+}
+
+
+// Returns the appropriate mapping for an ASCII character in a domain label.
+char
+Jid::prepDomainLabelAscii(char ch, bool *valid) {
+ *valid = true;
+ // TODO: A literal reading of the spec seems to say that we do
+ // not need to check for these illegal characters (an "internationalized
+ // domain label" runs ToASCII with UseSTD3... set to false). But that
+ // can't be right. We should at least be checking that there are no '/'
+ // or '@' characters in the domain. Perhaps we should see what others
+ // do in this case.
+
+ switch (ch) {
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
+ case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
+ case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'V': case 'W': case 'X': case 'Y': case 'Z':
+ return (char)(ch + ('a' - 'A'));
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
+ case 0x06: case 0x07: case 0x08: case 0x09: case 0x0A: case 0x0B:
+ case 0x0C: case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11:
+ case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
+ case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D:
+ case 0x1E: case 0x1F: case 0x20: case 0x21: case 0x22: case 0x23:
+ case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29:
+ case 0x2A: case 0x2B: case 0x2C: case 0x2E: case 0x2F: case 0x3A:
+ case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: case 0x40:
+ case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F: case 0x60:
+ case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
+ *valid = false;
+ return 0;
+
+ default:
+ return ch;
+ }
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h
new file mode 100644
index 00000000..ae7944bf
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/jid.h
@@ -0,0 +1,144 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _jid_h_
+#define _jid_h_
+
+#include <string>
+#include "talk/xmllite/xmlconstants.h"
+
+namespace buzz {
+
+//! The Jid class encapsulates and provides parsing help for Jids
+//! A Jid consists of three parts. The node, the domain and the resource.
+//!
+//! node@domain/resource
+//!
+//! The node and resource are both optional. A valid jid is defined to have
+//! a domain. A bare jid is defined to not have a resource and a full jid
+//! *does* have a resource.
+class Jid {
+public:
+ explicit Jid();
+ explicit Jid(const std::string & jid_string);
+ explicit Jid(const std::string & node_name,
+ const std::string & domain_name,
+ const std::string & resource_name);
+ explicit Jid(bool special, const std::string & special_string);
+ Jid(const Jid & jid) : data_(jid.data_) {
+ if (data_ != NULL) {
+ data_->AddRef();
+ }
+ }
+ Jid & operator=(const Jid & jid) {
+ if (jid.data_ != NULL) {
+ jid.data_->AddRef();
+ }
+ if (data_ != NULL) {
+ data_->Release();
+ }
+ data_ = jid.data_;
+ return *this;
+ }
+ ~Jid() {
+ if (data_ != NULL) {
+ data_->Release();
+ }
+ }
+
+
+ const std::string & node() const { return !data_ ? STR_EMPTY : data_->node_name_; }
+ // void set_node(const std::string & node_name);
+ const std::string & domain() const { return !data_ ? STR_EMPTY : data_->domain_name_; }
+ // void set_domain(const std::string & domain_name);
+ const std::string & resource() const { return !data_ ? STR_EMPTY : data_->resource_name_; }
+ // void set_resource(const std::string & res_name);
+
+ std::string Str() const;
+ Jid BareJid() const;
+
+ bool IsValid() const;
+ bool IsBare() const;
+ bool IsFull() const;
+
+ bool BareEquals(const Jid & other) const;
+
+ bool operator==(const Jid & other) const;
+ bool operator!=(const Jid & other) const { return !operator==(other); }
+
+ bool operator<(const Jid & other) const { return Compare(other) < 0; };
+ bool operator>(const Jid & other) const { return Compare(other) > 0; };
+
+ int Compare(const Jid & other) const;
+
+
+private:
+
+ static std::string prepNode(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static char prepNodeAscii(char ch, bool *valid);
+ static std::string prepResource(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static char prepResourceAscii(char ch, bool *valid);
+ static std::string prepDomain(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ bool *valid);
+ static void prepDomain(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ std::string *buf, bool *valid);
+ static void prepDomainLabel(const std::string str,
+ std::string::const_iterator start, std::string::const_iterator end,
+ std::string *buf, bool *valid);
+ static char prepDomainLabelAscii(char ch, bool *valid);
+
+ class Data {
+ public:
+ Data() : refcount_(1) {}
+ Data(const std::string & node, const std::string &domain, const std::string & resource) :
+ node_name_(node),
+ domain_name_(domain),
+ resource_name_(resource),
+ refcount_(1) {}
+ const std::string node_name_;
+ const std::string domain_name_;
+ const std::string resource_name_;
+
+ void AddRef() { refcount_++; }
+ void Release() { if (!--refcount_) delete this; }
+ private:
+ int refcount_;
+ };
+
+ Data * data_;
+};
+
+}
+
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h
new file mode 100644
index 00000000..659820f5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/plainsaslhandler.h
@@ -0,0 +1,80 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PLAINSASLHANDLER_H_
+#define _PLAINSASLHANDLER_H_
+
+#include "talk/xmpp/saslhandler.h"
+#include <algorithm>
+
+namespace buzz {
+
+class PlainSaslHandler : public SaslHandler {
+public:
+ PlainSaslHandler(const Jid & jid, const XmppPassword & password) :
+ jid_(jid), password_(password) {}
+
+ virtual ~PlainSaslHandler() {}
+
+ // Should pick the best method according to this handler
+ // returns the empty string if none are suitable
+ virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) {
+
+ // Do not send @google.com passwords unencrypted
+ if (!encrypted && jid_.domain() == "google.com") {
+ return "";
+ }
+
+ std::vector<std::string>::const_iterator it = std::find(mechanisms.begin(), mechanisms.end(), "PLAIN");
+ if (it == mechanisms.end()) {
+ return "";
+ }
+ else {
+ return "PLAIN";
+ }
+ }
+
+ // Creates a SaslMechanism for the given mechanism name (you own it
+ // once you get it). If not handled, return NULL.
+ virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) {
+ if (mechanism == "PLAIN") {
+ return new SaslPlainMechanism(jid_, password_);
+ }
+ return NULL;
+ }
+
+private:
+ Jid jid_;
+ XmppPassword password_;
+
+};
+
+
+}
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h
new file mode 100644
index 00000000..8d2aa9d4
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/prexmppauth.h
@@ -0,0 +1,85 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PREXMPPAUTH_H_
+#define _PREXMPPAUTH_H_
+
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/saslhandler.h"
+#include "talk/xmpp/xmpppassword.h"
+
+namespace cricket {
+ class SocketAddress;
+}
+
+namespace buzz {
+
+class Jid;
+class SaslMechanism;
+
+class CaptchaChallenge {
+ public:
+ CaptchaChallenge() : captcha_needed_(false) {}
+ CaptchaChallenge(const std::string& token, const std::string& url)
+ : captcha_needed_(true), captcha_token_(token), captcha_image_url_(url) {
+ }
+
+ bool captcha_needed() const { return captcha_needed_; }
+ const std::string& captcha_token() const { return captcha_token_; }
+
+ // This url is relative to the gaia server. Once we have better tools
+ // for cracking URLs, we should probably make this a full URL
+ const std::string& captcha_image_url() const { return captcha_image_url_; }
+
+ private:
+ bool captcha_needed_;
+ std::string captcha_token_;
+ std::string captcha_image_url_;
+};
+
+class PreXmppAuth : public SaslHandler {
+public:
+ virtual ~PreXmppAuth() {}
+
+ virtual void StartPreXmppAuth(
+ const Jid & jid,
+ const cricket::SocketAddress & server,
+ const XmppPassword & pass,
+ const std::string & auth_cookie) = 0;
+
+ sigslot::signal0<> SignalAuthDone;
+
+ virtual bool IsAuthDone() = 0;
+ virtual bool IsAuthorized() = 0;
+ virtual bool HadError() = 0;
+ virtual CaptchaChallenge GetCaptchaChallenge() = 0;
+ virtual std::string GetAuthCookie() = 0;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h
new file mode 100644
index 00000000..a6630d90
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslcookiemechanism.h
@@ -0,0 +1,67 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLCOOKIEMECHANISM_H_
+#define _SASLCOOKIEMECHANISM_H_
+
+#include "talk/xmpp/saslmechanism.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+class SaslCookieMechanism : public SaslMechanism {
+
+public:
+ SaslCookieMechanism(const std::string & mechanism, const std::string & username, const std::string & cookie) :
+ mechanism_(mechanism), username_(username), cookie_(cookie) {}
+
+ virtual std::string GetMechanismName() { return mechanism_; }
+
+ virtual XmlElement * StartSaslAuth() {
+ // send initial request
+ XmlElement * el = new XmlElement(QN_SASL_AUTH, true);
+ el->AddAttr(QN_MECHANISM, mechanism_);
+
+ std::string credential;
+ credential.append("\0", 1);
+ credential.append(username_);
+ credential.append("\0", 1);
+ credential.append(cookie_);
+ el->AddText(Base64Encode(credential));
+ return el;
+ }
+
+private:
+ std::string mechanism_;
+ std::string username_;
+ std::string cookie_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h
new file mode 100644
index 00000000..b57d3baf
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslhandler.h
@@ -0,0 +1,59 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLHANDLER_H_
+#define _SASLHANDLER_H_
+
+#include <string>
+
+namespace buzz {
+
+class XmlElement;
+class SaslMechanism;
+
+// Creates mechanisms to deal with a given mechanism
+class SaslHandler {
+
+public:
+
+ // Intended to be subclassed
+ virtual ~SaslHandler() {}
+
+ // Should pick the best method according to this handler
+ // returns the empty string if none are suitable
+ virtual std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) = 0;
+
+ // Creates a SaslMechanism for the given mechanism name (you own it
+ // once you get it).
+ // If not handled, return NULL.
+ virtual SaslMechanism * CreateSaslMechanism(const std::string & mechanism) = 0;
+};
+
+}
+
+#endif
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc
new file mode 100644
index 00000000..092df104
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.cc
@@ -0,0 +1,68 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/base/base64.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmpp/saslmechanism.h"
+
+namespace buzz {
+
+XmlElement *
+SaslMechanism::StartSaslAuth() {
+ return new XmlElement(QN_SASL_AUTH, true);
+}
+
+XmlElement *
+SaslMechanism::HandleSaslChallenge(const XmlElement * challenge) {
+ return new XmlElement(QN_SASL_ABORT, true);
+}
+
+void
+SaslMechanism::HandleSaslSuccess(const XmlElement * success) {
+}
+
+void
+SaslMechanism::HandleSaslFailure(const XmlElement * failure) {
+}
+
+std::string
+SaslMechanism::Base64Encode(const std::string & plain) {
+ return Base64::encode(plain);
+}
+
+std::string
+SaslMechanism::Base64Decode(const std::string & encoded) {
+ return Base64::decode(encoded);
+}
+
+std::string
+SaslMechanism::Base64EncodeFromArray(const char * plain, size_t length) {
+ return Base64::encodeFromArray(plain, length);
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h
new file mode 100644
index 00000000..f2e5adce
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslmechanism.h
@@ -0,0 +1,74 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLMECHANISM_H_
+#define _SASLMECHANISM_H_
+
+#include <string>
+
+namespace buzz {
+
+class XmlElement;
+
+
+// Defines a mechnanism to do SASL authentication.
+// Subclass instances should have a self-contained way to present
+// credentials.
+class SaslMechanism {
+
+public:
+
+ // Intended to be subclassed
+ virtual ~SaslMechanism() {}
+
+ // Should return the name of the SASL mechanism, e.g., "PLAIN"
+ virtual std::string GetMechanismName() = 0;
+
+ // Should generate the initial "auth" request. Default is just <auth/>.
+ virtual XmlElement * StartSaslAuth();
+
+ // Should respond to a SASL "<challenge>" request. Default is
+ // to abort (for mechanisms that do not do challenge-response)
+ virtual XmlElement * HandleSaslChallenge(const XmlElement * challenge);
+
+ // Notification of a SASL "<success>". Sometimes information
+ // is passed on success.
+ virtual void HandleSaslSuccess(const XmlElement * success);
+
+ // Notification of a SASL "<failure>". Sometimes information
+ // for the user is passed on failure.
+ virtual void HandleSaslFailure(const XmlElement * failure);
+
+protected:
+ static std::string Base64Encode(const std::string & plain);
+ static std::string Base64Decode(const std::string & encoded);
+ static std::string Base64EncodeFromArray(const char * plain, size_t length);
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h
new file mode 100644
index 00000000..7e0b0562
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/saslplainmechanism.h
@@ -0,0 +1,65 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SASLPLAINMECHANISM_H_
+#define _SASLPLAINMECHANISM_H_
+
+#include "talk/xmpp/saslmechanism.h"
+#include "talk/xmpp/xmpppassword.h"
+
+namespace buzz {
+
+class SaslPlainMechanism : public SaslMechanism {
+
+public:
+ SaslPlainMechanism(const buzz::Jid user_jid, const XmppPassword & password) :
+ user_jid_(user_jid), password_(password) {}
+
+ virtual std::string GetMechanismName() { return "PLAIN"; }
+
+ virtual XmlElement * StartSaslAuth() {
+ // send initial request
+ XmlElement * el = new XmlElement(QN_SASL_AUTH, true);
+ el->AddAttr(QN_MECHANISM, "PLAIN");
+
+ FormatXmppPassword credential;
+ credential.Append("\0", 1);
+ credential.Append(user_jid_.Str());
+ credential.Append("\0", 1);
+ credential.Append(&password_);
+ el->AddText(Base64EncodeFromArray(credential.GetData(), credential.GetLength()));
+ return el;
+ }
+
+private:
+ Jid user_jid_;
+ XmppPassword password_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc
new file mode 100644
index 00000000..959b6f88
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.cc
@@ -0,0 +1,372 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xmppclient.h"
+#include "xmpptask.h"
+#include "talk/xmpp/constants.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/saslplainmechanism.h"
+#include "talk/xmpp/prexmppauth.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmpp/plainsaslhandler.h"
+
+namespace buzz {
+
+Task *
+XmppClient::GetParent(int code) {
+ if (code == XMPP_CLIENT_TASK_CODE)
+ return this;
+ else
+ return Task::GetParent(code);
+}
+
+class XmppClient::Private :
+ public sigslot::has_slots<>,
+ public XmppSessionHandler,
+ public XmppOutputHandler {
+public:
+
+ Private(XmppClient * client) :
+ client_(client),
+ socket_(NULL),
+ engine_(NULL),
+ proxy_port_(0),
+ pre_engine_error_(XmppEngine::ERROR_NONE),
+ signal_closed_(false) {}
+
+ // the owner
+ XmppClient * const client_;
+
+ // the two main objects
+ scoped_ptr<AsyncSocket> socket_;
+ scoped_ptr<XmppEngine> engine_;
+ scoped_ptr<PreXmppAuth> pre_auth_;
+ XmppPassword pass_;
+ std::string auth_cookie_;
+ cricket::SocketAddress server_;
+ std::string proxy_host_;
+ int proxy_port_;
+ XmppEngine::Error pre_engine_error_;
+ CaptchaChallenge captcha_challenge_;
+ bool signal_closed_;
+
+ // implementations of interfaces
+ void OnStateChange(int state);
+ void WriteOutput(const char * bytes, size_t len);
+ void StartTls(const std::string & domainname);
+ void CloseConnection();
+
+ // slots for socket signals
+ void OnSocketConnected();
+ void OnSocketRead();
+ void OnSocketClosed();
+};
+
+XmppReturnStatus
+XmppClient::Connect(const XmppClientSettings & settings, AsyncSocket * socket, PreXmppAuth * pre_auth) {
+ if (socket == NULL)
+ return XMPP_RETURN_BADARGUMENT;
+ if (d_->socket_.get() != NULL)
+ return XMPP_RETURN_BADSTATE;
+
+ d_->socket_.reset(socket);
+
+ d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected);
+ d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead);
+ d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed);
+
+ d_->engine_.reset(XmppEngine::Create());
+ d_->engine_->SetSessionHandler(d_.get());
+ d_->engine_->SetOutputHandler(d_.get());
+ d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY));
+ if (!settings.resource().empty()) {
+ d_->engine_->SetRequestedResource(settings.resource());
+ }
+ d_->engine_->SetUseTls(settings.use_tls());
+
+
+ d_->pass_ = settings.pass();
+ d_->auth_cookie_ = settings.auth_cookie();
+ d_->server_ = settings.server();
+ d_->proxy_host_ = settings.proxy_host();
+ d_->proxy_port_ = settings.proxy_port();
+ d_->pre_auth_.reset(pre_auth);
+
+ return XMPP_RETURN_OK;
+}
+
+XmppEngine::State
+XmppClient::GetState() {
+ if (d_->engine_.get() == NULL)
+ return XmppEngine::STATE_NONE;
+ return d_->engine_->GetState();
+}
+
+XmppEngine::Error
+XmppClient::GetError() {
+ if (d_->engine_.get() == NULL)
+ return XmppEngine::ERROR_NONE;
+ if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE)
+ return d_->pre_engine_error_;
+ return d_->engine_->GetError();
+}
+
+CaptchaChallenge XmppClient::GetCaptchaChallenge() {
+ if (d_->engine_.get() == NULL)
+ return CaptchaChallenge();
+ return d_->captcha_challenge_;
+}
+
+std::string
+XmppClient::GetAuthCookie() {
+ if (d_->engine_.get() == NULL)
+ return "";
+ return d_->auth_cookie_;
+}
+
+static void
+ForgetPassword(std::string & to_erase) {
+ size_t len = to_erase.size();
+ for (size_t i = 0; i < len; i++) {
+ // get rid of characters
+ to_erase[i] = 'x';
+ }
+ // get rid of length
+ to_erase.erase();
+}
+
+int
+XmppClient::ProcessStart() {
+ if (d_->pre_auth_.get()) {
+ d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone);
+ d_->pre_auth_->StartPreXmppAuth(
+ d_->engine_->GetUser(), d_->server_, d_->pass_, d_->auth_cookie_);
+ d_->pass_.Clear(); // done with this;
+ return STATE_PRE_XMPP_LOGIN;
+ }
+ else {
+ d_->engine_->SetSaslHandler(new PlainSaslHandler(
+ d_->engine_->GetUser(), d_->pass_));
+ d_->pass_.Clear(); // done with this;
+ return STATE_START_XMPP_LOGIN;
+ }
+}
+
+void
+XmppClient::OnAuthDone() {
+ Wake();
+}
+
+int
+XmppClient::ProcessCookieLogin() {
+ // Don't know how this could happen, but crash reports show it as NULL
+ if (!d_->pre_auth_.get()) {
+ d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
+ EnsureClosed();
+ return STATE_ERROR;
+ }
+
+ // Wait until pre authentication is done is done
+ if (!d_->pre_auth_->IsAuthDone())
+ return STATE_BLOCKED;
+
+ if (!d_->pre_auth_->IsAuthorized()) {
+ // maybe split out a case when gaia is down?
+ if (d_->pre_auth_->HadError()) {
+ d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
+ }
+ else {
+ d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED;
+ d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge();
+ }
+ d_->pre_auth_.reset(NULL); // done with this
+ EnsureClosed();
+ return STATE_ERROR;
+ }
+
+ // Save auth cookie as a result
+ d_->auth_cookie_ = d_->pre_auth_->GetAuthCookie();
+
+ // transfer ownership of pre_auth_ to engine
+ d_->engine_->SetSaslHandler(d_->pre_auth_.release());
+
+ return STATE_START_XMPP_LOGIN;
+}
+
+int
+XmppClient::ProcessStartXmppLogin() {
+ // Done with pre-connect tasks - connect!
+ if (!d_->socket_->Connect(d_->server_)) {
+ EnsureClosed();
+ return STATE_ERROR;
+ }
+
+ return STATE_RESPONSE;
+}
+
+int
+XmppClient::ProcessResponse() {
+ // Hang around while we are connected.
+ if (!delivering_signal_ && (d_->engine_.get() == NULL ||
+ d_->engine_->GetState() == XmppEngine::STATE_CLOSED))
+ return STATE_DONE;
+ return STATE_BLOCKED;
+}
+
+XmppReturnStatus
+XmppClient::Disconnect() {
+ if (d_->socket_.get() == NULL)
+ return XMPP_RETURN_BADSTATE;
+ d_->engine_->Disconnect();
+ return XMPP_RETURN_OK;
+}
+
+XmppClient::XmppClient(Task * parent) : Task(parent),
+ delivering_signal_(false) {
+ d_.reset(new Private(this));
+}
+
+XmppClient::~XmppClient() {}
+
+const Jid &
+XmppClient::jid() {
+ return d_->engine_->FullJid();
+}
+
+
+std::string
+XmppClient::NextId() {
+ return d_->engine_->NextId();
+}
+
+XmppReturnStatus
+XmppClient::SendStanza(const XmlElement * stanza) {
+ return d_->engine_->SendStanza(stanza);
+}
+
+XmppReturnStatus
+XmppClient::SendStanzaError(const XmlElement * old_stanza, XmppStanzaError xse, const std::string & message) {
+ return d_->engine_->SendStanzaError(old_stanza, xse, message);
+}
+
+XmppReturnStatus
+XmppClient::SendRaw(const std::string & text) {
+ return d_->engine_->SendRaw(text);
+}
+
+XmppEngine*
+XmppClient::engine() {
+ return d_->engine_.get();
+}
+
+void
+XmppClient::Private::OnSocketConnected() {
+ engine_->Connect();
+}
+
+void
+XmppClient::Private::OnSocketRead() {
+ char bytes[4096];
+ size_t bytes_read;
+ for (;;) {
+ if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) {
+ // TODO: deal with error information
+ return;
+ }
+
+ if (bytes_read == 0)
+ return;
+
+//#ifdef _DEBUG
+ client_->SignalLogInput(bytes, bytes_read);
+//#endif
+
+ engine_->HandleInput(bytes, bytes_read);
+ }
+}
+
+void
+XmppClient::Private::OnSocketClosed() {
+ engine_->ConnectionClosed();
+}
+
+void
+XmppClient::Private::OnStateChange(int state) {
+ if (state == XmppEngine::STATE_CLOSED) {
+ client_->EnsureClosed();
+ }
+ else {
+ client_->SignalStateChange((XmppEngine::State)state);
+ }
+ client_->Wake();
+}
+
+void
+XmppClient::Private::WriteOutput(const char * bytes, size_t len) {
+
+//#ifdef _DEBUG
+ client_->SignalLogOutput(bytes, len);
+//#endif
+
+ socket_->Write(bytes, len);
+ // TODO: deal with error information
+}
+
+void
+XmppClient::Private::StartTls(const std::string & domain) {
+#if defined(FEATURE_ENABLE_SSL)
+ socket_->StartTls(domain);
+#endif
+}
+
+void
+XmppClient::Private::CloseConnection() {
+ socket_->Close();
+}
+
+void
+XmppClient::AddXmppTask(XmppTask * task, XmppEngine::HandlerLevel level) {
+ d_->engine_->AddStanzaHandler(task, level);
+}
+
+void
+XmppClient::RemoveXmppTask(XmppTask * task) {
+ d_->engine_->RemoveStanzaHandler(task);
+}
+
+void
+XmppClient::EnsureClosed() {
+ if (!d_->signal_closed_) {
+ d_->signal_closed_ = true;
+ delivering_signal_ = true;
+ SignalStateChange(XmppEngine::STATE_CLOSED);
+ delivering_signal_ = false;
+ }
+}
+
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h
new file mode 100644
index 00000000..f8b4798c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclient.h
@@ -0,0 +1,157 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPCLIENT_H_
+#define _XMPPCLIENT_H_
+
+#include <string>
+#include "talk/base/basicdefs.h"
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/asyncsocket.h"
+#include "talk/xmpp/xmppclientsettings.h"
+#include "talk/base/task.h"
+
+namespace buzz {
+
+class XmppTask;
+class PreXmppAuth;
+class CaptchaChallenge;
+
+// Just some non-colliding number. Could have picked "1".
+#define XMPP_CLIENT_TASK_CODE 0x366c1e47
+
+/////////////////////////////////////////////////////////////////////
+//
+// XMPPCLIENT
+//
+/////////////////////////////////////////////////////////////////////
+//
+// See Task first. XmppClient is a parent task for XmppTasks.
+//
+// XmppClient is a task which is designed to be the parent task for
+// all tasks that depend on a single Xmpp connection. If you want to,
+// for example, listen for subscription requests forever, then your
+// listener should be a task that is a child of the XmppClient that owns
+// the connection you are using. XmppClient has all the utility methods
+// that basically drill through to XmppEngine.
+//
+// XmppClient is just a wrapper for XmppEngine, and if I were writing it
+// all over again, I would make XmppClient == XmppEngine. Why?
+// XmppEngine needs tasks too, for example it has an XmppLoginTask which
+// should just be the same kind of Task instead of an XmppEngine specific
+// thing. It would help do certain things like GAIA auth cleaner.
+//
+/////////////////////////////////////////////////////////////////////
+
+class XmppClient : public Task, public sigslot::has_slots<>
+{
+public:
+ XmppClient(Task * parent);
+ ~XmppClient();
+
+ XmppReturnStatus Connect(const XmppClientSettings & settings,
+ AsyncSocket * socket,
+ PreXmppAuth * preauth);
+
+ virtual Task * GetParent(int code);
+ virtual int ProcessStart();
+ virtual int ProcessResponse();
+ XmppReturnStatus Disconnect();
+ const Jid & jid();
+
+ sigslot::signal1<XmppEngine::State> SignalStateChange;
+ XmppEngine::State GetState();
+ XmppEngine::Error GetError();
+
+ // When there is an authentication error, we may have captcha info
+ // that the user can use to unlock their account
+ CaptchaChallenge GetCaptchaChallenge();
+
+ // When authentication is successful, this returns the service cookie
+ // (if we used GAIA authentication)
+ std::string GetAuthCookie();
+
+ std::string NextId();
+ XmppReturnStatus SendStanza(const XmlElement *stanza);
+ XmppReturnStatus SendRaw(const std::string & text);
+ XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal,
+ XmppStanzaError code,
+ const std::string & text);
+
+ XmppEngine* engine();
+
+ sigslot::signal2<const char *, int> SignalLogInput;
+ sigslot::signal2<const char *, int> SignalLogOutput;
+
+private:
+ friend class XmppTask;
+
+ void OnAuthDone();
+
+ // managed tasks and dispatching
+ void AddXmppTask(XmppTask *, XmppEngine::HandlerLevel);
+ void RemoveXmppTask(XmppTask *);
+
+ sigslot::signal0<> SignalDisconnected;
+
+private:
+ // Internal state management
+ enum {
+ STATE_PRE_XMPP_LOGIN = STATE_NEXT,
+ STATE_START_XMPP_LOGIN = STATE_NEXT + 1,
+ };
+ int Process(int state) {
+ switch (state) {
+ case STATE_PRE_XMPP_LOGIN: return ProcessCookieLogin();
+ case STATE_START_XMPP_LOGIN: return ProcessStartXmppLogin();
+ default: return Task::Process(state);
+ }
+ }
+
+ std::string GetStateName(int state) const {
+ switch (state) {
+ case STATE_PRE_XMPP_LOGIN: return "PRE_XMPP_LOGIN";
+ case STATE_START_XMPP_LOGIN: return "START_XMPP_LOGIN";
+ default: return Task::GetStateName(state);
+ }
+ }
+
+ int ProcessCookieLogin();
+ int ProcessStartXmppLogin();
+ void EnsureClosed();
+
+ class Private;
+ friend class Private;
+ scoped_ptr<Private> d_;
+
+ bool delivering_signal_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h
new file mode 100644
index 00000000..9795682b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppclientsettings.h
@@ -0,0 +1,94 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPCLIENTSETTINGS_H_
+#define _XMPPCLIENTSETTINGS_H_
+
+#include "talk/p2p/base/port.h"
+#include "talk/xmpp/xmpppassword.h"
+
+namespace buzz {
+
+class XmppClientSettings {
+public:
+ XmppClientSettings() :
+ use_tls_(false), use_cookie_auth_(false), protocol_(cricket::PROTO_TCP),
+ proxy_(cricket::PROXY_NONE), proxy_port_(80), use_proxy_auth_(false) {}
+
+ void set_user(const std::string & user) { user_ = user; }
+ void set_host(const std::string & host) { host_ = host; }
+ void set_pass(const XmppPassword & pass) { pass_ = pass; }
+ void set_auth_cookie(const std::string & cookie) { auth_cookie_ = cookie; }
+ void set_resource(const std::string & resource) { resource_ = resource; }
+ void set_use_tls(bool use_tls) { use_tls_ = use_tls; }
+ void set_server(const cricket::SocketAddress & server) {
+ server_ = server;
+ }
+ void set_protocol(cricket::ProtocolType protocol) { protocol_ = protocol; }
+ void set_proxy(cricket::ProxyType f) { proxy_ = f; }
+ void set_proxy_host(const std::string & host) { proxy_host_ = host; }
+ void set_proxy_port(int port) { proxy_port_ = port; };
+ void set_use_proxy_auth(bool f) { use_proxy_auth_ = f; }
+ void set_proxy_user(const std::string & user) { proxy_user_ = user; }
+ void set_proxy_pass(const XmppPassword & pass) { proxy_pass_ = pass; }
+
+ const std::string & user() const { return user_; }
+ const std::string & host() const { return host_; }
+ const XmppPassword & pass() const { return pass_; }
+ const std::string & auth_cookie() const { return auth_cookie_; }
+ const std::string & resource() const { return resource_; }
+ bool use_tls() const { return use_tls_; }
+ const cricket::SocketAddress & server() const { return server_; }
+ cricket::ProtocolType protocol() const { return protocol_; }
+ cricket::ProxyType proxy() const { return proxy_; }
+ const std::string & proxy_host() const { return proxy_host_; }
+ int proxy_port() const { return proxy_port_; }
+ bool use_proxy_auth() const { return use_proxy_auth_; }
+ const std::string & proxy_user() const { return proxy_user_; }
+ const XmppPassword & proxy_pass() const { return proxy_pass_; }
+
+private:
+ std::string user_;
+ std::string host_;
+ XmppPassword pass_;
+ std::string auth_cookie_;
+ std::string resource_;
+ bool use_tls_;
+ bool use_cookie_auth_;
+ cricket::SocketAddress server_;
+ cricket::ProtocolType protocol_;
+ cricket::ProxyType proxy_;
+ std::string proxy_host_;
+ int proxy_port_;
+ bool use_proxy_auth_;
+ std::string proxy_user_;
+ XmppPassword proxy_pass_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h
new file mode 100644
index 00000000..ef8f2ea8
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengine.h
@@ -0,0 +1,332 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmppengine_h_
+#define _xmppengine_h_
+
+// also part of the API
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/qname.h"
+#include "talk/xmllite/xmlelement.h"
+
+
+namespace buzz {
+
+class XmppEngine;
+class SaslHandler;
+typedef void * XmppIqCookie;
+
+//! XMPP stanza error codes.
+//! Used in XmppEngine.SendStanzaError().
+enum XmppStanzaError {
+ XSE_BAD_REQUEST,
+ XSE_CONFLICT,
+ XSE_FEATURE_NOT_IMPLEMENTED,
+ XSE_FORBIDDEN,
+ XSE_GONE,
+ XSE_INTERNAL_SERVER_ERROR,
+ XSE_ITEM_NOT_FOUND,
+ XSE_JID_MALFORMED,
+ XSE_NOT_ACCEPTABLE,
+ XSE_NOT_ALLOWED,
+ XSE_PAYMENT_REQUIRED,
+ XSE_RECIPIENT_UNAVAILABLE,
+ XSE_REDIRECT,
+ XSE_REGISTRATION_REQUIRED,
+ XSE_SERVER_NOT_FOUND,
+ XSE_SERVER_TIMEOUT,
+ XSE_RESOURCE_CONSTRAINT,
+ XSE_SERVICE_UNAVAILABLE,
+ XSE_SUBSCRIPTION_REQUIRED,
+ XSE_UNDEFINED_CONDITION,
+ XSE_UNEXPECTED_REQUEST,
+};
+
+// XmppReturnStatus
+// This is used by API functions to synchronously return status.
+enum XmppReturnStatus {
+ XMPP_RETURN_OK,
+ XMPP_RETURN_BADARGUMENT,
+ XMPP_RETURN_BADSTATE,
+ XMPP_RETURN_PENDING,
+ XMPP_RETURN_UNEXPECTED,
+ XMPP_RETURN_NOTYETIMPLEMENTED,
+};
+
+//! Callback for socket output for an XmppEngine connection.
+//! Register via XmppEngine.SetOutputHandler. An XmppEngine
+//! can call back to this handler while it is processing
+//! Connect, SendStanza, SendIq, Disconnect, or HandleInput.
+class XmppOutputHandler {
+public:
+
+ //! Deliver the specified bytes to the XMPP socket.
+ virtual void WriteOutput(const char * bytes, size_t len) = 0;
+
+ //! Initiate TLS encryption on the socket.
+ //! The implementation must verify that the SSL
+ //! certificate matches the given domainname.
+ virtual void StartTls(const std::string & domainname) = 0;
+
+ //! Called when engine wants the connecton closed.
+ virtual void CloseConnection() = 0;
+};
+
+//! Callback to deliver engine state change notifications
+//! to the object managing the engine.
+class XmppSessionHandler {
+public:
+ //! Called when engine changes state. Argument is new state.
+ virtual void OnStateChange(int state) = 0;
+};
+
+//! Callback to deliver stanzas to an Xmpp application module.
+//! Register via XmppEngine.SetDefaultSessionHandler or via
+//! XmppEngine.AddSessionHAndler.
+class XmppStanzaHandler {
+public:
+
+ //! Process the given stanza.
+ //! The handler must return true if it has handled the stanza.
+ //! A false return value causes the stanza to be passed on to
+ //! the next registered handler.
+ virtual bool HandleStanza(const XmlElement * stanza) = 0;
+};
+
+//! Callback to deliver iq responses (results and errors).
+//! Register while sending an iq via XmppEngine.SendIq.
+//! Iq responses are routed to matching XmppIqHandlers in preference
+//! to sending to any registered SessionHandlers.
+class XmppIqHandler {
+public:
+ //! Called to handle the iq response.
+ //! The response may be either a result or an error, and will have
+ //! an 'id' that matches the request and a 'from' that matches the
+ //! 'to' of the request. Called no more than once; once this is
+ //! called, the handler is automatically unregistered.
+ virtual void IqResponse(XmppIqCookie cookie, const XmlElement * pelStanza) = 0;
+};
+
+//! The XMPP connection engine.
+//! This engine implements the client side of the 'core' XMPP protocol.
+//! To use it, register an XmppOutputHandler to handle socket output
+//! and pass socket input to HandleInput. Then application code can
+//! set up the connection with a user, password, and other settings,
+//! and then call Connect() to initiate the connection.
+//! An application can listen for events and receive stanzas by
+//! registering an XmppStanzaHandler via AddStanzaHandler().
+class XmppEngine {
+public:
+ static XmppEngine * Create();
+ virtual ~XmppEngine() {}
+
+ //! Error codes. See GetError().
+ enum Error {
+ ERROR_NONE = 0, //!< No error
+ ERROR_XML, //!< Malformed XML or encoding error
+ ERROR_STREAM, //!< XMPP stream error - see GetStreamError()
+ ERROR_VERSION, //!< XMPP version error
+ ERROR_UNAUTHORIZED, //!< User is not authorized (rejected credentials)
+ ERROR_TLS, //!< TLS could not be negotiated
+ ERROR_AUTH, //!< Authentication could not be negotiated
+ ERROR_BIND, //!< Resource or session binding could not be negotiated
+ ERROR_CONNECTION_CLOSED,//!< Connection closed by output handler.
+ ERROR_DOCUMENT_CLOSED, //!< Closed by </stream:stream>
+ ERROR_SOCKET, //!< Socket error
+ };
+
+ //! States. See GetState().
+ enum State {
+ STATE_NONE = 0, //!< Nonexistent state
+ STATE_START, //!< Initial state.
+ STATE_OPENING, //!< Exchanging stream headers, authenticating and so on.
+ STATE_OPEN, //!< Authenticated and bound.
+ STATE_CLOSED, //!< Session closed, possibly due to error.
+ };
+
+ // SOCKET INPUT AND OUTPUT ------------------------------------------------
+
+ //! Registers the handler for socket output
+ virtual XmppReturnStatus SetOutputHandler(XmppOutputHandler *pxoh) = 0;
+
+ //! Provides socket input to the engine
+ virtual XmppReturnStatus HandleInput(const char * bytes, size_t len) = 0;
+
+ //! Advises the engine that the socket has closed
+ virtual XmppReturnStatus ConnectionClosed() = 0;
+
+ // SESSION SETUP ---------------------------------------------------------
+
+ //! Indicates the (bare) JID for the user to use.
+ virtual XmppReturnStatus SetUser(const Jid & jid)= 0;
+
+ //! Get the login (bare) JID.
+ virtual const Jid & GetUser() = 0;
+
+ //! Provides different methods for credentials for login.
+ //! Takes ownership of this object; deletes when login is done
+ virtual XmppReturnStatus SetSaslHandler(SaslHandler * h) = 0;
+
+ //! Sets whether TLS will be used within the connection (default true).
+ virtual XmppReturnStatus SetUseTls(bool useTls) = 0;
+
+ //! Sets an alternate domain from which we allows TLS certificates.
+ //! This is for use in the case where a we want to allow a proxy to
+ //! serve up its own certificate rather than one owned by the underlying
+ //! domain.
+ virtual XmppReturnStatus SetTlsServerDomain(const std::string & proxy_domain) = 0;
+
+ //! Gets whether TLS will be used within the connection.
+ virtual bool GetUseTls() = 0;
+
+ //! Sets the request resource name, if any (optional).
+ //! Note that the resource name may be overridden by the server; after
+ //! binding, the actual resource name is available as part of FullJid().
+ virtual XmppReturnStatus SetRequestedResource(const std::string& resource) = 0;
+
+ //! Gets the request resource name.
+ virtual const std::string & GetRequestedResource() = 0;
+
+ // SESSION MANAGEMENT ---------------------------------------------------
+
+ //! Set callback for state changes.
+ virtual XmppReturnStatus SetSessionHandler(XmppSessionHandler* handler) = 0;
+
+ //! Initiates the XMPP connection.
+ //! After supplying connection settings, call this once to initiate,
+ //! (optionally) encrypt, authenticate, and bind the connection.
+ virtual XmppReturnStatus Connect() = 0;
+
+ //! The current engine state.
+ virtual State GetState() = 0;
+
+ //! Returns true if the connection is encrypted (under TLS)
+ virtual bool IsEncrypted() = 0;
+
+ //! The error code.
+ //! Consult this after XmppOutputHandler.OnClose().
+ virtual Error GetError() = 0;
+
+ //! The stream:error stanza, when the error is XMPP_ERROR_STREAM.
+ //! Notice the stanza returned is owned by the XmppEngine and
+ //! is deleted when the engine is destroyed.
+ virtual const XmlElement * GetStreamError() = 0;
+
+ //! Closes down the connection.
+ //! Sends CloseConnection to output, and disconnects and registered
+ //! session handlers. After Disconnect completes, it is guaranteed
+ //! that no further callbacks will be made.
+ virtual XmppReturnStatus Disconnect() = 0;
+
+ // APPLICATION USE -------------------------------------------------------
+
+ enum HandlerLevel {
+ HL_NONE = 0,
+ HL_PEEK, //!< Sees messages before all other processing; cannot abort
+ HL_SINGLE, //!< Watches for a single message, e.g., by id and sender
+ HL_SENDER, //!< Watches for a type of message from a specific sender
+ HL_TYPE, //!< Watches a type of message, e.g., all groupchat msgs
+ HL_ALL, //!< Watches all messages - gets last shot
+ HL_COUNT, //!< Count of handler levels
+ };
+
+ //! Adds a listener for session events.
+ //! Stanza delivery is chained to session handlers; the first to
+ //! return 'true' is the last to get each stanza.
+ virtual XmppReturnStatus AddStanzaHandler(XmppStanzaHandler* handler, HandlerLevel level = HL_PEEK) = 0;
+
+ //! Removes a listener for session events.
+ virtual XmppReturnStatus RemoveStanzaHandler(XmppStanzaHandler* handler) = 0;
+
+ //! Sends a stanza to the server.
+ virtual XmppReturnStatus SendStanza(const XmlElement * pelStanza) = 0;
+
+ //! Sends raw text to the server
+ virtual XmppReturnStatus SendRaw(const std::string & text) = 0;
+
+ //! Sends an iq to the server, and registers a callback for the result.
+ //! Returns the cookie passed to the result handler.
+ virtual XmppReturnStatus SendIq(const XmlElement* pelStanza,
+ XmppIqHandler* iq_handler,
+ XmppIqCookie* cookie) = 0;
+
+ //! Unregisters an iq callback handler given its cookie.
+ //! No callback will come to this handler after it's unregistered.
+ virtual XmppReturnStatus RemoveIqHandler(XmppIqCookie cookie,
+ XmppIqHandler** iq_handler) = 0;
+
+
+ //! Forms and sends an error in response to the given stanza.
+ //! Swaps to and from, sets type to "error", and adds error information
+ //! based on the passed code. Text is optional and may be STR_EMPTY.
+ virtual XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal,
+ XmppStanzaError code,
+ const std::string & text) = 0;
+
+ //! The fullly bound JID.
+ //! This JID is only valid after binding has succeeded. If the value
+ //! is JID_NULL, the binding has not succeeded.
+ virtual const Jid & FullJid() = 0;
+
+ //! The next unused iq id for this connection.
+ //! Call this when building iq stanzas, to ensure that each iq
+ //! gets its own unique id.
+ virtual std::string NextId() = 0;
+
+};
+
+}
+
+
+// Move these to a better location
+
+#define XMPP_FAILED(x) \
+ ( (x) == buzz::XMPP_RETURN_OK ? false : true) \
+
+
+#define XMPP_SUCCEEDED(x) \
+ ( (x) == buzz::XMPP_RETURN_OK ? true : false) \
+
+#define IFR(x) \
+ do { \
+ xmpp_status = (x); \
+ if (XMPP_FAILED(xmpp_status)) { \
+ return xmpp_status; \
+ } \
+ } while (false) \
+
+
+#define IFC(x) \
+ do { \
+ xmpp_status = (x); \
+ if (XMPP_FAILED(xmpp_status)) { \
+ goto Cleanup; \
+ } \
+ } while (false) \
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc
new file mode 100644
index 00000000..173d711b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.cc
@@ -0,0 +1,480 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define TRACK_ARRAY_ALLOC_PROBLEM
+
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppengineimpl.h"
+#include "talk/xmpp/xmpplogintask.h"
+#include "talk/xmpp/constants.h"
+#include "talk/xmllite/xmlprinter.h"
+#include "talk/xmpp/saslhandler.h"
+// #include "buzz/saslmechanism.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+static const std::string XMPP_CLIENT_NAMESPACES[] = {
+ "stream", "http://etherx.jabber.org/streams",
+ "", "jabber:client",
+};
+
+static const size_t XMPP_CLIENT_NAMESPACES_LEN = 4;
+
+XmppEngine * XmppEngine::Create() {
+ return new XmppEngineImpl();
+}
+
+
+XmppEngineImpl::XmppEngineImpl() :
+ stanzaParseHandler_(this),
+ stanzaParser_(&stanzaParseHandler_),
+ engine_entered_(0),
+ user_jid_(JID_EMPTY),
+ password_(),
+ requested_resource_(STR_EMPTY),
+ tls_needed_(true),
+ login_task_(new XmppLoginTask(this)),
+ next_id_(0),
+ bound_jid_(JID_EMPTY),
+ state_(STATE_START),
+ encrypted_(false),
+ error_code_(ERROR_NONE),
+ stream_error_(NULL),
+ raised_reset_(false),
+ output_handler_(NULL),
+ session_handler_(NULL),
+ iq_entries_(new IqEntryVector()),
+ output_(new std::stringstream()),
+ sasl_handler_(NULL) {
+ for (int i = 0; i < HL_COUNT; i+= 1) {
+ stanza_handlers_[i].reset(new StanzaHandlerVector());
+ }
+}
+
+XmppEngineImpl::~XmppEngineImpl() {
+ DeleteIqCookies();
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetOutputHandler(XmppOutputHandler* output_handler) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ output_handler_ = output_handler;
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetSessionHandler(XmppSessionHandler* session_handler) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ session_handler_ = session_handler;
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::HandleInput(const char * bytes, size_t len) {
+ if (state_ < STATE_OPENING || state_ > STATE_OPEN)
+ return XMPP_RETURN_BADSTATE;
+
+ EnterExit ee(this);
+
+ stanzaParser_.Parse(bytes, len, false);
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::ConnectionClosed() {
+ if (state_ != STATE_CLOSED) {
+ EnterExit ee(this);
+ // If told that connection closed and not already closed,
+ // then connection was unpexectedly dropped.
+ SignalError(ERROR_CONNECTION_CLOSED);
+ }
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetUseTls(bool useTls) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ tls_needed_ = useTls;
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetTlsServerDomain(const std::string & tls_server_domain) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ tls_server_domain_= tls_server_domain;
+
+ return XMPP_RETURN_OK;
+}
+
+bool
+XmppEngineImpl::GetUseTls() {
+ return tls_needed_;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetUser(const Jid & jid) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ user_jid_ = jid;
+
+ return XMPP_RETURN_OK;
+}
+
+const Jid &
+XmppEngineImpl::GetUser() {
+ return user_jid_;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetSaslHandler(SaslHandler * sasl_handler) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ sasl_handler_.reset(sasl_handler);
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SetRequestedResource(const std::string & resource) {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ requested_resource_ = resource;
+
+ return XMPP_RETURN_OK;
+}
+
+const std::string &
+XmppEngineImpl::GetRequestedResource() {
+ return requested_resource_;
+}
+
+XmppReturnStatus
+XmppEngineImpl::AddStanzaHandler(XmppStanzaHandler * stanza_handler,
+ XmppEngine::HandlerLevel level) {
+ if (state_ == STATE_CLOSED)
+ return XMPP_RETURN_BADSTATE;
+
+ stanza_handlers_[level]->push_back(stanza_handler);
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::RemoveStanzaHandler(XmppStanzaHandler * stanza_handler) {
+
+ bool found = false;
+
+ for (int level = 0; level < HL_COUNT; level += 1) {
+ StanzaHandlerVector::iterator new_end =
+ std::remove(stanza_handlers_[level]->begin(),
+ stanza_handlers_[level]->end(),
+ stanza_handler);
+
+ if (new_end != stanza_handlers_[level]->end()) {
+ stanza_handlers_[level]->erase(new_end, stanza_handlers_[level]->end());
+ found = true;
+ }
+ }
+
+ if (!found) {
+ return XMPP_RETURN_BADARGUMENT;
+ }
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::Connect() {
+ if (state_ != STATE_START)
+ return XMPP_RETURN_BADSTATE;
+
+ EnterExit ee(this);
+
+ // get the login task started
+ state_ = STATE_OPENING;
+ if (login_task_.get()) {
+ login_task_->IncomingStanza(NULL, false);
+ if (login_task_->IsDone())
+ login_task_.reset();
+ }
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SendStanza(const XmlElement * element) {
+ if (state_ == STATE_CLOSED)
+ return XMPP_RETURN_BADSTATE;
+
+ EnterExit ee(this);
+
+ if (login_task_.get()) {
+ // still handshaking - then outbound stanzas are queued
+ login_task_->OutgoingStanza(element);
+ } else {
+ // handshake done - send straight through
+ InternalSendStanza(element);
+ }
+
+ return XMPP_RETURN_OK;
+}
+
+XmppReturnStatus
+XmppEngineImpl::SendRaw(const std::string & text) {
+ if (state_ == STATE_CLOSED || login_task_.get())
+ return XMPP_RETURN_BADSTATE;
+
+ EnterExit ee(this);
+
+ (*output_) << text;
+
+ return XMPP_RETURN_OK;
+}
+
+std::string
+XmppEngineImpl::NextId() {
+ std::stringstream ss;
+ ss << next_id_++;
+ return ss.str();
+}
+
+XmppReturnStatus
+XmppEngineImpl::Disconnect() {
+
+ if (state_ != STATE_CLOSED) {
+ EnterExit ee(this);
+ if (state_ == STATE_OPEN)
+ *output_ << "</stream:stream>";
+ state_ = STATE_CLOSED;
+ }
+
+ return XMPP_RETURN_OK;
+}
+
+void
+XmppEngineImpl::IncomingStart(const XmlElement * pelStart) {
+ if (HasError() || raised_reset_)
+ return;
+
+ if (login_task_.get()) {
+ // start-stream should go to login task
+ login_task_->IncomingStanza(pelStart, true);
+ if (login_task_->IsDone())
+ login_task_.reset();
+ }
+ else {
+ // if not logging in, it's an error to see a start
+ SignalError(ERROR_XML);
+ }
+}
+
+void
+XmppEngineImpl::IncomingStanza(const XmlElement * stanza) {
+ if (HasError() || raised_reset_)
+ return;
+
+ if (stanza->Name() == QN_STREAM_ERROR) {
+ // Explicit XMPP stream error
+ SignalStreamError(stanza);
+ } else if (login_task_.get()) {
+ // Handle login handshake
+ login_task_->IncomingStanza(stanza, false);
+ if (login_task_->IsDone())
+ login_task_.reset();
+ } else if (HandleIqResponse(stanza)) {
+ // iq is handled by above call
+ } else {
+ // give every "peek" handler a shot at all stanzas
+ for (size_t i = 0; i < stanza_handlers_[HL_PEEK]->size(); i += 1) {
+ (*stanza_handlers_[HL_PEEK])[i]->HandleStanza(stanza);
+ }
+
+ // give other handlers a shot in precedence order, stopping after handled
+ for (int level = HL_SINGLE; level <= HL_ALL; level += 1) {
+ for (size_t i = 0; i < stanza_handlers_[level]->size(); i += 1) {
+ if ((*stanza_handlers_[level])[i]->HandleStanza(stanza))
+ goto Handled;
+ }
+ }
+
+ // If nobody wants to handle a stanza then send back an error.
+ // Only do this for IQ stanzas as messages should probably just be dropped
+ // and presence stanzas should certainly be dropped.
+ std::string type = stanza->Attr(QN_TYPE);
+ if (stanza->Name() == QN_IQ &&
+ !(type == "error" || type == "result")) {
+ SendStanzaError(stanza, XSE_FEATURE_NOT_IMPLEMENTED, STR_EMPTY);
+ }
+ }
+ Handled:
+ ; // handled - we're done
+}
+
+void
+XmppEngineImpl::IncomingEnd(bool isError) {
+ if (HasError() || raised_reset_)
+ return;
+
+ SignalError(isError ? ERROR_XML : ERROR_DOCUMENT_CLOSED);
+}
+
+void
+XmppEngineImpl::InternalSendStart(const std::string & to) {
+ // send stream-beginning
+ // note, we put a \r\n at tne end fo the first line to cause non-XMPP
+ // line-oriented servers (e.g., Apache) to reveal themselves more quickly.
+ *output_ << "<stream:stream to=\"" << to << "\" version=\"1.0\" "
+ "xmlns:stream=\"http://etherx.jabber.org/streams\" "
+ "xmlns=\"jabber:client\">\r\n";
+}
+
+void
+XmppEngineImpl::InternalSendStanza(const XmlElement * element) {
+ // It should really never be necessary to set a FROM attribute on a stanza.
+ // It is implied by the bind on the stream and if you get it wrong
+ // (by flipping from/to on a message?) the server will close the stream.
+ ASSERT(!element->HasAttr(QN_FROM));
+
+ // TODO: consider caching the XmlPrinter
+ XmlPrinter::PrintXml(output_.get(), element,
+ XMPP_CLIENT_NAMESPACES, XMPP_CLIENT_NAMESPACES_LEN);
+}
+
+std::string
+XmppEngineImpl::ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted) {
+ return sasl_handler_->ChooseBestSaslMechanism(mechanisms, encrypted);
+}
+
+SaslMechanism *
+XmppEngineImpl::GetSaslMechanism(const std::string & name) {
+ return sasl_handler_->CreateSaslMechanism(name);
+}
+
+void
+XmppEngineImpl::SignalBound(const Jid & fullJid) {
+ if (state_ == STATE_OPENING) {
+ bound_jid_ = fullJid;
+ state_ = STATE_OPEN;
+ }
+}
+
+void
+XmppEngineImpl::SignalStreamError(const XmlElement * pelStreamError) {
+ if (state_ != STATE_CLOSED) {
+ stream_error_.reset(new XmlElement(*pelStreamError));
+ SignalError(ERROR_STREAM);
+ }
+}
+
+void
+XmppEngineImpl::SignalError(Error errorCode) {
+ if (state_ != STATE_CLOSED) {
+ error_code_ = errorCode;
+ state_ = STATE_CLOSED;
+ }
+}
+
+bool
+XmppEngineImpl::HasError() {
+ return error_code_ != ERROR_NONE;
+}
+
+void
+XmppEngineImpl::StartTls(const std::string & domain) {
+ if (output_handler_) {
+ output_handler_->StartTls(
+ tls_server_domain_.empty() ? domain : tls_server_domain_);
+ encrypted_ = true;
+ }
+}
+
+XmppEngineImpl::EnterExit::EnterExit(XmppEngineImpl* engine)
+ : engine_(engine),
+ state_(engine->state_),
+ error_(engine->error_code_) {
+ engine->engine_entered_ += 1;
+}
+
+XmppEngineImpl::EnterExit::~EnterExit() {
+ XmppEngineImpl* engine = engine_;
+
+ engine->engine_entered_ -= 1;
+
+ bool closing = (engine->state_ != state_ &&
+ engine->state_ == STATE_CLOSED);
+ bool flushing = closing || (engine->engine_entered_ == 0);
+
+ if (engine->output_handler_ && flushing) {
+ std::string output = engine->output_->str();
+ if (output.length() > 0)
+ engine->output_handler_->WriteOutput(output.c_str(), output.length());
+ engine->output_->str("");
+
+ if (closing) {
+ engine->output_handler_->CloseConnection();
+ engine->output_handler_ = 0;
+ }
+ }
+
+ if (engine->engine_entered_)
+ return;
+
+ if (engine->raised_reset_) {
+ engine->stanzaParser_.Reset();
+ engine->raised_reset_ = false;
+ }
+
+ if (engine->session_handler_) {
+ if (engine->state_ != state_)
+ engine->session_handler_->OnStateChange(engine->state_);
+ // Note: Handling of OnStateChange(CLOSED) should allow for the
+ // deletion of the engine, so no members should be accessed
+ // after this line.
+ }
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h
new file mode 100644
index 00000000..c36f168c
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl.h
@@ -0,0 +1,262 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmppengineimpl_h_
+#define _xmppengineimpl_h_
+
+#include <sstream>
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/xmppstanzaparser.h"
+
+namespace buzz {
+
+class XmppLoginTask;
+class XmppEngine;
+class XmppIqEntry;
+class SaslHandler;
+class SaslMechanism;
+
+
+//! The XMPP connection engine.
+//! This engine implements the client side of the 'core' XMPP protocol.
+//! To use it, register an XmppOutputHandler to handle socket output
+//! and pass socket input to HandleInput. Then application code can
+//! set up the connection with a user, password, and other settings,
+//! and then call Connect() to initiate the connection.
+//! An application can listen for events and receive stanzas by
+//! registering an XmppStanzaHandler via AddStanzaHandler().
+class XmppEngineImpl : public XmppEngine {
+public:
+ XmppEngineImpl();
+ virtual ~XmppEngineImpl();
+
+ // SOCKET INPUT AND OUTPUT ------------------------------------------------
+
+ //! Registers the handler for socket output
+ virtual XmppReturnStatus SetOutputHandler(XmppOutputHandler *pxoh);
+
+ //! Provides socket input to the engine
+ virtual XmppReturnStatus HandleInput(const char * bytes, size_t len);
+
+ //! Advises the engine that the socket has closed
+ virtual XmppReturnStatus ConnectionClosed();
+
+ // SESSION SETUP ---------------------------------------------------------
+
+ //! Indicates the (bare) JID for the user to use.
+ virtual XmppReturnStatus SetUser(const Jid & jid);
+
+ //! Get the login (bare) JID.
+ virtual const Jid & GetUser();
+
+ //! Indicates the autentication to use. Takes ownership of the object.
+ virtual XmppReturnStatus SetSaslHandler(SaslHandler * sasl_handler);
+
+ //! Sets whether TLS will be used within the connection (default true).
+ virtual XmppReturnStatus SetUseTls(bool useTls);
+
+ //! Sets an alternate domain from which we allows TLS certificates.
+ //! This is for use in the case where a we want to allow a proxy to
+ //! serve up its own certificate rather than one owned by the underlying
+ //! domain.
+ virtual XmppReturnStatus SetTlsServerDomain(const std::string & proxy_domain);
+
+ //! Gets whether TLS will be used within the connection.
+ virtual bool GetUseTls();
+
+ //! Sets the request resource name, if any (optional).
+ //! Note that the resource name may be overridden by the server; after
+ //! binding, the actual resource name is available as part of FullJid().
+ virtual XmppReturnStatus SetRequestedResource(const std::string& resource);
+
+ //! Gets the request resource name.
+ virtual const std::string & GetRequestedResource();
+
+ // SESSION MANAGEMENT ---------------------------------------------------
+
+ //! Set callback for state changes.
+ virtual XmppReturnStatus SetSessionHandler(XmppSessionHandler* handler);
+
+ //! Initiates the XMPP connection.
+ //! After supplying connection settings, call this once to initiate,
+ //! (optionally) encrypt, authenticate, and bind the connection.
+ virtual XmppReturnStatus Connect();
+
+ //! The current engine state.
+ virtual State GetState() { return state_; }
+
+ //! Returns true if the connection is encrypted (under TLS)
+ virtual bool IsEncrypted() { return encrypted_; }
+
+ //! The error code.
+ //! Consult this after XmppOutputHandler.OnClose().
+ virtual Error GetError() { return error_code_; }
+
+ //! The stream:error stanza, when the error is XMPP_ERROR_STREAM.
+ //! Notice the stanza returned is owned by the XmppEngine and
+ //! is deleted when the engine is destroyed.
+ virtual const XmlElement * GetStreamError() { return stream_error_.get(); }
+
+ //! Closes down the connection.
+ //! Sends CloseConnection to output, and disconnects and registered
+ //! session handlers. After Disconnect completes, it is guaranteed
+ //! that no further callbacks will be made.
+ virtual XmppReturnStatus Disconnect();
+
+ // APPLICATION USE -------------------------------------------------------
+
+ //! Adds a listener for session events.
+ //! Stanza delivery is chained to session handlers; the first to
+ //! return 'true' is the last to get each stanza.
+ virtual XmppReturnStatus AddStanzaHandler(XmppStanzaHandler* handler,
+ XmppEngine::HandlerLevel level);
+
+ //! Removes a listener for session events.
+ virtual XmppReturnStatus RemoveStanzaHandler(XmppStanzaHandler* handler);
+
+ //! Sends a stanza to the server.
+ virtual XmppReturnStatus SendStanza(const XmlElement * pelStanza);
+
+ //! Sends raw text to the server
+ virtual XmppReturnStatus SendRaw(const std::string & text);
+
+ //! Sends an iq to the server, and registers a callback for the result.
+ //! Returns the cookie passed to the result handler.
+ virtual XmppReturnStatus SendIq(const XmlElement* pelStanza,
+ XmppIqHandler* iq_handler,
+ XmppIqCookie* cookie);
+
+ //! Unregisters an iq callback handler given its cookie.
+ //! No callback will come to this handler after it's unregistered.
+ virtual XmppReturnStatus RemoveIqHandler(XmppIqCookie cookie,
+ XmppIqHandler** iq_handler);
+
+ //! Forms and sends an error in response to the given stanza.
+ //! Swaps to and from, sets type to "error", and adds error information
+ //! based on the passed code. Text is optional and may be STR_EMPTY.
+ virtual XmppReturnStatus SendStanzaError(const XmlElement * pelOriginal,
+ XmppStanzaError code,
+ const std::string & text);
+
+ //! The fullly bound JID.
+ //! This JID is only valid after binding has succeeded. If the value
+ //! is JID_NULL, the binding has not succeeded.
+ virtual const Jid & FullJid() { return bound_jid_; }
+
+ //! The next unused iq id for this connection.
+ //! Call this when building iq stanzas, to ensure that each iq
+ //! gets its own unique id.
+ virtual std::string NextId();
+
+private:
+ friend class XmppLoginTask;
+ friend class XmppIqEntry;
+
+ void IncomingStanza(const XmlElement *pelStanza);
+ void IncomingStart(const XmlElement *pelStanza);
+ void IncomingEnd(bool isError);
+
+ void InternalSendStart(const std::string & domainName);
+ void InternalSendStanza(const XmlElement * pelStanza);
+ std::string ChooseBestSaslMechanism(const std::vector<std::string> & mechanisms, bool encrypted);
+ SaslMechanism * GetSaslMechanism(const std::string & name);
+ void SignalBound(const Jid & fullJid);
+ void SignalStreamError(const XmlElement * pelStreamError);
+ void SignalError(Error errorCode);
+ bool HasError();
+ void DeleteIqCookies();
+ bool HandleIqResponse(const XmlElement * element);
+ void StartTls(const std::string & domain);
+ void RaiseReset() { raised_reset_ = true; }
+
+ class StanzaParseHandler : public XmppStanzaParseHandler {
+ public:
+ StanzaParseHandler(XmppEngineImpl * outer) : outer_(outer) {}
+ virtual void StartStream(const XmlElement * pelStream)
+ { outer_->IncomingStart(pelStream); }
+ virtual void Stanza(const XmlElement * pelStanza)
+ { outer_->IncomingStanza(pelStanza); }
+ virtual void EndStream()
+ { outer_->IncomingEnd(false); }
+ virtual void XmlError()
+ { outer_->IncomingEnd(true); }
+ private:
+ XmppEngineImpl * const outer_;
+ };
+
+ class EnterExit {
+ public:
+ EnterExit(XmppEngineImpl* engine);
+ ~EnterExit();
+ private:
+ XmppEngineImpl* engine_;
+ State state_;
+ Error error_;
+
+ };
+
+ friend class StanzaParseHandler;
+ friend class EnterExit;
+
+ StanzaParseHandler stanzaParseHandler_;
+ XmppStanzaParser stanzaParser_;
+
+
+ // state
+ int engine_entered_;
+ Jid user_jid_;
+ std::string password_;
+ std::string requested_resource_;
+ bool tls_needed_;
+ std::string tls_server_domain_;
+ scoped_ptr<XmppLoginTask> login_task_;
+
+ int next_id_;
+ Jid bound_jid_;
+ State state_;
+ bool encrypted_;
+ Error error_code_;
+ scoped_ptr<XmlElement> stream_error_;
+ bool raised_reset_;
+ XmppOutputHandler* output_handler_;
+ XmppSessionHandler* session_handler_;
+
+ typedef STD_VECTOR(XmppStanzaHandler*) StanzaHandlerVector;
+ scoped_ptr<StanzaHandlerVector> stanza_handlers_[HL_COUNT];
+
+ typedef STD_VECTOR(XmppIqEntry*) IqEntryVector;
+ scoped_ptr<IqEntryVector> iq_entries_;
+
+ scoped_ptr<SaslHandler> sasl_handler_;
+
+ scoped_ptr<std::stringstream> output_;
+};
+
+}
+
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc
new file mode 100644
index 00000000..eb623ed9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppengineimpl_iq.cc
@@ -0,0 +1,279 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <vector>
+#include <algorithm>
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppengineimpl.h"
+#include "talk/xmpp/constants.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+class XmppIqEntry {
+ XmppIqEntry(const std::string & id, const std::string & to,
+ XmppEngine * pxce, XmppIqHandler * iq_handler) :
+ id_(id),
+ to_(to),
+ engine_(pxce),
+ iq_handler_(iq_handler) {
+ }
+
+private:
+ friend class XmppEngineImpl;
+
+ const std::string id_;
+ const std::string to_;
+ XmppEngine * const engine_;
+ XmppIqHandler * const iq_handler_;
+};
+
+
+XmppReturnStatus
+XmppEngineImpl::SendIq(const XmlElement * element, XmppIqHandler * iq_handler,
+ XmppIqCookie* cookie) {
+ if (state_ == STATE_CLOSED)
+ return XMPP_RETURN_BADSTATE;
+ if (NULL == iq_handler)
+ return XMPP_RETURN_BADARGUMENT;
+ if (!element || element->Name() != QN_IQ)
+ return XMPP_RETURN_BADARGUMENT;
+
+ const std::string& type = element->Attr(QN_TYPE);
+ if (type != "get" && type != "set")
+ return XMPP_RETURN_BADARGUMENT;
+
+ if (!element->HasAttr(QN_ID))
+ return XMPP_RETURN_BADARGUMENT;
+ const std::string& id = element->Attr(QN_ID);
+
+ XmppIqEntry * iq_entry = new XmppIqEntry(id,
+ element->Attr(QN_TO),
+ this, iq_handler);
+ iq_entries_->push_back(iq_entry);
+ SendStanza(element);
+
+ if (cookie)
+ *cookie = iq_entry;
+
+ return XMPP_RETURN_OK;
+}
+
+
+XmppReturnStatus
+XmppEngineImpl::RemoveIqHandler(XmppIqCookie cookie,
+ XmppIqHandler ** iq_handler) {
+
+ std::vector<XmppIqEntry*, std::allocator<XmppIqEntry*> >::iterator pos;
+
+ pos = std::find(iq_entries_->begin(),
+ iq_entries_->end(),
+ reinterpret_cast<XmppIqEntry*>(cookie));
+
+ if (pos == iq_entries_->end())
+ return XMPP_RETURN_BADARGUMENT;
+
+ XmppIqEntry* entry = *pos;
+ iq_entries_->erase(pos);
+ if (iq_handler)
+ *iq_handler = entry->iq_handler_;
+ delete entry;
+
+ return XMPP_RETURN_OK;
+}
+
+void
+XmppEngineImpl::DeleteIqCookies() {
+ for (size_t i = 0; i < iq_entries_->size(); i += 1) {
+ XmppIqEntry * iq_entry_ = (*iq_entries_)[i];
+ (*iq_entries_)[i] = NULL;
+ delete iq_entry_;
+ }
+ iq_entries_->clear();
+}
+
+static void
+AecImpl(XmlElement * error_element, const QName & name,
+ const char * type, const char * code) {
+ error_element->AddElement(new XmlElement(QN_ERROR));
+ error_element->AddAttr(QN_CODE, code, 1);
+ error_element->AddAttr(QN_TYPE, type, 1);
+ error_element->AddElement(new XmlElement(name, true), 1);
+}
+
+
+static void
+AddErrorCode(XmlElement * error_element, XmppStanzaError code) {
+ switch (code) {
+ case XSE_BAD_REQUEST:
+ AecImpl(error_element, QN_STANZA_BAD_REQUEST, "modify", "400");
+ break;
+ case XSE_CONFLICT:
+ AecImpl(error_element, QN_STANZA_CONFLICT, "cancel", "409");
+ break;
+ case XSE_FEATURE_NOT_IMPLEMENTED:
+ AecImpl(error_element, QN_STANZA_FEATURE_NOT_IMPLEMENTED,
+ "cancel", "501");
+ break;
+ case XSE_FORBIDDEN:
+ AecImpl(error_element, QN_STANZA_FORBIDDEN, "auth", "403");
+ break;
+ case XSE_GONE:
+ AecImpl(error_element, QN_STANZA_GONE, "modify", "302");
+ break;
+ case XSE_INTERNAL_SERVER_ERROR:
+ AecImpl(error_element, QN_STANZA_INTERNAL_SERVER_ERROR, "wait", "500");
+ break;
+ case XSE_ITEM_NOT_FOUND:
+ AecImpl(error_element, QN_STANZA_ITEM_NOT_FOUND, "cancel", "404");
+ break;
+ case XSE_JID_MALFORMED:
+ AecImpl(error_element, QN_STANZA_JID_MALFORMED, "modify", "400");
+ break;
+ case XSE_NOT_ACCEPTABLE:
+ AecImpl(error_element, QN_STANZA_NOT_ACCEPTABLE, "cancel", "406");
+ break;
+ case XSE_NOT_ALLOWED:
+ AecImpl(error_element, QN_STANZA_NOT_ALLOWED, "cancel", "405");
+ break;
+ case XSE_PAYMENT_REQUIRED:
+ AecImpl(error_element, QN_STANZA_PAYMENT_REQUIRED, "auth", "402");
+ break;
+ case XSE_RECIPIENT_UNAVAILABLE:
+ AecImpl(error_element, QN_STANZA_RECIPIENT_UNAVAILABLE, "wait", "404");
+ break;
+ case XSE_REDIRECT:
+ AecImpl(error_element, QN_STANZA_REDIRECT, "modify", "302");
+ break;
+ case XSE_REGISTRATION_REQUIRED:
+ AecImpl(error_element, QN_STANZA_REGISTRATION_REQUIRED, "auth", "407");
+ break;
+ case XSE_SERVER_NOT_FOUND:
+ AecImpl(error_element, QN_STANZA_REMOTE_SERVER_NOT_FOUND,
+ "cancel", "404");
+ break;
+ case XSE_SERVER_TIMEOUT:
+ AecImpl(error_element, QN_STANZA_REMOTE_SERVER_TIMEOUT, "wait", "502");
+ break;
+ case XSE_RESOURCE_CONSTRAINT:
+ AecImpl(error_element, QN_STANZA_RESOURCE_CONSTRAINT, "wait", "500");
+ break;
+ case XSE_SERVICE_UNAVAILABLE:
+ AecImpl(error_element, QN_STANZA_SERVICE_UNAVAILABLE, "cancel", "503");
+ break;
+ case XSE_SUBSCRIPTION_REQUIRED:
+ AecImpl(error_element, QN_STANZA_SUBSCRIPTION_REQUIRED, "auth", "407");
+ break;
+ case XSE_UNDEFINED_CONDITION:
+ AecImpl(error_element, QN_STANZA_UNDEFINED_CONDITION, "wait", "500");
+ break;
+ case XSE_UNEXPECTED_REQUEST:
+ AecImpl(error_element, QN_STANZA_UNEXPECTED_REQUEST, "wait", "400");
+ break;
+ }
+}
+
+
+XmppReturnStatus
+XmppEngineImpl::SendStanzaError(const XmlElement * element_original,
+ XmppStanzaError code,
+ const std::string & text) {
+
+ if (state_ == STATE_CLOSED)
+ return XMPP_RETURN_BADSTATE;
+
+ XmlElement error_element(element_original->Name());
+ error_element.AddAttr(QN_TYPE, "error");
+
+ // copy attrs, copy 'from' to 'to' and strip 'from'
+ for (const XmlAttr * attribute = element_original->FirstAttr();
+ attribute; attribute = attribute->NextAttr()) {
+ QName name = attribute->Name();
+ if (name == QN_TO)
+ continue; // no need to put a from attr. Server will stamp stanza
+ else if (name == QN_FROM)
+ name = QN_TO;
+ else if (name == QN_TYPE)
+ continue;
+ error_element.AddAttr(name, attribute->Value());
+ }
+
+ // copy children
+ for (const XmlChild * child = element_original->FirstChild();
+ child;
+ child = child->NextChild()) {
+ if (child->IsText()) {
+ error_element.AddText(child->AsText()->Text());
+ } else {
+ error_element.AddElement(new XmlElement(*(child->AsElement())));
+ }
+ }
+
+ // add error information
+ AddErrorCode(&error_element, code);
+ if (text != STR_EMPTY) {
+ XmlElement * text_element = new XmlElement(QN_STANZA_TEXT, true);
+ text_element->AddText(text);
+ error_element.AddElement(text_element);
+ }
+
+ SendStanza(&error_element);
+
+ return XMPP_RETURN_OK;
+}
+
+
+bool
+XmppEngineImpl::HandleIqResponse(const XmlElement * element) {
+ if (iq_entries_->empty())
+ return false;
+ if (element->Name() != QN_IQ)
+ return false;
+ std::string type = element->Attr(QN_TYPE);
+ if (type != "result" && type != "error")
+ return false;
+ if (!element->HasAttr(QN_ID))
+ return false;
+ std::string id = element->Attr(QN_ID);
+ std::string from = element->Attr(QN_FROM);
+
+ for (std::vector<XmppIqEntry *>::iterator it = iq_entries_->begin();
+ it != iq_entries_->end(); it += 1) {
+ XmppIqEntry * iq_entry = *it;
+ if (iq_entry->id_ == id && iq_entry->to_ == from) {
+ iq_entries_->erase(it);
+ iq_entry->iq_handler_->IqResponse(iq_entry, element);
+ delete iq_entry;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc
new file mode 100644
index 00000000..470c2dc2
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.cc
@@ -0,0 +1,357 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string>
+#include <vector>
+#include <iostream>
+#include "talk/xmpp/jid.h"
+#include "talk/xmllite/xmlelement.h"
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppengineimpl.h"
+#include "talk/xmpp/constants.h"
+#include "talk/base/base64.h"
+#include "talk/xmpp/xmpplogintask.h"
+#include "talk/xmpp/saslmechanism.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+XmppLoginTask::XmppLoginTask(XmppEngineImpl * pctx) :
+ pctx_(pctx),
+ authNeeded_(true),
+ state_(LOGINSTATE_INIT),
+ pelStanza_(NULL),
+ isStart_(false),
+ iqId_(STR_EMPTY),
+ pelFeatures_(NULL),
+ fullJid_(STR_EMPTY),
+ streamId_(STR_EMPTY),
+ pvecQueuedStanzas_(new std::vector<XmlElement *>()),
+ sasl_mech_(NULL) {
+}
+
+XmppLoginTask::~XmppLoginTask() {
+ for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1)
+ delete (*pvecQueuedStanzas_)[i];
+}
+
+void
+XmppLoginTask::IncomingStanza(const XmlElement *element, bool isStart) {
+ pelStanza_ = element;
+ isStart_ = isStart;
+ Advance();
+ pelStanza_ = NULL;
+ isStart_ = false;
+}
+
+const XmlElement *
+XmppLoginTask::NextStanza() {
+ const XmlElement * result = pelStanza_;
+ pelStanza_ = NULL;
+ return result;
+}
+
+bool
+XmppLoginTask::Advance() {
+
+ for (;;) {
+
+ const XmlElement * element = NULL;
+
+ switch (state_) {
+
+ case LOGINSTATE_INIT: {
+ pctx_->RaiseReset();
+ pelFeatures_.reset(NULL);
+
+ pctx_->InternalSendStart(pctx_->user_jid_.domain());
+ state_ = LOGINSTATE_STREAMSTART_SENT;
+ break;
+ }
+
+ case LOGINSTATE_STREAMSTART_SENT: {
+ if (NULL == (element = NextStanza()))
+ return true;
+
+ if (!isStart_ || !HandleStartStream(element))
+ return Failure(XmppEngine::ERROR_VERSION);
+
+ state_ = LOGINSTATE_STARTED_XMPP;
+ return true;
+ }
+
+ case LOGINSTATE_STARTED_XMPP: {
+ if (NULL == (element = NextStanza()))
+ return true;
+
+ if (!HandleFeatures(element))
+ return Failure(XmppEngine::ERROR_VERSION);
+
+ if (pctx_->tls_needed_) {
+ state_ = LOGINSTATE_TLS_INIT;
+ continue;
+ }
+
+ if (authNeeded_) {
+ state_ = LOGINSTATE_AUTH_INIT;
+ continue;
+ }
+
+ state_ = LOGINSTATE_BIND_INIT;
+ continue;
+ }
+
+ case LOGINSTATE_TLS_INIT: {
+ const XmlElement * pelTls = GetFeature(QN_TLS_STARTTLS);
+ if (!pelTls)
+ return Failure(XmppEngine::ERROR_TLS);
+
+ XmlElement el(QN_TLS_STARTTLS, true);
+ pctx_->InternalSendStanza(&el);
+ state_ = LOGINSTATE_TLS_REQUESTED;
+ continue;
+ }
+
+ case LOGINSTATE_TLS_REQUESTED: {
+ if (NULL == (element = NextStanza()))
+ return true;
+ if (element->Name() != QN_TLS_PROCEED)
+ return Failure(XmppEngine::ERROR_TLS);
+
+ // The proper domain to verify against is the real underlying
+ // domain - i.e., the domain that owns the JID. Our XmppEngineImpl
+ // also allows matching against a proxy domain instead, if it is told
+ // to do so - see the implementation of XmppEngineImpl::StartTls and
+ // XmppEngine::SetTlsServerDomain to see how you can use that feature
+ pctx_->StartTls(pctx_->user_jid_.domain());
+ pctx_->tls_needed_ = false;
+ state_ = LOGINSTATE_INIT;
+ continue;
+ }
+
+ case LOGINSTATE_AUTH_INIT: {
+ const XmlElement * pelSaslAuth = GetFeature(QN_SASL_MECHANISMS);
+ if (!pelSaslAuth) {
+ return Failure(XmppEngine::ERROR_AUTH);
+ }
+
+ // Collect together the SASL auth mechanisms presented by the server
+ std::vector<std::string> mechanisms;
+ for (const XmlElement * pelMech =
+ pelSaslAuth->FirstNamed(QN_SASL_MECHANISM);
+ pelMech;
+ pelMech = pelMech->NextNamed(QN_SASL_MECHANISM)) {
+
+ mechanisms.push_back(pelMech->BodyText());
+ }
+
+ // Given all the mechanisms, choose the best
+ std::string choice(pctx_->ChooseBestSaslMechanism(mechanisms, pctx_->IsEncrypted()));
+ if (choice.empty()) {
+ return Failure(XmppEngine::ERROR_AUTH);
+ }
+
+ // No recognized auth mechanism - that's an error
+ sasl_mech_.reset(pctx_->GetSaslMechanism(choice));
+ if (sasl_mech_.get() == NULL) {
+ return Failure(XmppEngine::ERROR_AUTH);
+ }
+
+ // OK, let's start it.
+ XmlElement * auth = sasl_mech_->StartSaslAuth();
+ if (auth == NULL) {
+ return Failure(XmppEngine::ERROR_AUTH);
+ }
+
+ pctx_->InternalSendStanza(auth);
+ delete auth;
+ state_ = LOGINSTATE_SASL_RUNNING;
+ continue;
+ }
+
+ case LOGINSTATE_SASL_RUNNING: {
+ if (NULL == (element = NextStanza()))
+ return true;
+ if (element->Name().Namespace() != NS_SASL)
+ return Failure(XmppEngine::ERROR_AUTH);
+ if (element->Name() == QN_SASL_CHALLENGE) {
+ XmlElement * response = sasl_mech_->HandleSaslChallenge(element);
+ if (response == NULL) {
+ return Failure(XmppEngine::ERROR_AUTH);
+ }
+ pctx_->InternalSendStanza(response);
+ delete response;
+ state_ = LOGINSTATE_SASL_RUNNING;
+ continue;
+ }
+ if (element->Name() != QN_SASL_SUCCESS) {
+ return Failure(XmppEngine::ERROR_UNAUTHORIZED);
+ }
+
+ // Authenticated!
+ authNeeded_ = false;
+ state_ = LOGINSTATE_INIT;
+ continue;
+ }
+
+ case LOGINSTATE_BIND_INIT: {
+ const XmlElement * pelBindFeature = GetFeature(QN_BIND_BIND);
+ const XmlElement * pelSessionFeature = GetFeature(QN_SESSION_SESSION);
+ if (!pelBindFeature || !pelSessionFeature)
+ return Failure(XmppEngine::ERROR_BIND);
+
+ XmlElement iq(QN_IQ);
+ iq.AddAttr(QN_TYPE, "set");
+
+ iqId_ = pctx_->NextId();
+ iq.AddAttr(QN_ID, iqId_);
+ iq.AddElement(new XmlElement(QN_BIND_BIND, true));
+
+ if (pctx_->requested_resource_ != STR_EMPTY) {
+ iq.AddElement(new XmlElement(QN_BIND_RESOURCE), 1);
+ iq.AddText(pctx_->requested_resource_, 2);
+ }
+ pctx_->InternalSendStanza(&iq);
+ state_ = LOGINSTATE_BIND_REQUESTED;
+ continue;
+ }
+
+ case LOGINSTATE_BIND_REQUESTED: {
+ if (NULL == (element = NextStanza()))
+ return true;
+
+ if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ ||
+ element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set")
+ return true;
+
+ if (element->Attr(QN_TYPE) != "result" || element->FirstElement() == NULL ||
+ element->FirstElement()->Name() != QN_BIND_BIND)
+ return Failure(XmppEngine::ERROR_BIND);
+
+ fullJid_ = Jid(element->FirstElement()->TextNamed(QN_BIND_JID));
+ if (!fullJid_.IsFull()) {
+ return Failure(XmppEngine::ERROR_BIND);
+ }
+
+ if (pctx_->user_jid_.domain() != STR_DEFAULT_DOMAIN &&
+ fullJid_.BareJid() != pctx_->user_jid_) {
+ return Failure(XmppEngine::ERROR_BIND);
+ }
+
+ // now request session
+ XmlElement iq(QN_IQ);
+ iq.AddAttr(QN_TYPE, "set");
+
+ iqId_ = pctx_->NextId();
+ iq.AddAttr(QN_ID, iqId_);
+ iq.AddElement(new XmlElement(QN_SESSION_SESSION, true));
+ pctx_->InternalSendStanza(&iq);
+
+ state_ = LOGINSTATE_SESSION_REQUESTED;
+ continue;
+ }
+
+ case LOGINSTATE_SESSION_REQUESTED: {
+ if (NULL == (element = NextStanza()))
+ return true;
+ if (element->Name() != QN_IQ || element->Attr(QN_ID) != iqId_ ||
+ element->Attr(QN_TYPE) == "get" || element->Attr(QN_TYPE) == "set")
+ return false;
+
+ if (element->Attr(QN_TYPE) != "result")
+ return Failure(XmppEngine::ERROR_BIND);
+
+ pctx_->SignalBound(fullJid_);
+ FlushQueuedStanzas();
+ state_ = LOGINSTATE_DONE;
+ return true;
+ }
+
+ case LOGINSTATE_DONE:
+ return false;
+ }
+ }
+}
+
+bool
+XmppLoginTask::HandleStartStream(const XmlElement *element) {
+
+ if (element->Name() != QN_STREAM_STREAM)
+ return false;
+
+ if (element->Attr(QN_XMLNS) != "jabber:client")
+ return false;
+
+ if (element->Attr(QN_VERSION) != "1.0")
+ return false;
+
+ if (!element->HasAttr(QN_ID))
+ return false;
+
+ streamId_ = element->Attr(QN_ID);
+
+ return true;
+}
+
+bool
+XmppLoginTask::HandleFeatures(const XmlElement *element) {
+ if (element->Name() != QN_STREAM_FEATURES)
+ return false;
+
+ pelFeatures_.reset(new XmlElement(*element));
+ return true;
+}
+
+const XmlElement *
+XmppLoginTask::GetFeature(const QName & name) {
+ return pelFeatures_->FirstNamed(name);
+}
+
+bool
+XmppLoginTask::Failure(XmppEngine::Error reason) {
+ state_ = LOGINSTATE_DONE;
+ pctx_->SignalError(reason);
+ return false;
+}
+
+void
+XmppLoginTask::OutgoingStanza(const XmlElement * element) {
+ XmlElement * pelCopy = new XmlElement(*element);
+ pvecQueuedStanzas_->push_back(pelCopy);
+}
+
+void
+XmppLoginTask::FlushQueuedStanzas() {
+ for (size_t i = 0; i < pvecQueuedStanzas_->size(); i += 1) {
+ pctx_->InternalSendStanza((*pvecQueuedStanzas_)[i]);
+ delete (*pvecQueuedStanzas_)[i];
+ }
+ pvecQueuedStanzas_->clear();
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h
new file mode 100644
index 00000000..7f321a30
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpplogintask.h
@@ -0,0 +1,95 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _logintask_h_
+#define _logintask_h_
+
+#include <string>
+#include "talk/xmpp/jid.h"
+#include "talk/base/scoped_ptr.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/base/stl_decl.h"
+
+namespace buzz {
+
+class XmlElement;
+class XmppEngineImpl;
+class SaslMechanism;
+
+
+class XmppLoginTask {
+
+public:
+ XmppLoginTask(XmppEngineImpl *pctx);
+ ~XmppLoginTask();
+
+ bool IsDone()
+ { return state_ == LOGINSTATE_DONE; }
+ void IncomingStanza(const XmlElement * element, bool isStart);
+ void OutgoingStanza(const XmlElement *element);
+
+private:
+ enum LoginTaskState {
+ LOGINSTATE_INIT = 0,
+ LOGINSTATE_STREAMSTART_SENT,
+ LOGINSTATE_STARTED_XMPP,
+ LOGINSTATE_TLS_INIT,
+ LOGINSTATE_AUTH_INIT,
+ LOGINSTATE_BIND_INIT,
+ LOGINSTATE_TLS_REQUESTED,
+ LOGINSTATE_SASL_RUNNING,
+ LOGINSTATE_BIND_REQUESTED,
+ LOGINSTATE_SESSION_REQUESTED,
+ LOGINSTATE_DONE,
+ };
+
+ const XmlElement * NextStanza();
+ bool Advance();
+ bool HandleStartStream(const XmlElement * element);
+ bool HandleFeatures(const XmlElement * element);
+ const XmlElement * GetFeature(const QName & name);
+ bool Failure(XmppEngine::Error reason);
+ void FlushQueuedStanzas();
+
+ XmppEngineImpl * pctx_;
+ bool authNeeded_;
+ LoginTaskState state_;
+ const XmlElement * pelStanza_;
+ bool isStart_;
+ std::string iqId_;
+ scoped_ptr<XmlElement> pelFeatures_;
+ Jid fullJid_;
+ std::string streamId_;
+ scoped_ptr<std::vector<XmlElement *,
+ std::allocator<XmlElement *> > > pvecQueuedStanzas_;
+
+ scoped_ptr<SaslMechanism> sasl_mech_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h
new file mode 100644
index 00000000..f431b4e5
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpppassword.h
@@ -0,0 +1,163 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPPASSWORD_H_
+#define _XMPPPASSWORD_H_
+
+#include "talk/base/linked_ptr.h"
+#include "talk/base/scoped_ptr.h"
+
+namespace buzz {
+
+class XmppPasswordImpl {
+public:
+ virtual ~XmppPasswordImpl() {}
+ virtual size_t GetLength() const = 0;
+ virtual void CopyTo(char * dest, bool nullterminate) const = 0;
+ virtual std::string UrlEncode() const = 0;
+ virtual XmppPasswordImpl * Copy() const = 0;
+};
+
+class EmptyXmppPasswordImpl : public XmppPasswordImpl {
+public:
+ virtual ~EmptyXmppPasswordImpl() {}
+ virtual size_t GetLength() const { return 0; }
+ virtual void CopyTo(char * dest, bool nullterminate) const {
+ if (nullterminate) {
+ *dest = '\0';
+ }
+ }
+ virtual std::string UrlEncode() const { return ""; }
+ virtual XmppPasswordImpl * Copy() const { return new EmptyXmppPasswordImpl(); }
+};
+
+class XmppPassword {
+public:
+ XmppPassword() : impl_(new EmptyXmppPasswordImpl()) {}
+ size_t GetLength() const { return impl_->GetLength(); }
+ void CopyTo(char * dest, bool nullterminate) const { impl_->CopyTo(dest, nullterminate); }
+ XmppPassword(const XmppPassword & other) : impl_(other.impl_->Copy()) {}
+ explicit XmppPassword(const XmppPasswordImpl & impl) : impl_(impl.Copy()) {}
+ XmppPassword & operator=(const XmppPassword & other) {
+ if (this != &other) {
+ impl_.reset(other.impl_->Copy());
+ }
+ return *this;
+ }
+ void Clear() { impl_.reset(new EmptyXmppPasswordImpl()); }
+ std::string UrlEncode() const { return impl_->UrlEncode(); }
+
+private:
+ scoped_ptr<const XmppPasswordImpl> impl_;
+};
+
+
+// Used for constructing strings where a password is involved and we
+// need to ensure that we zero memory afterwards
+class FormatXmppPassword {
+public:
+ FormatXmppPassword() {
+ storage_ = new char[32];
+ capacity_ = 32;
+ length_ = 0;
+ storage_[0] = 0;
+ }
+
+ void Append(const std::string & text) {
+ Append(text.data(), text.length());
+ }
+
+ void Append(const char * data, size_t length) {
+ EnsureStorage(length_ + length + 1);
+ memcpy(storage_ + length_, data, length);
+ length_ += length;
+ storage_[length_] = '\0';
+ }
+
+ void Append(const XmppPassword * password) {
+ size_t len = password->GetLength();
+ EnsureStorage(length_ + len + 1);
+ password->CopyTo(storage_ + length_, true);
+ length_ += len;
+ }
+
+ size_t GetLength() {
+ return length_;
+ }
+
+ const char * GetData() {
+ return storage_;
+ }
+
+
+ // Ensures storage of at least n bytes
+ void EnsureStorage(size_t n) {
+ if (capacity_ >= n) {
+ return;
+ }
+
+ size_t old_capacity = capacity_;
+ char * old_storage = storage_;
+
+ for (;;) {
+ capacity_ *= 2;
+ if (capacity_ >= n)
+ break;
+ }
+
+ storage_ = new char[capacity_];
+
+ if (old_capacity) {
+ memcpy(storage_, old_storage, length_);
+
+ // zero memory in a way that an optimizer won't optimize it out
+ old_storage[0] = 0;
+ for (size_t i = 1; i < old_capacity; i++) {
+ old_storage[i] = old_storage[i - 1];
+ }
+ delete[] old_storage;
+ }
+ }
+
+ ~FormatXmppPassword() {
+ if (capacity_) {
+ storage_[0] = 0;
+ for (size_t i = 1; i < capacity_; i++) {
+ storage_[i] = storage_[i - 1];
+ }
+ }
+ delete[] storage_;
+ }
+private:
+ char * storage_;
+ size_t capacity_;
+ size_t length_;
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc
new file mode 100644
index 00000000..66ed44fb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.cc
@@ -0,0 +1,104 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <expat.h>
+#include "talk/xmllite/xmlelement.h"
+#include "talk/base/common.h"
+#include "talk/xmpp/xmppstanzaparser.h"
+#include "talk/xmpp/constants.h"
+
+#define new TRACK_NEW
+
+namespace buzz {
+
+XmppStanzaParser::XmppStanzaParser(XmppStanzaParseHandler *psph) :
+ psph_(psph),
+ innerHandler_(this),
+ parser_(&innerHandler_),
+ depth_(0),
+ builder_() {
+}
+
+void
+XmppStanzaParser::Reset() {
+ parser_.Reset();
+ depth_ = 0;
+ builder_.Reset();
+}
+
+void
+XmppStanzaParser::IncomingStartElement(
+ XmlParseContext * pctx, const char * name, const char ** atts) {
+ if (depth_++ == 0) {
+ XmlElement * pelStream = XmlBuilder::BuildElement(pctx, name, atts);
+ if (pelStream == NULL) {
+ pctx->RaiseError(XML_ERROR_SYNTAX);
+ return;
+ }
+ psph_->StartStream(pelStream);
+ delete pelStream;
+ return;
+ }
+
+ builder_.StartElement(pctx, name, atts);
+}
+
+void
+XmppStanzaParser::IncomingCharacterData(
+ XmlParseContext * pctx, const char * text, int len) {
+ if (depth_ > 1) {
+ builder_.CharacterData(pctx, text, len);
+ }
+}
+
+void
+XmppStanzaParser::IncomingEndElement(
+ XmlParseContext * pctx, const char * name) {
+ if (--depth_ == 0) {
+ psph_->EndStream();
+ return;
+ }
+
+ builder_.EndElement(pctx, name);
+
+ if (depth_ == 1) {
+ XmlElement *element = builder_.CreateElement();
+ psph_->Stanza(element);
+ delete element;
+ }
+}
+
+void
+XmppStanzaParser::IncomingError(
+ XmlParseContext * pctx, XML_Error errCode) {
+ UNUSED(pctx);
+ UNUSED(errCode);
+ psph_->XmlError();
+}
+
+}
+
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h
new file mode 100644
index 00000000..1e109a3d
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmppstanzaparser.h
@@ -0,0 +1,96 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _xmppstanzaparser_h_
+#define _xmppstanzaparser_h_
+
+#include "talk/xmllite/xmlparser.h"
+#include "talk/xmllite/xmlbuilder.h"
+
+
+namespace buzz {
+
+class XmlElement;
+
+class XmppStanzaParseHandler {
+public:
+ virtual void StartStream(const XmlElement * pelStream) = 0;
+ virtual void Stanza(const XmlElement * pelStanza) = 0;
+ virtual void EndStream() = 0;
+ virtual void XmlError() = 0;
+};
+
+class XmppStanzaParser {
+public:
+ XmppStanzaParser(XmppStanzaParseHandler *psph);
+ bool Parse(const char * data, size_t len, bool isFinal)
+ { return parser_.Parse(data, len, isFinal); }
+ void Reset();
+
+private:
+ class ParseHandler : public XmlParseHandler {
+ public:
+ ParseHandler(XmppStanzaParser * outer) : outer_(outer) {}
+ virtual void StartElement(XmlParseContext * pctx,
+ const char * name, const char ** atts)
+ { outer_->IncomingStartElement(pctx, name, atts); }
+ virtual void EndElement(XmlParseContext * pctx,
+ const char * name)
+ { outer_->IncomingEndElement(pctx, name); }
+ virtual void CharacterData(XmlParseContext * pctx,
+ const char * text, int len)
+ { outer_->IncomingCharacterData(pctx, text, len); }
+ virtual void Error(XmlParseContext * pctx,
+ XML_Error errCode)
+ { outer_->IncomingError(pctx, errCode); }
+ private:
+ XmppStanzaParser * const outer_;
+ };
+
+ friend class ParseHandler;
+
+ void IncomingStartElement(XmlParseContext * pctx,
+ const char * name, const char ** atts);
+ void IncomingEndElement(XmlParseContext * pctx,
+ const char * name);
+ void IncomingCharacterData(XmlParseContext * pctx,
+ const char * text, int len);
+ void IncomingError(XmlParseContext * pctx,
+ XML_Error errCode);
+
+ XmppStanzaParseHandler * psph_;
+ ParseHandler innerHandler_;
+ XmlParser parser_;
+ int depth_;
+ XmlBuilder builder_;
+
+ };
+
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc
new file mode 100644
index 00000000..82207f3b
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.cc
@@ -0,0 +1,168 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "talk/xmpp/xmpptask.h"
+#include "talk/xmpp/xmppclient.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/xmpp/constants.h"
+
+namespace buzz {
+
+XmppTask::XmppTask(Task * parent, XmppEngine::HandlerLevel level)
+ : Task(parent), client_(NULL) {
+ XmppClient * client = (XmppClient*)parent->GetParent(XMPP_CLIENT_TASK_CODE);
+ client_ = client;
+ id_ = client->NextId();
+ client->AddXmppTask(this, level);
+ client->SignalDisconnected.connect(this, &XmppTask::OnDisconnect);
+}
+
+XmppTask::~XmppTask() {
+ StopImpl();
+}
+
+void
+XmppTask::StopImpl() {
+ while (NextStanza() != NULL) {}
+ if (client_) {
+ client_->RemoveXmppTask(this);
+ client_->SignalDisconnected.disconnect(this);
+ client_ = NULL;
+ }
+}
+
+XmppReturnStatus
+XmppTask::SendStanza(const XmlElement * stanza) {
+ if (client_ == NULL)
+ return XMPP_RETURN_BADSTATE;
+ return client_->SendStanza(stanza);
+}
+
+XmppReturnStatus
+XmppTask::SendStanzaError(const XmlElement * element_original,
+ XmppStanzaError code,
+ const std::string & text) {
+ if (client_ == NULL)
+ return XMPP_RETURN_BADSTATE;
+ return client_->SendStanzaError(element_original, code, text);
+}
+
+void
+XmppTask::Stop() {
+ StopImpl();
+ Task::Stop();
+}
+
+void
+XmppTask::OnDisconnect() {
+ Error();
+}
+
+void
+XmppTask::QueueStanza(const XmlElement * stanza) {
+ stanza_queue_.push_back(new XmlElement(*stanza));
+ Wake();
+}
+
+const XmlElement *
+XmppTask::NextStanza() {
+ XmlElement * result = NULL;
+ if (!stanza_queue_.empty()) {
+ result = stanza_queue_.front();
+ stanza_queue_.pop_front();
+ }
+ next_stanza_.reset(result);
+ return result;
+}
+
+XmlElement *
+XmppTask::MakeIq(const std::string & type,
+ const buzz::Jid & to, const std::string id) {
+ XmlElement * result = new XmlElement(QN_IQ);
+ if (!type.empty())
+ result->AddAttr(QN_TYPE, type);
+ if (to != JID_EMPTY)
+ result->AddAttr(QN_TO, to.Str());
+ if (!id.empty())
+ result->AddAttr(QN_ID, id);
+ return result;
+}
+
+XmlElement *
+XmppTask::MakeIqResult(const XmlElement * query) {
+ XmlElement * result = new XmlElement(QN_IQ);
+ result->AddAttr(QN_TYPE, STR_RESULT);
+ if (query->HasAttr(QN_FROM)) {
+ result->AddAttr(QN_TO, query->Attr(QN_FROM));
+ }
+ result->AddAttr(QN_ID, query->Attr(QN_ID));
+ return result;
+}
+
+bool
+XmppTask::MatchResponseIq(const XmlElement * stanza,
+ const Jid & to, const std::string & id) {
+ if (stanza->Name() != QN_IQ)
+ return false;
+
+ if (stanza->Attr(QN_ID) != id)
+ return false;
+
+ Jid from(stanza->Attr(QN_FROM));
+ if (from != to) {
+ Jid me = client_->jid();
+ // we address the server as "", but it is legal for the server
+ // to identify itself with "domain" or "myself@domain"
+ if (to != JID_EMPTY) {
+ return false;
+ }
+
+ if (from != Jid(me.domain()) && from != me.BareJid()) {
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+bool
+XmppTask::MatchRequestIq(const XmlElement * stanza,
+ const std::string & type, const QName & qn) {
+ if (stanza->Name() != QN_IQ)
+ return false;
+
+ if (stanza->Attr(QN_TYPE) != type)
+ return false;
+
+ if (stanza->FirstNamed(qn) == NULL)
+ return false;
+
+ return true;
+}
+
+}
diff --git a/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h
new file mode 100644
index 00000000..3b56a1c9
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/libjingle/talk/xmpp/xmpptask.h
@@ -0,0 +1,113 @@
+/*
+ * libjingle
+ * Copyright 2004--2005, Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _XMPPTASK_H_
+#define _XMPPTASK_H_
+
+#include <string>
+#include <deque>
+#include "talk/base/sigslot.h"
+#include "talk/xmpp/xmppengine.h"
+#include "talk/base/task.h"
+
+namespace buzz {
+
+/////////////////////////////////////////////////////////////////////
+//
+// XMPPTASK
+//
+/////////////////////////////////////////////////////////////////////
+//
+// See Task and XmppClient first.
+//
+// XmppTask is a task that is designed to go underneath XmppClient and be
+// useful there. It has a way of finding its XmppClient parent so you
+// can have it nested arbitrarily deep under an XmppClient and it can
+// still find the XMPP services.
+//
+// Tasks register themselves to listen to particular kinds of stanzas
+// that are sent out by the client. Rather than processing stanzas
+// right away, they should decide if they own the sent stanza,
+// and if so, queue it and Wake() the task, or if a stanza does not belong
+// to you, return false right away so the next XmppTask can take a crack.
+// This technique (synchronous recognize, but asynchronous processing)
+// allows you to have arbitrary logic for recognizing stanzas yet still,
+// for example, disconnect a client while processing a stanza -
+// without reentrancy problems.
+//
+/////////////////////////////////////////////////////////////////////
+
+class XmppClient;
+
+class XmppTask :
+ public Task,
+ public XmppStanzaHandler,
+ public sigslot::has_slots<>
+{
+public:
+ XmppTask(Task * parent, XmppEngine::HandlerLevel level = XmppEngine::HL_NONE);
+ virtual ~XmppTask();
+
+ virtual XmppClient * GetClient() const { return client_; }
+ std::string task_id() const { return id_; }
+
+protected:
+ friend class XmppClient;
+
+ XmppReturnStatus SendStanza(const XmlElement * stanza);
+ XmppReturnStatus SetResult(const std::string & code);
+ XmppReturnStatus SendStanzaError(const XmlElement * element_original,
+ XmppStanzaError code,
+ const std::string & text);
+
+ virtual void Stop();
+ virtual bool HandleStanza(const XmlElement * stanza) { return false; }
+ virtual void OnDisconnect();
+ virtual int ProcessReponse() { return STATE_DONE; }
+
+ void QueueStanza(const XmlElement * stanza);
+ const XmlElement * NextStanza();
+
+ bool MatchResponseIq(const XmlElement * stanza, const Jid & to, const std::string & task_id);
+ bool MatchRequestIq(const XmlElement * stanza, const std::string & type, const QName & qn);
+ XmlElement *MakeIqResult(const XmlElement * query);
+ XmlElement *MakeIq(const std::string & type,
+ const Jid & to, const std::string task_id);
+
+private:
+ void StopImpl();
+
+ XmppClient * client_;
+ std::deque<XmlElement *> stanza_queue_;
+ scoped_ptr<XmlElement> next_stanza_;
+ std::string id_;
+
+};
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/jingle/voicecaller.h b/kopete/protocols/jabber/jingle/voicecaller.h
new file mode 100644
index 00000000..0f0d18bb
--- /dev/null
+++ b/kopete/protocols/jabber/jingle/voicecaller.h
@@ -0,0 +1,96 @@
+#define PsiAccount JabberAccount
+class PsiAccount;
+
+#ifndef VOICECALLER_H
+#define VOICECALLER_H
+
+#include "im.h"
+
+
+
+
+using namespace XMPP;
+
+/**
+ * \brief An abstract class for a voice call implementation.
+ */
+class VoiceCaller : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * \brief Base constructor.
+ *
+ * \param account the account to which this voice caller belongs
+ */
+ VoiceCaller(PsiAccount* account) : account_(account) { };
+
+ /**
+ * \brief Retrieves the account to which this voice caller belongs.
+ */
+ PsiAccount* account() { return account_; }
+
+ /**
+ * \brief Initializes the voice caller.
+ * This should be called when the connection is open.
+ */
+ virtual void initialize() = 0;
+
+ /**
+ * \brief De-initializes the voice caller.
+ * This should be called when the connection is about to be closed.
+ */
+ virtual void deinitialize() = 0;
+
+ /**
+ * \brief Call the given JID.
+ */
+ virtual void call(const Jid&) = 0;
+
+ /**
+ * \brief Accept a call from the given JID.
+ */
+ virtual void accept(const Jid&) = 0;
+
+ /**
+ * \brief Reject the call from the given JID.
+ */
+ virtual void reject(const Jid&) = 0;
+
+ /**
+ * \brief Terminate the call from the given JID.
+ */
+ virtual void terminate(const Jid&) = 0;
+
+signals:
+ /**
+ * \brief Incoming call from the given JID.
+ */
+ void incoming(const Jid&);
+
+ /**
+ * \brief Contact accepted an incoming call.
+ */
+ void accepted(const Jid&);
+
+ /**
+ * \brief Contact rejected an incoming call.
+ */
+ void rejected(const Jid&);
+
+ /**
+ * \brief Call with given JID is in progress.
+ */
+ void in_progress(const Jid&);
+
+ /**
+ * \brief Call with given JID is terminated.
+ */
+ void terminated(const Jid&);
+
+private:
+ PsiAccount* account_;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/kioslave/Makefile.am b/kopete/protocols/jabber/kioslave/Makefile.am
new file mode 100644
index 00000000..7fe4d3d6
--- /dev/null
+++ b/kopete/protocols/jabber/kioslave/Makefile.am
@@ -0,0 +1,25 @@
+METASOURCES = AUTO
+
+INCLUDES = \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../libiris/iris/include \
+ -I$(srcdir)/../libiris/iris/xmpp-im \
+ -I$(srcdir)/../libiris/iris/jabber \
+ -I$(srcdir)/../libiris/qca/src \
+ -I$(srcdir)/../libiris/cutestuff/util \
+ -I$(srcdir)/../libiris/cutestuff/network \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kio_jabberdisco.la
+
+kio_jabberdisco_la_SOURCES = jabberdisco.cpp
+kio_jabberdisco_la_LIBADD = ../libjabberclient.la ../libiris/qca/src/libqca.la ../libiris/iris/include/libiris.la ../libiris/iris/xmpp-im/libiris_xmpp_im.la ../libiris/iris/xmpp-core/libiris_xmpp_core.la ../libiris/iris/jabber/libiris_jabber.la ../libiris/cutestuff/util/libcutestuff_util.la ../libiris/cutestuff/network/libcutestuff_network.la $(LIB_KIO)
+kio_jabberdisco_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+
+noinst_HEADERS = jabberdisco.h
+
+protocol_DATA = jabberdisco.protocol
+protocoldir = $(kde_servicesdir)
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kio_jabberdisco.pot
diff --git a/kopete/protocols/jabber/kioslave/jabberdisco.cpp b/kopete/protocols/jabber/kioslave/jabberdisco.cpp
new file mode 100644
index 00000000..a6775320
--- /dev/null
+++ b/kopete/protocols/jabber/kioslave/jabberdisco.cpp
@@ -0,0 +1,399 @@
+
+/***************************************************************************
+ Jabber Service Discovery KIO Slave
+ -------------------
+ begin : Wed June 1 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2005 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+
+#include "jabberdisco.h"
+
+#include <stdlib.h>
+#include <qcstring.h>
+#include <qthread.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+
+#include <xmpp_tasks.h>
+#include "jabberclient.h"
+
+JabberDiscoProtocol::JabberDiscoProtocol ( const QCString &pool_socket, const QCString &app_socket )
+ : KIO::SlaveBase ( "kio_jabberdisco", pool_socket, app_socket )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Slave launched." << endl;
+
+ m_jabberClient = 0l;
+ m_connected = false;
+
+}
+
+
+JabberDiscoProtocol::~JabberDiscoProtocol ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Slave is shutting down." << endl;
+
+ delete m_jabberClient;
+
+}
+
+void JabberDiscoProtocol::setHost ( const QString &host, int port, const QString &user, const QString &pass )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << " Host " << host << ", port " << port << ", user " << user << endl;
+
+ m_host = host;
+ m_port = !port ? 5222 : port;
+ m_user = QString(user).replace ( "%", "@" );
+ m_password = pass;
+
+}
+
+void JabberDiscoProtocol::openConnection ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ if ( m_connected )
+ {
+ return;
+ }
+
+ // instantiate new client backend or clean up old one
+ if ( !m_jabberClient )
+ {
+ m_jabberClient = new JabberClient;
+
+ QObject::connect ( m_jabberClient, SIGNAL ( csDisconnected () ), this, SLOT ( slotCSDisconnected () ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( csError ( int ) ), this, SLOT ( slotCSError ( int ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( tlsWarning ( int ) ), this, SLOT ( slotHandleTLSWarning ( int ) ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) );
+ QObject::connect ( m_jabberClient, SIGNAL ( error ( JabberClient::ErrorCode ) ), this, SLOT ( slotClientError ( JabberClient::ErrorCode ) ) );
+
+ QObject::connect ( m_jabberClient, SIGNAL ( debugMessage ( const QString & ) ),
+ this, SLOT ( slotClientDebugMessage ( const QString & ) ) );
+ }
+ else
+ {
+ m_jabberClient->disconnect ();
+ }
+
+ // we need to use the old protocol for now
+ m_jabberClient->setUseXMPP09 ( true );
+
+ // set SSL flag (this should be converted to forceTLS when using the new protocol)
+ m_jabberClient->setUseSSL ( false );
+
+ // override server and port (this should be dropped when using the new protocol and no direct SSL)
+ m_jabberClient->setOverrideHost ( true, m_host, m_port );
+
+ // allow plaintext password authentication or not?
+ m_jabberClient->setAllowPlainTextPassword ( false );
+
+ switch ( m_jabberClient->connect ( XMPP::Jid ( m_user + QString("/") + "JabberBrowser" ), m_password ) )
+ {
+ case JabberClient::NoTLS:
+ // no SSL support, at the connecting stage this means the problem is client-side
+ error ( KIO::ERR_UPGRADE_REQUIRED, i18n ( "TLS" ) );
+ break;
+
+ case JabberClient::Ok:
+ default:
+ // everything alright!
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Waiting for socket to open..." << endl;
+ break;
+ }
+
+ connected ();
+
+}
+
+void JabberDiscoProtocol::closeConnection ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ if ( m_jabberClient )
+ {
+ m_jabberClient->disconnect ();
+ }
+
+}
+
+void JabberDiscoProtocol::slave_status ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ slaveStatus ( m_host, m_connected );
+
+}
+
+void JabberDiscoProtocol::get ( const KURL &url )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ m_command = Get;
+ m_url = url;
+
+ mimeType ( "inode/directory" );
+
+ finished ();
+
+}
+
+void JabberDiscoProtocol::listDir ( const KURL &url )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ m_command = ListDir;
+ m_url = url;
+
+ openConnection ();
+
+}
+
+void JabberDiscoProtocol::mimetype ( const KURL &/*url*/ )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << endl;
+
+ mimeType("inode/directory");
+
+ finished ();
+
+}
+
+void JabberDiscoProtocol::slotClientDebugMessage ( const QString &msg )
+{
+
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << msg << endl;
+
+}
+
+void JabberDiscoProtocol::slotHandleTLSWarning ( int validityResult )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Handling TLS warning..." << endl;
+
+ if ( messageBox ( KIO::SlaveBase::WarningContinueCancel,
+ i18n ( "The server certificate is invalid. Do you want to continue? " ),
+ i18n ( "Certificate Warning" ) ) == KMessageBox::Continue )
+ {
+ // resume stream
+ m_jabberClient->continueAfterTLSWarning ();
+ }
+ else
+ {
+ // disconnect stream
+ closeConnection ();
+ }
+
+}
+
+void JabberDiscoProtocol::slotClientError ( JabberClient::ErrorCode errorCode )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Handling client error..." << endl;
+
+ switch ( errorCode )
+ {
+ case JabberClient::NoTLS:
+ default:
+ error ( KIO::ERR_UPGRADE_REQUIRED, i18n ( "TLS" ) );
+ closeConnection ();
+ break;
+ }
+
+}
+
+void JabberDiscoProtocol::slotConnected ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Connected to Jabber server." << endl;
+
+ XMPP::JT_DiscoItems *discoTask;
+
+ m_connected = true;
+
+ // now execute command
+ switch ( m_command )
+ {
+ case ListDir: // list a directory
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Listing directory..." << endl;
+ discoTask = new XMPP::JT_DiscoItems ( m_jabberClient->rootTask () );
+ connect ( discoTask, SIGNAL ( finished () ), this, SLOT ( slotQueryFinished () ) );
+ discoTask->get ( m_host );
+ discoTask->go ( true );
+ break;
+
+ case Get: // retrieve an item
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Retrieving item..." << endl;
+ break;
+
+ default: // do nothing by default
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Unknown command " << m_command << endl;
+ break;
+ }
+
+}
+
+void JabberDiscoProtocol::slotQueryFinished ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << "Query task finished" << endl;
+
+ XMPP::JT_DiscoItems * task = (XMPP::JT_DiscoItems *) sender ();
+
+ if (!task->success ())
+ {
+ error ( KIO::ERR_COULD_NOT_READ, "" );
+ return;
+ }
+
+ XMPP::DiscoList::const_iterator itemsEnd = task->items().end ();
+ for (XMPP::DiscoList::const_iterator it = task->items().begin (); it != itemsEnd; ++it)
+ {
+ KIO::UDSAtom atom;
+ KIO::UDSEntry entry;
+
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = (*it).jid().userHost ();
+ entry.prepend ( atom );
+
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 0;
+ entry.prepend ( atom );
+
+ atom.m_uds = KIO::UDS_LINK_DEST;
+ atom.m_str = (*it).name ();
+ entry.prepend ( atom );
+
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str = "inode/directory";
+ entry.prepend ( atom );
+
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 0;
+ entry.prepend ( atom );
+
+ listEntry ( entry, false );
+
+ }
+
+ listEntry ( KIO::UDSEntry(), true );
+
+ finished ();
+
+}
+
+void JabberDiscoProtocol::slotCSDisconnected ()
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Disconnected from Jabber server." << endl;
+
+ /*
+ * We should delete the JabberClient instance here,
+ * but timers etc prevent us from doing so. Iris does
+ * not like to be deleted from a slot.
+ */
+ m_connected = false;
+
+}
+
+void JabberDiscoProtocol::slotCSError ( int errorCode )
+{
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Error in stream signalled." << endl;
+
+ if ( ( errorCode == XMPP::ClientStream::ErrAuth )
+ && ( m_jabberClient->clientStream()->errorCondition () == XMPP::ClientStream::NotAuthorized ) )
+ {
+ kdDebug ( JABBER_DISCO_DEBUG ) << k_funcinfo << "Incorrect password, retrying." << endl;
+
+ KIO::AuthInfo authInfo;
+ authInfo.username = m_user;
+ authInfo.password = m_password;
+ if ( openPassDlg ( authInfo, i18n ( "The login details are incorrect. Do you want to try again?" ) ) )
+ {
+ m_user = authInfo.username;
+ m_password = authInfo.password;
+ closeConnection ();
+ openConnection ();
+ }
+ else
+ {
+ closeConnection ();
+ error ( KIO::ERR_COULD_NOT_AUTHENTICATE, "" );
+ }
+ }
+ else
+ {
+ closeConnection ();
+ error ( KIO::ERR_CONNECTION_BROKEN, "" );
+ }
+
+}
+
+bool breakEventLoop = false;
+
+class EventLoopThread : public QThread
+{
+public:
+ void run ();
+};
+
+void EventLoopThread::run ()
+{
+
+ while ( true )
+ {
+ qApp->processEvents ();
+ msleep ( 100 );
+
+ if ( breakEventLoop )
+ break;
+ }
+
+}
+
+void JabberDiscoProtocol::dispatchLoop ()
+{
+
+ EventLoopThread eventLoopThread;
+
+ eventLoopThread.start ();
+ SlaveBase::dispatchLoop ();
+ breakEventLoop = true;
+ eventLoopThread.wait ();
+
+}
+
+extern "C"
+{
+ KDE_EXPORT int kdemain(int argc, char **argv);
+}
+
+
+int kdemain ( int argc, char **argv )
+{
+ KApplication app(argc, argv, "kio_jabberdisco", false, true);
+
+ kdDebug(JABBER_DISCO_DEBUG) << k_funcinfo << endl;
+
+ if ( argc != 4 )
+ {
+ kdDebug(JABBER_DISCO_DEBUG) << "Usage: kio_jabberdisco protocol domain-socket1 domain-socket2" << endl;
+ exit(-1);
+ }
+
+ JabberDiscoProtocol slave ( argv[2], argv[3] );
+ slave.dispatchLoop ();
+
+ return 0;
+}
+
+#include "jabberdisco.moc"
diff --git a/kopete/protocols/jabber/kioslave/jabberdisco.h b/kopete/protocols/jabber/kioslave/jabberdisco.h
new file mode 100644
index 00000000..f2f6d78d
--- /dev/null
+++ b/kopete/protocols/jabber/kioslave/jabberdisco.h
@@ -0,0 +1,82 @@
+
+/***************************************************************************
+ Jabber Service Discovery KIO Slave
+ -------------------
+ begin : Wed June 1 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2005 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef _JABBERDISCO_H_
+#define _JABBERDISCO_H_
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qcstring.h>
+
+#include <kurl.h>
+#include <kio/global.h>
+#include <kio/slavebase.h>
+#include <jabberclient.h>
+
+#define JABBER_DISCO_DEBUG 0
+
+class JabberClient;
+
+class JabberDiscoProtocol : public QObject, public KIO::SlaveBase
+{
+
+Q_OBJECT
+
+public:
+ JabberDiscoProtocol ( const QCString &pool_socket, const QCString &app_socket );
+ virtual ~JabberDiscoProtocol ();
+
+ void setHost ( const QString &host, int port, const QString &user, const QString &pass );
+
+ void openConnection ();
+ void closeConnection ();
+
+ void slave_status ();
+
+ void get ( const KURL &url );
+ void listDir ( const KURL &url );
+ void mimetype ( const KURL &url );
+
+ void dispatchLoop ();
+
+private slots:
+ void slotClientDebugMessage ( const QString &msg );
+ void slotHandleTLSWarning ( int validityResult );
+ void slotClientError ( JabberClient::ErrorCode errorCode );
+ void slotConnected ();
+ void slotCSDisconnected ();
+ void slotCSError ( int error );
+
+ void slotQueryFinished ();
+
+private:
+ enum CommandType { Get, ListDir };
+
+ QString m_host, m_user, m_password;
+ int m_port;
+ KURL m_url;
+ bool m_connected;
+
+ CommandType m_command;
+
+ JabberClient *m_jabberClient;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/kioslave/jabberdisco.protocol b/kopete/protocols/jabber/kioslave/jabberdisco.protocol
new file mode 100644
index 00000000..01237e73
--- /dev/null
+++ b/kopete/protocols/jabber/kioslave/jabberdisco.protocol
@@ -0,0 +1,53 @@
+[Protocol]
+exec=kio_jabberdisco
+protocol=jabber
+input=none
+output=filesystem
+reading=true
+writing=false
+makedir=false
+linking=false
+moving=false
+Icon=remote
+Description=A KIO slave for Jabber Service Discovery
+Description[be]=Модуль kioslave Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ ÑервіÑаў Jabber
+Description[bn]=Jabber সারà§à¦­à¦¿à¦¸ ডিসকভারির জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ কে-আই-ও সà§à¦²à§‡à¦­
+Description[bs]=KIO slave za otkrivanje Jabber servisa
+Description[ca]=Un esclau KIO pel servei de de descoberta del Jabber
+Description[cs]=Pomocný protokol pro zjišťování služeb Jabber
+Description[da]=En kioslave til at opdage jabber service
+Description[de]=Ein Ein-/Ausgabemodul zum Auffinden von Jabber-Diensten
+Description[el]=Ένα kioslave για την ανίχνευση υπηÏεσίας Jabber
+Description[es]=Un «kioslave» para el servicio de descubrimiento jabber
+Description[et]=Jabberi teenuste tuvastamise KIO-moodul
+Description[eu]=Jabber aurkikuntza zerbitzureako KIO morroi bat
+Description[fa]=یک پیرو KIO برای خدمت اکتشاÙÛŒ Jabber
+Description[fr]=Un module d'entrée / sortie pour la recherche de service Jabber
+Description[gl]=Un KIO slave para Jabber Service Discovery
+Description[hu]=KDE-protokoll a Jabber szolgáltatáskereső használatához
+Description[is]=kioslave fyrir Jabber þjónustu uppgötvun
+Description[it]=Un KIO slave per il servizio di discovery per Jabber
+Description[ja]=Jabber Service Discovery 㮠KIO スレーブ
+Description[ka]=KIO slave Jabber სერვისის დირექტáƒáƒ áƒ˜áƒ˜áƒ¡áƒ—ვის
+Description[kk]=Jabber қызметін байқау KIO slave қызметі
+Description[km]=KIO slave មួយ​សម្រាប់​របក​គំហើញ​សáŸážœáž¶ Jabber
+Description[lt]=Priedas (kioslave) FISH protokolui
+Description[nb]=En kioslave for Jabber tjenestesøk
+Description[nds]=En In-/Utgaavmoduul för't Finnen vun Jabber-Deensten
+Description[ne]=जà¥à¤¯à¤¾à¤¬à¤° सेवा खोजीका लागि कियो सà¥à¤²à¤¾à¤­
+Description[nl]=Een kioslave voor Jabber Service Discovery
+Description[nn]=Ein KIO-slave for Jabber-tenesteoppdaging
+Description[pl]=Wtyczka protokołu KIO dla usługi odkrywania usług Jabbera (Jabber Service Discovery)
+Description[pt]=Um 'kioslave' para a Descoberta de Serviços do Jabber
+Description[pt_BR]=Um KIO-Slave para a descoberta de serviço do Jabber
+Description[ru]=Обработчик KIO Ð´Ð»Ñ Ð¾Ð±Ð½Ð°Ñ€ÑƒÐ¶ÐµÐ½Ð¸Ñ Ñлужб Jabber
+Description[sk]=KIO otrok pre Jabber Service Discovery
+Description[sl]=KIO slave za odkrivanje storitev za Jabber
+Description[sr]=KIO Ñлуга за Jabber Service Discovery
+Description[sr@Latn]=KIO sluga za Jabber Service Discovery
+Description[sv]=En I/O-slav för Jabber tjänstupptäckt
+Description[tr]=Jabber Servis Bulucu için KIOSlave
+Description[uk]=Підлеглий Ð’/Ð’ Ð´Ð»Ñ Ð²Ð¸ÑÐ²Ð»ÐµÐ½Ð½Ñ Ñлужби Jabber
+Description[zh_CN]=Jabber æœåŠ¡å‘现的 KIO slave
+Description[zh_HK]=ç”¨æ–¼ç™¼ç¾ Jabber æœå‹™çš„ KIO slave
+Description[zh_TW]=Jabber æœå‹™çš„ kioslave
diff --git a/kopete/protocols/jabber/kopete_jabber.desktop b/kopete/protocols/jabber/kopete_jabber.desktop
new file mode 100644
index 00000000..28c1f89d
--- /dev/null
+++ b/kopete/protocols/jabber/kopete_jabber.desktop
@@ -0,0 +1,79 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=jabber_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_jabber
+X-Kopete-Messaging-Protocol=messaging/xmpp
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_jabber
+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=Jabber
+Name[hi]=जैबर
+Name[ne]=जà¥à¤¯à¤¾à¤¬à¤°
+Name[pa]=ਜੱਬਰ
+Name[ta]=ஜாபரà¯
+Comment=Protocol to connect to Jabber
+Comment[ar]=البروتوكول سيتصل بـ Jabber
+Comment[be]=Пратакол Jabber
+Comment[bg]=Протокол за връзка Ñ Jabber
+Comment[bn]=Jabber-ঠসংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh Jabber
+Comment[bs]=Jabber protokol
+Comment[ca]=Protocol per a connectar-se a Jabber
+Comment[cs]=Protokol k připojení k Jabberu
+Comment[cy]=Protocol i gysylltu â Jabber
+Comment[da]=Protokol til at forbinde til Jabber
+Comment[de]=Protokoll zur Verbindung mit Jabber
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο Jabber
+Comment[es]=Protocolo de conexión con Jabber
+Comment[et]=Protokoll ühendumiseks Jabberiga
+Comment[eu]=Jabber-era konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال به Jabber
+Comment[fi]=Yhteyskäytäntö Jabber-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur Jabber
+Comment[ga]=Prótacal chun ceangal le Jabber
+Comment[gl]=Protocolo para se conectar a Jabber
+Comment[he]=פרוטוקול התחברות ל- Jabber
+Comment[hi]=जैबर से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na Jabber
+Comment[hu]=Protokoll a Jabber használatához
+Comment[is]=Samskiptamáti til að tengjast Jabber
+Comment[it]=Protocollo per connessione a Jabber
+Comment[ja]=Jabber ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=Jabberთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Jabber-ге қоÑылу протоколы
+Comment[km]=ពិធីការ​ភ្ជាប់​ទៅ Jabber
+Comment[lt]=Protokolas prisijungimui prie Jabber
+Comment[mk]=Протокол за поврзување на Jabber
+Comment[nb]=Protokoll for å koble til Jabber
+Comment[nds]=Protokoll för't Tokoppeln na Jabber
+Comment[ne]=जà¥à¤¯à¤¾à¤¬à¤°à¤®à¤¾ जडान गरà¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor Jabber
+Comment[nn]=Protokoll for å kopla til Jabber
+Comment[pl]=Protokół połączenia z serwerem Jabbera
+Comment[pt]=Um protocolo para se ligar ao Jabber
+Comment[pt_BR]=Protocolo para conexão ao Jabber
+Comment[ro]=Protocol de conectare la Jabber
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Jabber
+Comment[sk]=Protokol pre pripojenie k Jabber
+Comment[sl]=Protokol za povezavo na Jabber
+Comment[sr]=Протокол за повезивање на Jabber
+Comment[sr@Latn]=Protokol za povezivanje na Jabber
+Comment[sv]=Protokoll för att ansluta till Jabber
+Comment[ta]=ஜாபரà¯à®Ÿà®©à¯ இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба Jabber
+Comment[tr]=Jabber'e bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Jabber
+Comment[uz]=Jabber uchun protokol
+Comment[uz@cyrillic]=Jabber учун протокол
+Comment[zh_CN]=连接到 Jabber åè®®
+Comment[zh_HK]=用來連接至 Jabber 的通訊å”定
+Comment[zh_TW]=連到 Jabber çš„å”定
+
diff --git a/kopete/protocols/jabber/libiris/001_last_activity.patch b/kopete/protocols/jabber/libiris/001_last_activity.patch
new file mode 100644
index 00000000..24673e80
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/001_last_activity.patch
@@ -0,0 +1,113 @@
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (revision 419672)
++++ iris/xmpp-im/xmpp_tasks.h (working copy)
+@@ -195,6 +195,29 @@
+ Private *d;
+ };
+
++ class JT_GetLastActivity : public Task
++ {
++ Q_OBJECT
++ public:
++ JT_GetLastActivity(Task *);
++ ~JT_GetLastActivity();
++
++ void get(const Jid &);
++
++ int seconds() const;
++ const QString &message() const;
++
++ void onGo();
++ bool take(const QDomElement &x);
++
++ private:
++ class Private;
++ Private *d;
++
++ QDomElement iq;
++ Jid jid;
++ };
++
+ class JT_GetServices : public Task
+ {
+ Q_OBJECT
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 419672)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -773,6 +773,74 @@
+
+
+ //----------------------------------------------------------------------------
++// JT_GetLastActivity
++//----------------------------------------------------------------------------
++class JT_GetLastActivity::Private
++{
++public:
++ Private() {}
++
++ int seconds;
++ QString message;
++};
++
++JT_GetLastActivity::JT_GetLastActivity(Task *parent)
++:Task(parent)
++{
++ d = new Private;
++}
++
++JT_GetLastActivity::~JT_GetLastActivity()
++{
++ delete d;
++}
++
++void JT_GetLastActivity::get(const Jid &j)
++{
++ jid = j;
++ iq = createIQ(doc(), "get", jid.full(), id());
++ QDomElement query = doc()->createElement("query");
++ query.setAttribute("xmlns", "jabber:iq:last");
++ iq.appendChild(query);
++}
++
++int JT_GetLastActivity::seconds() const
++{
++ return d->seconds;
++}
++
++const QString &JT_GetLastActivity::message() const
++{
++ return d->message;
++}
++
++void JT_GetLastActivity::onGo()
++{
++ send(iq);
++}
++
++bool JT_GetLastActivity::take(const QDomElement &x)
++{
++ if(!iqVerify(x, jid, id()))
++ return false;
++
++ if(x.attribute("type") == "result") {
++ QDomElement q = queryTag(x);
++
++ d->message = q.text();
++ bool ok;
++ d->seconds = q.attribute("seconds").toInt(&ok);
++
++ setSuccess(ok);
++ }
++ else {
++ setError(x);
++ }
++
++ return true;
++}
++
++//----------------------------------------------------------------------------
+ // JT_GetServices
+ //----------------------------------------------------------------------------
+ JT_GetServices::JT_GetServices(Task *parent)
diff --git a/kopete/protocols/jabber/libiris/002_offline_event.patch b/kopete/protocols/jabber/libiris/002_offline_event.patch
new file mode 100644
index 00000000..dfaa1f8e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/002_offline_event.patch
@@ -0,0 +1,17 @@
+? 002_offline_event.patch
+Index: iris/xmpp-im/types.cpp
+===================================================================
+RCS file: /home/kde/kdenetwork/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp,v
+retrieving revision 1.3
+diff -u -p -r1.3 types.cpp
+--- iris/xmpp-im/types.cpp 21 May 2004 14:35:44 -0000 1.3
++++ iris/xmpp-im/types.cpp 5 Feb 2005 21:04:44 -0000
+@@ -639,6 +639,8 @@ bool Message::fromStanza(const Stanza &s
+ d->eventList += ComposingEvent;
+ else if (evtag == "delivered")
+ d->eventList += DeliveredEvent;
++ else if (evtag == "offline")
++ d->eventList += OfflineEvent;
+ }
+ if (d->eventList.isEmpty())
+ d->eventList += CancelEvent;
diff --git a/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch b/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch
new file mode 100644
index 00000000..d4b0e285
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/003_case_insensitive_jid.patch
@@ -0,0 +1,14 @@
+Index: iris/xmpp-core/jid.cpp
+===================================================================
+--- iris/xmpp-core/jid.cpp (revision 469141)
++++ iris/xmpp-core/jid.cpp (working copy)
+@@ -233,6 +233,9 @@
+ b = d;
+ else
+ b = n + '@' + d;
++
++ b=b.lower(); // JID are not case sensitive
++
+ if(r.isEmpty())
+ f = b;
+ else
diff --git a/kopete/protocols/jabber/libiris/004_xhtml_im.patch b/kopete/protocols/jabber/libiris/004_xhtml_im.patch
new file mode 100644
index 00000000..990ab4f7
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/004_xhtml_im.patch
@@ -0,0 +1,266 @@
+Index: iris/include/xmpp.h
+===================================================================
+--- iris/include/xmpp.h (revision 470311)
++++ iris/include/xmpp.h (working copy)
+@@ -318,8 +318,11 @@
+
+ QDomDocument & doc() const;
+ QString baseNS() const;
++ QString xhtmlImNS() const;
++ QString xhtmlNS() const;
+ QDomElement createElement(const QString &ns, const QString &tagName);
+ QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text);
++ QDomElement createXHTMLElement(const QString &xHTML);
+ void appendChild(const QDomElement &e);
+
+ Kind kind() const;
+@@ -372,6 +375,8 @@
+
+ virtual QDomDocument & doc() const=0;
+ virtual QString baseNS() const=0;
++ virtual QString xhtmlImNS() const=0;
++ virtual QString xhtmlNS() const=0;
+ virtual bool old() const=0;
+
+ virtual void close()=0;
+@@ -479,6 +484,8 @@
+ // reimplemented
+ QDomDocument & doc() const;
+ QString baseNS() const;
++ QString xhtmlImNS() const;
++ QString xhtmlNS() const;
+ bool old() const;
+
+ void close();
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (revision 470311)
++++ iris/include/im.h (working copy)
+@@ -65,6 +65,7 @@
+ QString lang() const;
+ QString subject(const QString &lang="") const;
+ QString body(const QString &lang="") const;
++ QString xHTMLBody(const QString &lang="") const;
+ QString thread() const;
+ Stanza::Error error() const;
+
+@@ -75,6 +76,7 @@
+ void setLang(const QString &s);
+ void setSubject(const QString &s, const QString &lang="");
+ void setBody(const QString &s, const QString &lang="");
++ void setXHTMLBody(const QString &s, const QString &lang="", const QString &attr = "");
+ void setThread(const QString &s);
+ void setError(const Stanza::Error &err);
+
+@@ -286,6 +288,7 @@
+ bool canSearch() const;
+ bool canGroupchat() const;
+ bool canDisco() const;
++ bool canXHTML() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+
+@@ -298,6 +301,7 @@
+ FID_Disco,
+ FID_Gateway,
+ FID_VCard,
++ FID_Xhtml,
+
+ // private Psi actions
+ FID_Add
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 470311)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -19,7 +19,7 @@
+ */
+
+ #include"im.h"
+-
++#include "protocol.h"
+ #include<qmap.h>
+ #include<qapplication.h>
+
+@@ -180,7 +180,8 @@
+ Jid to, from;
+ QString id, type, lang;
+
+- StringMap subject, body;
++ StringMap subject, body, xHTMLBody;
++
+ QString thread;
+ Stanza::Error error;
+
+@@ -279,6 +280,11 @@
+ return d->body[lang];
+ }
+
++QString Message::xHTMLBody(const QString &lang) const
++{
++ return d->xHTMLBody[lang];
++}
++
+ QString Message::thread() const
+ {
+ return d->thread;
+@@ -340,9 +346,16 @@
+ void Message::setBody(const QString &s, const QString &lang)
+ {
+ d->body[lang] = s;
+- //d->flag = false;
+ }
+
++void Message::setXHTMLBody(const QString &s, const QString &lang, const QString &attr)
++{
++ //ugly but needed if s is not a node but a list of leaf
++
++ QString content = "<body xmlns='" + QString(NS_XHTML) + "' "+attr+" >\n" + s +"\n</body>";
++ d->xHTMLBody[lang] = content;
++}
++
+ void Message::setThread(const QString &s)
+ {
+ d->thread = s;
+@@ -489,7 +502,19 @@
+ s.appendChild(e);
+ }
+ }
+-
++ if ( !d->xHTMLBody.isEmpty()) {
++ QDomElement parent = s.createElement(s.xhtmlImNS(), "html");
++ for(it = d->xHTMLBody.begin(); it != d->xHTMLBody.end(); ++it) {
++ const QString &str = it.data();
++ if(!str.isEmpty()) {
++ QDomElement child = s.createXHTMLElement(str);
++ if(!it.key().isEmpty())
++ child.setAttributeNS(NS_XML, "xml:lang", it.key());
++ parent.appendChild(child);
++ }
++ }
++ s.appendChild(parent);
++ }
+ if(d->type == "error")
+ s.setError(d->error);
+
+@@ -591,6 +616,21 @@
+ else if(e.tagName() == "thread")
+ d->thread = e.text();
+ }
++ else if (e.namespaceURI() == s.xhtmlImNS()) {
++ if (e.tagName() == "html") {
++ QDomNodeList htmlNL= e.childNodes();
++ for (unsigned int x = 0; x < htmlNL.count(); x++) {
++ QDomElement i = htmlNL.item(x).toElement();
++
++ if (i.tagName() == "body") {
++ QDomDocument RichText;
++ QString lang = i.attributeNS(NS_XML, "lang", "");
++ RichText.appendChild(i);
++ d-> xHTMLBody[lang] = RichText.toString();
++ }
++ }
++ }
++ }
+ else {
+ //printf("extension element: [%s]\n", e.tagName().latin1());
+ }
+@@ -1418,6 +1458,16 @@
+ return test(ns);
+ }
+
++#define FID_XHTML "http://jabber.org/protocol/xhtml-im"
++bool Features::canXHTML() const
++{
++ QStringList ns;
++
++ ns << FID_XHTML;
++
++ return test(ns);
++}
++
+ #define FID_GROUPCHAT "jabber:iq:conference"
+ bool Features::canGroupchat() const
+ {
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 470311)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -1348,6 +1348,10 @@
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
++ feature.setAttribute("var", "http://jabber.org/protocol/xhtml-im");
++ query.appendChild(feature);
++
++ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
+ query.appendChild(feature);
+
+Index: iris/xmpp-core/protocol.h
+===================================================================
+--- iris/xmpp-core/protocol.h (revision 470311)
++++ iris/xmpp-core/protocol.h (working copy)
+@@ -35,6 +35,8 @@
+ #define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+ #define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+ #define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
++#define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im"
++#define NS_XHTML "http://www.w3.org/1999/xhtml"
+
+ namespace XMPP
+ {
+Index: iris/xmpp-core/stream.cpp
+===================================================================
+--- iris/xmpp-core/stream.cpp (revision 470311)
++++ iris/xmpp-core/stream.cpp (working copy)
+@@ -293,6 +293,16 @@
+ return d->s->baseNS();
+ }
+
++QString Stanza::xhtmlImNS() const
++{
++ return d->s->xhtmlImNS();
++}
++
++QString Stanza::xhtmlNS() const
++{
++ return d->s->xhtmlNS();
++}
++
+ QDomElement Stanza::createElement(const QString &ns, const QString &tagName)
+ {
+ return d->s->doc().createElementNS(ns, tagName);
+@@ -305,6 +315,16 @@
+ return e;
+ }
+
++QDomElement Stanza::createXHTMLElement(const QString &xHTML)
++{
++ QDomDocument doc;
++
++ doc.setContent(xHTML, true);
++ QDomElement root = doc.documentElement();
++ //QDomElement e;
++ return (root);
++}
++
+ void Stanza::appendChild(const QDomElement &e)
+ {
+ d->e.appendChild(e);
+@@ -861,6 +881,16 @@
+ return NS_CLIENT;
+ }
+
++QString ClientStream::xhtmlImNS() const
++{
++ return NS_XHTML_IM;
++}
++
++QString ClientStream::xhtmlNS() const
++{
++ return NS_XHTML;
++}
++
+ void ClientStream::setAllowPlain(bool b)
+ {
+ d->allowPlain = b;
diff --git a/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch b/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch
new file mode 100644
index 00000000..058825db
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/005_join_muc_with_password.patch
@@ -0,0 +1,163 @@
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (révision 498969)
++++ iris/include/im.h (copie de travail)
+@@ -607,6 +607,7 @@
+ FileTransferManager *fileTransferManager() const;
+
+ bool groupChatJoin(const QString &host, const QString &room, const QString &nick);
++ bool groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password);
+ void groupChatSetStatus(const QString &host, const QString &room, const Status &);
+ void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &);
+ void groupChatLeave(const QString &host, const QString &room);
+Index: iris/xmpp-im/client.cpp
+===================================================================
+--- iris/xmpp-im/client.cpp (révision 498969)
++++ iris/xmpp-im/client.cpp (copie de travail)
+@@ -315,6 +315,35 @@
+ return true;
+ }
+
++bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password)
++{
++ Jid jid(room + "@" + host + "/" + nick);
++ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
++ GroupChat &i = *it;
++ if(i.j.compare(jid, false)) {
++ // if this room is shutting down, then free it up
++ if(i.status == GroupChat::Closing)
++ it = d->groupChatList.remove(it);
++ else
++ return false;
++ }
++ else
++ ++it;
++ }
++
++ debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
++ GroupChat i;
++ i.j = jid;
++ i.status = GroupChat::Connecting;
++ d->groupChatList += i;
++
++ JT_MucPresence *j = new JT_MucPresence(rootTask());
++ j->pres(jid, Status(), password);
++ j->go(true);
++
++ return true;
++}
++
+ void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s)
+ {
+ Jid jid(room + "@" + host);
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (révision 498969)
++++ iris/xmpp-im/xmpp_tasks.h (copie de travail)
+@@ -439,6 +439,26 @@
+ class Private;
+ Private *d;
+ };
++
++ class JT_MucPresence : public Task
++ {
++ Q_OBJECT
++ public:
++ JT_MucPresence(Task *parent);
++ ~JT_MucPresence();
++
++ void pres(const Status &);
++ void pres(const Jid &, const Status &, const QString &password);
++
++ void onGo();
++
++ private:
++ QDomElement tag;
++ int type;
++
++ class Private;
++ Private *d;
++ };
+ }
+
+ #endif
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (révision 498969)
++++ iris/xmpp-im/xmpp_tasks.cpp (copie de travail)
+@@ -1956,3 +1956,75 @@
+ return true;
+ }
+
++//----------------------------------------------------------------------------
++// JT_MucPresence
++//----------------------------------------------------------------------------
++JT_MucPresence::JT_MucPresence(Task *parent)
++:Task(parent)
++{
++ type = -1;
++}
++
++JT_MucPresence::~JT_MucPresence()
++{
++}
++
++void JT_MucPresence::pres(const Status &s)
++{
++ type = 0;
++
++ tag = doc()->createElement("presence");
++ if(!s.isAvailable()) {
++ tag.setAttribute("type", "unavailable");
++ if(!s.status().isEmpty())
++ tag.appendChild(textTag(doc(), "status", s.status()));
++ }
++ else {
++ if(s.isInvisible())
++ tag.setAttribute("type", "invisible");
++
++ if(!s.show().isEmpty())
++ tag.appendChild(textTag(doc(), "show", s.show()));
++ if(!s.status().isEmpty())
++ tag.appendChild(textTag(doc(), "status", s.status()));
++
++ tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
++
++ if(!s.keyID().isEmpty()) {
++ QDomElement x = textTag(doc(), "x", s.keyID());
++ x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
++ tag.appendChild(x);
++ }
++ if(!s.xsigned().isEmpty()) {
++ QDomElement x = textTag(doc(), "x", s.xsigned());
++ x.setAttribute("xmlns", "jabber:x:signed");
++ tag.appendChild(x);
++ }
++
++ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
++ QDomElement c = doc()->createElement("c");
++ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
++ c.setAttribute("node",s.capsNode());
++ c.setAttribute("ver",s.capsVersion());
++ if (!s.capsExt().isEmpty())
++ c.setAttribute("ext",s.capsExt());
++ tag.appendChild(c);
++ }
++ }
++}
++
++void JT_MucPresence::pres(const Jid &to, const Status &s, const QString &password)
++{
++ pres(s);
++ tag.setAttribute("to", to.full());
++ QDomElement x = textTag(doc(), "x", s.xsigned());
++ x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
++ x.appendChild( textTag(doc(), "password", password.latin1()) );
++ tag.appendChild(x);
++}
++
++void JT_MucPresence::onGo()
++{
++ send(tag);
++ setSuccess();
++}
diff --git a/kopete/protocols/jabber/libiris/006_private_storage.patch b/kopete/protocols/jabber/libiris/006_private_storage.patch
new file mode 100644
index 00000000..288d24c5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/006_private_storage.patch
@@ -0,0 +1,130 @@
+Index: iris/xmpp-im/xmpp_tasks.h
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.h (revision 499691)
++++ iris/xmpp-im/xmpp_tasks.h (working copy)
+@@ -459,6 +459,27 @@
+ class Private;
+ Private *d;
+ };
++
++ class JT_PrivateStorage : public Task
++ {
++ Q_OBJECT
++ public:
++ JT_PrivateStorage(Task *parent);
++ ~JT_PrivateStorage();
++
++ void set(const QDomElement &);
++ void get(const QString &tag, const QString& xmlns);
++
++ QDomElement element();
++
++ void onGo();
++ bool take(const QDomElement &);
++
++ private:
++ class Private;
++ Private *d;
++ };
++
+ }
+
+ #endif
+Index: iris/xmpp-im/xmpp_tasks.cpp
+===================================================================
+--- iris/xmpp-im/xmpp_tasks.cpp (revision 499691)
++++ iris/xmpp-im/xmpp_tasks.cpp (working copy)
+@@ -2028,3 +2028,93 @@
+ send(tag);
+ setSuccess();
+ }
++
++
++//----------------------------------------------------------------------------
++// JT_PrivateStorage
++//----------------------------------------------------------------------------
++class JT_PrivateStorage::Private
++{
++ public:
++ Private() : type(-1) {}
++
++ QDomElement iq;
++ QDomElement elem;
++ int type;
++};
++
++JT_PrivateStorage::JT_PrivateStorage(Task *parent)
++ :Task(parent)
++{
++ d = new Private;
++}
++
++JT_PrivateStorage::~JT_PrivateStorage()
++{
++ delete d;
++}
++
++void JT_PrivateStorage::get(const QString& tag, const QString& xmlns)
++{
++ d->type = 0;
++ d->iq = createIQ(doc(), "get" , QString() , id() );
++ QDomElement query = doc()->createElement("query");
++ query.setAttribute("xmlns", "jabber:iq:private");
++ d->iq.appendChild(query);
++ QDomElement s = doc()->createElement(tag);
++ if(!xmlns.isEmpty())
++ s.setAttribute("xmlns", xmlns);
++ query.appendChild(s);
++}
++
++void JT_PrivateStorage::set(const QDomElement& element)
++{
++ d->type = 1;
++ d->elem=element;
++ QDomNode n=doc()->importNode(element,true);
++
++ d->iq = createIQ(doc(), "set" , QString() , id() );
++ QDomElement query = doc()->createElement("query");
++ query.setAttribute("xmlns", "jabber:iq:private");
++ d->iq.appendChild(query);
++ query.appendChild(n);
++}
++
++void JT_PrivateStorage::onGo()
++{
++ send(d->iq);
++}
++
++bool JT_PrivateStorage::take(const QDomElement &x)
++{
++ QString to = client()->host();
++ if(!iqVerify(x, to, id()))
++ return false;
++
++ if(x.attribute("type") == "result") {
++ if(d->type == 0) {
++ QDomElement q = queryTag(x);
++ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
++ QDomElement i = n.toElement();
++ if(i.isNull())
++ continue;
++ d->elem=i;
++ break;
++ }
++ }
++ setSuccess();
++ return true;
++ }
++ else {
++ setError(x);
++ }
++
++ return true;
++}
++
++
++QDomElement JT_PrivateStorage::element( )
++{
++ return d->elem;
++}
++
diff --git a/kopete/protocols/jabber/libiris/007_chatstates.patch b/kopete/protocols/jabber/libiris/007_chatstates.patch
new file mode 100644
index 00000000..af32728c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/007_chatstates.patch
@@ -0,0 +1,132 @@
+Index: iris/include/im.h
+===================================================================
+--- iris/include/im.h (revision 525193)
++++ iris/include/im.h (working copy)
+@@ -49,7 +49,7 @@
+ typedef QValueList<Url> UrlList;
+ typedef QMap<QString, QString> StringMap;
+ typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent,
+- ComposingEvent, CancelEvent } MsgEvent;
++ ComposingEvent, CancelEvent, InactiveEvent, GoneEvent } MsgEvent;
+
+ class Message
+ {
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 525193)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -544,28 +544,49 @@
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
++ else
++ s.appendChild( s.createElement(NS_CHATSTATES , "active" ) );
+
++ bool need_x_event=false;
+ for(QValueList<MsgEvent>::ConstIterator ev = d->eventList.begin(); ev != d->eventList.end(); ++ev) {
+ switch (*ev) {
+ case OfflineEvent:
+ x.appendChild(s.createElement("jabber:x:event", "offline"));
++ need_x_event=true;
+ break;
+ case DeliveredEvent:
+ x.appendChild(s.createElement("jabber:x:event", "delivered"));
++ need_x_event=true;
+ break;
+ case DisplayedEvent:
+ x.appendChild(s.createElement("jabber:x:event", "displayed"));
++ need_x_event=true;
+ break;
+ case ComposingEvent:
+ x.appendChild(s.createElement("jabber:x:event", "composing"));
++ need_x_event=true;
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "composing" ) );
+ break;
+ case CancelEvent:
+- // Add nothing
++ need_x_event=true;
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
++ case InactiveEvent:
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
++ break;
++ case GoneEvent:
++ if (d->body.isEmpty())
++ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
++ break;
+ }
+ }
+- s.appendChild(x);
+- }
++ if(need_x_event) //we don't need to have the (empty) x:event element if this is only <gone> or <inactive>
++ s.appendChild(x);
++ }
++
+
+ // xencrypted
+ if(!d->xencrypted.isEmpty())
+@@ -595,6 +616,7 @@
+ d->subject.clear();
+ d->body.clear();
+ d->thread = QString();
++ d->eventList.clear();
+
+ QDomElement root = s.element();
+
+@@ -631,6 +653,33 @@
+ }
+ }
+ }
++ else if (e.namespaceURI() == NS_CHATSTATES)
++ {
++ if(e.tagName() == "active")
++ {
++ //like in JEP-0022 we let the client know that we can receive ComposingEvent
++ // (we can do that according to §4.6 of the JEP-0085)
++ d->eventList += ComposingEvent;
++ d->eventList += InactiveEvent;
++ d->eventList += GoneEvent;
++ }
++ else if (e.tagName() == "composing")
++ {
++ d->eventList += ComposingEvent;
++ }
++ else if (e.tagName() == "paused")
++ {
++ d->eventList += CancelEvent;
++ }
++ else if (e.tagName() == "inactive")
++ {
++ d->eventList += InactiveEvent;
++ }
++ else if (e.tagName() == "gone")
++ {
++ d->eventList += GoneEvent;
++ }
++ }
+ else {
+ //printf("extension element: [%s]\n", e.tagName().latin1());
+ }
+@@ -664,7 +713,6 @@
+ }
+
+ // events
+- d->eventList.clear();
+ nl = root.elementsByTagNameNS("jabber:x:event", "x");
+ if (nl.count()) {
+ nl = nl.item(0).childNodes();
+Index: iris/xmpp-core/protocol.h
+===================================================================
+--- iris/xmpp-core/protocol.h (revision 525193)
++++ iris/xmpp-core/protocol.h (working copy)
+@@ -37,6 +37,7 @@
+ #define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
+ #define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im"
+ #define NS_XHTML "http://www.w3.org/1999/xhtml"
++#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
+
+ namespace XMPP
+ {
diff --git a/kopete/protocols/jabber/libiris/008_chatstatesfix.patch b/kopete/protocols/jabber/libiris/008_chatstatesfix.patch
new file mode 100644
index 00000000..63a4f680
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/008_chatstatesfix.patch
@@ -0,0 +1,38 @@
+Index: iris/xmpp-im/types.cpp
+===================================================================
+--- iris/xmpp-im/types.cpp (revision 526236)
++++ iris/xmpp-im/types.cpp (working copy)
+@@ -544,7 +544,7 @@
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
+- else
++ else if (d->type=="chat" || d->type=="groupchat")
+ s.appendChild( s.createElement(NS_CHATSTATES , "active" ) );
+
+ bool need_x_event=false;
+@@ -565,20 +565,20 @@
+ case ComposingEvent:
+ x.appendChild(s.createElement("jabber:x:event", "composing"));
+ need_x_event=true;
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "composing" ) );
+ break;
+ case CancelEvent:
+ need_x_event=true;
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
+ case InactiveEvent:
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
+ break;
+ case GoneEvent:
+- if (d->body.isEmpty())
++ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
+ break;
+ }
diff --git a/kopete/protocols/jabber/libiris/Makefile.am b/kopete/protocols/jabber/libiris/Makefile.am
new file mode 100644
index 00000000..a80d204c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = iris qca cutestuff
+
diff --git a/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING b/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING
new file mode 100644
index 00000000..1fd42d3a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/README_BEFORE_COMMITTING
@@ -0,0 +1,21 @@
+This library is the xmpp backend also used in Psi. (http://psi.affinix.com)
+The main author is Justin Karneges (infiniti@affinix.com) and other
+Psi developers, see the Psi homepage for details.
+
+Please DO NOT change the source unless really necessary. This is a
+third-party library and any change will make synching very hard in the
+future. It is best to send patches upstream so they'll end up in the
+main tree. We will benefit from them at the next synch point.
+
+If you really really need to make a change to one of the source files,
+please make sure to commit a diff to the original file in this directory in
+the form of 001_your_fix_name.patch. Always pick the next free number
+for your patch, the version found in this directory is meant to have
+all patches applied in order. When committing, CCMAIL kopete-devel@kde.org.
+
+Changes to the Makefile.am files are fine and require no diffs, since Psi
+uses qmake.
+
+This library depends on: libidn (compile time), qca-tls (runtime)
+
+27.02.2004, Till Gerken (till@tantalo.net)
diff --git a/kopete/protocols/jabber/libiris/cutestuff/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/Makefile.am
new file mode 100644
index 00000000..8f579310
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = network util
diff --git a/kopete/protocols/jabber/libiris/cutestuff/README b/kopete/protocols/jabber/libiris/cutestuff/README
new file mode 100644
index 00000000..c4509acc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/README
@@ -0,0 +1,13 @@
+iconset - generic classes for handling iconsets / animations
+idle - detecting desktop idle
+input - making life easier with text input (including richtext)
+openpgp - pgp/gpg classes
+richtext - richtext parsing function, xhtml conversion
+ssl - SSL
+tray - desktop tray icon
+util - various things, see util/TODO
+globalaccel - global hotkeys
+network - sockets, servers, dns, and proxies
+sasl - SASL library
+xmlsec - XML Encryption
+crash - generates some (hopefully useful) feedback when program crashes
diff --git a/kopete/protocols/jabber/libiris/cutestuff/TODO b/kopete/protocols/jabber/libiris/cutestuff/TODO
new file mode 100644
index 00000000..e897c854
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/TODO
@@ -0,0 +1,25 @@
+test:
+ httppoll
+ sasl
+ xmlenc
+
+code:
+ bsocket: 'maintain' internal sockets even after destruct (till flush)
+ qssl: server support
+ securestream: wrap QSSLFilter as ByteStream
+ qrandom: better randomness (use /dev/urandom on unix, srand on windows)
+ floating TODOs in gnupg, gpgproc ?
+ import misha's code
+ finish globalaccel
+ finish dirwatch
+ trayicon?
+
+port:
+ win32: bconsole
+
+document:
+ sha1
+ servsock
+ srvresolver
+ bsocket
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
new file mode 100644
index 00000000..5e370089
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/Makefile.am
@@ -0,0 +1,16 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcutestuff_network.la
+INCLUDES = -I$(srcdir)/../util -I$(srcdir)/../../qca/src $(all_includes)
+
+libcutestuff_network_la_SOURCES = \
+ bsocket.cpp \
+ httpconnect.cpp \
+ httppoll.cpp \
+ ndns.cpp \
+ servsock.cpp \
+ socks.cpp \
+ srvresolver.cpp
+
+KDE_OPTIONS = nofinal
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
new file mode 100644
index 00000000..57e5fe66
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.cpp
@@ -0,0 +1,394 @@
+/*
+ * bsocket.cpp - QSocket wrapper based on Bytestream with SRV DNS support
+ * 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"bsocket.h"
+
+#include<qcstring.h>
+#include<qsocket.h>
+#include<qdns.h>
+#include<qguardedptr.h>
+#include"safedelete.h"
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+#include"srvresolver.h"
+
+#ifdef BS_DEBUG
+#include<stdio.h>
+#endif
+
+#define READBUFSIZE 65536
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket::Private
+{
+public:
+ Private()
+ {
+ qsock = 0;
+ }
+
+ QSocket *qsock;
+ int state;
+
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+ SrvResolver srv;
+ QString host;
+ int port;
+ SafeDelete sd;
+};
+
+BSocket::BSocket(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+
+ reset();
+}
+
+BSocket::~BSocket()
+{
+ reset(true);
+ delete d;
+}
+
+void BSocket::reset(bool clear)
+{
+ if(d->qsock) {
+ d->qsock->disconnect(this);
+
+ if(!clear && d->qsock->isOpen()) {
+ // move remaining into the local queue
+ QByteArray block(d->qsock->bytesAvailable());
+ d->qsock->readBlock(block.data(), block.size());
+ appendRead(block);
+ }
+
+ d->sd.deleteLater(d->qsock);
+ d->qsock = 0;
+ }
+ else {
+ if(clear)
+ clearReadBuffer();
+ }
+
+ if(d->srv.isBusy())
+ d->srv.stop();
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->state = Idle;
+}
+
+void BSocket::ensureSocket()
+{
+ if(!d->qsock) {
+ d->qsock = new QSocket;
+#if QT_VERSION >= 0x030200
+ d->qsock->setReadBufferSize(READBUFSIZE);
+#endif
+ connect(d->qsock, SIGNAL(hostFound()), SLOT(qs_hostFound()));
+ connect(d->qsock, SIGNAL(connected()), SLOT(qs_connected()));
+ connect(d->qsock, SIGNAL(connectionClosed()), SLOT(qs_connectionClosed()));
+ connect(d->qsock, SIGNAL(delayedCloseFinished()), SLOT(qs_delayedCloseFinished()));
+ connect(d->qsock, SIGNAL(readyRead()), SLOT(qs_readyRead()));
+ connect(d->qsock, SIGNAL(bytesWritten(int)), SLOT(qs_bytesWritten(int)));
+ connect(d->qsock, SIGNAL(error(int)), SLOT(qs_error(int)));
+ }
+}
+
+void BSocket::connectToHost(const QString &host, Q_UINT16 port)
+{
+ reset(true);
+ d->host = host;
+ d->port = port;
+#ifdef NO_NDNS
+ d->state = Connecting;
+ do_connect();
+#else
+ d->state = HostLookup;
+ d->ndns.resolve(d->host);
+#endif
+}
+
+void BSocket::connectToServer(const QString &srv, const QString &type)
+{
+ reset(true);
+ d->state = HostLookup;
+ d->srv.resolve(srv, type, "tcp");
+}
+
+int BSocket::socket() const
+{
+ if(d->qsock)
+ return d->qsock->socket();
+ else
+ return -1;
+}
+
+void BSocket::setSocket(int s)
+{
+ reset(true);
+ ensureSocket();
+ d->state = Connected;
+ d->qsock->setSocket(s);
+}
+
+int BSocket::state() const
+{
+ return d->state;
+}
+
+bool BSocket::isOpen() const
+{
+ if(d->state == Connected)
+ return true;
+ else
+ return false;
+}
+
+void BSocket::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->qsock) {
+ d->qsock->close();
+ d->state = Closing;
+ if(d->qsock->bytesToWrite() == 0)
+ reset();
+ }
+ else {
+ reset();
+ }
+}
+
+void BSocket::write(const QByteArray &a)
+{
+ if(d->state != Connected)
+ return;
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: writing [%d]: {%s}\n", a.size(), cs.data());
+#endif
+ d->qsock->writeBlock(a.data(), a.size());
+}
+
+QByteArray BSocket::read(int bytes)
+{
+ QByteArray block;
+ if(d->qsock) {
+ int max = bytesAvailable();
+ if(bytes <= 0 || bytes > max)
+ bytes = max;
+ block.resize(bytes);
+ d->qsock->readBlock(block.data(), block.size());
+ }
+ else
+ block = ByteStream::read(bytes);
+
+#ifdef BS_DEBUG
+ QCString cs;
+ cs.resize(block.size()+1);
+ memcpy(cs.data(), block.data(), block.size());
+ QString s = QString::fromUtf8(cs);
+ fprintf(stderr, "BSocket: read [%d]: {%s}\n", block.size(), s.latin1());
+#endif
+ return block;
+}
+
+int BSocket::bytesAvailable() const
+{
+ if(d->qsock)
+ return d->qsock->bytesAvailable();
+ else
+ return ByteStream::bytesAvailable();
+}
+
+int BSocket::bytesToWrite() const
+{
+ if(!d->qsock)
+ return 0;
+ return d->qsock->bytesToWrite();
+}
+
+QHostAddress BSocket::address() const
+{
+ if(d->qsock)
+ return d->qsock->address();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::port() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+QHostAddress BSocket::peerAddress() const
+{
+ if(d->qsock)
+ return d->qsock->peerAddress();
+ else
+ return QHostAddress();
+}
+
+Q_UINT16 BSocket::peerPort() const
+{
+ if(d->qsock)
+ return d->qsock->port();
+ else
+ return 0;
+}
+
+void BSocket::srv_done()
+{
+ if(d->srv.failed()) {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ return;
+ }
+
+ d->host = d->srv.resultAddress().toString();
+ d->port = d->srv.resultPort();
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+}
+
+void BSocket::ndns_done()
+{
+#ifndef NO_NDNS
+ if(d->ndns.result()) {
+ d->host = d->ndns.resultString();
+ d->state = Connecting;
+ do_connect();
+ //QTimer::singleShot(0, this, SLOT(do_connect()));
+ //hostFound();
+ }
+ else {
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error resolving hostname.\n");
+#endif
+ error(ErrHostNotFound);
+ }
+#endif
+}
+
+void BSocket::do_connect()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connecting to %s:%d\n", d->host.latin1(), d->port);
+#endif
+ ensureSocket();
+ d->qsock->connectToHost(d->host, d->port);
+}
+
+void BSocket::qs_hostFound()
+{
+ //SafeDeleteLock s(&d->sd);
+}
+
+void BSocket::qs_connected()
+{
+ d->state = Connected;
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connected.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ connected();
+}
+
+void BSocket::qs_connectionClosed()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Connection Closed.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ connectionClosed();
+}
+
+void BSocket::qs_delayedCloseFinished()
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Delayed Close Finished.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+ reset();
+ delayedCloseFinished();
+}
+
+void BSocket::qs_readyRead()
+{
+ SafeDeleteLock s(&d->sd);
+ readyRead();
+}
+
+void BSocket::qs_bytesWritten(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: BytesWritten [%d].\n", x);
+#endif
+ SafeDeleteLock s(&d->sd);
+ bytesWritten(x);
+}
+
+void BSocket::qs_error(int x)
+{
+#ifdef BS_DEBUG
+ fprintf(stderr, "BSocket: Error.\n");
+#endif
+ SafeDeleteLock s(&d->sd);
+
+ // connection error during SRV host connect? try next
+ if(d->state == HostLookup && (x == QSocket::ErrConnectionRefused || x == QSocket::ErrHostNotFound)) {
+ d->srv.next();
+ return;
+ }
+
+ reset();
+ if(x == QSocket::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == QSocket::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == QSocket::ErrSocketRead)
+ error(ErrRead);
+}
+
+// CS_NAMESPACE_END
+
+#include "bsocket.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
new file mode 100644
index 00000000..bedaa54e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/bsocket.h
@@ -0,0 +1,87 @@
+/*
+ * bsocket.h - QSocket wrapper based on Bytestream with SRV DNS support
+ * 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_BSOCKET_H
+#define CS_BSOCKET_H
+
+#include<qobject.h>
+#include<qhostaddress.h>
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class BSocket : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound };
+ enum State { Idle, HostLookup, Connecting, Connected, Closing };
+ BSocket(QObject *parent=0);
+ ~BSocket();
+
+ void connectToHost(const QString &host, Q_UINT16 port);
+ void connectToServer(const QString &srv, const QString &type);
+ int socket() const;
+ void setSocket(int);
+ int state() const;
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // local
+ QHostAddress address() const;
+ Q_UINT16 port() const;
+
+ // remote
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void hostFound();
+ void connected();
+
+private slots:
+ void qs_hostFound();
+ void qs_connected();
+ void qs_connectionClosed();
+ void qs_delayedCloseFinished();
+ void qs_readyRead();
+ void qs_bytesWritten(int);
+ void qs_error(int);
+ void srv_done();
+ void ndns_done();
+ void do_connect();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ void ensureSocket();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
new file mode 100644
index 00000000..c194324a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.cpp
@@ -0,0 +1,369 @@
+/*
+ * httpconnect.cpp - HTTP "CONNECT" proxy
+ * 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"httpconnect.h"
+
+#include<qstringlist.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpConnect::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+
+ bool inHeader;
+ QStringList headerLines;
+
+ int toWrite;
+ bool active;
+};
+
+HttpConnect::HttpConnect(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+HttpConnect::~HttpConnect()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpConnect::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear) {
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ }
+ d->active = false;
+}
+
+void HttpConnect::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool HttpConnect::isOpen() const
+{
+ return d->active;
+}
+
+void HttpConnect::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void HttpConnect::write(const QByteArray &buf)
+{
+ if(d->active)
+ d->sock.write(buf);
+}
+
+QByteArray HttpConnect::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int HttpConnect::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int HttpConnect::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void HttpConnect::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ // connected, now send the request
+ QString s;
+ s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n";
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += "\r\n";
+
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->toWrite = block.size();
+ d->sock.write(block);
+}
+
+void HttpConnect::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void HttpConnect::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void HttpConnect::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Success >>\n");
+#endif
+ d->active = true;
+ connected();
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ return;
+ }
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+ }
+ else {
+ appendRead(block);
+ readyRead();
+ return;
+ }
+}
+
+void HttpConnect::sock_bytesWritten(int x)
+{
+ if(d->toWrite > 0) {
+ int size = x;
+ if(d->toWrite < x)
+ size = d->toWrite;
+ d->toWrite -= size;
+ x -= size;
+ }
+
+ if(d->active && x > 0)
+ bytesWritten(x);
+}
+
+void HttpConnect::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+// CS_NAMESPACE_END
+
+#include "httpconnect.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
new file mode 100644
index 00000000..38129c60
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httpconnect.h
@@ -0,0 +1,67 @@
+/*
+ * httpconnect.h - HTTP "CONNECT" proxy
+ * 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_HTTPCONNECT_H
+#define CS_HTTPCONNECT_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpConnect : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpConnect(QObject *parent=0);
+ ~HttpConnect();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+signals:
+ void connected();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
new file mode 100644
index 00000000..4975d0e5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.cpp
@@ -0,0 +1,666 @@
+/*
+ * httppoll.cpp - HTTP polling proxy
+ * 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"httppoll.h"
+
+#include<qstringlist.h>
+#include<qurl.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"bsocket.h"
+#include"base64.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+#define POLL_KEYS 64
+
+// CS_NAMESPACE_BEGIN
+
+static QByteArray randomArray(int size)
+{
+ QByteArray a(size);
+ for(int n = 0; n < size; ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ return a;
+}
+
+//----------------------------------------------------------------------------
+// HttpPoll
+//----------------------------------------------------------------------------
+static QString hpk(int n, const QString &s)
+{
+ if(n == 0)
+ return s;
+ else
+ return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
+}
+
+class HttpPoll::Private
+{
+public:
+ Private() {}
+
+ HttpProxyPost http;
+ QString host;
+ int port;
+ QString user, pass;
+ QString url;
+ bool use_proxy;
+
+ QByteArray out;
+
+ int state;
+ bool closing;
+ QString ident;
+
+ QTimer *t;
+
+ QString key[POLL_KEYS];
+ int key_n;
+
+ int polltime;
+};
+
+HttpPoll::HttpPoll(QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+
+ d->polltime = 30;
+ d->t = new QTimer;
+ connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
+
+ connect(&d->http, SIGNAL(result()), SLOT(http_result()));
+ connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
+
+ reset(true);
+}
+
+HttpPoll::~HttpPoll()
+{
+ reset(true);
+ delete d->t;
+ delete d;
+}
+
+void HttpPoll::reset(bool clear)
+{
+ if(d->http.isActive())
+ d->http.stop();
+ if(clear)
+ clearReadBuffer();
+ clearWriteBuffer();
+ d->out.resize(0);
+ d->state = 0;
+ d->closing = false;
+ d->t->stop();
+}
+
+void HttpPoll::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void HttpPoll::connectToUrl(const QString &url)
+{
+ connectToHost("", 0, url);
+}
+
+void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
+{
+ reset(true);
+
+ // using proxy?
+ if(!proxyHost.isEmpty()) {
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->url = url;
+ d->use_proxy = true;
+ }
+ else {
+ QUrl u = url;
+ d->host = u.host();
+ if(u.hasPort())
+ d->port = u.port();
+ else
+ d->port = 80;
+ d->url = u.encodedPathAndQuery();
+ d->use_proxy = false;
+ }
+
+ resetKey();
+ bool last;
+ QString key = getKey(&last);
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->state = 1;
+ d->http.setAuth(d->user, d->pass);
+ d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
+}
+
+QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
+{
+ QString str = ident;
+ if(!key.isEmpty()) {
+ str += ';';
+ str += key;
+ }
+ if(!newkey.isEmpty()) {
+ str += ';';
+ str += newkey;
+ }
+ str += ',';
+ QCString cs = str.latin1();
+ int len = cs.length();
+
+ QByteArray a(len + block.size());
+ memcpy(a.data(), cs.data(), len);
+ memcpy(a.data() + len, block.data(), block.size());
+ return a;
+}
+
+int HttpPoll::pollInterval() const
+{
+ return d->polltime;
+}
+
+void HttpPoll::setPollInterval(int seconds)
+{
+ d->polltime = seconds;
+}
+
+bool HttpPoll::isOpen() const
+{
+ return (d->state == 2 ? true: false);
+}
+
+void HttpPoll::close()
+{
+ if(d->state == 0 || d->closing)
+ return;
+
+ if(bytesToWrite() == 0)
+ reset();
+ else
+ d->closing = true;
+}
+
+void HttpPoll::http_result()
+{
+ // check for death :)
+ QGuardedPtr<QObject> self = this;
+ syncFinished();
+ if(!self)
+ return;
+
+ // get id and packet
+ QString id;
+ QString cookie = d->http.getHeader("Set-Cookie");
+ int n = cookie.find("ID=");
+ if(n == -1) {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ n += 3;
+ int n2 = cookie.find(';', n);
+ if(n2 != -1)
+ id = cookie.mid(n, n2-n);
+ else
+ id = cookie.mid(n);
+ QByteArray block = d->http.body();
+
+ // session error?
+ if(id.right(2) == ":0") {
+ if(id == "0:0" && d->state == 2) {
+ reset();
+ connectionClosed();
+ return;
+ }
+ else {
+ reset();
+ error(ErrRead);
+ return;
+ }
+ }
+
+ d->ident = id;
+ bool justNowConnected = false;
+ if(d->state == 1) {
+ d->state = 2;
+ justNowConnected = true;
+ }
+
+ // sync up again soon
+ if(bytesToWrite() > 0 || !d->closing)
+ d->t->start(d->polltime * 1000, true);
+
+ // connecting
+ if(justNowConnected) {
+ connected();
+ }
+ else {
+ if(!d->out.isEmpty()) {
+ int x = d->out.size();
+ d->out.resize(0);
+ takeWrite(x);
+ bytesWritten(x);
+ }
+ }
+
+ if(!self)
+ return;
+
+ if(!block.isEmpty()) {
+ appendRead(block);
+ readyRead();
+ }
+
+ if(!self)
+ return;
+
+ if(bytesToWrite() > 0) {
+ do_sync();
+ }
+ else {
+ if(d->closing) {
+ reset();
+ delayedCloseFinished();
+ return;
+ }
+ }
+}
+
+void HttpPoll::http_error(int x)
+{
+ reset();
+ if(x == HttpProxyPost::ErrConnectionRefused)
+ error(ErrConnectionRefused);
+ else if(x == HttpProxyPost::ErrHostNotFound)
+ error(ErrHostNotFound);
+ else if(x == HttpProxyPost::ErrSocket)
+ error(ErrRead);
+ else if(x == HttpProxyPost::ErrProxyConnect)
+ error(ErrProxyConnect);
+ else if(x == HttpProxyPost::ErrProxyNeg)
+ error(ErrProxyNeg);
+ else if(x == HttpProxyPost::ErrProxyAuth)
+ error(ErrProxyAuth);
+}
+
+int HttpPoll::tryWrite()
+{
+ if(!d->http.isActive())
+ do_sync();
+ return 0;
+}
+
+void HttpPoll::do_sync()
+{
+ if(d->http.isActive())
+ return;
+
+ d->t->stop();
+ d->out = takeWrite(0, false);
+
+ bool last;
+ QString key = getKey(&last);
+ QString newkey;
+ if(last) {
+ resetKey();
+ newkey = getKey(&last);
+ }
+
+ QGuardedPtr<QObject> self = this;
+ syncStarted();
+ if(!self)
+ return;
+
+ d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
+}
+
+void HttpPoll::resetKey()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpPoll: reset key!\n");
+#endif
+ QByteArray a = randomArray(64);
+ QString str = QString::fromLatin1(a.data(), a.size());
+
+ d->key_n = POLL_KEYS;
+ for(int n = 0; n < POLL_KEYS; ++n)
+ d->key[n] = hpk(n+1, str);
+}
+
+const QString & HttpPoll::getKey(bool *last)
+{
+ *last = false;
+ --(d->key_n);
+ if(d->key_n == 0)
+ *last = true;
+ return d->key[d->key_n];
+}
+
+
+//----------------------------------------------------------------------------
+// HttpProxyPost
+//----------------------------------------------------------------------------
+static QString extractLine(QByteArray *buf, bool *found)
+{
+ // scan for newline
+ int n;
+ for(n = 0; n < (int)buf->size()-1; ++n) {
+ if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
+ QCString cstr;
+ cstr.resize(n+1);
+ memcpy(cstr.data(), buf->data(), n);
+ n += 2; // hack off CR/LF
+
+ memmove(buf->data(), buf->data() + n, buf->size() - n);
+ buf->resize(buf->size() - n);
+ QString s = QString::fromUtf8(cstr);
+
+ if(found)
+ *found = true;
+ return s;
+ }
+ }
+
+ if(found)
+ *found = false;
+ return "";
+}
+
+static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
+{
+ int n = line.find(' ');
+ if(n == -1)
+ return false;
+ if(proto)
+ *proto = line.mid(0, n);
+ ++n;
+ int n2 = line.find(' ', n);
+ if(n2 == -1)
+ return false;
+ if(code)
+ *code = line.mid(n, n2-n).toInt();
+ n = n2+1;
+ if(msg)
+ *msg = line.mid(n);
+ return true;
+}
+
+class HttpProxyPost::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QByteArray postdata, recvBuf, body;
+ QString url;
+ QString user, pass;
+ bool inHeader;
+ QStringList headerLines;
+ bool asProxy;
+ QString host;
+};
+
+HttpProxyPost::HttpProxyPost(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+ reset(true);
+}
+
+HttpProxyPost::~HttpProxyPost()
+{
+ reset(true);
+ delete d;
+}
+
+void HttpProxyPost::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ d->recvBuf.resize(0);
+ if(clear)
+ d->body.resize(0);
+}
+
+void HttpProxyPost::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+bool HttpProxyPost::isActive() const
+{
+ return (d->sock.state() == BSocket::Idle ? false: true);
+}
+
+void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->url = url;
+ d->postdata = data;
+ d->asProxy = asProxy;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(proxyHost, proxyPort);
+}
+
+void HttpProxyPost::stop()
+{
+ reset();
+}
+
+QByteArray HttpProxyPost::body() const
+{
+ return d->body;
+}
+
+QString HttpProxyPost::getHeader(const QString &var) const
+{
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
+ const QString &s = *it;
+ int n = s.find(": ");
+ if(n == -1)
+ continue;
+ QString v = s.mid(0, n);
+ if(v == var)
+ return s.mid(n+2);
+ }
+ return "";
+}
+
+void HttpProxyPost::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: Connected\n");
+#endif
+ d->inHeader = true;
+ d->headerLines.clear();
+
+ QUrl u = d->url;
+
+ // connected, now send the request
+ QString s;
+ s += QString("POST ") + d->url + " HTTP/1.0\r\n";
+ if(d->asProxy) {
+ if(!d->user.isEmpty()) {
+ QString str = d->user + ':' + d->pass;
+ s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
+ }
+ s += "Proxy-Connection: Keep-Alive\r\n";
+ s += "Pragma: no-cache\r\n";
+ s += QString("Host: ") + u.host() + "\r\n";
+ }
+ else {
+ s += QString("Host: ") + d->host + "\r\n";
+ }
+ s += "Content-Type: application/x-www-form-urlencoded\r\n";
+ s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
+ s += "\r\n";
+
+ // write request
+ QCString cs = s.utf8();
+ QByteArray block(cs.length());
+ memcpy(block.data(), cs.data(), block.size());
+ d->sock.write(block);
+
+ // write postdata
+ d->sock.write(d->postdata);
+}
+
+void HttpProxyPost::sock_connectionClosed()
+{
+ d->body = d->recvBuf.copy();
+ reset();
+ result();
+}
+
+void HttpProxyPost::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->inHeader) {
+ // grab available lines
+ while(1) {
+ bool found;
+ QString line = extractLine(&d->recvBuf, &found);
+ if(!found)
+ break;
+ if(line.isEmpty()) {
+ d->inHeader = false;
+ break;
+ }
+ d->headerLines += line;
+ }
+
+ // done with grabbing the header?
+ if(!d->inHeader) {
+ QString str = d->headerLines.first();
+ d->headerLines.remove(d->headerLines.begin());
+
+ QString proto;
+ int code;
+ QString msg;
+ if(!extractMainHeader(str, &proto, &code, &msg)) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: invalid header!\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
+ for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
+ fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
+#endif
+ }
+
+ if(code == 200) { // OK
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Success >>\n");
+#endif
+ }
+ else {
+ int err;
+ QString errStr;
+ if(code == 407) { // Authentication failed
+ err = ErrProxyAuth;
+ errStr = tr("Authentication failed");
+ }
+ else if(code == 404) { // Host not found
+ err = ErrHostNotFound;
+ errStr = tr("Host not found");
+ }
+ else if(code == 403) { // Access denied
+ err = ErrProxyNeg;
+ errStr = tr("Access denied");
+ }
+ else if(code == 503) { // Connection refused
+ err = ErrConnectionRefused;
+ errStr = tr("Connection refused");
+ }
+ else { // invalid reply
+ err = ErrProxyNeg;
+ errStr = tr("Invalid reply");
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
+#endif
+ reset(true);
+ error(err);
+ return;
+ }
+ }
+ }
+}
+
+void HttpProxyPost::sock_error(int x)
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
+#endif
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+}
+
+// CS_NAMESPACE_END
+
+#include "httppoll.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
new file mode 100644
index 00000000..8bbebee3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/httppoll.h
@@ -0,0 +1,104 @@
+/*
+ * httppoll.h - HTTP polling proxy
+ * 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_HTTPPOLL_H
+#define CS_HTTPPOLL_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class HttpPoll : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpPoll(QObject *parent=0);
+ ~HttpPoll();
+
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToUrl(const QString &url);
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &url);
+
+ int pollInterval() const;
+ void setPollInterval(int seconds);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+
+signals:
+ void connected();
+ void syncStarted();
+ void syncFinished();
+
+protected:
+ int tryWrite();
+
+private slots:
+ void http_result();
+ void http_error(int);
+ void do_sync();
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ QByteArray makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block);
+ void resetKey();
+ const QString & getKey(bool *);
+};
+
+class HttpProxyPost : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused, ErrHostNotFound, ErrSocket, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ HttpProxyPost(QObject *parent=0);
+ ~HttpProxyPost();
+
+ void setAuth(const QString &user, const QString &pass="");
+ bool isActive() const;
+ void post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy=true);
+ void stop();
+ QByteArray body() const;
+ QString getHeader(const QString &) const;
+
+signals:
+ void result();
+ void error(int);
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_readyRead();
+ void sock_error(int);
+
+private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
new file mode 100644
index 00000000..7fe60973
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.cpp
@@ -0,0 +1,378 @@
+/*
+ * ndns.cpp - native DNS resolution
+ * Copyright (C) 2001, 2002 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
+ *
+ */
+
+//! \class NDns ndns.h
+//! \brief Simple DNS resolution using native system calls
+//!
+//! This class is to be used when Qt's QDns is not good enough. Because QDns
+//! does not use threads, it cannot make a system call asyncronously. Thus,
+//! QDns tries to imitate the behavior of each platform's native behavior, and
+//! generally falls short.
+//!
+//! NDns uses a thread to make the system call happen in the background. This
+//! gives your program native DNS behavior, at the cost of requiring threads
+//! to build.
+//!
+//! \code
+//! #include "ndns.h"
+//!
+//! ...
+//!
+//! NDns dns;
+//! dns.resolve("psi.affinix.com");
+//!
+//! // The class will emit the resultsReady() signal when the resolution
+//! // is finished. You may then retrieve the results:
+//!
+//! uint ip_address = dns.result();
+//!
+//! // or if you want to get the IP address as a string:
+//!
+//! QString ip_address = dns.resultString();
+//! \endcode
+
+#include"ndns.h"
+
+#include<qapplication.h>
+#include<qsocketdevice.h>
+#include<qptrlist.h>
+#include<qeventloop.h>
+
+#ifdef Q_OS_UNIX
+#include<netdb.h>
+#include<sys/types.h>
+#include<netinet/in.h>
+#include<arpa/inet.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//! \if _hide_doc_
+class NDnsWorkerEvent : public QCustomEvent
+{
+public:
+ enum Type { WorkerEvent = QEvent::User + 100 };
+ NDnsWorkerEvent(NDnsWorker *);
+
+ NDnsWorker *worker;
+};
+
+class NDnsWorker : public QThread
+{
+public:
+ NDnsWorker(QObject *, const QCString &);
+
+ bool success;
+ bool cancelled;
+ QHostAddress addr;
+
+protected:
+ void run();
+
+private:
+ QCString host;
+ QObject *par;
+};
+//! \endif
+
+//----------------------------------------------------------------------------
+// NDnsManager
+//----------------------------------------------------------------------------
+#ifndef HAVE_GETHOSTBYNAME_R
+static QMutex *workerMutex = 0;
+static QMutex *workerCancelled = 0;
+#endif
+static NDnsManager *man = 0;
+bool winsock_init = false;
+
+class NDnsManager::Item
+{
+public:
+ NDns *ndns;
+ NDnsWorker *worker;
+};
+
+class NDnsManager::Private
+{
+public:
+ Item *find(const NDns *n)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->ndns == n)
+ return i;
+ }
+ return 0;
+ }
+
+ Item *find(const NDnsWorker *w)
+ {
+ QPtrListIterator<Item> it(list);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->worker == w)
+ return i;
+ }
+ return 0;
+ }
+
+ QPtrList<Item> list;
+};
+
+NDnsManager::NDnsManager()
+{
+#ifndef HAVE_GETHOSTBYNAME_R
+ workerMutex = new QMutex;
+ workerCancelled = new QMutex;
+#endif
+
+#ifdef Q_OS_WIN32
+ if(!winsock_init) {
+ winsock_init = true;
+ QSocketDevice *sd = new QSocketDevice;
+ delete sd;
+ }
+#endif
+
+ d = new Private;
+ d->list.setAutoDelete(true);
+
+ connect(qApp, SIGNAL(aboutToQuit()), SLOT(app_aboutToQuit()));
+}
+
+NDnsManager::~NDnsManager()
+{
+ delete d;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ delete workerMutex;
+ workerMutex = 0;
+ delete workerCancelled;
+ workerCancelled = 0;
+#endif
+}
+
+void NDnsManager::resolve(NDns *self, const QString &name)
+{
+ Item *i = new Item;
+ i->ndns = self;
+ i->worker = new NDnsWorker(this, name.utf8());
+ d->list.append(i);
+
+ i->worker->start();
+}
+
+void NDnsManager::stop(NDns *self)
+{
+ Item *i = d->find(self);
+ if(!i)
+ return;
+ // disassociate
+ i->ndns = 0;
+
+#ifndef HAVE_GETHOSTBYNAME_R
+ // cancel
+ workerCancelled->lock();
+ i->worker->cancelled = true;
+ workerCancelled->unlock();
+#endif
+}
+
+bool NDnsManager::isBusy(const NDns *self) const
+{
+ Item *i = d->find(self);
+ return (i ? true: false);
+}
+
+bool NDnsManager::event(QEvent *e)
+{
+ if((int)e->type() == (int)NDnsWorkerEvent::WorkerEvent) {
+ NDnsWorkerEvent *we = static_cast<NDnsWorkerEvent*>(e);
+ we->worker->wait(); // ensure that the thread is terminated
+
+ Item *i = d->find(we->worker);
+ if(!i) {
+ // should NOT happen
+ return true;
+ }
+ QHostAddress addr = i->worker->addr;
+ NDns *ndns = i->ndns;
+ delete i->worker;
+ d->list.removeRef(i);
+
+ // nuke manager if no longer needed (code that follows MUST BE SAFE!)
+ tryDestroy();
+
+ // requestor still around?
+ if(ndns)
+ ndns->finished(addr);
+ return true;
+ }
+ return false;
+}
+
+void NDnsManager::tryDestroy()
+{
+ if(d->list.isEmpty()) {
+ man = 0;
+ delete this;
+ }
+}
+
+void NDnsManager::app_aboutToQuit()
+{
+ while(man) {
+ QEventLoop *e = qApp->eventLoop();
+ e->processEvents(QEventLoop::WaitForMore);
+ }
+}
+
+
+//----------------------------------------------------------------------------
+// NDns
+//----------------------------------------------------------------------------
+
+//! \fn void NDns::resultsReady()
+//! This signal is emitted when the DNS resolution succeeds or fails.
+
+//!
+//! Constructs an NDns object with parent \a parent.
+NDns::NDns(QObject *parent)
+:QObject(parent)
+{
+}
+
+//!
+//! Destroys the object and frees allocated resources.
+NDns::~NDns()
+{
+ stop();
+}
+
+//!
+//! Resolves hostname \a host (eg. psi.affinix.com)
+void NDns::resolve(const QString &host)
+{
+ stop();
+ if(!man)
+ man = new NDnsManager;
+ man->resolve(this, host);
+}
+
+//!
+//! Cancels the lookup action.
+//! \note This will not stop the underlying system call, which must finish before the next lookup will proceed.
+void NDns::stop()
+{
+ if(man)
+ man->stop(this);
+}
+
+//!
+//! Returns the IP address as a 32-bit integer in host-byte-order. This will be 0 if the lookup failed.
+//! \sa resultsReady()
+uint NDns::result() const
+{
+ return addr.ip4Addr();
+}
+
+//!
+//! Returns the IP address as a string. This will be an empty string if the lookup failed.
+//! \sa resultsReady()
+QString NDns::resultString() const
+{
+ return addr.toString();
+}
+
+//!
+//! Returns TRUE if busy resolving a hostname.
+bool NDns::isBusy() const
+{
+ if(!man)
+ return false;
+ return man->isBusy(this);
+}
+
+void NDns::finished(const QHostAddress &a)
+{
+ addr = a;
+ resultsReady();
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorkerEvent
+//----------------------------------------------------------------------------
+NDnsWorkerEvent::NDnsWorkerEvent(NDnsWorker *p)
+:QCustomEvent(WorkerEvent)
+{
+ worker = p;
+}
+
+//----------------------------------------------------------------------------
+// NDnsWorker
+//----------------------------------------------------------------------------
+NDnsWorker::NDnsWorker(QObject *_par, const QCString &_host)
+{
+ success = cancelled = false;
+ par = _par;
+ host = _host.copy(); // do we need this to avoid sharing across threads?
+}
+
+void NDnsWorker::run()
+{
+ hostent *h = 0;
+
+#ifdef HAVE_GETHOSTBYNAME_R
+ hostent buf;
+ char char_buf[1024];
+ int err;
+ gethostbyname_r(host.data(), &buf, char_buf, sizeof(char_buf), &h, &err);
+#else
+ // lock for gethostbyname
+ QMutexLocker locker(workerMutex);
+
+ // check for cancel
+ workerCancelled->lock();
+ bool cancel = cancelled;
+ workerCancelled->unlock();
+
+ if(!cancel)
+ h = gethostbyname(host.data());
+#endif
+
+ if(!h) {
+ success = false;
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+ return;
+ }
+
+ in_addr a = *((struct in_addr *)h->h_addr_list[0]);
+ addr.setAddress(ntohl(a.s_addr));
+ success = true;
+
+ QApplication::postEvent(par, new NDnsWorkerEvent(this));
+}
+
+// CS_NAMESPACE_END
+
+#include "ndns.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
new file mode 100644
index 00000000..c11d1a28
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/ndns.h
@@ -0,0 +1,88 @@
+/*
+ * ndns.h - native DNS resolution
+ * Copyright (C) 2001, 2002 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_NDNS_H
+#define CS_NDNS_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qthread.h>
+#include<qmutex.h>
+#include<qhostaddress.h>
+
+// CS_NAMESPACE_BEGIN
+
+class NDnsWorker;
+class NDnsManager;
+
+class NDns : public QObject
+{
+ Q_OBJECT
+public:
+ NDns(QObject *parent=0);
+ ~NDns();
+
+ void resolve(const QString &);
+ void stop();
+ bool isBusy() const;
+
+ uint result() const;
+ QString resultString() const;
+
+signals:
+ void resultsReady();
+
+private:
+ QHostAddress addr;
+
+ friend class NDnsManager;
+ void finished(const QHostAddress &);
+};
+
+class NDnsManager : public QObject
+{
+ Q_OBJECT
+public:
+ ~NDnsManager();
+ class Item;
+
+//! \if _hide_doc_
+protected:
+ bool event(QEvent *);
+//! \endif
+
+private slots:
+ void app_aboutToQuit();
+
+private:
+ class Private;
+ Private *d;
+
+ friend class NDns;
+ NDnsManager();
+ void resolve(NDns *self, const QString &name);
+ void stop(NDns *self);
+ bool isBusy(const NDns *self) const;
+ void tryDestroy();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
new file mode 100644
index 00000000..4aee36dc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.cpp
@@ -0,0 +1,112 @@
+/*
+ * servsock.cpp - simple wrapper to QServerSocket
+ * 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"servsock.h"
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// ServSock
+//----------------------------------------------------------------------------
+class ServSock::Private
+{
+public:
+ Private() {}
+
+ ServSockSignal *serv;
+};
+
+ServSock::ServSock(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->serv = 0;
+}
+
+ServSock::~ServSock()
+{
+ stop();
+ delete d;
+}
+
+bool ServSock::isActive() const
+{
+ return (d->serv ? true: false);
+}
+
+bool ServSock::listen(Q_UINT16 port)
+{
+ stop();
+
+ d->serv = new ServSockSignal(port);
+ if(!d->serv->ok()) {
+ delete d->serv;
+ d->serv = 0;
+ return false;
+ }
+ connect(d->serv, SIGNAL(connectionReady(int)), SLOT(sss_connectionReady(int)));
+
+ return true;
+}
+
+void ServSock::stop()
+{
+ delete d->serv;
+ d->serv = 0;
+}
+
+int ServSock::port() const
+{
+ if(d->serv)
+ return d->serv->port();
+ else
+ return -1;
+}
+
+QHostAddress ServSock::address() const
+{
+ if(d->serv)
+ return d->serv->address();
+ else
+ return QHostAddress();
+}
+
+void ServSock::sss_connectionReady(int s)
+{
+ connectionReady(s);
+}
+
+
+//----------------------------------------------------------------------------
+// ServSockSignal
+//----------------------------------------------------------------------------
+ServSockSignal::ServSockSignal(int port)
+:QServerSocket(port, 16)
+{
+}
+
+void ServSockSignal::newConnection(int x)
+{
+ connectionReady(x);
+}
+
+// CS_NAMESPACE_END
+
+#include "servsock.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
new file mode 100644
index 00000000..60a0c99d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/servsock.h
@@ -0,0 +1,68 @@
+/*
+ * servsock.h - simple wrapper to QServerSocket
+ * 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_SERVSOCK_H
+#define CS_SERVSOCK_H
+
+#include<qserversocket.h>
+
+// CS_NAMESPACE_BEGIN
+
+class ServSock : public QObject
+{
+ Q_OBJECT
+public:
+ ServSock(QObject *parent=0);
+ ~ServSock();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+
+signals:
+ void connectionReady(int);
+
+private slots:
+ void sss_connectionReady(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+class ServSockSignal : public QServerSocket
+{
+ Q_OBJECT
+public:
+ ServSockSignal(int port);
+
+signals:
+ void connectionReady(int);
+
+protected:
+ // reimplemented
+ void newConnection(int);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
new file mode 100644
index 00000000..bae374f5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.cpp
@@ -0,0 +1,1223 @@
+/*
+ * socks.cpp - SOCKS5 TCP proxy client/server
+ * 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"socks.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<qsocketdevice.h>
+#include<qsocketnotifier.h>
+
+#ifdef Q_OS_UNIX
+#include<sys/types.h>
+#include<netinet/in.h>
+#endif
+
+#ifdef Q_OS_WIN32
+#include<windows.h>
+#endif
+
+#include"servsock.h"
+#include"bsocket.h"
+
+#ifdef PROX_DEBUG
+#include<stdio.h>
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+//----------------------------------------------------------------------------
+// SocksUDP
+//----------------------------------------------------------------------------
+static QByteArray sp_create_udp(const QString &host, Q_UINT16 port, const QByteArray &buf)
+{
+ // detect for IP addresses
+ //QHostAddress addr;
+ //if(addr.setAddress(host))
+ // return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x00; // frag
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+ at += 2;
+
+ a.resize(at+buf.size());
+ memcpy(a.data() + at, buf.data(), buf.size());
+
+ return a;
+}
+
+struct SPS_UDP
+{
+ QString host;
+ Q_UINT16 port;
+ QByteArray data;
+};
+
+static int sp_read_udp(QByteArray *from, SPS_UDP *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ host = addr.toString();
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ host = addr.toString();
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ Q_UINT16 p;
+ memcpy(&p, from->data() + full_len - 2, 2);
+
+ s->host = host;
+ s->port = ntohs(p);
+ s->data.resize(from->size() - full_len);
+ memcpy(s->data.data(), from->data() + full_len, s->data.size());
+
+ return 1;
+}
+
+class SocksUDP::Private
+{
+public:
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+ SocksClient *sc;
+ QHostAddress routeAddr;
+ int routePort;
+ QString host;
+ int port;
+};
+
+SocksUDP::SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+:QObject(sc)
+{
+ d = new Private;
+ d->sc = sc;
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ d->host = host;
+ d->port = port;
+ d->routeAddr = routeAddr;
+ d->routePort = routePort;
+}
+
+SocksUDP::~SocksUDP()
+{
+ delete d->sn;
+ delete d->sd;
+ delete d;
+}
+
+void SocksUDP::change(const QString &host, int port)
+{
+ d->host = host;
+ d->port = port;
+}
+
+void SocksUDP::write(const QByteArray &data)
+{
+ QByteArray buf = sp_create_udp(d->host, d->port, data);
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(buf.data(), buf.size(), d->routeAddr, d->routePort);
+ d->sd->setBlocking(false);
+}
+
+void SocksUDP::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ packetReady(buf);
+}
+
+//----------------------------------------------------------------------------
+// SocksClient
+//----------------------------------------------------------------------------
+#define REQ_CONNECT 0x01
+#define REQ_BIND 0x02
+#define REQ_UDPASSOCIATE 0x03
+
+#define RET_SUCCESS 0x00
+#define RET_UNREACHABLE 0x04
+#define RET_CONNREFUSED 0x05
+
+// spc = socks packet client
+// sps = socks packet server
+// SPCS = socks packet client struct
+// SPSS = socks packet server struct
+
+// Version
+static QByteArray spc_set_version()
+{
+ QByteArray ver(4);
+ ver[0] = 0x05; // socks version 5
+ ver[1] = 0x02; // number of methods
+ ver[2] = 0x00; // no-auth
+ ver[3] = 0x02; // username
+ return ver;
+}
+
+static QByteArray sps_set_version(int method)
+{
+ QByteArray ver(2);
+ ver[0] = 0x05;
+ ver[1] = method;
+ return ver;
+}
+
+struct SPCS_VERSION
+{
+ unsigned char version;
+ QByteArray methodList;
+};
+
+static int spc_get_version(QByteArray *from, SPCS_VERSION *s)
+{
+ if(from->size() < 1)
+ return 0;
+ if(from->at(0) != 0x05) // only SOCKS5 supported
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ uint num = from->at(1);
+ if(num > 16) // who the heck has over 16 auth methods??
+ return -1;
+ if(from->size() < 2 + num)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2+num);
+ s->version = a[0];
+ s->methodList.resize(num);
+ memcpy(s->methodList.data(), a.data() + 2, num);
+ return 1;
+}
+
+struct SPSS_VERSION
+{
+ unsigned char version;
+ unsigned char method;
+};
+
+static int sps_get_version(QByteArray *from, SPSS_VERSION *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->method = a[1];
+ return 1;
+}
+
+// authUsername
+static QByteArray spc_set_authUsername(const QCString &user, const QCString &pass)
+{
+ int len1 = user.length();
+ int len2 = pass.length();
+ if(len1 > 255)
+ len1 = 255;
+ if(len2 > 255)
+ len2 = 255;
+ QByteArray a(1+1+len1+1+len2);
+ a[0] = 0x01; // username auth version 1
+ a[1] = len1;
+ memcpy(a.data() + 2, user.data(), len1);
+ a[2+len1] = len2;
+ memcpy(a.data() + 3 + len1, pass.data(), len2);
+ return a;
+}
+
+static QByteArray sps_set_authUsername(bool success)
+{
+ QByteArray a(2);
+ a[0] = 0x01;
+ a[1] = success ? 0x00 : 0xff;
+ return a;
+}
+
+struct SPCS_AUTHUSERNAME
+{
+ QString user, pass;
+};
+
+static int spc_get_authUsername(QByteArray *from, SPCS_AUTHUSERNAME *s)
+{
+ if(from->size() < 1)
+ return 0;
+ unsigned char ver = from->at(0);
+ if(ver != 0x01)
+ return -1;
+ if(from->size() < 2)
+ return 0;
+ unsigned char ulen = from->at(1);
+ if((int)from->size() < ulen + 3)
+ return 0;
+ unsigned char plen = from->at(ulen+2);
+ if((int)from->size() < ulen + plen + 3)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, ulen + plen + 3);
+
+ QCString user, pass;
+ user.resize(ulen+1);
+ pass.resize(plen+1);
+ memcpy(user.data(), a.data()+2, ulen);
+ memcpy(pass.data(), a.data()+ulen+3, plen);
+ s->user = QString::fromUtf8(user);
+ s->pass = QString::fromUtf8(pass);
+ return 1;
+}
+
+struct SPSS_AUTHUSERNAME
+{
+ unsigned char version;
+ bool success;
+};
+
+static int sps_get_authUsername(QByteArray *from, SPSS_AUTHUSERNAME *s)
+{
+ if(from->size() < 2)
+ return 0;
+ QByteArray a = ByteStream::takeArray(from, 2);
+ s->version = a[0];
+ s->success = a[1] == 0 ? true: false;
+ return 1;
+}
+
+// connectRequest
+static QByteArray sp_set_request(const QHostAddress &addr, unsigned short port, unsigned char cmd1)
+{
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ if(addr.isIp4Addr()) {
+ a[at++] = 0x01; // address type = ipv4
+ Q_UINT32 ip4 = htonl(addr.ip4Addr());
+ a.resize(at+4);
+ memcpy(a.data() + at, &ip4, 4);
+ at += 4;
+ }
+ else {
+ a[at++] = 0x04;
+ Q_UINT8 a6[16];
+ QStringList s6 = QStringList::split(':', addr.toString(), true);
+ int at = 0;
+ Q_UINT16 c;
+ bool ok;
+ for(QStringList::ConstIterator it = s6.begin(); it != s6.end(); ++it) {
+ c = (*it).toInt(&ok, 16);
+ a6[at++] = (c >> 8);
+ a6[at++] = c & 0xff;
+ }
+ a.resize(at+16);
+ memcpy(a.data() + at, a6, 16);
+ at += 16;
+ }
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+static QByteArray sp_set_request(const QString &host, Q_UINT16 port, unsigned char cmd1)
+{
+ // detect for IP addresses
+ QHostAddress addr;
+ if(addr.setAddress(host))
+ return sp_set_request(addr, port, cmd1);
+
+ QCString h = host.utf8();
+ h.truncate(255);
+ h = QString::fromUtf8(h).utf8(); // delete any partial characters?
+ int hlen = h.length();
+
+ int at = 0;
+ QByteArray a(4);
+ a[at++] = 0x05; // socks version 5
+ a[at++] = cmd1;
+ a[at++] = 0x00; // reserved
+ a[at++] = 0x03; // address type = domain
+
+ // host
+ a.resize(at+hlen+1);
+ a[at++] = hlen;
+ memcpy(a.data() + at, h.data(), hlen);
+ at += hlen;
+
+ // port
+ a.resize(at+2);
+ unsigned short p = htons(port);
+ memcpy(a.data() + at, &p, 2);
+
+ return a;
+}
+
+struct SPS_CONNREQ
+{
+ unsigned char version;
+ unsigned char cmd;
+ int address_type;
+ QString host;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+static int sp_get_request(QByteArray *from, SPS_CONNREQ *s)
+{
+ int full_len = 4;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QString host;
+ QHostAddress addr;
+ unsigned char atype = from->at(3);
+
+ if(atype == 0x01) {
+ full_len += 4;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT32 ip4;
+ memcpy(&ip4, from->data() + 4, 4);
+ addr.setAddress(ntohl(ip4));
+ }
+ else if(atype == 0x03) {
+ ++full_len;
+ if((int)from->size() < full_len)
+ return 0;
+ unsigned char host_len = from->at(4);
+ full_len += host_len;
+ if((int)from->size() < full_len)
+ return 0;
+ QCString cs(host_len+1);
+ memcpy(cs.data(), from->data() + 5, host_len);
+ host = QString::fromLatin1(cs);
+ }
+ else if(atype == 0x04) {
+ full_len += 16;
+ if((int)from->size() < full_len)
+ return 0;
+ Q_UINT8 a6[16];
+ memcpy(a6, from->data() + 4, 16);
+ addr.setAddress(a6);
+ }
+
+ full_len += 2;
+ if((int)from->size() < full_len)
+ return 0;
+
+ QByteArray a = ByteStream::takeArray(from, full_len);
+
+ Q_UINT16 p;
+ memcpy(&p, a.data() + full_len - 2, 2);
+
+ s->version = a[0];
+ s->cmd = a[1];
+ s->address_type = atype;
+ s->host = host;
+ s->addr = addr;
+ s->port = ntohs(p);
+
+ return 1;
+}
+
+enum { StepVersion, StepAuth, StepRequest };
+
+class SocksClient::Private
+{
+public:
+ Private() {}
+
+ BSocket sock;
+ QString host;
+ int port;
+ QString user, pass;
+ QString real_host;
+ int real_port;
+
+ QByteArray recvBuf;
+ bool active;
+ int step;
+ int authMethod;
+ bool incoming, waiting;
+
+ QString rhost;
+ int rport;
+
+ int pending;
+
+ bool udp;
+ QString udpAddr;
+ int udpPort;
+};
+
+SocksClient::SocksClient(QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = false;
+}
+
+SocksClient::SocksClient(int s, QObject *parent)
+:ByteStream(parent)
+{
+ init();
+
+ d->incoming = true;
+ d->waiting = true;
+ d->sock.setSocket(s);
+}
+
+void SocksClient::init()
+{
+ d = new Private;
+ connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
+ connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
+ connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
+ connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
+ connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
+ connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
+
+ reset(true);
+}
+
+SocksClient::~SocksClient()
+{
+ reset(true);
+ delete d;
+}
+
+void SocksClient::reset(bool clear)
+{
+ if(d->sock.state() != BSocket::Idle)
+ d->sock.close();
+ if(clear)
+ clearReadBuffer();
+ d->recvBuf.resize(0);
+ d->active = false;
+ d->waiting = false;
+ d->udp = false;
+ d->pending = 0;
+}
+
+bool SocksClient::isIncoming() const
+{
+ return d->incoming;
+}
+
+void SocksClient::setAuth(const QString &user, const QString &pass)
+{
+ d->user = user;
+ d->pass = pass;
+}
+
+void SocksClient::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode)
+{
+ reset(true);
+
+ d->host = proxyHost;
+ d->port = proxyPort;
+ d->real_host = host;
+ d->real_port = port;
+ d->udp = udpMode;
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
+ if(d->user.isEmpty())
+ fprintf(stderr, "\n");
+ else
+ fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
+#endif
+ d->sock.connectToHost(d->host, d->port);
+}
+
+bool SocksClient::isOpen() const
+{
+ return d->active;
+}
+
+void SocksClient::close()
+{
+ d->sock.close();
+ if(d->sock.bytesToWrite() == 0)
+ reset();
+}
+
+void SocksClient::writeData(const QByteArray &buf)
+{
+ d->pending += buf.size();
+ d->sock.write(buf);
+}
+
+void SocksClient::write(const QByteArray &buf)
+{
+ if(d->active && !d->udp)
+ d->sock.write(buf);
+}
+
+QByteArray SocksClient::read(int bytes)
+{
+ return ByteStream::read(bytes);
+}
+
+int SocksClient::bytesAvailable() const
+{
+ return ByteStream::bytesAvailable();
+}
+
+int SocksClient::bytesToWrite() const
+{
+ if(d->active)
+ return d->sock.bytesToWrite();
+ else
+ return 0;
+}
+
+void SocksClient::sock_connected()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Connected\n");
+#endif
+
+ d->step = StepVersion;
+ writeData(spc_set_version());
+}
+
+void SocksClient::sock_connectionClosed()
+{
+ if(d->active) {
+ reset();
+ connectionClosed();
+ }
+ else {
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::sock_delayedCloseFinished()
+{
+ if(d->active) {
+ reset();
+ delayedCloseFinished();
+ }
+}
+
+void SocksClient::sock_readyRead()
+{
+ QByteArray block = d->sock.read();
+
+ if(!d->active) {
+ if(d->incoming)
+ processIncoming(block);
+ else
+ processOutgoing(block);
+ }
+ else {
+ if(!d->udp) {
+ appendRead(block);
+ readyRead();
+ }
+ }
+}
+
+void SocksClient::processOutgoing(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: client recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(d->step == StepVersion) {
+ SPSS_VERSION s;
+ int r = sps_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05 || s.method == 0xff) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Method selection failed\n");
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ QString str;
+ if(s.method == 0x00) {
+ str = "None";
+ d->authMethod = AuthNone;
+ }
+ else if(s.method == 0x02) {
+ str = "Username/Password";
+ d->authMethod = AuthUsername;
+ }
+ else {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Server wants to use unknown method '%02x'\n", s.method);
+#endif
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ if(d->authMethod == AuthNone) {
+ // no auth, go straight to the request
+ do_request();
+ }
+ else if(d->authMethod == AuthUsername) {
+ d->step = StepAuth;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Authenticating [Username] ...\n");
+#endif
+ writeData(spc_set_authUsername(d->user.latin1(), d->pass.latin1()));
+ }
+ }
+ }
+ if(d->step == StepAuth) {
+ if(d->authMethod == AuthUsername) {
+ SPSS_AUTHUSERNAME s;
+ int r = sps_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x01) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ if(!s.success) {
+ reset(true);
+ error(ErrProxyAuth);
+ return;
+ }
+
+ do_request();
+ }
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.cmd != RET_SUCCESS) {
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Error >> [%02x]\n", s.cmd);
+#endif
+ reset(true);
+ if(s.cmd == RET_UNREACHABLE)
+ error(ErrHostNotFound);
+ else if(s.cmd == RET_CONNREFUSED)
+ error(ErrConnectionRefused);
+ else
+ error(ErrProxyNeg);
+ return;
+ }
+
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: client << Success >>\n");
+#endif
+ if(d->udp) {
+ if(s.address_type == 0x03)
+ d->udpAddr = s.host;
+ else
+ d->udpAddr = s.addr.toString();
+ d->udpPort = s.port;
+ }
+
+ d->active = true;
+
+ QGuardedPtr<QObject> self = this;
+ connected();
+ if(!self)
+ return;
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+ }
+ }
+}
+
+void SocksClient::do_request()
+{
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: Requesting ...\n");
+#endif
+ d->step = StepRequest;
+ int cmd = d->udp ? REQ_UDPASSOCIATE : REQ_CONNECT;
+ QByteArray buf;
+ if(!d->real_host.isEmpty())
+ buf = sp_set_request(d->real_host, d->real_port, cmd);
+ else
+ buf = sp_set_request(QHostAddress(), 0, cmd);
+ writeData(buf);
+}
+
+void SocksClient::sock_bytesWritten(int x)
+{
+ int bytes = x;
+ if(d->pending >= bytes) {
+ d->pending -= bytes;
+ bytes = 0;
+ }
+ else {
+ bytes -= d->pending;
+ d->pending = 0;
+ }
+ if(bytes > 0)
+ bytesWritten(bytes);
+}
+
+void SocksClient::sock_error(int x)
+{
+ if(d->active) {
+ reset();
+ error(ErrRead);
+ }
+ else {
+ reset(true);
+ if(x == BSocket::ErrHostNotFound)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrConnectionRefused)
+ error(ErrProxyConnect);
+ else if(x == BSocket::ErrRead)
+ error(ErrProxyNeg);
+ }
+}
+
+void SocksClient::serve()
+{
+ d->waiting = false;
+ d->step = StepVersion;
+ continueIncoming();
+}
+
+void SocksClient::processIncoming(const QByteArray &block)
+{
+#ifdef PROX_DEBUG
+ // show hex
+ fprintf(stderr, "SocksClient: server recv { ");
+ for(int n = 0; n < (int)block.size(); ++n)
+ fprintf(stderr, "%02X ", (unsigned char)block[n]);
+ fprintf(stderr, " } \n");
+#endif
+ ByteStream::appendArray(&d->recvBuf, block);
+
+ if(!d->waiting)
+ continueIncoming();
+}
+
+void SocksClient::continueIncoming()
+{
+ if(d->recvBuf.isEmpty())
+ return;
+
+ if(d->step == StepVersion) {
+ SPCS_VERSION s;
+ int r = spc_get_version(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ if(s.version != 0x05) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+
+ int methods = 0;
+ for(int n = 0; n < (int)s.methodList.size(); ++n) {
+ unsigned char c = s.methodList[n];
+ if(c == 0x00)
+ methods |= AuthNone;
+ else if(c == 0x02)
+ methods |= AuthUsername;
+ }
+ d->waiting = true;
+ incomingMethods(methods);
+ }
+ }
+ else if(d->step == StepAuth) {
+ SPCS_AUTHUSERNAME s;
+ int r = spc_get_authUsername(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ incomingAuth(s.user, s.pass);
+ }
+ }
+ else if(d->step == StepRequest) {
+ SPS_CONNREQ s;
+ int r = sp_get_request(&d->recvBuf, &s);
+ if(r == -1) {
+ reset(true);
+ error(ErrProxyNeg);
+ return;
+ }
+ else if(r == 1) {
+ d->waiting = true;
+ if(s.cmd == REQ_CONNECT) {
+ if(!s.host.isEmpty())
+ d->rhost = s.host;
+ else
+ d->rhost = s.addr.toString();
+ d->rport = s.port;
+ incomingConnectRequest(d->rhost, d->rport);
+ }
+ else if(s.cmd == REQ_UDPASSOCIATE) {
+ incomingUDPAssociateRequest();
+ }
+ else {
+ requestDeny();
+ return;
+ }
+ }
+ }
+}
+
+void SocksClient::chooseMethod(int method)
+{
+ if(d->step != StepVersion || !d->waiting)
+ return;
+
+ unsigned char c;
+ if(method == AuthNone) {
+ d->step = StepRequest;
+ c = 0x00;
+ }
+ else {
+ d->step = StepAuth;
+ c = 0x02;
+ }
+
+ // version response
+ d->waiting = false;
+ writeData(sps_set_version(c));
+ continueIncoming();
+}
+
+void SocksClient::authGrant(bool b)
+{
+ if(d->step != StepAuth || !d->waiting)
+ return;
+
+ if(b)
+ d->step = StepRequest;
+
+ // auth response
+ d->waiting = false;
+ writeData(sps_set_authUsername(b));
+ if(!b) {
+ reset(true);
+ return;
+ }
+ continueIncoming();
+}
+
+void SocksClient::requestDeny()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_UNREACHABLE));
+ reset(true);
+}
+
+void SocksClient::grantConnect()
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(d->rhost, d->rport, RET_SUCCESS));
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty()) {
+ appendRead(d->recvBuf);
+ d->recvBuf.resize(0);
+ readyRead();
+ }
+}
+
+void SocksClient::grantUDPAssociate(const QString &relayHost, int relayPort)
+{
+ if(d->step != StepRequest || !d->waiting)
+ return;
+
+ // response
+ d->waiting = false;
+ writeData(sp_set_request(relayHost, relayPort, RET_SUCCESS));
+ d->udp = true;
+ d->active = true;
+#ifdef PROX_DEBUG
+ fprintf(stderr, "SocksClient: server << Success >>\n");
+#endif
+
+ if(!d->recvBuf.isEmpty())
+ d->recvBuf.resize(0);
+}
+
+QHostAddress SocksClient::peerAddress() const
+{
+ return d->sock.peerAddress();
+}
+
+Q_UINT16 SocksClient::peerPort() const
+{
+ return d->sock.peerPort();
+}
+
+QString SocksClient::udpAddress() const
+{
+ return d->udpAddr;
+}
+
+Q_UINT16 SocksClient::udpPort() const
+{
+ return d->udpPort;
+}
+
+SocksUDP *SocksClient::createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort)
+{
+ return new SocksUDP(this, host, port, routeAddr, routePort);
+}
+
+//----------------------------------------------------------------------------
+// SocksServer
+//----------------------------------------------------------------------------
+class SocksServer::Private
+{
+public:
+ Private() {}
+
+ ServSock serv;
+ QPtrList<SocksClient> incomingConns;
+ QSocketDevice *sd;
+ QSocketNotifier *sn;
+};
+
+SocksServer::SocksServer(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->sd = 0;
+ d->sn = 0;
+ connect(&d->serv, SIGNAL(connectionReady(int)), SLOT(connectionReady(int)));
+}
+
+SocksServer::~SocksServer()
+{
+ stop();
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d;
+}
+
+bool SocksServer::isActive() const
+{
+ return d->serv.isActive();
+}
+
+bool SocksServer::listen(Q_UINT16 port, bool udp)
+{
+ stop();
+ if(!d->serv.listen(port))
+ return false;
+ if(udp) {
+ d->sd = new QSocketDevice(QSocketDevice::Datagram);
+ d->sd->setBlocking(false);
+ if(!d->sd->bind(QHostAddress(), port)) {
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+ return false;
+ }
+ d->sn = new QSocketNotifier(d->sd->socket(), QSocketNotifier::Read);
+ connect(d->sn, SIGNAL(activated(int)), SLOT(sn_activated(int)));
+ }
+ return true;
+}
+
+void SocksServer::stop()
+{
+ delete d->sn;
+ d->sn = 0;
+ delete d->sd;
+ d->sd = 0;
+ d->serv.stop();
+}
+
+int SocksServer::port() const
+{
+ return d->serv.port();
+}
+
+QHostAddress SocksServer::address() const
+{
+ return d->serv.address();
+}
+
+SocksClient *SocksServer::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ SocksClient *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+
+ // we don't care about errors anymore
+ disconnect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+
+ // don't serve the connection until the event loop, to give the caller a chance to map signals
+ QTimer::singleShot(0, c, SLOT(serve()));
+
+ return c;
+}
+
+void SocksServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+ if(d->sd) {
+ d->sd->setBlocking(true);
+ d->sd->writeBlock(data.data(), data.size(), addr, port);
+ d->sd->setBlocking(false);
+ }
+}
+
+void SocksServer::connectionReady(int s)
+{
+ SocksClient *c = new SocksClient(s, this);
+ connect(c, SIGNAL(error(int)), this, SLOT(connectionError()));
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void SocksServer::connectionError()
+{
+ SocksClient *c = (SocksClient *)sender();
+ d->incomingConns.removeRef(c);
+ c->deleteLater();
+}
+
+void SocksServer::sn_activated(int)
+{
+ QByteArray buf(8192);
+ int actual = d->sd->readBlock(buf.data(), buf.size());
+ buf.resize(actual);
+ QHostAddress pa = d->sd->peerAddress();
+ int pp = d->sd->peerPort();
+ SPS_UDP s;
+ int r = sp_read_udp(&buf, &s);
+ if(r != 1)
+ return;
+ incomingUDP(s.host, s.port, pa, pp, s.data);
+}
+
+// CS_NAMESPACE_END
+
+#include "socks.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/socks.h b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
new file mode 100644
index 00000000..8f1e4ddc
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/socks.h
@@ -0,0 +1,160 @@
+/*
+ * socks.h - SOCKS5 TCP proxy client/server
+ * 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_SOCKS_H
+#define CS_SOCKS_H
+
+#include"bytestream.h"
+
+// CS_NAMESPACE_BEGIN
+
+class QHostAddress;
+class SocksClient;
+class SocksServer;
+
+class SocksUDP : public QObject
+{
+ Q_OBJECT
+public:
+ ~SocksUDP();
+
+ void change(const QString &host, int port);
+ void write(const QByteArray &data);
+
+signals:
+ void packetReady(const QByteArray &data);
+
+private slots:
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+
+ friend class SocksClient;
+ SocksUDP(SocksClient *sc, const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+};
+
+class SocksClient : public ByteStream
+{
+ Q_OBJECT
+public:
+ enum Error { ErrConnectionRefused = ErrCustom, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth };
+ enum Method { AuthNone=0x0001, AuthUsername=0x0002 };
+ enum Request { ReqConnect, ReqUDPAssociate };
+ SocksClient(QObject *parent=0);
+ SocksClient(int, QObject *parent=0);
+ ~SocksClient();
+
+ bool isIncoming() const;
+
+ // outgoing
+ void setAuth(const QString &user, const QString &pass="");
+ void connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port, bool udpMode=false);
+
+ // incoming
+ void chooseMethod(int);
+ void authGrant(bool);
+ void requestDeny();
+ void grantConnect();
+ void grantUDPAssociate(const QString &relayHost, int relayPort);
+
+ // from ByteStream
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ // remote address
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+ // udp
+ QString udpAddress() const;
+ Q_UINT16 udpPort() const;
+ SocksUDP *createUDP(const QString &host, int port, const QHostAddress &routeAddr, int routePort);
+
+signals:
+ // outgoing
+ void connected();
+
+ // incoming
+ void incomingMethods(int);
+ void incomingAuth(const QString &user, const QString &pass);
+ void incomingConnectRequest(const QString &host, int port);
+ void incomingUDPAssociateRequest();
+
+private slots:
+ void sock_connected();
+ void sock_connectionClosed();
+ void sock_delayedCloseFinished();
+ void sock_readyRead();
+ void sock_bytesWritten(int);
+ void sock_error(int);
+ void serve();
+
+private:
+ class Private;
+ Private *d;
+
+ void init();
+ void reset(bool clear=false);
+ void do_request();
+ void processOutgoing(const QByteArray &);
+ void processIncoming(const QByteArray &);
+ void continueIncoming();
+ void writeData(const QByteArray &a);
+};
+
+class SocksServer : public QObject
+{
+ Q_OBJECT
+public:
+ SocksServer(QObject *parent=0);
+ ~SocksServer();
+
+ bool isActive() const;
+ bool listen(Q_UINT16 port, bool udp=false);
+ void stop();
+ int port() const;
+ QHostAddress address() const;
+ SocksClient *takeIncoming();
+
+ void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+
+signals:
+ void incomingReady();
+ void incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+
+private slots:
+ void connectionReady(int);
+ void connectionError();
+ void sn_activated(int);
+
+private:
+ class Private;
+ Private *d;
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
new file mode 100644
index 00000000..0c454c49
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.cpp
@@ -0,0 +1,320 @@
+/*
+ * srvresolver.cpp - class to simplify SRV lookups
+ * 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"srvresolver.h"
+
+#include<qcstring.h>
+#include<qtimer.h>
+#include<qdns.h>
+#include"safedelete.h"
+
+#ifndef NO_NDNS
+#include"ndns.h"
+#endif
+
+// CS_NAMESPACE_BEGIN
+
+static void sortSRVList(QValueList<QDns::Server> &list)
+{
+ QValueList<QDns::Server> tmp = list;
+ list.clear();
+
+ while(!tmp.isEmpty()) {
+ QValueList<QDns::Server>::Iterator p = tmp.end();
+ for(QValueList<QDns::Server>::Iterator it = tmp.begin(); it != tmp.end(); ++it) {
+ if(p == tmp.end())
+ p = it;
+ else {
+ int a = (*it).priority;
+ int b = (*p).priority;
+ int j = (*it).weight;
+ int k = (*p).weight;
+ if(a < b || (a == b && j < k))
+ p = it;
+ }
+ }
+ list.append(*p);
+ tmp.remove(p);
+ }
+}
+
+class SrvResolver::Private
+{
+public:
+ Private() {}
+
+ QDns *qdns;
+#ifndef NO_NDNS
+ NDns ndns;
+#endif
+
+ bool failed;
+ QHostAddress resultAddress;
+ Q_UINT16 resultPort;
+
+ bool srvonly;
+ QString srv;
+ QValueList<QDns::Server> servers;
+ bool aaaa;
+
+ QTimer t;
+ SafeDelete sd;
+};
+
+SrvResolver::SrvResolver(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->qdns = 0;
+
+#ifndef NO_NDNS
+ connect(&d->ndns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+#endif
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+ stop();
+}
+
+SrvResolver::~SrvResolver()
+{
+ stop();
+ delete d;
+}
+
+void SrvResolver::resolve(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = false;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::resolveSrvOnly(const QString &server, const QString &type, const QString &proto)
+{
+ stop();
+
+ d->failed = false;
+ d->srvonly = true;
+ d->srv = QString("_") + type + "._" + proto + '.' + server;
+ d->t.start(15000, true);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(qdns_done()));
+ d->qdns->setRecordType(QDns::Srv);
+ d->qdns->setLabel(d->srv);
+}
+
+void SrvResolver::next()
+{
+ if(d->servers.isEmpty())
+ return;
+
+ tryNext();
+}
+
+void SrvResolver::stop()
+{
+ if(d->t.isActive())
+ d->t.stop();
+ if(d->qdns) {
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+ }
+#ifndef NO_NDNS
+ if(d->ndns.isBusy())
+ d->ndns.stop();
+#endif
+ d->resultAddress = QHostAddress();
+ d->resultPort = 0;
+ d->servers.clear();
+ d->srv = "";
+ d->failed = true;
+}
+
+bool SrvResolver::isBusy() const
+{
+#ifndef NO_NDNS
+ if(d->qdns || d->ndns.isBusy())
+#else
+ if(d->qdns)
+#endif
+ return true;
+ else
+ return false;
+}
+
+QValueList<QDns::Server> SrvResolver::servers() const
+{
+ return d->servers;
+}
+
+bool SrvResolver::failed() const
+{
+ return d->failed;
+}
+
+QHostAddress SrvResolver::resultAddress() const
+{
+ return d->resultAddress;
+}
+
+Q_UINT16 SrvResolver::resultPort() const
+{
+ return d->resultPort;
+}
+
+void SrvResolver::tryNext()
+{
+#ifndef NO_NDNS
+ d->ndns.resolve(d->servers.first().name);
+#else
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(ndns_done()));
+ if(d->aaaa)
+ d->qdns->setRecordType(QDns::Aaaa); // IPv6
+ else
+ d->qdns->setRecordType(QDns::A); // IPv4
+ d->qdns->setLabel(d->servers.first().name);
+#endif
+}
+
+void SrvResolver::qdns_done()
+{
+ if(!d->qdns)
+ return;
+
+ // apparently we sometimes get this signal even though the results aren't ready
+ if(d->qdns->isWorking())
+ return;
+ d->t.stop();
+
+ SafeDeleteLock s(&d->sd);
+
+ // grab the server list and destroy the qdns object
+ QValueList<QDns::Server> list;
+ if(d->qdns->recordType() == QDns::Srv)
+ list = d->qdns->servers();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(list.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+ sortSRVList(list);
+ d->servers = list;
+
+ if(d->srvonly)
+ resultsReady();
+ else {
+ // kick it off
+ d->aaaa = true;
+ tryNext();
+ }
+}
+
+void SrvResolver::ndns_done()
+{
+#ifndef NO_NDNS
+ SafeDeleteLock s(&d->sd);
+
+ uint r = d->ndns.result();
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+
+ if(r) {
+ d->resultAddress = QHostAddress(d->ndns.result());
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#else
+ if(!d->qdns)
+ return;
+
+ // apparently we sometimes get this signal even though the results aren't ready
+ if(d->qdns->isWorking())
+ return;
+
+ SafeDeleteLock s(&d->sd);
+
+ // grab the address list and destroy the qdns object
+ QValueList<QHostAddress> list;
+ if(d->qdns->recordType() == QDns::A || d->qdns->recordType() == QDns::Aaaa)
+ list = d->qdns->addresses();
+ d->qdns->disconnect(this);
+ d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(!list.isEmpty()) {
+ int port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+ d->aaaa = true;
+
+ d->resultAddress = list.first();
+ d->resultPort = port;
+ resultsReady();
+ }
+ else {
+ if(!d->aaaa)
+ d->servers.remove(d->servers.begin());
+ d->aaaa = !d->aaaa;
+
+ // failed? bail if last one
+ if(d->servers.isEmpty()) {
+ stop();
+ resultsReady();
+ return;
+ }
+
+ // otherwise try the next
+ tryNext();
+ }
+#endif
+}
+
+void SrvResolver::t_timeout()
+{
+ SafeDeleteLock s(&d->sd);
+
+ stop();
+ resultsReady();
+}
+
+// CS_NAMESPACE_END
+
+#include "srvresolver.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
new file mode 100644
index 00000000..6c9ac4f3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/network/srvresolver.h
@@ -0,0 +1,65 @@
+/*
+ * srvresolver.h - class to simplify SRV lookups
+ * 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_SRVRESOLVER_H
+#define CS_SRVRESOLVER_H
+
+#include<qvaluelist.h>
+#include<qdns.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SrvResolver : public QObject
+{
+ Q_OBJECT
+public:
+ SrvResolver(QObject *parent=0);
+ ~SrvResolver();
+
+ void resolve(const QString &server, const QString &type, const QString &proto);
+ void resolveSrvOnly(const QString &server, const QString &type, const QString &proto);
+ void next();
+ void stop();
+ bool isBusy() const;
+
+ QValueList<QDns::Server> servers() const;
+
+ bool failed() const;
+ QHostAddress resultAddress() const;
+ Q_UINT16 resultPort() const;
+
+signals:
+ void resultsReady();
+
+private slots:
+ void qdns_done();
+ void ndns_done();
+ void t_timeout();
+
+private:
+ class Private;
+ Private *d;
+
+ void tryNext();
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am b/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am
new file mode 100644
index 00000000..649c0fcf
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libcutestuff_util.la
+INCLUDES = $(all_includes)
+
+libcutestuff_util_la_SOURCES = \
+ base64.cpp \
+ bytestream.cpp \
+ qrandom.cpp \
+ safedelete.cpp \
+ sha1.cpp \
+ showtextdlg.cpp
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/TODO b/kopete/protocols/jabber/libiris/cutestuff/util/TODO
new file mode 100644
index 00000000..42d94b7d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/TODO
@@ -0,0 +1,7 @@
+varlist
+common (opening urls)
+zip
+showtext
+format parsing
+xml handling, elem2string, etc
+
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp
new file mode 100644
index 00000000..a17ac335
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/base64.cpp
@@ -0,0 +1,182 @@
+/*
+ * base64.cpp - Base64 converting functions
+ * 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"base64.h"
+
+// CS_NAMESPACE_BEGIN
+
+//! \class Base64 base64.h
+//! \brief Base64 conversion functions.
+//!
+//! Converts Base64 data between arrays and strings.
+//!
+//! \code
+//! #include "base64.h"
+//!
+//! ...
+//!
+//! // encode a block of data into base64
+//! QByteArray block(1024);
+//! QByteArray enc = Base64::encode(block);
+//!
+//! \endcode
+
+//!
+//! Encodes array \a s and returns the result.
+QByteArray Base64::encode(const QByteArray &s)
+{
+ int i;
+ int len = s.size();
+ char tbl[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ int a, b, c;
+
+ QByteArray p((len+2)/3*4);
+ int at = 0;
+ for( i = 0; i < len; i += 3 ) {
+ a = ((unsigned char)s[i] & 3) << 4;
+ if(i + 1 < len) {
+ a += (unsigned char)s[i + 1] >> 4;
+ b = ((unsigned char)s[i + 1] & 0xF) << 2;
+ if(i + 2 < len) {
+ b += (unsigned char)s[i + 2] >> 6;
+ c = (unsigned char)s[i + 2] & 0x3F;
+ }
+ else
+ c = 64;
+ }
+ else
+ b = c = 64;
+
+ p[at++] = tbl[(unsigned char)s[i] >> 2];
+ p[at++] = tbl[a];
+ p[at++] = tbl[b];
+ p[at++] = tbl[c];
+ }
+ return p;
+}
+
+//!
+//! Decodes array \a s and returns the result.
+QByteArray Base64::decode(const QByteArray &s)
+{
+ // return value
+ QByteArray p;
+
+ // -1 specifies invalid
+ // 64 specifies eof
+ // everything else specifies data
+
+ char tbl[] = {
+ -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,62,-1,-1,-1,63,
+ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
+ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
+ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
+ 41,42,43,44,45,46,47,48,49,50,51,-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,-1,-1,-1,
+ };
+
+ // this should be a multiple of 4
+ int len = s.size();
+
+ if(len % 4)
+ return p;
+
+ p.resize(len / 4 * 3);
+
+ int i;
+ int at = 0;
+
+ int a, b, c, d;
+ c = d = 0;
+
+ for( i = 0; i < len; i += 4 ) {
+ a = tbl[(int)s[i]];
+ b = tbl[(int)s[i + 1]];
+ c = tbl[(int)s[i + 2]];
+ d = tbl[(int)s[i + 3]];
+ if((a == 64 || b == 64) || (a < 0 || b < 0 || c < 0 || d < 0)) {
+ p.resize(0);
+ return p;
+ }
+ p[at++] = ((a & 0x3F) << 2) | ((b >> 4) & 0x03);
+ p[at++] = ((b & 0x0F) << 4) | ((c >> 2) & 0x0F);
+ p[at++] = ((c & 0x03) << 6) | ((d >> 0) & 0x3F);
+ }
+
+ if(c & 64)
+ p.resize(at - 2);
+ else if(d & 64)
+ p.resize(at - 1);
+
+ return p;
+}
+
+//!
+//! Encodes array \a a and returns the result as a string.
+QString Base64::arrayToString(const QByteArray &a)
+{
+ QByteArray b = encode(a);
+ QCString c;
+ c.resize(b.size()+1);
+ memcpy(c.data(), b.data(), b.size());
+ return QString::fromLatin1(c);
+}
+
+//!
+//! Decodes string \a s and returns the result as an array.
+QByteArray Base64::stringToArray(const QString &s)
+{
+ if(s.isEmpty())
+ return QByteArray();
+
+ // Unfold data
+ QString us(s);
+ us.remove('\n');
+
+ const char *c = us.latin1();
+ int len = strlen(c);
+ QByteArray b(len);
+ memcpy(b.data(), c, len);
+ QByteArray a = decode(b);
+ return a;
+}
+
+//!
+//! Encodes string \a s and returns the result as a string.
+QString Base64::encodeString(const QString &s)
+{
+ QCString c = s.utf8();
+ int len = c.length();
+ QByteArray b(len);
+ memcpy(b.data(), c.data(), len);
+ return arrayToString(b);
+}
+
+// CS_NAMESPACE_END
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/base64.h b/kopete/protocols/jabber/libiris/cutestuff/util/base64.h
new file mode 100644
index 00000000..128472c1
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/base64.h
@@ -0,0 +1,40 @@
+/*
+ * base64.h - Base64 converting functions
+ * 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_BASE64_H
+#define CS_BASE64_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class Base64
+{
+public:
+ static QByteArray encode(const QByteArray &);
+ static QByteArray decode(const QByteArray &);
+ static QString arrayToString(const QByteArray &);
+ static QByteArray stringToArray(const QString &);
+ static QString encodeString(const QString &);
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp
new file mode 100644
index 00000000..1eccb284
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.cpp
@@ -0,0 +1,268 @@
+/*
+ * 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/jabber/libiris/cutestuff/util/bytestream.h b/kopete/protocols/jabber/libiris/cutestuff/util/bytestream.h
new file mode 100644
index 00000000..c33b3976
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/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/jabber/libiris/cutestuff/util/cipher.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp
new file mode 100644
index 00000000..3e2f3a15
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.cpp
@@ -0,0 +1,357 @@
+/*
+ * cipher.cpp - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * 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"cipher.h"
+
+#include<openssl/evp.h>
+#include<openssl/rsa.h>
+#include"bytestream.h"
+#include"qrandom.h"
+
+static bool lib_encryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+ QByteArray result(buf.size()+type->block_size);
+ int len;
+ EVP_CIPHER_CTX c;
+
+ unsigned char *ivp = NULL;
+ if(!iv.isEmpty())
+ ivp = (unsigned char *)iv.data();
+ EVP_CIPHER_CTX_init(&c);
+ //EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+ if(!EVP_EncryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+ return false;
+ if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ result.resize(len);
+ if(pad) {
+ QByteArray last(type->block_size);
+ if(!EVP_EncryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+ return false;
+ last.resize(len);
+ ByteStream::appendArray(&result, last);
+ }
+
+ memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+ *out = result;
+ return true;
+}
+
+static bool lib_decryptArray(const EVP_CIPHER *type, const QByteArray &buf, const QByteArray &key, const QByteArray &iv, bool pad, QByteArray *out)
+{
+ QByteArray result(buf.size()+type->block_size);
+ int len;
+ EVP_CIPHER_CTX c;
+
+ unsigned char *ivp = NULL;
+ if(!iv.isEmpty())
+ ivp = (unsigned char *)iv.data();
+ EVP_CIPHER_CTX_init(&c);
+ //EVP_CIPHER_CTX_set_padding(&c, pad ? 1: 0);
+ if(!EVP_DecryptInit_ex(&c, type, NULL, (unsigned char *)key.data(), ivp))
+ return false;
+ if(!pad) {
+ if(!EVP_EncryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ }
+ else {
+ if(!EVP_DecryptUpdate(&c, (unsigned char *)result.data(), &len, (unsigned char *)buf.data(), buf.size()))
+ return false;
+ }
+ result.resize(len);
+ if(pad) {
+ QByteArray last(type->block_size);
+ if(!EVP_DecryptFinal_ex(&c, (unsigned char *)last.data(), &len))
+ return false;
+ last.resize(len);
+ ByteStream::appendArray(&result, last);
+ }
+
+ memset(&c, 0, sizeof(EVP_CIPHER_CTX));
+ *out = result;
+ return true;
+}
+
+static bool lib_generateKeyIV(const EVP_CIPHER *type, const QByteArray &data, const QByteArray &salt, QByteArray *key, QByteArray *iv)
+{
+ QByteArray k, i;
+ unsigned char *kp = 0;
+ unsigned char *ip = 0;
+ if(key) {
+ k.resize(type->key_len);
+ kp = (unsigned char *)k.data();
+ }
+ if(iv) {
+ i.resize(type->iv_len);
+ ip = (unsigned char *)i.data();
+ }
+ if(!EVP_BytesToKey(type, EVP_sha1(), (unsigned char *)salt.data(), (unsigned char *)data.data(), data.size(), 1, kp, ip))
+ return false;
+ if(key)
+ *key = k;
+ if(iv)
+ *iv = i;
+ return true;
+}
+
+static const EVP_CIPHER * typeToCIPHER(Cipher::Type t)
+{
+ if(t == Cipher::TripleDES)
+ return EVP_des_ede3_cbc();
+ else if(t == Cipher::AES_128)
+ return EVP_aes_128_cbc();
+ else if(t == Cipher::AES_256)
+ return EVP_aes_256_cbc();
+ else
+ return 0;
+}
+
+Cipher::Key Cipher::generateKey(Type t)
+{
+ Key k;
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return k;
+ QByteArray out;
+ if(!lib_generateKeyIV(type, QRandom::randomArray(128), QRandom::randomArray(2), &out, 0))
+ return k;
+ k.setType(t);
+ k.setData(out);
+ return k;
+}
+
+QByteArray Cipher::generateIV(Type t)
+{
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_generateKeyIV(type, QCString("Get this man an iv!"), QByteArray(), 0, &out))
+ return QByteArray();
+ return out;
+}
+
+int Cipher::ivSize(Type t)
+{
+ const EVP_CIPHER *type = typeToCIPHER(t);
+ if(!type)
+ return -1;
+ return type->iv_len;
+}
+
+QByteArray Cipher::encrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+ if(ok)
+ *ok = false;
+ const EVP_CIPHER *type = typeToCIPHER(key.type());
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_encryptArray(type, buf, key.data(), iv, pad, &out))
+ return QByteArray();
+
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+QByteArray Cipher::decrypt(const QByteArray &buf, const Key &key, const QByteArray &iv, bool pad, bool *ok)
+{
+ if(ok)
+ *ok = false;
+ const EVP_CIPHER *type = typeToCIPHER(key.type());
+ if(!type)
+ return QByteArray();
+ QByteArray out;
+ if(!lib_decryptArray(type, buf, key.data(), iv, pad, &out))
+ return QByteArray();
+
+ if(ok)
+ *ok = true;
+ return out;
+}
+
+
+class RSAKey::Private
+{
+public:
+ Private() {}
+
+ RSA *rsa;
+ int ref;
+};
+
+RSAKey::RSAKey()
+{
+ d = 0;
+}
+
+RSAKey::RSAKey(const RSAKey &from)
+{
+ d = 0;
+ *this = from;
+}
+
+RSAKey & RSAKey::operator=(const RSAKey &from)
+{
+ free();
+
+ if(from.d) {
+ d = from.d;
+ ++d->ref;
+ }
+
+ return *this;
+}
+
+RSAKey::~RSAKey()
+{
+ free();
+}
+
+bool RSAKey::isNull() const
+{
+ return d ? false: true;
+}
+
+void * RSAKey::data() const
+{
+ if(d)
+ return (void *)d->rsa;
+ else
+ return 0;
+}
+
+void RSAKey::setData(void *p)
+{
+ free();
+
+ if(p) {
+ d = new Private;
+ d->ref = 1;
+ d->rsa = (RSA *)p;
+ }
+}
+
+void RSAKey::free()
+{
+ if(!d)
+ return;
+
+ --d->ref;
+ if(d->ref <= 0) {
+ RSA_free(d->rsa);
+ delete d;
+ }
+ d = 0;
+}
+
+RSAKey generateRSAKey()
+{
+ RSA *rsa = RSA_generate_key(1024, RSA_F4, NULL, NULL);
+ RSAKey key;
+ if(rsa)
+ key.setData(rsa);
+ return key;
+}
+
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ if(flen >= size - 11)
+ flen = size - 11;
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ if(flen >= size - 41)
+ flen = size - 41;
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_public_encrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
+
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok)
+{
+ if(ok)
+ *ok = false;
+
+ int size = RSA_size((RSA *)key.data());
+ int flen = buf.size();
+ QByteArray result(size);
+ unsigned char *from = (unsigned char *)buf.data();
+ unsigned char *to = (unsigned char *)result.data();
+ int r = RSA_private_decrypt(flen, from, to, (RSA *)key.data(), RSA_PKCS1_OAEP_PADDING);
+ if(r == -1)
+ return QByteArray();
+ result.resize(r);
+
+ if(ok)
+ *ok = true;
+ return result;
+}
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h
new file mode 100644
index 00000000..f162f16a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/cipher.h
@@ -0,0 +1,79 @@
+/*
+ * cipher.h - Simple wrapper to 3DES,AES128/256 CBC ciphers
+ * 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_CIPHER_H
+#define CS_CIPHER_H
+
+#include<qstring.h>
+#include<qcstring.h>
+
+namespace Cipher
+{
+ enum Type { None, TripleDES, AES_128, AES_256 };
+
+ class Key
+ {
+ public:
+ Key() { v_type = None; }
+
+ bool isValid() const { return (v_type == None ? false: true); }
+ void setType(Type x) { v_type = x; }
+ Type type() const { return v_type; }
+ void setData(const QByteArray &d) { v_data = d; }
+ const QByteArray & data() const { return v_data; }
+
+ private:
+ Type v_type;
+ QByteArray v_data;
+ };
+
+ Key generateKey(Type);
+ QByteArray generateIV(Type);
+ int ivSize(Type);
+ QByteArray encrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+ QByteArray decrypt(const QByteArray &, const Key &, const QByteArray &iv, bool pad, bool *ok=0);
+}
+
+class RSAKey
+{
+public:
+ RSAKey();
+ RSAKey(const RSAKey &);
+ RSAKey & operator=(const RSAKey &);
+ ~RSAKey();
+
+ bool isNull() const;
+ void *data() const;
+ void setData(void *);
+
+private:
+ class Private;
+ Private *d;
+
+ void free();
+};
+
+RSAKey generateRSAKey();
+QByteArray encryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray encryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+QByteArray decryptRSA2(const QByteArray &buf, const RSAKey &key, bool *ok=0);
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp
new file mode 100644
index 00000000..3becd7c5
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.cpp
@@ -0,0 +1,24 @@
+#include"qrandom.h"
+
+#include<stdlib.h>
+
+uchar QRandom::randomChar()
+{
+ return rand();
+}
+
+uint QRandom::randomInt()
+{
+ QByteArray a = randomArray(sizeof(uint));
+ uint x;
+ memcpy(&x, a.data(), a.size());
+ return x;
+}
+
+QByteArray QRandom::randomArray(uint size)
+{
+ QByteArray a(size);
+ for(uint n = 0; n < size; ++n)
+ a[n] = randomChar();
+ return a;
+}
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h
new file mode 100644
index 00000000..92339fb0
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/qrandom.h
@@ -0,0 +1,14 @@
+#ifndef CS_QRANDOM_H
+#define CS_QRANDOM_H
+
+#include<qcstring.h>
+
+class QRandom
+{
+public:
+ static uchar randomChar();
+ static uint randomInt();
+ static QByteArray randomArray(uint size);
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp
new file mode 100644
index 00000000..6bd012e9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.cpp
@@ -0,0 +1,119 @@
+#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/jabber/libiris/cutestuff/util/safedelete.h b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h
new file mode 100644
index 00000000..078d36cd
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/safedelete.h
@@ -0,0 +1,60 @@
+#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/jabber/libiris/cutestuff/util/sha1.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp
new file mode 100644
index 00000000..3e3eb07c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.cpp
@@ -0,0 +1,196 @@
+/*
+ * sha1.cpp - Secure Hash Algorithm 1
+ * 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"sha1.h"
+
+// CS_NAMESPACE_BEGIN
+
+/****************************************************************************
+ SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+****************************************************************************/
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+SHA1::SHA1()
+{
+ int wordSize;
+
+ qSysInfo(&wordSize, &bigEndian);
+}
+
+unsigned long SHA1::blk0(Q_UINT32 i)
+{
+ if(bigEndian)
+ return block->l[i];
+ else
+ return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+}
+
+// Hash a single 512-bit block. This is the core of the algorithm.
+void SHA1::transform(Q_UINT32 state[5], unsigned char buffer[64])
+{
+ Q_UINT32 a, b, c, d, e;
+
+ block = (CHAR64LONG16*)buffer;
+
+ // Copy context->state[] to working vars
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ // Add the working vars back into context.state[]
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ // Wipe variables
+ a = b = c = d = e = 0;
+}
+
+// SHA1Init - Initialize new context
+void SHA1::init(SHA1_CONTEXT* context)
+{
+ // SHA1 initialization constants
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+// Run your data through this
+void SHA1::update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+{
+ Q_UINT32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+
+ context->count[1] += (len >> 29);
+
+ if((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+// Add padding and return the message digest
+void SHA1::final(unsigned char digest[20], SHA1_CONTEXT* context)
+{
+ Q_UINT32 i, j;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); // Endian independent
+ }
+ update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ update(context, (unsigned char *)"\0", 1);
+ }
+ update(context, finalcount, 8); // Should cause a transform()
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ // Wipe variables
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+}
+
+QByteArray SHA1::hash(const QByteArray &a)
+{
+ SHA1_CONTEXT context;
+ QByteArray b(20);
+
+ SHA1 s;
+ s.init(&context);
+ s.update(&context, (unsigned char *)a.data(), (unsigned int)a.size());
+ s.final((unsigned char *)b.data(), &context);
+ return b;
+}
+
+QByteArray SHA1::hashString(const QCString &cs)
+{
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return SHA1::hash(a);
+}
+
+QString SHA1::digest(const QString &in)
+{
+ QByteArray a = SHA1::hashString(in.utf8());
+ QString out;
+ for(int n = 0; n < (int)a.size(); ++n) {
+ QString str;
+ str.sprintf("%02x", (uchar)a[n]);
+ out.append(str);
+ }
+
+ return out;
+}
+
+// CS_NAMESPACE_END
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h
new file mode 100644
index 00000000..6b0453b4
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/sha1.h
@@ -0,0 +1,63 @@
+/*
+ * sha1.h - Secure Hash Algorithm 1
+ * 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_SHA1_H
+#define CS_SHA1_H
+
+#include<qstring.h>
+
+// CS_NAMESPACE_BEGIN
+
+class SHA1
+{
+public:
+ static QByteArray hash(const QByteArray &);
+ static QByteArray hashString(const QCString &);
+ static QString digest(const QString &);
+
+private:
+ SHA1();
+
+ struct SHA1_CONTEXT
+ {
+ Q_UINT32 state[5];
+ Q_UINT32 count[2];
+ unsigned char buffer[64];
+ };
+
+ typedef union {
+ unsigned char c[64];
+ Q_UINT32 l[16];
+ } CHAR64LONG16;
+
+ void transform(Q_UINT32 state[5], unsigned char buffer[64]);
+ void init(SHA1_CONTEXT* context);
+ void update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len);
+ void final(unsigned char digest[20], SHA1_CONTEXT* context);
+
+ unsigned long blk0(Q_UINT32 i);
+ bool bigEndian;
+
+ CHAR64LONG16* block;
+};
+
+// CS_NAMESPACE_END
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp
new file mode 100644
index 00000000..0b02df60
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.cpp
@@ -0,0 +1,61 @@
+/*
+ * showtextdlg.cpp - dialog for displaying a text file
+ * 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"showtextdlg.h"
+
+#include<qlayout.h>
+#include<qtextedit.h>
+#include<qpushbutton.h>
+#include<qfile.h>
+#include<qtextstream.h>
+
+
+ShowTextDlg::ShowTextDlg(const QString &fname, bool rich, QWidget *parent, const char *name)
+:QDialog(parent, name, FALSE, WDestructiveClose)
+{
+ QString text;
+
+ QFile f(fname);
+ if(f.open(IO_ReadOnly)) {
+ QTextStream t(&f);
+ while(!t.eof())
+ text += t.readLine() + '\n';
+ f.close();
+ }
+
+ QVBoxLayout *vb1 = new QVBoxLayout(this, 8);
+ QTextEdit *te = new QTextEdit(this);
+ te->setReadOnly(TRUE);
+ te->setTextFormat(rich ? QTextEdit::RichText : QTextEdit::PlainText);
+ te->setText(text);
+
+ vb1->addWidget(te);
+
+ QHBoxLayout *hb1 = new QHBoxLayout(vb1);
+ hb1->addStretch(1);
+ QPushButton *pb = new QPushButton(tr("&OK"), this);
+ connect(pb, SIGNAL(clicked()), SLOT(accept()));
+ hb1->addWidget(pb);
+ hb1->addStretch(1);
+
+ resize(560, 384);
+}
+
+#include "showtextdlg.moc"
diff --git a/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h
new file mode 100644
index 00000000..f59ae32c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/cutestuff/util/showtextdlg.h
@@ -0,0 +1,33 @@
+/*
+ * showtextdlg.h - dialog for displaying a text file
+ * 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_SHOWTEXTDLG_H
+#define CS_SHOWTEXTDLG_H
+
+#include<qdialog.h>
+
+class ShowTextDlg : public QDialog
+{
+ Q_OBJECT
+public:
+ ShowTextDlg(const QString &fname, bool rich=FALSE, QWidget *parent=0, const char *name=0);
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/Makefile.am b/kopete/protocols/jabber/libiris/iris/Makefile.am
new file mode 100644
index 00000000..03e5818f
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = include jabber xmpp-core xmpp-im
diff --git a/kopete/protocols/jabber/libiris/iris/TODO b/kopete/protocols/jabber/libiris/iris/TODO
new file mode 100644
index 00000000..e6cf74c6
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/TODO
@@ -0,0 +1,16 @@
+- Stream::id(), Stream::lang()
+- whitespace pings (but disable when using http poll)
+- make stanza error conditions work for both 1.0 and old
+
+- xmpp-im (messages, roster, subscriptions, presence, privacy)
+- document xmpp-core
+- provide complete support for xmpp-core. this means all functionality from
+ the draft, and noting behavior issues (like IQ semantics) in the
+ library documentation.
+
+- SASL "EXTERNAL" w/ client certificate
+- SASL "ANONYMOUS" ?
+
+credits:
+ MD5 algorithm by Peter Deutsch (Aladdin Enterprises)
+
diff --git a/kopete/protocols/jabber/libiris/iris/include/Makefile.am b/kopete/protocols/jabber/libiris/iris/include/Makefile.am
new file mode 100644
index 00000000..6375392b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/Makefile.am
@@ -0,0 +1,7 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libiris.la
+INCLUDES = -Ixmpp-core -Ixmpp-im -I../cutestuff/util -I../cutestuff/network -I../qca/src $(all_includes)
+
+libiris_la_SOURCES = \
+ empty.cpp
diff --git a/kopete/protocols/jabber/libiris/iris/include/empty.cpp b/kopete/protocols/jabber/libiris/iris/include/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/empty.cpp
diff --git a/kopete/protocols/jabber/libiris/iris/include/im.h b/kopete/protocols/jabber/libiris/iris/include/im.h
new file mode 100644
index 00000000..832ec62a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/im.h
@@ -0,0 +1,721 @@
+/*
+ * im.h - XMPP "IM" library 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 XMPP_IM_H
+#define XMPP_IM_H
+
+#include<qdatetime.h>
+#include<qvaluelist.h>
+#include"xmpp.h"
+
+namespace XMPP
+{
+ class Url
+ {
+ public:
+ Url(const QString &url="", const QString &desc="");
+ Url(const Url &);
+ Url & operator=(const Url &);
+ ~Url();
+
+ QString url() const;
+ QString desc() const;
+
+ void setUrl(const QString &);
+ void setDesc(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ typedef QValueList<Url> UrlList;
+ typedef QMap<QString, QString> StringMap;
+ typedef enum { OfflineEvent, DeliveredEvent, DisplayedEvent,
+ ComposingEvent, CancelEvent, InactiveEvent, GoneEvent } MsgEvent;
+
+ class Message
+ {
+ public:
+ Message(const Jid &to="");
+ Message(const Message &from);
+ Message & operator=(const Message &from);
+ ~Message();
+
+ Jid to() const;
+ Jid from() const;
+ QString id() const;
+ QString type() const;
+ QString lang() const;
+ QString subject(const QString &lang="") const;
+ QString body(const QString &lang="") const;
+ QString xHTMLBody(const QString &lang="") const;
+ QString thread() const;
+ Stanza::Error error() const;
+
+ void setTo(const Jid &j);
+ void setFrom(const Jid &j);
+ void setId(const QString &s);
+ void setType(const QString &s);
+ void setLang(const QString &s);
+ void setSubject(const QString &s, const QString &lang="");
+ void setBody(const QString &s, const QString &lang="");
+ void setXHTMLBody(const QString &s, const QString &lang="", const QString &attr = "");
+ void setThread(const QString &s);
+ void setError(const Stanza::Error &err);
+
+ // JEP-0091
+ QDateTime timeStamp() const;
+ void setTimeStamp(const QDateTime &ts);
+
+ // JEP-0066
+ UrlList urlList() const;
+ void urlAdd(const Url &u);
+ void urlsClear();
+ void setUrlList(const UrlList &list);
+
+ // JEP-0022
+ QString eventId() const;
+ void setEventId(const QString& id);
+ bool containsEvents() const;
+ bool containsEvent(MsgEvent e) const;
+ void addEvent(MsgEvent e);
+
+ // JEP-0027
+ QString xencrypted() const;
+ void setXEncrypted(const QString &s);
+
+ // Obsolete invitation
+ QString invite() const;
+ void setInvite(const QString &s);
+
+ // for compatibility. delete me later
+ bool spooled() const;
+ void setSpooled(bool);
+ bool wasEncrypted() const;
+ void setWasEncrypted(bool);
+
+ Stanza toStanza(Stream *stream) const;
+ bool fromStanza(const Stanza &s, int tzoffset);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class Subscription
+ {
+ public:
+ enum SubType { None, To, From, Both, Remove };
+
+ Subscription(SubType type=None);
+
+ int type() const;
+
+ QString toString() const;
+ bool fromString(const QString &);
+
+ private:
+ SubType value;
+ };
+
+ class Status
+ {
+ public:
+ Status(const QString &show="", const QString &status="", int priority=0, bool available=true);
+ ~Status();
+
+ int priority() const;
+ const QString & show() const;
+ const QString & status() const;
+ QDateTime timeStamp() const;
+ const QString & keyID() const;
+ bool isAvailable() const;
+ bool isAway() const;
+ bool isInvisible() const;
+ bool hasError() const;
+ int errorCode() const;
+ const QString & errorString() const;
+
+ const QString & xsigned() const;
+ const QString & songTitle() const;
+ const QString & capsNode() const;
+ const QString & capsVersion() const;
+ const QString & capsExt() const;
+
+ void setPriority(int);
+ void setShow(const QString &);
+ void setStatus(const QString &);
+ void setTimeStamp(const QDateTime &);
+ void setKeyID(const QString &);
+ void setIsAvailable(bool);
+ void setIsInvisible(bool);
+ void setError(int, const QString &);
+ void setCapsNode(const QString&);
+ void setCapsVersion(const QString&);
+ void setCapsExt(const QString&);
+
+ void setXSigned(const QString &);
+ void setSongTitle(const QString &);
+
+ private:
+ int v_priority;
+ QString v_show, v_status, v_key;
+ QDateTime v_timeStamp;
+ bool v_isAvailable;
+ bool v_isInvisible;
+
+ QString v_xsigned;
+ // gabber song extension
+ QString v_songTitle;
+ QString v_capsNode, v_capsVersion, v_capsExt;
+
+ int ecode;
+ QString estr;
+
+ class Private;
+ Private *d;
+ };
+
+ class Resource
+ {
+ public:
+ Resource(const QString &name="", const Status &s=Status());
+ ~Resource();
+
+ const QString & name() const;
+ int priority() const;
+ const Status & status() const;
+
+ void setName(const QString &);
+ void setStatus(const Status &);
+
+ private:
+ QString v_name;
+ Status v_status;
+
+ class ResourcePrivate *d;
+ };
+
+ class ResourceList : public QValueList<Resource>
+ {
+ public:
+ ResourceList();
+ ~ResourceList();
+
+ ResourceList::Iterator find(const QString &);
+ ResourceList::Iterator priority();
+
+ ResourceList::ConstIterator find(const QString &) const;
+ ResourceList::ConstIterator priority() const;
+
+ private:
+ class ResourceListPrivate *d;
+ };
+
+ class RosterItem
+ {
+ public:
+ RosterItem(const Jid &jid="");
+ virtual ~RosterItem();
+
+ const Jid & jid() const;
+ const QString & name() const;
+ const QStringList & groups() const;
+ const Subscription & subscription() const;
+ const QString & ask() const;
+ bool isPush() const;
+ bool inGroup(const QString &) const;
+
+ virtual void setJid(const Jid &);
+ void setName(const QString &);
+ void setGroups(const QStringList &);
+ void setSubscription(const Subscription &);
+ void setAsk(const QString &);
+ void setIsPush(bool);
+ bool addGroup(const QString &);
+ bool removeGroup(const QString &);
+
+ QDomElement toXml(QDomDocument *) const;
+ bool fromXml(const QDomElement &);
+
+ private:
+ Jid v_jid;
+ QString v_name;
+ QStringList v_groups;
+ Subscription v_subscription;
+ QString v_ask;
+ bool v_push;
+
+ class RosterItemPrivate *d;
+ };
+
+ class Roster : public QValueList<RosterItem>
+ {
+ public:
+ Roster();
+ ~Roster();
+
+ Roster::Iterator find(const Jid &);
+ Roster::ConstIterator find(const Jid &) const;
+
+ private:
+ class RosterPrivate *d;
+ };
+
+ class Features
+ {
+ public:
+ Features();
+ Features(const QStringList &);
+ Features(const QString &);
+ ~Features();
+
+ QStringList list() const; // actual featurelist
+ void setList(const QStringList &);
+
+ // features
+ bool canRegister() const;
+ bool canSearch() const;
+ bool canGroupchat() const;
+ bool canVoice() const;
+ bool canDisco() const;
+ bool canXHTML() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+
+ enum FeatureID {
+ FID_Invalid = -1,
+ FID_None,
+ FID_Register,
+ FID_Search,
+ FID_Groupchat,
+ FID_Disco,
+ FID_Gateway,
+ FID_VCard,
+ FID_Xhtml,
+
+ // private Psi actions
+ FID_Add
+ };
+
+ // useful functions
+ bool test(const QStringList &) const;
+
+ QString name() const;
+ static QString name(long id);
+ static QString name(const QString &feature);
+
+ long id() const;
+ static long id(const QString &feature);
+ static QString feature(long id);
+
+ class FeatureName;
+ private:
+ QStringList _list;
+ };
+
+ class AgentItem
+ {
+ public:
+ AgentItem() { }
+
+ const Jid & jid() const { return v_jid; }
+ const QString & name() const { return v_name; }
+ const QString & category() const { return v_category; }
+ const QString & type() const { return v_type; }
+ const Features & features() const { return v_features; }
+
+ void setJid(const Jid &j) { v_jid = j; }
+ void setName(const QString &n) { v_name = n; }
+ void setCategory(const QString &c) { v_category = c; }
+ void setType(const QString &t) { v_type = t; }
+ void setFeatures(const Features &f) { v_features = f; }
+
+ private:
+ Jid v_jid;
+ QString v_name, v_category, v_type;
+ Features v_features;
+ };
+
+ typedef QValueList<AgentItem> AgentList;
+
+ class DiscoItem
+ {
+ public:
+ DiscoItem();
+ ~DiscoItem();
+
+ const Jid &jid() const;
+ const QString &node() const;
+ const QString &name() const;
+
+ void setJid(const Jid &);
+ void setName(const QString &);
+ void setNode(const QString &);
+
+ enum Action {
+ None = 0,
+ Remove,
+ Update
+ };
+
+ Action action() const;
+ void setAction(Action);
+
+ const Features &features() const;
+ void setFeatures(const Features &);
+
+ struct Identity
+ {
+ QString category;
+ QString name;
+ QString type;
+ };
+
+ typedef QValueList<Identity> Identities;
+
+ const Identities &identities() const;
+ void setIdentities(const Identities &);
+
+ // some useful helper functions
+ static Action string2action(QString s);
+ static QString action2string(Action a);
+
+ DiscoItem & operator= (const DiscoItem &);
+ DiscoItem(const DiscoItem &);
+
+ operator AgentItem() const { return toAgentItem(); }
+ AgentItem toAgentItem() const;
+ void fromAgentItem(const AgentItem &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ typedef QValueList<DiscoItem> DiscoList;
+
+ class FormField
+ {
+ public:
+ enum { username, nick, password, name, first, last, email, address, city, state, zip, phone, url, date, misc };
+ FormField(const QString &type="", const QString &value="");
+ ~FormField();
+
+ int type() const;
+ QString fieldName() const;
+ QString realName() const;
+ bool isSecret() const;
+ const QString & value() const;
+ void setType(int);
+ bool setType(const QString &);
+ void setValue(const QString &);
+
+ private:
+ int tagNameToType(const QString &) const;
+ QString typeToTagName(int) const;
+
+ int v_type;
+ QString v_value;
+
+ class Private;
+ Private *d;
+ };
+
+ class Form : public QValueList<FormField>
+ {
+ public:
+ Form(const Jid &j="");
+ ~Form();
+
+ Jid jid() const;
+ QString instructions() const;
+ QString key() const;
+ void setJid(const Jid &);
+ void setInstructions(const QString &);
+ void setKey(const QString &);
+
+ private:
+ Jid v_jid;
+ QString v_instructions, v_key;
+
+ class Private;
+ Private *d;
+ };
+
+ class SearchResult
+ {
+ public:
+ SearchResult(const Jid &jid="");
+ ~SearchResult();
+
+ const Jid & jid() const;
+ const QString & nick() const;
+ const QString & first() const;
+ const QString & last() const;
+ const QString & email() const;
+
+ void setJid(const Jid &);
+ void setNick(const QString &);
+ void setFirst(const QString &);
+ void setLast(const QString &);
+ void setEmail(const QString &);
+
+ private:
+ Jid v_jid;
+ QString v_nick, v_first, v_last, v_email;
+ };
+
+ class Client;
+ class LiveRosterItem;
+ class LiveRoster;
+ class S5BManager;
+ class IBBManager;
+ class JidLinkManager;
+ class FileTransferManager;
+
+ class Task : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { ErrDisc };
+ Task(Task *parent);
+ Task(Client *, bool isRoot);
+ virtual ~Task();
+
+ Task *parent() const;
+ Client *client() const;
+ QDomDocument *doc() const;
+ QString id() const;
+
+ bool success() const;
+ int statusCode() const;
+ const QString & statusString() const;
+
+ void go(bool autoDelete=false);
+ virtual bool take(const QDomElement &);
+ void safeDelete();
+
+ signals:
+ void finished();
+
+ protected:
+ virtual void onGo();
+ virtual void onDisconnect();
+ void send(const QDomElement &);
+ void setSuccess(int code=0, const QString &str="");
+ void setError(const QDomElement &);
+ void setError(int code=0, const QString &str="");
+ void debug(const char *, ...);
+ void debug(const QString &);
+ bool iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns="");
+
+ private slots:
+ void clientDisconnected();
+ void done();
+
+ private:
+ void init();
+
+ class TaskPrivate;
+ TaskPrivate *d;
+ };
+
+ class Client : public QObject
+ {
+ Q_OBJECT
+
+ public:
+ Client(QObject *parent=0);
+ ~Client();
+
+ bool isActive() const;
+ void connectToServer(ClientStream *s, const Jid &j, bool auth=true);
+ void start(const QString &host, const QString &user, const QString &pass, const QString &resource);
+ void close(bool fast=false);
+
+ Stream & stream();
+ const LiveRoster & roster() const;
+ const ResourceList & resourceList() const;
+
+ void send(const QDomElement &);
+ void send(const QString &);
+
+ QString host() const;
+ QString user() const;
+ QString pass() const;
+ QString resource() const;
+ Jid jid() const;
+
+ void rosterRequest();
+ void sendMessage(const Message &);
+ void sendSubscription(const Jid &, const QString &);
+ void setPresence(const Status &);
+
+ void debug(const QString &);
+ QString genUniqueId();
+ Task *rootTask();
+ QDomDocument *doc() const;
+
+ QString OSName() const;
+ QString timeZone() const;
+ int timeZoneOffset() const;
+ QString clientName() const;
+ QString clientVersion() const;
+ QString capsNode() const;
+ QString capsVersion() const;
+ QString capsExt() const;
+
+ void setOSName(const QString &);
+ void setTimeZone(const QString &, int);
+ void setClientName(const QString &);
+ void setClientVersion(const QString &);
+ void setCapsNode(const QString &);
+ void setCapsVersion(const QString &);
+
+ void setIdentity(DiscoItem::Identity);
+ DiscoItem::Identity identity();
+
+ void addExtension(const QString& ext, const Features& f);
+ void removeExtension(const QString& ext);
+ const Features& extension(const QString& ext) const;
+ QStringList extensions() const;
+
+ S5BManager *s5bManager() const;
+ IBBManager *ibbManager() const;
+ JidLinkManager *jidLinkManager() const;
+
+ void setFileTransferEnabled(bool b);
+ FileTransferManager *fileTransferManager() const;
+
+ bool groupChatJoin(const QString &host, const QString &room, const QString &nick);
+ bool groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password);
+ void groupChatSetStatus(const QString &host, const QString &room, const Status &);
+ void groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &);
+ void groupChatLeave(const QString &host, const QString &room);
+
+ signals:
+ void activated();
+ void disconnected();
+ //void authFinished(bool, int, const QString &);
+ void rosterRequestFinished(bool, int, const QString &);
+ void rosterItemAdded(const RosterItem &);
+ void rosterItemUpdated(const RosterItem &);
+ void rosterItemRemoved(const RosterItem &);
+ void resourceAvailable(const Jid &, const Resource &);
+ void resourceUnavailable(const Jid &, const Resource &);
+ void presenceError(const Jid &, int, const QString &);
+ void subscription(const Jid &, const QString &);
+ void messageReceived(const Message &);
+ void debugText(const QString &);
+ void xmlIncoming(const QString &);
+ void xmlOutgoing(const QString &);
+ void groupChatJoined(const Jid &);
+ void groupChatLeft(const Jid &);
+ void groupChatPresence(const Jid &, const Status &);
+ void groupChatError(const Jid &, int, const QString &);
+
+ void incomingJidLink();
+
+ private slots:
+ //void streamConnected();
+ //void streamHandshaken();
+ //void streamError(const StreamError &);
+ //void streamSSLCertificateReady(const QSSLCert &);
+ //void streamCloseFinished();
+ void streamError(int);
+ void streamReadyRead();
+ void streamIncomingXml(const QString &);
+ void streamOutgoingXml(const QString &);
+
+ void slotRosterRequestFinished();
+
+ // basic daemons
+ void ppSubscription(const Jid &, const QString &);
+ void ppPresence(const Jid &, const Status &);
+ void pmMessage(const Message &);
+ void prRoster(const Roster &);
+
+ void s5b_incomingReady();
+ void ibb_incomingReady();
+
+ public:
+ class GroupChat;
+ private:
+ void cleanup();
+ void distribute(const QDomElement &);
+ void importRoster(const Roster &);
+ void importRosterItem(const RosterItem &);
+ void updateSelfPresence(const Jid &, const Status &);
+ void updatePresence(LiveRosterItem *, const Jid &, const Status &);
+
+ class ClientPrivate;
+ ClientPrivate *d;
+ };
+
+ class LiveRosterItem : public RosterItem
+ {
+ public:
+ LiveRosterItem(const Jid &j="");
+ LiveRosterItem(const RosterItem &);
+ ~LiveRosterItem();
+
+ void setRosterItem(const RosterItem &);
+
+ ResourceList & resourceList();
+ ResourceList::Iterator priority();
+
+ const ResourceList & resourceList() const;
+ ResourceList::ConstIterator priority() const;
+
+ bool isAvailable() const;
+ const Status & lastUnavailableStatus() const;
+ bool flagForDelete() const;
+
+ void setLastUnavailableStatus(const Status &);
+ void setFlagForDelete(bool);
+
+ private:
+ ResourceList v_resourceList;
+ Status v_lastUnavailableStatus;
+ bool v_flagForDelete;
+
+ class LiveRosterItemPrivate;
+ LiveRosterItemPrivate *d;
+ };
+
+ class LiveRoster : public QValueList<LiveRosterItem>
+ {
+ public:
+ LiveRoster();
+ ~LiveRoster();
+
+ void flagAllForDelete();
+ LiveRoster::Iterator find(const Jid &, bool compareRes=true);
+ LiveRoster::ConstIterator find(const Jid &, bool compareRes=true) const;
+
+ private:
+ class LiveRosterPrivate;
+ LiveRosterPrivate *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/include/xmpp.h b/kopete/protocols/jabber/libiris/iris/include/xmpp.h
new file mode 100644
index 00000000..5636f963
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/include/xmpp.h
@@ -0,0 +1,553 @@
+/*
+ * xmpp.h - XMPP "core" library 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 XMPP_H
+#define XMPP_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include<qhostaddress.h>
+#include<qstring.h>
+#include<qcstring.h>
+#include<qxml.h>
+#include<qdom.h>
+
+namespace QCA
+{
+ class TLS;
+}
+
+#ifndef CS_XMPP
+class ByteStream;
+#endif
+
+namespace XMPP
+{
+ // CS_IMPORT_BEGIN cutestuff/bytestream.h
+#ifdef CS_XMPP
+ class ByteStream;
+#endif
+ // CS_IMPORT_END
+
+ class Debug
+ {
+ public:
+ virtual ~Debug();
+
+ virtual void msg(const QString &)=0;
+ virtual void outgoingTag(const QString &)=0;
+ virtual void incomingTag(const QString &)=0;
+ virtual void outgoingXml(const QDomElement &)=0;
+ virtual void incomingXml(const QDomElement &)=0;
+ };
+
+ void setDebug(Debug *);
+
+ 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;
+ };
+
+ class AdvancedConnector : public Connector
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrConnectionRefused, ErrHostNotFound, ErrProxyConnect, ErrProxyNeg, ErrProxyAuth, ErrStream };
+ AdvancedConnector(QObject *parent=0);
+ virtual ~AdvancedConnector();
+
+ class Proxy
+ {
+ public:
+ enum { None, HttpConnect, HttpPoll, Socks };
+ Proxy();
+ ~Proxy();
+
+ int type() const;
+ QString host() const;
+ Q_UINT16 port() const;
+ QString url() const;
+ QString user() const;
+ QString pass() const;
+ int pollInterval() const;
+
+ void setHttpConnect(const QString &host, Q_UINT16 port);
+ void setHttpPoll(const QString &host, Q_UINT16 port, const QString &url);
+ void setSocks(const QString &host, Q_UINT16 port);
+ void setUserPass(const QString &user, const QString &pass);
+ void setPollInterval(int secs);
+
+ private:
+ int t;
+ QString v_host, v_url;
+ Q_UINT16 v_port;
+ QString v_user, v_pass;
+ int v_poll;
+ };
+
+ void setProxy(const Proxy &proxy);
+ void setOptHostPort(const QString &host, Q_UINT16 port);
+ void setOptProbe(bool);
+ void setOptSSL(bool);
+
+ void changePollInterval(int secs);
+
+ void connectToServer(const QString &server);
+ ByteStream *stream() const;
+ void done();
+
+ int errorCode() const;
+
+ signals:
+ void srvLookup(const QString &server);
+ void srvResult(bool success);
+ void httpSyncStarted();
+ void httpSyncFinished();
+
+ private slots:
+ void dns_done();
+ void srv_done();
+ void bs_connected();
+ void bs_error(int);
+ void http_syncStarted();
+ void http_syncFinished();
+
+ private:
+ class Private;
+ Private *d;
+
+ void cleanup();
+ void do_resolve();
+ void do_connect();
+ void tryNextSrv();
+ };
+
+ 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);
+ };
+
+ 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;
+ };
+
+ class Jid
+ {
+ public:
+ Jid();
+ ~Jid();
+
+ Jid(const QString &s);
+ Jid(const char *s);
+ Jid & operator=(const QString &s);
+ Jid & operator=(const char *s);
+
+ void set(const QString &s);
+ void set(const QString &domain, const QString &node, const QString &resource="");
+
+ void setDomain(const QString &s);
+ void setNode(const QString &s);
+ void setResource(const QString &s);
+
+ const QString & domain() const { return d; }
+ const QString & node() const { return n; }
+ const QString & resource() const { return r; }
+ const QString & bare() const { return b; }
+ const QString & full() const { return f; }
+
+ Jid withNode(const QString &s) const;
+ Jid withResource(const QString &s) const;
+
+ bool isValid() const;
+ bool isEmpty() const;
+ bool compare(const Jid &a, bool compareRes=true) const;
+
+ static bool validDomain(const QString &s, QString *norm=0);
+ static bool validNode(const QString &s, QString *norm=0);
+ static bool validResource(const QString &s, QString *norm=0);
+
+ // TODO: kill these later
+ const QString & host() const { return d; }
+ const QString & user() const { return n; }
+ const QString & userHost() const { return b; }
+
+ private:
+ void reset();
+ void update();
+
+ QString f, b, d, n, r;
+ bool valid;
+ };
+
+ class Stream;
+ class Stanza
+ {
+ public:
+ enum Kind { Message, Presence, IQ };
+ enum ErrorType { Cancel, Continue, Modify, Auth, Wait };
+ enum ErrorCond
+ {
+ BadRequest,
+ Conflict,
+ FeatureNotImplemented,
+ Forbidden,
+ InternalServerError,
+ ItemNotFound,
+ JidMalformed,
+ NotAllowed,
+ PaymentRequired,
+ RecipientUnavailable,
+ RegistrationRequired,
+ ServerNotFound,
+ ServerTimeout,
+ ResourceConstraint,
+ ServiceUnavailable,
+ SubscriptionRequired,
+ UndefinedCondition,
+ UnexpectedRequest
+ };
+
+ Stanza();
+ Stanza(const Stanza &from);
+ Stanza & operator=(const Stanza &from);
+ virtual ~Stanza();
+
+ class Error
+ {
+ public:
+ Error(int type=Cancel, int condition=UndefinedCondition, const QString &text="", const QDomElement &appSpec=QDomElement());
+
+ int type;
+ int condition;
+ QString text;
+ QDomElement appSpec;
+ };
+
+ bool isNull() const;
+
+ QDomElement element() const;
+ QString toString() const;
+
+ QDomDocument & doc() const;
+ QString baseNS() const;
+ QString xhtmlImNS() const;
+ QString xhtmlNS() const;
+ QDomElement createElement(const QString &ns, const QString &tagName);
+ QDomElement createTextElement(const QString &ns, const QString &tagName, const QString &text);
+ QDomElement createXHTMLElement(const QString &xHTML);
+ void appendChild(const QDomElement &e);
+
+ Kind kind() const;
+ void setKind(Kind k);
+
+ Jid to() const;
+ Jid from() const;
+ QString id() const;
+ QString type() const;
+ QString lang() const;
+
+ void setTo(const Jid &j);
+ void setFrom(const Jid &j);
+ void setId(const QString &id);
+ void setType(const QString &type);
+ void setLang(const QString &lang);
+
+ Error error() const;
+ void setError(const Error &err);
+ void clearError();
+
+ private:
+ friend class Stream;
+ Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id);
+ Stanza(Stream *s, const QDomElement &e);
+
+ class Private;
+ Private *d;
+ };
+
+ class Stream : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum Error { ErrParse, ErrProtocol, ErrStream, ErrCustom = 10 };
+ enum StreamCond {
+ GenericStreamError,
+ Conflict,
+ ConnectionTimeout,
+ InternalServerError,
+ InvalidFrom,
+ InvalidXml,
+ PolicyViolation,
+ ResourceConstraint,
+ SystemShutdown
+ };
+
+ Stream(QObject *parent=0);
+ virtual ~Stream();
+
+ virtual QDomDocument & doc() const=0;
+ virtual QString baseNS() const=0;
+ virtual QString xhtmlImNS() const=0;
+ virtual QString xhtmlNS() const=0;
+ virtual bool old() const=0;
+
+ virtual void close()=0;
+ virtual bool stanzaAvailable() const=0;
+ virtual Stanza read()=0;
+ virtual void write(const Stanza &s)=0;
+
+ virtual int errorCondition() const=0;
+ virtual QString errorText() const=0;
+ virtual QDomElement errorAppSpec() 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 QDomElement &e, bool clip=false);
+
+ signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+ void stanzaWritten();
+ void error(int);
+ };
+
+ 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
+ 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
+ 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(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls=0, QObject *parent=0); // server
+ ~ClientStream();
+
+ Jid jid() const;
+ void connectToServer(const Jid &jid, 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();
+
+ // SASL information
+ QString saslMechanism() const;
+ int saslSSF() const;
+
+ // binding
+ void setResourceBinding(bool);
+
+ // security options (old protocol only uses the first !)
+ void setAllowPlain(bool);
+ void setRequireMutualAuth(bool);
+ void setSSFRange(int low, int high);
+ void setOldOnly(bool);
+ void setSASLMechanism(const QString &s);
+ void setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ // reimplemented
+ QDomDocument & doc() const;
+ QString baseNS() const;
+ QString xhtmlImNS() const;
+ QString xhtmlNS() const;
+ bool old() const;
+
+ void close();
+ bool stanzaAvailable() const;
+ Stanza read();
+ void write(const Stanza &s);
+
+ int errorCondition() const;
+ QString errorText() const;
+ QDomElement errorAppSpec() const;
+
+ // extra
+ void writeDirect(const QString &s);
+ void setNoopTime(int mills);
+
+ signals:
+ void connected();
+ void securityLayerActivated(int);
+ void needAuthParams(bool user, bool pass, bool realm);
+ void authenticated();
+ void warning(int);
+ void incomingXml(const QString &s);
+ void outgoingXml(const QString &s);
+
+ public slots:
+ void continueAfterWarning();
+
+ private slots:
+ void cr_connected();
+ void cr_error();
+
+ 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 sasl_clientFirstStep(const QString &mech, const QByteArray *clientInit);
+ void sasl_nextStep(const QByteArray &stepData);
+ void sasl_needParams(bool user, bool authzid, bool pass, bool realm);
+ void sasl_authCheck(const QString &user, const QString &authzid);
+ void sasl_authenticated();
+ void sasl_error(int);
+
+ void doNoop();
+ void doReadyRead();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool all=false);
+ void processNext();
+ int convertedSASLCond() const;
+ bool handleNeed();
+ void handleError();
+ void srvProcessNext();
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am b/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am
new file mode 100644
index 00000000..d480984d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/Makefile.am
@@ -0,0 +1,15 @@
+# we deal with s5b.moc separately since KDE's build system can't cope with Q_OBJECT in .cpp files
+METASOURCES = filetransfer.moc xmpp_ibb.moc xmpp_jidlink.moc
+
+noinst_LTLIBRARIES = libiris_jabber.la
+INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../xmpp-core -I$(srcdir)/../xmpp-im -I$(srcdir)/../../cutestuff/util -I$(srcdir)/../../cutestuff/network -I$(srcdir)/../../qca/src $(all_includes)
+
+libiris_jabber_la_SOURCES = \
+ filetransfer.cpp s5b.cpp xmpp_ibb.cpp xmpp_jidlink.cpp all_mocs.cpp
+
+s5b.lo: s5b.moc
+
+CLEANFILES = s5b.moc
+s5b.moc: $(srcdir)/s5b.cpp $(srcdir)/s5b.h
+ ${MOC} $(srcdir)/s5b.h > $@
+ ${MOC} $(srcdir)/s5b.cpp >> $@
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp b/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp
new file mode 100644
index 00000000..f962a854
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/all_mocs.cpp
@@ -0,0 +1,23 @@
+/*
+ * all_mocs.cpp - #include all .moc files in this directory
+ * Copyright (C) 2004 Richard Smith
+ *
+ * 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 "filetransfer.moc"
+#include "xmpp_ibb.moc"
+#include "xmpp_jidlink.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp
new file mode 100644
index 00000000..1697b6a2
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.cpp
@@ -0,0 +1,770 @@
+/*
+ * filetransfer.cpp - File Transfer
+ * Copyright (C) 2004 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"filetransfer.h"
+
+#include<qtimer.h>
+#include<qptrlist.h>
+#include<qguardedptr.h>
+#include<qfileinfo.h>
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+
+#define SENDBUFSIZE 65536
+
+using namespace XMPP;
+
+// firstChildElement
+//
+// Get an element's first child element
+static QDomElement firstChildElement(const QDomElement &e)
+{
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ if(n.isElement())
+ return n.toElement();
+ }
+ return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// FileTransfer
+//----------------------------------------------------------------------------
+class FileTransfer::Private
+{
+public:
+ FileTransferManager *m;
+ JT_FT *ft;
+ Jid peer;
+ QString fname;
+ Q_LLONG size;
+ Q_LLONG sent;
+ QString desc;
+ bool rangeSupported;
+ Q_LLONG rangeOffset, rangeLength, length;
+ QString streamType;
+ bool needStream;
+ QString id, iq_id;
+ S5BConnection *c;
+ Jid proxy;
+ int state;
+ bool sender;
+};
+
+FileTransfer::FileTransfer(FileTransferManager *m, QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->m = m;
+ d->ft = 0;
+ d->c = 0;
+ reset();
+}
+
+FileTransfer::~FileTransfer()
+{
+ reset();
+ delete d;
+}
+
+void FileTransfer::reset()
+{
+ d->m->unlink(this);
+
+ delete d->ft;
+ d->ft = 0;
+
+ delete d->c;
+ d->c = 0;
+
+ d->state = Idle;
+ d->needStream = false;
+ d->sent = 0;
+ d->sender = false;
+}
+
+void FileTransfer::setProxy(const Jid &proxy)
+{
+ d->proxy = proxy;
+}
+
+void FileTransfer::sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc)
+{
+ d->state = Requesting;
+ d->peer = to;
+ d->fname = fname;
+ d->size = size;
+ d->desc = desc;
+ d->sender = true;
+ d->id = d->m->link(this);
+
+ d->ft = new JT_FT(d->m->client()->rootTask());
+ connect(d->ft, SIGNAL(finished()), SLOT(ft_finished()));
+ QStringList list;
+ list += "http://jabber.org/protocol/bytestreams";
+ d->ft->request(to, d->id, fname, size, desc, list);
+ d->ft->go(true);
+}
+
+int FileTransfer::dataSizeNeeded() const
+{
+ int pending = d->c->bytesToWrite();
+ if(pending >= SENDBUFSIZE)
+ return 0;
+ Q_LLONG left = d->length - (d->sent + pending);
+ int size = SENDBUFSIZE - pending;
+ if((Q_LLONG)size > left)
+ size = (int)left;
+ return size;
+}
+
+void FileTransfer::writeFileData(const QByteArray &a)
+{
+ int pending = d->c->bytesToWrite();
+ Q_LLONG left = d->length - (d->sent + pending);
+ if(left == 0)
+ return;
+
+ QByteArray block;
+ if((Q_LLONG)a.size() > left) {
+ block = a.copy();
+ block.resize((uint)left);
+ }
+ else
+ block = a;
+ d->c->write(block);
+}
+
+Jid FileTransfer::peer() const
+{
+ return d->peer;
+}
+
+QString FileTransfer::fileName() const
+{
+ return d->fname;
+}
+
+Q_LLONG FileTransfer::fileSize() const
+{
+ return d->size;
+}
+
+QString FileTransfer::description() const
+{
+ return d->desc;
+}
+
+bool FileTransfer::rangeSupported() const
+{
+ return d->rangeSupported;
+}
+
+Q_LLONG FileTransfer::offset() const
+{
+ return d->rangeOffset;
+}
+
+Q_LLONG FileTransfer::length() const
+{
+ return d->length;
+}
+
+void FileTransfer::accept(Q_LLONG offset, Q_LLONG length)
+{
+ d->state = Connecting;
+ d->rangeOffset = offset;
+ d->rangeLength = length;
+ if(length > 0)
+ d->length = length;
+ else
+ d->length = d->size;
+ d->streamType = "http://jabber.org/protocol/bytestreams";
+ d->m->con_accept(this);
+}
+
+void FileTransfer::close()
+{
+ if(d->state == Idle)
+ return;
+ if(d->state == WaitingForAccept)
+ d->m->con_reject(this);
+ else if(d->state == Active)
+ d->c->close();
+ reset();
+}
+
+S5BConnection *FileTransfer::s5bConnection() const
+{
+ return d->c;
+}
+
+void FileTransfer::ft_finished()
+{
+ JT_FT *ft = d->ft;
+ d->ft = 0;
+
+ if(ft->success()) {
+ d->state = Connecting;
+ d->rangeOffset = ft->rangeOffset();
+ d->length = ft->rangeLength();
+ if(d->length == 0)
+ d->length = d->size - d->rangeOffset;
+ d->streamType = ft->streamType();
+ d->c = d->m->client()->s5bManager()->createConnection();
+ connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+ connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+ connect(d->c, SIGNAL(bytesWritten(int)), SLOT(s5b_bytesWritten(int)));
+ connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+
+ if(d->proxy.isValid())
+ d->c->setProxy(d->proxy);
+ d->c->connectToJid(d->peer, d->id);
+ accepted();
+ }
+ else {
+ reset();
+ if(ft->statusCode() == 403)
+ error(ErrReject);
+ else
+ error(ErrNeg);
+ }
+}
+
+void FileTransfer::takeConnection(S5BConnection *c)
+{
+ d->c = c;
+ connect(d->c, SIGNAL(connected()), SLOT(s5b_connected()));
+ connect(d->c, SIGNAL(connectionClosed()), SLOT(s5b_connectionClosed()));
+ connect(d->c, SIGNAL(readyRead()), SLOT(s5b_readyRead()));
+ connect(d->c, SIGNAL(error(int)), SLOT(s5b_error(int)));
+ if(d->proxy.isValid())
+ d->c->setProxy(d->proxy);
+ accepted();
+ QTimer::singleShot(0, this, SLOT(doAccept()));
+}
+
+void FileTransfer::s5b_connected()
+{
+ d->state = Active;
+ connected();
+}
+
+void FileTransfer::s5b_connectionClosed()
+{
+ reset();
+ error(ErrStream);
+}
+
+void FileTransfer::s5b_readyRead()
+{
+ QByteArray a = d->c->read();
+ Q_LLONG need = d->length - d->sent;
+ if((Q_LLONG)a.size() > need)
+ a.resize((uint)need);
+ d->sent += a.size();
+ if(d->sent == d->length)
+ reset();
+ readyRead(a);
+}
+
+void FileTransfer::s5b_bytesWritten(int x)
+{
+ d->sent += x;
+ if(d->sent == d->length)
+ reset();
+ bytesWritten(x);
+}
+
+void FileTransfer::s5b_error(int x)
+{
+ reset();
+ if(x == S5BConnection::ErrRefused || x == S5BConnection::ErrConnect)
+ error(ErrConnect);
+ else if(x == S5BConnection::ErrProxy)
+ error(ErrProxy);
+ else
+ error(ErrStream);
+}
+
+void FileTransfer::man_waitForAccept(const FTRequest &req)
+{
+ d->state = WaitingForAccept;
+ d->peer = req.from;
+ d->id = req.id;
+ d->iq_id = req.iq_id;
+ d->fname = req.fname;
+ d->size = req.size;
+ d->desc = req.desc;
+ d->rangeSupported = req.rangeSupported;
+}
+
+void FileTransfer::doAccept()
+{
+ d->c->accept();
+}
+
+//----------------------------------------------------------------------------
+// FileTransferManager
+//----------------------------------------------------------------------------
+class FileTransferManager::Private
+{
+public:
+ Client *client;
+ QPtrList<FileTransfer> list, incoming;
+ JT_PushFT *pft;
+};
+
+FileTransferManager::FileTransferManager(Client *client)
+:QObject(client)
+{
+ d = new Private;
+ d->client = client;
+
+ d->pft = new JT_PushFT(d->client->rootTask());
+ connect(d->pft, SIGNAL(incoming(const FTRequest &)), SLOT(pft_incoming(const FTRequest &)));
+}
+
+FileTransferManager::~FileTransferManager()
+{
+ d->incoming.setAutoDelete(true);
+ d->incoming.clear();
+ delete d->pft;
+ delete d;
+}
+
+Client *FileTransferManager::client() const
+{
+ return d->client;
+}
+
+FileTransfer *FileTransferManager::createTransfer()
+{
+ FileTransfer *ft = new FileTransfer(this);
+ return ft;
+}
+
+FileTransfer *FileTransferManager::takeIncoming()
+{
+ if(d->incoming.isEmpty())
+ return 0;
+
+ FileTransfer *ft = d->incoming.getFirst();
+ d->incoming.removeRef(ft);
+
+ // move to active list
+ d->list.append(ft);
+ return ft;
+}
+
+void FileTransferManager::pft_incoming(const FTRequest &req)
+{
+ bool found = false;
+ for(QStringList::ConstIterator it = req.streamTypes.begin(); it != req.streamTypes.end(); ++it) {
+ if((*it) == "http://jabber.org/protocol/bytestreams") {
+ found = true;
+ break;
+ }
+ }
+ if(!found) {
+ d->pft->respondError(req.from, req.iq_id, 400, "No valid stream types");
+ return;
+ }
+ if(!d->client->s5bManager()->isAcceptableSID(req.from, req.id)) {
+ d->pft->respondError(req.from, req.iq_id, 400, "SID in use");
+ return;
+ }
+
+ FileTransfer *ft = new FileTransfer(this);
+ ft->man_waitForAccept(req);
+ d->incoming.append(ft);
+ incomingReady();
+}
+
+void FileTransferManager::s5b_incomingReady(S5BConnection *c)
+{
+ QPtrListIterator<FileTransfer> it(d->list);
+ FileTransfer *ft = 0;
+ for(FileTransfer *i; (i = it.current()); ++it) {
+ if(i->d->needStream && i->d->peer.compare(c->peer()) && i->d->id == c->sid()) {
+ ft = i;
+ break;
+ }
+ }
+ if(!ft) {
+ c->close();
+ delete c;
+ return;
+ }
+ ft->takeConnection(c);
+}
+
+QString FileTransferManager::link(FileTransfer *ft)
+{
+ d->list.append(ft);
+ return d->client->s5bManager()->genUniqueSID(ft->d->peer);
+}
+
+void FileTransferManager::con_accept(FileTransfer *ft)
+{
+ ft->d->needStream = true;
+ d->pft->respondSuccess(ft->d->peer, ft->d->iq_id, ft->d->rangeOffset, ft->d->rangeLength, ft->d->streamType);
+}
+
+void FileTransferManager::con_reject(FileTransfer *ft)
+{
+ d->pft->respondError(ft->d->peer, ft->d->iq_id, 403, "Declined");
+}
+
+void FileTransferManager::unlink(FileTransfer *ft)
+{
+ d->list.removeRef(ft);
+}
+
+//----------------------------------------------------------------------------
+// JT_FT
+//----------------------------------------------------------------------------
+class JT_FT::Private
+{
+public:
+ QDomElement iq;
+ Jid to;
+ Q_LLONG size, rangeOffset, rangeLength;
+ QString streamType;
+ QStringList streamTypes;
+};
+
+JT_FT::JT_FT(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+}
+
+JT_FT::~JT_FT()
+{
+ delete d;
+}
+
+void JT_FT::request(const Jid &to, const QString &_id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes)
+{
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement si = doc()->createElement("si");
+ si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+ si.setAttribute("id", _id);
+ si.setAttribute("profile", "http://jabber.org/protocol/si/profile/file-transfer");
+
+ QDomElement file = doc()->createElement("file");
+ file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+ file.setAttribute("name", fname);
+ file.setAttribute("size", QString::number(size));
+ if(!desc.isEmpty()) {
+ QDomElement de = doc()->createElement("desc");
+ de.appendChild(doc()->createTextNode(desc));
+ file.appendChild(de);
+ }
+ QDomElement range = doc()->createElement("range");
+ file.appendChild(range);
+ si.appendChild(file);
+
+ QDomElement feature = doc()->createElement("feature");
+ feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+ QDomElement x = doc()->createElement("x");
+ x.setAttribute("xmlns", "jabber:x:data");
+ x.setAttribute("type", "form");
+
+ QDomElement field = doc()->createElement("field");
+ field.setAttribute("var", "stream-method");
+ field.setAttribute("type", "list-single");
+ for(QStringList::ConstIterator it = streamTypes.begin(); it != streamTypes.end(); ++it) {
+ QDomElement option = doc()->createElement("option");
+ QDomElement value = doc()->createElement("value");
+ value.appendChild(doc()->createTextNode(*it));
+ option.appendChild(value);
+ field.appendChild(option);
+ }
+
+ x.appendChild(field);
+ feature.appendChild(x);
+
+ si.appendChild(feature);
+ iq.appendChild(si);
+
+ d->streamTypes = streamTypes;
+ d->size = size;
+ d->iq = iq;
+}
+
+Q_LLONG JT_FT::rangeOffset() const
+{
+ return d->rangeOffset;
+}
+
+Q_LLONG JT_FT::rangeLength() const
+{
+ return d->rangeLength;
+}
+
+QString JT_FT::streamType() const
+{
+ return d->streamType;
+}
+
+void JT_FT::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_FT::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement si = firstChildElement(x);
+ if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si") {
+ setError(900, "");
+ return true;
+ }
+
+ QString id = si.attribute("id");
+
+ Q_LLONG range_offset = 0;
+ Q_LLONG range_length = 0;
+
+ QDomElement file = si.elementsByTagName("file").item(0).toElement();
+ if(!file.isNull()) {
+ QDomElement range = file.elementsByTagName("range").item(0).toElement();
+ if(!range.isNull()) {
+ int x;
+ bool ok;
+ if(range.hasAttribute("offset")) {
+#if QT_VERSION >= 0x030200
+ x = range.attribute("offset").toLongLong(&ok);
+#else
+ x = range.attribute("offset").toLong(&ok);
+#endif
+ if(!ok || x < 0) {
+ setError(900, "");
+ return true;
+ }
+ range_offset = x;
+ }
+ if(range.hasAttribute("length")) {
+#if QT_VERSION >= 0x030200
+ x = range.attribute("length").toLongLong(&ok);
+#else
+ x = range.attribute("length").toLong(&ok);
+#endif
+ if(!ok || x < 0) {
+ setError(900, "");
+ return true;
+ }
+ range_length = x;
+ }
+ }
+ }
+
+ if(range_offset > d->size || (range_length > (d->size - range_offset))) {
+ setError(900, "");
+ return true;
+ }
+
+ QString streamtype;
+ QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+ if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+ QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+ if(!x.isNull() && x.attribute("type") == "submit") {
+ QDomElement field = x.elementsByTagName("field").item(0).toElement();
+ if(!field.isNull() && field.attribute("var") == "stream-method") {
+ QDomElement value = field.elementsByTagName("value").item(0).toElement();
+ if(!value.isNull())
+ streamtype = value.text();
+ }
+ }
+ }
+
+ // must be one of the offered streamtypes
+ bool found = false;
+ for(QStringList::ConstIterator it = d->streamTypes.begin(); it != d->streamTypes.end(); ++it) {
+ if((*it) == streamtype) {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return true;
+
+ d->rangeOffset = range_offset;
+ d->rangeLength = range_length;
+ d->streamType = streamtype;
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushFT
+//----------------------------------------------------------------------------
+JT_PushFT::JT_PushFT(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushFT::~JT_PushFT()
+{
+}
+
+void JT_PushFT::respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement si = doc()->createElement("si");
+ si.setAttribute("xmlns", "http://jabber.org/protocol/si");
+
+ if(rangeOffset != 0 || rangeLength != 0) {
+ QDomElement file = doc()->createElement("file");
+ file.setAttribute("xmlns", "http://jabber.org/protocol/si/profile/file-transfer");
+ QDomElement range = doc()->createElement("range");
+ if(rangeOffset > 0)
+ range.setAttribute("offset", QString::number(rangeOffset));
+ if(rangeLength > 0)
+ range.setAttribute("length", QString::number(rangeLength));
+ file.appendChild(range);
+ si.appendChild(file);
+ }
+
+ QDomElement feature = doc()->createElement("feature");
+ feature.setAttribute("xmlns", "http://jabber.org/protocol/feature-neg");
+ QDomElement x = doc()->createElement("x");
+ x.setAttribute("xmlns", "jabber:x:data");
+ x.setAttribute("type", "submit");
+
+ QDomElement field = doc()->createElement("field");
+ field.setAttribute("var", "stream-method");
+ QDomElement value = doc()->createElement("value");
+ value.appendChild(doc()->createTextNode(streamType));
+ field.appendChild(value);
+
+ x.appendChild(field);
+ feature.appendChild(x);
+
+ si.appendChild(feature);
+ iq.appendChild(si);
+ send(iq);
+}
+
+void JT_PushFT::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+bool JT_PushFT::take(const QDomElement &e)
+{
+ // must be an iq-set tag
+ if(e.tagName() != "iq")
+ return false;
+ if(e.attribute("type") != "set")
+ return false;
+
+ QDomElement si = firstChildElement(e);
+ if(si.attribute("xmlns") != "http://jabber.org/protocol/si" || si.tagName() != "si")
+ return false;
+ if(si.attribute("profile") != "http://jabber.org/protocol/si/profile/file-transfer")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QString id = si.attribute("id");
+
+ QDomElement file = si.elementsByTagName("file").item(0).toElement();
+ if(file.isNull())
+ return true;
+
+ QString fname = file.attribute("name");
+ if(fname.isEmpty()) {
+ respondError(from, id, 400, "Bad file name");
+ return true;
+ }
+
+ // ensure kosher
+ {
+ QFileInfo fi(fname);
+ fname = fi.fileName();
+ }
+
+ bool ok;
+#if QT_VERSION >= 0x030200
+ Q_LLONG size = file.attribute("size").toLongLong(&ok);
+#else
+ Q_LLONG size = file.attribute("size").toLong(&ok);
+#endif
+ if(!ok || size < 0) {
+ respondError(from, id, 400, "Bad file size");
+ return true;
+ }
+
+ QString desc;
+ QDomElement de = file.elementsByTagName("desc").item(0).toElement();
+ if(!de.isNull())
+ desc = de.text();
+
+ bool rangeSupported = false;
+ QDomElement range = file.elementsByTagName("range").item(0).toElement();
+ if(!range.isNull())
+ rangeSupported = true;
+
+ QStringList streamTypes;
+ QDomElement feature = si.elementsByTagName("feature").item(0).toElement();
+ if(!feature.isNull() && feature.attribute("xmlns") == "http://jabber.org/protocol/feature-neg") {
+ QDomElement x = feature.elementsByTagName("x").item(0).toElement();
+ if(!x.isNull() /*&& x.attribute("type") == "form"*/) {
+ QDomElement field = x.elementsByTagName("field").item(0).toElement();
+ if(!field.isNull() && field.attribute("var") == "stream-method" && field.attribute("type") == "list-single") {
+ QDomNodeList nl = field.elementsByTagName("option");
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomElement e = nl.item(n).toElement();
+ QDomElement value = e.elementsByTagName("value").item(0).toElement();
+ if(!value.isNull())
+ streamTypes += value.text();
+ }
+ }
+ }
+ }
+
+ FTRequest r;
+ r.from = from;
+ r.iq_id = e.attribute("id");
+ r.id = id;
+ r.fname = fname;
+ r.size = size;
+ r.desc = desc;
+ r.rangeSupported = rangeSupported;
+ r.streamTypes = streamTypes;
+
+ incoming(r);
+ return true;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h
new file mode 100644
index 00000000..9ad4d403
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/filetransfer.h
@@ -0,0 +1,170 @@
+/*
+ * filetransfer.h - File Transfer
+ * Copyright (C) 2004 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 XMPP_FILETRANSFER_H
+#define XMPP_FILETRANSFER_H
+
+#include"im.h"
+
+#if QT_VERSION < 0x030200
+typedef long int Q_LLONG;
+#endif
+
+namespace XMPP
+{
+ class S5BConnection;
+ struct FTRequest;
+
+ class FileTransfer : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { ErrReject, ErrNeg, ErrConnect, ErrProxy, ErrStream };
+ enum { Idle, Requesting, Connecting, WaitingForAccept, Active };
+ ~FileTransfer();
+
+ void setProxy(const Jid &proxy);
+
+ // send
+ void sendFile(const Jid &to, const QString &fname, Q_LLONG size, const QString &desc);
+ Q_LLONG offset() const;
+ Q_LLONG length() const;
+ int dataSizeNeeded() const;
+ void writeFileData(const QByteArray &a);
+
+ // receive
+ Jid peer() const;
+ QString fileName() const;
+ Q_LLONG fileSize() const;
+ QString description() const;
+ bool rangeSupported() const;
+ void accept(Q_LLONG offset=0, Q_LLONG length=0);
+
+ // both
+ void close(); // reject, or stop sending/receiving
+ S5BConnection *s5bConnection() const; // active link
+
+ signals:
+ void accepted(); // indicates S5BConnection has started
+ void connected();
+ void readyRead(const QByteArray &a);
+ void bytesWritten(int);
+ void error(int);
+
+ private slots:
+ void ft_finished();
+ void s5b_connected();
+ void s5b_connectionClosed();
+ void s5b_readyRead();
+ void s5b_bytesWritten(int);
+ void s5b_error(int);
+ void doAccept();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset();
+
+ friend class FileTransferManager;
+ FileTransfer(FileTransferManager *, QObject *parent=0);
+ void man_waitForAccept(const FTRequest &req);
+ void takeConnection(S5BConnection *c);
+ };
+
+ class FileTransferManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ FileTransferManager(Client *);
+ ~FileTransferManager();
+
+ Client *client() const;
+ FileTransfer *createTransfer();
+ FileTransfer *takeIncoming();
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void pft_incoming(const FTRequest &req);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class Client;
+ void s5b_incomingReady(S5BConnection *);
+
+ friend class FileTransfer;
+ QString link(FileTransfer *);
+ void con_accept(FileTransfer *);
+ void con_reject(FileTransfer *);
+ void unlink(FileTransfer *);
+ };
+
+ class JT_FT : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_FT(Task *parent);
+ ~JT_FT();
+
+ void request(const Jid &to, const QString &id, const QString &fname, Q_LLONG size, const QString &desc, const QStringList &streamTypes);
+ Q_LLONG rangeOffset() const;
+ Q_LLONG rangeLength() const;
+ QString streamType() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ struct FTRequest
+ {
+ Jid from;
+ QString iq_id, id;
+ QString fname;
+ Q_LLONG size;
+ QString desc;
+ bool rangeSupported;
+ QStringList streamTypes;
+ };
+ class JT_PushFT : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushFT(Task *parent);
+ ~JT_PushFT();
+
+ void respondSuccess(const Jid &to, const QString &id, Q_LLONG rangeOffset, Q_LLONG rangeLength, const QString &streamType);
+ void respondError(const Jid &to, const QString &id, int code, const QString &str);
+
+ bool take(const QDomElement &);
+
+ signals:
+ void incoming(const FTRequest &req);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp b/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp
new file mode 100644
index 00000000..b4b9be44
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/s5b.cpp
@@ -0,0 +1,2538 @@
+/*
+ * s5b.cpp - direct connection protocol via tcp
+ * 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 <config.h>
+
+#include"s5b.h"
+
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include<stdlib.h>
+#include<qca.h>
+#include"xmpp_xmlcommon.h"
+#include"hash.h"
+#include"socks.h"
+#include"safedelete.h"
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# include <netinet/in.h>
+#endif
+
+#define MAXSTREAMHOSTS 5
+
+//#define S5B_DEBUG
+
+namespace XMPP {
+
+static QString makeKey(const QString &sid, const Jid &initiator, const Jid &target)
+{
+ QString str = sid + initiator.full() + target.full();
+ return QCA::SHA1::hashToString(str.utf8());
+}
+
+static bool haveHost(const StreamHostList &list, const Jid &j)
+{
+ for(StreamHostList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ if((*it).jid().compare(j))
+ return true;
+ }
+ return false;
+}
+
+class S5BManager::Item : public QObject
+{
+ Q_OBJECT
+public:
+ enum { Idle, Initiator, Target, Active };
+ enum { ErrRefused, ErrConnect, ErrWrongHost, ErrProxy };
+ enum { Unknown, Fast, NotFast };
+ S5BManager *m;
+ int state;
+ QString sid, key, out_key, out_id, in_id;
+ Jid self, peer;
+ StreamHostList in_hosts;
+ JT_S5B *task, *proxy_task;
+ SocksClient *client, *client_out;
+ SocksUDP *client_udp, *client_out_udp;
+ S5BConnector *conn, *proxy_conn;
+ bool wantFast;
+ StreamHost proxy;
+ int targetMode; // initiator sets this once it figures it out
+ bool fast; // target sets this
+ bool activated;
+ bool lateProxy;
+ bool connSuccess;
+ bool localFailed, remoteFailed;
+ bool allowIncoming;
+ bool udp;
+ int statusCode;
+ Jid activatedStream;
+
+ Item(S5BManager *manager);
+ ~Item();
+
+ void reset();
+ void startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool udp);
+ void startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool fast, bool udp);
+ void handleFast(const StreamHostList &hosts, const QString &iq_id);
+
+ void doOutgoing();
+ void doIncoming();
+ void setIncomingClient(SocksClient *sc);
+ void incomingActivate(const Jid &streamHost);
+
+signals:
+ void accepted();
+ void tryingHosts(const StreamHostList &list);
+ void proxyConnect();
+ void waitingForActivation();
+ void connected();
+ void error(int);
+
+private slots:
+ void jt_finished();
+ void conn_result(bool b);
+ void proxy_result(bool b);
+ void proxy_finished();
+ void sc_readyRead();
+ void sc_bytesWritten(int);
+ void sc_error(int);
+
+private:
+ void doConnectError();
+ void tryActivation();
+ void checkForActivation();
+ void checkFailure();
+ void finished();
+};
+
+//----------------------------------------------------------------------------
+// S5BDatagram
+//----------------------------------------------------------------------------
+S5BDatagram::S5BDatagram()
+{
+ _source = 0;
+ _dest = 0;
+}
+
+S5BDatagram::S5BDatagram(int source, int dest, const QByteArray &data)
+{
+ _source = source;
+ _dest = dest;
+ _buf = data;
+}
+
+int S5BDatagram::sourcePort() const
+{
+ return _source;
+}
+
+int S5BDatagram::destPort() const
+{
+ return _dest;
+}
+
+QByteArray S5BDatagram::data() const
+{
+ return _buf;
+}
+
+//----------------------------------------------------------------------------
+// S5BConnection
+//----------------------------------------------------------------------------
+class S5BConnection::Private
+{
+public:
+ S5BManager *m;
+ SocksClient *sc;
+ SocksUDP *su;
+ int state;
+ Jid peer;
+ QString sid;
+ bool remote;
+ bool switched;
+ bool notifyRead, notifyClose;
+ int id;
+ S5BRequest req;
+ Jid proxy;
+ Mode mode;
+ QPtrList<S5BDatagram> dglist;
+};
+
+static int id_conn = 0;
+static int num_conn = 0;
+
+S5BConnection::S5BConnection(S5BManager *m, QObject *parent)
+:ByteStream(parent)
+{
+ d = new Private;
+ d->m = m;
+ d->sc = 0;
+ d->su = 0;
+
+ ++num_conn;
+ d->id = id_conn++;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: constructing, count=%d, %p\n", d->id, num_conn, this);
+#endif
+
+ reset();
+}
+
+S5BConnection::~S5BConnection()
+{
+ reset(true);
+
+ --num_conn;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+#endif
+
+ delete d;
+}
+
+void S5BConnection::reset(bool clear)
+{
+ d->m->con_unlink(this);
+ if(clear && d->sc) {
+ delete d->sc;
+ d->sc = 0;
+ }
+ delete d->su;
+ d->su = 0;
+ if(clear) {
+ d->dglist.setAutoDelete(true);
+ d->dglist.clear();
+ d->dglist.setAutoDelete(false);
+ }
+ d->state = Idle;
+ d->peer = Jid();
+ d->sid = QString();
+ d->remote = false;
+ d->switched = false;
+ d->notifyRead = false;
+ d->notifyClose = false;
+}
+
+Jid S5BConnection::proxy() const
+{
+ return d->proxy;
+}
+
+void S5BConnection::setProxy(const Jid &proxy)
+{
+ d->proxy = proxy;
+}
+
+void S5BConnection::connectToJid(const Jid &peer, const QString &sid, Mode m)
+{
+ reset(true);
+ if(!d->m->isAcceptableSID(peer, sid))
+ return;
+
+ d->peer = peer;
+ d->sid = sid;
+ d->state = Requesting;
+ d->mode = m;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: connecting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ d->m->con_connect(this);
+}
+
+void S5BConnection::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ d->state = Connecting;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ d->m->con_accept(this);
+}
+
+void S5BConnection::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->state == WaitingForAccept)
+ d->m->con_reject(this);
+ else if(d->state == Active)
+ d->sc->close();
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: closing %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+ reset();
+}
+
+Jid S5BConnection::peer() const
+{
+ return d->peer;
+}
+
+QString S5BConnection::sid() const
+{
+ return d->sid;
+}
+
+bool S5BConnection::isRemote() const
+{
+ return d->remote;
+}
+
+S5BConnection::Mode S5BConnection::mode() const
+{
+ return d->mode;
+}
+
+int S5BConnection::state() const
+{
+ return d->state;
+}
+
+bool S5BConnection::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void S5BConnection::write(const QByteArray &buf)
+{
+ if(d->state == Active && d->mode == Stream)
+ d->sc->write(buf);
+}
+
+QByteArray S5BConnection::read(int bytes)
+{
+ if(d->sc)
+ return d->sc->read(bytes);
+ else
+ return QByteArray();
+}
+
+int S5BConnection::bytesAvailable() const
+{
+ if(d->sc)
+ return d->sc->bytesAvailable();
+ else
+ return 0;
+}
+
+int S5BConnection::bytesToWrite() const
+{
+ if(d->state == Active)
+ return d->sc->bytesToWrite();
+ else
+ return 0;
+}
+
+void S5BConnection::writeDatagram(const S5BDatagram &i)
+{
+ QByteArray buf(i.data().size() + 4);
+ ushort ssp = htons(i.sourcePort());
+ ushort sdp = htons(i.destPort());
+ QByteArray data = i.data();
+ memcpy(buf.data(), &ssp, 2);
+ memcpy(buf.data() + 2, &sdp, 2);
+ memcpy(buf.data() + 4, data.data(), data.size());
+ sendUDP(buf);
+}
+
+S5BDatagram S5BConnection::readDatagram()
+{
+ if(d->dglist.isEmpty())
+ return S5BDatagram();
+ S5BDatagram *i = d->dglist.getFirst();
+ d->dglist.removeRef(i);
+ S5BDatagram val = *i;
+ delete i;
+ return val;
+}
+
+int S5BConnection::datagramsAvailable() const
+{
+ return d->dglist.count();
+}
+
+void S5BConnection::man_waitForAccept(const S5BRequest &r)
+{
+ d->state = WaitingForAccept;
+ d->remote = true;
+ d->req = r;
+ d->peer = r.from;
+ d->sid = r.sid;
+ d->mode = r.udp ? Datagram : Stream;
+}
+
+void S5BConnection::man_clientReady(SocksClient *sc, SocksUDP *sc_udp)
+{
+ d->sc = sc;
+ connect(d->sc, SIGNAL(connectionClosed()), SLOT(sc_connectionClosed()));
+ connect(d->sc, SIGNAL(delayedCloseFinished()), SLOT(sc_delayedCloseFinished()));
+ connect(d->sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(d->sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(d->sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ if(sc_udp) {
+ d->su = sc_udp;
+ connect(d->su, SIGNAL(packetReady(const QByteArray &)), SLOT(su_packetReady(const QByteArray &)));
+ }
+
+ d->state = Active;
+#ifdef S5B_DEBUG
+ printf("S5BConnection[%d]: %s [%s] <<< success >>>\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+#endif
+
+ // bytes already in the stream?
+ if(d->sc->bytesAvailable()) {
+#ifdef S5B_DEBUG
+ printf("Stream has %d bytes in it.\n", d->sc->bytesAvailable());
+#endif
+ d->notifyRead = true;
+ }
+ // closed before it got here?
+ if(!d->sc->isOpen()) {
+#ifdef S5B_DEBUG
+ printf("Stream was closed before S5B request finished?\n");
+#endif
+ d->notifyClose = true;
+ }
+ if(d->notifyRead || d->notifyClose)
+ QTimer::singleShot(0, this, SLOT(doPending()));
+ connected();
+}
+
+void S5BConnection::doPending()
+{
+ if(d->notifyRead) {
+ if(d->notifyClose)
+ QTimer::singleShot(0, this, SLOT(doPending()));
+ sc_readyRead();
+ }
+ else if(d->notifyClose)
+ sc_connectionClosed();
+}
+
+void S5BConnection::man_udpReady(const QByteArray &buf)
+{
+ handleUDP(buf);
+}
+
+void S5BConnection::man_failed(int x)
+{
+ reset(true);
+ if(x == S5BManager::Item::ErrRefused)
+ error(ErrRefused);
+ if(x == S5BManager::Item::ErrConnect)
+ error(ErrConnect);
+ if(x == S5BManager::Item::ErrWrongHost)
+ error(ErrConnect);
+ if(x == S5BManager::Item::ErrProxy)
+ error(ErrProxy);
+}
+
+void S5BConnection::sc_connectionClosed()
+{
+ // if we have a pending read notification, postpone close
+ if(d->notifyRead) {
+#ifdef S5B_DEBUG
+ printf("closed while pending read\n");
+#endif
+ d->notifyClose = true;
+ return;
+ }
+ d->notifyClose = false;
+ reset();
+ connectionClosed();
+}
+
+void S5BConnection::sc_delayedCloseFinished()
+{
+ // echo
+ delayedCloseFinished();
+}
+
+void S5BConnection::sc_readyRead()
+{
+ if(d->mode == Datagram) {
+ // throw the data away
+ d->sc->read();
+ return;
+ }
+
+ d->notifyRead = false;
+ // echo
+ readyRead();
+}
+
+void S5BConnection::sc_bytesWritten(int x)
+{
+ // echo
+ bytesWritten(x);
+}
+
+void S5BConnection::sc_error(int)
+{
+ reset();
+ error(ErrSocket);
+}
+
+void S5BConnection::su_packetReady(const QByteArray &buf)
+{
+ handleUDP(buf);
+}
+
+void S5BConnection::handleUDP(const QByteArray &buf)
+{
+ // must be at least 4 bytes, to accomodate virtual ports
+ if(buf.size() < 4)
+ return; // drop
+
+ ushort ssp, sdp;
+ memcpy(&ssp, buf.data(), 2);
+ memcpy(&sdp, buf.data() + 2, 2);
+ int source = ntohs(ssp);
+ int dest = ntohs(sdp);
+ QByteArray data(buf.size() - 4);
+ memcpy(data.data(), buf.data() + 4, data.size());
+ d->dglist.append(new S5BDatagram(source, dest, data));
+
+ datagramReady();
+}
+
+void S5BConnection::sendUDP(const QByteArray &buf)
+{
+ if(d->su)
+ d->su->write(buf);
+ else
+ d->m->con_sendUDP(this, buf);
+}
+
+//----------------------------------------------------------------------------
+// S5BManager
+//----------------------------------------------------------------------------
+class S5BManager::Entry
+{
+public:
+ Entry()
+ {
+ i = 0;
+ query = 0;
+ udp_init = false;
+ }
+
+ ~Entry()
+ {
+ delete query;
+ }
+
+ S5BConnection *c;
+ Item *i;
+ QString sid;
+ JT_S5B *query;
+ StreamHost proxyInfo;
+ QGuardedPtr<S5BServer> relatedServer;
+
+ bool udp_init;
+ QHostAddress udp_addr;
+ int udp_port;
+};
+
+class S5BManager::Private
+{
+public:
+ Client *client;
+ S5BServer *serv;
+ QPtrList<Entry> activeList;
+ S5BConnectionList incomingConns;
+ JT_PushS5B *ps;
+};
+
+S5BManager::S5BManager(Client *parent)
+:QObject(parent)
+{
+ // S5B needs SHA1
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ d = new Private;
+ d->client = parent;
+ d->serv = 0;
+ d->activeList.setAutoDelete(true);
+
+ d->ps = new JT_PushS5B(d->client->rootTask());
+ connect(d->ps, SIGNAL(incoming(const S5BRequest &)), SLOT(ps_incoming(const S5BRequest &)));
+ connect(d->ps, SIGNAL(incomingUDPSuccess(const Jid &, const QString &)), SLOT(ps_incomingUDPSuccess(const Jid &, const QString &)));
+ connect(d->ps, SIGNAL(incomingActivate(const Jid &, const QString &, const Jid &)), SLOT(ps_incomingActivate(const Jid &, const QString &, const Jid &)));
+}
+
+S5BManager::~S5BManager()
+{
+ setServer(0);
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d->ps;
+ delete d;
+}
+
+Client *S5BManager::client() const
+{
+ return d->client;
+}
+
+S5BServer *S5BManager::server() const
+{
+ return d->serv;
+}
+
+void S5BManager::setServer(S5BServer *serv)
+{
+ if(d->serv) {
+ d->serv->unlink(this);
+ d->serv = 0;
+ }
+
+ if(serv) {
+ d->serv = serv;
+ d->serv->link(this);
+ }
+}
+
+S5BConnection *S5BManager::createConnection()
+{
+ S5BConnection *c = new S5BConnection(this);
+ return c;
+}
+
+S5BConnection *S5BManager::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ S5BConnection *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+
+ // move to activeList
+ Entry *e = new Entry;
+ e->c = c;
+ e->sid = c->d->sid;
+ d->activeList.append(e);
+
+ return c;
+}
+
+void S5BManager::ps_incoming(const S5BRequest &req)
+{
+#ifdef S5B_DEBUG
+ printf("S5BManager: incoming from %s\n", req.from.full().latin1());
+#endif
+
+ bool ok = false;
+ // ensure we don't already have an incoming connection from this peer+sid
+ S5BConnection *c = findIncoming(req.from, req.sid);
+ if(!c) {
+ // do we have an active entry with this sid already?
+ Entry *e = findEntryBySID(req.from, req.sid);
+ if(e) {
+ if(e->i) {
+ // loopback
+ if(req.from.compare(d->client->jid()) && (req.id == e->i->out_id)) {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: loopback\n");
+#endif
+ ok = true;
+ }
+ // allowed by 'fast mode'
+ else if(e->i->state == Item::Initiator && e->i->targetMode == Item::Unknown) {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: fast-mode\n");
+#endif
+ e->i->handleFast(req.hosts, req.id);
+ return;
+ }
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("ALLOWED: we don't have it\n");
+#endif
+ ok = true;
+ }
+ }
+ if(!ok) {
+ d->ps->respondError(req.from, req.id, 406, "SID in use");
+ return;
+ }
+
+ // create an incoming connection
+ c = new S5BConnection(this);
+ c->man_waitForAccept(req);
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void S5BManager::ps_incomingUDPSuccess(const Jid &from, const QString &key)
+{
+ Entry *e = findEntryByHash(key);
+ if(e && e->i) {
+ if(e->i->conn)
+ e->i->conn->man_udpSuccess(from);
+ else if(e->i->proxy_conn)
+ e->i->proxy_conn->man_udpSuccess(from);
+ }
+}
+
+void S5BManager::ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost)
+{
+ Entry *e = findEntryBySID(from, sid);
+ if(e && e->i)
+ e->i->incomingActivate(streamHost);
+}
+
+void S5BManager::doSuccess(const Jid &peer, const QString &id, const Jid &streamHost)
+{
+ d->ps->respondSuccess(peer, id, streamHost);
+}
+
+void S5BManager::doError(const Jid &peer, const QString &id, int code, const QString &str)
+{
+ d->ps->respondError(peer, id, code, str);
+}
+
+void S5BManager::doActivate(const Jid &peer, const QString &sid, const Jid &streamHost)
+{
+ d->ps->sendActivate(peer, sid, streamHost);
+}
+
+QString S5BManager::genUniqueSID(const Jid &peer) const
+{
+ // get unused key
+ QString sid;
+ do {
+ sid = "s5b_";
+ for(int i = 0; i < 4; ++i) {
+ int word = rand() & 0xffff;
+ for(int n = 0; n < 4; ++n) {
+ QString s;
+ s.sprintf("%x", (word >> (n * 4)) & 0xf);
+ sid.append(s);
+ }
+ }
+ } while(!isAcceptableSID(peer, sid));
+ return sid;
+}
+
+bool S5BManager::isAcceptableSID(const Jid &peer, const QString &sid) const
+{
+ QString key = makeKey(sid, d->client->jid(), peer);
+ QString key_out = makeKey(sid, peer, d->client->jid());
+
+ // if we have a server, then check through it
+ if(d->serv) {
+ if(findServerEntryByHash(key) || findServerEntryByHash(key_out))
+ return false;
+ }
+ else {
+ if(findEntryByHash(key) || findEntryByHash(key_out))
+ return false;
+ }
+ return true;
+}
+
+S5BConnection *S5BManager::findIncoming(const Jid &from, const QString &sid) const
+{
+ QPtrListIterator<S5BConnection> it(d->incomingConns);
+ for(S5BConnection *c; (c = it.current()); ++it) {
+ if(c->d->peer.compare(from) && c->d->sid == sid)
+ return c;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(S5BConnection *c) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->c == c)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntry(Item *i) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i == i)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryByHash(const QString &key) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i && e->i->key == key)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findEntryBySID(const Jid &peer, const QString &sid) const
+{
+ QPtrListIterator<Entry> it(d->activeList);
+ for(Entry *e; (e = it.current()); ++it) {
+ if(e->i && e->i->peer.compare(peer) && e->sid == sid)
+ return e;
+ }
+ return 0;
+}
+
+S5BManager::Entry *S5BManager::findServerEntryByHash(const QString &key) const
+{
+ const QPtrList<S5BManager> &manList = d->serv->managerList();
+ QPtrListIterator<S5BManager> it(manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ Entry *e = m->findEntryByHash(key);
+ if(e)
+ return e;
+ }
+ return 0;
+}
+
+bool S5BManager::srv_ownsHash(const QString &key) const
+{
+ if(findEntryByHash(key))
+ return true;
+ return false;
+}
+
+void S5BManager::srv_incomingReady(SocksClient *sc, const QString &key)
+{
+ Entry *e = findEntryByHash(key);
+ if(!e->i->allowIncoming) {
+ sc->requestDeny();
+ SafeDelete::deleteSingle(sc);
+ return;
+ }
+ if(e->c->d->mode == S5BConnection::Datagram)
+ sc->grantUDPAssociate("", 0);
+ else
+ sc->grantConnect();
+ e->relatedServer = (S5BServer *)sender();
+ e->i->setIncomingClient(sc);
+}
+
+void S5BManager::srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data)
+{
+ Entry *e = findEntryByHash(key);
+ if(!e->c->d->mode != S5BConnection::Datagram)
+ return; // this key isn't in udp mode? drop!
+
+ if(init) {
+ if(e->udp_init)
+ return; // only init once
+
+ // lock on to this sender
+ e->udp_addr = addr;
+ e->udp_port = port;
+ e->udp_init = true;
+
+ // reply that initialization was successful
+ d->ps->sendUDPSuccess(e->c->d->peer, key);
+ return;
+ }
+
+ // not initialized yet? something went wrong
+ if(!e->udp_init)
+ return;
+
+ // must come from same source as when initialized
+ if(addr.toString() != e->udp_addr.toString() || port != e->udp_port)
+ return;
+
+ e->c->man_udpReady(data);
+}
+
+void S5BManager::srv_unlink()
+{
+ d->serv = 0;
+}
+
+void S5BManager::con_connect(S5BConnection *c)
+{
+ if(findEntry(c))
+ return;
+ Entry *e = new Entry;
+ e->c = c;
+ e->sid = c->d->sid;
+ d->activeList.append(e);
+
+ if(c->d->proxy.isValid()) {
+ queryProxy(e);
+ return;
+ }
+ entryContinue(e);
+}
+
+void S5BManager::con_accept(S5BConnection *c)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+
+ if(e->c->d->req.fast) {
+ if(targetShouldOfferProxy(e)) {
+ queryProxy(e);
+ return;
+ }
+ }
+ entryContinue(e);
+}
+
+void S5BManager::con_reject(S5BConnection *c)
+{
+ d->ps->respondError(c->d->peer, c->d->req.id, 406, "Not acceptable");
+}
+
+void S5BManager::con_unlink(S5BConnection *c)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+
+ // active incoming request? cancel it
+ if(e->i && e->i->conn)
+ d->ps->respondError(e->i->peer, e->i->out_id, 406, "Not acceptable");
+ delete e->i;
+ d->activeList.removeRef(e);
+}
+
+void S5BManager::con_sendUDP(S5BConnection *c, const QByteArray &buf)
+{
+ Entry *e = findEntry(c);
+ if(!e)
+ return;
+ if(!e->udp_init)
+ return;
+
+ if(e->relatedServer)
+ e->relatedServer->writeUDP(e->udp_addr, e->udp_port, buf);
+}
+
+void S5BManager::item_accepted()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->accepted(); // signal
+}
+
+void S5BManager::item_tryingHosts(const StreamHostList &list)
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->tryingHosts(list); // signal
+}
+
+void S5BManager::item_proxyConnect()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->proxyConnect(); // signal
+}
+
+void S5BManager::item_waitingForActivation()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->waitingForActivation(); // signal
+}
+
+void S5BManager::item_connected()
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ // grab the client
+ SocksClient *client = i->client;
+ i->client = 0;
+ SocksUDP *client_udp = i->client_udp;
+ i->client_udp = 0;
+
+ // give it to the connection
+ e->c->man_clientReady(client, client_udp);
+}
+
+void S5BManager::item_error(int x)
+{
+ Item *i = (Item *)sender();
+ Entry *e = findEntry(i);
+
+ e->c->man_failed(x);
+}
+
+void S5BManager::entryContinue(Entry *e)
+{
+ e->i = new Item(this);
+ e->i->proxy = e->proxyInfo;
+
+ connect(e->i, SIGNAL(accepted()), SLOT(item_accepted()));
+ connect(e->i, SIGNAL(tryingHosts(const StreamHostList &)), SLOT(item_tryingHosts(const StreamHostList &)));
+ connect(e->i, SIGNAL(proxyConnect()), SLOT(item_proxyConnect()));
+ connect(e->i, SIGNAL(waitingForActivation()), SLOT(item_waitingForActivation()));
+ connect(e->i, SIGNAL(connected()), SLOT(item_connected()));
+ connect(e->i, SIGNAL(error(int)), SLOT(item_error(int)));
+
+ if(e->c->isRemote()) {
+ const S5BRequest &req = e->c->d->req;
+ e->i->startTarget(e->sid, d->client->jid(), e->c->d->peer, req.hosts, req.id, req.fast, req.udp);
+ }
+ else {
+ e->i->startInitiator(e->sid, d->client->jid(), e->c->d->peer, true, e->c->d->mode == S5BConnection::Datagram ? true: false);
+ e->c->requesting(); // signal
+ }
+}
+
+void S5BManager::queryProxy(Entry *e)
+{
+ QGuardedPtr<QObject> self = this;
+ e->c->proxyQuery(); // signal
+ if(!self)
+ return;
+
+#ifdef S5B_DEBUG
+ printf("querying proxy: [%s]\n", e->c->d->proxy.full().latin1());
+#endif
+ e->query = new JT_S5B(d->client->rootTask());
+ connect(e->query, SIGNAL(finished()), SLOT(query_finished()));
+ e->query->requestProxyInfo(e->c->d->proxy);
+ e->query->go(true);
+}
+
+void S5BManager::query_finished()
+{
+ JT_S5B *query = (JT_S5B *)sender();
+ Entry *e;
+ bool found = false;
+ QPtrListIterator<Entry> it(d->activeList);
+ for(; (e = it.current()); ++it) {
+ if(e->query == query) {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ return;
+ e->query = 0;
+
+#ifdef S5B_DEBUG
+ printf("query finished: ");
+#endif
+ if(query->success()) {
+ e->proxyInfo = query->proxyInfo();
+#ifdef S5B_DEBUG
+ printf("host/ip=[%s] port=[%d]\n", e->proxyInfo.host().latin1(), e->proxyInfo.port());
+#endif
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("fail\n");
+#endif
+ }
+
+ QGuardedPtr<QObject> self = this;
+ e->c->proxyResult(query->success()); // signal
+ if(!self)
+ return;
+
+ entryContinue(e);
+}
+
+bool S5BManager::targetShouldOfferProxy(Entry *e)
+{
+ if(!e->c->d->proxy.isValid())
+ return false;
+
+ // if target, don't offer any proxy if the initiator already did
+ const StreamHostList &hosts = e->c->d->req.hosts;
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ if((*it).isProxy())
+ return false;
+ }
+
+ // ensure we don't offer the same proxy as the initiator
+ if(haveHost(hosts, e->c->d->proxy))
+ return false;
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// S5BManager::Item
+//----------------------------------------------------------------------------
+S5BManager::Item::Item(S5BManager *manager) : QObject(0)
+{
+ m = manager;
+ task = 0;
+ proxy_task = 0;
+ conn = 0;
+ proxy_conn = 0;
+ client_udp = 0;
+ client = 0;
+ client_out_udp = 0;
+ client_out = 0;
+ reset();
+}
+
+S5BManager::Item::~Item()
+{
+ reset();
+}
+
+void S5BManager::Item::reset()
+{
+ delete task;
+ task = 0;
+
+ delete proxy_task;
+ proxy_task = 0;
+
+ delete conn;
+ conn = 0;
+
+ delete proxy_conn;
+ proxy_conn = 0;
+
+ delete client_udp;
+ client_udp = 0;
+
+ delete client;
+ client = 0;
+
+ delete client_out_udp;
+ client_out_udp = 0;
+
+ delete client_out;
+ client_out = 0;
+
+ state = Idle;
+ wantFast = false;
+ targetMode = Unknown;
+ fast = false;
+ activated = false;
+ lateProxy = false;
+ connSuccess = false;
+ localFailed = false;
+ remoteFailed = false;
+ allowIncoming = false;
+ udp = false;
+}
+
+void S5BManager::Item::startInitiator(const QString &_sid, const Jid &_self, const Jid &_peer, bool fast, bool _udp)
+{
+ sid = _sid;
+ self = _self;
+ peer = _peer;
+ key = makeKey(sid, self, peer);
+ out_key = makeKey(sid, peer, self);
+ wantFast = fast;
+ udp = _udp;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item initiating request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+ state = Initiator;
+ doOutgoing();
+}
+
+void S5BManager::Item::startTarget(const QString &_sid, const Jid &_self, const Jid &_peer, const StreamHostList &hosts, const QString &iq_id, bool _fast, bool _udp)
+{
+ sid = _sid;
+ peer = _peer;
+ self = _self;
+ in_hosts = hosts;
+ in_id = iq_id;
+ fast = _fast;
+ key = makeKey(sid, self, peer);
+ out_key = makeKey(sid, peer, self);
+ udp = _udp;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item incoming request %s [%s]\n", peer.full().latin1(), sid.latin1());
+#endif
+ state = Target;
+ if(fast)
+ doOutgoing();
+ doIncoming();
+}
+
+void S5BManager::Item::handleFast(const StreamHostList &hosts, const QString &iq_id)
+{
+ targetMode = Fast;
+
+ QGuardedPtr<QObject> self = this;
+ accepted();
+ if(!self)
+ return;
+
+ // if we already have a stream, then bounce this request
+ if(client) {
+ m->doError(peer, iq_id, 406, "Not acceptable");
+ }
+ else {
+ in_hosts = hosts;
+ in_id = iq_id;
+ doIncoming();
+ }
+}
+
+void S5BManager::Item::doOutgoing()
+{
+ StreamHostList hosts;
+ S5BServer *serv = m->server();
+ if(serv && serv->isActive() && !haveHost(in_hosts, m->client()->jid())) {
+ QStringList hostList = serv->hostList();
+ for(QStringList::ConstIterator it = hostList.begin(); it != hostList.end(); ++it) {
+ StreamHost h;
+ h.setJid(m->client()->jid());
+ h.setHost(*it);
+ h.setPort(serv->port());
+ hosts += h;
+ }
+ }
+
+ // if the proxy is valid, then it's ok to add (the manager already ensured that it doesn't conflict)
+ if(proxy.jid().isValid())
+ hosts += proxy;
+
+ // if we're the target and we have no streamhosts of our own, then don't even bother with fast-mode
+ if(state == Target && hosts.isEmpty()) {
+ fast = false;
+ return;
+ }
+
+ allowIncoming = true;
+
+ task = new JT_S5B(m->client()->rootTask());
+ connect(task, SIGNAL(finished()), SLOT(jt_finished()));
+ task->request(peer, sid, hosts, state == Initiator ? wantFast : false, udp);
+ out_id = task->id();
+ task->go(true);
+}
+
+void S5BManager::Item::doIncoming()
+{
+ if(in_hosts.isEmpty()) {
+ doConnectError();
+ return;
+ }
+
+ StreamHostList list;
+ if(lateProxy) {
+ // take just the proxy streamhosts
+ for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+ if((*it).isProxy())
+ list += *it;
+ }
+ lateProxy = false;
+ }
+ else {
+ // only try doing the late proxy trick if using fast mode AND we did not offer a proxy
+ if((state == Initiator || (state == Target && fast)) && !proxy.jid().isValid()) {
+ // take just the non-proxy streamhosts
+ bool hasProxies = false;
+ for(StreamHostList::ConstIterator it = in_hosts.begin(); it != in_hosts.end(); ++it) {
+ if((*it).isProxy())
+ hasProxies = true;
+ else
+ list += *it;
+ }
+ if(hasProxies) {
+ lateProxy = true;
+
+ // no regular streamhosts? wait for remote error
+ if(list.isEmpty())
+ return;
+ }
+ }
+ else
+ list = in_hosts;
+ }
+
+ conn = new S5BConnector;
+ connect(conn, SIGNAL(result(bool)), SLOT(conn_result(bool)));
+
+ QGuardedPtr<QObject> self = this;
+ tryingHosts(list);
+ if(!self)
+ return;
+
+ conn->start(m->client()->jid(), list, out_key, udp, lateProxy ? 10 : 30);
+}
+
+void S5BManager::Item::setIncomingClient(SocksClient *sc)
+{
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item: %s [%s] successful incoming connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ client = sc;
+ allowIncoming = false;
+}
+
+void S5BManager::Item::incomingActivate(const Jid &streamHost)
+{
+ if(!activated) {
+ activatedStream = streamHost;
+ checkForActivation();
+ }
+}
+
+void S5BManager::Item::jt_finished()
+{
+ JT_S5B *j = task;
+ task = 0;
+
+#ifdef S5B_DEBUG
+ printf("jt_finished: state=%s, %s\n", state == Initiator ? "initiator" : "target", j->success() ? "ok" : "fail");
+#endif
+
+ if(state == Initiator) {
+ if(targetMode == Unknown) {
+ targetMode = NotFast;
+ QGuardedPtr<QObject> self = this;
+ accepted();
+ if(!self)
+ return;
+ }
+ }
+
+ // if we've already reported successfully connecting to them, then this response doesn't matter
+ if(state == Initiator && connSuccess) {
+ tryActivation();
+ return;
+ }
+
+ if(j->success()) {
+ // stop connecting out
+ if(conn || lateProxy) {
+ delete conn;
+ conn = 0;
+ doConnectError();
+ }
+
+ Jid streamHost = j->streamHostUsed();
+
+ // they connected to us?
+ if(streamHost.compare(self)) {
+ if(client) {
+ if(state == Initiator) {
+ activatedStream = streamHost;
+ tryActivation();
+ }
+ else
+ checkForActivation();
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s claims to have connected to us, but we don't see this\n", peer.full().latin1());
+#endif
+ reset();
+ error(ErrWrongHost);
+ }
+ }
+ else if(streamHost.compare(proxy.jid())) {
+ // toss out any direct incoming, since it won't be used
+ delete client;
+ client = 0;
+ allowIncoming = false;
+
+#ifdef S5B_DEBUG
+ printf("attempting to connect to proxy\n");
+#endif
+ // connect to the proxy
+ proxy_conn = new S5BConnector;
+ connect(proxy_conn, SIGNAL(result(bool)), SLOT(proxy_result(bool)));
+ StreamHostList list;
+ list += proxy;
+
+ QGuardedPtr<QObject> self = this;
+ proxyConnect();
+ if(!self)
+ return;
+
+ proxy_conn->start(m->client()->jid(), list, key, udp, 30);
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s claims to have connected to a streamhost we never offered\n", peer.full().latin1());
+#endif
+ reset();
+ error(ErrWrongHost);
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s [%s] error\n", peer.full().latin1(), sid.latin1());
+#endif
+ remoteFailed = true;
+ statusCode = j->statusCode();
+
+ if(lateProxy) {
+ if(!conn)
+ doIncoming();
+ }
+ else {
+ // if connSuccess is true at this point, then we're a Target
+ if(connSuccess)
+ checkForActivation();
+ else
+ checkFailure();
+ }
+ }
+}
+
+void S5BManager::Item::conn_result(bool b)
+{
+ if(b) {
+ SocksClient *sc = conn->takeClient();
+ SocksUDP *sc_udp = conn->takeUDP();
+ StreamHost h = conn->streamHostUsed();
+ delete conn;
+ conn = 0;
+ connSuccess = true;
+
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item: %s [%s] successful outgoing connection\n", peer.full().latin1(), sid.latin1());
+#endif
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ m->doSuccess(peer, in_id, h.jid());
+
+ // if the first batch works, don't try proxy
+ lateProxy = false;
+
+ // if initiator, run with this one
+ if(state == Initiator) {
+ // if we had an incoming one, toss it
+ delete client_udp;
+ client_udp = sc_udp;
+ delete client;
+ client = sc;
+ allowIncoming = false;
+ activatedStream = peer;
+ tryActivation();
+ }
+ else {
+ client_out_udp = sc_udp;
+ client_out = sc;
+ checkForActivation();
+ }
+ }
+ else {
+ delete conn;
+ conn = 0;
+
+ // if we delayed the proxies for later, try now
+ if(lateProxy) {
+ if(remoteFailed)
+ doIncoming();
+ }
+ else
+ doConnectError();
+ }
+}
+
+void S5BManager::Item::proxy_result(bool b)
+{
+#ifdef S5B_DEBUG
+ printf("proxy_result: %s\n", b ? "ok" : "fail");
+#endif
+ if(b) {
+ SocksClient *sc = proxy_conn->takeClient();
+ SocksUDP *sc_udp = proxy_conn->takeUDP();
+ delete proxy_conn;
+ proxy_conn = 0;
+
+ connect(sc, SIGNAL(readyRead()), SLOT(sc_readyRead()));
+ connect(sc, SIGNAL(bytesWritten(int)), SLOT(sc_bytesWritten(int)));
+ connect(sc, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ client = sc;
+ client_udp = sc_udp;
+
+ // activate
+#ifdef S5B_DEBUG
+ printf("activating proxy stream\n");
+#endif
+ proxy_task = new JT_S5B(m->client()->rootTask());
+ connect(proxy_task, SIGNAL(finished()), SLOT(proxy_finished()));
+ proxy_task->requestActivation(proxy.jid(), sid, peer);
+ proxy_task->go(true);
+ }
+ else {
+ delete proxy_conn;
+ proxy_conn = 0;
+ reset();
+ error(ErrProxy);
+ }
+}
+
+void S5BManager::Item::proxy_finished()
+{
+ JT_S5B *j = proxy_task;
+ proxy_task = 0;
+
+ if(j->success()) {
+#ifdef S5B_DEBUG
+ printf("proxy stream activated\n");
+#endif
+ if(state == Initiator) {
+ activatedStream = proxy.jid();
+ tryActivation();
+ }
+ else
+ checkForActivation();
+ }
+ else {
+ reset();
+ error(ErrProxy);
+ }
+}
+
+void S5BManager::Item::sc_readyRead()
+{
+#ifdef S5B_DEBUG
+ printf("sc_readyRead\n");
+#endif
+ // only targets check for activation, and only should do it if there is no pending outgoing iq-set
+ if(state == Target && !task && !proxy_task)
+ checkForActivation();
+}
+
+void S5BManager::Item::sc_bytesWritten(int)
+{
+#ifdef S5B_DEBUG
+ printf("sc_bytesWritten\n");
+#endif
+ // this should only happen to the initiator, and should always be 1 byte (the '\r' sent earlier)
+ finished();
+}
+
+void S5BManager::Item::sc_error(int)
+{
+#ifdef S5B_DEBUG
+ printf("sc_error\n");
+#endif
+ reset();
+ error(ErrConnect);
+}
+
+void S5BManager::Item::doConnectError()
+{
+ localFailed = true;
+ m->doError(peer, in_id, 404, "Could not connect to given hosts");
+ checkFailure();
+}
+
+void S5BManager::Item::tryActivation()
+{
+#ifdef S5B_DEBUG
+ printf("tryActivation\n");
+#endif
+ if(activated) {
+#ifdef S5B_DEBUG
+ printf("already activated !?\n");
+#endif
+ return;
+ }
+
+ if(targetMode == NotFast) {
+#ifdef S5B_DEBUG
+ printf("tryActivation: NotFast\n");
+#endif
+ // nothing to activate, we're done
+ finished();
+ }
+ else if(targetMode == Fast) {
+ // with fast mode, we don't wait for the iq reply, so delete the task (if any)
+ delete task;
+ task = 0;
+
+ activated = true;
+
+ // if udp, activate using special stanza
+ if(udp) {
+ m->doActivate(peer, sid, activatedStream);
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("sending extra CR\n");
+#endif
+ // must send [CR] to activate target streamhost
+ QByteArray a(1);
+ a[0] = '\r';
+ client->write(a);
+ }
+ }
+}
+
+void S5BManager::Item::checkForActivation()
+{
+ QPtrList<SocksClient> clientList;
+ if(client)
+ clientList.append(client);
+ if(client_out)
+ clientList.append(client_out);
+ QPtrListIterator<SocksClient> it(clientList);
+ for(SocksClient *sc; (sc = it.current()); ++it) {
+#ifdef S5B_DEBUG
+ printf("checking for activation\n");
+#endif
+ if(fast) {
+ bool ok = false;
+ if(udp) {
+ if((sc == client_out && activatedStream.compare(self)) || (sc == client && !activatedStream.compare(self))) {
+ clientList.removeRef(sc);
+ ok = true;
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("need CR\n");
+#endif
+ if(sc->bytesAvailable() >= 1) {
+ clientList.removeRef(sc);
+ QByteArray a = sc->read(1);
+ if(a[0] != '\r') {
+ delete sc;
+ return;
+ }
+ ok = true;
+ }
+ }
+
+ if(ok) {
+ SocksUDP *sc_udp = 0;
+ if(sc == client) {
+ delete client_out_udp;
+ client_out_udp = 0;
+ sc_udp = client_udp;
+ }
+ else if(sc == client_out) {
+ delete client_udp;
+ client_udp = 0;
+ sc_udp = client_out_udp;
+ }
+
+ sc->disconnect(this);
+ clientList.setAutoDelete(true);
+ clientList.clear();
+ client = sc;
+ client_out = 0;
+ client_udp = sc_udp;
+ activated = true;
+#ifdef S5B_DEBUG
+ printf("activation success\n");
+#endif
+ break;
+ }
+ }
+ else {
+#ifdef S5B_DEBUG
+ printf("not fast mode, no need to wait for anything\n");
+#endif
+ clientList.removeRef(sc);
+ sc->disconnect(this);
+ clientList.setAutoDelete(true);
+ clientList.clear();
+ client = sc;
+ client_out = 0;
+ activated = true;
+ break;
+ }
+ }
+
+ if(activated) {
+ finished();
+ }
+ else {
+ // only emit waitingForActivation if there is nothing left to do
+ if((connSuccess || localFailed) && !proxy_task && !proxy_conn)
+ waitingForActivation();
+ }
+}
+
+void S5BManager::Item::checkFailure()
+{
+ bool failed = false;
+ if(state == Initiator) {
+ if(remoteFailed) {
+ if((localFailed && targetMode == Fast) || targetMode == NotFast)
+ failed = true;
+ }
+ }
+ else {
+ if(localFailed) {
+ if((remoteFailed && fast) || !fast)
+ failed = true;
+ }
+ }
+
+ if(failed) {
+ if(state == Initiator) {
+ reset();
+ if(statusCode == 404)
+ error(ErrConnect);
+ else
+ error(ErrRefused);
+ }
+ else {
+ reset();
+ error(ErrConnect);
+ }
+ }
+}
+
+void S5BManager::Item::finished()
+{
+ client->disconnect(this);
+ state = Active;
+#ifdef S5B_DEBUG
+ printf("S5BManager::Item %s [%s] linked successfully\n", peer.full().latin1(), sid.latin1());
+#endif
+ connected();
+}
+
+//----------------------------------------------------------------------------
+// S5BConnector
+//----------------------------------------------------------------------------
+class S5BConnector::Item : public QObject
+{
+ Q_OBJECT
+public:
+ SocksClient *client;
+ SocksUDP *client_udp;
+ StreamHost host;
+ QString key;
+ bool udp;
+ int udp_tries;
+ QTimer t;
+ Jid jid;
+
+ Item(const Jid &self, const StreamHost &_host, const QString &_key, bool _udp) : QObject(0)
+ {
+ jid = self;
+ host = _host;
+ key = _key;
+ udp = _udp;
+ client = new SocksClient;
+ client_udp = 0;
+ connect(client, SIGNAL(connected()), SLOT(sc_connected()));
+ connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+ connect(&t, SIGNAL(timeout()), SLOT(trySendUDP()));
+ }
+
+ ~Item()
+ {
+ cleanup();
+ }
+
+ void start()
+ {
+ client->connectToHost(host.host(), host.port(), key, 0, udp);
+ }
+
+ void udpSuccess()
+ {
+ t.stop();
+ client_udp->change(key, 0); // flip over to the data port
+ success();
+ }
+
+signals:
+ void result(bool);
+
+private slots:
+ void sc_connected()
+ {
+ // if udp, need to send init packet before we are good
+ if(udp) {
+ // port 1 is init
+ client_udp = client->createUDP(key, 1, client->peerAddress(), client->peerPort());
+ udp_tries = 0;
+ t.start(5000);
+ trySendUDP();
+ return;
+ }
+
+ success();
+ }
+
+ void sc_error(int)
+ {
+#ifdef S5B_DEBUG
+ printf("S5BConnector[%s]: error\n", host.host().latin1());
+#endif
+ cleanup();
+ result(false);
+ }
+
+ void trySendUDP()
+ {
+ if(udp_tries == 5) {
+ t.stop();
+ cleanup();
+ result(false);
+ return;
+ }
+
+ // send initialization with our JID
+ QCString cs = jid.full().utf8();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ client_udp->write(a);
+ ++udp_tries;
+ }
+
+private:
+ void cleanup()
+ {
+ delete client_udp;
+ client_udp = 0;
+ delete client;
+ client = 0;
+ }
+
+ void success()
+ {
+#ifdef S5B_DEBUG
+ printf("S5BConnector[%s]: success\n", host.host().latin1());
+#endif
+ client->disconnect(this);
+ result(true);
+ }
+};
+
+class S5BConnector::Private
+{
+public:
+ SocksClient *active;
+ SocksUDP *active_udp;
+ QPtrList<Item> itemList;
+ QString key;
+ StreamHost activeHost;
+ QTimer t;
+};
+
+S5BConnector::S5BConnector(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->active = 0;
+ d->active_udp = 0;
+ d->itemList.setAutoDelete(true);
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+S5BConnector::~S5BConnector()
+{
+ reset();
+ delete d;
+}
+
+void S5BConnector::reset()
+{
+ d->t.stop();
+ delete d->active_udp;
+ d->active_udp = 0;
+ delete d->active;
+ d->active = 0;
+ d->itemList.clear();
+}
+
+void S5BConnector::start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout)
+{
+ reset();
+
+#ifdef S5B_DEBUG
+ printf("S5BConnector: starting [%p]!\n", this);
+#endif
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ Item *i = new Item(self, *it, key, udp);
+ connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+ d->itemList.append(i);
+ i->start();
+ }
+ d->t.start(timeout * 1000);
+}
+
+SocksClient *S5BConnector::takeClient()
+{
+ SocksClient *c = d->active;
+ d->active = 0;
+ return c;
+}
+
+SocksUDP *S5BConnector::takeUDP()
+{
+ SocksUDP *c = d->active_udp;
+ d->active_udp = 0;
+ return c;
+}
+
+StreamHost S5BConnector::streamHostUsed() const
+{
+ return d->activeHost;
+}
+
+void S5BConnector::item_result(bool b)
+{
+ Item *i = (Item *)sender();
+ if(b) {
+ d->active = i->client;
+ i->client = 0;
+ d->active_udp = i->client_udp;
+ i->client_udp = 0;
+ d->activeHost = i->host;
+ d->itemList.clear();
+ d->t.stop();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: complete! [%p]\n", this);
+#endif
+ result(true);
+ }
+ else {
+ d->itemList.removeRef(i);
+ if(d->itemList.isEmpty()) {
+ d->t.stop();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: failed! [%p]\n", this);
+#endif
+ result(false);
+ }
+ }
+}
+
+void S5BConnector::t_timeout()
+{
+ reset();
+#ifdef S5B_DEBUG
+ printf("S5BConnector: failed! (timeout)\n");
+#endif
+ result(false);
+}
+
+void S5BConnector::man_udpSuccess(const Jid &streamHost)
+{
+ // was anyone sending to this streamhost?
+ QPtrListIterator<Item> it(d->itemList);
+ for(Item *i; (i = it.current()); ++it) {
+ if(i->host.jid().compare(streamHost) && i->client_udp) {
+ i->udpSuccess();
+ return;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+// S5BServer
+//----------------------------------------------------------------------------
+class S5BServer::Item : public QObject
+{
+ Q_OBJECT
+public:
+ SocksClient *client;
+ QString host;
+ QTimer expire;
+
+ Item(SocksClient *c) : QObject(0)
+ {
+ client = c;
+ connect(client, SIGNAL(incomingMethods(int)), SLOT(sc_incomingMethods(int)));
+ connect(client, SIGNAL(incomingConnectRequest(const QString &, int)), SLOT(sc_incomingConnectRequest(const QString &, int)));
+ connect(client, SIGNAL(error(int)), SLOT(sc_error(int)));
+
+ connect(&expire, SIGNAL(timeout()), SLOT(doError()));
+ resetExpiration();
+ }
+
+ ~Item()
+ {
+ delete client;
+ }
+
+ void resetExpiration()
+ {
+ expire.start(30000);
+ }
+
+signals:
+ void result(bool);
+
+private slots:
+ void doError()
+ {
+ expire.stop();
+ delete client;
+ client = 0;
+ result(false);
+ }
+
+ void sc_incomingMethods(int m)
+ {
+ if(m & SocksClient::AuthNone)
+ client->chooseMethod(SocksClient::AuthNone);
+ else
+ doError();
+ }
+
+ void sc_incomingConnectRequest(const QString &_host, int port)
+ {
+ if(port == 0) {
+ host = _host;
+ client->disconnect(this);
+ result(true);
+ }
+ else
+ doError();
+ }
+
+ void sc_error(int)
+ {
+ doError();
+ }
+};
+
+class S5BServer::Private
+{
+public:
+ SocksServer serv;
+ QStringList hostList;
+ QPtrList<S5BManager> manList;
+ QPtrList<Item> itemList;
+};
+
+S5BServer::S5BServer(QObject *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->itemList.setAutoDelete(true);
+ connect(&d->serv, SIGNAL(incomingReady()), SLOT(ss_incomingReady()));
+ connect(&d->serv, SIGNAL(incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)), SLOT(ss_incomingUDP(const QString &, int, const QHostAddress &, int, const QByteArray &)));
+}
+
+S5BServer::~S5BServer()
+{
+ unlinkAll();
+ delete d;
+}
+
+bool S5BServer::isActive() const
+{
+ return d->serv.isActive();
+}
+
+bool S5BServer::start(int port)
+{
+ d->serv.stop();
+ return d->serv.listen(port, true);
+}
+
+void S5BServer::stop()
+{
+ d->serv.stop();
+}
+
+void S5BServer::setHostList(const QStringList &list)
+{
+ d->hostList = list;
+}
+
+QStringList S5BServer::hostList() const
+{
+ return d->hostList;
+}
+
+int S5BServer::port() const
+{
+ return d->serv.port();
+}
+
+void S5BServer::ss_incomingReady()
+{
+ Item *i = new Item(d->serv.takeIncoming());
+#ifdef S5B_DEBUG
+ printf("S5BServer: incoming connection from %s:%d\n", i->client->peerAddress().toString().latin1(), i->client->peerPort());
+#endif
+ connect(i, SIGNAL(result(bool)), SLOT(item_result(bool)));
+ d->itemList.append(i);
+}
+
+void S5BServer::ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data)
+{
+ if(port != 0 || port != 1)
+ return;
+
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ if(m->srv_ownsHash(host)) {
+ m->srv_incomingUDP(port == 1 ? true : false, addr, sourcePort, host, data);
+ return;
+ }
+ }
+}
+
+void S5BServer::item_result(bool b)
+{
+ Item *i = (Item *)sender();
+#ifdef S5B_DEBUG
+ printf("S5BServer item result: %d\n", b);
+#endif
+ if(!b) {
+ d->itemList.removeRef(i);
+ return;
+ }
+
+ SocksClient *c = i->client;
+ i->client = 0;
+ QString key = i->host;
+ d->itemList.removeRef(i);
+
+ // find the appropriate manager for this incoming connection
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it) {
+ if(m->srv_ownsHash(key)) {
+ m->srv_incomingReady(c, key);
+ return;
+ }
+ }
+
+ // throw it away
+ delete c;
+}
+
+void S5BServer::link(S5BManager *m)
+{
+ d->manList.append(m);
+}
+
+void S5BServer::unlink(S5BManager *m)
+{
+ d->manList.removeRef(m);
+}
+
+void S5BServer::unlinkAll()
+{
+ QPtrListIterator<S5BManager> it(d->manList);
+ for(S5BManager *m; (m = it.current()); ++it)
+ m->srv_unlink();
+ d->manList.clear();
+}
+
+const QPtrList<S5BManager> & S5BServer::managerList() const
+{
+ return d->manList;
+}
+
+void S5BServer::writeUDP(const QHostAddress &addr, int port, const QByteArray &data)
+{
+ d->serv.writeUDP(addr, port, data);
+}
+
+//----------------------------------------------------------------------------
+// JT_S5B
+//----------------------------------------------------------------------------
+class JT_S5B::Private
+{
+public:
+ QDomElement iq;
+ Jid to;
+ Jid streamHost;
+ StreamHost proxyInfo;
+ int mode;
+ QTimer t;
+};
+
+JT_S5B::JT_S5B(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ d->mode = -1;
+ connect(&d->t, SIGNAL(timeout()), SLOT(t_timeout()));
+}
+
+JT_S5B::~JT_S5B()
+{
+ delete d;
+}
+
+void JT_S5B::request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp)
+{
+ d->mode = 0;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ query.setAttribute("sid", sid);
+ query.setAttribute("mode", udp ? "udp" : "tcp" );
+ iq.appendChild(query);
+ for(StreamHostList::ConstIterator it = hosts.begin(); it != hosts.end(); ++it) {
+ QDomElement shost = doc()->createElement("streamhost");
+ shost.setAttribute("jid", (*it).jid().full());
+ shost.setAttribute("host", (*it).host());
+ shost.setAttribute("port", QString::number((*it).port()));
+ if((*it).isProxy()) {
+ QDomElement p = doc()->createElement("proxy");
+ p.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ shost.appendChild(p);
+ }
+ query.appendChild(shost);
+ }
+ if(fast) {
+ QDomElement e = doc()->createElement("fast");
+ e.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ query.appendChild(e);
+ }
+ d->iq = iq;
+}
+
+void JT_S5B::requestProxyInfo(const Jid &to)
+{
+ d->mode = 1;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ iq.appendChild(query);
+ d->iq = iq;
+}
+
+void JT_S5B::requestActivation(const Jid &to, const QString &sid, const Jid &target)
+{
+ d->mode = 2;
+
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ query.setAttribute("sid", sid);
+ iq.appendChild(query);
+ QDomElement act = doc()->createElement("activate");
+ act.appendChild(doc()->createTextNode(target.full()));
+ query.appendChild(act);
+ d->iq = iq;
+}
+
+void JT_S5B::onGo()
+{
+ if(d->mode == 1)
+ d->t.start(15000, true);
+ send(d->iq);
+}
+
+void JT_S5B::onDisconnect()
+{
+ d->t.stop();
+}
+
+bool JT_S5B::take(const QDomElement &x)
+{
+ if(d->mode == -1)
+ return false;
+
+ if(!iqVerify(x, d->to, id()))
+ return false;
+
+ d->t.stop();
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+ if(d->mode == 0) {
+ d->streamHost = "";
+ if(!q.isNull()) {
+ QDomElement shost = q.elementsByTagName("streamhost-used").item(0).toElement();
+ if(!shost.isNull())
+ d->streamHost = shost.attribute("jid");
+ }
+
+ setSuccess();
+ }
+ else if(d->mode == 1) {
+ if(!q.isNull()) {
+ QDomElement shost = q.elementsByTagName("streamhost").item(0).toElement();
+ if(!shost.isNull()) {
+ Jid j = shost.attribute("jid");
+ if(j.isValid()) {
+ QString host = shost.attribute("host");
+ if(!host.isEmpty()) {
+ int port = shost.attribute("port").toInt();
+ StreamHost h;
+ h.setJid(j);
+ h.setHost(host);
+ h.setPort(port);
+ h.setIsProxy(true);
+ d->proxyInfo = h;
+ }
+ }
+ }
+ }
+
+ setSuccess();
+ }
+ else {
+ setSuccess();
+ }
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+void JT_S5B::t_timeout()
+{
+ d->mode = -1;
+ setError(500, "Timed out");
+}
+
+Jid JT_S5B::streamHostUsed() const
+{
+ return d->streamHost;
+}
+
+StreamHost JT_S5B::proxyInfo() const
+{
+ return d->proxyInfo;
+}
+
+//----------------------------------------------------------------------------
+// JT_PushS5B
+//----------------------------------------------------------------------------
+JT_PushS5B::JT_PushS5B(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushS5B::~JT_PushS5B()
+{
+}
+
+int JT_PushS5B::priority() const
+{
+ return 1;
+}
+
+bool JT_PushS5B::take(const QDomElement &e)
+{
+ // look for udpsuccess
+ if(e.tagName() == "message") {
+ QDomElement x = e.elementsByTagName("udpsuccess").item(0).toElement();
+ if(!x.isNull() && x.attribute("xmlns") == "http://jabber.org/protocol/bytestreams") {
+ incomingUDPSuccess(Jid(x.attribute("from")), x.attribute("dstaddr"));
+ return true;
+ }
+ x = e.elementsByTagName("activate").item(0).toElement();
+ if(!x.isNull() && x.attribute("xmlns") == "http://affinix.com/jabber/stream") {
+ incomingActivate(Jid(x.attribute("from")), x.attribute("sid"), Jid(x.attribute("jid")));
+ return true;
+ }
+ return false;
+ }
+
+ // must be an iq-set tag
+ if(e.tagName() != "iq")
+ return false;
+ if(e.attribute("type") != "set")
+ return false;
+ if(queryNS(e) != "http://jabber.org/protocol/bytestreams")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QDomElement q = queryTag(e);
+ QString sid = q.attribute("sid");
+
+ StreamHostList hosts;
+ QDomNodeList nl = q.elementsByTagName("streamhost");
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomElement shost = nl.item(n).toElement();
+ if(hosts.count() < MAXSTREAMHOSTS) {
+ Jid j = shost.attribute("jid");
+ if(!j.isValid())
+ continue;
+ QString host = shost.attribute("host");
+ if(host.isEmpty())
+ continue;
+ int port = shost.attribute("port").toInt();
+ QDomElement p = shost.elementsByTagName("proxy").item(0).toElement();
+ bool isProxy = false;
+ if(!p.isNull() && p.attribute("xmlns") == "http://affinix.com/jabber/stream")
+ isProxy = true;
+
+ StreamHost h;
+ h.setJid(j);
+ h.setHost(host);
+ h.setPort(port);
+ h.setIsProxy(isProxy);
+ hosts += h;
+ }
+ }
+
+ bool fast = false;
+ QDomElement t;
+ t = q.elementsByTagName("fast").item(0).toElement();
+ if(!t.isNull() && t.attribute("xmlns") == "http://affinix.com/jabber/stream")
+ fast = true;
+
+ S5BRequest r;
+ r.from = from;
+ r.id = e.attribute("id");
+ r.sid = sid;
+ r.hosts = hosts;
+ r.fast = fast;
+ r.udp = q.attribute("mode") == "udp" ? true: false;
+
+ incoming(r);
+ return true;
+}
+
+void JT_PushS5B::respondSuccess(const Jid &to, const QString &id, const Jid &streamHost)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ iq.appendChild(query);
+ QDomElement shost = doc()->createElement("streamhost-used");
+ shost.setAttribute("jid", streamHost.full());
+ query.appendChild(shost);
+ send(iq);
+}
+
+void JT_PushS5B::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+void JT_PushS5B::sendUDPSuccess(const Jid &to, const QString &dstaddr)
+{
+ QDomElement m = doc()->createElement("message");
+ m.setAttribute("to", to.full());
+ QDomElement u = doc()->createElement("udpsuccess");
+ u.setAttribute("xmlns", "http://jabber.org/protocol/bytestreams");
+ u.setAttribute("dstaddr", dstaddr);
+ m.appendChild(u);
+ send(m);
+}
+
+void JT_PushS5B::sendActivate(const Jid &to, const QString &sid, const Jid &streamHost)
+{
+ QDomElement m = doc()->createElement("message");
+ m.setAttribute("to", to.full());
+ QDomElement act = doc()->createElement("activate");
+ act.setAttribute("xmlns", "http://affinix.com/jabber/stream");
+ act.setAttribute("sid", sid);
+ act.setAttribute("jid", streamHost.full());
+ m.appendChild(act);
+ send(m);
+}
+
+//----------------------------------------------------------------------------
+// StreamHost
+//----------------------------------------------------------------------------
+StreamHost::StreamHost()
+{
+ v_port = -1;
+ proxy = false;
+}
+
+const Jid & StreamHost::jid() const
+{
+ return j;
+}
+
+const QString & StreamHost::host() const
+{
+ return v_host;
+}
+
+int StreamHost::port() const
+{
+ return v_port;
+}
+
+bool StreamHost::isProxy() const
+{
+ return proxy;
+}
+
+void StreamHost::setJid(const Jid &_j)
+{
+ j = _j;
+}
+
+void StreamHost::setHost(const QString &host)
+{
+ v_host = host;
+}
+
+void StreamHost::setPort(int port)
+{
+ v_port = port;
+}
+
+void StreamHost::setIsProxy(bool b)
+{
+ proxy = b;
+}
+
+}
+
+#include"s5b.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/s5b.h b/kopete/protocols/jabber/libiris/iris/jabber/s5b.h
new file mode 100644
index 00000000..dec06969
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/s5b.h
@@ -0,0 +1,341 @@
+/*
+ * s5b.h - direct connection protocol via tcp
+ * 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 XMPP_S5B_H
+#define XMPP_S5B_H
+
+#include<qobject.h>
+#include<qcstring.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include"im.h"
+#include"bytestream.h"
+
+class SocksClient;
+class SocksUDP;
+
+namespace XMPP
+{
+ class StreamHost;
+ class S5BConnection;
+ class S5BManager;
+ class S5BServer;
+ struct S5BRequest;
+ typedef QValueList<StreamHost> StreamHostList;
+ typedef QPtrList<S5BConnection> S5BConnectionList;
+ typedef QPtrListIterator<S5BConnection> S5BConnectionListIt;
+
+ class S5BDatagram
+ {
+ public:
+ S5BDatagram();
+ S5BDatagram(int source, int dest, const QByteArray &data);
+
+ int sourcePort() const;
+ int destPort() const;
+ QByteArray data() const;
+
+ private:
+ int _source, _dest;
+ QByteArray _buf;
+ };
+
+ class S5BConnection : public ByteStream
+ {
+ Q_OBJECT
+ public:
+ enum Mode { Stream, Datagram };
+ enum Error { ErrRefused, ErrConnect, ErrProxy, ErrSocket };
+ enum State { Idle, Requesting, Connecting, WaitingForAccept, Active };
+ ~S5BConnection();
+
+ Jid proxy() const;
+ void setProxy(const Jid &proxy);
+
+ void connectToJid(const Jid &peer, const QString &sid, Mode m = Stream);
+ void accept();
+ void close();
+
+ Jid peer() const;
+ QString sid() const;
+ bool isRemote() const;
+ Mode mode() const;
+ int state() const;
+
+ bool isOpen() const;
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ void writeDatagram(const S5BDatagram &);
+ S5BDatagram readDatagram();
+ int datagramsAvailable() const;
+
+ signals:
+ void proxyQuery(); // querying proxy for streamhost information
+ void proxyResult(bool b); // query success / fail
+ void requesting(); // sent actual S5B request (initiator only)
+ void accepted(); // target accepted (initiator only
+ void tryingHosts(const StreamHostList &hosts); // currently connecting to these hosts
+ void proxyConnect(); // connecting to proxy
+ void waitingForActivation(); // waiting for activation (target only)
+ void connected(); // connection active
+ void datagramReady();
+
+ private slots:
+ void doPending();
+
+ void sc_connectionClosed();
+ void sc_delayedCloseFinished();
+ void sc_readyRead();
+ void sc_bytesWritten(int);
+ void sc_error(int);
+
+ void su_packetReady(const QByteArray &buf);
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+ void handleUDP(const QByteArray &buf);
+ void sendUDP(const QByteArray &buf);
+
+ friend class S5BManager;
+ void man_waitForAccept(const S5BRequest &r);
+ void man_clientReady(SocksClient *, SocksUDP *);
+ void man_udpReady(const QByteArray &buf);
+ void man_failed(int);
+ S5BConnection(S5BManager *, QObject *parent=0);
+ };
+
+ class S5BManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BManager(Client *);
+ ~S5BManager();
+
+ Client *client() const;
+ S5BServer *server() const;
+ void setServer(S5BServer *s);
+
+ bool isAcceptableSID(const Jid &peer, const QString &sid) const;
+ QString genUniqueSID(const Jid &peer) const;
+
+ S5BConnection *createConnection();
+ S5BConnection *takeIncoming();
+
+ class Item;
+ class Entry;
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void ps_incoming(const S5BRequest &req);
+ void ps_incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+ void ps_incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+ void item_accepted();
+ void item_tryingHosts(const StreamHostList &list);
+ void item_proxyConnect();
+ void item_waitingForActivation();
+ void item_connected();
+ void item_error(int);
+ void query_finished();
+
+ private:
+ class Private;
+ Private *d;
+
+ S5BConnection *findIncoming(const Jid &from, const QString &sid) const;
+ Entry *findEntry(S5BConnection *) const;
+ Entry *findEntry(Item *) const;
+ Entry *findEntryByHash(const QString &key) const;
+ Entry *findEntryBySID(const Jid &peer, const QString &sid) const;
+ Entry *findServerEntryByHash(const QString &key) const;
+
+ void entryContinue(Entry *e);
+ void queryProxy(Entry *e);
+ bool targetShouldOfferProxy(Entry *e);
+
+ friend class S5BConnection;
+ void con_connect(S5BConnection *);
+ void con_accept(S5BConnection *);
+ void con_reject(S5BConnection *);
+ void con_unlink(S5BConnection *);
+ void con_sendUDP(S5BConnection *, const QByteArray &buf);
+
+ friend class S5BServer;
+ bool srv_ownsHash(const QString &key) const;
+ void srv_incomingReady(SocksClient *sc, const QString &key);
+ void srv_incomingUDP(bool init, const QHostAddress &addr, int port, const QString &key, const QByteArray &data);
+ void srv_unlink();
+
+ friend class Item;
+ void doSuccess(const Jid &peer, const QString &id, const Jid &streamHost);
+ void doError(const Jid &peer, const QString &id, int, const QString &);
+ void doActivate(const Jid &peer, const QString &sid, const Jid &streamHost);
+ };
+
+ class S5BConnector : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BConnector(QObject *parent=0);
+ ~S5BConnector();
+
+ void reset();
+ void start(const Jid &self, const StreamHostList &hosts, const QString &key, bool udp, int timeout);
+ SocksClient *takeClient();
+ SocksUDP *takeUDP();
+ StreamHost streamHostUsed() const;
+
+ class Item;
+
+ signals:
+ void result(bool);
+
+ private slots:
+ void item_result(bool);
+ void t_timeout();
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class S5BManager;
+ void man_udpSuccess(const Jid &streamHost);
+ };
+
+ // listens on a port for serving
+ class S5BServer : public QObject
+ {
+ Q_OBJECT
+ public:
+ S5BServer(QObject *par=0);
+ ~S5BServer();
+
+ bool isActive() const;
+ bool start(int port);
+ void stop();
+ int port() const;
+ void setHostList(const QStringList &);
+ QStringList hostList() const;
+
+ class Item;
+
+ private slots:
+ void ss_incomingReady();
+ void ss_incomingUDP(const QString &host, int port, const QHostAddress &addr, int sourcePort, const QByteArray &data);
+ void item_result(bool);
+
+ private:
+ class Private;
+ Private *d;
+
+ friend class S5BManager;
+ void link(S5BManager *);
+ void unlink(S5BManager *);
+ void unlinkAll();
+ const QPtrList<S5BManager> & managerList() const;
+ void writeUDP(const QHostAddress &addr, int port, const QByteArray &data);
+ };
+
+ class JT_S5B : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_S5B(Task *);
+ ~JT_S5B();
+
+ void request(const Jid &to, const QString &sid, const StreamHostList &hosts, bool fast, bool udp=false);
+ void requestProxyInfo(const Jid &to);
+ void requestActivation(const Jid &to, const QString &sid, const Jid &target);
+
+ void onGo();
+ void onDisconnect();
+ bool take(const QDomElement &);
+
+ Jid streamHostUsed() const;
+ StreamHost proxyInfo() const;
+
+ private slots:
+ void t_timeout();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ struct S5BRequest
+ {
+ Jid from;
+ QString id, sid;
+ StreamHostList hosts;
+ bool fast;
+ bool udp;
+ };
+ class JT_PushS5B : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushS5B(Task *);
+ ~JT_PushS5B();
+
+ int priority() const;
+
+ void respondSuccess(const Jid &to, const QString &id, const Jid &streamHost);
+ void respondError(const Jid &to, const QString &id, int code, const QString &str);
+ void sendUDPSuccess(const Jid &to, const QString &dstaddr);
+ void sendActivate(const Jid &to, const QString &sid, const Jid &streamHost);
+
+ bool take(const QDomElement &);
+
+ signals:
+ void incoming(const S5BRequest &req);
+ void incomingUDPSuccess(const Jid &from, const QString &dstaddr);
+ void incomingActivate(const Jid &from, const QString &sid, const Jid &streamHost);
+ };
+
+ class StreamHost
+ {
+ public:
+ StreamHost();
+
+ const Jid & jid() const;
+ const QString & host() const;
+ int port() const;
+ bool isProxy() const;
+ void setJid(const Jid &);
+ void setHost(const QString &);
+ void setPort(int);
+ void setIsProxy(bool);
+
+ private:
+ Jid j;
+ QString v_host;
+ int v_port;
+ bool proxy;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp
new file mode 100644
index 00000000..813157bf
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.cpp
@@ -0,0 +1,638 @@
+/*
+ * ibb.cpp - Inband bytestream
+ * Copyright (C) 2001, 2002 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"xmpp_ibb.h"
+
+#include<qtimer.h>
+#include"xmpp_xmlcommon.h"
+#include"base64.h"
+
+#include<stdlib.h>
+
+#define IBB_PACKET_SIZE 4096
+#define IBB_PACKET_DELAY 0
+
+using namespace XMPP;
+
+static int num_conn = 0;
+static int id_conn = 0;
+
+//----------------------------------------------------------------------------
+// IBBConnection
+//----------------------------------------------------------------------------
+class IBBConnection::Private
+{
+public:
+ Private() {}
+
+ int state;
+ Jid peer;
+ QString sid;
+ IBBManager *m;
+ JT_IBB *j;
+ QDomElement comment;
+ QString iq_id;
+
+ int blockSize;
+ QByteArray recvbuf, sendbuf;
+ bool closePending, closing;
+
+ int id;
+};
+
+IBBConnection::IBBConnection(IBBManager *m)
+:ByteStream(m)
+{
+ d = new Private;
+ d->m = m;
+ d->j = 0;
+ reset();
+
+ ++num_conn;
+ d->id = id_conn++;
+ QString dstr; dstr.sprintf("IBBConnection[%d]: constructing, count=%d\n", d->id, num_conn);
+ d->m->client()->debug(dstr);
+}
+
+void IBBConnection::reset(bool clear)
+{
+ d->m->unlink(this);
+ d->state = Idle;
+ d->closePending = false;
+ d->closing = false;
+
+ delete d->j;
+ d->j = 0;
+
+ d->sendbuf.resize(0);
+ if(clear)
+ d->recvbuf.resize(0);
+}
+
+IBBConnection::~IBBConnection()
+{
+ reset(true);
+
+ --num_conn;
+ QString dstr; dstr.sprintf("IBBConnection[%d]: destructing, count=%d\n", d->id, num_conn);
+ d->m->client()->debug(dstr);
+
+ delete d;
+}
+
+void IBBConnection::connectToJid(const Jid &peer, const QDomElement &comment)
+{
+ close();
+ reset(true);
+
+ d->state = Requesting;
+ d->peer = peer;
+ d->comment = comment;
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: initiating request to %s\n", d->id, peer.full().latin1());
+ d->m->client()->debug(dstr);
+
+ d->j = new JT_IBB(d->m->client()->rootTask());
+ connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+ d->j->request(d->peer, comment);
+ d->j->go(true);
+}
+
+void IBBConnection::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: accepting %s [%s]\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+ d->m->client()->debug(dstr);
+
+ d->m->doAccept(this, d->iq_id);
+ d->state = Active;
+ d->m->link(this);
+}
+
+void IBBConnection::close()
+{
+ if(d->state == Idle)
+ return;
+
+ if(d->state == WaitingForAccept) {
+ d->m->doReject(this, d->iq_id, 403, "Rejected");
+ reset();
+ return;
+ }
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: closing\n", d->id);
+ d->m->client()->debug(dstr);
+
+ if(d->state == Active) {
+ // if there is data pending to be written, then pend the closing
+ if(bytesToWrite() > 0) {
+ d->closePending = true;
+ trySend();
+ return;
+ }
+
+ // send a close packet
+ JT_IBB *j = new JT_IBB(d->m->client()->rootTask());
+ j->sendData(d->peer, d->sid, QByteArray(), true);
+ j->go(true);
+ }
+
+ reset();
+}
+
+int IBBConnection::state() const
+{
+ return d->state;
+}
+
+Jid IBBConnection::peer() const
+{
+ return d->peer;
+}
+
+QString IBBConnection::streamid() const
+{
+ return d->sid;
+}
+
+QDomElement IBBConnection::comment() const
+{
+ return d->comment;
+}
+
+bool IBBConnection::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void IBBConnection::write(const QByteArray &a)
+{
+ if(d->state != Active || d->closePending || d->closing)
+ return;
+
+ // append to the end of our send buffer
+ int oldsize = d->sendbuf.size();
+ d->sendbuf.resize(oldsize + a.size());
+ memcpy(d->sendbuf.data() + oldsize, a.data(), a.size());
+
+ trySend();
+}
+
+QByteArray IBBConnection::read(int)
+{
+ // TODO: obey argument
+ QByteArray a = d->recvbuf.copy();
+ d->recvbuf.resize(0);
+ return a;
+}
+
+int IBBConnection::bytesAvailable() const
+{
+ return d->recvbuf.size();
+}
+
+int IBBConnection::bytesToWrite() const
+{
+ return d->sendbuf.size();
+}
+
+void IBBConnection::waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id)
+{
+ close();
+ reset(true);
+
+ d->state = WaitingForAccept;
+ d->peer = peer;
+ d->sid = sid;
+ d->comment = comment;
+ d->iq_id = iq_id;
+}
+
+void IBBConnection::takeIncomingData(const QByteArray &a, bool close)
+{
+ // append to the end of our recv buffer
+ int oldsize = d->recvbuf.size();
+ d->recvbuf.resize(oldsize + a.size());
+ memcpy(d->recvbuf.data() + oldsize, a.data(), a.size());
+
+ readyRead();
+
+ if(close) {
+ reset();
+ connectionClosed();
+ }
+}
+
+void IBBConnection::ibb_finished()
+{
+ JT_IBB *j = d->j;
+ d->j = 0;
+
+ if(j->success()) {
+ if(j->mode() == JT_IBB::ModeRequest) {
+ d->sid = j->streamid();
+
+ QString dstr; dstr.sprintf("IBBConnection[%d]: %s [%s] accepted.\n", d->id, d->peer.full().latin1(), d->sid.latin1());
+ d->m->client()->debug(dstr);
+
+ d->state = Active;
+ d->m->link(this);
+ connected();
+ }
+ else {
+ bytesWritten(d->blockSize);
+
+ if(d->closing) {
+ reset();
+ delayedCloseFinished();
+ }
+
+ if(!d->sendbuf.isEmpty() || d->closePending)
+ QTimer::singleShot(IBB_PACKET_DELAY, this, SLOT(trySend()));
+ }
+ }
+ else {
+ if(j->mode() == JT_IBB::ModeRequest) {
+ QString dstr; dstr.sprintf("IBBConnection[%d]: %s refused.\n", d->id, d->peer.full().latin1());
+ d->m->client()->debug(dstr);
+
+ reset(true);
+ error(ErrRequest);
+ }
+ else {
+ reset(true);
+ error(ErrData);
+ }
+ }
+}
+
+void IBBConnection::trySend()
+{
+ // if we already have an active task, then don't do anything
+ if(d->j)
+ return;
+
+ QByteArray a;
+ if(!d->sendbuf.isEmpty()) {
+ // take a chunk
+ if(d->sendbuf.size() < IBB_PACKET_SIZE)
+ a.resize(d->sendbuf.size());
+ else
+ a.resize(IBB_PACKET_SIZE);
+ memcpy(a.data(), d->sendbuf.data(), a.size());
+ d->sendbuf.resize(d->sendbuf.size() - a.size());
+ }
+
+ bool doClose = false;
+ if(d->sendbuf.isEmpty() && d->closePending)
+ doClose = true;
+
+ // null operation?
+ if(a.isEmpty() && !doClose)
+ return;
+
+ printf("IBBConnection[%d]: sending [%d] bytes ", d->id, a.size());
+ if(doClose)
+ printf("and closing.\n");
+ else
+ printf("(%d bytes left)\n", d->sendbuf.size());
+
+ if(doClose) {
+ d->closePending = false;
+ d->closing = true;
+ }
+
+ d->blockSize = a.size();
+ d->j = new JT_IBB(d->m->client()->rootTask());
+ connect(d->j, SIGNAL(finished()), SLOT(ibb_finished()));
+ d->j->sendData(d->peer, d->sid, a, doClose);
+ d->j->go(true);
+}
+
+
+//----------------------------------------------------------------------------
+// IBBManager
+//----------------------------------------------------------------------------
+class IBBManager::Private
+{
+public:
+ Private() {}
+
+ Client *client;
+ IBBConnectionList activeConns;
+ IBBConnectionList incomingConns;
+ JT_IBB *ibb;
+};
+
+IBBManager::IBBManager(Client *parent)
+:QObject(parent)
+{
+ d = new Private;
+ d->client = parent;
+
+ d->ibb = new JT_IBB(d->client->rootTask(), true);
+ connect(d->ibb, SIGNAL(incomingRequest(const Jid &, const QString &, const QDomElement &)), SLOT(ibb_incomingRequest(const Jid &, const QString &, const QDomElement &)));
+ connect(d->ibb, SIGNAL(incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)), SLOT(ibb_incomingData(const Jid &, const QString &, const QString &, const QByteArray &, bool)));
+}
+
+IBBManager::~IBBManager()
+{
+ d->incomingConns.setAutoDelete(true);
+ d->incomingConns.clear();
+ delete d->ibb;
+ delete d;
+}
+
+Client *IBBManager::client() const
+{
+ return d->client;
+}
+
+IBBConnection *IBBManager::takeIncoming()
+{
+ if(d->incomingConns.isEmpty())
+ return 0;
+
+ IBBConnection *c = d->incomingConns.getFirst();
+ d->incomingConns.removeRef(c);
+ return c;
+}
+
+void IBBManager::ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &comment)
+{
+ QString sid = genUniqueKey();
+
+ // create a "waiting" connection
+ IBBConnection *c = new IBBConnection(this);
+ c->waitForAccept(from, sid, comment, id);
+ d->incomingConns.append(c);
+ incomingReady();
+}
+
+void IBBManager::ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close)
+{
+ IBBConnection *c = findConnection(streamid, from);
+ if(!c) {
+ d->ibb->respondError(from, id, 404, "No such stream");
+ }
+ else {
+ d->ibb->respondAck(from, id);
+ c->takeIncomingData(data, close);
+ }
+}
+
+QString IBBManager::genKey() const
+{
+ QString key = "ibb_";
+
+ for(int i = 0; i < 4; ++i) {
+ int word = rand() & 0xffff;
+ for(int n = 0; n < 4; ++n) {
+ QString s;
+ s.sprintf("%x", (word >> (n * 4)) & 0xf);
+ key.append(s);
+ }
+ }
+
+ return key;
+}
+
+QString IBBManager::genUniqueKey() const
+{
+ // get unused key
+ QString key;
+ while(1) {
+ key = genKey();
+
+ if(!findConnection(key))
+ break;
+ }
+
+ return key;
+}
+
+void IBBManager::link(IBBConnection *c)
+{
+ d->activeConns.append(c);
+}
+
+void IBBManager::unlink(IBBConnection *c)
+{
+ d->activeConns.removeRef(c);
+}
+
+IBBConnection *IBBManager::findConnection(const QString &sid, const Jid &peer) const
+{
+ IBBConnectionListIt it(d->activeConns);
+ for(IBBConnection *c; (c = it.current()); ++it) {
+ if(c->streamid() == sid && (peer.isEmpty() || c->peer().compare(peer)) )
+ return c;
+ }
+ return 0;
+}
+
+void IBBManager::doAccept(IBBConnection *c, const QString &id)
+{
+ d->ibb->respondSuccess(c->peer(), id, c->streamid());
+}
+
+void IBBManager::doReject(IBBConnection *c, const QString &id, int code, const QString &str)
+{
+ d->ibb->respondError(c->peer(), id, code, str);
+}
+
+
+//----------------------------------------------------------------------------
+// JT_IBB
+//----------------------------------------------------------------------------
+class JT_IBB::Private
+{
+public:
+ Private() {}
+
+ QDomElement iq;
+ int mode;
+ bool serve;
+ Jid to;
+ QString streamid;
+};
+
+JT_IBB::JT_IBB(Task *parent, bool serve)
+:Task(parent)
+{
+ d = new Private;
+ d->serve = serve;
+}
+
+JT_IBB::~JT_IBB()
+{
+ delete d;
+}
+
+void JT_IBB::request(const Jid &to, const QDomElement &comment)
+{
+ d->mode = ModeRequest;
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(comment);
+ d->iq = iq;
+}
+
+void JT_IBB::sendData(const Jid &to, const QString &streamid, const QByteArray &a, bool close)
+{
+ d->mode = ModeSendData;
+ QDomElement iq;
+ d->to = to;
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "streamid", streamid));
+ if(!a.isEmpty())
+ query.appendChild(textTag(doc(), "data", Base64::arrayToString(a)));
+ if(close) {
+ QDomElement c = doc()->createElement("close");
+ query.appendChild(c);
+ }
+ d->iq = iq;
+}
+
+void JT_IBB::respondSuccess(const Jid &to, const QString &id, const QString &streamid)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/ibb");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "streamid", streamid));
+ send(iq);
+}
+
+void JT_IBB::respondError(const Jid &to, const QString &id, int code, const QString &str)
+{
+ QDomElement iq = createIQ(doc(), "error", to.full(), id);
+ QDomElement err = textTag(doc(), "error", str);
+ err.setAttribute("code", QString::number(code));
+ iq.appendChild(err);
+ send(iq);
+}
+
+void JT_IBB::respondAck(const Jid &to, const QString &id)
+{
+ QDomElement iq = createIQ(doc(), "result", to.full(), id);
+ send(iq);
+}
+
+void JT_IBB::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_IBB::take(const QDomElement &e)
+{
+ if(d->serve) {
+ // must be an iq-set tag
+ if(e.tagName() != "iq" || e.attribute("type") != "set")
+ return false;
+
+ if(queryNS(e) != "http://jabber.org/protocol/ibb")
+ return false;
+
+ Jid from(e.attribute("from"));
+ QString id = e.attribute("id");
+ QDomElement q = queryTag(e);
+
+ bool found;
+ QDomElement s = findSubTag(q, "streamid", &found);
+ if(!found) {
+ QDomElement comment = findSubTag(q, "comment", &found);
+ incomingRequest(from, id, comment);
+ }
+ else {
+ QString sid = tagContent(s);
+ QByteArray a;
+ bool close = false;
+ s = findSubTag(q, "data", &found);
+ if(found)
+ a = Base64::stringToArray(tagContent(s));
+ s = findSubTag(q, "close", &found);
+ if(found)
+ close = true;
+
+ incomingData(from, sid, id, a, close);
+ }
+
+ return true;
+ }
+ else {
+ Jid from(e.attribute("from"));
+ if(e.attribute("id") != id() || !d->to.compare(from))
+ return false;
+
+ if(e.attribute("type") == "result") {
+ QDomElement q = queryTag(e);
+
+ // request
+ if(d->mode == ModeRequest) {
+ bool found;
+ QDomElement s = findSubTag(q, "streamid", &found);
+ if(found)
+ d->streamid = tagContent(s);
+ else
+ d->streamid = "";
+ setSuccess();
+ }
+ // sendData
+ else {
+ // thank you for the ack, kind sir
+ setSuccess();
+ }
+ }
+ else {
+ setError(e);
+ }
+
+ return true;
+ }
+}
+
+QString JT_IBB::streamid() const
+{
+ return d->streamid;
+}
+
+Jid JT_IBB::jid() const
+{
+ return d->to;
+}
+
+int JT_IBB::mode() const
+{
+ return d->mode;
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h
new file mode 100644
index 00000000..73de4ac4
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_ibb.h
@@ -0,0 +1,145 @@
+/*
+ * ibb.h - Inband bytestream
+ * Copyright (C) 2001, 2002 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 JABBER_IBB_H
+#define JABBER_IBB_H
+
+#include<qobject.h>
+#include<qdom.h>
+#include<qstring.h>
+#include<qptrlist.h>
+#include"bytestream.h"
+#include"im.h"
+
+namespace XMPP
+{
+ class Client;
+ class IBBManager;
+
+ // this is an IBB connection. use it much like a qsocket
+ class IBBConnection : public ByteStream
+ {
+ Q_OBJECT
+ public:
+ enum { ErrRequest, ErrData };
+ enum { Idle, Requesting, WaitingForAccept, Active };
+ IBBConnection(IBBManager *);
+ ~IBBConnection();
+
+ void connectToJid(const Jid &peer, const QDomElement &comment);
+ void accept();
+ void close();
+
+ int state() const;
+ Jid peer() const;
+ QString streamid() const;
+ QDomElement comment() const;
+
+ bool isOpen() const;
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ signals:
+ void connected();
+
+ private slots:
+ void ibb_finished();
+ void trySend();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+
+ friend class IBBManager;
+ void waitForAccept(const Jid &peer, const QString &sid, const QDomElement &comment, const QString &iq_id);
+ void takeIncomingData(const QByteArray &, bool close);
+ };
+
+ typedef QPtrList<IBBConnection> IBBConnectionList;
+ typedef QPtrListIterator<IBBConnection> IBBConnectionListIt;
+ class IBBManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ IBBManager(Client *);
+ ~IBBManager();
+
+ Client *client() const;
+
+ IBBConnection *takeIncoming();
+
+ signals:
+ void incomingReady();
+
+ private slots:
+ void ibb_incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+ void ibb_incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+ private:
+ class Private;
+ Private *d;
+
+ QString genKey() const;
+
+ friend class IBBConnection;
+ IBBConnection *findConnection(const QString &sid, const Jid &peer="") const;
+ QString genUniqueKey() const;
+ void link(IBBConnection *);
+ void unlink(IBBConnection *);
+ void doAccept(IBBConnection *c, const QString &id);
+ void doReject(IBBConnection *c, const QString &id, int, const QString &);
+ };
+
+ class JT_IBB : public Task
+ {
+ Q_OBJECT
+ public:
+ enum { ModeRequest, ModeSendData };
+ JT_IBB(Task *, bool serve=false);
+ ~JT_IBB();
+
+ void request(const Jid &, const QDomElement &comment);
+ void sendData(const Jid &, const QString &streamid, const QByteArray &data, bool close);
+ void respondSuccess(const Jid &, const QString &id, const QString &streamid);
+ void respondError(const Jid &, const QString &id, int code, const QString &str);
+ void respondAck(const Jid &to, const QString &id);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ QString streamid() const;
+ Jid jid() const;
+ int mode() const;
+
+ signals:
+ void incomingRequest(const Jid &from, const QString &id, const QDomElement &);
+ void incomingData(const Jid &from, const QString &streamid, const QString &id, const QByteArray &data, bool close);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp
new file mode 100644
index 00000000..eb140880
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.cpp
@@ -0,0 +1,318 @@
+/*
+ * jidlink.cpp - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002 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"xmpp_jidlink.h"
+
+#include<qdom.h>
+#include<qtimer.h>
+#include"im.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// JidLink
+//----------------------------------------------------------------------------
+class JidLink::Private
+{
+public:
+ Client *client;
+ ByteStream *bs;
+ int type;
+ int state;
+ Jid peer;
+};
+
+JidLink::JidLink(Client *client)
+:QObject(client->jidLinkManager())
+{
+ d = new Private;
+ d->client = client;
+ d->bs = 0;
+
+ reset();
+}
+
+JidLink::~JidLink()
+{
+ reset(true);
+
+ delete d;
+}
+
+void JidLink::reset(bool clear)
+{
+ d->type = None;
+ d->state = Idle;
+
+ if(d->bs) {
+ unlink();
+ d->bs->close();
+ if(clear) {
+ delete d->bs;
+ d->bs = 0;
+ }
+ }
+}
+
+void JidLink::connectToJid(const Jid &jid, int type, const QDomElement &comment)
+{
+ reset(true);
+ if(type == DTCP)
+ d->bs = d->client->s5bManager()->createConnection();
+ else if(type == IBB)
+ d->bs = new IBBConnection(d->client->ibbManager());
+ else
+ return;
+
+ d->type = type;
+ d->peer = jid;
+ d->state = Connecting;
+
+ link();
+
+ if(type == DTCP) {
+ S5BConnection *c = (S5BConnection *)d->bs;
+ status(StatDTCPRequesting);
+ c->connectToJid(jid, d->client->s5bManager()->genUniqueSID(jid));
+ }
+ else {
+ IBBConnection *c = (IBBConnection *)d->bs;
+ status(StatIBBRequesting);
+ c->connectToJid(jid, comment);
+ }
+}
+
+void JidLink::link()
+{
+ if(d->type == DTCP) {
+ S5BConnection *c = (S5BConnection *)d->bs;
+ connect(c, SIGNAL(connected()), SLOT(dtcp_connected()));
+ connect(c, SIGNAL(accepted()), SLOT(dtcp_accepted()));
+ }
+ else {
+ IBBConnection *c = (IBBConnection *)d->bs;
+ connect(c, SIGNAL(connected()), SLOT(ibb_connected()));
+ }
+
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+}
+
+void JidLink::unlink()
+{
+ d->bs->disconnect(this);
+}
+
+void JidLink::accept()
+{
+ if(d->state != WaitingForAccept)
+ return;
+
+ QTimer::singleShot(0, this, SLOT(doRealAccept()));
+}
+
+void JidLink::doRealAccept()
+{
+ if(d->type == DTCP) {
+ ((S5BConnection *)d->bs)->accept();
+ d->state = Connecting;
+ dtcp_accepted();
+ }
+ else {
+ ((IBBConnection *)d->bs)->accept();
+ d->state = Active;
+ connected();
+ }
+}
+
+bool JidLink::setStream(ByteStream *bs)
+{
+ reset(true);
+ int type = None;
+ if(bs->inherits("XMPP::S5BConnection"))
+ type = DTCP;
+ else if(bs->inherits("XMPP::IBBConnection"))
+ type = IBB;
+
+ if(type == None)
+ return false;
+
+ d->type = type;
+ d->bs = bs;
+ d->state = WaitingForAccept;
+
+ link();
+
+ if(d->type == DTCP)
+ d->peer = ((S5BConnection *)d->bs)->peer();
+ else
+ d->peer = ((IBBConnection *)d->bs)->peer();
+
+ return true;
+}
+
+int JidLink::type() const
+{
+ return d->type;
+}
+
+Jid JidLink::peer() const
+{
+ return d->peer;
+}
+
+int JidLink::state() const
+{
+ return d->state;
+}
+
+bool JidLink::isOpen() const
+{
+ if(d->state == Active)
+ return true;
+ else
+ return false;
+}
+
+void JidLink::close()
+{
+ if(d->state == Idle)
+ return;
+ reset();
+}
+
+void JidLink::write(const QByteArray &a)
+{
+ if(d->state == Active)
+ d->bs->write(a);
+}
+
+QByteArray JidLink::read(int bytes)
+{
+ if(d->bs)
+ return d->bs->read(bytes);
+ else
+ return QByteArray();
+}
+
+int JidLink::bytesAvailable() const
+{
+ if(d->bs)
+ return d->bs->bytesAvailable();
+ else
+ return 0;
+}
+
+int JidLink::bytesToWrite() const
+{
+ if(d->state == Active)
+ return d->bs->bytesToWrite();
+ else
+ return 0;
+}
+
+void JidLink::dtcp_accepted()
+{
+ status(StatDTCPAccepted);
+}
+
+void JidLink::dtcp_connected()
+{
+ d->state = Active;
+ status(StatDTCPConnected);
+ connected();
+}
+
+void JidLink::ibb_connected()
+{
+ d->state = Active;
+ status(StatIBBConnected);
+ connected();
+}
+
+void JidLink::bs_connectionClosed()
+{
+ reset();
+ connectionClosed();
+}
+
+void JidLink::bs_error(int)
+{
+ reset();
+ error(ErrConnect);
+}
+
+void JidLink::bs_readyRead()
+{
+ readyRead();
+}
+
+void JidLink::bs_bytesWritten(int x)
+{
+ bytesWritten(x);
+}
+
+
+//----------------------------------------------------------------------------
+// JidLinkManager
+//----------------------------------------------------------------------------
+class JidLinkManager::Private
+{
+public:
+ Private() {}
+
+ Client *client;
+ QPtrList<JidLink> incomingList;
+};
+
+JidLinkManager::JidLinkManager(Client *par)
+:QObject(par)
+{
+ d = new Private;
+ d->client = par;
+}
+
+JidLinkManager::~JidLinkManager()
+{
+ d->incomingList.setAutoDelete(true);
+ d->incomingList.clear();
+ delete d;
+}
+
+JidLink *JidLinkManager::takeIncoming()
+{
+ if(d->incomingList.isEmpty())
+ return 0;
+
+ JidLink *j = d->incomingList.getFirst();
+ d->incomingList.removeRef(j);
+ return j;
+}
+
+void JidLinkManager::insertStream(ByteStream *bs)
+{
+ JidLink *j = new JidLink(d->client);
+ if(j->setStream(bs))
+ d->incomingList.append(j);
+}
diff --git a/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h
new file mode 100644
index 00000000..955fce50
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/jabber/xmpp_jidlink.h
@@ -0,0 +1,114 @@
+/*
+ * jidlink.h - establish a link between Jabber IDs
+ * Copyright (C) 2001, 2002 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
+ *
+ */
+
+/*
+ NOTE: this is not to be confused with JEP-0041
+*/
+
+#ifndef JABBER_JIDLINK_H
+#define JABBER_JIDLINK_H
+
+#include<qobject.h>
+#include<qstring.h>
+#include"xmpp.h"
+
+class ByteStream;
+
+namespace XMPP
+{
+ class Client;
+
+ class JidLink : public QObject
+ {
+ Q_OBJECT
+ public:
+ enum { None, DTCP, IBB };
+ enum { Idle, Connecting, WaitingForAccept, Active };
+ enum { ErrConnect, ErrStream };
+ enum { StatDTCPRequesting, StatDTCPAccepted, StatDTCPConnected, StatIBBRequesting, StatIBBConnected };
+ JidLink(Client *client);
+ ~JidLink();
+
+ void connectToJid(const Jid &jid, int type, const QDomElement &comment);
+ void accept();
+
+ int type() const;
+ Jid peer() const;
+ int state() const;
+
+ bool isOpen() const;
+ void close();
+ void write(const QByteArray &);
+ QByteArray read(int bytes=0);
+ int bytesAvailable() const;
+ int bytesToWrite() const;
+
+ signals:
+ void connected();
+ void connectionClosed();
+ void readyRead();
+ void bytesWritten(int);
+ void error(int);
+ void status(int);
+
+ private slots:
+ void dtcp_connected();
+ void dtcp_accepted();
+ void ibb_connected();
+
+ void bs_connectionClosed();
+ void bs_error(int);
+ void bs_readyRead();
+ void bs_bytesWritten(int);
+
+ void doRealAccept();
+
+ private:
+ class Private;
+ Private *d;
+
+ void reset(bool clear=false);
+
+ void link();
+ void unlink();
+
+ friend class JidLinkManager;
+ bool setStream(ByteStream *);
+ };
+
+ // the job of JidLinkManager is to keep track of streams and properly shut them down
+ class JidLinkManager : public QObject
+ {
+ Q_OBJECT
+ public:
+ JidLinkManager(Client *);
+ ~JidLinkManager();
+
+ JidLink *takeIncoming();
+
+ void insertStream(ByteStream *);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am b/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am
new file mode 100644
index 00000000..f35b1c68
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/Makefile.am
@@ -0,0 +1,30 @@
+# The only Q_OBJECT lines are in securestream.{h,cpp} and we deal with them below.
+# Give metasources a file with no Q_OBJECT line to stop unsermake assuming we want METASOURCES = AUTO
+METASOURCES = ignore_this_warning.moc
+
+noinst_LTLIBRARIES = libiris_xmpp_core.la
+AM_CPPFLAGS = $(IDN_CFLAGS)
+INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../xmpp-core -I$(srcdir)/../xmpp-im -I$(srcdir)/../../cutestuff/util -I$(srcdir)/../../cutestuff/network -I$(srcdir)/../../qca/src $(all_includes)
+
+libiris_xmpp_core_la_CPPFLAGS = $(IDN_CFLAGS)
+libiris_xmpp_core_la_LDFLAGS = $(IDN_LIBS)
+libiris_xmpp_core_la_SOURCES = \
+ connector.cpp \
+ jid.cpp \
+ securestream.cpp \
+ tlshandler.cpp \
+ hash.cpp \
+ protocol.cpp \
+ stream.cpp \
+ xmlprotocol.cpp \
+ parser.cpp \
+ simplesasl.cpp
+
+libiris_xmpp_core_la_COMPILE_FIRST = securestream.moc
+
+CLEANFILES = securestream.moc
+securestream.moc: $(srcdir)/securestream.cpp $(srcdir)/securestream.h
+ ${MOC} $(srcdir)/securestream.h > $@
+ ${MOC} $(srcdir)/securestream.cpp >> $@
+
+KDE_OPTIONS = nofinal
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp
new file mode 100644
index 00000000..8ebc3ee8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/connector.cpp
@@ -0,0 +1,719 @@
+/*
+ * connector.cpp - establish a connection to an XMPP server
+ * 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
+ *
+ */
+
+/*
+ TODO:
+
+ - Test and analyze all possible branches
+
+ XMPP::AdvancedConnector is "good for now." The only real issue is that
+ most of what it provides is just to work around the old Jabber/XMPP 0.9
+ connection behavior. When XMPP 1.0 has taken over the world, we can
+ greatly simplify this class. - Sep 3rd, 2003.
+*/
+
+#include"xmpp.h"
+
+#include<qguardedptr.h>
+#include<qca.h>
+#include"safedelete.h"
+
+#ifdef NO_NDNS
+#include<qdns.h>
+#else
+#include"ndns.h"
+#endif
+
+#include"srvresolver.h"
+#include"bsocket.h"
+#include"httpconnect.h"
+#include"httppoll.h"
+#include"socks.h"
+#include"hash.h"
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// Connector
+//----------------------------------------------------------------------------
+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;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector::Proxy
+//----------------------------------------------------------------------------
+AdvancedConnector::Proxy::Proxy()
+{
+ t = None;
+ v_poll = 30;
+}
+
+AdvancedConnector::Proxy::~Proxy()
+{
+}
+
+int AdvancedConnector::Proxy::type() const
+{
+ return t;
+}
+
+QString AdvancedConnector::Proxy::host() const
+{
+ return v_host;
+}
+
+Q_UINT16 AdvancedConnector::Proxy::port() const
+{
+ return v_port;
+}
+
+QString AdvancedConnector::Proxy::url() const
+{
+ return v_url;
+}
+
+QString AdvancedConnector::Proxy::user() const
+{
+ return v_user;
+}
+
+QString AdvancedConnector::Proxy::pass() const
+{
+ return v_pass;
+}
+
+int AdvancedConnector::Proxy::pollInterval() const
+{
+ return v_poll;
+}
+
+void AdvancedConnector::Proxy::setHttpConnect(const QString &host, Q_UINT16 port)
+{
+ t = HttpConnect;
+ v_host = host;
+ v_port = port;
+}
+
+void AdvancedConnector::Proxy::setHttpPoll(const QString &host, Q_UINT16 port, const QString &url)
+{
+ t = HttpPoll;
+ v_host = host;
+ v_port = port;
+ v_url = url;
+}
+
+void AdvancedConnector::Proxy::setSocks(const QString &host, Q_UINT16 port)
+{
+ t = Socks;
+ v_host = host;
+ v_port = port;
+}
+
+void AdvancedConnector::Proxy::setUserPass(const QString &user, const QString &pass)
+{
+ v_user = user;
+ v_pass = pass;
+}
+
+void AdvancedConnector::Proxy::setPollInterval(int secs)
+{
+ v_poll = secs;
+}
+
+
+//----------------------------------------------------------------------------
+// AdvancedConnector
+//----------------------------------------------------------------------------
+enum { Idle, Connecting, Connected };
+class AdvancedConnector::Private
+{
+public:
+ int mode;
+ ByteStream *bs;
+#ifdef NO_NDNS
+ QDns *qdns;
+#else
+ NDns dns;
+#endif
+ SrvResolver srv;
+
+ QString server;
+ QString opt_host;
+ int opt_port;
+ bool opt_probe, opt_ssl;
+ Proxy proxy;
+
+ QString host;
+ int port;
+ QValueList<QDns::Server> servers;
+ int errorCode;
+
+ bool multi, using_srv;
+ bool will_be_ssl;
+ int probe_mode;
+
+ bool aaaa;
+ SafeDelete sd;
+};
+
+AdvancedConnector::AdvancedConnector(QObject *parent)
+:Connector(parent)
+{
+ d = new Private;
+ d->bs = 0;
+#ifdef NO_NDNS
+ d->qdns = 0;
+#else
+ connect(&d->dns, SIGNAL(resultsReady()), SLOT(dns_done()));
+#endif
+ connect(&d->srv, SIGNAL(resultsReady()), SLOT(srv_done()));
+ d->opt_probe = false;
+ d->opt_ssl = false;
+ cleanup();
+ d->errorCode = 0;
+}
+
+AdvancedConnector::~AdvancedConnector()
+{
+ cleanup();
+ delete d;
+}
+
+void AdvancedConnector::cleanup()
+{
+ d->mode = Idle;
+
+ // stop any dns
+#ifdef NO_NDNS
+ if(d->qdns) {
+ d->qdns->disconnect(this);
+ d->qdns->deleteLater();
+ //d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+ }
+#else
+ if(d->dns.isBusy())
+ d->dns.stop();
+#endif
+ if(d->srv.isBusy())
+ d->srv.stop();
+
+ // destroy the bytestream, if there is one
+ delete d->bs;
+ d->bs = 0;
+
+ d->multi = false;
+ d->using_srv = false;
+ d->will_be_ssl = false;
+ d->probe_mode = -1;
+
+ setUseSSL(false);
+ setPeerAddressNone();
+}
+
+void AdvancedConnector::setProxy(const Proxy &proxy)
+{
+ if(d->mode != Idle)
+ return;
+ d->proxy = proxy;
+}
+
+void AdvancedConnector::setOptHostPort(const QString &host, Q_UINT16 _port)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_host = host;
+ d->opt_port = _port;
+}
+
+void AdvancedConnector::setOptProbe(bool b)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_probe = b;
+}
+
+void AdvancedConnector::setOptSSL(bool b)
+{
+ if(d->mode != Idle)
+ return;
+ d->opt_ssl = b;
+}
+
+void AdvancedConnector::connectToServer(const QString &server)
+{
+ if(d->mode != Idle)
+ return;
+ if(server.isEmpty())
+ return;
+
+ d->errorCode = 0;
+ d->server = server;
+ d->mode = Connecting;
+ d->aaaa = true;
+
+ if(d->proxy.type() == Proxy::HttpPoll) {
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ HttpPoll *s = new HttpPoll;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(syncStarted()), SLOT(http_syncStarted()));
+ connect(s, SIGNAL(syncFinished()), SLOT(http_syncFinished()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->setPollInterval(d->proxy.pollInterval());
+
+ if(d->proxy.host().isEmpty())
+ s->connectToUrl(d->proxy.url());
+ else
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->proxy.url());
+ }
+ else {
+ if(!d->opt_host.isEmpty()) {
+ d->host = d->opt_host;
+ d->port = d->opt_port;
+ do_resolve();
+ }
+ else {
+ d->multi = true;
+
+ QGuardedPtr<QObject> self = this;
+ srvLookup(d->server);
+ if(!self)
+ return;
+
+ d->srv.resolveSrvOnly(d->server, "xmpp-client", "tcp");
+ }
+ }
+}
+
+void AdvancedConnector::changePollInterval(int secs)
+{
+ if(d->bs && (d->bs->inherits("XMPP::HttpPoll") || d->bs->inherits("HttpPoll"))) {
+ HttpPoll *s = static_cast<HttpPoll*>(d->bs);
+ s->setPollInterval(secs);
+ }
+}
+
+ByteStream *AdvancedConnector::stream() const
+{
+ if(d->mode == Connected)
+ return d->bs;
+ else
+ return 0;
+}
+
+void AdvancedConnector::done()
+{
+ cleanup();
+}
+
+int AdvancedConnector::errorCode() const
+{
+ return d->errorCode;
+}
+
+void AdvancedConnector::do_resolve()
+{
+#ifdef NO_NDNS
+ printf("resolving (aaaa=%d)\n", d->aaaa);
+ d->qdns = new QDns;
+ connect(d->qdns, SIGNAL(resultsReady()), SLOT(dns_done()));
+ if(d->aaaa)
+ d->qdns->setRecordType(QDns::Aaaa); // IPv6
+ else
+ d->qdns->setRecordType(QDns::A); // IPv4
+ d->qdns->setLabel(d->host);
+#else
+ d->dns.resolve(d->host);
+#endif
+}
+
+void AdvancedConnector::dns_done()
+{
+ bool failed = false;
+ QHostAddress addr;
+
+#ifdef NO_NDNS
+ //if(!d->qdns)
+ // return;
+
+ // apparently we sometimes get this signal even though the results aren' t ready
+ //if(d->qdns->isWorking())
+ // return;
+
+ //SafeDeleteLock s(&d->sd);
+
+ // grab the address list and destroy the qdns object
+ QValueList<QHostAddress> list = d->qdns->addresses();
+ d->qdns->disconnect(this);
+ d->qdns->deleteLater();
+ //d->sd.deleteLater(d->qdns);
+ d->qdns = 0;
+
+ if(list.isEmpty()) {
+ if(d->aaaa) {
+ d->aaaa = false;
+ do_resolve();
+ return;
+ }
+ //do_resolve();
+ //return;
+ failed = true;
+ }
+ else
+ addr = list.first();
+#else
+ if(d->dns.result() == 0)
+ failed = true;
+ else
+ addr = QHostAddress(d->dns.result());
+#endif
+
+ if(failed) {
+#ifdef XMPP_DEBUG
+ printf("dns1\n");
+#endif
+ // using proxy? then try the unresolved host through the proxy
+ if(d->proxy.type() != Proxy::None) {
+#ifdef XMPP_DEBUG
+ printf("dns1.1\n");
+#endif
+ do_connect();
+ }
+ else if(d->using_srv) {
+#ifdef XMPP_DEBUG
+ printf("dns1.2\n");
+#endif
+ if(d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+ printf("dns1.2.1\n");
+#endif
+ cleanup();
+ d->errorCode = ErrConnectionRefused;
+ error();
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns1.2.2\n");
+#endif
+ tryNextSrv();
+ return;
+ }
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns1.3\n");
+#endif
+ cleanup();
+ d->errorCode = ErrHostNotFound;
+ error();
+ }
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("dns2\n");
+#endif
+ d->host = addr.toString();
+ do_connect();
+ }
+}
+
+void AdvancedConnector::do_connect()
+{
+#ifdef XMPP_DEBUG
+ printf("trying %s:%d\n", d->host.latin1(), d->port);
+#endif
+ int t = d->proxy.type();
+ if(t == Proxy::None) {
+#ifdef XMPP_DEBUG
+ printf("do_connect1\n");
+#endif
+ BSocket *s = new BSocket;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ s->connectToHost(d->host, d->port);
+ }
+ else if(t == Proxy::HttpConnect) {
+#ifdef XMPP_DEBUG
+ printf("do_connect2\n");
+#endif
+ HttpConnect *s = new HttpConnect;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+ }
+ else if(t == Proxy::Socks) {
+#ifdef XMPP_DEBUG
+ printf("do_connect3\n");
+#endif
+ SocksClient *s = new SocksClient;
+ d->bs = s;
+ connect(s, SIGNAL(connected()), SLOT(bs_connected()));
+ connect(s, SIGNAL(error(int)), SLOT(bs_error(int)));
+ if(!d->proxy.user().isEmpty())
+ s->setAuth(d->proxy.user(), d->proxy.pass());
+ s->connectToHost(d->proxy.host(), d->proxy.port(), d->host, d->port);
+ }
+}
+
+void AdvancedConnector::tryNextSrv()
+{
+#ifdef XMPP_DEBUG
+ printf("trying next srv\n");
+#endif
+ d->host = d->servers.first().name;
+ d->port = d->servers.first().port;
+ d->servers.remove(d->servers.begin());
+ do_resolve();
+}
+
+void AdvancedConnector::srv_done()
+{
+ QGuardedPtr<QObject> self = this;
+#ifdef XMPP_DEBUG
+ printf("srv_done1\n");
+#endif
+ d->servers = d->srv.servers();
+ if(d->servers.isEmpty()) {
+ srvResult(false);
+ if(!self)
+ return;
+
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1\n");
+#endif
+ // fall back to A record
+ d->using_srv = false;
+ d->host = d->server;
+ if(d->opt_probe) {
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1.1\n");
+#endif
+ d->probe_mode = 0;
+ d->port = 5223;
+ d->will_be_ssl = true;
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("srv_done1.1.2\n");
+#endif
+ d->probe_mode = 1;
+ d->port = 5222;
+ }
+ do_resolve();
+ return;
+ }
+
+ srvResult(true);
+ if(!self)
+ return;
+
+ d->using_srv = true;
+ tryNextSrv();
+}
+
+void AdvancedConnector::bs_connected()
+{
+ if(d->proxy.type() == Proxy::None) {
+ QHostAddress h = (static_cast<BSocket*>(d->bs))->peerAddress();
+ int p = (static_cast<BSocket*>(d->bs))->peerPort();
+ setPeerAddress(h, p);
+ }
+
+ // only allow ssl override if proxy==poll or host:port
+ if((d->proxy.type() == Proxy::HttpPoll || !d->opt_host.isEmpty()) && d->opt_ssl)
+ setUseSSL(true);
+ else if(d->will_be_ssl)
+ setUseSSL(true);
+
+ d->mode = Connected;
+ connected();
+}
+
+void AdvancedConnector::bs_error(int x)
+{
+ if(d->mode == Connected) {
+ d->errorCode = ErrStream;
+ error();
+ return;
+ }
+
+ bool proxyError = false;
+ int err = ErrConnectionRefused;
+ int t = d->proxy.type();
+
+#ifdef XMPP_DEBUG
+ printf("bse1\n");
+#endif
+
+ // figure out the error
+ if(t == Proxy::None) {
+ if(x == BSocket::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else
+ err = ErrConnectionRefused;
+ }
+ else if(t == Proxy::HttpConnect) {
+ if(x == HttpConnect::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == HttpConnect::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == HttpConnect::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == HttpConnect::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+ else if(t == Proxy::HttpPoll) {
+ if(x == HttpPoll::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == HttpPoll::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == HttpPoll::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == HttpPoll::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+ else if(t == Proxy::Socks) {
+ if(x == SocksClient::ErrConnectionRefused)
+ err = ErrConnectionRefused;
+ else if(x == SocksClient::ErrHostNotFound)
+ err = ErrHostNotFound;
+ else {
+ proxyError = true;
+ if(x == SocksClient::ErrProxyAuth)
+ err = ErrProxyAuth;
+ else if(x == SocksClient::ErrProxyNeg)
+ err = ErrProxyNeg;
+ else
+ err = ErrProxyConnect;
+ }
+ }
+
+ // no-multi or proxy error means we quit
+ if(!d->multi || proxyError) {
+ cleanup();
+ d->errorCode = err;
+ error();
+ return;
+ }
+
+ if(d->using_srv && !d->servers.isEmpty()) {
+#ifdef XMPP_DEBUG
+ printf("bse1.1\n");
+#endif
+ tryNextSrv();
+ }
+ else if(!d->using_srv && d->opt_probe && d->probe_mode == 0) {
+#ifdef XMPP_DEBUG
+ printf("bse1.2\n");
+#endif
+ d->probe_mode = 1;
+ d->port = 5222;
+ d->will_be_ssl = false;
+ do_connect();
+ }
+ else {
+#ifdef XMPP_DEBUG
+ printf("bse1.3\n");
+#endif
+ cleanup();
+ d->errorCode = ErrConnectionRefused;
+ error();
+ }
+}
+
+void AdvancedConnector::http_syncStarted()
+{
+ httpSyncStarted();
+}
+
+void AdvancedConnector::http_syncFinished()
+{
+ httpSyncFinished();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp
new file mode 100644
index 00000000..4d7f9e41
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.cpp
@@ -0,0 +1,670 @@
+/*
+ * hash.cpp - hashing functions for SHA1 and MD5
+ * 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"hash.h"
+
+namespace XMPP
+{
+
+static bool bigEndian;
+static bool haveEndian = false;
+
+static void ensureEndian()
+{
+ if(!haveEndian) {
+ haveEndian = true;
+ int wordSize;
+ qSysInfo(&wordSize, &bigEndian);
+ }
+}
+
+//----------------------------------------------------------------------------
+// MD5
+//----------------------------------------------------------------------------
+
+/* NOTE: the following code was modified to not need BYTE_ORDER -- Justin */
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id$ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef Q_UINT8 md5_byte_t; /* 8-bit byte */
+typedef Q_UINT32 md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ {
+ if(bigEndian)
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+ X = xbuf; /* (dynamic only) */
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+ else /* dynamic big-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+
+//----------------------------------------------------------------------------
+// SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+//----------------------------------------------------------------------------
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+struct SHA1_CONTEXT
+{
+ Q_UINT32 state[5];
+ Q_UINT32 count[2];
+ unsigned char buffer[64];
+};
+
+typedef union {
+ unsigned char c[64];
+ Q_UINT32 l[16];
+} CHAR64LONG16;
+
+class SHA1Context : public QCA_HashContext
+{
+public:
+ SHA1_CONTEXT _context;
+ CHAR64LONG16* block;
+
+ SHA1Context()
+ {
+ reset();
+ }
+
+ QCA_HashContext *clone()
+ {
+ return new SHA1Context(*this);
+ }
+
+ void reset()
+ {
+ sha1_init(&_context);
+ }
+
+ void update(const char *in, unsigned int len)
+ {
+ sha1_update(&_context, (unsigned char *)in, (unsigned int)len);
+ }
+
+ void final(QByteArray *out)
+ {
+ QByteArray b(20);
+ sha1_final((unsigned char *)b.data(), &_context);
+ *out = b;
+ }
+
+ unsigned long blk0(Q_UINT32 i)
+ {
+ if(bigEndian)
+ return block->l[i];
+ else
+ return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+ }
+
+ // Hash a single 512-bit block. This is the core of the algorithm.
+ void transform(Q_UINT32 state[5], unsigned char buffer[64])
+ {
+ Q_UINT32 a, b, c, d, e;
+
+ block = (CHAR64LONG16*)buffer;
+
+ // Copy context->state[] to working vars
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ // Add the working vars back into context.state[]
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ // Wipe variables
+ a = b = c = d = e = 0;
+ }
+
+ // SHA1Init - Initialize new context
+ void sha1_init(SHA1_CONTEXT* context)
+ {
+ // SHA1 initialization constants
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+ }
+
+ // Run your data through this
+ void sha1_update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+ {
+ Q_UINT32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+
+ context->count[1] += (len >> 29);
+
+ if((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+ }
+
+ // Add padding and return the message digest
+ void sha1_final(unsigned char digest[20], SHA1_CONTEXT* context)
+ {
+ Q_UINT32 i, j;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); // Endian independent
+ }
+ sha1_update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ sha1_update(context, (unsigned char *)"\0", 1);
+ }
+ sha1_update(context, finalcount, 8); // Should cause a transform()
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ // Wipe variables
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+ }
+};
+
+class MD5Context : public QCA_HashContext
+{
+public:
+ MD5Context()
+ {
+ reset();
+ }
+
+ QCA_HashContext *clone()
+ {
+ return new MD5Context(*this);
+ }
+
+ void reset()
+ {
+ md5_init(&md5);
+ }
+
+ void update(const char *in, unsigned int len)
+ {
+ md5_append(&md5, (const md5_byte_t *)in, len);
+ }
+
+ void final(QByteArray *out)
+ {
+ QByteArray b(16);
+ md5_finish(&md5, (md5_byte_t *)b.data());
+ *out = b;
+ }
+
+ md5_state_t md5;
+};
+
+class HashProvider : public QCAProvider
+{
+public:
+ HashProvider() {}
+ ~HashProvider() {}
+
+ void init()
+ {
+ ensureEndian();
+ }
+
+ int qcaVersion() const
+ {
+ return QCA_PLUGIN_VERSION;
+ }
+
+ int capabilities() const
+ {
+ return (QCA::CAP_SHA1 | QCA::CAP_MD5);
+ }
+
+ void *context(int cap)
+ {
+ if(cap == QCA::CAP_SHA1)
+ return new SHA1Context;
+ if(cap == QCA::CAP_MD5)
+ return new MD5Context;
+ return 0;
+ }
+};
+
+QCAProvider *createProviderHash()
+{
+ return (new HashProvider);
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h
new file mode 100644
index 00000000..a4d2eea8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/hash.h
@@ -0,0 +1,31 @@
+/*
+ * hash.h - hashing functions for SHA1 and MD5
+ * 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 HASH_H
+#define HASH_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+ QCAProvider *createProviderHash();
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp
new file mode 100644
index 00000000..29932513
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/jid.cpp
@@ -0,0 +1,409 @@
+/*
+ * jid.cpp - class for verifying and manipulating Jabber IDs
+ * 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"xmpp.h"
+
+#include<qdict.h>
+#include<stringprep.h>
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// StringPrepCache
+//----------------------------------------------------------------------------
+class StringPrepCache
+{
+public:
+ static bool nameprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->nameprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+ {
+ that->nameprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->nameprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+ static bool nodeprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->nodeprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+ {
+ that->nodeprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->nodeprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+ static bool resourceprep(const QString &in, int maxbytes, QString *out)
+ {
+ if(in.isEmpty())
+ {
+ if(out)
+ *out = QString();
+ return true;
+ }
+
+ StringPrepCache *that = get_instance();
+
+ Result *r = that->resourceprep_table.find(in);
+ if(r)
+ {
+ if(!r->norm)
+ return false;
+ if(out)
+ *out = *(r->norm);
+ return true;
+ }
+
+ QCString cs = in.utf8();
+ cs.resize(maxbytes);
+ if(stringprep(cs.data(), maxbytes, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+ {
+ that->resourceprep_table.insert(in, new Result);
+ return false;
+ }
+
+ QString norm = QString::fromUtf8(cs);
+ that->resourceprep_table.insert(in, new Result(norm));
+ if(out)
+ *out = norm;
+ return true;
+ }
+
+private:
+ class Result
+ {
+ public:
+ QString *norm;
+
+ Result() : norm(0)
+ {
+ }
+
+ Result(const QString &s) : norm(new QString(s))
+ {
+ }
+
+ ~Result()
+ {
+ delete norm;
+ }
+ };
+
+ QDict<Result> nameprep_table;
+ QDict<Result> nodeprep_table;
+ QDict<Result> resourceprep_table;
+
+ static StringPrepCache *instance;
+
+ static StringPrepCache *get_instance()
+ {
+ if(!instance)
+ instance = new StringPrepCache;
+ return instance;
+ }
+
+ StringPrepCache()
+ {
+ nameprep_table.setAutoDelete(true);
+ nodeprep_table.setAutoDelete(true);
+ resourceprep_table.setAutoDelete(true);
+ }
+};
+
+StringPrepCache *StringPrepCache::instance = 0;
+
+//----------------------------------------------------------------------------
+// Jid
+//----------------------------------------------------------------------------
+Jid::Jid()
+{
+ valid = false;
+}
+
+Jid::~Jid()
+{
+}
+
+Jid::Jid(const QString &s)
+{
+ set(s);
+}
+
+Jid::Jid(const char *s)
+{
+ set(QString(s));
+}
+
+Jid & Jid::operator=(const QString &s)
+{
+ set(s);
+ return *this;
+}
+
+Jid & Jid::operator=(const char *s)
+{
+ set(QString(s));
+ return *this;
+}
+
+void Jid::reset()
+{
+ f = QString();
+ b = QString();
+ d = QString();
+ n = QString();
+ r = QString();
+ valid = false;
+}
+
+void Jid::update()
+{
+ // build 'bare' and 'full' jids
+ if(n.isEmpty())
+ b = d;
+ else
+ b = n + '@' + d;
+
+ b=b.lower(); // JID are not case sensitive
+
+ if(r.isEmpty())
+ f = b;
+ else
+ f = b + '/' + r;
+ if(f.isEmpty())
+ valid = false;
+}
+
+void Jid::set(const QString &s)
+{
+ QString rest, domain, node, resource;
+ QString norm_domain, norm_node, norm_resource;
+ int x = s.find('/');
+ if(x != -1) {
+ rest = s.mid(0, x);
+ resource = s.mid(x+1);
+ }
+ else {
+ rest = s;
+ resource = QString();
+ }
+ if(!validResource(resource, &norm_resource)) {
+ reset();
+ return;
+ }
+
+ x = rest.find('@');
+ if(x != -1) {
+ node = rest.mid(0, x);
+ domain = rest.mid(x+1);
+ }
+ else {
+ node = QString();
+ domain = rest;
+ }
+ if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node)) {
+ reset();
+ return;
+ }
+
+ valid = true;
+ d = norm_domain;
+ n = norm_node;
+ r = norm_resource;
+ update();
+}
+
+void Jid::set(const QString &domain, const QString &node, const QString &resource)
+{
+ QString norm_domain, norm_node, norm_resource;
+ if(!validDomain(domain, &norm_domain) || !validNode(node, &norm_node) || !validResource(resource, &norm_resource)) {
+ reset();
+ return;
+ }
+ valid = true;
+ d = norm_domain;
+ n = norm_node;
+ r = norm_resource;
+ update();
+}
+
+void Jid::setDomain(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validDomain(s, &norm)) {
+ reset();
+ return;
+ }
+ d = norm;
+ update();
+}
+
+void Jid::setNode(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validNode(s, &norm)) {
+ reset();
+ return;
+ }
+ n = norm;
+ update();
+}
+
+void Jid::setResource(const QString &s)
+{
+ if(!valid)
+ return;
+ QString norm;
+ if(!validResource(s, &norm)) {
+ reset();
+ return;
+ }
+ r = norm;
+ update();
+}
+
+Jid Jid::withNode(const QString &s) const
+{
+ Jid j = *this;
+ j.setNode(s);
+ return j;
+}
+
+Jid Jid::withResource(const QString &s) const
+{
+ Jid j = *this;
+ j.setResource(s);
+ return j;
+}
+
+bool Jid::isValid() const
+{
+ return valid;
+}
+
+bool Jid::isEmpty() const
+{
+ return f.isEmpty();
+}
+
+bool Jid::compare(const Jid &a, bool compareRes) const
+{
+ // only compare valid jids
+ if(!valid || !a.valid)
+ return false;
+
+ if(compareRes ? (f != a.f) : (b != a.b))
+ return false;
+
+ return true;
+}
+
+bool Jid::validDomain(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_nameprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::nameprep(s, 1024, norm);
+}
+
+bool Jid::validNode(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_nodeprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::nodeprep(s, 1024, norm);
+}
+
+bool Jid::validResource(const QString &s, QString *norm)
+{
+ /*QCString cs = s.utf8();
+ cs.resize(1024);
+ if(stringprep(cs.data(), 1024, (Stringprep_profile_flags)0, stringprep_xmpp_resourceprep) != 0)
+ return false;
+ if(norm)
+ *norm = QString::fromUtf8(cs);
+ return true;*/
+ return StringPrepCache::resourceprep(s, 1024, norm);
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp
new file mode 100644
index 00000000..e1a64532
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.cpp
@@ -0,0 +1,798 @@
+/*
+ * parser.cpp - parse an XMPP "document"
+ * 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
+ *
+ */
+
+/*
+ TODO:
+
+ For XMPP::Parser to be "perfect", some things must be solved/changed in the
+ Qt library:
+
+ - Fix weird QDomElement::haveAttributeNS() bug (patch submitted to
+ Trolltech on Aug 31st, 2003).
+ - Fix weird behavior in QXmlSimpleReader of reporting endElement() when
+ the '/' character of a self-closing tag is reached, instead of when
+ the final '>' is reached.
+ - Fix incremental parsing bugs in QXmlSimpleReader. At the moment, the
+ only bug I've found is related to attribute parsing, but there might
+ be more (search for '###' in $QTDIR/src/xml/qxml.cpp).
+
+ We have workarounds for all of the above problems in the code below.
+
+ - Deal with the <?xml?> processing instruction as an event type, so that we
+ can feed it back to the application properly. Right now it is completely
+ untrackable and is simply tacked into the first event's actualString. We
+ can't easily do this because QXmlSimpleReader eats an extra byte beyond
+ the processing instruction before reporting it.
+
+ - Make QXmlInputSource capable of accepting data incrementally, to ensure
+ proper text encoding detection and processing over a network. This is
+ technically not a bug, as we have our own subclass below to do it, but
+ it would be nice if Qt had this already.
+*/
+
+#include"parser.h"
+
+#include<qtextcodec.h>
+#include<qptrlist.h>
+#include<string.h>
+
+using namespace XMPP;
+
+static bool qt_bug_check = false;
+static bool qt_bug_have;
+
+//----------------------------------------------------------------------------
+// StreamInput
+//----------------------------------------------------------------------------
+class StreamInput : public QXmlInputSource
+{
+public:
+ StreamInput()
+ {
+ dec = 0;
+ reset();
+ }
+
+ ~StreamInput()
+ {
+ delete dec;
+ }
+
+ void reset()
+ {
+ delete dec;
+ dec = 0;
+ in.resize(0);
+ out = "";
+ at = 0;
+ paused = false;
+ mightChangeEncoding = true;
+ checkBad = true;
+ last = QChar();
+ v_encoding = "";
+ resetLastData();
+ }
+
+ void resetLastData()
+ {
+ last_string = "";
+ }
+
+ QString lastString() const
+ {
+ return last_string;
+ }
+
+ void appendData(const QByteArray &a)
+ {
+ int oldsize = in.size();
+ in.resize(oldsize + a.size());
+ memcpy(in.data() + oldsize, a.data(), a.size());
+ processBuf();
+ }
+
+ QChar lastRead()
+ {
+ return last;
+ }
+
+ QChar next()
+ {
+ if(paused)
+ return EndOfData;
+ else
+ return readNext();
+ }
+
+ // NOTE: setting 'peek' to true allows the same char to be read again,
+ // however this still advances the internal byte processing.
+ QChar readNext(bool peek=false)
+ {
+ QChar c;
+ if(mightChangeEncoding)
+ c = EndOfData;
+ else {
+ if(out.isEmpty()) {
+ QString s;
+ if(!tryExtractPart(&s))
+ c = EndOfData;
+ else {
+ out = s;
+ c = out[0];
+ }
+ }
+ else
+ c = out[0];
+ if(!peek)
+ out.remove(0, 1);
+ }
+ if(c == EndOfData) {
+#ifdef XMPP_PARSER_DEBUG
+ printf("next() = EOD\n");
+#endif
+ }
+ else {
+#ifdef XMPP_PARSER_DEBUG
+ printf("next() = [%c]\n", c.latin1());
+#endif
+ last = c;
+ }
+
+ return c;
+ }
+
+ QByteArray unprocessed() const
+ {
+ QByteArray a(in.size() - at);
+ memcpy(a.data(), in.data() + at, a.size());
+ return a;
+ }
+
+ void pause(bool b)
+ {
+ paused = b;
+ }
+
+ bool isPaused()
+ {
+ return paused;
+ }
+
+ QString encoding() const
+ {
+ return v_encoding;
+ }
+
+private:
+ QTextDecoder *dec;
+ QByteArray in;
+ QString out;
+ int at;
+ bool paused;
+ bool mightChangeEncoding;
+ QChar last;
+ QString v_encoding;
+ QString last_string;
+ bool checkBad;
+
+ void processBuf()
+ {
+#ifdef XMPP_PARSER_DEBUG
+ printf("processing. size=%d, at=%d\n", in.size(), at);
+#endif
+ if(!dec) {
+ QTextCodec *codec = 0;
+ uchar *p = (uchar *)in.data() + at;
+ int size = in.size() - at;
+
+ // do we have enough information to determine the encoding?
+ if(size == 0)
+ return;
+ bool utf16 = false;
+ if(p[0] == 0xfe || p[0] == 0xff) {
+ // probably going to be a UTF-16 byte order mark
+ if(size < 2)
+ return;
+ if((p[0] == 0xfe && p[1] == 0xff) || (p[0] == 0xff && p[1] == 0xfe)) {
+ // ok it is UTF-16
+ utf16 = true;
+ }
+ }
+ if(utf16)
+ codec = QTextCodec::codecForMib(1000); // UTF-16
+ else
+ codec = QTextCodec::codecForMib(106); // UTF-8
+
+ v_encoding = codec->name();
+ dec = codec->makeDecoder();
+
+ // for utf16, put in the byte order mark
+ if(utf16) {
+ out += dec->toUnicode((const char *)p, 2);
+ at += 2;
+ }
+ }
+
+ if(mightChangeEncoding) {
+ while(1) {
+ int n = out.find('<');
+ if(n != -1) {
+ // we need a closing bracket
+ int n2 = out.find('>', n);
+ if(n2 != -1) {
+ ++n2;
+ QString h = out.mid(n, n2-n);
+ QString enc = processXmlHeader(h);
+ QTextCodec *codec = 0;
+ if(!enc.isEmpty())
+ codec = QTextCodec::codecForName(enc.latin1());
+
+ // changing codecs
+ if(codec) {
+ v_encoding = codec->name();
+ delete dec;
+ dec = codec->makeDecoder();
+ }
+ mightChangeEncoding = false;
+ out.truncate(0);
+ at = 0;
+ resetLastData();
+ break;
+ }
+ }
+ QString s;
+ if(!tryExtractPart(&s))
+ break;
+ if(checkBad && checkForBadChars(s)) {
+ // go to the parser
+ mightChangeEncoding = false;
+ out.truncate(0);
+ at = 0;
+ resetLastData();
+ break;
+ }
+ out += s;
+ }
+ }
+ }
+
+ QString processXmlHeader(const QString &h)
+ {
+ if(h.left(5) != "<?xml")
+ return "";
+
+ int endPos = h.find(">");
+ int startPos = h.find("encoding");
+ if(startPos < endPos && startPos != -1) {
+ QString encoding;
+ do {
+ startPos++;
+ if(startPos > endPos) {
+ return "";
+ }
+ } while(h[startPos] != '"' && h[startPos] != '\'');
+ startPos++;
+ while(h[startPos] != '"' && h[startPos] != '\'') {
+ encoding += h[startPos];
+ startPos++;
+ if(startPos > endPos) {
+ return "";
+ }
+ }
+ return encoding;
+ }
+ else
+ return "";
+ }
+
+ bool tryExtractPart(QString *s)
+ {
+ int size = in.size() - at;
+ if(size == 0)
+ return false;
+ uchar *p = (uchar *)in.data() + at;
+ QString nextChars;
+ while(1) {
+ nextChars = dec->toUnicode((const char *)p, 1);
+ ++p;
+ ++at;
+ if(!nextChars.isEmpty())
+ break;
+ if(at == (int)in.size())
+ return false;
+ }
+ last_string += nextChars;
+ *s = nextChars;
+
+ // free processed data?
+ if(at >= 1024) {
+ char *p = in.data();
+ int size = in.size() - at;
+ memmove(p, p + at, size);
+ in.resize(size);
+ at = 0;
+ }
+
+ return true;
+ }
+
+ bool checkForBadChars(const QString &s)
+ {
+ int len = s.find('<');
+ if(len == -1)
+ len = s.length();
+ else
+ checkBad = false;
+ for(int n = 0; n < len; ++n) {
+ if(!s.at(n).isSpace())
+ return true;
+ }
+ return false;
+ }
+};
+
+
+//----------------------------------------------------------------------------
+// ParserHandler
+//----------------------------------------------------------------------------
+namespace XMPP
+{
+ class ParserHandler : public QXmlDefaultHandler
+ {
+ public:
+ ParserHandler(StreamInput *_in, QDomDocument *_doc)
+ {
+ in = _in;
+ doc = _doc;
+ needMore = false;
+ }
+
+ ~ParserHandler()
+ {
+ eventList.setAutoDelete(true);
+ eventList.clear();
+ }
+
+ bool startDocument()
+ {
+ depth = 0;
+ return true;
+ }
+
+ bool endDocument()
+ {
+ return true;
+ }
+
+ bool startPrefixMapping(const QString &prefix, const QString &uri)
+ {
+ if(depth == 0) {
+ nsnames += prefix;
+ nsvalues += uri;
+ }
+ return true;
+ }
+
+ bool startElement(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts)
+ {
+ if(depth == 0) {
+ Parser::Event *e = new Parser::Event;
+ QXmlAttributes a;
+ for(int n = 0; n < atts.length(); ++n) {
+ QString uri = atts.uri(n);
+ QString ln = atts.localName(n);
+ if(a.index(uri, ln) == -1)
+ a.append(atts.qName(n), uri, ln, atts.value(n));
+ }
+ e->setDocumentOpen(namespaceURI, localName, qName, a, nsnames, nsvalues);
+ nsnames.clear();
+ nsvalues.clear();
+ e->setActualString(in->lastString());
+
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+ }
+ else {
+ QDomElement e = doc->createElementNS(namespaceURI, qName);
+ for(int n = 0; n < atts.length(); ++n) {
+ QString uri = atts.uri(n);
+ QString ln = atts.localName(n);
+ bool have;
+ if(!uri.isEmpty()) {
+ have = e.hasAttributeNS(uri, ln);
+ if(qt_bug_have)
+ have = !have;
+ }
+ else
+ have = e.hasAttribute(ln);
+ if(!have)
+ e.setAttributeNS(uri, atts.qName(n), atts.value(n));
+ }
+
+ if(depth == 1) {
+ elem = e;
+ current = e;
+ }
+ else {
+ current.appendChild(e);
+ current = e;
+ }
+ }
+ ++depth;
+ return true;
+ }
+
+ bool endElement(const QString &namespaceURI, const QString &localName, const QString &qName)
+ {
+ --depth;
+ if(depth == 0) {
+ Parser::Event *e = new Parser::Event;
+ e->setDocumentClose(namespaceURI, localName, qName);
+ e->setActualString(in->lastString());
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+ }
+ else {
+ // done with a depth 1 element?
+ if(depth == 1) {
+ Parser::Event *e = new Parser::Event;
+ e->setElement(elem);
+ e->setActualString(in->lastString());
+ in->resetLastData();
+ eventList.append(e);
+ in->pause(true);
+
+ elem = QDomElement();
+ current = QDomElement();
+ }
+ else
+ current = current.parentNode().toElement();
+ }
+
+ if(in->lastRead() == '/')
+ checkNeedMore();
+
+ return true;
+ }
+
+ bool characters(const QString &str)
+ {
+ if(depth >= 1) {
+ QString content = str;
+ if(content.isEmpty())
+ return true;
+
+ if(!current.isNull()) {
+ QDomText text = doc->createTextNode(content);
+ current.appendChild(text);
+ }
+ }
+ return true;
+ }
+
+ /*bool processingInstruction(const QString &target, const QString &data)
+ {
+ printf("Processing: [%s], [%s]\n", target.latin1(), data.latin1());
+ in->resetLastData();
+ return true;
+ }*/
+
+ void checkNeedMore()
+ {
+ // Here we will work around QXmlSimpleReader strangeness and self-closing tags.
+ // The problem is that endElement() is called when the '/' is read, not when
+ // the final '>' is read. This is a potential problem when obtaining unprocessed
+ // bytes from StreamInput after this event, as the '>' character will end up
+ // in the unprocessed chunk. To work around this, we need to advance StreamInput's
+ // internal byte processing, but not the xml character data. This way, the '>'
+ // will get processed and will no longer be in the unprocessed return, but
+ // QXmlSimpleReader can still read it. To do this, we call StreamInput::readNext
+ // with 'peek' mode.
+ QChar c = in->readNext(true); // peek
+ if(c == QXmlInputSource::EndOfData) {
+ needMore = true;
+ }
+ else {
+ // We'll assume the next char is a '>'. If it isn't, then
+ // QXmlSimpleReader will deal with that problem on the next
+ // parse. We don't need to take any action here.
+ needMore = false;
+
+ // there should have been a pending event
+ Parser::Event *e = eventList.getFirst();
+ if(e) {
+ e->setActualString(e->actualString() + '>');
+ in->resetLastData();
+ }
+ }
+ }
+
+ Parser::Event *takeEvent()
+ {
+ if(needMore)
+ return 0;
+ if(eventList.isEmpty())
+ return 0;
+
+ Parser::Event *e = eventList.getFirst();
+ eventList.removeRef(e);
+ in->pause(false);
+ return e;
+ }
+
+ StreamInput *in;
+ QDomDocument *doc;
+ int depth;
+ QStringList nsnames, nsvalues;
+ QDomElement elem, current;
+ QPtrList<Parser::Event> eventList;
+ bool needMore;
+ };
+}
+
+
+//----------------------------------------------------------------------------
+// Event
+//----------------------------------------------------------------------------
+class Parser::Event::Private
+{
+public:
+ int type;
+ QString ns, ln, qn;
+ QXmlAttributes a;
+ QDomElement e;
+ QString str;
+ QStringList nsnames, nsvalues;
+};
+
+Parser::Event::Event()
+{
+ d = 0;
+}
+
+Parser::Event::Event(const Event &from)
+{
+ d = 0;
+ *this = from;
+}
+
+Parser::Event & Parser::Event::operator=(const Event &from)
+{
+ delete d;
+ d = 0;
+ if(from.d)
+ d = new Private(*from.d);
+ return *this;
+}
+
+Parser::Event::~Event()
+{
+ delete d;
+}
+
+bool Parser::Event::isNull() const
+{
+ return (d ? false: true);
+}
+
+int Parser::Event::type() const
+{
+ if(isNull())
+ return -1;
+ return d->type;
+}
+
+QString Parser::Event::nsprefix(const QString &s) const
+{
+ QStringList::ConstIterator it = d->nsnames.begin();
+ QStringList::ConstIterator it2 = d->nsvalues.begin();
+ for(; it != d->nsnames.end(); ++it) {
+ if((*it) == s)
+ return (*it2);
+ ++it2;
+ }
+ return QString::null;
+}
+
+QString Parser::Event::namespaceURI() const
+{
+ return d->ns;
+}
+
+QString Parser::Event::localName() const
+{
+ return d->ln;
+}
+
+QString Parser::Event::qName() const
+{
+ return d->qn;
+}
+
+QXmlAttributes Parser::Event::atts() const
+{
+ return d->a;
+}
+
+QString Parser::Event::actualString() const
+{
+ return d->str;
+}
+
+QDomElement Parser::Event::element() const
+{
+ return d->e;
+}
+
+void Parser::Event::setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues)
+{
+ if(!d)
+ d = new Private;
+ d->type = DocumentOpen;
+ d->ns = namespaceURI;
+ d->ln = localName;
+ d->qn = qName;
+ d->a = atts;
+ d->nsnames = nsnames;
+ d->nsvalues = nsvalues;
+}
+
+void Parser::Event::setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName)
+{
+ if(!d)
+ d = new Private;
+ d->type = DocumentClose;
+ d->ns = namespaceURI;
+ d->ln = localName;
+ d->qn = qName;
+}
+
+void Parser::Event::setElement(const QDomElement &elem)
+{
+ if(!d)
+ d = new Private;
+ d->type = Element;
+ d->e = elem;
+}
+
+void Parser::Event::setError()
+{
+ if(!d)
+ d = new Private;
+ d->type = Error;
+}
+
+void Parser::Event::setActualString(const QString &str)
+{
+ d->str = str;
+}
+
+//----------------------------------------------------------------------------
+// Parser
+//----------------------------------------------------------------------------
+class Parser::Private
+{
+public:
+ Private()
+ {
+ doc = 0;
+ in = 0;
+ handler = 0;
+ reader = 0;
+ reset();
+ }
+
+ ~Private()
+ {
+ reset(false);
+ }
+
+ void reset(bool create=true)
+ {
+ delete reader;
+ delete handler;
+ delete in;
+ delete doc;
+
+ if(create) {
+ doc = new QDomDocument;
+ in = new StreamInput;
+ handler = new ParserHandler(in, doc);
+ reader = new QXmlSimpleReader;
+ reader->setContentHandler(handler);
+
+ // initialize the reader
+ in->pause(true);
+ reader->parse(in, true);
+ in->pause(false);
+ }
+ }
+
+ QDomDocument *doc;
+ StreamInput *in;
+ ParserHandler *handler;
+ QXmlSimpleReader *reader;
+};
+
+Parser::Parser()
+{
+ d = new Private;
+
+ // check for evil bug in Qt <= 3.2.1
+ if(!qt_bug_check) {
+ qt_bug_check = true;
+ QDomElement e = d->doc->createElementNS("someuri", "somename");
+ if(e.hasAttributeNS("someuri", "somename"))
+ qt_bug_have = true;
+ else
+ qt_bug_have = false;
+ }
+}
+
+Parser::~Parser()
+{
+ delete d;
+}
+
+void Parser::reset()
+{
+ d->reset();
+}
+
+void Parser::appendData(const QByteArray &a)
+{
+ d->in->appendData(a);
+
+ // if handler was waiting for more, give it a kick
+ if(d->handler->needMore)
+ d->handler->checkNeedMore();
+}
+
+Parser::Event Parser::readNext()
+{
+ Event e;
+ if(d->handler->needMore)
+ return e;
+ Event *ep = d->handler->takeEvent();
+ if(!ep) {
+ if(!d->reader->parseContinue()) {
+ e.setError();
+ return e;
+ }
+ ep = d->handler->takeEvent();
+ if(!ep)
+ return e;
+ }
+ e = *ep;
+ delete ep;
+ return e;
+}
+
+QByteArray Parser::unprocessed() const
+{
+ return d->in->unprocessed();
+}
+
+QString Parser::encoding() const
+{
+ return d->in->encoding();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h
new file mode 100644
index 00000000..808b6c3d
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/parser.h
@@ -0,0 +1,86 @@
+/*
+ * parser.h - parse an XMPP "document"
+ * 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 PARSER_H
+#define PARSER_H
+
+#include<qdom.h>
+#include<qxml.h>
+
+namespace XMPP
+{
+ class Parser
+ {
+ public:
+ Parser();
+ ~Parser();
+
+ class Event
+ {
+ public:
+ enum Type { DocumentOpen, DocumentClose, Element, Error };
+ Event();
+ Event(const Event &);
+ Event & operator=(const Event &);
+ ~Event();
+
+ bool isNull() const;
+ int type() const;
+
+ // for document open
+ QString nsprefix(const QString &s=QString::null) const;
+
+ // for document open / close
+ QString namespaceURI() const;
+ QString localName() const;
+ QString qName() const;
+ QXmlAttributes atts() const;
+
+ // for element
+ QDomElement element() const;
+
+ // for any
+ QString actualString() const;
+
+ // setup
+ void setDocumentOpen(const QString &namespaceURI, const QString &localName, const QString &qName, const QXmlAttributes &atts, const QStringList &nsnames, const QStringList &nsvalues);
+ void setDocumentClose(const QString &namespaceURI, const QString &localName, const QString &qName);
+ void setElement(const QDomElement &elem);
+ void setError();
+ void setActualString(const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ void reset();
+ void appendData(const QByteArray &a);
+ Event readNext();
+ QByteArray unprocessed() const;
+ QString encoding() const;
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp
new file mode 100644
index 00000000..dfd3253c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.cpp
@@ -0,0 +1,1595 @@
+/*
+ * protocol.cpp - XMPP-Core protocol state machine
+ * Copyright (C) 2004 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
+ *
+ */
+
+// TODO: let the app know if tls is required
+// require mutual auth for server out/in
+// report ErrProtocol if server uses wrong NS
+// use send() instead of writeElement() in CoreProtocol
+
+#include"protocol.h"
+
+#include<qca.h>
+#include"base64.h"
+#include"hash.h"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+using namespace XMPP;
+
+// printArray
+//
+// This function prints out an array of bytes as latin characters, converting
+// non-printable bytes into hex values as necessary. Useful for displaying
+// QByteArrays for debugging purposes.
+static QString printArray(const QByteArray &a)
+{
+ QString s;
+ for(uint n = 0; n < a.size(); ++n) {
+ unsigned char c = (unsigned char)a[(int)n];
+ if(c < 32 || c >= 127) {
+ QString str;
+ str.sprintf("[%02x]", c);
+ s += str;
+ }
+ else
+ s += c;
+ }
+ return s;
+}
+
+// firstChildElement
+//
+// Get an element's first child element
+static QDomElement firstChildElement(const QDomElement &e)
+{
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ if(n.isElement())
+ return n.toElement();
+ }
+ return QDomElement();
+}
+
+//----------------------------------------------------------------------------
+// Version
+//----------------------------------------------------------------------------
+Version::Version(int maj, int min)
+{
+ major = maj;
+ minor = min;
+}
+
+//----------------------------------------------------------------------------
+// StreamFeatures
+//----------------------------------------------------------------------------
+StreamFeatures::StreamFeatures()
+{
+ tls_supported = false;
+ sasl_supported = false;
+ bind_supported = false;
+ tls_required = false;
+}
+
+//----------------------------------------------------------------------------
+// BasicProtocol
+//----------------------------------------------------------------------------
+BasicProtocol::SASLCondEntry BasicProtocol::saslCondTable[] =
+{
+ { "aborted", Aborted },
+ { "incorrect-encoding", IncorrectEncoding },
+ { "invalid-authzid", InvalidAuthzid },
+ { "invalid-mechanism", InvalidMech },
+ { "mechanism-too-weak", MechTooWeak },
+ { "not-authorized", NotAuthorized },
+ { "temporary-auth-failure", TemporaryAuthFailure },
+ { 0, 0 },
+};
+
+BasicProtocol::StreamCondEntry BasicProtocol::streamCondTable[] =
+{
+ { "bad-format", BadFormat },
+ { "bad-namespace-prefix", BadNamespacePrefix },
+ { "conflict", Conflict },
+ { "connection-timeout", ConnectionTimeout },
+ { "host-gone", HostGone },
+ { "host-unknown", HostUnknown },
+ { "improper-addressing", ImproperAddressing },
+ { "internal-server-error", InternalServerError },
+ { "invalid-from", InvalidFrom },
+ { "invalid-id", InvalidId },
+ { "invalid-namespace", InvalidNamespace },
+ { "invalid-xml", InvalidXml },
+ { "not-authorized", StreamNotAuthorized },
+ { "policy-violation", PolicyViolation },
+ { "remote-connection-failed", RemoteConnectionFailed },
+ { "resource-constraint", ResourceConstraint },
+ { "restricted-xml", RestrictedXml },
+ { "see-other-host", SeeOtherHost },
+ { "system-shutdown", SystemShutdown },
+ { "undefined-condition", UndefinedCondition },
+ { "unsupported-encoding", UnsupportedEncoding },
+ { "unsupported-stanza-type", UnsupportedStanzaType },
+ { "unsupported-version", UnsupportedVersion },
+ { "xml-not-well-formed", XmlNotWellFormed },
+ { 0, 0 },
+};
+
+BasicProtocol::BasicProtocol()
+:XmlProtocol()
+{
+ init();
+}
+
+BasicProtocol::~BasicProtocol()
+{
+}
+
+void BasicProtocol::init()
+{
+ errCond = -1;
+ sasl_authed = false;
+ doShutdown = false;
+ delayedError = false;
+ closeError = false;
+ ready = false;
+ stanzasPending = 0;
+ stanzasWritten = 0;
+}
+
+void BasicProtocol::reset()
+{
+ XmlProtocol::reset();
+ init();
+
+ to = QString();
+ from = QString();
+ id = QString();
+ lang = QString();
+ version = Version(1,0);
+ errText = QString();
+ errAppSpec = QDomElement();
+ otherHost = QString();
+ spare.resize(0);
+ sasl_mech = QString();
+ sasl_mechlist.clear();
+ sasl_step.resize(0);
+ stanzaToRecv = QDomElement();
+ sendList.clear();
+}
+
+void BasicProtocol::sendStanza(const QDomElement &e)
+{
+ SendItem i;
+ i.stanzaToSend = e;
+ sendList += i;
+}
+
+void BasicProtocol::sendDirect(const QString &s)
+{
+ SendItem i;
+ i.stringToSend = s;
+ sendList += i;
+}
+
+void BasicProtocol::sendWhitespace()
+{
+ SendItem i;
+ i.doWhitespace = true;
+ sendList += i;
+}
+
+QDomElement BasicProtocol::recvStanza()
+{
+ QDomElement e = stanzaToRecv;
+ stanzaToRecv = QDomElement();
+ return e;
+}
+
+void BasicProtocol::shutdown()
+{
+ doShutdown = true;
+}
+
+void BasicProtocol::shutdownWithError(int cond, const QString &str)
+{
+ otherHost = str;
+ delayErrorAndClose(cond);
+}
+
+bool BasicProtocol::isReady() const
+{
+ return ready;
+}
+
+void BasicProtocol::setReady(bool b)
+{
+ ready = b;
+}
+
+QString BasicProtocol::saslMech() const
+{
+ return sasl_mech;
+}
+
+QByteArray BasicProtocol::saslStep() const
+{
+ return sasl_step;
+}
+
+void BasicProtocol::setSASLMechList(const QStringList &list)
+{
+ sasl_mechlist = list;
+}
+
+void BasicProtocol::setSASLFirst(const QString &mech, const QByteArray &step)
+{
+ sasl_mech = mech;
+ sasl_step = step;
+}
+
+void BasicProtocol::setSASLNext(const QByteArray &step)
+{
+ sasl_step = step;
+}
+
+void BasicProtocol::setSASLAuthed()
+{
+ sasl_authed = true;
+}
+
+int BasicProtocol::stringToSASLCond(const QString &s)
+{
+ for(int n = 0; saslCondTable[n].str; ++n) {
+ if(s == saslCondTable[n].str)
+ return saslCondTable[n].cond;
+ }
+ return -1;
+}
+
+int BasicProtocol::stringToStreamCond(const QString &s)
+{
+ for(int n = 0; streamCondTable[n].str; ++n) {
+ if(s == streamCondTable[n].str)
+ return streamCondTable[n].cond;
+ }
+ return -1;
+}
+
+QString BasicProtocol::saslCondToString(int x)
+{
+ for(int n = 0; saslCondTable[n].str; ++n) {
+ if(x == saslCondTable[n].cond)
+ return saslCondTable[n].str;
+ }
+ return QString();
+}
+
+QString BasicProtocol::streamCondToString(int x)
+{
+ for(int n = 0; streamCondTable[n].str; ++n) {
+ if(x == streamCondTable[n].cond)
+ return streamCondTable[n].str;
+ }
+ return QString();
+}
+
+void BasicProtocol::extractStreamError(const QDomElement &e)
+{
+ QString text;
+ QDomElement appSpec;
+
+ QDomElement t = firstChildElement(e);
+ if(t.isNull() || t.namespaceURI() != NS_STREAMS) {
+ // probably old-style error
+ errCond = -1;
+ errText = e.text();
+ }
+ else
+ errCond = stringToStreamCond(t.tagName());
+
+ if(errCond != -1) {
+ if(errCond == SeeOtherHost)
+ otherHost = t.text();
+
+ t = e.elementsByTagNameNS(NS_STREAMS, "text").item(0).toElement();
+ if(!t.isNull())
+ text = t.text();
+
+ // find first non-standard namespaced element
+ QDomNodeList nl = e.childNodes();
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement() && i.namespaceURI() != NS_STREAMS) {
+ appSpec = i.toElement();
+ break;
+ }
+ }
+
+ errText = text;
+ errAppSpec = appSpec;
+ }
+}
+
+void BasicProtocol::send(const QDomElement &e, bool clip)
+{
+ writeElement(e, TypeElement, false, clip);
+}
+
+void BasicProtocol::sendStreamError(int cond, const QString &text, const QDomElement &appSpec)
+{
+ QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+ QDomElement err = doc.createElementNS(NS_STREAMS, streamCondToString(cond));
+ if(!otherHost.isEmpty())
+ err.appendChild(doc.createTextNode(otherHost));
+ se.appendChild(err);
+ if(!text.isEmpty()) {
+ QDomElement te = doc.createElementNS(NS_STREAMS, "text");
+ te.setAttributeNS(NS_XML, "xml:lang", "en");
+ te.appendChild(doc.createTextNode(text));
+ se.appendChild(te);
+ }
+ se.appendChild(appSpec);
+
+ writeElement(se, 100, false);
+}
+
+void BasicProtocol::sendStreamError(const QString &text)
+{
+ QDomElement se = doc.createElementNS(NS_ETHERX, "stream:error");
+ se.appendChild(doc.createTextNode(text));
+
+ writeElement(se, 100, false);
+}
+
+bool BasicProtocol::errorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+ closeError = true;
+ errCond = cond;
+ errText = text;
+ errAppSpec = appSpec;
+ sendStreamError(cond, text, appSpec);
+ return close();
+}
+
+bool BasicProtocol::error(int code)
+{
+ event = EError;
+ errorCode = code;
+ return true;
+}
+
+void BasicProtocol::delayErrorAndClose(int cond, const QString &text, const QDomElement &appSpec)
+{
+ errorCode = ErrStream;
+ errCond = cond;
+ errText = text;
+ errAppSpec = appSpec;
+ delayedError = true;
+}
+
+void BasicProtocol::delayError(int code)
+{
+ errorCode = code;
+ delayedError = true;
+}
+
+QDomElement BasicProtocol::docElement()
+{
+ // create the root element
+ QDomElement e = doc.createElementNS(NS_ETHERX, "stream:stream");
+
+ QString defns = defaultNamespace();
+ QStringList list = extraNamespaces();
+
+ // HACK: using attributes seems to be the only way to get additional namespaces in here
+ if(!defns.isEmpty())
+ e.setAttribute("xmlns", defns);
+ for(QStringList::ConstIterator it = list.begin(); it != list.end();) {
+ QString prefix = *(it++);
+ QString uri = *(it++);
+ e.setAttribute(QString("xmlns:") + prefix, uri);
+ }
+
+ // additional attributes
+ if(!isIncoming() && !to.isEmpty())
+ e.setAttribute("to", to);
+ if(isIncoming() && !from.isEmpty())
+ e.setAttribute("from", from);
+ if(!id.isEmpty())
+ e.setAttribute("id", id);
+ if(!lang.isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", lang);
+ if(version.major > 0 || version.minor > 0)
+ e.setAttribute("version", QString::number(version.major) + '.' + QString::number(version.minor));
+
+ return e;
+}
+
+void BasicProtocol::handleDocOpen(const Parser::Event &pe)
+{
+ if(isIncoming()) {
+ if(xmlEncoding() != "UTF-8") {
+ delayErrorAndClose(UnsupportedEncoding);
+ return;
+ }
+ }
+
+ if(pe.namespaceURI() == NS_ETHERX && pe.localName() == "stream") {
+ QXmlAttributes atts = pe.atts();
+
+ // grab the version
+ int major = 0;
+ int minor = 0;
+ QString verstr = atts.value("version");
+ if(!verstr.isEmpty()) {
+ int n = verstr.find('.');
+ if(n != -1) {
+ major = verstr.mid(0, n).toInt();
+ minor = verstr.mid(n+1).toInt();
+ }
+ else {
+ major = verstr.toInt();
+ minor = 0;
+ }
+ }
+ version = Version(major, minor);
+
+ if(isIncoming()) {
+ to = atts.value("to");
+ QString peerLang = atts.value(NS_XML, "lang");
+ if(!peerLang.isEmpty())
+ lang = peerLang;
+ }
+ // outgoing
+ else {
+ from = atts.value("from");
+ lang = atts.value(NS_XML, "lang");
+ id = atts.value("id");
+ }
+
+ handleStreamOpen(pe);
+ }
+ else {
+ if(isIncoming())
+ delayErrorAndClose(BadFormat);
+ else
+ delayError(ErrProtocol);
+ }
+}
+
+bool BasicProtocol::handleError()
+{
+ if(isIncoming())
+ return errorAndClose(XmlNotWellFormed);
+ else
+ return error(ErrParse);
+}
+
+bool BasicProtocol::handleCloseFinished()
+{
+ if(closeError) {
+ event = EError;
+ errorCode = ErrStream;
+ // note: errCond and friends are already set at this point
+ }
+ else
+ event = EClosed;
+ return true;
+}
+
+bool BasicProtocol::doStep(const QDomElement &e)
+{
+ // handle pending error
+ if(delayedError) {
+ if(isIncoming())
+ return errorAndClose(errCond, errText, errAppSpec);
+ else
+ return error(errorCode);
+ }
+
+ // shutdown?
+ if(doShutdown) {
+ doShutdown = false;
+ return close();
+ }
+
+ if(!e.isNull()) {
+ // check for error
+ if(e.namespaceURI() == NS_ETHERX && e.tagName() == "error") {
+ extractStreamError(e);
+ return error(ErrStream);
+ }
+ }
+
+ if(ready) {
+ // stanzas written?
+ if(stanzasWritten > 0) {
+ --stanzasWritten;
+ event = EStanzaSent;
+ return true;
+ }
+ // send items?
+ if(!sendList.isEmpty()) {
+ SendItem i;
+ {
+ QValueList<SendItem>::Iterator it = sendList.begin();
+ i = (*it);
+ sendList.remove(it);
+ }
+
+ // outgoing stanza?
+ if(!i.stanzaToSend.isNull()) {
+ ++stanzasPending;
+ writeElement(i.stanzaToSend, TypeStanza, true);
+ event = ESend;
+ }
+ // direct send?
+ else if(!i.stringToSend.isEmpty()) {
+ writeString(i.stringToSend, TypeDirect, true);
+ event = ESend;
+ }
+ // whitespace keepalive?
+ else if(i.doWhitespace) {
+ writeString("\n", TypePing, false);
+ event = ESend;
+ }
+ return true;
+ }
+ else {
+ // if we have pending outgoing stanzas, ask for write notification
+ if(stanzasPending)
+ notify |= NSend;
+ }
+ }
+
+ return doStep2(e);
+}
+
+void BasicProtocol::itemWritten(int id, int)
+{
+ if(id == TypeStanza) {
+ --stanzasPending;
+ ++stanzasWritten;
+ }
+}
+
+QString BasicProtocol::defaultNamespace()
+{
+ // default none
+ return QString();
+}
+
+QStringList BasicProtocol::extraNamespaces()
+{
+ // default none
+ return QStringList();
+}
+
+void BasicProtocol::handleStreamOpen(const Parser::Event &)
+{
+ // default does nothing
+}
+
+//----------------------------------------------------------------------------
+// CoreProtocol
+//----------------------------------------------------------------------------
+CoreProtocol::CoreProtocol()
+:BasicProtocol()
+{
+ init();
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+void CoreProtocol::init()
+{
+ step = Start;
+
+ // ??
+ server = false;
+ dialback = false;
+ dialback_verify = false;
+
+ // settings
+ jid = Jid();
+ password = QString();
+ oldOnly = false;
+ allowPlain = false;
+ doTLS = true;
+ doAuth = true;
+ doBinding = true;
+
+ // input
+ user = QString();
+ host = QString();
+
+ // status
+ old = false;
+ digest = false;
+ tls_started = false;
+ sasl_started = false;
+}
+
+void CoreProtocol::reset()
+{
+ BasicProtocol::reset();
+ init();
+}
+
+void CoreProtocol::startClientOut(const Jid &_jid, bool _oldOnly, bool tlsActive, bool _doAuth)
+{
+ jid = _jid;
+ to = _jid.domain();
+ oldOnly = _oldOnly;
+ doAuth = _doAuth;
+ tls_started = tlsActive;
+
+ if(oldOnly)
+ version = Version(0,0);
+ startConnect();
+}
+
+void CoreProtocol::startServerOut(const QString &_to)
+{
+ server = true;
+ to = _to;
+ startConnect();
+}
+
+void CoreProtocol::startDialbackOut(const QString &_to, const QString &_from)
+{
+ server = true;
+ dialback = true;
+ to = _to;
+ self_from = _from;
+ startConnect();
+}
+
+void CoreProtocol::startDialbackVerifyOut(const QString &_to, const QString &_from, const QString &id, const QString &key)
+{
+ server = true;
+ dialback = true;
+ dialback_verify = true;
+ to = _to;
+ self_from = _from;
+ dialback_id = id;
+ dialback_key = key;
+ startConnect();
+}
+
+void CoreProtocol::startClientIn(const QString &_id)
+{
+ id = _id;
+ startAccept();
+}
+
+void CoreProtocol::startServerIn(const QString &_id)
+{
+ server = true;
+ id = _id;
+ startAccept();
+}
+
+void CoreProtocol::setLang(const QString &s)
+{
+ lang = s;
+}
+
+void CoreProtocol::setAllowTLS(bool b)
+{
+ doTLS = b;
+}
+
+void CoreProtocol::setAllowBind(bool b)
+{
+ doBinding = b;
+}
+
+void CoreProtocol::setAllowPlain(bool b)
+{
+ allowPlain = b;
+}
+
+void CoreProtocol::setPassword(const QString &s)
+{
+ password = s;
+}
+
+void CoreProtocol::setFrom(const QString &s)
+{
+ from = s;
+}
+
+void CoreProtocol::setDialbackKey(const QString &s)
+{
+ dialback_key = s;
+}
+
+bool CoreProtocol::loginComplete()
+{
+ setReady(true);
+
+ event = EReady;
+ step = Done;
+ return true;
+}
+
+int CoreProtocol::getOldErrorCode(const QDomElement &e)
+{
+ QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+ if(err.isNull() || !err.hasAttribute("code"))
+ return -1;
+ return err.attribute("code").toInt();
+}
+
+/*QString CoreProtocol::xmlToString(const QDomElement &e, bool clip)
+{
+ // determine an appropriate 'fakeNS' to use
+ QString ns;
+ if(e.prefix() == "stream")
+ ns = NS_ETHERX;
+ else if(e.prefix() == "db")
+ ns = NS_DIALBACK;
+ else
+ ns = NS_CLIENT;
+ return ::xmlToString(e, ns, "stream:stream", clip);
+}*/
+
+bool CoreProtocol::stepAdvancesParser() const
+{
+ if(stepRequiresElement())
+ return true;
+ else if(isReady())
+ return true;
+ return false;
+}
+
+// all element-needing steps need to be registered here
+bool CoreProtocol::stepRequiresElement() const
+{
+ switch(step) {
+ case GetFeatures:
+ case GetTLSProceed:
+ case GetSASLChallenge:
+ case GetBindResponse:
+ case GetAuthGetResponse:
+ case GetAuthSetResponse:
+ case GetRequest:
+ case GetSASLResponse:
+ return true;
+ }
+ return false;
+}
+
+void CoreProtocol::stringSend(const QString &s)
+{
+#ifdef XMPP_TEST
+ TD::outgoingTag(s);
+#endif
+}
+
+void CoreProtocol::stringRecv(const QString &s)
+{
+#ifdef XMPP_TEST
+ TD::incomingTag(s);
+#endif
+}
+
+QString CoreProtocol::defaultNamespace()
+{
+ if(server)
+ return NS_SERVER;
+ else
+ return NS_CLIENT;
+}
+
+QStringList CoreProtocol::extraNamespaces()
+{
+ QStringList list;
+ if(dialback) {
+ list += "db";
+ list += NS_DIALBACK;
+ }
+ return list;
+}
+
+void CoreProtocol::handleStreamOpen(const Parser::Event &pe)
+{
+ if(isIncoming()) {
+ QString ns = pe.nsprefix();
+ QString db;
+ if(server) {
+ db = pe.nsprefix("db");
+ if(!db.isEmpty())
+ dialback = true;
+ }
+
+ // verify namespace
+ if((!server && ns != NS_CLIENT) || (server && ns != NS_SERVER) || (dialback && db != NS_DIALBACK)) {
+ delayErrorAndClose(InvalidNamespace);
+ return;
+ }
+
+ // verify version
+ if(version.major < 1 && !dialback) {
+ delayErrorAndClose(UnsupportedVersion);
+ return;
+ }
+ }
+ else {
+ if(!dialback) {
+ if(version.major >= 1 && !oldOnly)
+ old = false;
+ else
+ old = true;
+ }
+ }
+}
+
+void CoreProtocol::elementSend(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+ TD::outgoingXml(e);
+#endif
+}
+
+void CoreProtocol::elementRecv(const QDomElement &e)
+{
+#ifdef XMPP_TEST
+ TD::incomingXml(e);
+#endif
+}
+
+bool CoreProtocol::doStep2(const QDomElement &e)
+{
+ if(dialback)
+ return dialbackStep(e);
+ else
+ return normalStep(e);
+}
+
+bool CoreProtocol::isValidStanza(const QDomElement &e) const
+{
+ QString s = e.tagName();
+ if(e.namespaceURI() == (server ? NS_SERVER : NS_CLIENT) && (s == "message" || s == "presence" || s == "iq"))
+ return true;
+ else
+ return false;
+}
+
+bool CoreProtocol::grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item)
+{
+ for(QValueList<DBItem>::Iterator it = dbpending.begin(); it != dbpending.end(); ++it) {
+ const DBItem &i = *it;
+ if(i.type == type && i.to.compare(to) && i.from.compare(from)) {
+ const DBItem &i = (*it);
+ *item = i;
+ dbpending.remove(it);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CoreProtocol::dialbackStep(const QDomElement &e)
+{
+ if(step == Start) {
+ setReady(true);
+ step = Done;
+ event = EReady;
+ return true;
+ }
+
+ if(!dbrequests.isEmpty()) {
+ // process a request
+ DBItem i;
+ {
+ QValueList<DBItem>::Iterator it = dbrequests.begin();
+ i = (*it);
+ dbrequests.remove(it);
+ }
+
+ QDomElement r;
+ if(i.type == DBItem::ResultRequest) {
+ r = doc.createElementNS(NS_DIALBACK, "db:result");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.appendChild(doc.createTextNode(i.key));
+ dbpending += i;
+ }
+ else if(i.type == DBItem::ResultGrant) {
+ r = doc.createElementNS(NS_DIALBACK, "db:result");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("type", i.ok ? "valid" : "invalid");
+ if(i.ok) {
+ i.type = DBItem::Validated;
+ dbvalidated += i;
+ }
+ else {
+ // TODO: disconnect after writing element
+ }
+ }
+ else if(i.type == DBItem::VerifyRequest) {
+ r = doc.createElementNS(NS_DIALBACK, "db:verify");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("id", i.id);
+ r.appendChild(doc.createTextNode(i.key));
+ dbpending += i;
+ }
+ // VerifyGrant
+ else {
+ r = doc.createElementNS(NS_DIALBACK, "db:verify");
+ r.setAttribute("to", i.to.full());
+ r.setAttribute("from", i.from.full());
+ r.setAttribute("id", i.id);
+ r.setAttribute("type", i.ok ? "valid" : "invalid");
+ }
+
+ writeElement(r, TypeElement, false);
+ event = ESend;
+ return true;
+ }
+
+ if(!e.isNull()) {
+ if(e.namespaceURI() == NS_DIALBACK) {
+ if(e.tagName() == "result") {
+ Jid to, from;
+ to.set(e.attribute("to"), "");
+ from.set(e.attribute("from"), "");
+ if(isIncoming()) {
+ QString key = e.text();
+ // TODO: report event
+ }
+ else {
+ bool ok = (e.attribute("type") == "valid") ? true: false;
+ DBItem i;
+ if(grabPendingItem(from, to, DBItem::ResultRequest, &i)) {
+ if(ok) {
+ i.type = DBItem::Validated;
+ i.ok = true;
+ dbvalidated += i;
+ // TODO: report event
+ }
+ else {
+ // TODO: report event
+ }
+ }
+ }
+ }
+ else if(e.tagName() == "verify") {
+ Jid to, from;
+ to.set(e.attribute("to"), "");
+ from.set(e.attribute("from"), "");
+ QString id = e.attribute("id");
+ if(isIncoming()) {
+ QString key = e.text();
+ // TODO: report event
+ }
+ else {
+ bool ok = (e.attribute("type") == "valid") ? true: false;
+ DBItem i;
+ if(grabPendingItem(from, to, DBItem::VerifyRequest, &i)) {
+ if(ok) {
+ // TODO: report event
+ }
+ else {
+ // TODO: report event
+ }
+ }
+ }
+ }
+ }
+ else {
+ if(isReady()) {
+ if(isValidStanza(e)) {
+ // TODO: disconnect if stanza is from unverified sender
+ // TODO: ignore packets from receiving servers
+ stanzaToRecv = e;
+ event = EStanzaReady;
+ return true;
+ }
+ }
+ }
+ }
+
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+}
+
+bool CoreProtocol::normalStep(const QDomElement &e)
+{
+ if(step == Start) {
+ if(isIncoming()) {
+ need = NSASLMechs;
+ step = SendFeatures;
+ return false;
+ }
+ else {
+ if(old) {
+ if(doAuth)
+ step = HandleAuthGet;
+ else
+ return loginComplete();
+ }
+ else
+ step = GetFeatures;
+
+ return processStep();
+ }
+ }
+ else if(step == HandleFeatures) {
+ // deal with TLS?
+ if(doTLS && !tls_started && !sasl_authed && features.tls_supported) {
+ QDomElement e = doc.createElementNS(NS_TLS, "starttls");
+
+ send(e, true);
+ event = ESend;
+ step = GetTLSProceed;
+ return true;
+ }
+
+ // deal with SASL?
+ if(!sasl_authed) {
+ if(!features.sasl_supported) {
+ // SASL MUST be supported
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+#ifdef XMPP_TEST
+ TD::msg("starting SASL authentication...");
+#endif
+ need = NSASLFirst;
+ step = GetSASLFirst;
+ return false;
+ }
+
+ if(server) {
+ return loginComplete();
+ }
+ else {
+ if(!doBinding)
+ return loginComplete();
+ }
+
+ // deal with bind
+ if(!features.bind_supported) {
+ // bind MUST be supported
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("type", "set");
+ e.setAttribute("id", "bind_1");
+ QDomElement b = doc.createElementNS(NS_BIND, "bind");
+
+ // request specific resource?
+ QString resource = jid.resource();
+ if(!resource.isEmpty()) {
+ QDomElement r = doc.createElement("resource");
+ r.appendChild(doc.createTextNode(jid.resource()));
+ b.appendChild(r);
+ }
+
+ e.appendChild(b);
+
+ send(e);
+ event = ESend;
+ step = GetBindResponse;
+ return true;
+ }
+ else if(step == GetSASLFirst) {
+ QDomElement e = doc.createElementNS(NS_SASL, "auth");
+ e.setAttribute("mechanism", sasl_mech);
+ if(!sasl_step.isEmpty()) {
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+ e.appendChild(doc.createTextNode(Base64::arrayToString(sasl_step)));
+ }
+
+ send(e, true);
+ event = ESend;
+ step = GetSASLChallenge;
+ return true;
+ }
+ else if(step == GetSASLNext) {
+ if(isIncoming()) {
+ if(sasl_authed) {
+ QDomElement e = doc.createElementNS(NS_SASL, "success");
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = IncHandleSASLSuccess;
+ return true;
+ }
+ else {
+ QByteArray stepData = sasl_step;
+ QDomElement e = doc.createElementNS(NS_SASL, "challenge");
+ if(!stepData.isEmpty())
+ e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = GetSASLResponse;
+ return true;
+ }
+ }
+ else {
+ QByteArray stepData = sasl_step;
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL OUT: [%1]").arg(printArray(sasl_step)));
+#endif
+ QDomElement e = doc.createElementNS(NS_SASL, "response");
+ if(!stepData.isEmpty())
+ e.appendChild(doc.createTextNode(Base64::arrayToString(stepData)));
+
+ send(e, true);
+ event = ESend;
+ step = GetSASLChallenge;
+ return true;
+ }
+ }
+ else if(step == HandleSASLSuccess) {
+ need = NSASLLayer;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ else if(step == HandleAuthGet) {
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("to", to);
+ e.setAttribute("type", "get");
+ e.setAttribute("id", "auth_1");
+ QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+ QDomElement u = doc.createElement("username");
+ u.appendChild(doc.createTextNode(jid.node()));
+ q.appendChild(u);
+ e.appendChild(q);
+
+ send(e);
+ event = ESend;
+ step = GetAuthGetResponse;
+ return true;
+ }
+ else if(step == HandleAuthSet) {
+ QDomElement e = doc.createElement("iq");
+ e.setAttribute("to", to);
+ e.setAttribute("type", "set");
+ e.setAttribute("id", "auth_2");
+ QDomElement q = doc.createElementNS("jabber:iq:auth", "query");
+ QDomElement u = doc.createElement("username");
+ u.appendChild(doc.createTextNode(jid.node()));
+ q.appendChild(u);
+ QDomElement p;
+ if(digest) {
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ p = doc.createElement("digest");
+ QCString cs = id.utf8() + password.utf8();
+ p.appendChild(doc.createTextNode(QCA::SHA1::hashToString(cs)));
+ }
+ else {
+ p = doc.createElement("password");
+ p.appendChild(doc.createTextNode(password));
+ }
+ q.appendChild(p);
+ QDomElement r = doc.createElement("resource");
+ r.appendChild(doc.createTextNode(jid.resource()));
+ q.appendChild(r);
+ e.appendChild(q);
+
+ send(e, true);
+ event = ESend;
+ step = GetAuthSetResponse;
+ return true;
+ }
+ // server
+ else if(step == SendFeatures) {
+ QDomElement f = doc.createElementNS(NS_ETHERX, "stream:features");
+ if(!tls_started && !sasl_authed) { // don't offer tls if we are already sasl'd
+ QDomElement tls = doc.createElementNS(NS_TLS, "starttls");
+ f.appendChild(tls);
+ }
+
+ if(sasl_authed) {
+ if(!server) {
+ QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+ f.appendChild(bind);
+ }
+ }
+ else {
+ QDomElement mechs = doc.createElementNS(NS_SASL, "mechanisms");
+ for(QStringList::ConstIterator it = sasl_mechlist.begin(); it != sasl_mechlist.end(); ++it) {
+ QDomElement m = doc.createElement("mechanism");
+ m.appendChild(doc.createTextNode(*it));
+ mechs.appendChild(m);
+ }
+ f.appendChild(mechs);
+ }
+
+ writeElement(f, TypeElement, false);
+ event = ESend;
+ step = GetRequest;
+ return true;
+ }
+ // server
+ else if(step == HandleTLS) {
+ tls_started = true;
+ need = NStartTLS;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ // server
+ else if(step == IncHandleSASLSuccess) {
+ event = ESASLSuccess;
+ spare = resetStream();
+ step = Start;
+ printf("sasl success\n");
+ return true;
+ }
+ else if(step == GetFeatures) {
+ // we are waiting for stream features
+ if(e.namespaceURI() == NS_ETHERX && e.tagName() == "features") {
+ // extract features
+ StreamFeatures f;
+ QDomElement s = e.elementsByTagNameNS(NS_TLS, "starttls").item(0).toElement();
+ if(!s.isNull()) {
+ f.tls_supported = true;
+ f.tls_required = s.elementsByTagNameNS(NS_TLS, "required").count() > 0;
+ }
+ QDomElement m = e.elementsByTagNameNS(NS_SASL, "mechanisms").item(0).toElement();
+ if(!m.isNull()) {
+ f.sasl_supported = true;
+ QDomNodeList l = m.elementsByTagNameNS(NS_SASL, "mechanism");
+ for(uint n = 0; n < l.count(); ++n)
+ f.sasl_mechs += l.item(n).toElement().text();
+ }
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ if(!b.isNull())
+ f.bind_supported = true;
+
+ if(f.tls_supported) {
+#ifdef XMPP_TEST
+ QString s = "STARTTLS is available";
+ if(f.tls_required)
+ s += " (required)";
+ TD::msg(s);
+#endif
+ }
+ if(f.sasl_supported) {
+#ifdef XMPP_TEST
+ QString s = "SASL mechs:";
+ for(QStringList::ConstIterator it = f.sasl_mechs.begin(); it != f.sasl_mechs.end(); ++it)
+ s += QString(" [%1]").arg((*it));
+ TD::msg(s);
+#endif
+ }
+
+ if(doAuth) {
+ event = EFeatures;
+ features = f;
+ step = HandleFeatures;
+ return true;
+ }
+ else
+ return loginComplete();
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetTLSProceed) {
+ // waiting for proceed to starttls
+ if(e.namespaceURI() == NS_TLS) {
+ if(e.tagName() == "proceed") {
+#ifdef XMPP_TEST
+ TD::msg("Server wants us to proceed with ssl handshake");
+#endif
+ tls_started = true;
+ need = NStartTLS;
+ spare = resetStream();
+ step = Start;
+ return false;
+ }
+ else if(e.tagName() == "failure") {
+ event = EError;
+ errorCode = ErrStartTLS;
+ return true;
+ }
+ else {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetSASLChallenge) {
+ // waiting for sasl challenge/success/fail
+ if(e.namespaceURI() == NS_SASL) {
+ if(e.tagName() == "challenge") {
+ QByteArray a = Base64::stringToArray(e.text());
+#ifdef XMPP_TEST
+ TD::msg(QString("SASL IN: [%1]").arg(printArray(a)));
+#endif
+ sasl_step = a;
+ need = NSASLNext;
+ step = GetSASLNext;
+ return false;
+ }
+ else if(e.tagName() == "success") {
+ sasl_authed = true;
+ event = ESASLSuccess;
+ step = HandleSASLSuccess;
+ return true;
+ }
+ else if(e.tagName() == "failure") {
+ QDomElement t = firstChildElement(e);
+ if(t.isNull() || t.namespaceURI() != NS_SASL)
+ errCond = -1;
+ else
+ errCond = stringToSASLCond(t.tagName());
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ else {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ }
+ }
+ else if(step == GetBindResponse) {
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ if(id == "bind_1" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ Jid j;
+ if(!b.isNull()) {
+ QDomElement je = e.elementsByTagName("jid").item(0).toElement();
+ j = je.text();
+ }
+ if(!j.isValid()) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ jid = j;
+ return loginComplete();
+ }
+ else {
+ errCond = -1;
+
+ QDomElement err = e.elementsByTagNameNS(NS_CLIENT, "error").item(0).toElement();
+ if(!err.isNull()) {
+ // get error condition
+ QDomNodeList nl = err.childNodes();
+ QDomElement t;
+ for(uint n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ t = i.toElement();
+ break;
+ }
+ }
+ if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+ QString cond = t.tagName();
+ if(cond == "not-allowed")
+ errCond = BindNotAllowed;
+ else if(cond == "conflict")
+ errCond = BindConflict;
+ }
+ }
+
+ event = EError;
+ errorCode = ErrBind;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetAuthGetResponse) {
+ // waiting for an iq
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ Jid from(e.attribute("from"));
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+ if(okfrom && id == "auth_1" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ QDomElement q = e.elementsByTagNameNS("jabber:iq:auth", "query").item(0).toElement();
+ if(q.isNull() || q.elementsByTagName("username").item(0).isNull() || q.elementsByTagName("resource").item(0).isNull()) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+ bool plain_supported = !q.elementsByTagName("password").item(0).isNull();
+ bool digest_supported = !q.elementsByTagName("digest").item(0).isNull();
+
+ if(!digest_supported && !plain_supported) {
+ event = EError;
+ errorCode = ErrProtocol;
+ return true;
+ }
+
+ // plain text not allowed?
+ if(!digest_supported && !allowPlain) {
+ event = EError;
+ errorCode = ErrPlain;
+ return true;
+ }
+
+ digest = digest_supported;
+ need = NPassword;
+ step = HandleAuthSet;
+ return false;
+ }
+ else {
+ errCond = getOldErrorCode(e);
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else if(step == GetAuthSetResponse) {
+ // waiting for an iq
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ Jid from(e.attribute("from"));
+ QString type(e.attribute("type"));
+ QString id(e.attribute("id"));
+
+ bool okfrom = (from.isEmpty() || from.compare(Jid(to)));
+ if(okfrom && id == "auth_2" && (type == "result" || type == "error")) {
+ if(type == "result") {
+ return loginComplete();
+ }
+ else {
+ errCond = getOldErrorCode(e);
+
+ event = EError;
+ errorCode = ErrAuth;
+ return true;
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ else {
+ // ignore
+ }
+ }
+ // server
+ else if(step == GetRequest) {
+ printf("get request: [%s], %s\n", e.namespaceURI().latin1(), e.tagName().latin1());
+ if(e.namespaceURI() == NS_TLS && e.localName() == "starttls") {
+ // TODO: don't let this be done twice
+
+ QDomElement e = doc.createElementNS(NS_TLS, "proceed");
+ writeElement(e, TypeElement, false, true);
+ event = ESend;
+ step = HandleTLS;
+ return true;
+ }
+ if(e.namespaceURI() == NS_SASL) {
+ if(e.localName() == "auth") {
+ if(sasl_started) {
+ // TODO
+ printf("error\n");
+ return false;
+ }
+
+ sasl_started = true;
+ sasl_mech = e.attribute("mechanism");
+ // TODO: if child text missing, don't pass it
+ sasl_step = Base64::stringToArray(e.text());
+ need = NSASLFirst;
+ step = GetSASLNext;
+ return false;
+ }
+ else {
+ // TODO
+ printf("unknown sasl tag\n");
+ return false;
+ }
+ }
+ if(e.namespaceURI() == NS_CLIENT && e.tagName() == "iq") {
+ QDomElement b = e.elementsByTagNameNS(NS_BIND, "bind").item(0).toElement();
+ if(!b.isNull()) {
+ QDomElement res = b.elementsByTagName("resource").item(0).toElement();
+ QString resource = res.text();
+
+ QDomElement r = doc.createElement("iq");
+ r.setAttribute("type", "result");
+ r.setAttribute("id", e.attribute("id"));
+ QDomElement bind = doc.createElementNS(NS_BIND, "bind");
+ QDomElement jid = doc.createElement("jid");
+ Jid j = user + '@' + host + '/' + resource;
+ jid.appendChild(doc.createTextNode(j.full()));
+ bind.appendChild(jid);
+ r.appendChild(bind);
+
+ writeElement(r, TypeElement, false);
+ event = ESend;
+ // TODO
+ return true;
+ }
+ else {
+ // TODO
+ }
+ }
+ }
+ else if(step == GetSASLResponse) {
+ if(e.namespaceURI() == NS_SASL && e.localName() == "response") {
+ sasl_step = Base64::stringToArray(e.text());
+ need = NSASLNext;
+ step = GetSASLNext;
+ return false;
+ }
+ }
+
+ if(isReady()) {
+ if(!e.isNull() && isValidStanza(e)) {
+ stanzaToRecv = e;
+ event = EStanzaReady;
+ setIncomingAsExternal();
+ return true;
+ }
+ }
+
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h
new file mode 100644
index 00000000..8511ce32
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/protocol.h
@@ -0,0 +1,355 @@
+/*
+ * protocol.h - XMPP-Core protocol state machine
+ * Copyright (C) 2004 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 PROTOCOL_H
+#define PROTOCOL_H
+
+#include<qpair.h>
+#include"xmlprotocol.h"
+#include"xmpp.h"
+
+#define NS_ETHERX "http://etherx.jabber.org/streams"
+#define NS_CLIENT "jabber:client"
+#define NS_SERVER "jabber:server"
+#define NS_DIALBACK "jabber:server:dialback"
+#define NS_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
+#define NS_TLS "urn:ietf:params:xml:ns:xmpp-tls"
+#define NS_SASL "urn:ietf:params:xml:ns:xmpp-sasl"
+#define NS_SESSION "urn:ietf:params:xml:ns:xmpp-session"
+#define NS_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_BIND "urn:ietf:params:xml:ns:xmpp-bind"
+#define NS_XHTML_IM "http://jabber.org/protocol/xhtml-im"
+#define NS_XHTML "http://www.w3.org/1999/xhtml"
+#define NS_CHATSTATES "http://jabber.org/protocol/chatstates"
+
+namespace XMPP
+{
+ class Version
+ {
+ public:
+ Version(int maj=0, int min=0);
+
+ int major, minor;
+ };
+
+ class StreamFeatures
+ {
+ public:
+ StreamFeatures();
+
+ bool tls_supported, sasl_supported, bind_supported;
+ bool tls_required;
+ QStringList sasl_mechs;
+ };
+
+ class BasicProtocol : public XmlProtocol
+ {
+ public:
+ // xmpp 1.0 error conditions
+ enum SASLCond {
+ Aborted,
+ IncorrectEncoding,
+ InvalidAuthzid,
+ InvalidMech,
+ MechTooWeak,
+ NotAuthorized,
+ TemporaryAuthFailure
+ };
+ enum StreamCond {
+ BadFormat,
+ BadNamespacePrefix,
+ Conflict,
+ ConnectionTimeout,
+ HostGone,
+ HostUnknown,
+ ImproperAddressing,
+ InternalServerError,
+ InvalidFrom,
+ InvalidId,
+ InvalidNamespace,
+ InvalidXml,
+ StreamNotAuthorized,
+ PolicyViolation,
+ RemoteConnectionFailed,
+ ResourceConstraint,
+ RestrictedXml,
+ SeeOtherHost,
+ SystemShutdown,
+ UndefinedCondition,
+ UnsupportedEncoding,
+ UnsupportedStanzaType,
+ UnsupportedVersion,
+ XmlNotWellFormed
+ };
+ enum BindCond {
+ BindBadRequest,
+ BindNotAllowed,
+ BindConflict
+ };
+
+ // extend the XmlProtocol enums
+ enum Need {
+ NSASLMechs = XmlProtocol::NCustom, // need SASL mechlist
+ NStartTLS, // need to switch on TLS layer
+ NSASLFirst, // need SASL first step
+ NSASLNext, // need SASL next step
+ NSASLLayer, // need to switch on SASL layer
+ NCustom = XmlProtocol::NCustom+10
+ };
+ enum Event {
+ EFeatures = XmlProtocol::ECustom, // breakpoint after features packet is received
+ ESASLSuccess, // breakpoint after successful sasl auth
+ EStanzaReady, // a stanza was received
+ EStanzaSent, // a stanza was sent
+ EReady, // stream is ready for stanza use
+ ECustom = XmlProtocol::ECustom+10
+ };
+ enum Error {
+ ErrProtocol = XmlProtocol::ErrCustom, // there was an error in the xmpp-core protocol exchange
+ ErrStream, // <stream:error>, see errCond, errText, and errAppSpec for details
+ ErrStartTLS, // server refused starttls
+ ErrAuth, // authorization error. errCond holds sasl condition (or numeric code for old-protocol)
+ ErrBind, // server refused resource bind
+ ErrCustom = XmlProtocol::ErrCustom+10
+ };
+
+ BasicProtocol();
+ ~BasicProtocol();
+
+ void reset();
+
+ // for outgoing xml
+ QDomDocument doc;
+
+ // sasl-related
+ QString saslMech() const;
+ QByteArray saslStep() const;
+ void setSASLMechList(const QStringList &list);
+ void setSASLFirst(const QString &mech, const QByteArray &step);
+ void setSASLNext(const QByteArray &step);
+ void setSASLAuthed();
+
+ // send / recv
+ void sendStanza(const QDomElement &e);
+ void sendDirect(const QString &s);
+ void sendWhitespace();
+ QDomElement recvStanza();
+
+ // shutdown
+ void shutdown();
+ void shutdownWithError(int cond, const QString &otherHost="");
+
+ // <stream> information
+ QString to, from, id, lang;
+ Version version;
+
+ // error output
+ int errCond;
+ QString errText;
+ QDomElement errAppSpec;
+ QString otherHost;
+
+ QByteArray spare; // filled with unprocessed data on NStartTLS and NSASLLayer
+
+ bool isReady() const;
+
+ enum { TypeElement, TypeStanza, TypeDirect, TypePing };
+
+ protected:
+ static int stringToSASLCond(const QString &s);
+ static int stringToStreamCond(const QString &s);
+ static QString saslCondToString(int);
+ static QString streamCondToString(int);
+
+ void send(const QDomElement &e, bool clip=false);
+ void sendStreamError(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ void sendStreamError(const QString &text); // old-style
+
+ bool errorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ bool error(int code);
+ void delayErrorAndClose(int cond, const QString &text="", const QDomElement &appSpec=QDomElement());
+ void delayError(int code);
+
+ // reimplemented
+ QDomElement docElement();
+ void handleDocOpen(const Parser::Event &pe);
+ bool handleError();
+ bool handleCloseFinished();
+ bool doStep(const QDomElement &e);
+ void itemWritten(int id, int size);
+
+ virtual QString defaultNamespace();
+ virtual QStringList extraNamespaces(); // stringlist: prefix,uri,prefix,uri, [...]
+ virtual void handleStreamOpen(const Parser::Event &pe);
+ virtual bool doStep2(const QDomElement &e)=0;
+
+ void setReady(bool b);
+
+ QString sasl_mech;
+ QStringList sasl_mechlist;
+ QByteArray sasl_step;
+ bool sasl_authed;
+
+ QDomElement stanzaToRecv;
+
+ private:
+ struct SASLCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static SASLCondEntry saslCondTable[];
+
+ struct StreamCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static StreamCondEntry streamCondTable[];
+
+ struct SendItem
+ {
+ QDomElement stanzaToSend;
+ QString stringToSend;
+ bool doWhitespace;
+ };
+ QValueList<SendItem> sendList;
+
+ bool doShutdown, delayedError, closeError, ready;
+ int stanzasPending, stanzasWritten;
+
+ void init();
+ void extractStreamError(const QDomElement &e);
+ };
+
+ class CoreProtocol : public BasicProtocol
+ {
+ public:
+ enum {
+ NPassword = NCustom, // need password for old-mode
+ EDBVerify = ECustom, // breakpoint after db:verify request
+ ErrPlain = ErrCustom // server only supports plain, but allowPlain is false locally
+ };
+
+ CoreProtocol();
+ ~CoreProtocol();
+
+ void reset();
+
+ void startClientOut(const Jid &jid, bool oldOnly, bool tlsActive, bool doAuth);
+ void startServerOut(const QString &to);
+ void startDialbackOut(const QString &to, const QString &from);
+ void startDialbackVerifyOut(const QString &to, const QString &from, const QString &id, const QString &key);
+ void startClientIn(const QString &id);
+ void startServerIn(const QString &id);
+
+ void setLang(const QString &s);
+ void setAllowTLS(bool b);
+ void setAllowBind(bool b);
+ void setAllowPlain(bool b); // old-mode
+
+ void setPassword(const QString &s);
+ void setFrom(const QString &s);
+ void setDialbackKey(const QString &s);
+
+ // input
+ QString user, host;
+
+ // status
+ bool old;
+
+ StreamFeatures features;
+
+ //static QString xmlToString(const QDomElement &e, bool clip=false);
+
+ class DBItem
+ {
+ public:
+ enum { ResultRequest, ResultGrant, VerifyRequest, VerifyGrant, Validated };
+ int type;
+ Jid to, from;
+ QString key, id;
+ bool ok;
+ };
+
+ private:
+ enum Step {
+ Start,
+ Done,
+ SendFeatures,
+ GetRequest,
+ HandleTLS,
+ GetSASLResponse,
+ IncHandleSASLSuccess,
+ GetFeatures, // read features packet
+ HandleFeatures, // act on features, by initiating tls, sasl, or bind
+ GetTLSProceed, // read <proceed/> tls response
+ GetSASLFirst, // perform sasl first step using provided data
+ GetSASLChallenge, // read server sasl challenge
+ GetSASLNext, // perform sasl next step using provided data
+ HandleSASLSuccess, // handle what must be done after reporting sasl success
+ GetBindResponse, // read bind response
+ HandleAuthGet, // send old-protocol auth-get
+ GetAuthGetResponse, // read auth-get response
+ HandleAuthSet, // send old-protocol auth-set
+ GetAuthSetResponse // read auth-set response
+ };
+
+ QValueList<DBItem> dbrequests, dbpending, dbvalidated;
+
+ bool server, dialback, dialback_verify;
+ int step;
+
+ bool digest;
+ bool tls_started, sasl_started;
+
+ Jid jid;
+ bool oldOnly;
+ bool allowPlain;
+ bool doTLS, doAuth, doBinding;
+ QString password;
+
+ QString dialback_id, dialback_key;
+ QString self_from;
+
+ void init();
+ static int getOldErrorCode(const QDomElement &e);
+ bool loginComplete();
+
+ bool isValidStanza(const QDomElement &e) const;
+ bool grabPendingItem(const Jid &to, const Jid &from, int type, DBItem *item);
+ bool normalStep(const QDomElement &e);
+ bool dialbackStep(const QDomElement &e);
+
+ // reimplemented
+ bool stepAdvancesParser() const;
+ bool stepRequiresElement() const;
+ void stringSend(const QString &s);
+ void stringRecv(const QString &s);
+ QString defaultNamespace();
+ QStringList extraNamespaces();
+ void handleStreamOpen(const Parser::Event &pe);
+ bool doStep2(const QDomElement &e);
+ void elementSend(const QDomElement &e);
+ void elementRecv(const QDomElement &e);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/qcaprovider.h
new file mode 100644
index 00000000..a7f1805b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/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/jabber/libiris/iris/xmpp-core/securestream.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp
new file mode 100644
index 00000000..6bd902d9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.cpp
@@ -0,0 +1,589 @@
+/*
+ * securestream.cpp - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004 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
+ *
+ */
+
+/*
+ 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"securestream.h"
+
+#include<qguardedptr.h>
+#include<qvaluelist.h>
+#include<qtimer.h>
+
+#ifdef USE_TLSHANDLER
+#include"xmpp.h"
+#endif
+
+//----------------------------------------------------------------------------
+// LayerTracker
+//----------------------------------------------------------------------------
+class LayerTracker
+{
+public:
+ struct Item
+ {
+ int plain;
+ int encoded;
+ };
+
+ LayerTracker();
+
+ void reset();
+ void addPlain(int plain);
+ void specifyEncoded(int encoded, int plain);
+ int finished(int encoded);
+
+ int p;
+ QValueList<Item> list;
+};
+
+LayerTracker::LayerTracker()
+{
+ p = 0;
+}
+
+void LayerTracker::reset()
+{
+ 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
+//----------------------------------------------------------------------------
+class SecureLayer : public QObject
+{
+ Q_OBJECT
+public:
+ enum { TLS, SASL, TLSH };
+ int type;
+ union {
+ QCA::TLS *tls;
+ QCA::SASL *sasl;
+#ifdef USE_TLSHANDLER
+ XMPP::TLSHandler *tlsHandler;
+#endif
+ } p;
+ LayerTracker layer;
+ bool tls_done;
+ int prebytes;
+
+ 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(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(XMPP::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 init()
+ {
+ tls_done = false;
+ prebytes = 0;
+ }
+
+ void 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 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 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;
+ }
+
+signals:
+ void tlsHandshaken();
+ void tlsClosed(const QByteArray &);
+ void readyRead(const QByteArray &);
+ void needWrite(const QByteArray &);
+ void error(int);
+
+private slots:
+ void tls_handshaken()
+ {
+ tls_done = true;
+ tlsHandshaken();
+ }
+
+ void tls_readyRead()
+ {
+ QByteArray a = p.tls->read();
+ readyRead(a);
+ }
+
+ void tls_readyReadOutgoing(int plainBytes)
+ {
+ QByteArray a = p.tls->readOutgoing();
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+
+ void tls_closed()
+ {
+ QByteArray a = p.tls->readUnprocessed();
+ tlsClosed(a);
+ }
+
+ void tls_error(int x)
+ {
+ error(x);
+ }
+
+ void sasl_readyRead()
+ {
+ QByteArray a = p.sasl->read();
+ readyRead(a);
+ }
+
+ void sasl_readyReadOutgoing(int plainBytes)
+ {
+ QByteArray a = p.sasl->readOutgoing();
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+
+ void sasl_error(int x)
+ {
+ error(x);
+ }
+
+#ifdef USE_TLSHANDLER
+ void tlsHandler_success()
+ {
+ tls_done = true;
+ tlsHandshaken();
+ }
+
+ void tlsHandler_fail()
+ {
+ error(0);
+ }
+
+ void tlsHandler_closed()
+ {
+ tlsClosed(QByteArray());
+ }
+
+ void tlsHandler_readyRead(const QByteArray &a)
+ {
+ readyRead(a);
+ }
+
+ void tlsHandler_readyReadOutgoing(const QByteArray &a, int plainBytes)
+ {
+ if(tls_done)
+ layer.specifyEncoded(a.size(), plainBytes);
+ needWrite(a);
+ }
+#endif
+};
+
+#include"securestream.moc"
+
+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(XMPP::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, XMPP::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);
+ if(bytesAvailable())
+ readyRead();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h
new file mode 100644
index 00000000..c5787a2b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/securestream.h
@@ -0,0 +1,84 @@
+/*
+ * securestream.h - combines a ByteStream with TLS and SASL
+ * Copyright (C) 2004 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 SECURESTREAM_H
+#define SECURESTREAM_H
+
+#include<qca.h>
+#include"bytestream.h"
+
+#define USE_TLSHANDLER
+
+#ifdef USE_TLSHANDLER
+namespace XMPP
+{
+ 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(XMPP::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;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp
new file mode 100644
index 00000000..54c4f405
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.cpp
@@ -0,0 +1,459 @@
+/*
+ * simplesasl.cpp - Simple SASL implementation
+ * 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"simplesasl.h"
+
+#include<qhostaddress.h>
+#include<qstringlist.h>
+#include<qptrlist.h>
+#include<qvaluelist.h>
+#include<qca.h>
+#include<stdlib.h>
+#include"base64.h"
+
+namespace XMPP
+{
+
+struct Prop
+{
+ QCString var, val;
+};
+
+class PropList : public QValueList<Prop>
+{
+public:
+ PropList() : QValueList<Prop>()
+ {
+ }
+
+ void set(const QCString &var, const QCString &val)
+ {
+ Prop p;
+ p.var = var;
+ p.val = val;
+ append(p);
+ }
+
+ QCString get(const QCString &var)
+ {
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ return (*it).val;
+ }
+ return QCString();
+ }
+
+ QCString toString() const
+ {
+ QCString str;
+ bool first = true;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if(!first)
+ str += ',';
+ str += (*it).var + "=\"" + (*it).val + '\"';
+ first = false;
+ }
+ return str;
+ }
+
+ bool fromString(const QCString &str)
+ {
+ PropList list;
+ int at = 0;
+ while(1) {
+ int n = str.find('=', at);
+ if(n == -1)
+ break;
+ QCString var, val;
+ var = str.mid(at, n-at);
+ at = n + 1;
+ if(str[at] == '\"') {
+ ++at;
+ n = str.find('\"', at);
+ if(n == -1)
+ break;
+ val = str.mid(at, n-at);
+ at = n + 1;
+ }
+ else {
+ n = str.find(',', at);
+ if(n != -1) {
+ val = str.mid(at, n-at);
+ at = n;
+ }
+ else {
+ val = str.mid(at);
+ at = str.length()-1;
+ }
+ }
+ Prop prop;
+ prop.var = var;
+ prop.val = val;
+ list.append(prop);
+
+ if(str[at] != ',')
+ break;
+ ++at;
+ }
+
+ // integrity check
+ if(list.varCount("nonce") != 1)
+ return false;
+ if(list.varCount("algorithm") != 1)
+ return false;
+ *this = list;
+ return true;
+ }
+
+ int varCount(const QCString &var)
+ {
+ int n = 0;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ ++n;
+ }
+ return n;
+ }
+
+ QStringList getValues(const QCString &var)
+ {
+ QStringList list;
+ for(ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).var == var)
+ list += (*it).val;
+ }
+ return list;
+ }
+};
+
+class SimpleSASLContext : public QCA_SASLContext
+{
+public:
+ // core props
+ QString service, host;
+
+ // state
+ int step;
+ QByteArray in_buf;
+ QString out_mech;
+ QByteArray out_buf;
+ bool capable;
+ int err;
+
+ QCA_SASLNeedParams need;
+ QCA_SASLNeedParams have;
+ QString user, authz, pass, realm;
+
+ SimpleSASLContext()
+ {
+ reset();
+ }
+
+ ~SimpleSASLContext()
+ {
+ reset();
+ }
+
+ void reset()
+ {
+ resetState();
+ resetParams();
+ }
+
+ void resetState()
+ {
+ out_mech = QString();
+ out_buf.resize(0);
+ err = -1;
+ }
+
+ void resetParams()
+ {
+ capable = true;
+ need.user = false;
+ need.authzid = false;
+ need.pass = false;
+ need.realm = false;
+ have.user = false;
+ have.authzid = false;
+ have.pass = false;
+ have.realm = false;
+ user = QString();
+ authz = QString();
+ pass = QString();
+ realm = QString();
+ }
+
+ void setCoreProps(const QString &_service, const QString &_host, QCA_SASLHostPort *, QCA_SASLHostPort *)
+ {
+ service = _service;
+ host = _host;
+ }
+
+ void setSecurityProps(bool, bool, bool, bool, bool reqForward, bool reqCreds, bool reqMutual, int ssfMin, int, const QString &, int)
+ {
+ if(reqForward || reqCreds || reqMutual || ssfMin > 0)
+ capable = false;
+ else
+ capable = true;
+ }
+
+ int security() const
+ {
+ return 0;
+ }
+
+ int errorCond() const
+ {
+ return err;
+ }
+
+ bool clientStart(const QStringList &mechlist)
+ {
+ bool haveMech = false;
+ for(QStringList::ConstIterator it = mechlist.begin(); it != mechlist.end(); ++it) {
+ if((*it) == "DIGEST-MD5") {
+ haveMech = true;
+ break;
+ }
+ }
+ if(!capable || !haveMech) {
+ err = QCA::SASL::NoMech;
+ return false;
+ }
+
+ resetState();
+ step = 0;
+ return true;
+ }
+
+ int clientFirstStep(bool)
+ {
+ return clientTryAgain();
+ }
+
+ bool serverStart(const QString &, QStringList *, const QString &)
+ {
+ return false;
+ }
+
+ int serverFirstStep(const QString &, const QByteArray *)
+ {
+ return Error;
+ }
+
+ QCA_SASLNeedParams clientParamsNeeded() const
+ {
+ return need;
+ }
+
+ void setClientParams(const QString *_user, const QString *_authzid, const QString *_pass, const QString *_realm)
+ {
+ if(_user) {
+ user = *_user;
+ need.user = false;
+ have.user = true;
+ }
+ if(_authzid) {
+ authz = *_authzid;
+ need.authzid = false;
+ have.authzid = true;
+ }
+ if(_pass) {
+ pass = *_pass;
+ need.pass = false;
+ have.pass = true;
+ }
+ if(_realm) {
+ realm = *_realm;
+ need.realm = false;
+ have.realm = true;
+ }
+ }
+
+ QString username() const
+ {
+ return QString();
+ }
+
+ QString authzid() const
+ {
+ return QString();
+ }
+
+ int nextStep(const QByteArray &in)
+ {
+ in_buf = in.copy();
+ return tryAgain();
+ }
+
+ int tryAgain()
+ {
+ return clientTryAgain();
+ }
+
+ QString mech() const
+ {
+ return out_mech;
+ }
+
+ const QByteArray *clientInit() const
+ {
+ return 0;
+ }
+
+ QByteArray result() const
+ {
+ return out_buf;
+ }
+
+ int clientTryAgain()
+ {
+ if(step == 0) {
+ out_mech = "DIGEST-MD5";
+ ++step;
+ return Continue;
+ }
+ else if(step == 1) {
+ // if we still need params, then the app has failed us!
+ if(need.user || need.authzid || need.pass || need.realm) {
+ err = -1;
+ return Error;
+ }
+
+ // see if some params are needed
+ if(!have.user)
+ need.user = true;
+ if(!have.authzid)
+ need.authzid = true;
+ if(!have.pass)
+ need.pass = true;
+ if(need.user || need.authzid || need.pass)
+ return NeedParams;
+
+ // get props
+ QCString cs(in_buf.data(), in_buf.size()+1);
+ PropList in;
+ if(!in.fromString(cs)) {
+ err = QCA::SASL::BadProto;
+ return Error;
+ }
+
+ // make a cnonce
+ QByteArray a(32);
+ for(int n = 0; n < (int)a.size(); ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ QCString cnonce = Base64::arrayToString(a).latin1();
+
+ // make other variables
+ realm = host;
+ QCString nonce = in.get("nonce");
+ QCString nc = "00000001";
+ QCString uri = service.utf8() + '/' + host.utf8();
+ QCString qop = "auth";
+
+ // build 'response'
+ QCString X = user.utf8() + ':' + realm.utf8() + ':' + pass.utf8();
+ QByteArray Y = QCA::MD5::hash(X);
+ QCString tmp = QCString(":") + nonce + ':' + cnonce + ':' + authz.utf8();
+ QByteArray A1(Y.size() + tmp.length());
+ memcpy(A1.data(), Y.data(), Y.size());
+ memcpy(A1.data() + Y.size(), tmp.data(), tmp.length());
+ QCString A2 = "AUTHENTICATE:" + uri;
+ QCString HA1 = QCA::MD5::hashToString(A1).latin1();
+ QCString HA2 = QCA::MD5::hashToString(A2).latin1();
+ QCString KD = HA1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + HA2;
+ QCString Z = QCA::MD5::hashToString(KD).latin1();
+
+ // build output
+ PropList out;
+ out.set("username", user.utf8());
+ out.set("realm", host.utf8());
+ out.set("nonce", nonce);
+ out.set("cnonce", cnonce);
+ out.set("nc", nc);
+ out.set("serv-type", service.utf8());
+ out.set("host", host.utf8());
+ out.set("digest-uri", uri);
+ out.set("qop", qop);
+ out.set("response", Z);
+ out.set("charset", "utf-8");
+ out.set("authzid", authz.utf8());
+ QCString s = out.toString();
+
+ // done
+ out_buf.resize(s.length());
+ memcpy(out_buf.data(), s.data(), out_buf.size());
+ ++step;
+ return Continue;
+ }
+ else {
+ out_buf.resize(0);
+ return Success;
+ }
+ }
+
+ bool encode(const QByteArray &a, QByteArray *b)
+ {
+ *b = a.copy();
+ return true;
+ }
+
+ bool decode(const QByteArray &a, QByteArray *b)
+ {
+ *b = a.copy();
+ return true;
+ }
+};
+
+class QCASimpleSASL : public QCAProvider
+{
+public:
+ QCASimpleSASL() {}
+ ~QCASimpleSASL() {}
+
+ void init()
+ {
+ }
+
+ int qcaVersion() const
+ {
+ return QCA_PLUGIN_VERSION;
+ }
+
+ int capabilities() const
+ {
+ return QCA::CAP_SASL;
+ }
+
+ void *context(int cap)
+ {
+ if(cap == QCA::CAP_SASL)
+ return new SimpleSASLContext;
+ return 0;
+ }
+};
+
+QCAProvider *createProviderSimpleSASL()
+{
+ return (new QCASimpleSASL);
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h
new file mode 100644
index 00000000..12a08c0e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/simplesasl.h
@@ -0,0 +1,31 @@
+/*
+ * simplesasl.h - Simple SASL implementation
+ * 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 SIMPLESASL_H
+#define SIMPLESASL_H
+
+#include"qcaprovider.h"
+
+namespace XMPP
+{
+ QCAProvider *createProviderSimpleSASL();
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp
new file mode 100644
index 00000000..bfcc218c
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/stream.cpp
@@ -0,0 +1,1762 @@
+/*
+ * stream.cpp - handles a client stream
+ * 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
+ *
+ */
+
+/*
+ Notes:
+ - For Non-SASL auth (JEP-0078), username and resource fields are required.
+
+ TODO:
+ - sasl needParams is totally jacked? PLAIN requires authzid, etc
+ - server error handling
+ - reply with protocol errors if the client send something wrong
+ - don't necessarily disconnect on protocol error. prepare for more.
+ - server function
+ - deal with stream 'to' attribute dynamically
+ - flag tls/sasl/binding support dynamically (have the ability to specify extra stream:features)
+ - inform the caller about the user authentication information
+ - sasl security settings
+ - resource-binding interaction
+ - timeouts
+ - allow exchanges of non-standard stanzas
+ - send </stream:stream> even if we close prematurely?
+ - ensure ClientStream and child classes are fully deletable after signals
+ - xml:lang in root (<stream>) element
+ - sasl external
+ - sasl anonymous
+*/
+
+#include"xmpp.h"
+
+#include<qtextstream.h>
+#include<qguardedptr.h>
+#include<qtimer.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"
+
+#ifdef XMPP_TEST
+#include"td.h"
+#endif
+
+//#define XMPP_DEBUG
+
+using namespace XMPP;
+
+static Debug *debug_ptr = 0;
+void XMPP::setDebug(Debug *p)
+{
+ debug_ptr = p;
+}
+
+static QByteArray randomArray(int size)
+{
+ QByteArray a(size);
+ for(int n = 0; n < size; ++n)
+ a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
+ return a;
+}
+
+static QString genId()
+{
+ // need SHA1 here
+ if(!QCA::isSupported(QCA::CAP_SHA1))
+ QCA::insertProvider(createProviderHash());
+
+ return QCA::SHA1::hashToString(randomArray(128));
+}
+
+//----------------------------------------------------------------------------
+// Stanza
+//----------------------------------------------------------------------------
+Stanza::Error::Error(int _type, int _condition, const QString &_text, const QDomElement &_appSpec)
+{
+ type = _type;
+ condition = _condition;
+ text = _text;
+ appSpec = _appSpec;
+}
+
+class Stanza::Private
+{
+public:
+ struct ErrorTypeEntry
+ {
+ const char *str;
+ int type;
+ };
+ static ErrorTypeEntry errorTypeTable[];
+
+ struct ErrorCondEntry
+ {
+ const char *str;
+ int cond;
+ };
+ static ErrorCondEntry errorCondTable[];
+
+ static int stringToKind(const QString &s)
+ {
+ if(s == "message")
+ return Message;
+ else if(s == "presence")
+ return Presence;
+ else if(s == "iq")
+ return IQ;
+ else
+ return -1;
+ }
+
+ static QString kindToString(Kind k)
+ {
+ if(k == Message)
+ return "message";
+ else if(k == Presence)
+ return "presence";
+ else
+ return "iq";
+ }
+
+ static int stringToErrorType(const QString &s)
+ {
+ for(int n = 0; errorTypeTable[n].str; ++n) {
+ if(s == errorTypeTable[n].str)
+ return errorTypeTable[n].type;
+ }
+ return -1;
+ }
+
+ static QString errorTypeToString(int x)
+ {
+ for(int n = 0; errorTypeTable[n].str; ++n) {
+ if(x == errorTypeTable[n].type)
+ return errorTypeTable[n].str;
+ }
+ return QString();
+ }
+
+ static int stringToErrorCond(const QString &s)
+ {
+ for(int n = 0; errorCondTable[n].str; ++n) {
+ if(s == errorCondTable[n].str)
+ return errorCondTable[n].cond;
+ }
+ return -1;
+ }
+
+ static QString errorCondToString(int x)
+ {
+ for(int n = 0; errorCondTable[n].str; ++n) {
+ if(x == errorCondTable[n].cond)
+ return errorCondTable[n].str;
+ }
+ return QString();
+ }
+
+ Stream *s;
+ QDomElement e;
+};
+
+Stanza::Private::ErrorTypeEntry Stanza::Private::errorTypeTable[] =
+{
+ { "cancel", Cancel },
+ { "continue", Continue },
+ { "modify", Modify },
+ { "auth", Auth },
+ { "wait", Wait },
+ { 0, 0 },
+};
+
+Stanza::Private::ErrorCondEntry Stanza::Private::errorCondTable[] =
+{
+ { "bad-request", BadRequest },
+ { "conflict", Conflict },
+ { "feature-not-implemented", FeatureNotImplemented },
+ { "forbidden", Forbidden },
+ { "internal-server-error", InternalServerError },
+ { "item-not-found", ItemNotFound },
+ { "jid-malformed", JidMalformed },
+ { "not-allowed", NotAllowed },
+ { "payment-required", PaymentRequired },
+ { "recipient-unavailable", RecipientUnavailable },
+ { "registration-required", RegistrationRequired },
+ { "remote-server-not-found", ServerNotFound },
+ { "remote-server-timeout", ServerTimeout },
+ { "resource-constraint", ResourceConstraint },
+ { "service-unavailable", ServiceUnavailable },
+ { "subscription-required", SubscriptionRequired },
+ { "undefined-condition", UndefinedCondition },
+ { "unexpected-request", UnexpectedRequest },
+ { 0, 0 },
+};
+
+Stanza::Stanza()
+{
+ d = 0;
+}
+
+Stanza::Stanza(Stream *s, Kind k, const Jid &to, const QString &type, const QString &id)
+{
+ d = new Private;
+
+ Kind kind;
+ if(k == Message || k == Presence || k == IQ)
+ kind = k;
+ else
+ kind = Message;
+
+ d->s = s;
+ d->e = d->s->doc().createElementNS(s->baseNS(), Private::kindToString(kind));
+ if(to.isValid())
+ setTo(to);
+ if(!type.isEmpty())
+ setType(type);
+ if(!id.isEmpty())
+ setId(id);
+}
+
+Stanza::Stanza(Stream *s, const QDomElement &e)
+{
+ d = 0;
+ if(e.namespaceURI() != s->baseNS())
+ return;
+ int x = Private::stringToKind(e.tagName());
+ if(x == -1)
+ return;
+ d = new Private;
+ d->s = s;
+ d->e = e;
+}
+
+Stanza::Stanza(const Stanza &from)
+{
+ d = 0;
+ *this = from;
+}
+
+Stanza & Stanza::operator=(const Stanza &from)
+{
+ delete d;
+ d = 0;
+ if(from.d)
+ d = new Private(*from.d);
+ return *this;
+}
+
+Stanza::~Stanza()
+{
+ delete d;
+}
+
+bool Stanza::isNull() const
+{
+ return (d ? false: true);
+}
+
+QDomElement Stanza::element() const
+{
+ return d->e;
+}
+
+QString Stanza::toString() const
+{
+ return Stream::xmlToString(d->e);
+}
+
+QDomDocument & Stanza::doc() const
+{
+ return d->s->doc();
+}
+
+QString Stanza::baseNS() const
+{
+ return d->s->baseNS();
+}
+
+QString Stanza::xhtmlImNS() const
+{
+ return d->s->xhtmlImNS();
+}
+
+QString Stanza::xhtmlNS() const
+{
+ return d->s->xhtmlNS();
+}
+
+QDomElement Stanza::createElement(const QString &ns, const QString &tagName)
+{
+ return d->s->doc().createElementNS(ns, tagName);
+}
+
+QDomElement Stanza::createTextElement(const QString &ns, const QString &tagName, const QString &text)
+{
+ QDomElement e = d->s->doc().createElementNS(ns, tagName);
+ e.appendChild(d->s->doc().createTextNode(text));
+ return e;
+}
+
+QDomElement Stanza::createXHTMLElement(const QString &xHTML)
+{
+ QDomDocument doc;
+
+ doc.setContent(xHTML, true);
+ QDomElement root = doc.documentElement();
+ //QDomElement e;
+ return (root);
+}
+
+void Stanza::appendChild(const QDomElement &e)
+{
+ d->e.appendChild(e);
+}
+
+Stanza::Kind Stanza::kind() const
+{
+ return (Kind)Private::stringToKind(d->e.tagName());
+}
+
+void Stanza::setKind(Kind k)
+{
+ d->e.setTagName(Private::kindToString(k));
+}
+
+Jid Stanza::to() const
+{
+ return Jid(d->e.attribute("to"));
+}
+
+Jid Stanza::from() const
+{
+ return Jid(d->e.attribute("from"));
+}
+
+QString Stanza::id() const
+{
+ return d->e.attribute("id");
+}
+
+QString Stanza::type() const
+{
+ return d->e.attribute("type");
+}
+
+QString Stanza::lang() const
+{
+ return d->e.attributeNS(NS_XML, "lang", QString());
+}
+
+void Stanza::setTo(const Jid &j)
+{
+ d->e.setAttribute("to", j.full());
+}
+
+void Stanza::setFrom(const Jid &j)
+{
+ d->e.setAttribute("from", j.full());
+}
+
+void Stanza::setId(const QString &id)
+{
+ d->e.setAttribute("id", id);
+}
+
+void Stanza::setType(const QString &type)
+{
+ d->e.setAttribute("type", type);
+}
+
+void Stanza::setLang(const QString &lang)
+{
+ d->e.setAttribute("xml:lang", lang);
+}
+
+Stanza::Error Stanza::error() const
+{
+ Error err;
+ QDomElement e = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(e.isNull())
+ return err;
+
+ // type
+ int x = Private::stringToErrorType(e.attribute("type"));
+ if(x != -1)
+ err.type = x;
+
+ // condition: find first element
+ QDomNodeList nl = e.childNodes();
+ QDomElement t;
+ uint n;
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ t = i.toElement();
+ break;
+ }
+ }
+ if(!t.isNull() && t.namespaceURI() == NS_STANZAS) {
+ x = Private::stringToErrorCond(t.tagName());
+ if(x != -1)
+ err.condition = x;
+ }
+
+ // text
+ t = e.elementsByTagNameNS(NS_STANZAS, "text").item(0).toElement();
+ if(!t.isNull())
+ err.text = t.text();
+ else
+ err.text = e.text();
+
+ // appspec: find first non-standard namespaced element
+ nl = e.childNodes();
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement() && i.namespaceURI() != NS_STANZAS) {
+ err.appSpec = i.toElement();
+ break;
+ }
+ }
+ return err;
+}
+
+void Stanza::setError(const Error &err)
+{
+ // create the element if necessary
+ QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(errElem.isNull()) {
+ errElem = d->e.ownerDocument().createElementNS(d->s->baseNS(), "error");
+ d->e.appendChild(errElem);
+ }
+
+ // error type/condition
+ if(d->s->old()) {
+ errElem.setAttribute("code", QString::number(err.condition));
+ }
+ else {
+ QString stype = Private::errorTypeToString(err.type);
+ if(stype.isEmpty())
+ return;
+ QString scond = Private::errorCondToString(err.condition);
+ if(scond.isEmpty())
+ return;
+
+ errElem.setAttribute("type", stype);
+ errElem.appendChild(d->e.ownerDocument().createElementNS(d->s->baseNS(), scond));
+ }
+
+ // text
+ if(d->s->old()) {
+ errElem.appendChild(d->e.ownerDocument().createTextNode(err.text));
+ }
+ else {
+ QDomElement te = d->e.ownerDocument().createElementNS(d->s->baseNS(), "text");
+ te.appendChild(d->e.ownerDocument().createTextNode(err.text));
+ errElem.appendChild(te);
+ }
+
+ // application specific
+ errElem.appendChild(err.appSpec);
+}
+
+void Stanza::clearError()
+{
+ QDomElement errElem = d->e.elementsByTagNameNS(d->s->baseNS(), "error").item(0).toElement();
+ if(!errElem.isNull())
+ d->e.removeChild(errElem);
+}
+
+//----------------------------------------------------------------------------
+// Stream
+//----------------------------------------------------------------------------
+static XmlProtocol *foo = 0;
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+Stanza Stream::createStanza(Stanza::Kind k, const Jid &to, const QString &type, const QString &id)
+{
+ return Stanza(this, k, to, type, id);
+}
+
+Stanza Stream::createStanza(const QDomElement &e)
+{
+ return Stanza(this, e);
+}
+
+QString Stream::xmlToString(const QDomElement &e, bool clip)
+{
+ if(!foo)
+ foo = new CoreProtocol;
+ return foo->elementToString(e, clip);
+}
+
+//----------------------------------------------------------------------------
+// ClientStream
+//----------------------------------------------------------------------------
+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);
+
+ oldOnly = false;
+ allowPlain = false;
+ mutualAuth = false;
+ haveLocalAddr = false;
+ minimumSSF = 0;
+ maximumSSF = 0;
+ doBinding = true;
+
+ in_rrsig = false;
+
+ reset();
+ }
+
+ void reset()
+ {
+ state = Idle;
+ notify = 0;
+ newStanzas = false;
+ sasl_ssf = 0;
+ tls_warned = false;
+ using_tls = false;
+ }
+
+ Jid jid;
+ 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 newStanzas;
+ int sasl_ssf;
+ bool tls_warned, using_tls;
+ bool doAuth;
+
+ QStringList sasl_mechlist;
+
+ int errCond;
+ QString errText;
+ QDomElement errAppSpec;
+
+ QPtrList<Stanza> in;
+
+ QTimer noopTimer;
+ 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()));
+
+ d->noop_time = 0;
+ connect(&d->noopTimer, SIGNAL(timeout()), SLOT(doNoop()));
+
+ d->tlsHandler = tlsHandler;
+}
+
+ClientStream::ClientStream(const QString &host, const QString &defRealm, ByteStream *bs, QCA::TLS *tls, QObject *parent)
+:Stream(parent)
+{
+ d = new Private;
+ d->mode = Server;
+ d->bs = bs;
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+
+ 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->server = host;
+ d->defRealm = defRealm;
+
+ d->tls = tls;
+
+ d->srv.startClientIn(genId());
+ //d->srv.startServerIn(genId());
+ //d->state = Connecting;
+ //d->jid = Jid();
+ //d->server = QString();
+}
+
+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();
+ }
+ // server
+ else {
+ if(d->tls)
+ d->tls->reset();
+
+ if(d->bs) {
+ d->bs->close();
+ d->bs = 0;
+ }
+
+ d->srv.reset();
+ }
+
+ if(all)
+ d->in.clear();
+}
+
+Jid ClientStream::jid() const
+{
+ return d->jid;
+}
+
+void ClientStream::connectToServer(const Jid &jid, bool auth)
+{
+ reset(true);
+ d->state = Connecting;
+ d->jid = jid;
+ d->doAuth = auth;
+ d->server = d->jid.domain();
+
+ 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;
+ 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) ? true: false;
+}
+
+bool ClientStream::isAuthenticated() const
+{
+ return (d->state == Active) ? true: false;
+}
+
+void ClientStream::setUsername(const QString &s)
+{
+ if(d->sasl)
+ d->sasl->setUsername(s);
+}
+
+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::setResourceBinding(bool b)
+{
+ d->doBinding = b;
+}
+
+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);
+}
+
+QString ClientStream::saslMechanism() const
+{
+ return d->client.saslMech();
+}
+
+int ClientStream::saslSSF() const
+{
+ return d->sasl_ssf;
+}
+
+void ClientStream::setSASLMechanism(const QString &s)
+{
+ d->sasl_mech = s;
+}
+
+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;
+}
+
+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();
+ }
+}
+
+QDomDocument & ClientStream::doc() const
+{
+ return d->client.doc;
+}
+
+QString ClientStream::baseNS() const
+{
+ return NS_CLIENT;
+}
+
+QString ClientStream::xhtmlImNS() const
+{
+ return NS_XHTML_IM;
+}
+
+QString ClientStream::xhtmlNS() const
+{
+ return NS_XHTML;
+}
+
+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::stanzaAvailable() const
+{
+ return (!d->in.isEmpty());
+}
+
+Stanza ClientStream::read()
+{
+ if(d->in.isEmpty())
+ return Stanza();
+ else {
+ Stanza *sp = d->in.getFirst();
+ Stanza s = *sp;
+ d->in.removeRef(sp);
+ return s;
+ }
+}
+
+void ClientStream::write(const Stanza &s)
+{
+ if(d->state == Active) {
+ d->client.sendStanza(s.element());
+ processNext();
+ }
+}
+
+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;
+ connected();
+ if(!self)
+ return;
+
+ // immediate SSL?
+ if(d->conn->useSSL()) {
+ d->using_tls = true;
+ d->ss->startTLSClient(d->tlsHandler, d->server, spare);
+ }
+ else {
+ d->client.addIncomingData(spare);
+ processNext();
+ }
+}
+
+void ClientStream::cr_error()
+{
+ reset();
+ error(ErrConnection);
+}
+
+void ClientStream::bs_connectionClosed()
+{
+ reset();
+ 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 = d->ss->read();
+
+#ifdef XMPP_DEBUG
+ QCString cs(a.data(), a.size()+1);
+ fprintf(stderr, "ClientStream: recv: %d [%s]\n", a.size(), cs.data());
+#endif
+
+ if(d->mode == Client)
+ d->client.addIncomingData(a);
+ else
+ d->srv.addIncomingData(a);
+ if(d->notify & CoreProtocol::NRecv) {
+#ifdef XMPP_DEBUG
+ printf("We needed data, so let's process it\n");
+#endif
+ processNext();
+ }
+}
+
+void ClientStream::ss_bytesWritten(int bytes)
+{
+ if(d->mode == Client)
+ d->client.outgoingDataWritten(bytes);
+ else
+ d->srv.outgoingDataWritten(bytes);
+
+ if(d->notify & CoreProtocol::NSend) {
+#ifdef XMPP_DEBUG
+ printf("We were waiting for data to be written, so let's process\n");
+#endif
+ processNext();
+ }
+}
+
+void ClientStream::ss_tlsHandshaken()
+{
+ QGuardedPtr<QObject> self = this;
+ securityLayerActivated(LayerTLS);
+ if(!self)
+ return;
+ processNext();
+}
+
+void ClientStream::ss_tlsClosed()
+{
+ reset();
+ connectionClosed();
+}
+
+void ClientStream::ss_error(int x)
+{
+ if(x == SecureStream::ErrTLS) {
+ reset();
+ d->errCond = TLSFail;
+ error(ErrTLS);
+ }
+ else {
+ reset();
+ error(ErrSecurityLayer);
+ }
+}
+
+void ClientStream::sasl_clientFirstStep(const QString &mech, const QByteArray *stepData)
+{
+ d->client.setSASLFirst(mech, stepData ? *stepData : QByteArray());
+ //d->client.sasl_mech = mech;
+ //d->client.sasl_firstStep = stepData ? true : false;
+ //d->client.sasl_step = stepData ? *stepData : QByteArray();
+
+ processNext();
+}
+
+void ClientStream::sasl_nextStep(const QByteArray &stepData)
+{
+ if(d->mode == Client)
+ d->client.setSASLNext(stepData);
+ //d->client.sasl_step = stepData;
+ else
+ d->srv.setSASLNext(stepData);
+ //d->srv.sasl_step = stepData;
+
+ processNext();
+}
+
+void ClientStream::sasl_needParams(bool user, bool authzid, bool pass, bool realm)
+{
+#ifdef XMPP_DEBUG
+ printf("need params: %d,%d,%d,%d\n", user, authzid, pass, realm);
+#endif
+ if(authzid && !user) {
+ d->sasl->setAuthzid(d->jid.bare());
+ //d->sasl->setAuthzid("infiniti.homelesshackers.org");
+ }
+ if(user || pass || realm) {
+ d->state = NeedParams;
+ needAuthParams(user, pass, realm);
+ }
+ else
+ d->sasl->continueAfterParams();
+}
+
+void ClientStream::sasl_authCheck(const QString &user, const QString &)
+{
+//#ifdef XMPP_DEBUG
+// printf("authcheck: [%s], [%s]\n", user.latin1(), authzid.latin1());
+//#endif
+ QString u = user;
+ int n = u.find('@');
+ if(n != -1)
+ u.truncate(n);
+ d->srv.user = u;
+ d->sasl->continueAfterAuthCheck();
+}
+
+void ClientStream::sasl_authenticated()
+{
+#ifdef XMPP_DEBUG
+ printf("sasl authed!!\n");
+#endif
+ d->sasl_ssf = d->sasl->ssf();
+
+ if(d->mode == Server) {
+ d->srv.setSASLAuthed();
+ processNext();
+ }
+}
+
+void ClientStream::sasl_error(int)
+{
+//#ifdef XMPP_DEBUG
+// printf("sasl error: %d\n", c);
+//#endif
+ // has to be auth error
+ int x = convertedSASLCond();
+ reset();
+ d->errCond = x;
+ error(ErrAuth);
+}
+
+void ClientStream::srvProcessNext()
+{
+ while(1) {
+ printf("Processing step...\n");
+ if(!d->srv.processStep()) {
+ int need = d->srv.need;
+ if(need == CoreProtocol::NNotify) {
+ d->notify = d->srv.notify;
+ if(d->notify & CoreProtocol::NSend)
+ printf("More data needs to be written to process next step\n");
+ if(d->notify & CoreProtocol::NRecv)
+ printf("More data is needed to process next step\n");
+ }
+ else if(need == CoreProtocol::NSASLMechs) {
+ if(!d->sasl) {
+ d->sasl = new QCA::SASL;
+ connect(d->sasl, SIGNAL(authCheck(const QString &, const QString &)), SLOT(sasl_authCheck(const QString &, const QString &)));
+ connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+ connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+ connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+ //d->sasl->setAllowAnonymous(false);
+ //d->sasl->setRequirePassCredentials(true);
+ //d->sasl->setExternalAuthID("localhost");
+
+ d->sasl->setMinimumSSF(0);
+ d->sasl->setMaximumSSF(256);
+
+ QStringList list;
+ // TODO: d->server is probably wrong here
+ if(!d->sasl->startServer("xmpp", d->server, d->defRealm, &list)) {
+ printf("Error initializing SASL\n");
+ return;
+ }
+ d->sasl_mechlist = list;
+ }
+ d->srv.setSASLMechList(d->sasl_mechlist);
+ continue;
+ }
+ else if(need == CoreProtocol::NStartTLS) {
+ printf("Need StartTLS\n");
+ if(!d->tls->startServer()) {
+ printf("unable to start server!\n");
+ // TODO
+ return;
+ }
+ QByteArray a = d->srv.spare;
+ d->ss->startTLSServer(d->tls, a);
+ }
+ else if(need == CoreProtocol::NSASLFirst) {
+ printf("Need SASL First Step\n");
+ QByteArray a = d->srv.saslStep();
+ d->sasl->putServerFirstStep(d->srv.saslMech(), a);
+ }
+ else if(need == CoreProtocol::NSASLNext) {
+ printf("Need SASL Next Step\n");
+ QByteArray a = d->srv.saslStep();
+ QCString cs(a.data(), a.size()+1);
+ printf("[%s]\n", cs.data());
+ d->sasl->putStep(a);
+ }
+ else if(need == CoreProtocol::NSASLLayer) {
+ }
+
+ // now we can announce stanzas
+ //if(!d->in.isEmpty())
+ // readyRead();
+ return;
+ }
+
+ d->notify = 0;
+
+ int event = d->srv.event;
+ printf("event: %d\n", event);
+ switch(event) {
+ case CoreProtocol::EError: {
+ printf("Error! Code=%d\n", d->srv.errorCode);
+ reset();
+ error(ErrProtocol);
+ //handleError();
+ return;
+ }
+ case CoreProtocol::ESend: {
+ QByteArray a = d->srv.takeOutgoingData();
+ QCString cs(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ printf("Need Send: {%s}\n", cs.data());
+ d->ss->write(a);
+ break;
+ }
+ case CoreProtocol::ERecvOpen: {
+ printf("Break (RecvOpen)\n");
+
+ // calculate key
+ QCString str = QCA::SHA1::hashToString("secret").utf8();
+ str = QCA::SHA1::hashToString(str + "im.pyxa.org").utf8();
+ str = QCA::SHA1::hashToString(str + d->srv.id.utf8()).utf8();
+ d->srv.setDialbackKey(str);
+
+ //d->srv.setDialbackKey("3c5d721ea2fcc45b163a11420e4e358f87e3142a");
+
+ if(d->srv.to != d->server) {
+ // host-gone, host-unknown, see-other-host
+ d->srv.shutdownWithError(CoreProtocol::HostUnknown);
+ }
+ else
+ d->srv.setFrom(d->server);
+ break;
+ }
+ case CoreProtocol::ESASLSuccess: {
+ printf("Break SASL Success\n");
+ disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+ QByteArray a = d->srv.spare;
+ d->ss->setLayerSASL(d->sasl, a);
+ break;
+ }
+ case CoreProtocol::EPeerClosed: {
+ // TODO: this isn' an error
+ printf("peer closed\n");
+ reset();
+ error(ErrProtocol);
+ return;
+ }
+ }
+ }
+}
+
+void ClientStream::doReadyRead()
+{
+ //QGuardedPtr<QObject> self = this;
+ readyRead();
+ //if(!self)
+ // return;
+ //d->in_rrsig = false;
+}
+
+void ClientStream::processNext()
+{
+ if(d->mode == Server) {
+ srvProcessNext();
+ return;
+ }
+
+ QGuardedPtr<QObject> self = this;
+
+ while(1) {
+#ifdef XMPP_DEBUG
+ printf("Processing step...\n");
+#endif
+ bool ok = d->client.processStep();
+ // deal with send/received items
+ for(QValueList<XmlProtocol::TransferItem>::ConstIterator it = d->client.transferItemList.begin(); it != d->client.transferItemList.end(); ++it) {
+ const XmlProtocol::TransferItem &i = *it;
+ if(i.isExternal)
+ continue;
+ QString str;
+ if(i.isString) {
+ // skip whitespace pings
+ if(i.str.stripWhiteSpace().isEmpty())
+ continue;
+ str = i.str;
+ }
+ else
+ str = d->client.elementToString(i.elem);
+ if(i.isSent)
+ outgoingXml(str);
+ else
+ incomingXml(str);
+ }
+
+ if(!ok) {
+ bool cont = handleNeed();
+
+ // now we can announce stanzas
+ //if(!d->in_rrsig && !d->in.isEmpty()) {
+ if(!d->in.isEmpty()) {
+ //d->in_rrsig = true;
+ QTimer::singleShot(0, this, SLOT(doReadyRead()));
+ }
+
+ if(cont)
+ continue;
+ return;
+ }
+
+ int event = d->client.event;
+ d->notify = 0;
+ switch(event) {
+ case CoreProtocol::EError: {
+#ifdef XMPP_DEBUG
+ printf("Error! Code=%d\n", d->client.errorCode);
+#endif
+ handleError();
+ return;
+ }
+ case CoreProtocol::ESend: {
+ QByteArray a = d->client.takeOutgoingData();
+#ifdef XMPP_DEBUG
+ QCString cs(a.size()+1);
+ memcpy(cs.data(), a.data(), a.size());
+ printf("Need Send: {%s}\n", cs.data());
+#endif
+ d->ss->write(a);
+ break;
+ }
+ case CoreProtocol::ERecvOpen: {
+#ifdef XMPP_DEBUG
+ printf("Break (RecvOpen)\n");
+#endif
+
+#ifdef XMPP_TEST
+ QString s = QString("handshake success (lang=[%1]").arg(d->client.lang);
+ if(!d->client.from.isEmpty())
+ s += QString(", from=[%1]").arg(d->client.from);
+ s += ')';
+ TD::msg(s);
+#endif
+
+ if(d->client.old) {
+ d->state = WaitVersion;
+ warning(WarnOldVersion);
+ return;
+ }
+ break;
+ }
+ case CoreProtocol::EFeatures: {
+#ifdef XMPP_DEBUG
+ printf("Break (Features)\n");
+#endif
+ if(!d->tls_warned && !d->using_tls && !d->client.features.tls_supported) {
+ d->tls_warned = true;
+ d->state = WaitTLS;
+ warning(WarnNoTLS);
+ return;
+ }
+ break;
+ }
+ case CoreProtocol::ESASLSuccess: {
+#ifdef XMPP_DEBUG
+ printf("Break SASL Success\n");
+#endif
+ break;
+ }
+ case CoreProtocol::EReady: {
+#ifdef XMPP_DEBUG
+ printf("Done!\n");
+#endif
+ // grab the JID, in case it changed
+ // TODO: d->jid = d->client.jid;
+ d->state = Active;
+ setNoopTime(d->noop_time);
+ authenticated();
+ if(!self)
+ return;
+ break;
+ }
+ case CoreProtocol::EPeerClosed: {
+#ifdef XMPP_DEBUG
+ printf("DocumentClosed\n");
+#endif
+ reset();
+ connectionClosed();
+ return;
+ }
+ case CoreProtocol::EStanzaReady: {
+#ifdef XMPP_DEBUG
+ printf("StanzaReady\n");
+#endif
+ // store the stanza for now, announce after processing all events
+ Stanza s = createStanza(d->client.recvStanza());
+ if(s.isNull())
+ break;
+ d->in.append(new Stanza(s));
+ break;
+ }
+ case CoreProtocol::EStanzaSent: {
+#ifdef XMPP_DEBUG
+ printf("StanzasSent\n");
+#endif
+ stanzaWritten();
+ if(!self)
+ return;
+ break;
+ }
+ case CoreProtocol::EClosed: {
+#ifdef XMPP_DEBUG
+ printf("Closed\n");
+#endif
+ reset();
+ delayedCloseFinished();
+ return;
+ }
+ }
+ }
+}
+
+bool ClientStream::handleNeed()
+{
+ int need = d->client.need;
+ if(need == CoreProtocol::NNotify) {
+ d->notify = d->client.notify;
+#ifdef XMPP_DEBUG
+ if(d->notify & CoreProtocol::NSend)
+ printf("More data needs to be written to process next step\n");
+ if(d->notify & CoreProtocol::NRecv)
+ printf("More data is needed to process next step\n");
+#endif
+ return false;
+ }
+
+ d->notify = 0;
+ switch(need) {
+ case CoreProtocol::NStartTLS: {
+#ifdef XMPP_DEBUG
+ printf("Need StartTLS\n");
+#endif
+ d->using_tls = true;
+ d->ss->startTLSClient(d->tlsHandler, d->server, d->client.spare);
+ return false;
+ }
+ case CoreProtocol::NSASLFirst: {
+#ifdef XMPP_DEBUG
+ printf("Need SASL First Step\n");
+#endif
+ // no SASL plugin? fall back to Simple SASL
+ if(!QCA::isSupported(QCA::CAP_SASL)) {
+ // Simple SASL needs MD5. do we have that either?
+ if(!QCA::isSupported(QCA::CAP_MD5))
+ QCA::insertProvider(createProviderHash());
+ QCA::insertProvider(createProviderSimpleSASL());
+ }
+
+ d->sasl = new QCA::SASL;
+ connect(d->sasl, SIGNAL(clientFirstStep(const QString &, const QByteArray *)), SLOT(sasl_clientFirstStep(const QString &, const QByteArray *)));
+ connect(d->sasl, SIGNAL(nextStep(const QByteArray &)), SLOT(sasl_nextStep(const QByteArray &)));
+ connect(d->sasl, SIGNAL(needParams(bool, bool, bool, bool)), SLOT(sasl_needParams(bool, bool, bool, bool)));
+ connect(d->sasl, SIGNAL(authenticated()), SLOT(sasl_authenticated()));
+ connect(d->sasl, SIGNAL(error(int)), SLOT(sasl_error(int)));
+
+ if(d->haveLocalAddr)
+ d->sasl->setLocalAddr(d->localAddr, d->localPort);
+ if(d->conn->havePeerAddress())
+ d->sasl->setRemoteAddr(d->conn->peerAddress(), d->conn->peerPort());
+
+ d->sasl->setAllowAnonymous(false);
+
+ //d->sasl_mech = "ANONYMOUS";
+ //d->sasl->setRequirePassCredentials(true);
+
+ //d->sasl->setExternalAuthID("localhost");
+ //d->sasl->setExternalSSF(64);
+ //d->sasl_mech = "EXTERNAL";
+
+ d->sasl->setAllowPlain(d->allowPlain);
+ d->sasl->setRequireMutualAuth(d->mutualAuth);
+
+ d->sasl->setMinimumSSF(d->minimumSSF);
+ d->sasl->setMaximumSSF(d->maximumSSF);
+
+ QStringList ml;
+ if(!d->sasl_mech.isEmpty())
+ ml += d->sasl_mech;
+ else
+ ml = d->client.features.sasl_mechs;
+
+ if(!d->sasl->startClient("xmpp", d->server, ml, true)) {
+ int x = convertedSASLCond();
+ reset();
+ d->errCond = x;
+ error(ErrAuth);
+ return false;
+ }
+ return false;
+ }
+ case CoreProtocol::NSASLNext: {
+#ifdef XMPP_DEBUG
+ printf("Need SASL Next Step\n");
+#endif
+ QByteArray a = d->client.saslStep();
+ d->sasl->putStep(a);
+ return false;
+ }
+ case CoreProtocol::NSASLLayer: {
+ // SecureStream will handle the errors from this point
+ disconnect(d->sasl, SIGNAL(error(int)), this, SLOT(sasl_error(int)));
+ d->ss->setLayerSASL(d->sasl, d->client.spare);
+ if(d->sasl_ssf > 0) {
+ QGuardedPtr<QObject> self = this;
+ securityLayerActivated(LayerSASL);
+ if(!self)
+ return false;
+ }
+ break;
+ }
+ case CoreProtocol::NPassword: {
+#ifdef XMPP_DEBUG
+ printf("Need Password\n");
+#endif
+ d->state = NeedParams;
+ needAuthParams(false, true, false);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int ClientStream::convertedSASLCond() const
+{
+ int x = d->sasl->errorCondition();
+ if(x == QCA::SASL::NoMech)
+ return NoMech;
+ else if(x == QCA::SASL::BadProto)
+ return BadProto;
+ else if(x == QCA::SASL::BadServ)
+ return BadServ;
+ else if(x == QCA::SASL::TooWeak)
+ return MechTooWeak;
+ else
+ return GenericAuthError;
+}
+
+void ClientStream::doNoop()
+{
+ if(d->state == Active) {
+#ifdef XMPP_DEBUG
+ printf("doPing\n");
+#endif
+ d->client.sendWhitespace();
+ processNext();
+ }
+}
+
+void ClientStream::writeDirect(const QString &s)
+{
+ if(d->state == Active) {
+#ifdef XMPP_DEBUG
+ printf("writeDirect\n");
+#endif
+ d->client.sendDirect(s);
+ processNext();
+ }
+}
+
+void ClientStream::handleError()
+{
+ int c = d->client.errorCode;
+ if(c == CoreProtocol::ErrParse) {
+ reset();
+ error(ErrParse);
+ }
+ else if(c == CoreProtocol::ErrProtocol) {
+ reset();
+ error(ErrProtocol);
+ }
+ else if(c == CoreProtocol::ErrStream) {
+ int x = d->client.errCond;
+ QString text = d->client.errText;
+ QDomElement appSpec = d->client.errAppSpec;
+
+ int connErr = -1;
+ int strErr = -1;
+
+ switch(x) {
+ case CoreProtocol::BadFormat: { break; } // should NOT happen (we send the right format)
+ case CoreProtocol::BadNamespacePrefix: { break; } // should NOT happen (we send prefixes)
+ case CoreProtocol::Conflict: { strErr = Conflict; break; }
+ case CoreProtocol::ConnectionTimeout: { strErr = ConnectionTimeout; break; }
+ case CoreProtocol::HostGone: { connErr = HostGone; break; }
+ case CoreProtocol::HostUnknown: { connErr = HostUnknown; break; }
+ case CoreProtocol::ImproperAddressing: { break; } // should NOT happen (we aren't a server)
+ case CoreProtocol::InternalServerError: { strErr = InternalServerError; break; }
+ case CoreProtocol::InvalidFrom: { strErr = InvalidFrom; break; }
+ case CoreProtocol::InvalidId: { break; } // should NOT happen (clients don't specify id)
+ case CoreProtocol::InvalidNamespace: { break; } // should NOT happen (we set the right ns)
+ case CoreProtocol::InvalidXml: { strErr = InvalidXml; break; } // shouldn't happen either, but just in case ...
+ case CoreProtocol::StreamNotAuthorized: { break; } // should NOT happen (we're not stupid)
+ case CoreProtocol::PolicyViolation: { strErr = PolicyViolation; break; }
+ case CoreProtocol::RemoteConnectionFailed: { connErr = RemoteConnectionFailed; break; }
+ case CoreProtocol::ResourceConstraint: { strErr = ResourceConstraint; break; }
+ case CoreProtocol::RestrictedXml: { strErr = InvalidXml; break; } // group with this one
+ case CoreProtocol::SeeOtherHost: { connErr = SeeOtherHost; break; }
+ case CoreProtocol::SystemShutdown: { strErr = SystemShutdown; break; }
+ case CoreProtocol::UndefinedCondition: { break; } // leave as null error
+ case CoreProtocol::UnsupportedEncoding: { break; } // should NOT happen (we send good encoding)
+ case CoreProtocol::UnsupportedStanzaType: { break; } // should NOT happen (we're not stupid)
+ case CoreProtocol::UnsupportedVersion: { connErr = UnsupportedVersion; break; }
+ case CoreProtocol::XmlNotWellFormed: { strErr = InvalidXml; break; } // group with this one
+ default: { break; }
+ }
+
+ reset();
+
+ d->errText = text;
+ d->errAppSpec = appSpec;
+ if(connErr != -1) {
+ d->errCond = connErr;
+ error(ErrNeg);
+ }
+ else {
+ if(strErr != -1)
+ d->errCond = strErr;
+ else
+ d->errCond = GenericStreamError;
+ error(ErrStream);
+ }
+ }
+ else if(c == CoreProtocol::ErrStartTLS) {
+ reset();
+ d->errCond = TLSStart;
+ error(ErrTLS);
+ }
+ else if(c == CoreProtocol::ErrAuth) {
+ int x = d->client.errCond;
+ int r = GenericAuthError;
+ if(d->client.old) {
+ if(x == 401) // not authorized
+ r = NotAuthorized;
+ else if(x == 409) // conflict
+ r = GenericAuthError;
+ else if(x == 406) // not acceptable (this should NOT happen)
+ r = GenericAuthError;
+ }
+ else {
+ switch(x) {
+ case CoreProtocol::Aborted: { r = GenericAuthError; break; } // should NOT happen (we never send <abort/>)
+ case CoreProtocol::IncorrectEncoding: { r = GenericAuthError; break; } // should NOT happen
+ case CoreProtocol::InvalidAuthzid: { r = InvalidAuthzid; break; }
+ case CoreProtocol::InvalidMech: { r = InvalidMech; break; }
+ case CoreProtocol::MechTooWeak: { r = MechTooWeak; break; }
+ case CoreProtocol::NotAuthorized: { r = NotAuthorized; break; }
+ case CoreProtocol::TemporaryAuthFailure: { r = TemporaryAuthFailure; break; }
+ }
+ }
+ reset();
+ d->errCond = r;
+ error(ErrAuth);
+ }
+ else if(c == CoreProtocol::ErrPlain) {
+ reset();
+ d->errCond = NoMech;
+ error(ErrAuth);
+ }
+ else if(c == CoreProtocol::ErrBind) {
+ int r = -1;
+ if(d->client.errCond == CoreProtocol::BindBadRequest) {
+ // should NOT happen
+ }
+ else if(d->client.errCond == CoreProtocol::BindNotAllowed) {
+ r = BindNotAllowed;
+ }
+ else if(d->client.errCond == CoreProtocol::BindConflict) {
+ r = BindConflict;
+ }
+
+ if(r != -1) {
+ reset();
+ d->errCond = r;
+ error(ErrBind);
+ }
+ else {
+ reset();
+ error(ErrProtocol);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+// Debug
+//----------------------------------------------------------------------------
+Debug::~Debug()
+{
+}
+
+#ifdef XMPP_TEST
+TD::TD()
+{
+}
+
+TD::~TD()
+{
+}
+
+void TD::msg(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->msg(s);
+}
+
+void TD::outgoingTag(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->outgoingTag(s);
+}
+
+void TD::incomingTag(const QString &s)
+{
+ if(debug_ptr)
+ debug_ptr->incomingTag(s);
+}
+
+void TD::outgoingXml(const QDomElement &e)
+{
+ if(debug_ptr)
+ debug_ptr->outgoingXml(e);
+}
+
+void TD::incomingXml(const QDomElement &e)
+{
+ if(debug_ptr)
+ debug_ptr->incomingXml(e);
+}
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h
new file mode 100644
index 00000000..b636e190
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/td.h
@@ -0,0 +1,20 @@
+#ifndef TESTDEBUG_H
+#define TESTDEBUG_H
+
+#include<qdom.h>
+
+class TD
+{
+public:
+ TD();
+ ~TD();
+
+ static void msg(const QString &);
+ static void outgoingTag(const QString &);
+ static void incomingTag(const QString &);
+ static void outgoingXml(const QDomElement &);
+ static void incomingXml(const QDomElement &);
+};
+
+#endif
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp
new file mode 100644
index 00000000..f3ac0067
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/tlshandler.cpp
@@ -0,0 +1,138 @@
+/*
+ * tlshandler.cpp - abstract wrapper for TLS
+ * 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"xmpp.h"
+
+#include<qtimer.h>
+#include"qca.h"
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// TLSHandler
+//----------------------------------------------------------------------------
+TLSHandler::TLSHandler(QObject *parent)
+:QObject(parent)
+{
+}
+
+TLSHandler::~TLSHandler()
+{
+}
+
+
+//----------------------------------------------------------------------------
+// QCATLSHandler
+//----------------------------------------------------------------------------
+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();
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp
new file mode 100644
index 00000000..c70a04a9
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.cpp
@@ -0,0 +1,543 @@
+/*
+ * xmlprotocol.cpp - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004 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"xmlprotocol.h"
+
+#include"bytestream.h"
+
+using namespace XMPP;
+
+// stripExtraNS
+//
+// This function removes namespace information from various nodes for
+// display purposes only (the element is pretty much useless for processing
+// after this). We do this because QXml is a bit overzealous about outputting
+// redundant namespaces.
+static QDomElement stripExtraNS(const QDomElement &e)
+{
+ // find closest parent with a namespace
+ QDomNode par = e.parentNode();
+ while(!par.isNull() && par.namespaceURI().isNull())
+ par = par.parentNode();
+ bool noShowNS = false;
+ if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+ noShowNS = true;
+
+ // build qName (prefix:localName)
+ QString qName;
+ if(!e.prefix().isEmpty())
+ qName = e.prefix() + ':' + e.localName();
+ else
+ qName = e.tagName();
+
+ QDomElement i;
+ uint x;
+ if(noShowNS)
+ i = e.ownerDocument().createElement(qName);
+ else
+ i = e.ownerDocument().createElementNS(e.namespaceURI(), qName);
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).cloneNode().toAttr();
+
+ // don't show xml namespace
+ if(a.namespaceURI() == NS_XML)
+ i.setAttribute(QString("xml:") + a.name(), a.value());
+ else
+ i.setAttributeNodeNS(a);
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(stripExtraNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+// xmlToString
+//
+// This function converts a QDomElement into a QString, using stripExtraNS
+// to make it pretty.
+static QString xmlToString(const QDomElement &e, const QString &fakeNS, const QString &fakeQName, bool clip)
+{
+ QDomElement i = e.cloneNode().toElement();
+
+ // It seems QDom can only have one namespace attribute at a time (see docElement 'HACK').
+ // Fortunately we only need one kind depending on the input, so it is specified here.
+ QDomElement fake = e.ownerDocument().createElementNS(fakeNS, fakeQName);
+ fake.appendChild(i);
+ fake = stripExtraNS(fake);
+ QString out;
+ {
+ QTextStream ts(&out, IO_WriteOnly);
+ fake.firstChild().save(ts, 0);
+ }
+ // 'clip' means to remove any unwanted (and unneeded) characters, such as a trailing newline
+ if(clip) {
+ int n = out.findRev('>');
+ out.truncate(n+1);
+ }
+ return out;
+}
+
+// createRootXmlTags
+//
+// This function creates three QStrings, one being an <?xml .. ?> processing
+// instruction, and the others being the opening and closing tags of an
+// element, <foo> and </foo>. This basically allows us to get the raw XML
+// text needed to open/close an XML stream, without resorting to generating
+// the XML ourselves. This function uses QDom to do the generation, which
+// ensures proper encoding and entity output.
+static void createRootXmlTags(const QDomElement &root, QString *xmlHeader, QString *tagOpen, QString *tagClose)
+{
+ QDomElement e = root.cloneNode(false).toElement();
+
+ // insert a dummy element to ensure open and closing tags are generated
+ QDomElement dummy = e.ownerDocument().createElement("dummy");
+ e.appendChild(dummy);
+
+ // convert to xml->text
+ QString str;
+ {
+ QTextStream ts(&str, IO_WriteOnly);
+ e.save(ts, 0);
+ }
+
+ // parse the tags out
+ int n = str.find('<');
+ int n2 = str.find('>', n);
+ ++n2;
+ *tagOpen = str.mid(n, n2-n);
+ n2 = str.findRev('>');
+ n = str.findRev('<');
+ ++n2;
+ *tagClose = str.mid(n, n2-n);
+
+ // generate a nice xml processing header
+ *xmlHeader = "<?xml version=\"1.0\"?>";
+}
+
+//----------------------------------------------------------------------------
+// Protocol
+//----------------------------------------------------------------------------
+XmlProtocol::TransferItem::TransferItem()
+{
+}
+
+XmlProtocol::TransferItem::TransferItem(const QString &_str, bool sent, bool external)
+{
+ isString = true;
+ isSent = sent;
+ isExternal = external;
+ str = _str;
+}
+
+XmlProtocol::TransferItem::TransferItem(const QDomElement &_elem, bool sent, bool external)
+{
+ isString = false;
+ isSent = sent;
+ isExternal = external;
+ elem = _elem;
+}
+
+XmlProtocol::XmlProtocol()
+{
+ init();
+}
+
+XmlProtocol::~XmlProtocol()
+{
+}
+
+void XmlProtocol::init()
+{
+ incoming = false;
+ peerClosed = false;
+ closeWritten = false;
+}
+
+void XmlProtocol::reset()
+{
+ init();
+
+ elem = QDomElement();
+ tagOpen = QString();
+ tagClose = QString();
+ xml.reset();
+ outData.resize(0);
+ trackQueue.clear();
+ transferItemList.clear();
+}
+
+void XmlProtocol::addIncomingData(const QByteArray &a)
+{
+ xml.appendData(a);
+}
+
+QByteArray XmlProtocol::takeOutgoingData()
+{
+ QByteArray a = outData.copy();
+ outData.resize(0);
+ return a;
+}
+
+void XmlProtocol::outgoingDataWritten(int bytes)
+{
+ for(QValueList<TrackItem>::Iterator it = trackQueue.begin(); it != trackQueue.end();) {
+ TrackItem &i = *it;
+
+ // enough bytes?
+ if(bytes < i.size) {
+ i.size -= bytes;
+ break;
+ }
+ int type = i.type;
+ int id = i.id;
+ int size = i.size;
+ bytes -= i.size;
+ it = trackQueue.remove(it);
+
+ if(type == TrackItem::Raw) {
+ // do nothing
+ }
+ else if(type == TrackItem::Close) {
+ closeWritten = true;
+ }
+ else if(type == TrackItem::Custom) {
+ itemWritten(id, size);
+ }
+ }
+}
+
+bool XmlProtocol::processStep()
+{
+ Parser::Event pe;
+ notify = 0;
+ transferItemList.clear();
+
+ if(state != Closing && (state == RecvOpen || stepAdvancesParser())) {
+ // if we get here, then it's because we're in some step that advances the parser
+ pe = xml.readNext();
+ if(!pe.isNull()) {
+ // note: error/close events should be handled for ALL steps, so do them here
+ switch(pe.type()) {
+ case Parser::Event::DocumentOpen: {
+ transferItemList += TransferItem(pe.actualString(), false);
+
+ //stringRecv(pe.actualString());
+ break;
+ }
+ case Parser::Event::DocumentClose: {
+ transferItemList += TransferItem(pe.actualString(), false);
+
+ //stringRecv(pe.actualString());
+ if(incoming) {
+ sendTagClose();
+ event = ESend;
+ peerClosed = true;
+ state = Closing;
+ }
+ else {
+ event = EPeerClosed;
+ }
+ return true;
+ }
+ case Parser::Event::Element: {
+ transferItemList += TransferItem(pe.element(), false);
+
+ //elementRecv(pe.element());
+ break;
+ }
+ case Parser::Event::Error: {
+ if(incoming) {
+ // If we get a parse error during the initial element exchange,
+ // flip immediately into 'open' mode so that we can report an error.
+ if(state == RecvOpen) {
+ sendTagOpen();
+ state = Open;
+ }
+ return handleError();
+ }
+ else {
+ event = EError;
+ errorCode = ErrParse;
+ return true;
+ }
+ }
+ }
+ }
+ else {
+ if(state == RecvOpen || stepRequiresElement()) {
+ need = NNotify;
+ notify |= NRecv;
+ return false;
+ }
+ }
+ }
+
+ return baseStep(pe);
+}
+
+QString XmlProtocol::xmlEncoding() const
+{
+ return xml.encoding();
+}
+
+QString XmlProtocol::elementToString(const QDomElement &e, bool clip)
+{
+ if(elem.isNull())
+ elem = elemDoc.importNode(docElement(), true).toElement();
+
+ // Determine the appropriate 'fakeNS' to use
+ QString ns;
+
+ // first, check root namespace
+ QString pre = e.prefix();
+ if(pre.isNull())
+ pre = "";
+ if(pre == elem.prefix()) {
+ ns = elem.namespaceURI();
+ }
+ else {
+ // scan the root attributes for 'xmlns' (oh joyous hacks)
+ QDomNamedNodeMap al = elem.attributes();
+ uint n;
+ for(n = 0; n < al.count(); ++n) {
+ QDomAttr a = al.item(n).toAttr();
+ QString s = a.name();
+ int x = s.find(':');
+ if(x != -1)
+ s = s.mid(x+1);
+ else
+ s = "";
+ if(pre == s) {
+ ns = a.value();
+ break;
+ }
+ }
+ if(n >= al.count()) {
+ // if we get here, then no appropriate ns was found. use root then..
+ ns = elem.namespaceURI();
+ }
+ }
+
+ // build qName
+ QString qn;
+ if(!elem.prefix().isEmpty())
+ qn = elem.prefix() + ':';
+ qn += elem.localName();
+
+ // make the string
+ return xmlToString(e, ns, qn, clip);
+}
+
+bool XmlProtocol::stepRequiresElement() const
+{
+ // default returns false
+ return false;
+}
+
+void XmlProtocol::itemWritten(int, int)
+{
+ // default does nothing
+}
+
+void XmlProtocol::stringSend(const QString &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::stringRecv(const QString &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::elementSend(const QDomElement &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::elementRecv(const QDomElement &)
+{
+ // default does nothing
+}
+
+void XmlProtocol::startConnect()
+{
+ incoming = false;
+ state = SendOpen;
+}
+
+void XmlProtocol::startAccept()
+{
+ incoming = true;
+ state = RecvOpen;
+}
+
+bool XmlProtocol::close()
+{
+ sendTagClose();
+ event = ESend;
+ state = Closing;
+ return true;
+}
+
+int XmlProtocol::writeString(const QString &s, int id, bool external)
+{
+ transferItemList += TransferItem(s, true, external);
+ return internalWriteString(s, TrackItem::Custom, id);
+}
+
+int XmlProtocol::writeElement(const QDomElement &e, int id, bool external, bool clip)
+{
+ if(e.isNull())
+ return 0;
+ transferItemList += TransferItem(e, true, external);
+
+ //elementSend(e);
+ QString out = elementToString(e, clip);
+ return internalWriteString(out, TrackItem::Custom, id);
+}
+
+QByteArray XmlProtocol::resetStream()
+{
+ // reset the state
+ if(incoming)
+ state = RecvOpen;
+ else
+ state = SendOpen;
+
+ // grab unprocessed data before resetting
+ QByteArray spare = xml.unprocessed();
+ xml.reset();
+ return spare;
+}
+
+int XmlProtocol::internalWriteData(const QByteArray &a, TrackItem::Type t, int id)
+{
+ TrackItem i;
+ i.type = t;
+ i.id = id;
+ i.size = a.size();
+ trackQueue += i;
+
+ ByteStream::appendArray(&outData, a);
+ return a.size();
+}
+
+int XmlProtocol::internalWriteString(const QString &s, TrackItem::Type t, int id)
+{
+ QCString cs = s.utf8();
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return internalWriteData(a, t, id);
+}
+
+void XmlProtocol::sendTagOpen()
+{
+ if(elem.isNull())
+ elem = elemDoc.importNode(docElement(), true).toElement();
+
+ QString xmlHeader;
+ createRootXmlTags(elem, &xmlHeader, &tagOpen, &tagClose);
+
+ QString s;
+ s += xmlHeader + '\n';
+ s += tagOpen + '\n';
+
+ transferItemList += TransferItem(xmlHeader, true);
+ transferItemList += TransferItem(tagOpen, true);
+
+ //stringSend(xmlHeader);
+ //stringSend(tagOpen);
+ internalWriteString(s, TrackItem::Raw);
+}
+
+void XmlProtocol::sendTagClose()
+{
+ transferItemList += TransferItem(tagClose, true);
+
+ //stringSend(tagClose);
+ internalWriteString(tagClose, TrackItem::Close);
+}
+
+bool XmlProtocol::baseStep(const Parser::Event &pe)
+{
+ // Basic
+ if(state == SendOpen) {
+ sendTagOpen();
+ event = ESend;
+ if(incoming)
+ state = Open;
+ else
+ state = RecvOpen;
+ return true;
+ }
+ else if(state == RecvOpen) {
+ if(incoming)
+ state = SendOpen;
+ else
+ state = Open;
+
+ // note: event will always be DocumentOpen here
+ handleDocOpen(pe);
+ event = ERecvOpen;
+ return true;
+ }
+ else if(state == Open) {
+ QDomElement e;
+ if(pe.type() == Parser::Event::Element)
+ e = pe.element();
+ return doStep(e);
+ }
+ // Closing
+ else {
+ if(closeWritten) {
+ if(peerClosed) {
+ event = EPeerClosed;
+ return true;
+ }
+ else
+ return handleCloseFinished();
+ }
+
+ need = NNotify;
+ notify = NSend;
+ return false;
+ }
+}
+
+void XmlProtocol::setIncomingAsExternal()
+{
+ for(QValueList<TransferItem>::Iterator it = transferItemList.begin(); it != transferItemList.end(); ++it) {
+ TransferItem &i = *it;
+ // look for elements received
+ if(!i.isString && !i.isSent)
+ i.isExternal = true;
+ }
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h
new file mode 100644
index 00000000..5bf2cbda
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-core/xmlprotocol.h
@@ -0,0 +1,145 @@
+/*
+ * xmlprotocol.h - state machine for 'jabber-like' protocols
+ * Copyright (C) 2004 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 XMLPROTOCOL_H
+#define XMLPROTOCOL_H
+
+#include<qdom.h>
+#include<qvaluelist.h>
+#include"parser.h"
+
+#define NS_XML "http://www.w3.org/XML/1998/namespace"
+
+namespace XMPP
+{
+ class XmlProtocol
+ {
+ public:
+ enum Need {
+ NNotify, // need a data send and/or recv update
+ NCustom = 10
+ };
+ enum Event {
+ EError, // unrecoverable error, see errorCode for details
+ ESend, // data needs to be sent, use takeOutgoingData()
+ ERecvOpen, // breakpoint after root element open tag is received
+ EPeerClosed, // root element close tag received
+ EClosed, // finished closing
+ ECustom = 10
+ };
+ enum Error {
+ ErrParse, // there was an error parsing the xml
+ ErrCustom = 10
+ };
+ enum Notify {
+ NSend = 0x01, // need to know if data has been written
+ NRecv = 0x02 // need incoming data
+ };
+
+ XmlProtocol();
+ virtual ~XmlProtocol();
+
+ virtual void reset();
+
+ // byte I/O for the stream
+ void addIncomingData(const QByteArray &);
+ QByteArray takeOutgoingData();
+ void outgoingDataWritten(int);
+
+ // advance the state machine
+ bool processStep();
+
+ // set these before returning from a step
+ int need, event, errorCode, notify;
+
+ inline bool isIncoming() const { return incoming; }
+ QString xmlEncoding() const;
+ QString elementToString(const QDomElement &e, bool clip=false);
+
+ class TransferItem
+ {
+ public:
+ TransferItem();
+ TransferItem(const QString &str, bool sent, bool external=false);
+ TransferItem(const QDomElement &elem, bool sent, bool external=false);
+
+ bool isSent; // else, received
+ bool isString; // else, is element
+ bool isExternal; // not owned by protocol
+ QString str;
+ QDomElement elem;
+ };
+ QValueList<TransferItem> transferItemList;
+ void setIncomingAsExternal();
+
+ protected:
+ virtual QDomElement docElement()=0;
+ virtual void handleDocOpen(const Parser::Event &pe)=0;
+ virtual bool handleError()=0;
+ virtual bool handleCloseFinished()=0;
+ virtual bool stepAdvancesParser() const=0;
+ virtual bool stepRequiresElement() const;
+ virtual bool doStep(const QDomElement &e)=0;
+ virtual void itemWritten(int id, int size);
+
+ // 'debug'
+ virtual void stringSend(const QString &s);
+ virtual void stringRecv(const QString &s);
+ virtual void elementSend(const QDomElement &e);
+ virtual void elementRecv(const QDomElement &e);
+
+ void startConnect();
+ void startAccept();
+ bool close();
+ int writeString(const QString &s, int id, bool external);
+ int writeElement(const QDomElement &e, int id, bool external, bool clip=false);
+ QByteArray resetStream();
+
+ private:
+ enum { SendOpen, RecvOpen, Open, Closing };
+ class TrackItem
+ {
+ public:
+ enum Type { Raw, Close, Custom };
+ int type, id, size;
+ };
+
+ bool incoming;
+ QDomDocument elemDoc;
+ QDomElement elem;
+ QString tagOpen, tagClose;
+ int state;
+ bool peerClosed;
+ bool closeWritten;
+
+ Parser xml;
+ QByteArray outData;
+ QValueList<TrackItem> trackQueue;
+
+ void init();
+ int internalWriteData(const QByteArray &a, TrackItem::Type t, int id=-1);
+ int internalWriteString(const QString &s, TrackItem::Type t, int id=-1);
+ void sendTagOpen();
+ void sendTagClose();
+ bool baseStep(const Parser::Event &pe);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am b/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am
new file mode 100644
index 00000000..c6dff330
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile.am
@@ -0,0 +1,19 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libiris_xmpp_im.la
+INCLUDES = -I$(srcdir)/../include -I$(srcdir)/../xmpp-core -I$(srcdir)/../xmpp-im -I$(srcdir)/../jabber -I$(srcdir)/../../cutestuff/util -I$(srcdir)/../../cutestuff/network -I$(srcdir)/../../qca/src $(all_includes)
+
+libiris_xmpp_im_la_SOURCES = \
+ client.cpp \
+ types.cpp \
+ xmpp_tasks.cpp \
+ xmpp_vcard.cpp \
+ xmpp_xmlcommon.cpp
+
+CLEANFILES = types.moc
+types.lo: types.moc
+types.moc: $(top_builddir)/kopete/protocols/jabber/libiris/iris/xmpp-im/Makefile
+ ${MOC} -o types.moc $(srcdir)/../xmpp-im/types.cpp
+
+KDE_OPTIONS = nofinal
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp
new file mode 100644
index 00000000..0baeb820
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/client.cpp
@@ -0,0 +1,1522 @@
+/*
+ * client.cpp - IM Client
+ * 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"im.h"
+#include"safedelete.h"
+
+//! \class Client client.h
+//! \brief Communicates with the Jabber network. Start here.
+//!
+//! Client controls an active Jabber connection. It allows you to connect,
+//! authenticate, manipulate the roster, and send / receive messages and
+//! presence. It is the centerpiece of this library, and all Tasks must pass
+//! through it.
+//!
+//! For convenience, many Tasks are handled internally to Client (such as
+//! JT_Auth). However, for accessing features beyond the basics provided by
+//! Client, you will need to manually invoke Tasks. Fortunately, the
+//! process is very simple.
+//!
+//! The entire Task system is heavily founded on Qt. All Tasks have a parent,
+//! except for the root Task, and are considered QObjects. By using Qt's RTTI
+//! facilities (QObject::sender(), QObject::isA(), etc), you can use a
+//! "fire and forget" approach with Tasks.
+//!
+//! \code
+//! #include "client.h"
+//! using namespace Jabber;
+//!
+//! ...
+//!
+//! Client *client;
+//!
+//! Session::Session()
+//! {
+//! client = new Client;
+//! connect(client, SIGNAL(handshaken()), SLOT(clientHandshaken()));
+//! connect(client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(authFinished(bool, int, const QString &)));
+//! client->connectToHost("jabber.org");
+//! }
+//!
+//! void Session::clientHandshaken()
+//! {
+//! client->authDigest("jabtest", "12345", "Psi");
+//! }
+//!
+//! void Session::authFinished(bool success, int, const QString &err)
+//! {
+//! if(success)
+//! printf("Login success!");
+//! else
+//! printf("Login failed. Here's why: %s\n", err.latin1());
+//! }
+//! \endcode
+
+#include<stdarg.h>
+#include<qmap.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include<qguardedptr.h>
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"s5b.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+#include"filetransfer.h"
+
+/*#include<stdio.h>
+#include<stdarg.h>
+#include<qstring.h>
+#include<qdom.h>
+#include<qobjectlist.h>
+#include<qtimer.h>
+#include"xmpp_stream.h"
+#include"xmpp_tasks.h"
+#include"xmpp_xmlcommon.h"
+#include"xmpp_dtcp.h"
+#include"xmpp_ibb.h"
+#include"xmpp_jidlink.h"
+
+using namespace Jabber;*/
+
+#ifdef Q_WS_WIN
+#define vsnprintf _vsnprintf
+#endif
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Client
+//----------------------------------------------------------------------------
+class Client::GroupChat
+{
+public:
+ enum { Connecting, Connected, Closing };
+ GroupChat() {}
+
+ Jid j;
+ int status;
+};
+
+class Client::ClientPrivate
+{
+public:
+ ClientPrivate() {}
+
+ ClientStream *stream;
+ QDomDocument doc;
+ int id_seed;
+ Task *root;
+ QString host, user, pass, resource;
+ QString osname, tzname, clientName, clientVersion, capsNode, capsVersion, capsExt;
+ DiscoItem::Identity identity;
+ QMap<QString,Features> extension_features;
+ int tzoffset;
+ bool active;
+
+ LiveRoster roster;
+ ResourceList resourceList;
+ S5BManager *s5bman;
+ IBBManager *ibbman;
+ JidLinkManager *jlman;
+ FileTransferManager *ftman;
+ bool ftEnabled;
+ QValueList<GroupChat> groupChatList;
+};
+
+
+Client::Client(QObject *par)
+:QObject(par)
+{
+ d = new ClientPrivate;
+ d->tzoffset = 0;
+ d->active = false;
+ d->osname = "N/A";
+ d->clientName = "N/A";
+ d->clientVersion = "0.0";
+ d->capsNode = "";
+ d->capsVersion = "";
+ d->capsExt = "";
+
+ d->id_seed = 0xaaaa;
+ d->root = new Task(this, true);
+
+ d->stream = 0;
+
+ d->s5bman = new S5BManager(this);
+ connect(d->s5bman, SIGNAL(incomingReady()), SLOT(s5b_incomingReady()));
+
+ d->ibbman = new IBBManager(this);
+ connect(d->ibbman, SIGNAL(incomingReady()), SLOT(ibb_incomingReady()));
+
+ d->jlman = new JidLinkManager(this);
+
+ d->ftman = 0;
+}
+
+Client::~Client()
+{
+ close(true);
+
+ delete d->ftman;
+ delete d->jlman;
+ delete d->ibbman;
+ delete d->s5bman;
+ delete d->root;
+ //delete d->stream;
+ delete d;
+}
+
+void Client::connectToServer(ClientStream *s, const Jid &j, 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()));
+ connect(d->stream, SIGNAL(incomingXml(const QString &)), SLOT(streamIncomingXml(const QString &)));
+ connect(d->stream, SIGNAL(outgoingXml(const QString &)), SLOT(streamOutgoingXml(const QString &)));
+
+ d->stream->connectToServer(j, auth);
+}
+
+void Client::start(const QString &host, const QString &user, const QString &pass, const QString &_resource)
+{
+ // TODO
+ d->host = host;
+ d->user = user;
+ d->pass = pass;
+ d->resource = _resource;
+
+ Status stat;
+ stat.setIsAvailable(false);
+ d->resourceList += Resource(resource(), stat);
+
+ JT_PushPresence *pp = new JT_PushPresence(rootTask());
+ connect(pp, SIGNAL(subscription(const Jid &, const QString &)), SLOT(ppSubscription(const Jid &, const QString &)));
+ connect(pp, SIGNAL(presence(const Jid &, const Status &)), SLOT(ppPresence(const Jid &, const Status &)));
+
+ JT_PushMessage *pm = new JT_PushMessage(rootTask());
+ connect(pm, SIGNAL(message(const Message &)), SLOT(pmMessage(const Message &)));
+
+ JT_PushRoster *pr = new JT_PushRoster(rootTask());
+ connect(pr, SIGNAL(roster(const Roster &)), SLOT(prRoster(const Roster &)));
+
+ new JT_ServInfo(rootTask());
+
+ d->active = true;
+}
+
+void Client::setFileTransferEnabled(bool b)
+{
+ if(b) {
+ if(!d->ftman)
+ d->ftman = new FileTransferManager(this);
+ }
+ else {
+ if(d->ftman) {
+ delete d->ftman;
+ d->ftman = 0;
+ }
+ }
+}
+
+FileTransferManager *Client::fileTransferManager() const
+{
+ return d->ftman;
+}
+
+JidLinkManager *Client::jidLinkManager() const
+{
+ return d->jlman;
+}
+
+S5BManager *Client::s5bManager() const
+{
+ return d->s5bman;
+}
+
+IBBManager *Client::ibbManager() const
+{
+ return d->ibbman;
+}
+
+bool Client::isActive() const
+{
+ return d->active;
+}
+
+void Client::groupChatChangeNick(const QString &host, const QString &room, const QString &nick, const Status &_s)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ i.j = jid;
+
+ Status s = _s;
+ s.setIsAvailable(true);
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, s);
+ j->go(true);
+
+ break;
+ }
+ }
+}
+
+bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ // if this room is shutting down, then free it up
+ if(i.status == GroupChat::Closing)
+ it = d->groupChatList.remove(it);
+ else
+ return false;
+ }
+ else
+ ++it;
+ }
+
+ debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
+ GroupChat i;
+ i.j = jid;
+ i.status = GroupChat::Connecting;
+ d->groupChatList += i;
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, Status());
+ j->go(true);
+
+ return true;
+}
+
+bool Client::groupChatJoin(const QString &host, const QString &room, const QString &nick, const QString &password)
+{
+ Jid jid(room + "@" + host + "/" + nick);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end();) {
+ GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ // if this room is shutting down, then free it up
+ if(i.status == GroupChat::Closing)
+ it = d->groupChatList.remove(it);
+ else
+ return false;
+ }
+ else
+ ++it;
+ }
+
+ debug(QString("Client: Joined: [%1]\n").arg(jid.full()));
+ GroupChat i;
+ i.j = jid;
+ i.status = GroupChat::Connecting;
+ d->groupChatList += i;
+
+ JT_MucPresence *j = new JT_MucPresence(rootTask());
+ j->pres(jid, Status(), password);
+ j->go(true);
+
+ return true;
+}
+
+void Client::groupChatSetStatus(const QString &host, const QString &room, const Status &_s)
+{
+ Jid jid(room + "@" + host);
+ bool found = false;
+ for(QValueList<GroupChat>::ConstIterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ const GroupChat &i = *it;
+ if(i.j.compare(jid, false)) {
+ found = true;
+ jid = i.j;
+ break;
+ }
+ }
+ if(!found)
+ return;
+
+ Status s = _s;
+ s.setIsAvailable(true);
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(jid, s);
+ j->go(true);
+}
+
+void Client::groupChatLeave(const QString &host, const QString &room)
+{
+ Jid jid(room + "@" + host);
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+
+ if(!i.j.compare(jid, false))
+ continue;
+
+ i.status = GroupChat::Closing;
+ debug(QString("Client: Leaving: [%1]\n").arg(i.j.full()));
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ Status s;
+ s.setIsAvailable(false);
+ j->pres(i.j, s);
+ j->go(true);
+ }
+}
+
+/*void Client::start()
+{
+ if(d->stream->old()) {
+ // old has no activation step
+ d->active = true;
+ activated();
+ }
+ else {
+ // TODO: IM session
+ }
+}*/
+
+// TODO: fast close
+void Client::close(bool)
+{
+ if(d->stream) {
+ if(d->active) {
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+ i.status = GroupChat::Closing;
+
+ JT_Presence *j = new JT_Presence(rootTask());
+ Status s;
+ s.setIsAvailable(false);
+ j->pres(i.j, s);
+ j->go(true);
+ }
+ }
+
+ d->stream->disconnect(this);
+ d->stream->close();
+ d->stream = 0;
+ }
+ disconnected();
+ cleanup();
+}
+
+void Client::cleanup()
+{
+ d->active = false;
+ //d->authed = false;
+ d->groupChatList.clear();
+}
+
+/*void Client::continueAfterCert()
+{
+ d->stream->continueAfterCert();
+}
+
+void Client::streamConnected()
+{
+ connected();
+}
+
+void Client::streamHandshaken()
+{
+ handshaken();
+}*/
+
+void Client::streamError(int)
+{
+ //StreamError e = err;
+ //error(e);
+
+ //if(!e.isWarning()) {
+ disconnected();
+ cleanup();
+ //}
+}
+
+/*void Client::streamSSLCertificateReady(const QSSLCert &cert)
+{
+ sslCertReady(cert);
+}
+
+void Client::streamCloseFinished()
+{
+ closeFinished();
+}*/
+
+static QDomElement oldStyleNS(const QDomElement &e)
+{
+ // find closest parent with a namespace
+ QDomNode par = e.parentNode();
+ while(!par.isNull() && par.namespaceURI().isNull())
+ par = par.parentNode();
+ bool noShowNS = false;
+ if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+ noShowNS = true;
+
+ QDomElement i;
+ uint x;
+ //if(noShowNS)
+ i = e.ownerDocument().createElement(e.tagName());
+ //else
+ // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x)
+ i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+ if(!noShowNS)
+ i.setAttribute("xmlns", e.namespaceURI());
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(oldStyleNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+void Client::streamReadyRead()
+{
+ // HACK HACK HACK
+ QGuardedPtr<ClientStream> pstream = d->stream;
+
+ while(pstream && d->stream->stanzaAvailable()) {
+ Stanza s = d->stream->read();
+
+ QString out = s.toString();
+ debug(QString("Client: incoming: [\n%1]\n").arg(out));
+ xmlIncoming(out);
+
+ QDomElement x = oldStyleNS(s.element());
+ distribute(x);
+ }
+}
+
+void Client::streamIncomingXml(const QString &s)
+{
+ QString str = s;
+ if(str.at(str.length()-1) != '\n')
+ str += '\n';
+ xmlIncoming(str);
+}
+
+void Client::streamOutgoingXml(const QString &s)
+{
+ QString str = s;
+ if(str.at(str.length()-1) != '\n')
+ str += '\n';
+ xmlOutgoing(str);
+}
+
+void Client::debug(const QString &str)
+{
+ debugText(str);
+}
+
+QString Client::genUniqueId()
+{
+ QString s;
+ s.sprintf("a%x", d->id_seed);
+ d->id_seed += 0x10;
+ return s;
+}
+
+Task *Client::rootTask()
+{
+ return d->root;
+}
+
+QDomDocument *Client::doc() const
+{
+ return &d->doc;
+}
+
+void Client::distribute(const QDomElement &x)
+{
+ if(x.hasAttribute("from")) {
+ Jid j(x.attribute("from"));
+ if(!j.isValid()) {
+ debug("Client: bad 'from' JID\n");
+ return;
+ }
+ }
+
+ if(!rootTask()->take(x)) {
+ debug("Client: packet was ignored.\n");
+ }
+}
+
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+ uint x;
+
+ // grab child nodes
+ /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x)
+ frag.appendChild(nl.item(x).cloneNode());*/
+
+ // find closest xmlns
+ QDomNode n = e;
+ while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+ n = n.parentNode();
+ QString ns;
+ if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+ ns = "jabber:client";
+ else
+ ns = n.toElement().attribute("xmlns");
+
+ // make a new node
+ QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).toAttr();
+ if(a.name() != "xmlns")
+ i.setAttributeNodeNS(a.cloneNode().toAttr());
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(addCorrectNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+
+ //i.appendChild(frag);
+ return i;
+}
+
+void Client::send(const QDomElement &x)
+{
+ if(!d->stream)
+ return;
+
+ //QString out;
+ //QTextStream ts(&out, IO_WriteOnly);
+ //x.save(ts, 0);
+
+ //QString out = Stream::xmlToString(x);
+ //debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+ //xmlOutgoing(out);
+
+ QDomElement e = addCorrectNS(x);
+ Stanza s = d->stream->createStanza(e);
+ if(s.isNull()) {
+ //printf("bad stanza??\n");
+ return;
+ }
+
+ QString out = s.toString();
+ debug(QString("Client: outgoing: [\n%1]\n").arg(out));
+ xmlOutgoing(out);
+
+ //printf("x[%s] x2[%s] s[%s]\n", Stream::xmlToString(x).latin1(), Stream::xmlToString(e).latin1(), s.toString().latin1());
+ d->stream->write(s);
+}
+
+void Client::send(const QString &str)
+{
+ if(!d->stream)
+ return;
+
+ debug(QString("Client: outgoing: [\n%1]\n").arg(str));
+ xmlOutgoing(str);
+ static_cast<ClientStream*>(d->stream)->writeDirect(str);
+}
+
+Stream & Client::stream()
+{
+ return *d->stream;
+}
+
+const LiveRoster & Client::roster() const
+{
+ return d->roster;
+}
+
+const ResourceList & Client::resourceList() const
+{
+ return d->resourceList;
+}
+
+QString Client::host() const
+{
+ return d->host;
+}
+
+QString Client::user() const
+{
+ return d->user;
+}
+
+QString Client::pass() const
+{
+ return d->pass;
+}
+
+QString Client::resource() const
+{
+ return d->resource;
+}
+
+Jid Client::jid() const
+{
+ QString s;
+ if(!d->user.isEmpty())
+ s += d->user + '@';
+ s += d->host;
+ if(!d->resource.isEmpty()) {
+ s += '/';
+ s += d->resource;
+ }
+
+ return Jid(s);
+}
+
+void Client::ppSubscription(const Jid &j, const QString &s)
+{
+ subscription(j, s);
+}
+
+void Client::ppPresence(const Jid &j, const Status &s)
+{
+ if(s.isAvailable())
+ debug(QString("Client: %1 is available.\n").arg(j.full()));
+ else
+ debug(QString("Client: %1 is unavailable.\n").arg(j.full()));
+
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ GroupChat &i = *it;
+
+ if(i.j.compare(j, false)) {
+ bool us = (i.j.resource() == j.resource() || j.resource().isEmpty()) ? true: false;
+
+ debug(QString("for groupchat i=[%1] pres=[%2], [us=%3].\n").arg(i.j.full()).arg(j.full()).arg(us));
+ switch(i.status) {
+ case GroupChat::Connecting:
+ if(us && s.hasError()) {
+ Jid j = i.j;
+ d->groupChatList.remove(it);
+ groupChatError(j, s.errorCode(), s.errorString());
+ }
+ else {
+ // don't signal success unless it is a non-error presence
+ if(!s.hasError()) {
+ i.status = GroupChat::Connected;
+ groupChatJoined(i.j);
+ }
+ groupChatPresence(j, s);
+ }
+ break;
+ case GroupChat::Connected:
+ groupChatPresence(j, s);
+ break;
+ case GroupChat::Closing:
+ if(us && !s.isAvailable()) {
+ Jid j = i.j;
+ d->groupChatList.remove(it);
+ groupChatLeft(j);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return;
+ }
+ }
+
+ if(s.hasError()) {
+ presenceError(j, s.errorCode(), s.errorString());
+ return;
+ }
+
+ // is it me?
+ if(j.compare(jid(), false)) {
+ updateSelfPresence(j, s);
+ }
+ else {
+ // update all relavent roster entries
+ for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end(); ++it) {
+ LiveRosterItem &i = *it;
+
+ if(!i.jid().compare(j, false))
+ continue;
+
+ // roster item has its own resource?
+ if(!i.jid().resource().isEmpty()) {
+ if(i.jid().resource() != j.resource())
+ continue;
+ }
+
+ updatePresence(&i, j, s);
+ }
+ }
+}
+
+void Client::updateSelfPresence(const Jid &j, const Status &s)
+{
+ ResourceList::Iterator rit = d->resourceList.find(j.resource());
+ bool found = (rit == d->resourceList.end()) ? false: true;
+
+ // unavailable? remove the resource
+ if(!s.isAvailable()) {
+ if(found) {
+ debug(QString("Client: Removing self resource: name=[%1]\n").arg(j.resource()));
+ (*rit).setStatus(s);
+ resourceUnavailable(j, *rit);
+ d->resourceList.remove(rit);
+ }
+ }
+ // available? add/update the resource
+ else {
+ Resource r;
+ if(!found) {
+ r = Resource(j.resource(), s);
+ d->resourceList += r;
+ debug(QString("Client: Adding self resource: name=[%1]\n").arg(j.resource()));
+ }
+ else {
+ (*rit).setStatus(s);
+ r = *rit;
+ debug(QString("Client: Updating self resource: name=[%1]\n").arg(j.resource()));
+ }
+
+ resourceAvailable(j, r);
+ }
+}
+
+void Client::updatePresence(LiveRosterItem *i, const Jid &j, const Status &s)
+{
+ ResourceList::Iterator rit = i->resourceList().find(j.resource());
+ bool found = (rit == i->resourceList().end()) ? false: true;
+
+ // unavailable? remove the resource
+ if(!s.isAvailable()) {
+ if(found) {
+ (*rit).setStatus(s);
+ debug(QString("Client: Removing resource from [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ resourceUnavailable(j, *rit);
+ i->resourceList().remove(rit);
+ i->setLastUnavailableStatus(s);
+ }
+ }
+ // available? add/update the resource
+ else {
+ Resource r;
+ if(!found) {
+ r = Resource(j.resource(), s);
+ i->resourceList() += r;
+ debug(QString("Client: Adding resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ }
+ else {
+ (*rit).setStatus(s);
+ r = *rit;
+ debug(QString("Client: Updating resource to [%1]: name=[%2]\n").arg(i->jid().full()).arg(j.resource()));
+ }
+
+ resourceAvailable(j, r);
+ }
+}
+
+void Client::pmMessage(const Message &m)
+{
+ debug(QString("Client: Message from %1\n").arg(m.from().full()));
+
+ if(m.type() == "groupchat") {
+ for(QValueList<GroupChat>::Iterator it = d->groupChatList.begin(); it != d->groupChatList.end(); it++) {
+ const GroupChat &i = *it;
+
+ if(!i.j.compare(m.from(), false))
+ continue;
+
+ if(i.status == GroupChat::Connected)
+ messageReceived(m);
+ }
+ }
+ else
+ messageReceived(m);
+}
+
+void Client::prRoster(const Roster &r)
+{
+ importRoster(r);
+}
+
+void Client::rosterRequest()
+{
+ if(!d->active)
+ return;
+
+ JT_Roster *r = new JT_Roster(rootTask());
+ connect(r, SIGNAL(finished()), SLOT(slotRosterRequestFinished()));
+ r->get();
+ d->roster.flagAllForDelete(); // mod_groups patch
+ r->go(true);
+}
+
+void Client::slotRosterRequestFinished()
+{
+ JT_Roster *r = (JT_Roster *)sender();
+ // on success, let's take it
+ if(r->success()) {
+ //d->roster.flagAllForDelete(); // mod_groups patch
+
+ importRoster(r->roster());
+
+ for(LiveRoster::Iterator it = d->roster.begin(); it != d->roster.end();) {
+ LiveRosterItem &i = *it;
+ if(i.flagForDelete()) {
+ rosterItemRemoved(i);
+ it = d->roster.remove(it);
+ }
+ else
+ ++it;
+ }
+ }
+ else {
+ // don't report a disconnect. Client::error() will do that.
+ if(r->statusCode() == Task::ErrDisc)
+ return;
+ }
+
+ // report success / fail
+ rosterRequestFinished(r->success(), r->statusCode(), r->statusString());
+}
+
+void Client::importRoster(const Roster &r)
+{
+ for(Roster::ConstIterator it = r.begin(); it != r.end(); ++it) {
+ importRosterItem(*it);
+ }
+}
+
+void Client::importRosterItem(const RosterItem &item)
+{
+ QString substr;
+ switch(item.subscription().type()) {
+ case Subscription::Both:
+ substr = "<-->"; break;
+ case Subscription::From:
+ substr = " ->"; break;
+ case Subscription::To:
+ substr = "<- "; break;
+ case Subscription::Remove:
+ substr = "xxxx"; break;
+ case Subscription::None:
+ default:
+ substr = "----"; break;
+ }
+
+ QString dstr, str;
+ str.sprintf(" %s %-32s", substr.latin1(), item.jid().full().latin1());
+ if(!item.name().isEmpty())
+ str += QString(" [") + item.name() + "]";
+ str += '\n';
+
+ // Remove
+ if(item.subscription().type() == Subscription::Remove) {
+ LiveRoster::Iterator it = d->roster.find(item.jid());
+ if(it != d->roster.end()) {
+ rosterItemRemoved(*it);
+ d->roster.remove(it);
+ }
+ dstr = "Client: (Removed) ";
+ }
+ // Add/Update
+ else {
+ LiveRoster::Iterator it = d->roster.find(item.jid());
+ if(it != d->roster.end()) {
+ LiveRosterItem &i = *it;
+ i.setFlagForDelete(false);
+ i.setRosterItem(item);
+ rosterItemUpdated(i);
+ dstr = "Client: (Updated) ";
+ }
+ else {
+ LiveRosterItem i(item);
+ d->roster += i;
+
+ // signal it
+ rosterItemAdded(i);
+ dstr = "Client: (Added) ";
+ }
+ }
+
+ debug(dstr + str);
+}
+
+void Client::sendMessage(const Message &m)
+{
+ JT_Message *j = new JT_Message(rootTask(), m);
+ j->go(true);
+}
+
+void Client::sendSubscription(const Jid &jid, const QString &type)
+{
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->sub(jid, type);
+ j->go(true);
+}
+
+void Client::setPresence(const Status &s)
+{
+ JT_Presence *j = new JT_Presence(rootTask());
+ j->pres(s);
+ j->go(true);
+
+ // update our resourceList
+ ppPresence(jid(), s);
+ //ResourceList::Iterator rit = d->resourceList.find(resource());
+ //Resource &r = *rit;
+ //r.setStatus(s);
+}
+
+QString Client::OSName() const
+{
+ return d->osname;
+}
+
+QString Client::timeZone() const
+{
+ return d->tzname;
+}
+
+int Client::timeZoneOffset() const
+{
+ return d->tzoffset;
+}
+
+QString Client::clientName() const
+{
+ return d->clientName;
+}
+
+QString Client::clientVersion() const
+{
+ return d->clientVersion;
+}
+
+QString Client::capsNode() const
+{
+ return d->capsNode;
+}
+
+QString Client::capsVersion() const
+{
+ return d->capsVersion;
+}
+
+QString Client::capsExt() const
+{
+ return d->capsExt;
+}
+
+void Client::setOSName(const QString &name)
+{
+ d->osname = name;
+}
+
+void Client::setTimeZone(const QString &name, int offset)
+{
+ d->tzname = name;
+ d->tzoffset = offset;
+}
+
+void Client::setClientName(const QString &s)
+{
+ d->clientName = s;
+}
+
+void Client::setClientVersion(const QString &s)
+{
+ d->clientVersion = s;
+}
+
+void Client::setCapsNode(const QString &s)
+{
+ d->capsNode = s;
+}
+
+void Client::setCapsVersion(const QString &s)
+{
+ d->capsVersion = s;
+}
+
+DiscoItem::Identity Client::identity()
+{
+ return d->identity;
+}
+
+void Client::setIdentity(DiscoItem::Identity identity)
+{
+ d->identity = identity;
+}
+
+void Client::addExtension(const QString& ext, const Features& features)
+{
+ if (!ext.isEmpty()) {
+ d->extension_features[ext] = features;
+ d->capsExt = extensions().join(" ");
+ }
+}
+
+void Client::removeExtension(const QString& ext)
+{
+ if (d->extension_features.contains(ext)) {
+ d->extension_features.remove(ext);
+ d->capsExt = extensions().join(" ");
+ }
+}
+
+QStringList Client::extensions() const
+{
+ return d->extension_features.keys();
+}
+
+const Features& Client::extension(const QString& ext) const
+{
+ return d->extension_features[ext];
+}
+
+void Client::s5b_incomingReady()
+{
+ S5BConnection *c = d->s5bman->takeIncoming();
+ if(!c)
+ return;
+ if(!d->ftman) {
+ c->close();
+ c->deleteLater();
+ return;
+ }
+ d->ftman->s5b_incomingReady(c);
+ //d->jlman->insertStream(c);
+ //incomingJidLink();
+}
+
+void Client::ibb_incomingReady()
+{
+ IBBConnection *c = d->ibbman->takeIncoming();
+ if(!c)
+ return;
+ c->deleteLater();
+ //d->jlman->insertStream(c);
+ //incomingJidLink();
+}
+
+//----------------------------------------------------------------------------
+// Task
+//----------------------------------------------------------------------------
+class Task::TaskPrivate
+{
+public:
+ TaskPrivate() {}
+
+ QString id;
+ bool success;
+ int statusCode;
+ QString statusString;
+ Client *client;
+ bool insig, deleteme, autoDelete;
+ bool done;
+};
+
+Task::Task(Task *parent)
+:QObject(parent)
+{
+ init();
+
+ 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->insig = false;
+ d->deleteme = false;
+ d->autoDelete = false;
+ d->done = false;
+}
+
+Task *Task::parent() const
+{
+ return (Task *)QObject::parent();
+}
+
+Client *Task::client() const
+{
+ return d->client;
+}
+
+QDomDocument *Task::doc() const
+{
+ return client()->doc();
+}
+
+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(const QDomElement &x)
+{
+ const QObjectList *p = children();
+ if(!p)
+ return false;
+
+ // pass along the xml
+ QObjectListIt it(*p);
+ Task *t;
+ for(; it.current(); ++it) {
+ QObject *obj = it.current();
+ if(!obj->inherits("XMPP::Task"))
+ continue;
+
+ t = static_cast<Task*>(obj);
+ if(t->take(x))
+ return true;
+ }
+
+ return false;
+}
+
+void Task::safeDelete()
+{
+ if(d->deleteme)
+ return;
+
+ d->deleteme = true;
+ if(!d->insig)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+}
+
+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(const QDomElement &x)
+{
+ client()->send(x);
+}
+
+void Task::setSuccess(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = true;
+ d->statusCode = code;
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::setError(const QDomElement &e)
+{
+ if(!d->done) {
+ d->success = false;
+ getErrorFromElement(e, &d->statusCode, &d->statusString);
+ done();
+ }
+}
+
+void Task::setError(int code, const QString &str)
+{
+ if(!d->done) {
+ d->success = false;
+ d->statusCode = code;
+ d->statusString = str;
+ done();
+ }
+}
+
+void Task::done()
+{
+ if(d->done || d->insig)
+ return;
+ d->done = true;
+
+ if(d->deleteme || d->autoDelete)
+ d->deleteme = true;
+
+ d->insig = true;
+ finished();
+ d->insig = 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::iqVerify(const QDomElement &x, const Jid &to, const QString &id, const QString &xmlns)
+{
+ if(x.tagName() != "iq")
+ return false;
+
+ Jid from(x.attribute("from"));
+ Jid local = client()->jid();
+ Jid server = client()->host();
+
+ // empty 'from' ?
+ if(from.isEmpty()) {
+ // allowed if we are querying the server
+ if(!to.isEmpty() && !to.compare(server))
+ return false;
+ }
+ // from ourself?
+ else if(from.compare(local, false)) {
+ // allowed if we are querying ourself or the server
+ if(!to.isEmpty() && !to.compare(local, false) && !to.compare(server))
+ return false;
+ }
+ // from anywhere else?
+ else {
+ if(!from.compare(to))
+ return false;
+ }
+
+ if(!id.isEmpty()) {
+ if(x.attribute("id") != id)
+ return false;
+ }
+
+ if(!xmlns.isEmpty()) {
+ if(queryNS(x) != xmlns)
+ return false;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// LiveRosterItem
+//---------------------------------------------------------------------------
+LiveRosterItem::LiveRosterItem(const Jid &jid)
+:RosterItem(jid)
+{
+ setFlagForDelete(false);
+}
+
+LiveRosterItem::LiveRosterItem(const RosterItem &i)
+{
+ setRosterItem(i);
+ setFlagForDelete(false);
+}
+
+LiveRosterItem::~LiveRosterItem()
+{
+}
+
+void LiveRosterItem::setRosterItem(const RosterItem &i)
+{
+ setJid(i.jid());
+ setName(i.name());
+ setGroups(i.groups());
+ setSubscription(i.subscription());
+ setAsk(i.ask());
+ setIsPush(i.isPush());
+}
+
+ResourceList & LiveRosterItem::resourceList()
+{
+ return v_resourceList;
+}
+
+ResourceList::Iterator LiveRosterItem::priority()
+{
+ return v_resourceList.priority();
+}
+
+const ResourceList & LiveRosterItem::resourceList() const
+{
+ return v_resourceList;
+}
+
+ResourceList::ConstIterator LiveRosterItem::priority() const
+{
+ return v_resourceList.priority();
+}
+
+bool LiveRosterItem::isAvailable() const
+{
+ if(v_resourceList.count() > 0)
+ return true;
+ return false;
+}
+
+const Status & LiveRosterItem::lastUnavailableStatus() const
+{
+ return v_lastUnavailableStatus;
+}
+
+bool LiveRosterItem::flagForDelete() const
+{
+ return v_flagForDelete;
+}
+
+void LiveRosterItem::setLastUnavailableStatus(const Status &s)
+{
+ v_lastUnavailableStatus = s;
+}
+
+void LiveRosterItem::setFlagForDelete(bool b)
+{
+ v_flagForDelete = b;
+}
+
+//---------------------------------------------------------------------------
+// LiveRoster
+//---------------------------------------------------------------------------
+LiveRoster::LiveRoster()
+:QValueList<LiveRosterItem>()
+{
+}
+
+LiveRoster::~LiveRoster()
+{
+}
+
+void LiveRoster::flagAllForDelete()
+{
+ for(Iterator it = begin(); it != end(); ++it)
+ (*it).setFlagForDelete(true);
+}
+
+LiveRoster::Iterator LiveRoster::find(const Jid &j, bool compareRes)
+{
+ Iterator it;
+ for(it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j, compareRes))
+ break;
+ }
+ return it;
+}
+
+LiveRoster::ConstIterator LiveRoster::find(const Jid &j, bool compareRes) const
+{
+ ConstIterator it;
+ for(it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j, compareRes))
+ break;
+ }
+ return it;
+}
+
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp
new file mode 100644
index 00000000..1e457584
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/types.cpp
@@ -0,0 +1,1876 @@
+/*
+ * types.cpp - IM data types
+ * 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"im.h"
+#include "protocol.h"
+#include<qmap.h>
+#include<qapplication.h>
+
+#define NS_XML "http://www.w3.org/XML/1998/namespace"
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+static QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}
+
+static QDateTime stamp2TS(const QString &ts)
+{
+ if(ts.length() != 17)
+ return QDateTime();
+
+ int year = ts.mid(0,4).toInt();
+ int month = ts.mid(4,2).toInt();
+ int day = ts.mid(6,2).toInt();
+
+ int hour = ts.mid(9,2).toInt();
+ int min = ts.mid(12,2).toInt();
+ int sec = ts.mid(15,2).toInt();
+
+ QDate xd;
+ xd.setYMD(year, month, day);
+ if(!xd.isValid())
+ return QDateTime();
+
+ QTime xt;
+ xt.setHMS(hour, min, sec);
+ if(!xt.isValid())
+ return QDateTime();
+
+ return QDateTime(xd, xt);
+}
+
+/*static QString TS2stamp(const QDateTime &d)
+{
+ QString str;
+
+ str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+ d.date().year(),
+ d.date().month(),
+ d.date().day(),
+ d.time().hour(),
+ d.time().minute(),
+ d.time().second());
+
+ return str;
+}*/
+
+namespace XMPP
+{
+
+//----------------------------------------------------------------------------
+// Url
+//----------------------------------------------------------------------------
+class Url::Private
+{
+public:
+ QString url;
+ QString desc;
+};
+
+//! \brief Construct Url object with a given URL and Description.
+//!
+//! This function will construct a Url object.
+//! \param QString - url (default: empty string)
+//! \param QString - description of url (default: empty string)
+//! \sa setUrl() setDesc()
+Url::Url(const QString &url, const QString &desc)
+{
+ d = new Private;
+ d->url = url;
+ d->desc = desc;
+}
+
+//! \brief Construct Url object.
+//!
+//! Overloaded constructor which will constructs a exact copy of the Url object that was passed to the constructor.
+//! \param Url - Url Object
+Url::Url(const Url &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+//! \brief operator overloader needed for d pointer (Internel).
+Url & Url::operator=(const Url &from)
+{
+ *d = *from.d;
+ return *this;
+}
+
+//! \brief destroy Url object.
+Url::~Url()
+{
+ delete d;
+}
+
+//! \brief Get url information.
+//!
+//! Returns url information.
+QString Url::url() const
+{
+ return d->url;
+}
+
+//! \brief Get Description information.
+//!
+//! Returns desction of the URL.
+QString Url::desc() const
+{
+ return d->desc;
+}
+
+//! \brief Set Url information.
+//!
+//! Set url information.
+//! \param url - url string (eg: http://psi.affinix.com/)
+void Url::setUrl(const QString &url)
+{
+ d->url = url;
+}
+
+//! \brief Set Description information.
+//!
+//! Set description of the url.
+//! \param desc - description of url
+void Url::setDesc(const QString &desc)
+{
+ d->desc = desc;
+}
+
+//----------------------------------------------------------------------------
+// Message
+//----------------------------------------------------------------------------
+class Message::Private
+{
+public:
+ Jid to, from;
+ QString id, type, lang;
+
+ StringMap subject, body, xHTMLBody;
+
+ QString thread;
+ Stanza::Error error;
+
+ // extensions
+ QDateTime timeStamp;
+ UrlList urlList;
+ QValueList<MsgEvent> eventList;
+ QString eventId;
+ QString xencrypted, invite;
+
+ bool spooled, wasEncrypted;
+};
+
+//! \brief Constructs Message with given Jid information.
+//!
+//! This function will construct a Message container.
+//! \param to - specify reciver (default: empty string)
+Message::Message(const Jid &to)
+{
+ d = new Private;
+ d->to = to;
+ d->spooled = false;
+ d->wasEncrypted = false;
+ /*d->flag = false;
+ d->spooled = false;
+ d->wasEncrypted = false;
+ d->errorCode = -1;*/
+}
+
+//! \brief Constructs a copy of Message object
+//!
+//! Overloaded constructor which will constructs a exact copy of the Message
+//! object that was passed to the constructor.
+//! \param from - Message object you want to copy
+Message::Message(const Message &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+//! \brief Required for internel use.
+Message & Message::operator=(const Message &from)
+{
+ *d = *from.d;
+ return *this;
+}
+
+//! \brief Destroy Message object.
+Message::~Message()
+{
+ delete d;
+}
+
+//! \brief Return receiver's Jid information.
+Jid Message::to() const
+{
+ return d->to;
+}
+
+//! \brief Return sender's Jid information.
+Jid Message::from() const
+{
+ return d->from;
+}
+
+QString Message::id() const
+{
+ return d->id;
+}
+
+//! \brief Return type information
+QString Message::type() const
+{
+ return d->type;
+}
+
+QString Message::lang() const
+{
+ return d->lang;
+}
+
+//! \brief Return subject information.
+QString Message::subject(const QString &lang) const
+{
+ return d->subject[lang];
+}
+
+//! \brief Return body information.
+//!
+//! This function will return a plain text or the Richtext version if it
+//! it exists.
+//! \param rich - Returns richtext if true and plain text if false. (default: false)
+//! \note Richtext is in Qt's richtext format and not in xhtml.
+QString Message::body(const QString &lang) const
+{
+ return d->body[lang];
+}
+
+QString Message::xHTMLBody(const QString &lang) const
+{
+ return d->xHTMLBody[lang];
+}
+
+QString Message::thread() const
+{
+ return d->thread;
+}
+
+Stanza::Error Message::error() const
+{
+ return d->error;
+}
+
+//! \brief Set receivers information
+//!
+//! \param to - Receivers Jabber id
+void Message::setTo(const Jid &j)
+{
+ d->to = j;
+ //d->flag = false;
+}
+
+void Message::setFrom(const Jid &j)
+{
+ d->from = j;
+ //d->flag = false;
+}
+
+void Message::setId(const QString &s)
+{
+ d->id = s;
+}
+
+//! \brief Set Type of message
+//!
+//! \param type - type of message your going to send
+void Message::setType(const QString &s)
+{
+ d->type = s;
+ //d->flag = false;
+}
+
+void Message::setLang(const QString &s)
+{
+ d->lang = s;
+}
+
+//! \brief Set subject
+//!
+//! \param subject - Subject information
+void Message::setSubject(const QString &s, const QString &lang)
+{
+ d->subject[lang] = s;
+ //d->flag = false;
+}
+
+//! \brief Set body
+//!
+//! \param body - body information
+//! \param rich - set richtext if true and set plaintext if false.
+//! \note Richtext support will be implemented in the future... Sorry.
+void Message::setBody(const QString &s, const QString &lang)
+{
+ d->body[lang] = s;
+}
+
+void Message::setXHTMLBody(const QString &s, const QString &lang, const QString &attr)
+{
+ //ugly but needed if s is not a node but a list of leaf
+
+ QString content = "<body xmlns='" + QString(NS_XHTML) + "' "+attr+" >\n" + s +"\n</body>";
+ d->xHTMLBody[lang] = content;
+}
+
+void Message::setThread(const QString &s)
+{
+ d->thread = s;
+}
+
+void Message::setError(const Stanza::Error &err)
+{
+ d->error = err;
+}
+
+QDateTime Message::timeStamp() const
+{
+ return d->timeStamp;
+}
+
+void Message::setTimeStamp(const QDateTime &ts)
+{
+ d->timeStamp = ts;
+}
+
+//! \brief Return list of urls attached to message.
+UrlList Message::urlList() const
+{
+ return d->urlList;
+}
+
+//! \brief Add Url to the url list.
+//!
+//! \param url - url to append
+void Message::urlAdd(const Url &u)
+{
+ d->urlList += u;
+}
+
+//! \brief clear out the url list.
+void Message::urlsClear()
+{
+ d->urlList.clear();
+}
+
+//! \brief Set urls to send
+//!
+//! \param urlList - list of urls to send
+void Message::setUrlList(const UrlList &list)
+{
+ d->urlList = list;
+}
+
+QString Message::eventId() const
+{
+ return d->eventId;
+}
+
+void Message::setEventId(const QString& id)
+{
+ d->eventId = id;
+}
+
+bool Message::containsEvents() const
+{
+ return !d->eventList.isEmpty();
+}
+
+bool Message::containsEvent(MsgEvent e) const
+{
+ return d->eventList.contains(e);
+}
+
+void Message::addEvent(MsgEvent e)
+{
+ if (!d->eventList.contains(e)) {
+ if (e == CancelEvent || containsEvent(CancelEvent))
+ d->eventList.clear(); // Reset list
+ d->eventList += e;
+ }
+}
+
+QString Message::xencrypted() const
+{
+ return d->xencrypted;
+}
+
+void Message::setXEncrypted(const QString &s)
+{
+ d->xencrypted = s;
+}
+
+QString Message::invite() const
+{
+ return d->invite;
+}
+
+void Message::setInvite(const QString &s)
+{
+ d->invite = s;
+}
+
+bool Message::spooled() const
+{
+ return d->spooled;
+}
+
+void Message::setSpooled(bool b)
+{
+ d->spooled = b;
+}
+
+bool Message::wasEncrypted() const
+{
+ return d->wasEncrypted;
+}
+
+void Message::setWasEncrypted(bool b)
+{
+ d->wasEncrypted = b;
+}
+
+Stanza Message::toStanza(Stream *stream) const
+{
+ Stanza s = stream->createStanza(Stanza::Message, d->to, d->type);
+ if(!d->from.isEmpty())
+ s.setFrom(d->from);
+ if(!d->id.isEmpty())
+ s.setId(d->id);
+ if(!d->lang.isEmpty())
+ s.setLang(d->lang);
+
+ StringMap::ConstIterator it;
+ for(it = d->subject.begin(); it != d->subject.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement e = s.createTextElement(s.baseNS(), "subject", str);
+ if(!it.key().isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", it.key());
+ s.appendChild(e);
+ }
+ }
+ for(it = d->body.begin(); it != d->body.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement e = s.createTextElement(s.baseNS(), "body", str);
+ if(!it.key().isEmpty())
+ e.setAttributeNS(NS_XML, "xml:lang", it.key());
+ s.appendChild(e);
+ }
+ }
+ if ( !d->xHTMLBody.isEmpty()) {
+ QDomElement parent = s.createElement(s.xhtmlImNS(), "html");
+ for(it = d->xHTMLBody.begin(); it != d->xHTMLBody.end(); ++it) {
+ const QString &str = it.data();
+ if(!str.isEmpty()) {
+ QDomElement child = s.createXHTMLElement(str);
+ if(!it.key().isEmpty())
+ child.setAttributeNS(NS_XML, "xml:lang", it.key());
+ parent.appendChild(child);
+ }
+ }
+ s.appendChild(parent);
+ }
+ if(d->type == "error")
+ s.setError(d->error);
+
+ // timestamp
+ /*if(!d->timeStamp.isNull()) {
+ QDomElement e = s.createElement("jabber:x:delay", "x");
+ e.setAttribute("stamp", TS2stamp(d->timeStamp));
+ s.appendChild(e);
+ }*/
+
+ // urls
+ for(QValueList<Url>::ConstIterator uit = d->urlList.begin(); uit != d->urlList.end(); ++uit) {
+ QDomElement x = s.createElement("jabber:x:oob", "x");
+ x.appendChild(s.createTextElement("jabber:x:oob", "url", (*uit).url()));
+ if(!(*uit).desc().isEmpty())
+ x.appendChild(s.createTextElement("jabber:x:oob", "desc", (*uit).desc()));
+ s.appendChild(x);
+ }
+
+ // events
+ if (!d->eventList.isEmpty()) {
+ QDomElement x = s.createElement("jabber:x:event", "x");
+
+ if (d->body.isEmpty()) {
+ if (d->eventId.isEmpty())
+ x.appendChild(s.createElement("jabber:x:event","id"));
+ else
+ x.appendChild(s.createTextElement("jabber:x:event","id",d->eventId));
+ }
+ else if (d->type=="chat" || d->type=="groupchat")
+ s.appendChild( s.createElement(NS_CHATSTATES , "active" ) );
+
+ bool need_x_event=false;
+ for(QValueList<MsgEvent>::ConstIterator ev = d->eventList.begin(); ev != d->eventList.end(); ++ev) {
+ switch (*ev) {
+ case OfflineEvent:
+ x.appendChild(s.createElement("jabber:x:event", "offline"));
+ need_x_event=true;
+ break;
+ case DeliveredEvent:
+ x.appendChild(s.createElement("jabber:x:event", "delivered"));
+ need_x_event=true;
+ break;
+ case DisplayedEvent:
+ x.appendChild(s.createElement("jabber:x:event", "displayed"));
+ need_x_event=true;
+ break;
+ case ComposingEvent:
+ x.appendChild(s.createElement("jabber:x:event", "composing"));
+ need_x_event=true;
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "composing" ) );
+ break;
+ case CancelEvent:
+ need_x_event=true;
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "paused" ) );
+ break;
+ case InactiveEvent:
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "inactive" ) );
+ break;
+ case GoneEvent:
+ if (d->body.isEmpty() && (d->type=="chat" || d->type=="groupchat") )
+ s.appendChild( s.createElement(NS_CHATSTATES , "gone" ) );
+ break;
+ }
+ }
+ if(need_x_event) //we don't need to have the (empty) x:event element if this is only <gone> or <inactive>
+ s.appendChild(x);
+ }
+
+
+ // xencrypted
+ if(!d->xencrypted.isEmpty())
+ s.appendChild(s.createTextElement("jabber:x:encrypted", "x", d->xencrypted));
+
+ // invite
+ if(!d->invite.isEmpty()) {
+ QDomElement e = s.createElement("jabber:x:conference", "x");
+ e.setAttribute("jid", d->invite);
+ s.appendChild(e);
+ }
+
+ return s;
+}
+
+bool Message::fromStanza(const Stanza &s, int timeZoneOffset)
+{
+ if(s.kind() != Stanza::Message)
+ return false;
+
+ setTo(s.to());
+ setFrom(s.from());
+ setId(s.id());
+ setType(s.type());
+ setLang(s.lang());
+
+ d->subject.clear();
+ d->body.clear();
+ d->thread = QString();
+ d->eventList.clear();
+
+ QDomElement root = s.element();
+
+ QDomNodeList nl = root.childNodes();
+ uint n;
+ for(n = 0; n < nl.count(); ++n) {
+ QDomNode i = nl.item(n);
+ if(i.isElement()) {
+ QDomElement e = i.toElement();
+ if(e.namespaceURI() == s.baseNS()) {
+ if(e.tagName() == "subject") {
+ QString lang = e.attributeNS(NS_XML, "lang", "");
+ d->subject[lang] = e.text();
+ }
+ else if(e.tagName() == "body") {
+ QString lang = e.attributeNS(NS_XML, "lang", "");
+ d->body[lang] = e.text();
+ }
+ else if(e.tagName() == "thread")
+ d->thread = e.text();
+ }
+ else if (e.namespaceURI() == s.xhtmlImNS()) {
+ if (e.tagName() == "html") {
+ QDomNodeList htmlNL= e.childNodes();
+ for (unsigned int x = 0; x < htmlNL.count(); x++) {
+ QDomElement i = htmlNL.item(x).toElement();
+
+ if (i.tagName() == "body") {
+ QDomDocument RichText;
+ QString lang = i.attributeNS(NS_XML, "lang", "");
+ RichText.appendChild(i);
+ d-> xHTMLBody[lang] = RichText.toString();
+ }
+ }
+ }
+ }
+ else if (e.namespaceURI() == NS_CHATSTATES)
+ {
+ if(e.tagName() == "active")
+ {
+ //like in JEP-0022 we let the client know that we can receive ComposingEvent
+ // (we can do that according to §4.6 of the JEP-0085)
+ d->eventList += ComposingEvent;
+ d->eventList += InactiveEvent;
+ d->eventList += GoneEvent;
+ }
+ else if (e.tagName() == "composing")
+ {
+ d->eventList += ComposingEvent;
+ }
+ else if (e.tagName() == "paused")
+ {
+ d->eventList += CancelEvent;
+ }
+ else if (e.tagName() == "inactive")
+ {
+ d->eventList += InactiveEvent;
+ }
+ else if (e.tagName() == "gone")
+ {
+ d->eventList += GoneEvent;
+ }
+ }
+ else {
+ //printf("extension element: [%s]\n", e.tagName().latin1());
+ }
+ }
+ }
+
+ if(s.type() == "error")
+ d->error = s.error();
+
+ // timestamp
+ QDomElement t = root.elementsByTagNameNS("jabber:x:delay", "x").item(0).toElement();
+ if(!t.isNull()) {
+ d->timeStamp = stamp2TS(t.attribute("stamp"));
+ d->timeStamp = d->timeStamp.addSecs(timeZoneOffset * 3600);
+ d->spooled = true;
+ }
+ else {
+ d->timeStamp = QDateTime::currentDateTime();
+ d->spooled = false;
+ }
+
+ // urls
+ d->urlList.clear();
+ nl = root.elementsByTagNameNS("jabber:x:oob", "x");
+ for(n = 0; n < nl.count(); ++n) {
+ QDomElement t = nl.item(n).toElement();
+ Url u;
+ u.setUrl(t.elementsByTagName("url").item(0).toElement().text());
+ u.setDesc(t.elementsByTagName("desc").item(0).toElement().text());
+ d->urlList += u;
+ }
+
+ // events
+ nl = root.elementsByTagNameNS("jabber:x:event", "x");
+ if (nl.count()) {
+ nl = nl.item(0).childNodes();
+ for(n = 0; n < nl.count(); ++n) {
+ QString evtag = nl.item(n).toElement().tagName();
+ if (evtag == "id") {
+ d->eventId = nl.item(n).toElement().text();
+ }
+ else if (evtag == "displayed")
+ d->eventList += DisplayedEvent;
+ else if (evtag == "composing")
+ d->eventList += ComposingEvent;
+ else if (evtag == "delivered")
+ d->eventList += DeliveredEvent;
+ else if (evtag == "offline")
+ d->eventList += OfflineEvent;
+ }
+ if (d->eventList.isEmpty())
+ d->eventList += CancelEvent;
+ }
+
+ // xencrypted
+ t = root.elementsByTagNameNS("jabber:x:encrypted", "x").item(0).toElement();
+ if(!t.isNull())
+ d->xencrypted = t.text();
+ else
+ d->xencrypted = QString();
+
+ // invite
+ t = root.elementsByTagNameNS("jabber:x:conference", "x").item(0).toElement();
+ if(!t.isNull())
+ d->invite = t.attribute("jid");
+ else
+ d->invite = QString();
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// Subscription
+//---------------------------------------------------------------------------
+Subscription::Subscription(SubType type)
+{
+ value = type;
+}
+
+int Subscription::type() const
+{
+ return value;
+}
+
+QString Subscription::toString() const
+{
+ switch(value) {
+ case Remove:
+ return "remove";
+ case Both:
+ return "both";
+ case From:
+ return "from";
+ case To:
+ return "to";
+ case None:
+ default:
+ return "none";
+ }
+}
+
+bool Subscription::fromString(const QString &s)
+{
+ if(s == "remove")
+ value = Remove;
+ else if(s == "both")
+ value = Both;
+ else if(s == "from")
+ value = From;
+ else if(s == "to")
+ value = To;
+ else if(s == "none")
+ value = None;
+ else
+ return false;
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Status
+//---------------------------------------------------------------------------
+Status::Status(const QString &show, const QString &status, int priority, bool available)
+{
+ v_isAvailable = available;
+ v_show = show;
+ v_status = status;
+ v_priority = priority;
+ v_timeStamp = QDateTime::currentDateTime();
+ v_isInvisible = false;
+ ecode = -1;
+}
+
+Status::~Status()
+{
+}
+
+bool Status::hasError() const
+{
+ return (ecode != -1);
+}
+
+void Status::setError(int code, const QString &str)
+{
+ ecode = code;
+ estr = str;
+}
+
+void Status::setIsAvailable(bool available)
+{
+ v_isAvailable = available;
+}
+
+void Status::setIsInvisible(bool invisible)
+{
+ v_isInvisible = invisible;
+}
+
+void Status::setPriority(int x)
+{
+ v_priority = x;
+}
+
+void Status::setShow(const QString & _show)
+{
+ v_show = _show;
+}
+
+void Status::setStatus(const QString & _status)
+{
+ v_status = _status;
+}
+
+void Status::setTimeStamp(const QDateTime & _timestamp)
+{
+ v_timeStamp = _timestamp;
+}
+
+void Status::setKeyID(const QString &key)
+{
+ v_key = key;
+}
+
+void Status::setXSigned(const QString &s)
+{
+ v_xsigned = s;
+}
+
+void Status::setSongTitle(const QString & _songtitle)
+{
+ v_songTitle = _songtitle;
+}
+
+void Status::setCapsNode(const QString & _capsNode)
+{
+ v_capsNode = _capsNode;
+}
+
+void Status::setCapsVersion(const QString & _capsVersion)
+{
+ v_capsVersion = _capsVersion;
+}
+
+void Status::setCapsExt(const QString & _capsExt)
+{
+ v_capsExt = _capsExt;
+}
+
+bool Status::isAvailable() const
+{
+ return v_isAvailable;
+}
+
+bool Status::isAway() const
+{
+ if(v_show == "away" || v_show == "xa" || v_show == "dnd")
+ return true;
+
+ return false;
+}
+
+bool Status::isInvisible() const
+{
+ return v_isInvisible;
+}
+
+int Status::priority() const
+{
+ return v_priority;
+}
+
+const QString & Status::show() const
+{
+ return v_show;
+}
+const QString & Status::status() const
+{
+ return v_status;
+}
+
+QDateTime Status::timeStamp() const
+{
+ return v_timeStamp;
+}
+
+const QString & Status::keyID() const
+{
+ return v_key;
+}
+
+const QString & Status::xsigned() const
+{
+ return v_xsigned;
+}
+
+const QString & Status::songTitle() const
+{
+ return v_songTitle;
+}
+
+const QString & Status::capsNode() const
+{
+ return v_capsNode;
+}
+
+const QString & Status::capsVersion() const
+{
+ return v_capsVersion;
+}
+
+const QString & Status::capsExt() const
+{
+ return v_capsExt;
+}
+
+int Status::errorCode() const
+{
+ return ecode;
+}
+
+const QString & Status::errorString() const
+{
+ return estr;
+}
+
+
+//---------------------------------------------------------------------------
+// Resource
+//---------------------------------------------------------------------------
+Resource::Resource(const QString &name, const Status &stat)
+{
+ v_name = name;
+ v_status = stat;
+}
+
+Resource::~Resource()
+{
+}
+
+const QString & Resource::name() const
+{
+ return v_name;
+}
+
+int Resource::priority() const
+{
+ return v_status.priority();
+}
+
+const Status & Resource::status() const
+{
+ return v_status;
+}
+
+void Resource::setName(const QString & _name)
+{
+ v_name = _name;
+}
+
+void Resource::setStatus(const Status & _status)
+{
+ v_status = _status;
+}
+
+
+//---------------------------------------------------------------------------
+// ResourceList
+//---------------------------------------------------------------------------
+ResourceList::ResourceList()
+:QValueList<Resource>()
+{
+}
+
+ResourceList::~ResourceList()
+{
+}
+
+ResourceList::Iterator ResourceList::find(const QString & _find)
+{
+ for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+ if((*it).name() == _find)
+ return it;
+ }
+
+ return end();
+}
+
+ResourceList::Iterator ResourceList::priority()
+{
+ ResourceList::Iterator highest = end();
+
+ for(ResourceList::Iterator it = begin(); it != end(); ++it) {
+ if(highest == end() || (*it).priority() > (*highest).priority())
+ highest = it;
+ }
+
+ return highest;
+}
+
+ResourceList::ConstIterator ResourceList::find(const QString & _find) const
+{
+ for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).name() == _find)
+ return it;
+ }
+
+ return end();
+}
+
+ResourceList::ConstIterator ResourceList::priority() const
+{
+ ResourceList::ConstIterator highest = end();
+
+ for(ResourceList::ConstIterator it = begin(); it != end(); ++it) {
+ if(highest == end() || (*it).priority() > (*highest).priority())
+ highest = it;
+ }
+
+ return highest;
+}
+
+
+//---------------------------------------------------------------------------
+// RosterItem
+//---------------------------------------------------------------------------
+RosterItem::RosterItem(const Jid &_jid)
+{
+ v_jid = _jid;
+}
+
+RosterItem::~RosterItem()
+{
+}
+
+const Jid & RosterItem::jid() const
+{
+ return v_jid;
+}
+
+const QString & RosterItem::name() const
+{
+ return v_name;
+}
+
+const QStringList & RosterItem::groups() const
+{
+ return v_groups;
+}
+
+const Subscription & RosterItem::subscription() const
+{
+ return v_subscription;
+}
+
+const QString & RosterItem::ask() const
+{
+ return v_ask;
+}
+
+bool RosterItem::isPush() const
+{
+ return v_push;
+}
+
+bool RosterItem::inGroup(const QString &g) const
+{
+ for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+ if(*it == g)
+ return true;
+ }
+ return false;
+}
+
+void RosterItem::setJid(const Jid &_jid)
+{
+ v_jid = _jid;
+}
+
+void RosterItem::setName(const QString &_name)
+{
+ v_name = _name;
+}
+
+void RosterItem::setGroups(const QStringList &_groups)
+{
+ v_groups = _groups;
+}
+
+void RosterItem::setSubscription(const Subscription &type)
+{
+ v_subscription = type;
+}
+
+void RosterItem::setAsk(const QString &_ask)
+{
+ v_ask = _ask;
+}
+
+void RosterItem::setIsPush(bool b)
+{
+ v_push = b;
+}
+
+bool RosterItem::addGroup(const QString &g)
+{
+ if(inGroup(g))
+ return false;
+
+ v_groups += g;
+ return true;
+}
+
+bool RosterItem::removeGroup(const QString &g)
+{
+ for(QStringList::Iterator it = v_groups.begin(); it != v_groups.end(); ++it) {
+ if(*it == g) {
+ v_groups.remove(it);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QDomElement RosterItem::toXml(QDomDocument *doc) const
+{
+ QDomElement item = doc->createElement("item");
+ item.setAttribute("jid", v_jid.full());
+ item.setAttribute("name", v_name);
+ item.setAttribute("subscription", v_subscription.toString());
+ if(!v_ask.isEmpty())
+ item.setAttribute("ask", v_ask);
+ for(QStringList::ConstIterator it = v_groups.begin(); it != v_groups.end(); ++it)
+ item.appendChild(textTag(doc, "group", *it));
+
+ return item;
+}
+
+bool RosterItem::fromXml(const QDomElement &item)
+{
+ if(item.tagName() != "item")
+ return false;
+ Jid j(item.attribute("jid"));
+ if(!j.isValid())
+ return false;
+ QString na = item.attribute("name");
+ Subscription s;
+ if(!s.fromString(item.attribute("subscription")) )
+ return false;
+ QStringList g;
+ for(QDomNode n = item.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == "group")
+ g += tagContent(i);
+ }
+ QString a = item.attribute("ask");
+
+ v_jid = j;
+ v_name = na;
+ v_subscription = s;
+ v_groups = g;
+ v_ask = a;
+
+ return true;
+}
+
+
+//---------------------------------------------------------------------------
+// Roster
+//---------------------------------------------------------------------------
+Roster::Roster()
+:QValueList<RosterItem>()
+{
+}
+
+Roster::~Roster()
+{
+}
+
+Roster::Iterator Roster::find(const Jid &j)
+{
+ for(Roster::Iterator it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j))
+ return it;
+ }
+
+ return end();
+}
+
+Roster::ConstIterator Roster::find(const Jid &j) const
+{
+ for(Roster::ConstIterator it = begin(); it != end(); ++it) {
+ if((*it).jid().compare(j))
+ return it;
+ }
+
+ return end();
+}
+
+
+//---------------------------------------------------------------------------
+// FormField
+//---------------------------------------------------------------------------
+FormField::FormField(const QString &type, const QString &value)
+{
+ v_type = misc;
+ if(!type.isEmpty()) {
+ int x = tagNameToType(type);
+ if(x != -1)
+ v_type = x;
+ }
+ v_value = value;
+}
+
+FormField::~FormField()
+{
+}
+
+int FormField::type() const
+{
+ return v_type;
+}
+
+QString FormField::realName() const
+{
+ return typeToTagName(v_type);
+}
+
+QString FormField::fieldName() const
+{
+ switch(v_type) {
+ case username: return QObject::tr("Username");
+ case nick: return QObject::tr("Nickname");
+ case password: return QObject::tr("Password");
+ case name: return QObject::tr("Name");
+ case first: return QObject::tr("First Name");
+ case last: return QObject::tr("Last Name");
+ case email: return QObject::tr("E-mail");
+ case address: return QObject::tr("Address");
+ case city: return QObject::tr("City");
+ case state: return QObject::tr("State");
+ case zip: return QObject::tr("Zipcode");
+ case phone: return QObject::tr("Phone");
+ case url: return QObject::tr("URL");
+ case date: return QObject::tr("Date");
+ case misc: return QObject::tr("Misc");
+ default: return "";
+ };
+}
+
+bool FormField::isSecret() const
+{
+ return (type() == password);
+}
+
+const QString & FormField::value() const
+{
+ return v_value;
+}
+
+void FormField::setType(int x)
+{
+ v_type = x;
+}
+
+bool FormField::setType(const QString &in)
+{
+ int x = tagNameToType(in);
+ if(x == -1)
+ return false;
+
+ v_type = x;
+ return true;
+}
+
+void FormField::setValue(const QString &in)
+{
+ v_value = in;
+}
+
+int FormField::tagNameToType(const QString &in) const
+{
+ if(!in.compare("username")) return username;
+ if(!in.compare("nick")) return nick;
+ if(!in.compare("password")) return password;
+ if(!in.compare("name")) return name;
+ if(!in.compare("first")) return first;
+ if(!in.compare("last")) return last;
+ if(!in.compare("email")) return email;
+ if(!in.compare("address")) return address;
+ if(!in.compare("city")) return city;
+ if(!in.compare("state")) return state;
+ if(!in.compare("zip")) return zip;
+ if(!in.compare("phone")) return phone;
+ if(!in.compare("url")) return url;
+ if(!in.compare("date")) return date;
+ if(!in.compare("misc")) return misc;
+
+ return -1;
+}
+
+QString FormField::typeToTagName(int type) const
+{
+ switch(type) {
+ case username: return "username";
+ case nick: return "nick";
+ case password: return "password";
+ case name: return "name";
+ case first: return "first";
+ case last: return "last";
+ case email: return "email";
+ case address: return "address";
+ case city: return "city";
+ case state: return "state";
+ case zip: return "zipcode";
+ case phone: return "phone";
+ case url: return "url";
+ case date: return "date";
+ case misc: return "misc";
+ default: return "";
+ };
+}
+
+
+//---------------------------------------------------------------------------
+// Form
+//---------------------------------------------------------------------------
+Form::Form(const Jid &j)
+:QValueList<FormField>()
+{
+ setJid(j);
+}
+
+Form::~Form()
+{
+}
+
+Jid Form::jid() const
+{
+ return v_jid;
+}
+
+QString Form::instructions() const
+{
+ return v_instructions;
+}
+
+QString Form::key() const
+{
+ return v_key;
+}
+
+void Form::setJid(const Jid &j)
+{
+ v_jid = j;
+}
+
+void Form::setInstructions(const QString &s)
+{
+ v_instructions = s;
+}
+
+void Form::setKey(const QString &s)
+{
+ v_key = s;
+}
+
+
+//---------------------------------------------------------------------------
+// SearchResult
+//---------------------------------------------------------------------------
+SearchResult::SearchResult(const Jid &jid)
+{
+ setJid(jid);
+}
+
+SearchResult::~SearchResult()
+{
+}
+
+const Jid & SearchResult::jid() const
+{
+ return v_jid;
+}
+
+const QString & SearchResult::nick() const
+{
+ return v_nick;
+}
+
+const QString & SearchResult::first() const
+{
+ return v_first;
+}
+
+const QString & SearchResult::last() const
+{
+ return v_last;
+}
+
+const QString & SearchResult::email() const
+{
+ return v_email;
+}
+
+void SearchResult::setJid(const Jid &jid)
+{
+ v_jid = jid;
+}
+
+void SearchResult::setNick(const QString &nick)
+{
+ v_nick = nick;
+}
+
+void SearchResult::setFirst(const QString &first)
+{
+ v_first = first;
+}
+
+void SearchResult::setLast(const QString &last)
+{
+ v_last = last;
+}
+
+void SearchResult::setEmail(const QString &email)
+{
+ v_email = email;
+}
+
+//---------------------------------------------------------------------------
+// Features
+//---------------------------------------------------------------------------
+
+Features::Features()
+{
+}
+
+Features::Features(const QStringList &l)
+{
+ setList(l);
+}
+
+Features::Features(const QString &str)
+{
+ QStringList l;
+ l << str;
+
+ setList(l);
+}
+
+Features::~Features()
+{
+}
+
+QStringList Features::list() const
+{
+ return _list;
+}
+
+void Features::setList(const QStringList &l)
+{
+ _list = l;
+}
+
+bool Features::test(const QStringList &ns) const
+{
+ QStringList::ConstIterator it = ns.begin();
+ for ( ; it != ns.end(); ++it)
+ if ( _list.find( *it ) != _list.end() )
+ return true;
+
+ return false;
+}
+
+#define FID_REGISTER "jabber:iq:register"
+bool Features::canRegister() const
+{
+ QStringList ns;
+ ns << FID_REGISTER;
+
+ return test(ns);
+}
+
+#define FID_SEARCH "jabber:iq:search"
+bool Features::canSearch() const
+{
+ QStringList ns;
+ ns << FID_SEARCH;
+
+ return test(ns);
+}
+
+#define FID_XHTML "http://jabber.org/protocol/xhtml-im"
+bool Features::canXHTML() const
+{
+ QStringList ns;
+
+ ns << FID_XHTML;
+
+ return test(ns);
+}
+
+#define FID_GROUPCHAT "jabber:iq:conference"
+bool Features::canGroupchat() const
+{
+ QStringList ns;
+ ns << "http://jabber.org/protocol/muc";
+ ns << FID_GROUPCHAT;
+
+ return test(ns);
+}
+
+#define FID_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
+bool Features::canVoice() const
+{
+ QStringList ns;
+ ns << FID_VOICE;
+
+ return test(ns);
+}
+
+#define FID_GATEWAY "jabber:iq:gateway"
+bool Features::isGateway() const
+{
+ QStringList ns;
+ ns << FID_GATEWAY;
+
+ return test(ns);
+}
+
+#define FID_DISCO "http://jabber.org/protocol/disco"
+bool Features::canDisco() const
+{
+ QStringList ns;
+ ns << FID_DISCO;
+ ns << "http://jabber.org/protocol/disco#info";
+ ns << "http://jabber.org/protocol/disco#items";
+
+ return test(ns);
+}
+
+#define FID_VCARD "vcard-temp"
+bool Features::haveVCard() const
+{
+ QStringList ns;
+ ns << FID_VCARD;
+
+ return test(ns);
+}
+
+// custom Psi acitons
+#define FID_ADD "psi:add"
+
+class Features::FeatureName : public QObject
+{
+ Q_OBJECT
+public:
+ FeatureName()
+ : QObject(qApp)
+ {
+ id2s[FID_Invalid] = tr("ERROR: Incorrect usage of Features class");
+ id2s[FID_None] = tr("None");
+ id2s[FID_Register] = tr("Register");
+ id2s[FID_Search] = tr("Search");
+ id2s[FID_Groupchat] = tr("Groupchat");
+ id2s[FID_Gateway] = tr("Gateway");
+ id2s[FID_Disco] = tr("Service Discovery");
+ id2s[FID_VCard] = tr("VCard");
+
+ // custom Psi actions
+ id2s[FID_Add] = tr("Add to roster");
+
+ // compute reverse map
+ //QMap<QString, long>::Iterator it = id2s.begin();
+ //for ( ; it != id2s.end(); ++it)
+ // s2id[it.data()] = it.key();
+
+ id2f[FID_Register] = FID_REGISTER;
+ id2f[FID_Search] = FID_SEARCH;
+ id2f[FID_Groupchat] = FID_GROUPCHAT;
+ id2f[FID_Gateway] = FID_GATEWAY;
+ id2f[FID_Disco] = FID_DISCO;
+ id2f[FID_VCard] = FID_VCARD;
+
+ // custom Psi actions
+ id2f[FID_Add] = FID_ADD;
+ }
+
+ //QMap<QString, long> s2id;
+ QMap<long, QString> id2s;
+ QMap<long, QString> id2f;
+};
+
+static Features::FeatureName *featureName = 0;
+
+long Features::id() const
+{
+ if ( _list.count() > 1 )
+ return FID_Invalid;
+ else if ( canRegister() )
+ return FID_Register;
+ else if ( canSearch() )
+ return FID_Search;
+ else if ( canGroupchat() )
+ return FID_Groupchat;
+ else if ( isGateway() )
+ return FID_Gateway;
+ else if ( canDisco() )
+ return FID_Disco;
+ else if ( haveVCard() )
+ return FID_VCard;
+ else if ( test(FID_ADD) )
+ return FID_Add;
+
+ return FID_None;
+}
+
+long Features::id(const QString &feature)
+{
+ Features f(feature);
+ return f.id();
+}
+
+QString Features::feature(long id)
+{
+ if ( !featureName )
+ featureName = new FeatureName();
+
+ return featureName->id2f[id];
+}
+
+QString Features::name(long id)
+{
+ if ( !featureName )
+ featureName = new FeatureName();
+
+ return featureName->id2s[id];
+}
+
+QString Features::name() const
+{
+ return name(id());
+}
+
+QString Features::name(const QString &feature)
+{
+ Features f(feature);
+ return f.name(f.id());
+}
+
+//---------------------------------------------------------------------------
+// DiscoItem
+//---------------------------------------------------------------------------
+class DiscoItem::Private
+{
+public:
+ Private()
+ {
+ action = None;
+ }
+
+ Jid jid;
+ QString name;
+ QString node;
+ Action action;
+
+ Features features;
+ Identities identities;
+};
+
+DiscoItem::DiscoItem()
+{
+ d = new Private;
+}
+
+DiscoItem::DiscoItem(const DiscoItem &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+DiscoItem & DiscoItem::operator= (const DiscoItem &from)
+{
+ d->jid = from.d->jid;
+ d->name = from.d->name;
+ d->node = from.d->node;
+ d->action = from.d->action;
+ d->features = from.d->features;
+ d->identities = from.d->identities;
+
+ return *this;
+}
+
+DiscoItem::~DiscoItem()
+{
+ delete d;
+}
+
+AgentItem DiscoItem::toAgentItem() const
+{
+ AgentItem ai;
+
+ ai.setJid( jid() );
+ ai.setName( name() );
+
+ Identity id;
+ if ( !identities().isEmpty() )
+ id = identities().first();
+
+ ai.setCategory( id.category );
+ ai.setType( id.type );
+
+ ai.setFeatures( d->features );
+
+ return ai;
+}
+
+void DiscoItem::fromAgentItem(const AgentItem &ai)
+{
+ setJid( ai.jid() );
+ setName( ai.name() );
+
+ Identity id;
+ id.category = ai.category();
+ id.type = ai.type();
+ id.name = ai.name();
+
+ Identities idList;
+ idList << id;
+
+ setIdentities( idList );
+
+ setFeatures( ai.features() );
+}
+
+const Jid &DiscoItem::jid() const
+{
+ return d->jid;
+}
+
+void DiscoItem::setJid(const Jid &j)
+{
+ d->jid = j;
+}
+
+const QString &DiscoItem::name() const
+{
+ return d->name;
+}
+
+void DiscoItem::setName(const QString &n)
+{
+ d->name = n;
+}
+
+const QString &DiscoItem::node() const
+{
+ return d->node;
+}
+
+void DiscoItem::setNode(const QString &n)
+{
+ d->node = n;
+}
+
+DiscoItem::Action DiscoItem::action() const
+{
+ return d->action;
+}
+
+void DiscoItem::setAction(Action a)
+{
+ d->action = a;
+}
+
+const Features &DiscoItem::features() const
+{
+ return d->features;
+}
+
+void DiscoItem::setFeatures(const Features &f)
+{
+ d->features = f;
+}
+
+const DiscoItem::Identities &DiscoItem::identities() const
+{
+ return d->identities;
+}
+
+void DiscoItem::setIdentities(const Identities &i)
+{
+ d->identities = i;
+
+ if ( name().isEmpty() && i.count() )
+ setName( i.first().name );
+}
+
+
+DiscoItem::Action DiscoItem::string2action(QString s)
+{
+ Action a;
+
+ if ( s == "update" )
+ a = Update;
+ else if ( s == "remove" )
+ a = Remove;
+ else
+ a = None;
+
+ return a;
+}
+
+QString DiscoItem::action2string(Action a)
+{
+ QString s;
+
+ if ( a == Update )
+ s = "update";
+ else if ( a == Remove )
+ s = "remove";
+ else
+ s = QString::null;
+
+ return s;
+}
+
+}
+
+#include"types.moc"
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp
new file mode 100644
index 00000000..ffd7e6ae
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.cpp
@@ -0,0 +1,2120 @@
+/*
+ * tasks.cpp - basic tasks
+ * Copyright (C) 2001, 2002 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"xmpp_tasks.h"
+
+#include"base64.h"
+//#include"sha1.h"
+#include"xmpp_xmlcommon.h"
+//#include"xmpp_stream.h"
+//#include"xmpp_types.h"
+#include"xmpp_vcard.h"
+
+#include<qregexp.h>
+#include<qvaluelist.h>
+
+using namespace XMPP;
+
+
+static QString lineEncode(QString str)
+{
+ str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash
+ str.replace(QRegExp("\\|"), "\\p"); // pipe to \p
+ str.replace(QRegExp("\n"), "\\n"); // newline to \n
+ return str;
+}
+
+static QString lineDecode(const QString &str)
+{
+ QString ret;
+
+ for(unsigned int n = 0; n < str.length(); ++n) {
+ if(str.at(n) == '\\') {
+ ++n;
+ if(n >= str.length())
+ break;
+
+ if(str.at(n) == 'n')
+ ret.append('\n');
+ if(str.at(n) == 'p')
+ ret.append('|');
+ if(str.at(n) == '\\')
+ ret.append('\\');
+ }
+ else {
+ ret.append(str.at(n));
+ }
+ }
+
+ return ret;
+}
+
+static Roster xmlReadRoster(const QDomElement &q, bool push)
+{
+ Roster r;
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "item") {
+ RosterItem item;
+ item.fromXml(i);
+
+ if(push)
+ item.setIsPush(true);
+
+ r += item;
+ }
+ }
+
+ return r;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Register
+//----------------------------------------------------------------------------
+class JT_Register::Private
+{
+public:
+ Private() {}
+
+ Form form;
+ Jid jid;
+ int type;
+};
+
+JT_Register::JT_Register(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ d->type = -1;
+}
+
+JT_Register::~JT_Register()
+{
+ delete d;
+}
+
+void JT_Register::reg(const QString &user, const QString &pass)
+{
+ d->type = 0;
+ to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "username", user));
+ query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::changepw(const QString &pass)
+{
+ d->type = 1;
+ to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "username", client()->user()));
+ query.appendChild(textTag(doc(), "password", pass));
+}
+
+void JT_Register::unreg(const Jid &j)
+{
+ d->type = 2;
+ to = j.isEmpty() ? client()->host() : j.full();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+
+ // this may be useful
+ if(!d->form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", d->form.key()));
+
+ query.appendChild(doc()->createElement("remove"));
+}
+
+void JT_Register::getForm(const Jid &j)
+{
+ d->type = 3;
+ to = j;
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+}
+
+void JT_Register::setForm(const Form &form)
+{
+ d->type = 4;
+ to = form.jid();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:register");
+ iq.appendChild(query);
+
+ // key?
+ if(!form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", form.key()));
+
+ // fields
+ for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+ const FormField &f = *it;
+ query.appendChild(textTag(doc(), f.realName(), f.value()));
+ }
+}
+
+const Form & JT_Register::form() const
+{
+ return d->form;
+}
+
+void JT_Register::onGo()
+{
+ send(iq);
+}
+
+bool JT_Register::take(const QDomElement &x)
+{
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ Jid from(x.attribute("from"));
+ if(x.attribute("type") == "result") {
+ if(d->type == 3) {
+ d->form.clear();
+ d->form.setJid(from);
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "instructions")
+ d->form.setInstructions(tagContent(i));
+ else if(i.tagName() == "key")
+ d->form.setKey(tagContent(i));
+ else {
+ FormField f;
+ if(f.setType(i.tagName())) {
+ f.setValue(tagContent(i));
+ d->form += f;
+ }
+ }
+ }
+ }
+
+ setSuccess();
+ }
+ else
+ setError(x);
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_UnRegister
+//----------------------------------------------------------------------------
+class JT_UnRegister::Private
+{
+public:
+ Private() { }
+
+ Jid j;
+ JT_Register *jt_reg;
+};
+
+JT_UnRegister::JT_UnRegister(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+ d->jt_reg = 0;
+}
+
+JT_UnRegister::~JT_UnRegister()
+{
+ delete d->jt_reg;
+ delete d;
+}
+
+void JT_UnRegister::unreg(const Jid &j)
+{
+ d->j = j;
+}
+
+void JT_UnRegister::onGo()
+{
+ delete d->jt_reg;
+
+ d->jt_reg = new JT_Register(this);
+ d->jt_reg->getForm(d->j);
+ connect(d->jt_reg, SIGNAL(finished()), SLOT(getFormFinished()));
+ d->jt_reg->go(false);
+}
+
+void JT_UnRegister::getFormFinished()
+{
+ disconnect(d->jt_reg, 0, this, 0);
+
+ d->jt_reg->unreg(d->j);
+ connect(d->jt_reg, SIGNAL(finished()), SLOT(unregFinished()));
+ d->jt_reg->go(false);
+}
+
+void JT_UnRegister::unregFinished()
+{
+ if ( d->jt_reg->success() )
+ setSuccess();
+ else
+ setError(d->jt_reg->statusCode(), d->jt_reg->statusString());
+
+ delete d->jt_reg;
+ d->jt_reg = 0;
+}
+
+//----------------------------------------------------------------------------
+// JT_Roster
+//----------------------------------------------------------------------------
+class JT_Roster::Private
+{
+public:
+ Private() {}
+
+ Roster roster;
+ QValueList<QDomElement> itemList;
+};
+
+JT_Roster::JT_Roster(Task *parent)
+:Task(parent)
+{
+ type = -1;
+ d = new Private;
+}
+
+JT_Roster::~JT_Roster()
+{
+ delete d;
+}
+
+void JT_Roster::get()
+{
+ type = 0;
+ //to = client()->host();
+ iq = createIQ(doc(), "get", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:roster");
+ iq.appendChild(query);
+}
+
+void JT_Roster::set(const Jid &jid, const QString &name, const QStringList &groups)
+{
+ type = 1;
+ //to = client()->host();
+ QDomElement item = doc()->createElement("item");
+ item.setAttribute("jid", jid.full());
+ if(!name.isEmpty())
+ item.setAttribute("name", name);
+ for(QStringList::ConstIterator it = groups.begin(); it != groups.end(); ++it)
+ item.appendChild(textTag(doc(), "group", *it));
+ d->itemList += item;
+}
+
+void JT_Roster::remove(const Jid &jid)
+{
+ type = 1;
+ //to = client()->host();
+ QDomElement item = doc()->createElement("item");
+ item.setAttribute("jid", jid.full());
+ item.setAttribute("subscription", "remove");
+ d->itemList += item;
+}
+
+void JT_Roster::onGo()
+{
+ if(type == 0)
+ send(iq);
+ else if(type == 1) {
+ //to = client()->host();
+ iq = createIQ(doc(), "set", to.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:roster");
+ iq.appendChild(query);
+ for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+ query.appendChild(*it);
+ send(iq);
+ }
+}
+
+const Roster & JT_Roster::roster() const
+{
+ return d->roster;
+}
+
+QString JT_Roster::toString() const
+{
+ if(type != 1)
+ return "";
+
+ QDomElement i = doc()->createElement("request");
+ i.setAttribute("type", "JT_Roster");
+ for(QValueList<QDomElement>::ConstIterator it = d->itemList.begin(); it != d->itemList.end(); ++it)
+ i.appendChild(*it);
+ return lineEncode(Stream::xmlToString(i));
+ return "";
+}
+
+bool JT_Roster::fromString(const QString &str)
+{
+ QDomDocument *dd = new QDomDocument;
+ if(!dd->setContent(lineDecode(str).utf8()))
+ return false;
+ QDomElement e = doc()->importNode(dd->documentElement(), true).toElement();
+ delete dd;
+
+ if(e.tagName() != "request" || e.attribute("type") != "JT_Roster")
+ return false;
+
+ type = 1;
+ d->itemList.clear();
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ d->itemList += i;
+ }
+
+ return true;
+}
+
+bool JT_Roster::take(const QDomElement &x)
+{
+ if(!iqVerify(x, client()->host(), id()))
+ return false;
+
+ // get
+ if(type == 0) {
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+ d->roster = xmlReadRoster(q, false);
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+ }
+ // set
+ else if(type == 1) {
+ if(x.attribute("type") == "result")
+ setSuccess();
+ else
+ setError(x);
+
+ return true;
+ }
+ // remove
+ else if(type == 2) {
+ setSuccess();
+ return true;
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushRoster
+//----------------------------------------------------------------------------
+JT_PushRoster::JT_PushRoster(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushRoster::~JT_PushRoster()
+{
+}
+
+bool JT_PushRoster::take(const QDomElement &e)
+{
+ // must be an iq-set tag
+ if(e.tagName() != "iq" || e.attribute("type") != "set")
+ return false;
+
+ if(!iqVerify(e, client()->host(), "", "jabber:iq:roster"))
+ return false;
+
+ roster(xmlReadRoster(queryTag(e), true));
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Presence
+//----------------------------------------------------------------------------
+JT_Presence::JT_Presence(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+JT_Presence::~JT_Presence()
+{
+}
+
+void JT_Presence::pres(const Status &s)
+{
+ type = 0;
+
+ tag = doc()->createElement("presence");
+ if(!s.isAvailable()) {
+ tag.setAttribute("type", "unavailable");
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+ }
+ else {
+ if(s.isInvisible())
+ tag.setAttribute("type", "invisible");
+
+ if(!s.show().isEmpty())
+ tag.appendChild(textTag(doc(), "show", s.show()));
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+
+ tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
+
+ if(!s.keyID().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.keyID());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
+ tag.appendChild(x);
+ }
+ if(!s.xsigned().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "jabber:x:signed");
+ tag.appendChild(x);
+ }
+
+ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
+ QDomElement c = doc()->createElement("c");
+ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
+ c.setAttribute("node",s.capsNode());
+ c.setAttribute("ver",s.capsVersion());
+ if (!s.capsExt().isEmpty())
+ c.setAttribute("ext",s.capsExt());
+ tag.appendChild(c);
+ }
+ }
+}
+
+void JT_Presence::pres(const Jid &to, const Status &s)
+{
+ pres(s);
+ tag.setAttribute("to", to.full());
+}
+
+void JT_Presence::sub(const Jid &to, const QString &subType)
+{
+ type = 1;
+
+ tag = doc()->createElement("presence");
+ tag.setAttribute("to", to.full());
+ tag.setAttribute("type", subType);
+}
+
+void JT_Presence::onGo()
+{
+ send(tag);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushPresence
+//----------------------------------------------------------------------------
+JT_PushPresence::JT_PushPresence(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushPresence::~JT_PushPresence()
+{
+}
+
+bool JT_PushPresence::take(const QDomElement &e)
+{
+ if(e.tagName() != "presence")
+ return false;
+
+ Jid j(e.attribute("from"));
+ Status p;
+
+ if(e.hasAttribute("type")) {
+ QString type = e.attribute("type");
+ if(type == "unavailable") {
+ p.setIsAvailable(false);
+ }
+ else if(type == "error") {
+ QString str = "";
+ int code = 0;
+ getErrorFromElement(e, &code, &str);
+ p.setError(code, str);
+ }
+ else {
+ subscription(j, type);
+ return true;
+ }
+ }
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(e, "status", &found);
+ if(found)
+ p.setStatus(tagContent(tag));
+ tag = findSubTag(e, "show", &found);
+ if(found)
+ p.setShow(tagContent(tag));
+ tag = findSubTag(e, "priority", &found);
+ if(found)
+ p.setPriority(tagContent(tag).toInt());
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:delay") {
+ if(i.hasAttribute("stamp")) {
+ QDateTime dt;
+ if(stamp2TS(i.attribute("stamp"), &dt))
+ dt = dt.addSecs(client()->timeZoneOffset() * 3600);
+ p.setTimeStamp(dt);
+ }
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "gabber:x:music:info") {
+ QDomElement t;
+ bool found;
+ QString title, state;
+
+ t = findSubTag(i, "title", &found);
+ if(found)
+ title = tagContent(t);
+ t = findSubTag(i, "state", &found);
+ if(found)
+ state = tagContent(t);
+
+ if(!title.isEmpty() && state == "playing")
+ p.setSongTitle(title);
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "jabber:x:signed") {
+ p.setXSigned(tagContent(i));
+ }
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/e2e") {
+ p.setKeyID(tagContent(i));
+ }
+ else if(i.tagName() == "c" && i.attribute("xmlns") == "http://jabber.org/protocol/caps") {
+ p.setCapsNode(i.attribute("node"));
+ p.setCapsVersion(i.attribute("ver"));
+ p.setCapsExt(i.attribute("ext"));
+ }
+ }
+
+ presence(j, p);
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Message
+//----------------------------------------------------------------------------
+static QDomElement oldStyleNS(const QDomElement &e)
+{
+ // find closest parent with a namespace
+ QDomNode par = e.parentNode();
+ while(!par.isNull() && par.namespaceURI().isNull())
+ par = par.parentNode();
+ bool noShowNS = false;
+ if(!par.isNull() && par.namespaceURI() == e.namespaceURI())
+ noShowNS = true;
+
+ QDomElement i;
+ uint x;
+ //if(noShowNS)
+ i = e.ownerDocument().createElement(e.tagName());
+ //else
+ // i = e.ownerDocument().createElementNS(e.namespaceURI(), e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x)
+ i.setAttributeNode(al.item(x).cloneNode().toAttr());
+
+ if(!noShowNS)
+ i.setAttribute("xmlns", e.namespaceURI());
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(oldStyleNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+ return i;
+}
+
+JT_Message::JT_Message(Task *parent, const Message &msg)
+:Task(parent)
+{
+ m = msg;
+ m.setId(id());
+}
+
+JT_Message::~JT_Message()
+{
+}
+
+void JT_Message::onGo()
+{
+ Stanza s = m.toStanza(&(client()->stream()));
+ QDomElement e = oldStyleNS(s.element());
+ send(e);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PushMessage
+//----------------------------------------------------------------------------
+static QDomElement addCorrectNS(const QDomElement &e)
+{
+ uint x;
+
+ // grab child nodes
+ /*QDomDocumentFragment frag = e.ownerDocument().createDocumentFragment();
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x)
+ frag.appendChild(nl.item(x).cloneNode());*/
+
+ // find closest xmlns
+ QDomNode n = e;
+ while(!n.isNull() && !n.toElement().hasAttribute("xmlns"))
+ n = n.parentNode();
+ QString ns;
+ if(n.isNull() || !n.toElement().hasAttribute("xmlns"))
+ ns = "jabber:client";
+ else
+ ns = n.toElement().attribute("xmlns");
+
+ // make a new node
+ QDomElement i = e.ownerDocument().createElementNS(ns, e.tagName());
+
+ // copy attributes
+ QDomNamedNodeMap al = e.attributes();
+ for(x = 0; x < al.count(); ++x) {
+ QDomAttr a = al.item(x).toAttr();
+ if(a.name() != "xmlns")
+ i.setAttributeNodeNS(al.item(x).cloneNode().toAttr());
+ }
+
+ // copy children
+ QDomNodeList nl = e.childNodes();
+ for(x = 0; x < nl.count(); ++x) {
+ QDomNode n = nl.item(x);
+ if(n.isElement())
+ i.appendChild(addCorrectNS(n.toElement()));
+ else
+ i.appendChild(n.cloneNode());
+ }
+
+ //i.appendChild(frag);
+ return i;
+}
+
+JT_PushMessage::JT_PushMessage(Task *parent)
+:Task(parent)
+{
+}
+
+JT_PushMessage::~JT_PushMessage()
+{
+}
+
+bool JT_PushMessage::take(const QDomElement &e)
+{
+ if(e.tagName() != "message")
+ return false;
+
+ Stanza s = client()->stream().createStanza(addCorrectNS(e));
+ if(s.isNull()) {
+ //printf("take: bad stanza??\n");
+ return false;
+ }
+
+ Message m;
+ if(!m.fromStanza(s, client()->timeZoneOffset())) {
+ //printf("bad message\n");
+ return false;
+ }
+
+ message(m);
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_GetLastActivity
+//----------------------------------------------------------------------------
+class JT_GetLastActivity::Private
+{
+public:
+ Private() {}
+
+ int seconds;
+ QString message;
+};
+
+JT_GetLastActivity::JT_GetLastActivity(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+}
+
+JT_GetLastActivity::~JT_GetLastActivity()
+{
+ delete d;
+}
+
+void JT_GetLastActivity::get(const Jid &j)
+{
+ jid = j;
+ iq = createIQ(doc(), "get", jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:last");
+ iq.appendChild(query);
+}
+
+int JT_GetLastActivity::seconds() const
+{
+ return d->seconds;
+}
+
+const QString &JT_GetLastActivity::message() const
+{
+ return d->message;
+}
+
+void JT_GetLastActivity::onGo()
+{
+ send(iq);
+}
+
+bool JT_GetLastActivity::take(const QDomElement &x)
+{
+ if(!iqVerify(x, jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ d->message = q.text();
+ bool ok;
+ d->seconds = q.attribute("seconds").toInt(&ok);
+
+ setSuccess(ok);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_GetServices
+//----------------------------------------------------------------------------
+JT_GetServices::JT_GetServices(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_GetServices::get(const Jid &j)
+{
+ agentList.clear();
+
+ jid = j;
+ iq = createIQ(doc(), "get", jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:agents");
+ iq.appendChild(query);
+}
+
+const AgentList & JT_GetServices::agents() const
+{
+ return agentList;
+}
+
+void JT_GetServices::onGo()
+{
+ send(iq);
+}
+
+bool JT_GetServices::take(const QDomElement &x)
+{
+ if(!iqVerify(x, jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ // agents
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "agent") {
+ AgentItem a;
+
+ a.setJid(Jid(i.attribute("jid")));
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(i, "name", &found);
+ if(found)
+ a.setName(tagContent(tag));
+
+ // determine which namespaces does item support
+ QStringList ns;
+
+ tag = findSubTag(i, "register", &found);
+ if(found)
+ ns << "jabber:iq:register";
+ tag = findSubTag(i, "search", &found);
+ if(found)
+ ns << "jabber:iq:search";
+ tag = findSubTag(i, "groupchat", &found);
+ if(found)
+ ns << "jabber:iq:conference";
+ tag = findSubTag(i, "transport", &found);
+ if(found)
+ ns << "jabber:iq:gateway";
+
+ a.setFeatures(ns);
+
+ agentList += a;
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_VCard
+//----------------------------------------------------------------------------
+class JT_VCard::Private
+{
+public:
+ Private() {}
+
+ QDomElement iq;
+ Jid jid;
+ VCard vcard;
+};
+
+JT_VCard::JT_VCard(Task *parent)
+:Task(parent)
+{
+ type = -1;
+ d = new Private;
+}
+
+JT_VCard::~JT_VCard()
+{
+ delete d;
+}
+
+void JT_VCard::get(const Jid &_jid)
+{
+ type = 0;
+ d->jid = _jid;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement v = doc()->createElement("vCard");
+ v.setAttribute("xmlns", "vcard-temp");
+ v.setAttribute("version", "2.0");
+ v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+ d->iq.appendChild(v);
+}
+
+const Jid & JT_VCard::jid() const
+{
+ return d->jid;
+}
+
+const VCard & JT_VCard::vcard() const
+{
+ return d->vcard;
+}
+
+void JT_VCard::set(const VCard &card)
+{
+ type = 1;
+ d->vcard = card;
+ d->jid = "";
+ d->iq = createIQ(doc(), "set", d->jid.full(), id());
+ d->iq.appendChild(card.toXml(doc()) );
+}
+
+void JT_VCard::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_VCard::take(const QDomElement &x)
+{
+ Jid to = d->jid;
+ if (to.userHost() == client()->jid().userHost())
+ to = client()->host();
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement q = n.toElement();
+ if(q.isNull())
+ continue;
+
+ if(q.tagName().upper() == "VCARD") {
+ if(d->vcard.fromXml(q)) {
+ setSuccess();
+ return true;
+ }
+ }
+ }
+
+ setError(ErrDisc + 1, tr("No VCard available"));
+ return true;
+ }
+ else {
+ setSuccess();
+ return true;
+ }
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Search
+//----------------------------------------------------------------------------
+class JT_Search::Private
+{
+public:
+ Private() {}
+
+ Jid jid;
+ Form form;
+ QValueList<SearchResult> resultList;
+};
+
+JT_Search::JT_Search(Task *parent)
+:Task(parent)
+{
+ d = new Private;
+ type = -1;
+}
+
+JT_Search::~JT_Search()
+{
+ delete d;
+}
+
+void JT_Search::get(const Jid &jid)
+{
+ type = 0;
+ d->jid = jid;
+ iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:search");
+ iq.appendChild(query);
+}
+
+void JT_Search::set(const Form &form)
+{
+ type = 1;
+ d->jid = form.jid();
+ iq = createIQ(doc(), "set", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:search");
+ iq.appendChild(query);
+
+ // key?
+ if(!form.key().isEmpty())
+ query.appendChild(textTag(doc(), "key", form.key()));
+
+ // fields
+ for(Form::ConstIterator it = form.begin(); it != form.end(); ++it) {
+ const FormField &f = *it;
+ query.appendChild(textTag(doc(), f.realName(), f.value()));
+ }
+}
+
+const Form & JT_Search::form() const
+{
+ return d->form;
+}
+
+const QValueList<SearchResult> & JT_Search::results() const
+{
+ return d->resultList;
+}
+
+void JT_Search::onGo()
+{
+ send(iq);
+}
+
+bool JT_Search::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ Jid from(x.attribute("from"));
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ d->form.clear();
+ d->form.setJid(from);
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "instructions")
+ d->form.setInstructions(tagContent(i));
+ else if(i.tagName() == "key")
+ d->form.setKey(tagContent(i));
+ else {
+ FormField f;
+ if(f.setType(i.tagName())) {
+ f.setValue(tagContent(i));
+ d->form += f;
+ }
+ }
+ }
+ }
+ else {
+ d->resultList.clear();
+
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if(i.tagName() == "item") {
+ SearchResult r(Jid(i.attribute("jid")));
+
+ QDomElement tag;
+ bool found;
+
+ tag = findSubTag(i, "nick", &found);
+ if(found)
+ r.setNick(tagContent(tag));
+ tag = findSubTag(i, "first", &found);
+ if(found)
+ r.setFirst(tagContent(tag));
+ tag = findSubTag(i, "last", &found);
+ if(found)
+ r.setLast(tagContent(tag));
+ tag = findSubTag(i, "email", &found);
+ if(found)
+ r.setEmail(tagContent(tag));
+
+ d->resultList += r;
+ }
+ }
+ }
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientVersion
+//----------------------------------------------------------------------------
+JT_ClientVersion::JT_ClientVersion(Task *parent)
+:Task(parent)
+{
+}
+
+void JT_ClientVersion::get(const Jid &jid)
+{
+ j = jid;
+ iq = createIQ(doc(), "get", j.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:version");
+ iq.appendChild(query);
+}
+
+void JT_ClientVersion::onGo()
+{
+ send(iq);
+}
+
+bool JT_ClientVersion::take(const QDomElement &x)
+{
+ if(!iqVerify(x, j, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ bool found;
+ QDomElement q = queryTag(x);
+ QDomElement tag;
+ tag = findSubTag(q, "name", &found);
+ if(found)
+ v_name = tagContent(tag);
+ tag = findSubTag(q, "version", &found);
+ if(found)
+ v_ver = tagContent(tag);
+ tag = findSubTag(q, "os", &found);
+ if(found)
+ v_os = tagContent(tag);
+
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+const Jid & JT_ClientVersion::jid() const
+{
+ return j;
+}
+
+const QString & JT_ClientVersion::name() const
+{
+ return v_name;
+}
+
+const QString & JT_ClientVersion::version() const
+{
+ return v_ver;
+}
+
+const QString & JT_ClientVersion::os() const
+{
+ return v_os;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_ClientTime
+//----------------------------------------------------------------------------
+/*JT_ClientTime::JT_ClientTime(Task *parent, const Jid &_j)
+:Task(parent)
+{
+ j = _j;
+ iq = createIQ("get", j.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:time");
+ iq.appendChild(query);
+}
+
+void JT_ClientTime::go()
+{
+ send(iq);
+}
+
+bool JT_ClientTime::take(const QDomElement &x)
+{
+ if(x.attribute("id") != id())
+ return FALSE;
+
+ if(x.attribute("type") == "result") {
+ bool found;
+ QDomElement q = queryTag(x);
+ QDomElement tag;
+ tag = findSubTag(q, "utc", &found);
+ if(found)
+ stamp2TS(tagContent(tag), &utc);
+ tag = findSubTag(q, "tz", &found);
+ if(found)
+ timezone = tagContent(tag);
+ tag = findSubTag(q, "display", &found);
+ if(found)
+ display = tagContent(tag);
+
+ setSuccess(TRUE);
+ }
+ else {
+ setError(getErrorString(x));
+ setSuccess(FALSE);
+ }
+
+ return TRUE;
+}
+*/
+
+
+//----------------------------------------------------------------------------
+// JT_ServInfo
+//----------------------------------------------------------------------------
+JT_ServInfo::JT_ServInfo(Task *parent)
+:Task(parent)
+{
+}
+
+JT_ServInfo::~JT_ServInfo()
+{
+}
+
+bool JT_ServInfo::take(const QDomElement &e)
+{
+ if(e.tagName() != "iq" || e.attribute("type") != "get")
+ return false;
+
+ QString ns = queryNS(e);
+ if(ns == "jabber:iq:version") {
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:version");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "name", client()->clientName()));
+ query.appendChild(textTag(doc(), "version", client()->clientVersion()));
+ query.appendChild(textTag(doc(), "os", client()->OSName()));
+ send(iq);
+ return true;
+ }
+ //else if(ns == "jabber:iq:time") {
+ // QDomElement iq = createIQ("result", e.attribute("from"), e.attribute("id"));
+ // QDomElement query = doc()->createElement("query");
+ // query.setAttribute("xmlns", "jabber:iq:time");
+ // iq.appendChild(query);
+ // QDateTime local = QDateTime::currentDateTime();
+ // QDateTime utc = local.addSecs(-getTZOffset() * 3600);
+ // QString str = getTZString();
+ // query.appendChild(textTag("utc", TS2stamp(utc)));
+ // query.appendChild(textTag("tz", str));
+ // query.appendChild(textTag("display", QString("%1 %2").arg(local.toString()).arg(str)));
+ // send(iq);
+ // return TRUE;
+ //}
+ else if(ns == "http://jabber.org/protocol/disco#info") {
+ // Find out the node
+ QString node;
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ if(found) // NOTE: Should always be true, since a NS was found above
+ node = q.attribute("node");
+
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+ if (!node.isEmpty())
+ query.setAttribute("node", node);
+ iq.appendChild(query);
+
+ // Identity
+ DiscoItem::Identity identity = client()->identity();
+ QDomElement id = doc()->createElement("identity");
+ if (!identity.category.isEmpty() && !identity.type.isEmpty()) {
+ id.setAttribute("category",identity.category);
+ id.setAttribute("type",identity.type);
+ if (!identity.name.isEmpty()) {
+ id.setAttribute("name",identity.name);
+ }
+ }
+ else {
+ // Default values
+ id.setAttribute("category","client");
+ id.setAttribute("type","pc");
+ }
+ query.appendChild(id);
+
+ QDomElement feature;
+ if (node.isEmpty() || node == client()->capsNode() + "#" + client()->capsVersion()) {
+ // Standard features
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/bytestreams");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/si");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/xhtml-im");
+ query.appendChild(feature);
+
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", "http://jabber.org/protocol/disco#info");
+ query.appendChild(feature);
+
+ if (node.isEmpty()) {
+ // Extended features
+ QStringList exts = client()->extensions();
+ for (QStringList::ConstIterator i = exts.begin(); i != exts.end(); ++i) {
+ const QStringList& l = client()->extension(*i).list();
+ for ( QStringList::ConstIterator j = l.begin(); j != l.end(); ++j ) {
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", *j);
+ query.appendChild(feature);
+ }
+ }
+ }
+ }
+ else if (node.startsWith(client()->capsNode() + "#")) {
+ QString ext = node.right(node.length()-client()->capsNode().length()-1);
+ if (client()->extensions().contains(ext)) {
+ const QStringList& l = client()->extension(ext).list();
+ for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
+ feature = doc()->createElement("feature");
+ feature.setAttribute("var", *it);
+ query.appendChild(feature);
+ }
+ }
+ else {
+ // TODO: ERROR
+ }
+ }
+ else {
+ // TODO: ERROR
+ }
+
+ send(iq);
+ return true;
+ }
+
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+// JT_Gateway
+//----------------------------------------------------------------------------
+JT_Gateway::JT_Gateway(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+void JT_Gateway::get(const Jid &jid)
+{
+ type = 0;
+ v_jid = jid;
+ iq = createIQ(doc(), "get", v_jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:gateway");
+ iq.appendChild(query);
+}
+
+void JT_Gateway::set(const Jid &jid, const QString &prompt)
+{
+ type = 1;
+ v_jid = jid;
+ v_prompt = prompt;
+ iq = createIQ(doc(), "set", v_jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:gateway");
+ iq.appendChild(query);
+ query.appendChild(textTag(doc(), "prompt", v_prompt));
+}
+
+void JT_Gateway::onGo()
+{
+ send(iq);
+}
+
+Jid JT_Gateway::jid() const
+{
+ return v_jid;
+}
+
+QString JT_Gateway::desc() const
+{
+ return v_desc;
+}
+
+QString JT_Gateway::prompt() const
+{
+ return v_prompt;
+}
+
+bool JT_Gateway::take(const QDomElement &x)
+{
+ if(!iqVerify(x, v_jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(type == 0) {
+ QDomElement query = queryTag(x);
+ bool found;
+ QDomElement tag;
+ tag = findSubTag(query, "desc", &found);
+ if(found)
+ v_desc = tagContent(tag);
+ tag = findSubTag(query, "prompt", &found);
+ if(found)
+ v_prompt = tagContent(tag);
+ }
+ else {
+ QDomElement query = queryTag(x);
+ bool found;
+ QDomElement tag;
+ tag = findSubTag(query, "prompt", &found);
+ if(found)
+ v_prompt = tagContent(tag);
+ }
+
+ setSuccess();
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_Browse
+//----------------------------------------------------------------------------
+class JT_Browse::Private
+{
+public:
+ QDomElement iq;
+ Jid jid;
+ AgentList agentList;
+ AgentItem root;
+};
+
+JT_Browse::JT_Browse (Task *parent)
+:Task (parent)
+{
+ d = new Private;
+}
+
+JT_Browse::~JT_Browse ()
+{
+ delete d;
+}
+
+void JT_Browse::get (const Jid &j)
+{
+ d->agentList.clear();
+
+ d->jid = j;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("item");
+ query.setAttribute("xmlns", "jabber:iq:browse");
+ d->iq.appendChild(query);
+}
+
+const AgentList & JT_Browse::agents() const
+{
+ return d->agentList;
+}
+
+const AgentItem & JT_Browse::root() const
+{
+ return d->root;
+}
+
+void JT_Browse::onGo ()
+{
+ send(d->iq);
+}
+
+AgentItem JT_Browse::browseHelper (const QDomElement &i)
+{
+ AgentItem a;
+
+ if ( i.tagName() == "ns" )
+ return a;
+
+ a.setName ( i.attribute("name") );
+ a.setJid ( i.attribute("jid") );
+
+ // there are two types of category/type specification:
+ //
+ // 1. <item category="category_name" type="type_name" />
+ // 2. <category_name type="type_name" />
+
+ if ( i.tagName() == "item" || i.tagName() == "query" )
+ a.setCategory ( i.attribute("category") );
+ else
+ a.setCategory ( i.tagName() );
+
+ a.setType ( i.attribute("type") );
+
+ QStringList ns;
+ for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ if ( i.tagName() == "ns" )
+ ns << i.text();
+ }
+
+ // For now, conference.jabber.org returns proper namespace only
+ // when browsing individual rooms. So it's a quick client-side fix.
+ if ( !a.features().canGroupchat() && a.category() == "conference" )
+ ns << "jabber:iq:conference";
+
+ a.setFeatures (ns);
+
+ return a;
+}
+
+bool JT_Browse::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ for(QDomNode n = x.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+
+ d->root = browseHelper (i);
+
+ for(QDomNode nn = i.firstChild(); !nn.isNull(); nn = nn.nextSibling()) {
+ QDomElement e = nn.toElement();
+ if ( e.isNull() )
+ continue;
+ if ( e.tagName() == "ns" )
+ continue;
+
+ d->agentList += browseHelper (e);
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoItems
+//----------------------------------------------------------------------------
+class JT_DiscoItems::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ DiscoList items;
+};
+
+JT_DiscoItems::JT_DiscoItems(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoItems::~JT_DiscoItems()
+{
+ delete d;
+}
+
+void JT_DiscoItems::get(const DiscoItem &item)
+{
+ get(item.jid(), item.node());
+}
+
+void JT_DiscoItems::get (const Jid &j, const QString &node)
+{
+ d->items.clear();
+
+ d->jid = j;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+ if ( !node.isEmpty() )
+ query.setAttribute("node", node);
+
+ d->iq.appendChild(query);
+}
+
+const DiscoList &JT_DiscoItems::items() const
+{
+ return d->items;
+}
+
+void JT_DiscoItems::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoItems::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement e = n.toElement();
+ if( e.isNull() )
+ continue;
+
+ if ( e.tagName() == "item" ) {
+ DiscoItem item;
+
+ item.setJid ( e.attribute("jid") );
+ item.setName( e.attribute("name") );
+ item.setNode( e.attribute("node") );
+ item.setAction( DiscoItem::string2action(e.attribute("action")) );
+
+ d->items.append( item );
+ }
+ }
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoInfo
+//----------------------------------------------------------------------------
+class JT_DiscoInfo::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ QString node;
+ DiscoItem item;
+};
+
+JT_DiscoInfo::JT_DiscoInfo(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoInfo::~JT_DiscoInfo()
+{
+ delete d;
+}
+
+void JT_DiscoInfo::get(const DiscoItem &item)
+{
+ DiscoItem::Identity id;
+ if ( item.identities().count() == 1 )
+ id = item.identities().first();
+ get(item.jid(), item.node(), id);
+}
+
+void JT_DiscoInfo::get (const Jid &j, const QString &node, DiscoItem::Identity ident)
+{
+ d->item = DiscoItem(); // clear item
+
+ d->jid = j;
+ d->node = node;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+
+ if ( !node.isEmpty() )
+ query.setAttribute("node", node);
+
+ if ( !ident.category.isEmpty() && !ident.type.isEmpty() ) {
+ QDomElement i = doc()->createElement("item");
+
+ i.setAttribute("category", ident.category);
+ i.setAttribute("type", ident.type);
+ if ( !ident.name.isEmpty() )
+ i.setAttribute("name", ident.name);
+
+ query.appendChild( i );
+
+ }
+
+ d->iq.appendChild(query);
+}
+
+
+/**
+ * Original requested jid.
+ * Is here because sometimes the responder does not include this information
+ * in the reply.
+ */
+const Jid& JT_DiscoInfo::jid() const
+{
+ return d->jid;
+}
+
+/**
+ * Original requested node.
+ * Is here because sometimes the responder does not include this information
+ * in the reply.
+ */
+const QString& JT_DiscoInfo::node() const
+{
+ return d->node;
+}
+
+
+
+const DiscoItem &JT_DiscoInfo::item() const
+{
+ return d->item;
+}
+
+void JT_DiscoInfo::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoInfo::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ QDomElement q = queryTag(x);
+
+ DiscoItem item;
+
+ item.setJid( d->jid );
+ item.setNode( q.attribute("node") );
+
+ QStringList features;
+ DiscoItem::Identities identities;
+
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement e = n.toElement();
+ if( e.isNull() )
+ continue;
+
+ if ( e.tagName() == "feature" ) {
+ features << e.attribute("var");
+ }
+ else if ( e.tagName() == "identity" ) {
+ DiscoItem::Identity id;
+
+ id.category = e.attribute("category");
+ id.name = e.attribute("name");
+ id.type = e.attribute("type");
+
+ identities.append( id );
+ }
+ }
+
+ item.setFeatures( features );
+ item.setIdentities( identities );
+
+ d->item = item;
+
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_DiscoPublish
+//----------------------------------------------------------------------------
+class JT_DiscoPublish::Private
+{
+public:
+ Private() { }
+
+ QDomElement iq;
+ Jid jid;
+ DiscoList list;
+};
+
+JT_DiscoPublish::JT_DiscoPublish(Task *parent)
+: Task(parent)
+{
+ d = new Private;
+}
+
+JT_DiscoPublish::~JT_DiscoPublish()
+{
+ delete d;
+}
+
+void JT_DiscoPublish::set(const Jid &j, const DiscoList &list)
+{
+ d->list = list;
+ d->jid = j;
+
+ d->iq = createIQ(doc(), "set", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
+
+ // FIXME: unsure about this
+ //if ( !node.isEmpty() )
+ // query.setAttribute("node", node);
+
+ DiscoList::ConstIterator it = list.begin();
+ for ( ; it != list.end(); ++it) {
+ QDomElement w = doc()->createElement("item");
+
+ w.setAttribute("jid", (*it).jid().full());
+ if ( !(*it).name().isEmpty() )
+ w.setAttribute("name", (*it).name());
+ if ( !(*it).node().isEmpty() )
+ w.setAttribute("node", (*it).node());
+ w.setAttribute("action", DiscoItem::action2string((*it).action()));
+
+ query.appendChild( w );
+ }
+
+ d->iq.appendChild(query);
+}
+
+void JT_DiscoPublish::onGo ()
+{
+ send(d->iq);
+}
+
+bool JT_DiscoPublish::take(const QDomElement &x)
+{
+ if(!iqVerify(x, d->jid, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ setSuccess(true);
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+// JT_MucPresence
+//----------------------------------------------------------------------------
+JT_MucPresence::JT_MucPresence(Task *parent)
+:Task(parent)
+{
+ type = -1;
+}
+
+JT_MucPresence::~JT_MucPresence()
+{
+}
+
+void JT_MucPresence::pres(const Status &s)
+{
+ type = 0;
+
+ tag = doc()->createElement("presence");
+ if(!s.isAvailable()) {
+ tag.setAttribute("type", "unavailable");
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+ }
+ else {
+ if(s.isInvisible())
+ tag.setAttribute("type", "invisible");
+
+ if(!s.show().isEmpty())
+ tag.appendChild(textTag(doc(), "show", s.show()));
+ if(!s.status().isEmpty())
+ tag.appendChild(textTag(doc(), "status", s.status()));
+
+ tag.appendChild( textTag(doc(), "priority", QString("%1").arg(s.priority()) ) );
+
+ if(!s.keyID().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.keyID());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/e2e");
+ tag.appendChild(x);
+ }
+ if(!s.xsigned().isEmpty()) {
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "jabber:x:signed");
+ tag.appendChild(x);
+ }
+
+ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
+ QDomElement c = doc()->createElement("c");
+ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
+ c.setAttribute("node",s.capsNode());
+ c.setAttribute("ver",s.capsVersion());
+ if (!s.capsExt().isEmpty())
+ c.setAttribute("ext",s.capsExt());
+ tag.appendChild(c);
+ }
+ }
+}
+
+void JT_MucPresence::pres(const Jid &to, const Status &s, const QString &password)
+{
+ pres(s);
+ tag.setAttribute("to", to.full());
+ QDomElement x = textTag(doc(), "x", s.xsigned());
+ x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
+ x.appendChild( textTag(doc(), "password", password.latin1()) );
+ tag.appendChild(x);
+}
+
+void JT_MucPresence::onGo()
+{
+ send(tag);
+ setSuccess();
+}
+
+
+//----------------------------------------------------------------------------
+// JT_PrivateStorage
+//----------------------------------------------------------------------------
+class JT_PrivateStorage::Private
+{
+ public:
+ Private() : type(-1) {}
+
+ QDomElement iq;
+ QDomElement elem;
+ int type;
+};
+
+JT_PrivateStorage::JT_PrivateStorage(Task *parent)
+ :Task(parent)
+{
+ d = new Private;
+}
+
+JT_PrivateStorage::~JT_PrivateStorage()
+{
+ delete d;
+}
+
+void JT_PrivateStorage::get(const QString& tag, const QString& xmlns)
+{
+ d->type = 0;
+ d->iq = createIQ(doc(), "get" , QString() , id() );
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:private");
+ d->iq.appendChild(query);
+ QDomElement s = doc()->createElement(tag);
+ if(!xmlns.isEmpty())
+ s.setAttribute("xmlns", xmlns);
+ query.appendChild(s);
+}
+
+void JT_PrivateStorage::set(const QDomElement& element)
+{
+ d->type = 1;
+ d->elem=element;
+ QDomNode n=doc()->importNode(element,true);
+
+ d->iq = createIQ(doc(), "set" , QString() , id() );
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "jabber:iq:private");
+ d->iq.appendChild(query);
+ query.appendChild(n);
+}
+
+void JT_PrivateStorage::onGo()
+{
+ send(d->iq);
+}
+
+bool JT_PrivateStorage::take(const QDomElement &x)
+{
+ QString to = client()->host();
+ if(!iqVerify(x, to, id()))
+ return false;
+
+ if(x.attribute("type") == "result") {
+ if(d->type == 0) {
+ QDomElement q = queryTag(x);
+ for(QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ d->elem=i;
+ break;
+ }
+ }
+ setSuccess();
+ return true;
+ }
+ else {
+ setError(x);
+ }
+
+ return true;
+}
+
+
+QDomElement JT_PrivateStorage::element( )
+{
+ return d->elem;
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h
new file mode 100644
index 00000000..ceb1e294
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_tasks.h
@@ -0,0 +1,485 @@
+/*
+ * tasks.h - basic tasks
+ * Copyright (C) 2001, 2002 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 JABBER_TASKS_H
+#define JABBER_TASKS_H
+
+#include<qstring.h>
+#include<qdom.h>
+
+#include"im.h"
+#include"xmpp_vcard.h"
+
+namespace XMPP
+{
+ class Roster;
+ class Status;
+
+ class JT_Register : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Register(Task *parent);
+ ~JT_Register();
+
+ void reg(const QString &user, const QString &pass);
+ void changepw(const QString &pass);
+ void unreg(const Jid &j="");
+
+ const Form & form() const;
+ void getForm(const Jid &);
+ void setForm(const Form &);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ QDomElement iq;
+ Jid to;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_UnRegister : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_UnRegister(Task *parent);
+ ~JT_UnRegister();
+
+ void unreg(const Jid &);
+
+ void onGo();
+
+ private slots:
+ void getFormFinished();
+ void unregFinished();
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Roster : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Roster(Task *parent);
+ ~JT_Roster();
+
+ void get();
+ void set(const Jid &, const QString &name, const QStringList &groups);
+ void remove(const Jid &);
+
+ const Roster & roster() const;
+
+ QString toString() const;
+ bool fromString(const QString &);
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ int type;
+ QDomElement iq;
+ Jid to;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushRoster : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushRoster(Task *parent);
+ ~JT_PushRoster();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void roster(const Roster &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Presence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Presence(Task *parent);
+ ~JT_Presence();
+
+ void pres(const Status &);
+ void pres(const Jid &, const Status &);
+ void sub(const Jid &, const QString &subType);
+
+ void onGo();
+
+ private:
+ QDomElement tag;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushPresence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushPresence(Task *parent);
+ ~JT_PushPresence();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void presence(const Jid &, const Status &);
+ void subscription(const Jid &, const QString &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_Message : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Message(Task *parent, const Message &);
+ ~JT_Message();
+
+ void onGo();
+
+ private:
+ Message m;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PushMessage : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PushMessage(Task *parent);
+ ~JT_PushMessage();
+
+ bool take(const QDomElement &);
+
+ signals:
+ void message(const Message &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_GetLastActivity : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_GetLastActivity(Task *);
+ ~JT_GetLastActivity();
+
+ void get(const Jid &);
+
+ int seconds() const;
+ const QString &message() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ class Private;
+ Private *d;
+
+ QDomElement iq;
+ Jid jid;
+ };
+
+ class JT_GetServices : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_GetServices(Task *);
+
+ void get(const Jid &);
+
+ const AgentList & agents() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ class Private;
+ Private *d;
+
+ QDomElement iq;
+ Jid jid;
+ AgentList agentList;
+ };
+
+ class JT_VCard : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_VCard(Task *parent);
+ ~JT_VCard();
+
+ void get(const Jid &);
+ void set(const VCard &);
+
+ const Jid & jid() const;
+ const VCard & vcard() const;
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_Search : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Search(Task *parent);
+ ~JT_Search();
+
+ const Form & form() const;
+ const QValueList<SearchResult> & results() const;
+
+ void get(const Jid &);
+ void set(const Form &);
+
+ void onGo();
+ bool take(const QDomElement &x);
+
+ private:
+ QDomElement iq;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_ClientVersion : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ClientVersion(Task *);
+
+ void get(const Jid &);
+ void onGo();
+ bool take(const QDomElement &);
+
+ const Jid & jid() const;
+ const QString & name() const;
+ const QString & version() const;
+ const QString & os() const;
+
+ private:
+ QDomElement iq;
+
+ Jid j;
+ QString v_name, v_ver, v_os;
+ };
+/*
+ class JT_ClientTime : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ClientTime(Task *, const Jid &);
+
+ void go();
+ bool take(const QDomElement &);
+
+ Jid j;
+ QDateTime utc;
+ QString timezone, display;
+
+ private:
+ QDomElement iq;
+ };
+*/
+ class JT_ServInfo : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_ServInfo(Task *);
+ ~JT_ServInfo();
+
+ bool take(const QDomElement &);
+ };
+
+ class JT_Gateway : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Gateway(Task *);
+
+ void get(const Jid &);
+ void set(const Jid &, const QString &prompt);
+ void onGo();
+ bool take(const QDomElement &);
+
+ Jid jid() const;
+ QString desc() const;
+ QString prompt() const;
+
+ private:
+ QDomElement iq;
+
+ int type;
+ Jid v_jid;
+ QString v_prompt, v_desc;
+ };
+
+ class JT_Browse : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_Browse(Task *);
+ ~JT_Browse();
+
+ void get(const Jid &);
+
+ const AgentList & agents() const;
+ const AgentItem & root() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+
+ AgentItem browseHelper (const QDomElement &i);
+ };
+
+ class JT_DiscoItems : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoItems(Task *);
+ ~JT_DiscoItems();
+
+ void get(const Jid &, const QString &node = QString::null);
+ void get(const DiscoItem &);
+
+ const DiscoList &items() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_DiscoInfo : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoInfo(Task *);
+ ~JT_DiscoInfo();
+
+ void get(const Jid &, const QString &node = QString::null, const DiscoItem::Identity = DiscoItem::Identity());
+ void get(const DiscoItem &);
+
+ const DiscoItem &item() const;
+ const Jid& jid() const;
+ const QString& node() const;
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_DiscoPublish : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_DiscoPublish(Task *);
+ ~JT_DiscoPublish();
+
+ void set(const Jid &, const DiscoList &);
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+ class JT_MucPresence : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_MucPresence(Task *parent);
+ ~JT_MucPresence();
+
+ void pres(const Status &);
+ void pres(const Jid &, const Status &, const QString &password);
+
+ void onGo();
+
+ private:
+ QDomElement tag;
+ int type;
+
+ class Private;
+ Private *d;
+ };
+
+ class JT_PrivateStorage : public Task
+ {
+ Q_OBJECT
+ public:
+ JT_PrivateStorage(Task *parent);
+ ~JT_PrivateStorage();
+
+ void set(const QDomElement &);
+ void get(const QString &tag, const QString& xmlns);
+
+ QDomElement element();
+
+ void onGo();
+ bool take(const QDomElement &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp
new file mode 100644
index 00000000..296c53c6
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.cpp
@@ -0,0 +1,1241 @@
+/*
+ * xmpp_vcard.cpp - classes for handling vCards
+ * Copyright (C) 2003 Michail Pishchagin
+ *
+ * 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 "xmpp_vcard.h"
+
+#include "base64.h"
+
+#include <qdom.h>
+#include <qdatetime.h>
+
+#include <qimage.h> // needed for image format recognition
+#include <qbuffer.h>
+
+// Justin's XML helper functions
+
+static QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+static QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = FALSE;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName().upper() == name.upper()) { // mblsha: ignore case when searching
+ if(found)
+ *found = TRUE;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}
+
+// mblsha's own functions
+
+static QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+ QDomElement tag = doc->createElement(name);
+
+ return tag;
+}
+
+static bool hasSubTag(const QDomElement &e, const QString &name)
+{
+ bool found;
+ findSubTag(e, name, &found);
+ return found;
+}
+
+static QString subTagText(const QDomElement &e, const QString &name)
+{
+ bool found;
+ QDomElement i = findSubTag(e, name, &found);
+ if ( found )
+ return i.text().stripWhiteSpace();
+ return QString::null;
+}
+
+using namespace XMPP;
+
+//----------------------------------------------------------------------------
+// VCard
+//----------------------------------------------------------------------------
+static QString image2type(const QByteArray &ba)
+{
+ QBuffer buf(ba);
+ buf.open(IO_ReadOnly);
+ QString format = QImageIO::imageFormat( &buf );
+
+ // TODO: add more formats
+ if ( format == "PNG" || format == "PsiPNG" )
+ return "image/png";
+ if ( format == "MNG" )
+ return "video/x-mng";
+ if ( format == "GIF" )
+ return "image/gif";
+ if ( format == "BMP" )
+ return "image/bmp";
+ if ( format == "XPM" )
+ return "image/x-xpm";
+ if ( format == "SVG" )
+ return "image/svg+xml";
+ if ( format == "JPEG" )
+ return "image/jpeg";
+
+ qWarning("WARNING! VCard::image2type: unknown format = '%s'", format.latin1());
+
+ return "image/unknown";
+}
+
+// Long lines of encoded binary data SHOULD BE folded to 75 characters using the folding method defined in [MIME-DIR].
+static QString foldString(const QString &s)
+{
+ QString ret;
+
+ for (int i = 0; i < (int)s.length(); i++) {
+ if ( !(i % 75) )
+ ret += '\n';
+ ret += s[i];
+ }
+
+ return ret;
+}
+
+class VCard::Private
+{
+public:
+ Private();
+ ~Private();
+
+ QString version;
+ QString fullName;
+ QString familyName, givenName, middleName, prefixName, suffixName;
+ QString nickName;
+
+ QByteArray photo;
+ QString photoURI;
+
+ QString bday;
+ AddressList addressList;
+ LabelList labelList;
+ PhoneList phoneList;
+ EmailList emailList;
+ QString jid;
+ QString mailer;
+ QString timezone;
+ Geo geo;
+ QString title;
+ QString role;
+
+ QByteArray logo;
+ QString logoURI;
+
+ VCard *agent;
+ QString agentURI;
+
+ Org org;
+ QStringList categories;
+ QString note;
+ QString prodId;
+ QString rev;
+ QString sortString;
+
+ QByteArray sound;
+ QString soundURI, soundPhonetic;
+
+ QString uid;
+ QString url;
+ QString desc;
+ PrivacyClass privacyClass;
+ QByteArray key;
+
+ bool isEmpty();
+};
+
+VCard::Private::Private()
+{
+ privacyClass = pcNone;
+ agent = 0;
+}
+
+VCard::Private::~Private()
+{
+ delete agent;
+}
+
+bool VCard::Private::isEmpty()
+{
+ if ( !version.isEmpty() ||
+ !fullName.isEmpty() ||
+ !familyName.isEmpty() || !givenName.isEmpty() || !middleName.isEmpty() || !prefixName.isEmpty() || !suffixName.isEmpty() ||
+ !nickName.isEmpty() ||
+ !photo.isEmpty() || !photoURI.isEmpty() ||
+ !bday.isEmpty() ||
+ !addressList.isEmpty() ||
+ !labelList.isEmpty() ||
+ !phoneList.isEmpty() ||
+ !emailList.isEmpty() ||
+ !jid.isEmpty() ||
+ !mailer.isEmpty() ||
+ !timezone.isEmpty() ||
+ !geo.lat.isEmpty() || !geo.lon.isEmpty() ||
+ !title.isEmpty() ||
+ !role.isEmpty() ||
+ !logo.isEmpty() || !logoURI.isEmpty() ||
+ (agent && !agent->isEmpty()) || !agentURI.isEmpty() ||
+ !org.name.isEmpty() || !org.unit.isEmpty() ||
+ !categories.isEmpty() ||
+ !note.isEmpty() ||
+ !prodId.isEmpty() ||
+ !rev.isEmpty() ||
+ !sortString.isEmpty() ||
+ !sound.isEmpty() || !soundURI.isEmpty() || !soundPhonetic.isEmpty() ||
+ !uid.isEmpty() ||
+ !url.isEmpty() ||
+ !desc.isEmpty() ||
+ (privacyClass != pcNone) ||
+ !key.isEmpty() )
+ {
+ return false;
+ }
+ return true;
+}
+
+VCard::VCard()
+{
+ d = new Private;
+}
+
+VCard::VCard(const VCard &from)
+{
+ d = new Private;
+ *this = from;
+}
+
+VCard & VCard::operator=(const VCard &from)
+{
+ if(d->agent) {
+ delete d->agent;
+ d->agent = 0;
+ }
+
+ *d = *from.d;
+
+ if(from.d->agent) {
+ // dup the agent
+ d->agent = new VCard(*from.d->agent);
+ }
+
+ return *this;
+}
+
+VCard::~VCard()
+{
+ delete d;
+}
+
+QDomElement VCard::toXml(QDomDocument *doc) const
+{
+ QDomElement v = doc->createElement("vCard");
+ v.setAttribute("version", "2.0");
+ v.setAttribute("prodid", "-//HandGen//NONSGML vGen v1.0//EN");
+ v.setAttribute("xmlns", "vcard-temp");
+
+ if ( !d->version.isEmpty() )
+ v.appendChild( textTag(doc, "VERSION", d->version) );
+ if ( !d->fullName.isEmpty() )
+ v.appendChild( textTag(doc, "FN", d->fullName) );
+
+ if ( !d->familyName.isEmpty() || !d->givenName.isEmpty() || !d->middleName.isEmpty() ||
+ !d->prefixName.isEmpty() || !d->suffixName.isEmpty() ) {
+ QDomElement w = doc->createElement("N");
+
+ if ( !d->familyName.isEmpty() )
+ w.appendChild( textTag(doc, "FAMILY", d->familyName) );
+ if ( !d->givenName.isEmpty() )
+ w.appendChild( textTag(doc, "GIVEN", d->givenName) );
+ if ( !d->middleName.isEmpty() )
+ w.appendChild( textTag(doc, "MIDDLE", d->middleName) );
+ if ( !d->prefixName.isEmpty() )
+ w.appendChild( textTag(doc, "PREFIX", d->prefixName) );
+ if ( !d->suffixName.isEmpty() )
+ w.appendChild( textTag(doc, "SUFFIX", d->suffixName) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->nickName.isEmpty() )
+ v.appendChild( textTag(doc, "NICKNAME", d->nickName) );
+
+ if ( !d->photo.isEmpty() || !d->photoURI.isEmpty() ) {
+ QDomElement w = doc->createElement("PHOTO");
+
+ if ( !d->photo.isEmpty() ) {
+ w.appendChild( textTag(doc, "TYPE", image2type(d->photo)) );
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->photo)) ) );
+ }
+ else if ( !d->photoURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->photoURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->bday.isEmpty() )
+ v.appendChild( textTag(doc, "BDAY", d->bday) );
+
+ if ( !d->addressList.isEmpty() ) {
+ AddressList::Iterator it = d->addressList.begin();
+ for ( ; it != d->addressList.end(); ++it ) {
+ QDomElement w = doc->createElement("ADR");
+ Address a = *it;
+
+ if ( a.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( a.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( a.postal )
+ w.appendChild( emptyTag(doc, "POSTAL") );
+ if ( a.parcel )
+ w.appendChild( emptyTag(doc, "PARCEL") );
+ if ( a.dom )
+ w.appendChild( emptyTag(doc, "DOM") );
+ if ( a.intl )
+ w.appendChild( emptyTag(doc, "INTL") );
+ if ( a.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !a.pobox.isEmpty() )
+ w.appendChild( textTag(doc, "POBOX", a.pobox) );
+ if ( !a.extaddr.isEmpty() )
+ w.appendChild( textTag(doc, "EXTADR", a.extaddr) );
+ if ( !a.street.isEmpty() )
+ w.appendChild( textTag(doc, "STREET", a.street) );
+ if ( !a.locality.isEmpty() )
+ w.appendChild( textTag(doc, "LOCALITY", a.locality) );
+ if ( !a.region.isEmpty() )
+ w.appendChild( textTag(doc, "REGION", a.region) );
+ if ( !a.pcode.isEmpty() )
+ w.appendChild( textTag(doc, "PCODE", a.pcode) );
+ if ( !a.country.isEmpty() )
+ w.appendChild( textTag(doc, "CTRY", a.country) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->labelList.isEmpty() ) {
+ LabelList::Iterator it = d->labelList.begin();
+ for ( ; it != d->labelList.end(); ++it ) {
+ QDomElement w = doc->createElement("LABEL");
+ Label l = *it;
+
+ if ( l.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( l.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( l.postal )
+ w.appendChild( emptyTag(doc, "POSTAL") );
+ if ( l.parcel )
+ w.appendChild( emptyTag(doc, "PARCEL") );
+ if ( l.dom )
+ w.appendChild( emptyTag(doc, "DOM") );
+ if ( l.intl )
+ w.appendChild( emptyTag(doc, "INTL") );
+ if ( l.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !l.lines.isEmpty() ) {
+ QStringList::Iterator it = l.lines.begin();
+ for ( ; it != l.lines.end(); ++it )
+ w.appendChild( textTag(doc, "LINE", *it) );
+ }
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->phoneList.isEmpty() ) {
+ PhoneList::Iterator it = d->phoneList.begin();
+ for ( ; it != d->phoneList.end(); ++it ) {
+ QDomElement w = doc->createElement("TEL");
+ Phone p = *it;
+
+ if ( p.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( p.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( p.voice )
+ w.appendChild( emptyTag(doc, "VOICE") );
+ if ( p.fax )
+ w.appendChild( emptyTag(doc, "FAX") );
+ if ( p.pager )
+ w.appendChild( emptyTag(doc, "PAGER") );
+ if ( p.msg )
+ w.appendChild( emptyTag(doc, "MSG") );
+ if ( p.cell )
+ w.appendChild( emptyTag(doc, "CELL") );
+ if ( p.video )
+ w.appendChild( emptyTag(doc, "VIDEO") );
+ if ( p.bbs )
+ w.appendChild( emptyTag(doc, "BBS") );
+ if ( p.modem )
+ w.appendChild( emptyTag(doc, "MODEM") );
+ if ( p.isdn )
+ w.appendChild( emptyTag(doc, "ISDN") );
+ if ( p.pcs )
+ w.appendChild( emptyTag(doc, "PCS") );
+ if ( p.pref )
+ w.appendChild( emptyTag(doc, "PREF") );
+
+ if ( !p.number.isEmpty() )
+ w.appendChild( textTag(doc, "NUMBER", p.number) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->emailList.isEmpty() ) {
+ EmailList::Iterator it = d->emailList.begin();
+ for ( ; it != d->emailList.end(); ++it ) {
+ QDomElement w = doc->createElement("EMAIL");
+ Email e = *it;
+
+ if ( e.home )
+ w.appendChild( emptyTag(doc, "HOME") );
+ if ( e.work )
+ w.appendChild( emptyTag(doc, "WORK") );
+ if ( e.internet )
+ w.appendChild( emptyTag(doc, "INTERNET") );
+ if ( e.x400 )
+ w.appendChild( emptyTag(doc, "X400") );
+
+ if ( !e.userid.isEmpty() )
+ w.appendChild( textTag(doc, "USERID", e.userid) );
+
+ v.appendChild(w);
+ }
+ }
+
+ if ( !d->jid.isEmpty() )
+ v.appendChild( textTag(doc, "JABBERID", d->jid) );
+ if ( !d->mailer.isEmpty() )
+ v.appendChild( textTag(doc, "MAILER", d->mailer) );
+ if ( !d->timezone.isEmpty() )
+ v.appendChild( textTag(doc, "TZ", d->timezone) );
+
+ if ( !d->geo.lat.isEmpty() || !d->geo.lon.isEmpty() ) {
+ QDomElement w = doc->createElement("GEO");
+
+ if ( !d->geo.lat.isEmpty() )
+ w.appendChild( textTag(doc, "LAT", d->geo.lat) );
+ if ( !d->geo.lon.isEmpty() )
+ w.appendChild( textTag(doc, "LON", d->geo.lon));
+
+ v.appendChild(w);
+ }
+
+ if ( !d->title.isEmpty() )
+ v.appendChild( textTag(doc, "TITLE", d->title) );
+ if ( !d->role.isEmpty() )
+ v.appendChild( textTag(doc, "ROLE", d->role) );
+
+ if ( !d->logo.isEmpty() || !d->logoURI.isEmpty() ) {
+ QDomElement w = doc->createElement("LOGO");
+
+ if ( !d->logo.isEmpty() ) {
+ w.appendChild( textTag(doc, "TYPE", image2type(d->logo)) );
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->logo)) ) );
+ }
+ else if ( !d->logoURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->logoURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->agentURI.isEmpty() || (d->agent && d->agent->isEmpty()) ) {
+ QDomElement w = doc->createElement("AGENT");
+
+ if ( d->agent && !d->agent->isEmpty() )
+ w.appendChild( d->agent->toXml(doc) );
+ else if ( !d->agentURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->agentURI) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->org.name.isEmpty() || !d->org.unit.isEmpty() ) {
+ QDomElement w = doc->createElement("ORG");
+
+ if ( !d->org.name.isEmpty() )
+ w.appendChild( textTag(doc, "ORGNAME", d->org.name) );
+
+ if ( !d->org.unit.isEmpty() ) {
+ QStringList::Iterator it = d->org.unit.begin();
+ for ( ; it != d->org.unit.end(); ++it )
+ w.appendChild( textTag(doc, "ORGUNIT", *it) );
+ }
+
+ v.appendChild(w);
+ }
+
+ if ( !d->categories.isEmpty() ) {
+ QDomElement w = doc->createElement("CATEGORIES");
+
+ QStringList::Iterator it = d->categories.begin();
+ for ( ; it != d->categories.end(); ++it )
+ w.appendChild( textTag(doc, "KEYWORD", *it) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->note.isEmpty() )
+ v.appendChild( textTag(doc, "NOTE", d->note) );
+ if ( !d->prodId.isEmpty() )
+ v.appendChild( textTag(doc, "PRODID", d->prodId) );
+ if ( !d->rev.isEmpty() )
+ v.appendChild( textTag(doc, "REV", d->rev) );
+ if ( !d->sortString.isEmpty() )
+ v.appendChild( textTag(doc, "SORT-STRING", d->sortString) );
+
+ if ( !d->sound.isEmpty() || !d->soundURI.isEmpty() || !d->soundPhonetic.isEmpty() ) {
+ QDomElement w = doc->createElement("SOUND");
+
+ if ( !d->sound.isEmpty() )
+ w.appendChild( textTag(doc, "BINVAL", foldString( Base64::arrayToString(d->sound)) ) );
+ else if ( !d->soundURI.isEmpty() )
+ w.appendChild( textTag(doc, "EXTVAL", d->soundURI) );
+ else if ( !d->soundPhonetic.isEmpty() )
+ w.appendChild( textTag(doc, "PHONETIC", d->soundPhonetic) );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->uid.isEmpty() )
+ v.appendChild( textTag(doc, "UID", d->uid) );
+ if ( !d->url.isEmpty() )
+ v.appendChild( textTag(doc, "URL", d->url) );
+ if ( !d->desc.isEmpty() )
+ v.appendChild( textTag(doc, "DESC", d->desc) );
+
+ if ( d->privacyClass != pcNone ) {
+ QDomElement w = doc->createElement("CLASS");
+
+ if ( d->privacyClass == pcPublic )
+ w.appendChild( emptyTag(doc, "PUBLIC") );
+ else if ( d->privacyClass == pcPrivate )
+ w.appendChild( emptyTag(doc, "PRIVATE") );
+ else if ( d->privacyClass == pcConfidential )
+ w.appendChild( emptyTag(doc, "CONFIDENTIAL") );
+
+ v.appendChild(w);
+ }
+
+ if ( !d->key.isEmpty() ) {
+ QDomElement w = doc->createElement("KEY");
+
+ // TODO: Justin, please check out this code
+ w.appendChild( textTag(doc, "TYPE", "text/plain")); // FIXME
+ w.appendChild( textTag(doc, "CRED", QString::fromUtf8(d->key)) ); // FIXME
+
+ v.appendChild(w);
+ }
+
+ return v;
+}
+
+bool VCard::fromXml(const QDomElement &q)
+{
+ if ( q.tagName().upper() != "VCARD" )
+ return false;
+
+ QDomNode n = q.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() ) {
+ QDomElement i = n.toElement();
+ if ( i.isNull() )
+ continue;
+
+ QString tag = i.tagName().upper();
+
+ bool found;
+ QDomElement e;
+
+ if ( tag == "VERSION" )
+ d->version = i.text().stripWhiteSpace();
+ else if ( tag == "FN" )
+ d->fullName = i.text().stripWhiteSpace();
+ else if ( tag == "N" ) {
+ d->familyName = subTagText(i, "FAMILY");
+ d->givenName = subTagText(i, "GIVEN");
+ d->middleName = subTagText(i, "MIDDLE");
+ d->prefixName = subTagText(i, "PREFIX");
+ d->suffixName = subTagText(i, "SUFFIX");
+ }
+ else if ( tag == "NICKNAME" )
+ d->nickName = i.text().stripWhiteSpace();
+ else if ( tag == "PHOTO" ) {
+ d->photo = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->photoURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "BDAY" )
+ d->bday = i.text().stripWhiteSpace();
+ else if ( tag == "ADR" ) {
+ Address a;
+
+ a.home = hasSubTag(i, "HOME");
+ a.work = hasSubTag(i, "WORK");
+ a.postal = hasSubTag(i, "POSTAL");
+ a.parcel = hasSubTag(i, "PARCEL");
+ a.dom = hasSubTag(i, "DOM");
+ a.intl = hasSubTag(i, "INTL");
+ a.pref = hasSubTag(i, "PREF");
+
+ a.pobox = subTagText(i, "POBOX");
+ a.extaddr = subTagText(i, "EXTADR");
+ a.street = subTagText(i, "STREET");
+ a.locality = subTagText(i, "LOCALITY");
+ a.region = subTagText(i, "REGION");
+ a.pcode = subTagText(i, "PCODE");
+ a.country = subTagText(i, "CTRY");
+
+ if ( a.country.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "COUNTRY") )
+ a.country = subTagText(i, "COUNTRY");
+
+ if ( a.extaddr.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "EXTADD") )
+ a.extaddr = subTagText(i, "EXTADD");
+
+ d->addressList.append ( a );
+ }
+ else if ( tag == "LABEL" ) {
+ Label l;
+
+ l.home = hasSubTag(i, "HOME");
+ l.work = hasSubTag(i, "WORK");
+ l.postal = hasSubTag(i, "POSTAL");
+ l.parcel = hasSubTag(i, "PARCEL");
+ l.dom = hasSubTag(i, "DOM");
+ l.intl = hasSubTag(i, "INTL");
+ l.pref = hasSubTag(i, "PREF");
+
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ii = nn.toElement();
+ if ( ii.isNull() )
+ continue;
+
+ if ( ii.tagName().upper() == "LINE" )
+ l.lines.append ( ii.text().stripWhiteSpace() );
+ }
+
+ d->labelList.append ( l );
+ }
+ else if ( tag == "TEL" ) {
+ Phone p;
+
+ p.home = hasSubTag(i, "HOME");
+ p.work = hasSubTag(i, "WORK");
+ p.voice = hasSubTag(i, "VOICE");
+ p.fax = hasSubTag(i, "FAX");
+ p.pager = hasSubTag(i, "PAGER");
+ p.msg = hasSubTag(i, "MSG");
+ p.cell = hasSubTag(i, "CELL");
+ p.video = hasSubTag(i, "VIDEO");
+ p.bbs = hasSubTag(i, "BBS");
+ p.modem = hasSubTag(i, "MODEM");
+ p.isdn = hasSubTag(i, "ISDN");
+ p.pcs = hasSubTag(i, "PCS");
+ p.pref = hasSubTag(i, "PREF");
+
+ p.number = subTagText(i, "NUMBER");
+
+ if ( p.number.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( hasSubTag(i, "VOICE") )
+ p.number = subTagText(i, "VOICE");
+
+ d->phoneList.append ( p );
+ }
+ else if ( tag == "EMAIL" ) {
+ Email m;
+
+ m.home = hasSubTag(i, "HOME");
+ m.work = hasSubTag(i, "WORK");
+ m.internet = hasSubTag(i, "INTERNET");
+ m.x400 = hasSubTag(i, "X400");
+
+ m.userid = subTagText(i, "USERID");
+
+ if ( m.userid.isEmpty() ) // FIXME: Workaround for Psi prior to 0.9
+ if ( !i.text().isEmpty() )
+ m.userid = i.text().stripWhiteSpace();
+
+ d->emailList.append ( m );
+ }
+ else if ( tag == "JABBERID" )
+ d->jid = i.text().stripWhiteSpace();
+ else if ( tag == "MAILER" )
+ d->mailer = i.text().stripWhiteSpace();
+ else if ( tag == "TZ" )
+ d->timezone = i.text().stripWhiteSpace();
+ else if ( tag == "GEO" ) {
+ d->geo.lat = subTagText(i, "LAT");
+ d->geo.lon = subTagText(i, "LON");
+ }
+ else if ( tag == "TITLE" )
+ d->title = i.text().stripWhiteSpace();
+ else if ( tag == "ROLE" )
+ d->role = i.text().stripWhiteSpace();
+ else if ( tag == "LOGO" ) {
+ d->logo = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->logoURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "AGENT" ) {
+ e = findSubTag(i, "VCARD", &found);
+ if ( found ) {
+ VCard a;
+ if ( a.fromXml(e) ) {
+ if ( !d->agent )
+ d->agent = new VCard;
+ *(d->agent) = a;
+ }
+ }
+
+ d->agentURI = subTagText(i, "EXTVAL");
+ }
+ else if ( tag == "ORG" ) {
+ d->org.name = subTagText(i, "ORGNAME");
+
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ii = nn.toElement();
+ if ( ii.isNull() )
+ continue;
+
+ if ( ii.tagName().upper() == "ORGUNIT" )
+ d->org.unit.append( ii.text().stripWhiteSpace() );
+ }
+ }
+ else if ( tag == "CATEGORIES") {
+ QDomNode nn = i.firstChild();
+ for ( ; !nn.isNull(); nn = nn.nextSibling() ) {
+ QDomElement ee = nn.toElement();
+ if ( ee.isNull() )
+ continue;
+
+ if ( ee.tagName().upper() == "KEYWORD" )
+ d->categories << ee.text().stripWhiteSpace();
+ }
+ }
+ else if ( tag == "NOTE" )
+ d->note = i.text().stripWhiteSpace();
+ else if ( tag == "PRODID" )
+ d->prodId = i.text().stripWhiteSpace();
+ else if ( tag == "REV" )
+ d->rev = i.text().stripWhiteSpace();
+ else if ( tag == "SORT-STRING" )
+ d->sortString = i.text().stripWhiteSpace();
+ else if ( tag == "SOUND" ) {
+ d->sound = Base64::stringToArray( subTagText(i, "BINVAL") );
+ d->soundURI = subTagText(i, "EXTVAL");
+ d->soundPhonetic = subTagText(i, "PHONETIC");
+ }
+ else if ( tag == "UID" )
+ d->uid = i.text().stripWhiteSpace();
+ else if ( tag == "URL")
+ d->url = i.text().stripWhiteSpace();
+ else if ( tag == "DESC" )
+ d->desc = i.text().stripWhiteSpace();
+ else if ( tag == "CLASS" ) {
+ if ( hasSubTag(i, "PUBLIC") )
+ d->privacyClass = pcPublic;
+ else if ( hasSubTag(i, "PRIVATE") )
+ d->privacyClass = pcPrivate;
+ else if ( hasSubTag(i, "CONFIDENTIAL") )
+ d->privacyClass = pcConfidential;
+ }
+ else if ( tag == "KEY" ) {
+ // TODO: Justin, please check out this code
+ e = findSubTag(i, "TYPE", &found);
+ QString type = "text/plain";
+ if ( found )
+ type = e.text().stripWhiteSpace();
+
+ e = findSubTag(i, "CRED", &found );
+ if ( !found )
+ e = findSubTag(i, "BINVAL", &found); // case for very clever clients ;-)
+
+ if ( found )
+ d->key = e.text().utf8(); // FIXME
+ }
+ }
+
+ return true;
+}
+
+bool VCard::isEmpty() const
+{
+ return d->isEmpty();
+}
+
+// Some constructors
+
+VCard::Address::Address()
+{
+ home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Label::Label()
+{
+ home = work = postal = parcel = dom = intl = pref = false;
+}
+
+VCard::Phone::Phone()
+{
+ home = work = voice = fax = pager = msg = cell = video = bbs = modem = isdn = pcs = pref = false;
+}
+
+VCard::Email::Email()
+{
+ home = work = internet = x400 = false;
+}
+
+VCard::Geo::Geo()
+{
+}
+
+VCard::Org::Org()
+{
+}
+
+// vCard properties...
+
+const QString &VCard::version() const
+{
+ return d->version;
+}
+
+void VCard::setVersion(const QString &v)
+{
+ d->version = v;
+}
+
+const QString &VCard::fullName() const
+{
+ return d->fullName;
+}
+
+void VCard::setFullName(const QString &n)
+{
+ d->fullName = n;
+}
+
+const QString &VCard::familyName() const
+{
+ return d->familyName;
+}
+
+void VCard::setFamilyName(const QString &n)
+{
+ d->familyName = n;
+}
+
+const QString &VCard::givenName() const
+{
+ return d->givenName;
+}
+
+void VCard::setGivenName(const QString &n)
+{
+ d->givenName = n;
+}
+
+const QString &VCard::middleName() const
+{
+ return d->middleName;
+}
+
+void VCard::setMiddleName(const QString &n)
+{
+ d->middleName = n;
+}
+
+const QString &VCard::prefixName() const
+{
+ return d->prefixName;
+}
+
+void VCard::setPrefixName(const QString &p)
+{
+ d->prefixName = p;
+}
+
+const QString &VCard::suffixName() const
+{
+ return d->suffixName;
+}
+
+void VCard::setSuffixName(const QString &s)
+{
+ d->suffixName = s;
+}
+
+const QString &VCard::nickName() const
+{
+ return d->nickName;
+}
+
+void VCard::setNickName(const QString &n)
+{
+ d->nickName = n;
+}
+
+const QByteArray &VCard::photo() const
+{
+ return d->photo;
+}
+
+void VCard::setPhoto(const QByteArray &i)
+{
+ d->photo = i;
+}
+
+const QString &VCard::photoURI() const
+{
+ return d->photoURI;
+}
+
+void VCard::setPhotoURI(const QString &p)
+{
+ d->photoURI = p;
+}
+
+const QDate VCard::bday() const
+{
+ return QDate::fromString(d->bday);
+}
+
+void VCard::setBday(const QDate &date)
+{
+ d->bday = date.toString();
+}
+
+const QString &VCard::bdayStr() const
+{
+ return d->bday;
+}
+
+void VCard::setBdayStr(const QString &date)
+{
+ d->bday = date;
+}
+
+const VCard::AddressList &VCard::addressList() const
+{
+ return d->addressList;
+}
+
+void VCard::setAddressList(const VCard::AddressList &a)
+{
+ d->addressList = a;
+}
+
+const VCard::LabelList &VCard::labelList() const
+{
+ return d->labelList;
+}
+
+void VCard::setLabelList(const VCard::LabelList &l)
+{
+ d->labelList = l;
+}
+
+const VCard::PhoneList &VCard::phoneList() const
+{
+ return d->phoneList;
+}
+
+void VCard::setPhoneList(const VCard::PhoneList &p)
+{
+ d->phoneList = p;
+}
+
+const VCard::EmailList &VCard::emailList() const
+{
+ return d->emailList;
+}
+
+void VCard::setEmailList(const VCard::EmailList &e)
+{
+ d->emailList = e;
+}
+
+const QString &VCard::jid() const
+{
+ return d->jid;
+}
+
+void VCard::setJid(const QString &j)
+{
+ d->jid = j;
+}
+
+const QString &VCard::mailer() const
+{
+ return d->mailer;
+}
+
+void VCard::setMailer(const QString &m)
+{
+ d->mailer = m;
+}
+
+const QString &VCard::timezone() const
+{
+ return d->timezone;
+}
+
+void VCard::setTimezone(const QString &t)
+{
+ d->timezone = t;
+}
+
+const VCard::Geo &VCard::geo() const
+{
+ return d->geo;
+}
+
+void VCard::setGeo(const VCard::Geo &g)
+{
+ d->geo = g;
+}
+
+const QString &VCard::title() const
+{
+ return d->title;
+}
+
+void VCard::setTitle(const QString &t)
+{
+ d->title = t;
+}
+
+const QString &VCard::role() const
+{
+ return d->role;
+}
+
+void VCard::setRole(const QString &r)
+{
+ d->role = r;
+}
+
+const QByteArray &VCard::logo() const
+{
+ return d->logo;
+}
+
+void VCard::setLogo(const QByteArray &i)
+{
+ d->logo = i;
+}
+
+const QString &VCard::logoURI() const
+{
+ return d->logoURI;
+}
+
+void VCard::setLogoURI(const QString &l)
+{
+ d->logoURI = l;
+}
+
+const VCard *VCard::agent() const
+{
+ return d->agent;
+}
+
+void VCard::setAgent(const VCard &v)
+{
+ if ( !d->agent )
+ d->agent = new VCard;
+ *(d->agent) = v;
+}
+
+const QString VCard::agentURI() const
+{
+ return d->agentURI;
+}
+
+void VCard::setAgentURI(const QString &a)
+{
+ d->agentURI = a;
+}
+
+const VCard::Org &VCard::org() const
+{
+ return d->org;
+}
+
+void VCard::setOrg(const VCard::Org &o)
+{
+ d->org = o;
+}
+
+const QStringList &VCard::categories() const
+{
+ return d->categories;
+}
+
+void VCard::setCategories(const QStringList &c)
+{
+ d->categories = c;
+}
+
+const QString &VCard::note() const
+{
+ return d->note;
+}
+
+void VCard::setNote(const QString &n)
+{
+ d->note = n;
+}
+
+const QString &VCard::prodId() const
+{
+ return d->prodId;
+}
+
+void VCard::setProdId(const QString &p)
+{
+ d->prodId = p;
+}
+
+const QString &VCard::rev() const
+{
+ return d->rev;
+}
+
+void VCard::setRev(const QString &r)
+{
+ d->rev = r;
+}
+
+const QString &VCard::sortString() const
+{
+ return d->sortString;
+}
+
+void VCard::setSortString(const QString &s)
+{
+ d->sortString = s;
+}
+
+const QByteArray &VCard::sound() const
+{
+ return d->sound;
+}
+
+void VCard::setSound(const QByteArray &s)
+{
+ d->sound = s;
+}
+
+const QString &VCard::soundURI() const
+{
+ return d->soundURI;
+}
+
+void VCard::setSoundURI(const QString &s)
+{
+ d->soundURI = s;
+}
+
+const QString &VCard::soundPhonetic() const
+{
+ return d->soundPhonetic;
+}
+
+void VCard::setSoundPhonetic(const QString &s)
+{
+ d->soundPhonetic = s;
+}
+
+const QString &VCard::uid() const
+{
+ return d->uid;
+}
+
+void VCard::setUid(const QString &u)
+{
+ d->uid = u;
+}
+
+const QString &VCard::url() const
+{
+ return d->url;
+}
+
+void VCard::setUrl(const QString &u)
+{
+ d->url = u;
+}
+
+const QString &VCard::desc() const
+{
+ return d->desc;
+}
+
+void VCard::setDesc(const QString &desc)
+{
+ d->desc = desc;
+}
+
+const VCard::PrivacyClass &VCard::privacyClass() const
+{
+ return d->privacyClass;
+}
+
+void VCard::setPrivacyClass(const VCard::PrivacyClass &c)
+{
+ d->privacyClass = c;
+}
+
+const QByteArray &VCard::key() const
+{
+ return d->key;
+}
+
+void VCard::setKey(const QByteArray &k)
+{
+ d->key = k;
+}
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h
new file mode 100644
index 00000000..ae8cc873
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_vcard.h
@@ -0,0 +1,284 @@
+/*
+ * xmpp_vcard.h - classes for handling vCards
+ * Copyright (C) 2003 Michail Pishchagin
+ *
+ * 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 JABBER_VCARD_H
+#define JABBER_VCARD_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcstring.h>
+
+#include <qvaluelist.h>
+#include <qdom.h>
+
+class QDate;
+
+namespace XMPP
+{
+ class VCard
+ {
+ public:
+ VCard();
+ VCard(const VCard &);
+ VCard & operator=(const VCard &);
+ ~VCard();
+
+ QDomElement toXml(QDomDocument *) const;
+ bool fromXml(const QDomElement &);
+ bool isEmpty() const;
+
+ const QString &version() const;
+ void setVersion(const QString &);
+
+ const QString &fullName() const;
+ void setFullName(const QString &);
+
+
+ const QString &familyName() const;
+ void setFamilyName(const QString &);
+
+ const QString &givenName() const;
+ void setGivenName(const QString &);
+
+ const QString &middleName() const;
+ void setMiddleName(const QString &);
+
+ const QString &prefixName() const;
+ void setPrefixName(const QString &);
+
+ const QString &suffixName() const;
+ void setSuffixName(const QString &);
+
+
+ const QString &nickName() const;
+ void setNickName(const QString &);
+
+
+ const QByteArray &photo() const;
+ void setPhoto(const QByteArray &);
+
+ const QString &photoURI() const;
+ void setPhotoURI(const QString &);
+
+
+ const QDate bday() const;
+ void setBday(const QDate &);
+
+ const QString &bdayStr() const;
+ void setBdayStr(const QString &);
+
+
+ class Address {
+ public:
+ Address();
+
+ bool home;
+ bool work;
+ bool postal;
+ bool parcel;
+
+ bool dom;
+ bool intl;
+
+ bool pref;
+
+ QString pobox;
+ QString extaddr;
+ QString street;
+ QString locality;
+ QString region;
+ QString pcode;
+ QString country;
+ };
+ typedef QValueList<Address> AddressList;
+ const AddressList &addressList() const;
+ void setAddressList(const AddressList &);
+
+ class Label {
+ public:
+ Label();
+
+ bool home;
+ bool work;
+ bool postal;
+ bool parcel;
+
+ bool dom;
+ bool intl;
+
+ bool pref;
+
+ QStringList lines;
+ };
+ typedef QValueList<Label> LabelList;
+ const LabelList &labelList() const;
+ void setLabelList(const LabelList &);
+
+
+ class Phone {
+ public:
+ Phone();
+
+ bool home;
+ bool work;
+ bool voice;
+ bool fax;
+ bool pager;
+ bool msg;
+ bool cell;
+ bool video;
+ bool bbs;
+ bool modem;
+ bool isdn;
+ bool pcs;
+ bool pref;
+
+ QString number;
+ };
+ typedef QValueList<Phone> PhoneList;
+ const PhoneList &phoneList() const;
+ void setPhoneList(const PhoneList &);
+
+
+ class Email {
+ public:
+ Email();
+
+ bool home;
+ bool work;
+ bool internet;
+ bool x400;
+
+ QString userid;
+ };
+ typedef QValueList<Email> EmailList;
+ const EmailList &emailList() const;
+ void setEmailList(const EmailList &);
+
+
+ const QString &jid() const;
+ void setJid(const QString &);
+
+ const QString &mailer() const;
+ void setMailer(const QString &);
+
+ const QString &timezone() const;
+ void setTimezone(const QString &);
+
+
+ class Geo {
+ public:
+ Geo();
+
+ QString lat;
+ QString lon;
+ };
+ const Geo &geo() const;
+ void setGeo(const Geo &);
+
+
+ const QString &title() const;
+ void setTitle(const QString &);
+
+ const QString &role() const;
+ void setRole(const QString &);
+
+
+ const QByteArray &logo() const;
+ void setLogo(const QByteArray &);
+
+ const QString &logoURI() const;
+ void setLogoURI(const QString &);
+
+
+ const VCard *agent() const;
+ void setAgent(const VCard &);
+
+ const QString agentURI() const;
+ void setAgentURI(const QString &);
+
+
+ class Org {
+ public:
+ Org();
+
+ QString name;
+ QStringList unit;
+ };
+ const Org &org() const;
+ void setOrg(const Org &);
+
+
+ const QStringList &categories() const;
+ void setCategories(const QStringList &);
+
+ const QString &note() const;
+ void setNote(const QString &);
+
+ const QString &prodId() const; // it must equal to "Psi" ;-)
+ void setProdId(const QString &);
+
+ const QString &rev() const;
+ void setRev(const QString &);
+
+ const QString &sortString() const;
+ void setSortString(const QString &);
+
+
+ const QByteArray &sound() const;
+ void setSound(const QByteArray &);
+
+ const QString &soundURI() const;
+ void setSoundURI(const QString &);
+
+ const QString &soundPhonetic() const;
+ void setSoundPhonetic(const QString &);
+
+
+ const QString &uid() const;
+ void setUid(const QString &);
+
+ const QString &url() const;
+ void setUrl(const QString &);
+
+ const QString &desc() const;
+ void setDesc(const QString &);
+
+
+ enum PrivacyClass {
+ pcNone = 0,
+ pcPublic = 1,
+ pcPrivate,
+ pcConfidential
+ };
+ const PrivacyClass &privacyClass() const;
+ void setPrivacyClass(const PrivacyClass &);
+
+
+ const QByteArray &key() const;
+ void setKey(const QByteArray &);
+
+ private:
+ class Private;
+ Private *d;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp
new file mode 100644
index 00000000..2715faf8
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.cpp
@@ -0,0 +1,386 @@
+/*
+ * xmlcommon.cpp - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002 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"xmpp_xmlcommon.h"
+
+#include <qstring.h>
+#include <qdom.h>
+#include <qdatetime.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qstringlist.h>
+#include <qcolor.h>
+
+#include"im.h"
+
+bool stamp2TS(const QString &ts, QDateTime *d)
+{
+ if(ts.length() != 17)
+ return false;
+
+ int year = ts.mid(0,4).toInt();
+ int month = ts.mid(4,2).toInt();
+ int day = ts.mid(6,2).toInt();
+
+ int hour = ts.mid(9,2).toInt();
+ int min = ts.mid(12,2).toInt();
+ int sec = ts.mid(15,2).toInt();
+
+ QDate xd;
+ xd.setYMD(year, month, day);
+ if(!xd.isValid())
+ return false;
+
+ QTime xt;
+ xt.setHMS(hour, min, sec);
+ if(!xt.isValid())
+ return false;
+
+ d->setDate(xd);
+ d->setTime(xt);
+
+ return true;
+}
+
+QString TS2stamp(const QDateTime &d)
+{
+ QString str;
+
+ str.sprintf("%04d%02d%02dT%02d:%02d:%02d",
+ d.date().year(),
+ d.date().month(),
+ d.date().day(),
+ d.time().hour(),
+ d.time().minute(),
+ d.time().second());
+
+ return str;
+}
+
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc->createElement(name);
+ QDomText text = doc->createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}
+
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = false;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == name) {
+ if(found)
+ *found = true;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}
+
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id)
+{
+ QDomElement iq = doc->createElement("iq");
+ if(!type.isEmpty())
+ iq.setAttribute("type", type);
+ if(!to.isEmpty())
+ iq.setAttribute("to", to);
+ if(!id.isEmpty())
+ iq.setAttribute("id", id);
+
+ return iq;
+}
+
+QDomElement queryTag(const QDomElement &e)
+{
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ return q;
+}
+
+QString queryNS(const QDomElement &e)
+{
+ bool found;
+ QDomElement q = findSubTag(e, "query", &found);
+ if(found)
+ return q.attribute("xmlns");
+
+ return "";
+}
+
+void getErrorFromElement(const QDomElement &e, int *code, QString *str)
+{
+ bool found;
+ QDomElement tag = findSubTag(e, "error", &found);
+ if(!found)
+ return;
+
+ if(code)
+ *code = tag.attribute("code").toInt();
+ if(str)
+ *str = tagContent(tag);
+}
+
+//----------------------------------------------------------------------------
+// XMLHelper
+//----------------------------------------------------------------------------
+
+namespace XMLHelper {
+
+QDomElement emptyTag(QDomDocument *doc, const QString &name)
+{
+ QDomElement tag = doc->createElement(name);
+
+ return tag;
+}
+
+bool hasSubTag(const QDomElement &e, const QString &name)
+{
+ bool found;
+ findSubTag(e, name, &found);
+ return found;
+}
+
+QString subTagText(const QDomElement &e, const QString &name)
+{
+ bool found;
+ QDomElement i = findSubTag(e, name, &found);
+ if ( found )
+ return i.text();
+ return QString::null;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(content);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, int content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(QString::number(content));
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, bool content)
+{
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(content ? "true" : "false");
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s)
+{
+ QString str;
+ str.sprintf("%d,%d", s.width(), s.height());
+
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(str);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r)
+{
+ QString str;
+ str.sprintf("%d,%d,%d,%d", r.x(), r.y(), r.width(), r.height());
+
+ QDomElement tag = doc.createElement(name);
+ QDomText text = doc.createTextNode(str);
+ tag.appendChild(text);
+
+ return tag;
+}
+
+QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l)
+{
+ QDomElement tag = doc.createElement(name);
+ for(QStringList::ConstIterator it = l.begin(); it != l.end(); ++it)
+ tag.appendChild(textTag(doc, "item", *it));
+
+ return tag;
+}
+
+/*QString tagContent(const QDomElement &e)
+{
+ // look for some tag content
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomText i = n.toText();
+ if(i.isNull())
+ continue;
+ return i.data();
+ }
+
+ return "";
+}*/
+
+/*QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found)
+{
+ if(found)
+ *found = FALSE;
+
+ for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == name) {
+ if(found)
+ *found = TRUE;
+ return i;
+ }
+ }
+
+ QDomElement tmp;
+ return tmp;
+}*/
+
+void readEntry(const QDomElement &e, const QString &name, QString *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = tagContent(tag);
+}
+
+void readNumEntry(const QDomElement &e, const QString &name, int *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = tagContent(tag).toInt();
+}
+
+void readBoolEntry(const QDomElement &e, const QString &name, bool *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ *v = (tagContent(tag) == "true") ? TRUE: FALSE;
+}
+
+void readSizeEntry(const QDomElement &e, const QString &name, QSize *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list = QStringList::split(',', tagContent(tag));
+ if(list.count() != 2)
+ return;
+ QSize s;
+ s.setWidth(list[0].toInt());
+ s.setHeight(list[1].toInt());
+ *v = s;
+}
+
+void readRectEntry(const QDomElement &e, const QString &name, QRect *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list = QStringList::split(',', tagContent(tag));
+ if(list.count() != 4)
+ return;
+ QRect r;
+ r.setX(list[0].toInt());
+ r.setY(list[1].toInt());
+ r.setWidth(list[2].toInt());
+ r.setHeight(list[3].toInt());
+ *v = r;
+}
+
+void readColorEntry(const QDomElement &e, const QString &name, QColor *v)
+{
+ bool found = FALSE;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QColor c;
+ c.setNamedColor(tagContent(tag));
+ if(c.isValid())
+ *v = c;
+}
+
+void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v)
+{
+ bool found = false;
+ QDomElement tag = findSubTag(e, name, &found);
+ if(!found)
+ return;
+ QStringList list;
+ for(QDomNode n = tag.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ QDomElement i = n.toElement();
+ if(i.isNull())
+ continue;
+ if(i.tagName() == "item")
+ list += tagContent(i);
+ }
+ *v = list;
+}
+
+void setBoolAttribute(QDomElement e, const QString &name, bool b)
+{
+ e.setAttribute(name, b ? "true" : "false");
+}
+
+void readBoolAttribute(QDomElement e, const QString &name, bool *v)
+{
+ if(e.hasAttribute(name)) {
+ QString s = e.attribute(name);
+ *v = (s == "true") ? TRUE: FALSE;
+ }
+}
+
+}
+
diff --git a/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h
new file mode 100644
index 00000000..f0499c4b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/iris/xmpp-im/xmpp_xmlcommon.h
@@ -0,0 +1,71 @@
+/*
+ * xmlcommon.h - helper functions for dealing with XML
+ * Copyright (C) 2001, 2002 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 JABBER_XMLCOMMON_H
+#define JABBER_XMLCOMMON_H
+
+#include<qdom.h>
+
+class QDateTime;
+class QRect;
+class QSize;
+class QColor;
+class QStringList;
+
+bool stamp2TS(const QString &ts, QDateTime *d);
+QString TS2stamp(const QDateTime &d);
+QDomElement textTag(QDomDocument *doc, const QString &name, const QString &content);
+QString tagContent(const QDomElement &e);
+QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id);
+QDomElement queryTag(const QDomElement &e);
+QString queryNS(const QDomElement &e);
+void getErrorFromElement(const QDomElement &e, int *code, QString *str);
+
+namespace XMLHelper {
+ //QDomElement findSubTag(const QDomElement &e, const QString &name, bool *found);
+ bool hasSubTag(const QDomElement &e, const QString &name);
+
+ QDomElement emptyTag(QDomDocument *doc, const QString &name);
+ QString subTagText(const QDomElement &e, const QString &name);
+
+ QDomElement textTag(QDomDocument &doc, const QString &name, const QString &content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, int content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, bool content);
+ QDomElement textTag(QDomDocument &doc, const QString &name, QSize &s);
+ QDomElement textTag(QDomDocument &doc, const QString &name, QRect &r);
+ QDomElement stringListToXml(QDomDocument &doc, const QString &name, const QStringList &l);
+
+ void readEntry(const QDomElement &e, const QString &name, QString *v);
+ void readNumEntry(const QDomElement &e, const QString &name, int *v);
+ void readBoolEntry(const QDomElement &e, const QString &name, bool *v);
+ void readSizeEntry(const QDomElement &e, const QString &name, QSize *v);
+ void readRectEntry(const QDomElement &e, const QString &name, QRect *v);
+ void readColorEntry(const QDomElement &e, const QString &name, QColor *v);
+
+ void xmlToStringList(const QDomElement &e, const QString &name, QStringList *v);
+
+ void setBoolAttribute(QDomElement e, const QString &name, bool b);
+ void readBoolAttribute(QDomElement e, const QString &name, bool *v);
+
+ //QString tagContent(const QDomElement &e); // obsolete;
+}
+
+#endif
diff --git a/kopete/protocols/jabber/libiris/jingle_iris.patch b/kopete/protocols/jabber/libiris/jingle_iris.patch
new file mode 100644
index 00000000..41acad0e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/jingle_iris.patch
@@ -0,0 +1,432 @@
+diff -ur psi/iris/include/im.h psi-jingle/iris/include/im.h
+--- psi/iris/include/im.h 2005-12-27 15:12:42.000000000 +0100
++++ psi-jingle/iris/include/im.h 2005-12-27 11:05:53.000000000 +0100
+@@ -22,6 +22,7 @@
+ #define XMPP_IM_H
+
+ #include<qdatetime.h>
++#include<qvaluelist.h>
+ #include"xmpp.h"
+
+ namespace XMPP
+@@ -153,6 +154,9 @@
+
+ const QString & xsigned() const;
+ const QString & songTitle() const;
++ const QString & capsNode() const;
++ const QString & capsVersion() const;
++ const QString & capsExt() const;
+
+ void setPriority(int);
+ void setShow(const QString &);
+@@ -162,6 +166,9 @@
+ void setIsAvailable(bool);
+ void setIsInvisible(bool);
+ void setError(int, const QString &);
++ void setCapsNode(const QString&);
++ void setCapsVersion(const QString&);
++ void setCapsExt(const QString&);
+
+ void setXSigned(const QString &);
+ void setSongTitle(const QString &);
+@@ -176,6 +183,7 @@
+ QString v_xsigned;
+ // gabber song extension
+ QString v_songTitle;
++ QString v_capsNode, v_capsVersion, v_capsExt;
+
+ int ecode;
+ QString estr;
+@@ -285,6 +293,7 @@
+ bool canRegister() const;
+ bool canSearch() const;
+ bool canGroupchat() const;
++ bool canVoice() const;
+ bool canDisco() const;
+ bool isGateway() const;
+ bool haveVCard() const;
+@@ -567,12 +576,25 @@
+ int timeZoneOffset() const;
+ QString clientName() const;
+ QString clientVersion() const;
++ QString capsNode() const;
++ QString capsVersion() const;
++ QString capsExt() const;
+
+ void setOSName(const QString &);
+ void setTimeZone(const QString &, int);
+ void setClientName(const QString &);
+ void setClientVersion(const QString &);
++ void setCapsNode(const QString &);
++ void setCapsVersion(const QString &);
+
++ void setIdentity(DiscoItem::Identity);
++ DiscoItem::Identity identity();
++
++ void addExtension(const QString& ext, const Features& f);
++ void removeExtension(const QString& ext);
++ const Features& extension(const QString& ext) const;
++ QStringList extensions() const;
++
+ S5BManager *s5bManager() const;
+ IBBManager *ibbManager() const;
+ JidLinkManager *jidLinkManager() const;
+diff -ur psi/iris/xmpp-im/client.cpp psi-jingle/iris/xmpp-im/client.cpp
+--- psi/iris/xmpp-im/client.cpp 2005-12-27 15:12:44.000000000 +0100
++++ psi-jingle/iris/xmpp-im/client.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -70,6 +70,7 @@
+ //! \endcode
+
+ #include<stdarg.h>
++#include<qmap.h>
+ #include<qobjectlist.h>
+ #include<qtimer.h>
+ #include<qguardedptr.h>
+@@ -125,7 +126,9 @@
+ int id_seed;
+ Task *root;
+ QString host, user, pass, resource;
+- QString osname, tzname, clientName, clientVersion;
++ QString osname, tzname, clientName, clientVersion, capsNode, capsVersion, capsExt;
++ DiscoItem::Identity identity;
++ QMap<QString,Features> extension_features;
+ int tzoffset;
+ bool active;
+
+@@ -149,6 +152,9 @@
+ d->osname = "N/A";
+ d->clientName = "N/A";
+ d->clientVersion = "0.0";
++ d->capsNode = "";
++ d->capsVersion = "";
++ d->capsExt = "";
+
+ d->id_seed = 0xaaaa;
+ d->root = new Task(this, true);
+@@ -996,6 +1002,21 @@
+ return d->clientVersion;
+ }
+
++QString Client::capsNode() const
++{
++ return d->capsNode;
++}
++
++QString Client::capsVersion() const
++{
++ return d->capsVersion;
++}
++
++QString Client::capsExt() const
++{
++ return d->capsExt;
++}
++
+ void Client::setOSName(const QString &name)
+ {
+ d->osname = name;
+@@ -1017,6 +1038,52 @@
+ d->clientVersion = s;
+ }
+
++void Client::setCapsNode(const QString &s)
++{
++ d->capsNode = s;
++}
++
++void Client::setCapsVersion(const QString &s)
++{
++ d->capsVersion = s;
++}
++
++DiscoItem::Identity Client::identity()
++{
++ return d->identity;
++}
++
++void Client::setIdentity(DiscoItem::Identity identity)
++{
++ d->identity = identity;
++}
++
++void Client::addExtension(const QString& ext, const Features& features)
++{
++ if (!ext.isEmpty()) {
++ d->extension_features[ext] = features;
++ d->capsExt = extensions().join(" ");
++ }
++}
++
++void Client::removeExtension(const QString& ext)
++{
++ if (d->extension_features.contains(ext)) {
++ d->extension_features.remove(ext);
++ d->capsExt = extensions().join(" ");
++ }
++}
++
++QStringList Client::extensions() const
++{
++ return d->extension_features.keys();
++}
++
++const Features& Client::extension(const QString& ext) const
++{
++ return d->extension_features[ext];
++}
++
+ void Client::s5b_incomingReady()
+ {
+ S5BConnection *c = d->s5bman->takeIncoming();
+diff -ur psi/iris/xmpp-im/types.cpp psi-jingle/iris/xmpp-im/types.cpp
+--- psi/iris/xmpp-im/types.cpp 2005-12-27 15:12:55.000000000 +0100
++++ psi-jingle/iris/xmpp-im/types.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -784,6 +784,21 @@
+ v_songTitle = _songtitle;
+ }
+
++void Status::setCapsNode(const QString & _capsNode)
++{
++ v_capsNode = _capsNode;
++}
++
++void Status::setCapsVersion(const QString & _capsVersion)
++{
++ v_capsVersion = _capsVersion;
++}
++
++void Status::setCapsExt(const QString & _capsExt)
++{
++ v_capsExt = _capsExt;
++}
++
+ bool Status::isAvailable() const
+ {
+ return v_isAvailable;
+@@ -836,6 +851,21 @@
+ return v_songTitle;
+ }
+
++const QString & Status::capsNode() const
++{
++ return v_capsNode;
++}
++
++const QString & Status::capsVersion() const
++{
++ return v_capsVersion;
++}
++
++const QString & Status::capsExt() const
++{
++ return v_capsExt;
++}
++
+ int Status::errorCode() const
+ {
+ return ecode;
+@@ -1427,6 +1457,15 @@
+ return test(ns);
+ }
+
++#define FID_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
++bool Features::canVoice() const
++{
++ QStringList ns;
++ ns << FID_VOICE;
++
++ return test(ns);
++}
++
+ #define FID_GATEWAY "jabber:iq:gateway"
+ bool Features::isGateway() const
+ {
+diff -ur psi/iris/xmpp-im/xmpp_tasks.cpp psi-jingle/iris/xmpp-im/xmpp_tasks.cpp
+--- psi/iris/xmpp-im/xmpp_tasks.cpp 2005-12-27 15:12:45.000000000 +0100
++++ psi-jingle/iris/xmpp-im/xmpp_tasks.cpp 2005-12-27 11:05:53.000000000 +0100
+@@ -516,6 +516,16 @@
+ x.setAttribute("xmlns", "jabber:x:signed");
+ tag.appendChild(x);
+ }
++
++ if(!s.capsNode().isEmpty() && !s.capsVersion().isEmpty()) {
++ QDomElement c = doc()->createElement("c");
++ c.setAttribute("xmlns","http://jabber.org/protocol/caps");
++ c.setAttribute("node",s.capsNode());
++ c.setAttribute("ver",s.capsVersion());
++ if (!s.capsExt().isEmpty())
++ c.setAttribute("ext",s.capsExt());
++ tag.appendChild(c);
++ }
+ }
+ }
+
+@@ -625,6 +635,11 @@
+ else if(i.tagName() == "x" && i.attribute("xmlns") == "http://jabber.org/protocol/e2e") {
+ p.setKeyID(tagContent(i));
+ }
++ else if(i.tagName() == "c" && i.attribute("xmlns") == "http://jabber.org/protocol/caps") {
++ p.setCapsNode(i.attribute("node"));
++ p.setCapsVersion(i.attribute("ver"));
++ p.setCapsExt(i.attribute("ext"));
++ }
+ }
+
+ presence(j, p);
+@@ -1265,23 +1280,86 @@
+ // return TRUE;
+ //}
+ else if(ns == "http://jabber.org/protocol/disco#info") {
++ // Find out the node
++ QString node;
++ bool found;
++ QDomElement q = findSubTag(e, "query", &found);
++ if(found) // NOTE: Should always be true, since a NS was found above
++ node = q.attribute("node");
++
+ QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
++ if (!node.isEmpty())
++ query.setAttribute("node", node);
+ iq.appendChild(query);
+- QDomElement feature;
+
+- feature = doc()->createElement("feature");
+- feature.setAttribute("var", "http://jabber.org/protocol/bytestreams");
+- query.appendChild(feature);
+-
+- feature = doc()->createElement("feature");
+- feature.setAttribute("var", "http://jabber.org/protocol/si");
+- query.appendChild(feature);
+-
+- feature = doc()->createElement("feature");
+- feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
+- query.appendChild(feature);
++ // Identity
++ DiscoItem::Identity identity = client()->identity();
++ QDomElement id = doc()->createElement("identity");
++ if (!identity.category.isEmpty() && !identity.type.isEmpty()) {
++ id.setAttribute("category",identity.category);
++ id.setAttribute("type",identity.type);
++ if (!identity.name.isEmpty()) {
++ id.setAttribute("name",identity.name);
++ }
++ }
++ else {
++ // Default values
++ id.setAttribute("category","client");
++ id.setAttribute("type","pc");
++ }
++ query.appendChild(id);
++
++ QDomElement feature;
++ if (node.isEmpty() || node == client()->capsNode() + "#" + client()->capsVersion()) {
++ // Standard features
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", "http://jabber.org/protocol/bytestreams");
++ query.appendChild(feature);
++
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", "http://jabber.org/protocol/si");
++ query.appendChild(feature);
++
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", "http://jabber.org/protocol/si/profile/file-transfer");
++ query.appendChild(feature);
++
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", "http://jabber.org/protocol/disco#info");
++ query.appendChild(feature);
++
++ if (node.isEmpty()) {
++ // Extended features
++ QStringList exts = client()->extensions();
++ for (QStringList::ConstIterator i = exts.begin(); i != exts.end(); ++i) {
++ const QStringList& l = client()->extension(*i).list();
++ for ( QStringList::ConstIterator j = l.begin(); j != l.end(); ++j ) {
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", *j);
++ query.appendChild(feature);
++ }
++ }
++ }
++ }
++ else if (node.startsWith(client()->capsNode() + "#")) {
++ QString ext = node.right(node.length()-client()->capsNode().length()-1);
++ if (client()->extensions().contains(ext)) {
++ const QStringList& l = client()->extension(ext).list();
++ for ( QStringList::ConstIterator it = l.begin(); it != l.end(); ++it ) {
++ feature = doc()->createElement("feature");
++ feature.setAttribute("var", *it);
++ query.appendChild(feature);
++ }
++ }
++ else {
++ // TODO: ERROR
++ }
++ }
++ else {
++ // TODO: ERROR
++ }
+
+ send(iq);
+ return true;
+@@ -1599,6 +1677,7 @@
+
+ QDomElement iq;
+ Jid jid;
++ QString node;
+ DiscoItem item;
+ };
+
+@@ -1626,6 +1705,7 @@
+ d->item = DiscoItem(); // clear item
+
+ d->jid = j;
++ d->node = node;
+ d->iq = createIQ(doc(), "get", d->jid.full(), id());
+ QDomElement query = doc()->createElement("query");
+ query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
+@@ -1648,6 +1728,29 @@
+ d->iq.appendChild(query);
+ }
+
++
++/**
++ * Original requested jid.
++ * Is here because sometimes the responder does not include this information
++ * in the reply.
++ */
++const Jid& JT_DiscoInfo::jid() const
++{
++ return d->jid;
++}
++
++/**
++ * Original requested node.
++ * Is here because sometimes the responder does not include this information
++ * in the reply.
++ */
++const QString& JT_DiscoInfo::node() const
++{
++ return d->node;
++}
++
++
++
+ const DiscoItem &JT_DiscoInfo::item() const
+ {
+ return d->item;
+diff -ur psi/iris/xmpp-im/xmpp_tasks.h psi-jingle/iris/xmpp-im/xmpp_tasks.h
+--- psi/iris/xmpp-im/xmpp_tasks.h 2005-12-27 15:12:45.000000000 +0100
++++ psi-jingle/iris/xmpp-im/xmpp_tasks.h 2005-12-27 11:05:53.000000000 +0100
+@@ -389,6 +389,8 @@
+ void get(const DiscoItem &);
+
+ const DiscoItem &item() const;
++ const Jid& jid() const;
++ const QString& node() const;
+
+ void onGo();
+ bool take(const QDomElement &);
diff --git a/kopete/protocols/jabber/libiris/qca/COPYING b/kopete/protocols/jabber/libiris/qca/COPYING
new file mode 100644
index 00000000..b1e3f5a2
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/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/jabber/libiris/qca/INSTALL b/kopete/protocols/jabber/libiris/qca/INSTALL
new file mode 100644
index 00000000..8dd34099
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/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/jabber/libiris/qca/Makefile.am b/kopete/protocols/jabber/libiris/qca/Makefile.am
new file mode 100644
index 00000000..af437a64
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = src
diff --git a/kopete/protocols/jabber/libiris/qca/README b/kopete/protocols/jabber/libiris/qca/README
new file mode 100644
index 00000000..0641713a
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/README
@@ -0,0 +1,29 @@
+Qt Cryptographic Architecture
+-----------------------------
+Version: API v1.0, Plugin v1
+Author: Justin Karneges <justin@affinix.com>
+Date: September 10th 2003
+
+This library provides an easy API for the following features:
+
+ SSL/TLS
+ X509
+ SASL
+ RSA
+ Hashing (SHA1, MD5)
+ Ciphers (BlowFish, 3DES, AES)
+
+Functionality is supplied via plugins. This is useful for avoiding
+dependence on a particular crypto library and makes upgrading easier,
+as there is no need to recompile your application when adding or
+upgrading a crypto plugin. Also, by pushing crypto functionality into
+plugins, your application is free of legal issues, such as export
+regulation.
+
+And of course, you get a very simple crypto API for Qt, where you can
+do things like:
+
+ QString hash = QCA::SHA1::hashToString(blockOfData);
+
+Have fun!
+
diff --git a/kopete/protocols/jabber/libiris/qca/TODO b/kopete/protocols/jabber/libiris/qca/TODO
new file mode 100644
index 00000000..bc8247e0
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/TODO
@@ -0,0 +1,6 @@
+* plugins: thread safety ?
+
+* dsa
+* diffie-hellman
+* entropy
+
diff --git a/kopete/protocols/jabber/libiris/qca/src/Makefile.am b/kopete/protocols/jabber/libiris/qca/src/Makefile.am
new file mode 100644
index 00000000..b43d303e
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/Makefile.am
@@ -0,0 +1,7 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libqca.la
+INCLUDES = $(all_includes)
+
+libqca_la_SOURCES = \
+ qca.cpp
diff --git a/kopete/protocols/jabber/libiris/qca/src/qca.cpp b/kopete/protocols/jabber/libiris/qca/src/qca.cpp
new file mode 100644
index 00000000..5b67e6e3
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/qca/src/qca.cpp
@@ -0,0 +1,1481 @@
+/*
+ * 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;
+ 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;
+ d->c = from.d->c->clone();
+ 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;
+ 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/jabber/libiris/qca/src/qca.h b/kopete/protocols/jabber/libiris/qca/src/qca.h
new file mode 100644
index 00000000..e7cd1609
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/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/jabber/libiris/qca/src/qcaprovider.h b/kopete/protocols/jabber/libiris/qca/src/qcaprovider.h
new file mode 100644
index 00000000..a7f1805b
--- /dev/null
+++ b/kopete/protocols/jabber/libiris/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/jabber/ui/Makefile.am b/kopete/protocols/jabber/ui/Makefile.am
new file mode 100644
index 00000000..caf81209
--- /dev/null
+++ b/kopete/protocols/jabber/ui/Makefile.am
@@ -0,0 +1,31 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/../libiris/iris/include \
+ -I$(srcdir)/../libiris/iris/xmpp-im \
+ -I$(srcdir)/../libiris/iris/jabber \
+ -I$(srcdir)/../libiris/qca/src \
+ -I$(srcdir)/../libiris/cutestuff/util \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopetejabberui.la
+
+libkopetejabberui_la_SOURCES = dlgsendraw.ui dlgjabbersendraw.cpp \
+ dlgaddcontact.ui jabberaddcontactpage.cpp dlgvcard.ui dlgjabbervcard.cpp \
+ dlgjabberservices.cpp dlgregister.ui dlgjabberregister.cpp dlgbrowse.ui dlgjabberbrowse.cpp \
+ dlgjabbereditaccountwidget.ui jabbereditaccountwidget.cpp dlgjabberregisteraccount.ui \
+ jabberregisteraccount.cpp dlgjabberchooseserver.ui jabberchooseserver.cpp dlgchangepassword.ui \
+ dlgjabberchangepassword.cpp empty.cpp dlgchatroomslist.ui dlgjabberchatroomslist.cpp dlgchatjoin.ui \
+ dlgjabberchatjoin.cpp dlgservices.ui
+
+EXTRA_DIST = dlgjabbereditaccountwidget.ui \
+ dlgsendraw.ui \
+ dlgaddcontact.ui \
+ dlgrename.ui \
+ dlgvcard.ui \
+ dlgservices.ui \
+ dlgregister.ui \
+ dlgbrowse.ui
+
+
+noinst_HEADERS = dlgjabberchatroomslist.h dlgjabberchatjoin.h
diff --git a/kopete/protocols/jabber/ui/dlgaddcontact.ui b/kopete/protocols/jabber/ui/dlgaddcontact.ui
new file mode 100644
index 00000000..20a4ab98
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgaddcontact.ui
@@ -0,0 +1,105 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgAddContact</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>dlgAddContact</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>398</width>
+ <height>345</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Add Contacts</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblID</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Jabber ID:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The Jabber ID for the account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The Jabber ID for the account you would like to add. Note that this must include the username and the domain (like an E-mail address), as there are many Jabber servers.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The Jabber ID for the account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The Jabber ID for the account you would like to add. Note that this must include the username and the domain (like an E-mail address), as there are many Jabber servers.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: joe@jabber.org)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>190</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgbrowse.ui b/kopete/protocols/jabber/ui/dlgbrowse.ui
new file mode 100644
index 00000000..f45f224a
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgbrowse.ui
@@ -0,0 +1,201 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgBrowse</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>dlgBrowse</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>818</width>
+ <height>381</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Jabber Search</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSplitter" row="0" column="0">
+ <property name="name">
+ <cstring>splitter1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>dynamicForm</cstring>
+ </property>
+ <property name="title">
+ <string>Search For</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblWait</cstring>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <property name="text">
+ <string>Please wait while retrieving search form...</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QTable">
+ <column>
+ <property name="text">
+ <string>JID</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>First Name</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Last Name</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Nick</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Email</string>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>tblResults</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="resizePolicy">
+ <enum>AutoOneFit</enum>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>5</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>NoSelection</enum>
+ </property>
+ <property name="focusStyle">
+ <enum>FollowStyle</enum>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>buttonsLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>buttonsSpacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnSearch</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Search</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnClose</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>btnClose</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgBrowse</receiver>
+ <slot>close()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgchangepassword.ui b/kopete/protocols/jabber/ui/dlgchangepassword.ui
new file mode 100644
index 00000000..c3e34de6
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgchangepassword.ui
@@ -0,0 +1,88 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>DlgChangePassword</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgChangePassword</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>308</width>
+ <height>147</height>
+ </rect>
+ </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>Current password:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>New password:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>New password:</string>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="0" column="1">
+ <property name="name">
+ <cstring>peCurrentPassword</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="1" column="1">
+ <property name="name">
+ <cstring>peNewPassword1</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="2" column="1">
+ <property name="name">
+ <cstring>peNewPassword2</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lblStatus</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Please enter your current password first
+and then your new password twice.</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgchatjoin.ui b/kopete/protocols/jabber/ui/dlgchatjoin.ui
new file mode 100644
index 00000000..699f9ef4
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgchatjoin.ui
@@ -0,0 +1,130 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgChatJoin</class>
+<widget class="KDialog">
+ <property name="name">
+ <cstring>dlgChatJoin</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>291</width>
+ <height>160</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblNick</cstring>
+ </property>
+ <property name="text">
+ <string>Nick:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leServer</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>leNick</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leRoom</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblRoom</cstring>
+ </property>
+ <property name="text">
+ <string>Room:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbJoin</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Join</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbBrowse</cstring>
+ </property>
+ <property name="text">
+ <string>Bro&amp;wse</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>pbJoin</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgChatJoin</receiver>
+ <slot>slotJoin()</slot>
+ </connection>
+ <connection>
+ <sender>pbBrowse</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgChatJoin</receiver>
+ <slot>slotBowse()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>leRoom</tabstop>
+ <tabstop>leServer</tabstop>
+ <tabstop>leNick</tabstop>
+</tabstops>
+<slots>
+ <slot>slotBowse()</slot>
+ <slot>slotJoin()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgchatroomslist.ui b/kopete/protocols/jabber/ui/dlgchatroomslist.ui
new file mode 100644
index 00000000..f4624de3
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgchatroomslist.ui
@@ -0,0 +1,185 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgChatRoomsList</class>
+<widget class="KDialog">
+ <property name="name">
+ <cstring>dlgChatRoomsList</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>467</width>
+ <height>298</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>List Chatrooms</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="text">
+ <string>Server</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leServer</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbQuery</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Query</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QTable">
+ <column>
+ <property name="text">
+ <string>Chatroom Name</string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Chatroom Description</string>
+ </property>
+ <property name="pixmap">
+ <pixmap></pixmap>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>tblChatRoomsList</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>ClickFocus</enum>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>2</number>
+ </property>
+ <property name="rowMovingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="columnMovingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>SingleRow</enum>
+ </property>
+ <property name="focusStyle">
+ <enum>FollowStyle</enum>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>121</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbJoin</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Join</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbClose</cstring>
+ </property>
+ <property name="text">
+ <string>Clos&amp;e</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>pbClose</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgChatRoomsList</receiver>
+ <slot>close()</slot>
+ </connection>
+ <connection>
+ <sender>pbJoin</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgChatRoomsList</receiver>
+ <slot>slotJoin()</slot>
+ </connection>
+ <connection>
+ <sender>pbQuery</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgChatRoomsList</receiver>
+ <slot>slotQuery()</slot>
+ </connection>
+ <connection>
+ <sender>tblChatRoomsList</sender>
+ <signal>clicked(int,int,int,const QPoint&amp;)</signal>
+ <receiver>dlgChatRoomsList</receiver>
+ <slot>slotClick(int,int,int,const QPoint&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>tblChatRoomsList</sender>
+ <signal>doubleClicked(int,int,int,const QPoint&amp;)</signal>
+ <receiver>dlgChatRoomsList</receiver>
+ <slot>slotDoubleClick(int,int,int,const QPoint&amp;)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>slotQuery()</slot>
+ <slot>slotJoin()</slot>
+ <slot>slotClick(int row, int col, int button, const QPoint&amp; mousePos)</slot>
+ <slot>slotDoubleClick(int row, int col, int button, const QPoint&amp; mousePos)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kdialog.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgjabberbrowse.cpp b/kopete/protocols/jabber/ui/dlgjabberbrowse.cpp
new file mode 100644
index 00000000..f8e2b4ce
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberbrowse.cpp
@@ -0,0 +1,144 @@
+
+/***************************************************************************
+ dlgjabberbrowse.cpp - description
+ -------------------
+ begin : Wed Dec 11 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kpushbutton.h>
+#include <qgroupbox.h>
+#include <qtable.h>
+#include <qlabel.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+#include "jabberclient.h"
+#include "jabberformtranslator.h"
+#include "dlgjabberbrowse.h"
+
+dlgJabberBrowse::dlgJabberBrowse (JabberAccount *account, const XMPP::Jid & jid, QWidget * parent, const char *name):dlgBrowse (parent, name)
+{
+ m_account = account;
+
+ // disable the left margin
+ tblResults->setLeftMargin (0);
+
+ // no content for now
+ tblResults->setNumRows (0);
+
+ // disable user selections
+ tblResults->setSelectionMode (QTable::NoSelection);
+
+ XMPP::JT_Search * task = new XMPP::JT_Search (m_account->client()->rootTask ());
+
+ connect (task, SIGNAL (finished ()), this, SLOT (slotGotForm ()));
+
+ task->get (jid);
+ task->go (true);
+}
+
+void dlgJabberBrowse::slotGotForm ()
+{
+ XMPP::JT_Search * task = (XMPP::JT_Search *) sender ();
+
+ // delete the wait message
+ delete lblWait;
+
+ if (!task->success ())
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Information, i18n ("Unable to retrieve search form."), i18n ("Jabber Error"));
+
+ return;
+ }
+
+
+ // translate the form and create it inside the display widget
+ translator = new JabberFormTranslator (task->form (), dynamicForm);
+ dynamicForm->layout()->add( translator );
+ translator->show();
+
+ // enable the send button
+ btnSearch->setEnabled (true);
+
+ // adjust table
+ tblResults->setNumCols (5);
+
+ for (int i = 0; i < 5; i++)
+ {
+ // allow autostretching
+ tblResults->setColumnStretchable (i, true);
+ }
+
+ connect (btnSearch, SIGNAL (clicked ()), this, SLOT (slotSendForm ()));
+
+}
+
+void dlgJabberBrowse::slotSendForm ()
+{
+
+ XMPP::JT_Search * task = new XMPP::JT_Search (m_account->client()->rootTask ());
+
+ connect (task, SIGNAL (finished ()), this, SLOT (slotSentForm ()));
+
+ task->set (translator->resultData ());
+ task->go (true);
+
+ btnSearch->setEnabled (false);
+ btnClose->setEnabled (false);
+
+}
+
+void dlgJabberBrowse::slotSentForm ()
+{
+ XMPP::JT_Search * task = (XMPP::JT_Search *) sender ();
+
+ btnSearch->setEnabled (true);
+ btnClose->setEnabled (true);
+
+ if (!task->success ())
+ {
+ KMessageBox::queuedMessageBox (this, KMessageBox::Error, i18n ("The Jabber server declined the search."), i18n ("Jabber Search"));
+
+ return;
+ }
+
+ tblResults->setNumRows (task->results ().count ());
+
+ int row = 0;
+
+ for (QValueList < XMPP::SearchResult >::const_iterator it = task->results ().begin (); it != task->results ().end (); ++it)
+ {
+ tblResults->setText (row, 0, (*it).jid ().userHost ());
+ tblResults->setText (row, 1, (*it).first ());
+ tblResults->setText (row, 2, (*it).last ());
+ tblResults->setText (row, 3, (*it).nick ());
+ tblResults->setText (row, 4, (*it).email ());
+
+ row++;
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ tblResults->setColumnStretchable (i, false);
+ tblResults->adjustColumn (i);
+ }
+}
+
+dlgJabberBrowse::~dlgJabberBrowse ()
+{
+}
+
+#include "dlgjabberbrowse.moc"
diff --git a/kopete/protocols/jabber/ui/dlgjabberbrowse.h b/kopete/protocols/jabber/ui/dlgjabberbrowse.h
new file mode 100644
index 00000000..8a8b6cc1
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberbrowse.h
@@ -0,0 +1,54 @@
+
+/***************************************************************************
+ dlgjabberbrowse.h - description
+ -------------------
+ begin : Wed Dec 11 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERBROWSE_H
+#define DLGJABBERBROWSE_H
+
+#include <qwidget.h>
+
+#include "xmpp_tasks.h"
+
+#include "jabberaccount.h"
+#include "jabberformtranslator.h"
+#include "dlgbrowse.h"
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class dlgJabberBrowse:public dlgBrowse
+{
+
+ Q_OBJECT
+
+public:
+ dlgJabberBrowse (JabberAccount *account, const XMPP::Jid & jid, QWidget * parent = 0, const char *name = 0);
+ ~dlgJabberBrowse ();
+
+private slots:
+ void slotGotForm ();
+ void slotSendForm ();
+ void slotSentForm ();
+
+private:
+ JabberAccount *m_account;
+ JabberFormTranslator * translator;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/dlgjabberchangepassword.cpp b/kopete/protocols/jabber/ui/dlgjabberchangepassword.cpp
new file mode 100644
index 00000000..9f6ae65d
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchangepassword.cpp
@@ -0,0 +1,135 @@
+
+/***************************************************************************
+ Change the password of a Jabber account
+ -------------------
+ begin : Tue May 31 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2005 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "dlgjabberchangepassword.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpassdlg.h>
+#include <kmessagebox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <kopetepassword.h>
+#include <xmpp_tasks.h>
+#include "jabberaccount.h"
+#include "dlgchangepassword.h"
+
+DlgJabberChangePassword::DlgJabberChangePassword ( JabberAccount *account, QWidget *parent, const char *name )
+ : KDialogBase ( parent, name, true, i18n("Change Jabber Password"),
+ KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true )
+{
+
+ m_account = account;
+
+ m_mainWidget = new DlgChangePassword ( this );
+ setMainWidget ( m_mainWidget );
+
+}
+
+DlgJabberChangePassword::~DlgJabberChangePassword()
+{
+}
+
+void DlgJabberChangePassword::slotOk ()
+{
+
+ if ( !strlen ( m_mainWidget->peCurrentPassword->password () )
+ || ( m_account->password().cachedValue () != m_mainWidget->peCurrentPassword->password () ) )
+ {
+ KMessageBox::queuedMessageBox ( this, KMessageBox::Sorry,
+ i18n ( "You entered your current password incorrectly." ),
+ i18n ( "Password Incorrect" ) );
+ return;
+ }
+
+ if ( strcmp ( m_mainWidget->peNewPassword1->password (), m_mainWidget->peNewPassword2->password () ) != 0 )
+ {
+ KMessageBox::queuedMessageBox ( this, KMessageBox::Sorry,
+ i18n ( "Your new passwords do not match. Please enter them again." ),
+ i18n ( "Password Incorrect" ) );
+ return;
+ }
+
+ if ( !strlen ( m_mainWidget->peNewPassword1->password () ) )
+ {
+ KMessageBox::queuedMessageBox ( this, KMessageBox::Sorry,
+ i18n ( "For security reasons, you are not allowed to set an empty password." ),
+ i18n ( "Password Incorrect" ) );
+ return;
+ }
+
+ if ( !m_account->isConnected () )
+ {
+ if ( KMessageBox::questionYesNo ( this,
+ i18n ( "Your account needs to be connected before the password can be changed. Do you want to try to connect now?" ),
+ i18n ( "Jabber Password Change" ), i18n("Connect"), i18n("Stay Offline") ) == KMessageBox::Yes )
+ {
+ connect ( m_account, SIGNAL ( isConnectedChanged () ), this, SLOT ( slotChangePassword () ) );
+ m_account->connect ();
+ }
+ }
+ else
+ {
+ slotChangePassword ();
+ }
+
+}
+
+void DlgJabberChangePassword::slotCancel ()
+{
+
+ deleteLater ();
+
+}
+
+void DlgJabberChangePassword::slotChangePassword ()
+{
+
+ XMPP::JT_Register *task = new XMPP::JT_Register ( m_account->client()->rootTask () );
+ QObject::connect ( task, SIGNAL ( finished () ), this, SLOT ( slotChangePasswordDone () ) );
+
+ task->changepw ( m_mainWidget->peNewPassword1->password () );
+ task->go ( true );
+
+}
+
+void DlgJabberChangePassword::slotChangePasswordDone ()
+{
+
+ XMPP::JT_Register *task = (XMPP::JT_Register *) sender ();
+
+ if ( task->success () )
+ {
+ KMessageBox::queuedMessageBox ( dynamic_cast<QWidget*>(parent()), KMessageBox::Information,
+ i18n ( "Your password has been changed successfully. Please note that the change may not be instantaneous. If you have problems logging in with your new password, please contact the administrator." ),
+ i18n ( "Jabber Password Change" ) );
+
+ m_account->password().set ( m_mainWidget->peNewPassword1->password () );
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox ( dynamic_cast<QWidget*>(parent()), KMessageBox::Sorry,
+ i18n ( "Your password could not be changed. Either your server does not support this feature or the administrator does not allow you to change your password." ) );
+ }
+
+ deleteLater();
+
+}
+
+#include "dlgjabberchangepassword.moc"
diff --git a/kopete/protocols/jabber/ui/dlgjabberchangepassword.h b/kopete/protocols/jabber/ui/dlgjabberchangepassword.h
new file mode 100644
index 00000000..84e880c5
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchangepassword.h
@@ -0,0 +1,51 @@
+
+/***************************************************************************
+ Change the password of a Jabber account
+ -------------------
+ begin : Tue May 31 2005
+ copyright : (C) 2005 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2005 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERCHANGEPASSWORD_H
+#define DLGJABBERCHANGEPASSWORD_H
+
+#include <kdialogbase.h>
+
+class JabberAccount;
+class DlgChangePassword;
+
+/**
+@author Till Gerken
+*/
+class DlgJabberChangePassword : public KDialogBase
+{
+
+Q_OBJECT
+
+public:
+ DlgJabberChangePassword ( JabberAccount *account, QWidget *parent = 0, const char *name = 0);
+ ~DlgJabberChangePassword();
+
+private slots:
+ void slotOk ();
+ void slotCancel ();
+ void slotChangePassword ();
+ void slotChangePasswordDone ();
+
+private:
+ DlgChangePassword *m_mainWidget;
+ JabberAccount *m_account;
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/dlgjabberchatjoin.cpp b/kopete/protocols/jabber/ui/dlgjabberchatjoin.cpp
new file mode 100644
index 00000000..620bff03
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchatjoin.cpp
@@ -0,0 +1,129 @@
+
+/***************************************************************************
+ dlgjabberchatjoin.cpp - description
+ -------------------
+ begin : Fri Dec 13 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "dlgjabberchatroomslist.h"
+
+#include "dlgjabberchatjoin.h"
+
+dlgJabberChatJoin::dlgJabberChatJoin(JabberAccount *account, QWidget* parent, const char* name) :
+dlgChatJoin(parent, name),
+m_account(account)
+{
+ setCaption(i18n("Join Jabber Groupchat"));
+ leNick->setText(m_account->client()->client()->user());
+ checkDefaultChatroomServer();
+}
+
+dlgJabberChatJoin::~dlgJabberChatJoin()
+{
+}
+
+/*$SPECIALIZATION$*/
+void dlgJabberChatJoin::slotJoin()
+{
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ m_account->client()->joinGroupChat(leServer->text(), leRoom->text(), leNick->text());
+ accept();
+}
+
+void dlgJabberChatJoin::slotBowse()
+{
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ dlgJabberChatRoomsList *crl = new dlgJabberChatRoomsList(m_account, leServer->text() , leNick->text());
+ crl->show();
+ accept();
+}
+
+/*
+ TODO : Used to look for the default chat server,
+ this is duplicate with dlgjabberservices.cpp
+ should be merged elsewhere !
+*/
+// JabberAccount *m_account;
+// XMPP::JT_GetServices * serviceTask;
+
+void dlgJabberChatJoin::checkDefaultChatroomServer()
+{
+ XMPP::JT_GetServices *serviceTask = new XMPP::JT_GetServices(m_account->client()->rootTask());
+ connect(serviceTask, SIGNAL (finished()), this, SLOT (slotQueryFinished()));
+
+ serviceTask->get(m_account->server());
+ serviceTask->go(true);
+}
+
+void dlgJabberChatJoin::slotQueryFinished()
+{
+ XMPP::JT_GetServices *task = (XMPP::JT_GetServices*)sender();
+ if (!task->success ())
+ return;
+
+ if(!leServer->text().isEmpty())
+ { //the user already started to type the server manyally. abort auto-detect
+ return;
+ }
+
+ for (XMPP::AgentList::const_iterator it = task->agents().begin(); it != task->agents().end(); ++it)
+ {
+ XMPP::JT_DiscoInfo *discoTask = new XMPP::JT_DiscoInfo(m_account->client()->rootTask());
+ connect(discoTask, SIGNAL (finished()), this, SLOT (slotDiscoFinished()));
+
+ discoTask->get((*it).jid().full());
+ discoTask->go(true);
+ }
+}
+
+void dlgJabberChatJoin::slotDiscoFinished()
+{
+ XMPP::JT_DiscoInfo *task = (XMPP::JT_DiscoInfo*)sender();
+
+ if (!task->success())
+ return;
+
+ if(!leServer->text().isEmpty())
+ { //the user already started to type the server manyally. abort auto-detect
+ return;
+ }
+
+
+ if (task->item().features().canGroupchat() && !task->item().features().isGateway())
+ {
+ leServer->setText(task->item().jid().full());
+ }
+}
+
+// end todo
+
+#include "dlgjabberchatjoin.moc"
+
diff --git a/kopete/protocols/jabber/ui/dlgjabberchatjoin.h b/kopete/protocols/jabber/ui/dlgjabberchatjoin.h
new file mode 100644
index 00000000..fad58fcb
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchatjoin.h
@@ -0,0 +1,65 @@
+
+/***************************************************************************
+ dlgjabberchatjoin.h - description
+ -------------------
+ begin : Fri Dec 13 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERCHATJOIN_H
+#define DLGJABBERCHATJOIN_H
+
+#include "dlgchatjoin.h"
+#include "jabberaccount.h"
+
+class dlgJabberChatJoin : public dlgChatJoin
+{
+ Q_OBJECT
+
+public:
+ dlgJabberChatJoin(JabberAccount *account, QWidget* parent = 0, const char* name = 0);
+ ~dlgJabberChatJoin();
+ /*$PUBLIC_FUNCTIONS$*/
+
+public slots:
+ /*$PUBLIC_SLOTS$*/
+ virtual void slotJoin();
+ virtual void slotBowse();
+
+protected:
+ /*$PROTECTED_FUNCTIONS$*/
+
+protected slots:
+ /*$PROTECTED_SLOTS$*/
+
+private:
+
+
+ JabberAccount *m_account;
+
+ /*
+ TODO : Used to look for the default chat server,
+ this is duplicate with dlgjabberservices.h
+ should be merged elsewhere !
+ */
+ void checkDefaultChatroomServer();
+private slots:
+ void slotQueryFinished();
+ void slotDiscoFinished();
+
+ // end todo.
+
+};
+
+#endif
+
diff --git a/kopete/protocols/jabber/ui/dlgjabberchatroomslist.cpp b/kopete/protocols/jabber/ui/dlgjabberchatroomslist.cpp
new file mode 100644
index 00000000..aea2843f
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchatroomslist.cpp
@@ -0,0 +1,117 @@
+//
+// C++ Implementation:
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qtable.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+
+#include "dlgjabberchatroomslist.h"
+#include "jabberprotocol.h"
+
+dlgJabberChatRoomsList::dlgJabberChatRoomsList(JabberAccount* account, const QString& server, const QString &nick, QWidget *parent, const char *name) :
+dlgChatRoomsList(parent, name),
+ m_account(account) , m_selectedRow(-1) , m_nick(nick)
+{
+ if (!server.isNull())
+ leServer->setText(server);
+ else if(m_account->isConnected())
+ leServer->setText(m_account->server());
+
+ m_chatServer = leServer->text();
+
+ // locales
+ setCaption(i18n("List Chatrooms"));
+
+ tblChatRoomsList->setLeftMargin (0);
+ tblChatRoomsList->setColumnStretchable(0, true);
+ tblChatRoomsList->setColumnStretchable(1, true);
+
+ if (!server.isNull())
+ slotQuery();
+}
+
+dlgJabberChatRoomsList::~dlgJabberChatRoomsList()
+{
+}
+
+/*$SPECIALIZATION$*/
+void dlgJabberChatRoomsList::slotJoin()
+{
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ if (m_selectedRow >= 0)
+ {
+ kdDebug (JABBER_DEBUG_GLOBAL) << "join chat room : " << m_account->client()->client()->user() << " @ " << tblChatRoomsList->text(m_selectedRow, 0) << " on " << m_chatServer << endl;
+ m_account->client()->joinGroupChat(m_chatServer, tblChatRoomsList->text(m_selectedRow, 0), m_nick);
+ }
+}
+
+void dlgJabberChatRoomsList::slotQuery()
+{
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ tblChatRoomsList->setNumRows(0);
+
+ XMPP::JT_DiscoItems *discoTask = new XMPP::JT_DiscoItems(m_account->client()->rootTask());
+ connect (discoTask, SIGNAL(finished()), this, SLOT(slotQueryFinished()));
+
+ m_chatServer = leServer->text();
+ discoTask->get(leServer->text());
+ discoTask->go(true);
+}
+
+void dlgJabberChatRoomsList::slotQueryFinished()
+{
+ XMPP::JT_DiscoItems *task = (XMPP::JT_DiscoItems*)sender();
+ if (!task->success())
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Error, i18n("Unable to retrieve the list of chat rooms."), i18n("Jabber Error"));
+ return;
+ }
+
+ const XMPP::DiscoList& items = task->items();
+ tblChatRoomsList->setNumRows(items.count());
+
+ int row = 0;
+ for (XMPP::DiscoList::const_iterator it = items.begin(); it != items.end(); ++it)
+ {
+ tblChatRoomsList->setText(row, 0, (*it).jid().user());
+ tblChatRoomsList->setText(row, 1, (*it).name());
+ ++row;
+ }
+}
+
+void dlgJabberChatRoomsList::slotDoubleClick(int row, int /*col*/, int /*button*/, const QPoint& /*mousePos*/)
+{
+ m_selectedRow = row;
+ slotJoin();
+}
+
+void dlgJabberChatRoomsList::slotClick(int row, int /*col*/, int /*button*/, const QPoint& /*mousePos*/)
+{
+ m_selectedRow = row;
+}
+
+#include "dlgjabberchatroomslist.moc"
+
diff --git a/kopete/protocols/jabber/ui/dlgjabberchatroomslist.h b/kopete/protocols/jabber/ui/dlgjabberchatroomslist.h
new file mode 100644
index 00000000..1db296d7
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchatroomslist.h
@@ -0,0 +1,54 @@
+//
+// C++ Interface:
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2005
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef DLGJABBERCHATROOMSLIST_H
+#define DLGJABBERCHATROOMSLIST_H
+
+#include "jabberaccount.h"
+#include "xmpp_tasks.h"
+
+#include "dlgchatroomslist.h"
+
+class dlgJabberChatRoomsList : public dlgChatRoomsList
+{
+ Q_OBJECT
+
+public:
+ dlgJabberChatRoomsList(JabberAccount* account, const QString& server = QString::null, const QString& nick = QString::null, QWidget* parent = 0, const char* name = 0);
+ ~dlgJabberChatRoomsList();
+ /*$PUBLIC_FUNCTIONS$*/
+
+public slots:
+ /*$PUBLIC_SLOTS$*/
+ virtual void slotJoin();
+ virtual void slotQuery();
+ virtual void slotDoubleClick(int row, int col, int button, const QPoint& mousePos);
+ virtual void slotClick(int row, int col, int button, const QPoint& mousePos);
+
+protected:
+ /*$PROTECTED_FUNCTIONS$*/
+
+protected slots:
+ /*$PROTECTED_SLOTS$*/
+
+ void slotQueryFinished();
+
+private:
+
+ JabberAccount *m_account;
+ int m_selectedRow;
+ QString m_chatServer;
+ QString m_nick;
+};
+
+#endif
+
diff --git a/kopete/protocols/jabber/ui/dlgjabberchooseserver.ui b/kopete/protocols/jabber/ui/dlgjabberchooseserver.ui
new file mode 100644
index 00000000..0a04b388
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberchooseserver.ui
@@ -0,0 +1,107 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgJabberChooseServer</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgJabberChooseServer</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>334</width>
+ <height>343</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Choose Server - Jabber</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTable" row="0" column="0">
+ <column>
+ <property name="text">
+ <string>Server</string>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Description</string>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>listServers</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="resizePolicy">
+ <enum>Default</enum>
+ </property>
+ <property name="hScrollBarMode">
+ <enum>Auto</enum>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>2</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="sorting">
+ <bool>false</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>SingleRow</enum>
+ </property>
+ <property name="resizeMode" stdset="0">
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="2" column="0">
+ <property name="name">
+ <cstring>linkServerDetails</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;a href="http://www.jabber.org/network/"&gt;Details about free public Jabber servers&lt;/a&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblStatus</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="2846">789c75d54953dd381007f03b9fe255fa969aea585ea59a9a039084256c0f0810a6e6204b32fbfe58a7e6bb8fddff3609c90ce6f27b6acbea764bfef07e72b0b53e79ff61ee6ee6672761128efdede47dbcbfb878fef3af3ffe9e7b67eac9f05fb849feeeb7b977d3d9244c36ae2ed300ef7b50664cac337127b626cf9db885f3a22e075325762694324edfc4de74954c4e6b705ed595dc4fe2ceb842c6fdd16093e5be6e24be1e9ce7856f647e7f227685ad647db4230e65d178194fe26eb8c43cb8903f89df1e5d4a3c1d8a7d9559acff40dccf57c1e5e0d20c97b811fb2ad928f31f8b43e59b56c6dde0aa1d2ef1ba3895b194f5901d5cd7755307b93f8add70c9f87470636a5be2fe7d71d5b416f905b1b399433db706dbcc3695d4930ab1192ef1e660d7b8429fbf216e6dd2f9b3c1a18d59443df7c4a9772dcec5dd70897707f7a355c2f33f8a8bc66678fe17b5cb50bf4f62eb7223fd430fe2d0a40cf7afc2ce1658cff9e0646c9521fe515cfad2209f457555607ee99f543bafcf3f1337aecbe4fdd005eccb4ceacd976217cacccaf8bdd8b719fa830fd57926f5e62b38984ceac12fea5ac7a5bf53eb23c689e156f3e5353834fafc67b5c3faf862b491fcf949dd66526f967aa61082ce77a04e46eac7f23e536c8b1ce3ab70e80cf291fe4ba96f70a91fefc33137c8675edcb555867807c7d220bf95d139eedf53d746fa95b3d1fafc85c15dd666b9bc0fb670b4c88fbcba3f2164fcebab518f1bb1f10eef9f23dc6abf7003c760a45f7977748efbafc579db68fc231cfb05cbf34fd429c7fb3f1d5da0decba3b15fb85677c89fe4bceb8a7e39c8bf529b1cfd78acce612e5f8d7c315eb699e697d441dfcf0e9c8c9e4f97ea02f5a4237595cbf9c84be2fe80a9b17fa3ba413da8535bac97d2abf17ee53ce9ea100af8014ed6211f1d4faec27e9c8d463d19eb69fa7a231f525b7501275fa3bfcd683d9feed46d055fa9638e7c3e8fd6f3fbf6d518bf51a702cf43beb61dfbe71c4efd8692f8157572a8dff6abd10fd7ea0ef3d1c2e806cf43bfdace75a817d6e3a2b7e8974db8df7118bf57772df617fadf875062ff213fdf7738ce8f2775897a30fab58d49e7cfe1ae6ad11ff3eadae23c5d52fb12df4fa3ee2fb91ffd1b626cb05fb6e0ae8ea8d799ba89e8d799daeaf7e159ed1accff32da62bfcbf7a08b7dc658ffa2da27d47baa6e615a56077d1fe887d4cf88f59caa6327ef87e57bd20d7fb2fee90c17137b6e3970e4c41d1f8dbf7f8fe1633ee1533ee373bee04bbee26b3e7e1bc3377ccb773ce37b7ee0477ee2677ee179be7913b3c08bfc913ff1675ee2655ee155fec26bbcfe26668337798ba7bccd3bbccb5f798ff7f980bfbd8939e48c0de75c70c915d7dcb06547fc630c11796a2950a4441d1dd1319dd0299dbd8939a70bbaecc7afe89a6ee896ee6846f774fe36777aa0c73ee2899ee985e6698116e9e1e7faf4511fe9137da6255aa6155aa52fbfd6b08f59a375daa04ddaa2296d8fbffe14b343bbf495f6689f0ee8dbffc41c524686722aa8a4ea7f626a6ac892f3ecc9fbff8e99ce7ceb838f3ef9eefb6fbfc4787fe48ffdc98fb3f431fffc3ef72fd6519eaa</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kactivelabel.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgjabbereditaccountwidget.ui b/kopete/protocols/jabber/ui/dlgjabbereditaccountwidget.ui
new file mode 100644
index 00000000..099e84a6
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabbereditaccountwidget.ui
@@ -0,0 +1,993 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>DlgJabberEditAccountWidget</class>
+<author>Daniel Stone</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgJabberEditAccountWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>706</width>
+ <height>455</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - Jabber</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget10</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox65</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>layout61</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Jabber ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The Jabber ID for the account you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The Jabber ID for the account you would like to use. Note that this must include the username and the domain (like an E-mail address), as there are many Jabber servers.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mID</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The Jabber ID for the account you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The Jabber ID for the account you would like to use. Note that this must include the username and the domain (for example, joe@jabber.org), as there are many Jabber servers.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget">
+ <property name="name">
+ <cstring>mPass</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbAutoConnect</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="QCheckBox">
+ <property name="name">
+ <cstring>cbGlobalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Exclu&amp;de from Global Identity</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblRegistration</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the Jabber network, you will need an account on a Jabber server. If you do not yet have an account, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRegister</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;gister New Account</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Change Password</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>btnChangePassword</cstring>
+ </property>
+ <property name="text">
+ <string>Change &amp;Your Password</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>tlChangePwSuccess</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>If you have an existing Jabber account and would like to change its password, you can use this button to enter a new password.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Co&amp;nnection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox66</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbUseSSL</cstring>
+ </property>
+ <property name="text">
+ <string>Use protocol encr&amp;yption (SSL)</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this box to enable SSL encrypted communication with the server.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbAllowPlainTextPassword</cstring>
+ </property>
+ <property name="text">
+ <string>Allow plain-te&amp;xt password authentication</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbCustomServer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Override default server information</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </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>false</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>mServer</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 jabber.org).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</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 jabber.org).</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</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>mPort</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>mPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</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>5222</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>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox8</cstring>
+ </property>
+ <property name="title">
+ <string>Location Settings</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_3</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;esource:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mResource</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The resource name you would like to use on the Jabber network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The resource name you would like to use on the Jabber network. Jabber allows you to sign on with the same account from multiple locations with different resource names, so you may wish to enter 'Home' or 'Work' here, for example.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mResource</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Kopete</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The resource name you would like to use on the Jabber network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The resource name you would like to use on the Jabber network. Jabber allows you to sign on with the same account from multiple locations with different resource names, so you may wish to enter 'Home' or 'Work' here, for example.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer28</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_3_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>P&amp;riority:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mResource</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The resource name you would like to use on the Jabber network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The resource name you would like to use on the Jabber network. Jabber allows you to sign on with the same account from multiple locations with different resource names, so you may wish to enter 'Home' or 'Work' here, for example.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mPriority</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="value">
+ <number>5</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Each resource can have different &lt;b&gt;priority &lt;/b&gt; levels. The messages will be sent to the resource which has the highest priority level.
+
+If two resources have the same priority, the messages will be sent to the one connected the latest.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer43</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>200</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Fi&amp;le Transfer</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>File Transfer Settings</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout70</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leProxyJID</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Pro&amp;xy JID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leProxyJID</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout68</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leLocalIP</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Por&amp;t:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sbLocalPort</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>sbLocalPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>8010</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Public &amp;IP address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leLocalIP</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;&lt;ul&gt;&lt;li&gt;The information in the "public IP address" and "port" fields apply to all Jabber accounts.&lt;/li&gt;
+&lt;li&gt;You can leave the "public IP address" empty if you do not use NAT.&lt;/li&gt;
+&lt;li&gt;A hostname is also valid.&lt;/li&gt;
+&lt;li&gt;Changes to these fields will only take effect the next time you start Kopete.&lt;/li&gt;
+&lt;li&gt;The "Proxy JID" can be configured per account.&lt;/li&gt;&lt;/ul&gt;&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer29</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>241</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Pri&amp;vacy</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox7</cstring>
+ </property>
+ <property name="title">
+ <string>General Privacy</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="0" column="1">
+ <property name="name">
+ <cstring>spacer9</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>cbHideSystemInfo</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Hide system and client info</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>By default, Kopete gives the other users some info about your system and the client. You can check this box in order to hide those infos.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox6</cstring>
+ </property>
+ <property name="title">
+ <string>Notifications</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbSendEvents</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Always send not&amp;ifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box if you want to always send notifications to your contacts.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>30</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbSendDeliveredEvent</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Alwa&amp;ys send delivered notifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Check this box to send the &lt;b&gt;Delivered notification&lt;/b&gt; to your contacts : when a message is delivered to Kopete, Kopete can notify your contact that it has received the message.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbSendDisplayedEvent</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Al&amp;ways send displayed notifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Check this box to send the &lt;b&gt;Displayed notification&lt;/b&gt; to your contacts : when a message is displayed in Kopete, Kopete can notify your contact that it has displayed the message.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbSendComposingEvent</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Always send &amp;typing notifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Check this box to send the &lt;b&gt;Typing notification&lt;/b&gt; to your contacts : when you are composing a message, you might want your contact to know that you are typing so that he knows you are answering.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbSendGoneEvent</cstring>
+ </property>
+ <property name="text">
+ <string>Always send &amp;gone notifications (closing the window)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>110</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </grid>
+</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="868">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000032b49444154388db59531681b6714c77f32373c8186ef0305eea0050932f8201d944d493bc4d0a1a21e4427bb533a74299dbc25905288a7d0b9836932d58116eac1411932388ba421a5a7a17005174e83e00e2cb80f6ab83708d2e18bec8ada26d0f4c1c1ddbbf7fdeeff3efeefbbda70346419b76fdd7ecd3b88e16858ab2dc183c3c1ebee7a97a99b521515d969f65610e71cd971c6f8d7312ccef3c152e9b39f9e11351d36164acdb819d4a9b4c4362ce5a2c48a45162588253ff5cfe5a2c406862405d9138e5eea2a18609a4fb12d212d7ea42c334089ac92e6423113cab902826d4227568a002480a942780dead16a2767e0ca55949a81668023b2c2e8952139748c270e58aa115aebc2675b86b80b6143710aa1b9049ccd336e064a5979e8e039ec7f5f78544368af1b24807ca64cff50befba6a0b765d8be2b67f00bc1562c95e6441646afe40d54b9f36948af2fb4df078722440c0e2af6f70a064f0be2568beea6c5885b01af2d6f4a2db10dc8ff128e0edc19f4f32f8576dbe1707022fcf2b4647babce175f8780f0c31307a7e0162bdc55c5e52247e742fabbc31843af2f9886c32d40d4b0fb4849278ef20476ee59c62f7ced3831848d55f0aa62816ca6de11ad37ed2fa10f1ce9c4619ac2c647824a45dc1100f2a9e2542e067b9f82155f108adf539c61f781924efc0745c0be57273240b08409e62ac508d0f085c94c112c83e778a54608434331733cbc9f331a5bf2636f85a855bfda15f9694e27565ad785e99fcae0a062fb6e4479a2f43e16eacd3a0fef433175ec7e95a1aa98a6d0e95454f355f2bff65803e8f5bddbf7f70a0687393bf72ced2e74ba253bdfb631a1c139872e948d7e487c83ab15979a2301dcba033a373c7e52f0f851c1f885d0ed080ec88f7374ae672b7f3b72249b115143389fce7f4e5e91d11398cefd986e6c099816839fbd1bd2c9b91ad3147afd16a32387534580ac58957c0e3ece485230d77c5ba6a1f4fa42ef9398719253153e1f5f8f687f9013df80f16684c1e0161969b20aae0d47437fc007d0f950882210c19fad81bf24f04e399701a04820380769a2e485e28a0b14b380e4a5927059e85be67cac5dfae63fc61af87fd4ff027ed7f0e16858fb1ba5cd86c64770b2e90000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>cbSendEvents</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cbSendDisplayedEvent</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbSendEvents</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cbSendComposingEvent</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbSendEvents</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cbSendDeliveredEvent</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget10</tabstop>
+ <tabstop>mID</tabstop>
+ <tabstop>mPass</tabstop>
+ <tabstop>cbAutoConnect</tabstop>
+ <tabstop>btnRegister</tabstop>
+ <tabstop>btnChangePassword</tabstop>
+ <tabstop>cbUseSSL</tabstop>
+ <tabstop>cbAllowPlainTextPassword</tabstop>
+ <tabstop>cbCustomServer</tabstop>
+ <tabstop>mServer</tabstop>
+ <tabstop>mPort</tabstop>
+ <tabstop>mResource</tabstop>
+ <tabstop>mPriority</tabstop>
+ <tabstop>leLocalIP</tabstop>
+ <tabstop>sbLocalPort</tabstop>
+ <tabstop>leProxyJID</tabstop>
+ <tabstop>cbHideSystemInfo</tabstop>
+ <tabstop>cbSendEvents</tabstop>
+ <tabstop>cbSendDeliveredEvent</tabstop>
+ <tabstop>cbSendDisplayedEvent</tabstop>
+ <tabstop>cbSendComposingEvent</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kopetepasswordwidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgjabberregister.cpp b/kopete/protocols/jabber/ui/dlgjabberregister.cpp
new file mode 100644
index 00000000..e16b5652
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberregister.cpp
@@ -0,0 +1,117 @@
+
+/***************************************************************************
+ dlgjabberregister.cpp - description
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qpushbutton.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include "jabberaccount.h"
+#include "jabberprotocol.h"
+#include "jabberclient.h"
+#include "dlgjabberregister.h"
+
+dlgJabberRegister::dlgJabberRegister (JabberAccount *account, const XMPP::Jid & jid, QWidget * parent, const char *name):dlgRegister (parent, name)
+{
+ m_account = account;
+
+ XMPP::JT_Register * task = new XMPP::JT_Register(m_account->client()->rootTask ());
+
+ connect (task, SIGNAL (finished ()), this, SLOT (slotGotForm ()));
+
+ task->getForm (jid);
+ task->go (true);
+
+ translator = 0;
+
+}
+
+void dlgJabberRegister::slotGotForm ()
+{
+ XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();
+
+ // remove the "wait" message
+ delete lblWait;
+
+ if (!task->success ())
+ {
+ KMessageBox::error (this, i18n ("Unable to retrieve registration form.\nReason: \"%1\"").arg (task->statusString (), 1), i18n ("Jabber Error"));
+
+ deleteLater ();
+
+ return;
+ }
+
+ // translate the form and create it inside the box widget
+ translator = new JabberFormTranslator (task->form (), grpForm);
+ static_cast<QBoxLayout*>(grpForm->layout())->insertWidget(1, translator);
+ translator->show();
+ resize(sizeHint());
+
+ // enable the send button
+ btnRegister->setEnabled (true);
+
+ connect (btnRegister, SIGNAL (clicked ()), this, SLOT (slotSendForm ()));
+
+}
+
+void dlgJabberRegister::slotSendForm ()
+{
+ if(!translator)
+ return;
+ XMPP::JT_Register * task = new XMPP::JT_Register (m_account->client()->rootTask ());
+
+ connect (task, SIGNAL (finished ()), this, SLOT (slotSentForm ()));
+
+ task->setForm (translator->resultData ());
+ task->go (true);
+
+ btnRegister->setEnabled (false);
+ btnCancel->setEnabled (false);
+
+}
+
+void dlgJabberRegister::slotSentForm ()
+{
+ XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();
+
+ if (task->success ())
+ {
+ KMessageBox::information (this, i18n ("Registration sent successfully."), i18n ("Jabber Registration"));
+
+ deleteLater ();
+ }
+ else
+ {
+ KMessageBox::error (this,
+ i18n ("The server denied the registration form.\nReason: \"%1\"").arg (task->statusString (), 1), i18n ("Jabber Registration"));
+
+ btnRegister->setEnabled (true);
+ btnRegister->setEnabled (true);
+ }
+
+}
+
+dlgJabberRegister::~dlgJabberRegister ()
+{
+
+ delete translator;
+
+}
+
+#include "dlgjabberregister.moc"
diff --git a/kopete/protocols/jabber/ui/dlgjabberregister.h b/kopete/protocols/jabber/ui/dlgjabberregister.h
new file mode 100644
index 00000000..36bc0bab
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberregister.h
@@ -0,0 +1,58 @@
+
+/***************************************************************************
+ dlgjabberregister.h - description
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERREGISTER_H
+#define DLGJABBERREGISTER_H
+
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+
+#include "im.h"
+#include "xmpp.h"
+
+#include "jabberaccount.h"
+#include "dlgregister.h"
+#include "jabberformtranslator.h"
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class dlgJabberRegister:public dlgRegister
+{
+
+ Q_OBJECT
+
+public:
+ dlgJabberRegister (JabberAccount *account, const XMPP::Jid & jid, QWidget * parent = 0, const char *name = 0);
+ ~dlgJabberRegister ();
+
+private slots:
+ void slotGotForm ();
+ void slotSendForm ();
+ void slotSentForm ();
+
+private:
+ JabberAccount *m_account;
+ JabberFormTranslator * translator;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/dlgjabberregisteraccount.ui b/kopete/protocols/jabber/ui/dlgjabberregisteraccount.ui
new file mode 100644
index 00000000..c411bb96
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberregisteraccount.ui
@@ -0,0 +1,319 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>DlgJabberRegisterAccount</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>DlgJabberRegisterAccount</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>346</width>
+ <height>376</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>350</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Register Account - Jabber</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>lblJID</cstring>
+ </property>
+ <property name="text">
+ <string>Desired Jabber &amp;ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leJID</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>pixPasswordVerify</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="2">
+ <property name="name">
+ <cstring>layoutServerEntry</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leServer</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>btnChooseServer</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;hoose...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>lblPassword</cstring>
+ </property>
+ <property name="text">
+ <string>Pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lePassword</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="4" column="2">
+ <property name="name">
+ <cstring>sbPort</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cbUseSSL</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Use protocol encr&amp;yption (SSL)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this box to enable SSL encrypted communication with the server.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to enable SSL encrypted communication with the server. Note that this is not end-to-end encryption, but rather encrypted communication with the server.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>pixJID</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>lblPort</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sbPort</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>lblPasswordVerify</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Repeat password:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lePasswordVerify</cstring>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="2" column="2">
+ <property name="name">
+ <cstring>lePassword</cstring>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>pixServer</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="text">
+ <string>Jabber &amp;server:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>leServer</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>pixPassword</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>16</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KPasswordEdit" row="3" column="2">
+ <property name="name">
+ <cstring>lePasswordVerify</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="2">
+ <property name="name">
+ <cstring>leJID</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="6" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblJIDInformation</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacerMiddle</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblStatusMessage</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>leServer</tabstop>
+ <tabstop>btnChooseServer</tabstop>
+ <tabstop>leJID</tabstop>
+ <tabstop>lePassword</tabstop>
+ <tabstop>lePasswordVerify</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+ <includehint>kpassdlg.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgjabbersendraw.cpp b/kopete/protocols/jabber/ui/dlgjabbersendraw.cpp
new file mode 100644
index 00000000..17b2d181
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabbersendraw.cpp
@@ -0,0 +1,116 @@
+
+/***************************************************************************
+ dlgjabbersendraw.cpp - Raw XML dialog
+ -------------------
+ begin : Sun Aug 25 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "dlgjabbersendraw.h"
+
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qtextedit.h>
+#include <kdebug.h>
+#include "jabberclient.h"
+
+dlgJabberSendRaw::dlgJabberSendRaw ( JabberClient *client, QWidget *parent, const char *name )
+ : DlgSendRaw (parent, name)
+{
+ // Connect the GUI elements to things that do stuff
+ connect (btnSend, SIGNAL (clicked ()), this, SLOT (slotSend ()));
+ connect (btnClose, SIGNAL (clicked ()), this, SLOT (slotCancel ()));
+ connect (btnClear, SIGNAL (clicked ()), this, SLOT (slotClear ()));
+ connect (inputWidget, SIGNAL (activated (int)), this, SLOT (slotCreateMessage (int)));
+
+ m_client = client;
+
+ show();
+}
+
+dlgJabberSendRaw::~dlgJabberSendRaw ()
+{
+ // Nothing yet
+}
+
+void dlgJabberSendRaw::slotCancel ()
+{
+ close(true);
+}
+
+void dlgJabberSendRaw::slotClear ()
+{
+ inputWidget->setCurrentItem(0);
+ tePacket->clear();
+}
+
+void dlgJabberSendRaw::slotCreateMessage(int index)
+{
+ switch (index) {
+ case 1:
+ tePacket->setText(QString("<iq type='set' to='%1'>\n<query xmlns='jabber:iq:register'><remove/>\n</query>\n</iq>")
+ .arg ( m_client->jid().domain () ) );
+ break;
+ case 2:
+ tePacket->setText("<presence>\n<show>\?\?\?</show>\n<status>\?\?\?</status>\n</presence>");
+ break;
+ case 3:
+ tePacket->setText("<iq type='get' to='USER@DOMAIN'>\n<query xmlns='jabber:iq:last'/></iq>");
+ break;
+ case 4:
+ tePacket->setText(QString("<message to='USER@DOMAIN' from='%1@%2/%3'>\n<body>Body text</body>\n</message>")
+ .arg ( m_client->jid().node (), m_client->jid().domain (), m_client->jid().resource () ) );
+ break;
+ case 5:
+ tePacket->setText(QString("<message to='USER@DOMAIN' from='%1@%2/%3'>\n<subject>Subject</subject><body>Body text</body>\n</message>")
+ .arg ( m_client->jid().node (), m_client->jid().domain (), m_client->jid().resource () ) );
+
+ break;
+ case 6:
+ tePacket->setText("<iq type='set'>\n<query xmlns='jabber:iq:roster'>\n<item name='NAME' jid='USER@DOMAIN'>\n<group>GROUP</group>\n</item>\n</query>\n</iq>");
+ break;
+ case 7:
+ tePacket->setText("<iq type='set'>\n<query xmlns='jabber:iq:roster'>\n<item jid='USER@DOMAIN' subscription='remove'/>\n</query>\n</iq>");
+ break;
+ case 8:
+ tePacket->setText("<presence to='USER@DOMAIN' type='\?\?\?'/>");
+ break;
+ default:
+ tePacket->clear();
+ break;
+ }
+}
+
+void dlgJabberSendRaw::slotSend()
+{
+ kdDebug (14130) << "[dlgJabberSendRaw] Sending RAW message" << endl;
+
+ // Tell our engine to send
+ m_client->send (tePacket->text ());
+
+ // set temlapte combobox to "User Defined" and clear content
+ inputWidget->setCurrentItem(0);
+ tePacket->clear();
+}
+
+#include "dlgjabbersendraw.moc"
+
+/*
+ * Local variables:
+ * mode: c++
+ * c-indentation-style: k&r
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/dlgjabbersendraw.h b/kopete/protocols/jabber/ui/dlgjabbersendraw.h
new file mode 100644
index 00000000..6570363b
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabbersendraw.h
@@ -0,0 +1,85 @@
+
+/***************************************************************************
+ dlgjabbersendraw.h - Raw XML dialog
+ -------------------
+ begin : Sun Aug 25 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERSENDRAW_H
+#define DLGJABBERSENDRAW_H
+
+#include <qwidget.h>
+#include "dlgsendraw.h"
+
+class JabberClient;
+
+/**
+ * A dialog to send raw strings to the jabber server.
+ *
+ * It comes with a QComboBox to choose some "template" strings
+ * like "Availability Status", "Subscription",...
+ *
+ * @author Till Gerken <till@tantalo.net>
+ * @author Chris TenHarmsel <tenharmsel@users.sf.net>
+ */
+class dlgJabberSendRaw:public DlgSendRaw
+{
+Q_OBJECT
+
+public:
+ dlgJabberSendRaw ( JabberClient *client, QWidget * parent = 0, const char *name = 0);
+ virtual ~ dlgJabberSendRaw ();
+
+public slots:
+
+ /**
+ * Closes the SendRaw Dialog.
+ */
+ void slotCancel ();
+
+ /**
+ * Clears current xml message in tePacket.
+ */
+ void slotClear ();
+
+ /**
+ * Sets a xml message in tePacket(QTextWidget)
+ * according to the state of inputWidget.
+ */
+ void slotCreateMessage (int);
+
+ /**
+ * Sends a xml message to the server,
+ * clears tePacket afterwards.
+ */
+ void slotSend();
+
+private:
+ /**
+ * This is what we talk through
+ */
+ JabberClient *m_client;
+};
+
+
+#endif
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/dlgjabberservices.cpp b/kopete/protocols/jabber/ui/dlgjabberservices.cpp
new file mode 100644
index 00000000..00e99f45
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberservices.cpp
@@ -0,0 +1,237 @@
+
+/***************************************************************************
+ dlgjabberservices.cpp - Service browsing
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qtable.h>
+
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "dlgjabberregister.h"
+#include "dlgjabberbrowse.h"
+#include "dlgjabberservices.h"
+
+#include "dlgjabberservices.moc"
+
+dlgJabberServices::dlgJabberServices (JabberAccount *account, QWidget *parent, const char *name):dlgServices (parent, name)
+{
+ m_account = account;
+
+ if(m_account->isConnected())
+ {
+ // pre-populate the server field
+ leServer->setText(m_account->server());
+ }
+
+ // disable the left margin
+ //tblServices->setLeftMargin (0);
+
+ // no content for now
+ //tblServices->setNumRows (0);
+
+ // disable the buttons as long as nothing has been selected
+ btnRegister->setDisabled (true);
+ btnBrowse->setDisabled (true);
+
+ // allow autostretching
+ //tblServices->setColumnStretchable (0, true);
+ //tblServices->setColumnStretchable (1, true);
+
+ // disable user selections
+ //tblServices->setSelectionMode (QTable::NoSelection);
+
+ // name table headers
+ //tblServices->horizontalHeader ()->setLabel (0, i18n ("Name"));
+ //tblServices->horizontalHeader ()->setLabel (1, i18n ("Address"));
+
+ connect (btnQuery, SIGNAL (clicked ()), this, SLOT (slotDisco ()));
+ //connect (tblServices, SIGNAL (clicked (int, int, int, const QPoint &)), this, SLOT (slotSetSelection (int, int, int, const QPoint &)));
+ connect (lvServices, SIGNAL (selectionChanged (QListViewItem *)), this, SLOT (slotSetSelection (QListViewItem *)));
+
+ connect (btnRegister, SIGNAL (clicked ()), this, SLOT (slotRegister ()));
+ connect (btnBrowse, SIGNAL (clicked ()), this, SLOT (slotBrowse ()));
+
+}
+
+void dlgJabberServices::slotSetSelection (QListViewItem *it)
+{
+ dlgJabberServies_item *item=dynamic_cast<dlgJabberServies_item*>(it);
+ if(!item)
+ {
+ btnRegister->setDisabled (true);
+ btnBrowse->setDisabled (true);
+ }
+ else
+ {
+ btnRegister->setDisabled (! item->can_register);
+ btnBrowse->setDisabled (! item->can_browse);
+ current_jid=item->jid;
+ }
+
+}
+
+void dlgJabberServices::slotService ()
+{
+
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ XMPP::JT_GetServices *serviceTask = new XMPP::JT_GetServices (m_account->client()->rootTask ());
+ connect (serviceTask, SIGNAL (finished ()), this, SLOT (slotServiceFinished ()));
+
+ /* populate server field if it is empty */
+ if(leServer->text().isEmpty())
+ leServer->setText(m_account->server());
+
+ kdDebug (14130) << "[dlgJabberServices] Trying to fetch a list of services at " << leServer->text () << endl;
+
+ serviceTask->get (leServer->text ());
+ serviceTask->go (true);
+}
+
+
+
+void dlgJabberServices::slotServiceFinished ()
+{
+ kdDebug (14130) << "[dlgJabberServices] Query task finished" << endl;
+
+ XMPP::JT_GetServices * task = (XMPP::JT_GetServices *) sender ();
+
+ if (!task->success ())
+ {
+ QString error = task->statusString();
+ KMessageBox::queuedMessageBox (this, KMessageBox::Error, i18n ("Unable to retrieve the list of services.\nReason: %1").arg(error), i18n ("Jabber Error"));
+ return;
+ }
+
+ lvServices->clear();
+
+ for (XMPP::AgentList::const_iterator it = task->agents ().begin (); it != task->agents ().end (); ++it)
+ {
+ dlgJabberServies_item *item=new dlgJabberServies_item( lvServices , (*it).jid ().userHost () , (*it).name ());
+ item->jid=(*it).jid();
+ item->can_browse=(*it).features().canSearch();
+ item->can_register=(*it).features().canRegister();
+ }
+}
+
+void dlgJabberServices::slotDisco()
+{
+ lvServices->clear();
+
+ if(!m_account->isConnected())
+ {
+ m_account->errorConnectFirst();
+ return;
+ }
+
+ JT_DiscoItems *jt = new JT_DiscoItems(m_account->client()->rootTask());
+ connect(jt, SIGNAL(finished()), this, SLOT(slotDiscoFinished()));
+
+ /* populate server field if it is empty */
+ if(leServer->text().isEmpty())
+ leServer->setText(m_account->server());
+
+ jt->get(leServer->text() , QString());
+ jt->go(true);
+}
+
+
+
+
+
+void dlgJabberServices::slotDiscoFinished( )
+{
+ XMPP::JT_DiscoItems *jt = (JT_DiscoItems *)sender();
+
+ if ( jt->success() )
+ {
+ QValueList<XMPP::DiscoItem> list = jt->items();
+
+ lvServices->clear();
+
+ for(QValueList<XMPP::DiscoItem>::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ const XMPP::DiscoItem a = *it;
+ dlgJabberServies_item *item=new dlgJabberServies_item( lvServices , (*it).jid ().userHost () , (*it).name ());
+ item->jid=a.jid();
+ item->updateInfo(a.jid() , a.node(), m_account);
+ }
+ }
+ else
+ {
+ slotService();
+ }
+}
+
+
+void dlgJabberServices::slotRegister ()
+{
+
+ dlgJabberRegister *registerDialog = new dlgJabberRegister (m_account, current_jid);
+
+ registerDialog->show ();
+ registerDialog->raise ();
+
+}
+
+void dlgJabberServices::slotBrowse ()
+{
+
+ dlgJabberBrowse *browseDialog = new dlgJabberBrowse (m_account, current_jid);
+
+ browseDialog->show ();
+ browseDialog->raise ();
+
+}
+
+dlgJabberServices::~dlgJabberServices ()
+{
+}
+
+void dlgJabberServies_item::updateInfo( const XMPP::Jid & jid , const QString & node , JabberAccount *account )
+{
+ XMPP::JT_DiscoInfo *jt = new XMPP::JT_DiscoInfo(account->client()->rootTask());
+ connect(jt, SIGNAL(finished()),this, SLOT(slotDiscoFinished()));
+ jt->get(jid, node);
+ jt->go(true);
+
+}
+
+void dlgJabberServies_item::slotDiscoFinished( )
+{
+ JT_DiscoInfo *jt = (JT_DiscoInfo *)sender();
+
+ if ( jt->success() )
+ {
+ can_browse = jt->item().features().canSearch();
+ can_register = jt->item().features().canRegister();
+ }
+ else
+ {
+ //TODO: error message (it's a simple message box to show)
+ }
+}
+
diff --git a/kopete/protocols/jabber/ui/dlgjabberservices.h b/kopete/protocols/jabber/ui/dlgjabberservices.h
new file mode 100644
index 00000000..f2d4cedc
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabberservices.h
@@ -0,0 +1,73 @@
+
+/***************************************************************************
+ dlgjabberservices.h - description
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERSERVICES_H
+#define DLGJABBERSERVICES_H
+
+#include <qwidget.h>
+
+#include "jabberaccount.h"
+#include "xmpp_tasks.h"
+
+#include "dlgservices.h"
+#include <qlistview.h>
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class dlgJabberServices:public dlgServices
+{
+ Q_OBJECT
+
+public:
+ dlgJabberServices (JabberAccount *account, QWidget *parent = 0, const char *name = 0);
+ ~dlgJabberServices ();
+
+private slots:
+ void slotSetSelection (QListViewItem *);
+ void slotService ();
+ void slotServiceFinished ();
+ void slotRegister ();
+ void slotBrowse ();
+
+ void slotDisco();
+ void slotDiscoFinished();
+
+private:
+ JabberAccount *m_account;
+ XMPP::Jid current_jid;
+
+};
+
+
+class dlgJabberServies_item : protected QObject, public QListViewItem
+{
+ Q_OBJECT
+ public:
+ dlgJabberServies_item( QListView *parent , const QString &s1 , const QString &s2 )
+ : QListViewItem(parent,s1,s2), can_browse(false) , can_register(false) {}
+ bool can_browse, can_register;
+ XMPP::Jid jid;
+
+ void updateInfo(const XMPP::Jid& jid, const QString &node , JabberAccount *account);
+ private slots:
+ void slotDiscoFinished();
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/dlgjabbervcard.cpp b/kopete/protocols/jabber/ui/dlgjabbervcard.cpp
new file mode 100644
index 00000000..b35e091a
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabbervcard.cpp
@@ -0,0 +1,563 @@
+
+/***************************************************************************
+ dlgjabbervcard.cpp - vCard dialog
+ -------------------
+ begin : Thu Aug 08 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ (C) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+ email : kopete-devel@kde.org
+
+ Rewritten version of the original dialog
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "dlgjabbervcard.h"
+
+// Qt includes
+#include <qtextedit.h>
+#include <qwidgetstack.h>
+#include <qregexp.h>
+#include <qbuffer.h>
+
+// KDE includes
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kurllabel.h>
+#include <kmessagebox.h>
+#include <krun.h>
+#include <kio/netaccess.h>
+#include <kfiledialog.h>
+#include <kpixmapregionselectordialog.h>
+#include <kstandarddirs.h>
+
+// libiris(XMPP backend) includes
+#include "im.h"
+#include "xmpp.h"
+#include "xmpp_tasks.h"
+
+// Kopete includes
+#include "jabberprotocol.h"
+#include "jabbercontact.h"
+#include "jabberaccount.h"
+#include "jabbercontactpool.h"
+#include "jabberbasecontact.h"
+#include "jabberclient.h"
+#include "dlgvcard.h"
+
+/*
+ * Constructs a dlgJabberVCard which is a child of 'parent', with the
+ * name 'name'
+ *
+ */
+dlgJabberVCard::dlgJabberVCard (JabberAccount *account, JabberBaseContact *contact, QWidget * parent, const char *name)
+ : KDialogBase (parent, name, false, i18n("Jabber vCard"), Close | User1 | User2, Close, false, i18n("&Save User Info"), i18n("&Fetch vCard") )
+{
+
+ m_account = account;
+ m_contact = contact;
+
+ m_mainWidget = new dlgVCard(this);
+ setMainWidget(m_mainWidget);
+
+ connect (this, SIGNAL (user1Clicked()), this, SLOT (slotSaveVCard ()));
+ connect (this, SIGNAL( user2Clicked()), this, SLOT (slotGetVCard ()));
+
+ connect (m_mainWidget->btnSelectPhoto, SIGNAL (clicked()), this, SLOT (slotSelectPhoto()));
+ connect (m_mainWidget->btnClearPhoto, SIGNAL (clicked()), this, SLOT (slotClearPhoto()));
+ connect (m_mainWidget->urlHomeEmail, SIGNAL (leftClickedURL(const QString &)), this, SLOT (slotOpenURL (const QString &)));
+ connect (m_mainWidget->urlWorkEmail, SIGNAL (leftClickedURL(const QString &)), this, SLOT (slotOpenURL (const QString &)));
+ connect (m_mainWidget->urlHomepage, SIGNAL (leftClickedURL(const QString &)), this, SLOT (slotOpenURL (const QString &)));
+
+ assignContactProperties();
+
+ show ();
+ raise ();
+
+ slotGetVCard();
+}
+
+/*
+ * Destroys the object and frees any allocated resources
+ */
+dlgJabberVCard::~dlgJabberVCard ()
+{
+ // no need to delete child widgets, Qt does it all for us
+}
+
+/*
+ * Activated when the close button gets pressed. Deletes the dialog.
+ */
+void dlgJabberVCard::slotClose()
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Deleting dialog." << endl;
+ delayedDestruct();
+}
+
+/*
+ * Assign the contact properties to this dialog
+ */
+void dlgJabberVCard::assignContactProperties ()
+{
+ // general tab
+ m_mainWidget->leNick->setText (m_contact->property(m_account->protocol()->propNickName).value().toString());
+ m_mainWidget->leName->setText (m_contact->property(m_account->protocol()->propFullName).value().toString());
+ // Guess the JID from the Kopete::Contact if the propJid is empty.
+ if( m_contact->property( m_account->protocol()->propJid ).value().toString().isEmpty() )
+ m_mainWidget->leJID->setText (m_contact->rosterItem().jid().full());
+ else
+ m_mainWidget->leJID->setText (m_contact->property(m_account->protocol()->propJid).value().toString());
+ m_mainWidget->leBirthday->setText (m_contact->property(m_account->protocol()->propBirthday).value().toString());
+ m_mainWidget->leTimezone->setText (m_contact->property(m_account->protocol()->propTimezone).value().toString());
+
+ QString homepage = m_contact->property(m_account->protocol()->propHomepage).value().toString();
+ m_mainWidget->leHomepage->setText (homepage);
+ m_mainWidget->urlHomepage->setText (homepage);
+ m_mainWidget->urlHomepage->setURL (homepage);
+ m_mainWidget->urlHomepage->setUseCursor ( !homepage.isEmpty () );
+
+ // Set photo
+ m_photoPath = m_contact->property(m_account->protocol()->propPhoto).value().toString();
+ if( !m_photoPath.isEmpty() )
+ {
+ m_mainWidget->lblPhoto->setPixmap( QPixmap(m_photoPath) );
+ }
+
+ // addresses
+ m_mainWidget->leWorkStreet->setText (m_contact->property(m_account->protocol()->propWorkStreet).value().toString());
+ m_mainWidget->leWorkExtAddr->setText (m_contact->property(m_account->protocol()->propWorkExtAddr).value().toString());
+ m_mainWidget->leWorkPOBox->setText (m_contact->property(m_account->protocol()->propWorkPOBox).value().toString());
+ m_mainWidget->leWorkCity->setText (m_contact->property(m_account->protocol()->propWorkCity).value().toString());
+ m_mainWidget->leWorkPostalCode->setText (m_contact->property(m_account->protocol()->propWorkPostalCode).value().toString());
+ m_mainWidget->leWorkCountry->setText (m_contact->property(m_account->protocol()->propWorkCountry).value().toString());
+
+ m_mainWidget->leHomeStreet->setText (m_contact->property(m_account->protocol()->propHomeStreet).value().toString());
+ m_mainWidget->leHomeExtAddr->setText (m_contact->property(m_account->protocol()->propHomeExtAddr).value().toString());
+ m_mainWidget->leHomePOBox->setText (m_contact->property(m_account->protocol()->propHomePOBox).value().toString());
+ m_mainWidget->leHomeCity->setText (m_contact->property(m_account->protocol()->propHomeCity).value().toString());
+ m_mainWidget->leHomePostalCode->setText (m_contact->property(m_account->protocol()->propHomePostalCode).value().toString());
+ m_mainWidget->leHomeCountry->setText (m_contact->property(m_account->protocol()->propHomeCountry).value().toString());
+
+ // email
+ m_mainWidget->urlWorkEmail->setUseCursor ( false );
+ m_mainWidget->urlHomeEmail->setUseCursor ( false );
+
+ QString workEmail = m_contact->property(m_account->protocol()->propWorkEmailAddress).value().toString();
+ QString homeEmail = m_contact->property(m_account->protocol()->propEmailAddress).value().toString();
+ m_mainWidget->leWorkEmail->setText (workEmail);
+ m_mainWidget->urlWorkEmail->setText (workEmail);
+ m_mainWidget->urlWorkEmail->setURL ("mailto:" + workEmail);
+ bool enableMail=!workEmail.stripWhiteSpace().isEmpty ();
+ m_mainWidget->urlWorkEmail->setUseCursor ( enableMail );
+ m_mainWidget->urlWorkEmail->setEnabled ( enableMail );
+
+ m_mainWidget->leHomeEmail->setText (homeEmail);
+ m_mainWidget->urlHomeEmail->setText (homeEmail);
+ enableMail=!homeEmail.stripWhiteSpace().isEmpty ();
+ m_mainWidget->urlHomeEmail->setURL ("mailto:" + homeEmail);
+ m_mainWidget->urlHomeEmail->setUseCursor ( enableMail );
+ m_mainWidget->urlHomeEmail->setEnabled ( enableMail );
+
+ // work information tab
+ m_mainWidget->leCompany->setText (m_contact->property(m_account->protocol()->propCompanyName).value().toString());
+ m_mainWidget->leDepartment->setText (m_contact->property(m_account->protocol()->propCompanyDepartement).value().toString());
+ m_mainWidget->lePosition->setText (m_contact->property(m_account->protocol()->propCompanyPosition).value().toString());
+ m_mainWidget->leRole->setText (m_contact->property(m_account->protocol()->propCompanyRole).value().toString());
+
+ // phone numbers tab
+ m_mainWidget->lePhoneFax->setText(m_contact->property(m_account->protocol()->propPhoneFax).value().toString());
+ m_mainWidget->lePhoneWork->setText(m_contact->property(m_account->protocol()->propWorkPhone).value().toString());
+ m_mainWidget->lePhoneCell->setText(m_contact->property(m_account->protocol()->propPrivateMobilePhone).value().toString());
+ m_mainWidget->lePhoneHome->setText(m_contact->property(m_account->protocol()->propPrivatePhone).value().toString());
+
+ // about tab
+ m_mainWidget->teAbout->setText (m_contact->property(m_account->protocol()->propAbout).value().toString());
+
+ if(m_account->myself() == m_contact)
+ setReadOnly (false);
+ else
+ setReadOnly (true);
+}
+
+void dlgJabberVCard::setReadOnly (bool state)
+{
+ // general tab
+ m_mainWidget->leNick->setReadOnly (state);
+ m_mainWidget->leName->setReadOnly (state);
+ m_mainWidget->leJID->setReadOnly (state);
+ m_mainWidget->leBirthday->setReadOnly (state);
+ m_mainWidget->leTimezone->setReadOnly (state);
+ m_mainWidget->wsHomepage->raiseWidget(state ? 0 : 1);
+ // Disable photo buttons when read only
+ m_mainWidget->btnSelectPhoto->setEnabled(!state);
+ m_mainWidget->btnClearPhoto->setEnabled(!state);
+
+ // home address tab
+ m_mainWidget->leHomeStreet->setReadOnly (state);
+ m_mainWidget->leHomeExtAddr->setReadOnly (state);
+ m_mainWidget->leHomePOBox->setReadOnly (state);
+ m_mainWidget->leHomeCity->setReadOnly (state);
+ m_mainWidget->leHomePostalCode->setReadOnly (state);
+ m_mainWidget->leHomeCountry->setReadOnly (state);
+ m_mainWidget->wsHomeEmail->raiseWidget(state ? 0 : 1);
+
+ // work address tab
+ m_mainWidget->leWorkStreet->setReadOnly (state);
+ m_mainWidget->leWorkExtAddr->setReadOnly (state);
+ m_mainWidget->leWorkPOBox->setReadOnly (state);
+ m_mainWidget->leWorkCity->setReadOnly (state);
+ m_mainWidget->leWorkPostalCode->setReadOnly (state);
+ m_mainWidget->leWorkCountry->setReadOnly (state);
+ m_mainWidget->wsWorkEmail->raiseWidget(state ? 0 : 1);
+
+ // work information tab
+ m_mainWidget->leCompany->setReadOnly (state);
+ m_mainWidget->leDepartment->setReadOnly (state);
+ m_mainWidget->lePosition->setReadOnly (state);
+ m_mainWidget->leRole->setReadOnly (state);
+
+ // phone numbers tab
+ m_mainWidget->lePhoneHome->setReadOnly (state);
+ m_mainWidget->lePhoneWork->setReadOnly (state);
+ m_mainWidget->lePhoneFax->setReadOnly (state);
+ m_mainWidget->lePhoneCell->setReadOnly (state);
+
+ // about tab
+ m_mainWidget->teAbout->setReadOnly (state);
+
+ // save button
+ enableButton(User1, !state);
+}
+
+void dlgJabberVCard::setEnabled(bool state)
+{
+ // general tab
+ m_mainWidget->leNick->setEnabled (state);
+ m_mainWidget->leName->setEnabled (state);
+ m_mainWidget->leJID->setEnabled (state);
+ m_mainWidget->leBirthday->setEnabled (state);
+ m_mainWidget->leTimezone->setEnabled (state);
+ m_mainWidget->wsHomepage->raiseWidget(state ? 1 : 0);
+ // Disable photo buttons when read only
+ m_mainWidget->btnSelectPhoto->setEnabled(state);
+ m_mainWidget->btnClearPhoto->setEnabled(state);
+
+ // home address tab
+ m_mainWidget->leHomeStreet->setEnabled (state);
+ m_mainWidget->leHomeExtAddr->setEnabled (state);
+ m_mainWidget->leHomePOBox->setEnabled (state);
+ m_mainWidget->leHomeCity->setEnabled (state);
+ m_mainWidget->leHomePostalCode->setEnabled (state);
+ m_mainWidget->leHomeCountry->setEnabled (state);
+ m_mainWidget->wsHomeEmail->raiseWidget(state ? 0 : 1);
+
+ // work address tab
+ m_mainWidget->leWorkStreet->setEnabled (state);
+ m_mainWidget->leWorkExtAddr->setEnabled (state);
+ m_mainWidget->leWorkPOBox->setEnabled (state);
+ m_mainWidget->leWorkCity->setEnabled (state);
+ m_mainWidget->leWorkPostalCode->setEnabled (state);
+ m_mainWidget->leWorkCountry->setEnabled (state);
+ m_mainWidget->wsWorkEmail->raiseWidget(state ? 0 : 1);
+
+ // work information tab
+ m_mainWidget->leCompany->setEnabled (state);
+ m_mainWidget->leDepartment->setEnabled (state);
+ m_mainWidget->lePosition->setEnabled (state);
+ m_mainWidget->leRole->setEnabled (state);
+
+ // phone numbers tab
+ m_mainWidget->lePhoneHome->setEnabled (state);
+ m_mainWidget->lePhoneWork->setEnabled (state);
+ m_mainWidget->lePhoneFax->setEnabled (state);
+ m_mainWidget->lePhoneCell->setEnabled (state);
+
+ // about tab
+ m_mainWidget->teAbout->setEnabled (state);
+
+ // save button
+ enableButton(User1, state);
+ enableButton(User2, state);
+}
+
+/*
+ * Saves a vCard to the contact properties
+ */
+void dlgJabberVCard::slotSaveVCard()
+{
+ setEnabled(false);
+ m_mainWidget->lblStatus->setText( i18n("Saving vCard to server...") );
+
+ XMPP::VCard vCard;
+ XMPP::VCard::AddressList addressList;
+ XMPP::VCard::EmailList emailList;
+ XMPP::VCard::PhoneList phoneList;
+
+ // General information
+ vCard.setNickName( m_mainWidget->leNick->text() );
+ vCard.setFullName( m_mainWidget->leName->text() );
+ vCard.setJid( m_mainWidget->leJID->text() );
+ vCard.setBdayStr( m_mainWidget->leBirthday->text() );
+ vCard.setTimezone( m_mainWidget->leTimezone->text() );
+ vCard.setUrl( m_mainWidget->leHomepage->text() );
+
+ // home address tab
+ XMPP::VCard::Address homeAddress;
+
+ homeAddress.home = true;
+ homeAddress.street = m_mainWidget->leHomeStreet->text();
+ homeAddress.extaddr = m_mainWidget->leHomeExtAddr->text();
+ homeAddress.pobox = m_mainWidget->leHomePOBox->text();
+ homeAddress.locality = m_mainWidget->leHomeCity->text();
+ homeAddress.pcode = m_mainWidget->leHomePostalCode->text();
+ homeAddress.country = m_mainWidget->leHomeCountry->text();
+
+ // work address tab
+ XMPP::VCard::Address workAddress;
+
+ workAddress.work = true;
+ workAddress.street = m_mainWidget->leWorkStreet->text();
+ workAddress.extaddr = m_mainWidget->leWorkExtAddr->text();
+ workAddress.pobox = m_mainWidget->leWorkPOBox->text();
+ workAddress.locality = m_mainWidget->leWorkCity->text();
+ workAddress.pcode = m_mainWidget->leWorkPostalCode->text();
+ workAddress.country = m_mainWidget->leWorkCountry->text();
+
+ addressList.append(homeAddress);
+ addressList.append(workAddress);
+
+ vCard.setAddressList(addressList);
+
+ // home email
+ XMPP::VCard::Email homeEmail;
+
+ homeEmail.home = true;
+ homeEmail.userid = m_mainWidget->leHomeEmail->text();
+
+ // work email
+ XMPP::VCard::Email workEmail;
+
+ workEmail.work = true;
+ workEmail.userid = m_mainWidget->leWorkEmail->text();
+
+ emailList.append(homeEmail);
+ emailList.append(workEmail);
+
+ vCard.setEmailList(emailList);
+
+ // work information tab
+ XMPP::VCard::Org org;
+ org.name = m_mainWidget->leCompany->text();
+ org.unit = QStringList::split(",", m_mainWidget->leDepartment->text());
+ vCard.setOrg(org);
+ vCard.setTitle( m_mainWidget->lePosition->text() );
+ vCard.setRole( m_mainWidget->leRole->text() );
+
+ // phone numbers tab
+ XMPP::VCard::Phone phoneHome;
+ phoneHome.home = true;
+ phoneHome.number = m_mainWidget->lePhoneHome->text();
+
+ XMPP::VCard::Phone phoneWork;
+ phoneWork.work = true;
+ phoneWork.number = m_mainWidget->lePhoneWork->text();
+
+ XMPP::VCard::Phone phoneFax;
+ phoneFax.fax = true;
+ phoneFax.number = m_mainWidget->lePhoneFax->text();
+
+ XMPP::VCard::Phone phoneCell;
+ phoneCell.cell = true;
+ phoneCell.number = m_mainWidget->lePhoneCell->text();
+
+ phoneList.append(phoneHome);
+ phoneList.append(phoneWork);
+ phoneList.append(phoneFax);
+ phoneList.append(phoneCell);
+
+ vCard.setPhoneList(phoneList);
+
+ // about tab
+ vCard.setDesc( m_mainWidget->teAbout->text() );
+
+ // Set contact photo as a binary value (if he has set a photo)
+ if( !m_photoPath.isEmpty() )
+ {
+ QString photoPath = m_photoPath;
+ QImage image( photoPath );
+ QByteArray ba;
+ QBuffer buffer( ba );
+ buffer.open( IO_WriteOnly );
+ image.save( &buffer, "PNG" );
+ vCard.setPhoto( ba );
+ }
+
+ vCard.setVersion("3.0");
+ vCard.setProdId("Kopete");
+
+ XMPP::JT_VCard *task = new XMPP::JT_VCard( m_account->client()->rootTask() );
+ // signal to ourselves when the vCard data arrived
+ QObject::connect(task, SIGNAL(finished()), this, SLOT(slotVCardSaved()));
+ task->set(vCard);
+ task->go(true);
+}
+
+void dlgJabberVCard::slotVCardSaved()
+{
+ XMPP::JT_VCard *vCard = (XMPP::JT_VCard*)sender();
+
+ if( vCard->success() )
+ {
+ m_mainWidget->lblStatus->setText( i18n("vCard save sucessful.") );
+ m_contact->setPropertiesFromVCard( vCard->vcard() );
+ }
+ else
+ {
+ m_mainWidget->lblStatus->setText( i18n("Error: Unable to save vCard.") );
+ }
+
+ setEnabled(true);
+}
+
+void dlgJabberVCard::slotGetVCard()
+{
+ m_mainWidget->lblStatus->setText( i18n("Fetching contact vCard...") );
+
+ setReadOnly(true);
+ setEnabled(false);
+
+ XMPP::JT_VCard *task = new XMPP::JT_VCard ( m_account->client()->rootTask() );
+ // signal to ourselves when the vCard data arrived
+ QObject::connect( task, SIGNAL ( finished () ), this, SLOT ( slotGotVCard () ) );
+ task->get ( m_contact->rosterItem().jid().full() );
+ task->go ( true );
+}
+
+void dlgJabberVCard::slotGotVCard()
+{
+ XMPP::JT_VCard * vCard = (XMPP::JT_VCard *) sender ();
+
+ if( vCard->success() )
+ {
+ m_contact->setPropertiesFromVCard( vCard->vcard() );
+ setEnabled( true );
+
+ assignContactProperties();
+
+ m_mainWidget->lblStatus->setText( i18n("vCard fetching Done.") );
+ }
+ else
+ {
+ m_mainWidget->lblStatus->setText( i18n("Error: vCard could not be fetched correctly. Check connectivity with the Jabber server.") );
+ //it is maybe possible to anyway edit our own vCard (if it is new
+ if(m_account->myself() == m_contact)
+ setEnabled( true );
+ }
+}
+
+void dlgJabberVCard::slotSelectPhoto()
+{
+ QString path;
+ bool remoteFile = false;
+ KURL filePath = KFileDialog::getImageOpenURL( QString::null, this, i18n( "Jabber Photo" ) );
+ if( filePath.isEmpty() )
+ return;
+
+ if( !filePath.isLocalFile() )
+ {
+ if( !KIO::NetAccess::download( filePath, path, this ) )
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "Downloading of Jabber contact photo failed!" ) );
+ return;
+ }
+ remoteFile = true;
+ }
+ else
+ path = filePath.path();
+
+ QImage img( path );
+ img = KPixmapRegionSelectorDialog::getSelectedImage( QPixmap(img), 96, 96, this );
+
+ if( !img.isNull() )
+ {
+ if(img.width() > 96 || img.height() > 96)
+ {
+ // Scale and crop the picture.
+ img = img.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(img.width() < img.height())
+ img = img.copy((img.width()-img.height())/2, 0, 96, 96);
+ else if (img.width() > img.height())
+ img = img.copy(0, (img.height()-img.width())/2, 96, 96);
+
+ }
+ else if (img.width() < 32 || img.height() < 32)
+ {
+ // Scale and crop the picture.
+ img = img.smoothScale( 32, 32, QImage::ScaleMin );
+ // crop image if not square
+ if(img.width() < img.height())
+ img = img.copy((img.width()-img.height())/2, 0, 32, 32);
+ else if (img.width() > img.height())
+ img = img.copy(0, (img.height()-img.width())/2, 32, 32);
+
+ }
+ else if (img.width() != img.height())
+ {
+ if(img.width() < img.height())
+ img = img.copy((img.width()-img.height())/2, 0, img.height(), img.height());
+ else if (img.width() > img.height())
+ img = img.copy(0, (img.height()-img.width())/2, img.height(), img.height());
+ }
+
+ m_photoPath = locateLocal("appdata", "jabberphotos/" + m_contact->rosterItem().jid().full().lower().replace(QRegExp("[./~]"),"-") +".png");
+ if( img.save(m_photoPath, "PNG") )
+ {
+ m_mainWidget->lblPhoto->setPixmap( QPixmap(img) );
+ }
+ else
+ {
+ m_photoPath = QString::null;
+ }
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox( this, KMessageBox::Sorry, i18n( "<qt>An error occurred when trying to change the photo.<br>"
+ "Make sure that you have selected a correct image file</qt>" ) );
+ }
+ if( remoteFile )
+ KIO::NetAccess::removeTempFile( path );
+}
+
+void dlgJabberVCard::slotClearPhoto()
+{
+ m_mainWidget->lblPhoto->setPixmap( QPixmap() );
+ m_photoPath = QString::null;
+}
+
+void dlgJabberVCard::slotOpenURL(const QString &url)
+{
+ if ( !url.isEmpty () || (url == QString::fromLatin1("mailto:") ) )
+ new KRun(KURL( url ) );
+}
+
+#include "dlgjabbervcard.moc"
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/dlgjabbervcard.h b/kopete/protocols/jabber/ui/dlgjabbervcard.h
new file mode 100644
index 00000000..2bea5a2b
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgjabbervcard.h
@@ -0,0 +1,118 @@
+
+/***************************************************************************
+ dlgjabbervcard.h - vCard dialog
+ -------------------
+ begin : Thu Aug 08 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ (C) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DLGJABBERVCARD_H
+#define DLGJABBERVCARD_H
+
+#include <kdialogbase.h>
+#include "xmpp_vcard.h"
+
+class JabberAccount;
+class JabberContact;
+class JabberBaseContact;
+class QString;
+class dlgVCard;
+
+/**
+ * @brief Show the information of a Jabber contact.
+ *
+ * This dialog shows the information of a Jabber contact from
+ * the contact properties(from Kopete).
+ * Also it is used to edit the information of the Account myself contact.
+ *
+ * First it fetch a new version of the vcard then it display the
+ * information. User can force the update using the "Update vCard" button.
+ *
+ * @author Till Gerken <till@tantolo.net>
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+ */
+class dlgJabberVCard : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create the information(vcard) dialog.
+ *
+ * @param account the current Jabber account
+ * @param contact the contact to display or edit information.
+ * @param widget Parent widget.
+ * @param name widget name.
+ */
+ dlgJabberVCard (JabberAccount *account, JabberBaseContact *contact, QWidget * parent = 0, const char *name = 0);
+ ~dlgJabberVCard ();
+
+private slots:
+ /**
+ * Show the KFileDialog for image to select a photo for the contact.
+ */
+ void slotSelectPhoto();
+ /**
+ * Remove(clear) the photo.
+ * Maybe the user doesn't want to export a photo anymore.
+ */
+ void slotClearPhoto();
+ /**
+ * Send vCard to the server.
+ */
+ void slotSaveVCard();
+ /**
+ * Put back the information from the dialog into the contact properties
+ */
+ void slotVCardSaved();
+ /**
+ * Close the dialog.
+ */
+ void slotClose();
+ /**
+ * Open a link. (ex: the homepage link or the email address)
+ */
+ void slotOpenURL(const QString &url);
+
+ /**
+ * Retrieve vCard information for the current contact.
+ */
+ void slotGetVCard();
+ /**
+ * vCard was succesfully fetched, update contact properties
+ * and enable display.
+ */
+ void slotGotVCard();
+
+private:
+ JabberAccount *m_account;
+ JabberBaseContact *m_contact;
+ dlgVCard *m_mainWidget;
+ QString m_photoPath;
+
+ void assignContactProperties();
+ void setReadOnly(bool state);
+ void setEnabled(bool state);
+
+};
+
+#endif // DLGJABBERVCARD_H
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/kopete/protocols/jabber/ui/dlgregister.ui b/kopete/protocols/jabber/ui/dlgregister.ui
new file mode 100644
index 00000000..a3930c2e
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgregister.ui
@@ -0,0 +1,162 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>dlgRegister</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>dlgRegister</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>338</width>
+ <height>119</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Register with Jabber Service</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>grpForm</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="margin">
+ <number>10</number>
+ </property>
+ <property name="title">
+ <string>Registration Form</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblWait</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Please wait while querying the server...</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>0</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRegister</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Register</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>btnCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgRegister</receiver>
+ <slot>reject()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgsendraw.ui b/kopete/protocols/jabber/ui/dlgsendraw.ui
new file mode 100644
index 00000000..08e31f66
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgsendraw.ui
@@ -0,0 +1,159 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>DlgSendRaw</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>DlgSendRaw</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>519</width>
+ <height>233</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Send Raw XML Packet</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblInfo</cstring>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>0</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="text">
+ <string>Type in the packet that should be sent to the server:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lblRealStatus</cstring>
+ </property>
+ </widget>
+ <widget class="QTextEdit">
+ <property name="name">
+ <cstring>tePacket</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>User Defined</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Account Deletion</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Availability Status</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Last Active Time</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Message with Body</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Message with Subject</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Add Roster Item</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Delete Roster Item</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Subscription</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>inputWidget</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnClear</cstring>
+ </property>
+ <property name="text">
+ <string>Clea&amp;r</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnSend</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Send</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>25</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnClose</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgservices.ui b/kopete/protocols/jabber/ui/dlgservices.ui
new file mode 100644
index 00000000..7679309d
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgservices.ui
@@ -0,0 +1,199 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgServices</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>dlgServices</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>446</width>
+ <height>292</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Jabber Service Management</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </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>lblServer</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leServer</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnQuery</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Query Server</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QListView">
+ <column>
+ <property name="text">
+ <string>Jid</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>lvServices</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>111</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnRegister</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Register</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnBrowse</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Browse</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnClose</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>btnClose</sender>
+ <signal>clicked()</signal>
+ <receiver>dlgServices</receiver>
+ <slot>close()</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/jabber/ui/dlgvcard.ui b/kopete/protocols/jabber/ui/dlgvcard.ui
new file mode 100644
index 00000000..efaf5519
--- /dev/null
+++ b/kopete/protocols/jabber/ui/dlgvcard.ui
@@ -0,0 +1,1064 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>dlgVCard</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>dlgVCard</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>764</width>
+ <height>487</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget3</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;General</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblBirthday</cstring>
+ </property>
+ <property name="text">
+ <string>Birthday:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leBirthday</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblHomepage</cstring>
+ </property>
+ <property name="text">
+ <string>Homepage:</string>
+ </property>
+ </widget>
+ <widget class="QWidgetStack">
+ <property name="name">
+ <cstring>wsHomepage</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ <widget class="KURLLabel">
+ <property name="name">
+ <cstring>urlHomepage</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>5</y>
+ <width>450</width>
+ <height>18</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>1</number>
+ </attribute>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leHomepage</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblTimezone</cstring>
+ </property>
+ <property name="text">
+ <string>Timezone:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leTimezone</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblJID</cstring>
+ </property>
+ <property name="text">
+ <string>Jabber ID:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leJID</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblName</cstring>
+ </property>
+ <property name="text">
+ <string>Full name:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblNick</cstring>
+ </property>
+ <property name="text">
+ <string>Nickname:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leNick</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="7" column="0">
+ <property name="name">
+ <cstring>spacer22</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="7" column="1">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="6" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Photo</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>btnSelectPhoto</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Select Photo...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>btnClearPhoto</cstring>
+ </property>
+ <property name="text">
+ <string>Clear Pho&amp;to</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>lblPhoto</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="0" column="0">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="3">
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Home Address</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout36</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>lblState</cstring>
+ </property>
+ <property name="text">
+ <string>Postal code:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblPOBox</cstring>
+ </property>
+ <property name="text">
+ <string>PO box:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leHomeExtAddr</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lblCity</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <spacer row="8" column="1">
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QWidgetStack" row="6" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>wsHomeEmail</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ <widget class="KURLLabel">
+ <property name="name">
+ <cstring>urlHomeEmail</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>4</y>
+ <width>430</width>
+ <height>18</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>1</number>
+ </attribute>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leHomeEmail</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>25</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>lblCountry</cstring>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblStreet</cstring>
+ </property>
+ <property name="text">
+ <string>Street:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>lblEmail</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Email:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>leHomePOBox</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>leHomePostalCode</cstring>
+ </property>
+ </widget>
+ <spacer row="7" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>leHomeCountry</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>leHomeCity</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leHomeStreet</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Work Address</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout37</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="8" column="1">
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>leWorkPOBox</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>leWorkCountry</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lblCity_2</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leWorkExtAddr</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblPOBox_2</cstring>
+ </property>
+ <property name="text">
+ <string>PO box:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>leWorkCity</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leWorkStreet</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>lblEmail_2</cstring>
+ </property>
+ <property name="text">
+ <string>Email:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>lblCountry_2</cstring>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>leWorkPostalCode</cstring>
+ </property>
+ </widget>
+ <spacer row="7" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>spacer25</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>lblState_2</cstring>
+ </property>
+ <property name="text">
+ <string>Postal code:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblStreet_2</cstring>
+ </property>
+ <property name="text">
+ <string>Street:</string>
+ </property>
+ </widget>
+ <widget class="QWidgetStack" row="6" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>wsWorkEmail</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>0</number>
+ </attribute>
+ <widget class="KURLLabel">
+ <property name="name">
+ <cstring>urlWorkEmail</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>5</y>
+ <width>437</width>
+ <height>18</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>page</cstring>
+ </property>
+ <attribute name="id">
+ <number>1</number>
+ </attribute>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leWorkEmail</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>25</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </widget>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Wor&amp;k Information</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout57</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>leDepartment</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>leCompany</cstring>
+ </property>
+ </widget>
+ <spacer row="4" column="1">
+ <property name="name">
+ <cstring>spacer26</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblPosition</cstring>
+ </property>
+ <property name="text">
+ <string>Position:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>lePosition</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>leRole</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lblRole</cstring>
+ </property>
+ <property name="text">
+ <string>Role:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblDepartment</cstring>
+ </property>
+ <property name="text">
+ <string>Department:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblCompany</cstring>
+ </property>
+ <property name="text">
+ <string>Company:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Phone &amp;Numbers</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout59</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="4" column="1">
+ <property name="name">
+ <cstring>spacer27</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>lePhoneFax</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>lePhoneCell</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lblPhoneFax</cstring>
+ </property>
+ <property name="text">
+ <string>Fax:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>lePhoneHome</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lblPhoneCell</cstring>
+ </property>
+ <property name="text">
+ <string>Cell:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>lePhoneWork</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblPhoneWork</cstring>
+ </property>
+ <property name="text">
+ <string>Work:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblPhoneHome</cstring>
+ </property>
+ <property name="text">
+ <string>Home:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>A&amp;bout</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QTextEdit" row="0" column="0">
+ <property name="name">
+ <cstring>teAbout</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblStatus</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>tabWidget3</tabstop>
+ <tabstop>leNick</tabstop>
+ <tabstop>leName</tabstop>
+ <tabstop>leJID</tabstop>
+ <tabstop>leBirthday</tabstop>
+ <tabstop>leTimezone</tabstop>
+ <tabstop>leHomepage</tabstop>
+ <tabstop>leHomeStreet</tabstop>
+ <tabstop>leHomeExtAddr</tabstop>
+ <tabstop>leHomePOBox</tabstop>
+ <tabstop>leHomeCity</tabstop>
+ <tabstop>leHomePostalCode</tabstop>
+ <tabstop>leHomeCountry</tabstop>
+ <tabstop>leHomeEmail</tabstop>
+ <tabstop>leWorkStreet</tabstop>
+ <tabstop>leWorkExtAddr</tabstop>
+ <tabstop>leWorkPOBox</tabstop>
+ <tabstop>leWorkCity</tabstop>
+ <tabstop>leWorkPostalCode</tabstop>
+ <tabstop>leWorkCountry</tabstop>
+ <tabstop>leWorkEmail</tabstop>
+ <tabstop>leCompany</tabstop>
+ <tabstop>leDepartment</tabstop>
+ <tabstop>lePosition</tabstop>
+ <tabstop>leRole</tabstop>
+ <tabstop>lePhoneHome</tabstop>
+ <tabstop>lePhoneWork</tabstop>
+ <tabstop>lePhoneFax</tabstop>
+ <tabstop>lePhoneCell</tabstop>
+ <tabstop>teAbout</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurllabel.h</includehint>
+ <includehint>kurllabel.h</includehint>
+ <includehint>kurllabel.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/jabber/ui/empty.cpp b/kopete/protocols/jabber/ui/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/jabber/ui/empty.cpp
diff --git a/kopete/protocols/jabber/ui/jabberaddcontactpage.cpp b/kopete/protocols/jabber/ui/jabberaddcontactpage.cpp
new file mode 100644
index 00000000..950d5680
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberaddcontactpage.cpp
@@ -0,0 +1,224 @@
+
+/***************************************************************************
+ jabberaddcontactpage.cpp - Add contact widget
+ -------------------
+ begin : Thu Aug 08 2002
+ copyright : (C) 2003 by Till Gerken <till@tantalo.net>
+ (C) 2003 by Daniel Stone <dstone@kde.org>
+ (C) 2006 by Olivier Goffart <ogoffart at kde.org>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "jabberaddcontactpage.h"
+
+#include <qlayout.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kopeteaccount.h>
+#include <qlabel.h>
+#include <kopetegroup.h>
+#include <kopetemetacontact.h>
+
+#include "dlgaddcontact.h"
+#include "jabberaccount.h"
+#include "jabbertransport.h"
+#include "kopetecontact.h"
+#include "jabberclient.h"
+#include "xmpp_tasks.h"
+
+JabberAddContactPage::JabberAddContactPage (Kopete::Account * owner, QWidget * parent, const char *name):AddContactPage (parent, name)
+{
+ (new QVBoxLayout (this))->setAutoAdd (true);
+
+ JabberTransport *transport=dynamic_cast<JabberTransport*>(owner);
+ JabberAccount *jaccount= transport ? transport->account() : dynamic_cast<JabberAccount*>(owner);
+
+ if (jaccount->isConnected ())
+ {
+ jabData = new dlgAddContact (this);
+ jabData->show ();
+
+ if(transport)
+ {
+ jabData->textLabel1->setText( i18n("Loading instruction from gateway...") );
+ XMPP::JT_Gateway * gatewayTask = new XMPP::JT_Gateway ( jaccount->client()->rootTask () );
+ QObject::connect (gatewayTask, SIGNAL (finished ()), this, SLOT (slotPromtReceived()));
+ gatewayTask->get ( transport->myself()->contactId() );
+ gatewayTask->go ( true );
+ }
+ canadd = true;
+ }
+ else
+ {
+ noaddMsg1 = new QLabel (i18n ("You need to be connected to be able to add contacts."), this);
+ noaddMsg2 = new QLabel (i18n ("Connect to the Jabber network and try again."), this);
+ canadd = false;
+ }
+
+}
+
+JabberAddContactPage::~JabberAddContactPage ()
+{
+}
+
+bool JabberAddContactPage::validateData ()
+{
+ return true;
+}
+
+
+bool JabberAddContactPage::apply ( Kopete::Account *account, Kopete::MetaContact *parentContact )
+{
+
+ if( canadd && validateData () )
+ {
+ JabberTransport *transport=dynamic_cast<JabberTransport*>(account);
+ JabberAccount *jaccount=transport?transport->account():dynamic_cast<JabberAccount*>(account);
+
+ QString contactId = jabData->addID->text ();
+
+ if(transport)
+ {
+ XMPP::JT_Gateway * gatewayTask = new XMPP::JT_Gateway ( jaccount->client()->rootTask () );
+ JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND *workaround =
+ new JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND( transport , parentContact , gatewayTask );
+ QObject::connect (gatewayTask, SIGNAL (finished ()), workaround, SLOT (slotJidReceived()));
+ gatewayTask->set ( transport->myself()->contactId() , contactId );
+ gatewayTask->go ( true );
+ return true;
+ }
+
+ QString displayName = parentContact->displayName ();
+ /*
+ if ( displayName.isEmpty () )
+ displayName = contactId;
+ */
+ // collect all group names
+ QStringList groupNames;
+ Kopete::GroupList groupList = parentContact->groups();
+ for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
+ groupNames += group->displayName();
+
+ if ( jaccount->addContact ( contactId, parentContact, Kopete::Account::ChangeKABC ) )
+ {
+ XMPP::RosterItem item;
+ XMPP::Jid jid ( contactId );
+
+ item.setJid ( jid );
+ item.setName ( displayName );
+ item.setGroups ( groupNames );
+
+ // add the new contact to our roster.
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( jaccount->client()->rootTask () );
+
+ rosterTask->set ( item.jid(), item.name(), item.groups() );
+ rosterTask->go ( true );
+
+ // send a subscription request.
+ XMPP::JT_Presence *presenceTask = new XMPP::JT_Presence ( jaccount->client()->rootTask () );
+
+ presenceTask->sub ( jid, "subscribe" );
+ presenceTask->go ( true );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void JabberAddContactPage::slotPromtReceived( )
+{
+ XMPP::JT_Gateway * task = (XMPP::JT_Gateway *) sender ();
+
+ if (task->success ())
+ {
+ jabData->lblID->setText( task->prompt() );
+ jabData->textLabel1->setText( task->desc() );
+ }
+ else
+ {
+ jabData->textLabel1->setText( i18n("An error occured while loading instructions from gateway.") );
+ }
+}
+
+JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND::JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND( JabberTransport *t, Kopete::MetaContact * mc, QObject* task )
+ : QObject(task) , metacontact(mc) , transport(t)
+{}
+
+void JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND::slotJidReceived( )
+{
+ XMPP::JT_Gateway * task = (XMPP::JT_Gateway *) sender ();
+
+ if (!task->success ())
+ {
+ return;
+ // maybe we should show an error message, but i don't like showing error message - Olivier
+ }
+
+ QString contactId=task->prompt();
+
+ Kopete::MetaContact* parentContact=metacontact;
+ JabberAccount *jaccount=transport->account();;
+
+ /*\
+ * this is a copy of the end of JabberAddContactPage::apply
+ \*/
+
+ QString displayName = parentContact->displayName ();
+ /*
+ if ( displayName.isEmpty () )
+ displayName = contactId;
+ */
+ // collect all group names
+ QStringList groupNames;
+ Kopete::GroupList groupList = parentContact->groups();
+ for(Kopete::Group *group = groupList.first(); group; group = groupList.next())
+ groupNames += group->displayName();
+
+ if ( jaccount->addContact ( contactId, parentContact, Kopete::Account::ChangeKABC ) )
+ {
+ XMPP::RosterItem item;
+ XMPP::Jid jid ( contactId );
+
+ item.setJid ( jid );
+ item.setName ( displayName );
+ item.setGroups ( groupNames );
+
+ // add the new contact to our roster.
+ XMPP::JT_Roster * rosterTask = new XMPP::JT_Roster ( jaccount->client()->rootTask () );
+
+ rosterTask->set ( item.jid(), item.name(), item.groups() );
+ rosterTask->go ( true );
+
+ // send a subscription request.
+ XMPP::JT_Presence *presenceTask = new XMPP::JT_Presence ( jaccount->client()->rootTask () );
+
+ presenceTask->sub ( jid, "subscribe" );
+ presenceTask->go ( true );
+
+ return;
+ }
+}
+
+
+
+#include "jabberaddcontactpage.moc"
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/jabberaddcontactpage.h b/kopete/protocols/jabber/ui/jabberaddcontactpage.h
new file mode 100644
index 00000000..8081d0ad
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberaddcontactpage.h
@@ -0,0 +1,76 @@
+
+/***************************************************************************
+ jabberaddcontactpage.h - Add contact widget
+ -------------------
+ begin : Thu Aug 08 2002
+ copyright : (C) 2003 by Till Gerken <till@tantalo.net>
+ (C) 2003 by Daniel Stone <dstone@kde.org>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBERADDCONTACTPAGE_H
+#define JABBERADDCONTACTPAGE_H
+
+#include <addcontactpage.h>
+
+/**
+ *@author Daniel Stone
+ */
+class dlgAddContact;
+class JabberAccount;
+class QLabel;
+
+class JabberAddContactPage:public AddContactPage
+{
+ Q_OBJECT
+
+public:
+ JabberAddContactPage (Kopete::Account * owner, QWidget * parent = 0, const char *name = 0);
+ ~JabberAddContactPage ();
+ virtual bool validateData ();
+ virtual bool apply (Kopete::Account *, Kopete::MetaContact *);
+ dlgAddContact *jabData;
+ QLabel *noaddMsg1;
+ QLabel *noaddMsg2;
+ bool canadd;
+public slots:
+ void slotPromtReceived();
+};
+
+class JabberTransport;
+
+/**
+ * @author Olivier Goffart
+ * this class is just there to workaround the fact that it's not possible to add contact assync with Kopete::AddContactPage::apply
+ */
+class JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND : public QObject
+{ Q_OBJECT
+ public:
+ JabberAddContactPage_there_is_no_possibility_to_add_assync_WORKAROUND( JabberTransport * , Kopete::MetaContact *mc, QObject *parent);
+ Kopete::MetaContact *metacontact;
+ JabberTransport *transport;
+ public slots:
+ void slotJidReceived();
+};
+
+
+#endif
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/jabberchooseserver.cpp b/kopete/protocols/jabber/ui/jabberchooseserver.cpp
new file mode 100644
index 00000000..66598432
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberchooseserver.cpp
@@ -0,0 +1,149 @@
+
+/***************************************************************************
+ jabberchooseserver.cpp - Server list for Jabber
+ -------------------
+ begin : Mon Jul 12 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "jabberchooseserver.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <qtable.h>
+#include <qlabel.h>
+#include "jabberprotocol.h"
+#include "dlgjabberchooseserver.h"
+#include "jabberregisteraccount.h"
+
+JabberChooseServer::JabberChooseServer ( JabberRegisterAccount *parent, const char *name )
+ : KDialogBase ( parent, name, true, i18n("Choose Jabber Server"),
+ KDialogBase::Ok | KDialogBase::Cancel )
+{
+
+ mParentWidget = parent;
+ mSelectedRow = -1;
+
+ mMainWidget = new DlgJabberChooseServer ( this );
+ setMainWidget ( mMainWidget );
+
+ mMainWidget->lblStatus->setText ( i18n ( "Retrieving server list...") );
+
+ mMainWidget->listServers->setLeftMargin ( 0 );
+
+ // retrieve server list
+ mTransferJob = KIO::get ( "http://www.jabber.org/servers.xml" );
+
+ connect ( mTransferJob, SIGNAL ( result ( KIO::Job* ) ), this, SLOT ( slotTransferResult ( KIO::Job* ) ) );
+ connect ( mTransferJob, SIGNAL ( data ( KIO::Job*, const QByteArray& ) ), this, SLOT ( slotTransferData ( KIO::Job*, const QByteArray& ) ) );
+
+ connect ( mMainWidget->listServers, SIGNAL ( pressed ( int, int, int, const QPoint & ) ), this, SLOT ( slotSetSelection ( int ) ) );
+ connect ( mMainWidget->listServers, SIGNAL ( doubleClicked ( int, int, int, const QPoint & ) ), this, SLOT ( slotOk () ) );
+
+ enableButtonOK ( false );
+
+}
+
+JabberChooseServer::~JabberChooseServer()
+{
+}
+
+void JabberChooseServer::slotOk ()
+{
+
+ if ( mSelectedRow != -1 )
+ {
+ mParentWidget->setServer ( mMainWidget->listServers->text ( mSelectedRow, 0 ) );
+ }
+
+ deleteLater ();
+
+}
+
+void JabberChooseServer::slotCancel ()
+{
+
+ deleteLater ();
+
+}
+
+void JabberChooseServer::slotSetSelection ( int row )
+{
+
+ mSelectedRow = row;
+ mMainWidget->listServers->selectRow ( row );
+ enableButtonOK ( true );
+
+}
+
+void JabberChooseServer::slotTransferData ( KIO::Job */*job*/, const QByteArray &data )
+{
+
+ unsigned oldSize = xmlServerList.size ();
+
+ xmlServerList.resize ( oldSize + data.size () );
+
+ memcpy ( &xmlServerList.data()[oldSize], data.data (), data.size () );
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Server list now " << xmlServerList.size () << endl;
+
+}
+
+void JabberChooseServer::slotTransferResult ( KIO::Job *job )
+{
+
+ if ( job->error () || mTransferJob->isErrorPage () )
+ {
+ mMainWidget->lblStatus->setText ( i18n ( "Could not retrieve server list." ) );
+ return;
+ }
+ else
+ {
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Received server list ok!" << endl;
+
+ // clear status message
+ mMainWidget->lblStatus->setText ( "" );
+
+ // parse XML list
+ QDomDocument doc;
+
+ if ( !doc.setContent ( xmlServerList ) )
+ {
+ mMainWidget->lblStatus->setText ( i18n ( "Could not parse the server list.") );
+ return;
+ }
+
+ QDomElement docElement = doc.documentElement ();
+
+ mMainWidget->listServers->setNumRows ( docElement.childNodes().count () );
+
+ int listIndex = 0;
+ for( QDomNode node = docElement.firstChild (); !node.isNull (); node = node.nextSibling (), listIndex++ )
+ {
+ QDomNamedNodeMap attributes = node.attributes ();
+ mMainWidget->listServers->setText ( listIndex, 0, attributes.namedItem ( "jid" ).nodeValue () );
+ mMainWidget->listServers->setText ( listIndex, 1, attributes.namedItem ( "name" ).nodeValue () );
+ }
+
+ mMainWidget->listServers->adjustColumn ( 0 );
+ mMainWidget->listServers->adjustColumn ( 1 );
+ }
+
+}
+
+
+#include "jabberchooseserver.moc"
diff --git a/kopete/protocols/jabber/ui/jabberchooseserver.h b/kopete/protocols/jabber/ui/jabberchooseserver.h
new file mode 100644
index 00000000..0cc8045d
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberchooseserver.h
@@ -0,0 +1,65 @@
+
+/***************************************************************************
+ jabberchooseserver.h - Server list for Jabber
+ -------------------
+ begin : Mon Jul 12 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBERCHOOSESERVER_H
+#define JABBERCHOOSESERVER_H
+
+#include <kdialogbase.h>
+#include <qcstring.h>
+
+class JabberRegisterAccount;
+class DlgJabberChooseServer;
+
+namespace KIO
+{
+ class Job;
+ class TransferJob;
+}
+
+/**
+@author Kopete Developers
+*/
+class JabberChooseServer : public KDialogBase
+{
+
+Q_OBJECT
+
+public:
+ JabberChooseServer ( JabberRegisterAccount *parent = 0, const char *name = 0);
+
+ ~JabberChooseServer();
+
+private slots:
+ void slotOk ();
+ void slotCancel ();
+ void slotTransferData ( KIO::Job *job, const QByteArray &data );
+ void slotTransferResult ( KIO::Job *job );
+ void slotSetSelection ( int row );
+
+private:
+ DlgJabberChooseServer *mMainWidget;
+ JabberRegisterAccount *mParentWidget;
+ KIO::TransferJob *mTransferJob;
+ QByteArray xmlServerList;
+
+ int mSelectedRow;
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/jabbereditaccountwidget.cpp b/kopete/protocols/jabber/ui/jabbereditaccountwidget.cpp
new file mode 100644
index 00000000..b0284e41
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabbereditaccountwidget.cpp
@@ -0,0 +1,286 @@
+
+/***************************************************************************
+ jabberaccountwidget.cpp - Account widget for Jabber
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ Based on code by Olivier Goffart <ogoffart @ kde.org>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <kdebug.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kpassdlg.h>
+#include <kopetepassword.h>
+#include <kopetepasswordedaccount.h>
+
+#include "kopeteuiglobal.h"
+#include "kopetepasswordwidget.h"
+
+#include "jabberclient.h"
+#include "jabbereditaccountwidget.h"
+#include "jabberregisteraccount.h"
+#include "dlgjabberchangepassword.h"
+
+JabberEditAccountWidget::JabberEditAccountWidget (JabberProtocol * proto, JabberAccount * ident, QWidget * parent, const char *name)
+ : DlgJabberEditAccountWidget (parent, name), KopeteEditAccountWidget (ident)
+{
+
+ m_protocol = proto;
+
+ connect (mID, SIGNAL (textChanged (const QString &)), this, SLOT (updateServerField ()));
+ connect (cbCustomServer, SIGNAL (toggled (bool)), this, SLOT (updateServerField ()));
+
+ connect (cbUseSSL, SIGNAL (toggled (bool)), this, SLOT (sslToggled (bool)));
+
+ connect (btnChangePassword, SIGNAL ( clicked() ), this, SLOT ( slotChangePasswordClicked () ));
+
+ if (account())
+ {
+ // we are working with an existing account
+ reopen ();
+ btnRegister->setEnabled ( false );
+ }
+ else
+ {
+ // this is a new account
+ btnChangePassword->setEnabled ( false );
+ connect (btnRegister, SIGNAL (clicked ()), this, SLOT (registerClicked ()));
+ }
+}
+
+JabberEditAccountWidget::~JabberEditAccountWidget ()
+{
+}
+
+JabberAccount *JabberEditAccountWidget::account ()
+{
+
+ return dynamic_cast<JabberAccount *>(KopeteEditAccountWidget::account () );
+
+}
+
+void JabberEditAccountWidget::reopen ()
+{
+
+ // FIXME: this is temporary until Kopete supports accound ID changes!
+ mID->setDisabled(true);
+
+ mID->setText (account()->accountId ());
+ mPass->load (&account()->password ());
+ cbAutoConnect->setChecked (account()->excludeConnect());
+
+ mResource->setText (account()->configGroup()->readEntry ("Resource", QString::fromLatin1("Kopete")));
+ mPriority->setValue (account()->configGroup()->readNumEntry ("Priority", 5));
+ mServer->setText (account()->configGroup()->readEntry ("Server", QString::null));
+
+ cbUseSSL->setChecked (account()->configGroup()->readBoolEntry( "UseSSL", false));
+
+ mPort->setValue (account()->configGroup()->readNumEntry("Port", 5222));
+
+ QString auth = account()->configGroup()->readEntry("AuthType", QString::null);
+
+ cbCustomServer->setChecked (account()->configGroup()->readBoolEntry("CustomServer",false));
+
+ if(cbCustomServer->isChecked ())
+ {
+ labelServer->setEnabled(true);
+ mServer->setEnabled(true);
+ labelPort->setEnabled(true);
+ mPort->setEnabled(true);
+ }
+ else
+ {
+ mServer->setEnabled (false);
+ mServer->setText(mID->text().section("@", 1));
+ }
+
+ cbAllowPlainTextPassword->setChecked (account()->configGroup()->readBoolEntry("AllowPlainTextPassword", true));
+
+ KGlobal::config()->setGroup("Jabber");
+ leLocalIP->setText (KGlobal::config()->readEntry("LocalIP", ""));
+ sbLocalPort->setValue (KGlobal::config()->readNumEntry("LocalPort", 8010));
+
+ leProxyJID->setText (account()->configGroup()->readEntry("ProxyJID", QString::null));
+
+ // Privacy
+ cbSendEvents->setChecked( account()->configGroup()->readBoolEntry("SendEvents", true) );
+ cbSendDeliveredEvent->setChecked( account()->configGroup()->readBoolEntry("SendDeliveredEvent", true) );
+ cbSendDisplayedEvent->setChecked( account()->configGroup()->readBoolEntry("SendDisplayedEvent", true) );
+ cbSendComposingEvent->setChecked( account()->configGroup()->readBoolEntry("SendComposingEvent", true) );
+ cbSendGoneEvent->setChecked( account()->configGroup()->readBoolEntry("SendGoneEvent", true) );
+
+ cbHideSystemInfo->setChecked( account()->configGroup()->readBoolEntry("HideSystemInfo", false) );
+
+ // Global Identity
+ cbGlobalIdentity->setChecked( account()->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) );
+}
+
+Kopete::Account *JabberEditAccountWidget::apply ()
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << "JabberEditAccount::apply()" << endl;
+
+ if (!account())
+ {
+ setAccount(new JabberAccount (m_protocol, mID->text ()));
+ }
+
+ if(account()->isConnected())
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Information,
+ i18n("The changes you just made will take effect next time you log in with Jabber."),
+ i18n("Jabber Changes During Online Jabber Session"));
+ }
+
+ this->writeConfig ();
+
+ static_cast<JabberAccount*>(account())->setS5BServerPort ( sbLocalPort->value () );
+
+ return account();
+}
+
+
+void JabberEditAccountWidget::writeConfig ()
+{
+ account()->configGroup()->writeEntry("UseSSL", cbUseSSL->isChecked());
+
+ mPass->save(&account()->password ());
+
+ account()->configGroup()->writeEntry("CustomServer", cbCustomServer->isChecked());
+
+ // FIXME: The call below represents a flaw in the current Kopete API.
+ // Once the API is cleaned up, this will most likely require a change.
+ //account()->setAccountId(mID->text());
+
+ account()->configGroup()->writeEntry("AllowPlainTextPassword", cbAllowPlainTextPassword->isChecked());
+ account()->configGroup()->writeEntry("Server", mServer->text ());
+ account()->configGroup()->writeEntry("Resource", mResource->text ());
+ account()->configGroup()->writeEntry("Priority", QString::number (mPriority->value ()));
+ account()->configGroup()->writeEntry("Port", QString::number (mPort->value ()));
+
+ account()->setExcludeConnect(cbAutoConnect->isChecked());
+
+ KGlobal::config()->setGroup("Jabber");
+ KGlobal::config()->writeEntry("LocalIP", leLocalIP->text());
+ KGlobal::config()->writeEntry("LocalPort", sbLocalPort->value());
+
+ account()->configGroup()->writeEntry("ProxyJID", leProxyJID->text());
+
+ // Privacy
+ account()->configGroup()->writeEntry("SendEvents", cbSendEvents->isChecked());
+ account()->configGroup()->writeEntry("SendDeliveredEvent", cbSendDeliveredEvent->isChecked());
+ account()->configGroup()->writeEntry("SendDisplayedEvent", cbSendDisplayedEvent->isChecked());
+ account()->configGroup()->writeEntry("SendComposingEvent", cbSendComposingEvent->isChecked());
+ account()->configGroup()->writeEntry("SendGoneEvent", cbSendGoneEvent->isChecked());
+
+ account()->configGroup()->writeEntry("HideSystemInfo", cbHideSystemInfo->isChecked());
+
+ // Global Identity
+ account()->configGroup()->writeEntry("ExcludeGlobalIdentity", cbGlobalIdentity->isChecked());
+}
+
+bool JabberEditAccountWidget::validateData ()
+{
+
+ if(!mID->text().contains('@'))
+ {
+ KMessageBox::sorry(this, i18n("The Jabber ID you have chosen is invalid. "
+ "Please make sure it is in the form user@server.com, like an email address."),
+ i18n("Invalid Jabber ID"));
+
+ return false;
+ }
+
+ return true;
+}
+
+void JabberEditAccountWidget::updateServerField ()
+{
+
+ if(!cbCustomServer->isChecked())
+ {
+ QString newServer = mID->text().section("@", 1);
+ mPort->setValue(5222);
+ // check if ssl is enabled and set the port correctly
+ sslToggled(cbUseSSL->isChecked());
+ mServer->setText(newServer);
+ labelServer->setEnabled(false);
+ mServer->setEnabled(false);
+ labelPort->setEnabled(false);
+ mPort->setEnabled(false);
+ }
+ else
+ {
+ labelServer->setEnabled(true);
+ mServer->setEnabled(true);
+ labelPort->setEnabled(true);
+ mPort->setEnabled(true);
+ }
+
+}
+
+void JabberEditAccountWidget::deleteClicked ()
+{
+
+ // delete account here
+
+}
+
+void JabberEditAccountWidget::registerClicked ()
+{
+
+ JabberRegisterAccount *registerDlg = new JabberRegisterAccount ( this );
+
+ registerDlg->show ();
+
+}
+
+void JabberEditAccountWidget::slotChangePasswordClicked ()
+{
+
+ DlgJabberChangePassword *passwordDlg = new DlgJabberChangePassword ( account (), this );
+
+ connect ( passwordDlg, SIGNAL ( destroyed () ), this, SLOT ( slotChangePasswordFinished () ) );
+
+ passwordDlg->show ();
+
+}
+
+void JabberEditAccountWidget::slotChangePasswordFinished ()
+{
+
+ // in case the password has been changed, we need to update it in the UI
+ reopen ();
+
+}
+
+void JabberEditAccountWidget::sslToggled (bool value)
+{
+ if (value && (mPort->value() == 5222))
+ mPort->stepUp ();
+ else
+ if(!value && (mPort->value() == 5223))
+ mPort->stepDown ();
+}
+
+#include "jabbereditaccountwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/jabber/ui/jabbereditaccountwidget.h b/kopete/protocols/jabber/ui/jabbereditaccountwidget.h
new file mode 100644
index 00000000..1f3ba378
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabbereditaccountwidget.h
@@ -0,0 +1,62 @@
+
+/***************************************************************************
+ jabberaccountwidget.h - Account widget for Jabber
+ -------------------
+ begin : Mon Dec 9 2002
+ copyright : (C) 2002-2003 by Till Gerken <till@tantalo.net>
+ Based on code by Olivier Goffart <ogoffart @ kde.org>
+ email : kopete-devel@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBEREDITACCOUNTWIDEGET_H
+#define JABBEREDITACCOUNTWIDEGET_H
+
+#include <qwidget.h>
+#include <kprogress.h>
+#include "editaccountwidget.h"
+#include "jabberaccount.h"
+#include "dlgjabbereditaccountwidget.h"
+#include "jabberprotocol.h"
+
+/**
+ *@author Till Gerken <till@tantalo.net>
+ */
+
+class JabberEditAccountWidget:public DlgJabberEditAccountWidget, public KopeteEditAccountWidget
+{
+
+Q_OBJECT
+
+public:
+ JabberEditAccountWidget (JabberProtocol * proto, JabberAccount *, QWidget * parent = 0, const char *name = 0);
+ ~JabberEditAccountWidget ();
+ virtual bool validateData ();
+ virtual Kopete::Account *apply ();
+ JabberAccount *account ();
+
+private slots:
+ void registerClicked ();
+ void slotChangePasswordClicked ();
+ void slotChangePasswordFinished ();
+ void deleteClicked ();
+ void sslToggled (bool);
+ void updateServerField ();
+
+private:
+ JabberProtocol *m_protocol;
+
+ void reopen ();
+ void writeConfig ();
+
+};
+
+#endif
diff --git a/kopete/protocols/jabber/ui/jabberregisteraccount.cpp b/kopete/protocols/jabber/ui/jabberregisteraccount.cpp
new file mode 100644
index 00000000..f5142736
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberregisteraccount.cpp
@@ -0,0 +1,389 @@
+
+/***************************************************************************
+ jabberregister.cpp - Register dialog for Jabber
+ -------------------
+ begin : Sun Jul 11 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "jabberregisteraccount.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <klineedit.h>
+#include <kpassdlg.h>
+#include <knuminput.h>
+#include <kpushbutton.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qtimer.h>
+#include <qregexp.h>
+
+#include "qca.h"
+#include "xmpp.h"
+#include "xmpp_tasks.h"
+
+#include "kopeteuiglobal.h"
+#include "kopetepasswordwidget.h"
+#include "jabberprotocol.h"
+#include "jabberaccount.h"
+#include "jabberclient.h"
+#include "jabberconnector.h"
+#include "jabbereditaccountwidget.h"
+#include "jabberchooseserver.h"
+#include "dlgjabberregisteraccount.h"
+
+JabberRegisterAccount::JabberRegisterAccount ( JabberEditAccountWidget *parent, const char *name )
+ : KDialogBase ( parent, name, true, i18n("Register New Jabber Account"),
+ KDialogBase::Ok | KDialogBase::Cancel )
+{
+
+ mParentWidget = parent;
+
+ // setup main dialog
+ mMainWidget = new DlgJabberRegisterAccount ( this );
+ setMainWidget ( mMainWidget );
+
+ // replace "Ok" button with a "Register" button
+ KGuiItem registerButton = KStdGuiItem::ok();
+ registerButton.setText ( i18n ( "Register" ) );
+ setButtonOK ( registerButton );
+
+ enableButtonSeparator ( true );
+
+ // clear variables
+ jabberClient = new JabberClient ();
+
+ connect ( jabberClient, SIGNAL ( csError ( int ) ), this, SLOT ( slotCSError ( int ) ) );
+ connect ( jabberClient, SIGNAL ( tlsWarning ( int ) ), this, SLOT ( slotHandleTLSWarning ( int ) ) );
+ connect ( jabberClient, SIGNAL ( connected () ), this, SLOT ( slotConnected () ) );
+
+ jidRegExp.setPattern ( "[\\w\\d.+_-]{1,}@[\\w\\d.-]{1,}" );
+ hintPixmap = KGlobal::iconLoader()->loadIcon ( "jabber_online", KIcon::Small );
+
+ mSuccess = false;
+
+ // get all settings from the main dialog
+ mMainWidget->leServer->setText ( parent->mServer->text () );
+ mMainWidget->leJID->setText ( parent->mID->text () );
+ mMainWidget->lePassword->setText ( parent->mPass->password () );
+ // mMainWidget->lePasswordVerify->setText ( parent->mPass->password () ); //BUG 114631
+ mMainWidget->sbPort->setValue ( parent->mPort->value () );
+ mMainWidget->cbUseSSL->setChecked ( parent->cbUseSSL->isChecked () );
+
+ // connect buttons to slots, ok is already connected by default
+ connect ( this, SIGNAL ( cancelClicked () ), this, SLOT ( slotDeleteDialog () ) );
+ connect ( mMainWidget->btnChooseServer, SIGNAL ( clicked () ), this, SLOT ( slotChooseServer () ) );
+ connect ( mMainWidget->leServer, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( slotJIDInformation () ) );
+ connect ( mMainWidget->leJID, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( slotJIDInformation () ) );
+ connect ( mMainWidget->cbUseSSL, SIGNAL ( toggled ( bool ) ), this, SLOT ( slotSSLToggled () ) );
+
+ connect ( mMainWidget->leServer, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( validateData () ) );
+ connect ( mMainWidget->leJID, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( validateData () ) );
+ connect ( mMainWidget->lePassword, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( validateData () ) );
+ connect ( mMainWidget->lePasswordVerify, SIGNAL ( textChanged ( const QString & ) ), this, SLOT ( validateData () ) );
+
+ // display JID info now
+ slotJIDInformation ();
+
+ // display validation info
+ validateData ();
+}
+
+
+JabberRegisterAccount::~JabberRegisterAccount()
+{
+ delete jabberClient;
+}
+
+void JabberRegisterAccount::slotDeleteDialog ()
+{
+
+ deleteLater ();
+
+}
+
+void JabberRegisterAccount::validateData ()
+{
+
+ int valid = true;
+ int passwordHighlight = false;
+
+ if ( mMainWidget->leServer->text().isEmpty () )
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Please enter a server name, or click Choose." ) );
+ mMainWidget->pixServer->setPixmap ( hintPixmap );
+ valid = false;
+ }
+ else
+ {
+ mMainWidget->pixServer->setText ( "" );
+ }
+
+ if ( valid && !jidRegExp.exactMatch ( mMainWidget->leJID->text() ) )
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Please enter a valid Jabber ID." ) );
+ mMainWidget->pixJID->setPixmap ( hintPixmap );
+ valid = false;
+ }
+ else
+ {
+ mMainWidget->pixJID->setText ( "" );
+ }
+
+ if ( valid &&
+ ( QString::fromLatin1 ( mMainWidget->lePassword->password () ).isEmpty () ||
+ QString::fromLatin1 ( mMainWidget->lePasswordVerify->password () ).isEmpty () ) )
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Please enter the same password twice." ) );
+ valid = false;
+ passwordHighlight = true;
+ }
+
+ if ( valid &&
+ ( QString::fromLatin1 ( mMainWidget->lePassword->password () ) !=
+ QString::fromLatin1 ( mMainWidget->lePasswordVerify->password () ) ) )
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Password entries do not match." ) );
+ valid = false;
+ passwordHighlight = true;
+ }
+
+ if ( passwordHighlight == true )
+ {
+ mMainWidget->pixPassword->setPixmap ( hintPixmap );
+ mMainWidget->pixPasswordVerify->setPixmap ( hintPixmap );
+ }
+ else {
+ mMainWidget->pixPassword->setText ( "" );
+ mMainWidget->pixPasswordVerify->setText ( "" );
+ }
+
+ if ( valid )
+ {
+ // clear status message if we have valid data
+ mMainWidget->lblStatusMessage->setText ( "" );
+ }
+
+ enableButtonOK ( valid );
+
+}
+
+void JabberRegisterAccount::slotJIDInformation ()
+{
+
+ if ( !mMainWidget->leServer->text().isEmpty () &&
+ ( !jidRegExp.exactMatch ( mMainWidget->leJID->text () ) ||
+ ( mMainWidget->leJID->text().section ( "@", 1 ) != mMainWidget->leServer->text () ) ) )
+ {
+ mMainWidget->lblJIDInformation->setText ( i18n ( "Unless you know what you are doing, your JID should be of the form "
+ "\"username@server.com\". In your case for example \"username@%1\"." ).
+ arg ( mMainWidget->leServer->text () ) );
+ }
+ else
+ {
+ mMainWidget->lblJIDInformation->setText ( "" );
+ }
+
+}
+
+void JabberRegisterAccount::slotSSLToggled ()
+{
+
+ if ( mMainWidget->cbUseSSL->isChecked () )
+ {
+ if ( mMainWidget->sbPort->value () == 5222 )
+ {
+ mMainWidget->sbPort->setValue ( 5223 );
+ }
+ }
+ else
+ {
+ if ( mMainWidget->sbPort->value () == 5223 )
+ {
+ mMainWidget->sbPort->setValue ( 5222 );
+ }
+ }
+
+}
+
+void JabberRegisterAccount::slotChooseServer ()
+{
+
+ (new JabberChooseServer ( this ))->show ();
+
+}
+
+void JabberRegisterAccount::setServer ( const QString &server )
+{
+
+ mMainWidget->leServer->setText ( server );
+ mMainWidget->leJID->setText ( QString ( "@%1" ).arg ( server ) );
+
+}
+
+void JabberRegisterAccount::slotOk ()
+{
+
+ mMainWidget->lblStatusMessage->setText ( "" );
+
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Registering a new Jabber account." << endl;
+
+ enableButtonOK ( false );
+
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Connecting to server..." ) );
+
+ // cancel any previous attempt
+ jabberClient->disconnect ();
+
+ // FIXME: we need to use the old protocol for now
+ jabberClient->setUseXMPP09 ( true );
+
+ jabberClient->setUseSSL ( mMainWidget->cbUseSSL->isChecked () );
+
+ // FIXME: check this when using the new protocol
+ jabberClient->setOverrideHost ( true, mMainWidget->leServer->text (), mMainWidget->sbPort->value () );
+
+ // start connection, no authentication
+ switch ( jabberClient->connect ( XMPP::Jid ( mMainWidget->leJID->text () ), QString::null, false ) )
+ {
+ case JabberClient::NoTLS:
+ // no SSL support, at the connecting stage this means the problem is client-side
+ 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 ( mMainWidget->leJID->text () ),
+ i18n ("Jabber SSL Error"));
+ break;
+
+ case JabberClient::Ok:
+ default:
+ // everything alright!
+ break;
+ }
+
+}
+
+void JabberRegisterAccount::disconnect ()
+{
+
+ if(jabberClient)
+ jabberClient->disconnect ();
+
+ if ( !mSuccess )
+ enableButtonOK ( true );
+
+}
+
+void JabberRegisterAccount::slotHandleTLSWarning ( int validityResult )
+{
+ kdDebug ( JABBER_DEBUG_GLOBAL ) << k_funcinfo << "Handling TLS warning..." << endl;
+
+ if ( JabberAccount::handleTLSWarning ( jabberClient, validityResult ) )
+ {
+ // resume stream
+ jabberClient->continueAfterTLSWarning ();
+ }
+ else
+ {
+ // disconnect stream
+ disconnect ();
+ }
+
+}
+
+void JabberRegisterAccount::slotCSError (int error)
+{
+ kdDebug(JABBER_DEBUG_GLOBAL) << k_funcinfo << "Error in stream signalled, disconnecting." << endl;
+
+ Kopete::Account::DisconnectReason errorClass;
+
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Protocol error." ) );
+
+ // display message to user
+ JabberAccount::handleStreamError (error, jabberClient->clientStream()->errorCondition (), jabberClient->clientConnector()->errorCode (), mMainWidget->leServer->text (), errorClass);
+
+ disconnect ();
+
+}
+
+void JabberRegisterAccount::slotConnected ()
+{
+ kdDebug (JABBER_DEBUG_GLOBAL) << k_funcinfo << "Launching registration task..." << endl;
+
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Connected successfully, registering new account..." ) );
+
+ XMPP::JT_Register * task = new XMPP::JT_Register (jabberClient->rootTask ());
+ QObject::connect (task, SIGNAL (finished ()), this, SLOT (slotRegisterUserDone ()));
+ task->reg (mMainWidget->leJID->text().section("@", 0, 0), mMainWidget->lePassword->password ());
+ task->go (true);
+
+}
+
+void JabberRegisterAccount::slotRegisterUserDone ()
+{
+ XMPP::JT_Register * task = (XMPP::JT_Register *) sender ();
+
+ if (task->success ())
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Registration successful." ) );
+
+ // save settings to parent
+ mParentWidget->mServer->setText ( mMainWidget->leServer->text () );
+ mParentWidget->mID->setText ( mMainWidget->leJID->text () );
+ mParentWidget->mPass->setPassword ( mMainWidget->lePassword->password () );
+ mParentWidget->mPort->setValue ( mMainWidget->sbPort->value () );
+ mParentWidget->cbUseSSL->setChecked ( mMainWidget->cbUseSSL->isChecked () );
+
+ // disable input widgets
+ mMainWidget->btnChooseServer->setEnabled ( false );
+ mMainWidget->leServer->setEnabled ( false );
+ mMainWidget->leJID->setEnabled ( false );
+ mMainWidget->lePassword->setEnabled ( false );
+ mMainWidget->lePasswordVerify->setEnabled ( false );
+ mMainWidget->sbPort->setEnabled ( false );
+ mMainWidget->cbUseSSL->setEnabled ( false );
+
+ // disable input widget labels
+ mMainWidget->lblServer->setEnabled ( false );
+ mMainWidget->lblJID->setEnabled ( false );
+ mMainWidget->lblPassword->setEnabled ( false );
+ mMainWidget->lblPasswordVerify->setEnabled ( false );
+ mMainWidget->lblPort->setEnabled ( false );
+
+ mSuccess = true;
+
+ // rewire buttons
+ enableButtonOK ( false );
+ setButtonCancel ( KStdGuiItem::close () );
+ connect ( this, SIGNAL ( closeClicked () ), this, SLOT ( slotDeleteDialog () ) );
+ }
+ else
+ {
+ mMainWidget->lblStatusMessage->setText ( i18n ( "Registration failed." ) );
+ KMessageBox::queuedMessageBox (Kopete::UI::Global::mainWidget (), KMessageBox::Information,
+ i18n ("Unable to create account on the server. The Jabber ID is probably already in use."),
+ i18n ("Jabber Account Registration"));
+
+ }
+
+ // FIXME: this is required because Iris crashes if we try
+ // to disconnect here. Hopefully Justin can fix this.
+ QTimer::singleShot(0, this, SLOT(disconnect ()));
+
+}
+
+#include "jabberregisteraccount.moc"
diff --git a/kopete/protocols/jabber/ui/jabberregisteraccount.h b/kopete/protocols/jabber/ui/jabberregisteraccount.h
new file mode 100644
index 00000000..40d643a7
--- /dev/null
+++ b/kopete/protocols/jabber/ui/jabberregisteraccount.h
@@ -0,0 +1,75 @@
+
+/***************************************************************************
+ jabberregister.h - Register dialog for Jabber
+ -------------------
+ begin : Sun Jul 11 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2001-2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef JABBERREGISTER_H
+#define JABBERREGISTER_H
+
+#include <kdialogbase.h>
+#include <qregexp.h>
+#include <qpixmap.h>
+
+class DlgJabberRegisterAccount;
+class JabberProtocol;
+class JabberClient;
+class JabberEditAccountWidget;
+
+/**
+@author Till Gerken
+*/
+class JabberRegisterAccount : public KDialogBase
+{
+
+Q_OBJECT
+
+public:
+ JabberRegisterAccount ( JabberEditAccountWidget *parent = 0, const char *name = 0 );
+
+ ~JabberRegisterAccount ();
+
+ void setServer ( const QString &server );
+
+private slots:
+ void slotChooseServer ();
+ void slotJIDInformation ();
+ void slotSSLToggled ();
+ void slotOk ();
+
+ void slotHandleTLSWarning ( int validityResult );
+ void slotCSError ( int error );
+ void slotConnected ();
+
+ void slotRegisterUserDone ();
+ void slotDeleteDialog ();
+ void validateData ();
+
+ void disconnect ();
+
+private:
+ DlgJabberRegisterAccount *mMainWidget;
+ JabberEditAccountWidget *mParentWidget;
+
+ QRegExp jidRegExp;
+ QPixmap hintPixmap;
+
+ JabberClient *jabberClient;
+
+ bool mSuccess;
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/Makefile.am b/kopete/protocols/meanwhile/Makefile.am
new file mode 100644
index 00000000..e161fac3
--- /dev/null
+++ b/kopete/protocols/meanwhile/Makefile.am
@@ -0,0 +1,37 @@
+METASOURCES = AUTO
+SUBDIRS = ui icons
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/ui \
+ -I./ui \
+ $(all_includes) \
+ $(MEANWHILE_CFLAGS)
+
+noinst_HEADERS = \
+ meanwhileprotocol.h \
+ meanwhileaddcontactpage.h \
+ meanwhileeditaccountwidget.h \
+ meanwhileaccount.h \
+ meanwhilecontact.h \
+ meanwhilesession.h \
+ meanwhileplugin.h
+
+kde_module_LTLIBRARIES = kopete_meanwhile.la
+
+kopete_meanwhile_la_SOURCES = \
+ meanwhileprotocol.cpp \
+ meanwhileaddcontactpage.cpp \
+ meanwhileeditaccountwidget.cpp \
+ meanwhileaccount.cpp \
+ meanwhilecontact.cpp \
+ meanwhilesession.cpp \
+ meanwhileplugin.cpp
+
+kopete_meanwhile_la_LDFLAGS = -no-undefined -module \
+ $(KDE_PLUGIN) $(all_libraries)
+
+kopete_meanwhile_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la \
+ ui/libkopetemeanwhileui.la $(MEANWHILE_LIBS)
+
+service_DATA = kopete_meanwhile.desktop
+servicedir= $(kde_servicesdir)
diff --git a/kopete/protocols/meanwhile/README b/kopete/protocols/meanwhile/README
new file mode 100644
index 00000000..5c77dbf6
--- /dev/null
+++ b/kopete/protocols/meanwhile/README
@@ -0,0 +1,53 @@
+This is a kopete plugin for meanwhile using the libmeanwhile library.
+
+To INSTALL
+==========
+
+1. install libmeanwhile
+2. install kopete with the meanwhile plugin.
+
+installing libmeanwhile
+=======================
+from http://meanwhile.sf.net - use version 0.3
+Refer to INSTALL in the meanwhile code. Run configure without specifying the --with_gaim_src
+ # autogen.sh
+ # ./configure --prefix=/usr
+ # make
+ # su -c make install
+
+installing kopete with meanwhile plugin
+=======================================
+1. Get kopete src from kopete.kde.org (latest cvs)
+2. move this directory to $KOPETE_SRC/kopete/protocols/meanwhile,
+3. patch the configure script in $KOPETE_SRC to pickup meanwhile
+ in $KOPETE_SRC
+ # patch -P0 < kopete/protocols/meanwhile/configure.patch
+4. patch Makefile.am in protocols with protocols-makefile.patch
+ in $KOPETE_SRC
+ # patch -P0 < kopete/protocols/meanwhile/protocols-makefile.patch
+5. run automake to process this Makefile.am
+ in $KOPETE_SRC
+ # automake kopete/protocols/Makefile
+6. libmeanwhile uses glib and kopete's makefile have no references to glib, so
+ edit $KOPETE_SRC/kopete/protocols/meanwhile/Makefile.am and verify that
+ GLIB_INCLUDES points to the right places. These variables are currently
+ set for Fedora-core-1. If you have to modify the variable, rebuild the
+ makefile.
+ in $KOPETE_SRC
+ # automake kopete/protocols/meanwhile/Makefile
+7. follow the steps defined in kopete INSTALL:
+ on fedora/redhat this could be (from $KOPETE_SRC)
+ # ./configure --prefix=/usr/
+ # make
+ # make install
+7.1 if you want to install only meanwhile on an existing copy of kopete,
+ after the make, try make install in
+ $KOPETE_SRC/kopete/protocols/meanwhile
+
+8. enjoy*.
+
+* Hopefully i have fixed this, but when you try to add accounts and you
+donot find meanwhile listed, copy kopete/protocols/meanwhile/kopete_meanwhile.desktop
+to $KDE_DIR/share/services (/usr/share/services on fedora).
+* If you find the icons for meanwhile missing, overwrite Makefile in
+ kopete/protocols/meanwhile/icons with Makefile.siva
diff --git a/kopete/protocols/meanwhile/icons/Makefile.am b/kopete/protocols/meanwhile/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/protocols/meanwhile/icons/cr128-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr128-app-meanwhile_protocol.png
new file mode 100644
index 00000000..93289326
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr128-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_away.png b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_away.png
new file mode 100644
index 00000000..9dc152db
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_away.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_dnd.png b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_dnd.png
new file mode 100644
index 00000000..df361d8d
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_dnd.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_idle.png b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_idle.png
new file mode 100644
index 00000000..4b97a931
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_idle.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_unknown.png b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_unknown.png
new file mode 100644
index 00000000..673c937a
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr16-action-meanwhile_unknown.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr16-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr16-app-meanwhile_protocol.png
new file mode 100644
index 00000000..69767ef3
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr16-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr22-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr22-app-meanwhile_protocol.png
new file mode 100644
index 00000000..87aec661
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr22-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr32-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr32-app-meanwhile_protocol.png
new file mode 100644
index 00000000..72cf82ab
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr32-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr48-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr48-app-meanwhile_protocol.png
new file mode 100644
index 00000000..49c08f76
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr48-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/cr64-app-meanwhile_protocol.png b/kopete/protocols/meanwhile/icons/cr64-app-meanwhile_protocol.png
new file mode 100644
index 00000000..f4b008e7
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/cr64-app-meanwhile_protocol.png
Binary files differ
diff --git a/kopete/protocols/meanwhile/icons/crsc-app-meanwhile_protocol.svgz b/kopete/protocols/meanwhile/icons/crsc-app-meanwhile_protocol.svgz
new file mode 100644
index 00000000..55e6ef45
--- /dev/null
+++ b/kopete/protocols/meanwhile/icons/crsc-app-meanwhile_protocol.svgz
Binary files differ
diff --git a/kopete/protocols/meanwhile/kopete_meanwhile.desktop b/kopete/protocols/meanwhile/kopete_meanwhile.desktop
new file mode 100644
index 00000000..80aa6383
--- /dev/null
+++ b/kopete/protocols/meanwhile/kopete_meanwhile.desktop
@@ -0,0 +1,58 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=meanwhile_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_meanwhile
+X-Kopete-Messaging-Protocol=messaging/meanwhile
+X-KDE-PluginInfo-Author=Jeremy Kerr
+X-KDE-PluginInfo-Email=jk@ozlabs.org
+X-KDE-PluginInfo-Name=kopete_meanwhile
+X-KDE-PluginInfo-Version=0.0.1
+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=Meanwhile
+Name[is]=à meðan
+Name[mk]=Во меѓувреме
+Name[nb]=Imens
+Name[ne]=उकà¥à¤¤ अवधि
+Name[sk]=Medzitým
+Name[ta]=இடைபà¯à®ªà®Ÿà¯à®Ÿ பொழà¯à®¤à®¿à®²à¯
+Comment=Meanwhile (Lotus Sametime) Protocol
+Comment[bg]=Протокол Ñ Meanwhile (Lotus Sametime)
+Comment[ca]=Protocol Meanwhile (Lotus Sametime)
+Comment[cs]=Meanwhile (Lotus Sametime) protokol
+Comment[da]=Meanwhile-protokol (Lotus Sametime)
+Comment[de]=Meanwhile-Protokoll (Lotus Sametime)
+Comment[el]=ΠÏωτόκολλο Meanwhile (Lotus Sametime)
+Comment[es]=Protocolo Meanwhile (Lotus Sametime)
+Comment[et]=Meanwhile (Lotus Sametime) protokoll
+Comment[fa]=قرارداد Meanwhile‌ (گاهی لوتوس)
+Comment[fr]=Protocole Meanwhile (Lotus Sametime)
+Comment[he]=פרוטוקול Meanwhile
+Comment[hu]=Protokoll a Meanwhile (Lotus Sametime) használatához
+Comment[is]="Meanwhile" (Lotus Sametimr) samskipturegla
+Comment[it]=Protocollo Meanwhile (Lotus Sametime)
+Comment[ja]=Meanwhile (Lotus Sametime) プロトコル
+Comment[km]=ពីធីការ Meanwhile (Lotus Sametime)
+Comment[lt]=Meanwhile (Lotus Sametime) protokolas
+Comment[nb]=Programtillegg for Meanwhile-protokoll (Lotus Sametime)
+Comment[nds]=Meanwhile-Protokoll (Lotus Sametime)
+Comment[ne]=उकà¥à¤¤ समयको (उही समयको लोटस) पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Procotol voor Meanwhile (Lotus Sametime)
+Comment[pl]=Protokół Meanwhile (Lotus Sametime)
+Comment[pt]=Protocolo Meanwhile (Lotus Sametime)
+Comment[pt_BR]=Protocolo Meanwhile (Lotus Sametime)
+Comment[ru]=Протокол Meanwhile (Lotus Sametime)
+Comment[sk]=Meanwhile (Lotus Sametime) Protokol
+Comment[sl]=Protokol Meanwhile (Lotus Sametime)
+Comment[sr]=Протокол Meanwhile (Lotus Sametime)
+Comment[sr@Latn]=Protokol Meanwhile (Lotus Sametime)
+Comment[sv]=Meanwhile-protokoll (Lotus Sametime)
+Comment[tr]=Meanwhile Protokolü
+Comment[uk]=Протокол Meanwhile (Lotus Sametime)
+Comment[zh_CN]=Meanwhile (Lotus Sametime) åè®®
+Comment[zh_TW]=Meanwhile (Lotus Sametime)å”定外掛程å¼
diff --git a/kopete/protocols/meanwhile/meanwhileaccount.cpp b/kopete/protocols/meanwhile/meanwhileaccount.cpp
new file mode 100644
index 00000000..c130475b
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileaccount.cpp
@@ -0,0 +1,274 @@
+/*
+ meanwhileaccount.cpp - a meanwhile account
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+ Copyright (c) 2005 by Jeremy Kerr <jk@ozlabs.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <signal.h>
+#include "meanwhileprotocol.h"
+#include "meanwhileplugin.h"
+#include "meanwhileaccount.h"
+#include "meanwhilecontact.h"
+#include "meanwhilesession.h"
+#include "kopetechatsession.h"
+#include "kopetecontactlist.h"
+#include "kopetepassword.h"
+
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kconfigbase.h>
+#include "kopeteaway.h"
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <qdict.h>
+
+MeanwhileAccount::MeanwhileAccount(
+ MeanwhileProtocol *parent,
+ const QString &accountID,
+ const char *name)
+ : Kopete::PasswordedAccount(parent, accountID, 0, name)
+{
+ HERE;
+ m_meanwhileId = accountID;
+ m_session = 0L;
+ setMyself(new MeanwhileContact(m_meanwhileId, m_meanwhileId, this,
+ Kopete::ContactList::self()->myself()));
+ setOnlineStatus(parent->statusOffline);
+ infoPlugin = new MeanwhilePlugin();
+}
+
+MeanwhileAccount::~MeanwhileAccount()
+{
+ if (m_session)
+ delete m_session;
+}
+
+void MeanwhileAccount::setPlugin(MeanwhilePlugin *plugin)
+{
+ delete infoPlugin;
+ infoPlugin = plugin;
+}
+
+bool MeanwhileAccount::createContact(
+ const QString & contactId ,
+ Kopete::MetaContact * parentContact)
+{
+ MeanwhileContact* newContact = new MeanwhileContact(contactId,
+ parentContact->displayName(), this, parentContact);
+
+ MeanwhileProtocol *p = static_cast<MeanwhileProtocol *>(protocol());
+
+ if ((newContact != 0L) && (m_session != 0L)
+ && (myself()->onlineStatus() !=
+ p->statusOffline))
+ m_session->addContact(newContact);
+
+ return newContact != 0L;
+}
+
+void MeanwhileAccount::connectWithPassword(const QString &password)
+{
+ if (password.isEmpty()) {
+ disconnect(Kopete::Account::Manual);
+ return;
+ }
+
+ if (m_session == 0L) {
+ m_session = new MeanwhileSession(this);
+ if (m_session == 0L) {
+ mwDebug() << "session creation failed" << endl;
+ return;
+ }
+
+ QObject::connect(m_session,
+ SIGNAL(sessionStateChange(Kopete::OnlineStatus)),
+ this, SLOT(slotSessionStateChange(Kopete::OnlineStatus)));
+ QObject::connect(m_session,
+ SIGNAL(serverNotification(const QString &)),
+ this, SLOT(slotServerNotification(const QString&)));
+
+ }
+
+ if (m_session == 0L) {
+ mwDebug() << "No memory for session" << endl;
+ return;
+ }
+
+ if (!m_session->isConnected() && !m_session->isConnecting())
+ m_session->connect(password);
+
+ m_session->setStatus(initialStatus());
+}
+
+void MeanwhileAccount::disconnect()
+{
+ disconnect(Manual);
+}
+
+void MeanwhileAccount::disconnect(Kopete::Account::DisconnectReason reason)
+{
+ if (m_session == 0L)
+ return;
+
+ MeanwhileProtocol *p = static_cast<MeanwhileProtocol *>(protocol());
+ setAllContactsStatus(p->statusOffline);
+ disconnected(reason);
+ emit isConnectedChanged();
+
+ delete m_session;
+ m_session = 0L;
+}
+
+KActionMenu * MeanwhileAccount::actionMenu()
+{
+ KActionMenu *menu = Kopete::Account::actionMenu();
+
+ menu->popupMenu()->insertSeparator();
+
+#if 0
+ menu->insert(new KAction(i18n("&Change Status Message"), QString::null, 0,
+ this, SLOT(meanwhileChangeStatus()), this,
+ "meanwhileChangeStatus"));
+ //infoPlugin->addCustomMenus(theMenu);
+#endif
+ return menu;
+}
+
+QString MeanwhileAccount::getServerName()
+{
+ return configGroup()->readEntry("Server");
+}
+
+int MeanwhileAccount::getServerPort()
+{
+ return configGroup()->readNumEntry("Port");
+}
+
+void MeanwhileAccount::setServerName(const QString &server)
+{
+ configGroup()->writeEntry("Server", server);
+}
+
+void MeanwhileAccount::setServerPort(int port)
+{
+ configGroup()->writeEntry("Port", port);
+}
+
+void MeanwhileAccount::setClientID(int client, int major, int minor)
+{
+ configGroup()->writeEntry("clientID", client);
+ configGroup()->writeEntry("clientVersionMajor", major);
+ configGroup()->writeEntry("clientVersionMinor", minor);
+}
+
+void MeanwhileAccount::resetClientID()
+{
+ configGroup()->deleteEntry("clientID");
+ configGroup()->deleteEntry("clientVersionMajor");
+ configGroup()->deleteEntry("clientVersionMinor");
+}
+
+bool MeanwhileAccount::getClientIDParams(int *clientID,
+ int *verMajor, int *verMinor)
+{
+ bool custom_id = configGroup()->hasKey("clientID");
+
+ MeanwhileSession::getDefaultClientIDParams(clientID, verMajor, verMinor);
+
+ if (custom_id) {
+ *clientID = configGroup()->readUnsignedNumEntry("clientID", *clientID);
+ *verMajor = configGroup()->readUnsignedNumEntry("clientVersionMajor",
+ *verMinor);
+ *verMinor = configGroup()->readUnsignedNumEntry("clientVersionMinor",
+ *verMinor);
+ }
+
+ return custom_id;
+}
+
+void MeanwhileAccount::slotServerNotification(const QString &mesg)
+{
+ KMessageBox::queuedMessageBox(0, KMessageBox::Error , mesg,
+ i18n("Meanwhile Plugin: Message from server"), KMessageBox::Notify);
+}
+
+QString MeanwhileAccount::meanwhileId() const
+{
+ return m_meanwhileId;
+}
+
+void MeanwhileAccount::setAway(bool away, const QString &reason)
+{
+ MeanwhileProtocol *p = static_cast<MeanwhileProtocol *>(protocol());
+ setOnlineStatus(away ? p->statusIdle : p->statusOnline, reason);
+}
+
+void MeanwhileAccount::setOnlineStatus(const Kopete::OnlineStatus &status,
+ const QString &reason)
+{
+ HERE;
+ Kopete::OnlineStatus oldstatus = myself()->onlineStatus();
+
+ mwDebug() << "From: " << oldstatus.description() << "(" <<
+ oldstatus.internalStatus() << "):" << oldstatus.isDefinitelyOnline()
+ << endl;
+ mwDebug() << "To: " << status.description() << "(" <<
+ status.internalStatus() << "):" << status.isDefinitelyOnline() << endl;
+
+ if (oldstatus == status)
+ return;
+
+ if (!oldstatus.isDefinitelyOnline() && status.isDefinitelyOnline()) {
+ connect();
+
+ } else if (oldstatus.isDefinitelyOnline() && !status.isDefinitelyOnline()) {
+ disconnect(Kopete::Account::Manual);
+
+ } else if (m_session)
+ /* todo: check session state? */
+ m_session->setStatus(status, reason);
+
+ else
+ mwDebug() << "Trying to set status, but no session exists" << endl;
+
+ /* we should set this on the callback below */
+ //myself()->setOnlineStatus(status);
+}
+
+void MeanwhileAccount::syncContactsToServer()
+{
+ if (m_session != 0L)
+ m_session->syncContactsToServer();
+}
+
+void MeanwhileAccount::slotSessionStateChange(Kopete::OnlineStatus status)
+{
+ HERE;
+ Kopete::OnlineStatus oldstatus = myself()->onlineStatus();
+ myself()->setOnlineStatus(status);
+
+ if (status.isDefinitelyOnline() != oldstatus.isDefinitelyOnline()) {
+ if (status.isDefinitelyOnline())
+ m_session->addContacts(contacts());
+ emit isConnectedChanged();
+ }
+}
+
+MeanwhileSession *MeanwhileAccount::session()
+{
+ return m_session;
+}
+
+#include "meanwhileaccount.moc"
diff --git a/kopete/protocols/meanwhile/meanwhileaccount.h b/kopete/protocols/meanwhile/meanwhileaccount.h
new file mode 100644
index 00000000..d08b07c5
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileaccount.h
@@ -0,0 +1,121 @@
+/*
+ meanwhileaccount.h - meanwhile account
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+ Copyright (c) 2005 by Jeremy Kerr <jk@ozlabs.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILEACCOUNT_H
+#define MEANWHILEACCOUNT_H
+
+#include <kopetepasswordedaccount.h>
+#include "meanwhileprotocol.h"
+#include "meanwhileplugin.h"
+
+class MeanwhileSession;
+
+/**
+ * A class to handle a single Meanwhile Account.
+ */
+class MeanwhileAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a new Meanwhile account
+ * @param protocol The MeanwhileProtocol that this acccount is for
+ * @param accountID The (meanwhile) account id of this account
+ * @param name The name of this account
+ */
+ MeanwhileAccount(MeanwhileProtocol *protocol, const QString &accountID,
+ const char *name = 0L);
+
+ ~MeanwhileAccount();
+
+ virtual bool createContact(const QString &contactId,
+ Kopete::MetaContact *parentContact);
+
+ virtual void connectWithPassword(const QString &password);
+
+ virtual void disconnect();
+
+ virtual void disconnect(Kopete::Account::DisconnectReason reason);
+
+ virtual KActionMenu *actionMenu();
+
+ /** Get the server host name */
+ QString getServerName();
+ /** Get the server port */
+ int getServerPort();
+ /** Set the server host name */
+ void setServerName(const QString &server);
+ /** Set the server port */
+ void setServerPort(int port);
+ /** Provide an information plugin for this account */
+ void setPlugin(MeanwhilePlugin *plugin);
+
+ /** Set the client identification parameters for the sametime connection */
+ void setClientID(int client, int major, int minor);
+
+ /* returns true if custom IDs are in use, and populates the args */
+ bool getClientIDParams(int *clientID, int *verMajor, int *verMinor);
+
+ /** Reset client identification parameters to their defaults */
+ void resetClientID();
+
+ MeanwhilePlugin *infoPlugin;
+
+ /**
+ * Save the current contact list to the server
+ */
+ void syncContactsToServer();
+
+ /**
+ * Get a reference to the meanwhile session object, if one exists
+ */
+ MeanwhileSession *session();
+
+ /**
+ * Get the meanwhile id for this account
+ * @return The meanwhile ID for the account
+ */
+ QString meanwhileId() const;
+
+public slots:
+ /**
+ * Called by the session to notify that the state has changed
+ */
+ void slotSessionStateChange(Kopete::OnlineStatus status);
+
+ /**
+ * Called by the session when a notification message has been received
+ */
+ void slotServerNotification(const QString &mesg);
+
+ /** Reimplemented from Kopete::Account */
+ void setOnlineStatus(const Kopete::OnlineStatus& status,
+ const QString &reason = QString::null);
+ void setAway(bool away, const QString&reason = QString::null);
+
+private:
+ /** Current online status */
+ Kopete::OnlineStatus status;
+
+ /** A meanwhile session */
+ MeanwhileSession *m_session;
+
+ /* The user id for this account */
+ QString m_meanwhileId;
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhileaddcontactpage.cpp b/kopete/protocols/meanwhile/meanwhileaddcontactpage.cpp
new file mode 100644
index 00000000..8709700b
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileaddcontactpage.cpp
@@ -0,0 +1,74 @@
+/*
+ meanwhileaddcontactpage.cpp - add a contact
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "meanwhileaddcontactpage.h"
+#include <qpushbutton.h>
+#include <qlayout.h>
+#include <kopeteaccount.h>
+#include <kopetemetacontact.h>
+#include <qlineedit.h>
+
+#include "meanwhileprotocol.h"
+#include "meanwhileaccount.h"
+#include "meanwhileplugin.h"
+
+MeanwhileAddContactPage::MeanwhileAddContactPage(
+ QWidget* parent,
+ Kopete::Account *_account)
+ : AddContactPage(parent, 0L), theAccount(_account),
+ theParent(parent)
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ theDialog = new MeanwhileAddContactBase(this);
+ MeanwhileAccount *account =
+ static_cast<MeanwhileAccount *>(_account);
+ if (account->infoPlugin->canProvideMeanwhileId())
+ {
+ QObject::connect(theDialog->btnFindUser, SIGNAL(clicked()),
+ SLOT(slotFindUser()));
+ }
+ else
+ theDialog->btnFindUser->setDisabled(true);
+ theDialog->show();
+}
+
+MeanwhileAddContactPage::~MeanwhileAddContactPage()
+{
+}
+
+void MeanwhileAddContactPage::slotFindUser()
+{
+ MeanwhileAccount *account =
+ static_cast<MeanwhileAccount *>(theAccount);
+ account->infoPlugin->getMeanwhileId(theParent,
+ theDialog->contactID);
+}
+
+bool MeanwhileAddContactPage::apply(
+ Kopete::Account* a,
+ Kopete::MetaContact* m )
+{
+ QString displayName = theDialog->contactID->text();
+ MeanwhileAccount* myAccount = static_cast<MeanwhileAccount*>(a);
+ return myAccount->addContact(displayName, m, Kopete::Account::ChangeKABC );
+}
+
+bool MeanwhileAddContactPage::validateData()
+{
+ return ! theDialog->contactID->text().isEmpty();
+}
+
+#include "meanwhileaddcontactpage.moc"
diff --git a/kopete/protocols/meanwhile/meanwhileaddcontactpage.h b/kopete/protocols/meanwhile/meanwhileaddcontactpage.h
new file mode 100644
index 00000000..889b2707
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileaddcontactpage.h
@@ -0,0 +1,45 @@
+/*
+ meanwhileaddcontactpage.h - add a contact
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILEADDCONTACTPAGE_H
+#define MEANWHILEADDCONTACTPAGE_H
+
+#include <addcontactpage.h>
+#include "meanwhileaddcontactbase.h"
+
+namespace Kopete { class Account; }
+namespace Kopete { class MetaContact; }
+
+class MeanwhileAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ MeanwhileAddContactPage( QWidget* parent = 0,
+ Kopete::Account *account=0);
+ ~MeanwhileAddContactPage();
+
+ virtual bool apply(Kopete::Account* a, Kopete::MetaContact* m);
+ virtual bool validateData();
+
+protected:
+ MeanwhileAddContactBase *theDialog;
+ Kopete::Account *theAccount;
+ QWidget *theParent;
+public slots:
+ void slotFindUser();
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhilecontact.cpp b/kopete/protocols/meanwhile/meanwhilecontact.cpp
new file mode 100644
index 00000000..fd30bc46
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhilecontact.cpp
@@ -0,0 +1,132 @@
+/*
+ meanwhilecontact.cpp - a meanwhile contact
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kopeteaccount.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+
+#include "meanwhileprotocol.h"
+#include "meanwhilesession.h"
+#include "meanwhileaccount.h"
+#include "meanwhilecontact.h"
+#include "meanwhileplugin.h"
+
+MeanwhileContact::MeanwhileContact(QString userId, QString nickname,
+ MeanwhileAccount *account, Kopete::MetaContact *parent)
+ : Kopete::Contact(account, userId, parent)
+{
+ setNickName(nickname);
+ m_msgManager = 0L;
+ m_meanwhileId = userId;
+ setOnlineStatus(static_cast<MeanwhileProtocol *>(account->protocol())
+ ->statusOffline);
+}
+
+MeanwhileContact::~MeanwhileContact()
+{
+}
+
+bool MeanwhileContact::isReachable()
+{
+ return isOnline();
+}
+
+void MeanwhileContact::serialize(QMap<QString, QString> &serializedData,
+ QMap<QString, QString> &addressBookData)
+{
+ Kopete::Contact::serialize(serializedData, addressBookData);
+}
+
+void MeanwhileContact::showContactSettings()
+{
+}
+
+void MeanwhileContact::slotUserInfo()
+{
+ MeanwhileAccount *theAccount = static_cast<MeanwhileAccount *>( account());
+ theAccount->infoPlugin->showUserInfo(m_meanwhileId);
+}
+
+Kopete::ChatSession* MeanwhileContact::manager(CanCreateFlags canCreate)
+{
+ if (m_msgManager != 0L || canCreate == Kopete::Contact::CannotCreate)
+ return m_msgManager;
+
+ QPtrList<Kopete::Contact> contacts;
+ contacts.append(this);
+ m_msgManager = Kopete::ChatSessionManager::self()->
+ create(account()->myself(), contacts, protocol());
+
+ connect(m_msgManager,
+ SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession*)),
+ this, SLOT(sendMessage(Kopete::Message&)));
+
+ connect(m_msgManager, SIGNAL(myselfTyping(bool)),
+ this, SLOT(slotSendTyping(bool)));
+
+ connect(m_msgManager, SIGNAL(destroyed()),
+ this, SLOT(slotChatSessionDestroyed()));
+
+ return m_msgManager;
+}
+
+QString MeanwhileContact::meanwhileId() const
+{
+ return m_meanwhileId;
+}
+
+void MeanwhileContact::sendMessage(Kopete::Message &message)
+{
+ static_cast<MeanwhileAccount *>(account())->session()->sendMessage(message);
+}
+
+void MeanwhileContact::slotSendTyping(bool isTyping)
+{
+ static_cast<MeanwhileAccount *>(account())->session()->
+ sendTyping(this, isTyping);
+}
+
+void MeanwhileContact::receivedMessage(const QString &message)
+{
+ Kopete::ContactPtrList contactList;
+ contactList.append(account()->myself());
+ Kopete::Message kmessage(this, contactList, message,
+ Kopete::Message::Inbound);
+
+ manager(Kopete::Contact::CanCreate)->appendMessage(kmessage);
+}
+
+void MeanwhileContact::sync(unsigned int changed)
+{
+ if (changed)
+ static_cast<MeanwhileAccount *>(account())->syncContactsToServer();
+}
+
+void MeanwhileContact::slotChatSessionDestroyed()
+{
+ m_msgManager->deref();
+ m_msgManager = 0L;
+}
+
+#include "meanwhilecontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/meanwhile/meanwhilecontact.h b/kopete/protocols/meanwhile/meanwhilecontact.h
new file mode 100644
index 00000000..eface094
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhilecontact.h
@@ -0,0 +1,68 @@
+/*
+ meanwhilecontact.h - a contact
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILECONTACT_H
+#define MEANWHILECONTACT_H
+
+#include <qmap.h>
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+#include "meanwhileaccount.h"
+
+class KAction;
+class KActionCollection;
+namespace Kopete { class Account; }
+namespace Kopete { class ChatSession; }
+namespace Kopete { class MetaContact; }
+
+class MeanwhileContact : public Kopete::Contact
+{
+ Q_OBJECT
+public:
+
+ MeanwhileContact(QString userId, QString nickname,
+ MeanwhileAccount *account, Kopete::MetaContact *parent);
+ ~MeanwhileContact();
+
+ virtual bool isReachable();
+
+ virtual void serialize(QMap<QString, QString> &serializedData,
+ QMap<QString, QString> &addressBookData);
+
+ virtual Kopete::ChatSession *manager(
+ CanCreateFlags canCreate = CanCreate);
+
+ QString meanwhileId() const;
+
+ virtual void sync(unsigned int changed = 0xff);
+
+public slots:
+
+ void sendMessage( Kopete::Message &message );
+ void receivedMessage( const QString &message );
+ virtual void slotUserInfo();
+
+protected slots:
+ void showContactSettings();
+ void slotChatSessionDestroyed();
+ void slotSendTyping(bool isTyping);
+
+private:
+ QString m_meanwhileId;
+ Kopete::ChatSession *m_msgManager;
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhileeditaccountwidget.cpp b/kopete/protocols/meanwhile/meanwhileeditaccountwidget.cpp
new file mode 100644
index 00000000..f3d05710
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileeditaccountwidget.cpp
@@ -0,0 +1,194 @@
+/*
+ meanwhileeditaccountwidget.cpp - edit an account
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <kdebug.h>
+#include <kopeteaccount.h>
+#include <kopetepasswordwidget.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include "meanwhileprotocol.h"
+#include "meanwhileaccount.h"
+#include "meanwhileeditaccountwidget.h"
+#include "meanwhilesession.h"
+
+#define DEFAULT_SERVER "messaging.opensource.ibm.com"
+#define DEFAULT_PORT 1533
+
+void MeanwhileEditAccountWidget::setupClientList()
+{
+ const struct MeanwhileClientID *id;
+ int i = 0;
+
+ for (id = MeanwhileSession::getClientIDs(); id->name; id++, i++) {
+ QString name = QString("%1 (0x%2)")
+ .arg(QString(id->name))
+ .arg(id->id, 0, 16);
+
+ mClientID->insertItem(name, i);
+
+ if (id->id == mwLogin_MEANWHILE)
+ mClientID->setCurrentItem(i);
+ }
+}
+
+void MeanwhileEditAccountWidget::selectClientListItem(int selectedID)
+{
+ const struct MeanwhileClientID *id;
+ int i = 0;
+
+ for (id = MeanwhileSession::getClientIDs(); id->name; id++, i++) {
+ if (id->id == selectedID) {
+ mClientID->setCurrentItem(i);
+ break;
+ }
+ }
+}
+
+MeanwhileEditAccountWidget::MeanwhileEditAccountWidget(
+ QWidget* parent,
+ Kopete::Account* theAccount,
+ MeanwhileProtocol *theProtocol)
+ : MeanwhileEditAccountBase(parent),
+ KopeteEditAccountWidget( theAccount )
+{
+ protocol = theProtocol;
+
+ /* setup client identifier combo box */
+ setupClientList();
+
+ if (account())
+ {
+ int clientID, verMajor, verMinor;
+ bool useCustomID;
+
+ mScreenName->setText(account()->accountId());
+ mScreenName->setReadOnly(true);
+ mScreenName->setDisabled(true);
+ mPasswordWidget->load(&static_cast<MeanwhileAccount*>(account())->password());
+ mAutoConnect->setChecked(account()->excludeConnect());
+
+ MeanwhileAccount *myAccount = static_cast<MeanwhileAccount *>(account());
+ useCustomID = myAccount->getClientIDParams(&clientID,
+ &verMajor, &verMinor);
+
+ mServerName->setText(myAccount->getServerName());
+ mServerPort->setValue(myAccount->getServerPort());
+
+ if (useCustomID) {
+ selectClientListItem(clientID);
+ mClientVersionMajor->setValue(verMajor);
+ mClientVersionMinor->setValue(verMinor);
+ chkCustomClientID->setChecked(true);
+ }
+
+ }
+ else
+ {
+ slotSetServer2Default();
+ }
+
+ QObject::connect(btnServerDefaults, SIGNAL(clicked()),
+ SLOT(slotSetServer2Default()));
+
+ show();
+}
+
+MeanwhileEditAccountWidget::~MeanwhileEditAccountWidget()
+{
+}
+
+
+Kopete::Account* MeanwhileEditAccountWidget::apply()
+{
+ if(!account())
+ setAccount(new MeanwhileAccount(protocol, mScreenName->text()));
+
+ MeanwhileAccount *myAccount = static_cast<MeanwhileAccount *>(account());
+
+ myAccount->setExcludeConnect(mAutoConnect->isChecked());
+
+ mPasswordWidget->save(&static_cast<MeanwhileAccount*>(account())->password());
+
+ myAccount->setServerName(mServerName->text());
+ myAccount->setServerPort(mServerPort->value());
+
+ if (chkCustomClientID->isChecked()) {
+ const struct MeanwhileClientID *ids = MeanwhileSession::getClientIDs();
+ myAccount->setClientID(ids[mClientID->currentItem()].id,
+ mClientVersionMajor->value(),
+ mClientVersionMinor->value());
+ } else {
+ myAccount->resetClientID();
+ }
+
+ return myAccount;
+}
+
+bool MeanwhileEditAccountWidget::validateData()
+{
+ if(mScreenName->text().isEmpty())
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>You must enter a valid screen name.</qt>"),
+ i18n("Meanwhile Plugin"));
+ return false;
+ }
+ if( !mPasswordWidget->validate() )
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>You must deselect password remembering or enter a valid password.</qt>"),
+ i18n("Meanwhile Plugin"));
+ return false;
+ }
+ if (mServerName->text().isEmpty())
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>You must enter the server's hostname/ip address.</qt>"),
+ i18n("Meanwhile Plugin"));
+ return false;
+ }
+ if (mServerPort->text() == 0)
+ {
+ KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>0 is not a valid port number.</qt>"),
+ i18n("Meanwhile Plugin"));
+ return false;
+ }
+ return true;
+}
+
+void MeanwhileEditAccountWidget::slotSetServer2Default()
+{
+ int clientID, verMajor, verMinor;
+
+ MeanwhileSession::getDefaultClientIDParams(&clientID,
+ &verMajor, &verMinor);
+
+ mServerName->setText(DEFAULT_SERVER);
+ mServerPort->setValue(DEFAULT_PORT);
+ chkCustomClientID->setChecked(false);
+ selectClientListItem(clientID);
+ mClientVersionMajor->setValue(verMajor);
+ mClientVersionMinor->setValue(verMinor);
+}
+
+#include "meanwhileeditaccountwidget.moc"
diff --git a/kopete/protocols/meanwhile/meanwhileeditaccountwidget.h b/kopete/protocols/meanwhile/meanwhileeditaccountwidget.h
new file mode 100644
index 00000000..9e05465b
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileeditaccountwidget.h
@@ -0,0 +1,51 @@
+/*
+ meanwhileeditaccountwidget.h - edit an account
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILEEDITACCOUNTWIDGET_H
+#define MEANWHILEEDITACCOUNTWIDGET_H
+
+#include <qwidget.h>
+#include <editaccountwidget.h>
+#include "meanwhileeditaccountbase.h"
+
+class QVBoxLayout;
+namespace Kopete { class Account; }
+
+class MeanwhileEditAccountWidget :
+ public MeanwhileEditAccountBase,
+ public KopeteEditAccountWidget
+{
+Q_OBJECT
+public:
+ MeanwhileEditAccountWidget( QWidget* parent,
+ Kopete::Account* account,
+ MeanwhileProtocol *protocol);
+
+ ~MeanwhileEditAccountWidget();
+
+ virtual Kopete::Account* apply();
+
+ virtual bool validateData();
+protected slots:
+ void slotSetServer2Default();
+protected:
+ MeanwhileProtocol *protocol;
+private:
+ void setupClientList();
+ void selectClientListItem(int selectedID);
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhileplugin.cpp b/kopete/protocols/meanwhile/meanwhileplugin.cpp
new file mode 100644
index 00000000..d8f617cb
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileplugin.cpp
@@ -0,0 +1,37 @@
+/*
+ meanwhileplugin.cpp - a plugin to provide more functions
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <qwidget.h>
+#include <qlineedit.h>
+#include "meanwhileplugin.h"
+
+void MeanwhilePlugin::getMeanwhileId(QWidget * /*parent*/,
+ QLineEdit * /*fillThis*/)
+{
+}
+
+bool MeanwhilePlugin::canProvideMeanwhileId()
+{
+ return false;
+}
+
+void MeanwhilePlugin::showUserInfo(const QString &/*userid*/)
+{
+}
+
+void MeanwhilePlugin::addCustomMenus(KActionMenu *menu)
+{
+}
diff --git a/kopete/protocols/meanwhile/meanwhileplugin.h b/kopete/protocols/meanwhile/meanwhileplugin.h
new file mode 100644
index 00000000..b2ddbdcb
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileplugin.h
@@ -0,0 +1,47 @@
+/*
+ meanwhileplugin.h - provide helpers for meanwhile from an external plugin
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef __MEANWHILE_PLUGIN_H__
+#define __MEANWHILE_PLUGIN_H__
+
+#include "qstring.h"
+#include <kaction.h>
+
+class QLineEdit;
+
+class MeanwhilePlugin
+{
+public:
+ /* do something when the find button on add contact is hit
+ * - like do lookups in company databases
+ * when done fill 'fillThis' with the id of the contact
+ */
+ virtual void getMeanwhileId(QWidget *parent,QLineEdit *fillThis);
+ /* can this plugin provide the above functionality */
+ virtual bool canProvideMeanwhileId();
+
+ /* show user info has been selected - the meanwhile server doesnt
+ * seem to have any info...maybe you can find it somewhere else
+ */
+ virtual void showUserInfo(const QString &userid);
+
+ /* if you want to provide more functions on the rightclick dropdown
+ * menu...implement this
+ */
+ virtual void addCustomMenus(KActionMenu *menu);
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhileprotocol.cpp b/kopete/protocols/meanwhile/meanwhileprotocol.cpp
new file mode 100644
index 00000000..434de2f3
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileprotocol.cpp
@@ -0,0 +1,126 @@
+/*
+ meanwhileprotocol.cpp - the meanwhile protocol
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "meanwhileprotocol.h"
+#include "meanwhileaddcontactpage.h"
+#include "meanwhileeditaccountwidget.h"
+#include "meanwhileaccount.h"
+#include <kgenericfactory.h>
+#include "kopeteaccountmanager.h"
+#include "kopeteglobal.h"
+#include "kopeteonlinestatusmanager.h"
+
+#include "mw_common.h"
+
+typedef KGenericFactory<MeanwhileProtocol> MeanwhileProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY(kopete_meanwhile,
+ MeanwhileProtocolFactory("kopete_meanwhile"))
+
+MeanwhileProtocol::MeanwhileProtocol(QObject* parent, const char *name,
+ const QStringList &/*args*/)
+: Kopete::Protocol(MeanwhileProtocolFactory::instance(), parent, name),
+
+ statusOffline(Kopete::OnlineStatus::Offline, 25, this, 0, QString::null,
+ i18n("Offline"), i18n("Offline"),
+ Kopete::OnlineStatusManager::Offline,
+ Kopete::OnlineStatusManager::DisabledIfOffline),
+
+ statusOnline(Kopete::OnlineStatus::Online, 25, this, mwStatus_ACTIVE,
+ QString::null, i18n("Online"), i18n("Online"),
+ Kopete::OnlineStatusManager::Online, 0),
+
+ statusAway(Kopete::OnlineStatus::Away, 20, this, mwStatus_AWAY,
+ "meanwhile_away", i18n("Away"), i18n("Away"),
+ Kopete::OnlineStatusManager::Away,
+ Kopete::OnlineStatusManager::HasAwayMessage),
+
+ statusBusy(Kopete::OnlineStatus::Away, 25, this, mwStatus_BUSY,
+ "meanwhile_dnd", i18n("Busy"), i18n("Busy"),
+ Kopete::OnlineStatusManager::Busy,
+ Kopete::OnlineStatusManager::HasAwayMessage),
+
+ statusIdle(Kopete::OnlineStatus::Away, 30, this, mwStatus_AWAY,
+ "meanwhile_idle", i18n("Idle"), i18n("Idle"),
+ Kopete::OnlineStatusManager::Idle, 0),
+
+ statusAccountOffline(Kopete::OnlineStatus::Offline, 0, this, 0,
+ QString::null, i18n("Account Offline")),
+
+ statusMessage(QString::fromLatin1("statusMessage"),
+ i18n("Status Message"), QString::null, false, true),
+
+ awayMessage(Kopete::Global::Properties::self()->awayMessage())
+{
+ HERE;
+
+ addAddressBookField("messaging/meanwhile", Kopete::Plugin::MakeIndexField);
+}
+
+MeanwhileProtocol::~MeanwhileProtocol()
+{
+}
+
+AddContactPage * MeanwhileProtocol::createAddContactWidget(QWidget *parent,
+ Kopete::Account *account )
+{
+ return new MeanwhileAddContactPage(parent, account);
+}
+
+KopeteEditAccountWidget * MeanwhileProtocol::createEditAccountWidget(
+ Kopete::Account *account, QWidget *parent )
+{
+ return new MeanwhileEditAccountWidget(parent, account, this);
+}
+
+Kopete::Account *MeanwhileProtocol::createNewAccount(const QString &accountId)
+{
+ return new MeanwhileAccount(this, accountId, accountId.ascii());
+}
+
+Kopete::Contact *MeanwhileProtocol::deserializeContact(
+ Kopete::MetaContact *metaContact,
+ const QMap<QString,
+ QString> &serializedData,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ QString contactId = serializedData[ "contactId" ];
+ QString accountId = serializedData[ "accountId" ];
+
+ MeanwhileAccount *theAccount =
+ static_cast<MeanwhileAccount*>(
+ Kopete::AccountManager::self()->
+ findAccount(pluginId(), accountId));
+
+ if(!theAccount)
+ {
+ return 0;
+ }
+
+ theAccount->addContact(contactId, metaContact, Kopete::Account::DontChangeKABC);
+ return theAccount->contacts()[contactId];
+}
+
+const Kopete::OnlineStatus MeanwhileProtocol::accountOfflineStatus()
+{
+ return statusAccountOffline;
+}
+
+const Kopete::OnlineStatus MeanwhileProtocol::lookupStatus(
+ enum Kopete::OnlineStatusManager::Categories cats)
+{
+ return Kopete::OnlineStatusManager::self()->onlineStatus(this, cats);
+}
+#include "meanwhileprotocol.moc"
diff --git a/kopete/protocols/meanwhile/meanwhileprotocol.h b/kopete/protocols/meanwhile/meanwhileprotocol.h
new file mode 100644
index 00000000..aa7a9861
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhileprotocol.h
@@ -0,0 +1,77 @@
+/*
+ meanwhileprotocl.h - the meanwhile protocol definition
+
+ Copyright (c) 2005 by Jeremy Kerr <jk@ozlabs.org>
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILEPROTOCOL_H
+#define MEANWHILEPROTOCOL_H
+
+#include <kopeteprotocol.h>
+
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "kopeteonlinestatus.h"
+#include "kopeteonlinestatusmanager.h"
+#include "addcontactpage.h"
+
+#include <kdebug.h>
+#define MEANWHILE_DEBUG 14200
+#define HERE kdDebug(MEANWHILE_DEBUG) << k_funcinfo << endl
+#define mwDebug() kdDebug(MEANWHILE_DEBUG)
+
+class MeanwhileAccount;
+class MeanwhileEditAccountWidget;
+class MeanwhileAddContactPage;
+
+class MeanwhileProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+public:
+ MeanwhileProtocol(QObject *parent, const char *name,
+ const QStringList &args);
+
+ ~MeanwhileProtocol();
+
+ virtual AddContactPage *createAddContactWidget(QWidget *parent,
+ Kopete::Account *account);
+
+ virtual KopeteEditAccountWidget *createEditAccountWidget(
+ Kopete::Account *account, QWidget *parent);
+
+ virtual Kopete::Account *createNewAccount(const QString &accountId);
+
+ virtual Kopete::Contact *deserializeContact(
+ Kopete::MetaContact *metaContact,
+ const QMap<QString,QString> &serializedData,
+ const QMap<QString, QString> &addressBookData);
+
+ const Kopete::OnlineStatus accountOfflineStatus();
+
+ const Kopete::OnlineStatus lookupStatus(
+ enum Kopete::OnlineStatusManager::Categories cats);
+
+ const Kopete::OnlineStatus statusOffline;
+ const Kopete::OnlineStatus statusOnline;
+ const Kopete::OnlineStatus statusAway;
+ const Kopete::OnlineStatus statusBusy;
+ const Kopete::OnlineStatus statusIdle;
+ const Kopete::OnlineStatus statusAccountOffline;
+
+ Kopete::ContactPropertyTmpl statusMessage;
+ Kopete::ContactPropertyTmpl awayMessage;
+
+};
+
+#endif
diff --git a/kopete/protocols/meanwhile/meanwhilesession.cpp b/kopete/protocols/meanwhile/meanwhilesession.cpp
new file mode 100644
index 00000000..974ca4af
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhilesession.cpp
@@ -0,0 +1,965 @@
+/*
+ meanwhilesession.cpp - interface to the 'C' meanwhile library
+
+ Copyright (c) 2003-2004 by Sivaram Gottimukkala <suppandi@gmail.com>
+ Copyright (c) 2005 by Jeremy Kerr <jk@ozlabs.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include <kopetepassword.h>
+#include <kopetechatsession.h>
+#include <kopetegroup.h>
+#include <kopetecontactlist.h>
+#include "meanwhilesession.h"
+#include "meanwhileprotocol.h"
+
+#include <mw_channel.h>
+#include <mw_message.h>
+#include <mw_error.h>
+#include <mw_service.h>
+#include <mw_session.h>
+#include <mw_srvc_aware.h>
+#include <mw_srvc_conf.h>
+#include <mw_srvc_im.h>
+#include <mw_srvc_store.h>
+#include <mw_cipher.h>
+#include <mw_st_list.h>
+
+#define set_session_handler(a,b) sessionHandler.a = _handleSession ## b
+#define set_aware_handler(a,b) awareHandler.a = _handleAware ## b
+#define set_aware_list_handler(a,b) \
+ awareListHandler.a = _handleAwareList ## b
+#define set_im_handler(a,b) imHandler.a = _handleIm ## b
+
+#define get_protocol() (static_cast<MeanwhileProtocol *>(account->protocol()))
+
+static struct MeanwhileClientID ids[] = {
+ { mwLogin_LIB, "Lotus Binary Library" },
+ { mwLogin_JAVA_WEB, "Lotus Java Applet", },
+ { mwLogin_BINARY, "Lotus Binary App", },
+ { mwLogin_JAVA_APP, "Lotus Java App", },
+ { mwLogin_LINKS, "Sametime Links", },
+
+ { mwLogin_NOTES_6_5, "Notes 6.5", },
+ { mwLogin_NOTES_6_5_3, "Notes 6.5.3", },
+ { mwLogin_NOTES_7_0_beta, "Notes 7.0 beta", },
+ { mwLogin_NOTES_7_0, "Notes 7.0", },
+ { mwLogin_ICT, "ICT", },
+ { mwLogin_ICT_1_7_8_2, "ICT 1.7.8.2", },
+ { mwLogin_ICT_SIP, "ICT SIP", },
+ { mwLogin_NOTESBUDDY_4_14, "NotesBuddy 4.14", },
+ { mwLogin_NOTESBUDDY_4_15, "NotesBuddy 4.15" },
+ { mwLogin_NOTESBUDDY_4_16, "NotesBuddy 4.16" },
+ { mwLogin_SANITY, "Sanity", },
+ { mwLogin_ST_PERL, "ST Perl", },
+ { mwLogin_PMR_ALERT, "PMR Alert", },
+ { mwLogin_TRILLIAN, "Trillian", },
+ { mwLogin_TRILLIAN_IBM, "Trillian (IBM)", },
+ { mwLogin_MEANWHILE, "Meanwhile Library", },
+ { 0, NULL },
+};
+
+MeanwhileSession::MeanwhileSession(MeanwhileAccount *account)
+{
+ HERE;
+ this->account = account;
+ session = 0L;
+ socket = 0L;
+ state = mwSession_STOPPED;
+
+ /* set up main session hander */
+ memset(&sessionHandler, 0, sizeof(sessionHandler));
+ set_session_handler(io_write, IOWrite);
+ set_session_handler(io_close, IOClose);
+ set_session_handler(on_stateChange, StateChange);
+ set_session_handler(on_setPrivacyInfo, SetPrivacyInfo);
+ set_session_handler(on_setUserStatus, SetUserStatus);
+ set_session_handler(on_admin, Admin);
+ set_session_handler(on_announce, Announce);
+ set_session_handler(clear, Clear);
+
+ session = mwSession_new(&sessionHandler);
+ mwSession_setClientData(session, this, 0L);
+
+ /* set up the aware service */
+ memset(&awareHandler, 0, sizeof(awareHandler));
+ set_aware_handler(on_attrib, Attrib);
+
+ awareService = mwServiceAware_new(session, &awareHandler);
+ mwSession_addService(session, (struct mwService *)awareService);
+
+ /* create an aware list */
+ memset(&awareListHandler, 0, sizeof(awareListHandler));
+ set_aware_list_handler(on_aware, Aware);
+ set_aware_list_handler(on_attrib, Attrib);
+ awareList = mwAwareList_new(awareService, &awareListHandler);
+ mwAwareList_setClientData(awareList, this, 0L);
+
+ /* set up an im service */
+ memset(&imHandler, 0, sizeof(imHandler));
+ set_im_handler(conversation_opened, ConvOpened);
+ set_im_handler(conversation_closed, ConvClosed);
+ set_im_handler(conversation_recv, ConvReceived);
+ imHandler.place_invite = 0L;
+ imHandler.clear = 0L;
+
+ imService = mwServiceIm_new(session, &imHandler);
+ mwService_setClientData((struct mwService *)imService, this, 0L);
+ mwSession_addService(session, (struct mwService *) imService);
+
+ /* add resolve service */
+ resolveService = mwServiceResolve_new(session);
+ mwService_setClientData((struct mwService *)resolveService, this, 0L);
+ mwSession_addService(session, (struct mwService *) resolveService);
+
+ /* storage service */
+ storageService = mwServiceStorage_new(session);
+ mwService_setClientData((struct mwService *)storageService, this, 0L);
+ mwSession_addService(session, (struct mwService *) storageService);
+
+#if 0
+ /* conference service setup - just declines invites for now. */
+ memset(&conf_handler, 0, sizeof(conf_handler));
+ conf_handler.on_invited = _conference_invite;
+
+ srvc_conf = mwServiceConference_new(session, &conf_handler);
+ mwService_setClientData((struct mwService *)srvc_conf, this, 0L);
+ mwSession_addService(session, (struct mwService *) srvc_conf);
+#endif
+
+ /* add a necessary cipher */
+ mwSession_addCipher(session, mwCipher_new_RC2_40(session));
+ mwSession_addCipher(session, mwCipher_new_RC2_128(session));
+}
+
+MeanwhileSession::~MeanwhileSession()
+{
+ HERE;
+ if (isConnected() || isConnecting())
+ disconnect();
+
+ mwSession_removeService(session, mwService_STORAGE);
+ mwSession_removeService(session, mwService_RESOLVE);
+ mwSession_removeService(session, mwService_IM);
+ mwSession_removeService(session, mwService_AWARE);
+
+ mwAwareList_free(awareList);
+ mwService_free(MW_SERVICE(storageService));
+ mwService_free(MW_SERVICE(resolveService));
+ mwService_free(MW_SERVICE(imService));
+ mwService_free(MW_SERVICE(awareService));
+ mwCipher_free(mwSession_getCipher(session, mwCipher_RC2_40));
+ mwCipher_free(mwSession_getCipher(session, mwCipher_RC2_128));
+
+ mwSession_free(session);
+
+ if (socket)
+ delete socket;
+}
+
+void MeanwhileSession::getDefaultClientIDParams(int *clientID,
+ int *verMajor, int *verMinor)
+{
+ *clientID = mwLogin_MEANWHILE;
+ *verMajor = MW_PROTOCOL_VERSION_MAJOR;
+ *verMinor = MW_PROTOCOL_VERSION_MINOR;
+}
+
+/* external interface called by meanwhileaccount */
+void MeanwhileSession::connect(QString password)
+{
+ int port, clientID, versionMajor, versionMinor;
+ bool useCustomID;
+ QString host;
+
+ HERE;
+
+ host = account->getServerName();
+ port = account->getServerPort();
+ useCustomID = account->getClientIDParams(&clientID,
+ &versionMajor, &versionMinor);
+
+ KExtendedSocket *sock = new KExtendedSocket(host, port,
+ KExtendedSocket::bufferedSocket);
+
+ if (sock->connect()) {
+ KMessageBox::queuedMessageBox(0, KMessageBox::Error,
+ i18n( "Could not connect to server"), i18n("Meanwhile Plugin"),
+ KMessageBox::Notify);
+ delete sock;
+ return;
+ }
+ socket = sock;
+ /* we want to receive signals when there is data to read */
+ sock->enableRead(true);
+ QObject::connect(sock, SIGNAL(readyRead()), this,
+ SLOT(slotSocketDataAvailable()));
+ QObject::connect(sock, SIGNAL(closed(int)), this,
+ SLOT(slotSocketClosed(int)));
+
+ /* set login details */
+ mwSession_setProperty(session, mwSession_AUTH_USER_ID,
+ g_strdup(account->meanwhileId().ascii()), g_free);
+ mwSession_setProperty(session, mwSession_AUTH_PASSWORD,
+ g_strdup(password.ascii()), g_free);
+
+ /* set client type parameters */
+ if (useCustomID) {
+ mwSession_setProperty(session, mwSession_CLIENT_TYPE_ID,
+ GUINT_TO_POINTER(clientID), NULL);
+ mwSession_setProperty(session, mwSession_CLIENT_VER_MAJOR,
+ GUINT_TO_POINTER(versionMajor), NULL);
+ mwSession_setProperty(session, mwSession_CLIENT_VER_MINOR,
+ GUINT_TO_POINTER(versionMinor), NULL);
+ }
+
+ mwDebug() << "ids: "
+ << mwSession_getProperty(session, mwSession_CLIENT_TYPE_ID) << " v"
+ << mwSession_getProperty(session, mwSession_CLIENT_VER_MAJOR) << "."
+ << mwSession_getProperty(session, mwSession_CLIENT_VER_MINOR) <<
+ endl;
+
+ /* go!! */
+ mwSession_start(session);
+}
+
+void MeanwhileSession::disconnect()
+{
+ HERE;
+ if (state == mwSession_STOPPED || state == mwSession_STOPPING)
+ return;
+
+ mwSession_stop(session, ERR_SUCCESS);
+}
+
+bool MeanwhileSession::isConnected()
+{
+ return mwSession_isStarted(session);
+}
+
+bool MeanwhileSession::isConnecting()
+{
+ return mwSession_isStarting(session);
+}
+
+static void free_id_block(void *data, void *p)
+{
+ if (p != 0L || data == 0L)
+ return;
+ struct mwAwareIdBlock *id = (struct mwAwareIdBlock *)data;
+ free(id->user);
+ free(id);
+}
+
+void MeanwhileSession::addContacts(const QDict<Kopete::Contact>& contacts)
+{
+ HERE;
+ QDictIterator<Kopete::Contact> it(contacts);
+ GList *buddies = 0L;
+
+ /** Convert our QDict of kopete contact to a GList of meanwhile buddies */
+ for( ; it.current(); ++it) {
+ MeanwhileContact *contact =
+ static_cast<MeanwhileContact *>(it.current());
+ struct mwAwareIdBlock *id = (struct mwAwareIdBlock *)
+ malloc(sizeof(*id));
+ if (id == 0L)
+ continue;
+ id->user = strdup(contact->meanwhileId().ascii());
+ id->community = 0L;
+ id->type = mwAware_USER;
+ buddies = g_list_append(buddies, id);
+ }
+
+ mwAwareList_addAware(awareList, buddies);
+
+ g_list_foreach(buddies, free_id_block, 0L);
+ g_list_free(buddies);
+}
+
+/* private functions used only by the meanwhile session object */
+void MeanwhileSession::addContact(const Kopete::Contact *contact)
+{
+ HERE;
+ struct mwAwareIdBlock id = { mwAware_USER,
+ strdup(static_cast<const MeanwhileContact *>(contact)
+ ->meanwhileId().ascii()),
+ 0L };
+
+ GList *buddies = g_list_prepend(0L, &id);
+ mwAwareList_addAware(awareList, buddies);
+ g_list_free(buddies);
+ free(id.user);
+}
+
+int MeanwhileSession::sendMessage(Kopete::Message &message)
+{
+ HERE;
+ MeanwhileContact *contact =
+ static_cast<MeanwhileContact *>(message.to().first());
+ if (!contact) {
+ mwDebug() << "No target for message!" <<endl;
+ return 0;
+ }
+
+ struct mwIdBlock target = { strdup(contact->meanwhileId().ascii()), 0L };
+ struct mwConversation *conv;
+
+ conv = mwServiceIm_getConversation(imService, &target);
+ free(target.user);
+ if (conv == 0L) {
+ mwDebug() << "No target for conversation with '"
+ << contact->meanwhileId() << "'" << endl;
+ return 0;
+ }
+
+ struct ConversationData *convdata = (struct ConversationData *)
+ mwConversation_getClientData(conv);
+
+ if (convdata == 0L) {
+ convdata = createConversationData(conv, contact, true);
+ if (convdata == 0L) {
+ mwDebug() << "No memory for conversation data!" << endl;
+ return 0;
+ }
+ }
+
+ /* if there's other messages in the queue, or the conversation isn't open,
+ * then append to the queue instead of sending right away */
+ if ((convdata->queue && !convdata->queue->isEmpty()) ||
+ !mwConversation_isOpen(conv)) {
+ convdata->queue->append(message);
+ mwConversation_open(conv);
+
+ } else if (!mwConversation_send(conv, mwImSend_PLAIN,
+ message.plainBody().ascii())) {
+ convdata->chat->appendMessage(message);
+ convdata->chat->messageSucceeded();
+ }
+ return 1;
+}
+
+void MeanwhileSession::sendTyping(MeanwhileContact *contact, bool isTyping)
+{
+ HERE;
+ struct mwIdBlock target = { strdup(contact->meanwhileId().ascii()), 0L };
+ struct mwConversation *conv;
+
+ conv = mwServiceIm_getConversation(imService, &target);
+ free(target.user);
+ if (conv == 0L)
+ return;
+
+ if (mwConversation_isOpen(conv))
+ mwConversation_send(conv, mwImSend_TYPING, (void *)isTyping);
+}
+
+void MeanwhileSession::setStatus(Kopete::OnlineStatus status,
+ const QString msg)
+{
+ HERE;
+ mwDebug() << "setStatus: " << status.description() << "("
+ << status.internalStatus() << ")" << endl;
+ if (status.internalStatus() == 0)
+ return;
+
+ struct mwUserStatus stat;
+ mwUserStatus_clone(&stat, mwSession_getUserStatus(session));
+
+ free(stat.desc);
+
+ stat.status = (mwStatusType)status.internalStatus();
+ if (msg.isNull() || msg.isEmpty())
+ stat.desc = strdup(status.description().ascii());
+ else
+ stat.desc = strdup(msg.ascii());
+
+ mwSession_setUserStatus(session, &stat);
+ /* will free stat.desc */
+ mwUserStatus_clear(&stat);
+}
+
+void MeanwhileSession::syncContactsToServer()
+{
+ HERE;
+ struct mwSametimeList *list = mwSametimeList_new();
+
+ /* set up a fallback group for top-level contacts */
+ struct mwSametimeGroup *topstgroup = mwSametimeGroup_new(list,
+ mwSametimeGroup_DYNAMIC, "People");
+ mwSametimeGroup_setOpen(topstgroup, true);
+
+ QDictIterator<Kopete::Contact> it(account->contacts());
+ for( ; it.current(); ++it ) {
+ MeanwhileContact *contact =
+ static_cast<MeanwhileContact *>(it.current());
+
+ /* Find the group that the metacontact is in */
+ Kopete::MetaContact *mc = contact->metaContact();
+ /* myself doesn't have a metacontact */
+ if (mc == 0L)
+ continue;
+
+ Kopete::Group *contactgroup = mc->groups().getFirst();
+ if (contactgroup == 0L)
+ continue;
+
+ if (contactgroup->type() == Kopete::Group::Temporary)
+ continue;
+
+ struct mwSametimeGroup *stgroup;
+ if (contactgroup->type() == Kopete::Group::TopLevel) {
+ stgroup = topstgroup;
+ } else {
+ /* find (or create) a matching sametime list group */
+ stgroup = mwSametimeList_findGroup(list,
+ contactgroup->displayName().ascii());
+ if (stgroup == 0L) {
+ stgroup = mwSametimeGroup_new(list, mwSametimeGroup_DYNAMIC,
+ contactgroup->displayName().ascii());
+ }
+ mwSametimeGroup_setOpen(stgroup, contactgroup->isExpanded());
+ mwSametimeGroup_setAlias(stgroup,
+ contactgroup->pluginData(account->protocol(), "alias")
+ .ascii());
+ }
+
+ /* now add the user (by IDBlock) */
+ struct mwIdBlock id =
+ { (gchar*)contact->meanwhileId().ascii(), 0L };
+ struct mwSametimeUser *stuser = mwSametimeUser_new(stgroup,
+ mwSametimeUser_NORMAL, &id);
+
+ mwSametimeUser_setAlias(stuser, contact->nickName().ascii());
+ }
+
+ /* store! */
+ struct mwPutBuffer *buf = mwPutBuffer_new();
+ struct mwStorageUnit *unit = mwStorageUnit_new(mwStore_AWARE_LIST);
+ struct mwOpaque *opaque = mwStorageUnit_asOpaque(unit);
+
+ mwSametimeList_put(buf, list);
+ mwPutBuffer_finalize(opaque, buf);
+
+ mwServiceStorage_save(storageService, unit, NULL, NULL, NULL);
+
+ mwSametimeList_free(list);
+}
+
+void MeanwhileSession::syncContactsFromServer()
+{
+ struct mwStorageUnit *unit = mwStorageUnit_new(mwStore_AWARE_LIST);
+ mwServiceStorage_load(storageService, unit, &_handleStorageLoad, 0L, 0L);
+}
+
+#define MEANWHILE_SESSION_BUFSIZ 4096
+
+void MeanwhileSession::slotSocketDataAvailable()
+{
+ HERE;
+ guchar *buf;
+ Q_LONG bytesRead;
+
+ if (socket == 0L)
+ return;
+
+ if (!(buf = (guchar *)malloc(MEANWHILE_SESSION_BUFSIZ))) {
+ mwDebug() << "buffer malloc failed" << endl;
+ return;
+ }
+
+ while (socket && socket->bytesAvailable() > 0) {
+ bytesRead = socket->readBlock((char *)buf, MEANWHILE_SESSION_BUFSIZ);
+ if (bytesRead < 0)
+ break;
+ mwSession_recv(session, buf, (unsigned int)bytesRead);
+ }
+ free(buf);
+}
+
+void MeanwhileSession::slotSocketClosed(int reason)
+{
+ HERE;
+
+ if (reason & KExtendedSocket::involuntary)
+ emit serverNotification(
+ QString("Lost connection with Meanwhile server"));
+
+ if (socket) {
+ delete socket;
+ socket = 0L;
+ }
+
+ mwSession_stop(session, 0x00);
+}
+
+
+Kopete::OnlineStatus MeanwhileSession::convertStatus(int mstatus)
+{
+ MeanwhileProtocol *protocol =
+ static_cast<MeanwhileProtocol *>(account->protocol());
+
+ switch (mstatus) {
+ case mwStatus_ACTIVE:
+ return protocol->statusOnline;
+ break;
+ case mwStatus_IDLE:
+ return protocol->statusIdle;
+ break;
+ case mwStatus_AWAY:
+ return protocol->statusAway;
+ break;
+ case mwStatus_BUSY:
+ return protocol->statusBusy;
+ break;
+ case 0:
+ return protocol->statusOffline;
+ break;
+ default:
+ mwDebug() << "unknown status lookup: " << mstatus << endl;
+ }
+ return protocol->statusOffline;
+}
+
+void MeanwhileSession::resolveContactNickname(MeanwhileContact *contact)
+{
+ /* @todo: FIXME: leak! */
+ char *id = strdup(contact->meanwhileId().ascii());
+ GList *query = g_list_prepend(NULL, id);
+ mwServiceResolve_resolve(resolveService, query, mwResolveFlag_USERS,
+ _handleResolveLookupResults, contact, NULL);
+}
+
+QString MeanwhileSession::getNickName(struct mwLoginInfo *logininfo)
+{
+ if (logininfo == 0L || logininfo->user_name == 0L)
+ return QString::null;
+ return getNickName(logininfo->user_name);
+}
+
+QString MeanwhileSession::getNickName(QString name)
+{
+
+ int index = name.find(" - ");
+ if (index != -1)
+ name = name.remove(0, index + 3);
+ index = name.find('/');
+ if (index != -1)
+ name = name.left(index);
+
+ return name;
+}
+
+MeanwhileContact *MeanwhileSession::conversationContact(
+ struct mwConversation *conv)
+{
+ struct mwIdBlock *target = mwConversation_getTarget(conv);
+ if (target == 0L || target->user == 0L) {
+ return 0L;
+ }
+ QString user(target->user);
+
+ MeanwhileContact *contact =
+ static_cast<MeanwhileContact *>(account->contacts()[user]);
+
+ struct mwLoginInfo *logininfo = mwConversation_getTargetInfo(conv);
+ QString name = getNickName(logininfo);
+
+ if (!contact) {
+ account->addContact(user, name, 0L, Kopete::Account::Temporary);
+ contact = static_cast<MeanwhileContact *>(account->contacts()[user]);
+ } else
+ contact->setNickName(name);
+
+ return contact;
+}
+
+/* priave session handling functions, called by libmeanwhile callbacks */
+void MeanwhileSession::handleSessionStateChange(
+ enum mwSessionState state, gpointer data)
+{
+ HERE;
+ this->state = state;
+
+ switch (state) {
+ case mwSession_STARTING:
+ case mwSession_HANDSHAKE:
+ case mwSession_HANDSHAKE_ACK:
+ case mwSession_LOGIN:
+ case mwSession_LOGIN_REDIR:
+ case mwSession_LOGIN_CONT:
+ case mwSession_LOGIN_ACK:
+ break;
+
+ case mwSession_STARTED:
+ {
+ struct mwUserStatus stat = { mwStatus_ACTIVE, 0, 0L };
+ mwSession_setUserStatus(session, &stat);
+ struct mwLoginInfo *logininfo = mwSession_getLoginInfo(session);
+ if (logininfo) {
+ account->myself()->setNickName(getNickName(logininfo));
+ }
+ syncContactsFromServer();
+ }
+ break;
+
+ case mwSession_STOPPING:
+ {
+ unsigned int info = GPOINTER_TO_UINT(data);
+ if (info & ERR_FAILURE) {
+ if (info == INCORRECT_LOGIN)
+ account->password().setWrong();
+ char *reason = mwError(info);
+ emit serverNotification(QString(reason));
+ free(reason);
+ }
+ }
+
+ emit sessionStateChange(
+ static_cast<MeanwhileProtocol *>(account->protocol())
+ ->statusOffline);
+ break;
+
+ case mwSession_STOPPED:
+ break;
+
+ case mwSession_UNKNOWN:
+ default:
+ mwDebug() << "Unhandled state change " << state << endl;
+ }
+}
+
+int MeanwhileSession::handleSessionIOWrite(const guchar *buffer,
+ gsize count)
+{
+ HERE;
+
+ if (socket == 0L)
+ return 1;
+
+ int remaining, retval = 0;
+ for (remaining = count; remaining > 0; remaining -= retval) {
+ retval = socket->writeBlock((char *)buffer, count);
+ if (retval <= 0)
+ return 1;
+ }
+ socket->flush();
+ return 0;
+}
+
+void MeanwhileSession::handleSessionAdmin(const char *text)
+{
+ HERE;
+ emit serverNotification(QString(text));
+}
+
+void MeanwhileSession::handleSessionAnnounce(struct mwLoginInfo *from,
+ gboolean /* may_reply */, const char *text)
+{
+ HERE;
+ QString message;
+ message.sprintf("Announcement from %s:\n%s", from->user_id, text);
+ emit serverNotification(message);
+}
+
+void MeanwhileSession::handleSessionSetUserStatus()
+{
+ struct mwUserStatus *userstatus = mwSession_getUserStatus(session);
+ emit sessionStateChange(convertStatus((unsigned int)userstatus->status));
+}
+
+void MeanwhileSession::handleSessionSetPrivacyInfo()
+{
+}
+
+void MeanwhileSession::handleSessionIOClose()
+{
+ HERE;
+
+ if (socket == 0L)
+ return;
+
+ QObject::disconnect(socket, SIGNAL(closed(int)),
+ this, SLOT(slotSocketClosed(int)));
+ socket->flush();
+ socket->closeNow();
+
+ delete socket;
+ socket = 0L;
+}
+
+void MeanwhileSession::handleSessionClear()
+{
+}
+
+void MeanwhileSession::handleAwareAttrib(struct mwAwareAttribute * /* attrib */)
+{
+ HERE;
+}
+
+void MeanwhileSession::handleAwareListAware(struct mwAwareSnapshot *snapshot)
+{
+ HERE;
+ MeanwhileContact *contact = static_cast<MeanwhileContact *>
+ (account->contacts()[snapshot->id.user]);
+
+ if (contact == 0L)
+ return;
+
+ /* use the setUserStatus callback for status updates for myself. */
+ if (contact == account->myself())
+ return;
+
+ contact->setProperty(get_protocol()->statusMessage, snapshot->status.desc);
+ contact->setProperty(get_protocol()->awayMessage, snapshot->status.desc);
+
+ Kopete::OnlineStatus onlinestatus;
+ if (snapshot->online) {
+ onlinestatus = convertStatus(snapshot->status.status);
+ resolveContactNickname(contact);
+ } else {
+ onlinestatus = convertStatus(0);
+ }
+
+ contact->setOnlineStatus(onlinestatus);
+
+#if 0
+ /* Commented out in previous kopete/meanwhile plugin for some reason,
+ * but has still been ported to the new API.
+ */
+ time_t idletime = 0;
+ if (snapshot->status.status == mwStatus_IDLE) {
+ idletime = (snapshot->status.time == 0xdeadbeef) ?
+ 0 : snapshot->status.time;
+ if (idletime != 0) {
+ contact->setStatusDescription(statusDesc + "[" +
+ QString::number(idletime/60)+" mins]");
+ }
+ } else
+ contact->setStatusDescription(snapshot->status.desc);
+#endif
+}
+
+void MeanwhileSession::handleAwareListAttrib(struct mwAwareIdBlock * /* id */,
+ struct mwAwareAttribute * /* attrib */)
+{
+ HERE;
+}
+
+struct MeanwhileSession::ConversationData
+ *MeanwhileSession::createConversationData(
+ struct mwConversation *conv, MeanwhileContact *contact,
+ bool createQueue)
+{
+ struct ConversationData *cd = new ConversationData();
+
+ if (cd == 0L)
+ return 0L;
+
+ cd->contact = contact;
+ cd->chat = contact->manager(Kopete::Contact::CanCreate);
+ cd->chat->ref();
+ if (createQueue)
+ cd->queue = new QValueList<Kopete::Message>();
+
+ mwConversation_setClientData(conv, cd, 0L);
+
+ return cd;
+}
+
+void MeanwhileSession::handleImConvOpened(struct mwConversation *conv)
+{
+ HERE;
+
+ struct ConversationData *convdata =
+ (struct ConversationData *)mwConversation_getClientData(conv);
+
+ if (convdata == 0L) {
+ /* a new conversation */
+ convdata = createConversationData(conv, conversationContact(conv));
+
+ if (convdata == 0L) {
+ mwDebug() << "No memory for conversation data!" << endl;
+ return;
+ }
+
+ } else if (convdata->queue && !convdata->queue->isEmpty()) {
+ /* send any messages that were waiting for the conversation to open */
+ QValueList<Kopete::Message>::iterator it;
+ for (it = convdata->queue->begin(); it != convdata->queue->end();
+ ++it) {
+ mwConversation_send(conv, mwImSend_PLAIN,
+ (*it).plainBody().ascii());
+ convdata->chat->appendMessage(*it);
+ convdata->chat->messageSucceeded();
+ }
+ convdata->queue->clear();
+ delete convdata->queue;
+ convdata->queue = 0L;
+ }
+ resolveContactNickname(convdata->contact);
+}
+
+void MeanwhileSession::handleImConvClosed(struct mwConversation *conv,
+ guint32)
+{
+ HERE;
+
+ ConversationData *convdata =
+ (ConversationData *)mwConversation_getClientData(conv);
+
+ if (!convdata)
+ return;
+
+ mwConversation_setClientData(conv, 0L, 0L);
+
+ convdata->chat->removeContact(convdata->contact);
+ convdata->chat->deref();
+ convdata->chat = 0L;
+ if (convdata->queue != 0L) {
+ convdata->queue->clear();
+ delete convdata->queue;
+ convdata->queue = 0L;
+ }
+ free(convdata);
+}
+
+void MeanwhileSession::handleImConvReceived(struct mwConversation *conv,
+ enum mwImSendType type, gconstpointer msg)
+{
+ HERE;
+ ConversationData *convdata =
+ (ConversationData *)mwConversation_getClientData(conv);
+
+ if (!convdata)
+ return;
+
+ switch (type) {
+ case mwImSend_PLAIN:
+ {
+ Kopete::Message message(convdata->contact, account->myself(),
+ QString((char *)msg), Kopete::Message::Inbound);
+ convdata->chat->appendMessage(message);
+ }
+ break;
+ case mwImSend_TYPING:
+ convdata->chat->receivedTypingMsg(convdata->contact);
+ break;
+ default:
+ mwDebug() << "Unable to handle message type: " << type << endl;
+ }
+}
+
+void MeanwhileSession::handleResolveLookupResults(
+ struct mwServiceResolve * /* srvc */, guint32 /* id */,
+ guint32 /* code */, GList *results, gpointer data)
+{
+ struct mwResolveResult *result;
+ struct mwResolveMatch *match;
+
+ if (results == 0L)
+ return;
+ if ((result = (struct mwResolveResult *)results->data) == 0L)
+ return;
+
+ if (result->matches == 0L)
+ return;
+ if ((match = (struct mwResolveMatch *)result->matches->data) == 0L)
+ return;
+
+ mwDebug() << "resolve lookup returned '" << match->name << "'" << endl;
+
+ MeanwhileContact *contact = (MeanwhileContact *)data;
+ if (contact == 0L)
+ return;
+
+ contact->setNickName(getNickName(match->name));
+}
+
+void MeanwhileSession::handleStorageLoad(struct mwServiceStorage * /* srvc */,
+ guint32 result, struct mwStorageUnit *item, gpointer /* data */)
+{
+ HERE;
+ if (result != ERR_SUCCESS) {
+ mwDebug() << "contact list load returned " << result << endl;
+ return;
+ }
+
+ struct mwGetBuffer *buf = mwGetBuffer_wrap(mwStorageUnit_asOpaque(item));
+ struct mwSametimeList *list = mwSametimeList_new();
+ mwSametimeList_get(buf, list);
+
+ GList *gl, *glf, *cl, *clf;
+
+ Kopete::ContactList *contactlist = Kopete::ContactList::self();
+
+ for (glf = gl = mwSametimeList_getGroups(list); gl; gl = gl->next) {
+ struct mwSametimeGroup *stgroup = (struct mwSametimeGroup *)gl->data;
+
+ Kopete::Group *group =
+ contactlist->findGroup(mwSametimeGroup_getName(stgroup));
+ group->setPluginData(account->protocol(), "alias",
+ mwSametimeGroup_getAlias(stgroup));
+
+ for (clf = cl = mwSametimeGroup_getUsers(stgroup); cl; cl = cl->next) {
+ struct mwSametimeUser *stuser = (struct mwSametimeUser *)cl->data;
+
+ MeanwhileContact *contact = static_cast<MeanwhileContact *>
+ (account->contacts()[mwSametimeUser_getUser(stuser)]);
+
+ if (contact != 0L)
+ continue;
+
+ account->addContact(mwSametimeUser_getUser(stuser),
+ mwSametimeUser_getAlias(stuser), group,
+ Kopete::Account::ChangeKABC);
+ }
+ g_list_free(clf);
+ }
+ g_list_free(glf);
+
+ mwSametimeList_free(list);
+}
+
+const struct MeanwhileClientID *MeanwhileSession::getClientIDs()
+{
+ return ids;
+}
+
+#if 0
+MEANWHILE_HOOK_CONFERENCE(conference_invite,
+ (struct mwConference *conf, struct mwLoginInfo *inviter,
+ const char *invite),
+ (conf, inviter, invite))
+{
+ HERE;
+ QString message;
+
+ message.sprintf("%s has invited you to a conference called \"%s\"\n"
+ "However, this version of the meanwhile plugin does "
+ "not support conferences, so the invitiation has been declined.",
+ inviter->user_id, invite);
+
+ mwConference_reject(conf, ERR_SUCCESS,
+ "Sorry, my client doesn't support conferences!");
+ KMessageBox::queuedMessageBox(0, KMessageBox::Sorry , message,
+ i18n("Meanwhile Plugin: Conference invitation"),
+ KMessageBox::Notify);
+}
+#endif
+#include "meanwhilesession.moc"
diff --git a/kopete/protocols/meanwhile/meanwhilesession.h b/kopete/protocols/meanwhile/meanwhilesession.h
new file mode 100644
index 00000000..0e2a3558
--- /dev/null
+++ b/kopete/protocols/meanwhile/meanwhilesession.h
@@ -0,0 +1,350 @@
+/*
+ meanwhilesession.h - interface to the 'C' meanwhile session
+
+ Copyright (c) 2005 by Jeremy Kerr <jk@ozlabs.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MEANWHILESESSION_H
+#define MEANWHILESESSION_H
+
+#include "meanwhileaccount.h"
+#include "meanwhilecontact.h"
+#include <kextendedsocket.h>
+
+#include <mw_session.h>
+#include <mw_service.h>
+#include <mw_srvc_aware.h>
+#include <mw_srvc_im.h>
+#include <mw_srvc_resolve.h>
+
+struct MeanwhileClientID {
+ int id;
+ const char *name;
+};
+
+/**
+ * A class to handle libmeanwhile session management.
+ */
+class MeanwhileSession : public QObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a session. By default, the session is not connected - you will
+ * need to call login() to initiate the connection process.
+ * @param account The account that the connection is for
+ */
+ MeanwhileSession(MeanwhileAccount *account);
+
+ /**
+ * Destroy the session
+ */
+ ~MeanwhileSession();
+
+ /**
+ * Connect to the server. This will open a socket and start login. Note that
+ * the connection process is ascychronous - a loginDone() signal will be
+ * emitted when sucessfully logged in.
+ */
+ void connect(QString password);
+
+ /**
+ * Disconnect from the server.
+ */
+ void disconnect();
+
+ /**
+ * Set our (the local contact's) online status. The internalStatus of the
+ * state argument will be used to define the state message we send - it
+ * should be one of the Status enum fields (and not Offline)
+ * @param state the new state of the local user
+ * @param msg a custom message to use, if required
+ */
+ void setStatus(Kopete::OnlineStatus status,
+ const QString msg = QString::null);
+
+ /**
+ * Add a single contact to be registered for status updates
+ * @param contact The contact to register
+ */
+ void addContact(const Kopete::Contact *contact);
+
+ /**
+ * Add a list of contacts to be registered for status updates
+ * @param contact The list of contacts to register
+ */
+ void addContacts(const QDict<Kopete::Contact>& contacts);
+
+ /**
+ * Send a message (with recipient specified).
+ * @param message The message to send
+ * @return non-zero if the message could be sent
+ */
+ int sendMessage(Kopete::Message &message);
+
+ /**
+ * Send a typing notification to a contact
+ * @param contact The contact to notify
+ * @param isTyping If true, the typing notification is set
+ */
+ void sendTyping(MeanwhileContact *contact, bool isTyping);
+
+ /**
+ * Determine if the session is connected to the server
+ * @return true if the session is connected
+ */
+ bool isConnected();
+
+ /**
+ * Determine if the session is in the process of connecting to the server
+ * @return true if the session is connecting
+ */
+ bool isConnecting();
+
+ static const struct MeanwhileClientID *getClientIDs();
+
+ static void getDefaultClientIDParams(int *clientID,
+ int *verMajor, int *verMinor);
+signals:
+ /**
+ * Emitted when the status of the connection changes
+ * @param status The new status of the session
+ */
+ void sessionStateChange(Kopete::OnlineStatus status);
+
+ /**
+ * Emitted when a notification is received from the server, or other
+ * out-of-band data (eg, the password is incorrect).
+ * @param mesgString A description of the notification
+ */
+ void serverNotification(const QString &mesgString);
+
+private:
+ /** Main libmeanwhile session object */
+ struct mwSession *session;
+
+ /** Session handler */
+ struct mwSessionHandler sessionHandler;
+
+ /** Aware service */
+ struct mwServiceAware *awareService;
+
+ /** Aware handler */
+ struct mwAwareHandler awareHandler;
+
+ /** Aware List Handler */
+ struct mwAwareListHandler awareListHandler;
+
+ /** The aware list */
+ struct mwAwareList *awareList;
+
+ /** Aware service */
+ struct mwServiceIm *imService;
+
+ /** Aware handler */
+ struct mwImHandler imHandler;
+
+ /** Resolve service */
+ struct mwServiceResolve *resolveService;
+
+ /** Storage service, for contact list */
+ struct mwServiceStorage *storageService;
+
+ /** Last recorded meanwhile state */
+ enum mwSessionState state;
+
+ /** The kopete account that this library is for */
+ MeanwhileAccount *account;
+
+ /** socket to the server */
+ KExtendedSocket *socket;
+
+ /* These structures are stored in the libmeanwhile 'ClientData' fields */
+
+ /** Stored in the mwConversation struct */
+ struct ConversationData {
+ MeanwhileContact *contact;
+ Kopete::ChatSession *chat;
+ QValueList<Kopete::Message> *queue;
+ };
+
+ /** (To be) stored in the mwConference struct */
+ struct ConferenceData {
+ Kopete::ChatSession *chatsession;
+ };
+
+ /**
+ * Initialise the conversation data struct for a conversation, and store it
+ * in the meanwhile conversation object
+ * @param conv the meanwhile conversation object
+ * @param contact the contact that the conversation is with
+ * @param createQueue whether a message queue is required for this
+ * conversation
+ * @return The created conversation data struct
+ */
+ struct ConversationData *createConversationData(
+ struct mwConversation *conv, MeanwhileContact *contact,
+ bool createQueue = false);
+
+ /**
+ * Get the contact for a conversation
+ * @param conv the meanwhile conversation
+ * @return the contact that this conversation is held with
+ */
+ MeanwhileContact *conversationContact(struct mwConversation *conv);
+
+ /**
+ * Convert a libmeanwhile-type status into one of the MeanwhileProtocol
+ * statuses
+ * @param mstatus The internal status to convert
+ * @return The Meanwhile status
+ */
+ Kopete::OnlineStatus convertStatus(int mstatus);
+
+ /**
+ * Parse the nickname of a libmeanwhile contact. From what I've seen,
+ * usernames are in the format:
+ * <userid> - <name>/<domain>/<domain>
+ * @param name the extened username to parse
+ * @return just the name part of the username info
+ */
+ QString getNickName(QString name);
+
+ /**
+ * Convenience method to call the above from a mwLoginInfo struct. All is
+ * checked for null.
+ * @param logininfo the login info for a contact
+ * @return just the name part of the login info data
+ */
+ QString getNickName(struct mwLoginInfo *logininfo);
+
+ /**
+ * Resolve a contact to find (and set) the display name. This requires the
+ * session to be connected to use the meanwhile resolve service.
+ * @param contact The contact to resolve
+ */
+ void resolveContactNickname(MeanwhileContact *contact);
+
+public:
+ void syncContactsToServer();
+ void syncContactsFromServer();
+
+private slots:
+
+ /** Notify the library that data is available on the socket */
+ void slotSocketDataAvailable();
+
+ /**
+ * Notify the library that the socket has been closed
+ * @param reason the reason for closing
+ */
+ void slotSocketClosed(int reason);
+
+private:
+ /* ugly callbacks for libmeanwhile interface. These declare a static method
+ * to proxy the callback from libmeanwhile to a call to the MeanwhileSession
+ */
+
+#define declare_session_handler_type(type, func, args, ...) \
+ static type _handleSession ## func ( \
+ struct mwSession *mwsession, __VA_ARGS__) { \
+ MeanwhileSession *session = \
+ (MeanwhileSession *)mwSession_getClientData(mwsession); \
+ return session->handleSession ## func args; \
+ }; \
+ type handleSession ## func(__VA_ARGS__)
+#define declare_session_handler(func, args, ...) \
+ static void _handleSession ## func ( \
+ struct mwSession *mwsession, ## __VA_ARGS__) { \
+ MeanwhileSession *session = \
+ (MeanwhileSession *)mwSession_getClientData(mwsession); \
+ session->handleSession ## func args; \
+ }; \
+ void handleSession ## func(__VA_ARGS__)
+
+ declare_session_handler_type(int, IOWrite, (buf, len),
+ const guchar *buf, gsize len);
+ declare_session_handler(IOClose,());
+ declare_session_handler(Clear,());
+ declare_session_handler(StateChange, (state, info),
+ enum mwSessionState state, gpointer info);
+ declare_session_handler(SetPrivacyInfo,());
+ declare_session_handler(SetUserStatus,());
+ declare_session_handler(Admin, (text), const char *text);
+ declare_session_handler(Announce, (from, may_reply, text),
+ struct mwLoginInfo *from, gboolean may_reply, const char *text);
+
+#define declare_aware_handler(func, args, ...) \
+ static void _handleAware ## func ( \
+ struct mwServiceAware *srvc, ## __VA_ARGS__) { \
+ MeanwhileSession *session = (MeanwhileSession *) \
+ mwService_getClientData((struct mwService *)srvc); \
+ return session->handleAware ## func args; \
+ }; \
+ void handleAware ## func(__VA_ARGS__)
+
+ declare_aware_handler(Attrib, (attrib), struct mwAwareAttribute *attrib);
+ declare_aware_handler(Clear,());
+
+#define declare_aware_list_handler(func, args, ...) \
+ static void _handleAwareList ## func ( \
+ struct mwAwareList *list, ## __VA_ARGS__){ \
+ MeanwhileSession *session = (MeanwhileSession *) \
+ mwAwareList_getClientData(list); \
+ return session->handleAwareList ## func args; \
+ }; \
+ void handleAwareList ## func(__VA_ARGS__)
+
+ declare_aware_list_handler(Aware, (snapshot),
+ struct mwAwareSnapshot *snapshot);
+ declare_aware_list_handler(Attrib, (id, attrib),
+ struct mwAwareIdBlock *id, struct mwAwareAttribute *attrib);
+ declare_aware_list_handler(Clear,());
+
+#define declare_im_handler(func, args, ...) \
+ static void _handleIm ## func ( \
+ struct mwConversation *conv, ## __VA_ARGS__) { \
+ MeanwhileSession *session = (MeanwhileSession *) \
+ mwService_getClientData( \
+ (struct mwService *)mwConversation_getService(conv)); \
+ return session->handleIm ## func args; \
+ }; \
+ void handleIm ## func (struct mwConversation *conv, ## __VA_ARGS__)
+
+ declare_im_handler(ConvOpened, (conv));
+ declare_im_handler(ConvClosed, (conv, err), guint32 err);
+ declare_im_handler(ConvReceived, (conv, type, msg),
+ enum mwImSendType type, gconstpointer msg);
+
+ /* resolve service */
+ static void _handleResolveLookupResults(struct mwServiceResolve *srvc,
+ guint32 id, guint32 code, GList *results, gpointer data) {
+ MeanwhileSession *session = (MeanwhileSession *)
+ mwService_getClientData(MW_SERVICE(srvc));
+ session->handleResolveLookupResults(srvc, id, code, results, data);
+ };
+ void handleResolveLookupResults(struct mwServiceResolve *srvc, guint32 id,
+ guint32 code, GList *results, gpointer data);
+
+ /* storage service */
+ static void _handleStorageLoad(struct mwServiceStorage *srvc,
+ guint32 result, struct mwStorageUnit *item, gpointer data) {
+ MeanwhileSession *session = (MeanwhileSession *)
+ mwService_getClientData(MW_SERVICE(srvc));
+ session->handleStorageLoad(srvc, result, item, data);
+ };
+ void handleStorageLoad(struct mwServiceStorage *srvc,
+ guint32 result, struct mwStorageUnit *item, gpointer data);
+};
+
+#endif
+
diff --git a/kopete/protocols/meanwhile/ui/Makefile.am b/kopete/protocols/meanwhile/ui/Makefile.am
new file mode 100644
index 00000000..466f68b2
--- /dev/null
+++ b/kopete/protocols/meanwhile/ui/Makefile.am
@@ -0,0 +1,8 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkopetemeanwhileui.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+libkopetemeanwhileui_la_SOURCES = empty.cpp meanwhileeditaccountbase.ui meanwhileaddcontactbase.ui
diff --git a/kopete/protocols/meanwhile/ui/empty.cpp b/kopete/protocols/meanwhile/ui/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/meanwhile/ui/empty.cpp
diff --git a/kopete/protocols/meanwhile/ui/meanwhileaddcontactbase.ui b/kopete/protocols/meanwhile/ui/meanwhileaddcontactbase.ui
new file mode 100644
index 00000000..d146a8ed
--- /dev/null
+++ b/kopete/protocols/meanwhile/ui/meanwhileaddcontactbase.ui
@@ -0,0 +1,111 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>MeanwhileAddContactBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>396</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Add Sametime Contact</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout53</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;Userid:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>contactID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user id of the contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user id of the contact you would like to add.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>contactID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user id of the contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user id of the contact you would like to add.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnFindUser</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Find</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Find Userid</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Find Userid</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: johndoe)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </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>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/meanwhile/ui/meanwhileeditaccountbase.ui b/kopete/protocols/meanwhile/ui/meanwhileeditaccountbase.ui
new file mode 100644
index 00000000..2f160039
--- /dev/null
+++ b/kopete/protocols/meanwhile/ui/meanwhileeditaccountbase.ui
@@ -0,0 +1,437 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>MeanwhileEditAccountBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MeanwhileEditAccountBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>640</width>
+ <height>450</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Edit Meanwhile Account</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </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>groupBox53</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>layout81</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>label1</cstring>
+ </property>
+ <property name="text">
+ <string>Meanwhile &amp;username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mScreenName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your Sametime userid</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Your Sametime userid</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mScreenName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Your Sametime userid</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Your Sametime userid</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget">
+ <property name="name">
+ <cstring>mPasswordWidget</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mAutoConnect</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>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Connection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox54</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout56</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mServerName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostname of the Sametime server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostname of the Sametime server you wish to connect to.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mServerName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostname of the Sametime server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostname of the Sametime server you wish to connect to.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout57</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblPort</cstring>
+ </property>
+ <property name="text">
+ <string>Po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mServerPort</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the Sametime server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the Sametime server that you would like to connect to. Usually this is 1533.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mServerPort</cstring>
+ </property>
+ <property name="maxValue">
+ <number>65534</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>1533</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the Sametime server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the Sametime server that you would like to connect to. Usually this is 1533.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Client Identifier</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkCustomClientID</cstring>
+ </property>
+ <property name="text">
+ <string>Use custom client identifier</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>mClientID</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblClientIdentifier</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Client identifier</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mClientID</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mClientVersionMajor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblVersionSeparator</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>.</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>mClientVersionMinor</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblClientVersion</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Client version (major.minor)</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mClientVersionMajor</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnServerDefaults</cstring>
+ </property>
+ <property name="text">
+ <string>Restore &amp;Defaults</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Restore the server and port values to their defaults.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Restore the server and port values to their defaults.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</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>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ </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>
+<connections>
+ <connection>
+ <sender>chkCustomClientID</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mClientID</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomClientID</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mClientVersionMajor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomClientID</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mClientVersionMinor</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomClientID</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblClientIdentifier</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>chkCustomClientID</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblClientVersion</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>mScreenName</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kopetepasswordwidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/msn/Changelog b/kopete/protocols/msn/Changelog
new file mode 100644
index 00000000..80f52264
--- /dev/null
+++ b/kopete/protocols/msn/Changelog
@@ -0,0 +1,106 @@
+Have fun using this all-improved plugin and feel free to contribute patches
+and other improvements to our mailing list! Although we all like to boast
+about our great work, we're sure there are still bugs remaining, which is
+why we don't call this release 1.0, but only 0.5.
+
+Nevertheless, we feel this new MSN plugin is an enormous step forward from
+the last 0.4.1 release and we recommend anyone to try out this all-improved
+plugin. Please read the release notes first before reporting bugs, but please
+do report anything not listed there!
+
+Thanks for your interest in Kopete!
+
+ October 2002, the Kopete team <kopete-devel@kde.org>
+
+
+CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.4.1
+
+- Ported the plugin to the new MetaContact API, allowing a locally cached
+ copy of the contact list to be always available (even when offline) and
+ to combine your MSN contacts with other messaging systems in one entry
+ in the contact list.
+
+- Added additional online states ('be right back', 'out to lunch', 'busy',
+ 'invisible') and the possibility to connect directly with a particular
+ status (especially useful with 'invisible')
+
+- Fix multi-user chat now the API finally supports it properly
+
+- Fix a grave bug in Kopete 0.4.1 where Kopete would popup the 'new user'
+ dialog for every user in your block list, asking whether you want to
+ allow or block the user, often crashing Kopete completely
+
+- Fix support for Unicode messages
+
+- Fix the 'unhandled error 219' problem that caused Kopete to disconnect
+ unexpectedly for some people
+
+- Added possibility to talk with users who aren't in the contact list
+
+- Incoming filetransfers
+
+- As usual, several other bugfixes
+
+CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.4
+
+- Added block/unblock user
+
+- Don't show contacts from the allow list if they are not also in the
+ friend list (like deleted contacts). Small problem: there already was
+ a need to have a gui for manipulating blocked/allowed contacts, with
+ this change this is even a bit more urgent...
+
+- Hopefully fix a problem with an empty reverse list on a fresh MSN account.
+ can't test, because by the time the recompile was done the reverse list
+ was no longer empty...
+
+- Fix a problem with MSN users no longer receiving messages. Apparently
+ Microsoft changed the server so messages without an explicit font name are
+ no longer passed on.
+
+- Fixed UTF8 handling not really being UTF8. MSN should work fine now with
+ all unicode characters
+
+- Moved the plugin to use KGenericFactory as preparation for more KDE-style
+ plugin handling (as opposed to the current custom code)
+
+- Fixed crash when disconnecting while an earlier connect was still running
+
+- Made the connect code asynchronous, so connecting doesn't hang kopete
+ while processing
+
+- Fixed minor memory leak in the connect code
+
+CHANGES IN THE MSN PLUGIN SINCE KOPETE 0.3
+
+Many things changed since 0.3. I won't mention them all, because so much of
+the internal code changed that the individual commits often fix more than I
+was even aware of at that time. Below are the bigger changes and fixes:
+
+- Ported the plugin to the new KopeteMessageManager. This move unifies the
+ handling of various resources like chat windows, balloons, system tray
+ flashing, and more. In Kopete 0.3 this was the exclusive domain of the
+ ICQ plugin, in this release all plugins except IRC already use the shared
+ code.
+
+- Rewrote almost all of the internal protocol handling, fixing an awful lot
+ of bugs during the process. The main goal was to make the code more
+ maintainable and extensible, but the gratuitous bug fixes are of course
+ much more useful for most people. The most important fix of all is a
+ grave bug that caused the plugin to read a fixed-size 1kb buffer in Kopete
+ 0.3 without checking for additional data, often causing the plugin to
+ seemingly 'hang'.
+
+- Added the ability to change the display name while connected. This can
+ currently only be done from the context menu. The option in the
+ preferences never worked, and still does not do what you'd expect it to
+ do. Sorry :)
+
+- Added much more useful debug code for developers, testers and other
+ interested people. It is also a lot *more* debug output, so if you're
+ scared of console output, better not start Kopete from it...
+
+- All those tiny bugfixes of which I don't even know whether they fix
+ regressions introduced during the development of version 0.4, or whether
+ they fix long-standing bugs.
+
diff --git a/kopete/protocols/msn/Makefile.am b/kopete/protocols/msn/Makefile.am
new file mode 100644
index 00000000..ffecef3c
--- /dev/null
+++ b/kopete/protocols/msn/Makefile.am
@@ -0,0 +1,46 @@
+if include_msn_webcam
+WEBCAM = webcam
+WEBCAM_LIBMINICWRAPPER = webcam/libmimicwrapper.la
+endif
+
+KDE_OPTIONS = nofinal
+METASOURCES = AUTO
+SUBDIRS = ui $(WEBCAM) . icons config
+AM_CPPFLAGS = -Iui \
+ -I$(srcdir)/webcam \
+ -I$(srcdir)/ui \
+ $(KOPETE_INCLUDES) \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_msn.la
+lib_LTLIBRARIES = libkopete_msn_shared.la
+
+CLEANFILES = dummy.cpp
+
+libkopete_msn_shared_la_SOURCES = msnprotocol.cpp msnaccount.cpp \
+ msnaddcontactpage.cpp msncontact.cpp msnsocket.cpp msnchatsession.cpp msndebugrawcmddlg.cpp \
+ msnnotifysocket.cpp msnswitchboardsocket.cpp msnfiletransfersocket.cpp msninvitation.cpp \
+ sha1.cpp msnsecureloginhandler.cpp msnchallengehandler.cpp dispatcher.cpp \
+ p2p.cpp messageformatter.cpp incomingtransfer.cpp outgoingtransfer.cpp \
+ webcam.cpp
+
+libkopete_msn_shared_la_LIBADD = ./ui/libkopetemsnui.la ../../libkopete/libkopete.la $(WEBCAM_LIBMINICWRAPPER) ../../libkopete/avdevice/libkopete_videodevice.la $(LIB_KIO)
+libkopete_msn_shared_la_LDFLAGS = -version-info 0:0:0 -no-undefined $(all_libraries)
+
+kopete_msn_la_SOURCES = dummy.cpp webcam.cpp
+kopete_msn_la_LIBADD = libkopete_msn_shared.la
+
+kopete_msn_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+
+dummy.cpp: $(srcdir)/Makefile.am
+ echo '#include "kdemacros.h"' > $@
+ echo 'extern "C" KDE_EXPORT void *init_libkopete_msn_shared();' >> $@
+ echo 'extern "C" KDE_EXPORT void *init_kopete_msn() { return init_libkopete_msn_shared(); }' >> $@
+
+service_DATA = kopete_msn.desktop
+servicedir = $(kde_servicesdir)
+
+mydatadir = $(kde_datadir)/kopete_msn
+mydata_DATA = msnchatui.rc
+noinst_HEADERS = p2p.h dispatcher.h messageformatter.h incomingtransfer.h \
+ outgoingtransfer.h webcam.h
diff --git a/kopete/protocols/msn/ReleaseNotes b/kopete/protocols/msn/ReleaseNotes
new file mode 100644
index 00000000..3a2b2f6a
--- /dev/null
+++ b/kopete/protocols/msn/ReleaseNotes
@@ -0,0 +1,31 @@
+If you can bear with beta-quality code, we welcome you to try out this
+highly improved plugin and use it as your primary instant messaging system!
+Patches are always welcome on kopete-devel@kde.org. Trivial fixes can be
+committed directly to KDE CVS if you have an account, but please coordinate
+larger changes with us to avoid double work.
+
+Thanks for your interest in Kopete!
+
+ October 2002, the Kopete team <kopete-devel@kde.org>
+
+RELEASE NOTES FOR THE MSN PLUGIN IN KOPETE 0.5
+
+Most of the MSN plugin has seen an enormous improvement since the previous
+release, with many bugs fixed in all aspects of the plugin, most notably
+the contact list handling and group chats. See the Changelog for a more
+detailed description of what was changed in this release.
+
+Some issues in MSN are still remaining though. We hope to fix them in the
+next release, since we think they are not critical enough to delay this
+release:
+
+- MSN allows you to have multiple groups with the same name, because
+ internally a group ID is used. Kopete currently uses the name as a unique
+ identifier, however, and will likely get a bit confused by this. If you
+ do experience problems, you could join both groups using another MSN
+ client, like the official client, Trillian or Gaim as a workaround.
+
+- Kopete contacts can be at Top-Level and in no groups. MSN doesn't
+ support this freature. The kopete's contact list can differe from server
+ if you have top-level contact
+
diff --git a/kopete/protocols/msn/TODO b/kopete/protocols/msn/TODO
new file mode 100644
index 00000000..be73e830
--- /dev/null
+++ b/kopete/protocols/msn/TODO
@@ -0,0 +1,5 @@
+Some things to think about as of 2003/04/15
+(according to Gof)
+Adding 'search for user'
+Adding MSN chatroom protocol
+Add MSN alerts
diff --git a/kopete/protocols/msn/config/Makefile.am b/kopete/protocols/msn/config/Makefile.am
new file mode 100644
index 00000000..ab29db48
--- /dev/null
+++ b/kopete/protocols/msn/config/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_kopete_msn.la
+
+kcm_kopete_msn_la_SOURCES = msnprefs.ui msnpreferences.cpp
+kcm_kopete_msn_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kcm_kopete_msn_la_LIBADD = ../../../libkopete/libkopete.la $(LIB_KUTILS)
+
+service_DATA = kopete_msn_config.desktop
+servicedir = $(kde_servicesdir)/kconfiguredialog
+
diff --git a/kopete/protocols/msn/config/kopete_msn_config.desktop b/kopete/protocols/msn/config/kopete_msn_config.desktop
new file mode 100644
index 00000000..2af4da08
--- /dev/null
+++ b/kopete/protocols/msn/config/kopete_msn_config.desktop
@@ -0,0 +1,123 @@
+[Desktop Entry]
+Icon=msn_protocol
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=kopete_msn
+X-KDE-FactoryName=MSNProtocolConfigFactory
+X-KDE-ParentApp=kopete_msn
+X-KDE-ParentComponents=kopete_msn
+
+Name=MSN Plugin
+Name[ar]=توصيلة MSN
+Name[be]=Модуль MSN
+Name[bg]=MSN
+Name[bn]=à¦à¦®à¦à¦¸à¦à¦¨ পà§à¦²à¦¾à¦—িন
+Name[br]=Lugent MSN
+Name[bs]=MSN dodatak
+Name[ca]=Connector de MSN
+Name[cs]=MSN modul
+Name[cy]=Ategyn MSN
+Name[da]=MSN-Plugin
+Name[de]=MSN-Modul
+Name[el]=ΠÏόσθετο MSN
+Name[es]=Complemento de MSN
+Name[et]=MSN plugin
+Name[eu]=MSN Plugin-a
+Name[fa]=وصلۀ ام‌اس‌ان
+Name[fi]=MSN-liitännäinen
+Name[fr]=Module MSN
+Name[ga]=Breiseán MSN
+Name[gl]=Plugin para MSN
+Name[he]=תוסף MSN
+Name[hi]=à¤à¤®à¤à¤¸à¤à¤¨ पà¥à¤²à¤—इन
+Name[hr]=MSN umetak
+Name[hu]=MSN modul
+Name[is]=MSN íforrit
+Name[it]=Plugin MSN
+Name[ja]=MSN プラグイン
+Name[ka]=MSN მáƒáƒ“ული
+Name[kk]=MSN плагин модулі
+Name[km]=កម្មវិធី​ជំនួយ MSN
+Name[lt]=MSN įskiepis
+Name[mk]=MSN-приклучок
+Name[nb]=MSN programtillegg
+Name[nds]=MSN-Moduul
+Name[ne]=à¤à¤®à¤à¤¸à¤à¤¨ पà¥à¤²à¤—इन
+Name[nl]=MSN-plugin
+Name[nn]=MSN-programtillegg
+Name[pa]=MSN ਪਲੱਗਇਨ
+Name[pl]=Wtyczka MSN
+Name[pt]='Plugin' MSN
+Name[pt_BR]=Plug-in MSN
+Name[ro]=Modul MSN
+Name[ru]=Модуль MSN
+Name[se]=MSN-lassemoduvla
+Name[sk]=Modul MSN
+Name[sl]=Vstavek za MSN
+Name[sr]=MSN прикључак
+Name[sr@Latn]=MSN prikljuÄak
+Name[sv]=MSN-insticksprogram
+Name[ta]=MSN செரà¯à®•à®²à¯
+Name[tg]=Модули MSN
+Name[tr]=MSN Eklentisi
+Name[uk]=Втулок MSN
+Name[uz]=MSN plagini
+Name[uz@cyrillic]=MSN плагини
+Name[wa]=Tchôke-divins MSN
+Name[zh_CN]=MSN æ’件
+Name[zh_HK]=MSN æ’件
+Name[zh_TW]=MSN 外掛程å¼
+Comment=Microsoft Network Protocol
+Comment[ar]=بروتوكول شبكة Microsoft
+Comment[be]=Пратакол Ñеткі Microsoft Network
+Comment[bg]=Протокол за връзка Ñ Microsoft Network
+Comment[bn]=মাইকà§à¦°à§‹à¦¸à¦«à§à¦Ÿ নেটওয়ারà§à¦• পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad rouedad Microsoft
+Comment[bs]=Microsoft Network protokol
+Comment[ca]=Protocol per a la xarxa de Microsoft
+Comment[cs]=Protokol sítě Microsoft
+Comment[cy]=Protocol Rhwydwaith Microsoft
+Comment[de]=Microsoft Netzwerk-Protokoll
+Comment[el]=ΠÏωτόκολλο Microsoft Network
+Comment[es]=Protocolo de red de Microsoft
+Comment[et]=Microsofti võrguprotokoll
+Comment[fa]=قرارداد شبکۀ میکروساÙت
+Comment[fi]=Microsoft Network -yhteyskäytäntö
+Comment[fr]=Protocole réseau Microsoft
+Comment[ga]=Prótacal Gréasáin Mhicrosoft
+Comment[gl]=Protocolo para a rede de Microsoft
+Comment[he]=תוסף חיבור לרשת מיקרוסופט
+Comment[hi]=माइकà¥à¤°à¥‹à¤¸à¥‰à¤«à¥à¤Ÿ नेटवरà¥à¤• पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Microsoft mrežni protokol
+Comment[hu]=Microsoft hálózati protokoll
+Comment[is]=Microsoft Network Protocol
+Comment[it]=Protocollo di rete Microsoft
+Comment[ja]=Microsoft ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ—ロトコル
+Comment[ka]=Microsoft ქსელის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Microsoft Network желі протоколы
+Comment[km]=ពិធីការ​បណ្ដាញ​ម៉ៃក្រូសូហ្វ
+Comment[lt]=Microsoft tinklo protokolas
+Comment[mk]=Мрежен протокол на Microsoft
+Comment[nds]=Microsoft-Nettwarkprotokoll
+Comment[ne]=माइकà¥à¤°à¥‹à¤¸à¤«à¥à¤Ÿ सञà¥à¤œà¤¾à¤² पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor Microsoft Network
+Comment[nn]=Microsoft Network-protokoll
+Comment[pl]=Protokół Microsoft Network
+Comment[pt]=Protocolo da Microsoft Network
+Comment[pt_BR]=Protocolo de Rede Microsoft
+Comment[ru]=Протокол Ñети Microsoft Network
+Comment[sl]=Protokol za povezavo na MSN
+Comment[sv]=Microsoft-nätverksprotokoll
+Comment[ta]=மைகà¯à®°à¯‹à®šà®¾à®ªà¯à®Ÿà¯ இணைய விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди Шабакаи Microsoft
+Comment[tr]=Microsoft Ağ Protokolü
+Comment[uk]=Мережний протокол Microsoft
+Comment[uz]=Microsoft tarmogʻi bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=Microsoft тармоғи билан алоқа ўрнатиш учун протокол
+Comment[wa]=Protocole pol rantoele da Microsoft
+Comment[zh_CN]=Microsoft Network åè®®
+Comment[zh_HK]=Microsoft 網絡通訊å”定
+Comment[zh_TW]=Microsoft 網路å”定
+
diff --git a/kopete/protocols/msn/config/msnpreferences.cpp b/kopete/protocols/msn/config/msnpreferences.cpp
new file mode 100644
index 00000000..b28c2ea3
--- /dev/null
+++ b/kopete/protocols/msn/config/msnpreferences.cpp
@@ -0,0 +1,33 @@
+/*
+ msnpreferences.cpp - MSN Preferences Widget
+
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kgenericfactory.h>
+#include "kcautoconfigmodule.h"
+#include "msnprefs.h"
+
+class MSNPreferences;
+
+typedef KGenericFactory<MSNPreferences> MSNProtocolConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_kopete_msn, MSNProtocolConfigFactory( "kcm_kopete_msn" ) )
+
+class MSNPreferences : public KCAutoConfigModule
+{
+public:
+ MSNPreferences( QWidget *parent = 0, const char * = 0, const QStringList &args = QStringList() ) : KCAutoConfigModule( MSNProtocolConfigFactory::instance(), parent, args )
+ {
+ setMainWidget( new msnPrefsUI( this ) , "MSN");
+ }
+};
diff --git a/kopete/protocols/msn/config/msnprefs.ui b/kopete/protocols/msn/config/msnprefs.ui
new file mode 100644
index 00000000..c9321a60
--- /dev/null
+++ b/kopete/protocols/msn/config/msnprefs.ui
@@ -0,0 +1,217 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>msnPrefsUI</class>
+<author>Duncan Mac-Vicar P.</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>msnPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>522</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>General</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame3_3_3_2_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>NotifyNewChat</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Automatically open a chat window when someone starts a conversation</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>AutoDownloadPicture</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Automatically download the display picture if possible</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>useCustomEmoticons</cstring>
+ </property>
+ <property name="text">
+ <string>Download and show custom emoticons (experimental)</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_3_3_3</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel3_2_2_2_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Away Messages</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame3_3_3_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>SendAwayMessages</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Send &amp;away messages</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout18</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Do not send more than one away message every</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>AwayMessageSeconds</cstring>
+ </property>
+ <property name="value">
+ <number>90</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>seconds</string>
+ </property>
+ </widget>
+ </hbox>
+ </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>21</width>
+ <height>70</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>SendAwayMessages</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>AwayMessageSeconds</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>NotifyNewChat</tabstop>
+ <tabstop>AutoDownloadPicture</tabstop>
+ <tabstop>useCustomEmoticons</tabstop>
+ <tabstop>SendAwayMessages</tabstop>
+ <tabstop>AwayMessageSeconds</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">knuminput.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/msn/dispatcher.cpp b/kopete/protocols/msn/dispatcher.cpp
new file mode 100644
index 00000000..8b8bbed6
--- /dev/null
+++ b/kopete/protocols/msn/dispatcher.cpp
@@ -0,0 +1,647 @@
+/*
+ dispatcher.cpp - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 "dispatcher.h"
+#include "incomingtransfer.h"
+#include "outgoingtransfer.h"
+
+#if MSN_WEBCAM
+#include "webcam.h"
+#endif
+
+using P2P::Dispatcher;
+using P2P::Message;
+using P2P::TransferContext;
+using P2P::IncomingTransfer;
+using P2P::OutgoingTransfer;
+
+#include "msnswitchboardsocket.h"
+
+// Kde includes
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+
+// Qt includes
+#include <qdatastream.h>
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <qtextstream.h>
+
+// Kopete includes
+#include <kopetechatsession.h> // Just for getting the contact
+#include <kopeteaccount.h>
+#include <kopetetransfermanager.h>
+
+#include <stdlib.h>
+
+Dispatcher::Dispatcher(QObject *parent, const QString& contact, const QStringList &ip)
+ : QObject(parent) , m_contact(contact) , m_callbackChannel(0l) , m_ip(ip)
+{}
+
+Dispatcher::~Dispatcher()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ if(m_callbackChannel)
+ {
+ delete m_callbackChannel;
+ m_callbackChannel = 0l;
+ }
+}
+
+void Dispatcher::detach(TransferContext* transfer)
+{
+ m_sessions.remove(transfer->m_sessionId);
+ transfer->deleteLater();
+}
+
+QString Dispatcher::localContact()
+{
+ return m_contact;
+}
+
+void Dispatcher::requestDisplayIcon(const QString& from, const QString& msnObject)
+{
+ Q_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
+ TransferContext* current =
+ new IncomingTransfer(from, this, sessionId);
+
+ current->m_branch = P2P::Uid::createUid();
+ current->m_callId = P2P::Uid::createUid();
+ current->setType(P2P::UserDisplayIcon);
+ current->m_object = msnObject;
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId, current);
+
+ kdDebug(14140) << k_funcinfo << "Requesting, " << msnObject << endl;
+
+ QString context = QString::fromUtf8(KCodecs::base64Encode(msnObject.utf8()));
+ // NOTE remove the \0 character automatically
+ // appended to a QCString.
+ context.replace("=", QString::null);
+ QString content =
+ "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n"
+ "SessionID: " + QString::number(sessionId) + "\r\n"
+ "AppID: 1\r\n"
+ "Context: " + context + "\r\n"
+ "\r\n";
+ // Send the sending client an invitation message.
+ current->sendMessage(INVITE, content);
+}
+
+void Dispatcher::sendFile(const QString& path, Q_INT64 fileSize, const QString& to)
+{
+ // Create a new transfer context that will handle
+ // the file transfer.
+ Q_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
+ TransferContext *current =
+ new OutgoingTransfer(to, this, sessionId);
+ current->m_branch = P2P::Uid::createUid();
+ current->m_callId = P2P::Uid::createUid();
+ current->setType(P2P::File);
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId, current);
+
+ // Set the transfer context file.
+ current->m_file = new QFile(path);
+ // Create the file context data.
+ QString context;
+
+ QByteArray header(638);
+ header.fill('\0');
+ QDataStream writer(header, IO_WriteOnly);
+ writer.setByteOrder(QDataStream::LittleEndian);
+
+ // Write the header length to the stream.
+ writer << (Q_INT32)638;
+ // Write client version to the stream.
+ writer << (Q_INT32)3;
+ // Write the file size to the stream.
+ writer << fileSize;
+ // Write the file transfer flag to the stream.
+ // TODO support file preview. For now disable file preview.
+ writer << (Q_INT32)1;
+ // Write the file name in utf-16 to the stream.
+ QTextStream ts(header, IO_WriteOnly);
+ ts.setEncoding(QTextStream::RawUnicode);
+ ts.device()->at(20);
+ ts << path.section('/', -1);
+ // NOTE Background Sharing base64 [540..569]
+ // TODO add support for background sharing.
+ // Write file exchange type to the stream.
+ // NOTE File - 0xFFFFFFFF
+ // NOTE Background Sharing - 0xFFFFFFFE
+ writer.device()->at(570);
+ writer << (Q_UINT32)0xFFFFFFFF;
+
+ // Encode the file context header to base64 encoding.
+ context = QString::fromUtf8(KCodecs::base64Encode(header));
+
+ // Send an INVITE message to the recipient.
+ QString content = "EUF-GUID: {5D3E02AB-6190-11D3-BBBB-00C04F795683}\r\n"
+ "SessionID: " + QString::number(sessionId) + "\r\n"
+ "AppID: 2\r\n"
+ "Context: " + context + "\r\n"
+ "\r\n";
+ current->sendMessage(INVITE, content);
+}
+
+void Dispatcher::sendImage(const QString& /*fileName*/, const QString& /*to*/)
+{
+// TODO kdDebug(14140) << k_funcinfo << endl;
+// QFile imageFile(fileName);
+// if(!imageFile.open(IO_ReadOnly))
+// {
+// kdDebug(14140) << k_funcinfo << "Error opening image file."
+// << endl;
+// return;
+// }
+//
+// OutgoingTransfer *outbound =
+// new OutgoingTransfer(to, this, 64);
+//
+// outbound->sendImage(imageFile.readAll());
+}
+
+#if MSN_WEBCAM
+void Dispatcher::startWebcam(const QString &/*myHandle*/, const QString &msgHandle, bool wantToReceive)
+{
+ Q_UINT32 sessionId = rand()%0xFFFFFF00 + 4;
+ Webcam::Who who= wantToReceive ? Webcam::wViewer : Webcam::wProducer;
+ TransferContext* current =
+ new Webcam(who, msgHandle, this, sessionId);
+
+ current->m_branch = P2P::Uid::createUid();
+ current->m_callId = P2P::Uid::createUid();
+ current->setType(P2P::WebcamType);
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId, current);
+
+ // {4BD96FC0-AB17-4425-A14A-439185962DC8} <- i want to show you my webcam
+ // {1C9AA97E-9C05-4583-A3BD-908A196F1E92} <- i want to see your webcam
+ QString GUID= (who==Webcam::wProducer) ? "4BD96FC0-AB17-4425-A14A-439185962DC8" : "1C9AA97E-9C05-4583-A3BD-908A196F1E92" ;
+
+ QString content="EUF-GUID: {"+GUID+"}\r\n"
+ "SessionID: "+ QString::number(sessionId)+"\r\n"
+ "AppID: 4\r\n"
+ "Context: ewBCADgAQgBFADcAMABEAEUALQBFADIAQwBBAC0ANAA0ADAAMAAtAEEARQAwADMALQA4ADgARgBGADgANQBCADkARgA0AEUAOAB9AA==\r\n\r\n";
+
+ // context is the base64 of the utf16 of {B8BE70DE-E2CA-4400-AE03-88FF85B9F4E8}
+
+ current->sendMessage( INVITE , content );
+}
+#endif
+
+
+
+void Dispatcher::slotReadMessage(const QString &from, const QByteArray& stream)
+{
+ P2P::Message receivedMessage =
+ m_messageFormatter.readMessage(stream);
+
+ receivedMessage.source = from;
+
+ if(receivedMessage.contentType == "application/x-msnmsgrp2p")
+ {
+ if((receivedMessage.header.dataSize == 0)/* && ((receivedMessage.header.flag & 0x02) == 0x02)*/)
+ {
+ TransferContext *current = 0l;
+ QMap<Q_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
+ for(; it != m_sessions.end(); it++)
+ {
+ if(receivedMessage.header.ackSessionIdentifier == it.data()->m_identifier){
+ current = it.data();
+ break;
+ }
+ }
+
+ if(current){
+ // Inform the transfer object of the acknowledge.
+ current->m_ackSessionIdentifier = receivedMessage.header.identifier;
+ current->m_ackUniqueIdentifier = receivedMessage.header.ackSessionIdentifier;
+ current->acknowledged();
+ }
+ else
+ {
+ kdDebug(14140) << k_funcinfo
+ << "no transfer context with identifier, "
+ << receivedMessage.header.ackSessionIdentifier
+ << endl;
+ }
+ return;
+ }
+
+ if(m_messageBuffer.contains(receivedMessage.header.identifier))
+ {
+ kdDebug(14140) << k_funcinfo
+ << QString("retrieving buffered messsage, %1").arg(receivedMessage.header.identifier)
+ << endl;
+
+ // The message was split, try to reconstruct the message
+ // with this received piece.
+ Message bufferedMessage = m_messageBuffer[receivedMessage.header.identifier];
+ // Remove the buffered message.
+ m_messageBuffer.remove(receivedMessage.header.identifier);
+
+ bufferedMessage.body.resize(bufferedMessage.body.size() + receivedMessage.header.dataSize);
+ for(Q_UINT32 i=0; i < receivedMessage.header.dataSize; i++){
+ // Add the remaining message data to the buffered message.
+ bufferedMessage.body[receivedMessage.header.dataOffset + i] = receivedMessage.body[i];
+ }
+ bufferedMessage.header.dataSize += receivedMessage.header.dataSize;
+ bufferedMessage.header.dataOffset = 0;
+
+ receivedMessage = bufferedMessage;
+ }
+
+ // Dispatch the received message.
+ dispatch(receivedMessage);
+ }
+}
+
+void Dispatcher::dispatch(const P2P::Message& message)
+
+{
+ TransferContext *messageHandler = 0l;
+
+ if(message.header.sessionId > 0)
+ {
+ if(m_sessions.contains(message.header.sessionId)){
+ messageHandler = m_sessions[message.header.sessionId];
+ }
+ }
+ else
+ {
+ QString body =
+ QCString(message.body.data(), message.header.dataSize);
+ QRegExp regex("SessionID: ([0-9]*)\r\n");
+ if(regex.search(body) > 0)
+ {
+ Q_UINT32 sessionId = regex.cap(1).toUInt();
+ if(m_sessions.contains(sessionId)){
+ // Retrieve the message handler associated with the specified session Id.
+ messageHandler = m_sessions[sessionId];
+ }
+ }
+ else
+ {
+ // Otherwise, try to retrieve the message handler
+ // based on the acknowlegded unique identifier.
+ if(m_sessions.contains(message.header.ackUniqueIdentifier)){
+ messageHandler =
+ m_sessions[message.header.ackUniqueIdentifier];
+ }
+
+ if(!messageHandler)
+ {
+ // If the message handler still has not been found,
+ // try to retrieve the handler based on the call id.
+ regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ QString callId = regex.cap(1);
+
+ TransferContext *current = 0l;
+ QMap<Q_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
+ for(; it != m_sessions.end(); it++)
+ {
+ current = it.data();
+ if(current->m_callId == callId){
+ messageHandler = current;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if(messageHandler){
+ // Process the received message using the
+ // retrieved registered handler.
+ messageHandler->m_ackSessionIdentifier = message.header.identifier;
+ messageHandler->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
+ messageHandler->processMessage(message);
+ }
+ else
+ {
+ // There are no objects registered, with the retrieved session Id,
+ // to handle the received message; default to this dispatcher.
+
+ if(message.header.totalDataSize > message.header.dataOffset + message.header.dataSize)
+ {
+ // The entire message has not been received;
+ // buffer the recevied portion of the original message.
+ kdDebug(14140) << k_funcinfo
+ << QString("Buffering messsage, %1").arg(message.header.identifier)
+ << endl;
+ m_messageBuffer.insert(message.header.identifier, message);
+ return;
+ }
+
+ QString body =
+ QCString(message.body.data(), message.header.dataSize);
+ kdDebug(14140) << k_funcinfo << "received, " << body << endl;
+
+ if(body.startsWith("INVITE"))
+ {
+ // Retrieve the branch, call id, and session id.
+ // These fields will be used later on in the p2p
+ // transaction.
+ QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ QString branch = regex.cap(1);
+ regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ QString callId = regex.cap(1);
+ regex = QRegExp("SessionID: ([0-9]*)\r\n");
+ regex.search(body);
+ QString sessionId = regex.cap(1);
+ // Retrieve the contact that requested the session.
+ regex = QRegExp("From: <msnmsgr:([^>]*)>");
+ regex.search(body);
+ QString from = regex.cap(1);
+ // Retrieve the application identifier which
+ // is used to determine what type of session
+ // is being requested.
+ regex = QRegExp("AppID: ([0-9]*)\r\n");
+ regex.search(body);
+ Q_UINT32 applicationId = regex.cap(1).toUInt();
+
+ if(applicationId == 1 || applicationId == 11 || applicationId == 12 )
+ { //the AppID is 12 since Messenger 7.5
+ // A contact has requested a session to download
+ // a display icon (User Display Icon or CustomEmotion).
+
+ regex = QRegExp("Context: ([0-9a-zA-Z+/=]*)");
+ regex.search(body);
+ QCString msnobj;
+
+ // Decode the msn object from base64 encoding.
+ KCodecs::base64Decode(regex.cap(1).utf8() , msnobj);
+ kdDebug(14140) << k_funcinfo << "Contact requested, "
+ << msnobj << endl;
+
+ // Create a new transfer context that will handle
+ // the user display icon transfer.
+ TransferContext *current =
+ new OutgoingTransfer(from, this, sessionId.toUInt());
+ current->m_branch = branch;
+ current->m_callId = callId;
+ current->setType(P2P::UserDisplayIcon);
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId.toUInt(), current);
+
+ // Determine the display icon being requested.
+ QString fileName = objectList.contains(msnobj)
+ ? objectList[msnobj]
+ : m_pictureUrl;
+ QFile *source = new QFile(fileName);
+ // Try to open the source file for reading.
+ // If an error occurs, send an internal
+ // error message to the recipient.
+ if(!source->open(IO_ReadOnly))
+ {
+ current->error();
+ return;
+ }
+
+ current->m_file = source;
+ // Acknowledge the session request.
+ current->acknowledge(message);
+
+ current->m_ackSessionIdentifier = message.header.identifier;
+ current->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
+ // Send a 200 OK message to the recipient.
+ QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
+ current->sendMessage(OK, content);
+ }
+ else if(applicationId == 2)
+ {
+ // A contact has requested a session to
+ // send a file.
+
+ kdDebug(14140) << k_funcinfo << "File transfer invitation." << endl;
+
+ // Create a new transfer context that will handle
+ // the file transfer.
+ TransferContext *transfer =
+ new IncomingTransfer(from, this, sessionId.toUInt());
+ transfer->m_branch = branch;
+ transfer->m_callId = callId;
+ transfer->setType(P2P::File);
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId.toUInt(), transfer);
+
+ regex = QRegExp("Context: ([0-9a-zA-Z+/=]*)");
+ regex.search(body);
+ QByteArray context;
+
+ // Decode the file context from base64 encoding.
+ KCodecs::base64Decode(regex.cap(1).utf8(), context);
+ QDataStream reader(context, IO_ReadOnly);
+ reader.setByteOrder(QDataStream::LittleEndian);
+ //Retrieve the file info from the context field.
+ // File Size [8..15] Int64
+ reader.device()->at(8);
+ Q_INT64 fileSize;
+ reader >> fileSize;
+ // Flag [15..18] Int32
+ // 0x00 File transfer with preview data.
+ // 0x01 File transfer without preview data.
+ // 0x02 Background sharing.
+ Q_INT32 flag;
+ reader >> flag;
+ kdDebug(14140) << flag << endl;
+ // FileName UTF16 (Unicode) [19..539]
+ QByteArray bytes(520);
+ reader.readRawBytes(bytes.data(), bytes.size());
+ QTextStream ts(bytes, IO_ReadOnly);
+ ts.setEncoding(QTextStream::Unicode);
+ QString fileName;
+ fileName = ts.readLine().utf8();
+
+ emit incomingTransfer(from, fileName, fileSize);
+
+ kdDebug(14140) <<
+ QString("%1, %2 bytes.").arg(fileName, QString::number(fileSize))
+ << endl
+ << endl;
+
+ // Get the contact that is sending the file.
+ Kopete::Contact *contact = getContactByAccountId(from);
+
+ if(contact)
+ {
+ // Acknowledge the file invitation message.
+ transfer->acknowledge(message);
+
+ transfer->m_ackSessionIdentifier = message.header.identifier;
+ transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
+
+ QObject::connect(Kopete::TransferManager::transferManager(), SIGNAL(accepted(Kopete::Transfer*, const QString&)), transfer, SLOT(slotTransferAccepted(Kopete::Transfer*, const QString&)));
+ QObject::connect(Kopete::TransferManager::transferManager(), SIGNAL(refused(const Kopete::FileTransferInfo&)), transfer, SLOT(slotTransferRefused(const Kopete::FileTransferInfo&)));
+
+ // Show the file transfer accept/decline dialog.
+ Kopete::TransferManager::transferManager()->askIncomingTransfer(contact, fileName, fileSize, QString::null, sessionId);
+ }
+ else
+ {
+ kdWarning(14140) << fileName << " from " << from
+ << " has failed; could not retrieve contact from contact list."
+ << endl;
+ transfer->m_ackSessionIdentifier = message.header.identifier;
+ transfer->m_ackUniqueIdentifier = message.header.ackSessionIdentifier;
+ transfer->sendMessage(ERROR);
+ }
+ }
+ else if(applicationId == 4)
+ {
+#if MSN_WEBCAM
+ regex = QRegExp("EUF-GUID: \\{([0-9a-zA-Z\\-]*)\\}");
+ regex.search(body);
+ QString GUID=regex.cap(1);
+
+ kdDebug(14140) << k_funcinfo << "webcam " << GUID << endl;
+
+ Webcam::Who who;
+ if(GUID=="4BD96FC0-AB17-4425-A14A-439185962DC8")
+ { //that mean "I want to send MY webcam"
+ who=Webcam::wViewer;
+ }
+ else if(GUID=="1C9AA97E-9C05-4583-A3BD-908A196F1E92")
+ { //that mean "I want YOU to send YOUR webcam"
+ who=Webcam::wProducer;
+ }
+ else
+ { //unknown GUID
+ //current->error();
+ kdWarning(14140) << k_funcinfo << "Unknown GUID " << GUID << endl;
+ return;
+ }
+
+ TransferContext *current = new P2P::Webcam(who, from, this, sessionId.toUInt());
+ current->m_branch = branch;
+ current->m_callId = callId;
+
+ // Add the transfer to the list.
+ m_sessions.insert(sessionId.toUInt(), current);
+ // Acknowledge the session request.
+ current->acknowledge(message);
+ QTimer::singleShot(0,current, SLOT(askIncommingInvitation()) );
+#endif
+ }
+ }
+ else if(message.header.sessionId == 64)
+ {
+ // A contact has sent an inkformat (handwriting) gif.
+ // NOTE The entire message body is UTF16 encoded.
+ QString body = "";
+ for (Q_UINT32 i=0; i < message.header.totalDataSize; i++){
+ if (message.body[i] != QChar('\0')){
+ body += QChar(message.body[i]);
+ }
+ }
+
+ QRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)");
+ regex.search(body);
+ QString contentType = regex.cap(1);
+
+ if(contentType == "image/gif")
+ {
+ IncomingTransfer transfer(message.source, this, message.header.sessionId);
+ transfer.acknowledge(message);
+
+ regex = QRegExp("base64:([0-9a-zA-Z+/=]*)");
+ regex.search(body);
+ QString base64 = regex.cap(1);
+ QByteArray image;
+// Convert from base64 encoding to byte array.
+ KCodecs::base64Decode(base64.utf8(), image);
+// Create a temporary file to store the image data.
+ KTempFile *ink = new KTempFile(locateLocal("tmp", "inkformatgif-" ), ".gif");
+ ink->setAutoDelete(true);
+// Save the image data to disk.
+ ink->file()->writeBlock(image);
+ ink->file()->close();
+ displayIconReceived(ink, "inkformatgif");
+ ink = 0l;
+ }
+ }
+ }
+}
+
+void Dispatcher::messageAcknowledged(unsigned int correlationId, bool fullReceive)
+{
+ if(fullReceive)
+ {
+ TransferContext *current = 0l;
+ QMap<Q_UINT32, TransferContext*>::Iterator it = m_sessions.begin();
+ for(; it != m_sessions.end(); it++)
+ {
+ current = it.data();
+ if(current->m_transactionId == correlationId)
+ {
+ // Inform the transfer object of the acknowledge.
+ current->readyWrite();
+ break;
+ }
+ }
+ }
+}
+
+Kopete::Contact* Dispatcher::getContactByAccountId(const QString& accountId)
+{
+ Kopete::Contact *contact = 0l;
+ if(parent())
+ {
+ // Retrieve the contact from the current chat session context.
+ Kopete::ChatSession *session = dynamic_cast<Kopete::ChatSession*>(parent()->parent());
+ if(session)
+ {
+ contact = session->account()->contacts()[accountId];
+ session->setCanBeDeleted(false);
+ }
+ }
+ return contact;
+}
+
+Dispatcher::CallbackChannel::CallbackChannel(MSNSwitchBoardSocket *switchboard)
+{
+ m_switchboard = switchboard;
+}
+
+Dispatcher::CallbackChannel::~CallbackChannel()
+{}
+
+Q_UINT32 Dispatcher::CallbackChannel::send(const QByteArray& stream)
+{
+ return m_switchboard->sendCommand("MSG", "D", true, stream, true);
+}
+
+Dispatcher::CallbackChannel* Dispatcher::callbackChannel()
+{
+ if(m_callbackChannel == 0l){
+ MSNSwitchBoardSocket *callback = dynamic_cast<MSNSwitchBoardSocket *>(parent());
+ if(callback == 0l) return 0l;
+ m_callbackChannel = new Dispatcher::CallbackChannel(callback);
+ }
+
+ return m_callbackChannel;
+}
+
+#include "dispatcher.moc"
diff --git a/kopete/protocols/msn/dispatcher.h b/kopete/protocols/msn/dispatcher.h
new file mode 100644
index 00000000..56bd1856
--- /dev/null
+++ b/kopete/protocols/msn/dispatcher.h
@@ -0,0 +1,107 @@
+/*
+ dispatcher.h - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 DISPATCHER_H
+#define DISPATCHER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+#include "kopete_export.h"
+
+#include "p2p.h"
+#include "messageformatter.h"
+#include "incomingtransfer.h"
+#include "outgoingtransfer.h"
+
+
+namespace Kopete { class Contact; }
+class MSNSwitchBoardSocket;
+
+/**
+@author Kopete Developers
+*/
+namespace P2P{
+ class IncomingTransfer;
+ class OutgoingTransfer;
+
+ class KOPETE_EXPORT Dispatcher : public QObject
+ { Q_OBJECT
+ public:
+ Dispatcher(QObject *parent, const QString& contact, const QStringList &ip);
+ ~Dispatcher();
+
+ void detach(TransferContext* transfer);
+ QString localContact();
+ void requestDisplayIcon(const QString& from, const QString& msnObject);
+ void sendFile(const QString& path, Q_INT64 fileSize, const QString& to);
+ void sendImage(const QString& fileName, const QString& to);
+ QString m_pictureUrl;
+ QMap<QString, QString> objectList;
+
+#if MSN_WEBCAM
+ void startWebcam(const QString &myHandle, const QString &msgHandle, bool wantToReceive);
+#endif
+
+
+ public slots:
+ void slotReadMessage(const QString &from, const QByteArray& stream);
+ void messageAcknowledged(unsigned int correlationId, bool fullReceive);
+
+ signals:
+ void sendCommand(const QString &cmd, const QString &args = QString::null, bool addId = true, const QByteArray &body = QByteArray(), bool binary=false);
+ void displayIconReceived(KTempFile* file, const QString& msnObject);
+ void incomingTransfer(const QString& from, const QString& fileName, Q_INT64 fileSize);
+
+ private:
+ class CallbackChannel
+ {
+ public:
+ CallbackChannel(MSNSwitchBoardSocket *switchboard);
+ ~CallbackChannel();
+
+ Q_UINT32 send(const QByteArray& stream);
+
+ private:
+ MSNSwitchBoardSocket *m_switchboard;
+ };
+
+ public:
+ CallbackChannel* callbackChannel();
+ /**
+ * IP's of this compiter, the first one is the one seen by the server.
+ */
+ QStringList localIp() { return m_ip; }
+
+
+ private:
+ void dispatch(const P2P::Message& message);
+ Kopete::Contact* getContactByAccountId(const QString& accountId);
+
+ P2P::MessageFormatter m_messageFormatter;
+ QMap<Q_UINT32, P2P::TransferContext*> m_sessions;
+ QMap<Q_UINT32, P2P::Message> m_messageBuffer;
+ QString m_contact;
+ CallbackChannel *m_callbackChannel;
+ QStringList m_ip;
+
+ friend class P2P::TransferContext;
+ friend class P2P::IncomingTransfer;
+ friend class P2P::OutgoingTransfer;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/msn/icons/Makefile.am b/kopete/protocols/msn/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/protocols/msn/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/protocols/msn/icons/cr128-app-msn_protocol.png b/kopete/protocols/msn/icons/cr128-app-msn_protocol.png
new file mode 100644
index 00000000..dc94a4e9
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr128-app-msn_protocol.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_away.png b/kopete/protocols/msn/icons/cr16-action-msn_away.png
new file mode 100644
index 00000000..cbbd45fc
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_away.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_blocked.png b/kopete/protocols/msn/icons/cr16-action-msn_blocked.png
new file mode 100644
index 00000000..80efc4c7
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_blocked.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_brb.png b/kopete/protocols/msn/icons/cr16-action-msn_brb.png
new file mode 100644
index 00000000..3f1a0d30
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_brb.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_busy.png b/kopete/protocols/msn/icons/cr16-action-msn_busy.png
new file mode 100644
index 00000000..b3dcac08
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_busy.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng b/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng
new file mode 100644
index 00000000..38629273
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_connecting.mng
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_invisible.png b/kopete/protocols/msn/icons/cr16-action-msn_invisible.png
new file mode 100644
index 00000000..ce42bef0
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_invisible.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_lunch.png b/kopete/protocols/msn/icons/cr16-action-msn_lunch.png
new file mode 100644
index 00000000..abf42e3f
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_lunch.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_na.png b/kopete/protocols/msn/icons/cr16-action-msn_na.png
new file mode 100644
index 00000000..b1aa91af
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_na.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png b/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png
new file mode 100644
index 00000000..d42bb0ae
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_newmsg.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_offline.png b/kopete/protocols/msn/icons/cr16-action-msn_offline.png
new file mode 100644
index 00000000..5cf9ffd5
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_offline.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_online.png b/kopete/protocols/msn/icons/cr16-action-msn_online.png
new file mode 100644
index 00000000..71169ad2
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_online.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-action-msn_phone.png b/kopete/protocols/msn/icons/cr16-action-msn_phone.png
new file mode 100644
index 00000000..857ec14a
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-action-msn_phone.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr16-app-msn_protocol.png b/kopete/protocols/msn/icons/cr16-app-msn_protocol.png
new file mode 100644
index 00000000..a18ff5d4
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr16-app-msn_protocol.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr32-app-msn_protocol.png b/kopete/protocols/msn/icons/cr32-app-msn_protocol.png
new file mode 100644
index 00000000..2c9b130b
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr32-app-msn_protocol.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr48-app-msn_protocol.png b/kopete/protocols/msn/icons/cr48-app-msn_protocol.png
new file mode 100644
index 00000000..ad495100
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr48-app-msn_protocol.png
Binary files differ
diff --git a/kopete/protocols/msn/icons/cr64-app-msn_protocol.png b/kopete/protocols/msn/icons/cr64-app-msn_protocol.png
new file mode 100644
index 00000000..338f81bf
--- /dev/null
+++ b/kopete/protocols/msn/icons/cr64-app-msn_protocol.png
Binary files differ
diff --git a/kopete/protocols/msn/incomingtransfer.cpp b/kopete/protocols/msn/incomingtransfer.cpp
new file mode 100644
index 00000000..99422ef7
--- /dev/null
+++ b/kopete/protocols/msn/incomingtransfer.cpp
@@ -0,0 +1,381 @@
+/*
+ incomingtransfer.cpp - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 "incomingtransfer.h"
+using P2P::TransferContext;
+using P2P::IncomingTransfer;
+using P2P::Message;
+
+// Kde includes
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kserversocket.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+using namespace KNetwork;
+
+// Qt includes
+#include <qfile.h>
+#include <qregexp.h>
+
+// Kopete includes
+#include <kopetetransfermanager.h>
+
+IncomingTransfer::IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
+: TransferContext(from,dispatcher,sessionId)
+{
+ m_direction = P2P::Incoming;
+ m_listener = 0l;
+}
+
+IncomingTransfer::~IncomingTransfer()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ if(m_listener)
+ {
+ delete m_listener;
+ m_listener = 0l;
+ }
+
+ if(m_socket)
+ {
+ delete m_socket;
+ m_socket = 0l;
+ }
+}
+
+
+void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const QString& /*fileName*/)
+{
+ Q_UINT32 sessionId = transfer->info().internalId().toUInt();
+ if(sessionId!=m_sessionId)
+ return;
+
+ QObject::connect(transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
+ m_transfer = transfer;
+
+ QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
+ sendMessage(OK, content);
+
+ QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
+}
+
+void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info)
+{
+ Q_UINT32 sessionId = info.internalId().toUInt();
+ if(sessionId!=m_sessionId)
+ return;
+
+ QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
+ // Send the sending client a cancelation message.
+ sendMessage(DECLINE, content);
+ m_state=Finished;
+
+ QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
+}
+
+
+
+void IncomingTransfer::acknowledged()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ switch(m_state)
+ {
+ case Invitation:
+ // NOTE UDI: base identifier acknowledge message, ignore.
+ // UDI: 200 OK message should follow.
+ if(m_type == File)
+ {
+ // FT: 200 OK acknowledged message.
+ // If this is the first connection between the two clients, a direct connection invitation
+ // should follow. Otherwise, the file transfer may start right away.
+ if(m_transfer)
+ {
+ QFile *destination = new QFile(m_transfer->destinationURL().path());
+ if(!destination->open(IO_WriteOnly))
+ {
+ m_transfer->slotError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing"));
+ m_transfer = 0l;
+
+ error();
+ return;
+ }
+ m_file = destination;
+ }
+ m_state = Negotiation;
+ }
+ break;
+
+ case Negotiation:
+ // 200 OK acknowledge message.
+ break;
+
+ case DataTransfer:
+ break;
+
+ case Finished:
+ // UDI: Bye acknowledge message.
+ m_dispatcher->detach(this);
+ break;
+ }
+}
+
+void IncomingTransfer::processMessage(const Message& message)
+{
+ if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030))
+ {
+ // UserDisplayIcon data or File data is in this message.
+ // Write the recieved data to the file.
+ kdDebug(14140) << k_funcinfo << QString("Received, %1 bytes").arg(message.header.dataSize) << endl;
+
+ m_file->writeBlock(message.body.data(), message.header.dataSize);
+ if(m_transfer){
+ m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize);
+ }
+
+ if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize)
+ {
+ // Transfer is complete.
+ if(m_type == UserDisplayIcon){
+ m_tempFile->close();
+ m_dispatcher->displayIconReceived(m_tempFile, m_object);
+ m_tempFile = 0l;
+ m_file = 0l;
+ }
+ else
+ {
+ m_file->close();
+ }
+
+ m_isComplete = true;
+ // Send data acknowledge message.
+ acknowledge(message);
+
+ if(m_type == UserDisplayIcon)
+ {
+ m_state = Finished;
+ // Send BYE message.
+ sendMessage(BYE, "\r\n");
+ }
+ }
+ }
+ else if(message.header.dataSize == 4 && message.applicationIdentifier == 1)
+ {
+ // Data preparation message.
+ m_tempFile = new KTempFile(locateLocal("tmp", "msnpicture--"), ".png");
+ m_tempFile->setAutoDelete(true);
+ m_file = m_tempFile->file();
+ m_state = DataTransfer;
+ // Send data preparation acknowledge message.
+ acknowledge(message);
+ }
+ else
+ {
+ QString body =
+ QCString(message.body.data(), message.header.dataSize);
+// kdDebug(14140) << k_funcinfo << "received, " << body << endl;
+
+ if(body.startsWith("INVITE"))
+ {
+ // Retrieve some MSNSLP headers used when
+ // replying to this INVITE message.
+ QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_branch = regex.cap(1);
+ // NOTE Call-ID never changes.
+ regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_callId = regex.cap(1);
+ regex = QRegExp("Bridges: ([^\r\n]*)\r\n");
+ regex.search(body);
+ QString bridges = regex.cap(1);
+ // The NetID field is 0 if the Conn-Type is
+ // Direct-Connect or Firewall, otherwise, it is
+ // a randomly generated number.
+ regex = QRegExp("NetID: (\\-?\\d+)\r\n");
+ regex.search(body);
+ QString netId = regex.cap(1);
+ kdDebug(14140) << "net id, " << netId << endl;
+ // Connection Types
+ // - Direct-Connect
+ // - Port-Restrict-NAT
+ // - IP-Restrict-NAT
+ // - Symmetric-NAT
+ // - Firewall
+ regex = QRegExp("Conn-Type: ([^\r\n]+)\r\n");
+ regex.search(body);
+ QString connType = regex.cap(1);
+
+ bool wouldListen = false;
+ if(netId.toUInt() == 0 && connType == "Direct-Connect"){
+ wouldListen = true;
+
+ }
+ else if(connType == "IP-Restrict-NAT"){
+ wouldListen = true;
+ }
+#if 1
+ wouldListen = false; // TODO Direct connection support
+#endif
+ QString content;
+
+ if(wouldListen)
+ {
+ // Create a listening socket for direct file transfer.
+ m_listener = new KServerSocket("", "");
+ m_listener->setResolutionEnabled(true);
+ // Create the callback that will try to accept incoming connections.
+ QObject::connect(m_listener, SIGNAL(readyAccept()), SLOT(slotAccept()));
+ QObject::connect(m_listener, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
+ // Listen for incoming connections.
+ bool isListening = m_listener->listen(1);
+ kdDebug(14140) << k_funcinfo << (isListening ? "listening" : "not listening") << endl;
+ kdDebug(14140) << k_funcinfo
+ << "local endpoint, " << m_listener->localAddress().nodeName()
+ << endl;
+
+ content = "Bridge: TCPv1\r\n"
+ "Listening: true\r\n" +
+ QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
+ QString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->localAddress().nodeName()) +
+ QString("IPv4Internal-Port: %1\r\n").arg(m_listener->localAddress().serviceName()) +
+ "\r\n";
+ }
+ else
+ {
+ content =
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n";
+ }
+
+ m_state = DataTransfer;
+
+ if (m_type != File)
+ {
+ // NOTE For file transfers, the connection invite *must not* be acknowledged in any way
+ // as this trips MSN 7.5
+
+ acknowledge(message);
+ // Send 200 OK message to the sending client.
+ sendMessage(OK, content);
+ }
+ }
+ else if(body.startsWith("BYE"))
+ {
+ m_state = Finished;
+ // Send the sending client an acknowledge message.
+ acknowledge(message);
+
+ if(m_file && m_transfer)
+ {
+ if(m_isComplete){
+ // The transfer is complete.
+ m_transfer->slotComplete();
+ }
+ else
+ {
+ // The transfer has been canceled remotely.
+ if(m_transfer){
+ // Inform the user of the file transfer cancelation.
+ m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
+ }
+ // Remove the partially received file.
+ m_file->remove();
+ }
+ }
+
+ // Dispose of this transfer context.
+ m_dispatcher->detach(this);
+ }
+ else if(body.startsWith("MSNSLP/1.0 200 OK"))
+ {
+ if(m_type == UserDisplayIcon){
+ m_state = Negotiation;
+ // Acknowledge the 200 OK message.
+ acknowledge(message);
+ }
+ }
+ }
+}
+
+void IncomingTransfer::slotListenError(int /*errorCode*/)
+{
+ kdDebug(14140) << k_funcinfo << m_listener->errorString() << endl;
+}
+
+void IncomingTransfer::slotAccept()
+{
+ // Try to accept an incoming connection from the sending client.
+ m_socket = static_cast<KBufferedSocket*>(m_listener->accept());
+ if(!m_socket)
+ {
+ // NOTE If direct connection fails, the sending
+ // client wil transfer the file data through the
+ // existing session.
+ kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl;
+ // Close the listening endpoint.
+ m_listener->close();
+ return;
+ }
+
+ kdDebug(14140) << k_funcinfo << "Direct connection established." << endl;
+
+ // Set the socket to non blocking,
+ // enable the ready read signal and disable
+ // ready write signal.
+ // NOTE readyWrite consumes too much cpu usage.
+ m_socket->setBlocking(false);
+ m_socket->enableRead(true);
+ m_socket->enableWrite(false);
+
+ // Create the callback that will try to read bytes from the accepted socket.
+ QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
+ // Create the callback that will try to handle the socket close event.
+ QObject::connect(m_socket, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
+ // Create the callback that will try to handle the socket error event.
+ QObject::connect(m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+}
+
+void IncomingTransfer::slotSocketRead()
+{
+ int available = m_socket->bytesAvailable();
+ kdDebug(14140) << k_funcinfo << available << ", bytes available." << endl;
+ if(available > 0)
+ {
+ QByteArray buffer(available);
+ m_socket->readBlock(buffer.data(), buffer.size());
+
+ if(QString(buffer) == "foo"){
+ kdDebug(14140) << "Connection Check." << endl;
+ }
+ }
+}
+
+void IncomingTransfer::slotSocketClosed()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+void IncomingTransfer::slotSocketError(int errorCode)
+{
+ kdDebug(14140) << k_funcinfo << errorCode << endl;
+}
+
+#include "incomingtransfer.moc"
diff --git a/kopete/protocols/msn/incomingtransfer.h b/kopete/protocols/msn/incomingtransfer.h
new file mode 100644
index 00000000..23e101b3
--- /dev/null
+++ b/kopete/protocols/msn/incomingtransfer.h
@@ -0,0 +1,57 @@
+/*
+ incomingtransfer.h - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 INCOMINGTRANSFER_H
+#define INCOMINGTRANSFER_H
+
+#include "p2p.h"
+#include "dispatcher.h"
+
+namespace KNetwork{
+ class KServerSocket;
+}
+
+/**
+@author Kopete Developers
+*/
+namespace P2P{
+ class IncomingTransfer : public P2P::TransferContext
+ { Q_OBJECT
+ public:
+ IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId);
+ virtual ~IncomingTransfer();
+
+ private slots:
+ void slotListenError(int errorCode);
+ void slotAccept();
+ void slotSocketRead();
+ void slotSocketClosed();
+ void slotSocketError(int errorCode);
+
+ void slotTransferAccepted(Kopete::Transfer* transfer, const QString& fileName);
+ void slotTransferRefused(const Kopete::FileTransferInfo& info);
+
+
+ private:
+ virtual void acknowledged();
+ virtual void processMessage(const Message& message);
+
+ KTempFile *m_tempFile;
+ KNetwork::KServerSocket *m_listener;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/msn/kopete_msn.desktop b/kopete/protocols/msn/kopete_msn.desktop
new file mode 100644
index 00000000..a8350f6b
--- /dev/null
+++ b/kopete/protocols/msn/kopete_msn.desktop
@@ -0,0 +1,99 @@
+[Desktop Entry]
+Type=Service
+Icon=msn_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_msn
+X-Kopete-Version=1000900
+X-Kopete-Messaging-Protocol=messaging/msn
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_msn
+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=MSN Messenger
+Name[ar]=مرسال MSN
+Name[bn]=à¦à¦®à¦à¦¸à¦à¦¨ বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•
+Name[cy]=Negesydd MSN
+Name[da]=MSN-Messenger
+Name[de]=MSN-Messenger
+Name[eo]=MSN-mesaÄilo
+Name[fa]=پیام‌رسان ام‌اس‌ان
+Name[gl]=MSN Messanger
+Name[hi]=à¤à¤®à¤à¤¸à¤à¤¨ मैसेंजर
+Name[ja]=MSN メッセンジャー
+Name[km]=កម្មវិធី​ផ្ញើសារ MSN
+Name[lt]=MSN žinuÄių klientas
+Name[mk]=ГлаÑник за MSN
+Name[nds]=MSN-Kortnarichtendeenst
+Name[ne]=à¤à¤®à¤à¤¸à¤à¤¨ मेसेनà¥à¤œà¤°
+Name[pa]=MSN ਸà©à¨¨à©‡à¨¹à©‡à¨¦à¨¾à¨°
+Name[pl]=Komunikator MSN Messenger
+Name[pt_BR]=Mensageiro MSN
+Name[ro]=Mesaje instantanee MSN
+Name[tg]=MSN Пайёмбар
+Name[uk]=Кур'єр MSN
+Name[uz]=MSN mesenjer
+Name[uz@cyrillic]=MSN меÑенжер
+Comment=Protocol to connect to MSN Messenger
+Comment[ar]=البرتوكول سيتصل بمرسال MSN
+Comment[be]=Пратакол MSN Messenger
+Comment[bg]=Протокол за връзка Ñ MSN Messenger
+Comment[bn]=à¦à¦®à¦à¦¸à¦à¦¨ বারà§à¦¤à¦¾à¦¬à¦¾à¦¹à¦•à§‡ সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh MSN Messenger
+Comment[bs]=MSN Messenger protokol
+Comment[ca]=Protocol per a connectar-se a MSN Messenger
+Comment[cs]=Protokol k připojení k MSN Messengeru
+Comment[cy]=Protocol i gysylltu â Negesydd MSN
+Comment[da]=Protokol til at forbinde til MSN-Messenger
+Comment[de]=Protokoll zur Verbindung mit dem MSN-Messenger
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο MSN Messenger
+Comment[es]=Protocolo para conectar con MSN Messenger
+Comment[et]=Protokoll ühendumiseks MSN Messengeriga
+Comment[eu]=MSN Messenger-era konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال به پیام‌رسان ام‌اس‌ان
+Comment[fi]=Yhteyskäytäntö MSN Messanger -verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur MSN Messenger
+Comment[ga]=Prótacal chun ceangal le MSN Messenger
+Comment[gl]=Protocolo para se conectar ó MSN Messanger
+Comment[he]=פרוטוקול התחברות ל- MSN Messenger
+Comment[hi]=à¤à¤®à¤à¤¸à¤à¤¨ मैसेंजर से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na MSN Messenger
+Comment[hu]=Protokoll az MSN Messenger használatához
+Comment[is]=Samskiptamáti til að tengjast MSN Messenger
+Comment[it]=Protocollo per connessione a MSN Messenger
+Comment[ja]=MSN メッセンジャーã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=MSN Messenger დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=MSN Messenger-ге қоÑылу протоколы
+Comment[km]=ពិធីការ​ដើម្បី​ភ្ជាប់​ទៅ​កម្មវិធី​ផ្ញើសារ MSN
+Comment[lt]=Protokolas prisijungimui prie MSN žinuÄių kliento
+Comment[mk]=Протокол за поврзување на ГлаÑникот на MSN
+Comment[nb]=Protokoll for å koble til MSN Messenger
+Comment[nds]=Protokoll för't Tokoppeln na den MSN-Kortnarichtendeenst
+Comment[ne]=à¤à¤®à¤à¤¸à¤à¤¨ मेसेनà¥à¤œà¤°à¤®à¤¾ जडान गरà¥à¤¨à¥à¤ªà¤°à¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor MSN Messenger
+Comment[nn]=Protokoll for å kopla til MSN Messenger
+Comment[pl]=Protokół połączenia z serwerem MSN Messenger
+Comment[pt]=Um protocolo para ser ligar ao MSN Messenger
+Comment[pt_BR]=Protocolo para conexão ao MSN Messenger
+Comment[ro]=Protocol de conectare la MSN Messenger
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº MSN Messenger
+Comment[sk]=Protokol pre pripojenie k MSN Messenger
+Comment[sl]=Protokol za povezavo na MSN Messenger
+Comment[sr]=Протокол за повезивање на MSN Messenger
+Comment[sr@Latn]=Protokol za povezivanje na MSN Messenger
+Comment[sv]=Protokoll för att ansluta till MSN-meddelandeklient
+Comment[ta]= MSN Messenger யà¯à®Ÿà®©à¯ இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба MSN Пайёмбар
+Comment[tr]=MSN Messenger'a bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· MSN Messenger
+Comment[uz]=MSN mesenjer bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=MSN меÑенжер билан алоқа ўрнатиш учун протокол
+Comment[wa]=Protocole po s' raloyî a MSN
+Comment[zh_CN]=连接到 MSN Messenger åè®®
+Comment[zh_HK]=用來連接至 MSN Messenger 的通訊å”定
+Comment[zh_TW]=連線到 MSN çš„å”定
+
diff --git a/kopete/protocols/msn/messageformatter.cpp b/kopete/protocols/msn/messageformatter.cpp
new file mode 100644
index 00000000..3a698ac4
--- /dev/null
+++ b/kopete/protocols/msn/messageformatter.cpp
@@ -0,0 +1,192 @@
+/*
+ messageformatter.cpp - msn p2p protocol
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 "messageformatter.h"
+#include "p2p.h"
+
+// Qt includes
+#include <qdatastream.h>
+#include <qregexp.h>
+
+// Kde includes
+#include <kdebug.h>
+
+using P2P::MessageFormatter;
+using P2P::Message;
+
+MessageFormatter::MessageFormatter(QObject *parent, const char *name) : QObject(parent, name)
+{}
+
+MessageFormatter::~MessageFormatter()
+{}
+
+Message MessageFormatter::readMessage(const QByteArray& stream, bool compact)
+{
+ Message inbound;
+
+ Q_UINT32 index = 0;
+ if(compact == false)
+ {
+ // Determine the end position of the message header.
+ while(index < stream.size())
+ {
+ if(stream[index++] == '\n'){
+ if(stream[index - 3] == '\n')
+ break;
+ }
+ }
+
+ // Retrieve the message header.
+ QString messageHeader = QCString(stream.data(), index);
+
+ // Retrieve the message mime version, content type,
+ // and p2p destination.
+ QRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)");
+ regex.search(messageHeader);
+ QString contentType = regex.cap(1);
+
+ if(contentType != "application/x-msnmsgrp2p")
+ return inbound;
+
+// kdDebug(14140) << k_funcinfo << endl;
+
+ regex = QRegExp("MIME-Version: (\\d.\\d)");
+ regex.search(messageHeader);
+ inbound.mimeVersion = regex.cap(1);
+ inbound.contentType = contentType;
+ regex = QRegExp("P2P-Dest: ([^\r\n]*)");
+ regex.search(messageHeader);
+ QString destination = regex.cap(1);
+ }
+
+ QDataStream reader(stream, IO_ReadOnly);
+ reader.setByteOrder(QDataStream::LittleEndian);
+ // Seek to the start position of the message
+ // transport header.
+ reader.device()->at(index);
+
+ // Read the message transport headers from the stream.
+ reader >> inbound.header.sessionId;
+ reader >> inbound.header.identifier;
+ reader >> inbound.header.dataOffset;
+ reader >> inbound.header.totalDataSize;
+ reader >> inbound.header.dataSize;
+ reader >> inbound.header.flag;
+ reader >> inbound.header.ackSessionIdentifier;
+ reader >> inbound.header.ackUniqueIdentifier;
+ reader >> inbound.header.ackDataSize;
+
+ /*kdDebug(14140)
+ << "session id, " << inbound.header.sessionId << endl
+ << "identifier, " << inbound.header.identifier << endl
+ << "data offset, " << inbound.header.dataOffset << endl
+ << "total size, " << inbound.header.totalDataSize << endl
+ << "data size, " << inbound.header.dataSize << endl
+ << "flag, " << inbound.header.flag << endl
+ << "ack session identifier, " << inbound.header.ackSessionIdentifier << endl
+ << "ack unique identifier, " << inbound.header.ackUniqueIdentifier << endl
+ << "ack data size, " << inbound.header.ackDataSize
+ << endl;*/
+
+ // Read the message body from the stream.
+ if(inbound.header.dataSize > 0){
+ inbound.body.resize(inbound.header.dataSize);
+ reader.readRawBytes(inbound.body.data(), inbound.header.dataSize);
+ }
+
+ if(compact == false)
+ {
+ reader.setByteOrder(QDataStream::BigEndian);
+ // Read the message application identifier from the stream.
+ reader >> inbound.applicationIdentifier;
+
+/* kdDebug(14140)
+ << "application identifier, " << inbound.applicationIdentifier
+ << endl;*/
+ }
+
+ return inbound;
+}
+
+void MessageFormatter::writeMessage(const Message& message, QByteArray& stream, bool compact)
+{
+// kdDebug(14140) << k_funcinfo << endl;
+
+ QDataStream writer(stream, IO_WriteOnly);
+ writer.setByteOrder(QDataStream::LittleEndian);
+
+ if(compact == false)
+ {
+ const QCString messageHeader = QString("MIME-Version: 1.0\r\n"
+ "Content-Type: application/x-msnmsgrp2p\r\n"
+ "P2P-Dest: " + message.destination + "\r\n"
+ "\r\n").utf8();
+ // Set the capacity of the message buffer.
+ stream.resize(messageHeader.length() + 48 + message.body.size() + 4);
+ // Write the message header to the stream
+ writer.writeRawBytes(messageHeader.data(), messageHeader.length());
+ }
+ else
+ {
+ // Set the capacity of the message buffer.
+ stream.resize(4 + 48 + message.body.size());
+ // Write the message size to the stream.
+ writer << (Q_INT32)(48+message.body.size());
+ }
+
+
+ // Write the transport headers to the stream.
+ writer << message.header.sessionId;
+ writer << message.header.identifier;
+ writer << message.header.dataOffset;
+ writer << message.header.totalDataSize;
+ writer << message.header.dataSize;
+ writer << message.header.flag;
+ writer << message.header.ackSessionIdentifier;
+ writer << message.header.ackUniqueIdentifier;
+ writer << message.header.ackDataSize;
+
+/* kdDebug(14140)
+ << "session id, " << message.header.sessionId << endl
+ << "identifier, " << message.header.identifier << endl
+ << "data offset, " << message.header.dataOffset << endl
+ << "total size, " << message.header.totalDataSize << endl
+ << "data size, " << message.header.dataSize << endl
+ << "flag, " << message.header.flag << endl
+ << "ack session identifier, " << message.header.ackSessionIdentifier << endl
+ << "ack unique identifier, " << message.header.ackUniqueIdentifier << endl
+ << "ack data size, " << message.header.ackDataSize
+ << endl;
+*/
+ if(message.body.size() > 0){
+ // Write the messge body to the stream.
+ writer.writeRawBytes(message.body.data(), message.body.size());
+ }
+
+ if(compact == false)
+ {
+ // Seek to the message application identifier section.
+ writer.setByteOrder(QDataStream::BigEndian);
+ // Write the message application identifier to the stream.
+ writer << message.applicationIdentifier;
+
+/* kdDebug(14140)
+ << "application identifier, " << message.applicationIdentifier
+ << endl;
+ */
+ }
+}
+
+#include "messageformatter.moc"
diff --git a/kopete/protocols/msn/messageformatter.h b/kopete/protocols/msn/messageformatter.h
new file mode 100644
index 00000000..9eae8682
--- /dev/null
+++ b/kopete/protocols/msn/messageformatter.h
@@ -0,0 +1,40 @@
+/*
+ messageformatter.h - msn p2p protocol
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 MESSAGEFORMATTER_H
+#define MESSAGEFORMATTER_H
+
+#include <qobject.h>
+
+namespace P2P{
+ class Message;
+}
+
+/**
+@author Kopete Developers
+*/
+namespace P2P{
+ class MessageFormatter : public QObject
+ { Q_OBJECT
+ public:
+ MessageFormatter(QObject *parent = 0, const char *name = 0);
+ ~MessageFormatter();
+
+ Message readMessage(const QByteArray& stream, bool compact=false);
+ void writeMessage(const Message& message, QByteArray& stream, bool compact=false);
+ };
+}
+
+#endif
diff --git a/kopete/protocols/msn/msnaccount.cpp b/kopete/protocols/msn/msnaccount.cpp
new file mode 100644
index 00000000..01caec11
--- /dev/null
+++ b/kopete/protocols/msn/msnaccount.cpp
@@ -0,0 +1,1499 @@
+/*
+ msnaccount.h - Manages a single MSN account
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "msnaccount.h"
+
+#include <config.h>
+
+#include <kaction.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kstandarddirs.h>
+#include <kmdcodec.h>
+#include <klocale.h>
+
+#include <qfile.h>
+#include <qregexp.h>
+#include <qvalidator.h>
+#include <qimage.h>
+
+#include "msncontact.h"
+#include "msnnotifysocket.h"
+#include "msnchatsession.h"
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopetemetacontact.h"
+#include "kopetepassword.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+#include "kopetechatsessionmanager.h"
+#include "contactaddednotifydialog.h"
+#include "kopeteutils.h"
+
+#include "sha1.h"
+
+
+#if !defined NDEBUG
+#include "msndebugrawcmddlg.h"
+#include <kglobal.h>
+#endif
+
+#if MSN_WEBCAM
+#include "avdevice/videodevicepool.h"
+#endif
+
+MSNAccount::MSNAccount( MSNProtocol *parent, const QString& AccountID, const char *name )
+ : Kopete::PasswordedAccount ( parent, AccountID.lower(), 0, name )
+{
+ m_notifySocket = 0L;
+ m_connectstatus = MSNProtocol::protocol()->NLN;
+ m_addWizard_metaContact = 0L;
+ m_newContactList=false;
+
+ // Init the myself contact
+ setMyself( new MSNContact( this, accountId(), Kopete::ContactList::self()->myself() ) );
+ //myself()->setOnlineStatus( MSNProtocol::protocol()->FLN );
+
+ 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 * ) ) );
+
+ QObject::connect( Kopete::ContactList::self(), SIGNAL( globalIdentityChanged(const QString&, const QVariant& ) ), SLOT( slotGlobalIdentityChanged(const QString&, const QVariant& ) ));
+
+ m_openInboxAction = new KAction( i18n( "Open Inbo&x..." ), "mail_generic", 0, this, SLOT( slotOpenInbox() ), this, "m_openInboxAction" );
+ m_changeDNAction = new KAction( i18n( "&Change Display Name..." ), QString::null, 0, this, SLOT( slotChangePublicName() ), this, "renameAction" );
+ m_startChatAction = new KAction( i18n( "&Start Chat..." ), "mail_generic", 0, this, SLOT( slotStartChat() ), this, "startChatAction" );
+
+
+ KConfigGroup *config=configGroup();
+
+ m_blockList = config->readListEntry( "blockList" ) ;
+ m_allowList = config->readListEntry( "allowList" ) ;
+ m_reverseList = config->readListEntry( "reverseList" ) ;
+
+ // Load the avatar
+ m_pictureFilename = locateLocal( "appdata", "msnpicture-"+ accountId().lower().replace(QRegExp("[./~]"),"-") +".png" );
+ resetPictureObject(true);
+
+ static_cast<MSNContact *>( myself() )->setInfo( "PHH", config->readEntry("PHH") );
+ static_cast<MSNContact *>( myself() )->setInfo( "PHM", config->readEntry("PHM") );
+ static_cast<MSNContact *>( myself() )->setInfo( "PHW", config->readEntry("PHW") );
+ //this is the display name
+ static_cast<MSNContact *>( myself() )->setInfo( "MFN", config->readEntry("MFN") );
+
+ //construct the group list
+ //Before 2003-11-14 the MSN server allowed us to download the group list without downloading the whole contactlist, but it's not possible anymore
+ QPtrList<Kopete::Group> groupList = Kopete::ContactList::self()->groups();
+ for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() )
+ {
+ QString groupGuid=g->pluginData( protocol(), accountId() + " id" );
+ if ( !groupGuid.isEmpty() )
+ m_groupList.insert( groupGuid , g );
+ }
+
+ // Set the client Id for the myself contact. It sets what MSN feature we support.
+ m_clientId = MSNProtocol::MSNC4 | MSNProtocol::InkFormatGIF | MSNProtocol::SupportMultiPacketMessaging;
+
+#if MSN_WEBCAM
+ Kopete::AV::VideoDevicePool::self()->scanDevices();
+ if( Kopete::AV::VideoDevicePool::self()->hasDevices() )
+ {
+ m_clientId |= MSNProtocol::SupportWebcam;
+ }
+#endif
+}
+
+
+QString MSNAccount::serverName()
+{
+ return configGroup()->readEntry( "serverName" , "messenger.hotmail.com" );
+}
+
+uint MSNAccount::serverPort()
+{
+ return configGroup()->readNumEntry( "serverPort" , 1863 );
+}
+
+bool MSNAccount::useHttpMethod() const
+{
+ return configGroup()->readBoolEntry( "useHttpMethod" , false );
+}
+
+QString MSNAccount::myselfClientId() const
+{
+ return QString::number(m_clientId, 10);
+}
+
+void MSNAccount::connectWithPassword( const QString &passwd )
+{
+ m_newContactList=false;
+ if ( isConnected() )
+ {
+ kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request "
+ << "(Already Connected)" << endl;
+ return;
+ }
+
+ if ( m_notifySocket )
+ {
+ kdDebug( 14140 ) << k_funcinfo <<"Ignoring Connect request (Already connecting)" << endl;
+ return;
+ }
+
+ m_password = passwd;
+
+ if ( m_password.isNull() )
+ {
+ kdDebug( 14140 ) << k_funcinfo <<"Abort connection (null password)" << endl;
+ return;
+ }
+
+
+ if ( contacts().count() <= 1 )
+ {
+ // Maybe the contactlist.xml has been removed, and the serial number not updated
+ // ( the 1 is for the myself contact )
+ configGroup()->writeEntry( "serial", 0 );
+ }
+
+ m_openInboxAction->setEnabled( false );
+
+ createNotificationServer(serverName(), serverPort());
+}
+
+void MSNAccount::createNotificationServer( const QString &host, uint port )
+{
+ if(m_notifySocket) //we are switching from one to another notifysocket.
+ {
+ //remove every slots to that socket, so we won't delete receive signals
+ // from the old socket thinking they are from the new one
+ QObject::disconnect( m_notifySocket , 0, this, 0 );
+ m_notifySocket->deleteLater(); //be sure it will be deleted
+ m_notifySocket=0L;
+ }
+
+ m_msgHandle.clear();
+
+ myself()->setOnlineStatus( MSNProtocol::protocol()->CNT );
+
+
+ m_notifySocket = new MSNNotifySocket( this, accountId() , m_password);
+ m_notifySocket->setUseHttpMethod( useHttpMethod() );
+
+ QObject::connect( m_notifySocket, SIGNAL( groupAdded( const QString&, const QString& ) ),
+ SLOT( slotGroupAdded( const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( groupRenamed( const QString&, const QString& ) ),
+ SLOT( slotGroupRenamed( const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( groupListed( const QString&, const QString& ) ),
+ SLOT( slotGroupAdded( const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( groupRemoved( const QString& ) ),
+ SLOT( slotGroupRemoved( const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( contactList(const QString&, const QString&, const QString&, uint, const QString& ) ),
+ SLOT( slotContactListed(const QString&, const QString&, const QString&, uint, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL(contactAdded(const QString&, const QString&, const QString&, const QString&, const QString& ) ),
+ SLOT( slotContactAdded(const QString&, const QString&, const QString&, const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( contactRemoved(const QString&, const QString&, const QString&, const QString& ) ),
+ SLOT( slotContactRemoved(const QString&, const QString&, const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( statusChanged( const Kopete::OnlineStatus & ) ),
+ SLOT( slotStatusChanged( const Kopete::OnlineStatus & ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( invitedToChat( const QString&, const QString&, const QString&, const QString&, const QString& ) ),
+ SLOT( slotCreateChat( const QString&, const QString&, const QString&, const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( startChat( const QString&, const QString& ) ),
+ SLOT( slotCreateChat( const QString&, const QString& ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( socketClosed() ),
+ SLOT( slotNotifySocketClosed() ) );
+ QObject::connect( m_notifySocket, SIGNAL( newContactList() ),
+ SLOT( slotNewContactList() ) );
+ QObject::connect( m_notifySocket, SIGNAL( receivedNotificationServer(const QString&, uint ) ),
+ SLOT(createNotificationServer(const QString&, uint ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( hotmailSeted( bool ) ),
+ m_openInboxAction, SLOT( setEnabled( bool ) ) );
+ QObject::connect( m_notifySocket, SIGNAL( errorMessage(int, const QString& ) ),
+ SLOT( slotErrorMessageReceived(int, const QString& ) ) );
+
+ m_notifySocket->setStatus( m_connectstatus );
+ m_notifySocket->connect(host, port);
+}
+
+void MSNAccount::disconnect()
+{
+ if ( m_notifySocket )
+ m_notifySocket->disconnect();
+}
+
+KActionMenu * MSNAccount::actionMenu()
+{
+ KActionMenu *m_actionMenu=Kopete::Account::actionMenu();
+ if ( isConnected() )
+ {
+ m_openInboxAction->setEnabled( true );
+ m_startChatAction->setEnabled( true );
+ m_changeDNAction->setEnabled( true );
+ }
+ else
+ {
+ m_openInboxAction->setEnabled( false );
+ m_startChatAction->setEnabled( false );
+ m_changeDNAction->setEnabled( false );
+ }
+
+ m_actionMenu->popupMenu()->insertSeparator();
+
+ m_actionMenu->insert( m_changeDNAction );
+ m_actionMenu->insert( m_startChatAction );
+
+// m_actionMenu->popupMenu()->insertSeparator();
+
+ m_actionMenu->insert( m_openInboxAction );
+
+#if !defined NDEBUG
+ KActionMenu *debugMenu = new KActionMenu( "Debug", m_actionMenu );
+ debugMenu->insert( new KAction( i18n( "Send Raw C&ommand..." ), 0,
+ this, SLOT( slotDebugRawCommand() ), debugMenu, "m_debugRawCommand" ) );
+ m_actionMenu->popupMenu()->insertSeparator();
+ m_actionMenu->insert( debugMenu );
+#endif
+
+ return m_actionMenu;
+}
+
+MSNNotifySocket *MSNAccount::notifySocket()
+{
+ return m_notifySocket;
+}
+
+
+void MSNAccount::setOnlineStatus( const Kopete::OnlineStatus &status , const QString &reason)
+{
+ kdDebug( 14140 ) << k_funcinfo << status.description() << endl;
+
+ // HACK: When changing song, do don't anything while connected
+ if( reason.contains("[Music]") && ( status == MSNProtocol::protocol()->UNK || status == MSNProtocol::protocol()->CNT ) )
+ return;
+
+ // Only send personal message when logged.
+ if( m_notifySocket && m_notifySocket->isLogged() )
+ {
+ // Only update the personal/status message, don't change the online status
+ // since it's the same.
+ if( reason.contains("[Music]") )
+ {
+ QString personalMessage = reason.section("[Music]", 1);
+ setPersonalMessage( MSNProtocol::PersonalMessageMusic, personalMessage );
+
+ // Don't send un-needed status change.
+ return;
+ }
+ else
+ {
+ setPersonalMessage( MSNProtocol::PersonalMessageNormal, reason );
+ }
+ }
+
+ if(status.status()== Kopete::OnlineStatus::Offline)
+ disconnect();
+ else if ( m_notifySocket )
+ {
+ m_notifySocket->setStatus( status );
+ }
+ else
+ {
+ m_connectstatus = status;
+ connect();
+ }
+
+
+}
+
+void MSNAccount::slotStartChat()
+{
+
+ bool ok;
+ QString handle = KInputDialog::getText( i18n( "Start Chat - MSN Plugin" ),
+ i18n( "Please enter the email address of the person with whom you want to chat:" ), QString::null, &ok ).lower();
+ if ( ok )
+ {
+ if ( MSNProtocol::validContactId( handle ) )
+ {
+ if ( !contacts()[ handle ] )
+ addContact( handle, handle, 0L, Kopete::Account::Temporary );
+
+ contacts()[ handle ]->execute();
+ }
+ else
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) );
+ }
+ }
+}
+
+void MSNAccount::slotDebugRawCommand()
+{
+#if !defined NDEBUG
+ if ( !isConnected() )
+ return;
+
+ MSNDebugRawCmdDlg *dlg = new MSNDebugRawCmdDlg( 0L );
+ int result = dlg->exec();
+ if ( result == QDialog::Accepted && m_notifySocket )
+ {
+ m_notifySocket->sendCommand( dlg->command(), dlg->params(),
+ dlg->addId(), dlg->msg().replace( "\n", "\r\n" ).utf8() );
+ }
+ delete dlg;
+#endif
+}
+
+void MSNAccount::slotChangePublicName()
+{
+ if ( !isConnected() )
+ {
+ return;
+ //TODO: change it anyway, and sync at the next connection
+ }
+
+ bool ok;
+ QString name = KInputDialog::getText( i18n( "Change Display Name - MSN Plugin" ),
+ i18n( "Enter the new display name by which you want to be visible to your friends on MSN:" ),
+ myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString(), &ok );
+
+ if ( ok )
+ {
+ if ( name.length() > 387 )
+ {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>The display name you entered is too long. Please use a shorter name.\n"
+ "Your display name has <b>not</b> been changed.</qt>" ),
+ i18n( "Change Display Name - MSN Plugin" ) );
+ return;
+ }
+
+ setPublicName( name );
+ }
+}
+
+
+void MSNAccount::slotOpenInbox()
+{
+ if ( m_notifySocket )
+ m_notifySocket->slotOpenInbox();
+}
+
+
+void MSNAccount::slotNotifySocketClosed()
+{
+ kdDebug( 14140 ) << k_funcinfo << endl;
+
+ Kopete::Account::DisconnectReason reason=(Kopete::Account::DisconnectReason)(m_notifySocket->disconnectReason());
+ m_notifySocket->deleteLater();
+ m_notifySocket = 0l;
+ myself()->setOnlineStatus( MSNProtocol::protocol()->FLN );
+ setAllContactsStatus( MSNProtocol::protocol()->FLN );
+ disconnected(reason);
+
+
+ if(reason == Kopete::Account::OtherClient)
+ { //close all chat sessions, so new message will arive to the other client.
+
+ QValueList<Kopete::ChatSession*> sessions = Kopete::ChatSessionManager::self()->sessions();
+ QValueList<Kopete::ChatSession*>::Iterator it;
+ for (it=sessions.begin() ; it != sessions.end() ; it++ )
+ {
+ MSNChatSession *msnCS = dynamic_cast<MSNChatSession *>( *it );
+ if ( msnCS && msnCS->account() == this )
+ {
+ msnCS->slotCloseSession();
+ }
+ }
+ }
+
+#if 0
+ else if ( state == 0x10 ) // connection died unexpectedly
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error , i18n( "The connection with the MSN server was lost unexpectedly.\n"
+ "If you cannot reconnect now, the server might be down. In that case, please try again later." ),
+ i18n( "Connection Lost - MSN Plugin" ), KMessageBox::Notify );
+ }
+#endif
+ m_msgHandle.clear();
+ // kdDebug( 14140 ) << "MSNAccount::slotNotifySocketClosed - done" << endl;
+}
+
+void MSNAccount::slotStatusChanged( const Kopete::OnlineStatus &status )
+{
+// kdDebug( 14140 ) << k_funcinfo << status.internalStatus() << endl;
+ myself()->setOnlineStatus( status );
+
+ if(m_newContactList)
+ {
+ m_newContactList=false;
+
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ MSNContact *c = static_cast<MSNContact *>( *it );
+ if(c && c->isDeleted() && c->metaContact() && !c->metaContact()->isTemporary() && c!=myself())
+ {
+ if(c->serverGroups().isEmpty())
+ { //the contact is new, add it on the server
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+ addContactServerside( c->contactId() , c->metaContact()->groups() );
+ }
+ else //the contact had been deleted, remove it.
+ {
+ c->deleteLater();
+ }
+ }
+ }
+ }
+}
+
+
+void MSNAccount::slotPersonalMessageChanged( const QString& personalMessage )
+{
+ QString oldPersonalMessage=myself()->property(MSNProtocol::protocol()->propPersonalMessage).value().toString() ;
+ if ( personalMessage != oldPersonalMessage )
+ {
+ myself()->setProperty( MSNProtocol::protocol()->propPersonalMessage, personalMessage );
+ configGroup()->writeEntry( "personalMessage" , personalMessage );
+ }
+}
+
+void MSNAccount::setPublicName( const QString &publicName )
+{
+ if ( m_notifySocket )
+ {
+ m_notifySocket->changePublicName( publicName, QString::null );
+ }
+}
+
+void MSNAccount::setPersonalMessage( MSNProtocol::PersonalMessageType type, const QString &personalMessage )
+{
+ if ( m_notifySocket )
+ {
+ m_notifySocket->changePersonalMessage( type, personalMessage );
+ }
+ /* Eh, if we can't change the display name, don't let make the user think it has changed
+ else if(type == MSNProtocol::PersonalMessageNormal) // Normal personalMessage, not a dynamic one that need formatting.
+ {
+ slotPersonalMessageChanged( personalMessage );
+ }*/
+}
+
+void MSNAccount::slotGroupAdded( const QString& groupName, const QString &groupGuid )
+{
+ if ( m_groupList.contains( groupGuid ) )
+ {
+ // Group can already be in the list since the idle timer does a 'List Groups'
+ // command. Simply return, don't issue a warning.
+ // kdDebug( 14140 ) << k_funcinfo << "Group " << groupName << " already in list, skipped." << endl;
+ return;
+ }
+
+ //--------- Find the appropriate Kopete::Group, or create one ---------//
+ QPtrList<Kopete::Group> groupList = Kopete::ContactList::self()->groups();
+ Kopete::Group *fallBack = 0L;
+
+ //check if we have one in the old group list. if yes, update the id translate map.
+ for(QMap<QString, Kopete::Group*>::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it )
+ {
+ Kopete::Group *g=it.data();
+ if (g && g->pluginData( protocol(), accountId() + " displayName" ) == groupName &&
+ g->pluginData( protocol(), accountId() + " id" ).isEmpty() )
+ { //it has the same name! we got it. (and it is not yet an msn group)
+ fallBack=g;
+ /*if ( g->displayName() != groupName )
+ {
+ // The displayName was changed in Kopete while we were offline
+ // FIXME: update the server right now
+ }*/
+ break;
+ }
+ }
+
+ if(!fallBack)
+ {
+ //it's certenly a new group ! search if one already exist with the same displayname.
+ for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() )
+ {
+ /* --This has been replaced by the loop right before.
+ if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() )
+ {
+ if ( g->pluginData( protocol(), accountId() + " id" ).toUInt() == groupNumber )
+ {
+ m_groupList.insert( groupNumber, g );
+ QString oldGroupName;
+ if ( g->pluginData( protocol(), accountId() + " displayName" ) != groupName )
+ {
+ // The displayName of the group has been modified by another client
+ slotGroupRenamed( groupName, groupNumber );
+ }
+ return;
+ }
+ }
+ else {*/
+ if ( g->displayName() == groupName && (groupGuid.isEmpty()|| g->type()==Kopete::Group::Normal) &&
+ g->pluginData( protocol(), accountId() + " id" ).isEmpty() )
+ {
+ fallBack = g;
+ kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" in the old MSN group. But kopete has already one with the same name." << endl;
+ break;
+ }
+ }
+ }
+
+ if ( !fallBack )
+ {
+ if( groupGuid.isEmpty() )
+ { // The group #0 is an unremovable group. his default name is "~" ,
+ // but the official client rename it i18n("others contact") at the first
+ // connection.
+ // In many case, the users don't use that group as a real group, or just as
+ // a group to put all contact that are not sorted.
+ fallBack = Kopete::Group::topLevel();
+ }
+ else
+ {
+ fallBack = new Kopete::Group( groupName );
+ Kopete::ContactList::self()->addGroup( fallBack );
+ kdDebug( 14140 ) << k_funcinfo << "We didn't found the group " << groupName <<" So we're creating a new one." << endl;
+
+ }
+ }
+
+ fallBack->setPluginData( protocol(), accountId() + " id", groupGuid );
+ fallBack->setPluginData( protocol(), accountId() + " displayName", groupName );
+ m_groupList.insert( groupGuid, fallBack );
+
+ // We have pending groups that we need add a contact to
+ if ( tmp_addToNewGroup.contains(groupName) )
+ {
+ QStringList list=tmp_addToNewGroup[groupName];
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ QString contactId = *it;
+ kdDebug( 14140 ) << k_funcinfo << "Adding to new group: " << contactId << endl;
+ MSNContact *c = static_cast<MSNContact *>(contacts()[contactId]);
+ if(c && c->hasProperty(MSNProtocol::protocol()->propGuid.key()) )
+ notifySocket()->addContact( contactId, MSNProtocol::FL, QString::null, c->guid(), groupGuid );
+ else
+ {
+ // If we get to here, we're currently adding a new contact, add the groupGUID to the groupList
+ // to add when contact will be added to contactlist.
+ if( tmp_addNewContactToGroup.contains( contactId ) )
+ tmp_addNewContactToGroup[contactId].append(groupGuid);
+ else
+ tmp_addNewContactToGroup.insert(contactId, QStringList(groupGuid) );
+ }
+ }
+ tmp_addToNewGroup.remove(groupName);
+ }
+}
+
+void MSNAccount::slotGroupRenamed( const QString &groupGuid, const QString& groupName )
+{
+ if ( m_groupList.contains( groupGuid ) )
+ {
+ m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " id", groupGuid );
+ m_groupList[ groupGuid ]->setPluginData( protocol(), accountId() + " displayName", groupName );
+ m_groupList[ groupGuid ]->setDisplayName( groupName );
+ }
+ else
+ {
+ slotGroupAdded( groupName, groupGuid );
+ }
+}
+
+void MSNAccount::slotGroupRemoved( const QString& groupGuid )
+{
+ if ( m_groupList.contains( groupGuid ) )
+ {
+ m_groupList[ groupGuid ]->setPluginData( protocol(), QMap<QString,QString>() );
+ m_groupList.remove( groupGuid );
+ }
+}
+
+void MSNAccount::addGroup( const QString &groupName, const QString& contactToAdd )
+{
+ if ( !contactToAdd.isNull() )
+ {
+ if( tmp_addToNewGroup.contains(groupName) )
+ {
+ tmp_addToNewGroup[groupName].append(contactToAdd);
+ //A group with the same name is about to be added,
+ // we don't need to add a second group with the same name
+ kdDebug( 14140 ) << k_funcinfo << "no need to re-add " << groupName << " for " << contactToAdd << endl;
+ return;
+ }
+ else
+ {
+ tmp_addToNewGroup.insert(groupName,QStringList(contactToAdd));
+ kdDebug( 14140 ) << k_funcinfo << "preparing to add " << groupName << " for " << contactToAdd << endl;
+ }
+ }
+
+ if ( m_notifySocket )
+ m_notifySocket->addGroup( groupName );
+
+}
+
+void MSNAccount::slotKopeteGroupRenamed( Kopete::Group *g )
+{
+ if ( notifySocket() && g->type() == Kopete::Group::Normal )
+ {
+ if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() &&
+ g->displayName() != g->pluginData( protocol(), accountId() + " displayName" ) &&
+ m_groupList.contains( g->pluginData( protocol(), accountId() + " id" ) ) )
+ {
+ notifySocket()->renameGroup( g->displayName(), g->pluginData( protocol(), accountId() + " id" ) );
+ }
+ }
+}
+
+void MSNAccount::slotKopeteGroupRemoved( Kopete::Group *g )
+{
+ //The old gorup list is only used whe syncing the contactlist.
+ //We can assume the contactlist is already fully synced at this time.
+ //The group g is maybe in the oldGroupList. We remove everithing since
+ //we don't need it anymore, no need to search it
+ m_oldGroupList.clear();
+
+
+ if ( !g->pluginData( protocol(), accountId() + " id" ).isEmpty() )
+ {
+ QString groupGuid = g->pluginData( protocol(), accountId() + " id" );
+ if ( !m_groupList.contains( groupGuid ) )
+ {
+ // the group is maybe already removed in the server
+ slotGroupRemoved( groupGuid );
+ return;
+ }
+
+ //this is also done later, but he have to do it now!
+ // (in slotGroupRemoved)
+ m_groupList.remove(groupGuid);
+
+ if ( groupGuid.isEmpty() )
+ {
+ // the group #0 can't be deleted
+ // then we set it as the top-level group
+ if ( g->type() == Kopete::Group::TopLevel )
+ return;
+
+ Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " id", "" );
+ Kopete::Group::topLevel()->setPluginData( protocol(), accountId() + " displayName", g->pluginData( protocol(), accountId() + " displayName" ) );
+ g->setPluginData( protocol(), accountId() + " id", QString::null ); // the group should be soon deleted, but make sure
+
+ return;
+ }
+
+ if ( m_notifySocket )
+ {
+ bool still_have_contact=false;
+ // if contact are contains only in the group we are removing, abort the
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ MSNContact *c = static_cast<MSNContact *>( it.current() );
+ if ( c && c->serverGroups().contains( groupGuid ) )
+ {
+ /** don't do that becasue theses may already have been sent
+ m_notifySocket->removeContact( c->contactId(), groupNumber, MSNProtocol::FL );
+ */
+ still_have_contact=true;
+ break;
+ }
+ }
+ if(!still_have_contact)
+ m_notifySocket->removeGroup( groupGuid );
+ }
+ }
+}
+
+void MSNAccount::slotNewContactList()
+{
+ m_oldGroupList=m_groupList;
+ for(QMap<QString, Kopete::Group*>::Iterator it=m_oldGroupList.begin() ; it != m_oldGroupList.end() ; ++it )
+ { //they are about to be changed
+ if(it.data())
+ it.data()->setPluginData( protocol(), accountId() + " id", QString::null );
+ }
+
+ m_allowList.clear();
+ m_blockList.clear();
+ m_reverseList.clear();
+ m_groupList.clear();
+ KConfigGroup *config=configGroup();
+ config->writeEntry( "blockList" , QString::null ) ;
+ config->writeEntry( "allowList" , QString::null );
+ config->writeEntry( "reverseList" , QString::null );
+
+ // clear all date information which will be received.
+ // if the information is not anymore on the server, it will not be received
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ MSNContact *c = static_cast<MSNContact *>( *it );
+ c->setBlocked( false );
+ c->setAllowed( false );
+ c->setReversed( false );
+ c->setDeleted( true );
+ c->setInfo( "PHH", QString::null );
+ c->setInfo( "PHW", QString::null );
+ c->setInfo( "PHM", QString::null );
+ c->removeProperty( MSNProtocol::protocol()->propGuid );
+ }
+ m_newContactList=true;
+}
+
+void MSNAccount::slotContactListed( const QString& handle, const QString& publicName, const QString &contactGuid, uint lists, const QString& groups )
+{
+ // On empty lists handle might be empty, ignore that
+ // ignore also the myself contact.
+ if ( handle.isEmpty() || handle==accountId())
+ return;
+
+ MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] );
+
+ if ( lists & 1 ) // FL
+ {
+ QStringList contactGroups = QStringList::split( ",", groups, false );
+ if ( c )
+ {
+ if( !c->metaContact() )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "the contact " << c->contactId() << " has no meta contact" <<endl;
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+
+ c->setMetaContact(metaContact);
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+ }
+
+ // Contact exists, update data.
+ // Merging difference between server contact list and Kopete::Contact's contact list into MetaContact's contact-list
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+ if(!publicName.isEmpty() && publicName!=handle)
+ c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName );
+ else
+ c->removeProperty( Kopete::Global::Properties::self()->nickName() );
+ c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid);
+
+ const QMap<QString, Kopete::Group *> oldServerGroups = c->serverGroups();
+ c->clearServerGroups();
+ for ( QStringList::ConstIterator it = contactGroups.begin(); it != contactGroups.end(); ++it )
+ {
+ QString newServerGroupID = *it;
+ if(m_groupList.contains(newServerGroupID))
+ {
+ Kopete::Group *newServerGroup=m_groupList[ newServerGroupID ] ;
+ c->contactAddedToGroup( newServerGroupID, newServerGroup );
+ if( !c->metaContact()->groups().contains(newServerGroup) )
+ {
+ // The contact has been added in a group by another client
+ c->metaContact()->addToGroup( newServerGroup );
+ }
+ }
+ }
+
+ for ( QMap<QString, Kopete::Group *>::ConstIterator it = oldServerGroups.begin(); it != oldServerGroups.end(); ++it )
+ {
+ Kopete::Group *old_group=m_oldGroupList[it.key()];
+ if(old_group)
+ {
+ QString oldnewID=old_group->pluginData(protocol() , accountId() +" id");
+ if ( !oldnewID.isEmpty() && contactGroups.contains( oldnewID ) )
+ continue; //ok, it's correctn no need to do anything.
+
+ c->metaContact()->removeFromGroup( old_group );
+ }
+ }
+
+ c->setDeleted(false);
+
+ // Update server if the contact has been moved to another group while MSN was offline
+ c->sync();
+ }
+ else
+ {
+ Kopete::MetaContact *metaContact = new Kopete::MetaContact();
+
+ c = new MSNContact( this, handle, metaContact );
+ c->setDeleted(true); //we don't want to sync
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+ if(!publicName.isEmpty() && publicName!=handle)
+ c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName );
+ else
+ c->removeProperty( Kopete::Global::Properties::self()->nickName() );
+ c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid );
+
+ for ( QStringList::Iterator it = contactGroups.begin();
+ it != contactGroups.end(); ++it )
+ {
+ QString groupGuid = *it;
+ if(m_groupList.contains(groupGuid))
+ {
+ c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] );
+ metaContact->addToGroup( m_groupList[ groupGuid ] );
+ }
+ }
+ Kopete::ContactList::self()->addMetaContact( metaContact );
+
+ c->setDeleted(false);
+ }
+ }
+ else //the contact is _not_ in the FL, it has been removed
+ {
+ if(c)
+ {
+ c->setOnlineStatus( static_cast<MSNProtocol*>(protocol())->UNK );
+ c->clearServerGroups();
+ //TODO: display a message and suggest to remove the contact.
+ // but i fear a simple messageBox QuestionYesNo here gives a nice crash.
+ //delete ct;
+ }
+ }
+ if ( lists & 2 )
+ slotContactAdded( handle, "AL", publicName, QString::null, QString::null );
+ else if(c)
+ c->setAllowed(false);
+ if ( lists & 4 )
+ slotContactAdded( handle, "BL", publicName, QString::null, QString::null );
+ else if(c)
+ c->setBlocked(false);
+ if ( lists & 8 )
+ slotContactAdded( handle, "RL", publicName, QString::null, QString::null );
+ else if(c)
+ c->setReversed(false);
+ if ( lists & 16 ) // This contact is on the pending list. Add to the reverse list and delete from the pending list
+ {
+ notifySocket()->addContact( handle, MSNProtocol::RL, QString::null, QString::null, QString::null );
+ notifySocket()->removeContact( handle, MSNProtocol::PL, QString::null, QString::null );
+ }
+}
+
+void MSNAccount::slotContactAdded( const QString& handle, const QString& list, const QString& publicName, const QString& contactGuid, const QString &groupGuid )
+{
+ if ( list == "FL" )
+ {
+ bool new_contact = false;
+ if ( !contacts()[ handle ] )
+ {
+ new_contact = true;
+
+ Kopete::MetaContact *m= m_addWizard_metaContact ? m_addWizard_metaContact : new Kopete::MetaContact();
+
+ MSNContact *c = new MSNContact( this, handle, m );
+ if(!publicName.isEmpty() && publicName!=handle)
+ c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName );
+ else
+ c->removeProperty( Kopete::Global::Properties::self()->nickName() );
+ c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid );
+ // Add the new contact to the group he belongs.
+ if ( tmp_addNewContactToGroup.contains(handle) )
+ {
+ QStringList list = tmp_addNewContactToGroup[handle];
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ QString groupGuid = *it;
+
+ // If the group didn't exist yet (yay for async operations), don't add the contact to the group
+ // Let slotGroupAdded do it.
+ if( m_groupList.contains(groupGuid) )
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Adding " << handle << " to group: " << groupGuid << endl;
+ notifySocket()->addContact( handle, MSNProtocol::FL, QString::null, contactGuid, groupGuid );
+
+ c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] );
+
+ m->addToGroup( m_groupList[ groupGuid ] );
+
+ }
+ if ( !m_addWizard_metaContact )
+ {
+ Kopete::ContactList::self()->addMetaContact( m );
+ }
+ }
+ tmp_addNewContactToGroup.remove(handle);
+ }
+
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+
+ m_addWizard_metaContact = 0L;
+ }
+ if ( !new_contact )
+ {
+ // Contact has been added to a group
+ MSNContact *c = findContactByGuid(contactGuid);
+ if(c != 0L)
+ {
+ // Make sure that the contact has always his contactGUID.
+ if( !c->hasProperty(MSNProtocol::protocol()->propGuid.key()) )
+ c->setProperty( MSNProtocol::protocol()->propGuid, contactGuid );
+
+ if ( c->onlineStatus() == MSNProtocol::protocol()->UNK )
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+
+ if ( c->metaContact() && c->metaContact()->isTemporary() )
+ c->metaContact()->setTemporary( false, m_groupList.contains( groupGuid ) ? m_groupList[ groupGuid ] : 0L );
+ else
+ {
+ if(m_groupList.contains(groupGuid))
+ {
+ if( c->metaContact() )
+ c->metaContact()->addToGroup( m_groupList[groupGuid] );
+ c->contactAddedToGroup( groupGuid, m_groupList[ groupGuid ] );
+ }
+ }
+ }
+ }
+
+ if ( !handle.isEmpty() && !m_allowList.contains( handle ) && !m_blockList.contains( handle ) )
+ {
+ kdDebug(14140) << k_funcinfo << "Trying to add contact to AL. " << endl;
+ notifySocket()->addContact(handle, MSNProtocol::AL, QString::null, QString::null, QString::null );
+ }
+ }
+ else if ( list == "BL" )
+ {
+ if ( contacts()[ handle ] )
+ static_cast<MSNContact *>( contacts()[ handle ] )->setBlocked( true );
+ if ( !m_blockList.contains( handle ) )
+ {
+ m_blockList.append( handle );
+ configGroup()->writeEntry( "blockList" , m_blockList ) ;
+ }
+ }
+ else if ( list == "AL" )
+ {
+ if ( contacts()[ handle ] )
+ static_cast<MSNContact *>( contacts()[ handle ] )->setAllowed( true );
+ if ( !m_allowList.contains( handle ) )
+ {
+ m_allowList.append( handle );
+ configGroup()->writeEntry( "allowList" , m_allowList ) ;
+ }
+ }
+ else if ( list == "RL" )
+ {
+ // search for new Contacts
+ Kopete::Contact *ct=contacts()[ handle ];
+ if ( !ct || !ct->metaContact() || ct->metaContact()->isTemporary() )
+ {
+ // Users in the allow list or block list now never trigger the
+ // 'new user' dialog, which makes it impossible to add those here.
+ // Not necessarily bad, but the usability effects need more thought
+ // before I declare it good :- )
+ if ( !m_allowList.contains( handle ) && !m_blockList.contains( handle ) )
+ {
+ QString nick; //in most case, the public name is not know
+ if(publicName!=handle) // so we don't whos it if it is not know
+ nick=publicName;
+ Kopete::UI::ContactAddedNotifyDialog *dialog=
+ new Kopete::UI::ContactAddedNotifyDialog( handle,nick,this,
+ Kopete::UI::ContactAddedNotifyDialog::InfoButton );
+ QObject::connect(dialog,SIGNAL(applyClicked(const QString&)),
+ this,SLOT(slotContactAddedNotifyDialogClosed(const QString& )));
+ dialog->show();
+ }
+ }
+ else
+ {
+ static_cast<MSNContact *>( ct )->setReversed( true );
+ }
+ m_reverseList.append( handle );
+ configGroup()->writeEntry( "reverseList" , m_reverseList ) ;
+ }
+}
+
+void MSNAccount::slotContactRemoved( const QString& handle, const QString& list, const QString& contactGuid, const QString& groupGuid )
+{
+ kdDebug( 14140 ) << k_funcinfo << "handle: " << handle << " list: " << list << " contact-uid: " << contactGuid << endl;
+ MSNContact *c=static_cast<MSNContact *>( contacts()[ handle ] );
+ if ( list == "BL" )
+ {
+ m_blockList.remove( handle );
+ configGroup()->writeEntry( "blockList" , m_blockList ) ;
+ if ( !m_allowList.contains( handle ) )
+ notifySocket()->addContact( handle, MSNProtocol::AL, QString::null, QString::null, QString::null );
+
+ if(c)
+ c->setBlocked( false );
+ }
+ else if ( list == "AL" )
+ {
+ m_allowList.remove( handle );
+ configGroup()->writeEntry( "allowList" , m_allowList ) ;
+ if ( !m_blockList.contains( handle ) )
+ notifySocket()->addContact( handle, MSNProtocol::BL, QString::null, QString::null, QString::null );
+
+ if(c)
+ c->setAllowed( false );
+ }
+ else if ( list == "RL" )
+ {
+ m_reverseList.remove( handle );
+ configGroup()->writeEntry( "reverseList" , m_reverseList ) ;
+
+ if ( c )
+ {
+ // Contact is removed from the reverse list
+ // only MSN can do this, so this is currently not supported
+ c->setReversed( false );
+ /*
+ InfoWidget *info = new InfoWidget( 0 );
+ info->title->setText( "<b>" + i18n( "Contact removed!" ) +"</b>" );
+ QString dummy;
+ dummy = "<center><b>" + imContact->getPublicName() + "( " +imContact->getHandle() +" )</b></center><br>";
+ dummy += i18n( "has removed you from his contact list!" ) + "<br>";
+ dummy += i18n( "This contact is now removed from your contact list" );
+ info->infoText->setText( dummy );
+ info->setCaption( "KMerlin - Info" );
+ info->show();
+ */
+ }
+ }
+ else if ( list == "FL" )
+ {
+ // The FL list only use the contact GUID, use the contact referenced by the GUID.
+ MSNContact *contactRemoved = findContactByGuid(contactGuid);
+ QStringList groupGuidList;
+ bool deleteContact = groupGuid.isEmpty() ? true : false; // Delete the contact when the group GUID is empty.
+ // Remove the contact from the contact list for all the group he is a member.
+ if( groupGuid.isEmpty() )
+ {
+ if(contactRemoved)
+ {
+ QPtrList<Kopete::Group> groupList = contactRemoved->metaContact()->groups();
+ for( QPtrList<Kopete::Group>::Iterator it = groupList.begin(); it != groupList.end(); ++it )
+ {
+ Kopete::Group *group = *it;
+ if ( !group->pluginData( protocol(), accountId() + " id" ).isEmpty() )
+ {
+ groupGuidList.append( group->pluginData( protocol(), accountId() + " id" ) );
+ }
+ }
+ }
+ }
+ else
+ {
+ groupGuidList.append( groupGuid );
+ }
+
+ if( !groupGuidList.isEmpty() )
+ {
+ QStringList::const_iterator stringIt;
+ for( stringIt = groupGuidList.begin(); stringIt != groupGuidList.end(); ++stringIt )
+ {
+ // Contact is removed from the FL list, remove it from the group
+ if(contactRemoved != 0L)
+ contactRemoved->contactRemovedFromGroup( *stringIt );
+
+ //check if the group is now empty to remove it
+ if ( m_notifySocket )
+ {
+ bool still_have_contact=false;
+ // if contact are contains only in the group we are removing, abort the
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ MSNContact *c2 = static_cast<MSNContact *>( it.current() );
+ if ( c2->serverGroups().contains( *stringIt ) )
+ {
+ still_have_contact=true;
+ break;
+ }
+ }
+ if(!still_have_contact)
+ m_notifySocket->removeGroup( *stringIt );
+ }
+ }
+ }
+ if(deleteContact && contactRemoved)
+ {
+ kdDebug(14140) << k_funcinfo << "Deleting the MSNContact " << contactRemoved->contactId() << endl;
+ contactRemoved->deleteLater();
+ }
+ }
+}
+
+void MSNAccount::slotCreateChat( const QString& address, const QString& auth )
+{
+ slotCreateChat( 0L, address, auth, m_msgHandle.first(), m_msgHandle.first() );
+}
+
+void MSNAccount::slotCreateChat( const QString& ID, const QString& address, const QString& auth,
+ const QString& handle_, const QString& publicName )
+{
+ QString handle = handle_.lower();
+
+ if ( handle.isEmpty() )
+ {
+ // we have lost the handle?
+ kdDebug(14140) << k_funcinfo << "Impossible to open a chat session, I forgot the contact to invite" <<endl;
+ // forget it
+ return;
+ }
+
+// kdDebug( 14140 ) << k_funcinfo <<"Creating chat for " << handle << endl;
+
+ if ( !contacts()[ handle ] )
+ addContact( handle, publicName, 0L, Kopete::Account::Temporary );
+
+ MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] );
+
+ if ( c && myself() )
+ {
+ // we can't use simply c->manager(true) here to get the manager, because this will re-open
+ // another chat session, and then, close this new one. We have to re-create the manager manualy.
+ MSNChatSession *manager = dynamic_cast<MSNChatSession*>( c->manager( Kopete::Contact::CannotCreate ) );
+ if(!manager)
+ {
+ Kopete::ContactPtrList chatmembers;
+ chatmembers.append(c);
+ manager = new MSNChatSession( protocol(), myself(), chatmembers );
+ }
+
+ manager->createChat( handle, address, auth, ID );
+
+ /**
+ * This code should open a chatwindow when a socket is open
+ * It has been disabled because gaim open switchboeard too often
+ *
+ * the solution is to open the window only when the contact start typing
+ * see MSNChatSession::receivedTypingMsg
+ *
+
+ KGlobal::config()->setGroup( "MSN" );
+ bool notifyNewChat = KGlobal::config()->readBoolEntry( "NotifyNewChat", false );
+ if ( !ID.isEmpty() && notifyNewChat )
+ {
+ // this temporary message should open the window if they not exist
+ QString body = i18n( "%1 has started a chat with you" ).arg( c->metaContact()->displayName() );
+ Kopete::Message tmpMsg = Kopete::Message( c, manager->members(), body, Kopete::Message::Internal, Kopete::Message::PlainText );
+ manager->appendMessage( tmpMsg );
+ }
+ */
+ }
+
+ if(!m_msgHandle.isEmpty())
+ m_msgHandle.pop_front();
+}
+
+void MSNAccount::slotStartChatSession( const QString& handle )
+{
+ // First create a message manager, because we might get an existing
+ // manager back, in which case we likely also have an active switchboard
+ // connection to reuse...
+
+ MSNContact *c = static_cast<MSNContact *>( contacts()[ handle ] );
+ // if ( isConnected() && c && myself() && handle != m_msnId )
+ if ( m_notifySocket && c && myself() && handle != accountId() )
+ {
+ if ( !c->manager(Kopete::Contact::CannotCreate) || !static_cast<MSNChatSession *>( c->manager( Kopete::Contact::CanCreate ) )->service() )
+ {
+ m_msgHandle.prepend(handle);
+ m_notifySocket->createChatSession();
+ }
+ }
+}
+
+void MSNAccount::slotContactAddedNotifyDialogClosed(const QString& handle)
+{
+ const Kopete::UI::ContactAddedNotifyDialog *dialog =
+ dynamic_cast<const Kopete::UI::ContactAddedNotifyDialog *>(sender());
+ if(!dialog || !m_notifySocket)
+ return;
+
+ if(dialog->added())
+ {
+ Kopete::MetaContact *mc=dialog->addContact();
+ if(mc)
+ { //if the contact has been added this way, it's because the other user added us.
+ // don't forgot to set the reversed flag (Bug 114400)
+ MSNContact *c=dynamic_cast<MSNContact*>(mc->contacts().first());
+ if(c && c->contactId() == handle )
+ {
+ c->setReversed( true );
+ }
+ }
+ }
+
+ if ( !dialog->authorized() )
+ {
+ if ( m_allowList.contains( handle ) )
+ m_notifySocket->removeContact( handle, MSNProtocol::AL, QString::null, QString::null );
+ else if ( !m_blockList.contains( handle ) )
+ m_notifySocket->addContact( handle, MSNProtocol::BL, QString::null, QString::null, QString::null );
+ }
+ else
+ {
+ if ( m_blockList.contains( handle ) )
+ m_notifySocket->removeContact( handle, MSNProtocol::BL, QString::null, QString::null );
+ else if ( !m_allowList.contains( handle ) )
+ m_notifySocket->addContact( handle, MSNProtocol::AL, QString::null, QString::null, QString::null );
+ }
+
+
+}
+
+void MSNAccount::slotGlobalIdentityChanged( const QString &key, const QVariant &value )
+{
+ if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) )
+ {
+ if(key == Kopete::Global::Properties::self()->nickName().key())
+ {
+ QString oldNick = myself()->property( Kopete::Global::Properties::self()->nickName()).value().toString();
+ QString newNick = value.toString();
+
+ if(newNick != oldNick)
+ {
+ setPublicName( value.toString() );
+ }
+ }
+ else if(key == Kopete::Global::Properties::self()->photo().key())
+ {
+ m_pictureFilename = value.toString();
+ kdDebug( 14140 ) << k_funcinfo << m_pictureFilename << endl;
+ resetPictureObject(false, true);
+ }
+ }
+}
+
+void MSNAccount::slotErrorMessageReceived( int type, const QString &msg )
+{
+ QString caption = i18n( "MSN Plugin" );
+
+ // Use different notification type based on the error context.
+ switch(type)
+ {
+ case MSNSocket::ErrorConnectionLost:
+ {
+ Kopete::Utils::notifyConnectionLost( this, caption, msg );
+ break;
+ }
+ case MSNSocket::ErrorConnectionError:
+ {
+ Kopete::Utils::notifyConnectionError( this, caption, msg );
+ break;
+ }
+ case MSNSocket::ErrorCannotConnect:
+ {
+ Kopete::Utils::notifyCannotConnect( this );
+ break;
+ }
+ case MSNSocket::ErrorInformation:
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information, msg, caption );
+ break;
+ }
+ case MSNSocket::ErrorServerError:
+ default:
+ {
+ Kopete::Utils::notifyServerError( this, caption, msg );
+ break;
+ }
+ }
+}
+
+bool MSNAccount::createContact( const QString &contactId, Kopete::MetaContact *metaContact )
+{
+ if ( !metaContact->isTemporary() && m_notifySocket)
+ {
+ m_addWizard_metaContact = metaContact;
+
+ addContactServerside(contactId, metaContact->groups());
+
+ // FIXME: Find out if this contact was really added or not!
+ return true;
+ }
+ else
+ {
+ // This is a temporary contact. ( a person who messaged us but is not on our conntact list.
+ // We don't want to create it on the server.Just create the local contact object and add it
+ // Or we are diconnected, and in that case, the contact will be added when connecting
+ MSNContact *newContact = new MSNContact( this, contactId, metaContact );
+ newContact->setDeleted(true);
+ return true;
+ }
+
+}
+
+void MSNAccount::addContactServerside(const QString &contactId, QPtrList<Kopete::Group> groupList)
+{
+ // First of all, fill the temporary group list. The contact will be moved to his group(s).
+ // When we receive back his contact GUID(required to move a contact between groups)
+ for( Kopete::Group *group = groupList.first(); group; group = groupList.next() )
+ {
+ // TODO: It it time that libkopete generate a unique ID that contains protocols, account and contact id.
+ QString groupId = group->pluginData( protocol(), accountId() + " id" );
+ // If the groupId is empty, that's mean the Kopete group is not on the MSN server.
+ if( !groupId.isEmpty() )
+ {
+ // Something got corrupted on contactlist.xml
+ if( !m_groupList.contains(groupId) )
+ {
+ // Clear the group plugin data.
+ group->setPluginData( protocol() , accountId() + " id" , QString::null);
+ group->setPluginData( protocol() , accountId() + " displayName" , QString::null);
+ kdDebug( 14140 ) << k_funcinfo << " Group " << group->displayName() << " marked with id #" << groupId << " does not seems to be anymore on the server" << endl;
+
+ // Add the group on MSN server, will fix the corruption.
+ kdDebug(14140) << k_funcinfo << "Fixing group corruption, re-adding " << group->displayName() << "to the server." << endl;
+ addGroup( group->displayName(), contactId);
+ }
+ else
+ {
+ // Add the group that the contact belong to add it when we will receive the contact GUID.
+ if( tmp_addNewContactToGroup.contains( contactId ) )
+ tmp_addNewContactToGroup[contactId].append(groupId);
+ else
+ tmp_addNewContactToGroup.insert(contactId, QStringList(groupId) );
+ }
+ }
+ else
+ {
+ if( !group->displayName().isEmpty() && group->type() == Kopete::Group::Normal )
+ {
+ kdDebug(14140) << k_funcinfo << "Group not on MSN server, add it" << endl;
+ addGroup( group->displayName(), contactId );
+ }
+ }
+ }
+
+ // After add the contact to the top-level, it will be moved to required groups later.
+ kdDebug( 14140 ) << k_funcinfo << "Add the contact on the server " << endl;
+ m_notifySocket->addContact( contactId, MSNProtocol::FL, contactId, QString::null, QString::null );
+}
+
+MSNContact *MSNAccount::findContactByGuid(const QString &contactGuid)
+{
+ kdDebug(14140) << k_funcinfo << "Looking for " << contactGuid << endl;
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for ( ; it.current(); ++it )
+ {
+ MSNContact *c = dynamic_cast<MSNContact *>( it.current() );
+
+ if(c && c->guid() == contactGuid )
+ {
+ kdDebug(14140) << k_funcinfo << "OK found a contact. " << endl;
+ // Found the contact GUID
+ return c;
+ }
+ }
+
+ return 0L;
+}
+
+bool MSNAccount::isHotmail() const
+{
+ if ( !m_openInboxAction )
+ return false;
+ return m_openInboxAction->isEnabled();
+}
+
+QString MSNAccount::pictureUrl()
+{
+ return m_pictureFilename;
+}
+
+void MSNAccount::setPictureUrl(const QString &url)
+{
+ m_pictureFilename = url;
+}
+
+QString MSNAccount::pictureObject()
+{
+ if(m_pictureObj.isNull())
+ resetPictureObject(true); //silent=true to keep infinite loop away
+ return m_pictureObj;
+}
+
+void MSNAccount::resetPictureObject(bool silent, bool force)
+{
+ QString old=m_pictureObj;
+
+ if(!configGroup()->readBoolEntry("exportCustomPicture") && !force)
+ {
+ m_pictureObj="";
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+ else
+ {
+ // Check if the picture is a 96x96 image, if not scale, crop and save.
+ QImage picture(m_pictureFilename);
+ if(picture.isNull())
+ {
+ m_pictureObj="";
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+ else
+ {
+ if(picture.width() != 96 || picture.height() != 96)
+ {
+ // Save to a new location in msnpictures.
+ QString newLocation( locateLocal( "appdata", "msnpictures/"+ KURL(m_pictureFilename).fileName().lower() ) );
+
+ // Scale and crop the picture.
+ picture = MSNProtocol::protocol()->scalePicture(picture);
+
+ // Use the cropped/scaled image now.
+ if(!picture.save(newLocation, "PNG"))
+ {
+ m_pictureObj="";
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+ m_pictureFilename = newLocation;
+ }
+ }
+
+ QFile pictFile( m_pictureFilename );
+ if(!pictFile.open(IO_ReadOnly))
+ {
+ m_pictureObj="";
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+ else
+ {
+ QByteArray ar=pictFile.readAll();
+ QString sha1d= QString((KCodecs::base64Encode(SHA1::hash(ar))));
+
+ QString size=QString::number( pictFile.size() );
+ QString all= "Creator"+accountId()+"Size"+size+"Type3Locationkopete.tmpFriendlyAAA=SHA1D"+ sha1d;
+ m_pictureObj="<msnobj Creator=\"" + accountId() + "\" Size=\"" + size + "\" Type=\"3\" Location=\"kopete.tmp\" Friendly=\"AAA=\" SHA1D=\""+sha1d+"\" SHA1C=\""+ QString(KCodecs::base64Encode(SHA1::hashString(all.utf8()))) +"\"/>";
+ myself()->setProperty( Kopete::Global::Properties::self()->photo() , m_pictureFilename );
+ }
+ }
+
+ if(old!=m_pictureObj && isConnected() && m_notifySocket && !silent)
+ {
+ //update the msn pict
+ m_notifySocket->setStatus( myself()->onlineStatus() );
+ }
+}
+
+#include "msnaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
+
diff --git a/kopete/protocols/msn/msnaccount.h b/kopete/protocols/msn/msnaccount.h
new file mode 100644
index 00000000..7fbee16b
--- /dev/null
+++ b/kopete/protocols/msn/msnaccount.h
@@ -0,0 +1,270 @@
+/*
+ msnaccount.h - Manages a single MSN account
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Michaêl Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MSNACCOUNT_H
+#define MSNACCOUNT_H
+
+#include <qobject.h>
+
+#include "kopetepasswordedaccount.h"
+
+#include "msnprotocol.h"
+
+class KAction;
+class KActionMenu;
+
+class MSNNotifySocket;
+class MSNContact;
+
+/**
+ * MSNAccount encapsulates everything that is account-based, as opposed to
+ * protocol based. This basically means sockets, current status, and account
+ * info are stored in the account, whereas the protocol is only the
+ * 'manager' class that creates and manages accounts.
+ */
+class MSNAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+
+public:
+ MSNAccount( MSNProtocol *parent, const QString &accountID, const char *name = 0L );
+
+ /*
+ * return the menu for this account
+ */
+ virtual KActionMenu* actionMenu();
+
+ //------ internal functions
+ /**
+ * change the publicName to this new name
+ */
+ void setPublicName( const QString &name );
+ void setPersonalMessage(MSNProtocol::PersonalMessageType type, const QString &personalMessage );
+
+ /**
+ * Returns the address of the MSN server
+ */
+ QString serverName();
+
+ /**
+ * Returns the address of the MSN server port
+ */
+ uint serverPort();
+
+ MSNNotifySocket *notifySocket();
+
+ /**
+ * return true if we are able to send mail, or to open hotmail inbox
+ */
+ bool isHotmail() const;
+
+
+ /**
+ * Return the picture url.
+ */
+ QString pictureUrl();
+
+ /**
+ * Set the picture url.
+ */
+ void setPictureUrl(const QString &url);
+
+ /**
+ * return the <msnobj> tag of the display picture
+ */
+ QString pictureObject();
+
+ /**
+ * reset the <msnobj>. This method should be called if the displayimage has changed
+ * If we are actualy connected, it will imediatly update the <msgobj> on the server, exepted
+ * if @param silent is set to true
+ * @param force Force the application of MSN picture
+ */
+ void resetPictureObject(bool silent=false, bool force=false);
+
+ //BEGIN Http
+
+ bool useHttpMethod() const;
+
+ //END
+
+ /**
+ * Return the client ID for the myself contact of this account.
+ * It is dynamic to see if we really have a webcam or not.
+ */
+ QString myselfClientId() const;
+
+public slots:
+ virtual void connectWithPassword( const QString &password ) ;
+ virtual void disconnect() ;
+ virtual void setOnlineStatus( const Kopete::OnlineStatus &status , const QString &reason = QString::null);
+
+ /**
+ * Ask to the account to create a new chat session
+ */
+ void slotStartChatSession( const QString& handle );
+
+ /**
+ * Single slot to display error message.
+ */
+ void slotErrorMessageReceived( int type, const QString &msg );
+
+protected:
+ virtual bool createContact( const QString &contactId, Kopete::MetaContact *parentContact );
+
+
+private slots:
+ // Actions related
+ void slotStartChat();
+ void slotOpenInbox();
+ void slotChangePublicName();
+
+//#if !defined NDEBUG //(Stupid moc which don't see when he don't need to slot this slot)
+ /**
+ * Show simple debugging aid
+ */
+ void slotDebugRawCommand();
+//#endif
+
+ // notifySocket related
+ void slotStatusChanged( const Kopete::OnlineStatus &status );
+ void slotNotifySocketClosed();
+ void slotPersonalMessageChanged(const QString& personalMessage);
+ void slotContactRemoved(const QString& handle, const QString& list, const QString& contactGuid, const QString& groupGuid );
+ void slotContactAdded(const QString& handle, const QString& list, const QString& publicName, const QString& contactGuid, const QString &groupGuid );
+ void slotContactListed( const QString& handle, const QString& publicName, const QString &contactGuid, uint lists, const QString& groups );
+ void slotNewContactList();
+ /**
+ * The group has successful renamed in the server
+ * groupName: is new new group name
+ */
+ void slotGroupRenamed(const QString &groupGuid, const QString& groupName );
+ /**
+ * A new group was created on the server (or received durring an LSG command)
+ */
+ void slotGroupAdded( const QString& groupName, const QString &groupGuid );
+ /**
+ * Group was removed from the server
+ */
+ void slotGroupRemoved( const QString &groupGuid );
+ /**
+ * Incoming RING command: connect to the Switchboard server and send
+ * the startChat signal
+ */
+ void slotCreateChat( const QString& sessionID, const QString& address, const QString& auth,
+ const QString& handle, const QString& publicName );
+ /**
+ * Incoming XFR command: this is an result from
+ * slotStartChatSession(handle)
+ * connect to the switchboard server and sen startChat signal
+ */
+ void slotCreateChat( const QString& address, const QString& auth);
+
+
+ // ui related
+ /**
+ * A kopetegroup is renamed, rename group on the server
+ */
+ void slotKopeteGroupRenamed( Kopete::Group *g );
+
+ /**
+ * A kopetegroup is removed, remove the group in the server
+ **/
+ void slotKopeteGroupRemoved( Kopete::Group* );
+
+ /**
+ * add contact ui
+ */
+ void slotContactAddedNotifyDialogClosed( const QString &handle);
+
+ /**
+ * When the dispatch server sends us the notification server to use.
+ */
+ void createNotificationServer( const QString &host, uint port );
+
+ /**
+ * When a global identity key get changed.
+ */
+ void slotGlobalIdentityChanged( const QString &key, const QVariant &value );
+
+private:
+ MSNNotifySocket *m_notifySocket;
+ KAction *m_openInboxAction;
+ KAction *m_startChatAction;
+ KAction *m_changeDNAction;
+
+ // status which will be using for connecting
+ Kopete::OnlineStatus m_connectstatus;
+
+ QStringList m_msgHandle;
+
+ bool m_newContactList;
+
+
+ /**
+ * Add the contact on the server in the given groups.
+ * this is a helper function called bu createContact and slotStatusChanged
+ */
+ void addContactServerside(const QString &contactId, QPtrList<Kopete::Group> groupList);
+
+
+
+public: //FIXME: should be private
+ QMap<QString, Kopete::Group*> m_groupList;
+
+ void addGroup( const QString &groupName, const QString &contactToAdd = QString::null );
+
+ /**
+ * Find and retrive a MSNContact by its contactGuid. (Helper function)
+ */
+ MSNContact *findContactByGuid(const QString &contactGuid);
+private:
+
+ // server data
+ QStringList m_allowList;
+ QStringList m_blockList;
+ QStringList m_reverseList;
+
+ Kopete::MetaContact *m_addWizard_metaContact;
+ QMap< QString, QStringList > tmp_addToNewGroup;
+ QMap< QString, QStringList > tmp_addNewContactToGroup;
+
+ QString m_pictureObj; //a cache of the <msnobj>
+ QString m_pictureFilename; // the picture filename.
+
+ //this is the translation between old to new groups id when syncing from server.
+ QMap<QString, Kopete::Group*> m_oldGroupList;
+
+ /**
+ * I need the password in createNotificationServer.
+ * but i can't ask it there with password() because a nested loop will provoque crash
+ * at this place. so i'm forced to keep it here.
+ * I would like an API to request the password WITHOUT askling it.
+ */
+ QString m_password;
+
+ /**
+ * Cliend ID is a bitfield that contains supported features for a MSN contact.
+ */
+ uint m_clientId;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnaddcontactpage.cpp b/kopete/protocols/msn/msnaddcontactpage.cpp
new file mode 100644
index 00000000..337939e6
--- /dev/null
+++ b/kopete/protocols/msn/msnaddcontactpage.cpp
@@ -0,0 +1,85 @@
+/*
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Kopete (c) 2002-2005 by The Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include <qlayout.h>
+#include <qlineedit.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "msnadd.h"
+#include "msnaddcontactpage.h"
+#include "msnprotocol.h"
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+
+MSNAddContactPage::MSNAddContactPage(bool connected, QWidget *parent, const char *name )
+ : AddContactPage(parent,name)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+/* if ( connected )
+ {*/
+ msndata = new msnAddUI(this);
+ /*
+ msndata->cmbGroup->insertStringList(owner->getGroups());
+ msndata->cmbGroup->setCurrentItem(0);
+ */
+ canadd = true;
+
+/* }
+ else
+ {
+ noaddMsg1 = new QLabel( i18n( "You need to be connected to be able to add contacts." ), this );
+ noaddMsg2 = new QLabel( i18n( "Please connect to the MSN network and try again." ), this );
+ canadd = false;
+}*/
+
+}
+MSNAddContactPage::~MSNAddContactPage()
+{
+}
+
+bool MSNAddContactPage::apply( Kopete::Account* i, Kopete::MetaContact*m )
+{
+ if ( validateData() )
+ {
+ QString userid = msndata->addID->text();
+ return i->addContact( userid , m, Kopete::Account::ChangeKABC );
+ }
+ return false;
+}
+
+
+bool MSNAddContactPage::validateData()
+{
+ if(!canadd)
+ return false;
+
+ QString userid = msndata->addID->text();
+
+ if(MSNProtocol::validContactId(userid))
+ return true;
+
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) );
+
+ return false;
+
+}
+
+#include "msnaddcontactpage.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnaddcontactpage.h b/kopete/protocols/msn/msnaddcontactpage.h
new file mode 100644
index 00000000..c2e3fb3b
--- /dev/null
+++ b/kopete/protocols/msn/msnaddcontactpage.h
@@ -0,0 +1,33 @@
+
+#ifndef MSNADDCONTACTPAGE_H
+#define MSNADDCONTACTPAGE_H
+
+#include <qwidget.h>
+#include <addcontactpage.h>
+#include <qlabel.h>
+
+/**
+ *@author duncan
+ */
+class msnAddUI;
+class MSNProtocol;
+
+class MSNAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ MSNAddContactPage(bool connected, QWidget *parent=0, const char *name=0);
+ ~MSNAddContactPage();
+ msnAddUI *msndata;
+ QLabel *noaddMsg1;
+ QLabel *noaddMsg2;
+ bool canadd;
+ virtual bool validateData();
+ virtual bool apply( Kopete::Account*, Kopete::MetaContact* );
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnchallengehandler.cpp b/kopete/protocols/msn/msnchallengehandler.cpp
new file mode 100644
index 00000000..e0bcc987
--- /dev/null
+++ b/kopete/protocols/msn/msnchallengehandler.cpp
@@ -0,0 +1,151 @@
+/*
+ msnchallengehandler.h - Computes a msn challenge response hash key.
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+ Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org>
+
+ Portions taken from
+ http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
+
+ *************************************************************************
+ * *
+ * 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 "msnchallengehandler.h"
+
+#include <qdatastream.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+
+MSNChallengeHandler::MSNChallengeHandler(const QString& productKey, const QString& productId)
+{
+ m_productKey = productKey;
+ m_productId = productId;
+}
+
+
+MSNChallengeHandler::~MSNChallengeHandler()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+QString MSNChallengeHandler::computeHash(const QString& challengeString)
+{
+ // Step One: THe MD5 Hash.
+
+ // Combine the received challenge string with the product key.
+ KMD5 md5((challengeString + m_productKey).utf8());
+ QCString digest = md5.hexDigest();
+
+ kdDebug(14140) << k_funcinfo << "md5: " << digest << endl;
+
+ QValueVector<Q_INT32> md5Integers(4);
+ for(Q_UINT32 i=0; i < md5Integers.count(); i++)
+ {
+ md5Integers[i] = hexSwap(digest.mid(i*8, 8)).toUInt(0, 16) & 0x7FFFFFFF;
+ kdDebug(14140) << k_funcinfo << ("0x" + hexSwap(digest.mid(i*8, 8))) << " " << md5Integers[i] << endl;
+ }
+
+ // Step Two: Create the challenge string key
+
+ QString challengeKey = challengeString + m_productId;
+ // Pad to multiple of 8.
+ challengeKey = challengeKey.leftJustify(challengeKey.length() + (8 - challengeKey.length() % 8), '0');
+
+ kdDebug(14140) << k_funcinfo << "challenge key: " << challengeKey << endl;
+
+ QValueVector<Q_INT32> challengeIntegers(challengeKey.length() / 4);
+ for(Q_UINT32 i=0; i < challengeIntegers.count(); i++)
+ {
+ QString sNum = challengeKey.mid(i*4, 4), sNumHex;
+
+ // Go through the number string, determining the hex equivalent of each value
+ // and add that to our new hex string for this number.
+ for(uint j=0; j < sNum.length(); j++) {
+ sNumHex += QString::number((int)sNum[j].latin1(), 16);
+ }
+
+ // swap because of the byte ordering issue.
+ sNumHex = hexSwap(sNumHex);
+ // Assign the converted number.
+ challengeIntegers[i] = sNumHex.toInt(0, 16);
+ kdDebug(14140) << k_funcinfo << sNum << (": 0x"+sNumHex) << " " << challengeIntegers[i] << endl;
+ }
+
+ // Step Three: Create the 64-bit hash key.
+
+ // Get the hash key using the specified arrays.
+ Q_INT64 key = createHashKey(md5Integers, challengeIntegers);
+ kdDebug(14140) << k_funcinfo << "key: " << key << endl;
+
+ // Step Four: Create the final hash key.
+
+ QString upper = QString::number(QString(digest.mid(0, 16)).toULongLong(0, 16)^key, 16);
+ if(upper.length() % 16 != 0)
+ upper = upper.rightJustify(upper.length() + (16 - upper.length() % 16), '0');
+
+ QString lower = QString::number(QString(digest.mid(16, 16)).toULongLong(0, 16)^key, 16);
+ if(lower.length() % 16 != 0)
+ lower = lower.rightJustify(lower.length() + (16 - lower.length() % 16), '0');
+
+ return (upper + lower);
+}
+
+Q_INT64 MSNChallengeHandler::createHashKey(const QValueVector<Q_INT32>& md5Integers,
+ const QValueVector<Q_INT32>& challengeIntegers)
+{
+ kdDebug(14140) << k_funcinfo << "Creating 64-bit key." << endl;
+
+ Q_INT64 magicNumber = 0x0E79A9C1L, high = 0L, low = 0L;
+
+ for(uint i=0; i < challengeIntegers.count(); i += 2)
+ {
+ Q_INT64 temp = ((challengeIntegers[i] * magicNumber) % 0x7FFFFFFF) + high;
+ temp = ((temp * md5Integers[0]) + md5Integers[1]) % 0x7FFFFFFF;
+
+ high = (challengeIntegers[i + 1] + temp) % 0x7FFFFFFF;
+ high = ((high * md5Integers[2]) + md5Integers[3]) % 0x7FFFFFFF;
+
+ low += high + temp;
+ }
+
+ high = (high + md5Integers[1]) % 0x7FFFFFFF;
+ low = (low + md5Integers[3]) % 0x7FFFFFFF;
+
+ QDataStream buffer(QByteArray(8), IO_ReadWrite);
+ buffer.setByteOrder(QDataStream::LittleEndian);
+ buffer << (Q_INT32)high;
+ buffer << (Q_INT32)low;
+
+ buffer.device()->reset();
+ buffer.setByteOrder(QDataStream::BigEndian);
+ Q_INT64 key;
+ buffer >> key;
+
+ return key;
+}
+
+QString MSNChallengeHandler::hexSwap(const QString& in)
+{
+ QString sHex = in, swapped;
+ while(sHex.length() > 0)
+ {
+ swapped = swapped + sHex.mid(sHex.length() - 2, 2);
+ sHex = sHex.remove(sHex.length() - 2, 2);
+ }
+ return swapped;
+}
+
+QString MSNChallengeHandler::productId()
+{
+ return m_productId;
+}
+
+#include "msnchallengehandler.moc"
diff --git a/kopete/protocols/msn/msnchallengehandler.h b/kopete/protocols/msn/msnchallengehandler.h
new file mode 100644
index 00000000..9e866560
--- /dev/null
+++ b/kopete/protocols/msn/msnchallengehandler.h
@@ -0,0 +1,64 @@
+/*
+ msnchallengehandler.h - Computes a msn challenge response hash key.
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+ Kopete (c) 2003-2005 by The Kopete developers <kopete-devel@kde.org>
+
+ Portions taken from
+ http://msnpiki.msnfanatic.com/index.php/MSNP11:Challenges
+
+ *************************************************************************
+ * *
+ * 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 MSNCHALLENGEHANDLER_H
+#define MSNCHALLENGEHANDLER_H
+
+#include <qobject.h>
+#include <qvaluevector.h>
+
+/**
+ * Provides a simple way to compute a msn challenge response hash key.
+ *
+ * @author Gregg Edghill
+ */
+class MSNChallengeHandler : public QObject
+{
+Q_OBJECT
+public:
+ MSNChallengeHandler(const QString& productKey, const QString& productId);
+ ~MSNChallengeHandler();
+
+ /**
+ * Computes the response hash string for the specified challenge string.
+ */
+ QString computeHash(const QString& challengeString);
+
+ /**
+ * Returns the product id used by the challenge handler.
+ */
+ QString productId();
+
+private:
+
+ /**
+ * Creates a 64-bit hash key.
+ */
+ Q_INT64 createHashKey(const QValueVector<Q_INT32>& md5Integers, const QValueVector<Q_INT32>& challengeIntegers);
+
+ /**
+ * Swaps the bytes in a hex string.
+ */
+ QString hexSwap(const QString& in);
+
+ QString m_productKey;
+ QString m_productId;
+};
+
+#endif
diff --git a/kopete/protocols/msn/msnchatsession.cpp b/kopete/protocols/msn/msnchatsession.cpp
new file mode 100644
index 00000000..3bf5d0c6
--- /dev/null
+++ b/kopete/protocols/msn/msnchatsession.cpp
@@ -0,0 +1,775 @@
+/*
+ msnchatsession.cpp - MSN Message Manager
+
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "msnchatsession.h"
+
+#include <qlabel.h>
+#include <qimage.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qiconset.h>
+
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <ktempfile.h>
+#include <kmainwindow.h>
+#include <ktoolbar.h>
+#include <krun.h>
+
+#include "kopetecontactaction.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+#include "kopeteview.h"
+
+#include "msncontact.h"
+#include "msnfiletransfersocket.h"
+#include "msnaccount.h"
+#include "msnswitchboardsocket.h"
+
+#include "config.h"
+
+#if !defined NDEBUG
+#include "msndebugrawcmddlg.h"
+#endif
+
+MSNChatSession::MSNChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user,
+ Kopete::ContactPtrList others, const char *name )
+: Kopete::ChatSession( user, others, protocol, name )
+{
+ Kopete::ChatSessionManager::self()->registerChatSession( this );
+ m_chatService = 0l;
+ m_timeoutTimer =0L;
+ m_newSession = true;
+ m_connectionTry=0;
+
+ setInstance(protocol->instance());
+
+ connect( this, SIGNAL( messageSent( Kopete::Message&,
+ Kopete::ChatSession* ) ),
+ this, SLOT( slotMessageSent( Kopete::Message&,
+ Kopete::ChatSession* ) ) );
+
+ connect( this, SIGNAL( invitation(MSNInvitation*& , const QString & , long unsigned int , MSNChatSession* , MSNContact* ) ) ,
+ protocol, SIGNAL( invitation(MSNInvitation*& , const QString & , long unsigned int , MSNChatSession* , MSNContact* ) ) );
+
+
+ m_actionInvite = new KActionMenu( i18n( "&Invite" ), "kontact_contacts", actionCollection(), "msnInvite" );
+ connect ( m_actionInvite->popupMenu() , SIGNAL( aboutToShow() ) , this , SLOT(slotActionInviteAboutToShow() ) ) ;
+
+ #if !defined NDEBUG
+ new KAction( i18n( "Send Raw C&ommand..." ), 0, this, SLOT( slotDebugRawCommand() ), actionCollection(), "msnDebugRawCommand" ) ;
+ #endif
+
+
+ m_actionNudge=new KAction( i18n( "Send Nudge" ), "bell", 0, this, SLOT(slotSendNudge() ), actionCollection(), "msnSendNudge" ) ;
+#if MSN_WEBCAM
+ // Invite to receive webcam action
+ m_actionWebcamReceive=new KAction( i18n( "View Contact's Webcam" ), "webcamreceive", 0, this, SLOT(slotWebcamReceive()), actionCollection(), "msnWebcamReceive" ) ;
+
+ //Send webcam action
+ m_actionWebcamSend=new KAction( i18n( "Send Webcam" ), "webcamsend", 0, this, SLOT(slotWebcamSend()), actionCollection(), "msnWebcamSend" ) ;
+#endif
+
+ new KAction( i18n( "Send File" ),"attach", 0, this, SLOT( slotSendFile() ), actionCollection(), "msnSendFile" );
+
+ MSNContact *c = static_cast<MSNContact*>( others.first() );
+ (new KAction( i18n( "Request Display Picture" ), "image", 0, this, SLOT( slotRequestPicture() ), actionCollection(), "msnRequestDisplayPicture" ))->setEnabled(!c->object().isEmpty());
+
+ if ( !c->object().isEmpty() )
+ {
+
+ connect( c, SIGNAL( displayPictureChanged() ), this, SLOT( slotDisplayPictureChanged() ) );
+ m_image = new QLabel( 0L, "kde toolbar widget" );
+ new KWidgetAction( m_image, i18n( "MSN Display Picture" ), 0, this, SLOT( slotRequestPicture() ), actionCollection(), "msnDisplayPicture" );
+ if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()) )
+ {
+ //if the view doesn't exist yet, we will be unable to get the size of the toolbar
+ // so when the view will exist, we will show the displaypicture.
+ //How to know when a our view is created? We can't.
+ // but chances are the next created view will be for this KMM
+ // And if it is not? never mind. the icon will just be sized 22x22
+ connect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
+ //it's viewActivated and not viewCreated because the view get his mainwindow only when it is shown.
+ }
+ }
+ else
+ {
+ m_image = 0L;
+ }
+
+ setXMLFile("msnchatui.rc");
+
+ setMayInvite( true );
+}
+
+MSNChatSession::~MSNChatSession()
+{
+ delete m_image;
+ //force to disconnect the switchboard
+ //not needed since the m_chatService has us as parent
+ // if(m_chatService)
+ // delete m_chatService;
+
+ QMap<unsigned long int, MSNInvitation*>::Iterator it;
+ for( it = m_invitations.begin(); it != m_invitations.end() ; it = m_invitations.begin())
+ {
+ delete *it;
+ m_invitations.remove( it );
+ }
+}
+
+void MSNChatSession::createChat( const QString &handle,
+ const QString &address, const QString &auth, const QString &ID )
+{
+ /* disabled because i don't want to reopen a chatwindow if we just closed it
+ * and the contact take much time to type his message
+ m_newSession= !(ID.isEmpty());
+ */
+
+ if( m_chatService )
+ {
+ kdDebug(14140) << k_funcinfo << "Service already exists, disconnect them." << endl;
+ delete m_chatService;
+ }
+
+// uncomment this line if you don't want to the peer know when you close the window
+// setCanBeDeleted( false );
+
+ m_chatService = new MSNSwitchBoardSocket( static_cast<MSNAccount*>( myself()->account() ) , this);
+ m_chatService->setUseHttpMethod( static_cast<MSNAccount*>( myself()->account() )->useHttpMethod() );
+ m_chatService->setHandle( myself()->account()->accountId() );
+ m_chatService->setMsgHandle( handle );
+ m_chatService->connectToSwitchBoard( ID, address, auth );
+
+ connect( m_chatService, SIGNAL( userJoined(const QString&,const QString&,bool)),
+ this, SLOT( slotUserJoined(const QString&,const QString&,bool) ) );
+ connect( m_chatService, SIGNAL( userLeft(const QString&,const QString&)),
+ this, SLOT( slotUserLeft(const QString&,const QString&) ) );
+ connect( m_chatService, SIGNAL( msgReceived( Kopete::Message & ) ),
+ this, SLOT( slotMessageReceived( Kopete::Message & ) ) );
+ connect( m_chatService, SIGNAL( switchBoardClosed() ),
+ this, SLOT( slotSwitchBoardClosed() ) );
+ connect( m_chatService, SIGNAL( receivedTypingMsg( const QString &, bool ) ),
+ this, SLOT( receivedTypingMsg( const QString &, bool ) ) );
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if(config->readBoolEntry( "SendTypingNotification" , true) )
+ {
+ connect( this, SIGNAL( myselfTyping( bool ) ),
+ m_chatService, SLOT( sendTypingMsg( bool ) ) );
+ }
+ connect( m_chatService, SIGNAL( msgAcknowledgement(unsigned int, bool) ),
+ this, SLOT( slotAcknowledgement(unsigned int, bool) ) );
+ connect( m_chatService, SIGNAL( invitation( const QString&, const QString& ) ),
+ this, SLOT( slotInvitation( const QString&, const QString& ) ) );
+ connect( m_chatService, SIGNAL( nudgeReceived(const QString&) ),
+ this, SLOT( slotNudgeReceived(const QString&) ) );
+ connect( m_chatService, SIGNAL( errorMessage(int, const QString& ) ), static_cast<MSNAccount *>(myself()->account()), SLOT( slotErrorMessageReceived(int, const QString& ) ) );
+
+ if(!m_timeoutTimer)
+ {
+ m_timeoutTimer=new QTimer(this);
+ connect( m_timeoutTimer , SIGNAL(timeout()), this , SLOT(slotConnectionTimeout() ) );
+ }
+ m_timeoutTimer->start(20000,true);
+}
+
+void MSNChatSession::slotUserJoined( const QString &handle, const QString &publicName, bool IRO )
+{
+ delete m_timeoutTimer;
+ m_timeoutTimer=0L;
+
+ if( !account()->contacts()[ handle ] )
+ account()->addContact( handle, QString::null, 0L, Kopete::Account::Temporary);
+
+ MSNContact *c = static_cast<MSNContact*>( account()->contacts()[ handle ] );
+
+ c->setProperty( Kopete::Global::Properties::self()->nickName() , publicName);
+
+ if(c->clientFlags() & MSNProtocol::MSNC4 )
+ {
+ m_actionNudge->setEnabled(true);
+ }
+#if MSN_WEBCAM
+ if(c->clientFlags() & MSNProtocol::SupportWebcam )
+ {
+ m_actionWebcamReceive->setEnabled(true);
+ }
+#endif
+
+ addContact(c , IRO); // don't show notificaions when we join wesalef
+ if(!m_messagesQueue.empty() || !m_invitations.isEmpty())
+ sendMessageQueue();
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if ( members().count()==1 && config->readNumEntry( "DownloadPicture", 1 ) >= 1 && !c->object().isEmpty() && !c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
+ slotRequestPicture();
+}
+
+void MSNChatSession::slotUserLeft( const QString &handle, const QString& reason )
+{
+ MSNContact *c = static_cast<MSNContact*>( myself()->account()->contacts()[ handle ] );
+ if(c)
+ removeContact(c, reason );
+}
+
+
+
+void MSNChatSession::slotSwitchBoardClosed()
+{
+ //kdDebug(14140) << "MSNChatSession::slotSwitchBoardClosed" << endl;
+ m_chatService->deleteLater();
+ m_chatService=0l;
+
+ cleanMessageQueue( i18n("Connection closed") );
+
+ if(m_invitations.isEmpty())
+ setCanBeDeleted( true );
+}
+
+void MSNChatSession::slotMessageSent(Kopete::Message &message,Kopete::ChatSession *)
+{
+ m_newSession=false;
+ if(m_chatService)
+ {
+ int id = m_chatService->sendMsg(message);
+ if(id == -1)
+ {
+ m_messagesQueue.append(message);
+ kdDebug(14140) << k_funcinfo << "Message added to the queue" <<endl;
+ }
+ else if( id== -2 ) //the message has not been sent
+ {
+ //FIXME: tell the what window the message has been processed. but we havent't sent it
+ messageSucceeded(); //that should stop the blonking icon.
+ }
+ else if( id == -3) //the message has been sent as an immge
+ {
+ appendMessage(message);
+ messageSucceeded();
+ }
+ else
+ {
+ m_messagesSent.insert( id, message );
+ message.setBg(QColor()); // clear the bgColor
+ message.setBody(message.plainBody() , Kopete::Message::PlainText ); //clear every custom tag which are not sent
+ appendMessage(message); // send the own msg to chat window
+ }
+ }
+ else // There's no switchboard available, so we must create a new one!
+ {
+ startChatSession();
+ m_messagesQueue.append(message);
+// sendMessageQueue();
+ //m_msgQueued=new Kopete::Message(message);
+ }
+}
+
+void MSNChatSession::slotMessageReceived( Kopete::Message &msg )
+{
+ m_newSession=false;
+ if( msg.plainBody().startsWith( "AutoMessage: " ) )
+ {
+ //FIXME: HardCodded color are not so good
+ msg.setFg( QColor( "SlateGray3" ) );
+ QFont f;
+ f.setItalic( true );
+ msg.setFont( f );
+ }
+ appendMessage( msg );
+}
+
+void MSNChatSession::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 ) ;
+ }
+ }
+ KAction *b=new KAction( i18n ("Other..."), 0, this, SLOT( slotInviteOtherContact() ), m_actionInvite, "actionOther" );
+ m_actionInvite->insert( b );
+ m_inviteactions.append( b ) ;
+}
+
+void MSNChatSession::slotCloseSession()
+{
+ kdDebug(14140) << k_funcinfo << m_chatService <<endl;
+ if(m_chatService)
+ m_chatService->slotCloseSession();
+}
+
+void MSNChatSession::slotInviteContact( Kopete::Contact *contact )
+{
+ if(contact)
+ inviteContact( contact->contactId() );
+}
+
+void MSNChatSession::inviteContact(const QString &contactId)
+{
+ if( m_chatService )
+ m_chatService->slotInviteContact( contactId );
+ else
+ startChatSession();
+}
+
+void MSNChatSession::slotInviteOtherContact()
+{
+ bool ok;
+ QString handle = KInputDialog::getText(i18n( "MSN Plugin" ),
+ i18n( "Please enter the email address of the person you want to invite:" ),
+ QString::null, &ok );
+ if( !ok )
+ return;
+
+ if( handle.contains('@') != 1 || handle.contains('.') <1)
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n("<qt>You must enter a valid email address.</qt>"), i18n("MSN Plugin"));
+ return;
+ }
+
+ inviteContact(handle);
+}
+
+
+void MSNChatSession::sendMessageQueue()
+{
+ if(!m_chatService)
+ {
+ kdDebug(14140) <<k_funcinfo << "Service doesn't exist" <<endl;
+ return;
+ }
+// kdDebug(14140) << "MSNChatSession::sendMessageQueue: " << m_messagesQueue.count() <<endl;
+ for ( QValueList<Kopete::Message>::iterator it = m_messagesQueue.begin(); it!=m_messagesQueue.end(); it = m_messagesQueue.begin() )
+ {
+ //m_chatService->sendMsg( *it) ;
+ slotMessageSent(*it , this);
+ m_messagesQueue.remove(it);
+ }
+
+
+ QMap<unsigned long int, MSNInvitation*>::Iterator it;
+ for( it = m_invitations.begin(); it != m_invitations.end() ; ++it)
+ {
+ if(! (*it)->incoming() && (*it)->state()<MSNInvitation::Invited)
+ {
+ m_chatService->sendCommand( "MSG" , "N", true, (*it)->invitationHead().utf8() );
+ (*it)->setState(MSNInvitation::Invited);
+ }
+ }
+}
+
+void MSNChatSession::slotAcknowledgement(unsigned int id, bool ack)
+{
+ if ( !m_messagesSent.contains( id ) )
+ {
+ // This is maybe a ACK/NAK for a non-messaging message
+ return;
+ }
+
+ if ( !ack )
+ {
+ Kopete::Message m = m_messagesSent[ id ];
+ QString body = i18n( "The following message has not been sent correctly:\n%1" ).arg( m.plainBody() );
+ Kopete::Message msg = Kopete::Message( m.to().first(), members(), body, Kopete::Message::Internal, Kopete::Message::PlainText );
+ appendMessage( msg );
+ //stop the stupid animation
+ messageSucceeded();
+ }
+ else
+ {
+ messageSucceeded();
+ }
+
+ m_messagesSent.remove( id );
+}
+
+void MSNChatSession::slotInvitation(const QString &handle, const QString &msg)
+{
+ //FIXME! a contact from another account can send a file
+ MSNContact *c = static_cast<MSNContact*>( myself()->account()->contacts()[ handle ] );
+ if(!c)
+ return;
+
+ QRegExp rx("Invitation-Cookie: ([0-9]*)");
+ rx.search(msg);
+ long unsigned int cookie=rx.cap(1).toUInt();
+
+ if(m_invitations.contains(cookie))
+ {
+ MSNInvitation *msnI=m_invitations[cookie];
+ msnI->parseInvitation(msg);
+ }
+ else if( msg.contains("Invitation-Command: INVITE") )
+ {
+ if( msg.contains(MSNFileTransferSocket::applicationID()) )
+ {
+ MSNFileTransferSocket *MFTS=new MSNFileTransferSocket(myself()->account()->accountId(),c,true,this);
+ connect(MFTS, SIGNAL( done(MSNInvitation*) ) , this , SLOT( invitationDone(MSNInvitation*) ));
+ m_invitations.insert( cookie , MFTS);
+ MFTS->parseInvitation(msg);
+ setCanBeDeleted(false);
+ }
+ else
+ {
+ MSNInvitation *i=0l;
+ emit invitation( i , msg, cookie, this, c );
+ if(i)
+ {
+ m_invitations.insert( cookie , i );
+ //don't delete this if all invitation are not done
+ setCanBeDeleted(false);
+ }
+ else
+ {
+ rx=QRegExp("Application-Name: ([^\\r\\n]*)");
+ rx.search(msg);
+ QString inviteName = rx.cap( 1 );
+
+ QString body = i18n(
+ "%1 has sent an unimplemented invitation, the invitation was rejected.\n"
+ "The invitation was: %2" )
+ .arg( c->property( Kopete::Global::Properties::self()->nickName()).value().toString(), inviteName );
+ Kopete::Message tmpMsg = Kopete::Message( c , members() , body , Kopete::Message::Internal, Kopete::Message::PlainText);
+ appendMessage(tmpMsg);
+
+ m_chatService->sendCommand( "MSG" , "N", true, MSNInvitation::unimplemented(cookie) );
+ }
+ }
+ }
+}
+
+void MSNChatSession::invitationDone(MSNInvitation* MFTS)
+{
+ kdDebug(14140) << k_funcinfo <<endl;
+ m_invitations.remove(MFTS->cookie());
+// MFTS->deleteLater();
+ delete MFTS;
+ if(!m_chatService && m_invitations.isEmpty())
+ setCanBeDeleted(true);
+}
+
+void MSNChatSession::sendFile(const QString &fileLocation, const QString &/*fileName*/,
+ long unsigned int fileSize)
+{
+ // TODO create a switchboard to send the file is one is not available.
+ if(m_chatService && members().getFirst())
+ {
+ m_chatService->PeerDispatcher()->sendFile(fileLocation, (Q_INT64)fileSize, members().getFirst()->contactId());
+ }
+}
+
+void MSNChatSession::initInvitation(MSNInvitation* invitation)
+{
+ connect(invitation->object(), SIGNAL( done(MSNInvitation*) ) , this , SLOT( invitationDone(MSNInvitation*) ));
+ m_invitations.insert( invitation->cookie() , invitation);
+
+ if(m_chatService)
+ {
+ m_chatService->sendCommand( "MSG" , "N", true, invitation->invitationHead().utf8() );
+ invitation->setState(MSNInvitation::Invited);
+ }
+ else
+ {
+ startChatSession();
+ }
+}
+
+void MSNChatSession::slotRequestPicture()
+{
+ QPtrList<Kopete::Contact> mb=members();
+ MSNContact *c = static_cast<MSNContact*>( mb.first() );
+ if(!c)
+ return;
+
+ if( !c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
+ {
+ if(m_chatService)
+ {
+ if( !c->object().isEmpty() )
+ m_chatService->requestDisplayPicture();
+ }
+ else if(myself()->onlineStatus().isDefinitelyOnline() && myself()->onlineStatus().status() != Kopete::OnlineStatus::Invisible )
+ startChatSession();
+ }
+ else
+ { //we already have the picture, just show it.
+ KRun::runURL( KURL::fromPathOrURL( c->property(Kopete::Global::Properties::self()->photo()).value().toString() ), "image/png" );
+ }
+
+}
+
+void MSNChatSession::slotDisplayPictureChanged()
+{
+ QPtrList<Kopete::Contact> mb=members();
+ MSNContact *c = static_cast<MSNContact *>( mb.first() );
+ if ( c && m_image )
+ {
+ if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
+ {
+ int sz=22;
+ // get the size of the toolbar were the aciton is plugged.
+ // if you know a better way to get the toolbar, let me know
+ KMainWindow *w= view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L;
+ if(w)
+ {
+ //We connected that in the constructor. we don't need to keep this slot active.
+ disconnect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
+
+ QPtrListIterator<KToolBar> it=w->toolBarIterator() ;
+ KAction *imgAction=actionCollection()->action("msnDisplayPicture");
+ if(imgAction) while(it)
+ {
+ KToolBar *tb=*it;
+ if(imgAction->isPlugged(tb))
+ {
+ sz=tb->iconSize();
+ //ipdate if the size of the toolbar change.
+ disconnect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
+ connect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
+ break;
+ }
+ ++it;
+ }
+ }
+ QString imgURL=c->property(Kopete::Global::Properties::self()->photo()).value().toString();
+ QImage scaledImg = QPixmap( imgURL ).convertToImage().smoothScale( sz, sz );
+ if(!scaledImg.isNull())
+ m_image->setPixmap( scaledImg );
+ else
+ { //the image has maybe not been transfered correctly.. force to download again
+ c->removeProperty(Kopete::Global::Properties::self()->photo());
+ //slotDisplayPictureChanged(); //don't do that or we might end in a infinite loop
+ }
+ QToolTip::add( m_image, "<qt><img src=\"" + imgURL + "\"></qt>" );
+
+ }
+ else
+ {
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if ( config->readNumEntry( "DownloadPicture", 1 ) >= 1 && !c->object().isEmpty() )
+ slotRequestPicture();
+ }
+ }
+}
+
+void MSNChatSession::slotDebugRawCommand()
+{
+#if !defined NDEBUG
+ if ( !m_chatService )
+ return;
+
+ MSNDebugRawCmdDlg *dlg = new MSNDebugRawCmdDlg( 0L );
+ int result = dlg->exec();
+ if( result == QDialog::Accepted && m_chatService )
+ {
+ m_chatService->sendCommand( dlg->command(), dlg->params(),
+ dlg->addId(), dlg->msg().replace("\n","\r\n").utf8() );
+ }
+ delete dlg;
+#endif
+}
+
+
+void MSNChatSession::receivedTypingMsg( const QString &contactId, bool b )
+{
+ MSNContact *c = dynamic_cast<MSNContact *>( account()->contacts()[ contactId ] );
+ if(c && m_newSession && !view(false))
+ {
+ //this was originaly in MSNAccount::slotCreateChat
+ KGlobal::config()->setGroup( "MSN" );
+ bool notifyNewChat = KGlobal::config()->readBoolEntry( "NotifyNewChat", false );
+ if ( notifyNewChat )
+ {
+ // this internal message should open the window if they not exist
+ QString body = i18n( "%1 has started a chat with you" ).arg( c->metaContact()->displayName() );
+ Kopete::Message tmpMsg = Kopete::Message( c, members(), body, Kopete::Message::Internal, Kopete::Message::PlainText );
+ appendMessage( tmpMsg );
+ }
+ }
+ m_newSession=false;
+ if(c)
+ Kopete::ChatSession::receivedTypingMsg(c,b);
+}
+
+void MSNChatSession::slotSendNudge()
+{
+ if(m_chatService)
+ {
+ m_chatService->sendNudge();
+ Kopete::Message msg = Kopete::Message( myself(), members() , i18n ( "has sent a nudge" ), Kopete::Message::Outbound,
+ Kopete::Message::PlainText, QString(), Kopete::Message::TypeAction );
+ appendMessage( msg );
+
+ }
+}
+
+
+void MSNChatSession::slotNudgeReceived(const QString& handle)
+{
+ Kopete::Contact *c = account()->contacts()[ handle ] ;
+ if(!c)
+ c=members().getFirst();
+ Kopete::Message msg = Kopete::Message(c, myself(), i18n ( "has sent you a nudge" ), Kopete::Message::Inbound,
+ Kopete::Message::PlainText, QString(), Kopete::Message::TypeAction );
+ appendMessage( msg );
+ // Emit the nudge/buzz notification (configured by user).
+ emitNudgeNotification();
+}
+
+
+void MSNChatSession::slotWebcamReceive()
+{
+#if MSN_WEBCAM
+ if(m_chatService && members().getFirst())
+ {
+ m_chatService->PeerDispatcher()->startWebcam(myself()->contactId() , members().getFirst()->contactId() , true);
+ }
+#endif
+}
+
+void MSNChatSession::slotWebcamSend()
+{
+#if MSN_WEBCAM
+ kdDebug(14140) << k_funcinfo << endl;
+ if(m_chatService && members().getFirst())
+ {
+ m_chatService->PeerDispatcher()->startWebcam(myself()->contactId() , members().getFirst()->contactId() , false);
+ }
+#endif
+}
+
+
+void MSNChatSession::slotSendFile()
+ {
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<MSNContact *>(contacts.first())->sendFile();
+ }
+
+void MSNChatSession::startChatSession()
+{
+ QPtrList<Kopete::Contact> mb=members();
+ static_cast<MSNAccount*>( account() )->slotStartChatSession( mb.first()->contactId() );
+
+ if(!m_timeoutTimer)
+ {
+ m_timeoutTimer=new QTimer(this);
+ connect( m_timeoutTimer , SIGNAL(timeout()), this , SLOT(slotConnectionTimeout() ) );
+ }
+ m_timeoutTimer->start(20000, true);
+}
+
+
+void MSNChatSession::cleanMessageQueue( const QString & reason )
+{
+ delete m_timeoutTimer;
+ m_timeoutTimer=0L;
+
+ uint nb=m_messagesQueue.count()+m_messagesSent.count();
+ if(nb==0)
+ return;
+ else if(nb==1)
+ {
+ Kopete::Message m;
+ if(m_messagesQueue.count() == 1)
+ m=m_messagesQueue.first();
+ else
+ m=m_messagesSent.begin().data();
+
+ QString body=i18n("The following message has not been sent correctly (%1): \n%2").arg(reason, m.plainBody());
+ Kopete::Message msg = Kopete::Message(m.to().first() , members() , body , Kopete::Message::Internal, Kopete::Message::PlainText);
+ appendMessage(msg);
+ }
+ else
+ {
+ Kopete::Message m;
+ QString body=i18n("These messages have not been sent correctly (%1): <br /><ul>").arg(reason);
+ for ( QMap<unsigned int , Kopete::Message>::iterator it = m_messagesSent.begin(); it!=m_messagesSent.end(); it = m_messagesSent.begin() )
+ {
+ m=it.data();
+ body+= "<li>"+m.escapedBody()+"</li>";
+ m_messagesSent.remove(it);
+ }
+ for ( QValueList<Kopete::Message>::iterator it = m_messagesQueue.begin(); it!=m_messagesQueue.end(); it = m_messagesQueue.begin() )
+ {
+ m=(*it);
+ body+= "<li>"+m.escapedBody()+"</li>";
+ m_messagesQueue.remove(it);
+ }
+ body+="</ul>";
+ Kopete::Message msg = Kopete::Message(m.to().first() , members() , body , Kopete::Message::Internal, Kopete::Message::RichText);
+ appendMessage(msg);
+
+ }
+ m_messagesQueue.clear();
+ m_messagesSent.clear();
+ messageSucceeded(); //stop stupid animation
+}
+
+void MSNChatSession::slotConnectionTimeout()
+{
+ m_connectionTry++;
+ if(m_chatService)
+ {
+ disconnect(m_chatService , 0 , this , 0 );
+ m_chatService->deleteLater();
+ m_chatService=0L;
+ }
+
+ if( m_connectionTry > 3 )
+ {
+ cleanMessageQueue( i18n("Impossible to establish the connection") );
+ delete m_timeoutTimer;
+ m_timeoutTimer=0L;
+ return;
+ }
+ startChatSession();
+
+}
+
+
+
+
+#include "msnchatsession.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnchatsession.h b/kopete/protocols/msn/msnchatsession.h
new file mode 100644
index 00000000..8011574e
--- /dev/null
+++ b/kopete/protocols/msn/msnchatsession.h
@@ -0,0 +1,140 @@
+/*
+ msnchatsession.h - MSN Message Manager
+
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MSNMESSAGEMANAGER_H
+#define MSNMESSAGEMANAGER_H
+
+#include "kopetechatsession.h"
+
+class MSNSwitchBoardSocket;
+class KActionCollection;
+class MSNInvitation;
+class MSNContact;
+class KActionMenu;
+class QLabel;
+
+
+/**
+ * @author Olivier Goffart
+ */
+class KOPETE_EXPORT MSNChatSession : public Kopete::ChatSession
+{
+ Q_OBJECT
+
+public:
+ MSNChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user, Kopete::ContactPtrList others, const char *name = 0 );
+ ~MSNChatSession();
+
+ void createChat( const QString &handle, const QString &address, const QString &auth, const QString &ID = QString::null );
+
+ MSNSwitchBoardSocket *service() { return m_chatService; };
+
+ void sendFile( const QString &fileLocation, const QString &fileName,
+ long unsigned int fileSize );
+
+ /**
+ * append an invitation in the invitation map, and send the first invitation message
+ */
+ void initInvitation(MSNInvitation* invitation);
+
+ virtual void inviteContact(const QString& );
+
+public slots:
+ void slotCloseSession();
+ void slotInviteOtherContact();
+
+ void invitationDone( MSNInvitation* );
+
+ void slotRequestPicture();
+
+ /**
+ * this is a reimplementation of ChatSesstion slot.
+ * the original slot is not virtual, but that's not a problem because it's a slot.
+ */
+ virtual void receivedTypingMsg( const QString &, bool );
+
+ void slotConnectionTimeout();
+
+private slots:
+ void slotMessageSent( Kopete::Message &message, Kopete::ChatSession *kmm );
+ void slotMessageReceived( Kopete::Message &message );
+
+ void slotUserJoined( const QString &handle, const QString &publicName, bool IRO );
+ void slotUserLeft( const QString &handle, const QString &reason );
+ void slotSwitchBoardClosed();
+ void slotInviteContact( Kopete::Contact *contact );
+ void slotAcknowledgement( unsigned int id, bool ack );
+ void slotInvitation( const QString &handle, const QString &msg );
+
+ void slotActionInviteAboutToShow();
+
+ void slotDisplayPictureChanged();
+
+ /**
+ * (debug)
+ */
+ void slotDebugRawCommand();
+
+ void slotSendNudge();
+ void slotWebcamReceive();
+ void slotWebcamSend();
+ void slotSendFile();
+
+ void slotNudgeReceived(const QString& handle);
+
+private:
+
+ MSNSwitchBoardSocket *m_chatService;
+ QString otherString;
+ KActionMenu *m_actionInvite;
+ QPtrList<KAction> m_inviteactions;
+ KAction *m_actionNudge;
+ KAction *m_actionWebcamReceive;
+ KAction *m_actionWebcamSend;
+
+ //Messages sent before the ending of the connection are queued
+ QValueList<Kopete::Message> m_messagesQueue;
+ void sendMessageQueue();
+ void cleanMessageQueue( const QString &reason);
+ void startChatSession();
+
+ QMap<unsigned int, Kopete::Message> m_messagesSent;
+
+ QMap<long unsigned int, MSNInvitation*> m_invitations;
+
+
+ /**
+ * weither or not the "has opened a new chat" message need to be sent if the user is typing
+ */
+ bool m_newSession;
+
+ QLabel *m_image;
+ QTimer *m_timeoutTimer;
+ uint m_connectionTry;
+
+
+signals:
+ /*
+ * This signal is relayed to the protocol and after, to plugins
+ */
+ void invitation(MSNInvitation*& invitation, const QString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/msn/msnchatui.rc b/kopete/protocols/msn/msnchatui.rc
new file mode 100644
index 00000000..6dbc94ca
--- /dev/null
+++ b/kopete/protocols/msn/msnchatui.rc
@@ -0,0 +1,27 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="12" name="kopete_msn_chat">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="msnWebcamReceive" />
+ <Action name="msnWebcamSend" />
+ <Action name="msnSendFile" />
+ <Action name="msnSendNudge" />
+ <Action name="msnRequestDisplayPicture" />
+ <Action name="msnInvite" />
+<!-- <Menu name="debug">
+ <text>&amp;Debug</text>
+ <Action name="msnDebugRawCommand" />
+ </Menu> -->
+ </Menu>
+ </MenuBar>
+
+
+ <ToolBar name="statusToolBar">
+ <Action name="msnDisplayPicture" />
+ <Action name="msnWebcamReceive" />
+ <Action name="msnWebcamSend" />
+ <Action name="msnSendFile" />
+ <Action name="msnSendNudge" />
+ </ToolBar>
+</kpartgui>
diff --git a/kopete/protocols/msn/msncontact.cpp b/kopete/protocols/msn/msncontact.cpp
new file mode 100644
index 00000000..8a490a1b
--- /dev/null
+++ b/kopete/protocols/msn/msncontact.cpp
@@ -0,0 +1,713 @@
+/*
+ msncontact.cpp - MSN Contact
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "msncontact.h"
+
+#include <qcheckbox.h>
+
+#undef KDE_NO_COMPAT
+#include <kaction.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <krun.h>
+#include <ktempfile.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <qregexp.h>
+#include <kio/job.h>
+
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+
+#include "msninfo.h"
+#include "msnchatsession.h"
+#include "msnnotifysocket.h"
+#include "msnaccount.h"
+
+MSNContact::MSNContact( Kopete::Account *account, const QString &id, Kopete::MetaContact *parent )
+: Kopete::Contact( account, id, parent )
+{
+ m_deleted = false;
+ m_allowed = false;
+ m_blocked = false;
+ m_reversed = false;
+ m_moving = false;
+
+ m_clientFlags=0;
+
+ setFileCapable( true );
+
+ // When we are not connected, it's because we are loading the contactlist.
+ // so we set the initial status to offline.
+ // We set offline directly because modifying the status after is too slow.
+ // (notification, contactlist updating,....)
+ //
+ // FIXME: Hacks like these shouldn't happen in the protocols, but should be
+ // covered properly at the libkopete level instead - Martijn
+ //
+ // When we are connected, it can be because the user added a contact with the
+ // wizard, and it can be because we are creating a temporary contact.
+ // if it's added by the wizard, the status will be set immediately after.
+ // if it's a temporary contact, better to set the unknown status.
+ setOnlineStatus( ( parent && parent->isTemporary() ) ? MSNProtocol::protocol()->UNK : MSNProtocol::protocol()->FLN );
+
+ actionBlock = 0L;
+
+ setProperty(MSNProtocol::protocol()->propEmail, id);
+}
+
+MSNContact::~MSNContact()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+bool MSNContact::isReachable()
+{
+ if ( account()->isConnected() && isOnline() && account()->myself()->onlineStatus() != MSNProtocol::protocol()->HDN )
+ return true;
+
+ MSNChatSession *kmm=dynamic_cast<MSNChatSession*>(manager(Kopete::Contact::CannotCreate));
+ if( kmm && kmm->service() ) //the chat socket is open. than mean message will be sent
+ return true;
+
+ // When we are invisible we can't start a chat with others, make isReachable return false
+ // (This is an MSN limitation, not a problem in Kopete)
+ if ( !account()->isConnected() || account()->myself()->onlineStatus() == MSNProtocol::protocol()->HDN )
+ return false;
+
+ //if the contact is offline, it is impossible to send it a message. but it is impossible
+ //to be sure the contact is realy offline. For example, if the contact is not on the contactlist for
+ //some reason.
+ if( onlineStatus() == MSNProtocol::protocol()->FLN && ( isAllowed() || isBlocked() ) && !serverGroups().isEmpty() )
+ return false;
+
+ return true;
+}
+
+Kopete::ChatSession *MSNContact::manager( Kopete::Contact::CanCreateFlags canCreate )
+{
+ Kopete::ContactPtrList chatmembers;
+ chatmembers.append(this);
+
+ Kopete::ChatSession *_manager = Kopete::ChatSessionManager::self()->findChatSession( account()->myself(), chatmembers, protocol() );
+ MSNChatSession *manager = dynamic_cast<MSNChatSession*>( _manager );
+ if(!manager && canCreate==Kopete::Contact::CanCreate)
+ {
+ manager = new MSNChatSession( protocol(), account()->myself(), chatmembers );
+ static_cast<MSNAccount*>( account() )->slotStartChatSession( contactId() );
+ }
+ return manager;
+}
+
+QPtrList<KAction> *MSNContact::customContextMenuActions()
+{
+ QPtrList<KAction> *m_actionCollection = new QPtrList<KAction>;
+
+ // Block/unblock Contact
+ QString label = isBlocked() ? i18n( "Unblock User" ) : i18n( "Block User" );
+ if( !actionBlock )
+ {
+ actionBlock = new KAction( label, "msn_blocked",0, this, SLOT( slotBlockUser() ),
+ this, "actionBlock" );
+
+ //show profile
+ actionShowProfile = new KAction( i18n("Show Profile") , 0, this, SLOT( slotShowProfile() ),
+ this, "actionShowProfile" );
+
+ // Send mail (only available if it is an hotmail account)
+ actionSendMail = new KAction( i18n("Send Email...") , "mail_generic",0, this, SLOT( slotSendMail() ),
+ this, "actionSendMail" );
+
+ // Invite to receive webcam
+ actionWebcamReceive = new KAction( i18n( "View Contact's Webcam" ), "webcamreceive", 0, this, SLOT(slotWebcamReceive() ), this, "msnWebcamReceive" ) ;
+
+ //Send webcam action
+ actionWebcamSend = new KAction( i18n( "Send Webcam" ), "webcamsend", 0, this, SLOT(slotWebcamSend() ), this, "msnWebcamSend" ) ;
+ }
+ else
+ actionBlock->setText( label );
+
+ actionSendMail->setEnabled( static_cast<MSNAccount*>(account())->isHotmail());
+
+ m_actionCollection->append( actionBlock );
+ m_actionCollection->append( actionShowProfile );
+ m_actionCollection->append( actionSendMail );
+ m_actionCollection->append( actionWebcamReceive );
+ m_actionCollection->append( actionWebcamSend );
+
+
+ return m_actionCollection;
+}
+
+void MSNContact::slotBlockUser()
+{
+ MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket();
+ if( !notify )
+ {
+ KMessageBox::error( Kopete::UI::Global::mainWidget(),
+ i18n( "<qt>Please go online to block or unblock a contact.</qt>" ),
+ i18n( "MSN Plugin" ));
+ return;
+ }
+
+ if( m_blocked )
+ {
+ notify->removeContact( contactId(), MSNProtocol::BL, QString::null, QString::null );
+ }
+ else
+ {
+ if(m_allowed)
+ notify->removeContact( contactId(), MSNProtocol::AL, QString::null, QString::null );
+ else
+ notify->addContact( contactId(), MSNProtocol::BL, QString::null, QString::null, QString::null );
+ }
+}
+
+void MSNContact::slotUserInfo()
+{
+ KDialogBase *infoDialog=new KDialogBase( 0l, "infoDialog", /*modal = */false, QString::null, KDialogBase::Close , KDialogBase::Close, false );
+ QString nick=property( Kopete::Global::Properties::self()->nickName()).value().toString();
+ QString personalMessage=property( MSNProtocol::protocol()->propPersonalMessage).value().toString();
+ MSNInfo *info=new MSNInfo ( infoDialog,"info");
+ info->m_id->setText( contactId() );
+ info->m_displayName->setText(nick);
+ info->m_personalMessage->setText(personalMessage);
+ info->m_phh->setText(m_phoneHome);
+ info->m_phw->setText(m_phoneWork);
+ info->m_phm->setText(m_phoneMobile);
+ info->m_reversed->setChecked(m_reversed);
+
+ connect( info->m_reversed, SIGNAL(toggled(bool)) , this, SLOT(slotUserInfoDialogReversedToggled()));
+
+ infoDialog->setMainWidget(info);
+ infoDialog->setCaption(nick);
+ infoDialog->show();
+}
+
+void MSNContact::slotUserInfoDialogReversedToggled()
+{
+ //workaround to make this checkboxe readonly
+ const QCheckBox *cb=dynamic_cast<const QCheckBox*>(sender());
+ if(cb && cb->isChecked()!=m_reversed)
+ const_cast<QCheckBox*>(cb)->setChecked(m_reversed);
+}
+
+void MSNContact::deleteContact()
+{
+ kdDebug( 14140 ) << k_funcinfo << endl;
+
+ MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket();
+ if( notify )
+ {
+ if( hasProperty(MSNProtocol::protocol()->propGuid.key()) )
+ {
+ // Remove from all groups he belongs (if applicable)
+ for( QMap<QString, Kopete::Group*>::Iterator it = m_serverGroups.begin(); it != m_serverGroups.end(); ++it )
+ {
+ kdDebug(14140) << k_funcinfo << "Removing contact from group \"" << it.key() << "\"" << endl;
+ notify->removeContact( contactId(), MSNProtocol::FL, guid(), it.key() );
+ }
+
+ // Then trully remove it from server contact list,
+ // because only removing the contact from his groups isn't sufficent from MSNP11.
+ kdDebug( 14140 ) << k_funcinfo << "Removing contact from top-level." << endl;
+ notify->removeContact( contactId(), MSNProtocol::FL, guid(), QString::null);
+ }
+ else
+ {
+ kdDebug( 14140 ) << k_funcinfo << "The contact is already removed from server, just delete it" << endl;
+ deleteLater();
+ }
+ }
+ else
+ {
+ // FIXME: This case should be handled by Kopete, not by the plugins :( - Martijn
+ // FIXME: We should be able to delete contacts offline, and remove it from server next time we go online - Olivier
+ KMessageBox::error( Kopete::UI::Global::mainWidget(), i18n( "<qt>Please go online to remove a contact from your contact list.</qt>" ), i18n( "MSN Plugin" ));
+ }
+}
+
+bool MSNContact::isBlocked() const
+{
+ return m_blocked;
+}
+
+void MSNContact::setBlocked( bool blocked )
+{
+ if( m_blocked != blocked )
+ {
+ m_blocked = blocked;
+ //update the status
+ setOnlineStatus(m_currentStatus);
+ //m_currentStatus is used here. previously it was onlineStatus() but this may cause problem when
+ // the account is offline because of the Kopete::Contact::OnlineStatus() account offline hack.
+ }
+}
+
+bool MSNContact::isAllowed() const
+{
+ return m_allowed;
+}
+
+void MSNContact::setAllowed( bool allowed )
+{
+ m_allowed = allowed;
+}
+
+bool MSNContact::isReversed() const
+{
+ return m_reversed;
+}
+
+void MSNContact::setReversed( bool reversed )
+{
+ m_reversed= reversed;
+}
+
+bool MSNContact::isDeleted() const
+{
+ return m_deleted;
+}
+
+void MSNContact::setDeleted( bool deleted )
+{
+ m_deleted= deleted;
+}
+
+uint MSNContact::clientFlags() const
+{
+ return m_clientFlags;
+}
+
+void MSNContact::setClientFlags( uint flags )
+{
+ if(m_clientFlags != flags)
+ {
+ if(hasProperty( MSNProtocol::protocol()->propClient.key() ))
+ {
+ if( flags & MSNProtocol::WebMessenger)
+ setProperty( MSNProtocol::protocol()->propClient , i18n("Web Messenger") );
+ else if( flags & MSNProtocol::WindowsMobile)
+ setProperty( MSNProtocol::protocol()->propClient , i18n("Windows Mobile") );
+ else if( flags & MSNProtocol::MSNMobileDevice)
+ setProperty( MSNProtocol::protocol()->propClient , i18n("MSN Mobile") );
+ else if( m_obj.contains("kopete") )
+ setProperty( MSNProtocol::protocol()->propClient , i18n("Kopete") );
+ }
+
+ }
+ m_clientFlags=flags;
+}
+
+void MSNContact::setInfo(const QString &type,const QString &data )
+{
+ if( type == "PHH" )
+ {
+ m_phoneHome = data;
+ setProperty(MSNProtocol::protocol()->propPhoneHome, data);
+ }
+ else if( type == "PHW" )
+ {
+ m_phoneWork=data;
+ setProperty(MSNProtocol::protocol()->propPhoneWork, data);
+ }
+ else if( type == "PHM" )
+ {
+ m_phoneMobile = data;
+ setProperty(MSNProtocol::protocol()->propPhoneMobile, data);
+ }
+ else if( type == "MOB" )
+ {
+ if( data == "Y" )
+ m_phone_mob = true;
+ else if( data == "N" )
+ m_phone_mob = false;
+ else
+ kdDebug( 14140 ) << k_funcinfo << "Unknown MOB " << data << endl;
+ }
+ else if( type == "MFN" )
+ {
+ setProperty(Kopete::Global::Properties::self()->nickName(), data );
+ }
+ else
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Unknow info " << type << " " << data << endl;
+ }
+}
+
+
+void MSNContact::serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> & /* addressBookData */ )
+{
+ // Contact id and display name are already set for us, only add the rest
+ QString groups;
+ bool firstEntry = true;
+ for( QMap<QString, Kopete::Group *>::ConstIterator it = m_serverGroups.begin(); it != m_serverGroups.end(); ++it )
+ {
+ if( !firstEntry )
+ {
+ groups += ",";
+ firstEntry = true;
+ }
+ groups += it.key();
+ }
+
+ QString lists="C";
+ if(m_blocked)
+ lists +="B";
+ if(m_allowed)
+ lists +="A";
+ if(m_reversed)
+ lists +="R";
+
+ serializedData[ "groups" ] = groups;
+ serializedData[ "PHH" ] = m_phoneHome;
+ serializedData[ "PHW" ] = m_phoneWork;
+ serializedData[ "PHM" ] = m_phoneMobile;
+ serializedData[ "lists" ] = lists;
+ serializedData[ "obj" ] = m_obj;
+ serializedData[ "contactGuid" ] = guid();
+}
+
+
+QString MSNContact::guid(){ return property(MSNProtocol::protocol()->propGuid).value().toString(); }
+
+QString MSNContact::phoneHome(){ return m_phoneHome ;}
+QString MSNContact::phoneWork(){ return m_phoneWork ;}
+QString MSNContact::phoneMobile(){ return m_phoneMobile ;}
+
+
+const QMap<QString, Kopete::Group*> MSNContact::serverGroups() const
+{
+ return m_serverGroups;
+}
+void MSNContact::clearServerGroups()
+{
+ m_serverGroups.clear();
+}
+
+
+void MSNContact::sync( unsigned int changed )
+{
+ if( ! (changed & Kopete::Contact::MovedBetweenGroup) )
+ return; //we are only interested by a change in groups
+
+ if(!metaContact() || metaContact()->isTemporary() )
+ return;
+
+ if(m_moving)
+ {
+ //We need to make sure that syncGroups is not called twice successively
+ // because m_serverGroups will be only updated with the reply of the server
+ // and then, the same command can be sent twice.
+ // FIXME: if this method is called a seconds times, that mean change can be
+ // done in the contactlist. we should found a way to recall this
+ // method later. (a QTimer?)
+ kdDebug( 14140 ) << k_funcinfo << " This contact is already moving. Abort sync id: " << contactId() << endl;
+ return;
+ }
+
+ MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket();
+ if( !notify )
+ {
+ //We are not connected, we will doing it next connection.
+ //Force to reload the whole contactlist from server to suync groups when connecting
+ account()->configGroup()->writeEntry("serial", 0 );
+ return;
+ }
+
+ if(m_deleted) //the contact hasn't been synced from server yet.
+ return;
+
+ unsigned int count=m_serverGroups.count();
+
+ //Don't add the contact if it's myself.
+ if(count==0 && contactId() == account()->accountId())
+ return;
+
+ //STEP ONE : add the contact to every kopetegroups where the MC is
+ QPtrList<Kopete::Group> groupList = metaContact()->groups();
+ for ( Kopete::Group *group = groupList.first(); group; group = groupList.next() )
+ {
+ //For each group, ensure it is on the MSN server
+ if( !group->pluginData( protocol() , account()->accountId() + " id" ).isEmpty() )
+ {
+ QString Gid=group->pluginData( protocol(), account()->accountId() + " id" );
+ if( !static_cast<MSNAccount*>( account() )->m_groupList.contains(Gid) )
+ { // ohoh! something is corrupted on the contactlist.xml
+ // anyway, we never should add a contact to an unexisting group on the server.
+ // This shouln't be possible anymore 2004-06-10 -Olivier
+
+ //repair the problem
+ group->setPluginData( protocol() , account()->accountId() + " id" , QString::null);
+ group->setPluginData( protocol() , account()->accountId() + " displayName" , QString::null);
+ kdWarning( 14140 ) << k_funcinfo << " Group " << group->displayName() << " marked with id #" <<Gid << " does not seems to be anymore on the server" << endl;
+
+ if(!group->displayName().isEmpty() && group->type() == Kopete::Group::Normal) //not the top-level
+ {
+ //Create the group and add the contact
+ static_cast<MSNAccount*>( account() )->addGroup( group->displayName(),contactId() );
+ count++;
+ m_moving=true;
+ }
+ }
+ else if( !m_serverGroups.contains(Gid) )
+ {
+ //Add the contact to the group on the server
+ notify->addContact( contactId(), MSNProtocol::FL, QString::null, guid(), Gid );
+ count++;
+ m_moving=true;
+ }
+ }
+ else
+ {
+ if(!group->displayName().isEmpty() && group->type() == Kopete::Group::Normal) //not the top-level
+ {
+ //Create the group and add the contact
+ static_cast<MSNAccount*>( account() )->addGroup( group->displayName(),contactId() );
+
+ //WARNING: if contact is not correctly added (because the group was not aded corrdctly for hinstance),
+ // if we increment the count, the contact can be deleted from the old group, and be lost :-(
+ count++;
+ m_moving=true;
+ }
+ }
+ }
+
+ //STEP TWO : remove the contact from groups where the MC is not, but let it at least in one group
+
+ //contact is not in that group. on the server. we will remove them dirrectly after the loop
+ QValueList<QString> removinglist;
+
+ for( QMap<QString, Kopete::Group*>::Iterator it = m_serverGroups.begin();(count > 1 && it != m_serverGroups.end()); ++it )
+ {
+ if( !static_cast<MSNAccount*>( account() )->m_groupList.contains(it.key()) )
+ { // ohoh! something is corrupted on the contactlist.xml
+ // anyway, we never should add a contact to an unexisting group on the server.
+
+ //repair the problem ... //contactRemovedFromGroup( it.key() );
+ // ... later (we can't remove it from the map now )
+ removinglist.append(it.key());
+ count--;
+
+ kdDebug( 14140 ) << k_funcinfo << "the group marked with id #" << it.key() << " does not seems to be anymore on the server" << endl;
+
+ continue;
+ }
+
+ Kopete::Group *group=it.data();
+ if(!group) //we can't trust the data of it() see in MSNProtocol::deserializeContact why
+ group=static_cast<MSNAccount*>( account() )->m_groupList[it.key()];
+ if( !metaContact()->groups().contains(group) )
+ {
+ m_moving=true;
+ notify->removeContact( contactId(), MSNProtocol::FL, guid(), it.key() );
+ count--;
+ }
+ }
+
+ for(QValueList<QString>::Iterator it= removinglist.begin() ; it != removinglist.end() ; ++it )
+ contactRemovedFromGroup(*it);
+
+ //FINAL TEST: is the contact at least in a group..
+ // this may happens if we just added a temporary contact to top-level
+ // we add the contact to the group #0 (the default one)
+ /*if(count==0)
+ {
+// notify->addContact( contactId(), MSNProtocol::FL, QString::null, guid(), "0");
+ }*/
+}
+
+void MSNContact::contactAddedToGroup( const QString& groupId, Kopete::Group *group )
+{
+ m_serverGroups.insert( groupId, group );
+ m_moving=false;
+}
+
+void MSNContact::contactRemovedFromGroup( const QString& groupId )
+{
+ m_serverGroups.remove( groupId );
+ if(m_serverGroups.isEmpty() && !m_moving)
+ {
+ deleteLater();
+ }
+ m_moving=false;
+}
+
+
+void MSNContact::rename( const QString &newName )
+{
+ //kdDebug( 14140 ) << k_funcinfo << "From: " << displayName() << ", to: " << newName << endl;
+
+/* if( newName == displayName() )
+ return;*/
+
+ // FIXME: This should be called anymore.
+ MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket();
+ if( notify )
+ {
+ notify->changePublicName( newName, contactId() );
+ }
+}
+
+void MSNContact::slotShowProfile()
+{
+ KRun::runURL( KURL( QString::fromLatin1("http://members.msn.com/?pgmarket=it-it&mem=") + contactId()) , "text/html" );
+}
+
+
+/**
+ * FIXME: Make this a standard KMM API call
+ */
+void MSNContact::sendFile( const KURL &sourceURL, const QString &altFileName, uint /*fileSize*/ )
+{
+ QString filePath;
+
+ //If the file location is null, then get it from a file open dialog
+ if( !sourceURL.isValid() )
+ filePath = KFileDialog::getOpenFileName( QString::null ,"*", 0l , i18n( "Kopete File Transfer" ));
+ else
+ filePath = sourceURL.path(-1);
+
+ //kdDebug(14140) << "MSNContact::sendFile: File chosen to send:" << fileName << endl;
+
+ if ( !filePath.isEmpty() )
+ {
+ Q_UINT32 fileSize = QFileInfo(filePath).size();
+ //Send the file
+ static_cast<MSNChatSession*>( manager(Kopete::Contact::CanCreate) )->sendFile( filePath, altFileName, fileSize );
+
+ }
+}
+
+void MSNContact::setOnlineStatus(const Kopete::OnlineStatus& status)
+{
+ if(isBlocked() && status.internalStatus() < 15)
+ {
+ Kopete::Contact::setOnlineStatus(
+ Kopete::OnlineStatus(status.status() ,
+ (status.weight()==0) ? 0 : (status.weight() -1) ,
+ protocol() ,
+ status.internalStatus()+15 ,
+ status.overlayIcons() + QStringList("msn_blocked") ,
+ i18n("%1|Blocked").arg( status.description() ) ) );
+ }
+ else if(!isBlocked() && status.internalStatus() >= 15)
+ { //the user is not blocked, but the status is blocked
+ switch(status.internalStatus()-15)
+ {
+ case 1:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->NLN);
+ break;
+ case 2:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->BSY);
+ break;
+ case 3:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->BRB);
+ break;
+ case 4:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->AWY);
+ break;
+ case 5:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->PHN);
+ break;
+ case 6:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->LUN);
+ break;
+ case 7:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->FLN);
+ break;
+ case 8:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->HDN);
+ break;
+ case 9:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->IDL);
+ break;
+ default:
+ Kopete::Contact::setOnlineStatus(MSNProtocol::protocol()->UNK);
+ break;
+ }
+ }
+ else
+ Kopete::Contact::setOnlineStatus(status);
+ m_currentStatus=status;
+}
+
+void MSNContact::slotSendMail()
+{
+ MSNNotifySocket *notify = static_cast<MSNAccount*>( account() )->notifySocket();
+ if( notify )
+ {
+ notify->sendMail( contactId() );
+ }
+}
+
+void MSNContact::setDisplayPicture(KTempFile *f)
+{
+ //copy the temp file somewere else.
+ // in a better world, the file could be dirrectly wrote at the correct location.
+ // but the custom emoticon code is to deeply merged in the display picture code while it could be separated.
+ QString newlocation=locateLocal( "appdata", "msnpictures/"+ contactId().lower().replace(QRegExp("[./~]"),"-") +".png" ) ;
+
+ KIO::Job *j=KIO::file_move( KURL::fromPathOrURL( f->name() ) , KURL::fromPathOrURL( newlocation ) , -1, true /*overwrite*/ , false /*resume*/ , false /*showProgressInfo*/ );
+
+ f->setAutoDelete(false);
+ delete f;
+
+ //let the time to KIO to copy the file
+ connect(j, SIGNAL(result(KIO::Job *)) , this, SLOT(slotEmitDisplayPictureChanged() ));
+}
+
+void MSNContact::slotEmitDisplayPictureChanged()
+{
+ QString newlocation=locateLocal( "appdata", "msnpictures/"+ contactId().lower().replace(QRegExp("[./~]"),"-") +".png" ) ;
+ setProperty( Kopete::Global::Properties::self()->photo() , newlocation );
+ emit displayPictureChanged();
+}
+
+void MSNContact::setObject(const QString &obj)
+{
+ if(m_obj==obj && (obj.isEmpty() || hasProperty(Kopete::Global::Properties::self()->photo().key())))
+ return;
+
+ m_obj=obj;
+
+ removeProperty( Kopete::Global::Properties::self()->photo() ) ;
+ emit displayPictureChanged();
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if ( config->readNumEntry( "DownloadPicture", 2 ) >= 2 && !obj.isEmpty()
+ && account()->myself()->onlineStatus().status() != Kopete::OnlineStatus::Invisible )
+ manager(Kopete::Contact::CanCreate); //create the manager which will download the photo automatically.
+}
+
+#include "msncontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msncontact.h b/kopete/protocols/msn/msncontact.h
new file mode 100644
index 00000000..bcd6cc68
--- /dev/null
+++ b/kopete/protocols/msn/msncontact.h
@@ -0,0 +1,199 @@
+/*
+ msncontact.h - MSN Contact
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002 by Ryan Cumming <bodnar42@phalynx.dhs.org>
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MSNCONTACT_H
+#define MSNCONTACT_H
+
+#include "kopetecontact.h"
+#include "kopeteonlinestatus.h"
+
+#include <kurl.h>
+
+class QListView;
+class QListViewItem;
+class QPixmap;
+class QTimer;
+
+class MSNChatSession;
+class KAction;
+class KActionCollection;
+class KTempFile;
+
+namespace Kopete { class Protocol; }
+namespace Kopete { class OnlineStatus; }
+
+class MSNContact : public Kopete::Contact
+{
+ Q_OBJECT
+
+public:
+ MSNContact( Kopete::Account *account, const QString &id, Kopete::MetaContact *parent );
+ ~MSNContact();
+
+ /**
+ * Indicate whether this contact is blocked
+ */
+ bool isBlocked() const;
+ void setBlocked( bool b );
+
+ /**
+ * Indicate whether this contact is deleted
+ * (not on the serverside list)
+ */
+ bool isDeleted() const;
+ void setDeleted( bool d );
+
+ /**
+ * Indicate whether this contact is allowed
+ */
+ bool isAllowed() const;
+ void setAllowed( bool d );
+
+ /**
+ * Indicate whether this contact is on the reversed list
+ */
+ bool isReversed() const;
+ void setReversed( bool d );
+
+ /**
+ * set one phone number
+ */
+ void setInfo(const QString &type, const QString &data);
+
+ /**
+ * The groups in which the user is located on the server.
+ */
+ const QMap<QString, Kopete::Group *> serverGroups() const;
+ /**
+ * clear that map
+ */
+ void clearServerGroups();
+
+ /**
+ * client flags (say what version of msn messenger the contact is using)
+ */
+ uint clientFlags() const;
+ void setClientFlags( uint );
+
+ virtual bool isReachable();
+
+ virtual QPtrList<KAction> *customContextMenuActions();
+
+ /**
+ * update the server group map
+ */
+ void contactRemovedFromGroup( const QString& groupId );
+ void contactAddedToGroup(const QString& groupId, Kopete::Group *group );
+
+ virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData );
+
+ /**
+ * Rename contact on server
+ */
+ virtual void rename( const QString &newName ) KDE_DEPRECATED;
+
+ /**
+ * Returns the MSN Message Manager associated with this contact
+ */
+ virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate );
+
+
+ /**
+ * Because blocked contact have a small auto-modified status
+ */
+ void setOnlineStatus(const Kopete::OnlineStatus&);
+
+ QString guid();
+ QString phoneHome();
+ QString phoneWork();
+ QString phoneMobile();
+
+ void setObject(const QString &obj);
+ QString object() const { return m_obj; }
+
+public slots:
+ virtual void slotUserInfo();
+ virtual void deleteContact();
+ virtual void sendFile( const KURL &sourceURL = KURL(),
+ const QString &fileName = QString::null, uint fileSize = 0L );
+
+ /**
+ * Every time the kopete's contactlist is modified, we sync the serverlist with it
+ */
+ virtual void sync( unsigned int cvhanged= 0xff);
+
+
+ void setDisplayPicture(KTempFile *f) ;
+
+signals:
+ void displayPictureChanged();
+
+private slots:
+ void slotBlockUser();
+ void slotShowProfile();
+ void slotSendMail();
+ void slotEmitDisplayPictureChanged();
+
+ /**
+ * Workaround to make this checkboxe readonly
+ */
+ void slotUserInfoDialogReversedToggled();
+
+private:
+ QMap<QString, Kopete::Group *> m_serverGroups;
+
+ bool m_blocked;
+ bool m_allowed;
+ bool m_deleted;
+ bool m_reversed;
+ bool m_moving;
+ bool m_phone_mob;
+
+ uint m_clientFlags;
+
+ QString m_phoneHome;
+ QString m_phoneWork;
+ QString m_phoneMobile;
+
+
+ KAction *actionBlock;
+ KAction *actionShowProfile;
+ KAction *actionSendMail;
+ KAction *actionWebcamReceive;
+ KAction *actionWebcamSend;
+
+ QString m_obj; //the MSNObject
+
+ /**
+ * keep the current status here. (it's normally already in Kopete::Contact::d->onlineStatus)
+ * This is a workaround to prevent problems with the account offline status.
+ */
+ Kopete::OnlineStatus m_currentStatus;
+
+ //MSNProtocol::deserializeContact need to acess some contact insternals
+ friend class MSNProtocol;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msndebugrawcmddlg.cpp b/kopete/protocols/msn/msndebugrawcmddlg.cpp
new file mode 100644
index 00000000..341c6808
--- /dev/null
+++ b/kopete/protocols/msn/msndebugrawcmddlg.cpp
@@ -0,0 +1,73 @@
+/*
+ msndebugrawcmddlg.cpp - Send a raw MSN command for debugging
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 "msndebugrawcmddlg.h"
+
+#include "ui/msndebugrawcommand_base.h"
+
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <ktextedit.h>
+
+#include <klocale.h>
+
+MSNDebugRawCmdDlg::MSNDebugRawCmdDlg( QWidget *parent )
+: KDialogBase( parent, 0L, true,
+ i18n( "DEBUG: Send Raw Command - MSN Plugin" ), Ok | Cancel,
+ Ok, true )
+{
+ setInitialSize( QSize( 350, 200 ) );
+
+ m_main = new MSNDebugRawCommand_base( this );
+ setMainWidget( m_main );
+}
+
+MSNDebugRawCmdDlg::~MSNDebugRawCmdDlg()
+{
+}
+
+QString MSNDebugRawCmdDlg::command()
+{
+ return m_main->m_command->text();
+}
+
+QString MSNDebugRawCmdDlg::params()
+{
+ return m_main->m_params->text();
+}
+
+bool MSNDebugRawCmdDlg::addNewline()
+{
+ return m_main->m_addNewline->isChecked();
+}
+
+bool MSNDebugRawCmdDlg::addId()
+{
+ return m_main->m_addId->isChecked();
+}
+
+QString MSNDebugRawCmdDlg::msg()
+{
+ return m_main->m_msg->text();
+}
+
+#include "msndebugrawcmddlg.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msndebugrawcmddlg.h b/kopete/protocols/msn/msndebugrawcmddlg.h
new file mode 100644
index 00000000..3721daae
--- /dev/null
+++ b/kopete/protocols/msn/msndebugrawcmddlg.h
@@ -0,0 +1,53 @@
+/*
+ msndebugrawcmddlg.h - Send a raw MSN command for debugging
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 MSNDEBUGRAWCMDDLG_H
+#define MSNDEBUGRAWCMDDLG_H
+
+#include <kdialogbase.h>
+
+class MSNDebugRawCommand_base;
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ *
+ * Simple debugging help
+ */
+class MSNDebugRawCmdDlg : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ MSNDebugRawCmdDlg( QWidget *parent );
+ ~MSNDebugRawCmdDlg();
+
+ QString command();
+ QString params();
+ bool addNewline();
+ bool addId();
+ QString msg();
+
+private:
+ MSNDebugRawCommand_base *m_main;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnfiletransfersocket.cpp b/kopete/protocols/msn/msnfiletransfersocket.cpp
new file mode 100644
index 00000000..54a09e4a
--- /dev/null
+++ b/kopete/protocols/msn/msnfiletransfersocket.cpp
@@ -0,0 +1,481 @@
+/***************************************************************************
+ msnfiletransfersocket.cpp - description
+ -------------------
+ begin : mer jui 31 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "msnfiletransfersocket.h"
+
+#include <stdlib.h>
+#include <math.h>
+
+//qt
+#include <qtimer.h>
+
+// kde
+#include <kdebug.h>
+#include <kserversocket.h>
+#include <kbufferedsocket.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+
+#include "kopetetransfermanager.h"
+#include "kopetecontact.h"
+#include "kopetemetacontact.h"
+#include "msnchatsession.h"
+#include "msnswitchboardsocket.h"
+#include "msnnotifysocket.h"
+#include "msnaccount.h"
+
+using namespace KNetwork;
+
+MSNFileTransferSocket::MSNFileTransferSocket(const QString &handle, Kopete::Contact *c,bool incoming, QObject* parent)
+ : MSNSocket(parent) , MSNInvitation(incoming, MSNFileTransferSocket::applicationID() , i18n("File Transfer - MSN Plugin"))
+{
+ m_handle=handle;
+ m_kopeteTransfer=0l;
+ m_file=0L;
+ m_server=0L;
+ m_contact=c;
+ ready=true;
+
+ QObject::connect( this, SIGNAL( socketClosed() ), this, SLOT( slotSocketClosed() ) );
+ QObject::connect( this, SIGNAL( blockRead( const QByteArray & ) ), this, SLOT(slotReadBlock( const QByteArray & ) ) );
+}
+
+MSNFileTransferSocket::~MSNFileTransferSocket()
+{
+ delete m_file;
+ delete m_server;
+ kdDebug(14140) << "MSNFileTransferSocket::~MSNFileTransferSocket" <<endl;
+}
+
+void MSNFileTransferSocket::parseCommand(const QString & cmd, uint id, const QString & data)
+{
+ if( cmd == "VER" )
+ {
+ if(data.section( ' ', 0, 0 ) != "MSNFTP")
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::parseCommand (VER): bad version: disconnect" <<endl;
+ disconnect();
+ }
+ else
+ {
+ if( m_incoming )
+ sendCommand( "USR", m_handle + " " + m_authcook, false );
+ else
+ sendCommand( "VER", "MSNFTP" , false );
+ }
+ }
+ else if( cmd == "FIL" )
+ {
+ m_size=id; //data.toUInt(); //BUG: the size is take as id bye MSNSocket because it is a number
+
+ m_downsize=0;
+ m_file=new QFile(m_fileName);
+
+ if( m_file->open( IO_WriteOnly ))
+ sendCommand( "TFR" ,NULL,false);
+ else
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::parseCommand: ERROR: unable to open file - disconnect " <<endl;
+ disconnect();
+ }
+ }
+ else if( cmd == "BYE" )
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::parseCommand : end of transfer " <<endl;
+ disconnect();
+ }
+ else if( cmd == "USR" )
+ {
+ if(data.section( ' ', 1, 1 )!= m_authcook)
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::parseCommand (USR): bad auth" <<endl;
+ disconnect();
+ }
+ else
+ sendCommand("FIL" , QString::number(size()) , false);
+ }
+ else if( cmd == "TFR" )
+ {
+ m_downsize=0;
+ ready=true;
+ QTimer::singleShot( 0, this, SLOT(slotSendFile()) );
+ }
+ else if( cmd == "CCL" )
+ {
+ disconnect();
+ }
+ else
+ kdDebug(14140) << "MSNFileTransferSocket::parseCommand : unknown command " <<cmd <<endl;
+
+// kdDebug(14140) << "MSNFileTransferSocket::parseCommand : done " <<cmd <<endl;
+}
+
+void MSNFileTransferSocket::doneConnect()
+{
+ if(m_incoming)
+ sendCommand( "VER", "MSNFTP", false );
+ MSNSocket::doneConnect();
+}
+
+void MSNFileTransferSocket::bytesReceived(const QByteArray & head)
+{
+ if(head[0]!='\0')
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::bytesReceived: transfer aborted" <<endl;
+ QTimer::singleShot(0,this,SLOT(disconnect()));
+ }
+ unsigned int sz=(int)((unsigned char)head.data()[2])*256+(int)((unsigned char)head.data()[1]);
+// kdDebug(14140) << "MSNFileTransferSocket::bytesReceived: " << sz <<endl;
+ readBlock(sz);
+}
+
+void MSNFileTransferSocket::slotSocketClosed()
+{
+ kdDebug(14140) << "MSNFileTransferSocket::slotSocketClose "<< endl;
+ if(m_file)
+ m_file->close();
+ delete m_file;
+ m_file=0L;
+ delete m_server;
+ m_server=0L;
+ if(m_kopeteTransfer)
+ {
+ if( (m_downsize!=m_size || m_downsize==0 ) )
+ m_kopeteTransfer->slotError( KIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) );
+ else
+ m_kopeteTransfer->slotComplete();
+ }
+ emit done(this);
+}
+
+void MSNFileTransferSocket::slotReadBlock(const QByteArray &block)
+{
+ m_file->writeBlock( block.data(), block.size() ); // write to file
+
+ m_downsize+=block.size();
+ if(m_kopeteTransfer) m_kopeteTransfer->slotProcessed(m_downsize);
+ kdDebug(14140) << "MSNFileTransferSocket - " << m_downsize << " of " << m_size <<" done"<<endl;
+
+ if(m_downsize==m_size)
+ {
+ //the transfer seems to be finished.
+ sendCommand( "BYE" ,"16777989",false);
+ // if we are not already disconected in 30 seconds, do it.
+ QTimer::singleShot( 30000 , this, SLOT(disconnect() ) );
+
+ }
+}
+
+void MSNFileTransferSocket::setKopeteTransfer(Kopete::Transfer *kt)
+{
+ m_kopeteTransfer=kt;
+ if(kt)
+ {
+ QObject::connect(kt , SIGNAL(transferCanceled()), this, SLOT(abort()));
+ QObject::connect(kt, SIGNAL(destroyed()) , this , SLOT(slotKopeteTransferDestroyed()));
+ }
+}
+
+void MSNFileTransferSocket::listen(int port)
+{
+ m_server = new KServerSocket();
+
+ QObject::connect( m_server, SIGNAL(readyAccept()), this, SLOT(slotAcceptConnection()));
+ m_server->setAddress(QString::number(port));
+
+ kdDebug(14140) << "MSNFileTransferSocket::listen: about to listen"<<endl;
+ bool listenResult = m_server->listen(1);
+ kdDebug(14140) << "MSNFileTransferSocket::listen: result: "<< listenResult <<endl;
+ QTimer::singleShot( 60000, this, SLOT(slotTimer()) );
+ kdDebug(14140) << "MSNFileTransferSocket::listen done" <<endl;
+}
+
+void MSNFileTransferSocket::slotAcceptConnection()
+{
+ kdDebug(14140) << "MSNFileTransferSocket::slotAcceptConnection" <<endl;
+ if(!accept(m_server))
+ {
+ if( m_kopeteTransfer)
+ m_kopeteTransfer->slotError( KIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) );
+ emit done(this);
+ }
+}
+
+void MSNFileTransferSocket::slotTimer()
+{
+ if(onlineStatus() != Disconnected)
+ return;
+ kdDebug(14140) << "MSNFileTransferSocket::slotTimer: timeout "<< endl;
+ if( m_kopeteTransfer)
+ {
+ m_kopeteTransfer->slotError( KIO::ERR_CONNECTION_BROKEN , i18n( "Connection timed out" ) );
+ }
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+ if(manager && manager->service())
+ manager->service()->sendCommand( "MSG" , "N", true, rejectMessage("TIMEOUT") );
+
+ emit done(this);
+}
+
+void MSNFileTransferSocket::abort()
+{
+ if(m_incoming)
+ {
+ sendCommand( "CCL" , NULL ,false);
+ }
+ else
+ {
+ QByteArray bytes(3);
+ bytes[0]='\1';
+ bytes[1]='\0';
+ bytes[2]='\0';
+ sendBytes( bytes );
+ m_downsize=m_size; //we don't want to send data anymore;
+ }
+ //the timer wait one second, the time to send the CCL or the binary header
+ //retarding the disconnection keep away from a crash. (in KIO::Job::emitResult when `delete this`)
+ QTimer::singleShot( 1000, this, SLOT(disconnect()) );
+ ready=false;
+}
+
+void MSNFileTransferSocket::setFile( const QString &fn, long unsigned int fileSize )
+{
+ m_fileName=fn;
+ if(!m_incoming)
+ {
+ if(m_file)
+ {
+ kdDebug(14140) << "MSNFileTransferSocket::setFileName: WARNING m_file already exists" << endl;
+ delete m_file;
+ }
+ m_file = new QFile( fn );
+ if(!m_file->open(IO_ReadOnly))
+ {
+ //FIXME: abort transfer here
+ kdDebug(14140) << "MSNFileTransferSocket::setFileName: WARNING unable to open the file" << endl;
+ }
+
+ //If the fileSize is 0 it was not given, we are to get it from the file
+ if(fileSize == 0L)
+ m_size = m_file->size();
+ else
+ m_size = fileSize;
+ }
+}
+
+
+void MSNFileTransferSocket::slotSendFile()
+{
+// kdDebug(14140) <<"MSNFileTransferSocket::slotSendFile()" <<endl;
+ if( m_downsize >= m_size)
+ {
+ //the transfer seems to be finished.
+ // if we are not already disconected in 30 seconds, do it.
+ QTimer::singleShot( 30000 , this, SLOT(disconnect() ) );
+ return;
+ }
+
+ if(ready)
+ {
+ char data[2046];
+ int bytesRead = m_file->readBlock( data, 2045 );
+
+ QByteArray block(bytesRead+3);
+// char i1= (char)fmod( bytesRead, 256 ) ;
+// char i2= (char)floor( bytesRead / 256 ) ;
+// kdDebug(14140) << "MSNFileTransferSocket::slotSendFile: " << (int)i1 <<" + 256* "<< (int)i2 <<" = " << bytesRead <<endl;
+ block[0]='\0';
+ block[1]= (char)fmod( bytesRead, 256 );
+ block[2]= (char)floor( bytesRead / 256 );
+
+ for ( int f = 0; f < bytesRead; f++ )
+ {
+ block[f+3] = data[f];
+ }
+
+ sendBytes(block);
+
+ m_downsize+=bytesRead;
+ if(m_kopeteTransfer)
+ m_kopeteTransfer->slotProcessed(m_downsize);
+ kdDebug(14140) << "MSNFileTransferSocket::slotSendFile: " << m_downsize << " of " << m_size <<" done"<<endl;
+ }
+ ready=false;
+
+ QTimer::singleShot( 10, this, SLOT(slotSendFile()) );
+}
+
+void MSNFileTransferSocket::slotReadyWrite()
+{
+ ready=true;
+ MSNSocket::slotReadyWrite();
+}
+
+QString MSNFileTransferSocket::invitationHead()
+{
+ QTimer::singleShot( 10 * 60000, this, SLOT(slotTimer()) ); //the user has 10 mins to accept or refuse or initiate the transfer
+
+ return QString( MSNInvitation::invitationHead()+
+ "Application-File: "+ m_fileName.right( m_fileName.length() - m_fileName.findRev( '/' ) - 1 ) +"\r\n"
+ "Application-FileSize: "+ QString::number(size()) +"\r\n\r\n").utf8();
+}
+
+void MSNFileTransferSocket::parseInvitation(const QString& msg)
+{
+ QRegExp rx("Invitation-Command: ([A-Z]*)");
+ rx.search(msg);
+ QString command=rx.cap(1);
+ if( msg.contains("Invitation-Command: INVITE") )
+ {
+ rx=QRegExp("Application-File: ([^\\r\\n]*)");
+ rx.search(msg);
+ QString filename = rx.cap(1);
+ rx=QRegExp("Application-FileSize: ([0-9]*)");
+ rx.search(msg);
+ unsigned long int filesize= rx.cap(1).toUInt();
+
+ MSNInvitation::parseInvitation(msg); //for the cookie
+
+ Kopete::TransferManager::transferManager()->askIncomingTransfer( m_contact , filename, filesize, QString::null, QString::number( cookie() ) );
+
+ QObject::connect( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString& ) ),this, SLOT( slotFileTransferAccepted( Kopete::Transfer *, const QString& ) ) );
+ QObject::connect( Kopete::TransferManager::transferManager(), SIGNAL( refused( const Kopete::FileTransferInfo & ) ), this, SLOT( slotFileTransferRefused( const Kopete::FileTransferInfo & ) ) );
+
+ }
+ else if( msg.contains("Invitation-Command: ACCEPT") )
+ {
+ if(incoming())
+ {
+ rx=QRegExp("IP-Address: ([0-9\\.]*)");
+ rx.search(msg);
+ QString ip_address = rx.cap(1);
+ rx=QRegExp("AuthCookie: ([0-9]*)");
+ rx.search(msg);
+ QString authcook = rx.cap(1);
+ rx=QRegExp("Port: ([0-9]*)");
+ rx.search(msg);
+ QString port = rx.cap(1);
+
+ setAuthCookie(authcook);
+ connect(ip_address, port.toUInt());
+ }
+ else
+ {
+ unsigned long int auth = (rand()%(999999))+1;
+ setAuthCookie(QString::number(auth));
+
+ setKopeteTransfer(Kopete::TransferManager::transferManager()->addTransfer(m_contact, fileName(), size(), m_contact->metaContact() ? m_contact->metaContact()->displayName() : m_contact->contactId() , Kopete::FileTransferInfo::Outgoing));
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+ if(manager && manager->service())
+ {
+ MSNNotifySocket *notify=static_cast<MSNAccount*>(manager->account())->notifySocket();
+ if(notify){
+
+ QCString message=QString(
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: ACCEPT\r\n"
+ "Invitation-Cookie: " + QString::number(cookie()) + "\r\n"
+ "IP-Address: " + notify->localIP() + "\r\n"
+ "Port: 6891\r\n"
+ "AuthCookie: "+QString::number(auth)+"\r\n"
+ "Launch-Application: FALSE\r\n"
+ "Request-Data: IP-Address:\r\n\r\n").utf8();
+
+ manager->service()->sendCommand( "MSG" , "N", true, message );
+ }
+ }
+
+ listen(6891);
+ }
+ }
+ else //CANCEL
+ {
+ MSNInvitation::parseInvitation(msg);
+ if( m_kopeteTransfer)
+ m_kopeteTransfer->slotError( KIO::ERR_ABORTED , i18n( "The remote user aborted" ) );
+ emit done(this);
+
+ }
+}
+
+void MSNFileTransferSocket::slotFileTransferAccepted(Kopete::Transfer *trans, const QString& fileName)
+{
+ if(trans->info().internalId().toULong() != cookie())
+ return;
+
+ if(!trans->info().contact())
+ return;
+
+ setKopeteTransfer(trans);
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+
+ if(manager && manager->service())
+ {
+ setFile(fileName);
+
+ QCString message=QString(
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: ACCEPT\r\n"
+ "Invitation-Cookie: " + QString::number(cookie()) + "\r\n"
+ "Launch-Application: FALSE\r\n"
+ "Request-Data: IP-Address:\r\n" ).utf8();
+ manager->service()->sendCommand( "MSG" , "N", true, message );
+
+ QTimer::singleShot( 3 * 60000, this, SLOT(slotTimer()) ); //if after 3 minutes the transfer has not begin, delete this
+ }
+ else
+ {
+ if( m_kopeteTransfer)
+ m_kopeteTransfer->slotError( KIO::ERR_UNKNOWN , i18n( "An unknown error occurred" ) );
+ emit done(this);
+
+ }
+}
+
+void MSNFileTransferSocket::slotFileTransferRefused(const Kopete::FileTransferInfo &info)
+{
+ if(info.internalId().toULong() != cookie())
+ return;
+
+ if(!info.contact())
+ return;
+
+ MSNChatSession* manager=dynamic_cast<MSNChatSession*>(m_contact->manager());
+
+ if(manager && manager->service())
+ {
+ manager->service()->sendCommand( "MSG" , "N", true, rejectMessage() );
+ }
+
+ emit done(this);
+}
+
+void MSNFileTransferSocket::slotKopeteTransferDestroyed()
+{
+ m_kopeteTransfer=0L;
+}
+
+
+#include "msnfiletransfersocket.moc"
+
diff --git a/kopete/protocols/msn/msnfiletransfersocket.h b/kopete/protocols/msn/msnfiletransfersocket.h
new file mode 100644
index 00000000..82ae0662
--- /dev/null
+++ b/kopete/protocols/msn/msnfiletransfersocket.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ msnfiletransfersocket.h - description
+ -------------------
+ begin : mer jui 31 2002
+ copyright : (C) 2002 by Olivier Goffart
+ email : ogoffart @ kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef MSNFILETRANSFERSOCKET_H
+#define MSNFILETRANSFERSOCKET_H
+
+#include <qwidget.h>
+
+#include "msnsocket.h"
+#include "msninvitation.h"
+
+class QFile;
+
+namespace KNetwork {
+ class KServerSocket;
+}
+
+namespace Kopete { class Transfer; }
+namespace Kopete { class FileTransferInfo; }
+namespace Kopete { class Protocol; }
+namespace Kopete { class Contact; }
+
+/**
+ * @author Olivier Goffart
+ */
+class MSNFileTransferSocket : public MSNSocket , public MSNInvitation
+{
+ Q_OBJECT
+
+public:
+ MSNFileTransferSocket(const QString &myID,Kopete::Contact* c, bool incoming, QObject* parent = 0L );
+ ~MSNFileTransferSocket();
+
+ static QString applicationID() { return "5D3E02AB-6190-11d3-BBBB-00C04F795683"; }
+ QString invitationHead();
+
+
+ void setKopeteTransfer( Kopete::Transfer *kt );
+ Kopete::Transfer* kopeteTransfer() { return m_kopeteTransfer; }
+ void setFile( const QString &fn, long unsigned int fileSize = 0L );
+ void setAuthCookie( const QString &c ) { m_authcook = c; }
+ QString fileName() { return m_fileName;}
+ long unsigned int size() { return m_size;}
+ void listen( int port );
+
+ virtual void parseInvitation(const QString& invitation);
+
+ virtual QObject* object() { return this; }
+
+public slots:
+ void abort();
+
+signals:
+ void done( MSNInvitation * );
+
+protected:
+ /**
+ * This reimplementation sets up the negotiating with the server and
+ * suppresses the change of the status to online until the handshake
+ * is complete.
+ */
+ virtual void doneConnect();
+
+ /**
+ * Handle an MSN command response line.
+ */
+ virtual void parseCommand(const QString & cmd, uint id, const QString & data);
+ virtual void bytesReceived(const QByteArray & data);
+
+protected slots:
+ virtual void slotReadyWrite();
+
+private slots:
+ void slotSocketClosed();
+ void slotReadBlock(const QByteArray &);
+ void slotAcceptConnection();
+ void slotTimer();
+ void slotSendFile();
+
+ void slotFileTransferRefused( const Kopete::FileTransferInfo &info );
+ void slotFileTransferAccepted( Kopete::Transfer *trans, const QString& fileName );
+ /* the Kopete::Transfer has been deleted */
+ void slotKopeteTransferDestroyed();
+
+
+private:
+ QString m_handle;
+ Kopete::Contact *m_contact;
+
+ long unsigned int m_size;
+ long unsigned int m_downsize;
+ QString m_authcook;
+ QString m_fileName;
+ Kopete::Transfer* m_kopeteTransfer;
+ QFile *m_file ;
+ KNetwork::KServerSocket *m_server;
+
+ bool ready;
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/msn/msninvitation.cpp b/kopete/protocols/msn/msninvitation.cpp
new file mode 100644
index 00000000..ddc8136a
--- /dev/null
+++ b/kopete/protocols/msn/msninvitation.cpp
@@ -0,0 +1,100 @@
+/*
+ msninvitation.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "msninvitation.h"
+#include <stdlib.h>
+#include <qregexp.h>
+
+MSNInvitation::MSNInvitation(bool incoming, const QString &applicationID , const QString &applicationName)
+{
+ m_incoming=incoming;
+ m_applicationId=applicationID;
+ m_applicationName=applicationName;
+ m_cookie= (rand()%(999999))+1;
+ m_state = Nothing;
+}
+
+
+MSNInvitation::~MSNInvitation()
+{
+}
+
+QCString MSNInvitation::unimplemented(long unsigned int cookie)
+{
+ return QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: CANCEL\r\n"
+ "Cancel-Code: REJECT_NOT_INSTALLED\r\n"
+ "Invitation-Cookie: " + QString::number(cookie) + "\r\n"
+ "Session-ID: {120019D9-C3F5-4F94-978D-CB33534C3309}\r\n\r\n").utf8();
+ //FIXME: i don't know at all what Seession-ID is
+}
+
+QString MSNInvitation::invitationHead()
+{
+ setState(Invited);
+ return QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Application-Name: " + m_applicationName + "\r\n"
+ "Application-GUID: {" + m_applicationId + "}\r\n"
+ "Invitation-Command: INVITE\r\n"
+ "Invitation-Cookie: " +QString::number(m_cookie) +"\r\n");
+}
+
+QCString MSNInvitation::rejectMessage(const QString & rejectcode)
+{
+ return QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n"
+ "\r\n"
+ "Invitation-Command: CANCEL\r\n"
+ "Invitation-Cookie: " + QString::number(cookie()) + "\r\n"
+ "Cancel-Code: "+ rejectcode +"\r\n").utf8();
+}
+
+void MSNInvitation::parseInvitation(const QString& msg)
+{
+ QRegExp rx("Invitation-Command: ([A-Z]*)");
+ rx.search(msg);
+ QString command=rx.cap(1);
+
+ if(command=="INVITE")
+ {
+ rx=QRegExp("Invitation-Cookie: ([0-9]*)");
+ rx.search(msg);
+ m_cookie=rx.cap(1).toUInt();
+ }
+ else if(command=="CANCEL")
+ {
+ /*rx=QRegExp("Cancel-Code: ([0-9]*)");
+ rx.search(msg);
+ QString code=rx.cap(1).toUInt();
+ //TODO: parse the code*/
+ }
+// else if(command=="ACCEPT")
+}
+
+MSNInvitation::State MSNInvitation::state()
+{
+ return m_state;
+}
+
+void MSNInvitation::setState(MSNInvitation::State s)
+{
+ m_state=s;
+}
+
diff --git a/kopete/protocols/msn/msninvitation.h b/kopete/protocols/msn/msninvitation.h
new file mode 100644
index 00000000..90010dc3
--- /dev/null
+++ b/kopete/protocols/msn/msninvitation.h
@@ -0,0 +1,126 @@
+/*
+ msninvitation.cpp
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MSNINVITATION_H
+#define MSNINVITATION_H
+
+#include <qstring.h>
+
+#include "kopete_export.h"
+
+class QObject;
+
+/**
+ * @author Olivier Goffart
+ *
+ * The invitation is the base class which handle an MSN invitation.
+ * The implemented class must to herits from QObject too.
+ * You can accept the invitation by catching @ref MSNProtocol::invitation() signals
+ * or create one and insert it to a kmm with @ref MSNChatSession::initInvitation()
+ * you can add action with @ref Kopete::Plugin::customChatActions()
+ */
+class KOPETE_EXPORT MSNInvitation
+{
+public:
+ /**
+ * Constructor
+ * @param incoming say if it is an incoming invitation
+ * @param applicationID is the exadecimal id of the invitation
+ * @param applicationName is a i18n'ed string of the name of the application
+ */
+ MSNInvitation(bool incoming,const QString &applicationID , const QString &applicationName);
+ virtual ~MSNInvitation();
+
+ /**
+ * @internal
+ * it is a reject invitation because the invitation is not implemented
+ */
+ static QCString unimplemented(long unsigned int cookie);
+
+ /**
+ * you can set manualy the cookie. note that a cookie is automatically generated when a new
+ * invitation is created, or in @ref parseInvitation
+ */
+ void setCookie( long unsigned int c ) { m_cookie = c; }
+ /**
+ * @return the cookie
+ */
+ long unsigned int cookie() { return m_cookie; }
+
+ /**
+ * @return true if it is an incommijng invitation
+ */
+ bool incoming() { return m_incoming; }
+
+
+ /**
+ * reimplement this. this is the invitation string used in @ref MSNChatSession::initInvitation()
+ * the default implementation return the common begin.
+ * You can also set the state to Invited (the default implementation do that)
+ */
+ virtual QString invitationHead();
+
+ /**
+ * This is the reject invitation string
+ * @param rejectcode is the code, it can be "REJECT" or "TIMEOUT"
+ */
+ QCString rejectMessage(const QString & rejectcode = "REJECT");
+
+ /**
+ * reimplement this method. it is called when an invitation message with the invitation's cookie is received
+ * the default implementation parse the cookie, or the reject message
+ */
+ virtual void parseInvitation(const QString& invitation);
+
+ /**
+ * return the qobject (this)
+ */
+ virtual QObject* object()=0;
+//signals:
+ /**
+ * reimplement this as a signal, and emit it when the invitation has to be destroyed.
+ * don't delete the invitation yourself
+ */
+ virtual void done(MSNInvitation*)=0;
+
+ /**
+ * This indiquate the state. it is going to be completed later
+ * - Nothing means than nothing has been done in the invitaiton (nothing has been sent/received)
+ * - Invited means than the invitaiton has been sent
+ */
+ enum State { Nothing=0 , Invited=1 };
+ /**
+ * retrun the current state
+ */
+ State state();
+ /**
+ * set the current State
+ */
+ void setState(State);
+
+
+
+protected:
+ bool m_incoming;
+ long unsigned int m_cookie;
+ QString m_applicationId;
+ QString m_applicationName;
+ State m_state;
+
+
+};
+
+#endif
diff --git a/kopete/protocols/msn/msnnotifysocket.cpp b/kopete/protocols/msn/msnnotifysocket.cpp
new file mode 100644
index 00000000..e31e0257
--- /dev/null
+++ b/kopete/protocols/msn/msnnotifysocket.cpp
@@ -0,0 +1,1309 @@
+/*
+ msnnotifysocket.cpp - Notify Socket for the MSN Protocol
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions taken from
+ KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 "msnnotifysocket.h"
+#include "msncontact.h"
+#include "msnaccount.h"
+#include "msnsecureloginhandler.h"
+#include "msnchallengehandler.h"
+
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <qdom.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <klocale.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+#include <krun.h>
+#include <kio/job.h>
+#include <qfile.h>
+#include <kconfig.h>
+#include <knotification.h>
+
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+
+#include <ctime>
+
+
+MSNNotifySocket::MSNNotifySocket( MSNAccount *account, const QString& /*msnId*/, const QString &password )
+: MSNSocket( account )
+{
+ m_newstatus = MSNProtocol::protocol()->NLN;
+ m_secureLoginHandler=0L;
+ m_challengeHandler = 0L;
+
+ m_isHotmailAccount=false;
+ m_ping=false;
+ m_disconnectReason=Kopete::Account::Unknown;
+
+ m_account = account;
+ m_password=password;
+ QObject::connect( this, SIGNAL( blockRead( const QByteArray & ) ),
+ this, SLOT( slotReadMessage( const QByteArray & ) ) );
+ m_keepaliveTimer = 0L;
+ m_isLogged = false;
+}
+
+MSNNotifySocket::~MSNNotifySocket()
+{
+ delete m_secureLoginHandler;
+ delete m_challengeHandler;
+
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+void MSNNotifySocket::doneConnect()
+{
+// kdDebug( 14140 ) << k_funcinfo << "Negotiating server protocol version" << endl;
+ sendCommand( "VER", "MSNP11 MSNP10 CVR0" );
+}
+
+
+void MSNNotifySocket::disconnect()
+{
+ m_isLogged = false;
+ if( m_disconnectReason==Kopete::Account::Unknown )
+ m_disconnectReason=Kopete::Account::Manual;
+ if( onlineStatus() == Connected )
+ sendCommand( "OUT", QString::null, false );
+
+ if( m_keepaliveTimer )
+ m_keepaliveTimer->stop();
+
+ // the socket is not connected yet, so I should force the signals
+ if ( onlineStatus() == Disconnected || onlineStatus() == Connecting )
+ emit socketClosed();
+ else
+ MSNSocket::disconnect();
+}
+
+void MSNNotifySocket::handleError( uint code, uint id )
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ QString handle;
+ if(m_tmpHandles.contains(id))
+ handle=m_tmpHandles[id];
+
+ QString msg;
+ MSNSocket::ErrorType type;
+ // See http://www.hypothetic.org/docs/msn/basics.php for a
+ // description of all possible error codes.
+ // TODO: Add support for all of these!
+ switch( code )
+ {
+ case 201:
+ case 205:
+ case 208:
+ {
+ msg = i18n( "<qt>The MSN user '%1' does not exist.<br>Please check the MSN ID.</qt>" ).arg( handle );
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 207:
+ case 218:
+ case 540:
+ {
+ msg = i18n( "<qt>An internal error occurred in the MSN plugin.<br>"
+ "MSN Error: %1<br>"
+ "please send us a detailed bug report "
+ "at kopete-devel@kde.org containing the raw debug output on the "
+ "console (in gzipped format, as it is probably a lot of output.)" ).arg(code);
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 209:
+ {
+ if(handle==m_account->accountId())
+ {
+ msg = i18n( "Unable to change your display name.\n"
+ "Please ensure your display is not too long and does not contains censored words." );
+ type = MSNSocket::ErrorServerError;
+ }
+ /*else
+ {
+ QString msg = i18n( "You are trying to change the display name of a user who has not "
+ "confirmed his or her email address;\n"
+ "the contact was not renamed on the server." );
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error, msg, i18n( "MSN Plugin" ) );
+ }*/
+ break;
+ }
+ case 210:
+ {
+ msg = i18n("Your contact list is full; you cannot add any new contacts.");
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 215:
+ {
+ msg = i18n( "<qt>The user '%1' already exists in this group on the MSN server;<br>"
+ "if Kopete does not show the user, please send us a detailed bug report "
+ "at kopete-devel@kde.org containing the raw debug output on the "
+ "console (in gzipped format, as it is probably a lot of output.)</qt>" ).arg(handle);
+ type = MSNSocket::ErrorInformation;
+ break;
+ }
+ case 216:
+ {
+ //This might happen is you rename an user if he is not in the contactlist
+ //currently, we just iniore;
+ //TODO: try to don't rename user not in the list
+ //actualy, the bug is in MSNChatSession::slotUserJoined()
+ break;
+ }
+ case 219:
+ {
+ msg = i18n( "The user '%1' seems to already be blocked or allowed on the server." ).arg(handle);
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 223:
+ {
+ msg = i18n( "You have reached the maximum number of groups:\n"
+ "MSN does not support more than 30 groups." );
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 224:
+ case 225:
+ case 230:
+ {
+ msg = i18n("Kopete is trying to perform an operation on a group or a contact that does not exists on the server.\n"
+ "This might happen if the Kopete contact list and the MSN-server contact list are not correctly synchronized; if this is the case, you probably should send a bug report.");
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+
+ case 229:
+ {
+ msg = i18n("The group name is too long; it has not been changed on the MSN server.");
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 710:
+ {
+ msg = i18n( "You cannot open a Hotmail inbox because you do not have an MSN account with a valid "
+ "Hotmail or MSN mailbox." );
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 715:
+ {
+ /*
+ //if(handlev==m_account->accountId())
+ QString msg = i18n( "Your email address has not been verified with the MSN server.\n"
+ "You should have received a mail with a link to confirm your email address.\n"
+ "Some functions will be restricted if you do not confirm your email address." );
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, msg, i18n( "MSN Plugin" ) );//TODO don't show again
+ */
+ break;
+ }
+ case 800:
+ {
+ //This happen when too much commends are sent to the server.
+ //the command will not be executed, too bad.
+ // ignore it for now, as we don't really know what command it was.
+ /* QString msg = i18#n( "You are trying to change your status, or your display name too rapidly.\n"
+ "This might happen if you added yourself to your own contact list." );
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry, msg, i18n( "MSN Plugin" ) );
+ //FIXME: try to fix this problem*/
+ break;
+ }
+ case 911:
+ m_disconnectReason=Kopete::Account::BadPassword;
+ disconnect();
+ break;
+ case 913:
+ {
+ msg = i18n( "You can not send messages when you are offline or when you are invisible." );
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+ case 923:
+ {
+ msg = i18n( "You are trying to perform an action you are not allowed to perform in 'kid mode'." );
+ type = MSNSocket::ErrorServerError;
+ break;
+ }
+
+ default:
+ MSNSocket::handleError( code, id );
+ break;
+ }
+
+ if( !msg.isEmpty() )
+ emit errorMessage( type, msg );
+}
+
+void MSNNotifySocket::parseCommand( const QString &cmd, uint id, const QString &data )
+{
+ //kdDebug(14140) << "MSNNotifySocket::parseCommand: Command: " << cmd << endl;
+
+ if ( cmd == "VER" )
+ {
+ sendCommand( "CVR", "0x0409 winnt 5.1 i386 MSNMSGR 7.0.0816 MSMSGS " + m_account->accountId() );
+/*
+ struct utsname utsBuf;
+ uname ( &utsBuf );
+
+ sendCommand( "CVR", i18n( "MS Local code, see http://www.microsoft.com/globaldev/reference/oslocversion.mspx", "0x0409" ) +
+ " " + escape( utsBuf.sysname ) + " " + escape( utsBuf.release ) + " " + escape( utsBuf.machine ) + " Kopete " +
+ escape( kapp->aboutData()->version() ) + " Kopete " + m_msnId );
+*/
+ }
+ else if ( cmd == "CVR" ) //else if ( cmd == "INF" )
+ {
+ sendCommand( "USR", "TWN I " + m_account->accountId() );
+ }
+ else if( cmd == "USR" ) //// here follow the auth processus
+ {
+ if( data.section( ' ', 1, 1 ) == "S" )
+ {
+ m_secureLoginHandler = new MSNSecureLoginHandler(m_account->accountId(), m_password, data.section( ' ' , 2 , 2 ));
+
+ QObject::connect(m_secureLoginHandler, SIGNAL(loginFailed()), this, SLOT(sslLoginFailed()));
+ QObject::connect(m_secureLoginHandler, SIGNAL(loginBadPassword()), this, SLOT(sslLoginIncorrect()));
+ QObject::connect(m_secureLoginHandler, SIGNAL(loginSuccesful(QString )), this, SLOT(sslLoginSucceeded(QString )));
+
+ m_secureLoginHandler->login();
+ }
+ else
+ {
+ // Successful authentication.
+ m_disconnectReason=Kopete::Account::Unknown;
+
+ // Synchronize with the server.
+ QString lastSyncTime, lastChange;
+
+ if(m_account->contacts().count() > 1)
+ {
+ // Retrieve the last synchronization timestamp, and last change timestamp.
+ lastSyncTime = m_account->configGroup()->readEntry("lastsynctime", "0");
+ lastChange = m_account->configGroup()->readEntry("lastchange", "0");
+ }
+ else
+ {
+ //the contactliust has maybe being removed, force to sync
+ //(the only contact is myself)
+ lastSyncTime="0";
+ lastChange="0";
+ }
+
+ sendCommand( "SYN", lastChange + " " + lastSyncTime);
+ // Get client features.
+ if(!useHttpMethod()) {
+ sendCommand( "GCF", "Shields.xml");
+ // We are connected start to ping
+ slotSendKeepAlive();
+ }
+ }
+ }
+ else if( cmd == "LST" )
+ {
+ // MSNP11 changed command. Now it's:
+ // LST N=passport@hotmail.com F=Display%20Name C=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 13 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ // But can be
+ // LST N=passport@hotmail.com 10
+ QString publicName, contactGuid, groups;
+ uint lists;
+
+ QRegExp regex("N=([^ ]+)(?: F=([^ ]+))?(?: C=([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}))? (\\d+)\\s?((?:[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12},?)*)$");
+ regex.search(data);
+
+ // Capture passport email.
+ m_tmpLastHandle = regex.cap(1);
+ // Capture public name.
+ publicName = unescape( regex.cap(2) );
+ // Capture contact guid.
+ contactGuid = regex.cap(3);
+ // Capture list enum type.
+ lists = regex.cap(4).toUInt();
+ // Capture contact group(s) guid(s)
+ groups = regex.cap(5);
+
+// kdDebug(14140) << k_funcinfo << " msnId: " << m_tmpLastHandle << " publicName: " << publicName << " contactGuid: " << contactGuid << " list: " << lists << " groupGuid: " << groups << endl;
+
+ // handle, publicName, Contact GUID, lists, Group GUID
+ emit contactList( m_tmpLastHandle , publicName, contactGuid, lists, groups );
+ }
+ else if( cmd == "GCF" )
+ {
+ m_configFile = data.section(' ', 0, 0);
+ readBlock( data.section( ' ', 1, 1 ).toUInt() );
+ }
+ else if( cmd == "MSG" )
+ {
+ readBlock( data.section( ' ', 2, 2 ).toUInt() );
+ }
+ else if( cmd == "ILN" || cmd == "NLN" )
+ {
+ // status handle publicName strangeNumber MSNOBJECT
+ MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ data.section( ' ', 1, 1 ) ] );
+ if( c && c->contactId() != m_account->accountId() )
+ {
+ QString publicName=unescape( data.section( ' ', 2, 2 ) );
+ if ( (publicName!=c->contactId() || c->hasProperty(Kopete::Global::Properties::self()->nickName().key()) ) &&
+ publicName!=c->property( Kopete::Global::Properties::self()->nickName()).value().toString() )
+
+ changePublicName(publicName,c->contactId());
+ QString obj=unescape(data.section( ' ', 4, 4 ));
+ c->setObject( obj );
+ c->setOnlineStatus( convertOnlineStatus( data.section( ' ', 0, 0 ) ) );
+ c->setClientFlags(data.section( ' ', 3, 3 ).toUInt());
+ }
+ }
+ else if( cmd == "UBX" )
+ {
+ m_tmpLastHandle = data.section(' ', 0, 0);
+ uint length = data.section( ' ', 1, 1 ).toUInt();
+ if(length > 0) {
+ readBlock( length );
+ }
+ }
+ else if( cmd == "UUX" )
+ {
+ // UUX is sended to acknowledge that the server has received and processed the personal Message.
+ // if the result is 0, set the myself() contact personalMessage.
+ if( data.section(' ', 0, 0) == QString::fromUtf8("0") )
+ m_account->myself()->setProperty(MSNProtocol::protocol()->propPersonalMessage, m_propertyPersonalMessage);
+ }
+ else if( cmd == "FLN" )
+ {
+ MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ data.section( ' ', 0, 0 ) ] );
+ if( c && c->contactId() != m_account->accountId() )
+ {
+ c->setOnlineStatus( MSNProtocol::protocol()->FLN );
+ c->removeProperty( MSNProtocol::protocol()->propClient );
+ }
+ }
+ else if( cmd == "XFR" )
+ {
+ QString stype=data.section( ' ', 0, 0 );
+ if( stype=="SB" ) //switchboard connection (chat)
+ {
+ // Address, AuthInfo
+ emit startChat( data.section( ' ', 1, 1 ), data.section( ' ', 3, 3 ) );
+ }
+ else if( stype=="NS" ) //notifysocket ; Got our notification server
+ { //we are connecting and we receive the initial NS, or the msn server encounter a problem, and we are switching to another switchboard
+ QString host = data.section( ' ', 1, 1 );
+ QString server = host.section( ':', 0, 0 );
+ uint port = host.section( ':', 1, 1 ).toUInt();
+ setOnlineStatus( Connected );
+ emit receivedNotificationServer( server, port );
+ disconnect();
+ }
+
+ }
+ else if( cmd == "RNG" )
+ {
+ // SessionID, Address, AuthInfo, handle, publicName
+ emit invitedToChat( QString::number( id ), data.section( ' ', 0, 0 ), data.section( ' ', 2, 2 ),
+ data.section( ' ', 3, 3 ), unescape( data.section( ' ', 4, 4 ) ) );
+ }
+ else if( cmd == "ADC" )
+ {
+ QString msnId, list, publicName, contactGuid, groupGuid;
+
+ // Retrieve the list parameter (FL/AL/BL/RL)
+ list = data.section( ' ', 0, 0 );
+
+ // Examples of received data
+ // ADC TrID xL N=example@passport.com
+ // ADC TrID FL C=contactGuid groupdGuid
+ // ADC TrID RL N=example@passport.com F=friednly%20name
+ // ADC TrID FL N=ex@pas.com F=My%20Name C=contactGuid
+ // Thanks Gregg for that complex RegExp.
+ QRegExp regex("(?:N=([^ ]+))?(?: F=([^ ]+))?(?: C=([0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}))?\\s?((?:[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12},?)*)$");
+ regex.search( data.section( ' ', 1 ) );
+
+ // Capture passport email.
+ msnId = regex.cap(1);
+ // Capture public name.
+ publicName = unescape( regex.cap(2) );
+ // Capture contact guid.
+ contactGuid = regex.cap(3);
+ // Capture contact group(s) guid(s)
+ groupGuid = regex.cap(4);
+
+// kdDebug(14140) << k_funcinfo << list << " msnId: " << msnId << " publicName: " << publicName << " contactGuid: " << contactGuid << " groupGuid: " << groupGuid << endl;
+
+ // handle, list, publicName, contactGuid, groupGuid
+ emit contactAdded( msnId, list, publicName, contactGuid, groupGuid );
+ }
+ else if( cmd == "REM" ) // someone is removed from a list
+ {
+ QString handle, list, contactGuid, groupGuid;
+ list = data.section( ' ', 0, 0 );
+ if( list == "FL" )
+ {
+ // Removing a contact
+ if( data.contains( ' ' ) < 2 )
+ {
+ contactGuid = data.section( ' ', 1, 1 );
+ }
+ // Removing a contact from a group
+ else if( data.contains( ' ' ) < 3 )
+ {
+ contactGuid = data.section( ' ', 1, 1 );
+ groupGuid = data.section( ' ', 2, 2 );
+ }
+ }
+ else
+ {
+ handle = data.section( ' ', 1, 1);
+ }
+
+ // handle, list, contactGuid, groupGuid
+ emit contactRemoved( handle, list, contactGuid, groupGuid );
+
+ }
+ else if( cmd == "OUT" )
+ {
+ if( data.section( ' ', 0, 0 ) == "OTH" )
+ {
+ m_disconnectReason=Kopete::Account::OtherClient;
+ }
+ disconnect();
+ }
+ else if( cmd == "CHG" )
+ {
+ QString status = data.section( ' ', 0, 0 );
+ setOnlineStatus( Connected );
+ emit statusChanged( convertOnlineStatus( status ) );
+ }
+ else if( cmd == "SBP" )
+ {
+ QString contactGuid, type, publicName;
+ contactGuid = data.section( ' ', 0, 0 );
+ type = data.section( ' ', 1, 1 );
+ if(type == "MFN" )
+ {
+ publicName = unescape( data.section( ' ', 2, 2 ) );
+ MSNContact *c = m_account->findContactByGuid( contactGuid );
+ if(c != 0L)
+ {
+ c->setProperty( Kopete::Global::Properties::self()->nickName(), publicName );
+ }
+ }
+ }
+ else if( cmd == "LSG" )
+ {
+ // New Format: LSG Friends xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ // groupDisplayName, groupGuid
+ emit groupListed( unescape( data.section( ' ', 0, 0 ) ), data.section( ' ', 1, 1) );
+ }
+ else if( cmd == "ADG" )
+ {
+ // groupName, groupGuid
+ emit groupAdded( unescape( data.section( ' ', 0, 0 ) ),
+ data.section( ' ', 1, 1 ) );
+ }
+ else if( cmd == "REG" )
+ {
+ // groupGuid, groupName
+ emit groupRenamed( data.section( ' ', 0, 0 ), unescape( data.section( ' ', 1, 1 ) ) );
+ }
+ else if( cmd == "RMG" )
+ {
+ // groupGuid
+ emit groupRemoved( data.section( ' ', 1, 1 ) );
+ }
+ else if( cmd == "CHL" )
+ {
+ m_challengeHandler = new MSNChallengeHandler("CFHUR$52U_{VIX5T", "PROD0101{0RM?UBW");
+ // Compute the challenge response hash, and send the response.
+ QString chlResponse = m_challengeHandler->computeHash(data.section(' ', 0, 0));
+ sendCommand("QRY", m_challengeHandler->productId(), true, chlResponse.utf8());
+ // Dispose of the challenge handler.
+ m_challengeHandler->deleteLater();
+ m_challengeHandler = 0L;
+ }
+ else if( cmd == "SYN" )
+ {
+ // Retrieve the last synchronization timestamp known to the server.
+ QString lastSyncTime = data.section( ' ', 1, 1 );
+ QString lastChange = data.section( ' ', 0, 0 );
+ if( lastSyncTime != m_account->configGroup()->readEntry("lastsynctime") ||
+ lastChange != m_account->configGroup()->readEntry("lastchange") )
+ {
+ // If the server timestamp and the local timestamp are different,
+ // prepare to receive the contact list.
+ emit newContactList(); // remove all contacts datas, msn sends a new contact list
+ m_account->configGroup()->writeEntry( "lastsynctime" , lastSyncTime);
+ m_account->configGroup()->writeEntry( "lastchange", lastChange);
+ }else
+ kdDebug(14140) << k_funcinfo << "Contact list up-to-date." << endl;
+
+ // set the status
+ setStatus( m_newstatus );
+ }
+ else if( cmd == "BPR" )
+ {
+ MSNContact *c = static_cast<MSNContact*>( m_account->contacts()[ m_tmpLastHandle ] );
+ if( c )
+ c->setInfo(data.section( ' ', 0, 0 ),unescape(data.section( ' ', 1, 1 )));
+ }
+ else if( cmd == "PRP" )
+ {
+ MSNContact *c = static_cast<MSNContact*>( m_account->myself() );
+ if( c )
+ {
+ QString type = data.section( ' ', 0, 0 );
+ QString prpData = unescape( data.section( ' ', 1, 1 ) ); //SECURITY????????
+ c->setInfo( type, prpData );
+ m_account->configGroup()->writeEntry( type, prpData );
+ }
+ }
+ else if( cmd == "BLP" )
+ {
+ if( id > 0 ) //FROM BLP
+ {
+ m_account->configGroup()->writeEntry( "serial" , data.section( ' ', 0, 0 ) );
+ m_account->configGroup()->writeEntry( "BLP" , data.section( ' ', 1, 1 ) );
+ }
+ else //FROM SYN
+ m_account->configGroup()->writeEntry( "BLP" , data.section( ' ', 0, 0 ) );
+ }
+ else if( cmd == "QRY" )
+ {
+ // Do nothing
+ }
+ else if( cmd == "QNG" )
+ {
+ //this is a reply from a ping
+ m_ping=false;
+
+ // id is the timeout in fact, and we remove 5% of it
+ if( m_keepaliveTimer )
+ m_keepaliveTimer->start( id * 950, true );
+ kdDebug( 14140 ) << k_funcinfo << "timerTimeout=" << id << "sec"<< endl;
+ }
+ else if( cmd == "URL" )
+ {
+ // URL 6 /cgi-bin/HoTMaiL https://loginnet.passport.com/ppsecure/md5auth.srf?lc=1033 2
+ //example of reply: URL 10 /cgi-bin/HoTMaiL https://msnialogin.passport.com/ppsecure/md5auth.srf?lc=1036 3
+ QString from_action_url = data.section( ' ', 1, 1 );
+ QString rru = data.section( ' ', 0, 0 );
+ QString id = data.section( ' ', 2, 2 );
+
+ //write the tmp file
+ QString UserID=m_account->accountId();
+
+ time_t actualTime;
+ time(&actualTime);
+ QString sl = QString::number( ( unsigned long ) actualTime - m_loginTime.toULong() );
+
+ QString md5this( m_MSPAuth + sl + m_password );
+ KMD5 md5( md5this.utf8() );
+
+ QString hotmailRequest = "<html>\n"
+ "<head>\n"
+ "<noscript>\n"
+ "<meta http-equiv=Refresh content=\"0; url=http://www.hotmail.com\">\n"
+ "</noscript>\n"
+ "</head>\n"
+ "<body onload=\"document.pform.submit(); \">\n"
+ "<form name=\"pform\" action=\"" + from_action_url + "\" method=\"POST\">\n"
+ "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n"
+ "<input type=\"hidden\" name=\"login\" value=\"" + UserID.left( UserID.find('@') ) + "\">\n"
+ "<input type=\"hidden\" name=\"username\" value=\"" + UserID + "\">\n"
+ "<input type=\"hidden\" name=\"sid\" value=\"" + m_sid + "\">\n"
+ "<input type=\"hidden\" name=\"kv\" value=\"" + m_kv + "\">\n"
+ "<input type=\"hidden\" name=\"id\" value=\""+ id +"\">\n"
+ "<input type=\"hidden\" name=\"sl\" value=\"" + sl +"\">\n"
+ "<input type=\"hidden\" name=\"rru\" value=\"" + rru + "\">\n"
+ "<input type=\"hidden\" name=\"auth\" value=\"" + m_MSPAuth + "\">\n"
+ "<input type=\"hidden\" name=\"creds\" value=\"" + QString::fromLatin1( md5.hexDigest() ) + "\">\n"
+ "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n"
+ "<input type=\"hidden\" name=\"js\" value=\"yes\">\n"
+ "</form></body>\n</html>\n";
+
+ KTempFile tmpMailFile( locateLocal( "tmp", "kopetehotmail-" ), ".html" );
+ *tmpMailFile.textStream() << hotmailRequest;
+ tmpMailFile.file()->flush();
+
+ KRun::runURL( KURL::fromPathOrURL( tmpMailFile.name() ), "text/html" , true );
+
+ }
+ else if ( cmd == "NOT" )
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Received NOT command, issueing read block for '" << id << " more bytes" << endl;
+ readBlock( id );
+ }
+ else
+ {
+ // Let the base class handle the rest
+ //MSNSocket::parseCommand( cmd, id, data );
+ kdDebug( 14140 ) << k_funcinfo << "Unimplemented command '" << cmd << " " << id << " " << data << "' from server!" << endl;
+ }
+}
+
+
+void MSNNotifySocket::sslLoginFailed()
+{
+ m_disconnectReason=Kopete::Account::InvalidHost;
+ disconnect();
+}
+
+void MSNNotifySocket::sslLoginIncorrect()
+{
+ m_disconnectReason=Kopete::Account::BadPassword;
+ disconnect();
+}
+
+void MSNNotifySocket::sslLoginSucceeded(QString ticket)
+{
+ sendCommand("USR" , "TWN S " + ticket);
+
+ m_secureLoginHandler->deleteLater();
+ m_secureLoginHandler = 0L;
+}
+
+void MSNNotifySocket::slotMSNAlertUnwanted()
+{
+ // user not interested .. clean up the list of actions
+ m_msnAlertURLs.clear();
+}
+
+void MSNNotifySocket::slotMSNAlertLink(unsigned int action)
+{
+ // index into our action list and pull out the URL that was clicked ..
+ KURL tempURLForLaunch(m_msnAlertURLs[action-1]);
+
+ KRun* urlToRun = new KRun(tempURLForLaunch);
+}
+
+void MSNNotifySocket::slotOpenInbox()
+{
+ sendCommand("URL", "INBOX" );
+}
+
+void MSNNotifySocket::sendMail(const QString &email)
+{
+ sendCommand("URL", QString("COMPOSE " + email).utf8() );
+}
+
+bool MSNNotifySocket::setUseHttpMethod(bool useHttp)
+{
+ bool ret = MSNSocket::setUseHttpMethod( useHttp );
+
+ if( useHttpMethod() ) {
+ if( m_keepaliveTimer ) {
+ delete m_keepaliveTimer;
+ m_keepaliveTimer = 0L;
+ }
+ }
+ else {
+ if( !m_keepaliveTimer ) {
+ m_keepaliveTimer = new QTimer( this, "m_keepaliveTimer" );
+ QObject::connect( m_keepaliveTimer, SIGNAL( timeout() ), SLOT( slotSendKeepAlive() ) );
+ }
+ }
+
+ return ret;
+}
+
+void MSNNotifySocket::slotReadMessage( const QByteArray &bytes )
+{
+ QString msg = QString::fromUtf8(bytes, bytes.size());
+
+ if(msg.contains("text/x-msmsgsinitialmdatanotification"))
+ {
+ //Mail-Data: <MD><E><I>301</I><IU>1</IU><O>4</O><OU>2</OU></E><Q><QTM>409600</QTM><QNM>204800</QNM></Q></MD>
+ // MD - Mail Data
+ // E - email
+ // I - initial mail
+ // IU - initial unread
+ // O - other mail
+ // OU - other unread.
+ QRegExp regex("<MD><E><I>(\\d+)?</I>(?:<IU>(\\d+)?</IU>)<O>(\\d+)?</O><OU>(\\d+)?</OU></E><Q>.*</Q></MD>");
+ regex.search(msg);
+
+ bool unread;
+ // Retrieve the number of unread email messages.
+ mailCount = regex.cap(2).toUInt(&unread);
+ if(unread && mailCount > 0)
+ {
+ // If there are new email message available, raise the unread email event.
+ QObject::connect(KNotification::event( "msn_mail", i18n( "You have one unread message in your MSN inbox.",
+ "You have %n unread messages in your MSN inbox.", mailCount ), 0 , 0 , i18n( "Open Inbox..." ) ),
+ SIGNAL(activated(unsigned int ) ) , this, SLOT( slotOpenInbox() ) );
+ }
+ }
+ else if(msg.contains("text/x-msmsgsactivemailnotification"))
+ {
+ //this sends the server if mails are deleted
+ QString m = msg.right(msg.length() - msg.find("Message-Delta:") );
+ m = m.left(msg.find("\r\n"));
+ mailCount = mailCount - m.right(m.length() -m.find(" ")-1).toUInt();
+ }
+ else if(msg.contains("text/x-msmsgsemailnotification"))
+ {
+ //this sends the server if a new mail has arrived
+ QRegExp rx("From-Addr: ([A-Za-z0-9@._\\-]*)");
+ rx.search(msg);
+ QString m=rx.cap(1);
+
+ mailCount++;
+
+ //TODO: it is also possible to get the subject (but warning about the encoding)
+ QObject::connect(KNotification::event( "msn_mail",i18n( "You have one new email from %1 in your MSN inbox." ).arg(m),
+ 0 , 0 , i18n( "Open Inbox..." ) ),
+ SIGNAL(activated(unsigned int ) ) , this, SLOT( slotOpenInbox() ) );
+ }
+ else if(msg.contains("text/x-msmsgsprofile"))
+ {
+ //Hotmail profile
+ if(msg.contains("MSPAuth:"))
+ {
+ QRegExp rx("MSPAuth: ([A-Za-z0-9$!*]*)");
+ rx.search(msg);
+ m_MSPAuth=rx.cap(1);
+ }
+ if(msg.contains("sid:"))
+ {
+ QRegExp rx("sid: ([0-9]*)");
+ rx.search(msg);
+ m_sid=rx.cap(1);
+ }
+ if(msg.contains("kv:"))
+ {
+ QRegExp rx("kv: ([0-9]*)");
+ rx.search(msg);
+ m_kv=rx.cap(1);
+ }
+ if(msg.contains("LoginTime:"))
+ {
+ QRegExp rx("LoginTime: ([0-9]*)");
+ rx.search(msg);
+ m_loginTime=rx.cap(1);
+ }
+ else //IN MSNP9 there are no logintime it seems, so set it manualy
+ {
+ time_t actualTime;
+ time(&actualTime);
+ m_loginTime=QString::number((unsigned long)actualTime);
+ }
+ if(msg.contains("EmailEnabled:"))
+ {
+ QRegExp rx("EmailEnabled: ([0-9]*)");
+ rx.search(msg);
+ m_isHotmailAccount = (rx.cap(1).toUInt() == 1);
+ emit hotmailSeted(m_isHotmailAccount);
+ }
+ if(msg.contains("ClientIP:"))
+ {
+ QRegExp rx("ClientIP: ([0-9.]*)");
+ rx.search(msg);
+ m_localIP = rx.cap(1);
+ }
+
+ // We are logged when we receive the initial profile from Hotmail.
+ m_isLogged = true;
+ }
+ else if (msg.contains("NOTIFICATION"))
+ {
+ // MSN alert (i.e. NOTIFICATION) [for docs see http://www.hypothetic.org/docs/msn/client/notification.php]
+ // format of msg is as follows:
+ //
+ // <NOTIFICATION ver="2" id="1342902633" siteid="199999999" siteurl="http://alerts.msn.com">
+ // <TO pid="0x0006BFFD:0x8582C0FB" name="example@passport.com"/>
+ // <MSG pri="1" id="1342902633">
+ // <SUBSCR url="http://g.msn.com/3ALMSNTRACKING/199999999ToastChange?http://alerts.msn.com/Alerts/MyAlerts.aspx?strela=1"/>
+ // <ACTION url="http://g.msn.com/3ALMSNTRACKING/199999999ToastAction?http://alerts.msn.com/Alerts/MyAlerts.aspx?strela=1"/>
+ // <BODY lang="3076" icon="">
+ // <TEXT>utf8-encoded text</TEXT>
+ // </BODY>
+ // </MSG>
+ // </NOTIFICATION>
+
+ // MSN sends out badly formed XML .. fix it for them (thanks MS!)
+ QString notificationDOMAsString(msg);
+
+ QRegExp rx( "&(?!amp;)" ); // match ampersands but not &amp;
+ notificationDOMAsString.replace(rx, "&amp;");
+ QDomDocument alertDOM;
+ alertDOM.setContent(notificationDOMAsString);
+
+ QDomNodeList msgElements = alertDOM.elementsByTagName("MSG");
+ for (uint i = 0 ; i < msgElements.count() ; i++)
+ {
+ QString subscString;
+ QString actionString;
+ QString textString;
+
+ QDomNode msgDOM = msgElements.item(i);
+
+ QDomNodeList msgChildren = msgDOM.childNodes();
+ for (uint i = 0 ; i < msgChildren.length() ; i++) {
+ QDomNode child = msgChildren.item(i);
+ QDomElement element = child.toElement();
+ if (element.tagName() == "SUBSCR")
+ {
+ QDomAttr subscElementURLAttribute;
+ if (element.hasAttribute("url"))
+ {
+ subscElementURLAttribute = element.attributeNode("url");
+ subscString = subscElementURLAttribute.value();
+ }
+ }
+ else if (element.tagName() == "ACTION")
+ {
+ // process ACTION node to pull out URL the alert is tied to
+ QDomAttr actionElementURLAttribute;
+ if (element.hasAttribute("url"))
+ {
+ actionElementURLAttribute = element.attributeNode("url");
+ actionString = actionElementURLAttribute.value();
+ }
+ }
+ else if (element.tagName() == "BODY")
+ {
+ // process BODY node to get the text of the alert
+ QDomNodeList textElements = element.elementsByTagName("TEXT");
+ if (textElements.count() >= 1)
+ {
+ QDomElement textElement = textElements.item(0).toElement();
+ textString = textElement.text();
+ }
+ }
+
+
+ }
+
+// kdDebug( 14140 ) << "subscString " << subscString << " actionString " << actionString << " textString " << textString << endl;
+ // build an internal list of actions ... we'll need to index into this list when we receive an event
+ QStringList actions;
+ actions.append(i18n("More Information"));
+ m_msnAlertURLs.append(actionString);
+
+ actions.append(i18n("Manage Subscription"));
+ m_msnAlertURLs.append(subscString);
+
+ // Don't do any MSN alerts notification for new blog updates
+ if( subscString != QString::fromLatin1("s.htm") && actionString != QString::fromLatin1("a.htm") )
+ {
+ KNotification* notification = KNotification::event("msn_alert", textString, 0L, 0L, actions);
+ QObject::connect(notification, SIGNAL(activated(unsigned int)), this, SLOT(slotMSNAlertLink(unsigned int)));
+ QObject::connect(notification, SIGNAL(closed()), this, SLOT(slotMSNAlertUnwanted()));
+ }
+ } // end for each MSG tag
+ }
+
+ if(!m_configFile.isNull())
+ {
+ // TODO Get client features.
+ }
+
+ if(!m_tmpLastHandle.isNull())
+ {
+ QString personalMessage, currentMedia;
+ QDomDocument psm;
+ if( psm.setContent(msg) )
+ {
+ // Get the first child of the xml "document";
+ QDomElement psmElement = psm.documentElement().firstChild().toElement();
+
+ while( !psmElement.isNull() )
+ {
+ if(psmElement.tagName() == QString::fromUtf8("PSM"))
+ {
+ personalMessage = psmElement.text();
+ kdDebug(14140) << k_funcinfo << "Personnal Message received: " << personalMessage << endl;
+ }
+ else if(psmElement.tagName() == QString::fromUtf8("CurrentMedia"))
+ {
+ if( !psmElement.text().isEmpty() )
+ {
+ kdDebug(14140) << k_funcinfo << "XML CurrentMedia: " << psmElement.text() << endl;
+ currentMedia = processCurrentMedia( psmElement.text() );
+ }
+ }
+ psmElement = psmElement.nextSibling().toElement();
+ }
+
+ MSNContact *contact = static_cast<MSNContact*>(m_account->contacts()[ m_tmpLastHandle ]);
+ if(contact)
+ {
+ contact->setProperty(MSNProtocol::protocol()->propPersonalMessage, currentMedia.isEmpty() ? personalMessage : currentMedia);
+ }
+ }
+ m_tmpLastHandle = QString::null;
+ }
+}
+
+QString MSNNotifySocket::processCurrentMedia( const QString &mediaXmlElement )
+{
+ /*
+ The value of the CurrentMedia tag you can think of like an array
+ seperated by "\0" characters (literal backslash followed by zero, not NULL).
+
+ The elements of this "array" are as follows:
+
+ * Application - This is the app you are using. Usually empty
+ * Type - This is the type of PSM, either “Musicâ€, “Games†or “Officeâ€
+ * Enabled - This is a boolean value (0/1) to enable/disable
+ * Format - A formatter string ala .Net; For example, “{0} - {1}â€
+ * First line - The first line (Matches {0} in the Format)
+ * Second line - The second line (Matches {1} in the Format)
+ * Third line - The third line (Matches {2} in the Format)
+
+ There is probably no limit to the number of lines unless you go over the maximum length of the tag.
+
+ Example of currentMedia xml tag:
+ <CurrentMedia>\0Music\01\0{0} - {1}\0 Song Title\0Song Artist\0Song Album\0\0</CurrentMedia>
+ <CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia>
+ <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>
+
+ From http://msnpiki.msnfanatic.com/index.php/MSNP11:Changes
+ */
+ QString application, type, format, currentMedia;
+ bool enabled=false, test;
+ // \0 is textual, it's the "array" separator.
+ QStringList argumentLists = QStringList::split(QString::fromUtf8("\\0"), mediaXmlElement, true);
+
+ // Retrive the "stable" array elements.
+ application = argumentLists[0];
+ type = argumentLists[1];
+ enabled = argumentLists[2].toInt(&test);
+ format = argumentLists[3];
+
+ // Get the formatter strings
+ QStringList formatterStrings;
+ QStringList::ConstIterator it;
+ for( it = argumentLists.at(4); it != argumentLists.end(); ++it )
+ {
+ formatterStrings.append( *it );
+ }
+
+ // Replace the formatter in the format string.
+ currentMedia = format;
+ for(uint i=0; i<formatterStrings.size(); i++)
+ {
+ currentMedia = currentMedia.replace(QString("{%1}").arg(i), formatterStrings[i]);
+ }
+
+ if( type == QString::fromUtf8("Music") )
+ {
+ // the "♫" is encoded in utf8 (and should be in utf8)
+ currentMedia = i18n("Now Listening: ♫ %1 ♫").arg(currentMedia);
+ }
+
+ kdDebug(1414) << "Current Media received: " << currentMedia << endl;
+
+ return currentMedia;
+}
+
+void MSNNotifySocket::addGroup(const QString& groupName)
+{
+ // escape spaces
+ sendCommand( "ADG", escape( groupName ) );
+}
+
+void MSNNotifySocket::renameGroup( const QString& groupName, const QString& groupGuid )
+{
+ // escape spaces
+ sendCommand( "REG", groupGuid + " " + escape( groupName ) );
+}
+
+void MSNNotifySocket::removeGroup( const QString& groupGuid )
+{
+ sendCommand( "RMG", groupGuid );
+}
+
+void MSNNotifySocket::addContact( const QString &handle, int list, const QString& publicName, const QString& contactGuid, const QString& groupGuid )
+{
+ QString args;
+ switch( list )
+ {
+ case MSNProtocol::FL:
+ {
+ // Adding the contact to a group
+ if( !contactGuid.isEmpty() )
+ {
+ args = QString("FL C=%1 %2").arg( contactGuid ).arg( groupGuid );
+ kdDebug(14140) << k_funcinfo << "In adding contact to a group" << endl;
+ }
+ // Adding a new contact
+ else
+ {
+ args = QString("FL N=%1 F=%2").arg( handle ).arg( escape( publicName ) );
+ kdDebug(14140) << k_funcinfo << "In adding contact to a new contact" << endl;
+ }
+ break;
+ }
+ case MSNProtocol::AL:
+ args = QString("AL N=%1").arg( handle );
+ break;
+ case MSNProtocol::BL:
+ args = QString("BL N=%1").arg( handle );
+ break;
+ case MSNProtocol::RL:
+ args = QString("RL N=%1").arg( handle );
+ break;
+ default:
+ kdDebug(14140) << k_funcinfo <<"WARNING! Unknown list " << list << "!" << endl;
+ return;
+ }
+ unsigned int id=sendCommand( "ADC", args );
+ m_tmpHandles[id]=handle;
+}
+
+void MSNNotifySocket::removeContact( const QString &handle, int list, const QString& contactGuid, const QString& groupGuid )
+{
+ QString args;
+ switch( list )
+ {
+ case MSNProtocol::FL:
+ args = "FL " + contactGuid;
+ // Removing a contact from a group
+ if( !groupGuid.isEmpty() )
+ args += " " + groupGuid;
+ break;
+ case MSNProtocol::AL:
+ args = "AL " + handle;
+ break;
+ case MSNProtocol::BL:
+ args = "BL " + handle;
+ break;
+ case MSNProtocol::PL:
+ args = "PL " + handle;
+ break;
+ default:
+ kdDebug(14140) <<k_funcinfo << "WARNING! Unknown list " << list << "!" << endl;
+ return;
+ }
+ unsigned int id=sendCommand( "REM", args );
+ m_tmpHandles[id]=handle;
+}
+
+void MSNNotifySocket::setStatus( const Kopete::OnlineStatus &status )
+{
+// kdDebug( 14140 ) << k_funcinfo << statusToString( status ) << endl;
+
+ if( onlineStatus() == Disconnected )
+ m_newstatus = status;
+ else
+ sendCommand( "CHG", statusToString( status ) + " " + m_account->myselfClientId() + " " + escape(m_account->pictureObject()) );
+}
+
+void MSNNotifySocket::changePublicName( const QString &publicName, const QString &handle )
+{
+ QString tempPublicName = publicName;
+
+ //The maximum length is 387. but with utf8 or encodage, each character may be triple
+ // 387/3 = 129 so we make sure the lenght is not logner than 129 char, even if
+ // it's possible to have longer nicks.
+ if( escape(publicName).length() > 129 )
+ {
+ tempPublicName = publicName.left(129);
+ }
+
+ if( handle.isNull() )
+ {
+ unsigned int id = sendCommand( "PRP", "MFN " + escape( tempPublicName ) );
+ m_tmpHandles[id] = m_account->accountId();
+ }
+ else
+ {
+ MSNContact *currentContact = static_cast<MSNContact *>(m_account->contacts()[handle]);
+ if(currentContact && !currentContact->guid().isEmpty() )
+ {
+ // FIXME if there is not guid server disconnects.
+ unsigned int id = sendCommand( "SBP", currentContact->guid() + " MFN " + escape( tempPublicName ) );
+ m_tmpHandles[id] = handle;
+ }
+ }
+}
+
+void MSNNotifySocket::changePersonalMessage( MSNProtocol::PersonalMessageType type, const QString &personalMessage )
+{
+ QString tempPersonalMessage;
+ QString xmlCurrentMedia;
+
+ // Only espace and cut the personalMessage is the type is normal.
+ if(type == MSNProtocol::PersonalMessageNormal)
+ {
+ tempPersonalMessage = personalMessage;
+ //Magic number : 129 characters
+ if( escape(personalMessage).length() > 129 )
+ {
+ // We cut. for now.
+ tempPersonalMessage = personalMessage.left(129);
+ }
+ }
+
+ QDomDocument xmlMessage;
+ xmlMessage.appendChild( xmlMessage.createElement( "Data" ) );
+
+ QDomElement psm = xmlMessage.createElement("PSM");
+ psm.appendChild( xmlMessage.createTextNode( tempPersonalMessage ) );
+ xmlMessage.documentElement().appendChild( psm );
+
+ QDomElement currentMedia = xmlMessage.createElement("CurrentMedia");
+
+ /* Example of currentMedia xml tag:
+ <CurrentMedia>\0Music\01\0{0} - {1}\0 Song Title\0Song Artist\0Song Album\0\0</CurrentMedia>
+ <CurrentMedia>\0Games\01\0Playing {0}\0Game Name\0</CurrentMedia>
+ <CurrentMedia>\0Office\01\0Office Message\0Office App Name\0</CurrentMedia>
+ */
+ switch(type)
+ {
+ case MSNProtocol::PersonalMessageMusic:
+ {
+ xmlCurrentMedia = "\\0Music\\01\\0";
+ QStringList mediaList = QStringList::split(";", personalMessage);
+ QString formatterArguments;
+ if( !mediaList[0].isEmpty() ) // Current Track
+ {
+ xmlCurrentMedia += "{0}";
+ formatterArguments += QString("%1\\0").arg(mediaList[0]);
+ }
+ if( !mediaList[1].isEmpty() ) // Current Artist
+ {
+ xmlCurrentMedia += " - {1}";
+ formatterArguments += QString("%1\\0").arg(mediaList[1]);
+ }
+ if( !mediaList[2].isEmpty() ) // Current Album
+ {
+ xmlCurrentMedia += " ({2})";
+ formatterArguments += QString("%1\\0").arg(mediaList[2]);
+ }
+ xmlCurrentMedia += "\\0" + formatterArguments + "\\0";
+ break;
+ }
+ default:
+ break;
+ }
+
+ currentMedia.appendChild( xmlMessage.createTextNode( xmlCurrentMedia ) );
+
+ // Set the status message for myself, check if currentMedia is empty, for either using the normal or Music personal
+ m_propertyPersonalMessage = xmlCurrentMedia.isEmpty() ? tempPersonalMessage : processCurrentMedia( currentMedia.text() );
+
+ xmlMessage.documentElement().appendChild( currentMedia );
+
+ unsigned int id = sendCommand("UUX","",true, xmlMessage.toString().utf8(), false);
+ m_tmpHandles[id] = m_account->accountId();
+
+}
+
+void MSNNotifySocket::changePhoneNumber( const QString &key, const QString &data )
+{
+ sendCommand( "PRP", key + " " + escape ( data ) );
+}
+
+
+void MSNNotifySocket::createChatSession()
+{
+ sendCommand( "XFR", "SB" );
+}
+
+QString MSNNotifySocket::statusToString( const Kopete::OnlineStatus &status ) const
+{
+ if( status == MSNProtocol::protocol()->NLN )
+ return "NLN";
+ else if( status == MSNProtocol::protocol()->BSY )
+ return "BSY";
+ else if( status == MSNProtocol::protocol()->BRB )
+ return "BRB";
+ else if( status == MSNProtocol::protocol()->AWY )
+ return "AWY";
+ else if( status == MSNProtocol::protocol()->PHN )
+ return "PHN";
+ else if( status == MSNProtocol::protocol()->LUN )
+ return "LUN";
+ else if( status == MSNProtocol::protocol()->FLN )
+ return "FLN";
+ else if( status == MSNProtocol::protocol()->HDN )
+ return "HDN";
+ else if( status == MSNProtocol::protocol()->IDL )
+ return "IDL";
+ else
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Unknown status " << status.internalStatus() << "!" << endl;
+ return "UNK";
+ }
+}
+
+void MSNNotifySocket::slotSendKeepAlive()
+{
+ //we did not received the previous QNG
+ if(m_ping)
+ {
+ m_disconnectReason=Kopete::Account::ConnectionReset;
+ disconnect();
+ /*KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Information,
+ i18n( "The connection with the MSN network has been lost." ) , i18n ("MSN Plugin") );*/
+ return;
+ }
+ else
+ {
+ // Send a dummy command to fake activity. This makes sure MSN doesn't
+ // disconnect you when the notify socket is idle.
+ sendCommand( "PNG" , QString::null , false );
+ m_ping=true;
+ }
+
+ //at least 90 second has been ellapsed since the last messages
+ // we shouldn't receive error from theses command anymore
+ m_tmpHandles.clear();
+}
+
+Kopete::OnlineStatus MSNNotifySocket::convertOnlineStatus( const QString &status )
+{
+ if( status == "NLN" )
+ return MSNProtocol::protocol()->NLN;
+ else if( status == "FLN" )
+ return MSNProtocol::protocol()->FLN;
+ else if( status == "HDN" )
+ return MSNProtocol::protocol()->HDN;
+ else if( status == "PHN" )
+ return MSNProtocol::protocol()->PHN;
+ else if( status == "LUN" )
+ return MSNProtocol::protocol()->LUN;
+ else if( status == "BRB" )
+ return MSNProtocol::protocol()->BRB;
+ else if( status == "AWY" )
+ return MSNProtocol::protocol()->AWY;
+ else if( status == "BSY" )
+ return MSNProtocol::protocol()->BSY;
+ else if( status == "IDL" )
+ return MSNProtocol::protocol()->IDL;
+ else
+ return MSNProtocol::protocol()->UNK;
+}
+
+
+#include "msnnotifysocket.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnnotifysocket.h b/kopete/protocols/msn/msnnotifysocket.h
new file mode 100644
index 00000000..7f915410
--- /dev/null
+++ b/kopete/protocols/msn/msnnotifysocket.h
@@ -0,0 +1,216 @@
+/*
+ msnnotifysocket.h - Notify Socket for the MSN Protocol
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions taken from
+ KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 MSNNOTIFYSOCKET_H
+#define MSNNOTIFYSOCKET_H
+
+#include "msnsocket.h"
+#include "msnprotocol.h"
+
+
+class MSNDispatchSocket;
+class MSNAccount;
+class KTempFile;
+class MSNSecureLoginHandler;
+class MSNChallengeHandler;
+
+/**
+ * @author Olaf Lueg
+ * @author Olivier Goffart
+ */
+class MSNNotifySocket : public MSNSocket
+{
+ Q_OBJECT
+
+public:
+ MSNNotifySocket( MSNAccount* account, const QString &msnId, const QString &password );
+ ~MSNNotifySocket();
+
+ virtual void disconnect();
+
+ void setStatus( const Kopete::OnlineStatus &status );
+ void addContact( const QString &handle, int list, const QString& publicName, const QString& contactGuid, const QString& groupGuid );
+ void removeContact( const QString &handle, int list, const QString &contactGuid, const QString &groupGuid );
+
+ void addGroup( const QString& groupName );
+ void removeGroup( const QString& group );
+ void renameGroup( const QString& groupName, const QString& groupGuid );
+
+ void changePublicName( const QString& publicName , const QString &handle=QString::null );
+ void changePersonalMessage( MSNProtocol::PersonalMessageType type , const QString& personalMessage );
+
+ void changePhoneNumber( const QString &key, const QString &data );
+
+ void createChatSession();
+
+ void sendMail(const QString &email);
+
+ /**
+ * this should return a Kopete::Account::DisconnectReason value
+ */
+ int disconnectReason() { return m_disconnectReason; }
+
+ QString localIP() { return m_localIP; }
+
+ bool setUseHttpMethod( bool useHttpMethod );
+
+ bool isLogged() const { return m_isLogged; }
+
+public slots:
+ void slotOpenInbox();
+ void slotMSNAlertLink(unsigned int action);
+ void slotMSNAlertUnwanted();
+
+signals:
+ void newContactList();
+ void contactList(const QString& handle, const QString& publicName, const QString &contactGuid, uint lists, const QString& groups);
+ void contactStatus(const QString&, const QString&, const QString& );
+ void contactAdded(const QString& handle, const QString& list, const QString& publicName, const QString& contactGuid, const QString& groupGuid);
+ //void contactRemoved(const QString&, const QString&, uint);
+ void contactRemoved(const QString& handle, const QString& list, const QString& contactGuid, const QString& groupGuid);
+
+ void groupListed(const QString&, const QString&);
+ void groupAdded( const QString&, const QString&);
+ void groupRenamed( const QString&, const QString& );
+ void groupRemoved( const QString& );
+
+ void invitedToChat(const QString&, const QString&, const QString&, const QString&, const QString& );
+ void startChat( const QString&, const QString& );
+
+ void statusChanged( const Kopete::OnlineStatus &newStatus );
+
+ void hotmailSeted(bool) ;
+
+
+ /**
+ * When the dispatch server sends us the notification server to use, this
+ * signal is emitted. After this the socket is automatically closed.
+ */
+ void receivedNotificationServer( const QString &host, uint port );
+
+
+protected:
+ /**
+ * Handle an MSN command response line.
+ */
+ virtual void parseCommand( const QString &cmd, uint id,
+ const QString &data );
+
+ /**
+ * Handle an MSN error condition.
+ * This reimplementation handles most of the other MSN error codes.
+ */
+ virtual void handleError( uint code, uint id );
+
+ /**
+ * This reimplementation sets up the negotiating with the server and
+ * suppresses the change of the status to online until the handshake
+ * is complete.
+ */
+ virtual void doneConnect();
+
+
+private slots:
+ /**
+ * We received a message from the server, which is sent as raw data,
+ * instead of cr/lf line-based text.
+ */
+ void slotReadMessage( const QByteArray &bytes );
+
+ /**
+ * Send a keepalive to the server to avoid idle connections to cause
+ * MSN closing the connection
+ */
+ void slotSendKeepAlive();
+
+ void sslLoginFailed();
+ void sslLoginIncorrect();
+ void sslLoginSucceeded(QString ticket);
+
+
+private:
+ /**
+ * Convert the MSN status strings to a Kopete::OnlineStatus
+ */
+ Kopete::OnlineStatus convertOnlineStatus( const QString &statusString );
+
+ MSNAccount *m_account;
+ QString m_password;
+ QStringList m_msnAlertURLs;
+
+ unsigned int mailCount;
+
+ Kopete::OnlineStatus m_newstatus;
+
+ /**
+ * Convert an entry of the Status enum back to a string
+ */
+ QString statusToString( const Kopete::OnlineStatus &status ) const;
+
+ /**
+ * Process the CurrentMedia XML element.
+ * @param mediaXmlElement the source XML element as text.
+ */
+ QString processCurrentMedia( const QString &mediaXmlElement );
+
+ //know the last handle used
+ QString m_tmpLastHandle;
+ QMap <unsigned int,QString> m_tmpHandles;
+ QString m_configFile;
+
+ //for hotmail inbox opening
+ bool m_isHotmailAccount;
+ QString m_MSPAuth;
+ QString m_kv;
+ QString m_sid;
+ QString m_loginTime;
+ QString m_localIP;
+ MSNSecureLoginHandler *m_secureLoginHandler;
+
+ MSNChallengeHandler *m_challengeHandler;
+ QTimer *m_keepaliveTimer;
+
+ bool m_ping;
+
+ int m_disconnectReason;
+
+ /**
+ * Used to set the myself() personalMessage when the acknowledge(UUX) command is received.
+ * The personalMessage is built into @ref changePersonalMessage
+ */
+ QString m_propertyPersonalMessage;
+
+ /**
+ * Used to tell when we are logged in to MSN Messeger service.
+ * Logged when we receive the initial profile message from Hotmail.
+ *
+ * Some commands only make sense to be done when logged.
+ */
+ bool m_isLogged;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnprotocol.cpp b/kopete/protocols/msn/msnprotocol.cpp
new file mode 100644
index 00000000..2a5b4319
--- /dev/null
+++ b/kopete/protocols/msn/msnprotocol.cpp
@@ -0,0 +1,179 @@
+/*
+ msnprotocol.cpp - Kopete MSN Protocol Plugin
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qimage.h>
+
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kconfig.h>
+#include <kdeversion.h>
+#include <kaboutdata.h>
+
+#include "kopeteaccountmanager.h"
+#include "kopeteglobal.h"
+#include "kopeteonlinestatusmanager.h"
+
+#include "msnaddcontactpage.h"
+#include "msneditaccountwidget.h"
+#include "msncontact.h"
+#include "msnaccount.h"
+#include "msnprotocol.h"
+#include "msnchatsession.h"
+
+typedef KGenericFactory<MSNProtocol> MSNProtocolFactory;
+#if KDE_IS_VERSION(3,2,90)
+static const KAboutData aboutdata("kopete_msn", I18N_NOOP("MSN Messenger") , "1.0" );
+K_EXPORT_COMPONENT_FACTORY( libkopete_msn_shared, MSNProtocolFactory( &aboutdata ) )
+#else
+K_EXPORT_COMPONENT_FACTORY( libkopete_msn_shared, MSNProtocolFactory( "kopete_msn" ) )
+#endif
+
+MSNProtocol *MSNProtocol::s_protocol = 0L;
+
+MSNProtocol::MSNProtocol( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Protocol( MSNProtocolFactory::instance(), parent, name ),
+ NLN( Kopete::OnlineStatus::Online, 25, this, 1, QString::null, i18n( "Online" ) , i18n( "O&nline" ), Kopete::OnlineStatusManager::Online,Kopete::OnlineStatusManager::HasAwayMessage ),
+ BSY( Kopete::OnlineStatus::Away, 20, this, 2, "msn_busy", i18n( "Busy" ) , i18n( "&Busy" ), Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage ),
+ BRB( Kopete::OnlineStatus::Away, 22, this, 3, "msn_brb", i18n( "Be Right Back" ), i18n( "Be &Right Back" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ),
+ AWY( Kopete::OnlineStatus::Away, 18, this, 4, "contact_away_overlay", i18n( "Away From Computer" ),i18n( "&Away" ), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage ),
+ PHN( Kopete::OnlineStatus::Away, 12, this, 5, "contact_phone_overlay", i18n( "On the Phone" ) , i18n( "On The &Phone" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ),
+ LUN( Kopete::OnlineStatus::Away, 15, this, 6, "contact_food_overlay", i18n( "Out to Lunch" ) , i18n( "Out To &Lunch" ) , 0 , Kopete::OnlineStatusManager::HasAwayMessage ),
+ FLN( Kopete::OnlineStatus::Offline, 0, this, 7, QString::null, i18n( "Offline" ) , i18n( "&Offline" ), Kopete::OnlineStatusManager::Offline,Kopete::OnlineStatusManager::DisabledIfOffline ),
+ HDN( Kopete::OnlineStatus::Invisible, 3, this, 8, "contact_invisible_overlay", i18n( "Invisible" ) , i18n( "&Invisible" ), Kopete::OnlineStatusManager::Invisible ),
+ IDL( Kopete::OnlineStatus::Away, 10, this, 9, "contact_away_overlay", i18n( "Idle" ) , i18n( "&Idle" ), Kopete::OnlineStatusManager::Idle , Kopete::OnlineStatusManager::HideFromMenu ),
+ UNK( Kopete::OnlineStatus::Unknown, 25, this, 0, "status_unknown", i18n( "Status not available" ) ),
+ CNT( Kopete::OnlineStatus::Connecting, 2, this, 10,"msn_connecting", i18n( "Connecting" ) ),
+ propEmail(Kopete::Global::Properties::self()->emailAddress()),
+ propPhoneHome(Kopete::Global::Properties::self()->privatePhone()),
+ propPhoneWork(Kopete::Global::Properties::self()->workPhone()),
+ propPhoneMobile(Kopete::Global::Properties::self()->privateMobilePhone()),
+ propClient("client", i18n("Remote Client"), 0, false),
+ propGuid("guid", i18n("Contact GUID"), 0, true),
+ propPersonalMessage(Kopete::Global::Properties::self()->awayMessage())
+{
+ s_protocol = this;
+
+ addAddressBookField( "messaging/msn", Kopete::Plugin::MakeIndexField );
+
+ setCapabilities( Kopete::Protocol::BaseFgColor | Kopete::Protocol::BaseFont | Kopete::Protocol::BaseFormatting );
+
+ // m_status = m_unknownStatus = UNK;
+}
+
+Kopete::Contact *MSNProtocol::deserializeContact( Kopete::MetaContact *metaContact, const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ QString contactId = serializedData[ "contactId" ] ;
+ QString accountId = serializedData[ "accountId" ] ;
+ QString lists = serializedData[ "lists" ];
+ QStringList groups = QStringList::split( ",", serializedData[ "groups" ] );
+ QString contactGuid = serializedData[ "contactGuid" ] ;
+
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
+
+ Kopete::Account *account = accounts[ accountId ];
+ if( !account )
+ account = createNewAccount( accountId );
+
+ // Create MSN contact
+ MSNContact *c = new MSNContact( account, contactId, metaContact );
+
+ for( QStringList::Iterator it = groups.begin() ; it != groups.end(); ++it )
+ c->contactAddedToGroup( *it, 0L /* FIXME - m_groupList[ ( *it ).toUInt() ]*/ );
+
+ c->m_obj= serializedData[ "obj" ];
+ c->setInfo( "PHH" , serializedData[ "PHH" ] );
+ c->setInfo( "PHW" , serializedData[ "PHW" ] );
+ c->setInfo( "PHM" , serializedData[ "PHM" ] );
+ c->setProperty( propGuid, contactGuid );
+
+ c->setBlocked( (bool)(lists.contains('B')) );
+ c->setAllowed( (bool)(lists.contains('A')) );
+ c->setReversed( (bool)(lists.contains('R')) );
+
+ return c;
+}
+
+AddContactPage *MSNProtocol::createAddContactWidget(QWidget *parent , Kopete::Account *i)
+{
+ return (new MSNAddContactPage(i->isConnected(),parent));
+}
+
+KopeteEditAccountWidget *MSNProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new MSNEditAccountWidget(this,account,parent);
+}
+
+Kopete::Account *MSNProtocol::createNewAccount(const QString &accountId)
+{
+ return new MSNAccount(this, accountId);
+}
+
+
+// NOTE: CALL THIS ONLY BEING CONNECTED
+void MSNProtocol::slotSyncContactList()
+{
+/* if ( ! mIsConnected )
+ {
+ return;
+ }
+ // First, delete D marked contacts
+ QStringList localcontacts;
+
+ contactsFile->setGroup("Default");
+
+ contactsFile->readListEntry("Contacts",localcontacts);
+ QString tmpUin;
+ tmpUin.sprintf("%d",uin);
+ tmp.append(tmpUin);
+ cnt=contactsFile->readNumEntry("Count",0);
+*/
+}
+
+MSNProtocol* MSNProtocol::protocol()
+{
+ return s_protocol;
+}
+
+bool MSNProtocol::validContactId(const QString& userid)
+{
+ return ( userid.contains('@') ==1 && userid.contains('.') >=1 && userid.contains(' ') == 0);
+}
+
+QImage MSNProtocol::scalePicture(const QImage &picture)
+{
+ QImage img(picture);
+ img = img.smoothScale( 96, 96, QImage::ScaleMin );
+ // crop image if not square
+ if(img.width() < img.height())
+ {
+ img = img.copy((img.width()-img.height())/2, 0, 96, 96);
+ }
+ else if(img.width() > img.height())
+ {
+ img = img.copy(0, (img.height()-img.width())/2, 96, 96);
+ }
+
+ return img;
+}
+#include "msnprotocol.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnprotocol.h b/kopete/protocols/msn/msnprotocol.h
new file mode 100644
index 00000000..7017fd90
--- /dev/null
+++ b/kopete/protocols/msn/msnprotocol.h
@@ -0,0 +1,187 @@
+/*
+ msnprotocol.h - Kopete MSN Protocol Plugin
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __msnprotocol_h__
+#define __msnprotocol_h__
+
+#include <qmap.h>
+#include <qstringlist.h>
+
+#include "kopeteprotocol.h"
+#include "kopeteonlinestatus.h"
+#include "kopetecontactproperty.h"
+
+#include "msnsocket.h"
+
+
+class QImage;
+
+class KAction;
+class KActionMenu;
+
+class MSNContact;
+class MSNAccount;
+class MSNNotifySocket;
+class MSNSwitchBoardSocket;
+class MSNChatSession;
+class MSNInvitation;
+namespace Kopete { class ChatSession; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class Contact; }
+namespace Kopete { class Message; }
+namespace Kopete { class Group; }
+
+/**
+ * @author duncan
+ * @author Martijn Klingens <klingens@kde.org>
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+class KOPETE_EXPORT MSNProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ MSNProtocol( QObject *parent, const char *name, const QStringList &args );
+
+ /**
+ * SyncMode indicates whether settings differing between client and
+ * server should be propagated to keep them in sync.
+ * SyncToServer - Ignore the server setting when sent. Instead, push
+ * the local setting to the server. Used when changing
+ * settings offline.
+ * SyncFromServer - Update locally stored settings with the value sent
+ * by the server. Used when connecting to the server if
+ * no offline changes are pending to force a sync.
+ * SyncBoth - Changes are updated both ways. This is truly a
+ * 'first come, first serve' scenario, which breaks if
+ * the 'old' value is sent by one peer before the other
+ * end is able to push the new value. An example of this
+ * is changing the MSN nickname offline - the server can
+ * only be updated after it has sent the old value to
+ * the client during connect, destroying the new setting.
+ * Once connected this is often the most useful setting.
+ * DontSync - Do not sync values at all. This is used if settings
+ * are overridden locally, but should not be sent to the
+ * server, nor should the client update server-pushed
+ * values. This can be useful for e.g. contact lists.
+ */
+ enum SyncMode
+ {
+ DontSync = 0x00,
+ SyncToServer = 0x01,
+ SyncFromServer = 0x02,
+ SyncBoth = 0x03
+ };
+
+ /**
+ * The possible MSN online statuses
+ */
+ const Kopete::OnlineStatus NLN; //online
+ const Kopete::OnlineStatus BSY; //busy
+ const Kopete::OnlineStatus BRB; //be right back
+ const Kopete::OnlineStatus AWY; //away
+ const Kopete::OnlineStatus PHN; //on the phone
+ const Kopete::OnlineStatus LUN; //out to lunch
+ const Kopete::OnlineStatus FLN; //offline
+ const Kopete::OnlineStatus HDN; //invisible
+ const Kopete::OnlineStatus IDL; //idle
+ const Kopete::OnlineStatus UNK; //inknown (internal)
+ const Kopete::OnlineStatus CNT; //connecting (internal)
+
+ const Kopete::ContactPropertyTmpl propEmail;
+ const Kopete::ContactPropertyTmpl propPhoneHome;
+ const Kopete::ContactPropertyTmpl propPhoneWork;
+ const Kopete::ContactPropertyTmpl propPhoneMobile;
+ const Kopete::ContactPropertyTmpl propClient;
+ const Kopete::ContactPropertyTmpl propGuid;
+ const Kopete::ContactPropertyTmpl propPersonalMessage; // it's the equivalent of away message.
+
+ enum List
+ {
+ FL, // forward
+ AL, // allow
+ BL, // blocked
+ RL, // reverse
+ PL // pending
+ };
+
+ // Enums used to build the Kopete's MSN ClientId.
+ enum MSNClientInformationFields
+ {
+ WindowsMobile = 0x1,
+ InkFormatGIF = 0x04,
+ InkFormatISF = 0x08,
+ SupportWebcam = 0x10,
+ SupportMultiPacketMessaging = 0x20,
+ MSNMobileDevice = 0x40,
+ MSNDirectDevice = 0x80,
+ WebMessenger = 0x100,
+ SupportDirectIM = 0x4000,
+ SupportWinks = 0x8000,
+ MSNC1 = 0x10000000,
+ MSNC2 = 0x20000000,
+ MSNC3 = 0x30000000,
+ MSNC4 = 0x40000000
+ };
+
+ enum PersonalMessageType
+ {
+ PersonalMessageNormal,
+ PersonalMessageMusic,
+ PersonalMessageGame,
+ PersonalMessageOffice
+ };
+
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData, const QMap<QString, QString> &addressBookData );
+
+ virtual AddContactPage *createAddContactWidget( QWidget *parent , Kopete::Account *i);
+ virtual KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+ virtual Kopete::Account *createNewAccount(const QString &accountId);
+
+ static MSNProtocol* protocol();
+ static bool validContactId(const QString&);
+ QImage scalePicture(const QImage &picture);
+
+private slots:
+ void slotSyncContactList();
+
+private:
+
+ static MSNProtocol *s_protocol;
+
+signals:
+ /**
+ * A new msn invitation has been arrived. plugins can connect this signal to handle invitations.
+ * if the invitationID match to their internal id. they can create a new MSNInvitation and pass it via invitation
+ *
+ * @param invitation should be set by the plugin to the new invitaiton. plugin should check it is equal to 0L before
+ * @param bodyMSG is the whole invitation message
+ * @param cookie is the invitation cookie
+ * @param msnMM is the message manager
+ * @param c is the contact
+ */
+ void invitation(MSNInvitation*& invitation, const QString &bodyMSG , long unsigned int cookie , MSNChatSession* msnMM , MSNContact* c );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnsecureloginhandler.cpp b/kopete/protocols/msn/msnsecureloginhandler.cpp
new file mode 100644
index 00000000..00f862fe
--- /dev/null
+++ b/kopete/protocols/msn/msnsecureloginhandler.cpp
@@ -0,0 +1,131 @@
+/*
+ msnsecureloginhandler.cpp - SSL login for MSN protocol
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "msnsecureloginhandler.h"
+
+// Qt includes
+#include <qregexp.h>
+
+// KDE includes
+#include <kio/job.h>
+#include <kurl.h>
+#include <kdebug.h>
+
+MSNSecureLoginHandler::MSNSecureLoginHandler(const QString &accountId, const QString &password, const QString &authParameters)
+ : m_password(password), m_accountId(accountId), m_authentification(authParameters)
+{
+
+}
+
+MSNSecureLoginHandler::~MSNSecureLoginHandler()
+{
+// kdDebug(14140) << k_funcinfo << endl;
+}
+
+void MSNSecureLoginHandler::login()
+{
+ // Retrive the login server.
+ // Do a reload and don't show the progress.
+ KIO::Job *getLoginServer = KIO::get(KURL("https://nexus.passport.com/rdr/pprdr.asp"), true, false);
+
+ getLoginServer->addMetaData("cookies", "manual");
+ getLoginServer->addMetaData("cache", "reload");
+ getLoginServer->addMetaData("PropagateHttpHeader", "true");
+
+ connect(getLoginServer, SIGNAL(result(KIO::Job *)), this, SLOT(slotLoginServerReceived(KIO::Job* )));
+}
+
+void MSNSecureLoginHandler::slotLoginServerReceived(KIO::Job *loginJob)
+{
+ if(!loginJob->error())
+ {
+ // Retrive the HTTP header
+ QString httpHeaders = loginJob->queryMetaData("HTTP-Headers");
+
+ // Get the login URL using QRegExp
+ QRegExp rx("PassportURLs: DARealm=(.*),DALogin=(.*),DAReg=");
+ rx.search(httpHeaders);
+
+ // Set the loginUrl and loginServer
+ QString loginUrl = rx.cap(2);
+ QString loginServer = loginUrl.section('/', 0, 0);
+
+ kdDebug(14140) << k_funcinfo << loginServer << endl;
+
+ QString authURL = "https://" + loginUrl;
+
+ KIO::Job *authJob = KIO::get(KURL(authURL), true, false);
+ authJob->addMetaData("cookies", "manual");
+
+ QString authRequest = "Authorization: Passport1.4 "
+ "OrgVerb=GET,"
+ "OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,"
+ "sign-in=" + KURL::encode_string(m_accountId) +
+ ",pwd=" + KURL::encode_string( m_password ).replace(',',"%2C") +
+ "," + m_authentification + "\r\n";
+
+// warning, this debug contains the password
+// kdDebug(14140) << k_funcinfo << "Auth request: " << authRequest << endl;
+
+ authJob->addMetaData("customHTTPHeader", authRequest);
+ authJob->addMetaData("SendLanguageSettings", "false");
+ authJob->addMetaData("PropagateHttpHeader", "true");
+ authJob->addMetaData("cookies", "manual");
+ authJob->addMetaData("cache", "reload");
+
+ connect(authJob, SIGNAL(result(KIO::Job *)), this, SLOT(slotTweenerReceived(KIO::Job* )));
+ }
+ else
+ {
+ kdDebug(14140) << k_funcinfo << loginJob->errorString() << endl;
+
+ emit loginFailed();
+ }
+}
+
+void MSNSecureLoginHandler::slotTweenerReceived(KIO::Job *authJob)
+{
+ if(!authJob->error())
+ {
+ QString httpHeaders = authJob->queryMetaData("HTTP-Headers");
+
+// kdDebug(14140) << k_funcinfo << "HTTP headers: " << httpHeaders << endl;
+
+ // Check if we get "401 Unauthorized", thats means it's a bad password.
+ if(httpHeaders.contains(QString::fromUtf8("401 Unauthorized")))
+ {
+// kdDebug(14140) << k_funcinfo << "MSN Login Bad password." << endl;
+ emit loginBadPassword();
+ }
+ else
+ {
+ QRegExp rx("from-PP='(.*)'");
+ rx.search(httpHeaders);
+ QString ticket = rx.cap(1);
+
+ // kdDebug(14140) << k_funcinfo << "Received ticket: " << ticket << endl;
+
+ emit loginSuccesful(ticket);
+ }
+ }
+ else
+ {
+ kdDebug(14140) << k_funcinfo << authJob->errorString() << endl;
+
+ emit loginFailed();
+ }
+}
+#include "msnsecureloginhandler.moc"
diff --git a/kopete/protocols/msn/msnsecureloginhandler.h b/kopete/protocols/msn/msnsecureloginhandler.h
new file mode 100644
index 00000000..8e4dc466
--- /dev/null
+++ b/kopete/protocols/msn/msnsecureloginhandler.h
@@ -0,0 +1,76 @@
+/*
+ msnsecureloginhandler.h - SSL login for MSN protocol
+
+ Copyright (c) 2005 by Michaël Larouche <michael.larouche@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MSNSECURELOGINHANDLER_H
+#define MSNSECURELOGINHANDLER_H
+
+#include <qobject.h>
+
+namespace KIO
+{
+ class Job;
+ class MetaData;
+}
+
+/**
+ * This class handle the login process. It connect to the .NET Password service and retrive the ticket(tweener) to login.
+ * Use KIO.
+ *
+ * @author Michaël Larouche <michael.larouche@kdemail.net>
+*/
+class MSNSecureLoginHandler : public QObject
+{
+Q_OBJECT
+public:
+ MSNSecureLoginHandler(const QString &accountId, const QString &password, const QString &authParameters);
+
+ ~MSNSecureLoginHandler();
+
+ void login();
+
+signals:
+ /**
+ * TODO: return to const QString &
+ */
+ void loginSuccesful(QString ticket);
+ void loginBadPassword();
+ void loginFailed();
+
+private slots:
+ void slotLoginServerReceived(KIO::Job *);
+ /**
+ * We have received our ticket to login.
+ */
+ void slotTweenerReceived(KIO::Job *);
+
+private:
+ /**
+ * Store the password.
+ */
+ QString m_password;
+ /**
+ * Store the accountId.
+ */
+ QString m_accountId;
+ /**
+ * Store the authentification parameters
+ */
+ QString m_authentification;
+
+ void displayMetaData(KIO::MetaData data);
+};
+
+#endif
diff --git a/kopete/protocols/msn/msnsocket.cpp b/kopete/protocols/msn/msnsocket.cpp
new file mode 100644
index 00000000..a650cd83
--- /dev/null
+++ b/kopete/protocols/msn/msnsocket.cpp
@@ -0,0 +1,1099 @@
+/*
+ msnsocket.cpp - Base class for the sockets used in MSN
+
+ Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 "msnsocket.h"
+//#include "msnprotocol.h"
+
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kbufferedsocket.h>
+#include <kserversocket.h>
+#include <kresolver.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+
+#include "kopeteuiglobal.h"
+
+using namespace KNetwork;
+
+class MimeMessage
+{
+ public:
+ MimeMessage(const QString &msg) : message(msg) {}
+
+ QString getValue(const QString &key)
+ {
+ QRegExp rx(key+": ([^\r\n]+)");
+ rx.search(message);
+ return rx.cap(1);
+ }
+ private:
+ QString message;
+};
+
+MSNSocket::MSNSocket(QObject* parent) : QObject (parent)
+{
+ m_onlineStatus = Disconnected;
+ m_socket = 0L;
+ m_useHttp = false;
+ m_timer = 0L;
+}
+
+MSNSocket::~MSNSocket()
+{
+ //if ( m_onlineStatus != Disconnected )
+ // disconnect();
+ delete m_timer;
+ m_timer = 0L;
+ doneDisconnect();
+ if ( m_socket )
+ m_socket->deleteLater();
+}
+
+void MSNSocket::connect( const QString &server, uint port )
+{
+ if ( m_onlineStatus == Connected || m_onlineStatus == Connecting )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Already connected or connecting! Not connecting again." << endl;
+ return;
+ }
+
+ if( m_onlineStatus == Disconnecting )
+ {
+ // Cleanup first.
+ // FIXME: More generic!!!
+ kdWarning( 14140 ) << k_funcinfo << "We're still disconnecting! Deleting socket the hard way first." << endl;
+ delete m_socket;
+ }
+
+ setOnlineStatus( Connecting );
+ m_id = 0;
+ //m_lastId = 0;
+ m_waitBlockSize = 0;
+ m_buffer = Buffer( 0 );
+
+ //m_sendQueue.clear();
+
+ m_server = server;
+ m_port = port;
+
+ if(!m_useHttp)
+ m_socket = new KBufferedSocket( server, QString::number(port) );
+ else {
+ m_socket = new KBufferedSocket( m_gateway, "80" );
+ }
+
+ m_socket->enableRead( true );
+
+ // enableWrite eats the CPU, and we only need it when the queue is
+ // non-empty, so disable it until we have actual data in the queue
+ m_socket->enableWrite( false );
+
+ QObject::connect( m_socket, SIGNAL( readyRead() ), this, SLOT( slotDataReceived() ) );
+ QObject::connect( m_socket, SIGNAL( readyWrite() ), this, SLOT( slotReadyWrite() ) );
+ QObject::connect( m_socket, SIGNAL( hostFound() ), this, SLOT( slotHostFound() ) );
+ QObject::connect( m_socket, SIGNAL( connected( const KResolverEntry&) ), this, SLOT( slotConnectionSuccess() ) );
+ QObject::connect( m_socket, SIGNAL( gotError( int ) ), this, SLOT( slotSocketError( int ) ) );
+ QObject::connect( m_socket, SIGNAL( closed( ) ), this, SLOT( slotSocketClosed( ) ) );
+
+ if(m_useHttp)
+ {
+ if(m_timer == 0L)
+ {
+ m_timer = new QTimer(this, "Http poll timer");
+ // Connect the slot HttpPoll with the timer timeout signal.
+ QObject::connect(m_timer, SIGNAL(timeout()), this, SLOT(slotHttpPoll()));
+ }
+ }
+
+ aboutToConnect();
+
+ // start the asynchronous connection
+ m_socket->connect();
+}
+
+void MSNSocket::disconnect()
+{
+ if(m_useHttp)
+ if(m_timer->isActive()) {
+ // If the timer is still active, stop the timer.
+ m_timer->stop();
+ }
+
+ if ( m_socket )
+ m_socket->closeNow();
+ else
+ slotSocketClosed();
+}
+
+void MSNSocket::aboutToConnect()
+{
+ /* Empty default implementation */
+}
+
+void MSNSocket::doneConnect()
+{
+ setOnlineStatus( Connected );
+}
+
+void MSNSocket::doneDisconnect()
+{
+ setOnlineStatus( Disconnected );
+}
+
+void MSNSocket::setOnlineStatus( MSNSocket::OnlineStatus status )
+{
+ if ( m_onlineStatus == status )
+ return;
+
+ m_onlineStatus = status;
+ emit onlineStatusChanged( status );
+}
+
+void MSNSocket::slotSocketError( int error )
+{
+ kdWarning( 14140 ) << k_funcinfo << "Error: " << error << " (" << m_socket->errorString() << ")" << endl;
+
+ if(!KSocketBase::isFatalError(error))
+ return;
+ //we only care about fatal error
+
+ QString errormsg = i18n( "There was an error while connecting to the MSN server.\nError message:\n" );
+ if ( error == KSocketBase::LookupFailure )
+ errormsg += i18n( "Unable to lookup %1" ).arg( m_socket->peerResolver().nodeName() );
+ else
+ errormsg += m_socket->errorString() ;
+
+ //delete m_socket;
+ m_socket->deleteLater();
+ m_socket = 0L;
+
+ setOnlineStatus( Disconnected );
+ emit connectionFailed();
+ //like if the socket is closed
+ emit socketClosed();
+
+ emit errorMessage( ErrorConnectionError, errormsg );
+}
+
+void MSNSocket::slotDataReceived()
+{
+ int avail = m_socket->bytesAvailable();
+ if ( avail < 0 )
+ {
+ // error!
+ kdWarning( 14140 ) << k_funcinfo << "bytesAvailable() returned " << avail
+ << ". This should not happen!" << endl
+ << "Are we disconnected? Backtrace:" << endl << kdBacktrace() << endl;
+ return;
+ }
+
+ // incoming data, plus an extra char where we pretend a NUL is so the conversion
+ // to QCString doesn't go over the end of the allocated memory.
+ char *buffer = new char[ avail + 1 ];
+ int ret = m_socket->readBlock( buffer, avail );
+
+ if ( ret < 0 )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "readBlock() returned " << ret << "!" <<endl;
+ }
+ else if ( ret == 0 )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "readBlock() returned no data!" <<endl;
+ }
+ else
+ {
+ if ( avail )
+ {
+ if ( ret != avail)
+ {
+ kdWarning( 14140 ) << k_funcinfo << avail << " bytes were reported available, "
+ << "but readBlock() returned only " << ret << " bytes! Proceeding anyway." << endl;
+ }
+ }
+ else
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Read " << ret << " bytes into 4kb block." << endl;
+ }
+
+
+ QString rawData;
+
+ if(m_useHttp)
+ {
+ bool error = false;
+ QByteArray bytes;
+
+ // Check if all data has arrived.
+ rawData = QString(QCString(buffer, avail + 1));
+ bool headers = (rawData.find(QRegExp("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)")) != -1);
+
+ if(headers)
+ {
+ // The http header packet arrived.
+ int endOfHeaders = rawData.find("\r\n\r\n");
+ if((endOfHeaders + 4) == avail)
+ {
+ // Only the response headers data is included.
+ QRegExp re("Content-Length: ([^\r\n]+)");
+ if(re.search(rawData) != -1)
+ {
+ bool valid;
+ int l = re.cap(1).toInt(&valid);
+ if(valid && l > 0)
+ {
+ // The packet contains the headers but does not contain the content data;
+ // buffer the data received and read again.
+ m_buffer.add(buffer, avail);
+
+ delete[] buffer;
+ // Update how much data remains.
+ m_remaining = l;
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Write the received data to the buffer.
+ m_buffer.add(buffer, avail);
+
+ m_remaining -= avail;
+ if(m_remaining != 0)
+ {
+ // We have not received all the content data, read again.
+ delete[] buffer;
+ return;
+ }
+
+ // At this point, we have all the bytes returned from the web request.
+ bytes = m_buffer.take(m_buffer.size());
+ }
+
+ if(bytes.size() == 0)
+ {
+ // The response headers and the content came in one packet.
+ bytes.assign(buffer, avail);
+ }
+
+
+ // Create the web response object from the response bytes.
+ WebResponse response(bytes);
+
+ if(response.getStatusCode() == 100) {
+ return;
+ }
+
+ if(response.getStatusCode() == 200)
+ {
+ // If we received a valid response, read the required headers.
+ // Retrieve the X-MSN-Messenger header.
+ QString header = response.getHeaders()->getValue("X-MSN-Messenger");
+
+ QStringList parts = QStringList::split(";", header.replace(" ", ""));
+ if(!header.isNull() && (parts.count() >= 2))
+ {
+ if(parts[0].find("SessionID", 0) != -1)
+ {
+ // Assign the session id.
+ m_sessionId = parts[0].section("=", 1, 1);
+ }else
+ error = true;
+
+ if(parts[1].find("GW-IP", 0) != -1)
+ {
+ // Assign the gateway IP address.
+ m_gwip = parts[1].section("=", 1, 1);
+ }else
+ error = true;
+
+
+ if(parts.count() > 2)
+ if((parts[2].find("Session", 0) != -1) && (parts[2].section("=", 1, 1) == "close"))
+ {
+ // The http session has been closed by the server, disconnect.
+ kdDebug(14140) << k_funcinfo << "Session closed." << endl;
+ m_bCanPoll = false;
+ disconnect();
+ return;
+ }
+ }else
+ error = true;
+
+ // Retrieve the content length header.
+ header = response.getHeaders()->getValue("Content-Length");
+
+ if(!header.isNull())
+ {
+ bool valid;
+ int length = header.toInt(&valid);
+ if(valid && (length == 0))
+ {
+ // If the response content length is zero, there is nothing to do.
+ m_pending = false;
+ return;
+ }
+
+ if(valid && (length > 0))
+ {
+ // Otherwise, if the content length is greater than zero, get the web response stream.
+ QDataStream *stream = response.getResponseStream();
+ buffer = new char[length];
+ // Read the web response content.
+ stream->readRawBytes(buffer, length);
+ ret = length;
+ }else
+ error = true;
+ }else
+ error = true;
+ }else
+ error = true;
+
+ if(error)
+ {
+ kdDebug(14140) << k_funcinfo << "Http error: " << response.getStatusCode() << " "
+ << response.getStatusDescription() << endl;
+
+ // If we encountered an error, disconnect and return.
+ m_bCanPoll = false;
+ // Disconnect from the service.
+ disconnect();
+ return;
+ }
+ }
+
+ // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug:
+ // all MSN commands start with one or more uppercase characters.
+ // For now just check the first three chars, let's see how accurate it is.
+ // Additionally, if we receive an MSN-P2P packet, strip off anything after the P2P header.
+ rawData = QString( QCString( buffer, ((!m_useHttp)? avail : ret) + 1 ) ).stripWhiteSpace().replace(
+ QRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" );
+
+ bool isBinary = false;
+ for ( uint i = 0; i < 3 ; ++i )
+ {
+ if ( (rawData[ i ] < 'A' || rawData[ i ] > 'Z') && (rawData[ i ] < '0' || rawData[ i ] > '9') )
+ isBinary = true;
+ }
+
+ if ( isBinary )
+ kdDebug( 14141 ) << k_funcinfo << "(Stripped binary data)" << endl;
+ else
+ kdDebug( 14141 ) << k_funcinfo << rawData << endl;
+
+ // fill the buffer with the received data
+ m_buffer.add( buffer, ret );
+
+ slotReadLine();
+
+ if(m_useHttp) {
+ // Set data pending to false.
+ m_pending = false;
+ }
+ }
+
+ // Cleanup.
+ delete[] buffer;
+}
+
+void MSNSocket::slotReadLine()
+{
+ // We have data, first check if it's meant for a block read, otherwise
+ // parse the first line (which will recursively parse the other lines)
+ if ( !pollReadBlock() )
+ {
+ if ( m_buffer.size() >= 3 && ( m_buffer.data()[ 0 ] == '\0' || m_buffer.data()[ 0 ]== '\1' ) )
+ {
+ bytesReceived( m_buffer.take( 3 ) );
+ QTimer::singleShot( 0, this, SLOT( slotReadLine() ) );
+ return;
+ }
+
+ int index = -1;
+ for ( uint x = 0; m_buffer.size() > x + 1; ++x )
+ {
+ if ( ( m_buffer[ x ] == '\r' ) && ( m_buffer[ x + 1 ] == '\n' ) )
+ {
+ index = x;
+ break;
+ }
+ }
+
+ if ( index != -1 )
+ {
+ QString command = QString::fromUtf8( m_buffer.take( index + 2 ), index );
+ command.replace( "\r\n", "" );
+ //kdDebug( 14141 ) << k_funcinfo << command << endl;
+
+ // Don't block the GUI while parsing data, only do a single line!
+ // (Done before parseLine() to prevent a potential crash)
+ QTimer::singleShot( 0, this, SLOT( slotReadLine() ) );
+
+ parseLine( command );
+ // WARNING: At this point 'this' can be deleted (when disconnecting)
+ }
+ }
+}
+
+void MSNSocket::readBlock( uint len )
+{
+ if ( m_waitBlockSize )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Cannot wait for data block: still waiting for other block of size "
+ << m_waitBlockSize << "! Data will not be returned." << endl;
+ return;
+ }
+
+ m_waitBlockSize = len;
+
+ //kdDebug( 14140 ) << k_funcinfo << "Preparing for block read of size " << len << endl;
+
+ // Try to return the data now, if available. Otherwise slotDataReady
+ // will do this whenever all data is there.
+ pollReadBlock();
+}
+
+bool MSNSocket::pollReadBlock()
+{
+ if ( !m_waitBlockSize )
+ {
+ return false;
+ }
+ else if ( m_buffer.size() < m_waitBlockSize )
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Waiting for data. Received: " << m_buffer.size() << ", required: " << m_waitBlockSize << endl;
+ return true;
+ }
+
+ QByteArray block = m_buffer.take( m_waitBlockSize );
+
+ //kdDebug( 14140 ) << k_funcinfo << "Successfully read block of size " << m_waitBlockSize << endl;
+
+ m_waitBlockSize = 0;
+ emit blockRead( block);
+
+ return false;
+}
+
+void MSNSocket::parseLine( const QString &str )
+{
+ QString cmd = str.section( ' ', 0, 0 );
+ QString data = str.section( ' ', 2 ).replace( "\r\n" , "" );
+
+ bool isNum;
+ uint id = str.section( ' ', 1, 1 ).toUInt( &isNum );
+
+ // In some rare cases, like the 'NLN' / 'FLN' commands no id at all
+ // is sent. Here it's actually a real parameter...
+ if ( !isNum )
+ data = str.section( ' ', 1, 1 ) + " " + data;
+
+ //if ( isNum && id )
+ // m_lastId = id;
+
+ //kdDebug( 14140 ) << k_funcinfo << "Parsing command " << cmd << " (ID " << id << "): '" << data << "'" << endl;
+
+ data.replace( "\r\n", "" );
+ bool isError;
+ uint errorCode = cmd.toUInt( &isError );
+ if ( isError )
+ handleError( errorCode, id );
+ else
+ parseCommand( cmd, id, data );
+}
+
+void MSNSocket::handleError( uint code, uint /* id */ )
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ QString msg;
+ ErrorType type = ErrorServerError;
+ switch ( code )
+ {
+/*
+ // We cant show message for error we don't know what they are or not related to the correct socket
+ // Theses following messages are not so instructive
+ case 205:
+ msg = i18n ( "An invalid username has been specified.\nPlease correct it, and try to reconnect.\n" );
+ break;
+ case 201:
+ msg = i18n ( "Fully Qualified domain name missing.\n" );
+ break;
+ case 207:
+ msg = i18n ( "You are already logged in!\n" );
+ break;
+ case 208:
+ msg = i18n ( "You specified an invalid username.\nPlease correct it, and try to reconnect.\n");
+ break;
+ case 209:
+ msg = i18n ( "Your nickname is invalid. Please check it, correct it,\nand try to reconnect.\n" );
+ break;
+ case 210:
+ msg = i18n ( "Your list has reached its maximum capacity.\nNo more contacts can be added, unless you remove some first.\n" );
+ break;
+ case 216:
+ msg = i18n ( "This user is not in your contact list.\n " );
+ break;
+ case 300:
+ msg = i18n ( "Some required fields are missing. Please fill them in and try again.\n" );
+ break;
+ case 302:
+ msg = i18n ( "You are not logged in.\n" );
+ break;
+*/
+ case 500:
+ msg = i18n ( "An internal server error occurred. Please try again later." );
+ type = MSNSocket::ErrorCannotConnect;
+ break;
+ case 502:
+ msg = i18n ( "It is no longer possible to perform this operation. The MSN server does not allow it anymore." );
+ type = MSNSocket::ErrorServerError;
+ break;
+ case 600:
+ case 910:
+ case 912:
+ case 921:
+ case 922:
+ msg = i18n ( "The MSN server is busy. Please try again later." );
+ type = MSNSocket::ErrorConnectionError;
+ break;
+ case 601:
+ case 604:
+ case 605:
+ case 914:
+ case 915:
+ case 916:
+ case 917:
+ msg = i18n ( "The server is not available at the moment. Please try again later." );
+ type = MSNSocket::ErrorCannotConnect;
+ break;
+ // Server error
+ default:
+ // FIXME: if the error causes a disconnect, it will crash, but we can't disconnect every time
+ msg = i18n( "Unhandled MSN error code %1 \n"
+ "Please fill a bug report with a detailed description and if possible the last console debug output." ).arg( code );
+ // "See http://www.hypothetic.org/docs/msn/basics.php for a description of all error codes."
+ break;
+ }
+
+ if ( !msg.isEmpty() )
+ emit errorMessage( type, msg );
+
+ return;
+}
+
+int MSNSocket::sendCommand( const QString &cmd, const QString &args, bool addId, const QByteArray &body, bool binary )
+{
+ if ( !m_socket )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "m_socket == NULL!" << endl;
+ return -1;
+ }
+
+ QCString data = cmd.utf8();
+ if ( addId )
+ data += " " + QString::number( m_id ).utf8();
+
+ if ( !args.isEmpty() )
+ data += " " + args.utf8();
+
+ // Add length in bytes, not characters
+ if ( !body.isEmpty() )
+ data += " " + QString::number( body.size() - (binary ? 0 : 1 ) ).utf8();
+
+ data += "\r\n";
+
+
+ // the command will be sent in slotReadyWrite
+ QByteArray bytes;
+ const uint length = data.length();
+ bytes.duplicate(data.data(), length);
+ if(!body.isEmpty())
+ {
+ uint l = body.size() - (binary ? 0 : 1);
+ bytes.resize(length + l);
+ for(uint i=0; i < l; i++)
+ bytes[length + i] = body[i];
+ }
+
+ // Add the request to the queue.
+ m_sendQueue.append(bytes);
+ m_socket->enableWrite(true);
+
+ if ( addId )
+ {
+ ++m_id;
+ return m_id - 1;
+ }
+
+ return 0;
+}
+
+void MSNSocket::slotReadyWrite()
+{
+ if ( !m_sendQueue.isEmpty() )
+ {
+ // If the command queue is not empty, retrieve the first command.
+ QValueList<QByteArray>::Iterator it = m_sendQueue.begin();
+
+ if(m_useHttp)
+ {
+ // If web response data is not pending, send the http request.
+ if(!m_pending)
+ {
+ m_pending = true;
+ // Temporarily disable http polling.
+ m_bCanPoll = false;
+ // Set the host to the msn gateway by default.
+ QString host = m_gateway;
+ QString query; // Web request query string.
+
+ if(m_bIsFirstInTransaction)
+ {
+ query.append("Action=open&Server=");
+ query.append(m_type);
+
+ query += "&IP=" + m_server;
+
+ m_bIsFirstInTransaction = false;
+ }
+ else
+ {
+ // If this is not the first request sent in the transaction,
+ // only add the session Id.
+ host = m_gwip;
+ query += "SessionID=" + m_sessionId;
+ }
+
+ // Create the web request headers.
+ QString s = makeHttpRequestString(host, query, (*it).size());
+
+ uint length = s.length();
+ // Create the web request bytes.
+ QByteArray bytes(length + (*it).size());
+
+ // Copy the request headers into the request bytes.
+ for(uint i=0; i < length; i++)
+ bytes[i] = s.ascii()[i];
+ // Copy the request body into the request bytes.
+ for(uint i=0; i < (*it).size(); i++)
+ bytes[length + i] = (*it)[i];
+
+ kdDebug( 14141 ) << k_funcinfo << "Sending http command: " << QString(*it).stripWhiteSpace() << endl;
+
+ // Write the request bytes to the socket.
+ m_socket->writeBlock(bytes.data(), bytes.size());
+
+ // Remove the request from the request queue.
+ m_sendQueue.remove(it);
+
+ if(m_sendQueue.isEmpty())
+ {
+ // Disable sending requests.
+ m_socket->enableWrite(false);
+ // If the request queue is empty, poll the server.
+ m_bCanPoll = true;
+ }
+ }
+ }
+ else
+ {
+ // Otherwise, send the command normally.
+
+ // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug:
+ // When sending an MSN-P2P packet, strip off anything after the P2P header.
+ QString debugData = QString( *it ).stripWhiteSpace().replace(
+ QRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" );
+ kdDebug( 14141 ) << k_funcinfo << "Sending command: " << debugData << endl;
+
+ m_socket->writeBlock( *it, ( *it ).size() );
+ m_sendQueue.remove( it );
+
+ // If the queue is empty agalin stop waiting for readyWrite signals
+ // because of the CPU usage
+ if ( m_sendQueue.isEmpty() )
+ m_socket->enableWrite( false );
+ }
+ }
+ else
+ {
+ m_socket->enableWrite( false );
+
+ if(m_useHttp)
+ {
+ // If the request queue is empty, poll the server.
+ m_bCanPoll = true;
+ }
+ }
+}
+
+QString MSNSocket::escape( const QString &str )
+{
+ //return ( KURL::encode_string( str, 106 ) );
+ //It's not needed to encode everything. The official msn client only encode spaces and %
+ //If we encode more, the size can be longer than excepted.
+
+ int old_length= str.length();
+ QChar *new_segment = new QChar[ old_length * 3 + 1 ];
+ int new_length = 0;
+
+ for ( int i = 0; i < old_length; i++ )
+ {
+ unsigned short character = str[i].unicode();
+
+ if( character <= 32 || character == '%' )
+ {
+ new_segment[ new_length++ ] = '%';
+
+ unsigned int c = character / 16;
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_segment[ new_length++ ] = c;
+
+ c = character % 16;
+ c += (c > 9) ? ('A' - 10) : '0';
+ new_segment[ new_length++ ] = c;
+ }
+ else
+ new_segment[ new_length++ ] = str[i];
+ }
+
+ QString result = QString(new_segment, new_length);
+ delete [] new_segment;
+ return result;
+
+}
+
+QString MSNSocket::unescape( const QString &str )
+{
+ QString str2 = KURL::decode_string( str, 106 );
+ //remove msn+ colors code
+ str2 = str2.replace( QRegExp("[\\x1-\\x8]"), "" ); // old msn+ colors
+ // added by kaoul <erwin.kwolek at gmail.com>
+ str2 = str2.replace( QRegExp("\\xB7[&@\'#0]"),""); // dot ...
+ str2 = str2.replace( QRegExp("\\xB7\\$,?\\d{1,2}"),""); // dot dollar (comma)? 0-99
+
+ return str2;
+}
+
+void MSNSocket::slotConnectionSuccess()
+{
+ if(m_useHttp)
+ {
+ // If we are connected, set the data pending flag to false,
+ // and disable http polling.
+ m_pending = false;
+ m_bCanPoll = false;
+ // If we are connected, start the timer.
+ m_timer->start(2000, false);
+ }
+
+ //kdDebug( 14140 ) << k_funcinfo << endl;
+ doneConnect();
+}
+
+void MSNSocket::slotHostFound()
+{
+ // nothing to do
+}
+
+void MSNSocket::slotSocketClosed()
+{
+ kdDebug( 14140 ) << k_funcinfo << "Socket closed. " << endl;
+
+ if ( !m_socket || m_onlineStatus == Disconnected )
+ {
+ kdDebug( 14140 ) << k_funcinfo << "Socket already deleted or already disconnected" << endl;
+ return;
+ }
+
+ doneDisconnect();
+
+ m_buffer = Buffer( 0 );
+ //delete m_socket;
+ m_socket->deleteLater();
+ m_socket = 0L;
+
+ emit socketClosed();
+}
+
+void MSNSocket::slotHttpPoll()
+{
+ if(m_pending || !m_bCanPoll){
+ // If data is pending or poll has been temporary disabled, return.
+ return;
+ }
+
+ // Create the http request headers.
+ const QCString headers = makeHttpRequestString(m_gwip, "Action=poll&SessionID=" + m_sessionId, 0).utf8();
+ m_socket->writeBlock(headers, headers.length());
+ // Wait for the response.
+ m_pending = true;
+ m_socket->enableWrite(true);
+}
+
+// Used in MSNFileTransferSocket
+// FIXME: Why is this here if it's only used for file transfer? - Martijn
+void MSNSocket::bytesReceived( const QByteArray & /* data */ )
+{
+ kdWarning( 14140 ) << k_funcinfo << "Unknown bytes were received" << endl;
+}
+
+void MSNSocket::sendBytes( const QByteArray &data )
+{
+ if ( !m_socket )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Not yet connected" << endl;
+ return;
+ }
+
+ m_socket->writeBlock( data, data.size() );
+ m_socket->enableWrite( true );
+}
+
+bool MSNSocket::setUseHttpMethod( bool useHttp )
+{
+ if( m_useHttp == useHttp )
+ return true;
+
+ if( useHttp ) {
+ QString s = QString( this->className() ).lower();
+ if( s == "msnnotifysocket" )
+ m_type = "NS";
+ else if( s == "msnswitchboardsocket" )
+ m_type = "SB";
+ else
+ m_type = QString::null;
+
+ if( m_type.isNull() )
+ return false;
+
+ m_bCanPoll = false;
+ m_bIsFirstInTransaction = true;
+ m_pending = false;
+ m_remaining = 0;
+ m_gateway = "gateway.messenger.hotmail.com";
+ }
+
+ if ( m_onlineStatus != Disconnected )
+ disconnect();
+
+ m_useHttp = useHttp;
+
+ return true;
+}
+
+bool MSNSocket::useHttpMethod() const
+{
+ return m_useHttp;
+}
+
+bool MSNSocket::accept( KServerSocket *server )
+{
+ if ( m_socket )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Socket already exists!" << endl;
+ return false;
+ }
+
+ m_socket = static_cast<KBufferedSocket*>(server->accept());
+
+ if ( !m_socket )
+ {
+// kdWarning( 14140 ) << k_funcinfo << "Socket not created. Error nb" << server->error() << " : " << server->errorString() << endl;
+ return false;
+ }
+
+ kdDebug( 14140 ) << k_funcinfo << "incoming connection accepted" << endl;
+
+ setOnlineStatus( Connecting );
+
+ m_id = 0;
+ //m_lastId = 0;
+ m_waitBlockSize = 0;
+
+ m_socket->setBlocking( false );
+ m_socket->enableRead( true );
+ m_socket->enableWrite( true );
+
+ QObject::connect( m_socket, SIGNAL( readyRead() ), this, SLOT( slotDataReceived() ) );
+ QObject::connect( m_socket, SIGNAL( readyWrite() ), this, SLOT( slotReadyWrite() ) );
+ QObject::connect( m_socket, SIGNAL( closed() ), this, SLOT( slotSocketClosed() ) );
+ QObject::connect( m_socket, SIGNAL( gotError( int ) ), this, SLOT( slotSocketError( int ) ) );
+
+ doneConnect();
+ return true;
+}
+
+QString MSNSocket::getLocalIP()
+{
+ if ( !m_socket )
+ return QString::null;
+
+ const KSocketAddress address = m_socket->localAddress();
+
+ QString ip = address.nodeName();
+
+ kdDebug( 14140 ) << k_funcinfo << "IP: " << ip <<endl;
+ //delete address;
+ return ip;
+}
+
+MSNSocket::Buffer::Buffer( unsigned int sz )
+: QByteArray( sz )
+{
+}
+
+MSNSocket::Buffer::~Buffer()
+{
+}
+
+void MSNSocket::Buffer::add( char *str, unsigned int sz )
+{
+ char *b = new char[ size() + sz ];
+ for ( uint f = 0; f < size(); f++ )
+ b[ f ] = data()[ f ];
+ for ( uint f = 0; f < sz; f++ )
+ b[ size() + f ] = str[ f ];
+
+ duplicate( b, size() + sz );
+ delete[] b;
+}
+
+QByteArray MSNSocket::Buffer::take( unsigned blockSize )
+{
+ if ( size() < blockSize )
+ {
+ kdWarning( 14140 ) << k_funcinfo << "Buffer size " << size() << " < asked size " << blockSize << "!" << endl;
+ return QByteArray();
+ }
+
+ QByteArray rep( blockSize );
+ for( uint i = 0; i < blockSize; i++ )
+ rep[ i ] = data()[ i ];
+
+ char *str = new char[ size() - blockSize ];
+ for ( uint i = 0; i < size() - blockSize; i++ )
+ str[ i ] = data()[ blockSize + i ];
+ duplicate( str, size() - blockSize );
+ delete[] str;
+
+ return rep;
+}
+
+QString MSNSocket::makeHttpRequestString(const QString& host, const QString& query, uint contentLength)
+{
+ QString s(
+ "POST http://" + host + "/gateway/gateway.dll?" + query + " HTTP/1.1\r\n" +
+ "Accept: */*\r\n" +
+ "Accept-Language: en-us\r\n" +
+ "User-Agent: MSMSGS\r\n" +
+ "Host: " + host + "\r\n" +
+ "Proxy-Connection: Keep-Alive\r\n" +
+ "Connection: Keep-Alive\r\n" +
+ "Pragma: no-cache\r\n" +
+ "Content-Type: application/x-msn-messenger\r\n" +
+ "Content-Length: " + QString::number(contentLength) + "\r\n" +
+ "\r\n");
+ return s;
+}
+
+MSNSocket::WebResponse::WebResponse(const QByteArray& bytes)
+{
+ m_statusCode = 0;
+ m_stream = 0;
+
+ int headerEnd;
+ QString header;
+ QString data(QCString(bytes, bytes.size() + 1));
+
+ // Parse the HTTP status header
+ QRegExp re("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)");
+ headerEnd = data.find("\r\n");
+ header = data.left( (headerEnd == -1) ? 20 : headerEnd );
+
+ re.search(header);
+ m_statusCode = re.cap(1).toInt();
+ m_statusDescription = re.cap(2);
+
+ // Remove the web response status header.
+ data = data.mid(headerEnd + 2, (data.find("\r\n\r\n") + 2) - (headerEnd + 2));
+ // Create a MimeMessage, removing the HTTP status header
+ m_headers = new MimeMessage(data);
+
+ // Retrieve the contentlength header.
+ header = m_headers->getValue("Content-Length");
+ if(!header.isNull())
+ {
+ bool valid;
+ int length = header.toInt(&valid);
+ if(valid && length > 0)
+ {
+ // If the content length is valid, and not zero,
+ // copy the web response content bytes.
+ int offset = bytes.size() - length;
+
+ QByteArray content(length);
+ for(int i=0; i < length; i++)
+ content[i] = bytes[offset + i];
+ // Create the web response stream from the response content bytes.
+ m_stream = new QDataStream(content, IO_ReadOnly);
+ }
+ }
+}
+
+MSNSocket::WebResponse::~WebResponse()
+{
+ delete m_headers;
+ m_headers = 0;
+ delete m_stream;
+ m_stream = 0;
+}
+
+MimeMessage* MSNSocket::WebResponse::getHeaders()
+{
+ return m_headers;
+}
+
+QDataStream* MSNSocket::WebResponse::getResponseStream()
+{
+ return m_stream;
+}
+
+int MSNSocket::WebResponse::getStatusCode()
+{
+ return m_statusCode;
+}
+
+QString MSNSocket::WebResponse::getStatusDescription()
+{
+ return m_statusDescription;
+}
+
+
+#include "msnsocket.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnsocket.h b/kopete/protocols/msn/msnsocket.h
new file mode 100644
index 00000000..85df08c7
--- /dev/null
+++ b/kopete/protocols/msn/msnsocket.h
@@ -0,0 +1,362 @@
+/*
+ msnsocket.h - Base class for the sockets used in MSN
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2004 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 MSNSOCKET_H
+#define MSNSOCKET_H
+
+#include <qobject.h>
+#include <qdatastream.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+#include <qvaluelist.h>
+
+#include "kopete_export.h"
+
+namespace KNetwork {
+ class KBufferedSocket;
+ class KServerSocket;
+}
+
+class MimeMessage;
+
+/**
+ * @author Martijn Klingens <klingens@kde.org>
+ *
+ * MSNSocket encapsulates the common functionality shared by the Dispatch
+ * Server, the Notification Server and the Switchboard Server. It is
+ * inherited by the various specialized classes.
+ */
+class KOPETE_EXPORT MSNSocket : public QObject
+{
+ Q_OBJECT
+
+public:
+ MSNSocket(QObject* parent=0l);
+ ~MSNSocket();
+
+ /**
+ * Asynchronously read a block of data of the specified size. When the
+ * data is available, the blockRead() signal will be emitted with the
+ * data as parameter.
+ *
+ * NOTE: As the block queue takes precedence over the line-based
+ * command-processing this method can effectively block all
+ * communications when passed a wrong length!
+ */
+ void readBlock( uint len );
+
+ /**
+ * OnlineStatus encapsulates the 4 states a connection can be in,
+ * Connecting, Connected, Disconnecting, Disconnected. Connecting
+ * and Disconnecting are in the default implementation not used,
+ * because the socket connect is an atomic operation and not yet
+ * performed asynchronously.
+ * In derived classes, like the Notification Server, this state is
+ * actively used, because merely having a socket connection established
+ * by no means indicates we're actually online - the rest of the
+ * handshake likely has to follow first.
+ */
+ enum OnlineStatus { Connecting, Connected, Disconnecting, Disconnected };
+ enum LookupStatus { Processing, Success, Failed };
+ enum Transport { TcpTransport, HttpTransport };
+ enum ErrorType { ErrorConnectionLost, ErrorConnectionError, ErrorCannotConnect, ErrorServerError, ErrorInformation};
+
+ OnlineStatus onlineStatus() { return m_onlineStatus; }
+
+ /*
+ * return the local ip.
+ * Used for filetransfer
+ */
+ QString getLocalIP();
+
+ //BEGIN Http
+
+ virtual bool setUseHttpMethod( bool useHttp );
+ bool useHttpMethod() const;
+
+ //END
+
+public slots:
+ void connect( const QString &server, uint port );
+ virtual void disconnect();
+
+
+ /**
+ * Send an MSN command to the socket
+ *
+ * For debugging it's convenient to have this method public, but using
+ * it outside this class is deprecated for any other use!
+ *
+ * The size of the body (if any) is automatically added to the argument
+ * list and shouldn't be explicitly specified! This size is in bytes
+ * instead of characters to reflect what actually goes over the wire.
+ *
+ * if the param binary is set to true, then, the body is send as a binary message
+ *
+ * return the id
+ */
+ int sendCommand( const QString &cmd, const QString &args = QString::null,
+ bool addId = true, const QByteArray &body = QByteArray() , bool binary=false );
+
+signals:
+ /**
+ * A block read is ready.
+ * After this the normal line-based reads go on again
+ */
+ void blockRead( const QByteArray &block );
+
+ /**
+ * The online status has changed
+ */
+ void onlineStatusChanged( MSNSocket::OnlineStatus status );
+
+ /**
+ * The connection failed
+ */
+ void connectionFailed();
+
+ /**
+ * The connection was closed
+ */
+ void socketClosed();
+
+ /**
+ * A error has occured. Handle the display of the message.
+ */
+ void errorMessage( int type, const QString &msg );
+
+protected:
+ /**
+ * Convenience method: escape spaces with '%20' for use in the protocol.
+ * Doesn't escape any other sequence.
+ */
+ QString escape( const QString &str );
+
+ /**
+ * And the other way round...
+ */
+ QString unescape( const QString &str );
+
+ /**
+ * Set the online status. Emits onlineStatusChanged.
+ */
+ void setOnlineStatus( OnlineStatus status );
+
+ /**
+ * This method is called directly before the socket will actually connect.
+ * Override in derived classes to setup whatever is needed before connect.
+ */
+ virtual void aboutToConnect();
+
+ /**
+ * Directly after the connect, this method is called. The default
+ * implementation sets the OnlineStatus to Connected, be sure to override
+ * this if a handshake is required.
+ */
+ virtual void doneConnect();
+
+ /**
+ * Directly after the disconnect, this method is called before the actual
+ * cleanup takes place. The socket is close here. Cleanup internal
+ * variables here.
+ */
+ virtual void doneDisconnect();
+
+ /**
+ * Handle an MSN error condition.
+ * The default implementation displays a generic error message and
+ * closes the connection. Override to allow more graceful handling and
+ * possibly recovery.
+ */
+ virtual void handleError( uint code, uint id );
+
+ /**
+ * Handle an MSN command response line.
+ * This method is pure virtual and *must* be overridden in derived
+ * classes.
+ */
+ virtual void parseCommand( const QString &cmd, uint id,
+ const QString &data ) = 0;
+
+ /**
+ * Used in MSNFileTransferSocket
+ */
+ virtual void bytesReceived( const QByteArray & );
+ bool accept( KNetwork::KServerSocket * );
+ void sendBytes( const QByteArray &data );
+
+ const QString &server() { return m_server; }
+ uint port() { return m_port; }
+
+ /**
+ * The last confirmed ID by the server
+ */
+ //uint m_lastId;
+
+private slots:
+ void slotDataReceived();
+ /**
+ * If the socket emits a connectionFailed() then this slot is called
+ * to handle the error.
+ */
+ void slotSocketError( int error );
+
+ /*
+ * Calls connectDone() when connection is successfully established.
+ */
+ void slotConnectionSuccess();
+
+ /**
+ * Sets m_lookupProgress to 'Finished' if count > 0 or 'Failed' if count = 0.
+ */
+ void slotHostFound( );
+
+ /**
+ * Check if new lines of data are available and process the first line
+ */
+ void slotReadLine();
+
+ void slotSocketClosed();
+
+ //BEGIN Http
+
+ /**
+ * Sends a poll request to the msn gateway when using HttpTransport.
+ * equivalent to sending a PNG command over TcpTransport.
+ */
+ void slotHttpPoll();
+
+ //END
+
+protected slots:
+ virtual void slotReadyWrite();
+
+private:
+ /**
+ * Check if we're waiting for a block of raw data. Emits blockRead()
+ * when the data is available.
+ * Returns true when still waiting and false when there is no pending
+ * read, or when the read is successfully handled.
+ */
+ bool pollReadBlock();
+
+ /**
+ * The id of the message sent to the MSN server. This ID will increment
+ * for each subsequent message sent.
+ */
+ uint m_id;
+
+ /**
+ * Queue of pending commands (should be mostly empty, but is needed to
+ * send more than one command to the server)
+ */
+ QValueList<QByteArray> m_sendQueue;
+
+ /**
+ * Parse a single line of data.
+ * Will call either parseCommand or handleError depending on the type of
+ * data received.
+ */
+ void parseLine( const QString &str );
+
+ KNetwork::KBufferedSocket *m_socket;
+ OnlineStatus m_onlineStatus;
+
+ QString m_server;
+ uint m_port;
+
+ /**
+ * The size of the requested block for block-based reads
+ */
+ uint m_waitBlockSize;
+
+ class Buffer : public QByteArray
+ {
+ public:
+ Buffer( unsigned size = 0 );
+ ~Buffer();
+ void add( char *str, unsigned size );
+ QByteArray take( unsigned size );
+ };
+
+ Buffer m_buffer;
+
+ //BEGIN Http
+
+ /**
+ * Makes a http request headers string using the specified, host, query, and content length.
+ * return: The string containing the http request headers.
+ */
+ QString makeHttpRequestString(const QString& host, const QString& query, uint contentLength);
+
+ bool m_useHttp; // Indicates whether to use the msn http gateway to connect to the msn service.
+ bool m_bCanPoll; // Indicates whether polling of the http server is allowed.
+ bool m_bIsFirstInTransaction; // Indicates whether pending message to be sent is the first in the transaction.
+ // If so, the gateway is used.
+ // Use the gateway only for initial connected state; Otherwise, use the host.
+ QString m_gateway; // Msn http gateway domain name.
+ QString m_gwip; // The ip address of the msn gateway.
+ QString m_sessionId; // session id.
+ QTimer *m_timer; // Msn http poll timer.
+ QString m_type; // Indicates the type of socket being used. NS or SB
+ bool m_pending; // Indicates whether a http response is pending.
+ int m_remaining; // Indicates how many bytes of content data remain
+ // to be received if the content bytes are sent in
+ // a seperate packet(s).
+
+ /**
+ * Provides access to information returned from a URI request.
+ */
+ class WebResponse
+ {
+ public:
+ WebResponse(const QByteArray& bytes);
+ ~WebResponse();
+
+ /**
+ * Gets the headers associated with this response from the server.
+ */
+ MimeMessage* getHeaders();
+ /**
+ * Gets the data stream used to read the body of the response from the server.
+ */
+ QDataStream* getResponseStream();
+ /**
+ * Gets the status code of the response.
+ */
+ int getStatusCode();
+ /**
+ * Gets the status description returned with the response.
+ */
+ QString getStatusDescription();
+
+ private:
+ MimeMessage *m_headers;
+ QDataStream *m_stream;
+ int m_statusCode;
+ QString m_statusDescription;
+ };
+
+ //END
+};
+
+#endif
+
diff --git a/kopete/protocols/msn/msnswitchboardsocket.cpp b/kopete/protocols/msn/msnswitchboardsocket.cpp
new file mode 100644
index 00000000..ae09a93c
--- /dev/null
+++ b/kopete/protocols/msn/msnswitchboardsocket.cpp
@@ -0,0 +1,1142 @@
+/*
+ msnswitchboardsocket.cpp - switch board connection socket
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 "msnswitchboardsocket.h"
+
+#include <stdlib.h>
+#include <time.h>
+#include <cmath>
+
+// qt
+#include <qstylesheet.h>
+#include <qregexp.h>
+#include <qimage.h>
+#include <qtimer.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+// kde
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <ktempfile.h>
+#include <kconfig.h>
+#include <kmdcodec.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+
+// for the display picture
+#include <msncontact.h>
+#include "msnnotifysocket.h"
+
+//kopete
+#include "msnaccount.h"
+#include "msnprotocol.h"
+#include "kopetemessage.h"
+#include "kopetecontact.h"
+#include "kopeteuiglobal.h"
+#include "private/kopeteemoticons.h"
+//#include "kopeteaccountmanager.h"
+//#include "kopeteprotocol.h"
+
+#include "sha1.h"
+
+#include "dispatcher.h"
+using P2P::Dispatcher;
+
+MSNSwitchBoardSocket::MSNSwitchBoardSocket( MSNAccount *account , QObject *parent )
+: MSNSocket( parent )
+{
+ m_account = account;
+ m_recvIcons=0;
+ m_emoticonTimer=0L;
+ m_chunks=0;
+ m_clientcapsSent=false;
+ m_dispatcher = 0l;
+ m_keepAlive = 0l;
+ m_keepAliveNb=0;
+}
+
+MSNSwitchBoardSocket::~MSNSwitchBoardSocket()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ QMap<QString , QPair<QString , KTempFile*> >::Iterator it;
+ for ( it = m_emoticons.begin(); it != m_emoticons.end(); ++it )
+ {
+ delete it.data().second;
+ }
+}
+
+void MSNSwitchBoardSocket::connectToSwitchBoard(QString ID, QString address, QString auth)
+{
+ // we need these for the handshake later on (when we're connected)
+ m_ID = ID;
+ m_auth = auth;
+
+ QString server = address.left( address.find( ":" ) );
+ uint port = address.right( address.length() - address.findRev( ":" ) - 1 ).toUInt();
+
+ QObject::connect( this, SIGNAL( blockRead( const QByteArray & ) ),
+ this, SLOT(slotReadMessage( const QByteArray & ) ) );
+
+ QObject::connect( this, SIGNAL( onlineStatusChanged( MSNSocket::OnlineStatus ) ),
+ this, SLOT( slotOnlineStatusChanged( MSNSocket::OnlineStatus ) ) );
+
+ QObject::connect( this, SIGNAL( socketClosed( ) ),
+ this, SLOT( slotSocketClosed( ) ) );
+
+ connect( server, port );
+}
+
+void MSNSwitchBoardSocket::handleError( uint code, uint id )
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ QString msg;
+ MSNSocket::ErrorType type;
+
+ switch( code )
+ {
+ case 208:
+ {
+ msg = i18n( "Invalid user:\n"
+ "this MSN user does not exist; please check the MSN ID." );
+ type = MSNSocket::ErrorServerError;
+
+ userLeftChat(m_msgHandle , i18n("user never joined"));
+ break;
+ }
+ case 215:
+ {
+ msg = i18n( "The user %1 is already in this chat." ).arg( m_msgHandle );
+ type = MSNSocket::ErrorServerError;
+
+ //userLeftChat(m_msgHandle , i18n("user was twice in this chat") ); //(the user shouln't join there
+ break;
+ }
+ case 216:
+ {
+ msg = i18n( "The user %1 is online but has blocked you:\nyou can not talk to this user." ).arg( m_msgHandle );
+ type = MSNSocket::ErrorInformation;
+
+ userLeftChat(m_msgHandle, i18n("user blocked you"));
+ break;
+ }
+ case 217:
+ {
+ // TODO: we need to know the nickname instead of the handle.
+ msg = i18n( "The user %1 is currently not signed in.\n" "Messages will not be delivered." ).arg( m_msgHandle );
+ type = MSNSocket::ErrorServerError;
+
+ userLeftChat(m_msgHandle, i18n("user disconnected"));
+ break;
+ }
+ case 713:
+ {
+ QString msg = i18n( "You are trying to invite too many contacts to this chat at the same time" ).arg( m_msgHandle );
+ type = MSNSocket::ErrorInformation;
+
+ userLeftChat(m_msgHandle, i18n("user blocked you"));
+ break;
+ }
+ case 911:
+ {
+ msg = i18n("Kopete MSN plugin has trouble authenticating with switchboard server.");
+ type = MSNSocket::ErrorServerError;
+
+ break;
+ }
+ default:
+ MSNSocket::handleError( code, id );
+ break;
+ }
+
+ if( !msg.isEmpty() )
+ emit errorMessage( type, msg );
+}
+
+void MSNSwitchBoardSocket::parseCommand( const QString &cmd, uint id ,
+ const QString &data )
+{
+ if( cmd == "NAK" )
+ {
+ emit msgAcknowledgement(id, false); // msg was not accepted
+ }
+ else if( cmd == "ACK" )
+ {
+ emit msgAcknowledgement(id, true); // msg has received
+ }
+ else if( cmd == "JOI" )
+ {
+ // new user joins the chat, update user in chat list
+ QString handle = data.section( ' ', 0, 0 );
+ QString screenname = unescape(data.section( ' ', 1, 1 ));
+ if( !m_chatMembers.contains( handle ) )
+ m_chatMembers.append( handle );
+ emit userJoined( handle, screenname, false );
+ }
+ else if( cmd == "IRO" )
+ {
+ // we have joined a multi chat session- this are the users in this chat
+ QString handle = data.section( ' ', 2, 2 );
+ if( !m_chatMembers.contains( handle ) )
+ m_chatMembers.append( handle );
+
+ QString screenname = unescape(data.section( ' ', 3, 3));
+ emit userJoined( handle, screenname, true );
+ }
+ else if( cmd == "USR" )
+ {
+ slotInviteContact(m_msgHandle);
+ }
+ else if( cmd == "BYE" )
+ {
+ // some has disconnect from chat, update user in chat list
+ cleanQueue(); //in case some message are waiting their emoticons, never mind, send them
+
+ QString handle = data.section( ' ', 0, 0 ).replace( "\r\n" , "" );
+ userLeftChat( handle, (data.section( ' ', 1, 1 ) == "1" ) ? i18n("timeout") : QString::null );
+ }
+ else if( cmd == "MSG" )
+ {
+ QString len = data.section( ' ', 2, 2 );
+
+ // we need to know who's sending is the block...
+ m_msgHandle = data.section( ' ', 0, 0 );
+
+ /*//This is WRONG! the displayName is never updated on the switchboeardsocket
+ //so we can't trust it.
+ //that's why the official client does not uptade alaws the nickname immediately.
+ if(m_account->contacts()[ m_msgHandle ])
+ {
+ QString displayName=data.section( ' ', 1, 1 );
+ if(m_account->contacts()[ m_msgHandle ]->displayName() != displayName)
+ m_account->contacts()[ m_msgHandle ]->rename(displayName);
+ }*/
+
+ readBlock(len.toUInt());
+ }
+}
+
+void MSNSwitchBoardSocket::slotReadMessage( const QByteArray &bytes )
+{
+ QString msg = QString::fromUtf8(bytes, bytes.size());
+
+ QRegExp rx("Content-Type: ([A-Za-z0-9/\\-]*)");
+ rx.search(msg);
+ QString type=rx.cap(1);
+
+ rx=QRegExp("User-Agent: ([A-Za-z0-9./\\-]*)");
+ rx.search(msg);
+ QString clientStr=rx.cap(1);
+
+ if( !clientStr.isNull() && !m_msgHandle.isNull())
+ {
+ Kopete::Contact *c=m_account->contacts()[m_msgHandle];
+ if(c)
+ c->setProperty( MSNProtocol::protocol()->propClient , clientStr );
+ }
+
+ // incoming message for File-transfer
+ if( type== "text/x-msmsgsinvite" )
+ {
+ emit invitation(m_msgHandle,msg);
+ }
+ else if( type== "text/x-msmsgscontrol" )
+ {
+ QString message;
+ message = msg.right( msg.length() - msg.findRev( " " ) - 1 );
+ message = message.replace( "\r\n" ,"" );
+ emit receivedTypingMsg( message.lower(), true );
+ }
+ else if(type == "text/x-msnmsgr-datacast")
+ {
+ if(msg.contains("ID:"))
+ {
+ QRegExp rx("ID: ([0-9]*)");
+ rx.search(msg);
+ uint dataCastId = rx.cap(1).toUInt();
+ if( dataCastId == 1 )
+ {
+ kdDebug(14140) << k_funcinfo << "Received a nudge !" << endl;
+ emit nudgeReceived(m_msgHandle);
+ }
+ }
+ }
+ else if(type=="text/plain" /* || type.isEmpty()*/ )
+ {
+ // Some MSN Clients (like CCMSN) don't like to stick to the rules.
+ // In case of CCMSN, it doesn't send what the content type is when
+ // sending a text message. So if it's not supplied, we'll just
+ // assume its that.
+
+ QColor fontColor;
+ QFont font;
+
+ if ( msg.contains( "X-MMS-IM-Format" ) )
+ {
+ QString fontName;
+ QString fontInfo;
+ QString color;
+
+ rx=QRegExp("X-MMS-IM-Format: ([^\r\n]*)");
+ rx.search(msg);
+ fontInfo =rx.cap(1);
+
+ color = parseFontAttr(fontInfo, "CO");
+
+ // FIXME: this is so BAAAAAAAAAAAAD :(
+ if (!color.isEmpty() && color.toInt(0,16)!=0)
+ {
+ if ( color.length() == 2) // only #RR (red color) given
+ fontColor.setRgb(
+ color.mid(0,2).toInt(0,16),
+ 0,
+ 0);
+ else if ( color.length() == 4) // #GGRR (green, red) given.
+ {
+ fontColor.setRgb(
+ color.mid(2,2).toInt(0,16),
+ color.mid(0,2).toInt(0,16),
+ 0);
+ }
+ else if ( color.length() == 6) // full #BBGGRR given
+ {
+ fontColor.setRgb(
+ color.mid(4,2).toInt(0, 16),
+ color.mid(2,2).toInt(0,16),
+ color.mid(0,2).toInt(0,16));
+ }
+ }
+
+ fontName = parseFontAttr(fontInfo, "FN").replace( "%20" , " " );
+
+ // Some clients like Trillian and Kopete itself send a font
+ // name of 'MS Serif' since MS changed the server to
+ // _require_ a font name specified in june 2002.
+ // MSN's own client defaults to 'MS Sans Serif', which also
+ // has issues.
+ // Handle 'MS Serif' and 'MS Sans Serif' as an empty font name
+ if( !fontName.isEmpty() && fontName != "MS Serif" && fontName != "MS Sans Serif" )
+ {
+ QString ef=parseFontAttr( fontInfo, "EF" );
+
+ font = QFont( fontName,
+ parseFontAttr( fontInfo, "PF" ).toInt(), // font size
+ ef.contains( 'B' ) ? QFont::Bold : QFont::Normal,
+ ef.contains( 'I' ) );
+ font.setUnderline(ef.contains( 'U' ));
+ font.setStrikeOut(ef.contains( 'S' ));
+ }
+ }
+
+ QPtrList<Kopete::Contact> others;
+ others.append( m_account->myself() );
+ QStringList::iterator it2;
+ for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 )
+ {
+ if( *it2 != m_msgHandle )
+ others.append( m_account->contacts()[ *it2 ] );
+ }
+
+ QString message=msg.right( msg.length() - msg.find("\r\n\r\n") - 4 );
+
+ //Stupid MSN PLUS colors code. message with incorrect charactere are not showed correctly in the chatwindow.
+ //TODO: parse theses one to show the color too in Kopete
+ message.replace("\3","").replace("\4","").replace("\2","").replace("\5","").replace("\6","").replace("\7","");
+
+ if(!m_account->contacts()[m_msgHandle])
+ {
+ //this may happens if the contact has been deleted.
+ kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl;
+ if( !m_chatMembers.contains( m_msgHandle ) )
+ m_chatMembers.append( m_msgHandle );
+ emit userJoined( m_msgHandle , m_msgHandle , false);
+ }
+
+ Kopete::Message kmsg( m_account->contacts()[ m_msgHandle ], others,
+ message,
+ Kopete::Message::Inbound , Kopete::Message::PlainText );
+
+ kmsg.setFg( fontColor );
+ kmsg.setFont( font );
+
+ rx=QRegExp("Chunks: ([0-9]*)");
+ rx.search(msg);
+ unsigned int chunks=rx.cap(1).toUInt();
+ rx=QRegExp("Chunk: ([0-9]*)");
+ rx.search(msg);
+ unsigned int chunk=rx.cap(1).toUInt();
+
+ if(chunk != 0 && !m_msgQueue.isEmpty())
+ {
+ QString msg=m_msgQueue.last().plainBody();
+ m_msgQueue.pop_back(); //removes the last item
+ kmsg.setBody( msg+ message, Kopete::Message::PlainText );
+ }
+
+ if(chunk == 0 )
+ m_chunks=chunks;
+ else if(chunk+1 >= m_chunks)
+ m_chunks=0;
+
+ if ( m_recvIcons > 0 || m_chunks > 0)
+ { //Some custom emoticons are waiting to be received. so append the message to the queue
+ //Or the message has not been fully received, so same thing
+ kdDebug(14140) << k_funcinfo << "Message not fully received => append to queue. Emoticon left: " << m_recvIcons << " chunks: " << chunk+1 << " of " << m_chunks <<endl;
+ m_msgQueue.append( kmsg );
+ if(!m_emoticonTimer) //to be sure no message will be lost, we will appends message to
+ { // the queue in 15 secondes even if we have not received emoticons
+ m_emoticonTimer=new QTimer(this);
+ QObject::connect(m_emoticonTimer , SIGNAL(timeout()) , this, SLOT(cleanQueue()));
+ m_emoticonTimer->start( 15000 , true );
+ }
+ }
+ else
+ emit msgReceived( parseCustomEmoticons( kmsg ) );
+
+ }
+ else if( type== "text/x-mms-emoticon" || type== "text/x-mms-animemoticon")
+ {
+ // TODO remove Displatcher.
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if ( config->readBoolEntry( "useCustomEmoticons", true ) )
+ {
+ QRegExp rx("([^\\s]*)[\\s]*(<msnobj [^>]*>)");
+ rx.setMinimal(true);
+ int pos = rx.search(msg);
+ while( pos != -1)
+ {
+ QString msnobj=rx.cap(2);
+ QString txt=rx.cap(1);
+ kdDebug(14140) << k_funcinfo << "emoticon: " << txt << " msnobj: " << msnobj<< endl;
+
+ if( !m_emoticons.contains(msnobj) || !m_emoticons[msnobj].second )
+ {
+ m_emoticons.insert(msnobj, qMakePair(txt,(KTempFile*)0L));
+ MSNContact *c=static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]);
+ if(!c)
+ return;
+
+ // we are receiving emoticons, so delay message display until received signal
+ m_recvIcons++;
+ PeerDispatcher()->requestDisplayIcon(m_msgHandle, msnobj);
+ }
+ pos=rx.search(msg, pos+rx.matchedLength());
+ }
+ }
+ }
+ else if( type== "application/x-msnmsgrp2p" )
+ {
+ PeerDispatcher()->slotReadMessage(m_msgHandle, bytes);
+ }
+ else if( type == "text/x-clientcaps" )
+ {
+ rx=QRegExp("Client-Name: ([A-Za-z0-9.$!*/% \\-]*)");
+ rx.search(msg);
+ clientStr=unescape( rx.cap(1) );
+
+ if( !clientStr.isNull() && !m_msgHandle.isNull())
+ {
+ Kopete::Contact *c=m_account->contacts()[m_msgHandle];
+ if(c)
+ c->setProperty( MSNProtocol::protocol()->propClient , clientStr );
+ }
+
+ if(!m_clientcapsSent)
+ {
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+
+ QString JabberID;
+ if(config->readBoolEntry("SendJabber", true))
+ JabberID=config->readEntry("JabberAccount");
+
+ if(!JabberID.isEmpty())
+ JabberID="JabberID: "+JabberID +"\r\n";
+
+ if( config->readBoolEntry("SendClientInfo", true) || !JabberID.isEmpty())
+ {
+
+ QCString message = QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-clientcaps\r\n"
+ "Client-Name: Kopete/"+escape(kapp->aboutData()->version())+"\r\n"
+ +JabberID+
+ "\r\n" ).utf8();
+
+ QString args = "U";
+ sendCommand( "MSG", args, true, message );
+ }
+ m_clientcapsSent=true;
+ }
+
+
+ }
+ else if(type == "image/gif" || msg.contains("Message-ID:"))
+ {
+ // Incoming inkformatgif.
+ QRegExp regex("Message-ID: \\{([0-9A-F\\-]*)\\}");
+ regex.search(msg);
+ QString messageId = regex.cap(1);
+ regex = QRegExp("Chunks: (\\d+)");
+ regex.search(msg);
+ QString chunks = regex.cap(1);
+ regex = QRegExp("Chunk: (\\d+)");
+ regex.search(msg);
+ QString chunk = regex.cap(1);
+
+ if(!messageId.isNull())
+ {
+ bool valid = true;
+ // Retrieve the nmber of data chunks.
+ Q_UINT32 numberOfChunks = chunks.toUInt(&valid);
+ if(valid && (numberOfChunks > 1))
+ {
+ regex = QRegExp("base64:([0-9a-zA-Z+/=]+)");
+ regex.search(msg);
+ // Retrieve the first chunk of the ink format gif.
+ QString base64 = regex.cap(1);
+ // More chunks are expected, buffer the chunk received.
+ InkMessage inkMessage;
+ inkMessage.chunks = numberOfChunks;
+ inkMessage.data += base64;
+ m_inkMessageBuffer.insert(messageId, inkMessage);
+ }
+ }
+ else
+ {
+ // There is only one chunk of data.
+ regex = QRegExp("base64:([0-9a-zA-Z+/=]*)");
+ regex.search(msg);
+ // Retrieve the base64 encoded ink data.
+ QString data = regex.cap(1);
+ DispatchInkMessage(data);
+ }
+
+ if(!messageId.isNull())
+ {
+ if(m_inkMessageBuffer.contains(messageId))
+ {
+ if(chunks.isNull())
+ {
+ InkMessage inkMessage = m_inkMessageBuffer[messageId];
+ inkMessage.data += msg.section("\r\n\r\n", -1);
+ if(inkMessage.chunks == chunk.toUInt() + 1)
+ {
+ DispatchInkMessage(inkMessage.data);
+ // Remove the ink message from the buffer.
+ m_inkMessageBuffer.remove(messageId);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ kdDebug(14140) << k_funcinfo <<" Unknown type '" << type << "' message: \n"<< msg <<endl;
+ }
+}
+
+void MSNSwitchBoardSocket::DispatchInkMessage(const QString& base64String)
+{
+ QByteArray image;
+ // Convert from base64 encoded string to byte array.
+ KCodecs::base64Decode(base64String.utf8() , image);
+ KTempFile *inkImage = new KTempFile(locateLocal( "tmp", "inkformatgif-" ), ".gif");
+ inkImage->setAutoDelete(true);
+ inkImage->file()->writeBlock(image.data(), image.size());
+ inkImage->file()->close();
+
+ slotEmoticonReceived(inkImage , "inkformatgif");
+ inkImage = 0l;
+}
+
+void MSNSwitchBoardSocket::sendTypingMsg( bool isTyping )
+{
+ if( !isTyping )
+ return;
+
+ if ( onlineStatus() != Connected || m_chatMembers.empty())
+ {
+ //we are not yet in a chat.
+ //if we send that command now, we may get disconnected.
+ return;
+ }
+
+
+ QCString message = QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msmsgscontrol\r\n"
+ "TypingUser: " + m_myHandle + "\r\n"
+ "\r\n" ).utf8();
+
+ // Length is appended by sendCommand()
+ QString args = "U";
+ sendCommand( "MSG", args, true, message );
+}
+
+// this Invites an Contact
+void MSNSwitchBoardSocket::slotInviteContact(const QString &handle)
+{
+ m_msgHandle=handle;
+ sendCommand( "CAL", handle );
+}
+//
+// Send a custum emoticon
+//
+int MSNSwitchBoardSocket::sendCustomEmoticon(const QString &name, const QString &filename)
+{
+ QString picObj;
+
+ //try to find it in the cache.
+ const QMap<QString, QString> objectList = PeerDispatcher()->objectList;
+ for (QMap<QString,QString>::ConstIterator it = objectList.begin(); it != objectList.end(); ++it )
+ {
+ if(it.data() == filename)
+ {
+ picObj=it.key();
+ break;
+ }
+ }
+
+ if(picObj.isNull())
+ { //if not found in the cache, generate the picture object
+ QFileInfo fi(filename);
+ // open the icon file
+ QFile pictFile(fi.filePath());
+ if (pictFile.open(IO_ReadOnly)) {
+
+ QByteArray ar = pictFile.readAll();
+ pictFile.close();
+
+ QString sha1d = QString(KCodecs::base64Encode(SHA1::hash(ar)));
+ QString size = QString::number( pictFile.size() );
+ QString all = "Creator" + m_account->accountId() + "Size" + size + "Type2Location" + fi.fileName() + "FriendlyAAA=SHA1D" + sha1d;
+ QString sha1c = QString(KCodecs::base64Encode(SHA1::hashString(all.utf8())));
+ picObj = "<msnobj Creator=\"" + m_account->accountId() + "\" Size=\"" + size + "\" Type=\"2\" Location=\""+ fi.fileName() + "\" Friendly=\"AAA=\" SHA1D=\""+sha1d+ "\" SHA1C=\""+sha1c+"\"/>";
+
+ PeerDispatcher()->objectList.insert(picObj, filename);
+ }
+ else
+ return 0;
+ }
+
+ QString msg = "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-mms-emoticon\r\n"
+ "\r\n" +
+ name + "\t" + picObj + "\t\r\n";
+
+ return sendCommand("MSG", "A", true, msg.utf8());
+
+}
+
+// this sends a short message to the server
+int MSNSwitchBoardSocket::sendMsg( const Kopete::Message &msg )
+{
+ if ( onlineStatus() != Connected || m_chatMembers.empty())
+ {
+// m_messagesQueue.append(msg);
+ return -1;
+ }
+
+#if 0 //this is to test webcam
+ if(msg.plainBody().contains("/webcam"))
+ {
+ PeerDispatcher()->startWebcam( m_myHandle , m_msgHandle);
+ return -3;
+ }
+#endif
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ if ( config->readBoolEntry( "exportEmoticons", false ) )
+ {
+ QMap<QString, QStringList> emap = Kopete::Emoticons::self()->emoticonAndPicList();
+
+ // Check the list for any custom emoticons
+ for (QMap<QString, QStringList>::const_iterator itr = emap.begin(); itr != emap.end(); itr++)
+ {
+ for ( QStringList::const_iterator itr2 = itr.data().constBegin(); itr2 != itr.data().constEnd(); ++itr2 )
+ {
+ if ( msg.plainBody().contains( *itr2 ) )
+ sendCustomEmoticon( *itr2, itr.key() );
+ }
+ }
+ }
+
+ if( msg.format() & Kopete::Message::RichText )
+ {
+ QRegExp regex("^\\s*<img src=\"([^>\"]+)\"[^>]*>\\s*$");
+ if(regex.search(msg.escapedBody()) != -1)
+ {
+ // FIXME why are we sending the images.. the contact should request them.
+ PeerDispatcher()->sendImage(regex.cap(1), m_msgHandle);
+ return -3;
+ }
+ }
+
+ // User-Agent is not a official flag, but GAIM has it
+ QString UA;
+ if( config->readBoolEntry("SendClientInfo", true) )
+ {
+ UA="User-Agent: Kopete/"+escape(kapp->aboutData()->version())+"\r\n";
+ }
+
+ QString head =
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: text/plain; charset=UTF-8\r\n"
+ +UA+
+ "X-MMS-IM-Format: ";
+
+ if(msg.font() != QFont() )
+ {
+ //It's verry strange that if the font name is bigger than 31 char, the _server_ close the socket and don't deliver the message.
+ // the real question is why ? my guess is that MS patched the server because a bug in their client, but that's just a guess.
+ // - Olivier 06-2005
+ head += "FN=" + escape( msg.font().family().left(31));
+ head += "; EF=";
+ if(msg.font().bold())
+ head += "B";
+ if(msg.font().italic())
+ head += "I";
+ if(msg.font().strikeOut())
+ head += "S";
+ if(msg.font().underline())
+ head += "U";
+ head += "; ";
+ }
+ else head+="FN=; EF=; ";
+ /*
+ * I don't know what to set by default, so i decided to set nothing. CF Bug 82734
+ * (but don't forgeto to add an empty FN= and EF= , or webmessenger will break. (CF Bug 102371) )
+ else head+="FN=MS%20Serif; EF=; ";
+ */
+
+ // Color support
+ if (msg.fg().isValid())
+ {
+ QString colorCode = QColor(msg.fg().blue(),msg.fg().green(),msg.fg().red()).name().remove(0,1); //colors aren't sent in RGB but in BGR (O.G.)
+ head += "CO=" + colorCode;
+ }
+ else
+ {
+ head += "CO=0";
+ }
+
+ head += "; CS=0; PF=0";
+ if (msg.plainBody().isRightToLeft())
+ head += "; RL=1";
+ head += "\r\n";
+
+ QString message= msg.plainBody().replace( "\n" , "\r\n" );
+
+ //-- Check if the message isn't too big, TODO: do that at the libkopete level.
+ int len_H=head.utf8().length(); // != head.length() because i need the size in butes and
+ int len_M=message.utf8().length(); // some utf8 char may be longer than one byte
+ if( len_H+len_M >= 1660 ) //1664 is the maximum size of messages allowed by the server
+ {
+ //We will certenly split the message in several ones.
+ //It's possible to made the opposite client join them, as explained in this MS Word document
+ //http://www.bot-depot.com/forums/index.php?act=Attach&type=post&id=35110
+
+ head+="Message-ID: {7B7B34E6-7A8D-44FF-926C-1799156B58"+QString::number( rand()%10)+QString::number( rand()%10)+"}\r\n";
+ int len_H=head.utf8().length()+ 14; //14 is the size of "Chunks: x"
+ //this is the size of each part of the message (excluding the header)
+ int futurmessages_size=1400; //1400 is a common good size
+ //int futurmessages_size=1664-len_H;
+
+ int nb=(int)ceil((float)(len_M)/(float)(futurmessages_size));
+
+ if(KMessageBox::warningContinueCancel(0L /* FIXME: we should try to find a parent somewere*/ ,
+ i18n("The message you are trying to send is too long; it will be split into %1 messages.").arg(nb) ,
+ i18n("Message too big - MSN Plugin" ), KStdGuiItem::cont() , "SendLongMessages" )
+ == KMessageBox::Continue )
+ {
+ int place=0;
+ int result;
+ int chunk=0;
+ do
+ {
+ QString m=message.mid(place, futurmessages_size);
+ place += futurmessages_size;
+
+ //make sure the size is not too big because of utf8
+ int d=m.utf8().length() + len_H -1664;
+ if( d > 0 )
+ {//it contains some utf8 chars, so we strip the string a bit.
+ m=m.left( futurmessages_size - d );
+ place -= d;
+ }
+
+ //try to snip on space if possible
+ int len=m.length();
+ d=0;
+ while(d<200 && !m[len-d].isSpace() )
+ d++;
+ if(d<200)
+ {
+ m=m.left(len-d);
+ place -= d;
+ }
+ QString chunk_str;
+ if(chunk==0)
+ chunk_str="Chunks: "+QString::number(nb)+"\r\n";
+ else if(chunk<nb)
+ chunk_str="Chunk: "+QString::number(chunk)+"\r\n";
+ else
+ {
+ kdDebug(14140) << k_funcinfo <<"The message is slit in more than initially estimated" <<endl;
+ }
+ result=sendCommand( "MSG", "A", true, (head+chunk_str+"\r\n"+m).utf8() );
+ chunk++;
+ }
+ while(place < len_M) ;
+
+ while(chunk<nb)
+ {
+ kdDebug(14140) << k_funcinfo <<"The message is plit in less than initially estimated. Sending empty message to complete" <<endl;
+ QString chunk_str="Chunk: "+QString::number(chunk);
+ sendCommand( "MSG", "A", true, (head+chunk_str+"\r\n").utf8() );
+ chunk++;
+ }
+ return result;
+ }
+ return -2; //the message hasn't been sent.
+ }
+
+ if(!m_keepAlive)
+ {
+ m_keepAliveNb=20;
+ m_keepAlive=new QTimer(this);
+ QObject::connect(m_keepAlive, SIGNAL(timeout()) , this , SLOT(slotKeepAliveTimer()));
+ m_keepAlive->start(50*1000);
+ }
+
+
+ return sendCommand( "MSG", "A", true, (head+"\r\n"+message).utf8() );
+}
+
+void MSNSwitchBoardSocket::slotSocketClosed( )
+{
+ for( QStringList::Iterator it = m_chatMembers.begin(); it != m_chatMembers.end(); ++it )
+ {
+ emit userLeft( (*it), i18n("connection closed"));
+ }
+
+ // we have lost the connection, send a message to chatwindow (this will not displayed)
+// emit switchBoardIsActive(false);
+ emit switchBoardClosed( );
+}
+
+void MSNSwitchBoardSocket::slotCloseSession()
+{
+ sendCommand( "OUT", QString::null, false );
+ disconnect();
+}
+
+// Check if we are connected. If so, then send the handshake.
+void MSNSwitchBoardSocket::slotOnlineStatusChanged( MSNSocket::OnlineStatus status )
+{
+ if (status == Connected)
+ {
+ QCString command;
+ QString args;
+
+ if( !m_ID ) // we're inviting
+ {
+ command = "USR";
+ args = m_myHandle + " " + m_auth;
+ }
+ else // we're invited
+ {
+ command = "ANS";
+ args = m_myHandle + " " + m_auth + " " + m_ID;
+ }
+ sendCommand( command, args );
+
+ if(!m_keepAlive)
+ {
+ m_keepAliveNb=20;
+ m_keepAlive=new QTimer(this);
+ QObject::connect(m_keepAlive, SIGNAL(timeout()) , this , SLOT(slotKeepAliveTimer()));
+ m_keepAlive->start(50*1000);
+ }
+ }
+}
+
+void MSNSwitchBoardSocket::userLeftChat(const QString& handle , const QString &reason)
+{
+ emit userLeft( handle, reason );
+
+ if( m_chatMembers.contains( handle ) )
+ m_chatMembers.remove( handle );
+
+ if(m_chatMembers.isEmpty())
+ disconnect();
+}
+
+void MSNSwitchBoardSocket::requestDisplayPicture()
+{
+ MSNContact *contact = static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]);
+ if(!contact) return;
+
+ PeerDispatcher()->requestDisplayIcon(m_msgHandle, contact->object());
+}
+
+void MSNSwitchBoardSocket::slotEmoticonReceived( KTempFile *file, const QString &msnObj )
+{
+ kdDebug(14141) << k_funcinfo << msnObj << endl;
+
+ if(m_emoticons.contains(msnObj))
+ { //it's an emoticon
+ m_emoticons[msnObj].second=file;
+
+ if( m_recvIcons > 0 )
+ m_recvIcons--;
+ kdDebug(14140) << k_funcinfo << "emoticons received queue is now: " << m_recvIcons << endl;
+
+ if ( m_recvIcons <= 0 )
+ cleanQueue();
+ }
+ else if(msnObj == "inkformatgif")
+ {
+ QString msg=i18n("<img src=\"%1\" alt=\"Typewrited message\" />" ).arg( file->name() );
+
+ kdDebug(14140) << k_funcinfo << file->name() <<endl;
+
+ m_typewrited.append(file);
+ m_typewrited.setAutoDelete(true);
+
+ QPtrList<Kopete::Contact> others;
+ others.append( m_account->myself() );
+
+ QStringList::iterator it2;
+ for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 )
+ {
+ if( *it2 != m_msgHandle )
+ others.append( m_account->contacts()[ *it2 ] );
+ }
+
+ if(!m_account->contacts()[m_msgHandle])
+ {
+ //this may happens if the contact has been deleted.
+ kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl;
+ if( !m_chatMembers.contains( m_msgHandle ) )
+ m_chatMembers.append( m_msgHandle );
+ emit userJoined( m_msgHandle , m_msgHandle , false);
+ }
+
+ Kopete::Message kmsg( m_account->contacts()[ m_msgHandle ], others,
+ msg, Kopete::Message::Inbound , Kopete::Message::RichText );
+
+ emit msgReceived( kmsg );
+ }
+ else //if it is not an emoticon,
+ { // it's certenly the displaypicture.
+ MSNContact *c=static_cast<MSNContact*>(m_account->contacts()[m_msgHandle]);
+ if(c && c->object()==msnObj)
+ c->setDisplayPicture(file);
+ else
+ delete file;
+ }
+}
+
+void MSNSwitchBoardSocket::slotIncomingFileTransfer(const QString& from, const QString& /*fileName*/, Q_INT64 /*fileSize*/)
+{
+ QPtrList<Kopete::Contact> others;
+ others.append( m_account->myself() );
+ QStringList::iterator it2;
+ for( it2 = m_chatMembers.begin(); it2 != m_chatMembers.end(); ++it2 )
+ {
+ if( *it2 != m_msgHandle )
+ others.append( m_account->contacts()[ *it2 ] );
+ }
+
+ if(!m_account->contacts()[m_msgHandle])
+ {
+ //this may happens if the contact has been deleted.
+ kdDebug(14140) << k_funcinfo <<"WARNING: contact is null, adding it" <<endl;
+ if( !m_chatMembers.contains( m_msgHandle ) )
+ m_chatMembers.append( m_msgHandle );
+ emit userJoined( m_msgHandle , m_msgHandle , false);
+ }
+ QString invite = "Incoming file transfer.";
+ Kopete::Message msg =
+ Kopete::Message(m_account->contacts()[from], others, invite, Kopete::Message::Internal, Kopete::Message::PlainText);
+ emit msgReceived(msg);
+}
+
+void MSNSwitchBoardSocket::cleanQueue()
+{
+ if(m_emoticonTimer)
+ {
+ m_emoticonTimer->stop();
+ m_emoticonTimer->deleteLater();
+ m_emoticonTimer=0L;
+ }
+ kdDebug(14141) << k_funcinfo << m_msgQueue.count() << endl;
+
+ QValueList<const Kopete::Message>::Iterator it_msg;
+ for ( it_msg = m_msgQueue.begin(); it_msg != m_msgQueue.end(); ++it_msg )
+ {
+ Kopete::Message kmsg = (*it_msg);
+ emit msgReceived( parseCustomEmoticons( kmsg ) );
+ }
+ m_msgQueue.clear();
+}
+
+Kopete::Message &MSNSwitchBoardSocket::parseCustomEmoticons(Kopete::Message &kmsg)
+{
+ QString message=kmsg.escapedBody();
+ QMap<QString , QPair<QString , KTempFile*> >::Iterator it;
+ for ( it = m_emoticons.begin(); it != m_emoticons.end(); ++it )
+ {
+ QString es=QStyleSheet::escape(it.data().first);
+ KTempFile *f=it.data().second;
+ if(message.contains(es) && f)
+ {
+ QString imgPath = f->name();
+ QImage iconImage(imgPath);
+ /* We don't use a comple algoritm (like the one in the #if) because the msn client shows
+ * emoticons like that. So, in that case, we show like the MSN client */
+ #if 0
+ QString em = QRegExp::escape( es );
+ message.replace( QRegExp(QString::fromLatin1( "(^|[\\W\\s]|%1)(%2)(?!\\w)" ).arg(em).arg(em)),
+ QString::fromLatin1("\\1<img align=\"center\" width=\"") +
+ #endif
+ //match any occurence which is not in a html tag.
+ message.replace( QRegExp(QString::fromLatin1("%1(?![^><]*>)").arg(QRegExp::escape(es))),
+ QString::fromLatin1("<img align=\"center\" width=\"") +
+ QString::number(iconImage.width()) +
+ QString::fromLatin1("\" height=\"") +
+ QString::number(iconImage.height()) +
+ QString::fromLatin1("\" src=\"") + imgPath +
+ QString::fromLatin1("\" title=\"") + es +
+ QString::fromLatin1("\" alt=\"") + es +
+ QString::fromLatin1( "\"/>" ) );
+ kmsg.setBody(message, Kopete::Message::RichText);
+ }
+ }
+ return kmsg;
+}
+
+int MSNSwitchBoardSocket::sendNudge()
+{
+ QCString message = QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-msnmsgr-datacast\r\n"
+ "\r\n"
+ "ID: 1\r\n"
+ "\r\n\r\n" ).utf8();
+
+ QString args = "U";
+ return sendCommand( "MSG", args, true, message );
+}
+
+
+
+// FIXME: This is nasty... replace with a regexp or so.
+QString MSNSwitchBoardSocket::parseFontAttr(QString str, QString attr)
+{
+ QString tmp;
+ int pos1=0, pos2=0;
+
+ pos1 = str.find(attr + "=");
+
+ if (pos1 == -1)
+ return "";
+
+ pos2 = str.find(";", pos1+3);
+
+ if (pos2 == -1)
+ tmp = str.mid(pos1+3, str.length() - pos1 - 3);
+ else
+ tmp = str.mid(pos1+3, pos2 - pos1 - 3);
+
+ return tmp;
+}
+
+Dispatcher* MSNSwitchBoardSocket::PeerDispatcher()
+{
+ if(!m_dispatcher)
+ {
+ // Create a new msnslp dispatcher to handle
+ // all peer to peer requests.
+ QStringList ip;
+ if(m_account->notifySocket())
+ {
+ ip << m_account->notifySocket()->localIP();
+ if(m_account->notifySocket()->localIP() != m_account->notifySocket()->getLocalIP())
+ ip << m_account->notifySocket()->getLocalIP();
+ }
+ m_dispatcher = new Dispatcher(this, m_account->accountId(),ip );
+
+// QObject::connect(this, SIGNAL(blockRead(const QByteArray&)), m_dispatcher, SLOT(slotReadMessage(const QByteArray&)));
+// QObject::connect(m_dispatcher, SIGNAL(sendCommand(const QString&, const QString&, bool, const QByteArray&, bool)), this, SLOT(sendCommand(const QString&, const QString&, bool, const QByteArray&, bool)));
+ QObject::connect(m_dispatcher, SIGNAL(incomingTransfer(const QString&, const QString&, Q_INT64)), this, SLOT(slotIncomingFileTransfer(const QString&, const QString&, Q_INT64)));
+ QObject::connect(m_dispatcher, SIGNAL(displayIconReceived(KTempFile *, const QString&)), this, SLOT(slotEmoticonReceived( KTempFile *, const QString&)));
+ QObject::connect(this, SIGNAL(msgAcknowledgement(unsigned int, bool)), m_dispatcher, SLOT(messageAcknowledged(unsigned int, bool)));
+ m_dispatcher->m_pictureUrl = m_account->pictureUrl();
+ }
+ return m_dispatcher;
+}
+
+void MSNSwitchBoardSocket::slotKeepAliveTimer( )
+{
+ /*
+ This is a workaround against the bug 113425
+ The problem: the P2P::Webcam class is parent of us, and when we get deleted, it get deleted.
+ the correct solution would be to change that.
+ The second problem: after one minute of inactivity, the official client close the chat socket.
+ the workaround: we simulate the activity by sending small packet each 50 seconds
+ the nice side effect: the "xxx has closed the chat" is now meaningfull
+ the bad side effect: some switchboard connection may be maintained for really long time!
+ */
+
+ if ( onlineStatus() != Connected || m_chatMembers.empty())
+ {
+ //we are not yet in a chat.
+ //if we send that command now, we may get disconnected.
+ return;
+ }
+
+
+ QCString message = QString( "MIME-Version: 1.0\r\n"
+ "Content-Type: text/x-keepalive\r\n"
+ "\r\n" ).utf8();
+
+ // Length is appended by sendCommand()
+ QString args = "U";
+ sendCommand( "MSG", args, true, message );
+
+ m_keepAliveNb--;
+ if(m_keepAliveNb <= 0)
+ {
+ m_keepAlive->deleteLater();
+ m_keepAlive=0L;
+ }
+}
+
+#include "msnswitchboardsocket.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/msnswitchboardsocket.h b/kopete/protocols/msn/msnswitchboardsocket.h
new file mode 100644
index 00000000..5a6f9628
--- /dev/null
+++ b/kopete/protocols/msn/msnswitchboardsocket.h
@@ -0,0 +1,166 @@
+/*
+ msnswitchboardsocket.h - switch board connection socket
+
+ Copyright (c) 2002 by Martijn Klingens <klingens@kde.org>
+ Copyright (c) 2002-2006 by Olivier Goffart <ogoffart@ kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ Portions of this code are taken from KMerlin,
+ (c) 2001 by Olaf Lueg <olueg@olsd.de>
+
+ *************************************************************************
+ * *
+ * 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 MSNSWITCHBOARDSOCKET_H
+#define MSNSWITCHBOARDSOCKET_H
+
+#include <qobject.h>
+#include <qstrlist.h>
+#include <qvaluevector.h>
+
+#include <kstringhandler.h>
+
+#include "msnsocket.h"
+
+namespace Kopete { class Message; }
+class MSNAccount;
+class QTimer;
+
+class MSNP2PDisplatcher;
+class KTempFile;
+
+namespace P2P { class Dispatcher; }
+
+#include "dispatcher.h"
+
+class KOPETE_EXPORT MSNSwitchBoardSocket : public MSNSocket
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Contructor: id is the KopeteMessageMangager's id
+ */
+ MSNSwitchBoardSocket( MSNAccount * account , QObject *parent);
+ ~MSNSwitchBoardSocket();
+
+private:
+ P2P::Dispatcher *m_dispatcher;
+ MSNAccount *m_account;
+
+ QString m_myHandle; // our handle
+
+ // contains the handle of the last person that msg'ed us.
+ // since we receive the actual message by readBlock(), we need
+ // to remember what the handle was of the person sending us the message.
+ QString m_msgHandle;
+
+ QString m_ID;
+ QString m_auth;
+ QStringList m_chatMembers;
+
+ //used for emoticons
+ QValueList<const Kopete::Message> m_msgQueue;
+ unsigned m_recvIcons;
+ QMap<QString , QPair<QString , KTempFile*> > m_emoticons;
+ Kopete::Message &parseCustomEmoticons(Kopete::Message &msg);
+ QTimer *m_emoticonTimer;
+ QPtrList<KTempFile> m_typewrited;
+
+ struct InkMessage{
+ Q_UINT32 chunks;
+ QString data;
+ };
+ QMap<QString, InkMessage> m_inkMessageBuffer;
+
+ /** the number of chunk for currents messages */
+ unsigned int m_chunks;
+
+ /** true is we already sent the x-clientcaps message */
+ bool m_clientcapsSent;
+
+private:
+ void DispatchInkMessage(const QString &base64String);
+
+protected:
+ /**
+ * Handle an MSN command response line.
+ */
+ virtual void parseCommand( const QString &cmd, uint id,
+ const QString &data );
+
+ /**
+ * Handle exceptions that might occur during a chat.
+ */
+ virtual void handleError( uint code, uint id );
+
+ QString parseFontAttr( QString str, QString attr );
+
+
+public:
+ void connectToSwitchBoard( QString ID, QString address, QString auth );
+ void setHandle( QString handle ) { m_myHandle = handle; }
+ void setMsgHandle( QString handle ) { m_msgHandle = handle; }
+
+ const QStringList &chatMembers() { return m_chatMembers; }
+
+ void userLeftChat( const QString &handle , const QString &reason );
+ int sendMsg( const Kopete::Message &msg );
+ int sendCustomEmoticon(const QString &name, const QString &filename);
+
+ int sendNudge();
+
+ P2P::Dispatcher* PeerDispatcher();
+
+public slots:
+ void slotCloseSession();
+ void slotInviteContact(const QString &handle);
+
+ /**
+ * Notify the server that the user is typing a message
+ */
+ void sendTypingMsg( bool isTyping );
+
+ void requestDisplayPicture();
+
+ /** workaround Bug 113425 . see slotKeepAliveTimer() **/
+ QTimer *m_keepAlive;
+ int m_keepAliveNb;
+
+
+
+private slots:
+ void slotOnlineStatusChanged( MSNSocket::OnlineStatus status );
+ void slotSocketClosed( );
+ void slotReadMessage( const QByteArray &bytes );
+ void slotEmoticonReceived( KTempFile *, const QString& );
+ void slotIncomingFileTransfer(const QString& from, const QString& fileName, Q_INT64 fileSize);
+ void cleanQueue();
+
+ /** workaround Bug 113425 . see comment inside the function **/
+ void slotKeepAliveTimer();
+
+signals:
+ void msgReceived( Kopete::Message &msg );
+ void receivedTypingMsg( const QString &contactId, bool isTyping );
+ void msgAcknowledgement(unsigned int, bool);
+ void userJoined(const QString& handle , const QString &publicName , bool IRO);
+ void userLeft(const QString& handle , const QString &reason);
+ void nudgeReceived(const QString &handle);
+
+ void switchBoardClosed( );
+ void invitation(const QString& handle, const QString& msg);
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/outgoingtransfer.cpp b/kopete/protocols/msn/outgoingtransfer.cpp
new file mode 100644
index 00000000..4879cf52
--- /dev/null
+++ b/kopete/protocols/msn/outgoingtransfer.cpp
@@ -0,0 +1,432 @@
+/*
+ outgoingtransfer.cpp - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 "outgoingtransfer.h"
+
+#include <stdlib.h>
+
+// Kde includes
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmdcodec.h>
+using namespace KNetwork;
+
+// Qt includes
+#include <qfile.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+// Kopete includes
+#include <kopetetransfermanager.h>
+
+#include <netinet/in.h> // For htonl
+using P2P::TransferContext;
+using P2P::Dispatcher;
+using P2P::OutgoingTransfer;
+using P2P::Message;
+
+OutgoingTransfer::OutgoingTransfer(const QString& to, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
+: TransferContext(to,dispatcher,sessionId)
+{
+ m_direction = Outgoing;
+ m_handshake = 0x01;
+}
+
+OutgoingTransfer::~OutgoingTransfer()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+void OutgoingTransfer::sendImage(const QByteArray& image)
+{
+
+// TODO QByteArray base64 = KCodecs::base64Encode(image);
+//
+// QCString body = "MIME-Version: 1.0\r\n"
+// "Content-Type: image/gif\r\n"
+// "\r\n"
+// "base64:" +
+//
+// Message outbound;
+// outbound.header.sessionId = m_sessionId;
+// outbound.header.identifier = m_baseIdentifier;
+// outbound.header.dataOffset = 0;
+// outbound.header.totalDataSize = 4;
+// outbound.header.dataSize = 4;
+// outbound.header.flag = 0;
+// outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4;
+// outbound.header.ackUniqueIdentifier = 0;
+// outbound.header.ackDataSize = 0l;
+// QByteArray bytes(4);
+// bytes.fill('\0');
+// outbound.body = bytes;
+// outbound.applicationIdentifier = 0;
+// outbound.attachApplicationId = false;
+// outbound.destination = m_recipient;
+//
+// sendMessage(outbound, body);
+}
+
+void OutgoingTransfer::slotSendData()
+{
+ Q_INT32 bytesRead = 0;
+ QByteArray buffer(1202);
+ if(!m_file)
+ return;
+
+ // Read a chunk from the source file.
+ bytesRead = m_file->readBlock(buffer.data(), buffer.size());
+
+ if (bytesRead < 0) {
+ m_file->close();
+ // ### error handling
+ }
+ else {
+
+ if(bytesRead < 1202){
+ buffer.resize(bytesRead);
+ }
+
+ kdDebug(14140) << k_funcinfo << QString("Sending, %1 bytes").arg(bytesRead) << endl;
+
+ if((m_offset + bytesRead) < m_file->size())
+ {
+ sendData(buffer);
+ m_offset += bytesRead;
+ }
+ else
+ {
+ m_isComplete = true;
+ // Send the last chunk of the file.
+ sendData(buffer);
+ m_offset += buffer.size();
+ // Close the file.
+ m_file->close();
+ }
+ }
+
+ if(m_transfer){
+ m_transfer->slotProcessed(m_offset);
+ if(m_isComplete){
+ // The transfer is complete.
+ m_transfer->slotComplete();
+ }
+ }
+}
+
+void OutgoingTransfer::acknowledged()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ switch(m_state)
+ {
+ case Invitation:
+ {
+ if(m_type == UserDisplayIcon)
+ {
+ m_state = Negotiation;
+ // Send data preparation message.
+ sendDataPreparation();
+ }
+ break;
+ }
+
+ case Negotiation:
+ {
+ if(m_type == UserDisplayIcon)
+ {
+ // <<< Data preparation acknowledge message.
+ m_state = DataTransfer;
+ m_identifier++;
+ // Start sending data.
+ slotSendData();
+ }
+ break;
+ }
+
+ case DataTransfer:
+ // NOTE <<< Data acknowledged message.
+ // <<< Bye message should follow.
+ if(m_type == File)
+ {
+ if(m_handshake == 0x01)
+ {
+ // Data handshake acknowledge message.
+ // Start sending data.
+ slotSendData();
+ }
+ else if(m_handshake == 0x02)
+ {
+ // Data acknowledge message.
+ // Send the recipient a BYE message.
+ m_state = Finished;
+ sendMessage(BYE, "\r\n");
+ }
+ }
+
+ break;
+
+ case Finished:
+ if(m_type == File)
+ {
+ // BYE acknowledge message.
+ m_dispatcher->detach(this);
+ }
+
+ break;
+ }
+}
+
+void OutgoingTransfer::processMessage(const Message& message)
+{
+ QString body =
+ QCString(message.body.data(), message.header.dataSize);
+ kdDebug(14140) << k_funcinfo << "received, " << body << endl;
+
+ if(body.startsWith("BYE"))
+ {
+ m_state = Finished;
+ // Send the recipient an acknowledge message.
+ acknowledge(message);
+ if(!m_isComplete)
+ {
+ // The peer cancelled the transfer.
+ if(m_transfer)
+ {
+ // Inform the user of the file transfer cancelation.
+ m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
+ }
+ }
+ // Dispose of this transfer context.
+ m_dispatcher->detach(this);
+ }
+ else if(body.startsWith("MSNSLP/1.0 200 OK"))
+ {
+ // Retrieve the message content type.
+ QRegExp regex("Content-Type: ([A-Za-z0-9$!*/\\-]*)");
+ regex.search(body);
+ QString contentType = regex.cap(1);
+
+ if(contentType == "application/x-msnmsgr-sessionreqbody")
+ {
+ // Recipient has accepted the file transfer.
+ // Acknowledge the recipient.
+ acknowledge(message);
+
+ // Try to open the file for reading.
+ // If an error occurs, send an internal
+ // error message to the recipient.
+ if(!m_file->open(IO_ReadOnly)){
+ error();
+ return;
+ }
+
+ // Retrieve the receiving client's contact.
+ Kopete::Contact *contact = m_dispatcher->getContactByAccountId(m_recipient);
+ if(contact == 0l)
+ {
+ error();
+ return;
+ }
+
+ m_transfer =
+ Kopete::TransferManager::transferManager()->addTransfer(contact, m_file->name(), m_file->size(), m_recipient, Kopete::FileTransferInfo::Outgoing);
+
+ QObject::connect(m_transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
+
+ m_state = Negotiation;
+
+ m_branch = P2P::Uid::createUid();
+
+ // Send the direct connection invitation message.
+ QString content = "Bridges: TRUDPv1 TCPv1\r\n" +
+ QString("NetID: %1\r\n").arg("-123657987") +
+ QString("Conn-Type: %1\r\n").arg("Restrict-NAT") +
+ "UPnPNat: false\r\n"
+ "ICF: false\r\n" +
+ QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
+ "\r\n";
+ sendMessage(INVITE, content);
+ }
+ else if(contentType == "application/x-msnmsgr-transrespbody")
+ {
+ // Determine whether the recipient created
+ // a listening endpoint.
+ regex = QRegExp("Listening: ([^\r\n]+)\r\n");
+ regex.search(body);
+ bool isListening = (regex.cap(1) == "true");
+
+ // Send the recipient an acknowledge message.
+ acknowledge(message);
+
+ m_state = DataTransfer;
+
+#if 1
+ isListening = false; // TODO complete direct connection.
+#endif
+ if(isListening)
+ {
+ // Retrieve the hashed nonce for this direct connection instance.
+ regex = QRegExp("Hashed-Nonce: \\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_nonce = regex.cap(1);
+ // Retrieve the listening endpoints of the receiving client.
+ regex = QRegExp("IPv4Internal-Addrs: ([^\r\n]+)\r\n");
+ regex.search(body);
+ m_peerEndpoints = QStringList::split(" ", regex.cap(1));
+ m_endpointIterator = m_peerEndpoints.begin();
+ // Retrieve the listening port of the receiving client.
+ regex = QRegExp("IPv4Internal-Port: ([^\r\n]+)\r\n");
+ regex.search(body);
+ m_remotePort = regex.cap(1);
+
+ // Try to connect to the receiving client's
+ // listening endpoint.
+ connectToEndpoint(*m_endpointIterator);
+ }
+ else
+ {
+ m_handshake = 0x02;
+ // Otherwise, send data through the already
+ // existing session.
+ slotSendData();
+ }
+ }
+ }
+ else if(body.startsWith("MSNSLP/1.0 603 Decline"))
+ {
+ // File transfer has been cancelled remotely.
+ // Send an acknowledge message
+ acknowledge(message);
+ if(m_transfer)
+ {
+ // Inform the user of the file transfer cancelation.
+ m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
+ }
+
+ if(m_file && m_file->isOpen()){
+ // Close the file.
+ m_file->close();
+ }
+ m_dispatcher->detach(this);
+ }
+}
+
+void OutgoingTransfer::readyToSend()
+{
+ if(m_isComplete){
+ // Ignore, do nothing.
+ return;
+ }
+
+ slotSendData();
+}
+
+void OutgoingTransfer::connectToEndpoint(const QString& hostName)
+{
+ m_socket = new KBufferedSocket(hostName, m_remotePort);
+ m_socket->setBlocking(false);
+ m_socket->enableRead(true);
+ // Disable write signal for now. Only enable
+ // when we are ready to sent data.
+ // NOTE readyWrite consumes too much cpu usage.
+ m_socket->enableWrite(false);
+ QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(slotRead()));
+ QObject::connect(m_socket, SIGNAL(connected(const KResolverEntry&)), this, SLOT(slotConnected()));
+ QObject::connect(m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+ QObject::connect(m_socket, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
+ // Try to connect to the endpoint.
+ m_socket->connect();
+}
+
+void OutgoingTransfer::slotConnected()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ // Check if connection is ok.
+ Q_UINT32 bytesWritten = m_socket->writeBlock(QCString("foo").data(), 4);
+ if(bytesWritten != 4)
+ {
+ // Not all data was written, close the socket.
+ m_socket->closeNow();
+ // Schedule the data to be sent through the existing session.
+ QTimer::singleShot(2000, this, SLOT(slotSendData()));
+ return;
+ }
+
+ // Send data handshake message.
+ P2P::Message handshake;
+ handshake.header.sessionId = 0;
+ handshake.header.identifier = ++m_identifier;
+ handshake.header.dataOffset = 0l;
+ handshake.header.totalDataSize = 0l;
+ handshake.header.dataSize = 0;
+ // Set the flag to indicate that this is
+ // a direct connection handshake message.
+ handshake.header.flag = 0x100;
+ QString nonce = m_nonce.remove('-');
+ handshake.header.ackSessionIdentifier = nonce.mid(0, 8).toUInt(0, 16);
+ handshake.header.ackUniqueIdentifier =
+ nonce.mid(8, 4).toUInt(0, 16) | (nonce.mid(12, 4).toUInt(0, 16) << 16);
+ const Q_UINT32 lo = nonce.mid(16, 8).toUInt(0, 16);
+ const Q_UINT32 hi = nonce.mid(24, 8).toUInt(0, 16);
+ handshake.header.ackDataSize =
+ ((Q_INT64)htonl(lo)) | (((Q_INT64)htonl(hi)) << 32);
+
+ QByteArray stream;
+ // Write the message to the memory stream.
+ m_messageFormatter.writeMessage(handshake, stream, true);
+ // Send the byte stream over the wire.
+ m_socket->writeBlock(stream.data(), stream.size());
+}
+
+void OutgoingTransfer::slotRead()
+{
+ Q_INT32 bytesAvailable = m_socket->bytesAvailable();
+ kdDebug(14140) << k_funcinfo << bytesAvailable << ", bytes available." << endl;
+}
+
+void OutgoingTransfer::slotSocketError(int)
+{
+ kdDebug(14140) << k_funcinfo << m_socket->errorString() << endl;
+ // If an error has occurred, try to connect
+ // to another available peer endpoint.
+ // If there are no more available endpoints,
+ // send the data through the session.
+ m_socket->closeNow();
+
+ // Move to the next available endpoint.
+ m_endpointIterator++;
+ if(m_endpointIterator != m_peerEndpoints.end()){
+ // Try to connect to the endpoint.
+ connectToEndpoint(*m_endpointIterator);
+ }
+ else
+ {
+ // Otherwise, send the data through the session.
+ m_identifier -= 1;
+ QTimer::singleShot(2000, this, SLOT(slotSendData()));
+ }
+}
+
+void OutgoingTransfer::slotSocketClosed()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ m_socket->deleteLater();
+ m_socket = 0l;
+}
+
+#include "outgoingtransfer.moc"
diff --git a/kopete/protocols/msn/outgoingtransfer.h b/kopete/protocols/msn/outgoingtransfer.h
new file mode 100644
index 00000000..014971ef
--- /dev/null
+++ b/kopete/protocols/msn/outgoingtransfer.h
@@ -0,0 +1,59 @@
+/*
+ outgoingtransfer.h - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 OUTGOINGTRANSFER_H
+#define OUTGOINGTRANSFER_H
+
+#include "p2p.h"
+#include "dispatcher.h"
+#include <qstringlist.h>
+
+/**
+@author Kopete Developers
+*/
+namespace P2P{
+ class OutgoingTransfer : public TransferContext
+ { Q_OBJECT
+ public:
+ OutgoingTransfer(const QString& to, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId);
+ virtual ~OutgoingTransfer();
+
+ void sendImage(const QByteArray& image);
+
+ private slots:
+ void slotConnected();
+ void slotRead();
+ void slotSendData();
+ void slotSocketError(int);
+ void slotSocketClosed();
+
+ private:
+ virtual void acknowledged();
+ void connectToEndpoint(const QString& hostName);
+ virtual void processMessage(const Message& message);
+
+ QStringList m_peerEndpoints;
+ QStringList::Iterator m_endpointIterator;
+ QString m_remotePort;
+ QString m_nonce;
+ char m_handshake;
+
+ protected:
+ virtual void readyToSend();
+ };
+}
+
+#endif
diff --git a/kopete/protocols/msn/p2p.cpp b/kopete/protocols/msn/p2p.cpp
new file mode 100644
index 00000000..219fd935
--- /dev/null
+++ b/kopete/protocols/msn/p2p.cpp
@@ -0,0 +1,412 @@
+/*
+ p2p.cpp - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 "p2p.h"
+#include "dispatcher.h"
+using P2P::TransferContext;
+using P2P::Message;
+using P2P::MessageType;
+using P2P::TransferType;
+
+#include <stdlib.h>
+
+// Kde includes
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+// Qt includes
+#include <qfile.h>
+
+// Kopete includes
+#include <kopetetransfermanager.h>
+
+QString P2P::Uid::createUid()
+{
+ return (QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16) + "-"
+ + QString::number(rand()%0xAAFF+0x1111, 16) + "-"
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)
+ + QString::number((unsigned long int)rand()%0xAAFF+0x1111, 16)).upper();
+}
+
+TransferContext::TransferContext(const QString &contact, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
+ : QObject(dispatcher) ,
+ m_sessionId(sessionId) ,
+ m_identifier(0) ,
+ m_file(0) ,
+ m_transactionId (0),
+ m_ackSessionIdentifier (0) ,
+ m_ackUniqueIdentifier ( 0 ),
+ m_transfer ( 0l) ,
+
+ m_baseIdentifier(rand()%0x0FFFFFF0 + 4),
+ m_dispatcher (dispatcher),
+ m_isComplete (false) ,
+ m_offset(0),
+ m_totalDataSize(0),
+ m_recipient(contact),
+ m_sender(dispatcher->localContact()),
+ m_socket(0),
+ m_state ( Invitation)
+{
+ m_type = File ; //uh, why??? -Olivier
+}
+
+TransferContext::~TransferContext()
+{
+ m_transfer = 0l;
+
+ if(m_file){
+ delete m_file;
+ m_file = 0l;
+ }
+}
+
+void TransferContext::acknowledge(const Message& message)
+{
+ kdDebug(14140) << k_funcinfo << m_dispatcher<< endl;
+
+ Message outbound;
+ outbound.header.sessionId = message.header.sessionId;
+
+ if(m_identifier == 0){
+ m_identifier = m_baseIdentifier;
+ }
+// else if(m_state == Finished && m_direction == Incoming){
+// m_identifier = m_baseIdentifier - 1;
+// }
+ else if(m_state == Finished && m_direction == Outgoing){
+ m_identifier = m_baseIdentifier + 1;
+ }
+ else
+ ++m_identifier;
+
+ outbound.header.identifier = m_identifier;
+ outbound.header.dataOffset = 0l;
+ outbound.header.totalDataSize = message.header.totalDataSize;
+ outbound.header.dataSize = 0;
+// if(m_type == UserDisplayIcon && m_state == Finished){
+// if(m_direction == Outgoing){
+// outbound.header.flag = 0x40;
+// }
+// }
+// else
+ outbound.header.flag = 2;
+
+ outbound.header.ackSessionIdentifier = message.header.identifier;
+ outbound.header.ackUniqueIdentifier = message.header.ackSessionIdentifier;
+ outbound.header.ackDataSize = message.header.totalDataSize;
+ // NOTE outbound.body is null by default
+ outbound.applicationIdentifier = 0l;
+ outbound.destination = m_recipient;
+
+ QByteArray stream;
+ // Write the acknowledge message to the stream.
+ m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
+ if(!m_socket)
+ {
+ // Send the acknowledge message.
+ m_dispatcher->callbackChannel()->send(stream);
+ }
+ else
+ {
+ // Send acknowledge message directly.
+ m_socket->writeBlock(stream.data(), stream.size());
+ }
+}
+
+void TransferContext::error()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ sendMessage(ERROR);
+ m_dispatcher->detach(this);
+}
+
+void TransferContext::sendData(const QByteArray& bytes)
+{
+ Message outbound;
+ outbound.header.sessionId = m_sessionId;
+ outbound.header.identifier = m_identifier;
+ outbound.header.dataOffset = m_offset;
+ if(m_file){
+ outbound.header.totalDataSize = m_file->size();
+ }
+ else
+ outbound.header.totalDataSize = m_totalDataSize;
+
+ outbound.header.dataSize = bytes.size();
+ if(m_type == UserDisplayIcon){
+ outbound.header.flag = 0x20;
+ }
+ else if(m_type == P2P::File){
+ outbound.header.flag = 0x01000030;
+ }
+ else outbound.header.flag = 0;
+
+ outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4;
+ outbound.header.ackUniqueIdentifier = 0;
+ outbound.header.ackDataSize = 0l;
+ outbound.body = bytes;
+ outbound.applicationIdentifier = (uint)m_type;
+
+ outbound.destination = m_recipient;
+
+ QByteArray stream;
+ m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
+ if(!m_socket)
+ {
+ // Send the data message.
+ m_transactionId = m_dispatcher->callbackChannel()->send(stream);
+ }
+ else
+ {
+ // Send data directly.
+ m_socket->writeBlock(stream.data(), stream.size());
+ }
+}
+
+void TransferContext::sendDataPreparation()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ Message outbound;
+ outbound.header.sessionId = m_sessionId;
+ outbound.header.identifier = ++m_identifier;
+ outbound.header.dataOffset = 0;
+ outbound.header.totalDataSize = 4;
+ outbound.header.dataSize = 4;
+ outbound.header.flag = 0;
+ outbound.header.ackSessionIdentifier = rand()%0x8FFFFFF0 + 4;
+ outbound.header.ackUniqueIdentifier = 0;
+ outbound.header.ackDataSize = 0l;
+ QByteArray bytes(4);
+ bytes.fill('\0');
+ outbound.body = bytes;
+ outbound.applicationIdentifier = 1;
+ outbound.destination = m_recipient;
+
+ QByteArray stream;
+ m_messageFormatter.writeMessage(outbound, stream);
+ // Send the receiving client the data prepartion message.
+ m_dispatcher->callbackChannel()->send(stream);
+}
+
+void TransferContext::sendMessage(MessageType type, const QString& content, Q_INT32 flag, Q_INT32 appId)
+{
+ Message outbound;
+ if(appId != 0){
+ outbound.header.sessionId = m_sessionId;
+ }
+ else
+ outbound.header.sessionId = 0;
+
+ if(m_identifier == 0){
+ m_identifier = m_baseIdentifier;
+ }
+ else if(m_state == Invitation && m_direction == P2P::Outgoing && m_type == UserDisplayIcon)
+ {
+ m_identifier -= 3;
+ }
+ else if(m_state == Invitation && m_direction == P2P::Incoming && m_type == File)
+ {
+ m_identifier -= 3;
+ }
+ else
+ ++m_identifier;
+
+ outbound.header.identifier = m_identifier;
+ outbound.header.flag = flag;
+ outbound.header.ackSessionIdentifier = m_ackSessionIdentifier;
+ outbound.header.ackUniqueIdentifier = m_ackUniqueIdentifier;
+ outbound.header.ackDataSize = 0l;
+ outbound.applicationIdentifier = appId;
+ outbound.destination = m_recipient;
+
+ QString contentType, cSeq, method;
+
+ switch(m_state)
+ {
+ case DataTransfer:
+ contentType = "application/x-msnmsgr-transreqbody";
+ if(m_type == File && m_direction == Incoming)
+ {
+ contentType = "application/x-msnmsgr-transrespbody";
+ }
+ break;
+ case Finished:
+ contentType = "application/x-msnmsgr-sessionclosebody";
+ break;
+ default:
+ contentType = "application/x-msnmsgr-sessionreqbody";
+ if(m_type == File && m_direction == Outgoing)
+ {
+ if(m_state == Negotiation){
+ contentType = "application/x-msnmsgr-transreqbody";
+ }
+ }
+ if(m_type == P2P::WebcamType && type==P2P::INVITE && m_state == Negotiation)
+ {
+ contentType = "application/x-msnmsgr-transreqbody";
+ }
+ break;
+ }
+
+ switch(type)
+ {
+ case BYE:
+ method = "BYE MSNMSGR:" + m_recipient + " MSNSLP/1.0";
+ cSeq = "0";
+ break;
+
+ case DECLINE:
+ method = "MSNSLP/1.0 603 DECLINE";
+ cSeq = "1";
+ break;
+
+ case ERROR:
+ contentType = "null";
+ method = "MSNSLP/1.0 500 Internal Error";
+ cSeq = "1";
+ break;
+
+ case INVITE:
+ method = "INVITE MSNMSGR:" + m_recipient + " MSNSLP/1.0";
+ cSeq = "0";
+ break;
+
+ case OK:
+ method = "MSNSLP/1.0 200 OK";
+ cSeq = "1";
+ break;
+ }
+
+ QCString body = QString(method + "\r\n"
+ "To: <msnmsgr:" + m_recipient + ">\r\n"
+ "From: <msnmsgr:" + m_sender + ">\r\n"
+ "Via: MSNSLP/1.0/TLP ;branch={" + m_branch.upper() + "}\r\n"
+ "CSeq: "+ cSeq +"\r\n"
+ "Call-ID: {" + m_callId.upper() + "}\r\n"
+ "Max-Forwards: 0\r\n"
+ "Content-Type: " + contentType + "\r\n"
+ "Content-Length: "+ QString::number(content.length() + 1) + "\r\n"
+ "\r\n" +
+ content).utf8();
+
+ // NOTE The body must have a null character at the end.
+ // QCString by chance automatically adds a \0 to the
+ // end of the string.
+
+ outbound.header.totalDataSize = body.size();
+ // Send the outbound message.
+ sendMessage(outbound, body);
+}
+
+void TransferContext::sendMessage(Message& outbound, const QByteArray& body)
+{
+ Q_INT64 offset = 0L, bytesLeft = outbound.header.totalDataSize;
+ Q_INT16 chunkLength = 1202;
+
+ // Split the outbound message if necessary.
+ while(bytesLeft > 0L)
+ {
+ if(bytesLeft < chunkLength)
+ {
+ // Copy the last chunk of the multipart message.
+ outbound.body.duplicate(body.data() + offset, bytesLeft);
+ outbound.header.dataSize = bytesLeft;
+ outbound.header.dataOffset = offset;
+ bytesLeft = 0L;
+ }
+ else
+ {
+ // Copy the next chunk of the multipart message in the sequence.
+ outbound.body.duplicate(body.data() + offset, chunkLength);
+ outbound.header.dataSize = chunkLength;
+ outbound.header.dataOffset = offset;
+ offset += chunkLength;
+ bytesLeft -= offset;
+ }
+
+ kdDebug(14140) << k_funcinfo <<
+ QCString(outbound.body.data(), outbound.body.size())
+ << endl;
+
+ QByteArray stream;
+ // Write the outbound message to the stream.
+ m_messageFormatter.writeMessage(outbound, stream, (m_socket != 0l));
+ if(!m_socket)
+ {
+ // Send the outbound message.
+ m_dispatcher->callbackChannel()->send(stream);
+ }
+ else
+ {
+ // Send outbound message directly.
+ m_socket->writeBlock(stream.data(), stream.size());
+ }
+ }
+}
+
+void TransferContext::setType(TransferType type)
+{
+ m_type = type;
+}
+
+void TransferContext::abort()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ if(m_transfer)
+ {
+ if(m_transfer->error() == KIO::ERR_ABORTED)
+ {
+ switch(m_direction)
+ {
+ case P2P::Outgoing:
+ if(m_type == File)
+ {
+ // Do nothing.
+ }
+ break;
+
+ case P2P::Incoming:
+ if(m_type == File)
+ {
+ // Do nothing.
+ }
+ break;
+ }
+ }
+ else
+ {
+ m_state = Finished;
+ sendMessage(BYE, "\r\n");
+ }
+ }
+}
+
+void TransferContext::readyWrite()
+{
+ if(m_direction == Outgoing && m_state == DataTransfer){
+ readyToSend();
+ }
+}
+
+void TransferContext::readyToSend()
+{}
+
+#include "p2p.moc"
diff --git a/kopete/protocols/msn/p2p.h b/kopete/protocols/msn/p2p.h
new file mode 100644
index 00000000..c9b29af1
--- /dev/null
+++ b/kopete/protocols/msn/p2p.h
@@ -0,0 +1,147 @@
+/*
+ p2p.h - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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 P2P_H
+#define P2P_H
+
+// Qt includes
+#include <qobject.h>
+#include "messageformatter.h"
+
+#include "kopete_export.h"
+
+#include "config.h"
+
+namespace Kopete { class Transfer; }
+namespace Kopete { struct FileTransferInfo; }
+namespace P2P { class Dispatcher; }
+namespace KNetwork { class KBufferedSocket; }
+class QFile;
+class KTempFile;
+
+/**
+@author Kopete Developers
+*/
+namespace System{
+ class Guid
+ {
+ public:
+ ~Guid(){}
+ static Guid newGuid();
+ QString toString();
+
+ private:
+ Guid(){}
+ };
+}
+
+namespace P2P{
+
+ enum TransferType { UserDisplayIcon = 1, File = 2, WebcamType=4};
+ enum TransferDirection { Incoming = 1, Outgoing = 8};
+ enum MessageType { BYE, OK, DECLINE, ERROR, INVITE };
+
+ enum CommunicationState
+ {
+ Invitation = 1,
+ Negotiation = 2,
+ DataTransfer = 8,
+ Finished = 16
+ };
+
+ struct TransportHeader
+ {
+ Q_UINT32 sessionId;
+ Q_UINT32 identifier;
+ Q_INT64 dataOffset;
+ Q_INT64 totalDataSize;
+ Q_UINT32 dataSize;
+ Q_UINT32 flag;
+ Q_UINT32 ackSessionIdentifier;
+ Q_UINT32 ackUniqueIdentifier;
+ Q_INT64 ackDataSize;
+ };
+
+ struct Message
+ {
+ public:
+ QString mimeVersion;
+ QString contentType;
+ QString destination;
+ QString source;
+ TransportHeader header;
+ QByteArray body;
+ Q_INT32 applicationIdentifier;
+ bool attachApplicationIdentifier;
+ };
+
+ class KOPETE_EXPORT Uid
+ {
+ public: static QString createUid();
+ };
+
+ class KOPETE_EXPORT TransferContext : public QObject
+ { Q_OBJECT
+ public:
+ virtual ~TransferContext();
+
+ void acknowledge(const Message& message);
+ virtual void acknowledged() = 0;
+ void error();
+ virtual void processMessage(const P2P::Message& message) = 0;
+ void sendDataPreparation();
+ void sendMessage(MessageType type, const QString& content=QString::null, Q_INT32 flag=0, Q_INT32 appId=0);
+ void setType(TransferType type);
+
+ public:
+ Q_UINT32 m_sessionId;
+ Q_UINT32 m_identifier;
+ QFile *m_file;
+ Q_UINT32 m_transactionId;
+ Q_UINT32 m_ackSessionIdentifier;
+ Q_UINT32 m_ackUniqueIdentifier;
+ Kopete::Transfer *m_transfer;
+ QString m_branch;
+ QString m_callId;
+ QString m_object;
+
+
+ public slots:
+ void abort();
+ void readyWrite();
+
+ protected:
+ TransferContext(const QString& contact, P2P::Dispatcher *dispatcher,Q_UINT32 sessionId);
+ void sendData(const QByteArray& bytes);
+ void sendMessage(P2P::Message& outbound, const QByteArray& body);
+ virtual void readyToSend();
+
+ Q_UINT32 m_baseIdentifier;
+ TransferDirection m_direction;
+ P2P::Dispatcher *m_dispatcher;
+ bool m_isComplete;
+ Q_INT64 m_offset;
+ Q_INT64 m_totalDataSize;
+ P2P::MessageFormatter m_messageFormatter;
+ QString m_recipient;
+ QString m_sender;
+ KNetwork::KBufferedSocket *m_socket;
+ CommunicationState m_state;
+ TransferType m_type;
+ };
+}
+
+#endif
diff --git a/kopete/protocols/msn/sha1.cpp b/kopete/protocols/msn/sha1.cpp
new file mode 100644
index 00000000..84ad13ad
--- /dev/null
+++ b/kopete/protocols/msn/sha1.cpp
@@ -0,0 +1,192 @@
+/*
+ * sha1.cpp - Secure Hash Algorithm 1
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include"sha1.h"
+
+/****************************************************************************
+ SHA1 - from a public domain implementation by Steve Reid (steve@edmweb.com)
+****************************************************************************/
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15]^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+SHA1::SHA1()
+{
+ int wordSize;
+
+ qSysInfo(&wordSize, &bigEndian);
+}
+
+unsigned long SHA1::blk0(Q_UINT32 i)
+{
+ if(bigEndian)
+ return block->l[i];
+ else
+ return (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) | (rol(block->l[i],8)&0x00FF00FF));
+}
+
+// Hash a single 512-bit block. This is the core of the algorithm.
+void SHA1::transform(Q_UINT32 state[5], unsigned char buffer[64])
+{
+ Q_UINT32 a, b, c, d, e;
+
+ block = (CHAR64LONG16*)buffer;
+
+ // Copy context->state[] to working vars
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ // 4 rounds of 20 operations each. Loop unrolled.
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ // Add the working vars back into context.state[]
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+
+ // Wipe variables
+ a = b = c = d = e = 0;
+}
+
+// SHA1Init - Initialize new context
+void SHA1::init(SHA1_CONTEXT* context)
+{
+ // SHA1 initialization constants
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+// Run your data through this
+void SHA1::update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len)
+{
+ Q_UINT32 i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+
+ context->count[1] += (len >> 29);
+
+ if((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+// Add padding and return the message digest
+void SHA1::final(unsigned char digest[20], SHA1_CONTEXT* context)
+{
+ Q_UINT32 i, j;
+ unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); // Endian independent
+ }
+ update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ update(context, (unsigned char *)"\0", 1);
+ }
+ update(context, finalcount, 8); // Should cause a transform()
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+
+ // Wipe variables
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+}
+
+QByteArray SHA1::hash(const QByteArray &a)
+{
+ SHA1_CONTEXT context;
+ QByteArray b(20);
+
+ SHA1 s;
+ s.init(&context);
+ s.update(&context, (unsigned char *)a.data(), (unsigned int)a.size());
+ s.final((unsigned char *)b.data(), &context);
+ return b;
+}
+
+QByteArray SHA1::hashString(const QCString &cs)
+{
+ QByteArray a(cs.length());
+ memcpy(a.data(), cs.data(), a.size());
+ return SHA1::hash(a);
+}
+
+QString SHA1::digest(const QString &in)
+{
+ QByteArray a = SHA1::hashString(in.utf8());
+ QString out;
+ for(int n = 0; n < (int)a.size(); ++n) {
+ QString str;
+ str.sprintf("%02x", (uchar)a[n]);
+ out.append(str);
+ }
+
+ return out;
+}
diff --git a/kopete/protocols/msn/sha1.h b/kopete/protocols/msn/sha1.h
new file mode 100644
index 00000000..24f31af0
--- /dev/null
+++ b/kopete/protocols/msn/sha1.h
@@ -0,0 +1,59 @@
+/*
+ * sha1.h - Secure Hash Algorithm 1
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef CS_SHA1_H
+#define CS_SHA1_H
+
+#include<qstring.h>
+
+class SHA1
+{
+public:
+ static QByteArray hash(const QByteArray &);
+ static QByteArray hashString(const QCString &);
+ static QString digest(const QString &);
+
+private:
+ SHA1();
+
+ struct SHA1_CONTEXT
+ {
+ Q_UINT32 state[5];
+ Q_UINT32 count[2];
+ unsigned char buffer[64];
+ };
+
+ typedef union {
+ unsigned char c[64];
+ Q_UINT32 l[16];
+ } CHAR64LONG16;
+
+ void transform(Q_UINT32 state[5], unsigned char buffer[64]);
+ void init(SHA1_CONTEXT* context);
+ void update(SHA1_CONTEXT* context, unsigned char* data, Q_UINT32 len);
+ void final(unsigned char digest[20], SHA1_CONTEXT* context);
+
+ unsigned long blk0(Q_UINT32 i);
+ bool bigEndian;
+
+ CHAR64LONG16* block;
+};
+
+#endif
diff --git a/kopete/protocols/msn/transport.cpp b/kopete/protocols/msn/transport.cpp
new file mode 100644
index 00000000..492117b6
--- /dev/null
+++ b/kopete/protocols/msn/transport.cpp
@@ -0,0 +1,356 @@
+/*
+ transport.cpp - Peer to peer transport
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#include "transport.h"
+#include "messageformatter.h"
+
+//BEGIN QT Includes
+//END
+
+//BEGIN KDE Includes
+#include <kclientsocketbase.h>
+#include <kdebug.h>
+#include <kstreamsocket.h>
+//END
+
+//BEGIN Using Directives
+using namespace KNetwork;
+//END
+
+#include "msnswitchboardsocket.h"
+
+namespace PeerToPeer {
+
+Transport::Transport(QObject* parent, const char* name)
+ : QObject(parent, name)
+{
+ mFormatter = new PeerToPeer::MessageFormatter(this);
+}
+
+
+Transport::~Transport()
+{
+}
+
+//BEGIN Public Methods
+
+TransportBridge* Transport::getBridge (const QString& to, Q_UINT16 port, TransportBridgeType type, const QString& identifier)
+{
+ TransportBridge *bridge = 0l;
+ KInetSocketAddress address;
+ if (mAddresses.contains(to))
+ {
+ address = mAddresses[to];
+ }
+ else
+ {
+ address = KInetSocketAddress(KIpAddress(to), port);
+ mAddresses[to] = address;
+ }
+
+ if (PeerToPeer::Tcp == type){
+ bridge = new TcpTransportBridge(address, mFormatter, this, identifier.ascii());
+ }
+
+ if (PeerToPeer::Udp == type){
+// TODO Add class UdpTransportBridge
+// bridge = new UdpTransportBridge(address, this, mFormatter, identifier.ascii());
+ }
+
+ if (bridge != 0l)
+ {
+ QObject::connect(bridge, SIGNAL(readyRead(const QByteArray&)), SLOT(slotOnReceive(const QByteArray&)));
+ }
+
+ return 0l;
+}
+
+void Transport::setDefaultBridge(MSNSwitchBoardSocket* mss)
+{
+ mDefaultBridge = mss;
+ QObject::connect((MSNSwitchBoardSocket*)mDefaultBridge, SIGNAL(messageReceived(const QString&, const QByteArray&)), SLOT(slotOnReceive(const QString&, const QByteArray&)));
+}
+
+//END
+
+//BEGIN Private Slot Methods
+
+// void Transport::slotOnReceive(Message& message)
+// {
+// }
+
+void Transport::slotOnReceive(const QString& contact, const QByteArray& bytes)
+{
+ kdDebug (14140) << k_funcinfo << " >> RECEIVED " << bytes.size() << " bytes." << endl;
+// Message message = mFormatter->readMessage(bytes);
+}
+
+//END
+
+
+
+
+TransportBridge::TransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, QObject* parent, const char* name)
+: QObject(parent, name)
+{
+ mAddress = to;
+ mFormatter = formatter;
+}
+
+TransportBridge::TransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, QObject* parent, const char* name)
+: QObject(parent, name)
+{
+ mSocket = socket;
+ mAddress = mSocket->peerAddress();
+}
+
+TransportBridge::~TransportBridge()
+{
+}
+
+//BEGIN Public Methods
+
+void TransportBridge::connect()
+{
+ slotOnConnect();
+}
+
+void TransportBridge::disconnect()
+{
+ slotOnDisconnect();
+}
+
+//END
+
+//BEGIN Protected Slot Methods
+
+void TransportBridge::slotOnConnect()
+{
+}
+
+void TransportBridge::slotOnDisconnect()
+{
+}
+
+void TransportBridge::slotOnError(int)
+{
+}
+
+void TransportBridge::slotOnSocketClose()
+{
+}
+
+void TransportBridge::slotOnSocketConnect()
+{
+}
+
+void TransportBridge::slotOnSocketReceive()
+{
+}
+
+
+//END
+
+
+
+TcpTransportBridge::TcpTransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, QObject* parent, const char* name)
+: TransportBridge(to, formatter, parent, name)
+{
+ mSocket = new KStreamSocket(mAddress.ipAddress().toString(), QString::number(mAddress.port()), this);
+ mSocket->setBlocking(false);
+ QObject::connect(mSocket, SIGNAL(connected(const KResolverEntry&)), SLOT(slotOnSocketConnect()));
+ QObject::connect(mSocket, SIGNAL(gotError(int)), SLOT(slotOnError(int)));
+ mConnected = false;
+}
+
+TcpTransportBridge::TcpTransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, QObject* parent, const char* name)
+: TransportBridge(socket, formatter, parent, name)
+{
+ mConnected = (mSocket->state() == KStreamSocket::Open) ? true : false;
+ mSocket->setBlocking(false);
+}
+
+TcpTransportBridge::~TcpTransportBridge()
+{
+}
+
+//BEGIN Protected Slot Methods
+
+void TcpTransportBridge::slotOnConnect()
+{
+ if (mConnected)
+ {
+ kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") ALREADY CONNECTED " << mSocket->peerAddress().toString() << " <-> " << mSocket->localAddress().toString() << endl;
+ return;
+ }
+
+ KStreamSocket *socket = static_cast<KStreamSocket*>(mSocket);
+ socket->setTimeout(5000);
+ QObject::connect(socket, SIGNAL(timeOut()), SLOT(slotOnSocketConnectTimeout()));
+ mSocket->connect();
+}
+
+void TcpTransportBridge::slotOnDisconnect()
+{
+ if (mConnected){
+ mSocket->close();
+ }
+}
+
+void TcpTransportBridge::slotOnError(int errorCode)
+{
+ kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") ERROR occurred on {" << mSocket->localAddress().toString() << " <-> " << mSocket->peerAddress().toString() << "} - " << mSocket->errorString() << endl;
+ emit bridgeError(QString("Bridge ERROR %1: %2").arg(errorCode).arg(mSocket->errorString()));
+ if (mConnected){
+ mSocket->disconnect();
+ mConnected = false;
+ }
+ mSocket->deleteLater();
+ mSocket = 0l;
+}
+
+void TcpTransportBridge::slotOnSocketClose()
+{
+ mSocket->disconnect();
+ kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") DISCONNECTED {" << mSocket->peerAddress().toString() << " <-> " << mSocket->localAddress().toString() << "}" << endl;
+ mConnected = false;
+ mSocket->deleteLater();
+ mSocket = 0l;
+
+ emit bridgeDisconnect();
+}
+
+void TcpTransportBridge::slotOnSocketConnect()
+{
+ kdDebug(14140) << k_funcinfo << "Bridge (" << name() << ") CONNECTED to " << mSocket->peerAddress().toString() << " from "
+ << mSocket->localAddress().toString() << endl;
+ mConnected = true;
+
+ QObject::connect(mSocket, SIGNAL(readyRead()), SLOT(slotOnSocketReceive()));
+ QObject::connect(mSocket, SIGNAL(closed()), SLOT(slotOnSocketClose()));
+
+ mVerified = true;
+ QString foo = "foo\0";
+ mSocket->writeBlock(foo.ascii(), foo.length());
+ foo = QString::null;
+
+ emit bridgeConnect();
+}
+
+void TcpTransportBridge::slotOnSocketReceive()
+{
+ kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") RECEIVED " << mSocket->bytesAvailable() << " bytes." << endl;
+
+ QByteArray bytes(mSocket->bytesAvailable());
+ mSocket->readBlock(bytes.data(), bytes.size());
+ // Write the data to the buffer.
+ mBuffer.write(bytes);
+
+ if (mVerified == false && mBuffer.size() >= 4)
+ {
+ QByteArray foo = mBuffer.read(4);
+ if (QString(foo) == "foo"){
+ kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") CONNECTION verified." << endl;
+ mVerified = true;
+ }
+ }
+
+ while(mBuffer.size() > 0)
+ {
+ if (mBuffer.size() >= 4 && mLength == 0)
+ {
+ QByteArray array = mBuffer.read(4);
+ for (int i=0; i < 4; i++){
+ ((char*)mLength)[i] = array[i];
+ }
+ }
+
+ if (mLength > 0 && mBuffer.size() >= mLength)
+ {
+ kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") read " << mLength << " bytes." << endl;
+ bytes = mBuffer.read(mLength);
+ mLength = 0;
+// Message message = mFormatter->readMessage(bytes, true);
+// emit messageReceived(message);
+ }
+ else
+ {
+ kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") waiting for " << mLength << " bytes." << endl;
+ break;
+ }
+ }
+}
+
+//END
+
+//BEGIN Private Slot Methods
+
+void TcpTransportBridge::slotOnSocketConnectTimeout()
+{
+ kdDebug (14140) << k_funcinfo << "Bridge (" << name() << ") CONNECT timeout." << endl;
+ emit bridgeConnectTimeout();
+ mSocket->deleteLater();
+ mSocket = 0l;
+}
+
+//END
+
+
+
+
+TcpTransportBridge::Buffer::Buffer(Q_UINT32 length)
+: QByteArray(length)
+{
+}
+
+TcpTransportBridge::Buffer::~Buffer()
+{
+}
+
+//BEGIN Public Methods
+
+void TcpTransportBridge::Buffer::write(const QByteArray& bytes)
+{
+ resize(size() + bytes.size());
+ for (uint i=0; i < bytes.size(); i++){
+ (*this)[size() + i] = bytes[i];
+ }
+}
+
+QByteArray TcpTransportBridge::Buffer::read(Q_UINT32 length)
+{
+ if (length >= size()) return QByteArray();
+
+ QByteArray buffer;
+ buffer.duplicate(data(), length);
+
+ char *bytes = new char[size() - length];
+ for(uint i=0; i < size() - length; i++){
+ bytes[i] = data()[length + i];
+ }
+
+ duplicate(bytes, size() - length);
+ delete[] bytes;
+
+ return buffer;
+}
+
+//END
+
+}
+
+#include "transport.moc"
+
diff --git a/kopete/protocols/msn/transport.h b/kopete/protocols/msn/transport.h
new file mode 100644
index 00000000..0dae0f32
--- /dev/null
+++ b/kopete/protocols/msn/transport.h
@@ -0,0 +1,167 @@
+/*
+ transport.h - Peer to peer transport
+
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; version 2 of the License. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef PEERTOPEERTRANSPORT_H
+#define PEERTOPEERTRANSPORT_H
+
+//BEGIN QT Includes
+#include <qobject.h>
+#include <qguardedptr.h>
+#include <qvaluelist.h>
+//END
+
+//BEGIN KDE Includes
+#include <ksocketaddress.h>
+//END
+
+namespace KNetwork {
+class KClientSocketBase;
+}
+
+class MSNSwitchBoardSocket;
+
+namespace PeerToPeer {
+
+class MessageFormatter;
+class TransportBridge;
+
+enum TransportBridgeType
+{
+ Tcp = 1,
+ Udp = 2
+};
+
+/**
+ @author Gregg Edghill <gregg.edghill@gmail.com> */
+/** @brief Represents the protocol used to send and receive message between peers. */
+class Transport : public QObject
+{
+ Q_OBJECT
+public:
+ /** @brief Creates a new instance of the class Transport. */
+ Transport(QObject* parent, const char* name = 0l);
+ ~Transport();
+ /** @brief Get a transport bridge with the specified address, port, type and identifier. */
+ TransportBridge* getBridge(const QString& address, Q_UINT16 port, TransportBridgeType type, const QString& identifier);
+ /** @brief Sets the default transport bridge. */
+ void setDefaultBridge(MSNSwitchBoardSocket* mss);
+
+private slots:
+ /** @brief Invokes when a message is received on a transport bridge. */
+// void slotOnReceive(Message& message);
+ /** @brief Invokes when a message is received on the default transport bridge (relay). */
+ void slotOnReceive(const QString& contact, const QByteArray& bytes);
+
+private:
+ /** @brief Known SocketAddresses of peers. */
+ QMap<QString, KNetwork::KInetSocketAddress> mAddresses;
+ /** @brief The list the connected transport bridges. */
+ QValueList<TransportBridge*> mBridges;
+ /** @brief The default transport bridge (relay). */
+ QGuardedPtr<MSNSwitchBoardSocket> mDefaultBridge;
+ /** @brief Message formatter used to ser/deser message. */
+ MessageFormatter *mFormatter;
+};
+
+/** @brief Represents the channel connecting two peers. */
+class TransportBridge : public QObject
+{
+ Q_OBJECT
+public:
+ virtual ~TransportBridge();
+
+protected:
+ /** @brief Creates a new instance of the class TransportBridge with the specified address and formatter. */
+ TransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, QObject* parent, const char* name = 0l);
+ /** @brief Creates a new instance of the class TransportBridge with the specified socket and formatter. */
+ TransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, QObject* parent, const char* name = 0l);
+
+public:
+ /** @brief Creates a connection between two peers. */
+ void connect();
+ /** @brief Disconnects the connection between two peers. */
+ void disconnect();
+
+protected slots:
+ virtual void slotOnConnect();
+ virtual void slotOnDisconnect();
+ virtual void slotOnError(int);
+ virtual void slotOnSocketClose();
+ virtual void slotOnSocketConnect();
+ virtual void slotOnSocketReceive();
+
+signals:
+ void bridgeConnect();
+ void bridgeDisconnect();
+ void bridgeError(const QString& e);
+ void bytesReceived(const QByteArray&);
+
+protected:
+
+ KNetwork::KInetSocketAddress mAddress;
+ bool mConnected;
+ MessageFormatter *mFormatter;
+ Q_UINT32 mLength;
+ KNetwork::KClientSocketBase *mSocket;
+ bool mVerified;
+};
+
+class TcpTransportBridge : public TransportBridge
+{
+ Q_OBJECT
+ friend class Transport;
+
+public:
+ virtual ~TcpTransportBridge();
+
+private:
+ TcpTransportBridge(const KNetwork::KInetSocketAddress& to, MessageFormatter* formatter, QObject* parent, const char* name = 0l);
+ TcpTransportBridge(KNetwork::KClientSocketBase* socket, MessageFormatter* formatter, QObject* parent, const char* name = 0l);
+
+protected slots:
+ virtual void slotOnConnect();
+ virtual void slotOnDisconnect();
+ virtual void slotOnError(int);
+ virtual void slotOnSocketClose();
+ virtual void slotOnSocketConnect();
+ virtual void slotOnSocketReceive();
+
+private slots:
+ void slotOnSocketConnectTimeout();
+
+signals:
+ void bridgeConnectTimeout();
+
+private:
+ class Buffer : public QByteArray
+ {
+ public:
+ Buffer(Q_UINT32 length = 0);
+ ~Buffer();
+
+ public:
+ void write(const QByteArray& bytes);
+ QByteArray read(Q_UINT32 length);
+ };
+
+ Buffer mBuffer;
+ Q_UINT32 mLength;
+};
+
+
+}
+
+#endif
diff --git a/kopete/protocols/msn/ui/Makefile.am b/kopete/protocols/msn/ui/Makefile.am
new file mode 100644
index 00000000..08a7cbac
--- /dev/null
+++ b/kopete/protocols/msn/ui/Makefile.am
@@ -0,0 +1,12 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopetemsnui.la
+
+libkopetemsnui_la_SOURCES = msnadd.ui msndebugrawcommand_base.ui msninfo.ui \
+ msneditaccountui.ui msneditaccountwidget.cpp
+
+EXTRA_DIST = msnadd.ui msninfo.ui
+
diff --git a/kopete/protocols/msn/ui/msnadd.ui b/kopete/protocols/msn/ui/msnadd.ui
new file mode 100644
index 00000000..ff99bb92
--- /dev/null
+++ b/kopete/protocols/msn/ui/msnadd.ui
@@ -0,0 +1,97 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>msnAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>msnAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>397</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout21</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;MSN Passport ID:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the MSN contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the MSN contact you would like to add. This should be in the form of a valid E-mail address.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>addID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the MSN contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the MSN contact you would like to add. This should be in the form of a valid E-mail address.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: joe@hotmail.com)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>160</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/msn/ui/msndebugrawcommand_base.ui b/kopete/protocols/msn/ui/msndebugrawcommand_base.ui
new file mode 100644
index 00000000..3b98ce0d
--- /dev/null
+++ b/kopete/protocols/msn/ui/msndebugrawcommand_base.ui
@@ -0,0 +1,107 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>MSNDebugRawCommand_base</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MSNDebugRawCommand_base</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>201</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Parameters:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_params</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>m_command</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Co&amp;mmand:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_command</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_params</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_addId</cstring>
+ </property>
+ <property name="text">
+ <string>Add &amp;ID</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_addNewline</cstring>
+ </property>
+ <property name="text">
+ <string>Add &amp;new line</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="KTextEdit" row="5" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_msg</cstring>
+ </property>
+ <property name="textFormat">
+ <enum>PlainText</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Message:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>m_command</tabstop>
+ <tabstop>m_params</tabstop>
+ <tabstop>m_addId</tabstop>
+ <tabstop>m_addNewline</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>ktextedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/msn/ui/msneditaccountui.ui b/kopete/protocols/msn/ui/msneditaccountui.ui
new file mode 100644
index 00000000..5a3b8294
--- /dev/null
+++ b/kopete/protocols/msn/ui/msneditaccountui.ui
@@ -0,0 +1,1421 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>MSNEditAccountUI</class>
+<author>Olivier Goffart</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>604</width>
+ <height>437</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - MSN</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>tabWidget3</cstring>
+ </property>
+ <property name="tabShape">
+ <enum>Rounded</enum>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab_connection</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic Setup</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer41</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>146</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the Microsoft network, you will need a Microsoft Passport.&lt;br&gt;&lt;br&gt;If you do not currently have a Passport, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonRegister</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;gister New Account</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>m_accountInfo</cstring>
+ </property>
+ <property name="title">
+ <string>Account Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;MSN Passport ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_login</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the MSN contact you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the MSN contact you would like to use. This should be in the form of a valid E-mail address.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_login</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of the MSN contact you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of the MSN contact you would like to use. This should be in the form of a valid E-mail address.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget" row="1" column="0">
+ <property name="name">
+ <cstring>m_password</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>m_autologin</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you check this checkbox, the account will not be connected when you press the "Connect All" button, or at startup when automatic connection at startup is enabled.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>m_globalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Exclu&amp;de from Global Identity</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>MSN &amp;Settings</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <italic>1</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>&lt;qt&gt;&lt;b&gt;Note:&lt;/b&gt; These settings are applicable to all MSN accounts</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>global_settings_page</cstring>
+ </property>
+ <property name="title">
+ <string>Global MSN Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>NotifyNewChat</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Au&amp;tomatically open a chat window when someone starts a conversation</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This option will notify you when a contact starts typing their message, before the message is sent or finished.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_4</cstring>
+ </property>
+ <property name="text">
+ <string>Download the msn picture:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;&lt;p&gt;Indicate when Kopete will download the display pictures of contacts&lt;/p&gt;
+&lt;dl&gt;&lt;dt&gt;Only manually&lt;/dt&gt;&lt;dd&gt;The picture is not downloaded automatically. It is only downloaded when the user requests it&lt;/dd&gt;
+&lt;dt&gt;When a chat is open&lt;/dt&gt;&lt;dd&gt;The picture is downloaded when a conversation socket is opened, i.e. when you open a chat window&lt;/dd&gt;
+&lt;dt&gt;Automatically&lt;/dt&gt;&lt;dd&gt;Always try to download the picture if the contact has one. &lt;b&gt;Note:&lt;/b&gt; this will open a socket, and let the user know you are downloading their picture.&lt;/dd&gt;&lt;/dl&gt;</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Only Manually</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>When a Chat is Open</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Automatically</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>DownloadPicture</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="currentItem">
+ <number>2</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;&lt;p&gt;Indicate when Kopete will download the pictures of contacts&lt;/p&gt;
+&lt;dl&gt;&lt;dt&gt;Only manually&lt;/dt&gt;&lt;dd&gt;The picture is not downloaded automatically. It is only downloaded when the user requests it&lt;/dd&gt;
+&lt;dt&gt;When a chat is open&lt;/dt&gt;&lt;dd&gt;The picture is downloaded when a conversation socket is opened, i.e. when you open a chat window&lt;/dd&gt;
+&lt;dt&gt;Automatically&lt;/dt&gt;&lt;dd&gt;Always try to download the picture if the contact has one. &lt;b&gt;Note:&lt;/b&gt; this will open a socket, and let the user know you are downloading their picture.&lt;/dd&gt;&lt;/dl&gt;</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>useCustomEmoticons</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Download and show custom emoticons</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>MSN Messenger allows users to download and use custom emoticons. If this option is enabled, Kopete will download these emoticons and show them.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>exportEmoticons</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xport the current emoticon theme to users</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only work with emoticons in the PNG format</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Export all the emoticon themes as custom emoticons.
+Only works for emoticons in the PNG format.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>privacy_page</cstring>
+ </property>
+ <property name="title">
+ <string>Privacy</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>SendClientInfo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Send client information</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>&lt;qt&gt;Make it possible for your contacts to detect if you are using Kopete.&lt;br&gt;We recommend leaving this checked.&lt;/qt&gt;</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Third party MSN clients, such as Kopete, give users the ability to let other third party clients guess which client they are using. We recommend leaving this checkbox checked.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>SendTypingNotification</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Send &amp;typing notifications</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>&lt;qt&gt;Check this box to send &lt;b&gt;Typing notifications&lt;/b&gt; to your contacts. When you are composing a message, you might want your contact to know that you are typing so that he knows you are answering.&lt;/qt&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout28</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>SendJabber</cstring>
+ </property>
+ <property name="text">
+ <string>Expose my Jabber account to Jabber users</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you have a Jabber account, you may let Jabber users on an MSN gateway know that you are also using Jabber.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>JabberAccount</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you have a Jabber account, you may let Jabber users on an MSN gateway know that you are also using Jabber.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer26</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer25</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_5</cstring>
+ </property>
+ <property name="text">
+ <string>There are also privacy options in the "Contacts" tab</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer20</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab_info</cstring>
+ </property>
+ <attribute name="title">
+ <string>User &amp;Info</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout22_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Nickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_displayName</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The alias you would like to use on MSN. You may change this at any time you wish.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_displayName</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The alias you would like to use on MSN. You may change this at any time you wish.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>m_phones</cstring>
+ </property>
+ <property name="title">
+ <string>Phone Numbers</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Hom&amp;e:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_phh</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Work:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_phw</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>m_phw</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_phh</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Mobile:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_phm</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_phm</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Display Picture</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_useDisplayPicture</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xport a display picture</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Please select a square image. The image will be scaled to 96x96.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_selectImage</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Select Image...</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>61</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7_2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_displayPicture</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer7_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer16</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="QLabel">
+ <property name="name">
+ <cstring>m_warning_1</cstring>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>WARNING: You need to be connected to modify this page.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab_contacts</cstring>
+ </property>
+ <attribute name="title">
+ <string>Con&amp;tacts</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>label_font</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;Italics&lt;/i&gt; contacts are not on your contact list.&lt;br&gt;
+&lt;br&gt;
+&lt;b&gt;Bold&lt;/b&gt; contacts are in your contact list but you are not in their contact list.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Bloc&amp;ked contacts:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_BL</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0">
+ <property name="name">
+ <cstring>m_AL</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_blockButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;&gt;</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_allowButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;&lt;</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Allo&amp;wed contacts:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_AL</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="2">
+ <property name="name">
+ <cstring>m_BL</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout58</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer47</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_blp</cstring>
+ </property>
+ <property name="text">
+ <string>Block all users not in 'Allowed' &amp;list</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Checking this box will block all users not explicitly shown in the allowed list here, including any contacts not on your contact list.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer50</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>41</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout59</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer48</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>81</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>m_RLButton</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>200</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>View &amp;Reverse List</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The reverse list is the list of contacts who added you to their own contact list.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The reverse list is the list of contacts who added you to their own contact list.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer49</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_warning_2</cstring>
+ </property>
+ <property name="paletteForegroundColor">
+ <color>
+ <red>255</red>
+ <green>0</green>
+ <blue>0</blue>
+ </color>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>WARNING: You need to be connected to modify this page</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Co&amp;nnection</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox66</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences (for advanced users)</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionOverrideServer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Override default server information</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout20</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver /</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_serverName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>labelPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_serverPort</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_serverName</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>messenger.hotmail.com</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_serverPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>1863</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Only modify these values if you want to use a special IM proxy server, like SIMP</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionUseHttpMethod</cstring>
+ </property>
+ <property name="text">
+ <string>Use &amp;HTTP method</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Connect to MSN Messenger using an HTTP-like protocol on port 80.
+This may be used to connect on a network with a restrictive firewall.
+Only check this option if the normal connection doesn't work.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout22</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_useWebcamPort</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;pecify a base port for incoming webcam connections:</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you are behind a firewall, you may specify a base port to use for the incoming connection, and configure your firewall to accept connections on a range of 10 ports, starting at this one. Incoming connections are used for the webcam. If you don't specify a port yourself, the operating system will choose an available port for you. It is recommended to leave the checkbox unchecked.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_webcamPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>6891</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you are behind a firewall, you may specify a base port to use for the incoming connection, and configure your firewall to accept connections on a range of 10 ports, starting at this one. Incoming connections are used for the webcam. If you don't specify a port yourself, the operating system will choose an available port for you. It is recommended to leave the checkbox unchecked.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </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>21</width>
+ <height>70</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="868">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000032b49444154388db59531681b6714c77f32373c8186ef0305eea0050932f8201d944d493bc4d0a1a21e4427bb533a74299dbc25905288a7d0b9836932d58116eac1411932388ba421a5a7a17005174e83e00e2cb80f6ab83708d2e18bec8ada26d0f4c1c1ddbbf7fdeeff3efeefbbda70346419b76fdd7ecd3b88e16858ab2dc183c3c1ebee7a97a99b521515d969f65610e71cd971c6f8d7312ccef3c152e9b39f9e11351d36164acdb819d4a9b4c4362ce5a2c48a45162588253ff5cfe5a2c406862405d9138e5eea2a18609a4fb12d212d7ea42c334089ac92e6423113cab902826d4227568a002480a942780dead16a2767e0ca55949a81668023b2c2e8952139748c270e58aa115aebc2675b86b80b6143710aa1b9049ccd336e064a5979e8e039ec7f5f78544368af1b24807ca64cff50befba6a0b765d8be2b67f00bc1562c95e6441646afe40d54b9f36948af2fb4df078722440c0e2af6f70a064f0be2568beea6c5885b01af2d6f4a2db10dc8ff128e0edc19f4f32f8576dbe1707022fcf2b4647babce175f8780f0c31307a7e0162bdc55c5e52247e742fabbc31843af2f9886c32d40d4b0fb4849278ef20476ee59c62f7ced3831848d55f0aa62816ca6de11ad37ed2fa10f1ce9c4619ac2c647824a45dc1100f2a9e2542e067b9f82155f108adf539c61f781924efc0745c0be57273240b08409e62ac508d0f085c94c112c83e778a54608434331733cbc9f331a5bf2636f85a855bfda15f9694e27565ad785e99fcae0a062fb6e4479a2f43e16eacd3a0fef433175ec7e95a1aa98a6d0e95454f355f2bff65803e8f5bddbf7f70a0687393bf72ced2e74ba253bdfb631a1c139872e948d7e487c83ab15979a2301dcba033a373c7e52f0f851c1f885d0ed080ec88f7374ae672b7f3b72249b115143389fce7f4e5e91d11398cefd986e6c099816839fbd1bd2c9b91ad3147afd16a32387534580ac58957c0e3ece485230d77c5ba6a1f4fa42ef9398719253153e1f5f8f687f9013df80f16684c1e0161969b20aae0d47437fc007d0f950882210c19fad81bf24f04e399701a04820380769a2e485e28a0b14b380e4a5927059e85be67cac5dfae63fc61af87fd4ff027ed7f0e16858fb1ba5cd86c64770b2e90000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>m_useDisplayPicture</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_selectImage</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_useDisplayPicture</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_selectImage</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_useDisplayPicture</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelServer</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_serverName</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_serverPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_useDisplayPicture</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_displayPicture</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>m_useWebcamPort</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_webcamPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget3</tabstop>
+ <tabstop>optionOverrideServer</tabstop>
+ <tabstop>m_serverName</tabstop>
+ <tabstop>m_serverPort</tabstop>
+ <tabstop>optionUseHttpMethod</tabstop>
+ <tabstop>m_login</tabstop>
+ <tabstop>m_autologin</tabstop>
+ <tabstop>buttonRegister</tabstop>
+ <tabstop>m_displayName</tabstop>
+ <tabstop>m_phw</tabstop>
+ <tabstop>m_phh</tabstop>
+ <tabstop>m_phm</tabstop>
+ <tabstop>m_useDisplayPicture</tabstop>
+ <tabstop>m_selectImage</tabstop>
+ <tabstop>m_AL</tabstop>
+ <tabstop>m_blockButton</tabstop>
+ <tabstop>m_allowButton</tabstop>
+ <tabstop>m_BL</tabstop>
+ <tabstop>m_blp</tabstop>
+ <tabstop>m_RLButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kopetepasswordwidget.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/msn/ui/msneditaccountwidget.cpp b/kopete/protocols/msn/ui/msneditaccountwidget.cpp
new file mode 100644
index 00000000..1829f41d
--- /dev/null
+++ b/kopete/protocols/msn/ui/msneditaccountwidget.cpp
@@ -0,0 +1,369 @@
+/*
+ msneditaccountwidget.cpp - MSN Account Widget
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "msneditaccountwidget.h"
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <qspinbox.h>
+#include <kcombobox.h>
+
+#include <kautoconfig.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <kpassdlg.h>
+#include <krun.h>
+#include <kconfig.h>
+#include <kpixmapregionselectordialog.h>
+
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+
+#include "kopetepasswordwidget.h"
+#include "kopeteaccountmanager.h"
+
+#include "msnaccount.h"
+#include "msncontact.h"
+#include "msneditaccountui.h"
+#include "msnnotifysocket.h"
+#include "msnprotocol.h"
+
+class MSNEditAccountWidgetPrivate
+{
+public:
+ MSNProtocol *protocol;
+ KAutoConfig *autoConfig;
+ MSNEditAccountUI *ui;
+
+ QString pictureUrl;
+ QImage pictureData;
+};
+
+MSNEditAccountWidget::MSNEditAccountWidget( MSNProtocol *proto, Kopete::Account *account, QWidget *parent, const char * /* name */ )
+: QWidget( parent ), KopeteEditAccountWidget( account )
+{
+ d = new MSNEditAccountWidgetPrivate;
+
+ d->protocol=proto;
+
+ ( new QVBoxLayout( this, 0, 0 ) )->setAutoAdd( true );
+
+ d->ui = new MSNEditAccountUI( this );
+
+ d->autoConfig = new KAutoConfig( d->ui );
+ d->autoConfig->addWidget( d->ui->global_settings_page, "MSN" );
+ d->autoConfig->addWidget( d->ui->privacy_page, "MSN" );
+ //the JabberAccount need to be saved as text, and can't be handled by kautoconfig
+ d->autoConfig->ignoreSubWidget( d->ui->JabberAccount );
+ d->autoConfig->retrieveSettings( true );
+
+ //Get a list of all jabber accounts
+ KGlobal::config()->setGroup("MSN");
+ QString jab_account=KGlobal::config()->readEntry("JabberAccount");
+
+ QPtrList<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts();
+ for(Kopete::Account *a=accounts.first() ; a; a=accounts.next() )
+ {
+ if(a->protocol()->pluginId()=="JabberProtocol")
+ {
+ d->ui->JabberAccount->insertItem(a->accountId());
+ if( jab_account.isEmpty() )
+ jab_account=a->accountId();
+ }
+ }
+ d->ui->JabberAccount->setCurrentText(jab_account);
+
+ // FIXME: actually, I don't know how to set fonts for qlistboxitem - Olivier
+ d->ui->label_font->hide();
+
+ // default fields
+ if ( account )
+ {
+ KConfigGroup * config=account->configGroup();
+
+ d->ui->m_login->setText( account->accountId() );
+ d->ui->m_password->load( &static_cast<MSNAccount *>(account)->password() );
+
+ //remove me after we can change account ids (Matt)
+ d->ui->m_login->setDisabled( true );
+ d->ui->m_autologin->setChecked( account->excludeConnect() );
+ if ( ( static_cast<MSNAccount*>(account)->serverName() != "messenger.hotmail.com" ) || ( static_cast<MSNAccount*>(account)->serverPort() != 1863) ) {
+ d->ui->optionOverrideServer->setChecked( true );
+ }
+
+ d->ui->m_webcamPort->setDisabled(true);
+ uint port=config->readNumEntry("WebcamPort" ,0);
+ d->ui->m_useWebcamPort->setChecked( port != 0);
+ d->ui->m_webcamPort->setValue( port != 0 ? port : 6891 );
+
+ d->ui->optionUseHttpMethod->setChecked( static_cast<MSNAccount*>(account)->useHttpMethod() );
+
+ MSNContact *myself = static_cast<MSNContact *>( account->myself() );
+
+ d->ui->m_displayName->setText( myself->property( Kopete::Global::Properties::self()->nickName()).value().toString() );
+ d->ui->m_phw->setText( config->readEntry("PHW") );
+ d->ui->m_phm->setText( config->readEntry("PHM") );
+ d->ui->m_phh->setText( config->readEntry("PHH") );
+
+ bool connected = account->isConnected();
+ if ( connected )
+ {
+ d->ui->m_warning_1->hide();
+ d->ui->m_warning_2->hide();
+ }
+ d->ui->m_phones->setEnabled( connected );
+ d->ui->m_displayName->setEnabled( connected );
+ d->ui->m_allowButton->setEnabled( connected );
+ d->ui->m_blockButton->setEnabled( connected );
+
+ MSNAccount *m_account = static_cast<MSNAccount*>( account );
+ d->ui->m_serverName->setText( m_account->serverName() );
+ d->ui->m_serverPort->setValue( m_account->serverPort() );
+
+ QStringList blockList = config->readListEntry( "blockList" );
+ QStringList allowList = config->readListEntry( "allowList" );
+ //QStringList reverseList = config->readListEntry("reverseList" );
+
+ for ( QStringList::Iterator it = blockList.begin(); it != blockList.end(); ++it )
+ d->ui->m_BL->insertItem( *it );
+
+ for ( QStringList::Iterator it = allowList.begin(); it != allowList.end(); ++it )
+ d->ui->m_AL->insertItem( *it );
+
+ d->ui->m_blp->setChecked( config->readEntry( "BLP" ) == "BL" );
+
+ d->pictureUrl = locateLocal( "appdata", "msnpicture-" +
+ account->accountId().lower().replace( QRegExp("[./~]" ), "-" ) + ".png" );
+ d->ui->m_displayPicture->setPixmap( d->pictureUrl );
+
+ d->ui->m_useDisplayPicture->setChecked( config->readBoolEntry( "exportCustomPicture" ));
+
+ // Global Identity
+ d->ui->m_globalIdentity->setChecked( config->readBoolEntry("ExcludeGlobalIdentity", false) );
+ }
+ else
+ {
+ d->ui->tab_contacts->setDisabled( true );
+ d->ui->m_displayName->setDisabled( true );
+ d->ui->m_phones->setDisabled( true );
+ }
+
+ connect( d->ui->m_allowButton, SIGNAL( clicked() ), this, SLOT( slotAllow() ) );
+ connect( d->ui->m_blockButton, SIGNAL( clicked() ), this, SLOT( slotBlock() ) );
+ connect( d->ui->m_selectImage, SIGNAL( clicked() ), this, SLOT( slotSelectImage() ) );
+ connect( d->ui->m_RLButton, SIGNAL( clicked() ), this, SLOT( slotShowReverseList() ) );
+ connect( d->ui->buttonRegister, SIGNAL(clicked()), this, SLOT(slotOpenRegister()));
+ QWidget::setTabOrder( d->ui->m_login, d->ui->m_password->mRemembered );
+ QWidget::setTabOrder( d->ui->m_password->mRemembered, d->ui->m_password->mPassword );
+ QWidget::setTabOrder( d->ui->m_password->mPassword, d->ui->m_autologin );
+}
+
+MSNEditAccountWidget::~MSNEditAccountWidget()
+{
+ delete d;
+}
+
+Kopete::Account * MSNEditAccountWidget::apply()
+{
+ d->autoConfig->saveSettings();
+ KGlobal::config()->setGroup("MSN");
+ KGlobal::config()->writeEntry("JabberAccount", d->ui->JabberAccount->currentText());
+
+ if ( !account() )
+ setAccount( new MSNAccount( d->protocol, d->ui->m_login->text() ) );
+
+ KConfigGroup *config=account()->configGroup();
+
+ account()->setExcludeConnect( d->ui->m_autologin->isChecked() );
+ d->ui->m_password->save( &static_cast<MSNAccount *>(account())->password() );
+
+ config->writeEntry( "exportCustomPicture", d->ui->m_useDisplayPicture->isChecked() );
+ if (d->ui->optionOverrideServer->isChecked() ) {
+ config->writeEntry( "serverName", d->ui->m_serverName->text() );
+ config->writeEntry( "serverPort", d->ui->m_serverPort->value() );
+ }
+ else {
+ config->writeEntry( "serverName", "messenger.hotmail.com" );
+ config->writeEntry( "serverPort", "1863" );
+ }
+
+ config->writeEntry( "useHttpMethod", d->ui->optionUseHttpMethod->isChecked() );
+
+ if(d->ui->m_useWebcamPort->isChecked())
+ config->writeEntry( "WebcamPort" , d->ui->m_webcamPort->value() );
+ else
+ config->writeEntry( "WebcamPort" , 0 );
+
+ // Global Identity
+ config->writeEntry( "ExcludeGlobalIdentity", d->ui->m_globalIdentity->isChecked() );
+
+ // Save the avatar image
+ if( d->ui->m_useDisplayPicture->isChecked() && !d->pictureData.isNull() )
+ {
+ d->pictureUrl = locateLocal( "appdata", "msnpicture-" +
+ account()->accountId().lower().replace( QRegExp("[./~]" ), "-" ) + ".png" );
+ if ( d->pictureData.save( d->pictureUrl, "PNG" ) )
+ {
+ static_cast<MSNAccount *>( account() )->setPictureUrl( d->pictureUrl );
+ }
+ else
+ {
+ KMessageBox::sorry( this, i18n( "<qt>An error occurred when trying to change the display picture.<br>"
+ "Make sure that you have selected a correct image file</qt>" ), i18n( "MSN Plugin" ) );
+ }
+ }
+
+ static_cast<MSNAccount *>( account() )->resetPictureObject();
+
+ if ( account()->isConnected() )
+ {
+ MSNContact *myself = static_cast<MSNContact *>( account()->myself() );
+ MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket();
+ if ( d->ui->m_displayName->text() != myself->property( Kopete::Global::Properties::self()->nickName()).value().toString() )
+ static_cast<MSNAccount *>( account() )->setPublicName( d->ui->m_displayName->text() );
+
+ if ( notify )
+ {
+ if ( d->ui->m_phw->text() != myself->phoneWork() && ( !d->ui->m_phw->text().isEmpty() || !myself->phoneWork().isEmpty() ) )
+ notify->changePhoneNumber( "PHW", d->ui->m_phw->text() );
+ if( d->ui->m_phh->text() != myself->phoneHome() && ( !d->ui->m_phh->text().isEmpty() || !myself->phoneHome().isEmpty() ) )
+ notify->changePhoneNumber( "PHH", d->ui->m_phh->text() );
+ if( d->ui->m_phm->text() != myself->phoneMobile() && ( !d->ui->m_phm->text().isEmpty() || !myself->phoneMobile().isEmpty() ) )
+ notify->changePhoneNumber( "PHM", d->ui->m_phm->text() );
+ // (the && .isEmpty is because one can be null and the other empty)
+
+ if ( ( config->readEntry("BLP") == "BL" ) != d->ui->m_blp->isChecked() )
+ {
+ // Yes, I know, calling sendCommand here is not very clean - Olivier
+ notify->sendCommand( "BLP", d->ui->m_blp->isChecked() ? "BL" : "AL" );
+ }
+ }
+ }
+ return account();
+}
+
+bool MSNEditAccountWidget::validateData()
+{
+ QString userid = d->ui->m_login->text();
+ if ( MSNProtocol::validContactId( userid ) )
+ return true;
+
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Sorry,
+ i18n( "<qt>You must enter a valid email address.</qt>" ), i18n( "MSN Plugin" ) );
+ return false;
+}
+
+void MSNEditAccountWidget::slotAllow()
+{
+ //TODO: play with multiple selection
+ QListBoxItem *item = d->ui->m_BL->selectedItem();
+ if ( !item )
+ return;
+
+ QString handle = item->text();
+
+ MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket();
+ if ( !notify )
+ return;
+ notify->removeContact( handle, MSNProtocol::BL, QString::null, QString::null );
+
+ d->ui->m_BL->takeItem( item );
+ d->ui->m_AL->insertItem( item );
+}
+
+void MSNEditAccountWidget::slotBlock()
+{
+ //TODO: play with multiple selection
+ QListBoxItem *item = d->ui->m_AL->selectedItem();
+ if ( !item )
+ return;
+
+ QString handle = item->text();
+
+ MSNNotifySocket *notify = static_cast<MSNAccount *>( account() )->notifySocket();
+ if ( !notify )
+ return;
+
+ notify->removeContact( handle, MSNProtocol::AL, QString::null, QString::null );
+
+ d->ui->m_AL->takeItem( item );
+ d->ui->m_BL->insertItem( item );
+}
+
+void MSNEditAccountWidget::slotShowReverseList()
+{
+ QStringList reverseList = account()->configGroup()->readListEntry( "reverseList" );
+ KMessageBox::informationList( this, i18n( "Here you can see a list of contacts who added you to their contact list" ), reverseList,
+ i18n( "Reverse List - MSN Plugin" ) );
+}
+
+void MSNEditAccountWidget::slotSelectImage()
+{
+ QString path = 0;
+ bool remoteFile = false;
+ KURL filePath = KFileDialog::getImageOpenURL( QString::null, this, i18n( "MSN Display Picture" ) );
+ if( filePath.isEmpty() )
+ return;
+
+ if( !filePath.isLocalFile() ) {
+ if(!KIO::NetAccess::download( filePath, path, this )) {
+ KMessageBox::sorry( this, i18n( "Downloading of display image failed" ), i18n( "MSN Plugin" ) );
+ return;
+ }
+ remoteFile = true;
+ }
+ else path = filePath.path();
+
+ QImage img( path );
+ img = KPixmapRegionSelectorDialog::getSelectedImage( QPixmap(img), 96, 96, this );
+
+ if(!img.isNull())
+ {
+ img = MSNProtocol::protocol()->scalePicture(img);
+
+ d->ui->m_displayPicture->setPixmap( QPixmap(img) );
+ d->pictureData = img;
+ }
+ else
+ {
+ KMessageBox::sorry( this, i18n( "<qt>An error occurred when trying to change the display picture.<br>"
+ "Make sure that you have selected a correct image file</qt>" ), i18n( "MSN Plugin" ) );
+ }
+ if( remoteFile ) KIO::NetAccess::removeTempFile( path );
+}
+
+void MSNEditAccountWidget::slotOpenRegister()
+{
+ KRun::runURL( "http://register.passport.net/", "text/html" );
+}
+
+#include "msneditaccountwidget.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/ui/msneditaccountwidget.h b/kopete/protocols/msn/ui/msneditaccountwidget.h
new file mode 100644
index 00000000..2b8b8f6e
--- /dev/null
+++ b/kopete/protocols/msn/ui/msneditaccountwidget.h
@@ -0,0 +1,59 @@
+/*
+ msneditaccountwidget.h - MSN Account Widget
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2003 by Martijn Klingens <klingens@kde.org>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MSNEDITACCOUNTWIDEGET_H
+#define MSNEDITACCOUNTWIDEGET_H
+
+#include <qwidget.h>
+
+#include "editaccountwidget.h"
+
+namespace Kopete { class Account; }
+
+class MSNProtocol;
+
+class MSNEditAccountWidgetPrivate;
+
+/**
+ * @author Olivier Goffart <ogoffart @ kde.org>
+ */
+class MSNEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+
+public:
+ MSNEditAccountWidget( MSNProtocol *proto, Kopete::Account *account, QWidget *parent = 0, const char *name = 0 );
+ ~MSNEditAccountWidget();
+ virtual bool validateData();
+ virtual Kopete::Account * apply();
+
+private slots:
+ void slotAllow();
+ void slotBlock();
+ void slotShowReverseList();
+ void slotSelectImage();
+ void slotOpenRegister();
+
+private:
+ MSNEditAccountWidgetPrivate *d;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/msn/ui/msninfo.ui b/kopete/protocols/msn/ui/msninfo.ui
new file mode 100644
index 00000000..17f1eecd
--- /dev/null
+++ b/kopete/protocols/msn/ui/msninfo.ui
@@ -0,0 +1,221 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>MSNInfo</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MSNInfo</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>457</width>
+ <height>360</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout22</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Email address:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_id</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout22_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Display name:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_displayName</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Personal message:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>m_personalMessage</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Phones</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Home:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Work:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>m_phw</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>m_phh</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Mobile:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>m_phm</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_reversed</cstring>
+ </property>
+ <property name="text">
+ <string>I am on &amp;the contact list of this contact</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Show whether you are on the contact list of this user</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this box is checked, you are on this user's contact list.
+If not, the user has not added you to their list, or has removed you.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/msn/webcam.cpp b/kopete/protocols/msn/webcam.cpp
new file mode 100644
index 00000000..db27d65f
--- /dev/null
+++ b/kopete/protocols/msn/webcam.cpp
@@ -0,0 +1,891 @@
+/*
+ Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include "webcam.h"
+
+#if MSN_WEBCAM
+
+#include <stdlib.h>
+#include <kdebug.h>
+#include <qregexp.h>
+#include <kbufferedsocket.h>
+#include <klocale.h>
+#include <kserversocket.h>
+#include <kmessagebox.h>
+#include <qlabel.h>
+#include <qguardedptr.h>
+#include <qtimer.h>
+#include <qevent.h>
+#include <qdatetime.h>
+#include <kconfig.h>
+
+#include "dispatcher.h"
+
+#include "mimicwrapper.h"
+#include "msnwebcamdialog.h"
+
+
+#include "avdevice/videodevicepool.h"
+
+using namespace KNetwork;
+
+namespace P2P {
+
+Webcam::Webcam(Who who, const QString& to, Dispatcher *parent, Q_UINT32 sessionId)
+ : TransferContext(to,parent,sessionId) , m_who(who) , m_timerId(0)
+{
+ setType(P2P::WebcamType);
+ m_direction = Incoming;
+ m_listener = 0l;
+ m_webcamSocket=0L;
+// m_webcamState=wsNegotiating;
+
+ m_mimic=0L;
+ m_widget=0L;
+
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+
+ // Read the configuration to get the number of frame per second to send
+ int webCamFps=config->readNumEntry("WebcamFPS", 25);
+ m_timerFps = 1000 / webCamFps;
+}
+
+Webcam::~Webcam()
+{
+ kdDebug(14140) << k_funcinfo<< "################################################" << endl;
+ m_dispatcher=0l;
+ delete m_mimic;
+ delete m_webcamSocket;
+ delete m_widget;
+
+ if(m_timerId != 0) //if we were sending
+ {
+ Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self();
+ videoDevice->stopCapturing();
+ videoDevice->close();
+ }
+
+}
+
+void Webcam::askIncommingInvitation()
+{
+ m_direction = Incoming;
+ //protect, in case this is deleted when the messagebox is active
+ QGuardedPtr<Webcam> _this = this;
+ QString message= (m_who==wProducer) ?
+ i18n("<qt>The contact %1 wants to see <b>your</b> webcam, do you want them to see it?</qt>") :
+ i18n("The contact %1 wants to show you his/her webcam, do you want to see it?") ;
+ int result=KMessageBox::questionYesNo( 0L , message.arg(m_recipient),
+ i18n("Webcam invitation - Kopete MSN Plugin") , i18n("Accept") , i18n("Decline"));
+ if(!_this)
+ return;
+
+ QString content = QString("SessionID: %1\r\n\r\n").arg(m_sessionId);
+ if(result==KMessageBox::Yes)
+ {
+ //Send two message, an OK, and an invite.
+ //Normaly, the user should decline the invite (i hope)
+
+ // Send a 200 OK message to the recipient.
+ sendMessage(OK, content);
+
+
+ //send an INVITE message we want the user decline
+ //need to change the branch of the second message
+ m_branch=Uid::createUid();
+ m_state = Negotiation; //set type to application/x-msnmsgr-transreqbody
+
+ content=QString("Bridges: TRUDPv1 TCPv1\r\n"
+ "NetID: -1280904111\r\n"
+ "Conn-Type: Firewall\r\n"
+ "UPnPNat: false\r\n"
+ "ICF: false\r\n\r\n");
+
+ sendMessage(INVITE, content);
+
+ }
+ else
+ {
+ //Decline the invitation
+ sendMessage(DECLINE, content);
+ m_state=Finished;
+ }
+}
+
+void Webcam::sendBYEMessage()
+{
+ m_state=Finished;
+ QString content="Context: dAMAgQ==\r\n";
+ sendMessage(BYE,content);
+
+ //If ever the opposite client was dead or something, we'll ack anyway, so everything get cleaned
+ QTimer::singleShot(60*1000 , this, SLOT(acknowledged()));
+}
+
+
+
+void Webcam::acknowledged()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ switch(m_state)
+ {
+ case Invitation:
+ {
+// m_state=Negotiation;
+ break;
+ }
+
+ /*
+ case Negotiation:
+ {
+ if(m_type == UserDisplayIcon)
+ {
+ <<< Data preparation acknowledge message.
+ m_state = DataTransfer;
+ m_identifier++;
+ Start sending data.
+ slotSendData();
+ }
+ break;
+ }
+
+ case DataTransfer:
+ NOTE <<< Data acknowledged message.
+ <<< Bye message should follow.
+ if(m_type == File)
+ {
+ if(m_handshake == 0x01)
+ {
+ Data handshake acknowledge message.
+ Start sending data.
+ slotSendData();
+ }
+ else if(m_handshake == 0x02)
+ {
+ Data acknowledge message.
+ Send the recipient a BYE message.
+ m_state = Finished;
+ sendMessage(BYE, "\r\n");
+ }
+ }
+
+ break;
+ */
+ case Finished:
+ //BYE or DECLINE acknowledge message.
+ m_dispatcher->detach(this);
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+
+void Webcam::processMessage(const Message& message)
+{
+ if(message.header.dataOffset+message.header.dataSize >= message.header.totalDataSize)
+ acknowledge( message ); //aknowledge if needed
+
+ if(message.applicationIdentifier != 4l)
+ {
+ QString body = QCString(message.body.data(), message.header.dataSize);
+ kdDebug(14141) << k_funcinfo << "received, " << body << endl;
+
+ if(body.startsWith("MSNSLP/1.0 200 OK"))
+ {
+ m_direction = Outgoing;
+ }
+ if(body.startsWith("INVITE"))
+ {
+ if(m_direction == Outgoing)
+ {
+ QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_branch=regex.cap(1);
+ //decline
+ sendMessage(DECLINE);
+ makeSIPMessage("syn",0x17,0x2a,0x01);
+ }
+ }
+ else if(body.startsWith("MSNSLP/1.0 603 DECLINE"))
+ {
+ //if it is the declinaison of the second invite message, we have to don't care
+ //TODO anyway, if it's the declinaison of our invitation, we have to something
+ }
+ else if(body.startsWith("BYE"))
+ {
+ m_state = Finished;
+
+ // Dispose of this transfer context.
+ m_dispatcher->detach(this);
+ }
+ return;
+ }
+
+
+
+ //Let's take the fun, we entering into the delicious webcam negotiation binary protocol
+
+ //well, there is maybe better to take utf16, but it's ascii, so no problem.
+ QByteArray dataMessage=message.body;
+
+#if 0
+ QString echoS="";
+ unsigned int f=0;
+ while(f<dataMessage.size())
+ {
+ echoS+="\n";
+ for(unsigned int q=0; q<16 ; q++)
+ {
+ if(q+f<dataMessage.size())
+ {
+ unsigned int N=(unsigned int) (dataMessage[q+f]);
+ if(N<16)
+ echoS+="0";
+ echoS+=QString::number( N ,16)+" ";
+ }
+ else
+ echoS+=" ";
+ }
+ echoS+=" ";
+
+ for(unsigned int q=0; (q<16 && (q+f)<dataMessage.size()) ; q++)
+ {
+ unsigned char X=dataMessage[q+f];
+ char C=((char)(( X<128 && X>31 ) ? X : '.'));
+ echoS+=QString::fromLatin1(&C,1);
+ }
+ f+=16;
+ }
+ kdDebug(14141) << k_funcinfo << dataMessage.size() << echoS << endl;
+#endif
+
+
+
+
+
+ for(uint pos=m_content.isNull() ? 10 : 0; pos<dataMessage.size(); pos+=2)
+ {
+ if(dataMessage[pos] !=0 )
+ m_content+=dataMessage[pos];
+ }
+
+ if(message.header.dataOffset+message.header.dataSize < message.header.totalDataSize)
+ return;
+
+ kdDebug(14141) << k_funcinfo << "Message contents: " << m_content << "\n" << endl;
+ if(m_content.startsWith("syn"))
+ {
+ if(m_direction == Incoming)
+ makeSIPMessage("syn",0x17,0x2a,0x01);
+ else
+ makeSIPMessage("ack",0xea,0x00,0x00);
+ }
+ else if(m_content.startsWith("ack"))
+ {
+ if(m_direction == Incoming)
+ makeSIPMessage("ack",0xea,0x00,0x00);
+
+ if(m_who==wProducer)
+ {
+ uint sess=rand()%1000+5000;
+ uint rid=rand()%100+50;
+ m_myAuth=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid).arg(sess);
+ kdDebug(14140) << k_funcinfo << "m_myAuth= " << m_myAuth << endl;
+ QString producerxml=xml(sess , rid);
+ kdDebug(14140) << k_funcinfo << "producerxml= " << producerxml << endl;
+ makeSIPMessage(producerxml);
+ }
+ }
+ else if(m_content.contains("<producer>") || m_content.contains("<viewer>"))
+ {
+ QRegExp rx("<rid>([0-9]*)</rid>.*<session>([0-9]*)</session>");
+ rx.search(m_content);
+ QString rid=rx.cap(1);
+ QString sess=rx.cap(2);
+ if(m_content.contains("<producer>"))
+ {
+
+ QString viewerxml=xml(sess.toUInt() , rid.toUInt());
+ kdDebug(14140) << k_funcinfo << "vewerxml= " << viewerxml << endl;
+ makeSIPMessage( viewerxml ,0x00,0x09,0x00 );
+ m_peerAuth=m_myAuth=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid,sess);
+ kdDebug(14140) << k_funcinfo << "m_auth= " << m_myAuth << endl;
+ }
+ else
+ {
+ m_peerAuth=QString("recipientid=%1&sessionid=%2\r\n\r\n").arg(rid,sess);
+
+ makeSIPMessage("receivedViewerData", 0xec , 0xda , 0x03);
+ }
+
+ if(!m_listener)
+ {
+ //it should have been creed in xml
+ sendBYEMessage();
+ return;
+ }
+ //m_listener->setResolutionEnabled(true);
+ // Create the callback that will try to accept incoming connections.
+ QObject::connect(m_listener, SIGNAL(readyAccept()), this, SLOT(slotAccept()));
+ QObject::connect(m_listener, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
+ // Listen for incoming connections.
+ bool isListening = m_listener->listen();
+ kdDebug(14140) << k_funcinfo << (isListening ? QString("listening %1").arg(m_listener->localAddress().toString()) : QString("not listening")) << endl;
+
+ rx=QRegExp("<tcpport>([^<]*)</tcpport>");
+ rx.search(m_content);
+ QString port1=rx.cap(1);
+ if(port1=="0")
+ port1=QString::null;
+
+ rx=QRegExp("<tcplocalport>([^<]*)</tcplocalport>");
+ rx.search(m_content);
+ QString port2=rx.cap(1);
+ if(port2==port1 || port2=="0")
+ port2=QString::null;
+
+ rx=QRegExp("<tcpexternalport>([^<]*)</tcpexternalport>");
+ rx.search(m_content);
+ QString port3=rx.cap(1);
+ if(port3==port1 || port3==port2 || port3=="0")
+ port3=QString::null;
+
+ int an=0;
+ while(true)
+ {
+ an++;
+ if(!m_content.contains( QString("<tcpipaddress%1>").arg(an) ))
+ break;
+ rx=QRegExp(QString("<tcpipaddress%1>([^<]*)</tcpipaddress%2>").arg(an).arg(an));
+ rx.search(m_content);
+ QString ip=rx.cap(1);
+ if(ip.isNull())
+ continue;
+
+ if(!port1.isNull())
+ {
+ kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port1 << endl;
+ KBufferedSocket *sock=new KBufferedSocket( ip, port1, this );
+ m_allSockets.append(sock);
+ QObject::connect( sock, SIGNAL( connected( const KResolverEntry&) ), this, SLOT( slotSocketConnected() ) );
+ QObject::connect( sock, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
+ sock->connect(ip, port1);
+ kdDebug(14140) << k_funcinfo << "okok " << sock << " - " << sock->peerAddress().toString() << " ; " << sock->localAddress().toString() << endl;
+ }
+ if(!port2.isNull())
+ {
+ kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port2 << endl;
+ KBufferedSocket *sock=new KBufferedSocket( ip, port2, this );
+ m_allSockets.append(sock);
+ QObject::connect( sock, SIGNAL( connected( const KResolverEntry&) ), this, SLOT( slotSocketConnected() ) );
+ QObject::connect( sock, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
+ sock->connect(ip, port2);
+ }
+ if(!port3.isNull())
+ {
+ kdDebug(14140) << k_funcinfo << "trying to connect on " << ip <<":" << port3 << endl;
+ KBufferedSocket *sock=new KBufferedSocket( ip, port3, this );
+ m_allSockets.append(sock);
+ QObject::connect( sock, SIGNAL( connected( const KResolverEntry&) ), this, SLOT( slotSocketConnected() ) );
+ QObject::connect( sock, SIGNAL( gotError(int)), this, SLOT(slotSocketError(int)));
+ sock->connect(ip, port3);
+ }
+ }
+ QValueList<KBufferedSocket*>::iterator it;
+ for ( it = m_allSockets.begin(); it != m_allSockets.end(); ++it )
+ {
+ KBufferedSocket *sock=(*it);
+
+ //sock->enableRead( false );
+ kdDebug(14140) << k_funcinfo << "connect to " << sock << " - "<< sock->peerAddress().toString() << " ; " << sock->localAddress().toString() << endl;
+ }
+ }
+ else if(m_content.contains("receivedViewerData"))
+ {
+ //I'm happy you received the xml i sent, really.
+ }
+ else
+ error();
+ m_content=QString::null;
+}
+
+void Webcam::makeSIPMessage(const QString &message, Q_UINT8 XX, Q_UINT8 YY , Q_UINT8 ZZ)
+{
+ QByteArray dataMessage; //(12+message.length()*2);
+ QDataStream writer(dataMessage, IO_WriteOnly);
+ writer.setByteOrder(QDataStream::LittleEndian);
+ writer << (Q_UINT8)0x80;
+ writer << (Q_UINT8)XX;
+ writer << (Q_UINT8)YY;
+ writer << (Q_UINT8)ZZ;
+ writer << (Q_UINT8)0x08;
+ writer << (Q_UINT8)0x00;
+ writer << message+'\0';
+ //writer << (Q_UINT16)0x0000;
+
+ /*QString echoS="";
+ unsigned int f=0;
+ while(f<dataMessage.size())
+ {
+ echoS+="\n";
+ for(unsigned int q=0; q<16 ; q++)
+ {
+ if(q+f<dataMessage.size())
+ {
+ unsigned int N=(unsigned int) (dataMessage[q+f]);
+ if(N<16)
+ echoS+="0";
+ echoS+=QString::number( N ,16)+" ";
+ }
+ else
+ echoS+=" ";
+ }
+ echoS+=" ";
+
+ for(unsigned int q=0; (q<16 && (q+f)<dataMessage.size()) ; q++)
+ {
+ unsigned char X=dataMessage[q+f];
+ char C=((char)(( X<128 && X>31 ) ? X : '.'));
+ echoS+=QString::fromLatin1(&C,1);
+ }
+ f+=16;
+ }
+ kdDebug(14141) << k_funcinfo << dataMessage.size() << echoS << endl;*/
+
+
+ sendBigP2PMessage(dataMessage);
+}
+
+void Webcam::sendBigP2PMessage( const QByteArray & dataMessage)
+{
+ unsigned int size=m_totalDataSize=dataMessage.size();
+ m_offset=0;
+ ++m_identifier;
+
+ for(unsigned int f=0;f<size;f+=1200)
+ {
+ m_offset=f;
+ QByteArray dm2;
+ dm2.duplicate(dataMessage.data()+m_offset, QMIN(1200,m_totalDataSize-m_offset));
+ sendData( dm2 );
+ m_offset+=dm2.size();
+ }
+ m_offset=0;
+ m_totalDataSize=0;
+}
+
+
+
+QString Webcam::xml(uint session , uint rid)
+{
+ QString who= ( m_who == wProducer ) ? QString("producer") : QString("viewer");
+
+ QString ip;
+
+ uint ip_number=1;
+ QStringList::iterator it;
+ QStringList ips=m_dispatcher->localIp();
+ for ( it = ips.begin(); it != ips.end(); ++it )
+ {
+ ip+=QString("<tcpipaddress%1>%2</tcpipaddress%3>").arg(ip_number).arg(*it).arg(ip_number);
+ ++ip_number;
+ }
+
+ QString port = QString::number(getAvailablePort());
+
+ m_listener = new KServerSocket(port, this) ;
+
+ return "<" + who + "><version>2.0</version><rid>"+QString::number(rid)+"</rid><udprid>"+QString::number(rid+1)+"</udprid><session>"+QString::number(session)+"</session><ctypes>0</ctypes><cpu>2931</cpu>" +
+ "<tcp><tcpport>"+port+"</tcpport>\t\t\t\t\t\t\t\t <tcplocalport>"+port+"</tcplocalport>\t\t\t\t\t\t\t\t <tcpexternalport>"+port+"</tcpexternalport>"+ip+"</tcp>"+
+ "<udp><udplocalport>7786</udplocalport><udpexternalport>31863</udpexternalport><udpexternalip>"+ ip +"</udpexternalip><a1_port>31859</a1_port><b1_port>31860</b1_port><b2_port>31861</b2_port><b3_port>31862</b3_port><symmetricallocation>1</symmetricallocation><symmetricallocationincrement>1</symmetricallocationincrement><udpversion>1</udpversion><udpinternalipaddress1>127.0.0.1</udpinternalipaddress1></udp>"+
+ "<codec></codec><channelmode>1</channelmode></"+who+">\r\n\r\n";
+}
+
+int Webcam::getAvailablePort()
+{
+ KConfig *config = KGlobal::config();
+ config->setGroup( "MSN" );
+ QString basePort=config->readEntry("WebcamPort");
+ if(basePort.isEmpty() || basePort == "0" )
+ basePort="6891";
+
+ uint firstport = basePort.toInt();
+ uint maxOffset=config->readUnsignedNumEntry("WebcamMaxPortOffset", 10);
+ uint lastport = firstport + maxOffset;
+
+ // try to find an available port
+ //
+ KServerSocket *ss = new KServerSocket();
+ ss->setFamily(KResolver::InetFamily);
+ bool found = false;
+ unsigned int port = firstport;
+ for( ; port <= lastport; ++port) {
+ ss->setAddress( QString::number( port ) );
+ bool success = ss->listen();
+ if( found = ( success && ss->error() == KSocketBase::NoError ) )
+ break;
+ ss->close();
+ }
+ delete ss;
+
+
+ kdDebug(14140) << k_funcinfo<< "found available port : " << port << endl;
+
+ return port;
+}
+
+
+/* ---------- Now functions about the dirrect connection --------- */
+
+void Webcam::slotSocketConnected()
+{
+ kdDebug(14140) << k_funcinfo <<"##########################" << endl;
+
+ m_webcamSocket=const_cast<KBufferedSocket*>(static_cast<const KBufferedSocket*>(sender()));
+ if(!m_webcamSocket)
+ return;
+
+ kdDebug(14140) << k_funcinfo << "Connection established on " << m_webcamSocket->peerAddress().toString() << " ; " << m_webcamSocket->localAddress().toString() << endl;
+
+ m_webcamSocket->setBlocking(false);
+ m_webcamSocket->enableRead(true);
+ m_webcamSocket->enableWrite(false);
+
+ // Create the callback that will try to read bytes from the accepted socket.
+ QObject::connect(m_webcamSocket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
+ // Create the callback that will try to handle the socket close event.
+ QObject::connect(m_webcamSocket, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
+ // Create the callback that will try to handle the socket error event.
+// QObject::connect(m_webcamSocket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+
+ m_webcamStates[m_webcamSocket]=wsConnected;
+ QCString to_send=m_peerAuth.utf8();
+ m_webcamSocket->writeBlock(to_send.data(), to_send.length());
+ kdDebug(14140) << k_funcinfo << "sending "<< m_peerAuth << endl;
+
+}
+
+
+void Webcam::slotAccept()
+{
+ // Try to accept an incoming connection from the sending client.
+ m_webcamSocket = static_cast<KBufferedSocket*>(m_listener->accept());
+ if(!m_webcamSocket)
+ {
+ // NOTE If direct connection fails, the sending
+ // client wil transfer the file data through the
+ // existing session.
+ kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl;
+ // Close the listening endpoint.
+// m_listener->close();
+ return;
+ }
+
+ kdDebug(14140) << k_funcinfo << "################################ Direct connection established." << endl;
+
+ // Set the socket to non blocking,
+ // enable the ready read signal and disable
+ // ready write signal.
+ // NOTE readyWrite consumes too much cpu usage.
+ m_webcamSocket->setBlocking(false);
+ m_webcamSocket->enableRead(true);
+ m_webcamSocket->enableWrite(false);
+
+ // Create the callback that will try to read bytes from the accepted socket.
+ QObject::connect(m_webcamSocket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
+ // Create the callback that will try to handle the socket close event.
+ QObject::connect(m_webcamSocket, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
+ // Create the callback that will try to handle the socket error event.
+ QObject::connect(m_webcamSocket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+
+ m_allSockets.append(m_webcamSocket);
+ m_webcamStates[m_webcamSocket]=wsNegotiating;
+}
+
+void Webcam::slotSocketRead()
+{
+ m_webcamSocket=const_cast<KBufferedSocket*>(static_cast<const KBufferedSocket*>(sender()));
+
+ uint available = m_webcamSocket->bytesAvailable();
+ kdDebug(14140) << k_funcinfo << m_webcamSocket << "############# " << available << " bytes available." << endl;
+
+ QByteArray avail_buff(available);
+ m_webcamSocket->peekBlock(avail_buff.data(), avail_buff.size());
+
+ kdDebug(14140) << k_funcinfo << m_webcamSocket << avail_buff << endl;
+
+
+
+ const QString connected_str("connected\r\n\r\n");
+ switch(m_webcamStates[m_webcamSocket])
+ {
+ case wsNegotiating:
+ {
+ if(available < m_myAuth.length())
+ {
+ kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<m_myAuth.length()<< " )"<< endl;
+ break;
+ }
+ QByteArray buffer(available);
+ m_webcamSocket->readBlock(buffer.data(), buffer.size());
+
+ kdDebug(14140) << k_funcinfo << buffer.data() << endl;
+
+ if(QString(buffer) == m_myAuth )
+ {
+ closeAllOtherSockets();
+ kdDebug(14140) << k_funcinfo << "Sending " << connected_str << endl;
+ QCString conne=connected_str.utf8();
+ m_webcamSocket->writeBlock(conne.data(), conne.length());
+ m_webcamStates[m_webcamSocket]=wsConnecting;
+
+ //SHOULD NOT BE THERE
+ m_mimic=new MimicWrapper();
+ if(m_who==wProducer)
+ {
+ Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self();
+ videoDevice->open();
+ videoDevice->setSize(320, 240);
+ videoDevice->startCapturing();
+
+ m_timerId=startTimer(m_timerFps);
+ kdDebug(14140) << k_funcinfo << "new timer" << m_timerId << endl;
+ }
+ m_widget=new MSNWebcamDialog(m_recipient);
+ connect(m_widget, SIGNAL( closingWebcamDialog() ) , this , SLOT(sendBYEMessage()));
+
+ }
+ else
+ {
+ kdWarning(14140) << k_funcinfo << "Auth failed" << endl;
+ m_webcamSocket->disconnect();
+ m_webcamSocket->deleteLater();
+ m_allSockets.remove(m_webcamSocket);
+ m_webcamSocket=0l;
+ //sendBYEMessage();
+ }
+ break;
+ }
+ case wsConnecting:
+ case wsConnected:
+ {
+ if(available < connected_str.length())
+ {
+ kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<connected_str.length()<< " )"<< endl;
+ break;
+ }
+ QByteArray buffer(connected_str.length());
+ m_webcamSocket->readBlock(buffer.data(), buffer.size());
+
+// kdDebug(14140) << k_funcinfo << "state " << m_webcamState << " received :" << QCString(buffer) << endl;
+
+
+ if(QString(buffer) == connected_str)
+ {
+ if(m_webcamStates[m_webcamSocket]==wsConnected)
+ {
+ closeAllOtherSockets();
+ kdDebug(14140) << k_funcinfo << "Sending " << connected_str << endl;
+ QCString conne=connected_str.utf8();
+ m_webcamSocket->writeBlock(conne.data(), conne.length());
+
+ //SHOULD BE DONE IN ALL CASE
+ m_mimic=new MimicWrapper();
+ if(m_who==wProducer)
+ {
+ Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self();
+ videoDevice->open();
+ videoDevice->setSize(320, 240);
+ videoDevice->startCapturing();
+
+ m_timerId=startTimer(m_timerFps);
+ kdDebug(14140) << k_funcinfo << "new timer" << m_timerId << endl;
+ }
+ m_widget=new MSNWebcamDialog(m_recipient);
+ connect(m_widget, SIGNAL( closingWebcamDialog() ) , this , SLOT(sendBYEMessage()));
+
+ }
+ m_webcamStates[m_webcamSocket]=wsTransfer;
+
+ }
+ else
+ {
+ kdWarning(14140) << k_funcinfo << "Connecting failed" << endl;
+ m_webcamSocket->disconnect();
+ m_webcamSocket->deleteLater();
+ m_allSockets.remove(m_webcamSocket);
+ m_webcamSocket=0l;
+ }
+ break;
+ }
+ case wsTransfer:
+ {
+ if(m_who==wProducer)
+ {
+ kdWarning(14140) << k_funcinfo << "data received when we are producer"<< endl;
+ break;
+ }
+ if(available < 24)
+ {
+ kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<24<< " )"<< endl;
+ break;
+ }
+ QByteArray buffer(24);
+ m_webcamSocket->peekBlock(buffer.data(), buffer.size());
+
+ Q_UINT32 paysize=(uchar)buffer[8] + ((uchar)buffer[9]<<8) + ((uchar)buffer[10]<<16) + ((uchar)buffer[11]<<24);
+
+ if(available < (paysize+24))
+ {
+ kdDebug(14140) << k_funcinfo << "waiting more data ( " << available << " of " <<paysize<< " )"<< endl;
+ break;
+ }
+ m_webcamSocket->readBlock(buffer.data(), 24); //flush
+ buffer.resize(paysize);
+ m_webcamSocket->readBlock(buffer.data(), buffer.size());
+
+ QPixmap pix=m_mimic->decode(buffer);
+ if(pix.isNull())
+ {
+ kdWarning(14140) << k_funcinfo << "incorrect pixmap returned, better to stop everything"<< endl;
+ m_webcamSocket->disconnect();
+ sendBYEMessage();
+ }
+ m_widget->newImage(pix);
+ break;
+ }
+ default:
+ break;
+ }
+
+}
+
+void Webcam::slotListenError(int errorCode)
+{
+ kdWarning(14140) << k_funcinfo << "Error " << errorCode << " : " << m_listener->errorString() << endl;
+}
+
+void Webcam::slotSocketClosed()
+{
+ if(!m_dispatcher) //we are in this destructor
+ return;
+
+ KBufferedSocket *m_webcamSocket=const_cast<KBufferedSocket*>(static_cast<const KBufferedSocket*>(sender()));
+
+ kdDebug(14140) << k_funcinfo << m_webcamSocket << endl;
+
+ if(m_listener)
+ { //if we are still waiting for other socket to connect, just remove this socket from the socket list
+ m_webcamSocket->disconnect();
+ m_webcamSocket->deleteLater();
+ m_allSockets.remove(m_webcamSocket);
+ m_webcamSocket=0l;
+ }
+ else // else, close the session
+ sendBYEMessage();
+
+}
+
+void Webcam::slotSocketError(int errorCode)
+{
+ KBufferedSocket *socket=const_cast<KBufferedSocket*>(static_cast<const KBufferedSocket*>(sender()));
+ kdDebug(14140) << k_funcinfo << socket << " - " << errorCode << " : " << socket->errorString() << endl;
+ //sendBYEMessage();
+}
+
+void Webcam::closeAllOtherSockets()
+{
+ //m_lisener->close();
+ delete m_listener;
+ m_listener=0l;
+
+ QValueList<KBufferedSocket*>::iterator it;
+ for ( it = m_allSockets.begin(); it != m_allSockets.end(); ++it )
+ {
+ KBufferedSocket *sock=(*it);
+ if(sock != m_webcamSocket)
+ delete sock;
+ }
+ m_allSockets.clear();
+}
+
+
+void Webcam::timerEvent( QTimerEvent *e )
+{
+ if(e->timerId() != m_timerId)
+ return TransferContext::timerEvent(e);
+
+// kdDebug(14140) << k_funcinfo << endl;
+
+ Kopete::AV::VideoDevicePool *videoDevice = Kopete::AV::VideoDevicePool::self();
+ videoDevice->getFrame();
+ QImage img;
+ videoDevice->getImage(&img);
+
+ if(m_widget)
+ m_widget->newImage(img);
+
+ if(img.width()!=320 || img.height()!=240)
+ {
+ kdWarning(14140) << k_funcinfo << "Bad image size " <<img.width() << "x" << img.height() << endl;
+ return;
+ }
+
+ uchar *bits=img.bits();
+ QByteArray image_data(img.width()*img.height()*3);
+ uint b2=0;
+ uint imgsize=img.width()*img.height()*4;
+ for(uint f=0; f< imgsize; f+=4)
+ {
+ image_data[b2+0]=bits[f+2];
+ image_data[b2+1]=bits[f+1];
+ image_data[b2+2]=bits[f+0];
+ b2+=3;
+ }
+
+ QByteArray frame=m_mimic->encode(image_data);
+
+
+ kdDebug(14140) << k_funcinfo << "Sendinf frame of size " << frame.size() << endl;
+ //build the header.
+ QByteArray header;
+
+ QDataStream writer(header, IO_WriteOnly);
+ writer.setByteOrder(QDataStream::LittleEndian);
+ writer << (Q_UINT16)24; // header size
+ writer << (Q_UINT16)img.width();
+ writer << (Q_UINT16)img.height();
+ writer << (Q_UINT16)0x0000; //wtf .?
+ writer << (Q_UINT32)frame.size();
+ writer << (Q_UINT8)('M') << (Q_UINT8)('L') << (Q_UINT8)('2') << (Q_UINT8)('0');
+ writer << (Q_UINT32)0x00000000; //wtf .?
+ writer << QTime::currentTime(); //FIXME: possible midnight bug ?
+
+ m_webcamSocket->writeBlock(header.data(), header.size());
+ m_webcamSocket->writeBlock(frame.data(), frame.size());
+}
+
+
+}
+
+
+#include "webcam.moc"
+
+#endif
+
diff --git a/kopete/protocols/msn/webcam.h b/kopete/protocols/msn/webcam.h
new file mode 100644
index 00000000..4dc72fae
--- /dev/null
+++ b/kopete/protocols/msn/webcam.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef P2PWEBCAM_H
+#define P2PWEBCAM_H
+
+#include "p2p.h"
+
+#if MSN_WEBCAM
+
+namespace KNetwork{ class KServerSocket; class KBufferedSocket; }
+
+class MimicWrapper;
+class QLabel;
+class MSNWebcamDialog;
+class QTimerEvent;
+
+namespace P2P {
+
+
+class Webcam : public TransferContext
+{ Q_OBJECT
+ public:
+ enum Who { wProducer , wViewer };
+
+ Webcam( Who who , const QString& to, Dispatcher *parent, Q_UINT32 sessionID);
+ ~Webcam( );
+
+ virtual void processMessage(const Message& message);
+
+ public slots:
+ void askIncommingInvitation();
+ virtual void acknowledged();
+ void sendBYEMessage();
+
+ private:
+ void makeSIPMessage(const QString &message, Q_UINT8 XX=0, Q_UINT8 YY=9 , Q_UINT8 ZZ=0);
+ void sendBigP2PMessage( const QByteArray& dataMessage );
+ void closeAllOtherSockets();
+ QString m_content;
+
+ QString xml(uint session , uint rid);
+ int getAvailablePort();
+
+
+ KNetwork::KServerSocket *m_listener;
+ KNetwork::KBufferedSocket *m_webcamSocket;
+
+ enum WebcamStatus { wsNegotiating , wsConnecting, wsConnected, wsTransfer } ;
+
+ Who m_who;
+
+ QString m_myAuth;
+ QString m_peerAuth;
+
+ MimicWrapper *m_mimic;
+ MSNWebcamDialog *m_widget;
+
+ QValueList<KNetwork::KBufferedSocket* > m_allSockets;
+ QMap<KNetwork::KBufferedSocket*, WebcamStatus> m_webcamStates;
+
+ int m_timerId;
+ int m_timerFps;
+
+ private slots:
+ void slotListenError(int errorCode);
+ void slotAccept();
+ void slotSocketRead();
+ void slotSocketClosed();
+ void slotSocketError(int errorCode);
+ void slotSocketConnected();
+// void slotReadyWrite();
+ protected:
+ virtual void timerEvent( QTimerEvent * );
+};
+
+}
+
+#endif
+
+#endif
diff --git a/kopete/protocols/msn/webcam/Makefile.am b/kopete/protocols/msn/webcam/Makefile.am
new file mode 100644
index 00000000..27d37fe0
--- /dev/null
+++ b/kopete/protocols/msn/webcam/Makefile.am
@@ -0,0 +1,14 @@
+METASOURCES = AUTO
+SUBDIRS = libmimic
+AM_CPPFLAGS = -I$(srcdir)/libmimic \
+ $(KOPETE_INCLUDES) \
+ $(all_includes) \
+ $(GLIB_CFLAGS)
+
+noinst_LTLIBRARIES = libmimicwrapper.la
+
+libmimicwrapper_la_SOURCES = mimicwrapper.cpp msnwebcamdialog.cpp
+libmimicwrapper_la_LIBADD = ./libmimic/libmimic.la
+libmimicwrapper_la_LDFLAGS = -no-undefined $(GLIB_LIBS) $(all_libraries)
+
+
diff --git a/kopete/protocols/msn/webcam/libmimic/AUTHORS b/kopete/protocols/msn/webcam/libmimic/AUTHORS
new file mode 100644
index 00000000..8dd0671d
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/AUTHORS
@@ -0,0 +1,2 @@
+Ole André Vadla Ravnås <oleavr@gmail.com>
+
diff --git a/kopete/protocols/msn/webcam/libmimic/COPYING b/kopete/protocols/msn/webcam/libmimic/COPYING
new file mode 100644
index 00000000..b1e3f5a2
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/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/msn/webcam/libmimic/Makefile.am b/kopete/protocols/msn/webcam/libmimic/Makefile.am
new file mode 100644
index 00000000..1a2c99d3
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/Makefile.am
@@ -0,0 +1,24 @@
+# INCLUDES = @GLIB_CFLAGS@
+AM_CPPFLAGS = $(all_includes) $(GLIB_CFLAGS)
+
+# libmimicincludedir = $(includedir)
+# libmimicinclude_HEADERS = mimic.h
+
+noinst_LTLIBRARIES = libmimic.la
+libmimic_la_SOURCES = \
+ mimic.c \
+ encode.c \
+ decode.c \
+ bitstring.c \
+ vlc_common.c \
+ vlc_encode.c \
+ vlc_decode.c \
+ fdct_quant.c \
+ idct_dequant.c \
+ colorspace.c \
+ deblock.c \
+ mimic-private.h
+# libmimic_la_LDFLAGS = \
+# -version-info $(MIMIC_CURRENT):$(MIMIC_REVISION):$(MIMIC_AGE) \
+# -export-symbols-regex "^[^_].*"
+
diff --git a/kopete/protocols/msn/webcam/libmimic/README b/kopete/protocols/msn/webcam/libmimic/README
new file mode 100644
index 00000000..c60336ec
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/README
@@ -0,0 +1,40 @@
+ABOUT
+-----
+
+libmimic is an open source video encoding/decoding library for Mimic V2.x-
+encoded content (fourCC: ML20), which is the encoding used by MSN Messenger
+for webcam conversations.
+
+It was written because there was no third-party MSN-client that supported
+this feature due to this proprietary/unknown codec involved. I didn't like
+this lack of interoperability, so I decided to do something about it. After
+studying the official MSN-client a little closer, it became clear that the
+codec involved was statically linked into the executable, so there was no
+easy way to use the codec code through Wine. So for fun, and challenge, I
+reverse-engineered the original implementation by studying the massive
+amount of assembly code involved, and after a lot of hard work I ended
+up with this implementation in C.
+
+It should be noted that reverse-engineering for interoperability is 100%
+legal here in Norway (and in most European countries).
+
+
+THANKS
+------
+
+Special thanks to Rob Taylor and the rest of the Farsight-team for all
+the feedback and inspiration during development, you guys rock! :-)
+
+
+BOTTOM LINE
+-----------
+
+If you like my work and decide to use it in your project, please feel free
+to credit me. I put a lot of time and hard work into this, so I hope others
+will find it useful.
+
+Well, enough chit chat, enjoy! :-)
+
+Ole André Vadla Ravnås
+oleavr at gmail dot com
+
diff --git a/kopete/protocols/msn/webcam/libmimic/bitstring.c b/kopete/protocols/msn/webcam/libmimic/bitstring.c
new file mode 100644
index 00000000..2aef7284
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/bitstring.c
@@ -0,0 +1,88 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 "mimic-private.h"
+
+/*
+ * _read_bits
+ *
+ * Internal helper-function used to read num_bits
+ * from stream.
+ */
+guint32 _read_bits(MimCtx *ctx, gint num_bits)
+{
+ guint32 bits;
+
+ if (ctx->cur_chunk_len >= 16) {
+ guchar *input_buf = (guchar *) ctx->data_buffer + ctx->data_index;
+
+ if (!ctx->read_odd) {
+ ctx->read_odd = TRUE;
+
+ ctx->cur_chunk = (input_buf[3] << 24) |
+ (input_buf[2] << 16) |
+ (input_buf[1] << 8) |
+ input_buf[0];
+
+ } else {
+ ctx->read_odd = FALSE;
+
+ ctx->cur_chunk = (input_buf[1] << 24) |
+ (input_buf[0] << 16) |
+ (input_buf[7] << 8) |
+ input_buf[6];
+
+ ctx->data_index += 4;
+ }
+
+ ctx->cur_chunk_len -= 16;
+ }
+
+ bits = (ctx->cur_chunk << ctx->cur_chunk_len) >> (32 - num_bits);
+ ctx->cur_chunk_len += num_bits;
+
+ return bits;
+}
+
+/*
+ * _write_bits
+ *
+ * Internal helper-function used to write "length"
+ * bits of "bits" to stream.
+ */
+void _write_bits(MimCtx *ctx, guint32 bits, gint length)
+{
+ /* Left-align the bit string within its 32-bit container. */
+ bits <<= (32 - length);
+
+ /* Append the bit string (one or more of the trailing bits might not fit, but that's ok). */
+ ctx->cur_chunk |= bits >> ctx->cur_chunk_len;
+ ctx->cur_chunk_len += length;
+
+ /* Is it full? */
+ if (ctx->cur_chunk_len >= 32) {
+
+ /* Add the full 32-bit chunk to the stream and update counter. */
+ ctx->chunk_ptr[0] = GUINT32_TO_LE(ctx->cur_chunk);
+ ctx->chunk_ptr++;
+ ctx->cur_chunk_len -= 32;
+
+ /* Add any trailing bits that didn't fit. */
+ ctx->cur_chunk = bits << (length - ctx->cur_chunk_len);
+ }
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/colorspace.c b/kopete/protocols/msn/webcam/libmimic/colorspace.c
new file mode 100644
index 00000000..620992c6
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/colorspace.c
@@ -0,0 +1,161 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 "mimic-private.h"
+
+#define RED_INDEX_1 0
+#define GREEN_INDEX_1 1
+#define BLUE_INDEX_1 2
+
+#define RED_INDEX_2 3
+#define GREEN_INDEX_2 4
+#define BLUE_INDEX_2 5
+
+/*
+ * _rgb_to_yuv
+ *
+ * Internal helper-function used to convert an image
+ * from RGB 24-bpp packed-pixel to YUV420 planar.
+ */
+void _rgb_to_yuv(const guchar *input_rgb,
+ guchar *output_y,
+ guchar *output_cb,
+ guchar *output_cr,
+ gint width,
+ gint height)
+{
+ gint y, x;
+
+ for (y = 0; y < height; y += 2) {
+
+ const guchar *src1, *src2;
+ guchar *dst1, *dst2, *dst3, *dst4;
+ gint num_cols;
+
+ src1 = input_rgb + ((height - 1 - y) * width * 3);
+ src2 = input_rgb + ((height - 2 - y) * width * 3);
+
+ dst1 = output_y + (y * width);
+ dst2 = output_y + ((y + 1) * width);
+ dst3 = output_cb + ((y / 2) * (width / 2));
+ dst4 = output_cr + ((y / 2) * (width / 2));
+
+ num_cols = width / 2;
+
+ for (x = 0; x < num_cols; x++) {
+
+ gint expr1, expr2, expr3, expr4, expr5, v;
+
+ expr1 = (src1[BLUE_INDEX_1] * 19595) + (src1[GREEN_INDEX_1] * 38470) + (src1[RED_INDEX_1] * 7471);
+ expr2 = (src1[BLUE_INDEX_2] * 19595) + (src1[GREEN_INDEX_2] * 38470) + (src1[RED_INDEX_2] * 7471);
+ expr3 = (src2[BLUE_INDEX_1] * 19595) + (src2[GREEN_INDEX_1] * 38470) + (src2[RED_INDEX_1] * 7471);
+ expr4 = (src2[BLUE_INDEX_2] * 19595) + (src2[GREEN_INDEX_2] * 38470) + (src2[RED_INDEX_2] * 7471);
+
+ expr5 = expr1 + expr2 + expr3 + expr4;
+
+ dst1[0] = expr1 >> 16;
+ dst1[1] = expr2 >> 16;
+ dst2[0] = expr3 >> 16;
+ dst2[1] = expr4 >> 16;
+
+ v = (((src1[BLUE_INDEX_1] + src1[BLUE_INDEX_2] + src2[BLUE_INDEX_1] + src2[BLUE_INDEX_2]) << 16) - expr5 + 131071) >> 16;
+ dst3[0] = _clamp_value(((v * 57475) >> 18) + 128);
+
+ v = (((src1[RED_INDEX_1] + src1[RED_INDEX_2] + src2[RED_INDEX_1] + src2[RED_INDEX_2]) << 16) - expr5 + 131071) >> 16;
+ dst4[0] = ((v * 32244) >> 18) + 128;
+
+ src1 += 6;
+ src2 += 6;
+
+ dst1 += 2;
+ dst2 += 2;
+ dst3++;
+ dst4++;
+
+ }
+
+ }
+
+}
+
+/*
+ * _yuv_to_rgb
+ *
+ * Internal helper-function used to convert an image
+ * from YUV420 planar to RGB 24-bpp packed-pixel.
+ */
+void _yuv_to_rgb(const guchar *input_y,
+ const guchar *input_cb,
+ const guchar *input_cr,
+ guchar *output_rgb,
+ guint width,
+ guint height)
+{
+ const guchar *src_y, *src_cb, *src_cr;
+ guchar *dst_rgb;
+ guint i, j, rgb_stride;
+
+ src_y = input_y;
+ src_cb = input_cb;
+ src_cr = input_cr;
+
+ rgb_stride = width * 3;
+ dst_rgb = output_rgb + (rgb_stride * (height - 1));
+
+ for (i = 0; i < height; i++) {
+ const guchar *p_y, *p_cb, *p_cr;
+ guchar *p_rgb;
+
+ p_y = src_y;
+ p_cb = src_cb;
+ p_cr = src_cr;
+
+ p_rgb = dst_rgb;
+
+ for (j = 0; j < width; j++) {
+ gint v;
+
+ v = ((p_y[0] * 65536) + ((p_cr[0] - 128) * 133169)) / 65536;
+ p_rgb[0] = _clamp_value(v);
+
+ v = ((p_y[0] * 65536) - ((p_cr[0] - 128) * 25821) - ((p_cb[0] - 128) * 38076)) / 65536;
+ p_rgb[1] = _clamp_value(v);
+
+ v = ((p_y[0] * 65536) + ((p_cb[0] - 128) * 74711)) / 65536;
+ p_rgb[2] = _clamp_value(v);
+
+ p_y++;
+ if ((j + 1) % 2 == 0) {
+ p_cb++;
+ p_cr++;
+ }
+
+ p_rgb += 3;
+ }
+
+ src_y += width;
+ if ((i + 1) % 2 == 0) {
+ src_cb += (width + 1) / 2;
+ src_cr += (width + 1) / 2;
+ }
+
+ dst_rgb -= rgb_stride;
+
+ }
+
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/deblock.c b/kopete/protocols/msn/webcam/libmimic/deblock.c
new file mode 100644
index 00000000..cfd6ff6d
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/deblock.c
@@ -0,0 +1,450 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include "mimic-private.h"
+
+static void deblock_horizontal(guchar *blocks, guint stride, guint row_count);
+static void deblock_vertical(guchar *blocks, guint stride, guint row_count);
+
+static gboolean deblock_h_consider_entire(guchar *blocks, guint stride);
+static void deblock_h_do_entire(guchar *blocks, guint stride);
+static void deblock_h_do_boundaries(guchar *blocks, guint stride);
+
+static gboolean deblock_v_consider_entire(guchar *blocks, guint stride);
+static void deblock_v_do_entire(guchar *blocks, guint stride);
+static void deblock_v_do_boundaries(guchar *blocks, guint stride);
+
+/*
+ * _deblock
+ *
+ * Internal helper-function used for de-blocking.
+ */
+void _deblock(guchar *blocks, guint stride, guint row_count)
+{
+ deblock_horizontal(blocks, stride, row_count);
+ deblock_vertical(blocks, stride, row_count);
+}
+
+static void deblock_horizontal(guchar *blocks, guint stride, guint row_count)
+{
+ guchar *p1;
+ gint i, j, n1, n2;
+
+ if (stride <= 8 || row_count == 0)
+ return;
+
+ p1 = blocks + 4;
+ n1 = ((row_count - 1) >> 2) + 1;
+ n2 = ((stride - 9) >> 3) + 1;
+
+ for (i = 0; i < n1; i++) {
+ guchar *p;
+
+ p = p1;
+
+ for (j = 0; j < n2; j++) {
+
+ if (deblock_h_consider_entire(p - 1, stride) == TRUE) {
+
+ gint v1, v2, v;
+
+ v1 = p[0];
+ v2 = p[7];
+
+ v = v1 - v2;
+ if (v <= 0)
+ v = v2 - v1;
+
+ if (v < 20)
+ deblock_h_do_entire(p - 1, stride);
+
+ } else {
+ deblock_h_do_boundaries(p - 1, stride);
+ }
+
+ p += 8;
+ }
+
+ p1 += stride * 4;
+ }
+}
+
+static void deblock_vertical(guchar *blocks, guint stride, guint row_count)
+{
+ gint i, j, k, n1, n2;
+ guchar *p1, *p2;
+
+ if (stride == 0 || row_count <= 8)
+ return;
+
+ p1 = blocks + (stride * 3);
+ p2 = blocks + (stride * 4);
+
+ n1 = ((row_count - 9) >> 3) + 1;
+ n2 = ((stride - 1) >> 3) + 1;
+
+ for (i = 0; i < n1; i++) {
+ guchar *p3, *p4;
+
+ p3 = p1;
+ p4 = p2;
+
+ for (j = 0; j < n2; j++) {
+
+ if (deblock_v_consider_entire(p3, stride) == TRUE) {
+ guchar *p5;
+ gboolean do_entire;
+
+ p5 = p3 + (stride * 8);
+ do_entire = TRUE;
+
+ for (k = 0; k < 8; k++) {
+ gint v1, v2, v;
+
+ v1 = p4[k];
+ v2 = p5[k];
+
+ v = v1 - v2;
+ if (v <= 0)
+ v = v2 - v1;
+
+ if (v > 20) {
+ do_entire = FALSE;
+ break;
+ }
+ }
+
+ if (do_entire)
+ deblock_v_do_entire(p3, stride);
+ } else {
+ deblock_v_do_boundaries(p3, stride);
+ }
+
+ p3 += 8;
+ p4 += 8;
+ }
+
+ p1 += stride * 8;
+ p2 += stride * 8;
+ }
+}
+
+static gboolean deblock_h_consider_entire(guchar *blocks, guint stride)
+{
+ guchar *p;
+ gint i, j, count;
+
+ count = 0;
+ p = blocks;
+
+ for (i = 0; i < 4; i++) {
+
+ for (j = 1; j <= 7; j++) {
+ gint v1, v2, v;
+
+ v1 = p[j];
+ v2 = p[j+1];
+
+ v = v1 - v2;
+ if (v <= 0)
+ v = v2 - v1;
+
+ if (v <= 1)
+ count--;
+ }
+
+ p += stride;
+ }
+
+ return (count <= -20);
+}
+
+static void deblock_h_do_entire(guchar *blocks, guint stride)
+{
+ guchar buf[8], *p;
+ gint i;
+
+ p = blocks;
+
+ for (i = 0; i < 4; i++) {
+ gint v, low, high;
+
+ v = p[0] - p[1];
+ if (v <= 0)
+ v = p[1] - p[0];
+
+ if (v < 10)
+ low = p[0];
+ else
+ low = p[1];
+
+ v = p[8] - p[9];
+ if (v <= 0)
+ v = p[9] - p[8];
+
+ if (v >= 10)
+ high = p[8];
+ else
+ high = p[9];
+
+ v = (low * 3) + p[1] + p[2] + p[3] + p[4] + 4;
+ buf[0] = (((p[1] + v) << 1) - p[4] + p[5]) >> 4;
+
+ v += p[5] - low;
+ buf[1] = (((p[2] + v) << 1) - p[5] + p[6]) >> 4;
+
+ v += p[6] - low;
+ buf[2] = (((p[3] + v) << 1) - p[6] + p[7]) >> 4;
+
+ v += p[7] - low;
+ buf[3] = (((p[4] + v) << 1) - p[1] - p[7] + p[8] + low) >> 4;
+
+ v += p[8] - p[1];
+ buf[4] = (((p[5] + v) << 1) + p[1] - p[2] - p[8] + high) >> 4;
+
+ v += high - p[2];
+ buf[5] = (((p[6] + v) << 1) + p[2] - p[3]) >> 4;
+
+ v += high - p[3];
+ buf[6] = (((p[7] + v) << 1) + p[3] - p[4]) >> 4;
+
+ v += high;
+ buf[7] = (((p[8] + v) << 1) - p[4] - p[5]) >> 4;
+
+ memcpy(p + 1, buf, 8);
+
+ p += stride;
+ }
+}
+
+static void deblock_h_do_boundaries(guchar *blocks, guint stride)
+{
+ guchar *p;
+ gint i;
+
+ p = blocks;
+
+ for (i = 0; i < 4; i++) {
+ gint v, v1, v2, v3;
+
+ v = p[4] - p[5];
+
+ if ((v / 2) != 0) {
+
+ v1 = ((p[3] - p[6]) * 2) - (v * 5);
+
+ if (abs(v1) < 80) {
+
+ v2 = ((p[3] - p[2]) * 5) + ((p[1] - p[4]) * 2);
+ v3 = (p[5] * 2) + (p[7] * 5) - (p[8] * 7);
+
+ v = abs(v1) - MIN(abs(v2), abs(v3));
+
+ if (v > 0) {
+
+ v = ((v * 5) + 32) >> 6;
+ if (v > 0) {
+
+ v2 = (p[4] - p[5]) / 2;
+ v3 = (((v1 < 0) * 2) - 1) * v;
+
+ if (v2 > 0)
+ v = MIN(v2, ((v3 < 0) - 1) & v3);
+ else
+ v = MAX(v2, ((v3 > 0) - 1) & v3);
+
+ p[4] -= v;
+ p[5] += v;
+ }
+ }
+ }
+ }
+
+ p += stride;
+ }
+}
+
+static gboolean deblock_v_consider_entire(guchar *blocks, guint stride)
+{
+ gint count, i, j;
+ guchar *p1, *p2;
+
+ count = 0;
+
+ p1 = blocks + stride;
+ p2 = blocks + (stride * 2);
+
+ for (i = 0; i < 7; i++) {
+
+ for (j = 0; j < 8; j++) {
+ gint v1, v2, v;
+
+ v1 = p1[j];
+ v2 = p2[j];
+
+ v = v1 - v2;
+ if (v <= 0)
+ v = v2 - v1;
+
+ if (v <= 1)
+ count++;
+ }
+
+ p1 += stride;
+ p2 += stride;
+ }
+
+ return (count > 40);
+}
+
+static void deblock_v_do_entire(guchar *blocks, guint stride)
+{
+ gint offset0, offset1, offset2, offset3;
+ gint offset4, offset5, offset6, offset7;
+ gint offset8, i;
+ guchar *p, buf[8];
+
+ offset0 = stride - (stride * 6);
+ offset1 = (stride * 2) - (stride * 6);
+ offset2 = (stride * 3) - (stride * 6);
+ offset3 = (stride * 4) - (stride * 6);
+ offset4 = (stride * 5) - (stride * 6);
+ offset5 = 0;
+ offset6 = (stride * 7) - (stride * 6);
+ offset7 = (stride * 8) - (stride * 6);
+ offset8 = (stride * 9) - (stride * 6);
+
+ p = blocks + (stride * 6);
+
+ for (i = 0; i < 8; i++) {
+ gint v, low, high;
+
+ v = blocks[i] - p[offset0];
+ if (v <= 0)
+ v = p[offset0] - blocks[i];
+
+ if (v < 10)
+ low = blocks[i];
+ else
+ low = p[offset0];
+
+ v = p[offset7] - p[offset8];
+ if (v <= 0)
+ v = p[offset8] - p[offset7];
+
+ if (v < 10)
+ high = p[offset8];
+ else
+ high = p[offset7];
+
+ v = p[offset0] + (low * 3) + p[offset1] + p[offset2] + p[offset3] + 4;
+
+ buf[0] = (((p[offset0] + v) << 1) - p[offset3] + p[offset4]) >> 4;
+
+ v += p[offset4] - low;
+
+ buf[1] = (((p[offset1] + v) << 1) - p[offset4] + p[0]) >> 4;
+
+ v += p[0] - low;
+
+ buf[2] = (((p[offset2] + v) << 1) - p[0] + p[offset6]) >> 4;
+
+ v += p[offset6] - low;
+
+ buf[3] = (((p[offset3] + v) << 1) - p[offset0] - p[offset6] + p[offset7] + low) >> 4;
+
+ v += p[offset7] - p[offset0];
+
+ buf[4] = (((p[offset4] + v) << 1) - p[offset7] - p[offset1] + p[offset0] + high) >> 4;
+
+ v += high - p[offset1];
+
+ buf[5] = (((p[0] + v) << 1) - p[offset2] + p[offset1]) >> 4;
+
+ v += high - p[offset2];
+
+ buf[6] = (((p[offset6] + v) << 1) - p[offset3] + p[offset2]) >> 4;
+
+ v += high;
+
+ buf[7] = (((p[offset7] + v) << 1) - p[offset4] - p[offset3]) >> 4;
+
+ p[offset0] = buf[0];
+ p[offset1] = buf[1];
+ p[offset2] = buf[2];
+ p[offset3] = buf[3];
+ p[offset4] = buf[4];
+ p[offset5] = buf[5];
+ p[offset6] = buf[6];
+ p[offset7] = buf[7];
+
+ p++;
+ }
+}
+
+static void deblock_v_do_boundaries(guchar *blocks, guint stride)
+{
+ guchar *p;
+ gint offset0, offset1, offset2, offset3;
+ gint offset4, offset5, offset6, offset7;
+ gint i;
+
+ p = blocks + (stride * 3);
+
+ offset0 = stride - (stride * 3);
+ offset1 = (stride * 2) - (stride * 3);
+ offset2 = 0;
+ offset3 = (stride * 4) - (stride * 3);
+ offset4 = (stride * 5) - (stride * 3);
+ offset5 = (stride * 6) - (stride * 3);
+ offset6 = (stride * 7) - (stride * 3);
+ offset7 = (stride * 8) - (stride * 3);
+
+ for (i = 0; i < 8; i++) {
+ gint v1, v2, v3, v;
+
+ v1 = ((p[offset4] - p[offset3]) * 5) + ((p[offset2] - p[offset5]) * 2);
+
+ if (abs(v1) < 80) {
+
+ v2 = ((p[offset2] - p[offset1]) * 5) + ((p[offset0] - p[offset3]) * 2);
+ v3 = ((p[offset6] - p[offset5]) * 5) + ((p[offset4] - p[offset7]) * 2);
+
+ v = abs(v1) - MIN(abs(v2), abs(v3));
+ if (v < 0)
+ v = 0;
+
+ v2 = (p[offset3] - p[offset4]) / 2;
+ v3 = (((v * 5) + 32) >> 6) * (((v1 < 0) * 2) - 1);
+
+ if (v2 > 0)
+ v = MIN(v2, ((v3 < 0) - 1) & v3);
+ else
+ v = MAX(v2, ((v3 > 0) - 1) & v3);
+ } else {
+ v = 0;
+ }
+
+ p[offset3] -= v;
+ p[offset4] += v;
+
+ p++;
+ }
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/decode.c b/kopete/protocols/msn/webcam/libmimic/decode.c
new file mode 100644
index 00000000..2bc0e6c9
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/decode.c
@@ -0,0 +1,311 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <string.h>
+#include "mimic-private.h"
+
+
+static gboolean decode(MimCtx *ctx, gboolean is_pframe);
+
+/**
+ * Decode a MIMIC-encoded frame into RGB data.
+ *
+ * @param ctx the mimic context
+ * @param input_buffer buffer containing the MIMIC-encoded frame to decode
+ * @param output_buffer buffer that will receive the decoded frame in RGB 24-bpp packed pixel top-down format
+ * (use #mimic_get_property to determine the required buffer size, as well as frame width and height)
+ * @returns #TRUE on success
+ */
+gboolean mimic_decode_frame(MimCtx *ctx,
+ const guchar *input_buffer,
+ guchar *output_buffer)
+{
+ gboolean result, is_pframe;
+ guchar *input_y, *input_cr, *input_cb;
+ gint width, height;
+
+ /*
+ * Some sanity checks.
+ */
+ if (ctx == NULL || input_buffer == NULL || output_buffer == NULL)
+ {
+ return FALSE;
+ }
+
+ if (!ctx->decoder_initialized)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Get frame dimensions.
+ */
+ width = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 4)));
+ height = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 6)));
+
+ /*
+ * Resolution changing is not supported.
+ */
+ if (width != ctx->frame_width ||
+ height != ctx->frame_height)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Increment frame counter.
+ */
+ ctx->frame_num++;
+
+ /*
+ * Initialize state.
+ */
+ ctx->quality = GUINT16_FROM_LE(*((guint16 *) (input_buffer + 2)));
+ is_pframe = GUINT32_FROM_LE(*((guint32 *) (input_buffer + 12)));
+ ctx->num_coeffs = input_buffer[16];
+
+ ctx->data_buffer = (gchar *) (input_buffer + 20);
+ ctx->data_index = 0;
+ ctx->cur_chunk_len = 16;
+ ctx->read_odd = FALSE;
+
+ /*
+ * Decode frame.
+ */
+ if (!(is_pframe && ctx->prev_frame_buf == NULL))
+ result = decode(ctx, is_pframe);
+ else
+ {
+ result = FALSE;
+ }
+
+ /*
+ * Perform YUV 420 to RGB conversion.
+ */
+ input_y = ctx->cur_frame_buf;
+ input_cr = ctx->cur_frame_buf + ctx->y_size;
+ input_cb = ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size;
+
+ _yuv_to_rgb(input_y,
+ input_cb,
+ input_cr,
+ output_buffer,
+ ctx->frame_width,
+ ctx->frame_height);
+
+ return result;
+}
+
+/*
+ * decode_main
+ *
+ * Main decoding loop.
+ */
+static gboolean decode(MimCtx *ctx, gboolean is_pframe)
+{
+ gint y, x, i, j, chrom_ch, *bptr, base_offset, offset;
+ gint dct_block[64];
+ guchar *src, *dst, *p;
+ guint32 bit;
+
+ /*
+ * Clear Cr and Cb planes.
+ */
+ p = ctx->cur_frame_buf + ctx->y_size;
+ memset(p, 128, 2 * ctx->crcb_size);
+
+ /*
+ * Decode Y plane.
+ */
+ for (y = 0; y < ctx->num_vblocks_y; y++) {
+
+ base_offset = ctx->y_stride * 8 * y;
+
+ src = ctx->prev_frame_buf + base_offset;
+ dst = ctx->cur_frame_buf + base_offset;
+
+ for (x = 0; x < ctx->num_hblocks_y; x++) {
+
+ /* Check for a change condition in the current block. */
+
+ if (is_pframe)
+ bit = _read_bits(ctx, 1);
+ else
+ bit = 0;
+
+ if (bit == 0) {
+
+ /* Yes: Is the new content the same as it was in one of
+ * the 15 last frames preceding the previous? */
+
+ if (is_pframe)
+ bit = _read_bits(ctx, 1);
+
+ if (bit == 0) {
+
+ /* No: decode it. */
+
+ if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) {
+
+ return FALSE;
+ }
+
+ _idct_dequant_block(ctx, dct_block, 0);
+
+ bptr = dct_block;
+ for (i = 0; i < 8; i++) {
+ offset = ctx->y_stride * i;
+
+ for (j = 0; j < 8; j++) {
+ guint v;
+
+ if (bptr[j] <= 255)
+ v = (bptr[j] >= 0) ? bptr[j] : 0;
+ else
+ v = 255;
+
+ *(dst + offset + j) = v;
+ }
+
+ bptr += 8;
+ }
+ } else {
+ guint32 backref;
+
+ /* Yes: read the backreference (4 bits) and copy. */
+
+ backref = _read_bits(ctx, 4);
+
+ p = ctx->buf_ptrs[(ctx->ptr_index + backref) % 16];
+ p += base_offset + (x * 8);
+
+ for (i = 0; i < 8; i++) {
+ offset = ctx->y_stride * i;
+
+ memcpy(dst + offset, p + offset, 8);
+ }
+ }
+ } else {
+
+ /* No change no worries: just copy from the previous frame. */
+
+ for (i = 0; i < 8; i++) {
+ offset = ctx->y_stride * i;
+
+ memcpy(dst + offset, src + offset, 8);
+ }
+ }
+
+ src += 8;
+ dst += 8;
+ }
+ }
+
+ /*
+ * Decode Cr and Cb planes.
+ */
+ for (chrom_ch = 0; chrom_ch < 2; chrom_ch++) {
+
+ base_offset = ctx->y_size + (ctx->crcb_size * chrom_ch);
+
+ for (y = 0; y < ctx->num_vblocks_cbcr; y++) {
+ guint num_rows = 8;
+
+ /* The last row of blocks in chrominance for 160x120 resolution
+ * is half the normal height and must be accounted for. */
+ if (y + 1 == ctx->num_vblocks_cbcr && ctx->frame_height % 16 != 0)
+ num_rows = 4;
+
+ offset = base_offset + (ctx->crcb_stride * 8 * y);
+
+ src = ctx->prev_frame_buf + offset;
+ dst = ctx->cur_frame_buf + offset;
+
+ for (x = 0; x < ctx->num_hblocks_cbcr; x++) {
+
+ /* Check for a change condition in the current block. */
+
+ if (is_pframe)
+ bit = _read_bits(ctx, 1);
+ else
+ bit = 1;
+
+ if (bit == 1) {
+
+ /* Yes: decode it. */
+
+ if (_vlc_decode_block(ctx, dct_block, ctx->num_coeffs) == FALSE) {
+
+ /* Corrupted frame: clear Cr and Cb planes and return. */
+ p = ctx->cur_frame_buf + ctx->y_size;
+ memset(p, 128, ctx->crcb_size * 2);
+
+ return FALSE;
+ }
+
+ _idct_dequant_block(ctx, dct_block, 1);
+
+ for (i = 0; i < num_rows; i++) {
+ p = dst + (ctx->crcb_stride * i);
+
+ for (j = 0; j < 8; j++)
+ p[j] = dct_block[(i * 8) + j];
+ }
+
+ } else {
+
+ /* No change no worries: just copy from the previous frame. */
+
+ for (i = 0; i < num_rows; i++) {
+ offset = ctx->crcb_stride * i;
+
+ memcpy(dst + offset, src + offset, 8);
+ }
+ }
+
+ src += 8;
+ dst += 8;
+ }
+ }
+ }
+
+ /*
+ * Make a copy of the current frame and store in
+ * the circular pointer list of 16 entries.
+ */
+ ctx->prev_frame_buf = ctx->buf_ptrs[ctx->ptr_index];
+ memcpy(ctx->prev_frame_buf, ctx->cur_frame_buf,
+ ctx->y_size + (ctx->crcb_size * 2));
+
+ if (--ctx->ptr_index < 0)
+ ctx->ptr_index = 15;
+
+ /*
+ * Perform deblocking on all planes.
+ */
+ _deblock(ctx->cur_frame_buf,
+ ctx->y_stride, ctx->y_row_count);
+
+ _deblock(ctx->cur_frame_buf + ctx->y_size,
+ ctx->crcb_stride, ctx->crcb_row_count);
+
+ _deblock(ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size,
+ ctx->crcb_stride, ctx->crcb_row_count);
+
+ return TRUE;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/encode.c b/kopete/protocols/msn/webcam/libmimic/encode.c
new file mode 100644
index 00000000..909ebd80
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/encode.c
@@ -0,0 +1,419 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "mimic-private.h"
+
+#define LUMINANCE_THRESHOLD 32.0f
+#define CHROMINANCE_THRESHOLD 36.0f
+
+static void encode_main(MimCtx *ctx, guchar *out_buf, gboolean is_pframe);
+
+/**
+ * Encode a MIMIC-encoded frame from RGB data.
+ *
+ * @param ctx the mimic context
+ * @param input_buffer buffer containing pixeldata in RGB 24-bpp packed pixel top-down format
+ * @param output_buffer buffer that will receive the MIMIC-encoded frame
+ * (use #mimic_get_property to determine the required buffer size)
+ * @param output_length pointer to an integer that receives the length of the encoded data
+ * written to output_buffer
+ * @param make_keyframe whether the encoder should make this frame a keyframe
+ * @returns #TRUE on success
+ */
+gboolean mimic_encode_frame(MimCtx *ctx,
+ const guchar *input_buffer,
+ guchar *output_buffer,
+ gint *output_length,
+ gboolean make_keyframe)
+{
+ guchar *output_y, *output_cb, *output_cr;
+
+ /*
+ * Some sanity checks.
+ */
+ if (ctx == NULL || input_buffer == NULL ||
+ output_buffer == NULL || output_length == NULL)
+ {
+ return FALSE;
+ }
+
+ if (!ctx->encoder_initialized)
+ return FALSE;
+
+ /*
+ * Initialize state.
+ */
+ ctx->chunk_ptr = (guint32 *) (output_buffer + 20);
+ ctx->cur_chunk = 0;
+ ctx->cur_chunk_len = 0;
+
+ if (ctx->frame_num == 0)
+ make_keyframe = TRUE;
+
+ /*
+ * Write header.
+ */
+ memset(output_buffer, 0, 20);
+ *((guint16 *) (output_buffer + 0)) = GUINT16_TO_LE(256);
+ *((guint16 *) (output_buffer + 2)) = GUINT16_TO_LE(ctx->quality);
+ *((guint16 *) (output_buffer + 4)) = GUINT16_TO_LE(ctx->frame_width);
+ *((guint16 *) (output_buffer + 6)) = GUINT16_TO_LE(ctx->frame_height);
+ *((guint32 *) (output_buffer + 12)) = GUINT32_TO_LE((make_keyframe == 0));
+ *(output_buffer + 16) = ctx->num_coeffs;
+ *(output_buffer + 17) = 0;
+
+ /*
+ * Perform RGB to YUV 420 conversion.
+ */
+ output_y = ctx->cur_frame_buf;
+ output_cr = ctx->cur_frame_buf + ctx->y_size;
+ output_cb = ctx->cur_frame_buf + ctx->y_size + ctx->crcb_size;
+
+ _rgb_to_yuv(input_buffer,
+ output_y,
+ output_cb,
+ output_cr,
+ ctx->frame_width,
+ ctx->frame_height);
+
+ /*
+ * Encode frame.
+ */
+ encode_main(ctx, output_buffer, (make_keyframe == FALSE));
+
+ /*
+ * Write out any pending bits to stream by zero-padding with 32 bits.
+ */
+ _write_bits(ctx, 0, 32);
+
+ /*
+ * Calculate bytes written.
+ */
+ *output_length = (guchar *) ctx->chunk_ptr - output_buffer;
+
+ /*
+ * Increment frame counter.
+ */
+ ctx->frame_num++;
+
+ return TRUE;
+}
+
+static gdouble compare_blocks(const guchar *p1,
+ const guchar *p2,
+ gint stride,
+ gint row_count,
+ gboolean is_chrom);
+
+/*
+ * encode_main
+ *
+ * Main encoding loop.
+ */
+static void encode_main(MimCtx *ctx, guchar *out_buf, gboolean is_pframe)
+{
+ gint x, y, i, offset, chrom_ch;
+ gint dct_block[64];
+ guchar *src, *dst, *p1, *p2;
+ gdouble match;
+ gboolean encoded;
+
+ /*
+ * Round down small differences in luminance channel.
+ */
+ if (is_pframe) {
+
+ p1 = ctx->cur_frame_buf;
+ p2 = ctx->prev_frame_buf;
+
+ for (i = 0; i < ctx->y_size; i++) {
+
+ if (abs(p2[0] - p1[0]) < 7)
+ p1[0] = p2[0];
+
+ p1++;
+ p2++;
+ }
+ }
+
+ /*
+ * Encode Y plane.
+ */
+ for (y = 0; y < ctx->num_vblocks_y; y++) {
+
+ for (x = 0; x < ctx->num_hblocks_y; x++) {
+
+ /* Calculate final offset into buffer. */
+ offset = (ctx->y_stride * 8 * y) + (x * 8);
+
+ src = NULL;
+ encoded = FALSE;
+
+ if (is_pframe) {
+
+ /* Is the current block similar enough to what it was in the previous frame? */
+
+ match = compare_blocks(ctx->cur_frame_buf + offset,
+ ctx->prev_frame_buf + offset,
+ ctx->y_stride, 8,
+ FALSE);
+
+ if (match > LUMINANCE_THRESHOLD) {
+
+ /* Yes: write out '1' to indicate a no-change condition. */
+
+ _write_bits(ctx, 1, 1);
+
+ src = ctx->prev_frame_buf + offset;
+ encoded = TRUE;
+
+ } else {
+
+ /* No: Is the current block similar enough to what it was in one
+ * of the (up to) 15 last frames preceding the previous? */
+
+ gint best_index = 0;
+ gdouble best_match = 0.0;
+
+ gint num_backrefs = ctx->frame_num - 1;
+ if (num_backrefs > 15)
+ num_backrefs = 15;
+
+ for (i = 1; i <= num_backrefs; i++) {
+
+ match = compare_blocks(ctx->buf_ptrs[(ctx->ptr_index + i) % 16] + offset,
+ ctx->cur_frame_buf + offset,
+ ctx->y_stride, 8,
+ FALSE);
+
+ if (match > LUMINANCE_THRESHOLD && match > best_match) {
+ best_index = i;
+ best_match = match;
+ }
+
+ }
+
+ if (best_index != 0) {
+
+ /* Yes: write out '01' to indicate a "change but like previous"-condition,
+ * followed by 4 bits containing the back-reference. */
+ _write_bits(ctx, 0, 1);
+ _write_bits(ctx, 1, 1);
+ _write_bits(ctx, best_index, 4);
+
+ src = ctx->buf_ptrs[(ctx->ptr_index + best_index) % 16] + offset;
+ encoded = TRUE;
+
+ }
+ }
+ }
+
+ if (!encoded) {
+
+ /* Keyframe or in any case no? ;-) Well, encode it then. */
+
+ if (is_pframe) {
+ _write_bits(ctx, 0, 1);
+ _write_bits(ctx, 0, 1);
+ }
+
+ _fdct_quant_block(ctx,
+ dct_block,
+ ctx->cur_frame_buf + offset,
+ ctx->y_stride,
+ FALSE,
+ ctx->num_coeffs);
+
+ _vlc_encode_block(ctx,
+ dct_block,
+ ctx->num_coeffs);
+
+ }
+
+ /* And if there was some kind of no-change condition,
+ * we want to copy the previous block. */
+ if (src != NULL) {
+
+ dst = ctx->cur_frame_buf + offset;
+ for (i = 0; i < 8; i++) {
+
+ memcpy(dst, src, 8);
+
+ src += ctx->y_stride;
+ dst += ctx->y_stride;
+ }
+
+ }
+
+ }
+
+ }
+
+ /*
+ * Encode Cr and Cb planes.
+ */
+ for (chrom_ch = 0; chrom_ch < 2; chrom_ch++) {
+
+ /* Calculate base offset into buffer. */
+ gint base_offset = ctx->y_size + (ctx->crcb_size * chrom_ch);
+
+ for (y = 0; y < ctx->num_vblocks_cbcr; y++) {
+ guchar tmp_block[64];
+ guint num_rows = 8;
+
+ /* The last row of blocks in chrominance for 160x120 resolution
+ * is half the normal height and must be accounted for. */
+ if (y + 1 == ctx->num_vblocks_cbcr && ctx->frame_height % 16 != 0)
+ num_rows = 4;
+
+ for (x = 0; x < ctx->num_hblocks_cbcr; x++) {
+
+ /* Calculate final offset into buffer. */
+ offset = base_offset + (ctx->crcb_stride * 8 * y) + (x * 8);
+
+ src = NULL;
+ encoded = FALSE;
+
+ if (is_pframe) {
+
+ /* Is the current block similar enough to what it was in the previous frame? */
+
+ match = compare_blocks(ctx->prev_frame_buf + offset,
+ ctx->cur_frame_buf + offset,
+ ctx->crcb_stride, num_rows,
+ TRUE);
+
+ if (match > CHROMINANCE_THRESHOLD) {
+
+ /* Yes: write out '0' to indicate a no-change condition. */
+
+ _write_bits(ctx, 0, 1);
+
+ encoded = TRUE;
+
+ src = ctx->prev_frame_buf + offset;
+ dst = ctx->cur_frame_buf + offset;
+ for (i = 0; i < num_rows; i++) {
+
+ memcpy(dst, src, 8);
+
+ src += ctx->crcb_stride;
+ dst += ctx->crcb_stride;
+ }
+ }
+
+ }
+
+ if (!encoded) {
+
+ /* Keyframe or just not similar enough? ;-) Well, encode it then. */
+
+ if (is_pframe)
+ _write_bits(ctx, 1, 1);
+
+ /* Use a temporary array to handle cases where the
+ * current block is not of normal height (see above). */
+ src = ctx->cur_frame_buf + offset;
+ dst = tmp_block;
+ for (i = 0; i < 8; i++) {
+
+ memcpy(dst, src, 8);
+
+ if (i < (num_rows - 1))
+ src += ctx->crcb_stride;
+ dst += 8;
+ }
+
+ _fdct_quant_block(ctx,
+ dct_block,
+ tmp_block,
+ 8,
+ TRUE,
+ ctx->num_coeffs);
+
+ _vlc_encode_block(ctx,
+ dct_block,
+ ctx->num_coeffs);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ /*
+ * Make a copy of the current frame and store in
+ * the circular pointer list of 16 entries.
+ */
+ ctx->prev_frame_buf = ctx->buf_ptrs[ctx->ptr_index];
+ memcpy(ctx->prev_frame_buf, ctx->cur_frame_buf,
+ ctx->y_size + (ctx->crcb_size * 2));
+
+ if (--ctx->ptr_index < 0)
+ ctx->ptr_index = 15;
+}
+
+/*
+ * compare_blocks
+ *
+ * Helper-function used to compare two blocks and
+ * determine how similar they are.
+ */
+static gdouble compare_blocks(const guchar *p1,
+ const guchar *p2,
+ gint stride,
+ gint row_count,
+ gboolean is_chrom)
+{
+ gint i, j, sum;
+ gdouble d;
+
+ sum = 0;
+
+ for (i = 0; i < row_count; i++) {
+
+ for (j = 0; j < 8; j++) {
+
+ gint d = p2[j] - p1[j];
+
+ sum += d * d;
+ }
+
+ p1 += stride;
+ p2 += stride;
+ }
+
+ if (is_chrom) {
+ if (row_count == 8)
+ d = sum * 0.015625;
+ else
+ d = sum * 0.03125;
+ } else {
+ d = sum / 64;
+ }
+
+ if (d == 0.0f)
+ return 100.0f;
+ else
+ return (10.0f * log(65025.0f / d)) / G_LN10;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/fdct_quant.c b/kopete/protocols/msn/webcam/libmimic/fdct_quant.c
new file mode 100644
index 00000000..7e8d0bdd
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/fdct_quant.c
@@ -0,0 +1,181 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 "mimic-private.h"
+
+extern guchar _col_zag[64];
+
+void _fdct_quant_block(MimCtx *ctx, gint *block, const guchar *src,
+ gint stride, gboolean is_chrom, gint num_coeffs)
+{
+ gint sum1, sum2, sum3, sum4;
+ gint diff1, diff2, diff3, diff4;
+ gint ex1, ex2, ex3, ex4, ex5;
+ gint i, j;
+ const guchar *p1;
+ gint *iptr;
+
+ /*
+ * Forward DCT, first pass (horizontal).
+ */
+ p1 = src;
+ iptr = block;
+
+ for (i = 0; i < 8; i++) {
+ sum1 = p1[0] + p1[7];
+ sum2 = p1[1] + p1[6];
+ sum3 = p1[2] + p1[5];
+ sum4 = p1[3] + p1[4];
+
+ diff1 = p1[0] - p1[7];
+ diff2 = p1[1] - p1[6];
+ diff3 = p1[2] - p1[5];
+ diff4 = p1[3] - p1[4];
+
+ ex1 = ((diff1 + diff4) * 851) - (diff1 * 282);
+ ex2 = ((diff2 + diff3) * 1004) - (diff2 * 804);
+ ex3 = ((diff2 + diff3) * 1004) - (diff3 * 1204);
+ ex4 = ((diff1 + diff4) * 851) - (diff4 * 1420);
+
+ iptr[0] = sum1 + sum2 + sum3 + sum4;
+ iptr[2] = (((sum1 - sum4) * 1337) + ((sum2 - sum3) * 554)) >> 10;
+ iptr[4] = sum1 - sum2 - sum3 + sum4;
+
+ iptr[1] = (ex1 + ex2 + ex3 + ex4) >> 10;
+ iptr[3] = ((ex4 - ex2) * 181) >> 17;
+ iptr[5] = ((ex1 - ex3) * 181) >> 17;
+
+ p1 += stride;
+ iptr += 8;
+ }
+
+ p1 = src;
+ iptr = block;
+
+ /*
+ * Forward DCT, first pass (vertical).
+ *
+ * This is only known to be correct for i == 0, though it seems to be ...
+ */
+ for (i = 0; i < 6; i++) {
+ sum1 = iptr[ 0 + i] + iptr[56 + i];
+ sum2 = iptr[ 8 + i] + iptr[48 + i];
+ sum3 = iptr[16 + i] + iptr[40 + i];
+ sum4 = iptr[24 + i] + iptr[32 + i];
+
+ diff1 = iptr[ 0 + i] - iptr[56 + i];
+ diff2 = iptr[ 8 + i] - iptr[48 + i];
+ diff3 = iptr[16 + i] - iptr[40 + i];
+ diff4 = iptr[24 + i] - iptr[32 + i];
+
+ ex1 = ((diff1 + diff4) * 851) - (diff1 * 282);
+ ex2 = ((diff2 + diff3) * 1004) - (diff2 * 804);
+ ex3 = ((diff2 + diff3) * 1004) - (diff3 * 1204);
+ ex4 = ((diff1 + diff4) * 851) - (diff4 * 1420);
+
+ ex5 = (sum1 + sum2 - sum3 - sum4) * 554;
+
+ for (j = 0; j < 7 - i; j++) {
+ switch (j) {
+
+ case 0:
+ iptr[ 0 + i] = (16 + sum1 + sum2 + sum3 + sum4) >> 5;
+ break;
+
+ case 1:
+ iptr[ 8 + i] = (16384 + ex1 + ex2 + ex3 + ex4) >> 15;
+ break;
+
+ case 2:
+ iptr[16 + i] = (16384 + ((sum1 - sum4) * 783) + ex5) >> 15;
+ break;
+
+ case 3:
+ iptr[24 + i] = (8192 + (((ex4 - ex2) >> 8) * 181)) >> 14;
+ break;
+
+ case 4:
+ iptr[32 + i] = (16 + sum1 - sum2 - sum3 + sum4) >> 5;
+ break;
+
+ case 5:
+ iptr[40 + i] = (8192 + (((ex1 - ex3) >> 8) * 181)) >> 14;
+ break;
+
+ case 6:
+ iptr[48 + i] = (16384 - ((sum2 - sum3) * 1891) + ex5) >> 15;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Quantize.
+ */
+ block[0] /= 2;
+ block[8] /= 4;
+ block[1] /= 4;
+ block[6] = 0;
+
+ if (num_coeffs > 3) {
+
+ gdouble s = (10000 - ctx->quality) * 10.0 * (gfloat) 9.9999997e-5;
+
+ if (s > 10.0)
+ s = 10.0;
+ else if (is_chrom != 0 && s < 1.0)
+ s = 1.0;
+ else if (s < 2.0)
+ s = 2.0;
+
+ s = 1.0 / s;
+
+ for (i = 3; i < num_coeffs; i++) {
+
+ gdouble coeff, r;
+
+ coeff = block[_col_zag[i]] * s;
+ r = coeff - (gint) coeff;
+
+ if (r >= 0.6)
+ block[_col_zag[i]] = (gint) (coeff + 1.0);
+ else if (r <= -0.6)
+ block[_col_zag[i]] = (gint) (coeff - 1.0);
+ else
+ block[_col_zag[i]] = (gint) coeff;
+
+ if (block[_col_zag[i]] > 120)
+ block[_col_zag[i]] = 120;
+ else if (block[_col_zag[i]] < -120)
+ block[_col_zag[i]] = -120;
+ }
+ }
+
+ if (block[8] > 120)
+ block[8] = 120;
+ else if (block[8] < -120)
+ block[8] = -120;
+
+ if (block[1] > 120)
+ block[1] = 120;
+ else if (block[1] < -120)
+ block[1] = -120;
+
+ for (i = num_coeffs; i < 64; i++)
+ block[_col_zag[i]] = 0;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/idct_dequant.c b/kopete/protocols/msn/webcam/libmimic/idct_dequant.c
new file mode 100644
index 00000000..e5d64fb4
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/idct_dequant.c
@@ -0,0 +1,134 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 "mimic-private.h"
+
+void _idct_dequant_block(MimCtx *ctx, gint *block, gboolean is_chrom)
+{
+ gdouble f;
+ gint i, *p;
+
+ /*
+ * De-quantize.
+ */
+ f = (10000 - ctx->quality) * 10.0 * (gfloat) 9.9999997e-5;
+
+ if (f > 10.0)
+ f = 10.0;
+
+ if (!is_chrom) {
+ if (f < 2.0)
+ f = 2.0;
+ } else {
+ if (f < 1.0)
+ f = 1.0;
+ }
+
+ block[0] <<= 1;
+ block[1] <<= 2;
+ block[8] <<= 2;
+
+ for (i = 2; i < 64; i++) {
+ if (i == 8)
+ continue;
+
+ block[i] *= f;
+ }
+
+ /*
+ * Inverse DCT, first pass (horizontal).
+ */
+ p = block;
+
+ for (i = 0; i < 8; i++) {
+ gint v1, v2, v3, v4, v5, v6, v7, v8;
+ gint va, vb;
+
+ va = (p[0] << 11) + (p[4] << 11);
+ vb = ((p[2] << 2) * 392) + (((p[2] << 2) + (p[6] << 2)) * 277);
+ v1 = va + vb + 512;
+ v2 = va - vb + 512;
+
+ va = (p[0] << 11) - (p[4] << 11);
+ vb = (((p[2] << 2) + (p[6] << 2)) * 277) - ((p[6] << 2) * 946);
+ v3 = va + vb + 512;
+ v4 = va - vb + 512;
+
+ va = (p[1] << 9) + (p[3] * 724) + (p[7] << 9);
+ vb = (p[1] << 9) + (p[5] * 724) - (p[7] << 9);
+ v5 = (((va + vb) * 213) - (vb * 71)) >> 6;
+ v6 = (((va + vb) * 213) - (va * 355)) >> 6;
+
+ va = (p[1] << 9) - (p[3] * 724) + (p[7] << 9);
+ vb = (p[1] << 9) - (p[5] * 724) - (p[7] << 9);
+ v7 = (((va + vb) * 251) - (va * 201)) >> 6;
+ v8 = (((va + vb) * 251) - (vb * 301)) >> 6;
+
+ p[0] = (v1 + v5) >> 10;
+ p[1] = (v3 + v7) >> 10;
+ p[2] = (v4 + v8) >> 10;
+ p[3] = (v2 + v6) >> 10;
+ p[4] = (v2 - v6) >> 10;
+ p[5] = (v4 - v8) >> 10;
+ p[6] = (v3 - v7) >> 10;
+ p[7] = (v1 - v5) >> 10;
+
+ p += 8;
+ }
+
+ /*
+ * Inverse dct, second pass (vertical).
+ */
+ p = block;
+
+ for (i = 0; i < 8; i++) {
+ gint v1, v2, v3, v4, v5, v6, v7, v8;
+ gint va, vb;
+
+ va = (p[0] << 9) + (p[32] << 9);
+ vb = ((p[16] + p[48]) * 277) + (p[16] * 392);
+ v1 = va + vb + 1024;
+ v2 = va - vb + 1024;
+
+ va = (p[0] << 9) - (p[32] << 9);
+ vb = ((p[16] + p[48]) * 277) - (p[48] * 946);
+ v3 = va + vb + 1024;
+ v4 = va - vb + 1024;
+
+ va = ((p[8] << 7) + (p[24] * 181) + (p[56] << 7)) >> 6;
+ vb = ((p[8] << 7) + (p[40] * 181) - (p[56] << 7)) >> 6;
+ v5 = ((va + vb) * 213) - (vb * 71);
+ v6 = ((va + vb) * 213) - (va * 355);
+
+ va = ((p[8] << 7) - (p[24] * 181) + (p[56] << 7)) >> 6;
+ vb = ((p[8] << 7) - (p[40] * 181) - (p[56] << 7)) >> 6;
+ v7 = ((va + vb) * 251) - (va * 201);
+ v8 = ((va + vb) * 251) - (vb * 301);
+
+ p[0] = (v1 + v5) >> 11;
+ p[8] = (v3 + v7) >> 11;
+ p[16] = (v4 + v8) >> 11;
+ p[24] = (v2 + v6) >> 11;
+ p[32] = (v2 - v6) >> 11;
+ p[40] = (v4 - v8) >> 11;
+ p[48] = (v3 - v7) >> 11;
+ p[56] = (v1 - v5) >> 11;
+
+ p++;
+ }
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/mimic-private.h b/kopete/protocols/msn/webcam/libmimic/mimic-private.h
new file mode 100644
index 00000000..d33c50b7
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/mimic-private.h
@@ -0,0 +1,117 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 MIMIC_PRIVATE_H
+#define MIMIC_PRIVATE_H
+
+#include "mimic.h"
+
+#define ENCODER_BUFFER_SIZE 16384
+#define ENCODER_QUALITY_DEFAULT 0
+#define ENCODER_QUALITY_MIN 0
+#define ENCODER_QUALITY_MAX 10000
+
+struct _MimCtx {
+ gboolean encoder_initialized;
+ gboolean decoder_initialized;
+
+ gint frame_width;
+ gint frame_height;
+ gint quality;
+ gint num_coeffs;
+
+ gint y_stride;
+ gint y_row_count;
+ gint y_size;
+
+ gint crcb_stride;
+ gint crcb_row_count;
+ gint crcb_size;
+
+ gint num_vblocks_y;
+ gint num_hblocks_y;
+
+ gint num_vblocks_cbcr;
+ gint num_hblocks_cbcr;
+
+ guchar *cur_frame_buf;
+ guchar *prev_frame_buf;
+
+ gint8 vlcdec_lookup[2296];
+
+ gchar *data_buffer;
+ guint data_index;
+
+ guint32 cur_chunk;
+ gint cur_chunk_len;
+
+ guint32 *chunk_ptr;
+ gboolean read_odd;
+
+ gint frame_num;
+
+ gint ptr_index;
+ guchar *buf_ptrs[16];
+};
+
+typedef struct {
+ guchar length1;
+ guint32 part1;
+
+ guchar length2;
+ guint32 part2;
+} VlcSymbol;
+
+typedef struct {
+ guint32 magic;
+ guchar pos_add;
+ guchar num_bits;
+} VlcMagic;
+
+void _mimic_init(MimCtx *ctx, gint width, gint height);
+guchar _clamp_value(gint value);
+
+guint32 _read_bits(MimCtx *ctx, gint num_bits);
+void _write_bits(MimCtx *ctx, guint32 bits, gint length);
+
+void _vlc_encode_block(MimCtx *ctx, const gint *block, gint num_coeffs);
+gboolean _vlc_decode_block(MimCtx *ctx, gint *block, gint num_coeffs);
+
+void _fdct_quant_block(MimCtx *ctx, gint *block, const guchar *src,
+ gint stride, gboolean is_chrom, gint num_coeffs);
+void _idct_dequant_block(MimCtx *ctx, gint *block, gboolean is_chrom);
+
+VlcMagic *_find_magic(guint magic);
+void _initialize_vlcdec_lookup(gint8 *lookup_tbl);
+
+void _rgb_to_yuv(const guchar *input_rgb,
+ guchar *output_y,
+ guchar *output_cb,
+ guchar *output_cr,
+ gint width,
+ gint height);
+void _yuv_to_rgb(const guchar *input_y,
+ const guchar *input_cb,
+ const guchar *input_cr,
+ guchar *output_rgb,
+ guint width,
+ guint height);
+
+void _deblock(guchar *blocks, guint stride, guint row_count);
+
+#endif
+
diff --git a/kopete/protocols/msn/webcam/libmimic/mimic.c b/kopete/protocols/msn/webcam/libmimic/mimic.c
new file mode 100644
index 00000000..95564755
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/mimic.c
@@ -0,0 +1,334 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <string.h>
+#include "mimic-private.h"
+
+/**
+ * Creates a new instance and returns a pointer to the new context
+ * that can be used for either encoding or decoding by calling
+ * #mimic_encoder_init or #mimic_decoder_init.
+ *
+ * #mimic_close is called to free any resources associated with
+ * the context once done.
+ *
+ * @returns a new mimic context
+ */
+MimCtx *mimic_open()
+{
+ MimCtx *ctx;
+
+ ctx = g_new0(MimCtx, 1);
+
+ ctx->encoder_initialized = FALSE;
+ ctx->decoder_initialized = FALSE;
+
+ return ctx;
+}
+
+/**
+ * Frees any resources associated with the given context.
+ *
+ * @param ctx the mimic context to free
+ */
+void mimic_close(MimCtx *ctx)
+{
+ if (ctx->encoder_initialized || ctx->decoder_initialized) {
+ gint i;
+
+ g_free(ctx->cur_frame_buf);
+
+ for (i = 0; i < 16; i++)
+ g_free(ctx->buf_ptrs[i]);
+ }
+
+ g_free(ctx);
+}
+
+/*
+ * mimic_init
+ *
+ * Internal helper-function used to initialize
+ * a given context.
+ */
+static void mimic_init(MimCtx *ctx, gint width, gint height)
+{
+ gint bufsize, i;
+
+ /*
+ * Dimensions-related.
+ */
+ ctx->frame_width = width;
+ ctx->frame_height = height;
+
+ ctx->y_stride = ctx->frame_width;
+ ctx->y_row_count = ctx->frame_height;
+ ctx->y_size = ctx->y_stride * ctx->y_row_count;
+
+ ctx->crcb_stride = ctx->y_stride / 2;
+ ctx->crcb_row_count = ctx->y_row_count / 2;
+ ctx->crcb_size = ctx->crcb_stride * ctx->crcb_row_count;
+
+ ctx->num_vblocks_y = ctx->frame_height / 8;
+ ctx->num_hblocks_y = ctx->frame_width / 8;
+
+ ctx->num_vblocks_cbcr = ctx->frame_height / 16;
+ ctx->num_hblocks_cbcr = ctx->frame_width / 16;
+
+ if (ctx->frame_height % 16 != 0)
+ ctx->num_vblocks_cbcr++;
+
+ /*
+ * Initialize state.
+ */
+ ctx->frame_num = 0;
+ ctx->ptr_index = 15;
+ ctx->num_coeffs = 28;
+
+ /*
+ * Allocate memory for buffers.
+ */
+ ctx->cur_frame_buf = g_new(guchar, (320 * 240 * 3) / 2);
+
+ bufsize = ctx->y_size + (ctx->crcb_size * 2);
+ for (i = 0; i < 16; i++)
+ ctx->buf_ptrs[i] = g_new(guchar, bufsize);
+
+ /*
+ * Initialize vlc lookup used by decoder.
+ */
+ _initialize_vlcdec_lookup(ctx->vlcdec_lookup);
+}
+
+/**
+ * Initialize the mimic encoder and prepare for encoding by
+ * initializing internal state and allocating resources as
+ * needed.
+ *
+ * After initializing use #mimic_get_property to determine
+ * the size of the output buffer needed for calls to
+ * #mimic_encode_frame. Use #mimic_set_property to set
+ * encoding quality.
+ *
+ * Note that once a given context has been initialized
+ * for either encoding or decoding it is not possible
+ * to initialize it again.
+ *
+ * @param ctx the mimic context to initialize
+ * @param resolution a #MimicResEnum used to specify the resolution
+ * @returns #TRUE on success
+ */
+gboolean mimic_encoder_init(MimCtx *ctx, const MimicResEnum resolution)
+{
+ gint width, height;
+
+ /* Check if we've been initialized before. */
+ if (ctx->encoder_initialized || ctx->decoder_initialized)
+ return FALSE;
+
+ /* Check resolution. */
+ if (resolution == MIMIC_RES_LOW) {
+ width = 160;
+ height = 120;
+ } else if (resolution == MIMIC_RES_HIGH) {
+ width = 320;
+ height = 240;
+ } else {
+ return FALSE;
+ }
+
+ /* Initialize! */
+ mimic_init(ctx, width, height);
+
+ /* Set a default quality setting. */
+ ctx->quality = ENCODER_QUALITY_DEFAULT;
+
+ ctx->encoder_initialized = TRUE;
+
+ return TRUE;
+}
+
+/**
+ * Initialize the mimic decoder. The frame passed in frame_buffer
+ * is used to determine the resolution so that the internal state
+ * can be prepared and resources allocated accordingly. Note that
+ * the frame passed has to be a keyframe.
+ *
+ * After initializing use #mimic_get_property to determine required
+ * buffer-size, resolution, quality, etc.
+ *
+ * Note that once a given context has been initialized
+ * for either encoding or decoding it is not possible
+ * to initialize it again.
+ *
+ * @param ctx the mimic context to initialize
+ * @param frame_buffer buffer containing the first frame to decode
+ * @returns #TRUE on success
+ */
+gboolean mimic_decoder_init(MimCtx *ctx, const guchar *frame_buffer)
+{
+ gint width, height;
+ gboolean is_keyframe;
+
+ /* Check if we've been initialized before and that
+ * frame_buffer is not NULL. */
+ if (ctx->encoder_initialized || ctx->decoder_initialized ||
+ frame_buffer == NULL)
+ {
+ return FALSE;
+ }
+
+ /* Check resolution. */
+ width = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 4)));
+ height = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 6)));
+
+ if (!(width == 160 && height == 120) && !(width == 320 && height == 240))
+ return FALSE;
+
+ /* Check that we're initialized with a keyframe. */
+ is_keyframe = (GUINT32_FROM_LE(*((guint32 *) (frame_buffer + 12))) == 0);
+
+ if (!is_keyframe)
+ return FALSE;
+
+ /* Get quality setting (in case we get queried for it before decoding). */
+ ctx->quality = GUINT16_FROM_LE(*((guint16 *) (frame_buffer + 2)));
+
+ /* Initialize! */
+ mimic_init(ctx, width, height);
+
+ ctx->decoder_initialized = TRUE;
+
+ return TRUE;
+}
+
+/**
+ * Get a property from a given mimic context. The context
+ * has to be initialized.
+ *
+ * Currently the following properties are defined:
+ * - "buffer_size"
+ * - Required output buffer size
+ * - "width"
+ * - Frame width
+ * - "height"
+ * - Frame height
+ * - "quality"
+ * - Encoder: Encoding quality used
+ * - Decoder: Decoding quality of the last known frame
+ *
+ * @param ctx the mimic context to retrieve the property from
+ * @param name of the property to retrieve the current value of
+ * @param data pointer to the data that will receive the retrieved value
+ * @returns #TRUE on success
+ */
+gboolean mimic_get_property(MimCtx *ctx, const gchar *name, gpointer data)
+{
+ /* Either the encoder or the decoder has to be initialized. */
+ if (!ctx->encoder_initialized && !ctx->decoder_initialized)
+ return FALSE;
+
+ if (ctx->encoder_initialized) {
+
+ if (strcmp(name, "buffer_size") == 0) {
+ *((gint *) data) = ENCODER_BUFFER_SIZE;
+
+ return TRUE;
+ }
+
+ } else { /* decoder_initialized */
+
+ if (strcmp(name, "buffer_size") == 0) {
+ *((gint *) data) = ctx->frame_width * ctx->frame_height * 3;
+
+ return TRUE;
+ }
+ }
+
+ if (strcmp(name, "width") == 0) {
+ *((gint *) data) = ctx->frame_width;
+
+ return TRUE;
+ } else if (strcmp(name, "height") == 0) {
+ *((gint *) data) = ctx->frame_height;
+
+ return TRUE;
+ } else if (strcmp(name, "quality") == 0) {
+ *((gint *) data) = ctx->quality;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * Set a property in a given mimic context. The context
+ * has to be initialized.
+ *
+ * Currently the following properties are defined:
+ * - "quality"
+ * - Encoding quality used by encoder.
+ *
+ * @param ctx the mimic context to set a property in
+ * @param name of the property to set to a new value
+ * @param data pointer to the data that contains the new value
+ * @returns #TRUE on success
+ */
+gboolean mimic_set_property(MimCtx *ctx, const gchar *name, gpointer data)
+{
+ /* Either the encoder or the decoder has to be initialized. */
+ if (!ctx->encoder_initialized && !ctx->decoder_initialized)
+ return FALSE;
+
+ if (ctx->encoder_initialized) {
+
+ if (strcmp(name, "quality") == 0) {
+ gint new_quality = *((gint *) data);
+
+ if (new_quality < ENCODER_QUALITY_MIN ||
+ new_quality > ENCODER_QUALITY_MAX)
+ {
+ return FALSE;
+ }
+
+ ctx->quality = new_quality;
+
+ return TRUE;
+ }
+
+ } else { /* decoder_initialized */ }
+
+ return FALSE;
+}
+
+/*
+ * _clamp_value
+ *
+ * Internal helper-function used to clamp a given
+ * value to the range [ 0, 255 ].
+ */
+guchar _clamp_value(gint value)
+{
+ if (value < 0)
+ return 0;
+ else if (value > 255)
+ return 255;
+ else
+ return value;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/mimic.h b/kopete/protocols/msn/webcam/libmimic/mimic.h
new file mode 100644
index 00000000..491548f4
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/mimic.h
@@ -0,0 +1,73 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 MIMIC_H
+#define MIMIC_H
+
+#include <glib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup libmimic libmimic public API
+ * @brief The public API of the libmimic library
+ *
+ * libmimic provides the API required for encoding and decoding
+ * MIMIC v2.x-encoded content.
+ *
+ * @{
+ */
+
+/**
+ * The mimic encoding/decoding context returned by #mimic_open
+ * and used for all further API calls until #mimic_close.
+ */
+typedef struct _MimCtx MimCtx;
+
+typedef enum {
+ MIMIC_RES_LOW, /**< 160x120 resolution */
+ MIMIC_RES_HIGH /**< 320x240 resolution */
+} MimicResEnum;
+
+MimCtx *mimic_open();
+void mimic_close(MimCtx *ctx);
+
+gboolean mimic_encoder_init(MimCtx *ctx, const MimicResEnum resolution);
+gboolean mimic_decoder_init(MimCtx *ctx, const guchar *frame_buffer);
+
+gboolean mimic_get_property(MimCtx *ctx, const gchar *name, gpointer data);
+gboolean mimic_set_property(MimCtx *ctx, const gchar *name, gpointer data);
+
+gboolean mimic_encode_frame(MimCtx *ctx,
+ const guchar *input_buffer,
+ guchar *output_buffer,
+ gint *output_length,
+ gboolean make_keyframe);
+gboolean mimic_decode_frame(MimCtx *ctx,
+ const guchar *input_buffer,
+ guchar *output_buffer);
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/kopete/protocols/msn/webcam/libmimic/query.c b/kopete/protocols/msn/webcam/libmimic/query.c
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/query.c
@@ -0,0 +1 @@
+
diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_common.c b/kopete/protocols/msn/webcam/libmimic/vlc_common.c
new file mode 100644
index 00000000..cbb0acc5
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/vlc_common.c
@@ -0,0 +1,1364 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include "mimic-private.h"
+
+guchar _col_zag[64] = {
+ 0, 8, 1, 2, 9, 16, 24, 17,
+ 10, 3, 4, 11, 18, 25, 32, 40,
+ 33, 26, 19, 12, 5, 6, 13, 20,
+ 27, 34, 41, 48, 56, 49, 42, 35,
+ 28, 21, 14, 7, 15, 22, 29, 36,
+ 43, 50, 57, 58, 51, 44, 37, 30,
+ 23, 31, 38, 45, 52, 59, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+VlcSymbol _vlc_alphabet[16][128] = {
+
+ /*
+ * base alphabet - no zeroes prefixed
+ */
+ {
+ { 3, 0x1, 0, 0 }, { 4, 0x7, 0, 0 },
+ { 4, 0x5, 0, 0 }, { 6, 0x27, 0, 0 },
+ { 6, 0x25, 0, 0 }, { 6, 0x23, 0, 0 },
+ { 6, 0x21, 0, 0 }, { 8, 0xcf, 0, 0 },
+ { 8, 0xcd, 0, 0 }, { 8, 0xcb, 0, 0 },
+ { 8, 0xc9, 0, 0 }, { 8, 0xc7, 0, 0 },
+ { 8, 0xc5, 0, 0 }, { 8, 0xc3, 0, 0 },
+ { 8, 0xc1, 0, 0 }, { 10, 0x35f, 0, 0 },
+ { 10, 0x35d, 0, 0 }, { 10, 0x35b, 0, 0 },
+ { 10, 0x359, 0, 0 }, { 10, 0x357, 0, 0 },
+ { 10, 0x355, 0, 0 }, { 10, 0x353, 0, 0 },
+ { 10, 0x351, 0, 0 }, { 10, 0x34f, 0, 0 },
+ { 10, 0x34d, 0, 0 }, { 10, 0x34b, 0, 0 },
+ { 10, 0x349, 0, 0 }, { 10, 0x347, 0, 0 },
+ { 10, 0x345, 0, 0 }, { 10, 0x343, 0, 0 },
+ { 10, 0x341, 0, 0 }, { 12, 0xeff, 0, 0 },
+ { 12, 0xefd, 0, 0 }, { 12, 0xefb, 0, 0 },
+ { 12, 0xef9, 0, 0 }, { 12, 0xef7, 0, 0 },
+ { 12, 0xef5, 0, 0 }, { 12, 0xef3, 0, 0 },
+ { 12, 0xef1, 0, 0 }, { 12, 0xeef, 0, 0 },
+ { 12, 0xeed, 0, 0 }, { 12, 0xeeb, 0, 0 },
+ { 12, 0xee9, 0, 0 }, { 12, 0xee7, 0, 0 },
+ { 12, 0xee5, 0, 0 }, { 12, 0xee3, 0, 0 },
+ { 12, 0xee1, 0, 0 }, { 12, 0xedf, 0, 0 },
+ { 12, 0xedd, 0, 0 }, { 12, 0xedb, 0, 0 },
+ { 12, 0xed9, 0, 0 }, { 12, 0xed7, 0, 0 },
+ { 12, 0xed5, 0, 0 }, { 12, 0xed3, 0, 0 },
+ { 12, 0xed1, 0, 0 }, { 12, 0xecf, 0, 0 },
+ { 12, 0xecd, 0, 0 }, { 12, 0xecb, 0, 0 },
+ { 12, 0xec9, 0, 0 }, { 12, 0xec7, 0, 0 },
+ { 12, 0xec5, 0, 0 }, { 12, 0xec3, 0, 0 },
+ { 12, 0xec1, 0, 0 }, { 17, 0x1fd7f, 0, 0 },
+ { 17, 0x1fd7d, 0, 0 }, { 17, 0x1fd7b, 0, 0 },
+ { 17, 0x1fd79, 0, 0 }, { 17, 0x1fd77, 0, 0 },
+ { 17, 0x1fd75, 0, 0 }, { 17, 0x1fd73, 0, 0 },
+ { 17, 0x1fd71, 0, 0 }, { 17, 0x1fd6f, 0, 0 },
+ { 17, 0x1fd6d, 0, 0 }, { 17, 0x1fd6b, 0, 0 },
+ { 17, 0x1fd69, 0, 0 }, { 17, 0x1fd67, 0, 0 },
+ { 17, 0x1fd65, 0, 0 }, { 17, 0x1fd63, 0, 0 },
+ { 17, 0x1fd61, 0, 0 }, { 17, 0x1fd5f, 0, 0 },
+ { 17, 0x1fd5d, 0, 0 }, { 17, 0x1fd5b, 0, 0 },
+ { 17, 0x1fd59, 0, 0 }, { 17, 0x1fd57, 0, 0 },
+ { 17, 0x1fd55, 0, 0 }, { 17, 0x1fd53, 0, 0 },
+ { 17, 0x1fd51, 0, 0 }, { 17, 0x1fd4f, 0, 0 },
+ { 17, 0x1fd4d, 0, 0 }, { 17, 0x1fd4b, 0, 0 },
+ { 17, 0x1fd49, 0, 0 }, { 17, 0x1fd47, 0, 0 },
+ { 17, 0x1fd45, 0, 0 }, { 17, 0x1fd43, 0, 0 },
+ { 17, 0x1fd41, 0, 0 }, { 17, 0x1fd3f, 0, 0 },
+ { 17, 0x1fd3d, 0, 0 }, { 17, 0x1fd3b, 0, 0 },
+ { 17, 0x1fd39, 0, 0 }, { 17, 0x1fd37, 0, 0 },
+ { 17, 0x1fd35, 0, 0 }, { 17, 0x1fd33, 0, 0 },
+ { 17, 0x1fd31, 0, 0 }, { 17, 0x1fd2f, 0, 0 },
+ { 17, 0x1fd2d, 0, 0 }, { 17, 0x1fd2b, 0, 0 },
+ { 17, 0x1fd29, 0, 0 }, { 17, 0x1fd27, 0, 0 },
+ { 17, 0x1fd25, 0, 0 }, { 17, 0x1fd23, 0, 0 },
+ { 17, 0x1fd21, 0, 0 }, { 17, 0x1fd1f, 0, 0 },
+ { 17, 0x1fd1d, 0, 0 }, { 17, 0x1fd1b, 0, 0 },
+ { 17, 0x1fd19, 0, 0 }, { 17, 0x1fd17, 0, 0 },
+ { 17, 0x1fd15, 0, 0 }, { 17, 0x1fd13, 0, 0 },
+ { 17, 0x1fd11, 0, 0 }, { 17, 0x1fd0f, 0, 0 },
+ { 17, 0x1fd0d, 0, 0 }, { 17, 0x1fd0b, 0, 0 },
+ { 17, 0x1fd09, 0, 0 }, { 17, 0x1fd07, 0, 0 },
+ { 17, 0x1fd05, 0, 0 }, { 17, 0x1fd03, 0, 0 },
+ { 17, 0x1fd01, 0, 0 }, { 17, 0x1fd01, 0, 0 }
+ },
+
+ /*
+ * prefixed with 1 zero
+ */
+ {
+ { 5, 0x17, 0, 0 }, { 8, 0xe7, 0, 0 },
+ { 8, 0xe5, 0, 0 }, { 9, 0x1d7, 0, 0 },
+ { 9, 0x1d5, 0, 0 }, { 9, 0x1d3, 0, 0 },
+ { 9, 0x1d1, 0, 0 }, { 12, 0xf8f, 0, 0 },
+ { 12, 0xf8d, 0, 0 }, { 12, 0xf8b, 0, 0 },
+ { 12, 0xf89, 0, 0 }, { 12, 0xf87, 0, 0 },
+ { 12, 0xf85, 0, 0 }, { 12, 0xf83, 0, 0 },
+ { 12, 0xf81, 0, 0 }, { 15, 0x7f1f, 0, 0 },
+ { 15, 0x7f1d, 0, 0 }, { 15, 0x7f1b, 0, 0 },
+ { 15, 0x7f19, 0, 0 }, { 15, 0x7f17, 0, 0 },
+ { 15, 0x7f15, 0, 0 }, { 15, 0x7f13, 0, 0 },
+ { 15, 0x7f11, 0, 0 }, { 15, 0x7f0f, 0, 0 },
+ { 15, 0x7f0d, 0, 0 }, { 15, 0x7f0b, 0, 0 },
+ { 15, 0x7f09, 0, 0 }, { 15, 0x7f07, 0, 0 },
+ { 15, 0x7f05, 0, 0 }, { 15, 0x7f03, 0, 0 },
+ { 15, 0x7f01, 0, 0 }, { 16, 0xfe7f, 0, 0 },
+ { 16, 0xfe7d, 0, 0 }, { 16, 0xfe7b, 0, 0 },
+ { 16, 0xfe79, 0, 0 }, { 16, 0xfe77, 0, 0 },
+ { 16, 0xfe75, 0, 0 }, { 16, 0xfe73, 0, 0 },
+ { 16, 0xfe71, 0, 0 }, { 16, 0xfe6f, 0, 0 },
+ { 16, 0xfe6d, 0, 0 }, { 16, 0xfe6b, 0, 0 },
+ { 16, 0xfe69, 0, 0 }, { 16, 0xfe67, 0, 0 },
+ { 16, 0xfe65, 0, 0 }, { 16, 0xfe63, 0, 0 },
+ { 16, 0xfe61, 0, 0 }, { 16, 0xfe5f, 0, 0 },
+ { 16, 0xfe5d, 0, 0 }, { 16, 0xfe5b, 0, 0 },
+ { 16, 0xfe59, 0, 0 }, { 16, 0xfe57, 0, 0 },
+ { 16, 0xfe55, 0, 0 }, { 16, 0xfe53, 0, 0 },
+ { 16, 0xfe51, 0, 0 }, { 16, 0xfe4f, 0, 0 },
+ { 16, 0xfe4d, 0, 0 }, { 16, 0xfe4b, 0, 0 },
+ { 16, 0xfe49, 0, 0 }, { 16, 0xfe47, 0, 0 },
+ { 16, 0xfe45, 0, 0 }, { 16, 0xfe43, 0, 0 },
+ { 16, 0xfe41, 0, 0 }, { 27, 0x7fffff9, 7, 0x7f },
+ { 27, 0x7fffff9, 7, 0x7d }, { 27, 0x7fffff9, 7, 0x7b },
+ { 27, 0x7fffff9, 7, 0x79 }, { 27, 0x7fffff9, 7, 0x77 },
+ { 27, 0x7fffff9, 7, 0x75 }, { 27, 0x7fffff9, 7, 0x73 },
+ { 27, 0x7fffff9, 7, 0x71 }, { 27, 0x7fffff9, 7, 0x6f },
+ { 27, 0x7fffff9, 7, 0x6d }, { 27, 0x7fffff9, 7, 0x6b },
+ { 27, 0x7fffff9, 7, 0x69 }, { 27, 0x7fffff9, 7, 0x67 },
+ { 27, 0x7fffff9, 7, 0x65 }, { 27, 0x7fffff9, 7, 0x63 },
+ { 27, 0x7fffff9, 7, 0x61 }, { 27, 0x7fffff9, 7, 0x5f },
+ { 27, 0x7fffff9, 7, 0x5d }, { 27, 0x7fffff9, 7, 0x5b },
+ { 27, 0x7fffff9, 7, 0x59 }, { 27, 0x7fffff9, 7, 0x57 },
+ { 27, 0x7fffff9, 7, 0x55 }, { 27, 0x7fffff9, 7, 0x53 },
+ { 27, 0x7fffff9, 7, 0x51 }, { 27, 0x7fffff9, 7, 0x4f },
+ { 27, 0x7fffff9, 7, 0x4d }, { 27, 0x7fffff9, 7, 0x4b },
+ { 27, 0x7fffff9, 7, 0x49 }, { 27, 0x7fffff9, 7, 0x47 },
+ { 27, 0x7fffff9, 7, 0x45 }, { 27, 0x7fffff9, 7, 0x43 },
+ { 27, 0x7fffff9, 7, 0x41 }, { 27, 0x7fffff9, 7, 0x3f },
+ { 27, 0x7fffff9, 7, 0x3d }, { 27, 0x7fffff9, 7, 0x3b },
+ { 27, 0x7fffff9, 7, 0x39 }, { 27, 0x7fffff9, 7, 0x37 },
+ { 27, 0x7fffff9, 7, 0x35 }, { 27, 0x7fffff9, 7, 0x33 },
+ { 27, 0x7fffff9, 7, 0x31 }, { 27, 0x7fffff9, 7, 0x2f },
+ { 27, 0x7fffff9, 7, 0x2d }, { 27, 0x7fffff9, 7, 0x2b },
+ { 27, 0x7fffff9, 7, 0x29 }, { 27, 0x7fffff9, 7, 0x27 },
+ { 27, 0x7fffff9, 7, 0x25 }, { 27, 0x7fffff9, 7, 0x23 },
+ { 27, 0x7fffff9, 7, 0x21 }, { 27, 0x7fffff9, 7, 0x1f },
+ { 27, 0x7fffff9, 7, 0x1d }, { 27, 0x7fffff9, 7, 0x1b },
+ { 27, 0x7fffff9, 7, 0x19 }, { 27, 0x7fffff9, 7, 0x17 },
+ { 27, 0x7fffff9, 7, 0x15 }, { 27, 0x7fffff9, 7, 0x13 },
+ { 27, 0x7fffff9, 7, 0x11 }, { 27, 0x7fffff9, 7, 0xf },
+ { 27, 0x7fffff9, 7, 0xd }, { 27, 0x7fffff9, 7, 0xb },
+ { 27, 0x7fffff9, 7, 0x9 }, { 27, 0x7fffff9, 7, 0x7 },
+ { 27, 0x7fffff9, 7, 0x5 }, { 27, 0x7fffff9, 7, 0x3 },
+ { 27, 0x7fffff9, 7, 0x1 }, { 27, 0x7fffff9, 7, 0x1 }
+ },
+
+ /*
+ * prefixed with 2 zeroes
+ */
+ {
+ { 6, 0x37, 0, 0 }, { 9, 0x1ef, 0, 0 },
+ { 9, 0x1ed, 0, 0 }, { 12, 0xfd7, 0, 0 },
+ { 12, 0xfd5, 0, 0 }, { 12, 0xfd3, 0, 0 },
+ { 12, 0xfd1, 0, 0 }, { 13, 0x1fbf, 0, 0 },
+ { 13, 0x1fbd, 0, 0 }, { 13, 0x1fbb, 0, 0 },
+ { 13, 0x1fb9, 0, 0 }, { 13, 0x1fb7, 0, 0 },
+ { 13, 0x1fb5, 0, 0 }, { 13, 0x1fb3, 0, 0 },
+ { 13, 0x1fb1, 0, 0 }, { 25, 0x1ffff7f, 0, 0 },
+ { 25, 0x1ffff7d, 0, 0 }, { 25, 0x1ffff7b, 0, 0 },
+ { 25, 0x1ffff79, 0, 0 }, { 25, 0x1ffff77, 0, 0 },
+ { 25, 0x1ffff75, 0, 0 }, { 25, 0x1ffff73, 0, 0 },
+ { 25, 0x1ffff71, 0, 0 }, { 25, 0x1ffff6f, 0, 0 },
+ { 25, 0x1ffff6d, 0, 0 }, { 25, 0x1ffff6b, 0, 0 },
+ { 25, 0x1ffff69, 0, 0 }, { 25, 0x1ffff67, 0, 0 },
+ { 25, 0x1ffff65, 0, 0 }, { 25, 0x1ffff63, 0, 0 },
+ { 25, 0x1ffff61, 0, 0 }, { 30, 0x3ffffe3f, 0, 0 },
+ { 30, 0x3ffffe3d, 0, 0 }, { 30, 0x3ffffe3b, 0, 0 },
+ { 30, 0x3ffffe39, 0, 0 }, { 30, 0x3ffffe37, 0, 0 },
+ { 30, 0x3ffffe35, 0, 0 }, { 30, 0x3ffffe33, 0, 0 },
+ { 30, 0x3ffffe31, 0, 0 }, { 30, 0x3ffffe2f, 0, 0 },
+ { 30, 0x3ffffe2d, 0, 0 }, { 30, 0x3ffffe2b, 0, 0 },
+ { 30, 0x3ffffe29, 0, 0 }, { 30, 0x3ffffe27, 0, 0 },
+ { 30, 0x3ffffe25, 0, 0 }, { 30, 0x3ffffe23, 0, 0 },
+ { 30, 0x3ffffe21, 0, 0 }, { 30, 0x3ffffe1f, 0, 0 },
+ { 30, 0x3ffffe1d, 0, 0 }, { 30, 0x3ffffe1b, 0, 0 },
+ { 30, 0x3ffffe19, 0, 0 }, { 30, 0x3ffffe17, 0, 0 },
+ { 30, 0x3ffffe15, 0, 0 }, { 30, 0x3ffffe13, 0, 0 },
+ { 30, 0x3ffffe11, 0, 0 }, { 30, 0x3ffffe0f, 0, 0 },
+ { 30, 0x3ffffe0d, 0, 0 }, { 30, 0x3ffffe0b, 0, 0 },
+ { 30, 0x3ffffe09, 0, 0 }, { 30, 0x3ffffe07, 0, 0 },
+ { 30, 0x3ffffe05, 0, 0 }, { 30, 0x3ffffe03, 0, 0 },
+ { 30, 0x3ffffe01, 0, 0 }, { 27, 0x7fffffa, 7, 0x7f },
+ { 27, 0x7fffffa, 7, 0x7d }, { 27, 0x7fffffa, 7, 0x7b },
+ { 27, 0x7fffffa, 7, 0x79 }, { 27, 0x7fffffa, 7, 0x77 },
+ { 27, 0x7fffffa, 7, 0x75 }, { 27, 0x7fffffa, 7, 0x73 },
+ { 27, 0x7fffffa, 7, 0x71 }, { 27, 0x7fffffa, 7, 0x6f },
+ { 27, 0x7fffffa, 7, 0x6d }, { 27, 0x7fffffa, 7, 0x6b },
+ { 27, 0x7fffffa, 7, 0x69 }, { 27, 0x7fffffa, 7, 0x67 },
+ { 27, 0x7fffffa, 7, 0x65 }, { 27, 0x7fffffa, 7, 0x63 },
+ { 27, 0x7fffffa, 7, 0x61 }, { 27, 0x7fffffa, 7, 0x5f },
+ { 27, 0x7fffffa, 7, 0x5d }, { 27, 0x7fffffa, 7, 0x5b },
+ { 27, 0x7fffffa, 7, 0x59 }, { 27, 0x7fffffa, 7, 0x57 },
+ { 27, 0x7fffffa, 7, 0x55 }, { 27, 0x7fffffa, 7, 0x53 },
+ { 27, 0x7fffffa, 7, 0x51 }, { 27, 0x7fffffa, 7, 0x4f },
+ { 27, 0x7fffffa, 7, 0x4d }, { 27, 0x7fffffa, 7, 0x4b },
+ { 27, 0x7fffffa, 7, 0x49 }, { 27, 0x7fffffa, 7, 0x47 },
+ { 27, 0x7fffffa, 7, 0x45 }, { 27, 0x7fffffa, 7, 0x43 },
+ { 27, 0x7fffffa, 7, 0x41 }, { 27, 0x7fffffa, 7, 0x3f },
+ { 27, 0x7fffffa, 7, 0x3d }, { 27, 0x7fffffa, 7, 0x3b },
+ { 27, 0x7fffffa, 7, 0x39 }, { 27, 0x7fffffa, 7, 0x37 },
+ { 27, 0x7fffffa, 7, 0x35 }, { 27, 0x7fffffa, 7, 0x33 },
+ { 27, 0x7fffffa, 7, 0x31 }, { 27, 0x7fffffa, 7, 0x2f },
+ { 27, 0x7fffffa, 7, 0x2d }, { 27, 0x7fffffa, 7, 0x2b },
+ { 27, 0x7fffffa, 7, 0x29 }, { 27, 0x7fffffa, 7, 0x27 },
+ { 27, 0x7fffffa, 7, 0x25 }, { 27, 0x7fffffa, 7, 0x23 },
+ { 27, 0x7fffffa, 7, 0x21 }, { 27, 0x7fffffa, 7, 0x1f },
+ { 27, 0x7fffffa, 7, 0x1d }, { 27, 0x7fffffa, 7, 0x1b },
+ { 27, 0x7fffffa, 7, 0x19 }, { 27, 0x7fffffa, 7, 0x17 },
+ { 27, 0x7fffffa, 7, 0x15 }, { 27, 0x7fffffa, 7, 0x13 },
+ { 27, 0x7fffffa, 7, 0x11 }, { 27, 0x7fffffa, 7, 0xf },
+ { 27, 0x7fffffa, 7, 0xd }, { 27, 0x7fffffa, 7, 0xb },
+ { 27, 0x7fffffa, 7, 0x9 }, { 27, 0x7fffffa, 7, 0x7 },
+ { 27, 0x7fffffa, 7, 0x5 }, { 27, 0x7fffffa, 7, 0x3 },
+ { 27, 0x7fffffa, 7, 0x1 }, { 27, 0x7fffffa, 7, 0x1 }
+ },
+
+ /*
+ * prefixed with 3 zeroes
+ */
+ {
+ { 7, 0x71, 0, 0 }, { 10, 0x3ef, 0, 0 },
+ { 10, 0x3ed, 0, 0 }, { 17, 0x1ffdf, 0, 0 },
+ { 17, 0x1ffdd, 0, 0 }, { 17, 0x1ffdb, 0, 0 },
+ { 17, 0x1ffd9, 0, 0 }, { 21, 0x1fffbf, 0, 0 },
+ { 21, 0x1fffbd, 0, 0 }, { 21, 0x1fffbb, 0, 0 },
+ { 21, 0x1fffb9, 0, 0 }, { 21, 0x1fffb7, 0, 0 },
+ { 21, 0x1fffb5, 0, 0 }, { 21, 0x1fffb3, 0, 0 },
+ { 21, 0x1fffb1, 0, 0 }, { 26, 0x3ffff1f, 0, 0 },
+ { 26, 0x3ffff1d, 0, 0 }, { 26, 0x3ffff1b, 0, 0 },
+ { 26, 0x3ffff19, 0, 0 }, { 26, 0x3ffff17, 0, 0 },
+ { 26, 0x3ffff15, 0, 0 }, { 26, 0x3ffff13, 0, 0 },
+ { 26, 0x3ffff11, 0, 0 }, { 26, 0x3ffff0f, 0, 0 },
+ { 26, 0x3ffff0d, 0, 0 }, { 26, 0x3ffff0b, 0, 0 },
+ { 26, 0x3ffff09, 0, 0 }, { 26, 0x3ffff07, 0, 0 },
+ { 26, 0x3ffff05, 0, 0 }, { 26, 0x3ffff03, 0, 0 },
+ { 26, 0x3ffff01, 0, 0 }, { 30, 0x3ffffe7f, 0, 0 },
+ { 30, 0x3ffffe7d, 0, 0 }, { 30, 0x3ffffe7b, 0, 0 },
+ { 30, 0x3ffffe79, 0, 0 }, { 30, 0x3ffffe77, 0, 0 },
+ { 30, 0x3ffffe75, 0, 0 }, { 30, 0x3ffffe73, 0, 0 },
+ { 30, 0x3ffffe71, 0, 0 }, { 30, 0x3ffffe6f, 0, 0 },
+ { 30, 0x3ffffe6d, 0, 0 }, { 30, 0x3ffffe6b, 0, 0 },
+ { 30, 0x3ffffe69, 0, 0 }, { 30, 0x3ffffe67, 0, 0 },
+ { 30, 0x3ffffe65, 0, 0 }, { 30, 0x3ffffe63, 0, 0 },
+ { 30, 0x3ffffe61, 0, 0 }, { 30, 0x3ffffe5f, 0, 0 },
+ { 30, 0x3ffffe5d, 0, 0 }, { 30, 0x3ffffe5b, 0, 0 },
+ { 30, 0x3ffffe59, 0, 0 }, { 30, 0x3ffffe57, 0, 0 },
+ { 30, 0x3ffffe55, 0, 0 }, { 30, 0x3ffffe53, 0, 0 },
+ { 30, 0x3ffffe51, 0, 0 }, { 30, 0x3ffffe4f, 0, 0 },
+ { 30, 0x3ffffe4d, 0, 0 }, { 30, 0x3ffffe4b, 0, 0 },
+ { 30, 0x3ffffe49, 0, 0 }, { 30, 0x3ffffe47, 0, 0 },
+ { 30, 0x3ffffe45, 0, 0 }, { 30, 0x3ffffe43, 0, 0 },
+ { 30, 0x3ffffe41, 0, 0 }, { 27, 0x7fffffb, 7, 0x7f },
+ { 27, 0x7fffffb, 7, 0x7d }, { 27, 0x7fffffb, 7, 0x7b },
+ { 27, 0x7fffffb, 7, 0x79 }, { 27, 0x7fffffb, 7, 0x77 },
+ { 27, 0x7fffffb, 7, 0x75 }, { 27, 0x7fffffb, 7, 0x73 },
+ { 27, 0x7fffffb, 7, 0x71 }, { 27, 0x7fffffb, 7, 0x6f },
+ { 27, 0x7fffffb, 7, 0x6d }, { 27, 0x7fffffb, 7, 0x6b },
+ { 27, 0x7fffffb, 7, 0x69 }, { 27, 0x7fffffb, 7, 0x67 },
+ { 27, 0x7fffffb, 7, 0x65 }, { 27, 0x7fffffb, 7, 0x63 },
+ { 27, 0x7fffffb, 7, 0x61 }, { 27, 0x7fffffb, 7, 0x5f },
+ { 27, 0x7fffffb, 7, 0x5d }, { 27, 0x7fffffb, 7, 0x5b },
+ { 27, 0x7fffffb, 7, 0x59 }, { 27, 0x7fffffb, 7, 0x57 },
+ { 27, 0x7fffffb, 7, 0x55 }, { 27, 0x7fffffb, 7, 0x53 },
+ { 27, 0x7fffffb, 7, 0x51 }, { 27, 0x7fffffb, 7, 0x4f },
+ { 27, 0x7fffffb, 7, 0x4d }, { 27, 0x7fffffb, 7, 0x4b },
+ { 27, 0x7fffffb, 7, 0x49 }, { 27, 0x7fffffb, 7, 0x47 },
+ { 27, 0x7fffffb, 7, 0x45 }, { 27, 0x7fffffb, 7, 0x43 },
+ { 27, 0x7fffffb, 7, 0x41 }, { 27, 0x7fffffb, 7, 0x3f },
+ { 27, 0x7fffffb, 7, 0x3d }, { 27, 0x7fffffb, 7, 0x3b },
+ { 27, 0x7fffffb, 7, 0x39 }, { 27, 0x7fffffb, 7, 0x37 },
+ { 27, 0x7fffffb, 7, 0x35 }, { 27, 0x7fffffb, 7, 0x33 },
+ { 27, 0x7fffffb, 7, 0x31 }, { 27, 0x7fffffb, 7, 0x2f },
+ { 27, 0x7fffffb, 7, 0x2d }, { 27, 0x7fffffb, 7, 0x2b },
+ { 27, 0x7fffffb, 7, 0x29 }, { 27, 0x7fffffb, 7, 0x27 },
+ { 27, 0x7fffffb, 7, 0x25 }, { 27, 0x7fffffb, 7, 0x23 },
+ { 27, 0x7fffffb, 7, 0x21 }, { 27, 0x7fffffb, 7, 0x1f },
+ { 27, 0x7fffffb, 7, 0x1d }, { 27, 0x7fffffb, 7, 0x1b },
+ { 27, 0x7fffffb, 7, 0x19 }, { 27, 0x7fffffb, 7, 0x17 },
+ { 27, 0x7fffffb, 7, 0x15 }, { 27, 0x7fffffb, 7, 0x13 },
+ { 27, 0x7fffffb, 7, 0x11 }, { 27, 0x7fffffb, 7, 0xf },
+ { 27, 0x7fffffb, 7, 0xd }, { 27, 0x7fffffb, 7, 0xb },
+ { 27, 0x7fffffb, 7, 0x9 }, { 27, 0x7fffffb, 7, 0x7 },
+ { 27, 0x7fffffb, 7, 0x5 }, { 27, 0x7fffffb, 7, 0x3 },
+ { 27, 0x7fffffb, 7, 0x1 }, { 27, 0x7fffffb, 7, 0x1 }
+ },
+
+ /*
+ * prefixed with 4 zeroes
+ */
+ {
+ { 8, 0xf1, 0, 0 }, { 11, 0x7e3, 0, 0 },
+ { 11, 0x7e1, 0, 0 }, { 18, 0x3ffc7, 0, 0 },
+ { 18, 0x3ffc5, 0, 0 }, { 18, 0x3ffc3, 0, 0 },
+ { 18, 0x3ffc1, 0, 0 }, { 22, 0x3fff8f, 0, 0 },
+ { 22, 0x3fff8d, 0, 0 }, { 22, 0x3fff8b, 0, 0 },
+ { 22, 0x3fff89, 0, 0 }, { 22, 0x3fff87, 0, 0 },
+ { 22, 0x3fff85, 0, 0 }, { 22, 0x3fff83, 0, 0 },
+ { 22, 0x3fff81, 0, 0 }, { 26, 0x3ffff3f, 0, 0 },
+ { 26, 0x3ffff3d, 0, 0 }, { 26, 0x3ffff3b, 0, 0 },
+ { 26, 0x3ffff39, 0, 0 }, { 26, 0x3ffff37, 0, 0 },
+ { 26, 0x3ffff35, 0, 0 }, { 26, 0x3ffff33, 0, 0 },
+ { 26, 0x3ffff31, 0, 0 }, { 26, 0x3ffff2f, 0, 0 },
+ { 26, 0x3ffff2d, 0, 0 }, { 26, 0x3ffff2b, 0, 0 },
+ { 26, 0x3ffff29, 0, 0 }, { 26, 0x3ffff27, 0, 0 },
+ { 26, 0x3ffff25, 0, 0 }, { 26, 0x3ffff23, 0, 0 },
+ { 26, 0x3ffff21, 0, 0 }, { 30, 0x3ffffebf, 0, 0 },
+ { 30, 0x3ffffebd, 0, 0 }, { 30, 0x3ffffebb, 0, 0 },
+ { 30, 0x3ffffeb9, 0, 0 }, { 30, 0x3ffffeb7, 0, 0 },
+ { 30, 0x3ffffeb5, 0, 0 }, { 30, 0x3ffffeb3, 0, 0 },
+ { 30, 0x3ffffeb1, 0, 0 }, { 30, 0x3ffffeaf, 0, 0 },
+ { 30, 0x3ffffead, 0, 0 }, { 30, 0x3ffffeab, 0, 0 },
+ { 30, 0x3ffffea9, 0, 0 }, { 30, 0x3ffffea7, 0, 0 },
+ { 30, 0x3ffffea5, 0, 0 }, { 30, 0x3ffffea3, 0, 0 },
+ { 30, 0x3ffffea1, 0, 0 }, { 30, 0x3ffffe9f, 0, 0 },
+ { 30, 0x3ffffe9d, 0, 0 }, { 30, 0x3ffffe9b, 0, 0 },
+ { 30, 0x3ffffe99, 0, 0 }, { 30, 0x3ffffe97, 0, 0 },
+ { 30, 0x3ffffe95, 0, 0 }, { 30, 0x3ffffe93, 0, 0 },
+ { 30, 0x3ffffe91, 0, 0 }, { 30, 0x3ffffe8f, 0, 0 },
+ { 30, 0x3ffffe8d, 0, 0 }, { 30, 0x3ffffe8b, 0, 0 },
+ { 30, 0x3ffffe89, 0, 0 }, { 30, 0x3ffffe87, 0, 0 },
+ { 30, 0x3ffffe85, 0, 0 }, { 30, 0x3ffffe83, 0, 0 },
+ { 30, 0x3ffffe81, 0, 0 }, { 28, 0xffffff8, 7, 0x7f },
+ { 28, 0xffffff8, 7, 0x7d }, { 28, 0xffffff8, 7, 0x7b },
+ { 28, 0xffffff8, 7, 0x79 }, { 28, 0xffffff8, 7, 0x77 },
+ { 28, 0xffffff8, 7, 0x75 }, { 28, 0xffffff8, 7, 0x73 },
+ { 28, 0xffffff8, 7, 0x71 }, { 28, 0xffffff8, 7, 0x6f },
+ { 28, 0xffffff8, 7, 0x6d }, { 28, 0xffffff8, 7, 0x6b },
+ { 28, 0xffffff8, 7, 0x69 }, { 28, 0xffffff8, 7, 0x67 },
+ { 28, 0xffffff8, 7, 0x65 }, { 28, 0xffffff8, 7, 0x63 },
+ { 28, 0xffffff8, 7, 0x61 }, { 28, 0xffffff8, 7, 0x5f },
+ { 28, 0xffffff8, 7, 0x5d }, { 28, 0xffffff8, 7, 0x5b },
+ { 28, 0xffffff8, 7, 0x59 }, { 28, 0xffffff8, 7, 0x57 },
+ { 28, 0xffffff8, 7, 0x55 }, { 28, 0xffffff8, 7, 0x53 },
+ { 28, 0xffffff8, 7, 0x51 }, { 28, 0xffffff8, 7, 0x4f },
+ { 28, 0xffffff8, 7, 0x4d }, { 28, 0xffffff8, 7, 0x4b },
+ { 28, 0xffffff8, 7, 0x49 }, { 28, 0xffffff8, 7, 0x47 },
+ { 28, 0xffffff8, 7, 0x45 }, { 28, 0xffffff8, 7, 0x43 },
+ { 28, 0xffffff8, 7, 0x41 }, { 28, 0xffffff8, 7, 0x3f },
+ { 28, 0xffffff8, 7, 0x3d }, { 28, 0xffffff8, 7, 0x3b },
+ { 28, 0xffffff8, 7, 0x39 }, { 28, 0xffffff8, 7, 0x37 },
+ { 28, 0xffffff8, 7, 0x35 }, { 28, 0xffffff8, 7, 0x33 },
+ { 28, 0xffffff8, 7, 0x31 }, { 28, 0xffffff8, 7, 0x2f },
+ { 28, 0xffffff8, 7, 0x2d }, { 28, 0xffffff8, 7, 0x2b },
+ { 28, 0xffffff8, 7, 0x29 }, { 28, 0xffffff8, 7, 0x27 },
+ { 28, 0xffffff8, 7, 0x25 }, { 28, 0xffffff8, 7, 0x23 },
+ { 28, 0xffffff8, 7, 0x21 }, { 28, 0xffffff8, 7, 0x1f },
+ { 28, 0xffffff8, 7, 0x1d }, { 28, 0xffffff8, 7, 0x1b },
+ { 28, 0xffffff8, 7, 0x19 }, { 28, 0xffffff8, 7, 0x17 },
+ { 28, 0xffffff8, 7, 0x15 }, { 28, 0xffffff8, 7, 0x13 },
+ { 28, 0xffffff8, 7, 0x11 }, { 28, 0xffffff8, 7, 0xf },
+ { 28, 0xffffff8, 7, 0xd }, { 28, 0xffffff8, 7, 0xb },
+ { 28, 0xffffff8, 7, 0x9 }, { 28, 0xffffff8, 7, 0x7 },
+ { 28, 0xffffff8, 7, 0x5 }, { 28, 0xffffff8, 7, 0x3 },
+ { 28, 0xffffff8, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 5 zeroes
+ */
+ {
+ { 8, 0xf3, 0, 0 }, { 11, 0x7e7, 0, 0 },
+ { 11, 0x7e5, 0, 0 }, { 18, 0x3ffcf, 0, 0 },
+ { 18, 0x3ffcd, 0, 0 }, { 18, 0x3ffcb, 0, 0 },
+ { 18, 0x3ffc9, 0, 0 }, { 22, 0x3fff9f, 0, 0 },
+ { 22, 0x3fff9d, 0, 0 }, { 22, 0x3fff9b, 0, 0 },
+ { 22, 0x3fff99, 0, 0 }, { 22, 0x3fff97, 0, 0 },
+ { 22, 0x3fff95, 0, 0 }, { 22, 0x3fff93, 0, 0 },
+ { 22, 0x3fff91, 0, 0 }, { 26, 0x3ffff5f, 0, 0 },
+ { 26, 0x3ffff5d, 0, 0 }, { 26, 0x3ffff5b, 0, 0 },
+ { 26, 0x3ffff59, 0, 0 }, { 26, 0x3ffff57, 0, 0 },
+ { 26, 0x3ffff55, 0, 0 }, { 26, 0x3ffff53, 0, 0 },
+ { 26, 0x3ffff51, 0, 0 }, { 26, 0x3ffff4f, 0, 0 },
+ { 26, 0x3ffff4d, 0, 0 }, { 26, 0x3ffff4b, 0, 0 },
+ { 26, 0x3ffff49, 0, 0 }, { 26, 0x3ffff47, 0, 0 },
+ { 26, 0x3ffff45, 0, 0 }, { 26, 0x3ffff43, 0, 0 },
+ { 26, 0x3ffff41, 0, 0 }, { 30, 0x3ffffeff, 0, 0 },
+ { 30, 0x3ffffefd, 0, 0 }, { 30, 0x3ffffefb, 0, 0 },
+ { 30, 0x3ffffef9, 0, 0 }, { 30, 0x3ffffef7, 0, 0 },
+ { 30, 0x3ffffef5, 0, 0 }, { 30, 0x3ffffef3, 0, 0 },
+ { 30, 0x3ffffef1, 0, 0 }, { 30, 0x3ffffeef, 0, 0 },
+ { 30, 0x3ffffeed, 0, 0 }, { 30, 0x3ffffeeb, 0, 0 },
+ { 30, 0x3ffffee9, 0, 0 }, { 30, 0x3ffffee7, 0, 0 },
+ { 30, 0x3ffffee5, 0, 0 }, { 30, 0x3ffffee3, 0, 0 },
+ { 30, 0x3ffffee1, 0, 0 }, { 30, 0x3ffffedf, 0, 0 },
+ { 30, 0x3ffffedd, 0, 0 }, { 30, 0x3ffffedb, 0, 0 },
+ { 30, 0x3ffffed9, 0, 0 }, { 30, 0x3ffffed7, 0, 0 },
+ { 30, 0x3ffffed5, 0, 0 }, { 30, 0x3ffffed3, 0, 0 },
+ { 30, 0x3ffffed1, 0, 0 }, { 30, 0x3ffffecf, 0, 0 },
+ { 30, 0x3ffffecd, 0, 0 }, { 30, 0x3ffffecb, 0, 0 },
+ { 30, 0x3ffffec9, 0, 0 }, { 30, 0x3ffffec7, 0, 0 },
+ { 30, 0x3ffffec5, 0, 0 }, { 30, 0x3ffffec3, 0, 0 },
+ { 30, 0x3ffffec1, 0, 0 }, { 28, 0xffffff9, 7, 0x7f },
+ { 28, 0xffffff9, 7, 0x7d }, { 28, 0xffffff9, 7, 0x7b },
+ { 28, 0xffffff9, 7, 0x79 }, { 28, 0xffffff9, 7, 0x77 },
+ { 28, 0xffffff9, 7, 0x75 }, { 28, 0xffffff9, 7, 0x73 },
+ { 28, 0xffffff9, 7, 0x71 }, { 28, 0xffffff9, 7, 0x6f },
+ { 28, 0xffffff9, 7, 0x6d }, { 28, 0xffffff9, 7, 0x6b },
+ { 28, 0xffffff9, 7, 0x69 }, { 28, 0xffffff9, 7, 0x67 },
+ { 28, 0xffffff9, 7, 0x65 }, { 28, 0xffffff9, 7, 0x63 },
+ { 28, 0xffffff9, 7, 0x61 }, { 28, 0xffffff9, 7, 0x5f },
+ { 28, 0xffffff9, 7, 0x5d }, { 28, 0xffffff9, 7, 0x5b },
+ { 28, 0xffffff9, 7, 0x59 }, { 28, 0xffffff9, 7, 0x57 },
+ { 28, 0xffffff9, 7, 0x55 }, { 28, 0xffffff9, 7, 0x53 },
+ { 28, 0xffffff9, 7, 0x51 }, { 28, 0xffffff9, 7, 0x4f },
+ { 28, 0xffffff9, 7, 0x4d }, { 28, 0xffffff9, 7, 0x4b },
+ { 28, 0xffffff9, 7, 0x49 }, { 28, 0xffffff9, 7, 0x47 },
+ { 28, 0xffffff9, 7, 0x45 }, { 28, 0xffffff9, 7, 0x43 },
+ { 28, 0xffffff9, 7, 0x41 }, { 28, 0xffffff9, 7, 0x3f },
+ { 28, 0xffffff9, 7, 0x3d }, { 28, 0xffffff9, 7, 0x3b },
+ { 28, 0xffffff9, 7, 0x39 }, { 28, 0xffffff9, 7, 0x37 },
+ { 28, 0xffffff9, 7, 0x35 }, { 28, 0xffffff9, 7, 0x33 },
+ { 28, 0xffffff9, 7, 0x31 }, { 28, 0xffffff9, 7, 0x2f },
+ { 28, 0xffffff9, 7, 0x2d }, { 28, 0xffffff9, 7, 0x2b },
+ { 28, 0xffffff9, 7, 0x29 }, { 28, 0xffffff9, 7, 0x27 },
+ { 28, 0xffffff9, 7, 0x25 }, { 28, 0xffffff9, 7, 0x23 },
+ { 28, 0xffffff9, 7, 0x21 }, { 28, 0xffffff9, 7, 0x1f },
+ { 28, 0xffffff9, 7, 0x1d }, { 28, 0xffffff9, 7, 0x1b },
+ { 28, 0xffffff9, 7, 0x19 }, { 28, 0xffffff9, 7, 0x17 },
+ { 28, 0xffffff9, 7, 0x15 }, { 28, 0xffffff9, 7, 0x13 },
+ { 28, 0xffffff9, 7, 0x11 }, { 28, 0xffffff9, 7, 0xf },
+ { 28, 0xffffff9, 7, 0xd }, { 28, 0xffffff9, 7, 0xb },
+ { 28, 0xffffff9, 7, 0x9 }, { 28, 0xffffff9, 7, 0x7 },
+ { 28, 0xffffff9, 7, 0x5 }, { 28, 0xffffff9, 7, 0x3 },
+ { 28, 0xffffff9, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 6 zeroes
+ */
+ {
+ { 8, 0xf5, 0, 0 }, { 14, 0x3feb, 0, 0 },
+ { 14, 0x3fe9, 0, 0 }, { 18, 0x3ffd7, 0, 0 },
+ { 18, 0x3ffd5, 0, 0 }, { 18, 0x3ffd3, 0, 0 },
+ { 18, 0x3ffd1, 0, 0 }, { 22, 0x3fffaf, 0, 0 },
+ { 22, 0x3fffad, 0, 0 }, { 22, 0x3fffab, 0, 0 },
+ { 22, 0x3fffa9, 0, 0 }, { 22, 0x3fffa7, 0, 0 },
+ { 22, 0x3fffa5, 0, 0 }, { 22, 0x3fffa3, 0, 0 },
+ { 22, 0x3fffa1, 0, 0 }, { 26, 0x3ffff7f, 0, 0 },
+ { 26, 0x3ffff7d, 0, 0 }, { 26, 0x3ffff7b, 0, 0 },
+ { 26, 0x3ffff79, 0, 0 }, { 26, 0x3ffff77, 0, 0 },
+ { 26, 0x3ffff75, 0, 0 }, { 26, 0x3ffff73, 0, 0 },
+ { 26, 0x3ffff71, 0, 0 }, { 26, 0x3ffff6f, 0, 0 },
+ { 26, 0x3ffff6d, 0, 0 }, { 26, 0x3ffff6b, 0, 0 },
+ { 26, 0x3ffff69, 0, 0 }, { 26, 0x3ffff67, 0, 0 },
+ { 26, 0x3ffff65, 0, 0 }, { 26, 0x3ffff63, 0, 0 },
+ { 26, 0x3ffff61, 0, 0 }, { 31, 0x7ffffe3f, 0, 0 },
+ { 31, 0x7ffffe3d, 0, 0 }, { 31, 0x7ffffe3b, 0, 0 },
+ { 31, 0x7ffffe39, 0, 0 }, { 31, 0x7ffffe37, 0, 0 },
+ { 31, 0x7ffffe35, 0, 0 }, { 31, 0x7ffffe33, 0, 0 },
+ { 31, 0x7ffffe31, 0, 0 }, { 31, 0x7ffffe2f, 0, 0 },
+ { 31, 0x7ffffe2d, 0, 0 }, { 31, 0x7ffffe2b, 0, 0 },
+ { 31, 0x7ffffe29, 0, 0 }, { 31, 0x7ffffe27, 0, 0 },
+ { 31, 0x7ffffe25, 0, 0 }, { 31, 0x7ffffe23, 0, 0 },
+ { 31, 0x7ffffe21, 0, 0 }, { 31, 0x7ffffe1f, 0, 0 },
+ { 31, 0x7ffffe1d, 0, 0 }, { 31, 0x7ffffe1b, 0, 0 },
+ { 31, 0x7ffffe19, 0, 0 }, { 31, 0x7ffffe17, 0, 0 },
+ { 31, 0x7ffffe15, 0, 0 }, { 31, 0x7ffffe13, 0, 0 },
+ { 31, 0x7ffffe11, 0, 0 }, { 31, 0x7ffffe0f, 0, 0 },
+ { 31, 0x7ffffe0d, 0, 0 }, { 31, 0x7ffffe0b, 0, 0 },
+ { 31, 0x7ffffe09, 0, 0 }, { 31, 0x7ffffe07, 0, 0 },
+ { 31, 0x7ffffe05, 0, 0 }, { 31, 0x7ffffe03, 0, 0 },
+ { 31, 0x7ffffe01, 0, 0 }, { 28, 0xffffffa, 7, 0x7f },
+ { 28, 0xffffffa, 7, 0x7d }, { 28, 0xffffffa, 7, 0x7b },
+ { 28, 0xffffffa, 7, 0x79 }, { 28, 0xffffffa, 7, 0x77 },
+ { 28, 0xffffffa, 7, 0x75 }, { 28, 0xffffffa, 7, 0x73 },
+ { 28, 0xffffffa, 7, 0x71 }, { 28, 0xffffffa, 7, 0x6f },
+ { 28, 0xffffffa, 7, 0x6d }, { 28, 0xffffffa, 7, 0x6b },
+ { 28, 0xffffffa, 7, 0x69 }, { 28, 0xffffffa, 7, 0x67 },
+ { 28, 0xffffffa, 7, 0x65 }, { 28, 0xffffffa, 7, 0x63 },
+ { 28, 0xffffffa, 7, 0x61 }, { 28, 0xffffffa, 7, 0x5f },
+ { 28, 0xffffffa, 7, 0x5d }, { 28, 0xffffffa, 7, 0x5b },
+ { 28, 0xffffffa, 7, 0x59 }, { 28, 0xffffffa, 7, 0x57 },
+ { 28, 0xffffffa, 7, 0x55 }, { 28, 0xffffffa, 7, 0x53 },
+ { 28, 0xffffffa, 7, 0x51 }, { 28, 0xffffffa, 7, 0x4f },
+ { 28, 0xffffffa, 7, 0x4d }, { 28, 0xffffffa, 7, 0x4b },
+ { 28, 0xffffffa, 7, 0x49 }, { 28, 0xffffffa, 7, 0x47 },
+ { 28, 0xffffffa, 7, 0x45 }, { 28, 0xffffffa, 7, 0x43 },
+ { 28, 0xffffffa, 7, 0x41 }, { 28, 0xffffffa, 7, 0x3f },
+ { 28, 0xffffffa, 7, 0x3d }, { 28, 0xffffffa, 7, 0x3b },
+ { 28, 0xffffffa, 7, 0x39 }, { 28, 0xffffffa, 7, 0x37 },
+ { 28, 0xffffffa, 7, 0x35 }, { 28, 0xffffffa, 7, 0x33 },
+ { 28, 0xffffffa, 7, 0x31 }, { 28, 0xffffffa, 7, 0x2f },
+ { 28, 0xffffffa, 7, 0x2d }, { 28, 0xffffffa, 7, 0x2b },
+ { 28, 0xffffffa, 7, 0x29 }, { 28, 0xffffffa, 7, 0x27 },
+ { 28, 0xffffffa, 7, 0x25 }, { 28, 0xffffffa, 7, 0x23 },
+ { 28, 0xffffffa, 7, 0x21 }, { 28, 0xffffffa, 7, 0x1f },
+ { 28, 0xffffffa, 7, 0x1d }, { 28, 0xffffffa, 7, 0x1b },
+ { 28, 0xffffffa, 7, 0x19 }, { 28, 0xffffffa, 7, 0x17 },
+ { 28, 0xffffffa, 7, 0x15 }, { 28, 0xffffffa, 7, 0x13 },
+ { 28, 0xffffffa, 7, 0x11 }, { 28, 0xffffffa, 7, 0xf },
+ { 28, 0xffffffa, 7, 0xd }, { 28, 0xffffffa, 7, 0xb },
+ { 28, 0xffffffa, 7, 0x9 }, { 28, 0xffffffa, 7, 0x7 },
+ { 28, 0xffffffa, 7, 0x5 }, { 28, 0xffffffa, 7, 0x3 },
+ { 28, 0xffffffa, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 7 zeroes
+ */
+ {
+ { 9, 0x1f3, 0, 0 }, { 14, 0x3fef, 0, 0 },
+ { 14, 0x3fed, 0, 0 }, { 18, 0x3ffdf, 0, 0 },
+ { 18, 0x3ffdd, 0, 0 }, { 18, 0x3ffdb, 0, 0 },
+ { 18, 0x3ffd9, 0, 0 }, { 22, 0x3fffbf, 0, 0 },
+ { 22, 0x3fffbd, 0, 0 }, { 22, 0x3fffbb, 0, 0 },
+ { 22, 0x3fffb9, 0, 0 }, { 22, 0x3fffb7, 0, 0 },
+ { 22, 0x3fffb5, 0, 0 }, { 22, 0x3fffb3, 0, 0 },
+ { 22, 0x3fffb1, 0, 0 }, { 27, 0x7ffff1f, 0, 0 },
+ { 27, 0x7ffff1d, 0, 0 }, { 27, 0x7ffff1b, 0, 0 },
+ { 27, 0x7ffff19, 0, 0 }, { 27, 0x7ffff17, 0, 0 },
+ { 27, 0x7ffff15, 0, 0 }, { 27, 0x7ffff13, 0, 0 },
+ { 27, 0x7ffff11, 0, 0 }, { 27, 0x7ffff0f, 0, 0 },
+ { 27, 0x7ffff0d, 0, 0 }, { 27, 0x7ffff0b, 0, 0 },
+ { 27, 0x7ffff09, 0, 0 }, { 27, 0x7ffff07, 0, 0 },
+ { 27, 0x7ffff05, 0, 0 }, { 27, 0x7ffff03, 0, 0 },
+ { 27, 0x7ffff01, 0, 0 }, { 31, 0x7ffffe7f, 0, 0 },
+ { 31, 0x7ffffe7d, 0, 0 }, { 31, 0x7ffffe7b, 0, 0 },
+ { 31, 0x7ffffe79, 0, 0 }, { 31, 0x7ffffe77, 0, 0 },
+ { 31, 0x7ffffe75, 0, 0 }, { 31, 0x7ffffe73, 0, 0 },
+ { 31, 0x7ffffe71, 0, 0 }, { 31, 0x7ffffe6f, 0, 0 },
+ { 31, 0x7ffffe6d, 0, 0 }, { 31, 0x7ffffe6b, 0, 0 },
+ { 31, 0x7ffffe69, 0, 0 }, { 31, 0x7ffffe67, 0, 0 },
+ { 31, 0x7ffffe65, 0, 0 }, { 31, 0x7ffffe63, 0, 0 },
+ { 31, 0x7ffffe61, 0, 0 }, { 31, 0x7ffffe5f, 0, 0 },
+ { 31, 0x7ffffe5d, 0, 0 }, { 31, 0x7ffffe5b, 0, 0 },
+ { 31, 0x7ffffe59, 0, 0 }, { 31, 0x7ffffe57, 0, 0 },
+ { 31, 0x7ffffe55, 0, 0 }, { 31, 0x7ffffe53, 0, 0 },
+ { 31, 0x7ffffe51, 0, 0 }, { 31, 0x7ffffe4f, 0, 0 },
+ { 31, 0x7ffffe4d, 0, 0 }, { 31, 0x7ffffe4b, 0, 0 },
+ { 31, 0x7ffffe49, 0, 0 }, { 31, 0x7ffffe47, 0, 0 },
+ { 31, 0x7ffffe45, 0, 0 }, { 31, 0x7ffffe43, 0, 0 },
+ { 31, 0x7ffffe41, 0, 0 }, { 28, 0xffffffb, 7, 0x7f },
+ { 28, 0xffffffb, 7, 0x7d }, { 28, 0xffffffb, 7, 0x7b },
+ { 28, 0xffffffb, 7, 0x79 }, { 28, 0xffffffb, 7, 0x77 },
+ { 28, 0xffffffb, 7, 0x75 }, { 28, 0xffffffb, 7, 0x73 },
+ { 28, 0xffffffb, 7, 0x71 }, { 28, 0xffffffb, 7, 0x6f },
+ { 28, 0xffffffb, 7, 0x6d }, { 28, 0xffffffb, 7, 0x6b },
+ { 28, 0xffffffb, 7, 0x69 }, { 28, 0xffffffb, 7, 0x67 },
+ { 28, 0xffffffb, 7, 0x65 }, { 28, 0xffffffb, 7, 0x63 },
+ { 28, 0xffffffb, 7, 0x61 }, { 28, 0xffffffb, 7, 0x5f },
+ { 28, 0xffffffb, 7, 0x5d }, { 28, 0xffffffb, 7, 0x5b },
+ { 28, 0xffffffb, 7, 0x59 }, { 28, 0xffffffb, 7, 0x57 },
+ { 28, 0xffffffb, 7, 0x55 }, { 28, 0xffffffb, 7, 0x53 },
+ { 28, 0xffffffb, 7, 0x51 }, { 28, 0xffffffb, 7, 0x4f },
+ { 28, 0xffffffb, 7, 0x4d }, { 28, 0xffffffb, 7, 0x4b },
+ { 28, 0xffffffb, 7, 0x49 }, { 28, 0xffffffb, 7, 0x47 },
+ { 28, 0xffffffb, 7, 0x45 }, { 28, 0xffffffb, 7, 0x43 },
+ { 28, 0xffffffb, 7, 0x41 }, { 28, 0xffffffb, 7, 0x3f },
+ { 28, 0xffffffb, 7, 0x3d }, { 28, 0xffffffb, 7, 0x3b },
+ { 28, 0xffffffb, 7, 0x39 }, { 28, 0xffffffb, 7, 0x37 },
+ { 28, 0xffffffb, 7, 0x35 }, { 28, 0xffffffb, 7, 0x33 },
+ { 28, 0xffffffb, 7, 0x31 }, { 28, 0xffffffb, 7, 0x2f },
+ { 28, 0xffffffb, 7, 0x2d }, { 28, 0xffffffb, 7, 0x2b },
+ { 28, 0xffffffb, 7, 0x29 }, { 28, 0xffffffb, 7, 0x27 },
+ { 28, 0xffffffb, 7, 0x25 }, { 28, 0xffffffb, 7, 0x23 },
+ { 28, 0xffffffb, 7, 0x21 }, { 28, 0xffffffb, 7, 0x1f },
+ { 28, 0xffffffb, 7, 0x1d }, { 28, 0xffffffb, 7, 0x1b },
+ { 28, 0xffffffb, 7, 0x19 }, { 28, 0xffffffb, 7, 0x17 },
+ { 28, 0xffffffb, 7, 0x15 }, { 28, 0xffffffb, 7, 0x13 },
+ { 28, 0xffffffb, 7, 0x11 }, { 28, 0xffffffb, 7, 0xf },
+ { 28, 0xffffffb, 7, 0xd }, { 28, 0xffffffb, 7, 0xb },
+ { 28, 0xffffffb, 7, 0x9 }, { 28, 0xffffffb, 7, 0x7 },
+ { 28, 0xffffffb, 7, 0x5 }, { 28, 0xffffffb, 7, 0x3 },
+ { 28, 0xffffffb, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 8 zeroes
+ */
+ {
+ { 9, 0x1f5, 0, 0 }, { 15, 0x7fe3, 0, 0 },
+ { 15, 0x7fe1, 0, 0 }, { 19, 0x7ffc7, 0, 0 },
+ { 19, 0x7ffc5, 0, 0 }, { 19, 0x7ffc3, 0, 0 },
+ { 19, 0x7ffc1, 0, 0 }, { 23, 0x7fff8f, 0, 0 },
+ { 23, 0x7fff8d, 0, 0 }, { 23, 0x7fff8b, 0, 0 },
+ { 23, 0x7fff89, 0, 0 }, { 23, 0x7fff87, 0, 0 },
+ { 23, 0x7fff85, 0, 0 }, { 23, 0x7fff83, 0, 0 },
+ { 23, 0x7fff81, 0, 0 }, { 27, 0x7ffff3f, 0, 0 },
+ { 27, 0x7ffff3d, 0, 0 }, { 27, 0x7ffff3b, 0, 0 },
+ { 27, 0x7ffff39, 0, 0 }, { 27, 0x7ffff37, 0, 0 },
+ { 27, 0x7ffff35, 0, 0 }, { 27, 0x7ffff33, 0, 0 },
+ { 27, 0x7ffff31, 0, 0 }, { 27, 0x7ffff2f, 0, 0 },
+ { 27, 0x7ffff2d, 0, 0 }, { 27, 0x7ffff2b, 0, 0 },
+ { 27, 0x7ffff29, 0, 0 }, { 27, 0x7ffff27, 0, 0 },
+ { 27, 0x7ffff25, 0, 0 }, { 27, 0x7ffff23, 0, 0 },
+ { 27, 0x7ffff21, 0, 0 }, { 31, 0x7ffffebf, 0, 0 },
+ { 31, 0x7ffffebd, 0, 0 }, { 31, 0x7ffffebb, 0, 0 },
+ { 31, 0x7ffffeb9, 0, 0 }, { 31, 0x7ffffeb7, 0, 0 },
+ { 31, 0x7ffffeb5, 0, 0 }, { 31, 0x7ffffeb3, 0, 0 },
+ { 31, 0x7ffffeb1, 0, 0 }, { 31, 0x7ffffeaf, 0, 0 },
+ { 31, 0x7ffffead, 0, 0 }, { 31, 0x7ffffeab, 0, 0 },
+ { 31, 0x7ffffea9, 0, 0 }, { 31, 0x7ffffea7, 0, 0 },
+ { 31, 0x7ffffea5, 0, 0 }, { 31, 0x7ffffea3, 0, 0 },
+ { 31, 0x7ffffea1, 0, 0 }, { 31, 0x7ffffe9f, 0, 0 },
+ { 31, 0x7ffffe9d, 0, 0 }, { 31, 0x7ffffe9b, 0, 0 },
+ { 31, 0x7ffffe99, 0, 0 }, { 31, 0x7ffffe97, 0, 0 },
+ { 31, 0x7ffffe95, 0, 0 }, { 31, 0x7ffffe93, 0, 0 },
+ { 31, 0x7ffffe91, 0, 0 }, { 31, 0x7ffffe8f, 0, 0 },
+ { 31, 0x7ffffe8d, 0, 0 }, { 31, 0x7ffffe8b, 0, 0 },
+ { 31, 0x7ffffe89, 0, 0 }, { 31, 0x7ffffe87, 0, 0 },
+ { 31, 0x7ffffe85, 0, 0 }, { 31, 0x7ffffe83, 0, 0 },
+ { 31, 0x7ffffe81, 0, 0 }, { 29, 0x1ffffff8, 7, 0x7f },
+ { 29, 0x1ffffff8, 7, 0x7d }, { 29, 0x1ffffff8, 7, 0x7b },
+ { 29, 0x1ffffff8, 7, 0x79 }, { 29, 0x1ffffff8, 7, 0x77 },
+ { 29, 0x1ffffff8, 7, 0x75 }, { 29, 0x1ffffff8, 7, 0x73 },
+ { 29, 0x1ffffff8, 7, 0x71 }, { 29, 0x1ffffff8, 7, 0x6f },
+ { 29, 0x1ffffff8, 7, 0x6d }, { 29, 0x1ffffff8, 7, 0x6b },
+ { 29, 0x1ffffff8, 7, 0x69 }, { 29, 0x1ffffff8, 7, 0x67 },
+ { 29, 0x1ffffff8, 7, 0x65 }, { 29, 0x1ffffff8, 7, 0x63 },
+ { 29, 0x1ffffff8, 7, 0x61 }, { 29, 0x1ffffff8, 7, 0x5f },
+ { 29, 0x1ffffff8, 7, 0x5d }, { 29, 0x1ffffff8, 7, 0x5b },
+ { 29, 0x1ffffff8, 7, 0x59 }, { 29, 0x1ffffff8, 7, 0x57 },
+ { 29, 0x1ffffff8, 7, 0x55 }, { 29, 0x1ffffff8, 7, 0x53 },
+ { 29, 0x1ffffff8, 7, 0x51 }, { 29, 0x1ffffff8, 7, 0x4f },
+ { 29, 0x1ffffff8, 7, 0x4d }, { 29, 0x1ffffff8, 7, 0x4b },
+ { 29, 0x1ffffff8, 7, 0x49 }, { 29, 0x1ffffff8, 7, 0x47 },
+ { 29, 0x1ffffff8, 7, 0x45 }, { 29, 0x1ffffff8, 7, 0x43 },
+ { 29, 0x1ffffff8, 7, 0x41 }, { 29, 0x1ffffff8, 7, 0x3f },
+ { 29, 0x1ffffff8, 7, 0x3d }, { 29, 0x1ffffff8, 7, 0x3b },
+ { 29, 0x1ffffff8, 7, 0x39 }, { 29, 0x1ffffff8, 7, 0x37 },
+ { 29, 0x1ffffff8, 7, 0x35 }, { 29, 0x1ffffff8, 7, 0x33 },
+ { 29, 0x1ffffff8, 7, 0x31 }, { 29, 0x1ffffff8, 7, 0x2f },
+ { 29, 0x1ffffff8, 7, 0x2d }, { 29, 0x1ffffff8, 7, 0x2b },
+ { 29, 0x1ffffff8, 7, 0x29 }, { 29, 0x1ffffff8, 7, 0x27 },
+ { 29, 0x1ffffff8, 7, 0x25 }, { 29, 0x1ffffff8, 7, 0x23 },
+ { 29, 0x1ffffff8, 7, 0x21 }, { 29, 0x1ffffff8, 7, 0x1f },
+ { 29, 0x1ffffff8, 7, 0x1d }, { 29, 0x1ffffff8, 7, 0x1b },
+ { 29, 0x1ffffff8, 7, 0x19 }, { 29, 0x1ffffff8, 7, 0x17 },
+ { 29, 0x1ffffff8, 7, 0x15 }, { 29, 0x1ffffff8, 7, 0x13 },
+ { 29, 0x1ffffff8, 7, 0x11 }, { 29, 0x1ffffff8, 7, 0xf },
+ { 29, 0x1ffffff8, 7, 0xd }, { 29, 0x1ffffff8, 7, 0xb },
+ { 29, 0x1ffffff8, 7, 0x9 }, { 29, 0x1ffffff8, 7, 0x7 },
+ { 29, 0x1ffffff8, 7, 0x5 }, { 29, 0x1ffffff8, 7, 0x3 },
+ { 29, 0x1ffffff8, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 9 zeroes
+ */
+ {
+ { 11, 0x7f7, 0, 0 }, { 15, 0x7fe7, 0, 0 },
+ { 15, 0x7fe5, 0, 0 }, { 19, 0x7ffcf, 0, 0 },
+ { 19, 0x7ffcd, 0, 0 }, { 19, 0x7ffcb, 0, 0 },
+ { 19, 0x7ffc9, 0, 0 }, { 23, 0x7fff9f, 0, 0 },
+ { 23, 0x7fff9d, 0, 0 }, { 23, 0x7fff9b, 0, 0 },
+ { 23, 0x7fff99, 0, 0 }, { 23, 0x7fff97, 0, 0 },
+ { 23, 0x7fff95, 0, 0 }, { 23, 0x7fff93, 0, 0 },
+ { 23, 0x7fff91, 0, 0 }, { 27, 0x7ffff5f, 0, 0 },
+ { 27, 0x7ffff5d, 0, 0 }, { 27, 0x7ffff5b, 0, 0 },
+ { 27, 0x7ffff59, 0, 0 }, { 27, 0x7ffff57, 0, 0 },
+ { 27, 0x7ffff55, 0, 0 }, { 27, 0x7ffff53, 0, 0 },
+ { 27, 0x7ffff51, 0, 0 }, { 27, 0x7ffff4f, 0, 0 },
+ { 27, 0x7ffff4d, 0, 0 }, { 27, 0x7ffff4b, 0, 0 },
+ { 27, 0x7ffff49, 0, 0 }, { 27, 0x7ffff47, 0, 0 },
+ { 27, 0x7ffff45, 0, 0 }, { 27, 0x7ffff43, 0, 0 },
+ { 27, 0x7ffff41, 0, 0 }, { 31, 0x7ffffeff, 0, 0 },
+ { 31, 0x7ffffefd, 0, 0 }, { 31, 0x7ffffefb, 0, 0 },
+ { 31, 0x7ffffef9, 0, 0 }, { 31, 0x7ffffef7, 0, 0 },
+ { 31, 0x7ffffef5, 0, 0 }, { 31, 0x7ffffef3, 0, 0 },
+ { 31, 0x7ffffef1, 0, 0 }, { 31, 0x7ffffeef, 0, 0 },
+ { 31, 0x7ffffeed, 0, 0 }, { 31, 0x7ffffeeb, 0, 0 },
+ { 31, 0x7ffffee9, 0, 0 }, { 31, 0x7ffffee7, 0, 0 },
+ { 31, 0x7ffffee5, 0, 0 }, { 31, 0x7ffffee3, 0, 0 },
+ { 31, 0x7ffffee1, 0, 0 }, { 31, 0x7ffffedf, 0, 0 },
+ { 31, 0x7ffffedd, 0, 0 }, { 31, 0x7ffffedb, 0, 0 },
+ { 31, 0x7ffffed9, 0, 0 }, { 31, 0x7ffffed7, 0, 0 },
+ { 31, 0x7ffffed5, 0, 0 }, { 31, 0x7ffffed3, 0, 0 },
+ { 31, 0x7ffffed1, 0, 0 }, { 31, 0x7ffffecf, 0, 0 },
+ { 31, 0x7ffffecd, 0, 0 }, { 31, 0x7ffffecb, 0, 0 },
+ { 31, 0x7ffffec9, 0, 0 }, { 31, 0x7ffffec7, 0, 0 },
+ { 31, 0x7ffffec5, 0, 0 }, { 31, 0x7ffffec3, 0, 0 },
+ { 31, 0x7ffffec1, 0, 0 }, { 29, 0x1ffffff9, 7, 0x7f },
+ { 29, 0x1ffffff9, 7, 0x7d }, { 29, 0x1ffffff9, 7, 0x7b },
+ { 29, 0x1ffffff9, 7, 0x79 }, { 29, 0x1ffffff9, 7, 0x77 },
+ { 29, 0x1ffffff9, 7, 0x75 }, { 29, 0x1ffffff9, 7, 0x73 },
+ { 29, 0x1ffffff9, 7, 0x71 }, { 29, 0x1ffffff9, 7, 0x6f },
+ { 29, 0x1ffffff9, 7, 0x6d }, { 29, 0x1ffffff9, 7, 0x6b },
+ { 29, 0x1ffffff9, 7, 0x69 }, { 29, 0x1ffffff9, 7, 0x67 },
+ { 29, 0x1ffffff9, 7, 0x65 }, { 29, 0x1ffffff9, 7, 0x63 },
+ { 29, 0x1ffffff9, 7, 0x61 }, { 29, 0x1ffffff9, 7, 0x5f },
+ { 29, 0x1ffffff9, 7, 0x5d }, { 29, 0x1ffffff9, 7, 0x5b },
+ { 29, 0x1ffffff9, 7, 0x59 }, { 29, 0x1ffffff9, 7, 0x57 },
+ { 29, 0x1ffffff9, 7, 0x55 }, { 29, 0x1ffffff9, 7, 0x53 },
+ { 29, 0x1ffffff9, 7, 0x51 }, { 29, 0x1ffffff9, 7, 0x4f },
+ { 29, 0x1ffffff9, 7, 0x4d }, { 29, 0x1ffffff9, 7, 0x4b },
+ { 29, 0x1ffffff9, 7, 0x49 }, { 29, 0x1ffffff9, 7, 0x47 },
+ { 29, 0x1ffffff9, 7, 0x45 }, { 29, 0x1ffffff9, 7, 0x43 },
+ { 29, 0x1ffffff9, 7, 0x41 }, { 29, 0x1ffffff9, 7, 0x3f },
+ { 29, 0x1ffffff9, 7, 0x3d }, { 29, 0x1ffffff9, 7, 0x3b },
+ { 29, 0x1ffffff9, 7, 0x39 }, { 29, 0x1ffffff9, 7, 0x37 },
+ { 29, 0x1ffffff9, 7, 0x35 }, { 29, 0x1ffffff9, 7, 0x33 },
+ { 29, 0x1ffffff9, 7, 0x31 }, { 29, 0x1ffffff9, 7, 0x2f },
+ { 29, 0x1ffffff9, 7, 0x2d }, { 29, 0x1ffffff9, 7, 0x2b },
+ { 29, 0x1ffffff9, 7, 0x29 }, { 29, 0x1ffffff9, 7, 0x27 },
+ { 29, 0x1ffffff9, 7, 0x25 }, { 29, 0x1ffffff9, 7, 0x23 },
+ { 29, 0x1ffffff9, 7, 0x21 }, { 29, 0x1ffffff9, 7, 0x1f },
+ { 29, 0x1ffffff9, 7, 0x1d }, { 29, 0x1ffffff9, 7, 0x1b },
+ { 29, 0x1ffffff9, 7, 0x19 }, { 29, 0x1ffffff9, 7, 0x17 },
+ { 29, 0x1ffffff9, 7, 0x15 }, { 29, 0x1ffffff9, 7, 0x13 },
+ { 29, 0x1ffffff9, 7, 0x11 }, { 29, 0x1ffffff9, 7, 0xf },
+ { 29, 0x1ffffff9, 7, 0xd }, { 29, 0x1ffffff9, 7, 0xb },
+ { 29, 0x1ffffff9, 7, 0x9 }, { 29, 0x1ffffff9, 7, 0x7 },
+ { 29, 0x1ffffff9, 7, 0x5 }, { 29, 0x1ffffff9, 7, 0x3 },
+ { 29, 0x1ffffff9, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 10 zeroes
+ */
+ {
+ { 12, 0xff1, 0, 0 }, { 15, 0x7feb, 0, 0 },
+ { 15, 0x7fe9, 0, 0 }, { 19, 0x7ffd7, 0, 0 },
+ { 19, 0x7ffd5, 0, 0 }, { 19, 0x7ffd3, 0, 0 },
+ { 19, 0x7ffd1, 0, 0 }, { 23, 0x7fffaf, 0, 0 },
+ { 23, 0x7fffad, 0, 0 }, { 23, 0x7fffab, 0, 0 },
+ { 23, 0x7fffa9, 0, 0 }, { 23, 0x7fffa7, 0, 0 },
+ { 23, 0x7fffa5, 0, 0 }, { 23, 0x7fffa3, 0, 0 },
+ { 23, 0x7fffa1, 0, 0 }, { 27, 0x7ffff7f, 0, 0 },
+ { 27, 0x7ffff7d, 0, 0 }, { 27, 0x7ffff7b, 0, 0 },
+ { 27, 0x7ffff79, 0, 0 }, { 27, 0x7ffff77, 0, 0 },
+ { 27, 0x7ffff75, 0, 0 }, { 27, 0x7ffff73, 0, 0 },
+ { 27, 0x7ffff71, 0, 0 }, { 27, 0x7ffff6f, 0, 0 },
+ { 27, 0x7ffff6d, 0, 0 }, { 27, 0x7ffff6b, 0, 0 },
+ { 27, 0x7ffff69, 0, 0 }, { 27, 0x7ffff67, 0, 0 },
+ { 27, 0x7ffff65, 0, 0 }, { 27, 0x7ffff63, 0, 0 },
+ { 27, 0x7ffff61, 0, 0 }, { 32, 0xfffffe3f, 0, 0 },
+ { 32, 0xfffffe3d, 0, 0 }, { 32, 0xfffffe3b, 0, 0 },
+ { 32, 0xfffffe39, 0, 0 }, { 32, 0xfffffe37, 0, 0 },
+ { 32, 0xfffffe35, 0, 0 }, { 32, 0xfffffe33, 0, 0 },
+ { 32, 0xfffffe31, 0, 0 }, { 32, 0xfffffe2f, 0, 0 },
+ { 32, 0xfffffe2d, 0, 0 }, { 32, 0xfffffe2b, 0, 0 },
+ { 32, 0xfffffe29, 0, 0 }, { 32, 0xfffffe27, 0, 0 },
+ { 32, 0xfffffe25, 0, 0 }, { 32, 0xfffffe23, 0, 0 },
+ { 32, 0xfffffe21, 0, 0 }, { 32, 0xfffffe1f, 0, 0 },
+ { 32, 0xfffffe1d, 0, 0 }, { 32, 0xfffffe1b, 0, 0 },
+ { 32, 0xfffffe19, 0, 0 }, { 32, 0xfffffe17, 0, 0 },
+ { 32, 0xfffffe15, 0, 0 }, { 32, 0xfffffe13, 0, 0 },
+ { 32, 0xfffffe11, 0, 0 }, { 32, 0xfffffe0f, 0, 0 },
+ { 32, 0xfffffe0d, 0, 0 }, { 32, 0xfffffe0b, 0, 0 },
+ { 32, 0xfffffe09, 0, 0 }, { 32, 0xfffffe07, 0, 0 },
+ { 32, 0xfffffe05, 0, 0 }, { 32, 0xfffffe03, 0, 0 },
+ { 32, 0xfffffe01, 0, 0 }, { 29, 0x1ffffffa, 7, 0x7f },
+ { 29, 0x1ffffffa, 7, 0x7d }, { 29, 0x1ffffffa, 7, 0x7b },
+ { 29, 0x1ffffffa, 7, 0x79 }, { 29, 0x1ffffffa, 7, 0x77 },
+ { 29, 0x1ffffffa, 7, 0x75 }, { 29, 0x1ffffffa, 7, 0x73 },
+ { 29, 0x1ffffffa, 7, 0x71 }, { 29, 0x1ffffffa, 7, 0x6f },
+ { 29, 0x1ffffffa, 7, 0x6d }, { 29, 0x1ffffffa, 7, 0x6b },
+ { 29, 0x1ffffffa, 7, 0x69 }, { 29, 0x1ffffffa, 7, 0x67 },
+ { 29, 0x1ffffffa, 7, 0x65 }, { 29, 0x1ffffffa, 7, 0x63 },
+ { 29, 0x1ffffffa, 7, 0x61 }, { 29, 0x1ffffffa, 7, 0x5f },
+ { 29, 0x1ffffffa, 7, 0x5d }, { 29, 0x1ffffffa, 7, 0x5b },
+ { 29, 0x1ffffffa, 7, 0x59 }, { 29, 0x1ffffffa, 7, 0x57 },
+ { 29, 0x1ffffffa, 7, 0x55 }, { 29, 0x1ffffffa, 7, 0x53 },
+ { 29, 0x1ffffffa, 7, 0x51 }, { 29, 0x1ffffffa, 7, 0x4f },
+ { 29, 0x1ffffffa, 7, 0x4d }, { 29, 0x1ffffffa, 7, 0x4b },
+ { 29, 0x1ffffffa, 7, 0x49 }, { 29, 0x1ffffffa, 7, 0x47 },
+ { 29, 0x1ffffffa, 7, 0x45 }, { 29, 0x1ffffffa, 7, 0x43 },
+ { 29, 0x1ffffffa, 7, 0x41 }, { 29, 0x1ffffffa, 7, 0x3f },
+ { 29, 0x1ffffffa, 7, 0x3d }, { 29, 0x1ffffffa, 7, 0x3b },
+ { 29, 0x1ffffffa, 7, 0x39 }, { 29, 0x1ffffffa, 7, 0x37 },
+ { 29, 0x1ffffffa, 7, 0x35 }, { 29, 0x1ffffffa, 7, 0x33 },
+ { 29, 0x1ffffffa, 7, 0x31 }, { 29, 0x1ffffffa, 7, 0x2f },
+ { 29, 0x1ffffffa, 7, 0x2d }, { 29, 0x1ffffffa, 7, 0x2b },
+ { 29, 0x1ffffffa, 7, 0x29 }, { 29, 0x1ffffffa, 7, 0x27 },
+ { 29, 0x1ffffffa, 7, 0x25 }, { 29, 0x1ffffffa, 7, 0x23 },
+ { 29, 0x1ffffffa, 7, 0x21 }, { 29, 0x1ffffffa, 7, 0x1f },
+ { 29, 0x1ffffffa, 7, 0x1d }, { 29, 0x1ffffffa, 7, 0x1b },
+ { 29, 0x1ffffffa, 7, 0x19 }, { 29, 0x1ffffffa, 7, 0x17 },
+ { 29, 0x1ffffffa, 7, 0x15 }, { 29, 0x1ffffffa, 7, 0x13 },
+ { 29, 0x1ffffffa, 7, 0x11 }, { 29, 0x1ffffffa, 7, 0xf },
+ { 29, 0x1ffffffa, 7, 0xd }, { 29, 0x1ffffffa, 7, 0xb },
+ { 29, 0x1ffffffa, 7, 0x9 }, { 29, 0x1ffffffa, 7, 0x7 },
+ { 29, 0x1ffffffa, 7, 0x5 }, { 29, 0x1ffffffa, 7, 0x3 },
+ { 29, 0x1ffffffa, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 11 zeroes
+ */
+ {
+ { 12, 0xff3, 0, 0 }, { 15, 0x7fef, 0, 0 },
+ { 15, 0x7fed, 0, 0 }, { 19, 0x7ffdf, 0, 0 },
+ { 19, 0x7ffdd, 0, 0 }, { 19, 0x7ffdb, 0, 0 },
+ { 19, 0x7ffd9, 0, 0 }, { 23, 0x7fffbf, 0, 0 },
+ { 23, 0x7fffbd, 0, 0 }, { 23, 0x7fffbb, 0, 0 },
+ { 23, 0x7fffb9, 0, 0 }, { 23, 0x7fffb7, 0, 0 },
+ { 23, 0x7fffb5, 0, 0 }, { 23, 0x7fffb3, 0, 0 },
+ { 23, 0x7fffb1, 0, 0 }, { 28, 0xfffff1f, 0, 0 },
+ { 28, 0xfffff1d, 0, 0 }, { 28, 0xfffff1b, 0, 0 },
+ { 28, 0xfffff19, 0, 0 }, { 28, 0xfffff17, 0, 0 },
+ { 28, 0xfffff15, 0, 0 }, { 28, 0xfffff13, 0, 0 },
+ { 28, 0xfffff11, 0, 0 }, { 28, 0xfffff0f, 0, 0 },
+ { 28, 0xfffff0d, 0, 0 }, { 28, 0xfffff0b, 0, 0 },
+ { 28, 0xfffff09, 0, 0 }, { 28, 0xfffff07, 0, 0 },
+ { 28, 0xfffff05, 0, 0 }, { 28, 0xfffff03, 0, 0 },
+ { 28, 0xfffff01, 0, 0 }, { 32, 0xfffffe7f, 0, 0 },
+ { 32, 0xfffffe7d, 0, 0 }, { 32, 0xfffffe7b, 0, 0 },
+ { 32, 0xfffffe79, 0, 0 }, { 32, 0xfffffe77, 0, 0 },
+ { 32, 0xfffffe75, 0, 0 }, { 32, 0xfffffe73, 0, 0 },
+ { 32, 0xfffffe71, 0, 0 }, { 32, 0xfffffe6f, 0, 0 },
+ { 32, 0xfffffe6d, 0, 0 }, { 32, 0xfffffe6b, 0, 0 },
+ { 32, 0xfffffe69, 0, 0 }, { 32, 0xfffffe67, 0, 0 },
+ { 32, 0xfffffe65, 0, 0 }, { 32, 0xfffffe63, 0, 0 },
+ { 32, 0xfffffe61, 0, 0 }, { 32, 0xfffffe5f, 0, 0 },
+ { 32, 0xfffffe5d, 0, 0 }, { 32, 0xfffffe5b, 0, 0 },
+ { 32, 0xfffffe59, 0, 0 }, { 32, 0xfffffe57, 0, 0 },
+ { 32, 0xfffffe55, 0, 0 }, { 32, 0xfffffe53, 0, 0 },
+ { 32, 0xfffffe51, 0, 0 }, { 32, 0xfffffe4f, 0, 0 },
+ { 32, 0xfffffe4d, 0, 0 }, { 32, 0xfffffe4b, 0, 0 },
+ { 32, 0xfffffe49, 0, 0 }, { 32, 0xfffffe47, 0, 0 },
+ { 32, 0xfffffe45, 0, 0 }, { 32, 0xfffffe43, 0, 0 },
+ { 32, 0xfffffe41, 0, 0 }, { 29, 0x1ffffffb, 7, 0x7f },
+ { 29, 0x1ffffffb, 7, 0x7d }, { 29, 0x1ffffffb, 7, 0x7b },
+ { 29, 0x1ffffffb, 7, 0x79 }, { 29, 0x1ffffffb, 7, 0x77 },
+ { 29, 0x1ffffffb, 7, 0x75 }, { 29, 0x1ffffffb, 7, 0x73 },
+ { 29, 0x1ffffffb, 7, 0x71 }, { 29, 0x1ffffffb, 7, 0x6f },
+ { 29, 0x1ffffffb, 7, 0x6d }, { 29, 0x1ffffffb, 7, 0x6b },
+ { 29, 0x1ffffffb, 7, 0x69 }, { 29, 0x1ffffffb, 7, 0x67 },
+ { 29, 0x1ffffffb, 7, 0x65 }, { 29, 0x1ffffffb, 7, 0x63 },
+ { 29, 0x1ffffffb, 7, 0x61 }, { 29, 0x1ffffffb, 7, 0x5f },
+ { 29, 0x1ffffffb, 7, 0x5d }, { 29, 0x1ffffffb, 7, 0x5b },
+ { 29, 0x1ffffffb, 7, 0x59 }, { 29, 0x1ffffffb, 7, 0x57 },
+ { 29, 0x1ffffffb, 7, 0x55 }, { 29, 0x1ffffffb, 7, 0x53 },
+ { 29, 0x1ffffffb, 7, 0x51 }, { 29, 0x1ffffffb, 7, 0x4f },
+ { 29, 0x1ffffffb, 7, 0x4d }, { 29, 0x1ffffffb, 7, 0x4b },
+ { 29, 0x1ffffffb, 7, 0x49 }, { 29, 0x1ffffffb, 7, 0x47 },
+ { 29, 0x1ffffffb, 7, 0x45 }, { 29, 0x1ffffffb, 7, 0x43 },
+ { 29, 0x1ffffffb, 7, 0x41 }, { 29, 0x1ffffffb, 7, 0x3f },
+ { 29, 0x1ffffffb, 7, 0x3d }, { 29, 0x1ffffffb, 7, 0x3b },
+ { 29, 0x1ffffffb, 7, 0x39 }, { 29, 0x1ffffffb, 7, 0x37 },
+ { 29, 0x1ffffffb, 7, 0x35 }, { 29, 0x1ffffffb, 7, 0x33 },
+ { 29, 0x1ffffffb, 7, 0x31 }, { 29, 0x1ffffffb, 7, 0x2f },
+ { 29, 0x1ffffffb, 7, 0x2d }, { 29, 0x1ffffffb, 7, 0x2b },
+ { 29, 0x1ffffffb, 7, 0x29 }, { 29, 0x1ffffffb, 7, 0x27 },
+ { 29, 0x1ffffffb, 7, 0x25 }, { 29, 0x1ffffffb, 7, 0x23 },
+ { 29, 0x1ffffffb, 7, 0x21 }, { 29, 0x1ffffffb, 7, 0x1f },
+ { 29, 0x1ffffffb, 7, 0x1d }, { 29, 0x1ffffffb, 7, 0x1b },
+ { 29, 0x1ffffffb, 7, 0x19 }, { 29, 0x1ffffffb, 7, 0x17 },
+ { 29, 0x1ffffffb, 7, 0x15 }, { 29, 0x1ffffffb, 7, 0x13 },
+ { 29, 0x1ffffffb, 7, 0x11 }, { 29, 0x1ffffffb, 7, 0xf },
+ { 29, 0x1ffffffb, 7, 0xd }, { 29, 0x1ffffffb, 7, 0xb },
+ { 29, 0x1ffffffb, 7, 0x9 }, { 29, 0x1ffffffb, 7, 0x7 },
+ { 29, 0x1ffffffb, 7, 0x5 }, { 29, 0x1ffffffb, 7, 0x3 },
+ { 29, 0x1ffffffb, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 12 zeroes
+ */
+ {
+ { 12, 0xff5, 0, 0 }, { 16, 0xffe3, 0, 0 },
+ { 16, 0xffe1, 0, 0 }, { 20, 0xfffc7, 0, 0 },
+ { 20, 0xfffc5, 0, 0 }, { 20, 0xfffc3, 0, 0 },
+ { 20, 0xfffc1, 0, 0 }, { 24, 0xffff8f, 0, 0 },
+ { 24, 0xffff8d, 0, 0 }, { 24, 0xffff8b, 0, 0 },
+ { 24, 0xffff89, 0, 0 }, { 24, 0xffff87, 0, 0 },
+ { 24, 0xffff85, 0, 0 }, { 24, 0xffff83, 0, 0 },
+ { 24, 0xffff81, 0, 0 }, { 28, 0xfffff3f, 0, 0 },
+ { 28, 0xfffff3d, 0, 0 }, { 28, 0xfffff3b, 0, 0 },
+ { 28, 0xfffff39, 0, 0 }, { 28, 0xfffff37, 0, 0 },
+ { 28, 0xfffff35, 0, 0 }, { 28, 0xfffff33, 0, 0 },
+ { 28, 0xfffff31, 0, 0 }, { 28, 0xfffff2f, 0, 0 },
+ { 28, 0xfffff2d, 0, 0 }, { 28, 0xfffff2b, 0, 0 },
+ { 28, 0xfffff29, 0, 0 }, { 28, 0xfffff27, 0, 0 },
+ { 28, 0xfffff25, 0, 0 }, { 28, 0xfffff23, 0, 0 },
+ { 28, 0xfffff21, 0, 0 }, { 32, 0xfffffebf, 0, 0 },
+ { 32, 0xfffffebd, 0, 0 }, { 32, 0xfffffebb, 0, 0 },
+ { 32, 0xfffffeb9, 0, 0 }, { 32, 0xfffffeb7, 0, 0 },
+ { 32, 0xfffffeb5, 0, 0 }, { 32, 0xfffffeb3, 0, 0 },
+ { 32, 0xfffffeb1, 0, 0 }, { 32, 0xfffffeaf, 0, 0 },
+ { 32, 0xfffffead, 0, 0 }, { 32, 0xfffffeab, 0, 0 },
+ { 32, 0xfffffea9, 0, 0 }, { 32, 0xfffffea7, 0, 0 },
+ { 32, 0xfffffea5, 0, 0 }, { 32, 0xfffffea3, 0, 0 },
+ { 32, 0xfffffea1, 0, 0 }, { 32, 0xfffffe9f, 0, 0 },
+ { 32, 0xfffffe9d, 0, 0 }, { 32, 0xfffffe9b, 0, 0 },
+ { 32, 0xfffffe99, 0, 0 }, { 32, 0xfffffe97, 0, 0 },
+ { 32, 0xfffffe95, 0, 0 }, { 32, 0xfffffe93, 0, 0 },
+ { 32, 0xfffffe91, 0, 0 }, { 32, 0xfffffe8f, 0, 0 },
+ { 32, 0xfffffe8d, 0, 0 }, { 32, 0xfffffe8b, 0, 0 },
+ { 32, 0xfffffe89, 0, 0 }, { 32, 0xfffffe87, 0, 0 },
+ { 32, 0xfffffe85, 0, 0 }, { 32, 0xfffffe83, 0, 0 },
+ { 32, 0xfffffe81, 0, 0 }, { 30, 0x1fff7400, 7, 0x7f },
+ { 30, 0x1fff7400, 7, 0x7d }, { 30, 0x1fff7400, 7, 0x7b },
+ { 30, 0x1fff7400, 7, 0x79 }, { 30, 0x1fff7400, 7, 0x77 },
+ { 30, 0x1fff7400, 7, 0x75 }, { 30, 0x1fff7400, 7, 0x73 },
+ { 30, 0x1fff7400, 7, 0x71 }, { 30, 0x1fff7400, 7, 0x6f },
+ { 30, 0x1fff7400, 7, 0x6d }, { 30, 0x1fff7400, 7, 0x6b },
+ { 30, 0x1fff7400, 7, 0x69 }, { 30, 0x1fff7400, 7, 0x67 },
+ { 30, 0x1fff7400, 7, 0x65 }, { 30, 0x1fff7400, 7, 0x63 },
+ { 30, 0x1fff7400, 7, 0x61 }, { 30, 0x1fff7400, 7, 0x5f },
+ { 30, 0x1fff7400, 7, 0x5d }, { 30, 0x1fff7400, 7, 0x5b },
+ { 30, 0x1fff7400, 7, 0x59 }, { 30, 0x1fff7400, 7, 0x57 },
+ { 30, 0x1fff7400, 7, 0x55 }, { 30, 0x1fff7400, 7, 0x53 },
+ { 30, 0x1fff7400, 7, 0x51 }, { 30, 0x1fff7400, 7, 0x4f },
+ { 30, 0x1fff7400, 7, 0x4d }, { 30, 0x1fff7400, 7, 0x4b },
+ { 30, 0x1fff7400, 7, 0x49 }, { 30, 0x1fff7400, 7, 0x47 },
+ { 30, 0x1fff7400, 7, 0x45 }, { 30, 0x1fff7400, 7, 0x43 },
+ { 30, 0x1fff7400, 7, 0x41 }, { 30, 0x1fff7400, 7, 0x3f },
+ { 30, 0x1fff7400, 7, 0x3d }, { 30, 0x1fff7400, 7, 0x3b },
+ { 30, 0x1fff7400, 7, 0x39 }, { 30, 0x1fff7400, 7, 0x37 },
+ { 30, 0x1fff7400, 7, 0x35 }, { 30, 0x1fff7400, 7, 0x33 },
+ { 30, 0x1fff7400, 7, 0x31 }, { 30, 0x1fff7400, 7, 0x2f },
+ { 30, 0x1fff7400, 7, 0x2d }, { 30, 0x1fff7400, 7, 0x2b },
+ { 30, 0x1fff7400, 7, 0x29 }, { 30, 0x1fff7400, 7, 0x27 },
+ { 30, 0x1fff7400, 7, 0x25 }, { 30, 0x1fff7400, 7, 0x23 },
+ { 30, 0x1fff7400, 7, 0x21 }, { 30, 0x1fff7400, 7, 0x1f },
+ { 30, 0x1fff7400, 7, 0x1d }, { 30, 0x1fff7400, 7, 0x1b },
+ { 30, 0x1fff7400, 7, 0x19 }, { 30, 0x1fff7400, 7, 0x17 },
+ { 30, 0x1fff7400, 7, 0x15 }, { 30, 0x1fff7400, 7, 0x13 },
+ { 30, 0x1fff7400, 7, 0x11 }, { 30, 0x1fff7400, 7, 0xf },
+ { 30, 0x1fff7400, 7, 0xd }, { 30, 0x1fff7400, 7, 0xb },
+ { 30, 0x1fff7400, 7, 0x9 }, { 30, 0x1fff7400, 7, 0x7 },
+ { 30, 0x1fff7400, 7, 0x5 }, { 30, 0x1fff7400, 7, 0x3 },
+ { 30, 0x1fff7400, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 13 zeroes
+ */
+ {
+ { 12, 0xff7, 0, 0 }, { 16, 0xffe7, 0, 0 },
+ { 16, 0xffe5, 0, 0 }, { 20, 0xfffcf, 0, 0 },
+ { 20, 0xfffcd, 0, 0 }, { 20, 0xfffcb, 0, 0 },
+ { 20, 0xfffc9, 0, 0 }, { 24, 0xffff9f, 0, 0 },
+ { 24, 0xffff9d, 0, 0 }, { 24, 0xffff9b, 0, 0 },
+ { 24, 0xffff99, 0, 0 }, { 24, 0xffff97, 0, 0 },
+ { 24, 0xffff95, 0, 0 }, { 24, 0xffff93, 0, 0 },
+ { 24, 0xffff91, 0, 0 }, { 28, 0xfffff5f, 0, 0 },
+ { 28, 0xfffff5d, 0, 0 }, { 28, 0xfffff5b, 0, 0 },
+ { 28, 0xfffff59, 0, 0 }, { 28, 0xfffff57, 0, 0 },
+ { 28, 0xfffff55, 0, 0 }, { 28, 0xfffff53, 0, 0 },
+ { 28, 0xfffff51, 0, 0 }, { 28, 0xfffff4f, 0, 0 },
+ { 28, 0xfffff4d, 0, 0 }, { 28, 0xfffff4b, 0, 0 },
+ { 28, 0xfffff49, 0, 0 }, { 28, 0xfffff47, 0, 0 },
+ { 28, 0xfffff45, 0, 0 }, { 28, 0xfffff43, 0, 0 },
+ { 28, 0xfffff41, 0, 0 }, { 32, 0xfffffeff, 0, 0 },
+ { 32, 0xfffffefd, 0, 0 }, { 32, 0xfffffefb, 0, 0 },
+ { 32, 0xfffffef9, 0, 0 }, { 32, 0xfffffef7, 0, 0 },
+ { 32, 0xfffffef5, 0, 0 }, { 32, 0xfffffef3, 0, 0 },
+ { 32, 0xfffffef1, 0, 0 }, { 32, 0xfffffeef, 0, 0 },
+ { 32, 0xfffffeed, 0, 0 }, { 32, 0xfffffeeb, 0, 0 },
+ { 32, 0xfffffee9, 0, 0 }, { 32, 0xfffffee7, 0, 0 },
+ { 32, 0xfffffee5, 0, 0 }, { 32, 0xfffffee3, 0, 0 },
+ { 32, 0xfffffee1, 0, 0 }, { 32, 0xfffffedf, 0, 0 },
+ { 32, 0xfffffedd, 0, 0 }, { 32, 0xfffffedb, 0, 0 },
+ { 32, 0xfffffed9, 0, 0 }, { 32, 0xfffffed7, 0, 0 },
+ { 32, 0xfffffed5, 0, 0 }, { 32, 0xfffffed3, 0, 0 },
+ { 32, 0xfffffed1, 0, 0 }, { 32, 0xfffffecf, 0, 0 },
+ { 32, 0xfffffecd, 0, 0 }, { 32, 0xfffffecb, 0, 0 },
+ { 32, 0xfffffec9, 0, 0 }, { 32, 0xfffffec7, 0, 0 },
+ { 32, 0xfffffec5, 0, 0 }, { 32, 0xfffffec3, 0, 0 },
+ { 32, 0xfffffec1, 0, 0 }, { 30, 0x3ffffff9, 7, 0x7f },
+ { 30, 0x3ffffff9, 7, 0x7d }, { 30, 0x3ffffff9, 7, 0x7b },
+ { 30, 0x3ffffff9, 7, 0x79 }, { 30, 0x3ffffff9, 7, 0x77 },
+ { 30, 0x3ffffff9, 7, 0x75 }, { 30, 0x3ffffff9, 7, 0x73 },
+ { 30, 0x3ffffff9, 7, 0x71 }, { 30, 0x3ffffff9, 7, 0x6f },
+ { 30, 0x3ffffff9, 7, 0x6d }, { 30, 0x3ffffff9, 7, 0x6b },
+ { 30, 0x3ffffff9, 7, 0x69 }, { 30, 0x3ffffff9, 7, 0x67 },
+ { 30, 0x3ffffff9, 7, 0x65 }, { 30, 0x3ffffff9, 7, 0x63 },
+ { 30, 0x3ffffff9, 7, 0x61 }, { 30, 0x3ffffff9, 7, 0x5f },
+ { 30, 0x3ffffff9, 7, 0x5d }, { 30, 0x3ffffff9, 7, 0x5b },
+ { 30, 0x3ffffff9, 7, 0x59 }, { 30, 0x3ffffff9, 7, 0x57 },
+ { 30, 0x3ffffff9, 7, 0x55 }, { 30, 0x3ffffff9, 7, 0x53 },
+ { 30, 0x3ffffff9, 7, 0x51 }, { 30, 0x3ffffff9, 7, 0x4f },
+ { 30, 0x3ffffff9, 7, 0x4d }, { 30, 0x3ffffff9, 7, 0x4b },
+ { 30, 0x3ffffff9, 7, 0x49 }, { 30, 0x3ffffff9, 7, 0x47 },
+ { 30, 0x3ffffff9, 7, 0x45 }, { 30, 0x3ffffff9, 7, 0x43 },
+ { 30, 0x3ffffff9, 7, 0x41 }, { 30, 0x3ffffff9, 7, 0x3f },
+ { 30, 0x3ffffff9, 7, 0x3d }, { 30, 0x3ffffff9, 7, 0x3b },
+ { 30, 0x3ffffff9, 7, 0x39 }, { 30, 0x3ffffff9, 7, 0x37 },
+ { 30, 0x3ffffff9, 7, 0x35 }, { 30, 0x3ffffff9, 7, 0x33 },
+ { 30, 0x3ffffff9, 7, 0x31 }, { 30, 0x3ffffff9, 7, 0x2f },
+ { 30, 0x3ffffff9, 7, 0x2d }, { 30, 0x3ffffff9, 7, 0x2b },
+ { 30, 0x3ffffff9, 7, 0x29 }, { 30, 0x3ffffff9, 7, 0x27 },
+ { 30, 0x3ffffff9, 7, 0x25 }, { 30, 0x3ffffff9, 7, 0x23 },
+ { 30, 0x3ffffff9, 7, 0x21 }, { 30, 0x3ffffff9, 7, 0x1f },
+ { 30, 0x3ffffff9, 7, 0x1d }, { 30, 0x3ffffff9, 7, 0x1b },
+ { 30, 0x3ffffff9, 7, 0x19 }, { 30, 0x3ffffff9, 7, 0x17 },
+ { 30, 0x3ffffff9, 7, 0x15 }, { 30, 0x3ffffff9, 7, 0x13 },
+ { 30, 0x3ffffff9, 7, 0x11 }, { 30, 0x3ffffff9, 7, 0xf },
+ { 30, 0x3ffffff9, 7, 0xd }, { 30, 0x3ffffff9, 7, 0xb },
+ { 30, 0x3ffffff9, 7, 0x9 }, { 30, 0x3ffffff9, 7, 0x7 },
+ { 30, 0x3ffffff9, 7, 0x5 }, { 30, 0x3ffffff9, 7, 0x3 },
+ { 30, 0x3ffffff9, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 14 zeroes
+ */
+ {
+ { 13, 0x1ff1, 0, 0 }, { 16, 0xffeb, 0, 0 },
+ { 16, 0xffe9, 0, 0 }, { 20, 0xfffd7, 0, 0 },
+ { 20, 0xfffd5, 0, 0 }, { 20, 0xfffd3, 0, 0 },
+ { 20, 0xfffd1, 0, 0 }, { 24, 0xffffaf, 0, 0 },
+ { 24, 0xffffad, 0, 0 }, { 24, 0xffffab, 0, 0 },
+ { 24, 0xffffa9, 0, 0 }, { 24, 0xffffa7, 0, 0 },
+ { 24, 0xffffa5, 0, 0 }, { 24, 0xffffa3, 0, 0 },
+ { 24, 0xffffa1, 0, 0 }, { 28, 0xfffff7f, 0, 0 },
+ { 28, 0xfffff7d, 0, 0 }, { 28, 0xfffff7b, 0, 0 },
+ { 28, 0xfffff79, 0, 0 }, { 28, 0xfffff77, 0, 0 },
+ { 28, 0xfffff75, 0, 0 }, { 28, 0xfffff73, 0, 0 },
+ { 28, 0xfffff71, 0, 0 }, { 28, 0xfffff6f, 0, 0 },
+ { 28, 0xfffff6d, 0, 0 }, { 28, 0xfffff6b, 0, 0 },
+ { 28, 0xfffff69, 0, 0 }, { 28, 0xfffff67, 0, 0 },
+ { 28, 0xfffff65, 0, 0 }, { 28, 0xfffff63, 0, 0 },
+ { 28, 0xfffff61, 0, 0 }, { 27, 0x7fffff8, 6, 0x3f },
+ { 27, 0x7fffff8, 6, 0x3d }, { 27, 0x7fffff8, 6, 0x3b },
+ { 27, 0x7fffff8, 6, 0x39 }, { 27, 0x7fffff8, 6, 0x37 },
+ { 27, 0x7fffff8, 6, 0x35 }, { 27, 0x7fffff8, 6, 0x33 },
+ { 27, 0x7fffff8, 6, 0x31 }, { 27, 0x7fffff8, 6, 0x2f },
+ { 27, 0x7fffff8, 6, 0x2d }, { 27, 0x7fffff8, 6, 0x2b },
+ { 27, 0x7fffff8, 6, 0x29 }, { 27, 0x7fffff8, 6, 0x27 },
+ { 27, 0x7fffff8, 6, 0x25 }, { 27, 0x7fffff8, 6, 0x23 },
+ { 27, 0x7fffff8, 6, 0x21 }, { 27, 0x7fffff8, 6, 0x1f },
+ { 27, 0x7fffff8, 6, 0x1d }, { 27, 0x7fffff8, 6, 0x1b },
+ { 27, 0x7fffff8, 6, 0x19 }, { 27, 0x7fffff8, 6, 0x17 },
+ { 27, 0x7fffff8, 6, 0x15 }, { 27, 0x7fffff8, 6, 0x13 },
+ { 27, 0x7fffff8, 6, 0x11 }, { 27, 0x7fffff8, 6, 0xf },
+ { 27, 0x7fffff8, 6, 0xd }, { 27, 0x7fffff8, 6, 0xb },
+ { 27, 0x7fffff8, 6, 0x9 }, { 27, 0x7fffff8, 6, 0x7 },
+ { 27, 0x7fffff8, 6, 0x5 }, { 27, 0x7fffff8, 6, 0x3 },
+ { 27, 0x7fffff8, 6, 0x1 }, { 30, 0x3ffffffa, 7, 0x7f },
+ { 30, 0x3ffffffa, 7, 0x7d }, { 30, 0x3ffffffa, 7, 0x7b },
+ { 30, 0x3ffffffa, 7, 0x79 }, { 30, 0x3ffffffa, 7, 0x77 },
+ { 30, 0x3ffffffa, 7, 0x75 }, { 30, 0x3ffffffa, 7, 0x73 },
+ { 30, 0x3ffffffa, 7, 0x71 }, { 30, 0x3ffffffa, 7, 0x6f },
+ { 30, 0x3ffffffa, 7, 0x6d }, { 30, 0x3ffffffa, 7, 0x6b },
+ { 30, 0x3ffffffa, 7, 0x69 }, { 30, 0x3ffffffa, 7, 0x67 },
+ { 30, 0x3ffffffa, 7, 0x65 }, { 30, 0x3ffffffa, 7, 0x63 },
+ { 30, 0x3ffffffa, 7, 0x61 }, { 30, 0x3ffffffa, 7, 0x5f },
+ { 30, 0x3ffffffa, 7, 0x5d }, { 30, 0x3ffffffa, 7, 0x5b },
+ { 30, 0x3ffffffa, 7, 0x59 }, { 30, 0x3ffffffa, 7, 0x57 },
+ { 30, 0x3ffffffa, 7, 0x55 }, { 30, 0x3ffffffa, 7, 0x53 },
+ { 30, 0x3ffffffa, 7, 0x51 }, { 30, 0x3ffffffa, 7, 0x4f },
+ { 30, 0x3ffffffa, 7, 0x4d }, { 30, 0x3ffffffa, 7, 0x4b },
+ { 30, 0x3ffffffa, 7, 0x49 }, { 30, 0x3ffffffa, 7, 0x47 },
+ { 30, 0x3ffffffa, 7, 0x45 }, { 30, 0x3ffffffa, 7, 0x43 },
+ { 30, 0x3ffffffa, 7, 0x41 }, { 30, 0x3ffffffa, 7, 0x3f },
+ { 30, 0x3ffffffa, 7, 0x3d }, { 30, 0x3ffffffa, 7, 0x3b },
+ { 30, 0x3ffffffa, 7, 0x39 }, { 30, 0x3ffffffa, 7, 0x37 },
+ { 30, 0x3ffffffa, 7, 0x35 }, { 30, 0x3ffffffa, 7, 0x33 },
+ { 30, 0x3ffffffa, 7, 0x31 }, { 30, 0x3ffffffa, 7, 0x2f },
+ { 30, 0x3ffffffa, 7, 0x2d }, { 30, 0x3ffffffa, 7, 0x2b },
+ { 30, 0x3ffffffa, 7, 0x29 }, { 30, 0x3ffffffa, 7, 0x27 },
+ { 30, 0x3ffffffa, 7, 0x25 }, { 30, 0x3ffffffa, 7, 0x23 },
+ { 30, 0x3ffffffa, 7, 0x21 }, { 30, 0x3ffffffa, 7, 0x1f },
+ { 30, 0x3ffffffa, 7, 0x1d }, { 30, 0x3ffffffa, 7, 0x1b },
+ { 30, 0x3ffffffa, 7, 0x19 }, { 30, 0x3ffffffa, 7, 0x17 },
+ { 30, 0x3ffffffa, 7, 0x15 }, { 30, 0x3ffffffa, 7, 0x13 },
+ { 30, 0x3ffffffa, 7, 0x11 }, { 30, 0x3ffffffa, 7, 0xf },
+ { 30, 0x3ffffffa, 7, 0xd }, { 30, 0x3ffffffa, 7, 0xb },
+ { 30, 0x3ffffffa, 7, 0x9 }, { 30, 0x3ffffffa, 7, 0x7 },
+ { 30, 0x3ffffffa, 7, 0x5 }, { 30, 0x3ffffffa, 7, 0x3 },
+ { 30, 0x3ffffffa, 7, 0x1 }, { 0, 0, 0, 0 }
+ },
+
+ /*
+ * prefixed with 15 zeroes
+ */
+ {
+ { 13, 0x1ff3, 0, 0 }, { 2, 0x3, 0, 0 },
+ { 2, 0x1, 0, 0 }, { 3, 0x7, 0, 0 },
+ { 3, 0x5, 0, 0 }, { 3, 0x3, 0, 0 },
+ { 3, 0x1, 0, 0 }, { 31, 0x7ffffffb, 4, 0xf },
+ { 31, 0x7ffffffb, 4, 0xd }, { 31, 0x7ffffffb, 4, 0xb },
+ { 31, 0x7ffffffb, 4, 0x9 }, { 31, 0x7ffffffb, 4, 0x7 },
+ { 31, 0x7ffffffb, 4, 0x5 }, { 31, 0x7ffffffb, 4, 0x3 },
+ { 31, 0x7ffffffb, 4, 0x1 }, { 5, 0x1f, 0, 0 },
+ { 5, 0x1d, 0, 0 }, { 5, 0x1b, 0, 0 },
+ { 5, 0x19, 0, 0 }, { 5, 0x17, 0, 0 },
+ { 5, 0x15, 0, 0 }, { 5, 0x13, 0, 0 },
+ { 5, 0x11, 0, 0 }, { 5, 0xf, 0, 0 },
+ { 5, 0xd, 0, 0 }, { 5, 0xb, 0, 0 },
+ { 5, 0x9, 0, 0 }, { 5, 0x7, 0, 0 },
+ { 5, 0x5, 0, 0 }, { 5, 0x3, 0, 0 },
+ { 5, 0x1, 0, 0 }, { 6, 0x3f, 0, 0 },
+ { 6, 0x3d, 0, 0 }, { 6, 0x3b, 0, 0 },
+ { 6, 0x39, 0, 0 }, { 6, 0x37, 0, 0 },
+ { 6, 0x35, 0, 0 }, { 6, 0x33, 0, 0 },
+ { 6, 0x31, 0, 0 }, { 6, 0x2f, 0, 0 },
+ { 6, 0x2d, 0, 0 }, { 6, 0x2b, 0, 0 },
+ { 6, 0x29, 0, 0 }, { 6, 0x27, 0, 0 },
+ { 6, 0x25, 0, 0 }, { 6, 0x23, 0, 0 },
+ { 6, 0x21, 0, 0 }, { 6, 0x1f, 0, 0 },
+ { 6, 0x1d, 0, 0 }, { 6, 0x1b, 0, 0 },
+ { 6, 0x19, 0, 0 }, { 6, 0x17, 0, 0 },
+ { 6, 0x15, 0, 0 }, { 6, 0x13, 0, 0 },
+ { 6, 0x11, 0, 0 }, { 6, 0xf, 0, 0 },
+ { 6, 0xd, 0, 0 }, { 6, 0xb, 0, 0 },
+ { 6, 0x9, 0, 0 }, { 6, 0x7, 0, 0 },
+ { 6, 0x5, 0, 0 }, { 6, 0x3, 0, 0 },
+ { 6, 0x1, 0, 0 }, { 7, 0x7f, 0, 0 },
+ { 7, 0x7d, 0, 0 }, { 7, 0x7b, 0, 0 },
+ { 7, 0x79, 0, 0 }, { 7, 0x77, 0, 0 },
+ { 7, 0x75, 0, 0 }, { 7, 0x73, 0, 0 },
+ { 7, 0x71, 0, 0 }, { 7, 0x6f, 0, 0 },
+ { 7, 0x6d, 0, 0 }, { 7, 0x6b, 0, 0 },
+ { 7, 0x69, 0, 0 }, { 7, 0x67, 0, 0 },
+ { 7, 0x65, 0, 0 }, { 7, 0x63, 0, 0 },
+ { 7, 0x61, 0, 0 }, { 7, 0x5f, 0, 0 },
+ { 7, 0x5d, 0, 0 }, { 7, 0x5b, 0, 0 },
+ { 7, 0x59, 0, 0 }, { 7, 0x57, 0, 0 },
+ { 7, 0x55, 0, 0 }, { 7, 0x53, 0, 0 },
+ { 7, 0x51, 0, 0 }, { 7, 0x4f, 0, 0 },
+ { 7, 0x4d, 0, 0 }, { 7, 0x4b, 0, 0 },
+ { 7, 0x49, 0, 0 }, { 7, 0x47, 0, 0 },
+ { 7, 0x45, 0, 0 }, { 7, 0x43, 0, 0 },
+ { 7, 0x41, 0, 0 }, { 7, 0x3f, 0, 0 },
+ { 7, 0x3d, 0, 0 }, { 7, 0x3b, 0, 0 },
+ { 7, 0x39, 0, 0 }, { 7, 0x37, 0, 0 },
+ { 7, 0x35, 0, 0 }, { 7, 0x33, 0, 0 },
+ { 7, 0x31, 0, 0 }, { 7, 0x2f, 0, 0 },
+ { 7, 0x2d, 0, 0 }, { 7, 0x2b, 0, 0 },
+ { 7, 0x29, 0, 0 }, { 7, 0x27, 0, 0 },
+ { 7, 0x25, 0, 0 }, { 7, 0x23, 0, 0 },
+ { 7, 0x21, 0, 0 }, { 7, 0x1f, 0, 0 },
+ { 7, 0x1d, 0, 0 }, { 7, 0x1b, 0, 0 },
+ { 7, 0x19, 0, 0 }, { 7, 0x17, 0, 0 },
+ { 7, 0x15, 0, 0 }, { 7, 0x13, 0, 0 },
+ { 7, 0x11, 0, 0 }, { 7, 0xf, 0, 0 },
+ { 7, 0xd, 0, 0 }, { 7, 0xb, 0, 0 },
+ { 7, 0x9, 0, 0 }, { 7, 0x7, 0, 0 },
+ { 7, 0x5, 0, 0 }, { 7, 0x3, 0, 0 },
+ { 7, 0x1, 0, 0 }, { 0, 0, 0, 0 }
+ }
+};
+
+VlcMagic _magic_values[] = {
+ { 0x0, 0, 1 },
+ { 0x1, 0, 2 },
+ { 0x4, 0, 3 },
+ { 0xB, 1, 1 },
+ { 0xC, 0, 4 },
+ { 0x1A, 0, 5 },
+ { 0x1B, 2, 1 },
+ { 0x38, 3, 1 },
+ { 0x39, 1, 2 },
+ { 0x3A, 1, 3 },
+ { 0x3B, 0, 6 },
+ { 0x78, 4, 1 },
+ { 0x79, 5, 1 },
+ { 0x7A, 6, 1 },
+ { 0x7B, 2, 2 },
+ { 0xF8, 1, 4 },
+ { 0xF9, 7, 1 },
+ { 0xFA, 8, 1 },
+ { 0xFB, 3, 2 },
+ { 0x1F8, 4, 2 },
+ { 0x1F9, 5, 2 },
+ { 0x1FA, 2, 3 },
+ { 0x1FB, 2, 4 },
+ { 0x3F8, 1, 5 },
+ { 0x3F9, 1, 6 },
+ { 0x3FA, 0, 7 },
+ { 0x3FB, 9, 1 },
+ { 0x7F8, 10, 1 },
+ { 0x7F9, 11, 1 },
+ { 0x7FA, 12, 1 },
+ { 0x7FB, 13, 1 },
+ { 0xFF8, 14, 1 },
+ { 0xFF9, 15, 1 },
+ { 0xFFA, 6, 2 },
+ { 0xFFB, 7, 2 },
+ { 0x1FF8, 8, 2 },
+ { 0x1FF9, 9, 2 },
+ { 0x1FFA, 10, 2 },
+ { 0x1FFB, 11, 2 },
+ { 0x3FF8, 12, 2 },
+ { 0x3FF9, 13, 2 },
+ { 0x3FFA, 14, 2 },
+ { 0x3FFB, 3, 3 },
+ { 0x7FF8, 4, 3 },
+ { 0x7FF9, 5, 3 },
+ { 0x7FFA, 6, 3 },
+ { 0x7FFB, 7, 3 },
+ { 0xFFF8, 8, 3 },
+ { 0xFFF9, 9, 3 },
+ { 0xFFFA, 10, 3 },
+ { 0xFFFB, 11, 3 },
+ { 0x1FFF8, 12, 3 },
+ { 0x1FFF9, 13, 3 },
+ { 0x1FFFA, 14, 3 },
+ { 0x1FFFB, 3, 4 },
+ { 0x3FFF8, 4, 4 },
+ { 0x3FFF9, 5, 4 },
+ { 0x3FFFA, 6, 4 },
+ { 0x3FFFB, 7, 4 },
+ { 0x7FFF8, 8, 4 },
+ { 0x7FFF9, 9, 4 },
+ { 0x7FFFA, 10, 4 },
+ { 0x7FFFB, 11, 4 },
+ { 0xFFFF8, 12, 4 },
+ { 0xFFFF9, 13, 4 },
+ { 0xFFFFA, 14, 4 },
+ { 0xFFFFB, 2, 5 },
+ { 0x1FFFF8, 3, 5 },
+ { 0x1FFFF9, 4, 5 },
+ { 0x1FFFFA, 5, 5 },
+ { 0x1FFFFB, 6, 5 },
+ { 0x3FFFF8, 7, 5 },
+ { 0x3FFFF9, 8, 5 },
+ { 0x3FFFFA, 9, 5 },
+ { 0x3FFFFB, 10, 5 },
+ { 0x7FFFF8, 11, 5 },
+ { 0x7FFFF9, 12, 5 },
+ { 0x7FFFFA, 13, 5 },
+ { 0x7FFFFB, 14, 5 },
+ { 0xFFFFF8, 2, 6 },
+ { 0xFFFFF9, 3, 6 },
+ { 0xFFFFFA, 4, 6 },
+ { 0xFFFFFB, 5, 6 },
+ { 0x1FFFFF8, 6, 6 },
+ { 0x1FFFFF9, 7, 6 },
+ { 0x1FFFFFA, 8, 6 },
+ { 0x1FFFFFB, 9, 6 },
+ { 0x3FFFFF8, 10, 6 },
+ { 0x3FFFFF9, 11, 6 },
+ { 0x3FFFFFA, 12, 6 },
+ { 0x3FFFFFB, 13, 6 },
+ { 0x7FFFFF8, 14, 6 },
+ { 0x7FFFFF9, 1, 7 },
+ { 0x7FFFFFA, 2, 7 },
+ { 0x7FFFFFB, 3, 7 },
+ { 0xFFFFFF8, 4, 7 },
+ { 0xFFFFFF9, 5, 7 },
+ { 0xFFFFFFA, 6, 7 },
+ { 0xFFFFFFB, 7, 7 },
+ { 0x1FFFFFF8, 8, 7 },
+ { 0x1FFFFFF9, 9, 7 },
+ { 0x1FFFFFFA, 10, 7 },
+ { 0x1FFFFFFB, 11, 7 },
+ { 0x3FFFFFF8, 12, 7 },
+ { 0x3FFFFFF9, 13, 7 },
+ { 0x3FFFFFFA, 14, 7 }
+};
+
+/*
+ * _find_magic
+ *
+ * Internal helper-function used to locate a given
+ * VlcMagic entry.
+ */
+VlcMagic *_find_magic(guint magic)
+{
+ gint low = 0;
+ gint high = sizeof(_magic_values) / sizeof(VlcMagic) - 1;
+ gint mid;
+
+ while (low <= high) {
+ mid = (low + high) / 2;
+
+ if (_magic_values[mid].magic < magic)
+ low = mid + 1;
+ else if (_magic_values[mid].magic > magic)
+ high = mid - 1;
+ else
+ return &_magic_values[mid];
+ }
+
+ return NULL;
+}
+
+/*
+ * _initialize_vlcdec_lookup
+ *
+ * Internal helper-function used to initialize
+ * the lookup-table used by the VLC-decoder.
+ */
+void _initialize_vlcdec_lookup(gint8 *lookup_tbl)
+{
+ gint8 util_buf[3072];
+ gint v1_start, v1_end, v1_dec, util_buf_offset;
+ gint util_buf_offset_inc, buf1_val, samples_offset;
+ gint v1, v2;
+ gint8 *p, *p1, *p2, *p3;
+
+ util_buf[0] = 0;
+ util_buf[1] = 0;
+ util_buf[2] = 0;
+ util_buf[3] = 1;
+ util_buf[4] = 1;
+ util_buf[5] = 1;
+ util_buf[765] = 1;
+ util_buf[766] = 0;
+ util_buf[767] = 1;
+ lookup_tbl[255] = 255;
+ lookup_tbl[256] = 1;
+
+ v1_start = -3;
+ v1_dec = 4;
+
+ util_buf_offset = 11;
+ util_buf_offset_inc = 12;
+ buf1_val = 2;
+
+ samples_offset = 509;
+
+ do {
+ v1 = v1_start;
+ v1_end = -(abs(v1_start) + 1) / 2;
+ v2 = 0;
+
+ p2 = util_buf + util_buf_offset - 3;
+
+ do {
+ p1 = util_buf + ((v1 & 0xff) * 3);
+ p1[0] = buf1_val;
+ p1[1] = v2;
+ p1[2] = buf1_val;
+
+ p2[1] = buf1_val;
+ p2[2] = v2 + 1;
+ p2[3] = buf1_val;
+
+ p3 = lookup_tbl + samples_offset + v2 + 1;
+ p3[0] = v1 & 0xff;
+ p3[1] = -(v1 & 0xff);
+
+ v1++;
+ v2 += 2;
+ p2 -= 3;
+ } while (v1 <= v1_end);
+
+ v1_start -= v1_dec;
+ v1_dec *= 2;
+
+ util_buf_offset += util_buf_offset_inc;
+ util_buf_offset_inc *= 2;
+ buf1_val++;
+
+ samples_offset += 255;
+ } while (buf1_val <= 7);
+
+ p = lookup_tbl + 1785 + util_buf[388];
+ p[0] = 129;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_decode.c b/kopete/protocols/msn/webcam/libmimic/vlc_decode.c
new file mode 100644
index 00000000..5675342d
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/vlc_decode.c
@@ -0,0 +1,119 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <string.h>
+#include "mimic-private.h"
+
+extern guchar _col_zag[64];
+
+/*
+ * _vlc_decode_block
+ *
+ * De-serialize (reconstruct) a variable length coded 8x8 block.
+ */
+gboolean _vlc_decode_block(MimCtx *ctx, gint *block, gint num_coeffs)
+{
+ guint pos;
+
+ memset(block, 0, 64 * sizeof(gint));
+
+ /* The DC-value is read in as is. */
+ block[0] = _read_bits(ctx, 8);
+
+ for (pos = 1; pos < num_coeffs; pos++) {
+
+ guint prev_data_index, prev_cur_chunk_len, prev_chunk;
+ guint value, num_bits;
+ gboolean prev_read_odd, found_magic;
+
+ /* Save context. */
+ prev_data_index = ctx->data_index;
+ prev_cur_chunk_len = ctx->cur_chunk_len;
+ prev_chunk = ctx->cur_chunk;
+ prev_read_odd = ctx->read_odd;
+
+ /* Grab 16 bits. */
+ value = _read_bits(ctx, 16) << 16;
+
+ /* Restore context. */
+ ctx->data_index = prev_data_index;
+ ctx->cur_chunk_len = prev_cur_chunk_len;
+ ctx->cur_chunk = prev_chunk;
+ ctx->read_odd = prev_read_odd;
+
+ /* Analyze and determine number of bits to read initially. */
+ num_bits = 3;
+ if ((value >> 30) == 0 || (value >> 30) == 1) {
+ num_bits = 2;
+ } else if ((value & 0xE0000000) != 0x80000000) {
+ guint nibble = value >> 28;
+
+ if (nibble == 11 || nibble == 12) {
+ num_bits = 4;
+ } else if (nibble == 10) {
+ _read_bits(ctx, 4);
+
+ return TRUE;
+ } else {
+ if (((value << 2) & 0x8000000) == 0)
+ num_bits = 2;
+
+ num_bits += 2;
+ }
+ }
+
+ /* Read that number of bits. */
+ value = _read_bits(ctx, num_bits);
+
+ /*
+ * Look up the current value against the magic ones,
+ * and continue extending it bit by bit from the input
+ * stream until the magic value is found or we have
+ * read 32 bits (in which case we give up).
+ */
+ found_magic = FALSE;
+ while (!found_magic) {
+ VlcMagic *magic;
+
+ if (num_bits > 32)
+ return FALSE;
+
+ magic = _find_magic(value);
+
+ if (magic != NULL) {
+ pos += magic->pos_add;
+ num_bits = magic->num_bits;
+
+ found_magic = TRUE;
+ } else {
+ value <<= 1;
+ value |= _read_bits(ctx, 1);
+
+ num_bits++;
+ }
+ }
+
+ /* Read the number of bits given by magic value entry. */
+ value = _read_bits(ctx, num_bits);
+
+ /* Gotcha! :-) */
+ block[_col_zag[pos]] = ctx->vlcdec_lookup[(num_bits * 255) + value];
+ }
+
+ return TRUE;
+}
+
diff --git a/kopete/protocols/msn/webcam/libmimic/vlc_encode.c b/kopete/protocols/msn/webcam/libmimic/vlc_encode.c
new file mode 100644
index 00000000..8d301627
--- /dev/null
+++ b/kopete/protocols/msn/webcam/libmimic/vlc_encode.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2005 Ole André Vadla Ravnås <oleavr@gmail.com>
+ *
+ * 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 <stdlib.h>
+#include "mimic-private.h"
+
+extern guchar _col_zag[64];
+extern VlcSymbol _vlc_alphabet[16][128];
+
+/*
+ * _vlc_encode_block
+ *
+ * Serialize an 8x8 block using variable length coding.
+ */
+void _vlc_encode_block(MimCtx *ctx, const gint *block, gint num_coeffs)
+{
+ gint i, num_zeroes;
+
+ /* The DC value is written out as is. */
+ _write_bits(ctx, block[0], 8);
+
+ /* Number of zeroes prefixing the next non-zero value. */
+ num_zeroes = 0;
+
+ for (i = 1; i < num_coeffs && num_zeroes <= 14; i++) {
+
+ /* Fetch AC coefficients from block in zig-zag order. */
+ gint value = block[_col_zag[i]];
+
+ if (value != 0) {
+ VlcSymbol sym;
+
+ /* Clip input values to [-128, +128]. */
+ if (value < -128)
+ value = -128;
+ else if (value > 128)
+ value = 128;
+
+ /* Look up symbol for the current non-zero value. */
+ sym = _vlc_alphabet[num_zeroes][abs(value) - 1];
+
+ /* No symbol? very rare... */
+ if (sym.length1 <= 0)
+ break;
+
+ /* The symbols for negative values are the same as for positives, minus one. */
+ if (value < 0) {
+ if (sym.length2 > 0)
+ sym.part2 -= 1;
+ else
+ sym.part1 -= 1;
+ }
+
+ /* Write out the full symbol. */
+ _write_bits(ctx, sym.part1, sym.length1);
+ if (sym.length2 > 0)
+ _write_bits(ctx, sym.part2, sym.length2);
+
+ /* Start counting zeroes again. */
+ num_zeroes = 0;
+ } else {
+ num_zeroes++;
+ }
+ }
+
+ /* Write out EOB if necessary. */
+ if (num_zeroes > 0)
+ _write_bits(ctx, 0xA, 4);
+}
+
diff --git a/kopete/protocols/msn/webcam/mimicwrapper.cpp b/kopete/protocols/msn/webcam/mimicwrapper.cpp
new file mode 100644
index 00000000..f7a43d93
--- /dev/null
+++ b/kopete/protocols/msn/webcam/mimicwrapper.cpp
@@ -0,0 +1,105 @@
+/*
+ Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#include "mimicwrapper.h"
+
+#include "libmimic/mimic.h"
+
+//#include <qbytearray.h>
+#include <kdebug.h>
+#include <qimage.h>
+
+MimicWrapper::MimicWrapper() : m_init(false)
+{
+ m_mimctx=mimic_open();
+}
+
+MimicWrapper::~MimicWrapper()
+{
+ mimic_close(m_mimctx);
+}
+
+
+QPixmap MimicWrapper::decode(const QByteArray& data)
+{
+ if(!m_init)
+ {
+ if(!mimic_decoder_init(m_mimctx, (guchar*)(data.data())))
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to init decoder" << endl;
+ return QPixmap();
+ }
+ if (!mimic_get_property( m_mimctx, "buffer_size", &m_bufferSize) )
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to get buffer size" << endl;
+ return QPixmap();
+ }
+ m_init=true;
+ }
+
+ QByteArray buff(m_bufferSize);
+ if(!mimic_decode_frame(m_mimctx, (guchar*)(data.data()) , (guchar*)(buff.data()) ) )
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to decode frame" << endl;
+ return QPixmap();
+ }
+ int width,height;
+ mimic_get_property(m_mimctx, "width", &width);
+ mimic_get_property(m_mimctx, "height", &height);
+
+
+ QByteArray buff2(m_bufferSize*4/3);
+ uint b2=0;
+ for(uint f=0;f<m_bufferSize;f+=3)
+ {
+ buff2[b2+0]=buff[f+2];
+ buff2[b2+1]=buff[f+1];
+ buff2[b2+2]=buff[f+0];
+ buff2[b2+3]=0x00;
+ b2+=4;
+ }
+
+ QImage img( (uchar*)(buff2.data()) , width , height , 32 , 0L , 0, QImage::BigEndian );
+ return QPixmap(img);
+}
+
+QByteArray MimicWrapper::encode(const QByteArray& data)
+{
+ if(!m_init)
+ {
+ if(!mimic_encoder_init(m_mimctx, MIMIC_RES_HIGH))
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to init encoder" << endl;
+ return QByteArray();
+ }
+ if (!mimic_get_property( m_mimctx, "buffer_size", &m_bufferSize) )
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to get buffer size" << endl;
+ return QByteArray();
+ }
+ m_init=true;
+ m_numFrames=0;
+ }
+
+ QByteArray buff(m_bufferSize);
+ int buff_new_size;
+ if(!mimic_encode_frame(m_mimctx, (guchar*)(data.data()) , (guchar*)(buff.data()) , (gint*)(&buff_new_size) , m_numFrames%15==0 ) )
+ {
+ kdWarning(14140) << k_funcinfo << "Impossible to decode frame" << endl;
+ return QByteArray();
+ }
+ buff.resize(buff_new_size);
+ ++m_numFrames;
+ return buff;
+}
diff --git a/kopete/protocols/msn/webcam/mimicwrapper.h b/kopete/protocols/msn/webcam/mimicwrapper.h
new file mode 100644
index 00000000..c4a7475f
--- /dev/null
+++ b/kopete/protocols/msn/webcam/mimicwrapper.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (c) 2005 by Olivier Goffart <ogoffart@ kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MIMICWRAPPER_H
+#define MIMICWREPPER_H
+
+#include <qpixmap.h>
+
+#include "kopete_export.h"
+
+typedef struct _MimCtx MimCtx;
+
+class KOPETE_EXPORT MimicWrapper
+{
+ public:
+ MimicWrapper();
+ ~MimicWrapper();
+
+ QPixmap decode(const QByteArray &data);
+ QByteArray encode(const QByteArray &data);
+
+ private:
+ MimCtx *m_mimctx;
+ bool m_init;
+ uint m_bufferSize;
+ uint m_numFrames;
+};
+
+#endif
+
diff --git a/kopete/protocols/msn/webcam/msnwebcamdialog.cpp b/kopete/protocols/msn/webcam/msnwebcamdialog.cpp
new file mode 100644
index 00000000..092135f0
--- /dev/null
+++ b/kopete/protocols/msn/webcam/msnwebcamdialog.cpp
@@ -0,0 +1,82 @@
+/*
+ Kopete MSN Protocol
+ Copyright (c) 2005 by Olivier Goffart <ogoffart @kde.org>
+
+ Note: this is just YahooWebcamDialog with s/Yahoo/MSN/g
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "msnwebcamdialog.h"
+
+#include <qframe.h>
+#include <qobject.h>
+#include <qwidget.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+
+
+MSNWebcamDialog::MSNWebcamDialog( const QString& contact, QWidget * parent, const char * name )
+ : KDialogBase( KDialogBase::Plain, i18n( "Webcam for %1" ).arg( contact ),
+ KDialogBase::Close, KDialogBase::Close, parent, name, false, true /*seperator*/ ),
+ m_imageContainer( this )
+{
+ setInitialSize( QSize(320,290), true );
+
+ setEscapeButton( KDialogBase::Close );
+ /*
+ QObject::connect( contact, SIGNAL( signalReceivedWebcamImage( const QPixmap& ) ),
+ this, SLOT( newImage( const QPixmap& ) ) );
+ */
+ QObject::connect( this, SIGNAL( closeClicked() ), this, SIGNAL( closingWebcamDialog() ) );
+ /*
+ QObject::connect( contact, SIGNAL( webcamClosed( int ) ), this, SLOT( webcamClosed( int ) ) );
+ */
+ QFrame* page = plainPage();
+ if ( page )
+ {
+ kdDebug(14180) << k_funcinfo << "Adding webcam image container" << endl;
+ //m_imageContainer.setText( i18n( "No webcam image received" ) );
+ //m_imageContainer.setAlignment( Qt::AlignCenter );
+ m_imageContainer.setMinimumSize(320,240);
+ }
+ show();
+}
+
+MSNWebcamDialog::~ MSNWebcamDialog( )
+{
+
+}
+
+void MSNWebcamDialog::newImage( const QPixmap & image )
+{
+ kdDebug(14180) << k_funcinfo << "New image received" << endl;
+ // kdDebug(14180) << image << endl;
+ //m_imageContainer.clear();
+ m_imageContainer.updatePixmap( image );
+ //show();
+}
+
+void MSNWebcamDialog::webcamClosed( int reason )
+{
+ kdDebug(14180) << k_funcinfo << "webcam closed with reason?? " << reason <<endl;
+ //m_imageContainer.clear();
+ //m_imageContainer.setText( i18n( "Webcam closed with reason %1" ).arg( QString::number( reason ) ) );
+ //m_imageContainer.setAlignment( Qt::AlignCenter );
+ //show();
+}
+
+// kate: indent-mode csands; tab-width 4;
+
+#include "msnwebcamdialog.moc"
diff --git a/kopete/protocols/msn/webcam/msnwebcamdialog.h b/kopete/protocols/msn/webcam/msnwebcamdialog.h
new file mode 100644
index 00000000..dc10285d
--- /dev/null
+++ b/kopete/protocols/msn/webcam/msnwebcamdialog.h
@@ -0,0 +1,55 @@
+/*
+ Kopete MSN Protocol
+
+ Copyright (c) 2005 by Olivier Goffart <ogoffart @kde.org>
+
+ Note: this is just YahooWebcamDialog with s/Yahoo/MSN/g
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOWEBCAMDIALOG_H_
+#define YAHOOWEBCAMDIALOG_H_
+
+//#include <qlabel.h>
+#include <webcamwidget.h>
+#include <kdialogbase.h>
+
+#include "kopete_export.h"
+
+
+class QPixmap;
+class QWidget;
+class MSNContact;
+
+class KOPETE_EXPORT MSNWebcamDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ MSNWebcamDialog( const QString& contact, QWidget* parent = 0, const char* name = 0 );
+ ~MSNWebcamDialog();
+
+public slots:
+ void newImage( const QPixmap& image );
+ void webcamClosed( int );
+
+signals:
+ void closingWebcamDialog();
+
+private:
+ Kopete::WebcamWidget m_imageContainer;
+
+};
+
+#endif
+//kate: indent-mode csands; auto-insert-doxygen on;
diff --git a/kopete/protocols/oscar/Makefile.am b/kopete/protocols/oscar/Makefile.am
new file mode 100644
index 00000000..c782e8c1
--- /dev/null
+++ b/kopete/protocols/oscar/Makefile.am
@@ -0,0 +1,15 @@
+SUBDIRS = liboscar . aim icq icons
+METASOURCES = AUTO
+AM_CPPFLAGS = -I./ui -I$(srcdir)/ui \
+ -I./liboscar -I$(srcdir)/liboscar \
+ $(KOPETE_INCLUDES) $(all_includes)
+
+lib_LTLIBRARIES = libkopete_oscar.la
+
+libkopete_oscar_la_SOURCES = oscaraccount.cpp oscarcontact.cpp oscarmyselfcontact.cpp \
+ oscarencodingselectionbase.ui oscarencodingselectiondialog.cpp \
+ oscarlistcontactsbase.ui oscarlistnonservercontacts.cpp \
+ oscarvisibilitybase.ui oscarvisibilitydialog.cpp \
+ oscarversionupdater.cpp
+libkopete_oscar_la_LDFLAGS = -no-undefined -version-info 2:0:0 $(all_libraries)
+libkopete_oscar_la_LIBADD = $(LIB_KIO) $(top_builddir)/kopete/libkopete/libkopete.la ./liboscar/liboscar.la
diff --git a/kopete/protocols/oscar/TODO b/kopete/protocols/oscar/TODO
new file mode 100644
index 00000000..25c82ee8
--- /dev/null
+++ b/kopete/protocols/oscar/TODO
@@ -0,0 +1,53 @@
+This is the TODO file for the OSCAR plugin.
+
+====== Possible refactorings =====
+
+- Unify status handling for ICQ and AIM? I like the ICQ::Presence thing, that's cool
+- Do delayed contact creation like on MSN so that when we actually get a good status
+ code back from the SSI manipulation, we create the contact then rather than hoping
+ it all works out.
+- serialize all the ssi information, either via properties for via the
+ Contact::serialize() method. We need to load it back to support proper auth handling
+
+
+====== Catching up to OscarSocket =====
+
+- Fill in all the ICQ user info
+- Add preferences for "Requires Auth", "Web Aware", etc.
+
+
+====== Adding new features not in oscarsocket ======
+
+Support direct connections
+Support file transfers
+A bunch of other stuff i'm probably forgetting.
+
+Add support for the many privacy options OSCAR has
+
+
+====== Left Over from the previous TODO ======
+There is some overlap here, and this is some of the stuff
+that was done in oscarsocket, that will need redoing in liboscar
+
+- general support for SNAC (0x15, *)
+- fix adding contacts for both addcontactwizard and serverside list
+- support encoding-settings for RTF-messages
+- use RTF in outgoing messages
+- keepalive for connection to server (icq has ping packets)
+- Keep users from adding their own UIN to their userlist
+- honor encodings for both sides (I need more knowledge about this!)
+- Option: Allow access from contacts on my contact list only
+- group handling in general
+- error handling on channel 0x04 messages. properly disconnect and emit a
+ signal in oscarsocket.
+- save groupID in KopeteGroups
+- somehow sync server and local list, this is not as trivial as everybody
+ always thinks it is because you cannot sure if local changes or
+ serverside-changes caused the difference (think about two clients being used
+ for the same account, one at home and one at work).
+- make renaming serverside contacts possible (function is there but fails due
+ to massive contactlist bugs caused by above mentioned classes)
+- support logging in with something different than "online" status for AIM
+- finish icq userinfo dialog and sending your own icq userinfo to the server,
+ it's easy to do but because of the mass of items takes lots of time
+ and is extremely boring. (requires snac 0x15, * parsing)
diff --git a/kopete/protocols/oscar/aim/Makefile.am b/kopete/protocols/oscar/aim/Makefile.am
new file mode 100644
index 00000000..91d12552
--- /dev/null
+++ b/kopete/protocols/oscar/aim/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS = ui
+METASOURCES = AUTO
+AM_CPPFLAGS = -I$(srcdir)/../ \
+ -I$(srcdir)/ui/ \
+ -I$(top_builddir)/kopete/protocols/oscar/aim/ui \
+ -I$(srcdir)/../liboscar \
+ $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_aim.la
+
+kopete_aim_la_SOURCES = aimprotocol.cpp aimaccount.cpp aimcontact.cpp aimuserinfo.cpp aimjoinchat.cpp aimchatsession.cpp
+
+kopete_aim_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_aim_la_LIBADD = ../libkopete_oscar.la ui/libkopeteaimui.la \
+ $(top_builddir)/kopete/libkopete/libkopete.la
+
+service_DATA = kopete_aim.desktop aim.protocol
+servicedir = $(kde_servicesdir)
diff --git a/kopete/protocols/oscar/aim/aim.protocol b/kopete/protocols/oscar/aim/aim.protocol
new file mode 100644
index 00000000..ae9f6c69
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aim.protocol
@@ -0,0 +1,13 @@
+[Protocol]
+exec=kopete "%u"
+protocol=aim
+input=none
+output=none
+helper=true
+listing=false
+reading=false
+writing=false
+makedir=false
+deleting=false
+URIMode=rawuri
+Icon=aim_icon
diff --git a/kopete/protocols/oscar/aim/aimaccount.cpp b/kopete/protocols/oscar/aim/aimaccount.cpp
new file mode 100644
index 00000000..c6228040
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimaccount.cpp
@@ -0,0 +1,924 @@
+/*
+ aimaccount.cpp - Oscar Protocol Plugin, AIM part
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qdom.h>
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kmdcodec.h>
+
+#include "kopeteawayaction.h"
+#include "kopetepassword.h"
+#include "kopetestdaction.h"
+#include "kopeteuiglobal.h"
+#include "kopetecontactlist.h"
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteview.h"
+#include <kopeteuiglobal.h>
+
+#include "aimprotocol.h"
+#include "aimaccount.h"
+#include "aimchatsession.h"
+#include "aimcontact.h"
+#include "aimuserinfo.h"
+#include "aimjoinchat.h"
+#include "oscarmyselfcontact.h"
+#include "oscarvisibilitydialog.h"
+
+#include "oscarutils.h"
+#include "client.h"
+#include "ssimanager.h"
+
+
+const DWORD AIM_ONLINE = 0x0;
+const DWORD AIM_AWAY = 0x1;
+
+namespace Kopete { class MetaContact; }
+
+AIMMyselfContact::AIMMyselfContact( AIMAccount *acct )
+: OscarMyselfContact( acct )
+{
+ m_acct = acct;
+}
+
+void AIMMyselfContact::userInfoUpdated()
+{
+ if ( ( details().userClass() & 32 ) == 0 )
+ setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOnline ); //we're online
+ else
+ setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusAway ); //we're away
+}
+
+void AIMMyselfContact::setOwnProfile( const QString& newProfile )
+{
+ m_profileString = newProfile;
+ if ( m_acct->isConnected() )
+ m_acct->engine()->updateProfile( newProfile );
+}
+
+QString AIMMyselfContact::userProfile()
+{
+ return m_profileString;
+}
+
+Kopete::ChatSession* AIMMyselfContact::manager( Kopete::Contact::CanCreateFlags canCreate,
+ Oscar::WORD exchange, const QString& room )
+{
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << endl;
+ Kopete::ContactPtrList chatMembers;
+ chatMembers.append( this );
+ Kopete::ChatSession* genericManager = 0L;
+ genericManager = Kopete::ChatSessionManager::self()->findChatSession( account()->myself(), chatMembers, protocol() );
+ AIMChatSession* session = dynamic_cast<AIMChatSession*>( genericManager );
+
+ if ( !session && canCreate == Contact::CanCreate )
+ {
+ session = new AIMChatSession( this, chatMembers, account()->protocol(), exchange, room );
+ session->setEngine( m_acct->engine() );
+
+ connect( session, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession* ) ),
+ this, SLOT( sendMessage( Kopete::Message&, Kopete::ChatSession* ) ) );
+ m_chatRoomSessions.append( session );
+ }
+ return session;
+}
+
+void AIMMyselfContact::chatSessionDestroyed( Kopete::ChatSession* session )
+{
+ m_chatRoomSessions.remove( session );
+}
+
+void AIMMyselfContact::sendMessage( Kopete::Message& message, Kopete::ChatSession* session )
+{
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "sending a message" << endl;
+ //TODO: remove duplication - factor into a message utils class or something
+ Oscar::Message msg;
+ QString s;
+
+ if (message.plainBody().isEmpty()) // no text, do nothing
+ return;
+ //okay, now we need to change the message.escapedBody from real HTML to aimhtml.
+ //looking right now for docs on that "format".
+ //looks like everything except for alignment codes comes in the format of spans
+
+ //font-style:italic -> <i>
+ //font-weight:600 -> <b> (anything > 400 should be <b>, 400 is not bold)
+ //text-decoration:underline -> <u>
+ //font-family: -> <font face="">
+ //font-size:xxpt -> <font ptsize=xx>
+
+ s=message.escapedBody();
+ s.replace ( QRegExp( QString::fromLatin1("<span style=\"([^\"]*)\">([^<]*)</span>")),
+ QString::fromLatin1("<style>\\1;\"\\2</style>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-style:italic;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<i><style>\\1\\2\"\\3</style></i>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-weight:600;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<b><style>\\1\\2\"\\3</style></b>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)text-decoration:underline;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<u><style>\\1\\2\"\\3</style></u>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-family:([^;]*);([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font face=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-size:([^p]*)pt;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font ptsize=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)color:([^;]*);([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font color=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("\\2"));
+
+ //okay now change the <font ptsize="xx"> to <font size="xx">
+
+ //0-9 are size 1
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"\\d\">")),
+ QString::fromLatin1("<font size=\"1\">"));
+ //10-11 are size 2
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[01]\">")),
+ QString::fromLatin1("<font size=\"2\">"));
+ //12-13 are size 3
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[23]\">")),
+ QString::fromLatin1("<font size=\"3\">"));
+ //14-16 are size 4
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[456]\">")),
+ QString::fromLatin1("<font size=\"4\">"));
+ //17-22 are size 5
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"(?:1[789]|2[012])\">")),
+ QString::fromLatin1("<font size=\"5\">"));
+ //23-29 are size 6
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"2[3456789]\">")),QString::fromLatin1("<font size=\"6\">"));
+ //30- (and any I missed) are size 7
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"[^\"]*\">")),QString::fromLatin1("<font size=\"7\">"));
+
+ s.replace ( QRegExp ( QString::fromLatin1("<br[ /]*>")), QString::fromLatin1("<br>") );
+
+ kdDebug(14190) << k_funcinfo << "sending "
+ << s << endl;
+
+ msg.setSender( contactId() );
+ msg.setText( Oscar::Message::UserDefined, s, m_acct->defaultCodec() );
+ msg.setTimestamp(message.timestamp());
+ msg.setType(0x03);
+ msg.addProperty( Oscar::Message::ChatRoom );
+
+ AIMChatSession* aimSession = dynamic_cast<AIMChatSession*>( session );
+ if ( !aimSession )
+ {
+ kdWarning(OSCAR_AIM_DEBUG) << "couldn't convert to AIM chat room session!" << endl;
+ session->messageSucceeded();
+ return;
+ }
+ msg.setExchange( aimSession->exchange() );
+ msg.setChatRoom( aimSession->roomName() );
+
+ m_acct->engine()->sendMessage( msg );
+ //session->appendMessage( message );
+ session->messageSucceeded();
+}
+
+
+AIMAccount::AIMAccount(Kopete::Protocol *parent, QString accountID, const char *name)
+ : OscarAccount(parent, accountID, name, false)
+{
+ kdDebug(14152) << k_funcinfo << accountID << ": Called."<< endl;
+ AIMMyselfContact* mc = new AIMMyselfContact( this );
+ setMyself( mc );
+ myself()->setOnlineStatus( static_cast<AIMProtocol*>( parent )->statusOffline );
+ QString profile = configGroup()->readEntry( "Profile",
+ i18n( "Visit the Kopete website at <a href=\"http://kopete.kde.org\">http://kopete.kde.org</a>") );
+ mc->setOwnProfile( profile );
+
+ m_joinChatDialog = 0;
+ m_visibilityDialog = 0;
+ QObject::connect( Kopete::ContactList::self(),
+ SIGNAL( globalIdentityChanged( const QString&, const QVariant& ) ),
+ this,
+ SLOT( slotGlobalIdentityChanged( const QString&, const QVariant& ) ) );
+
+ QObject::connect( engine(), SIGNAL( chatRoomConnected( WORD, const QString& ) ),
+ this, SLOT( connectedToChatRoom( WORD, const QString& ) ) );
+
+ QObject::connect( engine(), SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ),
+ this, SLOT( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ) );
+
+ QObject::connect( engine(), SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ),
+ this, SLOT( userLeftChat( Oscar::WORD, const QString&, const QString& ) ) );
+
+ QObject::connect( this, SIGNAL( buddyIconChanged() ), this, SLOT( slotBuddyIconChanged() ) );
+
+}
+
+AIMAccount::~AIMAccount()
+{
+}
+
+OscarContact *AIMAccount::createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem )
+{
+ AIMContact* contact = new AIMContact( this, contactId, parentContact, QString::null, ssiItem );
+ if ( !ssiItem.alias().isEmpty() )
+ contact->setProperty( Kopete::Global::Properties::self()->nickName(), ssiItem.alias() );
+
+ return contact;
+}
+
+QString AIMAccount::sanitizedMessage( const QString& message )
+{
+ QDomDocument doc;
+ QString domError;
+ int errLine = 0, errCol = 0;
+ doc.setContent( message, false, &domError, &errLine, &errCol );
+ if ( !domError.isEmpty() ) //error parsing, do nothing
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "error from dom document conversion: "
+ << domError << endl;
+ return message;
+ }
+ else
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "conversion to dom document successful."
+ << "looking for font tags" << endl;
+ QDomNodeList fontTagList = doc.elementsByTagName( "font" );
+ if ( fontTagList.count() == 0 )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "No font tags found. Returning normal message" << endl;
+ return message;
+ }
+ else
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Found font tags. Attempting replacement" << endl;
+ uint numFontTags = fontTagList.count();
+ for ( uint i = 0; i < numFontTags; i++ )
+ {
+ QDomNode fontNode = fontTagList.item(i);
+ QDomElement fontEl;
+ if ( !fontNode.isNull() && fontNode.isElement() )
+ fontEl = fontTagList.item(i).toElement();
+ else
+ continue;
+ if ( fontEl.hasAttribute( "back" ) )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Found attribute to replace. Doing replacement" << endl;
+ QString backgroundColor = fontEl.attribute( "back" );
+ backgroundColor.insert( 0, "background-color: " );
+ backgroundColor.append( ';' );
+ fontEl.setAttribute( "style", backgroundColor );
+ fontEl.removeAttribute( "back" );
+ }
+ }
+ }
+ }
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "sanitized message is " << doc.toString();
+ return doc.toString();
+}
+
+KActionMenu* AIMAccount::actionMenu()
+{
+// kdDebug(14152) << k_funcinfo << accountId() << ": Called." << endl;
+ // mActionMenu is managed by Kopete::Account. It is deleted when
+ // it is no longer shown, so we can (safely) just make a new one here.
+ KActionMenu *mActionMenu = new KActionMenu(accountId(),
+ myself()->onlineStatus().iconFor( this ), this, "AIMAccount::mActionMenu");
+
+ AIMProtocol *p = AIMProtocol::protocol();
+
+ QString accountNick = myself()->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ mActionMenu->popupMenu()->insertTitle( myself()->onlineStatus().iconFor( myself() ),
+ i18n( "%2 <%1>" ).arg( accountId(), accountNick ));
+
+ mActionMenu->insert( new KAction( i18n("Online"), p->statusOnline.iconFor( this ), 0, this,
+ SLOT( slotGoOnline() ), mActionMenu, "AIMAccount::mActionOnline") );
+
+ KAction* mActionAway = new Kopete::AwayAction(i18n("Away"), p->statusAway.iconFor( this ), 0, this,
+ SLOT(slotGoAway( const QString & )), this, "AIMAccount::mActionNA" );
+ mActionAway->setEnabled( isConnected() );
+ mActionMenu->insert( mActionAway );
+
+ KAction* mActionOffline = new KAction( i18n("Offline"), p->statusOffline.iconFor(this), 0, this,
+ SLOT( slotGoOffline() ), mActionMenu, "AIMAccount::mActionOffline");
+
+ mActionMenu->insert( mActionOffline );
+ mActionMenu->popupMenu()->insertSeparator();
+
+ KAction* m_joinChatAction = new KAction( i18n( "Join Chat..." ), QString::null, 0, this,
+ SLOT( slotJoinChat() ), mActionMenu, "join_a_chat" );
+
+ mActionMenu->insert( new KToggleAction( i18n( "Set Visibility..." ), 0, 0,
+ this, SLOT( slotSetVisiblility() ), this,
+ "AIMAccount::mActionSetVisibility") );
+
+ mActionMenu->insert( m_joinChatAction );
+
+ KAction* m_editInfoAction = new KAction( i18n( "Edit User Info..." ), "identity", 0,
+ this, SLOT( slotEditInfo() ), mActionMenu, "actionEditInfo");
+
+ mActionMenu->insert( m_editInfoAction );
+
+ return mActionMenu;
+}
+
+void AIMAccount::setAway(bool away, const QString &awayReason)
+{
+// kdDebug(14152) << k_funcinfo << accountId() << "reason is " << awayReason << endl;
+ if ( away )
+ {
+ engine()->setStatus( Client::Away, awayReason );
+ AIMMyselfContact* me = static_cast<AIMMyselfContact *> ( myself() );
+ me->setLastAwayMessage(awayReason);
+ me->setProperty( Kopete::Global::Properties::self()->awayMessage(), awayReason );
+ }
+ else
+ {
+ engine()->setStatus( Client::Online );
+ AIMMyselfContact* me = static_cast<AIMMyselfContact *> ( myself() );
+ me->setLastAwayMessage(QString::null);
+ me->removeProperty( Kopete::Global::Properties::self()->awayMessage() );
+ }
+}
+
+void AIMAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason )
+{
+ kdDebug(14152) << k_funcinfo << "called with reason = " << reason <<" status = "<< status.status() << endl;;
+ if ( status.status() == Kopete::OnlineStatus::Online )
+ setAway( false );
+ if ( status.status() == Kopete::OnlineStatus::Away )
+ setAway( true, reason );
+}
+
+
+void AIMAccount::setUserProfile(const QString &profile)
+{
+ kdDebug(14152) << k_funcinfo << "called." << endl;
+ AIMMyselfContact* aimmc = dynamic_cast<AIMMyselfContact*>( myself() );
+ if ( aimmc )
+ aimmc->setOwnProfile( profile );
+ configGroup()->writeEntry( QString::fromLatin1( "Profile" ), profile );
+}
+
+void AIMAccount::slotEditInfo()
+{
+ if ( !isConnected() )
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n( "Editing your user info is not possible because "
+ "you are not connected." ),
+ i18n( "Unable to edit user info" ) );
+ return;
+ }
+ AIMUserInfoDialog *myInfo = new AIMUserInfoDialog(static_cast<AIMContact *>( myself() ), this, true, 0L, "myInfo");
+ myInfo->exec(); // This is a modal dialog
+}
+
+void AIMAccount::slotGlobalIdentityChanged( const QString& key, const QVariant& value )
+{
+ //do something with the photo
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Global identity changed" << endl;
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "key: " << key << endl;
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "value: " << value << endl;
+
+ if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) )
+ {
+ if ( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ //edit ssi item to change alias (if possible)
+ }
+
+ if ( key == Kopete::Global::Properties::self()->photo().key() )
+ {
+ setBuddyIcon( value.toString() );
+ }
+ }
+}
+
+void AIMAccount::slotBuddyIconChanged()
+{
+ // need to disconnect because we could end up with many connections
+ QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) );
+ if ( !engine()->isActive() )
+ {
+ QObject::connect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) );
+ return;
+ }
+
+ QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString();
+
+ SSIManager* ssi = engine()->ssiManager();
+ Oscar::SSI item = ssi->findItemForIconByRef( 1 );
+
+ if ( photoPath.isEmpty() )
+ {
+ if ( item )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Removing icon hash item from ssi" << endl;
+ Oscar::SSI s(item);
+
+ //remove hash and alias
+ QValueList<TLV> tList( item.tlvList() );
+ TLV t = Oscar::findTLV( tList, 0x00D5 );
+ if ( t )
+ tList.remove( t );
+
+ item.setTLVList( tList );
+ //s is old, item is new. modification will occur
+ engine()->modifySSIItem( s, item );
+ }
+ }
+ else
+ {
+ QFile iconFile( photoPath );
+ iconFile.open( IO_ReadOnly );
+
+ KMD5 iconHash;
+ iconHash.update( iconFile );
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "hash is :" << iconHash.hexDigest() << endl;
+
+ //find old item, create updated item
+ if ( !item )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "no existing icon hash item in ssi. creating new" << endl;
+
+ TLV t;
+ t.type = 0x00D5;
+ t.data.resize( 18 );
+ t.data[0] = 0x00;
+ t.data[1] = 0x10;
+ memcpy(t.data.data() + 2, iconHash.rawDigest(), 16);
+ t.length = t.data.size();
+
+ QValueList<Oscar::TLV> list;
+ list.append( t );
+
+ Oscar::SSI s( "1", 0, ssi->nextContactId(), ROSTER_BUDDYICONS, list );
+
+ //item is a non-valid ssi item, so the function will add an item
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "setting new icon item" << endl;
+ engine()->modifySSIItem( item, s );
+ }
+ else
+ { //found an item
+ Oscar::SSI s(item);
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "modifying old item in ssi." << endl;
+ QValueList<TLV> tList( item.tlvList() );
+
+ TLV t = Oscar::findTLV( tList, 0x00D5 );
+ if ( t )
+ tList.remove( t );
+ else
+ t.type = 0x00D5;
+
+ t.data.resize( 18 );
+ t.data[0] = 0x00;
+ t.data[1] = 0x10;
+ memcpy(t.data.data() + 2, iconHash.rawDigest(), 16);
+ t.length = t.data.size();
+ tList.append( t );
+
+ item.setTLVList( tList );
+ //s is old, item is new. modification will occur
+ engine()->modifySSIItem( s, item );
+ }
+ iconFile.close();
+ }
+}
+
+void AIMAccount::slotJoinChat()
+{
+ if ( !isConnected() )
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n( "Joining an AIM chat room is not possible because "
+ "you are not connected." ),
+ i18n( "Unable to Join AIM Chat Room" ) );
+ return;
+ }
+
+ //get the exchange info
+ //create the dialog
+ //join the chat room
+ if ( !m_joinChatDialog )
+ {
+ m_joinChatDialog = new AIMJoinChatUI( this, false, Kopete::UI::Global::mainWidget() );
+ QObject::connect( m_joinChatDialog, SIGNAL( closing( int ) ),
+ this, SLOT( joinChatDialogClosed( int ) ) );
+ QValueList<int> list = engine()->chatExchangeList();
+ m_joinChatDialog->setExchangeList( list );
+ m_joinChatDialog->show();
+ }
+ else
+ m_joinChatDialog->raise();
+}
+
+void AIMAccount::slotGoOnline()
+{
+ if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Away )
+ {
+ kdDebug(14152) << k_funcinfo << accountId() << " was away. welcome back." << endl;
+ engine()->setStatus( Client::Online );
+ myself()->removeProperty( Kopete::Global::Properties::self()->awayMessage() );
+ }
+ else if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline )
+ {
+ kdDebug(14152) << k_funcinfo << accountId() << " was offline. time to connect" << endl;
+ OscarAccount::connect();
+ }
+ else
+ {
+ kdDebug(14152) << k_funcinfo << accountId() << " is already online, doing nothing" << endl;
+ }
+}
+
+void AIMAccount::slotGoAway(const QString &message)
+{
+ kdDebug(14152) << k_funcinfo << message << endl;
+ setAway(true, message);
+}
+
+void AIMAccount::joinChatDialogClosed( int code )
+{
+ if ( code == QDialog::Accepted )
+ {
+ //join the chat
+ kdDebug(14152) << k_funcinfo << "chat accepted." << endl;
+ engine()->joinChatRoom( m_joinChatDialog->roomName(),
+ m_joinChatDialog->exchange().toInt() );
+ }
+
+ m_joinChatDialog->delayedDestruct();
+ m_joinChatDialog = 0L;
+}
+
+void AIMAccount::loginActions()
+{
+ OscarAccount::loginActions();
+
+ using namespace AIM::PrivacySettings;
+ int privacySetting = this->configGroup()->readNumEntry( "PrivacySetting", AllowAll );
+ this->setPrivacySettings( privacySetting );
+}
+
+void AIMAccount::disconnected( DisconnectReason reason )
+{
+ kdDebug( OSCAR_AIM_DEBUG ) << k_funcinfo << "Attempting to set status offline" << endl;
+ myself()->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline );
+
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for( ; it.current(); ++it )
+ {
+ OscarContact* oc = dynamic_cast<OscarContact*>( it.current() );
+ if ( oc )
+ oc->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline );
+ }
+
+ OscarAccount::disconnected( reason );
+}
+
+void AIMAccount::messageReceived( const Oscar::Message& message )
+{
+ kdDebug(14152) << k_funcinfo << " Got a message, calling OscarAccount::messageReceived" << endl;
+ // Want to call the parent to do everything else
+ if ( message.type() != 0x0003 )
+ {
+ OscarAccount::messageReceived(message);
+
+ // Check to see if our status is away, and send an away message
+ // Might be duplicate code from the parent class to get some needed information
+ // Perhaps a refactoring is needed.
+ kdDebug(14152) << k_funcinfo << "Checking to see if I'm online.." << endl;
+ if( myself()->onlineStatus().status() == Kopete::OnlineStatus::Away )
+ {
+ QString sender = Oscar::normalize( message.sender() );
+ AIMContact* aimSender = static_cast<AIMContact *> ( contacts()[sender] ); //should exist now
+ if ( !aimSender )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << "For some reason, could not get the contact "
+ << "That this message is from: " << message.sender() << ", Discarding message" << endl;
+ return;
+ }
+ // Create, or get, a chat session with the contact
+ Kopete::ChatSession* chatSession = aimSender->manager( Kopete::Contact::CanCreate );
+
+ // get the away message we have set
+ AIMMyselfContact* myContact = static_cast<AIMMyselfContact *> ( myself() );
+ QString msg = myContact->lastAwayMessage();
+ kdDebug(14152) << k_funcinfo << "Got away message: " << msg << endl;
+ // Create the message
+ Kopete::Message chatMessage( myself(), aimSender, msg, Kopete::Message::Outbound,
+ Kopete::Message::RichText );
+ kdDebug(14152) << k_funcinfo << "Sending autoresponse" << endl;
+ // Send the message
+ aimSender->sendAutoResponse( chatMessage );
+ }
+ }
+
+ if ( message.type() == 0x0003 )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "have chat message" << endl;
+ //handle chat room messages seperately
+ QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
+ QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end();
+ for ( it = chats.begin(); it != itEnd; ++it )
+ {
+ Kopete::ChatSession* kcs = ( *it );
+ AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
+ if ( !session )
+ continue;
+
+ if ( session->exchange() == message.exchange() &&
+ Oscar::normalize( session->roomName() ) ==
+ Oscar::normalize( message.chatRoom() ) )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "found chat session for chat room" << endl;
+ Kopete::Contact* ocSender = contacts()[Oscar::normalize( message.sender() )];
+ //sanitize;
+ QString sanitizedMsg = sanitizedMessage( message.text( defaultCodec() ) );
+
+ Kopete::ContactPtrList me;
+ me.append( myself() );
+ Kopete::Message chatMessage( message.timestamp(), ocSender, me, sanitizedMsg,
+ Kopete::Message::Inbound, Kopete::Message::RichText );
+
+ session->appendMessage( chatMessage );
+ }
+ }
+ }
+}
+
+void AIMAccount::connectedToChatRoom( WORD exchange, const QString& room )
+{
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Creating chat room session" << endl;
+ Kopete::ContactPtrList emptyList;
+ AIMMyselfContact* me = static_cast<AIMMyselfContact*>( myself() );
+ AIMChatSession* session = dynamic_cast<AIMChatSession*>( me->manager( Kopete::Contact::CanCreate,
+ exchange, room ) );
+ session->setDisplayName( room );
+ if ( session->view( true ) )
+ session->raiseView();
+}
+
+void AIMAccount::userJoinedChat( WORD exchange, const QString& room, const QString& contact )
+{
+ if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) )
+ return;
+
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "user " << contact << " has joined the chat" << endl;
+ QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
+ QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end();
+ for ( it = chats.begin(); it != itEnd; ++it )
+ {
+ Kopete::ChatSession* kcs = ( *it );
+ AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
+ if ( !session )
+ continue;
+
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << session->exchange() << " " << exchange << endl;
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << session->roomName() << " " << room << endl;
+ if ( session->exchange() == exchange && session->roomName() == room )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "found correct chat session" << endl;
+ Kopete::Contact* c;
+ if ( contacts()[Oscar::normalize( contact )] )
+ c = contacts()[Oscar::normalize( contact )];
+ else
+ {
+ Kopete::MetaContact* mc = addContact( Oscar::normalize( contact ),
+ contact, 0, Kopete::Account::Temporary );
+ if ( !mc )
+ kdWarning(OSCAR_AIM_DEBUG) << "Unable to add contact for chat room" << endl;
+
+ c = mc->contacts().first();
+ c->setNickName( contact );
+ }
+
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "adding contact" << endl;
+ session->addContact( c, static_cast<AIMProtocol*>( protocol() )->statusOnline, true /* suppress */ );
+ }
+ }
+}
+
+void AIMAccount::userLeftChat( WORD exchange, const QString& room, const QString& contact )
+{
+ if ( Oscar::normalize( contact ) == Oscar::normalize( myself()->contactId() ) )
+ return;
+
+ QValueList<Kopete::ChatSession*> chats = Kopete::ChatSessionManager::self()->sessions();
+ QValueList<Kopete::ChatSession*>::iterator it, itEnd = chats.end();
+ for ( it = chats.begin(); it != itEnd; ++it )
+ {
+ Kopete::ChatSession* kcs = ( *it );
+ AIMChatSession* session = dynamic_cast<AIMChatSession*>( kcs );
+ if ( !session )
+ continue;
+
+ if ( session->exchange() == exchange && session->roomName() == room )
+ {
+ //delete temp contact
+ Kopete::Contact* c = contacts()[Oscar::normalize( contact )];
+ if ( !c )
+ {
+ kdWarning(OSCAR_AIM_DEBUG) << k_funcinfo << "couldn't find the contact that's left the chat!" << endl;
+ continue;
+ }
+ session->removeContact( c );
+ Kopete::MetaContact* mc = c->metaContact();
+ if ( mc->isTemporary() )
+ {
+ mc->removeContact( c );
+ delete c;
+ delete mc;
+ }
+ }
+ }
+}
+
+
+void AIMAccount::connectWithPassword( const QString & )
+{
+ kdDebug(14152) << k_funcinfo << "accountId='" << accountId() << "'" << endl;
+
+ // Get the screen name for this account
+ QString screenName = accountId();
+ QString server = configGroup()->readEntry( "Server", QString::fromLatin1( "login.oscar.aol.com" ) );
+ uint port = configGroup()->readNumEntry( "Port", 5190 );
+
+ Connection* c = setupConnection( server, port );
+
+ QString _password = password().cachedValue();
+ if ( _password.isEmpty() )
+ {
+ kdDebug(14150) << "Kopete is unable to attempt to sign-on to the "
+ << "AIM network because no password was specified in the "
+ << "preferences." << endl;
+ }
+ else if ( myself()->onlineStatus() == static_cast<AIMProtocol*>( protocol() )->statusOffline )
+ {
+ kdDebug(14152) << k_funcinfo << "Logging in as " << accountId() << endl ;
+ updateVersionUpdaterStamp();
+ engine()->start( server, port, accountId(), _password );
+ engine()->connectToServer( c, server, true /* doAuth */ );
+ myself()->setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusConnecting );
+ }
+}
+
+void AIMAccount::slotSetVisiblility()
+{
+ if( !isConnected() )
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n("You must be online to set users visibility."),
+ i18n("ICQ Plugin") );
+ return;
+ }
+
+ if ( !m_visibilityDialog )
+ {
+ m_visibilityDialog = new OscarVisibilityDialog( engine(), Kopete::UI::Global::mainWidget() );
+ QObject::connect( m_visibilityDialog, SIGNAL( closing() ),
+ this, SLOT( slotVisibilityDialogClosed() ) );
+
+ //add all contacts;
+ OscarVisibilityDialog::ContactMap contactMap;
+ QMap<QString, QString> revContactMap;
+
+ QValueList<Oscar::SSI> contactList = engine()->ssiManager()->contactList();
+ QValueList<Oscar::SSI>::const_iterator it, cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ {
+ QString contactId = ( *it ).name();
+
+ OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it ).name()] );
+ if ( oc )
+ {
+ contactMap.insert( oc->nickName(), contactId );
+ revContactMap.insert( contactId, oc->nickName() );
+ }
+ else
+ {
+ contactMap.insert( contactId, contactId );
+ revContactMap.insert( contactId, contactId );
+ }
+ }
+ m_visibilityDialog->addContacts( contactMap );
+
+ //add contacts from visible list
+ QStringList tmpList;
+
+ contactList = engine()->ssiManager()->visibleList();
+ cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ tmpList.append( revContactMap[( *it ).name()] );
+
+ m_visibilityDialog->addVisibleContacts( tmpList );
+
+ //add contacts from invisible list
+ tmpList.clear();
+
+ contactList = engine()->ssiManager()->invisibleList();
+ cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ tmpList.append( revContactMap[( *it ).name()] );
+
+ m_visibilityDialog->addInvisibleContacts( tmpList );
+
+ m_visibilityDialog->resize( 550, 350 );
+ m_visibilityDialog->show();
+ }
+ else
+ {
+ m_visibilityDialog->raise();
+ }
+}
+
+void AIMAccount::slotVisibilityDialogClosed()
+{
+ m_visibilityDialog->delayedDestruct();
+ m_visibilityDialog = 0L;
+}
+
+void AIMAccount::setPrivacySettings( int privacy )
+{
+ using namespace AIM::PrivacySettings;
+
+ BYTE privacyByte = 0x01;
+ DWORD userClasses = 0xFFFFFFFF;
+
+ switch ( privacy )
+ {
+ case AllowAll:
+ privacyByte = 0x01;
+ break;
+ case BlockAll:
+ privacyByte = 0x02;
+ break;
+ case AllowPremitList:
+ privacyByte = 0x03;
+ break;
+ case BlockDenyList:
+ privacyByte = 0x04;
+ break;
+ case AllowMyContacts:
+ privacyByte = 0x05;
+ break;
+ case BlockAIM:
+ privacyByte = 0x01;
+ userClasses = 0x00000004;
+ break;
+ }
+
+ this->setPrivacyTLVs( privacyByte, userClasses );
+}
+
+void AIMAccount::setPrivacyTLVs( BYTE privacy, DWORD userClasses )
+{
+ SSIManager* ssi = engine()->ssiManager();
+ Oscar::SSI item = ssi->findItem( QString::null, ROSTER_VISIBILITY );
+
+ QValueList<Oscar::TLV> tList;
+
+ tList.append( TLV( 0x00CA, 1, (char *)&privacy ) );
+ tList.append( TLV( 0x00CB, sizeof(userClasses), (char *)&userClasses ) );
+
+ if ( !item )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Adding new privacy TLV item" << endl;
+ Oscar::SSI s( QString::null, 0, ssi->nextContactId(), ROSTER_VISIBILITY, tList );
+ engine()->modifySSIItem( item, s );
+ }
+ else
+ { //found an item
+ Oscar::SSI s(item);
+
+ if ( Oscar::uptateTLVs( s, tList ) == true )
+ {
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating privacy TLV item" << endl;
+ engine()->modifySSIItem( item, s );
+ }
+ }
+}
+
+#include "aimaccount.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/aimaccount.h b/kopete/protocols/oscar/aim/aimaccount.h
new file mode 100644
index 00000000..034b9836
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimaccount.h
@@ -0,0 +1,146 @@
+/*
+ AIMAccount - Oscar Protocol Account
+
+ Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+*/
+
+#ifndef AIMACCOUNT_H
+#define AIMACCOUNT_H
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qwidget.h>
+#include "oscartypeclasses.h"
+
+#include "oscaraccount.h"
+#include "oscarmyselfcontact.h"
+
+namespace AIM
+{
+ namespace PrivacySettings
+ {
+ enum { AllowAll = 0, AllowMyContacts, AllowPremitList, BlockAll, BlockAIM, BlockDenyList };
+ }
+}
+
+namespace Kopete
+{
+class Contact;
+class Group;
+class ChatSession;
+}
+
+class KAction;
+class OscarContact;
+class AIMContact;
+class AIMAccount;
+class AIMJoinChatUI;
+class AIMChatSession;
+class OscarVisibilityDialog;
+
+class AIMMyselfContact : public OscarMyselfContact
+{
+Q_OBJECT
+public:
+ AIMMyselfContact( AIMAccount *acct );
+ void userInfoUpdated();
+ void setOwnProfile( const QString& newProfile );
+ QString userProfile();
+ void setLastAwayMessage( const QString& msg) {m_lastAwayMessage = msg;}
+ QString lastAwayMessage() { return m_lastAwayMessage; };
+
+ virtual Kopete::ChatSession* manager( Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate,
+ WORD exchange = 0, const QString& room = QString::null);
+
+public slots:
+ void sendMessage( Kopete::Message&, Kopete::ChatSession* session );
+ void chatSessionDestroyed( Kopete::ChatSession* );
+
+private:
+ QString m_profileString;
+ AIMAccount* m_acct;
+ /**
+ * There has GOT to be a better way to get this away message
+ */
+ QString m_lastAwayMessage;
+ QValueList<Kopete::ChatSession*> m_chatRoomSessions;
+
+
+};
+
+class AIMAccount : public OscarAccount
+{
+Q_OBJECT
+
+public:
+ AIMAccount(Kopete::Protocol *parent, QString accountID, const char *name=0L);
+ virtual ~AIMAccount();
+
+ // Accessor method for the action menu
+ virtual KActionMenu* actionMenu();
+
+ void setAway(bool away, const QString &awayReason = QString::null );
+
+ virtual void connectWithPassword( const QString &password );
+
+ void setUserProfile(const QString &profile);
+
+ void setPrivacySettings( int privacy );
+
+public slots:
+ /** Reimplementation from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason = QString::null );
+ void slotEditInfo();
+ void slotGoOnline();
+
+ void slotGlobalIdentityChanged( const QString&, const QVariant& );
+ void slotBuddyIconChanged();
+
+ void slotJoinChat();
+
+protected slots:
+ void slotGoAway(const QString&);
+ void joinChatDialogClosed( int );
+
+ virtual void loginActions();
+ virtual void disconnected( Kopete::Account::DisconnectReason reason );
+ virtual void messageReceived( const Oscar::Message& message );
+
+ void connectedToChatRoom( WORD exchange, const QString& roomName );
+ void userJoinedChat( Oscar::WORD exchange, const QString& room, const QString& contact );
+ void userLeftChat( Oscar::WORD exchange, const QString& room, const QString& contact );
+
+ void slotSetVisiblility();
+ void slotVisibilityDialogClosed();
+
+protected:
+
+ /**
+ * Implement virtual method from OscarAccount
+ * This allows OscarAccount to take care of adding new contacts
+ */
+ OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem );
+
+ QString sanitizedMessage( const QString& message );
+
+private:
+ // Set privacy tlv item
+ void setPrivacyTLVs( BYTE privacy, DWORD userClasses );
+
+ AIMJoinChatUI* m_joinChatDialog;
+ OscarVisibilityDialog* m_visibilityDialog;
+};
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/aimchatsession.cpp b/kopete/protocols/oscar/aim/aimchatsession.cpp
new file mode 100644
index 00000000..fa0616a6
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimchatsession.cpp
@@ -0,0 +1,73 @@
+// aimchatsession.cpp
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+
+#include "aimchatsession.h"
+#include "kopetecontact.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteprotocol.h"
+#include "client.h"
+
+AIMChatSession::AIMChatSession( const Kopete::Contact* user, Kopete::ContactPtrList others,
+ Kopete::Protocol* protocol, Oscar::WORD exchange,
+ const QString& room )
+
+ : Kopete::ChatSession( user, others, protocol, "AIMChatSession" )
+{
+ Kopete::ChatSessionManager::self()->registerChatSession( this );
+ setInstance( protocol->instance() );
+ setMayInvite( false );
+ m_exchange = exchange;
+ m_roomName = room;
+ m_engine = 0;
+}
+
+AIMChatSession::~AIMChatSession()
+{
+ m_engine->disconnectChatRoom( m_exchange, m_roomName );
+}
+
+void AIMChatSession::setEngine( Client* engine )
+{
+ m_engine = engine;
+}
+
+QString AIMChatSession::roomName() const
+{
+
+ return m_roomName;
+}
+
+void AIMChatSession::setRoomName( const QString& room )
+{
+ m_roomName = room;
+}
+
+Oscar::WORD AIMChatSession::exchange() const
+{
+ return m_exchange;
+}
+
+void AIMChatSession::setExchange( Oscar::WORD exchange )
+{
+ m_exchange = exchange;
+}
+
+
+#include "aimchatsession.moc"
diff --git a/kopete/protocols/oscar/aim/aimchatsession.h b/kopete/protocols/oscar/aim/aimchatsession.h
new file mode 100644
index 00000000..79c0685e
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimchatsession.h
@@ -0,0 +1,77 @@
+// aimchatsession.h
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#ifndef AIMCHATSESSION_H
+#define AIMCHATSESSION_H
+
+#include "kopetechatsession.h"
+#include "oscartypes.h"
+
+class Client;
+
+class AIMChatSession : public Kopete::ChatSession
+{
+Q_OBJECT
+public:
+ AIMChatSession( const Kopete::Contact* contact, Kopete::ContactPtrList others,
+ Kopete::Protocol* protocol, Oscar::WORD exchange = 0,
+ const QString& room = QString::null );
+ virtual ~AIMChatSession();
+
+ /**
+ * Set the engine to use so that we can disconnect from the chat service
+ * properly
+ */
+ void setEngine( Client* engine );
+
+ /**
+ * Get the name of the AIM chat room represented by
+ * this ChatSession object
+ * @return the name of the chat room
+ */
+ QString roomName() const;
+
+ /**
+ * Set the name of the AIM chat room represented by
+ * this ChatSession object
+ * @param room the name of the AIM chat room
+ */
+ void setRoomName( const QString& room );
+
+ /**
+ * Get the exchange of the AIM chat room represented by
+ * this ChatSession object
+ * @return the exchange of the chat room
+ */
+ Oscar::WORD exchange() const;
+
+ /**
+ * Set the exchange of the AIM chat room represented by
+ * this ChatSession object
+ * @param exchange the exchange of the AIM chat room
+ */
+ void setExchange( Oscar::WORD exchange );
+
+private:
+ QString m_roomName;
+ Oscar::WORD m_exchange;
+ Client* m_engine;
+};
+
+
+#endif
diff --git a/kopete/protocols/oscar/aim/aimcontact.cpp b/kopete/protocols/oscar/aim/aimcontact.cpp
new file mode 100644
index 00000000..7e46c585
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimcontact.cpp
@@ -0,0 +1,517 @@
+/*
+ aimcontact.cpp - Oscar Protocol Plugin
+
+ Copyright (c) 2003 by Will Stephenson
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <time.h>
+
+#include <qimage.h>
+#include <qregexp.h>
+#include <qtimer.h>
+#include <qtextcodec.h>
+
+#include <kapplication.h>
+#include <kactionclasses.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include "kopeteaway.h"
+#include "kopetechatsession.h"
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+
+//liboscar
+#include "client.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "ssimanager.h"
+
+#include "aimprotocol.h"
+#include "aimuserinfo.h"
+#include "aimcontact.h"
+#include "aimaccount.h"
+
+AIMContact::AIMContact( Kopete::Account* account, const QString& name, Kopete::MetaContact* parent,
+ const QString& icon, const Oscar::SSI& ssiItem )
+: OscarContact(account, name, parent, icon, ssiItem )
+{
+ mProtocol=static_cast<AIMProtocol *>(protocol());
+ setOnlineStatus( mProtocol->statusOffline );
+
+ m_infoDialog = 0L;
+ m_warnUserAction = 0L;
+ mUserProfile="";
+ m_haveAwayMessage = false;
+ m_mobile = false;
+ // Set the last autoresponse time to the current time yesterday
+ m_lastAutoresponseTime = QDateTime::currentDateTime().addDays(-1);
+
+ QObject::connect( mAccount->engine(), SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ),
+ this, SLOT( userInfoUpdated( const QString&, const UserDetails& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( userIsOffline( const QString& ) ),
+ this, SLOT( userOffline( const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const QString&, const QString& ) ),
+ this, SLOT( updateAwayMessage( const QString&, const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedProfile( const QString&, const QString& ) ),
+ this, SLOT( updateProfile( const QString&, const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ),
+ this, SLOT( gotWarning( const QString&, Q_UINT16, Q_UINT16 ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( haveIconForContact( const QString&, QByteArray ) ),
+ this, SLOT( haveIcon( const QString&, QByteArray ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( iconServerConnected() ),
+ this, SLOT( requestBuddyIcon() ) );
+ QObject::connect( this, SIGNAL( featuresUpdated() ), this, SLOT( updateFeatures() ) );
+}
+
+AIMContact::~AIMContact()
+{
+}
+
+bool AIMContact::isReachable()
+{
+ return true;
+}
+
+QPtrList<KAction> *AIMContact::customContextMenuActions()
+{
+
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+ if ( !m_warnUserAction )
+ {
+ m_warnUserAction = new KAction( i18n( "&Warn User" ), 0, this, SLOT( warnUser() ), this, "warnAction" );
+ }
+ m_actionVisibleTo = new KToggleAction(i18n("Always &Visible To"), "", 0,
+ this, SLOT(slotVisibleTo()), this, "actionVisibleTo");
+ m_actionInvisibleTo = new KToggleAction(i18n("Always &Invisible To"), "", 0,
+ this, SLOT(slotInvisibleTo()), this, "actionInvisibleTo");
+
+ bool on = account()->isConnected();
+
+ m_warnUserAction->setEnabled( on );
+
+ m_actionVisibleTo->setEnabled(on);
+ m_actionInvisibleTo->setEnabled(on);
+
+ SSIManager* ssi = account()->engine()->ssiManager();
+ m_actionVisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_VISIBLE ));
+ m_actionInvisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_INVISIBLE ));
+
+ actionCollection->append( m_warnUserAction );
+
+ actionCollection->append(m_actionVisibleTo);
+ actionCollection->append(m_actionInvisibleTo);
+
+
+ return actionCollection;
+}
+
+const QString AIMContact::awayMessage()
+{
+ return property(mProtocol->awayMessage).value().toString();
+}
+
+void AIMContact::setAwayMessage(const QString &message)
+{
+ kdDebug(14152) << k_funcinfo <<
+ "Called for '" << contactId() << "', away msg='" << message << "'" << endl;
+ QString filteredMessage = message;
+ filteredMessage.replace(
+ QRegExp(QString::fromLatin1("<[hH][tT][mM][lL].*>(.*)</[hH][tT][mM][lL]>")),
+ QString::fromLatin1("\\1"));
+ filteredMessage.replace(
+ QRegExp(QString::fromLatin1("<[bB][oO][dD][yY].*>(.*)</[bB][oO][dD][yY]>")),
+ QString::fromLatin1("\\1") );
+ QRegExp fontRemover( QString::fromLatin1("<[fF][oO][nN][tT].*>(.*)</[fF][oO][nN][tT]>") );
+ fontRemover.setMinimal(true);
+ while ( filteredMessage.find( fontRemover ) != -1 )
+ filteredMessage.replace( fontRemover, QString::fromLatin1("\\1") );
+ setProperty(mProtocol->awayMessage, filteredMessage);
+}
+
+int AIMContact::warningLevel() const
+{
+ return m_warningLevel;
+}
+
+void AIMContact::updateSSIItem()
+{
+ if ( m_ssiItem.type() != 0xFFFF && m_ssiItem.waitingAuth() == false &&
+ onlineStatus() == Kopete::OnlineStatus::Unknown )
+ {
+ //make sure they're offline
+ setOnlineStatus( static_cast<AIMProtocol*>( protocol() )->statusOffline );
+ }
+}
+
+void AIMContact::slotUserInfo()
+{
+ if ( !m_infoDialog)
+ {
+ m_infoDialog = new AIMUserInfoDialog( this, static_cast<AIMAccount*>( account() ), false, Kopete::UI::Global::mainWidget(), 0 );
+ if( !m_infoDialog )
+ return;
+ connect( m_infoDialog, SIGNAL( finished() ), this, SLOT( closeUserInfoDialog() ) );
+ m_infoDialog->show();
+ if ( mAccount->isConnected() )
+ {
+ mAccount->engine()->requestAIMProfile( contactId() );
+ mAccount->engine()->requestAIMAwayMessage( contactId() );
+ }
+ }
+ else
+ m_infoDialog->raise();
+}
+
+void AIMContact::userInfoUpdated( const QString& contact, const UserDetails& details )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << endl;
+
+ //if they don't have an SSI alias, make sure we use the capitalization from the
+ //server so their contact id looks all pretty.
+ QString nickname = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if ( nickname.isEmpty() || Oscar::normalize( nickname ) == Oscar::normalize( contact ) )
+ setNickName( contact );
+
+ ( details.userClass() & CLASS_WIRELESS ) ? m_mobile = true : m_mobile = false;
+
+ if ( ( details.userClass() & CLASS_AWAY ) == STATUS_ONLINE )
+ {
+ if ( m_mobile )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is mobile-online." << endl;
+ setOnlineStatus( mProtocol->statusWirelessOnline );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is online." << endl;
+ setOnlineStatus( mProtocol->statusOnline ); //we're online
+ }
+ removeProperty( mProtocol->awayMessage );
+ m_haveAwayMessage = false;
+ }
+ else if ( ( details.userClass() & CLASS_AWAY ) ) // STATUS_AWAY
+ {
+ if ( m_mobile )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is mobile-away." << endl;
+ setOnlineStatus( mProtocol->statusWirelessOnline );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " is away." << endl;
+ setOnlineStatus( mProtocol->statusAway ); //we're away
+ }
+ if ( !m_haveAwayMessage ) //prevent cyclic away message requests
+ {
+ mAccount->engine()->requestAIMAwayMessage( contactId() );
+ m_haveAwayMessage = true;
+ }
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact: " << contact << " class " << details.userClass() << " is unhandled... defaulting to away." << endl;
+ setOnlineStatus( mProtocol->statusAway ); //we're away
+ if ( !m_haveAwayMessage ) //prevent cyclic away message requests
+ {
+ mAccount->engine()->requestAIMAwayMessage( contactId() );
+ m_haveAwayMessage = true;
+ }
+ }
+
+ if ( details.buddyIconHash().size() > 0 && details.buddyIconHash() != m_details.buddyIconHash() )
+ {
+ if ( !mAccount->engine()->hasIconConnection() )
+ mAccount->engine()->requestServerRedirect( 0x0010 );
+
+ int time = ( KApplication::random() % 10 ) * 1000;
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating buddy icon in " << time/1000 << " seconds" << endl;
+ QTimer::singleShot( time, this, SLOT( requestBuddyIcon() ) );
+ }
+
+ OscarContact::userInfoUpdated( contact, details );
+}
+
+void AIMContact::userOnline( const QString& userId )
+{
+ if ( Oscar::normalize( userId ) == Oscar::normalize( contactId() ) )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Getting more contact info" << endl;
+ setOnlineStatus( mProtocol->statusOnline );
+ }
+}
+
+void AIMContact::userOffline( const QString& userId )
+{
+ if ( Oscar::normalize( userId ) == Oscar::normalize( contactId() ) )
+ {
+ setOnlineStatus( mProtocol->statusOffline );
+ removeProperty( mProtocol->awayMessage );
+ }
+}
+
+void AIMContact::updateAwayMessage( const QString& contact, const QString& message )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+ else
+ {
+ if ( message.isEmpty() )
+ {
+ removeProperty( mProtocol->awayMessage );
+ if ( !m_mobile )
+ setOnlineStatus( mProtocol->statusOnline );
+ else
+ setOnlineStatus( mProtocol->statusWirelessOnline );
+ m_haveAwayMessage = false;
+ }
+ else
+ {
+ m_haveAwayMessage = true;
+ setAwayMessage( message );
+ if ( !m_mobile )
+ setOnlineStatus( mProtocol->statusAway );
+ else
+ setOnlineStatus( mProtocol->statusWirelessAway );
+ }
+ }
+
+ emit updatedProfile();
+}
+
+void AIMContact::updateProfile( const QString& contact, const QString& profile )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ setProperty( mProtocol->clientProfile, profile );
+ emit updatedProfile();
+}
+
+void AIMContact::gotWarning( const QString& contact, Q_UINT16 increase, Q_UINT16 newLevel )
+{
+ //somebody just got bitchslapped! :O
+ Q_UNUSED( increase );
+ if ( Oscar::normalize( contact ) == Oscar::normalize( contactId() ) )
+ m_warningLevel = newLevel;
+
+ //TODO add a KNotify event after merge to HEAD
+}
+
+void AIMContact::requestBuddyIcon()
+{
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating buddy icon for " << contactId() << endl;
+ if ( m_details.buddyIconHash().size() > 0 )
+ {
+ account()->engine()->requestBuddyIcon( contactId(), m_details.buddyIconHash(),
+ m_details.iconCheckSumType() );
+ }
+}
+
+void AIMContact::haveIcon( const QString& user, QByteArray icon )
+{
+ if ( Oscar::normalize( user ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Updating icon for " << contactId() << endl;
+ QImage buddyIcon( icon );
+ if ( buddyIcon.isNull() )
+ {
+ kdWarning(OSCAR_AIM_DEBUG) << k_funcinfo << "Failed to convert buddy icon to QImage" << endl;
+ return;
+ }
+
+ setProperty( Kopete::Global::Properties::self()->photo(), buddyIcon );
+}
+
+void AIMContact::closeUserInfoDialog()
+{
+ m_infoDialog->delayedDestruct();
+ m_infoDialog = 0L;
+}
+
+void AIMContact::warnUser()
+{
+ QString nick = property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ QString message = i18n( "<qt>Would you like to warn %1 anonymously or with your name?<br>" \
+ "(Warning a user on AIM will result in a \"Warning Level\"" \
+ " increasing for the user you warn. Once this level has reached a" \
+ " certain point, they will not be able to sign on. Please do not abuse" \
+ " this function, it is meant for legitimate practices.)</qt>" ).arg( nick );
+
+
+ int result = KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget(), message,
+ i18n( "Warn User %1?" ).arg( nick ),
+ i18n( "Warn Anonymously" ), i18n( "Warn" ) );
+
+ if ( result == KMessageBox::Yes )
+ mAccount->engine()->sendWarning( contactId(), true);
+ else if ( result == KMessageBox::No )
+ mAccount->engine()->sendWarning( contactId(), false);
+}
+
+void AIMContact::slotVisibleTo()
+{
+ account()->engine()->setVisibleTo( contactId(), m_actionVisibleTo->isChecked() );
+}
+
+void AIMContact::slotInvisibleTo()
+{
+ account()->engine()->setInvisibleTo( contactId(), m_actionInvisibleTo->isChecked() );
+}
+
+void AIMContact::slotSendMsg(Kopete::Message& message, Kopete::ChatSession *)
+{
+ Oscar::Message msg;
+ QString s;
+
+ if (message.plainBody().isEmpty()) // no text, do nothing
+ return;
+ //okay, now we need to change the message.escapedBody from real HTML to aimhtml.
+ //looking right now for docs on that "format".
+ //looks like everything except for alignment codes comes in the format of spans
+
+ //font-style:italic -> <i>
+ //font-weight:600 -> <b> (anything > 400 should be <b>, 400 is not bold)
+ //text-decoration:underline -> <u>
+ //font-family: -> <font face="">
+ //font-size:xxpt -> <font ptsize=xx>
+
+ s=message.escapedBody();
+ s.replace ( QRegExp( QString::fromLatin1("<span style=\"([^\"]*)\">([^<]*)</span>")),
+ QString::fromLatin1("<style>\\1;\"\\2</style>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-style:italic;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<i><style>\\1\\2\"\\3</style></i>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-weight:600;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<b><style>\\1\\2\"\\3</style></b>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)text-decoration:underline;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<u><style>\\1\\2\"\\3</style></u>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-family:([^;]*);([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font face=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)font-size:([^p]*)pt;([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font ptsize=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)color:([^;]*);([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("<font color=\"\\2\"><style>\\1\\3\"\\4</style></font>"));
+
+ s.replace ( QRegExp( QString::fromLatin1("<style>([^\"]*)\"([^<]*)</style>")),
+ QString::fromLatin1("\\2"));
+
+ //okay now change the <font ptsize="xx"> to <font size="xx">
+
+ //0-9 are size 1
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"\\d\">")),
+ QString::fromLatin1("<font size=\"1\">"));
+ //10-11 are size 2
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[01]\">")),
+ QString::fromLatin1("<font size=\"2\">"));
+ //12-13 are size 3
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[23]\">")),
+ QString::fromLatin1("<font size=\"3\">"));
+ //14-16 are size 4
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"1[456]\">")),
+ QString::fromLatin1("<font size=\"4\">"));
+ //17-22 are size 5
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"(?:1[789]|2[012])\">")),
+ QString::fromLatin1("<font size=\"5\">"));
+ //23-29 are size 6
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"2[3456789]\">")),QString::fromLatin1("<font size=\"6\">"));
+ //30- (and any I missed) are size 7
+ s.replace ( QRegExp ( QString::fromLatin1("<font ptsize=\"[^\"]*\">")),QString::fromLatin1("<font size=\"7\">"));
+
+ s.replace ( QRegExp ( QString::fromLatin1("<br[ /]*>")), QString::fromLatin1("<br>") );
+
+ // strip left over line break
+ s.remove( QRegExp( QString::fromLatin1( "<br>$" ) ) );
+
+ kdDebug(14190) << k_funcinfo << "sending "
+ << s << endl;
+
+ // XXX Need to check for message size?
+
+ if ( m_details.hasCap( CAP_UTF8 ) )
+ msg.setText( Oscar::Message::UCS2, s );
+ else
+ msg.setText( Oscar::Message::UserDefined, s, contactCodec() );
+
+ msg.setReceiver(mName);
+ msg.setTimestamp(message.timestamp());
+ msg.setType(0x01);
+
+ mAccount->engine()->sendMessage(msg);
+
+ // Show the message we just sent in the chat window
+ manager(Kopete::Contact::CanCreate)->appendMessage(message);
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+void AIMContact::updateFeatures()
+{
+ setProperty( static_cast<AIMProtocol*>(protocol())->clientFeatures, m_clientFeatures );
+}
+
+void AIMContact::sendAutoResponse(Kopete::Message& msg)
+{
+ // The target time is 2 minutes later than the last message
+ int delta = m_lastAutoresponseTime.secsTo( QDateTime::currentDateTime() );
+ kdDebug(14152) << k_funcinfo << "Last autoresponse time: " << m_lastAutoresponseTime << endl;
+ kdDebug(14152) << k_funcinfo << "Current time: " << QDateTime::currentDateTime() << endl;
+ kdDebug(14152) << k_funcinfo << "Difference: " << delta << endl;
+ // Check to see if we're past that time
+ if(delta > 120)
+ {
+ kdDebug(14152) << k_funcinfo << "Sending auto response" << endl;
+
+ // This code was yoinked straight from OscarContact::slotSendMsg()
+ // If only that slot wasn't private, but I'm not gonna change it right now.
+ Oscar::Message message;
+
+ if ( m_details.hasCap( CAP_UTF8 ) )
+ {
+ message.setText( Oscar::Message::UCS2, msg.plainBody() );
+ }
+ else
+ {
+ QTextCodec* codec = contactCodec();
+ message.setText( Oscar::Message::UserDefined, msg.plainBody(), codec );
+ }
+
+ message.setTimestamp( msg.timestamp() );
+ message.setSender( mAccount->accountId() );
+ message.setReceiver( mName );
+ message.setType( 0x01 );
+
+ // isAuto defaults to false
+ mAccount->engine()->sendMessage( message, true);
+ kdDebug(14152) << k_funcinfo << "Sent auto response" << endl;
+ manager(Kopete::Contact::CanCreate)->appendMessage(msg);
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+ // Update the last autoresponse time
+ m_lastAutoresponseTime = QDateTime::currentDateTime();
+ }
+ else
+ {
+ kdDebug(14152) << k_funcinfo << "Not enough time since last autoresponse, NOT sending" << endl;
+ }
+}
+#include "aimcontact.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/aimcontact.h b/kopete/protocols/oscar/aim/aimcontact.h
new file mode 100644
index 00000000..458db2f5
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimcontact.h
@@ -0,0 +1,102 @@
+/*
+ aimcontact.h - Oscar Protocol Plugin
+
+ Copyright (c) 2003 by Will Stephenson
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef AIMCONTACT_H
+#define AIMCONTACT_H
+
+#include <qdatetime.h>
+
+#include "oscarcontact.h"
+
+
+namespace Kopete
+{
+class ChatSession;
+}
+
+class AIMAccount;
+class AIMProtocol;
+class AIMUserInfoDialog;
+
+class AIMContact : public OscarContact
+{
+Q_OBJECT
+
+public:
+ AIMContact( Kopete::Account*, const QString&, Kopete::MetaContact*,
+ const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() );
+ virtual ~AIMContact();
+
+ bool isReachable();
+ QPtrList<KAction> *customContextMenuActions();
+
+ const QString &userProfile() { return mUserProfile; }
+
+ virtual const QString awayMessage();
+ virtual void setAwayMessage( const QString &message );
+
+ int warningLevel() const;
+
+ /**
+ * Gets the last time an autoresponse was sent to this contact
+ * @returns QDateTime Object that represents the date/time
+ */
+ QDateTime lastAutoResponseTime() {return m_lastAutoresponseTime;}
+
+ /** Sends an auto response to this contact */
+ virtual void sendAutoResponse(Kopete::Message& msg);
+
+public slots:
+ void updateSSIItem();
+ void slotUserInfo();
+ void userInfoUpdated( const QString& contact, const UserDetails& details );
+ void userOnline( const QString& userId );
+ void userOffline( const QString& userId );
+ void updateAwayMessage( const QString& userId, const QString& message );
+ void updateProfile( const QString& contact, const QString& profile );
+ void gotWarning( const QString& contact, Q_UINT16, Q_UINT16 );
+
+signals:
+ void updatedProfile();
+
+protected slots:
+ virtual void slotSendMsg(Kopete::Message& message, Kopete::ChatSession *);
+ virtual void updateFeatures();
+
+private slots:
+ void requestBuddyIcon();
+ void haveIcon( const QString&, QByteArray );
+ void closeUserInfoDialog();
+ void warnUser();
+
+ void slotVisibleTo();
+ void slotInvisibleTo();
+
+private:
+ AIMProtocol* mProtocol;
+ AIMUserInfoDialog* m_infoDialog;
+ QString mUserProfile;
+ bool m_haveAwayMessage;
+ bool m_mobile; // Is this user mobile (i.e. do they have message forwarding on, or mobile AIM)
+ QDateTime m_lastAutoresponseTime;
+
+ KAction* m_warnUserAction;
+ KToggleAction *m_actionVisibleTo;
+ KToggleAction *m_actionInvisibleTo;
+};
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/aimjoinchat.cpp b/kopete/protocols/oscar/aim/aimjoinchat.cpp
new file mode 100644
index 00000000..8b8c78a9
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimjoinchat.cpp
@@ -0,0 +1,94 @@
+// aimjoinchat.cpp
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+
+#include "aimjoinchat.h"
+
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <klocale.h>
+
+#include "aimjoinchatbase.h"
+#include "aimaccount.h"
+
+AIMJoinChatUI::AIMJoinChatUI( AIMAccount* account, bool modal,
+ QWidget* parent, const char* name )
+ : KDialogBase( parent, name, modal, i18n( "Join AIM Chat Room" ),
+ Cancel | User1, User1, true, i18n( "Join" ) )
+{
+
+ kdDebug(OSCAR_AIM_DEBUG) << k_funcinfo << "Account " << account->accountId()
+ << " joining a chat room" << endl;
+
+ m_account = account;
+
+ m_joinUI = new AIMJoinChatBase( this, "aimjoinchatbase" );
+
+ setMainWidget( m_joinUI );
+
+ QObject::connect( this, SIGNAL( user1Clicked() ), this, SLOT( joinChat() ) );
+ QObject::connect( this, SIGNAL( cancelClicked() ), this, SLOT( closeClicked() ) );
+}
+
+AIMJoinChatUI::~AIMJoinChatUI()
+{
+ m_exchanges.clear();
+}
+
+void AIMJoinChatUI::setExchangeList( const QValueList<int>& list )
+{
+ m_exchanges = list;
+ QStringList exchangeList;
+ QValueList<int>::const_iterator it = list.begin();
+ while ( it != list.end() )
+ {
+ exchangeList.append( QString::number( ( *it ) ) );
+ ++it;
+ }
+
+
+ m_joinUI->exchange->insertStringList( exchangeList );
+}
+
+void AIMJoinChatUI::joinChat()
+{
+ m_roomName = m_joinUI->roomName->text();
+ int item = m_joinUI->exchange->currentItem();
+ m_exchange = m_joinUI->exchange->text( item );
+
+ emit closing( QDialog::Accepted );
+}
+
+void AIMJoinChatUI::closeClicked()
+{
+ //hmm, do nothing?
+ emit closing( QDialog::Rejected );
+}
+
+QString AIMJoinChatUI::roomName() const
+{
+ return m_roomName;
+}
+
+QString AIMJoinChatUI::exchange() const
+{
+ return m_exchange;
+}
+
+#include "aimjoinchat.moc"
+//kate: space-indent on; indent-width 4;
diff --git a/kopete/protocols/oscar/aim/aimjoinchat.h b/kopete/protocols/oscar/aim/aimjoinchat.h
new file mode 100644
index 00000000..dc74a8a9
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimjoinchat.h
@@ -0,0 +1,62 @@
+// aimjoinchat.h
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+
+#ifndef AIMJOINCHAT_H
+#define AIMJOINCHAT_H
+
+#include <kdialogbase.h>
+
+#include "oscartypes.h"
+
+class AIMAccount;
+class AIMJoinChatBase;
+
+class AIMJoinChatUI : public KDialogBase
+{
+Q_OBJECT
+public:
+ AIMJoinChatUI( AIMAccount*, bool modal, QWidget* parent = 0,
+ const char* name = 0 );
+ ~AIMJoinChatUI();
+
+ void setExchangeList( const QValueList<int>& );
+ QValueList<int> exchangeList() const;
+
+ QString roomName() const;
+ QString exchange() const;
+
+
+protected slots:
+ void joinChat();
+ void closeClicked();
+
+signals:
+ void closing( int );
+
+private:
+ AIMJoinChatBase* m_joinUI;
+ AIMAccount* m_account;
+ QValueList<int> m_exchanges;
+ QString m_roomName;
+ QString m_exchange;
+
+};
+
+#endif
+//kate: space-indent on; indent-width 4;
diff --git a/kopete/protocols/oscar/aim/aimprotocol.cpp b/kopete/protocols/oscar/aim/aimprotocol.cpp
new file mode 100644
index 00000000..9f64fe28
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimprotocol.cpp
@@ -0,0 +1,320 @@
+/*
+ oscarprotocol.cpp - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#include <qstringlist.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+#include "aimprotocol.h"
+#include "aimaccount.h"
+#include "aimcontact.h"
+#include "aimaddcontactpage.h"
+#include "aimeditaccountwidget.h"
+
+#include "accountselector.h"
+#include "kopeteaccountmanager.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <kimageio.h>
+
+typedef KGenericFactory<AIMProtocol> AIMProtocolFactory;
+
+K_EXPORT_COMPONENT_FACTORY( kopete_aim, AIMProtocolFactory( "kopete_aim" ) )
+
+AIMProtocol* AIMProtocol::protocolStatic_ = 0L;
+
+
+AIMProtocolHandler::AIMProtocolHandler() : Kopete::MimeTypeHandler(false)
+{
+ registerAsProtocolHandler(QString::fromLatin1("aim"));
+}
+
+void AIMProtocolHandler::handleURL(const KURL &url) const
+{
+/**
+ * Send a Message =================================================
+ * aim:goim
+ * aim:goim?screenname=screen+name
+ * aim:goim?screenname=screen+name&message=message
+ * Add Buddy ======================================================
+ * aim:addbuddy
+ * aim:addbuddy?screenname=screen+name
+ * Buddy Icon =====================================================
+ * aim:buddyicon
+ * aim:buddyicon?src=image_source
+ * aim:buddyicon?screename=screen+name
+ * aim:buddyicon?src=image_source&screename=screen+name
+ * Get and Send Files =============================================
+ * aim:getfile?screename=(sn)
+ * aim:sendfile?screenname=(sn)
+ * Register User ==================================================
+ * aim:RegisterUser?ScreenName=sn&Password=pw&SignonNow=False
+ * Away Message ===================================================
+ * aim:goaway?message=brb+or+something
+ * Chat Rooms =====================================================
+ * aim:GoChat?RoomName=room+name&Exchange=number
+ **/
+
+ AIMProtocol *proto = AIMProtocol::protocol();
+ kdDebug(14152) << k_funcinfo << "URL url : '" << url.url() << "'" << endl;
+ kdDebug(14152) << k_funcinfo << "URL path : '" << url.path() << "'" << endl;
+ QString command = url.path();
+ QString realCommand, firstParam, secondParam;
+ bool needContactAddition = false;
+ if ( command.find( "goim", 0, false ) != -1 )
+ {
+ realCommand = "goim";
+ kdDebug(14152) << k_funcinfo << "Handling send IM request" << endl;
+ command.remove(0,4);
+ if ( command.find( "?screenname=", 0, false ) == -1 )
+ {
+ kdWarning(14152) << k_funcinfo << "Unhandled AIM URI:" << url.url() << endl;
+ return;
+ }
+ command.remove( 0, 12 );
+ int andSign = command.find( "&" );
+ if ( andSign > 0 )
+ command = command.left( andSign );
+
+ firstParam = command;
+ firstParam.replace( "+", " " );
+ needContactAddition = true;
+ }
+ else
+ if ( command.find( "addbuddy", 0, false ) != -1 )
+ {
+ realCommand = "addbuddy";
+ kdDebug(14152) << k_funcinfo << "Handling AIM add buddy request" << endl;
+ command.remove( 0, 8 );
+ if ( command.find( "?screenname=", 0, false ) == -1 )
+ {
+ kdWarning(14152) << k_funcinfo << "Unhandled AIM URI:" << url.url() << endl;
+ return;
+ }
+
+ command.remove(0, 12);
+ int andSign = command.find("&");
+ if ( andSign > 0 )
+ command = command.left(andSign);
+ command.replace("+", " ");
+
+ firstParam = command;
+ needContactAddition = true;
+ }
+ else
+ if ( command.find( "gochat", 0, false ) != -1 )
+ {
+ realCommand = "gochat";
+ kdDebug(14152) << k_funcinfo << "Handling AIM chat room request" << endl;
+ command.remove( 0, 6 );
+
+ if ( command.find( "?RoomName=", 0, false ) == -1 )
+ {
+ kdWarning(14152) << "Unhandled AIM URI: " << url.url() << endl;
+ return;
+ }
+
+ command.remove( 0, 10 );
+
+ int andSign = command.find("&");
+ if (andSign > 0) // strip off anything else for now
+ {
+ firstParam = command.left(andSign);
+ }
+ command.remove( 0, andSign );
+ kdDebug(14152) << "command is now: " << command << endl;
+ command.remove( 0, 10 ); //remove "&Exchange="
+ secondParam = command;
+ kdDebug(14152) << k_funcinfo << firstParam << " " << secondParam << endl;
+ firstParam.replace("+", " ");
+ }
+
+ Kopete::Account *account = 0;
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(proto);
+
+ if (accounts.count() == 1)
+ {
+ QDictIterator<Kopete::Account> it(accounts);
+ account = it.current();
+
+ }
+ else
+ {
+ KDialogBase *chooser = new KDialogBase(0, "chooser", true,
+ i18n("Choose Account"), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, false);
+ AccountSelector *accSelector = new AccountSelector(proto, chooser, "accSelector");
+ chooser->setMainWidget(accSelector);
+
+ int ret = chooser->exec();
+ account = accSelector->selectedItem();
+
+ delete chooser;
+ if (ret == QDialog::Rejected || account == 0)
+ {
+ kdDebug(14152) << k_funcinfo << "Cancelled" << endl;
+ return;
+ }
+ }
+
+ Kopete::MetaContact* mc = 0;
+ if ( needContactAddition || realCommand == "addbuddy" )
+ {
+ if ( !account->isConnected() )
+ {
+ kdDebug(14152) << k_funcinfo << "Can't add contact, we are offline!" << endl;
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n("You need to be connected to be able to add contacts."),
+ i18n("AIM") );
+ return;
+ }
+
+ if (KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(),
+ i18n("Do you want to add '%1' to your contact list?").arg(command),
+ QString::null, i18n("Add"), i18n("Do Not Add"))
+ != KMessageBox::Yes)
+ {
+ kdDebug(14152) << k_funcinfo << "Cancelled" << endl;
+ return;
+ }
+
+ kdDebug(14152) << k_funcinfo <<
+ "Adding Contact; screenname = " << firstParam << endl;
+ mc = account->addContact(firstParam, command, 0L, Kopete::Account::Temporary);
+ }
+
+ if ( realCommand == "gochat" )
+ {
+ AIMAccount* aimAccount = dynamic_cast<AIMAccount*>( account );
+ if ( aimAccount && aimAccount->isConnected() )
+ {
+ aimAccount->engine()->joinChatRoom( firstParam, secondParam.toInt() );
+ }
+ else
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n( "Unable to connect to the chat room %1 because the account"
+ " for %2 is not connected." ).arg( firstParam ).arg( aimAccount->accountId() ),
+ QString::null );
+
+ }
+
+ if ( mc && realCommand == "goim" )
+ {
+ mc->execute();
+ }
+
+}
+
+
+
+
+AIMProtocol::AIMProtocol(QObject *parent, const char *name, const QStringList &)
+ : Kopete::Protocol( AIMProtocolFactory::instance(), parent, name ),
+ statusOnline( Kopete::OnlineStatus::Online, 2, this, 0, QString::null, i18n("Online"), i18n("Online"), Kopete::OnlineStatusManager::Online ),
+ statusOffline( Kopete::OnlineStatus::Offline, 2, this, 10, QString::null, i18n("Offline"), i18n("Offline"), Kopete::OnlineStatusManager::Offline ),
+ statusAway( Kopete::OnlineStatus::Away, 2, this, 20, "contact_away_overlay", i18n("Away"), i18n("Away"), Kopete::OnlineStatusManager::Away,
+ Kopete::OnlineStatusManager::HasAwayMessage ),
+ statusWirelessOnline( Kopete::OnlineStatus::Online, 1, this, 30, "contact_phone_overlay", i18n("Mobile"), i18n("Mobile"),
+ Kopete::OnlineStatusManager::Online, Kopete::OnlineStatusManager::HideFromMenu ),
+ statusWirelessAway( Kopete::OnlineStatus::Away, 1, this, 31, QStringList::split( " ", "contact_phone_overlay contact_away_overlay"),
+ i18n("Mobile Away"), i18n("Mobile Away"), Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HideFromMenu ),
+ statusConnecting(Kopete::OnlineStatus::Connecting, 99, this, 99, "aim_connecting", i18n("Connecting...")),
+ awayMessage(Kopete::Global::Properties::self()->awayMessage()),
+ clientFeatures("clientFeatures", i18n("Client Features"), 0, false),
+ clientProfile( "clientProfile", i18n( "User Profile"), 0, false, true),
+ iconHash("iconHash", i18n("Buddy Icon MD5 Hash"), QString::null, true, false, true)
+{
+ if (protocolStatic_)
+ kdDebug(14152) << k_funcinfo << "AIM plugin already initialized" << endl;
+ else
+ protocolStatic_ = this;
+
+ setCapabilities(0x1FFF); // setting capabilities - FIXME to use proper enum
+ kdDebug(14152) << k_funcinfo << "capabilities set to 0x1FFF" << endl;
+ addAddressBookField("messaging/aim", Kopete::Plugin::MakeIndexField);
+ KImageIO::registerFormats();
+}
+
+AIMProtocol::~AIMProtocol()
+{
+ protocolStatic_ =0L;
+}
+
+AIMProtocol *AIMProtocol::protocol(void)
+{
+ return protocolStatic_;
+}
+
+Kopete::Contact *AIMProtocol::deserializeContact(Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &/*addressBookData*/)
+{
+
+ QString contactId = serializedData["contactId"];
+ QString accountId = serializedData["accountId"];
+ QString displayName = serializedData["displayName"];
+
+ // Get the account it belongs to
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
+ Kopete::Account *account = accounts[accountId];
+
+ if ( !account ) //no account
+ return 0;
+
+ uint ssiGid = 0, ssiBid = 0, ssiType = 0xFFFF;
+ QString ssiName;
+ bool ssiWaitingAuth = false;
+ if ( serializedData.contains( "ssi_type" ) )
+ {
+ ssiName = serializedData["ssi_name"];
+ QString authStatus = serializedData["ssi_waitingAuth"];
+ if ( authStatus == "true" )
+ ssiWaitingAuth = true;
+ ssiGid = serializedData["ssi_gid"].toUInt();
+ ssiBid = serializedData["ssi_bid"].toUInt();
+ ssiType = serializedData["ssi_type"].toUInt();
+ }
+
+ Oscar::SSI item( ssiName, ssiGid, ssiBid, ssiType, QValueList<TLV>(), 0 );
+ item.setWaitingAuth( ssiWaitingAuth );
+
+ AIMContact *c = new AIMContact( account, contactId, metaContact, QString::null, item );
+ return c;
+}
+
+AddContactPage *AIMProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account)
+{
+ return ( new AIMAddContactPage( account->isConnected(), parent ) );
+}
+
+KopeteEditAccountWidget *AIMProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return ( new AIMEditAccountWidget( this, account, parent ) );
+}
+
+Kopete::Account *AIMProtocol::createNewAccount(const QString &accountId)
+{
+ return ( new AIMAccount( this, accountId ) );
+}
+
+#include "aimprotocol.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/aim/aimprotocol.h b/kopete/protocols/oscar/aim/aimprotocol.h
new file mode 100644
index 00000000..e6c578e6
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimprotocol.h
@@ -0,0 +1,85 @@
+/*
+ oscarprotocol.h - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef AIMPROTOCOL_H
+#define AIMPROTOCOL_H
+
+#include "kopeteprotocol.h"
+#include "kopetecontactproperty.h"
+#include "kopetemimetypehandler.h"
+#include "kopeteonlinestatus.h"
+
+#include <qmap.h>
+
+namespace Kopete
+{
+class OnlineStatus;
+}
+
+class AIMProtocolHandler : public Kopete::MimeTypeHandler
+{
+public:
+ AIMProtocolHandler();
+ void handleURL( const KURL & url ) const;
+};
+
+class AIMProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ AIMProtocol( QObject *parent, const char *name, const QStringList &args );
+ virtual ~AIMProtocol();
+ /**
+ * Return the active instance of the protocol
+ * because it's a singleton, can only be used inside AIM classes, not in oscar lib
+ */
+ static AIMProtocol *protocol();
+
+ bool canSendOffline() const { return false; }
+
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &addressBookData );
+
+ AddContactPage*createAddContactWidget( QWidget *parent, Kopete::Account *account );
+ KopeteEditAccountWidget* createEditAccountWidget( Kopete::Account *account, QWidget *parent );
+ Kopete::Account* createNewAccount( const QString &accountId );
+
+ /**
+ * The set of online statuses that AIM contacts can have
+ */
+ const Kopete::OnlineStatus statusOnline;
+ const Kopete::OnlineStatus statusOffline;
+ const Kopete::OnlineStatus statusAway;
+ const Kopete::OnlineStatus statusWirelessOnline;
+ const Kopete::OnlineStatus statusWirelessAway;
+ const Kopete::OnlineStatus statusConnecting;
+
+ const Kopete::ContactPropertyTmpl awayMessage;
+ const Kopete::ContactPropertyTmpl clientFeatures;
+ const Kopete::ContactPropertyTmpl clientProfile;
+ const Kopete::ContactPropertyTmpl iconHash;
+
+private:
+ /** The active instance of oscarprotocol */
+ static AIMProtocol *protocolStatic_;
+ AIMProtocolHandler protohandler;
+};
+
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/aimuserinfo.cpp b/kopete/protocols/oscar/aim/aimuserinfo.cpp
new file mode 100644
index 00000000..81bdd9c7
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimuserinfo.cpp
@@ -0,0 +1,224 @@
+/*
+ oscaruserinfo.cpp - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#include "aimuserinfo.h"
+
+#include "aimaccount.h"
+#include "aimcontact.h"
+#include "aimprotocol.h"
+
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <ktextbrowser.h>
+#include <kdebug.h>
+#include <kapplication.h>
+
+#include <ktextedit.h>
+#include <krun.h>
+
+AIMUserInfoDialog::AIMUserInfoDialog( Kopete::Contact *c, AIMAccount *acc, bool modal,
+ QWidget *parent, const char* name )
+ : KDialogBase( parent, name, modal, i18n( "User Information on %1" )
+ .arg( c->property( Kopete::Global::Properties::self()->nickName() ).value().toString() ),
+ Cancel | Ok , Ok, true )
+{
+ kdDebug(14200) << k_funcinfo << "for contact '" << c->contactId() << "'" << endl;
+
+ m_contact = c;
+ mAccount = acc;
+
+ mMainWidget = new AIMUserInfoWidget(this, "aimuserinfowidget");
+ setMainWidget(mMainWidget);
+
+ QObject::connect(this, SIGNAL(okClicked()), this, SLOT(slotSaveClicked()));
+ QObject::connect(this, SIGNAL(user1Clicked()), this, SLOT(slotUpdateClicked()));
+ QObject::connect(this, SIGNAL(cancelClicked()), this, SLOT(slotCloseClicked()));
+ QObject::connect(c, SIGNAL(updatedProfile()), this, SLOT(slotUpdateProfile()));
+
+ mMainWidget->txtScreenName->setText( c->contactId() );
+
+ QString nickName = c->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if( nickName.isEmpty() )
+ mMainWidget->txtNickName->setText( c->contactId() );
+ else
+ mMainWidget->txtNickName->setText( nickName );
+
+ if(m_contact == mAccount->myself()) // edit own account profile
+ {
+ mMainWidget->lblWarnLevel->hide();
+ mMainWidget->txtWarnLevel->hide();
+ mMainWidget->lblIdleTime->hide();
+ mMainWidget->txtIdleTime->hide();
+ mMainWidget->lblOnlineSince->hide();
+ mMainWidget->txtOnlineSince->hide();
+ mMainWidget->txtAwayMessage->hide();
+ mMainWidget->lblAwayMessage->hide();
+
+ userInfoView=0L;
+ mMainWidget->userInfoFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+ QVBoxLayout *l = new QVBoxLayout(mMainWidget->userInfoFrame);
+ userInfoEdit = new KTextEdit(QString::null, QString::null,
+ mMainWidget->userInfoFrame, "userInfoEdit");
+ userInfoEdit->setTextFormat(PlainText);
+
+ AIMMyselfContact* aimmc = dynamic_cast<AIMMyselfContact*>( c );
+ if ( aimmc )
+ userInfoEdit->setText( aimmc->userProfile() );
+ else
+ userInfoEdit->setText( QString::null );
+
+ setButtonText(Ok, i18n("&Save Profile"));
+ showButton(User1, false);
+ l->addWidget(userInfoEdit);
+ }
+ else
+ {
+ userInfoEdit=0L;
+ mMainWidget->userInfoFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
+ QVBoxLayout *l = new QVBoxLayout(mMainWidget->userInfoFrame);
+ userInfoView = new KTextBrowser(mMainWidget->userInfoFrame, "userInfoView");
+ userInfoView->setTextFormat(AutoText);
+ userInfoView->setNotifyClick(true);
+ QObject::connect(
+ userInfoView, SIGNAL(urlClick(const QString&)),
+ this, SLOT(slotUrlClicked(const QString&)));
+ QObject::connect(
+ userInfoView, SIGNAL(mailClick(const QString&, const QString&)),
+ this, SLOT(slotMailClicked(const QString&, const QString&)));
+ showButton(Cancel, false);
+ setButtonText(Ok, i18n("&Close"));
+ setEscapeButton(Ok);
+ l->addWidget(userInfoView);
+
+ if(m_contact->isOnline())
+ {
+ // Update the user view to indicate that we're requesting the user's profile
+ userInfoView->setText(i18n("Requesting User Profile, please wait..."));
+ }
+ QTimer::singleShot(0, this, SLOT(slotUpdateProfile()));
+ }
+}
+
+AIMUserInfoDialog::~AIMUserInfoDialog()
+{
+ kdDebug(14200) << k_funcinfo << "Called." << endl;
+}
+
+void AIMUserInfoDialog::slotUpdateClicked()
+{
+ kdDebug(14200) << k_funcinfo << "Called." << endl;
+ QString newNick = mMainWidget->txtNickName->text();
+ QString currentNick = m_contact->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if ( !newNick.isEmpty() && ( newNick != currentNick ) )
+ {
+ //m_contact->rename(newNick);
+ //emit updateNickname(newNick);
+ setCaption(i18n("User Information on %1").arg(newNick));
+ }
+
+}
+
+void AIMUserInfoDialog::slotSaveClicked()
+{
+ kdDebug(14200) << k_funcinfo << "Called." << endl;
+
+ if (userInfoEdit)
+ { // editable mode, set profile
+ QString newNick = mMainWidget->txtNickName->text();
+ QString currentNick = m_contact->property( Kopete::Global::Properties::self()->nickName() ).value().toString();
+ if(!newNick.isEmpty() && ( newNick != currentNick ) )
+ {
+ //m_contact->rename(newNick);
+ //emit updateNickname(newNick);
+ setCaption(i18n("User Information on %1").arg(newNick));
+ }
+
+ mAccount->setUserProfile(userInfoEdit->text());
+ }
+
+ emit closing();
+}
+
+void AIMUserInfoDialog::slotCloseClicked()
+{
+ kdDebug(14200) << k_funcinfo << "Called." << endl;
+ emit closing();
+}
+
+void AIMUserInfoDialog::slotUpdateProfile()
+{
+ kdDebug(14152) << k_funcinfo << "Got User Profile." << endl;
+ AIMProtocol* p = static_cast<AIMProtocol*>( mAccount->protocol() );
+ QString awayMessage = m_contact->property( p->awayMessage ).value().toString();
+ mMainWidget->txtAwayMessage->setText( awayMessage );
+
+ if ( awayMessage.isNull() )
+ {
+ mMainWidget->txtAwayMessage->hide();
+ mMainWidget->lblAwayMessage->hide();
+ }
+ else
+ {
+ mMainWidget->txtAwayMessage->show();
+ mMainWidget->lblAwayMessage->show();
+ }
+
+ QString onlineSince = m_contact->property("onlineSince").value().toString();
+ //QString onlineSince = m_details.onlineSinceTime().toString();
+ mMainWidget->txtOnlineSince->setText( onlineSince );
+
+ AIMContact* c = static_cast<AIMContact*>( m_contact );
+ mMainWidget->txtIdleTime->setText(c->formattedIdleTime());
+ mMainWidget->txtWarnLevel->setText(QString::number(c->warningLevel()));
+
+ QString contactProfile = m_contact->property( p->clientProfile ).value().toString();
+ if ( contactProfile.isNull() )
+ {
+ contactProfile =
+ i18n("<html><body><I>No user information provided</I></body></html>");
+ }
+
+ if(userInfoEdit)
+ {
+ userInfoEdit->setText(contactProfile);
+ }
+ else if(userInfoView)
+ {
+ userInfoView->setText(contactProfile);
+ }
+
+}
+
+void AIMUserInfoDialog::slotUrlClicked(const QString &url)
+{
+ new KRun(KURL(url));
+}
+
+void AIMUserInfoDialog::slotMailClicked(const QString&, const QString &address)
+{
+ new KRun(KURL(address));
+}
+
+#include "aimuserinfo.moc"
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/aim/aimuserinfo.h b/kopete/protocols/oscar/aim/aimuserinfo.h
new file mode 100644
index 00000000..f128610f
--- /dev/null
+++ b/kopete/protocols/oscar/aim/aimuserinfo.h
@@ -0,0 +1,59 @@
+/*
+ oscaruserinfo.h - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+
+ Kopete (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef AIMUSERINFO_H
+#define AIMUSERINFO_H
+
+#include <kdialogbase.h>
+#include "aiminfobase.h"
+
+namespace Kopete { class Contact; }
+class KTextEdit;
+class OscarAccount;
+class AIMMyselfContact;
+class AIMAccount;
+
+class AIMUserInfoDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ AIMUserInfoDialog(Kopete::Contact *c, AIMAccount *acc, bool modal,
+ QWidget *parent, const char* name);
+ ~AIMUserInfoDialog();
+
+ private:
+ AIMAccount *mAccount;
+ Kopete::Contact* m_contact;
+ AIMUserInfoWidget *mMainWidget;
+ KTextBrowser *userInfoView;
+ KTextEdit *userInfoEdit;
+
+ private slots:
+ void slotSaveClicked();
+ void slotCloseClicked();
+ void slotUpdateClicked();
+ void slotUpdateProfile();
+ void slotUrlClicked(const QString&);
+ void slotMailClicked(const QString&, const QString&);
+
+ signals:
+// void updateNickname(const QString &);
+ void closing();
+};
+
+#endif
+
diff --git a/kopete/protocols/oscar/aim/kopete_aim.desktop b/kopete/protocols/oscar/aim/kopete_aim.desktop
new file mode 100644
index 00000000..0ab4fe69
--- /dev/null
+++ b/kopete/protocols/oscar/aim/kopete_aim.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=aim_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_aim
+X-Kopete-Messaging-Protocol=messaging/aim
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_aim
+X-KDE-PluginInfo-Version=0.10.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=AIM
+Name[bn]=à¦-আই-à¦à¦®
+Name[hi]=à¤à¤†à¤°à¤ˆà¤à¤®
+Name[ne]=à¤à¤†à¤ˆà¤à¤®
+Comment=Protocol to connect to AIM
+Comment[ar]=البرتوكول سيتصل بـ AIM
+Comment[be]=Пратакол AIM
+Comment[bg]=Протокол за връзка Ñ AIM
+Comment[bn]=à¦-আই-à¦à¦®à¦¤à§‡ সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh AIM
+Comment[bs]=AIM protokol
+Comment[ca]=Protocol per a connectar-se a AIM
+Comment[cs]=Protokol k připojení k AIM
+Comment[cy]=Protocol i gysylltu ag AIM
+Comment[da]=Protokol til at forbinde til AIM
+Comment[de]=Protokoll zur Verbindung mit dem AIM
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο AIM
+Comment[es]=Protocolo para conectar a AIM
+Comment[et]=Protokoll ühendumiseks AIM-iga
+Comment[eu]=AIM-era konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال به AIM
+Comment[fi]=Yhteyskäytäntö AIM-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur AIM
+Comment[ga]=Prótacal chun ceangal le AIM
+Comment[gl]=Protocolo para se conectar ó AIM
+Comment[he]=פרוטוקול התחברות ל- AIM
+Comment[hi]=से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na AIM
+Comment[hu]=Protokoll az AIM használatához
+Comment[is]=Samskiptamáti til að tengjast AIM
+Comment[it]=Protocollo per connessione a AIM
+Comment[ja]=AIM ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=AIM დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=AIM-ге қоÑылу протоколы
+Comment[km]=ពិធីការ​ភ្ជាប់​ទៅ AIM
+Comment[lt]=Protokolas prisijungimui prie AIM
+Comment[mk]=Протокол за поврзување на AIM
+Comment[nb]=Protokoll for å koble til AIM
+Comment[nds]=Protokoll för't Tokoppeln na AIM
+Comment[ne]=à¤à¤†à¤ˆà¤à¤® मा जडान गरà¥à¤¨à¥à¤ªà¤°à¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor AOL Instant Messenger
+Comment[nn]=Protokoll for å kopla til AIM
+Comment[pl]=Protokół połączenia z serwerem AIM
+Comment[pt]=Um protocolo para se ligar ao AIM
+Comment[pt_BR]=Protocolo de conexão ao AIM
+Comment[ro]=Protocol de conectare la AIM
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº AIM
+Comment[sk]=Protokol pre pripojenie k AIM
+Comment[sl]=Protokol za povezavo na AIM
+Comment[sr]=Протокол за повезивање на AIM
+Comment[sr@Latn]=Protokol za povezivanje na AIM
+Comment[sv]=Protokoll för att ansluta till AIM
+Comment[ta]=IRC உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба AIM
+Comment[tr]=AIM'e bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· AIM
+Comment[uz]=AIM bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=AIM билан алоқа ўрнатиш учун протокол
+Comment[zh_CN]=连接到 AIM åè®®
+Comment[zh_HK]=用來連接至 AIM 的通訊å”定
+Comment[zh_TW]=連線到 AIM çš„å”定
diff --git a/kopete/protocols/oscar/aim/ui/Makefile.am b/kopete/protocols/oscar/aim/ui/Makefile.am
new file mode 100644
index 00000000..aa690449
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/Makefile.am
@@ -0,0 +1,15 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../liboscar \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteaimui.la
+
+libkopeteaimui_la_SOURCES = aimaddcontactui.ui aimeditaccountui.ui \
+ aiminfobase.ui aimjoinchatbase.ui aimaddcontactpage.cpp aimeditaccountwidget.cpp
+
+libkopeteaimui_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la
+
+
diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp
new file mode 100644
index 00000000..cf5fbae5
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.cpp
@@ -0,0 +1,83 @@
+/***************************************************************************
+ description
+ -------------------
+ begin :
+ copyright : (C) 2002 by nbetcher
+ email : nbetcher@usinternet.com
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "aimaddcontactui.h"
+#include "aimaddcontactpage.h"
+
+#include "kopeteaccount.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+AIMAddContactPage::AIMAddContactPage(bool connected, QWidget *parent,
+ const char *name )
+ : AddContactPage(parent,name)
+{
+ m_gui = 0;
+ (new QVBoxLayout(this))->setAutoAdd(true);
+
+ if(connected)
+ {
+ m_gui = new aimAddContactUI(this);
+ canadd = true;
+ }
+ else
+ {
+ noaddMsg1 = new QLabel(i18n("You need to be connected to be able to add contacts."), this);
+ noaddMsg2 = new QLabel(i18n("Connect to the AIM network and try again."), this);
+ canadd = false;
+ }
+}
+
+
+AIMAddContactPage::~AIMAddContactPage()
+{
+}
+
+bool AIMAddContactPage::validateData()
+{
+ if ( !canadd )
+ return false;
+
+ if ( !m_gui )
+ return false;
+
+ QString sn = m_gui->addSN->text();
+ if ( sn.isEmpty() )
+ {
+ KMessageBox::sorry ( this,
+ i18n("<qt>You must enter a valid screen name.</qt>"),
+ i18n("No Screen Name") );
+ return false;
+ }
+ return true;
+}
+
+bool AIMAddContactPage::apply(Kopete::Account *account,
+ Kopete::MetaContact *metaContact)
+{
+ if(validateData())
+ { // If everything is ok
+ return account->addContact( m_gui->addSN->text(), metaContact, Kopete::Account::ChangeKABC );
+ }
+ return false;
+}
+//kate: tab-width 4; indent-mode csands;
+
+#include "aimaddcontactpage.moc"
diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h
new file mode 100644
index 00000000..979d0472
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimaddcontactpage.h
@@ -0,0 +1,41 @@
+
+#ifndef AIMADDCONTACTPAGE_H
+#define AIMADDCONTACTPAGE_H
+
+#include <qwidget.h>
+#include <qlabel.h>
+#include "addcontactpage.h"
+
+class aimAddContactUI;
+class AIMAccount;
+namespace Kopete
+{
+class Account;
+class MetaContact;
+}
+
+class AIMAddContactPage : public AddContactPage
+{
+Q_OBJECT
+
+public:
+ AIMAddContactPage(bool connected, QWidget *parent=0,
+ const char *name=0);
+ ~AIMAddContactPage();
+
+ /** Validates the data entered */
+ virtual bool validateData();
+ /** Applies the addition to the account */
+ virtual bool apply( Kopete::Account *account, Kopete::MetaContact *);
+
+protected:
+ /** The actual GUI */
+ aimAddContactUI *m_gui;
+ QLabel *noaddMsg1;
+ QLabel *noaddMsg2;
+ bool canadd;
+};
+#endif
+
+//kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui b/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui
new file mode 100644
index 00000000..b3267eb0
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimaddcontactui.ui
@@ -0,0 +1,64 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>aimAddContactUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>aimAddContactUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>455</width>
+ <height>131</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Contact Information</string>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <property name="layoutSpacing" stdset="0">
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>addSN</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>AIM screen name:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>addSN</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui b/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui
new file mode 100644
index 00000000..d8a7b9f3
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimeditaccountui.ui
@@ -0,0 +1,540 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>aimEditAccountUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>aimEditAccountUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>560</width>
+ <height>583</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - AIM</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>labelStatusMessage</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget6</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox72</cstring>
+ </property>
+ <property name="title">
+ <string>Account Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblAccountId</cstring>
+ </property>
+ <property name="text">
+ <string>AIM &amp;screen name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtAccountId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The screen name of your AIM account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The screen name of your AIM account. This should be in the form of an alphanumeric string (spaces allowed, not case sensitive).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>edtAccountId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The screen name of your AIM account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The screen name of your AIM account. This should be in the form of an alphanumeric string (spaces allowed, not case sensitive).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget" row="1" column="0">
+ <property name="name">
+ <cstring>mPasswordWidget</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>mGlobalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Exclu&amp;de from Global Identity</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>mAutoLogon</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the AOL Instant Messaging network, you will need to use a screen name from AIM, AOL, or .Mac.&lt;br&gt;&lt;br&gt;If you do not currently have an AIM screen name, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonRegister</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;gister New Account</string>
+ </property>
+ </widget>
+ </hbox>
+ </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>90</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Accou&amp;nt Preferences</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox73</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionOverrideServer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Override default server information</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout58</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtServerAddress</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the AIM server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the AIM server you wish to connect to. Normally you will want the default (login.oscar.aol.com).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>edtServerAddress</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>login.oscar.aol.com</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the AIM server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the AIM server you wish to connect to. Normally you will want the default (login.oscar.aol.com).</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sbxServerPort</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the AIM server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the AIM server that you would like to connect to. Normally this is 5190.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>sbxServerPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65534</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5190</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the AIM server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the AIM server that you would like to connect to. Normally this is 5190.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer21</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>200</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>encodingCombo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Default to the following &amp;encoding for messages:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>encodingCombo</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Pri&amp;vacy</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Visibility settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>rbAllowPerimtList</cstring>
+ </property>
+ <property name="text">
+ <string>Allow only from visible list</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>rbBlockAll</cstring>
+ </property>
+ <property name="text">
+ <string>Block all users</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="1">
+ <property name="name">
+ <cstring>rbBlockAIM</cstring>
+ </property>
+ <property name="text">
+ <string>Block AIM users</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="1">
+ <property name="name">
+ <cstring>rbBlockDenyList</cstring>
+ </property>
+ <property name="text">
+ <string>Block only from invisible list</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rbAllowAll</cstring>
+ </property>
+ <property name="text">
+ <string>Allow all users</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0">
+ <property name="name">
+ <cstring>rbAllowMyContacts</cstring>
+ </property>
+ <property name="text">
+ <string>Allow only contact list's users</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>225</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</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="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblServer</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>edtServerAddress</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>sbxServerPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget6</tabstop>
+ <tabstop>edtAccountId</tabstop>
+ <tabstop>mAutoLogon</tabstop>
+ <tabstop>buttonRegister</tabstop>
+ <tabstop>optionOverrideServer</tabstop>
+ <tabstop>edtServerAddress</tabstop>
+ <tabstop>sbxServerPort</tabstop>
+ <tabstop>encodingCombo</tabstop>
+ <tabstop>rbAllowAll</tabstop>
+ <tabstop>rbAllowMyContacts</tabstop>
+ <tabstop>rbAllowPerimtList</tabstop>
+ <tabstop>rbBlockAll</tabstop>
+ <tabstop>rbBlockAIM</tabstop>
+ <tabstop>rbBlockDenyList</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kopetepasswordwidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp
new file mode 100644
index 00000000..de720e17
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.cpp
@@ -0,0 +1,172 @@
+#include "aimeditaccountwidget.h"
+#include "aimeditaccountui.h"
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+
+#include <kdebug.h>
+#include <krun.h>
+#include <kpassdlg.h>
+#include <kconfig.h>
+
+#include "kopetepassword.h"
+#include "kopetepasswordwidget.h"
+
+#include "aimprotocol.h"
+#include "aimaccount.h"
+
+AIMEditAccountWidget::AIMEditAccountWidget( AIMProtocol *protocol,
+ Kopete::Account *account, QWidget *parent, const char *name )
+ : QWidget( parent, name ), KopeteEditAccountWidget( account )
+{
+ //kdDebug(14152) << k_funcinfo << "Called." << endl;
+
+ mAccount = dynamic_cast<AIMAccount*>( account );
+ mProtocol = protocol;
+
+ // create the gui (generated from a .ui file)
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ mGui = new aimEditAccountUI( this, "AIMEditAccountWidget::mGui" );
+
+ // Read in the settings from the account if it exists
+ if ( mAccount )
+ {
+ mGui->mPasswordWidget->load( &mAccount->password() );
+ mGui->edtAccountId->setText( account->accountId() );
+ //Remove me after we can change Account IDs (Matt)
+ mGui->edtAccountId->setDisabled( true );
+ mGui->mAutoLogon->setChecked( account->excludeConnect() );
+ QString serverEntry = account->configGroup()->readEntry( "Server", "login.oscar.aol.com" );
+ int portEntry = account->configGroup()->readNumEntry( "Port", 5190 );
+ if ( serverEntry != "login.oscar.aol.com" || portEntry != 5190 )
+ mGui->optionOverrideServer->setChecked( true );
+ else
+ mGui->optionOverrideServer->setChecked( false );
+
+ mGui->edtServerAddress->setText( serverEntry );
+ mGui->sbxServerPort->setValue( portEntry );
+
+ using namespace AIM::PrivacySettings;
+
+ int privacySetting = mAccount->configGroup()->readNumEntry( "PrivacySetting", AllowAll );
+ switch( privacySetting )
+ {
+ case AllowAll:
+ mGui->rbAllowAll->setChecked( true );
+ break;
+ case AllowMyContacts:
+ mGui->rbAllowMyContacts->setChecked( true );
+ break;
+ case AllowPremitList:
+ mGui->rbAllowPerimtList->setChecked( true );
+ break;
+ case BlockAll:
+ mGui->rbBlockAll->setChecked( true );
+ break;
+ case BlockAIM:
+ mGui->rbBlockAIM->setChecked( true );
+ break;
+ case BlockDenyList:
+ mGui->rbBlockDenyList->setChecked( true );
+ break;
+ default:
+ mGui->rbAllowAll->setChecked( true );
+ }
+
+ // Global Identity
+ mGui->mGlobalIdentity->setChecked( account->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) );
+ }
+ QObject::connect( mGui->buttonRegister, SIGNAL( clicked() ), this, SLOT( slotOpenRegister() ) );
+
+ /* Set tab order to password custom widget correctly */
+ QWidget::setTabOrder( mGui->edtAccountId, mGui->mPasswordWidget->mRemembered );
+ QWidget::setTabOrder( mGui->mPasswordWidget->mRemembered, mGui->mPasswordWidget->mPassword );
+ QWidget::setTabOrder( mGui->mPasswordWidget->mPassword, mGui->mAutoLogon );
+}
+
+AIMEditAccountWidget::~AIMEditAccountWidget()
+{}
+
+Kopete::Account *AIMEditAccountWidget::apply()
+{
+ kdDebug( 14152 ) << k_funcinfo << "Called." << endl;
+
+ // If this is a new account, create it
+ if ( !mAccount )
+ {
+ kdDebug( 14152 ) << k_funcinfo << "creating a new account" << endl;
+ QString newId = mGui->edtAccountId->text();
+ mAccount = new AIMAccount( mProtocol, newId );
+ }
+
+ mGui->mPasswordWidget->save( &mAccount->password() );
+
+ mAccount->setExcludeConnect( mGui->mAutoLogon->isChecked() ); // save the autologon choice
+ if ( mGui->optionOverrideServer->isChecked() )
+ {
+ static_cast<OscarAccount *>( mAccount )->setServerAddress( mGui->edtServerAddress->text() );
+ static_cast<OscarAccount *>( mAccount )->setServerPort( mGui->sbxServerPort->value() );
+ }
+ else
+ {
+ static_cast<OscarAccount *>( mAccount )->setServerAddress( "login.oscar.aol.com" );
+ static_cast<OscarAccount *>( mAccount )->setServerPort( 5190 );
+ }
+
+ using namespace AIM::PrivacySettings;
+ int privacySetting = AllowAll;
+
+ if ( mGui->rbAllowAll->isChecked() )
+ privacySetting = AllowAll;
+ else if ( mGui->rbAllowMyContacts->isChecked() )
+ privacySetting = AllowMyContacts;
+ else if ( mGui->rbAllowPerimtList->isChecked() )
+ privacySetting = AllowPremitList;
+ else if ( mGui->rbBlockAll->isChecked() )
+ privacySetting = BlockAll;
+ else if ( mGui->rbBlockAIM->isChecked() )
+ privacySetting = BlockAIM;
+ else if ( mGui->rbBlockDenyList->isChecked() )
+ privacySetting = BlockDenyList;
+
+ mAccount->configGroup()->writeEntry( "PrivacySetting", privacySetting );
+ mAccount->setPrivacySettings( privacySetting );
+
+ // Global Identity
+ mAccount->configGroup()->writeEntry( "ExcludeGlobalIdentity", mGui->mGlobalIdentity->isChecked() );
+ return mAccount;
+}
+
+bool AIMEditAccountWidget::validateData()
+{
+ //kdDebug(14152) << k_funcinfo << "Called." << endl;
+
+ QString userName = mGui->edtAccountId->text();
+ QString server = mGui->edtServerAddress->text();
+ int port = mGui->sbxServerPort->value();
+
+ if ( userName.length() < 1 )
+ return false;
+
+ if ( port < 1 )
+ return false;
+
+ if ( server.length() < 1 )
+ return false;
+
+ // Seems good to me
+ //kdDebug(14152) << k_funcinfo << "Account data validated successfully." << endl;
+ return true;
+}
+
+void AIMEditAccountWidget::slotOpenRegister()
+{
+ KRun::runURL( "http://my.screenname.aol.com/_cqr/login/login.psp?siteId=snshomepage&mcState=initialized&createSn=1", "text/html" );
+}
+
+#include "aimeditaccountwidget.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h
new file mode 100644
index 00000000..ccb2b451
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimeditaccountwidget.h
@@ -0,0 +1,58 @@
+/*
+ AIMeditaccountwidget.h - AIM Account Widget
+
+ Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef AIMEDITACCOUNTWIDGET_H
+#define AIMEDITACCOUNTWIDGET_H
+
+#include <qwidget.h>
+#include "editaccountwidget.h"
+/**
+ * @author Chris TenHarmsel <tenharmsel@staticmethod.net>
+ */
+
+namespace Kopete
+{
+class Account;
+}
+
+class AIMAccount;
+class AIMProtocol;
+class aimEditAccountUI;
+
+class AIMEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+Q_OBJECT
+
+public:
+ AIMEditAccountWidget(AIMProtocol *protocol, Kopete::Account *account,
+ QWidget *parent=0, const char *name=0);
+ virtual ~AIMEditAccountWidget();
+
+ virtual bool validateData();
+ virtual Kopete::Account *apply();
+
+private slots:
+ void slotOpenRegister();
+
+protected:
+ AIMAccount *mAccount;
+ AIMProtocol *mProtocol;
+ aimEditAccountUI *mGui;
+};
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/aim/ui/aiminfobase.ui b/kopete/protocols/oscar/aim/ui/aiminfobase.ui
new file mode 100644
index 00000000..db22a574
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aiminfobase.ui
@@ -0,0 +1,246 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>AIMUserInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AIMUserInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>360</width>
+ <height>408</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>360</width>
+ <height>400</height>
+ </size>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout9</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblNickName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Nickname:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>txtNickName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblScreenName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Screen name:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>txtScreenName</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblWarnLevel</cstring>
+ </property>
+ <property name="text">
+ <string>Warning level:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>txtWarnLevel</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblIdleTime</cstring>
+ </property>
+ <property name="text">
+ <string>Idle minutes:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>txtIdleTime</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblOnlineSince</cstring>
+ </property>
+ <property name="text">
+ <string>Online since:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>txtOnlineSince</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblAwayMessage</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Away message:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ </widget>
+ <widget class="KTextBrowser">
+ <property name="name">
+ <cstring>txtAwayMessage</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>AutoText</enum>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Profile:</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>userInfoFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>64</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>txtNickName</tabstop>
+ <tabstop>txtScreenName</tabstop>
+ <tabstop>txtWarnLevel</tabstop>
+ <tabstop>txtIdleTime</tabstop>
+ <tabstop>txtOnlineSince</tabstop>
+ <tabstop>txtAwayMessage</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>ktextbrowser.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui b/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui
new file mode 100644
index 00000000..d1d93edf
--- /dev/null
+++ b/kopete/protocols/oscar/aim/ui/aimjoinchatbase.ui
@@ -0,0 +1,124 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>AIMJoinChatBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>AIMJoinChatBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>343</width>
+ <height>99</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Please enter the name of the chat room you wish to join.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>60</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Room &amp;name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>roomName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>E&amp;xchange:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>exchange</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="2">
+ <property name="name">
+ <cstring>roomName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="2">
+ <property name="name">
+ <cstring>exchange</cstring>
+ </property>
+ </widget>
+ <spacer row="4" column="2">
+ <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>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icons/Makefile.am b/kopete/protocols/oscar/icons/Makefile.am
new file mode 100644
index 00000000..9143c6b4
--- /dev/null
+++ b/kopete/protocols/oscar/icons/Makefile.am
@@ -0,0 +1,2 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
diff --git a/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png
new file mode 100644
index 00000000..05a91d59
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr128-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png
new file mode 100644
index 00000000..4c976880
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr128-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_away.png b/kopete/protocols/oscar/icons/cr16-action-aim_away.png
new file mode 100644
index 00000000..36fa2beb
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-aim_away.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng b/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng
new file mode 100644
index 00000000..90417a14
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-aim_connecting.mng
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_offline.png b/kopete/protocols/oscar/icons/cr16-action-aim_offline.png
new file mode 100644
index 00000000..c070d66b
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-aim_offline.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-aim_online.png b/kopete/protocols/oscar/icons/cr16-action-aim_online.png
new file mode 100644
index 00000000..ff1087cb
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-aim_online.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_away.png b/kopete/protocols/oscar/icons/cr16-action-icq_away.png
new file mode 100644
index 00000000..81dfb456
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_away.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng b/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng
new file mode 100644
index 00000000..1a7aa5a5
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_connecting.mng
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png b/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png
new file mode 100644
index 00000000..cf94ee0f
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_dnd.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png b/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png
new file mode 100644
index 00000000..51f51623
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_ffc.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png b/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png
new file mode 100644
index 00000000..c7e37ce9
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_invisible.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_na.png b/kopete/protocols/oscar/icons/cr16-action-icq_na.png
new file mode 100644
index 00000000..b1aa91af
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_na.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png b/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png
new file mode 100644
index 00000000..d4689965
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_occupied.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_offline.png b/kopete/protocols/oscar/icons/cr16-action-icq_offline.png
new file mode 100644
index 00000000..a9d11031
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_offline.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-action-icq_online.png b/kopete/protocols/oscar/icons/cr16-action-icq_online.png
new file mode 100644
index 00000000..8a9dac28
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-action-icq_online.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png
new file mode 100644
index 00000000..1e89df85
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png
new file mode 100644
index 00000000..eda0f8c4
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr16-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png
new file mode 100644
index 00000000..590aee96
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr32-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png
new file mode 100644
index 00000000..0f153eac
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr32-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png
new file mode 100644
index 00000000..8aeb38e1
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr48-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png
new file mode 100644
index 00000000..ee04d73b
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr48-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png b/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png
new file mode 100644
index 00000000..49e8c91a
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr64-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png b/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png
new file mode 100644
index 00000000..7b9d8cad
--- /dev/null
+++ b/kopete/protocols/oscar/icons/cr64-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_away.png b/kopete/protocols/oscar/icons/hi16-action-aim_away.png
new file mode 100644
index 00000000..3924d7c6
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-aim_away.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng b/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng
new file mode 100644
index 00000000..19767318
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-aim_connecting.mng
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_offline.png b/kopete/protocols/oscar/icons/hi16-action-aim_offline.png
new file mode 100644
index 00000000..49a4ef5f
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-aim_offline.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-aim_online.png b/kopete/protocols/oscar/icons/hi16-action-aim_online.png
new file mode 100644
index 00000000..e6e53d90
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-aim_online.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_away.png b/kopete/protocols/oscar/icons/hi16-action-icq_away.png
new file mode 100644
index 00000000..ccee571d
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_away.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng b/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng
new file mode 100644
index 00000000..a44714b7
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_connecting.mng
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png b/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png
new file mode 100644
index 00000000..600e2438
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_dnd.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png b/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png
new file mode 100644
index 00000000..d31ee480
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_ffc.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png b/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png
new file mode 100644
index 00000000..c5ed807d
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_invisible.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_na.png b/kopete/protocols/oscar/icons/hi16-action-icq_na.png
new file mode 100644
index 00000000..7df16924
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_na.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png b/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png
new file mode 100644
index 00000000..154bc21d
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_occupied.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_offline.png b/kopete/protocols/oscar/icons/hi16-action-icq_offline.png
new file mode 100644
index 00000000..42189ff5
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_offline.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-action-icq_online.png b/kopete/protocols/oscar/icons/hi16-action-icq_online.png
new file mode 100644
index 00000000..52e431a2
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-action-icq_online.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png b/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png
new file mode 100644
index 00000000..e6e53d90
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png b/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png
new file mode 100644
index 00000000..45b2a9cd
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi16-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png b/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png
new file mode 100644
index 00000000..f1fe71d2
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi32-app-aim_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png b/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png
new file mode 100644
index 00000000..e8a3f030
--- /dev/null
+++ b/kopete/protocols/oscar/icons/hi32-app-icq_protocol.png
Binary files differ
diff --git a/kopete/protocols/oscar/icq/Makefile.am b/kopete/protocols/oscar/icq/Makefile.am
new file mode 100644
index 00000000..defadf37
--- /dev/null
+++ b/kopete/protocols/oscar/icq/Makefile.am
@@ -0,0 +1,22 @@
+SUBDIRS = ui .
+METASOURCES = AUTO
+AM_CPPFLAGS = -I$(srcdir)/../ \
+ -I$(srcdir)/ui/ \
+ -I$(top_builddir)/kopete/protocols/oscar/icq/ui \
+ -I$(srcdir)/../liboscar \
+ $(KOPETE_INCLUDES) $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_icq.la
+
+kopete_icq_la_SOURCES = icqpresence.cpp icqaccount.cpp icqcontact.cpp icqprotocol.cpp
+# icquserinfo.cpp icqreadaway.cpp icqsendsmsdialog.cpp
+
+kopete_icq_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_icq_la_LIBADD = ../libkopete_oscar.la \
+ $(top_builddir)/kopete/libkopete/libkopete.la ui/libkopeteicqui.la
+
+service_DATA = kopete_icq.desktop
+servicedir = $(kde_servicesdir)
+
+mime_DATA = x-icq.desktop
+mimedir = $(kde_mimedir)/application
diff --git a/kopete/protocols/oscar/icq/icqaccount.cpp b/kopete/protocols/oscar/icq/icqaccount.cpp
new file mode 100644
index 00000000..9a071442
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqaccount.cpp
@@ -0,0 +1,529 @@
+/*
+ icqaccount.cpp - ICQ Account Class
+
+ Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qfile.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kmdcodec.h>
+#include <kmessagebox.h>
+
+#include "kopeteawayaction.h"
+#include "kopetemessage.h"
+#include "kopetecontactlist.h"
+#include "kopeteuiglobal.h"
+
+#include "client.h"
+#include "icquserinfo.h"
+#include "oscarsettings.h"
+#include "oscarutils.h"
+#include "ssimanager.h"
+
+#include "icqcontact.h"
+#include "icqprotocol.h"
+#include "icqaccount.h"
+
+#include "oscarvisibilitydialog.h"
+
+ICQMyselfContact::ICQMyselfContact( ICQAccount *acct ) : OscarMyselfContact( acct )
+{
+ QObject::connect( acct->engine(), SIGNAL( loggedIn() ), this, SLOT( fetchShortInfo() ) );
+ QObject::connect( acct->engine(), SIGNAL( receivedIcqShortInfo( const QString& ) ),
+ this, SLOT( receivedShortInfo( const QString& ) ) );
+}
+
+void ICQMyselfContact::userInfoUpdated()
+{
+ DWORD extendedStatus = details().extendedStatus();
+ kdDebug( OSCAR_ICQ_DEBUG ) << k_funcinfo << "extendedStatus is " << QString::number( extendedStatus, 16 ) << endl;
+ ICQ::Presence presence = ICQ::Presence::fromOscarStatus( extendedStatus & 0xffff );
+ setOnlineStatus( presence.toOnlineStatus() );
+ setProperty( Kopete::Global::Properties::self()->awayMessage(), static_cast<ICQAccount*>( account() )->engine()->statusMessage() );
+}
+
+void ICQMyselfContact::receivedShortInfo( const QString& contact )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ ICQShortInfo shortInfo = static_cast<ICQAccount*>( account() )->engine()->getShortInfo( contact );
+ if ( !shortInfo.nickname.isEmpty() )
+ {
+ setProperty( Kopete::Global::Properties::self()->nickName(), static_cast<ICQAccount*>( account() )->defaultCodec()->toUnicode( shortInfo.nickname ) );
+ }
+}
+
+void ICQMyselfContact::fetchShortInfo()
+{
+ static_cast<ICQAccount*>( account() )->engine()->requestShortInfo( contactId() );
+}
+
+ICQAccount::ICQAccount(Kopete::Protocol *parent, QString accountID, const char *name)
+ : OscarAccount(parent, accountID, name, true)
+{
+ kdDebug(14152) << k_funcinfo << accountID << ": Called."<< endl;
+ setMyself( new ICQMyselfContact( this ) );
+ myself()->setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() );
+
+ m_visibilityDialog = 0;
+
+ QString nickName = configGroup()->readEntry("NickName", QString::null);
+ mWebAware = configGroup()->readBoolEntry( "WebAware", false );
+ mHideIP = configGroup()->readBoolEntry( "HideIP", true );
+ mInitialStatusMessage = QString::null;
+
+ QObject::connect( Kopete::ContactList::self(), SIGNAL( globalIdentityChanged( const QString&, const QVariant& ) ),
+ this, SLOT( slotGlobalIdentityChanged( const QString&, const QVariant& ) ) );
+
+ QObject::connect( this, SIGNAL( buddyIconChanged() ), this, SLOT( slotBuddyIconChanged() ) );
+
+ //setIgnoreUnknownContacts(pluginData(protocol(), "IgnoreUnknownContacts").toUInt() == 1);
+
+ /* FIXME: need to do this when web aware or hide ip change
+ if(isConnected() && (oldhideip != mHideIP || oldwebaware != mWebAware))
+ {
+ kdDebug(14153) << k_funcinfo <<
+ "sending status to reflect HideIP and WebAware settings" << endl;
+ //setStatus(mStatus, QString::null);
+ }*/
+}
+
+ICQAccount::~ICQAccount()
+{
+}
+
+ICQProtocol* ICQAccount::protocol()
+{
+ return static_cast<ICQProtocol*>(OscarAccount::protocol());
+}
+
+
+ICQ::Presence ICQAccount::presence()
+{
+ return ICQ::Presence::fromOnlineStatus( myself()->onlineStatus() );
+}
+
+
+KActionMenu* ICQAccount::actionMenu()
+{
+ KActionMenu* actionMenu = Kopete::Account::actionMenu();
+
+ actionMenu->popupMenu()->insertSeparator();
+
+ KToggleAction* actionInvisible =
+ new KToggleAction( i18n( "In&visible" ),
+ ICQ::Presence( presence().type(), ICQ::Presence::Invisible ).toOnlineStatus().iconFor( this ),
+ 0, this, SLOT( slotToggleInvisible() ), this );
+ actionInvisible->setChecked( presence().visibility() == ICQ::Presence::Invisible );
+ actionMenu->insert( actionInvisible );
+
+ actionMenu->popupMenu()->insertSeparator();
+ actionMenu->insert( new KToggleAction( i18n( "Set Visibility..." ), 0, 0,
+ this, SLOT( slotSetVisiblility() ), this,
+ "ICQAccount::mActionSetVisibility") );
+ //actionMenu->insert( new KToggleAction( i18n( "Send &SMS..." ), 0, 0, this, SLOT( slotSendSMS() ), this, "ICQAccount::mActionSendSMS") );
+
+ return actionMenu;
+}
+
+
+void ICQAccount::connectWithPassword( const QString &password )
+{
+ if ( password.isNull() )
+ return;
+
+ kdDebug(14153) << k_funcinfo << "accountId='" << accountId() << "'" << endl;
+
+ Kopete::OnlineStatus status = initialStatus();
+ if ( status == Kopete::OnlineStatus() &&
+ status.status() == Kopete::OnlineStatus::Unknown )
+ //use default online in case of invalid online status for connecting
+ status = Kopete::OnlineStatus( Kopete::OnlineStatus::Online );
+ ICQ::Presence pres = ICQ::Presence::fromOnlineStatus( status );
+ bool accountIsOffline = ( presence().type() == ICQ::Presence::Offline ||
+ myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() );
+
+ if ( accountIsOffline )
+ {
+ myself()->setOnlineStatus( protocol()->statusManager()->connectingStatus() );
+ QString icqNumber = accountId();
+ kdDebug(14153) << k_funcinfo << "Logging in as " << icqNumber << endl ;
+ QString server = configGroup()->readEntry( "Server", QString::fromLatin1( "login.oscar.aol.com" ) );
+ uint port = configGroup()->readNumEntry( "Port", 5190 );
+ Connection* c = setupConnection( server, port );
+
+ //set up the settings for the account
+ Oscar::Settings* oscarSettings = engine()->clientSettings();
+ oscarSettings->setWebAware( configGroup()->readBoolEntry( "WebAware", false ) );
+ oscarSettings->setHideIP( configGroup()->readBoolEntry( "HideIP", true ) );
+ //FIXME: also needed for the other call to setStatus (in setPresenceTarget)
+ DWORD status = pres.toOscarStatus();
+
+ if ( !mHideIP )
+ status |= ICQ::StatusCode::SHOWIP;
+ if ( mWebAware )
+ status |= ICQ::StatusCode::WEBAWARE;
+
+ engine()->setStatus( status, mInitialStatusMessage );
+ updateVersionUpdaterStamp();
+ engine()->start( server, port, accountId(), password );
+ engine()->connectToServer( c, server, true /* doAuth */ );
+
+ mInitialStatusMessage = QString::null;
+ }
+}
+
+void ICQAccount::disconnected( DisconnectReason reason )
+{
+ kdDebug(14153) << k_funcinfo << "Attempting to set status offline" << endl;
+ ICQ::Presence presOffline = ICQ::Presence( ICQ::Presence::Offline, presence().visibility() );
+ myself()->setOnlineStatus( presOffline.toOnlineStatus() );
+
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for( ; it.current(); ++it )
+ {
+ OscarContact* oc = dynamic_cast<OscarContact*>( it.current() );
+ if ( oc )
+ {
+ if ( oc->ssiItem().waitingAuth() )
+ oc->setOnlineStatus( protocol()->statusManager()->waitingForAuth() );
+ else
+ oc->setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() );
+ }
+ }
+
+ OscarAccount::disconnected( reason );
+}
+
+
+void ICQAccount::slotToggleInvisible()
+{
+ using namespace ICQ;
+ setInvisible( (presence().visibility() == Presence::Visible) ? Presence::Invisible : Presence::Visible );
+}
+
+void ICQAccount::slotSetVisiblility()
+{
+ if( !isConnected() )
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(),
+ i18n("You must be online to set users visibility."),
+ i18n("ICQ Plugin") );
+ return;
+ }
+
+ if ( !m_visibilityDialog )
+ {
+ m_visibilityDialog = new OscarVisibilityDialog( engine(), Kopete::UI::Global::mainWidget() );
+ QObject::connect( m_visibilityDialog, SIGNAL( closing() ),
+ this, SLOT( slotVisibilityDialogClosed() ) );
+
+ //add all contacts;
+ OscarVisibilityDialog::ContactMap contactMap;
+ //temporary map for faster lookup of contactId
+ QMap<QString, QString> revContactMap;
+
+ QValueList<Oscar::SSI> contactList = engine()->ssiManager()->contactList();
+ QValueList<Oscar::SSI>::const_iterator it, cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ {
+ QString contactId = ( *it ).name();
+
+ OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it ).name()] );
+ if ( oc )
+ { //for better orientation in lists use nickName and icq number
+ QString screenName( "%1 (%2)" );
+ screenName = screenName.arg( oc->nickName(), contactId);
+ contactMap.insert( screenName, contactId );
+ revContactMap.insert( contactId, screenName );
+ }
+ else
+ {
+ contactMap.insert( contactId, contactId );
+ revContactMap.insert( contactId, contactId );
+ }
+ }
+ m_visibilityDialog->addContacts( contactMap );
+
+ //add contacts from visible list
+ QStringList tmpList;
+
+ contactList = engine()->ssiManager()->visibleList();
+ cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ tmpList.append( revContactMap[( *it ).name()] );
+
+ m_visibilityDialog->addVisibleContacts( tmpList );
+
+ //add contacts from invisible list
+ tmpList.clear();
+
+ contactList = engine()->ssiManager()->invisibleList();
+ cEnd = contactList.constEnd();
+
+ for ( it = contactList.constBegin(); it != cEnd; ++it )
+ tmpList.append( revContactMap[( *it ).name()] );
+
+ m_visibilityDialog->addInvisibleContacts( tmpList );
+
+ m_visibilityDialog->resize( 550, 350 );
+ m_visibilityDialog->show();
+ }
+ else
+ {
+ m_visibilityDialog->raise();
+ }
+}
+
+void ICQAccount::slotVisibilityDialogClosed()
+{
+ m_visibilityDialog->delayedDestruct();
+ m_visibilityDialog = 0L;
+}
+
+void ICQAccount::setAway( bool away, const QString &awayReason )
+{
+ kdDebug(14153) << k_funcinfo << "account='" << accountId() << "'" << endl;
+ if ( away )
+ setPresenceType( ICQ::Presence::Away, awayReason );
+ else
+ setPresenceType( ICQ::Presence::Online );
+}
+
+
+void ICQAccount::setInvisible( ICQ::Presence::Visibility vis )
+{
+ ICQ::Presence pres = presence();
+ if ( vis == pres.visibility() )
+ return;
+
+ kdDebug(14153) << k_funcinfo << "changing invisible setting to " << (int)vis << endl;
+ setPresenceTarget( ICQ::Presence( pres.type(), vis ) );
+}
+
+void ICQAccount::setPresenceType( ICQ::Presence::Type type, const QString &message )
+{
+ ICQ::Presence pres = presence();
+ kdDebug(14153) << k_funcinfo << "new type=" << (int)type << ", old type=" << (int)pres.type() << ", new message=" << message << endl;
+ //setAwayMessage(awayMessage);
+ setPresenceTarget( ICQ::Presence( type, pres.visibility() ), message );
+}
+
+void ICQAccount::setPresenceTarget( const ICQ::Presence &newPres, const QString &message )
+{
+ bool targetIsOffline = (newPres.type() == ICQ::Presence::Offline);
+ bool accountIsOffline = ( presence().type() == ICQ::Presence::Offline ||
+ myself()->onlineStatus() == protocol()->statusManager()->connectingStatus() );
+
+ if ( targetIsOffline )
+ {
+ OscarAccount::disconnect();
+ // allow toggling invisibility when offline
+ myself()->setOnlineStatus( newPres.toOnlineStatus() );
+ }
+ else if ( accountIsOffline )
+ {
+ mInitialStatusMessage = message;
+ OscarAccount::connect( newPres.toOnlineStatus() );
+ }
+ else
+ {
+ engine()->setStatus( newPres.toOscarStatus(), message );
+ }
+}
+
+
+void ICQAccount::setOnlineStatus( const Kopete::OnlineStatus& status, const QString& reason )
+{
+ if ( status.status() == Kopete::OnlineStatus::Invisible )
+ {
+ // called from outside, i.e. not by our custom action menu entry...
+
+ if ( presence().type() == ICQ::Presence::Offline )
+ {
+ // ...when we are offline go online invisible.
+ setPresenceTarget( ICQ::Presence( ICQ::Presence::Online, ICQ::Presence::Invisible ) );
+ }
+ else
+ {
+ // ...when we are not offline set invisible.
+ setInvisible( ICQ::Presence::Invisible );
+ }
+ }
+ else
+ {
+ setPresenceType( ICQ::Presence::fromOnlineStatus( status ).type(), reason );
+ }
+}
+
+
+OscarContact *ICQAccount::createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem )
+{
+ ICQContact* contact = new ICQContact( this, contactId, parentContact, QString::null, ssiItem );
+ if ( !ssiItem.alias().isEmpty() )
+ contact->setProperty( Kopete::Global::Properties::self()->nickName(), ssiItem.alias() );
+
+ if ( isConnected() )
+ contact->loggedIn();
+
+ return contact;
+}
+
+QString ICQAccount::sanitizedMessage( const QString& message )
+{
+ return Kopete::Message::escape( message );
+}
+
+
+void ICQAccount::slotGlobalIdentityChanged( const QString& key, const QVariant& value )
+{
+ //do something with the photo
+ kdDebug(14153) << k_funcinfo << "Global identity changed" << endl;
+ kdDebug(14153) << k_funcinfo << "key: " << key << endl;
+ kdDebug(14153) << k_funcinfo << "value: " << value << endl;
+
+ if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) )
+ {
+ if ( key == Kopete::Global::Properties::self()->nickName().key() )
+ {
+ //edit ssi item to change alias (if possible)
+ }
+
+ if ( key == Kopete::Global::Properties::self()->photo().key() )
+ {
+ setBuddyIcon( value.toString() );
+ }
+ }
+}
+
+void ICQAccount::slotBuddyIconChanged()
+{
+ // need to disconnect because we could end up with many connections
+ QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) );
+ if ( !engine()->isActive() )
+ {
+ QObject::connect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotBuddyIconChanged() ) );
+ return;
+ }
+
+ QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString();
+
+ SSIManager* ssi = engine()->ssiManager();
+ Oscar::SSI item = ssi->findItemForIconByRef( 1 );
+
+ if ( photoPath.isEmpty() )
+ {
+ if ( item )
+ {
+ kdDebug(14153) << k_funcinfo << "Removing icon hash item from ssi" << endl;
+ Oscar::SSI s(item);
+
+ //remove hash and alias
+ QValueList<TLV> tList( item.tlvList() );
+ TLV t = Oscar::findTLV( tList, 0x00D5 );
+ if ( t )
+ tList.remove( t );
+
+ t = Oscar::findTLV( tList, 0x0131 );
+ if ( t )
+ tList.remove( t );
+
+ item.setTLVList( tList );
+ //s is old, item is new. modification will occur
+ engine()->modifySSIItem( s, item );
+ }
+ }
+ else
+ {
+ QFile iconFile( photoPath );
+ iconFile.open( IO_ReadOnly );
+
+ KMD5 iconHash;
+ iconHash.update( iconFile );
+ kdDebug(14153) << k_funcinfo << "hash is :" << iconHash.hexDigest() << endl;
+
+ //find old item, create updated item
+ if ( !item )
+ {
+ kdDebug(14153) << k_funcinfo << "no existing icon hash item in ssi. creating new" << endl;
+
+ TLV t;
+ t.type = 0x00D5;
+ t.data.resize( 18 );
+ t.data[0] = 0x01;
+ t.data[1] = 0x10;
+ memcpy(t.data.data() + 2, iconHash.rawDigest(), 16);
+ t.length = t.data.size();
+
+ //alias, it's always empty
+ TLV t2;
+ t2.type = 0x0131;
+ t2.length = 0;
+
+ QValueList<Oscar::TLV> list;
+ list.append( t );
+ list.append( t2 );
+
+ Oscar::SSI s( "1", 0, ssi->nextContactId(), ROSTER_BUDDYICONS, list );
+
+ //item is a non-valid ssi item, so the function will add an item
+ kdDebug(14153) << k_funcinfo << "setting new icon item" << endl;
+ engine()->modifySSIItem( item, s );
+ }
+ else
+ { //found an item
+ Oscar::SSI s(item);
+ kdDebug(14153) << k_funcinfo << "modifying old item in ssi." << endl;
+ QValueList<TLV> tList( item.tlvList() );
+
+ TLV t = Oscar::findTLV( tList, 0x00D5 );
+ if ( t )
+ tList.remove( t );
+ else
+ t.type = 0x00D5;
+
+ t.data.resize( 18 );
+ t.data[0] = 0x01;
+ t.data[1] = 0x10;
+ memcpy(t.data.data() + 2, iconHash.rawDigest(), 16);
+ t.length = t.data.size();
+ tList.append( t );
+
+ //add empty alias
+ t = Oscar::findTLV( tList, 0x0131 );
+ if ( !t )
+ {
+ t.type = 0x0131;
+ t.length = 0;
+ tList.append( t );
+ }
+
+ item.setTLVList( tList );
+ //s is old, item is new. modification will occur
+ engine()->modifySSIItem( s, item );
+ }
+ iconFile.close();
+ }
+}
+
+#include "icqaccount.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/icq/icqaccount.h b/kopete/protocols/oscar/icq/icqaccount.h
new file mode 100644
index 00000000..f6231ec9
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqaccount.h
@@ -0,0 +1,105 @@
+/*
+ icqaccount.h - ICQ Account Class Header
+
+ Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+*/
+
+#ifndef ICQACCOUNT_H
+#define ICQACCOUNT_H
+
+#include "oscaraccount.h"
+#include "oscarmyselfcontact.h"
+
+#include "icqpresence.h"
+#include "oscartypeclasses.h"
+
+class KAction;
+namespace Kopete { class AwayAction; }
+class ICQProtocol;
+class ICQAccount;
+class OscarVisibilityDialog;
+
+class ICQMyselfContact : public OscarMyselfContact
+{
+Q_OBJECT
+public:
+ ICQMyselfContact( ICQAccount *acct );
+ void userInfoUpdated();
+
+public slots:
+ void receivedShortInfo( const QString& );
+ void fetchShortInfo();
+};
+
+
+class ICQAccount : public OscarAccount
+{
+Q_OBJECT
+
+public:
+ ICQAccount( Kopete::Protocol *parent, QString accountID, const char *name = 0L );
+ virtual ~ICQAccount();
+
+ ICQProtocol *protocol();
+
+ // Accessor method for the action menu
+ virtual KActionMenu* actionMenu();
+
+ /** Reimplementation from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus&, const QString& );
+
+ virtual void setAway( bool away, const QString &awayReason );
+
+ void connectWithPassword( const QString &password );
+
+ void setUserProfile( const QString &profile );
+
+protected:
+ virtual OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem );
+
+ virtual QString sanitizedMessage( const QString& message );
+
+protected slots:
+ virtual void disconnected( DisconnectReason reason );
+
+
+private:
+ ICQ::Presence presence();
+
+ void setInvisible( ICQ::Presence::Visibility );
+ void setPresenceType( ICQ::Presence::Type, const QString &awayMessage = QString::null );
+ void setPresenceTarget( const ICQ::Presence &presence, const QString &message = QString::null );
+
+ //const unsigned long fullStatus( const unsigned long plainStatus );
+
+private slots:
+ void slotToggleInvisible();
+
+ void slotSetVisiblility();
+ void slotVisibilityDialogClosed();
+
+ void slotGlobalIdentityChanged( const QString& key, const QVariant& value );
+
+ void slotBuddyIconChanged();
+
+private:
+ bool mWebAware;
+ bool mHideIP;
+ QString mInitialStatusMessage;
+ OscarVisibilityDialog* m_visibilityDialog;
+};
+
+#endif
+//kate: indent-mode csands;
diff --git a/kopete/protocols/oscar/icq/icqcontact.cpp b/kopete/protocols/oscar/icq/icqcontact.cpp
new file mode 100644
index 00000000..8ba8d195
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqcontact.cpp
@@ -0,0 +1,939 @@
+/*
+ icqontact.cpp - Oscar Protocol Plugin
+
+ Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2003 by Olivier Goffart
+ Kopete (c) 2003-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqcontact.h"
+
+#include <qtimer.h>
+#include <qimage.h>
+#include <qfile.h>
+
+#include <kaction.h>
+#include <kactionclasses.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kpassivepopup.h>
+#include <kinputdialog.h>
+#include <kmdcodec.h>
+#include <kstandarddirs.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+
+#include "icquserinfo.h"
+#include "icqreadaway.h"
+#include "icqprotocol.h"
+#include "icqaccount.h"
+#include "icqpresence.h"
+#include "icquserinfowidget.h"
+#include "icqauthreplydialog.h"
+
+#include "client.h"
+#include "oscarutils.h"
+#include "oscarencodingselectiondialog.h"
+#include "ssimanager.h"
+
+ICQContact::ICQContact( ICQAccount *account, const QString &name, Kopete::MetaContact *parent,
+ const QString& icon, const Oscar::SSI& ssiItem )
+: OscarContact( account, name, parent, icon, ssiItem )
+{
+ mProtocol = static_cast<ICQProtocol *>(protocol());
+ m_infoWidget = 0L;
+ m_requestingNickname = false;
+ m_oesd = 0;
+ m_buddyIconDirty = false;
+
+ if ( ssiItem.waitingAuth() )
+ setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
+ else
+ setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() );
+
+ QObject::connect( mAccount->engine(), SIGNAL( loggedIn() ), this, SLOT( loggedIn() ) );
+ //QObject::connect( mAccount->engine(), SIGNAL( userIsOnline( const QString& ) ), this, SLOT( userOnline( const QString&, UserDetails ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( userIsOffline( const QString& ) ), this, SLOT( userOffline( const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( authRequestReceived( const QString&, const QString& ) ),
+ this, SLOT( slotGotAuthRequest( const QString&, const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( authReplyReceived( const QString&, const QString&, bool ) ),
+ this, SLOT( slotGotAuthReply(const QString&, const QString&, bool ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedIcqShortInfo( const QString& ) ),
+ this, SLOT( receivedShortInfo( const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedIcqLongInfo( const QString& ) ),
+ this, SLOT( receivedLongInfo( const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ),
+ this, SLOT( userInfoUpdated( const QString&, const UserDetails& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const QString&, const QString& ) ),
+ this, SLOT( receivedStatusMessage( const QString&, const QString& ) ) );
+ QObject::connect( mAccount->engine(), SIGNAL( receivedAwayMessage( const Oscar::Message& ) ),
+ this, SLOT( receivedStatusMessage( const Oscar::Message& ) ) );
+ QObject::connect( this, SIGNAL( featuresUpdated() ), this, SLOT( updateFeatures() ) );
+ QObject::connect( mAccount->engine(), SIGNAL( iconServerConnected() ),
+ this, SLOT( requestBuddyIcon() ) );
+ QObject::connect( mAccount->engine(), SIGNAL( haveIconForContact( const QString&, QByteArray ) ),
+ this, SLOT( haveIcon( const QString&, QByteArray ) ) );
+
+}
+
+ICQContact::~ICQContact()
+{
+ delete m_infoWidget;
+}
+
+void ICQContact::updateSSIItem()
+{
+ //kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << endl;
+ if ( m_ssiItem.waitingAuth() )
+ setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
+
+ if ( m_ssiItem.type() != 0xFFFF && m_ssiItem.waitingAuth() == false &&
+ onlineStatus() == Kopete::OnlineStatus::Unknown )
+ {
+ //make sure they're offline
+ setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() );
+ }
+}
+
+
+void ICQContact::userInfoUpdated( const QString& contact, const UserDetails& details )
+{
+ //kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << contact << contactId() << endl;
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ // invalidate old away message if user was offline
+ if ( !isOnline() )
+ removeProperty( mProtocol->awayMessage );
+
+ kdDebug( OSCAR_ICQ_DEBUG ) << k_funcinfo << "extendedStatus is " << details.extendedStatus() << endl;
+ ICQ::Presence presence = ICQ::Presence::fromOscarStatus( details.extendedStatus() & 0xffff );
+ setOnlineStatus( presence.toOnlineStatus() );
+
+ // ICQ does not support status messages for state Online
+ if ( presence.type() == ICQ::Presence::Online )
+ {
+ mAccount->engine()->removeICQAwayMessageRequest( contactId() );
+ removeProperty( mProtocol->awayMessage );
+ }
+ else
+ {
+ if ( ICQ::Presence::fromOnlineStatus( account()->myself()->onlineStatus() ).visibility() == ICQ::Presence::Visible )
+ {
+ switch ( presence.type() )
+ {
+ case ICQ::Presence::Away:
+ mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQAway );
+ break;
+ case ICQ::Presence::NotAvailable:
+ mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQNotAvailable );
+ break;
+ case ICQ::Presence::Occupied:
+ mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQOccupied );
+ break;
+ case ICQ::Presence::DoNotDisturb:
+ mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQDoNotDisturb );
+ break;
+ case ICQ::Presence::FreeForChat:
+ mAccount->engine()->addICQAwayMessageRequest( contactId(), Client::ICQFreeForChat );
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ mAccount->engine()->removeICQAwayMessageRequest( contactId() );
+ }
+ }
+
+
+ if ( details.dcOutsideSpecified() )
+ {
+ if ( details.dcExternalIp().isUnspecified() )
+ removeProperty( mProtocol->ipAddress );
+ else
+ setProperty( mProtocol->ipAddress, details.dcExternalIp().toString() );
+ }
+
+ if ( details.capabilitiesSpecified() )
+ {
+ if ( details.clientName().isEmpty() )
+ removeProperty( mProtocol->clientFeatures );
+ else
+ setProperty( mProtocol->clientFeatures, details.clientName() );
+ }
+
+ if ( details.buddyIconHash().size() > 0 && details.buddyIconHash() != m_details.buddyIconHash() )
+ {
+ m_buddyIconDirty = true;
+ if ( cachedBuddyIcon( details.buddyIconHash() ) == false )
+ {
+ if ( !mAccount->engine()->hasIconConnection() )
+ {
+ mAccount->engine()->connectToIconServer();
+ }
+ else
+ {
+ int time = ( KApplication::random() % 10 ) * 1000;
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating buddy icon in "
+ << time/1000 << " seconds" << endl;
+ QTimer::singleShot( time, this, SLOT( requestBuddyIcon() ) );
+ }
+ }
+ }
+
+ OscarContact::userInfoUpdated( contact, details );
+}
+
+void ICQContact::userOnline( const QString& userId )
+{
+ if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " online" << endl;
+ ICQ::Presence online = mProtocol->statusManager()->presenceOf( ICQ::Presence::Online );
+ //mAccount->engine()->requestStatusInfo( contactId() );
+}
+
+void ICQContact::userOffline( const QString& userId )
+{
+ if ( Oscar::normalize( userId ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_ICQ_DEBUG) << "Setting " << userId << " offline" << endl;
+ ICQ::Presence offline = mProtocol->statusManager()->presenceOf( ICQ::Presence::Offline );
+ setOnlineStatus( mProtocol->statusManager()->onlineStatusOf( offline ) );
+}
+
+void ICQContact::loggedIn()
+{
+ if ( metaContact()->isTemporary() )
+ return;
+
+ if ( m_ssiItem.waitingAuth() )
+ setOnlineStatus( mProtocol->statusManager()->waitingForAuth() );
+
+ if ( ( ( hasProperty( Kopete::Global::Properties::self()->nickName().key() )
+ && nickName() == contactId() )
+ || !hasProperty( Kopete::Global::Properties::self()->nickName().key() ) ) &&
+ !m_requestingNickname && m_ssiItem.alias().isEmpty() )
+ {
+ m_requestingNickname = true;
+ int time = ( KApplication::random() % 20 ) * 1000;
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "updating nickname in " << time/1000 << " seconds" << endl;
+ QTimer::singleShot( time, this, SLOT( requestShortInfo() ) );
+ }
+
+}
+
+void ICQContact::requestShortInfo()
+{
+ if ( mAccount->isConnected() )
+ mAccount->engine()->requestShortInfo( contactId() );
+}
+
+void ICQContact::slotRequestAuth()
+{
+ QString reason = KInputDialog::getText( i18n("Request Authorization"),
+ i18n("Reason for requesting authorization:") );
+ if ( !reason.isNull() )
+ mAccount->engine()->requestAuth( contactId(), reason );
+}
+
+void ICQContact::slotSendAuth()
+{
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Sending auth reply" << endl;
+ ICQAuthReplyDialog replyDialog( 0, "replyDialog", false );
+
+ replyDialog.setUser( property( Kopete::Global::Properties::self()->nickName() ).value().toString() );
+ if ( replyDialog.exec() )
+ mAccount->engine()->sendAuth( contactId(), replyDialog.reason(), replyDialog.grantAuth() );
+}
+
+void ICQContact::slotGotAuthReply( const QString& contact, const QString& reason, bool granted )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << endl;
+ QString message;
+ if( granted )
+ {
+ message = i18n( "User %1 has granted your authorization request.\nReason: %2" )
+ .arg( property( Kopete::Global::Properties::self()->nickName() ).value().toString() )
+ .arg( reason );
+
+ // remove the unknown status
+ setOnlineStatus( ICQ::Presence( ICQ::Presence::Offline, ICQ::Presence::Visible ).toOnlineStatus() );
+ }
+ else
+ {
+ message = i18n( "User %1 has rejected the authorization request.\nReason: %2" )
+ .arg( property( Kopete::Global::Properties::self()->nickName() ).value().toString() )
+ .arg( reason );
+ }
+ KNotifyClient::event( Kopete::UI::Global::sysTrayWId(), "icq_authorization", message );
+}
+
+void ICQContact::slotGotAuthRequest( const QString& contact, const QString& reason )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ ICQAuthReplyDialog *replyDialog = new ICQAuthReplyDialog();
+
+ connect( replyDialog, SIGNAL( okClicked() ), this, SLOT( slotAuthReplyDialogOkClicked() ) );
+ replyDialog->setUser( property( Kopete::Global::Properties::self()->nickName() ).value().toString() );
+ replyDialog->setRequestReason( reason );
+ replyDialog->setModal( TRUE );
+ replyDialog->show();
+}
+
+void ICQContact::slotAuthReplyDialogOkClicked()
+{
+ // Do not need to delete will delete itself automatically
+ ICQAuthReplyDialog *replyDialog = (ICQAuthReplyDialog*)sender();
+
+ if (replyDialog)
+ mAccount->engine()->sendAuth( contactId(), replyDialog->reason(), replyDialog->grantAuth() );
+}
+
+void ICQContact::receivedLongInfo( const QString& contact )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ {
+ if ( m_infoWidget )
+ m_infoWidget->delayedDestruct();
+ return;
+ }
+
+ QTextCodec* codec = contactCodec();
+
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "received long info from engine" << endl;
+
+ ICQGeneralUserInfo genInfo = mAccount->engine()->getGeneralInfo( contact );
+ if ( m_ssiItem.alias().isEmpty() && !genInfo.nickname.isEmpty() )
+ setNickName( codec->toUnicode( genInfo.nickname ) );
+ emit haveBasicInfo( genInfo );
+
+ ICQWorkUserInfo workInfo = mAccount->engine()->getWorkInfo( contact );
+ emit haveWorkInfo( workInfo );
+
+ ICQMoreUserInfo moreInfo = mAccount->engine()->getMoreInfo( contact );
+ emit haveMoreInfo( moreInfo );
+
+ ICQInterestInfo interestInfo = mAccount->engine()->getInterestInfo( contact );
+ emit haveInterestInfo( interestInfo );
+
+}
+
+void ICQContact::receivedShortInfo( const QString& contact )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ QTextCodec* codec = contactCodec();
+
+ m_requestingNickname = false; //done requesting nickname
+ ICQShortInfo shortInfo = mAccount->engine()->getShortInfo( contact );
+ /*
+ if(!shortInfo.firstName.isEmpty())
+ setProperty( mProtocol->firstName, codec->toUnicode( shortInfo.firstName ) );
+ else
+ removeProperty(mProtocol->firstName);
+
+ if(!shortInfo.lastName.isEmpty())
+ setProperty( mProtocol->lastName, codec->toUnicode( shortInfo.lastName ) );
+ else
+ removeProperty(mProtocol->lastName);
+ */
+ if ( m_ssiItem.alias().isEmpty() && !shortInfo.nickname.isEmpty() )
+ {
+ kdDebug(14153) << k_funcinfo <<
+ "setting new displayname for former UIN-only Contact" << endl;
+ setProperty( Kopete::Global::Properties::self()->nickName(), codec->toUnicode( shortInfo.nickname ) );
+ }
+
+}
+
+void ICQContact::receivedStatusMessage( const QString &contact, const QString &message )
+{
+ if ( Oscar::normalize( contact ) != Oscar::normalize( contactId() ) )
+ return;
+
+ if ( ! message.isEmpty() )
+ setProperty( mProtocol->awayMessage, message );
+ else
+ removeProperty( mProtocol->awayMessage );
+}
+
+void ICQContact::receivedStatusMessage( const Oscar::Message &message )
+{
+ if ( Oscar::normalize( message.sender() ) != Oscar::normalize( contactId() ) )
+ return;
+
+ //decode message
+ QTextCodec* codec = contactCodec();
+
+ QString realText = message.text(codec);
+
+ if ( !realText.isEmpty() )
+ setProperty( mProtocol->awayMessage, realText );
+ else
+ removeProperty( mProtocol->awayMessage );
+}
+
+void ICQContact::slotSendMsg( Kopete::Message& msg, Kopete::ChatSession* session )
+{
+ //Why is this unused?
+ Q_UNUSED( session );
+
+ QTextCodec* codec = contactCodec();
+
+ int messageChannel = 0x01;
+ Oscar::Message::Encoding messageEncoding;
+
+ if ( isOnline() && m_details.hasCap( CAP_UTF8 ) )
+ messageEncoding = Oscar::Message::UCS2;
+ else
+ messageEncoding = Oscar::Message::UserDefined;
+
+ QString msgText( msg.plainBody() );
+ // TODO: More intelligent handling of message length.
+ uint chunk_length = !isOnline() ? 450 : 4096;
+ uint msgPosition = 0;
+
+ do
+ {
+ QString msgChunk( msgText.mid( msgPosition, chunk_length ) );
+ // Try to split on space if needed
+ if ( msgChunk.length() == chunk_length )
+ {
+ for ( int i = 0; i < 100; i++ )
+ {
+ if ( msgChunk[chunk_length - i].isSpace() )
+ {
+ msgChunk = msgChunk.left( chunk_length - i );
+ msgPosition++;
+ }
+ }
+ }
+ msgPosition += msgChunk.length();
+
+ Oscar::Message message( messageEncoding, msgChunk, messageChannel, 0, msg.timestamp(), codec );
+ message.setSender( mAccount->accountId() );
+ message.setReceiver( mName );
+ mAccount->engine()->sendMessage( message );
+ } while ( msgPosition < msgText.length() );
+
+ manager(Kopete::Contact::CanCreate)->appendMessage(msg);
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+void ICQContact::updateFeatures()
+{
+ setProperty( static_cast<ICQProtocol*>(protocol())->clientFeatures, m_clientFeatures );
+}
+
+void ICQContact::requestBuddyIcon()
+{
+ if ( m_buddyIconDirty && m_details.buddyIconHash().size() > 0 )
+ {
+ account()->engine()->requestBuddyIcon( contactId(), m_details.buddyIconHash(),
+ m_details.iconCheckSumType() );
+ }
+}
+
+void ICQContact::haveIcon( const QString& user, QByteArray icon )
+{
+ if ( Oscar::normalize( user ) != Oscar::normalize( contactId() ) )
+ return;
+
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Updating icon for " << contactId() << endl;
+
+ KMD5 buddyIconHash( icon );
+ if ( memcmp( buddyIconHash.rawDigest(), m_details.buddyIconHash().data(), 16 ) == 0 )
+ {
+ QString iconLocation( locateLocal( "appdata", "oscarpictures/"+ contactId() ) );
+
+ QFile iconFile( iconLocation );
+ if ( !iconFile.open( IO_WriteOnly ) )
+ {
+ kdDebug(14153) << k_funcinfo << "Cannot open file"
+ << iconLocation << " for writing!" << endl;
+ return;
+ }
+
+ iconFile.writeBlock( icon );
+ iconFile.close();
+
+ setProperty( Kopete::Global::Properties::self()->photo(), QString::null );
+ setProperty( Kopete::Global::Properties::self()->photo(), iconLocation );
+ m_buddyIconDirty = false;
+ }
+ else
+ {
+ kdDebug(14153) << k_funcinfo << "Buddy icon hash does not match!" << endl;
+ removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+}
+
+bool ICQContact::cachedBuddyIcon( QByteArray hash )
+{
+ QString iconLocation( locateLocal( "appdata", "oscarpictures/"+ contactId() ) );
+
+ QFile iconFile( iconLocation );
+ if ( !iconFile.open( IO_ReadOnly ) )
+ return false;
+
+ KMD5 buddyIconHash;
+ buddyIconHash.update( iconFile );
+ iconFile.close();
+
+ if ( memcmp( buddyIconHash.rawDigest(), hash.data(), 16 ) == 0 )
+ {
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Updating icon for "
+ << contactId() << " from local cache" << endl;
+ setProperty( Kopete::Global::Properties::self()->photo(), QString::null );
+ setProperty( Kopete::Global::Properties::self()->photo(), iconLocation );
+ m_buddyIconDirty = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+#if 0
+void ICQContact::slotContactChanged(const UserInfo &u)
+{
+ if (u.sn != contactName())
+ return;
+
+ // update mInfo and general stuff from OscarContact
+ slotParseUserInfo(u);
+
+ /*kdDebug(14190) << k_funcinfo << "Called for '"
+ << displayName() << "', contactName()=" << contactName() << endl;*/
+ QStringList capList;
+ // Append client name and version in case we found one
+ if (!mInfo.clientName.isEmpty())
+ {
+ if (!mInfo.clientVersion.isEmpty())
+ {
+ capList << i18n("Translators: client-name client-version",
+ "%1 %2").arg(mInfo.clientName, mInfo.clientVersion);
+ }
+ else
+ {
+ capList << mInfo.clientName;
+ }
+ }
+ // and now for some general informative capabilities
+ if (hasCap(CAP_UTF8))
+ capList << i18n("UTF-8");
+ if (hasCap(CAP_RTFMSGS))
+ capList << i18n("RTF-Messages");
+ if (hasCap(CAP_IMIMAGE))
+ capList << i18n("DirectIM/IMImage");
+ if (hasCap(CAP_CHAT))
+ capList << i18n("Groupchat");
+
+ if (capList.count() > 0)
+ setProperty(mProtocol->clientFeatures, capList.join(", "));
+ else
+ removeProperty(mProtocol->clientFeatures);
+
+ unsigned int newStatus = 0;
+ mInvisible = (mInfo.icqextstatus & ICQ_STATUS_IS_INVIS);
+
+ if (mInfo.icqextstatus & ICQ_STATUS_IS_FFC)
+ newStatus = OSCAR_FFC;
+ else if (mInfo.icqextstatus & ICQ_STATUS_IS_DND)
+ newStatus = OSCAR_DND;
+ else if (mInfo.icqextstatus & ICQ_STATUS_IS_OCC)
+ newStatus = OSCAR_OCC;
+ else if (mInfo.icqextstatus & ICQ_STATUS_IS_NA)
+ newStatus = OSCAR_NA;
+ else if (mInfo.icqextstatus & ICQ_STATUS_IS_AWAY)
+ newStatus = OSCAR_AWAY;
+ else
+ newStatus = OSCAR_ONLINE;
+
+ if (this != account()->myself())
+ {
+ if(newStatus != onlineStatus().internalStatus())
+ {
+ if(newStatus != OSCAR_ONLINE) // if user changed to some state other than online
+ {
+ mAccount->engine()->requestAwayMessage(this);
+ }
+ else // user changed to "Online" status and has no away message anymore
+ {
+ removeProperty(mProtocol->awayMessage);
+ }
+ }
+ }
+
+ setStatus(newStatus);
+}
+
+void ICQContact::slotOffgoingBuddy(QString sender)
+{
+ if(sender != contactName())
+ return;
+
+ removeProperty(mProtocol->clientFeatures);
+ removeProperty(mProtocol->awayMessage);
+ setOnlineStatus(mProtocol->statusOffline);
+}
+
+void ICQContact::gotIM(OscarSocket::OscarMessageType /*type*/, const QString &message)
+{
+ // Build a Kopete::Message and set the body as Rich Text
+ Kopete::ContactPtrList tmpList;
+ tmpList.append(account()->myself());
+ Kopete::Message msg(this, tmpList, message, Kopete::Message::Inbound,
+ Kopete::Message::RichText);
+ manager(true)->appendMessage(msg);
+}
+
+
+void ICQContact::slotSendMsg(Kopete::Message& message, Kopete::ChatSession *)
+{
+ if (message.plainBody().isEmpty()) // no text, do nothing
+ return;
+
+ // Check to see if we're even online
+ if(!account()->isConnected())
+ {
+ KMessageBox::sorry(Kopete::UI::Global::mainWidget(),
+ i18n("<qt>You must be logged on to ICQ before you can "
+ "send a message to a user.</qt>"),
+ i18n("Not Signed On"));
+ return;
+ }
+
+ // FIXME: We don't do HTML in ICQ
+ // we might be able to do that in AIM and we might also convert
+ // HTML to RTF for ICQ type-2 messages [mETz]
+ static_cast<OscarAccount*>(account())->engine()->sendIM(
+ message.plainBody(), this, false);
+
+ // Show the message we just sent in the chat window
+ manager(Kopete::Contact::CanCreate)->appendMessage(message);
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+#endif
+
+bool ICQContact::isReachable()
+{
+ return account()->isConnected();
+}
+
+QPtrList<KAction> *ICQContact::customContextMenuActions()
+{
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+/*
+ QString awTxt;
+ QString awIcn;
+ unsigned int status = onlineStatus().internalStatus();
+ if (status >= 15)
+ status -= 15; // get rid of invis addon
+ switch(status)
+ {
+ case OSCAR_FFC:
+ awTxt = i18n("Read 'Free For Chat' &Message");
+ awIcn = "icq_ffc";
+ break;
+ case OSCAR_DND:
+ awTxt = i18n("Read 'Do Not Disturb' &Message");
+ awIcn = "icq_dnd";
+ break;
+ case OSCAR_NA:
+ awTxt = i18n("Read 'Not Available' &Message");
+ awIcn = "icq_na";
+ break;
+ case OSCAR_OCC:
+ awTxt = i18n("Read 'Occupied' &Message");
+ awIcn = "icq_occ";
+ break;
+ default:
+ awTxt = i18n("Read 'Away' &Message");
+ awIcn = "icq_away";
+ break;
+ }
+
+ if(actionReadAwayMessage==0)
+ {
+ actionReadAwayMessage = new KAction(awTxt, awIcn, 0,
+ this, SLOT(slotReadAwayMessage()), this, "actionReadAwayMessage");
+ */
+ actionRequestAuth = new KAction(i18n("&Request Authorization"), "mail_reply", 0,
+ this, SLOT(slotRequestAuth()), this, "actionRequestAuth");
+ actionSendAuth = new KAction(i18n("&Grant Authorization"), "mail_forward", 0,
+ this, SLOT(slotSendAuth()), this, "actionSendAuth");
+ /*
+ }
+ else
+ {
+ actionReadAwayMessage->setText(awTxt);
+ actionReadAwayMessage->setIconSet(SmallIconSet(awIcn));
+ }
+
+*/
+ m_actionIgnore = new KToggleAction(i18n("&Ignore"), "", 0,
+ this, SLOT(slotIgnore()), this, "actionIgnore");
+ m_actionVisibleTo = new KToggleAction(i18n("Always &Visible To"), "", 0,
+ this, SLOT(slotVisibleTo()), this, "actionVisibleTo");
+ m_actionInvisibleTo = new KToggleAction(i18n("Always &Invisible To"), "", 0,
+ this, SLOT(slotInvisibleTo()), this, "actionInvisibleTo");
+
+ bool on = account()->isConnected();
+ if ( m_ssiItem.waitingAuth() )
+ actionRequestAuth->setEnabled(on);
+ else
+ actionRequestAuth->setEnabled(false);
+
+ actionSendAuth->setEnabled(on);
+
+
+ m_selectEncoding = new KAction( i18n( "Select Encoding..." ), "charset", 0,
+ this, SLOT( changeContactEncoding() ), this, "changeEncoding" );
+
+/*
+ actionReadAwayMessage->setEnabled(status != OSCAR_OFFLINE && status != OSCAR_ONLINE);
+*/
+ m_actionIgnore->setEnabled(on);
+ m_actionVisibleTo->setEnabled(on);
+ m_actionInvisibleTo->setEnabled(on);
+
+ SSIManager* ssi = account()->engine()->ssiManager();
+ m_actionIgnore->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_IGNORE ));
+ m_actionVisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_VISIBLE ));
+ m_actionInvisibleTo->setChecked( ssi->findItem( m_ssiItem.name(), ROSTER_INVISIBLE ));
+
+ actionCollection->append(actionRequestAuth);
+ actionCollection->append(actionSendAuth);
+ actionCollection->append( m_selectEncoding );
+
+ actionCollection->append(m_actionIgnore);
+ actionCollection->append(m_actionVisibleTo);
+ actionCollection->append(m_actionInvisibleTo);
+
+// actionCollection->append(actionReadAwayMessage);
+
+ return actionCollection;
+}
+
+
+void ICQContact::slotUserInfo()
+{
+ m_infoWidget = new ICQUserInfoWidget( Kopete::UI::Global::mainWidget(), "icq info" );
+ QObject::connect( m_infoWidget, SIGNAL( finished() ), this, SLOT( closeUserInfoDialog() ) );
+ m_infoWidget->setContact( this );
+ m_infoWidget->show();
+ if ( account()->isConnected() )
+ mAccount->engine()->requestFullInfo( contactId() );
+}
+
+void ICQContact::closeUserInfoDialog()
+{
+ QObject::disconnect( this, 0, m_infoWidget, 0 );
+ m_infoWidget->delayedDestruct();
+ m_infoWidget = 0L;
+}
+
+void ICQContact::changeContactEncoding()
+{
+ if ( m_oesd )
+ return;
+
+ m_oesd = new OscarEncodingSelectionDialog( Kopete::UI::Global::mainWidget(), property(mProtocol->contactEncoding).value().toInt() );
+ connect( m_oesd, SIGNAL( closing( int ) ),
+ this, SLOT( changeEncodingDialogClosed( int ) ) );
+ m_oesd->show();
+}
+
+void ICQContact::changeEncodingDialogClosed( int result )
+{
+ if ( result == QDialog::Accepted )
+ {
+ int mib = m_oesd->selectedEncoding();
+ if ( mib != 0 )
+ {
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "setting encoding mib to "
+ << m_oesd->selectedEncoding() << endl;
+ setProperty( mProtocol->contactEncoding, m_oesd->selectedEncoding() );
+ }
+ else
+ {
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo
+ << "setting encoding to default" << endl;
+ removeProperty( mProtocol->contactEncoding );
+ }
+ }
+
+ if ( m_oesd )
+ {
+ m_oesd->delayedDestruct();
+ m_oesd = 0L;
+ }
+}
+
+
+void ICQContact::slotIgnore()
+{
+ account()->engine()->setIgnore( contactId(), m_actionIgnore->isChecked() );
+}
+
+void ICQContact::slotVisibleTo()
+{
+ account()->engine()->setVisibleTo( contactId(), m_actionVisibleTo->isChecked() );
+}
+
+void ICQContact::slotInvisibleTo()
+{
+ account()->engine()->setInvisibleTo( contactId(), m_actionInvisibleTo->isChecked() );
+}
+
+#if 0
+
+void ICQContact::slotReadAwayMessage()
+{
+ kdDebug(14153) << k_funcinfo << "account='" << account()->accountId() <<
+ "', contact='" << displayName() << "'" << endl;
+
+ if (!awayMessageDialog)
+ {
+ awayMessageDialog = new ICQReadAway(this, 0L, "awayMessageDialog");
+ if(!awayMessageDialog)
+ return;
+ QObject::connect(awayMessageDialog, SIGNAL(closing()), this, SLOT(slotCloseAwayMessageDialog()));
+ awayMessageDialog->show();
+ }
+ else
+ {
+ awayMessageDialog->raise();
+ }
+}
+
+
+void ICQContact::slotCloseAwayMessageDialog()
+{
+ awayMessageDialog->delayedDestruct();
+ awayMessageDialog = 0L;
+}
+
+
+const QString ICQContact::awayMessage()
+{
+ kdDebug(14150) << k_funcinfo << property(mProtocol->awayMessage).value().toString() << endl;
+ return property(mProtocol->awayMessage).value().toString();
+}
+
+
+void ICQContact::setAwayMessage(const QString &message)
+{
+ /*kdDebug(14150) << k_funcinfo <<
+ "Called for '" << displayName() << "', away msg='" << message << "'" << endl;*/
+ setProperty(mProtocol->awayMessage, message);
+ emit awayMessageChanged();
+}
+
+
+void ICQContact::slotUpdGeneralInfo(const int seq, const ICQGeneralUserInfo &inf)
+{
+ // compare reply's sequence with the one we sent with our last request
+ if(seq != userinfoRequestSequence)
+ return;
+ generalInfo = inf;
+
+ if(!generalInfo.firstName.isEmpty())
+ setProperty(mProtocol->firstName, generalInfo.firstName);
+ else
+ removeProperty(mProtocol->firstName);
+
+ if(!generalInfo.lastName.isEmpty())
+ setProperty(mProtocol->lastName, generalInfo.lastName);
+ else
+ removeProperty(mProtocol->lastName);
+
+ if(!generalInfo.eMail.isEmpty())
+ setProperty(mProtocol->emailAddress, generalInfo.eMail);
+ else
+ removeProperty(mProtocol->emailAddress);
+ /*
+ if(!generalInfo.phoneNumber.isEmpty())
+ setProperty("privPhoneNum", generalInfo.phoneNumber);
+ else
+ removeProperty("privPhoneNum");
+
+ if(!generalInfo.faxNumber.isEmpty())
+ setProperty("privFaxNum", generalInfo.faxNumber);
+ else
+ removeProperty("privFaxNum");
+
+ if(!generalInfo.cellularNumber.isEmpty())
+ setProperty("privMobileNum", generalInfo.cellularNumber);
+ else
+ removeProperty("privMobileNum");
+ */
+
+ if(contactName() == displayName() && !generalInfo.nickName.isEmpty())
+ {
+ kdDebug(14153) << k_funcinfo << "setting new displayname for former UIN-only Contact" << endl;
+ setDisplayName(generalInfo.nickName);
+ }
+
+ incUserInfoCounter();
+}
+
+
+void ICQContact::slotSnacFailed(WORD snacID)
+{
+ if (userinfoRequestSequence != 0)
+ kdDebug(14153) << k_funcinfo << "snacID = " << snacID << " seq = " << userinfoRequestSequence << endl;
+
+ //TODO: ugly interaction between snacID and request sequence, see OscarSocket::sendCLI_TOICQSRV
+ if (snacID == (0x0000 << 16) | userinfoRequestSequence)
+ {
+ userinfoRequestSequence = 0;
+ emit userInfoRequestFailed();
+ }
+}
+
+void ICQContact::slotIgnore()
+{
+ kdDebug(14150) << k_funcinfo <<
+ "Called; ignore = " << actionIgnore->isChecked() << endl;
+ setIgnore(actionIgnore->isChecked(), true);
+}
+
+void ICQContact::slotVisibleTo()
+{
+ kdDebug(14150) << k_funcinfo <<
+ "Called; visible = " << actionVisibleTo->isChecked() << endl;
+ setVisibleTo(actionVisibleTo->isChecked(), true);
+}
+#endif
+#include "icqcontact.moc"
+//kate: indent-mode csands; tab-width 4; replace-tabs off; space-indent off;
diff --git a/kopete/protocols/oscar/icq/icqcontact.h b/kopete/protocols/oscar/icq/icqcontact.h
new file mode 100644
index 00000000..41084e63
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqcontact.h
@@ -0,0 +1,155 @@
+/*
+ icqcontact.h - ICQ Contact
+
+ Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2003 by Olivier Goffart
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef ICQCONTACT_H
+#define ICQCONTACT_H
+
+#include "oscarcontact.h"
+#include "userdetails.h"
+
+class OscarEncodingSelectionDialog;
+class KAction;
+class KToggleAction;
+namespace Kopete { class ChatSession; }
+namespace Kopete { class OnlineStatus; }
+class ICQProtocol;
+class ICQAccount;
+class OscarAccount;
+class ICQUserInfo; // user info dialog
+class ICQReadAway;
+
+class ICQGeneralUserInfo;
+class ICQWorkUserInfo;
+class ICQUserInfoWidget;
+class ICQInterestInfoWidget;
+
+/**
+ * Contact for ICQ over Oscar protocol
+ * @author Stefan Gehn
+ * @author Richard Smith
+ * @author Matt Rogers
+ */
+class ICQContact : public OscarContact
+{
+Q_OBJECT
+
+public:
+
+ /** Normal ICQ constructor */
+ ICQContact( ICQAccount *account, const QString &name, Kopete::MetaContact *parent,
+ const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() );
+ virtual ~ICQContact();
+
+ /**
+ * Returns a set of custom menu items for
+ * the context menu
+ */
+ virtual QPtrList<KAction> *customContextMenuActions();
+
+ /** Return whether or not this contact is reachable. */
+ virtual bool isReachable();
+
+
+ //virtual const QString awayMessage();
+ //virtual void setAwayMessage(const QString &message);
+
+public slots:
+ virtual void slotUserInfo();
+ virtual void updateSSIItem();
+ void userInfoUpdated( const QString& contact, const UserDetails& details );
+
+ void userOnline( const QString& userId );
+ void userOffline( const QString& userID );
+ void loggedIn();
+
+ void requestShortInfo();
+
+signals:
+ void haveBasicInfo( const ICQGeneralUserInfo& );
+ void haveWorkInfo( const ICQWorkUserInfo& );
+ void haveEmailInfo( const ICQEmailInfo& );
+ void haveMoreInfo( const ICQMoreUserInfo& );
+ void haveInterestInfo( const ICQInterestInfo& );
+
+private:
+ bool cachedBuddyIcon( QByteArray hash );
+ bool m_buddyIconDirty;
+
+ bool m_requestingNickname;
+ ICQProtocol *mProtocol;
+ ICQUserInfoWidget* m_infoWidget;
+ /*
+ ICQReadAway *awayMessageDialog;
+ KAction *actionReadAwayMessage;
+ */
+ KAction *actionRequestAuth;
+ KAction *actionSendAuth;
+ KAction *m_selectEncoding;
+
+ KToggleAction *m_actionIgnore;
+ KToggleAction *m_actionVisibleTo;
+ KToggleAction *m_actionInvisibleTo;
+
+ /*
+ bool mInvisible;
+ */
+
+ OscarEncodingSelectionDialog* m_oesd;
+
+protected slots:
+ virtual void slotSendMsg(Kopete::Message& message, Kopete::ChatSession *);
+ virtual void updateFeatures();
+
+private slots:
+ /** Request authorization from this contact */
+ void slotRequestAuth();
+
+ /** Authorize this contact */
+ void slotSendAuth();
+
+ void slotAuthReplyDialogOkClicked();
+
+ /** We have received an auth request */
+ void slotGotAuthRequest( const QString& contact, const QString& reason );
+
+ /** We have received an auth reply */
+ void slotGotAuthReply( const QString& contact, const QString& reason, bool granted );
+
+ void closeUserInfoDialog();
+
+ void receivedLongInfo( const QString& contact );
+ void receivedShortInfo( const QString& contact );
+
+ void changeContactEncoding();
+ void changeEncodingDialogClosed( int );
+
+ void requestBuddyIcon();
+ void haveIcon( const QString&, QByteArray );
+ void receivedStatusMessage( const QString &contact, const QString &message );
+ void receivedStatusMessage( const Oscar::Message &message );
+
+//void slotCloseAwayMessageDialog();
+ //void slotReadAwayMessage();
+
+ void slotIgnore();
+ void slotVisibleTo();
+ void slotInvisibleTo();
+};
+
+#endif
+//kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/icq/icqpresence.cpp b/kopete/protocols/oscar/icq/icqpresence.cpp
new file mode 100644
index 00000000..ab6bb670
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqpresence.cpp
@@ -0,0 +1,294 @@
+/*
+ icqpresence.cpp - ICQ online status and presence management
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <utility>
+#include <vector>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+
+#include <kopeteonlinestatus.h>
+#include <kopeteonlinestatusmanager.h>
+
+#include "icqprotocol.h"
+
+#include "icqpresence.h"
+
+namespace ICQ
+{
+
+//BEGIN struct PresenceTypeData
+
+struct PresenceTypeData
+{
+ Presence::Type type;
+ Kopete::OnlineStatus::StatusType onlineStatusType;
+ unsigned long setFlag;
+ unsigned long getFlag;
+ QString caption;
+ QString visibleName;
+ QString invisibleName;
+ const char *visibleIcon;
+ const char *invisibleIcon;
+ unsigned int categories;
+ unsigned int options;
+
+ static const PresenceTypeData *all();
+ static const PresenceTypeData &forType( Presence::Type type );
+ static const PresenceTypeData &forStatus( unsigned long status );
+ static const PresenceTypeData &forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType );
+};
+
+const PresenceTypeData *PresenceTypeData::all()
+{
+ using namespace Kopete;
+ using namespace ICQ::StatusCode;
+ /**
+ * The order here is important - this is the order the IS_XXX flags will be checked for in.
+ * That, in particular, means that NA, Occupied and DND must appear before Away, and that
+ * DND must appear before Occupied. Offline (testing all bits) must go first, and Online
+ * (testing no bits - will always match a status) must go last.
+ *
+ * Free For Chat is currently listed after Away, since if someone is Away + Free For Chat we
+ * want to show them as Away more than we want to show them FFC.
+ */
+ static const PresenceTypeData data[] =
+ {
+ { Presence::Offline, OnlineStatus::Offline, OFFLINE, OFFLINE, i18n( "O&ffline" ), i18n("Offline"), i18n("Offline"), 0, "contact_invisible_overlay", Kopete::OnlineStatusManager::Offline, 0 },
+ { Presence::DoNotDisturb, OnlineStatus::Away, SET_DND, IS_DND, i18n( "&Do Not Disturb" ), i18n("Do Not Disturb"), i18n("Do Not Disturb (Invisible)"), "contact_busy_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage },
+ { Presence::Occupied, OnlineStatus::Away, SET_OCC, IS_OCC, i18n( "O&ccupied" ), i18n("Occupied"), i18n("Occupied (Invisible)"), "contact_busy_overlay", "contact_invisible_overlay", 0, Kopete::OnlineStatusManager::HasAwayMessage },
+ { Presence::NotAvailable, OnlineStatus::Away, SET_NA, IS_NA, i18n( "Not A&vailable" ), i18n("Not Available"), i18n("Not Available (Invisible)"), "contact_xa_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::ExtendedAway, Kopete::OnlineStatusManager::HasAwayMessage },
+ { Presence::Away, OnlineStatus::Away, SET_AWAY, IS_AWAY, i18n( "&Away" ), i18n("Away"), i18n("Away (Invisible)"), "contact_away_overlay", "contact_invisible_overlay", Kopete::OnlineStatusManager::Away, Kopete::OnlineStatusManager::HasAwayMessage },
+ { Presence::FreeForChat, OnlineStatus::Online, SET_FFC, IS_FFC, i18n( "&Free for Chat" ), i18n("Free For Chat"), i18n("Free For Chat (Invisible)"), "icq_ffc", "contact_invisible_overlay", Kopete::OnlineStatusManager::FreeForChat, 0 },
+ { Presence::Online, OnlineStatus::Online, ONLINE, ONLINE, i18n( "O&nline" ), i18n("Online"), i18n("Online (Invisible)"), 0, "contact_invisible_overlay", Kopete::OnlineStatusManager::Online, 0 }
+ };
+ return data;
+}
+
+const PresenceTypeData &PresenceTypeData::forType( Presence::Type type )
+{
+ const PresenceTypeData *array = all();
+ for ( uint n = 0; n < Presence::TypeCount; ++n )
+ if ( array[n].type == type )
+ return array[n];
+ kdWarning(14153) << k_funcinfo << "type " << (int)type << " not found! Returning Offline" << endl;
+ return array[0];
+}
+
+const PresenceTypeData &PresenceTypeData::forStatus( unsigned long status )
+{
+ const PresenceTypeData *array = all();
+ for ( uint n = 0; n < Presence::TypeCount; ++n )
+ {
+ //kdDebug(14153) << k_funcinfo << "array[n].getFlag is " << array[n].getFlag << ", status is " << status << ", & is " << (array[n].getFlag & status) << endl;
+ if ( (array[n].getFlag & status) == array[n].getFlag )
+ return array[n];
+ }
+ kdWarning(14153) << k_funcinfo << "status " << (int)status << " not found! Returning Offline. This should not happen." << endl;
+ return array[0];
+}
+
+const PresenceTypeData &PresenceTypeData::forOnlineStatusType( const Kopete::OnlineStatus::StatusType statusType )
+{
+ const PresenceTypeData *array = all();
+ for ( int n = Presence::TypeCount - 1; n >= 0; --n )
+ {
+ if ( array[n].onlineStatusType == statusType )
+ return array[n];
+ }
+ kdWarning(14153) << k_funcinfo << "online status " << (int)statusType << " not found! Returning Offline. This should not happen." << endl;
+ return array[0];
+}
+
+//END struct PresenceTypeData
+
+//BEGIN class OnlineStatusManager
+
+class OnlineStatusManager::Private
+{
+public:
+ typedef std::vector<Kopete::OnlineStatus> StatusList;
+
+ // connecting and unknown should have the same internal status as offline, so converting to a Presence gives an Offline one
+ Private()
+ : connecting( Kopete::OnlineStatus::Connecting, 99, ICQProtocol::protocol(),
+ 99, "icq_connecting", i18n("Connecting...") )
+ , unknown( Kopete::OnlineStatus::Unknown, 0, ICQProtocol::protocol(),
+ Presence::Offline, "status_unknown", i18n("Unknown") )
+ , waitingForAuth( Kopete::OnlineStatus::Unknown, 1, ICQProtocol::protocol(),
+ Presence::Offline, "button_cancel", i18n("Waiting for Authorization") )
+ , invisible( Kopete::OnlineStatus::Invisible, 2, ICQProtocol::protocol(),
+ Presence::Offline, QString::null, QString::null,
+ QString::null, Kopete::OnlineStatusManager::Invisible,
+ Kopete::OnlineStatusManager::HideFromMenu )
+
+ {
+ createStatusList( false, 0, visibleStatusList );
+ createStatusList( true, Presence::TypeCount, invisibleStatusList );
+ }
+ void createStatusList( bool invisible, const uint invisibleOffset, StatusList &statusList )
+ {
+ //weight 0, 1 and 2 are used by KOS unknown, waitingForAuth and invisible
+ const uint firstUsableWeight = 3;
+ statusList.reserve( Presence::TypeCount );
+ for ( uint n = 0; n < Presence::TypeCount; ++n )
+ {
+ const PresenceTypeData &data = PresenceTypeData::forType( static_cast<Presence::Type>(n) );
+ const uint weight = n + firstUsableWeight;
+ const uint internalStatus = n + invisibleOffset;
+ QStringList overlayIcons( data.visibleIcon );
+ QString description( data.visibleName );
+ Kopete::OnlineStatus status;
+ if ( invisible )
+ {
+ overlayIcons << data.invisibleIcon;
+ description = data.invisibleName;
+ //don't add invisible KOS to account's context menu
+ status = Kopete::OnlineStatus( data.onlineStatusType, weight,
+ ICQProtocol::protocol(), internalStatus,
+ overlayIcons, description );
+ }
+ else
+ {
+ //add visible KOS
+ status = Kopete::OnlineStatus( data.onlineStatusType, weight,
+ ICQProtocol::protocol(), internalStatus,
+ overlayIcons, description,
+ data.caption, data.categories, data.options );
+ }
+ statusList.push_back( status );
+ }
+ }
+
+ StatusList visibleStatusList, invisibleStatusList;
+ Kopete::OnlineStatus connecting;
+ Kopete::OnlineStatus unknown;
+ Kopete::OnlineStatus waitingForAuth;
+ Kopete::OnlineStatus invisible;
+};
+
+OnlineStatusManager::OnlineStatusManager()
+ : d( new Private )
+{
+}
+
+OnlineStatusManager::~OnlineStatusManager()
+{
+ delete d;
+}
+
+Presence OnlineStatusManager::presenceOf( uint internalStatus )
+{
+ if ( internalStatus < Presence::TypeCount )
+ {
+ return Presence( static_cast<Presence::Type>( internalStatus ), Presence::Visible );
+ }
+ else if ( internalStatus < 2 * Presence::TypeCount )
+ {
+ return Presence( static_cast<Presence::Type>( internalStatus - Presence::TypeCount ), Presence::Invisible );
+ }
+ else
+ {
+ kdWarning(14153) << k_funcinfo << "No presence exists for internal status " << internalStatus << "! Returning Offline" << endl;
+ return Presence( Presence::Offline, Presence::Visible );
+ }
+}
+
+Kopete::OnlineStatus OnlineStatusManager::onlineStatusOf( const Presence &presence )
+{
+ if ( presence.visibility() == Presence::Visible )
+ return d->visibleStatusList[ presence.type() ];
+ else
+ return d->invisibleStatusList[ presence.type() ];
+}
+
+Kopete::OnlineStatus OnlineStatusManager::connectingStatus()
+{
+ return d->connecting;
+}
+
+Kopete::OnlineStatus OnlineStatusManager::unknownStatus()
+{
+ return d->unknown;
+}
+
+Kopete::OnlineStatus OnlineStatusManager::waitingForAuth()
+{
+ return d->waitingForAuth;
+}
+
+//END class OnlineStatusManager
+
+//BEGIN class Presence
+
+Presence Presence::fromOnlineStatus( const Kopete::OnlineStatus &status )
+{
+ if ( status.protocol() == ICQProtocol::protocol() )
+ {
+ OnlineStatusManager *store = ICQProtocol::protocol()->statusManager();
+ return store->presenceOf( status.internalStatus() );
+ }
+ else
+ {
+ //status is a libkopete builtin status object
+ //don't even think about converting it to ICQ::Presence using presenceOf!
+ return Presence( PresenceTypeData::forOnlineStatusType( status.status() ).type,
+ Presence::Visible );
+ }
+}
+
+Kopete::OnlineStatus Presence::toOnlineStatus() const
+{
+ OnlineStatusManager *store = ICQProtocol::protocol()->statusManager();
+ return store->onlineStatusOf( *this );
+}
+
+
+unsigned long Presence::toOscarStatus() const
+{
+ unsigned long basicStatus = basicOscarStatus();
+ if ( _visibility == Invisible )
+ basicStatus |= StatusCode::INVISIBLE;
+ return basicStatus;
+}
+
+Presence Presence::fromOscarStatus( unsigned long code )
+{
+ Type type = typeFromOscarStatus( code & ~StatusCode::INVISIBLE );
+ bool invisible = (code & StatusCode::INVISIBLE) == StatusCode::INVISIBLE;
+ return Presence( type, invisible ? Invisible : Visible );
+}
+
+
+unsigned long Presence::basicOscarStatus() const
+{
+ const PresenceTypeData &data = PresenceTypeData::forType( _type );
+ return data.setFlag;
+}
+
+Presence::Type Presence::typeFromOscarStatus( unsigned long status )
+{
+ const PresenceTypeData &data = PresenceTypeData::forStatus( status );
+ return data.type;
+}
+
+//END class Presence
+
+} // end namespace ICQ
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: indent-mode: csands; space-indent off; tab-width 4;
diff --git a/kopete/protocols/oscar/icq/icqpresence.h b/kopete/protocols/oscar/icq/icqpresence.h
new file mode 100644
index 00000000..d7ef9ed2
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqpresence.h
@@ -0,0 +1,177 @@
+/*
+ icqpresence.h - ICQ online status and presence management
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef ICQCOMMON_H
+#define ICQCOMMON_H
+
+#include <kdebug.h>
+
+namespace Kopete { class OnlineStatus; }
+
+namespace ICQ
+{
+
+class Presence;
+
+/**
+ * @brief This namespace contains status flags used in OSCAR's on-the-wire format.
+ *
+ * The IS_XXX values are bits representing actual status flags. However, the flags
+ * are just that -- flags. ICQ statuses are represented by a combination of these
+ * flags rather than just one value. This seems to be for backwards compatibility
+ * reasons -- this way you can add a new status and existing clients should still
+ * work correctly.
+ *
+ * So, when changing status you need to specify not only what status it is, but
+ * also all other status flags that are appropriate. The SET_XXX flags do just that;
+ * SET_DND for instance sets the DND, Occupied and Away bits.
+ */
+namespace StatusCode
+{
+ enum
+ {
+ OFFLINE = 0xFFFFFFFF,
+ ONLINE = 0x00000000,
+ INVISIBLE = 0x00000100,
+
+ IS_DND = 0x00000002, ///< Do Not Disturb
+ IS_OCC = 0x00000010, ///< Occupied
+ IS_NA = 0x00000004, ///< Not Available
+ IS_AWAY = 0x00000001, ///< Away
+ IS_FFC = 0x00000020, ///< Free For Chat
+
+ SET_DND = 0x00000013, //== DND + Occupied + Away
+ SET_OCC = 0x00000011, //== Occupied + Away
+ SET_NA = 0x00000005, //== NA + Away
+ SET_AWAY = 0x00000001,
+ SET_FFC = 0x00000020,
+
+ WEBAWARE = 0x00010000,
+ SHOWIP = 0x00020000
+ };
+} // end namespace StatusCode
+
+/**
+ * @brief A manager for ICQ's online statuses
+ *
+ * Looks after ICQ's numerous online statuses, and maps between them and Presence objects.
+ * A single instance of this class is held by the ICQProtocol object.
+ */
+class OnlineStatusManager
+{
+public:
+ OnlineStatusManager();
+ ~OnlineStatusManager();
+ ICQ::Presence presenceOf( uint internalStatus );
+ Kopete::OnlineStatus onlineStatusOf( const ICQ::Presence &presence );
+ Kopete::OnlineStatus connectingStatus();
+ Kopete::OnlineStatus unknownStatus();
+ Kopete::OnlineStatus waitingForAuth();
+
+private:
+ class Private;
+ Private *d;
+};
+
+/**
+ * @brief An ICQ online presence object
+ */
+class Presence
+{
+public:
+ /**
+ * Friendly types this status can be
+ */
+ enum Type { Offline, DoNotDisturb, Occupied, NotAvailable, Away, Online, FreeForChat };
+ enum { TypeCount = FreeForChat + 1 };
+
+ enum Visibility { Invisible, Visible };
+
+ Presence( Type type, Visibility vis ) : _type(type), _visibility(vis) {}
+
+ Type type() const { return _type; }
+ Visibility visibility() const { return _visibility; }
+
+ /**
+ * Generate a Presence object from an online status
+ */
+ static Presence fromOnlineStatus( const Kopete::OnlineStatus &status );
+
+ /**
+ * Convert this Presence object to an online status
+ */
+ Kopete::OnlineStatus toOnlineStatus() const;
+
+ /**
+ * Get the status code to pass to liboscar to set us to this Status.
+ * @note This is not the opposite of fromOnlineStatus(). The set and get codes don't match.
+ */
+ unsigned long toOscarStatus() const;
+
+ /**
+ * Get the status a contact is at based on liboscar's view of its status.
+ * @note This is not the opposite of toOnlineStatus().
+ */
+ static Presence fromOscarStatus( unsigned long code );
+
+ bool operator==( const Presence &other ) const { return other._type == _type && other._visibility == _visibility; }
+ bool operator!=( const Presence &other ) const { return !(*this == other); }
+
+private:
+ unsigned long basicOscarStatus() const;
+ static Type typeFromOscarStatus( unsigned long status );
+private:
+ Type _type;
+ Visibility _visibility;
+};
+
+}
+
+#if 0
+const unsigned int ICQ_PORT = 5190;
+
+
+const unsigned short ICQ_SEARCHSTATE_OFFLINE = 0;
+const unsigned short ICQ_SEARCHSTATE_ONLINE = 1;
+const unsigned short ICQ_SEARCHSTATE_DISABLED = 2;
+
+
+// Taken from libicq, not sure if we ever support these requests
+const unsigned char PHONEBOOK_SIGN[16] =
+{
+ 0x90, 0x7C, 0x21, 0x2C, 0x91, 0x4D, 0xD3, 0x11,
+ 0xAD, 0xEB, 0x00, 0x04, 0xAC, 0x96, 0xAA, 0xB2
+};
+
+const unsigned char PLUGINS_SIGN[16] =
+{
+ 0xF0, 0x02, 0xBF, 0x71, 0x43, 0x71, 0xD3, 0x11,
+ 0x8D, 0xD2, 0x00, 0x10, 0x4B, 0x06, 0x46, 0x2E
+};
+
+/*
+const unsigned char SHARED_FILES_SIGN[16] =
+{
+ 0xF0, 0x2D, 0x12, 0xD9, 0x30, 0x91, 0xD3, 0x11,
+ 0x8D, 0xD7, 0x00, 0x10, 0x4B, 0x06, 0x46, 0x2E
+};
+*/
+#endif
+
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: indent-mode: csands
diff --git a/kopete/protocols/oscar/icq/icqprotocol.cpp b/kopete/protocols/oscar/icq/icqprotocol.cpp
new file mode 100644
index 00000000..42616e32
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqprotocol.cpp
@@ -0,0 +1,820 @@
+/*
+ icqprotocol.cpp - ICQ Protocol Plugin
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <netinet/in.h> // for ntohl()
+
+#include <qcombobox.h>
+/*
+#include <qhostaddress.h>
+#include <qlistbox.h>
+#include <qspinbox.h>
+#include <qtextedit.h>
+
+#include <kdatewidget.h>*/
+#include <qvaluelist.h>
+#include <kdialogbase.h>
+/*
+#include <klineedit.h>
+#include <kurllabel.h>
+*/
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kmessagebox.h>
+
+#include "kopeteglobal.h"
+#include "kopeteuiglobal.h"
+#include "accountselector.h"
+#include "kopeteaccountmanager.h"
+
+#include "oscartypeclasses.h"
+
+#include "icqaccount.h"
+#include "icqcontact.h"
+#include "icqaddcontactpage.h"
+#include "icqeditaccountwidget.h"
+//#include "icquserinfowidget.h"
+
+
+#include "icqprotocol.h"
+
+typedef KGenericFactory<ICQProtocol> ICQProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_icq, ICQProtocolFactory( "kopete_icq" ) )
+
+//BEGIN class ICQProtocolHandler
+
+ICQProtocolHandler::ICQProtocolHandler() : Kopete::MimeTypeHandler(false)
+{
+ registerAsMimeHandler(QString::fromLatin1("application/x-icq"));
+}
+
+void ICQProtocolHandler::handleURL(const QString &mimeType, const KURL & url) const
+{
+ if (mimeType != "application/x-icq")
+ return;
+
+ /**
+ * File Format usually looks like
+ *
+ * [ICQ User]
+ * UIN=123456789
+ * Email=
+ * NickName=
+ * FirstName=
+ * LastName=
+ */
+
+ KSimpleConfig file(url.path(), true);
+
+ if (file.hasGroup("ICQ User"))
+ file.setGroup("ICQ User");
+ else if (file.hasGroup("ICQ Message User"))
+ file.setGroup("ICQ Message User");
+ else
+ return;
+
+ ICQProtocol *proto = ICQProtocol::protocol();
+
+ QString uin = file.readEntry("UIN");
+ if (uin.isEmpty())
+ return;
+
+ QString nick = file.readEntry("NickName");
+ QString first = file.readEntry("FirstName");
+ QString last = file.readEntry("LastName");
+ QString email = file.readEntry("Email");
+
+ Kopete::Account *account = 0;
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(proto);
+ // do not show chooser if we only have one account to "choose" from
+ if (accounts.count() == 1)
+ {
+ QDictIterator<Kopete::Account> it(accounts);
+ account = it.current();
+ }
+ else
+ {
+ KDialogBase *chooser = new KDialogBase(0, "chooser", true,
+ i18n("Choose Account"), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok, false);
+ AccountSelector *accSelector = new AccountSelector(proto, chooser,
+ "accSelector");
+ chooser->setMainWidget(accSelector);
+
+ int ret = chooser->exec();
+ account = accSelector->selectedItem();
+
+ delete chooser;
+ if (ret == QDialog::Rejected || account == 0)
+ {
+ kdDebug(14153) << k_funcinfo << "Cancelled" << endl;
+ return;
+ }
+ }
+
+ if (!account->isConnected())
+ {
+ kdDebug(14153) << k_funcinfo << "Can't add contact, we are offline!" << endl;
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n("You must be online to add a contact."), i18n("ICQ") );
+ return;
+ }
+
+ QString nickuin = nick.isEmpty() ?
+ i18n("'%1'").arg(uin) :
+ i18n("'%1' (%2)").arg(nick, uin);
+
+ if (KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(),
+ i18n("Do you want to add %1 to your contact list?").arg(nickuin), QString::null, i18n("Add"), i18n("Do Not Add"))
+ != KMessageBox::Yes)
+ {
+ kdDebug(14153) << k_funcinfo << "Cancelled" << endl;
+ return;
+ }
+
+ kdDebug(14153) << k_funcinfo <<
+ "Adding Contact; uin = " << uin << ", nick = '" << nick <<
+ "', firstname = '" << first << "', lastname = '" << last <<"'" << endl;
+ if (account->addContact(uin, nick, 0L, Kopete::Account::Temporary))
+ {
+ Kopete::Contact *contact = account->contacts()[uin];
+ if (!first.isEmpty())
+ contact->setProperty(Kopete::Global::Properties::self()->firstName(), first);
+ if (!last.isEmpty())
+ contact->setProperty(Kopete::Global::Properties::self()->lastName(), last);
+ if (!email.isEmpty())
+ contact->setProperty(Kopete::Global::Properties::self()->emailAddress(), email);
+ }
+}
+
+//END class ICQProtocolHandler
+
+//BEGIN class ICQProtocol
+
+ICQProtocol* ICQProtocol::protocolStatic_ = 0L;
+
+ICQProtocol::ICQProtocol(QObject *parent, const char *name, const QStringList&)
+: Kopete::Protocol( ICQProtocolFactory::instance(), parent, name ),
+ firstName(Kopete::Global::Properties::self()->firstName()),
+ lastName(Kopete::Global::Properties::self()->lastName()),
+ awayMessage(Kopete::Global::Properties::self()->awayMessage()),
+ emailAddress(Kopete::Global::Properties::self()->emailAddress()),
+ ipAddress("ipAddress", i18n("IP Address") ),
+ clientFeatures("clientFeatures", i18n("Client Features"), 0, false),
+ buddyIconHash("iconHash", i18n("Buddy Icon MD5 Hash"), QString::null, true, false, true),
+ contactEncoding( "contactEncoding", i18n( "Contact Encoding" ), QString::null, true, false, true )
+
+{
+ if (protocolStatic_)
+ kdWarning(14153) << k_funcinfo << "ICQ plugin already initialized" << endl;
+ else
+ protocolStatic_ = this;
+
+ // must be done after protocolStatic_ is set...
+ statusManager_ = new ICQ::OnlineStatusManager;
+
+ addAddressBookField("messaging/icq", Kopete::Plugin::MakeIndexField);
+
+ initGenders();
+ initLang();
+ initCountries();
+ initEncodings();
+ initMaritals();
+ initInterests();
+}
+
+ICQProtocol::~ICQProtocol()
+{
+ delete statusManager_;
+ protocolStatic_ =0L;
+}
+
+void ICQProtocol::initGenders()
+{
+ mGenders.insert(0, ""); // unspecified
+ mGenders.insert(1, i18n("Female"));
+ mGenders.insert(2, i18n("Male"));
+}
+
+void ICQProtocol::initCountries()
+{
+ mCountries.insert(0, ""); // unspecified
+ KLocale *kl = KGlobal::locale(); //KLocale(QString::fromLatin1("kopete"));
+
+ mCountries.insert(93, kl->twoAlphaToCountryName("af"));
+ mCountries.insert(355, kl->twoAlphaToCountryName("al"));
+ mCountries.insert(213, kl->twoAlphaToCountryName("dz"));
+ mCountries.insert(684, kl->twoAlphaToCountryName("as"));
+ mCountries.insert(376, kl->twoAlphaToCountryName("ad"));
+ mCountries.insert(244, kl->twoAlphaToCountryName("ao"));
+ mCountries.insert(101, kl->twoAlphaToCountryName("ai"));
+ mCountries.insert(102, kl->twoAlphaToCountryName("ag"));
+ mCountries.insert(54, kl->twoAlphaToCountryName("ar"));
+ mCountries.insert(374, kl->twoAlphaToCountryName("am"));
+ mCountries.insert(297, kl->twoAlphaToCountryName("aw"));
+ mCountries.insert(247, i18n("Ascension Island"));
+ mCountries.insert(61, kl->twoAlphaToCountryName("au"));
+ mCountries.insert(6721, i18n("Australian Antarctic Territory"));
+ mCountries.insert(43, kl->twoAlphaToCountryName("at"));
+ mCountries.insert(994, kl->twoAlphaToCountryName("az"));
+ mCountries.insert(103, kl->twoAlphaToCountryName("bs"));
+ mCountries.insert(973, kl->twoAlphaToCountryName("bh"));
+ mCountries.insert(880, kl->twoAlphaToCountryName("bd"));
+ mCountries.insert(104, kl->twoAlphaToCountryName("bb"));
+ mCountries.insert(120, i18n("Barbuda"));
+ mCountries.insert(375, kl->twoAlphaToCountryName("by"));
+ mCountries.insert(32, kl->twoAlphaToCountryName("be"));
+ mCountries.insert(501, kl->twoAlphaToCountryName("bz"));
+ mCountries.insert(229, kl->twoAlphaToCountryName("bj"));
+ mCountries.insert(105, kl->twoAlphaToCountryName("bm"));
+ mCountries.insert(975, kl->twoAlphaToCountryName("bt"));
+ mCountries.insert(591, kl->twoAlphaToCountryName("bo"));
+ mCountries.insert(387, kl->twoAlphaToCountryName("ba"));
+ mCountries.insert(267, kl->twoAlphaToCountryName("bw"));
+ mCountries.insert(55, kl->twoAlphaToCountryName("br"));
+ mCountries.insert(106, i18n("British Virgin Islands"));
+ mCountries.insert(673, kl->twoAlphaToCountryName("bn"));
+ mCountries.insert(359, kl->twoAlphaToCountryName("bg"));
+ mCountries.insert(226, kl->twoAlphaToCountryName("bf"));
+ mCountries.insert(257, kl->twoAlphaToCountryName("bi"));
+ mCountries.insert(855, kl->twoAlphaToCountryName("kh"));
+ mCountries.insert(237, kl->twoAlphaToCountryName("cm"));
+ mCountries.insert(107, kl->twoAlphaToCountryName("ca"));
+ mCountries.insert(238, kl->twoAlphaToCountryName("cv"));
+ mCountries.insert(108, kl->twoAlphaToCountryName("ky"));
+ mCountries.insert(236, kl->twoAlphaToCountryName("cf"));
+ mCountries.insert(235, kl->twoAlphaToCountryName("td"));
+ mCountries.insert(56, kl->twoAlphaToCountryName("cl"));
+ mCountries.insert(86, kl->twoAlphaToCountryName("cn"));
+ mCountries.insert(672, kl->twoAlphaToCountryName("cx"));
+ mCountries.insert(6101, kl->twoAlphaToCountryName("c"));
+ mCountries.insert(57, kl->twoAlphaToCountryName("co"));
+ mCountries.insert(2691, kl->twoAlphaToCountryName("km"));
+ mCountries.insert(242, kl->twoAlphaToCountryName("cg"));
+ mCountries.insert(682, kl->twoAlphaToCountryName("ck"));
+ mCountries.insert(506, kl->twoAlphaToCountryName("cr"));
+ mCountries.insert(385, kl->twoAlphaToCountryName("hr"));
+ mCountries.insert(53, kl->twoAlphaToCountryName("cu"));
+ mCountries.insert(357, kl->twoAlphaToCountryName("cy"));
+ mCountries.insert(42, kl->twoAlphaToCountryName("cz"));
+ mCountries.insert(45, kl->twoAlphaToCountryName("dk"));
+ mCountries.insert(246, i18n("Diego Garcia"));
+ mCountries.insert(253, kl->twoAlphaToCountryName("dj"));
+ mCountries.insert(109, kl->twoAlphaToCountryName("dm"));
+ mCountries.insert(110, kl->twoAlphaToCountryName("do"));
+ mCountries.insert(593, kl->twoAlphaToCountryName("ec"));
+ mCountries.insert(20, kl->twoAlphaToCountryName("eg"));
+ mCountries.insert(503, kl->twoAlphaToCountryName("sv"));
+ mCountries.insert(240, kl->twoAlphaToCountryName("gq"));
+ mCountries.insert(291, kl->twoAlphaToCountryName("er"));
+ mCountries.insert(372, kl->twoAlphaToCountryName("ee"));
+ mCountries.insert(251, kl->twoAlphaToCountryName("et"));
+ mCountries.insert(298, kl->twoAlphaToCountryName("fo"));
+ mCountries.insert(500, kl->twoAlphaToCountryName("fk"));
+ mCountries.insert(679, kl->twoAlphaToCountryName("fj"));
+ mCountries.insert(358, kl->twoAlphaToCountryName("fi"));
+ mCountries.insert(33, kl->twoAlphaToCountryName("fr"));
+ mCountries.insert(5901, i18n("French Antilles"));
+ mCountries.insert(594, kl->twoAlphaToCountryName("gf"));
+ mCountries.insert(689, kl->twoAlphaToCountryName("pf"));
+ mCountries.insert(241, kl->twoAlphaToCountryName("ga"));
+ mCountries.insert(220, kl->twoAlphaToCountryName("gm"));
+ mCountries.insert(995, kl->twoAlphaToCountryName("ge"));
+ mCountries.insert(49, kl->twoAlphaToCountryName("de"));
+ mCountries.insert(233, kl->twoAlphaToCountryName("gh"));
+ mCountries.insert(350, kl->twoAlphaToCountryName("gi"));
+ mCountries.insert(30, kl->twoAlphaToCountryName("gr"));
+ mCountries.insert(299, kl->twoAlphaToCountryName("gl"));
+ mCountries.insert(111, kl->twoAlphaToCountryName("gd"));
+ mCountries.insert(590, kl->twoAlphaToCountryName("gp"));
+ mCountries.insert(671, kl->twoAlphaToCountryName("gu"));
+ mCountries.insert(5399, i18n("Guantanamo Bay"));
+ mCountries.insert(502, kl->twoAlphaToCountryName("gt"));
+ mCountries.insert(224, kl->twoAlphaToCountryName("gn"));
+ mCountries.insert(245, kl->twoAlphaToCountryName("gw"));
+ mCountries.insert(592, kl->twoAlphaToCountryName("gy"));
+ mCountries.insert(509, kl->twoAlphaToCountryName("ht"));
+ mCountries.insert(504, kl->twoAlphaToCountryName("hn"));
+ mCountries.insert(852, kl->twoAlphaToCountryName("hk"));
+ mCountries.insert(36, kl->twoAlphaToCountryName("hu"));
+ mCountries.insert(871, i18n("INMARSAT (Atlantic-East)"));
+ mCountries.insert(874, i18n("INMARSAT (Atlantic-West)"));
+ mCountries.insert(873, i18n("INMARSAT (Indian)"));
+ mCountries.insert(872, i18n("INMARSAT (Pacific)"));
+ mCountries.insert(870, i18n("INMARSAT"));
+ mCountries.insert(354, kl->twoAlphaToCountryName("is"));
+ mCountries.insert(91, kl->twoAlphaToCountryName("in"));
+ mCountries.insert(62, kl->twoAlphaToCountryName("id"));
+ mCountries.insert(800, i18n("International Freephone Service"));
+ mCountries.insert(98, kl->twoAlphaToCountryName("ir"));
+ mCountries.insert(964, kl->twoAlphaToCountryName("iq"));
+ mCountries.insert(353, kl->twoAlphaToCountryName("ie"));
+ mCountries.insert(972, kl->twoAlphaToCountryName("il"));
+ mCountries.insert(39, kl->twoAlphaToCountryName("it"));
+ mCountries.insert(225, i18n("Ivory Coast"));
+ mCountries.insert(112, kl->twoAlphaToCountryName("jm"));
+ mCountries.insert(81, kl->twoAlphaToCountryName("jp"));
+ mCountries.insert(962, kl->twoAlphaToCountryName("jo"));
+ mCountries.insert(705, kl->twoAlphaToCountryName("kz"));
+ mCountries.insert(254, kl->twoAlphaToCountryName("ke"));
+ mCountries.insert(686, kl->twoAlphaToCountryName("ki"));
+ mCountries.insert(850, kl->twoAlphaToCountryName("kp"));
+ mCountries.insert(82, kl->twoAlphaToCountryName("kr"));
+ mCountries.insert(965, kl->twoAlphaToCountryName("kw"));
+ mCountries.insert(706, kl->twoAlphaToCountryName("kg"));
+ mCountries.insert(856, kl->twoAlphaToCountryName("la"));
+ mCountries.insert(371, kl->twoAlphaToCountryName("lv"));
+ mCountries.insert(961, kl->twoAlphaToCountryName("kb"));
+ mCountries.insert(266, kl->twoAlphaToCountryName("ls"));
+ mCountries.insert(231, kl->twoAlphaToCountryName("lr"));
+ mCountries.insert(218, kl->twoAlphaToCountryName("ly"));
+ mCountries.insert(4101, kl->twoAlphaToCountryName("li"));
+ mCountries.insert(370, kl->twoAlphaToCountryName("lt"));
+ mCountries.insert(352, kl->twoAlphaToCountryName("lu"));
+ mCountries.insert(853, kl->twoAlphaToCountryName("mo"));
+ mCountries.insert(261, kl->twoAlphaToCountryName("mg"));
+ mCountries.insert(265, kl->twoAlphaToCountryName("mw"));
+ mCountries.insert(60, kl->twoAlphaToCountryName("my"));
+ mCountries.insert(960, kl->twoAlphaToCountryName("mv"));
+ mCountries.insert(223, kl->twoAlphaToCountryName("ml"));
+ mCountries.insert(356, kl->twoAlphaToCountryName("mt"));
+ mCountries.insert(692, kl->twoAlphaToCountryName("mh"));
+ mCountries.insert(596, kl->twoAlphaToCountryName("mq"));
+ mCountries.insert(222, kl->twoAlphaToCountryName("mr"));
+ mCountries.insert(230, kl->twoAlphaToCountryName("mu"));
+ mCountries.insert(269, kl->twoAlphaToCountryName("yt"));
+ mCountries.insert(52, kl->twoAlphaToCountryName("mx"));
+ mCountries.insert(691, kl->twoAlphaToCountryName("fm"));
+ mCountries.insert(373, kl->twoAlphaToCountryName("md"));
+ mCountries.insert(377, kl->twoAlphaToCountryName("mc"));
+ mCountries.insert(976, kl->twoAlphaToCountryName("mn"));
+ mCountries.insert(113, kl->twoAlphaToCountryName("ms"));
+ mCountries.insert(212, kl->twoAlphaToCountryName("ma"));
+ mCountries.insert(258, kl->twoAlphaToCountryName("mz"));
+ mCountries.insert(95, kl->twoAlphaToCountryName("mm"));
+ mCountries.insert(264, kl->twoAlphaToCountryName("na"));
+ mCountries.insert(674, kl->twoAlphaToCountryName("nr"));
+ mCountries.insert(977, kl->twoAlphaToCountryName("np"));
+ mCountries.insert(599, kl->twoAlphaToCountryName("an"));
+ mCountries.insert(31, kl->twoAlphaToCountryName("nl"));
+ mCountries.insert(114, i18n("Nevis"));
+ mCountries.insert(687, kl->twoAlphaToCountryName("nc"));
+ mCountries.insert(64, kl->twoAlphaToCountryName("nz"));
+ mCountries.insert(505, kl->twoAlphaToCountryName("ni"));
+ mCountries.insert(227, kl->twoAlphaToCountryName("ne"));
+ mCountries.insert(234, kl->twoAlphaToCountryName("ng"));
+ mCountries.insert(683, kl->twoAlphaToCountryName("nu"));
+ mCountries.insert(6722, kl->twoAlphaToCountryName("nf"));
+ mCountries.insert(47, kl->twoAlphaToCountryName("no"));
+ mCountries.insert(968, kl->twoAlphaToCountryName("om"));
+ mCountries.insert(92, kl->twoAlphaToCountryName("pk"));
+ mCountries.insert(680, kl->twoAlphaToCountryName("pw"));
+ mCountries.insert(507, kl->twoAlphaToCountryName("pa"));
+ mCountries.insert(675, kl->twoAlphaToCountryName("pg"));
+ mCountries.insert(595, kl->twoAlphaToCountryName("py"));
+ mCountries.insert(51, kl->twoAlphaToCountryName("pe"));
+ mCountries.insert(63, kl->twoAlphaToCountryName("ph"));
+ mCountries.insert(48, kl->twoAlphaToCountryName("pl"));
+ mCountries.insert(351, kl->twoAlphaToCountryName("pt"));
+ mCountries.insert(121, kl->twoAlphaToCountryName("pr"));
+ mCountries.insert(974, kl->twoAlphaToCountryName("qa"));
+ mCountries.insert(389, kl->twoAlphaToCountryName("mk"));
+ mCountries.insert(262, i18n("Reunion Island"));
+ mCountries.insert(40, kl->twoAlphaToCountryName("ro"));
+ mCountries.insert(6701, i18n("Rota Island"));
+ mCountries.insert(7, kl->twoAlphaToCountryName("ru"));
+ mCountries.insert(250, kl->twoAlphaToCountryName("rw"));
+ mCountries.insert(122, kl->twoAlphaToCountryName("lc"));
+ mCountries.insert(670, i18n("Ivory Coast"));
+ mCountries.insert(378, kl->twoAlphaToCountryName("sm"));
+ mCountries.insert(239, kl->twoAlphaToCountryName("st"));
+ mCountries.insert(966, kl->twoAlphaToCountryName("sa"));
+ mCountries.insert(221, kl->twoAlphaToCountryName("sn"));
+ mCountries.insert(248, kl->twoAlphaToCountryName("sc"));
+ mCountries.insert(232, kl->twoAlphaToCountryName("sl"));
+ mCountries.insert(65, kl->twoAlphaToCountryName("sg"));
+ mCountries.insert(4201, kl->twoAlphaToCountryName("sk"));
+ mCountries.insert(386, kl->twoAlphaToCountryName("si"));
+ mCountries.insert(677, kl->twoAlphaToCountryName("sb"));
+ mCountries.insert(252, kl->twoAlphaToCountryName("so"));
+ mCountries.insert(27, kl->twoAlphaToCountryName("za"));
+ mCountries.insert(34, kl->twoAlphaToCountryName("es"));
+ mCountries.insert(94, kl->twoAlphaToCountryName("lk"));
+ mCountries.insert(290, kl->twoAlphaToCountryName("sh"));
+ mCountries.insert(115, kl->twoAlphaToCountryName("kn"));
+ mCountries.insert(508, kl->twoAlphaToCountryName("pm"));
+ mCountries.insert(116, kl->twoAlphaToCountryName("vc"));
+ mCountries.insert(249, kl->twoAlphaToCountryName("sd"));
+ mCountries.insert(597, kl->twoAlphaToCountryName("sr"));
+ mCountries.insert(268, kl->twoAlphaToCountryName("sz"));
+ mCountries.insert(46, kl->twoAlphaToCountryName("se"));
+ mCountries.insert(41, kl->twoAlphaToCountryName("ch"));
+ mCountries.insert(963, kl->twoAlphaToCountryName("sy"));
+ mCountries.insert(886, kl->twoAlphaToCountryName("tw"));
+ mCountries.insert(708, kl->twoAlphaToCountryName("tj"));
+ mCountries.insert(255, kl->twoAlphaToCountryName("tz"));
+ mCountries.insert(66, kl->twoAlphaToCountryName("th"));
+ mCountries.insert(6702, i18n("Tinian Island"));
+ mCountries.insert(228, kl->twoAlphaToCountryName("tg")); // Togo
+ mCountries.insert(690, kl->twoAlphaToCountryName("tk")); // Tokelau
+ mCountries.insert(676, kl->twoAlphaToCountryName("to")); // Tonga
+ mCountries.insert(117, kl->twoAlphaToCountryName("tt")); // Trinidad and Tobago
+ mCountries.insert(216, kl->twoAlphaToCountryName("tn")); // Tunisia
+ mCountries.insert(90, kl->twoAlphaToCountryName("tr"));
+ mCountries.insert(709, kl->twoAlphaToCountryName("tm"));
+ mCountries.insert(118, kl->twoAlphaToCountryName("tc")); // Turks and Caicos Island
+ mCountries.insert(688, kl->twoAlphaToCountryName("tv")); // Tuvalu
+ mCountries.insert(1, kl->twoAlphaToCountryName("us")); // United States of America
+ mCountries.insert(256, kl->twoAlphaToCountryName("ug")); // Uganda
+ mCountries.insert(380, kl->twoAlphaToCountryName("ua")); // Ukraine
+ mCountries.insert(971, kl->twoAlphaToCountryName("ae")); // United Arab Emirates
+ mCountries.insert(44, kl->twoAlphaToCountryName("gb")); // United Kingdom
+ mCountries.insert(123, kl->twoAlphaToCountryName("vi")); // United States Virgin Islands
+ mCountries.insert(598, kl->twoAlphaToCountryName("uy")); // Uruguay
+ mCountries.insert(711, kl->twoAlphaToCountryName("uz")); // Uzbekistan
+ mCountries.insert(678, kl->twoAlphaToCountryName("vu")); // Vanuatu
+ mCountries.insert(379, kl->twoAlphaToCountryName("va")); // Vatican City
+ mCountries.insert(58, kl->twoAlphaToCountryName("ve")); // Venezuela
+ mCountries.insert(84, kl->twoAlphaToCountryName("vn")); // Vietnam
+ mCountries.insert(681, kl->twoAlphaToCountryName("wf")); // Wallis and Futuna Islands
+ mCountries.insert(685, kl->twoAlphaToCountryName("eh"));
+ mCountries.insert(967, kl->twoAlphaToCountryName("ye"));
+ mCountries.insert(381, kl->twoAlphaToCountryName("yu"));
+ mCountries.insert(243, kl->twoAlphaToCountryName("zr"));
+ mCountries.insert(260, kl->twoAlphaToCountryName("zm"));
+ mCountries.insert(263, kl->twoAlphaToCountryName("zw"));
+}
+
+void ICQProtocol::initLang()
+{
+
+ KLocale *kl = KGlobal::locale(); //KLocale(QString::fromLatin1("kopete"));
+
+ mLanguages.insert(0 , "");
+ mLanguages.insert(1 , kl->twoAlphaToLanguageName("ar") /*i18n("Arabic")*/);
+ mLanguages.insert(2 , i18n("Bhojpuri"));
+ mLanguages.insert(3 , kl->twoAlphaToLanguageName("bg") /*i18n("Bulgarian")*/);
+ mLanguages.insert(4 , kl->twoAlphaToLanguageName("my") /*i18n("Burmese")*/);
+ mLanguages.insert(5 , i18n("Cantonese"));
+ mLanguages.insert(6 , kl->twoAlphaToLanguageName("ca") /*i18n("Catalan")*/);
+ mLanguages.insert(7 , kl->twoAlphaToLanguageName("zh") /*i18n("Chinese")*/);
+ mLanguages.insert(8 , kl->twoAlphaToLanguageName("hr") /*i18n("Croatian")*/);
+ mLanguages.insert(9 , kl->twoAlphaToLanguageName("cs") /*i18n("Czech")*/);
+ mLanguages.insert(10, kl->twoAlphaToLanguageName("da") /*i18n("Danish")*/);
+ mLanguages.insert(11, kl->twoAlphaToLanguageName("nl") /*i18n("Dutch")*/);
+ mLanguages.insert(12, kl->twoAlphaToLanguageName("en") /*i18n("English")*/);
+ mLanguages.insert(13, kl->twoAlphaToLanguageName("eo") /*i18n("Esperanto")*/);
+ mLanguages.insert(14, kl->twoAlphaToLanguageName("et") /*i18n("Estonian")*/);
+ mLanguages.insert(15, i18n("Farsi"));
+ mLanguages.insert(16, kl->twoAlphaToLanguageName("fi") /*i18n("Finnish")*/);
+ mLanguages.insert(17, kl->twoAlphaToLanguageName("fr") /*i18n("French")*/);
+ mLanguages.insert(18, kl->twoAlphaToLanguageName("gd") /*i18n("Gaelic")*/);
+ mLanguages.insert(19, kl->twoAlphaToLanguageName("de") /*i18n("German")*/);
+ mLanguages.insert(20, kl->twoAlphaToLanguageName("el") /*i18n("Greek")*/);
+ mLanguages.insert(21, kl->twoAlphaToLanguageName("he") /*i18n("Hebrew")*/);
+ mLanguages.insert(22, kl->twoAlphaToLanguageName("hi") /*i18n("Hindi")*/);
+ mLanguages.insert(23, kl->twoAlphaToLanguageName("hu") /*i18n("Hungarian")*/);
+ mLanguages.insert(24, kl->twoAlphaToLanguageName("is") /*i18n("Icelandic")*/);
+ mLanguages.insert(25, kl->twoAlphaToLanguageName("id") /*i18n("Indonesian")*/);
+ mLanguages.insert(26, kl->twoAlphaToLanguageName("it") /*i18n("Italian")*/);
+ mLanguages.insert(27, kl->twoAlphaToLanguageName("ja") /*i18n("Japanese")*/);
+ mLanguages.insert(28, kl->twoAlphaToLanguageName("km") /*i18n("Khmer")*/);
+ mLanguages.insert(29, kl->twoAlphaToLanguageName("ko") /*i18n("Korean")*/);
+ mLanguages.insert(30, kl->twoAlphaToLanguageName("lo") /*i18n("Lao")*/);
+ mLanguages.insert(31, kl->twoAlphaToLanguageName("lv") /*i18n("Latvian")*/);
+ mLanguages.insert(32, kl->twoAlphaToLanguageName("lt") /*i18n("Lithuanian")*/);
+ mLanguages.insert(33, kl->twoAlphaToLanguageName("ms") /*i18n("Malay")*/);
+ mLanguages.insert(34, kl->twoAlphaToLanguageName("no") /*i18n("Norwegian")*/);
+ mLanguages.insert(35, kl->twoAlphaToLanguageName("pl") /*i18n("Polish")*/);
+ mLanguages.insert(36, kl->twoAlphaToLanguageName("pt") /*i18n("Portuguese")*/);
+ mLanguages.insert(37, kl->twoAlphaToLanguageName("ro") /*i18n("Romanian")*/);
+ mLanguages.insert(38, kl->twoAlphaToLanguageName("ru") /*i18n("Russian")*/);
+ mLanguages.insert(39, kl->twoAlphaToLanguageName("sr") /*i18n("Serbian")*/);
+ mLanguages.insert(40, kl->twoAlphaToLanguageName("sk") /*i18n("Slovak")*/);
+ mLanguages.insert(41, kl->twoAlphaToLanguageName("sl") /*i18n("Slovenian")*/);
+ mLanguages.insert(42, kl->twoAlphaToLanguageName("so") /*i18n("Somali")*/);
+ mLanguages.insert(43, kl->twoAlphaToLanguageName("es") /*i18n("Spanish")*/);
+ mLanguages.insert(44, kl->twoAlphaToLanguageName("sw") /*i18n("Swahili")*/);
+ mLanguages.insert(45, kl->twoAlphaToLanguageName("sv") /*i18n("Swedish")*/);
+ mLanguages.insert(46, kl->twoAlphaToLanguageName("tl") /*i18n("Tagalog")*/);
+ mLanguages.insert(47, kl->twoAlphaToLanguageName("tt") /*i18n("Tatar")*/);
+ mLanguages.insert(48, kl->twoAlphaToLanguageName("th") /*i18n("Thai")*/);
+ mLanguages.insert(49, kl->twoAlphaToLanguageName("tr") /*i18n("Turkish")*/);
+ mLanguages.insert(50, kl->twoAlphaToLanguageName("uk") /*i18n("Ukrainian")*/);
+ mLanguages.insert(51, kl->twoAlphaToLanguageName("ur") /*i18n("Urdu")*/);
+ mLanguages.insert(52, kl->twoAlphaToLanguageName("vi") /*i18n("Vietnamese")*/);
+ mLanguages.insert(53, kl->twoAlphaToLanguageName("yi") /*i18n("Yiddish")*/);
+ mLanguages.insert(54, kl->twoAlphaToLanguageName("yo") /*i18n("Yoruba")*/);
+ mLanguages.insert(55, i18n("Taiwanese"));
+ mLanguages.insert(56, kl->twoAlphaToLanguageName("af") /*i18n("Afrikaans")*/);
+ mLanguages.insert(57, kl->twoAlphaToLanguageName("fa") /*i18n("Persian")*/);
+ mLanguages.insert(58, kl->twoAlphaToLanguageName("sq") /*i18n("Albanian")*/);
+ mLanguages.insert(59, kl->twoAlphaToLanguageName("hy") /*i18n("Armenian")*/);
+}
+
+void ICQProtocol::initEncodings()
+{
+ mEncodings.insert(2026, i18n("Big5"));
+ mEncodings.insert(2101, i18n("Big5-HKSCS"));
+ mEncodings.insert(18, i18n("euc-JP Japanese"));
+ mEncodings.insert(38, i18n("euc-KR Korean"));
+ mEncodings.insert(57, i18n("GB-2312 Chinese"));
+ mEncodings.insert(113, i18n("GBK Chinese"));
+ mEncodings.insert(114, i18n("GB18030 Chinese"));
+
+ mEncodings.insert(16, i18n("JIS Japanese"));
+ mEncodings.insert(17, i18n("Shift-JIS Japanese"));
+
+ mEncodings.insert(2084, i18n("KOI8-R Russian"));
+ mEncodings.insert(2088, i18n("KOI8-U Ukrainian"));
+
+ mEncodings.insert(4, i18n("ISO-8859-1 Western"));
+ mEncodings.insert(5, i18n("ISO-8859-2 Central European"));
+ mEncodings.insert(6, i18n("ISO-8859-3 Central European"));
+ mEncodings.insert(7, i18n("ISO-8859-4 Baltic"));
+ mEncodings.insert(8, i18n("ISO-8859-5 Cyrillic"));
+ mEncodings.insert(9, i18n("ISO-8859-6 Arabic"));
+ mEncodings.insert(10, i18n("ISO-8859-7 Greek"));
+ mEncodings.insert(11, i18n("ISO-8859-8 Hebrew, visually ordered"));
+ mEncodings.insert(85, i18n("ISO-8859-8-I Hebrew, logically ordered"));
+ mEncodings.insert(12, i18n("ISO-8859-9 Turkish"));
+ mEncodings.insert(13, i18n("ISO-8859-10"));
+ mEncodings.insert(109, i18n("ISO-8859-13"));
+ mEncodings.insert(110, i18n("ISO-8859-14"));
+ mEncodings.insert(111, i18n("ISO-8859-15 Western"));
+
+ mEncodings.insert(2250, i18n("Windows-1250 Central European"));
+ mEncodings.insert(2251, i18n("Windows-1251 Cyrillic"));
+ mEncodings.insert(2252, i18n("Windows-1252 Western"));
+ mEncodings.insert(2253, i18n("Windows-1253 Greek"));
+ mEncodings.insert(2254, i18n("Windows-1254 Turkish"));
+ mEncodings.insert(2255, i18n("Windows-1255 Hebrew"));
+ mEncodings.insert(2256, i18n("Windows-1256 Arabic"));
+ mEncodings.insert(2257, i18n("Windows-1257 Baltic"));
+ mEncodings.insert(2258, i18n("Windows-1258 Viet Nam"));
+
+ mEncodings.insert(2009, i18n("IBM 850"));
+ mEncodings.insert(2085, i18n("IBM 866"));
+
+ mEncodings.insert(2259, i18n("TIS-620 Thai"));
+
+ mEncodings.insert(106, i18n("UTF-8 Unicode"));
+ mEncodings.insert(1015, i18n("UTF-16 Unicode"));
+
+/*
+Missing ones (copied from qtextcodec doc):
+TSCII -- Tamil
+utf8 -- Unicode, 8-bit
+utf16 -- Unicode
+CP874
+Apple Roman
+*/
+}
+void ICQProtocol::initMaritals()
+{
+ mMarital.insert(0 , "");
+ mMarital.insert(10 , i18n("Single"));
+ mMarital.insert(11 , i18n("Long term relationship"));
+ mMarital.insert(12 , i18n("Engaged"));
+ mMarital.insert(20 , i18n("Married"));
+ mMarital.insert(30 , i18n("Divorced"));
+ mMarital.insert(31 , i18n("Separated"));
+ mMarital.insert(40 , i18n("Widowed"));
+
+}
+
+void ICQProtocol::initInterests()
+{
+ mInterests.insert(0 , "");
+ mInterests.insert(100, i18n("Art"));
+ mInterests.insert(101, i18n("Cars"));
+ mInterests.insert(102, i18n("Celebrities"));
+ mInterests.insert(103, i18n("Collections"));
+ mInterests.insert(104, i18n("Computers"));
+ mInterests.insert(105, i18n("Culture"));
+ mInterests.insert(106, i18n("Fitness"));
+ mInterests.insert(107, i18n("Games"));
+ mInterests.insert(108, i18n("Hobbies"));
+ mInterests.insert(109, i18n("ICQ - Help"));
+ mInterests.insert(110, i18n("Internet"));
+ mInterests.insert(111, i18n("Lifestyle"));
+ mInterests.insert(112, i18n("Movies"));
+ mInterests.insert(113, i18n("Music"));
+ mInterests.insert(114, i18n("Outdoors"));
+ mInterests.insert(115, i18n("Parenting"));
+ mInterests.insert(116, i18n("Pets and animals"));
+ mInterests.insert(117, i18n("Religion"));
+ mInterests.insert(118, i18n("Science"));
+ mInterests.insert(119, i18n("Skills"));
+ mInterests.insert(120, i18n("Sports"));
+ mInterests.insert(121, i18n("Web design"));
+ mInterests.insert(122, i18n("Ecology"));
+ mInterests.insert(123, i18n("News and media"));
+ mInterests.insert(124, i18n("Government"));
+ mInterests.insert(125, i18n("Business"));
+ mInterests.insert(126, i18n("Mystics"));
+ mInterests.insert(127, i18n("Travel"));
+ mInterests.insert(128, i18n("Astronomy"));
+ mInterests.insert(129, i18n("Space"));
+ mInterests.insert(130, i18n("Clothing"));
+ mInterests.insert(131, i18n("Parties"));
+ mInterests.insert(132, i18n("Women"));
+ mInterests.insert(133, i18n("Social science"));
+ mInterests.insert(134, i18n("60's"));
+ mInterests.insert(135, i18n("70's"));
+ mInterests.insert(136, i18n("40's"));
+ mInterests.insert(137, i18n("50's"));
+ mInterests.insert(138, i18n("Finance and corporate"));
+ mInterests.insert(139, i18n("Entertainment"));
+ mInterests.insert(140, i18n("Consumer electronics"));
+ mInterests.insert(141, i18n("Retail stores"));
+ mInterests.insert(142, i18n("Health and beauty"));
+ mInterests.insert(143, i18n("Media"));
+ mInterests.insert(144, i18n("Household products"));
+ mInterests.insert(145, i18n("Mail order catalog"));
+ mInterests.insert(146, i18n("Business services"));
+ mInterests.insert(147, i18n("Audio and visual"));
+ mInterests.insert(148, i18n("Sporting and athletic"));
+ mInterests.insert(149, i18n("Publishing"));
+ mInterests.insert(150, i18n("Home automation"));
+
+}
+
+void ICQProtocol::fillComboFromTable(QComboBox *box, const QMap<int, QString> &map)
+{
+// kdDebug(14153) << k_funcinfo << "Called." << endl;
+
+ QStringList list = map.values();
+ list.sort();
+ box->insertStringList(list);
+}
+
+void ICQProtocol::setComboFromTable(QComboBox *box, const QMap<int, QString> &map, int value)
+{
+// kdDebug(14153) << k_funcinfo << "Called." << endl;
+ QMap<int, QString>::ConstIterator it;
+ it = map.find(value);
+ if (!(*it))
+ return;
+
+ for(int i=0; i<box->count(); i++)
+ {
+ if((*it) == box->text(i))
+ {
+ box->setCurrentItem(i);
+ return;
+ }
+ }
+}
+
+int ICQProtocol::getCodeForCombo(QComboBox *cmb, const QMap<int, QString> &map)
+{
+ const QString curText = cmb->currentText();
+
+ QMap<int, QString>::ConstIterator it;
+ for(it = map.begin(); it != map.end(); ++it)
+ {
+ if(it.data() == curText)
+ return it.key();
+ }
+ return 0; // unspecified is always first 0
+}
+#if 0
+
+void ICQProtocol::fillTZCombo(QComboBox *combo)
+{
+ QTime time(12, 0);
+ QTime done(0, 0);
+
+ while(time > done)
+ {
+ combo->insertItem("GMT-" + time.toString("h:mm"));
+ // subtract 30 minutes
+ time = time.addSecs(-30 * 60);
+ }
+
+ time = QTime(0, 0);
+ done = QTime(12, 0);
+
+ while(time <= done)
+ {
+ combo->insertItem("GMT+" + time.toString("h:mm"));
+ // add 30 minutes
+ time = time.addSecs(30 * 60);
+ }
+}
+
+void ICQProtocol::setTZComboValue(QComboBox *combo, const char &tz)
+{
+ kdDebug(14153) << k_funcinfo << "tz=" << int(tz) << endl;
+ if ((tz < -24) || (tz > 24))
+ combo->setCurrentItem(24); // GMT+0:00 as default
+ else
+ combo->setCurrentItem(24 + tz);
+}
+
+char ICQProtocol::getTZComboValue(QComboBox *combo)
+{
+ char ret = combo->currentItem() - 24;
+// kdDebug(14153) << k_funcinfo << "return value=" << int(ret) << endl;
+ return ret;
+}
+
+#endif
+ICQProtocol *ICQProtocol::protocol()
+{
+ return protocolStatic_;
+}
+
+bool ICQProtocol::canSendOffline() const
+{
+ return true;
+}
+
+Kopete::Contact *ICQProtocol::deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &/*addressBookData*/ )
+{
+ QString accountId = serializedData["accountId"];
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts(this);
+ ICQAccount *account = static_cast<ICQAccount*>(accounts[accountId]);
+
+ if(!account)
+ {
+ kdWarning(14153) << k_funcinfo <<
+ "WARNING: Account for contact does not exist, skipping " << accountId << endl;
+ return 0;
+ }
+
+ QString contactId=serializedData["contactId"];
+ uint ssiGid = 0, ssiBid = 0, ssiType = 0xFFFF;
+ QString ssiName;
+ bool ssiWaitingAuth = false;
+ if ( serializedData.contains( "ssi_name" ) )
+ ssiName = serializedData["ssi_name"];
+
+ if ( serializedData.contains( "ssi_waitingAuth" ) )
+ {
+ QString authStatus = serializedData["ssi_waitingAuth"];
+ if ( authStatus == "true" )
+ ssiWaitingAuth = true;
+ }
+
+ if ( serializedData.contains( "ssi_gid" ) )
+ ssiGid = serializedData["ssi_gid"].toUInt();
+ if ( serializedData.contains( "ssi_bid" ) )
+ ssiBid = serializedData["ssi_bid"].toUInt();
+ if ( serializedData.contains( "ssi_type" ) )
+ ssiType = serializedData["ssi_type"].toUInt();
+
+ Oscar::SSI item( ssiName, ssiGid, ssiBid, ssiType, QValueList<TLV>(), 0 );
+ item.setWaitingAuth( ssiWaitingAuth );
+ ICQContact *c = new ICQContact( account, contactId, metaContact, QString::null, item );
+ return c;
+}
+
+AddContactPage *ICQProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *account)
+{
+ return new ICQAddContactPage( static_cast<ICQAccount*>( account ), parent);
+}
+
+KopeteEditAccountWidget *ICQProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new ICQEditAccountWidget(this, account, parent);
+}
+
+Kopete::Account *ICQProtocol::createNewAccount(const QString &accountId)
+{
+ return new ICQAccount(this, accountId);
+}
+
+ICQ::OnlineStatusManager *ICQProtocol::statusManager()
+{
+ return statusManager_;
+}
+
+//END class ICQProtocol
+
+#include "icqprotocol.moc"
+// kate: indent-mode csands;
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/icq/icqprotocol.h b/kopete/protocols/oscar/icq/icqprotocol.h
new file mode 100644
index 00000000..8e3c1be9
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqprotocol.h
@@ -0,0 +1,106 @@
+/*
+ oscarprotocol.h - Oscar Protocol Plugin
+
+ Copyright (c) 2003 by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQPROTOCOL_H
+#define ICQPROTOCOL_H
+
+#include "kopeteprotocol.h"
+#include "kopetemimetypehandler.h"
+#include "kopeteonlinestatus.h"
+
+class QComboBox;
+/*class ICQUserInfoWidget;
+class ICQContact;*/
+
+namespace ICQ { class OnlineStatusManager; }
+
+class ICQProtocolHandler : public Kopete::MimeTypeHandler
+{
+public:
+ ICQProtocolHandler();
+ void handleURL(const QString &mimeType, const KURL & url) const;
+};
+
+
+class ICQProtocol : public Kopete::Protocol
+{
+Q_OBJECT
+
+public:
+ ICQProtocol(QObject *parent, const char *name, const QStringList &args);
+ virtual ~ICQProtocol();
+
+ /**
+ * Return the active instance of the protocol
+ */
+ static ICQProtocol *protocol();
+
+ virtual bool canSendOffline() const;
+
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &addressBookData );
+ AddContactPage *createAddContactWidget(QWidget *parent, Kopete::Account *account);
+ KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+ Kopete::Account *createNewAccount(const QString &accountId);
+
+ ICQ::OnlineStatusManager *statusManager();
+
+
+ const Kopete::ContactPropertyTmpl firstName;
+ const Kopete::ContactPropertyTmpl lastName;
+ const Kopete::ContactPropertyTmpl awayMessage;
+ const Kopete::ContactPropertyTmpl emailAddress;
+ const Kopete::ContactPropertyTmpl ipAddress;
+ const Kopete::ContactPropertyTmpl clientFeatures;
+ const Kopete::ContactPropertyTmpl buddyIconHash;
+ const Kopete::ContactPropertyTmpl contactEncoding;
+
+ const QMap<int, QString> &genders() { return mGenders; }
+ const QMap<int, QString> &countries() { return mCountries; }
+ const QMap<int, QString> &languages() { return mLanguages; }
+ const QMap<int, QString> &encodings() { return mEncodings; }
+ const QMap<int, QString> &maritals() { return mMarital; }
+ const QMap<int, QString> &interests() { return mInterests; }
+
+ void fillComboFromTable( QComboBox*, const QMap<int, QString>& );
+ void setComboFromTable( QComboBox*, const QMap<int, QString>&, int );
+ int getCodeForCombo( QComboBox*, const QMap<int, QString>& );
+ /* void fillTZCombo(QComboBox *combo);
+ void setTZComboValue(QComboBox *combo, const char &tz);
+ char getTZComboValue(QComboBox *combo); */
+
+private:
+ void initGenders();
+ void initLang();
+ void initCountries();
+ void initEncodings();
+ void initMaritals();
+ void initInterests();
+
+private:
+ static ICQProtocol* protocolStatic_;
+ ICQ::OnlineStatusManager* statusManager_;
+ QMap<int, QString> mGenders;
+ QMap<int, QString> mCountries;
+ QMap<int, QString> mLanguages;
+ QMap<int, QString> mEncodings;
+ QMap<int, QString> mMarital;
+ QMap<int, QString> mInterests;
+ ICQProtocolHandler protohandler;
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/icq/icqreadaway.cpp b/kopete/protocols/oscar/icq/icqreadaway.cpp
new file mode 100644
index 00000000..94cccafd
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqreadaway.cpp
@@ -0,0 +1,106 @@
+/*
+ icqreadaway.cpp - ICQ Protocol Plugin
+
+ Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqreadaway.h"
+
+#include "icqprotocol.h"
+#include "icqaccount.h"
+#include "icqcontact.h"
+
+#include <qvbox.h>
+
+#include <ktextbrowser.h>
+#include <klocale.h>
+#include <krun.h>
+
+#include <assert.h>
+
+
+ICQReadAway::ICQReadAway(ICQContact *c, QWidget *parent, const char* name)
+ : KDialogBase(parent, name, false, QString::null, Close | User1,
+ Close, false, i18n("&Fetch Again"))
+{
+ assert(c);
+
+ mAccount = static_cast<ICQAccount*>(c->account());
+ mContact = c;
+ setCaption(i18n("'%2' Message for %1").arg(c->displayName()).arg(c->onlineStatus().description()));
+
+ QVBox *mMainWidget = makeVBoxMainWidget();
+
+ awayMessageBrowser = new KTextBrowser(mMainWidget, "userInfoView");
+ awayMessageBrowser->setTextFormat(AutoText);
+ awayMessageBrowser->setNotifyClick(true);
+ awayMessageBrowser->setText(mContact->awayMessage());
+
+ QObject::connect(
+ awayMessageBrowser, SIGNAL(urlClick(const QString&)),
+ this, SLOT(slotUrlClicked(const QString&)));
+ QObject::connect(
+ awayMessageBrowser, SIGNAL(mailClick(const QString&, const QString&)),
+ this, SLOT(slotMailClicked(const QString&, const QString&)));
+
+ connect(this, SIGNAL(user1Clicked()),
+ this, SLOT(slotFetchAwayMessage()));
+ connect(this, SIGNAL(closeClicked()),
+ this, SLOT(slotCloseClicked()));
+
+ connect(c, SIGNAL(awayMessageChanged()),
+ this, SLOT(slotAwayMessageChanged()));
+
+ slotFetchAwayMessage();
+}
+
+void ICQReadAway::slotFetchAwayMessage()
+{
+ if(!mAccount->isConnected())
+ return;
+
+ awayMessageBrowser->setDisabled(true);
+ enableButton(User1,false);
+
+ mAccount->engine()->requestAwayMessage(mContact);
+
+ setCaption(i18n("Fetching '%2' Message for %1...").arg(mContact->displayName()).arg(mContact->onlineStatus().description()));
+} // END slotFetchAwayMessage()
+
+void ICQReadAway::slotAwayMessageChanged()
+{
+ setCaption(i18n("'%2' Message for %1").arg(mContact->displayName()).arg(mContact->onlineStatus().description()));
+ awayMessageBrowser->setText(mContact->awayMessage());
+
+ awayMessageBrowser->setDisabled(false);
+ enableButton(User1,true);
+
+} // END slotAwayMessageChanged()
+
+void ICQReadAway::slotCloseClicked()
+{
+ emit closing();
+}
+
+void ICQReadAway::slotUrlClicked(const QString &url)
+{
+ new KRun(KURL(url));
+}
+
+void ICQReadAway::slotMailClicked(const QString&, const QString &address)
+{
+ new KRun(KURL(address));
+}
+
+#include "icqreadaway.moc"
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/icq/icqreadaway.h b/kopete/protocols/oscar/icq/icqreadaway.h
new file mode 100644
index 00000000..7e62588e
--- /dev/null
+++ b/kopete/protocols/oscar/icq/icqreadaway.h
@@ -0,0 +1,52 @@
+/*
+ icqreadaway.h - ICQ Protocol Plugin
+
+ Copyright (c) 2003 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQREADAWAY_H
+#define ICQREADAWAY_H
+
+#include <kdebug.h>
+#include <kdialogbase.h>
+
+class ICQAccount;
+class ICQContact;
+class KTextBrowser;
+class QVBox;
+
+class ICQReadAway : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ICQReadAway(ICQContact *, QWidget *parent = 0, const char* name = "ICQReadAway");
+
+ private slots:
+ void slotFetchAwayMessage();
+ void slotAwayMessageChanged();
+ void slotCloseClicked();
+ void slotUrlClicked(const QString &url);
+ void slotMailClicked(const QString&, const QString &address);
+
+ signals:
+ void closing();
+
+ private:
+ ICQAccount *mAccount;
+ ICQContact *mContact;
+ QVBox *mMainWidget;
+ KTextBrowser *awayMessageBrowser;
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/icq/kopete_icq.desktop b/kopete/protocols/oscar/icq/kopete_icq.desktop
new file mode 100644
index 00000000..c774afde
--- /dev/null
+++ b/kopete/protocols/oscar/icq/kopete_icq.desktop
@@ -0,0 +1,78 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=icq_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_icq
+X-Kopete-Messaging-Protocol=messaging/icq
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_icq
+X-KDE-PluginInfo-Version=0.10.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=ICQ
+Name[bn]=আই-সি-কিউ
+Name[hi]=आईसीकà¥à¤¯à¥‚
+Name[ne]=आईसीकà¥à¤¯à¥‚
+Comment=Protocol to connect to ICQ
+Comment[ar]=البرتوكول سيتصل بـ ICQ
+Comment[be]=Пратакол ICQ
+Comment[bg]=Протокол за връзка Ñ ICQ
+Comment[bn]=আই-সি-কিউতে সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh AIM
+Comment[bs]=ICQ protokol
+Comment[ca]=Protocol per a connectar-se a ICQ
+Comment[cs]=Protokol k připojení k ICQ
+Comment[cy]=Protocol i gysylltu ag ICQ
+Comment[da]=Protokol til at forbinde til ICQ
+Comment[de]=Protokoll zur Verbindung mit ICQ
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο ICQ
+Comment[es]=Protocolo de conexión de ICQ
+Comment[et]=Protokoll ühendumiseks ICQ-ga
+Comment[eu]=ICQ-ra konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال به ICQ
+Comment[fi]=Yhteyskäytäntö ICQ-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur ICQ
+Comment[ga]=Prótacal chun ceangal le ICQ
+Comment[gl]=Protocolo para se conectar á rede ICQ
+Comment[he]=פרוטוקול התחברות ל- ICQ
+Comment[hi]=आईसीकà¥à¤¯à¥‚ से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na ICQ
+Comment[hu]=Protokoll az ICQ használatához
+Comment[is]=Samskiptamáti til að tengjast ICQ
+Comment[it]=Protocollo per connessione a ICQ
+Comment[ja]=ICQ ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=ICQ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=ICQ-ге қоÑылу протоколы
+Comment[km]=ពិធីការភ្ជាប់​ទៅ ICQ
+Comment[lt]=Protokolas prisijungimui prie ICQ
+Comment[mk]=Протокол за поврзување на ICQ
+Comment[nb]=Protokoll for å koble til ICQ
+Comment[nds]=Protokoll för't Tokoppeln na ICQ
+Comment[ne]=आईसीकà¥à¤¯à¥‚ मा जडान गरà¥à¤¨à¥à¤ªà¤°à¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor ICQ
+Comment[nn]=Protokoll for å kopla til ICQ
+Comment[pl]=Protokół połączenia z serwerem ICQ
+Comment[pt]=Um protocolo para se ligar ao ICQ
+Comment[pt_BR]=Protocolo de conexão ao ICQ
+Comment[ro]=Protocol de conectare la ICQ
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº ICQ
+Comment[sk]=Protokol pre pripojenie k ICQ
+Comment[sl]=Protokol za povezavo na ICQ
+Comment[sr]=Протокол за повезивање на ICQ
+Comment[sr@Latn]=Protokol za povezivanje na ICQ
+Comment[sv]=Protokoll för att ansluta till ICQ
+Comment[ta]=ICQ உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба ICQ
+Comment[tr]=ICQ'a bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· ICQ
+Comment[uz]=ICQ bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=ICQ билан алоқа ўрнатиш учун протокол
+Comment[zh_CN]=连接到 ICQ åè®®
+Comment[zh_HK]=用來連接至 ICQ 的通訊å”定
+Comment[zh_TW]=連線到 ICQ çš„å”定
+
diff --git a/kopete/protocols/oscar/icq/ui/Makefile.am b/kopete/protocols/oscar/icq/ui/Makefile.am
new file mode 100644
index 00000000..24a726f2
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/Makefile.am
@@ -0,0 +1,17 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = -I$(srcdir)/../ \
+ -I$(srcdir)/../../ \
+ -I$(srcdir)/ui/ \
+ -I$(srcdir)/../../liboscar \
+ -I$(srcdir)/../../../../ \
+ $(KOPETE_INCLUDES) $(all_includes)
+
+noinst_LTLIBRARIES = libkopeteicqui.la
+
+libkopeteicqui_la_SOURCES = icqadd.ui icqeditaccountui.ui \
+ icqeditaccountwidget.cpp icqgeneralinfo.ui icqotherinfowidget.ui icqworkinfowidget.ui icqinterestinfowidget.ui\
+ icquserinfowidget.cpp icqauthreplyui.ui icqauthreplydialog.cpp icqaddcontactpage.cpp \
+ icqsearchbase.ui icqsearchdialog.cpp icqsearchdialog.h
+
+libkopeteicqui_la_LIBADD = $(top_builddir)/kopete/libkopete/libkopete.la
+
diff --git a/kopete/protocols/oscar/icq/ui/icqadd.ui b/kopete/protocols/oscar/icq/ui/icqadd.ui
new file mode 100644
index 00000000..ef793fbb
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqadd.ui
@@ -0,0 +1,122 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>icqAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>icqAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>452</width>
+ <height>88</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>UIN #:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>uinEdit</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Alternatively, you can search the ICQ Whitepages :</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>47</width>
+ <height>26</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>searchButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Search</string>
+ </property>
+ <property name="iconSet">
+ <iconset>image0</iconset>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<images>
+ <image name="image0">
+ <data format="PNG" length="736">89504e470d0a1a0a0000000d49484452000000100000001008060000001ff3ff61000002a749444154388d7d91cd4b945114c69f73ef3b33ea7ca838a6a32681501194d2975050b4c82f92dc042e5a550b5bf60744bb16b58a8268218144d026da64da228a0a2b52d1c8c8c48f2c54669c19df79df793fefbd2d662469860e1cb870cef3e339cf2500989b5b88e56cb78b0857f2b6d3e67b0e0b0503baf4e57bdbb21eb8b6fadedf7fda4599a2e999f9bdb66b5fb75db79b3164b8c6b3504af8426852885adff3272dc31cb14c313e38d827fe0568593d77225811b8d8d810475555a89e88e0791e0c330f2515cc7c9e6ccb822f8d6f00964a009e6b5f8ed554211a0d235c5501ce1874c30411414a89582cdc0625c3e964e64c3900b35de768301000e70c1ae7608c81738e80a6a1b2b202b16814cd4d8946ced550b90c98e33a158c113ccf47ceccc3cc5b080534282591d94c637d6d1d5bd92c2ccb3af2f0d1e8bd92135cd7370184018088c0350ec639a291086291083ccf432e6740d3822c994cc54a1c5886f5d1755d48a920a584520a4a291000251508844c3a83baf82e1051e90996e5dc5959fe0d21fd4270424208015184e9ba8e0f139350d050460fd6de7ec80e5786313b3307c33021a484effb104222994c61ecc52b380ec1cbfcc281fcd33dd3379af7ec04d0f497c5ae8977afc77b7acf6262620a7a2e0d2505a0181a1a1388d735209f5a41647504bb833fdcad8de4e896c9864edd5edb00006d9bd49468c4c0406f318b420b2121a440eaf324226d3588b79c0f6a536303d6fc2a9e5d4d5c1bb8bfb6cc769829f7cd2010aaf77741f7dbb095d1517bb81b0dadf57dd1907bf3f1a5448b5656b52d2ea6c62b6bf076ad09355f17cc939d84face736185d10bd9d9541dfbbb5c1010018c1158f14d44205600ad878ebdf9f47cfceec6a6e5b0d6e39a1139d8a5b1e2707878e47f660a15aaddfcb9a4df4a3f79d921abf7f52cda1d737f0030624881b39160420000000049454e44ae426082</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp
new file mode 100644
index 00000000..b1ab2eb4
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.cpp
@@ -0,0 +1,126 @@
+ /*
+ icqaddcontactpage.cpp - ICQ Protocol Plugin
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqaddcontactpage.h"
+
+#include <ctype.h>
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qtabwidget.h>
+#include <qlabel.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+
+#include "icqadd.h"
+#include "icqaccount.h"
+#include "icqprotocol.h"
+#include "icqsearchdialog.h"
+
+
+ICQAddContactPage::ICQAddContactPage(ICQAccount *owner, QWidget *parent, const char *name)
+ : AddContactPage(parent,name)
+{
+ kdDebug(14153) << k_funcinfo << "called" << endl;
+ mAccount = owner;
+ m_searchDialog = 0L;
+
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ addUI = new icqAddUI(this);
+ connect( addUI->searchButton, SIGNAL( clicked() ), this, SLOT( showSearchDialog() ) );
+}
+
+ICQAddContactPage::~ICQAddContactPage()
+{
+}
+
+void ICQAddContactPage::setUINFromSearch( const QString& uin )
+{
+ addUI->uinEdit->setText( uin );
+}
+
+void ICQAddContactPage::showEvent(QShowEvent *e)
+{
+// slotSelectionChanged();
+ AddContactPage::showEvent(e);
+}
+
+bool ICQAddContactPage::apply(Kopete::Account* , Kopete::MetaContact *parentContact )
+{
+ kdDebug(14153) << k_funcinfo << "called; adding contact..." << endl;
+
+ QString contactId = addUI->uinEdit->text();
+ kdDebug(14153) << k_funcinfo << "uin=" << contactId << endl;
+ return mAccount->addContact(contactId, parentContact, Kopete::Account::ChangeKABC );
+
+}
+
+bool ICQAddContactPage::validateData()
+{
+ if(!mAccount->isConnected())
+ {
+ // Account currently offline
+ addUI->searchButton->setEnabled( false );
+ addUI->uinEdit->setEnabled( false );
+ KMessageBox::sorry( this, i18n("You must be online to add a contact."), i18n("ICQ Plugin") );
+ return false;
+ }
+
+ Q_ULONG uin = addUI->uinEdit->text().toULong();
+ if ( uin < 1000 )
+ {
+ // Invalid (or missing) UIN
+ KMessageBox::sorry( this, i18n("You must enter a valid UIN."), i18n("ICQ Plugin") );
+ return false;
+ }
+ else
+ {
+ // UIN is valid
+ return true;
+ }
+}
+
+void ICQAddContactPage::showSearchDialog()
+{
+ if ( m_searchDialog )
+ m_searchDialog->raise();
+ else
+ {
+ m_searchDialog = new ICQSearchDialog( mAccount, this, "icqSearchDialog" );
+ m_searchDialog->show();
+ connect( m_searchDialog, SIGNAL( finished() ), this, SLOT( searchDialogDestroyed() ) );
+ }
+}
+
+void ICQAddContactPage::searchDialogDestroyed()
+{
+ QObject::disconnect( this, 0, m_searchDialog, 0 );
+ m_searchDialog->delayedDestruct();
+ m_searchDialog = NULL;
+}
+
+
+#include "icqaddcontactpage.moc"
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h
new file mode 100644
index 00000000..e9285b79
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqaddcontactpage.h
@@ -0,0 +1,60 @@
+ /*
+ icqaddcontactpage.h - ICQ Protocol Plugin
+
+ Copyright (c) 2002 by Stefan Gehn <metz AT gehn.net>
+ Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQADDCONTACTPAGE_H
+#define ICQADDCONTACTPAGE_H
+
+#include <qwidget.h>
+#include <addcontactpage.h>
+
+/**
+ *@author Matt Rogers
+ *@author Stefan Gehn
+ */
+class icqAddUI;
+class ICQAccount;
+class ICQSearchDialog;
+
+class ICQAddContactPage : public AddContactPage
+{
+Q_OBJECT
+
+public:
+ ICQAddContactPage(ICQAccount *owner, QWidget *parent = 0, const char *name = 0);
+ ~ICQAddContactPage();
+
+ virtual bool validateData();
+ virtual bool apply(Kopete::Account* , Kopete::MetaContact *parentContact);
+
+ void setUINFromSearch( const QString& );
+
+protected:
+ void showEvent(QShowEvent *e);
+
+private slots:
+ void showSearchDialog();
+ void searchDialogDestroyed();
+private:
+
+ ICQAccount *mAccount;
+ icqAddUI *addUI;
+ ICQSearchDialog* m_searchDialog;
+};
+
+#endif
+
+//kate: space-indent off; replace-tabs off; indent-mode csands;
diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp
new file mode 100644
index 00000000..76b56fba
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.cpp
@@ -0,0 +1,73 @@
+/*
+ Kopete Oscar Protocol
+ icqauthreplydialog.cpp - ICQ authorization reply dialog
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "icqauthreplydialog.h"
+#include "icqauthreplyui.h"
+
+#include <klocale.h>
+
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+
+ICQAuthReplyDialog::ICQAuthReplyDialog( QWidget *parent, const char *name, bool wasRequested )
+ : KDialogBase( parent, name, true, i18n( "Authorization Reply" ), KDialogBase::Ok | KDialogBase::Cancel )
+{
+ m_ui = new ICQAuthReplyUI( this );
+ setMainWidget( m_ui );
+ m_wasRequested = wasRequested;
+
+ if ( !m_wasRequested )
+ {
+ m_ui->lblReqReason->hide();
+ m_ui->lblRequestReason->hide();
+ }
+ else
+ {
+ this->setWFlags( this->getWFlags() | Qt::WDestructiveClose );
+ }
+}
+
+ICQAuthReplyDialog::~ICQAuthReplyDialog()
+{
+}
+
+void ICQAuthReplyDialog::setUser( const QString & user )
+{
+ if ( m_wasRequested )
+ m_ui->lblUserReq->setText(
+ i18n( "<b>%1</b> requested authorization to add you to his/her contact list." ).arg( user ) );
+ else
+ m_ui->lblUserReq->setText( i18n( "Authorization reply to <b>%1</b>." ).arg( user ) );
+}
+
+void ICQAuthReplyDialog::setRequestReason( const QString & reason )
+{
+ m_ui->lblRequestReason->setText( reason );
+}
+
+QString ICQAuthReplyDialog::reason()
+{
+ return m_ui->leReason->text();
+}
+
+bool ICQAuthReplyDialog::grantAuth()
+{
+ return m_ui->rbGrant->isChecked();
+}
+
+#include "icqauthreplydialog.moc"
diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h
new file mode 100644
index 00000000..da27b241
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqauthreplydialog.h
@@ -0,0 +1,45 @@
+/*
+ Kopete Oscar Protocol
+ icqauthreplydialog.h - ICQ authorization reply dialog
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef ICQAUTHREPLYDIALOG_H
+#define ICQAUTHREPLYDIALOG_H
+
+#include <kdialogbase.h>
+
+class ICQAuthReplyUI;
+
+/**
+ * A dialog to ask user what to do when a contact requests authorization
+ * @author Gustavo Pichorim Boiko
+ */
+class ICQAuthReplyDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ ICQAuthReplyDialog(QWidget *parent = 0, const char *name = 0, bool wasRequested = true);
+ ~ICQAuthReplyDialog();
+
+ void setUser( const QString& user );
+ void setRequestReason( const QString& reason );
+ QString reason();
+ bool grantAuth();
+private:
+ bool m_wasRequested;
+ ICQAuthReplyUI *m_ui;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui b/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui
new file mode 100644
index 00000000..12607856
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqauthreplyui.ui
@@ -0,0 +1,196 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQAuthReplyUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQAuthReplyUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>412</width>
+ <height>148</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>ICQ Authorization Reply</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout22</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblReason</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Reason:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>leReason</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout23</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>bgAction</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="title">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="0" column="0">
+ <property name="name">
+ <cstring>rbGrant</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Grant authorization</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="0" column="1">
+ <property name="name">
+ <cstring>rbDecline</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Decline authorization</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer12</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>220</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lblUserReq</cstring>
+ </property>
+ <property name="text">
+ <string>%1 requested authorization to add you to his/her contact list.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout24</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblReqReason</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Request Reason:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblRequestReason</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Some reason...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui b/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui
new file mode 100644
index 00000000..3ecc91cb
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqeditaccountui.ui
@@ -0,0 +1,486 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQEditAccountUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQEditAccountUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>592</width>
+ <height>404</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - ICQ</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>tabWidget7</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Account Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblAccountId</cstring>
+ </property>
+ <property name="text">
+ <string>IC&amp;Q UIN:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtAccountId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of your ICQ account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of your ICQ account. This should be in the form of a number (no decimals, no spaces).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>edtAccountId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The user ID of your ICQ account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The user ID of your ICQ account. This should be in the form of a number (no decimals, no spaces).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="Kopete::UI::PasswordWidget">
+ <property name="name">
+ <cstring>mPasswordWidget</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkAutoLogin</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>E&amp;xclude from connect all</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you check that case, the account will not be connected when you press the "Connect All" button, or at startup even if you selected to automatically connect at startup</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>chkGlobalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Exclu&amp;de from Global Identity</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the ICQ network, you will need an ICQ account.&lt;br&gt;&lt;br&gt;
+If you do not currently have an ICQ account, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonRegister</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;gister New Account</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacerBasicSetup</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Accou&amp;nt Preferences</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSpinBox" row="1" column="3">
+ <property name="name">
+ <cstring>edtServerPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65534</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5190</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the ICQ server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the ICQ server that you would like to connect to. Normally this is 5190.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="2">
+ <property name="name">
+ <cstring>edtServerAddress</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>login.icq.com</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the ICQ server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the ICQ server you wish to connect to. Normally you will want the default (login.icq.com).</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>lblServerPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Po&amp;rt:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtServerPort</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the ICQ server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the ICQ server that you would like to connect to. Normally this is 5190.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver /</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtServerAddress</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the ICQ server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the ICQ server you wish to connect to. Normally you will want the default (login.icq.com).</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="4">
+ <property name="name">
+ <cstring>optionOverrideServer</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Override default server information</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>groupBox65</cstring>
+ </property>
+ <property name="title">
+ <string>Privacy Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>chkRequireAuth</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Require authorization before someone can add you to their contact list</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enable authorization requirement, which will not allow users to add you to their contact list without authorization from you.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable authorization requirement, which will not allow users to add you to their contact list without authorization from you. Check this box, and you will have to confirm any users who add you to their list before they may see your online status.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>chkHideIP</cstring>
+ </property>
+ <property name="text">
+ <string>Hide &amp;IP address</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this to hide your IP address from people when they view your user info</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Checking this box will not allow people to see what your IP address if they view your ICQ user details such as name, address, or age.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>chkWebAware</cstring>
+ </property>
+ <property name="text">
+ <string>Make my status available via &amp;ICQ's unified messaging center</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check this box to enable Web Aware functionality.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this box to enable ICQ's Web Aware functionality, which allows people to see your online status from ICQ's web page, and send you a message without necessarily having ICQ themselves.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacerPreferences</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox" row="2" column="1">
+ <property name="name">
+ <cstring>encodingCombo</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Default to the following &amp;encoding for messages:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>encodingCombo</cstring>
+ </property>
+ </widget>
+ </grid>
+ </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="1002">89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b000003b149444154388dad945f4c5b551cc73fe7dc4b7b4bcba0762d45c43114323599ee6192609c51d883892ce083f1718b3ebb185f8dc91e972cf39d2d2a2f1af664b6f1e0fe3863a0718969700eb0c52142da0242a1bd6d696f7bcff101585203ceb8fd9ece39f99dcff9fe7edf939f88c562ec465f5f9fe609442c161362173c3e3eae7b7a7ac8e7f36432196cdbfe4f907c3e4f2291201e8fe338cec3737357e9e8e828aded1e229d650e1f2d51754b082110124c13a4dc5ea341eb9dc284c0558a853f3ce8cb0677ef500fde7d39d2596679e326597b8e9abb85d7a770ab16ab6983ec5a05b487a70e36f0f4e10afe408d6a558310980108478dba4a1e8233990c5d474b64ed39aa3a8fe5f3317fbf81dbd70bccfeb205947632fd74f6589c1c6ea2f70d03a58ba0c1f2c9bdc1b66de3b8256a6e11cbe7e3ee1d181b590124fe2693aeee08d223c82c3a2c24b7b874bec8f26288774f7bd054504aef0dde6e99c0eb83f9fb266323cb80a27fb0958141836044605a2ee5523393371cc646fee2da37195aa35d0c0c5b4859ac03d7e91712dcaac5adab3650a3ff9d08ef7dd8404bb48869e5d958b5b87dadc4c9a1464e9f0d0326df7ebd86bd2e310cb1bf62d384d59441f2d70a070e1c60e09489929b988681bdd9cc97170bcc4c65595f71f8e0e3301337fc24a7732467831875a47f289652b0be5e4151e6d07316c1b0c0340d8ab92023e76d66a6b2840e36d2fb7a13fee632475e6edc367ea98a90fb98b7dd6310ca0328a44761582e1bab41befabcc0ec940d28bc5e93b68e064cab84e1d9beaeb48934eac1f53b01c1b000fca496aa54b61a99fcde61662a4b4b4b23d1680be9d426173e4df3602a48ea411989a4fd590f52a8fd156b05ed9d350e3defe3cfdf4b4c7ce770ea7d3fb9f520afbe1620daeee5c26735d20b9b9cfb6811a754a439e4e5c5639a4caa1e5caf586bfc0197b78702005cb9b4cae4cd3267ce8638fe964bd72b393e39d74928d242617303a756a37f284447770dcdbffc6384a05a85de1306e9a52057c7527c7131c3c42d3f475eb2303c82d4fc3276d6811db37efeb148723082d9b08f79f97c1e5729109a9a28307cc622d2d6cdf52b2b24efe548dedb00142009862cfa879ee1a71f6cec928353511472fbf4389148b0b0e0c108081412458dfe21c9f11351e67e7358595468246d1d1e5e38a6e9e851bc39d84ab502a669331dafec0d8ec7e3e8cb06e1a881d727d1ae40180a434a8c9db129a54126ad48a7358c2b4c5352c8c374bcccdab2bb37d8719cba79fab8211f9df218e0582c261e95f8bfc04f1a1e8bc5c4dfe0a190172af6a9690000000049454e44ae426082</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblServer</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblServerPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>edtServerAddress</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>edtServerPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget7</tabstop>
+ <tabstop>edtAccountId</tabstop>
+ <tabstop>chkAutoLogin</tabstop>
+ <tabstop>buttonRegister</tabstop>
+ <tabstop>optionOverrideServer</tabstop>
+ <tabstop>edtServerAddress</tabstop>
+ <tabstop>edtServerPort</tabstop>
+ <tabstop>chkRequireAuth</tabstop>
+ <tabstop>chkHideIP</tabstop>
+ <tabstop>chkWebAware</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kopetepasswordwidget.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp
new file mode 100644
index 00000000..e4b308be
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.cpp
@@ -0,0 +1,190 @@
+/*
+ icqeditaccountwidget.cpp - ICQ Account Widget
+
+ Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqeditaccountwidget.h"
+#include "icqeditaccountui.h"
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qtextedit.h>
+#include <qspinbox.h>
+#include <qpushbutton.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kjanuswidget.h>
+#include <kurllabel.h>
+#include <kdatewidget.h>
+#include <krun.h>
+#include <kpassdlg.h>
+
+#include "kopetepassword.h"
+#include "kopetepasswordwidget.h"
+
+#include "icqprotocol.h"
+#include "icqaccount.h"
+#include "icqcontact.h"
+
+ICQEditAccountWidget::ICQEditAccountWidget(ICQProtocol *protocol,
+ Kopete::Account *account, QWidget *parent, const char *name)
+ : QWidget(parent, name), KopeteEditAccountWidget(account)
+{
+ kdDebug(14153) << k_funcinfo << "Called." << endl;
+
+ mAccount=dynamic_cast<ICQAccount*>(account);
+ mProtocol=protocol;
+
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ mAccountSettings = new ICQEditAccountUI( this );
+
+ mProtocol->fillComboFromTable( mAccountSettings->encodingCombo, mProtocol->encodings() );
+
+ // Read in the settings from the account if it exists
+ if(mAccount)
+ {
+ mAccountSettings->edtAccountId->setText(mAccount->accountId());
+
+ // TODO: Remove me after we can change Account IDs (Matt)
+ mAccountSettings->edtAccountId->setDisabled(true);
+ mAccountSettings->mPasswordWidget->load(&mAccount->password());
+ mAccountSettings->chkAutoLogin->setChecked(mAccount->excludeConnect());
+
+ QString serverEntry = mAccount->configGroup()->readEntry("Server", "login.oscar.aol.com");
+ int portEntry = mAccount->configGroup()->readNumEntry("Port", 5190);
+ if ( serverEntry != "login.oscar.aol.com" || ( portEntry != 5190) )
+ mAccountSettings->optionOverrideServer->setChecked( true );
+
+ mAccountSettings->edtServerAddress->setText( serverEntry );
+ mAccountSettings->edtServerPort->setValue( portEntry );
+
+ bool configValue = mAccount->configGroup()->readBoolEntry( "RequireAuth", false );
+ mAccountSettings->chkRequireAuth->setChecked( configValue );
+
+ configValue = mAccount->configGroup()->readBoolEntry( "HideIP", true );
+ mAccountSettings->chkHideIP->setChecked( configValue );
+
+ configValue = mAccount->configGroup()->readBoolEntry( "WebAware", false );
+ mAccountSettings->chkWebAware->setChecked( configValue );
+
+ int encodingValue = mAccount->configGroup()->readNumEntry( "DefaultEncoding", 4 );
+ mProtocol->setComboFromTable( mAccountSettings->encodingCombo,
+ mProtocol->encodings(),
+ encodingValue );
+
+ // Global Identity
+ mAccountSettings->chkGlobalIdentity->setChecked( mAccount->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) );
+ }
+ else
+ {
+ mProtocol->setComboFromTable( mAccountSettings->encodingCombo,
+ mProtocol->encodings(),
+ 4 );
+ }
+
+ QObject::connect(mAccountSettings->buttonRegister, SIGNAL(clicked()), this, SLOT(slotOpenRegister()));
+
+ /* Set tab order to password custom widget correctly */
+ QWidget::setTabOrder( mAccountSettings->edtAccountId, mAccountSettings->mPasswordWidget->mRemembered );
+ QWidget::setTabOrder( mAccountSettings->mPasswordWidget->mRemembered, mAccountSettings->mPasswordWidget->mPassword );
+ QWidget::setTabOrder( mAccountSettings->mPasswordWidget->mPassword, mAccountSettings->chkAutoLogin );
+
+}
+
+Kopete::Account *ICQEditAccountWidget::apply()
+{
+ kdDebug(14153) << k_funcinfo << "Called." << endl;
+
+ // If this is a new account, create it
+ if (!mAccount)
+ {
+ kdDebug(14153) << k_funcinfo << "Creating a new account" << endl;
+ mAccount = new ICQAccount(mProtocol, mAccountSettings->edtAccountId->text());
+ if(!mAccount)
+ return NULL;
+ }
+
+ mAccountSettings->mPasswordWidget->save(&mAccount->password());
+ mAccount->setExcludeConnect(mAccountSettings->chkAutoLogin->isChecked());
+
+ bool configValue = mAccountSettings->chkRequireAuth->isChecked();
+ mAccount->configGroup()->writeEntry( "RequireAuth", configValue );
+
+ configValue = mAccountSettings->chkHideIP->isChecked();
+ mAccount->configGroup()->writeEntry( "HideIP", configValue );
+
+ configValue = mAccountSettings->chkWebAware->isChecked();
+ mAccount->configGroup()->writeEntry( "WebAware", configValue );
+
+ int encodingMib = mProtocol->getCodeForCombo( mAccountSettings->encodingCombo,
+ mProtocol->encodings() );
+ mAccount->configGroup()->writeEntry( "DefaultEncoding", encodingMib );
+
+ if ( mAccountSettings->optionOverrideServer->isChecked() )
+ {
+ mAccount->setServerAddress(mAccountSettings->edtServerAddress->text());
+ mAccount->setServerPort(mAccountSettings->edtServerPort->value());
+ }
+ else
+ {
+ mAccount->setServerAddress("login.oscar.aol.com");
+ mAccount->setServerPort(5190);
+ }
+
+ // Global Identity
+ mAccount->configGroup()->writeEntry( "ExcludeGlobalIdentity", mAccountSettings->chkGlobalIdentity->isChecked() );
+
+ return mAccount;
+}
+
+bool ICQEditAccountWidget::validateData()
+{
+ kdDebug(14153) << k_funcinfo << "Called." << endl;
+
+ QString userName = mAccountSettings->edtAccountId->text();
+
+ if (userName.isEmpty())
+ return false;
+
+ for (unsigned int i=0; i<userName.length(); i++)
+ {
+ if(!(userName[i]).isNumber())
+ return false;
+ }
+
+ // No need to check port, min and max values are properly defined in .ui
+
+ if (mAccountSettings->edtServerAddress->text().isEmpty())
+ return false;
+
+ // Seems good to me
+ kdDebug(14153) << k_funcinfo <<
+ "Account data validated successfully." << endl;
+ return true;
+}
+
+void ICQEditAccountWidget::slotOpenRegister()
+{
+ KRun::runURL( "http://go.icq.com/register/", "text/html" );
+}
+
+#include "icqeditaccountwidget.moc"
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h
new file mode 100644
index 00000000..fc5c6d38
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqeditaccountwidget.h
@@ -0,0 +1,52 @@
+/*
+ icqeditaccountwidget.h - ICQ Account Widget
+
+ Copyright (c) 2003 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQEDITACCOUNTWIDGET_H
+#define ICQEDITACCOUNTWIDGET_H
+
+#include <qwidget.h>
+#include <qdatetime.h>
+#include "editaccountwidget.h"
+
+namespace Kopete { class Account; }
+
+class ICQAccount;
+class ICQProtocol;
+class ICQEditAccountUI;
+
+class ICQEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+Q_OBJECT
+
+public:
+ ICQEditAccountWidget(ICQProtocol *, Kopete::Account *,
+ QWidget *parent=0, const char *name=0);
+
+ virtual bool validateData();
+ virtual Kopete::Account *apply();
+
+private slots:
+ void slotOpenRegister();
+
+protected:
+ ICQAccount *mAccount;
+ ICQProtocol *mProtocol;
+ ICQEditAccountUI *mAccountSettings;
+};
+#endif
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui b/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui
new file mode 100644
index 00000000..6383bec1
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqgeneralinfo.ui
@@ -0,0 +1,611 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQGeneralInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQGeneralInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>488</width>
+ <height>572</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Location &amp;&amp; Contact Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;City:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cityEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addressEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Phone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>phoneEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;State:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>stateEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>cityEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Countr&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>countryEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>homepageEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Email:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>emailEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel10_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Homepage:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>homepageEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="3">
+ <property name="name">
+ <cstring>cellEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>countryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>stateEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Fa&amp;x:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>faxEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>faxEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>phoneEdit</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Ce&amp;ll:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>addressEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Zip:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>zipEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>zipEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Personal Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>uinEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>fullNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Full name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fullNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>ipEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>125</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>timezoneEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>nickNameEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>nickNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>uinLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;UIN #:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>uinEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>birthdayLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Birthday:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>birthday</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="3" column="3">
+ <property name="name">
+ <cstring>ageSpinBox</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Gen&amp;der:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genderEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>birthday</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>fullNameEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>genderEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>ipLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;IP:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ipEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Timezone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>timezoneEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>maritalLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Marital status:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>A&amp;ge:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ageSpinBox</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>marital</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Origin</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>oStateEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>oCountryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>oCityEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>State:</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>nickNameEdit</tabstop>
+ <tabstop>fullNameEdit</tabstop>
+ <tabstop>genderEdit</tabstop>
+ <tabstop>uinEdit</tabstop>
+ <tabstop>ipEdit</tabstop>
+ <tabstop>timezoneEdit</tabstop>
+ <tabstop>ageSpinBox</tabstop>
+ <tabstop>addressEdit</tabstop>
+ <tabstop>cityEdit</tabstop>
+ <tabstop>stateEdit</tabstop>
+ <tabstop>zipEdit</tabstop>
+ <tabstop>countryEdit</tabstop>
+ <tabstop>phoneEdit</tabstop>
+ <tabstop>faxEdit</tabstop>
+ <tabstop>cellEdit</tabstop>
+ <tabstop>emailEdit</tabstop>
+ <tabstop>homepageEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui
new file mode 100644
index 00000000..ce4041c9
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqinterestinfowidget.ui
@@ -0,0 +1,116 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQInterestInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQInterestInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>660</width>
+ <height>572</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Interests</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>desc1</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>desc2</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>desc3</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="0">
+ <property name="name">
+ <cstring>topic2</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="0">
+ <property name="name">
+ <cstring>topic1</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="0">
+ <property name="name">
+ <cstring>topic3</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="0">
+ <property name="name">
+ <cstring>topic4</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>desc4</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="1" column="0">
+ <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>220</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui
new file mode 100644
index 00000000..4e5a3a34
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqotherinfowidget.ui
@@ -0,0 +1,68 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQOtherInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQOtherInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>289</width>
+ <height>473</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="4" column="1">
+ <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>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="text">
+ <string>Email addresses:</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>emailListBox</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Contact notes:</string>
+ </property>
+ </widget>
+ <widget class="QTextEdit" row="3" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>notesEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqsearchbase.ui b/kopete/protocols/oscar/icq/ui/icqsearchbase.ui
new file mode 100644
index 00000000..68e59281
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqsearchbase.ui
@@ -0,0 +1,493 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQSearchBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQSearchBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>603</width>
+ <height>465</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton" row="4" column="1">
+ <property name="name">
+ <cstring>clearButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>C&amp;lear</string>
+ </property>
+ <property name="stdItem" stdset="0">
+ <number>10</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Clear the results</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="7" column="1">
+ <property name="name">
+ <cstring>closeButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ <property name="stdItem" stdset="0">
+ <number>13</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Close this dialog</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>stopButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Stop</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="stdItem" stdset="0">
+ <number>26</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Stops the search</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="5" column="1">
+ <property name="name">
+ <cstring>addButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="stdItem" stdset="0">
+ <number>27</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Add the selected user to your contact list</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="6" column="1">
+ <property name="name">
+ <cstring>userInfoButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>User Info</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Show information about the selected contact</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <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>41</width>
+ <height>190</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget3</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>UIN Search</string>
+ </attribute>
+ <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;UIN #:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>uin</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>uin</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>105</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>ICQ Whitepages Search</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>lastName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>nickName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Last name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>lastName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;First name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>firstName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Email:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>email</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickName</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="3">
+ <property name="name">
+ <cstring>country</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>Lan&amp;guage:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>language</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1">
+ <property name="name">
+ <cstring>language</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>city</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;City:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>city</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>firstName</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>email</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Gender:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>gender</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <property name="name">
+ <cstring>gender</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>onlyOnline</cstring>
+ </property>
+ <property name="text">
+ <string>Only search for online contacts</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;ountry:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>country</cstring>
+ </property>
+ </widget>
+ <spacer row="5" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="2" rowspan="1" colspan="2">
+ <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>166</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </widget>
+ <widget class="KListView" row="1" column="0" rowspan="7" colspan="1">
+ <column>
+ <property name="text">
+ <string>UIN</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Nickname</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>Email</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Requires Authorization?</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>searchResults</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is where the results from your search are displayed. If you double-click a result, the search window will close and pass the UIN of the contact you wish to add back to the Add Contact Wizard. You can only add one contact at a time.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>searchButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Search</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Search the ICQ Whitepages with your search criteria</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>newSearchButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>New Search</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Clears both search fields and results</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>tabWidget3</tabstop>
+ <tabstop>uin</tabstop>
+ <tabstop>nickName</tabstop>
+ <tabstop>firstName</tabstop>
+ <tabstop>email</tabstop>
+ <tabstop>lastName</tabstop>
+ <tabstop>gender</tabstop>
+ <tabstop>city</tabstop>
+ <tabstop>language</tabstop>
+ <tabstop>country</tabstop>
+ <tabstop>onlyOnline</tabstop>
+ <tabstop>searchButton</tabstop>
+ <tabstop>stopButton</tabstop>
+ <tabstop>clearButton</tabstop>
+ <tabstop>addButton</tabstop>
+ <tabstop>userInfoButton</tabstop>
+ <tabstop>closeButton</tabstop>
+ <tabstop>searchResults</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp b/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp
new file mode 100644
index 00000000..0010166a
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqsearchdialog.cpp
@@ -0,0 +1,320 @@
+/*
+ Kopete Oscar Protocol
+ icqsearchdialog.cpp - search for people
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqsearchdialog.h"
+
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlayout.h>
+#include <qtextcodec.h>
+#include <qtabwidget.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+
+#include "kopeteuiglobal.h"
+
+#include "icqaccount.h"
+#include "icqaddcontactpage.h"
+#include "icqprotocol.h"
+#include "icqsearchbase.h"
+#include "oscartypes.h"
+#include "icqcontact.h"
+#include "icquserinfowidget.h"
+
+ICQSearchDialog::ICQSearchDialog( ICQAccount* account, QWidget* parent, const char* name )
+: KDialogBase( parent, name, true, i18n( "ICQ User Search" ), 0, NoDefault )
+{
+ m_account = account;
+ m_searchUI = new ICQSearchBase( this, name );
+ setMainWidget( m_searchUI );
+ connect( m_searchUI->searchButton, SIGNAL( clicked() ), this, SLOT( startSearch() ) );
+ connect( m_searchUI->searchResults, SIGNAL( selectionChanged() ), this, SLOT( resultSelectionChanged() ) );
+ connect( m_searchUI->addButton, SIGNAL( clicked() ), this, SLOT( addContact() ) );
+ connect( m_searchUI->clearButton, SIGNAL( clicked() ), this, SLOT( clearResults() ) );
+ connect( m_searchUI->stopButton, SIGNAL( clicked() ), this, SLOT( stopSearch() ) );
+ connect( m_searchUI->closeButton, SIGNAL( clicked() ), this, SLOT( closeDialog() ) );
+ connect( m_searchUI->userInfoButton, SIGNAL( clicked() ), this, SLOT( userInfo() ) );
+ connect( m_searchUI->newSearchButton, SIGNAL( clicked() ), this, SLOT( newSearch() ) );
+
+ ICQProtocol *p = ICQProtocol::protocol();
+ p->fillComboFromTable( m_searchUI->gender, p->genders() );
+ p->fillComboFromTable( m_searchUI->country, p->countries() );
+ p->fillComboFromTable( m_searchUI->language, p->languages() );
+
+ m_contact = NULL;
+ m_infoWidget = NULL;
+
+ m_contact = NULL;
+ m_infoWidget = NULL;
+}
+
+
+ICQSearchDialog::~ICQSearchDialog()
+{
+}
+
+void ICQSearchDialog::startSearch()
+{
+ // Doing the search only if the account is online, otherwise warn the user
+ if(!m_account->isConnected())
+ {
+ // Account currently offline
+ m_searchUI->searchButton->setEnabled( false );
+ KMessageBox::sorry( this, i18n("You must be online to search the ICQ Whitepages."), i18n("ICQ Plugin") );
+ }
+ else
+ {
+ // Account is online
+ clearResults();
+
+ m_searchUI->stopButton->setEnabled( true );
+ m_searchUI->searchButton->setEnabled( false );
+ m_searchUI->newSearchButton->setEnabled( false );
+
+ connect( m_account->engine(), SIGNAL( gotSearchResults( const ICQSearchResult& ) ),
+ this, SLOT( newResult( const ICQSearchResult& ) ) );
+ connect( m_account->engine(), SIGNAL( endOfSearch( int ) ),
+ this, SLOT( searchFinished( int ) ) );
+
+ const QWidget* currentPage = m_searchUI->tabWidget3->currentPage();
+
+ if ( currentPage == m_searchUI->tab )
+ {
+ if( m_searchUI->uin->text().isEmpty() || m_searchUI->uin->text().toULong() == 0 )
+ {
+ // Invalid UIN
+ stopSearch();
+ clearResults();
+ KMessageBox::sorry( this, i18n("You must enter a valid UIN."), i18n("ICQ Plugin") );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Search aborted: invalid UIN " << m_searchUI->uin->text() << endl;
+ }
+ else
+ {
+ //doing a uin search
+ m_account->engine()->uinSearch( m_searchUI->uin->text() );
+ }
+ }
+ else if ( currentPage == m_searchUI->tab_2 )
+ {
+ //create a ICQWPSearchInfo struct and send it
+ ICQProtocol* p = ICQProtocol::protocol();
+ ICQWPSearchInfo info;
+ QTextCodec* codec = m_account->defaultCodec();
+ info.firstName = codec->fromUnicode( m_searchUI->firstName->text() );
+ info.lastName = codec->fromUnicode( m_searchUI->lastName->text() );
+ info.nickName = codec->fromUnicode( m_searchUI->nickName->text() );
+ info.email = codec->fromUnicode( m_searchUI->email->text() );
+ info.city = codec->fromUnicode( m_searchUI->city->text() ); // City
+ info.gender = p->getCodeForCombo(m_searchUI->gender, p->genders()); // Gender
+ info.language = p->getCodeForCombo(m_searchUI->language, p->languages()); // Lang
+ info.country =p->getCodeForCombo(m_searchUI->country, p->countries()); // country code
+ info.onlineOnly = m_searchUI->onlyOnline->isChecked();
+
+ // Check if the user has actually entered things to search
+ if( info.firstName.isEmpty() &&
+ info.lastName.isEmpty() &&
+ info.nickName.isEmpty() &&
+ info.email.isEmpty() &&
+ info.city.isEmpty() &&
+ (info.gender == 0) &&
+ (info.language == 0) &&
+ (info.country == 0)
+ )
+ {
+ // All fields were blank
+ stopSearch();
+ clearResults();
+ KMessageBox::information(this, i18n("You must enter search criteria."), i18n("ICQ Plugin") );
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Search aborted: all fields were blank" << endl;
+ }
+ else
+ {
+ // Start the search
+ m_account->engine()->whitePagesSearch( info );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting whitepage search" << endl;
+ }
+ }
+ }
+}
+
+void ICQSearchDialog::stopSearch()
+{
+ disconnect( m_account->engine(), SIGNAL( gotSearchResults( const ICQSearchResult& ) ),
+ this, SLOT( newResult( const ICQSearchResult& ) ) );
+ disconnect( m_account->engine(), SIGNAL( endOfSearch( int ) ),
+ this, SLOT( searchFinished( int ) ) );
+
+ m_searchUI->stopButton->setEnabled( false );
+ m_searchUI->searchButton->setEnabled( true );
+ m_searchUI->newSearchButton->setEnabled( true );
+}
+
+void ICQSearchDialog::addContact()
+{
+ ICQAddContactPage* iacp = dynamic_cast<ICQAddContactPage*>( parent() );
+ if ( !iacp )
+ {
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "The ICQ ACP is not our parent!!" << endl;
+ }
+ else
+ {
+ QString uin = m_searchUI->searchResults->selectedItem()->text( 0 );
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Passing " << uin << " back to the ACP" << endl;
+ iacp->setUINFromSearch( uin );
+
+ // Closing the dialog
+ closeDialog();
+ }
+}
+
+void ICQSearchDialog::userInfo()
+{
+ // Lookup user info only if the account is online, otherwise warn the user
+ if(!m_account->isConnected())
+ {
+ // Account currently offline
+ KMessageBox::sorry( this, i18n("You must be online to display user info."), i18n("ICQ Plugin") );
+ }
+ else
+ {
+ // Account currently online
+ m_contact = new ICQContact( m_account,
+ m_searchUI->searchResults->selectedItem()->text( 0 ),
+ NULL);
+
+ m_infoWidget = new ICQUserInfoWidget( Kopete::UI::Global::mainWidget(), "icq info" );
+ QObject::connect( m_infoWidget, SIGNAL( finished() ), this, SLOT( closeUserInfo() ) );
+
+ m_infoWidget->setContact( m_contact );
+ m_infoWidget->setModal(true);
+ m_infoWidget->show();
+ if ( m_contact->account()->isConnected() )
+ m_account->engine()->requestFullInfo( m_contact->contactId() );
+ kdDebug(OSCAR_ICQ_DEBUG) << k_funcinfo << "Displaying user info" << endl;
+ }
+}
+
+void ICQSearchDialog::closeUserInfo()
+{
+ // Free the ICQUserInfoWidget
+ QObject::disconnect( this, 0, m_infoWidget, 0 );
+ m_infoWidget->delayedDestruct();
+ m_infoWidget = NULL;
+
+ // Free the ICQContact
+ delete m_contact;
+ m_contact = NULL;
+}
+
+void ICQSearchDialog::clearResults()
+{
+ stopSearch();
+ m_searchUI->searchResults->clear();
+ m_searchUI->addButton->setEnabled( false );
+ m_searchUI->userInfoButton->setEnabled( false );
+ m_searchUI->searchButton->setEnabled( true );
+}
+
+void ICQSearchDialog::closeDialog()
+{
+ stopSearch();
+ clearResults();
+ clearFields();
+
+ slotClose();
+}
+
+void ICQSearchDialog::resultSelectionChanged()
+{
+ if ( !m_searchUI->searchResults->selectedItem() )
+ {
+ m_searchUI->addButton->setEnabled( false );
+ m_searchUI->userInfoButton->setEnabled( false );
+ }
+ else
+ {
+ m_searchUI->addButton->setEnabled( true );
+ m_searchUI->userInfoButton->setEnabled( true );
+ }
+}
+
+void ICQSearchDialog::newResult( const ICQSearchResult& info )
+{
+ if ( info.uin == 1 )
+ {
+ //TODO update progress
+ return;
+ }
+
+ QTextCodec* codec = m_account->defaultCodec();
+
+ QListViewItem *item = new QListViewItem( m_searchUI->searchResults, QString::number( info.uin ),
+ codec->toUnicode( info.nickName ),
+ codec->toUnicode( info.firstName ),
+ codec->toUnicode( info.lastName ),
+ codec->toUnicode( info.email ),
+ info.auth ? i18n( "Yes" ) : i18n( "No" ) );
+
+ if ( !item )
+ return;
+
+ if ( info.online )
+ item->setPixmap( 0, SmallIcon( "icq_online" ) );
+ else
+ item->setPixmap( 0, SmallIcon( "icq_offline" ) );
+
+}
+
+void ICQSearchDialog::searchFinished( int numLeft )
+{
+ kdWarning(OSCAR_ICQ_DEBUG) << k_funcinfo << "There are " << numLeft << "contact left out of this search" << endl;
+ m_searchUI->stopButton->setEnabled( false );
+ m_searchUI->clearButton->setEnabled( true );
+ m_searchUI->searchButton->setEnabled( true );
+ m_searchUI->newSearchButton->setEnabled( true );
+}
+
+void ICQSearchDialog::clearFields()
+{
+ m_searchUI->uin->setText( QString::null );
+
+ m_searchUI->firstName->setText( QString::null );
+ m_searchUI->lastName->setText( QString::null );
+ m_searchUI->nickName->setText( QString::null );
+ m_searchUI->email->setText( QString::null );
+ m_searchUI->city->setText( QString::null );
+ m_searchUI->gender->setCurrentItem( 0 ); // Unspecified
+ m_searchUI->country->setCurrentItem( 0 );
+ m_searchUI->language->setCurrentItem( 0 );
+ m_searchUI->onlyOnline->setChecked( false );
+}
+
+void ICQSearchDialog::newSearch()
+{
+ clearResults();
+ clearFields();
+}
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
+
+#include "icqsearchdialog.moc"
diff --git a/kopete/protocols/oscar/icq/ui/icqsearchdialog.h b/kopete/protocols/oscar/icq/ui/icqsearchdialog.h
new file mode 100644
index 00000000..b14aa2a1
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqsearchdialog.h
@@ -0,0 +1,69 @@
+/*
+ Kopete Oscar Protocol
+ icqsearchdialog.h - search for people
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQSEARCHDIALOG_H
+#define ICQSEARCHDIALOG_H
+
+#include <kdialogbase.h>
+#include "icquserinfo.h"
+
+class ICQAccount;
+class ICQSearchBase;
+class ICQContact;
+class ICQUserInfoWidget;
+/**
+@author Kopete Developers
+*/
+class ICQSearchDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ ICQSearchDialog( ICQAccount* account, QWidget* parent = 0, const char* name = 0 );
+ ~ICQSearchDialog();
+
+private slots:
+ void startSearch();
+ void stopSearch();
+ void addContact();
+ void clearResults();
+ void closeDialog();
+ void userInfo();
+ void closeUserInfo();
+ void newSearch();
+
+ /// Enable/disable buttons when the selection changes
+ void resultSelectionChanged();
+
+ /// Add a search result to the listview
+ void newResult( const ICQSearchResult& info );
+
+ /// The search is finished
+ void searchFinished( int numLeft );
+
+private:
+ ICQAccount* m_account;
+ ICQSearchBase* m_searchUI;
+ ICQContact* m_contact;
+ ICQUserInfoWidget* m_infoWidget;
+
+ void clearFields();
+};
+
+#endif
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
diff --git a/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp b/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp
new file mode 100644
index 00000000..3830e05f
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icquserinfowidget.cpp
@@ -0,0 +1,190 @@
+/*
+ Kopete Oscar Protocol
+ icquserinfowidget.cpp - Display ICQ user info
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icquserinfowidget.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qobject.h>
+#include <qtextcodec.h>
+
+#include <kdatewidget.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kjanuswidget.h>
+#include <klocale.h>
+
+#include "icqgeneralinfo.h"
+#include "icqcontact.h"
+#include "icqprotocol.h"
+#include "icqworkinfowidget.h"
+#include "icqotherinfowidget.h"
+#include "icqinterestinfowidget.h"
+
+
+ICQUserInfoWidget::ICQUserInfoWidget( QWidget * parent, const char * name )
+: KDialogBase( KDialogBase::IconList, 0, parent, name, false, i18n( "ICQ User Information" ), Ok )
+{
+ kdDebug(14153) << k_funcinfo << "Creating new icq user info widget" << endl;
+
+ QFrame* genInfo = addPage( i18n( "General Info" ),
+ i18n( "General ICQ Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "identity" ), KIcon::Desktop ) );
+ QVBoxLayout* genLayout = new QVBoxLayout( genInfo );
+ m_genInfoWidget = new ICQGeneralInfoWidget( genInfo, "Basic Information" );
+ genLayout->addWidget( m_genInfoWidget );
+
+ QFrame* workInfo = addPage( i18n( "Work Info" ),
+ i18n( "Work Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "attach" ), KIcon::Desktop ) );
+ QVBoxLayout* workLayout = new QVBoxLayout( workInfo );
+ m_workInfoWidget = new ICQWorkInfoWidget( workInfo, "Work Information" );
+ workLayout->addWidget( m_workInfoWidget );
+
+ QFrame* otherInfo = addPage( i18n( "Other Info" ),
+ i18n( "Other ICQ Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "email" ), KIcon::Desktop ) );
+ QVBoxLayout* otherLayout = new QVBoxLayout( otherInfo );
+ m_otherInfoWidget = new ICQOtherInfoWidget( otherInfo, "Other Information" );
+ otherLayout->addWidget( m_otherInfoWidget );
+
+ QFrame* interestInfo = addPage( i18n( "Interest Info" ),
+ i18n( "Interest" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "email" ), KIcon::Desktop ) );
+ QVBoxLayout* interestLayout = new QVBoxLayout( interestInfo );
+ m_interestInfoWidget = new ICQInterestInfoWidget( interestInfo, "Other Information" );
+ interestLayout->addWidget( m_interestInfoWidget );
+
+}
+
+void ICQUserInfoWidget::setContact( ICQContact* contact )
+{
+ m_contact = contact;
+ QObject::connect( contact, SIGNAL( haveBasicInfo( const ICQGeneralUserInfo& ) ),
+ this, SLOT( fillBasicInfo( const ICQGeneralUserInfo& ) ) );
+ QObject::connect( contact, SIGNAL( haveWorkInfo( const ICQWorkUserInfo& ) ),
+ this, SLOT( fillWorkInfo( const ICQWorkUserInfo& ) ) );
+ QObject::connect( contact, SIGNAL( haveEmailInfo( const ICQEmailInfo& ) ),
+ this, SLOT( fillEmailInfo( const ICQEmailInfo& ) ) );
+ QObject::connect( contact, SIGNAL( haveMoreInfo( const ICQMoreUserInfo& ) ),
+ this, SLOT( fillMoreInfo( const ICQMoreUserInfo& ) ) );
+ QObject::connect( contact, SIGNAL( haveInterestInfo( const ICQInterestInfo& ) ),
+ this, SLOT( fillInterestInfo( const ICQInterestInfo& ) ) );
+}
+
+void ICQUserInfoWidget::fillBasicInfo( const ICQGeneralUserInfo& ui )
+{
+ QTextCodec* codec = m_contact->contactCodec();
+ m_genInfoWidget->uinEdit->setText( m_contact->contactId() );
+ m_genInfoWidget->nickNameEdit->setText( codec->toUnicode( ui.nickname ) );
+ m_genInfoWidget->fullNameEdit->setText( codec->toUnicode( ui.firstName ) + " " + codec->toUnicode( ui.lastName ) );
+ m_genInfoWidget->ipEdit->setText( m_contact->property( "ipAddress" ).value().toString() );
+ m_genInfoWidget->emailEdit->setText( codec->toUnicode( ui.email ) );
+ m_genInfoWidget->cityEdit->setText( codec->toUnicode( ui.city ) );
+ m_genInfoWidget->stateEdit->setText( codec->toUnicode( ui.state ) );
+ m_genInfoWidget->phoneEdit->setText( codec->toUnicode( ui.phoneNumber ) );
+ m_genInfoWidget->faxEdit->setText( codec->toUnicode( ui.faxNumber ) );
+ m_genInfoWidget->addressEdit->setText( codec->toUnicode( ui.address ) );
+ m_genInfoWidget->cellEdit->setText( codec->toUnicode( ui.cellNumber ) );
+ m_genInfoWidget->zipEdit->setText( codec->toUnicode( ui.zip ) );
+
+ QString country = static_cast<ICQProtocol*>( m_contact->protocol() )->countries()[ui.country];
+ m_genInfoWidget->countryEdit->setText( country );
+}
+
+void ICQUserInfoWidget::fillWorkInfo( const ICQWorkUserInfo& ui )
+{
+ QTextCodec* codec = m_contact->contactCodec();
+ m_workInfoWidget->cityEdit->setText( codec->toUnicode( ui.city ) );
+ m_workInfoWidget->stateEdit->setText( codec->toUnicode( ui.state ) );
+ m_workInfoWidget->phoneEdit->setText( codec->toUnicode( ui.phone ) );
+ m_workInfoWidget->faxEdit->setText( codec->toUnicode( ui.fax ) );
+ m_workInfoWidget->addressEdit->setText( codec->toUnicode( ui.address ) );
+ m_workInfoWidget->zipEdit->setText( codec->toUnicode( ui.zip ) );
+ m_workInfoWidget->companyEdit->setText( codec->toUnicode( ui.company ) );
+ m_workInfoWidget->departmentEdit->setText( codec->toUnicode( ui.department ) );
+ m_workInfoWidget->positionEdit->setText( codec->toUnicode( ui.position ) );
+ m_workInfoWidget->homepageEdit->setText( codec->toUnicode( ui.homepage ) );
+
+ ICQProtocol* p = static_cast<ICQProtocol*>( m_contact->protocol() );
+ QString country = p->countries()[ui.country];
+ m_workInfoWidget->countryEdit->setText( country );
+
+ //TODO handle the occupation
+}
+
+void ICQUserInfoWidget::fillEmailInfo( const ICQEmailInfo& )
+{
+}
+
+void ICQUserInfoWidget::fillInterestInfo( const ICQInterestInfo& info)
+{
+ QTextCodec* codec = m_contact->contactCodec();
+ if (info.count>0) {
+ QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[0]];
+ m_interestInfoWidget->topic1->setText( topic );
+ m_interestInfoWidget->desc1->setText( codec->toUnicode( info.descriptions[0] ) );
+ }
+ if (info.count>1) {
+ QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[1]];
+ m_interestInfoWidget->topic2->setText( topic );
+ m_interestInfoWidget->desc2->setText( codec->toUnicode( info.descriptions[1] ) );
+ }
+ if (info.count>2) {
+ QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[2]];
+ m_interestInfoWidget->topic3->setText( topic );
+ m_interestInfoWidget->desc3->setText( codec->toUnicode( info.descriptions[2] ) );
+ }
+ if (info.count>3) {
+ QString topic = static_cast<ICQProtocol*>( m_contact->protocol() )->interests()[info.topics[3]];
+ m_interestInfoWidget->topic4->setText( topic );
+ m_interestInfoWidget->desc4->setText( codec->toUnicode( info.descriptions[3] ) );
+ }
+}
+
+void ICQUserInfoWidget::fillMoreInfo( const ICQMoreUserInfo& ui )
+{
+ QTextCodec* codec = m_contact->contactCodec();
+ m_genInfoWidget->ageSpinBox->setValue( ui.age );
+ if ( ui.birthday.isValid() )
+ m_genInfoWidget->birthday->setText( KGlobal::locale()->formatDate( ui.birthday,true ) );
+
+ QString gender = static_cast<ICQProtocol*>( m_contact->protocol() )->genders()[ui.gender];
+ m_genInfoWidget->genderEdit->setText( gender );
+ m_genInfoWidget->homepageEdit->setText( codec->toUnicode( ui.homepage ) );
+
+ QString ms = static_cast<ICQProtocol*>( m_contact->protocol() )->maritals()[ui.marital];
+ m_genInfoWidget->marital->setText( ms );
+
+ m_genInfoWidget->oCityEdit->setText( codec->toUnicode( ui.ocity) );
+ m_genInfoWidget->oStateEdit->setText( codec->toUnicode( ui.ostate) );
+
+ QString ocountry = static_cast<ICQProtocol*>( m_contact->protocol() )->countries()[ui.ocountry];
+ m_genInfoWidget->oCountryEdit->setText( ocountry );
+
+ //TODO languages
+}
+
+
+#include "icquserinfowidget.moc"
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
+
diff --git a/kopete/protocols/oscar/icq/ui/icquserinfowidget.h b/kopete/protocols/oscar/icq/ui/icquserinfowidget.h
new file mode 100644
index 00000000..ef478e59
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icquserinfowidget.h
@@ -0,0 +1,58 @@
+/*
+ Kopete Oscar Protocol
+ icquserinfowidget.h - Display ICQ user info
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _ICQUSERINFOWIDGET_H_
+#define _ICQUSERINFOWIDGET_H_
+
+#include <kdialogbase.h>
+#include <icquserinfo.h>
+
+class KJanusWidget;
+class ICQGeneralInfoWidget;
+class ICQWorkInfoWidget;
+class ICQOtherInfoWidget;
+class ICQInterestInfoWidget;
+class ICQContact;
+
+class ICQUserInfoWidget : public KDialogBase
+{
+Q_OBJECT
+public:
+ ICQUserInfoWidget( QWidget* parent = 0, const char* name = 0 );
+ void setContact( ICQContact* contact );
+
+public slots:
+ void fillBasicInfo( const ICQGeneralUserInfo& );
+ void fillWorkInfo( const ICQWorkUserInfo& );
+ void fillEmailInfo( const ICQEmailInfo& );
+ void fillMoreInfo( const ICQMoreUserInfo& );
+ void fillInterestInfo( const ICQInterestInfo& );
+
+private:
+ ICQGeneralInfoWidget* m_genInfoWidget;
+ ICQWorkInfoWidget* m_workInfoWidget;
+ ICQOtherInfoWidget* m_otherInfoWidget;
+ ICQInterestInfoWidget * m_interestInfoWidget;
+ KJanusWidget* m_janusWidget;
+ ICQContact* m_contact;
+
+};
+
+#endif
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui b/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui
new file mode 100644
index 00000000..a31021ba
--- /dev/null
+++ b/kopete/protocols/oscar/icq/ui/icqworkinfowidget.ui
@@ -0,0 +1,249 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ICQWorkInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ICQWorkInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>328</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Personal Work Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Phone:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel11</cstring>
+ </property>
+ <property name="text">
+ <string>Fax:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="text">
+ <string>Department:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>departmentEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Position:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>positionEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>phoneEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>faxEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Company Location Information</string>
+ </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>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Homepage:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Address:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Zip:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>State:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>companyEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>homepageEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>addressEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>cityEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>stateEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>zipEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1">
+ <property name="name">
+ <cstring>countryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </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>70</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/icq/x-icq.desktop b/kopete/protocols/oscar/icq/x-icq.desktop
new file mode 100644
index 00000000..1d8b3eb2
--- /dev/null
+++ b/kopete/protocols/oscar/icq/x-icq.desktop
@@ -0,0 +1,60 @@
+[Desktop Entry]
+Comment=ICQ Contact
+Comment[ar]=جهة اتصال على ICQ
+Comment[be]=ICQ
+Comment[bg]=Връзка Ñ ICQ Contact
+Comment[bn]=আই-সি-কিউ যোগাযোগ
+Comment[br]=Darempred ICQ
+Comment[bs]=ICQ kontakt
+Comment[ca]=Contacte ICQ
+Comment[cs]=ICQ kontakt
+Comment[cy]=Cysylltiad ICQ
+Comment[da]=ICQ-Kontakt
+Comment[de]=ICQ-Kontakt
+Comment[el]=Επαφή ICQ
+Comment[eo]=ICQ-kontakto
+Comment[es]=Contacto de ICQ
+Comment[et]=ICQ kontakt
+Comment[eu]=ICQ kontaktua
+Comment[fa]=تماس ICQ
+Comment[fi]=ICQ-kontakti
+Comment[fr]=Contact ICQ
+Comment[gl]=Contacto ICQ
+Comment[he]=×יש-קשר ICQ
+Comment[hi]=आईसीकà¥à¤¯à¥‚ समà¥à¤ªà¤°à¥à¤•
+Comment[hr]=ICQ kontakt
+Comment[hu]=ICQ-kapcsolat
+Comment[is]=ICQ tengiliður
+Comment[it]=Contatto ICQ
+Comment[ja]=ICQ コンタクト
+Comment[ka]=ICQ მეგáƒáƒ‘áƒáƒ áƒ˜
+Comment[kk]=ICQ байланыÑ
+Comment[km]=ទំនាក់​ទំនង​ ICQ
+Comment[lt]=ICQ kontaktas
+Comment[mk]=Контакт на ICQ
+Comment[nb]=ICQ kontakt
+Comment[nds]=ICQ-Kontakt
+Comment[ne]=आईसीकà¥à¤¯à¥‚ समà¥à¤ªà¤°à¥à¤•
+Comment[nl]=ICQ contact
+Comment[nn]=ICQ-kontakt
+Comment[pl]=Kontakt ICQ
+Comment[pt]=Contacto de ICQ
+Comment[pt_BR]=Contato ICQ
+Comment[ru]=Контакт ICQ
+Comment[se]=ICQ-oktavuohta
+Comment[sk]=Kontakt ICQ
+Comment[sl]=Stik ICQ
+Comment[sr]=ICQ контакт
+Comment[sr@Latn]=ICQ kontakt
+Comment[sv]=ICQ-kontakt
+Comment[ta]=ICQ தொடரà¯à®ªà¯
+Comment[tg]=ПайваÑтшавии ICQ
+Comment[tr]=ICQ Bağlantısı
+Comment[uk]=Контакт ICQ
+Comment[zh_CN]=ICQ è”系人
+Comment[zh_HK]=ICQ è¯çµ¡äºº
+Comment[zh_TW]=ICQ è¯çµ¡äºº
+Type=MimeType
+MimeType=application/x-icq
+Patterns=*.uin;*.icq
+Icon=licq
diff --git a/kopete/protocols/oscar/liboscar/DESIGN b/kopete/protocols/oscar/liboscar/DESIGN
new file mode 100644
index 00000000..3f772e22
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/DESIGN
@@ -0,0 +1,12 @@
+This file attempts to detail the design of the liboscar library. It's still a
+work in progress.
+
+liboscar is based off of the libgroupwise library which handles connections to
+Novell's Groupwise messenging system. libgroupwise is based off of the libiris
+library which is used to interface with the jabber instant messaging network.
+
+Details of the library:
+============================================
+
+All the protocol actions are encapsulated in Tasks.
+
diff --git a/kopete/protocols/oscar/liboscar/HACKING b/kopete/protocols/oscar/liboscar/HACKING
new file mode 100644
index 00000000..9bd25476
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/HACKING
@@ -0,0 +1,194 @@
+This is the oscar HACKING file. It details the current coding style that is being
+used in this plugin. Thanks to Scott Wheeler for providing the skeleton I based this
+file on
+
+================================================================================
+Code Documentation
+================================================================================
+
+Please add doxygen comments to the header files where appropriate. I don't expect
+anyone to add comments for functions that they're overriding from the base class
+but comments everywhere would be good.
+
+Please comment parts of the code that might be unclear, need more thinking about,
+reimplementing, etc. It will help people look for things to do if they want to help
+out.
+
+Please don't remove the kdDebug lines from any of the source files. If they're
+excessive, either wrap them in an ifdef and put the ifdef in the soon to be
+created oscardebug.h file so that they can be enabled and disabled at the will of
+other developers or users. I also tend to use kdDebug statements to document
+my code in the place of comments for the simpler sections.
+
+================================================================================
+Indentation
+================================================================================
+
+I use tabs to indent everything. When I say tabs, I mean the 'tab' character. Please
+don't use 8 spaces to indent. Just hit the 'tab' key, and make sure that space indentation
+is turned off in whatever editor you use. However, the exception to the indentation
+rule is anything that's inside of a namespace block should not be indented.
+
+
+static void foo()
+{
+ if ( bar() ) <-- 1 tab
+ baz(); <-- 2 tabs
+}
+
+namespace
+{
+class Foo
+{
+Q_OBJECT
+public:
+ Foo();
+ ~Foo();
+};
+}
+
+
+
+
+vim or kate modelines that modify the way tabs are displayed are encouraged, as
+long as they don't actually change the way tabs are saved to a file.
+
+================================================================================
+Braces
+================================================================================
+
+Braces opening classes, structs, namespaces, functions, and conditionals should be
+on their own line. Here's an example:
+
+class Foo
+{
+ // stuff
+};
+
+if ( foo == bar )
+{
+ // stuff
+}
+
+while ( foo == bar &&
+ baz == quux &&
+ flop == pop )
+{
+ // stuff
+}
+
+static void foo()
+{
+ // stuff
+}
+
+Also conditionals / loops that only contiain one line in their body (but where
+the conditional statement fits onto one line) should omit braces:
+
+if ( foo == bar )
+ baz();
+
+But:
+
+if ( baz == quux &&
+ ralf == spot )
+{
+ bar();
+}
+
+================================================================================
+Spaces
+================================================================================
+
+Spaces should be used between the conditional / loop type and the
+conditional statement. They should also not be used after parenthesis. However
+the should be to mark of mathematical or comparative operators.
+
+if ( foo == bar )
+ ^ ^ ^
+
+is correct. However:
+
+if(foo == bar)
+
+is not.
+
+================================================================================
+Header Organization
+================================================================================
+
+Member variables should always be private and prefixed with "m_". Accessors may
+not be inline in the headers. The organization of the members in a class should be
+roughly as follows:
+
+public:
+public slots:
+protected:
+protected slots:
+signals:
+private: // member funtions
+private slots:
+private: // member variables
+
+If there are no private slots there is no need for two private sections, however
+private functions and private variables should be clearly separated.
+
+The implementations files -- .cpp files -- should follow (when possible) the
+same order of function declarations as the header files.
+
+Virtual functions should always be marked as such even in derived classes where
+it is not strictly necessary.
+
+================================================================================
+Whitespace
+================================================================================
+
+Whitespace should be used liberally. When blocks of code are logically distinct
+I tend to put a blank line between them. This is difficult to explain
+systematically but after looking a bit at the current code organization this
+ideally will be somewhat clear.
+
+Parenthesis should be padded by spaces on one side. This is easier to illustrate in
+an example:
+
+void Client::foo() //correct
+void Client::foo3( int, int, int ) //correct
+
+void Client::foo(int, int, int) //incorrect
+void Client::foo(int,int,int) //also incorrect
+
+Operators should be padded by spaces in conditionals. Again, more examples to
+illustrate
+
+if (foo==bar)
+m+=(n*2)-3;
+
+should be:
+
+if ( foo == bar )
+m += ( n * 2 ) - 3;
+
+================================================================================
+Pointer and Reference Operators
+================================================================================
+
+This one is pretty simple. I prefer "Foo* f" to "Foo *f" in function signatures
+and declarations. The same goes for "Foo& f" over "Foo &f".
+
+================================================================================
+
+There are likely things missing here and I'll try to add them over time as I
+notice things that are often missed. Please let me know if specific points are
+ambiguous.
+
+Also, please note that since this library is based heavily off of Kopete's
+libgroupwise library that the coding style in certain files may not match what's
+written in this document. Those files that don't match will be corrected eventually.
+
+To make things easier on you, kate modelines are provided at the end of certain files
+to help enforce the coding style. If you're using the new C S&S Indenter that will be in
+KDE 3.4, I can provide a patch that will automatically implement the space padding around
+parenthesis. Please mail me so I can send it to you.
+
+Matt Rogers <mattr@kde.org>
+
diff --git a/kopete/protocols/oscar/liboscar/Makefile.am b/kopete/protocols/oscar/liboscar/Makefile.am
new file mode 100644
index 00000000..ea757b69
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/Makefile.am
@@ -0,0 +1,26 @@
+
+METASOURCES = AUTO
+noinst_LTLIBRARIES = liboscar.la
+
+AM_CPPFLAGS = -I$(top_srcdir)/kopete/libkopete $(all_includes)
+
+
+liboscar_la_SOURCES = oscarutils.cpp client.cpp task.cpp connector.cpp \
+ inputprotocolbase.cpp coreprotocol.cpp flapprotocol.cpp snacprotocol.cpp transfer.cpp rtf.cc \
+ bytestream.cpp oscarclientstream.cpp safedelete.cpp stream.cpp oscarconnector.cpp \
+ oscarbytestream.cpp buffer.cpp md5.c logintask.cpp aimlogintask.cpp icqlogintask.cpp \
+ closeconnectiontask.cpp rateclassmanager.cpp serverversionstask.cpp rateinfotask.cpp \
+ errortask.cpp locationrightstask.cpp profiletask.cpp blmlimitstask.cpp \
+ servicesetuptask.cpp icbmparamstask.cpp ssimanager.cpp rateclass.cpp rateclass.h \
+ prmparamstask.cpp ssiparamstask.cpp ssilisttask.cpp ssiactivatetask.cpp \
+ clientreadytask.cpp senddcinfotask.cpp sendidletimetask.cpp ownuserinfotask.cpp \
+ connection.cpp onlinenotifiertask.cpp userdetails.cpp ssimodifytask.cpp \
+ oscartypeclasses.cpp oscarmessage.cpp messagereceivertask.cpp sendmessagetask.cpp icqtask.cpp \
+ offlinemessagestask.cpp ssiauthtask.cpp userinfotask.cpp icquserinfo.cpp icquserinfotask.cpp \
+ usersearchtask.cpp warningtask.cpp changevisibilitytask.cpp typingnotifytask.cpp \
+ buddyicontask.cpp serverredirecttask.cpp oscarsettings.cpp \
+ chatnavservicetask.cpp connectionhandler.cpp chatservicetask.cpp
+
+liboscar_la_LDFLAGS = -no-undefined $(all_libraries)
+liboscar_la_LIBADD = $(LIB_QT) $(LIB_KDECORE)
+
diff --git a/kopete/protocols/oscar/liboscar/TODO b/kopete/protocols/oscar/liboscar/TODO
new file mode 100644
index 00000000..1ec9be98
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/TODO
@@ -0,0 +1,37 @@
+This is the TODO file for liboscar. Please note that this TODO file is on a
+very short timeframe since the goal is to have liboscar done before KDE 3.4.
+Realistically, KDE 4 is a better goal, but i want to push hard for KDE 3.4
+
+If you're going to be looking at the docs, I suggest downloading the zip file (click the download link) from iserverd.khstu.ru/oscar for
+faster loading.
+
+Misc. Before Merge things
+====================================
+
+- Don't hardcode the values in SendDCInfoTask. Find a way to get them from the account or something.
+- Rename SendDCInfoTask to SendExtInfoTask (rename the files on the server too. contact sysadmin@kde.org to see about this. It may have to wait until the merge)
+- Check capabilities handling (the code is from oscarsocket, we need to make sure it will still work ok for liboscar until we come up with something better)
+- Test moving contacts from one group to another
+
+
+Direct Connections
+====================================
+When/If we get around to it. Matt knows absolutely nothing about direct connections and the only online source of documentation is no longer online. :(
+This will definately be one of those things we have to dissect gaim for. :/
+
+
+SNAC 0x15 parsing
+====================================
+
+SNAC 0x15 parsing is done. however parts may need to be reworked as things have gotten
+very messy. we currently don't do a good job of handling extra data (i.e. i can't call
+addInitialData with just the initial data and get the type 1 tlv length right. maybe a
+prepareSend( const Buffer& ) function that adds the type one tlv to our packet so we
+get the tlv length right.
+
+also, we may want to implement a removeInitialData function that we can call if the packet
+is for us so we don't have to have code in all the icq tasks that get rid of the initial tlv
+data that we parse in parse initial data.
+
+
+
diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.cpp b/kopete/protocols/oscar/liboscar/aimlogintask.cpp
new file mode 100644
index 00000000..69a9c770
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/aimlogintask.cpp
@@ -0,0 +1,254 @@
+/*
+ Kopete Oscar Protocol
+ aimlogintask.h - Handles logging into to the AIM service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "aimlogintask.h"
+
+#include <stdlib.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include "connection.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+#include "md5.h"
+
+using namespace Oscar;
+
+AimLoginTask::AimLoginTask( Task* parent )
+ : Task ( parent )
+{
+}
+
+AimLoginTask::~AimLoginTask()
+{
+}
+
+void AimLoginTask::onGo()
+{
+ //send Snac 17,06
+ sendAuthStringRequest();
+ //when we have the authKey, login
+ connect( this, SIGNAL( haveAuthKey() ), this, SLOT( sendLoginRequest() ) );
+}
+
+bool AimLoginTask::forMe( Transfer* transfer ) const
+{
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st && st->snacService() == 0x17 )
+ {
+ WORD subtype = st->snacSubtype();
+ switch ( subtype )
+ {
+ case 0x0002:
+ case 0x0003:
+ case 0x0006:
+ case 0x0007:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+ return false;
+}
+
+const QByteArray& AimLoginTask::cookie() const
+{
+ return m_cookie;
+}
+
+const QString& AimLoginTask::bosHost() const
+{
+ return m_bosHost;
+}
+
+const QString& AimLoginTask::bosPort() const
+{
+ return m_bosPort;
+}
+
+bool AimLoginTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if (!st)
+ return false;
+
+ WORD subtype = st->snacSubtype();
+ switch ( subtype )
+ {
+ case 0x0003:
+ setTransfer( transfer );
+ handleLoginResponse();
+ setTransfer( 0 );
+ return true;
+ break;
+ case 0x0007:
+ setTransfer( transfer );
+ processAuthStringReply();
+ setTransfer( 0 );
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ return false;
+ }
+ return false;
+}
+
+void AimLoginTask::sendAuthStringRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo
+ << "SEND CLI_AUTH_REQUEST, sending login request" << endl;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0017, 0x0006, 0x0000, client()->snacSequence() };
+
+ Buffer* outbuf = new Buffer;
+ outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1() );
+ outbuf->addDWord(0x004B0000); // empty TLV 0x004B
+ outbuf->addDWord(0x005A0000); // empty TLV 0x005A
+
+ Transfer* st = createTransfer( f, s, outbuf );
+ send( st );
+}
+
+void AimLoginTask::processAuthStringReply()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got the authorization key" << endl;
+ Buffer *inbuf = transfer()->buffer();
+ WORD keylen = inbuf->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Key length is " << keylen << endl;
+ m_authKey.duplicate( inbuf->getBlock(keylen) );
+ emit haveAuthKey();
+}
+
+void AimLoginTask::sendLoginRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_MD5_LOGIN) sending AIM login" << endl;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0017, 0x0002, 0x0000, client()->snacSequence() };
+ Buffer *outbuf = new Buffer;
+ const Oscar::ClientVersion* version = client()->version();
+
+ outbuf->addTLV(0x0001, client()->userId().length(), client()->userId().latin1());
+
+ QByteArray digest( 17 ); //apparently MD5 digests are 16 bytes long
+ encodePassword( digest );
+ digest[16] = '\0'; //do this so that addTLV sees a NULL-terminator
+
+ outbuf->addTLV(0x0025, 16, digest);
+ outbuf->addTLV(0x0003, version->clientString.length(), version->clientString.latin1() );
+ outbuf->addTLV16(0x0016, version->clientId );
+ outbuf->addTLV16(0x0017, version->major );
+ outbuf->addTLV16(0x0018, version->minor );
+ outbuf->addTLV16(0x0019, version->point );
+ outbuf->addTLV16(0x001a, version->build );
+ outbuf->addDWord(0x00140004); //TLV type 0x0014, length 0x0004
+ outbuf->addDWord( version->other ); //TLV data for type 0x0014
+ outbuf->addTLV(0x000f, version->lang.length(), version->lang.latin1() );
+ outbuf->addTLV(0x000e, version->country.length(), version->country.latin1() );
+
+ //if set, old-style buddy lists will not work... you will need to use SSI
+ outbuf->addTLV8(0x004a,0x01);
+
+ Transfer *st = createTransfer( f, s, outbuf );
+ send( st );
+}
+
+void AimLoginTask::handleLoginResponse()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV SNAC 0x17, 0x07 - AIM Login Response" << endl;
+
+ SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer() );
+
+ if ( !st )
+ {
+ setError( -1 , QString::null );
+ return;
+ }
+
+ QValueList<TLV> tlvList = st->buffer()->getTLVList();
+
+ TLV uin = findTLV( tlvList, 0x0001 );
+ if ( uin )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [SN], SN=" << QString( uin.data ) << endl;
+ }
+
+ TLV err = findTLV( tlvList, 0x0008 );
+
+ if ( err )
+ {
+ WORD errorNum = ( ( err.data[0] << 8 ) | err.data[1] );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << k_funcinfo << "found TLV(8) [ERROR] error= " <<
+ errorNum << endl;
+ Oscar::SNAC s = { 0, 0, 0, 0 };
+ client()->fatalTaskError( s, errorNum );
+ setError( errorNum, QString::null );
+ return; //if there's an error, we'll need to disconnect anyways
+ }
+
+ TLV server = findTLV( tlvList, 0x0005 );
+ if ( server )
+ {
+ QString ip = QString( server.data );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << ip << endl;
+ int index = ip.find( ':' );
+ m_bosHost = ip.left( index );
+ ip.remove( 0 , index+1 ); //get rid of the colon and everything before it
+ m_bosPort = ip.left(4); //we only need 4 bytes
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "We should reconnect to server '" << m_bosHost <<
+ "' on port " << m_bosPort << endl;
+ }
+
+ TLV cookie = findTLV( tlvList, 0x0006 );
+ if ( cookie )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl;
+ m_cookie.duplicate( cookie.data );
+ setSuccess( 0, QString::null );
+ }
+ tlvList.clear();
+}
+
+void AimLoginTask::encodePassword( QByteArray& digest ) const
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ md5_state_t state;
+ md5_init( &state );
+ md5_append( &state, ( const md5_byte_t* ) m_authKey.data(), m_authKey.size() );
+ md5_append( &state, ( const md5_byte_t* ) client()->password().latin1(), client()->password().length() );
+ md5_append( &state, ( const md5_byte_t* ) AIM_MD5_STRING, strlen( AIM_MD5_STRING ) );
+ md5_finish( &state, ( md5_byte_t* ) digest.data() );
+}
+
+//kate: indent-mode csands; tab-width 4;
+
+#include "aimlogintask.moc"
diff --git a/kopete/protocols/oscar/liboscar/aimlogintask.h b/kopete/protocols/oscar/liboscar/aimlogintask.h
new file mode 100644
index 00000000..66308178
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/aimlogintask.h
@@ -0,0 +1,82 @@
+/*
+ Kopete Oscar Protocol
+ aimlogintask.h - Handles logging into to the AIM service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCAR_AIMLOGINTASK_H_
+#define _OSCAR_AIMLOGINTASK_H_
+
+#include "task.h"
+
+using namespace Oscar;
+
+class AimLoginTask : public Task
+{
+Q_OBJECT
+public:
+ AimLoginTask( Task* parent );
+ ~AimLoginTask();
+ bool take( Transfer* transfer );
+ virtual void onGo();
+
+ //Protocol specific stuff
+ const QByteArray& cookie() const;
+ const QString& bosHost() const;
+ const QString& bosPort() const;
+
+protected:
+ bool forMe( Transfer* transfer ) const;
+
+signals:
+ void haveAuthKey();
+
+private:
+ //! Encodes a password using MD5
+ void encodePassword( QByteArray& digest ) const;
+
+ //! Send SNAC 0x17, 0x06
+ void sendAuthStringRequest();
+
+ //! Handle SNAC 0x17, 0x07
+ void processAuthStringReply();
+
+ //! Handle SNAC 0x17, 0x03
+ void handleLoginResponse();
+
+ //! Parse the error codes to generate a reason why sign-on failed
+ //Massive code duplication with CloseConnectionTask
+ bool parseDisconnectCode( int error, QString& reason );
+
+private slots:
+ //! Send SNAC 0x17, 0x02
+ void sendLoginRequest();
+
+private:
+ //! The authorization key to use when encoding the password
+ QByteArray m_authKey;
+
+ //! The all important connection cookie
+ QByteArray m_cookie;
+
+ //! The new BOS Host
+ QString m_bosHost;
+
+ //! The new BOS Port
+ QString m_bosPort;
+
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.cpp b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp
new file mode 100644
index 00000000..c3fe7e6e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/blmlimitstask.cpp
@@ -0,0 +1,94 @@
+/*
+ Kopete Oscar Protocol
+ blmlimitstask - Get the BLM service limits
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "blmlimitstask.h"
+#include <kdebug.h>
+#include "connection.h"
+#include "transfer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+
+BLMLimitsTask::BLMLimitsTask( Task* parent )
+ : Task( parent )
+{
+}
+
+
+BLMLimitsTask::~BLMLimitsTask()
+{
+}
+
+
+bool BLMLimitsTask::forMe(const Transfer* transfer) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st->snacService() == 3 && st->snacSubtype() == 3 )
+ return true;
+ else
+ return false;
+}
+
+bool BLMLimitsTask::take(Transfer* transfer)
+{
+ if ( forMe( transfer ) )
+ {
+ Buffer* buffer = transfer->buffer();
+ while ( buffer->length() != 0 )
+ {
+ TLV t = buffer->getTLV();
+ switch ( t.type )
+ {
+ case 0x0001:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Max BLM entries: "
+ << t.data << endl;
+ break;
+ case 0x0002:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max watcher entries: "
+ << t.data << endl;
+ break;
+ case 0x0003:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Max online notifications(?): "
+ << t.data << endl;
+ break;
+ }
+ }
+ setSuccess( 0, QString::null );
+ return true;
+ }
+ else
+ return false;
+}
+
+void BLMLimitsTask::onGo()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending BLM limits request" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0003, 0x0002, 0x0000, client()->snacSequence() };
+
+ Buffer* buffer = new Buffer();
+ buffer->addTLV16( 0x0005, 0x0003 );
+
+ Transfer *t = createTransfer( f, s, buffer );
+ send( t );
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/blmlimitstask.h b/kopete/protocols/oscar/liboscar/blmlimitstask.h
new file mode 100644
index 00000000..7ded03a7
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/blmlimitstask.h
@@ -0,0 +1,43 @@
+/*
+ Kopete Oscar Protocol
+ blmlimitstask.h - Fetch the limits for the BLM service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef BLMLIMITSTASK_H
+#define BLMLIMITSTASK_H
+
+#include "task.h"
+
+/**
+Fetch the limits for the BLM service
+
+@author Matt Rogers
+*/
+class BLMLimitsTask : public Task
+{
+public:
+ BLMLimitsTask( Task* parent );
+
+ ~BLMLimitsTask();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.cpp b/kopete/protocols/oscar/liboscar/buddyicontask.cpp
new file mode 100644
index 00000000..b2a35b1d
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/buddyicontask.cpp
@@ -0,0 +1,245 @@
+// buddyicontask.cpp
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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 "buddyicontask.h"
+
+#include <qstring.h>
+#include <kdebug.h>
+#include "buffer.h"
+#include "connection.h"
+#include "transfer.h"
+#include "oscarutils.h"
+#include <typeinfo>
+
+BuddyIconTask::BuddyIconTask( Task* parent )
+ :Task( parent )
+{
+ m_seq = 0;
+ m_refNum = -1;
+ m_iconLength = 0;
+ m_hashType = 0;
+}
+
+void BuddyIconTask::uploadIcon( WORD length, const QByteArray& data )
+{
+ m_iconLength = length;
+ m_icon = data;
+ m_action = Send;
+}
+
+void BuddyIconTask::requestIconFor( const QString& user )
+{
+ m_user = user;
+ m_action = Receive;
+}
+
+void BuddyIconTask::setHash( const QByteArray& md5Hash )
+{
+ m_hash = md5Hash;
+}
+
+void BuddyIconTask::setHashType( BYTE type )
+{
+ m_hashType = type;
+}
+
+void BuddyIconTask::onGo()
+{
+ if ( m_action == Send && m_icon.count() == 0 )
+ return;
+
+ if ( m_action == Receive && ( m_user.isEmpty() || m_hash.count() == 0 ) )
+ return;
+
+ if ( m_action == Receive )
+ {
+ if ( client()->isIcq() )
+ sendICQBuddyIconRequest();
+ else
+ sendAIMBuddyIconRequest();
+ }
+ else
+ sendIcon();
+}
+
+bool BuddyIconTask::forMe( const Transfer* transfer )
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacRequest() != m_seq )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sequences don't match" << endl;
+ return false;
+ }
+
+ if ( st->snacService() == 0x0010 )
+ {
+ switch( st->snacSubtype() )
+ {
+ case 0x0003:
+ case 0x0005:
+ case 0x0007:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool BuddyIconTask::take( Transfer* transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ setTransfer( transfer );
+ if ( st->snacSubtype() == 0x0003 )
+ handleUploadResponse();
+ else if ( st->snacSubtype() == 0x0005 )
+ handleAIMBuddyIconResponse();
+ else
+ handleICQBuddyIconResponse();
+
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return true;
+}
+
+void BuddyIconTask::sendIcon()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << "icon length: " << m_iconLength << endl;
+ FLAP f = { 0x02, 0, 0 };
+ m_seq = client()->snacSequence();
+ SNAC s = { 0x0010, 0x0002, 0x0000, m_seq };
+ Buffer* b = new Buffer;
+ b->addWord( 1 ); //gaim hard codes it, so will we
+ b->addWord( m_iconLength );
+ b->addString( m_icon );
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+void BuddyIconTask::handleUploadResponse()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server acked icon upload" << endl;
+ Buffer* b = transfer()->buffer();
+ b->skipBytes( 4 );
+ BYTE iconHashSize = b->getByte();
+ QByteArray hash( b->getBlock( iconHashSize ) );
+ //check the hash
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "hash " << hash << endl;
+ setSuccess( 0, QString::null );
+}
+
+
+void BuddyIconTask::sendAIMBuddyIconRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl;
+ FLAP f = { 0x02, 0, 0 };
+ m_seq = client()->snacSequence();
+ SNAC s = { 0x0010, 0x0004, 0x0000, m_seq };
+ Buffer* b = new Buffer;
+
+ b->addBUIN( m_user.latin1() ); //TODO: check encoding
+ b->addByte( 0x01 );
+ b->addWord( 0x0001 );
+ b->addByte( m_hashType );
+ b->addByte( m_hash.size() ); //MD5 Hash Size
+ b->addString( m_hash, m_hash.size() ); //MD5 Hash
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+void BuddyIconTask::handleAIMBuddyIconResponse()
+{
+ Buffer* b = transfer()->buffer();
+ QString user = b->getBUIN();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl;
+ b->skipBytes(2); //unknown field. not used
+ BYTE iconType = b->getByte();
+ Q_UNUSED( iconType );
+ BYTE hashSize = b->getByte();
+ QByteArray iconHash;
+ iconHash.duplicate( b->getBlock(hashSize) );
+ WORD iconSize = b->getWord();
+ QByteArray icon;
+ icon.duplicate( b->getBlock(iconSize) );
+ emit haveIcon( user, icon );
+}
+
+void BuddyIconTask::sendICQBuddyIconRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting buddy icon for " << m_user << endl;
+ FLAP f = { 0x02, 0, 0 };
+ m_seq = client()->snacSequence();
+ SNAC s = { 0x0010, 0x0006, 0x0000, m_seq };
+ Buffer* b = new Buffer;
+
+ b->addBUIN( m_user.latin1() ); //TODO: check encoding
+ b->addByte( 0x01 );
+ b->addWord( 0x0001 );
+ b->addByte( m_hashType );
+ b->addByte( m_hash.size() ); //MD5 Hash Size
+ b->addString( m_hash, m_hash.size() ); //MD5 Hash
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+void BuddyIconTask::handleICQBuddyIconResponse()
+{
+ Buffer* b = transfer()->buffer();
+ QString user = b->getBUIN();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Receiving buddy icon for " << user << endl;
+
+ b->skipBytes(2); //not used
+ BYTE iconType = b->getByte();
+ Q_UNUSED( iconType );
+
+ BYTE hashSize = b->getByte();
+ QByteArray iconHash;
+ iconHash.duplicate( b->getBlock(hashSize) );
+
+ b->skipBytes(1); //not used
+ b->skipBytes(2); //not used
+ BYTE iconType2 = b->getByte();
+ Q_UNUSED( iconType2 );
+
+ BYTE hashSize2 = b->getByte();
+ QByteArray iconHash2;
+ iconHash2.duplicate( b->getBlock(hashSize2) );
+
+ WORD iconSize = b->getWord();
+ QByteArray icon;
+ icon.duplicate( b->getBlock(iconSize) );
+
+ emit haveIcon( user, icon );
+}
+
+#include "buddyicontask.moc"
+
+
diff --git a/kopete/protocols/oscar/liboscar/buddyicontask.h b/kopete/protocols/oscar/liboscar/buddyicontask.h
new file mode 100644
index 00000000..af7931f0
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/buddyicontask.h
@@ -0,0 +1,69 @@
+// buddyicontask.h
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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 fdeven 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 BUDDYICONTASK_H
+#define BUDDYICONTASK_H
+
+#include "task.h"
+#include <qcstring.h>
+
+class Transfer;
+
+class BuddyIconTask : public Task
+{
+Q_OBJECT
+public:
+ BuddyIconTask( Task* parent );
+
+ void uploadIcon( WORD length, const QByteArray& data );
+ void setReferenceNum( WORD num );
+
+ void requestIconFor( const QString& user );
+ void setHash( const QByteArray& md5Hash );
+ void setHashType( BYTE type );
+
+ //! Task implementation
+ void onGo();
+ bool forMe( const Transfer* transfer );
+ bool take( Transfer* transfer );
+
+signals:
+ void haveIcon( const QString&, QByteArray );
+
+private:
+ void sendIcon();
+ void handleUploadResponse();
+ void sendAIMBuddyIconRequest();
+ void handleAIMBuddyIconResponse();
+ void sendICQBuddyIconRequest();
+ void handleICQBuddyIconResponse();
+
+private:
+ enum Action { Send, Receive };
+ Action m_action;
+ WORD m_iconLength;
+ int m_refNum;
+ QByteArray m_icon;
+ QString m_user;
+ QByteArray m_hash;
+ BYTE m_hashType;
+ DWORD m_seq;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/buffer.cpp b/kopete/protocols/oscar/liboscar/buffer.cpp
new file mode 100644
index 00000000..b04587e7
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/buffer.cpp
@@ -0,0 +1,519 @@
+/***************************************************************************
+ buffer.cpp - description
+ -------------------
+ begin : Thu Jun 6 2002
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include "buffer.h"
+
+#include <ctype.h>
+
+Buffer::Buffer()
+{
+ mReadPos=0;
+}
+
+Buffer::Buffer( const Buffer& other )
+{
+ mBuffer.duplicate( other.mBuffer );
+ mReadPos = other.mReadPos;
+}
+
+Buffer::Buffer(const char *b, Q_ULONG len)
+{
+ mBuffer.duplicate(b, len);
+ mReadPos=0;
+}
+
+Buffer::Buffer( const QByteArray& data )
+{
+ mBuffer.duplicate( data );
+ mReadPos = 0;
+}
+
+
+Buffer::~Buffer()
+{
+}
+
+
+int Buffer::addByte(const BYTE b)
+{
+ expandBuffer(1);
+ mBuffer[mBuffer.size()-1] = b;
+
+ return mBuffer.size();
+}
+
+int Buffer::addLEByte(const BYTE b)
+{
+ expandBuffer(1);
+ mBuffer[mBuffer.size()-1] = ((b) & 0xff);
+
+ return mBuffer.size();
+}
+
+
+int Buffer::addWord(const WORD w)
+{
+ expandBuffer(2);
+ mBuffer[mBuffer.size()-2] = ((w & 0xff00) >> 8);
+ mBuffer[mBuffer.size()-1] = (w & 0x00ff);
+
+ return mBuffer.size();
+}
+
+int Buffer::addLEWord(const WORD w)
+{
+ expandBuffer(2);
+ mBuffer[mBuffer.size()-2] = (unsigned char) ((w >> 0) & 0xff);
+ mBuffer[mBuffer.size()-1] = (unsigned char) ((w >> 8) & 0xff);
+
+ return mBuffer.size();
+}
+
+
+int Buffer::addDWord(const DWORD dw)
+{
+ expandBuffer(4);
+ mBuffer[mBuffer.size()-4] = (dw & 0xff000000) >> 24;
+ mBuffer[mBuffer.size()-3] = (dw & 0x00ff0000) >> 16;
+ mBuffer[mBuffer.size()-2] = (dw & 0x0000ff00) >> 8;
+ mBuffer[mBuffer.size()-1] = (dw & 0x000000ff);
+
+ return mBuffer.size();
+}
+
+int Buffer::addLEDWord(const DWORD dw)
+{
+ expandBuffer(4);
+ mBuffer[mBuffer.size()-4] = (unsigned char) ((dw >> 0) & 0xff);
+ mBuffer[mBuffer.size()-3] = (unsigned char) ((dw >> 8) & 0xff);
+ mBuffer[mBuffer.size()-2] = (unsigned char) ((dw >> 16) & 0xff);
+ mBuffer[mBuffer.size()-1] = (unsigned char) ((dw >> 24) & 0xff);
+
+ return mBuffer.size();
+}
+
+int Buffer::addString(QByteArray s)
+{
+ unsigned int pos = mBuffer.size();
+ int len = s.size();
+ expandBuffer(len);
+
+ for ( int i = 0; i < len; i++ )
+ mBuffer[pos + i] = s[i];
+
+ return mBuffer.size();
+}
+
+int Buffer::addString(QByteArray s, DWORD len)
+{
+ Q_UNUSED( len );
+ return addString( s );
+}
+
+int Buffer::addString( const char* s, DWORD len )
+{
+ QByteArray qba;
+ qba.duplicate( s, len );
+ return addString( qba );
+}
+
+int Buffer::addString(const unsigned char* s, DWORD len)
+{
+ QByteArray qba;
+ qba.duplicate( (const char*) s, len );
+ return addString( qba );
+}
+
+int Buffer::addLEString(const char *s, const DWORD len)
+{
+ unsigned int pos = mBuffer.size();
+ expandBuffer(len);
+ //concatenate the new string onto the buffer
+ for(unsigned int i=0; i<len; i++)
+ {
+ mBuffer[pos+i]=((s[i]) & 0xff);
+ }
+ return mBuffer.size();
+}
+
+
+void Buffer::clear()
+{
+ mBuffer.truncate( 0 );
+ mReadPos=0;
+}
+
+int Buffer::addTLV( const TLV& t )
+{
+ return addTLV( t.type, t.length, t.data );
+}
+
+int Buffer::addTLV(WORD type, WORD len, const char *data)
+{
+
+ addWord(type);
+ addWord(len);
+ return addString(data,len);
+}
+
+int Buffer::addLETLV(WORD type, WORD len, const char *data)
+{
+ addLEWord( type );
+ addLEWord( len );
+ return addString( data, len );
+}
+
+BYTE Buffer::getByte()
+{
+ BYTE thebyte = 0x00;
+
+ if(mReadPos < mBuffer.size())
+ {
+ thebyte = mBuffer[mReadPos];
+ mReadPos++;
+ }
+ else
+ kdDebug(14150) << "Buffer::getByte(): mBuffer empty" << endl;
+
+ return thebyte;
+}
+
+void Buffer::skipBytes( int bytesToSkip )
+{
+ if (mReadPos < mBuffer.size())
+ mReadPos += bytesToSkip;
+}
+
+BYTE Buffer::getLEByte()
+{
+ BYTE b = getByte();
+ return (b & 0xff);
+}
+
+WORD Buffer::getWord()
+{
+ WORD theword, theword2, retword;
+ theword = getByte();
+ theword2 = getByte();
+ retword = (theword << 8) | theword2;
+ return retword;
+}
+
+WORD Buffer::getLEWord()
+{
+ WORD theword1, theword2, retword;
+ theword1 = getLEByte();
+ theword2 = getLEByte();
+ retword = (theword2 << 8) | theword1;
+ return retword;
+}
+
+DWORD Buffer::getDWord()
+{
+ DWORD word1, word2;
+ DWORD retdword;
+ word1 = getWord();
+ word2 = getWord();
+ retdword = (word1 << 16) | word2;
+ return retdword;
+}
+
+DWORD Buffer::getLEDWord()
+{
+ DWORD word1, word2, retdword;
+ word1 = getLEWord();
+ word2 = getLEWord();
+ retdword = (word2 << 16) | word1;
+ return retdword;
+}
+
+void Buffer::setBuf(char *b, const WORD len)
+{
+ kdDebug(14150) << k_funcinfo << "Called." << endl;
+
+ mBuffer.duplicate(b, len);
+ mReadPos = 0;
+}
+
+QByteArray Buffer::getBlock(WORD len)
+{
+ QByteArray ch( len );
+ for ( int i = 0; i < len; i++ )
+ {
+ ch[i] = getByte();
+ }
+
+ return ch;
+}
+
+QByteArray Buffer::getBBlock(WORD len)
+{
+ QByteArray data;
+ data.duplicate(mBuffer.data() + mReadPos, len);
+ mReadPos += len;
+ return data;
+}
+
+
+WORD *Buffer::getWordBlock(WORD len)
+{
+ kdDebug(14150) << k_funcinfo << "of length " << len << endl;
+ WORD *ch=new WORD[len+1];
+ for (unsigned int i=0; i<len; i++)
+ {
+ ch[i]=getWord();
+ }
+ ch[len]=0;
+ return ch;
+}
+
+
+QCString Buffer::getLEBlock(WORD len)
+{
+ QCString ch;
+ for (unsigned int i=0;i<len;i++)
+ ch += getLEByte();
+
+ return ch;
+}
+
+int Buffer::addTLV16(const WORD type, const WORD data)
+{
+ addWord(type);
+ addWord(0x0002); //2 bytes long
+ return addWord(data);
+}
+
+int Buffer::addLETLV16(const WORD type, const WORD data)
+{
+ addLEWord(type);
+ addLEWord(0x0002); //2 bytes long
+ return addLEWord(data);
+}
+
+int Buffer::addTLV8(const WORD type, const BYTE data)
+{
+ addWord(type);
+ addWord(0x0001); //1 byte long
+ return addByte(data);
+}
+
+int Buffer::addLETLV8(const WORD type, const BYTE data)
+{
+ addLEWord(type);
+ addLEWord(0x0001); //1 byte long
+ return addLEByte(data);
+}
+
+TLV Buffer::getTLV()
+{
+ TLV t;
+ if(length() >= 4)
+ {
+ t.type = getWord();
+ t.length = getWord();
+ if ( t )
+ t.data = getBlock( t.length );
+ /*else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Invalid TLV in buffer" << endl;*/
+ }
+
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV data is " << t.data << endl;
+ return t;
+}
+
+QValueList<TLV> Buffer::getTLVList()
+{
+ QValueList<TLV> ql;
+
+ while (mReadPos < mBuffer.size())
+ {
+ TLV t;
+
+ t = getTLV();
+ if ( !t )
+ {
+ kdDebug(14150) << k_funcinfo << "Invalid TLV found" << endl;
+ continue;
+ }
+
+ //kdDebug(14150) << k_funcinfo << "got TLV(" << t.type << ")" << endl;
+ ql.append(t);
+ }
+
+ return ql;
+}
+
+int Buffer::addChatTLV(const WORD type, const WORD exchange,
+ const QString &roomname, const WORD instance)
+{
+ addWord(type);
+ addWord(0x0005 + roomname.length());
+ addWord(exchange);
+ addByte(roomname.length());
+ addString(roomname.latin1(), roomname.length()); // TODO: check encoding
+
+ return addWord(instance);
+}
+
+void Buffer::expandBuffer(unsigned int inc)
+{
+ mBuffer.resize(mBuffer.size()+inc, QGArray::SpeedOptim);
+}
+
+QCString Buffer::getLNTS()
+{
+ WORD len = getLEWord();
+ QCString qcs;
+ qcs.duplicate( getBlock(len) );
+ return qcs;
+}
+
+QCString Buffer::getLELNTS()
+{
+ WORD len = getLEWord();
+ QCString qcs;
+ qcs.duplicate( getBlock(len) );
+ return qcs;
+}
+
+int Buffer::addLNTS(const char *s)
+{
+ unsigned int len = strlen(s);
+
+ addLEWord(len+1);
+ if(len > 0)
+ addString(s, len);
+ int ret = addByte(0x00);
+ return ret;
+}
+
+int Buffer::addLELNTS(const char *s)
+{
+ unsigned int len = strlen(s);
+ int ret = addLEWord(len+1);
+ if(len > 0)
+ ret = addLEString(s, len);
+ ret = addByte(0x00);
+ return ret;
+}
+
+int Buffer::addBSTR(const char * s)
+{
+ unsigned int len = strlen(s);
+ int ret = addWord(len);
+ if(len > 0)
+ ret = addString(s, len);
+ return ret;
+}
+
+QByteArray Buffer::getBSTR()
+{
+ WORD len = getWord();
+ QByteArray qba;
+ qba.duplicate( getBlock(len) );
+ return qba;
+}
+
+int Buffer::addBUIN(const char * s)
+{
+ unsigned int len = strlen(s);
+ int ret = addByte(len);
+ ret = addString(s, len);
+ return ret;
+}
+
+QByteArray Buffer::getBUIN()
+{
+ BYTE len = getByte();
+ QByteArray qba;
+ qba.duplicate( getBlock(len) );
+ return qba;
+}
+
+char *Buffer::buffer() const
+{
+ return mBuffer.data();
+}
+
+int Buffer::length() const
+{
+ return (mBuffer.size() - mReadPos);
+}
+
+QString Buffer::toString() const
+{
+ // line format:
+ //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........|
+
+ int i = 0;
+ QString output = "\n";
+ QString hex, ascii;
+
+ QByteArray::ConstIterator it;
+ for ( it = mBuffer.begin(); it != mBuffer.end(); ++it )
+ {
+ i++;
+
+ unsigned char c = static_cast<unsigned char>(*it);
+
+ if ( c < 0x10 )
+ hex.append("0");
+ hex.append(QString("%1 ").arg(c, 0, 16));
+
+ ascii.append(isprint(c) ? c : '.');
+
+ if (i == 16)
+ {
+ output += hex + " |" + ascii + "|\n";
+ i=0;
+ hex=QString::null;
+ ascii=QString::null;
+ }
+ }
+
+ if(!hex.isEmpty())
+ output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|';
+ output.append('\n');
+
+ return output;
+}
+
+QString Buffer::peekBSTR()
+{
+ int lastPos = mReadPos;
+ QByteArray qba = getBSTR();
+ mReadPos = lastPos;
+ return QString( qba );
+}
+QString Buffer::peekBUIN()
+{
+ int lastPos = mReadPos;
+ QByteArray qba = getBUIN();
+ mReadPos = lastPos;
+ return QString( qba );
+}
+
+Buffer::operator QByteArray() const
+{
+ return mBuffer;
+}
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/buffer.h b/kopete/protocols/oscar/liboscar/buffer.h
new file mode 100644
index 00000000..900ddb50
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/buffer.h
@@ -0,0 +1,268 @@
+/***************************************************************************
+ buffer.h - description
+ -------------------
+ begin : Thu Jun 6 2002
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2003-2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include "oscartypes.h"
+
+#include <qvaluelist.h>
+#include <qcstring.h>
+
+class QString;
+
+using namespace Oscar;
+
+/**
+ * @brief A data buffer
+ */
+class Buffer
+{
+ public:
+ /** Default constructor */
+ Buffer();
+ Buffer( const Buffer& other );
+
+ /**
+ * \brief Create a prefilled buffer
+ *
+ * Constructor that creates a prefilled buffer of @p len length
+ * that contains the data from @p b.
+ */
+ Buffer(const char *b, Q_ULONG len);
+
+ /**
+ * \brief Create a prefilled buffer
+ *
+ * Constructor that creates a prefilled buffer from the QByteArray \p data
+ */
+ Buffer( const QByteArray& data );
+
+
+ /** Default destructor */
+ ~Buffer();
+
+ /**
+ * returns the raw buffer
+ */
+ char *buffer() const;
+
+ /**
+ * Returns the remaining length of the buffer past the current read
+ * position.
+ */
+ int length() const;
+
+ /**
+ * adds the given string to the buffer (make sure it's NULL-terminated)
+ */
+ int addString(QByteArray);
+ int addString(QByteArray, DWORD);
+ int addString(const char*, DWORD);
+ int addString(const unsigned char*, DWORD);
+
+ /**
+ * Little-endian version of addString
+ */
+ int addLEString(const char *, const DWORD);
+
+ /**
+ * adds the given string to the buffer with the length in front of it
+ * (make sure it's NULL-terminated)
+ */
+ int addLNTS(const char * s);
+ /**
+ * Little-endian version of addLNTS
+ */
+ int addLELNTS(const char * s);
+
+ /**
+ * adds the given DWord to the buffer
+ */
+ int addDWord(const DWORD);
+
+ /**
+ * adds the given word to the buffer
+ */
+ int addWord(const WORD);
+
+ /**
+ * adds the given word to the buffer in
+ * little-endian format as needed by old icq server
+ */
+ int addLEWord(const WORD w);
+
+ /**
+ * adds the given DWord to the buffer in
+ * little-endian format as needed by old icq server
+ */
+ int addLEDWord(const DWORD dw);
+
+ /**
+ * adds the given byte to the buffer
+ */
+ int addByte(const BYTE);
+ int addLEByte(const BYTE);
+
+ /**
+ * empties the current buffer.
+ */
+ void clear();
+
+ /**
+ * Adds a TLV to the buffer
+ */
+ int addTLV( const TLV& t );
+
+ /**
+ * Adds a TLV with the given type and data
+ */
+ int addTLV(WORD, WORD, const char *);
+
+ /**
+ * Adds a little-endian TLV with the given type and data
+ */
+ int addLETLV(WORD, WORD, const char *);
+
+ /**
+ * Returns a QString representation of the buffer
+ */
+ QString toString() const;
+
+ /**
+ * gets a DWord out of the buffer
+ */
+ DWORD getDWord();
+
+ /**
+ * Gets a word out of the buffer
+ */
+ WORD getWord();
+
+ /**
+ * Gets a byte out of the buffer
+ * It's not a constant method. It advances the buffer
+ * to the next BYTE after returning one.
+ */
+ BYTE getByte();
+
+ /**
+ * Skip \p bytesToSkip number of bytes in the buffer
+ * Like getByte() the buffer is advanced when skipping
+ */
+ void skipBytes( int bytesToSkip );
+
+ /**
+ * Same as above but returns little-endian
+ */
+ WORD getLEWord();
+ DWORD getLEDWord();
+ BYTE getLEByte();
+
+ /**
+ * Set the buffer to the given values.
+ */
+ void setBuf(char *, const WORD);
+
+ /**
+ * Allocates memory for and gets a block of buffer bytes
+ */
+ QByteArray getBlock(WORD len);
+ QByteArray getBBlock(WORD len);
+
+ /**
+ * Allocates memory for and gets a block of buffer words
+ */
+ WORD *getWordBlock(WORD len);
+
+ /**
+ * Same as above but returning little-endian
+ */
+ QCString getLEBlock(WORD len);
+
+ /**
+ * Convenience function that gets a LNTS (long null terminated string)
+ * from the buffer. Otherwise you'd need a getWord() + getBlock() call :)
+ */
+ QCString getLNTS();
+ QCString getLELNTS();
+
+ /**
+ * adds a 16-bit long TLV
+ */
+ int addTLV16(const WORD type, const WORD data);
+
+ /**
+ * adds a 16-bit long little-endian TLV
+ */
+ int addLETLV16(const WORD type, const WORD data);
+
+ /**
+ * adds the given byte to a TLV
+ */
+ int addTLV8(const WORD type, const BYTE data);
+
+ /**
+ * adds the given byte to a little-endian TLV
+ */
+ int addLETLV8(const WORD type, const BYTE data);
+
+ /**
+ * Gets a TLV, storing it in a struct and returning it
+ */
+ TLV getTLV();
+
+ /**
+ * Gets a list of TLV's
+ */
+ QValueList<TLV> getTLVList();
+
+ /**
+ * Creates a chat data segment for a tlv and calls addTLV with that data
+ */
+ int addChatTLV(const WORD, const WORD, const QString &, const WORD);
+
+ /**
+ * Similar to the LNTS functions but string is NOT null-terminated
+ */
+ int addBSTR(const char * s);
+ QByteArray getBSTR();
+ QString peekBSTR();
+
+ int addBUIN(const char * s);
+ QByteArray getBUIN();
+ QString peekBUIN();
+
+ operator QByteArray() const;
+
+ private:
+ /**
+ * Make the buffer bigger by @p inc bytes
+ */
+ void expandBuffer(unsigned int inc);
+
+ private:
+ QByteArray mBuffer;
+ unsigned int mReadPos;
+
+};
+
+#endif
+// kate: tab-width 4; indent-mode csands;
+// vim: set noet ts=4 sts=4 sw=4:
diff --git a/kopete/protocols/oscar/liboscar/bytestream.cpp b/kopete/protocols/oscar/liboscar/bytestream.cpp
new file mode 100644
index 00000000..7faa803b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/bytestream.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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/oscar/liboscar/bytestream.h b/kopete/protocols/oscar/liboscar/bytestream.h
new file mode 100644
index 00000000..7f964fbd
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/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/oscar/liboscar/changevisibilitytask.cpp b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp
new file mode 100644
index 00000000..5cb44720
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.cpp
@@ -0,0 +1,150 @@
+/*
+ Kopete Oscar Protocol
+ changevisibilitytask.cpp - Changes the visibility of the account via SSI
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "changevisibilitytask.h"
+
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include "buffer.h"
+#include "client.h"
+#include "connection.h"
+#include "oscartypeclasses.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "ssimanager.h"
+#include "transfer.h"
+
+
+ChangeVisibilityTask::ChangeVisibilityTask(Task* parent): Task(parent)
+{
+ m_sequence = 0;
+ m_visible = true;
+}
+
+
+ChangeVisibilityTask::~ChangeVisibilityTask()
+{
+}
+
+void ChangeVisibilityTask::setVisible( bool visible )
+{
+ m_visible = visible;
+}
+
+bool ChangeVisibilityTask::forMe(const Transfer* transfer) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ SNAC s = st->snac(); //cheat
+ if ( s.family == 0x0013 && s.subtype == 0x000E )
+ return true;
+ else
+ return false;
+}
+
+bool ChangeVisibilityTask::take(Transfer* transfer)
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return true;
+ }
+ else
+ {
+ setError( 0, QString::null );
+ return false;
+ }
+}
+
+void ChangeVisibilityTask::onGo()
+{
+ SSIManager* manager = client()->ssiManager();
+ Oscar::SSI item = manager->visibilityItem();
+ if ( !item )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Didn't find a visibility item" << endl;
+ setError( 0, QString::null );
+ return;
+ }
+
+ Buffer c8tlv;
+ BYTE visibleByte = m_visible ? 0x04 : 0x03;
+ c8tlv.addByte( visibleByte );
+
+ QValueList<Oscar::TLV> tList;
+ tList.append( TLV( 0x00CA, c8tlv.length(), c8tlv.buffer() ) );
+
+ Oscar::SSI newSSI(item);
+ if ( Oscar::uptateTLVs( newSSI, tList ) == false )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Visibility didn't change, don't update" << endl;
+ setSuccess( 0, QString::null );
+ return;
+ }
+
+ //remove the old item and add the new item indicating the
+ //change in visibility.
+ manager->removeItem( item );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found visibility item. changing setting" << endl;
+ manager->newItem( newSSI );
+ sendEditStart();
+
+ Buffer* b = new Buffer();
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x0009, 0x0000, client()->snacSequence() };
+ m_sequence = s.id;
+ b->addWord( 0 );
+ b->addWord( newSSI.gid() );
+ b->addWord( newSSI.bid() );
+ b->addWord( newSSI.type() );
+ b->addWord( newSSI.tlvListLength() );
+
+ QValueList<TLV>::const_iterator it2 = newSSI.tlvList().begin();
+ QValueList<TLV>::const_iterator listEnd2 = newSSI.tlvList().end();
+ for( ; it2 != listEnd2; ++it2 )
+ b->addTLV( ( *it2 ) );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending visibility update" << endl;
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+ sendEditEnd();
+}
+
+void ChangeVisibilityTask::sendEditStart()
+{
+ SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() };
+ FLAP editStart = { 0x02, 0, 0 };
+ Buffer* emptyBuffer = new Buffer;
+ Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer );
+ send( t1 );
+}
+
+void ChangeVisibilityTask::sendEditEnd()
+{
+ SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() };
+ FLAP editEnd = { 0x02, 0, 0 };
+ Buffer* emptyBuffer = new Buffer;
+ Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer );
+ send( t5 );
+}
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/changevisibilitytask.h b/kopete/protocols/oscar/liboscar/changevisibilitytask.h
new file mode 100644
index 00000000..0ec5ab04
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/changevisibilitytask.h
@@ -0,0 +1,58 @@
+/*
+ Kopete Oscar Protocol
+ changevisibilitytask.h - Changes the visibility of the account via SSI
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHANGEVISIBILITYTASK_H
+#define CHANGEVISIBILITYTASK_H
+
+#include "task.h"
+
+/**
+ * This class provides a way to change how the account user
+ * appears on everybody else's contact list. It is used to
+ * implement the invisible online status in ICQ and AIM
+ * @author Matt Rogers
+ */
+class ChangeVisibilityTask : public Task
+{
+public:
+ ChangeVisibilityTask( Task* parent );
+ ~ChangeVisibilityTask();
+
+ void setVisible( bool visible = true );
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+private:
+ //damnit, this is ugly. time to refactor SSI stuff out into it's own
+ //class, file, whatever.
+ //! Send the SSI edit start packet
+ void sendEditStart();
+
+ //! Send the SSI edit end packet
+ void sendEditEnd();
+
+private:
+ bool m_visible;
+ DWORD m_sequence;
+};
+
+#endif
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp
new file mode 100644
index 00000000..f661d1f4
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.cpp
@@ -0,0 +1,355 @@
+/*
+ Kopete Oscar Protocol - Chat Navigation service handlers
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatnavservicetask.h"
+
+#include <kdebug.h>
+
+#include "transfer.h"
+#include "buffer.h"
+#include "task.h"
+#include "client.h"
+#include "connection.h"
+
+
+ChatNavServiceTask::ChatNavServiceTask( Task* parent ) : Task( parent )
+{
+ m_type = Limits;
+}
+
+
+ChatNavServiceTask::~ChatNavServiceTask()
+{
+}
+
+void ChatNavServiceTask::setRequestType( RequestType rt )
+{
+ m_type = rt;
+}
+
+ChatNavServiceTask::RequestType ChatNavServiceTask::requestType()
+{
+ return m_type;
+}
+
+QValueList<int> ChatNavServiceTask::exchangeList() const
+{
+ return m_exchanges;
+}
+
+bool ChatNavServiceTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+ if ( st->snacService() == 0x000D && st->snacSubtype() == 0x0009 )
+ return true;
+
+ return false;
+}
+
+bool ChatNavServiceTask::take( Transfer* transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+
+ setTransfer( transfer );
+ Buffer* b = transfer->buffer();
+ while ( b->length() > 0 )
+ {
+ TLV t = b->getTLV();
+ switch ( t.type )
+ {
+ case 0x0001:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got chat redirect TLV" << endl;
+ break;
+ case 0x0002:
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got max concurrent rooms TLV" << endl;
+ Buffer tlvTwo(t.data);
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max concurrent rooms is " << tlvTwo.getByte() << endl;
+ break;
+ }
+ case 0x0003:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange info TLV found" << endl;
+ handleExchangeInfo( t );
+ //set the exchanges for the client
+ emit haveChatExchanges( m_exchanges );
+ break;
+ case 0x0004:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room info TLV found" << endl;
+ handleBasicRoomInfo( t );
+ break;
+ };
+ }
+
+
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return true;
+
+}
+
+void ChatNavServiceTask::onGo()
+{
+ FLAP f = { 0x02, 0, 0x00 };
+ SNAC s = { 0x000D, m_type, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer();
+
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+void ChatNavServiceTask::createRoom( WORD exchange, const QString& name )
+{
+ //most of this comes from gaim. thanks to them for figuring it out
+ QString cookie = "create"; //hardcoded, seems to be ignored by AOL
+ QString lang = "en";
+ QString charset = "us-ascii";
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x000D, 0x0008, 0x0000, client()->snacSequence() };
+ Buffer *b = new Buffer;
+
+ b->addWord( exchange );
+ b->addBUIN( cookie.latin1() );
+ b->addWord( 0xFFFF ); //assign the last instance
+ b->addByte( 0x01 ); //detail level
+
+ //just send three TLVs
+ b->addWord( 0x0003 );
+
+ //i'm lazy, add TLVs manually
+
+ b->addWord( 0x00D3 ); //type of 0x00D3 - name
+ b->addWord( name.length() );
+ b->addString( name.latin1(), name.length() );
+
+ b->addWord( 0x00D6 ); //type of 0x00D6 - charset
+ b->addWord( charset.length() );
+ b->addString( charset.latin1(), charset.length() );
+
+ b->addWord( 0x00D7 ); //type of 0x00D7 - lang
+ b->addWord( lang.length() );
+ b->addString( lang.latin1(), lang.length() );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending join room packet" << endl;
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+
+void ChatNavServiceTask::handleExchangeInfo( const TLV& t )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << "Parsing exchange info TLV" << endl;
+ Buffer b(t.data);
+ ChatExchangeInfo exchangeInfo;
+
+ exchangeInfo.number = b.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "exchange id is: " << exchangeInfo.number << endl;
+ b.getWord();
+ while ( b.length() > 0 )
+ {
+ TLV t = b.getTLV();
+ Buffer tmp = t.data;
+ switch (t.type)
+ {
+ case 0x02:
+ //kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl;
+ break;
+ case 0x03:
+ exchangeInfo.maxRooms = tmp.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << "max concurrent rooms for the exchange is " << t.data << endl;
+ break;
+ case 0x04:
+ exchangeInfo.maxRoomNameLength = tmp.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max room name length is " << exchangeInfo.maxRoomNameLength << endl;
+ break;
+ case 0x05:
+ //kdDebug(OSCAR_RAW_DEBUG) << "received root rooms info" << endl;
+ break;
+ case 0x06:
+ //kdDebug(OSCAR_RAW_DEBUG) << "received search tags" << endl;
+ break;
+ case 0xCA:
+ //kdDebug(OSCAR_RAW_DEBUG) << "have exchange creation time" << endl;
+ break;
+ case 0xC9:
+ //kdDebug(OSCAR_RAW_DEBUG) << "got chat flag" << endl;
+ break;
+ case 0xD0:
+ //kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl;
+ break;
+ case 0xD1:
+ exchangeInfo.maxMsgLength = tmp.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl;
+ break;
+ case 0xD2:
+ kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl;
+ break;
+ case 0xD3:
+ {
+ QString eName( t.data );
+ kdDebug(OSCAR_RAW_DEBUG) << "exchange name: " << eName << endl;
+ exchangeInfo.description = eName;
+ break;
+ }
+ case 0xD4:
+ //kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl;
+ break;
+ case 0xD5:
+ exchangeInfo.canCreate = tmp.getByte();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "creation permissions " << exchangeInfo.canCreate << endl;
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl;
+ break;
+ }
+ }
+ m_exchanges.append( exchangeInfo.number );
+}
+
+void ChatNavServiceTask::handleBasicRoomInfo( const TLV& t )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << "Parsing room info TLV" << t.length << endl;
+ Buffer b(t.data);
+ WORD exchange = b.getWord();
+ QByteArray cookie( b.getBlock( b.getByte() ) );
+ WORD instance = b.getWord();
+ b.getByte(); //detail level, which i'm not sure we need
+ WORD tlvCount = b.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "e: " << exchange
+ << " c: " << cookie << " i: " << instance << endl;
+
+ QValueList<Oscar::TLV> tlvList = b.getTLVList();
+ QValueList<Oscar::TLV>::iterator it, itEnd = tlvList.end();
+ QString roomName;
+ for ( it = tlvList.begin(); it != itEnd; ++it )
+ {
+ TLV t = ( *it );
+ switch (t.type)
+ {
+ case 0x66:
+ kdDebug(OSCAR_RAW_DEBUG) << "user class is " << t.data << endl;
+ break;
+ case 0x67:
+ kdDebug(OSCAR_RAW_DEBUG) << "user array" << endl;
+ break;
+ case 0x68:
+ kdDebug(OSCAR_RAW_DEBUG) << "evil generated" << t.data << endl;
+ break;
+ case 0x69:
+ kdDebug(OSCAR_RAW_DEBUG) << "evil generated array" << endl;
+ break;
+ case 0x6A:
+ roomName = QString( t.data );
+ kdDebug(OSCAR_RAW_DEBUG) << "fully qualified name" << roomName << endl;
+ break;
+ case 0x6B:
+ kdDebug(OSCAR_RAW_DEBUG) << "moderator" << endl;
+ break;
+ case 0x6D:
+ kdDebug(OSCAR_RAW_DEBUG) << "num children" << endl;
+ break;
+ case 0x06F:
+ kdDebug(OSCAR_RAW_DEBUG) << "occupancy" << endl;
+ break;
+ case 0x71:
+ kdDebug(OSCAR_RAW_DEBUG) << "occupant evil" << endl;
+ break;
+ case 0x75:
+ kdDebug(OSCAR_RAW_DEBUG) << "room activity" << endl;
+ break;
+ case 0xD0:
+ kdDebug(OSCAR_RAW_DEBUG) << "got mandantory channels" << endl;
+ break;
+ case 0xD1:
+ kdDebug(OSCAR_RAW_DEBUG) << "max message length" << t.data << endl;
+ break;
+ case 0xD2:
+ kdDebug(OSCAR_RAW_DEBUG) << "max occupancy" << t.data << endl;
+ break;
+ case 0xD3:
+ kdDebug(OSCAR_RAW_DEBUG) << "exchange name" << endl;
+ break;
+ case 0xD4:
+ kdDebug(OSCAR_RAW_DEBUG) << "got optional channels" << endl;
+ break;
+ case 0xD5:
+ kdDebug(OSCAR_RAW_DEBUG) << "creation permissions " << t.data << endl;
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << "unknown TLV type " << t.type << endl;
+ break;
+ }
+ }
+
+ emit connectChat( exchange, cookie, instance, roomName );
+}
+
+void ChatNavServiceTask::handleCreateRoomInfo( const TLV& t )
+{
+ Buffer b( t.data );
+ WORD exchange = b.getWord();
+ WORD cookieLength = b.getByte();
+ QByteArray cookie( b.getBlock( cookieLength ) );
+ WORD instance = b.getWord();
+ BYTE detailLevel = b.getByte();
+
+ if ( detailLevel != 0x02 )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown detail level in response" << endl;
+ return;
+ }
+
+ WORD numberTlvs = b.getWord();
+ QValueList<Oscar::TLV> roomTLVList = b.getTLVList();
+ QValueList<Oscar::TLV>::iterator itEnd = roomTLVList.end();
+ for ( QValueList<Oscar::TLV>::iterator it = roomTLVList.begin();
+ it != itEnd; ++ it )
+ {
+ switch( ( *it ).type )
+ {
+ case 0x006A:
+ {
+ QString fqcn = QString( ( *it ).data );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "fqcn: " << fqcn << endl;
+ break;
+ }
+ case 0x00C9:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags: " << t.data << endl;
+ break;
+ case 0x00CA:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create time: " << t.data << endl;
+ break;
+ case 0x00D1:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max msg len: " << t.data << endl;
+ break;
+ case 0x00D2:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "max occupancy: " << t.data << endl;
+ break;
+ case 0x00D3:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "name: " << QString( t.data ) << endl;
+ break;
+ case 0x00D5:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "create perms: " << t.data << endl;
+ break;
+ };
+ }
+}
+
+#include "chatnavservicetask.moc"
+//kate: indent-mode csands; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/chatnavservicetask.h b/kopete/protocols/oscar/liboscar/chatnavservicetask.h
new file mode 100644
index 00000000..6b7d8764
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/chatnavservicetask.h
@@ -0,0 +1,67 @@
+/*
+ Kopete Oscar Protocol - Chat Navigation service handlers
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATNAVSERVICETASK_H
+#define CHATNAVSERVICETASK_H
+
+#include "task.h"
+
+#include <qvaluelist.h>
+#include <oscartypes.h>
+
+class Transfer;
+
+/**
+ * @author Matt Rogers
+ */
+class ChatNavServiceTask : public Task
+{
+Q_OBJECT
+public:
+ ChatNavServiceTask( Task* parent );
+ ~ChatNavServiceTask();
+
+ enum RequestType { Limits = 0x0002, Exchange, Room, ExtRoom, Members,
+ Search, Create };
+
+ void setRequestType( RequestType );
+ RequestType requestType();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+ void createRoom( WORD exchange, const QString& name ); //create a room. sends the packet as well
+
+ QValueList<int> exchangeList() const;
+
+signals:
+ void haveChatExchanges( const QValueList<int>& );
+ void connectChat( WORD, QByteArray, WORD, const QString& );
+
+private:
+ void handleExchangeInfo( const TLV& t );
+ void handleBasicRoomInfo( const TLV& t );
+ void handleCreateRoomInfo( const TLV& t );
+
+private:
+ QValueList<int> m_exchanges;
+ RequestType m_type;
+};
+
+#endif
+
+//kate: indent-mode csands; tab-width 4;
+
diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.cpp b/kopete/protocols/oscar/liboscar/chatservicetask.cpp
new file mode 100644
index 00000000..9d07afe8
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/chatservicetask.cpp
@@ -0,0 +1,359 @@
+// Kopete Oscar Protocol - chat service task
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+
+
+#include "chatservicetask.h"
+
+#include <qstring.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <qtextcodec.h>
+
+#include "connection.h"
+#include "transfer.h"
+#include "buffer.h"
+#include "oscartypes.h"
+
+ChatServiceTask::ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room )
+ : Task( parent ), m_encoding( "us-ascii" )
+{
+ m_exchange = exchange;
+ m_room = room;
+}
+
+ChatServiceTask::~ChatServiceTask()
+{
+
+}
+
+void ChatServiceTask::setMessage( const Oscar::Message& msg )
+{
+ m_message = msg;
+}
+
+void ChatServiceTask::setEncoding( const QCString& enc )
+{
+ m_encoding = enc;
+}
+
+void ChatServiceTask::onGo()
+{
+ if ( !m_message )
+ {
+ setSuccess( true, QString::null );
+ return;
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending '" << m_message.textArray() << "' to the "
+ << m_room << " room" << endl;
+ Buffer* b = new Buffer();
+ b->addDWord( KApplication::random() ); //use kapp since it's convenient
+ b->addDWord( KApplication::random() );
+ b->addWord( 0x0003 ); //this be message channel 3 mateys! arrr!!
+ b->addDWord( 0x00010000 ); //TLV 1 - this means it's a public message
+ b->addDWord( 0x00060000 ); //TLV 6 - enables the server sending you your message back
+
+ Buffer tlv5;
+ TLV type2, type3, type1;
+
+ type2.type = 0x0002;
+ type2.length = 0x0008;
+ type2.data = m_encoding;
+
+ type3.type = 0x0003;
+ type3.length = 0x0002;
+ type3.data = QCString( "en" ); //hardcode for right now. don't know that we can do others
+
+ type1.type = 0x0001;
+ type1.length = m_message.textArray().size();
+ type1.data = m_message.textArray();
+ tlv5.addWord( 0x0005 );
+ tlv5.addWord( 12 + type1.length + type2.length + type3.length );
+ tlv5.addTLV( type1 );
+ tlv5.addTLV( type2 );
+ tlv5.addTLV( type3 );
+
+ b->addString( tlv5.buffer(), tlv5.length() );
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x000E, 0x0005, 0x0000, client()->snacSequence() };
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+ setSuccess( true );
+}
+
+bool ChatServiceTask::forMe( const Transfer* t ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() != 0x000E )
+ return false;
+
+ switch ( st->snacSubtype() )
+ {
+ case 0x0003:
+ case 0x0002:
+ case 0x0006:
+ case 0x0009:
+ case 0x0004:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+bool ChatServiceTask::take( Transfer* t )
+{
+ if ( !forMe( t ) )
+ return false;
+
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( t );
+ if ( !st )
+ return false;
+
+ setTransfer( t );
+
+ switch ( st->snacSubtype() )
+ {
+ case 0x0002:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parse room info" << endl;
+ parseRoomInfo();
+ break;
+ case 0x0003:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user joined notification" << endl;
+ parseJoinNotification();
+ break;
+ case 0x0004:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user left notification" << endl;
+ parseLeftNotification();
+ break;
+ case 0x0006:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message from room to client" << endl;
+ parseChatMessage();
+ break;
+ case 0x0009:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "chat error or data" << endl;
+ break;
+ };
+
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return true;
+}
+
+void ChatServiceTask::parseRoomInfo()
+{
+ WORD instance;
+ BYTE detailLevel;
+ Buffer* b = transfer()->buffer();
+
+ m_exchange = b->getWord();
+ QByteArray cookie( b->getBlock( b->getByte() ) );
+ instance = b->getWord();
+
+ detailLevel = b->getByte();
+
+ //skip the tlv count, we don't care. Buffer::getTLVList() handles this all
+ //correctly anyways
+ b->skipBytes( 2 );
+
+ QValueList<Oscar::TLV> tlvList = b->getTLVList();
+ QValueList<Oscar::TLV>::iterator it = tlvList.begin();
+ QValueList<Oscar::TLV>::iterator itEnd = tlvList.end();
+ for ( ; it != itEnd; ++it )
+ {
+ switch ( ( *it ).type )
+ {
+ case 0x006A:
+ m_internalRoom = QString( ( *it ).data );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "room name: " << m_room << endl;
+ break;
+ case 0x006F:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "num occupants: " << ( *it ).data << endl;
+ break;
+ case 0x0073:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "occupant list" << endl;
+ break;
+ case 0x00C9:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "flags" << endl;
+ break;
+ case 0x00CA: //creation time
+ case 0x00D1: //max message length
+ case 0x00D3: //room description
+ case 0x00D6: //encoding 1
+ case 0x00D7: //language 1
+ case 0x00D8: //encoding 2
+ case 0x00D9: //language 2
+ case 0x00DA: //maximum visible message length
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unhandled TLV type " << ( *it ).type << endl;
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown TLV type " << ( *it ).type << endl;
+ break;
+ }
+ }
+}
+
+void ChatServiceTask::parseJoinNotification()
+{
+ Buffer* b = transfer()->buffer();
+ while ( b->length() > 0 )
+ {
+ QString sender( b->getBUIN() );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl;
+ WORD warningLevel = b->getWord();
+ WORD numTLVs = b->getWord();
+ for ( int i = 0; i < numTLVs; i++ )
+ {
+ TLV t = b->getTLV();
+ switch ( t.type )
+ {
+ case 0x0001:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl;
+ break;
+ case 0x000F:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl;
+ break;
+ case 0x0003:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl;
+ break;
+ }
+ }
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "emitted userJoinedChat" << endl;
+ emit userJoinedChat( m_exchange, m_room, sender );
+ }
+
+}
+
+void ChatServiceTask::parseLeftNotification()
+{
+ Buffer* b = transfer()->buffer();
+ while ( b->length() > 0 )
+ {
+ QString sender( b->getBUIN() );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user name:" << sender << endl;
+ WORD warningLevel = b->getWord();
+ WORD numTLVs = b->getWord();
+ for ( int i = 0; i < numTLVs; i++ )
+ {
+ TLV t = b->getTLV();
+ switch ( t.type )
+ {
+ case 0x0001:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "user class: " << t.data << endl;
+ break;
+ case 0x000F:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "idle time: " << t.data << endl;
+ break;
+ case 0x0003:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "signon: " << t.data << endl;
+ break;
+ }
+ }
+ emit userLeftChat( m_exchange, m_room, sender );
+ }
+}
+
+void ChatServiceTask::parseChatMessage()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "have new chat room message" << endl;
+ Buffer* b = transfer()->buffer();
+ bool whisper = true, reflection = false;
+ QByteArray language, encoding, message;
+ QString sender;
+ QByteArray icbmCookie( b->getBlock( 8 ) );
+ b->skipBytes( 2 ); //message channel always 0x03
+ QValueList<Oscar::TLV> chatTLVs = b->getTLVList();
+ QValueList<Oscar::TLV>::iterator it, itEnd = chatTLVs.end();
+ for ( it = chatTLVs.begin(); it != itEnd; ++it )
+ {
+ switch ( ( *it ).type )
+ {
+ case 0x0001: //if present, message was sent to the room
+ whisper = false;
+ break;
+ case 0x0006: //enable reflection
+ reflection = true;
+ break;
+ case 0x0005: //the good stuff - the actual message
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsing the message" << endl;
+ //oooh! look! more TLVS! i love those!
+ Buffer b( ( *it ).data );
+ while ( b.length() >= 4 )
+ {
+ TLV t = b.getTLV();
+ switch( t.type )
+ {
+ case 0x0003:
+ language = t.data;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "language: " << language << endl;
+ break;
+ case 0x0002:
+ encoding = t.data;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "encoding: " << encoding << endl;
+ break;
+ case 0x0001:
+ message = t.data;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message: " << message << endl;
+ break;
+ }
+ }
+ }
+ break;
+ case 0x0003: //user info
+ {
+ Buffer b( ( *it ).data );
+ sender = QString( b.getBUIN() );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got user info. sender is " << sender << endl;
+ }
+ break;
+
+ }
+ }
+
+ QTextCodec* codec = QTextCodec::codecForName( encoding );
+ if ( ! codec )
+ codec = QTextCodec::codecForMib( 4 );
+ QString msgText( codec->toUnicode( message ) );
+ Oscar::Message omessage;
+ omessage.setReceiver( client()->userId() );
+ omessage.setSender( sender );
+ omessage.setTimestamp( QDateTime::currentDateTime() );
+ omessage.setText( Oscar::Message::UTF8, msgText );
+ omessage.setType( 0x03 );
+ omessage.setExchange( m_exchange );
+ omessage.setChatRoom( m_room );
+ emit newChatMessage( omessage );
+}
+
+void ChatServiceTask::parseChatError()
+{
+
+}
+
+
+#include "chatservicetask.moc"
+
diff --git a/kopete/protocols/oscar/liboscar/chatservicetask.h b/kopete/protocols/oscar/liboscar/chatservicetask.h
new file mode 100644
index 00000000..90e29300
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/chatservicetask.h
@@ -0,0 +1,65 @@
+// Kopete Oscar Protocol - Chat service handling
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+
+#ifndef CHATSERVICETASK_H
+#define CHATSERVICETASK_H
+
+#include "task.h"
+#include "oscarmessage.h"
+
+class Transfer;
+
+class ChatServiceTask : public Task
+{
+Q_OBJECT
+public:
+ ChatServiceTask( Task* parent, Oscar::WORD exchange, const QString& room );
+ ~ChatServiceTask();
+
+ void onGo();
+ bool take( Transfer* t );
+
+ void parseRoomInfo();
+
+ void parseJoinNotification();
+ void parseLeftNotification();
+
+ void parseChatMessage();
+ void parseChatError();
+
+ void setMessage( const Oscar::Message& msg );
+ void setEncoding( const QCString &enc );
+
+signals:
+ void userJoinedChat( Oscar::WORD, const QString& r, const QString& u );
+ void userLeftChat( Oscar::WORD, const QString& r, const QString& u );
+ void newChatMessage( const Oscar::Message& msg );
+
+protected:
+ bool forMe( const Transfer* t ) const;
+
+private:
+ WORD m_exchange;
+ QString m_room;
+ QString m_internalRoom;
+ Oscar::Message m_message;
+ QCString m_encoding;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/client.cpp b/kopete/protocols/oscar/liboscar/client.cpp
new file mode 100644
index 00000000..af967512
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/client.cpp
@@ -0,0 +1,1353 @@
+/*
+ client.cpp - Kopete Oscar Protocol
+
+ Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "client.h"
+
+#include <qtimer.h>
+#include <qtextcodec.h>
+
+#include <kdebug.h> //for kdDebug()
+#include <klocale.h>
+
+#include "buddyicontask.h"
+#include "clientreadytask.h"
+#include "connectionhandler.h"
+#include "changevisibilitytask.h"
+#include "chatnavservicetask.h"
+#include "errortask.h"
+#include "icquserinfo.h"
+#include "icquserinfotask.h"
+#include "logintask.h"
+#include "connection.h"
+#include "messagereceivertask.h"
+#include "onlinenotifiertask.h"
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+#include "oscarsettings.h"
+#include "oscarutils.h"
+#include "ownuserinfotask.h"
+#include "profiletask.h"
+#include "senddcinfotask.h"
+#include "sendmessagetask.h"
+#include "serverredirecttask.h"
+#include "servicesetuptask.h"
+#include "ssimanager.h"
+#include "ssimodifytask.h"
+#include "ssiauthtask.h"
+#include "offlinemessagestask.h"
+#include "task.h"
+#include "typingnotifytask.h"
+#include "userinfotask.h"
+#include "usersearchtask.h"
+#include "warningtask.h"
+#include "chatservicetask.h"
+#include "rateclassmanager.h"
+
+
+namespace
+{
+ class DefaultCodecProvider : public Client::CodecProvider
+ {
+ public:
+ virtual QTextCodec* codecForContact( const QString& ) const
+ {
+ return QTextCodec::codecForMib( 4 );
+ }
+ virtual QTextCodec* codecForAccount() const
+ {
+ return QTextCodec::codecForMib( 4 );
+ }
+ };
+
+ DefaultCodecProvider defaultCodecProvider;
+}
+
+class Client::ClientPrivate
+{
+public:
+ ClientPrivate() {}
+
+ QString host, user, pass;
+ uint port;
+ int tzoffset;
+ bool active;
+
+ enum { StageOne, StageTwo };
+ int stage;
+
+ //Protocol specific data
+ bool isIcq;
+ bool redirectRequested;
+ QValueList<WORD> redirectionServices;
+ WORD currentRedirect;
+ QByteArray cookie;
+ DWORD connectAsStatus; // icq only
+ QString connectWithMessage; // icq only
+ Oscar::Settings* settings;
+
+ //Tasks
+ ErrorTask* errorTask;
+ OnlineNotifierTask* onlineNotifier;
+ OwnUserInfoTask* ownStatusTask;
+ MessageReceiverTask* messageReceiverTask;
+ SSIAuthTask* ssiAuthTask;
+ ICQUserInfoRequestTask* icqInfoTask;
+ UserInfoTask* userInfoTask;
+ TypingNotifyTask * typingNotifyTask;
+ SSIModifyTask* ssiModifyTask;
+ //Managers
+ SSIManager* ssiManager;
+ ConnectionHandler connections;
+
+ //Our Userinfo
+ UserDetails ourDetails;
+
+ //Infos
+ QValueList<int> exchanges;
+
+ QString statusMessage; // for away-,DND-message etc...
+
+ //away messages
+ struct AwayMsgRequest
+ {
+ QString contact;
+ ICQStatus contactStatus;
+ };
+ QValueList<AwayMsgRequest> awayMsgRequestQueue;
+ QTimer* awayMsgRequestTimer;
+ CodecProvider* codecProvider;
+
+ const Oscar::ClientVersion* version;
+};
+
+Client::Client( QObject* parent )
+:QObject( parent, "oscarclient" )
+{
+ m_loginTask = 0L;
+ m_loginTaskTwo = 0L;
+
+ d = new ClientPrivate;
+ d->tzoffset = 0;
+ d->active = false;
+ d->isIcq = false; //default to AIM
+ d->redirectRequested = false;
+ d->currentRedirect = 0;
+ d->connectAsStatus = 0x0; // default to online
+ d->ssiManager = new SSIManager( this );
+ d->settings = new Oscar::Settings();
+ d->errorTask = 0L;
+ d->onlineNotifier = 0L;
+ d->ownStatusTask = 0L;
+ d->messageReceiverTask = 0L;
+ d->ssiAuthTask = 0L;
+ d->icqInfoTask = 0L;
+ d->userInfoTask = 0L;
+ d->stage = ClientPrivate::StageOne;
+ d->typingNotifyTask = 0L;
+ d->ssiModifyTask = 0L;
+ d->awayMsgRequestTimer = new QTimer();
+ d->codecProvider = &defaultCodecProvider;
+
+ connect( this, SIGNAL( redirectionFinished( WORD ) ),
+ this, SLOT( checkRedirectionQueue( WORD ) ) );
+ connect( d->awayMsgRequestTimer, SIGNAL( timeout() ),
+ this, SLOT( nextICQAwayMessageRequest() ) );
+}
+
+Client::~Client()
+{
+
+ //delete the connections differently than in deleteConnections()
+ //deleteLater() seems to cause destruction order issues
+ deleteStaticTasks();
+ delete d->settings;
+ delete d->ssiManager;
+ delete d->awayMsgRequestTimer;
+ delete d;
+}
+
+Oscar::Settings* Client::clientSettings() const
+{
+ return d->settings;
+}
+
+void Client::connectToServer( Connection *c, const QString& server, bool auth )
+{
+ d->connections.append( c );
+ if ( auth == true )
+ {
+ m_loginTask = new StageOneLoginTask( c->rootTask() );
+ connect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
+ }
+
+ connect( c, SIGNAL( socketError( int, const QString& ) ), this, SLOT( determineDisconnection( int, const QString& ) ) );
+ c->connectToServer(server, auth);
+}
+
+void Client::start( const QString &host, const uint port, const QString &userId, const QString &pass )
+{
+ Q_UNUSED( host );
+ Q_UNUSED( port );
+ d->user = userId;
+ d->pass = pass;
+ d->stage = ClientPrivate::StageOne;
+ d->active = false;
+}
+
+void Client::close()
+{
+ d->active = false;
+ d->awayMsgRequestTimer->stop();
+ d->awayMsgRequestQueue.clear();
+ d->connections.clear();
+ deleteStaticTasks();
+
+ //don't clear the stored status between stage one and two
+ if ( d->stage == ClientPrivate::StageTwo )
+ {
+ d->connectAsStatus = 0x0;
+ d->connectWithMessage = QString::null;
+ }
+
+ d->exchanges.clear();
+ d->redirectRequested = false;
+ d->currentRedirect = 0;
+ d->redirectionServices.clear();
+ d->ssiManager->clear();
+}
+
+void Client::setStatus( AIMStatus status, const QString &_message )
+{
+ // AIM: you're away exactly when your away message isn't empty.
+ // can't use QString::null as a message either; ProfileTask
+ // interprets null as "don't change".
+ QString message;
+ if ( status == Online )
+ message = QString::fromAscii("");
+ else
+ {
+ if ( _message.isEmpty() )
+ message = QString::fromAscii(" ");
+ else
+ message = _message;
+ }
+
+ Connection* c = d->connections.connectionForFamily( 0x0002 );
+ if ( !c )
+ return;
+ ProfileTask* pt = new ProfileTask( c->rootTask() );
+ pt->setAwayMessage( message );
+ pt->go( true );
+}
+
+void Client::setStatus( DWORD status, const QString &message )
+{
+ // remember the message to reply with, when requested
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting status message to "<< message << endl;
+ d->statusMessage = message;
+ // ICQ: if we're active, set status. otherwise, just store the status for later.
+ if ( d->active )
+ {
+ //the first connection is always the BOS connection
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return; //TODO trigger an error of some sort?
+
+ ChangeVisibilityTask* cvt = new ChangeVisibilityTask( c->rootTask() );
+ if ( ( status & 0x0100 ) == 0x0100 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting invisible" << endl;
+ cvt->setVisible( false );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Setting visible" << endl;
+ cvt->setVisible( true );
+ }
+ cvt->go( true );
+ c = d->connections.connectionForFamily( 0x0002 );
+ if ( !c )
+ return;
+
+ SendDCInfoTask* sdcit = new SendDCInfoTask( c->rootTask(), status );
+ sdcit->go( true ); //autodelete
+ // TODO: send away message
+ }
+ else
+ {
+ d->connectAsStatus = status;
+ d->connectWithMessage = message;
+ }
+}
+
+UserDetails Client::ourInfo() const
+{
+ return d->ourDetails;
+}
+
+QString Client::host()
+{
+ return d->host;
+}
+
+int Client::port()
+{
+ return d->port;
+}
+
+SSIManager* Client::ssiManager() const
+{
+ return d->ssiManager;
+}
+
+const Oscar::ClientVersion* Client::version() const
+{
+ return d->version;
+}
+
+// SLOTS //
+
+void Client::streamConnected()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ d->stage = ClientPrivate::StageTwo;
+ if ( m_loginTaskTwo )
+ m_loginTaskTwo->go();
+}
+
+void Client::lt_loginFinished()
+{
+ /* Check for stage two login first, since we create the stage two
+ * task when we finish stage one
+ */
+ if ( d->stage == ClientPrivate::StageTwo )
+ {
+ //we've finished logging in. start the services setup
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage two done. setting up services" << endl;
+ initializeStaticTasks();
+ ServiceSetupTask* ssTask = new ServiceSetupTask( d->connections.defaultConnection()->rootTask() );
+ connect( ssTask, SIGNAL( finished() ), this, SLOT( serviceSetupFinished() ) );
+ ssTask->go( true ); //fire and forget
+ m_loginTaskTwo->deleteLater();
+ m_loginTaskTwo = 0;
+ }
+ else if ( d->stage == ClientPrivate::StageOne )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "stage one login done" << endl;
+ disconnect( m_loginTask, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
+
+ if ( m_loginTask->statusCode() == 0 ) //we can start stage two
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no errors from stage one. moving to stage two" << endl;
+
+ //cache these values since they'll be deleted when we close the connections (which deletes the tasks)
+ d->host = m_loginTask->bosServer();
+ d->port = m_loginTask->bosPort().toUInt();
+ d->cookie = m_loginTask->loginCookie();
+ close();
+ QTimer::singleShot( 100, this, SLOT(startStageTwo() ) );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "errors reported. not moving to stage two" << endl;
+ close(); //deletes the connections for us
+ }
+
+ m_loginTask->deleteLater();
+ m_loginTask = 0;
+ }
+
+}
+
+void Client::startStageTwo()
+{
+ //create a new connection and set it up
+ Connection* c = createConnection( d->host, QString::number( d->port ) );
+ new CloseConnectionTask( c->rootTask() );
+
+ //create the new login task
+ m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
+ m_loginTaskTwo->setCookie( d->cookie );
+ QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( lt_loginFinished() ) );
+
+
+ //connect
+ QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
+ connectToServer( c, d->host, false ) ;
+
+}
+
+void Client::serviceSetupFinished()
+{
+ d->active = true;
+
+ if ( isIcq() )
+ setStatus( d->connectAsStatus, d->connectWithMessage );
+
+ d->ownStatusTask->go();
+
+ if ( isIcq() )
+ {
+ //retrieve offline messages
+ Connection* c = d->connections.connectionForFamily( 0x0015 );
+ if ( !c )
+ return;
+
+ OfflineMessagesTask *offlineMsgTask = new OfflineMessagesTask( c->rootTask() );
+ connect( offlineMsgTask, SIGNAL( receivedOfflineMessage(const Oscar::Message& ) ),
+ this, SIGNAL( messageReceived(const Oscar::Message& ) ) );
+ offlineMsgTask->go( true );
+ }
+
+ emit haveSSIList();
+ emit loggedIn();
+}
+
+void Client::receivedIcqInfo( const QString& contact, unsigned int type )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "received icq info for " << contact
+ << " of type " << type << endl;
+
+ if ( type == ICQUserInfoRequestTask::Short )
+ emit receivedIcqShortInfo( contact );
+ else
+ emit receivedIcqLongInfo( contact );
+}
+
+void Client::receivedInfo( Q_UINT16 sequence )
+{
+ UserDetails details = d->userInfoTask->getInfoFor( sequence );
+ emit receivedUserInfo( details.userId(), details );
+}
+
+void Client::offlineUser( const QString& user, const UserDetails& )
+{
+ emit userIsOffline( user );
+}
+
+void Client::haveOwnUserInfo()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << endl;
+ UserDetails ud = d->ownStatusTask->getInfo();
+ d->ourDetails = ud;
+ emit haveOwnInfo();
+}
+
+void Client::setCodecProvider( Client::CodecProvider* codecProvider )
+{
+ d->codecProvider = codecProvider;
+}
+
+void Client::setVersion( const Oscar::ClientVersion* version )
+{
+ d->version = version;
+}
+
+// INTERNALS //
+
+QString Client::userId() const
+{
+ return d->user;
+}
+
+QString Client::password() const
+{
+ return d->pass;
+}
+
+QString Client::statusMessage() const
+{
+ return d->statusMessage;
+}
+
+void Client::setStatusMessage( const QString &message )
+{
+ d->statusMessage = message;
+}
+
+QCString Client::ipAddress() const
+{
+ //!TODO determine ip address
+ return "127.0.0.1";
+}
+
+void Client::notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal )
+{
+ emit taskError( s, errCode, fatal );
+}
+
+void Client::notifySocketError( int errCode, const QString& msg )
+{
+ emit socketError( errCode, msg );
+}
+
+void Client::sendMessage( const Oscar::Message& msg, bool isAuto)
+{
+ Connection* c = 0L;
+ if ( msg.type() == 0x0003 )
+ {
+ c = d->connections.connectionForChatRoom( msg.exchange(), msg.chatRoom() );
+ if ( !c )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending message to chat room" << endl;
+ ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), msg.exchange(), msg.chatRoom() );
+ cst->setMessage( msg );
+ cst->setEncoding( d->codecProvider->codecForAccount()->name() );
+ cst->go( true );
+ }
+ else
+ {
+ c = d->connections.connectionForFamily( 0x0004 );
+ if ( !c )
+ return;
+ SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
+ // Set whether or not the message is an automated response
+ sendMsgTask->setAutoResponse( isAuto );
+ sendMsgTask->setMessage( msg );
+ sendMsgTask->go( true );
+ }
+}
+
+void Client::receivedMessage( const Oscar::Message& msg )
+{
+ if ( msg.type() == 2 && !msg.hasProperty( Oscar::Message::AutoResponse ) )
+ {
+ // type 2 message needs an autoresponse, regardless of type
+ Connection* c = d->connections.connectionForFamily( 0x0004 );
+ if ( !c )
+ return;
+
+ Oscar::Message response ( msg );
+ if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
+ {
+ QTextCodec* codec = d->codecProvider->codecForContact( msg.sender() );
+ response.setText( Oscar::Message::UserDefined, statusMessage(), codec );
+ }
+ else
+ {
+ response.setEncoding( Oscar::Message::UserDefined );
+ response.setTextArray( QByteArray() );
+ }
+ response.setReceiver( msg.sender() );
+ response.addProperty( Oscar::Message::AutoResponse );
+ SendMessageTask *sendMsgTask = new SendMessageTask( c->rootTask() );
+ sendMsgTask->setMessage( response );
+ sendMsgTask->go( true );
+ }
+ if ( msg.hasProperty( Oscar::Message::StatusMessageRequest ) )
+ {
+ if ( msg.hasProperty( Oscar::Message::AutoResponse ) )
+ {
+ // we got a response to a status message request.
+ QString awayMessage( msg.text( d->codecProvider->codecForContact( msg.sender() ) ) );
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received an away message: " << awayMessage << endl;
+ emit receivedAwayMessage( msg.sender(), awayMessage );
+ }
+ }
+ else if ( ! msg.hasProperty( Oscar::Message::AutoResponse ) )
+ {
+ // Filter out miranda's invisible check
+ if ( msg.messageType() == 0x0004 && msg.textArray().isEmpty() )
+ return;
+
+ // let application handle it
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Emitting receivedMessage" << endl;
+ emit messageReceived( msg );
+ }
+}
+
+void Client::requestAuth( const QString& contactid, const QString& reason )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+ d->ssiAuthTask->sendAuthRequest( contactid, reason );
+}
+
+void Client::sendAuth( const QString& contactid, const QString& reason, bool auth )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+ d->ssiAuthTask->sendAuthReply( contactid, reason, auth );
+}
+
+bool Client::isActive() const
+{
+ return d->active;
+}
+
+bool Client::isIcq() const
+{
+ return d->isIcq;
+}
+
+void Client::setIsIcq( bool isIcq )
+{
+ d->isIcq = isIcq;
+}
+
+void Client::debug( const QString& str )
+{
+ Q_UNUSED(str);
+// qDebug( "CLIENT: %s", str.ascii() );
+}
+
+void Client::initializeStaticTasks()
+{
+ //set up the extra tasks
+ Connection* c = d->connections.defaultConnection();
+ if ( !c )
+ return;
+ d->errorTask = new ErrorTask( c->rootTask() );
+ d->onlineNotifier = new OnlineNotifierTask( c->rootTask() );
+ d->ownStatusTask = new OwnUserInfoTask( c->rootTask() );
+ d->messageReceiverTask = new MessageReceiverTask( c->rootTask() );
+ d->ssiAuthTask = new SSIAuthTask( c->rootTask() );
+ d->icqInfoTask = new ICQUserInfoRequestTask( c->rootTask() );
+ d->userInfoTask = new UserInfoTask( c->rootTask() );
+ d->typingNotifyTask = new TypingNotifyTask( c->rootTask() );
+ d->ssiModifyTask = new SSIModifyTask( c->rootTask(), true );
+
+ connect( d->onlineNotifier, SIGNAL( userIsOnline( const QString&, const UserDetails& ) ),
+ this, SIGNAL( receivedUserInfo( const QString&, const UserDetails& ) ) );
+ connect( d->onlineNotifier, SIGNAL( userIsOffline( const QString&, const UserDetails& ) ),
+ this, SLOT( offlineUser( const QString&, const UserDetails & ) ) );
+
+ connect( d->ownStatusTask, SIGNAL( gotInfo() ), this, SLOT( haveOwnUserInfo() ) );
+ connect( d->ownStatusTask, SIGNAL( buddyIconUploadRequested() ), this,
+ SIGNAL( iconNeedsUploading() ) );
+
+ connect( d->messageReceiverTask, SIGNAL( receivedMessage( const Oscar::Message& ) ),
+ this, SLOT( receivedMessage( const Oscar::Message& ) ) );
+
+ connect( d->ssiAuthTask, SIGNAL( authRequested( const QString&, const QString& ) ),
+ this, SIGNAL( authRequestReceived( const QString&, const QString& ) ) );
+ connect( d->ssiAuthTask, SIGNAL( authReplied( const QString&, const QString&, bool ) ),
+ this, SIGNAL( authReplyReceived( const QString&, const QString&, bool ) ) );
+
+ connect( d->icqInfoTask, SIGNAL( receivedInfoFor( const QString&, unsigned int ) ),
+ this, SLOT( receivedIcqInfo( const QString&, unsigned int ) ) );
+
+ connect( d->userInfoTask, SIGNAL( receivedProfile( const QString&, const QString& ) ),
+ this, SIGNAL( receivedProfile( const QString&, const QString& ) ) );
+ connect( d->userInfoTask, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ),
+ this, SIGNAL( receivedAwayMessage( const QString&, const QString& ) ) );
+ connect( d->typingNotifyTask, SIGNAL( typingStarted( const QString& ) ),
+ this, SIGNAL( userStartedTyping( const QString& ) ) );
+ connect( d->typingNotifyTask, SIGNAL( typingFinished( const QString& ) ),
+ this, SIGNAL( userStoppedTyping( const QString& ) ) );
+}
+
+void Client::removeGroup( const QString& groupName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing group " << groupName << " from SSI" << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->removeGroup( groupName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::addGroup( const QString& groupName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group " << groupName << " to SSI" << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->addGroup( groupName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::addContact( const QString& contactName, const QString& groupName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact " << contactName << " to SSI in group " << groupName << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->addContact( contactName, groupName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::removeContact( const QString& contactName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Removing contact " << contactName << " from SSI" << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->removeContact( contactName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::renameGroup( const QString & oldGroupName, const QString & newGroupName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Renaming group " << oldGroupName << " to " << newGroupName << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->renameGroup( oldGroupName, newGroupName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem )
+{
+ int action = 0; //0 modify, 1 add, 2 remove TODO cleanup!
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ if ( !oldItem && newItem )
+ action = 1;
+ if ( oldItem && !newItem )
+ action = 2;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Add/Mod/Del item on server" << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ switch ( action )
+ {
+ case 0:
+ if ( ssimt->modifyItem( oldItem, newItem ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+ break;
+ case 1:
+ if ( ssimt->addItem( newItem ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+ break;
+ case 2:
+ if ( ssimt->removeItem( oldItem ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+ break;
+ }
+}
+
+void Client::changeContactGroup( const QString& contact, const QString& newGroupName )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0013 );
+ if ( !c )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Changing " << contact << "'s group to "
+ << newGroupName << endl;
+ SSIModifyTask* ssimt = new SSIModifyTask( c->rootTask() );
+ if ( ssimt->changeGroup( contact, newGroupName ) )
+ ssimt->go( true );
+ else
+ delete ssimt;
+}
+
+void Client::requestFullInfo( const QString& contactId )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0015 );
+ if ( !c )
+ return;
+ d->icqInfoTask->setUser( contactId );
+ d->icqInfoTask->setType( ICQUserInfoRequestTask::Long );
+ d->icqInfoTask->go();
+}
+
+void Client::requestShortInfo( const QString& contactId )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0015 );
+ if ( !c )
+ return;
+ d->icqInfoTask->setUser( contactId );
+ d->icqInfoTask->setType( ICQUserInfoRequestTask::Short );
+ d->icqInfoTask->go();
+}
+
+void Client::sendWarning( const QString& contact, bool anonymous )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0004 );
+ if ( !c )
+ return;
+ WarningTask* warnTask = new WarningTask( c->rootTask() );
+ warnTask->setContact( contact );
+ warnTask->setAnonymous( anonymous );
+ QObject::connect( warnTask, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ),
+ this, SIGNAL( userWarned( const QString&, Q_UINT16, Q_UINT16 ) ) );
+ warnTask->go( true );
+}
+
+ICQGeneralUserInfo Client::getGeneralInfo( const QString& contact )
+{
+ return d->icqInfoTask->generalInfoFor( contact );
+}
+
+ICQWorkUserInfo Client::getWorkInfo( const QString& contact )
+{
+ return d->icqInfoTask->workInfoFor( contact );
+}
+
+ICQEmailInfo Client::getEmailInfo( const QString& contact )
+{
+ return d->icqInfoTask->emailInfoFor( contact );
+}
+
+ICQMoreUserInfo Client::getMoreInfo( const QString& contact )
+{
+ return d->icqInfoTask->moreInfoFor( contact );
+}
+
+ICQInterestInfo Client::getInterestInfo( const QString& contact )
+{
+ return d->icqInfoTask->interestInfoFor( contact );
+}
+
+ICQShortInfo Client::getShortInfo( const QString& contact )
+{
+ return d->icqInfoTask->shortInfoFor( contact );
+}
+
+QValueList<int> Client::chatExchangeList() const
+{
+ return d->exchanges;
+}
+
+void Client::setChatExchangeList( const QValueList<int>& exchanges )
+{
+ d->exchanges = exchanges;
+}
+
+void Client::requestAIMProfile( const QString& contact )
+{
+ d->userInfoTask->requestInfoFor( contact, UserInfoTask::Profile );
+}
+
+void Client::requestAIMAwayMessage( const QString& contact )
+{
+ d->userInfoTask->requestInfoFor( contact, UserInfoTask::AwayMessage );
+}
+
+void Client::requestICQAwayMessage( const QString& contact, ICQStatus contactStatus )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting away message for " << contact << endl;
+ Oscar::Message msg;
+ msg.setType( 2 );
+ msg.setReceiver( contact );
+ msg.addProperty( Oscar::Message::StatusMessageRequest );
+ switch ( contactStatus )
+ {
+ case ICQAway:
+ msg.setMessageType( 0xE8 ); // away
+ break;
+ case ICQOccupied:
+ msg.setMessageType( 0xE9 ); // occupied
+ break;
+ case ICQNotAvailable:
+ msg.setMessageType( 0xEA ); // not awailable
+ break;
+ case ICQDoNotDisturb:
+ msg.setMessageType( 0xEB ); // do not disturb
+ break;
+ case ICQFreeForChat:
+ msg.setMessageType( 0xEC ); // free for chat
+ break;
+ default:
+ // may be a good way to deal with possible error and lack of online status message?
+ emit receivedAwayMessage( contact, "Sorry, this protocol does not support this type of status message" );
+ return;
+ }
+ sendMessage( msg );
+}
+
+void Client::addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding away message request for "
+ << contact << " to queue" << endl;
+
+ //remove old request if still exists
+ removeICQAwayMessageRequest( contact );
+
+ ClientPrivate::AwayMsgRequest amr = { contact, contactStatus };
+ d->awayMsgRequestQueue.prepend( amr );
+
+ if ( !d->awayMsgRequestTimer->isActive() )
+ d->awayMsgRequestTimer->start( 1000 );
+}
+
+void Client::removeICQAwayMessageRequest( const QString& contact )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "removing away message request for "
+ << contact << " from queue" << endl;
+
+ QValueList<ClientPrivate::AwayMsgRequest>::iterator it = d->awayMsgRequestQueue.begin();
+ while ( it != d->awayMsgRequestQueue.end() )
+ {
+ if ( (*it).contact == contact )
+ it = d->awayMsgRequestQueue.erase( it );
+ else
+ it++;
+ }
+}
+
+void Client::nextICQAwayMessageRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "request queue count " << d->awayMsgRequestQueue.count() << endl;
+
+ if ( d->awayMsgRequestQueue.empty() )
+ {
+ d->awayMsgRequestTimer->stop();
+ return;
+ }
+ else
+ {
+ Connection* c = d->connections.connectionForFamily( 0x0004 );
+ if ( !c )
+ return;
+
+ SNAC s = { 0x0004, 0x0006, 0x0000, 0x00000000 };
+ //get time needed to restore level to initial
+ //for some reason when we are long under initial level
+ //icq server will start to block our messages
+ int time = c->rateManager()->timeToInitialLevel( s );
+ if ( time > 0 )
+ {
+ d->awayMsgRequestTimer->changeInterval( time );
+ return;
+ }
+ else
+ {
+ d->awayMsgRequestTimer->changeInterval( 5000 );
+ }
+ }
+
+ ClientPrivate::AwayMsgRequest amr;
+
+ amr = d->awayMsgRequestQueue.back();
+ d->awayMsgRequestQueue.pop_back();
+ requestICQAwayMessage( amr.contact, amr.contactStatus );
+}
+
+void Client::requestStatusInfo( const QString& contact )
+{
+ d->userInfoTask->requestInfoFor( contact, UserInfoTask::General );
+}
+
+void Client::whitePagesSearch( const ICQWPSearchInfo& info )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0015 );
+ if ( !c )
+ return;
+ UserSearchTask* ust = new UserSearchTask( c->rootTask() );
+ connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ),
+ this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
+ connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
+ ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works
+ ust->searchWhitePages( info );
+}
+
+void Client::uinSearch( const QString& uin )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0015 );
+ if ( !c )
+ return;
+ UserSearchTask* ust = new UserSearchTask( c->rootTask() );
+ connect( ust, SIGNAL( foundUser( const ICQSearchResult& ) ),
+ this, SIGNAL( gotSearchResults( const ICQSearchResult& ) ) );
+ connect( ust, SIGNAL( searchFinished( int ) ), this, SIGNAL( endOfSearch( int ) ) );
+ ust->go( true ); //onGo does nothing in this task. This is just here so autodelete works
+ ust->searchUserByUIN( uin );
+}
+
+void Client::updateProfile( const QString& profile )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0002 );
+ if ( !c )
+ return;
+ ProfileTask* pt = new ProfileTask( c->rootTask() );
+ pt->setProfileText( profile );
+ pt->go(true);
+}
+
+void Client::sendTyping( const QString & contact, bool typing )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0004 );
+ if ( !c )
+ return;
+ d->typingNotifyTask->setParams( contact, ( typing ? TypingNotifyTask::Begin : TypingNotifyTask::Finished ) );
+ d->typingNotifyTask->go( false ); // don't delete the task after sending
+}
+
+void Client::connectToIconServer()
+{
+ Connection* c = d->connections.connectionForFamily( 0x0010 );
+ if ( c )
+ return;
+
+ requestServerRedirect( 0x0010 );
+ return;
+}
+
+void Client::setIgnore( const QString& user, bool ignore )
+{
+ Oscar::SSI item = ssiManager()->findItem( user, ROSTER_IGNORE );
+ if ( item && !ignore )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from ignore list" << endl;
+ this->modifySSIItem( item, Oscar::SSI() );
+ }
+ else if ( !item && ignore )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to ignore list" << endl;
+ Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_IGNORE, QValueList<TLV>() );
+ this->modifySSIItem( Oscar::SSI(), s );
+ }
+}
+
+void Client::setVisibleTo( const QString& user, bool visible )
+{
+ Oscar::SSI item = ssiManager()->findItem( user, ROSTER_VISIBLE );
+ if ( item && !visible )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from visible list" << endl;
+ this->modifySSIItem( item, Oscar::SSI() );
+ }
+ else if ( !item && visible )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to visible list" << endl;
+ Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_VISIBLE, QValueList<TLV>() );
+ this->modifySSIItem( Oscar::SSI(), s );
+ }
+}
+
+void Client::setInvisibleTo( const QString& user, bool invisible )
+{
+ Oscar::SSI item = ssiManager()->findItem( user, ROSTER_INVISIBLE );
+ if ( item && !invisible )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << user << " from invisible list" << endl;
+ this->modifySSIItem( item, Oscar::SSI() );
+ }
+ else if ( !item && invisible )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << user << " to invisible list" << endl;
+ Oscar::SSI s( user, 0, ssiManager()->nextContactId(), ROSTER_INVISIBLE, QValueList<TLV>() );
+ this->modifySSIItem( Oscar::SSI(), s );
+ }
+}
+
+void Client::requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0010 );
+ if ( !c )
+ return;
+
+ BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
+ connect( bit, SIGNAL( haveIcon( const QString&, QByteArray ) ),
+ this, SIGNAL( haveIconForContact( const QString&, QByteArray ) ) );
+ bit->requestIconFor( user );
+ bit->setHashType( hashType );
+ bit->setHash( hash );
+ bit->go( true );
+}
+
+void Client::requestServerRedirect( WORD family, WORD exchange,
+ QByteArray cookie, WORD instance,
+ const QString& room )
+{
+ //making the assumption that family 2 will always be the BOS connection
+ //use it instead since we can't query for family 1
+ Connection* c = d->connections.connectionForFamily( family );
+ if ( c && family != 0x000E )
+ return; //we already have the connection
+
+ c = d->connections.connectionForFamily( 0x0002 );
+ if ( !c )
+ return;
+
+ if ( d->redirectionServices.findIndex( family ) == -1 )
+ d->redirectionServices.append( family ); //don't add families twice
+
+ if ( d->currentRedirect != 0 )
+ return; //we're already doing one redirection
+
+ d->currentRedirect = family;
+
+ //FIXME. this won't work if we have to defer the connection because we're
+ //already connecting to something
+ ServerRedirectTask* srt = new ServerRedirectTask( c->rootTask() );
+ if ( family == 0x000E )
+ {
+ srt->setChatParams( exchange, cookie, instance );
+ srt->setChatRoom( room );
+ }
+
+ connect( srt, SIGNAL( haveServer( const QString&, const QByteArray&, WORD ) ),
+ this, SLOT( haveServerForRedirect( const QString&, const QByteArray&, WORD ) ) );
+ srt->setService( family );
+ srt->go( true );
+}
+
+void Client::haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD )
+{
+ //nasty sender() usage to get the task with chat room info
+ QObject* o = const_cast<QObject*>( sender() );
+ ServerRedirectTask* srt = dynamic_cast<ServerRedirectTask*>( o );
+
+ //create a new connection and set it up
+ int colonPos = host.find(':');
+ QString realHost, realPort;
+ if ( colonPos != -1 )
+ {
+ realHost = host.left( colonPos );
+ realPort = host.right(4); //we only need 4 bytes
+ }
+ else
+ {
+ realHost = host;
+ realPort = QString::fromLatin1("5190");
+ }
+
+ Connection* c = createConnection( realHost, realPort );
+ //create the new login task
+ m_loginTaskTwo = new StageTwoLoginTask( c->rootTask() );
+ m_loginTaskTwo->setCookie( cookie );
+ QObject::connect( m_loginTaskTwo, SIGNAL( finished() ), this, SLOT( serverRedirectFinished() ) );
+
+ //connect
+ connectToServer( c, d->host, false );
+ QObject::connect( c, SIGNAL( connected() ), this, SLOT( streamConnected() ) );
+
+ if ( srt )
+ d->connections.addChatInfoForConnection( c, srt->chatExchange(), srt->chatRoomName() );
+}
+
+void Client::serverRedirectFinished()
+{
+ if ( m_loginTaskTwo->statusCode() == 0 )
+ { //stage two was successful
+ Connection* c = d->connections.connectionForFamily( d->currentRedirect );
+ if ( !c )
+ return;
+ ClientReadyTask* crt = new ClientReadyTask( c->rootTask() );
+ crt->setFamilies( c->supportedFamilies() );
+ crt->go( true );
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "redirection finished for service "
+ << d->currentRedirect << endl;
+
+ if ( d->currentRedirect == 0x0010 )
+ emit iconServerConnected();
+
+ if ( d->currentRedirect == 0x000D )
+ {
+ connect( this, SIGNAL( chatNavigationConnected() ),
+ this, SLOT( requestChatNavLimits() ) );
+ emit chatNavigationConnected();
+ }
+
+ if ( d->currentRedirect == 0x000E )
+ {
+ //HACK! such abuse! think of a better way
+ if ( !m_loginTaskTwo )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "no login task to get connection from!" << endl;
+ emit redirectionFinished( d->currentRedirect );
+ return;
+ }
+
+ Connection* c = m_loginTaskTwo->client();
+ QString roomName = d->connections.chatRoomForConnection( c );
+ WORD exchange = d->connections.exchangeForConnection( c );
+ if ( c )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting up chat connection" << endl;
+ ChatServiceTask* cst = new ChatServiceTask( c->rootTask(), exchange, roomName );
+ connect( cst, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ),
+ this, SIGNAL( userJoinedChat( Oscar::WORD, const QString&, const QString& ) ) );
+ connect( cst, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ),
+ this, SIGNAL( userLeftChat( Oscar::WORD, const QString&, const QString& ) ) );
+ connect( cst, SIGNAL( newChatMessage( const Oscar::Message& ) ),
+ this, SIGNAL( messageReceived( const Oscar::Message& ) ) );
+ }
+ emit chatRoomConnected( exchange, roomName );
+ }
+
+ emit redirectionFinished( d->currentRedirect );
+
+}
+
+void Client::checkRedirectionQueue( WORD family )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checking redirection queue" << endl;
+ d->redirectionServices.remove( family );
+ d->currentRedirect = 0;
+ if ( !d->redirectionServices.isEmpty() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "scheduling new redirection" << endl;
+ requestServerRedirect( d->redirectionServices.front() );
+ }
+}
+
+
+void Client::requestChatNavLimits()
+{
+ Connection* c = d->connections.connectionForFamily( 0x000D );
+ if ( !c )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "requesting chat nav service limits" << endl;
+ ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
+ cnst->setRequestType( ChatNavServiceTask::Limits );
+ QObject::connect( cnst, SIGNAL( haveChatExchanges( const QValueList<int>& ) ),
+ this, SLOT( setChatExchangeList( const QValueList<int>& ) ) );
+ cnst->go( true ); //autodelete
+
+}
+
+void Client::determineDisconnection( int code, const QString& string )
+{
+ if ( !sender() )
+ return;
+
+ //yay for the sender() hack!
+ QObject* obj = const_cast<QObject*>( sender() );
+ Connection* c = dynamic_cast<Connection*>( obj );
+ if ( !c )
+ return;
+
+ if ( c->isSupported( 0x0002 ) ||
+ d->stage == ClientPrivate::StageOne ) //emit on login
+ {
+ emit socketError( code, string );
+ }
+
+ //connection is deleted. deleteLater() is used
+ d->connections.remove( c );
+ c = 0;
+}
+
+void Client::sendBuddyIcon( const QByteArray& iconData )
+{
+ Connection* c = d->connections.connectionForFamily( 0x0010 );
+ if ( !c )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icon length is " << iconData.size() << endl;
+ BuddyIconTask* bit = new BuddyIconTask( c->rootTask() );
+ bit->uploadIcon( iconData.size(), iconData );
+ bit->go( true );
+}
+
+void Client::joinChatRoom( const QString& roomName, int exchange )
+{
+ Connection* c = d->connections.connectionForFamily( 0x000D );
+ if ( !c )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "joining the chat room '" << roomName
+ << "' on exchange " << exchange << endl;
+ ChatNavServiceTask* cnst = new ChatNavServiceTask( c->rootTask() );
+ connect( cnst, SIGNAL( connectChat( WORD, QByteArray, WORD, const QString& ) ),
+ this, SLOT( setupChatConnection( WORD, QByteArray, WORD, const QString& ) ) );
+ cnst->createRoom( exchange, roomName );
+
+}
+
+void Client::setupChatConnection( WORD exchange, QByteArray cookie, WORD instance, const QString& room )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is:" << cookie << endl;
+ QByteArray realCookie( cookie );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "connection to chat room" << endl;
+ requestServerRedirect( 0x000E, exchange, realCookie, instance, room );
+}
+
+void Client::disconnectChatRoom( WORD exchange, const QString& room )
+{
+ Connection* c = d->connections.connectionForChatRoom( exchange, room );
+ if ( !c )
+ return;
+
+ d->connections.remove( c );
+ c = 0;
+}
+
+
+Connection* Client::createConnection( const QString& host, const QString& port )
+{
+ KNetworkConnector* knc = new KNetworkConnector( 0 );
+ knc->setOptHostPort( host, port.toUInt() );
+ ClientStream* cs = new ClientStream( knc, 0 );
+ cs->setNoopTime( 60000 );
+ Connection* c = new Connection( knc, cs, "BOS" );
+ cs->setConnection( c );
+ c->setClient( this );
+ return c;
+}
+
+void Client::deleteStaticTasks()
+{
+ delete d->errorTask;
+ delete d->onlineNotifier;
+ delete d->ownStatusTask;
+ delete d->messageReceiverTask;
+ delete d->ssiAuthTask;
+ delete d->icqInfoTask;
+ delete d->userInfoTask;
+ delete d->typingNotifyTask;
+ delete d->ssiModifyTask;
+
+ d->errorTask = 0;
+ d->onlineNotifier = 0;
+ d->ownStatusTask = 0;
+ d->messageReceiverTask = 0;
+ d->ssiAuthTask = 0;
+ d->icqInfoTask = 0;
+ d->userInfoTask = 0;
+ d->typingNotifyTask = 0;
+ d->ssiModifyTask = 0;
+}
+
+bool Client::hasIconConnection( ) const
+{
+ Connection* c = d->connections.connectionForFamily( 0x0010 );
+ return c;
+}
+
+#include "client.moc"
+//kate: tab-width 4; indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/client.h b/kopete/protocols/oscar/liboscar/client.h
new file mode 100644
index 00000000..f5dc531e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/client.h
@@ -0,0 +1,521 @@
+/*
+ Kopete Oscar Protocol
+ client.h - The main interface for the Oscar protocol
+
+ Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef LIBOSCAR_CLIENT_H
+#define LIBOSCAR_CLIENT_H
+
+#include <qobject.h>
+#include <qstring.h>
+#include "kopete_export.h"
+#include "rtf2html.h"
+#include "transfer.h"
+#include "icquserinfo.h"
+#include "userdetails.h"
+#include "oscartypeclasses.h"
+#include "oscarmessage.h"
+
+class Connection;
+class StageOneLoginTask;
+class StageTwoLoginTask;
+class SSIManager;
+class UserDetails;
+class QString;
+class Task;
+class QTextCodec;
+
+namespace Oscar
+{
+class Settings;
+}
+
+class KOPETE_EXPORT Client : public QObject
+{
+Q_OBJECT
+
+public:
+
+ class CodecProvider {
+ public:
+ virtual ~CodecProvider() {}
+ virtual QTextCodec* codecForContact( const QString& contactName ) const = 0;
+ virtual QTextCodec* codecForAccount() const = 0;
+ };
+
+ enum ErrorCodes {
+ NoError = 0,
+ NotConnectedError = 1,
+ NonFatalProtocolError = 2,
+ FatalProtocolError = 3
+ };
+
+ enum AIMStatus { Online = 0, Away };
+ enum ICQStatus { ICQOnline = 0, ICQAway, ICQNotAvailable, ICQOccupied, ICQDoNotDisturb, ICQFreeForChat };
+
+ /*************
+ EXTERNAL API
+ *************/
+
+ Client(QObject *parent=0);
+ ~Client();
+
+ /**
+ * Get the settings object for this client instance
+ */
+ Oscar::Settings* clientSettings() const;
+
+ /**
+ * Start a connection to the server using the supplied @ref ClientStream.
+ * This is only a transport layer connection.
+ * @param s initialised connection object 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 indicate whether we're connecting to the authorizer or the bos server
+ */
+ void connectToServer( Connection *c, const QString& server, bool auth = true );
+
+ /**
+ * Start the login process for Oscar
+ * @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.
+ * @param pass The password to use when logging in
+ */
+ void start( const QString &host, const uint port, const QString &userId, const QString &pass );
+
+ /** Logout and disconnect */
+ void close();
+ /** Set our status for AIM */
+ void setStatus( AIMStatus status, const QString &message = QString::null );
+ /** Set our status for ICQ */
+ void setStatus( DWORD status, const QString &message = QString::null );
+
+ /** Retrieve our user info */
+ UserDetails ourInfo() const;
+
+ /**
+ * Remove a group to the contact list
+ * \param groupName the name of the group to remove
+ * \return true if the group removal was successful
+ */
+ void removeGroup( const QString& groupName );
+
+ /**
+ * Add a group from the contact list
+ * \param groupName the name of the group to add
+ * \return true if the group addition was successful
+ */
+ void addGroup( const QString& groupName );
+
+ /**
+ * Add a contact to the contact list
+ * \param contactName the screen name of the new contact to add
+ * \return true if the contact addition was successful
+ */
+ void addContact( const QString& contactName, const QString& groupName );
+
+ /**
+ * Remove a contact from the contact list
+ * \param contactName the screen name of the contact to remove
+ * \return true if the contact removal was successful
+ */
+ void removeContact( const QString &contactName );
+
+ /**
+ * Rename a group on the contact list
+ * \param oldGroupName the old group name
+ * \param newGroupName the new group name
+ */
+ void renameGroup( const QString& oldGroupName, const QString& newGroupName );
+
+ /**
+ * Modify an SSI item on the SSI list
+ * \param item the item to send to the server
+ */
+ void modifySSIItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem );
+
+ /**
+ * Change a contact's group on the server
+ * \param contact the contact to change
+ * \param newGroup the new group to move the contact to
+ */
+ void changeContactGroup( const QString& contact, const QString& newGroupName );
+
+ /**
+ * Send a message to a contact
+ * \param msg the message to be sent
+ * \param auto the message is an autoresponse message, default to false
+ */
+ void sendMessage( const Oscar::Message& msg, bool isAuto = false );
+
+ /**
+ * Request authorization from a contact
+ * \param contactid the id of the contact to request auth from
+ * \param reason the reason for this authorization request
+ */
+ void requestAuth( const QString& contactid, const QString& reason );
+
+ /**
+ * Grant or decline authorization to a contact
+ * \param contactid the id of the contact to grant/decline authorization
+ * \param reason the reason to grant/decline authorization
+ * \param auth grant or decline authorization
+ */
+ void sendAuth( const QString& contactid, const QString& reason, bool auth=true );
+
+ /**
+ * Request full user info from an ICQ contact
+ * \param contactId the UIN of the contact to get info for
+ */
+ void requestFullInfo( const QString& contactId );
+
+ /**
+ * Request short info for an ICQ contact
+ * \param contactId the UIN of the contact to get info for
+ */
+ void requestShortInfo( const QString& contactId );
+
+ /**
+ * Send a warning to the OSCAR servers about a contact
+ * \param contact the contact to send the warning to
+ * \param anon indicate whether to do it anonymously
+ */
+ void sendWarning( const QString& contact, bool anonymous );
+
+ /**
+ * Get the general ICQ info for a client
+ * \param contact the contact to get info for
+ */
+ ICQGeneralUserInfo getGeneralInfo( const QString& contact );
+
+ /**
+ * Get the work info for a contact
+ * \param contact the contact to get info for
+ */
+ ICQWorkUserInfo getWorkInfo( const QString& contact );
+
+ /**
+ * Get the email info for a contact
+ * \param contact the contact to get info for
+ */
+ ICQEmailInfo getEmailInfo( const QString& contact );
+
+ /**
+ * Get the additional info available for a contact
+ * \param contact the contact to get info for
+ */
+ ICQMoreUserInfo getMoreInfo( const QString& contact );
+
+ /**
+ * Get the interest info available for a contact
+ * \param contact the contact to get info for
+ */
+ ICQInterestInfo getInterestInfo( const QString& contact );
+
+ /**
+ * Get the short info available for an icq contact
+ * \param contact the contact to get info for
+ */
+ ICQShortInfo getShortInfo( const QString& contact );
+
+ /**
+ * Get the list of chat room exchanges we have
+ */
+ QValueList<int> chatExchangeList() const;
+
+ /**
+ * Request the aim profile
+ * \param contact the contact to get info for
+ */
+ void requestAIMProfile( const QString& contact );
+
+ /**
+ * Request the aim away message
+ * \param contact the contact to get info for
+ */
+ void requestAIMAwayMessage( const QString& contact );
+
+ /**
+ * Add the icq away message request to queue
+ * \param contact the contact to get info for
+ */
+ void addICQAwayMessageRequest( const QString& contact, ICQStatus contactStatus );
+
+ /**
+ * Remove the icq away message request from queue
+ * \param contact the contact to get info for
+ */
+ void removeICQAwayMessageRequest( const QString& contact );
+
+ /** Request the extended status info */
+ void requestStatusInfo( const QString& contact );
+
+ //! Run a whitepages search
+ void whitePagesSearch( const ICQWPSearchInfo& info );
+
+ //! Run a UIN search
+ void uinSearch( const QString& uin );
+
+ //! Update the user's AIM profile
+ void updateProfile( const QString& profile );
+
+ //! Get buddy icon information for a person
+ void requestBuddyIcon( const QString& user, const QByteArray& hash, BYTE hashType );
+
+ //! Start a server redirect for a different service
+ void requestServerRedirect( WORD family, WORD e = 0, QByteArray c = QByteArray(),
+ WORD instance = 0, const QString& room = QString::null );
+
+ //! Start uploading a buddy icon
+ void sendBuddyIcon( const QByteArray& imageData );
+
+ void joinChatRoom( const QString& roomName, int exchange );
+
+ void setIgnore( const QString& user, bool ignore );
+
+ void setVisibleTo( const QString& user, bool visible );
+
+ void setInvisibleTo( const QString& user, bool invisible );
+
+ /** Accessors needed for login */
+ QString host();
+ int port();
+
+ /** Send a typing notification */
+ void sendTyping( const QString & contact, bool typing );
+
+ /** Make a connection to the icon server */
+ void connectToIconServer();
+
+ bool hasIconConnection() const;
+
+ /** We've finished chatting in a chat room, disconnect from it */
+ void disconnectChatRoom( WORD exchange, const QString& room );
+
+ /** Set codec provider */
+ void setCodecProvider( CodecProvider* codecProvider );
+
+ /** Set pointer to version info */
+ void setVersion( const Oscar::ClientVersion* version );
+
+ /*************
+ INTERNAL (FOR USE BY TASKS OR CONNECTIONS) METHODS
+ *************/
+ /**
+ * Print a debug statement
+ */
+ void debug( const QString &str );
+
+ /** Have we logged in yet? */
+ bool isActive() const;
+
+ /** Accessor for the SSI Manager */
+ SSIManager* ssiManager() const;
+
+ /** Return version info */
+ const Oscar::ClientVersion* version() const;
+
+ /** The current user's user ID */
+ QString userId() const;
+
+ /** The current user's password */
+ QString password() const;
+
+ /** The current status message (a.k.a. away message) */
+ QString statusMessage() const;
+
+ /** Change the current status message w/o changing status */
+ void setStatusMessage( const QString &message );
+
+ /** ICQ Settings */
+ bool isIcq() const;
+ void setIsIcq( bool isIcq );
+
+ /** Host's IP address */
+ QCString ipAddress() const;
+
+ /** Notify that a task error was received */
+ void notifyTaskError( const Oscar::SNAC& s, int errCode, bool fatal );
+
+ /** Notify that a socket error has occured */
+ void notifySocketError( int errCode, const QString& msg );
+
+signals:
+ /** CONNECTION EVENTS */
+
+ /** Notifies that the login process has succeeded. */
+ void loggedIn();
+
+ /** Notifies that the login process has failed */
+ void loginFailed();
+
+ /** Notifies tasks and account so they can react properly */
+ void disconnected();
+
+ /** We were disconnected because we connected elsewhere */
+ void connectedElsewhere();
+
+ /** We have our own user info */
+ void haveOwnInfo();
+
+ /** We have our SSI list */
+ void haveSSIList();
+
+ /** a user is online. */
+ void userIsOnline( const QString& );
+
+ /** a user is offline. */
+ void userIsOffline( const QString& );
+
+ /** we've received a message */
+ void messageReceived( const Oscar::Message& );
+
+ /** we've received an authorization request */
+ void authRequestReceived( const QString& contact, const QString& reason );
+
+ /** we've received an authorization reply */
+ void authReplyReceived( const QString& contact, const QString& reason, bool auth );
+
+ /**
+ * we've received an error from a task and need to notify somebody
+ */
+ void taskError( const Oscar::SNAC& s, int errCode, bool fatal );
+
+ /**
+ * we've received a socket error and need to notify somebody
+ */
+ void socketError( int errCode, const QString& msg );
+
+ void receivedIcqShortInfo( const QString& contact );
+ void receivedIcqLongInfo( const QString& contact );
+
+ void receivedProfile( const QString& contact, const QString& profile );
+ void receivedAwayMessage( const QString& contact, const QString& message );
+ void receivedAwayMessage( const Oscar::Message& message );
+ void receivedUserInfo( const QString& contact, const UserDetails& details );
+
+ /** We warned a user */
+ void userWarned( const QString& contact, Q_UINT16 increase, Q_UINT16 newLevel );
+
+ /** Search signals */
+ void gotSearchResults( const ICQSearchResult& );
+ void endOfSearch( int);
+
+ /* Typing signals */
+ void userStartedTyping( const QString& contact );
+ void userStoppedTyping( const QString& contact );
+
+ /* Buddy icons */
+ void haveIconForContact( const QString&, QByteArray iconData );
+ void iconServerConnected();
+ void iconNeedsUploading();
+
+ /* Chat rooms */
+ void chatNavigationConnected();
+ void chatRoomConnected( WORD, const QString& );
+ void userJoinedChat( Oscar::WORD, const QString& room, const QString& contact );
+ void userLeftChat( Oscar::WORD, const QString& room, const QString& contact );
+
+ /* service redirection */
+ void redirectionFinished( WORD );
+
+
+protected slots:
+ // INTERNAL, FOR USE BY TASKS' finished() SIGNALS //
+
+ /** Singleshot timer to start stage two login */
+ void startStageTwo();
+
+ /**
+ * A login task finished. For stage one, this means we've either errored
+ * out, or gotten a cookie. For stage two, this means we've either done
+ * something wrong, or we're successfully connected
+ */
+ void lt_loginFinished();
+
+ /** Stream connected for stage two login */
+ void streamConnected();
+
+ /** We have our own user info */
+ void haveOwnUserInfo();
+
+ /** Service setup finished */
+ void serviceSetupFinished();
+
+ /** we have icq info for a contact */
+ void receivedIcqInfo( const QString& contact, unsigned int type );
+
+ /** we have normal user info for a contact */
+ void receivedInfo( Q_UINT16 sequence );
+
+ /** received a message of some kind */
+ void receivedMessage( const Oscar::Message& msg );
+
+ void offlineUser( const QString&, const UserDetails& );
+
+ void haveServerForRedirect( const QString& host, const QByteArray& cookie, WORD family );
+ void serverRedirectFinished();
+ void checkRedirectionQueue( WORD );
+
+ void requestChatNavLimits();
+ /**
+ * Set the list of chat room exchanges we have
+ */
+ void setChatExchangeList( const QValueList<int>& exchanges );
+
+ /**
+ * set up the connection to a chat room
+ */
+ void setupChatConnection( WORD, QByteArray, WORD, const QString& );
+
+
+ void determineDisconnection( int, const QString& );
+
+ void nextICQAwayMessageRequest();
+
+private:
+
+ /** Initialize some static tasks */
+ void initializeStaticTasks();
+
+ /** Delete the static tasks */
+ void deleteStaticTasks();
+
+ Connection* createConnection( const QString& host, const QString& port );
+
+ /**
+ * Request the icq away message
+ * \param contact the contact to get info for
+ */
+ //TODO only made a default for testing w/o frontend
+ void requestICQAwayMessage( const QString& contact, ICQStatus contactStatus = ICQAway );
+
+private:
+ class ClientPrivate;
+ ClientPrivate* d;
+
+ StageOneLoginTask* m_loginTask;
+ StageTwoLoginTask* m_loginTaskTwo;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
+
+
diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.cpp b/kopete/protocols/oscar/liboscar/clientreadytask.cpp
new file mode 100644
index 00000000..3338f7b3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/clientreadytask.cpp
@@ -0,0 +1,109 @@
+/*
+ Kopete Oscar Protocol
+ $FILENAME.cpp
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "clientreadytask.h"
+
+#include <kdebug.h>
+#include "buffer.h"
+#include "connection.h"
+#include "rateclass.h"
+#include "rateclassmanager.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+using namespace Oscar;
+
+ClientReadyTask::ClientReadyTask(Task* parent): Task(parent)
+{
+ m_classList = client()->rateManager()->classList();
+}
+
+
+ClientReadyTask::~ClientReadyTask()
+{
+}
+
+void ClientReadyTask::setFamilies( const QValueList<int>& families )
+{
+ m_familyList = families;
+}
+
+
+void ClientReadyTask::onGo()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x0002, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending client ready, end of login" << endl;
+ //nasty nasty nasty hack to get all the packets to work
+ QValueList<int>::const_iterator rcEnd = m_familyList.constEnd();
+ for ( QValueList<int>::const_iterator it = m_familyList.constBegin(); it != rcEnd; ++it )
+ {
+ //I have no idea what any of these values mean. I just copied them from oscarsocket
+ int i = ( *it );
+ buffer->addWord( i );
+ switch ( i )
+ {
+ case 0x0001:
+ buffer->addWord( 0x0003 );
+ break;
+ case 0x0013:
+ buffer->addWord( client()->isIcq() ? 0x0002 : 0x0003 );
+ break;
+ default:
+ buffer->addWord( 0x0001 );
+ };
+
+ if ( client()->isIcq() )
+ {
+ if ( i == 0x0002 )
+ buffer->addWord( 0x0101 );
+ else
+ buffer->addWord( 0x0110 );
+
+ //always add 0x047B
+ buffer->addWord( 0x047B );
+ }
+ else //we're AIM so AOL has us do something completely different! *sigh*
+ {
+ switch( i )
+ {
+ case 0x0008:
+ case 0x000B:
+ case 0x000C:
+ buffer->addWord( 0x0104 );
+ buffer->addWord( 0x0001 );
+ break;
+ default:
+ buffer->addWord( 0x0110 );
+ buffer->addWord( 0x059B );
+ break;
+ };
+ }
+ }
+
+ //send the damn thing so we can finally be finished
+ //with the hell that is oscar login. (just wait until you get a message)
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+ setSuccess( 0, QString::null );
+
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/clientreadytask.h b/kopete/protocols/oscar/liboscar/clientreadytask.h
new file mode 100644
index 00000000..4a9ea941
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/clientreadytask.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Oscar Protocol
+
+ Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef CLIENTREADYTASK_H
+#define CLIENTREADYTASK_H
+
+#include "task.h"
+
+#include "rateclass.h"
+#include "qvaluelist.h"
+
+/**
+Fire and forget task to let the server know we're ready
+
+@author Matt Rogers
+*/
+class ClientReadyTask : public Task
+{
+public:
+ ClientReadyTask( Task* parent );
+ ~ClientReadyTask();
+ virtual void onGo();
+
+ void setFamilies( const QValueList<int>& families );
+
+private:
+ QValueList<RateClass*> m_classList;
+ QValueList<int> m_familyList;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp
new file mode 100644
index 00000000..54926949
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.cpp
@@ -0,0 +1,146 @@
+/*
+ Kopete Oscar Protocol
+ closeconnectiontask.h - Handles the closing of the connection to the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "closeconnectiontask.h"
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include "connection.h"
+#include "transfer.h"
+#include "oscarutils.h"
+
+using namespace Oscar;
+
+CloseConnectionTask::CloseConnectionTask( Task* parent )
+ : Task(parent)
+{
+}
+
+
+CloseConnectionTask::~CloseConnectionTask()
+{
+}
+
+const QByteArray& CloseConnectionTask::cookie() const
+{
+ return m_cookie;
+}
+
+const QString& CloseConnectionTask::bosHost() const
+{
+ return m_bosHost;
+}
+
+const QString& CloseConnectionTask::bosPort() const
+{
+ return m_bosPort;
+}
+
+bool CloseConnectionTask::take( Transfer* transfer )
+{
+ QString errorReason;
+ WORD errorNum = 0;
+ if ( forMe( transfer ) )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "RECV (DISCONNECT)" << endl;
+
+ FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer );
+
+ if ( !ft )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo
+ << "Could not convert transfer object to type FlapTransfer!!" << endl;
+ return false;
+ }
+
+ QValueList<TLV> tlvList = ft->buffer()->getTLVList();
+
+ TLV uin = findTLV( tlvList, 0x0001 );
+ if ( uin )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(1) [UIN], uin=" << QString( uin.data ) << endl;
+ }
+
+ TLV err = findTLV( tlvList, 0x0008 );
+ if ( !err )
+ err = findTLV( tlvList, 0x0009 );
+
+ if ( err.type == 0x0008 || err.type == 0x0009 )
+ {
+ errorNum = ( ( err.data[0] << 8 ) | err.data[1] );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(8) [ERROR] error= " << errorNum << endl;
+
+ Oscar::SNAC s = { 0, 0, 0, 0 };
+ client()->fatalTaskError( s, errorNum );
+ return true; //if there's an error, we'll need to disconnect anyways
+ }
+
+ TLV server = findTLV( tlvList, 0x0005 );
+ if ( server )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(5) [SERVER] " << QString( server.data ) << endl;
+ QString ip = server.data;
+ int index = ip.find( ':' );
+ m_bosHost = ip.left( index );
+ ip.remove( 0 , index+1 ); //get rid of the colon and everything before it
+ m_bosPort = ip;
+ }
+
+ TLV cookie = findTLV( tlvList, 0x0006 );
+ if ( cookie )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "found TLV(6) [COOKIE]" << endl;
+ m_cookie.duplicate( cookie.data );
+ }
+
+ tlvList.clear();
+
+ if ( m_bosHost.isEmpty() )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Empty host address!" << endl;
+
+ Oscar::SNAC s = { 0, 0, 0, 0 };
+ client()->fatalTaskError( s, 0 );
+ return true;
+ }
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "We should reconnect to server '"
+ << m_bosHost << "' on port " << m_bosPort << endl;
+ setSuccess( errorNum, errorReason );
+ return true;
+ }
+ return false;
+}
+
+bool CloseConnectionTask::forMe( const Transfer* transfer ) const
+{
+ const FlapTransfer* ft = dynamic_cast<const FlapTransfer*> ( transfer );
+
+ if (!ft)
+ return false;
+
+ if ( ft && ft->flapChannel() == 4 )
+ return true;
+ else
+ return false;
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/closeconnectiontask.h b/kopete/protocols/oscar/liboscar/closeconnectiontask.h
new file mode 100644
index 00000000..b241b07e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/closeconnectiontask.h
@@ -0,0 +1,62 @@
+/*
+ Kopete Oscar Protocol
+ closeconnectiontask.h - Handles the closing of the connection to the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CLOSECONNECTIONTASK_H
+#define CLOSECONNECTIONTASK_H
+
+#include <task.h>
+#include <qcstring.h>
+
+class Transfer;
+class QString;
+
+/**
+@author Matt Rogers
+*/
+class CloseConnectionTask : public Task
+{
+public:
+ CloseConnectionTask(Task* parent);
+
+ ~CloseConnectionTask();
+
+ virtual bool take(Transfer* transfer);
+
+ //Protocol specific stuff
+ const QByteArray& cookie() const;
+ const QString& bosHost() const;
+ const QString& bosPort() const;
+
+
+protected:
+ virtual bool forMe(const Transfer* transfer) const;
+
+private:
+ bool parseDisconnectCode( int error, QString& reason );
+
+private:
+ QByteArray m_cookie;
+ QString m_bosHost;
+ QString m_bosPort;
+
+
+};
+
+#endif
+
+//kate: indent-mode csands; space-indent off; tab-width 4; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/connection.cpp b/kopete/protocols/oscar/liboscar/connection.cpp
new file mode 100644
index 00000000..c7cbc0fe
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connection.cpp
@@ -0,0 +1,248 @@
+/*
+ Kopete Oscar Protocol
+ connection.cpp - independent protocol encapsulation
+
+ Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connection.h"
+#include "client.h"
+#include "connector.h"
+#include "oscarclientstream.h"
+#include "rateclassmanager.h"
+#include "task.h"
+#include "transfer.h"
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include "oscartypeclasses.h"
+
+
+class ConnectionPrivate
+{
+public:
+ DWORD snacSequence;
+ WORD flapSequence;
+
+ QValueList<int> familyList;
+ RateClassManager* rateClassManager;
+
+ ClientStream* clientStream;
+ Connector* connector;
+ Client* client;
+
+ Task* root;
+};
+
+
+
+Connection::Connection( Connector* connector, ClientStream* cs, const char* name )
+: QObject( 0, name )
+{
+ d = new ConnectionPrivate();
+ d->clientStream = cs;
+ d->client = 0;
+ d->connector = connector;
+ d->rateClassManager = new RateClassManager( this );
+ d->root = new Task( this, true /* isRoot */ );
+ m_loggedIn = false;
+ initSequence();
+
+}
+
+Connection::~Connection()
+{
+
+ delete d->rateClassManager;
+ delete d->clientStream;
+ delete d->connector;
+ delete d->root;
+ delete d;
+}
+
+void Connection::setClient( Client* c )
+{
+ d->client = c;
+ connect( c, SIGNAL( loggedIn() ), this, SLOT( loggedIn() ) );
+}
+
+void Connection::connectToServer( const QString& host, bool auth )
+{
+ connect( d->clientStream, SIGNAL( error( int ) ), this, SLOT( streamSocketError( int ) ) );
+ connect( d->clientStream, SIGNAL( readyRead() ), this, SLOT( streamReadyRead() ) );
+ connect( d->clientStream, SIGNAL( connected() ), this, SIGNAL( connected() ) );
+ d->clientStream->connectToServer( host, auth );
+}
+
+void Connection::close()
+{
+ d->clientStream->close();
+ reset();
+}
+
+bool Connection::isSupported( int family ) const
+{
+ return ( d->familyList.findIndex( family ) != -1 );
+}
+
+QValueList<int> Connection::supportedFamilies() const
+{
+ return d->familyList;
+}
+
+void Connection::addToSupportedFamilies( const QValueList<int>& familyList )
+{
+ d->familyList += familyList;
+}
+
+void Connection::addToSupportedFamilies( int family )
+{
+ d->familyList.append( family );
+}
+
+void Connection::taskError( const Oscar::SNAC& s, int errCode )
+{
+ d->client->notifyTaskError( s, errCode, false /*fatal*/ );
+}
+
+void Connection::fatalTaskError( const Oscar::SNAC& s, int errCode )
+{
+ d->client->notifyTaskError( s, errCode, true /* fatal */ );
+}
+
+Oscar::Settings* Connection::settings() const
+{
+ return d->client->clientSettings();
+}
+
+Q_UINT16 Connection::flapSequence()
+{
+ d->flapSequence++;
+ if ( d->flapSequence >= 0x8000 ) //the max flap sequence is 0x8000 ( HEX )
+ d->flapSequence = 1;
+
+ return d->flapSequence;
+}
+
+Q_UINT32 Connection::snacSequence()
+{
+ d->snacSequence++;
+ return d->snacSequence;
+}
+
+QString Connection::userId() const
+{
+ return d->client->userId();
+}
+
+QString Connection::password() const
+{
+ return d->client->password();
+}
+
+bool Connection::isIcq() const
+{
+ return d->client->isIcq();
+}
+
+Task* Connection::rootTask() const
+{
+ return d->root;
+}
+
+SSIManager* Connection::ssiManager() const
+{
+ return d->client->ssiManager();
+}
+
+const Oscar::ClientVersion* Connection::version() const
+{
+ return d->client->version();
+}
+
+bool Connection::isLoggedIn() const
+{
+ return m_loggedIn;
+}
+
+RateClassManager* Connection::rateManager() const
+{
+ return d->rateClassManager;
+}
+
+void Connection::send( Transfer* request ) const
+{
+ if( !d->clientStream )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on!" << endl;
+ return;
+ }
+ d->rateClassManager->queue( request );
+
+}
+
+void Connection::forcedSend( Transfer* request ) const
+{
+ if ( !d->clientStream )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No stream to write on" << endl;
+ return;
+ }
+ d->clientStream->write( request );
+}
+
+void Connection::initSequence()
+{
+ d->snacSequence = ( KApplication::random() & 0xFFFF );
+ d->flapSequence = ( KApplication::random() & 0xFFFF );
+}
+
+void Connection::distribute( Transfer * transfer ) const
+{
+ //d->rateClassManager->recalcRateLevels();
+ if( !rootTask()->take( transfer ) )
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "root task refused transfer" << endl;
+
+ delete transfer;
+}
+
+void Connection::reset()
+{
+ //clear the family list
+ d->familyList.clear();
+ d->rateClassManager->reset();
+}
+
+void Connection::streamReadyRead()
+{
+ // take the incoming transfer and distribute it to the task tree
+ Transfer * transfer = d->clientStream->read();
+ distribute( transfer );
+}
+
+void Connection::loggedIn()
+{
+ m_loggedIn = true;
+}
+
+void Connection::streamSocketError( int code )
+{
+ emit socketError( code, d->clientStream->errorText() );
+}
+
+#include "connection.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/connection.h b/kopete/protocols/oscar/liboscar/connection.h
new file mode 100644
index 00000000..4170857e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connection.h
@@ -0,0 +1,209 @@
+/*
+Kopete Oscar Protocol
+connection.h - independent protocol encapsulation
+
+Copyright (c) 2004-2005 by Matt Rogers <mattr@kde.org>
+
+Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+*************************************************************************
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation; either *
+* version 2 of the License, or (at your option) any later version. *
+* *
+*************************************************************************
+*/
+#ifndef CONNECTION_H
+#define CONNECTION_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include "oscartypes.h"
+#include "rateclass.h"
+
+class ConnectionPrivate;
+class Client;
+class ClientStream;
+class Connector;
+class ByteStream;
+class Transfer;
+class RateClassManager;
+class SSIManager;
+class Task;
+
+
+namespace Oscar
+{
+class Settings;
+}
+
+/**
+ * This class encapsulates both the low level network layer code and the high
+ * level OSCAR protocol code required to create a single independent
+ * connection to an OSCAR server
+ * @author Matt Rogers
+ */
+class Connection : public QObject
+{
+Q_OBJECT
+public:
+
+ Connection( Connector* connector, ClientStream* cs, const char* name = 0 );
+ ~Connection();
+
+ void setClient( Client* );
+
+ void connectToServer( const QString& server, bool auth = true );
+ /**
+ * Close the connection and reset the connection data
+ */
+ void close();
+
+ /**
+ * Check to see if the family specified by @p family is supported by this
+ * connection.
+ * @param family the family number to check
+ */
+ bool isSupported( int family ) const;
+
+ /**
+ * Get the list of supported families
+ * @return The list of families supported on this connection
+ */
+ QValueList<int> supportedFamilies() const;
+
+ /**
+ * Add the SNAC families in \p familyList to the list of supported families for
+ * this connection
+ * \param familyList the list of families to add
+ */
+ void addToSupportedFamilies( const QValueList<int>& familyList );
+
+ /**
+ * Add the SNAC family in \p family to the list of supported families for
+ * this connection
+ * \overload
+ * \param family the single family to add to the list
+ */
+ void addToSupportedFamilies( int family );
+
+ /**
+ * Add the rate classes in \p rateClassList to the list of rate classes packets
+ * need to be filtered on
+ * \param rateClassList the list of rate classes to add
+ */
+ void addToRateClasses( const QValueList<RateClass*> rateClassList );
+
+ /**
+ * Add the rate class in \p rc to the list of rate classes packets
+ * need to be filtered on
+ * \overload
+ * \param rc the list rate class to add
+ */
+ void addToRateClasses( RateClass* rc );
+
+ /**
+ * Indicate to the connection that there has been an error in a task. The
+ * error won't require us to go offline, but the user should be notified
+ * about the error
+ * \param s the SNAC the error occured from
+ * \param errCode the error code
+ */
+ void taskError( const Oscar::SNAC& s, int errCode );
+
+ /**
+ * Indicate to the connection that there has been a fatal error in a task.
+ * This error will require a disconnection from the OSCAR service and if
+ * necessary, the user should be prompted to reconnect manually or an
+ * automatic reconnection should be attempted.
+ * \param s the SNAC the error occured from
+ * \param errCode the error code
+ */
+ void fatalTaskError( const Oscar::SNAC& s, int errCode );
+
+ /**
+ * Get the chat room name for this connection.
+ * @return the name of the room or QString::null if not connected to a room
+ */
+
+ /** Get the user settings object */
+ Oscar::Settings* settings() const;
+
+ /** Get the current FLAP sequence for this connection */
+ Q_UINT16 flapSequence();
+
+ /** Get the current SNAC sequence for this connection */
+ Q_UINT32 snacSequence();
+
+ /** Get the cookie for this connection */
+ QByteArray cookie() const;
+
+ QString userId() const;
+ QString password() const;
+ bool isIcq() const;
+ SSIManager* ssiManager() const;
+ const Oscar::ClientVersion* version() const;
+ RateClassManager* rateManager() const;
+ bool isLoggedIn() const;
+
+ /** Convenience function to get the root task for use in Tasks */
+ Task* rootTask() const;
+
+ /** Get the raw connector for this connection, in case we need it */
+ Connector* connector();
+
+ /** Get the byte stream for this connection, in case we need it */
+ ByteStream* byteStream();
+
+ void send( Transfer* t ) const;
+ void forcedSend( Transfer* t ) const;
+
+signals:
+
+ /** There's data ready to read */
+ void readyRead();
+
+ /** We've connected */
+ void connected();
+
+ /** We were disconnected */
+ void disconnected();
+
+ /**
+ * There was an error on the socket and we've disconnected
+ * \param errCode the error code from the operating system
+ * \param errString the i18n'ed string that describes the error
+ */
+ void socketError( int errCode, const QString& errString );
+
+
+private:
+ /** Seed the sequence numbers with random values */
+ void initSequence();
+
+ /** Distribute the transfer among the connection's tasks */
+ void distribute( Transfer* t ) const;
+
+private slots:
+ /** Reset the data for the connection.*/
+ void reset();
+
+ /** We've got something from the stream */
+ void streamReadyRead();
+
+ /** We've finished logging in */
+ void loggedIn();
+
+ void streamSocketError( int );
+
+private:
+
+ ConnectionPrivate* d;
+ bool m_loggedIn;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands; auto-insert-doxygen on;
diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.cpp b/kopete/protocols/oscar/liboscar/connectionhandler.cpp
new file mode 100644
index 00000000..bf5706ee
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connectionhandler.cpp
@@ -0,0 +1,174 @@
+/*
+ Kopete Oscar Protocol
+ Oscar Multiple Connection Handling
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connectionhandler.h"
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include "connection.h"
+#include "oscartypes.h"
+
+class ConnectionHandler::Private
+{
+public:
+ QValueList<Connection*> connections;
+ QMap<Connection*, ConnectionRoomInfo> chatRoomConnections;
+};
+
+ConnectionHandler::ConnectionHandler()
+{
+ d = new Private;
+}
+
+
+ConnectionHandler::~ConnectionHandler()
+{
+ delete d;
+}
+
+void ConnectionHandler::append( Connection* c )
+{
+ d->connections.append( c );
+}
+
+void ConnectionHandler::remove( Connection* c )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing connection "
+ << c << endl;
+ d->connections.remove( c );
+ c->deleteLater();
+}
+
+void ConnectionHandler::remove( int family )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing all connections " <<
+ "supporting family " << family << endl;
+ QValueList<Connection*>::iterator it = d->connections.begin();
+ QValueList<Connection*>::iterator itEnd = d->connections.end();
+ for ( ; it != itEnd; ++it )
+ {
+ if ( ( *it )->isSupported( family ) )
+ {
+ Connection* c = ( *it );
+ it = d->connections.remove( it );
+ c->deleteLater();
+ }
+ }
+}
+
+void ConnectionHandler::clear()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing all connections"
+ << endl;
+ while ( !d->connections.isEmpty() )
+ {
+ Connection *c = d->connections.front();
+ d->connections.pop_front();
+ c->deleteLater();
+ }
+}
+
+Connection* ConnectionHandler::connectionForFamily( int family ) const
+{
+ QValueList<Connection*>::iterator it = d->connections.begin();
+ QValueList<Connection*>::iterator itEnd = d->connections.end();
+ int connectionCount = 0;
+ Connection* lastConnection = 0;
+ for ( ; it != itEnd; ++it )
+ {
+ if ( ( *it )->isSupported( family ) )
+ {
+ connectionCount++;
+ lastConnection = ( *it );
+ }
+ }
+ if ( connectionCount == 1 )
+ return lastConnection;
+
+ return 0;
+}
+
+Connection* ConnectionHandler::defaultConnection() const
+{
+ if ( d->connections.isEmpty() || d->connections.count() > 1 )
+ return 0;
+
+ return d->connections.first();
+}
+
+void ConnectionHandler::addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room )
+{
+ if ( d->connections.findIndex( c ) == -1 )
+ d->connections.append( c );
+
+ ConnectionRoomInfo info = qMakePair( exchange, room );
+ d->chatRoomConnections[c] = info;
+}
+
+Connection* ConnectionHandler::connectionForChatRoom( Oscar::WORD exchange, const QString& room )
+{
+ ConnectionRoomInfo infoToFind = qMakePair( exchange, room );
+ QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end();
+ for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it )
+ {
+ if ( it.data() == infoToFind )
+ {
+ Connection* c = it.key();
+ return c;
+ }
+ }
+
+ return 0;
+}
+
+QString ConnectionHandler::chatRoomForConnection( Connection* c )
+{
+ if ( d->connections.findIndex( c ) == -1 )
+ return QString::null;
+
+ QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end();
+ for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it )
+ {
+ if ( it.key() == c )
+ {
+ QString room = it.data().second;
+ return room;
+ }
+ }
+
+ return QString::null;
+}
+
+Oscar::WORD ConnectionHandler::exchangeForConnection( Connection* c )
+{
+
+ if ( d->connections.findIndex( c ) == -1 )
+ return 0xFFFF;
+
+ QMap<Connection*, ConnectionRoomInfo>::iterator it, itEnd = d->chatRoomConnections.end();
+ for ( it = d->chatRoomConnections.begin(); it != itEnd; ++it )
+ {
+ if ( it.key() == c )
+ {
+ Oscar::WORD exchange = it.data().first;
+ return exchange;
+ }
+ }
+
+ return 0xFFFF;
+}
+
diff --git a/kopete/protocols/oscar/liboscar/connectionhandler.h b/kopete/protocols/oscar/liboscar/connectionhandler.h
new file mode 100644
index 00000000..6094cab3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connectionhandler.h
@@ -0,0 +1,118 @@
+/*
+ Kopete Oscar Protocol
+ Oscar Multiple Connection Handling
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CONNECTIONHANDLER_H
+#define CONNECTIONHANDLER_H
+
+#include "oscartypes.h"
+#include <qstring.h>
+#include <qpair.h>
+
+
+class Connection;
+
+typedef QPair<Oscar::WORD, QString> ConnectionRoomInfo;
+
+/**
+@author Kopete Developers
+*/
+class ConnectionHandler
+{
+public:
+ ConnectionHandler();
+ ~ConnectionHandler();
+
+ /**
+ * Add a connection to the handler so that it can be
+ * tracked and queried for later.
+ * @param c The connection to add to the handler
+ */
+ void append( Connection* c );
+
+ /**
+ * Remove a connection from the handler
+ * @param c The connection object to remove
+ */
+ void remove( Connection* c );
+
+ /**
+ * Remove a connection from the handler
+ * @param family The SNAC family for the connection to remove
+ */
+ void remove( int family );
+
+ /**
+ * Clear all the connections.
+ */
+ void clear();
+
+ /**
+ * Get the connection for a particular SNAC family. If there is
+ * more than one connection for a particular family or there is no
+ * connection, then zero is returned.
+ * @return A valid connection object for the family or 0
+ */
+ Connection* connectionForFamily( int family ) const;
+
+ /**
+ * Get the default connection. Returns zero when we're handling more than
+ * one connection.
+ * @return The only connection object we're tracking or zero if we have
+ * more than one.
+ */
+ Connection* defaultConnection() const;
+
+ /**
+ * Add chat room information to a connection so that we can track
+ * connections by chat room
+ * @param c The connection to add information to
+ * @param exchange the exchange the chat room is in
+ * @param room the name of the chat room
+ */
+ void addChatInfoForConnection( Connection* c, Oscar::WORD exchange, const QString& room );
+
+ /**
+ * Get the connection for a particular room name and exchange number.
+ * @param exchange the chat room exchange the room is on
+ * @param room the name of the chat room to find a connection for
+ * @return a Connection for the chat room or 0L if no connection for that room
+ */
+ Connection* connectionForChatRoom( Oscar::WORD exchange, const QString& room );
+
+ /**
+ * Get the room name for the chat room based the connection
+ * @return The name of the chat room that this connection is connected to
+ * If the connection passed in by @p c is not a chat room connection then
+ * QString::null is returned.
+ */
+ QString chatRoomForConnection( Connection* c );
+
+ /**
+ * Get the exchange number for the chat room based on the connection
+ * @return The exchange of the chat room that this connection is connected
+ * to. If the connection passed in by @p c is not a chat room connection
+ * then 0xFFFF is returned
+ */
+ Oscar::WORD exchangeForConnection( Connection* c );
+
+private:
+ class Private;
+ Private* d;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/connector.cpp b/kopete/protocols/oscar/liboscar/connector.cpp
new file mode 100644
index 00000000..03a61882
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connector.cpp
@@ -0,0 +1,62 @@
+/*
+ Kopete Oscar Protocol
+ connector.cpp - the Oscar socket connector
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connector.h"
+
+Connector::Connector(QObject *parent)
+:QObject(parent)
+{
+ setPeerAddressNone();
+}
+
+Connector::~Connector()
+{
+}
+
+bool Connector::havePeerAddress() const
+{
+ return haveaddr;
+}
+
+QHostAddress Connector::peerAddress() const
+{
+ return addr;
+}
+
+Q_UINT16 Connector::peerPort() const
+{
+ return port;
+}
+
+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/oscar/liboscar/connector.h b/kopete/protocols/oscar/liboscar/connector.h
new file mode 100644
index 00000000..fd673163
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/connector.h
@@ -0,0 +1,59 @@
+/*
+ Kopete Oscar Protocol
+ connector.h - the Oscar socket connector
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LIBOSCAR_CONNECTOR_H
+#define LIBOSCAR_CONNECTOR_H
+
+
+#include <qobject.h>
+#include "qhostaddress.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 havePeerAddress() const;
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void connected();
+ void error();
+
+protected:
+ void setPeerAddressNone();
+ void setPeerAddress(const QHostAddress &addr, Q_UINT16 port);
+
+private:
+ bool haveaddr;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.cpp b/kopete/protocols/oscar/liboscar/coreprotocol.cpp
new file mode 100644
index 00000000..4f114039
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/coreprotocol.cpp
@@ -0,0 +1,285 @@
+/*
+ Kopete Oscar Protocol
+ coreprotocol.h- the core Oscar protocol
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+ url_escape_string from Gaim src/protocols/novell/nmconn.c
+ Copyright (c) 2004 Novell, Inc. All Rights Reserved
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "coreprotocol.h"
+
+#include <qdatastream.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+
+#include <kdebug.h>
+#include <ctype.h>
+
+#include "oscartypes.h"
+#include "transfer.h"
+#include "flapprotocol.h"
+#include "snacprotocol.h"
+
+static QString toString( const QByteArray& buffer )
+{
+ // line format:
+ //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........|
+
+ int i = 0;
+ QString output = "\n";
+ QString hex, ascii;
+
+ QByteArray::ConstIterator it;
+ for ( it = buffer.begin(); it != buffer.end(); ++it )
+ {
+ i++;
+
+ unsigned char c = static_cast<unsigned char>(*it);
+
+ if ( c < 0x10 )
+ hex.append("0");
+ hex.append(QString("%1 ").arg(c, 0, 16));
+
+ ascii.append(isprint(c) ? c : '.');
+
+ if (i == 16)
+ {
+ output += hex + " |" + ascii + "|\n";
+ i=0;
+ hex=QString::null;
+ ascii=QString::null;
+ }
+ }
+
+ if(!hex.isEmpty())
+ output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|';
+ output.append('\n');
+
+ return output;
+}
+
+
+using namespace Oscar;
+
+CoreProtocol::CoreProtocol() : QObject()
+{
+ m_snacProtocol = new SnacProtocol( this, "snacprotocol" );
+ m_flapProtocol = new FlapProtocol( this, "flapprotocol" );
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+int CoreProtocol::state()
+{
+ return m_state;
+}
+
+void CoreProtocol::addIncomingData( const QByteArray & incomingBytes )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received " << incomingBytes.count() << " bytes. " << endl;
+ // store locally
+ 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++;
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "parsed transfer #" << transferCount << " in chunk" << endl;
+ int size = m_in.size();
+ if ( parsedBytes < size )
+ {
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "more data in chunk!" << endl;
+ // 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 )
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "message was incomplete, waiting for more..." << endl;
+
+ if ( m_snacProtocol->state() == OutOfSync || m_flapProtocol->state() == OutOfSync )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "protocol thinks it's out of sync. "
+ << "discarding the rest of the buffer and hoping the server regains sync soon..." << endl;
+ m_in.truncate( 0 );
+ }
+// kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "done processing chunk" << endl;
+}
+
+Transfer* CoreProtocol::incomingTransfer()
+{
+ if ( m_state == Available )
+ {
+ m_state = NoData;
+ return m_inTransfer;
+ m_inTransfer = 0;
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "we shouldn't be here!" << kdBacktrace() << endl;
+ return 0;
+ }
+}
+
+void cp_dump( const QByteArray &bytes )
+{
+#ifdef OSCAR_COREPROTOCOL_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << "contains: " << bytes.count() << " bytes" << endl;
+ for ( uint i = 0; i < bytes.count(); ++i )
+ {
+ printf( "%02x ", bytes[ i ] );
+ }
+ printf( "\n" );
+#else
+ Q_UNUSED( bytes );
+#endif
+}
+
+void CoreProtocol::outgoingTransfer( Transfer* outgoing )
+{
+ //kdDebug(OSCAR_RAW_DEBUG) << "CoreProtocol::outgoingTransfer()" << endl;
+ // Convert the outgoing data into wire format
+ // pretty leet, eh?
+ emit outgoingData( outgoing->toWire() );
+ delete outgoing;
+
+ return;
+}
+
+int CoreProtocol::wireToTransfer( const QByteArray& wire )
+{
+ // processing incoming data and reassembling it into transfers
+ // may be an event or a response
+
+ BYTE flapStart, flapChannel = 0;
+ WORD flapLength = 0;
+ WORD s1, s2 = 0;
+ uint bytesParsed = 0;
+
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current packet" << toString(wire) << endl;
+ if ( wire.size() < 6 ) //check for valid flap length
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo
+ << "packet not long enough! couldn't parse FLAP!" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << wire.size() << endl;
+ m_state = NeedMore;
+ return bytesParsed;
+ }
+
+ QDataStream din( wire, IO_ReadOnly );
+
+ // look at first four bytes and decide what to do with the chunk
+ if ( okToProceed( din ) )
+ {
+ din >> flapStart;
+ QByteArray packet;
+ packet.duplicate( wire );
+ if ( flapStart == 0x2A )
+ {
+ din >> flapChannel;
+ din >> flapLength; //discard the first one it's not really the flap length
+ din >> flapLength;
+ if ( wire.size() < flapLength )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo
+ << "Not enough bytes to make a correct transfer. Have " << wire.size()
+ << " bytes. need " << flapLength + 6 << " bytes" << endl;
+ m_state = NeedMore;
+ return bytesParsed;
+ }
+
+ if ( flapChannel != 2 )
+ {
+ Transfer *t = m_flapProtocol->parse( packet, bytesParsed );
+ if ( t )
+ {
+ m_inTransfer = t;
+ m_state = Available;
+ emit incomingData();
+ }
+ else
+ bytesParsed = 0;
+ }
+
+ if ( flapChannel == 2 )
+ {
+ din >> s1;
+ din >> s2;
+
+ Transfer * t = m_snacProtocol->parse( packet, bytesParsed );
+ if ( t )
+ {
+ m_inTransfer = t;
+ m_state = Available;
+ emit incomingData();
+ }
+ else
+ {
+ bytesParsed = 0;
+ m_state = NeedMore;
+ }
+ }
+ }
+ else
+ { //unknown wire format
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "unknown wire format detected!" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << flapStart << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet is " << endl << toString( wire ) << endl;
+ }
+
+ }
+ return bytesParsed;
+}
+
+void CoreProtocol::reset()
+{
+ m_in.resize( 0 );
+}
+
+void CoreProtocol::slotOutgoingData( const QCString &out )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << out.data() << endl;
+}
+
+bool CoreProtocol::okToProceed( const QDataStream &din )
+{
+ if ( din.atEnd() )
+ {
+ m_state = NeedMore;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Server message ended prematurely!" << endl;
+ return false;
+ }
+ else
+ return true;
+}
+
+#include "coreprotocol.moc"
+//kate: indent-mode csands; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/coreprotocol.h b/kopete/protocols/oscar/liboscar/coreprotocol.h
new file mode 100644
index 00000000..f49396af
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/coreprotocol.h
@@ -0,0 +1,108 @@
+/*
+ Kopete Groupwise Protocol
+ coreprotocol.h- the core GroupWise protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef GW_CORE_PROTOCOL_H
+#define GW_CORE_PROTOCOL_H
+
+#include <qcstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+class FlapProtocol;
+class SnacProtocol;
+class Transfer;
+
+class CoreProtocol : public QObject
+{
+Q_OBJECT
+public:
+ enum State { NeedMore, Available, NoData, OutOfSync };
+
+ CoreProtocol();
+
+ virtual ~CoreProtocol();
+
+ /**
+ * Reset the protocol, clear buffers
+ */
+ void reset();
+
+ /**
+ * Accept data from the network, and buffer it into a useful message
+ * This requires parsing out each FLAP, etc. from the incoming data
+ * @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( Transfer* 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( const QDataStream &din );
+ /**
+ * 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 );
+
+private:
+ QByteArray m_in; // buffer containing unprocessed bytes we received
+ int m_error;
+ Transfer* m_inTransfer; // the transfer that is being received
+ int m_state; // represents the protocol's overall state
+ SnacProtocol* m_snacProtocol;
+ FlapProtocol* m_flapProtocol;
+
+};
+
+#endif
+
diff --git a/kopete/protocols/oscar/liboscar/errortask.cpp b/kopete/protocols/oscar/liboscar/errortask.cpp
new file mode 100644
index 00000000..9e9ce95b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/errortask.cpp
@@ -0,0 +1,66 @@
+/*
+ Kopete Oscar Protocol
+ errortask.cpp - handle OSCAR protocol errors
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "errortask.h"
+#include <kdebug.h>
+#include "oscartypes.h"
+#include "transfer.h"
+
+ErrorTask::ErrorTask( Task* parent )
+ : Task( parent )
+{
+}
+
+
+ErrorTask::~ErrorTask()
+{
+}
+
+
+bool ErrorTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st->flapChannel() == 2 && st->snacSubtype() == 1 )
+ return true;
+ else
+ return false;
+}
+
+bool ErrorTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ Buffer* buffer = transfer->buffer();
+ //get the error code
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Error code is " << buffer->getWord() << endl;
+ TLV t = buffer->getTLV();
+ if ( t.type == 0x0008 && t.length > 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "TLV error subcode is "
+ << t.data << endl;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+//kate indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/errortask.h b/kopete/protocols/oscar/liboscar/errortask.h
new file mode 100644
index 00000000..f74152db
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/errortask.h
@@ -0,0 +1,39 @@
+/*
+ Kopete Oscar Protocol
+ errortask.h - handle OSCAR protocol errors
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef ERRORTASK_H
+#define ERRORTASK_H
+
+#include <task.h>
+
+/**
+Handles OSCAR protocol errors received from the server on snac subtype 0x01
+@author Matt Rogers
+*/
+class ErrorTask : public Task
+{
+public:
+ ErrorTask( Task* parent );
+ ~ErrorTask();
+ bool take( Transfer* transfer );
+
+protected:
+ bool forMe( const Transfer* transfer ) const;
+
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.cpp b/kopete/protocols/oscar/liboscar/flapprotocol.cpp
new file mode 100644
index 00000000..c5dc04ed
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/flapprotocol.cpp
@@ -0,0 +1,72 @@
+/*
+ Kopete Oscar Protocol
+ flapprotocol.cpp - reads the protocol used by Oscar for signaling stuff
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "flapprotocol.h"
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qobject.h>
+#include <kdebug.h>
+
+#include "transfer.h"
+
+using namespace Oscar;
+
+FlapProtocol::FlapProtocol(QObject *parent, const char *name)
+ : InputProtocolBase(parent, name)
+{
+}
+
+FlapProtocol::~FlapProtocol()
+{
+}
+
+Transfer* FlapProtocol::parse( const QByteArray & packet, uint& bytes )
+{
+ QDataStream* m_din = new QDataStream( packet, IO_ReadOnly );
+
+ BYTE b;
+ WORD w;
+
+ FLAP f;
+ *m_din >> b; //this should be the start byte
+ *m_din >> b;
+ f.channel = b;
+ *m_din >> w;
+ f.sequence = w;
+ *m_din >> w;
+ f.length = w;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel: " << f.channel
+ << " sequence: " << f.sequence << " length: " << f.length << endl;
+ //use pointer arithmatic to skip the flap and snac headers
+ //so we don't have to do double parsing in the tasks
+ char* charPacket = packet.data();
+ char* snacData = charPacket + 6;
+ Buffer *snacBuffer = new Buffer( snacData, f.length );
+
+ FlapTransfer* ft = new FlapTransfer( f, snacBuffer );
+ bytes = snacBuffer->length() + 6;
+ delete m_din;
+ m_din = 0;
+ return ft;
+}
+
+
+#include "flapprotocol.moc"
diff --git a/kopete/protocols/oscar/liboscar/flapprotocol.h b/kopete/protocols/oscar/liboscar/flapprotocol.h
new file mode 100644
index 00000000..d61cf6c4
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/flapprotocol.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Oscar Protocol
+ flapprotocol.h - reads the protocol used by Oscar for signaling stuff
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCAR_FLAPPROTOCOL_H
+#define OSCAR_FLAPPROTOCOL_H
+
+#include "inputprotocolbase.h"
+
+class FlapTransfer;
+
+
+class FlapProtocol : public InputProtocolBase
+{
+Q_OBJECT
+public:
+ FlapProtocol( QObject *parent = 0, const char *name = 0 );
+ ~FlapProtocol();
+
+ /**
+ * Attempt to parse the supplied data into an @ref SnacTransfer 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 FlapTransfer object if successfull, otherwise 0. The caller is responsible for deleting this object.
+ */
+ Transfer * parse( const QByteArray &, uint & bytes );
+
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.cpp b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp
new file mode 100644
index 00000000..960d4ee5
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icbmparamstask.cpp
@@ -0,0 +1,143 @@
+/*
+ Kopete Oscar Protocol
+ icbmparamstask.cpp - Get the ICBM parameters
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "icbmparamstask.h"
+
+#include <kdebug.h>
+#include "connection.h"
+#include "oscartypes.h"
+#include "transfer.h"
+#include "oscarutils.h"
+#include "buffer.h"
+
+ICBMParamsTask::ICBMParamsTask( Task* parent )
+ : Task( parent )
+{}
+
+
+ICBMParamsTask::~ICBMParamsTask()
+{}
+
+
+bool ICBMParamsTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st->snacService() == 4 && st->snacSubtype() == 5 )
+ return true;
+ else
+ return false;
+}
+
+bool ICBMParamsTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ handleICBMParameters();
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void ICBMParamsTask::onGo()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM Parameters request" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0004, 0x0004, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ Transfer* st = createTransfer( f, s, buffer );
+ send( st );
+}
+
+void ICBMParamsTask::handleICBMParameters()
+{
+ Buffer* buffer = transfer()->buffer();
+
+ WORD channel = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel=" << channel << endl;
+
+ /**
+ * bit1: messages allowed for specified channel
+ * bit2: missed calls notifications enabled for specified channel
+ * bit4: client supports typing notifications
+ */
+ DWORD messageFlags = buffer->getDWord();
+ WORD maxMessageSnacSize = buffer->getWord();
+ WORD maxSendWarnLvl = buffer->getWord(); // max sender Warning Level
+ WORD maxRecvWarnLvl = buffer->getWord(); // max Receiver Warning Level
+ WORD minMsgInterval = buffer->getWord(); // minimum message interval (msec)
+
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "messageFlags = " << messageFlags << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxMessageSnacSize = " << maxMessageSnacSize << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxSendWarnLvl = " << maxSendWarnLvl << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "maxRecvWarnLvl = " << maxRecvWarnLvl << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "minMsgInterval = " << minMsgInterval << endl;
+
+ /*WORD unknown = */buffer->getWord();
+
+ // Now we set our own parameters.
+ // The ICBM parameters have to be set up seperately for each channel.
+ // Some clients (namely Trillian) might refuse sending on channels that were not set up.
+ sendMessageParams( 0x01 );
+ sendMessageParams( 0x02 );
+ sendMessageParams( 0x04 );
+}
+
+void ICBMParamsTask::sendMessageParams( int channel )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICBM parameters for channel " << channel << endl;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0004, 0x0002, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+
+ // the channel for which to set up the parameters
+ buffer->addWord( channel );
+
+ //these are all read-write
+ // channel-flags
+ // bit 1 : messages allowed (always 1 or you cannot send IMs)
+ // bit 2 : missed call notifications enabled
+ // bit 4 : typing notifications enabled
+ if ( channel == 1 )
+ buffer->addDWord(0x0000000B);
+ else
+ buffer->addDWord(0x00000003);
+
+ //max message length (8000 bytes)
+ buffer->addWord(0x1f40);
+ //max sender warning level (999)
+ buffer->addWord(0x03e7);
+ //max receiver warning level (999)
+ buffer->addWord(0x03e7);
+ //min message interval limit (0 msec)
+ buffer->addWord(0x0000);
+ // unknown parameter
+ buffer->addWord(0x0000);
+
+ Transfer* st = createTransfer( f, s, buffer );
+ send( st );
+ setSuccess( 0, QString::null );
+}
+//kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/liboscar/icbmparamstask.h b/kopete/protocols/oscar/liboscar/icbmparamstask.h
new file mode 100644
index 00000000..c7bdfb16
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icbmparamstask.h
@@ -0,0 +1,56 @@
+/*
+ Kopete Oscar Protocol
+ icbmparamstask.h - Get the ICBM parameters
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef ICBMPARAMSTASK_H
+#define ICBMPARAMSTASK_H
+
+#include <task.h>
+
+/**
+Get the parameters we need to follow for instant messages
+
+@author Matt Rogers
+*/
+class ICBMParamsTask : public Task
+{
+public:
+ ICBMParamsTask( Task* parent );
+ ~ICBMParamsTask();
+
+ bool forMe( const Transfer* transfer ) const;
+ bool take( Transfer* transfer );
+
+ /**
+ * Handle the ICBM Parameters we get back from SNAC 0x04, 0x05
+ */
+ void handleICBMParameters();
+
+ /**
+ * Send the message parameters we want back to the server. Only
+ * appears to occur during login
+ * @param channel the channel to set up
+ */
+ void sendMessageParams( int channel );
+
+protected:
+ void onGo();
+
+};
+
+#endif
+
+//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.cpp b/kopete/protocols/oscar/liboscar/icqlogintask.cpp
new file mode 100644
index 00000000..c9f5cd52
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icqlogintask.cpp
@@ -0,0 +1,117 @@
+/*
+ Kopete Oscar Protocol
+ icqlogintask.cpp - Handles logging into to the ICQ service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqlogintask.h"
+
+#include <qstring.h>
+#include <kdebug.h>
+#include "transfer.h"
+#include "connection.h"
+#include "oscarutils.h"
+
+using namespace Oscar;
+
+IcqLoginTask::IcqLoginTask( Task* parent )
+ : Task( parent )
+{
+}
+
+IcqLoginTask::~IcqLoginTask()
+{
+}
+
+bool IcqLoginTask::take( Transfer* transfer )
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+bool IcqLoginTask::forMe( Transfer* transfer ) const
+{
+ //there shouldn't be a incoming transfer for this task
+ Q_UNUSED( transfer );
+ return false;
+}
+
+void IcqLoginTask::onGo()
+{
+ FLAP f = { 0x01, 0, 0 };
+ DWORD flapVersion = 0x00000001;
+ Buffer *outbuf = new Buffer();
+
+ QString encodedPassword = encodePassword( client()->password() );
+ const Oscar::ClientVersion* version = client()->version();
+
+ outbuf->addDWord( flapVersion );
+ outbuf->addTLV( 0x0001, client()->userId().length(), client()->userId().latin1() );
+ outbuf->addTLV( 0x0002, encodedPassword.length(), encodedPassword.latin1() );
+ outbuf->addTLV( 0x0003, version->clientString.length(), version->clientString.latin1() );
+ outbuf->addTLV16( 0x0016, version->clientId );
+ outbuf->addTLV16( 0x0017, version->major );
+ outbuf->addTLV16( 0x0018, version->minor );
+ outbuf->addTLV16( 0x0019, version->point );
+ outbuf->addTLV16(0x001a, version->build );
+ outbuf->addDWord( 0x00140004 ); //TLV type 0x0014, length 0x0004
+ outbuf->addDWord( version->other ); //TLV data for type 0x0014
+ outbuf->addTLV( 0x000f, version->lang.length(), version->lang.latin1() );
+ outbuf->addTLV( 0x000e, version->country.length(), version->country.latin1() );
+
+ Transfer* ft = createTransfer( f, outbuf );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending ICQ channel 0x01 login packet" << endl;
+ send( ft );
+ emit finished();
+}
+
+
+QString IcqLoginTask::encodePassword( const QString& loginPassword )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Called." << endl;
+
+ // TODO: check if latin1 is the right conversion
+ const char *password = loginPassword.latin1();
+ unsigned int i = 0;
+ QString encodedPassword = QString::null;
+
+ //encoding table used in ICQ password XOR transformation
+ unsigned char encoding_table[] =
+ {
+ 0xf3, 0x26, 0x81, 0xc4,
+ 0x39, 0x86, 0xdb, 0x92,
+ 0x71, 0xa3, 0xb9, 0xe6,
+ 0x53, 0x7a, 0x95, 0x7c
+ };
+
+ for (i = 0; i < 8; i++)
+ {
+ if(password[i] == 0)
+ break; //found a null in the password. don't encode it
+ encodedPassword.append( password[i] ^ encoding_table[i] );
+ }
+
+#ifdef OSCAR_PWDEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << " plaintext pw='" << loginPassword << "', length=" <<
+ loginPassword.length() << endl;
+
+ kdDebug(OSCAR_RAW_DEBUG) << " encoded pw='" << encodedPassword << "', length=" <<
+ encodedPassword.length() << endl;
+#endif
+
+ return encodedPassword;
+}
+
+#include "icqlogintask.moc"
diff --git a/kopete/protocols/oscar/liboscar/icqlogintask.h b/kopete/protocols/oscar/liboscar/icqlogintask.h
new file mode 100644
index 00000000..45fb3eb6
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icqlogintask.h
@@ -0,0 +1,47 @@
+/*
+ Kopete Oscar Protocol
+ icqlogintask.h - Handles logging into to the ICQ service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCAR_ICQLOGINTASK_H_
+#define _OSCAR_ICQLOGINTASK_H_
+
+#include <oscartypes.h>
+#include <task.h>
+
+class QString;
+class Transfer;
+
+using namespace Oscar;
+
+class IcqLoginTask : public Task
+{
+Q_OBJECT
+public:
+ IcqLoginTask( Task* parent );
+ ~IcqLoginTask();
+ bool take( Transfer* transfer );
+ virtual void onGo();
+
+protected:
+ bool forMe( Transfer* transfer ) const;
+
+private:
+ QString encodePassword( const QString& pw );
+
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/icqtask.cpp b/kopete/protocols/oscar/liboscar/icqtask.cpp
new file mode 100644
index 00000000..a383922f
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icqtask.cpp
@@ -0,0 +1,151 @@
+/*
+ Kopete Oscar Protocol
+ icqtask.cpp - SNAC 0x15 parsing
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icqtask.h"
+#include "buffer.h"
+#include "connection.h"
+
+#include <qstring.h>
+
+#include <kdebug.h>
+
+ICQTask::ICQTask( Task * parent )
+ : Task( parent )
+{
+ m_icquin = client()->userId().toULong();
+ m_sequence = 0;
+ m_requestType = 0xFFFF;
+ m_requestSubType = 0xFFFF;
+}
+
+ICQTask::~ ICQTask()
+{
+}
+
+void ICQTask::onGo()
+{
+}
+
+bool ICQTask::forMe( const Transfer *t ) const
+{
+ Q_UNUSED( t );
+ return false;
+}
+
+bool ICQTask::take( Transfer* t )
+{
+ Q_UNUSED( t );
+ return false;
+}
+
+void ICQTask::parseInitialData( Buffer buf )
+{
+ int tlvLength = 0;
+ WORD sequence = 0;
+ TLV tlv1 = buf.getTLV();
+ Buffer tlv1Buffer(tlv1.data, tlv1.length);
+ tlvLength = tlv1Buffer.getLEWord(); //FIXME handle the data chunk size
+ m_icquin = tlv1Buffer.getLEDWord(); //nice ICQ UIN
+ m_requestType = tlv1Buffer.getLEWord(); //request type
+ sequence = tlv1Buffer.getLEWord();
+ if ( m_requestType == 0x07DA ) //there's an extra data subtype
+ m_requestSubType = tlv1Buffer.getLEWord();
+ else
+ m_requestSubType = 0xFFFF;
+
+/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence
+ <<" request type: 0x" << QString::number( m_requestType, 16 )
+ << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl;*/
+}
+
+Buffer* ICQTask::addInitialData( Buffer* buf ) const
+{
+ if ( m_requestType == 0xFFFF )
+ { //something very wrong here
+ return 0;
+ }
+
+ Buffer* tlvData = new Buffer();
+ tlvData->addLEDWord( m_icquin ); // UIN
+ tlvData->addLEWord( m_requestType ); // request type
+ tlvData->addLEWord( m_sequence );
+
+ if ( m_requestSubType != 0xFFFF )
+ tlvData->addLEWord( m_requestSubType );
+
+ /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "uin: " << m_icquin << " sequence: " << sequence
+ <<" request type: 0x" << QString::number( m_requestType, 16 )
+ << " request sub type: 0x" << QString::number( m_requestSubType, 16 ) << endl; */
+ if ( buf != 0 )
+ tlvData->addString( buf->buffer(), buf->length() );
+
+ Buffer* newBuffer = new Buffer();
+ //add TLV 1
+ newBuffer->addWord( 0x0001 ); //TLV 1
+ newBuffer->addWord( tlvData->length() + 2 ); //TLV length
+ newBuffer->addLEWord( tlvData->length() ); // data chunk size
+ newBuffer->addString( tlvData->buffer(), tlvData->length() );
+
+ delete tlvData;
+
+ return newBuffer;
+}
+
+DWORD ICQTask::uin() const
+{
+ return m_icquin;
+}
+
+void ICQTask::setUin( DWORD uin )
+{
+ m_icquin = uin;
+}
+
+WORD ICQTask::sequence() const
+{
+ return m_sequence;
+}
+
+void ICQTask::setSequence( WORD sequence )
+{
+ m_sequence = sequence;
+}
+
+DWORD ICQTask::requestType() const
+{
+ return m_requestType;
+}
+
+void ICQTask::setRequestType( WORD type )
+{
+ m_requestType = type;
+}
+
+DWORD ICQTask::requestSubType() const
+{
+ return m_requestSubType;
+}
+
+void ICQTask::setRequestSubType( WORD subType )
+{
+ m_requestSubType = subType;
+}
+
+#include "icqtask.moc"
+
+//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/icqtask.h b/kopete/protocols/oscar/liboscar/icqtask.h
new file mode 100644
index 00000000..7d134b49
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icqtask.h
@@ -0,0 +1,63 @@
+/*
+ Kopete Oscar Protocol
+ icqtask.h - SNAC 0x15 parsing
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQTASK_H
+#define ICQTASK_H
+
+#include "task.h"
+
+using namespace Oscar;
+
+class Buffer;
+
+class ICQTask : public Task
+{
+Q_OBJECT
+public:
+ ICQTask( Task* parent );
+ ~ICQTask();
+
+ virtual void onGo();
+ virtual bool forMe( const Transfer* t ) const;
+ virtual bool take( Transfer* t );
+
+ void parseInitialData( Buffer buf );
+ Buffer* addInitialData( Buffer* buf = 0 ) const;
+
+ DWORD uin() const;
+ void setUin( DWORD uin );
+
+ WORD sequence() const;
+ void setSequence( WORD seqeunce );
+
+ DWORD requestType() const;
+ void setRequestType( WORD type );
+
+ DWORD requestSubType() const;
+ void setRequestSubType( WORD subType );
+
+private:
+ DWORD m_icquin; //little endian
+ WORD m_sequence;
+ WORD m_requestType; //little endian
+ WORD m_requestSubType; //little endian
+};
+
+//kate: tab-width 4; indent-mode csands;
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.cpp b/kopete/protocols/oscar/liboscar/icquserinfo.cpp
new file mode 100644
index 00000000..f853c045
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icquserinfo.cpp
@@ -0,0 +1,262 @@
+/*
+ Kopete Oscar Protocol
+ icquserinfo.h - ICQ User Info Data Types
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icquserinfo.h"
+#include "buffer.h"
+
+#include <kdebug.h>
+
+ICQShortInfo::ICQShortInfo()
+{
+ uin = 0;
+ needsAuth = false;
+ gender = 0;
+}
+
+void ICQShortInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ short user info packet" << endl;
+ nickname = buffer->getLELNTS();
+ firstName = buffer->getLELNTS();
+ lastName = buffer->getLELNTS();
+ email = buffer->getLELNTS();
+ needsAuth = buffer->getByte();
+ buffer->skipBytes( 1 ); //skip the unknown byte
+ gender = buffer->getByte();
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ short user info packet" << endl;
+}
+
+ICQGeneralUserInfo::ICQGeneralUserInfo()
+{
+ uin = 0;
+ country = 0;
+ timezone = 0;
+ publishEmail = false;
+ webaware = false;
+ allowsDC = false;
+}
+
+void ICQGeneralUserInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Parsing ICQ basic user info packet" << endl;
+ nickname = buffer->getLELNTS();
+ firstName = buffer->getLELNTS();
+ lastName = buffer->getLELNTS();
+ email = buffer->getLELNTS();
+ city = buffer->getLELNTS();
+ state = buffer->getLELNTS();
+ phoneNumber = buffer->getLELNTS();
+ faxNumber = buffer->getLELNTS();
+ address = buffer->getLELNTS();
+ cellNumber = buffer->getLELNTS();
+ zip = buffer->getLELNTS();
+ country = buffer->getLEWord();
+ timezone = buffer->getLEByte(); // UTC+(tzcode * 30min)
+ webaware = ( buffer->getByte() == 0x01 );
+ allowsDC = ( buffer->getByte() == 0x01 ); //taken from sim
+ publishEmail = ( buffer->getByte() == 0x01 );
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ basic user info packet" << endl;
+}
+
+ICQWorkUserInfo::ICQWorkUserInfo()
+{
+ country = 0;
+ occupation = 0;
+}
+
+void ICQWorkUserInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ city = buffer->getLELNTS();
+ state = buffer->getLELNTS();
+ phone = buffer->getLELNTS();
+ fax = buffer->getLELNTS();
+ address = buffer->getLELNTS();
+ zip = buffer->getLELNTS();
+ country = buffer->getLEWord();
+ company = buffer->getLELNTS();
+ department = buffer->getLELNTS();
+ position = buffer->getLELNTS();
+ occupation = buffer->getLEWord();
+ homepage = buffer->getLELNTS();
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl;
+}
+
+ICQMoreUserInfo::ICQMoreUserInfo()
+{
+ age = 0;
+ gender = 0;
+ lang1 = 0;
+ lang2 = 0;
+ lang3 = 0;
+ ocountry = 0;
+ marital = 0;
+}
+
+void ICQMoreUserInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ age = buffer->getLEWord();
+ gender = buffer->getByte();
+ homepage = buffer->getLELNTS();
+ WORD year = buffer->getLEWord();
+ BYTE month = buffer->getByte();
+ BYTE day = buffer->getByte();
+
+ // set birthday to NULL if at least one of the values in the buffer is 0
+ if ( year == 0 || month == 0 || day == 0 )
+ birthday = QDate();
+ else
+ birthday = QDate( year, month, day );
+
+ lang1 = buffer->getByte();
+ lang2 = buffer->getByte();
+ lang3 = buffer->getByte();
+ buffer->getLEWord(); //emtpy field
+ ocity = buffer->getLELNTS();
+ ostate = buffer->getLELNTS();
+ ocountry = buffer->getLEWord();
+ marital = buffer->getLEWord();
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Couldn't parse ICQ work user info packet" << endl;
+}
+
+ICQEmailInfo::ICQEmailInfo()
+{
+}
+
+void ICQEmailInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ int numEmails = buffer->getByte();
+ QString email;
+ for ( int i = 0; i < numEmails; i++ )
+ {
+ if ( buffer->getByte() == 0x00 )
+ email = buffer->getLELNTS();
+ }
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ email user info packet" << endl;
+}
+
+ICQInterestInfo::ICQInterestInfo()
+{
+ count=0;
+}
+
+void ICQInterestInfo::fill( Buffer* buffer )
+{
+ if ( buffer->getByte() == 0x0A )
+ {
+ count=0; //valid interests
+ int len= buffer->getByte(); //interests we get
+ for ( int i = 0; i < len; i++ )
+ {
+ int t=buffer->getLEWord();
+ QCString d = buffer->getLELNTS();
+ if (t>0) { //there is some topic
+ if (count<4) { //i think this could not happen, i have never seen more
+ topics[count]=t;
+ descriptions[count]=d;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got topic: "<<topics[count]<<" desc: " << topics[count] << endl;
+ count++;
+ } else {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got more than four interest infos" << endl;
+ }
+ }
+ }
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "LEN: "<< len << " COUNT: " << count<< endl;
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Coudln't parse ICQ interest user info packet" << endl;
+}
+
+ICQSearchResult::ICQSearchResult()
+{
+ auth = false;
+ online = false;
+ gender = 'U';
+}
+
+void ICQSearchResult::fill( Buffer* buffer )
+{
+ WORD datalength = buffer->getLEWord(); // data length
+ WORD len = 0;
+ uin = buffer->getLEDWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found UIN " << QString::number( uin ) << endl;
+ len = buffer->getLEWord();
+ if ( len > 0 )
+ nickName = QCString( buffer->getBlock( len ) );
+
+ len = buffer->getLEWord();
+ if ( len > 0 )
+ firstName = QCString( buffer->getBlock( len ) );
+
+ len = buffer->getLEWord();
+ if ( len > 0 )
+ lastName = QCString( buffer->getBlock( len ) );
+
+ len = buffer->getLEWord();
+ if ( len > 0 )
+ email = QCString( buffer->getBlock( len ) );
+
+ auth = ( buffer->getByte() != 0x01 );
+ online = ( buffer->getLEWord() == 0x0001 );
+ switch ( buffer->getByte() )
+ {
+ case 0x00:
+ gender = 'M';
+ break;
+ case 0x01:
+ gender = 'F';
+ break;
+ default:
+ gender = 'U';
+ break;
+ }
+ age = buffer->getLEWord();
+}
+
+ICQWPSearchInfo::ICQWPSearchInfo()
+{
+ age = 0;
+ gender = 0;
+ language = 0;
+ country = 0;
+ occupation = 0;
+ onlineOnly = false;
+}
+
+
+
+//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/icquserinfo.h b/kopete/protocols/oscar/liboscar/icquserinfo.h
new file mode 100644
index 00000000..ac054721
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icquserinfo.h
@@ -0,0 +1,213 @@
+/*
+ Kopete Oscar Protocol
+ icquserinfo.h - ICQ User Info Data Types
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _ICQUSERINFO_H_
+#define _ICQUSERINFO_H_
+
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include <qdatetime.h>
+#include "kopete_export.h"
+
+class Buffer;
+
+/**
+ * @file icquserinfo.h
+ * Classes encapsulating user data retrieved from the server
+ */
+
+class KOPETE_EXPORT ICQInfoBase
+{
+public:
+
+ ICQInfoBase() : m_sequence( 0 ) {}
+ virtual ~ICQInfoBase() {}
+ virtual void fill( Buffer* buffer ) = 0;
+
+ void setSequenceNumber( int number ) { m_sequence = number; }
+ int sequenceNumber() { return m_sequence; }
+
+private:
+ int m_sequence;
+};
+
+
+class KOPETE_EXPORT ICQShortInfo : public ICQInfoBase
+{
+public:
+ ICQShortInfo();
+ ~ICQShortInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ unsigned long uin;
+ QCString nickname;
+ QCString firstName;
+ QCString lastName;
+ QCString email;
+ bool needsAuth;
+ unsigned int gender; // 0=offline, 1=online, 2=not webaware
+};
+
+class KOPETE_EXPORT ICQGeneralUserInfo : public ICQInfoBase
+{
+public:
+ ICQGeneralUserInfo();
+ ~ICQGeneralUserInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ unsigned long uin;
+ QCString nickname;
+ QCString firstName;
+ QCString lastName;
+ QCString email;
+ QCString city;
+ QCString state;
+ QCString phoneNumber;
+ QCString faxNumber;
+ QCString address;
+ QCString cellNumber;
+ QCString zip;
+ int country;
+ char timezone;
+ bool publishEmail;
+ bool allowsDC;
+ bool webaware;
+};
+
+class KOPETE_EXPORT ICQWorkUserInfo : public ICQInfoBase
+{
+public:
+ ICQWorkUserInfo();
+ ~ICQWorkUserInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ QCString city;
+ QCString state;
+ QCString phone;
+ QCString fax;
+ QCString address;
+ QCString zip;
+ int country;
+ QCString company;
+ QCString department;
+ QCString position;
+ int occupation;
+ QCString homepage;
+};
+
+class KOPETE_EXPORT ICQMoreUserInfo : public ICQInfoBase
+{
+public:
+ ICQMoreUserInfo();
+ ~ICQMoreUserInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ int age;
+ unsigned int gender;
+ QCString homepage;
+ QDate birthday;
+ unsigned int lang1;
+ unsigned int lang2;
+ unsigned int lang3;
+ QCString ocity;
+ QCString ostate;
+ int ocountry;
+ int marital;
+};
+
+class KOPETE_EXPORT ICQEmailInfo : public ICQInfoBase
+{
+public:
+ ICQEmailInfo();
+ ~ICQEmailInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ QValueList<QCString> emailList;
+};
+
+class KOPETE_EXPORT ICQInterestInfo : public ICQInfoBase
+{
+public:
+ ICQInterestInfo();
+ ~ICQInterestInfo() {}
+ void fill( Buffer* buffer );
+
+public:
+ int count;
+ int topics[4];
+ QCString descriptions[4];
+};
+
+
+class KOPETE_EXPORT ICQSearchResult
+{
+public:
+ ICQSearchResult();
+ void fill( Buffer* buffer );
+ Q_UINT32 uin;
+ QCString firstName;
+ QCString lastName;
+ QCString nickName;
+ QCString email;
+ bool auth;
+ bool online;
+ char gender;
+ Q_UINT16 age;
+};
+
+class KOPETE_EXPORT ICQWPSearchInfo
+{
+public:
+ ICQWPSearchInfo();
+
+ QCString firstName;
+ QCString lastName;
+ QCString nickName;
+ QCString email;
+ int age;
+ int gender;
+ int language;
+ QCString city;
+ QCString state;
+ int country;
+ QCString company;
+ QCString department;
+ QCString position;
+ int occupation;
+ bool onlineOnly;
+};
+
+/*
+class ICQInfoItem
+{
+public:
+ int category;
+ QCString description;
+};
+
+
+typedef QValueList<ICQInfoItem> ICQInfoItemList;
+*/
+
+#endif
+//kate: space-indent off; tab-width 4; replace-tabs off; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.cpp b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp
new file mode 100644
index 00000000..068ac273
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icquserinfotask.cpp
@@ -0,0 +1,234 @@
+/*
+ Kopete Oscar Protocol
+ icqtask.h - SNAC 0x15 parsing
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "icquserinfotask.h"
+#include <kdebug.h>
+#include "connection.h"
+#include "transfer.h"
+#include "buffer.h"
+
+
+ICQUserInfoRequestTask::ICQUserInfoRequestTask( Task* parent ) : ICQTask( parent )
+{
+ //by default, request short info. it saves bandwidth
+ m_type = Short;
+}
+
+
+ICQUserInfoRequestTask::~ICQUserInfoRequestTask()
+{
+}
+
+
+bool ICQUserInfoRequestTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer );
+
+ if ( !st )
+ return false;
+
+ if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 )
+ return false;
+
+ Buffer buf( *( st->buffer() ) );
+ const_cast<ICQUserInfoRequestTask*>( this )->parseInitialData( buf );
+
+ if ( requestType() == 0x07DA )
+ {
+ switch ( requestSubType() )
+ {
+ case 0x00C8:
+ case 0x00D2:
+ case 0x00DC:
+ case 0x00E6:
+ case 0x00EB:
+ case 0x00F0:
+ case 0x00FA:
+ case 0x0104:
+ case 0x010E:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool ICQUserInfoRequestTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ ICQGeneralUserInfo genInfo;
+ ICQWorkUserInfo workInfo;
+ ICQMoreUserInfo moreInfo;
+ ICQEmailInfo emailInfo;
+ ICQShortInfo shortInfo;
+ ICQInterestInfo interestInfo;
+
+ setTransfer( transfer );
+ TLV tlv1 = transfer->buffer()->getTLV();
+ Buffer* buffer = new Buffer( tlv1.data, tlv1.length );
+
+ //FIXME this is silly. parseInitialData should take care of this for me.
+ buffer->skipBytes( 8 );
+ WORD seq = buffer->getLEWord(); // request sequence number
+ buffer->getLEWord(); // request data sub type
+ QString contactId = m_contactSequenceMap[seq];
+
+ switch ( requestSubType() )
+ {
+ case 0x00C8: //basic user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received basic info" << endl;
+ genInfo.setSequenceNumber( seq );
+ genInfo.fill( buffer );
+ m_genInfoMap[seq] = genInfo;
+ break;
+ case 0x00D2: //work user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received work info" << endl;
+ workInfo.setSequenceNumber( seq );
+ workInfo.fill( buffer );
+ m_workInfoMap[seq] = workInfo;
+ break;
+ case 0x00DC: //more user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received more info" << endl;
+ moreInfo.setSequenceNumber( seq );
+ moreInfo.fill( buffer );
+ m_moreInfoMap[seq] = moreInfo;
+ break;
+ case 0x00E6: //notes user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got Notes info, but we don't support it yet" << endl;
+ break;
+ case 0x00EB: //email user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received email info" << endl;
+ emailInfo.setSequenceNumber( seq );
+ emailInfo.fill( buffer );
+ m_emailInfoMap[seq] = emailInfo;
+ break;
+ case 0x00F0: //interests user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received interest info" << endl;
+ interestInfo.setSequenceNumber( seq );
+ interestInfo.fill( buffer );
+ m_interestInfoMap[seq] = interestInfo;
+ break;
+ case 0x00FA: //affliations user info
+ //affliations seems to be the last info we get, so be hacky and only emit the signal once
+ emit receivedInfoFor( contactId, Long );
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got affliations info, but we don't support it yet" << endl;
+ break;
+ case 0x0104:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received short user info" << endl;
+ shortInfo.setSequenceNumber( seq );
+ shortInfo.fill( buffer );
+ m_shortInfoMap[seq] = shortInfo;
+ break;
+ case 0x010E: //homepage category user info
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got homepage category info, but we don't support it yet" << endl;
+ break;
+ default:
+ break;
+ }
+
+
+ if ( m_type == Short )
+ emit receivedInfoFor( contactId, Short );
+
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void ICQUserInfoRequestTask::onGo()
+{
+ if ( m_userToRequestFor.isNull() )
+ return;
+
+ Buffer* sendBuf = 0L;
+ Buffer b;
+ if ( m_type != Short )
+ {
+ setRequestSubType( 0x04D0 );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting full user info for " << m_userToRequestFor << endl;
+ }
+ else
+ {
+ setRequestSubType( 0x04BA );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting short user info for " << m_userToRequestFor << endl;
+ }
+
+ setSequence( client()->snacSequence() );
+ setRequestType( 0x07D0 );
+ b.addLEDWord( m_userToRequestFor.toULong() );
+ sendBuf = addInitialData( &b );
+
+ m_contactSequenceMap[sequence()] = m_userToRequestFor;
+ m_reverseContactMap[m_userToRequestFor] = sequence();
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0015, 0x0002, 0, client()->snacSequence() };
+ Transfer* t = createTransfer( f, s, sendBuf );
+ send( t );
+}
+
+ICQGeneralUserInfo ICQUserInfoRequestTask::generalInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_genInfoMap[seq];
+}
+
+ICQWorkUserInfo ICQUserInfoRequestTask::workInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_workInfoMap[seq];
+}
+
+ICQMoreUserInfo ICQUserInfoRequestTask::moreInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_moreInfoMap[seq];
+}
+
+ICQEmailInfo ICQUserInfoRequestTask::emailInfoFor(const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_emailInfoMap[seq];
+}
+
+ICQShortInfo ICQUserInfoRequestTask::shortInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_shortInfoMap[seq];
+}
+
+ICQInterestInfo ICQUserInfoRequestTask::interestInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_interestInfoMap[seq];
+}
+
+QString ICQUserInfoRequestTask::notesInfoFor( const QString& contact )
+{
+ int seq = m_reverseContactMap[contact];
+ return m_notesInfoMap[seq];
+}
+
+
+#include "icquserinfotask.moc"
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/icquserinfotask.h b/kopete/protocols/oscar/liboscar/icquserinfotask.h
new file mode 100644
index 00000000..eba81b57
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/icquserinfotask.h
@@ -0,0 +1,77 @@
+/*
+ Kopete Oscar Protocol
+ icquserinfotask.h - SNAC 0x15 parsing for user info
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef ICQUSERINFOTASK_H
+#define ICQUSERINFOTASK_H
+
+#include <qmap.h>
+#include <qstring.h>
+
+#include "icqtask.h"
+#include "icquserinfo.h"
+
+class Transfer;
+
+/**
+@author Kopete Developers
+*/
+class ICQUserInfoRequestTask : public ICQTask
+{
+Q_OBJECT
+public:
+ ICQUserInfoRequestTask( Task* parent );
+ ~ICQUserInfoRequestTask();
+
+ enum { Long = 0, Short };
+
+ void setUser( const QString& user ) { m_userToRequestFor = user; }
+ void setType( unsigned int type ) { m_type = type; }
+ void setInfoToRequest( unsigned int type );
+
+ ICQGeneralUserInfo generalInfoFor( const QString& contact );
+ ICQEmailInfo emailInfoFor( const QString& contact );
+ ICQMoreUserInfo moreInfoFor( const QString& contact );
+ ICQWorkUserInfo workInfoFor( const QString& contact );
+ QString notesInfoFor( const QString& contact );
+ ICQShortInfo shortInfoFor( const QString& contact );
+ ICQInterestInfo interestInfoFor( const QString& contact );
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+signals:
+ void receivedInfoFor( const QString& contact, unsigned int type );
+
+private:
+ QMap<int, ICQGeneralUserInfo> m_genInfoMap;
+ QMap<int, ICQEmailInfo> m_emailInfoMap;
+ QMap<int, ICQMoreUserInfo> m_moreInfoMap;
+ QMap<int, ICQWorkUserInfo> m_workInfoMap;
+ QMap<int, ICQShortInfo> m_shortInfoMap;
+ QMap<int, ICQInterestInfo> m_interestInfoMap;
+ QMap<int, QString> m_notesInfoMap;
+ QMap<int, QString> m_contactSequenceMap;
+ QMap<QString, int> m_reverseContactMap;
+ unsigned int m_type;
+ QString m_userToRequestFor;
+
+};
+#endif
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp
new file mode 100644
index 00000000..abd34e53
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.cpp
@@ -0,0 +1,100 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "inputprotocolbase.h"
+
+InputProtocolBase::InputProtocolBase(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+ m_state = NeedMore;
+ m_bytes = 0;
+}
+
+
+InputProtocolBase::~InputProtocolBase()
+{
+}
+
+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 )
+ {
+ if ( m_din->atEnd() )
+ {
+ m_state = NeedMore;
+ qDebug( "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 > 1024 )
+ 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 ) )
+ {
+ qDebug( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %i bytes out of %i", temp.length(), val );
+ m_state = NeedMore;
+ return false;
+ }
+ }
+ data = temp;
+ len = val;
+ m_bytes += val;
+ return true;
+}
+
+#include "inputprotocolbase.moc"
diff --git a/kopete/protocols/oscar/liboscar/inputprotocolbase.h b/kopete/protocols/oscar/liboscar/inputprotocolbase.h
new file mode 100644
index 00000000..7bea895f
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/inputprotocolbase.h
@@ -0,0 +1,72 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef INPUTPROTOCOLBASE_H
+#define INPUTPROTOCOLBASE_H
+
+#include <qobject.h>
+
+class Transfer;
+/**
+Defines a basic interface for protocols dealing with input from the GroupWise server.
+
+@author Matt Rogers
+*/
+class InputProtocolBase : public QObject
+{
+Q_OBJECT
+public:
+ enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError };
+ InputProtocolBase(QObject *parent = 0, const char *name = 0);
+ ~InputProtocolBase();
+ /**
+ * 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/oscar/liboscar/locationrightstask.cpp b/kopete/protocols/oscar/liboscar/locationrightstask.cpp
new file mode 100644
index 00000000..5aae9a5e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/locationrightstask.cpp
@@ -0,0 +1,87 @@
+/*
+ Kopete Oscar Protocol
+ locationrightstask.cpp - Set up the service limitations
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "locationrightstask.h"
+#include <kdebug.h>
+#include "buffer.h"
+#include "transfer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "connection.h"
+
+using namespace Oscar;
+
+LocationRightsTask::LocationRightsTask( Task* parent )
+ : Task( parent )
+{
+}
+
+
+LocationRightsTask::~LocationRightsTask()
+{
+}
+
+
+bool LocationRightsTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st->snacService() == 2 && st->snacSubtype() == 3 )
+ return true;
+ else
+ return false;
+}
+
+bool LocationRightsTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ handleLocationRightsResponse();
+ setTransfer( 0 );
+ return true;
+ }
+ else
+ return false;
+}
+
+void LocationRightsTask::onGo()
+{
+ sendLocationRightsRequest();
+}
+
+void LocationRightsTask::sendLocationRightsRequest()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0002, 0x0002, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer();
+ Transfer* st = createTransfer( f, s, b );
+ send( st );
+}
+
+void LocationRightsTask::handleLocationRightsResponse()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring location rights response" << endl;
+ setSuccess( 0, QString::null );
+}
+
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/locationrightstask.h b/kopete/protocols/oscar/liboscar/locationrightstask.h
new file mode 100644
index 00000000..a3e82767
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/locationrightstask.h
@@ -0,0 +1,57 @@
+/*
+ Kopete Oscar Protocol
+ locationrightstask.h - Set up the service limitations
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LOCATIONRIGHTSTASK_H
+#define LOCATIONRIGHTSTASK_H
+
+#include <task.h>
+
+class Transfer;
+
+using namespace Oscar;
+
+/**
+This task handles location rights.
+This task implements the following SNACS:
+ \li 0x02, 0x02
+ \li 0x02, 0x03
+
+@author Kopete Developers
+*/
+class LocationRightsTask : public Task
+{
+public:
+ LocationRightsTask( Task* parent );
+ ~LocationRightsTask();
+ bool take( Transfer* transfer );
+
+protected:
+ bool forMe( const Transfer* transfer ) const;
+ void onGo();
+
+private:
+ //! Send the location rights request ( SNAC 0x02, 0x02 )
+ void sendLocationRightsRequest();
+
+ //! Handle the location rights reply ( SNAC 0x02, 0x03 )
+ void handleLocationRightsResponse();
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/logintask.cpp b/kopete/protocols/oscar/liboscar/logintask.cpp
new file mode 100644
index 00000000..962b2e1a
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/logintask.cpp
@@ -0,0 +1,218 @@
+/*
+ Kopete Oscar Protocol
+ logintask.cpp - Handles logging into to the AIM or ICQ service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "logintask.h"
+
+#include <qtimer.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "aimlogintask.h"
+#include "connection.h"
+#include "closeconnectiontask.h"
+#include "icqlogintask.h"
+#include "oscarutils.h"
+#include "rateinfotask.h"
+#include "serverversionstask.h"
+#include "transfer.h"
+
+
+
+/**
+ * Stage One Task Implementation
+ */
+
+StageOneLoginTask::StageOneLoginTask( Task* parent )
+ : Task ( parent )
+{
+ m_aimTask = 0L;
+ m_icqTask = 0L;
+ m_closeTask = 0L;
+}
+
+StageOneLoginTask::~StageOneLoginTask()
+{
+ delete m_aimTask;
+ delete m_icqTask;
+ delete m_closeTask;
+}
+
+bool StageOneLoginTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ if ( client()->isIcq() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting ICQ login" << endl;
+ m_icqTask = new IcqLoginTask( client()->rootTask() );
+ m_closeTask = new CloseConnectionTask( client()->rootTask() );
+
+ //connect finished signal
+ connect( m_closeTask, SIGNAL( finished() ), this, SLOT( closeTaskFinished() ) );
+ m_icqTask->go( true );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Starting AIM login" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the FLAP version back" << endl;
+
+ //send the flap version response
+ FLAP f = { 0x01, 0 , 0 };
+ Buffer *outbuf = new Buffer;
+ outbuf->addDWord(0x00000001); //flap version
+ f.length = outbuf->length();
+ Transfer* ft = createTransfer( f, outbuf );
+ send( ft );
+
+ m_aimTask = new AimLoginTask( client()->rootTask() );
+ connect( m_aimTask, SIGNAL( finished() ), this, SLOT( aimTaskFinished() ) );
+ m_aimTask->go( true );
+ }
+ return true;
+ }
+ return false;
+}
+
+void StageOneLoginTask::closeTaskFinished()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ m_cookie = m_closeTask->cookie();
+ m_bosPort = m_closeTask->bosPort();
+ m_bosServer = m_closeTask->bosHost();
+ m_closeTask->safeDelete();
+ setSuccess( m_closeTask->statusCode(), m_closeTask->statusString() );
+}
+
+void StageOneLoginTask::aimTaskFinished()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ m_cookie = m_aimTask->cookie();
+ m_bosPort = m_aimTask->bosPort();
+ m_bosServer = m_aimTask->bosHost();
+
+ setSuccess( m_aimTask->statusCode(), m_aimTask->statusString() );
+}
+
+bool StageOneLoginTask::forMe( Transfer* transfer ) const
+{
+ FlapTransfer* ft = dynamic_cast<FlapTransfer*> ( transfer );
+
+ if (!ft)
+ return false;
+
+ return ( ft && ft->flapChannel() == 1 );
+}
+
+const QByteArray& StageOneLoginTask::loginCookie() const
+{
+ return m_cookie;
+}
+
+const QString& StageOneLoginTask::bosServer() const
+{
+ return m_bosServer;
+}
+
+const QString& StageOneLoginTask::bosPort() const
+{
+ return m_bosPort;
+}
+
+
+/**
+ * Stage Two Task Implementation
+ */
+StageTwoLoginTask::StageTwoLoginTask( Task* parent )
+ : Task( parent )
+{
+ //Create our tasks
+ Task* rootTask = client()->rootTask();
+ m_versionTask = new ServerVersionsTask( rootTask );
+ m_rateTask = new RateInfoTask( rootTask );
+
+ QObject::connect( m_versionTask, SIGNAL( finished() ), this, SLOT( versionTaskFinished() ) );
+ QObject::connect( m_rateTask, SIGNAL( finished() ), this, SLOT( rateTaskFinished() ) );
+}
+
+StageTwoLoginTask::~StageTwoLoginTask()
+{
+ delete m_versionTask;
+}
+
+bool StageTwoLoginTask::take( Transfer* transfer )
+{
+ bool yes = forMe( transfer );
+ return yes;
+}
+
+bool StageTwoLoginTask::forMe( Transfer* transfer ) const
+{
+ FlapTransfer* ft = dynamic_cast<FlapTransfer*>( transfer );
+
+ if (!ft)
+ return false;
+
+ int channel = ft->flapChannel();
+ if ( channel == 1 )
+ return true;
+ else
+ return false;
+}
+
+void StageTwoLoginTask::onGo()
+{
+ if ( !m_cookie.isEmpty() )
+ {
+ //send the flap back
+ FLAP f = { 0x01, 0, 0 };
+ Buffer* outbuf = new Buffer();
+ outbuf->addDWord( 0x00000001 );
+ outbuf->addTLV( 0x06, m_cookie.size(), m_cookie.data() );
+ Transfer* ft = createTransfer( f, outbuf );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending the login cookie back" << endl;
+ send( ft );
+ }
+ else
+ setError( -1, QString::null );
+ return;
+}
+
+void StageTwoLoginTask::setCookie( const QByteArray& newCookie )
+{
+ m_cookie.duplicate( newCookie );
+}
+
+const QByteArray& StageTwoLoginTask::cookie()
+{
+ return m_cookie;
+}
+
+void StageTwoLoginTask::versionTaskFinished()
+{
+ //start the rate info task
+ m_rateTask->go(true);
+}
+
+void StageTwoLoginTask::rateTaskFinished()
+{
+ setSuccess( 0, QString::null );
+}
+
+#include "logintask.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/logintask.h b/kopete/protocols/oscar/liboscar/logintask.h
new file mode 100644
index 00000000..12061821
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/logintask.h
@@ -0,0 +1,144 @@
+/*
+ Kopete Oscar Protocol
+ logintask.h - Handles logging into to the AIM or ICQ service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCAR_LOGINTASK_H_
+#define _OSCAR_LOGINTASK_H_
+
+#include <qcstring.h>
+#include "oscartypes.h"
+#include "task.h"
+
+#include "aimlogintask.h"
+#include "icqlogintask.h"
+#include "closeconnectiontask.h"
+
+using namespace Oscar;
+
+class QString;
+class Transfer;
+
+
+/**
+ * \short Handle OSCAR login - stage 1
+ *
+ * OSCAR login is divided into two stages. The first stage handles the connection negotiation
+ * so that we can get a BOS server to connect to and start stage two of the login process
+ * This class handles the first stage of the OSCAR login process
+ * For more info about the OSCAR login process, visit http://iserverd1.khstu.ru/oscar
+ */
+class StageOneLoginTask : public Task
+{
+Q_OBJECT
+public:
+ StageOneLoginTask( Task* parent );
+ ~StageOneLoginTask();
+ bool take( Transfer* transfer );
+
+ //Protocol specific stuff
+
+ //! Get the BOS cookie
+ const QByteArray& loginCookie() const;
+
+ //! Get the BOS server
+ const QString& bosServer() const;
+
+ //! Get the BOS port
+ const QString& bosPort() const;
+
+ //! Get the error code, if there is one
+ int errorCode() const;
+
+ //! Get the error reason so it can be displayed
+ const QString& errorReason() const;
+
+
+public slots:
+ void closeTaskFinished();
+ void aimTaskFinished();
+
+protected:
+ bool forMe( Transfer* transfer ) const;
+
+private:
+
+ //Tasks we want to control
+ AimLoginTask* m_aimTask;
+ IcqLoginTask* m_icqTask;
+ CloseConnectionTask* m_closeTask;
+
+ //Private data we get from the tasks
+ QByteArray m_cookie;
+ QString m_bosServer;
+ QString m_bosPort;
+
+};
+
+/**
+ * \short Handle OSCAR Login - stage 2
+ *
+ * Oscar login is divided into two stages. The first stage handles the connection negotiation
+ * so that we can get a BOS server to connect to for the second stage. This class handles the
+ * second stage of Oscar login that establishes various things like rate limits, contact lists,
+ * and SNAC family versions
+ */
+
+class ServerVersionsTask;
+class RateInfoTask;
+
+class StageTwoLoginTask : public Task
+{
+Q_OBJECT
+public:
+ StageTwoLoginTask( Task* parent );
+ ~StageTwoLoginTask();
+ bool take( Transfer* transfer );
+ void onGo();
+
+ //protocol specifics
+ //! Set the cookie to send to the server
+ void setCookie( const QByteArray& newCookie );
+
+ //! Get the cookie to send to the server
+ const QByteArray& cookie();
+
+ QString host() const;
+ QString port() const;
+
+public slots:
+
+ //! Start the rate info task
+ void versionTaskFinished();
+
+ //! The rate info task is finished. Start the other ones
+ void rateTaskFinished();
+
+protected:
+ bool forMe( Transfer* transfer ) const;
+
+private:
+ QByteArray m_cookie;
+ QString m_host, m_port;
+
+ //tasks
+ ServerVersionsTask* m_versionTask;
+ RateInfoTask* m_rateTask;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/md5.c b/kopete/protocols/oscar/liboscar/md5.c
new file mode 100644
index 00000000..e6273585
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/md5.c
@@ -0,0 +1,392 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+#include <string.h>
+main()
+{
+ static const char *const test[7] = {
+ "", /*d41d8cd98f00b204e9800998ecf8427e*/
+ "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+ "abc", /*900150983cd24fb0d6963f7d28e17f72*/
+ "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+ "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ /*d174ab98d277d9f5a5611c2c9f419d9f*/
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+ };
+ int i;
+
+ for (i = 0; i < 7; ++i) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ printf("MD5 (\"%s\") = ", test[i]);
+ for (di = 0; di < 16; ++di)
+ printf("%02x", digest[di]);
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ md5_word_t X[16];
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/kopete/protocols/oscar/liboscar/md5.h b/kopete/protocols/oscar/liboscar/md5.h
new file mode 100644
index 00000000..501cdbe1
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/md5.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints. Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+#ifdef P1
+void md5_init(P1(md5_state_t *pms));
+#else
+void md5_init(md5_state_t *pms);
+#endif
+
+/* Append a string to the message. */
+#ifdef P3
+void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes));
+#else
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+#endif
+
+/* Finish the message and return the digest. */
+#ifdef P2
+void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16]));
+#else
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#endif
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.cpp b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp
new file mode 100644
index 00000000..2db05eb1
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/messagereceivertask.cpp
@@ -0,0 +1,461 @@
+/*
+ messagereceivertask.cpp - Incoming OSCAR Messaging Handler
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "messagereceivertask.h"
+
+#include <qtextcodec.h>
+#include <kdebug.h>
+#include "transfer.h"
+#include "buffer.h"
+#include "connection.h"
+#include "oscarutils.h"
+#include "userdetails.h"
+
+
+MessageReceiverTask::MessageReceiverTask( Task* parent ) : Task( parent )
+{
+}
+
+
+MessageReceiverTask::~MessageReceiverTask()
+{
+}
+
+
+bool MessageReceiverTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0004 )
+ {
+ WORD subtype = st->snacSubtype();
+ switch ( subtype )
+ {
+ case 0x0007:
+ case 0x000B:
+ return true;
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+ else
+ return false;
+}
+
+bool MessageReceiverTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+ m_currentSnacSubtype = st->snacSubtype();
+
+ Buffer* b = transfer->buffer();
+ m_icbmCookie = b->getBlock( 8 );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "icbm cookie is " << m_icbmCookie << endl;
+ m_channel = b->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "channel is " << m_channel << endl;
+
+ if ( m_currentSnacSubtype == 0x0007 )
+ {
+ UserDetails ud;
+ ud.fill( b );
+ m_fromUser = ud.userId();
+
+ switch( m_channel )
+ {
+ case 0x0001:
+ setTransfer( transfer );
+ handleType1Message();
+ setTransfer( 0 );
+ return true;
+ break;
+ case 0x0002:
+ setTransfer( transfer );
+ handleType2Message();
+ setTransfer( 0 );
+ return true;
+ break;
+ case 0x0004:
+ setTransfer( transfer );
+ handleType4Message();
+ setTransfer( 0 );
+ return true;
+ break;
+ default:
+ kdWarning(OSCAR_RAW_DEBUG) << "A message was received on an unknown channel. Channel is " << m_channel << endl;
+ return false;
+ break;
+ }
+ }
+ else
+ {
+ int screenNameLength = b->getByte();
+ m_fromUser = QString( b->getBlock( screenNameLength ) );
+ setTransfer( transfer );
+ handleAutoResponse();
+ setTransfer( 0 );
+ return true;
+ }
+ }
+ return false;
+}
+
+void MessageReceiverTask::handleType1Message()
+{
+ Oscar::Message msg;
+ QValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList();
+ TLV t = Oscar::findTLV( messageTLVList, 0x0002 );
+ if ( !t )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a message packet with no message!" << endl;
+ return;
+ }
+ Buffer messageBuffer( t.data );
+ QValueList<TLV> innerTLVList = messageBuffer.getTLVList();
+ QValueList<TLV>::iterator it = innerTLVList.begin(), listEnd = innerTLVList.end();
+ for ( ; (*it); ++it )
+ {
+ switch ( ( *it ).type )
+ {
+ case 0x0501:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got features tlv. length: "
+ << ( *it ).length << " data: " << ( *it ).data << endl;
+ break;
+ case 0x0101:
+ {
+ Buffer message( ( *it ).data );
+ m_charSet = message.getWord();
+ m_subCharSet = message.getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message charset: " << m_charSet
+ << " message subcharset: " << m_subCharSet << endl;
+ if ( m_charSet == 0x0002 )
+ msg.setEncoding( Oscar::Message::UCS2 );
+ else
+ msg.setEncoding( Oscar::Message::UserDefined );
+
+ //message length is buffer length - length of ( charset + subcharset ) */
+ int msgLength = ( *it ).length - 4;
+ QByteArray msgArray( message.getBlock( msgLength ) );
+ msg.setTextArray( msgArray );
+
+ break;
+ } //end case
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << ( *it ).type << endl;
+ break;
+ } //end switch
+ }
+
+ TLV autoResponse = Oscar::findTLV( messageTLVList, 0x0004 );
+ if ( autoResponse )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << "auto response message" << endl;
+ msg.addProperty( Oscar::Message::AutoResponse );
+ }
+ else
+ msg.addProperty( Oscar::Message::Normal );
+
+ msg.setSender( m_fromUser );
+ msg.setReceiver( client()->userId() );
+ msg.setTimestamp( QDateTime::currentDateTime() );
+ msg.setType( 0x01 );
+
+ emit receivedMessage( msg );
+}
+
+void MessageReceiverTask::handleType2Message()
+{
+ kdDebug(14151) << k_funcinfo << "Received Type 2 message. Trying to handle it..." << endl;
+
+ Oscar::Message msg;
+ QValueList<TLV> messageTLVList = transfer()->buffer()->getTLVList();
+ TLV t = Oscar::findTLV( messageTLVList, 0x0005 );
+ if ( !t )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "Received a channel 2 message packet with no message!" << endl;
+ return;
+ }
+ Buffer messageBuffer( t.data );
+ kdDebug(14151) << k_funcinfo << "Buffer length is " << messageBuffer.length() << endl;
+
+ // request type
+ int requestType = messageBuffer.getWord();
+ kdDebug(14151) << k_funcinfo << "Request type (0 - request, 1 - cancel, 2 - accept): " << requestType << endl;
+
+ // skip the message id cookie, already handled above
+ messageBuffer.skipBytes( 8 );
+
+ // next is capability identifier (GUID). skip for now
+ messageBuffer.skipBytes( 16 );
+
+ while( messageBuffer.length() > 0 )
+ {
+ TLV tlv = messageBuffer.getTLV();
+ switch ( tlv.type )
+ {
+ case 0x0004:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got external ip: "
+ << tlv.length << " data: " << tlv.data << endl;
+ break;
+ case 0x0005:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got listening port: "
+ << tlv.length << " data: " << tlv.data << endl;
+ break;
+ case 0x000A:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got Acktype: " // 0x0001 normal message, 2 Abort Request, 3 Acknowledge request
+ << tlv.length << " data: " << tlv.data << endl;
+ break;
+ case 0x000B:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown TLV 0x000B: "
+ << tlv.length << " data: " << tlv.data << endl;
+ break;
+ case 0x000F:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown empty TLV 0x000F" << endl;
+ break;
+ case 0x2711:
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got a TLV 2711" << endl;
+ Buffer tlv2711Buffer( tlv.data );
+ parseRendezvousData( &tlv2711Buffer, &msg );
+ if ( msg.messageType() == 0x1A )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received plugin message" << endl;
+ break;
+ }
+
+ switch ( requestType )
+ {
+ case 0x00: // some request
+ emit receivedMessage( msg );
+ break;
+ case 0x01:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received Abort Mesage" << endl;
+ break;
+ case 0x02:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received OK Message" << endl;
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Received unknown request type: " << requestType << endl;
+ break;
+ }
+
+ break;
+ } //end case
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << "Ignoring TLV of type " << tlv.type << endl;
+ break;
+ } //end switch
+ }//end while
+}
+
+void MessageReceiverTask::handleType4Message()
+{
+ TLV tlv5 = transfer()->buffer()->getTLV();
+ kdDebug(14151) << k_funcinfo << "The first TLV is of type " << tlv5.type << endl;
+ if (tlv5.type != 0x0005)
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Aborting because first TLV != TLV(5)" << endl;
+ return;
+ }
+
+ Buffer tlv5buffer(tlv5.data, tlv5.length);
+
+ DWORD uin = tlv5buffer.getLEDWord(); // little endian for no sane reason!
+ if ( QString::number(uin) != m_fromUser )
+ kdWarning(14151) << k_funcinfo << "message uin does not match uin found in packet header!" << endl;
+
+ BYTE msgType = tlv5buffer.getByte();
+ BYTE msgFlags = tlv5buffer.getByte();
+
+ kdDebug(14151) << k_funcinfo << "Received server message. type = " << msgType
+ << ", flags = " << msgFlags << endl;
+
+ //handle the special user types
+ Oscar::Message msg;
+ QString msgSender;
+ switch ( msgType )
+ {
+ case 0x0D:
+ msgSender = "ICQ Web Express";
+ msg.addProperty( Oscar::Message::WWP );
+ break;
+ case 0x0E:
+ msgSender = "ICQ Email Express";
+ msg.addProperty( Oscar::Message::EMail );
+ break;
+ default:
+ msgSender = m_fromUser;
+ break;
+ };
+
+ QCString msgText = tlv5buffer.getLNTS();
+ int msgLength = msgText.size();
+ if ( msgType == 0x0D || msgType == 0x0E )
+ {
+ for ( int i = 0; i < msgLength; i++ )
+ {
+ if ( msgText[i] == (char)0xFE )
+ msgText[i] = 0x20;
+ }
+ }
+
+ switch ( msgFlags )
+ {
+ case 0x03:
+ msg.addProperty( Oscar::Message::AutoResponse );
+ break;
+ case 0x01:
+ msg.addProperty( Oscar::Message::Normal );
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not handling message flag " << msgFlags << endl;
+ break;
+ }
+
+ msg.setType( 0x04 );
+ msg.setTimestamp( QDateTime::currentDateTime() );
+ msg.setSender( msgSender );
+ msg.setReceiver( client()->userId() );
+ msg.setEncoding( Oscar::Message::UserDefined );
+ msg.setTextArray( msgText );
+ emit receivedMessage( msg );
+}
+
+void MessageReceiverTask::handleAutoResponse()
+{
+ kdDebug(14151) << k_funcinfo << "Received auto response. Trying to handle it..." << endl;
+
+ Oscar::Message msg;
+ msg.addProperty( Oscar::Message::AutoResponse );
+ Buffer* b = transfer()->buffer();
+
+ // reason code
+ int reasonCode = b->getWord();
+ kdDebug(14151) << k_funcinfo << "Reason code (1 - channel not supported, 2 - busted payload, 3 - channel specific data): " << reasonCode << endl;
+
+ parseRendezvousData( b, &msg );
+ emit receivedMessage( msg );
+}
+
+void MessageReceiverTask::parseRendezvousData( Buffer* b, Oscar::Message* msg )
+{
+ int length1 = b->getLEWord();
+ if ( length1 != 0x001B )
+ { // all real messages (actually their header) seem to have length 0x1B
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Weired Message length. Bailing out!" << endl;
+ return;
+ }
+
+ int protocolVersion = b->getLEWord(); // the extended data protocol version, there are quite a few...
+
+ // plugin (for file transfer & stuff, all zeros for regular message
+ b->skipBytes( 16 );
+ // unknown
+ b->skipBytes( 2 );
+ // client capablities
+ b->skipBytes( 4 );
+ // unknown
+ b->skipBytes( 1 );
+
+ // (down)counter: basically just some number, ICQ counts down, miranda up, doesnt matter.
+ // BUT: when sending auto response on channel 2, like with the icbm cookie, we need to send the same value!
+ int channel2Counter = b->getLEWord();
+
+ // the next one is length (of a counter + following all-zero field), but also seems to indicate what type of message this is
+ int length2 = b->getLEWord();
+
+ // the only length usable ATM is 0x000E, which is a message
+ switch( length2 )
+ {
+ case 0x000E:
+ {
+ int cookie = b->getLEWord();
+ for ( int i = 0; i < 12; i++ )
+ { // 12 bytes all zeros
+ b->getByte();
+ }
+
+ // now starts the real message
+ // TODO if type is PLAIN, there is (might be?) an additional TLV with color and font information at the end...
+
+ uint messageType = b->getByte();
+ int flags = b->getByte();
+ int status = b->getLEWord(); // don't know what status this is or what to use it for
+ int priority = b->getLEWord(); // don't know what that's good for either
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Message type is: " << messageType << endl;
+
+ QCString msgText( b->getLELNTS() );
+ Oscar::Message::Encoding encoding = Oscar::Message::UserDefined;
+ int fgcolor = 0x00000000;
+ int bgcolor = 0x00ffffff;
+
+ // Don't parse plugin message
+ if ( b->length() >= 8 && messageType != 0x1A )
+ {
+ fgcolor = b->getLEDWord();
+ bgcolor = b->getLEDWord();
+
+ while ( b->length() >= 4 )
+ {
+ int capLength = b->getLEDWord();
+ if ( b->length() < capLength )
+ break;
+
+ QByteArray cap( b->getBlock( capLength ) );
+ if ( qstrncmp ( cap.data(), "{0946134E-4C7F-11D1-8222-444553540000}", capLength ) == 0 )
+ encoding = Oscar::Message::UTF8;
+ }
+ }
+
+ msg->setEncoding( encoding );
+ msg->setTextArray( msgText );
+
+ if ( ( messageType & 0xF0 ) == 0xE0 ) // check higher byte for value E -> status message request
+ msg->addProperty( Oscar::Message::StatusMessageRequest );
+ else
+ msg->addProperty( Oscar::Message::Request );
+
+ msg->setSender( m_fromUser );
+ msg->setReceiver( client()->userId() );
+ msg->setTimestamp( QDateTime::currentDateTime() );
+ msg->setType( 0x02 );
+ msg->setIcbmCookie( m_icbmCookie );
+ msg->setProtocolVersion( protocolVersion );
+ msg->setChannel2Counter( channel2Counter );
+ msg->setMessageType( messageType );
+
+ break;
+ }
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got unknown message with length2 " << length2 << endl;
+ }
+}
+
+QTextCodec* MessageReceiverTask::guessCodec( const QCString& string )
+{
+ Q_UNUSED( string );
+ return 0;
+}
+
+#include "messagereceivertask.moc"
+//kate: indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/messagereceivertask.h b/kopete/protocols/oscar/liboscar/messagereceivertask.h
new file mode 100644
index 00000000..b50a133f
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/messagereceivertask.h
@@ -0,0 +1,79 @@
+/*
+ messagereceivertask.h - Incoming OSCAR Messaging Handler
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef MESSAGERECEIVERTASK_H
+#define MESSAGERECEIVERTASK_H
+
+#include "task.h"
+#include <qstring.h>
+#include <qcstring.h>
+#include "oscarmessage.h"
+#include "oscartypeclasses.h"
+#include "oscarmessage.h"
+
+class QTextCodec;
+
+/**
+ * Handles receiving messages.
+ * @author Matt Rogers
+*/
+class MessageReceiverTask : public Task
+{
+Q_OBJECT
+public:
+
+ MessageReceiverTask( Task* parent );
+ ~MessageReceiverTask();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+
+signals:
+
+ void receivedMessage( const Oscar::Message& );
+
+private:
+
+ //!Handles messages from channel 1 (type 1 messages)
+ void handleType1Message();
+
+ //!Handles messages from channel 2 (type 2 messages)
+ void handleType2Message();
+
+ //!Handles messages from channel 4 (type 4 messages)
+ void handleType4Message();
+
+ //!Handles client auto responses (SNAC 0x04/0x0B)
+ void handleAutoResponse();
+
+ //!Parses Rendezvous data in Buffer and puts the information into Message
+ void parseRendezvousData( Buffer* b, Oscar::Message* msg );
+
+ QTextCodec* guessCodec( const QCString& string );
+
+private:
+
+ QByteArray m_icbmCookie;
+ int m_channel;
+ QString m_fromUser;
+ int m_currentSnacSubtype;
+ int m_charSet;
+ int m_subCharSet;
+
+};
+
+#endif
+
+//kate: indent-mode csands; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp
new file mode 100644
index 00000000..d97da7a3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.cpp
@@ -0,0 +1,166 @@
+/*
+ Kopete Oscar Protocol
+ offlinemessagestask.cpp - Offline messages handling
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "config.h"
+#include "offlinemessagestask.h"
+
+#include <time.h>
+
+#include "transfer.h"
+#include "buffer.h"
+#include "connection.h"
+
+#include <kdebug.h>
+
+OfflineMessagesTask::OfflineMessagesTask( Task* parent )
+ : ICQTask( parent )
+{
+ tzset();
+ m_sequence = 0;
+}
+
+OfflineMessagesTask::~OfflineMessagesTask()
+{
+}
+
+void OfflineMessagesTask::onGo()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Requesting offline messages" << endl;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() };
+
+ setRequestType( 0x003c ); //offline message request
+ setSequence( f.sequence );
+ Buffer* buf = addInitialData();
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+bool OfflineMessagesTask::forMe( const Transfer* t ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t );
+
+ if ( !st )
+ return false;
+
+ if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 )
+ return false;
+
+ Buffer buf( st->buffer()->buffer(), st->buffer()->length() );
+ const_cast<OfflineMessagesTask*>(this)->parseInitialData( buf );
+
+ if ( requestType() == 0x0041 || requestType() == 0x0042 )
+ return true;
+
+ return false;
+}
+
+bool OfflineMessagesTask::take( Transfer* t )
+{
+ if ( forMe( t ) )
+ {
+ setTransfer( t );
+
+ if ( requestType() == 0x0041 ) // Offline message
+ handleOfflineMessage();
+ else if ( requestType() == 0x0042 ) // end-of-offline messages
+ endOfMessages();
+
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void OfflineMessagesTask::handleOfflineMessage()
+{
+ TLV tlv1 = transfer()->buffer()->getTLV();
+ Buffer* buffer = new Buffer( tlv1.data, tlv1.length );
+
+ buffer->getLEWord(); // data chunk size
+ DWORD receiverUin = buffer->getLEDWord(); // target uin
+ buffer->getLEWord(); // request type
+ buffer->getLEWord(); // request sequence number: 0x0002
+
+ DWORD senderUin = buffer->getLEDWord();
+ WORD year = buffer->getLEWord();
+ BYTE month = buffer->getByte();
+ BYTE day = buffer->getByte();
+ BYTE hour = buffer->getByte();
+ BYTE minute = buffer->getByte();
+
+ BYTE type = buffer->getByte(); // msg type
+ BYTE flags = buffer->getByte(); // msg flags
+
+ WORD msgLength = buffer->getLEWord();
+ QByteArray msg = buffer->getBlock( msgLength );
+
+ QDate date(year, month, day);
+ QTime time(hour,minute);
+#ifndef HAVE_TM_GMTOFF
+ int tz = -( ::timezone );
+#else
+ int tz;
+ time_t now;
+ struct tm *tm;
+ now = ::time(NULL);
+ tm = ::localtime(&now);
+ /* daylight = tm->tm_isdst; // another linuxism */
+ tz = (tm->tm_gmtoff) / (60 * 60);
+#endif
+ time = time.addSecs( tz );
+
+ QDateTime hackyTime( date, time );
+ Oscar::Message message( Oscar::Message::UserDefined, msg, type, flags, hackyTime );
+ message.setSender( QString::number( senderUin ) );
+ message.setReceiver( QString::number( receiverUin ) );
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Received offline message '" << msg.data() << "' from " << senderUin << endl;
+
+ emit receivedOfflineMessage( message );
+}
+
+void OfflineMessagesTask::endOfMessages()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "End of Offline Messages" << endl;
+
+ TLV tlv1 = transfer()->buffer()->getTLV();
+ Buffer* buffer = new Buffer( tlv1.data, tlv1.length );
+
+ buffer->skipBytes( 8 );
+ m_sequence = buffer->getLEWord();
+
+ deleteOfflineMessages();
+
+ setSuccess( true );
+}
+
+void OfflineMessagesTask::deleteOfflineMessages()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() };
+
+
+ setRequestType( 0x003E ); //delete offline messages
+ setSequence( m_sequence );
+ Buffer* buf = addInitialData();
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+#include "offlinemessagestask.moc"
diff --git a/kopete/protocols/oscar/liboscar/offlinemessagestask.h b/kopete/protocols/oscar/liboscar/offlinemessagestask.h
new file mode 100644
index 00000000..da2454d3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/offlinemessagestask.h
@@ -0,0 +1,54 @@
+/*
+ Kopete Oscar Protocol
+ offlinemessagestask.h - Offline messages handling
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OFFLINEMESSAGESTASK_H
+#define OFFLINEMESSAGESTASK_H
+
+#include "icqtask.h"
+#include "oscarmessage.h"
+
+/**
+ICQ Offline messages handling
+
+@author Gustavo Pichorim Boiko
+*/
+class OfflineMessagesTask : public ICQTask
+{
+Q_OBJECT
+public:
+ OfflineMessagesTask( Task* parent );
+
+ ~OfflineMessagesTask();
+
+ virtual void onGo();
+ virtual bool forMe( const Transfer* t ) const;
+ virtual bool take( Transfer* t );
+
+signals:
+ void receivedOfflineMessage( const Oscar::Message& msg );
+
+private:
+ void handleOfflineMessage();
+ void endOfMessages();
+ void deleteOfflineMessages();
+
+private:
+ int m_sequence;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp
new file mode 100644
index 00000000..785e23f7
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.cpp
@@ -0,0 +1,99 @@
+/*
+ Kopete Oscar Protocol
+ onlinenotifiertask.cpp - handles all the status notifications
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "onlinenotifiertask.h"
+#include "buffer.h"
+#include "connection.h"
+#include "oscartypes.h"
+#include "transfer.h"
+
+#include <kdebug.h>
+
+OnlineNotifierTask::OnlineNotifierTask( Task* parent ) : Task( parent )
+{
+}
+
+
+OnlineNotifierTask::~OnlineNotifierTask()
+{
+}
+
+
+bool OnlineNotifierTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0003 )
+ {
+ switch ( st->snacSubtype() )
+ {
+ case 0x000B:
+ case 0x000C:
+ return true;
+ };
+ }
+ return false;
+}
+
+bool OnlineNotifierTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( st )
+ {
+ setTransfer( transfer );
+
+ if ( st->snacSubtype() == 0x000B )
+ userOnline();
+ else
+ userOffline();
+
+ setTransfer( 0 );
+ }
+ return true;
+ }
+ return false;
+}
+
+void OnlineNotifierTask::userOnline()
+{
+ Buffer* buffer = transfer()->buffer();
+ UserDetails ud;
+ ud.fill( buffer );
+ QString user = ud.userId();
+ //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now online" << endl;
+ emit userIsOnline( user, ud );
+}
+
+void OnlineNotifierTask::userOffline()
+{
+ Buffer* buffer = transfer()->buffer();
+ UserDetails ud;
+ ud.fill( buffer );
+ QString user = ud.userId();
+ //kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << user << " is now offline" << endl;
+ emit userIsOffline( user, ud );
+}
+
+#include "onlinenotifiertask.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/onlinenotifiertask.h b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h
new file mode 100644
index 00000000..2ec58e5a
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/onlinenotifiertask.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Oscar Protocol
+ onlinenotifiertask.h - handles all the status notifications
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef ONLINENOTIFIERTASK_H
+#define ONLINENOTIFIERTASK_H
+
+#include <task.h>
+#include "userdetails.h"
+
+class Transfer;
+class QString;
+/**
+Tracks status notifications (online, offline, etc.) for contacts
+Implements SNACS (0x03, 0x11) and (0x03, 0x12)
+
+@author Matt Rogers
+*/
+class OnlineNotifierTask : public Task
+{
+Q_OBJECT
+public:
+ OnlineNotifierTask( Task* parent );
+
+ ~OnlineNotifierTask();
+
+ virtual bool take( Transfer* transfer );
+
+protected:
+ virtual bool forMe( const Transfer* transfer ) const;
+
+signals:
+ void userIsOnline( const QString& user, const UserDetails& ud );
+ void userIsOffline( const QString& user, const UserDetails& ud );
+
+private:
+ void userOnline();
+ void userOffline();
+
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.cpp b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp
new file mode 100644
index 00000000..ea090442
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarbytestream.cpp
@@ -0,0 +1,141 @@
+
+/***************************************************************************
+ gwbytestream.cpp - Byte Stream using KNetwork sockets
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qobject.h>
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <kresolver.h>
+
+#include "oscarbytestream.h"
+
+KNetworkByteStream::KNetworkByteStream( QObject *parent, const char */*name*/ )
+ : ByteStream ( parent )
+{
+ kdDebug( 14151 ) << 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( 14151 ) << 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 ()
+{
+#ifdef OSCAR_EXCESSIVE_DEBUG
+ kdDebug ( 14151 ) << k_funcinfo << "Closing stream." << endl;
+#endif
+ // 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();
+#ifdef OSCAR_EXCESSIVE_DEBUG
+ kdDebug(14151) << k_funcinfo << "writing " << writeData.size() << " bytes." << endl;
+#endif
+ 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( 14151 ) << k_funcinfo << "Socket has been closed." << endl;
+
+ // depending on who closed the socket, emit different signals
+ if ( mClosing )
+ {
+ kdDebug( 14151 ) << "..by ourselves!" << endl;
+ kdDebug( 14151 ) << "socket error is " << socket()->errorString( socket()->error() ) << endl;
+ emit connectionClosed ();
+ }
+ else
+ {
+ kdDebug( 14151 ) << "..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( 14151 ) << k_funcinfo << "Socket error " << code << endl;
+
+ emit error( code );
+}
+
+#include "oscarbytestream.moc"
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/oscar/liboscar/oscarbytestream.h b/kopete/protocols/oscar/liboscar/oscarbytestream.h
new file mode 100644
index 00000000..bd618666
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarbytestream.h
@@ -0,0 +1,72 @@
+
+/***************************************************************************
+ gwbytestream.h - Byte Stream using KNetwork sockets
+ adapted from jabberbytestream.h
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef 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
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
+
diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.cpp b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp
new file mode 100644
index 00000000..e607a24b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarclientstream.cpp
@@ -0,0 +1,437 @@
+/*
+ oscarclientstream.cpp - Kopete Oscar Protocol
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarclientstream.h"
+
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qptrqueue.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+
+#include "bytestream.h"
+#include "connection.h"
+#include "connector.h"
+#include "coreprotocol.h"
+#include "rateclassmanager.h"
+#include "transfer.h"
+
+#define LIBOSCAR_DEBUG 0
+
+void cs_dump( const QByteArray &bytes );
+
+enum {
+ Idle,
+ Connecting,
+ Active,
+ Closing
+};
+
+enum {
+ ClientMode,
+ ServerMode
+};
+
+class ClientStream::Private
+{
+public:
+ Private()
+ {
+ conn = 0;
+ bs = 0;
+ connection = 0;
+
+ username = QString::null;
+ password = QString::null;
+ server = QString::null;
+ haveLocalAddr = false;
+ doBinding = true;
+
+ reset();
+ }
+ void reset()
+ {
+ state = Idle;
+ notify = 0;
+ newTransfers = false;
+ }
+
+ QString username;
+ QString password;
+ QString server;
+ bool doAuth; //send the initial login sequences to get the cookie
+ bool haveLocalAddr;
+ QHostAddress localAddr;
+ Q_UINT16 localPort;
+ bool doBinding;
+
+ Connector *conn;
+ ByteStream *bs;
+ CoreProtocol client;
+ Connection* connection;
+
+ QString defRealm;
+
+ int mode;
+ int state;
+ int notify;
+ bool newTransfers;
+
+ int errCond;
+ QString errText;
+
+ QPtrQueue<Transfer> in;
+
+ QTimer noopTimer; // used to send icq keepalive
+ int noop_time;
+};
+
+ClientStream::ClientStream(Connector *conn, QObject *parent)
+:Stream(parent)
+{
+ //qDebug("CLIENTSTREAM::ClientStream");
+
+ d = new Private;
+ d->mode = ClientMode;
+ 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()));
+}
+
+ClientStream::~ClientStream()
+{
+ reset();
+ delete d;
+}
+
+void ClientStream::reset(bool all)
+{
+ d->reset();
+ d->noopTimer.stop();
+
+ // client
+ if(d->mode == ClientMode)
+ {
+ // reset connector
+ if ( d->bs )
+ {
+ d->bs->close();
+ d->bs = 0;
+ }
+ if ( d->conn )
+ d->conn->done();
+
+ // reset state machine
+ d->client.reset();
+ }
+ if(all)
+ d->in.clear();
+}
+
+void ClientStream::connectToServer(const QString& server, bool auth)
+{
+ reset(true);
+ d->state = Connecting;
+ d->doAuth = auth;
+ d->server = server;
+
+ d->conn->connectToServer( d->server );
+}
+
+void ClientStream::continueAfterWarning()
+{
+/* unneeded?
+ if(d->state == WaitVersion) {
+ d->state = Connecting;
+ processNext();
+ }
+ else if(d->state == WaitTLS) {
+ d->state = Connecting;
+ processNext();
+ }
+*/
+}
+
+void ClientStream::accept()
+{
+
+}
+
+bool ClientStream::isActive() const
+{
+ return (d->state != Idle);
+}
+
+bool ClientStream::isAuthenticated() const
+{
+ return (d->state == Active);
+}
+
+void ClientStream::setNoopTime(int mills)
+{
+ d->noop_time = mills;
+
+ if(d->noop_time == 0) {
+ d->noopTimer.stop();
+ return;
+ }
+
+ if( d->state != Active )
+ 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;
+}
+
+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::setConnection( Connection *c )
+{
+ d->connection = c;
+}
+
+Connection* ClientStream::connection() const
+{
+ return d->connection;
+}
+
+
+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( Transfer *request )
+{
+ d->client.outgoingTransfer( request );
+}
+
+void cs_dump( const QByteArray &bytes )
+{
+#if 0
+ qDebug( "contains: %i bytes ", 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" );
+#endif
+ Q_UNUSED( bytes );
+}
+
+void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes )
+{
+ // take formatted bytes from CoreProtocol and put them on the wire
+ d->bs->write( outgoingBytes );
+}
+
+void ClientStream::cp_incomingData()
+{
+ Transfer * incoming = d->client.incomingTransfer();
+ if ( incoming )
+ {
+ d->in.enqueue( incoming );
+ d->newTransfers = true;
+ doReadyRead();
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo <<
+ "client signalled incomingData but none was available, state is: " <<
+ d->client.state() << endl;
+}
+
+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()));
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+
+ d->state = Active;
+ if ( d->noop_time )
+ d->noopTimer.start( d->noop_time );
+
+ QByteArray spare = d->bs->read();
+
+ QGuardedPtr<QObject> self = this;
+ emit connected();
+ if(!self)
+ return;
+}
+
+void ClientStream::cr_error()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ 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::bs_readyRead()
+{
+ QByteArray a;
+ //qDebug( "size of storage for incoming data is %i bytes.", a.size() );
+ a = d->bs->read();
+
+#if LIBOSCAR_DEBUG
+ QCString cs(a.data(), a.size()+1);
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "recv: " << a.size() << "bytes" << endl;
+ cs_dump( a );
+#endif
+
+ d->client.addIncomingData(a);
+}
+
+void ClientStream::bs_bytesWritten(int bytes)
+{
+#if LIBOSCAR_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << bytes << " bytes written" << endl;
+ Q_UNUSED( bytes );
+#else
+ Q_UNUSED( bytes );
+#endif
+}
+
+void ClientStream::srvProcessNext()
+{
+}
+
+void ClientStream::doReadyRead()
+{
+ emit readyRead();
+}
+
+void ClientStream::processNext()
+{
+ if( !d->in.isEmpty() )
+ {
+ QTimer::singleShot(0, this, SLOT(doReadyRead()));
+ }
+}
+
+bool ClientStream::handleNeed()
+{
+ return false;
+}
+
+void ClientStream::doNoop()
+{
+ if ( d->state != Active )
+ return;
+
+ FLAP f = { 0x05, d->connection->flapSequence(), 0 };
+ Buffer* b = new Buffer(); //deleted in Transfer destructor
+ Transfer* t = new FlapTransfer( f, b ); //deleted after being sent
+ write( t );
+}
+
+void ClientStream::handleError()
+{
+}
+
+#include "oscarclientstream.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/oscarclientstream.h b/kopete/protocols/oscar/liboscar/oscarclientstream.h
new file mode 100644
index 00000000..f8b4d2b6
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarclientstream.h
@@ -0,0 +1,164 @@
+/*
+ oscarclientstream.h - Kopete Oscar Protocol
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCAR_CLIENTSTREAM_H
+#define OSCAR_CLIENTSTREAM_H
+
+#include "stream.h"
+
+// forward defines
+class ByteStream;
+class Client;
+class Connector;
+class Connection;
+class Transfer;
+class QHostAddress;
+
+class ClientStream : public Stream
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up
+ ErrNeg, // Negotiation error, see condition
+ ErrAuth, // Auth error, see condition
+ 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 AuthCond {
+ GenericAuthError, // all-purpose "can't login" error
+ NoMech, // No appropriate auth mech available
+ BadProto, // Bad SASL auth protocol
+ BadServ, // Server failed mutual auth
+ InvalidUserId, // bad user id
+ 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, QObject *parent=0);
+ ~ClientStream();
+
+ void connectToServer(const QString& server, 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 setLocalAddr(const QHostAddress &addr, Q_UINT16 port);
+
+ void close();
+
+ /** Connection related stuff */
+ void setConnection( Connection* c );
+ Connection* connection() const;
+
+
+ /**
+ * 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( Transfer* request );
+
+ int errorCondition() const;
+ QString errorText() const;
+
+ // 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 authenticated(); // this signal is ordinarily emitted in processNext
+ void warning(int);
+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 bs_readyRead();
+ void bs_bytesWritten(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/oscar/liboscar/oscarconnector.cpp b/kopete/protocols/oscar/liboscar/oscarconnector.cpp
new file mode 100644
index 00000000..6fcef197
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarconnector.cpp
@@ -0,0 +1,108 @@
+
+/***************************************************************************
+ gwconnector.cpp - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 "oscarconnector.h"
+#include "oscarbytestream.h"
+
+KNetworkConnector::KNetworkConnector( QObject *parent, const char */*name*/ )
+ : Connector( parent )
+{
+ kdDebug( 14151 ) << 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( 14151 ) << k_funcinfo << "Initiating connection to " << mHost << endl;
+ Q_ASSERT( !mHost.isNull() );
+ Q_ASSERT( mPort );
+
+ 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( 14151 ) << 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( 14151 ) << 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 ( 14151 ) << k_funcinfo << endl;
+ mByteStream->close ();
+}
+
+void KNetworkConnector::setOptHostPort( const QString &host, Q_UINT16 port )
+{
+ kdDebug ( 14151 ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl;
+
+ mHost = host;
+ mPort = port;
+
+}
+
+#include "oscarconnector.moc"
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/oscar/liboscar/oscarconnector.h b/kopete/protocols/oscar/liboscar/oscarconnector.h
new file mode 100644
index 00000000..d130f7da
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarconnector.h
@@ -0,0 +1,69 @@
+
+/***************************************************************************
+ oscarconnector.h - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+ (C) 2004 by Matt Rogers <mattr@kde.org>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 OSCARCONNECTOR_H
+#define OSCARCONNECTOR_H
+
+#include "oscarbytestream.h"
+
+#include "connector.h"
+
+class ByteStream;
+class KNetworkByteStream;
+class KResolverEntry;
+
+/**
+@author Till Gerken
+@author Matt Rogers
+*/
+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 );
+
+ int errorCode();
+
+private slots:
+ void slotConnected();
+ void slotError( int );
+
+private:
+ QString mHost;
+ Q_UINT16 mPort;
+ int mErrorCode;
+
+ KNetworkByteStream *mByteStream;
+
+};
+
+#endif
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/oscar/liboscar/oscardebug.h b/kopete/protocols/oscar/liboscar/oscardebug.h
new file mode 100644
index 00000000..9c2d7e16
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscardebug.h
@@ -0,0 +1,35 @@
+// oscardebug.h
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301 USA
+
+#ifndef OSCARDEBUG_H
+#define OSCARDEBUG_H
+
+//OSCAR debugging definitions
+
+//uncomment to get debug output for user info parsing
+//#define OSCAR_USERINFO_DEBUG
+
+//uncomment to get excessive amounts of debug info from
+//various places in the code
+//#define OSCAR_EXCESSIVE_DEBUG
+
+//uncomment to get packet dumps in the debug output
+//#define OSCAR_PACKET_PARSING_DEBUG
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.cpp b/kopete/protocols/oscar/liboscar/oscarmessage.cpp
new file mode 100644
index 00000000..f4f512d2
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarmessage.cpp
@@ -0,0 +1,301 @@
+/*
+ Kopete Oscar Protocol
+ oscarmessage.cpp - Oscar Message Object
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+ Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarmessage.h"
+
+#include <qdeepcopy.h>
+#include <qtextcodec.h>
+
+
+Oscar::Message::Message()
+: m_channel( -1 ),
+ m_properties( -1 ),
+ m_messageType( 0 ),
+ m_protocolVersion( 0 ),
+ m_channel2Counter( 0 ),
+ m_encoding( UserDefined )
+{
+}
+
+Oscar::Message::Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp )
+: m_channel( channel ),
+ m_properties( properties ),
+ m_messageType( 0 ),
+ m_protocolVersion( 0 ),
+ m_channel2Counter( 0 ),
+ m_textArray( messageText ),
+ m_timestamp( timestamp ),
+ m_encoding( messageEncoding )
+{
+}
+
+Oscar::Message::Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp )
+: m_channel( channel ),
+ m_properties( properties ),
+ m_messageType( 0 ),
+ m_protocolVersion( 0 ),
+ m_channel2Counter( 0 ),
+ m_timestamp( timestamp ),
+ m_encoding( messageEncoding )
+{
+ setTextArray( messageText );
+}
+
+Oscar::Message::Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec )
+: m_channel( channel ),
+ m_properties( properties ),
+ m_messageType( 0 ),
+ m_protocolVersion( 0 ),
+ m_channel2Counter( 0 ),
+ m_timestamp( timestamp )
+{
+ setText( messageEncoding, messageText, codec );
+}
+
+QString Oscar::Message::sender() const
+{
+ return m_sender;
+}
+
+void Oscar::Message::setSender( const QString& sender )
+{
+ m_sender = sender;
+}
+
+QString Oscar::Message::receiver() const
+{
+ return m_receiver;
+}
+
+void Oscar::Message::setReceiver( const QString& receiver )
+{
+ m_receiver = receiver;
+}
+
+QByteArray Oscar::Message::textArray() const
+{
+ return m_textArray;
+}
+
+QString Oscar::Message::text( QTextCodec *codec ) const
+{
+ switch ( m_encoding )
+ {
+ case Oscar::Message::UserDefined:
+ return codec->toUnicode( m_textArray );
+ case Oscar::Message::UTF8:
+ return QString::fromUtf8( m_textArray.data(), m_textArray.size() );
+ case Oscar::Message::UCS2:
+ {
+ uint len = m_textArray.size() / 2;
+ QString result;
+ result.setLength( len );
+ QByteArray::ConstIterator p = m_textArray.begin();
+ for ( uint i = 0; i < len; i++)
+ {
+ char row = *p++;
+ char cell = *p++;
+ result[i] = QChar( cell, row );
+ }
+ //check if last character isn't null
+ if ( result[len-1].isNull() )
+ result.setLength( len - 1 );
+
+ return result;
+ }
+ default:
+ break; // Should never happen.
+ }
+ return QString::null;
+ //FIXME: warn at least with kdWarning if an unrecognised encoding style was seen.
+}
+
+void Oscar::Message::setText( Oscar::Message::Encoding newEncoding, const QString& newText, QTextCodec* codec )
+{
+ uint len;
+ switch ( newEncoding )
+ {
+ case Oscar::Message::UserDefined:
+ // Oscar::Message::setTextArray( const QCString& )
+ // strips trailing null byte automatically.
+ setTextArray( codec->fromUnicode( newText ) );
+ break;
+ case Oscar::Message::UTF8:
+ // Oscar::Message::setTextArray( const QCString& )
+ // strips trailing null byte automatically.
+ setTextArray( newText.utf8() );
+ break;
+ case Oscar::Message::UCS2:
+ {
+ len = newText.length();
+ m_textArray.resize( len * 2 );
+ QByteArray::Iterator p = m_textArray.begin();
+ for ( uint i = 0; i < len; i++)
+ {
+ *p++ = newText[i].row();
+ *p++ = newText[i].cell();
+ }
+ break;
+ }
+ default:
+ break; // Should never happen.
+ }
+ m_encoding = newEncoding;
+}
+
+void Oscar::Message::setTextArray( const QByteArray& newTextArray )
+{
+ m_textArray.duplicate( newTextArray );
+}
+
+void Oscar::Message::setTextArray( const QCString& newTextArray )
+{
+ m_textArray.duplicate( newTextArray );
+ uint len = m_textArray.size();
+ if ( len > 0 )
+ {
+ --len;
+ if ( m_textArray[len] == '\0' )
+ {
+ // Strip trailing null byte.
+ m_textArray.resize( len );
+ }
+ }
+}
+
+int Oscar::Message::properties() const
+{
+ return m_properties;
+}
+
+void Oscar::Message::addProperty( int prop )
+{
+ if ( m_properties == -1 )
+ m_properties = 0;
+
+ m_properties = m_properties | prop;
+}
+
+bool Oscar::Message::hasProperty( int prop ) const
+{
+ if ( m_properties == -1 )
+ return false;
+ if ( ( m_properties & prop ) == 0 )
+ return false;
+ else
+ return true;
+}
+
+int Oscar::Message::type() const
+{
+ return m_channel;
+}
+
+void Oscar::Message::setType( int newType )
+{
+ m_channel = newType;
+}
+
+QDateTime Oscar::Message::timestamp() const
+{
+ return m_timestamp;
+}
+
+void Oscar::Message::setTimestamp( QDateTime ts )
+{
+ m_timestamp = ts;
+}
+
+QByteArray Oscar::Message::icbmCookie() const
+{
+ return m_icbmCookie;
+}
+
+void Oscar::Message::setIcbmCookie( const QByteArray& cookie )
+{
+ m_icbmCookie.duplicate( cookie );
+}
+
+int Oscar::Message::protocolVersion() const
+{
+ return m_protocolVersion;
+}
+
+void Oscar::Message::setProtocolVersion( int version )
+{
+ m_protocolVersion = version;
+}
+
+int Oscar::Message::channel2Counter() const
+{
+ return m_channel2Counter;
+}
+
+void Oscar::Message::setChannel2Counter( int value )
+{
+ m_channel2Counter = value;
+}
+
+int Oscar::Message::messageType() const
+{
+ return m_messageType;
+}
+
+void Oscar::Message::setMessageType( int type )
+{
+ m_messageType = type;
+}
+
+Oscar::WORD Oscar::Message::exchange() const
+{
+ return m_exchange;
+}
+
+void Oscar::Message::setExchange( Oscar::WORD exchange )
+{
+ m_exchange = exchange;
+}
+
+QString Oscar::Message::chatRoom() const
+{
+ return m_chatRoom;
+}
+
+void Oscar::Message::setChatRoom( const QString& room )
+{
+ m_chatRoom = room;
+}
+
+Oscar::Message::Encoding Oscar::Message::encoding() const
+{
+ return m_encoding;
+}
+
+void Oscar::Message::setEncoding( Oscar::Message::Encoding newEncoding )
+{
+ m_encoding = newEncoding;
+}
+
+Oscar::Message::operator bool() const
+{
+ return m_channel != -1 && m_properties != -1;
+}
+
+//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4;
+
diff --git a/kopete/protocols/oscar/liboscar/oscarmessage.h b/kopete/protocols/oscar/liboscar/oscarmessage.h
new file mode 100644
index 00000000..7f081054
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarmessage.h
@@ -0,0 +1,182 @@
+/*
+ Kopete Oscar Protocol
+ oscarmessage.h - Oscar Message Object
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+ Copyright (c) 2005 Conrad Hoffmann <conrausch@gmx.de>
+ Copyright (c) 2005 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCARMESSAGE_H_
+#define _OSCARMESSAGE_H_
+
+#include <qglobal.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include <qdatetime.h>
+#include "kopete_export.h"
+#include "oscartypes.h"
+
+class QTextCodec;
+
+namespace Oscar
+{
+
+/**
+ * This class is responsible for holding all the details
+ * of a message and includes the following:
+ * \li channel ( type )
+ * \li encoding
+ */
+
+class KOPETE_EXPORT Message
+{
+public:
+
+ enum {
+ Normal = 0x0000,
+ AutoResponse = 0x0001,
+ WWP = 0x0002,
+ EMail = 0x0004,
+ ChatRoom = 0x0008,
+ Request = 0x0100,
+ StatusMessageRequest = 0x0200
+ };
+
+ enum Encoding {
+ UserDefined,
+ UTF8,
+ UCS2
+ };
+
+ Message();
+
+ Message( Encoding messageEncoding, const QByteArray& messageText, int channel, int properties, QDateTime timestamp );
+ Message( Encoding messageEncoding, const QCString& messageText, int channel, int properties, QDateTime timestamp );
+ Message( Encoding messageEncoding, const QString& messageText, int channel, int properties, QDateTime timestamp, QTextCodec* codec = 0 );
+
+ /** Get the sender of the message */
+ QString sender() const;
+
+ /** Set the sender of the message */
+ void setSender( const QString& sender );
+
+ /** Get the receiver of the message */
+ QString receiver() const;
+
+ /** Set the receiver of the message */
+ void setReceiver( const QString& receiver);
+
+ /** get the message text */
+ QString text( QTextCodec* codec ) const;
+
+ /** set the message text */
+ void setText( Encoding newEncoding, const QString& newText, QTextCodec* codec = 0);
+
+ /** get the message text as a bytearray for decoding */
+ QByteArray textArray() const;
+
+ /** set the message text as a bytearray for decoding */
+ void setTextArray( const QByteArray& newTextArray );
+
+ /** set the mesasge text as a bytearray for decoding */
+ void setTextArray( const QCString& newTextArray );
+
+ /** get the message properties */
+ int properties() const;
+
+ /** ask about a specific property */
+ bool hasProperty( int prop ) const;
+
+ /** add a property to the message */
+ void addProperty( int prop );
+
+ /** get the channel ( type ) of the message */
+ int type() const;
+
+ /** set the channel ( type ) of the message */
+ void setType( int newType );
+
+ /** get the timestamp of the message */
+ QDateTime timestamp() const;
+
+ /** set the timestamp of the message */
+ void setTimestamp( QDateTime ts );
+
+ /** get the ICBM cookie of the message */
+ QByteArray icbmCookie() const;
+
+ /** set the ICBM cookie of the message */
+ void setIcbmCookie( const QByteArray& cookie );
+
+ /** get the protocol version (channel 2 messages only) */
+ int protocolVersion() const;
+
+ /** prepare for handling of different protocol versions */
+ void setProtocolVersion( int version );
+
+ /** get the channel 2 counter value of the message */
+ int channel2Counter() const; // channel 2 message have an additional counter whose value needs be kept in a request response
+
+ /** set the channel 2 counter value */
+ void setChannel2Counter( int value );
+
+ /** get the message (content) type */
+ int messageType() const;
+
+ /** set the message (content) type */
+ void setMessageType( int type );
+
+ /** get the exchange for the chat room this message is for */
+ Oscar::WORD exchange() const;
+
+ /** set the exchange for the chat room this message is for */
+ void setExchange( Oscar::WORD );
+
+ /** get the chat room that this message is for */
+ QString chatRoom() const;
+
+ /** set the chat room that this message is for */
+ void setChatRoom( const QString& );
+
+ /** get the message encoding */
+ Encoding encoding() const;
+
+ /** set the message encoding */
+ void setEncoding( Encoding newEncoding );
+
+ operator bool() const;
+
+private:
+ //TODO d-pointer
+ QString m_sender;
+ QString m_receiver;
+ int m_channel;
+ int m_properties;
+ int m_messageType;
+ int m_protocolVersion;
+ int m_channel2Counter;
+ QByteArray m_icbmCookie;
+ QByteArray m_textArray;
+ QDateTime m_timestamp;
+ Oscar::WORD m_exchange;
+ QString m_chatRoom;
+ Encoding m_encoding;
+};
+
+}
+
+//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4;
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.cpp b/kopete/protocols/oscar/liboscar/oscarsettings.cpp
new file mode 100644
index 00000000..36b0bb12
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarsettings.cpp
@@ -0,0 +1,69 @@
+/*
+ Kopete Oscar Protocol
+ Oscar Backend Setting Storage
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "oscarsettings.h"
+
+namespace Oscar
+{
+
+Settings::Settings()
+{
+}
+
+
+Settings::~Settings()
+{
+}
+
+void Settings::setWebAware( bool aware )
+{
+ m_webAware = aware;
+}
+
+bool Settings::webAware() const
+{
+ return m_webAware;
+}
+
+void Settings::setRequireAuth( bool require )
+{
+ m_requireAuth = require;
+}
+
+bool Settings::requireAuth() const
+{
+ return m_requireAuth;
+}
+
+void Settings::setHideIP( bool hide )
+{
+ m_hideIP = hide;
+}
+
+bool Settings::hideIP() const
+{
+ return m_hideIP;
+}
+
+
+
+
+
+}
+
+//kate: indent-mode csands; tab-width 4;
+
diff --git a/kopete/protocols/oscar/liboscar/oscarsettings.h b/kopete/protocols/oscar/liboscar/oscarsettings.h
new file mode 100644
index 00000000..12ece2e6
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarsettings.h
@@ -0,0 +1,62 @@
+/*
+ Kopete Oscar Protocol
+ Oscar Backend Setting Storage
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef OSCARSETTINGS_H
+#define OSCARSETTINGS_H
+
+#include "kopete_export.h"
+
+namespace Oscar
+{
+
+/**
+* This class is used to keep track of various persistant settings that the backend will always
+* need to get from the frontend. This is the interface and storage class that will handle the
+* settings.
+* @author Matt Rogers
+*/
+class KOPETE_EXPORT Settings
+{
+public:
+ Settings();
+ ~Settings();
+
+ /* Web awareness settings */
+ void setWebAware( bool webAware );
+ bool webAware() const;
+
+ /* Authorization settings */
+ void setRequireAuth( bool require );
+ bool requireAuth() const;
+
+ /* Hide IP Settings */
+ void setHideIP( bool hide );
+ bool hideIP() const;
+
+private:
+
+ bool m_webAware;
+ bool m_requireAuth;
+ bool m_hideIP;
+};
+
+}
+
+#endif
+
+//kate: indent-mode csands; tab-width 4;
+
diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp
new file mode 100644
index 00000000..cd9e9619
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.cpp
@@ -0,0 +1,284 @@
+/*
+ Kopete Oscar Protocol
+ oscartypeclasses.cpp - Oscar Type Definitions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscartypeclasses.h"
+#include <qdeepcopy.h>
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include "oscarutils.h"
+#include "buffer.h"
+
+
+// using namespace Oscar;
+
+Oscar::TLV::TLV()
+{
+ type = 0;
+ length = 0;
+}
+
+Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, char* newData )
+{
+ type = newType;
+ length = newLength;
+ data.truncate(0);
+ data.duplicate( newData, length );
+}
+
+Oscar::TLV::TLV( Q_UINT16 newType, Q_UINT16 newLength, const QByteArray& newData )
+{
+ type = newType;
+ length = newLength;
+ data.duplicate( newData );
+}
+
+Oscar::TLV::TLV( const TLV& t )
+{
+ type = t.type;
+ length = t.length;
+ data.truncate(0);
+ data.duplicate( t.data );
+}
+
+Oscar::TLV::operator bool() const
+{
+ return type != 0;
+}
+
+
+Oscar::SSI::SSI()
+{
+ m_gid = 0;
+ m_bid = 0;
+ m_type = 0xFFFF;
+ m_tlvLength = 0;
+ m_waitingAuth = false;
+}
+
+Oscar::SSI::SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV> &tlvlist, int tlvLength )
+{
+ m_name = name;
+ m_gid = gid;
+ m_bid = bid;
+ m_type = type;
+ m_tlvLength = tlvLength;
+
+ //deepcopy the tlvs
+ m_tlvList = QDeepCopy< QValueList<TLV> >( tlvlist );
+
+ if ( m_tlvLength == 0 && !m_tlvList.isEmpty() )
+ refreshTLVLength();
+
+ checkTLVs();
+}
+
+Oscar::SSI::SSI( const Oscar::SSI& other )
+{
+ m_name = other.m_name;
+ m_gid = other.m_gid;
+ m_bid = other.m_bid;
+ m_type = other.m_type;
+ m_tlvLength = other.m_tlvLength;
+ m_alias = other.m_alias;
+ m_waitingAuth = other.m_waitingAuth;
+
+ //deepcopy the tlvs
+ m_tlvList = QDeepCopy< QValueList<TLV> >( other.m_tlvList );
+
+ if ( m_tlvLength == 0 && !m_tlvList.isEmpty() )
+ refreshTLVLength();
+}
+
+bool Oscar::SSI::isValid() const
+{
+ return m_type != 0xFFFF;
+}
+
+QString Oscar::SSI::name() const
+{
+ return m_name;
+}
+
+Q_UINT16 Oscar::SSI::gid() const
+{
+ return m_gid;
+}
+
+Q_UINT16 Oscar::SSI::bid() const
+{
+ return m_bid;
+}
+
+Q_UINT16 Oscar::SSI::type() const
+{
+ return m_type;
+}
+
+const QValueList<TLV>& Oscar::SSI::tlvList() const
+{
+ return m_tlvList;
+}
+
+void Oscar::SSI::setTLVListLength( Q_UINT16 newLength )
+{
+ m_tlvLength = newLength;
+}
+
+Q_UINT16 Oscar::SSI::tlvListLength() const
+{
+ return m_tlvLength;
+}
+
+void Oscar::SSI::setTLVList( QValueList<TLV> list )
+{
+ //deepcopy the tlvs
+ m_tlvList = QDeepCopy< QValueList<TLV> >( list );
+ refreshTLVLength();
+ checkTLVs();
+}
+
+void Oscar::SSI::refreshTLVLength()
+{
+ m_tlvLength = 0;
+ QValueList<TLV>::iterator it = m_tlvList.begin();
+ for( ; it != m_tlvList.end(); ++it )
+ {
+ m_tlvLength += 4;
+ m_tlvLength += (*it).length;
+ }
+}
+
+void Oscar::SSI::checkTLVs()
+{
+ //check for the auth TLV
+ TLV authTLV = findTLV( m_tlvList, 0x0066 );
+ if ( authTLV )
+ {
+ kdDebug(14151) << k_funcinfo << "Need auth for contact " << m_name << endl;
+ m_waitingAuth = true;
+ }
+ else
+ m_waitingAuth = false;
+
+ //check for the alias TLV
+ TLV aliasTLV = findTLV( m_tlvList, 0x0131 );
+ if ( aliasTLV )
+ {
+ m_alias = QString::fromUtf8( aliasTLV.data, aliasTLV.length );
+ kdDebug( 14151 ) << k_funcinfo << "Got an alias '" << m_alias << "' for contact '" << m_name << "'" << endl;
+ }
+
+ TLV privacyTLV = findTLV( m_tlvList, 0x00CA );
+ if ( privacyTLV )
+ kdDebug(14151) << k_funcinfo << "Found privacy settings " << privacyTLV.data << endl;
+
+ TLV infoTLV = findTLV( m_tlvList, 0x00CC );
+ if ( infoTLV )
+ kdDebug(14151) << k_funcinfo << "Found 'allow others to see...' options " << infoTLV.data << endl;
+}
+
+QString Oscar::SSI::alias() const
+{
+ return m_alias;
+}
+
+void Oscar::SSI::setAlias( const QString& newAlias )
+{
+ m_alias = newAlias;
+}
+
+bool Oscar::SSI::waitingAuth() const
+{
+ return m_waitingAuth;
+}
+
+void Oscar::SSI::setWaitingAuth( bool waiting )
+{
+ m_waitingAuth = waiting;
+}
+
+void Oscar::SSI::setIconHash( QByteArray hash )
+{
+ m_hash.duplicate( hash );
+}
+
+QByteArray Oscar::SSI::iconHash( ) const
+{
+ return m_hash;
+}
+
+QString Oscar::SSI::toString() const
+{
+ QString ssiString = QString::fromLatin1( "name: " );
+ ssiString += m_name;
+ ssiString += " gid: ";
+ ssiString += QString::number( m_gid );
+ ssiString += " bid: ";
+ ssiString += QString::number( m_bid );
+ ssiString += " type: ";
+ ssiString += QString::number( m_type );
+ ssiString += " tlv length: ";
+ ssiString += QString::number( m_tlvLength );
+ return ssiString;
+}
+
+bool Oscar::SSI::operator==( const SSI& item ) const
+{
+ if ( m_name == item.name() && m_gid == item.gid() && m_bid == item.bid() && m_type == item.type() )
+ return true;
+ else
+ return false;
+}
+
+Oscar::SSI::operator bool() const
+{
+ return isValid();
+}
+
+Oscar::SSI::operator QByteArray() const
+{
+ Buffer b;
+ QCString name( m_name.utf8() );
+ uint namelen = name.length();
+ const char *namedata = name;
+ b.addWord( namelen );
+ // Using namedata instead of name because
+ // Buffer::addString(QByteArray, DWORD) ignores it's second argument,
+ // while Buffer::addString(const char*, DWORD) does not ignore it.
+ // We must provide the explicit length argument to addString() because
+ // we don't need trailing null byte to be added when automatic
+ // conversion from QCString to QByteArray is performed.
+ // This hack will not be needed with Qt 4.
+ b.addString( namedata, namelen );
+ b.addWord( m_gid );
+ b.addWord( m_bid );
+ b.addWord( m_type );
+ b.addWord( m_tlvLength );
+ QValueList<Oscar::TLV>::const_iterator it = m_tlvList.begin();
+ for( ; it != m_tlvList.end(); ++it )
+ {
+ b.addWord( (*it).type );
+ b.addWord( (*it).length );
+ b.addString( (*it).data, (*it).data.size() );
+ }
+
+ return (QByteArray) b;
+}
+
+
+//kate: indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/oscartypeclasses.h b/kopete/protocols/oscar/liboscar/oscartypeclasses.h
new file mode 100644
index 00000000..94e0c910
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscartypeclasses.h
@@ -0,0 +1,144 @@
+/*
+ Kopete Oscar Protocol
+ oscartypeclasses.h - Oscar Type Definitions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCARTYPECLASSES_H_
+#define _OSCARTYPECLASSES_H_
+
+#include <qglobal.h>
+#include <qstring.h>
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include "kopete_export.h"
+
+namespace Oscar
+{
+class KOPETE_EXPORT TLV
+{
+public:
+
+ TLV();
+ TLV( Q_UINT16, Q_UINT16, char* data );
+ TLV( Q_UINT16, Q_UINT16, const QByteArray& );
+ TLV( const TLV& t );
+
+ operator bool() const;
+
+ Q_UINT16 type;
+ Q_UINT16 length;
+ QByteArray data;
+
+};
+
+class KOPETE_EXPORT SSI
+{
+public:
+ SSI();
+ SSI( const QString &name, int gid, int bid, int type, const QValueList<TLV>& tlvlist, int tlvLength = 0 );
+ SSI( const SSI& other );
+
+ /** Get the validity of this item */
+ bool isValid() const;
+
+ /** \brief The name of this SSI item.
+ * This is usually the screenname, ICQ number, or group name. */
+ QString name() const;
+
+ /** \brief The group id of the SSI item */
+ Q_UINT16 gid() const;
+
+ /** \brief The buddy id of the SSI item */
+ Q_UINT16 bid() const;
+
+ /**
+ * \brief The type of the SSI Item.
+ * see ROSTER_* defines on oscartypes.h
+ * Use a value of 0xFFFF for an SSI item not on the server list
+ */
+ Q_UINT16 type() const;
+
+ /** \brief the TLV list for the item */
+ const QValueList<TLV>& tlvList() const;
+
+ /** \brief Set the TLV list for the item */
+ void setTLVList( QValueList<TLV> );
+
+ /**
+ * \brief Set the length of the TLV list
+ *
+ * This is not the number of items in the list!! It's the aggregation of the
+ * sizes of the TLVs
+ */
+ void setTLVListLength( Q_UINT16 newLength );
+
+ /** \brief Get the TLV list length */
+ Q_UINT16 tlvListLength() const;
+
+ /**
+ * Get the alias for the SSI item
+ * This is TLV 0x0131 in an SSI item
+ */
+ QString alias() const;
+
+ /**
+ * Set the alias for the SSI item
+ * This should be done after a successful modification of the item
+ * on the server
+ */
+ void setAlias( const QString& newAlias );
+
+ /** \brief Indicates we're awaiting authorization from this item */
+ bool waitingAuth() const;
+
+ /** Set whether we are waiting authorization or not from this item */
+ void setWaitingAuth( bool waiting );
+
+ void setIconHash( QByteArray hash );
+
+ QByteArray iconHash() const;
+
+ /** \brief String representation of our SSI object */
+ QString toString() const;
+
+ bool operator==( const SSI& item ) const;
+ operator bool() const;
+
+ operator QByteArray() const;
+
+ void refreshTLVLength();
+
+ //! parse the TLVs checking for aliases and auth and stuff like that
+ void checkTLVs();
+
+private:
+ QString m_name;
+ int m_gid;
+ int m_bid;
+ int m_type;
+ QValueList<TLV> m_tlvList;
+ int m_tlvLength;
+ bool m_waitingAuth;
+ QString m_alias;
+ QByteArray m_hash;
+};
+
+}
+
+//kate: indent-mode csands; auto-insert-doxygen on; tab-width 4;
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/oscartypes.h b/kopete/protocols/oscar/liboscar/oscartypes.h
new file mode 100644
index 00000000..b7d4f55b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscartypes.h
@@ -0,0 +1,292 @@
+/*
+ Kopete Oscar Protocol
+ oscartypes.h - Oscar Type Definitions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCARTYPES_H_
+#define _OSCARTYPES_H_
+
+#include "oscartypeclasses.h"
+#include <qglobal.h>
+#include <qdatetime.h>
+#include <qstring.h>
+
+//! Debug Areas
+const int OSCAR_RAW_DEBUG = 14151;
+const int OSCAR_GEN_DEBUG = 14150;
+const int OSCAR_AIM_DEBUG = 14152;
+const int OSCAR_ICQ_DEBUG = 14153;
+
+namespace Oscar
+{
+//! Capabilities
+enum Capabilities
+{
+ CAP_CHAT = 0, CAP_VOICE, CAP_SENDFILE, CAP_ISICQ, CAP_IMIMAGE, CAP_BUDDYICON, CAP_SAVESTOCKS,
+ CAP_GETFILE, CAP_ICQSERVERRELAY, CAP_GAMES, CAP_GAMES2, CAP_SENDBUDDYLIST, CAP_RTFMSGS, CAP_IS_2001,
+ CAP_TRILLIAN, CAP_TRILLIANCRYPT, CAP_APINFO, CAP_UTF8, CAP_TYPING, CAP_INTEROPERATE, CAP_KOPETE, CAP_MICQ,
+ CAP_MACICQ, CAP_SIMOLD, CAP_SIMNEW, CAP_XTRAZ, CAP_STR_2001, CAP_STR_2002, CAP_LAST
+};
+
+typedef unsigned char cap[16];
+const cap oscar_caps[] =
+{
+ //CAP_CHAT,
+ {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ //CAP_VOICE,
+ {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_SENDFILE,
+ {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_ISICQ,
+ {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_IMIMAGE,
+ {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_BUDDYICON,
+ {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_SAVESTOCKS,
+ {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_GETFILE,
+ {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_ICQSERVERRELAY,
+ {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_GAMES,
+ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_GAMES2,
+ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_SENDBUDDYLIST,
+ {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_RTFMSGS,
+ {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
+ 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92},
+
+ // CAP_IS_2001,
+ {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
+ 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf},
+
+ // CAP_TRILLIAN
+ {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34,
+ 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x09},
+
+ // CAP_TRILLIANCRYPT
+ {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
+ 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00},
+
+ // CAP_APINFO,
+ {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6,
+ 0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B},
+
+ // CAP_UTF8,
+ {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_TYPING - client supports mini typing notifications
+ {0x56, 0x3F, 0xC8, 0x09, 0x0B, 0x6f, 0x41, 0xBD,
+ 0x9F, 0x79, 0x42, 0x26, 0x09, 0xDF, 0xA2, 0xF3},
+
+ // CAP_INTEROPERATE,
+ {0x09, 0x46, 0x13, 0x4D, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_KOPETE,
+ // last 4 bytes determine version
+ // NOTE change with each Kopete Release!
+ // first number, major version
+ // second number, minor version
+ // third number, point version 100+
+ // fourth number, point version 0-99
+ {'K', 'o', 'p', 'e', 't', 'e', ' ', 'I',
+ 'C', 'Q', ' ', ' ', 0, 12, 0, 7},
+
+ // CAP_MICQ
+ // last 4 bytes determine version
+ {0x6d, 0x49, 0x43, 0x51, 0x20, 0xa9, 0x20, 0x52,
+ 0x2e, 0x4b, 0x2e, 0x20, 0x00, 0x00, 0x00, 0x00},
+
+ // CAP_MACICQ
+ {0xDD, 0x16, 0xF2, 0x02, 0x84, 0xE6, 0x11, 0xD4,
+ 0x90, 0xDB, 0x00, 0x10, 0x4B, 0x9B, 0x4B, 0x7D},
+
+ // CAP_SIMOLD
+ // last byte determines version
+ // (major + 1) << 6 + minor
+ {0x97, 0xB1, 0x27, 0x51, 0x24, 0x3C, 0x43, 0x34,
+ 0xAD, 0x22, 0xD6, 0xAB, 0xF7, 0x3F, 0x14, 0x00},
+
+ // CAP_SIMNEW
+ // last 4 bytes determine version (US-ASCII encoded)
+ {'S', 'I', 'M', ' ', 'c', 'l', 'i', 'e',
+ 'n', 't', ' ', ' ', 0 , 0 , 0 , 0},
+
+ // CAP_XTRAZ
+ {0x1A, 0x09, 0x3C, 0x6C, 0xD7, 0xFD, 0x4E, 0xC5,
+ 0x9D, 0x51, 0xA6, 0x47, 0x4E, 0x34, 0xF5, 0xA0},
+
+ // CAP_STR_2001
+ {0xA0, 0xE9, 0x3F, 0x37, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_STR_2002
+ {0x10, 0xCF, 0x40, 0xD1, 0x4C, 0x7F, 0x11, 0xD1,
+ 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00},
+
+ // CAP_LAST,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+//! Oscar Data Types
+typedef Q_UINT8 BYTE;
+typedef Q_UINT16 WORD;
+typedef Q_UINT32 DWORD;
+
+
+struct FLAP
+{
+ BYTE channel;
+ WORD sequence;
+ WORD length;
+};
+
+struct SNAC
+{
+ WORD family;
+ WORD subtype;
+ WORD flags;
+ DWORD id;
+};
+
+struct RateInfo
+{
+ WORD classId;
+ DWORD windowSize;
+ DWORD initialLevel;
+ DWORD clearLevel;
+ DWORD alertLevel;
+ DWORD limitLevel;
+ DWORD disconnectLevel;
+ DWORD currentLevel;
+ DWORD maxLevel;
+ DWORD lastTime;
+ BYTE currentState;
+};
+
+struct ChatExchangeInfo
+{
+ WORD number;
+ WORD maxRooms;
+ WORD maxRoomNameLength;
+ WORD maxMsgLength;
+ BYTE flags;
+ QString description;
+ BYTE canCreate;
+ QString charset1;
+ QString charset2;
+ QString lang1;
+ QString lang2;
+};
+
+struct ChatRoomInfo
+{
+ WORD exchange;
+ QByteArray cookie;
+ WORD instance;
+ QString description;
+ WORD maxMsgLength;
+ QString name;
+};
+
+struct ClientVersion
+{
+ QString clientString;
+ WORD clientId;
+ WORD major;
+ WORD minor;
+ WORD point;
+ WORD build;
+ DWORD other;
+ QString country;
+ QString lang;
+};
+
+ /* ICQ Version Characteristics */
+ const unsigned char ICQ_TCP_VERSION = 0x0008;
+
+ /* AIM Version Characteristics */
+ const char AIM_MD5_STRING[] = "AOL Instant Messenger (SM)";
+
+ /* SSI types */
+ const WORD ROSTER_CONTACT = 0x0000; // a normal contact
+ const WORD ROSTER_GROUP = 0x0001; // a group of contacts
+ const WORD ROSTER_VISIBLE = 0x0002; // a contact on the visible list
+ const WORD ROSTER_INVISIBLE = 0x0003; // a contact on the invisible list
+ const WORD ROSTER_VISIBILITY = 0x0004; // this entry contains visibility setting TLV(0xca)=TLV(202)
+ const WORD ROSTER_PRESENCE = 0x0005; // Presence info (if others can see your idle status, etc)
+ const WORD ROSTER_ICQSHORTCUT = 0x0009; // Unknown or ICQ2k shortcut bar items
+ const WORD ROSTER_IGNORE = 0x000e; // a contact on the ignore list
+ const WORD ROSTER_LASTUPDATE = 0x000F; // Last update date (name: "LastUpdateDate")
+ const WORD ROSTER_NONICQ = 0x0010; // a non-icq contact, no UIN, used to send SMS
+ const WORD ROSTER_IMPORTTIME = 0x0013; // roster import time (name: "Import time")
+ const WORD ROSTER_BUDDYICONS = 0x0014; // Buddy icon info. (names: from "0" and incrementing by one)
+
+ /* User classes/statuses */
+ const WORD CLASS_UNCONFIRMED = 0x0001; // AOL Unconfirmed user
+ const WORD CLASS_ADMINISTRATOR = 0x0002; // AOL Administrator
+ const WORD CLASS_AOL = 0x0004; // AOL Staff
+ const WORD CLASS_COMMERCIAL = 0x0008; // AOL commercial account
+ const WORD CLASS_FREE = 0x0010; // ICQ non-commerical account
+ const WORD CLASS_AWAY = 0x0020; // Away status
+ const WORD CLASS_ICQ = 0x0040; // ICQ user
+ const WORD CLASS_WIRELESS = 0x0080; // AOL wireless user
+ const WORD CLASS_UNKNOWN100 = 0x0100; // Unknown
+ const WORD CLASS_UNKNOWN400 = 0x0400; // Unknown
+ const WORD CLASS_UNKNOWN800 = 0x0800; // Unknown
+
+ const WORD STATUS_ONLINE = 0x0000; // Online
+ const WORD STATUS_AWAY = 0x0001; // Away
+ const WORD STATUS_DND = 0x0002; // Do not Disturb
+ const WORD STATUS_NA = 0x0004; // Not Available
+ const WORD STATUS_OCCUPIED = 0x0010; // Occupied (BUSY/BISY)
+ const WORD STATUS_FREE4CHAT = 0x0020; // Free for chat
+ const WORD STATUS_INVISIBLE = 0x0100; // Invisible
+}
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/oscarutils.cpp b/kopete/protocols/oscar/liboscar/oscarutils.cpp
new file mode 100644
index 00000000..13e28d9e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarutils.cpp
@@ -0,0 +1,300 @@
+/*
+ Kopete Oscar Protocol
+ oscarutils.cpp - Oscar Utility Functions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarutils.h"
+#include <qhostaddress.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+
+using namespace Oscar;
+
+QString Oscar::normalize( const QString& contact )
+{
+ QString normal = contact.lower();
+ normal.remove( ' ' );
+ return normal;
+}
+
+bool Oscar::operator==( TLV a, TLV b )
+{
+ if ( a.type == b.type && a.length == b.length )
+ return true;
+ else
+ return false;
+}
+
+TLV Oscar::findTLV( const QValueList<TLV>& list, int type )
+{
+ TLV t;
+ QValueList<TLV>::const_iterator it;
+ for ( it = list.begin(); it != list.end(); ++it )
+ {
+ if ( ( *it ).type == type )
+ return ( *it );
+ }
+
+ return t;
+}
+
+bool Oscar::uptateTLVs( SSI& item, const QValueList<TLV>& list )
+{
+ bool changed = false;
+ QValueList<TLV> tList( item.tlvList() );
+
+ QValueList<TLV>::const_iterator it;
+ for ( it = list.begin(); it != list.end(); ++it )
+ {
+ TLV t = Oscar::findTLV( tList, ( *it ).type );
+ if ( t && t.length == ( *it ).length &&
+ memcmp( t.data.data(), ( *it ).data.data(), t.length ) == 0 )
+ continue;
+
+ if ( t )
+ tList.remove( t );
+
+ tList.append( *it );
+ changed = true;
+ }
+
+ if ( changed )
+ item.setTLVList( tList );
+
+ return changed;
+}
+
+int Oscar::parseCap( char* cap )
+{
+ int capflag = -1;
+ for (int i = 0; i < CAP_LAST; i++)
+ {
+ if (memcmp(&oscar_caps[i], cap, 16) == 0)
+ {
+ capflag = i;
+ break; // should only match once...
+ }
+ }
+ return capflag;
+}
+
+const QString Oscar::capToString( char* cap )
+{
+ QString dbg;
+
+ dbg.sprintf( "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ cap[0], cap[1], cap[2], cap[3], cap[4], cap[5], cap[6], cap[7], cap[8], cap[9],
+ cap[10], cap[11], cap[12], cap[13], cap[14], cap[15] );
+
+ return dbg;
+}
+
+DWORD Oscar::parseCapabilities( Buffer &inbuf, QString &versionString )
+{
+ //
+ // FIXME: port capabilities array to some qt based list class, makes usage of memcmp obsolete
+ //
+ DWORD capflags = 0;
+ QString dbgCaps = "CAPS: ";
+
+ while(inbuf.length() >= 16)
+ {
+ QByteArray cap;
+ cap.duplicate( inbuf.getBlock(16) );
+
+ for (int i=0; i < CAP_LAST; i++)
+ {
+ if (i == CAP_KOPETE)
+ {
+ if (memcmp(&oscar_caps[i], cap.data(), 12) == 0)
+ {
+ capflags |= (1 << i);
+ versionString.sprintf( "%d.%d.%d%d", cap[12], cap[13], cap[14], cap[15] );
+ versionString.insert( 0, "Kopete " );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Kopete version - " << versionString << endl;
+ }
+ }
+ else if (i == CAP_MICQ)
+ {
+ if (memcmp(&oscar_caps[i], cap.data(), 12) == 0)
+ {
+ kdDebug(14150) << k_funcinfo << "MICQ version : <" <<
+ (int)cap[12] << ":" << (int)cap[13] << ":" <<
+ (int)cap[14] << ":" << (int)cap[15] << ">" << endl;
+
+ capflags |= (1 << i);
+
+ // FIXME: how to decode this micq version mess? [mETz - 08.06.2004]
+ /*versionString.sprintf("%d.%d.%d%d",
+ cap[12], cap[13], cap[14], cap[15]);*/
+ break;
+ }
+ }
+ else if (i == CAP_SIMNEW)
+ {
+ if (memcmp(&oscar_caps[i], cap, 12) == 0)
+ {
+ kdDebug(14150) << k_funcinfo << "SIM version : <" <<
+ (unsigned int)cap[12] << ":" << (unsigned int)cap[13] << ":" <<
+ (unsigned int)cap[14] << ":" << (unsigned int)cap[15] << ">" << endl;
+ capflags |= (1 << i);
+ versionString.sprintf("%d.%d.%d%d",
+ cap[12], cap[13], cap[14], cap[15]);
+ versionString.insert( 0, "SIM " );
+ break;
+ }
+ }
+ else if (i == CAP_SIMOLD)
+ {
+ if (memcmp(&oscar_caps[i], cap, 15) == 0)
+ {
+ int hiVersion = (cap[15] >> 6) - 1;
+ unsigned loVersion = cap[15] & 0x1F;
+ kdDebug(14150) << k_funcinfo << "OLD SIM version : <" <<
+ hiVersion << ":" << loVersion << endl;
+ capflags |= (1 << i);
+ versionString.sprintf("%d.%d", (unsigned int)hiVersion, loVersion);
+ versionString.insert( 0, "SIM " );
+ break;
+ }
+ }
+ else if (memcmp(&oscar_caps[i], cap.data(), 16) == 0)
+ {
+ capflags |= (1 << i);
+ dbgCaps += capName(i);
+ break;
+ } // END if(memcmp...
+ } // END for...
+ }
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << dbgCaps << endl;
+ return capflags;
+}
+
+const QString Oscar::capName( int capNumber )
+{
+ QString capString;
+
+ switch ( capNumber )
+ {
+ case CAP_VOICE:
+ capString = "CAP_VOICE ";
+ break;
+ case CAP_BUDDYICON:
+ capString = "CAP_BUDDYICON ";
+ break;
+ case CAP_IMIMAGE:
+ capString = "CAP_IMIMAGE ";
+ break;
+ case CAP_CHAT:
+ capString = "CAP_CHAT ";
+ break;
+ case CAP_GETFILE:
+ capString = "CAP_GETFILE ";
+ break;
+ case CAP_SENDFILE:
+ capString = "CAP_SENDFILE ";
+ break;
+ case CAP_GAMES2:
+ case CAP_GAMES:
+ capString = "CAP_GAMES ";
+ break;
+ case CAP_SAVESTOCKS:
+ capString = "CAP_SAVESTOCKS ";
+ break;
+ case CAP_SENDBUDDYLIST:
+ capString = "CAP_SENDBUDDYLIST ";
+ break;
+ case CAP_ISICQ:
+ capString = "CAP_ISICQ ";
+ break;
+ case CAP_APINFO:
+ capString = "CAP_APINFO ";
+ break;
+ case CAP_RTFMSGS:
+ capString = "CAP_RTFMSGS ";
+ break;
+ case CAP_ICQSERVERRELAY:
+ capString = "CAP_ICQSERVERRELAY ";
+ break;
+ case CAP_IS_2001:
+ capString = "CAP_IS_2001 ";
+ break;
+ case CAP_TRILLIAN:
+ capString = "CAP_TRILLIAN ";
+ break;
+ case CAP_TRILLIANCRYPT:
+ capString = "CAP_TRILLIANCRYPT ";
+ break;
+ case CAP_UTF8:
+ capString = "CAP_UTF8 ";
+ break;
+ case CAP_TYPING:
+ capString = "CAP_TYPING ";
+ break;
+ case CAP_INTEROPERATE:
+ capString = "CAP_INTEROPERATE ";
+ break;
+ case CAP_KOPETE:
+ capString = "CAP_KOPETE ";
+ break;
+ case CAP_MICQ:
+ capString = "CAP_MICQ ";
+ break;
+ case CAP_MACICQ:
+ capString = "CAP_MACICQ ";
+ break;
+ case CAP_SIMOLD:
+ capString = "CAP_SIMOLD ";
+ break;
+ case CAP_SIMNEW:
+ capString = "CAP_SIMNEW ";
+ break;
+ case CAP_XTRAZ:
+ capString = "CAP_XTRAZ ";
+ break;
+ case CAP_STR_2001:
+ capString = "CAP_STR_2001 ";
+ break;
+ case CAP_STR_2002:
+ capString = "CAP_STR_2002 ";
+ break;
+ default:
+ capString = "UNKNOWN CAP ";
+ } // END switch
+
+ return capString;
+}
+
+DWORD Oscar::getNumericalIP(const QString &address)
+{
+ QHostAddress addr;
+ if ( addr.setAddress( address ) == false )
+ return 0;
+
+ return (DWORD)addr.toIPv4Address();
+}
+
+QString Oscar::getDottedDecimal( DWORD address )
+{
+ QHostAddress addr;
+ addr.setAddress((Q_UINT32)address);
+ return addr.toString();
+}
+
+
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/oscarutils.h b/kopete/protocols/oscar/liboscar/oscarutils.h
new file mode 100644
index 00000000..bf8b5aba
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/oscarutils.h
@@ -0,0 +1,93 @@
+/*
+ Kopete Oscar Protocol
+ oscarutils.h - Oscar Utility Functions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _OSCARUTILS_H_
+#define _OSCARUTILS_H_
+
+#include <qglobal.h>
+#include <qvaluelist.h>
+#include <qstring.h>
+#include "oscartypes.h"
+#include "buffer.h"
+
+namespace Oscar
+{
+
+///Normalize the contact name to all lowercase and no spaces
+KOPETE_EXPORT QString normalize( const QString& );
+
+///compare TLVs for equality
+KOPETE_EXPORT bool operator==( TLV, TLV );
+
+/**
+ * Find the TLV corresponding to the type in the list
+ */
+KOPETE_EXPORT TLV findTLV( const QValueList<TLV>&, int type );
+
+/**
+ * Update TLVs of SSI item from TLV list if necessary
+ * \return true if something was updated
+ */
+KOPETE_EXPORT bool uptateTLVs( SSI& item, const QValueList<TLV>& list );
+
+/**
+ * Get the value of the capability that corresponds to the value
+ * in the Capabilities enum
+ * \return -1 if the capability isn't found
+ * \return a non-negative number corresponding to the value of the
+ * capability in the Capabilities enum
+ */
+int parseCap( char* cap );
+
+/**
+ * Convert the capability to a string we can print
+ */
+const QString capToString(char *cap);
+
+/**
+ * Parse the character array for validness and a version string
+ * \param buffer the buffer we'll be parsing for capabilities
+ * \param versionString a QString reference that will contain the
+ * version string of the detected client. Will be QString::null if
+ * no client is found
+ * \return a DWORD containg a bit array of the capabilities we found
+ */
+DWORD parseCapabilities(Buffer &inbuf, QString &versionString);
+
+/**
+ * Get the name of the capability from its number
+ */
+const QString capName( int capNumber );
+
+/**
+ * Convert an IP address in dotted decimal notation to a
+ * numerical constant
+ */
+DWORD getNumericalIP( const QString& address );
+
+/**
+ * Convert a numerical constant that is an IP address to
+ * dotted decimal format
+ */
+QString getDottedDecimal( DWORD address );
+
+}
+
+#endif
+
+//kate: auto-insert-doxygen on; tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp
new file mode 100644
index 00000000..a1baf073
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.cpp
@@ -0,0 +1,137 @@
+/*
+ Kopete Oscar Protocol
+ aimlogintask.h - Handles logging into to the AIM service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "ownuserinfotask.h"
+#include <qcstring.h>
+#include <kdebug.h>
+#include "buffer.h"
+#include "connection.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+#include "userdetails.h"
+#include "ssimanager.h"
+
+
+using namespace Oscar;
+
+OwnUserInfoTask::OwnUserInfoTask( Task* parent ) : Task( parent )
+{
+}
+
+
+OwnUserInfoTask::~OwnUserInfoTask()
+{
+}
+
+
+bool OwnUserInfoTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+ else
+ {
+ if ( st->snacService() == 0x01 &&
+ ( st->snacSubtype() == 0x0F || st->snacSubtype() == 0x21 ) )
+ return true;
+ else
+ return false;
+ }
+
+}
+
+bool OwnUserInfoTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ Buffer* b = transfer->buffer();
+ if ( st->snacSubtype() == 0x0F )
+ {
+ UserDetails ud;
+ ud.fill( b );
+ m_details = ud;
+ emit gotInfo();
+ setSuccess( 0, QString::null );
+ return true;
+ }
+ else
+ {
+ bool needUpload = false;
+ WORD infoType = b->getWord();
+ if ( infoType == 0x0000 || infoType == 0x0001 )
+ {
+ BYTE flags = b->getByte();
+ if ( flags == 0x41 ) //we need to do a buddy upload when bit 8 = 1
+ needUpload = true;
+
+ QByteArray qba;
+ if ( b->length() != 0 )
+ { //buffer might be empty if flags bit 8 = 1
+ BYTE checksumLength = b->getByte();
+ qba.duplicate( b->getBlock( checksumLength ) );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Self icon checksum: " << qba << endl;
+ }
+
+ if ( needUpload )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buddy icon upload requested" << endl;
+ emit buddyIconUploadRequested();
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no item for hash found" << endl;
+ }
+ }
+
+ if ( infoType == 0x0002 )
+ {
+ QString availableMsg( b->getBSTR() );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "self available message: " << endl;
+ }
+
+ setSuccess( 0, QString::null );
+ return true;
+ }
+
+ }
+
+ return false;
+}
+
+void OwnUserInfoTask::onGo()
+{
+ //Send SNAC( 0x01, 0x0E )
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x000E, 0x0000, client()->snacSequence() };
+ Buffer *b = new Buffer(); //empty snac data
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+UserDetails OwnUserInfoTask::getInfo() const
+{
+ return m_details;
+}
+
+//kate: tab-width 4; indent-mode csands;
+
+#include "ownuserinfotask.moc"
diff --git a/kopete/protocols/oscar/liboscar/ownuserinfotask.h b/kopete/protocols/oscar/liboscar/ownuserinfotask.h
new file mode 100644
index 00000000..30a169db
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ownuserinfotask.h
@@ -0,0 +1,59 @@
+/*
+ Kopete Oscar Protocol
+ aimlogintask.h - Handles logging into to the AIM service
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef OWNUSERINFOTASK_H
+#define OWNUSERINFOTASK_H
+
+#include "task.h"
+#include "userdetails.h"
+
+/**
+Request our user info from the server and handle our user info when it comes back
+
+@author Kopete Developers
+*/
+class OwnUserInfoTask : public Task
+{
+Q_OBJECT
+public:
+ OwnUserInfoTask( Task* parent );
+
+ ~OwnUserInfoTask();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+ UserDetails getInfo() const;
+
+signals:
+ /** Emitted when user info is recieved. Needed because succeeded() is only emitted once. */
+ void gotInfo();
+
+ void haveAvailableMessage( const QString& );
+
+ void haveIconChecksum( const QString& );
+
+ void buddyIconUploadRequested();
+
+private:
+ UserDetails m_details;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.cpp b/kopete/protocols/oscar/liboscar/prmparamstask.cpp
new file mode 100644
index 00000000..3668c73b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/prmparamstask.cpp
@@ -0,0 +1,72 @@
+/*
+ Kopete Oscar Protocol
+ prmparamstask.h - handle OSCAR protocol errors
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "prmparamstask.h"
+#include <kdebug.h>
+#include "connection.h"
+#include "transfer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+
+using namespace Oscar;
+
+PRMParamsTask::PRMParamsTask( Task* parent )
+ : Task( parent )
+{
+}
+
+
+PRMParamsTask::~PRMParamsTask()
+{
+}
+
+
+bool PRMParamsTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0009 && st->snacSubtype() == 0x0003 )
+ return true;
+
+ return false;
+}
+
+bool PRMParamsTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Ignoring PRM Parameters. We don't use them" << endl;
+ setSuccess( 0, QString::null );
+ return true;
+ }
+
+ return false;
+}
+
+void PRMParamsTask::onGo()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending PRM Parameters request" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0009, 0x0002, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ Transfer *t = createTransfer( f, s, buffer );
+ send( t );
+}
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/prmparamstask.h b/kopete/protocols/oscar/liboscar/prmparamstask.h
new file mode 100644
index 00000000..eebfdc61
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/prmparamstask.h
@@ -0,0 +1,42 @@
+/*
+ Kopete Oscar Protocol
+ prmparamstask.h - handle OSCAR protocol errors
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef PRMPARAMSTASK_H
+#define PRMPARAMSTASK_H
+
+#include <task.h>
+
+class Transfer;
+
+/**
+@author Matt Rogers
+*/
+class PRMParamsTask : public Task
+{
+public:
+ PRMParamsTask( Task* parent );
+ ~PRMParamsTask();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+};
+
+#endif
+
+// kate: space-indent on; tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/profiletask.cpp b/kopete/protocols/oscar/liboscar/profiletask.cpp
new file mode 100644
index 00000000..d64d5dbe
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/profiletask.cpp
@@ -0,0 +1,119 @@
+/*
+ Kopete Oscar Protocol
+ profiletask.h - Update the user's profile on the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "profiletask.h"
+
+#include <qstring.h>
+#include <kdebug.h>
+
+#include "transfer.h"
+#include "connection.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+
+using namespace Oscar;
+
+ProfileTask::ProfileTask( Task* parent )
+ : Task( parent )
+{
+}
+
+
+ProfileTask::~ProfileTask()
+{
+}
+
+
+bool ProfileTask::forMe( const Transfer* transfer ) const
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+bool ProfileTask::take( Transfer* transfer )
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+void ProfileTask::onGo()
+{
+ sendProfileUpdate();
+}
+
+void ProfileTask::setProfileText( const QString& text )
+{
+ m_profileText = text;
+}
+
+void ProfileTask::setAwayMessage( const QString& text )
+{
+ m_awayMessage = text;
+}
+
+void ProfileTask::sendProfileUpdate()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND (CLI_SETUSERINFO/CLI_SET_LOCATION_INFO)" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0002, 0x0004, 0x0000, client()->snacSequence() };
+ Buffer *buffer = new Buffer();
+ Buffer capBuf;
+
+ if ( !m_profileText.isNull() && !client()->isIcq() )
+ {
+ static const QString defencoding = "text/aolrtf; charset=\"us-ascii\"";
+ buffer->addTLV(0x0001, defencoding.length(), defencoding.latin1());
+ buffer->addTLV(0x0002, m_profileText.length(), m_profileText.local8Bit());
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting profile = " << m_profileText << endl;
+ }
+
+ if ( !m_awayMessage.isNull() && !client()->isIcq() )
+ {
+ static const QString defencoding = "text/aolrtf; charset=\"us-ascii\"";
+ buffer->addTLV(0x0003, defencoding.length(), defencoding.latin1());
+ buffer->addTLV(0x0004, m_awayMessage.length(), m_awayMessage.local8Bit());
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting away message = " << m_awayMessage << endl;
+ }
+
+ if ( client()->isIcq() )
+ {
+ capBuf.addString( oscar_caps[CAP_ICQSERVERRELAY], 16 ); // we support type-2 messages
+ capBuf.addString( oscar_caps[CAP_UTF8], 16 ); // we can send/receive UTF encoded messages
+ capBuf.addString( oscar_caps[CAP_ISICQ], 16 ); // I think this is an icq client, but maybe I'm wrong
+ capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile
+ //capBuf.addString( oscar_caps[CAP_RTFMSGS], 16 ); // we do incoming RTF messages
+ capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us!
+ capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture?
+ }
+ else
+ {
+ capBuf.addString( oscar_caps[CAP_UTF8], 16 ); //we can send/receive UTF encoded messages
+ capBuf.addString( oscar_caps[CAP_KOPETE], 16 ); // we are the borg, resistance is futile
+ capBuf.addString( oscar_caps[CAP_TYPING], 16 ); // we know you're typing something to us!
+ capBuf.addString( oscar_caps[CAP_BUDDYICON], 16 ); //can you take my picture?
+ }
+
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "adding capabilities, size=" << capBuf.length() << endl;
+ buffer->addTLV(0x0005, capBuf.length(), capBuf.buffer());
+ Transfer* st = createTransfer( f, s , buffer );
+ send( st );
+ setSuccess( 0, QString::null );
+
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/profiletask.h b/kopete/protocols/oscar/liboscar/profiletask.h
new file mode 100644
index 00000000..23555105
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/profiletask.h
@@ -0,0 +1,56 @@
+/*
+ Kopete Oscar Protocol
+ profiletask.h - Update the user's profile on the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef PROFILETASK_H
+#define PROFILETASK_H
+
+#include "task.h"
+
+/**
+Task that sets the profile and away message on the server (AIM only).
+Also takes care of updating the capabilities supported by the client (AIM and ICQ).
+
+The profile will be updated only if the profile text has been set non-null.
+The away message will be set only if the away message has been set non-null.
+
+@author Matt Rogers
+*/
+class ProfileTask : public Task
+{
+public:
+ ProfileTask( Task* parent );
+ ~ProfileTask();
+
+ bool forMe( const Transfer* transfer ) const;
+ bool take( Transfer* transfer );
+ void onGo();
+
+ void setProfileText( const QString& text );
+ void setAwayMessage( const QString& text );
+
+private:
+
+ void sendProfileUpdate();
+
+private:
+ QString m_profileText;
+ QString m_awayMessage;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/rateclass.cpp b/kopete/protocols/oscar/liboscar/rateclass.cpp
new file mode 100644
index 00000000..7fee4239
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateclass.cpp
@@ -0,0 +1,246 @@
+/*
+ rateclass.cpp - Rate Limiting Implementation
+
+ Copyright (c) 2004 by Tom Linsky <thomas.linsky@cwru.edu>
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "rateclass.h"
+#include <qtimer.h>
+#include <kdebug.h>
+#include "transfer.h"
+
+using namespace Oscar;
+
+RateClass::RateClass( QObject* parent )
+: QObject( parent, 0 )
+{
+ m_waitingToSend = false;
+ m_packetTimer.start();
+}
+
+RateClass::~ RateClass()
+{
+ dumpQueue();
+ m_members.clear();
+}
+
+WORD RateClass::id() const
+{
+ return m_rateInfo.classId;
+}
+
+void RateClass::setRateInfo( RateInfo newRateInfo )
+{
+ m_rateInfo.classId = newRateInfo.classId;
+ m_rateInfo.windowSize = newRateInfo.windowSize;
+ m_rateInfo.clearLevel = newRateInfo.clearLevel;
+ m_rateInfo.alertLevel = newRateInfo.alertLevel;
+ m_rateInfo.limitLevel = newRateInfo.limitLevel;
+ m_rateInfo.disconnectLevel = newRateInfo.disconnectLevel;
+ m_rateInfo.currentLevel = newRateInfo.currentLevel;
+ m_rateInfo.initialLevel = newRateInfo.initialLevel;
+ m_rateInfo.maxLevel = newRateInfo.maxLevel;
+ m_rateInfo.lastTime = newRateInfo.lastTime;
+ m_rateInfo.currentState = newRateInfo.currentState;
+}
+
+void RateClass::addMember( const SNAC& s )
+{
+ addMember( s.family, s.subtype );
+}
+
+void RateClass::addMember( WORD family, WORD subtype )
+{
+ SnacPair snacPair;
+ snacPair.family = family;
+ snacPair.subtype = subtype;
+ m_members.append( snacPair );
+}
+
+bool RateClass::isMember(const SNAC &s) const
+{
+ QValueList<SnacPair>::const_iterator it;
+ QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd();
+ for ( it = m_members.constBegin(); it != spEnd; ++it )
+ {
+ if ( ( *it ).family == s.family && ( *it ).subtype == s.subtype )
+ return true;
+ }
+ return false;
+}
+
+bool RateClass::isMember( WORD family, WORD subtype ) const
+{
+
+ QValueList<SnacPair>::const_iterator it;
+ QValueList<SnacPair>::const_iterator spEnd = m_members.constEnd();
+ for ( it = m_members.constBegin(); it != spEnd; ++it )
+ {
+ if ( ( *it ).family == family && ( *it ).subtype == subtype )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void RateClass::enqueue( Transfer* t )
+{
+ m_packetQueue.push_back( t );
+ /*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Send queue length is now: "
+ << m_packetQueue.count() << endl;*/
+ setupTimer();
+}
+
+void RateClass::dequeue()
+{
+ m_packetQueue.pop_front();
+}
+
+bool RateClass::queueIsEmpty() const
+{
+ return m_packetQueue.isEmpty();
+}
+
+int RateClass::timeToInitialLevel()
+{
+ DWORD newLevel = 0;
+
+ //get time elapsed since the last packet was sent
+ int timeDiff = m_packetTimer.elapsed();
+
+ newLevel = calcNewLevel( timeDiff );
+
+ if ( newLevel < m_rateInfo.initialLevel )
+ {
+ int waitTime = ( m_rateInfo.initialLevel * m_rateInfo.windowSize ) - ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel );
+ return waitTime;
+ }
+
+ return 0;
+}
+
+int RateClass::timeToNextSend()
+{
+
+ DWORD newLevel = 0;
+
+ //get time elapsed since the last packet was sent
+ int timeDiff = m_packetTimer.elapsed();
+
+ DWORD windowSize = m_rateInfo.windowSize;
+ newLevel = calcNewLevel( timeDiff );
+
+ //The maximum level at which we can safely send a packet
+ DWORD maxPacket = m_rateInfo.alertLevel + RATE_SAFETY_TIME;
+
+/*kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate Information:"
+ << "\nWindow Size: " << windowSize
+ << "\nWindow Size - 1: " << windowSize - 1
+ << "\nOld Level: " << m_rateInfo.currentLevel
+ << "\nAlert Level: " << m_rateInfo.alertLevel
+ << "\nLimit Level: " << m_rateInfo.limitLevel
+ << "\nDisconnect Level: " << m_rateInfo.disconnectLevel
+ << "\nNew Level: " << newLevel
+ << "\nTime elapsed: " << timeDiff
+ << "\nMax level to send: " << maxPacket << endl; */
+
+ //If we are one packet or less away from being blocked, wait to send
+ if ( newLevel < maxPacket || newLevel < m_rateInfo.disconnectLevel )
+ {
+ int waitTime = ( windowSize * maxPacket ) - ( ( windowSize - 1 ) * m_rateInfo.currentLevel );
+ kdDebug(OSCAR_RAW_DEBUG) << "We're sending too fast. Will wait " << waitTime << "ms before sending" << endl;
+ return waitTime;
+ }
+
+ return 0;
+}
+
+DWORD RateClass::calcNewLevel( int timeDifference ) const
+{
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Time since last packet: "
+ // << timeDifference << endl;
+ //Calculate new rate level
+ //NewLevel = ( ( Window - 1 ) * OldLevel + TimeDiff )/Window
+ //add 1 because we never want to round down or there will be problems
+ uint newLevel = ( ( ( m_rateInfo.windowSize - 1 ) * m_rateInfo.currentLevel ) + timeDifference ) / m_rateInfo.windowSize;
+ if ( newLevel > m_rateInfo.initialLevel )
+ newLevel = m_rateInfo.initialLevel;
+
+ return newLevel;
+}
+
+void RateClass::setupTimer()
+{
+ if ( !m_waitingToSend )
+ {
+ m_waitingToSend = true;
+
+ int ttns = timeToNextSend();
+ if ( ttns <= 0 )
+ {
+ slotSend(); //send now
+ }
+ else
+ {
+ QTimer::singleShot( ttns, this, SLOT( slotSend() ) ); //or send later
+ }
+ }
+}
+
+void RateClass::slotSend()
+{
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( m_packetQueue.isEmpty() )
+ return;
+
+ //send then pop off the list
+ emit dataReady( m_packetQueue.first() );
+ dequeue();
+
+ updateRateInfo();
+
+ m_waitingToSend = false;
+
+ // check if we still have packets to send
+ if ( !m_packetQueue.isEmpty() )
+ setupTimer();
+}
+
+void RateClass::updateRateInfo()
+{
+ //Update rate info
+ DWORD newLevel = calcNewLevel( m_packetTimer.elapsed() );
+ m_rateInfo.currentLevel = newLevel;
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Current Level = " << newLevel << endl;
+
+ //restart the timer
+ m_packetTimer.restart();
+}
+
+void RateClass::dumpQueue()
+{
+ QValueList<Transfer*>::iterator it = m_packetQueue.begin();
+ while ( it != m_packetQueue.end() && m_packetQueue.count() > 0 )
+ {
+ Transfer* t = ( *it );
+ it = m_packetQueue.remove( it );
+ delete t;
+ }
+}
+
+#include "rateclass.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/rateclass.h b/kopete/protocols/oscar/liboscar/rateclass.h
new file mode 100644
index 00000000..1bb86f03
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateclass.h
@@ -0,0 +1,132 @@
+/*
+ rateclass.h - Oscar Rate Limiting Implementation
+
+ Copyright (c) 2004 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2004 by Matt Rogers <mattr@k
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RATECLASS_H
+#define RATECLASS_H
+
+#include "oscartypes.h"
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qdatetime.h>
+#include <qpair.h>
+
+const int RATE_SAFETY_TIME = 50;
+
+struct SnacPair
+{
+ int family;
+ int subtype;
+};
+
+class Transfer;
+
+class RateClass : public QObject
+{
+ Q_OBJECT
+public:
+ RateClass( QObject* parent = 0 );
+ ~RateClass();
+
+ /** Accessor for classid */
+ Oscar::WORD id() const;
+
+ /** Sets rate information */
+ void setRateInfo( Oscar::RateInfo newRateInfo );
+
+ /** Add a SNAC to the rate class */
+ void addMember( const Oscar::SNAC& s );
+
+ /** Adds rate class members */
+ void addMember( Oscar::WORD family, Oscar::WORD subtype );
+
+ /** Tells whether the passed snac is a member of this rate class */
+ bool isMember( const Oscar::SNAC& s ) const;
+
+ /**
+ * Tells whether the passed family and subtype combo is a member
+ * of this rate class
+ */
+ bool isMember( Oscar::WORD family, Oscar::WORD subtype ) const;
+
+ /** Add a packet to the queue */
+ void enqueue( Transfer* );
+
+ /** Takes a packet off the front of the queue */
+ void dequeue();
+
+ /** Check if the queue is empty */
+ bool queueIsEmpty() const;
+
+ /**
+ * Calulate the time until we can send again
+ * Uses the first packet on the queue to determine the time since that's
+ * the packet that will get sent.
+ * \return the time in milliseconds that we need to wait
+ */
+ int timeToNextSend();
+
+ /**
+ * Calulate the time until we get to initial level
+ * \return the time in milliseconds that we need to wait
+ */
+ int timeToInitialLevel();
+
+ /**
+ * Calculates a new rate level and updates the rate class' current level
+ * to match
+ */
+ void updateRateInfo();
+
+ /**
+ * Dump the current packet queue. These packets will not be sent. Used
+ * on disconnection
+ */
+ void dumpQueue();
+
+signals:
+
+ /** Tell the rate class manager we're ready to send */
+ void dataReady( Transfer* );
+
+private:
+
+ /** Calculate our new rate level */
+ Oscar::DWORD calcNewLevel( int timeDifference ) const;
+
+ /** sets up the timer for the transfer just added to the queue */
+ void setupTimer();
+
+private slots:
+ /**
+ * Send the packet. Basically emits dataReady for the first transfer
+ */
+ void slotSend();
+
+private:
+
+ Oscar::RateInfo m_rateInfo;
+ QValueList<SnacPair> m_members;
+ QValueList<Transfer*> m_packetQueue;
+ QTime m_packetTimer;
+
+ // we are waiting for the QTimer::singleShot() to send
+ bool m_waitingToSend;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.cpp b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp
new file mode 100644
index 00000000..8b306c0b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateclassmanager.cpp
@@ -0,0 +1,177 @@
+/*
+ Kopete Oscar Protocol
+ rateclassmanager.cpp - Manages the rates we get from the OSCAR server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvaluelist.h>
+#include <kdebug.h>
+
+
+#include "rateclassmanager.h"
+#include "transfer.h"
+#include "connection.h"
+#include "rateclass.h"
+
+
+class RateClassManagerPrivate
+{
+public:
+ //! The list of rate classes owned by this manager
+ QValueList<RateClass*> classList;
+ Connection* client;
+};
+
+RateClassManager::RateClassManager( Connection* parent, const char* name )
+: QObject( parent, name )
+{
+ d = new RateClassManagerPrivate();
+ d->client = parent;
+}
+
+RateClassManager::~RateClassManager()
+{
+ reset();
+ delete d;
+}
+
+void RateClassManager::reset()
+{
+ QValueList<RateClass*>::iterator it = d->classList.begin();
+ while ( it != d->classList.end() && d->classList.count() > 0)
+ {
+ RateClass* rc = ( *it );
+ it = d->classList.remove( it );
+ delete rc;
+ }
+}
+
+void RateClassManager::registerClass( RateClass* rc )
+{
+ QObject::connect( rc, SIGNAL( dataReady( Transfer* ) ), this, SLOT( transferReady( Transfer* ) ) );
+ d->classList.append( rc );
+}
+
+bool RateClassManager::canSend( Transfer* t ) const
+{
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( t );
+
+ if ( !st ) //no snac transfer, no rate limiting
+ { kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Not sending a snac" << endl;
+ return true;
+ }
+
+ RateClass* rc = findRateClass( st );
+ if ( rc )
+ {
+ if ( rc->timeToNextSend() == 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's okay to send" << endl;
+ return true;
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "rate class " << rc->id() << " said it's not okay to send yet" << endl;
+ return false;
+ }
+ }
+ else // no rate class
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "no rate class. doing no rate limiting" << endl;
+ return true;
+ }
+}
+
+void RateClassManager::queue( Transfer* t )
+{
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( t );
+ if ( !st )
+ { //we're not sending a snac
+ transferReady( t );
+ return;
+ }
+
+ RateClass* rc = findRateClass( st );
+ if ( rc )
+ rc->enqueue( st );
+ else
+ transferReady( t );
+}
+
+QValueList<RateClass*> RateClassManager::classList() const
+{
+ return d->classList;
+}
+
+void RateClassManager::transferReady( Transfer* t )
+{
+ //tell the client to send it again. We should be
+ //able to send it now
+ FlapTransfer* ft = dynamic_cast<FlapTransfer*>( t );
+
+ if ( ft )
+ ft->setFlapSequence( d->client->flapSequence() );
+
+ d->client->forcedSend( t );
+}
+
+
+RateClass* RateClassManager::findRateClass( SnacTransfer* st ) const
+{
+ SNAC s = st->snac();
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Looking for SNAC " << s.family << ", " << s.subtype << endl;
+ RateClass* rc = 0L;
+ QValueList<RateClass*>::const_iterator it;
+ QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd();
+
+ for ( it = d->classList.constBegin(); it != rcEnd; ++it )
+ {
+ if ( ( *it )->isMember( s.family, s.subtype ) )
+ {
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found SNAC(" << s.family << ", " << s.subtype << ") in class" << endl;
+ rc = ( *it );
+ break;
+ }
+ }
+
+ return rc;
+}
+
+void RateClassManager::recalcRateLevels()
+{
+ QValueList<RateClass*>::iterator it;
+ QValueList<RateClass*>::iterator rcEnd = d->classList.end();
+ for ( it = d->classList.begin(); it != rcEnd; ++it )
+ ( *it )->updateRateInfo();
+}
+
+int RateClassManager::timeToInitialLevel( SNAC s )
+{
+ QValueList<RateClass*>::const_iterator it;
+ QValueList<RateClass*>::const_iterator rcEnd = d->classList.constEnd();
+
+ for ( it = d->classList.constBegin(); it != rcEnd; ++it )
+ {
+ if ( ( *it )->isMember( s.family, s.subtype ) )
+ {
+ return ( *it )->timeToInitialLevel();
+ }
+ }
+ return 0;
+}
+
+#include "rateclassmanager.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/rateclassmanager.h b/kopete/protocols/oscar/liboscar/rateclassmanager.h
new file mode 100644
index 00000000..4b972ede
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateclassmanager.h
@@ -0,0 +1,83 @@
+/*
+ Kopete Oscar Protocol
+ rateclassmanager.h - Manages the rates we get from the OSCAR server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RATECLASSMANAGER_H
+#define RATECLASSMANAGER_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+#include "oscartypes.h"
+
+class Transfer;
+class SnacTransfer;
+class RateClass;
+class Connection;
+class RateClassManagerPrivate;
+
+
+class RateClassManager : public QObject
+{
+Q_OBJECT
+public:
+ RateClassManager( Connection* parent, const char* name = 0 );
+ ~RateClassManager();
+
+ /** Reset the rate manager */
+ void reset();
+
+ /** Tell the rate manager about the new class */
+ void registerClass( RateClass* );
+
+ //! Check if we can send the packet right away
+ bool canSend( Transfer* t ) const;
+
+ //! Queue a transfer for sending later
+ void queue( Transfer* t );
+
+ /** Get the list of rate classes */
+ QValueList<RateClass*> classList() const;
+
+ /** Recalculate the rate levels for all the classes */
+ void recalcRateLevels();
+
+ /**
+ * Find the rate class for the snac and
+ * calculate time until we get to initial level
+ * \return the time in milliseconds that we need to wait
+ */
+ int timeToInitialLevel( Oscar::SNAC s );
+
+public slots:
+
+ void transferReady( Transfer* );
+
+private:
+
+ /** Find the rate class for the transfer */
+ RateClass* findRateClass( SnacTransfer* st ) const;
+
+private:
+
+ RateClassManagerPrivate* d;
+
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.cpp b/kopete/protocols/oscar/liboscar/rateinfotask.cpp
new file mode 100644
index 00000000..f19cf792
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateinfotask.cpp
@@ -0,0 +1,173 @@
+/*
+ Kopete Oscar Protocol
+ rateinfotask.cpp - Fetch the rate class information
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "rateinfotask.h"
+
+#include <qvaluelist.h>
+#include <kdebug.h>
+#include "rateclass.h"
+#include "rateclassmanager.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+#include "connection.h"
+
+using namespace Oscar;
+
+RateInfoTask::RateInfoTask( Task* parent )
+ : Task( parent )
+{
+ connect( this, SIGNAL( gotRateLimits() ), this, SLOT( sendRateInfoAck() ) );
+}
+
+
+RateInfoTask::~RateInfoTask()
+{
+
+}
+
+
+bool RateInfoTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( st && st->snacService() == 1 && st->snacSubtype() == 7 )
+ return true;
+ else
+ return false;
+}
+
+bool RateInfoTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ handleRateInfoResponse();
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void RateInfoTask::onGo()
+{
+ sendRateInfoRequest();
+}
+
+void RateInfoTask::sendRateInfoRequest()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info request (SNAC 0x01, 0x06)" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x0006, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ Transfer* st = createTransfer( f, s, buffer );
+ send( st );
+}
+
+void RateInfoTask::handleRateInfoResponse()
+{
+ QValueList<RateClass*> rates;
+ Oscar::RateInfo ri;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "handling rate info response (SNAC 0x01, 0x07)" << endl;
+ Buffer* buffer = transfer()->buffer();
+
+ int numClasses = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got " << numClasses << " rate classes" << endl;
+ for ( int i = 0; i < numClasses; i++ )
+ {
+ RateClass* newClass = new RateClass( client()->rateManager() );
+ //parse rate classes and put them somewhere
+ ri.classId = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Rate class: " << ri.classId << endl;
+ //discard the rest (for right now)
+ ri.windowSize = buffer->getDWord(); //window size
+ ri.clearLevel = buffer->getDWord(); //clear level
+ ri.alertLevel = buffer->getDWord(); //alert level
+ ri.limitLevel = buffer->getDWord(); //limit level
+ ri.disconnectLevel = buffer->getDWord(); //disconnect level
+ ri.currentLevel = buffer->getDWord(); //current level
+ ri.initialLevel = ri.currentLevel;
+ ri.maxLevel = buffer->getDWord(); //max level
+ ri.lastTime = buffer->getDWord(); //last time
+ ri.currentState = buffer->getByte(); //current state
+
+ newClass->setRateInfo( ri );
+ rates.append( newClass );
+ }
+
+ int groupNum = 0;
+ int numGroupPairs = 0;
+
+ for ( int i = 0; i < numClasses; i++ )
+ {
+ groupNum = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding snac members to group " << groupNum << endl;
+
+ RateClass* rc = 0L;
+ QValueList<RateClass*>::iterator it = rates.begin();
+ for ( ; it != rates.end(); ++it )
+ {
+ if ( ( *it )->id() == groupNum )
+ {
+ rc = ( *it );
+ break;
+ }
+ }
+
+ m_rateGroups.append( groupNum );
+ numGroupPairs = buffer->getWord();
+ for ( int j = 0; j < numGroupPairs; j++ )
+ {
+ WORD family = buffer->getWord();
+ WORD subtype = buffer->getWord();
+ rc->addMember( family, subtype );
+ }
+ }
+
+ QValueList<RateClass*>::iterator it = rates.begin();
+ QValueList<RateClass*>::iterator rcEnd = rates.end();
+ for ( ; it != rcEnd; ++it )
+ client()->rateManager()->registerClass( ( *it ) );
+
+ emit gotRateLimits();
+}
+
+void RateInfoTask::sendRateInfoAck()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending rate info acknowledgement" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x0008, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+
+ QValueListConstIterator<int> cit = m_rateGroups.begin();
+ QValueListConstIterator<int> end = m_rateGroups.end();
+ for ( cit = m_rateGroups.begin(); cit != end; ++cit )
+ {
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding rate " << (*cit) << " to rate ack" << endl;
+ buffer->addWord( (*cit) );
+ }
+
+ Transfer* st = createTransfer( f, s, buffer );
+ send( st );
+ setSuccess( 0, QString::null );
+}
+
+#include "rateinfotask.moc"
+
+//kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/liboscar/rateinfotask.h b/kopete/protocols/oscar/liboscar/rateinfotask.h
new file mode 100644
index 00000000..3964f0ea
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rateinfotask.h
@@ -0,0 +1,64 @@
+/*
+ Kopete Oscar Protocol
+ rateinfotask.h - Fetch the rate class information
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RATEINFOTASK_H
+#define RATEINFOTASK_H
+
+#include "task.h"
+#include <qvaluelist.h>
+
+using namespace Oscar;
+
+/**
+@author Matt Rogers
+*/
+class RateInfoTask : public Task
+{
+Q_OBJECT
+public:
+ RateInfoTask( Task* parent );
+ ~RateInfoTask();
+ bool take( Transfer* transfer );
+
+protected:
+
+ bool forMe( const Transfer* transfer ) const;
+ void onGo();
+
+signals:
+ void gotRateLimits();
+
+private slots:
+
+ //! Send the rate info request (SNAC 0x01, 0x06)
+ void sendRateInfoRequest();
+
+ //! Handle the rate info response (SNAC 0x01, 0x07)
+ void handleRateInfoResponse();
+
+ //! Acknowledge the rate information
+ void sendRateInfoAck();
+
+private:
+ bool m_needRateAck;
+ QValueList<int> m_rateGroups;
+};
+
+//kate: tab-width 4; indent-mode csands;
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/rtf.cc b/kopete/protocols/oscar/liboscar/rtf.cc
new file mode 100644
index 00000000..6daa636e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rtf.cc
@@ -0,0 +1,2427 @@
+#define yy_create_buffer rtf_create_buffer
+#define yy_delete_buffer rtf_delete_buffer
+#define yy_scan_buffer rtf_scan_buffer
+#define yy_scan_string rtf_scan_string
+#define yy_scan_bytes rtf_scan_bytes
+#define yy_flex_debug rtf_flex_debug
+#define yy_init_buffer rtf_init_buffer
+#define yy_flush_buffer rtf_flush_buffer
+#define yy_load_buffer_state rtf_load_buffer_state
+#define yy_switch_to_buffer rtf_switch_to_buffer
+#define yyin rtfin
+#define yyleng rtfleng
+#define yylex rtflex
+#define yyout rtfout
+#define yyrestart rtfrestart
+#define yytext rtftext
+#define yywrap rtfwrap
+
+#line 20 "rtf.cc"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header$
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#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 yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext 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).
+ */
+typedef unsigned int yy_size_t;
+
+
+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;
+
+ /* 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 yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* 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".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* 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 yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = (int) (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
+static yyconst short int 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 int 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 int yy_meta[16] =
+ { 0,
+ 1, 1, 2, 1, 1, 2, 3, 4, 1, 2,
+ 2, 3, 2, 3, 3
+ } ;
+
+static yyconst short int 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 short int 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 short int 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 short int 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;
+
+/* 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 *yytext;
+#line 1 "rtf.ll"
+#define INITIAL 0
+#line 2 "rtf.ll"
+/*
+ rtf.ll - A simple RTF Parser (Flex code)
+
+ Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+update rtf.cc:
+flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll
+sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc
+rm -f lex.yy.c
+
+*/
+
+#define UP 1
+#define DOWN 2
+#define CMD 3
+#define TXT 4
+#define HEX 5
+#define IMG 6
+#define UNICODE_CHAR 7
+#define SKIP 8
+#define SLASH 9
+#define S_TXT 10
+
+#define YY_NEVER_INTERACTIVE 1
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_MAIN 0
+
+#define YY_NO_UNPUT 1
+#define YY_STACK_USED 0
+#line 447 "rtf.cc"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#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( yytext, yyleng, 1, yyout )
+#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->yy_is_interactive ) \
+ { \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#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
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * 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
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 46 "rtf.ll"
+
+
+#line 601 "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 ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! yy_current_buffer )
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *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:
+YY_RULE_SETUP
+#line 54 "rtf.ll"
+{ return IMG; }
+ YY_BREAK
+case 8:
+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 734 "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->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 yyin at a new source and called
+ * yylex(). 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->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->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->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 ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, 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->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 yylex */
+
+
+/* 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()
+ {
+ register char *dest = yy_current_buffer->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->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->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->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->yy_n_chars = yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* 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. */
+ yy_flex_realloc( (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->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+
+ yy_current_buffer->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;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yytext_ptr = &yy_current_buffer->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()
+ {
+ 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 );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ 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_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#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->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. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ 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 yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+
+ return c;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ 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->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_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 *) yy_flex_alloc( b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( ! b )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ 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 )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ 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) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_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;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ 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 *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_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;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (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 yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* 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 );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#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:
+ {
+ 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())
+ break;
+ QColor &c = colors[t.param];
+ 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/oscar/liboscar/rtf.ll b/kopete/protocols/oscar/liboscar/rtf.ll
new file mode 100644
index 00000000..d982234b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rtf.ll
@@ -0,0 +1,864 @@
+%{
+/*
+ rtf.ll - A simple RTF Parser (Flex code)
+
+ Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+
+update rtf.cc:
+flex -olex.yy.c `test -f rtf.ll || echo './'`rtf.ll
+sed '/^#/ s|lex.yy\.c|rtf.cc|' lex.yy.c >rtf.cc
+rm -f lex.yy.c
+
+*/
+
+#define UP 1
+#define DOWN 2
+#define CMD 3
+#define TXT 4
+#define HEX 5
+#define IMG 6
+#define UNICODE_CHAR 7
+#define SKIP 8
+#define SLASH 9
+#define S_TXT 10
+
+#define YY_NEVER_INTERACTIVE 1
+#define YY_ALWAYS_INTERACTIVE 0
+#define YY_MAIN 0
+
+%}
+
+%option nounput
+%option nostack
+%option prefix="rtf"
+
+%%
+
+"{" { return UP; }
+"}" { return DOWN; }
+"\\"[\\\{\}] { return SLASH; }
+"\\u"[0-9]{3,7}[ ]?"?" { return UNICODE_CHAR; }
+"\\"[A-Za-z]+[0-9]*[ ]? { return CMD; }
+"\\'"[0-9A-Fa-f][0-9A-Fa-f] { return HEX; }
+"<##"[^>]+">" { return IMG; }
+[^\\{}<]+ { return TXT; }
+. { return TXT; }
+%%
+
+#include "rtf2html.h"
+
+void ParStyle::clearFormatting()
+{
+ // For now, do nothing.
+ // dir is not a formatting item.
+}
+
+QString RTF2HTML::quoteString(const QString &_str, quoteMode mode)
+{
+ QString str = _str;
+ str.replace(QRegExp("&"), "&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:
+ {
+ 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())
+ break;
+ QColor &c = colors[t.param];
+ 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/oscar/liboscar/rtf2html.h b/kopete/protocols/oscar/liboscar/rtf2html.h
new file mode 100644
index 00000000..a305b89d
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/rtf2html.h
@@ -0,0 +1,207 @@
+/*
+ rtf2html.h - A simple RTF Parser
+
+ Copyright (c) 2002 by Vladimir Shutoff <vovan@shutoff.ru> (original code)
+ Copyright (c) 2004 by Thiago S. Barcelos <barcelos@ime.usp.br> (Kopete port)
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef RTF2HTML_H
+#define RTF2HTML_H
+
+#include <qstring.h>
+#include <stdio.h>
+
+#include <qtextcodec.h>
+#include <qcolor.h>
+#include <qregexp.h>
+#include <kdebug.h>
+
+#include <vector>
+#include <stack>
+#include <string>
+#include <stdarg.h>
+
+using namespace std;
+
+struct FontDef
+{
+ int charset;
+ string taggedName;
+ string nonTaggedName;
+};
+
+class RTF2HTML;
+
+enum TagEnum
+{
+ TAG_ALL = 0,
+ TAG_FONT_SIZE,
+ TAG_FONT_COLOR,
+ TAG_FONT_FAMILY,
+ TAG_BG_COLOR,
+ TAG_BOLD,
+ TAG_ITALIC,
+ TAG_UNDERLINE
+};
+
+class ParStyle
+{
+public:
+ ParStyle() { dir = DirLTR; }
+ void clearFormatting();
+
+public:
+ enum {DirLTR, DirRTL} dir;
+};
+
+class Level
+{
+public:
+ Level(RTF2HTML *_p);
+ Level(const Level&);
+ void setText(const char* str);
+ void setFontTbl() { m_bFontTbl = true; }
+ void setColors() { m_bColors = true; resetColors(); }
+ void setRed(unsigned char val) { setColor(val, &m_nRed); }
+ void setGreen(unsigned char val) { setColor(val, &m_nGreen); }
+ void setBlue(unsigned char val) { setColor(val, &m_nBlue); }
+ void setFont(unsigned nFont);
+ void setEncoding(unsigned nFont);
+ void setFontName();
+ void setFontColor(unsigned short color);
+ void setFontBgColor(unsigned short color);
+ void setFontSizeHalfPoints(unsigned short sizeInHalfPoints);
+ void setFontSize(unsigned short sizeInPoints);
+ void setBold(bool);
+ void setItalic(bool);
+ void setUnderline(bool);
+ void startParagraph();
+ bool isParagraphOpen() const;
+ void clearParagraphFormatting();
+ void setParagraphDirLTR();
+ void setParagraphDirRTL();
+ void addLineBreak();
+ void flush();
+ void reset();
+ void resetTag(TagEnum tag);
+protected:
+ string text;
+ void Init();
+ RTF2HTML *p;
+ void resetColors() { m_nRed = m_nGreen = m_nBlue = 0; m_bColorInit = false; }
+ void setColor(unsigned char val, unsigned char *p)
+ { *p = val; m_bColorInit=true; }
+
+ // Marks the position in m_tags where this level begun.
+ unsigned m_nTagsStartPos;
+
+ // True when parsing the fonts table
+ bool m_bFontTbl;
+ // True when parsing the colors table.
+ bool m_bColors;
+ // True when inside a 'fname' block.
+ bool m_bFontName;
+ // False until we get the tagged font name.
+ bool m_bTaggedFontNameOk;
+
+ unsigned char m_nRed;
+ unsigned char m_nGreen;
+ unsigned char m_nBlue;
+ bool m_bColorInit;
+ unsigned m_nFont; // 1-based
+ unsigned m_nEncoding;
+ unsigned m_nFontColor; // 1-based
+ unsigned m_nFontSize;
+ unsigned m_nFontBgColor; // 1-based
+ bool m_bBold;
+ bool m_bItalic;
+ bool m_bUnderline;
+};
+
+class OutTag
+{
+public:
+ OutTag(TagEnum _tag, unsigned _param) : tag(_tag), param(_param) {}
+ TagEnum tag;
+ unsigned param;
+};
+
+enum quoteMode
+{
+ quoteHTML,
+ quoteXML,
+ quoteNOBR
+};
+
+class RTF2HTML
+{
+ friend class Level;
+
+public:
+ RTF2HTML();
+ QString Parse(const char *rtf, const char *encoding);
+
+ // Paragraph-specific functions:
+
+ QString quoteString(const QString &_str, quoteMode mode = quoteHTML);
+ // Appends a string with formatting into the paragraph buffer.
+ void PrintUnquoted(const char *str, ...);
+ // Quotes and appends a string to the paragraph buffer.
+ void PrintQuoted(const QString &str);
+ // Writes down the tags from oTags into the paragraph buffer.
+ void FlushOutTags();
+ // Retrieves the top not-yet-written tag.
+ OutTag* getTopOutTag(TagEnum tagType);
+ // Writes down the paragraph buffer and resets the paragraph state.
+ void FlushParagraph();
+
+ // Document-wide functions:
+
+ void PutTag(TagEnum n)
+ {
+ tags.push(n);
+ }
+
+protected:
+
+// Paragraph members
+
+ // True if the paragraph was opened explicitly.
+ bool bExplicitParagraph;
+ // The paragraph's HTML buffer.
+ QString sParagraph;
+ // Defines the paragraph's formatting.
+ ParStyle parStyle;
+ // Tags which weren't yet printed out.
+ vector<OutTag> oTags;
+
+// Document members
+
+ // The document HTML buffer.
+ QString s;
+ // Fonts table.
+ vector<FontDef> fonts;
+ // Colors table.
+ vector<QColor> colors;
+ // Stack of tags (across all levels, not just current level)
+ stack<TagEnum> tags;
+
+// RTF parser internals
+
+ const char *rtf_ptr;
+ const char *encoding;
+ Level cur_level;
+ stack<Level> levels;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/safedelete.cpp b/kopete/protocols/oscar/liboscar/safedelete.cpp
new file mode 100644
index 00000000..703e8ed3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/safedelete.cpp
@@ -0,0 +1,139 @@
+/*
+ safedelete.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "safedelete.h"
+
+#include <qtimer.h>
+
+//----------------------------------------------------------------------------
+// SafeDelete
+//----------------------------------------------------------------------------
+SafeDelete::SafeDelete()
+{
+ lock = 0;
+}
+
+SafeDelete::~SafeDelete()
+{
+ if(lock)
+ lock->dying();
+}
+
+void SafeDelete::deleteLater(QObject *o)
+{
+ if(!lock)
+ deleteSingle(o);
+ else
+ list.append(o);
+}
+
+void SafeDelete::unlock()
+{
+ lock = 0;
+ deleteAll();
+}
+
+void SafeDelete::deleteAll()
+{
+ if(list.isEmpty())
+ return;
+
+ QObjectListIt it(list);
+ for(QObject *o; (o = it.current()); ++it)
+ deleteSingle(o);
+ list.clear();
+}
+
+void SafeDelete::deleteSingle(QObject *o)
+{
+#if QT_VERSION < 0x030000
+ // roll our own QObject::deleteLater()
+ SafeDeleteLater *sdl = SafeDeleteLater::ensureExists();
+ sdl->deleteItLater(o);
+#else
+ o->deleteLater();
+#endif
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLock
+//----------------------------------------------------------------------------
+SafeDeleteLock::SafeDeleteLock(SafeDelete *sd)
+{
+ own = false;
+ if(!sd->lock) {
+ _sd = sd;
+ _sd->lock = this;
+ }
+ else
+ _sd = 0;
+}
+
+SafeDeleteLock::~SafeDeleteLock()
+{
+ if(_sd) {
+ _sd->unlock();
+ if(own)
+ delete _sd;
+ }
+}
+
+void SafeDeleteLock::dying()
+{
+ _sd = new SafeDelete(*_sd);
+ own = true;
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLater
+//----------------------------------------------------------------------------
+SafeDeleteLater *SafeDeleteLater::self = 0;
+
+SafeDeleteLater *SafeDeleteLater::ensureExists()
+{
+ if(!self)
+ new SafeDeleteLater();
+ return self;
+}
+
+SafeDeleteLater::SafeDeleteLater()
+{
+ list.setAutoDelete(true);
+ self = this;
+ QTimer::singleShot(0, this, SLOT(explode()));
+}
+
+SafeDeleteLater::~SafeDeleteLater()
+{
+ list.clear();
+ self = 0;
+}
+
+void SafeDeleteLater::deleteItLater(QObject *o)
+{
+ list.append(o);
+}
+
+void SafeDeleteLater::explode()
+{
+ delete this;
+}
+
+#include "safedelete.moc"
+
diff --git a/kopete/protocols/oscar/liboscar/safedelete.h b/kopete/protocols/oscar/liboscar/safedelete.h
new file mode 100644
index 00000000..e8215c06
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/safedelete.h
@@ -0,0 +1,79 @@
+/*
+ gwclientstream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+#include<qobject.h>
+#include<qobjectlist.h>
+
+class SafeDelete;
+class SafeDeleteLock
+{
+public:
+ SafeDeleteLock(SafeDelete *sd);
+ ~SafeDeleteLock();
+
+private:
+ SafeDelete *_sd;
+ bool own;
+ friend class SafeDelete;
+ void dying();
+};
+
+class SafeDelete
+{
+public:
+ SafeDelete();
+ ~SafeDelete();
+
+ void deleteLater(QObject *o);
+
+ // same as QObject::deleteLater()
+ static void deleteSingle(QObject *o);
+
+private:
+ QObjectList list;
+ void deleteAll();
+
+ friend class SafeDeleteLock;
+ SafeDeleteLock *lock;
+ void unlock();
+};
+
+class SafeDeleteLater : public QObject
+{
+ Q_OBJECT
+public:
+ static SafeDeleteLater *ensureExists();
+ void deleteItLater(QObject *o);
+
+private slots:
+ void explode();
+
+private:
+ SafeDeleteLater();
+ ~SafeDeleteLater();
+
+ QObjectList list;
+ friend class SafeDelete;
+ static SafeDeleteLater *self;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.cpp b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp
new file mode 100644
index 00000000..af80e191
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/senddcinfotask.cpp
@@ -0,0 +1,107 @@
+/*
+ Kopete Oscar Protocol
+ senddcinfotask.cpp - Send the DC info to the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "senddcinfotask.h"
+
+#include <kdebug.h>
+#include "connection.h"
+#include "buffer.h"
+#include "oscarsettings.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+SendDCInfoTask::SendDCInfoTask(Task* parent, DWORD status): Task(parent), mStatus(status)
+{
+}
+
+
+SendDCInfoTask::~SendDCInfoTask()
+{
+}
+
+
+void SendDCInfoTask::onGo()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x001E, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending DC Info" << endl;
+
+ /** \TODO Support something more than online in the status flags
+ * \TODO Support something more than DC Disabled in the status flags
+ */
+ /*
+ if (status & ICQ_STATUS_SET_INVIS)
+ sendChangeVisibility(0x03);
+ else
+ sendChangeVisibility(0x04);
+ */
+
+ /* This is TLV 0x06 */
+ buffer->addWord( 0x0006 );
+ buffer->addWord( 0x0004 );
+ //### Don't hardcode this value
+ //Right now, it's always coded to not support DC
+ DWORD statusFlag = 0x01000000;
+ if ( client()->settings()->webAware() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting web aware on" << endl;
+ statusFlag |= 0x00010000;
+ }
+ if ( client()->settings()->hideIP() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting hide ip on" << endl;
+ statusFlag |= 0x10000000; // Direct connection upon authorization, hides IP
+ }
+
+ buffer->addDWord( statusFlag | mStatus );
+
+ /* Fill in the DC Info
+ * We don't support Direct Connection yet. So fill in some
+ * dummy values
+ */
+ buffer->addWord( 0x000C ); //TLV Type 0x0C
+ buffer->addWord( 0x0025 );
+
+ buffer->addDWord( 0x00000000 );
+ buffer->addWord( 0x0000 );
+ buffer->addWord( 0x0000 );
+
+ buffer->addByte( 0x00 ); // Mode, TODO: currently fixed to "Direct Connection disabled"
+ buffer->addWord( ICQ_TCP_VERSION ); // icq tcp protocol version, v8 currently
+
+ buffer->addDWord( 0x00000000 ); // Direct Connection Cookie
+ buffer->addDWord( 0x00000050 ); // web front port
+ buffer->addDWord( 0x00000003 ); // number of following client features
+ buffer->addDWord( 0x00000000 ); // InfoUpdateTime
+ buffer->addDWord( 0x00000000 ); // PhoneStatusTime
+ buffer->addDWord( 0x00000000 ); // PhoneBookTime
+ buffer->addWord( 0x0000 );
+
+ buffer->addWord( 0x0008 ); // TLV(8)
+ buffer->addWord( 0x0002 ); // length 2
+ buffer->addWord( 0x0000 ); // error code - 0
+
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+ setSuccess( 0, QString::null );
+}
+
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/senddcinfotask.h b/kopete/protocols/oscar/liboscar/senddcinfotask.h
new file mode 100644
index 00000000..d130cc40
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/senddcinfotask.h
@@ -0,0 +1,41 @@
+/*
+ Kopete Oscar Protocol
+ $FILENAME.h
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SENDDCINFOTASK_H
+#define SENDDCINFOTASK_H
+
+#include <task.h>
+
+/**
+Fire and forget task that sends our direct connection info
+
+@author Matt Rogers
+*/
+class SendDCInfoTask : public Task
+{
+public:
+ SendDCInfoTask( Task* parent, DWORD status );
+ ~SendDCInfoTask();
+ virtual void onGo();
+
+private:
+ DWORD mStatus;
+};
+
+#endif
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.cpp b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp
new file mode 100644
index 00000000..f0601e22
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/sendidletimetask.cpp
@@ -0,0 +1,57 @@
+/*
+ Kopete Oscar Protocol
+ sendidletimetask.cpp - Sends the idle time to the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "sendidletimetask.h"
+
+#include <kdebug.h>
+#include "connection.h"
+#include "buffer.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+SendIdleTimeTask::SendIdleTimeTask( Task* parent ) : Task( parent )
+{
+ m_idleTime = 0;
+}
+
+
+SendIdleTimeTask::~SendIdleTimeTask()
+{
+
+}
+
+
+void SendIdleTimeTask::onGo()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending idle time of " << m_idleTime << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x0011, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ buffer->addDWord( m_idleTime );
+
+ Transfer *t = createTransfer( f, s, buffer );
+ send( t );
+ setSuccess( 0, QString::null );
+
+}
+
+void SendIdleTimeTask::setIdleTime( DWORD newTime )
+{
+ m_idleTime = newTime;
+}
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/sendidletimetask.h b/kopete/protocols/oscar/liboscar/sendidletimetask.h
new file mode 100644
index 00000000..beba74c2
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/sendidletimetask.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Oscar Protocol
+ sendidletimetask.h - Send the idle time to the server
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SENDIDLETIMETASK_H
+#define SENDIDLETIMETASK_H
+
+#include "task.h"
+#include "oscartypes.h"
+
+/**
+Sends the idle time to the server
+
+@author Matt Rogers
+*/
+class SendIdleTimeTask : public Task
+{
+public:
+ SendIdleTimeTask( Task* parent );
+ ~SendIdleTimeTask();
+ virtual void onGo();
+
+ //! Set the idle time to send
+ void setIdleTime( DWORD );
+
+private:
+
+ DWORD m_idleTime;
+};
+
+#endif
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.cpp b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp
new file mode 100644
index 00000000..48509595
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/sendmessagetask.cpp
@@ -0,0 +1,447 @@
+/*
+ sendmessagetask.h - Outgoing OSCAR Messaging Handler
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "sendmessagetask.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include "connection.h"
+#include "oscartypes.h"
+#include "transfer.h"
+
+
+SendMessageTask::SendMessageTask(Task* parent): Task(parent)
+{
+ m_autoResponse = false;
+ m_cookieCount = 0x7FFF;
+}
+
+
+SendMessageTask::~SendMessageTask()
+{
+}
+
+void SendMessageTask::setMessage( const Oscar::Message& msg )
+{
+ m_message = msg;
+}
+
+void SendMessageTask::setAutoResponse( bool autoResponse )
+{
+ m_autoResponse = autoResponse;
+}
+
+void SendMessageTask::onGo()
+{
+ if ( m_message.textArray().isEmpty() && m_message.type() == 1 ) // at least channel 2 needs to send empty messages
+ {
+ setError(-1, "No message to send");
+ return;
+ }
+
+ // Check Message to see what SNAC to use
+ int snacSubfamily = 0x0006;
+ if ( ( m_message.type() == 2 ) && m_message.hasProperty( Oscar::Message::AutoResponse ) )
+ { // an auto response is send for ack of channel 2 messages
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending SNAC 0x0B instead of 0x06 " << endl;
+ snacSubfamily = 0x000B;
+ }
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0004, snacSubfamily, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer();
+
+ if ( snacSubfamily == 0x0006 )
+ {
+ DWORD cookie1 = KApplication::random();
+ DWORD cookie2 = KApplication::random();
+
+ b->addDWord( cookie1 );
+ b->addDWord( cookie2 );
+ }
+ else
+ {
+ b->addString( m_message.icbmCookie() ); // in automated response, we need the same cookie as in the request
+ }
+
+ b->addWord( m_message.type() );
+
+ b->addByte( m_message.receiver().length() );
+ b->addString( m_message.receiver().latin1(), m_message.receiver().length() );
+
+
+ if ( snacSubfamily == 0x0006 )
+ {
+ /* send a regular message */
+ switch ( m_message.type() )
+ {
+ case 1:
+ addChannel1Data( b );
+ break;
+ case 2:
+ addChannel2Data( b );
+ break;
+ case 4:
+ addChannel4Data( b );
+ break;
+ }
+
+ // Add the TLV to indicate if this is an autoresponse: 0x00040000
+ // Right now, only supported for the AIM client, I'm not sure about ICQ
+ // For some reason you can't have both a 0x0004 and 0x0003 TLV in the same
+ // SNAC, if you do the AIM server complains
+ if ( !client()->isIcq() && (m_autoResponse == true) )
+ {
+ TLV tlv4( 0x0004, 0, NULL);
+ b->addTLV( tlv4 );
+ }
+ else
+ {
+ b->addDWord( 0x00030000 ); //empty TLV 3 to get an ack from the server
+ }
+
+ if ( client()->isIcq() && m_message.type() != 2 && ! m_message.hasProperty( Oscar::Message::StatusMessageRequest ) )
+ b->addDWord( 0x00060000 ); //empty TLV 6 to store message on the server if not online
+ }
+ else
+ {
+ /* send an autoresponse */
+ b->addWord( 0x0003 ); // reason code: 1: channel not supported; 2: busted payload; 3: channel specific;
+ //TODO: i hardcoded it for now, since we don't suppoert error messages atm anyway
+ addRendezvousMessageData( b );
+ }
+
+ Transfer* t = createTransfer( f, s, b );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SENDING: " << t->toString() << endl;
+ send( t );
+
+ setSuccess(true);
+}
+
+
+void SendMessageTask::addBasicTLVs( Buffer* b )
+{
+ //TODO add stuff like user class, user status, online time, etc TLVS
+}
+
+
+void SendMessageTask::addChannel1Data( Buffer* b )
+{
+ Buffer tlv2buffer;
+
+ //Send features TLV using data from gaim. Features are different
+ //depending on whether we're ICQ or AIM
+ if ( client()->isIcq() )
+ {
+ tlv2buffer.addDWord( 0x05010002 ); //TLV 0x0501, length 2
+ tlv2buffer.addWord( 0x0106 ); //TLV 0x0501 data
+ }
+ else
+ {
+ tlv2buffer.addDWord( 0x05010004 ); //TLV 0x0501, length 4
+ tlv2buffer.addDWord( 0x01010102 ); //TLV 0x0501 data.
+ }
+ //we only send one message part. There's only one client that actually uses
+ //them and it's quite old and infrequently used
+ tlv2buffer.addWord( 0x0101 ); //add TLV(0x0101) also known as TLV(257)
+ tlv2buffer.addWord( m_message.textArray().size() + 4 ); // add TLV length
+
+ if ( m_message.encoding() == Oscar::Message::UserDefined )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message in "
+ << "per-contact encoding" << endl;
+ tlv2buffer.addWord( 0x0000 );
+ tlv2buffer.addWord( 0x0000 );
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Sending outgoing message as "
+ << "UCS-2" << endl;
+ tlv2buffer.addWord( 0x0002 );
+ tlv2buffer.addWord( 0x0000 );
+ }
+ tlv2buffer.addString( m_message.textArray() );
+
+ TLV tlv2( 0x0002, tlv2buffer.length(), tlv2buffer.buffer() );
+ b->addTLV( tlv2 );
+}
+
+void SendMessageTask::addChannel2Data( Buffer* b )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Trying to send type 2 message!" << endl;
+
+ Buffer tlv5buffer;
+
+ tlv5buffer.addWord( 0 ); // 0 = request; other possibilities: 1 = cancel; 2 = accept;
+ //TODO: i hardcoded it for now, don't yet what to use the other stuff for
+
+ // message id cookie. needs to be the same one as above, thus copy first eight bytes of buffer
+ Buffer* tmp = new Buffer(b->buffer(), 8);
+ tlv5buffer.addString( tmp->buffer(), 8 );
+ delete tmp;
+
+ /* send our client capability. oscardocs say this one means we support type 2 messages,
+ ethereal say it means we support server relay. however, it's what most clients send,
+ even official ones...
+ */
+
+ // too lazy to think about byte order :)
+ tlv5buffer.addByte( 0x09 );
+ tlv5buffer.addByte( 0x46 );
+ tlv5buffer.addByte( 0x13 );
+ tlv5buffer.addByte( 0x49 );
+ tlv5buffer.addByte( 0x4C );
+ tlv5buffer.addByte( 0x7F );
+ tlv5buffer.addByte( 0x11 );
+ tlv5buffer.addByte( 0xD1 );
+ tlv5buffer.addByte( 0x82 );
+ tlv5buffer.addByte( 0x22 );
+ tlv5buffer.addByte( 0x44 );
+ tlv5buffer.addByte( 0x45 );
+ tlv5buffer.addByte( 0x53 );
+ tlv5buffer.addByte( 0x54 );
+ tlv5buffer.addByte( 0x00 );
+ tlv5buffer.addByte( 0x00 );
+
+ // These are optional, would probably be a god ide to start using them, though
+
+ // add TLV 03: internal ip
+// tlv5buffer.addWord( 0x0003 ); // TLV Type
+// tlv5buffer.addWord( 0x0004 ); // TLV Length
+// tlv5buffer.addDWord( 0x00000000 ); // TLV Data: Internal IP
+
+ // add TLV 05: listening port
+// tlv5buffer.addWord( 0x0005 ); // TLV Type
+// tlv5buffer.addWord( 0x0002 ); // TLV Length
+// tlv5buffer.addWord( 0x0000 ); // TLV Data: listening port
+
+ // add TLV 0A: acktype (1 = normal message)
+ tlv5buffer.addWord( 0x000A ); // TLV Type
+ tlv5buffer.addWord( 0x0002 ); // TLV Length
+ tlv5buffer.addWord( 0x0001 ); // TLV Data: unknown, usually 1
+
+ // add TLV 0B: unknown
+// tlv5buffer.addWord( 0x000B ); // TLV Type
+// tlv5buffer.addWord( 0x0002 ); // TLV Length
+// tlv5buffer.addWord( 0x0000 ); // TLV Data: unknown
+
+ // add TLV 0F: unknown
+ tlv5buffer.addWord( 0x000F ); // TLV Type
+ tlv5buffer.addWord( 0x0000 ); // TLV Length
+ // TLV Data: empty
+
+
+
+ /* now comes the important TLV 0x2711 */
+
+ Buffer tlv2711buffer;
+ addRendezvousMessageData( &tlv2711buffer );
+ TLV tlv2711( 0x2711, tlv2711buffer.length(), tlv2711buffer.buffer() );
+ tlv5buffer.addTLV( tlv2711 );
+
+ TLV tlv5( 0x0005, tlv5buffer.length(), tlv5buffer.buffer() );
+ b->addTLV( tlv5 );
+}
+
+void SendMessageTask::addChannel4Data( Buffer* b )
+{
+ //TODO
+}
+
+void SendMessageTask::addRendezvousMessageData( Buffer* b )
+{
+ // first data segment
+ b->addLEWord( 0x001B ); // length of this data segment, always 27
+
+ // protocol version
+ // miranda,licq use 8, gaim,icq5 use 9, icq2003b uses 10.
+ // 9 seems to make things a litle difficult, 10 seems a little more like 8, but still more difficult
+ b->addLEWord( 0x0008 ); // so stick with 8 for now :)
+
+ for ( int i = 0; i < 16; i++)
+ {
+ b->addByte( 0x00 ); // pluginID or all zeros (see oscar docs)
+ }
+
+ b->addWord( 0x0000 ); // unknown
+ b->addLEDWord( 0x00000003 ); // FIXME client capabilities: not sure, but should be ICQ Server Relay
+ b->addByte( 0x0000 ); // unknown
+
+ // channel 2 counter: in auto response, use original message value. s/t else otherwise (most anythig will work)
+ int channel2Counter = 0xBEEF; // just some number for now
+ if ( m_message.hasProperty( Oscar::Message::AutoResponse ) )
+ channel2Counter = m_message.channel2Counter();
+ else
+ channel2Counter = (m_cookieCount--) & 0x7FFF;
+
+ b->addLEWord( channel2Counter ); // channel 2 counter
+
+ // second data segment
+ b->addLEWord( 0x000E ); // length of this data segment, always 14
+ b->addLEWord( channel2Counter ); // channel 2 counter
+
+ for ( int i = 0; i < 12; i++)
+ {
+ b->addByte( 0x00 ); // unknown, usually all zeros
+ }
+
+ // actual message data segment
+
+ // Message type
+ if ( m_message.messageType() == 0x00 )
+ b->addByte( 0x01 );
+ else
+ b->addByte( m_message.messageType() );
+
+ int messageFlags = 0x00; // Normal
+ if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) )
+ messageFlags = 0x03; // Auto message. required for both requesting and sending status messages
+ else if ( m_message.hasProperty( Oscar::Message::AutoResponse ) )
+ messageFlags = 0x00; // A regular type 2 msg ack requires 0x00 here...
+ b->addByte( messageFlags );
+
+ // status code, priority:
+ // common (ICQ) practice seems to be: both 1 when requesting away message, both 0 otherwise
+ // miranda sends 256/0 in away message request. it works, but i don't see the point...
+ // other then that, don't yet really know what they are for.
+ if ( m_message.hasProperty( Oscar::Message::StatusMessageRequest ) && ( ! m_message.hasProperty( Oscar::Message::AutoResponse ) ) )
+ {
+ b->addLEWord( 0x0001 ); // status (?)
+ b->addLEWord( 0x0001 ); // priority (?)
+ }
+ else
+ {
+ b->addLEWord( 0x0000 ); // status (?)
+ b->addLEWord( 0x0000 ); // priority (?)
+ }
+
+
+ b->addLEWord( m_message.textArray().size() + 1 ); // length of string + zero termination
+ b->addString( m_message.textArray() ); // string itself
+ b->addByte( 0x00 ); // zero termination
+ b->addLEDWord( 0x00000000 ); // foreground
+ b->addLEDWord( 0x00FFFFFF ); // foreground
+
+ if ( m_message.encoding() == Oscar::Message::UTF8 )
+ {
+ b->addLEDWord( 38 );
+ b->addString( "{0946134E-4C7F-11D1-8222-444553540000}", 38 );
+ }
+}
+
+
+
+/* Old oscarsocket code, which is here for reference in case this doesn't work
+QTextCodec *codec = 0L;
+WORD charset = 0x0000; // default to ascii
+WORD charsubset = 0x0000;
+int length = message.length();
+unsigned char *utfMessage = 0L;
+
+codec=QTextCodec::codecForMib(3); // US-ASCII
+
+if(codec)
+{
+ if(codec->canEncode(message)) // this returns true for some accented western european chars but kopete can't decode on receipt
+ {
+ //kdDebug(14151) << k_funcinfo << "Going to encode as US-ASCII" << endl;
+ // We are forcing kopete to send messages using ISO-8859-1
+ // It's a hack and should be reimplemented in a better way
+ charset=0x0003;
+ codec=QTextCodec::codecForMib(4);
+ //kdDebug(14151) << k_funcinfo << "Now trying ISO-8859-1" << endl;
+ }
+ else
+ {
+ codec=0L; // we failed encoding it as US-ASCII
+ //kdDebug(14151) << k_funcinfo << "Cannot encode as US-ASCII" << endl;
+ }
+}
+
+// if we couldn't encode it as ascii, and either the client says it can do UTF8, or we have no
+// contact specific encoding set, might as well send it as UTF-16BE as as ISO-8859-1
+if ( !codec && ( contact->hasCap(CAP_UTF8) || !contact->encoding() ) )
+{
+ // use UTF is peer supports it and encoding as US-ASCII failed
+ length=message.length()*2;
+ utfMessage=new unsigned char[length];
+ for(unsigned int l=0; l<message.length(); l++)
+ {
+ utfMessage[l*2]=message.unicode()[l].row();
+ utfMessage[(l*2)+1]=message.unicode()[l].cell();
+ }
+ charset=0x0002; // send UTF-16BE
+}
+
+// no codec and no charset and per-contact encoding set
+if(!codec && charset != 0x0002 && contact->encoding() != 0)
+{
+ codec=QTextCodec::codecForMib(contact->encoding());
+ if(codec)
+ charset=0x0003; //send as ISO-8859-1
+}
+
+if(!codec && charset != 0x0002) // it's neither unicode nor did we find a codec so far!
+{
+ kdDebug(14151) << k_funcinfo <<
+ "Couldn't find suitable encoding for outgoing message, " <<
+ "encoding using ISO-8859-1, prepare for receiver getting unreadable text :)" << endl;
+ charset=0x0003;
+ codec=QTextCodec::codecForMib(4); // ISO-8859-1
+}
+
+tlv2.addWord(0x0101); //add TLV(0x0101) also known as TLV(257)
+tlv2.addWord(length + 0x04); // add TLV length
+tlv2.addWord(charset); // normal char set
+tlv2.addWord(charsubset); // normal char set
+
+if(utfMessage)
+{
+ kdDebug(14151) << k_funcinfo << "Outgoing message encoded as 'UTF-16BE'" << endl;
+ tlv2.addString(utfMessage, length); // the actual message
+ delete [] utfMessage;
+}
+else
+{
+ kdDebug(14151) << k_funcinfo << "Outgoing message encoded as '" << codec->name() << "'" << endl;
+ QCString outgoingMessage=codec->fromUnicode(message);
+ tlv2.addString(outgoingMessage, length); // the actual message
+}
+// ====================================================================================
+
+outbuf.addTLV(0x0002, tlv2.length(), tlv2.buffer());
+
+if(isAuto) // No clue about this stuff, probably AIM-specific [mETz]
+{
+ outbuf.addWord(0x0004);
+ outbuf.addWord(0x0000);
+}
+
+if(mIsICQ)
+{
+ //outbuf.addWord(0x0003); // TLV.Type(0x03) - request an ack from server
+ //outbuf.addWord(0x0000);
+
+ outbuf.addWord(0x0006); // TLV.Type(0x06) - store message if recipient offline
+ outbuf.addWord(0x0000);
+}
+
+sendBuf(outbuf,0x02);
+*/
+
+
+
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/sendmessagetask.h b/kopete/protocols/oscar/liboscar/sendmessagetask.h
new file mode 100644
index 00000000..0eaff13f
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/sendmessagetask.h
@@ -0,0 +1,55 @@
+/*
+ sendmessagetask.h - Outgoing OSCAR Messaging Handler
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SENDMESSAGETASK_H
+#define SENDMESSAGETASK_H
+
+#include "task.h"
+#include <qstring.h>
+#include "oscarmessage.h"
+/**
+@author Kopete Developers
+*/
+class SendMessageTask : public Task
+{
+public:
+ SendMessageTask( Task* parent );
+ ~SendMessageTask();
+
+ //! Set the message to be sent
+ void setMessage( const Oscar::Message& msg );
+
+ //! Are we sending an auto response
+ void setAutoResponse( bool autoResponse );
+
+ virtual void onGo();
+
+private:
+ void addBasicTLVs( Buffer* b );
+ void addChannel1Data( Buffer* b );
+ void addChannel2Data( Buffer* b );
+ void addChannel4Data( Buffer* b );
+ void addRendezvousMessageData( Buffer* b );
+
+private:
+ Oscar::Message m_message;
+ bool m_autoResponse;
+ uint m_cookieCount;
+};
+
+#endif
+
+//kate: indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.cpp b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp
new file mode 100644
index 00000000..cccad909
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/serverredirecttask.cpp
@@ -0,0 +1,173 @@
+// Kopete Oscar Protocol - Server redirections
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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 "serverredirecttask.h"
+
+#include <kdebug.h>
+
+#include "buffer.h"
+#include "connection.h"
+#include "transfer.h"
+
+ServerRedirectTask::ServerRedirectTask( Task* parent )
+ :Task( parent ), m_service( 0 )
+{
+
+}
+
+void ServerRedirectTask::setService( WORD family )
+{
+ m_service = family;
+}
+
+void ServerRedirectTask::setChatParams( WORD exchange, QByteArray cookie, WORD instance )
+{
+ m_chatExchange = exchange;
+ m_chatCookie.duplicate( cookie );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "cookie is" << m_chatCookie << endl;
+ m_chatInstance = instance;
+}
+
+void ServerRedirectTask::setChatRoom( const QString& roomName )
+{
+ m_chatRoom = roomName;
+}
+
+
+void ServerRedirectTask::onGo()
+{
+ if ( m_service != 0 )
+ requestNewService();
+}
+
+bool ServerRedirectTask::forMe( const Transfer* transfer )
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 1 && st->snacSubtype() == 0x0005 )
+ return true;
+ else
+ return false;
+}
+
+bool ServerRedirectTask::take( Transfer* transfer )
+{
+ if ( !forMe( transfer ) )
+ return false;
+
+ setTransfer( transfer );
+ bool value = handleRedirect();
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return value;
+}
+
+
+void ServerRedirectTask::requestNewService()
+{
+ FLAP f = { 0x02, 0, 0x00 };
+ SNAC s = { 0x0001, 0x0004, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer();
+ b->addWord( m_service );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Requesting server for service " << m_service << endl;
+ if ( m_service == 0x000E )
+ {
+ b->addWord( 0x0001 );
+ b->addWord( m_chatCookie.size() + 5 );
+ b->addWord( m_chatExchange );
+ b->addByte( m_chatCookie.size() );
+ b->addString( m_chatCookie );
+ b->addWord( m_chatInstance );
+ }
+
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+bool ServerRedirectTask::handleRedirect()
+{
+ //TLVs 0x0D, 0x05, 0x06
+ //family id
+ //server
+ //auth cookie
+ Buffer* b = transfer()->buffer();
+ WORD typeD = b->getWord();
+ WORD typeDLen = b->getWord();
+ if ( typeD == 0x000D && typeDLen == 0x0002)
+ {
+ WORD realService = b->getWord();
+ if ( realService != m_service )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "wrong service for this task" << endl;
+ kdDebug(OSCAR_RAW_DEBUG ) << k_funcinfo << "should be " << m_service << " is "
+ << realService << endl;
+ return false;
+ }
+ }
+ else
+ return false;
+
+ TLV server = b->getTLV();
+ m_newHost = QString( server.data );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Host for service " << m_service
+ << " is " << m_newHost << endl;
+ if ( m_newHost.isEmpty() )
+ return false;
+
+ TLV cookie = b->getTLV();
+
+ if ( cookie.length == 0 || cookie.data.isEmpty() )
+ return false;
+ else
+ m_cookie = cookie.data;
+
+ emit haveServer( m_newHost, m_cookie, m_service );
+ return true;
+}
+
+QByteArray ServerRedirectTask::cookie() const
+{
+ return m_cookie;
+}
+
+QString ServerRedirectTask::newHost() const
+{
+ return m_newHost;
+}
+
+WORD ServerRedirectTask::service() const
+{
+ return m_service;
+}
+
+WORD ServerRedirectTask::chatExchange() const
+{
+ return m_chatExchange;
+}
+
+QString ServerRedirectTask::chatRoomName() const
+{
+ return m_chatRoom;
+}
+
+#include "serverredirecttask.moc"
+//kate: indent-mode csands; tab-width 4;
+
diff --git a/kopete/protocols/oscar/liboscar/serverredirecttask.h b/kopete/protocols/oscar/liboscar/serverredirecttask.h
new file mode 100644
index 00000000..19f14073
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/serverredirecttask.h
@@ -0,0 +1,72 @@
+// Kopete Oscar Protocol - Server redirections
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.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 SERVERREDIRECTTASK_H
+#define SERVERREDIRECTTASK_H
+
+#include "task.h"
+
+#include <qcstring.h>
+
+#include "oscartypes.h"
+
+class Transfer;
+
+class ServerRedirectTask : public Task
+{
+Q_OBJECT
+public:
+ ServerRedirectTask( Task* parent );
+
+ void setService( WORD family );
+ void setChatParams( WORD exchange, QByteArray cookie, WORD instance );
+ void setChatRoom( const QString& roomName );
+
+ WORD chatExchange() const;
+ QString chatRoomName() const;
+
+ //Task implementation
+ void onGo();
+ bool forMe( const Transfer* transfer );
+ bool take( Transfer* transfer );
+
+ void requestNewService();
+ bool handleRedirect();
+
+ QByteArray cookie() const;
+ QString newHost() const;
+ WORD service() const;
+
+signals:
+ void haveServer( const QString&, const QByteArray&, WORD );
+
+private:
+ WORD m_service;
+ QString m_newHost;
+ QByteArray m_cookie;
+
+ WORD m_chatExchange;
+ QByteArray m_chatCookie;
+ WORD m_chatInstance;
+ QString m_chatRoom;
+};
+
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.cpp b/kopete/protocols/oscar/liboscar/serverversionstask.cpp
new file mode 100644
index 00000000..e4186f18
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/serverversionstask.cpp
@@ -0,0 +1,169 @@
+/*
+ Kopete Oscar Protocol
+ serverversionstask.cpp - Handles the snac family versions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "serverversionstask.h"
+
+#include <kdebug.h>
+
+#include "connection.h"
+#include "buffer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+
+using namespace Oscar;
+
+ServerVersionsTask::ServerVersionsTask( Task* parent )
+ : Task( parent )
+{
+ m_family = 0;
+}
+
+
+ServerVersionsTask::~ServerVersionsTask()
+{
+}
+
+
+bool ServerVersionsTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( transfer );
+
+ if (!st)
+ return false;
+
+ if ( st->snacService() == 1 )
+ {
+ switch ( st->snacSubtype() )
+ {
+ case 0x03:
+ case 0x17:
+ case 0x18:
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool ServerVersionsTask::take( Transfer* transfer )
+{
+ SnacTransfer* st = dynamic_cast<SnacTransfer*> ( transfer );
+ if (!st)
+ return false;
+
+ if ( forMe( transfer ) )
+ {
+ switch ( st->snacSubtype() )
+ {
+ case 0x03:
+ setTransfer( transfer );
+ handleFamilies();
+ setTransfer( 0 );
+ return true;
+ break;
+ case 0x18:
+ setTransfer( transfer );
+ handleServerVersions();
+ setTransfer( 0 );
+ return true;
+ break;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+void ServerVersionsTask::handleFamilies()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo
+ << "RECV SNAC 0x01, 0x03 - got the list of families server supports" << endl;
+
+ Buffer* outbuf = transfer()->buffer();
+ if ( outbuf->length() % 2 != 0 )
+ {
+ setError( -1, QString::null );
+ return;
+ }
+
+ while ( outbuf->length () != 0 )
+ {
+ m_familiesList.append( outbuf->getWord() );
+ }
+ client()->addToSupportedFamilies( m_familiesList );
+ requestFamilyVersions(); // send back a CLI_FAMILIES packet
+}
+
+void ServerVersionsTask::requestFamilyVersions()
+{
+ bool isIcq = client()->isIcq();
+ int listLength = m_familiesList.count();
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0001, 0x0017, 0x0000, client()->snacSequence() };
+ WORD val;
+ Buffer* outbuf = new Buffer();
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SEND SNAC 0x01, 0x17 - Snac family versions we want" << endl;
+
+ for ( int i = 0; i < listLength; i++ )
+ {
+ outbuf->addWord( m_familiesList[i] );
+ if ( m_familiesList[i] == 0x0001 )
+ val = 0x0003;
+ else
+ {
+ if ( m_familiesList[i] == 0x0013 )
+ {
+ if ( isIcq )
+ val = 0x0004; // for ICQ2002
+ else
+ val = 0x0003;
+ }
+ else
+ val = 0x0001;
+ }
+
+ outbuf->addWord(val);
+ }
+
+ Transfer* st = createTransfer( f, s, outbuf );
+ st->toString();
+ send( st );
+}
+
+void ServerVersionsTask::handleServerVersions()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo <<
+ "RECV SNAC 0x01, 0x18, got list of families this server understands" << endl;
+
+ Buffer* buffer = transfer()->buffer();
+ int numFamilies = m_familiesList.count();
+ for ( int srvFamCount = 0; srvFamCount < numFamilies; srvFamCount++ )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "server version=" << buffer->getWord()
+ << ", server family=" << buffer->getWord() << endl;
+ }
+ setSuccess( 0, QString::null );
+}
+
+#include "serverversionstask.moc"
diff --git a/kopete/protocols/oscar/liboscar/serverversionstask.h b/kopete/protocols/oscar/liboscar/serverversionstask.h
new file mode 100644
index 00000000..a9c56f35
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/serverversionstask.h
@@ -0,0 +1,59 @@
+/*
+ Kopete Oscar Protocol
+ serverversionstask.h - Handles the snac family versions
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SERVERVERSIONSTASK_H
+#define SERVERVERSIONSTASK_H
+
+#include "task.h"
+#include <qvaluelist.h>
+#include "oscartypes.h"
+
+class Transfer;
+
+/**
+@author Matt Rogers
+*/
+class ServerVersionsTask : public Task
+{
+Q_OBJECT
+public:
+ ServerVersionsTask( Task* parent );
+
+ ~ServerVersionsTask();
+
+ bool forMe(const Transfer* transfer) const;
+ bool take(Transfer* transfer);
+
+
+private:
+ //! Handles the families the server supports
+ void handleFamilies();
+
+ //! Handles the version of each family the server supports
+ void handleServerVersions();
+
+ //! Request the versions we want for each snac family the
+ //! the server supports
+ void requestFamilyVersions();
+
+private:
+ QValueList<int> m_familiesList;
+ WORD m_family;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.cpp b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp
new file mode 100644
index 00000000..13e30101
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/servicesetuptask.cpp
@@ -0,0 +1,135 @@
+/*
+ Kopete Oscar Protocol
+ servicesetuptask.cpp - Set up the services for the BOS connection
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "servicesetuptask.h"
+
+#include <kdebug.h>
+#include "blmlimitstask.h"
+#include "connection.h"
+#include "clientreadytask.h"
+#include "icbmparamstask.h"
+#include "locationrightstask.h"
+#include "ownuserinfotask.h"
+#include "prmparamstask.h"
+#include "profiletask.h"
+#include "senddcinfotask.h"
+#include "sendidletimetask.h"
+#include "ssiactivatetask.h"
+#include "ssilisttask.h"
+#include "ssimanager.h"
+#include "ssiparamstask.h"
+#include "transfer.h"
+
+ServiceSetupTask::ServiceSetupTask( Task* parent )
+ : Task( parent )
+{
+ m_finishedTaskCount = 0;
+ m_locRightsTask = new LocationRightsTask( parent );
+ m_profileTask = new ProfileTask( parent );
+ m_blmLimitsTask = new BLMLimitsTask( parent );
+ m_icbmTask = new ICBMParamsTask( parent );
+ m_prmTask = new PRMParamsTask( parent );
+ m_ssiParamTask = new SSIParamsTask( parent );
+ m_ssiListTask = new SSIListTask( parent );
+ m_ssiActivateTask = new SSIActivateTask( parent );
+
+ QObject::connect( m_ssiListTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_ssiParamTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_prmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_icbmTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_blmLimitsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_profileTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_locRightsTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+ QObject::connect( m_ssiActivateTask, SIGNAL( finished() ), this, SLOT( childTaskFinished() ) );
+}
+
+
+ServiceSetupTask::~ServiceSetupTask()
+{
+ delete m_locRightsTask;
+ delete m_profileTask;
+ delete m_blmLimitsTask;
+ delete m_icbmTask;
+ //delete m_prmTask;
+ //delete m_ssiParamTask;
+ delete m_ssiListTask;
+}
+
+
+bool ServiceSetupTask::forMe( const Transfer* transfer ) const
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+bool ServiceSetupTask::take( Transfer* transfer )
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+void ServiceSetupTask::childTaskFinished()
+{
+ m_finishedTaskCount++;
+
+// kdDebug( OSCAR_RAW_DEBUG ) << "Finished count is " << m_finishedTaskCount << endl;
+
+ if ( m_finishedTaskCount == 7 )
+ {
+ if ( client()->ssiManager()->listComplete() )
+ m_ssiActivateTask->go( true );
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending DC info and client ready" << endl;
+ SendIdleTimeTask* sitt = new SendIdleTimeTask( client()->rootTask() );
+ QValueList<int> familyList;
+ familyList.append( 0x0001 );
+ familyList.append( 0x0002 );
+ familyList.append( 0x0003 );
+ familyList.append( 0x0004 );
+ familyList.append( 0x0006 );
+ familyList.append( 0x0008 );
+ familyList.append( 0x0009 );
+ familyList.append( 0x000A );
+ familyList.append( 0x0013 );
+ ClientReadyTask* crt = new ClientReadyTask( client()->rootTask() );
+ crt->setFamilies( familyList );
+ sitt->go( true );
+ crt->go( true ); //autodelete
+ }
+
+ if ( m_finishedTaskCount == 8 )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Service setup finished" << endl;
+ setSuccess( 0, QString::null );
+ }
+}
+
+
+void ServiceSetupTask::onGo()
+{
+ m_locRightsTask->go();
+ m_profileTask->go();
+ m_blmLimitsTask->go();
+ m_icbmTask->go();
+ m_prmTask->go( true );
+ m_ssiParamTask->go( true );
+ m_ssiListTask->go();
+}
+
+//kate: tab-width 4; indent-mode csands;
+
+#include "servicesetuptask.moc"
diff --git a/kopete/protocols/oscar/liboscar/servicesetuptask.h b/kopete/protocols/oscar/liboscar/servicesetuptask.h
new file mode 100644
index 00000000..c5bf5a70
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/servicesetuptask.h
@@ -0,0 +1,69 @@
+/*
+ Kopete Oscar Protocol
+ servicesetuptask.h - Set up the services for the BOS connection
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SERVICESETUPTASK_H
+#define SERVICESETUPTASK_H
+
+#include "task.h"
+
+class LocationRightsTask;
+class ProfileTask;
+class BLMLimitsTask;
+class ICBMParamsTask;
+class PRMParamsTask;
+class SSIParamsTask;
+class SSIListTask;
+class SSIActivateTask;
+
+
+/**
+Set up the various services for the BOS connection
+@author Matt Rogers
+*/
+class ServiceSetupTask : public Task
+{
+Q_OBJECT
+public:
+ ServiceSetupTask( Task* parent );
+ ~ServiceSetupTask();
+
+ bool forMe( const Transfer* transfer ) const;
+ bool take( Transfer* transfer );
+ void onGo();
+
+public slots:
+ void childTaskFinished();
+
+private:
+
+ /** Tracks how many tasks have finished */
+ int m_finishedTaskCount;
+
+ LocationRightsTask* m_locRightsTask;
+ ProfileTask* m_profileTask;
+ BLMLimitsTask* m_blmLimitsTask;
+ ICBMParamsTask* m_icbmTask;
+ PRMParamsTask* m_prmTask;
+ SSIParamsTask* m_ssiParamTask;
+ SSIListTask* m_ssiListTask;
+ SSIActivateTask* m_ssiActivateTask;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.cpp b/kopete/protocols/oscar/liboscar/snacprotocol.cpp
new file mode 100644
index 00000000..3bf16bbc
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/snacprotocol.cpp
@@ -0,0 +1,109 @@
+/*
+ Kopete Oscar Protocol
+ snacprotocol.cpp - reads the protocol used by Oscar for signalling stuff
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "snacprotocol.h"
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qobject.h>
+#include <kdebug.h>
+#include <stdlib.h>
+#include "transfer.h"
+
+
+using namespace Oscar;
+
+SnacProtocol::SnacProtocol(QObject *parent, const char *name)
+ : InputProtocolBase(parent, name)
+{
+}
+
+SnacProtocol::~SnacProtocol()
+{
+}
+
+Transfer* SnacProtocol::parse( const QByteArray & packet, uint& bytes )
+{
+ BYTE b;
+ WORD w;
+ DWORD dw;
+
+ FLAP f;
+ SNAC s;
+ SnacTransfer *st;
+ QDataStream* m_din = new QDataStream( packet, IO_ReadOnly );
+
+ //flap parsing
+ *m_din >> b; //this should be the start byte
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "start byte is " << b << endl;
+ *m_din >> b;
+ f.channel = b;
+ *m_din >> w;
+ f.sequence = w;
+ *m_din >> w;
+ f.length = w;
+
+ if ( ( f.length + 6 ) > packet.size() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Packet not big enough to parse!" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "packet size is " << packet.size()
+ << " we need " << f.length + 6 << endl;
+ return 0;
+ }
+ //snac parsing
+ *m_din >> w;
+ s.family = w;
+ *m_din >> w;
+ s.subtype = w;
+ *m_din >> w;
+ s.flags = w;
+ *m_din >> dw;
+ s.id = dw;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "family: " << s.family
+ << " subtype: " << s.subtype << " flags: " << s.flags
+ << " id: " << s.id << endl;
+
+ //use pointer arithmatic to skip the flap and snac headers
+ //so we don't have to do double parsing in the tasks
+ char* charPacket = packet.data();
+ char* snacData;
+ int snacOffset = 10; //default
+ if ( s.flags >= 0x8000 ) //skip the next 8 bytes, we don't care about the snac version ATM
+ {
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "skipping snac version" << endl;
+ snacOffset = 18;
+ snacData = charPacket + 24;
+ }
+ else
+ {
+ snacOffset = 10;
+ snacData = charPacket + 16;
+ }
+
+ Buffer *snacBuffer = new Buffer( snacData, f.length - snacOffset );
+ st = new SnacTransfer( f, s, snacBuffer );
+ bytes = f.length + 6;
+ delete m_din;
+ m_din = 0;
+ return st;
+}
+
+
+#include "snacprotocol.moc"
diff --git a/kopete/protocols/oscar/liboscar/snacprotocol.h b/kopete/protocols/oscar/liboscar/snacprotocol.h
new file mode 100644
index 00000000..eea5c032
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/snacprotocol.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Oscar Protocol
+ snacprotocol.h - reads the protocol used by Oscar for signalling stuff
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCAR_SNACPROTOCOL_H
+#define OSCAR_SNACPROTOCOL_H
+
+#include "inputprotocolbase.h"
+
+class SnacTransfer;
+
+
+class SnacProtocol : public InputProtocolBase
+{
+Q_OBJECT
+public:
+ SnacProtocol( QObject *parent = 0, const char *name = 0 );
+ ~SnacProtocol();
+
+ /**
+ * Attempt to parse the supplied data into an @ref SnacTransfer 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 );
+
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp
new file mode 100644
index 00000000..1e7a17d6
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.cpp
@@ -0,0 +1,50 @@
+/*
+ Kopete Oscar Protocol
+ ssiactivatetask.cpp - Send the SNAC for SSI activation
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include "ssiactivatetask.h"
+#include "connection.h"
+#include "buffer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+
+
+SSIActivateTask::SSIActivateTask(Task* parent): Task(parent)
+{
+}
+
+
+SSIActivateTask::~SSIActivateTask()
+{
+}
+
+
+void SSIActivateTask::onGo()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Sending SSI activate" << endl;
+ FLAP f = { 0x02, 0, 0 } ;
+ SNAC s = { 0x0013, 0x0007, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+ setSuccess( 0, QString::null );
+}
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssiactivatetask.h b/kopete/protocols/oscar/liboscar/ssiactivatetask.h
new file mode 100644
index 00000000..66f0a67b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiactivatetask.h
@@ -0,0 +1,38 @@
+/*
+ Kopete Oscar Protocol
+ ssiactivatetask.h - Send the SNAC for SSI activation
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SSIACTIVATETASK_H
+#define SSIACTIVATETASK_H
+
+#include "task.h"
+
+/**
+A fire and forget task to send the activation SNAC for the SSI list to the server.
+
+@author Matt Rogers
+*/
+class SSIActivateTask : public Task
+{
+public:
+ SSIActivateTask( Task* parent );
+ ~SSIActivateTask();
+ void onGo();
+};
+
+#endif
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.cpp b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp
new file mode 100644
index 00000000..59188d2b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiauthtask.cpp
@@ -0,0 +1,188 @@
+/*
+ Kopete Oscar Protocol
+ ssiauthtask.cpp - SSI Authentication Task
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ssiauthtask.h"
+#include "ssimanager.h"
+#include "transfer.h"
+#include "buffer.h"
+#include "connection.h"
+#include "oscarutils.h"
+
+#include <kdebug.h>
+
+SSIAuthTask::SSIAuthTask( Task* parent )
+ : Task( parent )
+{
+ m_manager = parent->client()->ssiManager();
+}
+
+SSIAuthTask::~SSIAuthTask()
+{
+}
+
+bool SSIAuthTask::forMe( const Transfer* t ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*> ( t );
+
+ if ( !st )
+ return false;
+
+ if ( st->snacService() != 0x0013 )
+ return false;
+
+ switch ( st->snacSubtype() )
+ {
+ case 0x0015: // Future authorization granted
+ case 0x0019: // Authorization request
+ case 0x001b: // Authorization reply
+ case 0x001c: // "You were added" message
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+bool SSIAuthTask::take( Transfer* t )
+{
+ if ( forMe( t ) )
+ {
+ setTransfer( t );
+ SnacTransfer* st = dynamic_cast<SnacTransfer*> ( t );
+
+ switch ( st->snacSubtype() )
+ {
+ case 0x0015: // Future authorization granted
+ handleFutureAuthGranted();
+ break;
+ case 0x0019: // Authorization request
+ handleAuthRequested();
+ break;
+ case 0x001b: // Authorization reply
+ handleAuthReplied();
+ break;
+ case 0x001c: // "You were added" message
+ handleAddedMessage();
+ break;
+ }
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void SSIAuthTask::grantFutureAuth( const QString& uin, const QString& reason )
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x0014, 0x0000, client()->snacSequence() };
+
+ Buffer* buf = new Buffer();
+ buf->addBUIN( uin.latin1() );
+ buf->addBSTR( reason.utf8() );
+ buf->addWord( 0x0000 ); // Unknown
+
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+void SSIAuthTask::sendAuthRequest( const QString& uin, const QString& reason )
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x0018, 0x0000, client()->snacSequence() };
+
+ Buffer* buf = new Buffer();
+ buf->addBUIN( uin.latin1() );
+ buf->addBSTR( reason.utf8() );
+ buf->addWord( 0x0000 ); // Unknown
+
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+void SSIAuthTask::sendAuthReply( const QString& uin, const QString& reason, bool auth )
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x001A, 0x0000, client()->snacSequence() };
+
+ Buffer* buf = new Buffer();
+ buf->addBUIN( uin.latin1() );
+ buf->addByte( auth ? 0x01 : 0x00 ); // accepted / declined
+ buf->addBSTR( reason.utf8() );
+
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+void SSIAuthTask::handleFutureAuthGranted()
+{
+ Buffer* buf = transfer()->buffer();
+
+ QString uin = Oscar::normalize( buf->getBUIN() );
+ QByteArray reason = buf->getBSTR();
+
+ buf->getWord(); // 0x0000 - Unknown
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Future authorization granted from " << uin << endl;
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl;
+ emit futureAuthGranted( uin, QString::fromUtf8( reason.data(), reason.size() ) );
+}
+
+void SSIAuthTask::handleAuthRequested()
+{
+ Buffer* buf = transfer()->buffer();
+
+ QString uin = Oscar::normalize( buf->getBUIN() );
+ QByteArray reason = buf->getBSTR();
+
+ buf->getWord(); // 0x0000 - Unknown
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization requested from " << uin << endl;
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl;
+
+ emit authRequested( uin, QString::fromUtf8( reason.data(), reason.size() ) );
+}
+
+void SSIAuthTask::handleAuthReplied()
+{
+ Buffer* buf = transfer()->buffer();
+
+ QString uin = Oscar::normalize( buf->getBUIN() );
+ bool accepted = buf->getByte();
+ QByteArray reason = buf->getBSTR();
+
+ if ( accepted )
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request accepted by " << uin << endl;
+ else
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Authorization request declined by " << uin << endl;
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Reason: " << reason << endl;
+ emit authReplied( uin, QString::fromUtf8( reason.data(), reason.size() ), accepted );
+}
+
+void SSIAuthTask::handleAddedMessage()
+{
+ Buffer* buf = transfer()->buffer();
+
+ QString uin = Oscar::normalize( buf->getBUIN() );
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "User " << uin << " added you to the contact list" << endl;
+ emit contactAddedYou( uin );
+}
+
+#include "ssiauthtask.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssiauthtask.h b/kopete/protocols/oscar/liboscar/ssiauthtask.h
new file mode 100644
index 00000000..d470cfe9
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiauthtask.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Oscar Protocol
+ ssiauthtask.h - SSI Authentication Task
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SSIAUTHTASK_H
+#define SSIAUTHTASK_H
+
+#include <task.h>
+
+class SSIManager;
+
+/**
+@author Kopete Developers
+*/
+class SSIAuthTask : public Task
+{
+Q_OBJECT
+public:
+ SSIAuthTask( Task* parent );
+
+ ~SSIAuthTask();
+
+ virtual bool forMe( const Transfer* t ) const;
+ virtual bool take( Transfer* t );
+
+ void grantFutureAuth( const QString& uin, const QString& reason );
+ void sendAuthRequest( const QString& uin, const QString& reason );
+ void sendAuthReply( const QString& uin, const QString& reason, bool auth );
+signals:
+ void futureAuthGranted( const QString& uin, const QString& reason );
+ void authRequested( const QString& uin, const QString& reason );
+ void authReplied( const QString& uin, const QString& reason, bool auth );
+ void contactAddedYou( const QString& uin );
+private:
+ void handleFutureAuthGranted();
+ void handleAuthRequested();
+ void handleAuthReplied();
+ void handleAddedMessage();
+
+private:
+ SSIManager* m_manager;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.cpp b/kopete/protocols/oscar/liboscar/ssilisttask.cpp
new file mode 100644
index 00000000..fe2a981d
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssilisttask.cpp
@@ -0,0 +1,174 @@
+/*
+ Kopete Oscar Protocol
+ ssilisttask.cpp - handles all operations dealing with the whole SSI list
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "ssilisttask.h"
+
+#include <kdebug.h>
+#include "connection.h"
+#include "oscarutils.h"
+#include "ssimanager.h"
+#include "transfer.h"
+
+SSIListTask::SSIListTask( Task* parent ) : Task( parent )
+{
+ m_ssiManager = client()->ssiManager();
+ QObject::connect( this, SIGNAL( newContact( const Oscar::SSI& ) ), m_ssiManager, SLOT( newContact( const Oscar::SSI& ) ) );
+ QObject::connect( this, SIGNAL( newGroup( const Oscar::SSI& ) ), m_ssiManager, SLOT( newGroup( const Oscar::SSI& ) ) );
+ QObject::connect( this, SIGNAL( newItem( const Oscar::SSI& ) ), m_ssiManager, SLOT( newItem( const Oscar::SSI& ) ) );
+}
+
+
+SSIListTask::~SSIListTask()
+{}
+
+bool SSIListTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer * st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0013 )
+ {
+ switch ( st->snacSubtype() )
+ {
+ case 0x0006:
+ case 0x000F:
+ return true;
+ default:
+ return false;
+ };
+ }
+
+ return false;
+}
+
+bool SSIListTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ SnacTransfer * st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( st->snacSubtype() == 0x0006 )
+ {
+ setTransfer( transfer );
+ handleSSIListReply();
+ setTransfer( 0 );
+ return true;
+ }
+ else if ( st->snacSubtype() == 0x000F )
+ {
+ setTransfer( transfer );
+ handleSSIUpToDate();
+ setTransfer( 0 );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SSIListTask::onGo()
+{
+ checkSSITimestamp();
+}
+
+void SSIListTask::handleSSIListReply()
+{
+ QValueList<TLV> tlvList;
+
+ Buffer* buffer = transfer()->buffer();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI Protocol version: " << buffer->getByte() << endl;
+ WORD ssiItems = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in this SSI packet: " << ssiItems << endl;
+ WORD parsedItems;
+ for ( parsedItems = 1; parsedItems <= ssiItems; ++parsedItems )
+ {
+ tlvList.clear();
+ WORD strlength = buffer->getWord();
+ QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength );
+ WORD groupId = buffer->getWord();
+ WORD itemId = buffer->getWord();
+ WORD itemType = buffer->getWord();
+ WORD tlvLength = buffer->getWord();
+ for ( int i = 0; i < tlvLength; )
+ {
+ TLV t = buffer->getTLV();
+ i += 4;
+ i += t.length;
+ tlvList.append( t );
+ }
+
+ if ( itemType == ROSTER_CONTACT )
+ itemName = Oscar::normalize( itemName );
+
+ Oscar::SSI s( itemName, groupId, itemId, itemType, tlvList );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got SSI Item: " << s.toString() << endl;
+ if ( s.type() == ROSTER_GROUP )
+ emit newGroup( s );
+
+ if ( s.type() == ROSTER_CONTACT )
+ emit newContact( s );
+
+ if ( s.type() != ROSTER_CONTACT && s.type() != ROSTER_GROUP )
+ emit newItem( s );
+ }
+
+ if ( buffer->length() > 0 )
+ {
+ client()->ssiManager()->setLastModificationTime( buffer->getDWord() );
+ //check the snac flags for another packet
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer() );
+ if ( st && st->snacFlags() == 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "SSI List complete" << endl;
+ client()->ssiManager()->setListComplete( true );
+ setSuccess( 0, QString::null );
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Awaiting another SSI packet" << endl;
+ }
+
+}
+
+void SSIListTask::handleSSIUpToDate()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Our SSI List is up to date" << endl;
+ Buffer* buffer = transfer()->buffer();
+
+ client()->ssiManager()->setLastModificationTime( buffer->getDWord() );
+ WORD ssiItems = buffer->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Number of items in SSI list: " << ssiItems << endl;
+
+ client()->ssiManager()->setListComplete( true );
+ setSuccess( 0, QString::null );
+}
+
+void SSIListTask::checkSSITimestamp()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Checking the timestamp of the SSI list" << endl;
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x0005, 0x0000, client()->snacSequence() };
+ Buffer* buffer = new Buffer();
+ buffer->addDWord( client()->ssiManager()->lastModificationTime() );
+ buffer->addDWord( client()->ssiManager()->numberOfItems() );
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+}
+
+#include "ssilisttask.moc"
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssilisttask.h b/kopete/protocols/oscar/liboscar/ssilisttask.h
new file mode 100644
index 00000000..96a4c3d8
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssilisttask.h
@@ -0,0 +1,106 @@
+/*
+ Kopete Oscar Protocol
+ ssilisttask.h - handles all operations dealing with the whole SSI list
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SSILISTTASK_H
+#define SSILISTTASK_H
+
+#include <task.h>
+
+class SSI;
+class SSIManager;
+
+/**
+ * This task handles all the operations dealing with the whole SSI list
+ * All SNACs handled by this task are in family 13. Subtypes 5, 6, and 7
+ * are handled by individual functions. Subtype F is handled in the take()
+ * function. We don't use subtype 4 because the same thing can be accomplished
+ * using subtypes 5 and 6 together.
+ *
+ * @author Matt Rogers
+*/
+class SSIListTask : public Task
+{
+Q_OBJECT
+public:
+ SSIListTask( Task* parent );
+ ~SSIListTask();
+
+ virtual bool take( Transfer* transfer );
+
+protected:
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual void onGo();
+
+signals:
+ /** We have a new group */
+ void newGroup( const Oscar::SSI& );
+
+ /** We have a new contact */
+ void newContact( const Oscar::SSI& );
+
+ /**
+ * We have a new contact and they're on the visible list
+ */
+ void newVisibleItem( const Oscar::SSI& );
+
+ /**
+ * We have a new contact and they're on the invisible list
+ */
+ void newInvisibleItem( const Oscar::SSI& );
+
+ /**
+ * We have a new item
+ * Used for items we don't explicitly handle yet
+ */
+ void newItem( const Oscar::SSI& );
+
+private:
+
+ /**
+ * Handle the list we get from the server
+ * This is SNAC( 0x13, 0x06 )
+ */
+ void handleSSIListReply();
+
+ /**
+ * Check the timestamp of the local SSI copy
+ * If it's up to date, we'll get SNAC( 13, 06 )
+ * If it's out of date, we'll get SNAC( 13, 0F )
+ * This is SNAC( 0x13, 0x05 )
+ */
+ void checkSSITimestamp();
+
+ /**
+ * The timestamp of the SSI is up to date
+ * This is SNAC( 0x13, 0x0F )
+ */
+ void handleSSIUpToDate();
+
+
+private:
+ /**
+ * Pointer to the SSI manager so we don't have to keep
+ * calling client()->ssiManager(). It's guaranteed to
+ * exist.
+ */
+ SSIManager* m_ssiManager;
+
+};
+
+#endif
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssimanager.cpp b/kopete/protocols/oscar/liboscar/ssimanager.cpp
new file mode 100644
index 00000000..066e93fa
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssimanager.cpp
@@ -0,0 +1,658 @@
+/*
+ Kopete Oscar Protocol
+ ssimanager.cpp - SSI management
+
+ Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+ Copyright ( c ) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu>
+
+ *************************************************************************
+ * *
+ * 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 "ssimanager.h"
+#include <kdebug.h>
+#include "oscarutils.h"
+
+// -------------------------------------------------------------------
+
+class SSIManagerPrivate
+{
+public:
+ QValueList<Oscar::SSI> SSIList;
+ QValueList<WORD> groupIdList;
+ QValueList<WORD> itemIdList;
+ bool complete;
+ DWORD lastModTime;
+ WORD maxContacts;
+ WORD maxGroups;
+ WORD maxVisible;
+ WORD maxInvisible;
+ WORD maxIgnore;
+ WORD nextContactId;
+ WORD nextGroupId;
+};
+
+SSIManager::SSIManager( QObject *parent, const char *name )
+ : QObject( parent, name )
+{
+ d = new SSIManagerPrivate;
+ d->complete = false;
+ d->lastModTime = 0;
+ d->nextContactId = 0;
+ d->nextGroupId = 0;
+ d->maxContacts = 999;
+ d->maxGroups = 999;
+ d->maxIgnore = 999;
+ d->maxInvisible = 999;
+ d->maxVisible = 999;
+}
+
+
+SSIManager::~SSIManager()
+{
+ clear();
+ delete d;
+}
+
+void SSIManager::clear()
+{
+ //delete all SSIs from the list
+ if ( d->SSIList.count() > 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Clearing the SSI list" << endl;
+ QValueList<Oscar::SSI>::iterator it = d->SSIList.begin();
+
+ while ( it != d->SSIList.end() && d->SSIList.count() > 0 )
+ it = d->SSIList.remove( it );
+ };
+
+ d->itemIdList.clear();
+ d->groupIdList.clear();
+ d->complete = false;
+ d->lastModTime = 0;
+ d->nextContactId = 0;
+ d->nextGroupId = 0;
+}
+
+WORD SSIManager::nextContactId()
+{
+ if ( d->nextContactId == 0 )
+ d->nextContactId++;
+
+ d->nextContactId = findFreeId( d->itemIdList, d->nextContactId );
+
+ if ( d->nextContactId == 0xFFFF )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free id!" << endl;
+ return 0xFFFF;
+ }
+
+ if ( d->itemIdList.contains( d->nextContactId ) == 0 )
+ d->itemIdList.append( d->nextContactId );
+
+ return d->nextContactId++;
+}
+
+WORD SSIManager::nextGroupId()
+{
+ if ( d->nextGroupId == 0 )
+ d->nextGroupId++;
+
+ d->nextGroupId = findFreeId( d->groupIdList, d->nextGroupId );
+
+ if ( d->nextGroupId == 0xFFFF )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "No free group id!" << endl;
+ return 0xFFFF;
+ }
+
+ if ( d->groupIdList.contains( d->nextGroupId ) == 0 )
+ d->groupIdList.append( d->nextGroupId );
+
+ return d->nextGroupId++;
+}
+
+WORD SSIManager::numberOfItems() const
+{
+ return d->SSIList.count();
+}
+
+DWORD SSIManager::lastModificationTime() const
+{
+ return d->lastModTime;
+}
+
+void SSIManager::setLastModificationTime( DWORD lastTime )
+{
+ d->lastModTime = lastTime;
+}
+
+void SSIManager::setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible, WORD maxInvisible, WORD maxIgnore )
+{
+ //I'm not using k_funcinfo for these debug statements because of
+ //the function's long signature
+ QString funcName = QString::fromLatin1( "[void SSIManager::setParameters] " );
+ kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed in SSI: "
+ << maxContacts << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of groups allowed in SSI: "
+ << maxGroups << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on visible list: "
+ << maxVisible << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on invisible list: "
+ << maxInvisible << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << funcName << "Max number of contacts allowed on ignore list: "
+ << maxIgnore << endl;
+
+ d->maxContacts = maxContacts;
+ d->maxGroups = maxGroups;
+ d->maxInvisible = maxInvisible;
+ d->maxVisible = maxVisible;
+ d->maxIgnore = maxIgnore;
+}
+
+void SSIManager::loadFromExisting( const QValueList<Oscar::SSI*>& newList )
+{
+ Q_UNUSED( newList );
+ //FIXME: NOT Implemented!
+}
+
+bool SSIManager::hasItem( const Oscar::SSI& item ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ {
+ Oscar::SSI s = ( *it );
+ if ( s == item )
+ return true;
+ }
+
+ return false;
+}
+
+Oscar::SSI SSIManager::findGroup( const QString &group ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_GROUP && (*it ).name().lower() == group.lower() )
+ return ( *it );
+
+
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findGroup( int groupId ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_GROUP && (*it ).gid() == groupId )
+ return ( *it );
+
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findContact( const QString &contact, const QString &group ) const
+{
+
+ if ( contact.isNull() || group.isNull() )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo <<
+ "Passed NULL name or group string, aborting!" << endl;
+
+ return m_dummyItem;
+ }
+
+ Oscar::SSI gr = findGroup( group ); // find the parent group
+ if ( gr.isValid() )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "gr->name= " << gr.name() <<
+ ", gr->gid= " << gr.gid() <<
+ ", gr->bid= " << gr.bid() <<
+ ", gr->type= " << gr.type() << endl;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ {
+ if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact && (*it ).gid() == gr.gid() )
+ {
+ //we have found our contact
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo <<
+ "Found contact " << contact << " in SSI data" << endl;
+ return ( *it );
+ }
+ }
+ }
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo <<
+ "ERROR: Group '" << group << "' not found!" << endl;
+ }
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findContact( const QString &contact ) const
+{
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_CONTACT && (*it ).name() == contact )
+ return ( *it );
+
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findContact( int contactId ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it!= listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_CONTACT && ( *it ).bid() == contactId )
+ return ( *it );
+
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findItemForIcon( QByteArray iconHash ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it!= listEnd; ++it )
+ {
+ if ( ( *it ).type() == ROSTER_BUDDYICONS )
+ {
+ TLV t = Oscar::findTLV( ( *it ).tlvList(), 0x00D5 );
+ Buffer b(t.data);
+ b.skipBytes(1); //don't care about flags
+ BYTE iconSize = b.getByte();
+ QByteArray hash( b.getBlock( iconSize ) );
+ if ( hash == iconHash )
+ {
+ Oscar::SSI s = ( *it );
+ return s;
+ }
+ }
+ }
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findItemForIconByRef( int ref ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it!= listEnd; ++it )
+ {
+ if ( ( *it ).type() == ROSTER_BUDDYICONS )
+ {
+ if ( ( *it ).name().toInt() == ref )
+ {
+ Oscar::SSI s = ( *it );
+ return s;
+ }
+ }
+ }
+ return m_dummyItem;
+}
+
+Oscar::SSI SSIManager::findItem( const QString &contact, int type ) const
+{
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+
+ for ( it = d->SSIList.begin(); it!= listEnd; ++it )
+ if ( ( *it ).type() == type && ( *it ).name() == contact )
+ return ( *it );
+
+ return m_dummyItem;
+}
+
+QValueList<Oscar::SSI> SSIManager::groupList() const
+{
+ QValueList<Oscar::SSI> list;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_GROUP )
+ list.append( ( *it ) );
+
+ return list;
+}
+
+QValueList<Oscar::SSI> SSIManager::contactList() const
+{
+ QValueList<Oscar::SSI> list;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_CONTACT )
+ list.append( ( *it ) );
+
+ return list;
+}
+
+QValueList<Oscar::SSI> SSIManager::visibleList() const
+{
+ QValueList<Oscar::SSI> list;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_VISIBLE )
+ list.append( ( *it ) );
+
+ return list;
+}
+
+QValueList<Oscar::SSI> SSIManager::invisibleList() const
+{
+ QValueList<Oscar::SSI> list;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_INVISIBLE )
+ list.append( ( *it ) );
+
+ return list;
+}
+
+QValueList<Oscar::SSI> SSIManager::contactsFromGroup( const QString &group ) const
+{
+ QValueList<Oscar::SSI> list;
+
+ Oscar::SSI gr = findGroup( group );
+ if ( gr.isValid() )
+ {
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == gr.gid() )
+ list.append( ( *it ) );
+ }
+ return list;
+}
+
+QValueList<Oscar::SSI> SSIManager::contactsFromGroup( int groupId ) const
+{
+ QValueList<Oscar::SSI> list;
+
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ if ( ( *it ).type() == ROSTER_CONTACT && (*it ).gid() == groupId )
+ list.append( ( *it ) );
+
+ return list;
+}
+
+Oscar::SSI SSIManager::visibilityItem() const
+{
+ Oscar::SSI item = m_dummyItem;
+ QValueList<Oscar::SSI>::const_iterator it, listEnd = d->SSIList.end();
+ for ( it = d->SSIList.begin(); it != listEnd; ++it )
+ {
+ if ( ( *it ).type() == 0x0004 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found visibility setting" << endl;
+ item = ( *it );
+ return item;
+ }
+ }
+
+ return item;
+}
+
+void SSIManager::setListComplete( bool complete )
+{
+ d->complete = complete;
+}
+
+bool SSIManager::listComplete() const
+{
+ return d->complete;
+}
+
+bool SSIManager::newGroup( const Oscar::SSI& group )
+{
+ //trying to find the group by its ID
+ QValueList<Oscar::SSI>::iterator it, listEnd = d->SSIList.end();
+ if ( findGroup( group.name() ).isValid() )
+ return false;
+
+ if ( !group.name().isEmpty() ) //avoid the group with gid 0 and bid 0
+ { // the group is really new
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding group '" << group.name() << "' to SSI list" << endl;
+
+ d->SSIList.append( group );
+ addID( group );
+ emit groupAdded( group );
+ return true;
+ }
+ return false;
+}
+
+bool SSIManager::updateGroup( const Oscar::SSI& group )
+{
+ Oscar::SSI oldGroup = findGroup( group.name() );
+
+ if ( oldGroup.isValid() )
+ {
+ removeID( oldGroup );
+ d->SSIList.remove( oldGroup );
+ }
+
+ if ( d->SSIList.findIndex( group ) != -1 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New group is already in list." << endl;
+ return false;
+ }
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating group '" << group.name() << "' in SSI list" << endl;
+ d->SSIList.append( group );
+ addID( group );
+ emit groupUpdated( group );
+
+ return true;
+}
+
+bool SSIManager::removeGroup( const Oscar::SSI& group )
+{
+ QString groupName = group.name();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing group " << group.name() << endl;
+ int remcount = d->SSIList.remove( group );
+ removeID( group );
+
+ if ( remcount == 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No groups removed" << endl;
+ return false;
+ }
+
+ emit groupRemoved( groupName );
+ return true;
+}
+
+bool SSIManager::removeGroup( const QString &group )
+{
+ Oscar::SSI gr = findGroup( group );
+
+ if ( gr.isValid() && removeGroup( gr ) )
+ {
+ return true;
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Group " << group << " not found." << endl;
+
+ return false;
+}
+
+bool SSIManager::newContact( const Oscar::SSI& contact )
+{
+ if ( d->SSIList.findIndex( contact ) == -1 )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding contact '" << contact.name() << "' to SSI list" << endl;
+ addID( contact );
+ d->SSIList.append( contact );
+ emit contactAdded( contact );
+ }
+ else
+ return false;
+ return true;
+}
+
+bool SSIManager::updateContact( const Oscar::SSI& contact )
+{
+ Oscar::SSI oldContact = findContact( contact.name() );
+
+ if ( oldContact.isValid() )
+ {
+ removeID( oldContact );
+ d->SSIList.remove( oldContact );
+ }
+
+ if ( d->SSIList.findIndex( contact ) != -1 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New contact is already in list." << endl;
+ return false;
+ }
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating contact '" << contact.name() << "' in SSI list" << endl;
+ addID( contact );
+ d->SSIList.append( contact );
+ emit contactUpdated( contact );
+
+ return true;
+}
+
+bool SSIManager::removeContact( const Oscar::SSI& contact )
+{
+ QString contactName = contact.name();
+ int remcount = d->SSIList.remove( contact );
+ removeID( contact );
+
+ if ( remcount == 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No contacts were removed." << endl;
+ return false;
+ }
+
+ emit contactRemoved( contactName );
+ return true;
+}
+
+bool SSIManager::removeContact( const QString &contact )
+{
+ Oscar::SSI ct = findContact( contact );
+
+ if ( ct.isValid() && removeContact( ct ) )
+ return true;
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Contact " << contact << " not found." << endl;
+
+ return false;
+}
+
+bool SSIManager::newItem( const Oscar::SSI& item )
+{
+ if ( d->SSIList.findIndex( item ) != -1 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Item is already in list." << endl;
+ return false;
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding item " << item.toString() << endl;
+ d->SSIList.append( item );
+ addID( item );
+ return true;
+}
+
+bool SSIManager::updateItem( const Oscar::SSI& item )
+{
+ Oscar::SSI oldItem = findItem( item.name(), item.type() );
+
+ if ( oldItem.isValid() )
+ {
+ removeID( oldItem );
+ d->SSIList.remove( oldItem );
+ }
+
+ if ( d->SSIList.findIndex( item ) != -1 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "New item is already in list." << endl;
+ return false;
+ }
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Updating item in SSI list" << endl;
+ addID( item );
+ d->SSIList.append( item );
+ return true;
+}
+
+bool SSIManager::removeItem( const Oscar::SSI& item )
+{
+ int remcount = d->SSIList.remove( item );
+ removeID( item );
+
+ if ( remcount == 0 )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "No items were removed." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+void SSIManager::addID( const Oscar::SSI& item )
+{
+ if ( item.type() == ROSTER_GROUP )
+ {
+ if ( d->groupIdList.contains( item.gid() ) == 0 )
+ d->groupIdList.append( item.gid() );
+ }
+ else
+ {
+ if ( d->itemIdList.contains( item.bid() ) == 0 )
+ d->itemIdList.append( item.bid() );
+ }
+}
+
+void SSIManager::removeID( const Oscar::SSI& item )
+{
+ if ( item.type() == ROSTER_GROUP )
+ {
+ d->groupIdList.remove( item.gid() );
+
+ if ( d->nextGroupId > item.gid() )
+ d->nextGroupId = item.gid();
+ }
+ else
+ {
+ d->itemIdList.remove( item.bid() );
+
+ if ( d->nextContactId > item.bid() )
+ d->nextContactId = item.bid();
+ }
+}
+
+WORD SSIManager::findFreeId( const QValueList<WORD>& idList, WORD fromId ) const
+{
+ for ( WORD id = fromId; id < 0x8000; id++ )
+ {
+ if ( idList.contains( id ) == 0 )
+ return id;
+ }
+
+ return 0xFFFF;
+}
+
+#include "ssimanager.moc"
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssimanager.h b/kopete/protocols/oscar/liboscar/ssimanager.h
new file mode 100644
index 00000000..24e87c6a
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssimanager.h
@@ -0,0 +1,154 @@
+/*
+ Kopete Oscar Protocol
+ ssimanager.h - SSI management
+
+ Copyright ( c ) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+ Copyright ( c ) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete ( c ) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ based on ssidata.h and ssidata.cpp ( c ) 2002 Tom Linsky <twl6@po.cwru.edu>
+
+ *************************************************************************
+ * *
+ * 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 SSIMANAGER_H
+#define SSIMANAGER_H
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include "oscartypes.h"
+#include "oscartypeclasses.h"
+
+using namespace Oscar;
+
+class SSIManagerPrivate;
+
+/**
+SSI management
+
+@author Gustavo Pichorim Boiko
+@author Matt Rogers
+*/
+class KOPETE_EXPORT SSIManager : public QObject
+{
+ Q_OBJECT
+public:
+ SSIManager( QObject* parent = 0, const char* name = 0 );
+
+ ~SSIManager();
+
+ /** Clear the internal SSI list */
+ void clear();
+
+ /** Get the next buddy id for an SSI item */
+ WORD nextContactId();
+
+ /** Get the next group id for an SSI item */
+ WORD nextGroupId();
+
+ /** Get the number of items in the SSI list. */
+ WORD numberOfItems() const;
+
+ /** Get the timestamp the list was last modified */
+ DWORD lastModificationTime() const;
+
+ /** Set the timestamp of the last modification time */
+ void setLastModificationTime( DWORD lastTime );
+
+ /** Set the parameters we should use for SSI */
+ void setParameters( WORD maxContacts, WORD maxGroups, WORD maxVisible,
+ WORD maxInvisible, WORD maxIgnore );
+
+ /**
+ * Load an existing list from SSI objects.
+ * The current SSI list will be overwritten and it's contents
+ * replaced with the data from the new list
+ */
+ void loadFromExisting( const QValueList<Oscar::SSI*>& newList );
+
+ bool hasItem( const Oscar::SSI& item ) const;
+
+ Oscar::SSI findGroup( const QString& group ) const;
+ Oscar::SSI findGroup( int groupId ) const;
+
+
+ Oscar::SSI findContact( const QString& contact, const QString& group ) const;
+ Oscar::SSI findContact( const QString& contact ) const;
+ Oscar::SSI findContact( int contactId ) const;
+
+ Oscar::SSI findItemForIcon( QByteArray iconHash ) const;
+ Oscar::SSI findItemForIconByRef( int ) const;
+
+ Oscar::SSI findItem( const QString &contact, int type ) const;
+
+ QValueList<Oscar::SSI> groupList() const;
+ QValueList<Oscar::SSI> contactList() const;
+ QValueList<Oscar::SSI> visibleList() const;
+ QValueList<Oscar::SSI> invisibleList() const;
+ QValueList<Oscar::SSI> contactsFromGroup( const QString& group ) const;
+ QValueList<Oscar::SSI> contactsFromGroup( int groupId ) const;
+
+ Oscar::SSI visibilityItem() const;
+
+ void setListComplete( bool complete );
+ bool listComplete() const;
+
+public slots:
+ bool newGroup( const Oscar::SSI& group );
+ bool updateGroup( const Oscar::SSI& group );
+ bool removeGroup( const Oscar::SSI& group );
+ bool removeGroup( const QString& group );
+
+ bool newContact( const Oscar::SSI& contact );
+ bool updateContact( const Oscar::SSI& contact );
+ bool removeContact( const Oscar::SSI& contact );
+ bool removeContact( const QString& contact );
+
+ bool newItem( const Oscar::SSI& item );
+ bool updateItem( const Oscar::SSI& item );
+ bool removeItem( const Oscar::SSI& item );
+
+ void addID( const Oscar::SSI& item );
+ void removeID( const Oscar::SSI& item );
+
+signals:
+
+ //! Emitted when we've added a new contact to the list
+ void contactAdded( const Oscar::SSI& );
+
+ //! Emitted when we've updated a contact in the list
+ void contactUpdated( const Oscar::SSI& );
+
+ //! Emitted when we've removed a contact from the list
+ void contactRemoved( const QString& contactName );
+
+ //! Emitted when we've added a new group to the list
+ void groupAdded( const Oscar::SSI& );
+
+ //! Emitted when we've updated a group in the list
+ void groupUpdated( const Oscar::SSI& );
+
+ //! Emitted when we've removed a group from the ssi list
+ void groupRemoved( const QString& groupName );
+
+ void modifyError( const QString& error );
+
+private:
+ WORD findFreeId( const QValueList<WORD>& idList, WORD fromId ) const;
+
+ SSIManagerPrivate* d;
+ Oscar::SSI m_dummyItem;
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.cpp b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp
new file mode 100644
index 00000000..2091fca8
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssimodifytask.cpp
@@ -0,0 +1,637 @@
+/*
+ Kopete Oscar Protocol
+ ssimodifytask.cpp - Handles all the ssi modification stuff
+
+ Copyright (c) 2004 by Kopete Developers <kopete-devel@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "ssimodifytask.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <qstring.h>
+#include "connection.h"
+#include "oscarutils.h"
+#include "transfer.h"
+
+
+SSIModifyTask::SSIModifyTask( Task* parent, bool staticTask ) : Task( parent )
+{
+ m_ssiManager = parent->client()->ssiManager();
+ m_static = staticTask;
+ m_opType = NoType;
+ m_opSubject = NoSubject;
+ m_id = 0;
+}
+
+
+SSIModifyTask::~SSIModifyTask()
+{
+}
+
+void SSIModifyTask::onGo()
+{
+ sendSSIUpdate();
+}
+
+bool SSIModifyTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( st )
+ {
+ setTransfer( transfer );
+
+ if ( st->snacSubtype() == 0x0008 )
+ handleSSIAdd();
+ else if ( st->snacSubtype() == 0x0009 )
+ handleSSIUpdate();
+ else if ( st->snacSubtype() == 0x000A )
+ handleSSIRemove();
+ else if ( st->snacSubtype() == 0x000E )
+ handleSSIAck();
+
+ setTransfer( 0 );
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+bool SSIModifyTask::addContact( const QString& contact, const QString& group, bool requiresAuth )
+{
+ m_opType = Add;
+ m_opSubject = Contact;
+
+ QString newContact = Oscar::normalize( contact );
+
+ Oscar::SSI oldItem = m_ssiManager->findContact( newContact );
+ Oscar::SSI groupItem = m_ssiManager->findGroup( group );
+
+ if ( !groupItem )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "group " << group << " does not exist on SSI. Aborting" << endl;
+ return false;
+ }
+
+ //create new SSI item and populate the TLV list
+ QValueList<TLV> tlvList;
+ if ( requiresAuth )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "This contact requires auth. adding appropriate tlv" << endl;
+ TLV t( 0x0066, 0, 0 );
+ tlvList.append( t );
+ }
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "creating new SSI item for " << contact << " in group " << group << endl;
+ Oscar::SSI newItem( newContact, groupItem.gid(), m_ssiManager->nextContactId(), ROSTER_CONTACT, tlvList );
+ m_newItem = newItem;
+ return true;
+}
+
+bool SSIModifyTask::removeContact( const QString& contact )
+{
+ m_opType = Remove;
+ m_opSubject = Contact;
+ m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling" << m_oldItem.name() << " for removal" << endl;
+ return true;
+}
+
+bool SSIModifyTask::changeGroup( const QString& contact, const QString& newGroup )
+{
+ m_opType = Change;
+ m_opSubject = Group;
+ m_oldItem = m_ssiManager->findContact( Oscar::normalize( contact ) );
+ Oscar::SSI oldGroupItem;
+ if ( m_oldItem.isValid() )
+ oldGroupItem = m_ssiManager->findGroup( newGroup );
+ else
+ return false;
+
+ if ( m_oldItem.gid() == oldGroupItem.gid() )
+ { //buddy already exists in this group
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "contact " << contact << " already exists in group " << oldGroupItem.name() << ". Aborting." << endl;
+ return false;
+ }
+
+ m_groupItem = m_ssiManager->findGroup( newGroup );
+ if ( !m_groupItem )
+ { //couldn't find group
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "new group " << newGroup << " not found in SSI. Aborting" << endl;
+ return false;
+ }
+
+ //create a new SSI item for the buddy in the new group
+ Oscar::SSI newItem( m_oldItem.name(), m_groupItem.gid(), m_oldItem.bid(), ROSTER_CONTACT, m_oldItem.tlvList() );
+ m_newItem = newItem;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Moving '" << m_oldItem.name() << "' to group " << m_groupItem.name() << endl;
+ return true;
+}
+
+bool SSIModifyTask::addGroup( const QString& groupName )
+{
+ m_opType = Add;
+ m_opSubject = Group;
+ m_newItem = m_ssiManager->findGroup( groupName );
+ QValueList<TLV> dummy;
+ Oscar::SSI newItem( groupName, m_ssiManager->nextGroupId(), 0, ROSTER_GROUP, dummy );
+ m_newItem = newItem;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding group '" << m_newItem.name() << "' to SSI" << endl;
+ return true;
+}
+
+bool SSIModifyTask::removeGroup( const QString& groupName )
+{
+ m_opType = Remove;
+ m_opSubject = Group;
+ m_oldItem = m_ssiManager->findGroup( groupName );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Scheduling group '" << m_oldItem.name() << "' for removal. " << endl;
+ return true;
+}
+
+bool SSIModifyTask::renameGroup( const QString& oldName, const QString & newName )
+{
+ m_opType = Rename;
+ m_opSubject = Group;
+ if ( oldName == newName )
+ return false;
+
+ m_oldItem = m_ssiManager->findGroup( oldName );
+ Oscar::SSI newItem( newName, m_oldItem.gid(), m_oldItem.bid(), ROSTER_GROUP, m_oldItem.tlvList() );
+ m_newItem = newItem;
+ return true;
+}
+
+bool SSIModifyTask::addItem( const Oscar::SSI& item )
+{
+ m_opType = Add;
+ m_opSubject = NoSubject;
+ m_newItem = item;
+ return true;
+}
+
+bool SSIModifyTask::removeItem( const Oscar::SSI& item )
+{
+ m_opType = Remove;
+ m_opSubject = NoSubject;
+ m_oldItem = item;
+ return true;
+}
+
+bool SSIModifyTask::modifyItem( const Oscar::SSI& oldItem, const Oscar::SSI& newItem )
+{
+ if ( !m_ssiManager->hasItem( oldItem ) )
+ return false;
+
+ //make sure there are some common things between the two items
+ if ( oldItem.type() != newItem.type() )
+ return false;
+
+ m_oldItem = oldItem;
+ m_newItem = newItem;
+ m_opType = Change;
+ m_opSubject = NoSubject;
+ return true;
+}
+
+bool SSIModifyTask::forMe( const Transfer * transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0013 )
+ {
+ WORD subtype = st->snacSubtype();
+ if ( m_static )
+ {
+ if ( subtype == 0x0008 || subtype == 0x0009 || subtype == 0x000A )
+ return true;
+ }
+ else
+ {
+ if ( subtype == 0x000E && m_id == st->snac().id )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SSIModifyTask::handleSSIAck()
+{
+ Buffer* b = transfer()->buffer();
+ int numItems = b->length() / 2;
+ for( int i = 0; i < numItems; ++i )
+ {
+ WORD ackCode = b->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << "Acknowledgement code is " << ackCode << endl;
+
+ if ( ackCode != 0x0000 )
+ freeIdOnError();
+
+ switch( ackCode )
+ {
+ case 0x0000:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "SSI Update successful" << endl;
+ updateSSIManager();
+ break;
+ case 0x0002:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item to modify not found in list" << endl;
+ setSuccess( 0, QString::null );
+ break;
+ case 0x0003:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Item already exists in SSI" << endl;
+ setSuccess( 0, QString::null );
+ break;
+ case 0x000A:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Error adding item ( invalid id, already in list, invalid data )" << endl;
+ setSuccess( 0, QString::null );
+ break;
+ case 0x000C:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item. Limit exceeded." << endl;
+ setSuccess( 0, QString::null );
+ break;
+ case 0x000D:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add ICQ item to AIM list ( and vice versa )" << endl;
+ setSuccess( 0, QString::null );
+ break;
+ case 0x000E:
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Can't add item because contact requires authorization" << endl;
+ Oscar::SSI groupItem = m_ssiManager->findGroup( m_newItem.gid() );
+ QString groupName = groupItem.name();
+ addContact( m_newItem.name(), groupName, true );
+ go();
+ break;
+ }
+ default:
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Unknown acknowledgement code" << endl;
+ setSuccess( 0, QString::null );
+ break;
+ }
+ };
+
+
+}
+
+void SSIModifyTask::sendSSIUpdate()
+{
+ //what type of update are we sending?
+ if ( m_opSubject == Group && m_opType == Change )
+ changeGroupOnServer();
+
+ //add an item to the ssi list
+ if ( m_opType == Add )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Adding an item to the SSI list" << endl;
+ sendEditStart();
+
+ //add the item
+ FLAP f1 = { 0x02, 0, 0 };
+ m_id = client()->snacSequence();
+ SNAC s1 = { 0x0013, 0x0008, 0x0000, m_id };
+ Buffer* ssiBuffer = new Buffer;
+ ssiBuffer->addString( m_newItem );
+ Transfer* t2 = createTransfer( f1, s1, ssiBuffer );
+ send( t2 );
+
+ sendEditEnd();
+ }
+
+ //remove an item
+ if ( m_opType == Remove )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI" << endl;
+ sendEditStart();
+
+ //remove the item
+ FLAP f1 = { 0x02, 0, 0 };
+ m_id = client()->snacSequence();
+ SNAC s1 = { 0x0013, 0x000A, 0x0000, m_id };
+ Buffer* ssiBuffer = new Buffer;
+ ssiBuffer->addString( m_oldItem );
+ Transfer* t2 = createTransfer( f1, s1, ssiBuffer );
+ send( t2 );
+
+ sendEditEnd();
+ }
+
+ //modify an item
+ //we use rename for group and change for other items
+ if ( m_opType == Rename || ( m_opType == Change && m_opSubject != Group ) )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Modifying the item: " << m_oldItem.toString() << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "changing it to: " << m_newItem.toString() << endl;
+ sendEditStart();
+
+ //change the group name
+ FLAP f1 = { 0x02, 0, 0 };
+ m_id = client()->snacSequence();
+ SNAC s1 = { 0x0013, 0x0009, 0x0000, m_id };
+ Buffer* ssiBuffer = new Buffer;
+ ssiBuffer->addString( m_newItem );
+ Transfer* t2 = createTransfer( f1, s1, ssiBuffer );
+ send( t2 );
+
+ sendEditEnd();
+ }
+
+}
+
+void SSIModifyTask::changeGroupOnServer()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Moving a contact from one group to another" << endl;
+
+ sendEditStart();
+
+ //remove the old buddy from the list
+ FLAP f1 = { 0x02, 0, 0 };
+ SNAC s1 = { 0x0013, 0x000A, 0x0000, client()->snacSequence() };
+ Buffer* b1 = new Buffer;
+ b1->addBSTR( m_oldItem.name().latin1() );
+ b1->addWord( m_oldItem.gid() );
+ b1->addWord( m_oldItem.bid() );
+ b1->addWord( m_oldItem.type() );
+ b1->addWord( 0 );
+ Transfer* t2 = createTransfer( f1, s1, b1 );
+ send( t2 );
+
+ //add the buddy to the list with a different group
+ FLAP f2 = { 0x02, 0, 0 };
+ m_id = client()->snacSequence(); //we don't care about the first ack
+ SNAC s2 = { 0x0013, 0x0008, 0x0000, m_id };
+ Buffer* b2 = new Buffer;
+ addItemToBuffer( m_newItem, b2 );
+
+ Transfer* t3 = createTransfer( f2, s2, b2 );
+ send( t3 );
+
+ //find the old group so we can change it's list of buddy ids
+ //what a kludge
+ Oscar::SSI oldGroupItem = m_ssiManager->findGroup( m_oldItem.gid() );
+ /* not checking the existance of oldGroupItem because if we got here
+ it has to exist */
+
+ //Change the 0x00C8 TLV in the old group item to remove the bid we're
+ //moving to a different group
+ QValueList<TLV> list = oldGroupItem.tlvList();
+ TLV oldIds = Oscar::findTLV( list, 0x00C8 );
+ if ( oldIds.type == 0x00C8 )
+ {
+ Buffer newTLVData;
+ Buffer tlvBuffer( oldIds.data, oldIds.length );
+ while ( tlvBuffer.length() != 0 )
+ {
+ WORD id = tlvBuffer.getWord();
+ if ( id != m_oldItem.bid() )
+ newTLVData.addWord( id );
+ }
+
+ TLV newGroupTLV( 0x00C8, newTLVData.length(), newTLVData.buffer() );
+
+ list.remove( oldIds );
+ list.append( newGroupTLV );
+ oldGroupItem.setTLVList( list );
+ }
+
+
+ //Change the 0x00C8 TLV in the new group item to add the bid we're
+ //adding to this group
+ QValueList<TLV> list2 = m_groupItem.tlvList();
+ TLV oldIds2 = Oscar::findTLV( list2, 0x00C8 );
+ TLV newGroupTLV;
+ if ( oldIds2.type == 0x00C8 )
+ {
+ Buffer tlvBuffer( oldIds2.data, oldIds2.length );
+ tlvBuffer.addWord( m_newItem.bid() );
+
+ TLV newGroupTLV( 0x00C8, tlvBuffer.length(), tlvBuffer.buffer() );
+ list2.remove( oldIds );
+ list2.append( newGroupTLV );
+ m_groupItem.setTLVList( list2 );
+ }
+
+ //change the group properties
+ FLAP f3 = { 0x02, 0, 0 };
+ SNAC s3 = { 0x0013, 0x0009, 0x0000, client()->snacSequence() };
+ Buffer* b3 = new Buffer;
+ addItemToBuffer( oldGroupItem, b3 );
+ addItemToBuffer( m_groupItem, b3 );
+
+ Transfer* t4 = createTransfer( f3, s3, b3 ); //we get no ack from this packet
+ send( t4 );
+
+ sendEditEnd();
+}
+
+void SSIModifyTask::updateSSIManager()
+{
+ if ( m_oldItem.isValid() && m_newItem.isValid() )
+ {
+ if ( m_opSubject == Contact )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl;
+ m_ssiManager->removeContact( m_oldItem.name() );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl;
+ m_ssiManager->newContact( m_newItem );
+ }
+ else if ( m_opSubject == Group )
+ {
+ if ( m_opType == Rename )
+ m_ssiManager->updateGroup( m_newItem );
+ else if ( m_opType == Change )
+ m_ssiManager->updateContact( m_newItem );
+ }
+ else if ( m_opSubject == NoSubject )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << endl;
+ m_ssiManager->removeItem( m_oldItem );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "and adding " << m_newItem.name() << " to SSI manager" << endl;
+ m_ssiManager->newItem( m_newItem );
+ }
+ setSuccess( 0, QString::null );
+ return;
+ }
+
+ if ( m_oldItem.isValid() && !m_newItem )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << m_oldItem.name() << " from SSI manager" << endl;
+ if ( m_opSubject == Group )
+ m_ssiManager->removeGroup( m_oldItem.name() );
+ else if ( m_opSubject == Contact )
+ m_ssiManager->removeContact( m_oldItem.name() );
+ else if ( m_opSubject == NoSubject )
+ m_ssiManager->removeItem( m_oldItem );
+ setSuccess( 0, QString::null );
+ return;
+ }
+
+ if ( m_newItem.isValid() && !m_oldItem )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << m_newItem.name() << " to SSI manager" << endl;
+ if ( m_opSubject == Group )
+ m_ssiManager->newGroup( m_newItem );
+ else if ( m_opSubject == Contact )
+ m_ssiManager->newContact( m_newItem );
+ else if ( m_opSubject == NoSubject )
+ m_ssiManager->newItem( m_newItem );
+ setSuccess( 0, QString::null );
+ return;
+ }
+
+ setSuccess( 0, QString::null );
+}
+
+void SSIModifyTask::freeIdOnError()
+{
+ if ( m_oldItem.isValid() && m_newItem.isValid() )
+ {
+ if ( m_opSubject == Contact || m_opSubject == NoSubject )
+ {
+ if ( m_oldItem.bid() != m_newItem.bid() )
+ m_ssiManager->removeID( m_newItem );
+ }
+ else if ( m_opSubject == Group )
+ {
+ if ( m_oldItem.gid() != m_newItem.gid() )
+ m_ssiManager->removeID( m_newItem );
+ }
+ }
+ else if ( m_newItem.isValid() && !m_oldItem )
+ {
+ if ( m_opSubject == Group || m_opSubject == Contact ||
+ m_opSubject == NoSubject )
+ {
+ m_ssiManager->removeID( m_newItem );
+ }
+ }
+}
+
+void SSIModifyTask::sendEditStart()
+{
+ SNAC editStartSnac = { 0x0013, 0x0011, 0x0000, client()->snacSequence() };
+ FLAP editStart = { 0x02, 0, 10 };
+ Buffer* emptyBuffer = new Buffer;
+ Transfer* t1 = createTransfer( editStart, editStartSnac, emptyBuffer );
+ send( t1 );
+}
+
+void SSIModifyTask::sendEditEnd()
+{
+ SNAC editEndSnac = { 0x0013, 0x0012, 0x0000, client()->snacSequence() };
+ FLAP editEnd = { 0x02, 0, 10 } ;
+ Buffer* emptyBuffer = new Buffer;
+ Transfer *t5 = createTransfer( editEnd, editEndSnac, emptyBuffer );
+ send( t5 );
+}
+
+void SSIModifyTask::addItemToBuffer( Oscar::SSI item, Buffer* buffer )
+{
+ buffer->addBSTR( item.name().latin1() );
+ buffer->addWord( item.gid() );
+ buffer->addWord( item.bid() );
+ buffer->addWord( item.type() );
+ buffer->addWord( item.tlvListLength() );
+
+ QValueList<TLV>::const_iterator it = item.tlvList().begin();
+ QValueList<TLV>::const_iterator listEnd = item.tlvList().end();
+ for( ; it != listEnd; ++it )
+ buffer->addTLV( ( *it ) );
+}
+
+Oscar::SSI SSIModifyTask::getItemFromBuffer( Buffer* buffer ) const
+{
+ QValueList<TLV> tlvList;
+
+ WORD strlength = buffer->getWord();
+ QString itemName = QString::fromUtf8( buffer->getBlock( strlength ), strlength );
+ WORD groupId = buffer->getWord();
+ WORD itemId = buffer->getWord();
+ WORD itemType = buffer->getWord();
+ WORD tlvLength = buffer->getWord();
+ for ( int i = 0; i < tlvLength; )
+ {
+ TLV t = buffer->getTLV();
+ i += 4;
+ i += t.length;
+ tlvList.append( t );
+ }
+
+ if ( itemType == ROSTER_CONTACT )
+ itemName = Oscar::normalize( itemName );
+
+ return Oscar::SSI( itemName, groupId, itemId, itemType, tlvList );
+}
+
+void SSIModifyTask::handleSSIAdd()
+{
+ Buffer* b = transfer()->buffer();
+
+ while ( b->length() > 0 )
+ {
+ Oscar::SSI item = getItemFromBuffer( b );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Adding " << item.name() << " to SSI manager" << endl;
+
+ if ( item.type() == ROSTER_GROUP )
+ m_ssiManager->newGroup( item );
+ else if ( item.type() == ROSTER_CONTACT )
+ m_ssiManager->newContact( item );
+ else
+ m_ssiManager->newItem( item );
+ }
+}
+
+void SSIModifyTask::handleSSIUpdate()
+{
+ Buffer* b = transfer()->buffer();
+
+ while ( b->length() > 0 )
+ {
+ Oscar::SSI item = getItemFromBuffer( b );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Updating " << item.name() << " in SSI manager" << endl;
+
+ if ( item.type() == ROSTER_GROUP )
+ m_ssiManager->updateGroup( item );
+ else if ( item.type() == ROSTER_CONTACT )
+ m_ssiManager->updateContact( item );
+ else
+ m_ssiManager->updateItem( item );
+ }
+}
+
+void SSIModifyTask::handleSSIRemove()
+{
+ Buffer* b = transfer()->buffer();
+
+ while ( b->length() > 0 )
+ {
+ Oscar::SSI item = getItemFromBuffer( b );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Removing " << item.name() << " from SSI manager" << endl;
+
+ if ( item.type() == ROSTER_GROUP )
+ m_ssiManager->removeGroup( item );
+ else if ( item.type() == ROSTER_CONTACT )
+ m_ssiManager->removeContact( item );
+ else
+ m_ssiManager->removeItem( item );
+ }
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/ssimodifytask.h b/kopete/protocols/oscar/liboscar/ssimodifytask.h
new file mode 100644
index 00000000..2c32dda3
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssimodifytask.h
@@ -0,0 +1,156 @@
+/*
+ Kopete Oscar Protocol
+ ssimodifytask.h - Handles all the ssi modification stuff
+
+ Copyright (c) 2004 by Kopete Developers <kopete-devel@kde.org>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SSIMODIFYTASK_H
+#define SSIMODIFYTASK_H
+
+#include "task.h"
+#include "oscartypes.h"
+#include "ssimanager.h"
+
+
+class Buffer;
+
+/**
+This class takes care of any SSI list modifications that need to be made. This includes:
+@li adds
+@li edits
+@li removes
+@li group changes
+@li alias changes
+@li authorization changes
+etc.
+
+This task implements the following SNACs from the SSI family (0x0013):
+@li 0x0008
+@li 0x0009
+@li 0x000A
+@li 0x000E
+@li 0x0011
+@li 0x0012
+
+@author Matt Rogers
+*/
+class SSIModifyTask : public Task
+{
+public:
+ SSIModifyTask( Task* parent, bool staticTask = false );
+ ~SSIModifyTask();
+
+ virtual void onGo();
+ virtual bool take( Transfer* transfer );
+
+ /* Contact properties */
+ enum OperationType { NoType = 0x00, Add = 0x10, Remove = 0x20, Rename = 0x40, Change = 0x80 };
+ enum OperationSubject { NoSubject = 0x000, Contact = 0x100, Group = 0x200, Visibility = 0x400, Invisibility = 0x800 };
+
+ //! Set up the stuff needed to add a contact.
+ //! @return true if we can send the packet
+ bool addContact( const QString& contact, const QString& group, bool requiresAuth = false );
+
+ //! Set up the stuff needed to remove a contact.
+ //! @return true if we can send the packet
+ bool removeContact( const QString& contact );
+
+ //! Set up the stuff needed to change groups
+ //! @return true if we can send the packet
+ bool changeGroup( const QString& contact, const QString& newGroup );
+
+ /* Group properties */
+
+ //! Add a new group to the SSI list
+ //! @return true if we can send the packet
+ bool addGroup( const QString& groupName );
+
+ //! Remove a group from the SSI list
+ //! @return true if we can send the packet
+ bool removeGroup( const QString& groupName );
+
+ //! Rename a group on the SSI list
+ //! @return true if we can send the packet
+ bool renameGroup( const QString& oldName, const QString& newName );
+
+ //! Add an item to the SSI list
+ //! Should be used for other items we don't have explicit functions for
+ //! like icon hashs, privacy settings, non-icq contacts, etc.
+ bool addItem( const SSI& item );
+
+ //! Remove an item from the SSI list
+ //! Should be used for other items we don't have explicit functions for
+ //! like icon hashs, privacy settings, non-icq contacts, etc.
+ bool removeItem( const SSI& item );
+
+ //! Modify an item on the SSI list
+ //! Should be used for other items we don't have explicit functions for
+ //! like icon hashs, privacy settings, non-icq contacts, etc.
+ bool modifyItem( const SSI& oldItem, const SSI& newItem );
+
+protected:
+ virtual bool forMe( const Transfer* transfer ) const;
+
+private:
+ //! Handle the acknowledgement from the server
+ void handleSSIAck();
+
+ //! Construct and send the packet to send to the server
+ void sendSSIUpdate();
+
+ //! Helper function to change the group on the server
+ void changeGroupOnServer();
+
+ //! Update the SSI Manager with the new data
+ void updateSSIManager();
+
+ //! Helper function to free id on error
+ void freeIdOnError();
+
+ //! Send the SSI edit start packet
+ void sendEditStart();
+
+ //! Send the SSI edit end packet
+ void sendEditEnd();
+
+ void addItemToBuffer( Oscar::SSI item, Buffer* buffer );
+ Oscar::SSI getItemFromBuffer( Buffer* buffer ) const;
+
+ //! Handle server request to add item
+ void handleSSIAdd();
+
+ //! Handle server request to update item
+ void handleSSIUpdate();
+
+ //! Handle server request to remove item
+ void handleSSIRemove();
+
+private:
+ SSI m_oldItem;
+ SSI m_newItem;
+ SSI m_groupItem;
+ OperationType m_opType;
+ OperationSubject m_opSubject;
+ WORD m_id;
+ SSIManager* m_ssiManager;
+ bool m_static;
+
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands
diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.cpp b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp
new file mode 100644
index 00000000..0be172e8
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiparamstask.cpp
@@ -0,0 +1,102 @@
+/*
+ Kopete Oscar Protocol
+ ssiparamstask.cpp - Get the SSI parameters so we can use them
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "ssiparamstask.h"
+#include <kdebug.h>
+#include "buffer.h"
+#include "connection.h"
+#include "transfer.h"
+#include "oscartypes.h"
+#include "oscarutils.h"
+#include "ssimanager.h"
+
+using namespace Oscar;
+
+SSIParamsTask::SSIParamsTask(Task* parent): Task(parent)
+{
+}
+
+
+SSIParamsTask::~SSIParamsTask()
+{
+}
+
+
+bool SSIParamsTask::forMe(const Transfer* transfer) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0013 && st->snacSubtype() == 0x0003 )
+ return true;
+
+ return false;
+}
+
+bool SSIParamsTask::take(Transfer* transfer)
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ handleParamReply();
+ setTransfer( 0 );
+ return true;
+ }
+
+ return false;
+}
+
+void SSIParamsTask::onGo()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0013, 0x0002, 0x0000, client()->snacSequence() };
+
+ Buffer* buffer = new Buffer();
+ buffer->addTLV16( 0x000B, 0x000F );
+
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+}
+
+void SSIParamsTask::handleParamReply()
+{
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Getting SSI parameters" << endl;
+ Buffer* buf = transfer()->buffer();
+ //manually parse the TLV out of the packet, since we only want certain things
+ if ( buf->getWord() != 0x0004 )
+ {
+ setError( -1, QString::null );
+ return; //no TLV of type 0x0004, bad packet. do nothing.
+ }
+ else
+ {
+ buf->skipBytes( 2 ); //the tlv length
+ WORD maxContacts = buf->getWord();
+ WORD maxGroups = buf->getWord();
+ WORD maxVisible = buf->getWord();
+ WORD maxInvisible = buf->getWord();
+ buf->skipBytes( 20 );
+ WORD maxIgnore = buf->getWord();
+ client()->ssiManager()->setParameters( maxContacts, maxGroups, maxVisible, maxInvisible, maxIgnore );
+ }
+ setSuccess( 0, QString::null );
+}
+
+// kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/liboscar/ssiparamstask.h b/kopete/protocols/oscar/liboscar/ssiparamstask.h
new file mode 100644
index 00000000..abf12aa2
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/ssiparamstask.h
@@ -0,0 +1,43 @@
+/*
+ Kopete Oscar Protocol
+ ssiparamstask.h - Get the SSI parameters so we can use them
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef SSIPARAMSTASK_H
+#define SSIPARAMSTASK_H
+
+#include "task.h"
+
+/**
+@author Kopete Developers
+*/
+class SSIParamsTask : public Task
+{
+public:
+ SSIParamsTask(Task* parent);
+
+ ~SSIParamsTask();
+
+ virtual bool forMe(const Transfer* transfer) const;
+ virtual bool take(Transfer* transfer);
+ virtual void onGo();
+
+private:
+ void handleParamReply();
+};
+
+#endif
+
+// kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/stream.cpp b/kopete/protocols/oscar/liboscar/stream.cpp
new file mode 100644
index 00000000..02967416
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/stream.cpp
@@ -0,0 +1,31 @@
+/*
+ stream.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "stream.h"
+
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+#include "stream.moc"
diff --git a/kopete/protocols/oscar/liboscar/stream.h b/kopete/protocols/oscar/liboscar/stream.h
new file mode 100644
index 00000000..9fbacbda
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/stream.h
@@ -0,0 +1,75 @@
+/*
+ stream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Based on code copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qobject.h>
+
+#ifndef OSCAR_STREAM_H
+#define OSCAR_STREAM_H
+
+class Transfer;
+
+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;
+
+ /**
+ * 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( Transfer *request) = 0;
+
+
+signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead(); //signals that there is a transfer ready to be read
+// void stanzaWritten();
+ void error(int);
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/task.cpp b/kopete/protocols/oscar/liboscar/task.cpp
new file mode 100644
index 00000000..2c7628d7
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/task.cpp
@@ -0,0 +1,291 @@
+/*
+ task.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+
+#include "connection.h"
+#include "transfer.h"
+#include "safedelete.h"
+#include "buffer.h"
+#include "task.h"
+
+
+class Task::TaskPrivate
+{
+public:
+ TaskPrivate() {}
+
+ Q_UINT32 id;
+ bool success;
+ int statusCode;
+ QString statusString;
+ Connection* client;
+ bool insignificant, deleteme, autoDelete;
+ bool done;
+ Transfer* transfer;
+};
+
+Task::Task(Task *parent)
+:QObject(parent)
+{
+ init();
+ d->client = parent->client();
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::Task(Connection* parent, bool)
+:QObject(0)
+{
+ init();
+ d->client = parent;
+ connect(d->client, SIGNAL(disconnected()), SLOT(clientDisconnected()));
+}
+
+Task::~Task()
+{
+ delete d->transfer;
+ 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->id = 0;
+}
+
+Task *Task::parent() const
+{
+ return (Task *)QObject::parent();
+}
+
+Connection *Task::client() const
+{
+ return d->client;
+}
+
+Transfer * Task::transfer() const
+{
+ return d->transfer;
+}
+
+void Task::setTransfer( Transfer * transfer )
+{
+ d->transfer = transfer;
+}
+
+long 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 ))
+ {
+ //qDebug( "Transfer ACCEPTED by: %s", t->className() );
+ return true;
+ }
+ //else
+ //qDebug( "Transfer refused by: %s", t->className() );
+ }
+
+ return false;
+}
+
+void Task::safeDelete()
+{
+ if(d->deleteme)
+ return;
+
+ d->deleteme = true;
+ if(!d->insignificant)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+ qDebug( "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( Transfer * 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;
+ 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();
+}
+
+Transfer* Task::createTransfer( struct FLAP f, struct SNAC s, Buffer* buffer )
+{
+ return new SnacTransfer( f, s, buffer );
+}
+
+Transfer* Task::createTransfer( struct FLAP f, Buffer* buffer )
+{
+ return new FlapTransfer( f, buffer );
+}
+
+Transfer* Task::createTransfer( Buffer* buffer )
+{
+ return new Transfer( buffer );
+}
+
+
+// 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)
+{
+ //black hole
+ Q_UNUSED( str );
+ //client()->debug(QString("%1: ").arg(className()) + str);
+}
+
+bool Task::forMe( const Transfer * transfer ) const
+{
+ Q_UNUSED( transfer );
+ return false;
+}
+
+void Task::setId( Q_UINT32 id )
+{
+ d->id = id;
+}
+
+#include "task.moc"
+
+//kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/liboscar/task.h b/kopete/protocols/oscar/liboscar/task.h
new file mode 100644
index 00000000..e48e02de
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/task.h
@@ -0,0 +1,116 @@
+/*
+ task.h - Kopete Oscar Protocol
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+ Based on code copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCAR_TASK_H
+#define OSCAR_TASK_H
+
+#include <qobject.h>
+
+#include "oscartypes.h"
+
+
+class QString;
+class Buffer;
+class Connection;
+class Transfer;
+
+using namespace Oscar;
+
+
+class Task : public QObject
+{
+ Q_OBJECT
+public:
+ enum { ErrDisc };
+ Task(Task *parent);
+ Task( Connection*, bool isRoot );
+ virtual ~Task();
+
+ Task *parent() const;
+ Connection* client() const;
+ Transfer *transfer() const;
+
+ long id() const;
+ void setId();
+
+ 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();
+
+ /**
+ * Direct setter for Tasks which don't have any fields
+ */
+ void setTransfer( Transfer * transfer );
+
+signals:
+ void finished();
+
+protected:
+ virtual void onGo();
+ virtual void onDisconnect();
+ void setId( Q_UINT32 id );
+ void send( Transfer * request );
+ void setSuccess( int code=0, const QString &str="" );
+ 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 flap, snac, and buffer
+ */
+ Transfer* createTransfer( FLAP f, SNAC s, Buffer* buffer );
+
+ /**
+ * Creates a transfer with the given flap and buffer
+ */
+ Transfer* createTransfer( FLAP f, Buffer* buffer );
+
+ /**
+ * Creates a transfer with the given buffer
+ */
+ Transfer* createTransfer( Buffer* buffer );
+
+private slots:
+ void clientDisconnected();
+ void done();
+
+private:
+ void init();
+
+ class TaskPrivate;
+ TaskPrivate *d;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/Makefile.am b/kopete/protocols/oscar/liboscar/tests/Makefile.am
new file mode 100644
index 00000000..9dc6b292
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/Makefile.am
@@ -0,0 +1,28 @@
+INCLUDES = $(all_includes) $(KOPETE_INCLUDES) -I$(top_srcdir)/kopete/protocols/oscar/liboscar
+METASOURCES = AUTO
+check_PROGRAMS = kunittest clientstream_test logintest userinfotest ssigrouptest redirecttest ipaddrtest
+
+kunittest_SOURCES = main.cpp kunittest.cpp chatnavtests.cpp
+kunittest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kunittest_LDADD = $(LIB_KDECORE) ../liboscar.la
+
+clientstream_test_SOURCES = clientstream_test.cpp
+clientstream_test_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+logintest_SOURCES = logintest.cpp
+logintest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+userinfotest_SOURCES = userinfotest.cpp
+userinfotest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+ssigrouptest_SOURCES = ssigrouptest.cpp
+ssigrouptest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+redirecttest_SOURCES = redirecttest.cpp
+redirecttest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+ipaddrtest_SOURCES = ipaddrtest.cpp
+ipaddrtest_LDADD = $(LIB_QT) $(LIB_KDECORE) ../liboscar.la
+
+check: kunittest
+ @./kunittest 2>&1 | grep "tests:"
diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp
new file mode 100644
index 00000000..07a89f98
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.cpp
@@ -0,0 +1,305 @@
+/*
+ Kopete Oscar Protocol - Chat Navigation parsing tests
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatnavtests.h"
+
+#include <iostream>
+
+#include "buffer.h"
+#include "oscartypeclasses.h"
+
+using namespace std;
+using namespace Oscar;
+
+
+ChatNavTests::ChatNavTests()
+{
+ m_buffer = 0;
+}
+
+
+ChatNavTests::~ChatNavTests()
+{
+}
+
+void ChatNavTests::setupExchangeTestBuffer()
+{
+ delete m_buffer;
+ m_buffer = 0;
+
+ m_buffer = new Buffer();
+ //TLV 0x02
+ m_buffer->addDWord(0x00020001);
+ m_buffer->addByte(0x03);
+ //TLV 0x03
+ m_buffer->addDWord(0x0003003C);
+ m_buffer->addDWord(0x0001000a);
+ m_buffer->addDWord(0x00030001);
+ m_buffer->addDWord(0x14000400);
+ m_buffer->addDWord(0x02200000);
+ m_buffer->addDWord(0xC9000200);
+ m_buffer->addDWord(0x4400CA00);
+ m_buffer->addDWord(0x04000000);
+ m_buffer->addDWord(0x0000D000);
+ m_buffer->addDWord(0x0000D100);
+ m_buffer->addDWord(0x0207D000);
+ m_buffer->addDWord(0xD2000200);
+ m_buffer->addDWord(0x2F00D400);
+ m_buffer->addDWord(0x0000D500);
+ m_buffer->addDWord(0x010100DA);
+ m_buffer->addDWord(0x00020066);
+}
+
+void ChatNavTests::setupRoomInfoTestBuffer()
+{
+
+ delete m_buffer;
+ m_buffer = 0;
+
+ m_buffer = new Buffer();
+ //TLV 0x04
+ m_buffer->addDWord(0x000400F8);
+ m_buffer->addWord(0x0004); //exchange
+ m_buffer->addByte(0x28); //cookie length
+ m_buffer->addByte(0x21); //start of cookie
+ m_buffer->addDWord(0x616F6C3A);
+ m_buffer->addDWord(0x2F2F3237);
+ m_buffer->addDWord(0x31393A31);
+ m_buffer->addDWord(0x302D342D);
+ m_buffer->addDWord(0x63686174);
+ m_buffer->addDWord(0x37343739);
+ m_buffer->addDWord(0x33333134);
+ m_buffer->addDWord(0x30313137);
+ m_buffer->addDWord(0x37393435);
+ m_buffer->addDWord(0x36363500);
+ m_buffer->addDWord(0x00020016);
+ m_buffer->addDWord(0x00660002);
+ m_buffer->addDWord(0x00000068);
+ m_buffer->addDWord(0x00040000);
+ m_buffer->addDWord(0x0000006A);
+ m_buffer->addDWord(0x00176368);
+ m_buffer->addDWord(0x61743734);
+ m_buffer->addDWord(0x37393333);
+ m_buffer->addDWord(0x31343031);
+ m_buffer->addDWord(0x31373739);
+ m_buffer->addDWord(0x34353636);
+ m_buffer->addDWord(0x35006D00);
+ m_buffer->addDWord(0x02000000);
+ m_buffer->addDWord(0x6E000200);
+ m_buffer->addDWord(0x00006F00);
+ m_buffer->addDWord(0x02000000);
+ m_buffer->addDWord(0x71000200);
+ m_buffer->addDWord(0x00007500);
+ m_buffer->addDWord(0x04000000);
+ m_buffer->addDWord(0x0000C900);
+ m_buffer->addDWord(0x02004000);
+ m_buffer->addDWord(0xCA000442);
+ m_buffer->addDWord(0xBEF90500);
+ m_buffer->addDWord(0xD0000200);
+ m_buffer->addDWord(0x0300D100);
+ m_buffer->addDWord(0x0207D000);
+ m_buffer->addDWord(0xD2000200);
+ m_buffer->addDWord(0x2600D300);
+ m_buffer->addDWord(0x17636861);
+ m_buffer->addDWord(0x74373437);
+ m_buffer->addDWord(0x39333331);
+ m_buffer->addDWord(0x34303131);
+ m_buffer->addDWord(0x37373934);
+ m_buffer->addDWord(0x35363635);
+ m_buffer->addDWord(0x00D40000);
+ m_buffer->addDWord(0x00D50001);
+ m_buffer->addDWord(0x0100D600);
+ m_buffer->addDWord(0x0875732D);
+ m_buffer->addDWord(0x61736369);
+ m_buffer->addDWord(0x6900D700);
+ m_buffer->addDWord(0x02656E00);
+ m_buffer->addDWord(0xD8000875);
+ m_buffer->addDWord(0x732D6173);
+ m_buffer->addDWord(0x63696900);
+ m_buffer->addDWord(0xD9000265);
+ m_buffer->addDWord(0x6E00DB00);
+ m_buffer->addDWord(0x0D756578);
+ m_buffer->addDWord(0x742F782D);
+ m_buffer->addDWord(0x616F6C72);
+ m_buffer->addDWord(0x746600DA);
+ m_buffer->addDWord(0x000200E8);
+}
+
+void ChatNavTests::allTests()
+{
+ exchangeParsingTest();
+ roominfoParsingTest();
+}
+
+void ChatNavTests::exchangeParsingTest()
+{
+ setupExchangeTestBuffer();
+ Buffer testBuffer(*m_buffer);
+ CHECK( testBuffer.length() != 0, true );
+ while ( testBuffer.length() != 0 )
+ {
+ TLV t = testBuffer.getTLV();
+ if ( t.type == 0x0002 )
+ {
+// cout << "Max concurrent rooms: " << t.data << endl;
+ }
+
+ t = testBuffer.getTLV();
+ if ( t.type == 0x0003 )
+ {
+// cout << "TLV of type 3 with length " << t.length << endl;
+ Buffer b(t.data);
+ WORD id = b.getWord();
+ CHECK( id > 0, true );
+ int tlvCount = b.getWord();
+ int realCount = 0;
+// cout << "Expecting " << tlvCount << " TLVs" << endl;
+ while ( b.length() > 0 )
+ {
+ TLV t = b.getTLV();
+ CHECK( t.type != 0, true );
+ switch (t.type)
+ {
+ case 0x02:
+ CHECK( t.length == 2, true );
+ CHECK( t.data.count() == 2, true );
+ break;
+ case 0x03:
+ CHECK( t.length == 1, true );
+ CHECK( t.data.count() == 1, true );
+ CHECK( t.data[0] > 0, true );
+ break;
+ case 0x04:
+ CHECK( t.length == 2, true );
+ CHECK( t.data.count() == 2, true );
+ //CHECK( t.data[0] > 0, true );
+ break;
+ case 0x05:
+ CHECK( t.length > 1, true );
+ break;
+ case 0x06:
+ CHECK( t.length > 2, true );
+ break;
+ case 0xCA:
+ CHECK( t.length == 4, true );
+ break;
+ case 0xD1:
+ CHECK( t.length == 2, true );
+ break;
+ case 0xD2:
+ CHECK( t.length == 2, true );
+ break;
+ case 0xD3:
+ CHECK( t.length > 0, true );
+ CHECK( t.data.count() == t.length, true );
+ break;
+ case 0xD5:
+ CHECK( t.length == 1, true );
+ break;
+ default:
+// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase);
+// cout << "unknown TLV type " << t.type << endl;
+// cout.flags(origFlags);
+ break;
+ }
+ realCount++;
+ }
+ CHECK( tlvCount == realCount, true );
+ }
+ CHECK( testBuffer.length() == 0, true );
+ }
+}
+
+void ChatNavTests::roominfoParsingTest()
+{
+ setupRoomInfoTestBuffer();
+ Buffer testBuffer(*m_buffer);
+ CHECK( testBuffer.length() != 0, true );
+ while ( testBuffer.length() != 0 )
+ {
+ TLV t = testBuffer.getTLV();
+
+// cout << "TLV of type " << t.type << " with length " << t.length << endl;
+
+
+ CHECK( t.type == 0x04, true );
+ CHECK( t.length > 8, true );
+ Buffer b( t.data );
+ CHECK( b.getWord() > 0, true );
+ BYTE cookieLength = b.getByte();
+ b.skipBytes( cookieLength );
+ CHECK( b.getWord() == 0, true );
+ CHECK( b.getByte() > 0, true );
+ int tlvCount = b.getWord();
+ int realCount = 0;
+// cout << "Expecting " << tlvCount << " TLVs" << endl;
+ while ( b.length() > 0 )
+ {
+ TLV t = b.getTLV();
+// ios::fmtflags origFlags = cout.flags(ios::hex|ios::showbase);
+// cout << "TLV of type " << t.type << endl;
+// cout.flags(origFlags);
+ CHECK( t.type != 0, true );
+ switch (t.type)
+ {
+ case 0x02:
+ CHECK( t.length == 2, true );
+ CHECK( t.data.count() == 2, true );
+ break;
+ case 0x03:
+ CHECK( t.length == 1, true );
+ CHECK( t.data.count() == 1, true );
+ CHECK( t.data[0] > 0, true );
+ break;
+ case 0x04:
+ CHECK( t.length == 2, true );
+ CHECK( t.data.count() == 2, true );
+ //CHECK( t.data[0] > 0, true );
+ break;
+ case 0x05:
+ CHECK( t.length > 1, true );
+ break;
+ case 0x06:
+ CHECK( t.length > 2, true );
+ break;
+ case 0xCA:
+ CHECK( t.length == 4, true );
+ break;
+ case 0xD1:
+ CHECK( t.length == 2, true );
+ break;
+ case 0xD2:
+ CHECK( t.length == 2, true );
+ break;
+ case 0xD3:
+ CHECK( t.length > 0, true );
+ CHECK( t.data.count() == t.length, true );
+ break;
+ case 0xD5:
+ CHECK( t.length == 1, true );
+ break;
+ default:
+ break;
+ }
+ realCount++;
+ }
+ CHECK( tlvCount == realCount, true );
+ }
+}
+
+//kate: indent-mode csands; tab-width 4;
+
+
diff --git a/kopete/protocols/oscar/liboscar/tests/chatnavtests.h b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h
new file mode 100644
index 00000000..9899682f
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/chatnavtests.h
@@ -0,0 +1,50 @@
+/*
+ Kopete Oscar Protocol - Chat Navigation parsing tests
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATNAVTESTS_H
+#define CHATNAVTESTS_H
+
+#include "tester.h"
+
+class Buffer;
+
+/**
+@author Kopete Developers
+*/
+class ChatNavTests : public Tester
+{
+public:
+ ChatNavTests();
+ ~ChatNavTests();
+
+ void allTests();
+
+// void limitsParsingTest();
+ void exchangeParsingTest();
+ void roominfoParsingTest();
+// void extRoomInfoParsingTest();
+// void memberListParsingTest();
+// void searchInfoParsingTest();
+// void createRoomParsingTest();
+
+ void setupExchangeTestBuffer();
+ void setupRoomInfoTestBuffer();
+
+private:
+ Buffer* m_buffer;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp
new file mode 100644
index 00000000..59f392de
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.cpp
@@ -0,0 +1,49 @@
+//Licensed under the GNU General Public License
+
+#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( "login.oscar.aol.com", 5190 );
+ myTestObject = new ClientStream( myConnector, myConnector);
+ // notify when the transport layer is connected
+ connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ // notify and start sending
+ //connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) );
+
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+ClientStreamTest::~ClientStreamTest()
+{
+ delete myTestObject;
+ delete myConnector;
+}
+
+void ClientStreamTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("login.oscar.aol.com");
+ // connect to server
+ qDebug( "connecting to server ");
+ myTestObject->connectToServer( server, true ); // fine up to here...
+}
+
+void ClientStreamTest::slotConnected()
+{
+ qDebug( "connection is up");
+ connected = true;
+}
+
+int main(int argc, char ** argv)
+{
+ ClientStreamTest a( argc, argv );
+ a.exec();
+ return 0;
+}
+
+#include "clientstream_test.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/clientstream_test.h b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h
new file mode 100644
index 00000000..32a0e3a9
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/clientstream_test.h
@@ -0,0 +1,49 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef clientstream_test_h
+#define clientstream_test_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class ClientStreamTest : public QApplication
+{
+Q_OBJECT
+public:
+ ClientStreamTest(int argc, char ** argv);
+
+ ~ClientStreamTest();
+
+ bool isConnected();
+
+public slots:
+ void slotDoTest();
+
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myTestObject;
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp
new file mode 100644
index 00000000..4f4e8df2
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.cpp
@@ -0,0 +1,58 @@
+//Licensed under the GNU General Public License
+
+#include <iostream>
+#include "ipaddrtest.h"
+#include <qstring.h>
+
+using namespace std;
+IPAddrTest::IPAddrTest(int argc, char ** argv)
+: QApplication( argc, argv )
+{
+}
+
+IPAddrTest::~IPAddrTest()
+{
+}
+
+bool IPAddrTest::testDottedDecimal()
+{
+ DWORD address = 1096652712;
+ return ( Oscar::getDottedDecimal( address ) == "65.93.151.168" );
+}
+
+bool IPAddrTest::testAllZeroDotted()
+{
+ DWORD address = 0;
+ return ( Oscar::getDottedDecimal( address ) == "0.0.0.0" );
+}
+
+bool IPAddrTest::testNumericalIP()
+{
+ QString address = "65.93.151.168";
+ return ( Oscar::getNumericalIP( address ) == 1096652712 );
+}
+
+bool IPAddrTest::testAllZeroNumerical()
+{
+ QString address = "0.0.0.0";
+ return ( Oscar::getNumericalIP( address ) == 0 );
+}
+
+void IPAddrTest::CheckTest(bool TestPassed)
+{
+ if ( TestPassed )
+ cout << "passed" << endl;
+ else
+ cout << "failed" << endl;
+}
+
+int main(int argc, char ** argv)
+{
+ IPAddrTest a( argc, argv );
+
+ a.CheckTest(a.testDottedDecimal());
+ a.CheckTest(a.testNumericalIP());
+ a.CheckTest(a.testAllZeroDotted() );
+ a.CheckTest( a.testAllZeroNumerical() );
+}
+
diff --git a/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h
new file mode 100644
index 00000000..9452ad82
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ipaddrtest.h
@@ -0,0 +1,35 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef IPADDRTEST_H
+#define IPADDRTEST_H
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "oscarutils.h"
+
+#define QT_FATAL_ASSERT 1
+
+class IPAddrTest : public QApplication
+{
+public:
+ IPAddrTest(int argc, char ** argv);
+ ~IPAddrTest();
+
+ bool testDottedDecimal();
+ bool testNumericalIP();
+ bool testAllZeroDotted();
+ bool testAllZeroNumerical();
+
+ void CheckTest(bool TestPassed);
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.cpp b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp
new file mode 100644
index 00000000..9f7ba693
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/kunittest.cpp
@@ -0,0 +1,167 @@
+/**
+ * kunittest.cpp
+ *
+ * Copyright (C) 2004 Zack Rusin <zack@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "kunittest.h"
+
+#include "tester.h"
+#include "chatnavtests.h"
+
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include <iostream>
+using namespace std;
+
+void KUnitTest::registerTests()
+{
+ ADD_TEST( ChatNavTests );
+// ADD_TEST( SampleTest );
+// ADD_TEST( OnePassTest );
+// ADD_TEST( TwoPassTest );
+// ADD_TEST( MultiFailTest );
+// ADD_TEST( ExpectedFailureTest );
+// ADD_TEST( UnexpectedPassTest );
+// ADD_TEST( OnlyUnexpectedPassTest );
+// ADD_TEST( SkipLogTest );
+// ADD_TEST( SkipWithFailTest );
+}
+
+KUnitTest::KUnitTest()
+{
+ QTimer::singleShot( 0, this, SLOT(checkRun()) );
+
+ m_tests.setAutoDelete( TRUE );
+// m_qtests.setAutoDelete( TRUE );
+
+ registerTests();
+}
+
+void KUnitTest::checkRun()
+{
+// if ( m_qtests.isEmpty() )
+// qApp->exit();
+}
+
+int KUnitTest::runTests()
+{
+ int globalSteps = 0;
+ int globalPasses = 0;
+ int globalFails = 0;
+ int globalXFails = 0;
+ int globalXPasses = 0;
+ int globalSkipped = 0;
+
+ cout << "# Running normal tests... #" << endl << endl;
+ QAsciiDictIterator<Tester> it( m_tests );
+
+ for( ; it.current(); ++it ) {
+ Tester* test = it.current();
+ test->allTests();
+ cout << it.currentKey() << " - ";
+ int numPass = test->testsFinished() - ( test->errorList().count() + test->xfailList().count() + test->skipList().count() );
+ int numFail = test->errorList().count() + test->xfailList().count();
+ int numXFail = test->xfailList().count();
+ int numXPass = test->xpassList().count();
+ int numSkip = test->skipList().count();
+
+ globalSteps += test->testsFinished();
+ globalPasses += numPass;
+ globalFails += numFail;
+ globalXFails += numXFail;
+ globalXPasses += numXPass;
+ globalSkipped += numSkip;
+
+ cout << numPass << " test" << ( ( 1 == numPass )?"":"s") << " passed";
+ if ( 0 < test->xpassList().count() ) {
+ cout << " (" << numXPass << " unexpected pass" << ( ( 1 == numXPass )?"":"es") << ")";
+ }
+ cout << ", " << numFail << " test" << ( ( 1 == numFail )?"":"s") << " failed";
+ if ( 0 < numXFail ) {
+ cout << " (" << numXFail << " expected failure" << ( ( 1 == numXFail )?"":"s") << ")";
+ }
+ if ( 0 < numSkip ) {
+ cout << "; also " << numSkip << " skipped";
+ }
+ cout << endl;
+
+ if ( 0 < numXPass ) {
+ cout << " Unexpected pass" << ( ( 1 == numXPass )?"":"es") << ":" << endl;
+ QStringList list = test->xpassList();
+ for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) {
+ cout << "\t" << (*itr).latin1() << endl;
+ }
+ }
+ if ( 0 < (numFail - numXFail) ) {
+ cout << " Unexpected failure" << ( ( 1 == numFail )?"":"s") << ":" << endl;
+ QStringList list = test->errorList();
+ for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) {
+ cout << "\t" << (*itr).latin1() << endl;
+ }
+ }
+ if ( 0 < numXFail ) {
+ cout << " Expected failure" << ( ( 1 == numXFail)?"":"s") << ":" << endl;
+ QStringList list = test->xfailList();
+ for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) {
+ cout << "\t" << (*itr).latin1() << endl;
+ }
+ }
+ if ( 0 < numSkip ) {
+ cout << " Skipped test" << ( ( 1 == numSkip )?"":"s") << ":" << endl;
+ QStringList list = test->skipList();
+ for ( QStringList::Iterator itr = list.begin(); itr != list.end(); ++itr ) {
+ cout << "\t" << (*itr).latin1() << endl;
+ }
+ }
+ cout << endl;
+ }
+
+ cout << "# Done with normal tests:" << endl;
+ cout << " Total test cases: " << m_tests.count() << endl;
+ cout << " Total test steps : " << globalSteps << endl;
+ cout << " Total passed test steps (including unexpected) : " << globalPasses << endl;
+ cout << " Total unexpected passed test steps : " << globalXPasses << endl;
+ cout << " Total failed test steps (including expected) : " << globalFails << endl;
+ cout << " Total expected failed test steps : " << globalXFails << endl;
+ cout << " Total skipped test steps : " << globalSkipped << endl;
+
+ return m_tests.count();
+}
+
+//void KUnitTest::addTester( QTester *test )
+//{
+// m_qtests.insert( test, test );
+// connect( test, SIGNAL(destroyed(QObject*)),
+// SLOT(qtesterDone(QObject* )) );
+//}
+
+void KUnitTest::qtesterDone( QObject *obj )
+{
+// m_qtests.remove( obj );
+// if ( m_qtests.isEmpty() )
+// qApp->quit();
+}
+
+#include "kunittest.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/kunittest.h b/kopete/protocols/oscar/liboscar/tests/kunittest.h
new file mode 100644
index 00000000..5148a82a
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/kunittest.h
@@ -0,0 +1,71 @@
+/**
+ * kunittest.h
+ *
+ * Copyright (C) 2004 Zack Rusin <zack@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef KUNITTEST_H
+#define KUNITTEST_H
+
+#include "tester.h"
+
+#include <qobject.h>
+#include <qasciidict.h>
+#include <qptrdict.h>
+
+#define ADD_TEST(x) addTester( #x, new x )
+#define ADD_QTEST(x) addTester( new x )
+
+class KUnitTest : public QObject
+{
+ Q_OBJECT
+public:
+ KUnitTest();
+
+ int runTests();
+public:
+ void addTester( const char *name, Tester* test )
+ {
+ m_tests.insert( name, test );
+ }
+// void addTester( QTester *test );
+
+private slots:
+ void qtesterDone( QObject *obj );
+ void checkRun();
+
+private:
+ void registerTests();
+
+private:
+ QAsciiDict<Tester> m_tests;
+// QPtrDict<QTester> m_qtests;
+ int globalTests;
+ int globalPasses;
+ int globalFails;
+ int globalXFails;
+ int globalXPasses;
+ int globalSkipped;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.cpp b/kopete/protocols/oscar/liboscar/tests/logintest.cpp
new file mode 100644
index 00000000..6dbc9646
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/logintest.cpp
@@ -0,0 +1,56 @@
+//Licensed under the GNU General Public License
+
+#include "logintest.h"
+
+LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ myConnector->setOptHostPort( "login.oscar.aol.com", 5190 );
+ myTestObject = new ClientStream( myConnector, myConnector);
+
+ // notify when the transport layer is connected
+ //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ myClient = new Client();
+
+ myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" );
+ myConnection->setClient( myClient );
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+LoginTest::~LoginTest()
+{
+ delete myTestObject;
+ delete myConnector;
+ delete myClient;
+}
+
+void LoginTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("login.oscar.aol.com");
+ // connect to server
+ qDebug( "connecting to server ");
+
+ myClient->setIsIcq( true );
+ myClient->start( server, 5190, "userid", "password" );
+ myClient->connectToServer( myConnection, server, true );
+ connected = true;
+}
+
+void LoginTest::slotConnected()
+{
+ qDebug( "connection is up");
+ connected = true;
+}
+
+int main(int argc, char ** argv)
+{
+ LoginTest a( argc, argv );
+ a.exec();
+ if ( !a.isConnected() )
+ return 0;
+}
+
+#include "logintest.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/logintest.h b/kopete/protocols/oscar/liboscar/tests/logintest.h
new file mode 100644
index 00000000..898a3d99
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/logintest.h
@@ -0,0 +1,53 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef logintest_h
+#define logintest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+#include "client.h"
+#include "connection.h"
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class LoginTest : public QApplication
+{
+Q_OBJECT
+public:
+ LoginTest(int argc, char ** argv);
+
+ ~LoginTest();
+
+ bool isConnected() { return connected; }
+
+public slots:
+ void slotDoTest();
+
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myTestObject;
+ Client* myClient;
+ Connection* myConnection;
+
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/main.cpp b/kopete/protocols/oscar/liboscar/tests/main.cpp
new file mode 100644
index 00000000..49966924
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/main.cpp
@@ -0,0 +1,35 @@
+/**
+ * main.cpp
+ *
+ * Copyright (C) 2004 Zack Rusin <zack@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "kunittest.h"
+
+int main( int argc, char** argv )
+{
+ Q_UNUSED( argc );
+ Q_UNUSED( argv );
+ KUnitTest tests;
+ return tests.runTests();
+}
diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp
new file mode 100644
index 00000000..a220e13e
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.cpp
@@ -0,0 +1,117 @@
+//Licensed under the GNU General Public License
+
+#include <iostream>
+#include "redirecttest.h"
+#include <qcstring.h>
+
+using namespace std;
+RedirectTest::RedirectTest(int argc, char ** argv)
+: QApplication( argc, argv ),
+ m_transfer(0),
+ m_task(0)
+{
+ m_root = new Task(0, true);
+}
+
+RedirectTest::~RedirectTest()
+{
+ delete m_root;
+}
+
+void RedirectTest::Setup()
+{
+ m_transfer = new SnacTransfer;
+ m_task = new ServerRedirectTask( m_root );
+}
+
+void RedirectTest::Teardown()
+{
+ delete m_task;
+ m_task = 0;
+ m_transfer = 0;
+}
+
+bool RedirectTest::testHandleRedirect()
+{
+ Buffer* b = SetupBuffer(0x0010, "REDF$");
+ m_transfer->setBuffer(b);
+
+ m_task->setService(0x0010);
+ m_task->setTransfer(m_transfer);
+ return m_task->handleRedirect();
+}
+
+bool RedirectTest::testInvalidService()
+{
+ Buffer* b = SetupBuffer(0x4321, "REDF$");
+ m_transfer->setBuffer(b);
+
+ m_task->setService(0x0010);
+ m_task->setTransfer(m_transfer);
+ return !m_task->handleRedirect();
+}
+
+bool RedirectTest::testInvalidCookie()
+{
+ Buffer* b = SetupBuffer(0x0010, "");
+ m_transfer->setBuffer(b);
+
+ m_task->setService(0x0010);
+ m_task->setTransfer(m_transfer);
+ return !m_task->handleRedirect();
+}
+
+bool RedirectTest::testCookieIsSet()
+{
+ Buffer* b = SetupBuffer(0x0010, "grouch");
+ m_transfer->setBuffer(b);
+
+ m_task->setService(0x0010);
+ m_task->setTransfer(m_transfer);
+ m_task->handleRedirect();
+
+ return !m_task->cookie().isEmpty();
+}
+
+Buffer* RedirectTest::SetupBuffer(WORD Service, QString Cookie)
+{
+ Buffer* b = new Buffer;
+ b->addTLV16(0x000D, Service);
+ b->addWord(0x0005);
+ b->addWord(0x0010);
+ b->addString("65.86.43.45:5190", 16);
+ b->addWord(0x0006);
+ b->addWord(Cookie.length());
+ b->addString(Cookie.latin1(), Cookie.length());
+ return b;
+}
+
+void RedirectTest::CheckTest(bool TestPassed)
+{
+ if ( TestPassed )
+ cout << "passed" << endl;
+ else
+ cout << "failed" << endl;
+}
+
+int main(int argc, char ** argv)
+{
+ RedirectTest a( argc, argv );
+
+ a.Setup();
+ a.CheckTest(a.testHandleRedirect());
+ a.Teardown();
+
+ a.Setup();
+ a.CheckTest(a.testInvalidService());
+ a.Teardown();
+
+ a.Setup();
+ a.CheckTest(a.testInvalidCookie());
+ a.Teardown();
+
+ a.Setup();
+ a.CheckTest(a.testCookieIsSet());
+ a.Teardown();
+}
+
diff --git a/kopete/protocols/oscar/liboscar/tests/redirecttest.h b/kopete/protocols/oscar/liboscar/tests/redirecttest.h
new file mode 100644
index 00000000..eda5d67a
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/redirecttest.h
@@ -0,0 +1,51 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef RedirectTest_h
+#define RedirectTest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "transfer.h"
+#include "oscartypes.h"
+#include "serverredirecttask.h"
+#include "task.h"
+
+#define QT_FATAL_ASSERT 1
+
+class RedirectTest : public QApplication
+{
+public:
+ RedirectTest(int argc, char ** argv);
+ ~RedirectTest();
+
+ bool testHandleRedirect();
+ bool testInvalidService();
+ bool testInvalidCookie();
+ bool testCookieIsSet();
+
+ void Setup();
+ void Teardown();
+
+ void CheckTest(bool TestPassed);
+
+private:
+ //Helper functions
+ Buffer* SetupBuffer(WORD Service, QString Cookie);
+
+ Task *m_root;
+ SnacTransfer * m_transfer;
+ ServerRedirectTask* m_task;
+
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp
new file mode 100644
index 00000000..a1a9e754
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.cpp
@@ -0,0 +1,73 @@
+//Licensed under the GNU General Public License
+
+#include "ssigrouptest.h"
+
+LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ myConnector->setOptHostPort( "login.oscar.aol.com", 5190 );
+ myTestObject = new ClientStream( myConnector, myConnector);
+
+ // notify when the transport layer is connected
+ //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ myClient = new Client();
+
+ myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" );
+ myConnection->setClient( myClient );
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+LoginTest::~LoginTest()
+{
+ delete myTestObject;
+ delete myConnector;
+ delete myClient;
+}
+
+void LoginTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("login.oscar.aol.com");
+ // connect to server
+ qDebug( "connecting to server ");
+
+ myClient->setIsIcq( true );
+ myClient->start( server, 5190, "userid", "password" );
+ myClient->connectToServer( myConnection, server, true );
+ QTimer::singleShot( 10000, this, SLOT(runAddGroupTest() ) );
+ connected = true;
+}
+
+void LoginTest::slotConnected()
+{
+ qDebug( "connection is up");
+ connected = true;
+}
+
+int main(int argc, char ** argv)
+{
+ LoginTest a( argc, argv );
+ a.exec();
+ if ( !a.isConnected() )
+ return 0;
+}
+
+void LoginTest::runAddGroupTest()
+{
+ qDebug( "running ssi group add test" );
+ QString group = QString::fromLatin1( "dummygroup" );
+ myClient->addGroup( group );
+ QTimer::singleShot( 5000, this, SLOT( runDelGroupTest() ) );
+}
+
+void LoginTest::runDelGroupTest()
+{
+ qDebug( "running ssi group del test" );
+ QString group = QString::fromLatin1( "dummygroup" );
+ myClient->removeGroup( group );
+}
+
+
+#include "ssigrouptest.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h
new file mode 100644
index 00000000..361c053b
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ssigrouptest.h
@@ -0,0 +1,54 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef logintest_h
+#define logintest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+#include "client.h"
+#include "connection.h"
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class LoginTest : public QApplication
+{
+Q_OBJECT
+public:
+ LoginTest(int argc, char ** argv);
+
+ ~LoginTest();
+
+ bool isConnected() { return connected; }
+
+public slots:
+ void slotDoTest();
+ void runAddGroupTest();
+ void runDelGroupTest();
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myTestObject;
+ Client* myClient;
+ Connection* myConnection;
+
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.cpp b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp
new file mode 100644
index 00000000..d8e05b36
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ssitest.cpp
@@ -0,0 +1,111 @@
+//Licensed under the GNU General Public License
+
+#include "ssitest.h"
+
+#include <qstring.h>
+
+SSITest::SSITest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ m_manager = new SSIManager(this);
+
+ testIt();
+
+}
+
+SSITest::~SSITest()
+{
+ delete m_manager;
+}
+
+void SSITest::testIt()
+{
+ QPtrList<TLV> tlvs;
+
+ //add three groups
+ SSI *ssi = new SSI( "FirstGroup", 1, 1, ROSTER_GROUP, tlvs);
+ m_manager->newGroup(ssi);
+
+ ssi = new SSI( "SecondGroup", 2, 2, ROSTER_GROUP, tlvs);
+ m_manager->newGroup(ssi);
+
+ ssi = new SSI( "ThirdGroup", 3, 3, ROSTER_GROUP, tlvs);
+ m_manager->newGroup(ssi);
+
+ //add six contacts
+ ssi = new SSI( "FirstContact", 1, 4, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ ssi = new SSI( "SecondContact", 1, 5, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ ssi = new SSI( "ThirdContact", 1, 6, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ ssi = new SSI( "FourthContact", 2, 7, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ ssi = new SSI( "FifthContact", 2, 8, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ ssi = new SSI( "SixthContact", 3, 9, ROSTER_CONTACT, tlvs);
+ m_manager->newContact(ssi);
+
+ //try to find a group by name
+ ssi = m_manager->findGroup("SecondGroup");
+ if ( ssi )
+ qDebug( QString("Found group SecondGroup with gid=%1").arg( ssi->gid() ).latin1());
+ else
+ qDebug( "Oops, group SecondGroup not found" );
+
+ //try to find a group by gid
+ ssi = m_manager->findGroup( 3 );
+ if ( ssi )
+ qDebug( QString("Found group 3 with name=%1").arg( ssi->name() ).latin1() );
+ else
+ qDebug( "Oops, group 3 not found" );
+
+ //try to find a contact by name
+ ssi = m_manager->findContact("ThirdContact");
+ if ( ssi )
+ qDebug( QString( "Found contact ThirdContact with gid=%1" ).arg( ssi->gid() ).latin1() );
+ else
+ qDebug( "Oops, contact ThirdContact not found" );
+
+ //try to find a contact using the name and the group name
+ ssi = m_manager->findContact("FourthContact","SecondGroup");
+ if ( ssi )
+ qDebug( QString("Found contact FourthContact with bid=%1").arg( ssi->bid() ).latin1() );
+ else
+ qDebug( "Oops, contact FourthContact not found" );
+
+
+ //try to find an invalid group
+ ssi = m_manager->findGroup("InvalidGroup");
+ if ( !ssi )
+ qDebug( "Good! It has detected the group is invalid :)" );
+
+ //contacts from a group
+ QValueList<SSI*> list = m_manager->contactsFromGroup("FirstGroup");
+ QValueList<SSI*>::iterator it;
+ qDebug( "Contacts from group FirtsGroup:" );
+ for ( it = list.begin(); it != list.end(); ++it)
+ qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() );
+
+ //the group list
+ QValueList<SSI*> list2 = m_manager->groupList();
+ qDebug( "Group list:" );
+ for ( it = list2.begin(); it != list2.end(); ++it)
+ qDebug( QString(" name=%1").arg( (*it)->name() ).latin1() );
+
+ //remove a group - this shouldn't report any debug line
+ m_manager->removeGroup( "SecondGroup" );
+
+}
+
+int main(int argc, char ** argv)
+{
+ SSITest a( argc, argv );
+ a.exec();
+}
+
+#include "ssitest.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/ssitest.h b/kopete/protocols/oscar/liboscar/tests/ssitest.h
new file mode 100644
index 00000000..19206465
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/ssitest.h
@@ -0,0 +1,34 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef ssitest_h
+#define ssitest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "ssimanager.h"
+
+#define QT_FATAL_ASSERT 1
+
+class SSITest : public QApplication
+{
+Q_OBJECT
+public:
+ SSITest(int argc, char ** argv);
+
+ ~SSITest();
+
+ void testIt();
+private:
+ SSIManager *m_manager;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/tester.h b/kopete/protocols/oscar/liboscar/tests/tester.h
new file mode 100644
index 00000000..2cb1f3af
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/tester.h
@@ -0,0 +1,121 @@
+/**
+ * tester.h
+ *
+ * Copyright (C) 2004 Zack Rusin <zack@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef TESTER_H
+#define TESTER_H
+
+#include <qstringlist.h>
+
+#define CHECK( x, y ) check( __FILE__, __LINE__, #x, x, y, false )
+#define XFAIL( x, y ) check( __FILE__, __LINE__, #x, x, y, true )
+#define SKIP( x ) skip( __FILE__, __LINE__, #x )
+
+class Tester
+{
+public:
+ Tester()
+ : m_tests( 0 )
+ {
+ }
+ virtual ~Tester() {}
+
+public:
+ virtual void allTests() = 0;
+
+public:
+ int testsFinished() const {
+ return m_tests;
+ }
+
+ QStringList errorList() const {
+ return m_errorList;
+ }
+
+ QStringList xfailList() const {
+ return m_xfailList;
+ }
+
+ QStringList xpassList() const {
+ return m_xpassList;
+ }
+
+ QStringList skipList() const {
+ return m_skipList;
+ }
+
+ void skip( const char *file, int line, QString msg )
+ {
+ QString skipEntry;
+ QTextStream ts( &skipEntry, IO_WriteOnly );
+ ts << file << "["<< line <<"]: " << msg;
+ m_skipList.append( skipEntry );
+
+ ++m_tests;
+ }
+
+protected:
+ template<typename T>
+ void check( const char *file, int line, const char *str,
+ const T &result, const T &expectedResult,
+ bool expectedFailure )
+ {
+ if ( result != expectedResult ) {
+ QString error;
+ QTextStream ts( &error, IO_WriteOnly );
+ ts << file << "["<< line <<"]:"
+ <<" failed on \""<< str <<"\""
+ << "\n\t\t result = '"
+ << result
+ << "', expected = '"<< expectedResult<<"'";
+ if ( expectedFailure ) {
+ m_xfailList.append( error );
+ } else {
+ m_errorList.append( error );
+ }
+ } else {
+ // then the test passed, but we want to record it if
+ // we were expecting a failure
+ if (expectedFailure) {
+ QString error;
+ QTextStream ts( &error, IO_WriteOnly );
+ ts << file << "["<< line <<"]:"
+ <<" unexpectedly passed on \""
+ << str <<"\"";
+ m_xpassList.append( error );
+ }
+ }
+ ++m_tests;
+ }
+
+private:
+ QStringList m_errorList;
+ QStringList m_xfailList;
+ QStringList m_xpassList;
+ QStringList m_skipList;
+ int m_tests;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp
new file mode 100644
index 00000000..72ef5acb
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.cpp
@@ -0,0 +1,67 @@
+//Licensed under the GNU General Public License
+
+#include "userinfotest.h"
+
+LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ myConnector->setOptHostPort( "login.oscar.aol.com", 5190 );
+ myTestObject = new ClientStream( myConnector, myConnector);
+
+ // notify when the transport layer is connected
+ //connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ myClient = new Client();
+
+ myConnection = new Connection( myConnector, myTestObject, "AUTHORIZER" );
+ myConnection->setClient( myClient );
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+LoginTest::~LoginTest()
+{
+ delete myTestObject;
+ delete myConnector;
+ delete myClient;
+}
+
+void LoginTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("login.oscar.aol.com");
+ // connect to server
+ qDebug( "connecting to server ");
+
+ myClient->setIsIcq( true );
+ myClient->start( server, 5190, "userid", "password" );
+ myClient->connectToServer( myConnection, server, true );
+ //QObject::connect( myClient, SIGNAL( userIsOnline( const QString& ) ), this, SLOT( runUserInfoTest()));
+ //QTimer::singleShot( 6000, this, SLOT(runUserInfoTest() ) );
+ connected = true;
+}
+
+void LoginTest::slotConnected()
+{
+ qDebug( "connection is up");
+ connected = true;
+}
+
+int main(int argc, char ** argv)
+{
+ LoginTest a( argc, argv );
+ a.exec();
+ if ( !a.isConnected() )
+ return 0;
+}
+
+void LoginTest::runUserInfoTest()
+{
+ qDebug( "running user info test" );
+ QString contact = QString::fromLatin1( "userid" );
+ myClient->requestFullInfo( contact );
+
+}
+
+
+#include "userinfotest.moc"
diff --git a/kopete/protocols/oscar/liboscar/tests/userinfotest.h b/kopete/protocols/oscar/liboscar/tests/userinfotest.h
new file mode 100644
index 00000000..433a6c48
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/tests/userinfotest.h
@@ -0,0 +1,53 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef logintest_h
+#define logintest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+#include "client.h"
+#include "connection.h"
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class LoginTest : public QApplication
+{
+Q_OBJECT
+public:
+ LoginTest(int argc, char ** argv);
+
+ ~LoginTest();
+
+ bool isConnected() { return connected; }
+
+public slots:
+ void slotDoTest();
+ void runUserInfoTest();
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myTestObject;
+ Client* myClient;
+ Connection* myConnection;
+
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/transfer.cpp b/kopete/protocols/oscar/liboscar/transfer.cpp
new file mode 100644
index 00000000..b442a97c
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/transfer.cpp
@@ -0,0 +1,367 @@
+/*
+ transfer.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 Matt Rogers <mattr@kde.org>
+
+ Based on code copyright (c) 2004 SUSE Linux AG <http://www.suse.com>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "transfer.h"
+#include <ctype.h>
+#include <qdeepcopy.h>
+#include <kdebug.h>
+
+Transfer::Transfer()
+{
+ m_isBufferValid = false;
+}
+
+Transfer::Transfer( Buffer* buf )
+{
+ m_buffer = buf;
+ m_isBufferValid = true;
+}
+
+Transfer::TransferType Transfer::type() const
+{
+ return Transfer::RawTransfer;
+}
+
+QByteArray Transfer::toWire()
+{
+ m_wireFormat.duplicate( m_buffer->buffer(), m_buffer->length() );
+ QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat );
+ return wire;
+}
+
+Transfer::~Transfer()
+{
+ delete m_buffer;
+ m_buffer = 0;
+}
+
+void Transfer::setBuffer( Buffer* buffer )
+{
+ m_buffer = buffer;
+}
+
+Buffer* Transfer::buffer()
+{
+ return m_buffer;
+}
+
+const Buffer* Transfer::buffer() const
+{
+ return m_buffer;
+}
+
+bool Transfer::dataValid() const
+{
+ return m_isBufferValid;
+}
+
+QString Transfer::toString() const
+{
+ // line format:
+ //00 03 00 0b 00 00 90 b8 f5 9f 09 31 31 33 37 38 |;tJ�..........|
+
+ int i = 0;
+ QString output = "\n";
+ QString hex, ascii;
+
+ QByteArray::ConstIterator it;
+ QByteArray::ConstIterator end = m_wireFormat.end();
+ for ( it = m_wireFormat.begin(); it != end; ++it )
+ {
+ i++;
+
+ unsigned char c = static_cast<unsigned char>(*it);
+
+ if(c < 0x10)
+ hex.append("0");
+ hex.append(QString("%1 ").arg(c, 0, 16));
+
+ ascii.append(isprint(c) ? c : '.');
+
+ if (i == 16)
+ {
+ output += hex + " |" + ascii + "|\n";
+ i=0;
+ hex=QString::null;
+ ascii=QString::null;
+ }
+ }
+
+ if(!hex.isEmpty())
+ output += hex.leftJustify(48, ' ') + " |" + ascii.leftJustify(16, ' ') + '|';
+ output.append('\n');
+
+ return output;
+}
+
+void Transfer::populateWireBuffer( int offset, const QByteArray& buffer )
+{
+ int j;
+ for ( uint i = 0; i < buffer.size(); ++i )
+ {
+ j = i + offset;
+ m_wireFormat[j] = buffer[i];
+ }
+}
+
+
+FlapTransfer::FlapTransfer()
+ : Transfer()
+{
+ m_isFlapValid = false;
+}
+
+FlapTransfer::FlapTransfer( struct FLAP f, Buffer* buffer )
+ : Transfer( buffer )
+{
+ m_flapChannel = f.channel;
+ m_flapSequence = f.sequence;
+ m_flapLength = f.length;
+
+ if ( m_flapChannel == 0 || m_flapLength < 6 )
+ m_isFlapValid = false;
+ else
+ m_isFlapValid = true;
+
+}
+
+FlapTransfer::FlapTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len )
+ : Transfer( buffer )
+{
+ m_flapChannel = chan;
+ m_flapSequence = seq;
+ m_flapLength = len;
+
+ if ( m_flapChannel == 0 || m_flapLength < 6 )
+ m_isFlapValid = false;
+ else
+ m_isFlapValid = true;
+}
+
+FlapTransfer::~FlapTransfer()
+{
+
+}
+
+QByteArray FlapTransfer::toWire()
+{
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer length is " << m_buffer.length() << endl;
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Buffer is " << m_buffer.toString() << endl;
+
+ m_wireFormat.truncate( 0 );
+ QByteArray useBuf;
+ useBuf.duplicate( m_buffer->buffer(), m_buffer->length() );
+ m_flapLength = useBuf.size();
+ m_wireFormat.resize( 6 + m_flapLength );
+ m_wireFormat[0] = 0x2A;
+ m_wireFormat[1] = m_flapChannel;
+ m_wireFormat[2] = (m_flapSequence & 0xFF00) >> 8;
+ m_wireFormat[3] = (m_flapSequence & 0x00FF);
+ m_wireFormat[4] = (m_flapLength & 0xFF00) >> 8;
+ m_wireFormat[5] = (m_flapLength & 0x00FF);
+
+ //deepcopy the high-level buffer to the wire format buffer
+ populateWireBuffer( 6, useBuf );
+ QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat );
+ return wire;
+}
+
+void FlapTransfer::setFlapChannel( BYTE channel )
+{
+ if ( channel != 0 )
+ {
+ m_flapChannel = channel;
+ m_isFlapValid = true;
+ }
+}
+
+
+BYTE FlapTransfer::flapChannel() const
+{
+ return m_flapChannel;
+}
+
+
+void FlapTransfer::setFlapSequence( WORD seq )
+{
+ m_flapSequence = seq;
+}
+
+
+WORD FlapTransfer::flapSequence() const
+{
+ return m_flapSequence;
+}
+
+void FlapTransfer::setFlapLength( WORD len )
+{
+ m_flapLength = len;
+}
+
+WORD FlapTransfer::flapLength() const
+{
+ return m_flapLength;
+}
+
+bool FlapTransfer::flapValid() const
+{
+ return m_isFlapValid;
+}
+
+Transfer::TransferType FlapTransfer::type() const
+{
+ return Transfer::FlapTransfer;
+}
+
+
+
+SnacTransfer::SnacTransfer()
+ : FlapTransfer()
+{
+ m_isSnacValid = false;
+}
+
+
+SnacTransfer::SnacTransfer( Buffer* buffer, BYTE chan, WORD seq, WORD len, WORD service,
+ WORD subtype, WORD flags, DWORD reqId )
+ : FlapTransfer( buffer, chan, seq, len )
+{
+ m_snacService = service;
+ m_snacSubtype = subtype;
+ m_snacFlags = flags;
+ m_snacReqId = reqId;
+
+ if ( m_snacService == 0 || m_snacSubtype == 0 )
+ m_isSnacValid = false;
+ else
+ m_isSnacValid = true;
+
+}
+
+SnacTransfer::SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer )
+ : FlapTransfer( f, buffer )
+{
+ m_snacService = s.family;
+ m_snacSubtype = s.subtype;
+ m_snacFlags = s.flags;
+ m_snacReqId = s.id;
+
+ if ( m_snacService == 0 || m_snacSubtype == 0 )
+ m_isSnacValid = false;
+ else
+ m_isSnacValid = true;
+}
+
+SnacTransfer::~SnacTransfer()
+{
+
+}
+
+QByteArray SnacTransfer::toWire()
+{
+
+ m_wireFormat.truncate( 0 );
+ QByteArray useBuf;
+ useBuf.duplicate( m_buffer->buffer(), m_buffer->length() );
+ setFlapLength( useBuf.size() + 10 );
+ m_wireFormat.resize( 16 + useBuf.size() );
+
+ //Transfer the flap - 6 bytes
+ m_wireFormat[0] = 0x2A;
+ m_wireFormat[1] = flapChannel();
+ m_wireFormat[2] = (flapSequence() & 0xFF00) >> 8;
+ m_wireFormat[3] = (flapSequence() & 0x00FF);
+ m_wireFormat[4] = (flapLength() & 0xFF00) >> 8;
+ m_wireFormat[5] = (flapLength() & 0x00FF);
+
+ //Transfer the Snac - 10 bytes
+ m_wireFormat[6] = (m_snacService & 0xFF00) >> 8;
+ m_wireFormat[7] = (m_snacService & 0x00FF);
+ m_wireFormat[8] = (m_snacSubtype & 0xFF00) >> 8;
+ m_wireFormat[9] = (m_snacSubtype & 0x00FF);
+ m_wireFormat[10] = (m_snacFlags & 0xFF00) >> 8;
+ m_wireFormat[11] = (m_snacFlags & 0x00FF);
+ m_wireFormat[12] = (m_snacReqId & 0xFF000000) >> 24;
+ m_wireFormat[13] = (m_snacReqId & 0x00FF0000) >> 16;
+ m_wireFormat[14] = (m_snacReqId & 0x0000FF00) >> 8;
+ m_wireFormat[15] = (m_snacReqId & 0x000000FF);
+
+ //deepcopy the high-level buffer to the wire format buffer
+ populateWireBuffer( 16, useBuf );
+ QByteArray wire = QDeepCopy<QByteArray>( m_wireFormat );
+ return wire;
+}
+
+Transfer::TransferType SnacTransfer::type() const
+{
+ return Transfer::SnacTransfer;
+}
+
+bool SnacTransfer::snacValid() const
+{
+ return m_isSnacValid;
+}
+
+void SnacTransfer::setSnacService( WORD service )
+{
+ m_snacService = service;
+}
+
+WORD SnacTransfer::snacService() const
+{
+ return m_snacService;
+}
+
+void SnacTransfer::setSnacSubtype( WORD subtype )
+{
+ m_snacSubtype = subtype;
+}
+
+WORD SnacTransfer::snacSubtype() const
+{
+ return m_snacSubtype;
+}
+
+void SnacTransfer::setSnacFlags( WORD flags )
+{
+ m_snacFlags = flags;
+}
+
+WORD SnacTransfer::snacFlags() const
+{
+ return m_snacFlags;
+}
+
+void SnacTransfer::setSnacRequest( DWORD id )
+{
+ m_snacReqId = id;
+}
+
+DWORD SnacTransfer::snacRequest() const
+{
+ return m_snacReqId;
+}
+
+SNAC SnacTransfer::snac() const
+{
+ SNAC s = { m_snacService, m_snacSubtype, m_snacFlags, m_snacReqId };
+ return s;
+}
+
+
diff --git a/kopete/protocols/oscar/liboscar/transfer.h b/kopete/protocols/oscar/liboscar/transfer.h
new file mode 100644
index 00000000..f42b1e83
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/transfer.h
@@ -0,0 +1,169 @@
+/*
+ transfer.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSFER_H
+#define TRANSFER_H
+
+#include "oscartypes.h"
+#include "buffer.h"
+
+
+using namespace Oscar;
+
+class Transfer
+{
+public:
+ enum TransferType { RawTransfer, FlapTransfer, SnacTransfer, DIMTransfer, FileTransfer };
+ Transfer();
+ Transfer( Buffer* buf );
+ virtual ~Transfer();
+
+ virtual TransferType type() const;
+
+ virtual QByteArray toWire();
+
+ //! Set the data buffer
+ void setBuffer( Buffer* buffer );
+
+ //! Get the data buffer
+ Buffer* buffer();
+
+ const Buffer* buffer() const; //used for const transfer objects
+
+ //! Get the validity of the data after the flap header
+ bool dataValid() const;
+
+ QString toString() const;
+
+ void populateWireBuffer( int offset, const QByteArray& buffer );
+
+protected:
+ //! The wire-format representation of our buffer
+ QByteArray m_wireFormat;
+
+ //! The high-level representation of our data
+ Buffer* m_buffer;
+
+private:
+
+ //! Flag to indicate whether we're a valid transfer
+ bool m_isBufferValid;
+
+};
+
+class FlapTransfer : public Transfer
+{
+public:
+
+ FlapTransfer( Buffer* buffer, BYTE chan = 0, WORD seq = 0, WORD len = 0 );
+ FlapTransfer( FLAP f, Buffer* buffer );
+ FlapTransfer();
+ virtual ~FlapTransfer();
+
+ virtual TransferType type() const;
+ virtual QByteArray toWire();
+
+
+ //! Set the FLAP channel
+ void setFlapChannel( BYTE channel );
+
+ //! Get the FLAP channel
+ BYTE flapChannel() const;
+
+ //! Set the FLAP sequence
+ void setFlapSequence( WORD seq );
+
+ //! Get the FLAP sequence
+ WORD flapSequence() const;
+
+ //! Set the length of the data after the FLAP
+ void setFlapLength( WORD len );
+
+ //! Get the length of the data after the FLAP
+ WORD flapLength() const;
+
+ //! Get the validity of the FLAP header
+ bool flapValid() const;
+
+private:
+ BYTE m_flapChannel;
+ WORD m_flapSequence;
+ WORD m_flapLength;
+
+ bool m_isFlapValid;
+
+};
+
+/**
+@author Matt Rogers
+*/
+class SnacTransfer : public FlapTransfer
+{
+public:
+
+ /*SnacTransfer();*/
+ SnacTransfer( Buffer*, BYTE chan = 0, WORD seq = 0, WORD len = 0, WORD service = 0,
+ WORD subtype = 0, WORD flags = 0, DWORD reqId = 0 );
+ SnacTransfer( struct FLAP f, struct SNAC s, Buffer* buffer );
+ SnacTransfer();
+ virtual ~SnacTransfer();
+
+ TransferType type() const;
+ virtual QByteArray toWire();
+
+
+ //! Set the SNAC service
+ void setSnacService( WORD service );
+
+ //! Get the SNAC service
+ WORD snacService() const;
+
+ //! Set the SNAC subtype
+ void setSnacSubtype( WORD subtype );
+
+ //! Get the SNAC subtype
+ WORD snacSubtype() const;
+
+ //! Set the SNAC flags
+ void setSnacFlags( WORD flags );
+
+ //! Get the SNAC flags
+ WORD snacFlags() const;
+
+ //! Set the SNAC request id
+ void setSnacRequest( DWORD id );
+
+ //! Get the SNAC request id
+ DWORD snacRequest() const;
+
+ //! Get the validity of the SNAC header
+ bool snacValid() const;
+
+ //! Get the SNAC header
+ SNAC snac() const;
+
+private:
+
+ WORD m_snacService;
+ WORD m_snacSubtype;
+ WORD m_snacFlags;
+ WORD m_snacReqId;
+
+ bool m_isSnacValid;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.cpp b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp
new file mode 100644
index 00000000..76503116
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/typingnotifytask.cpp
@@ -0,0 +1,124 @@
+/*
+ typingnotifytask.h - Send/Recieve typing notifications
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "typingnotifytask.h"
+
+#include <qstring.h>
+#include <kdebug.h>
+#include "transfer.h"
+#include "buffer.h"
+#include "connection.h"
+
+
+
+
+TypingNotifyTask::TypingNotifyTask( Task* parent )
+: Task( parent )
+{
+ m_notificationType = 0x0000;
+}
+
+TypingNotifyTask::~TypingNotifyTask()
+{
+}
+
+bool TypingNotifyTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0004 && st->snacSubtype() == 0x0014 )
+ return true;
+ else
+ return false;
+}
+
+bool TypingNotifyTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ handleNotification();
+ setTransfer( 0 );
+ return true;
+ }
+
+ return false;
+}
+
+void TypingNotifyTask::onGo()
+{
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0004, 0x0014, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer();
+
+ //notification id cookie. it's a quad-word
+ b->addDWord( 0x00000000 );
+ b->addDWord( 0x00000000 );
+
+ b->addWord( 0x0001 ); //mtn messages are always sent as type 1 messages
+
+ b->addBUIN( m_contact.latin1() );
+
+ b->addWord( m_notificationType );
+
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+
+ setSuccess( 0, QString::null );
+}
+
+void TypingNotifyTask::handleNotification()
+{
+ /* NB ICQ5 (windows) seems to only send 0x0002 and 0x0001, so I'm interpreting 0x001 as typing finished here - Will */
+ Buffer* b = transfer()->buffer();
+
+ //I don't care about the QWORD or the channel
+ b->skipBytes( 10 );
+
+ QString contact( b->getBUIN() );
+
+ Q_UINT32 word = b->getWord();
+ switch ( word )
+ {
+ case 0x0000:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has finished typing" << endl;
+ emit typingFinished( contact );
+ break;
+ case 0x0001:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has typed a word" << endl;
+ emit typingFinished( contact );
+ break;
+ case 0x0002:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " has started typing" << endl;
+ emit typingStarted( contact );
+ break;
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << contact << " typed an unknown typing notification - " << word << endl;
+ }
+}
+
+void TypingNotifyTask::setParams( const QString& contact, int notifyType )
+{
+ m_contact = contact;
+ m_notificationType = notifyType;
+}
+
+#include "typingnotifytask.moc"
+
+// kate: indent-mode csands; space-indent off; replace-tabs off;
+
diff --git a/kopete/protocols/oscar/liboscar/typingnotifytask.h b/kopete/protocols/oscar/liboscar/typingnotifytask.h
new file mode 100644
index 00000000..b2c9bfef
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/typingnotifytask.h
@@ -0,0 +1,62 @@
+/*
+ typingnotifytask.h - Send/Recieve typing notifications
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _TYPINGNOTIFYTASK_H_
+#define _TYPINGNOTIFYTASK_H_
+
+#include "task.h"
+#include <qstring.h>
+#include "oscartypeclasses.h"
+
+/**
+ * Handles sending and receiving mini typing notifications
+ * @author Matt Rogers
+ */
+class TypingNotifyTask : public Task
+{
+Q_OBJECT
+public:
+ enum { Finished = 0x0000, Typed = 0x0001, Begin = 0x0002 };
+
+ TypingNotifyTask( Task* parent );
+ ~TypingNotifyTask();
+
+ virtual bool forMe( const Transfer* transfer) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+ void setParams( const QString & contact, int notifyType );
+
+signals:
+ //! somebody started typing on the other end
+ void typingStarted( const QString& contact );
+
+ //! somebody finished typing
+ void typingFinished( const QString& contact );
+
+private:
+
+ //! Parse the incoming SNAC(0x04, 0x14)
+ void handleNotification();
+
+private:
+ WORD m_notificationType;
+ QString m_contact;
+};
+
+#endif
+
+//kate: indent-mode csands; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/userdetails.cpp b/kopete/protocols/oscar/liboscar/userdetails.cpp
new file mode 100644
index 00000000..db7d4d1d
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/userdetails.cpp
@@ -0,0 +1,555 @@
+/*
+ Kopete Oscar Protocol
+ userdetails.cpp - user details from the extended status packet
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "userdetails.h"
+
+#include "buffer.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <qptrlist.h>
+#include "oscarutils.h"
+#include "oscardebug.h"
+
+using namespace Oscar;
+
+UserDetails::UserDetails()
+{
+ m_warningLevel = 0;
+ m_userClass = 0;
+ m_idleTime = 0;
+ m_extendedStatus = 0;
+ m_capabilities = 0;
+ m_dcPort = 0;
+ m_dcType = 0;
+ m_dcProtoVersion = 0;
+ m_dcAuthCookie = 0;
+ m_dcWebFrontPort = 0;
+ m_dcClientFeatures = 0;
+ m_dcLastInfoUpdateTime = 0;
+ m_dcLastExtInfoUpdateTime = 0;
+ m_dcLastExtStatusUpdateTime = 0;
+ m_userClassSpecified = false;
+ m_memberSinceSpecified = false;
+ m_onlineSinceSpecified = false;
+ m_numSecondsOnlineSpecified = false;
+ m_idleTimeSpecified = false;
+ m_extendedStatusSpecified = false;
+ m_capabilitiesSpecified = false;
+ m_dcOutsideSpecified = false;
+ m_dcInsideSpecified = false;
+ m_iconSpecified = false;
+}
+
+
+UserDetails::~UserDetails()
+{
+}
+
+int UserDetails::warningLevel() const
+{
+ return m_warningLevel;
+}
+
+QString UserDetails::userId() const
+{
+ return m_userId;
+}
+
+WORD UserDetails::idleTime() const
+{
+ return m_idleTime;
+}
+
+KNetwork::KIpAddress UserDetails::dcInternalIp() const
+{
+ return m_dcInsideIp;
+}
+
+KNetwork::KIpAddress UserDetails::dcExternalIp() const
+{
+ return m_dcOutsideIp;
+}
+
+DWORD UserDetails::dcPort() const
+{
+ return m_dcPort;
+}
+
+QDateTime UserDetails::onlineSinceTime() const
+{
+ return m_onlineSince;
+}
+
+QDateTime UserDetails::memberSinceTime() const
+{
+ return m_memberSince;
+}
+
+int UserDetails::userClass() const
+{
+ return m_userClass;
+}
+
+DWORD UserDetails::extendedStatus() const
+{
+ return m_extendedStatus;
+}
+
+BYTE UserDetails::iconCheckSumType() const
+{
+ return m_iconChecksumType;
+}
+
+QByteArray UserDetails::buddyIconHash() const
+{
+ return m_md5IconHash;
+}
+
+QString UserDetails::clientName() const
+{
+ if ( !m_clientVersion.isEmpty() )
+ return i18n("Translators: client-name client-version",
+ "%1 %2").arg(m_clientName, m_clientVersion);
+ else
+ return m_clientName;
+}
+
+void UserDetails::fill( Buffer * buffer )
+{
+ BYTE snLen = buffer->getByte();
+ QString user = QString( buffer->getBlock( snLen ) );
+ if ( !user.isEmpty() )
+ m_userId = user;
+ m_warningLevel = buffer->getWord();
+ WORD numTLVs = buffer->getWord();
+
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Got user info for " << user << endl;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Warning level is " << m_warningLevel << endl;
+#endif
+ //start parsing TLVs
+ for( int i = 0; i < numTLVs; ++i )
+ {
+ TLV t = buffer->getTLV();
+ if ( t )
+ {
+ Buffer b( t.data, t.length );
+ switch( t.type )
+ {
+ case 0x0001: //user class
+ m_userClass = b.getWord();
+ m_userClassSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "User class is " << m_userClass << endl;
+#endif
+ break;
+ case 0x0002: //member since
+ case 0x0005: //member since
+ m_memberSince.setTime_t( b.getDWord() );
+ m_memberSinceSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Member since " << m_memberSince << endl;
+#endif
+ break;
+ case 0x0003: //sigon time
+ m_onlineSince.setTime_t( b.getDWord() );
+ m_onlineSinceSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Signed on at " << m_onlineSince << endl;
+#endif
+ break;
+ case 0x0004: //idle time
+ m_idleTime = b.getWord() * 60;
+#ifdef OSCAR_USERINFO_DEBUG
+ m_idleTimeSpecified = true;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Idle time is " << m_idleTime << endl;
+#endif
+ break;
+ case 0x0006: //extended user status
+ m_extendedStatus = b.getDWord();
+ m_extendedStatusSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Extended status is " << QString::number( m_extendedStatus, 16 ) << endl;
+#endif
+ break;
+ case 0x000A: //external IP address
+ m_dcOutsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) );
+ m_dcOutsideSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "External IP address is " << m_dcOutsideIp.toString() << endl;
+#endif
+ break;
+ case 0x000C: //DC info
+ m_dcInsideIp = KNetwork::KIpAddress( ntohl( b.getDWord() ) );
+ m_dcPort = b.getDWord();
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Internal IP address is " << m_dcInsideIp.toString() << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Port number is " << m_dcPort << endl;
+#endif
+ m_dcType = b.getByte();
+ m_dcProtoVersion = b.getWord();
+ m_dcAuthCookie = b.getDWord();
+ m_dcWebFrontPort = b.getDWord();
+ m_dcClientFeatures = b.getDWord();
+ m_dcLastInfoUpdateTime = b.getDWord();
+ m_dcLastExtInfoUpdateTime = b.getDWord();
+ m_dcLastExtStatusUpdateTime = b.getDWord();
+ b.getWord(); //unknown.
+ m_dcInsideSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got DC info" << endl;
+#endif
+ break;
+ case 0x000D: //capability info
+ m_capabilities = Oscar::parseCapabilities( b, m_clientVersion );
+ m_capabilitiesSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got capability info" << endl;
+#endif
+ break;
+ case 0x0010:
+ case 0x000F: //online time
+ m_numSecondsOnline = b.getDWord();
+ m_numSecondsOnlineSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Online for " << m_numSecondsOnline << endl;
+#endif
+ break;
+ case 0x001D:
+ {
+ if ( t.length == 0 )
+ break;
+
+ while ( b.length() > 0 )
+ {
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Icon and available message info" << endl;
+#endif
+ WORD type2 = b.getWord();
+ BYTE number = b.getByte();
+ BYTE length = b.getByte();
+ switch( type2 )
+ {
+ case 0x0000:
+ b.skipBytes(length);
+ break;
+ case 0x0001:
+ if ( length > 0 && ( number == 0x01 || number == 0x00 ) )
+ {
+ m_iconChecksumType = number;
+ m_md5IconHash.duplicate( b.getBlock( length ), length );
+ m_iconSpecified = true;
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "checksum:" << m_md5IconHash << endl;
+#endif
+ }
+ else
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << k_funcinfo << "icon checksum indicated"
+ << " but unable to parse checksum" << endl;
+ b.skipBytes( length );
+ }
+ break;
+ case 0x0002:
+ if ( length > 0 )
+ {
+ m_availableMessage = QString( b.getBSTR() );
+#ifdef OSCAR_USERINFO_DEBUG
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "available message:" << m_availableMessage << endl;
+#endif
+ if ( b.length() >= 4 && b.getWord() == 0x0001 )
+ {
+ b.skipBytes( 2 );
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Encoding:" << b.getBSTR() << endl;
+ }
+ }
+ else
+ kdDebug(OSCAR_RAW_DEBUG) << "not enough bytes for available message" << endl;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Unknown TLV, type=" << t.type << ", length=" << t.length
+ << " in userinfo" << endl;
+ break;
+ };
+ //detach buffer and free TLV data
+ b.clear();
+ }
+ }
+
+ //do client detection on fill
+ if ( m_capabilitiesSpecified )
+ detectClient();
+}
+
+void UserDetails::detectClient()
+{
+
+ /* My thanks to mETz for stealing^Wusing this code from SIM.
+ * Client type detection ---
+ * Most of this code is based on sim-icq code
+ * Thanks a lot for all the tests you guys must have made
+ * without sim-icq I would have only checked for the capabilities
+ */
+
+ bool clientMatched = false;
+ if (m_capabilities != 0)
+ {
+ bool clientMatched = false;
+ if (hasCap(CAP_KOPETE))
+ {
+ m_clientName=i18n("Kopete");
+ return;
+ }
+ else if (hasCap(CAP_MICQ))
+ {
+ m_clientName=i18n("MICQ");
+ return;
+ }
+ else if (hasCap(CAP_SIMNEW) || hasCap(CAP_SIMOLD))
+ {
+ m_clientName=i18n("SIM");
+ return;
+ }
+ else if (hasCap(CAP_TRILLIANCRYPT) || hasCap(CAP_TRILLIAN))
+ {
+ m_clientName=i18n("Trillian");
+ return;
+ }
+ else if (hasCap(CAP_MACICQ))
+ {
+ m_clientName=i18n("MacICQ");
+ return;
+ }
+ else if ((m_dcLastInfoUpdateTime & 0xFF7F0000L) == 0x7D000000L)
+ {
+ unsigned ver = m_dcLastInfoUpdateTime & 0xFFFF;
+ if (m_dcLastInfoUpdateTime & 0x00800000L)
+ m_clientName=i18n("Licq SSL");
+ else
+ m_clientName=i18n("Licq");
+
+ if (ver % 10)
+ m_clientVersion.sprintf("%d.%d.%u", ver/1000, (ver/10)%100, ver%10);
+ else
+ m_clientVersion.sprintf("%d.%u", ver/1000, (ver/10)%100);
+ return;
+ }
+ else // some client we could not detect using capabilities
+ {
+
+ clientMatched=true; // default case will set it to false again if we did not find anything
+ switch (m_dcLastInfoUpdateTime)
+ {
+ case 0xFFFFFFFFL: //gaim behaves like official AIM so we can't detect them, only look for miranda
+ {
+ if (m_dcLastExtStatusUpdateTime & 0x80000000)
+ m_clientName=QString::fromLatin1("Miranda alpha");
+ else
+ m_clientName=QString::fromLatin1("Miranda");
+
+ DWORD version = (m_dcLastExtInfoUpdateTime & 0xFFFFFF);
+ BYTE major1 = ((version >> 24) & 0xFF);
+ BYTE major2 = ((version >> 16) & 0xFF);
+ BYTE minor1 = ((version >> 8) & 0xFF);
+ BYTE minor2 = (version & 0xFF);
+ if (minor2 > 0) // w.x.y.z
+ {
+ m_clientVersion.sprintf("%u.%u.%u.%u", major1, major2,
+ minor1, minor2);
+ }
+ else if (minor1 > 0) // w.x.y
+ {
+ m_clientVersion.sprintf("%u.%u.%u", major1, major2, minor1);
+ }
+ else // w.x
+ {
+ m_clientVersion.sprintf("%u.%u", major1, major2);
+ }
+ }
+ break;
+ case 0xFFFFFF8FL:
+ m_clientName = QString::fromLatin1("StrICQ");
+ break;
+ case 0xFFFFFF42L:
+ m_clientName = QString::fromLatin1("mICQ");
+ break;
+ case 0xFFFFFFBEL:
+ m_clientName = QString::fromLatin1("alicq");
+ break;
+ case 0xFFFFFF7FL:
+ m_clientName = QString::fromLatin1("&RQ");
+ break;
+ case 0xFFFFFFABL:
+ m_clientName = QString::fromLatin1("YSM");
+ break;
+ case 0x3AA773EEL:
+ if ((m_dcLastExtStatusUpdateTime == 0x3AA66380L) &&
+ (m_dcLastExtInfoUpdateTime == 0x3A877A42L))
+ {
+ m_clientName=QString::fromLatin1("libicq2000");
+ }
+ break;
+ default:
+ clientMatched=false;
+ break;
+ }
+ }
+ }
+
+ if (!clientMatched) // now the fuzzy clientsearch starts =)
+ {
+ if (hasCap(CAP_TYPING))
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Client protocol version = " << m_dcProtoVersion << endl;
+ switch (m_dcProtoVersion)
+ {
+ case 10:
+ m_clientName=QString::fromLatin1("ICQ 2003b");
+ break;
+ case 9:
+ m_clientName=QString::fromLatin1("ICQ Lite");
+ break;
+ case 8:
+ m_clientName=QString::fromLatin1("Miranda");
+ break;
+ default:
+ m_clientName=QString::fromLatin1("ICQ2go");
+ }
+ }
+ else if (hasCap(CAP_BUDDYICON)) // only gaim seems to advertize this on ICQ
+ {
+ m_clientName = QString::fromLatin1("Gaim");
+ }
+ else if (hasCap(CAP_XTRAZ))
+ {
+ m_clientName = QString::fromLatin1("ICQ 4.0 Lite");
+ }
+ else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) &&
+ hasCap(CAP_IS_2001))
+ {
+ m_clientName = QString::fromLatin1( "ICQ 2001");
+ }
+ else if ((hasCap(CAP_STR_2001) || hasCap(CAP_ICQSERVERRELAY)) &&
+ hasCap(CAP_STR_2002))
+ {
+ m_clientName = QString::fromLatin1("ICQ 2002");
+ }
+ else if (hasCap(CAP_RTFMSGS) && hasCap(CAP_UTF8) &&
+ hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ))
+ {
+ m_clientName = QString::fromLatin1("ICQ 2003a");
+ }
+ else if (hasCap(CAP_ICQSERVERRELAY) && hasCap(CAP_ISICQ))
+ {
+ m_clientName =QString::fromLatin1("ICQ 2001b");
+ }
+ else if ((m_dcProtoVersion == 7) && hasCap(CAP_RTFMSGS))
+ {
+ m_clientName = QString::fromLatin1("GnomeICU");
+ }
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "detected client as: " << m_clientName
+ << " " << m_clientVersion << endl;
+
+}
+
+bool UserDetails::hasCap( int capNumber ) const
+{
+ bool capPresent = ( ( m_capabilities & ( 1 << capNumber ) ) != 0 );
+ return capPresent;
+}
+
+void UserDetails::merge( const UserDetails& ud )
+{
+ m_userId = ud.m_userId;
+ m_warningLevel = ud.m_warningLevel;
+ if ( ud.m_userClassSpecified )
+ {
+ m_userClass = ud.m_userClass;
+ m_userClassSpecified = true;
+ }
+ if ( ud.m_memberSinceSpecified )
+ {
+ m_memberSince = ud.m_memberSince;
+ m_memberSinceSpecified = true;
+ }
+ if ( ud.m_onlineSinceSpecified )
+ {
+ m_onlineSince = ud.m_onlineSince;
+ m_onlineSinceSpecified = true;
+ }
+ if ( ud.m_numSecondsOnlineSpecified )
+ {
+ m_numSecondsOnline = ud.m_numSecondsOnline;
+ m_numSecondsOnlineSpecified = true;
+ }
+ if ( ud.m_idleTimeSpecified )
+ {
+ m_idleTime = ud.m_idleTime;
+ m_idleTimeSpecified = true;
+ }
+ if ( ud.m_extendedStatusSpecified )
+ {
+ m_extendedStatus = ud.m_extendedStatus;
+ m_extendedStatusSpecified = true;
+ }
+ if ( ud.m_capabilitiesSpecified )
+ {
+ m_capabilities = ud.m_capabilities;
+ m_clientVersion = ud.m_clientVersion;
+ m_clientName = ud.m_clientName;
+ m_capabilitiesSpecified = true;
+ }
+ if ( ud.m_dcOutsideSpecified )
+ {
+ m_dcOutsideIp = ud.m_dcOutsideIp;
+ m_dcOutsideSpecified = true;
+ }
+ if ( ud.m_dcInsideSpecified )
+ {
+ m_dcInsideIp = ud.m_dcInsideIp;
+ m_dcPort = ud.m_dcPort;
+ m_dcType = ud.m_dcType;
+ m_dcProtoVersion = ud.m_dcProtoVersion;
+ m_dcAuthCookie = ud.m_dcAuthCookie;
+ m_dcWebFrontPort = ud.m_dcWebFrontPort;
+ m_dcClientFeatures = ud.m_dcClientFeatures;
+ m_dcLastInfoUpdateTime = ud.m_dcLastInfoUpdateTime;
+ m_dcLastExtInfoUpdateTime = ud.m_dcLastExtInfoUpdateTime;
+ m_dcLastExtStatusUpdateTime = ud.m_dcLastExtStatusUpdateTime;
+ m_dcInsideSpecified = true;
+ }
+ if ( ud.m_iconSpecified )
+ {
+ m_iconChecksumType = ud.m_iconChecksumType;
+ m_md5IconHash = ud.m_md5IconHash;
+ m_iconSpecified = true;
+ }
+ m_availableMessage = ud.m_availableMessage;
+}
+
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/userdetails.h b/kopete/protocols/oscar/liboscar/userdetails.h
new file mode 100644
index 00000000..fad79172
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/userdetails.h
@@ -0,0 +1,121 @@
+/*
+ Kopete Oscar Protocol
+ userdetails.h - user details from the extended status packet
+
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef USERDETAILS_H
+#define USERDETAILS_H
+
+#include <ksocketaddress.h>
+#include "oscartypes.h"
+#include "kopete_export.h"
+
+class Buffer;
+using namespace Oscar;
+
+/**
+ * Holds information from the extended user info packet
+ * @author Matt Rogers
+ */
+class KOPETE_EXPORT UserDetails
+{
+public:
+ UserDetails();
+ ~UserDetails();
+
+ QString userId() const; //! User ID accessor
+ int warningLevel() const; //! Warning level accessor
+ WORD idleTime() const; //! Idle time accessor
+ KNetwork::KIpAddress dcInternalIp() const; //! DC local IP accessor
+ KNetwork::KIpAddress dcExternalIp() const; //! DC outside IP accessor
+ DWORD dcPort() const; //! DC port number
+ QDateTime onlineSinceTime() const; //! Online since accessor
+ QDateTime memberSinceTime() const; //! Member since accessor
+ int userClass() const; //! User class accessor
+ DWORD extendedStatus() const; //!User status accessor
+ BYTE iconCheckSumType() const; //!Buddy icon hash type
+ QByteArray buddyIconHash() const; //! Buddy icon md5 hash accessor
+ QString clientName() const; //! Client name and version
+ bool hasCap( int capNumber ) const; //! Tell if we have this capability
+
+ /**
+ * Fill the class with data from a buffer
+ * It only updates what's available.
+ */
+ void fill( Buffer* buffer );
+
+ /**
+ * Merge only those data from another UserDetails
+ * which are marked as specified.
+ */
+ void merge( const UserDetails& ud );
+
+ bool userClassSpecified() const { return m_userClassSpecified; }
+ bool memberSinceSpecified() const { return m_memberSinceSpecified; }
+ bool onlineSinceSpecified() const { return m_onlineSinceSpecified; }
+ bool numSecondsOnlineSpecified() const { return m_numSecondsOnlineSpecified; }
+ bool idleTimeSpecified() const { return m_idleTimeSpecified; }
+ bool extendedStatusSpecified() const { return m_extendedStatusSpecified; }
+ bool capabilitiesSpecified() const { return m_capabilitiesSpecified; }
+ bool dcOutsideSpecified() const { return m_dcOutsideSpecified; }
+ bool dcInsideSpecified() const { return m_dcInsideSpecified; }
+ bool iconSpecified() const { return m_iconSpecified; }
+private:
+ //! Do client detection
+ void detectClient();
+
+
+private:
+ QString m_userId; /// the screename/uin of the contact
+ int m_warningLevel; /// the warning level of the contact
+ int m_userClass; /// the class of the user - TLV 0x01
+ QDateTime m_memberSince; /// how long the user's been a member - TLV 0x05
+ QDateTime m_onlineSince; /// how long the contact's been online - TLV 0x03
+ DWORD m_numSecondsOnline; /// how long the contact's been online in seconds
+ WORD m_idleTime; /// the idle time of the contact - TLV 0x0F
+ DWORD m_extendedStatus; /// the extended status of the contact - TLV 0x06
+ DWORD m_capabilities; //TLV 0x05
+ QString m_clientVersion; /// the version of client they're using
+ QString m_clientName; /// the name of the client they're using
+ KNetwork::KIpAddress m_dcOutsideIp; /// DC Real IP Address - TLV 0x0A
+ KNetwork::KIpAddress m_dcInsideIp; /// DC Internal IP Address - TLV 0x0C
+ DWORD m_dcPort; /// DC Port - TLV 0x0C
+ BYTE m_dcType; /// DC Type - TLV 0x0C
+ WORD m_dcProtoVersion; /// DC Protocol Version - TLV 0x0C
+ DWORD m_dcAuthCookie; /// DC Authorization Cookie - TLV 0x0C
+ DWORD m_dcWebFrontPort; /// DC Web Front Port - TLV 0x0C
+ DWORD m_dcClientFeatures; /// DC client features( whatever they are ) - TLV 0x0C
+ DWORD m_dcLastInfoUpdateTime; /// DC last info update time - TLV 0x0C
+ DWORD m_dcLastExtInfoUpdateTime; /// DC last exteneded info update time - TLV 0x0C
+ DWORD m_dcLastExtStatusUpdateTime; /// DC last extended status update time - TLV 0x0C
+ BYTE m_iconChecksumType; /// The OSCAR checksum type for the buddy icon TLV 0x1D
+ QByteArray m_md5IconHash; /// Buddy Icon MD5 Hash - TLV 0x1D
+ QString m_availableMessage; /// Message a person can have when available - TLV 0x0D
+
+ bool m_userClassSpecified;
+ bool m_memberSinceSpecified;
+ bool m_onlineSinceSpecified;
+ bool m_numSecondsOnlineSpecified;
+ bool m_idleTimeSpecified;
+ bool m_extendedStatusSpecified;
+ bool m_capabilitiesSpecified;
+ bool m_dcOutsideSpecified;
+ bool m_dcInsideSpecified;
+ bool m_iconSpecified;
+
+};
+
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/liboscar/userinfotask.cpp b/kopete/protocols/oscar/liboscar/userinfotask.cpp
new file mode 100644
index 00000000..a204c475
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/userinfotask.cpp
@@ -0,0 +1,156 @@
+/*
+Kopete Oscar Protocol
+userinfotask.h - Handle sending and receiving info requests for users
+
+Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
+
+Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+*************************************************************************
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation; either *
+* version 2 of the License, or (at your option) any later version. *
+* *
+*************************************************************************
+*/
+#include "userinfotask.h"
+
+#include <kdebug.h>
+
+#include "buffer.h"
+#include "connection.h"
+#include "transfer.h"
+#include "userdetails.h"
+
+
+
+UserInfoTask::UserInfoTask( Task* parent )
+: Task( parent )
+{
+}
+
+
+UserInfoTask::~UserInfoTask()
+{
+}
+
+bool UserInfoTask::forMe( const Transfer * transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+
+ if ( st->snacService() == 0x0002 && st->snacSubtype() == 0x0006 )
+ {
+ if ( m_contactSequenceMap.find( st->snacRequest() ) == m_contactSequenceMap.end() )
+ return false;
+ else
+ {
+ //kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Found sequence. taking packet" << endl;
+ return true;
+ }
+ }
+ else
+ return false;
+}
+
+bool UserInfoTask::take( Transfer * transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ Q_UINT16 seq = 0;
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( transfer );
+ if ( st )
+ seq = st->snacRequest();
+
+ if ( seq != 0 )
+ {
+ //AFAIK location info packets always have user info
+ Buffer* b = transfer->buffer();
+ UserDetails ud;
+ ud.fill( b );
+ m_sequenceInfoMap[seq] = ud;
+ emit gotInfo( seq );
+
+ QValueList<TLV> list = b->getTLVList();
+ QValueList<TLV>::iterator it = list.begin();
+ QString profile;
+ QString away;
+ for ( ; ( *it ); ++it )
+ {
+ switch( ( *it ).type )
+ {
+ case 0x0001: //profile text encoding
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "text encoding is " << QString( ( *it ).data )<< endl;
+ break;
+ case 0x0002: //profile text
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "The profile is '" << QString( ( *it ).data ) << "'" << endl;
+ profile = QString( ( *it ).data ); // aim always seems to use us-ascii encoding
+ emit receivedProfile( m_contactSequenceMap[seq], profile );
+ break;
+ case 0x0003: //away message encoding
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message encoding is " << QString( ( *it ).data ) << endl;
+ break;
+ case 0x0004: //away message
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Away message is '" << QString( ( *it ).data ) << "'" << endl;
+ away = QString( (*it ).data ); // aim always seems to use us-ascii encoding
+ emit receivedAwayMessage( m_contactSequenceMap[seq], away );
+ break;
+ case 0x0005: //capabilities
+ break;
+ default: //unknown
+ kdDebug(14151) << k_funcinfo << "Unknown user info type " << ( *it ).type << endl;
+ break;
+ };
+ }
+ list.clear();
+ }
+ setTransfer( 0 );
+ return true;
+ }
+ return false;
+}
+
+void UserInfoTask::onGo()
+{
+ if ( m_contactSequenceMap[m_seq].isEmpty() )
+ {
+ kdDebug( OSCAR_RAW_DEBUG ) << k_funcinfo << "Info requested for empty contact!" << endl;
+ return;
+ }
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0002, 0x0005, 0, m_seq };
+ Buffer* buffer = new Buffer();
+
+ buffer->addWord( m_typesSequenceMap[m_seq] );
+ buffer->addBUIN( m_contactSequenceMap[m_seq].local8Bit() );
+
+ Transfer* t = createTransfer( f, s, buffer );
+ send( t );
+}
+
+void UserInfoTask::requestInfoFor( const QString& contact, unsigned int types )
+{
+ Q_UINT16 seq = client()->snacSequence();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "setting sequence " << seq << " for contact " << contact << endl;
+ m_contactSequenceMap[seq] = contact;
+ m_typesSequenceMap[seq] = types;
+ m_seq = seq;
+ onGo();
+}
+
+UserDetails UserInfoTask::getInfoFor( Q_UINT16 sequence ) const
+{
+ return m_sequenceInfoMap[sequence];
+}
+
+
+
+//kate: indent-mode csands; tab-width 4;
+
+
+#include "userinfotask.moc"
diff --git a/kopete/protocols/oscar/liboscar/userinfotask.h b/kopete/protocols/oscar/liboscar/userinfotask.h
new file mode 100644
index 00000000..063eedd7
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/userinfotask.h
@@ -0,0 +1,69 @@
+/*
+ Kopete Oscar Protocol
+ userinfotask.h - Handle sending and receiving info requests for users
+
+ Copyright (c) 2004-2005 Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef USERINFOTASK_H
+#define USERINFOTASK_H
+
+#include "task.h"
+
+#include <qstring.h>
+#include "userdetails.h"
+
+class Transfer;
+
+/**
+Handles user information requests that are done via SNAC 02,05 and 02,06
+
+@author Kopete Developers
+*/
+class UserInfoTask : public Task
+{
+Q_OBJECT
+public:
+ UserInfoTask( Task* parent );
+ ~UserInfoTask();
+
+ enum { Profile = 0x0001, General = 0x0002, AwayMessage = 0x0003, Capabilities = 0x0004 };
+
+ //! Task implementation
+ bool forMe( const Transfer* transfer ) const;
+ bool take( Transfer* transfer );
+ void onGo();
+
+ void requestInfoFor( const QString& userId, unsigned int types );
+ UserDetails getInfoFor( Q_UINT16 sequence ) const;
+ QString contactForSequence( Q_UINT16 sequence ) const;
+
+
+signals:
+ void gotInfo( Q_UINT16 seqNumber );
+ void receivedProfile( const QString& contact, const QString& profile );
+ void receivedAwayMessage( const QString& contact, const QString& message );
+
+private:
+ QMap<Q_UINT16, UserDetails> m_sequenceInfoMap;
+ QMap<Q_UINT16, QString> m_contactSequenceMap;
+ QMap<Q_UINT16, unsigned int> m_typesSequenceMap;
+ Q_UINT16 m_seq;
+
+};
+
+
+
+#endif
+
+//kate: indent-mode csands; tab-width 4;
diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.cpp b/kopete/protocols/oscar/liboscar/usersearchtask.cpp
new file mode 100644
index 00000000..3fd31010
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/usersearchtask.cpp
@@ -0,0 +1,315 @@
+/*
+ Kopete Oscar Protocol
+ usersearchtask.cpp - Search for contacts
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "usersearchtask.h"
+
+#include "transfer.h"
+#include "buffer.h"
+#include "connection.h"
+
+UserSearchTask::UserSearchTask( Task* parent )
+ : ICQTask( parent )
+{
+}
+
+
+UserSearchTask::~UserSearchTask()
+{
+}
+
+void UserSearchTask::onGo()
+{
+}
+
+bool UserSearchTask::forMe( const Transfer* t ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( t );
+
+ if ( !st )
+ return false;
+
+ if ( st->snacService() != 0x0015 || st->snacSubtype() != 0x0003 )
+ return false;
+
+ Buffer buf( st->buffer()->buffer(), st->buffer()->length() );
+ const_cast<UserSearchTask*>(this)->parseInitialData( buf );
+
+ if ( requestType() == 0x07da && ( requestSubType() == 0x01a4 || requestSubType() == 0x01ae ) )
+ return true;
+
+ return false;
+}
+
+bool UserSearchTask::take( Transfer* t )
+{
+ if ( forMe( t ) )
+ {
+ setTransfer( t );
+
+ Q_UINT16 seq = 0;
+ SnacTransfer* st = dynamic_cast<SnacTransfer*>( t );
+ if ( st )
+ seq = st->snacRequest();
+
+ TLV tlv1 = transfer()->buffer()->getTLV();
+
+ if ( seq == 0 )
+ {
+ setTransfer( 0 );
+ return false;
+ }
+
+ Buffer* buffer = new Buffer( tlv1.data, tlv1.length );
+ ICQSearchResult result;
+ buffer->getLEWord(); // data chunk size
+ /*DWORD receiverUin =*/ buffer->getLEDWord(); // target uin
+ buffer->getLEWord(); // request type
+ buffer->getLEWord(); // request sequence number: 0x0002
+ buffer->getLEWord(); // request subtype
+
+ BYTE success = buffer->getByte(); // Success byte: always 0x0a
+
+ if ( ( success == 0x32 ) || ( success == 0x14 ) || ( success == 0x1E ) )
+ result.uin = 1;
+ else
+ result.fill( buffer );
+
+ m_results.append( result );
+
+ emit foundUser( result );
+
+ // Last user found reply
+ if ( requestSubType() == 0x01ae )
+ {
+ int moreUsersCount = buffer->getLEDWord();
+ emit searchFinished( moreUsersCount );
+ setSuccess( 0, QString::null );
+ }
+ setTransfer( 0 );
+ }
+ return true;
+}
+
+void UserSearchTask::searchUserByUIN( const QString& uin )
+{
+ //create a new result list
+ m_type = UINSearch;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() };
+
+ setRequestType( 0x07D0 ); //meta-information request
+ setRequestSubType( 0x0569 ); //subtype: META_SEARCH_BY_UIN
+ setSequence( f.sequence );
+ Buffer* tlvdata = new Buffer();
+ tlvdata->addLEWord( 0x0136 ); //tlv of type 0x0136 with length 4. all little endian
+ tlvdata->addLEWord( 0x0004 );
+ tlvdata->addLEDWord( uin.toULong() );
+ Buffer* buf = addInitialData( tlvdata );
+ delete tlvdata;
+
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+void UserSearchTask::searchWhitePages( const ICQWPSearchInfo& info )
+{
+ m_type = WhitepageSearch;
+
+ FLAP f = { 0x02, 0, 0 };
+ SNAC s = { 0x0015, 0x0002, 0x0000, client()->snacSequence() };
+
+ setRequestType( 0x07D0 );
+ setRequestSubType( 0x055F );
+ setSequence( f.sequence );
+ Buffer* tlvData = new Buffer();
+ /*
+ search.addLEWord(0x0533); // subtype: 1331
+
+ //LNTS FIRST
+ search.addLEWord(first.length());
+ if(first.length()>0)
+ search.addLEString(first.latin1(), first.length());
+
+ // LNTS LAST
+ search.addLEWord(last.length());
+ if(last.length()>0)
+ search.addLEString(last.latin1(), last.length());
+
+ // LNTS NICK
+ search.addLEWord(nick.length());
+ if(nick.length()>0)
+ search.addLEString(nick.latin1(), nick.length());
+
+ // LNTS EMAIL
+ search.addLEWord(mail.length());
+ if(mail.length()>0)
+ search.addLEString(mail.latin1(), mail.length());
+
+ // WORD.L MINAGE
+ search.addLEWord(minage);
+
+ // WORD.L MAXAGE
+ search.addLEWord(maxage);
+
+ // BYTE xx SEX 1=fem, 2=mal, 0=dontcare
+ if (sex==1)
+ search.addLEByte(0x01);
+ else if(sex==2)
+ search.addLEByte(0x02);
+ else
+ search.addLEByte(0x00);
+
+ // BYTE xx LANGUAGE
+ search.addLEByte(lang);
+
+ // LNTS CITY
+ search.addLEWord(city.length());
+ if(city.length()>0)
+ search.addLEString(city.latin1(), city.length());
+
+ // LNTS STATE
+ search.addLEWord(state.length());
+ if(state.length()>0)
+ search.addLEString(state.latin1(), state.length());
+
+ // WORD.L xx xx COUNTRY
+ search.addLEWord(country);
+
+ // LNTS COMPANY
+ search.addLEWord(company.length());
+ if(company.length()>0)
+ search.addLEString(company.latin1(), company.length());
+
+ // LNTS DEPARTMENT
+ search.addLEWord(department.length());
+ if(department.length()>0)
+ search.addLEString(department.latin1(), department.length());
+
+ // LNTS POSITION
+ search.addLEWord(position.length());
+ if(position.length()>0)
+ search.addLEString(position.latin1(), position.length());
+
+ // BYTE xx OCCUPATION
+ search.addLEByte(occupation);
+
+ //WORD.L xx xx PAST
+ search.addLEWord(0x0000);
+
+ //LNTS PASTDESC - The past description to search for.
+ search.addLEWord(0x0000);
+
+ // WORD.L xx xx INTERESTS - The interests category to search for.
+ search.addLEWord(0x0000);
+
+ // LNTS INTERDESC - The interests description to search for.
+ search.addLEWord(0x0000);
+
+ // WORD.L xx xx AFFILIATION - The affiliation to search for.
+ search.addLEWord(0x0000);
+
+ // LNTS AFFIDESC - The affiliation description to search for.
+ search.addLEWord(0x0000);
+
+ // WORD.L xx xx HOMEPAGE - The home page category to search for.
+ search.addLEWord(0x0000);
+
+ // LNTS HOMEDESC - The home page description to search for.
+ search.addLEWord(0x0000);
+
+ // BYTE xx ONLINE 1=online onliners, 0=dontcare
+ if(onlineOnly)
+ search.addLEByte(0x01);
+ else
+ search.addLEByte(0x00);
+ */
+ if ( !info.firstName.isEmpty() )
+ {
+ Buffer bufFileName;
+ bufFileName.addLEWord( info.firstName.length() );
+ bufFileName.addLEString( info.firstName, info.firstName.length() );
+ tlvData->addLETLV( 0x0140, bufFileName.length(), bufFileName.buffer() );
+ }
+
+ if ( !info.lastName.isEmpty() )
+ {
+ Buffer bufLastName;
+ bufLastName.addLEWord( info.lastName.length() );
+ bufLastName.addLEString( info.lastName, info.lastName.length() );
+ tlvData->addLETLV( 0x014A, bufLastName.length(), bufLastName.buffer() );
+ }
+
+ if ( !info.nickName.isEmpty() )
+ {
+ Buffer bufNickName;
+ bufNickName.addLEWord( info.nickName.length() );
+ bufNickName.addLEString( info.nickName, info.nickName.length() );
+ tlvData->addLETLV( 0x0154, bufNickName.length(), bufNickName.buffer() );
+ }
+
+ if ( !info.email.isEmpty() )
+ {
+ Buffer bufEmail;
+ bufEmail.addLEWord( info.email.length() );
+ bufEmail.addLEString( info.email, info.email.length() );
+ tlvData->addLETLV( 0x015E, bufEmail.length(), bufEmail.buffer() );
+ }
+
+ if ( info.age > 0 )
+ {
+ Buffer bufAge;
+ bufAge.addWord( info.age );
+ bufAge.addWord( info.age );
+ tlvData->addLETLV( 0x0168, bufAge.length(), bufAge.buffer() );
+ }
+
+ if ( info.gender > 0 )
+ tlvData->addLETLV8( 0x017C, info.gender );
+
+ if ( info.language > 0 )
+ tlvData->addLETLV16( 0x0186, info.language );
+
+ if ( info.country > 0 )
+ tlvData->addLETLV16( 0x01A4, info.country );
+
+ if ( !info.city.isEmpty() )
+ {
+ Buffer bufCity;
+ bufCity.addLEWord( info.city.length() );
+ bufCity.addLEString( info.city, info.city.length() );
+ tlvData->addLETLV( 0x0190, bufCity.length(), bufCity.buffer() );
+ }
+
+ if ( info.occupation > 0 )
+ tlvData->addLETLV16( 0x01CC, info.occupation );
+
+ if ( info.onlineOnly )
+ tlvData->addLETLV8( 0x0230, 0x01 );
+
+ Buffer* buf = addInitialData( tlvData );
+ delete tlvData; //we're done with it
+
+ Transfer* t = createTransfer( f, s, buf );
+ send( t );
+}
+
+
+#include "usersearchtask.moc"
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/oscar/liboscar/usersearchtask.h b/kopete/protocols/oscar/liboscar/usersearchtask.h
new file mode 100644
index 00000000..239efe36
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/usersearchtask.h
@@ -0,0 +1,61 @@
+/*
+ Kopete Oscar Protocol
+ usersearchtask.h - Search for contacts
+
+ Copyright (c) 2004 Gustavo Pichorim Boiko <gustavo.boiko@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef USERSEARCHTASK_H
+#define USERSEARCHTASK_H
+
+#include "icqtask.h"
+#include <qstring.h>
+#include "icquserinfo.h"
+
+/**
+Search for contacts
+
+@author Kopete Developers
+*/
+class UserSearchTask : public ICQTask
+{
+Q_OBJECT
+public:
+ UserSearchTask( Task* parent );
+
+ ~UserSearchTask();
+
+ enum SearchType { UINSearch, WhitepageSearch };
+
+ virtual void onGo();
+ virtual bool forMe( const Transfer* t ) const;
+ virtual bool take( Transfer* t );
+
+ /** Search by UIN */
+ void searchUserByUIN( const QString& uin );
+
+ void searchWhitePages( const ICQWPSearchInfo& info );
+
+signals:
+ void foundUser( const ICQSearchResult& result );
+ void searchFinished( int );
+
+private:
+ QValueList<ICQSearchResult> m_results;
+ QString m_uin;
+ Q_UINT16 m_seq;
+ SearchType m_type;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/liboscar/warningtask.cpp b/kopete/protocols/oscar/liboscar/warningtask.cpp
new file mode 100644
index 00000000..56051dc8
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/warningtask.cpp
@@ -0,0 +1,96 @@
+/*
+ Kopete Oscar Protocol
+ warningtask.cpp - send warnings to aim users
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "warningtask.h"
+
+#include <qstring.h>
+#include <kdebug.h>
+#include "transfer.h"
+#include "connection.h"
+
+WarningTask::WarningTask( Task* parent ): Task( parent )
+{
+}
+
+
+WarningTask::~WarningTask()
+{
+}
+
+void WarningTask::setContact( const QString& contact )
+{
+ m_contact = contact;
+}
+
+void WarningTask::setAnonymous( bool anon )
+{
+ m_sendAnon = anon;
+}
+
+bool WarningTask::forMe( const Transfer* transfer ) const
+{
+ const SnacTransfer* st = dynamic_cast<const SnacTransfer*>( transfer );
+ if ( !st )
+ return false;
+ if ( st->snacService() == 0x04 && st->snacSubtype() == 0x09 && st->snacRequest() == m_sequence )
+ return true;
+
+ return false;
+}
+
+bool WarningTask::take( Transfer* transfer )
+{
+ if ( forMe( transfer ) )
+ {
+ setTransfer( transfer );
+ Buffer *b = transfer->buffer();
+ m_increase = b->getWord();
+ m_newLevel = b->getWord();
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Got warning ack for " << m_contact << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Warning level increased " << m_increase
+ << " to " << m_newLevel << endl;
+ emit userWarned( m_contact, m_increase, m_newLevel );
+ setSuccess( 0, QString::null );
+ setTransfer( 0 );
+ return true;
+ }
+ else
+ {
+ setError( 0, QString::null );
+ return false;
+ }
+}
+
+void WarningTask::onGo()
+{
+ FLAP f = { 0x0002, 0, 0 };
+ SNAC s = { 0x0004, 0x0008, 0x0000, client()->snacSequence() };
+ Buffer* b = new Buffer;
+ if ( m_sendAnon )
+ b->addWord( 0x0001 );
+ else
+ b->addWord( 0x0000 );
+
+ b->addBUIN( m_contact.latin1() ); //TODO i should probably check the encoding here. nyeh
+ Transfer* t = createTransfer( f, s, b );
+ send( t );
+}
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
+
+#include "warningtask.moc"
diff --git a/kopete/protocols/oscar/liboscar/warningtask.h b/kopete/protocols/oscar/liboscar/warningtask.h
new file mode 100644
index 00000000..1280fab9
--- /dev/null
+++ b/kopete/protocols/oscar/liboscar/warningtask.h
@@ -0,0 +1,59 @@
+/*
+ Kopete Oscar Protocol
+ warningtask.cpp - send warnings to aim users
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef WARNINGTASK_H
+#define WARNINGTASK_H
+
+#include "task.h"
+#include <qmap.h>
+#include "oscartypes.h"
+
+/**
+@author Matt Rogers
+*/
+class WarningTask : public Task
+{
+Q_OBJECT
+public:
+ WarningTask( Task* parent );
+ ~WarningTask();
+
+ void setContact( const QString& contact );
+ void setAnonymous( bool anon );
+
+ WORD levelIncrease();
+ WORD newLevel();
+
+ virtual bool forMe( const Transfer* transfer ) const;
+ virtual bool take( Transfer* transfer );
+ virtual void onGo();
+
+signals:
+ void userWarned( const QString&, Q_UINT16, Q_UINT16 );
+
+private:
+ QString m_contact;
+ bool m_sendAnon;
+ WORD m_sequence;
+ WORD m_increase;
+ WORD m_newLevel;
+};
+
+#endif
+
+//kate: indent-mode csands; space-indent off; replace-tabs off; tab-width 4;
diff --git a/kopete/protocols/oscar/oscaraccount.cpp b/kopete/protocols/oscar/oscaraccount.cpp
new file mode 100644
index 00000000..353e3201
--- /dev/null
+++ b/kopete/protocols/oscar/oscaraccount.cpp
@@ -0,0 +1,914 @@
+/*
+ oscaraccount.cpp - Oscar Account Class
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Copyright (c) 2004 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscaraccount.h"
+
+#include "kopetepassword.h"
+#include "kopeteprotocol.h"
+#include "kopeteaway.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopeteawaydialog.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+#include "kopetecontactlist.h"
+#include "kopetecontact.h"
+#include "kopetechatsession.h"
+
+#include <assert.h>
+
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qstylesheet.h>
+#include <qtimer.h>
+#include <qptrlist.h>
+#include <qtextcodec.h>
+#include <qimage.h>
+#include <qfile.h>
+
+#include <kdebug.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassivepopup.h>
+#include <kstandarddirs.h>
+
+#include "client.h"
+#include "connection.h"
+#include "oscartypeclasses.h"
+#include "oscarmessage.h"
+#include "oscarutils.h"
+#include "oscarclientstream.h"
+#include "oscarconnector.h"
+#include "ssimanager.h"
+#include "oscarlistnonservercontacts.h"
+#include "oscarversionupdater.h"
+
+class OscarAccountPrivate : public Client::CodecProvider
+{
+ // Backreference
+ OscarAccount& account;
+public:
+ OscarAccountPrivate( OscarAccount& a ): account( a ) {}
+
+ //The liboscar hook for the account
+ Client* engine;
+
+ Q_UINT32 ssiLastModTime;
+
+ //contacts waiting on SSI add ack and their metacontact
+ QMap<QString, Kopete::MetaContact*> addContactMap;
+
+ //contacts waiting on their group to be added
+ QMap<QString, QString> contactAddQueue;
+ QMap<QString, QString> contactChangeQueue;
+
+ OscarListNonServerContacts* olnscDialog;
+
+ unsigned int versionUpdaterStamp;
+ bool versionAlreadyUpdated;
+
+ virtual QTextCodec* codecForContact( const QString& contactName ) const
+ {
+ return account.contactCodec( Oscar::normalize( contactName ) );
+ }
+
+ virtual QTextCodec* codecForAccount() const
+ {
+ return account.defaultCodec();
+ }
+};
+
+OscarAccount::OscarAccount(Kopete::Protocol *parent, const QString &accountID, const char *name, bool isICQ)
+: Kopete::PasswordedAccount( parent, accountID, isICQ ? 8 : 16, name )
+{
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << " accountID='" << accountID <<
+ "', isICQ=" << isICQ << endl;
+
+ d = new OscarAccountPrivate( *this );
+ d->engine = new Client( this );
+ d->engine->setIsIcq( isICQ );
+
+ d->versionAlreadyUpdated = false;
+ d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp();
+ if ( isICQ )
+ d->engine->setVersion( OscarVersionUpdater::self()->getICQVersion() );
+ else
+ d->engine->setVersion( OscarVersionUpdater::self()->getAIMVersion() );
+
+ d->engine->setCodecProvider( d );
+ d->olnscDialog = 0L;
+ QObject::connect( d->engine, SIGNAL( loggedIn() ), this, SLOT( loginActions() ) );
+ QObject::connect( d->engine, SIGNAL( messageReceived( const Oscar::Message& ) ),
+ this, SLOT( messageReceived(const Oscar::Message& ) ) );
+ QObject::connect( d->engine, SIGNAL( socketError( int, const QString& ) ),
+ this, SLOT( slotSocketError( int, const QString& ) ) );
+ QObject::connect( d->engine, SIGNAL( taskError( const Oscar::SNAC&, int, bool ) ),
+ this, SLOT( slotTaskError( const Oscar::SNAC&, int, bool ) ) );
+ QObject::connect( d->engine, SIGNAL( userStartedTyping( const QString& ) ),
+ this, SLOT( userStartedTyping( const QString& ) ) );
+ QObject::connect( d->engine, SIGNAL( userStoppedTyping( const QString& ) ),
+ this, SLOT( userStoppedTyping( const QString& ) ) );
+ QObject::connect( d->engine, SIGNAL( iconNeedsUploading() ),
+ this, SLOT( slotSendBuddyIcon() ) );
+}
+
+OscarAccount::~OscarAccount()
+{
+ OscarAccount::disconnect();
+ delete d;
+}
+
+Client* OscarAccount::engine()
+{
+ return d->engine;
+}
+
+void OscarAccount::logOff( Kopete::Account::DisconnectReason reason )
+{
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "accountId='" << accountId() << "'" << endl;
+ //disconnect the signals
+ Kopete::ContactList* kcl = Kopete::ContactList::self();
+ QObject::disconnect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ),
+ this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) );
+ QObject::disconnect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ),
+ this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) );
+ QObject::disconnect( d->engine->ssiManager(), SIGNAL( contactAdded( const Oscar::SSI& ) ),
+ this, SLOT( ssiContactAdded( const Oscar::SSI& ) ) );
+ QObject::disconnect( d->engine->ssiManager(), SIGNAL( groupAdded( const Oscar::SSI& ) ),
+ this, SLOT( ssiGroupAdded( const Oscar::SSI& ) ) );
+ QObject::disconnect( d->engine->ssiManager(), SIGNAL( groupUpdated( const Oscar::SSI& ) ),
+ this, SLOT( ssiGroupUpdated( const Oscar::SSI& ) ) );
+ QObject::disconnect( d->engine->ssiManager(), SIGNAL( contactUpdated( const Oscar::SSI& ) ),
+ this, SLOT( ssiContactUpdated( const Oscar::SSI& ) ) );
+
+ d->engine->close();
+ myself()->setOnlineStatus( Kopete::OnlineStatus::Offline );
+
+ d->contactAddQueue.clear();
+ d->contactChangeQueue.clear();
+
+ disconnected( reason );
+}
+
+void OscarAccount::disconnect()
+{
+ logOff( Kopete::Account::Manual );
+}
+
+bool OscarAccount::passwordWasWrong()
+{
+ return password().isWrong();
+}
+
+void OscarAccount::loginActions()
+{
+ password().setWrong( false );
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "processing SSI list" << endl;
+ processSSIList();
+
+ //start a chat nav connection
+ if ( !engine()->isIcq() )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "sending request for chat nav service" << endl;
+ d->engine->requestServerRedirect( 0x000D );
+ }
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "sending request for icon service" << endl;
+ d->engine->requestServerRedirect( 0x0010 );
+
+}
+
+void OscarAccount::processSSIList()
+{
+ //disconnect signals so we don't attempt to add things to SSI!
+ Kopete::ContactList* kcl = Kopete::ContactList::self();
+ QObject::disconnect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ),
+ this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) );
+ QObject::disconnect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ),
+ this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+
+ SSIManager* listManager = d->engine->ssiManager();
+
+ //first add groups
+ QValueList<SSI> groupList = listManager->groupList();
+ QValueList<SSI>::const_iterator git = groupList.constBegin();
+ QValueList<SSI>::const_iterator listEnd = groupList.constEnd();
+ //the protocol dictates that there is at least one group that has contacts
+ //so i don't have to check for an empty group list
+
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << groupList.count() << " groups to contact list" << endl;
+ for( ; git != listEnd; ++git )
+ { //add all the groups.
+ kdDebug( OSCAR_GEN_DEBUG ) << k_funcinfo << "Adding SSI group'" << ( *git ).name()
+ << "' to the kopete contact list" << endl;
+ kcl->findGroup( ( *git ).name() );
+ }
+
+ //then add contacts
+ QValueList<SSI> contactList = listManager->contactList();
+ QValueList<SSI>::const_iterator bit = contactList.constBegin();
+ QValueList<SSI>::const_iterator blistEnd = contactList.constEnd();
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << contactList.count() << " contacts to contact list" << endl;
+ for ( ; bit != blistEnd; ++bit )
+ {
+ SSI groupForAdd = listManager->findGroup( ( *bit ).gid() );
+ Kopete::Group* group;
+ if ( groupForAdd.isValid() )
+ group = kcl->findGroup( groupForAdd.name() ); //add if not present
+ else
+ group = kcl->findGroup( i18n( "Buddies" ) );
+
+ kdDebug( OSCAR_GEN_DEBUG ) << k_funcinfo << "Adding contact '" << ( *bit ).name() << "' to kopete list in group " <<
+ group->displayName() << endl;
+ OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *bit ).name()] );
+ if ( oc )
+ {
+ Oscar::SSI item = ( *bit );
+ oc->setSSIItem( item );
+ }
+ else
+ addContact( ( *bit ).name(), QString::null, group, Kopete::Account::DontChangeKABC );
+ }
+
+ QObject::connect( kcl, SIGNAL( groupRenamed( Kopete::Group*, const QString& ) ),
+ this, SLOT( kopeteGroupRenamed( Kopete::Group*, const QString& ) ) );
+ QObject::connect( kcl, SIGNAL( groupRemoved( Kopete::Group* ) ),
+ this, SLOT( kopeteGroupRemoved( Kopete::Group* ) ) );
+ QObject::connect( listManager, SIGNAL( contactAdded( const Oscar::SSI& ) ),
+ this, SLOT( ssiContactAdded( const Oscar::SSI& ) ) );
+ QObject::connect( listManager, SIGNAL( groupAdded( const Oscar::SSI& ) ),
+ this, SLOT( ssiGroupAdded( const Oscar::SSI& ) ) );
+ QObject::connect( listManager, SIGNAL( groupUpdated( const Oscar::SSI& ) ),
+ this, SLOT( ssiGroupUpdated( const Oscar::SSI& ) ) );
+ QObject::connect( listManager, SIGNAL( contactUpdated( const Oscar::SSI& ) ),
+ this, SLOT( ssiContactUpdated( const Oscar::SSI& ) ) );
+
+ //TODO: check the kopete contact list and handle non server side contacts appropriately.
+ QDict<Kopete::Contact> nonServerContacts = contacts();
+ QDictIterator<Kopete::Contact> it( nonServerContacts );
+ QStringList nonServerContactList;
+ for ( ; it.current(); ++it )
+ {
+ OscarContact* oc = dynamic_cast<OscarContact*>( ( *it ) );
+ if ( !oc )
+ continue;
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << oc->contactId() << " contact ssi type: " << oc->ssiItem().type() << endl;
+ if ( !oc->isOnServer() )
+ nonServerContactList.append( ( *it )->contactId() );
+ }
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "the following contacts are not on the server side list"
+ << nonServerContactList << endl;
+ bool showMissingContactsDialog = configGroup()->readBoolEntry(QString::fromLatin1("ShowMissingContactsDialog"), true);
+ if ( !nonServerContactList.isEmpty() && showMissingContactsDialog )
+ {
+ d->olnscDialog = new OscarListNonServerContacts( Kopete::UI::Global::mainWidget() );
+ QObject::connect( d->olnscDialog, SIGNAL( closing() ),
+ this, SLOT( nonServerAddContactDialogClosed() ) );
+ d->olnscDialog->addContacts( nonServerContactList );
+ d->olnscDialog->show();
+ }
+}
+
+void OscarAccount::nonServerAddContactDialogClosed()
+{
+ if ( !d->olnscDialog )
+ return;
+
+ if ( d->olnscDialog->result() == QDialog::Accepted )
+ {
+ //start adding contacts
+ kdDebug(OSCAR_GEN_DEBUG) << "adding non server contacts to the contact list" << endl;
+ //get the contact list. get the OscarContact object, then the group
+ //check if the group is on ssi, if not, add it
+ //if so, add the contact.
+ QStringList offliners = d->olnscDialog->nonServerContactList();
+ QStringList::iterator it, itEnd = offliners.end();
+ for ( it = offliners.begin(); it != itEnd; ++it )
+ {
+ OscarContact* oc = dynamic_cast<OscarContact*>( contacts()[( *it )] );
+ if ( !oc )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no OscarContact object available for"
+ << ( *it ) << endl;
+ continue;
+ }
+
+ Kopete::MetaContact* mc = oc->metaContact();
+ if ( !mc )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no metacontact object available for"
+ << ( oc->contactId() ) << endl;
+ continue;
+ }
+
+ Kopete::Group* group = mc->groups().first();
+ if ( !group )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "no metacontact object available for"
+ << ( oc->contactId() ) << endl;
+ continue;
+ }
+
+ addContactToSSI( ( *it ), group->displayName(), true );
+ }
+
+
+ }
+
+ bool showOnce = d->olnscDialog->onlyShowOnce();
+ configGroup()->writeEntry( QString::fromLatin1("ShowMissingContactsDialog") , !showOnce);
+ configGroup()->sync();
+
+ d->olnscDialog->delayedDestruct();
+ d->olnscDialog = 0L;
+}
+
+void OscarAccount::slotGoOffline()
+{
+ OscarAccount::disconnect();
+}
+
+void OscarAccount::slotGoOnline()
+{
+ //do nothing
+}
+
+void OscarAccount::kopeteGroupRemoved( Kopete::Group* group )
+{
+ if ( isConnected() )
+ d->engine->removeGroup( group->displayName() );
+}
+
+void OscarAccount::kopeteGroupAdded( Kopete::Group* group )
+{
+ if ( isConnected() )
+ d->engine->addGroup( group->displayName() );
+}
+
+void OscarAccount::kopeteGroupRenamed( Kopete::Group* group, const QString& oldName )
+{
+ if ( isConnected() )
+ d->engine->renameGroup( oldName, group->displayName() );
+}
+
+void OscarAccount::messageReceived( const Oscar::Message& message )
+{
+ //the message isn't for us somehow
+ if ( Oscar::normalize( message.receiver() ) != Oscar::normalize( accountId() ) )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "got a message but we're not the receiver: "
+ << message.textArray() << endl;
+ return;
+ }
+
+ /* Logic behind this:
+ * If we don't have the contact yet, create it as a temporary
+ * Create the message manager
+ * Get the sanitized message back
+ * Append to the chat window
+ */
+ QString sender = Oscar::normalize( message.sender() );
+ if ( !contacts()[sender] )
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << "Adding '" << sender << "' as temporary contact" << endl;
+ addContact( sender, QString::null, 0, Kopete::Account::Temporary );
+ }
+
+ OscarContact* ocSender = static_cast<OscarContact *> ( contacts()[sender] ); //should exist now
+
+ if ( !ocSender )
+ {
+ kdWarning(OSCAR_RAW_DEBUG) << "Temporary contact creation failed for '"
+ << sender << "'! Discarding message: " << message.textArray() << endl;
+ return;
+ }
+ else
+ {
+ if ( ( message.properties() & Oscar::Message::WWP ) == Oscar::Message::WWP )
+ ocSender->setNickName( i18n("ICQ Web Express") );
+ if ( ( message.properties() & Oscar::Message::EMail ) == Oscar::Message::EMail )
+ ocSender->setNickName( i18n("ICQ Email Express") );
+ }
+
+ Kopete::ChatSession* chatSession = ocSender->manager( Kopete::Contact::CanCreate );
+ chatSession->receivedTypingMsg( ocSender, false ); //person is done typing
+
+
+ //decode message
+ QString realText( message.text( contactCodec( ocSender ) ) );
+
+ //sanitize;
+ QString sanitizedMsg = sanitizedMessage( realText );
+
+ Kopete::ContactPtrList me;
+ me.append( myself() );
+ Kopete::Message chatMessage( message.timestamp(), ocSender, me, sanitizedMsg,
+ Kopete::Message::Inbound, Kopete::Message::RichText );
+
+ chatSession->appendMessage( chatMessage );
+}
+
+
+void OscarAccount::setServerAddress(const QString &server)
+{
+ configGroup()->writeEntry( QString::fromLatin1( "Server" ), server );
+}
+
+void OscarAccount::setServerPort(int port)
+{
+ if ( port > 0 )
+ configGroup()->writeEntry( QString::fromLatin1( "Port" ), port );
+ else //set to default 5190
+ configGroup()->writeEntry( QString::fromLatin1( "Port" ), 5190 );
+}
+
+QTextCodec* OscarAccount::defaultCodec() const
+{
+ return QTextCodec::codecForMib( configGroup()->readNumEntry( "DefaultEncoding", 4 ) );
+}
+
+QTextCodec* OscarAccount::contactCodec( const OscarContact* contact ) const
+{
+ if ( contact )
+ return contact->contactCodec();
+ else
+ return defaultCodec();
+}
+
+QTextCodec* OscarAccount::contactCodec( const QString& contactName ) const
+{
+ // XXX Need const_cast because Kopete::Account::contacts()
+ // XXX method is not const for some strange reason.
+ OscarContact* contact = static_cast<OscarContact *> ( const_cast<OscarAccount *>(this)->contacts()[contactName] );
+ return contactCodec( contact );
+}
+
+void OscarAccount::setBuddyIcon( KURL url )
+{
+ if ( url.path().isEmpty() )
+ {
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ }
+ else
+ {
+ QImage image( url.path() );
+ if ( image.isNull() )
+ return;
+
+ const QSize size = ( d->engine->isIcq() ) ? QSize( 52, 64 ) : QSize( 48, 48 );
+
+ image = image.smoothScale( size, QImage::ScaleMax );
+ if( image.width() > size.width())
+ image = image.copy( ( image.width() - size.width() ) / 2, 0, size.width(), image.height() );
+
+ if( image.height() > size.height())
+ image = image.copy( 0, ( image.height() - size.height() ) / 2, image.width(), size.height() );
+
+ QString newlocation( locateLocal( "appdata", "oscarpictures/"+ accountId() + ".jpg" ) );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Saving buddy icon: " << newlocation << endl;
+ if ( !image.save( newlocation, "JPEG" ) )
+ return;
+
+ myself()->setProperty( Kopete::Global::Properties::self()->photo() , newlocation );
+ }
+
+ emit buddyIconChanged();
+}
+
+bool OscarAccount::addContactToSSI( const QString& contactName, const QString& groupName, bool autoAddGroup )
+{
+ SSIManager* listManager = d->engine->ssiManager();
+ if ( !listManager->findGroup( groupName ) )
+ {
+ if ( !autoAddGroup )
+ return false;
+
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "adding non-existant group "
+ << groupName << endl;
+
+ d->contactAddQueue[Oscar::normalize( contactName )] = groupName;
+ d->engine->addGroup( groupName );
+ }
+ else
+ {
+ d->engine->addContact( contactName, groupName );
+ }
+
+ return true;
+}
+
+bool OscarAccount::changeContactGroupInSSI( const QString& contact, const QString& newGroupName, bool autoAddGroup )
+{
+ SSIManager* listManager = d->engine->ssiManager();
+ if ( !listManager->findGroup( newGroupName ) )
+ {
+ if ( !autoAddGroup )
+ return false;
+
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "adding non-existant group "
+ << newGroupName << endl;
+
+ d->contactChangeQueue[Oscar::normalize( contact )] = newGroupName;
+ d->engine->addGroup( newGroupName );
+ }
+ else
+ {
+ d->engine->changeContactGroup( contact, newGroupName );
+ }
+
+ return true;
+}
+
+Connection* OscarAccount::setupConnection( const QString& server, uint port )
+{
+ //set up the connector
+ KNetworkConnector* knc = new KNetworkConnector( 0 );
+ knc->setOptHostPort( server, port );
+
+ //set up the clientstream
+ ClientStream* cs = new ClientStream( knc, 0 );
+
+ Connection* c = new Connection( knc, cs, "AUTHORIZER" );
+ c->setClient( d->engine );
+
+ return c;
+}
+
+
+bool OscarAccount::createContact(const QString &contactId,
+ Kopete::MetaContact *parentContact)
+{
+ /* We're not even online or connecting
+ * (when getting server contacts), so don't bother
+ */
+ if ( !engine()->isActive() )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Can't add contact, we are offline!" << endl;
+ return false;
+ }
+
+ /* Logic for SSI additions
+ If the contact is temporary, no SSI addition at all. Just create the contact and be done with it
+ If the contact is not temporary, we need to do the following:
+ 1. Check if contact already exists in the SSI manager, if so, just create the contact
+ 2. If contact doesn't exist:
+ 2.a. create group on SSI if needed
+ 2.b. create contact on SSI
+ 2.c. create kopete contact
+ */
+
+ QValueList<TLV> dummyList;
+ if ( parentContact->isTemporary() )
+ {
+ SSI tempItem( contactId, 0, 0, 0xFFFF, dummyList, 0 );
+ return createNewContact( contactId, parentContact, tempItem );
+ }
+
+ SSI ssiItem = d->engine->ssiManager()->findContact( contactId );
+ if ( ssiItem )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Have new SSI entry. Finding contact" << endl;
+ if ( contacts()[ssiItem.name()] )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Found contact in list. Updating SSI item" << endl;
+ OscarContact* oc = static_cast<OscarContact*>( contacts()[ssiItem.name()] );
+ oc->setSSIItem( ssiItem );
+ return true;
+ }
+ else
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Didn't find contact in list, creating new contact" << endl;
+ return createNewContact( contactId, parentContact, ssiItem );
+ }
+ }
+ else
+ { //new contact, check temporary, if temporary, don't add to SSI. otherwise, add.
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "New contact '" << contactId << "' not in SSI."
+ << " Creating new contact" << endl;
+
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Adding " << contactId << " to server side list" << endl;
+
+ QString groupName;
+ Kopete::GroupList kopeteGroups = parentContact->groups(); //get the group list
+
+ if ( kopeteGroups.isEmpty() || kopeteGroups.first() == Kopete::Group::topLevel() )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Contact with NO group. " << "Adding to group 'Buddies'" << endl;
+ groupName = i18n("Buddies");
+ }
+ else
+ {
+ //apparently kopeteGroups.first() can be invalid. Attempt to prevent
+ //crashes in SSIData::findGroup(const QString& name)
+ groupName = kopeteGroups.first() ? kopeteGroups.first()->displayName() : i18n("Buddies");
+
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Contact with group." << " No. of groups = " << kopeteGroups.count() <<
+ " Name of first group = " << groupName << endl;
+ }
+
+ if( groupName.isEmpty() )
+ { // emergency exit, should never occur
+ kdWarning(OSCAR_GEN_DEBUG) << k_funcinfo << "Could not add contact because no groupname was given" << endl;
+ return false;
+ }
+
+ d->addContactMap[Oscar::normalize( contactId )] = parentContact;
+ addContactToSSI( Oscar::normalize( contactId ), groupName, true );
+ return true;
+ }
+}
+
+void OscarAccount::updateVersionUpdaterStamp()
+{
+ d->versionUpdaterStamp = OscarVersionUpdater::self()->stamp();
+}
+
+void OscarAccount::ssiContactAdded( const Oscar::SSI& item )
+{
+ if ( d->addContactMap.contains( Oscar::normalize( item.name() ) ) )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Received confirmation from server. adding " << item.name()
+ << " to the contact list" << endl;
+ createNewContact( item.name(), d->addContactMap[Oscar::normalize( item.name() )], item );
+ }
+ else if ( contacts()[item.name()] )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Received confirmation from server. modifying " << item.name() << endl;
+ OscarContact* oc = static_cast<OscarContact*>( contacts()[item.name()] );
+ oc->setSSIItem( item );
+ }
+ else
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Got addition for contact we weren't waiting on" << endl;
+}
+
+void OscarAccount::ssiGroupAdded( const Oscar::SSI& item )
+{
+ //check the contact add queue for any contacts matching the
+ //group name we just added
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Looking for contacts to be added in group " << item.name() << endl;
+ QMap<QString,QString>::iterator it;
+ for ( it = d->contactAddQueue.begin(); it != d->contactAddQueue.end(); ++it )
+ {
+ if ( Oscar::normalize( it.data() ) == Oscar::normalize( item.name() ) )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "starting delayed add of contact '" << it.key()
+ << "' to group " << item.name() << endl;
+
+ d->engine->addContact( Oscar::normalize( it.key() ), item.name() );
+ d->contactAddQueue.remove( it );
+ }
+ }
+
+ for ( it = d->contactChangeQueue.begin(); it != d->contactChangeQueue.end(); ++it )
+ {
+ if ( Oscar::normalize( it.data() ) == Oscar::normalize( item.name() ) )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "starting delayed change of contact '" << it.key()
+ << "' to group " << item.name() << endl;
+
+ d->engine->changeContactGroup( it.key(), item.name() );
+ d->contactChangeQueue.remove( it );
+ }
+ }
+}
+
+void OscarAccount::ssiContactUpdated( const Oscar::SSI& item )
+{
+ Kopete::Contact* contact = contacts()[item.name()];
+ if ( !contact )
+ return;
+ else
+ {
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Updating SSI Item" << endl;
+ OscarContact* oc = static_cast<OscarContact*>( contact );
+ oc->setSSIItem( item );
+ }
+}
+
+void OscarAccount::userStartedTyping( const QString & contact )
+{
+ Kopete::Contact * ct = contacts()[ Oscar::normalize( contact ) ];
+ if ( ct && contact != accountId() )
+ {
+ OscarContact * oc = static_cast<OscarContact *>( ct );
+ oc->startedTyping();
+ }
+}
+
+void OscarAccount::userStoppedTyping( const QString & contact )
+{
+ Kopete::Contact * ct = contacts()[ Oscar::normalize( contact ) ];
+ if ( ct && contact != accountId() )
+ {
+ OscarContact * oc = static_cast<OscarContact *>( ct );
+ oc->stoppedTyping();
+ }
+}
+
+void OscarAccount::slotSocketError( int errCode, const QString& errString )
+{
+ Q_UNUSED( errCode );
+ KPassivePopup::message( i18n( "account has been disconnected", "%1 disconnected" ).arg( accountId() ),
+ errString,
+ myself()->onlineStatus().protocolIcon(),
+ Kopete::UI::Global::mainWidget() );
+ logOff( Kopete::Account::ConnectionReset );
+}
+
+void OscarAccount::slotTaskError( const Oscar::SNAC& s, int code, bool fatal )
+{
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "error recieived from task" << endl;
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "service: " << s.family
+ << " subtype: " << s.subtype << " code: " << code << endl;
+
+ QString message;
+ if ( s.family == 0 && s.subtype == 0 )
+ {
+ message = getFLAPErrorMessage( code );
+ KPassivePopup::message( i18n( "account has been disconnected", "%1 disconnected" ).arg( accountId() ),
+ message, myself()->onlineStatus().protocolIcon(),
+ Kopete::UI::Global::mainWidget() );
+ switch ( code )
+ {
+ case 0x0000:
+ logOff( Kopete::Account::Unknown );
+ break;
+ case 0x0004:
+ case 0x0005:
+ logOff( Kopete::Account::BadPassword );
+ break;
+ case 0x0007:
+ case 0x0008:
+ case 0x0009:
+ case 0x0011:
+ logOff( Kopete::Account::BadUserName );
+ break;
+ default:
+ logOff( Kopete::Account::Manual );
+ }
+ return;
+ }
+ if ( !fatal )
+ message = i18n("There was an error in the protocol handling; it was not fatal, so you will not be disconnected.");
+ else
+ message = i18n("There was an error in the protocol handling; automatic reconnection occurring.");
+
+ KPassivePopup::message( i18n("OSCAR Protocol error"), message, myself()->onlineStatus().protocolIcon(),
+ Kopete::UI::Global::mainWidget() );
+ if ( fatal )
+ logOff( Kopete::Account::ConnectionReset );
+}
+
+void OscarAccount::slotSendBuddyIcon()
+{
+ //need to disconnect because we could end up with many connections
+ QObject::disconnect( engine(), SIGNAL( iconServerConnected() ), this, SLOT( slotSendBuddyIcon() ) );
+ QString photoPath = myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString();
+ if ( photoPath.isEmpty() )
+ return;
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << photoPath << endl;
+ QFile iconFile( photoPath );
+
+ if ( iconFile.open( IO_ReadOnly ) )
+ {
+ if ( !engine()->hasIconConnection() )
+ {
+ //will send icon when we connect to icon server
+ QObject::connect( engine(), SIGNAL( iconServerConnected() ),
+ this, SLOT( slotSendBuddyIcon() ) );
+ return;
+ }
+ QByteArray imageData = iconFile.readAll();
+ engine()->sendBuddyIcon( imageData );
+ }
+}
+
+QString OscarAccount::getFLAPErrorMessage( int code )
+{
+ bool isICQ = d->engine->isIcq();
+ QString acctType = isICQ ? i18n("ICQ") : i18n("AIM");
+ QString acctDescription = isICQ ? i18n("ICQ user id", "UIN") : i18n("AIM user id", "screen name");
+ QString reason;
+ //FLAP errors are always fatal
+ //negative codes are things added by liboscar developers
+ //to indicate generic errors in the task
+ switch ( code )
+ {
+ case 0x0001:
+ if ( isConnected() ) // multiple logins (on same UIN)
+ {
+ reason = i18n( "You have logged in more than once with the same %1," \
+ " account %2 is now disconnected.")
+ .arg( acctDescription ).arg( accountId() );
+ }
+ else // error while logging in
+ {
+ reason = i18n( "Sign on failed because either your %1 or " \
+ "password are invalid. Please check your settings for account %2.")
+ .arg( acctDescription ).arg( accountId() );
+
+ }
+ break;
+ case 0x0002: // Service temporarily unavailable
+ case 0x0014: // Reservation map error
+ reason = i18n("The %1 service is temporarily unavailable. Please try again later.")
+ .arg( acctType );
+ break;
+ case 0x0004: // Incorrect nick or password, re-enter
+ case 0x0005: // Mismatch nick or password, re-enter
+ reason = i18n("Could not sign on to %1 with account %2 because the " \
+ "password was incorrect.").arg( acctType ).arg( accountId() );
+ break;
+ case 0x0007: // non-existant ICQ#
+ case 0x0008: // non-existant ICQ#
+ reason = i18n("Could not sign on to %1 with nonexistent account %2.")
+ .arg( acctType ).arg( accountId() );
+ break;
+ case 0x0009: // Expired account
+ reason = i18n("Sign on to %1 failed because your account %2 expired.")
+ .arg( acctType ).arg( accountId() );
+ break;
+ case 0x0011: // Suspended account
+ reason = i18n("Sign on to %1 failed because your account %2 is " \
+ "currently suspended.").arg( acctType ).arg( accountId() );
+ break;
+ case 0x0015: // too many clients from same IP
+ case 0x0016: // too many clients from same IP
+ case 0x0017: // too many clients from same IP (reservation)
+ reason = i18n("Could not sign on to %1 as there are too many clients" \
+ " from the same computer.").arg( acctType );
+ break;
+ case 0x0018: // rate exceeded (turboing)
+ if ( isConnected() )
+ {
+ reason = i18n("Account %1 was blocked on the %2 server for" \
+ " sending messages too quickly." \
+ " Wait ten minutes and try again." \
+ " If you continue to try, you will" \
+ " need to wait even longer.")
+ .arg( accountId() ).arg( acctType );
+ }
+ else
+ {
+ reason = i18n("Account %1 was blocked on the %2 server for" \
+ " reconnecting too quickly." \
+ " Wait ten minutes and try again." \
+ " If you continue to try, you will" \
+ " need to wait even longer.")
+ .arg( accountId() ).arg( acctType) ;
+ }
+ break;
+ case 0x001C:
+ OscarVersionUpdater::self()->update( d->versionUpdaterStamp );
+ if ( !d->versionAlreadyUpdated )
+ {
+ reason = i18n("Sign on to %1 with your account %2 failed.")
+ .arg( acctType ).arg( accountId() );
+
+ d->versionAlreadyUpdated = true;
+ }
+ else
+ {
+ reason = i18n("The %1 server thinks the client you are using is " \
+ "too old. Please report this as a bug at http://bugs.kde.org")
+ .arg( acctType );
+ }
+ break;
+ case 0x0022: // Account suspended because of your age (age < 13)
+ reason = i18n("Account %1 was disabled on the %2 server because " \
+ "of your age (less than 13).")
+ .arg( accountId() ).arg( acctType );
+ break;
+ default:
+ if ( !isConnected() )
+ {
+ reason = i18n("Sign on to %1 with your account %2 failed.")
+ .arg( acctType ).arg( accountId() );
+ }
+ break;
+ }
+ return reason;
+}
+
+#include "oscaraccount.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/oscaraccount.h b/kopete/protocols/oscar/oscaraccount.h
new file mode 100644
index 00000000..aa8e806d
--- /dev/null
+++ b/kopete/protocols/oscar/oscaraccount.h
@@ -0,0 +1,204 @@
+/*
+ oscaraccount.h - Oscar Account Class
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2002 by Chris TenHarmsel <tenharmsel@staticmethod.net>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCARACCOUNT_H
+#define OSCARACCOUNT_H
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qwidget.h>
+
+#include "kopetepasswordedaccount.h"
+#include "oscartypeclasses.h"
+#include "oscarcontact.h"
+
+
+namespace Kopete
+{
+class Contact;
+class Group;
+}
+
+class Client;
+class Connection;
+class OscarContact;
+class OscarAccountPrivate;
+class QTextCodec;
+
+class KDE_EXPORT OscarAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+
+public:
+ OscarAccount(Kopete::Protocol *parent, const QString &accountID, const char *name=0L, bool isICQ=false);
+ virtual ~OscarAccount();
+
+ /** Provide the derived accounts and contacts with access to the backend */
+ Client* engine();
+
+ /** Disconnects this account */
+ virtual void disconnect();
+
+ /**
+ * Handle the various ways we can be logged off the oscar service
+ * and handle the passthrough of the disconnection through the API.
+ */
+ void logOff( Kopete::Account::DisconnectReason );
+
+ /**
+ * Was the password wrong last time we tried to connect?
+ */
+ bool passwordWasWrong();
+
+ /**
+ * Sets the account away
+ */
+ virtual void setAway( bool away, const QString &awayMessage = QString::null ) = 0;
+
+ /**
+ * Accessor method for the action menu
+ */
+ virtual KActionMenu* actionMenu() = 0;
+
+ /** Set the server address */
+ void setServerAddress( const QString& server );
+
+ /** Set the server port */
+ void setServerPort( int port );
+
+ /** Returns codec for account's default encoding */
+ QTextCodec* defaultCodec() const;
+
+ /**
+ * Returns codec for given contact's encoding or default one
+ * if contact has no encoding
+ */
+ QTextCodec* contactCodec( const OscarContact* contact ) const;
+
+ /**
+ * Returns codec for given contact's encoding or default one
+ * if contact has no encoding
+ */
+ QTextCodec* contactCodec( const QString& contactName ) const;
+
+ /**
+ * Sets buddy icon
+ */
+ void setBuddyIcon( KURL url );
+
+ /**
+ * Add a contact to the server site list
+ * \param contactName the screen name of the new contact to add
+ * \param groupName the group of the new contact
+ * \param autoAddGroup if the group doesn't exist add that group
+ * \return true if the contact will be added
+ */
+ bool addContactToSSI( const QString& contactName, const QString& groupName, bool autoAddGroup );
+
+ /**
+ * Change a contact's group on the server
+ * \param contact the contact to change
+ * \param newGroup the new group to move the contact to
+ * \param autoAddGroup if the new group doesn't exist add that group
+ * \return true if the contact will be added
+ */
+ bool changeContactGroupInSSI( const QString& contact, const QString& newGroupName, bool autoAddGroup );
+
+public slots:
+ void slotGoOffline();
+
+ void slotGoOnline();
+
+protected:
+ /**
+ * Setup a connection for a derived account based on the host and port
+ */
+ Connection* setupConnection( const QString& server, uint port );
+
+ /**
+ * Adds a contact to a meta contact
+ */
+ virtual bool createContact(const QString &contactId,
+ Kopete::MetaContact *parentContact );
+
+ /**
+ * Protocols using Oscar must implement this to perform the instantiation
+ * of their contact for Kopete. Called by @ref createContact().
+ * @param contactId theprotocol unique id of the contact
+ * @param parentContact the parent metacontact
+ * @return whether the creation succeeded or not
+ */
+ virtual OscarContact *createNewContact( const QString &contactId, Kopete::MetaContact *parentContact, const SSI& ssiItem ) = 0;
+
+ virtual QString sanitizedMessage( const QString& message ) = 0;
+
+ void updateVersionUpdaterStamp();
+
+protected slots:
+
+ //! do stuff on login
+ virtual void loginActions();
+
+ void processSSIList();
+
+ void kopeteGroupRemoved( Kopete::Group* g );
+ void kopeteGroupAdded( Kopete::Group* g );
+ void kopeteGroupRenamed( Kopete::Group* g, const QString& oldName );
+
+ virtual void messageReceived( const Oscar::Message& message );
+
+ void ssiGroupAdded( const Oscar::SSI& );
+ void ssiGroupUpdated( const Oscar::SSI& ) {}
+ void ssiGroupRemoved( const Oscar::SSI& ) {}
+ void ssiContactAdded( const Oscar::SSI& );
+ void ssiContactUpdated( const Oscar::SSI& );
+ void ssiContactRemoved( const Oscar::SSI& ) {}
+
+ /* slots for receiving typing notifications, and notify the appropriate OscarContact */
+ void userStartedTyping( const QString & contact );
+ void userStoppedTyping( const QString & contact );
+
+ void nonServerAddContactDialogClosed();
+
+signals:
+
+ void accountDisconnected( Kopete::Account::DisconnectReason reason );
+
+ void buddyIconChanged();
+
+private:
+ QString getFLAPErrorMessage( int code );
+
+private slots:
+ /** Handler from socket errors from a connection */
+ void slotSocketError( int, const QString& );
+
+ /** Handle task errors from the client */
+ void slotTaskError( const Oscar::SNAC& s, int errCode, bool fatal ) ;
+
+ /** Sends buddy icon to server */
+ void slotSendBuddyIcon();
+
+private:
+ OscarAccountPrivate *d;
+
+};
+
+#endif
+
+//kate: tab-width 4; indent-mode csands;
+
diff --git a/kopete/protocols/oscar/oscarcontact.cpp b/kopete/protocols/oscar/oscarcontact.cpp
new file mode 100644
index 00000000..660a82e6
--- /dev/null
+++ b/kopete/protocols/oscar/oscarcontact.cpp
@@ -0,0 +1,237 @@
+/*
+ oscarcontact.cpp - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarcontact.h"
+
+#include <time.h>
+
+#include <qapplication.h>
+#include <qtextcodec.h>
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include <kdeversion.h>
+
+#include "kopeteaccount.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetegroup.h"
+#include "kopeteuiglobal.h"
+#include <kopeteglobal.h>
+
+#include "oscaraccount.h"
+#include "client.h"
+#include "ssimanager.h"
+#include "oscarutils.h"
+
+#include <assert.h>
+
+OscarContact::OscarContact( Kopete::Account* account, const QString& name,
+ Kopete::MetaContact* parent, const QString& icon, const SSI& ssiItem )
+: Kopete::Contact( account, name, parent, icon )
+{
+ mAccount = static_cast<OscarAccount*>(account);
+ mName = name;
+ mMsgManager = 0L;
+ m_ssiItem = ssiItem;
+ connect( this, SIGNAL( updatedSSI() ), this, SLOT( updateSSIItem() ) );
+}
+
+OscarContact::~OscarContact()
+{
+}
+
+void OscarContact::serialize(QMap<QString, QString> &serializedData,
+ QMap<QString, QString> &/*addressBookData*/)
+{
+ serializedData["ssi_name"] = m_ssiItem.name();
+ serializedData["ssi_type"] = QString::number( m_ssiItem.type() );
+ serializedData["ssi_gid"] = QString::number( m_ssiItem.gid() );
+ serializedData["ssi_bid"] = QString::number( m_ssiItem.bid() );
+ serializedData["ssi_alias"] = m_ssiItem.alias();
+ serializedData["ssi_waitingAuth"] = m_ssiItem.waitingAuth() ? QString::fromLatin1( "true" ) : QString::fromLatin1( "false" );
+}
+
+bool OscarContact::isOnServer() const
+{
+ SSIManager* serverList = mAccount->engine()->ssiManager();
+ SSI ssi = serverList->findContact( Oscar::normalize( contactId() ) );
+
+ return ( ssi && ssi.type() != 0xFFFF );
+}
+
+void OscarContact::setSSIItem( const Oscar::SSI& ssiItem )
+{
+ m_ssiItem = ssiItem;
+
+ if ( !m_ssiItem.alias().isEmpty() )
+ setProperty( Kopete::Global::Properties::self()->nickName(), m_ssiItem.alias() );
+
+ emit updatedSSI();
+}
+
+Oscar::SSI OscarContact::ssiItem() const
+{
+ return m_ssiItem;
+}
+
+Kopete::ChatSession* OscarContact::manager( CanCreateFlags canCreate )
+{
+ if ( !mMsgManager && canCreate )
+ {
+ /*kdDebug(14190) << k_funcinfo <<
+ "Creating new ChatSession for contact '" << displayName() << "'" << endl;*/
+
+ QPtrList<Kopete::Contact> theContact;
+ theContact.append(this);
+
+ mMsgManager = Kopete::ChatSessionManager::self()->create(account()->myself(), theContact, protocol());
+
+ // This is for when the user types a message and presses send
+ connect(mMsgManager, SIGNAL( messageSent( Kopete::Message&, Kopete::ChatSession * ) ),
+ this, SLOT( slotSendMsg( Kopete::Message&, Kopete::ChatSession * ) ) );
+
+ // For when the message manager is destroyed
+ connect(mMsgManager, SIGNAL( destroyed() ),
+ this, SLOT( chatSessionDestroyed() ) );
+
+ connect(mMsgManager, SIGNAL( myselfTyping( bool ) ),
+ this, SLOT( slotTyping( bool ) ) );
+ }
+ return mMsgManager;
+}
+
+void OscarContact::deleteContact()
+{
+ mAccount->engine()->removeContact( contactId() );
+ deleteLater();
+}
+
+void OscarContact::chatSessionDestroyed()
+{
+ mMsgManager = 0L;
+}
+
+// Called when the metacontact owning this contact has changed groups
+void OscarContact::sync(unsigned int flags)
+{
+ /*
+ * If the contact has changed groups, then we update the server
+ * adding the group if it doesn't exist, changing the ssi item
+ * contained in the client and updating the contact's ssi item
+ * Otherwise, we don't do much
+ */
+
+ if( !metaContact() || metaContact()->isTemporary() )
+ return;
+
+ if ( (flags & Kopete::Contact::MovedBetweenGroup) == Kopete::Contact::MovedBetweenGroup )
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Moving a contact between groups" << endl;
+ SSIManager* ssiManager = mAccount->engine()->ssiManager();
+
+ SSI oldGroup = ssiManager->findGroup( m_ssiItem.gid() );
+ Kopete::Group* newGroup = metaContact()->groups().first();
+ if ( newGroup->displayName() == oldGroup.name() )
+ return; //we didn't really move
+
+ if ( m_ssiItem.isValid() )
+ mAccount->changeContactGroupInSSI( contactId(), newGroup->displayName(), true );
+ else
+ mAccount->addContactToSSI( contactId(), newGroup->displayName(), true );
+ }
+ return;
+}
+
+void OscarContact::userInfoUpdated( const QString& contact, const UserDetails& details )
+{
+ Q_UNUSED( contact );
+ setProperty( Kopete::Global::Properties::self()->onlineSince(), details.onlineSinceTime() );
+ setIdleTime( details.idleTime() );
+ m_warningLevel = details.warningLevel();
+ m_details.merge( details );
+
+ QStringList capList;
+ // Append client name and version in case we found one
+ if ( m_details.userClass() & 0x0080 /* WIRELESS */ )
+ capList << i18n( "Mobile AIM Client" );
+ else
+ {
+ if ( !m_details.clientName().isEmpty() )
+ {
+ capList << i18n( "Translators: client name and version",
+ "%1").arg( m_details.clientName() );
+ }
+ }
+
+ // and now for some general informative capabilities
+ if ( m_details.hasCap( CAP_BUDDYICON ) )
+ capList << i18n( "Buddy icons" );
+ if ( m_details.hasCap( CAP_UTF8 ) )
+ capList << i18n( "UTF-8" );
+ if ( m_details.hasCap( CAP_RTFMSGS ) )
+ capList << i18n( "Rich text messages" );
+ if ( m_details.hasCap( CAP_CHAT ) )
+ capList << i18n( "Group chat" );
+ if ( m_details.hasCap( CAP_VOICE ) )
+ capList << i18n( "Voice chat" );
+ if ( m_details.hasCap( CAP_IMIMAGE ) )
+ capList << i18n( "DirectIM/IMImage" );
+ if ( m_details.hasCap( CAP_SENDBUDDYLIST ) )
+ capList << i18n( "Send buddy list" );
+ if ( m_details.hasCap( CAP_SENDFILE ) )
+ capList << i18n( "File transfers" );
+ if ( m_details.hasCap( CAP_GAMES ) || m_details.hasCap( CAP_GAMES2 ) )
+ capList << i18n( "Games" );
+ if ( m_details.hasCap( CAP_TRILLIAN ) )
+ capList << i18n( "Trillian user" );
+
+ m_clientFeatures = capList.join( ", " );
+ emit featuresUpdated();
+}
+
+void OscarContact::startedTyping()
+{
+ if ( mMsgManager )
+ mMsgManager->receivedTypingMsg( this, true );
+}
+
+void OscarContact::stoppedTyping()
+{
+ if ( mMsgManager )
+ mMsgManager->receivedTypingMsg( this, false );
+}
+
+void OscarContact::slotTyping( bool typing )
+{
+ if ( this != account()->myself() )
+ account()->engine()->sendTyping( contactId(), typing );
+}
+
+QTextCodec* OscarContact::contactCodec() const
+{
+ if ( hasProperty( "contactEncoding" ) )
+ return QTextCodec::codecForMib( property( "contactEncoding" ).value().toInt() );
+ else
+ return mAccount->defaultCodec();
+}
+
+#include "oscarcontact.moc"
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/oscarcontact.h b/kopete/protocols/oscar/oscarcontact.h
new file mode 100644
index 00000000..51c31dd2
--- /dev/null
+++ b/kopete/protocols/oscar/oscarcontact.h
@@ -0,0 +1,140 @@
+/*
+ oscarcontact.h - Oscar Protocol Plugin
+
+ Copyright (c) 2002 by Tom Linsky <twl6@po.cwru.edu>
+ Copyright (c) 2004 by Matt Rogers <matt.rogers@kdemail.net>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef OSCARCONTACT_H
+#define OSCARCONTACT_H
+
+#include <qwidget.h>
+#include <qdatetime.h>
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+#include "userdetails.h"
+#include "client.h"
+#include "oscartypeclasses.h"
+
+namespace Kopete
+{
+class ChatSession;
+class OnlineStatus;
+}
+
+class OscarAccount;
+class QTimer;
+class QTextCodec;
+class KToggleAction;
+
+/**
+ * Contact for oscar protocol
+ * @author Matt Rogers
+ * @TODO Reimplement functions to do the following
+ * \li get the idle time
+ * \li get the real IP for the contact ( DC )
+ * \li get the local IP for the contact
+ * \li get the port for the DC
+ * \li get the sign on time for the contact
+ * \li get the status of authorization for the contact
+ * \li get the status of authoziation for the contact
+ * \li get the user info for the contact
+ * \li check if the contact has a certain capability
+ * \li request authorization from the contact
+ * \li get and set the custom encoding for the contact
+ * \li get and set the SSI group id for the contact
+ * \li get and set whether the contact is server side or not
+ * \li get/set the ignore setting for the contact
+ * \li get/set the visibility setting for the contact ( i.e. are we visible to the contact )
+ */
+class KDE_EXPORT OscarContact : public Kopete::Contact
+{
+Q_OBJECT
+
+public:
+ OscarContact( Kopete::Account* account, const QString& name,
+ Kopete::MetaContact* parent, const QString& icon = QString::null, const Oscar::SSI& ssiItem = Oscar::SSI() );
+
+ virtual ~OscarContact();
+
+ virtual void serialize(QMap<QString, QString>&, QMap<QString, QString>&);
+
+ virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CanCreate );
+
+ const QString &contactName() const { return mName; };
+ OscarAccount *account() const { return mAccount; };
+
+ bool isOnServer() const;
+
+ void setSSIItem( const Oscar::SSI& item );
+ Oscar::SSI ssiItem() const;
+
+ /** we received a typing notification from this contact, tell any message manager */
+ void startedTyping();
+ void stoppedTyping();
+
+ /**
+ * Returns codec for contact's encoding or default one
+ * if contact has no encoding
+ */
+ QTextCodec *contactCodec() const;
+
+public slots:
+ /** slot so that properties can be updated based on a new SSI item */
+ virtual void updateSSIItem() = 0;
+
+ /** Remove this contact from the server. Reimplemented from Kopete::Contact */
+ virtual void deleteContact();
+
+ /** the metacontact owning this contact changed */
+ virtual void sync(unsigned int flags);
+
+ /** our user info has been updated */
+ virtual void userInfoUpdated( const QString& contact, const UserDetails& details );
+
+ /** a user is online */
+ virtual void userOnline( const QString& ) = 0;
+
+ /** a user is offline */
+ virtual void userOffline( const QString& ) = 0;
+
+protected slots:
+ void slotTyping( bool typing );
+ virtual void updateFeatures() = 0;
+
+signals:
+ void updatedSSI();
+ void featuresUpdated();
+
+protected:
+ OscarAccount *mAccount;
+ QString mName;
+ Kopete::ChatSession *mMsgManager;
+ UserDetails m_details;
+ SSI m_ssiItem;
+ int m_warningLevel;
+ QString m_clientFeatures;
+
+private:
+ void initActions();
+
+protected slots:
+ virtual void slotSendMsg( Kopete::Message& msg, Kopete::ChatSession* session) = 0;
+
+private slots:
+ void chatSessionDestroyed();
+
+};
+
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/oscarencodingselectionbase.ui b/kopete/protocols/oscar/oscarencodingselectionbase.ui
new file mode 100644
index 00000000..1388b2d2
--- /dev/null
+++ b/kopete/protocols/oscar/oscarencodingselectionbase.ui
@@ -0,0 +1,58 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>OscarEncodingBaseUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>OscarEncodingBaseUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>290</width>
+ <height>55</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Use this &amp;encoding when chatting with this contact:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>encodingCombo</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="0">
+ <property name="name">
+ <cstring>encodingCombo</cstring>
+ </property>
+ </widget>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/oscarencodingselectiondialog.cpp b/kopete/protocols/oscar/oscarencodingselectiondialog.cpp
new file mode 100644
index 00000000..72e9081a
--- /dev/null
+++ b/kopete/protocols/oscar/oscarencodingselectiondialog.cpp
@@ -0,0 +1,121 @@
+// oscarencodingselectiondialog.cpp
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#include "oscarencodingselectionbase.h"
+#include "oscarencodingselectiondialog.h"
+
+#include <kdebug.h>
+#include <qcombobox.h>
+#include <klocale.h>
+
+OscarEncodingSelectionDialog::OscarEncodingSelectionDialog( QWidget* parent, int initialEncoding )
+ : KDialogBase( parent, 0, false, i18n( "Select Encoding" ), Ok | Cancel )
+{
+ int initialEncodingIndex;
+
+ clearWFlags( QWidget::WDestructiveClose );
+ m_encodingUI = new OscarEncodingBaseUI( this );
+ //fill the encoding combo boxes
+ m_encodings.insert(0, i18n("Default"));
+ m_encodings.insert(2026, i18n("Big5"));
+ m_encodings.insert(2101, i18n("Big5-HKSCS"));
+ m_encodings.insert(18, i18n("euc-JP Japanese"));
+ m_encodings.insert(38, i18n("euc-KR Korean"));
+ m_encodings.insert(57, i18n("GB-2312 Chinese"));
+ m_encodings.insert(113, i18n("GBK Chinese"));
+ m_encodings.insert(114, i18n("GB18030 Chinese"));
+
+ m_encodings.insert(16, i18n("JIS Japanese"));
+ m_encodings.insert(17, i18n("Shift-JIS Japanese"));
+
+ m_encodings.insert(2084, i18n("KOI8-R Russian"));
+ m_encodings.insert(2088, i18n("KOI8-U Ukrainian"));
+
+ m_encodings.insert(4, i18n("ISO-8859-1 Western"));
+ m_encodings.insert(5, i18n("ISO-8859-2 Central European"));
+ m_encodings.insert(6, i18n("ISO-8859-3 Central European"));
+ m_encodings.insert(7, i18n("ISO-8859-4 Baltic"));
+ m_encodings.insert(8, i18n("ISO-8859-5 Cyrillic"));
+ m_encodings.insert(9, i18n("ISO-8859-6 Arabic"));
+ m_encodings.insert(10, i18n("ISO-8859-7 Greek"));
+ m_encodings.insert(11, i18n("ISO-8859-8 Hebrew, visually ordered"));
+ m_encodings.insert(85, i18n("ISO-8859-8-I Hebrew, logically ordered"));
+ m_encodings.insert(12, i18n("ISO-8859-9 Turkish"));
+ m_encodings.insert(13, i18n("ISO-8859-10"));
+ m_encodings.insert(109, i18n("ISO-8859-13"));
+ m_encodings.insert(110, i18n("ISO-8859-14"));
+ m_encodings.insert(111, i18n("ISO-8859-15 Western"));
+
+ m_encodings.insert(2250, i18n("Windows-1250 Central European"));
+ m_encodings.insert(2251, i18n("Windows-1251 Cyrillic"));
+ m_encodings.insert(2252, i18n("Windows-1252 Western"));
+ m_encodings.insert(2253, i18n("Windows-1253 Greek"));
+ m_encodings.insert(2254, i18n("Windows-1254 Turkish"));
+ m_encodings.insert(2255, i18n("Windows-1255 Hebrew"));
+ m_encodings.insert(2256, i18n("Windows-1256 Arabic"));
+ m_encodings.insert(2257, i18n("Windows-1257 Baltic"));
+ m_encodings.insert(2258, i18n("Windows-1258 Viet Nam"));
+
+ m_encodings.insert(2009, i18n("IBM 850"));
+ m_encodings.insert(2085, i18n("IBM 866"));
+
+ m_encodings.insert(2259, i18n("TIS-620 Thai"));
+
+ m_encodings.insert(106, i18n("UTF-8 Unicode"));
+ m_encodings.insert(1015, i18n("UTF-16 Unicode"));
+
+ m_encodingUI->encodingCombo->insertStringList( m_encodings.values() );
+ if( (initialEncodingIndex = m_encodings.keys().findIndex(initialEncoding)) == -1 )
+ {
+ kdWarning() << k_funcinfo << "Requested encoding mib " << initialEncoding
+ << " not in encoding list - defaulting to first encoding item"
+ << " in list to be shown in combobox initially" << endl;
+ /* initialEncodingIndex = position in combobox, value 0 currently
+ * corresponds to ISO-8859-1, generally to the first item in combobox,
+ * which usually is the default
+ */
+ initialEncodingIndex = 0;
+ }
+ m_encodingUI->encodingCombo->setCurrentItem( initialEncodingIndex );
+ setMainWidget( m_encodingUI );
+}
+
+
+int OscarEncodingSelectionDialog::selectedEncoding() const
+{
+ QString encoding = m_encodingUI->encodingCombo->currentText();
+ int mib = m_encodings.keys()[ m_encodings.values().findIndex(encoding) ];
+
+ if( mib == -1 )
+ return 0;
+ return mib;
+}
+
+void OscarEncodingSelectionDialog::slotOk()
+{
+ emit closing( QDialog::Accepted );
+}
+
+void OscarEncodingSelectionDialog::slotCancel()
+{
+ emit closing( QDialog::Rejected );
+}
+
+#include "oscarencodingselectiondialog.moc"
+
diff --git a/kopete/protocols/oscar/oscarencodingselectiondialog.h b/kopete/protocols/oscar/oscarencodingselectiondialog.h
new file mode 100644
index 00000000..f99655e4
--- /dev/null
+++ b/kopete/protocols/oscar/oscarencodingselectiondialog.h
@@ -0,0 +1,48 @@
+// oscarencodingselectiondialog.h
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#ifndef OSCARENCODINGSELECTIONDIALOG_H
+#define OSCARENCODINGSELECTIONDIALOG_H
+
+#include <kdialogbase.h>
+#include "kopete_export.h"
+
+class OscarEncodingBaseUI;
+
+class KOPETE_EXPORT OscarEncodingSelectionDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ OscarEncodingSelectionDialog( QWidget* parent = 0, int initialEncoding = 4);
+ ~OscarEncodingSelectionDialog() {}
+
+ int selectedEncoding() const;
+
+signals:
+ void closing( int );
+
+protected slots:
+ void slotOk();
+ void slotCancel();
+
+private:
+ OscarEncodingBaseUI* m_encodingUI;
+ QMap<int, QString> m_encodings;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/oscarlistcontactsbase.ui b/kopete/protocols/oscar/oscarlistcontactsbase.ui
new file mode 100644
index 00000000..7a6d8f85
--- /dev/null
+++ b/kopete/protocols/oscar/oscarlistcontactsbase.ui
@@ -0,0 +1,49 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>OscarListContactsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>OscarListContactsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>478</width>
+ <height>361</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>The following contacts are not on your contact list. Would you like to add them?</string>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>notServerContacts</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>doNotShowAgain</cstring>
+ </property>
+ <property name="text">
+ <string>Do &amp;not ask again</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/oscarlistnonservercontacts.cpp b/kopete/protocols/oscar/oscarlistnonservercontacts.cpp
new file mode 100644
index 00000000..804849a4
--- /dev/null
+++ b/kopete/protocols/oscar/oscarlistnonservercontacts.cpp
@@ -0,0 +1,71 @@
+// oscarlistnonservercontacts.cpp
+
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+
+#include "oscarlistnonservercontacts.h"
+#include "oscarlistcontactsbase.h"
+#include <qstringlist.h>
+#include <qcheckbox.h>
+#include <klocale.h>
+
+OscarListNonServerContacts::OscarListNonServerContacts(QWidget* parent)
+ : KDialogBase( parent, 0, false, i18n( "Add Contacts to Server List" ),
+ Ok | Cancel )
+{
+ m_contactsList = new OscarListContactsBase( this );
+ setMainWidget( m_contactsList );
+ setButtonText( Ok, i18n( "&Add" ) );
+ setButtonText( Cancel, i18n( "Do &Not Add" ) );
+}
+
+OscarListNonServerContacts::~OscarListNonServerContacts()
+{
+
+}
+
+void OscarListNonServerContacts::addContacts( const QStringList& contactList )
+{
+ m_nonServerContacts = contactList;
+ m_contactsList->notServerContacts->insertStringList( contactList );
+}
+
+QStringList OscarListNonServerContacts::nonServerContactList() const
+{
+ return m_nonServerContacts;
+}
+
+bool OscarListNonServerContacts::onlyShowOnce()
+{
+ return m_contactsList->doNotShowAgain->isChecked();
+}
+
+
+void OscarListNonServerContacts::slotCancel()
+{
+ KDialogBase::slotCancel();
+ emit closing();
+}
+
+void OscarListNonServerContacts::slotOk()
+{
+ KDialogBase::slotOk();
+ emit closing();
+}
+
+#include "oscarlistnonservercontacts.moc"
diff --git a/kopete/protocols/oscar/oscarlistnonservercontacts.h b/kopete/protocols/oscar/oscarlistnonservercontacts.h
new file mode 100644
index 00000000..7f3fb4f8
--- /dev/null
+++ b/kopete/protocols/oscar/oscarlistnonservercontacts.h
@@ -0,0 +1,52 @@
+// oscarlistnonservercontacts.h
+// Copyright (C) 2005 Matt Rogers <mattr@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program 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 General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#ifndef OSCARLISTNONSERVERCONTACTS_H
+#define OSCARLISTNONSERVERCONTACTS_H
+
+#include <kdialogbase.h>
+#include "kopete_export.h"
+
+class OscarListContactsBase;
+class QStringList;
+
+class KOPETE_EXPORT OscarListNonServerContacts : public KDialogBase
+{
+Q_OBJECT
+public:
+ OscarListNonServerContacts( QWidget* parent );
+ ~OscarListNonServerContacts();
+
+ void addContacts( const QStringList& contactList );
+ QStringList nonServerContactList() const;
+
+ bool onlyShowOnce();
+
+protected:
+ virtual void slotOk();
+ virtual void slotCancel();
+
+signals:
+ void closing();
+
+private:
+ OscarListContactsBase* m_contactsList;
+ QStringList m_nonServerContacts;
+
+};
+#endif
diff --git a/kopete/protocols/oscar/oscarmyselfcontact.cpp b/kopete/protocols/oscar/oscarmyselfcontact.cpp
new file mode 100644
index 00000000..e234cf0f
--- /dev/null
+++ b/kopete/protocols/oscar/oscarmyselfcontact.cpp
@@ -0,0 +1,60 @@
+/*
+ oscarmyselfcontact.cpp - Oscar Protocol Plugin Myself Contact
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <klocale.h>
+
+#include "kopetecontactlist.h"
+
+#include "oscartypes.h"
+#include "oscaraccount.h"
+
+#include "oscarmyselfcontact.h"
+
+
+OscarMyselfContact::OscarMyselfContact( OscarAccount* account )
+: Kopete::Contact( account, account->accountId(), Kopete::ContactList::self()->myself() )
+{
+ QObject::connect( account->engine(), SIGNAL( haveOwnInfo() ), this, SLOT( userInfoUpdated() ) );
+}
+
+OscarMyselfContact::~OscarMyselfContact()
+{
+}
+
+bool OscarMyselfContact::isReachable()
+{
+ return false;
+}
+
+Kopete::ChatSession* OscarMyselfContact::manager(CanCreateFlags canCreate )
+{
+ return 0;
+}
+
+UserDetails OscarMyselfContact::details()
+{
+ OscarAccount *acct = static_cast<OscarAccount*>(account());
+ return acct->engine()->ourInfo();
+}
+
+void OscarMyselfContact::deleteContact()
+{
+ kdWarning( OSCAR_GEN_DEBUG ) << k_funcinfo << "called on myself contact! Ignoring." << endl << kdBacktrace() << endl;
+}
+
+#include "oscarmyselfcontact.moc"
+
+//kate: indent-mode csands;
diff --git a/kopete/protocols/oscar/oscarmyselfcontact.h b/kopete/protocols/oscar/oscarmyselfcontact.h
new file mode 100644
index 00000000..a8f7b1f8
--- /dev/null
+++ b/kopete/protocols/oscar/oscarmyselfcontact.h
@@ -0,0 +1,59 @@
+/*
+ oscarmyselfcontact.h - Oscar Protocol Plugin Myself Contact
+
+ Copyright (c) 2004 by Richard Smith <kde@metafoo.co.uk>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+ */
+
+#ifndef OSCARMYSELFCONTACT_H
+#define OSCARMYSELFCONTACT_H
+
+#include "kopetecontact.h"
+#include "userdetails.h"
+
+namespace Kopete
+{
+class ChatSession;
+class OnlineStatus;
+}
+
+class OscarAccount;
+class QTimer;
+class KToggleAction;
+
+/**
+ * myself() contact for oscar protocol
+ * @author Richard Smith
+ */
+class KDE_EXPORT OscarMyselfContact : public Kopete::Contact
+{
+Q_OBJECT
+
+public:
+ OscarMyselfContact( OscarAccount* account );
+ virtual ~OscarMyselfContact();
+
+ virtual bool isReachable();
+ virtual Kopete::ChatSession *manager( CanCreateFlags canCreate );
+
+ UserDetails details();
+
+public slots:
+ /** our user info has been updated */
+ virtual void userInfoUpdated() = 0;
+
+ /** I'm sorry Dave, I can't let you do that... */
+ virtual void deleteContact();
+};
+
+#endif
+//kate: tab-width 4; indent-mode csands;
diff --git a/kopete/protocols/oscar/oscarversionupdater.cpp b/kopete/protocols/oscar/oscarversionupdater.cpp
new file mode 100644
index 00000000..6e2cea09
--- /dev/null
+++ b/kopete/protocols/oscar/oscarversionupdater.cpp
@@ -0,0 +1,295 @@
+/*
+ oscarversionupdater.cpp - Version Updater
+
+ Copyright (c) 2006 by Roman Jarosz <kedgedev@centrum.cz>
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarversionupdater.h"
+
+#include <qdom.h>
+#include <qmutex.h>
+
+#include <kdebug.h>
+#include <kio/job.h>
+#include <kconfig.h>
+#include <kglobal.h>
+
+
+QMutex updateMutex;
+OscarVersionUpdater *OscarVersionUpdater::versionUpdaterStatic = 0L;
+
+OscarVersionUpdater::OscarVersionUpdater()
+: mStamp( 1 ), mUpdating( false )
+{
+ initICQVersionInfo();
+ initAIMVersionInfo();
+}
+
+OscarVersionUpdater::~OscarVersionUpdater()
+{
+}
+
+OscarVersionUpdater *OscarVersionUpdater::self()
+{
+ if ( !versionUpdaterStatic )
+ versionUpdaterStatic = new OscarVersionUpdater();
+
+ return versionUpdaterStatic;
+}
+
+bool OscarVersionUpdater::update( unsigned int stamp )
+{
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << endl;
+ bool doUpdate = false;
+ bool isUpdating = false;
+
+ updateMutex.lock();
+ if ( !mUpdating && stamp == mStamp )
+ {
+ doUpdate = true;
+ mUpdating = true;
+ }
+ isUpdating = mUpdating;
+ updateMutex.unlock();
+
+ if ( doUpdate )
+ {
+ mVersionData.resize( 0 );
+
+ KConfigGroup config( KGlobal::config(), "Oscar" );
+ QString url = config.readEntry( "NewVersionURL", "http://kopete.kde.org/oscarversions.xml" );
+ mTransferJob = KIO::get ( url );
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Download version info from server."<< endl;
+
+ connect ( mTransferJob, SIGNAL ( result ( KIO::Job* ) ),
+ this, SLOT ( slotTransferResult ( KIO::Job* ) ) );
+ connect ( mTransferJob, SIGNAL ( data ( KIO::Job*, const QByteArray& ) ),
+ this, SLOT ( slotTransferData ( KIO::Job*, const QByteArray& ) ) );
+ }
+ return isUpdating;
+}
+
+unsigned int OscarVersionUpdater::stamp() const
+{
+ return mStamp;
+}
+
+void OscarVersionUpdater::initICQVersionInfo()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+ KConfigGroup config( KGlobal::config(), "ICQVersion" );
+
+ mICQVersion.clientString = config.readEntry( "ClientString", "ICQBasic" );
+ mICQVersion.clientId = config.readEntry( "ClientId", "0x010A" ).toUShort( 0, 0 );
+ mICQVersion.major = config.readEntry( "Major", "0x0006" ).toUShort( 0, 0 );
+ mICQVersion.minor = config.readEntry( "Minor", "0x0000" ).toUShort( 0, 0 );
+ mICQVersion.point = config.readEntry( "Point", "0x0000" ).toUShort( 0, 0 );
+ mICQVersion.build = config.readEntry( "Build", "0x17AB" ).toUShort( 0, 0 );
+ mICQVersion.other = config.readEntry( "Other", "0x00007535" ).toUInt( 0, 0 );
+ mICQVersion.country = config.readEntry( "Country", "us" );
+ mICQVersion.lang = config.readEntry( "Lang", "en" );
+}
+
+void OscarVersionUpdater::initAIMVersionInfo()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+
+ KConfigGroup config( KGlobal::config(), "AIMVersion" );
+
+ mAIMVersion.clientString = config.readEntry( "ClientString", "AOL Instant Messenger (SM), version 5.1.3036/WIN32" );
+ mAIMVersion.clientId = config.readEntry( "ClientId", "0x0109" ).toUShort( 0, 0 );
+ mAIMVersion.major = config.readEntry( "Major", "0x0005" ).toUShort( 0, 0 );
+ mAIMVersion.minor = config.readEntry( "Minor", "0x0001" ).toUShort( 0, 0 );
+ mAIMVersion.point = config.readEntry( "Point", "0x0000" ).toUShort( 0, 0 );
+ mAIMVersion.build = config.readEntry( "Build", "0x0bdc" ).toUShort( 0, 0 );
+ mAIMVersion.other = config.readEntry( "Other", "0x000000d2" ).toUInt( 0, 0 );
+ mAIMVersion.country = config.readEntry( "Country", "us" );
+ mAIMVersion.lang = config.readEntry( "Lang", "en" );
+}
+
+void OscarVersionUpdater::printDebug()
+{
+ kdDebug(OSCAR_RAW_DEBUG) << "*************** AIM VERSION INFO ***************" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "client string: " << mAIMVersion.clientString << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "client id: " << QString::number( mAIMVersion.clientId, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "major: " << QString::number( mAIMVersion.major, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "minor: " << QString::number( mAIMVersion.minor, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "point: " << QString::number( mAIMVersion.point, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "build: " << QString::number( mAIMVersion.build, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "other: " << QString::number( mAIMVersion.other, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "country: " << mAIMVersion.country << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "lang: " << mAIMVersion.lang << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "************************************************" << endl;
+
+ kdDebug(OSCAR_RAW_DEBUG) << "*************** ICQ VERSION INFO ***************" << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "client string: " << mICQVersion.clientString << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "client id: " << QString::number( mICQVersion.clientId, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "major: " << QString::number( mICQVersion.major, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "minor: " << QString::number( mICQVersion.minor, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "point: " << QString::number( mICQVersion.point, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "build: " << QString::number( mICQVersion.build, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "other: " << QString::number( mICQVersion.other, 16 ) << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "country: " << mICQVersion.country << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "lang: " << mICQVersion.lang << endl;
+ kdDebug(OSCAR_RAW_DEBUG) << "************************************************" << endl;
+}
+
+void OscarVersionUpdater::slotTransferData ( KIO::Job */*job*/, const QByteArray &data )
+{
+ unsigned oldSize = mVersionData.size();
+ mVersionData.resize ( oldSize + data.size() );
+ memcpy ( &mVersionData.data()[oldSize], data.data(), data.size() );
+
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << "Data size " << mVersionData.size() << endl;
+}
+
+void OscarVersionUpdater::slotTransferResult ( KIO::Job *job )
+{
+ bool bUpdate = false;
+ if ( job->error() || mTransferJob->isErrorPage() )
+ {
+ //TODO show error
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Download of version info has faild!" << endl;
+ }
+ else
+ {
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Updating version info" << endl;
+
+ QDomDocument doc;
+ if ( doc.setContent ( mVersionData ) )
+ {
+ Oscar::ClientVersion tmpICQ = mICQVersion;
+ Oscar::ClientVersion tmpAIM = mAIMVersion;
+
+ parseDocument( doc );
+
+ if ( !equal( tmpICQ, mICQVersion ) )
+ {
+ storeVersionInfo( "ICQVersion", mICQVersion );
+ bUpdate = true;
+ }
+
+ if ( !equal( tmpAIM, mAIMVersion ) )
+ {
+ storeVersionInfo( "AIMVersion", mAIMVersion );
+ bUpdate = true;
+ }
+ }
+ }
+
+ // clear
+ mVersionData.resize( 0 );
+ mTransferJob = 0;
+
+ updateMutex.lock();
+ if ( bUpdate )
+ mStamp++;
+ mUpdating = false;
+ updateMutex.unlock();
+}
+
+void OscarVersionUpdater::parseDocument( QDomDocument& doc )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+
+ QDomElement root = doc.documentElement();
+ if ( root.tagName() != "oscar" )
+ return;
+
+ QDomElement versionElement = root.firstChild().toElement();
+ while( !versionElement.isNull() )
+ {
+ if ( versionElement.tagName() == "icq" )
+ parseVersion( mICQVersion, versionElement );
+ else if ( versionElement.tagName() == "aim" )
+ parseVersion( mAIMVersion, versionElement );
+
+ versionElement = versionElement.nextSibling().toElement();
+ }
+}
+
+bool OscarVersionUpdater::parseVersion( Oscar::ClientVersion& version, QDomElement& element )
+{
+ kdDebug(OSCAR_RAW_DEBUG) << k_funcinfo << endl;
+
+ // clear structure
+ version.clientString = QString::null;
+ version.clientId = 0x0000;
+ version.major = 0x0000;
+ version.minor = 0x0000;
+ version.point = 0x0000;
+ version.build = 0x0000;
+ version.other = 0x00000000;
+ version.country = QString::null;
+ version.lang = QString::null;
+
+ QDomElement versionChild = element.firstChild().toElement();
+ while ( !versionChild.isNull() )
+ {
+ if ( versionChild.tagName() == "client" )
+ version.clientString = versionChild.text();
+ else if ( versionChild.tagName() == "clientId" )
+ version.clientId = versionChild.text().toUShort( 0, 0);
+ else if ( versionChild.tagName() == "major" )
+ version.major = versionChild.text().toUShort( 0, 0 );
+ else if ( versionChild.tagName() == "minor" )
+ version.minor = versionChild.text().toUShort( 0, 0 );
+ else if ( versionChild.tagName() == "point" )
+ version.point = versionChild.text().toUShort( 0, 0 );
+ else if ( versionChild.tagName() == "build" )
+ version.build = versionChild.text().toUShort( 0, 0 );
+ else if ( versionChild.tagName() == "other" )
+ version.other = versionChild.text().toUInt( 0, 0 );
+ else if ( versionChild.tagName() == "country" )
+ version.country = versionChild.text();
+ else if ( versionChild.tagName() == "lang" )
+ version.lang = versionChild.text();
+
+ versionChild = versionChild.nextSibling().toElement();
+ }
+
+ return true;
+}
+
+void OscarVersionUpdater::storeVersionInfo( const QString& group, const Oscar::ClientVersion& version ) const
+{
+ kdDebug(OSCAR_GEN_DEBUG) << k_funcinfo << "Storing version info to group: " << group << endl;
+ KConfigGroup config( KGlobal::config(), group );
+
+ config.writeEntry( "ClientString", version.clientString );
+ config.writeEntry( "ClientId", version.clientId );
+ config.writeEntry( "Major", version.major );
+ config.writeEntry( "Minor", version.minor );
+ config.writeEntry( "Point", version.point );
+ config.writeEntry( "Build", version.build );
+ config.writeEntry( "Other", version.other );
+ config.writeEntry( "Country", version.country );
+ config.writeEntry( "Lang", version.lang );
+ config.sync();
+}
+
+bool OscarVersionUpdater::equal( const Oscar::ClientVersion& a, const Oscar::ClientVersion& b ) const
+{
+ if ( a.clientString != b.clientString || a.clientId != b.clientId ||
+ a.major != b.major|| a.minor != b.minor ||
+ a.point != b.point || a.build != b.build ||
+ a.other != b.other || a.country != b.country ||
+ a.lang != b.lang )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+#include "oscarversionupdater.moc"
diff --git a/kopete/protocols/oscar/oscarversionupdater.h b/kopete/protocols/oscar/oscarversionupdater.h
new file mode 100644
index 00000000..d6851f73
--- /dev/null
+++ b/kopete/protocols/oscar/oscarversionupdater.h
@@ -0,0 +1,120 @@
+/*
+ oscarversionupdater.h - Version Updater
+
+ Copyright (c) 2006 by Roman Jarosz <kedgedev@centrum.cz>
+ Kopete (c) 2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCARVERSIONUPDATER_H
+#define OSCARVERSIONUPDATER_H
+
+#include <qobject.h>
+
+#include <oscartypes.h>
+
+namespace KIO
+{
+class Job;
+class TransferJob;
+}
+
+class QDomElement;
+class QDomDocument;
+
+/**
+ @author Roman Jarosz <kedgedev@centrum.cz>
+*/
+
+class OscarVersionUpdater : public QObject
+{
+ Q_OBJECT
+
+public:
+ OscarVersionUpdater();
+ ~OscarVersionUpdater();
+
+ static OscarVersionUpdater* self();
+
+ /**
+ * Update version info from server.
+ * @param stamp is update number.
+ */
+ bool update( unsigned int stamp );
+
+ /**
+ * Update version info from server.
+ * @return true if update is in progress or starts.
+ */
+ unsigned int stamp() const;
+
+ /**
+ * Return structure with version info for ICQ.
+ * @return Oscar::ClientVersion.
+ */
+ const Oscar::ClientVersion* getICQVersion() const { return &mICQVersion; }
+
+ /**
+ * Return structure with version info for AIM.
+ * @return Oscar::ClientVersion.
+ */
+ const Oscar::ClientVersion* getAIMVersion() const { return &mAIMVersion; }
+
+ /**
+ * Set structure with ICQ version info to default.
+ */
+ void initICQVersionInfo();
+
+ /**
+ * Set structure with AIM version info to default.
+ */
+ void initAIMVersionInfo();
+
+ /**
+ * Print debug info.
+ */
+ void printDebug();
+
+private slots:
+ void slotTransferData( KIO::Job *job, const QByteArray &data );
+ void slotTransferResult( KIO::Job *job );
+
+private:
+ void parseDocument( QDomDocument& doc );
+ bool parseVersion( Oscar::ClientVersion& version, QDomElement& element );
+
+ /**
+ * Store version info structure to KConfigGroup
+ * @param group is the group name.
+ * @param version is version info structure.
+ */
+ void storeVersionInfo( const QString& group, const Oscar::ClientVersion& version ) const;
+
+ /**
+ * Compare two versions.
+ * @return true if a and b is equal.
+ */
+ bool equal( const Oscar::ClientVersion& a, const Oscar::ClientVersion& b ) const;
+
+private:
+ static OscarVersionUpdater *versionUpdaterStatic;
+
+ Oscar::ClientVersion mICQVersion;
+ Oscar::ClientVersion mAIMVersion;
+
+ KIO::TransferJob *mTransferJob;
+ QByteArray mVersionData;
+
+ unsigned int mStamp;
+ bool mUpdating;
+};
+
+#endif
diff --git a/kopete/protocols/oscar/oscarvisibilitybase.ui b/kopete/protocols/oscar/oscarvisibilitybase.ui
new file mode 100644
index 00000000..a6b98799
--- /dev/null
+++ b/kopete/protocols/oscar/oscarvisibilitybase.ui
@@ -0,0 +1,170 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>OscarVisibilityBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>OscarVisibilityBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>594</width>
+ <height>409</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Always visible:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Contacts:</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0" rowspan="9" colspan="1">
+ <property name="name">
+ <cstring>contacts</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>visibleAdd</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>visibleRemove</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <spacer row="4" column="1">
+ <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>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QListBox" row="1" column="2" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>visibleContacts</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="8" column="1">
+ <property name="name">
+ <cstring>invisibleRemove</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <spacer row="9" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="7" column="1">
+ <property name="name">
+ <cstring>invisibleAdd</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="6" column="2" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>invisibleContacts</cstring>
+ </property>
+ </widget>
+ <spacer row="6" column="1">
+ <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>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Always invisible:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>contacts</tabstop>
+ <tabstop>visibleAdd</tabstop>
+ <tabstop>visibleRemove</tabstop>
+ <tabstop>invisibleAdd</tabstop>
+ <tabstop>invisibleRemove</tabstop>
+ <tabstop>visibleContacts</tabstop>
+ <tabstop>invisibleContacts</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/oscar/oscarvisibilitydialog.cpp b/kopete/protocols/oscar/oscarvisibilitydialog.cpp
new file mode 100644
index 00000000..7958432c
--- /dev/null
+++ b/kopete/protocols/oscar/oscarvisibilitydialog.cpp
@@ -0,0 +1,135 @@
+/*
+ oscarvisibilitydialog.cpp - Visibility Dialog
+
+ Copyright (c) 2005 by Roman Jarosz <kedgedev@centrum.cz>
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "oscarvisibilitydialog.h"
+
+#include <qstringlist.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+
+#include "oscarvisibilitybase.h"
+#include "client.h"
+
+
+OscarVisibilityDialog::OscarVisibilityDialog( Client* client, QWidget* parent )
+ : KDialogBase( parent, 0, false, i18n( "Add Contacts to Visible or Invisible List" ),
+ Ok | Cancel ), m_client( client )
+{
+ m_visibilityUI = new OscarVisibilityBase( this );
+ setMainWidget( m_visibilityUI );
+
+ QObject::connect(m_visibilityUI->visibleAdd, SIGNAL(clicked()),
+ this, SLOT(slotAddToVisible()));
+ QObject::connect(m_visibilityUI->visibleRemove, SIGNAL(clicked()),
+ this, SLOT(slotRemoveFromVisible()));
+ QObject::connect(m_visibilityUI->invisibleAdd, SIGNAL(clicked()),
+ this, SLOT(slotAddToInvisible()));
+ QObject::connect(m_visibilityUI->invisibleRemove, SIGNAL(clicked()),
+ this, SLOT(slotRemoveFromInvisible()));
+}
+
+void OscarVisibilityDialog::addContacts( const ContactMap& contacts )
+{
+ m_contactMap = contacts;
+
+ ContactMap::Iterator it, cEnd = m_contactMap.end();
+ for ( it = m_contactMap.begin(); it != cEnd; ++it )
+ m_visibilityUI->contacts->insertItem( it.key() );
+
+}
+
+void OscarVisibilityDialog::addVisibleContacts( const QStringList& contactList )
+{
+ m_visibilityUI->visibleContacts->insertStringList( contactList );
+}
+
+void OscarVisibilityDialog::addInvisibleContacts( const QStringList& contactList )
+{
+ m_visibilityUI->invisibleContacts->insertStringList( contactList );
+}
+
+void OscarVisibilityDialog::slotAddToVisible()
+{
+ QListBoxItem *itm = m_visibilityUI->contacts->selectedItem();
+ if ( !itm ) return;
+
+ QString contactId = m_contactMap[itm->text()];
+ m_visibleListChangesMap[contactId] = Add;
+
+ if ( !m_visibilityUI->visibleContacts->findItem( itm->text(), Qt::CaseSensitive | Qt::ExactMatch ) )
+ m_visibilityUI->visibleContacts->insertItem( itm->text() );
+}
+
+void OscarVisibilityDialog::slotRemoveFromVisible()
+{
+ QListBoxItem *itm = m_visibilityUI->visibleContacts->selectedItem();
+ if ( !itm ) return;
+
+ QString contactId = m_contactMap[itm->text()];
+ m_visibleListChangesMap[contactId] = Remove;
+
+ int visIdx = m_visibilityUI->visibleContacts->index( itm );
+ m_visibilityUI->visibleContacts->removeItem( visIdx );
+}
+
+void OscarVisibilityDialog::slotAddToInvisible()
+{
+ QListBoxItem *itm = m_visibilityUI->contacts->selectedItem();
+ if ( !itm ) return;
+
+ QString contactId = m_contactMap[itm->text()];
+ m_invisibleListChangesMap[contactId] = Add;
+
+ if ( !m_visibilityUI->invisibleContacts->findItem( itm->text(), Qt::CaseSensitive | Qt::ExactMatch ) )
+ m_visibilityUI->invisibleContacts->insertItem( itm->text() );
+}
+
+void OscarVisibilityDialog::slotRemoveFromInvisible()
+{
+ QListBoxItem *itm = m_visibilityUI->invisibleContacts->selectedItem();
+ if ( !itm ) return;
+
+ QString contactId = m_contactMap[itm->text()];
+ m_invisibleListChangesMap[contactId] = Remove;
+
+ int visIdx = m_visibilityUI->invisibleContacts->index( itm );
+ m_visibilityUI->invisibleContacts->removeItem( visIdx );
+}
+
+void OscarVisibilityDialog::slotOk()
+{
+ ChangeMap::Iterator it, cEnd = m_visibleListChangesMap.end();
+ for ( it = m_visibleListChangesMap.begin(); it != cEnd; ++it ) {
+ m_client->setVisibleTo( it.key(), it.data() );
+ }
+
+ cEnd = m_invisibleListChangesMap.end();
+ for ( it = m_invisibleListChangesMap.begin(); it != cEnd; ++it ) {
+ m_client->setInvisibleTo( it.key(), it.data() );
+ }
+
+ KDialogBase::slotOk();
+ emit closing();
+}
+
+void OscarVisibilityDialog::slotCancel()
+{
+ KDialogBase::slotCancel();
+ emit closing();
+}
+
+#include "oscarvisibilitydialog.moc"
diff --git a/kopete/protocols/oscar/oscarvisibilitydialog.h b/kopete/protocols/oscar/oscarvisibilitydialog.h
new file mode 100644
index 00000000..fc45039f
--- /dev/null
+++ b/kopete/protocols/oscar/oscarvisibilitydialog.h
@@ -0,0 +1,70 @@
+/*
+ oscarvisibilitydialog.h - Visibility Dialog
+
+ Copyright (c) 2005 by Roman Jarosz <kedgedev@centrum.cz>
+ Kopete (c) 2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef OSCARVISIBILITYDIALOG_H
+#define OSCARVISIBILITYDIALOG_H
+
+#include <kdialogbase.h>
+#include "kopete_export.h"
+
+/**
+ @author Roman Jarosz <kedgedev@centrum.cz>
+*/
+class OscarVisibilityBase;
+class QStringList;
+class Client;
+
+class KOPETE_EXPORT OscarVisibilityDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ typedef QMap<QString, QString> ContactMap;
+
+ OscarVisibilityDialog( Client* client, QWidget* parent );
+ ~OscarVisibilityDialog() {}
+
+ void addContacts( const ContactMap& contacts );
+ void addVisibleContacts( const QStringList& contactList );
+ void addInvisibleContacts( const QStringList& contactList );
+
+signals:
+ void closing();
+
+protected:
+ virtual void slotOk();
+ virtual void slotCancel();
+
+protected slots:
+ void slotAddToVisible();
+ void slotRemoveFromVisible();
+ void slotAddToInvisible();
+ void slotRemoveFromInvisible();
+
+private:
+ enum Action{ Remove = 0, Add };
+ typedef QMap<QString, Action> ChangeMap;
+
+ //maps with changes that should be send to server
+ ChangeMap m_visibleListChangesMap;
+ ChangeMap m_invisibleListChangesMap;
+
+ ContactMap m_contactMap;
+
+ OscarVisibilityBase* m_visibilityUI;
+ Client* m_client;
+};
+
+#endif
diff --git a/kopete/protocols/sms/Makefile.am b/kopete/protocols/sms/Makefile.am
new file mode 100644
index 00000000..6a42aebc
--- /dev/null
+++ b/kopete/protocols/sms/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+SUBDIRS = ui services icons
+AM_CPPFLAGS = -I$(srcdir)/ui \
+ -I./ui \
+ -I$(srcdir)/services \
+ -I./services \
+ $(KOPETE_INCLUDES) \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_sms.la
+
+kopete_sms_la_SOURCES = smsaddcontactpage.cpp smscontact.cpp smseditaccountwidget.cpp \
+ smsprotocol.cpp serviceloader.cpp smsservice.cpp smsuserpreferences.cpp \
+ smsaccount.cpp
+kopete_sms_la_LDFLAGS = -no-undefined -module $(KDE_PLUGIN) $(all_libraries)
+kopete_sms_la_LIBADD = ./ui/libkopetesmsui.la \
+ ./services/libkopetesmsservices.la \
+ ../../libkopete/libkopete.la $(LIB_KIO)
+
+service_DATA = kopete_sms.desktop
+servicedir = $(kde_servicesdir)
diff --git a/kopete/protocols/sms/icons/Makefile.am b/kopete/protocols/sms/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/protocols/sms/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/protocols/sms/icons/cr128-app-sms_protocol.png b/kopete/protocols/sms/icons/cr128-app-sms_protocol.png
new file mode 100644
index 00000000..732327e2
--- /dev/null
+++ b/kopete/protocols/sms/icons/cr128-app-sms_protocol.png
Binary files differ
diff --git a/kopete/protocols/sms/icons/cr16-app-sms_protocol.png b/kopete/protocols/sms/icons/cr16-app-sms_protocol.png
new file mode 100644
index 00000000..3bfa3627
--- /dev/null
+++ b/kopete/protocols/sms/icons/cr16-app-sms_protocol.png
Binary files differ
diff --git a/kopete/protocols/sms/icons/cr32-app-sms_protocol.png b/kopete/protocols/sms/icons/cr32-app-sms_protocol.png
new file mode 100644
index 00000000..16110804
--- /dev/null
+++ b/kopete/protocols/sms/icons/cr32-app-sms_protocol.png
Binary files differ
diff --git a/kopete/protocols/sms/icons/cr48-app-sms_protocol.png b/kopete/protocols/sms/icons/cr48-app-sms_protocol.png
new file mode 100644
index 00000000..82049813
--- /dev/null
+++ b/kopete/protocols/sms/icons/cr48-app-sms_protocol.png
Binary files differ
diff --git a/kopete/protocols/sms/icons/cr64-app-sms_protocol.png b/kopete/protocols/sms/icons/cr64-app-sms_protocol.png
new file mode 100644
index 00000000..527771f7
--- /dev/null
+++ b/kopete/protocols/sms/icons/cr64-app-sms_protocol.png
Binary files differ
diff --git a/kopete/protocols/sms/kopete_sms.desktop b/kopete/protocols/sms/kopete_sms.desktop
new file mode 100644
index 00000000..783aa416
--- /dev/null
+++ b/kopete/protocols/sms/kopete_sms.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=sms_protocol
+ServiceTypes=Kopete/Protocol
+X-Kopete-Messaging-Protocol=messaging/sms
+X-KDE-Library=kopete_sms
+X-KDE-PluginInfo-Author=Richard Lärkäng
+X-KDE-PluginInfo-Email=richard@goteborg.utfors.se
+X-KDE-PluginInfo-Name=kopete_sms
+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=SMS
+Name[bn]=à¦à¦¸-à¦à¦®-à¦à¦¸
+Name[fa]=خدمت پیام کوتاه
+Name[hi]=à¤à¤¸à¤à¤®à¤à¤¸
+Name[km]=សáŸážœáž¶â€‹ážŸáž¶ážšâ€‹ážáŸ’លីៗ
+Name[ne]=à¤à¤¸ à¤à¤® à¤à¤¸
+Name[zh_CN]=短信æ¯
+Comment=Protocol to send SMS messages
+Comment[ar]=سيرسل البروتوكول رسالة عن طريق خدمة الرسائل القصيرة
+Comment[be]=Пратакол Ð°Ð´Ð¿Ñ€Ð°ÑžÐ»ÐµÐ½Ð½Ñ Ð¿Ð°Ð²ÐµÐ´Ð°Ð¼Ð»ÐµÐ½Ð½ÑÑž SMS
+Comment[bg]=Протокол за изпращане на SMS ÑъобщениÑ
+Comment[bn]=à¦à¦¸-à¦à¦®-à¦à¦¸ বারà§à¦¤à¦¾ পাঠাতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad evit kas kemennadoù SMS
+Comment[bs]=Protokol za slanje SMS poruka
+Comment[ca]=Protocol per a enviar missatges SMS
+Comment[cs]=Protokol k odesílání SMS zpráv
+Comment[cy]=Protocol i anfon negeseuon SMS
+Comment[da]=Protokol til at sende SMS-beskeder
+Comment[de]=Protokoll zur Versendung von SMS-Nachrichten
+Comment[el]=ΠÏωτόκολλο για αποστολή μηνυμάτων SMS
+Comment[es]=Protocolo de envío de SMS
+Comment[et]=Protokoll SMS-ide saatmiseks
+Comment[eu]=SMS mezuak bidaltzeko protokoloa
+Comment[fa]=قرارداد برای ارسال پیامهای کوتاه
+Comment[fi]=Yhteyskäytäntö SMS-viestien lähettämiseen
+Comment[fr]=Protocole pour envoyer des SMS
+Comment[ga]=Prótacal chun teachtaireachtaí SMS a sheoladh
+Comment[gl]=Protocolo para enviar mansaxes SMS
+Comment[he]=פרוטוקול שליחת הודעות SMS
+Comment[hi]=से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za slanje SMS poruka
+Comment[hu]=Protokoll SMS üzenetek küldéséhez
+Comment[is]=Samskiptamáti til að senda SMS skilaboð
+Comment[it]=Protocollo per inviare messaggi SMS
+Comment[ja]=SMS メッセージをé€ã‚‹ãƒ—ロトコル
+Comment[ka]=SMS შეტყáƒáƒ‘ინებების გáƒáƒ’ზáƒáƒ•áƒœáƒ˜áƒ¡ áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=SMS хабарларын жіберу протоколы
+Comment[km]=ពិធីការ​ដើម្បី​ផ្ញើ​សារ​ážáŸ’លីៗ
+Comment[lt]=Protokolas SMS žinuÄių siuntimui
+Comment[mk]=Протокол за иÑпраќање на SMS-пораки
+Comment[nb]=Protokoll for å sende SMS-meldinger
+Comment[nds]=Protokoll för't Sennen vun SMS-Narichten
+Comment[ne]=à¤à¤¸ à¤à¤® à¤à¤¸ सनà¥à¤¦à¥‡à¤¶ पठाउने पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor verzenden van SMS-berichten
+Comment[nn]=Protokoll for å senda SMS-meldingar
+Comment[pl]=Protokół wysyłania wiadomości SMS
+Comment[pt]=Protocolo para enviar mensagens SMS
+Comment[pt_BR]=Protocolo para o envio de mensagens SMS
+Comment[ro]=Protocol de trimis mesaje SMS
+Comment[ru]=Протокол отправки Ñообщений SMS
+Comment[sk]=Protokol pre posielanie správ SMS
+Comment[sl]=Protokol za poÅ¡iljanje SMS sporoÄil
+Comment[sr]=Протокол за Ñлање SMS порука
+Comment[sr@Latn]=Protokol za slanje SMS poruka
+Comment[sv]=Protokoll för att skicka SMS-meddelanden
+Comment[ta]=SMS செயà¯à®¤à®¿ அனà¯à®ªà¯à®ª விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордод барои фириÑтодани SMS пайёмҳо
+Comment[tr]=SMS mesajları gönderme iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð²Ñ–Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ SMS повідомлень
+Comment[uz]=SMS xabarlarni joʻnatish uchun protokol
+Comment[uz@cyrillic]=SMS хабарларни жўнатиш учун протокол
+Comment[wa]=Protocole po-z evoyî des messaedje SMS
+Comment[zh_CN]=å‘é€çŸ­ä¿¡æ¯çš„åè®®
+Comment[zh_HK]=ç”¨ä¾†ç™¼é€ SMS 訊æ¯çš„通訊å”定
+Comment[zh_TW]=é€ SMS 訊æ¯çš„å”定
diff --git a/kopete/protocols/sms/serviceloader.cpp b/kopete/protocols/sms/serviceloader.cpp
new file mode 100644
index 00000000..8a64a6c0
--- /dev/null
+++ b/kopete/protocols/sms/serviceloader.cpp
@@ -0,0 +1,74 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "config.h"
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "serviceloader.h"
+#include "smssend.h"
+#include "smsclient.h"
+#ifdef INCLUDE_SMSGSM
+# include "gsmlib.h"
+#endif
+#include "kopeteuiglobal.h"
+
+SMSService* ServiceLoader::loadService(const QString& name, Kopete::Account* account)
+{
+ kdWarning( 14160 ) << k_funcinfo << endl;
+
+ SMSService* s;
+ if (name == "SMSSend")
+ s = new SMSSend(account);
+ else if (name == "SMSClient")
+ s = new SMSClient(account);
+#ifdef INCLUDE_SMSGSM
+ else if (name == "GSMLib")
+ s = new GSMLib(account);
+#endif
+ else
+ {
+ KMessageBox::sorry(Kopete::UI::Global::mainWidget(), i18n("Could not load service %1.").arg(name),
+ i18n("Error Loading Service"));
+ s = 0L;
+ }
+
+ return s;
+}
+
+QStringList ServiceLoader::services()
+{
+ QStringList toReturn;
+ toReturn.append("SMSSend");
+ toReturn.append("SMSClient");
+#ifdef INCLUDE_SMSGSM
+ toReturn.append("GSMLib");
+#endif
+ return toReturn;
+}
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/serviceloader.h b/kopete/protocols/sms/serviceloader.h
new file mode 100644
index 00000000..fe29ebcc
--- /dev/null
+++ b/kopete/protocols/sms/serviceloader.h
@@ -0,0 +1,42 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SERVICELOADER_H
+#define SERVICELOADER_H
+
+#include "smsservice.h"
+#include <qstring.h>
+#include <qstringlist.h>
+
+class SMSContact;
+
+class ServiceLoader
+{
+public:
+ static SMSService* loadService(const QString& name, Kopete::Account* account);
+ static QStringList services();
+} ;
+
+#endif //SERVICELOADER_H
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/services/Makefile.am b/kopete/protocols/sms/services/Makefile.am
new file mode 100644
index 00000000..e21f1505
--- /dev/null
+++ b/kopete/protocols/sms/services/Makefile.am
@@ -0,0 +1,18 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ -I.. \
+ $(all_includes)
+
+KDE_CXXFLAGS = $(USE_EXCEPTIONS)
+
+noinst_LTLIBRARIES = libkopetesmsservices.la
+
+
+libkopetesmsservices_la_SOURCES = smssend.cpp smssendprefs.ui smssendprovider.cpp \
+ smsclient.cpp smsclientprefs.ui gsmlib.cpp gsmlibprefs.ui kopete_unix_serial.cpp
+
+if include_smsgsm
+libkopetesmsservices_la_LIBADD = -lgsmme
+endif
+
diff --git a/kopete/protocols/sms/services/gsmlib.cpp b/kopete/protocols/sms/services/gsmlib.cpp
new file mode 100644
index 00000000..e9b0542b
--- /dev/null
+++ b/kopete/protocols/sms/services/gsmlib.cpp
@@ -0,0 +1,462 @@
+/* *************************************************************************
+ * copyright: (C) 2005 Justin Huff <jjhuff@mspin.net> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * 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 "config.h"
+#ifdef INCLUDE_SMSGSM
+
+#include <qcombobox.h>
+#include <qlayout.h>
+#include <qapplication.h>
+#include <qevent.h>
+#include <qmutex.h>
+#include <qthread.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kdebug.h>
+#include <kconfigbase.h>
+
+#include <unistd.h>
+#include <gsmlib/gsm_me_ta.h>
+#include <gsmlib/gsm_sms.h>
+#include <gsmlib/gsm_util.h>
+#include <gsmlib/gsm_error.h>
+
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+
+#include "gsmlib.h"
+#include "gsmlibprefs.h"
+#include "smsprotocol.h"
+#include "smscontact.h"
+
+#include "kopete_unix_serial.h"
+
+/////////////////////////////////////////////////////////////////////
+#define GSMLIB_EVENT_ID 245
+GSMLibEvent::GSMLibEvent(SubType t) : QCustomEvent(QEvent::User+GSMLIB_EVENT_ID)
+{
+ setSubType(t);
+}
+
+GSMLibEvent::SubType GSMLibEvent::subType()
+{
+ return m_subType;
+}
+
+void GSMLibEvent::setSubType(GSMLibEvent::SubType t)
+{
+ m_subType = t;
+}
+
+/////////////////////////////////////////////////////////////////////
+GSMLibThread::GSMLibThread(QString dev, GSMLib* parent)
+{
+ m_device = dev;
+ m_parent = parent;
+ m_run = true;
+ m_MeTa = NULL;
+}
+
+GSMLibThread::~GSMLibThread()
+{
+ m_run = false;
+}
+
+void GSMLibThread::stop()
+{
+ m_run = false;
+ kdDebug( 14160 ) << "Waiting from GSMLibThread to die"<<endl;
+ if( wait(4000) == false )
+ kdWarning( 14160 ) << "GSMLibThread didn't exit!"<<endl;
+}
+void GSMLibThread::run()
+{
+ if( doConnect() )
+ {
+ while( m_run )
+ {
+ pollForMessages();
+ sendMessageQueue();
+ }
+ }
+
+ delete m_MeTa;
+ m_MeTa = NULL;
+ QApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::DISCONNECTED));
+ kdDebug( 14160 ) << "GSMLibThread exited"<<endl;
+}
+
+void GSMLibThread::send(const Kopete::Message& msg)
+{
+ if( m_MeTa )
+ {
+ m_outMessagesMutex.lock();
+ m_outMessages.push_back(msg);
+ m_outMessagesMutex.unlock();
+ }
+ else
+ {
+ GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
+ e->Reason = QString("GSMLib: Not Connected");
+ e->Message = msg;
+ QApplication::postEvent(m_parent, e);
+ }
+}
+
+
+bool GSMLibThread::doConnect()
+{
+ // open the port and ME/TA
+ try
+ {
+ kdDebug( 14160 ) << "Connecting to: '"<<m_device<<"'"<<endl;
+
+ gsmlib::Ref<gsmlib::Port> port = new gsmlib::KopeteUnixSerialPort(m_device.latin1(), 9600, gsmlib::DEFAULT_INIT_STRING, false);
+
+ kdDebug( 14160 ) << "Port created"<<endl;
+
+ m_MeTa = new gsmlib::MeTa(port);
+ std::string dummy1, dummy2, receiveStoreName;
+ m_MeTa->getSMSStore(dummy1, dummy2, receiveStoreName );
+ m_MeTa->setSMSStore(receiveStoreName, 3);
+
+ m_MeTa->setMessageService(1);
+
+ // switch on SMS routing
+ m_MeTa->setSMSRoutingToTA(true, false, false, true);
+
+ m_MeTa->setEventHandler(this);
+ QApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::CONNECTED));
+ return true;
+ }
+ catch(gsmlib::GsmException &e)
+ {
+ kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
+ m_run = false;
+ return false;
+ }
+}
+
+void GSMLibThread::SMSReception(gsmlib::SMSMessageRef newMessage, SMSMessageType messageType)
+{
+ try
+ {
+ IncomingMessage m;
+ m.Type = messageType;
+ m.Message = newMessage;
+
+ m_newMessages.push_back(m);
+ }
+ catch(gsmlib::GsmException &e)
+ {
+ kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
+ m_run = false;
+ }
+}
+
+void GSMLibThread::SMSReceptionIndication(std::string storeName, unsigned int index, SMSMessageType messageType)
+{
+ kdDebug( 14160 ) << k_funcinfo << "New Message in store: "<<storeName.c_str() << endl;
+
+ try
+ {
+ if( messageType != gsmlib::GsmEvent::NormalSMS )
+ return;
+
+ IncomingMessage m;
+ m.Index = index;
+ m.StoreName = storeName.c_str();
+ m.Type = messageType;
+ m_newMessages.push_back(m);
+ }
+ catch(gsmlib::GsmException &e)
+ {
+ kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
+ m_run = false;
+ }
+}
+
+void GSMLibThread::pollForMessages( )
+{
+ if( m_MeTa == NULL )
+ return;
+
+ try
+ {
+ struct timeval timeoutVal;
+ timeoutVal.tv_sec = 1;
+ timeoutVal.tv_usec = 0;
+ m_MeTa->waitEvent(&timeoutVal);
+
+ MessageList::iterator it;
+ for( it=m_newMessages.begin(); it!=m_newMessages.end(); it++)
+ {
+ IncomingMessage m = *it;
+
+ // Do we need to fetch it from the ME?
+ if( m.Message.isnull() )
+ {
+ gsmlib::SMSStoreRef store = m_MeTa->getSMSStore(m.StoreName.latin1());
+ store->setCaching(false);
+
+ m.Message = (*store.getptr())[m.Index].message();
+ store->erase(store->begin() + m.Index);
+ }
+
+ GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::NEW_MESSAGE );
+ e->Text = m.Message->userData().c_str();
+ e->Number = m.Message->address().toString().c_str();
+
+ QApplication::postEvent(m_parent, e);
+
+ }
+ m_newMessages.clear();
+ }
+ catch(gsmlib::GsmException &e)
+ {
+ kdWarning( 14160 ) << k_funcinfo<< e.what()<<endl;
+ m_run = false;
+ }
+}
+
+void GSMLibThread::sendMessageQueue()
+{
+ QMutexLocker _(&m_outMessagesMutex);
+
+ if(m_outMessages.size() == 0 )
+ return;
+
+ KopeteMessageList::iterator it;
+ for( it=m_outMessages.begin(); it!=m_outMessages.end(); it++)
+ sendMessage(*it);
+ m_outMessages.clear();
+}
+
+void GSMLibThread::sendMessage(const Kopete::Message& msg)
+{
+ QString reason;
+
+ if (!m_MeTa)
+ {
+ GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
+ e->Reason = QString("GSMLib: Not Connected");
+ e->Message = msg;
+ QApplication::postEvent(m_parent, e);
+ }
+
+ QString message = msg.plainBody();
+ QString nr = msg.to().first()->contactId();
+
+ // send SMS
+ try
+ {
+ gsmlib::Ref<gsmlib::SMSSubmitMessage> submitSMS = new gsmlib::SMSSubmitMessage();
+ gsmlib::Address destAddr( nr.latin1() );
+ submitSMS->setDestinationAddress(destAddr);
+ m_MeTa->sendSMSs(submitSMS, message.latin1(), true);
+
+ GSMLibEvent* e = new GSMLibEvent( GSMLibEvent::MSG_SENT );
+ e->Message = msg;
+ QApplication::postEvent(m_parent, e);
+ }
+ catch(gsmlib::GsmException &e)
+ {
+ GSMLibEvent* ev = new GSMLibEvent( GSMLibEvent::MSG_NOT_SENT );
+ ev->Reason = QString("GSMLib: ") + e.what();
+ ev->Message = msg;
+ QApplication::postEvent(m_parent, ev);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GSMLib::GSMLib(Kopete::Account* account)
+ : SMSService(account)
+{
+ prefWidget = 0L;
+ m_thread = NULL;
+
+ loadConfig();
+}
+
+GSMLib::~GSMLib()
+{
+ disconnect();
+}
+
+void GSMLib::saveConfig()
+{
+ if( m_account != NULL )
+ {
+ KConfigGroup* c = m_account->configGroup();
+
+ c->writeEntry(QString("%1:%2").arg("GSMLib").arg("Device"), m_device);
+ }
+}
+
+void GSMLib::loadConfig()
+{
+ m_device = "/dev/bluetooth/rfcomm0";
+ if( m_account != NULL )
+ {
+ QString temp;
+ KConfigGroup* c = m_account->configGroup();
+
+ temp = c->readEntry(QString("%1:%2").arg("GSMLib").arg("Device"), QString::null);
+ if( temp != QString::null )
+ m_device = temp;
+ }
+}
+
+void GSMLib::connect()
+{
+
+ m_thread = new GSMLibThread(m_device, this);
+ m_thread->start();
+
+}
+
+void GSMLib::disconnect()
+{
+ kdDebug( 14160 ) << k_funcinfo <<endl;
+
+ if( m_thread )
+ {
+ m_thread->stop();
+ delete m_thread;
+ m_thread = NULL;
+ emit disconnected();
+ }
+
+}
+
+void GSMLib::setWidgetContainer(QWidget* parent, QGridLayout* layout)
+{
+ m_parent = parent;
+ m_layout = layout;
+ QWidget *configWidget = configureWidget(parent);
+ layout->addMultiCellWidget(configWidget, 0, 1, 0, 1);
+ configWidget->show();
+}
+
+void GSMLib::send(const Kopete::Message& msg)
+{
+ m_thread->send(msg);
+}
+
+QWidget* GSMLib::configureWidget(QWidget* parent)
+{
+
+ if (prefWidget == 0L)
+ prefWidget = new GSMLibPrefsUI(parent);
+
+ loadConfig();
+ prefWidget->device->setURL(m_device);
+
+ return prefWidget;
+}
+
+void GSMLib::savePreferences()
+{
+ if( prefWidget )
+ {
+ m_device = prefWidget->device->url();
+ }
+ saveConfig();
+}
+
+int GSMLib::maxSize()
+{
+ return 160;
+}
+
+void GSMLib::customEvent(QCustomEvent* e)
+{
+ if( e->type() != QEvent::User+GSMLIB_EVENT_ID )
+ return;
+
+ if( m_account == NULL )
+ return;
+
+ GSMLibEvent* ge = (GSMLibEvent*)e;
+
+ kdDebug( 14160 ) << "Got event "<<ge->subType()<<endl;
+ switch( ge->subType() )
+ {
+ case GSMLibEvent::CONNECTED:
+ emit connected();
+ break;
+ case GSMLibEvent::DISCONNECTED:
+ disconnect();
+ break;
+ case GSMLibEvent::MSG_SENT:
+ emit messageSent(ge->Message);
+ break;
+ case GSMLibEvent::MSG_NOT_SENT:
+ emit messageNotSent(ge->Message, ge->Reason);
+ break;
+ case GSMLibEvent::NEW_MESSAGE:
+ {
+ QString nr = ge->Number;
+ QString text = ge->Text;
+
+ // Locate a contact
+ SMSContact* contact = static_cast<SMSContact*>( m_account->contacts().find( nr ));
+ if ( contact==NULL )
+ {
+ // No contact found, make a new one
+ Kopete::MetaContact* metaContact = new Kopete::MetaContact ();
+ metaContact->setTemporary ( true );
+ contact = new SMSContact(m_account, nr, nr, metaContact );
+ Kopete::ContactList::self ()->addMetaContact( metaContact );
+ contact->setOnlineStatus( SMSProtocol::protocol()->SMSOnline );
+ }
+
+ // Deliver the msg
+ Kopete::Message msg( contact, m_account->myself(), text, Kopete::Message::Inbound, Kopete::Message::RichText );
+ contact->manager(Kopete::Contact::CanCreate)->appendMessage( msg );
+ break;
+ }
+ }
+}
+
+
+
+
+const QString& GSMLib::description()
+{
+ QString url = "http://www.pxh.de/fs/gsmlib/";
+ m_description = i18n("<qt>GSMLib is a library (and utilities) for sending SMS via a GSM device. The program can be found on <a href=\"%1\">%1</a></qt>").arg(url).arg(url);
+ return m_description;
+}
+
+#include "gsmlib.moc"
+
+#endif
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/services/gsmlib.h b/kopete/protocols/sms/services/gsmlib.h
new file mode 100644
index 00000000..fa9ef1d2
--- /dev/null
+++ b/kopete/protocols/sms/services/gsmlib.h
@@ -0,0 +1,151 @@
+/* *************************************************************************
+ * copyright: (C) 2005 Justin Huff <jjhuff@mspin.net> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * 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 GSMLIB_H_039562406
+#define GSMLIB_H_039562406
+
+#include "config.h"
+#ifdef INCLUDE_SMSGSM
+
+#include <unistd.h>
+
+#include <gsmlib/gsm_unix_serial.h>
+#include <gsmlib/gsm_sms.h>
+#include <gsmlib/gsm_me_ta.h>
+#include <gsmlib/gsm_util.h>
+#include <gsmlib/gsm_event.h>
+
+#include "smsservice.h"
+#include "kopetemessage.h"
+
+#include <qobject.h>
+#include <qevent.h>
+#include <qthread.h>
+#include <qmutex.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+
+class GSMLibPrefsUI;
+class SMSContact;
+class QListViewItem;
+class KProcess;
+class GSMLibThread;
+
+class GSMLib : public SMSService
+{
+ Q_OBJECT
+public:
+ GSMLib(Kopete::Account* account);
+ ~GSMLib();
+
+ void send(const Kopete::Message& msg);
+ void setWidgetContainer(QWidget* parent, QGridLayout* container);
+
+ int maxSize();
+ const QString& description();
+
+public slots:
+ void savePreferences();
+ virtual void connect();
+ virtual void disconnect();
+
+//signals:
+// void messageSent(const Kopete::Message &);
+protected:
+ virtual void customEvent(QCustomEvent* e);
+
+ QWidget* configureWidget(QWidget* parent);
+ void saveConfig();
+ void loadConfig();
+
+ GSMLibPrefsUI* prefWidget;
+ QStringList output;
+
+ QString m_device;
+
+ QString m_description;
+
+ GSMLibThread* m_thread;
+
+} ;
+
+
+/// Custom event for async-events
+class GSMLibEvent : public QCustomEvent
+{
+public:
+ enum SubType { CONNECTED, DISCONNECTED, NEW_MESSAGE, MSG_SENT, MSG_NOT_SENT };
+
+ GSMLibEvent(SubType t);
+
+ SubType subType();
+ void setSubType(SubType t);
+
+ QString Text;
+ QString Number;
+
+ QString Reason;
+
+ Kopete::Message Message;
+protected:
+ SubType m_subType;
+};
+
+/// Thread to deal with GsmLib's blocking
+class GSMLibThread : public QThread, gsmlib::GsmEvent
+{
+public:
+ GSMLibThread(QString dev, GSMLib* parent);
+ virtual ~GSMLibThread();
+
+ virtual void run();
+ void stop();
+ void send(const Kopete::Message& msg);
+protected:
+ bool doConnect();
+ void pollForMessages();
+ void sendMessageQueue();
+ void sendMessage(const Kopete::Message& msg);
+ void SMSReception(gsmlib::SMSMessageRef newMessage, SMSMessageType messageType);
+ void SMSReceptionIndication(std::string storeName, unsigned int index, SMSMessageType messageType);
+
+ GSMLib* m_parent;
+ QString m_device;
+
+ gsmlib::MeTa* m_MeTa;
+
+ bool m_run;
+
+ struct IncomingMessage
+ {
+ int Index;
+ QString StoreName;
+ gsmlib::SMSMessageRef Message;
+ GsmEvent::SMSMessageType Type;
+
+ IncomingMessage() : Index(-1)
+ {}
+ };
+
+ typedef QValueList<IncomingMessage> MessageList;
+ MessageList m_newMessages;
+
+ typedef QValueList<Kopete::Message> KopeteMessageList;
+ KopeteMessageList m_outMessages;
+ QMutex m_outMessagesMutex;
+};
+
+#endif
+#endif //GSMLIB_H_039562406
diff --git a/kopete/protocols/sms/services/gsmlibprefs.ui b/kopete/protocols/sms/services/gsmlibprefs.ui
new file mode 100644
index 00000000..108f6326
--- /dev/null
+++ b/kopete/protocols/sms/services/gsmlibprefs.ui
@@ -0,0 +1,100 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>GSMLibPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>GSMLibPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>168</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>321</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>GSMLib Settings</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line14</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>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>Device:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>program</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>device</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/sms/services/kopete_unix_serial.cpp b/kopete/protocols/sms/services/kopete_unix_serial.cpp
new file mode 100644
index 00000000..b694ab22
--- /dev/null
+++ b/kopete/protocols/sms/services/kopete_unix_serial.cpp
@@ -0,0 +1,445 @@
+// *************************************************************************
+// * Taken from the GSM TA/ME library
+// *
+// * File: gsm_unix_port.cc
+// *
+// * Purpose: UNIX serial port implementation with extras
+// *
+// * Original Author: Peter Hofmann (software@pxh.de)
+// * Modified by: Justin Huff (jjhuff@mspin.net)
+// *
+// * Created: 10.5.1999
+// *************************************************************************
+#include "config.h"
+#ifdef INCLUDE_SMSGSM
+
+#include <gsmlib/gsm_util.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <pthread.h>
+#include <cassert>
+#include <assert.h>
+
+#include <qsocketnotifier.h>
+
+#include "kopete_unix_serial.h"
+
+using namespace std;
+using namespace gsmlib;
+
+static const int holdoff[] = {2000000, 1000000, 400000};
+static const int holdoffArraySize = sizeof(holdoff)/sizeof(int);
+
+// alarm handling for socket read/write
+// the timerMtx is necessary since several threads cannot use the
+// timer indepently of each other
+
+static pthread_mutex_t timerMtx = PTHREAD_MUTEX_INITIALIZER;
+#define pthread_mutex_lock(x)
+#define pthread_mutex_unlock(x)
+
+// for non-GNU systems, define alarm()
+#ifndef HAVE_ALARM
+unsigned int alarm(unsigned int seconds)
+{
+ struct itimerval old, newt;
+ newt.it_interval.tv_usec = 0;
+ newt.it_interval.tv_sec = 0;
+ newt.it_value.tv_usec = 0;
+ newt.it_value.tv_sec = (long int)seconds;
+ if (setitimer(ITIMER_REAL, &newt, &old) < 0)
+ return 0;
+ else
+ return old.it_value.tv_sec;
+}
+#endif
+
+// this routine is called in case of a timeout
+static void catchAlarm(int)
+{
+ // do nothing
+}
+
+// start timer
+static void startTimer()
+{
+ pthread_mutex_lock(&timerMtx);
+ struct sigaction newAction;
+ newAction.sa_handler = catchAlarm;
+ newAction.sa_flags = 0;
+ sigaction(SIGALRM, &newAction, NULL);
+ alarm(1);
+}
+
+// reset timer
+static void stopTimer()
+{
+ alarm(0);
+ sigaction(SIGALRM, NULL, NULL);
+ pthread_mutex_unlock(&timerMtx);
+}
+
+// KopeteUnixSerialPort members
+
+void KopeteUnixSerialPort::throwModemException(string message) throw(GsmException)
+{
+ ostringstream os;
+ os << message << " (errno: " << errno << "/" << strerror(errno) << ")";
+ throw GsmException(os.str(), OSError, errno);
+}
+
+void KopeteUnixSerialPort::putBack(unsigned char c)
+{
+ assert(_oldChar == -1);
+ _oldChar = c;
+}
+
+int KopeteUnixSerialPort::readByte() throw(GsmException)
+{
+ if (_oldChar != -1)
+ {
+ int result = _oldChar;
+ _oldChar = -1;
+ return result;
+ }
+
+ unsigned char c;
+ int timeElapsed = 0;
+ struct timeval oneSecond;
+ bool readDone = false;
+
+ while (! readDone && timeElapsed < _timeoutVal)
+ {
+ if (interrupted())
+ throwModemException("interrupted when reading from TA");
+
+ // setup fd_set data structure for select()
+ fd_set fdSet;
+ oneSecond.tv_sec = 1;
+ oneSecond.tv_usec = 0;
+ FD_ZERO(&fdSet);
+ FD_SET(_fd, &fdSet);
+
+ switch (select(FD_SETSIZE, &fdSet, NULL, NULL, &oneSecond))
+ {
+ case 1:
+ {
+ int res = read(_fd, &c, 1);
+ if (res != 1)
+ throwModemException("end of file when reading from TA");
+ else
+ readDone = true;
+ break;
+ }
+ case 0:
+ ++timeElapsed;
+ break;
+ default:
+ if (errno != EINTR)
+ throwModemException("reading from TA");
+ break;
+ }
+ }
+ if (! readDone)
+ throwModemException("timeout when reading from TA");
+
+#ifndef NDEBUG
+ if (debugLevel() >= 2)
+ {
+ // some useful debugging code
+ if (c == LF)
+ cerr << "<LF>";
+ else if (c == CR)
+ cerr << "<CR>";
+ else cerr << "<'" << (char) c << "'>";
+ cerr.flush();
+ }
+#endif
+ return c;
+}
+
+KopeteUnixSerialPort::KopeteUnixSerialPort(string device, speed_t lineSpeed,
+ string initString, bool swHandshake)
+ throw(GsmException) :
+ _oldChar(-1), _timeoutVal(TIMEOUT_SECS)
+{
+ _readNotifier = NULL;
+
+ struct termios t;
+
+ // open device
+ _fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (_fd == -1)
+ throwModemException("opening device");
+
+ // switch off non-blocking mode
+ int fdFlags;
+ if ((fdFlags = fcntl(_fd, F_GETFL)) == -1)
+ {
+ close(_fd);
+ throwModemException("getting file status flags failed");
+ }
+ fdFlags &= ~O_NONBLOCK;
+ if (fcntl(_fd, F_SETFL, fdFlags) == -1)
+ {
+ close(_fd);
+ throwModemException("switching of non-blocking mode failed");
+ }
+
+ // Set the close on exec flag
+ if ((fdFlags = fcntl(_fd, F_GETFD)) == -1)
+ {
+ close(_fd);
+ throwModemException("getting file status flags failed");
+ }
+ fdFlags |= FD_CLOEXEC;
+ if (fcntl(_fd, F_SETFD, fdFlags) == -1)
+ {
+ close(_fd);
+ throwModemException("switching of non-blocking mode failed");
+ }
+
+ long int saveTimeoutVal = _timeoutVal;
+ _timeoutVal = 3;
+ int initTries = holdoffArraySize;
+ while (initTries-- > 0)
+ {
+ // flush all pending output
+ tcflush(_fd, TCOFLUSH);
+
+ // toggle DTR to reset modem
+ int mctl = TIOCM_DTR;
+ if (ioctl(_fd, TIOCMBIC, &mctl) < 0 && errno != ENOTTY)
+ {
+ close(_fd);
+ throwModemException("clearing DTR failed");
+ }
+ // the waiting time for DTR toggling is increased with each loop
+ usleep(holdoff[initTries]);
+ if (ioctl(_fd, TIOCMBIS, &mctl) < 0 && errno != ENOTTY)
+ {
+ close(_fd);
+ throwModemException("setting DTR failed");
+ }
+ // get line modes
+ if (tcgetattr(_fd, &t) < 0)
+ {
+ close(_fd);
+ throwModemException("tcgetattr device");
+ }
+
+ // set line speed
+ cfsetispeed(&t, lineSpeed);
+ cfsetospeed(&t, lineSpeed);
+
+ // set the device to a sane state
+ t.c_iflag |= IGNPAR | (swHandshake ? IXON | IXOFF : 0);
+ t.c_iflag &= ~(INPCK | ISTRIP | IMAXBEL |
+ (swHandshake ? 0 : IXON | IXOFF)
+ | IXANY | IGNCR | ICRNL | IMAXBEL | INLCR | IGNBRK);
+ t.c_oflag &= ~(OPOST);
+ // be careful, only touch "known" flags
+ t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD |
+ (swHandshake ? CRTSCTS : 0 ));
+ t.c_cflag |= CS8 | CREAD | HUPCL | (swHandshake ? 0 : CRTSCTS) | CLOCAL;
+ t.c_lflag &= ~(ECHO | ECHOE | ECHOPRT | ECHOK | ECHOKE | ECHONL |
+ ECHOCTL | ISIG | IEXTEN | TOSTOP | FLUSHO | ICANON);
+ t.c_lflag |= NOFLSH;
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+
+ t.c_cc[VSUSP] = 0;
+
+ // write back
+ if(tcsetattr (_fd, TCSANOW, &t) < 0)
+ {
+ close(_fd);
+ throwModemException("tcsetattr device");
+ }
+ // the waiting time for writing to the ME/TA is increased with each loop
+ usleep(holdoff[initTries]);
+
+ // flush all pending input
+ tcflush(_fd, TCIFLUSH);
+
+ try
+ {
+ // reset modem
+ putLine("ATZ");
+ bool foundOK = false;
+ int readTries = 5;
+ while (readTries-- > 0)
+ {
+ // for the first call getLine() waits only 3 seconds
+ // because of _timeoutVal = 3
+ string s = getLine();
+ if (s.find("OK") != string::npos ||
+ s.find("CABLE: GSM") != string::npos)
+ {
+ foundOK = true;
+ readTries = 0; // found OK, exit loop
+ }
+ else if (s.find("ERROR") != string::npos)
+ readTries = 0; // error, exit loop
+ }
+
+ // set getLine/putLine timeout back to old value
+ _timeoutVal = saveTimeoutVal;
+
+ if (foundOK)
+ {
+ // init modem
+ readTries = 5;
+ putLine("AT" + initString);
+ while (readTries-- > 0)
+ {
+ string s = getLine();
+ if (s.find("OK") != string::npos ||
+ s.find("CABLE: GSM") != string::npos)
+ {
+ _readNotifier = new QSocketNotifier(_fd, QSocketNotifier::Read);
+ connect( _readNotifier, SIGNAL(activated(int)), this, SIGNAL(activated()));
+ return; // found OK, return
+ }
+ }
+ }
+ }
+ catch (GsmException &e)
+ {
+ _timeoutVal = saveTimeoutVal;
+ if (initTries == 0)
+ {
+ close(_fd);
+ throw e;
+ }
+ }
+ }
+ // no response after 3 tries
+ close(_fd);
+ throwModemException("reset modem failed");
+}
+
+string KopeteUnixSerialPort::getLine() throw(GsmException)
+{
+ string result;
+ int c;
+ while ((c = readByte()) >= 0)
+ {
+ while (c == CR)
+ {
+ c = readByte();
+ }
+ if (c == LF)
+ break;
+ result += c;
+ }
+
+#ifndef NDEBUG
+ if (debugLevel() >= 1)
+ cerr << "<-- " << result << endl;
+#endif
+
+ return result;
+}
+
+void KopeteUnixSerialPort::putLine(string line,
+ bool carriageReturn) throw(GsmException)
+{
+#ifndef NDEBUG
+ if (debugLevel() >= 1)
+ cerr << "--> " << line << endl;
+#endif
+
+ if (carriageReturn) line += CR;
+ const char *l = line.c_str();
+
+ int timeElapsed = 0;
+ struct timeval oneSecond;
+
+ ssize_t bytesWritten = 0;
+ while (bytesWritten < (ssize_t)line.length() && timeElapsed < _timeoutVal)
+ {
+ if (interrupted())
+ throwModemException("interrupted when writing to TA");
+
+ // setup fd_set data structure for select()
+ fd_set fdSet;
+ oneSecond.tv_sec = 1;
+ oneSecond.tv_usec = 0;
+ FD_ZERO(&fdSet);
+ FD_SET(_fd, &fdSet);
+
+ switch (select(FD_SETSIZE, NULL, &fdSet, NULL, &oneSecond))
+ {
+ case 1:
+ {
+ ssize_t bw = write(_fd, l + bytesWritten, line.length() - bytesWritten);
+ if (bw < 0)
+ throwModemException("writing to TA");
+ bytesWritten += bw;
+ break;
+ }
+ case 0:
+ ++timeElapsed;
+ break;
+ default:
+ if (errno != EINTR)
+ throwModemException("writing to TA");
+ break;
+ }
+ }
+
+ while (timeElapsed < _timeoutVal)
+ {
+ if (interrupted())
+ throwModemException("interrupted when writing to TA");
+ ::startTimer();
+ int res = tcdrain(_fd); // wait for output to be read by TA
+ ::stopTimer();
+ if (res == 0)
+ break;
+ else
+ {
+ assert(errno == EINTR);
+ ++timeElapsed;
+ }
+ }
+ if (timeElapsed >= _timeoutVal)
+ throwModemException("timeout when writing to TA");
+
+ // echo CR LF must be removed by higher layer functions in gsm_at because
+ // in order to properly handle unsolicited result codes from the ME/TA
+}
+
+bool KopeteUnixSerialPort::wait(GsmTime timeout) throw(GsmException)
+{
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(_fd, &fds);
+ return select(FD_SETSIZE, &fds, NULL, NULL, timeout) != 0;
+}
+
+// set timeout for read or write in seconds.
+void KopeteUnixSerialPort::setTimeOut(unsigned int timeout)
+{
+ _timeoutVal = timeout;
+}
+
+KopeteUnixSerialPort::~KopeteUnixSerialPort()
+{
+ delete _readNotifier;
+ _readNotifier = NULL;
+ if (_fd != -1)
+ close(_fd);
+}
+
+#include "kopete_unix_serial.moc"
+
+#endif
diff --git a/kopete/protocols/sms/services/kopete_unix_serial.h b/kopete/protocols/sms/services/kopete_unix_serial.h
new file mode 100644
index 00000000..0248556b
--- /dev/null
+++ b/kopete/protocols/sms/services/kopete_unix_serial.h
@@ -0,0 +1,70 @@
+// *************************************************************************
+// * Taken from the GSM TA/ME library
+// *
+// * File: gsm_unix_port.h
+// *
+// * Purpose: UNIX serial port implementation with extras
+// *
+// * Original Author: Peter Hofmann (software@pxh.de)
+// * Modified by: Justin Huff (jjhuff@mspin.net)
+// *
+// * Created: 4.5.1999
+// *************************************************************************
+
+#ifndef GSM_UNIX_SERIAL_KOPETE_H
+#define GSM_UNIX_SERIAL_KOPETE_H
+
+#include "config.h"
+#ifdef INCLUDE_SMSGSM
+
+#include <string>
+#include <gsmlib/gsm_error.h>
+#include <gsmlib/gsm_port.h>
+#include <gsmlib/gsm_util.h>
+#include <sys/types.h>
+#include <termios.h>
+
+#include <qobject.h>
+
+class QSocketNotifier;
+namespace gsmlib
+{
+
+class KopeteUnixSerialPort : public QObject, public Port
+{
+ Q_OBJECT;
+
+protected:
+ int _fd; // file descriptor for device
+ int _oldChar; // character set by putBack() (-1 == none)
+ long int _timeoutVal; // timeout for getLine/readByte
+
+ QSocketNotifier* _readNotifier;
+
+ // throw GsmException include UNIX errno
+ void throwModemException(std::string message) throw(GsmException);
+
+public:
+ // create Port given the UNIX device name
+ KopeteUnixSerialPort(std::string device, speed_t lineSpeed = DEFAULT_BAUD_RATE,
+ std::string initString = DEFAULT_INIT_STRING,
+ bool swHandshake = false)
+ throw(GsmException);
+ virtual ~KopeteUnixSerialPort();
+
+ // inherited from Port
+ void putBack(unsigned char c);
+ int readByte() throw(GsmException);
+ std::string getLine() throw(GsmException);
+ void putLine(std::string line,
+ bool carriageReturn = true) throw(GsmException);
+ bool wait(GsmTime timeout) throw(GsmException);
+ void setTimeOut(unsigned int timeout);
+
+signals:
+ void activated();
+};
+
+}
+#endif
+#endif // GSM_UNIX_SERIAL_KOPETE_H
diff --git a/kopete/protocols/sms/services/smsclient.cpp b/kopete/protocols/sms/services/smsclient.cpp
new file mode 100644
index 00000000..96c04818
--- /dev/null
+++ b/kopete/protocols/sms/services/smsclient.cpp
@@ -0,0 +1,192 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcombobox.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kdebug.h>
+#include <kconfigbase.h>
+
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+
+#include "smsclient.h"
+#include "smsclientprefs.h"
+#include "smsprotocol.h"
+
+SMSClient::SMSClient(Kopete::Account* account)
+ : SMSService(account)
+{
+ prefWidget = 0L;
+}
+
+SMSClient::~SMSClient()
+{
+}
+
+void SMSClient::setWidgetContainer(QWidget* parent, QGridLayout* layout)
+{
+ kdWarning( 14160 ) << k_funcinfo << "ml: " << layout << ", " << "mp: " << parent << endl;
+ m_parent = parent;
+ m_layout = layout;
+ QWidget *configWidget = configureWidget(parent);
+ layout->addMultiCellWidget(configWidget, 0, 1, 0, 1);
+ configWidget->show();
+}
+
+void SMSClient::send(const Kopete::Message& msg)
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be non-zero!!)" << endl;
+ if (!m_account) return;
+
+ m_msg = msg;
+
+ KConfigGroup* c = m_account->configGroup();
+ QString provider = c->readEntry(QString("%1:%2").arg("SMSClient").arg("ProviderName"), QString::null);
+
+ if (provider.isNull())
+ {
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("No provider configured"), i18n("Could Not Send Message"));
+ return;
+ }
+
+ QString programName = c->readEntry(QString("%1:%2").arg("SMSClient").arg("ProgramName"). QString::null);
+ if (programName.isNull())
+ programName = "/usr/bin/sms_client";
+
+ KProcess* p = new KProcess;
+
+ QString message = msg.plainBody();
+ QString nr = msg.to().first()->contactId();
+
+ *p << programName;
+ *p << provider + ":" + nr;
+ *p << message;
+
+ QObject::connect(p, SIGNAL(processExited(KProcess *)), this, SLOT(slotSendFinished(KProcess*)));
+ QObject::connect(p, SIGNAL(receivedStdout(KProcess*, char*, int)), this, SLOT(slotReceivedOutput(KProcess*, char*, int)));
+ QObject::connect(p, SIGNAL(receivedStderr(KProcess*, char*, int)), this, SLOT(slotReceivedOutput(KProcess*, char*, int)));
+
+ p->start(KProcess::Block, KProcess::AllOutput);
+}
+
+QWidget* SMSClient::configureWidget(QWidget* parent)
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be ok if zero!!)" << endl;
+
+ if (prefWidget == 0L)
+ prefWidget = new SMSClientPrefsUI(parent);
+
+ prefWidget->configDir->setMode(KFile::Directory);
+ QString configDir;
+ if (m_account)
+ configDir = m_account->configGroup()->readEntry(QString("%1:%2").arg("SMSClient").arg("ConfigDir"), QString::null);
+ if (configDir.isNull())
+ configDir = "/etc/sms";
+ prefWidget->configDir->setURL(configDir);
+
+ QString programName;
+ if (m_account)
+ programName = m_account->configGroup()->readEntry(QString("%1:%2").arg("SMSClient").arg("ProgramName"),
+ QString::null);
+ if (programName.isNull())
+ programName = "/usr/bin/sms_client";
+ prefWidget->program->setURL(programName);
+
+ prefWidget->provider->insertStringList(providers());
+
+ if (m_account)
+ {
+ QString pName = m_account->configGroup()->readEntry(QString("%1:%2").arg("SMSClient").arg("ProviderName"));
+ for (int i=0; i < prefWidget->provider->count(); i++)
+ {
+ if (prefWidget->provider->text(i) == pName)
+ {
+ prefWidget->provider->setCurrentItem(i);
+ break;
+ }
+ }
+ }
+
+ return prefWidget;
+}
+
+void SMSClient::savePreferences()
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be work if zero!!)" << endl;
+
+ if (prefWidget != 0L && m_account != 0L)
+ {
+ KConfigGroup* c = m_account->configGroup();
+
+ c->writeEntry(QString("%1:%2").arg("SMSClient").arg("ProgramName"), prefWidget->program->url());
+ c->writeEntry(QString("%1:%2").arg("SMSClient").arg("ConfigDir"), prefWidget->configDir->url());
+ c->writeEntry(QString("%1:%2").arg("SMSClient").arg("ProviderName"), prefWidget->provider->currentText());
+ }
+}
+
+QStringList SMSClient::providers()
+{
+ QStringList p;
+
+ QDir d;
+ d.setPath(QString("%1/services/").arg(prefWidget->configDir->url()));
+ p += d.entryList("*", QDir::Files);
+
+ return p;
+}
+
+void SMSClient::slotReceivedOutput(KProcess*, char *buffer, int buflen)
+{
+ QStringList lines = QStringList::split("\n", QString::fromLocal8Bit(buffer, buflen));
+ for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ output.append(*it);
+}
+
+void SMSClient::slotSendFinished(KProcess* p)
+{
+ if (p->exitStatus() == 0)
+ emit messageSent(m_msg);
+ else
+ emit messageNotSent(m_msg, output.join("\n"));
+}
+
+int SMSClient::maxSize()
+{
+ return 160;
+}
+
+const QString& SMSClient::description()
+{
+ QString url = "http://www.smsclient.org";
+ m_description = i18n("<qt>SMSClient is a program for sending SMS with the modem. The program can be found on <a href=\"%1\">%1</a></qt>").arg(url).arg(url);
+ return m_description;
+}
+
+#include "smsclient.moc"
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/services/smsclient.h b/kopete/protocols/sms/services/smsclient.h
new file mode 100644
index 00000000..bc260228
--- /dev/null
+++ b/kopete/protocols/sms/services/smsclient.h
@@ -0,0 +1,65 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSCLIENT_H
+#define SMSCLIENT_H
+
+#include "smsservice.h"
+#include "kopetemessage.h"
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class SMSClientPrefsUI;
+class SMSContact;
+class QListViewItem;
+class KProcess;
+
+class SMSClient : public SMSService
+{
+ Q_OBJECT
+public:
+ SMSClient(Kopete::Account* account);
+ ~SMSClient();
+
+ void send(const Kopete::Message& msg);
+ void setWidgetContainer(QWidget* parent, QGridLayout* container);
+
+ int maxSize();
+ const QString& description();
+
+public slots:
+ void savePreferences();
+
+private slots:
+ void slotReceivedOutput(KProcess*, char *buffer, int buflen);
+ void slotSendFinished(KProcess* p);
+signals:
+ void messageSent(const Kopete::Message &);
+
+private:
+ QWidget* configureWidget(QWidget* parent);
+
+ SMSClientPrefsUI* prefWidget;
+ QStringList providers();
+ QStringList output;
+
+ Kopete::Message m_msg;
+
+ QString m_description;
+} ;
+
+#endif //SMSCLIENT_H
diff --git a/kopete/protocols/sms/services/smsclientprefs.ui b/kopete/protocols/sms/services/smsclientprefs.ui
new file mode 100644
index 00000000..363081e3
--- /dev/null
+++ b/kopete/protocols/sms/services/smsclientprefs.ui
@@ -0,0 +1,135 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SMSClientPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SMSClientPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>168</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>321</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>SMSClient Settings</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line14</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>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>SMSClient &amp;program:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>program</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Pro&amp;vider:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>provider</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>program</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="1" column="1">
+ <property name="name">
+ <cstring>configDir</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1">
+ <property name="name">
+ <cstring>provider</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>SMSClient &amp;config path:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>configDir</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/sms/services/smssend.cpp b/kopete/protocols/sms/services/smssend.cpp
new file mode 100644
index 00000000..f3ea258c
--- /dev/null
+++ b/kopete/protocols/sms/services/smssend.cpp
@@ -0,0 +1,254 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard L�k�g <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qcombobox.h>
+#include <qvgroupbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+
+#include <kconfigbase.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+
+#include "smssend.h"
+#include "smssendprefs.h"
+#include "smssendprovider.h"
+#include "smsprotocol.h"
+
+SMSSend::SMSSend(Kopete::Account* account)
+ : SMSService(account)
+{
+ kdWarning( 14160 ) << k_funcinfo << " this = " << this << endl;
+ prefWidget = 0L;
+ m_provider = 0L;
+}
+
+SMSSend::~SMSSend()
+{
+}
+
+void SMSSend::send(const Kopete::Message& msg)
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be non-zero!!)" << endl;
+ QString provider = m_account->configGroup()->readEntry("SMSSend:ProviderName", QString::null);
+
+ if (provider.length() < 1)
+ {
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("No provider configured."), i18n("Could Not Send Message"));
+ return;
+ }
+
+ QString prefix = m_account->configGroup()->readEntry("SMSSend:Prefix", QString::null);
+ if (prefix.isNull())
+ {
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("No prefix set for SMSSend, please change it in the configuration dialog."), i18n("No Prefix"));
+ return;
+ }
+
+ m_provider = new SMSSendProvider(provider, prefix, m_account, this);
+
+ QObject::connect( m_provider, SIGNAL(messageSent(const Kopete::Message &)), this, SIGNAL(messageSent(const Kopete::Message &)));
+ QObject::connect( m_provider, SIGNAL(messageNotSent(const Kopete::Message &, const QString &)), this, SIGNAL(messageNotSent(const Kopete::Message &, const QString &)));
+
+ m_provider->send(msg);
+}
+
+void SMSSend::setWidgetContainer(QWidget* parent, QGridLayout* layout)
+{
+ kdWarning( 14160 ) << k_funcinfo << "ml: " << layout << ", " << "mp: " << parent << endl;
+ m_parent = parent;
+ m_layout = layout;
+
+ // could end up being deleted twice??
+ delete prefWidget;
+ prefWidget = new SMSSendPrefsUI(parent);
+ layout->addMultiCellWidget(prefWidget, 0, 1, 0, 1);
+
+ prefWidget->program->setMode(KFile::Directory);
+
+ QString prefix = QString::null;
+
+ if (m_account)
+ prefix = m_account->configGroup()->readEntry("SMSSend:Prefix", QString::null);
+ if (prefix.isNull())
+ {
+ QDir d("/usr/share/smssend");
+ if (d.exists())
+ {
+ prefix = "/usr";
+ }
+ d = "/usr/local/share/smssend";
+ if (d.exists())
+ {
+ prefix="/usr/local";
+ }
+ else
+ {
+ prefix="/usr";
+ }
+ }
+
+ QObject::connect (prefWidget->program, SIGNAL(textChanged(const QString &)),
+ this, SLOT(loadProviders(const QString&)));
+
+ prefWidget->program->setURL(prefix);
+
+ QObject::connect(prefWidget->provider, SIGNAL(activated(const QString &)),
+ this, SLOT(setOptions(const QString &)));
+
+ prefWidget->show();
+}
+
+void SMSSend::savePreferences()
+{
+ if (prefWidget != 0L && m_account != 0L && m_provider != 0L )
+ {
+ m_account->configGroup()->writeEntry("SMSSend:Prefix", prefWidget->program->url());
+ m_account->configGroup()->writeEntry("SMSSend:ProviderName", prefWidget->provider->currentText());
+ m_provider->save(args);
+ }
+}
+
+void SMSSend::loadProviders(const QString &prefix)
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be ok if zero)" << endl;
+
+ QStringList p;
+
+ prefWidget->provider->clear();
+
+ QDir d(prefix + "/share/smssend");
+ if (!d.exists())
+ {
+ setOptions(QString::null);
+ return;
+ }
+
+ p = d.entryList("*.sms");
+
+ d = QDir::homeDirPath()+"/.smssend/";
+
+ QStringList tmp(d.entryList("*.sms"));
+
+ for (QStringList::Iterator it = tmp.begin(); it != tmp.end(); ++it)
+ p.prepend(*it);
+
+ for (QStringList::iterator it = p.begin(); it != p.end(); ++it)
+ (*it).truncate((*it).length()-4);
+
+ prefWidget->provider->insertStringList(p);
+
+ bool found = false;
+ if (m_account)
+ { QString pName = m_account->configGroup()->readEntry("SMSSend:ProviderName", QString::null);
+ for (int i=0; i < prefWidget->provider->count(); i++)
+ {
+ if (prefWidget->provider->text(i) == pName)
+ {
+ found=true;
+ prefWidget->provider->setCurrentItem(i);
+ setOptions(pName);
+ break;
+ }
+ }
+ }
+ if (!found)
+ setOptions(prefWidget->provider->currentText());
+}
+
+void SMSSend::setOptions(const QString& name)
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be ok if zero!!)" << endl;
+ if(!prefWidget) return; // sanity check
+
+ prefWidget->providerLabel->setText(i18n("%1 Settings").arg(name));
+
+ labels.setAutoDelete(true);
+ labels.clear();
+ args.setAutoDelete(true);
+ args.clear();
+
+ if (m_provider) delete m_provider;
+ m_provider = new SMSSendProvider(name, prefWidget->program->url(), m_account, this);
+
+ for (int i=0; i < m_provider->count(); i++)
+ {
+ if (!m_provider->name(i).isNull())
+ {
+ QLabel *l = new QLabel(m_parent);
+ l->setText("&" + m_provider->name(i) + ":");
+ QToolTip::add(l, m_provider->description(i));
+ m_layout->addWidget(l, i+2, 0);
+ KLineEdit *e = new KLineEdit(m_parent);
+ e->setText(m_provider->value(i));
+ m_layout->addWidget(e, i+2, 1);
+ args.append(e);
+ labels.append(l);
+ l->setBuddy(e);
+ if(m_provider->isHidden(i))
+ e->setEchoMode(QLineEdit::Password);
+ e->show();
+ l->show();
+ }
+ }
+}
+void SMSSend::setAccount(Kopete::Account* account)
+{
+ m_provider->setAccount(account);
+ SMSService::setAccount(account);
+}
+
+int SMSSend::maxSize()
+{
+ kdWarning( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be non-zero!!)" << endl;
+
+ QString pName = m_account->configGroup()->readEntry("SMSSend:ProviderName", QString::null);
+ if (pName.length() < 1)
+ return 160;
+ QString prefix = m_account->configGroup()->readEntry("SMSSend:Prefix", QString::null);
+ if (prefix.isNull())
+ prefix = "/usr";
+ // quick sanity check
+ if (m_provider) delete m_provider;
+ m_provider = new SMSSendProvider(pName, prefix, m_account, this);
+ return m_provider->maxSize();
+}
+
+const QString& SMSSend::description()
+{
+ QString url = "http://zekiller.skytech.org/smssend_en.php";
+ m_description = i18n("<qt>SMSSend is a program for sending SMS through gateways on the web. It can be found on <a href=\"%1\">%2</a></qt>").arg(url).arg(url);
+ return m_description;
+}
+
+
+#include "smssend.moc"
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/services/smssend.h b/kopete/protocols/sms/services/smssend.h
new file mode 100644
index 00000000..556a21ea
--- /dev/null
+++ b/kopete/protocols/sms/services/smssend.h
@@ -0,0 +1,66 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSSEND_H
+#define SMSSEND_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qlabel.h>
+
+#include <klineedit.h>
+
+#include "smsservice.h"
+
+class SMSSendProvider;
+class SMSSendPrefsUI;
+class QListViewItem;
+class QGridLayout;
+
+class SMSSend : public SMSService
+{
+ Q_OBJECT
+public:
+ SMSSend(Kopete::Account* account);
+ ~SMSSend();
+
+ virtual void setAccount(Kopete::Account* account);
+
+ void send(const Kopete::Message& msg);
+ void setWidgetContainer(QWidget* parent, QGridLayout* container);
+
+ int maxSize();
+ const QString& description();
+
+public slots:
+ void savePreferences();
+
+private slots:
+ void setOptions(const QString& name);
+ void loadProviders(const QString& prefix);
+//signals:
+// void messageSent(const Kopete::Message&);
+
+private:
+ QGridLayout *settingsBoxLayout;
+ SMSSendProvider* m_provider;
+ SMSSendPrefsUI* prefWidget;
+ QPtrList<KLineEdit> args;
+ QPtrList<QLabel> labels;
+ QString m_description;
+} ;
+
+#endif //SMSSEND_H
diff --git a/kopete/protocols/sms/services/smssendprefs.ui b/kopete/protocols/sms/services/smssendprefs.ui
new file mode 100644
index 00000000..faf3a306
--- /dev/null
+++ b/kopete/protocols/sms/services/smssendprefs.ui
@@ -0,0 +1,188 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SMSSendPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SMSSendPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>338</width>
+ <height>195</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>311</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel7_2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>SMSSend Options</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line6_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="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>provider</cstring>
+ </property>
+ </widget>
+ <widget class="KURLRequester" row="0" column="1">
+ <property name="name">
+ <cstring>program</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pro&amp;vider:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>provider</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>4</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>SMSSend prefi&amp;x:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>program</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>351</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>providerLabel</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Provider Options</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line6</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>HLine</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>program</tabstop>
+ <tabstop>provider</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/sms/services/smssendprovider.cpp b/kopete/protocols/sms/services/smssendprovider.cpp
new file mode 100644
index 00000000..82827aab
--- /dev/null
+++ b/kopete/protocols/sms/services/smssendprovider.cpp
@@ -0,0 +1,288 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvaluelist.h>
+#include <qlabel.h>
+#include <qfile.h>
+
+#include <kconfigbase.h>
+#include <kprocess.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+
+#include "smssendprovider.h"
+#include "smsprotocol.h"
+#include "smscontact.h"
+
+SMSSendProvider::SMSSendProvider(const QString& providerName, const QString& prefixValue, Kopete::Account* account, QObject* parent, const char *name)
+ : QObject( parent, name ), m_account(account)
+{
+ kdWarning( 14160 ) << k_funcinfo << "this = " << this << ", m_account = " << m_account << " (should be ok if zero!!)" << endl;
+
+ provider = providerName;
+ prefix = prefixValue;
+ m_maxSize = 160;
+
+ messagePos = -1;
+ telPos = -1;
+
+ QString file = prefix + "/share/smssend/" + provider + ".sms";
+ QFile f(file);
+ if (f.open(IO_ReadOnly))
+ {
+ QTextStream t(&f);
+ QString group = QString("SMSSend-%1").arg(provider);
+ bool exactNumberMatch = false;
+ QStringList numberWords;
+ numberWords.append("Tel");
+ numberWords.append("Number");
+ numberWords.append("number");
+ numberWords.append("TelNum");
+ numberWords.append("Recipient");
+ numberWords.append("Tel1");
+ numberWords.append("To");
+ numberWords.append("nummer");
+ numberWords.append("telefone");
+ numberWords.append("ToPhone");
+
+ while( !t.eof())
+ {
+ QString s = t.readLine();
+ if( s[0] == '%')
+ {
+ QStringList args = QStringList::split(':',s);
+ QStringList options = QStringList::split(' ', args[0]);
+
+ names.append(options[0].replace(0,1,""));
+
+ bool hidden = false;
+ for(unsigned i = 1; i < options.count(); i++)
+ if(options[i] == "Hidden")
+ { hidden = true;
+ break;
+ }
+ isHiddens.append(hidden);
+
+ // Strip trailing whitespace in the end
+ // and '%' in the beginning
+ args[0] = args[0].simplifyWhiteSpace().mid(1);
+
+ descriptions.append(args[1]);
+ if (m_account)
+ values.append(m_account->configGroup()->readEntry(QString("%1:%2").arg(group).arg(names[names.count()-1]),
+ QString::null));
+ else
+ values.append("");
+
+ if( args[0].contains("Message") || args[0].contains("message")
+ || args[0].contains("message") || args[0].contains("nachricht")
+ || args[0].contains("Msg") || args[0].contains("Mensagem") )
+ {
+ for( unsigned i = 0; i < options.count(); i++)
+ {
+ if (options[i].contains("Size="))
+ {
+ QString option = options[i];
+ option.replace(0,5,"");
+ m_maxSize = option.toInt();
+ }
+ }
+ messagePos = names.count()-1;
+ }
+ else if (!exactNumberMatch)
+ {
+ for (QStringList::Iterator it=numberWords.begin(); it != numberWords.end(); ++it)
+ {
+ if (args[0].contains(*it))
+ {
+ telPos = names.count() - 1;
+ if (args[0] == *it)
+ {
+// kdDebug(14160) << "Exact match for " << args[0] << endl;
+ exactNumberMatch = true;
+ }
+// kdDebug(14160) << "args[0] (" << args[0] << ") contains " << *it << endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ f.close();
+
+ if ( messagePos == -1 || telPos == -1 )
+ {
+ canSend = false;
+ return;
+ }
+
+ canSend = true;
+}
+
+SMSSendProvider::~SMSSendProvider()
+{
+ kdWarning( 14160 ) << k_funcinfo << "this = " << this << endl;
+}
+
+void SMSSendProvider::setAccount(Kopete::Account *account)
+{
+ m_account = account;
+}
+
+const QString& SMSSendProvider::name(int i)
+{
+ if ( telPos == i || messagePos == i)
+ return QString::null;
+ else
+ return names[i];
+}
+
+const QString& SMSSendProvider::value(int i)
+{
+ return values[i];
+}
+
+const QString& SMSSendProvider::description(int i)
+{
+ return descriptions[i];
+}
+
+const bool SMSSendProvider::isHidden(int i)
+{
+ return isHiddens[i];
+}
+
+void SMSSendProvider::save(QPtrList<KLineEdit>& args)
+{
+ kdDebug( 14160 ) << k_funcinfo << "m_account = " << m_account << " (should be non-zero!!)" << endl;
+ if (!m_account) return; // prevent crash in worst case
+
+ QString group = QString("SMSSend-%1").arg(provider);
+ int namesI=0;
+
+ for (unsigned i=0; i < args.count(); i++)
+ {
+ if (telPos == namesI || messagePos == namesI)
+ {
+// kdDebug(14160) << k_funcinfo << "Skipping pos " << namesI << endl;
+ namesI++;
+ if (telPos == namesI || messagePos == namesI)
+ {
+// kdDebug(14160) << k_funcinfo << "Skipping pos " << namesI << endl;
+ namesI++;
+ }
+ }
+
+// kdDebug(14160) << k_funcinfo << "saving " << args.at(i) << " to " << names[namesI] << endl;
+ if (!args.at(i)->text().isEmpty())
+ { values[namesI] = args.at(i)->text();
+ m_account->configGroup()->writeEntry(QString("%1:%2").arg(group).arg(names[namesI]), values[namesI]);
+ }
+ namesI++;
+ }
+}
+
+int SMSSendProvider::count()
+{
+ return names.count();
+}
+
+void SMSSendProvider::send(const Kopete::Message& msg)
+{
+ if ( canSend == false )
+ {
+ if ( messagePos == -1 )
+ {
+ canSend = false;
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("Could not determine which argument which should contain the message."),
+ i18n("Could Not Send Message"));
+ return;
+ }
+ if ( telPos == -1 )
+ {
+ canSend = false;
+
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("Could not determine which argument which should contain the number."),
+ i18n("Could Not Send Message"));
+ return;
+ }
+ }
+
+ m_msg = msg;
+
+ QString message = msg.plainBody();
+ QString nr = dynamic_cast<SMSContact *>(msg.to().first())->qualifiedNumber();
+
+ if (canSend = false)
+ return;
+
+ values[messagePos] = message;
+ values[telPos] = nr;
+
+ KProcess* p = new KProcess;
+
+ kdWarning( 14160 ) << "Executing " << QString("%1/bin/smssend").arg(prefix) << " \"" << provider << "\" " << values.join("\" \"") << "\"" << endl;
+
+ *p << QString("%1/bin/smssend").arg(prefix) << provider << values;
+
+ output = "";
+ connect( p, SIGNAL(processExited(KProcess *)), this, SLOT(slotSendFinished(KProcess *)));
+ connect( p, SIGNAL(receivedStdout(KProcess *, char *, int)), this, SLOT(slotReceivedOutput(KProcess *, char *, int)));
+// connect( p, SIGNAL(receivedStderr(KProcess *, char *, int)), this, SLOT(slotReceivedOutput(KProcess *, char *, int)));
+
+ p->start(KProcess::NotifyOnExit, KProcess::AllOutput);
+}
+
+void SMSSendProvider::slotSendFinished(KProcess *p)
+{
+ kdWarning( 14160 ) << k_funcinfo << "this = " << this << ", es = " << p->exitStatus() << ", p = " << p << " (should be non-zero!!)" << endl;
+ if (p->exitStatus() == 0)
+ emit messageSent(m_msg);
+ else
+ emit messageNotSent(m_msg, QString().setLatin1(output));
+
+ p->deleteLater();
+}
+
+void SMSSendProvider::slotReceivedOutput(KProcess *, char *buffer, int buflen)
+{
+// QStringList lines = QStringList::split("\n", QString::fromLocal8Bit(buffer, buflen));
+// for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ for(int i = 0; i < buflen; i++)
+ output += buffer[i];
+ kdWarning( 14160 ) << k_funcinfo << " output now = " << output << endl;
+}
+
+int SMSSendProvider::maxSize()
+{
+ return m_maxSize;
+}
+
+#include "smssendprovider.moc"
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/services/smssendprovider.h b/kopete/protocols/sms/services/smssendprovider.h
new file mode 100644
index 00000000..8560be15
--- /dev/null
+++ b/kopete/protocols/sms/services/smssendprovider.h
@@ -0,0 +1,82 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSSENDPROVIDER_H
+#define SMSSENDPROVIDER_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include <qlabel.h>
+#include <qvaluelist.h>
+
+#include <klineedit.h>
+
+#include "kopetemessage.h"
+
+#include "smsaccount.h"
+
+class KProcess;
+namespace Kopete { class Account; }
+class SMSContact;
+
+class SMSSendProvider : public QObject
+{
+ Q_OBJECT
+public:
+ SMSSendProvider(const QString& providerName, const QString& prefixValue, Kopete::Account* account, QObject* parent = 0, const char* name = 0);
+ ~SMSSendProvider();
+
+ void setAccount(Kopete::Account *account);
+
+ int count();
+ const QString& name(int i);
+ const QString& value(int i);
+ const QString& description(int i);
+ const bool isHidden(int i);
+
+ void save(QPtrList<KLineEdit>& args);
+ void send(const Kopete::Message& msg);
+
+ int maxSize();
+private slots:
+ void slotReceivedOutput(KProcess*, char *buffer, int buflen);
+ void slotSendFinished(KProcess*);
+private:
+ QStringList names;
+ QStringList descriptions;
+ QStringList values;
+ QValueList<bool> isHiddens;
+
+ int messagePos;
+ int telPos;
+ int m_maxSize;
+
+ QString provider;
+ QString prefix;
+ QCString output;
+
+ Kopete::Account* m_account;
+
+ Kopete::Message m_msg;
+
+ bool canSend;
+signals:
+ void messageSent(const Kopete::Message& msg);
+ void messageNotSent(const Kopete::Message& msg, const QString &error);
+} ;
+
+#endif //SMSSENDPROVIDER_H
diff --git a/kopete/protocols/sms/smsaccount.cpp b/kopete/protocols/sms/smsaccount.cpp
new file mode 100644
index 00000000..5a13dca2
--- /dev/null
+++ b/kopete/protocols/sms/smsaccount.cpp
@@ -0,0 +1,202 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard L�k�g <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#undef KDE_NO_COMPAT
+
+#include <kconfigbase.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kprocess.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kopetemetacontact.h>
+#include <kopetecontactlist.h>
+
+#include "kopeteuiglobal.h"
+
+#include "serviceloader.h"
+
+#include "smsaccount.h"
+#include "smsprotocol.h"
+#include "smscontact.h"
+
+SMSAccount::SMSAccount( SMSProtocol *parent, const QString &accountID, const char *name )
+ : Kopete::Account( parent, accountID, name )
+{
+ setMyself( new SMSContact(this, accountID, accountID, Kopete::ContactList::self()->myself()) );
+ loadConfig();
+ myself()->setOnlineStatus( SMSProtocol::protocol()->SMSOffline );
+
+ QString sName = configGroup()->readEntry("ServiceName", QString::null);
+ theService = ServiceLoader::loadService(sName, this);
+
+ if( theService )
+ {
+ QObject::connect (theService, SIGNAL(messageSent(const Kopete::Message &)),
+ this, SLOT(slotSendingSuccess(const Kopete::Message &)));
+ QObject::connect (theService, SIGNAL(messageNotSent(const Kopete::Message &, const QString &)),
+ this, SLOT(slotSendingFailure(const Kopete::Message &, const QString &)));
+ QObject::connect (theService, SIGNAL(connected()), this, SLOT(slotConnected()));
+ QObject::connect (theService, SIGNAL(disconnected()), this, SLOT(slotDisconnected()));
+ }
+
+}
+
+SMSAccount::~SMSAccount()
+{
+ delete theService;
+ theService = NULL;
+}
+
+void SMSAccount::loadConfig()
+{
+ theSubEnable = configGroup()->readBoolEntry("SubEnable", false);
+ theSubCode = configGroup()->readEntry("SubCode", QString::null);
+ theLongMsgAction = (SMSMsgAction)configGroup()->readNumEntry("MsgAction", 0);
+}
+
+void SMSAccount::translateNumber(QString &theNumber)
+{
+ if(theNumber[0] == QChar('0') && theSubEnable)
+ theNumber.replace(0, 1, theSubCode);
+}
+
+const bool SMSAccount::splitNowMsgTooLong(int msgLength)
+{
+ if( theService == NULL )
+ return false;
+
+ int max = theService->maxSize();
+ if(theLongMsgAction == ACT_CANCEL) return false;
+ if(theLongMsgAction == ACT_SPLIT) return true;
+ if(KMessageBox::questionYesNo(Kopete::UI::Global::mainWidget(), i18n("This message is longer than the maximum length (%1). Should it be divided to %2 messages?").arg(max).arg(msgLength / max + 1),
+ i18n("Message Too Long"), i18n("Divide"), i18n("Do Not Divide")) == KMessageBox::Yes)
+ return true;
+ else
+ return false;
+}
+
+void SMSAccount::setAway( bool /*away*/, const QString &)
+{
+}
+
+void SMSAccount::connect(const Kopete::OnlineStatus&)
+{
+ myself()->setOnlineStatus( SMSProtocol::protocol()->SMSConnecting );
+ if( theService )
+ theService->connect();
+}
+
+void SMSAccount::slotConnected()
+{
+ myself()->setOnlineStatus( SMSProtocol::protocol()->SMSOnline );
+ setAllContactsStatus( SMSProtocol::protocol()->SMSOnline );
+}
+
+void SMSAccount::disconnect()
+{
+ if( theService )
+ theService->disconnect();
+}
+
+void SMSAccount::slotDisconnected()
+{
+ myself()->setOnlineStatus( SMSProtocol::protocol()->SMSOffline );
+ setAllContactsStatus( SMSProtocol::protocol()->SMSOffline );
+}
+
+void SMSAccount::slotSendMessage(Kopete::Message &msg)
+{
+ kdWarning( 14160 ) << k_funcinfo << " this = " << this << endl;
+
+ if(theService == 0L)
+ return;
+
+ int msgLength = msg.plainBody().length();
+
+ if( theService->maxSize() == -1 )
+ {
+ theService->send(msg);
+ }
+ else if( theService->maxSize() < msgLength )
+ {
+ if( splitNowMsgTooLong(msgLength) )
+ {
+ for (int i=0; i < msgLength / theService->maxSize() + 1; i++)
+ {
+ QString text = msg.plainBody();
+ text = text.mid( theService->maxSize() * i, theService->maxSize() );
+ Kopete::Message m( msg.from(), msg.to(), text, Kopete::Message::Outbound);
+
+ theService->send(m);
+ }
+ }
+ else
+ slotSendingFailure(msg, i18n("Message too long."));
+ }
+ else
+ {
+ theService->send(msg);
+ }
+
+}
+
+void SMSAccount::slotSendingSuccess(const Kopete::Message &msg)
+{
+ SMSContact* c = dynamic_cast<SMSContact*>(msg.to().first());
+ if( c )
+ c->slotSendingSuccess(msg);
+}
+
+void SMSAccount::slotSendingFailure(const Kopete::Message &msg, const QString &error)
+{
+ SMSContact* c = dynamic_cast<SMSContact*>(msg.to().first());
+ if( c )
+ c->slotSendingFailure(msg, error);
+}
+
+bool SMSAccount::createContact( const QString &contactId,
+ Kopete::MetaContact * parentContact )
+{
+ if (new SMSContact(this, contactId, parentContact->displayName(), parentContact))
+ return true;
+ else
+ return false;
+}
+
+KActionMenu* SMSAccount::actionMenu()
+{
+ KActionMenu *theActionMenu = Kopete::Account::actionMenu();
+ return theActionMenu;
+}
+
+void SMSAccount::setOnlineStatus( const Kopete::OnlineStatus & status , const QString &reason)
+{
+ if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Online )
+ connect();
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Offline )
+ disconnect();
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Away )
+ setAway( true, reason );
+}
+
+SMSService* SMSAccount::service()
+{
+ return theService;
+}
+
+#include "smsaccount.moc"
diff --git a/kopete/protocols/sms/smsaccount.h b/kopete/protocols/sms/smsaccount.h
new file mode 100644
index 00000000..2547fe6c
--- /dev/null
+++ b/kopete/protocols/sms/smsaccount.h
@@ -0,0 +1,79 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSACCOUNT_H
+#define SMSACCOUNT_H
+
+#include "kopeteaccount.h"
+
+class KActionMenu;
+class SMSProtocol;
+class SMSContact;
+class SMSService;
+class KProcess;
+
+enum SMSMsgAction { ACT_ASK = 0, ACT_CANCEL, ACT_SPLIT };
+
+class SMSAccount : public Kopete::Account
+{
+ Q_OBJECT
+
+public:
+ SMSAccount( SMSProtocol *parent, const QString &accountID, const char *name = 0L );
+ ~SMSAccount();
+
+ virtual KActionMenu* actionMenu(); // Per-protocol actions for the systray and the status bar
+
+ virtual void setAway( bool away, const QString & );
+
+ void translateNumber(QString &theNumber);
+
+ /**
+ * Checks to see if the message should be split or not, in case it is too long.
+ *
+ * Only ever call in case of message being too long - may result in user interaction.
+ */
+ const bool splitNowMsgTooLong(int msgLength);
+
+ SMSService* service();
+
+public slots:
+ void loadConfig();
+ void setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+
+public slots:
+ virtual void connect(const Kopete::OnlineStatus& initial= Kopete::OnlineStatus());
+ virtual void disconnect();
+ virtual void slotSendMessage(Kopete::Message &msg);
+
+protected slots:
+ virtual void slotSendingSuccess(const Kopete::Message &msg);
+ virtual void slotSendingFailure(const Kopete::Message &msg, const QString &error);
+ virtual void slotConnected();
+ virtual void slotDisconnected();
+
+
+protected:
+ bool createContact(const QString &contactId, Kopete::MetaContact *parentContact);
+
+private:
+ bool theSubEnable;
+ QString theSubCode;
+ SMSMsgAction theLongMsgAction;
+ SMSService* theService;
+};
+
+#endif
diff --git a/kopete/protocols/sms/smsaddcontactpage.cpp b/kopete/protocols/sms/smsaddcontactpage.cpp
new file mode 100644
index 00000000..55921b49
--- /dev/null
+++ b/kopete/protocols/sms/smsaddcontactpage.cpp
@@ -0,0 +1,65 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * 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 "smsadd.h"
+#include "smsaddcontactpage.h"
+#include "kopeteaccount.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+
+
+
+SMSAddContactPage::SMSAddContactPage(QWidget *parent, const char *name )
+ : AddContactPage(parent,name)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ smsdata = new smsAddUI(this);
+}
+
+SMSAddContactPage::~SMSAddContactPage()
+{
+
+}
+
+bool SMSAddContactPage::apply(Kopete::Account* a, Kopete::MetaContact* m)
+{
+ if ( validateData() )
+ {
+ QString nr = smsdata->addNr->text();
+ QString name = smsdata->addName->text();
+
+ return a->addContact( nr, m, Kopete::Account::ChangeKABC );
+ }
+
+ return false;
+}
+
+
+bool SMSAddContactPage::validateData()
+{
+ return true;
+}
+
+#include "smsaddcontactpage.moc"
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsaddcontactpage.h b/kopete/protocols/sms/smsaddcontactpage.h
new file mode 100644
index 00000000..37843bc9
--- /dev/null
+++ b/kopete/protocols/sms/smsaddcontactpage.h
@@ -0,0 +1,50 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * 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 SMSADDCONTACTPAGE_H
+#define SMSADDCONTACTPAGE_H
+
+#include <qwidget.h>
+#include <addcontactpage.h>
+#include <qlabel.h>
+
+class smsAddUI;
+class SMSProtocol;
+
+class SMSAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ SMSAddContactPage(QWidget *parent=0, const char *name=0);
+ ~SMSAddContactPage();
+ smsAddUI *smsdata;
+ virtual bool validateData();
+ virtual bool apply( Kopete::Account*, Kopete::MetaContact* );
+};
+
+
+#endif
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smscontact.cpp b/kopete/protocols/sms/smscontact.cpp
new file mode 100644
index 00000000..d220b380
--- /dev/null
+++ b/kopete/protocols/sms/smscontact.cpp
@@ -0,0 +1,142 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#undef KDE_NO_COMPAT
+#include <kconfigbase.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "kopetechatsessionmanager.h"
+#include "kopeteaccount.h"
+#include "kopeteuiglobal.h"
+
+#include "smscontact.h"
+#include "smsprotocol.h"
+#include "smsservice.h"
+#include "smsaccount.h"
+#include "smsuserpreferences.h"
+
+SMSContact::SMSContact( Kopete::Account* _account, const QString &phoneNumber,
+ const QString &displayName, Kopete::MetaContact *parent )
+: Kopete::Contact( _account, phoneNumber, parent ), m_phoneNumber( phoneNumber )
+{
+// kdWarning( 14160 ) << k_funcinfo << " this = " << this << ", phone = " << phoneNumber << endl;
+ setNickName( displayName );
+
+ m_msgManager = 0L;
+ m_actionPrefs = 0L;
+
+ if( account()->isConnected() )
+ setOnlineStatus( SMSProtocol::protocol()->SMSOnline );
+}
+
+void SMSContact::slotSendingSuccess(const Kopete::Message &msg)
+{
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+ manager(Kopete::Contact::CanCreate)->appendMessage((Kopete::Message &)msg);
+}
+
+void SMSContact::slotSendingFailure(const Kopete::Message &/*msg*/, const QString &error)
+{
+ KMessageBox::detailedError(Kopete::UI::Global::mainWidget(), i18n("Something went wrong when sending message."), error,
+ i18n("Could Not Send Message"));
+// manager()->messageFailed();
+ // TODO: swap for failed as above. show it anyway for now to allow closing of window.
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+void SMSContact::serialize( QMap<QString, QString> &serializedData,
+ QMap<QString, QString> & /* addressBookData */ )
+{
+ // Contact id and display name are already set for us
+ if (m_phoneNumber != contactId())
+ serializedData[ "contactId" ] = m_phoneNumber;
+}
+
+Kopete::ChatSession* SMSContact::manager( Kopete::Contact::CanCreateFlags canCreate )
+{
+ if ( m_msgManager || canCreate != Kopete::Contact::CanCreate )
+ {
+ return m_msgManager;
+ }
+ else
+ {
+ QPtrList<Kopete::Contact> contacts;
+ contacts.append(this);
+ m_msgManager = Kopete::ChatSessionManager::self()->create(account()->myself(), contacts, protocol());
+ connect(m_msgManager, SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession*)),
+ account(), SLOT(slotSendMessage(Kopete::Message&)));
+ connect(m_msgManager, SIGNAL(destroyed()), this, SLOT(slotChatSessionDestroyed()));
+ return m_msgManager;
+ }
+}
+
+void SMSContact::slotChatSessionDestroyed()
+{
+ m_msgManager = 0L;
+}
+
+
+void SMSContact::slotUserInfo()
+{
+}
+
+void SMSContact::deleteContact()
+{
+ deleteLater();
+}
+
+const QString SMSContact::qualifiedNumber()
+{
+ QString number = m_phoneNumber;
+ dynamic_cast<SMSAccount *>(account())->translateNumber(number);
+ return number;
+}
+
+const QString &SMSContact::phoneNumber()
+{
+ return m_phoneNumber;
+}
+
+void SMSContact::setPhoneNumber( const QString phoneNumber )
+{
+ deleteLater();
+ new SMSContact(account(), phoneNumber, nickName(), metaContact());
+}
+
+QPtrList<KAction>* SMSContact::customContextMenuActions()
+{
+ QPtrList<KAction> *m_actionCollection = new QPtrList<KAction>();
+ if( !m_actionPrefs )
+ m_actionPrefs = new KAction(i18n("&Contact Settings"), 0, this, SLOT(userPrefs()), this, "userPrefs");
+
+ m_actionCollection->append( m_actionPrefs );
+
+ return m_actionCollection;
+}
+
+void SMSContact::userPrefs()
+{
+ SMSUserPreferences* p = new SMSUserPreferences( this );
+ p->show();
+}
+
+#include "smscontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smscontact.h b/kopete/protocols/sms/smscontact.h
new file mode 100644
index 00000000..b47d2bd9
--- /dev/null
+++ b/kopete/protocols/sms/smscontact.h
@@ -0,0 +1,74 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSCONTACT_H
+#define SMSCONTACT_H
+
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+
+#include <qstring.h>
+
+class SMSAccount;
+namespace Kopete { class ChatSession; }
+namespace Kopete { class MetaContact; }
+
+class KActionCollection;
+class KAction;
+
+class SMSContact : public Kopete::Contact
+{
+ Q_OBJECT
+public:
+ SMSContact( Kopete::Account* _account, const QString &phoneNumber,
+ const QString &displayName, Kopete::MetaContact *parent );
+
+ QPtrList<KAction>* customContextMenuActions();
+
+ const QString &phoneNumber();
+ void setPhoneNumber( const QString phoneNumber );
+ const QString qualifiedNumber();
+
+ /**
+ * Serialize contact
+ */
+ virtual void serialize( QMap<QString, QString> &serializedData,
+ QMap<QString, QString> &addressBookData );
+
+ Kopete::ChatSession* manager( Kopete::Contact::CanCreateFlags canCreate = Kopete::Contact::CanCreate );
+
+public slots:
+ virtual void slotUserInfo();
+ virtual void deleteContact();
+ void slotSendingSuccess(const Kopete::Message &msg);
+ void slotSendingFailure(const Kopete::Message &msg, const QString &error);
+
+private slots:
+ void userPrefs();
+ void slotChatSessionDestroyed();
+
+private:
+ KAction* m_actionPrefs;
+
+ QString m_phoneNumber;
+
+ Kopete::ChatSession* m_msgManager;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smseditaccountwidget.cpp b/kopete/protocols/sms/smseditaccountwidget.cpp
new file mode 100644
index 00000000..b74c24f4
--- /dev/null
+++ b/kopete/protocols/sms/smseditaccountwidget.cpp
@@ -0,0 +1,147 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qvgroupbox.h>
+#include <qlayout.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+#include <qradiobutton.h>
+
+#include <kconfigbase.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <krestrictedline.h>
+
+#include "kopeteuiglobal.h"
+
+#include "smseditaccountwidget.h"
+#include "smsactprefs.h"
+#include "serviceloader.h"
+#include "smsprotocol.h"
+#include "smsaccount.h"
+
+SMSEditAccountWidget::SMSEditAccountWidget(SMSProtocol *protocol, Kopete::Account *account, QWidget *parent, const char */*name*/)
+ : QWidget(parent), KopeteEditAccountWidget(account)
+{
+ QVBoxLayout *l = new QVBoxLayout(this, QBoxLayout::Down);
+ preferencesDialog = new smsActPrefsUI(this);
+ l->addWidget(preferencesDialog);
+
+ service = 0L;
+ configWidget = 0L;
+ middleFrameLayout = 0L;
+
+ m_protocol = protocol;
+
+ QString sName;
+ if (account)
+ {
+ preferencesDialog->accountId->setText(account->accountId());
+ //Disable changing the account ID for now
+ //FIXME: Remove this when we can safely change the account ID (Matt)
+ preferencesDialog->accountId->setDisabled(true);
+ sName = account->configGroup()->readEntry("ServiceName", QString::null);
+ preferencesDialog->subEnable->setChecked(account->configGroup()->readBoolEntry("SubEnable", false));
+ preferencesDialog->subCode->setText(account->configGroup()->readEntry("SubCode", QString::null));
+ preferencesDialog->ifMessageTooLong->setCurrentItem(SMSMsgAction(account->configGroup()->readNumEntry("MsgAction", 0)));
+ }
+
+ preferencesDialog->serviceName->insertStringList(ServiceLoader::services());
+
+ connect (preferencesDialog->serviceName, SIGNAL(activated(const QString &)),
+ this, SLOT(setServicePreferences(const QString &)));
+ connect (preferencesDialog->descButton, SIGNAL(clicked()),
+ this, SLOT(showDescription()));
+
+
+ for (int i=0; i < preferencesDialog->serviceName->count(); i++)
+ {
+ if (preferencesDialog->serviceName->text(i) == sName)
+ {
+ preferencesDialog->serviceName->setCurrentItem(i);
+ break;
+ }
+ }
+ setServicePreferences(preferencesDialog->serviceName->currentText());
+}
+
+SMSEditAccountWidget::~SMSEditAccountWidget()
+{
+ delete service;
+}
+
+bool SMSEditAccountWidget::validateData()
+{
+ return true;
+}
+
+Kopete::Account* SMSEditAccountWidget::apply()
+{
+ if (!account())
+ setAccount( new SMSAccount( m_protocol, preferencesDialog->accountId->text() ) );
+
+ if (service)
+ service->setAccount(account());
+
+ KConfigGroup *c = account()->configGroup();
+ c->writeEntry("ServiceName", preferencesDialog->serviceName->currentText());
+ c->writeEntry("SubEnable", preferencesDialog->subEnable->isChecked() ? "true" : "false");
+ c->writeEntry("SubCode", preferencesDialog->subCode->text());
+ c->writeEntry("MsgAction", preferencesDialog->ifMessageTooLong->currentItem());
+
+ emit saved();
+ return account();
+}
+
+void SMSEditAccountWidget::setServicePreferences(const QString& serviceName)
+{
+ delete service;
+ delete configWidget;
+
+ service = ServiceLoader::loadService(serviceName, account());
+
+ if (service == 0L)
+ return;
+
+ connect (this, SIGNAL(saved()), service, SLOT(savePreferences()));
+
+ delete middleFrameLayout;
+ middleFrameLayout = new QGridLayout(preferencesDialog->middleFrame, 1, 2, 0, 6, "middleFrameLayout");
+ service->setWidgetContainer(preferencesDialog->middleFrame, middleFrameLayout);
+}
+
+void SMSEditAccountWidget::showDescription()
+{
+ SMSService* s = ServiceLoader::loadService(preferencesDialog->serviceName->currentText(), 0L);
+
+ QString d = s->description();
+
+ KMessageBox::information(Kopete::UI::Global::mainWidget(), d, i18n("Description"));
+}
+
+#include "smseditaccountwidget.moc"
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smseditaccountwidget.h b/kopete/protocols/sms/smseditaccountwidget.h
new file mode 100644
index 00000000..eaf83d42
--- /dev/null
+++ b/kopete/protocols/sms/smseditaccountwidget.h
@@ -0,0 +1,64 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSEDITACCOUNTWIDGET_H
+#define SMSEDITACCOUNTWIDGET_H
+
+#include <qwidget.h>
+#include "editaccountwidget.h"
+
+class SMSProtocol;
+class SMSService;
+class smsActPrefsUI;
+namespace Kopete { class Account; }
+class QGridLayout;
+
+class SMSEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+public:
+ SMSEditAccountWidget(SMSProtocol *protocol, Kopete::Account *theAccount, QWidget *parent = 0, const char *name = 0);
+ ~SMSEditAccountWidget();
+
+ bool validateData();
+ Kopete::Account* apply();
+public slots:
+ void setServicePreferences(const QString& serviceName);
+ void showDescription();
+protected:
+ smsActPrefsUI *preferencesDialog;
+ QWidget *configWidget;
+ SMSService *service;
+ SMSProtocol *m_protocol;
+ QGridLayout *middleFrameLayout;
+
+signals:
+ void saved();
+};
+
+#endif
+
+
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsprotocol.cpp b/kopete/protocols/sms/smsprotocol.cpp
new file mode 100644
index 00000000..6b6cd838
--- /dev/null
+++ b/kopete/protocols/sms/smsprotocol.cpp
@@ -0,0 +1,97 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kgenericfactory.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+
+#include "kopeteaccountmanager.h"
+#include "kopeteonlinestatusmanager.h"
+#include "smsprotocol.h"
+#include "smseditaccountwidget.h"
+#include "smscontact.h"
+#include "smsaddcontactpage.h"
+#include "smsaccount.h"
+
+typedef KGenericFactory<SMSProtocol> SMSProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_sms, SMSProtocolFactory( "kopete_sms" ) )
+
+SMSProtocol* SMSProtocol::s_protocol = 0L;
+
+SMSProtocol::SMSProtocol(QObject *parent, const char *name, const QStringList &/*args*/)
+: Kopete::Protocol( SMSProtocolFactory::instance(), parent, name ),
+ SMSOnline( Kopete::OnlineStatus::Online, 25, this, 0, QString::null, i18n( "Online" ), i18n( "Online" ), Kopete::OnlineStatusManager::Online ),
+ SMSConnecting( Kopete::OnlineStatus::Connecting,2, this, 3, QString::null, i18n( "Connecting" ) ),
+ SMSOffline( Kopete::OnlineStatus::Offline, 0, this, 2, QString::null, i18n( "Offline" ), i18n( "Offline" ), Kopete::OnlineStatusManager::Offline )
+{
+ if (s_protocol)
+ kdWarning( 14160 ) << k_funcinfo << "s_protocol already defined!" << endl;
+ else
+ s_protocol = this;
+
+ addAddressBookField("messaging/sms", Kopete::Plugin::MakeIndexField);
+}
+
+SMSProtocol::~SMSProtocol()
+{
+ s_protocol = 0L;
+}
+
+AddContactPage *SMSProtocol::createAddContactWidget(QWidget *parent, Kopete::Account */*i*/)
+{
+ return new SMSAddContactPage(parent);
+}
+
+KopeteEditAccountWidget* SMSProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new SMSEditAccountWidget(this, account, parent);
+}
+
+SMSProtocol* SMSProtocol::protocol()
+{
+ return s_protocol;
+}
+
+Kopete::Contact *SMSProtocol::deserializeContact(Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &/* addressBookData */)
+{
+ QString contactId = serializedData["contactId"];
+ QString accountId = serializedData["accountId"];
+ QString displayName = serializedData["displayName"];
+
+ QDict<Kopete::Account> accounts=Kopete::AccountManager::self()->accounts(this);
+
+ Kopete::Account *account = accounts[accountId];
+ if (!account)
+ {
+ kdDebug(14160) << "Account doesn't exist, skipping" << endl;
+ return 0;
+ }
+
+ return new SMSContact(account, contactId, displayName, metaContact);
+}
+
+Kopete::Account* SMSProtocol::createNewAccount(const QString &accountId)
+{
+ return new SMSAccount(this, accountId);
+}
+
+#include "smsprotocol.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsprotocol.h b/kopete/protocols/sms/smsprotocol.h
new file mode 100644
index 00000000..1d4aaa40
--- /dev/null
+++ b/kopete/protocols/sms/smsprotocol.h
@@ -0,0 +1,71 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSPROTOCOL_H
+#define SMSPROTOCOL_H
+
+#include <qmap.h>
+#include <qmovie.h>
+#include <qpixmap.h>
+#include <qptrdict.h>
+#include <qptrlist.h>
+#include <qstringlist.h>
+
+#include "kopeteprotocol.h"
+#include "kopeteonlinestatus.h"
+#include "kopetecontact.h"
+
+class KAction;
+class KActionMenu;
+
+namespace Kopete { class Contact; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class Message; }
+namespace Kopete { class ChatSession; }
+class SMSContact;
+
+class SMSProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+public:
+ SMSProtocol(QObject *parent, const char *name, const QStringList &args);
+ ~SMSProtocol();
+
+ static SMSProtocol *protocol();
+
+ /**
+ * Deserialize contact data
+ */
+ virtual Kopete::Contact *deserializeContact(Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData, const QMap<QString, QString> &addressBookData );
+
+ virtual AddContactPage *createAddContactWidget(QWidget *parent , Kopete::Account *i);
+ virtual KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+ virtual Kopete::Account *createNewAccount(const QString &accountId);
+
+ const Kopete::OnlineStatus SMSOnline;
+ const Kopete::OnlineStatus SMSOffline;
+ const Kopete::OnlineStatus SMSConnecting;
+
+private:
+ static SMSProtocol *s_protocol;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsservice.cpp b/kopete/protocols/sms/smsservice.cpp
new file mode 100644
index 00000000..81b46533
--- /dev/null
+++ b/kopete/protocols/sms/smsservice.cpp
@@ -0,0 +1,63 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlayout.h>
+
+#include <kdebug.h>
+
+#include "smsservice.h"
+
+SMSService::SMSService(Kopete::Account* account)
+ : QObject(), m_account(account)
+{
+}
+
+SMSService::~SMSService()
+{
+
+}
+
+void SMSService::setAccount(Kopete::Account* account)
+{
+ if(!m_account)
+ m_account = account;
+ if(account)
+ savePreferences();
+}
+
+// The Default impl simply flips a signal back
+void SMSService::connect()
+{
+ emit connected();
+}
+
+// The Default impl simply flips a signal back
+void SMSService::disconnect()
+{
+ emit disconnected();
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
+
+#include "smsservice.moc"
diff --git a/kopete/protocols/sms/smsservice.h b/kopete/protocols/sms/smsservice.h
new file mode 100644
index 00000000..f1c04470
--- /dev/null
+++ b/kopete/protocols/sms/smsservice.h
@@ -0,0 +1,83 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSSERVICE_H
+#define SMSSERVICE_H
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <qobject.h>
+
+#include "kopetemessage.h"
+
+class SMSContact;
+namespace Kopete { class Account; }
+class QGridLayout;
+class QWidget;
+
+class SMSService : public QObject
+{
+ Q_OBJECT
+public:
+ SMSService(Kopete::Account* account = 0);
+ virtual ~SMSService();
+
+ /**
+ * Reimplement to do extra stuff when the account is dynamically changed
+ * (other than just changing m_account).
+ *
+ * Don't forget to call SMSService::setAccount(...) after you've finished.
+ */
+ virtual void setAccount(Kopete::Account* account);
+
+ /**
+ * Called when the settings widget has a place to be. @param parent is the
+ * settings widget's parent and @param layout is the 2xn grid layout it may
+ * use.
+ */
+ virtual void setWidgetContainer(QWidget* parent, QGridLayout* layout) = 0;
+
+ virtual void send(const Kopete::Message& msg) = 0;
+ virtual int maxSize() = 0;
+ virtual const QString& description() = 0;
+
+public slots:
+ virtual void savePreferences() = 0;
+ virtual void connect();
+ virtual void disconnect();
+
+signals:
+ void messageSent(const Kopete::Message &);
+ void messageNotSent(const Kopete::Message &, const QString &);
+ void connected();
+ void disconnected();
+
+protected:
+ Kopete::Account* m_account;
+ QGridLayout* m_layout;
+ QWidget* m_parent;
+};
+
+#endif //SMSSERVICE_H
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsuserpreferences.cpp b/kopete/protocols/sms/smsuserpreferences.cpp
new file mode 100644
index 00000000..89677080
--- /dev/null
+++ b/kopete/protocols/sms/smsuserpreferences.cpp
@@ -0,0 +1,63 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <klineedit.h>
+
+#include "smsuserpreferences.h"
+#include "smsuserprefs.h"
+#include "smscontact.h"
+
+SMSUserPreferences::SMSUserPreferences( SMSContact* contact )
+ : KDialogBase( 0L, "userPrefs", true, i18n("User Preferences"), Ok|Cancel, Ok, true )
+{
+ m_contact = contact;
+ topWidget = makeVBoxMainWidget();
+ userPrefs = new SMSUserPrefsUI( topWidget );
+
+ userPrefs->telNumber->setText(m_contact->phoneNumber());
+ userPrefs->title->setText(m_contact->nickName());
+}
+
+SMSUserPreferences::~SMSUserPreferences()
+{
+
+}
+
+void SMSUserPreferences::slotOk()
+{
+ if (userPrefs->telNumber->text() != m_contact->phoneNumber())
+ m_contact->setPhoneNumber(userPrefs->telNumber->text());
+ slotCancel();
+}
+
+void SMSUserPreferences::slotCancel()
+{
+ deleteLater();
+}
+
+#include "smsuserpreferences.moc"
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/sms/smsuserpreferences.h b/kopete/protocols/sms/smsuserpreferences.h
new file mode 100644
index 00000000..29fb6dd2
--- /dev/null
+++ b/kopete/protocols/sms/smsuserpreferences.h
@@ -0,0 +1,44 @@
+/* *************************************************************************
+ * copyright: (C) 2003 Richard Lärkäng <nouseforaname@home.se> *
+ * copyright: (C) 2003 Gav Wood <gav@kde.org> *
+ *************************************************************************
+*/
+
+/* *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SMSUSERPREFERENCES_H
+#define SMSUSERPREFERENCES_H
+
+#include <kdialogbase.h>
+#include <qvbox.h>
+
+class SMSPreferencesBase;
+class SMSUserPrefsUI;
+class SMSContact;
+
+class SMSUserPreferences : public KDialogBase
+{
+ Q_OBJECT
+public:
+ SMSUserPreferences(SMSContact* contact);
+ ~SMSUserPreferences();
+private:
+ SMSPreferencesBase* prefBase;
+ SMSUserPrefsUI* userPrefs;
+ QVBox* topWidget;
+
+ SMSContact* m_contact;
+public slots:
+ void slotOk();
+ void slotCancel();
+} ;
+
+#endif //SMSUSERPREFERENCES_H
diff --git a/kopete/protocols/sms/ui/Makefile.am b/kopete/protocols/sms/ui/Makefile.am
new file mode 100644
index 00000000..660aa359
--- /dev/null
+++ b/kopete/protocols/sms/ui/Makefile.am
@@ -0,0 +1,8 @@
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libkopetesmsui.la
+
+libkopetesmsui_la_SOURCES = smsadd.ui smsactprefs.ui smsuserprefs.ui empty.cpp
diff --git a/kopete/protocols/sms/ui/empty.cpp b/kopete/protocols/sms/ui/empty.cpp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/protocols/sms/ui/empty.cpp
diff --git a/kopete/protocols/sms/ui/smsactprefs.ui b/kopete/protocols/sms/ui/smsactprefs.ui
new file mode 100644
index 00000000..62e53800
--- /dev/null
+++ b/kopete/protocols/sms/ui/smsactprefs.ui
@@ -0,0 +1,435 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>smsActPrefsUI</class>
+<author>Richard Lärkäng</author>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>smsActPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>465</width>
+ <height>437</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - SMS</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="QFrame">
+ <property name="name">
+ <cstring>middleFrame</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget9</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>groupBox61</cstring>
+ </property>
+ <property name="title">
+ <string>Account Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Account name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>accountId</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;SMS delivery service:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>serviceName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The delivery service that you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The delivery service that you would like to use. Note that you will need to have this software installed prior to using this account.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>accountId</cstring>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout35</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>serviceName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The delivery service that you would like to use.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The delivery service that you would like to use. Note that you will need to have this software installed prior to using this account.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>descButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Description</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Description of the SMS delivery service.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Description of the SMS delivery service, including download locations.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox22</cstring>
+ </property>
+ <property name="title">
+ <string>Information</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>To use SMS, you will need an account with a delivery service.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer25</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>181</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>A&amp;ccount Preferences</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox62</cstring>
+ </property>
+ <property name="title">
+ <string>Messaging Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout119</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>If the message is too &amp;long:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ifMessageTooLong</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>What should happen if you type a message that is too long to fit in a single SMS message.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>What should happen if you type a message that is too long to fit in a single SMS message. You can either choose to break it up into smaller messages automatically, cancel the message from being sent entirely, or have Kopete prompt you each time you enter a message that is too long.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Prompt (recommended)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Break Into Multiple</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Cancel Sending</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>ifMessageTooLong</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>What should happen if you type a message that is too long to fit in a single SMS message.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>What should happen if you type a message that is too long to fit in a single SMS message. You can either choose to break it up into smaller messages automatically, cancel the message from being sent entirely, or have Kopete prompt you each time you enter a message that is too long.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>subEnable</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enable phone number internationalization</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Check if you would like to enable phone number internationalization.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check if you would like to enable phone number internationalization. Without this option, you will only be able to use SMS for accounts within your country.</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout56</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_3</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Substitute leading &amp;zero with code:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>subCode</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>What you would like to substitute a leading zero with.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>What you would like to substitute a leading zero with.</string>
+ </property>
+ </widget>
+ <widget class="KRestrictedLine">
+ <property name="name">
+ <cstring>subCode</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>+</string>
+ </property>
+ <property name="validChars">
+ <string>1234567890+</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>What you would like to substitute a leading zero with.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>What you would like to substitute a leading zero with.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer27</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</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>
+<connections>
+ <connection>
+ <sender>subEnable</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2_3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>subEnable</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>subCode</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget9</tabstop>
+ <tabstop>accountId</tabstop>
+ <tabstop>serviceName</tabstop>
+ <tabstop>descButton</tabstop>
+ <tabstop>ifMessageTooLong</tabstop>
+ <tabstop>subEnable</tabstop>
+ <tabstop>subCode</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">knuminput.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>krestrictedline.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/sms/ui/smsadd.ui b/kopete/protocols/sms/ui/smsadd.ui
new file mode 100644
index 00000000..0ee71281
--- /dev/null
+++ b/kopete/protocols/sms/ui/smsadd.ui
@@ -0,0 +1,143 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>smsAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>smsAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>397</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout35</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout33</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Telephone number:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addNr</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The telephone number of the contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The telephone number of the contact you would like to add. This should be a number with SMS service available.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Contact na&amp;me:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout34</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>addNr</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The telephone number of the contact you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The telephone number of the contact you would like to add. This should be a number with SMS service available.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>addName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>A unique name for this SMS account.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <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>31</width>
+ <height>170</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>addNr</tabstop>
+ <tabstop>addName</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/sms/ui/smsuserprefs.ui b/kopete/protocols/sms/ui/smsuserprefs.ui
new file mode 100644
index 00000000..8a912792
--- /dev/null
+++ b/kopete/protocols/sms/ui/smsuserprefs.ui
@@ -0,0 +1,118 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>SMSUserPrefsUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>SMSUserPrefsUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>252</width>
+ <height>144</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>title</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Some One</string>
+ </property>
+ </widget>
+ <widget class="Line">
+ <property name="name">
+ <cstring>line10</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>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Telephone number:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>telNumber</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The telephone number of the contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The telephone number of the contact. This should be a number with SMS service available.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>telNumber</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The telephone number of the contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The telephone number of the contact. This should be a number with SMS service available.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer11</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/testbed/Makefile.am b/kopete/protocols/testbed/Makefile.am
new file mode 100644
index 00000000..b414547a
--- /dev/null
+++ b/kopete/protocols/testbed/Makefile.am
@@ -0,0 +1,15 @@
+METASOURCES = AUTO
+
+SUBDIRS = ui . icons
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(srcdir)/ui -Iui $(all_includes)
+
+noinst_HEADERS = testbedprotocol.h testbedcontact.h testbedaccount.h testbedaddcontactpage.h testbededitaccountwidget.h testbedfakeserver.h testbedincomingmessage.h
+kde_module_LTLIBRARIES = kopete_testbed.la
+kopete_testbed_la_SOURCES = testbedprotocol.cpp testbedcontact.cpp testbedaccount.cpp testbedaddcontactpage.cpp testbedaddui.ui testbededitaccountwidget.cpp testbedaccountpreferences.ui testbedfakeserver.cpp testbedincomingmessage.cpp
+kopete_testbed_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
+kopete_testbed_la_LIBADD = ../../libkopete/avdevice/libkopete_videodevice.la \
+ ui/libkopetetestbedui.la ../../libkopete/libkopete.la $(LIB_KIO)
+
+service_DATA = kopete_testbed.desktop
+servicedir= $(kde_servicesdir)
diff --git a/kopete/protocols/testbed/icons/Makefile.am b/kopete/protocols/testbed/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/protocols/testbed/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/protocols/testbed/icons/cr128-app-testbed_protocol.png b/kopete/protocols/testbed/icons/cr128-app-testbed_protocol.png
new file mode 100644
index 00000000..edfa2a2a
--- /dev/null
+++ b/kopete/protocols/testbed/icons/cr128-app-testbed_protocol.png
Binary files differ
diff --git a/kopete/protocols/testbed/icons/cr16-app-testbed_protocol.png b/kopete/protocols/testbed/icons/cr16-app-testbed_protocol.png
new file mode 100644
index 00000000..b97f23f4
--- /dev/null
+++ b/kopete/protocols/testbed/icons/cr16-app-testbed_protocol.png
Binary files differ
diff --git a/kopete/protocols/testbed/icons/cr32-app-testbed_protocol.png b/kopete/protocols/testbed/icons/cr32-app-testbed_protocol.png
new file mode 100644
index 00000000..82180b6a
--- /dev/null
+++ b/kopete/protocols/testbed/icons/cr32-app-testbed_protocol.png
Binary files differ
diff --git a/kopete/protocols/testbed/icons/cr48-app-testbed_protocol.png b/kopete/protocols/testbed/icons/cr48-app-testbed_protocol.png
new file mode 100644
index 00000000..f88a938f
--- /dev/null
+++ b/kopete/protocols/testbed/icons/cr48-app-testbed_protocol.png
Binary files differ
diff --git a/kopete/protocols/testbed/icons/cr64-app-testbed_protocol.png b/kopete/protocols/testbed/icons/cr64-app-testbed_protocol.png
new file mode 100644
index 00000000..daa9f28c
--- /dev/null
+++ b/kopete/protocols/testbed/icons/cr64-app-testbed_protocol.png
Binary files differ
diff --git a/kopete/protocols/testbed/kopete_testbed.desktop b/kopete/protocols/testbed/kopete_testbed.desktop
new file mode 100644
index 00000000..2ab38536
--- /dev/null
+++ b/kopete/protocols/testbed/kopete_testbed.desktop
@@ -0,0 +1,97 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=testbed_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_testbed
+X-Kopete-Messaging-Protocol=messaging/testbed
+X-KDE-PluginInfo-Author=Will Stephenson
+X-KDE-PluginInfo-Email=will@stevello.free-online.co.uk
+X-KDE-PluginInfo-Name=kopete_testbed
+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=Testbed
+Name[ar]=مكان اختبار
+Name[bn]=পরীকà§à¦·à¦¾à¦¸à§à¦¥à¦²
+Name[cy]=Mainc Arbrofi
+Name[eo]=Testejo
+Name[et]=Testija
+Name[fi]=Testipenkki
+Name[gl]=testbed
+Name[hi]=टेसà¥à¤Ÿà¤¬à¥‡à¤¡
+Name[hr]=Probno okruženje
+Name[hu]=Tesztelési
+Name[is]=Prufuhorn
+Name[lt]=Tikrinimas
+Name[nb]=Testbenk
+Name[ne]=टेसà¥à¤Ÿà¤¬à¥‡à¤¡
+Name[nl]=Testomgeving
+Name[nn]=Testbenk
+Name[pl]=Testowy
+Name[pt]=Testes
+Name[sk]=Test
+Name[sr]=Пробни Ñто
+Name[sr@Latn]=Probni sto
+Name[sv]=Testomgivning
+Name[tr]=Deneme ortamı
+Name[uk]=ТеÑÑ‚
+Name[zh_CN]=测试床
+Comment=Kopete test protocol
+Comment[ar]=بروتوكول اختبار Kopete
+Comment[be]=ТÑÑтавы пратакол Kopete
+Comment[bg]=Пробен протокол на Kopete
+Comment[bn]=কপেট পরীকà§à¦·à¦¾ পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[bs]=Kopete testni protokol
+Comment[ca]=Protocol per a proves en Kopete
+Comment[cy]=Protocol arbrofi Kopete
+Comment[da]=Kopete test-protokol
+Comment[de]=Kopete Testprotokoll
+Comment[el]=ΠÏωτόκολλο ελέγχου του Kopete
+Comment[es]=Protocolo de prueba de Kopete
+Comment[et]=Kopete testprotokoll
+Comment[eu]=Kopete proba protokoloa
+Comment[fa]=Kopete قرارداد را آزمایش می‌کند
+Comment[fi]=Kopeten testiyhteyskäytäntö
+Comment[fr]=Protocole de test de Kopete
+Comment[ga]=Prótacal tástála Kopete
+Comment[gl]=Protocolo de Kopete para probas
+Comment[he]=פרוטוקול הניסוי של Kopete
+Comment[hi]=के-ऑपà¥à¤Ÿà¥€ टेसà¥à¤Ÿ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Kopete protokol za testiranje
+Comment[hu]=Kopete tesztprotokoll
+Comment[is]=Kopete prufuíhlutur
+Comment[it]=Protocollo di test di Kopete
+Comment[ja]=Kopete テストプロトコル
+Comment[ka]=Kopete-ს ტესტირების áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Kopete Ñынау протоколы
+Comment[km]=ពិធីការ​សាកល្បង Kopete
+Comment[lt]=Kopete tikrinimo protokolas
+Comment[mk]=Протокол за теÑтирање на Kopete
+Comment[nb]=Kopete protokoll for tester
+Comment[nds]=Kopete-Pröövprotokoll
+Comment[ne]=कोपेट परीकà¥à¤·à¤£ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Kopete testprotocol
+Comment[nn]=Kopete-protokoll for testing
+Comment[pl]=Protokół testowy Kopete
+Comment[pt]=Protocolo de teste do Kopete
+Comment[pt_BR]=Protocolo de Teste do Kopete
+Comment[ro]=Protocol de test Kopete
+Comment[ru]=ТеÑтовый протокол Kopete
+Comment[se]=Kopete geahÄÄalanprotokolla
+Comment[sk]=Testovací protokol Kopete
+Comment[sl]=Preskusni protokol za Kopete
+Comment[sr]=Kopete-ов протокол за теÑтирање
+Comment[sr@Latn]=Kopete-ov protokol za testiranje
+Comment[sv]=Testprotokoll för Kopete
+Comment[ta]=Kopete விதிமà¯à®±à¯ˆ சோதனை
+Comment[tg]=Қарордоди Ñанҷишии Kopete
+Comment[tr]=Kopete deneme protokolü
+Comment[uk]=ТеÑтовий протокол Kopete
+Comment[wa]=Protocole di saye po Kopete
+Comment[zh_CN]=Kopete 测试åè®®
+Comment[zh_HK]=Kopete 測試通訊å”定
+Comment[zh_TW]=Kopete 測試å”定
diff --git a/kopete/protocols/testbed/testbedaccount.cpp b/kopete/protocols/testbed/testbedaccount.cpp
new file mode 100644
index 00000000..fbb3462a
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaccount.cpp
@@ -0,0 +1,176 @@
+/*
+ testbedaccount.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedaccount.h"
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+
+#include "testbedcontact.h"
+#include "testbedfakeserver.h"
+#include "testbedprotocol.h"
+
+
+TestbedAccount::TestbedAccount( TestbedProtocol *parent, const QString& accountID, const char *name )
+: Kopete::Account ( parent, accountID , name )
+{
+ // Init the myself contact
+ setMyself( new TestbedContact( this, accountId(), TestbedContact::Null, accountId(), Kopete::ContactList::self()->myself() ) );
+ myself()->setOnlineStatus( TestbedProtocol::protocol()->testbedOffline );
+ m_server = new TestbedFakeServer();;
+}
+
+TestbedAccount::~TestbedAccount()
+{
+ delete m_server;
+}
+
+KActionMenu* TestbedAccount::actionMenu()
+{
+ KActionMenu *mActionMenu = Kopete::Account::actionMenu();
+
+ mActionMenu->popupMenu()->insertSeparator();
+
+ KAction *action;
+
+ action = new KAction (i18n ("Show my own video..."), "testbed_showvideo", 0, this, SLOT (slotShowVideo ()), this, "actionShowVideo");
+ mActionMenu->insert(action);
+ action->setEnabled( isConnected() );
+
+ return mActionMenu;
+}
+
+bool TestbedAccount::createContact(const QString& contactId, Kopete::MetaContact* parentContact)
+{
+ TestbedContact* newContact = new TestbedContact( this, contactId, TestbedContact::Echo, parentContact->displayName(), parentContact );
+ return newContact != 0L;
+}
+
+void TestbedAccount::setAway( bool away, const QString & /* reason */ )
+{
+ if ( away )
+ slotGoAway();
+ else
+ slotGoOnline();
+}
+
+void TestbedAccount::setOnlineStatus(const Kopete::OnlineStatus& status, const QString &reason )
+{
+ if ( status.status() == Kopete::OnlineStatus::Online &&
+ myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline )
+ slotGoOnline();
+ else if (status.status() == Kopete::OnlineStatus::Online &&
+ myself()->onlineStatus().status() == Kopete::OnlineStatus::Away )
+ setAway( false, reason );
+ else if ( status.status() == Kopete::OnlineStatus::Offline )
+ slotGoOffline();
+ else if ( status.status() == Kopete::OnlineStatus::Away )
+ slotGoAway( /* reason */ );
+}
+
+void TestbedAccount::connect( const Kopete::OnlineStatus& /* initialStatus */ )
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+ myself()->setOnlineStatus( TestbedProtocol::protocol()->testbedOnline );
+ QObject::connect ( m_server, SIGNAL ( messageReceived( const QString & ) ),
+ this, SLOT ( receivedMessage( const QString & ) ) );
+}
+
+void TestbedAccount::disconnect()
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+ myself()->setOnlineStatus( TestbedProtocol::protocol()->testbedOffline );
+ QObject::disconnect ( m_server, 0, 0, 0 );
+}
+
+TestbedFakeServer * TestbedAccount::server()
+{
+ return m_server;
+}
+
+void TestbedAccount::slotGoOnline ()
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+
+ if (!isConnected ())
+ connect ();
+ else
+ myself()->setOnlineStatus( TestbedProtocol::protocol()->testbedOnline );
+ updateContactStatus();
+}
+
+void TestbedAccount::slotGoAway ()
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+
+ if (!isConnected ())
+ connect();
+
+ myself()->setOnlineStatus( TestbedProtocol::protocol()->testbedAway );
+ updateContactStatus();
+}
+
+
+void TestbedAccount::slotGoOffline ()
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+
+ if (isConnected ())
+ disconnect ();
+ updateContactStatus();
+}
+
+void TestbedAccount::slotShowVideo ()
+{
+ kdDebug ( 14210 ) << k_funcinfo << endl;
+
+ if (isConnected ())
+ TestbedWebcamDialog *testbedWebcamDialog = new TestbedWebcamDialog(0, 0, "Testbed video window");
+ updateContactStatus();
+}
+
+void TestbedAccount::receivedMessage( const QString &message )
+{
+ // Look up the contact the message is from
+ QString from;
+ TestbedContact* messageSender;
+
+ from = message.section( ':', 0, 0 );
+ Kopete::Contact* contact = contacts()[from];
+ messageSender = dynamic_cast<TestbedContact *>( contact );
+
+ kdDebug( 14210 ) << k_funcinfo << " got a message from " << from << ", " << messageSender << ", is: " << message << endl;
+ // Pass it on to the contact to process and display via a KMM
+ if ( messageSender )
+ messageSender->receivedMessage( message );
+ else
+ kdWarning(14210) << k_funcinfo << "unable to look up contact for delivery" << endl;
+}
+
+void TestbedAccount::updateContactStatus()
+{
+ QDictIterator<Kopete::Contact> itr( contacts() );
+ for ( ; itr.current(); ++itr )
+ itr.current()->setOnlineStatus( myself()->onlineStatus() );
+}
+
+
+#include "testbedaccount.moc"
diff --git a/kopete/protocols/testbed/testbedaccount.h b/kopete/protocols/testbed/testbedaccount.h
new file mode 100644
index 00000000..34429300
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaccount.h
@@ -0,0 +1,105 @@
+/*
+ testbedaccount.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TESTBEDACCOUNT_H
+#define TESTBEDACCOUNT_H
+
+#include <kopeteaccount.h>
+#include "testbedwebcamdialog.h"
+
+class KActionMenu;
+namespace Kopete { class Contact; }
+namespace Kopete { class MetaContact; }
+
+class TestbedContact;
+class TestbedProtocol;
+class TestbedFakeServer;
+
+/**
+ * This represents an account connected to the testbed
+ * @author Will Stephenson
+*/
+class TestbedAccount : public Kopete::Account
+{
+ Q_OBJECT
+public:
+ TestbedAccount( TestbedProtocol *parent, const QString& accountID, const char *name = 0 );
+ ~TestbedAccount();
+ /**
+ * Construct the context menu used for the status bar icon
+ */
+ virtual KActionMenu* actionMenu();
+
+ /**
+ * Creates a protocol specific Kopete::Contact subclass and adds it to the supplie
+ * Kopete::MetaContact
+ */
+ virtual bool createContact(const QString& contactId, Kopete::MetaContact* parentContact);
+ /**
+ * Called when Kopete is set globally away
+ */
+ virtual void setAway(bool away, const QString& reason);
+ /**
+ * Called when Kopete status is changed globally
+ */
+ virtual void setOnlineStatus(const Kopete::OnlineStatus& status , const QString &reason = QString::null);
+ /**
+ * 'Connect' to the testbed server. Only sets myself() online.
+ */
+ virtual void connect( const Kopete::OnlineStatus& initialStatus = Kopete::OnlineStatus::OnlineStatus() );
+ /**
+ * Disconnect from the server. Only sets myself() offline.
+ */
+ virtual void disconnect();
+ /**
+ * Return a reference to the server stub
+ */
+ TestbedFakeServer* server();
+public slots:
+ /**
+ * Called by the server when it has a message for us.
+ * This identifies the sending Kopete::Contact and passes it a Kopete::Message
+ */
+ void receivedMessage( const QString &message );
+
+protected:
+ /**
+ * This simulates contacts going on and offline in sync with the account's status changes
+ */
+ void updateContactStatus();
+ TestbedFakeServer* m_server;
+
+protected slots:
+ /**
+ * Change the account's status. Called by KActions and internally.
+ */
+ void slotGoOnline();
+ /**
+ * Change the account's status. Called by KActions and internally.
+ */
+ void slotGoAway();
+ /**
+ * Change the account's status. Called by KActions and internally.
+ */
+ void slotGoOffline();
+ /**
+ * Show webcam. Called by KActions and internally.
+ */
+ void slotShowVideo();
+
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbedaccountpreferences.ui b/kopete/protocols/testbed/testbedaccountpreferences.ui
new file mode 100644
index 00000000..e1c75ca6
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaccountpreferences.ui
@@ -0,0 +1,160 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>TestbedAccountPreferences</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TestbedAccountPreferences</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>387</width>
+ <height>372</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - Testbed</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_2</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_2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>accountLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Account name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_acctName</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_acctName</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>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox22</cstring>
+ </property>
+ <property name="title">
+ <string>Information</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>To use the testbed protocol, just make up an account name. This protocol has no real networking capability.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer27</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>131</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>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/testbed/testbedaddcontactpage.cpp b/kopete/protocols/testbed/testbedaddcontactpage.cpp
new file mode 100644
index 00000000..5327b5b4
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaddcontactpage.cpp
@@ -0,0 +1,68 @@
+/*
+ testbedaddcontactpage.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedaddcontactpage.h"
+
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <kdebug.h>
+
+#include "kopeteaccount.h"
+#include "kopetemetacontact.h"
+
+#include "testbedaddui.h"
+
+TestbedAddContactPage::TestbedAddContactPage( QWidget* parent, const char* name )
+ : AddContactPage(parent, name)
+{
+ kdDebug(14210) << k_funcinfo << endl;
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ m_testbedAddUI = new TestbedAddUI( this );
+}
+
+TestbedAddContactPage::~TestbedAddContactPage()
+{
+}
+
+bool TestbedAddContactPage::apply( Kopete::Account* a, Kopete::MetaContact* m )
+{
+ if ( validateData() )
+ {
+ bool ok = false;
+ QString type;
+ QString name;
+ if ( m_testbedAddUI->m_rbEcho->isOn() )
+ {
+ type = m_testbedAddUI->m_uniqueName->text();
+ name = QString::fromLatin1( "Echo Contact" );
+ ok = true;
+ }
+ if ( ok )
+ return a->addContact(type, /* FIXME: ? name, */ m, Kopete::Account::ChangeKABC );
+ else
+ return false;
+ }
+ return false;
+}
+
+bool TestbedAddContactPage::validateData()
+{
+ return true;
+}
+
+
+#include "testbedaddcontactpage.moc"
diff --git a/kopete/protocols/testbed/testbedaddcontactpage.h b/kopete/protocols/testbed/testbedaddcontactpage.h
new file mode 100644
index 00000000..fd7642f3
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaddcontactpage.h
@@ -0,0 +1,50 @@
+/*
+ testbedaddcontactpage.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 <addcontactpage.h>
+
+namespace Kopete { class Account; }
+namespace Kopete { class MetaContact; }
+class TestbedAddUI;
+
+/**
+ * A page in the Add Contact Wizard
+ * @author Will Stephenson
+*/
+class TestbedAddContactPage : public AddContactPage
+{
+ Q_OBJECT
+public:
+ TestbedAddContactPage( QWidget* parent = 0, const char* name = 0 );
+ ~TestbedAddContactPage();
+
+ /**
+ * 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:
+ TestbedAddUI *m_testbedAddUI;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbedaddui.ui b/kopete/protocols/testbed/testbedaddui.ui
new file mode 100644
index 00000000..c81a4d2f
--- /dev/null
+++ b/kopete/protocols/testbed/testbedaddui.ui
@@ -0,0 +1,107 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>TestbedAddUI</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>TestbedAddUI</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>375</width>
+ <height>241</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </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;Contact 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>252</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/testbed/testbedcontact.cpp b/kopete/protocols/testbed/testbedcontact.cpp
new file mode 100644
index 00000000..b5fc1c2c
--- /dev/null
+++ b/kopete/protocols/testbed/testbedcontact.cpp
@@ -0,0 +1,141 @@
+/*
+ testbedcontact.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedcontact.h"
+
+#include <kaction.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "kopeteaccount.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+
+#include "testbedaccount.h"
+#include "testbedfakeserver.h"
+#include "testbedprotocol.h"
+
+TestbedContact::TestbedContact( Kopete::Account* _account, const QString &uniqueName,
+ const TestbedContactType type, const QString &displayName, Kopete::MetaContact *parent )
+: Kopete::Contact( _account, uniqueName, parent )
+{
+ kdDebug( 14210 ) << k_funcinfo << " uniqueName: " << uniqueName << ", displayName: " << displayName << endl;
+ m_type = type;
+ // FIXME: ? setDisplayName( displayName );
+ m_msgManager = 0L;
+
+ setOnlineStatus( TestbedProtocol::protocol()->testbedOffline );
+}
+
+TestbedContact::~TestbedContact()
+{
+}
+
+bool TestbedContact::isReachable()
+{
+ return true;
+}
+
+void TestbedContact::serialize( QMap< QString, QString > &serializedData, QMap< QString, QString > & /* addressBookData */ )
+{
+ QString value;
+ switch ( m_type )
+ {
+ case Null:
+ value = "null";
+ case Echo:
+ value = "echo";
+ }
+ serializedData[ "contactType" ] = value;
+}
+
+Kopete::ChatSession* TestbedContact::manager( CanCreateFlags )
+{
+ kdDebug( 14210 ) << k_funcinfo << endl;
+ if ( m_msgManager )
+ {
+ return m_msgManager;
+ }
+ else
+ {
+ QPtrList<Kopete::Contact> contacts;
+ contacts.append(this);
+ m_msgManager = Kopete::ChatSessionManager::self()->create(account()->myself(), contacts, protocol());
+ connect(m_msgManager, SIGNAL(messageSent(Kopete::Message&, Kopete::ChatSession*)),
+ this, SLOT( sendMessage( Kopete::Message& ) ) );
+ connect(m_msgManager, SIGNAL(destroyed()), this, SLOT(slotChatSessionDestroyed()));
+ return m_msgManager;
+ }
+}
+
+
+QPtrList<KAction> *TestbedContact::customContextMenuActions() //OBSOLETE
+{
+ //FIXME!!! this function is obsolete, we should use XMLGUI instead
+ /*m_actionCollection = new KActionCollection( this, "userColl" );
+ m_actionPrefs = new KAction(i18n( "&Contact Settings" ), 0, this,
+ SLOT( showContactSettings( )), m_actionCollection, "contactSettings" );
+
+ return m_actionCollection;*/
+ return 0L;
+}
+
+void TestbedContact::showContactSettings()
+{
+ //TestbedContactSettings* p = new TestbedContactSettings( this );
+ //p->show();
+}
+
+void TestbedContact::sendMessage( Kopete::Message &message )
+{
+ kdDebug( 14210 ) << k_funcinfo << endl;
+ // convert to the what the server wants
+ // For this 'protocol', there's nothing to do
+ // send it
+ static_cast<TestbedAccount *>( account() )->server()->sendMessage(
+ message.to().first()->contactId(),
+ message.plainBody() );
+ // give it back to the manager to display
+ manager()->appendMessage( message );
+ // tell the manager it was sent successfully
+ manager()->messageSucceeded();
+}
+
+void TestbedContact::receivedMessage( const QString &message )
+{
+ // Create a Kopete::Message
+ Kopete::Message *newMessage;
+ Kopete::ContactPtrList contactList;
+ account();
+ contactList.append( account()->myself() );
+ newMessage = new Kopete::Message( this, contactList, message, Kopete::Message::Inbound );
+
+ // Add it to the manager
+ manager()->appendMessage (*newMessage);
+
+ delete newMessage;
+}
+
+void TestbedContact::slotChatSessionDestroyed()
+{
+ //FIXME: the chat window was closed? Take appropriate steps.
+ m_msgManager = 0L;
+}
+
+#include "testbedcontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/testbed/testbedcontact.h b/kopete/protocols/testbed/testbedcontact.h
new file mode 100644
index 00000000..7f7c168a
--- /dev/null
+++ b/kopete/protocols/testbed/testbedcontact.h
@@ -0,0 +1,95 @@
+/*
+ testbedcontact.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TESTBEDCONTACT_H
+#define TESTBEDCONTACT_H
+
+#include <qmap.h>
+#include "kopetecontact.h"
+#include "kopetemessage.h"
+
+class KAction;
+class KActionCollection;
+namespace Kopete { class Account; }
+namespace Kopete { class ChatSession; }
+namespace Kopete { class MetaContact; }
+
+/**
+@author Will Stephenson
+*/
+class TestbedContact : public Kopete::Contact
+{
+ Q_OBJECT
+public:
+ /**
+ * The range of possible contact types
+ */
+ enum TestbedContactType { Null, Echo };
+
+ TestbedContact( Kopete::Account* _account, const QString &uniqueName,
+ const TestbedContact::TestbedContactType type, const QString &displayName,
+ Kopete::MetaContact *parent );
+
+ ~TestbedContact();
+
+ 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( CanCreateFlags canCreate = CannotCreate );
+
+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 );
+ /**
+ * Called when an incoming message arrived
+ * This displays it in the chatwindow
+ */
+ void receivedMessage( const QString &message );
+
+protected slots:
+ /**
+ * Show the settings dialog
+ */
+ void showContactSettings();
+ /**
+ * Notify the contact that its current Kopete::ChatSession was
+ * destroyed - probably by the chatwindow being closed
+ */
+ void slotChatSessionDestroyed();
+
+protected:
+ Kopete::ChatSession* m_msgManager;
+ KActionCollection* m_actionCollection;
+ TestbedContactType m_type;
+ KAction* m_actionPrefs;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbededitaccountwidget.cpp b/kopete/protocols/testbed/testbededitaccountwidget.cpp
new file mode 100644
index 00000000..270b887a
--- /dev/null
+++ b/kopete/protocols/testbed/testbededitaccountwidget.cpp
@@ -0,0 +1,62 @@
+/*
+ testbededitaccountwidget.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbededitaccountwidget.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <kdebug.h>
+#include "kopeteaccount.h"
+#include "testbedaccountpreferences.h"
+#include "testbedaccount.h"
+#include "testbedprotocol.h"
+
+TestbedEditAccountWidget::TestbedEditAccountWidget( QWidget* parent, Kopete::Account* account)
+: QWidget( parent ), KopeteEditAccountWidget( account )
+{
+ ( new QVBoxLayout( this ) )->setAutoAdd( true );
+ kdDebug(14210) << k_funcinfo << endl;
+ m_preferencesWidget = new TestbedAccountPreferences( this );
+}
+
+TestbedEditAccountWidget::~TestbedEditAccountWidget()
+{
+}
+
+Kopete::Account* TestbedEditAccountWidget::apply()
+{
+ QString accountName;
+ if ( m_preferencesWidget->m_acctName->text().isEmpty() )
+ accountName = "Testbed Account";
+ else
+ accountName = m_preferencesWidget->m_acctName->text();
+
+ if ( account() )
+ // FIXME: ? account()->setAccountLabel(accountName);
+ account()->myself()->setProperty( Kopete::Global::Properties::self()->nickName(), accountName );
+ else
+ setAccount( new TestbedAccount( TestbedProtocol::protocol(), accountName ) );
+
+ return account();
+}
+
+bool TestbedEditAccountWidget::validateData()
+{
+ //return !( m_preferencesWidget->m_acctName->text().isEmpty() );
+ return true;
+}
+
+#include "testbededitaccountwidget.moc"
diff --git a/kopete/protocols/testbed/testbededitaccountwidget.h b/kopete/protocols/testbed/testbededitaccountwidget.h
new file mode 100644
index 00000000..9d2e6089
--- /dev/null
+++ b/kopete/protocols/testbed/testbededitaccountwidget.h
@@ -0,0 +1,52 @@
+/*
+ testbededitaccountwidget.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 TestbedAccountPreferences;
+
+/**
+ * A widget for editing this protocol's accounts
+ * @author Will Stephenson
+*/
+class TestbedEditAccountWidget : public QWidget, public KopeteEditAccountWidget
+{
+Q_OBJECT
+public:
+ TestbedEditAccountWidget( QWidget* parent, Kopete::Account* account);
+
+ ~TestbedEditAccountWidget();
+
+ /**
+ * Make an account out of the entered data
+ */
+ virtual Kopete::Account* apply();
+ /**
+ * Is the data correct?
+ */
+ virtual bool validateData();
+protected:
+ Kopete::Account *m_account;
+ TestbedAccountPreferences *m_preferencesWidget;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbedfakeserver.cpp b/kopete/protocols/testbed/testbedfakeserver.cpp
new file mode 100644
index 00000000..b1bb3e1e
--- /dev/null
+++ b/kopete/protocols/testbed/testbedfakeserver.cpp
@@ -0,0 +1,64 @@
+/*
+ testbedfakeserver.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedfakeserver.h"
+#include <qtimer.h>
+#include <kdebug.h>
+#include "testbedincomingmessage.h"
+
+
+TestbedFakeServer::TestbedFakeServer()
+{
+ m_incomingMessages.setAutoDelete( true );
+}
+
+TestbedFakeServer::~TestbedFakeServer()
+{
+}
+
+void TestbedFakeServer::sendMessage( QString contactId, QString message )
+{
+ // see what contact the message is for
+ // if it's for Echo, respond immediately
+ kdDebug( 14210 ) << k_funcinfo << "Message for: " << contactId << ", is: " << message << endl;
+ kdDebug( 14210 ) << "recipient is echo, coming back at you." << endl;
+ // put the message in a map and start a timer to tell it to deliver itself.
+ //emit messageReceived( QString::fromLatin1( "echo: " ) + message );
+ QString messageId = contactId + QString::fromLatin1(": ");
+ TestbedIncomingMessage* msg = new TestbedIncomingMessage( this, messageId + message );
+ m_incomingMessages.append( msg );
+ QTimer::singleShot( 1000, msg, SLOT( deliver() ) );
+
+ // This removes any delivered messages
+ purgeMessages();
+}
+
+void TestbedFakeServer::incomingMessage( QString message )
+{
+ emit messageReceived( message );
+}
+
+void TestbedFakeServer::purgeMessages()
+{
+ TestbedIncomingMessage* msg;
+ for ( msg = m_incomingMessages.first(); msg; msg = m_incomingMessages.next() )
+ {
+ if ( msg->delivered() )
+ m_incomingMessages.remove();
+ }
+}
+
+#include "testbedfakeserver.moc"
diff --git a/kopete/protocols/testbed/testbedfakeserver.h b/kopete/protocols/testbed/testbedfakeserver.h
new file mode 100644
index 00000000..c5daabe4
--- /dev/null
+++ b/kopete/protocols/testbed/testbedfakeserver.h
@@ -0,0 +1,66 @@
+/*
+ testbedfakeserver.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TESTBEDFAKESERVER_H
+#define TESTBEDFAKESERVER_H
+
+#include "qobject.h"
+#include <qptrlist.h>
+
+class TestbedIncomingMessage;
+
+/**
+ * This is a interface to a dummy IM server
+ * @author Will Stephenson
+ */
+class TestbedFakeServer : public QObject
+{
+ Q_OBJECT
+public:
+ TestbedFakeServer();
+ ~TestbedFakeServer();
+ /**
+ * Called to simulate sending a message to a remote contact
+ */
+ void sendMessage( QString contactId, QString message );
+
+public slots:
+ /**
+ * A message came in off the simulated wire.
+ * In reality, a message on the incoming message list
+ * connects to this slot when it's time to 'arrive'
+ */
+ void incomingMessage( QString message );
+
+signals:
+ /**
+ * Tells the account that a message arrived
+ */
+ void messageReceived( const QString &message );
+
+protected:
+ /**
+ * Utility method, just clears delivered messages from the
+ * incoming message list
+ */
+ void purgeMessages();
+ /**
+ * List of incoming messages
+ */
+ QPtrList<TestbedIncomingMessage> m_incomingMessages;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbedincomingmessage.cpp b/kopete/protocols/testbed/testbedincomingmessage.cpp
new file mode 100644
index 00000000..fbbd4c83
--- /dev/null
+++ b/kopete/protocols/testbed/testbedincomingmessage.cpp
@@ -0,0 +1,36 @@
+/*
+ testbedincomingmessage.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedincomingmessage.h"
+
+TestbedIncomingMessage::TestbedIncomingMessage( TestbedFakeServer* const server , QString message )
+{
+ m_server = server;
+ m_message = message;
+ m_delivered = false;
+}
+
+TestbedIncomingMessage::~TestbedIncomingMessage()
+{
+}
+
+void TestbedIncomingMessage::deliver()
+{
+ m_server->incomingMessage( m_message );
+ m_delivered = true;
+}
+
+#include "testbedincomingmessage.moc"
diff --git a/kopete/protocols/testbed/testbedincomingmessage.h b/kopete/protocols/testbed/testbedincomingmessage.h
new file mode 100644
index 00000000..226368d9
--- /dev/null
+++ b/kopete/protocols/testbed/testbedincomingmessage.h
@@ -0,0 +1,55 @@
+/*
+ testbedincomingmessage.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TESTBEDINCOMINGMESSAGE_H
+#define TESTBEDINCOMINGMESSAGE_H
+
+#include <qobject.h>
+#include "testbedfakeserver.h"
+
+/**
+ * A simulated incoming message, that hasn't yet arrived at the
+ * Kopete side 'client' of the simulated IM system.
+ * @author Will Stephenson
+ */
+class TestbedIncomingMessage : public QObject
+{
+Q_OBJECT
+public:
+ /**
+ * Create a new incoming message
+ * @param server The simulated Kopete side 'client' of the IM system where the message will arrive when 'delivered'
+ * @param message The simulated message
+ */
+ TestbedIncomingMessage( TestbedFakeServer* const server , QString message );
+ virtual ~TestbedIncomingMessage();
+ /**
+ * Has this message already been delivered?
+ */
+ bool delivered() { return m_delivered; }
+public slots:
+ /**
+ * 'Deliver' the message to Kopete by calling TestbedFakeServer::incomingMessage().
+ * This marks the message as delivered so it can be purged from the incoming list.
+ */
+ void deliver();
+protected:
+ QString m_message;
+ TestbedFakeServer* m_server;
+ bool m_delivered;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/testbedprotocol.cpp b/kopete/protocols/testbed/testbedprotocol.cpp
new file mode 100644
index 00000000..838a74b4
--- /dev/null
+++ b/kopete/protocols/testbed/testbedprotocol.cpp
@@ -0,0 +1,101 @@
+/*
+ testbedprotocol.cpp - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.u>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include <kgenericfactory.h>
+#include <kdebug.h>
+
+#include "kopeteaccountmanager.h"
+
+#include "testbedaccount.h"
+#include "testbedcontact.h"
+#include "testbedprotocol.h"
+#include "testbedaddcontactpage.h"
+#include "testbededitaccountwidget.h"
+
+typedef KGenericFactory<TestbedProtocol> TestbedProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_testbed, TestbedProtocolFactory( "kopete_testbed" ) )
+
+TestbedProtocol *TestbedProtocol::s_protocol = 0L;
+
+TestbedProtocol::TestbedProtocol( QObject* parent, const char *name, const QStringList &/*args*/ )
+ : Kopete::Protocol( TestbedProtocolFactory::instance(), parent, name ),
+ testbedOnline( Kopete::OnlineStatus::Online, 25, this, 0, QString::null, i18n( "Online" ), i18n( "O&nline" ) ),
+ testbedAway( Kopete::OnlineStatus::Away, 25, this, 1, "msn_away", i18n( "Away" ), i18n( "&Away" ) ),
+ testbedOffline( Kopete::OnlineStatus::Offline, 25, this, 2, QString::null, i18n( "Offline" ), i18n( "O&ffline" ) )
+
+{
+ kdDebug( 14210 ) << k_funcinfo << endl;
+
+ s_protocol = this;
+}
+
+TestbedProtocol::~TestbedProtocol()
+{
+}
+
+Kopete::Contact *TestbedProtocol::deserializeContact(
+ Kopete::MetaContact *metaContact, const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> &/* addressBookData */)
+{
+ QString contactId = serializedData[ "contactId" ];
+ QString accountId = serializedData[ "accountId" ];
+ QString displayName = serializedData[ "displayName" ];
+ QString type = serializedData[ "contactType" ];
+
+ TestbedContact::TestbedContactType tbcType;
+ if ( type == QString::fromLatin1( "echo" ) )
+ tbcType = TestbedContact::Echo;
+ if ( type == QString::fromLatin1( "null" ) )
+ tbcType = TestbedContact::Null;
+ else
+ tbcType = TestbedContact::Null;
+
+ QDict<Kopete::Account> accounts = Kopete::AccountManager::self()->accounts( this );
+
+ Kopete::Account *account = accounts[ accountId ];
+ if ( !account )
+ {
+ kdDebug(14210) << "Account doesn't exist, skipping" << endl;
+ return 0;
+ }
+
+ return new TestbedContact(account, contactId, tbcType, displayName, metaContact);
+}
+
+AddContactPage * TestbedProtocol::createAddContactWidget( QWidget *parent, Kopete::Account * /* account */ )
+{
+ kdDebug( 14210 ) << "Creating Add Contact Page" << endl;
+ return new TestbedAddContactPage( parent );
+}
+
+KopeteEditAccountWidget * TestbedProtocol::createEditAccountWidget( Kopete::Account *account, QWidget *parent )
+{
+ kdDebug(14210) << "Creating Edit Account Page" << endl;
+ return new TestbedEditAccountWidget( parent, account );
+}
+
+Kopete::Account *TestbedProtocol::createNewAccount( const QString &accountId )
+{
+ return new TestbedAccount( this, accountId );
+}
+
+TestbedProtocol *TestbedProtocol::protocol()
+{
+ return s_protocol;
+}
+
+
+
+#include "testbedprotocol.moc"
diff --git a/kopete/protocols/testbed/testbedprotocol.h b/kopete/protocols/testbed/testbedprotocol.h
new file mode 100644
index 00000000..7ee04b7d
--- /dev/null
+++ b/kopete/protocols/testbed/testbedprotocol.h
@@ -0,0 +1,74 @@
+/*
+ testbedprotocol.h - Kopete Testbed Protocol
+
+ Copyright (c) 2003 by Will Stephenson <will@stevello.free-online.co.uk>
+ Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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>
+
+
+/**
+ * Encapsulates the generic actions associated with this protocol
+ * @author Will Stephenson
+ */
+class TestbedProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+public:
+ TestbedProtocol(QObject *parent, const char *name, const QStringList &args);
+ ~TestbedProtocol();
+ /**
+ * Convert the serialised data back into a TestbedContact 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 TestbedContacts
+ */
+ 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 TestbedAccount
+ */
+ virtual Kopete::Account * createNewAccount( const QString &accountId );
+ /**
+ * Access the instance of this protocol
+ */
+ static TestbedProtocol *protocol();
+ /**
+ * Represents contacts that are Online
+ */
+ const Kopete::OnlineStatus testbedOnline;
+ /**
+ * Represents contacts that are Away
+ */
+ const Kopete::OnlineStatus testbedAway;
+ /**
+ * Represents contacts that are Offline
+ */
+ const Kopete::OnlineStatus testbedOffline;
+protected:
+ static TestbedProtocol *s_protocol;
+};
+
+#endif
diff --git a/kopete/protocols/testbed/ui/Makefile.am b/kopete/protocols/testbed/ui/Makefile.am
new file mode 100644
index 00000000..1bbf604c
--- /dev/null
+++ b/kopete/protocols/testbed/ui/Makefile.am
@@ -0,0 +1,7 @@
+INCLUDES =
+METASOURCES = AUTO
+AM_CPPFLAGS = $(KOPETE_INCLUDES) -I$(srcdir)/.. $(all_includes)
+libkopetetestbedui_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libkopetetestbedui.la
+noinst_HEADERS = testbedwebcamdialog.h
+libkopetetestbedui_la_SOURCES = testbedwebcamdialog.cpp
diff --git a/kopete/protocols/testbed/ui/testbedwebcamdialog.cpp b/kopete/protocols/testbed/ui/testbedwebcamdialog.cpp
new file mode 100644
index 00000000..22884036
--- /dev/null
+++ b/kopete/protocols/testbed/ui/testbedwebcamdialog.cpp
@@ -0,0 +1,80 @@
+/*
+ Kopete Testbed Protocol
+
+ Copyright (c) 2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "testbedwebcamdialog.h"
+#include <webcamwidget.h>
+#include "avdevice/videodevicepool.h"
+
+#include <qframe.h>
+#include <qobject.h>
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+TestbedWebcamDialog::TestbedWebcamDialog( const QString &contactId, QWidget * parent, const char * name )
+: KDialogBase( KDialogBase::Plain, Qt::WDestructiveClose, parent, name, false, i18n( "Webcam for %1" ).arg( contactId ),
+ KDialogBase::Close, KDialogBase::Close, true /*seperator*/ )
+{
+ setInitialSize( QSize(320,290), false );
+
+ setEscapeButton( KDialogBase::Close );
+// QObject::connect( this, SIGNAL( closeClicked() ), this, SIGNAL( closingWebcamDialog() ) );
+
+ QWidget *page = plainPage();
+ setMainWidget(page);
+
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ mImageContainer = new Kopete::WebcamWidget( page );
+ mImageContainer->setMinimumSize(320,240);
+ mImageContainer->setText( i18n( "No webcam image received" ) );
+ mImageContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ topLayout->add( mImageContainer );
+
+ show();
+
+ mVideoDevicePool = Kopete::AV::VideoDevicePool::self();
+ mVideoDevicePool->open();
+ mVideoDevicePool->setSize(320, 240);
+ mVideoDevicePool->startCapturing();
+ mVideoDevicePool->getFrame();
+ mVideoDevicePool->getImage(&mImage);
+kdDebug() << "Just captured 1st frame" << endl;
+
+ mPixmap=QPixmap(320,240,-1, QPixmap::DefaultOptim);
+ if (mPixmap.convertFromImage(mImage,0) == true)
+ mImageContainer->updatePixmap(mPixmap);
+ connect(&qtimer, SIGNAL(timeout()), this, SLOT(slotUpdateImage()) );
+ qtimer.start(0,FALSE);
+}
+
+TestbedWebcamDialog::~ TestbedWebcamDialog( )
+{
+ mVideoDevicePool->stopCapturing();
+ mVideoDevicePool->close();
+}
+
+void TestbedWebcamDialog::slotUpdateImage()
+{
+ mVideoDevicePool->getFrame();
+ mVideoDevicePool->getImage(&mImage);
+ mImageContainer->updatePixmap( QPixmap( mImage ) );
+}
+
+
+#include "testbedwebcamdialog.moc"
diff --git a/kopete/protocols/testbed/ui/testbedwebcamdialog.h b/kopete/protocols/testbed/ui/testbedwebcamdialog.h
new file mode 100644
index 00000000..59f43e68
--- /dev/null
+++ b/kopete/protocols/testbed/ui/testbedwebcamdialog.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Testbed Protocol
+
+ Copyright (c) 2006 by Cláudio da Silveira Pinheiro <taupter@gmail.com>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TESTBEDWEBCAMDIALOG_H
+#define TESTBEDWEBCAMDIALOG_H
+
+#include <qstring.h>
+#include <qimage.h>
+#include <qtimer.h>
+#include <qpixmap.h>
+#include <kdialogbase.h>
+
+/**
+ @author Kopete Developers <kopete-devel@kde.org>
+*/
+class QPixmap;
+class QWidget;
+class TestbedContact;
+
+namespace Kopete {
+ namespace AV {
+ class VideoDevicePool;
+ }
+ class WebcamWidget;
+}
+
+class TestbedWebcamDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ TestbedWebcamDialog( const QString &, QWidget* parent = 0, const char* name = 0 );
+ ~TestbedWebcamDialog();
+
+public slots:
+ void slotUpdateImage();
+//signals:
+// void closingWebcamDialog();
+
+private:
+ Kopete::WebcamWidget *mImageContainer;
+ QImage mImage;
+ QTimer qtimer;
+ QPixmap mPixmap;
+ Kopete::AV::VideoDevicePool *mVideoDevicePool;
+};
+
+#endif
diff --git a/kopete/protocols/winpopup/Makefile.am b/kopete/protocols/winpopup/Makefile.am
new file mode 100644
index 00000000..24d84fe4
--- /dev/null
+++ b/kopete/protocols/winpopup/Makefile.am
@@ -0,0 +1,22 @@
+METASOURCES = AUTO
+SUBDIRS = ui icons libwinpopup
+AM_CPPFLAGS = -I$(srcdir)/ui \
+ -I./ui \
+ -I$(srcdir)/libwinpopup \
+ $(KOPETE_INCLUDES) \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_wp.la
+
+noinst_HEADERS = wpprotocol.h wpeditaccount.h wpaccount.h wpuserinfo.h wpcontact.h wpaddcontact.h
+
+kopete_wp_la_SOURCES = wpprotocol.cpp wpcontact.cpp wpaddcontact.cpp wpeditaccount.cpp wpaccount.cpp wpuserinfo.cpp
+kopete_wp_la_LDFLAGS = -module -no-undefined $(KDE_PLUGIN) $(all_libraries)
+kopete_wp_la_LIBADD = ../../libkopete/libkopete.la ./ui/libkopetewpui.la \
+ ./libwinpopup/libwinpopup.la $(LIB_KIO)
+
+service_DATA = kopete_wp.desktop
+servicedir = $(kde_servicesdir)
+
+bin_SCRIPTS = winpopup-send.sh winpopup-install.sh
+
diff --git a/kopete/protocols/winpopup/icons/Makefile.am b/kopete/protocols/winpopup/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/protocols/winpopup/icons/cr128-app-wp_protocol.png b/kopete/protocols/winpopup/icons/cr128-app-wp_protocol.png
new file mode 100644
index 00000000..5d1e6003
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr128-app-wp_protocol.png
Binary files differ
diff --git a/kopete/protocols/winpopup/icons/cr16-action-wp_away.png b/kopete/protocols/winpopup/icons/cr16-action-wp_away.png
new file mode 100644
index 00000000..a2f4d5a6
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr16-action-wp_away.png
Binary files differ
diff --git a/kopete/protocols/winpopup/icons/cr16-app-wp_protocol.png b/kopete/protocols/winpopup/icons/cr16-app-wp_protocol.png
new file mode 100644
index 00000000..3387da90
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr16-app-wp_protocol.png
Binary files differ
diff --git a/kopete/protocols/winpopup/icons/cr32-app-wp_protocol.png b/kopete/protocols/winpopup/icons/cr32-app-wp_protocol.png
new file mode 100644
index 00000000..476798a0
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr32-app-wp_protocol.png
Binary files differ
diff --git a/kopete/protocols/winpopup/icons/cr48-app-wp_protocol.png b/kopete/protocols/winpopup/icons/cr48-app-wp_protocol.png
new file mode 100644
index 00000000..9fa4d6c4
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr48-app-wp_protocol.png
Binary files differ
diff --git a/kopete/protocols/winpopup/icons/cr64-app-wp_protocol.png b/kopete/protocols/winpopup/icons/cr64-app-wp_protocol.png
new file mode 100644
index 00000000..da4998ed
--- /dev/null
+++ b/kopete/protocols/winpopup/icons/cr64-app-wp_protocol.png
Binary files differ
diff --git a/kopete/protocols/winpopup/kopete_wp.desktop b/kopete/protocols/winpopup/kopete_wp.desktop
new file mode 100644
index 00000000..9280e14e
--- /dev/null
+++ b/kopete/protocols/winpopup/kopete_wp.desktop
@@ -0,0 +1,85 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=wp_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_wp
+X-Kopete-Messaging-Protocol=messaging/winpopup
+X-KDE-PluginInfo-Author=Gav Wood
+X-KDE-PluginInfo-Email=gav@kde.org
+X-KDE-PluginInfo-Name=kopete_wp
+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=WinPopup
+Name[ar]=منبثقWin
+Name[bn]=উইন পপ-আপ
+Name[ca]=Finestres emergents
+Name[eu]=WinPopup plugina
+Name[hi]=विन-पॉपअप
+Name[lt]=Windows iÅ¡Å¡okanÄios žinutÄ—s
+Name[ne]=विन पपअप
+Name[nn]=Vindaugsmeldingar
+Name[pt_BR]=Janela de Contexto
+Name[zh_CN]=WinPoup
+Comment=Protocol to send Windows WinPopup messages
+Comment[ar]=البرتوكول سيصدر رسائل Windows المنبثقة
+Comment[be]=Пратакол Windows WinPopup
+Comment[bg]=Протокол за изпращане на ÑÑŠÐ¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Windows WinPopup
+Comment[bn]=উইনà§à¦¡à§‹à¦œ উইন পপ-আপ বারà§à¦¤à¦¾ পাঠাতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad evit kas kemennadoù Windows WinPopup
+Comment[bs]=Protokol za slanje Windows WinPopup poruka
+Comment[ca]=Protocol per a enviar missatges de finestres emergents de Windows
+Comment[cs]=Protokol k odesílání WinPopup zpráv
+Comment[cy]=Protocol i anfon negeseuon WinPopup
+Comment[da]=Protokol til at sende Windows WinPopup-beskeder
+Comment[de]=Protokoll zur Versendung von Windows WinPopup-Nachrichten
+Comment[el]=ΠÏωτόκολλο για αποστολή μηνυμάτων Windows WinPopup
+Comment[es]=Protocolo para enviar un mensaje emergente de windows
+Comment[et]=Protokoll Windowsi WinPopup sõnumite saatmiseks
+Comment[eu]=Windows WinPopup mezuak bidaltzeko protokoloa
+Comment[fa]=قرارداد ارسال پیامهای WinPopup ویندوز
+Comment[fi]=Yhteyskäytäntö Windows WinPopup-viestien lähettämiseen
+Comment[fr]=Protocole pour envoyer des messages WinPopup
+Comment[ga]=Prótacal chun teachtaireachtaí Windows WinPopup a sheoladh
+Comment[gl]=Protocolo para enviar mensaxes a máquinas Windows mediante o protocolo Winpopup
+Comment[he]=תוסף לשליחת מסרי WinPopup
+Comment[hi]=विंडोज़ विन-पॉपअप मैसेंजर भेजने का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za slanje Windows WinPopup poruka
+Comment[hu]=Protokoll Windows felbukkanó üzenetek küldéséhez
+Comment[is]=Samskiptamáti til að senda Windows WinPopup skeyti
+Comment[it]=Protocollo per inviare messaggi Windows WinPopup
+Comment[ja]=Windows ã® WinPopup メッセージをé€ã‚‹ãƒ—ロトコル
+Comment[ka]=Windows WinPopup შეტყáƒáƒ‘ინებების გáƒáƒ’ზáƒáƒ•áƒœáƒ˜áƒ¡ áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Windows WinPopup хабарларды жіберу протоколы
+Comment[km]=ពិធីការ​ដើម្បី​ផ្ញើ​សារ WinPopup របស់​វ៉ីនដូ
+Comment[lt]=Protokolas Windows iÅ¡Å¡okanÄių žinuÄių siuntimui
+Comment[mk]=Протокол за иÑпраќање на пораки Ñо Windows WinPopup
+Comment[nb]=Protokoll for å sende Windows WinPopup -meldinger
+Comment[nds]=Protokoll för't Sennen vun Windows-WinPopup-Narichten
+Comment[ne]=विनà¥à¤¡à¥‹à¤œ विन पपअप सनà¥à¤¦à¥‡à¤¶ पठाउनà¥à¤ªà¤°à¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor verzenden van WinPopup-berichten
+Comment[nn]=Protokoll for å senda Windows-vindaugsmeldingar
+Comment[pl]=Protokół wysyłania komunikatów Windows WinPopup
+Comment[pt]=Um protocolo para enviar mensagens de WinPopup do Windows
+Comment[pt_BR]=Protocolo para o envio de mensagens de janela do Windows
+Comment[ro]=Protocol de trimis mesaje Windows WinPopup
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸ Ñообщений Windows WinPopup
+Comment[sk]=Protokol pre posielanie správ Windows WinPopup
+Comment[sl]=Protokol za poÅ¡iljanje sporoÄil za Windows WinPopup
+Comment[sr]=Протокол за Ñлање Windows WinPopup порука
+Comment[sr@Latn]=Protokol za slanje Windows WinPopup poruka
+Comment[sv]=Protokoll för att skicka Windows WinPopup-meddelanden
+Comment[ta]=விணà¯à®Ÿà¯‹à®¸à¯ தோனà¯à®±à¯à®®à¯ சாளர செயà¯à®¤à®¿à®¯à¯ˆ அனà¯à®ªà¯à®ªà¯à®µà®¤à®±à¯à®•à®¾à®© நெறிமà¯à®±à¯ˆ
+Comment[tg]=Қарордод барои фириÑтодани Windows WinPopup пайёмҳо
+Comment[tr]=Windows WinPopup mesajları gönderme iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð²Ñ–Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ WinPopup
+Comment[uz]=Windows WinPopup xabarlarni joʻnatish uchun protokol
+Comment[uz@cyrillic]=Windows WinPopup хабарларни жўнатиш учун протокол
+Comment[wa]=Protocole po les messaedjes WinPopup di Windows
+Comment[zh_CN]=å‘é€ Windows WinPopup ä¿¡æ¯çš„åè®®
+Comment[zh_HK]=ç”¨ä¾†ç™¼é€ Windows WinPopup 訊æ¯çš„通訊å”定
+Comment[zh_TW]=é€å‡º WinPopup 訊æ¯çš„å”定
diff --git a/kopete/protocols/winpopup/libwinpopup/Makefile.am b/kopete/protocols/winpopup/libwinpopup/Makefile.am
new file mode 100644
index 00000000..e9c5836e
--- /dev/null
+++ b/kopete/protocols/winpopup/libwinpopup/Makefile.am
@@ -0,0 +1,9 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libwinpopup.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/..\
+ $(all_includes)
+
+libwinpopup_la_SOURCES = libwinpopup.cpp
+
diff --git a/kopete/protocols/winpopup/libwinpopup/libwinpopup.cpp b/kopete/protocols/winpopup/libwinpopup/libwinpopup.cpp
new file mode 100644
index 00000000..d26e461c
--- /dev/null
+++ b/kopete/protocols/winpopup/libwinpopup/libwinpopup.cpp
@@ -0,0 +1,363 @@
+/***************************************************************************
+ libwinpopup.cpp - WP Library
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// QT Includes
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+
+// KDE Includes
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kdirlister.h>
+
+// Kopete Includes
+#include "kopeteuiglobal.h"
+
+// Local Includes
+#include "libwinpopup.h"
+
+WinPopupLib::WinPopupLib(const QString &smbClient,int groupFreq)
+ : smbClientBin(smbClient), groupCheckFreq(groupFreq)
+{
+ connect(&updateGroupDataTimer, SIGNAL(timeout()), this, SLOT(slotUpdateGroupData()));
+
+ updateGroupDataTimer.start(1, true);
+ QTimer::singleShot(1, this, SLOT(slotStartDirLister()));
+}
+
+WinPopupLib::~WinPopupLib()
+{
+}
+
+void WinPopupLib::slotStartDirLister()
+{
+ if (checkMessageDir()) {
+ dirLister = new KDirLister();
+ dirLister->setAutoUpdate(true);
+ connect(dirLister, SIGNAL(newItems(const KFileItemList &)), this, SLOT(slotNewMessages(const KFileItemList &)));
+ connect(dirLister, SIGNAL(completed()), this, SLOT(slotListCompleted()));
+ dirLister->openURL(KURL::fromPathOrURL(WP_POPUP_DIR));
+ }
+}
+
+/**
+ * return the group list
+ */
+const QStringList WinPopupLib::getGroups()
+{
+ QStringList ret;
+ QMap<QString, WorkGroup>::ConstIterator end = theGroups.end();
+ for(QMap<QString, WorkGroup>::ConstIterator i = theGroups.begin(); i != end; i++)
+ ret += i.key();
+
+ return ret;
+}
+
+/**
+ * return the host list
+ */
+const QStringList WinPopupLib::getHosts(const QString &Group)
+{
+ return theGroups[Group].Hosts();
+}
+
+/**
+ * return if a host is in the host list
+ */
+bool WinPopupLib::checkHost(const QString &Name)
+{
+// kdDebug() << "WP checkHost: " << Name << endl;
+ bool ret = false;
+
+ QMap<QString, WorkGroup>::Iterator end = theGroups.end();
+ for(QMap<QString, WorkGroup>::Iterator i = theGroups.begin(); i != end && !ret; i++) {
+ if ((*i).Hosts().contains(Name.upper())) {
+ ret = true;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+bool WinPopupLib::checkMessageDir()
+{
+ QDir dir(WP_POPUP_DIR);
+ if (! dir.exists()) {
+ int tmpYesNo = KMessageBox::warningYesNo(Kopete::UI::Global::mainWidget(),
+ i18n("Working directory %1 does not exist.\n"
+ "If you have not configured anything yet (samba) please see\n"
+ "Install Into Samba (Configure... -> Account -> Edit) information\n"
+ "on how to do this.\n"
+ "Should the directory be created? (May need root password)").arg(WP_POPUP_DIR),
+ QString::fromLatin1("Winpopup"), i18n("Create Directory"), i18n("Do Not Create"));
+ if (tmpYesNo == KMessageBox::Yes) {
+ QStringList kdesuArgs = QStringList(QString("-c mkdir -p -m 0777 " + WP_POPUP_DIR));
+ if (KApplication::kdeinitExecWait("kdesu", kdesuArgs) == 0) return true;
+ }
+ } else {
+ KFileItem tmpFileItem = KFileItem(KFileItem::Unknown, KFileItem::Unknown, KURL::fromPathOrURL(WP_POPUP_DIR));
+ mode_t tmpPerms = tmpFileItem.permissions();
+
+ if (tmpPerms != 0777) {
+
+ kdDebug(14170) << "Perms not ok!" << endl;
+
+ int tmpYesNo = KMessageBox::warningYesNo(Kopete::UI::Global::mainWidget(),
+ i18n("Permissions of the working directory "
+ "%1 are wrong!\n"
+ "You will not receive messages if you say no.\n"
+ "You can also correct it manually (chmod 0777 %1) and restart kopete.\n"
+ "Fix? (May need root password)").arg(WP_POPUP_DIR),
+ QString::fromLatin1("Winpopup"), i18n("Fix"), i18n("Do Not Fix"));
+ if (tmpYesNo == KMessageBox::Yes) {
+ QStringList kdesuArgs = QStringList(QString("-c chmod 0777 " + WP_POPUP_DIR));
+ if (KApplication::kdeinitExecWait("kdesu", kdesuArgs) == 0) return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * read the groups and their hosts
+ */
+void WinPopupLib::slotUpdateGroupData()
+{
+ passedInitialHost = false;
+ todo.clear();
+ currentGroupsMap.clear();
+ currentHost = QString::fromLatin1("LOCALHOST");
+ startReadProcess(currentHost);
+}
+
+void WinPopupLib::startReadProcess(const QString &Host)
+{
+ currentHosts.clear();
+ currentGroups.clear();
+ currentGroup = QString();
+
+ // for Samba 3
+ KProcIO *reader = new KProcIO;
+ *reader << smbClientBin << "-N" << "-E" << "-g" << "-L" << Host << "-";
+
+ connect(reader, SIGNAL(readReady(KProcIO *)), this, SLOT(slotReadProcessReady(KProcIO *)));
+ connect(reader, SIGNAL(processExited(KProcess *)), this, SLOT(slotReadProcessExited(KProcess *)));
+
+ if (!reader->start(KProcess::NotifyOnExit, true)) {
+ // still to come
+ kdDebug(14170) << "ReadProcess not started!" << endl;
+ }
+}
+
+void WinPopupLib::slotReadProcessReady(KProcIO *r)
+{
+ QString tmpLine = QString::null;
+ QRegExp group("^Workgroup\\|(.*)\\|(.*)$"), host("^Server\\|(.*)\\|(.*)$"),
+ info("^Domain=\\[([^\\]]+)\\] OS=\\[([^\\]]+)\\] Server=\\[([^\\]]+)\\]"),
+ error("Connection.*failed");
+
+ while (r->readln(tmpLine) > -1) {
+ if (info.search(tmpLine) != -1) currentGroup = info.cap(1);
+ if (host.search(tmpLine) != -1) currentHosts += host.cap(1);
+ if (group.search(tmpLine) != -1) currentGroups[group.cap(1)] = group.cap(2);
+ if (error.search(tmpLine) != -1) {
+ kdDebug(14170) << "Connection to " << currentHost << " failed!" << endl;
+ if (currentHost == QString::fromLatin1("LOCALHOST")) currentHost = QString::fromLatin1("failed"); // to be sure
+ }
+ }
+}
+
+void WinPopupLib::slotReadProcessExited(KProcess *r)
+{
+ delete r;
+
+ // Drop the first cycle - it's only the initial search host,
+ // the next round are the real masters. GF
+
+ if (passedInitialHost) {
+
+ // move currentHost from todo to done
+ todo.remove(currentHost);
+ done += currentHost;
+
+ if (!currentGroups.isEmpty()) {
+ QMap<QString, WorkGroup> newGroups;
+ //loop through the read groups and check for new ones
+ QMap<QString, QString>::ConstIterator end = currentGroups.end();
+ for (QMap<QString, QString>::ConstIterator i = currentGroups.begin(); i != end; i++) {
+ QString groupMaster = i.data();
+ if (!done.contains(groupMaster)) todo += groupMaster;
+ }
+ }
+
+ if (!currentGroup.isEmpty() && !currentHosts.isEmpty()) {
+ // create a workgroup object and put the hosts in
+ WorkGroup nWG;
+ nWG.addHosts(currentHosts);
+
+ currentGroupsMap.insert(currentGroup, nWG, true);
+ }
+
+ } else {
+ passedInitialHost = true;
+ if (!currentGroups.isEmpty()) {
+ QMap<QString, QString>::ConstIterator end = currentGroups.end();
+ for (QMap<QString, QString>::ConstIterator i = currentGroups.begin(); i != end; i++) {
+ QString groupMaster = i.data();
+ todo += groupMaster;
+ }
+ } else {
+ if (currentHost == QString::fromLatin1("failed"))
+ KMessageBox::error(Kopete::UI::Global::mainWidget(),
+ i18n("Connection to localhost failed!\n"
+ "Is your samba server running?"),
+ QString::fromLatin1("Winpopup"));
+ }
+ }
+
+ // maybe restart cycle
+ if (todo.count()) {
+ currentHost = todo[0];
+ startReadProcess(currentHost);
+ } else {
+ theGroups = currentGroupsMap;
+ updateGroupDataTimer.start(groupCheckFreq * 1000, true);
+ }
+}
+
+void WinPopupLib::slotListCompleted()
+{
+ /// only to check received messages during start up, then we use newItems. GF
+ disconnect(dirLister, SIGNAL(completed()), this, SLOT(slotListCompleted()));
+ readMessages(dirLister->items());
+}
+
+void WinPopupLib::slotNewMessages(const KFileItemList &items)
+{
+ readMessages(items);
+}
+
+/**
+ * read new arrived messages
+ */
+void WinPopupLib::readMessages(const KFileItemList &items)
+{
+ QPtrListIterator<KFileItem> it(items);
+ KFileItem *tmpItem;
+ while ((tmpItem = it.current()) != 0) {
+ if (tmpItem->isFile()) {
+ QFile messageFile(tmpItem->url().path());
+
+ if (messageFile.open(IO_ReadOnly)) {
+ QTextStream stream(&messageFile);
+ QString sender;
+ QDateTime time;
+ QString text;
+
+ // first line is sender, can this really be empty? GF
+ sender = stream.readLine();
+ sender = sender.upper();
+
+ // second line is time
+ QString tmpTime = stream.readLine();
+ time = QDateTime::fromString(tmpTime, Qt::ISODate);
+
+ while (!stream.atEnd()) {
+ text.append(stream.readLine());
+ text.append('\n');
+ }
+
+ // remove trailing CR
+ text = text.stripWhiteSpace();
+
+ messageFile.close();
+
+ // delete file
+ if (!messageFile.remove()) {
+ // QFile::remove() seems to be very persistent, it removes even files with 0444 owned by root
+ // if the directory permissions are 0777 - so this is just for safety. GF
+ kdDebug(14170) << "Message file not removed - how that?" << endl;
+ int tmpYesNo = KMessageBox::warningYesNo(Kopete::UI::Global::mainWidget(),
+ i18n("A message file could not be removed; "
+ "maybe the permissions are wrong.\n"
+ "Fix? (May need root password)"),
+ QString::fromLatin1("Winpopup"), i18n("Fix"), i18n("Do Not Fix"));
+ if (tmpYesNo == KMessageBox::Yes) {
+ QStringList kdesuArgs = QStringList(QString("-c chmod 0666 " + tmpItem->url().path()));
+ if (KApplication::kdeinitExecWait("kdesu", kdesuArgs) == 0) {
+ if (!messageFile.remove())
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("Still cannot remove it; please fix manually."));
+ }
+ }
+ }
+ if (!sender.isEmpty() && time.isValid())
+ emit signalNewMessage(text, time, sender);
+ else
+ kdDebug(14170) << "Received invalid message!" << endl;
+ }
+ } // isFile
+ ++it;
+ } // while
+}
+
+/**
+ * send a message
+ */
+void WinPopupLib::sendMessage(const QString &Body, const QString &Destination)
+{
+ KProcess *sender = new KProcess(this);
+ *sender << smbClientBin << "-M" << Destination;
+ *sender << "-N" << "-";
+
+ connect(sender, SIGNAL(processExited(KProcess *)), this, SLOT(slotSendProcessExited(KProcess *)));
+
+ if (sender->start(KProcess::NotifyOnExit, KProcess::Stdin)) {
+ sender->writeStdin(Body.local8Bit(), Body.local8Bit().length());
+ if (!sender->closeStdin()) {
+ delete sender;
+ }
+ } else {
+ delete sender;
+ }
+}
+
+void WinPopupLib::slotSendProcessExited(KProcess *p)
+{
+// emit sendJobDone(p->pid());
+ delete p;
+}
+
+void WinPopupLib::settingsChanged(const QString &smbClient, int groupFreq)
+{
+ smbClientBin = smbClient;
+ groupCheckFreq = groupFreq;
+
+ if (updateGroupDataTimer.isActive()) updateGroupDataTimer.changeInterval(groupCheckFreq * 1000);
+}
+
+#include "libwinpopup.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/libwinpopup/libwinpopup.h b/kopete/protocols/winpopup/libwinpopup/libwinpopup.h
new file mode 100644
index 00000000..77f8b8a6
--- /dev/null
+++ b/kopete/protocols/winpopup/libwinpopup/libwinpopup.h
@@ -0,0 +1,92 @@
+/***************************************************************************
+ libwinpopup.h - Base class for the WinPopup protocol
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef LIBWINPOPUP_H
+#define LIBWINPOPUP_H
+
+//QT includes
+#include <qobject.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qtimer.h>
+#include <qdatetime.h>
+
+// KDE Includes
+#include <kprocio.h>
+#include <kfileitem.h>
+
+const QString WP_POPUP_DIR = QString::fromLatin1("/var/lib/winpopup");
+
+class KDirLister;
+
+typedef QMap<QString, QString> stringMap;
+
+class WorkGroup
+{
+ QStringList groupHosts;
+
+public:
+ const QStringList &Hosts() { return groupHosts; }
+ void addHosts(const QStringList &newHosts) { groupHosts = newHosts; }
+};
+
+class WinPopupLib : public QObject
+{
+ Q_OBJECT
+
+public:
+ WinPopupLib(const QString &smbClient,int groupFreq);
+ ~WinPopupLib();
+
+ const QStringList getGroups();
+ const QStringList getHosts(const QString &Group);
+ bool checkHost(const QString &Name);
+ void settingsChanged(const QString &smbClient, int groupFreq);
+ void sendMessage(const QString &Body, const QString &Destination);
+
+private:
+ bool passedInitialHost;
+ QMap<QString, WorkGroup> theGroups, currentGroupsMap;
+ QString currentGroup, currentHost;
+ QStringList todo, done, currentHosts;
+ stringMap currentGroups;
+ QTimer updateGroupDataTimer;
+ QString smbClientBin;
+ int groupCheckFreq;
+ KDirLister *dirLister;
+
+ void readMessages(const KFileItemList &items);
+ bool checkMessageDir();
+
+private slots:
+ void slotUpdateGroupData();
+ void startReadProcess(const QString &Host);
+ void slotReadProcessReady(KProcIO *r);
+ void slotReadProcessExited(KProcess *r);
+ void slotSendProcessExited(KProcess *p);
+ void slotStartDirLister();
+ void slotListCompleted();
+ void slotNewMessages(const KFileItemList &items);
+
+signals:
+ void signalNewMessage(const QString &, const QDateTime &, const QString &);
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/ui/Makefile.am b/kopete/protocols/winpopup/ui/Makefile.am
new file mode 100644
index 00000000..11d8a7bc
--- /dev/null
+++ b/kopete/protocols/winpopup/ui/Makefile.am
@@ -0,0 +1,10 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkopetewpui.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+
+libkopetewpui_la_SOURCES = wpaddcontactbase.ui wpeditaccountbase.ui empty.cpp wpuserinfowidget.ui
+EXTRA_DIST = wpaddcontactbase.ui wpeditaccountbase.ui
diff --git a/kopete/protocols/winpopup/ui/empty.cpp b/kopete/protocols/winpopup/ui/empty.cpp
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/kopete/protocols/winpopup/ui/empty.cpp
@@ -0,0 +1 @@
+
diff --git a/kopete/protocols/winpopup/ui/wpaddcontactbase.ui b/kopete/protocols/winpopup/ui/wpaddcontactbase.ui
new file mode 100644
index 00000000..21286d54
--- /dev/null
+++ b/kopete/protocols/winpopup/ui/wpaddcontactbase.ui
@@ -0,0 +1,190 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>WPAddContactBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WPAddContactBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>396</width>
+ <height>342</height>
+ </rect>
+ </property>
+ <property name="font">
+ <font>
+ </font>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout59</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout57</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Com&amp;puter hostname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mHostName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages to.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Workgroup/domain:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mHostGroup</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The workgroup or domain the computer is on that you would like to use to send WinPopup messages to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The workgroup or domain the computer is on that you would like to use to send WinPopup messages to.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout58</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>mHostName</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages to.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>mHostGroup</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The workgroup or domain the computer is on that you would like to use to send WinPopup messages to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The workgroup or domain the computer is on that you would like to use to send WinPopup messages to.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>mRefresh</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Refresh</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Refresh the list of available workgroups &amp; domains on the Windows network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Refresh the list of available workgroups &amp; domains on the Windows network.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<tabstops>
+ <tabstop>mHostName</tabstop>
+ <tabstop>mHostGroup</tabstop>
+ <tabstop>mRefresh</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/winpopup/ui/wpeditaccountbase.ui b/kopete/protocols/winpopup/ui/wpeditaccountbase.ui
new file mode 100644
index 00000000..464c426d
--- /dev/null
+++ b/kopete/protocols/winpopup/ui/wpeditaccountbase.ui
@@ -0,0 +1,358 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WPEditAccountBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WPEditAccountBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>398</width>
+ <height>445</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - WinPopup</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>labelStatusMessage</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QTabWidget" row="0" column="0">
+ <property name="name">
+ <cstring>tabWidget10</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Basi&amp;c Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox51</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>layout40</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>label1</cstring>
+ </property>
+ <property name="text">
+ <string>Hos&amp;tname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mHostName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages as.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages as. Note that this does not have to be the actual hostname of the machine to send messages, but it does to receive them.</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mHostName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages as.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname you would like to use to send WinPopup messages as. Note that this does not have to be the actual hostname of the machine to send messages, but it does to receive them.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>doInstallSamba</cstring>
+ </property>
+ <property name="text">
+ <string>I&amp;nstall Into Samba</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Install support into Samba to enable this service.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Install support into Samba to enable this service.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox22</cstring>
+ </property>
+ <property name="title">
+ <string>Information</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>To receive WinPopup messages sent from other machines, the hostname above must be set to this machine's hostname.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>The samba server must be configured and running.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>"Install into Samba" is a simple method to create the directory for the temporary message files and configure your samba server.&lt;br&gt;
+However, the recommended way is to ask your administrator to create this directory ('mkdir -p -m 0777 /var/lib/winpopup') and add
+'message command = _PATH_TO_/winpopup-send.sh %s %m %t &amp;' (substitute _PATH_TO_ by the real path) to your smb.conf [global]-section.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ </vbox>
+ </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>21</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>S&amp;ystem</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>135</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;These options apply to all WinPopup accounts.&lt;/i&gt;</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Protocol Preferences</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Host check frequency:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Path to 'smbclient' executable:</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>mHostCheckFreq</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maxValue">
+ <number>3600</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>second(s)</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>mSmbcPath</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>doInstallSamba</sender>
+ <signal>clicked()</signal>
+ <receiver>WPEditAccountBase</receiver>
+ <slot>installSamba()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget10</tabstop>
+ <tabstop>mHostName</tabstop>
+ <tabstop>doInstallSamba</tabstop>
+</tabstops>
+<slots>
+ <slot>installSamba()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/winpopup/ui/wpuserinfowidget.ui b/kopete/protocols/winpopup/ui/wpuserinfowidget.ui
new file mode 100644
index 00000000..a899e3ca
--- /dev/null
+++ b/kopete/protocols/winpopup/ui/wpuserinfowidget.ui
@@ -0,0 +1,219 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WPUserInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>WPUserInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>402</width>
+ <height>175</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblComputerName</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Computer name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sComputerName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname of the computer for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname of the computer for this contact.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Comment:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Workgroup/domain:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sWorkgroup</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The workgroup or domain the contact's computer is on.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The workgroup or domain the contact's computer is on.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Operating s&amp;ystem:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sOS</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The operating system the contact's computer is running.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The operating system the contact's computer is running.</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver software:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sServer</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The software the contact's computer is running.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The software the contact's computer is running.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sComputerName</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The hostname of the computer for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The hostname of the computer for this contact.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sComment</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The comment of the computer for this contact.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The comment of the computer for this contact.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sWorkgroup</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The workgroup or domain the contact's computer is on.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The workgroup or domain the contact's computer is on.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sOS</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The operating system the contact's computer is running.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The operating system the contact's computer is running.</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>sServer</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The software the contact's computer is running.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The software the contact's computer is running.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>sComputerName</tabstop>
+ <tabstop>sWorkgroup</tabstop>
+ <tabstop>sOS</tabstop>
+ <tabstop>sServer</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="0"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/kopete/protocols/winpopup/winpopup-install.sh b/kopete/protocols/winpopup/winpopup-install.sh
new file mode 100755
index 00000000..3106b064
--- /dev/null
+++ b/kopete/protocols/winpopup/winpopup-install.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin
+
+# Grab the full path to the smb.conf file
+i=`find /etc -name smb.conf`
+
+# Create new smb.conf file with updated message command line
+echo "[global]" > ~/smb.conf.new
+echo " message command = $1 %s %m %t &" >> ~/smb.conf.new
+cat $i | grep -v "message command = " | grep -v "\[global\]" >> ~/smb.conf.new
+
+# Backup the old file
+mv -f $i "$i.old"
+
+# Move new file into place and reset permissions
+mv -f ~/smb.conf.new $i
+chown root:root $i
+chmod 644 $i
+
+# Create a winpopup directory somewhere "safe"
+#rm -rf /var/lib/winpopup --- a bit strong?
+if [ ! -d /var/lib/winpopup ]; then
+ mkdir -p /var/lib/winpopup
+fi
+
+chmod 0777 /var/lib/winpopup
+
+# This is to help if somebody grades up from the old behavior
+if [ -n "`ls -A /var/lib/winpopup/`" ]; then
+ chmod 666 /var/lib/winpopup/*
+fi
+
+rm -f /var/lib/winpopup/message
+
+# Force Samba to reread configuration
+killall -HUP smbd
diff --git a/kopete/protocols/winpopup/winpopup-send.sh b/kopete/protocols/winpopup/winpopup-send.sh
new file mode 100755
index 00000000..9a80b20b
--- /dev/null
+++ b/kopete/protocols/winpopup/winpopup-send.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+PATH=/bin:/usr/bin/:/usr/local/bin
+
+# Check input
+[ -z "$1" -o -z "$2" ] && exit 1
+
+# Check if file is indeed a file and readable
+[ ! -f "$1" -o ! -r "$1" ] && exit 1
+
+KOPETE_RUNNING=x`ps -A|grep -e "kopete$"`
+
+if [ "$KOPETE_RUNNING" = "x" ]; then
+
+ if [ -z "$3" ]; then
+ THIS_SERVER=`uname -n`
+ else
+ THIS_SERVER="$3"
+ fi
+
+ if [ "$2" != "$THIS_SERVER" ]; then
+ echo -e "Kopete is currently not running.\nYour message was not delivered!" \
+ | smbclient -N -M $2
+ fi
+
+else
+
+ # Create a unique filename
+ filename="/var/lib/winpopup/`date +%s_%N`"
+
+ # the time...
+ TIME=`date --iso-8601=seconds`
+
+ # the message
+ MESSAGE=`cat "$1"`
+
+ # Put it into the file
+ echo -e "$2\n$TIME\n$MESSAGE" > $filename
+
+
+fi
+
+# Remove the message from samba
+rm -f "$1"
+
diff --git a/kopete/protocols/winpopup/wpaccount.cpp b/kopete/protocols/winpopup/wpaccount.cpp
new file mode 100644
index 00000000..4b1342ff
--- /dev/null
+++ b/kopete/protocols/winpopup/wpaccount.cpp
@@ -0,0 +1,209 @@
+/***************************************************************************
+ wpaccount.cpp - WP Plugin
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@indigoarchive.net
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// QT Includes
+#include <qregexp.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kconfig.h>
+
+// Kopete Includes
+
+// Local Includes
+#include "wpaccount.h"
+
+class KPopupMenu;
+
+WPAccount::WPAccount(WPProtocol *parent, const QString &accountID, const char *name)
+ : Kopete::Account(parent, accountID, name)
+{
+// kdDebug(14170) << "WPAccount::WPAccount()" << endl;
+
+ mProtocol = WPProtocol::protocol();
+
+ // we need this before initActions
+ Kopete::MetaContact *myself = Kopete::ContactList::self()->myself();
+ setMyself( new WPContact(this, accountID, myself->displayName(), myself) );
+
+// if (excludeConnect()) connect(Kopete::OnlineStatus::Online); // ??
+}
+
+// Destructor
+WPAccount::~WPAccount()
+{
+}
+
+const QStringList WPAccount::getGroups()
+{
+ return mProtocol->getGroups();
+}
+
+const QStringList WPAccount::getHosts(const QString &Group)
+{
+ return mProtocol->getHosts(Group);
+}
+
+bool WPAccount::checkHost(const QString &Name)
+{
+// kdDebug() << "WPAccount::checkHost: " << Name << endl;
+ if (Name.upper() == QString::fromLatin1("LOCALHOST")) {
+ // Assume localhost is always there, but it will not appear in the samba output.
+ // Should never happen as localhost is now forbidden as contact, just for safety. GF
+ return true;
+ } else {
+ return mProtocol->checkHost(Name);
+ }
+}
+
+bool WPAccount::createContact(const QString &contactId, Kopete::MetaContact *parentContact )
+{
+// kdDebug(14170) << "[WPAccount::createContact] contactId: " << contactId << endl;
+
+ if (!contacts()[contactId]) {
+ WPContact *newContact = new WPContact(this, contactId, parentContact->displayName(), parentContact);
+ return newContact != 0;
+ } else {
+ kdDebug(14170) << "[WPAccount::addContact] Contact already exists" << endl;
+ }
+
+ return false;
+}
+
+void WPAccount::slotGotNewMessage(const QString &Body, const QDateTime &Arrival, const QString &From)
+{
+// kdDebug(14170) << "WPAccount::slotGotNewMessage(" << Body << ", " << Arrival.toString() << ", " << From << ")" << endl;
+
+ // Ignore messages from own host or IPs.
+ // IPs can not be matched to an account anyway.
+ // This should happen rarely but they make kopete crash.
+ // The reason for this seems to be in ChatSessionManager? GF
+ QRegExp ip("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
+
+// kdDebug(14170) << "ip.search: " << From << " match: " << ip.search(From) << endl;
+
+ if (From == accountId() || ip.exactMatch(From)) {
+ kdDebug(14170) << "Ignoring message from own host/account or IP." << endl;
+ return;
+ }
+
+ if (isConnected()) {
+ if (!isAway()) {
+ if(!contacts()[From]) {
+ addContact(From, From, 0, Kopete::Account::DontChangeKABC);
+ }
+ static_cast<WPContact *>(contacts()[From])->slotNewMessage(Body, Arrival);
+ }
+ else {
+ if (!theAwayMessage.isEmpty()) mProtocol->sendMessage(theAwayMessage, From);
+ }
+ } else {
+ // What to do with offline received messages?
+ kdDebug(14170) << "That's strange - we got a message while offline! Ignoring." << endl;
+ }
+}
+
+void WPAccount::connect(const Kopete::OnlineStatus &)
+{
+// kdDebug(14170) << "WPAccount::Connect()" << endl;
+ myself()->setOnlineStatus(mProtocol->WPOnline);
+}
+
+void WPAccount::disconnect()
+{
+// kdDebug(14170) << "WPAccount::Disconnect()" << endl;
+ myself()->setOnlineStatus(mProtocol->WPOffline);
+}
+
+/* I commented this code because deleting myself may have *dangerous* side effect, for example, for the status tracking.
+void WPAccount::updateAccountId()
+{
+ delete myself();
+ theInterface->setHostName(accountId());
+ myself() = new WPContact(this, accountId(), accountId(), 0);
+}*/
+
+void WPAccount::setAway(bool status, const QString &awayMessage)
+{
+// kdDebug(14170) << "WPAccount::setAway()" << endl;
+
+ theAwayMessage = awayMessage;
+
+// if(!isConnected())
+// theInterface->goOnline();
+ myself()->setOnlineStatus(status ? mProtocol->WPAway : mProtocol->WPOnline);
+}
+
+KActionMenu* WPAccount::actionMenu()
+{
+ kdDebug(14170) << "WPAccount::actionMenu()" << endl;
+
+ /// How to remove an action from Kopete::Account::actionMenu()? GF
+
+ KActionMenu *theActionMenu = new KActionMenu(accountId() , myself()->onlineStatus().iconFor(this), this);
+ theActionMenu->popupMenu()->insertTitle(myself()->onlineStatus().iconFor(this), i18n("WinPopup (%1)").arg(accountId()));
+
+ if (mProtocol)
+ {
+ KAction *goOnline = new KAction("Online", QIconSet(mProtocol->WPOnline.iconFor(this)), 0,
+ this, SLOT(connect()), theActionMenu, "actionGoAvailable");
+ goOnline->setEnabled(isConnected() && isAway());
+ theActionMenu->insert(goOnline);
+
+ KAction *goAway = new KAction("Away", QIconSet(mProtocol->WPAway.iconFor(this)), 0,
+ this, SLOT(goAway()), theActionMenu, "actionGoAway");
+ goAway->setEnabled(isConnected() && !isAway());
+ theActionMenu->insert(goAway);
+
+ /// One can not really go offline manually - appears online as long as samba server is running. GF
+
+ theActionMenu->popupMenu()->insertSeparator();
+ theActionMenu->insert(new KAction(i18n("Properties"), 0,
+ this, SLOT(editAccount()), theActionMenu, "actionAccountProperties"));
+
+ }
+
+ return theActionMenu;
+}
+
+void WPAccount::slotSendMessage(const QString &Body, const QString &Destination)
+{
+ kdDebug(14170) << "WPAccount::slotSendMessage(" << Body << ", " << Destination << ")" << endl;
+
+ if (myself()->onlineStatus().status() == Kopete::OnlineStatus::Away) myself()->setOnlineStatus(mProtocol->WPOnline);
+ mProtocol->sendMessage(Body, Destination);
+}
+
+void WPAccount::setOnlineStatus(const Kopete::OnlineStatus &status, const QString &reason)
+{
+ if (myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Online)
+ connect( status );
+ else if (myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Offline)
+ disconnect();
+ else if (myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline && status.status() == Kopete::OnlineStatus::Away)
+ setAway( true, reason );
+}
+
+#include "wpaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpaccount.h b/kopete/protocols/winpopup/wpaccount.h
new file mode 100644
index 00000000..f916ca86
--- /dev/null
+++ b/kopete/protocols/winpopup/wpaccount.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+ wpaccount.h - Base class for the Kopete WP account
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@indigoarchive.net
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WPACCOUNT_H
+#define WPACCOUNT_H
+
+
+// QT Includes
+#include <qpixmap.h>
+
+// KDE Includes
+
+// Kopete Includes
+#include "kopetemetacontact.h"
+#include "kopeteaccount.h"
+#include "kopeteonlinestatus.h"
+
+// Local Includes
+#include "wpcontact.h"
+#include "wpaddcontact.h"
+
+class KPopupMenu;
+class KActionMenu;
+class KAction;
+class WPProtocol;
+class KopeteWinPopup;
+
+/**
+ * The actual Account class used by Kopete.
+ */
+class WPAccount : public Kopete::Account
+{
+ Q_OBJECT
+
+// Kopete::Account overloading
+public:
+ WPAccount(WPProtocol *parent, const QString& accountID, const char *name = 0);
+ ~WPAccount();
+
+ virtual KActionMenu* actionMenu(); // Per-protocol actions for the systray and the status bar
+ virtual void setAway(bool status, const QString &); // Set user away
+
+public slots:
+ virtual void connect(const Kopete::OnlineStatus &); // Connect to server
+ virtual void disconnect(); // Disconnect from server
+
+ void goAvailable() { setAway(false, QString::null); } // Two convenience slots
+ void goAway() { setAway(true, QString::null); } // for available and away
+
+// Stuff used internally & by colleague classes
+public:
+ const QStringList getGroups();
+ const QStringList getHosts(const QString &Group);
+
+// Stuff used by WPContact
+public:
+ /**
+ * Returns whether or not the named host is online.
+ */
+ bool checkHost(const QString &Name);
+
+public slots:
+ /**
+ * Dispatches said message to the destination.
+ */
+ void slotSendMessage(const QString &Body, const QString &Destination);
+
+ /**
+ * Called when a new message arrives with the message's data.
+ */
+ void slotGotNewMessage(const QString &Body, const QDateTime &Arrival, const QString &From);
+
+ /* Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus &status , const QString &reason = QString::null);
+
+protected:
+ virtual bool createContact(const QString &contactId, Kopete::MetaContact *parentContact);
+
+private slots:
+// void updateAccountId();
+
+private:
+ WPProtocol *mProtocol;
+ QString theAwayMessage; // The message to give when the user is away
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpaddcontact.cpp b/kopete/protocols/winpopup/wpaddcontact.cpp
new file mode 100644
index 00000000..6187c644
--- /dev/null
+++ b/kopete/protocols/winpopup/wpaddcontact.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ wppreferences.cpp - description
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+// QT Includes
+#include <qlayout.h>
+
+// KDE Includes
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <kurlrequester.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+// Kopete Includes
+#include <addcontactpage.h>
+
+// Local Includes
+#include "wpaddcontactbase.h"
+#include "wpaccount.h"
+#include "wpaddcontact.h"
+
+WPAddContact::WPAddContact(QWidget *parent, WPAccount *newAccount, const char *name) : AddContactPage(parent, name)
+{
+// kdDebug(14170) << "WPAddContact::WPAddContact(<owner>, " << newAccount << ", <parent>, " << name << ")" << endl;
+
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ theDialog = new WPAddContactBase(this);
+ connect(theDialog->mHostGroup, SIGNAL(activated(const QString &)), this, SLOT(slotSelected(const QString &)));
+ connect(theDialog->mRefresh, SIGNAL(clicked()), this, SLOT(slotUpdateGroups()));
+ theDialog->show();
+
+ theAccount = newAccount;
+
+ slotUpdateGroups();
+ slotSelected(theDialog->mHostGroup->currentText());
+}
+
+WPAddContact::~WPAddContact()
+{
+}
+
+void WPAddContact::slotUpdateGroups()
+{
+ kdDebug(14170) << "WPAddContact::slotUpdateGroups()" << endl;
+
+ theDialog->mHostGroup->clear();
+ QStringList Groups = theAccount->getGroups();
+ QStringList::ConstIterator end = Groups.end();
+ for (QStringList::ConstIterator i = Groups.begin(); i != end; i++)
+ theDialog->mHostGroup->insertItem(SmallIcon("network"), *i);
+ slotSelected(theDialog->mHostGroup->currentText());
+}
+
+void WPAddContact::slotSelected(const QString &Group)
+{
+ kdDebug(14170) << "WPAddContact::slotSelected(" << Group << ")" << endl;
+
+ theDialog->mHostName->clear();
+ QStringList Hosts = theAccount->getHosts(Group);
+ QString ownHost = theAccount->myself()->contactId();
+ QStringList::ConstIterator end = Hosts.end();
+ for (QStringList::ConstIterator i = Hosts.begin(); i != end; i++)
+ if (*i != ownHost) theDialog->mHostName->insertItem(SmallIcon("personal"), *i);
+}
+
+bool WPAddContact::validateData()
+{
+ kdDebug(14170) << "WPAddContact::validateData()" << endl;
+
+ QString tmpHostName = theDialog->mHostName->currentText();
+
+ if (tmpHostName.isEmpty()) {
+ KMessageBox::sorry(this, i18n("<qt>You must enter a valid hostname.</qt>"), i18n("WinPopup"));
+ return false;
+ }
+
+ // If our own host is not allowed as contact localhost should be forbidden as well,
+ // additionally somehow localhost as contact crashes when receiving a message from it?? GF
+ if (tmpHostName.upper() == QString::fromLatin1("LOCALHOST")) {
+ KMessageBox::sorry(this, i18n("<qt>LOCALHOST is not allowed as contact.</qt>"), i18n("WinPopup"));
+ return false;
+ }
+
+ return true;
+}
+
+bool WPAddContact::apply(Kopete::Account *theAccount, Kopete::MetaContact *theMetaContact)
+{
+ kdDebug(14170) << "WPAddContact::apply(" << theAccount << ", " << theMetaContact << ")" << endl;
+
+ // TODO: make the nickname an option
+ return theAccount->addContact(theDialog->mHostName->currentText(), theMetaContact, Kopete::Account::ChangeKABC );
+}
+
+#include "wpaddcontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpaddcontact.h b/kopete/protocols/winpopup/wpaddcontact.h
new file mode 100644
index 00000000..4d593cba
--- /dev/null
+++ b/kopete/protocols/winpopup/wpaddcontact.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ wpaddcontact.h - description
+ -------------------
+ begin : Wed Jan 23 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WPADDCONTACT_H
+#define WPADDCONTACT_H
+
+// Kopete Includes
+#include <addcontactpage.h>
+
+// QT Includes
+
+// KDE Includes
+
+// Local Includes
+
+class WPAccount;
+class WPAddContactBase;
+namespace Kopete { class MetaContact; }
+
+class WPAddContact: public AddContactPage
+{
+ Q_OBJECT
+
+private:
+ WPAccount *theAccount;
+ WPAddContactBase *theDialog;
+
+public:
+ WPAddContact(QWidget *parent, WPAccount *newAccount, const char *name = 0);
+ ~WPAddContact();
+
+ virtual bool validateData();
+
+public slots:
+ virtual bool apply(Kopete::Account *theAccount, Kopete::MetaContact *theMetaContact);
+
+ void slotSelected(const QString &Group);
+ void slotUpdateGroups();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpcontact.cpp b/kopete/protocols/winpopup/wpcontact.cpp
new file mode 100644
index 00000000..7cf2529c
--- /dev/null
+++ b/kopete/protocols/winpopup/wpcontact.cpp
@@ -0,0 +1,189 @@
+/***************************************************************************
+ wpcontact.cpp - description
+ -------------------
+ begin : Fri Apr 12 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// Qt Includes
+#include <qregexp.h>
+
+// KDE Includes
+#include <kdebug.h>
+
+// Kopete Includes
+
+// Local Includes
+#include "wpcontact.h"
+#include "wpaccount.h"
+
+WPContact::WPContact(Kopete::Account *account, const QString &newHostName, const QString &nickName, Kopete::MetaContact *metaContact)
+ : Kopete::Contact(account, newHostName, metaContact)
+{
+// kdDebug(14170) << "WPContact::WPContact(<account>, " << newHostName << ", " << nickName << ", <parent>)" << endl;
+ kdDebug(14170) << "WPContact::WPContact: " << this << endl;
+
+ QString theNickName = nickName;
+
+ if (theNickName.isEmpty()) {
+ // Construct nickname from hostname with first letter to upper. GF
+ theNickName = newHostName.lower();
+ theNickName = theNickName.replace(0, 1, theNickName[0].upper());
+ }
+
+ setNickName(theNickName);
+ myWasConnected = false;
+
+
+ m_manager = 0;
+ m_infoDialog = 0;
+
+ // Initialise and start the periodical checking for contact's status
+ setOnlineStatus(static_cast<WPProtocol *>(protocol())->WPOffline);
+
+ connect(&checkStatus, SIGNAL(timeout()), this, SLOT(slotCheckStatus()));
+ checkStatus.start(1000, false);
+}
+
+QPtrList<KAction> * WPContact::customContextMenuActions()
+{
+ //myActionCollection = new KActionCollection(parent);
+ return 0;
+}
+
+void WPContact::serialize(QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData)
+{
+// kdDebug(14170) << "WP::serialize(...)" << endl;
+
+ Kopete::Contact::serialize(serializedData, addressBookData);
+}
+
+Kopete::ChatSession* WPContact::manager( Kopete::Contact::CanCreateFlags /*canCreate*/ ) // TODO: use the parameter as canCreate
+{
+ if (m_manager == 0) {
+ // Set up the message managers
+ QPtrList<Kopete::Contact> singleContact;
+ singleContact.append(this);
+
+ m_manager = Kopete::ChatSessionManager::self()->create( account()->myself(), singleContact, protocol() );
+
+ connect(m_manager, SIGNAL(messageSent(Kopete::Message &, Kopete::ChatSession *)), this, SLOT(slotSendMessage(Kopete::Message &)));
+ connect(m_manager, SIGNAL(messageSent(Kopete::Message &, Kopete::ChatSession *)), m_manager, SLOT(appendMessage(Kopete::Message &)));
+ connect(m_manager, SIGNAL(destroyed()), this, SLOT(slotChatSessionDestroyed()));
+ }
+
+ return m_manager;
+}
+
+/*
+bool WPContact::isOnline() const
+{
+ kdDebug(14170) << "[WPContact::isOnline()]" << endl;
+ return onlineStatus().status() != Kopete::OnlineStatus::Offline && onlineStatus().status() != Kopete::OnlineStatus::Unknown;
+}
+*/
+
+bool WPContact::isReachable()
+{
+// kdDebug(14170) << "[WPContact::isReachable()]" << endl;
+ return onlineStatus().status() != Kopete::OnlineStatus::Offline && onlineStatus().status() != Kopete::OnlineStatus::Unknown;
+}
+
+void WPContact::slotChatSessionDestroyed()
+{
+ m_manager = 0;
+}
+
+void WPContact::slotUserInfo()
+{
+ kdDebug( 14170 ) << k_funcinfo << endl;
+
+ if (!m_infoDialog) {
+ m_infoDialog = new WPUserInfo( this, static_cast<WPAccount*>( account() ) );
+ if (!m_infoDialog) return;
+ connect( m_infoDialog, SIGNAL( closing() ), this, SLOT( slotCloseUserInfoDialog() ) );
+ m_infoDialog->show();
+ } else {
+ m_infoDialog->raise();
+ }
+}
+
+void WPContact::slotCloseUserInfoDialog()
+{
+ m_infoDialog->delayedDestruct();
+ m_infoDialog = 0;
+}
+
+/*
+void deleteContact()
+{
+// deleteLater();
+}
+*/
+
+void WPContact::slotCheckStatus()
+{
+ bool oldWasConnected = myWasConnected;
+ bool newIsOnline = false;
+
+ myWasConnected = protocol() != 0 && account() != 0;
+ WPAccount *acct = dynamic_cast<WPAccount *>(account());
+ if (acct) newIsOnline = acct->checkHost(contactId());
+
+ if(newIsOnline != isOnline() || myWasConnected != oldWasConnected) {
+ Kopete::OnlineStatus tmpStatus = WPProtocol::protocol()->WPOffline;
+ if (myWasConnected && newIsOnline) {
+ tmpStatus = WPProtocol::protocol()->WPOnline;
+ }
+ setOnlineStatus(tmpStatus);
+ }
+}
+
+void WPContact::slotNewMessage(const QString &Body, const QDateTime &Arrival)
+{
+ kdDebug(14170) << "WPContact::slotNewMessage(" << Body << ", " << Arrival.toString() << ")" << endl;
+
+ QPtrList<Kopete::Contact> contactList;
+ contactList.append(account()->myself());
+
+ QRegExp subj("^Subject: ([^\n]*)\n(.*)$");
+ Kopete::Message msg;
+
+ if(subj.search(Body) == -1) {
+ msg = Kopete::Message(this, contactList, Body, Kopete::Message::Inbound);
+ } else {
+ msg = Kopete::Message(this, contactList, subj.cap(2), subj.cap(1), Kopete::Message::Inbound);
+ }
+
+ manager(Kopete::Contact::CannotCreate)->appendMessage(msg);
+}
+
+void WPContact::slotSendMessage( Kopete::Message& message )
+{
+// kdDebug(14170) << "WPContact::slotSendMessage(<message>)" << endl;
+ // Warning: this could crash
+ kdDebug(14170) << message.to().first() << " is " << dynamic_cast<WPContact *>( message.to().first() )->contactId() << endl;
+
+ QString Message = (!message.subject().isEmpty() ? "Subject: " + message.subject() + "\n" : QString("")) + message.plainBody();
+ WPAccount *acct = dynamic_cast<WPAccount *>(account());
+ WPContact *contact = dynamic_cast<WPContact *>( message.to().first() );
+ if (acct && contact) {
+ acct->slotSendMessage( Message, contact->contactId() );
+ m_manager->messageSucceeded();
+ }
+}
+
+#include "wpcontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpcontact.h b/kopete/protocols/winpopup/wpcontact.h
new file mode 100644
index 00000000..343e07f7
--- /dev/null
+++ b/kopete/protocols/winpopup/wpcontact.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ wpcontact.h - description
+ -------------------
+ begin : Fri Apr 12 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@indigoarchive.net
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 WPCONTACT_H
+#define WPCONTACT_H
+
+// KDE Includes
+#include <kaction.h>
+
+// Qt Includes
+//#include <qvaluestack.h>
+#include <qdatetime.h>
+#include <qptrlist.h>
+#include <qtimer.h>
+#include <qstringlist.h>
+
+// Kopete Includes
+#include "kopetecontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetechatsession.h"
+#include "kopetemessage.h"
+
+// Local Includes
+#include "wpprotocol.h"
+#include "wpuserinfo.h"
+
+class QTimer;
+class QListView;
+class QListViewItem;
+class KPopupMenu;
+class KAction;
+namespace Kopete { class MetaContact; }
+
+class WPContact: public Kopete::Contact
+{
+ Q_OBJECT
+
+public:
+ WPContact(Kopete::Account *account, const QString &userId, const QString &fullName, Kopete::MetaContact *metaContact);
+
+// virtual bool isOnline() const;
+ virtual bool isReachable();
+ virtual QPtrList<KAction> *customContextMenuActions();
+ virtual Kopete::ChatSession *manager(Kopete::Contact::CanCreateFlags = Kopete::Contact::CannotCreate);
+ virtual void serialize(QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData);
+
+public slots:
+ virtual void slotUserInfo();
+ void slotCheckStatus(); // the call back for the checkStatus timer
+ void slotNewMessage(const QString &Body, const QDateTime &Arrival);
+
+private slots:
+ void slotChatSessionDestroyed();
+ void slotSendMessage(Kopete::Message &message);
+ void slotCloseUserInfoDialog(); // Called when the userinfo dialog is getting closed
+
+private:
+ bool myWasConnected; // true if protocol connected at last check
+
+ QTimer checkStatus; // checks the status of this contact every second or so
+// KActionCollection *myActionCollection;
+ // holds all the protocol specific actions (not many!)
+ Kopete::ChatSession *m_manager;
+ // holds the two message managers - one for email and one for chat
+ WPUserInfo *m_infoDialog;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpeditaccount.cpp b/kopete/protocols/winpopup/wpeditaccount.cpp
new file mode 100644
index 00000000..454f5d6f
--- /dev/null
+++ b/kopete/protocols/winpopup/wpeditaccount.cpp
@@ -0,0 +1,138 @@
+/***************************************************************************
+ wpeditaccount.cpp - description
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// Standard Unix Includes
+#include <unistd.h>
+
+// QT Includes
+#include <qcheckbox.h>
+#include <qfile.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <knuminput.h>
+#include <klineedit.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+
+
+// Kopete Includes
+#include <addcontactpage.h>
+
+// Local Includes
+#include "wpaccount.h"
+#include "wpeditaccount.h"
+#include "wpprotocol.h"
+
+WPEditAccount::WPEditAccount(QWidget *parent, Kopete::Account *theAccount)
+ : WPEditAccountBase(parent), KopeteEditAccountWidget(theAccount)
+{
+ kdDebug(14170) << "WPEditAccount::WPEditAccount(<parent>, <theAccount>)";
+
+ mProtocol = WPProtocol::protocol();
+
+ QString tmpSmbcPath = KStandardDirs::findExe("smbclient");
+
+ if(account()) {
+ mHostName->setText(account()->accountId());
+// mAutoConnect->setChecked(account()->excludeConnect());
+ mHostName->setReadOnly(true);
+ KGlobal::config()->setGroup("WinPopup");
+ mHostCheckFreq->setValue(KGlobal::config()->readNumEntry("HostCheckFreq", 60));
+ mSmbcPath->setURL(KGlobal::config()->readEntry("SmbcPath", tmpSmbcPath));
+
+ }
+ else {
+ // no QT/KDE function? GF
+ QString theHostName = QString::null;
+ char *tmp = new char[255];
+
+ if (tmp != 0) {
+ gethostname(tmp, 255);
+ theHostName = tmp;
+ if (theHostName.contains('.') != 0) theHostName.remove(theHostName.find('.'), theHostName.length());
+ theHostName = theHostName.upper();
+ }
+
+ if (!theHostName.isEmpty())
+ mHostName->setText(theHostName);
+ else
+ mHostName->setText("LOCALHOST");
+
+ mHostCheckFreq->setValue(60);
+ mSmbcPath->setURL(tmpSmbcPath);
+ }
+
+ show();
+}
+
+void WPEditAccount::installSamba()
+{
+ mProtocol->installSamba();
+}
+
+bool WPEditAccount::validateData()
+{
+ kdDebug(14170) << "WPEditAccount::validateData()";
+
+ if(mHostName->text().isEmpty()) {
+ KMessageBox::sorry(this, i18n("<qt>You must enter a valid screen name.</qt>"), i18n("WinPopup"));
+ return false;
+ }
+
+ QFile smbc(mSmbcPath->url());
+ if (!smbc.exists()) {
+ KMessageBox::sorry(this, i18n("<qt>You must enter a valid smbclient path.</qt>"), i18n("WinPopup"));
+ return false;
+ }
+
+ return true;
+}
+
+void WPEditAccount::writeConfig()
+{
+ KGlobal::config()->setGroup("WinPopup");
+ KGlobal::config()->writeEntry("SmbcPath", mSmbcPath->url());
+ KGlobal::config()->writeEntry("HostCheckFreq", mHostCheckFreq->text());
+}
+
+Kopete::Account *WPEditAccount::apply()
+{
+ kdDebug(14170) << "WPEditAccount::apply()";
+
+ if(!account())
+ setAccount(new WPAccount(mProtocol, mHostName->text()));
+
+// account()->setExcludeConnect(mAutoConnect->isChecked());
+ writeConfig();
+
+ mProtocol->settingsChanged();
+
+ return account();
+}
+
+#include "wpeditaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpeditaccount.h b/kopete/protocols/winpopup/wpeditaccount.h
new file mode 100644
index 00000000..afa521a7
--- /dev/null
+++ b/kopete/protocols/winpopup/wpeditaccount.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ wpeditaccount.h - description
+ -------------------
+ begin : Wed Jan 23 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WPEDITACCOUNT_H
+#define WPEDITACCOUNT_H
+
+// KDE Includes
+
+// QT Includes
+
+// Kopete Includes
+#include "editaccountwidget.h"
+
+// Local Includes
+#include "wpprotocol.h"
+#include "wpaccount.h"
+#include "wpeditaccountbase.h"
+
+namespace Kopete { class Account; }
+
+class WPEditAccount: public WPEditAccountBase, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+
+private:
+ WPProtocol *mProtocol;
+ WPAccount *mAccount;
+
+public:
+ WPEditAccount(QWidget *parent, Kopete::Account *theAccount);
+
+ virtual bool validateData();
+ void writeConfig();
+
+public slots:
+ virtual Kopete::Account *apply();
+ virtual void installSamba();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpprotocol.cpp b/kopete/protocols/winpopup/wpprotocol.cpp
new file mode 100644
index 00000000..b765438c
--- /dev/null
+++ b/kopete/protocols/winpopup/wpprotocol.cpp
@@ -0,0 +1,187 @@
+/***************************************************************************
+ wpprotocol.cpp - WP Plugin
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// QT Includes
+#include <qmap.h>
+#include <qdict.h>
+
+// KDE Includes
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <kmessagebox.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+
+// Kopete Includes
+#include "kopeteaccountmanager.h"
+#include "kopeteuiglobal.h"
+
+// Local Includes
+#include "wpprotocol.h"
+#include "wpeditaccount.h"
+#include "wpaccount.h"
+#include "wpcontact.h"
+
+class KPopupMenu;
+
+WPProtocol *WPProtocol::sProtocol = 0;
+
+typedef KGenericFactory<WPProtocol> WPProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_wp, WPProtocolFactory( "kopete_wp" ) )
+
+// WP Protocol
+WPProtocol::WPProtocol( QObject *parent, const char *name, const QStringList & /* args */ )
+: Kopete::Protocol( WPProtocolFactory::instance(), parent, name ),
+ WPOnline( Kopete::OnlineStatus::Online, 25, this, 0, QString::null, i18n("Online"), i18n("Online")),
+ WPAway( Kopete::OnlineStatus::Away, 20, this, 1, "wp_away", i18n("Away"), i18n("Away")),
+ WPOffline( Kopete::OnlineStatus::Offline, 0, this, 2, QString::null, i18n("Offline"), i18n("Offline"))
+{
+// kdDebug(14170) << "WPProtocol::WPProtocol()" << endl;
+
+ sProtocol = this;
+
+ // Load Status Actions
+// initActions();
+ // TODO: Maybe use this in the future?
+
+ addAddressBookField( "messaging/winpopup", Kopete::Plugin::MakeIndexField );
+
+ readConfig();
+
+ popupClient = new WinPopupLib(smbClientBin, groupCheckFreq);
+ QObject::connect(popupClient, SIGNAL(signalNewMessage(const QString &, const QDateTime &, const QString &)),
+ this, SLOT(slotReceivedMessage(const QString &, const QDateTime &, const QString &)));
+}
+
+// Destructor
+WPProtocol::~WPProtocol()
+{
+// kdDebug(14170) << "WPProtocol::~WPProtocol()" << endl;
+
+ sProtocol = 0;
+}
+
+AddContactPage *WPProtocol::createAddContactWidget(QWidget *parent, Kopete::Account *theAccount)
+{
+// kdDebug(14170) << "WPProtocol::createAddContactWidget(<parent>, " << theAccount << ")" << endl;
+
+ return new WPAddContact(parent, dynamic_cast<WPAccount *>(theAccount));
+}
+
+Kopete::Contact *WPProtocol::deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData,
+ const QMap<QString, QString> & /* addressBookData */ )
+{
+ QString contactId = serializedData[ "contactId" ];
+ QString accountId = serializedData[ "accountId" ];
+
+ WPAccount *theAccount = static_cast<WPAccount *>(Kopete::AccountManager::self()->findAccount(protocol()->pluginId(), accountId));
+ if(!theAccount) {
+ kdDebug(14170) << "Account " << accountId << " not found" << endl;
+ return 0;
+ }
+
+ if(theAccount->contacts()[contactId]) {
+ kdDebug(14170) << "User " << contactId << " already in contacts map" << endl;
+ return 0;
+ }
+
+ theAccount->addContact(contactId, metaContact, Kopete::Account::DontChangeKABC);
+ return theAccount->contacts()[contactId];
+}
+
+KopeteEditAccountWidget *WPProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new WPEditAccount(parent, account);
+}
+
+Kopete::Account *WPProtocol::createNewAccount(const QString &accountId)
+{
+ return new WPAccount(this, accountId);
+}
+
+void WPProtocol::settingsChanged()
+{
+ kdDebug(14170) << "WPProtocol::slotSettingsChanged()" << endl;
+
+ readConfig();
+ popupClient->settingsChanged(smbClientBin, groupCheckFreq);
+}
+
+void WPProtocol::readConfig()
+{
+ KGlobal::config()->setGroup("WinPopup");
+ smbClientBin = KGlobal::config()->readEntry("SmbcPath", "/usr/bin/smbclient");
+ groupCheckFreq = KGlobal::config()->readNumEntry("HostCheckFreq", 60);
+}
+
+void WPProtocol::installSamba()
+{
+// kdDebug(14170) << "WPProtocol::installSamba()" endl;
+
+ QStringList args;
+ args += KStandardDirs::findExe("winpopup-install.sh");
+ args += KStandardDirs::findExe("winpopup-send.sh");
+ if (KApplication::kdeinitExecWait("kdesu", args) == 0)
+ KMessageBox::information(Kopete::UI::Global::mainWidget(), i18n("The Samba configuration file is modified."), i18n("Configuration Succeeded"));
+ else
+ KMessageBox::error(Kopete::UI::Global::mainWidget(), i18n("Updating the Samba configuration file failed."), i18n("Configuration Failed"));
+}
+
+/**
+ * search the contact for the new message and give it to its account
+ */
+void WPProtocol::slotReceivedMessage(const QString &Body, const QDateTime &Time, const QString &From)
+{
+ bool foundContact = false;
+ QString accountKey = QString::null;
+ QDict<Kopete::Account> Accounts = Kopete::AccountManager::self()->accounts(protocol());
+ for (QDictIterator<Kopete::Account> it(Accounts); it.current(); ++it) {
+ QDict<Kopete::Contact> Contacts = it.current()->contacts();
+ Kopete::Contact *theContact = Contacts[From];
+ if (theContact != 0) {
+ foundContact = true;
+ dynamic_cast<WPAccount *>(it.current())->slotGotNewMessage(Body, Time, From);
+ break;
+ }
+
+ if (accountKey.isEmpty() && it.current()->isConnected()) accountKey = it.currentKey();
+ }
+
+ // What to do with messages with no contact?
+ // Maybe send them to the next online account? GF
+ if (!foundContact) {
+ if (!accountKey.isEmpty())
+ dynamic_cast<WPAccount *>(Accounts[accountKey])->slotGotNewMessage(Body, Time, From);
+ else
+ kdDebug(14170) << "No contact or connected account found!" << endl;
+ }
+}
+
+void WPProtocol::sendMessage(const QString &Body, const QString &Destination)
+{
+ popupClient->sendMessage(Body, Destination);
+}
+
+#include "wpprotocol.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpprotocol.h b/kopete/protocols/winpopup/wpprotocol.h
new file mode 100644
index 00000000..92a9e434
--- /dev/null
+++ b/kopete/protocols/winpopup/wpprotocol.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+ wpprotocol.h - Base class for the Kopete WP protocol
+ -------------------
+ begin : Fri Apr 26 2002
+ copyright : (C) 2002 by Gav Wood
+ email : gav@kde.org
+
+ Based on code from : (C) 2002 by Duncan Mac-Vicar Prett
+ email : duncan@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WPPROTOCOL_H
+#define WPPROTOCOL_H
+
+// QT Includes
+#include <qpixmap.h>
+#include <qptrlist.h>
+#include <qdatetime.h>
+
+// Kopete Includes
+#include "kopetemetacontact.h"
+#include "kopeteprotocol.h"
+#include "kopeteonlinestatus.h"
+
+// Local Includes
+#include "libwinpopup.h"
+#include "wpaddcontact.h"
+
+namespace Kopete { class Account; }
+class KPopupMenu;
+class KActionMenu;
+class KAction;
+class WPContact;
+class WPAccount;
+
+/**
+ * The actual Protocol class used by Kopete.
+ */
+class WPProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+
+// Kopete::Protocol overloading
+public:
+ WPProtocol( QObject *parent, const char *name, const QStringList &args );
+ ~WPProtocol();
+
+ virtual AddContactPage *createAddContactWidget(QWidget *parent, Kopete::Account *theAccount);
+ virtual KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+ virtual Kopete::Account *createNewAccount(const QString &accountId);
+
+ const QStringList getGroups() {return popupClient->getGroups(); }
+ const QStringList getHosts(const QString &Group) { return popupClient->getHosts(Group); }
+ bool checkHost(const QString &Name) { return popupClient->checkHost(Name); }
+
+// Kopete::Plugin overloading
+public:
+ virtual Kopete::Contact *deserializeContact(Kopete::MetaContact *metaContact, const QMap<QString, QString> &serializedData, const QMap<QString, QString> &addressBookData);
+
+// Stuff used internally & by colleague classes
+public:
+ static WPProtocol *protocol() { return sProtocol; }
+
+ const Kopete::OnlineStatus WPOnline;
+ const Kopete::OnlineStatus WPAway;
+ const Kopete::OnlineStatus WPOffline;
+ void sendMessage(const QString &Body, const QString &Destination);
+ void settingsChanged(void); // Callback when settings changed
+
+public slots:
+ void installSamba(); // Modify smb.conf to use winpopup-send.sh script
+ void slotReceivedMessage(const QString &Body, const QDateTime &Time, const QString &From);
+
+private:
+ QString smbClientBin;
+ int groupCheckFreq;
+ void readConfig();
+ WinPopupLib *popupClient;
+ static WPProtocol *sProtocol; // Singleton
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpuserinfo.cpp b/kopete/protocols/winpopup/wpuserinfo.cpp
new file mode 100644
index 00000000..7e4348d4
--- /dev/null
+++ b/kopete/protocols/winpopup/wpuserinfo.cpp
@@ -0,0 +1,114 @@
+/***************************************************************************
+ wpuserinfo.cpp - WinPopup User Info
+ -------------------
+ begin : Tue May 06 2003
+ copyright : (C) 2003 by Tais M. Hansen
+ email : tais.hansen@osd.dk
+
+ Based on code from : (C) 2002-2003 by the Kopete developers
+ email : kopete-devel@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// QT Includes
+#include <qregexp.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <klocale.h>
+#include <klineedit.h>
+#include <ksimpleconfig.h>
+
+// Local Includes
+#include "wpuserinfo.h"
+#include "wpaccount.h"
+#include "wpcontact.h"
+
+WPUserInfo::WPUserInfo( WPContact *contact, WPAccount */*account*/, QWidget *parent, const char* name )
+ : KDialogBase( parent, name, false, QString::null, Close, Close, false ), m_contact(contact),
+ Comment(i18n("N/A")), Workgroup(i18n("N/A")), OS(i18n("N/A")), Software(i18n("N/A"))
+{
+// kdDebug( 14170 ) << k_funcinfo << endl;
+
+ setCaption( i18n( "User Info for %1" ).arg( m_contact->nickName() ) );
+
+ m_mainWidget = new WPUserInfoWidget( this, "WPUserInfo::m_mainWidget" );
+ setMainWidget( m_mainWidget );
+
+ m_mainWidget->sComputerName->setText( m_contact->contactId() );
+
+ m_mainWidget->sComment->setText(i18n("Looking"));
+ m_mainWidget->sWorkgroup->setText(i18n("Looking"));
+ m_mainWidget->sOS->setText(i18n("Looking"));
+ m_mainWidget->sServer->setText(i18n("Looking"));
+
+ connect( this, SIGNAL( closeClicked() ), this, SLOT( slotCloseClicked() ) );
+
+ startDetailsProcess(m_contact->contactId());
+}
+
+// I decided to do this direct here to avoid "Handstände" with signals and stuff
+// if we would do this in libwinpopup. GF
+void WPUserInfo::startDetailsProcess(const QString &host)
+{
+ KGlobal::config()->setGroup("WinPopup");
+ QString theSMBClientPath = KGlobal::config()->readEntry("SMBClientPath", "/usr/bin/smbclient");
+
+ KProcIO *details = new KProcIO;
+ *details << theSMBClientPath << "-N" << "-E" << "-g" << "-L" << host << "-";
+
+ connect(details, SIGNAL(readReady(KProcIO *)), this, SLOT(slotDetailsProcessReady(KProcIO *)));
+ connect(details, SIGNAL(processExited(KProcess *)), this, SLOT(slotDetailsProcessExited(KProcess *)));
+
+ if (!details->start(KProcess::NotifyOnExit, KProcess::Stderr)) {
+ slotDetailsProcessExited(details);
+ kdDebug(14170) << "DetailsProcess not started!" << endl;
+ }
+}
+
+void WPUserInfo::slotDetailsProcessReady(KProcIO *d)
+{
+ QString tmpLine = QString::null;
+ QRegExp info("^Domain=\\[(.*)\\]\\sOS=\\[(.*)\\]\\sServer=\\[(.*)\\]$"), host("^Server\\|(.*)\\|(.*)$");
+
+ while (d->readln(tmpLine) > -1) {
+ if (info.search(tmpLine) != -1) {
+ Workgroup = info.cap(1);
+ OS = info.cap(2);
+ Software = info.cap(3);
+ }
+ if (host.search(tmpLine) != -1) {
+ Comment = host.cap(2);
+ }
+ }
+}
+
+void WPUserInfo::slotDetailsProcessExited(KProcess *d)
+{
+ delete d;
+
+ m_mainWidget->sComment->setText(Comment);
+ m_mainWidget->sWorkgroup->setText(Workgroup);
+ m_mainWidget->sOS->setText(OS);
+ m_mainWidget->sServer->setText(Software);
+}
+
+void WPUserInfo::slotCloseClicked()
+{
+ kdDebug( 14170 ) << k_funcinfo << endl;
+
+ emit closing();
+}
+
+#include "wpuserinfo.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/winpopup/wpuserinfo.h b/kopete/protocols/winpopup/wpuserinfo.h
new file mode 100644
index 00000000..1c4ce0e9
--- /dev/null
+++ b/kopete/protocols/winpopup/wpuserinfo.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ wpuserinfo.h - WinPopup User Info
+ -------------------
+ begin : Tue May 06 2003
+ copyright : (C) 2003 by Tais M. Hansen
+ email : tais.hansen@osd.dk
+
+ Based on code from : (C) 2002-2003 by the Kopete developers
+ email : kopete-devel@kde.org
+ ***************************************************************************
+
+ ***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef WPUSERINFO_H
+#define WPUSERINFO_H
+
+// KDE Includes
+#include <kdialogbase.h>
+#include <kprocio.h>
+
+// Local Includes
+#include "wpuserinfowidget.h"
+
+class WPAccount;
+class WPContact;
+
+class WPUserInfo : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ WPUserInfo( WPContact *, WPAccount *, QWidget *parent = 0, const char* name = "WPUserInfo" );
+
+ void startDetailsProcess(const QString &host);
+
+ private slots:
+ void slotDetailsProcessReady(KProcIO *d);
+ void slotDetailsProcessExited(KProcess *d);
+ void slotCloseClicked();
+
+ signals:
+ void closing();
+
+ private:
+ WPContact *m_contact;
+ WPUserInfoWidget *m_mainWidget;
+
+ QString Comment, Workgroup, OS, Software;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+// kate: tab-width 4; indent-width 4; replace-trailing-space-save on;
diff --git a/kopete/protocols/yahoo/Makefile.am b/kopete/protocols/yahoo/Makefile.am
new file mode 100644
index 00000000..6815da99
--- /dev/null
+++ b/kopete/protocols/yahoo/Makefile.am
@@ -0,0 +1,26 @@
+SUBDIRS = libkyahoo ui icons
+METASOURCES = AUTO
+
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/ui \
+ -Iui \
+ -I$(srcdir)/libkyahoo \
+ $(all_includes)
+
+kde_module_LTLIBRARIES = kopete_yahoo.la
+
+kopete_yahoo_la_SOURCES = yahooprotocol.cpp yahoocontact.cpp \
+ yahooaddcontact.cpp yahooaccount.cpp yahooeditaccount.cpp yahooconferencemessagemanager.cpp \
+ yahoochatsession.cpp yahooverifyaccount.cpp yahoowebcam.cpp
+kopete_yahoo_la_LDFLAGS = -module $(KDE_PLUGIN)
+kopete_yahoo_la_LIBADD = ../../libkopete/avdevice/libkopete_videodevice.la \
+ $(top_builddir)/kopete/libkopete/libkopete.la ui/libkopeteyahooui.la libkyahoo/libkyahoo.la
+
+service_DATA = kopete_yahoo.desktop
+servicedir = $(kde_servicesdir)
+
+
+mydatadir = $(kde_datadir)/kopete_yahoo
+mydata_DATA = yahooconferenceui.rc yahoochatui.rc
+
+
diff --git a/kopete/protocols/yahoo/icons/Makefile.am b/kopete/protocols/yahoo/icons/Makefile.am
new file mode 100644
index 00000000..224eb420
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/Makefile.am
@@ -0,0 +1,3 @@
+kopeteicondir = $(kde_datadir)/kopete/icons
+kopeteicon_ICON = AUTO
+
diff --git a/kopete/protocols/yahoo/icons/cr128-app-yahoo_protocol.png b/kopete/protocols/yahoo/icons/cr128-app-yahoo_protocol.png
new file mode 100644
index 00000000..7b589e44
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr128-app-yahoo_protocol.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_away.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_away.png
new file mode 100644
index 00000000..49aaff75
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_away.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_busy.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_busy.png
new file mode 100644
index 00000000..13f40c97
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_busy.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_connecting.mng b/kopete/protocols/yahoo/icons/cr16-action-yahoo_connecting.mng
new file mode 100644
index 00000000..08b14859
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_connecting.mng
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_idle.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_idle.png
new file mode 100644
index 00000000..50f8cbce
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_idle.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_invisible.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_invisible.png
new file mode 100644
index 00000000..e7a99c43
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_invisible.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_mobile.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_mobile.png
new file mode 100644
index 00000000..4d071e67
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_mobile.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_stealthed.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_stealthed.png
new file mode 100644
index 00000000..0e13f48d
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_stealthed.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-action-yahoo_tea.png b/kopete/protocols/yahoo/icons/cr16-action-yahoo_tea.png
new file mode 100644
index 00000000..f920db8e
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-action-yahoo_tea.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr16-app-yahoo_protocol.png b/kopete/protocols/yahoo/icons/cr16-app-yahoo_protocol.png
new file mode 100644
index 00000000..30569212
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr16-app-yahoo_protocol.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr22-action-yahoo_stealthed.png b/kopete/protocols/yahoo/icons/cr22-action-yahoo_stealthed.png
new file mode 100644
index 00000000..0640918e
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr22-action-yahoo_stealthed.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr32-action-yahoo_stealthed.png b/kopete/protocols/yahoo/icons/cr32-action-yahoo_stealthed.png
new file mode 100644
index 00000000..44bff072
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr32-action-yahoo_stealthed.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr32-app-yahoo_protocol.png b/kopete/protocols/yahoo/icons/cr32-app-yahoo_protocol.png
new file mode 100644
index 00000000..5aafba04
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr32-app-yahoo_protocol.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr48-app-yahoo_protocol.png b/kopete/protocols/yahoo/icons/cr48-app-yahoo_protocol.png
new file mode 100644
index 00000000..4dfee265
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr48-app-yahoo_protocol.png
Binary files differ
diff --git a/kopete/protocols/yahoo/icons/cr64-app-yahoo_protocol.png b/kopete/protocols/yahoo/icons/cr64-app-yahoo_protocol.png
new file mode 100644
index 00000000..a7b58015
--- /dev/null
+++ b/kopete/protocols/yahoo/icons/cr64-app-yahoo_protocol.png
Binary files differ
diff --git a/kopete/protocols/yahoo/kopete_yahoo.desktop b/kopete/protocols/yahoo/kopete_yahoo.desktop
new file mode 100644
index 00000000..35fe6bf0
--- /dev/null
+++ b/kopete/protocols/yahoo/kopete_yahoo.desktop
@@ -0,0 +1,83 @@
+[Desktop Entry]
+Type=Service
+X-Kopete-Version=1000900
+Icon=yahoo_protocol
+ServiceTypes=Kopete/Protocol
+X-KDE-Library=kopete_yahoo
+X-Kopete-Messaging-Protocol=messaging/yahoo
+X-KDE-PluginInfo-Author=Kopete Developers
+X-KDE-PluginInfo-Email=kopete-devel@kde.org
+X-KDE-PluginInfo-Name=kopete_yahoo
+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=Yahoo
+Name[bn]=ইয়à§à¦¯à¦¾à¦¹à§
+Name[fa]=یاهو
+Name[hi]=याहू
+Name[km]=យ៉ាហ៊ូ
+Name[ne]=याहू
+Name[pa]=ਯਾਹੂ
+Name[ta]=யாஹூ
+Comment=Protocol to connect to Yahoo
+Comment[ar]=سيتصل البروتوكول بـ Yahoo
+Comment[be]=Пратакол Yahoo
+Comment[bg]=Протокол за връзка Ñ Yahoo
+Comment[bn]=ইয়à§à¦¯à¦¾à¦¹à§à¦¤à§‡ সংযোগ করতে পà§à¦°à§‹à¦Ÿà§‹à¦•à¦²
+Comment[br]=Komenad kevreañ ouzh Yahoo
+Comment[bs]=Yahoo protokol
+Comment[ca]=Protocol per a connectar-se a Yahoo
+Comment[cs]=Protokol k připojení k Yahoo
+Comment[cy]=Protocol i gysylltu â Yahoo
+Comment[da]=Protokol til at forbinde til Yahoo
+Comment[de]=Protokoll zur Verbindung mit Yahoo
+Comment[el]=ΠÏωτόκολλο για σÏνδεση στο Yahoo
+Comment[es]=Protocolo para conectar a Yahoo
+Comment[et]=Protokoll ühendumiseks Yahooga
+Comment[eu]=Yahoo-ra konektatzeko protokoloa
+Comment[fa]=قرارداد برای اتصال به یاهو
+Comment[fi]=Yhteyskäytäntö Yahoo-verkkoon kytkeytymiseen
+Comment[fr]=Protocole pour se connecter sur Yahoo
+Comment[ga]=Prótacal chun ceangal le Yahoo
+Comment[gl]=Protocolo para se conectar a Yahoo
+Comment[he]=פרוטוקול התחברות ל- Yahoo
+Comment[hi]=से जà¥à¤¡à¤¼à¤¨à¥‡ का पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¥‰à¤²
+Comment[hr]=Protokol za povezivanje na Yahoo
+Comment[hu]=Protokoll a Yahoo-hoz való csatlakozáshoz
+Comment[is]=Samskiptamáti til að tengjast Yahoo
+Comment[it]=Protocollo per connessione a Yahoo
+Comment[ja]=Yahoo ã«æŽ¥ç¶šã™ã‚‹ãƒ—ロトコル
+Comment[ka]=Yahoo-სთáƒáƒœ დáƒáƒ™áƒáƒ•áƒ¨áƒ˜áƒ áƒ”ბის áƒáƒ¥áƒ›áƒ˜
+Comment[kk]=Yahoo-ға қоÑылу протоколы
+Comment[km]=ពិធីការ​ដើម្បី​ភ្ជាប់​ទៅ​យ៉ាហ៊ូ
+Comment[lt]=Protokolas prisijungimui prie Yahoo
+Comment[mk]=Протокол за поврзување на Yahoo
+Comment[nb]=Protokoll for å koble til Yahoo
+Comment[nds]=Protokoll för't Tokoppeln na Yahoo
+Comment[ne]=याहूमा जडान गरà¥à¤¨à¥à¤ªà¤°à¥à¤¨à¥‡ पà¥à¤°à¥‹à¤Ÿà¥‹à¤•à¤²
+Comment[nl]=Protocol voor Yahoo
+Comment[nn]=Protokoll for å kopla til Yahoo
+Comment[pl]=Protokół połączenia z serwerem Yahoo
+Comment[pt]=Um protocolo para se ligar ao Yahoo
+Comment[pt_BR]=Protocolo para conexão ao Yahoo
+Comment[ro]=Protocol de conectare la Yahoo
+Comment[ru]=Протокол Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ðº Yahoo
+Comment[sk]=Protokol pre pripojenie k Yahoo
+Comment[sl]=Protokol za povezavo na Yahoo
+Comment[sr]=Протокол за повезивање на Yahoo
+Comment[sr@Latn]=Protokol za povezivanje na Yahoo
+Comment[sv]=Protokoll för att ansluta till Yahoo
+Comment[ta]=யாஹூ உடன௠இணைகà¯à®• விதிமà¯à®±à¯ˆ
+Comment[tg]=Қарордоди пайваÑтшавӣ ба Yahoo
+Comment[tr]=Yahoo'ya bağlantı iletişim kuralı
+Comment[uk]=Протокол Ð´Ð»Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Yahoo
+Comment[uz]=Yahoo bilan aloqa oʻrnatish uchun protokol
+Comment[uz@cyrillic]=Yahoo билан алоқа ўрнатиш учун протокол
+Comment[wa]=Protocole po s' raloyî a Yahoo
+Comment[zh_CN]=连接到 Yahoo åè®®
+Comment[zh_HK]=用來連接至 Yahoo 的通訊å”定
+Comment[zh_TW]=連線到 Yahoo çš„å”定
+
diff --git a/kopete/protocols/yahoo/libkyahoo/Makefile.am b/kopete/protocols/yahoo/libkyahoo/Makefile.am
new file mode 100644
index 00000000..b5e8e034
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/Makefile.am
@@ -0,0 +1,23 @@
+SUBDIRS = . tests
+METASOURCES = AUTO
+noinst_LTLIBRARIES = libkyahoo.la
+
+AM_CPPFLAGS = $(all_includes)
+
+
+libkyahoo_la_SOURCES = client.cpp task.cpp connector.cpp inputprotocolbase.cpp \
+ ymsgprotocol.cpp ymsgtransfer.cpp transfer.cpp yahoobytestream.cpp bytestream.cpp \
+ yahooclientstream.cpp yahooconnector.cpp safedelete.cpp stream.cpp sha1.c md5.c crypt.c \
+ coreprotocol.cpp logintask.cpp libyahoo.c yahoo_fn.c listtask.cpp statusnotifiertask.cpp \
+ mailnotifiertask.cpp messagereceivertask.cpp sendnotifytask.cpp sendmessagetask.cpp \
+ logofftask.cpp changestatustask.cpp modifybuddytask.cpp picturenotifiertask.cpp \
+ requestpicturetask.cpp yahoobuddyiconloader.cpp stealthtask.cpp sendpicturetask.cpp \
+ webcamtask.cpp conferencetask.cpp sendauthresptask.cpp pingtask.cpp yabtask.cpp \
+ yabentry.cpp modifyyabtask.cpp chatsessiontask.cpp sendfiletask.cpp filetransfernotifiertask.cpp \
+ receivefiletask.cpp
+libkyahoo_la_LDFLAGS = -no-undefined $(all_libraries)
+libkyahoo_la_LIBADD = $(LIB_QT)
+
+noinst_HEADERS = logintask.h yabentry.h yabtask.h modifyyabtask.h \
+ chatsessiontask.h
+KDE_OPTIONS = nofinal
diff --git a/kopete/protocols/yahoo/libkyahoo/bytestream.cpp b/kopete/protocols/yahoo/libkyahoo/bytestream.cpp
new file mode 100644
index 00000000..2da5ceef
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/bytestream.cpp
@@ -0,0 +1,289 @@
+/*
+ * 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 <kdebug.h>
+#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)
+{
+// kdDebug(14181) << k_funcinfo << endl;
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << "[data size: " << a.size() << "]" << endl;
+
+// kdDebug(14181) << k_funcinfo << "[Data: " << a << "]" << endl;
+
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << " " << bytes <<" [bytes]"<< endl;
+ 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
+{
+// kdDebug(14181) << k_funcinfo << "[bytes left: " << d->writeBuf.size() << " ]" << endl;
+ return d->writeBuf.size();
+}
+
+//!
+//! Writes string \a cs to the stream.
+void ByteStream::write(const QCString &cs)
+{
+// kdDebug(14181) << k_funcinfo << "[data size: " << cs.length() << "]" << endl;
+
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << endl;
+ appendArray(&d->readBuf, block);
+}
+
+//!
+//! Appends \a block to the end of the write buffer.
+void ByteStream::appendWrite(const QByteArray &block)
+{
+// kdDebug(14181) << k_funcinfo << "[data size: " << block.size() << "]" << endl;
+
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << "[data size: " << size << "][ delete :" << del << " ]" << endl;
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << "[data size: " << size << "][ delete :" << del << " ]" << endl;
+ 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()
+{
+// kdDebug(14181) << k_funcinfo << endl;
+ 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()
+{
+// kdDebug(14181) << k_funcinfo << "(THIS RETURNS -1)" << endl;
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << endl;
+ 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)
+{
+// kdDebug(14181) << k_funcinfo << "[int size] : " << size << " [bool del] " << del << endl;
+
+ 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/yahoo/libkyahoo/bytestream.h b/kopete/protocols/yahoo/libkyahoo/bytestream.h
new file mode 100644
index 00000000..7f964fbd
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/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/yahoo/libkyahoo/changestatustask.cpp b/kopete/protocols/yahoo/libkyahoo/changestatustask.cpp
new file mode 100644
index 00000000..a4da7b57
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/changestatustask.cpp
@@ -0,0 +1,85 @@
+/*
+ Kopete Yahoo Protocol
+ Change our Status
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "changestatustask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+ChangeStatusTask::ChangeStatusTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+ChangeStatusTask::~ChangeStatusTask()
+{
+}
+
+void ChangeStatusTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if( m_status == Yahoo::StatusInvisible ) // status --> Invisible
+ {
+ sendVisibility( Invisible );
+ }
+ else
+ {
+ YMSGTransfer *t = new YMSGTransfer( Yahoo::ServiceStatus );
+ t->setId( client()->sessionID() );
+
+ if( !m_message.isEmpty() )
+ {
+ m_status = Yahoo::StatusCustom;
+ t->setParam( 19, m_message.utf8() );
+ }
+ t->setParam( 10, m_status );
+ t->setParam( 47, m_type );
+ t->setParam( 97, 1 ); // it's utf8
+
+ send( t );
+
+ if( client()->status() == Yahoo::StatusInvisible ) // Invisible --> Status
+ sendVisibility( Visible );
+ }
+ setSuccess( true );
+}
+
+void ChangeStatusTask::sendVisibility( Visibility visible )
+{
+ YMSGTransfer *t = new YMSGTransfer( Yahoo::ServiceVisibility );
+ t->setId( client()->sessionID() );
+ t->setParam( 13, visible );
+ send( t );
+}
+
+void ChangeStatusTask::setMessage( const QString &msg )
+{
+ m_message = msg;
+}
+
+void ChangeStatusTask::setStatus( Yahoo::Status status )
+{
+ m_status = status;
+}
+
+void ChangeStatusTask::setType( Yahoo::StatusType type )
+{
+ m_type = type;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/changestatustask.h b/kopete/protocols/yahoo/libkyahoo/changestatustask.h
new file mode 100644
index 00000000..5455c665
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/changestatustask.h
@@ -0,0 +1,50 @@
+/*
+ Kopete Yahoo Protocol
+ Change our Status
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 CHANGESTATUSTASK_H
+#define CHANGESTATUSTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+
+class QString;
+
+
+/**
+@author André Duffeck
+*/
+class ChangeStatusTask : public Task
+{
+public:
+ enum Type { Available, Away };
+ ChangeStatusTask(Task *parent);
+ ~ChangeStatusTask();
+
+ virtual void onGo();
+
+ void setMessage( const QString &msg );
+ void setStatus( Yahoo::Status status );
+ void setType( Yahoo::StatusType type );
+private:
+ enum Visibility { Visible = 1, Invisible = 2 };
+ QString m_message;
+ Yahoo::Status m_status;
+ Yahoo::StatusType m_type;
+
+ void sendVisibility( Visibility visible );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/chatsessiontask.cpp b/kopete/protocols/yahoo/libkyahoo/chatsessiontask.cpp
new file mode 100644
index 00000000..553297e9
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/chatsessiontask.cpp
@@ -0,0 +1,66 @@
+/*
+ Kopete Yahoo Protocol
+ chatsessiontask.cpp - Register / Unregister a chatsession
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "chatsessiontask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+ChatSessionTask::ChatSessionTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+ChatSessionTask::~ChatSessionTask()
+{
+}
+
+void ChatSessionTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer( Yahoo::ServiceChatSession );
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, m_target.local8Bit() );
+ if( m_type == RegisterSession )
+ {
+ t->setParam( 13, 1 );
+ }
+ else
+ {
+ t->setParam( 13, 2 );
+ t->setParam( 34, 1 );
+ }
+ send( t );
+
+ setSuccess( true );
+}
+
+void ChatSessionTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void ChatSessionTask::setType( Type type )
+{
+ m_type = type;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/chatsessiontask.h b/kopete/protocols/yahoo/libkyahoo/chatsessiontask.h
new file mode 100644
index 00000000..b5b5c76c
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/chatsessiontask.h
@@ -0,0 +1,45 @@
+/*
+ Kopete Yahoo Protocol
+ chatsessiontask.h - Register / Unregister a chatsession
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef CHATSESSIONTASK_H
+#define CHATSESSIONTASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class ChatSessionTask : public Task
+{
+public:
+ enum Type { RegisterSession, UnregisterSession };
+ ChatSessionTask(Task *parent);
+ ~ChatSessionTask();
+
+ virtual void onGo();
+
+ void setTarget( const QString &to );
+ void setType( Type type );
+private:
+ QString m_target;
+ Type m_type;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/client.cpp b/kopete/protocols/yahoo/libkyahoo/client.cpp
new file mode 100644
index 00000000..186f1e12
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/client.cpp
@@ -0,0 +1,869 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2005-2006 André Duffeck <andre.duffeck@kdemail.net>
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+ Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kurl.h>
+#include <ksocketbase.h>
+
+#include "yahooclientstream.h"
+#include "yahooconnector.h"
+#include "task.h"
+#include "logintask.h"
+#include "listtask.h"
+#include "statusnotifiertask.h"
+#include "mailnotifiertask.h"
+#include "messagereceivertask.h"
+#include "sendnotifytask.h"
+#include "sendmessagetask.h"
+#include "logofftask.h"
+#include "changestatustask.h"
+#include "modifybuddytask.h"
+#include "picturenotifiertask.h"
+#include "requestpicturetask.h"
+#include "stealthtask.h"
+#include "sendpicturetask.h"
+#include "webcamtask.h"
+#include "conferencetask.h"
+#include "sendauthresptask.h"
+#include "pingtask.h"
+#include "yabtask.h"
+#include "modifyyabtask.h"
+#include "chatsessiontask.h"
+#include "sendfiletask.h"
+#include "filetransfernotifiertask.h"
+#include "receivefiletask.h"
+#include "client.h"
+#include "yahootypes.h"
+#include "yahoobuddyiconloader.h"
+
+using namespace KNetwork;
+
+class Client::ClientPrivate
+{
+public:
+ ClientPrivate() {}
+
+ ClientStream *stream;
+ int id_seed;
+ Task *root;
+ QString host, user, pass;
+ uint port;
+ bool active;
+ YahooBuddyIconLoader *iconLoader;
+ int error;
+ QString errorString;
+ QString errorInformation;
+
+ // tasks
+ bool tasksInitialized;
+ LoginTask * loginTask;
+ ListTask *listTask;
+ StatusNotifierTask *statusTask;
+ MailNotifierTask *mailTask;
+ MessageReceiverTask *messageReceiverTask;
+ PictureNotifierTask *pictureNotifierTask;
+ WebcamTask *webcamTask;
+ ConferenceTask *conferenceTask;
+ YABTask *yabTask;
+ FileTransferNotifierTask *fileTransferTask;
+
+ // Connection data
+ uint sessionID;
+ QString yCookie;
+ QString tCookie;
+ QString cCookie;
+ Yahoo::Status status;
+ Yahoo::Status statusOnConnect;
+ QString statusMessageOnConnect;
+ int pictureFlag;
+};
+
+Client::Client(QObject *par) :QObject(par, "yahooclient" )
+{
+ d = new ClientPrivate;
+/* d->tzoffset = 0;*/
+ d->active = false;
+
+ d->root = new Task(this, true);
+ d->statusOnConnect = Yahoo::StatusAvailable;
+ setStatus( Yahoo::StatusDisconnected );
+ d->tasksInitialized = false;
+ d->stream = 0L;
+ d->iconLoader = 0L;
+ d->loginTask = new LoginTask( d->root );
+ d->listTask = new ListTask( d->root );
+ d->pictureFlag = 0;
+ m_connector = 0L;
+
+ m_pingTimer = new QTimer( this );
+ QObject::connect( m_pingTimer, SIGNAL( timeout() ), this, SLOT( sendPing() ) );
+
+ QObject::connect( d->loginTask, SIGNAL( haveSessionID( uint ) ), SLOT( lt_gotSessionID( uint ) ) );
+ QObject::connect( d->loginTask, SIGNAL( loginResponse( int, const QString& ) ),
+ SLOT( slotLoginResponse( int, const QString& ) ) );
+ QObject::connect( d->loginTask, SIGNAL( haveCookies() ), SLOT( slotGotCookies() ) );
+ QObject::connect( d->listTask, SIGNAL( gotBuddy(const QString &, const QString &, const QString &) ),
+ SIGNAL( gotBuddy(const QString &, const QString &, const QString &) ) );
+ QObject::connect( d->listTask, SIGNAL( stealthStatusChanged( const QString&, Yahoo::StealthStatus ) ),
+ SIGNAL( stealthStatusChanged( const QString&, Yahoo::StealthStatus ) ) );
+}
+
+Client::~Client()
+{
+ close();
+ delete d->iconLoader;
+ delete d->root;
+ delete d;
+}
+
+void Client::connect( const QString &host, const uint port, const QString &userId, const QString &pass )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ d->host = host;
+ d->port = port;
+ d->user = userId;
+ d->pass = pass;
+ setStatus( Yahoo::StatusConnecting );
+
+ m_connector = new KNetworkConnector;
+ m_connector->setOptHostPort( host, port );
+ d->stream = new ClientStream( m_connector, this );
+ QObject::connect( d->stream, SIGNAL( connected() ), this, SLOT( cs_connected() ) );
+ QObject::connect( d->stream, SIGNAL( error(int) ), this, SLOT( streamError(int) ) );
+ QObject::connect( d->stream, SIGNAL( readyRead() ), this, SLOT( streamReadyRead() ) );
+
+ d->stream->connectToServer( host, false );
+}
+
+void Client::cancelConnect()
+{
+ d->loginTask->reset();
+}
+
+void Client::cs_connected()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ emit connected();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " starting login task ... "<< endl;
+
+ d->loginTask->setStateOnConnect( (d->statusOnConnect == Yahoo::StatusInvisible) ? Yahoo::StatusInvisible : Yahoo::StatusAvailable );
+ d->loginTask->go();
+ d->active = true;
+}
+
+void Client::close()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_pingTimer->stop();
+ if( d->active )
+ {
+ LogoffTask *lt = new LogoffTask( d->root );
+ lt->go( true );
+ }
+ if( d->tasksInitialized)
+ deleteTasks();
+ d->loginTask->reset();
+ if( d->stream ) {
+ QObject::disconnect( d->stream, SIGNAL( readyRead() ), this, SLOT( streamReadyRead() ) );
+ d->stream->deleteLater();
+ }
+ d->stream = 0L;
+ if( m_connector )
+ m_connector->deleteLater();
+ m_connector = 0L;
+}
+
+int Client::error()
+{
+ return d->error;
+}
+
+QString Client::errorString()
+{
+ return d->errorString;
+}
+
+QString Client::errorInformation()
+{
+ return d->errorInformation;
+}
+
+// SLOTS //
+void Client::streamError( int error )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "CLIENT ERROR (Error " << error << ")" << endl;
+ QString msg;
+
+ d->active = false;
+
+ // Examine error
+ if( error == ClientStream::ErrConnection ) // Ask Connector in this case
+ {
+ d->error = m_connector->errorCode();
+ d->errorString = KSocketBase::errorString( (KSocketBase::SocketError)d->error );
+ }
+ else
+ {
+ d->error = error;
+ d->errorString = d->stream->errorText();
+ }
+ close();
+ if( status() == Yahoo::StatusConnecting )
+ emit loginFailed();
+ else
+ emit disconnected();
+}
+
+void Client::streamReadyRead()
+{
+ // take the incoming transfer and distribute it to the task tree
+ Transfer * transfer = d->stream->read();
+ distribute( transfer );
+}
+
+void Client::lt_loginFinished()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ slotLoginResponse( d->loginTask->statusCode(), d->loginTask->statusString() );
+}
+
+void Client::slotLoginResponse( int response, const QString &msg )
+{
+ if( response == Yahoo::LoginOk )
+ {
+ if( !(d->statusOnConnect == Yahoo::StatusAvailable ||
+ d->statusOnConnect == Yahoo::StatusInvisible) ||
+ !d->statusMessageOnConnect.isEmpty() )
+ changeStatus( d->statusOnConnect, d->statusMessageOnConnect, Yahoo::StatusTypeAway );
+ d->statusMessageOnConnect = QString::null;
+ setStatus( d->statusOnConnect );
+ m_pingTimer->start( 60 * 1000 );
+ initTasks();
+ } else {
+ d->active = false;
+ close();
+ }
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Emitting loggedIn" << endl;
+ emit loggedIn( response, msg );
+}
+
+void Client::lt_gotSessionID( uint id )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Got SessionID: " << id << endl;
+ d->sessionID = id;
+}
+
+void Client::slotGotCookies()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Y: " << d->loginTask->yCookie()
+ << " T: " << d->loginTask->tCookie()
+ << " C: " << d->loginTask->cCookie() << endl;
+ d->yCookie = d->loginTask->yCookie();
+ d->tCookie = d->loginTask->tCookie();
+ d->cCookie = d->loginTask->cCookie();
+}
+
+// INTERNALS //
+
+// ***** Messaging handling *****
+void Client::sendTyping( const QString &who, bool typing )
+{
+ SendNotifyTask *snt = new SendNotifyTask( d->root );
+ snt->setTarget( who );
+ snt->setState( typing ? SendNotifyTask::Active : SendNotifyTask::NotActive );
+ snt->setType( SendNotifyTask::NotifyTyping );
+ snt->go( true );
+}
+
+void Client::sendWebcamInvite( const QString &who )
+{
+ if( !d->webcamTask->transmitting() )
+ d->webcamTask->registerWebcam();
+
+ d->webcamTask->addPendingInvitation( who );
+}
+
+void Client::sendMessage( const QString &to, const QString &msg )
+{
+ SendMessageTask *smt = new SendMessageTask( d->root );
+ smt->setTarget( to );
+ smt->setText( msg );
+ smt->setPicureFlag( pictureFlag() );
+ smt->go( true );
+}
+
+void Client::setChatSessionState( const QString &to, bool close )
+{
+ ChatSessionTask *cst = new ChatSessionTask( d->root );
+ cst->setTarget( to );
+ cst->setType( close ? ChatSessionTask::UnregisterSession : ChatSessionTask::RegisterSession );
+ cst->go( true );
+}
+
+void Client::sendBuzz( const QString &to )
+{
+ SendMessageTask *smt = new SendMessageTask( d->root );
+ smt->setTarget( to );
+ smt->setText( QString::fromLatin1( "<ding>" ) );
+ smt->setPicureFlag( pictureFlag() );
+ smt->go( true );
+}
+
+void Client::sendFile( unsigned int transferId, const QString &to, const QString &msg, KURL url )
+{
+ SendFileTask *sft = new SendFileTask( d->root );
+
+ QObject::connect( sft, SIGNAL(complete(unsigned int)), SIGNAL(fileTransferComplete(unsigned int)) );
+ QObject::connect( sft, SIGNAL(bytesProcessed(unsigned int, unsigned int)), SIGNAL(fileTransferBytesProcessed(unsigned int, unsigned int)) );
+ QObject::connect( sft, SIGNAL(error(unsigned int, int, const QString &)), SIGNAL(fileTransferError(unsigned int, int, const QString &)) );
+
+ QObject::connect( this, SIGNAL(fileTransferCanceled( unsigned int )), sft, SLOT(canceled( unsigned int )) );
+
+ sft->setTarget( to );
+ sft->setMessage( msg );
+ sft->setFileUrl( url );
+ sft->setTransferId( transferId );
+ sft->go( true );
+}
+
+void Client::receiveFile( unsigned int transferId, const QString &userId, KURL remoteURL, KURL localURL )
+{
+ ReceiveFileTask *rft = new ReceiveFileTask( d->root );
+
+ QObject::connect( rft, SIGNAL(complete(unsigned int)), SIGNAL(fileTransferComplete(unsigned int)) );
+ QObject::connect( rft, SIGNAL(bytesProcessed(unsigned int, unsigned int)), SIGNAL(fileTransferBytesProcessed(unsigned int, unsigned int)) );
+ QObject::connect( rft, SIGNAL(error(unsigned int, int, const QString &)), SIGNAL(fileTransferError(unsigned int, int, const QString &)) );
+ QObject::connect( this, SIGNAL(fileTransferCanceled( unsigned int )), rft, SLOT(canceled( unsigned int )) );
+
+ rft->setRemoteUrl( remoteURL );
+ rft->setLocalUrl( localURL );
+ rft->setTransferId( transferId );
+ rft->setUserId( userId );
+ if( remoteURL.url().startsWith( "http://" ) )
+ rft->setType( ReceiveFileTask::FileTransferAccept );
+ else
+ rft->setType( ReceiveFileTask::FileTransfer7Accept );
+ rft->go( true );
+}
+
+void Client::rejectFile( const QString &userId, KURL remoteURL )
+{
+ if( remoteURL.url().startsWith( "http://" ) )
+ return;
+
+ ReceiveFileTask *rft = new ReceiveFileTask( d->root );
+
+ rft->setRemoteUrl( remoteURL );
+ rft->setUserId( userId );
+ rft->setType( ReceiveFileTask::FileTransfer7Reject );
+ rft->go( true );
+}
+
+void Client::cancelFileTransfer( unsigned int transferId )
+{
+ emit fileTransferCanceled( transferId );
+}
+
+void Client::changeStatus( Yahoo::Status status, const QString &message, Yahoo::StatusType type )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "status: " << status
+ << " message: " << message
+ << " type: " << type << endl;
+ ChangeStatusTask *cst = new ChangeStatusTask( d->root );
+ cst->setStatus( status );
+ cst->setMessage( message );
+ cst->setType( type );
+ cst->go( true );
+
+ if( status == Yahoo::StatusInvisible )
+ stealthContact( QString::null, Yahoo::StealthOnline, Yahoo::StealthClear );
+
+ setStatus( status );
+}
+
+void Client::sendAuthReply( const QString &userId, bool accept, const QString &msg )
+{
+ SendAuthRespTask *sarp = new SendAuthRespTask( d->root );
+ sarp->setGranted( accept );
+ sarp->setTarget( userId );
+ sarp->setMessage( msg );
+ sarp->go( true );
+}
+
+void Client::sendPing()
+{
+ if( !d->active )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Disconnected. NOT sending a PING." << endl;
+ return;
+ }
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Sending a PING." << endl;
+ PingTask *pt = new PingTask( d->root );
+ pt->go( true );
+}
+
+// ***** Contactlist handling *****
+
+void Client::stealthContact(QString const &userId, Yahoo::StealthMode mode, Yahoo::StealthStatus state)
+{
+ StealthTask *st = new StealthTask( d->root );
+ st->setTarget( userId );
+ st->setState( state );
+ st->setMode( mode );
+ st->go( true );
+}
+
+void Client::addBuddy( const QString &userId, const QString &group, const QString &message )
+{
+ ModifyBuddyTask *mbt = new ModifyBuddyTask( d->root );
+ mbt->setType( ModifyBuddyTask::AddBuddy );
+ mbt->setTarget( userId );
+ mbt->setGroup( group );
+ mbt->setMessage( message );
+ mbt->go( true );
+}
+
+void Client::removeBuddy( const QString &userId, const QString &group )
+{
+ ModifyBuddyTask *mbt = new ModifyBuddyTask( d->root );
+ mbt->setType( ModifyBuddyTask::RemoveBuddy );
+ mbt->setTarget( userId );
+ mbt->setGroup( group );
+ mbt->go( true );
+}
+
+void Client::moveBuddy( const QString &userId, const QString &oldGroup, const QString &newGroup )
+{
+ ModifyBuddyTask *mbt = new ModifyBuddyTask( d->root );
+ mbt->setType( ModifyBuddyTask::MoveBuddy );
+ mbt->setTarget( userId );
+ mbt->setOldGroup( oldGroup );
+ mbt->setGroup( newGroup );
+ mbt->go( true );
+}
+
+// ***** Buddyicon handling *****
+
+void Client::requestPicture( const QString &userId )
+{
+ RequestPictureTask *rpt = new RequestPictureTask( d->root );
+ rpt->setTarget( userId );
+ rpt->go( true );
+}
+
+void Client::downloadPicture( const QString &userId, KURL url, int checksum )
+{
+ if( !d->iconLoader )
+ {
+ d->iconLoader = new YahooBuddyIconLoader( this );
+ QObject::connect( d->iconLoader, SIGNAL(fetchedBuddyIcon(const QString&, KTempFile*, int )),
+ SIGNAL(pictureDownloaded(const QString&, KTempFile*, int ) ) );
+ }
+
+ d->iconLoader->fetchBuddyIcon( QString(userId), KURL(url), checksum );
+}
+
+void Client::uploadPicture( KURL url )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "URL: " << url.url() << endl;
+ SendPictureTask *spt = new SendPictureTask( d->root );
+ spt->setType( SendPictureTask::UploadPicture );
+ spt->setFilename( url.fileName() );
+ if ( url.isLocalFile() )
+ spt->setPath( url.path() );
+ else
+ spt->setPath( url.url() );
+ d->pictureFlag = 2;
+ spt->go( true );
+}
+
+void Client::sendPictureChecksum( int checksum, const QString &who )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "checksum: " << checksum << endl;
+ SendPictureTask *spt = new SendPictureTask( d->root );
+ spt->setType( SendPictureTask::SendChecksum );
+ spt->setChecksum( checksum );
+ if( !who.isEmpty() )
+ spt->setTarget( who );
+ spt->go( true );
+}
+
+void Client::sendPictureInformation( const QString &userId, const QString &url, int checksum )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "checksum: " << checksum << endl;
+ SendPictureTask *spt = new SendPictureTask( d->root );
+ spt->setType( SendPictureTask::SendInformation );
+ spt->setChecksum( checksum );
+ spt->setUrl( url );
+ spt->setTarget( userId );
+ spt->go( true );
+}
+
+void Client::sendPictureStatusUpdate( const QString &userId, int type )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Setting PictureStatus to: " << type << endl;
+ SendPictureTask *spt = new SendPictureTask( d->root );
+ spt->setType( SendPictureTask::SendStatus );
+ spt->setStatus( type );
+ spt->setTarget( userId );
+ spt->go( true );
+}
+
+// ***** Webcam handling *****
+
+void Client::requestWebcam( const QString &userId )
+{
+ d->webcamTask->requestWebcam( userId );
+}
+
+void Client::closeWebcam( const QString &userId )
+{
+ d->webcamTask->closeWebcam( userId );
+}
+
+void Client::sendWebcamImage( const QByteArray &ar )
+{
+ d->webcamTask->sendWebcamImage( ar );
+}
+
+void Client::closeOutgoingWebcam()
+{
+ d->webcamTask->closeOutgoingWebcam();
+}
+
+
+void Client::grantWebcamAccess( const QString &userId )
+{
+ d->webcamTask->grantAccess( userId );
+}
+
+// ***** Conferences *****
+void Client::inviteConference( const QString &room, const QStringList &members, const QString &msg )
+{
+ d->conferenceTask->inviteConference( room, members, msg );
+}
+
+void Client::addInviteConference( const QString &room, const QStringList &who, const QStringList &members, const QString &msg )
+{
+ d->conferenceTask->addInvite( room, who, members, msg );
+}
+
+void Client::joinConference( const QString &room, const QStringList &members )
+{
+ d->conferenceTask->joinConference( room, members );
+}
+
+void Client::declineConference( const QString &room, const QStringList &members, const QString &msg )
+{
+ d->conferenceTask->declineConference( room, members, msg );
+}
+
+void Client::leaveConference( const QString &room, const QStringList &members )
+{
+ d->conferenceTask->leaveConference( room, members );
+}
+
+void Client::sendConferenceMessage( const QString &room, const QStringList &members, const QString &msg )
+{
+ d->conferenceTask->sendMessage( room, members, msg );
+}
+
+// ***** YAB *****
+void Client::getYABEntries( long lastMerge, long lastRemoteRevision )
+{
+ d->yabTask->getAllEntries( lastMerge, lastRemoteRevision);
+}
+
+void Client::saveYABEntry( YABEntry &entry )
+{
+ ModifyYABTask *myt = new ModifyYABTask( d->root );
+ myt->setAction( ModifyYABTask::EditEntry );
+ myt->setEntry( entry );
+ QObject::connect( myt, SIGNAL(gotEntry( YABEntry * )), this, SIGNAL( gotYABEntry( YABEntry * ) ) );
+ QObject::connect( myt, SIGNAL(error( YABEntry *, const QString &)), this, SIGNAL(modifyYABEntryError( YABEntry *, const QString & )));
+ myt->go(true);
+}
+
+void Client::addYABEntry( YABEntry &entry )
+{
+ ModifyYABTask *myt = new ModifyYABTask( d->root );
+ myt->setAction( ModifyYABTask::AddEntry );
+ myt->setEntry( entry );
+ QObject::connect( myt, SIGNAL(gotEntry( YABEntry * )), this, SIGNAL( gotYABEntry( YABEntry * ) ) );
+ QObject::connect( myt, SIGNAL(error( YABEntry *, const QString &)), this, SIGNAL(modifyYABEntryError( YABEntry *, const QString & )));
+ myt->go(true);
+}
+
+void Client::deleteYABEntry( YABEntry &entry )
+{
+ ModifyYABTask *myt = new ModifyYABTask( d->root );
+ myt->setAction( ModifyYABTask::DeleteEntry );
+ myt->setEntry( entry );
+ myt->go(true);
+}
+
+// ***** other *****
+void Client::notifyError( const QString &info, const QString & errorString, LogLevel level )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << QString::fromLatin1("\nThe following error occured: %1\n Reason: %2\n LogLevel: %3")
+ .arg(info).arg(errorString).arg(level) << endl;
+ d->errorString = errorString;
+ d->errorInformation = info;
+ emit error( level );
+}
+
+QString Client::userId()
+{
+ return d->user;
+}
+
+void Client::setUserId( const QString & userId )
+{
+ d->user = userId;
+}
+
+Yahoo::Status Client::status()
+{
+ return d->status;
+}
+
+void Client::setStatus( Yahoo::Status status )
+{
+ d->status = status;
+}
+
+
+void Client::setStatusOnConnect( Yahoo::Status status )
+{
+ d->statusOnConnect = status;
+}
+
+void Client::setStatusMessageOnConnect( const QString &msg )
+{
+ d->statusMessageOnConnect = msg;
+}
+
+void Client::setVerificationWord( const QString &word )
+{
+ d->loginTask->setVerificationWord( word );
+}
+
+QString Client::password()
+{
+ return d->pass;
+}
+
+QCString Client::ipAddress()
+{
+ //TODO determine ip address
+ return "127.0.0.1";
+}
+
+QString Client::host()
+{
+ return d->host;
+}
+
+int Client::port()
+{
+ return d->port;
+}
+
+uint Client::sessionID()
+{
+ return d->sessionID;
+}
+
+int Client::pictureFlag()
+{
+ return d->pictureFlag;
+}
+
+void Client::setPictureFlag( int flag )
+{
+ d->pictureFlag = flag;
+}
+
+QString Client::yCookie()
+{
+ return d->yCookie;
+}
+
+QString Client::tCookie()
+{
+ return d->tCookie;
+}
+
+QString Client::cCookie()
+{
+ return d->cCookie;
+}
+
+void Client::distribute( Transfer * transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ if( !rootTask()->take( transfer ) )
+ kdDebug(YAHOO_RAW_DEBUG) << "CLIENT: root task refused transfer" << endl;
+ delete transfer;
+}
+
+void Client::send( Transfer* request )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << "CLIENT::send()"<< endl;
+ if( !d->stream )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << "CLIENT - NO STREAM TO SEND ON!" << endl;
+ return;
+ }
+
+ d->stream->write( request );
+}
+
+void Client::debug(const QString &str)
+{
+ qDebug( "CLIENT: %s", str.ascii() );
+}
+
+Task * Client::rootTask()
+{
+ return d->root;
+}
+
+void Client::initTasks()
+{
+ if( d->tasksInitialized )
+ return;
+
+ d->statusTask = new StatusNotifierTask( d->root );
+ QObject::connect( d->statusTask, SIGNAL( statusChanged( const QString&, int, const QString&, int, int ) ),
+ SIGNAL( statusChanged( const QString&, int, const QString&, int, int ) ) );
+ QObject::connect( d->statusTask, SIGNAL( stealthStatusChanged( const QString&, Yahoo::StealthStatus ) ),
+ SIGNAL( stealthStatusChanged( const QString&, Yahoo::StealthStatus ) ) );
+ QObject::connect( d->statusTask, SIGNAL( loginResponse( int, const QString& ) ),
+ SLOT( slotLoginResponse( int, const QString& ) ) );
+ QObject::connect( d->statusTask, SIGNAL( authorizationRejected( const QString&, const QString& ) ),
+ SIGNAL( authorizationRejected( const QString&, const QString& ) ) );
+ QObject::connect( d->statusTask, SIGNAL( authorizationAccepted( const QString& ) ),
+ SIGNAL( authorizationAccepted( const QString& ) ) );
+ QObject::connect( d->statusTask, SIGNAL( gotAuthorizationRequest( const QString &, const QString &, const QString & ) ),
+ SIGNAL( gotAuthorizationRequest( const QString &, const QString &, const QString & ) ) );
+ QObject::connect( d->statusTask, SIGNAL( gotPictureChecksum( const QString &, int ) ),
+ SIGNAL( pictureChecksumNotify( const QString &, int ) ) );
+
+ d->mailTask = new MailNotifierTask( d->root );
+ QObject::connect( d->mailTask, SIGNAL( mailNotify(const QString&, const QString&, int) ),
+ SIGNAL( mailNotify(const QString&, const QString&, int) ) );
+
+ d->messageReceiverTask = new MessageReceiverTask( d->root );
+ QObject::connect( d->messageReceiverTask, SIGNAL( gotIm(const QString&, const QString&, long, int) ),
+ SIGNAL( gotIm(const QString&, const QString&, long, int) ) );
+ QObject::connect( d->messageReceiverTask, SIGNAL( systemMessage(const QString&) ),
+ SIGNAL( systemMessage(const QString&) ) );
+ QObject::connect( d->messageReceiverTask, SIGNAL( gotTypingNotify(const QString &, int) ),
+ SIGNAL( typingNotify(const QString &, int) ) );
+ QObject::connect( d->messageReceiverTask, SIGNAL( gotBuzz( const QString &, long ) ),
+ SIGNAL( gotBuzz( const QString &, long ) ) );
+ QObject::connect( d->messageReceiverTask, SIGNAL( gotWebcamInvite(const QString &) ),
+ SIGNAL( gotWebcamInvite(const QString &) ) );
+
+ d->pictureNotifierTask = new PictureNotifierTask( d->root );
+ QObject::connect( d->pictureNotifierTask, SIGNAL( pictureStatusNotify( const QString &, int ) ),
+ SIGNAL( pictureStatusNotify( const QString &, int ) ) );
+ QObject::connect( d->pictureNotifierTask, SIGNAL( pictureChecksumNotify( const QString &, int ) ),
+ SIGNAL( pictureChecksumNotify( const QString &, int ) ) );
+ QObject::connect( d->pictureNotifierTask, SIGNAL( pictureInfoNotify( const QString &, KURL, int ) ),
+ SIGNAL( pictureInfoNotify( const QString &, KURL, int ) ) );
+ QObject::connect( d->pictureNotifierTask, SIGNAL( pictureRequest( const QString & ) ),
+ SIGNAL( pictureRequest( const QString & ) ) );
+ QObject::connect( d->pictureNotifierTask, SIGNAL( pictureUploaded( const QString & ) ),
+ SIGNAL( pictureUploaded( const QString & ) ) );
+
+ d->webcamTask = new WebcamTask( d->root );
+ QObject::connect( d->webcamTask, SIGNAL( webcamImageReceived( const QString &, const QPixmap &) ),
+ SIGNAL( webcamImageReceived( const QString &, const QPixmap &) ) );
+ QObject::connect( d->webcamTask, SIGNAL( webcamNotAvailable( const QString & ) ),
+ SIGNAL( webcamNotAvailable( const QString & ) ) );
+ QObject::connect( d->webcamTask, SIGNAL( webcamClosed( const QString &, int ) ),
+ SIGNAL( webcamClosed( const QString &, int ) ) );
+ QObject::connect( d->webcamTask, SIGNAL( webcamPaused(const QString&) ),
+ SIGNAL( webcamPaused(const QString&) ) );
+ QObject::connect( d->webcamTask, SIGNAL( readyForTransmission() ),
+ SIGNAL( webcamReadyForTransmission() ) );
+ QObject::connect( d->webcamTask, SIGNAL( stopTransmission() ),
+ SIGNAL( webcamStopTransmission() ) );
+ QObject::connect( d->webcamTask, SIGNAL( viewerJoined( const QString &) ),
+ SIGNAL( webcamViewerJoined( const QString &) ) );
+ QObject::connect( d->webcamTask, SIGNAL( viewerLeft( const QString &) ),
+ SIGNAL( webcamViewerLeft( const QString &) ) );
+ QObject::connect( d->webcamTask, SIGNAL( viewerRequest( const QString &) ),
+ SIGNAL( webcamViewerRequest( const QString &) ) );
+
+ d->conferenceTask = new ConferenceTask( d->root );
+ QObject::connect( d->conferenceTask, SIGNAL( gotInvite( const QString &, const QString &, const QString &, const QStringList & ) ),
+ SIGNAL( gotConferenceInvite( const QString &, const QString &, const QString &, const QStringList & ) ) );
+ QObject::connect( d->conferenceTask, SIGNAL( gotMessage( const QString &, const QString &, const QString & ) ),
+ SIGNAL( gotConferenceMessage( const QString &, const QString &, const QString & ) ) );
+ QObject::connect( d->conferenceTask, SIGNAL( userJoined( const QString &, const QString & ) ),
+ SIGNAL( confUserJoined( const QString &, const QString & ) ) );
+ QObject::connect( d->conferenceTask, SIGNAL( userLeft( const QString &, const QString & ) ),
+ SIGNAL( confUserLeft( const QString &, const QString & ) ) );
+ QObject::connect( d->conferenceTask, SIGNAL( userDeclined( const QString &, const QString &, const QString & ) ),
+ SIGNAL( confUserDeclined( const QString &, const QString &, const QString & ) ) );
+
+ d->yabTask = new YABTask( d->root );
+ QObject::connect( d->yabTask, SIGNAL( gotEntry( YABEntry * ) ),
+ SIGNAL( gotYABEntry( YABEntry * ) ) );
+ QObject::connect( d->yabTask, SIGNAL( gotRevision( long, bool ) ),
+ SIGNAL( gotYABRevision( long, bool ) ) );
+
+ d->fileTransferTask = new FileTransferNotifierTask( d->root );
+ QObject::connect( d->fileTransferTask, SIGNAL(incomingFileTransfer( const QString &, const QString &,
+ long, const QString &, const QString &, unsigned long )),
+ SIGNAL(incomingFileTransfer( const QString &, const QString &,
+ long, const QString &, const QString &, unsigned long )) );
+}
+
+void Client::deleteTasks()
+{
+ d->tasksInitialized = false;
+ d->statusTask->deleteLater();
+ d->statusTask = 0L;
+ d->mailTask->deleteLater();
+ d->mailTask = 0L;
+ d->messageReceiverTask->deleteLater();
+ d->messageReceiverTask = 0L;
+ d->pictureNotifierTask->deleteLater();
+ d->pictureNotifierTask = 0L;
+ d->webcamTask->deleteLater();
+ d->webcamTask = 0L;
+ d->conferenceTask->deleteLater();
+ d->conferenceTask = 0L;
+ d->yabTask->deleteLater();
+ d->yabTask = 0L;
+ d->fileTransferTask->deleteLater();
+ d->fileTransferTask = 0;
+}
+
+#include "client.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/client.h b/kopete/protocols/yahoo/libkyahoo/client.h
new file mode 100644
index 00000000..711336f0
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/client.h
@@ -0,0 +1,618 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2005-2006 André Duffeck <andre.duffeck@kdemail.net>
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+ Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LIBYAHOO_CLIENT_H
+#define LIBYAHOO_CLIENT_H
+
+#include <qobject.h>
+
+#include "transfer.h"
+#include "yahootypes.h"
+
+#define YMSG_PROGRAM_VERSION_STRING "7,5,0,33"
+
+class QString;
+class QTimer;
+class ClientStream;
+class KNetworkConnector;
+class Task;
+class KURL;
+class KTempFile;
+class YABEntry;
+class SendFileTask;
+
+class Client : public QObject
+{
+Q_OBJECT
+
+ public:
+
+ /*************
+ EXTERNAL API
+ *************/
+
+ enum LogLevel { Debug, Info, Notice, Warning, Error, Critical };
+
+ Client(QObject *parent=0);
+ ~Client();
+ void setUserId( const QString& userName );
+
+ /**
+ * 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 indicate whether we're connecting to the authorizer or the bos server
+ */
+ void connect( const QString &host, const uint port, const QString &userId, const QString &pass );
+
+ /**
+ * Cancel active login attemps
+ */
+ void cancelConnect();
+
+ /**
+ * Logout and disconnect
+ */
+ void close();
+
+ /**
+ * Returns the errorcode
+ */
+ int error();
+
+ /**
+ * Returns a description of the error
+ */
+ QString errorString();
+
+ /**
+ * Returns information about what went wrong
+ */
+ QString errorInformation();
+
+ /**
+ * Specifies the status we connect with.
+ * The Yahoo protocol supports connecting into Online and Invisible state.
+ * If status is any other status the Client connects into Online state and changes into the specified state after the login.
+ * @param status the status to connect with
+ */
+ void setStatusOnConnect( Yahoo::Status status );
+
+ /**
+ * Specifies the status message we connect with.
+ * The Yahoo protocol does not support connecting with a status message. If msg is not empty the Client
+ * will change the status message after the login.
+ * @param msg the status message to connect with
+ */
+ void setStatusMessageOnConnect( const QString &msg );
+
+ /**
+ * Accessors needed for login
+ */
+ QString host();
+ int port();
+
+ /**
+ * return the pictureFlag describing the status of our buddy icon
+ * 0 = no icon, 2 = icon, 1 = avatar (?)
+ */
+ int pictureFlag();
+
+ /**
+ * set the pictureFlag describing the status of our buddy icon
+ */
+ void setPictureFlag( int flag );
+
+ /**
+ * Send a Typing notification
+ * @param to the buddy that should be notified
+ * @param typing true if there is typing activity, false if not
+ */
+ void sendTyping( const QString &to, bool typing );
+
+ /**
+ * Send a Message
+ * @param to the buddy that should receive the message
+ * @param msg the message
+ */
+ void sendMessage( const QString &to, const QString &msg );
+
+ /**
+ * Register / Unregister a chatsession
+ * @param to the buddy, the chatsession belongs to
+ * @param close if true, the chatsession will be closed, if false, it will be opened
+ */
+ void setChatSessionState( const QString &to, bool close );
+
+ /**
+ * Send a Buzz
+ * @param to the buddy that should receive the buzz
+ */
+ void sendBuzz( const QString &to );
+
+ /**
+ * Change our status
+ * @param status the status that will be set
+ * @param message the status message that will be set
+ * @param type Yahoo::StatusTypeAvailable means that the user is available, Yahoo::StatusTypeAway means that the user is away from the keyboard
+ */
+ void changeStatus(Yahoo::Status status, const QString &message, Yahoo::StatusType type);
+
+ /**
+ * Set the verification word that is needed for a account verification after
+ * too many wrong login attempts.
+ * @param word the verification word
+ */
+ void setVerificationWord( const QString &word );
+
+ /**
+ * Add a buddy to the contact list
+ * @param userId the yahoo ID of the buddy that should be added
+ * @param group the group where the buddy will be placed
+ * @param message the message that will be sent to the buddy along the authorization request
+ */
+ void addBuddy( const QString &userId, const QString &group, const QString &message = QString::fromLatin1("Please add me") );
+
+ /**
+ * Remove a buddy from the contact list
+ */
+ void removeBuddy( const QString &userId, const QString &group );
+
+ /**
+ * Move a buddy into another group
+ */
+ void moveBuddy( const QString &userId, const QString &oldGroup, const QString &newGroup );
+
+ /**
+ * Change the stealth status of a buddy
+ */
+ void stealthContact( QString const &userId, Yahoo::StealthMode mode, Yahoo::StealthStatus state );
+
+ /**
+ * Request the buddy's picture
+ */
+ void requestPicture( const QString &userId );
+
+ /**
+ * Download the buddy's picture
+ */
+ void downloadPicture( const QString &userId, KURL url, int checksum );
+
+ /**
+ * Send our picture
+ */
+ void uploadPicture( KURL url );
+
+ /**
+ * Send checksum of our picture
+ */
+ void sendPictureChecksum( int checksum, const QString & );
+
+ /**
+ * Send information about our picture
+ */
+ void sendPictureInformation( const QString &userId, const QString &url, int checksum );
+
+ /**
+ * Notify the buddies about our new status
+ */
+ void sendPictureStatusUpdate( const QString &userId, int type );
+
+ /**
+ * Send a response to the webcam invite ( Accept / Decline )
+ */
+ void requestWebcam( const QString &userId );
+
+ /**
+ * Stop receiving of webcam
+ */
+ void closeWebcam( const QString &userId );
+
+ /**
+ * Invite the user to view your Webcam
+ */
+ void sendWebcamInvite( const QString &userId );
+
+ /**
+ * transmit a new image to the watchers
+ */
+ void sendWebcamImage( const QByteArray &image );
+
+ /**
+ * Stop transmission
+ */
+ void closeOutgoingWebcam();
+
+ /**
+ * Allow a buddy to watch the cam
+ */
+ void grantWebcamAccess( const QString &userId );
+
+ /**
+ * Invite buddies to a conference
+ */
+ void inviteConference( const QString &room, const QStringList &members, const QString &msg );
+
+ /**
+ * Invite buddies to a already existing conference
+ */
+ void addInviteConference( const QString &room, const QStringList &who, const QStringList &members, const QString &msg );
+
+ /**
+ * Join a conference
+ */
+ void joinConference( const QString &room, const QStringList &members );
+
+ /**
+ * Decline to join a conference
+ */
+ void declineConference( const QString &room, const QStringList &members, const QString &msg );
+
+ /**
+ * Leave the conference
+ */
+ void leaveConference( const QString &room, const QStringList &members );
+
+ /**
+ * Send a message to the conference
+ */
+ void sendConferenceMessage( const QString &room, const QStringList &members, const QString &msg );
+
+ /**
+ * Send a authorization request response
+ */
+ void sendAuthReply( const QString &userId, bool accept, const QString &msg );
+
+ /**
+ * Fetches all entries of the YAB
+ */
+ void getYABEntries( long lastMerge, long lastRemoteRevision );
+
+ /**
+ * Saves a modified YAB entry
+ */
+ void saveYABEntry( YABEntry &entry );
+
+ /**
+ * Creates a new YAB entry
+ */
+ void addYABEntry( YABEntry &entry );
+
+ /**
+ * Deletes a YAB entry
+ */
+ void deleteYABEntry( YABEntry &entry );
+
+ /**
+ * Send a file to a buddy
+ */
+ void sendFile( unsigned int transferId, const QString &userId, const QString &msg, KURL url );
+
+ /**
+ * Receive a file from a buddy
+ */
+ void receiveFile( unsigned int transferId, const QString &userId, KURL remoteURL, KURL localURL );
+
+ /**
+ * Reject a file offered by a buddy
+ */
+ void rejectFile( const QString &userId, KURL remoteURL );
+
+ /**
+ * The user canceled the filetransfer
+ */
+ void cancelFileTransfer( unsigned int transferId );
+
+ /*************
+ INTERNAL (FOR USE BY TASKS) METHODS
+ *************/
+ /**
+ * Send an outgoing request to the server
+ */
+ void send( Transfer *request );
+
+ /**
+ * Print a debug statement
+ */
+ void debug( const QString &str );
+
+ /**
+ * The current user's user ID
+ */
+ QString userId();
+
+ /**
+ * The current user's password
+ */
+ QString password();
+
+ /**
+ * Host's IP address
+ */
+ QCString ipAddress();
+
+ /**
+ * current Session ID
+ */
+ uint sessionID();
+
+ /**
+ * Get our status
+ */
+ Yahoo::Status status();
+
+ /**
+ * Set our status
+ */
+ void setStatus( Yahoo::Status );
+
+ /**
+ * Access the root Task for this client, so tasks may be added to it.
+ */
+ Task* rootTask();
+
+ /**
+ * Accessors to the cookies
+ */
+ QString yCookie();
+ QString tCookie();
+ QString cCookie();
+
+ /**
+ * Error
+ */
+ void notifyError( const QString &info, const QString &errorString, LogLevel level );
+ signals:
+ /** CONNECTION EVENTS */
+ /**
+ * Notifies that the login process has succeeded.
+ */
+ void loggedIn( int, const QString& );
+
+ /**
+ * Notifies that the login process has failed
+ */
+ void loginFailed();
+
+ /**
+ * Notifies tasks and account so they can react properly
+ */
+ void connected();
+ /**
+ * Notifies tasks and account so they can react properly
+ */
+ void disconnected();
+ /**
+ * We were disconnected because we connected elsewhere
+ */
+ void connectedElsewhere();
+
+ void error( int level );
+ /**
+ * Notifies about our buddies and groups
+ */
+ void gotBuddy( const QString &, const QString &, const QString & );
+ /**
+ * Notifies about the status of online buddies
+ */
+ void statusChanged( const QString&, int, const QString&, int, int );
+ /**
+ * Notifies about the stealth status of buddies
+ */
+ void stealthStatusChanged( const QString &, Yahoo::StealthStatus );
+ /**
+ * Notifies about mails
+ */
+ void mailNotify( const QString&, const QString&, int );
+ /**
+ * We got a new message
+ */
+ void gotIm( const QString&, const QString&, long, int );
+ /**
+ * We got a new system message
+ */
+ void systemMessage( const QString& );
+ /**
+ * The buddy is typing a message
+ */
+ void typingNotify( const QString &, int );
+ /**
+ * The buddy has invited us to view his webcam
+ */
+ void gotWebcamInvite(const QString &);
+ /**
+ * Notifies about a BUZZ notification
+ */
+ void gotBuzz( const QString &, long );
+ /**
+ * Notifies about a changed picture status
+ */
+ void pictureStatusNotify( const QString &, int );
+ /**
+ * Notifies about a picture checksum
+ */
+ void pictureChecksumNotify( const QString &, int );
+ /**
+ * Notifies about a picture
+ */
+ void pictureInfoNotify( const QString &, KURL, int );
+ /**
+ * The iconLoader has successfully downloaded a picutre
+ */
+ void pictureDownloaded( const QString &, KTempFile *, int );
+ /**
+ * A Buddy asks for our picture
+ */
+ void pictureRequest( const QString & );
+ /**
+ * Information about the picture upload
+ */
+ void pictureUploaded( const QString & );
+ /**
+ * We've received a webcam image from a buddy
+ */
+ void webcamImageReceived( const QString &, const QPixmap &);
+ /**
+ * The requested Webcam is not available
+ */
+ void webcamNotAvailable( const QString & );
+ /**
+ * The connection to the webcam was closed
+ */
+ void webcamClosed( const QString &, int );
+ /**
+ * The webcamtransmission is paused
+ */
+ void webcamPaused(const QString&);
+ /**
+ * The webcam connection is ready for transmission
+ */
+ void webcamReadyForTransmission();
+ /**
+ * The webcam should stop sending images
+ */
+ void webcamStopTransmission();
+ /**
+ * A new buddy watches the cam
+ */
+ void webcamViewerJoined( const QString & );
+ /**
+ * A buddy no longer watches the cam
+ */
+ void webcamViewerLeft( const QString & );
+ /**
+ * A buddy wants to watch the cam
+ */
+ void webcamViewerRequest( const QString & );
+ /**
+ * A buddy invited us to a conference
+ */
+ void gotConferenceInvite( const QString &, const QString &, const QString &, const QStringList & );
+ /**
+ * A conference message was received
+ */
+ void gotConferenceMessage( const QString &, const QString &, const QString & );
+ /**
+ * A buddy joined the conference
+ */
+ void confUserJoined( const QString &, const QString & );
+ /**
+ * A buddy left the conference
+ */
+ void confUserLeft( const QString &, const QString & );
+ /**
+ * A buddy declined to join the conference
+ */
+ void confUserDeclined( const QString &, const QString &, const QString & );
+ /**
+ * A buddy accepted our authorization request
+ */
+ void authorizationAccepted( const QString & );
+ /**
+ * A buddy rejected our authorization request
+ */
+ void authorizationRejected( const QString &, const QString & );
+ /**
+ * A buddy requests authorization
+ */
+ void gotAuthorizationRequest( const QString &, const QString &, const QString & );
+ /**
+ * A revision of the Yahoo Addressbook was received
+ */
+ void gotYABRevision( long rev, bool merged );
+ /**
+ * A entry from the Yahoo Addressbook was retrieved
+ */
+ void gotYABEntry( YABEntry * );
+ /**
+ * An error occured while saving a Yahoo Addressbook entry
+ */
+ void modifyYABEntryError( YABEntry *, const QString & );
+ /**
+ * number of Bytes transferred for FileTransfer id
+ */
+ void fileTransferBytesProcessed( unsigned int, unsigned int );
+ /**
+ * filetransfer completed
+ */
+ void fileTransferComplete( unsigned int );
+ /**
+ * An error occured during the filetransfer
+ */
+ void fileTransferError( unsigned int, int, const QString & );
+ /**
+ * filetransfer canceled
+ */
+ void fileTransferCanceled( unsigned int );
+ /**
+ * A buddy is trying to send us a file
+ */
+ void incomingFileTransfer( const QString &, const QString &, long, const QString &,
+ const QString &, unsigned long );
+ protected slots:
+ // INTERNAL, FOR USE BY TASKS' finished() SIGNALS //
+ void lt_loginFinished();
+ void lt_gotSessionID( uint );
+ void cs_connected();
+ void slotGotCookies();
+
+ /**
+ * Used by tasks to identify a response to a login attempt
+ */
+ void slotLoginResponse( int, const QString& );
+
+ /**
+ * 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();
+
+ /**
+ * Send a Yahoo Ping packet to the server
+ */
+ void sendPing();
+ private:
+ void distribute( Transfer *transfer );
+
+ /**
+ * create static tasks and connect their signals
+ */
+ void initTasks();
+
+ /**
+ * remove static tasks and their singal connections
+ */
+ void deleteTasks();
+
+ class ClientPrivate;
+ ClientPrivate* d;
+ KNetworkConnector *m_connector;
+
+ QTimer *m_pingTimer;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/conferencetask.cpp b/kopete/protocols/yahoo/libkyahoo/conferencetask.cpp
new file mode 100644
index 00000000..5f68eaa0
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/conferencetask.cpp
@@ -0,0 +1,259 @@
+/*
+ Kopete Yahoo Protocol
+ Handles conferences
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "conferencetask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kdebug.h>
+
+ConferenceTask::ConferenceTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+ConferenceTask::~ConferenceTask()
+{
+}
+
+bool ConferenceTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = 0L;
+ t = static_cast<YMSGTransfer*>(transfer);
+
+ if( t->service() == Yahoo::ServiceConfInvite ||
+ t->service() == Yahoo::ServiceConfAddInvite)
+ parseInvitation( t );
+ else if( t->service() == Yahoo::ServiceConfMsg )
+ parseMessage( t );
+ else if( t->service() == Yahoo::ServiceConfLogon )
+ parseUserJoined( t );
+ else if( t->service() == Yahoo::ServiceConfLogoff )
+ parseUserLeft( t );
+ else if( t->service() == Yahoo::ServiceConfDecline )
+ parseUserDeclined( t );
+
+ return true;
+}
+
+bool ConferenceTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if ( t->service() == Yahoo::ServiceConfInvite ||
+ t->service() == Yahoo::ServiceConfLogon ||
+ t->service() == Yahoo::ServiceConfDecline ||
+ t->service() == Yahoo::ServiceConfLogoff ||
+ t->service() == Yahoo::ServiceConfAddInvite ||
+ t->service() == Yahoo::ServiceConfMsg )
+ return true;
+ else
+ return false;
+}
+
+void ConferenceTask::parseInvitation( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ int i = 0;
+ QString who = t->firstParam( 50 );
+ QString room = t->firstParam( 57 );
+ bool utf = QString( t->firstParam( 97 ) ).toInt() == 1;
+ QString msg;
+ if( utf )
+ msg = QString::fromUtf8( t->firstParam( 58 ) );
+ else
+ msg = t->firstParam( 58 );
+
+ QStringList members;
+ for( i = 0; i < t->paramCount( 52 ); i++ )
+ members.append( t->nthParam( 52, i ) );
+ for( i = 0; i < t->paramCount( 53 ); i++ )
+ members.append( t->nthParam( 53, i ) );
+ if( who == client()->userId() )
+ return;
+
+ if( !who.isEmpty() && !room.isEmpty() )
+ emit gotInvite( who, room, msg, members );
+}
+
+void ConferenceTask::parseMessage( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString room = t->firstParam( 57 );
+ QString from = t->firstParam( 3 );
+ bool utf = QString( t->firstParam( 97 ) ).toInt() == 1;
+ QString msg;
+ if( utf )
+ msg = QString::fromUtf8( t->firstParam( 14 ) );
+ else
+ msg = t->firstParam( 14 );
+
+ if( !msg.isEmpty() )
+ emit gotMessage( from, room, msg );
+}
+
+void ConferenceTask::parseUserJoined( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString room = t->firstParam( 57 );
+ QString who = t->firstParam( 53 );
+
+ if( !who.isEmpty() && !room.isEmpty() )
+ emit userJoined( who, room );
+}
+
+void ConferenceTask::parseUserLeft( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString room = t->firstParam( 57 );
+ QString who = t->firstParam( 56 );
+
+ if( !who.isEmpty() && !room.isEmpty() )
+ emit userLeft( who, room );
+}
+
+void ConferenceTask::parseUserDeclined( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString room = t->firstParam( 57 );
+ QString who = t->firstParam( 54 );
+ QString msg = t->firstParam( 14 );
+
+ if( !who.isEmpty() && !room.isEmpty() )
+ emit userDeclined( who, room, msg );
+}
+
+void ConferenceTask::inviteConference( const QString &room, const QStringList &members, const QString &msg )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfInvite);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 50, client()->userId().local8Bit() );
+ t->setParam( 57, room.local8Bit() );
+ t->setParam( 58, msg.local8Bit() );
+ t->setParam( 97, 1 );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ t->setParam( 52, (*it).local8Bit() );
+ t->setParam( 13, "0" );
+
+ send( t );
+}
+
+void ConferenceTask::addInvite( const QString &room, const QStringList &who, const QStringList &members, const QString &msg )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfAddInvite);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+
+ QString whoList = who.first();
+ for( uint i = 1; i < who.size(); i++ )
+ whoList += QString(",%1").arg( who[i] );
+ t->setParam( 51, whoList.local8Bit() );
+
+ t->setParam( 57, room.local8Bit() );
+ t->setParam( 58, msg.local8Bit() );
+ t->setParam( 97, 1 );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ {
+ t->setParam( 52, (*it).local8Bit() );
+ t->setParam( 53, (*it).local8Bit() ); // Note: this field should only be set if the buddy has already joined the conference, but no harm is done this way
+ }
+ t->setParam( 13, "0" );
+
+ send( t );
+}
+
+void ConferenceTask::joinConference( const QString &room, const QStringList &members )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfLogon);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ t->setParam( 3, (*it).local8Bit() );
+ t->setParam( 57, room.local8Bit() );
+
+ send( t );
+}
+
+void ConferenceTask::declineConference( const QString &room, const QStringList &members, const QString &msg )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfDecline);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ t->setParam( 3, (*it).local8Bit() );
+ t->setParam( 57, room.local8Bit() );
+ t->setParam( 14, msg.utf8() );
+ t->setParam( 97, 1 );
+
+ send( t );
+}
+void ConferenceTask::leaveConference( const QString &room, const QStringList &members )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfLogoff);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ t->setParam( 3, (*it).local8Bit() );
+ t->setParam( 57, room.local8Bit() );
+
+ send( t );
+}
+
+void ConferenceTask::sendMessage( const QString &room, const QStringList &members, const QString &msg )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceConfMsg);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ for( QStringList::const_iterator it = members.begin(); it != members.end(); it++ )
+ t->setParam( 53, (*it).local8Bit() );
+ t->setParam( 57, room.local8Bit() );
+ t->setParam( 14, msg.utf8() );
+ t->setParam( 97, 1 );
+
+ send( t );
+}
+#include "conferencetask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/conferencetask.h b/kopete/protocols/yahoo/libkyahoo/conferencetask.h
new file mode 100644
index 00000000..b6649a93
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/conferencetask.h
@@ -0,0 +1,57 @@
+/*
+ Kopete Yahoo Protocol
+ Handles conferences
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "task.h"
+
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class ConferenceTask : public Task
+{
+ Q_OBJECT
+public:
+ ConferenceTask(Task *parent);
+ ~ConferenceTask();
+
+ bool take(Transfer *transfer);
+ bool forMe( Transfer* transfer ) const;
+
+ void joinConference( const QString &room, const QStringList &members );
+ void declineConference( const QString &room, const QStringList &members, const QString &msg );
+ void leaveConference( const QString &room, const QStringList &members );
+ void sendMessage( const QString &room, const QStringList &members, const QString &msg );
+ void inviteConference( const QString &room, const QStringList &members, const QString &msg );
+ void addInvite( const QString &room, const QStringList &who, const QStringList &members, const QString &msg );
+signals:
+ void gotInvite( const QString &who, const QString &room, const QString &msg, const QStringList &members);
+ void gotMessage( const QString &who, const QString &room, const QString &msg );
+ void userJoined( const QString &who, const QString &room );
+ void userLeft( const QString &who, const QString &room );
+ void userDeclined( const QString &who, const QString &room, const QString &msg );
+private:
+ void parseInvitation( YMSGTransfer *transfer );
+ void parseMessage( YMSGTransfer *transfer );
+ void parseUserJoined( YMSGTransfer *transfer );
+ void parseUserLeft( YMSGTransfer *transfer );
+ void parseUserDeclined( YMSGTransfer *transfer );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/configure.in.in b/kopete/protocols/yahoo/libkyahoo/configure.in.in
new file mode 100644
index 00000000..7b819074
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/configure.in.in
@@ -0,0 +1,38 @@
+YAHOO2_VERSION=""
+AC_SUBST(YAHOO2_VERSION)
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_BIGENDIAN
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_SIZE_T
+AC_STRUCT_TM
+
+AC_CHECK_TYPE([uint8_t],,
+[AC_DEFINE([uint8_t], [unsigned char],
+[Define to `unsigned char' if not defined.])])
+AC_CHECK_TYPE([uint32_t],,
+[AC_DEFINE([uint32_t], [unsigned int],
+[Define to `unsigned int' if not defined.])])
+AC_CHECK_TYPE([uint64_t],,
+[AC_DEFINE([uint64_t], [unsigned long long],
+[Define to `unsigned long long' if not defined.])])
+
+dnl Checks for library functions.
+AC_CHECK_FUNCS(strerror)
+
+# Checks for library functions.
+
+AC_ARG_WITH([struct-callbacks], [AC_HELP_STRING([--with-struct-callbacks],
+[use a callback structure instead of callback functions])])
+if test "$with_struct_callbacks" = "yes"; then
+ AC_DEFINE(USE_STRUCT_CALLBACKS, 1,
+ [Define if you want to use a callback structure instead of callback functions])
+fi
+
+enable_sample_client="no"
+AM_CONDITIONAL(SAMPLE_CLIENT, test "$enable_sample_client" != "no")
+
+YAHOOPKGREQ=""
+AC_SUBST(YAHOOPKGREQ)
+
diff --git a/kopete/protocols/yahoo/libkyahoo/connector.cpp b/kopete/protocols/yahoo/libkyahoo/connector.cpp
new file mode 100644
index 00000000..6ae174e8
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/connector.cpp
@@ -0,0 +1,62 @@
+/*
+ Kopete Oscar Protocol
+ connector.cpp - the Oscar socket connector
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "connector.h"
+
+Connector::Connector(QObject *parent)
+:QObject(parent)
+{
+ setPeerAddressNone();
+}
+
+Connector::~Connector()
+{
+}
+
+bool Connector::havePeerAddress() const
+{
+ return haveaddr;
+}
+
+QHostAddress Connector::peerAddress() const
+{
+ return addr;
+}
+
+Q_UINT16 Connector::peerPort() const
+{
+ return port;
+}
+
+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/yahoo/libkyahoo/connector.h b/kopete/protocols/yahoo/libkyahoo/connector.h
new file mode 100644
index 00000000..70e01f3d
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/connector.h
@@ -0,0 +1,59 @@
+/*
+ Kopete Oscar Protocol
+ connector.h - the Oscar socket connector
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LIBKYAHOO_CONNECTOR_H
+#define LIBKYAHOO_CONNECTOR_H
+
+
+#include <qobject.h>
+#include "qhostaddress.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 havePeerAddress() const;
+ QHostAddress peerAddress() const;
+ Q_UINT16 peerPort() const;
+
+signals:
+ void connected();
+ void error();
+
+protected:
+ void setPeerAddressNone();
+ void setPeerAddress(const QHostAddress &addr, Q_UINT16 port);
+
+private:
+ bool haveaddr;
+ QHostAddress addr;
+ Q_UINT16 port;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/coreprotocol.cpp b/kopete/protocols/yahoo/libkyahoo/coreprotocol.cpp
new file mode 100644
index 00000000..b05cb16d
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/coreprotocol.cpp
@@ -0,0 +1,228 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Based on code
+ Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <string.h>
+#include <iostream>
+
+#include <qdatastream.h>
+#include <qdatetime.h>
+#include <qtextstream.h>
+
+
+#include <kdebug.h>
+#include <kurl.h>
+
+#include "coreprotocol.h"
+#include "ymsgprotocol.h"
+#include "ymsgtransfer.h"
+
+CoreProtocol::CoreProtocol() : QObject()
+{
+ m_YMSGProtocol = new YMSGProtocol( this, "ymsgprotocol" );
+}
+
+CoreProtocol::~CoreProtocol()
+{
+}
+
+int CoreProtocol::state()
+{
+ return m_state;
+}
+
+void CoreProtocol::addIncomingData( const QByteArray & incomingBytes )
+{
+ // store locally
+ int oldsize = m_in.size();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << incomingBytes.size() << " bytes. already had " << oldsize << " bytes" << endl;
+
+ 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++;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " parsed transfer " << transferCount << " in chunk of "<< parsedBytes << " bytes" << endl;
+ int size = m_in.size();
+ if ( parsedBytes < size )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " more data in chunk! ( I have parsed " << parsedBytes << " and total data of " << size << ")" << endl;
+ // 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 )
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " message was incomplete, waiting for more..." << endl;
+ /*
+ if ( m_eventProtocol->state() == EventProtocol::OutOfSync )
+ {
+ qDebug( " - protocol thinks it's out of sync, discarding the rest of the buffer and hoping the server regains sync soon..." );
+ m_in.truncate( 0 );
+ }
+ */
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " done processing chunk" << endl;
+
+}
+
+Transfer* CoreProtocol::incomingTransfer()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ if ( m_state == Available )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - got a transfer" << endl;
+ m_state = NoData;
+ return m_inTransfer;
+ m_inTransfer = 0;
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " no milk today" << endl;
+ return 0;
+ }
+}
+
+void cp_dump( const QByteArray &bytes )
+{
+#ifdef YAHOO_COREPROTOCOL_DEBUG
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " contains " << bytes.count() << " bytes" << endl;
+ for ( uint i = 0; i < bytes.count(); ++i )
+ {
+ printf( "%02x ", bytes[ i ] );
+ }
+ printf( "\n" );
+#else
+ Q_UNUSED( bytes );
+#endif
+}
+
+void CoreProtocol::outgoingTransfer( Transfer* outgoing )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ if ( outgoing->type() == Transfer::YMSGTransfer )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " got YMSGTransfer" << endl;
+ YMSGTransfer *yt = (YMSGTransfer *) outgoing;
+ QByteArray bytesOut = yt->serialize();
+
+ //QTextStream dout( bytesOut, IO_WriteOnly );
+ //dout.setEncoding( QTextStream::Latin1 );
+ //dout.setByteOrder( QDataStream::LittleEndian );
+ //dout << bytesOut;
+ //kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " " << bytesOut << endl;
+ emit outgoingData( bytesOut );
+ // now convert
+ //fieldsToWire( fields );
+ }
+ delete outgoing;
+}
+
+
+
+int CoreProtocol::wireToTransfer( const QByteArray& wire )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ // processing incoming data and reassembling it into transfers
+ // may be an event or a response
+
+ uint bytesParsed = 0;
+
+ if ( wire.size() < 20 ) // minimal value of a YMSG header
+ {
+ m_state = NeedMore;
+ return bytesParsed;
+ }
+
+ QDataStream din( wire, IO_ReadOnly );
+
+ // look at first four bytes and decide what to do with the chunk
+ if ( okToProceed( din ) )
+ {
+ if ( (wire[0] == 'Y') && (wire[1] == 'M') && (wire[2] == 'S') && (wire[3] == 'G'))
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - looks like a valid YMSG packet" << endl;
+ Transfer *t = m_YMSGProtocol->parse( wire, bytesParsed );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - YMSG Protocol parsed " << bytesParsed << " bytes" << endl;
+ if ( t )
+ {
+ m_inTransfer = t;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - got a valid packet " << endl;
+
+ m_state = Available;
+ emit incomingData();
+ }
+ else
+ bytesParsed = 0;
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - not a valid YMSG packet. Trying to recover: " << wire << endl;
+ QTextStream s( wire, IO_ReadOnly );
+ QString remaining = s.read();
+ int pos = remaining.find( "YMSG", bytesParsed );
+ if( pos >= 0 )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Recover successful." << endl;
+ bytesParsed += pos;
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Recover failed. Dump it!" << endl;
+ bytesParsed = wire.size();
+ }
+ }
+ }
+ return bytesParsed;
+}
+
+void CoreProtocol::reset()
+{
+ m_in.resize( 0 );
+}
+
+void CoreProtocol::slotOutgoingData( const QCString &out )
+{
+ qDebug( "%s", out.data() );
+}
+
+bool CoreProtocol::okToProceed( QDataStream &din)
+{
+ if ( din.atEnd() )
+ {
+ m_state = NeedMore;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " saved message prematurely" << endl;
+ return false;
+ }
+ else
+ return true;
+}
+
+#include "coreprotocol.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/coreprotocol.h b/kopete/protocols/yahoo/libkyahoo/coreprotocol.h
new file mode 100644
index 00000000..fb78aa39
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/coreprotocol.h
@@ -0,0 +1,107 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Based on code
+ Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOO_CORE_PROTOCOL_H
+#define YAHOO_CORE_PROTOCOL_H
+
+#include <qcstring.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+class Transfer;
+class YMSGProtocol;
+
+class CoreProtocol : public QObject
+{
+Q_OBJECT
+public:
+ enum State { NeedMore, Available, NoData, OutOfSync };
+
+ CoreProtocol();
+
+ virtual ~CoreProtocol();
+
+ /**
+ * Reset the protocol, clear buffers
+ */
+ void reset();
+
+ /**
+ * Accept data from the network, and buffer it into a useful message
+ * This requires parsing out each FLAP, etc. from the incoming data
+ * @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( Transfer* 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( QDataStream & );
+ /**
+ * 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 );
+
+private:
+ QByteArray m_in; // buffer containing unprocessed bytes we received
+ int m_error;
+ Transfer* m_inTransfer; // the transfer that is being received
+ int m_state; // represents the protocol's overall state
+ YMSGProtocol* m_YMSGProtocol;
+
+};
+
+#endif
+
diff --git a/kopete/protocols/yahoo/libkyahoo/crypt.c b/kopete/protocols/yahoo/libkyahoo/crypt.c
new file mode 100644
index 00000000..3aabeced
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/crypt.c
@@ -0,0 +1,210 @@
+/* One way encryption based on MD5 sum.
+ Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+ The GNU C 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.
+
+ The GNU C 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* warmenhoven took this file and made it work with the md5.[ch] we
+ * already had. isn't that lovely. people should just use linux or
+ * freebsd, crypt works properly on those systems. i hate solaris */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_STRING_H
+# include <string.h>
+#elif HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <stdlib.h>
+
+#include "md5.h"
+/* for MIN and MAX */
+#include "libyahoo.h"
+
+/* Define our magic string to mark salt for MD5 "encryption"
+ replacement. This is meant to be the same as for other MD5 based
+ encryption implementations. */
+static const char md5_salt_prefix[] = "$1$";
+
+/* Table with characters for base64 transformation. */
+static const char b64t[64] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+char *yahoo_crypt(const char *key, const char *salt);
+
+char *yahoo_crypt(const char *key, const char *salt)
+{
+ char *buffer = NULL;
+ int buflen = 0;
+ int needed = 3 + strlen (salt) + 1 + 26 + 1;
+
+ md5_byte_t alt_result[16];
+ md5_state_t ctx;
+ md5_state_t alt_ctx;
+ size_t salt_len;
+ size_t key_len;
+ size_t cnt;
+ char *cp;
+
+ if (buflen < needed) {
+ buflen = needed;
+ if ((buffer = realloc(buffer, buflen)) == NULL)
+ return NULL;
+ }
+
+ /* Find beginning of salt string. The prefix should normally always
+ be present. Just in case it is not. */
+ if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
+ /* Skip salt prefix. */
+ salt += sizeof (md5_salt_prefix) - 1;
+
+ salt_len = MIN (strcspn (salt, "$"), 8);
+ key_len = strlen (key);
+
+ /* Prepare for the real work. */
+ md5_init(&ctx);
+
+ /* Add the key string. */
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Because the SALT argument need not always have the salt prefix we
+ add it separately. */
+ md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
+
+ /* The last part is the salt string. This must be at most 8
+ characters and it ends at the first `$' character (for
+ compatibility which existing solutions). */
+ md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The
+ final result will be added to the first context. */
+ md5_init(&alt_ctx);
+
+ /* Add key. */
+ md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+ /* Add salt. */
+ md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Add key again. */
+ md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+ /* Now get result of this (16 bytes) and add it to the other
+ context. */
+ md5_finish(&alt_ctx, alt_result);
+
+ /* Add for any character in the key one byte of the alternate sum. */
+ for (cnt = key_len; cnt > 16; cnt -= 16)
+ md5_append(&ctx, alt_result, 16);
+ md5_append(&ctx, alt_result, cnt);
+
+ /* For the following code we need a NUL byte. */
+ alt_result[0] = '\0';
+
+ /* The original implementation now does something weird: for every 1
+ bit in the key the first 0 is added to the buffer, for every 0
+ bit the first character of the key. This does not seem to be
+ what was intended but we have to follow this to be compatible. */
+ for (cnt = key_len; cnt > 0; cnt >>= 1)
+ md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
+
+ /* Create intermediate result. */
+ md5_finish(&ctx, alt_result);
+
+ /* Now comes another weirdness. In fear of password crackers here
+ comes a quite long loop which just processes the output of the
+ previous round again. We cannot ignore this here. */
+ for (cnt = 0; cnt < 1000; ++cnt) {
+ /* New context. */
+ md5_init(&ctx);
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0)
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+ else
+ md5_append(&ctx, alt_result, 16);
+
+ /* Add salt for numbers not divisible by 3. */
+ if (cnt % 3 != 0)
+ md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+ /* Add key for numbers not divisible by 7. */
+ if (cnt % 7 != 0)
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Add key or last result. */
+ if ((cnt & 1) != 0)
+ md5_append(&ctx, alt_result, 16);
+ else
+ md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+ /* Create intermediate result. */
+ md5_finish(&ctx, alt_result);
+ }
+
+ /* Now we can construct the result string. It consists of three
+ parts. */
+
+ strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
+ cp = buffer + strlen(buffer);
+ buflen -= sizeof (md5_salt_prefix);
+
+ strncpy(cp, salt, MIN ((size_t) buflen, salt_len));
+ cp = cp + strlen(cp);
+ buflen -= MIN ((size_t) buflen, salt_len);
+
+ if (buflen > 0) {
+ *cp++ = '$';
+ --buflen;
+ }
+
+#define b64_from_24bit(B2, B1, B0, N) \
+ do { \
+ unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
+ int n = (N); \
+ while (n-- > 0 && buflen > 0) { \
+ *cp++ = b64t[w & 0x3f]; \
+ --buflen; \
+ w >>= 6; \
+ }\
+ } while (0)
+
+ b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
+ b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
+ b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
+ b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
+ b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
+ b64_from_24bit (0, 0, alt_result[11], 2);
+ if (buflen <= 0) {
+ FREE(buffer);
+ } else
+ *cp = '\0'; /* Terminate the string. */
+
+ /* Clear the buffer for the intermediate result so that people
+ attaching to processes or reading core dumps cannot get any
+ information. We do it in this way to clear correct_words[]
+ inside the MD5 implementation as well. */
+ md5_init(&ctx);
+ md5_finish(&ctx, alt_result);
+ memset (&ctx, '\0', sizeof (ctx));
+ memset (&alt_ctx, '\0', sizeof (alt_ctx));
+
+ return buffer;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.cpp b/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.cpp
new file mode 100644
index 00000000..7d2042e4
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.cpp
@@ -0,0 +1,152 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about incoming filetransfers
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "filetransfernotifiertask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+FileTransferNotifierTask::FileTransferNotifierTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+FileTransferNotifierTask::~FileTransferNotifierTask()
+{
+
+}
+
+bool FileTransferNotifierTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);
+
+ if( t->service() == Yahoo::ServiceFileTransfer )
+ parseFileTransfer( t );
+ else if( t->service() == Yahoo::ServiceFileTransfer7 )
+ parseFileTransfer7( t );
+ else if( t->service() == Yahoo::ServicePeerToPeer )
+ acceptFileTransfer( t );
+
+
+ return true;
+}
+
+bool FileTransferNotifierTask::forMe( Transfer *transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+
+ if( t->service() == Yahoo::ServiceP2PFileXfer ||
+ t->service() == Yahoo::ServicePeerToPeer ||
+ t->service() == Yahoo::ServiceFileTransfer ||
+ t->service() == Yahoo::ServiceFileTransfer7
+ )
+ return true;
+ else
+ return false;
+}
+
+void FileTransferNotifierTask::parseFileTransfer( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString from; /* key = 4 */
+ QString to; /* key = 5 */
+ QString url; /* key = 20 */
+ long expires; /* key = 38 */
+ QString msg; /* key = 14 */
+ QString filename; /* key = 27 */
+ unsigned long size; /* key = 28 */
+
+ from = t->firstParam( 4 );
+ to = t->firstParam( 5 );
+ url = t->firstParam( 20 );
+ expires = t->firstParam( 38 ).toLong();
+ msg = t->firstParam( 14 );
+ filename = t->firstParam( 27 );
+ size = t->firstParam( 28 ).toULong();
+
+
+
+ if( from.startsWith( "FILE_TRANSFER_SYSTEM" ) )
+ {
+ client()->notifyError( "Fileupload result received.", msg, Client::Notice );
+ return;
+ }
+
+ if( url.isEmpty() )
+ return;
+
+
+ unsigned int left = url.findRev( '/' ) + 1;
+ unsigned int right = url.findRev( '?' );
+ filename = url.mid( left, right - left );
+
+ emit incomingFileTransfer( from, url, expires, msg, filename, size );
+}
+
+void FileTransferNotifierTask::parseFileTransfer7( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString from; /* key = 4 */
+ QString to; /* key = 5 */
+ QString url; /* key = 20 */
+ long expires; /* key = 38 */
+ QString msg; /* key = 14 */
+ QString filename; /* key = 27 */
+ unsigned long size; /* key = 28 */
+
+ if( t->firstParam( 222 ).toInt() == 2 )
+ return; // user cancelled the file transfer
+
+ from = t->firstParam( 4 );
+ to = t->firstParam( 5 );
+ url = t->firstParam( 265 );
+ expires = t->firstParam( 38 ).toLong();
+ msg = t->firstParam( 14 );
+ filename = t->firstParam( 27 );
+ size = t->firstParam( 28 ).toULong();
+
+ emit incomingFileTransfer( from, url, expires, msg, filename, size );
+}
+
+void FileTransferNotifierTask::acceptFileTransfer( YMSGTransfer *transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePeerToPeer);
+ t->setId( client()->sessionID() );
+ t->setParam( 4, client()->userId().local8Bit() );
+ t->setParam( 5, transfer->firstParam( 4 ) );
+ t->setParam( 11, transfer->firstParam( 11 ) );
+
+ send( t );
+}
+
+#include "filetransfernotifiertask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.h b/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.h
new file mode 100644
index 00000000..0fd01eec
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/filetransfernotifiertask.h
@@ -0,0 +1,50 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about incoming filetransfers
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 FILETRANSFERNOTIFIERTASK_H
+#define FILETRANSFERNOTIFIERTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+
+class QString;
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class FileTransferNotifierTask : public Task
+{
+Q_OBJECT
+public:
+ FileTransferNotifierTask(Task *parent);
+ ~FileTransferNotifierTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+signals:
+ void incomingFileTransfer( const QString &who, const QString &url, long expires, const QString &msg ,
+ const QString &fname, unsigned long size );
+private:
+ void parseFileTransfer( YMSGTransfer *transfer );
+ void parseFileTransfer7( YMSGTransfer *transfer );
+ void acceptFileTransfer( YMSGTransfer *t );
+ void parseFileTransfer7Info( YMSGTransfer *YMSGtransfer );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.cpp b/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.cpp
new file mode 100644
index 00000000..5c2dfcc3
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.cpp
@@ -0,0 +1,98 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.cpp - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "inputprotocolbase.h"
+
+InputProtocolBase::InputProtocolBase(QObject *parent, const char *name)
+ : QObject(parent, name)
+{
+}
+
+
+InputProtocolBase::~InputProtocolBase()
+{
+}
+
+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 )
+ {
+ if ( m_din->atEnd() )
+ {
+ m_state = NeedMore;
+ qDebug( "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 > 1024 )
+ 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 ) )
+ {
+ qDebug( "InputProtocol::safeReadBytes() - string broke, giving up, only got: %i bytes out of %i", temp.length(), val );
+ m_state = NeedMore;
+ return false;
+ }
+ }
+ data = temp;
+ len = val;
+ m_bytes += val;
+ return true;
+}
+
+#include "inputprotocolbase.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.h b/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.h
new file mode 100644
index 00000000..d65bd8f7
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/inputprotocolbase.h
@@ -0,0 +1,72 @@
+/*
+ Kopete Groupwise Protocol
+ inputprotocolbase.h - Ancestor of all protocols used for reading GroupWise input
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef INPUTPROTOCOLBASE_H
+#define INPUTPROTOCOLBASE_H
+
+#include <qobject.h>
+
+class Transfer;
+/**
+Defines a basic interface for protocols dealing with input from the GroupWise server.
+
+@author Kopete Developers
+*/
+class InputProtocolBase : public QObject
+{
+Q_OBJECT
+public:
+ enum EventProtocolState { Success, NeedMore, OutOfSync, ProtocolError };
+ InputProtocolBase(QObject *parent = 0, const char *name = 0);
+ ~InputProtocolBase();
+ /**
+ * 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/yahoo/libkyahoo/libyahoo.c b/kopete/protocols/yahoo/libkyahoo/libyahoo.c
new file mode 100644
index 00000000..93ba9956
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/libyahoo.c
@@ -0,0 +1,532 @@
+/*
+ * libyahoo2: libyahoo2.c
+ *
+ * Some code copyright (C) 2002, Philip S Tellis <philip . tellis AT gmx . net>
+ *
+ * Much of this code was taken and adapted from the yahoo module for
+ * gaim released under the GNU GPL. This code is also released under the
+ * GNU GPL.
+ *
+ * This code is derivitive of Gaim <http://gaim.sourceforge.net>
+ * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * 1998-1999, Adam Fritzler <afritz@marko.net>
+ * 1998-2002, Rob Flynn <rob@marko.net>
+ * 2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
+ * 2001-2002, Brian Macke <macke@strangelove.net>
+ * 2001, Anand Biligiri S <abiligiri@users.sf.net>
+ * 2001, Valdis Kletnieks
+ * 2002, Sean Egan <bj91704@binghamton.edu>
+ * 2002, Toby Gray <toby.gray@ntlworld.com>
+ *
+ * This library also uses code from other libraries, namely:
+ * Portions from libfaim copyright 1998, 1999 Adam Fritzler
+ * <afritz@auk.cx>
+ * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
+ * <hiro-y@kcn.ne.jp>
+ *
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "libyahoo.h"
+#include "yahoo_fn.h"
+#include "md5.h"
+#include "sha1.h"
+
+extern char *yahoo_crypt(char *, char *);
+
+void yahooBase64(unsigned char *out, const unsigned char *in, int inlen)
+/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
+{
+ char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789._";
+
+ for (; inlen >= 3; inlen -= 3)
+ {
+ *out++ = base64digits[in[0] >> 2];
+ *out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ *out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)];
+ *out++ = base64digits[in[2] & 0x3f];
+ in += 3;
+ }
+ if (inlen > 0)
+ {
+ unsigned char fragment;
+
+ *out++ = base64digits[in[0] >> 2];
+ fragment = (in[0] << 4) & 0x30;
+ if (inlen > 1)
+ fragment |= in[1] >> 4;
+ *out++ = base64digits[fragment];
+ *out++ = (inlen < 2) ? '-'
+ : base64digits[(in[1] << 2) & 0x3c];
+ *out++ = '-';
+ }
+ *out = '\0';
+}
+
+void authresp_0x0b(const char *seed, const char *sn, const char *password, char *resp_6, char *resp_96 )
+{
+ md5_byte_t result[16];
+ md5_state_t ctx;
+
+ SHA1Context ctx1;
+ SHA1Context ctx2;
+
+ const char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ";
+ const char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop";
+
+ const char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5";
+ const char *operand_lookup = "+|&%/*^-";
+ const char *delimit_lookup = ",;";
+
+ unsigned char *password_hash = malloc(25);
+ unsigned char *crypt_hash = malloc(25);
+ char *crypt_result = NULL;
+ unsigned char pass_hash_xor1[64];
+ unsigned char pass_hash_xor2[64];
+ unsigned char crypt_hash_xor1[64];
+ unsigned char crypt_hash_xor2[64];
+ char chal[7];
+
+ unsigned char digest1[20];
+ unsigned char digest2[20];
+ unsigned char magic_key_char[4];
+ const unsigned char *magic_ptr;
+
+ unsigned int magic[64];
+ unsigned int magic_work = 0;
+ /*unsigned int value = 0;*/
+
+ char comparison_src[20];
+ int x, i, j;
+ int depth = 0, table = 0;
+ int cnt = 0;
+ int magic_cnt = 0;
+ int magic_len;
+ /*int times = 0;*/
+
+ memset(pass_hash_xor1, 0, 64);
+ memset(pass_hash_xor2, 0, 64);
+ memset(crypt_hash_xor1, 0, 64);
+ memset(crypt_hash_xor2, 0, 64);
+ memset(digest1, 0, 20);
+ memset(digest2, 0, 20);
+ memset(magic, 0, 64);
+ memset(resp_6, 0, 100);
+ memset(resp_96, 0, 100);
+ memset(magic_key_char, 0, 4);
+
+ /*
+ * Magic: Phase 1. Generate what seems to be a 30
+ * byte value (could change if base64
+ * ends up differently? I don't remember and I'm
+ * tired, so use a 64 byte buffer.
+ */
+
+ magic_ptr = (unsigned char *)seed;
+
+ while (*magic_ptr != (int)NULL) {
+ char *loc;
+
+ /* Ignore parentheses. */
+
+ if (*magic_ptr == '(' || *magic_ptr == ')') {
+ magic_ptr++;
+ continue;
+ }
+
+ /* Characters and digits verify against
+ the challenge lookup.
+ */
+
+ if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) {
+ loc = strchr(challenge_lookup, *magic_ptr);
+ if (!loc) {
+ /* This isn't good */
+ continue;
+ }
+
+ /* Get offset into lookup table and lsh 3. */
+
+ magic_work = loc - challenge_lookup;
+ magic_work <<= 3;
+
+ magic_ptr++;
+ continue;
+ } else {
+ unsigned int local_store;
+
+ loc = strchr(operand_lookup, *magic_ptr);
+ if (!loc) {
+ /* Also not good. */
+ continue;
+ }
+
+ local_store = loc - operand_lookup;
+
+ /* Oops; how did this happen? */
+ if (magic_cnt >= 64)
+ break;
+
+ magic[magic_cnt++] = magic_work | local_store;
+ magic_ptr++;
+ continue;
+ }
+ }
+
+ magic_len = magic_cnt;
+ magic_cnt = 0;
+
+ /* Magic: Phase 2. Take generated magic value and
+ * sprinkle fairy dust on the values. */
+
+ for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) {
+ unsigned char byte1;
+ unsigned char byte2;
+
+ /* Bad. Abort.
+ */
+ if (magic_cnt >= magic_len)
+ break;
+
+ byte1 = magic[magic_cnt];
+ byte2 = magic[magic_cnt+1];
+
+ byte1 *= 0xcd;
+ byte1 ^= byte2;
+
+ magic[magic_cnt+1] = byte1;
+ }
+
+ /* Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic
+ * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key
+ * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets
+ * into particular functions we'll later call to potentially alter the magic key.
+ *
+ * %-)
+ */
+
+ magic_cnt = 1;
+ x = 0;
+
+ do {
+ unsigned int bl = 0;
+ unsigned int cl = magic[magic_cnt++];
+
+ if (magic_cnt >= magic_len)
+ break;
+
+ if (cl > 0x7F) {
+ if (cl < 0xe0)
+ bl = cl = (cl & 0x1f) << 6;
+ else {
+ bl = magic[magic_cnt++];
+ cl = (cl & 0x0f) << 6;
+ bl = ((bl & 0x3f) + cl) << 6;
+ }
+
+ cl = magic[magic_cnt++];
+ bl = (cl & 0x3f) + bl;
+ } else
+ bl = cl;
+
+ comparison_src[x++] = (bl & 0xff00) >> 8;
+ comparison_src[x++] = bl & 0xff;
+ } while (x < 20);
+
+ /* Dump magic key into a char for SHA1 action. */
+
+
+ for(x = 0; x < 4; x++)
+ magic_key_char[x] = comparison_src[x];
+
+ /* Compute values for recursive function table! */
+ memcpy( chal, magic_key_char, 4 );
+ x = 1;
+ for( i = 0; i < 0xFFFF && x; i++ )
+ {
+ for( j = 0; j < 5 && x; j++ )
+ {
+ chal[4] = i;
+ chal[5] = i >> 8;
+ chal[6] = j;
+ md5_init( &ctx );
+ md5_append( &ctx, chal, 7 );
+ md5_finish( &ctx, result );
+ if( memcmp( comparison_src + 4, result, 16 ) == 0 )
+ {
+ depth = i;
+ table = j;
+ x = 0;
+ }
+ }
+ }
+
+ /* Transform magic_key_char using transform table */
+ x = magic_key_char[3] << 24 | magic_key_char[2] << 16
+ | magic_key_char[1] << 8 | magic_key_char[0];
+ x = yahoo_xfrm( table, depth, x );
+ x = yahoo_xfrm( table, depth, x );
+ magic_key_char[0] = x & 0xFF;
+ magic_key_char[1] = x >> 8 & 0xFF;
+ magic_key_char[2] = x >> 16 & 0xFF;
+ magic_key_char[3] = x >> 24 & 0xFF;
+
+ /* Get password and crypt hashes as per usual. */
+ md5_init(&ctx);
+ md5_append(&ctx, (md5_byte_t *)password, strlen(password));
+ md5_finish(&ctx, result);
+ yahooBase64(password_hash, result, 16);
+
+ md5_init(&ctx);
+ crypt_result = yahoo_crypt(password, "$1$_2S43d5f$");
+ md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
+ md5_finish(&ctx, result);
+ yahooBase64(crypt_hash, result, 16);
+
+ /* Our first authentication response is based off
+ * of the password hash. */
+
+ for (x = 0; x < (int)strlen((char *)password_hash); x++)
+ pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36;
+
+ if (cnt < 64)
+ memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt);
+
+ cnt = 0;
+
+ for (x = 0; x < (int)strlen((char *)password_hash); x++)
+ pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c;
+
+ if (cnt < 64)
+ memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+ SHA1Init(&ctx1);
+ SHA1Init(&ctx2);
+
+ /* The first context gets the password hash XORed
+ * with 0x36 plus a magic value
+ * which we previously extrapolated from our
+ * challenge. */
+
+ SHA1Update(&ctx1, pass_hash_xor1, 64);
+ if (j >= 3 )
+ ctx1.totalLength = 0x1ff;
+ SHA1Update(&ctx1, magic_key_char, 4);
+ SHA1Final(&ctx1, digest1);
+
+ /* The second context gets the password hash XORed
+ * with 0x5c plus the SHA-1 digest
+ * of the first context. */
+
+ SHA1Update(&ctx2, pass_hash_xor2, 64);
+ SHA1Update(&ctx2, digest1, 20);
+ SHA1Final(&ctx2, digest2);
+
+ /* Now that we have digest2, use it to fetch
+ * characters from an alphabet to construct
+ * our first authentication response. */
+
+ for (x = 0; x < 20; x += 2) {
+ unsigned int val = 0;
+ unsigned int lookup = 0;
+ char byte[6];
+
+ memset(&byte, 0, 6);
+
+ /* First two bytes of digest stuffed
+ * together.
+ */
+
+ val = digest2[x];
+ val <<= 8;
+ val += digest2[x+1];
+
+ lookup = (val >> 0x0b);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet1))
+ break;
+ sprintf(byte, "%c", alphabet1[lookup]);
+ strcat(resp_6, byte);
+ strcat(resp_6, "=");
+
+ lookup = (val >> 0x06);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_6, byte);
+
+ lookup = (val >> 0x01);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_6, byte);
+
+ lookup = (val & 0x01);
+ if (lookup >= strlen(delimit_lookup))
+ break;
+ sprintf(byte, "%c", delimit_lookup[lookup]);
+ strcat(resp_6, byte);
+ }
+
+ /* Our second authentication response is based off
+ * of the crypto hash. */
+
+ cnt = 0;
+ memset(&digest1, 0, 20);
+ memset(&digest2, 0, 20);
+
+ for (x = 0; x < (int)strlen((char *)crypt_hash); x++)
+ crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36;
+
+ if (cnt < 64)
+ memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt);
+
+ cnt = 0;
+
+ for (x = 0; x < (int)strlen((char *)crypt_hash); x++)
+ crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c;
+
+ if (cnt < 64)
+ memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+ SHA1Init(&ctx1);
+ SHA1Init(&ctx2);
+
+ /* The first context gets the password hash XORed
+ * with 0x36 plus a magic value
+ * which we previously extrapolated from our
+ * challenge. */
+
+ SHA1Update(&ctx1, crypt_hash_xor1, 64);
+ if (j >= 3 )
+ ctx1.totalLength = 0x1ff;
+ SHA1Update(&ctx1, magic_key_char, 4);
+ SHA1Final(&ctx1, digest1);
+
+ /* The second context gets the password hash XORed
+ * with 0x5c plus the SHA-1 digest
+ * of the first context. */
+
+ SHA1Update(&ctx2, crypt_hash_xor2, 64);
+ SHA1Update(&ctx2, digest1, 20);
+ SHA1Final(&ctx2, digest2);
+
+ /* Now that we have digest2, use it to fetch
+ * characters from an alphabet to construct
+ * our first authentication response. */
+
+ for (x = 0; x < 20; x += 2) {
+ unsigned int val = 0;
+ unsigned int lookup = 0;
+
+ char byte[6];
+
+ memset(&byte, 0, 6);
+
+ /* First two bytes of digest stuffed
+ * together. */
+
+ val = digest2[x];
+ val <<= 8;
+ val += digest2[x+1];
+
+ lookup = (val >> 0x0b);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet1))
+ break;
+ sprintf(byte, "%c", alphabet1[lookup]);
+ strcat(resp_96, byte);
+ strcat(resp_96, "=");
+
+ lookup = (val >> 0x06);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_96, byte);
+
+ lookup = (val >> 0x01);
+ lookup &= 0x1f;
+ if (lookup >= strlen(alphabet2))
+ break;
+ sprintf(byte, "%c", alphabet2[lookup]);
+ strcat(resp_96, byte);
+
+ lookup = (val & 0x01);
+ if (lookup >= strlen(delimit_lookup))
+ break;
+ sprintf(byte, "%c", delimit_lookup[lookup]);
+ strcat(resp_96, byte);
+ }
+
+ free(password_hash);
+ free(crypt_hash);
+}
+
+char * getcookie(const char *rawcookie)
+{
+ char * cookie=NULL;
+ char * tmpcookie;
+ char * cookieend;
+
+ if (strlen(rawcookie) < 2)
+ return NULL;
+
+ tmpcookie = strdup(rawcookie+2);
+ cookieend = strchr(tmpcookie, ';');
+
+ if(cookieend)
+ *cookieend = '\0';
+
+ cookie = strdup(tmpcookie);
+ FREE(tmpcookie);
+ /* cookieend=NULL; not sure why this was there since the value is not preserved in the stack -dd */
+
+ return cookie;
+}
+
+char * getlcookie(const char *cookie)
+{
+ char *tmp;
+ char *tmpend;
+ char *login_cookie = NULL;
+
+ tmpend = strstr(cookie, "n=");
+ if(tmpend) {
+ tmp = strdup(tmpend+2);
+ tmpend = strchr(tmp, '&');
+ if(tmpend)
+ *tmpend='\0';
+ login_cookie = strdup(tmp);
+ FREE(tmp);
+ }
+
+ return login_cookie;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/libyahoo.h b/kopete/protocols/yahoo/libkyahoo/libyahoo.h
new file mode 100644
index 00000000..3a57482d
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/libyahoo.h
@@ -0,0 +1,61 @@
+/*
+ * libyahoo2: libyahoo2.c
+ *
+ * Some code copyright (C) 2002, Philip S Tellis <philip . tellis AT gmx . net>
+ *
+ * Much of this code was taken and adapted from the yahoo module for
+ * gaim released under the GNU GPL. This code is also released under the
+ * GNU GPL.
+ *
+ * This code is derivitive of Gaim <http://gaim.sourceforge.net>
+ * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * 1998-1999, Adam Fritzler <afritz@marko.net>
+ * 1998-2002, Rob Flynn <rob@marko.net>
+ * 2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
+ * 2001-2002, Brian Macke <macke@strangelove.net>
+ * 2001, Anand Biligiri S <abiligiri@users.sf.net>
+ * 2001, Valdis Kletnieks
+ * 2002, Sean Egan <bj91704@binghamton.edu>
+ * 2002, Toby Gray <toby.gray@ntlworld.com>
+ *
+ * This library also uses code from other libraries, namely:
+ * Portions from libfaim copyright 1998, 1999 Adam Fritzler
+ * <afritz@auk.cx>
+ * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
+ * <hiro-y@kcn.ne.jp>
+ *
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#ifndef LIB_YAHOO_UTILS_H
+#define LIB_YAHOO_UTILS_H
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+#define FREE(x) if(x) {free(x); x=NULL;}
+
+void authresp_0x0b(const char *seed, const char *sn, const char *password, char *resp_6, char *resp_96 );
+void yahooBase64(unsigned char *out, const unsigned char *in, int inlen);
+char * getlcookie(const char *cookie);
+char * getcookie(const char *rawcookie);
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/listtask.cpp b/kopete/protocols/yahoo/libkyahoo/listtask.cpp
new file mode 100644
index 00000000..261e7896
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/listtask.cpp
@@ -0,0 +1,108 @@
+/*
+ Kopete Yahoo Protocol
+ Handles several lists such as buddylist, ignorelist and so on
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 <qstring.h>
+
+#include "listtask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "client.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kdebug.h>
+
+ListTask::ListTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+ListTask::~ListTask()
+{
+
+}
+
+bool ListTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer *>(transfer);
+
+ parseBuddyList( t );
+ parseStealthList( t );
+
+ return true;
+}
+
+bool ListTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+
+ if ( t->service() == Yahoo::ServiceList )
+ return true;
+ else
+ return false;
+}
+
+void ListTask::parseBuddyList( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString raw;
+ m_list.append( t->firstParam( 87 ) );
+
+ if( t->firstParam( 59 ).isEmpty() )
+ return;
+
+ QStringList groups;
+ groups = QStringList::split( "\n", m_list );
+
+ for ( QStringList::Iterator groupIt = groups.begin(); groupIt != groups.end(); ++groupIt )
+ {
+ QString group = (*groupIt).section(":", 0, 0);
+ QStringList buddies;
+ buddies = QStringList::split( ",", (*groupIt).section(":", 1,1) );
+ for ( QStringList::Iterator buddyIt = buddies.begin(); buddyIt != buddies.end(); ++buddyIt )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Parsed buddy: " << *buddyIt << " in group " << group << endl;
+ emit gotBuddy( *buddyIt, QString::null, group );
+ }
+ }
+ m_list.truncate( 0 );
+}
+
+void ListTask::parseStealthList( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString raw;
+ raw = t->firstParam( 185 );
+
+ QStringList buddies = QStringList::split( ",", raw );
+ for ( QStringList::Iterator it = buddies.begin(); it != buddies.end(); ++it )
+ {
+ emit stealthStatusChanged( *it, Yahoo::StealthActive );
+ }
+}
+
+#include "listtask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/listtask.h b/kopete/protocols/yahoo/libkyahoo/listtask.h
new file mode 100644
index 00000000..09b98495
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/listtask.h
@@ -0,0 +1,48 @@
+/*
+ Kopete Yahoo Protocol
+ Handles several lists such as buddylist, ignorelist and so on
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 LISTTASK_H
+#define LISTTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+
+class QString;
+class YMSGTransfer;
+/**
+@author André Duffeck
+*/
+class ListTask : public Task
+{
+Q_OBJECT
+public:
+ ListTask(Task *parent);
+ ~ListTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+ void parseBuddyList( YMSGTransfer *transfer );
+ void parseStealthList( YMSGTransfer *transfer );
+signals:
+ void gotBuddy(const QString&, const QString&, const QString&);
+ void stealthStatusChanged( const QString&, Yahoo::StealthStatus );
+private:
+ QString m_list;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/logintask.cpp b/kopete/protocols/yahoo/libkyahoo/logintask.cpp
new file mode 100644
index 00000000..72c598bc
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/logintask.cpp
@@ -0,0 +1,303 @@
+/*
+ Kopete Yahoo Protocol
+ Handles logging into to the Yahoo service
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qstring.h>
+
+#include "logintask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+#include <stdlib.h>
+extern "C"
+{
+#include "libyahoo.h"
+}
+
+LoginTask::LoginTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ mState = InitialState;
+}
+
+LoginTask::~LoginTask()
+{
+
+}
+
+bool LoginTask::take(Transfer* transfer)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ /*
+ Yahoo login task has various stages
+
+ 1 .- Initial State
+ 1.1 .- OnGo is called
+ 1.2 .- SendVerify() - send a service verify ack
+ 2.- SentVerify
+ 2.1 - take(), get a useless transfer, sendAuth is called
+ 3.- SentAuth
+ 2.2 - take(), get a transfer with login and challenge string
+ sendAuthResp is called.
+ 2.3 - Need to decode and send a transfer back
+ 4.- SentAuthResp
+ */
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer *>(transfer);
+
+ switch (mState)
+ {
+ case (InitialState):
+ client()->notifyError( "Error in login procedure.", "take called while in initial state", Client::Debug );
+ return false;
+ break;
+ case (SentVerify):
+ sendAuth( t );
+ return true;
+ break;
+ case (SentAuth):
+ sendAuthResp( t );
+ return true;
+ break;
+ case (SentAuthResp):
+ parseCookies( t );
+ handleAuthResp( t );
+ // Throw transfer to the next task as it contains further data
+ return false;
+ break;
+ default:
+ return false;
+ break;
+ }
+}
+
+bool LoginTask::forMe(Transfer* transfer) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ switch (mState)
+ {
+ case (InitialState):
+ //there shouldn't be a incoming transfer for this task at this state
+ return false;
+ break;
+ case (SentVerify):
+ if ( t->service() == Yahoo::ServiceVerify )
+ return true;
+ break;
+ case (SentAuth):
+ if ( t->service() == Yahoo::ServiceAuth )
+ return true;
+ break;
+ case (SentAuthResp ):
+ if ( t->service() == Yahoo::ServiceList ||
+ t->service() == Yahoo::ServiceAuthResp )
+ return true;
+ default:
+ return false;
+ break;
+ }
+ return false;
+}
+
+void LoginTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ /* initial state, we have to send a ServiceVerify */
+ if (mState == InitialState)
+ sendVerify();
+ else
+ client()->notifyError( "Error in login procedure.", "take called while not in initial state", Client::Debug );
+}
+
+void LoginTask::reset()
+{
+ mState = InitialState;
+}
+
+void LoginTask::sendVerify()
+{
+ /* send a ServiceVerify */
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceVerify);
+ send( t );
+ mState = SentVerify;
+}
+
+void LoginTask::sendAuth(YMSGTransfer* transfer)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ // transfer is the verify ack transfer, no useful data in it.
+ Q_UNUSED(transfer);
+
+ /* got ServiceVerify ACK, send a ServiceAuth with username */
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = new YMSGTransfer( Yahoo::ServiceAuth );
+ t->setParam( 1 , client()->userId().local8Bit() );
+ send(t);
+ mState = SentAuth;
+}
+
+void LoginTask::sendAuthResp(YMSGTransfer* t)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString sn = t->firstParam( 1 );
+ QString seed = t->firstParam( 94 );
+ QString version_s = t->firstParam( 13 );
+ uint sessionID = t->id();
+ int version = version_s.toInt();
+
+ switch (version)
+ {
+ case 0:
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Version pre 0x0b "<< version_s << endl;
+ break;
+ default:
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Version 0x0b "<< version_s << endl;
+ sendAuthResp_0x0b(sn, seed, sessionID);
+ break;
+ }
+ mState = SentAuthResp;
+
+ emit haveSessionID( sessionID );
+}
+
+void LoginTask::sendAuthResp_0x0b(const QString &sn, const QString &seed, uint sessionID)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " with seed " << seed << endl;
+ char *resp_6 = (char *) malloc(100);
+ char *resp_96 = (char *) malloc(100);
+ authresp_0x0b(seed.latin1(), sn.latin1(), (client()->password()).latin1(), resp_6, resp_96);
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "resp_6: " << resp_6 << " resp_69: " << resp_96 << endl;
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceAuthResp, m_stateOnConnect);
+ t->setId( sessionID );
+ t->setParam( 0 , sn.local8Bit());
+ t->setParam( 6 , resp_6);
+ t->setParam( 96 , resp_96);
+ t->setParam( 59 , "B\\tfckeert1kk1nl&b=2" ); // ???
+ t->setParam( 135 , "7,0,0,437" ); // Client version
+ t->setParam( 148 , -60 );
+ t->setParam( 244 , 524223 );
+ t->setParam( 1 , sn.local8Bit());
+
+ if( !m_verificationWord.isEmpty() )
+ {
+ t->setParam( 227 , m_verificationWord.local8Bit() );
+ m_verificationWord = QString::null;
+ }
+
+ free(resp_6);
+ free(resp_96);
+ send(t);
+
+}
+
+void LoginTask::sendAuthResp_pre_0x0b(const QString &/*sn*/, const QString &/*seed*/)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+void LoginTask::handleAuthResp(YMSGTransfer *t)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ switch( t->service() )
+ {
+ case( Yahoo::ServiceList ):
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Emitting Signal" << endl;
+ emit loginResponse( Yahoo::LoginOk, QString::null );
+ break;
+ case( Yahoo::ServiceAuthResp ):
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Emitting Signal" << endl;
+ emit loginResponse( t->firstParam( 66 ).toInt(), t->firstParam( 20 ) );
+ break;
+ default:
+ break;
+ }
+ mState = InitialState;
+}
+
+void LoginTask::setStateOnConnect( Yahoo::Status status )
+{
+ m_stateOnConnect = status;
+}
+
+void LoginTask::parseCookies( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ for( int i = 0; i < t->paramCount( 59 ); ++i)
+ {
+ QString cookie;
+ cookie = t->nthParam( 59, i );
+ if( cookie.startsWith( "Y" ) )
+ {
+ m_yCookie = getcookie( cookie.latin1() );
+ m_loginCookie = getlcookie( cookie.latin1() );
+ }
+ else if( cookie.startsWith( "T" ) )
+ {
+ m_tCookie = getcookie( cookie.latin1() );
+ }
+ else if( cookie.startsWith( "C" ) )
+ {
+ m_cCookie = getcookie( cookie.latin1() );
+ }
+ }
+ if( !m_yCookie.isEmpty() && !m_tCookie.isEmpty() &&
+ !m_cCookie.isEmpty() )
+ emit haveCookies();
+}
+
+void LoginTask::setVerificationWord( const QString &word )
+{
+ m_verificationWord = word;
+}
+
+const QString& LoginTask::yCookie()
+{
+ return m_yCookie;
+}
+
+const QString& LoginTask::tCookie()
+{
+ return m_tCookie;
+}
+
+const QString& LoginTask::cCookie()
+{
+ return m_cCookie;
+}
+
+const QString& LoginTask::loginCookie()
+{
+ return m_loginCookie;
+}
+#include "logintask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/logintask.h b/kopete/protocols/yahoo/libkyahoo/logintask.h
new file mode 100644
index 00000000..2ad68853
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/logintask.h
@@ -0,0 +1,75 @@
+/*
+ Kopete Yahoo Protocol
+ Handles logging into to the Yahoo service
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef LOGINTASK_H
+#define LOGINTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+
+class QString;
+class YMSGTransfer;
+
+/**
+@author Duncan Mac-Vicar
+*/
+class LoginTask : public Task
+{
+Q_OBJECT
+public:
+ LoginTask(Task *parent);
+ ~LoginTask();
+
+ bool take(Transfer* transfer);
+ virtual void onGo();
+
+ void reset();
+ void setStateOnConnect( Yahoo::Status status );
+ void setVerificationWord( const QString &word );
+
+ const QString &yCookie();
+ const QString &cCookie();
+ const QString &tCookie();
+ const QString &loginCookie();
+protected:
+ bool forMe( Transfer* transfer ) const;
+ enum State { InitialState, SentVerify, GotVerifyACK, SentAuth, GotAuthACK, SentAuthResp };
+ void sendVerify();
+ void sendAuth(YMSGTransfer* transfer);
+ void sendAuthResp(YMSGTransfer* transfer);
+ void sendAuthResp_0x0b(const QString &sn, const QString &seed, uint sessionID);
+ void sendAuthResp_pre_0x0b(const QString &sn, const QString &seed);
+ void handleAuthResp(YMSGTransfer *transfer);
+ void parseCookies( YMSGTransfer *transfer );
+signals:
+ void haveSessionID( uint );
+ void haveCookies();
+ void loginResponse( int, const QString& );
+private:
+ State mState;
+ Yahoo::Status m_stateOnConnect;
+ QString m_yCookie;
+ QString m_tCookie;
+ QString m_cCookie;
+ QString m_loginCookie;
+ QString m_verificationWord;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/logofftask.cpp b/kopete/protocols/yahoo/libkyahoo/logofftask.cpp
new file mode 100644
index 00000000..ed79245e
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/logofftask.cpp
@@ -0,0 +1,43 @@
+/*
+ Kopete Yahoo Protocol
+ Log off the Yahoo server
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "logofftask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+LogoffTask::LogoffTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+LogoffTask::~LogoffTask()
+{
+}
+
+void LogoffTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceLogoff);
+ t->setId( client()->sessionID() );
+ send( t );
+
+ setSuccess( true );
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/logofftask.h b/kopete/protocols/yahoo/libkyahoo/logofftask.h
new file mode 100644
index 00000000..7ef6799d
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/logofftask.h
@@ -0,0 +1,36 @@
+/*
+ Kopete Yahoo Protocol
+ Log off the Yahoo server
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 LOGOFFTASK_H
+#define LOGOFFTASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class LogoffTask : public Task
+{
+public:
+ LogoffTask(Task *parent);
+ ~LogoffTask();
+
+ virtual void onGo();
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.cpp b/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.cpp
new file mode 100644
index 00000000..7bea2c8f
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.cpp
@@ -0,0 +1,80 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about new mails
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 <qstring.h>
+
+#include "mailnotifiertask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+MailNotifierTask::MailNotifierTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+MailNotifierTask::~MailNotifierTask()
+{
+
+}
+
+bool MailNotifierTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer *>(transfer);
+
+ parseMail( t );
+
+ return true;
+}
+
+bool MailNotifierTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if ( t->service() == Yahoo::ServiceNewMail )
+ return true;
+ else
+ return false;
+}
+
+void MailNotifierTask::parseMail( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString count = t->firstParam( 9 );
+ QString mail = t->firstParam( 42 );
+ QString from = t->firstParam( 43 );
+ QString subject = t->firstParam( 18 );
+
+ if( !mail.isEmpty() && !from.isEmpty() && !subject.isEmpty() )
+ emit mailNotify( QString::fromLatin1( "%1 <%2>").arg( from, mail ), subject, count.toInt() );
+ else
+ emit mailNotify( QString::null, QString::null, count.toInt());
+}
+
+#include "mailnotifiertask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.h b/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.h
new file mode 100644
index 00000000..9fcf8ad4
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/mailnotifiertask.h
@@ -0,0 +1,44 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about new mails
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 MAILNOTIFIERTASK_H
+#define MAILNOTIFIERTASK_H
+
+#include "task.h"
+
+class QString;
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class MailNotifierTask : public Task
+{
+Q_OBJECT
+public:
+ MailNotifierTask(Task *parent);
+ ~MailNotifierTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+ void parseMail( YMSGTransfer *transfer );
+signals:
+ void mailNotify(const QString&, const QString&, int);
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/md5.c b/kopete/protocols/yahoo/libkyahoo/md5.c
new file mode 100644
index 00000000..ede1fc11
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/md5.c
@@ -0,0 +1,408 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "md5.h"
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+main()
+{
+ static const char *const test[7] = {
+ "", /*d41d8cd98f00b204e9800998ecf8427e*/
+ "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+ "abc", /*900150983cd24fb0d6963f7d28e17f72*/
+ "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+ "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ /*d174ab98d277d9f5a5611c2c9f419d9f*/
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+ };
+ int i;
+
+ for (i = 0; i < 7; ++i) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ printf("MD5 (\"%s\") = ", test[i]);
+ for (di = 0; di < 16; ++di)
+ printf("%02x", digest[di]);
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ md5_word_t X[16];
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/md5.h b/kopete/protocols/yahoo/libkyahoo/md5.h
new file mode 100644
index 00000000..501cdbe1
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/md5.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints. Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+#ifdef P1
+void md5_init(P1(md5_state_t *pms));
+#else
+void md5_init(md5_state_t *pms);
+#endif
+
+/* Append a string to the message. */
+#ifdef P3
+void md5_append(P3(md5_state_t *pms, const md5_byte_t *data, int nbytes));
+#else
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+#endif
+
+/* Finish the message and return the digest. */
+#ifdef P2
+void md5_finish(P2(md5_state_t *pms, md5_byte_t digest[16]));
+#else
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#endif
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/kopete/protocols/yahoo/libkyahoo/messagereceivertask.cpp b/kopete/protocols/yahoo/libkyahoo/messagereceivertask.cpp
new file mode 100644
index 00000000..f814d244
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/messagereceivertask.cpp
@@ -0,0 +1,148 @@
+/*
+ Kopete Yahoo Protocol
+ Receive Messages
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 <qstring.h>
+
+#include "messagereceivertask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+MessageReceiverTask::MessageReceiverTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+MessageReceiverTask::~MessageReceiverTask()
+{
+}
+
+bool MessageReceiverTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if( t->service() == Yahoo::ServiceNotify )
+ parseNotify( t );
+ else
+ parseMessage( t );
+
+ return true;
+}
+
+bool MessageReceiverTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if ( t->service() == Yahoo::ServiceMessage ||
+ t->service() == Yahoo::ServiceGameMsg ||
+ t->service() == Yahoo::ServiceSysMessage ||
+ t->service() == Yahoo::ServiceNotify )
+ return true;
+ else
+ return false;
+}
+
+void MessageReceiverTask::parseMessage( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ int cnt = t->paramCount( 5 );
+ for( int i = 0; i < cnt; ++i )
+ {
+ QString to = t->nthParam( 5, i );
+ QString timestamp = t->nthParamSeparated( 15, i, 4 );
+ QString utf8 = t->nthParamSeparated( 97, i, 4 );
+ QString from = t->nthParamSeparated( 1, i, 4 ).isEmpty() ? t->nthParam( 4, i ) : t->nthParamSeparated( 1, i, 4 );
+ QString msg = t->nthParamSeparated( 14, i, 4 );
+ QString sysmsg = t->nthParamSeparated( 16, i, 4 );
+
+ // The arrangement of the key->value pairs is different when there is only one message in the packet.
+ // Separating by key "5" (sender) doesn't work in that case, because the "1" and "4" keys are sent before the "5" key
+ if( cnt == 1 )
+ from = t->firstParam( 1 ).isEmpty() ? t->firstParam( 4 ) : t->firstParam( 1 );
+
+ if( !sysmsg.isEmpty() )
+ {
+ client()->notifyError( "Server message received: ", sysmsg, Client::Error );
+ continue;
+ }
+
+ if( msg.isEmpty() )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Got a empty message. Dropped." << endl;
+ continue;
+ }
+
+ if( utf8.startsWith( "1" ) )
+ msg = QString::fromUtf8( msg.latin1() );
+
+ if( t->service() == Yahoo::ServiceSysMessage )
+ emit systemMessage( sysmsg );
+ else
+ {
+ if( msg.startsWith( "<ding>" ) )
+ emit gotBuzz( from, timestamp.toLong() );
+ else
+ emit gotIm( from, msg, timestamp.toLong(), 0);
+ }
+ }
+}
+
+void MessageReceiverTask::parseNotify( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString from = t->firstParam( 4 );
+ //QString to = t->firstParam( 5 );
+ QString type = t->firstParam( 49 );
+ QString stat = t->firstParam( 13 );
+ QString ind = t->firstParam( 14 );
+
+ if( type.startsWith( "TYPING" ) )
+ emit gotTypingNotify( from, stat.toInt() );
+ else if( type.startsWith( "GAME" ) )
+ ;
+ else if( type.startsWith( "WEBCAMINVITE" ) )
+ {
+ if( ind.startsWith(" ") )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Got a WebcamInvitation." << endl;
+ emit gotWebcamInvite( from );
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Got a WebcamRequest-Response: " << ind.toInt() << endl;
+ }
+ }
+}
+
+#include "messagereceivertask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/messagereceivertask.h b/kopete/protocols/yahoo/libkyahoo/messagereceivertask.h
new file mode 100644
index 00000000..b9682315
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/messagereceivertask.h
@@ -0,0 +1,49 @@
+/*
+ Kopete Yahoo Protocol
+ Receive Messages
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 MESSAGERECEIVERTASK_H
+#define MESSAGERECEIVERTASK_H
+
+#include "task.h"
+
+class QString;
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class MessageReceiverTask : public Task
+{
+Q_OBJECT
+public:
+ MessageReceiverTask(Task *parent);
+ ~MessageReceiverTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+ void parseMessage( YMSGTransfer *transfer );
+ void parseNotify( YMSGTransfer *transfer );
+signals:
+ void gotIm(const QString&, const QString&, long, int);
+ void gotBuzz( const QString &who, long tm );
+ void systemMessage(const QString&);
+ void gotTypingNotify(const QString &, int);
+ void gotWebcamInvite(const QString &);
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/modifybuddytask.cpp b/kopete/protocols/yahoo/libkyahoo/modifybuddytask.cpp
new file mode 100644
index 00000000..afae97cf
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/modifybuddytask.cpp
@@ -0,0 +1,116 @@
+/*
+ Kopete Yahoo Protocol
+ Add a buddy to the Contactlist
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "modifybuddytask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+ModifyBuddyTask::ModifyBuddyTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+ModifyBuddyTask::~ModifyBuddyTask()
+{
+}
+
+void ModifyBuddyTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ switch( m_type )
+ {
+ case AddBuddy:
+ addBuddy();
+ break;
+ case RemoveBuddy:
+ removeBuddy();
+ break;
+ case MoveBuddy:
+ moveBuddy();
+ break;
+ }
+
+
+
+ setSuccess( true );
+}
+
+void ModifyBuddyTask::addBuddy()
+{
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceAddBuddy);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 7, m_target.local8Bit() );
+ t->setParam( 14, m_message.utf8() );
+ t->setParam( 65, m_group.local8Bit() );
+ t->setParam( 97, 1 ); // UTF-8
+ send( t );
+}
+
+void ModifyBuddyTask::removeBuddy()
+{
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceRemBuddy);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 7, m_target.local8Bit() );
+ t->setParam( 65, m_group.local8Bit() );
+ send( t );
+}
+
+void ModifyBuddyTask::moveBuddy()
+{
+ YMSGTransfer *mov = new YMSGTransfer( Yahoo::ServiceBuddyChangeGroup );
+ mov->setId( client()->sessionID() );
+ mov->setParam( 1, client()->userId().local8Bit() );
+ mov->setParam( 302, 240 );
+ mov->setParam( 300, 240 );
+ mov->setParam( 7, m_target.local8Bit() );
+ mov->setParam( 224, m_oldGroup.local8Bit() );
+ mov->setParam( 264, m_group.local8Bit() );
+ mov->setParam( 301, 240 );
+ mov->setParam( 303, 240 );
+ send( mov );
+}
+
+void ModifyBuddyTask::setTarget( const QString &target )
+{
+ m_target = target;
+}
+
+void ModifyBuddyTask::setMessage( const QString &text )
+{
+ m_message = text;
+}
+
+void ModifyBuddyTask::setGroup( const QString &group )
+{
+ m_group = group;
+}
+
+void ModifyBuddyTask::setOldGroup( const QString &old )
+{
+ m_oldGroup = old;
+}
+
+void ModifyBuddyTask::setType( Type type )
+{
+ m_type = type;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/modifybuddytask.h b/kopete/protocols/yahoo/libkyahoo/modifybuddytask.h
new file mode 100644
index 00000000..7438a25f
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/modifybuddytask.h
@@ -0,0 +1,53 @@
+/*
+ Kopete Yahoo Protocol
+ Add, remove or move a buddy to the Contactlist
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 MODIFYBUDDYTASK_H
+#define MODIFYBUDDYTASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class ModifyBuddyTask : public Task
+{
+public:
+ enum Type { AddBuddy, RemoveBuddy, MoveBuddy };
+ ModifyBuddyTask(Task *parent);
+ ~ModifyBuddyTask();
+
+ virtual void onGo();
+
+ void setType( Type type );
+ void setMessage( const QString &text );
+ void setTarget( const QString &target );
+ void setGroup( const QString &group );
+ void setOldGroup( const QString &group );
+private:
+ void addBuddy();
+ void removeBuddy();
+ void moveBuddy();
+
+ QString m_message;
+ QString m_target;
+ QString m_group;
+ QString m_oldGroup;
+ Type m_type;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/modifyyabtask.cpp b/kopete/protocols/yahoo/libkyahoo/modifyyabtask.cpp
new file mode 100644
index 00000000..fe741726
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/modifyyabtask.cpp
@@ -0,0 +1,205 @@
+/*
+ Kopete Yahoo Protocol
+ modifyyabtask.h - Handles the Yahoo Address Book
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "modifyyabtask.h"
+#include "yabtask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qdatastream.h>
+#include <qdom.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kbufferedsocket.h>
+
+using namespace KNetwork;
+ModifyYABTask::ModifyYABTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_socket = 0;
+}
+
+ModifyYABTask::~ModifyYABTask()
+{
+ delete m_socket;
+}
+
+void ModifyYABTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_socket = new KBufferedSocket( "address.yahoo.com", QString::number(80) );
+ connect( m_socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( connectSucceeded() ) );
+ connect( m_socket, SIGNAL( gotError(int) ), this, SLOT( connectFailed(int) ) );
+
+ m_socket->connect();
+}
+
+void ModifyYABTask::setAction( Action action )
+{
+ m_action = action;
+}
+
+void ModifyYABTask::setEntry( const YABEntry &entry )
+{
+ QDomDocument doc("");
+ QDomElement root = doc.createElement( "ab" );
+ QDomProcessingInstruction instr = doc.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\" ");
+ doc.appendChild(instr);
+ root.setAttribute( "k", client()->userId() );
+ root.setAttribute( "cc", "1" );
+ doc.appendChild( root );
+
+ QDomElement contact = doc.createElement( "ct" );
+ entry.fillQDomElement( contact );
+ switch( m_action )
+ {
+ case EditEntry:
+ contact.setAttribute( "e", "1" );
+ break;
+ case AddEntry:
+ contact.setAttribute( "a", "1" );
+ break;
+ case DeleteEntry:
+ contact.setAttribute( "d", "1" );
+ break;
+ }
+ root.appendChild( contact );
+
+ entry.dump();
+ m_postData = doc.toString();
+}
+
+void ModifyYABTask::connectFailed( int i)
+{
+ m_socket->close();
+ client()->notifyError( i18n( "An error occured saving the Addressbook entry." ),
+ QString( "%1 - %2").arg(i).arg(static_cast<const KBufferedSocket*>( sender() )->errorString()), Client::Error );
+}
+
+void ModifyYABTask::connectSucceeded()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString header = QString::fromLatin1("POST /yab/us?v=XM&prog=ymsgr&.intl=us&sync=1&tags=short&noclear=1& HTTP/1.1\r\n"
+ "Cookie: Y=%1; T=%2; C=%3 ;B=fckeert1kk1nl&b=2\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+ "Host: address.yahoo.com\r\n"
+ "Content-length: %4\r\n"
+ "Cache-Control: no-cache\r\n\r\n")
+ .arg(client()->yCookie()).arg(client()->tCookie())
+ .arg(client()->cCookie()).arg(m_postData.utf8().size());
+
+ QByteArray buffer;
+ QByteArray paket;
+ QDataStream stream( buffer, IO_WriteOnly );
+ stream.writeRawBytes( header.local8Bit(), header.length() );
+ stream.writeRawBytes( m_postData.utf8(), m_postData.utf8().size() );
+
+ if( m_socket->writeBlock( buffer, buffer.size() ) )
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Successful. Waiting for confirmation..." << endl;
+ else
+ {
+ client()->notifyError( i18n( "An error occured saving the Addressbook entry." ), m_socket->errorString(), Client::Error );
+ setSuccess( false );
+ return;
+ }
+
+ connect( m_socket, SIGNAL( readyRead() ), this, SLOT( slotRead() ) );
+}
+
+void ModifyYABTask::slotRead()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ QByteArray ar( m_socket->bytesAvailable() );
+ m_socket->readBlock ( ar.data (), ar.size () );
+ QString buf( ar );
+ m_data += buf.right( buf.length() - buf.find("<?xml") );
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << m_data.find("</ab>") << endl;
+ if( m_data.find("</ab>") < 0 )
+ return; // Need more data
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << m_data.find("</ab>") << endl;
+
+ m_socket->close();
+ QDomDocument doc;
+ QDomNodeList list;
+ QDomElement e;
+ uint it = 0;
+
+ doc.setContent( m_data );
+
+ list = doc.elementsByTagName( "ab" ); // Get the Addressbook
+ for( it = 0; it < list.count(); it++ ) {
+ if( !list.item( it ).isElement() )
+ continue;
+ e = list.item( it ).toElement();
+
+ if( !e.attribute( "lm" ).isEmpty() )
+ emit gotRevision( e.attribute( "lm" ).toLong(), true );
+
+ if( !e.attribute( "rt" ).isEmpty() )
+ emit gotRevision( e.attribute( "rt" ).toLong(), false );
+ }
+
+ list = doc.elementsByTagName( "ct" ); // Get records
+ for( it = 0; it < list.count(); it++ ) {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Parsing entry..." << endl;
+ if( !list.item( it ).isElement() )
+ continue;
+ e = list.item( it ).toElement();
+
+ YABEntry *entry = new YABEntry;
+ entry->fromQDomElement( e );
+ entry->source = YABEntry::SourceYAB;
+
+ switch( m_action )
+ {
+ case EditEntry:
+ if( !e.attribute( "es" ).isEmpty() && e.attribute( "es" ) != "0" ) // Check for edit errors
+ {
+ emit error( entry, i18n("The Yahoo Addressbook entry could not be saved:\n%1 - %2").arg( e.attribute("es") ).arg( e.attribute("ee") ) );
+ continue;
+ }
+ break;
+ case AddEntry:
+ if( !e.attribute( "as" ).isEmpty() && e.attribute( "as" ) != "0" ) // Check for add errors
+ {
+ emit error( entry, i18n("The Yahoo Addressbook entry could not be created:\n%1 - %2").arg( e.attribute("as") ).arg( e.attribute("ae") ) );
+ continue;
+ }
+ break;
+ case DeleteEntry:
+ if( !e.attribute( "ds" ).isEmpty() && e.attribute( "ds" ) != "0" ) // Check for delete errors
+ {
+ emit error( entry, i18n("The Yahoo Addressbook entry could not be deleted:\n%1 - %2").arg( e.attribute("ds") ).arg( e.attribute("de") ) );
+ continue;
+ }
+ break;
+ }
+
+ // No errors occured
+ emit gotEntry( entry );
+ }
+
+
+ setSuccess( true );
+}
+#include "modifyyabtask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/modifyyabtask.h b/kopete/protocols/yahoo/libkyahoo/modifyyabtask.h
new file mode 100644
index 00000000..488ae741
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/modifyyabtask.h
@@ -0,0 +1,64 @@
+/*
+ Kopete Yahoo Protocol
+ modifyyabtask.h - Saves a YAB entry
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef MODIFYYABTASK_H
+#define MODIFYYABTASK_H
+
+#include "task.h"
+#include "yabentry.h"
+
+struct KURL;
+namespace KIO {
+ class Job;
+ class TransferJob;
+}
+namespace KNetwork {
+ class KBufferedSocket;
+}
+class QDomElement;
+
+/**
+@author André Duffeck
+*/
+class ModifyYABTask : public Task
+{
+ Q_OBJECT
+public:
+ enum Action { AddEntry, EditEntry, DeleteEntry };
+ ModifyYABTask(Task *parent);
+ ~ModifyYABTask();
+
+ virtual void onGo();
+ void setAction( Action action );
+ void setEntry( const YABEntry & );
+signals:
+ void gotEntry( YABEntry * );
+ void gotRevision( long rev, bool merged );
+ void error( YABEntry *, const QString &);
+private slots:
+ void connectSucceeded();
+ void connectFailed( int );
+ void slotRead();
+private:
+ KIO::TransferJob *m_transferJob;
+ KNetwork::KBufferedSocket *m_socket;
+ QString m_postData;
+ QString m_data;
+ Action m_action;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/oscartypes.h b/kopete/protocols/yahoo/libkyahoo/oscartypes.h
new file mode 100644
index 00000000..944019e7
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/oscartypes.h
@@ -0,0 +1,31 @@
+/*
+ Kopete Oscar Protocol
+ oscartypes.h - Oscar Type Definitions
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef _YAHOOTYPES_H_
+#define _YAHOOTYPES_H_
+
+#include <qglobal.h>
+
+namespace Yahoo
+{
+ typedef Q_UINT8 BYTE;
+ typedef Q_UINT16 WORD;
+ typedef Q_UINT32 DWORD;
+}
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.cpp b/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.cpp
new file mode 100644
index 00000000..6259f7e8
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.cpp
@@ -0,0 +1,157 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about buddy icons
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "picturenotifiertask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kurl.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+PictureNotifierTask::PictureNotifierTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+PictureNotifierTask::~PictureNotifierTask()
+{
+
+}
+
+bool PictureNotifierTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ switch( t->service() )
+ {
+ case Yahoo::ServicePictureStatus:
+ parsePictureStatus( t );
+ break;
+ case Yahoo::ServicePictureChecksum:
+ parsePictureChecksum( t );
+ break;
+ case Yahoo::ServicePicture:
+ parsePicture( t );
+ break;
+ case Yahoo::ServicePictureUpload:
+ parsePictureUploadResponse( t );
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool PictureNotifierTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+
+ if ( t->service() == Yahoo::ServicePictureChecksum ||
+ t->service() == Yahoo::ServicePicture ||
+ t->service() == Yahoo::ServicePictureUpdate ||
+ t->service() == Yahoo::ServicePictureUpload ||
+ t->service() == Yahoo::ServicePictureStatus )
+ return true;
+ else
+ return false;
+}
+
+void PictureNotifierTask::parsePictureStatus( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString nick; /* key = 4 */
+ int state; /* key = 213 */
+
+ nick = t->firstParam( 4 );
+ state = t->firstParam( 213 ).toInt();
+
+ emit pictureStatusNotify( nick, state );
+}
+
+void PictureNotifierTask::parsePictureChecksum( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString nick; /* key = 4 */
+ int checksum; /* key = 192 */
+
+ nick = t->firstParam( 4 );
+ checksum = t->firstParam( 192 ).toInt();
+
+ if( nick != client()->userId() )
+ emit pictureChecksumNotify( nick, checksum );
+}
+
+void PictureNotifierTask::parsePicture( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString nick; /* key = 4 */
+ int type; /* key = 13: 1 = request, 2 = notification */
+ QString url; /* key = 20 */
+ int checksum; /* key = 192 */
+
+ nick = t->firstParam( 4 );
+ url = t->firstParam( 20 );
+ checksum = t->firstParam( 192 ).toInt();
+ type = t->firstParam( 13 ).toInt();
+
+ if( type == 1 )
+ emit pictureRequest( nick );
+ else if( type == 2 )
+ emit pictureInfoNotify( nick, KURL( url ), checksum );
+}
+
+void PictureNotifierTask::parsePictureUploadResponse( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString url;
+ QString error;
+
+ url = t->firstParam( 20 );
+ error = t->firstParam( 16 );
+
+ if( !error.isEmpty() )
+ client()->notifyError(i18n("The picture was not successfully uploaded"), error, Client::Error );
+
+ if( !url.isEmpty() )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Emitting url: " << url << endl;
+ emit pictureUploaded( url );
+ }
+}
+
+#include "picturenotifiertask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.h b/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.h
new file mode 100644
index 00000000..b6580903
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/picturenotifiertask.h
@@ -0,0 +1,51 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about buddy icons
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 PICTURENOTIFIERTASK_H
+#define PICTURENOTIFIERTASK_H
+
+#include "task.h"
+
+class QString;
+class KURL;
+class YMSGTransfer;
+/**
+@author André Duffeck
+*/
+class PictureNotifierTask : public Task
+{
+Q_OBJECT
+public:
+ PictureNotifierTask(Task *parent);
+ ~PictureNotifierTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+ void parsePictureChecksum( YMSGTransfer *transfer );
+ void parsePictureStatus( YMSGTransfer *transfer );
+ void parsePicture( YMSGTransfer *transfer );
+ void parsePictureUploadResponse( YMSGTransfer *transfer );
+signals:
+ void pictureStatusNotify( const QString &, int );
+ void pictureChecksumNotify( const QString &, int );
+ void pictureInfoNotify( const QString &, KURL, int );
+ void pictureRequest( const QString & );
+ void pictureUploaded( const QString & );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/pingtask.cpp b/kopete/protocols/yahoo/libkyahoo/pingtask.cpp
new file mode 100644
index 00000000..022d8e7f
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/pingtask.cpp
@@ -0,0 +1,46 @@
+/*
+ pingtask.cpp
+ Send a ping to the server
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "pingtask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+PingTask::PingTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+PingTask::~PingTask()
+{
+}
+
+void PingTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePing7);
+ t->setParam( 0, client()->userId().local8Bit() );
+ t->setId( client()->sessionID() );
+ send( t );
+
+ setSuccess( true );
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/pingtask.h b/kopete/protocols/yahoo/libkyahoo/pingtask.h
new file mode 100644
index 00000000..955e7304
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/pingtask.h
@@ -0,0 +1,36 @@
+/*
+ pingtask.h
+ Send a ping to the server
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef PINGTASK_H
+#define PINGTASK_H
+
+#include "task.h"
+
+/**
+@author André Duffeck
+*/
+class PingTask : public Task
+{
+public:
+ PingTask(Task *parent);
+ ~PingTask();
+
+ virtual void onGo();
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/receivefiletask.cpp b/kopete/protocols/yahoo/libkyahoo/receivefiletask.cpp
new file mode 100644
index 00000000..7b4f2fc3
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/receivefiletask.cpp
@@ -0,0 +1,243 @@
+/*
+ Kopete Yahoo Protocol
+ Receive a file
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "receivefiletask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qtimer.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+
+ReceiveFileTask::ReceiveFileTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_transmitted = 0;
+ m_file = 0;
+ m_transferJob = 0;
+}
+
+ReceiveFileTask::~ReceiveFileTask()
+{
+ delete m_file;
+ m_file = 0;
+}
+
+void ReceiveFileTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceFileTransfer7);
+ switch( m_type )
+ {
+ case FileTransferAccept:
+ m_file = new QFile( m_localUrl.path() );
+ if( !m_file->open( IO_WriteOnly ) )
+ {
+ emit error( m_transferId, KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Could not open file for writing.") );
+ setSuccess( false );
+ return;
+ }
+ m_transferJob = KIO::get( m_remoteUrl, false, false );
+ QObject::connect( m_transferJob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotComplete( KIO::Job* ) ) );
+ QObject::connect( m_transferJob, SIGNAL( data( KIO::Job*, const QByteArray & ) ), this, SLOT( slotData( KIO::Job*, const QByteArray & ) ) );
+ delete t;
+ break;
+ case FileTransfer7Accept:
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, m_userId.local8Bit() );
+ t->setParam( 265, m_remoteUrl.url().local8Bit() );
+ t->setParam( 222, 3 );
+
+ send( t );
+ break;
+ case FileTransfer7Reject:
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, m_userId.local8Bit() );
+ t->setParam( 265, m_remoteUrl.url().local8Bit() );
+ t->setParam( 222, 4 );
+
+ send( t );
+ break;
+ default:
+ delete t;
+ }
+}
+
+bool ReceiveFileTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);
+
+ parseFileTransfer7Info( t );
+
+ return true;
+}
+
+bool ReceiveFileTask::forMe( Transfer *transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+
+ if( t->service() == Yahoo::ServiceFileTransfer7Info )
+ {
+ // Only take this transfer if we are the corresponding task (in case of simultaneous file transfers)
+ if( t->firstParam( 265 ) == m_remoteUrl.url().local8Bit() )
+ return true;
+ else
+ return false;
+ }
+ else
+ return false;
+}
+
+void ReceiveFileTask::slotData( KIO::Job *job, const QByteArray& data )
+{
+ Q_UNUSED( job );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ m_transmitted += data.size();
+ emit bytesProcessed( m_transferId, m_transmitted );
+ m_file->writeBlock( data.data() , data.size() );
+
+}
+
+void ReceiveFileTask::slotComplete( KIO::Job *job )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ KIO::TransferJob *transfer = static_cast< KIO::TransferJob * >(job);
+
+ if( m_file )
+ m_file->close();
+ if ( job->error () || transfer->isErrorPage () )
+ {
+ emit error( m_transferId, KIO::ERR_ABORTED, i18n("An error occured while downloading the file.") );
+ setSuccess( false );
+ }
+ else
+ {
+ emit complete( m_transferId );
+ setSuccess( true );
+ }
+}
+
+void ReceiveFileTask::parseFileTransfer7Info( YMSGTransfer *transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if( transfer->firstParam( 249 ).toInt() == 1 )
+ {
+ // Reject P2P Transfer offer
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceFileTransfer7Accept);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, transfer->firstParam( 4 ) );
+ t->setParam( 265, transfer->firstParam( 265 ) );
+ t->setParam( 66, -3 );
+
+ send( t );
+ }
+ else if( transfer->firstParam( 249 ).toInt() == 3 )
+ {
+ m_file = new QFile( m_localUrl.path() );
+ if( !m_file->open( IO_WriteOnly ) )
+ {
+ emit error( m_transferId, KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Could not open file for writing.") );
+ setSuccess( false );
+ return;
+ }
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceFileTransfer7Accept);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, transfer->firstParam( 4 ) );
+ t->setParam( 265, transfer->firstParam( 265 ) );
+ t->setParam( 27, transfer->firstParam( 27 ) );
+ t->setParam( 249, 3 ); // Use Reflection server
+ t->setParam( 251, transfer->firstParam( 251 ) );
+
+ send( t );
+ // The server expects a HTTP HEAD command prior to the GET
+ m_mimetypeJob = KIO::mimetype(QString::fromLatin1("http://%1/relay?token=%2&sender=%3&recver=%4")
+ .arg(transfer->firstParam( 250 )).arg(transfer->firstParam( 251 )).arg(m_userId).arg(client()->userId()), false);
+ m_mimetypeJob->addMetaData("cookies", "manual");
+ m_mimetypeJob->addMetaData("setcookies", QString::fromLatin1("Cookie: T=%1; path=/; domain=.yahoo.com; Y=%2; C=%3;")
+ .arg(client()->tCookie()).arg(client()->yCookie()).arg(client()->cCookie()) );
+
+
+ m_transferJob = KIO::get( QString::fromLatin1("http://%1/relay?token=%2&sender=%3&recver=%4")
+ .arg(transfer->firstParam( 250 )).arg(transfer->firstParam( 251 )).arg(m_userId).arg(client()->userId()), false, false );
+ QObject::connect( m_transferJob, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotComplete( KIO::Job* ) ) );
+ QObject::connect( m_transferJob, SIGNAL( data( KIO::Job*, const QByteArray & ) ), this, SLOT( slotData( KIO::Job*, const QByteArray & ) ) );
+ m_transferJob->addMetaData("cookies", "manual");
+ m_transferJob->addMetaData("setcookies", QString::fromLatin1("Cookie: T=%1; path=/; domain=.yahoo.com; Y=%2; C=%3;")
+ .arg(client()->tCookie()).arg(client()->yCookie()).arg(client()->cCookie()) );
+ }
+}
+
+void ReceiveFileTask::setRemoteUrl( KURL url )
+{
+ m_remoteUrl = url;
+}
+
+void ReceiveFileTask::setLocalUrl( KURL url )
+{
+ m_localUrl = url;
+}
+
+void ReceiveFileTask::setTransferId( unsigned int transferId )
+{
+ m_transferId = transferId;
+}
+
+void ReceiveFileTask::setType( Type type )
+{
+ m_type = type;
+}
+
+void ReceiveFileTask::setUserId( const QString &userId )
+{
+ m_userId = userId;
+}
+
+void ReceiveFileTask::canceled( unsigned int id )
+{
+ if( m_transferId != id )
+ return;
+
+ if( m_transferJob )
+ m_transferJob->kill();
+
+ setSuccess( false );
+}
+
+#include "receivefiletask.moc"
+
diff --git a/kopete/protocols/yahoo/libkyahoo/receivefiletask.h b/kopete/protocols/yahoo/libkyahoo/receivefiletask.h
new file mode 100644
index 00000000..79bcb605
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/receivefiletask.h
@@ -0,0 +1,83 @@
+/*
+ Kopete Yahoo Protocol
+ Receive a file
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 RECEIVEFILETASK_H
+#define RECEIVEFILETASK_H
+
+#include "task.h"
+#include <qfile.h>
+#include <kurl.h>
+
+class QString;
+class QFile;
+namespace KIO {
+ class Job;
+ class TransferJob;
+ class MimetypeJob;
+}
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class ReceiveFileTask : public Task
+{
+ Q_OBJECT
+public:
+ enum Type { FileTransferAccept, FileTransfer7Accept, FileTransfer7Reject };
+ ReceiveFileTask(Task *parent);
+ ~ReceiveFileTask();
+
+ virtual void onGo();
+
+ void setRemoteUrl( KURL url );
+ void setLocalUrl( KURL url );
+ void setFileName( const QString &filename );
+ void setTransferId( unsigned int transferId );
+ void setType( Type type );
+ void setUserId( const QString & userId );
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+
+signals:
+ void bytesProcessed( unsigned int, unsigned int );
+ void complete( unsigned int );
+ void error( unsigned int, int, const QString & );
+
+private slots:
+ void slotData( KIO::Job *job, const QByteArray &data );
+ void slotComplete( KIO::Job *job );
+ void canceled( unsigned int );
+
+private:
+ void parseFileTransfer7Info( YMSGTransfer *transfer );
+
+ KURL m_remoteUrl;
+ KURL m_localUrl;
+ QString m_fileName;
+ QString m_userId;
+ QFile *m_file;
+ KIO::TransferJob *m_transferJob;
+ KIO::MimetypeJob *m_mimetypeJob;
+ unsigned int m_transferId;
+ unsigned int m_transmitted;
+ Type m_type;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/requestpicturetask.cpp b/kopete/protocols/yahoo/libkyahoo/requestpicturetask.cpp
new file mode 100644
index 00000000..6527737f
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/requestpicturetask.cpp
@@ -0,0 +1,52 @@
+/*
+ Kopete Yahoo Protocol
+ Request a Picture of a Buddy
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "requestpicturetask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+RequestPictureTask::RequestPictureTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+RequestPictureTask::~RequestPictureTask()
+{
+}
+
+void RequestPictureTask::onGo()
+{
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePicture);
+ t->setId( client()->sessionID() );
+ t->setParam( 4, client()->userId().local8Bit());
+ t->setParam( 5, m_target.local8Bit() );
+ t->setParam( 13, "1" );
+ send( t );
+
+ setSuccess( true );
+}
+
+void RequestPictureTask::setTarget( const QString &target )
+{
+ m_target = target;
+}
+
+#include "requestpicturetask.moc"
+
diff --git a/kopete/protocols/yahoo/libkyahoo/requestpicturetask.h b/kopete/protocols/yahoo/libkyahoo/requestpicturetask.h
new file mode 100644
index 00000000..146f585e
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/requestpicturetask.h
@@ -0,0 +1,41 @@
+/*
+ Kopete Yahoo Protocol
+ Request a Picture of a Buddy
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 REQUESTPICTURETASK_H
+#define REQUESTPICTURETASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class RequestPictureTask : public Task
+{
+Q_OBJECT
+public:
+ RequestPictureTask(Task *parent);
+ ~RequestPictureTask();
+
+ virtual void onGo();
+
+ void setTarget( const QString &target );
+private:
+ QString m_target;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/safedelete.cpp b/kopete/protocols/yahoo/libkyahoo/safedelete.cpp
new file mode 100644
index 00000000..703e8ed3
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/safedelete.cpp
@@ -0,0 +1,139 @@
+/*
+ safedelete.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "safedelete.h"
+
+#include <qtimer.h>
+
+//----------------------------------------------------------------------------
+// SafeDelete
+//----------------------------------------------------------------------------
+SafeDelete::SafeDelete()
+{
+ lock = 0;
+}
+
+SafeDelete::~SafeDelete()
+{
+ if(lock)
+ lock->dying();
+}
+
+void SafeDelete::deleteLater(QObject *o)
+{
+ if(!lock)
+ deleteSingle(o);
+ else
+ list.append(o);
+}
+
+void SafeDelete::unlock()
+{
+ lock = 0;
+ deleteAll();
+}
+
+void SafeDelete::deleteAll()
+{
+ if(list.isEmpty())
+ return;
+
+ QObjectListIt it(list);
+ for(QObject *o; (o = it.current()); ++it)
+ deleteSingle(o);
+ list.clear();
+}
+
+void SafeDelete::deleteSingle(QObject *o)
+{
+#if QT_VERSION < 0x030000
+ // roll our own QObject::deleteLater()
+ SafeDeleteLater *sdl = SafeDeleteLater::ensureExists();
+ sdl->deleteItLater(o);
+#else
+ o->deleteLater();
+#endif
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLock
+//----------------------------------------------------------------------------
+SafeDeleteLock::SafeDeleteLock(SafeDelete *sd)
+{
+ own = false;
+ if(!sd->lock) {
+ _sd = sd;
+ _sd->lock = this;
+ }
+ else
+ _sd = 0;
+}
+
+SafeDeleteLock::~SafeDeleteLock()
+{
+ if(_sd) {
+ _sd->unlock();
+ if(own)
+ delete _sd;
+ }
+}
+
+void SafeDeleteLock::dying()
+{
+ _sd = new SafeDelete(*_sd);
+ own = true;
+}
+
+//----------------------------------------------------------------------------
+// SafeDeleteLater
+//----------------------------------------------------------------------------
+SafeDeleteLater *SafeDeleteLater::self = 0;
+
+SafeDeleteLater *SafeDeleteLater::ensureExists()
+{
+ if(!self)
+ new SafeDeleteLater();
+ return self;
+}
+
+SafeDeleteLater::SafeDeleteLater()
+{
+ list.setAutoDelete(true);
+ self = this;
+ QTimer::singleShot(0, this, SLOT(explode()));
+}
+
+SafeDeleteLater::~SafeDeleteLater()
+{
+ list.clear();
+ self = 0;
+}
+
+void SafeDeleteLater::deleteItLater(QObject *o)
+{
+ list.append(o);
+}
+
+void SafeDeleteLater::explode()
+{
+ delete this;
+}
+
+#include "safedelete.moc"
+
diff --git a/kopete/protocols/yahoo/libkyahoo/safedelete.h b/kopete/protocols/yahoo/libkyahoo/safedelete.h
new file mode 100644
index 00000000..e8215c06
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/safedelete.h
@@ -0,0 +1,79 @@
+/*
+ gwclientstream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SAFEDELETE_H
+#define SAFEDELETE_H
+
+#include<qobject.h>
+#include<qobjectlist.h>
+
+class SafeDelete;
+class SafeDeleteLock
+{
+public:
+ SafeDeleteLock(SafeDelete *sd);
+ ~SafeDeleteLock();
+
+private:
+ SafeDelete *_sd;
+ bool own;
+ friend class SafeDelete;
+ void dying();
+};
+
+class SafeDelete
+{
+public:
+ SafeDelete();
+ ~SafeDelete();
+
+ void deleteLater(QObject *o);
+
+ // same as QObject::deleteLater()
+ static void deleteSingle(QObject *o);
+
+private:
+ QObjectList list;
+ void deleteAll();
+
+ friend class SafeDeleteLock;
+ SafeDeleteLock *lock;
+ void unlock();
+};
+
+class SafeDeleteLater : public QObject
+{
+ Q_OBJECT
+public:
+ static SafeDeleteLater *ensureExists();
+ void deleteItLater(QObject *o);
+
+private slots:
+ void explode();
+
+private:
+ SafeDeleteLater();
+ ~SafeDeleteLater();
+
+ QObjectList list;
+ friend class SafeDelete;
+ static SafeDeleteLater *self;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sendauthresptask.cpp b/kopete/protocols/yahoo/libkyahoo/sendauthresptask.cpp
new file mode 100644
index 00000000..7c40e708
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendauthresptask.cpp
@@ -0,0 +1,73 @@
+/*
+ Kopete Yahoo Protocol
+ Send a authorization request response
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2003-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "sendauthresptask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+SendAuthRespTask::SendAuthRespTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+SendAuthRespTask::~SendAuthRespTask()
+{
+}
+
+void SendAuthRespTask::onGo()
+{
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceAuthorization);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, m_target.local8Bit() );
+ if( m_granted )
+ {
+ t->setParam( 13, 1 );
+ }
+ else
+ {
+ t->setParam( 13, 2 );
+ t->setParam( 97, 1 ); // UTF
+ t->setParam( 14, m_msg.utf8() );
+
+ }
+ send( t );
+
+ setSuccess( true );
+}
+
+void SendAuthRespTask::setGranted( bool granted )
+{
+ m_granted = granted;
+}
+
+void SendAuthRespTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void SendAuthRespTask::setMessage( const QString &msg )
+{
+ m_msg = msg;
+}
+
+
+#include "sendauthresptask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/sendauthresptask.h b/kopete/protocols/yahoo/libkyahoo/sendauthresptask.h
new file mode 100644
index 00000000..8c0beb90
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendauthresptask.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Yahoo Protocol
+ Send a authorization request response
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2003-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef SENDAUTHRESPTASK_H
+#define SENDAUTHRESPTASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class SendAuthRespTask : public Task
+{
+Q_OBJECT
+public:
+ SendAuthRespTask(Task *parent);
+ ~SendAuthRespTask();
+
+ virtual void onGo();
+
+ void setGranted( bool );
+ void setTarget( const QString &to );
+ void setMessage( const QString &msg );
+private:
+ QString m_target;
+ bool m_granted;
+ QString m_msg;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sendfiletask.cpp b/kopete/protocols/yahoo/libkyahoo/sendfiletask.cpp
new file mode 100644
index 00000000..d0f843f2
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendfiletask.cpp
@@ -0,0 +1,189 @@
+/*
+ Kopete Yahoo Protocol
+ Send a file
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "sendfiletask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qtimer.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstreamsocket.h>
+#include <kio/global.h>
+
+using namespace KNetwork;
+
+SendFileTask::SendFileTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_transmitted = 0;
+ m_socket = 0;
+}
+
+SendFileTask::~SendFileTask()
+{
+ m_socket->deleteLater();
+ m_socket = 0;
+}
+
+void SendFileTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QTimer::singleShot( 0, this, SLOT(initiateUpload()) );
+}
+
+void SendFileTask::initiateUpload()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_socket = new KStreamSocket( "filetransfer.msg.yahoo.com", QString::number(80) );
+ m_socket->setBlocking( true );
+ connect( m_socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( connectSucceeded() ) );
+ connect( m_socket, SIGNAL( gotError(int) ), this, SLOT( connectFailed(int) ) );
+
+ m_socket->connect();
+}
+
+void SendFileTask::connectFailed( int i )
+{
+ QString err = m_socket->errorString();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << i << ": " << err << endl;
+ emit error( m_transferId, i, err );
+ setSuccess( false );
+}
+
+void SendFileTask::connectSucceeded()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer t( Yahoo::ServiceFileTransfer );
+
+ m_file.setName( m_url.path() );
+
+ t.setId( client()->sessionID() );
+ t.setParam( 0, client()->userId().local8Bit());
+ t.setParam( 5, m_target.local8Bit());
+ t.setParam( 28, m_file.size() );
+ t.setParam( 27, m_url.fileName().local8Bit() );
+ t.setParam( 14, "" );
+ QByteArray buffer;
+ QByteArray paket;
+ QDataStream stream( buffer, IO_WriteOnly );
+
+ if ( m_file.open(IO_ReadOnly ) )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "File successfully opened. Reading..." << endl;
+ }
+ else
+ {
+ client()->notifyError( i18n( "An error occured sending the file." ), m_file.errorString(), Client::Error );
+ setSuccess( false );
+ return;
+ }
+
+ paket = t.serialize();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Sizes: File (" << m_url << "): " << m_file.size() << " - paket: " << paket.size() << endl;
+ QString header = QString::fromLatin1("POST http://filetransfer.msg.yahoo.com:80/notifyft HTTP/1.1\r\n"
+ "Cookie: Y=%1; T=%2; C=%3 ;B=fckeert1kk1nl&b=2\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+ "Host: filetransfer.msg.yahoo.com:80\r\n"
+ "Content-length: %4\r\n"
+ "Cache-Control: no-cache\r\n\r\n").arg(client()->yCookie()).arg(client()->tCookie()).arg(client()->cCookie()).arg(m_file.size()+4+paket.size());
+ stream.writeRawBytes( header.local8Bit(), header.length() );
+ stream.writeRawBytes( paket.data(), paket.size() );
+ stream << (Q_INT8)0x32 << (Q_INT8)0x39 << (Q_INT8)0xc0 << (Q_INT8)0x80;
+
+ if( !m_socket->writeBlock( buffer, buffer.size() ) )
+ {
+ emit error( m_transferId, m_socket->error(), m_socket->errorString() );
+ m_socket->close();
+ }
+ else
+ {
+ connect( m_socket, SIGNAL(readyWrite()), this, SLOT(transmitData()) );
+ m_socket->enableWrite( true );
+ }
+}
+
+void SendFileTask::transmitData()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ int read = 0;
+ int written = 0;
+ char buf[1024];
+
+ m_socket->enableWrite( false );
+ read = m_file.readBlock( buf, 1024 );
+ written = m_socket->writeBlock( buf, read );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "read:" << read << " written: " << written << endl;
+
+ m_transmitted += read;
+ emit bytesProcessed( m_transferId, m_transmitted );
+
+ if( written != read )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Failed!" << endl;
+ emit error( m_transferId, m_socket->error(), m_socket->errorString() );
+ setSuccess( false );
+ return;
+ }
+ if( m_transmitted == m_file.size() )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Successful: " << m_transmitted << endl;
+ emit complete( m_transferId );
+ setSuccess( true );
+ m_socket->close();
+ }
+ else
+ {
+ m_socket->enableWrite( true );
+ }
+}
+void SendFileTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void SendFileTask::setMessage( const QString &msg )
+{
+ m_msg = msg;
+}
+
+void SendFileTask::setFileUrl( KURL url )
+{
+ m_url = url;
+
+}
+
+void SendFileTask::setTransferId( unsigned int transferId )
+{
+ m_transferId = transferId;
+}
+
+void SendFileTask::canceled( unsigned int id )
+{
+ if( m_transferId != id )
+ return;
+
+ if( m_socket )
+ m_socket->close();
+
+ setSuccess( false );
+}
+
+#include "sendfiletask.moc"
+
diff --git a/kopete/protocols/yahoo/libkyahoo/sendfiletask.h b/kopete/protocols/yahoo/libkyahoo/sendfiletask.h
new file mode 100644
index 00000000..41e62f77
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendfiletask.h
@@ -0,0 +1,68 @@
+/*
+ Kopete Yahoo Protocol
+ Send a file
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 SENDFILETASK_H
+#define SENDFILETASK_H
+
+#include "task.h"
+#include <kurl.h>
+#include <qfile.h>
+
+class QString;
+namespace KNetwork{
+ class KStreamSocket;
+}
+
+/**
+@author André Duffeck
+*/
+class SendFileTask : public Task
+{
+ Q_OBJECT
+public:
+ SendFileTask(Task *parent);
+ ~SendFileTask();
+
+ virtual void onGo();
+
+ void setTarget( const QString &to );
+ void setMessage( const QString &msg );
+ void setFileUrl( KURL url );
+ void setTransferId( unsigned int transferId );
+
+signals:
+ void bytesProcessed( unsigned int, unsigned int );
+ void complete( unsigned int );
+ void error( unsigned int, int, const QString & );
+
+private slots:
+ void initiateUpload();
+ void connectSucceeded();
+ void connectFailed( int );
+ void transmitData();
+ void canceled( unsigned int );
+
+private:
+ QString m_msg;
+ QString m_target;
+ KURL m_url;
+ QFile m_file;
+ unsigned int m_transferId;
+ unsigned int m_transmitted;
+ KNetwork::KStreamSocket *m_socket;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sendmessagetask.cpp b/kopete/protocols/yahoo/libkyahoo/sendmessagetask.cpp
new file mode 100644
index 00000000..d93ffcb9
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendmessagetask.cpp
@@ -0,0 +1,80 @@
+/*
+ Kopete Yahoo Protocol
+ Send a message
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+SendMessageTask::SendMessageTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+SendMessageTask::~SendMessageTask()
+{
+}
+
+void SendMessageTask::onGo()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if( m_text.isEmpty() )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Text to send is empty." << endl;
+ client()->notifyError( i18n( "An error occured sending the message" ), i18n( "The message is empty." ), Client::Debug );
+ return;
+ }
+ uint pos=0;
+
+ // split messages that are longer than 800 chars. they get dropped otherwise
+ while( pos < m_text.length() )
+ {
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceMessage, Yahoo::StatusOffline);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit() );
+ t->setParam( 5, m_target.local8Bit() );
+ t->setParam( 14, m_text.mid( pos, 700).utf8() );
+ t->setParam( 63, ";0" );
+ t->setParam( 64, "0" );
+ t->setParam( 97, 1 ); // UTF-8
+ t->setParam( 206, client()->pictureFlag() );
+ send( t );
+
+ pos += 700;
+ }
+
+ setSuccess( true );
+}
+
+void SendMessageTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void SendMessageTask::setText( const QString &text )
+{
+ m_text = text;
+}
+
+void SendMessageTask::setPicureFlag( int flag )
+{
+ m_pictureFlag = flag;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/sendmessagetask.h b/kopete/protocols/yahoo/libkyahoo/sendmessagetask.h
new file mode 100644
index 00000000..41a44ded
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendmessagetask.h
@@ -0,0 +1,44 @@
+/*
+ Kopete Yahoo Protocol
+ Send a message
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class SendMessageTask : public Task
+{
+public:
+ SendMessageTask(Task *parent);
+ ~SendMessageTask();
+
+ virtual void onGo();
+
+ void setText( const QString &text );
+ void setTarget( const QString &to );
+ void setPicureFlag( int flag );
+private:
+ QString m_text;
+ QString m_target;
+ int m_pictureFlag;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sendnotifytask.cpp b/kopete/protocols/yahoo/libkyahoo/sendnotifytask.cpp
new file mode 100644
index 00000000..8fd56115
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendnotifytask.cpp
@@ -0,0 +1,80 @@
+/*
+ Kopete Yahoo Protocol
+ Send a notification
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "sendnotifytask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <kdebug.h>
+
+SendNotifyTask::SendNotifyTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+SendNotifyTask::~SendNotifyTask()
+{
+}
+
+void SendNotifyTask::onGo()
+{
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceNotify);
+ t->setId( client()->sessionID() );
+ t->setStatus( Yahoo::StatusNotify );
+ t->setParam( 4, client()->userId().local8Bit() );
+ t->setParam( 5, m_target.local8Bit() );
+ t->setParam( 14, " " );
+ switch( m_type )
+ {
+ case NotifyTyping:
+ t->setParam( 13, m_state );
+ t->setParam( 49, "TYPING" );
+ break;
+ case NotifyWebcamInvite:
+ t->setParam( 13, 0 );
+ t->setParam( 49, "WEBCAMINVITE" );
+ break;
+ case NotifyGame:
+ default:
+ setSuccess( false );
+ delete t;
+ return;
+ break;
+ }
+ send( t );
+
+ setSuccess( true );
+}
+
+void SendNotifyTask::setType( Type type )
+{
+ m_type = type;
+}
+
+void SendNotifyTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void SendNotifyTask::setState( State state)
+{
+ m_state = state;
+}
+
+
+#include "sendnotifytask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/sendnotifytask.h b/kopete/protocols/yahoo/libkyahoo/sendnotifytask.h
new file mode 100644
index 00000000..6eb9f6dd
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendnotifytask.h
@@ -0,0 +1,48 @@
+/*
+ Kopete Yahoo Protocol
+ Send a notification
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 SENDNOTIFYTASK_H
+#define SENDNOTIFYTASK_H
+
+#include "task.h"
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class SendNotifyTask : public Task
+{
+Q_OBJECT
+public:
+ enum Type { NotifyTyping, NotifyWebcamInvite, NotifyGame };
+ enum State { Active = 1, NotActive = 0 };
+
+ SendNotifyTask(Task *parent);
+ ~SendNotifyTask();
+
+ virtual void onGo();
+
+ void setType( Type type );
+ void setTarget( const QString &to );
+ void setState( State );
+private:
+ QString m_target;
+ Type m_type;
+ State m_state;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sendpicturetask.cpp b/kopete/protocols/yahoo/libkyahoo/sendpicturetask.cpp
new file mode 100644
index 00000000..c1b1f5f0
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendpicturetask.cpp
@@ -0,0 +1,247 @@
+/*
+ Kopete Yahoo Protocol
+ sendpicturetask.cpp - Send our picture or information about it
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "sendpicturetask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qfile.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+using namespace KNetwork;
+
+SendPictureTask::SendPictureTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_socket = 0;
+}
+
+SendPictureTask::~SendPictureTask()
+{
+ delete m_socket;
+}
+
+void SendPictureTask::onGo()
+{
+ switch( m_type )
+ {
+ case UploadPicture:
+ initiateUpload();
+ break;
+ case SendChecksum:
+ sendChecksum();
+ break;
+ case SendInformation:
+ sendInformation();
+ case SendStatus:
+ sendStatus();
+ break;
+ }
+}
+
+void SendPictureTask::initiateUpload()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_socket = new KBufferedSocket( "filetransfer.msg.yahoo.com", QString::number(80) );
+ connect( m_socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( connectSucceeded() ) );
+ connect( m_socket, SIGNAL( gotError(int) ), this, SLOT( connectFailed(int) ) );
+
+ m_socket->connect();
+}
+
+void SendPictureTask::connectFailed( int i)
+{
+ m_socket->close();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << i << ": " << static_cast<const KBufferedSocket*>( sender() )->errorString() << endl;
+ client()->notifyError(i18n("The picture was not successfully uploaded"), QString("%1 - %2").arg(i).arg(static_cast<const KBufferedSocket*>( sender() )->errorString()), Client::Error );
+ setSuccess( false );
+}
+
+void SendPictureTask::connectSucceeded()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer t(Yahoo::ServicePictureUpload);
+
+ QFile file( m_path );
+
+ t.setId( client()->sessionID() );
+ t.setParam( 1, client()->userId().local8Bit());
+ t.setParam( 38, 604800);
+ t.setParam( 0, client()->userId().local8Bit());
+ t.setParam( 28, file.size() );
+ t.setParam( 27, m_fileName.local8Bit() );
+ t.setParam( 14, "" );
+ QByteArray buffer;
+ QByteArray paket;
+ QDataStream stream( buffer, IO_WriteOnly );
+
+ if ( file.open(IO_ReadOnly ) )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "File successfully opened. Reading..." << endl;
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error opening file: " << file.errorString() << endl;
+ client()->notifyError(i18n("Error opening file: %1").arg(m_path), file.errorString(), Client::Error );
+ return;
+ }
+
+ paket = t.serialize();
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Sizes: File (" << m_path << "): " << file.size() << " - paket: " << paket.size() << endl;
+ QString header = QString::fromLatin1("POST /notifyft HTTP/1.1\r\n"
+ "Cookie: Y=%1; T=%2; C=%3 ;\r\n"
+ "User-Agent: Mozilla/4.0 (compatible; MSIE 5.5)\r\n"
+ "Host: filetransfer.msg.yahoo.com\r\n"
+ "Content-length: %4\r\n"
+ "Cache-Control: no-cache\r\n\r\n").arg(client()->yCookie()).arg(client()->tCookie()).arg(client()->cCookie()).arg(file.size()+4+paket.size());
+ stream.writeRawBytes( header.local8Bit(), header.length() );
+ stream.writeRawBytes( paket.data(), paket.size() );
+ stream << (Q_INT8)0x32 << (Q_INT8)0x39 << (Q_INT8)0xc0 << (Q_INT8)0x80;
+ stream.writeRawBytes( file.readAll(), file.size() );
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Buffersize: " << buffer.size() << endl;
+ if( m_socket->writeBlock( buffer, buffer.size() ) )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Successful." << endl;
+ connect( m_socket, SIGNAL( readyRead() ), this, SLOT( readResult() ) );
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Failed." << endl;
+ m_socket->close();
+ setSuccess( false );
+ }
+}
+
+void SendPictureTask::readResult()
+{
+ QByteArray ar( m_socket->bytesAvailable() );
+ m_socket->readBlock ( ar.data (), ar.size () );
+ QString buf( ar );
+
+ m_socket->close();
+ if( buf.find( "error", 0, false ) >= 0 )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Picture upload failed" << endl;
+ setSuccess( false );
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Picture upload acknowledged." << endl;
+ setSuccess( true );
+ }
+
+}
+
+void SendPictureTask::sendChecksum()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePictureChecksum);
+ t->setId( client()->sessionID() );
+ t->setParam(1, client()->userId().local8Bit());
+ if( !m_target.isEmpty() )
+ t->setParam( 5, m_target.local8Bit() );
+ t->setParam(192, m_checksum);
+ t->setParam(212, 1);
+ send( t );
+
+ setSuccess( true );
+}
+
+void SendPictureTask::sendInformation()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePicture);
+ t->setId( client()->sessionID() );
+ t->setParam(1, client()->userId().local8Bit());
+ t->setParam(4, client()->userId().local8Bit());
+ t->setParam(13, 2 );
+ t->setParam(5, m_target.local8Bit() );
+ t->setParam(20, m_url.local8Bit() );
+ t->setParam(192, m_checksum);
+
+ send( t );
+
+ setSuccess( true );
+}
+
+void SendPictureTask::sendStatus()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServicePictureUpdate);
+ t->setId( client()->sessionID() );
+ t->setParam(1, client()->userId().local8Bit());
+ t->setParam(5, m_target.local8Bit() );
+ t->setParam(206, m_status );
+
+ send( t );
+
+ setSuccess( true );
+}
+
+void SendPictureTask::setType( Type type )
+{
+ m_type = type;
+}
+
+void SendPictureTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void SendPictureTask::setFilename( const QString &filename )
+{
+ m_fileName = filename;
+}
+
+void SendPictureTask::setFilesize( int filesize )
+{
+ m_fileSize = filesize;
+}
+
+void SendPictureTask::setPath( const QString &path )
+{
+ m_path = path;
+}
+
+void SendPictureTask::setChecksum( int checksum )
+{
+ m_checksum = checksum;
+}
+
+void SendPictureTask::setStatus( int status )
+{
+ m_status = status;
+}
+
+void SendPictureTask::setUrl( const QString &url )
+{
+ m_url = url;
+}
+
+#include "sendpicturetask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/sendpicturetask.h b/kopete/protocols/yahoo/libkyahoo/sendpicturetask.h
new file mode 100644
index 00000000..da008eb5
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sendpicturetask.h
@@ -0,0 +1,77 @@
+/*
+ Kopete Yahoo Protocol
+ sendpicturetask.h - Send our picture or information about it
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 SENDPICTURETASK_H
+#define SENDPICTURETASK_H
+
+#include "task.h"
+
+class QString;
+class QFile;
+namespace KIO {
+ class Job;
+ class TransferJob;
+}
+namespace KNetwork {
+ class KBufferedSocket;
+}
+
+/**
+@author André Duffeck
+*/
+class SendPictureTask : public Task
+{
+Q_OBJECT
+public:
+ enum Type { UploadPicture, SendChecksum, SendInformation, SendStatus };
+
+ SendPictureTask(Task *parent);
+ ~SendPictureTask();
+
+ virtual void onGo();
+
+ void setType( Type type );
+ void setTarget( const QString &to );
+ void setFilename( const QString & );
+ void setFilesize( int );
+ void setPath( const QString & );
+ void setChecksum( int );
+ void setStatus( int );
+ void setUrl( const QString & );
+private:
+ void initiateUpload();
+ void sendChecksum();
+ void sendInformation();
+ void sendStatus();
+private slots:
+ void connectSucceeded();
+ void connectFailed( int );
+ void readResult();
+private:
+ Type m_type;
+ QString m_target;
+ QString m_fileName;
+ int m_fileSize;
+ QString m_path;
+ int m_checksum;
+ int m_status;
+ QString m_url;
+ int m_transmitted;
+ QFile *m_file;
+ KNetwork::KBufferedSocket *m_socket;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/sha1.c b/kopete/protocols/yahoo/libkyahoo/sha1.c
new file mode 100644
index 00000000..c9a0edbf
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sha1.c
@@ -0,0 +1,628 @@
+/*-
+ * Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * Define WORDS_BIGENDIAN if compiling on a big-endian architecture.
+ *
+ * Define SHA1_TEST to test the implementation using the NIST's
+ * sample messages. The output should be:
+ *
+ * a9993e36 4706816a ba3e2571 7850c26c 9cd0d89d
+ * 84983e44 1c3bd26e baae4aa1 f95129e5 e54670f1
+ * 34aa973c d4c4daa4 f61eeb2b dbad2731 6534016f
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+#include <string.h>
+
+#include "sha1.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* !lint */
+
+#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+#define ROTR(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
+
+#define F_0_19(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define F_20_39(x, y, z) ((x) ^ (y) ^ (z))
+#define F_40_59(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+#define F_60_79(x, y, z) ((x) ^ (y) ^ (z))
+
+#define DO_ROUND(F, K) { \
+ temp = ROTL(a, 5) + F(b, c, d) + e + *(W++) + K; \
+ e = d; \
+ d = c; \
+ c = ROTL(b, 30); \
+ b = a; \
+ a = temp; \
+}
+
+#define K_0_19 0x5a827999L
+#define K_20_39 0x6ed9eba1L
+#define K_40_59 0x8f1bbcdcL
+#define K_60_79 0xca62c1d6L
+
+#ifndef RUNTIME_ENDIAN
+
+#ifdef WORDS_BIGENDIAN
+
+#define BYTESWAP(x) (x)
+#define BYTESWAP64(x) (x)
+
+#else /* WORDS_BIGENDIAN */
+
+#define BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | (ROTL((x), 8) & 0x00ff00ffL))
+
+static uint64_t _byteswap64(uint64_t x)
+{
+ uint32_t a = x >> 32;
+ uint32_t b = (uint32_t) x;
+ return ((uint64_t) BYTESWAP(b) << 32) | (uint64_t) BYTESWAP(a);
+}
+
+#define BYTESWAP64(x) _byteswap64(x)
+
+
+
+#endif /* WORDS_BIGENDIAN */
+
+#else /* !RUNTIME_ENDIAN */
+
+#define BYTESWAP(x) _byteswap(sc->littleEndian, x)
+#define BYTESWAP64(x) _byteswap64(sc->littleEndian, x)
+
+#define _BYTESWAP(x) ((ROTR((x), 8) & 0xff00ff00L) | \
+ (ROTL((x), 8) & 0x00ff00ffL))
+#define _BYTESWAP64(x) __byteswap64(x)
+
+static uint64_t __byteswap64(uint64_t x)
+{
+ uint32_t a = x >> 32;
+ uint32_t b = (uint32_t) x;
+ return ((uint64_t) _BYTESWAP(b) << 32) | (uint64_t) _BYTESWAP(a);
+}
+
+static uint32_t _byteswap(int littleEndian, uint32_t x)
+{
+ if (!littleEndian)
+ return x;
+ else
+ return _BYTESWAP(x);
+}
+
+static uint64_t _byteswap64(int littleEndian, uint64_t x)
+{
+ if (!littleEndian)
+ return x;
+ else
+ return _BYTESWAP64(x);
+}
+
+static void setEndian(int *littleEndianp)
+{
+ union {
+ uint32_t w;
+ uint8_t b[4];
+ } endian;
+
+ endian.w = 1L;
+ *littleEndianp = endian.b[0] != 0;
+}
+
+#endif /* !RUNTIME_ENDIAN */
+
+static const uint8_t padding[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void
+SHA1Init (SHA1Context *sc)
+{
+#ifdef RUNTIME_ENDIAN
+ setEndian (&sc->littleEndian);
+#endif /* RUNTIME_ENDIAN */
+
+ sc->totalLength = 0LL;
+ sc->hash[0] = 0x67452301L;
+ sc->hash[1] = 0xefcdab89L;
+ sc->hash[2] = 0x98badcfeL;
+ sc->hash[3] = 0x10325476L;
+ sc->hash[4] = 0xc3d2e1f0L;
+ sc->bufferLength = 0L;
+}
+
+static void
+burnStack (int size)
+{
+ char buf[128];
+
+ memset (buf, 0, sizeof (buf));
+ size -= sizeof (buf);
+ if (size > 0)
+ burnStack (size);
+}
+
+static void
+SHA1Guts (SHA1Context *sc, const uint32_t *cbuf)
+{
+ uint32_t buf[80];
+ uint32_t *W, *W3, *W8, *W14, *W16;
+ uint32_t a, b, c, d, e, temp;
+ int i;
+
+ W = buf;
+
+ for (i = 15; i >= 0; i--) {
+ *(W++) = BYTESWAP(*cbuf);
+ cbuf++;
+ }
+
+ W16 = &buf[0];
+ W14 = &buf[2];
+ W8 = &buf[8];
+ W3 = &buf[13];
+
+ for (i = 63; i >= 0; i--) {
+ *W = *(W3++) ^ *(W8++) ^ *(W14++) ^ *(W16++);
+ *W = ROTL(*W, 1);
+ W++;
+ }
+
+ a = sc->hash[0];
+ b = sc->hash[1];
+ c = sc->hash[2];
+ d = sc->hash[3];
+ e = sc->hash[4];
+
+ W = buf;
+
+#ifndef SHA1_UNROLL
+#define SHA1_UNROLL 20
+#endif /* !SHA1_UNROLL */
+
+#if SHA1_UNROLL == 1
+ for (i = 19; i >= 0; i--)
+ DO_ROUND(F_0_19, K_0_19);
+
+ for (i = 19; i >= 0; i--)
+ DO_ROUND(F_20_39, K_20_39);
+
+ for (i = 19; i >= 0; i--)
+ DO_ROUND(F_40_59, K_40_59);
+
+ for (i = 19; i >= 0; i--)
+ DO_ROUND(F_60_79, K_60_79);
+#elif SHA1_UNROLL == 2
+ for (i = 9; i >= 0; i--) {
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ }
+
+ for (i = 9; i >= 0; i--) {
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ }
+
+ for (i = 9; i >= 0; i--) {
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ }
+
+ for (i = 9; i >= 0; i--) {
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ }
+#elif SHA1_UNROLL == 4
+ for (i = 4; i >= 0; i--) {
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ }
+
+ for (i = 4; i >= 0; i--) {
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ }
+
+ for (i = 4; i >= 0; i--) {
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ }
+
+ for (i = 4; i >= 0; i--) {
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ }
+#elif SHA1_UNROLL == 5
+ for (i = 3; i >= 0; i--) {
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ }
+
+ for (i = 3; i >= 0; i--) {
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ }
+
+ for (i = 3; i >= 0; i--) {
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ }
+
+ for (i = 3; i >= 0; i--) {
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ }
+#elif SHA1_UNROLL == 10
+ for (i = 1; i >= 0; i--) {
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ }
+
+ for (i = 1; i >= 0; i--) {
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ }
+
+ for (i = 1; i >= 0; i--) {
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ }
+
+ for (i = 1; i >= 0; i--) {
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ }
+#elif SHA1_UNROLL == 20
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+ DO_ROUND(F_0_19, K_0_19);
+
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+ DO_ROUND(F_20_39, K_20_39);
+
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+ DO_ROUND(F_40_59, K_40_59);
+
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+ DO_ROUND(F_60_79, K_60_79);
+#else /* SHA1_UNROLL */
+#error SHA1_UNROLL must be 1, 2, 4, 5, 10 or 20!
+#endif
+
+ sc->hash[0] += a;
+ sc->hash[1] += b;
+ sc->hash[2] += c;
+ sc->hash[3] += d;
+ sc->hash[4] += e;
+}
+
+void
+SHA1Update (SHA1Context *sc, const void *vdata, uint32_t len)
+{
+ const uint8_t *data = vdata;
+ uint32_t bufferBytesLeft;
+ uint32_t bytesToCopy;
+ int needBurn = 0;
+
+#ifdef SHA1_FAST_COPY
+ if (sc->bufferLength) {
+ bufferBytesLeft = 64L - sc->bufferLength;
+
+ bytesToCopy = bufferBytesLeft;
+ if (bytesToCopy > len)
+ bytesToCopy = len;
+
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
+
+ sc->totalLength += bytesToCopy * 8L;
+
+ sc->bufferLength += bytesToCopy;
+ data += bytesToCopy;
+ len -= bytesToCopy;
+
+ if (sc->bufferLength == 64L) {
+ SHA1Guts (sc, sc->buffer.words);
+ needBurn = 1;
+ sc->bufferLength = 0L;
+ }
+ }
+
+ while (len > 63) {
+ sc->totalLength += 512L;
+
+ SHA1Guts (sc, data);
+ needBurn = 1;
+
+ data += 64L;
+ len -= 64L;
+ }
+
+ if (len) {
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, len);
+
+ sc->totalLength += len * 8L;
+
+ sc->bufferLength += len;
+ }
+#else /* SHA1_FAST_COPY */
+ while (len) {
+ bufferBytesLeft = 64L - sc->bufferLength;
+
+ bytesToCopy = bufferBytesLeft;
+ if (bytesToCopy > len)
+ bytesToCopy = len;
+
+ memcpy (&sc->buffer.bytes[sc->bufferLength], data, bytesToCopy);
+
+ sc->totalLength += bytesToCopy * 8L;
+
+ sc->bufferLength += bytesToCopy;
+ data += bytesToCopy;
+ len -= bytesToCopy;
+
+ if (sc->bufferLength == 64L) {
+ SHA1Guts (sc, sc->buffer.words);
+ needBurn = 1;
+ sc->bufferLength = 0L;
+ }
+ }
+#endif /* SHA1_FAST_COPY */
+
+ if (needBurn)
+ burnStack (sizeof (uint32_t[86]) + sizeof (uint32_t *[5]) + sizeof (int));
+}
+
+void
+SHA1Final (SHA1Context *sc, uint8_t hash[SHA1_HASH_SIZE])
+{
+ uint32_t bytesToPad;
+ uint64_t lengthPad;
+ int i;
+
+ bytesToPad = 120L - sc->bufferLength;
+ if (bytesToPad > 64L)
+ bytesToPad -= 64L;
+
+ lengthPad = BYTESWAP64(sc->totalLength);
+
+ SHA1Update (sc, padding, bytesToPad);
+ SHA1Update (sc, &lengthPad, 8L);
+
+ if (hash) {
+ for (i = 0; i < SHA1_HASH_WORDS; i++) {
+#ifdef SHA1_FAST_COPY
+ *((uint32_t *) hash) = BYTESWAP(sc->hash[i]);
+#else /* SHA1_FAST_COPY */
+ hash[0] = (uint8_t) (sc->hash[i] >> 24);
+ hash[1] = (uint8_t) (sc->hash[i] >> 16);
+ hash[2] = (uint8_t) (sc->hash[i] >> 8);
+ hash[3] = (uint8_t) sc->hash[i];
+#endif /* SHA1_FAST_COPY */
+ hash += 4;
+ }
+ }
+}
+
+#ifdef SHA1_TEST
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main (int argc, char *argv[])
+{
+ SHA1Context foo;
+ uint8_t hash[SHA1_HASH_SIZE];
+ char buf[1000];
+ int i;
+
+ SHA1Init (&foo);
+ SHA1Update (&foo, "abc", 3);
+ SHA1Final (&foo, hash);
+
+ for (i = 0; i < SHA1_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ SHA1Init (&foo);
+ SHA1Update (&foo,
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ 56);
+ SHA1Final (&foo, hash);
+
+ for (i = 0; i < SHA1_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ SHA1Init (&foo);
+ memset (buf, 'a', sizeof (buf));
+ for (i = 0; i < 1000; i++)
+ SHA1Update (&foo, buf, sizeof (buf));
+ SHA1Final (&foo, hash);
+
+ for (i = 0; i < SHA1_HASH_SIZE;) {
+ printf ("%02x", hash[i++]);
+ if (!(i % 4))
+ printf (" ");
+ }
+ printf ("\n");
+
+ exit (0);
+}
+
+#endif /* SHA1_TEST */
diff --git a/kopete/protocols/yahoo/libkyahoo/sha1.h b/kopete/protocols/yahoo/libkyahoo/sha1.h
new file mode 100644
index 00000000..02a4c732
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/sha1.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2001-2003 Allan Saddi <allan@saddi.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ALLAN SADDI AND HIS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ALLAN SADDI OR HIS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _SHA1_H
+#define _SHA1_H
+
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+#endif
+
+#define SHA1_HASH_SIZE 20
+
+/* Hash size in 32-bit words */
+#define SHA1_HASH_WORDS 5
+
+struct _SHA1Context {
+ uint64_t totalLength;
+ uint32_t hash[SHA1_HASH_WORDS];
+ uint32_t bufferLength;
+ union {
+ uint32_t words[16];
+ uint8_t bytes[64];
+ } buffer;
+#ifdef RUNTIME_ENDIAN
+ int littleEndian;
+#endif /* RUNTIME_ENDIAN */
+};
+
+typedef struct _SHA1Context SHA1Context;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void SHA1Init (SHA1Context *sc);
+void SHA1Update (SHA1Context *sc, const void *data, uint32_t len);
+void SHA1Final (SHA1Context *sc, uint8_t hash[SHA1_HASH_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SHA1_H */
diff --git a/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.cpp b/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.cpp
new file mode 100644
index 00000000..763d560c
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.cpp
@@ -0,0 +1,184 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about status changes of buddies
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "statusnotifiertask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+StatusNotifierTask::StatusNotifierTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+StatusNotifierTask::~StatusNotifierTask()
+{
+
+}
+
+bool StatusNotifierTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);
+
+ if( t->service() == Yahoo::ServiceStealthOffline )
+ parseStealthStatus( t );
+ else if( t->service() == Yahoo::ServiceAuthorization )
+ parseAuthorization( t );
+ else
+ parseStatus( t );
+
+ return true;
+}
+
+bool StatusNotifierTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+
+ if ( t->service() == Yahoo::ServiceLogon ||
+ t->service() == Yahoo::ServiceLogoff ||
+ t->service() == Yahoo::ServiceIsAway ||
+ t->service() == Yahoo::ServiceIsBack ||
+ t->service() == Yahoo::ServiceGameLogon ||
+ t->service() == Yahoo::ServiceGameLogoff ||
+ t->service() == Yahoo::ServiceIdAct ||
+ t->service() == Yahoo::ServiceIddeAct ||
+ t->service() == Yahoo::ServiceStatus ||
+ t->service() == Yahoo::ServiceStealthOffline ||
+ t->service() == Yahoo::ServiceAuthorization
+ )
+ return true;
+ else
+ return false;
+}
+
+void StatusNotifierTask::parseStatus( YMSGTransfer* t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if( t->status() == Yahoo::StatusDisconnected &&
+ t->service() == Yahoo::ServiceLogoff )
+ {
+ emit loginResponse( Yahoo::LoginDupl, QString::null );
+ }
+
+ QString myNick; /* key = 1 */
+ QString customError; /* key = 16 */
+ QString nick; /* key = 7 */
+ int state; /* key = 10 */
+ QString message; /* key = 19 */
+ int flags; /* key = 13 */
+ int away; /* key = 47 */
+ int idle; /* key = 137 */
+ bool utf; /* key = 97 */
+ int checksum; /* key = 192 */
+
+ customError = t->firstParam( 16 );
+ if( !customError.isEmpty() )
+ client()->notifyError( i18n("An unknown error has occured."), customError, Client::Warning );
+
+ myNick = t->firstParam( 1 );
+
+ for( int i = 0; i < t->paramCount( 7 ); ++i)
+ {
+ nick = t->nthParam( 7, i );
+ state = t->nthParamSeparated( 10, i, 7 ).toInt();
+ flags = t->nthParamSeparated( 13, i, 7 ).toInt();
+ away = t->nthParamSeparated( 47, i, 7 ).toInt();
+ idle = t->nthParamSeparated( 137, i, 7 ).toInt();
+ utf = t->nthParamSeparated( 97, i, 7 ).toInt() == 1;
+ checksum = t->nthParamSeparated( 192, i, 7 ).toInt();
+ if( utf )
+ message = QString::fromUtf8( t->nthParamSeparated( 19, i, 7 ) );
+ else
+ message = t->nthParamSeparated( 19, i, 7 );
+
+ if( t->service() == Yahoo::ServiceLogoff || ( state != 0 && flags == 0 ) )
+ emit statusChanged( nick, Yahoo::StatusOffline, QString::null, 0, 0 );
+ else
+ emit statusChanged( nick, state, message, away, idle );
+
+ if( checksum )
+ emit gotPictureChecksum( nick, checksum );
+ }
+}
+
+void StatusNotifierTask::parseAuthorization( YMSGTransfer* t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString nick; /* key = 4 */
+ QString msg; /* key = 14 */
+ int state; /* key = 13 */
+ bool utf; /* key = 97 */
+
+ utf = t->firstParam( 97 ).toInt() == 1;
+ nick = t->firstParam( 4 );
+ if( utf )
+ msg = QString::fromUtf8( t->firstParam( 14 ) );
+ else
+ msg = t->firstParam( 14 );
+ state = t->firstParam( 13 ).toInt();
+
+ if( state == 1 )
+ {
+ emit( authorizationAccepted( nick ) );
+ }
+ else if( state == 2 )
+ {
+ emit( authorizationRejected( nick, msg ) );
+ }
+ else // This is a request
+ {
+ QString fname = t->firstParam( 216 );
+ QString lname = t->firstParam( 254 );
+ QString name;
+ if( !fname.isEmpty() || !lname.isEmpty() )
+ name = QString("%1 %2").arg(fname).arg(lname);
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Emitting gotAuthorizationRequest( " << nick<< ", " << msg << ", " << name << " )" << endl;
+ emit gotAuthorizationRequest( nick, msg, name );
+ }
+}
+
+void StatusNotifierTask::parseStealthStatus( YMSGTransfer* t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString nick; /* key = 7 */
+ int state; /* key = 31 */
+
+ nick = t->firstParam( 7 );
+ state = t->firstParam( 31 ).toInt();
+
+ emit stealthStatusChanged( nick, ( state == 1 ) ? Yahoo::StealthActive : Yahoo::StealthNotActive );
+}
+
+#include "statusnotifiertask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.h b/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.h
new file mode 100644
index 00000000..c7b45b1c
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/statusnotifiertask.h
@@ -0,0 +1,53 @@
+/*
+ Kopete Yahoo Protocol
+ Notifies about status changes of buddies
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 STATUSNOTIFIERTASK_H
+#define STATUSNOTIFIERTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+
+class QString;
+class YMSGTransfer;
+
+/**
+@author André Duffeck
+*/
+class StatusNotifierTask : public Task
+{
+Q_OBJECT
+public:
+ StatusNotifierTask(Task *parent);
+ ~StatusNotifierTask();
+
+ bool take(Transfer *transfer);
+
+protected:
+ bool forMe( Transfer *transfer ) const;
+ void parseStatus( YMSGTransfer *transfer );
+ void parseStealthStatus( YMSGTransfer *transfer );
+ void parseAuthorization( YMSGTransfer *transfer );
+signals:
+ void statusChanged( const QString&, int, const QString&, int, int );
+ void stealthStatusChanged( const QString&, Yahoo::StealthStatus );
+ void loginResponse( int, const QString& );
+ void authorizationAccepted( const QString & );
+ void authorizationRejected( const QString &, const QString & );
+ void gotAuthorizationRequest( const QString &, const QString &, const QString & );
+ void gotPictureChecksum( const QString &, int );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/stealthtask.cpp b/kopete/protocols/yahoo/libkyahoo/stealthtask.cpp
new file mode 100644
index 00000000..01ab4e27
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/stealthtask.cpp
@@ -0,0 +1,76 @@
+/*
+ Kopete Yahoo Protocol
+ Stealth/Unstealth a buddy
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "stealthtask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+
+StealthTask::StealthTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+StealthTask::~StealthTask()
+{
+}
+
+void StealthTask::onGo()
+{
+ YMSGTransfer *t = new YMSGTransfer();
+ if( m_mode == Yahoo::StealthOnline )
+ {
+ t->setService( Yahoo::ServiceStealthOnline );
+ t->setParam( 13, "1" );
+ t->setParam( 31, m_state );
+ }
+ else if( m_mode == Yahoo::StealthOffline )
+ {
+ t->setService( Yahoo::ServiceStealthOffline );
+ t->setParam( 13, "1" );
+ t->setParam( 31, m_state );
+ }
+ else if( m_mode == Yahoo::StealthPermOffline )
+ {
+ t->setService( Yahoo::ServiceStealthOffline );
+ t->setParam( 13, "2" );
+ t->setParam( 31, m_state );
+ }
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit());
+ if( !m_target.isEmpty() )
+ t->setParam( 7, m_target.local8Bit() );
+ send( t );
+
+ setSuccess( true );
+}
+
+void StealthTask::setTarget( const QString &to )
+{
+ m_target = to;
+}
+
+void StealthTask::setState( Yahoo::StealthStatus state)
+{
+ m_state = state;
+}
+
+void StealthTask::setMode( Yahoo::StealthMode mode )
+{
+ m_mode = mode;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/stealthtask.h b/kopete/protocols/yahoo/libkyahoo/stealthtask.h
new file mode 100644
index 00000000..62e70340
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/stealthtask.h
@@ -0,0 +1,46 @@
+/*
+ Kopete Yahoo Protocol
+ Stealth/Unstealth a buddy
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 STEALTHTASK_H
+#define STEALTHTASK_H
+
+#include "task.h"
+#include "yahootypes.h"
+#include <kdebug.h>
+
+class QString;
+
+/**
+@author André Duffeck
+*/
+class StealthTask : public Task
+{
+public:
+ StealthTask(Task *parent);
+ ~StealthTask();
+
+ virtual void onGo();
+
+ void setTarget( const QString &to );
+ void setState( Yahoo::StealthStatus state );
+ void setMode( Yahoo::StealthMode mode );
+private:
+ QString m_target;
+ Yahoo::StealthMode m_mode;
+ Yahoo::StealthStatus m_state;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/stream.cpp b/kopete/protocols/yahoo/libkyahoo/stream.cpp
new file mode 100644
index 00000000..02967416
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/stream.cpp
@@ -0,0 +1,31 @@
+/*
+ stream.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "stream.h"
+
+Stream::Stream(QObject *parent)
+:QObject(parent)
+{
+}
+
+Stream::~Stream()
+{
+}
+
+#include "stream.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/stream.h b/kopete/protocols/yahoo/libkyahoo/stream.h
new file mode 100644
index 00000000..b5aa0452
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/stream.h
@@ -0,0 +1,76 @@
+/*
+ stream.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Based on code copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qobject.h>
+
+#ifndef YAHOO_STREAM_H
+#define YAHOO_STREAM_H
+
+class Transfer;
+
+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;
+
+ /**
+ * 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( Transfer *request) = 0;
+
+
+signals:
+ void connectionClosed();
+ void delayedCloseFinished();
+ void readyRead();
+// void stanzaWritten();
+ void error(int);
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/task.cpp b/kopete/protocols/yahoo/libkyahoo/task.cpp
new file mode 100644
index 00000000..805168a9
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/task.cpp
@@ -0,0 +1,265 @@
+/*
+ task.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <qtimer.h>
+
+#include "client.h"
+#include "transfer.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;
+}
+
+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 ))
+ {
+ qDebug( "Transfer ACCEPTED by: %s", t->className() );
+ return true;
+ }
+/* else
+ qDebug( "Transfer refused by: %s", t->className() );*/
+ }
+
+ return false;
+}
+
+void Task::safeDelete()
+{
+ if(d->deleteme)
+ return;
+
+ d->deleteme = true;
+ if(!d->insignificant)
+ SafeDelete::deleteSingle(this);
+}
+
+void Task::onGo()
+{
+ qDebug( "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( Transfer * 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;
+ 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/yahoo/libkyahoo/task.h b/kopete/protocols/yahoo/libkyahoo/task.h
new file mode 100644
index 00000000..581512b3
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/task.h
@@ -0,0 +1,93 @@
+/*
+ task.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOO_TASK_H
+#define YAHOO_TASK_H
+
+#include <qobject.h>
+
+class QString;
+
+class Client;
+class Request;
+class Transfer;
+
+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( Transfer * request );
+ void setSuccess( int code=0, const QString &str="" );
+ 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/yahoo/libkyahoo/tests/Makefile.am b/kopete/protocols/yahoo/libkyahoo/tests/Makefile.am
new file mode 100644
index 00000000..6e644285
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/tests/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(top_srcdir)/kopete/protocols/yahoo/libkyahoo -I../ $(all_includes)
+METASOURCES = AUTO
+check_PROGRAMS = clientstream_test
+
+clientstream_test_SOURCES = clientstream_test.cpp
+clientstream_test_LDADD = $(LIB_QT) $(LIB_KDECORE) ../libkyahoo.la
+
+#login_test_SOURCES = logintest.cpp
+#login_test_LDADD = $(LIB_QT) $(LIB_KDECORE) ../libkyahoo.la
diff --git a/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.cpp b/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.cpp
new file mode 100644
index 00000000..a52b1f56
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.cpp
@@ -0,0 +1,57 @@
+//Licensed under the GNU General Public License
+
+#include "clientstream_test.h"
+#include <kdebug.h>
+#include "../ymsgtransfer.h"
+#include "../yahootypes.h"
+
+ClientStreamTest::ClientStreamTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ //myConnector->setOptHostPort( "localhost", 8300 );
+ myConnector->setOptHostPort( "scs.msg.yahoo.com", 5050 );
+ myTestObject = new ClientStream( myConnector, myConnector);
+ // notify when the transport layer is connected
+ connect( myTestObject, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ // notify and start sending
+ //connect( myTestObject, SIGNAL( warning(int) ), SLOT( slotWarning(int) ) );
+
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+ClientStreamTest::~ClientStreamTest()
+{
+ delete myTestObject;
+ delete myConnector;
+}
+
+void ClientStreamTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("scs.msg.yahoo.com");
+ // connect to server
+ kdDebug(14180) << k_funcinfo << " connecting to server" << endl;
+ myTestObject->connectToServer( server, true ); // fine up to here...
+}
+
+void ClientStreamTest::slotConnected()
+{
+ kdDebug(14180) << k_funcinfo << " connection is up" << endl;
+ connected = true;
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceLogon);
+ t->setParam( 1, "kopetetest");
+
+ myTestObject->write(t);
+ while(1);
+}
+
+int main(int argc, char ** argv)
+{
+ ClientStreamTest a( argc, argv );
+ a.exec();
+ return 0;
+}
+
+#include "clientstream_test.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.h b/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.h
new file mode 100644
index 00000000..ef367cec
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/tests/clientstream_test.h
@@ -0,0 +1,49 @@
+//
+// C++ Implementation: clientstream_test
+//
+// Description:
+//
+//
+// Author: Kopete Developers <kopete-devel@kde.org>, (C) 2004
+// Licensed under the GNU General Public License
+
+#ifndef clientstream_test_h
+#define clientstream_test_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "yahooclientstream.h"
+#include "yahooconnector.h"
+
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class ClientStreamTest : public QApplication
+{
+Q_OBJECT
+public:
+ ClientStreamTest(int argc, char ** argv);
+
+ ~ClientStreamTest();
+
+ bool isConnected();
+
+public slots:
+ void slotDoTest();
+
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myTestObject;
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/tests/logintest.cpp b/kopete/protocols/yahoo/libkyahoo/tests/logintest.cpp
new file mode 100644
index 00000000..8778d9da
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/tests/logintest.cpp
@@ -0,0 +1,72 @@
+/*
+ Kopete Yahoo Protocol Tests
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Based on code
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "logintest.h"
+#include <kdebug.h>
+#include "../ymsgtransfer.h"
+#include "../yahootypes.h"
+
+LoginTest::LoginTest(int argc, char ** argv) : QApplication( argc, argv )
+{
+ // set up client stream
+ myConnector = new KNetworkConnector( 0 );
+ //myConnector->setOptHostPort( "localhost", 8300 );
+ myConnector->setOptHostPort( "scs.msg.yahoo.com", 5050 );
+ myClientStream = new ClientStream( myConnector, myConnector);
+ // notify when the transport layer is connected
+ myClient = new Client();
+ // do test once the event loop is running
+ QTimer::singleShot( 0, this, SLOT( slotDoTest() ) );
+ connected = false;
+}
+
+LoginTest::~LoginTest()
+{
+ delete myClientStream;
+ delete myConnector;
+ delete myClient;
+}
+
+void LoginTest::slotDoTest()
+{
+ QString server = QString::fromLatin1("scs.msg.yahoo.com");
+ // connect to server
+ kdDebug(14180) << k_funcinfo << " connecting to server" << endl;
+
+ connect( myClient, SIGNAL( connected() ), SLOT( slotConnected() ) );
+ myClient->start( server, 5050, "duncanmacvicar", "**********" );
+ myClient->connectToServer( myClientStream, server, true );
+}
+
+void LoginTest::slotConnected()
+{
+ kdDebug(14180) << k_funcinfo << " connection is up" << endl;
+ connected = true;
+}
+
+int main(int argc, char ** argv)
+{
+ LoginTest a( argc, argv );
+ a.exec();
+ if ( !a.isConnected() )
+ return 0;
+}
+
+#include "logintest.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/tests/logintest.h b/kopete/protocols/yahoo/libkyahoo/tests/logintest.h
new file mode 100644
index 00000000..12274843
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/tests/logintest.h
@@ -0,0 +1,64 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Based on code
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef logintest_h
+#define logintest_h
+
+#include <qglobal.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+#include "client.h"
+#include "coreprotocol.h"
+#include "yahooclientstream.h"
+#include "yahooconnector.h"
+
+#include "coreprotocol.h"
+
+#define QT_FATAL_ASSERT 1
+
+class LoginTest : public QApplication
+{
+Q_OBJECT
+public:
+ LoginTest(int argc, char ** argv);
+
+ ~LoginTest();
+
+ bool isConnected() { return connected; }
+
+public slots:
+ void slotDoTest();
+
+ void slotConnected();
+
+ //void slotWarning(int warning);
+
+ //void slotsend(int layer);
+
+private:
+ KNetworkConnector *myConnector;
+ ClientStream *myClientStream;
+ Client* myClient;
+
+ bool connected;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/transfer.cpp b/kopete/protocols/yahoo/libkyahoo/transfer.cpp
new file mode 100644
index 00000000..cc7f9b0a
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/transfer.cpp
@@ -0,0 +1,26 @@
+/*
+ transfer.cpp - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "transfer.h"
+
+Transfer::Transfer()
+{
+}
+
+Transfer::~Transfer()
+{
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/transfer.h b/kopete/protocols/yahoo/libkyahoo/transfer.h
new file mode 100644
index 00000000..dfa17b21
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/transfer.h
@@ -0,0 +1,35 @@
+/*
+ transfer.h - Kopete Groupwise Protocol
+
+ Copyright (c) 2004 SUSE Linux AG http://www.suse.com
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef TRANSFER_H
+#define TRANSFER_H
+
+/*class Buffer;*/
+
+class Transfer
+{
+public:
+ enum TransferType { YMSGTransfer };
+ Transfer();
+ virtual ~Transfer();
+
+ virtual TransferType type() = 0;
+
+};
+
+#endif
+
diff --git a/kopete/protocols/yahoo/libkyahoo/webcamtask.cpp b/kopete/protocols/yahoo/libkyahoo/webcamtask.cpp
new file mode 100644
index 00000000..29087440
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/webcamtask.cpp
@@ -0,0 +1,689 @@
+/*
+ Kopete Yahoo Protocol
+ Handles incoming webcam connections
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 "webcamtask.h"
+#include "sendnotifytask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qbuffer.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <ktempfile.h>
+#include <kprocess.h>
+#include <kstreamsocket.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+using namespace KNetwork;
+
+WebcamTask::WebcamTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ transmittingData = false;
+ transmissionPending = false;
+ timestamp = 1;
+}
+
+WebcamTask::~WebcamTask()
+{
+}
+
+bool WebcamTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);
+
+ if( t->service() == Yahoo::ServiceWebcam )
+ parseWebcamInformation( t );
+// else
+// parseMessage( transfer );
+
+ return true;
+}
+
+bool WebcamTask::forMe( Transfer* transfer ) const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if ( t->service() == Yahoo::ServiceWebcam )
+ return true;
+ else
+ return false;
+}
+
+void WebcamTask::requestWebcam( const QString &who )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceWebcam);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit());
+ t->setParam( 5, who.local8Bit() );
+ keyPending = who;
+
+ send( t );
+}
+
+void WebcamTask::parseWebcamInformation( YMSGTransfer *t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YahooWebcamInformation info;
+ info.sender = keyPending;
+ info.server = t->firstParam( 102 );
+ info.key = t->firstParam( 61 );
+ info.status = InitialStatus;
+ info.dataLength = 0;
+ info.buffer = 0L;
+ info.headerRead = false;
+ if( info.sender == client()->userId() )
+ {
+ transmittingData = true;
+ info.direction = Outgoing;
+ }
+ else
+ info.direction = Incoming;
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Got WebcamInformation: Sender: " << info.sender << " Server: " << info.server << " Key: " << info.key << endl;
+
+ KStreamSocket *socket = new KStreamSocket( info.server, QString::number(5100) );
+ socketMap[socket] = info;
+ socket->enableRead( true );
+ connect( socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( slotConnectionStage1Established() ) );
+ connect( socket, SIGNAL( gotError(int) ), this, SLOT( slotConnectionFailed(int) ) );
+ connect( socket, SIGNAL( readyRead() ), this, SLOT( slotRead() ) );
+
+ socket->connect();
+}
+
+void WebcamTask::slotConnectionStage1Established()
+{
+ KStreamSocket* socket = const_cast<KStreamSocket*>( dynamic_cast<const KStreamSocket*>( sender() ) );
+ if( !socket )
+ return;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Webcam connection Stage1 to the user " << socketMap[socket].sender << " established." << endl;
+ disconnect( socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( slotConnectionStage1Established() ) );
+ disconnect( socket, SIGNAL( gotError(int) ), this, SLOT( slotConnectionFailed(int) ) );
+ socketMap[socket].status = ConnectedStage1;
+
+
+ QByteArray buffer;
+ QDataStream stream( buffer, IO_WriteOnly );
+ QString s;
+ if( socketMap[socket].direction == Incoming )
+ {
+ socket->writeBlock( QCString("<RVWCFG>").data(), 8 );
+ s = QString("g=%1\r\n").arg(socketMap[socket].sender);
+ }
+ else
+ {
+ socket->writeBlock( QCString("<RUPCFG>").data(), 8 );
+ s = QString("f=1\r\n");
+ }
+
+ // Header: 08 00 01 00 00 00 00
+ stream << (Q_INT8)0x08 << (Q_INT8)0x00 << (Q_INT8)0x01 << (Q_INT8)0x00 << (Q_INT32)s.length();
+ stream.writeRawBytes( s.local8Bit(), s.length() );
+
+ socket->writeBlock( buffer.data(), buffer.size() );
+}
+
+void WebcamTask::slotConnectionStage2Established()
+{
+ KStreamSocket* socket = const_cast<KStreamSocket*>( dynamic_cast<const KStreamSocket*>( sender() ) );
+ if( !socket )
+ return;
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Webcam connection Stage2 to the user " << socketMap[socket].sender << " established." << endl;
+ disconnect( socket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( slotConnectionStage2Established() ) );
+ disconnect( socket, SIGNAL( gotError(int) ), this, SLOT( slotConnectionFailed(int) ) );
+ socketMap[socket].status = ConnectedStage2;
+
+ QByteArray buffer;
+ QDataStream stream( buffer, IO_WriteOnly );
+ QString s;
+
+
+ if( socketMap[socket].direction == Incoming )
+ {
+ // Send <REQIMG>-Packet
+ socket->writeBlock( QCString("<REQIMG>").data(), 8 );
+ // Send request information
+ s = QString("a=2\r\nc=us\r\ne=21\r\nu=%1\r\nt=%2\r\ni=\r\ng=%3\r\no=w-2-5-1\r\np=1")
+ .arg(client()->userId()).arg(socketMap[socket].key).arg(socketMap[socket].sender);
+ // Header: 08 00 01 00 00 00 00
+ stream << (Q_INT8)0x08 << (Q_INT8)0x00 << (Q_INT8)0x01 << (Q_INT8)0x00 << (Q_INT32)s.length();
+ }
+ else
+ {
+ // Send <REQIMG>-Packet
+ socket->writeBlock( QCString("<SNDIMG>").data(), 8 );
+ // Send request information
+ s = QString("a=2\r\nc=us\r\nu=%1\r\nt=%2\r\ni=%3\r\no=w-2-5-1\r\np=2\r\nb=KopeteWebcam\r\nd=\r\n")
+ .arg(client()->userId()).arg(socketMap[socket].key).arg(socket->localAddress().nodeName());
+ // Header: 08 00 05 00 00 00 00 01 00 00 00 01
+ stream << (Q_INT8)0x0d << (Q_INT8)0x00 << (Q_INT8)0x05 << (Q_INT8)0x00 << (Q_INT32)s.length()
+ << (Q_INT8)0x01 << (Q_INT8)0x00 << (Q_INT8)0x00 << (Q_INT8)0x00 << (Q_INT8)0x01;
+ }
+ socket->writeBlock( buffer.data(), buffer.size() );
+ socket->writeBlock( s.local8Bit(), s.length() );
+}
+
+void WebcamTask::slotConnectionFailed( int error )
+{
+ KStreamSocket* socket = const_cast<KStreamSocket*>( dynamic_cast<const KStreamSocket*>( sender() ) );
+ client()->notifyError( i18n("Webcam connection to the user %1 could not be established.\n\nPlease relogin and try again.")
+ .arg(socketMap[socket].sender), QString("%1 - %2").arg(error).arg( socket->errorString()), Client::Error );
+ socketMap.remove( socket );
+ socket->deleteLater();
+}
+
+void WebcamTask::slotRead()
+{
+ KStreamSocket* socket = const_cast<KStreamSocket*>( dynamic_cast<const KStreamSocket*>( sender() ) );
+ if( !socket )
+ return;
+
+ switch( socketMap[socket].status )
+ {
+ case ConnectedStage1:
+ disconnect( socket, SIGNAL( readyRead() ), this, SLOT( slotRead() ) );
+ connectStage2( socket );
+ break;
+ case ConnectedStage2:
+ case Sending:
+ case SendingEmpty:
+ processData( socket );
+ default:
+ break;
+ }
+}
+
+void WebcamTask::connectStage2( KStreamSocket *socket )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ QByteArray data( socket->bytesAvailable() );
+ socket->readBlock ( data.data (), data.size () );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Magic Byte:" << data[2] << endl;
+
+ socketMap[socket].status = ConnectedStage2;
+
+ QString server;
+ int i = 4;
+ KStreamSocket *newSocket;
+ switch( (const char)data[2] )
+ {
+ case (Q_INT8)0x06:
+ emit webcamNotAvailable(socketMap[socket].sender);
+ break;
+ case (Q_INT8)0x04:
+ case (Q_INT8)0x07:
+ while( (const char)data[i] != (Q_INT8)0x00 )
+ server += data[i++];
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Server:" << server << endl;
+ if( server.isEmpty() )
+ {
+ emit webcamNotAvailable(socketMap[socket].sender);
+ break;
+ }
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Connecting to " << server << endl;
+ newSocket = new KStreamSocket( server, QString::number(5100) );
+ socketMap[newSocket] = socketMap[socket];
+ newSocket->enableRead( true );
+ connect( newSocket, SIGNAL( connected( const KResolverEntry& ) ), this, SLOT( slotConnectionStage2Established() ) );
+ connect( newSocket, SIGNAL( gotError(int) ), this, SLOT( slotConnectionFailed(int) ) );
+ connect( newSocket, SIGNAL( readyRead() ), this, SLOT( slotRead() ) );
+ if( socketMap[newSocket].direction == Outgoing )
+ {
+ newSocket->enableWrite( true );
+ connect( newSocket, SIGNAL( readyWrite() ), this, SLOT( transmitWebcamImage() ) );
+ }
+
+ newSocket->connect();
+ break;
+ default:
+ break;
+ }
+ socketMap.remove( socket );
+ delete socket;
+}
+
+void WebcamTask::processData( KStreamSocket *socket )
+{
+ QByteArray data( socket->bytesAvailable() );
+
+ socket->readBlock ( data.data (), data.size () );
+ if( data.size() <= 0 )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "No data read." << endl;
+ return;
+ }
+
+ parseData( data, socket );
+}
+
+void WebcamTask::parseData( QByteArray &data, KStreamSocket *socket )
+{
+ uint headerLength = 0;
+ uint read = 0;
+ YahooWebcamInformation *info = &socketMap[socket];
+ if( !info->headerRead )
+ {
+ headerLength = data[0];
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "headerLength " << headerLength << endl;
+ if( data.size() < headerLength )
+ return;
+ if( headerLength >= 8 )
+ {
+ kdDebug() << data[0] << data[1] << data[2] << data[3] << data[4] << data[5] << data[6] << data[7] << endl;
+ info->reason = data[1];
+ info->dataLength = yahoo_get32(data.data() + 4);
+ }
+ if( headerLength == 13 )
+ {
+ kdDebug() << data[8] << data[9] << data[10] << data[11] << data[12] << endl;
+ info->timestamp = yahoo_get32(data.data() + 9);
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "PacketType: " << data[8] << " reason: " << info->reason << " timestamp: " << info->timestamp << endl;
+ QStringList::iterator it;
+ switch( data[8] )
+ {
+ case 0x00:
+ if( info->direction == Incoming )
+ {
+ if( info->timestamp == 0 )
+ {
+ emit webcamClosed( info->sender, 3 );
+ cleanUpConnection( socket );
+ }
+ }
+ else
+ {
+ info->type = UserRequest;
+ info->headerRead = true;
+ }
+ break;
+ case 0x02:
+ info->type = Image;
+ info->headerRead = true;
+ break;
+ case 0x04:
+ if( info->timestamp == 1 )
+ {
+ emit webcamPaused( info->sender );
+ }
+ break;
+ case 0x05:
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Ready for Transmission. " << info->timestamp << " watchers." << endl;
+ if( info->timestamp == 1 )
+ {
+ info->status = Sending;
+ emit readyForTransmission();
+ }
+ else if( info->timestamp == 0 )
+ {
+ info->status = SendingEmpty;
+ emit stopTransmission();
+ sendEmptyWebcamImage();
+ }
+
+ // Send Invitation packets
+ for(it = pendingInvitations.begin(); it != pendingInvitations.end(); it++)
+ {
+ SendNotifyTask *snt = new SendNotifyTask( parent() );
+ snt->setTarget( *it );
+ snt->setType( SendNotifyTask::NotifyWebcamInvite );
+ snt->go( true );
+ it = pendingInvitations.remove( it );
+ it--;
+ }
+ break;
+ case 0x07:
+
+ info->type = ConnectionClosed;
+ emit webcamClosed( info->sender, info->reason );
+ cleanUpConnection( socket );
+ case 0x0c:
+ info->type = NewWatcher;
+ info->headerRead = true;
+ break;
+ case 0x0d:
+ info->type = WatcherLeft;
+ info->headerRead = true;
+ break;
+ }
+ }
+ if( headerLength > 13 || headerLength <= 0) //Parse error
+ return;
+ if( !info->headerRead && data.size() > headerLength )
+ {
+ // More headers to read
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "More data to read..." << endl;
+ QByteArray newData( data.size() - headerLength );
+ QDataStream stream( newData, IO_WriteOnly );
+ stream.writeRawBytes( data.data() + headerLength, data.size() - headerLength );
+ parseData( newData, socket );
+ return;
+ }
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Parsed Packet: HeaderLen: " << headerLength << " DataLen: " << info->dataLength << endl;
+ }
+
+ if( info->dataLength <= 0 )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "No data to read. (info->dataLength <= 0)" << endl;
+ if( info->headerRead )
+ info->headerRead = false;
+ return;
+ }
+ if( headerLength >= data.size() )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "No data to read. (headerLength >= data.size())" << endl;
+ return; //Nothing to read here...
+ }
+ if( !info->buffer )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Buffer created" << endl;
+ info->buffer = new QBuffer();
+ info->buffer->open( IO_WriteOnly );
+ }
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "data.size() " << data.size() << " headerLength " << headerLength << " buffersize " << info->buffer->size() << endl;
+ read = headerLength + info->dataLength - info->buffer->size();
+ info->buffer->writeBlock( data.data() + headerLength, data.size() - headerLength );//info->dataLength - info->buffer->size() );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "read " << data.size() - headerLength << " Bytes, Buffer is now " << info->buffer->size() << endl;
+ if( info->buffer->size() >= static_cast<uint>(info->dataLength) )
+ {
+ info->buffer->close();
+ QString who;
+ switch( info->type )
+ {
+ case UserRequest:
+ {
+ who.append( info->buffer->buffer() );
+ who = who.mid( 2, who.find('\n') - 3);
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "User wants to view webcam: " << who << " len: " << who.length() << " Index: " << accessGranted.findIndex( who ) << endl;
+ if( accessGranted.findIndex( who ) >= 0 )
+ {
+ grantAccess( who );
+ }
+ else
+ emit viewerRequest( who );
+ }
+ break;
+ case NewWatcher:
+ who.append( info->buffer->buffer() );
+ who = who.left( who.length() - 1 );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "New Watcher of webcam: " << who << endl;
+ emit viewerJoined( who );
+ break;
+ case WatcherLeft:
+ who.append( info->buffer->buffer() );
+ who = who.left( who.length() - 1 );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "A Watcher left: " << who << " len: " << who.length() << endl;
+ accessGranted.remove( who );
+ emit viewerLeft( who );
+ break;
+ case Image:
+ {
+ QPixmap webcamImage;
+ //webcamImage.loadFromData( info->buffer->buffer() );
+
+ KTempFile jpcTmpImageFile;
+ KTempFile bmpTmpImageFile;
+ QFile *file = jpcTmpImageFile.file();
+ file->writeBlock((info->buffer->buffer()).data(), info->buffer->size());
+ file->close();
+
+ KProcess p;
+ p << "jasper";
+ p << "--input" << jpcTmpImageFile.name() << "--output" << bmpTmpImageFile.name() << "--output-format" << "bmp";
+
+ p.start( KProcess::Block );
+ if( p.exitStatus() != 0 )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << " jasper exited with status " << p.exitStatus() << " " << info->sender << endl;
+ }
+ else
+ {
+ webcamImage.load( bmpTmpImageFile.name() );
+ /******* UPTO THIS POINT ******/
+ emit webcamImageReceived( info->sender, webcamImage );
+ }
+ QFile::remove(jpcTmpImageFile.name());
+ QFile::remove(bmpTmpImageFile.name());
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Image Received. Size: " << webcamImage.size() << endl;
+ }
+ break;
+ default:
+ break;
+ }
+
+ info->headerRead = false;
+ delete info->buffer;
+ info->buffer = 0L;
+ }
+ if( data.size() > read )
+ {
+ // More headers to read
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "More data to read..." << data.size() - read << endl;
+ QByteArray newData( data.size() - read );
+ QDataStream stream( newData, IO_WriteOnly );
+ stream.writeRawBytes( data.data() + read, data.size() - read );
+ parseData( newData, socket );
+ }
+}
+
+void WebcamTask::cleanUpConnection( KStreamSocket *socket )
+{
+ socket->close();
+ YahooWebcamInformation *info = &socketMap[socket];
+ if( info->buffer )
+ delete info->buffer;
+ socketMap.remove( socket );
+ delete socket;
+}
+
+void WebcamTask::closeWebcam( const QString & who )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << it.data().sender << " - " << who << endl;
+ if( it.data().sender == who )
+ {
+ cleanUpConnection( it.key() );
+ return;
+ }
+ }
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. You tried to close a connection that didn't exist." << endl;
+ client()->notifyError( i18n( "An error occured closing the webcam session. " ), i18n( "You tried to close a connection that didn't exist." ), Client::Debug );
+}
+
+
+// Sending
+
+void WebcamTask::registerWebcam()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceWebcam);
+ t->setId( client()->sessionID() );
+ t->setParam( 1, client()->userId().local8Bit());
+ keyPending = client()->userId();
+
+ send( t );
+}
+
+void WebcamTask::addPendingInvitation( const QString &userId )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Inviting " << userId << " to watch the webcam." << endl;
+ pendingInvitations.append( userId );
+ accessGranted.append( userId );
+}
+
+void WebcamTask::grantAccess( const QString &userId )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ KStreamSocket *socket = 0L;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ if( it.data().direction == Outgoing )
+ {
+ socket = it.key();
+ break;
+ }
+ }
+ if( !socket )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. No outgoing socket found." << endl;
+ return;
+ }
+ QByteArray ar;
+ QDataStream stream( ar, IO_WriteOnly );
+ QString user = QString("u=%1").arg(userId);
+
+ stream << (Q_INT8)0x0d << (Q_INT8)0x00 << (Q_INT8)0x05 << (Q_INT8)0x00 << (Q_INT32)user.length()
+ << (Q_INT8)0x00 << (Q_INT8)0x00 << (Q_INT8)0x00 << (Q_INT8)0x00 << (Q_INT8)0x01;
+ socket->writeBlock( ar.data(), ar.size() );
+ socket->writeBlock( user.local8Bit(), user.length() );
+}
+
+void WebcamTask::closeOutgoingWebcam()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ KStreamSocket *socket = 0L;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ if( it.data().direction == Outgoing )
+ {
+ socket = it.key();
+ break;
+ }
+ }
+ if( !socket )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. No outgoing socket found." << endl;
+ return;
+ }
+
+ cleanUpConnection( socket );
+ transmittingData = false;
+}
+
+void WebcamTask::sendEmptyWebcamImage()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ KStreamSocket *socket = 0L;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ if( it.data().direction == Outgoing )
+ {
+ socket = it.key();
+ break;
+ }
+ }
+ if( !socket )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. No outgoing socket found." << endl;
+ return;
+ }
+ if( socketMap[socket].status != SendingEmpty )
+ return;
+
+ pictureBuffer.resize( 0 );
+ transmissionPending = true;
+
+ QTimer::singleShot( 1000, this, SLOT(sendEmptyWebcamImage()) );
+
+}
+
+void WebcamTask::sendWebcamImage( const QByteArray &image )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ pictureBuffer.duplicate( image );
+ transmissionPending = true;
+ KStreamSocket *socket = 0L;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ if( it.data().direction == Outgoing )
+ {
+ socket = it.key();
+ break;
+ }
+ }
+ if( !socket )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. No outgoing socket found." << endl;
+ return;
+ }
+
+ socket->enableWrite( true );
+}
+
+void WebcamTask::transmitWebcamImage()
+{
+ if( !transmissionPending )
+ return;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "arraysize: " << pictureBuffer.size() << endl;
+
+ // Find outgoing socket
+ KStreamSocket *socket = 0L;
+ SocketInfoMap::Iterator it;
+ for( it = socketMap.begin(); it != socketMap.end(); it++ )
+ {
+ if( it.data().direction == Outgoing )
+ {
+ socket = it.key();
+ break;
+ }
+ }
+ if( !socket )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error. No outgoing socket found." << endl;
+ return;
+ }
+
+ socket->enableWrite( false );
+ QByteArray buffer;
+ QDataStream stream( buffer, IO_WriteOnly );
+ stream << (Q_INT8)0x0d << (Q_INT8)0x00 << (Q_INT8)0x05 << (Q_INT8)0x00 << (Q_INT32)pictureBuffer.size()
+ << (Q_INT8)0x02 << (Q_INT32)timestamp++;
+ socket->writeBlock( buffer.data(), buffer.size() );
+ if( pictureBuffer.size() )
+ socket->writeBlock( pictureBuffer.data(), pictureBuffer.size() );
+
+ transmissionPending = false;
+}
+#include "webcamtask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/webcamtask.h b/kopete/protocols/yahoo/libkyahoo/webcamtask.h
new file mode 100644
index 00000000..71dd2a95
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/webcamtask.h
@@ -0,0 +1,112 @@
+/*
+ Kopete Yahoo Protocol
+ Handles incoming webcam connections
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 WEBCAMTASK_H
+#define WEBCAMTASK_H
+
+#include "task.h"
+#include <qmap.h>
+#include <qpixmap.h>
+#include <qstringlist.h>
+
+class QString;
+class YMSGTransfer;
+class QBuffer;
+namespace KNetwork {
+ class KStreamSocket;
+}
+using namespace KNetwork;
+
+enum ConnectionStatus{ InitialStatus, ConnectedStage1, ConnectedStage2, Receiving, Sending, SendingEmpty };
+enum PacketType { Image, ConnectionClosed, UserRequest, NewWatcher, WatcherLeft };
+enum Direction { Incoming, Outgoing };
+
+struct YahooWebcamInformation
+{
+ QString sender;
+ QString server;
+ QString key;
+ ConnectionStatus status;
+ PacketType type;
+ Direction direction;
+ uchar reason;
+ Q_INT32 dataLength;
+ Q_INT32 timestamp;
+ bool headerRead;
+ QBuffer *buffer;
+};
+
+typedef QMap< KStreamSocket *, YahooWebcamInformation > SocketInfoMap;
+
+/**
+@author André Duffeck
+*/
+class WebcamTask : public Task
+{
+ Q_OBJECT
+public:
+ WebcamTask(Task *parent);
+ ~WebcamTask();
+
+ bool take(Transfer *transfer);
+ bool forMe( Transfer* transfer ) const;
+
+ bool transmitting() { return transmittingData; }
+
+ void requestWebcam( const QString &who );
+ void closeWebcam( const QString &who );
+
+ void registerWebcam();
+ void sendWebcamImage( const QByteArray &image );
+ void addPendingInvitation( const QString &userId );
+ void grantAccess( const QString &userId );
+ void closeOutgoingWebcam();
+signals:
+ void webcamNotAvailable( const QString & );
+ void webcamClosed( const QString &, int );
+ void webcamPaused( const QString& );
+ void webcamImageReceived( const QString &, const QPixmap &);
+ void readyForTransmission();
+ void stopTransmission();
+ void viewerJoined( const QString & );
+ void viewerLeft( const QString & );
+ void viewerRequest( const QString & );
+private slots:
+ void slotConnectionStage1Established();
+ void slotConnectionStage2Established();
+ void slotConnectionFailed(int);
+ void slotRead();
+ void sendEmptyWebcamImage();
+ void transmitWebcamImage();
+private:
+ void parseWebcamInformation( YMSGTransfer *transfer );
+ void parseData( QByteArray &data, KStreamSocket *socket );
+
+ void connectStage2( KStreamSocket *socket );
+ void processData( KStreamSocket *socket );
+ void cleanUpConnection( KStreamSocket *socket );
+
+ QString keyPending; // the buddy we have requested the webcam from
+ SocketInfoMap socketMap;
+ bool transmittingData;
+ QStringList pendingInvitations;
+ QStringList accessGranted;
+ int timestamp;
+ QByteArray pictureBuffer;
+ bool transmissionPending;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/yabentry.cpp b/kopete/protocols/yahoo/libkyahoo/yabentry.cpp
new file mode 100644
index 00000000..9eab5ef1
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yabentry.cpp
@@ -0,0 +1,201 @@
+/*
+ yabcpp - Encapsulate Yahoo Adressbook information
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "yabentry.h"
+
+void YABEntry::fromQDomElement( const QDomElement &e )
+{
+ yahooId = e.attribute("yi");
+ YABId = e.attribute("id", "-1").toInt();
+ firstName = e.attribute("fn");
+ secondName = e.attribute("mn");
+ lastName = e.attribute("ln");
+ nickName = e.attribute("nn");
+ email = e.attribute("e0");
+ privatePhone = e.attribute("hp");
+ workPhone = e.attribute("wp");
+ pager = e.attribute("pa");
+ fax = e.attribute("fa");
+ phoneMobile = e.attribute("mo");
+ additionalNumber = e.attribute("ot");
+ altEmail1 = e.attribute("e1");
+ altEmail2 = e.attribute("e2");
+ privateURL = e.attribute("pu");
+ title = e.attribute("ti");
+ corporation = e.attribute("co");
+ workAdress = e.attribute("wa").replace( "&#xd;&#xa;", "\n" );
+ workCity = e.attribute("wc");
+ workState = e.attribute("ws");
+ workZIP = e.attribute("wz");
+ workCountry = e.attribute("wn");
+ workURL = e.attribute("wu");
+ privateAdress = e.attribute("ha").replace( "&#xd;&#xa;", "\n" );
+ privateCity = e.attribute("hc");
+ privateState = e.attribute("hs");
+ privateZIP = e.attribute("hz");
+ privateCountry = e.attribute("hn");
+ QString birtday = e.attribute("bi");
+ birthday = QDate( birtday.section("/",2,2).toInt(), birtday.section("/",1,1).toInt(), birtday.section("/",0,0).toInt() );
+ QString an = e.attribute("an");
+ anniversary = QDate( an.section("/",2,2).toInt(), an.section("/",1,1).toInt(), an.section("/",0,0).toInt() );
+ additional1 = e.attribute("c1");
+ additional2 = e.attribute("c2");
+ additional3 = e.attribute("c3");
+ additional4 = e.attribute("c4");
+ notes = e.attribute("cm").replace( "&#xd;&#xa;", "\n" );
+ imAIM = e.attribute("ima");
+ imGoogleTalk = e.attribute("img");
+ imICQ = e.attribute("imq");
+ imIRC = e.attribute("imc");
+ imMSN = e.attribute("imm");
+ imQQ = e.attribute("imqq");
+ imSkype = e.attribute("imk");
+}
+
+void YABEntry::fromQDomDocument( const QDomDocument &d )
+{
+ kdDebug() << d.toString() <<
+ d.elementsByTagName("yi").item(0).toElement().text();
+ yahooId = d.elementsByTagName("yi").item(0).toElement().text();
+ firstName = d.elementsByTagName("fn").item(0).toElement().text();
+ secondName = d.elementsByTagName("mn").item(0).toElement().text();
+ lastName = d.elementsByTagName("ln").item(0).toElement().text();
+ nickName = d.elementsByTagName("nn").item(0).toElement().text();
+ email = d.elementsByTagName("e0").item(0).toElement().text();
+ privatePhone = d.elementsByTagName("hp").item(0).toElement().text();
+ workPhone = d.elementsByTagName("wp").item(0).toElement().text();
+ pager = d.elementsByTagName("pa").item(0).toElement().text();
+ fax = d.elementsByTagName("fa").item(0).toElement().text();
+ phoneMobile = d.elementsByTagName("mo").item(0).toElement().text();
+ additionalNumber = d.elementsByTagName("ot").item(0).toElement().text();
+ altEmail1 = d.elementsByTagName("e1").item(0).toElement().text();
+ altEmail2 = d.elementsByTagName("e2").item(0).toElement().text();
+ privateURL = d.elementsByTagName("pu").item(0).toElement().text();
+ title = d.elementsByTagName("ti").item(0).toElement().text();
+ corporation = d.elementsByTagName("co").item(0).toElement().text();
+ workAdress = d.elementsByTagName("wa").item(0).toElement().text().replace( "&#xd;&#xa;", "\n" );
+ workCity = d.elementsByTagName("wc").item(0).toElement().text();
+ workState = d.elementsByTagName("ws").item(0).toElement().text();
+ workZIP = d.elementsByTagName("wz").item(0).toElement().text();
+ workCountry = d.elementsByTagName("wn").item(0).toElement().text();
+ workURL = d.elementsByTagName("wu").item(0).toElement().text();
+ privateAdress = d.elementsByTagName("ha").item(0).toElement().text().replace( "&#xd;&#xa;", "\n" );
+ privateCity = d.elementsByTagName("hc").item(0).toElement().text();
+ privateState = d.elementsByTagName("hs").item(0).toElement().text();
+ privateZIP = d.elementsByTagName("hz").item(0).toElement().text();
+ privateCountry = d.elementsByTagName("hn").item(0).toElement().text();
+ QString birtday = d.elementsByTagName("bi").item(0).toElement().text();
+ birthday = QDate( birtday.section("/",2,2).toInt(), birtday.section("/",1,1).toInt(), birtday.section("/",0,0).toInt() );
+ QString an = d.elementsByTagName("an").item(0).toElement().text();
+ anniversary = QDate( an.section("/",2,2).toInt(), an.section("/",1,1).toInt(), an.section("/",0,0).toInt() );
+ additional1 = d.elementsByTagName("c1").item(0).toElement().text();
+ additional2 = d.elementsByTagName("c2").item(0).toElement().text();
+ additional3 = d.elementsByTagName("c3").item(0).toElement().text();
+ additional4 = d.elementsByTagName("c4").item(0).toElement().text();
+ notes = d.elementsByTagName("cm").item(0).toElement().text().replace( "&#xd;&#xa;", "\n" );
+ imAIM = d.elementsByTagName("ima").item(0).toElement().text();
+ imGoogleTalk = d.elementsByTagName("img").item(0).toElement().text();
+ imICQ = d.elementsByTagName("imq").item(0).toElement().text();
+ imIRC = d.elementsByTagName("imc").item(0).toElement().text();
+ imMSN = d.elementsByTagName("imm").item(0).toElement().text();
+ imQQ = d.elementsByTagName("imqq").item(0).toElement().text();
+ imSkype = d.elementsByTagName("imk").item(0).toElement().text();
+}
+
+void YABEntry::fillQDomElement( QDomElement &e ) const
+{
+ e.setAttribute( "yi", yahooId );
+ e.setAttribute( "id", YABId );
+ e.setAttribute( "fn", firstName );
+ e.setAttribute( "mn", secondName );
+ e.setAttribute( "ln", lastName );
+ e.setAttribute( "nn", nickName );
+ e.setAttribute( "e0", email );
+ e.setAttribute( "hp", privatePhone );
+ e.setAttribute( "wp", workPhone );
+ e.setAttribute( "pa", pager );
+ e.setAttribute( "fa", fax );
+ e.setAttribute( "mo", phoneMobile );
+ e.setAttribute( "ot", additionalNumber );
+ e.setAttribute( "e1", altEmail1 );
+ e.setAttribute( "e2", altEmail2 );
+ e.setAttribute( "pu", privateURL );
+ e.setAttribute( "ti", title );
+ e.setAttribute( "co", corporation );
+ e.setAttribute( "wa", QString( workAdress ).replace( "\n", "&#xd;&#xa;" ) );
+ e.setAttribute( "wc", workCity );
+ e.setAttribute( "ws", workState );
+ e.setAttribute( "wz", workZIP );
+ e.setAttribute( "wn", workCountry );
+ e.setAttribute( "wu", workURL );
+ e.setAttribute( "ha", QString( privateAdress ).replace( "\n", "&#xd;&#xa;" ) );
+ e.setAttribute( "hc", privateCity );
+ e.setAttribute( "hs", privateState );
+ e.setAttribute( "hz", privateZIP );
+ e.setAttribute( "hn", privateCountry );
+ e.setAttribute( "bi", QString("%1/%2/%3").arg( birthday.day() ).arg( birthday.month() ).arg( birthday.year() ) );
+ e.setAttribute( "an", QString("%1/%2/%3").arg( anniversary.day() ).arg( anniversary.month() ).arg( anniversary.year() ) );
+ e.setAttribute( "c1", additional1 );
+ e.setAttribute( "c2", additional2 );
+ e.setAttribute( "c3", additional3 );
+ e.setAttribute( "c4", additional4 );
+ e.setAttribute( "cm", QString( notes ).replace( "\n", "&#xd;&#xa;" ) );
+ e.setAttribute( "ima", imAIM );
+ e.setAttribute( "img", imGoogleTalk );
+ e.setAttribute( "imq", imICQ );
+ e.setAttribute( "imc", imIRC );
+ e.setAttribute( "imm", imMSN );
+ e.setAttribute( "imqq", imQQ );
+ e.setAttribute( "imk", imSkype );
+}
+
+void YABEntry::dump() const
+{
+ kdDebug() << "firstName: " << firstName << endl <<
+ "secondName: " << secondName << endl <<
+ "lastName: " << lastName << endl <<
+ "nickName: " << nickName << endl <<
+ "title: " << title << endl <<
+ "phoneMobile: " << phoneMobile << endl <<
+ "email: " << email << endl <<
+ "yahooId: " << yahooId << endl <<
+ "pager: " << pager << endl <<
+ "fax: " << fax << endl <<
+ "additionalNumber: " << additionalNumber << endl <<
+ "altEmail1: " << altEmail1 << endl <<
+ "altEmail2: " << altEmail2 << endl <<
+ "privateAdress: " << privateAdress << endl <<
+ "privateCity: " << privateCity << endl <<
+ "privateState: " << privateState << endl <<
+ "privateZIP: " << privateZIP << endl <<
+ "privateCountry: " << privateCountry << endl <<
+ "privatePhone: " << privatePhone << endl <<
+ "privateURL: " << privateURL << endl <<
+ "corporation: " << corporation << endl <<
+ "workAdress: " << workAdress << endl <<
+ "workCity: " << workCity << endl <<
+ "workState: " << workState << endl <<
+ "workZIP: " << workZIP << endl <<
+ "workCountry: " << workCountry << endl <<
+ "workURL: " << workURL << endl <<
+ "birthday: " << birthday.toString() << endl <<
+ "anniversary: " << anniversary.toString() << endl <<
+ "notes: " << notes << endl <<
+ "additional1: " << additional1 << endl <<
+ "additional2: " << additional2 << endl <<
+ "additional3: " << additional3 << endl <<
+ "additional4: " << additional4 << endl;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/yabentry.h b/kopete/protocols/yahoo/libkyahoo/yabentry.h
new file mode 100644
index 00000000..b12845ce
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yabentry.h
@@ -0,0 +1,91 @@
+/*
+ yabentry.h - Encapsulate Yahoo Adressbook information
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#ifndef YABEntry_H
+#define YABEntry_H
+
+#include <kdebug.h>
+#include <qdatetime.h>
+#include <qdom.h>
+
+struct YABEntry
+{
+ enum Source { SourceYAB, SourceContact };
+
+ // Personal
+ QString firstName;
+ QString secondName;
+ QString lastName;
+ QString nickName;
+ QString title;
+
+ // Primary Information
+ QString phoneMobile;
+ QString email;
+ QString yahooId;
+ int YABId;
+ Source source;
+
+ // Additional Information
+ QString pager;
+ QString fax;
+ QString additionalNumber;
+ QString altEmail1;
+ QString altEmail2;
+ QString imAIM;
+ QString imICQ;
+ QString imMSN;
+ QString imGoogleTalk;
+ QString imSkype;
+ QString imIRC;
+ QString imQQ;
+
+ // Private Information
+ QString privateAdress;
+ QString privateCity;
+ QString privateState;
+ QString privateZIP;
+ QString privateCountry;
+ QString privatePhone;
+ QString privateURL;
+
+ // Work Information
+ QString corporation;
+ QString workAdress;
+ QString workCity;
+ QString workState;
+ QString workZIP;
+ QString workCountry;
+ QString workPhone;
+ QString workURL;
+
+ // Miscellanous
+ QDate birthday;
+ QDate anniversary;
+ QString notes;
+ QString additional1;
+ QString additional2;
+ QString additional3;
+ QString additional4;
+
+
+ void fromQDomElement( const QDomElement &e );
+ void fromQDomDocument( const QDomDocument &e );
+ void fillQDomElement( QDomElement &e ) const;
+
+ void dump() const;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/yabtask.cpp b/kopete/protocols/yahoo/libkyahoo/yabtask.cpp
new file mode 100644
index 00000000..38aea9ca
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yabtask.cpp
@@ -0,0 +1,160 @@
+/*
+ Kopete Yahoo Protocol
+ yabtask.h - Handles the Yahoo Address Book
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "yabtask.h"
+#include "transfer.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "client.h"
+#include <qstring.h>
+#include <qdatastream.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <klocale.h>
+
+YABTask::YABTask(Task* parent) : Task(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+}
+
+YABTask::~YABTask()
+{
+}
+
+bool YABTask::take( Transfer* transfer )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ if ( !forMe( transfer ) )
+ return false;
+
+ YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);
+
+ if( t->service() == Yahoo::ServiceContactDetails )
+ parseContactDetails( t );
+
+ return true;
+}
+
+bool YABTask::forMe( Transfer* transfer ) const
+{
+// kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ YMSGTransfer *t = 0L;
+ t = dynamic_cast<YMSGTransfer*>(transfer);
+ if (!t)
+ return false;
+
+ if ( t->service() == Yahoo::ServiceContactDetails )
+ return true;
+ else
+ return false;
+}
+
+void YABTask::parseContactDetails( YMSGTransfer* t )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ QString from; /* key = 7 */
+ int count;
+
+ from = t->firstParam( 4 );
+ count = t->paramCount( 5 );
+
+ for( int i = 0; i < count; i++ )
+ {
+ QString who = t->nthParam( 5, i );
+ QString s = t->nthParamSeparated( 280, i, 5 );
+ if( s.isEmpty() )
+ continue;
+
+ QDomDocument doc;
+ doc.setContent( s );
+ YABEntry *entry = new YABEntry;
+ entry->fromQDomDocument( doc );
+ entry->source = YABEntry::SourceContact;
+ entry->dump();
+ emit gotEntry( entry );
+ }
+}
+
+
+void YABTask::getAllEntries( long lastMerge, long lastRemoteRevision )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "LastMerge: " << lastMerge << " LastRemoteRevision: " << lastRemoteRevision << endl;
+ m_data = QString::null;
+ QString url = QString::fromLatin1("http://address.yahoo.com/yab/us?v=XM&prog=ymsgr&.intl=us&diffs=1&t=%1&tags=short&rt=%2&prog-ver=%3")
+ .arg( lastMerge ).arg( lastRemoteRevision ).arg( YMSG_PROGRAM_VERSION_STRING );
+
+ m_transferJob = KIO::get( url , false, false );
+ m_transferJob->addMetaData("cookies", "manual");
+ m_transferJob->addMetaData("setcookies", QString::fromLatin1("Cookie: Y=%1; T=%2; C=%3;")
+ .arg(client()->yCookie()).arg(client()->tCookie()).arg(client()->cCookie()) );
+ connect( m_transferJob, SIGNAL( data( KIO::Job *, const QByteArray & ) ), this, SLOT( slotData( KIO::Job*, const QByteArray & ) ) );
+ connect( m_transferJob, SIGNAL( result( KIO::Job *) ), this, SLOT( slotResult( KIO::Job* ) ) );
+}
+
+void YABTask::slotData( KIO::Job* /*job*/, const QByteArray &info )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ m_data += info;
+}
+
+void YABTask::slotResult( KIO::Job* job )
+{
+ if( job->error () || m_transferJob->isErrorPage () )
+ client()->notifyError( i18n( "Could not retrieve server side addressbook for user info." ), job->errorString(), Client::Info );
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Server side addressbook retrieved." << endl;
+ QDomDocument doc;
+ QDomNodeList list;
+ QDomElement e;
+ uint it = 0;
+
+ kdDebug(YAHOO_RAW_DEBUG) << m_data << endl;
+ doc.setContent( m_data );
+
+ list = doc.elementsByTagName( "ab" ); // Get the Addressbook
+ for( it = 0; it < list.count(); it++ ) {
+ if( !list.item( it ).isElement() )
+ continue;
+ e = list.item( it ).toElement();
+
+ if( !e.attribute( "lm" ).isEmpty() )
+ emit gotRevision( e.attribute( "lm" ).toLong(), true );
+
+ if( !e.attribute( "rt" ).isEmpty() )
+ emit gotRevision( e.attribute( "rt" ).toLong(), false );
+ }
+
+ list = doc.elementsByTagName( "ct" ); // Get records
+ for( it = 0; it < list.count(); it++ ) {
+ if( !list.item( it ).isElement() )
+ continue;
+ e = list.item( it ).toElement();
+
+ YABEntry *entry = new YABEntry;
+ entry->fromQDomElement( e );
+ entry->source = YABEntry::SourceYAB;
+ emit gotEntry( entry );
+ }
+ }
+}
+
+#include "yabtask.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/yabtask.h b/kopete/protocols/yahoo/libkyahoo/yabtask.h
new file mode 100644
index 00000000..bd22ead7
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yabtask.h
@@ -0,0 +1,60 @@
+/*
+ Kopete Yahoo Protocol
+ yabtask.h - Handles the Yahoo Address Book
+
+ Copyright (c) 2006 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YABTASK_H
+#define YABTASK_H
+
+#include "task.h"
+#include "yabentry.h"
+
+class YMSGTransfer;
+struct KURL;
+namespace KIO {
+ class Job;
+ class TransferJob;
+}
+class QDomElement;
+
+/**
+@author André Duffeck
+*/
+class YABTask : public Task
+{
+ Q_OBJECT
+public:
+ YABTask(Task *parent);
+ ~YABTask();
+
+ bool take(Transfer *transfer);
+ bool forMe( Transfer* transfer ) const;
+
+ void getAllEntries( long lastMerge, long lastRemoteRevision );
+ void saveEntry( const YABEntry & );
+signals:
+ void gotEntry( YABEntry * );
+ void gotRevision( long rev, bool merged );
+protected:
+ void parseContactDetails( YMSGTransfer* t );
+private slots:
+ void slotData( KIO::Job*, const QByteArray & );
+ void slotResult( KIO::Job* );
+private:
+ KIO::TransferJob *m_transferJob;
+ QString m_data;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoo_fn.c b/kopete/protocols/yahoo/libkyahoo/yahoo_fn.c
new file mode 100644
index 00000000..dec93561
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoo_fn.c
@@ -0,0 +1,4620 @@
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "yahoo_fn.h"
+
+unsigned char table_0[256] = {
+ 0x5A, 0x41, 0x11, 0x77, 0x29, 0x9C, 0x31, 0xAD,
+ 0x4A, 0x32, 0x1A, 0x6D, 0x56, 0x9F, 0x39, 0xA6,
+ 0x0C, 0xE8, 0x49, 0x40, 0xA4, 0x21, 0xE9, 0x01,
+ 0x91, 0x86, 0x2F, 0xB9, 0xED, 0x80, 0x51, 0xAB,
+ 0x7F, 0x92, 0xF2, 0x73, 0xCD, 0xD9, 0x75, 0x2A,
+ 0x70, 0x34, 0x35, 0x8D, 0xA8, 0x72, 0x7D, 0x9B,
+ 0x2E, 0xC5, 0x2D, 0x76, 0x1E, 0xBB, 0xE7, 0x37,
+ 0xBA, 0xB7, 0xB2, 0x03, 0x20, 0x17, 0x8A, 0x07,
+ 0xD6, 0x96, 0x13, 0x95, 0xE5, 0xF1, 0x18, 0x3B,
+ 0xA5, 0x62, 0x33, 0xC1, 0x44, 0x3D, 0x6C, 0xA7,
+ 0xBF, 0x1C, 0x60, 0xFF, 0x5B, 0xF5, 0x8E, 0xE6,
+ 0x5C, 0xCC, 0xF7, 0x69, 0x15, 0x0F, 0x0B, 0xBD,
+ 0x12, 0x9D, 0xB3, 0x65, 0x53, 0xB1, 0x14, 0xF4,
+ 0x19, 0x3E, 0xB6, 0x45, 0xCB, 0xA2, 0x7A, 0xD3,
+ 0xF8, 0xD1, 0x61, 0xEE, 0xBC, 0xC6, 0xB0, 0x5D,
+ 0x4B, 0x09, 0x26, 0xE1, 0x1D, 0x6E, 0xC3, 0xFB,
+ 0x68, 0x4C, 0x42, 0x52, 0x5F, 0xDE, 0xFD, 0xEF,
+ 0x81, 0x04, 0x6F, 0xE0, 0xF0, 0x1F, 0x0D, 0x7C,
+ 0x58, 0x4F, 0x1B, 0x30, 0xCF, 0x9A, 0x2B, 0x05,
+ 0xF6, 0x3F, 0x78, 0xAC, 0xD8, 0xEC, 0xE2, 0x25,
+ 0x93, 0xDA, 0x84, 0x8C, 0x4E, 0xD5, 0x38, 0x0A,
+ 0x06, 0x7E, 0xD4, 0x59, 0x98, 0xE3, 0x36, 0xC2,
+ 0xD2, 0xA3, 0x10, 0x79, 0xFA, 0xC9, 0x16, 0x27,
+ 0x66, 0x89, 0xFE, 0x57, 0xF3, 0x83, 0xB8, 0x28,
+ 0x3C, 0xC7, 0xCE, 0x71, 0xC8, 0xDB, 0x22, 0xE4,
+ 0xDD, 0xDF, 0x02, 0x8F, 0x5E, 0xEB, 0x48, 0x2C,
+ 0x08, 0xC4, 0x43, 0xEA, 0x50, 0x55, 0x90, 0x54,
+ 0x87, 0xCA, 0x00, 0x24, 0x6B, 0x85, 0x97, 0xD7,
+ 0xDC, 0x6A, 0x67, 0xD0, 0x88, 0xA1, 0x9E, 0xC0,
+ 0x46, 0xAE, 0x64, 0x74, 0x4D, 0xA0, 0x99, 0xB5,
+ 0x0E, 0x8B, 0xAA, 0x3A, 0xB4, 0xFC, 0xA9, 0x94,
+ 0x7B, 0xBE, 0xF9, 0xAF, 0x82, 0x63, 0x47, 0x23 };
+
+unsigned char table_1[256] = {
+ 0x08, 0xCB, 0x54, 0xCF, 0x97, 0x53, 0x59, 0xF1,
+ 0x66, 0xEC, 0xDB, 0x1B, 0xB1, 0xE2, 0x36, 0xEB,
+ 0xB3, 0x8F, 0x71, 0xA8, 0x90, 0x7D, 0xDA, 0xDC,
+ 0x2C, 0x2F, 0xE8, 0x6A, 0x73, 0x37, 0xAE, 0xCC,
+ 0xA1, 0x16, 0xE6, 0xFC, 0x9C, 0xA9, 0x2A, 0x3F,
+ 0x58, 0xFD, 0x56, 0x4C, 0xA5, 0xF2, 0x33, 0x99,
+ 0x1A, 0xB7, 0xFE, 0xA6, 0x1E, 0x32, 0x9E, 0x48,
+ 0x03, 0x4A, 0x78, 0xEE, 0xCA, 0xC3, 0x88, 0x7A,
+ 0xAC, 0x23, 0xAA, 0xBD, 0xDE, 0xD3, 0x67, 0x43,
+ 0xFF, 0x64, 0x8A, 0xF9, 0x04, 0xD0, 0x7B, 0xC2,
+ 0xBC, 0xF3, 0x89, 0x0E, 0xDD, 0xAB, 0x9D, 0x84,
+ 0x5A, 0x62, 0x7F, 0x6D, 0x82, 0x68, 0xA3, 0xED,
+ 0x2E, 0x07, 0x41, 0xEF, 0x2D, 0x70, 0x4F, 0x69,
+ 0x8E, 0xE7, 0x0F, 0x11, 0x19, 0xAF, 0x31, 0xFB,
+ 0x8D, 0x4B, 0x5F, 0x96, 0x75, 0x42, 0x6C, 0x46,
+ 0xE4, 0x55, 0xD6, 0x3B, 0xE1, 0xD1, 0xB0, 0xB5,
+ 0x45, 0x29, 0xC0, 0x94, 0x9F, 0xD4, 0x15, 0x17,
+ 0x3C, 0x47, 0xC8, 0xD9, 0xC6, 0x76, 0xB9, 0x02,
+ 0xE0, 0xC9, 0xB2, 0x01, 0xC1, 0x5D, 0x4E, 0x14,
+ 0xF4, 0xAD, 0xB6, 0x00, 0x72, 0xF0, 0x49, 0x0D,
+ 0xD8, 0x5E, 0x6F, 0x2B, 0x8C, 0x51, 0x83, 0xC5,
+ 0x0A, 0x85, 0xE5, 0x38, 0x7E, 0x26, 0xEA, 0x22,
+ 0x6B, 0x06, 0xD5, 0x8B, 0xBF, 0xC7, 0x35, 0x1D,
+ 0xF6, 0x24, 0x28, 0xCE, 0x9B, 0x77, 0x20, 0x60,
+ 0xF5, 0x87, 0x3D, 0x65, 0x86, 0x0C, 0xDF, 0xBA,
+ 0x12, 0xA4, 0x3A, 0x34, 0xD7, 0xA0, 0xF8, 0x63,
+ 0x52, 0x27, 0xB8, 0x18, 0xA7, 0x13, 0x91, 0x09,
+ 0x93, 0x5C, 0x10, 0x9A, 0xB4, 0xE9, 0x44, 0xC4,
+ 0x21, 0x57, 0x1C, 0x0B, 0xA2, 0x74, 0x4D, 0xBE,
+ 0xD2, 0x1F, 0xCD, 0xE3, 0x6E, 0x7C, 0x40, 0x50,
+ 0x39, 0x80, 0x98, 0xFA, 0x25, 0x92, 0x30, 0x5B,
+ 0x05, 0x95, 0xBB, 0x79, 0x61, 0x3E, 0x81, 0xF7 };
+
+unsigned char table_2[32] = {
+ 0x19, 0x05, 0x09, 0x1C, 0x0B, 0x1A, 0x12, 0x03,
+ 0x06, 0x04, 0x0D, 0x1D, 0x15, 0x0E, 0x1B, 0x18,
+ 0x00, 0x07, 0x08, 0x02, 0x13, 0x1F, 0x0C, 0x1E,
+ 0x16, 0x0A, 0x10, 0x0F, 0x01, 0x14, 0x11, 0x17 };
+
+unsigned char table_3[256] = {
+ 0xBC, 0x1B, 0xCC, 0x1E, 0x5B, 0x59, 0x4F, 0xA8,
+ 0x62, 0xC6, 0xC1, 0xBB, 0x83, 0x2D, 0xA3, 0xA6,
+ 0x5A, 0xDC, 0xE5, 0x93, 0xFB, 0x5C, 0xD6, 0x2A,
+ 0x97, 0xC7, 0x1C, 0x73, 0x08, 0x45, 0xD2, 0x89,
+ 0x4A, 0xD4, 0xCF, 0x0C, 0x1D, 0xD8, 0xCD, 0x26,
+ 0x8F, 0x11, 0x55, 0x8B, 0xD3, 0x53, 0xCE, 0x00,
+ 0xB5, 0x3B, 0x2E, 0x39, 0x88, 0x7B, 0x85, 0x46,
+ 0x54, 0xA5, 0x31, 0x40, 0x3E, 0x0A, 0x4C, 0x68,
+ 0x70, 0x0F, 0xBA, 0x0E, 0x75, 0x8A, 0xEB, 0x44,
+ 0x60, 0x6C, 0x05, 0xC9, 0xF0, 0xDD, 0x0D, 0x66,
+ 0xAB, 0xA1, 0xAD, 0xF2, 0x12, 0x6A, 0xE6, 0x27,
+ 0xF6, 0x9F, 0xDB, 0xB8, 0xF4, 0x56, 0x5E, 0x2C,
+ 0xDA, 0xFE, 0x34, 0x86, 0xF5, 0xC2, 0xB0, 0xF1,
+ 0xCB, 0xF3, 0x78, 0x9B, 0x7F, 0xB4, 0xD7, 0x58,
+ 0x74, 0x07, 0x72, 0x96, 0x02, 0xCA, 0xAC, 0xE8,
+ 0x5D, 0xA7, 0x32, 0xBD, 0x81, 0x43, 0x18, 0xF8,
+ 0x15, 0x0B, 0xE9, 0x76, 0x30, 0xBF, 0x3A, 0x22,
+ 0x9E, 0xD1, 0x79, 0x37, 0xBE, 0x8C, 0x7A, 0x98,
+ 0x21, 0x95, 0x10, 0x8D, 0xDF, 0xC0, 0x69, 0xC8,
+ 0x03, 0x6E, 0x4B, 0x36, 0xFC, 0x6F, 0xA9, 0x48,
+ 0x63, 0xE1, 0xB9, 0x24, 0x87, 0x13, 0xB2, 0xA4,
+ 0x84, 0x06, 0x14, 0x61, 0x3D, 0x92, 0xB1, 0x41,
+ 0xE2, 0x71, 0xAF, 0x16, 0xDE, 0x25, 0x82, 0xD9,
+ 0x2B, 0x33, 0x51, 0xA2, 0x4E, 0x7D, 0x94, 0xFF,
+ 0xFD, 0x5F, 0x80, 0xED, 0x64, 0xE7, 0x50, 0x6D,
+ 0xD0, 0x3C, 0x6B, 0x65, 0x77, 0x17, 0x1A, 0xEC,
+ 0xD5, 0xAA, 0xF9, 0xC4, 0x9C, 0x35, 0xE3, 0x42,
+ 0xE4, 0x19, 0x52, 0x67, 0xB7, 0x9D, 0x28, 0xC5,
+ 0x47, 0x38, 0x91, 0x57, 0xAE, 0x3F, 0x29, 0x9A,
+ 0x2F, 0xF7, 0x90, 0x04, 0xEE, 0xFA, 0x20, 0xB6,
+ 0xEA, 0x49, 0x23, 0x4D, 0xB3, 0x8E, 0xC3, 0x1F,
+ 0x7C, 0xEF, 0xE0, 0x99, 0x09, 0xA0, 0x01, 0x7E };
+
+unsigned char table_4[32] = {
+ 0x1F, 0x0B, 0x00, 0x1E, 0x03, 0x0E, 0x15, 0x01,
+ 0x1A, 0x17, 0x1D, 0x1B, 0x11, 0x0F, 0x0A, 0x12,
+ 0x13, 0x18, 0x02, 0x04, 0x09, 0x06, 0x0D, 0x07,
+ 0x08, 0x05, 0x10, 0x19, 0x0C, 0x14, 0x16, 0x1C };
+
+unsigned char table_5[256] = {
+ 0x9A, 0xAB, 0x61, 0x28, 0x0A, 0x23, 0xFC, 0xBA,
+ 0x90, 0x22, 0xB7, 0x62, 0xD9, 0x09, 0x91, 0xF4,
+ 0x7B, 0x5D, 0x6B, 0x80, 0xAC, 0x9E, 0x21, 0x72,
+ 0x64, 0x2D, 0xFF, 0x66, 0xEB, 0x5B, 0x05, 0xC8,
+ 0x1B, 0xD1, 0x55, 0xF5, 0x97, 0x08, 0xAE, 0xC7,
+ 0x00, 0xDE, 0xE1, 0x78, 0xD8, 0xB6, 0xF0, 0x17,
+ 0xE4, 0x32, 0xCD, 0x76, 0x07, 0x14, 0x7F, 0x7A,
+ 0xBF, 0xB4, 0x1D, 0x94, 0x48, 0x75, 0xFA, 0xA7,
+ 0x99, 0x7E, 0x65, 0x38, 0x29, 0x51, 0xC3, 0x83,
+ 0x7C, 0x0D, 0xA0, 0xCC, 0xF1, 0xDD, 0xE2, 0x49,
+ 0xF8, 0xD2, 0x25, 0x54, 0x9B, 0x0E, 0xB9, 0xFE,
+ 0x67, 0xC4, 0xCE, 0x13, 0xD4, 0xE7, 0xB8, 0x41,
+ 0x77, 0xDB, 0xA6, 0xB0, 0x11, 0x6A, 0x5E, 0x68,
+ 0x8D, 0xF9, 0x36, 0xD3, 0xC2, 0x3A, 0xAA, 0x59,
+ 0x03, 0xE0, 0xE3, 0xF3, 0x42, 0x2C, 0x04, 0x47,
+ 0xE6, 0x93, 0xCB, 0x6E, 0x20, 0xCA, 0x01, 0xA1,
+ 0x40, 0x2B, 0x2F, 0x5F, 0x87, 0xD0, 0xEC, 0x88,
+ 0x27, 0x58, 0xC6, 0x3E, 0xDF, 0x26, 0x5C, 0xE9,
+ 0x1F, 0x0F, 0x95, 0x1C, 0xFB, 0xA5, 0x12, 0x39,
+ 0x1E, 0x3C, 0x33, 0x43, 0x56, 0xE8, 0x82, 0xF7,
+ 0x7D, 0x89, 0xF2, 0xD7, 0x50, 0x92, 0x60, 0x4C,
+ 0x2A, 0x86, 0x16, 0x6C, 0x37, 0xC0, 0xAD, 0xB3,
+ 0x24, 0x45, 0xB1, 0xA2, 0x71, 0xA4, 0xA3, 0xED,
+ 0xC9, 0x5A, 0x4D, 0x84, 0x0C, 0x3F, 0xC5, 0x9D,
+ 0x63, 0x19, 0x79, 0x57, 0x96, 0x30, 0x74, 0xBB,
+ 0xDA, 0x1A, 0x9F, 0x44, 0xC1, 0x98, 0xE5, 0x81,
+ 0xD6, 0x18, 0x8F, 0xFD, 0x8E, 0x06, 0x6F, 0xF6,
+ 0x2E, 0x3B, 0xB5, 0x85, 0x8A, 0x9C, 0x53, 0x4A,
+ 0xA9, 0x52, 0x3D, 0x4E, 0xBE, 0xAF, 0xBC, 0xA8,
+ 0x4F, 0x6D, 0x15, 0x35, 0x8C, 0xBD, 0x34, 0x8B,
+ 0xDC, 0x0B, 0xCF, 0x31, 0xEA, 0xB2, 0x70, 0x4B,
+ 0x46, 0x73, 0x69, 0xD5, 0x10, 0xEE, 0x02, 0xEF };
+
+unsigned char table_6[32] = {
+ 0x1A, 0x1C, 0x0F, 0x0C, 0x00, 0x02, 0x13, 0x09,
+ 0x11, 0x05, 0x0D, 0x12, 0x18, 0x0B, 0x04, 0x10,
+ 0x14, 0x1B, 0x1E, 0x16, 0x07, 0x08, 0x03, 0x17,
+ 0x19, 0x1F, 0x01, 0x0E, 0x15, 0x06, 0x0A, 0x1D };
+
+unsigned char table_7[256] = {
+ 0x52, 0x11, 0x72, 0xD0, 0x76, 0xD7, 0xAE, 0x03,
+ 0x7F, 0x19, 0xF4, 0xB8, 0xB3, 0x5D, 0xCA, 0x2D,
+ 0x5C, 0x30, 0x53, 0x1A, 0x57, 0xF6, 0xAD, 0x83,
+ 0x29, 0x79, 0xD5, 0xF0, 0x0F, 0xC3, 0x8B, 0xD3,
+ 0x8E, 0x37, 0x01, 0xA6, 0xF1, 0x10, 0x04, 0x71,
+ 0xCC, 0xC6, 0xE7, 0xC2, 0x85, 0x94, 0xBD, 0x6F,
+ 0xCB, 0xEA, 0xFC, 0xA1, 0x38, 0x5E, 0x08, 0x2E,
+ 0x35, 0x42, 0x67, 0xD4, 0x56, 0x6D, 0x7C, 0xE5,
+ 0x0E, 0x7D, 0x12, 0x65, 0xF5, 0x33, 0x82, 0xC4,
+ 0x1D, 0xD2, 0x16, 0x58, 0xEC, 0xCD, 0xA8, 0xBF,
+ 0xAB, 0x07, 0x45, 0x55, 0xB7, 0x6A, 0x70, 0xF2,
+ 0xBE, 0x05, 0x6B, 0x9D, 0xEB, 0x13, 0x0D, 0x9F,
+ 0xE8, 0xA7, 0xC8, 0x31, 0x3C, 0xB6, 0x21, 0xC0,
+ 0x20, 0x60, 0x6C, 0xE2, 0xCE, 0x8C, 0xFD, 0x95,
+ 0xE3, 0x4A, 0xB5, 0xB2, 0x40, 0xB1, 0xF3, 0x17,
+ 0xF9, 0x24, 0x06, 0x22, 0x2F, 0x25, 0x93, 0x8A,
+ 0x2A, 0x7E, 0x28, 0x3D, 0x47, 0xF8, 0x89, 0xA5,
+ 0x7B, 0x9B, 0xC5, 0x84, 0x59, 0x46, 0x90, 0x74,
+ 0x69, 0xC7, 0xAA, 0xEE, 0x6E, 0xD6, 0xB0, 0x18,
+ 0x66, 0xA0, 0x7A, 0x1E, 0xFB, 0xDB, 0x4E, 0x51,
+ 0x92, 0xE4, 0xE0, 0x3E, 0xB4, 0xD8, 0x23, 0x3B,
+ 0xC1, 0x5F, 0xFE, 0x98, 0x99, 0x73, 0x09, 0xA9,
+ 0xA3, 0xDF, 0x14, 0x5A, 0x26, 0x8F, 0x0B, 0xAF,
+ 0x4C, 0x97, 0x54, 0xE1, 0x63, 0x48, 0xED, 0xBA,
+ 0xCF, 0xBB, 0x1F, 0xDC, 0xA4, 0xFA, 0x64, 0x75,
+ 0xDE, 0x81, 0x9A, 0xFF, 0x49, 0x41, 0x27, 0x62,
+ 0x02, 0x15, 0xD9, 0x86, 0xAC, 0x3F, 0x0C, 0x61,
+ 0xD1, 0x77, 0x2B, 0x1B, 0x96, 0xDA, 0x68, 0x1C,
+ 0x44, 0x32, 0xBC, 0xA2, 0x87, 0xF7, 0x91, 0x8D,
+ 0x80, 0xDD, 0x0A, 0x50, 0x34, 0x4B, 0x00, 0xB9,
+ 0x36, 0xE6, 0x78, 0x4F, 0xC9, 0xE9, 0x2C, 0x43,
+ 0x88, 0x9E, 0x9C, 0x5B, 0x4D, 0x3A, 0x39, 0xEF };
+
+unsigned char table_8[32] = {
+ 0x13, 0x08, 0x1E, 0x1D, 0x17, 0x16, 0x07, 0x1F,
+ 0x0E, 0x03, 0x1A, 0x19, 0x01, 0x12, 0x11, 0x10,
+ 0x09, 0x0C, 0x0F, 0x14, 0x0B, 0x05, 0x00, 0x04,
+ 0x1C, 0x18, 0x0A, 0x15, 0x02, 0x1B, 0x06, 0x0D };
+
+unsigned char table_9[256] = {
+ 0x20, 0x2A, 0xDA, 0xFE, 0x76, 0x0D, 0xED, 0x39,
+ 0x51, 0x4C, 0x46, 0x9A, 0xF1, 0xB0, 0x10, 0xC7,
+ 0xD1, 0x6F, 0x18, 0x24, 0xB9, 0x7A, 0x4F, 0x47,
+ 0xE0, 0x4E, 0x88, 0x09, 0x8A, 0xBA, 0x60, 0xBD,
+ 0xC2, 0x27, 0x93, 0x7D, 0x94, 0x40, 0xCB, 0x80,
+ 0xB8, 0x41, 0x84, 0x5D, 0xC1, 0x0F, 0x5E, 0x78,
+ 0x2B, 0x48, 0x28, 0x29, 0xEE, 0x81, 0x90, 0x86,
+ 0x50, 0x9C, 0xF3, 0xB2, 0x35, 0x52, 0x0C, 0x9D,
+ 0xFC, 0x69, 0xD6, 0xA6, 0x06, 0xD7, 0xC6, 0xFF,
+ 0x1C, 0x14, 0x57, 0x33, 0xE2, 0x1F, 0x83, 0xA8,
+ 0xF7, 0x99, 0xC5, 0xDC, 0x70, 0x9E, 0xF4, 0x6B,
+ 0x0A, 0x77, 0x95, 0x4A, 0x2E, 0x53, 0xF2, 0x62,
+ 0x98, 0xF8, 0x96, 0xDB, 0xE6, 0x32, 0x3C, 0x58,
+ 0xD5, 0x6D, 0xE7, 0x4B, 0xCE, 0x91, 0x43, 0xD8,
+ 0xFA, 0xE3, 0x4D, 0xD9, 0x68, 0xDE, 0xEC, 0x01,
+ 0x08, 0xD3, 0x8F, 0x19, 0xC4, 0xA7, 0x6E, 0x3E,
+ 0x63, 0x12, 0x72, 0x42, 0x9F, 0xB4, 0x04, 0x1B,
+ 0x7E, 0x11, 0x17, 0x73, 0xB5, 0x22, 0x56, 0xA1,
+ 0x89, 0xDD, 0xF5, 0x3F, 0x49, 0x26, 0x8D, 0x15,
+ 0x85, 0x75, 0x5F, 0x65, 0x82, 0xB6, 0xF6, 0xD2,
+ 0xA4, 0x55, 0x37, 0xC8, 0xA0, 0xCC, 0x66, 0x5C,
+ 0xC9, 0x25, 0x36, 0x67, 0x7C, 0xE1, 0xA3, 0xCF,
+ 0xA9, 0x59, 0x2F, 0xFB, 0xBB, 0x07, 0x87, 0xA2,
+ 0x44, 0x92, 0x13, 0x00, 0x16, 0x61, 0x38, 0xEB,
+ 0xAE, 0xD4, 0x1E, 0x64, 0x6A, 0xE4, 0xCA, 0x1D,
+ 0x6C, 0xDF, 0xAB, 0x5B, 0x03, 0x7B, 0x9B, 0x8C,
+ 0x5A, 0xFD, 0xC3, 0xB3, 0x0B, 0xAA, 0xAC, 0x8B,
+ 0xBE, 0xBC, 0x3D, 0x97, 0xCD, 0x05, 0x21, 0x8E,
+ 0xAD, 0xEA, 0x54, 0x30, 0xAF, 0x02, 0xB1, 0x34,
+ 0x0E, 0xA5, 0x3B, 0x45, 0x1A, 0x23, 0xE8, 0x7F,
+ 0xEF, 0xB7, 0x31, 0xD0, 0xBF, 0x3A, 0x79, 0xE5,
+ 0xF9, 0xF0, 0x2C, 0x74, 0xE9, 0x71, 0xC0, 0x2D };
+
+unsigned char table_10[32] = {
+ 0x1D, 0x12, 0x11, 0x0D, 0x1E, 0x19, 0x16, 0x1B,
+ 0x18, 0x13, 0x07, 0x17, 0x0C, 0x02, 0x00, 0x15,
+ 0x0E, 0x08, 0x05, 0x01, 0x10, 0x06, 0x04, 0x0F,
+ 0x1F, 0x1A, 0x0B, 0x09, 0x0A, 0x14, 0x1C, 0x03 };
+
+unsigned char table_11[256] = {
+ 0x6B, 0x1D, 0xC6, 0x0A, 0xB7, 0xAC, 0xB2, 0x11,
+ 0x29, 0xD3, 0xA2, 0x4D, 0xCB, 0x03, 0xEF, 0xA6,
+ 0xC1, 0x5D, 0x75, 0x48, 0x35, 0x6C, 0xE2, 0x84,
+ 0xAB, 0xAA, 0xD8, 0x2C, 0x0E, 0x95, 0x25, 0x27,
+ 0x7D, 0x0B, 0xD0, 0xFB, 0x14, 0xE5, 0xF2, 0x4E,
+ 0x7F, 0x2A, 0x63, 0x3C, 0xC9, 0xF6, 0xDC, 0x07,
+ 0x26, 0x55, 0xCF, 0x2B, 0xCD, 0xA7, 0x17, 0xD2,
+ 0x9A, 0x7B, 0x93, 0x78, 0x9E, 0xE6, 0x2F, 0x49,
+ 0x1E, 0xFD, 0xF0, 0xFE, 0x7C, 0x33, 0x92, 0xA3,
+ 0xC8, 0xA0, 0xA9, 0xC4, 0xA1, 0x94, 0x6D, 0x44,
+ 0x0C, 0x90, 0x3A, 0x8C, 0x8E, 0x85, 0xAF, 0x40,
+ 0x36, 0xA4, 0xD1, 0xB9, 0x19, 0x6F, 0xF4, 0xBA,
+ 0x1A, 0x73, 0xD9, 0xB5, 0xB4, 0x7A, 0xF9, 0x83,
+ 0x58, 0xAD, 0xCE, 0x60, 0x98, 0xDB, 0x1C, 0x1B,
+ 0x52, 0xB8, 0xF3, 0x96, 0xED, 0xDE, 0xB3, 0xEE,
+ 0x4F, 0xBD, 0x10, 0xD4, 0x43, 0xEA, 0xE7, 0x37,
+ 0x12, 0x3D, 0xA8, 0x22, 0x65, 0xEC, 0x5B, 0x08,
+ 0x9D, 0x0D, 0x5C, 0xB6, 0x8A, 0x79, 0x3F, 0x04,
+ 0xD6, 0x01, 0xE1, 0xBE, 0xDD, 0x50, 0xFA, 0x41,
+ 0x13, 0x91, 0xF7, 0xDA, 0x18, 0xB0, 0x45, 0x81,
+ 0x4C, 0xF5, 0x32, 0x23, 0x56, 0x5A, 0xEB, 0x97,
+ 0x34, 0x00, 0x77, 0x71, 0x4B, 0x70, 0xD5, 0x31,
+ 0x72, 0x05, 0xDF, 0xE8, 0x15, 0x3B, 0x54, 0x16,
+ 0x89, 0xE4, 0xF1, 0xD7, 0x80, 0x82, 0x4A, 0xE3,
+ 0x39, 0x06, 0x47, 0x28, 0xC2, 0x86, 0x87, 0xB1,
+ 0x62, 0x74, 0x53, 0x21, 0x67, 0x38, 0x42, 0xCA,
+ 0x9B, 0xC3, 0x51, 0x99, 0x8B, 0x1F, 0x24, 0x8D,
+ 0xF8, 0x68, 0x3E, 0x59, 0xBB, 0x61, 0x5F, 0xBC,
+ 0x09, 0x6E, 0x8F, 0x0F, 0x2D, 0xC0, 0xE0, 0x46,
+ 0x66, 0x69, 0xA5, 0xE9, 0x30, 0x9C, 0x5E, 0xAE,
+ 0xBF, 0xC7, 0x20, 0x7E, 0x6A, 0xC5, 0x88, 0xFC,
+ 0x64, 0x76, 0xFF, 0x9F, 0x2E, 0x02, 0xCC, 0x57 };
+
+unsigned char table_12[32] = {
+ 0x14, 0x1B, 0x18, 0x00, 0x1F, 0x15, 0x17, 0x07,
+ 0x11, 0x1A, 0x0E, 0x13, 0x12, 0x06, 0x01, 0x03,
+ 0x1C, 0x0C, 0x0B, 0x1D, 0x10, 0x0F, 0x09, 0x19,
+ 0x0D, 0x1E, 0x04, 0x05, 0x08, 0x16, 0x0A, 0x02 };
+
+unsigned char table_13[256] = {
+ 0x37, 0x8A, 0x1B, 0x91, 0xA5, 0x2B, 0x2D, 0x88,
+ 0x8E, 0xFE, 0x0E, 0xD3, 0xF3, 0xE9, 0x7D, 0xD1,
+ 0x24, 0xEA, 0xB1, 0x8B, 0x5C, 0xA4, 0x44, 0x7E,
+ 0x8C, 0x2C, 0x73, 0xD5, 0x50, 0x3E, 0xD7, 0x18,
+ 0xB9, 0xD6, 0xBA, 0x94, 0x0C, 0xFC, 0xCB, 0xB4,
+ 0x0D, 0x63, 0x4C, 0xDE, 0x77, 0x16, 0xFD, 0x81,
+ 0x3C, 0x11, 0x45, 0x36, 0xF6, 0x67, 0x95, 0x6D,
+ 0x6A, 0x1A, 0xA3, 0xC5, 0x92, 0x10, 0x28, 0x84,
+ 0x48, 0xA6, 0x23, 0xE3, 0x4B, 0xE1, 0xF5, 0x19,
+ 0xE0, 0x2E, 0x00, 0x61, 0x74, 0xCC, 0xF7, 0xB0,
+ 0x68, 0xC8, 0x40, 0x6F, 0x59, 0x52, 0x26, 0x99,
+ 0xC9, 0xF9, 0xC4, 0x53, 0x9B, 0xEC, 0x03, 0x17,
+ 0xE2, 0x06, 0x30, 0x7B, 0xBE, 0xCD, 0x1D, 0x3B,
+ 0xD2, 0x5B, 0x65, 0x21, 0x49, 0xB7, 0x79, 0xCF,
+ 0x82, 0x86, 0xC7, 0x62, 0xEE, 0x8D, 0xFF, 0xD4,
+ 0xC3, 0x85, 0xA7, 0xFA, 0xA9, 0x6B, 0xF2, 0x69,
+ 0x9C, 0x38, 0x78, 0xBD, 0x7F, 0xDD, 0xCE, 0xA1,
+ 0x33, 0xC2, 0x43, 0xEB, 0xD8, 0xE6, 0x2A, 0xE4,
+ 0x76, 0x6C, 0xAA, 0x46, 0x05, 0xE7, 0xA0, 0x0A,
+ 0x71, 0x98, 0x41, 0x5F, 0x0F, 0xEF, 0x51, 0xAD,
+ 0xF0, 0xED, 0x96, 0x5A, 0x42, 0x3F, 0xBF, 0x6E,
+ 0xBC, 0x5D, 0xC1, 0x15, 0x70, 0x54, 0x4D, 0x14,
+ 0xB5, 0xCA, 0x27, 0x80, 0x87, 0x39, 0x60, 0x47,
+ 0x9D, 0x2F, 0x56, 0x1F, 0xBB, 0x31, 0xF1, 0xE8,
+ 0xB3, 0x9E, 0x5E, 0x7C, 0xD0, 0xC6, 0xB2, 0x57,
+ 0x83, 0xAC, 0x09, 0x8F, 0xA2, 0x90, 0x13, 0x25,
+ 0x01, 0x08, 0x64, 0xB6, 0x02, 0xDB, 0x55, 0x32,
+ 0xAF, 0x9A, 0xC0, 0x1C, 0x12, 0x29, 0x0B, 0x72,
+ 0x4F, 0xDA, 0xAB, 0x35, 0xF8, 0x22, 0xD9, 0x4E,
+ 0x3D, 0x1E, 0xDC, 0x58, 0x20, 0x34, 0xAE, 0x66,
+ 0x75, 0x93, 0x9F, 0x3A, 0x07, 0xE5, 0x89, 0xDF,
+ 0x97, 0x4A, 0xB8, 0x7A, 0xF4, 0xFB, 0x04, 0xA8 };
+
+unsigned char table_14[32] = {
+ 0x04, 0x14, 0x13, 0x15, 0x1A, 0x1B, 0x0F, 0x16,
+ 0x02, 0x0D, 0x0C, 0x06, 0x10, 0x17, 0x01, 0x0B,
+ 0x1E, 0x08, 0x1C, 0x18, 0x19, 0x0A, 0x1F, 0x05,
+ 0x11, 0x09, 0x1D, 0x07, 0x0E, 0x12, 0x03, 0x00 };
+
+unsigned char table_15[256] = {
+ 0x61, 0x48, 0x58, 0x41, 0x7F, 0x88, 0x43, 0x42,
+ 0xD9, 0x80, 0x81, 0xFE, 0xC6, 0x49, 0xD7, 0x2C,
+ 0xE6, 0x5B, 0xEE, 0xFF, 0x2A, 0x6F, 0xBF, 0x98,
+ 0xD6, 0x20, 0xB9, 0xB1, 0x5D, 0x95, 0x72, 0x1E,
+ 0x82, 0x96, 0xDE, 0xC1, 0x40, 0xD8, 0x70, 0xA3,
+ 0xD1, 0x1F, 0xF0, 0x9F, 0x2D, 0xDC, 0x3F, 0xF9,
+ 0x5E, 0x0D, 0x15, 0x2F, 0x67, 0x31, 0x9D, 0x84,
+ 0x97, 0x0C, 0xF6, 0x79, 0xC2, 0xA7, 0xC0, 0x32,
+ 0xB3, 0xEB, 0xED, 0x71, 0x30, 0xCC, 0x4B, 0xA0,
+ 0xF5, 0xC4, 0xCD, 0x27, 0xFA, 0x11, 0x25, 0xDB,
+ 0x4F, 0xE2, 0x7E, 0xA6, 0xAF, 0x34, 0x69, 0x63,
+ 0x8F, 0x08, 0x1C, 0x85, 0xF1, 0x57, 0x78, 0xC8,
+ 0xA2, 0x83, 0xB5, 0x68, 0xF7, 0x64, 0x45, 0x26,
+ 0x3B, 0x03, 0xAD, 0x3C, 0x50, 0xD5, 0x77, 0xFC,
+ 0xFB, 0x18, 0xC9, 0xD2, 0x9C, 0xBB, 0xBA, 0x76,
+ 0x23, 0x55, 0xD3, 0x5A, 0x01, 0xE9, 0x87, 0x07,
+ 0x19, 0x09, 0x39, 0x8A, 0x91, 0x93, 0x12, 0xDF,
+ 0x22, 0xA8, 0xCF, 0x4E, 0x4D, 0x65, 0xB0, 0x0F,
+ 0x13, 0x53, 0x21, 0x8C, 0xE5, 0xB7, 0x0B, 0x0E,
+ 0x6C, 0x44, 0xCA, 0x7B, 0xC5, 0x6E, 0xCE, 0xE3,
+ 0x14, 0x29, 0xAC, 0x2E, 0xE7, 0x59, 0xE8, 0x0A,
+ 0xEA, 0x66, 0x7C, 0x94, 0x6D, 0x05, 0x9E, 0x9A,
+ 0x2B, 0x38, 0x6A, 0xCB, 0x51, 0xEF, 0x06, 0xDA,
+ 0xFD, 0x47, 0x92, 0x1D, 0xA5, 0x37, 0x33, 0xEC,
+ 0xB4, 0x52, 0x56, 0xC3, 0xF4, 0xF8, 0x8B, 0xD0,
+ 0xA4, 0x5F, 0x28, 0x89, 0x75, 0xC7, 0x04, 0x00,
+ 0xE4, 0x86, 0x36, 0x3A, 0x99, 0x16, 0x7D, 0xE0,
+ 0x7A, 0x4C, 0x54, 0x46, 0x73, 0xB2, 0xF3, 0xE1,
+ 0x62, 0xBE, 0x90, 0x4A, 0x24, 0x6B, 0x3E, 0xAA,
+ 0x1B, 0xF2, 0x60, 0xD4, 0xA9, 0x9B, 0x1A, 0xB8,
+ 0xA1, 0x35, 0xAE, 0xB6, 0x10, 0x5C, 0x17, 0xBC,
+ 0xAB, 0x8D, 0x02, 0x74, 0xBD, 0x3D, 0x8E, 0xDD };
+
+unsigned char table_16[256] = {
+ 0x3F, 0x9C, 0x17, 0xC1, 0x59, 0xC6, 0x23, 0x93,
+ 0x4B, 0xDF, 0xCB, 0x55, 0x2B, 0xDE, 0xCD, 0xAD,
+ 0xB3, 0xE7, 0x42, 0x2F, 0x02, 0x5A, 0x7B, 0x5C,
+ 0x8F, 0xD1, 0x11, 0xCE, 0xEC, 0xF6, 0xA4, 0xE6,
+ 0x58, 0x98, 0x6A, 0x99, 0xFB, 0x9B, 0x53, 0x21,
+ 0x8A, 0x09, 0x2E, 0x3C, 0x22, 0x38, 0xAC, 0x07,
+ 0x91, 0x46, 0xA9, 0x95, 0xC3, 0x14, 0x84, 0xDB,
+ 0x36, 0x68, 0x1D, 0xDD, 0xF9, 0x12, 0xE0, 0x3D,
+ 0x8D, 0x4D, 0x05, 0x86, 0x69, 0xC0, 0xD3, 0xD5,
+ 0xA5, 0xC9, 0xE5, 0x67, 0x6D, 0xE2, 0x7F, 0xFE,
+ 0xB2, 0x0F, 0x62, 0xCF, 0x37, 0x35, 0xF3, 0x28,
+ 0x16, 0xA6, 0x50, 0x76, 0x80, 0x00, 0x31, 0x97,
+ 0x39, 0x7C, 0x25, 0x0C, 0x64, 0xF2, 0x52, 0x1A,
+ 0x92, 0x4F, 0x2A, 0x56, 0x03, 0x4C, 0xBD, 0x10,
+ 0xB7, 0x2C, 0x8C, 0xAE, 0x73, 0xB9, 0xE9, 0xF7,
+ 0xA7, 0xE1, 0x75, 0xBC, 0xC5, 0x1C, 0x3A, 0x63,
+ 0x7A, 0x4A, 0x29, 0xD2, 0x71, 0xE8, 0x08, 0xA1,
+ 0xD4, 0xFD, 0x13, 0xFA, 0xA0, 0x27, 0x41, 0x72,
+ 0x82, 0x18, 0x51, 0x60, 0x5E, 0x66, 0x0D, 0xAA,
+ 0xD8, 0x1F, 0xAF, 0x45, 0xD0, 0xF1, 0x9F, 0x6B,
+ 0xE4, 0x44, 0x89, 0xEE, 0xC4, 0x0B, 0x6C, 0xCC,
+ 0x83, 0x77, 0xA2, 0x87, 0x0A, 0xA8, 0xED, 0x90,
+ 0x74, 0x6E, 0xF5, 0xAB, 0xA3, 0xB6, 0x5F, 0x0E,
+ 0x04, 0x9A, 0xB4, 0x8E, 0xF0, 0xFF, 0x88, 0xB5,
+ 0xF8, 0xBF, 0x8B, 0x6F, 0x4E, 0x79, 0x40, 0xCA,
+ 0x24, 0x26, 0xDC, 0x33, 0xEB, 0x2D, 0x5B, 0x1B,
+ 0x9D, 0xC7, 0x49, 0x48, 0x54, 0x85, 0xEF, 0xD7,
+ 0xC2, 0xB8, 0xC8, 0x5D, 0xD9, 0x3B, 0x15, 0xBB,
+ 0x65, 0xE3, 0xD6, 0x30, 0x3E, 0x1E, 0x32, 0x9E,
+ 0x57, 0x81, 0x34, 0x06, 0xFC, 0xBA, 0x7D, 0x20,
+ 0x70, 0xDA, 0x7E, 0x47, 0x94, 0x61, 0xB0, 0x78,
+ 0xF4, 0xBE, 0xEA, 0x19, 0x43, 0x01, 0xB1, 0x96 };
+
+unsigned char table_17[256] = {
+ 0x7E, 0xF1, 0xD3, 0x75, 0x87, 0xA6, 0xED, 0x9E,
+ 0xA9, 0xD5, 0xC6, 0xBF, 0xE6, 0x6A, 0xEE, 0x4B,
+ 0x34, 0xDF, 0x4C, 0x7D, 0xDD, 0xFE, 0x3F, 0xAF,
+ 0x66, 0x2D, 0x74, 0x6F, 0xFC, 0x4F, 0x5F, 0x88,
+ 0x29, 0x7B, 0xC7, 0x2A, 0x70, 0xE8, 0x1D, 0xDE,
+ 0xD0, 0x55, 0x71, 0x81, 0xC4, 0x0D, 0x50, 0x4E,
+ 0x58, 0x00, 0x96, 0x97, 0xBB, 0xD7, 0x53, 0x15,
+ 0x6C, 0x40, 0x17, 0xC9, 0xFF, 0x8F, 0x94, 0xFB,
+ 0x19, 0x9A, 0x3E, 0xB5, 0x5A, 0x5E, 0x86, 0x24,
+ 0xB8, 0x77, 0xBA, 0x85, 0x51, 0x18, 0xBE, 0x59,
+ 0x79, 0xF3, 0xD4, 0xC3, 0xAB, 0x28, 0xFD, 0x25,
+ 0x41, 0x91, 0x07, 0x8D, 0xAE, 0x49, 0xF5, 0x80,
+ 0x35, 0xA1, 0x9C, 0x3C, 0xE2, 0x65, 0xB3, 0xE0,
+ 0x16, 0xCB, 0x12, 0x6B, 0xF7, 0xB1, 0x93, 0x8A,
+ 0xCE, 0x54, 0x4D, 0xF8, 0x13, 0xA2, 0x95, 0x46,
+ 0xEA, 0x61, 0x57, 0x9D, 0x27, 0x8B, 0x3D, 0x60,
+ 0x36, 0x68, 0x06, 0x56, 0xB6, 0x1B, 0xD2, 0x89,
+ 0x10, 0xA7, 0xC5, 0x1A, 0x0B, 0x2C, 0xBD, 0x14,
+ 0x0A, 0xDC, 0x23, 0xA8, 0xE1, 0x04, 0x02, 0xC0,
+ 0xB2, 0x9B, 0xE3, 0x2E, 0x33, 0x7C, 0x32, 0xAC,
+ 0x7A, 0x39, 0xB0, 0xF9, 0x98, 0x5B, 0x3A, 0x48,
+ 0x21, 0x90, 0xB9, 0x20, 0xF0, 0xA0, 0x09, 0x1F,
+ 0x2F, 0xEF, 0xEB, 0x22, 0x78, 0x82, 0x37, 0xD6,
+ 0xD1, 0x84, 0x76, 0x01, 0xDB, 0x43, 0xC2, 0xB7,
+ 0x7F, 0xA4, 0xE5, 0xC1, 0x1C, 0x69, 0x05, 0xEC,
+ 0xD8, 0x38, 0x67, 0x42, 0x72, 0xBC, 0x73, 0xAD,
+ 0xA3, 0xE9, 0x4A, 0x8E, 0x47, 0x1E, 0xC8, 0x6E,
+ 0xDA, 0x5D, 0x2B, 0xF6, 0x30, 0x63, 0xCC, 0xF4,
+ 0xCD, 0x8C, 0x0F, 0x3B, 0xE7, 0xD9, 0xCF, 0xB4,
+ 0x03, 0x92, 0x0E, 0x31, 0xE4, 0x08, 0xF2, 0x45,
+ 0xCA, 0x83, 0x26, 0x5C, 0xA5, 0x44, 0x64, 0x6D,
+ 0x9F, 0x99, 0x62, 0xAA, 0xFA, 0x11, 0x0C, 0x52 };
+
+unsigned char table_18[256] = {
+ 0x0F, 0x42, 0x3D, 0x86, 0x3E, 0x66, 0xFE, 0x5C,
+ 0x52, 0xE2, 0xA3, 0xB3, 0xCE, 0x16, 0xCC, 0x95,
+ 0xB0, 0x8B, 0x82, 0x3B, 0x93, 0x7D, 0x62, 0x08,
+ 0x1C, 0x6E, 0xBB, 0xCB, 0x1D, 0x88, 0x69, 0xD4,
+ 0xC9, 0x40, 0x1F, 0xBE, 0x27, 0xBC, 0xDB, 0x38,
+ 0xE5, 0xA1, 0x71, 0xBA, 0x8A, 0x5E, 0xFD, 0x36,
+ 0x8F, 0x26, 0x6B, 0xE4, 0x20, 0x6D, 0xC5, 0xDE,
+ 0xE0, 0x83, 0x7C, 0xD5, 0xD9, 0x4D, 0xDC, 0xE3,
+ 0x0D, 0x32, 0xED, 0x0E, 0x2F, 0x21, 0xA7, 0x79,
+ 0xA0, 0xD3, 0x8C, 0x14, 0x6F, 0xB7, 0xF8, 0x85,
+ 0x5D, 0x37, 0x24, 0xD6, 0x25, 0xD2, 0x8E, 0xA5,
+ 0xB8, 0xCD, 0x5A, 0x9F, 0x05, 0xAD, 0x65, 0x9E,
+ 0x4F, 0x5B, 0x56, 0xF0, 0xAA, 0xC2, 0x28, 0xA8,
+ 0x6A, 0x01, 0x99, 0x2E, 0xA6, 0x77, 0x74, 0x64,
+ 0x76, 0x15, 0x90, 0x75, 0xAF, 0xE8, 0x39, 0x48,
+ 0x09, 0x11, 0xE1, 0x2D, 0xEC, 0xB5, 0x7A, 0xB1,
+ 0x94, 0x13, 0x41, 0x4C, 0x02, 0xA9, 0x97, 0xDF,
+ 0xC3, 0x8D, 0xEA, 0x3A, 0x9C, 0xD1, 0xA2, 0x9A,
+ 0xD7, 0x59, 0xD8, 0x18, 0xDA, 0x47, 0x89, 0x81,
+ 0xC7, 0xF5, 0xFC, 0x98, 0xCA, 0x91, 0x06, 0x68,
+ 0xC8, 0x07, 0x4A, 0x84, 0x0A, 0xE7, 0x33, 0x2C,
+ 0xEB, 0xDD, 0x5F, 0xAC, 0x23, 0x1A, 0x35, 0x70,
+ 0x43, 0x80, 0x61, 0xAE, 0xC1, 0xD0, 0x7B, 0x92,
+ 0x49, 0x51, 0x53, 0xC4, 0x34, 0x30, 0x0C, 0x4B,
+ 0x00, 0x04, 0x10, 0xFF, 0x63, 0x44, 0xB4, 0x0B,
+ 0x57, 0x72, 0xF1, 0x9D, 0x19, 0xF6, 0xB2, 0x87,
+ 0x1B, 0xEE, 0x46, 0x2A, 0xF3, 0xBF, 0x12, 0x96,
+ 0x58, 0x2B, 0xF9, 0xB6, 0xCF, 0x22, 0x3C, 0xAB,
+ 0x1E, 0x6C, 0x31, 0xC6, 0xF7, 0x78, 0x45, 0x17,
+ 0xE9, 0x7E, 0x73, 0xF2, 0x55, 0xFB, 0x3F, 0x9B,
+ 0xF4, 0xBD, 0xA4, 0x29, 0x60, 0x03, 0xB9, 0x50,
+ 0xFA, 0x4E, 0xEF, 0x54, 0xE6, 0x7F, 0xC0, 0x67 };
+
+unsigned char table_19[256] = {
+ 0xEA, 0xE7, 0x13, 0x14, 0xB9, 0xC0, 0xC4, 0x42,
+ 0x49, 0x6E, 0x2A, 0xA6, 0x65, 0x3C, 0x6A, 0x40,
+ 0x07, 0xCD, 0x4F, 0xFE, 0xF2, 0x2D, 0xC8, 0x30,
+ 0x9D, 0xBE, 0x1B, 0x9B, 0x4A, 0x7E, 0x9F, 0xA7,
+ 0x78, 0xAB, 0x4D, 0x1D, 0xF1, 0x96, 0x32, 0x84,
+ 0xFB, 0x80, 0x88, 0xE8, 0x41, 0x97, 0xDC, 0xD0,
+ 0x4E, 0x33, 0xA4, 0x3B, 0xE0, 0xDD, 0x36, 0xC9,
+ 0x72, 0x48, 0x8A, 0x2F, 0x35, 0xF0, 0xDF, 0x21,
+ 0xE1, 0xE5, 0x6C, 0x9A, 0x60, 0x8F, 0xB7, 0x24,
+ 0xE4, 0x9E, 0x8C, 0x0F, 0x3D, 0x28, 0xBB, 0xD6,
+ 0x69, 0xA0, 0x66, 0xC7, 0xE3, 0xD8, 0x11, 0x27,
+ 0xD9, 0x37, 0xF4, 0xF5, 0x8E, 0xD4, 0x76, 0xE2,
+ 0xDB, 0x15, 0xA2, 0x5C, 0x9C, 0xEE, 0x44, 0xED,
+ 0x2B, 0xB3, 0x75, 0x74, 0x71, 0x8B, 0x3A, 0x91,
+ 0x06, 0x19, 0xC1, 0x57, 0x89, 0xCC, 0x82, 0x10,
+ 0x17, 0xB2, 0x08, 0x70, 0x39, 0xCA, 0xBA, 0xB5,
+ 0xAA, 0xBF, 0x02, 0xBD, 0x26, 0x58, 0x04, 0x54,
+ 0x23, 0x4B, 0x90, 0x51, 0x6D, 0x98, 0xD5, 0xB0,
+ 0xAF, 0x22, 0xDA, 0xB4, 0x87, 0xFC, 0x7D, 0x18,
+ 0x6F, 0x64, 0x59, 0x09, 0x0C, 0xA5, 0x5D, 0x03,
+ 0x0A, 0xD3, 0xCE, 0x99, 0x8D, 0xC2, 0xC3, 0x62,
+ 0xD2, 0x83, 0x1A, 0xAC, 0x7C, 0x93, 0xD7, 0xA9,
+ 0x16, 0xF7, 0x77, 0xE6, 0x3E, 0x05, 0x73, 0x55,
+ 0x43, 0x95, 0x7A, 0x6B, 0x38, 0x67, 0x3F, 0xC6,
+ 0xAD, 0x0E, 0x29, 0x46, 0x45, 0xFA, 0xBC, 0xEC,
+ 0x5B, 0x7F, 0x0B, 0x1C, 0x01, 0x12, 0x85, 0x50,
+ 0xF9, 0xEF, 0x25, 0x34, 0x79, 0x2E, 0xEB, 0x00,
+ 0x5F, 0x86, 0xF8, 0x4C, 0xA8, 0x56, 0xB6, 0x5A,
+ 0xF3, 0x31, 0x94, 0x92, 0xB1, 0xB8, 0x52, 0xD1,
+ 0xCF, 0xCB, 0xA1, 0x81, 0x68, 0x47, 0xFF, 0xC5,
+ 0xFD, 0x1F, 0xDE, 0x53, 0xA3, 0x2C, 0x20, 0xF6,
+ 0x1E, 0x0D, 0xAE, 0x7B, 0x5E, 0x61, 0xE9, 0x63 };
+
+unsigned char table_20[32] = {
+ 0x0D, 0x0B, 0x11, 0x02, 0x05, 0x1B, 0x08, 0x1D,
+ 0x04, 0x14, 0x01, 0x09, 0x00, 0x19, 0x1E, 0x15,
+ 0x1F, 0x0A, 0x0F, 0x1C, 0x10, 0x16, 0x0C, 0x07,
+ 0x13, 0x1A, 0x06, 0x17, 0x0E, 0x12, 0x18, 0x03 };
+
+unsigned char table_21[256] = {
+ 0x4C, 0x94, 0xAD, 0x66, 0x9E, 0x69, 0x04, 0xA8,
+ 0x61, 0xE0, 0xE1, 0x3D, 0xFD, 0x9C, 0xFB, 0x19,
+ 0x1E, 0x80, 0x8C, 0xA0, 0xFC, 0x27, 0x26, 0x3B,
+ 0x48, 0x6D, 0x07, 0xE4, 0xEA, 0x17, 0x64, 0x9B,
+ 0xD0, 0xE2, 0xD1, 0x13, 0x39, 0xF5, 0x73, 0xD3,
+ 0x0C, 0x3A, 0x6E, 0x77, 0xFA, 0xE3, 0x2F, 0x44,
+ 0x7E, 0x72, 0x30, 0x43, 0xD4, 0x7F, 0x36, 0xD9,
+ 0xBD, 0x3E, 0x3F, 0x91, 0xBE, 0x54, 0x79, 0xA6,
+ 0x7C, 0x0E, 0xC5, 0x7A, 0x70, 0xC4, 0xD7, 0xCE,
+ 0xDA, 0xAA, 0x68, 0x8F, 0xBC, 0x96, 0x1B, 0x16,
+ 0xA2, 0xC6, 0x67, 0x09, 0x45, 0x9F, 0xCF, 0x41,
+ 0xC8, 0x60, 0x74, 0x99, 0x5D, 0x85, 0x5F, 0x50,
+ 0x33, 0x52, 0x22, 0xA9, 0xB5, 0x2D, 0x98, 0x87,
+ 0x15, 0x9A, 0xAC, 0x2C, 0xDE, 0xC0, 0xB8, 0x37,
+ 0x88, 0x1F, 0xC1, 0x4F, 0x65, 0x0F, 0x3C, 0x84,
+ 0x4B, 0x1A, 0xAB, 0xA4, 0x23, 0xCB, 0xB1, 0xC7,
+ 0xDB, 0xEF, 0x40, 0x0D, 0x46, 0xE8, 0xF4, 0x71,
+ 0x38, 0x01, 0x5C, 0x0B, 0x5E, 0xC9, 0xAF, 0xC3,
+ 0xF6, 0xB6, 0x10, 0x1D, 0xE5, 0x8A, 0x90, 0xA7,
+ 0xA3, 0x05, 0x4E, 0x14, 0x63, 0x25, 0x34, 0xEC,
+ 0x6B, 0x95, 0x21, 0x55, 0xF2, 0xF0, 0x47, 0x9D,
+ 0xF8, 0x8E, 0x02, 0x0A, 0xED, 0x97, 0xAE, 0x00,
+ 0x2A, 0xEB, 0xB2, 0xA5, 0x32, 0x06, 0x2E, 0xFE,
+ 0x8D, 0x7B, 0x7D, 0x35, 0x5A, 0xD2, 0xF1, 0xE9,
+ 0xF9, 0x62, 0xB7, 0xB9, 0x53, 0x75, 0x5B, 0x8B,
+ 0xCC, 0x6C, 0x18, 0x49, 0x89, 0x31, 0xB0, 0x92,
+ 0x6F, 0xDF, 0x03, 0x57, 0xF3, 0x58, 0xCA, 0x2B,
+ 0x93, 0xA1, 0xD6, 0x24, 0x29, 0xCD, 0x59, 0x1C,
+ 0x83, 0xB3, 0x42, 0xBF, 0x82, 0xB4, 0x11, 0x4A,
+ 0x08, 0xEE, 0x76, 0x4D, 0x12, 0xDC, 0xE6, 0xC2,
+ 0x56, 0xBA, 0x86, 0x28, 0x6A, 0x20, 0x51, 0xF7,
+ 0xFF, 0xD8, 0xE7, 0xDD, 0xBB, 0x78, 0xD5, 0x81 };
+
+unsigned char table_22[32] = {
+ 0x0B, 0x15, 0x1C, 0x0C, 0x06, 0x0A, 0x1D, 0x16,
+ 0x12, 0x0E, 0x04, 0x11, 0x1F, 0x0F, 0x07, 0x02,
+ 0x17, 0x13, 0x19, 0x18, 0x0D, 0x10, 0x1A, 0x05,
+ 0x03, 0x00, 0x01, 0x08, 0x09, 0x14, 0x1B, 0x1E };
+
+unsigned char table_23[256] = {
+ 0x36, 0x53, 0x2D, 0xD0, 0x7A, 0xF0, 0xD5, 0x1C,
+ 0x50, 0x61, 0x9A, 0x90, 0x0B, 0x29, 0x20, 0x77,
+ 0xF1, 0x82, 0xFE, 0xC1, 0xA7, 0xB6, 0x78, 0x87,
+ 0x02, 0x05, 0xCB, 0x28, 0xAE, 0xD6, 0x17, 0x1A,
+ 0x91, 0x5D, 0xB9, 0xE2, 0xDE, 0x6A, 0x4E, 0x07,
+ 0xAC, 0x38, 0x13, 0x3B, 0x46, 0xFD, 0xB7, 0xD1,
+ 0x79, 0xFB, 0x58, 0x76, 0x08, 0x47, 0x95, 0xA6,
+ 0x99, 0x9E, 0x12, 0x67, 0xC2, 0xED, 0x9C, 0x1B,
+ 0x89, 0x71, 0xB5, 0x4A, 0xAA, 0x5F, 0x34, 0x85,
+ 0x40, 0x2B, 0x9F, 0x37, 0x7C, 0x0F, 0xD4, 0x75,
+ 0x48, 0x27, 0x2E, 0xC9, 0xEB, 0x06, 0xDF, 0x8C,
+ 0x14, 0xAF, 0xEE, 0xA2, 0x74, 0x45, 0x8D, 0x70,
+ 0x6B, 0xD7, 0x56, 0xCF, 0xBC, 0x7B, 0x01, 0xC8,
+ 0x54, 0xB0, 0x3C, 0x39, 0xFA, 0x81, 0xDC, 0xBB,
+ 0x0D, 0xB2, 0xAD, 0x93, 0xC7, 0x8A, 0x73, 0x6C,
+ 0xC3, 0x04, 0x2F, 0xEF, 0x52, 0x33, 0x9D, 0x1E,
+ 0xC5, 0x65, 0x23, 0xD8, 0xB1, 0xD2, 0xE5, 0x25,
+ 0x2C, 0xE6, 0x92, 0xB4, 0xF7, 0xF4, 0x8F, 0x6E,
+ 0xE8, 0x5A, 0x8E, 0x7D, 0x4C, 0xB3, 0xFF, 0x41,
+ 0x26, 0xE3, 0x30, 0x69, 0xF8, 0x80, 0x57, 0x4F,
+ 0xA0, 0x7F, 0x66, 0x68, 0xE1, 0x7E, 0x0E, 0x31,
+ 0xE7, 0xEA, 0x3E, 0x8B, 0x4B, 0x94, 0xE9, 0xCD,
+ 0x19, 0x35, 0xA3, 0x98, 0xD9, 0x5B, 0x44, 0x2A,
+ 0xE0, 0x6D, 0xF3, 0xE4, 0x72, 0x18, 0x03, 0x59,
+ 0x84, 0x09, 0xA1, 0x9B, 0xBD, 0xDA, 0x4D, 0x63,
+ 0xCC, 0x3A, 0x10, 0xFC, 0x3F, 0x0A, 0x88, 0x24,
+ 0xF5, 0x21, 0xC4, 0x6F, 0x1F, 0x42, 0x62, 0x64,
+ 0x51, 0xDD, 0xCA, 0xF9, 0x22, 0xCE, 0xA8, 0x86,
+ 0xBA, 0xB8, 0x5C, 0xAB, 0x32, 0x00, 0x0C, 0xF2,
+ 0x83, 0xDB, 0xF6, 0x60, 0x3D, 0x16, 0xEC, 0x11,
+ 0xA4, 0xBE, 0x96, 0x5E, 0x97, 0xD3, 0xA5, 0x55,
+ 0x1D, 0x15, 0xC6, 0xBF, 0xA9, 0x43, 0xC0, 0x49 };
+
+unsigned char table_24[256] = {
+ 0xDC, 0x5A, 0xE6, 0x59, 0x64, 0xDA, 0x58, 0x40,
+ 0x95, 0xF8, 0x2A, 0xE0, 0x39, 0x7E, 0x32, 0x89,
+ 0x09, 0x93, 0xED, 0x55, 0xC3, 0x5B, 0x1A, 0xD1,
+ 0xA5, 0x8B, 0x0F, 0x13, 0xC9, 0xE1, 0x34, 0xD0,
+ 0xB6, 0xA2, 0xD9, 0x52, 0x57, 0x83, 0xFD, 0xE9,
+ 0xAC, 0x73, 0x6E, 0x21, 0xF1, 0x0E, 0x25, 0xCC,
+ 0x36, 0xFB, 0xF7, 0x92, 0x15, 0x30, 0x54, 0x91,
+ 0xD6, 0x9E, 0xAA, 0x35, 0x70, 0xB2, 0xC0, 0x27,
+ 0xFE, 0x04, 0xBC, 0xC7, 0x02, 0xFA, 0x7D, 0xE3,
+ 0xBE, 0x62, 0x79, 0x2B, 0x31, 0x6A, 0x8F, 0x7F,
+ 0x56, 0xF0, 0xB4, 0x0C, 0x1F, 0x68, 0xB7, 0xB9,
+ 0x0B, 0x14, 0x3E, 0xA9, 0x4B, 0x03, 0x10, 0xEE,
+ 0x2C, 0xAB, 0x8A, 0x77, 0xB1, 0xE7, 0xCA, 0xD4,
+ 0x98, 0x01, 0xAD, 0x1E, 0x50, 0x26, 0x82, 0x44,
+ 0xF3, 0xBF, 0xD3, 0x6B, 0x33, 0x0A, 0x3C, 0x5D,
+ 0xCE, 0x81, 0xC5, 0x78, 0x9F, 0xB8, 0x23, 0xDB,
+ 0x4E, 0xA1, 0x41, 0x76, 0xAE, 0x51, 0x86, 0x06,
+ 0x7A, 0x66, 0xA0, 0x5E, 0x29, 0x17, 0x84, 0x4A,
+ 0xB0, 0x3B, 0x3D, 0x71, 0x07, 0x7B, 0x0D, 0x9A,
+ 0x6F, 0x9B, 0x5C, 0x88, 0xB3, 0xD7, 0x24, 0xD5,
+ 0x48, 0xF5, 0xE8, 0xE4, 0xCF, 0x16, 0xA4, 0xC8,
+ 0xEF, 0x42, 0x22, 0xEC, 0x47, 0x69, 0x90, 0x63,
+ 0xE2, 0x1B, 0x87, 0x85, 0x3F, 0xDE, 0x8C, 0x60,
+ 0x99, 0xE5, 0x8E, 0x4F, 0xF4, 0xBA, 0xB5, 0x9C,
+ 0x37, 0x67, 0xBD, 0xA6, 0x97, 0xDD, 0xCB, 0x43,
+ 0x45, 0x19, 0x49, 0x1C, 0x75, 0xC1, 0xBB, 0xF2,
+ 0x46, 0xFC, 0x53, 0x9D, 0xD8, 0xA3, 0xDF, 0x2F,
+ 0xEB, 0x72, 0x94, 0xA8, 0x6D, 0xC6, 0x28, 0x4C,
+ 0x00, 0x38, 0xC2, 0x65, 0x05, 0x2E, 0xD2, 0x12,
+ 0xFF, 0x18, 0x61, 0x6C, 0x7C, 0x11, 0xAF, 0x96,
+ 0xCD, 0x20, 0x74, 0x08, 0x1D, 0xC4, 0xF9, 0x4D,
+ 0xEA, 0x8D, 0x2D, 0x5F, 0xF6, 0xA7, 0x80, 0x3A };
+
+unsigned char table_25[32] = {
+ 0x0A, 0x11, 0x17, 0x03, 0x05, 0x0B, 0x18, 0x13,
+ 0x09, 0x02, 0x00, 0x1C, 0x0C, 0x08, 0x1B, 0x14,
+ 0x06, 0x0E, 0x01, 0x0D, 0x16, 0x1E, 0x1D, 0x19,
+ 0x0F, 0x1A, 0x10, 0x04, 0x12, 0x15, 0x07, 0x1F };
+
+unsigned char table_26[32] = {
+ 0x19, 0x13, 0x1B, 0x01, 0x1C, 0x0D, 0x0C, 0x15,
+ 0x0B, 0x00, 0x1A, 0x0F, 0x12, 0x16, 0x08, 0x0A,
+ 0x03, 0x06, 0x14, 0x10, 0x18, 0x04, 0x11, 0x1D,
+ 0x1F, 0x07, 0x17, 0x05, 0x02, 0x0E, 0x1E, 0x09 };
+
+unsigned char table_27[256] = {
+ 0x72, 0xF0, 0x14, 0xCB, 0x61, 0xA5, 0xB2, 0x02,
+ 0x75, 0x22, 0xC3, 0x9D, 0x5A, 0x63, 0xFA, 0x5F,
+ 0xD9, 0x55, 0x58, 0x43, 0x24, 0x7D, 0x77, 0x93,
+ 0xBA, 0x50, 0x1D, 0xF7, 0x49, 0x18, 0xB0, 0x42,
+ 0xBB, 0xEC, 0x52, 0x38, 0xDC, 0xC8, 0x16, 0x54,
+ 0x17, 0x19, 0x89, 0x67, 0x33, 0x3C, 0x0A, 0xAD,
+ 0xC9, 0xDE, 0x81, 0xED, 0xBD, 0x0E, 0x0B, 0x6D,
+ 0x46, 0x30, 0x35, 0x2B, 0x8C, 0xA0, 0x1C, 0x0D,
+ 0xFD, 0xA1, 0x70, 0xC6, 0xD8, 0x41, 0xB3, 0xC0,
+ 0x44, 0xEB, 0x92, 0xBE, 0x6B, 0x98, 0x1A, 0x76,
+ 0x71, 0xC5, 0x51, 0x56, 0x80, 0xFC, 0x01, 0x53,
+ 0x4B, 0xD0, 0x8B, 0xD2, 0x7B, 0xE7, 0x15, 0x5D,
+ 0xE5, 0xA6, 0x8A, 0xD3, 0x9B, 0xF4, 0x69, 0x23,
+ 0xE8, 0xB6, 0xC7, 0xE2, 0x73, 0x9F, 0x88, 0xDF,
+ 0xB4, 0x28, 0xEE, 0xC2, 0x94, 0xB8, 0xF9, 0x7F,
+ 0x4A, 0x57, 0x06, 0xF6, 0xBF, 0xC1, 0xAB, 0xFB,
+ 0xA4, 0x8E, 0xD1, 0xD7, 0xF5, 0x7C, 0xA3, 0x1E,
+ 0x3B, 0x32, 0x03, 0xAA, 0x90, 0x5C, 0x48, 0xE0,
+ 0xE3, 0xCF, 0xD4, 0xEF, 0x59, 0xD5, 0x1B, 0x34,
+ 0x1F, 0x95, 0xCE, 0x7A, 0x20, 0x26, 0x87, 0xB7,
+ 0x78, 0x9C, 0x4F, 0xA2, 0x12, 0x97, 0x27, 0x3F,
+ 0xFF, 0x07, 0x84, 0x96, 0x04, 0xAF, 0xA8, 0xEA,
+ 0x2C, 0x6C, 0xAE, 0x37, 0x91, 0xA9, 0x10, 0xDB,
+ 0xCD, 0xDA, 0x08, 0x99, 0xF1, 0x4D, 0xCC, 0x68,
+ 0x79, 0x2E, 0xB1, 0x39, 0x9E, 0xE9, 0x2F, 0x6A,
+ 0x3D, 0x0F, 0x85, 0x8D, 0xCA, 0x29, 0x86, 0xD6,
+ 0xDD, 0x05, 0x25, 0x3A, 0x40, 0x21, 0x45, 0xAC,
+ 0x11, 0xF3, 0xA7, 0x09, 0x2A, 0x31, 0xE4, 0x0C,
+ 0xF8, 0x6E, 0x3E, 0xB5, 0x82, 0xFE, 0x74, 0x13,
+ 0x65, 0xE1, 0x2D, 0x8F, 0xE6, 0xC4, 0x00, 0x5B,
+ 0x4E, 0xB9, 0x66, 0xF2, 0x62, 0x36, 0x4C, 0x83,
+ 0x5E, 0x6F, 0x47, 0x64, 0xBC, 0x9A, 0x60, 0x7E };
+
+unsigned char table_28[32] = {
+ 0x15, 0x05, 0x08, 0x19, 0x02, 0x18, 0x1E, 0x07,
+ 0x0D, 0x0C, 0x1A, 0x06, 0x17, 0x03, 0x10, 0x09,
+ 0x01, 0x11, 0x1C, 0x04, 0x0F, 0x1F, 0x12, 0x0B,
+ 0x1B, 0x13, 0x0A, 0x16, 0x0E, 0x00, 0x1D, 0x14 };
+
+unsigned char table_29[256] = {
+ 0x34, 0x59, 0x05, 0x13, 0x09, 0x1D, 0xDF, 0x77,
+ 0x11, 0xA5, 0x92, 0x27, 0xCD, 0x7B, 0x5E, 0x80,
+ 0xF9, 0x50, 0x18, 0x24, 0xD4, 0x70, 0x4A, 0x39,
+ 0x66, 0xA4, 0xDB, 0xE9, 0xED, 0x48, 0xD9, 0xE7,
+ 0x32, 0xDA, 0x53, 0x8F, 0x72, 0xE1, 0xF6, 0xFE,
+ 0xD3, 0xAD, 0xA6, 0x1F, 0xB9, 0xD1, 0x0F, 0x4C,
+ 0x23, 0x90, 0x68, 0xBC, 0x4B, 0x9B, 0x3D, 0xAB,
+ 0xF0, 0x94, 0x4F, 0x1C, 0x07, 0x65, 0x7F, 0x01,
+ 0x5C, 0xD7, 0x21, 0x8C, 0xBF, 0x8E, 0xB8, 0x86,
+ 0x6C, 0x33, 0x36, 0xC1, 0x06, 0x74, 0x37, 0x84,
+ 0x41, 0xAE, 0x67, 0x29, 0xB4, 0x85, 0xCE, 0x2A,
+ 0xCB, 0x1E, 0x61, 0x9E, 0x7A, 0x44, 0x3E, 0x89,
+ 0x14, 0x20, 0x19, 0xBB, 0xE0, 0xAA, 0xCF, 0x83,
+ 0xA8, 0x93, 0x43, 0xF2, 0xAC, 0x0E, 0xD2, 0xCC,
+ 0xDD, 0x47, 0x58, 0xC9, 0xCA, 0x1B, 0x54, 0x6E,
+ 0x8A, 0x79, 0xF8, 0xC4, 0xFB, 0xD5, 0x91, 0xDE,
+ 0x12, 0x31, 0x99, 0xFA, 0x6D, 0xC8, 0x57, 0xEC,
+ 0xB7, 0x28, 0x0C, 0x52, 0xF1, 0x0D, 0xB1, 0x9A,
+ 0x26, 0x98, 0x16, 0x7D, 0xD0, 0x2E, 0x8B, 0xD8,
+ 0xE6, 0xE8, 0x30, 0xFD, 0x7C, 0x64, 0x5A, 0xBD,
+ 0x87, 0xE2, 0xA1, 0x3F, 0xC3, 0x38, 0x96, 0xA3,
+ 0x2D, 0xF3, 0x3A, 0xEE, 0xC0, 0x10, 0xEA, 0x6F,
+ 0x8D, 0x03, 0xF4, 0x51, 0x97, 0x7E, 0x56, 0x42,
+ 0x3C, 0x5D, 0x5F, 0xF5, 0x6A, 0xAF, 0xE4, 0xBE,
+ 0xBA, 0x78, 0xA0, 0x5B, 0x49, 0xA7, 0xC7, 0x9C,
+ 0x63, 0x6B, 0x00, 0x17, 0x69, 0x75, 0x3B, 0x40,
+ 0xEF, 0x45, 0xB5, 0x2B, 0x2F, 0x02, 0xC6, 0x22,
+ 0x9F, 0xFC, 0x73, 0x08, 0x81, 0xB2, 0x2C, 0x71,
+ 0x35, 0xA2, 0xE3, 0xB3, 0x9D, 0xC5, 0x0A, 0xC2,
+ 0x25, 0x82, 0xDC, 0x88, 0xA9, 0xE5, 0xF7, 0xEB,
+ 0xD6, 0x60, 0x76, 0x55, 0x0B, 0x4E, 0xFF, 0x1A,
+ 0x46, 0x62, 0xB6, 0xB0, 0x15, 0x04, 0x95, 0x4D };
+
+unsigned char table_30[32] = {
+ 0x00, 0x1C, 0x0E, 0x0C, 0x06, 0x16, 0x09, 0x12,
+ 0x01, 0x13, 0x0B, 0x14, 0x11, 0x08, 0x04, 0x18,
+ 0x10, 0x1B, 0x15, 0x03, 0x02, 0x19, 0x1A, 0x17,
+ 0x1E, 0x1F, 0x0F, 0x07, 0x0D, 0x05, 0x1D, 0x0A };
+
+unsigned char table_31[256] = {
+ 0xDF, 0xD8, 0x3F, 0xBC, 0x5F, 0xC9, 0x8E, 0x4C,
+ 0x0B, 0x3C, 0xE5, 0xBF, 0x39, 0xD5, 0x30, 0xDD,
+ 0x23, 0xC7, 0x72, 0x63, 0x1F, 0xF8, 0x96, 0x31,
+ 0x70, 0xD6, 0x9E, 0xE8, 0x9D, 0xF5, 0xEF, 0x65,
+ 0xC2, 0x50, 0x62, 0x77, 0xD3, 0x6C, 0x1A, 0x91,
+ 0xBB, 0xFF, 0xCD, 0x9B, 0xB6, 0xBA, 0xB8, 0x7A,
+ 0x14, 0xA7, 0x74, 0x89, 0xD4, 0x6E, 0x19, 0x69,
+ 0xAB, 0x01, 0x15, 0x0E, 0x87, 0x55, 0x79, 0x1C,
+ 0x18, 0xBE, 0xA8, 0xDB, 0x52, 0xD2, 0x8F, 0x7E,
+ 0x81, 0xAF, 0xFD, 0x5C, 0x3E, 0x1B, 0xB9, 0xB2,
+ 0xB7, 0x51, 0x57, 0x8C, 0xCF, 0x5B, 0xA4, 0x75,
+ 0xDE, 0x22, 0x8B, 0x10, 0x12, 0xC8, 0x35, 0x2D,
+ 0x45, 0xB5, 0xF0, 0x47, 0x88, 0x16, 0xEB, 0x67,
+ 0xD9, 0x0C, 0xF1, 0xC1, 0x34, 0x33, 0xC6, 0x78,
+ 0xB3, 0x26, 0xE3, 0xBD, 0x5D, 0x4E, 0x66, 0xE4,
+ 0xD7, 0xC4, 0xE6, 0xA1, 0xB0, 0x95, 0x2B, 0x9A,
+ 0x4A, 0x3A, 0xCB, 0x40, 0xE1, 0x60, 0x49, 0xCC,
+ 0x03, 0xAC, 0xF4, 0x97, 0x32, 0x0F, 0x38, 0x17,
+ 0xF9, 0xE0, 0xD1, 0xFB, 0x04, 0x5E, 0x68, 0x06,
+ 0xAE, 0xFA, 0xAA, 0xED, 0x24, 0x0D, 0x00, 0x61,
+ 0x20, 0xA3, 0x7B, 0x6B, 0x76, 0x27, 0xEA, 0xCE,
+ 0x6A, 0x82, 0x9F, 0x6D, 0x9C, 0x64, 0xA2, 0x11,
+ 0x37, 0x2A, 0xCA, 0x84, 0x25, 0x7C, 0x2F, 0x8D,
+ 0x90, 0xE7, 0x09, 0x93, 0xF3, 0x43, 0x71, 0xEC,
+ 0xA9, 0x7D, 0x94, 0xA6, 0x3D, 0x7F, 0x54, 0x44,
+ 0x99, 0x80, 0x41, 0xC0, 0xA0, 0x8A, 0x1E, 0xDC,
+ 0x08, 0xD0, 0x2E, 0x42, 0x05, 0x85, 0x86, 0xFE,
+ 0x3B, 0x59, 0xC3, 0x58, 0x13, 0xB4, 0x36, 0xA5,
+ 0x73, 0x28, 0x29, 0xDA, 0x4F, 0x1D, 0xB1, 0x53,
+ 0x46, 0x2C, 0xF2, 0x4D, 0xAD, 0xFC, 0x83, 0x02,
+ 0x6F, 0x07, 0xE9, 0xEE, 0x21, 0x98, 0x5A, 0xC5,
+ 0x92, 0x48, 0xF7, 0x0A, 0xF6, 0xE2, 0x4B, 0x56 };
+
+unsigned char table_32[256] = {
+ 0x7B, 0x0F, 0x56, 0x2F, 0x1E, 0x2A, 0x7A, 0xD1,
+ 0x02, 0x91, 0x4E, 0x37, 0x6C, 0x10, 0xA7, 0xF2,
+ 0x38, 0xAC, 0x9E, 0x2B, 0x5E, 0x23, 0xE3, 0x19,
+ 0x9B, 0xF6, 0xB0, 0x59, 0x14, 0xB9, 0xA9, 0x46,
+ 0x84, 0x1D, 0xC0, 0x98, 0xF3, 0xE1, 0xE8, 0x94,
+ 0x52, 0x35, 0xBA, 0xD8, 0x07, 0xEF, 0x31, 0xF8,
+ 0x03, 0x76, 0x9C, 0xD7, 0xE4, 0x8B, 0xAF, 0x60,
+ 0xDD, 0x51, 0x00, 0xDF, 0x11, 0x7F, 0x1C, 0xED,
+ 0x49, 0xC9, 0xF4, 0x87, 0x64, 0xFC, 0x5D, 0xAD,
+ 0x88, 0x85, 0xF7, 0x5A, 0x92, 0xDB, 0x72, 0x1A,
+ 0x83, 0x15, 0x30, 0x24, 0x9F, 0xFF, 0x5B, 0xF1,
+ 0xD2, 0xFD, 0xC2, 0xB5, 0x25, 0x22, 0x18, 0x3D,
+ 0xCD, 0x97, 0x8C, 0xCC, 0x78, 0x90, 0xAA, 0x5F,
+ 0x0A, 0x57, 0x05, 0x61, 0xD4, 0xA0, 0x3A, 0xDE,
+ 0x3B, 0xF9, 0x65, 0x68, 0x4F, 0x28, 0xFA, 0xEB,
+ 0x63, 0x2D, 0x8D, 0xD0, 0xA1, 0xFE, 0x12, 0x96,
+ 0x3C, 0x42, 0x29, 0xD6, 0xA4, 0x34, 0xBD, 0x70,
+ 0x89, 0xBE, 0xF5, 0x79, 0xAB, 0x8F, 0x32, 0xB4,
+ 0xEE, 0xE7, 0x2C, 0x04, 0x4B, 0xD5, 0xB1, 0x54,
+ 0xF0, 0xDA, 0x16, 0x77, 0xA6, 0x53, 0xB2, 0xE2,
+ 0x73, 0xBF, 0x17, 0xA8, 0x75, 0x26, 0xE0, 0xBC,
+ 0x0C, 0x71, 0xFB, 0x6D, 0x7E, 0xC5, 0xEA, 0x21,
+ 0x9D, 0x95, 0x8E, 0xA5, 0x48, 0xB8, 0x7D, 0xCB,
+ 0x01, 0x99, 0xE5, 0xBB, 0x82, 0xC4, 0xCA, 0xC1,
+ 0x58, 0x6E, 0x5C, 0x7C, 0xDC, 0x33, 0xB6, 0xC3,
+ 0x09, 0xC7, 0x1F, 0x0D, 0x43, 0x6F, 0xE9, 0x86,
+ 0x27, 0xC8, 0x44, 0xB3, 0xD3, 0xCF, 0x08, 0x66,
+ 0x1B, 0x20, 0x4D, 0xD9, 0xC6, 0x36, 0x40, 0x74,
+ 0x62, 0x6A, 0x55, 0xEC, 0x06, 0x2E, 0xE6, 0x80,
+ 0x13, 0x93, 0x50, 0xCE, 0x69, 0x3E, 0x67, 0x4A,
+ 0x81, 0x4C, 0x0B, 0x3F, 0xB7, 0x0E, 0x39, 0xAE,
+ 0x47, 0x6B, 0x8A, 0xA2, 0x9A, 0xA3, 0x45, 0x41 };
+
+unsigned char table_33[256] = {
+ 0xDE, 0xD3, 0x79, 0x67, 0x13, 0x5C, 0x04, 0xF2,
+ 0xD9, 0x9F, 0x65, 0x56, 0xCC, 0x3B, 0xA4, 0x9A,
+ 0x08, 0xBF, 0x26, 0xB2, 0xA7, 0x5E, 0xAA, 0xCA,
+ 0xBB, 0x2B, 0x38, 0x3F, 0xD8, 0x87, 0xFA, 0x5D,
+ 0x73, 0x8E, 0x1E, 0x93, 0x05, 0xAF, 0x3E, 0x4E,
+ 0x90, 0xDB, 0x0B, 0x33, 0x0D, 0x2F, 0x86, 0x4F,
+ 0xFD, 0xD0, 0x39, 0xB1, 0x8A, 0x1A, 0x20, 0xE6,
+ 0xCF, 0xA2, 0x82, 0xDF, 0x42, 0x9C, 0x30, 0x40,
+ 0xE3, 0xB0, 0x88, 0x5A, 0xEC, 0x25, 0xE2, 0xC4,
+ 0x12, 0x54, 0x50, 0x97, 0x96, 0x21, 0x23, 0x7B,
+ 0x1D, 0x61, 0x52, 0x34, 0x7D, 0x69, 0x16, 0xC3,
+ 0x31, 0xF8, 0x48, 0x19, 0x95, 0x01, 0x29, 0x8C,
+ 0x15, 0xAC, 0x84, 0x74, 0xAB, 0x70, 0xDA, 0x36,
+ 0xD6, 0x8F, 0xFE, 0x35, 0xD7, 0x2E, 0x89, 0x07,
+ 0x62, 0x17, 0xDC, 0x92, 0x45, 0x83, 0xB5, 0xE5,
+ 0x8B, 0xC0, 0x27, 0x85, 0x7C, 0x9D, 0x55, 0x81,
+ 0x71, 0xCD, 0xC9, 0x00, 0x02, 0xC1, 0x0A, 0x37,
+ 0xED, 0xEA, 0xC2, 0x98, 0x49, 0x06, 0x1C, 0x78,
+ 0x64, 0xCE, 0x9E, 0x4C, 0x7A, 0xB4, 0x43, 0x0F,
+ 0xE0, 0x7E, 0xBC, 0x5B, 0x51, 0xE7, 0x18, 0xF9,
+ 0x11, 0xA1, 0xF5, 0xC7, 0xCB, 0x4D, 0x6A, 0x0E,
+ 0x57, 0xF1, 0xFB, 0xB3, 0x99, 0xF0, 0x32, 0xD5,
+ 0xA9, 0x4B, 0x6F, 0x6D, 0xA8, 0xC5, 0xDD, 0x7F,
+ 0xEB, 0xBE, 0xFC, 0x2C, 0x22, 0x58, 0x03, 0x9B,
+ 0x77, 0xF7, 0xBD, 0xBA, 0xD2, 0x6B, 0xAD, 0x5F,
+ 0x10, 0x6E, 0x09, 0xD1, 0x1B, 0x24, 0xEF, 0x72,
+ 0x3D, 0x59, 0x28, 0xE1, 0xB7, 0x44, 0x8D, 0xB8,
+ 0xAE, 0x2D, 0x60, 0xA6, 0xC8, 0x0C, 0xF4, 0x41,
+ 0xA3, 0x68, 0x46, 0x6C, 0x76, 0xA0, 0xB6, 0x66,
+ 0xE4, 0x1F, 0x75, 0x4A, 0xFF, 0x2A, 0x94, 0xD4,
+ 0xF3, 0xE9, 0x91, 0x63, 0xA5, 0xB9, 0xE8, 0x14,
+ 0x80, 0x3C, 0xEE, 0x47, 0xC6, 0x3A, 0x53, 0xF6 };
+
+unsigned char table_34[256] = {
+ 0xF0, 0xE9, 0x3E, 0xD6, 0x89, 0xC8, 0xC7, 0x23,
+ 0x75, 0x26, 0x5F, 0x9C, 0x57, 0xB8, 0x2A, 0x29,
+ 0xE5, 0xB5, 0x68, 0xA4, 0x92, 0x46, 0x40, 0x7F,
+ 0xF2, 0xBC, 0x6A, 0xE0, 0x8F, 0x0F, 0xE4, 0x3A,
+ 0xE1, 0x30, 0x84, 0x6E, 0x82, 0x8E, 0x56, 0xC5,
+ 0x32, 0x85, 0xFB, 0x59, 0x43, 0x41, 0xC2, 0xF6,
+ 0x67, 0x5A, 0x7C, 0x34, 0xA1, 0xD0, 0x4B, 0xAC,
+ 0x61, 0x72, 0x6B, 0xAF, 0xC4, 0x20, 0x9A, 0xD4,
+ 0x74, 0x8D, 0x87, 0x83, 0xE2, 0x62, 0x6D, 0xE6,
+ 0xE7, 0xF9, 0x76, 0xCB, 0x18, 0x90, 0x4F, 0xFF,
+ 0xD3, 0x3C, 0x08, 0x79, 0x93, 0x2D, 0x95, 0xA3,
+ 0xDD, 0x5B, 0xDA, 0x7A, 0x39, 0x4D, 0xC1, 0x2E,
+ 0xCC, 0x53, 0xE8, 0xA2, 0xCF, 0x15, 0x78, 0x1C,
+ 0xEB, 0x9B, 0x7B, 0xAD, 0x31, 0x2F, 0xE3, 0xC9,
+ 0x3B, 0xEC, 0x2C, 0x49, 0x02, 0x52, 0x28, 0xBA,
+ 0x0C, 0x19, 0x24, 0xF7, 0x97, 0x09, 0xA6, 0xA0,
+ 0xDF, 0xD1, 0xD2, 0xDC, 0x51, 0xA5, 0x94, 0xFD,
+ 0x71, 0xF5, 0x50, 0x0A, 0x69, 0x25, 0x88, 0x5C,
+ 0x91, 0xD5, 0x47, 0x0B, 0x27, 0x13, 0x96, 0xD9,
+ 0xF1, 0xA9, 0x70, 0xC3, 0xBE, 0x42, 0x4E, 0x4A,
+ 0xB1, 0x07, 0xA7, 0x54, 0xFE, 0x48, 0x9F, 0x63,
+ 0x17, 0xAE, 0xB9, 0x58, 0x21, 0x35, 0xED, 0x5D,
+ 0x9D, 0x3D, 0xB4, 0xFC, 0xEA, 0x8C, 0x80, 0xA8,
+ 0x1E, 0xB0, 0xDE, 0x0D, 0x11, 0x6F, 0x04, 0x12,
+ 0xF4, 0x10, 0x64, 0x0E, 0xD7, 0x2B, 0xB3, 0x8B,
+ 0xB7, 0x01, 0x86, 0xCA, 0xFA, 0x9E, 0xEE, 0x66,
+ 0x37, 0x65, 0x81, 0x38, 0x1F, 0xAA, 0x73, 0xAB,
+ 0xBD, 0xDB, 0x14, 0xCD, 0x00, 0xBB, 0x98, 0x44,
+ 0x45, 0xB6, 0x99, 0x5E, 0xD8, 0x1D, 0x36, 0xF8,
+ 0x55, 0x6C, 0x16, 0x7E, 0x77, 0x3F, 0x22, 0xEF,
+ 0xF3, 0x7D, 0xC6, 0xCE, 0x8A, 0xB2, 0x33, 0x4C,
+ 0x03, 0x05, 0xBF, 0x06, 0x1B, 0xC0, 0x1A, 0x60 };
+
+unsigned char table_35[256] = {
+ 0xCC, 0x40, 0xEF, 0x1F, 0xDB, 0xE5, 0x71, 0x51,
+ 0x3B, 0x0F, 0x7D, 0x9C, 0x83, 0x17, 0x6F, 0x8F,
+ 0x13, 0xDC, 0x7F, 0xA9, 0xA5, 0xA2, 0x9D, 0xDF,
+ 0xE7, 0x97, 0x2A, 0x30, 0xF2, 0x73, 0xCF, 0x87,
+ 0x29, 0xB3, 0x86, 0x43, 0x09, 0xB0, 0x2E, 0x10,
+ 0x8E, 0xBC, 0x57, 0xBA, 0x68, 0xF5, 0xCB, 0x89,
+ 0x32, 0xC1, 0x6B, 0x1E, 0xAC, 0xB2, 0x2D, 0x6A,
+ 0x50, 0xEB, 0x18, 0x06, 0xD8, 0xC7, 0x36, 0x31,
+ 0xC5, 0xAF, 0x12, 0x15, 0xB7, 0x37, 0x4E, 0x01,
+ 0x14, 0x21, 0x44, 0x5E, 0xF4, 0xB4, 0xE4, 0x65,
+ 0xFE, 0x8A, 0xEA, 0x0D, 0xBB, 0x45, 0x8B, 0x25,
+ 0x80, 0x35, 0x61, 0xA8, 0x4A, 0x47, 0xAB, 0x91,
+ 0x1B, 0x1C, 0x05, 0x4D, 0x5A, 0xD4, 0xF1, 0x9B,
+ 0x0E, 0x98, 0xCA, 0x96, 0x42, 0x7E, 0x03, 0x5F,
+ 0xE2, 0x90, 0xBF, 0x82, 0xC9, 0x3D, 0xE0, 0x5C,
+ 0xFA, 0x3E, 0x41, 0x11, 0x79, 0x58, 0x24, 0x2C,
+ 0xC0, 0x28, 0x5D, 0xA3, 0xDE, 0x67, 0xFF, 0xA4,
+ 0x63, 0xB1, 0x22, 0x04, 0xFD, 0x70, 0x39, 0x46,
+ 0xAA, 0x0A, 0x34, 0x6C, 0xD7, 0x92, 0xA1, 0x3C,
+ 0x19, 0xD5, 0xFC, 0xAD, 0x85, 0x07, 0x00, 0x23,
+ 0xF8, 0x69, 0x56, 0x53, 0x55, 0x7A, 0xB8, 0xC8,
+ 0xDA, 0xCE, 0xF3, 0x5B, 0x49, 0xE1, 0xBE, 0xEC,
+ 0x1A, 0x88, 0x02, 0xBD, 0xF7, 0x1D, 0x64, 0xA0,
+ 0x4F, 0xD9, 0xE3, 0x95, 0xC6, 0x48, 0x2B, 0xED,
+ 0x9A, 0x9E, 0x26, 0x6E, 0xD1, 0x94, 0xB9, 0x93,
+ 0xDD, 0xF6, 0xA6, 0xFB, 0xC2, 0xB6, 0x0C, 0xE9,
+ 0x77, 0xF9, 0xCD, 0x08, 0xEE, 0x3F, 0xE6, 0x75,
+ 0xD6, 0x84, 0x76, 0x8C, 0xF0, 0xAE, 0xD2, 0x78,
+ 0x2F, 0x4B, 0x16, 0x4C, 0x27, 0x81, 0x6D, 0x99,
+ 0x38, 0xD3, 0x54, 0x62, 0x74, 0x20, 0x60, 0xC3,
+ 0x7C, 0x8D, 0x72, 0x0B, 0x52, 0xE8, 0xA7, 0x3A,
+ 0x59, 0xC4, 0x9F, 0xD0, 0x66, 0x7B, 0x33, 0xB5 };
+
+unsigned char table_36[256] = {
+ 0xDB, 0x6F, 0xFE, 0xB3, 0x5C, 0x1F, 0xB8, 0xBF,
+ 0xA3, 0x71, 0x11, 0x56, 0x90, 0xE2, 0x63, 0x18,
+ 0x83, 0x51, 0x21, 0xEB, 0x66, 0x08, 0xA6, 0xA5,
+ 0x1C, 0xF5, 0x14, 0x24, 0x41, 0x33, 0xA7, 0xB5,
+ 0xC7, 0x79, 0x57, 0x50, 0x85, 0xE1, 0x6D, 0xF7,
+ 0x0E, 0xDE, 0x67, 0xAB, 0xA1, 0x0B, 0xD9, 0x4A,
+ 0xCA, 0x36, 0xEA, 0xDA, 0x16, 0xEF, 0x9F, 0x0A,
+ 0x09, 0x9A, 0x1D, 0xC5, 0xD7, 0x5F, 0x19, 0xDC,
+ 0x15, 0x06, 0xE8, 0x94, 0x0C, 0x0D, 0xC9, 0x7C,
+ 0xD6, 0x62, 0xBB, 0x49, 0xF9, 0x61, 0x07, 0x9B,
+ 0x28, 0xC3, 0x9E, 0xF4, 0x38, 0x78, 0x20, 0x03,
+ 0xA2, 0x7F, 0xC2, 0x9D, 0x5E, 0x65, 0x52, 0x17,
+ 0x2E, 0x1B, 0xB0, 0x42, 0xBC, 0xFD, 0xF1, 0xD2,
+ 0xF6, 0x60, 0xD3, 0x29, 0x97, 0x3D, 0x0F, 0xB1,
+ 0x2F, 0x22, 0xDD, 0x80, 0x32, 0xF8, 0xAD, 0x70,
+ 0xB9, 0x8F, 0x37, 0xCE, 0x46, 0x58, 0xB7, 0x30,
+ 0xED, 0x7A, 0xE9, 0xC0, 0x7D, 0x13, 0x64, 0x23,
+ 0x4E, 0xC8, 0xF0, 0xCC, 0x3B, 0x45, 0x68, 0x8D,
+ 0xBE, 0x8B, 0xD8, 0x43, 0x02, 0x27, 0xE4, 0xAA,
+ 0x10, 0xF2, 0x59, 0x72, 0x40, 0x26, 0x69, 0xE5,
+ 0x05, 0x84, 0x4F, 0xE0, 0x6B, 0xC1, 0xAC, 0x4C,
+ 0xFB, 0x31, 0x77, 0x8E, 0xD4, 0x12, 0xA9, 0xB4,
+ 0xEC, 0x00, 0x76, 0x1E, 0x25, 0xAE, 0xE7, 0x3C,
+ 0x35, 0x93, 0x9C, 0xC4, 0xFC, 0x2D, 0x91, 0x04,
+ 0xAF, 0x53, 0x3F, 0xE6, 0xA4, 0xD0, 0x1A, 0xDF,
+ 0x3A, 0x55, 0x99, 0x01, 0xCB, 0x6C, 0x82, 0x3E,
+ 0x5D, 0xA8, 0x88, 0x54, 0x5B, 0x95, 0xCD, 0x8C,
+ 0x81, 0x34, 0xD1, 0x39, 0xFF, 0xEE, 0xFA, 0x8A,
+ 0x6E, 0x86, 0x92, 0x89, 0xF3, 0x6A, 0xBA, 0x2C,
+ 0xD5, 0x44, 0xC6, 0x96, 0xBD, 0xB2, 0x2B, 0x87,
+ 0x74, 0xA0, 0x73, 0x5A, 0x2A, 0x98, 0x75, 0x47,
+ 0x4B, 0xB6, 0x7B, 0x4D, 0xCF, 0x7E, 0x48, 0xE3 };
+
+unsigned char table_37[256] = {
+ 0x1F, 0xD6, 0xB1, 0xB3, 0x40, 0xAD, 0xDE, 0xB7,
+ 0x19, 0xB4, 0xE7, 0x0B, 0x9C, 0x2D, 0xE0, 0xF5,
+ 0xCF, 0x2C, 0x30, 0x65, 0x2F, 0xCD, 0x02, 0x91,
+ 0xCE, 0x2B, 0xBF, 0x78, 0xE6, 0xFA, 0x51, 0x48,
+ 0xFB, 0x4D, 0xBE, 0x71, 0x1A, 0x56, 0xFD, 0x81,
+ 0x33, 0x75, 0x89, 0x96, 0x37, 0x82, 0x9E, 0x93,
+ 0x41, 0x18, 0x5B, 0x2E, 0x22, 0x0F, 0xAF, 0x4B,
+ 0xB9, 0xD5, 0xEE, 0x6C, 0xE4, 0x05, 0xCC, 0x99,
+ 0xE5, 0x3B, 0x62, 0xBD, 0x7B, 0xAA, 0x4A, 0xE2,
+ 0x34, 0x43, 0xF7, 0x39, 0xFE, 0x14, 0x1D, 0xE3,
+ 0xF0, 0xA7, 0x77, 0xDF, 0xA0, 0xD3, 0xAC, 0xD9,
+ 0xEA, 0x76, 0xDD, 0xA4, 0xC5, 0xC9, 0x61, 0xF3,
+ 0xA8, 0xB0, 0x35, 0xE8, 0x68, 0xD4, 0x15, 0xF9,
+ 0x97, 0xED, 0x25, 0x0A, 0x88, 0x8F, 0x06, 0xA3,
+ 0x16, 0x36, 0x32, 0xA2, 0xC6, 0x64, 0xD7, 0x94,
+ 0xD2, 0x6D, 0x74, 0xFC, 0x44, 0x27, 0x5C, 0xFF,
+ 0x60, 0x1E, 0x58, 0x8B, 0x5E, 0xC7, 0x90, 0x17,
+ 0x63, 0xAE, 0xC3, 0x12, 0x13, 0x84, 0xEC, 0x49,
+ 0xA5, 0x9B, 0x31, 0x8D, 0xE1, 0x79, 0xF1, 0x00,
+ 0x28, 0x3D, 0xC2, 0x55, 0x20, 0x52, 0x95, 0x7E,
+ 0x42, 0x1C, 0x66, 0x92, 0x7D, 0xB6, 0xC4, 0xF4,
+ 0x80, 0xB2, 0x72, 0x6E, 0x11, 0xF6, 0x0D, 0x5A,
+ 0xEF, 0x9D, 0x69, 0x9A, 0x45, 0x67, 0x3F, 0xDA,
+ 0x8E, 0x57, 0x09, 0x7C, 0x38, 0xA6, 0x83, 0x87,
+ 0x7A, 0x08, 0x4C, 0x5F, 0x85, 0x7F, 0xD0, 0x04,
+ 0x50, 0xCB, 0xB8, 0x07, 0x24, 0x26, 0x29, 0x46,
+ 0x01, 0x03, 0xC1, 0xD8, 0xDC, 0x0E, 0x3C, 0x4F,
+ 0x53, 0x4E, 0xB5, 0xF8, 0xC0, 0x8A, 0xF2, 0xBB,
+ 0xE9, 0x5D, 0x2A, 0xBA, 0x0C, 0x1B, 0x3A, 0xA9,
+ 0x21, 0x6A, 0x70, 0xBC, 0xEB, 0xA1, 0x54, 0x10,
+ 0x98, 0x9F, 0x23, 0xD1, 0x6B, 0x59, 0x3E, 0xCA,
+ 0x73, 0xC8, 0x86, 0x47, 0xDB, 0xAB, 0x6F, 0x8C };
+
+unsigned char table_38[256] = {
+ 0xAA, 0x8D, 0x37, 0x94, 0x99, 0xDD, 0x70, 0x77,
+ 0x78, 0xC9, 0x0F, 0xFA, 0xE2, 0x05, 0xC2, 0x16,
+ 0x02, 0x4D, 0x44, 0x65, 0xAC, 0xB0, 0x39, 0xF8,
+ 0x06, 0x60, 0xD8, 0xE1, 0x19, 0xB4, 0x36, 0x20,
+ 0x59, 0x1D, 0xAD, 0xE4, 0xE8, 0xFF, 0x9D, 0x0D,
+ 0x51, 0x28, 0xE7, 0x8C, 0x0E, 0x97, 0xE3, 0xAE,
+ 0x6A, 0x27, 0x98, 0xDB, 0x26, 0xF6, 0xEC, 0xC6,
+ 0xC0, 0xBD, 0x68, 0x61, 0x83, 0x86, 0xE0, 0x2C,
+ 0xEE, 0x47, 0xF9, 0x5F, 0x6D, 0xBA, 0xE9, 0x72,
+ 0x8A, 0xBB, 0x08, 0x29, 0xAF, 0x1C, 0xD3, 0x5D,
+ 0xF7, 0x87, 0x6F, 0x9A, 0x2F, 0x11, 0xD9, 0x90,
+ 0x66, 0x8E, 0xEB, 0xB1, 0x2E, 0xEA, 0xA3, 0x55,
+ 0x2B, 0xCC, 0x4C, 0x4B, 0x48, 0x71, 0x3B, 0xFC,
+ 0xA4, 0x45, 0x0A, 0x8F, 0x7A, 0x13, 0x01, 0x22,
+ 0xC1, 0xF1, 0xA2, 0xB8, 0x7C, 0xF4, 0xB3, 0xB7,
+ 0x5B, 0xE5, 0x07, 0x50, 0x7E, 0x18, 0xEF, 0x91,
+ 0x5C, 0x15, 0x69, 0xBE, 0x0C, 0x93, 0x56, 0x35,
+ 0x7B, 0xCF, 0x34, 0x74, 0x3E, 0x5E, 0x31, 0x21,
+ 0x12, 0x63, 0x7F, 0x2A, 0x9B, 0xD4, 0x6B, 0xBC,
+ 0x33, 0x62, 0x30, 0x75, 0x17, 0x23, 0xB2, 0xF0,
+ 0x57, 0x67, 0x95, 0x3D, 0xCD, 0x10, 0xE6, 0xC8,
+ 0x8B, 0xA9, 0x73, 0xC4, 0x43, 0xBF, 0xA7, 0xCA,
+ 0xB5, 0xD5, 0xD6, 0x3F, 0x1A, 0x7D, 0x82, 0xA8,
+ 0x40, 0x64, 0xAB, 0x04, 0xC3, 0x1F, 0xA0, 0x5A,
+ 0x85, 0xF3, 0xDE, 0xFE, 0xDA, 0x1E, 0x81, 0x92,
+ 0x9C, 0x2D, 0x9F, 0x32, 0xB9, 0xA1, 0x96, 0xD0,
+ 0x4F, 0x38, 0x80, 0xCB, 0x6C, 0x14, 0x84, 0x1B,
+ 0xD7, 0xC5, 0xED, 0xD2, 0x3A, 0x0B, 0x88, 0xFD,
+ 0xDC, 0x49, 0x9E, 0xF5, 0xF2, 0x52, 0xA6, 0x24,
+ 0xC7, 0xB6, 0x03, 0x3C, 0xD1, 0x54, 0x41, 0xDF,
+ 0x89, 0x58, 0x79, 0xFB, 0x6E, 0xA5, 0x42, 0x25,
+ 0x09, 0x76, 0x00, 0x46, 0x4E, 0x53, 0xCE, 0x4A };
+
+unsigned char table_39[32] = {
+ 0x12, 0x18, 0x0E, 0x08, 0x16, 0x05, 0x06, 0x00,
+ 0x11, 0x17, 0x15, 0x1B, 0x14, 0x01, 0x1F, 0x19,
+ 0x04, 0x0D, 0x0A, 0x0F, 0x10, 0x07, 0x1D, 0x03,
+ 0x0B, 0x13, 0x0C, 0x09, 0x1E, 0x02, 0x1A, 0x1C };
+
+unsigned char table_40[32] = {
+ 0x16, 0x02, 0x06, 0x0E, 0x0D, 0x1C, 0x08, 0x0A,
+ 0x0F, 0x13, 0x0B, 0x18, 0x07, 0x04, 0x14, 0x01,
+ 0x1B, 0x05, 0x17, 0x1E, 0x11, 0x1A, 0x10, 0x1F,
+ 0x12, 0x19, 0x1D, 0x03, 0x0C, 0x00, 0x09, 0x15 };
+
+unsigned char table_41[32] = {
+ 0x13, 0x18, 0x04, 0x1F, 0x1D, 0x11, 0x03, 0x00,
+ 0x10, 0x12, 0x06, 0x0A, 0x1C, 0x07, 0x15, 0x0E,
+ 0x08, 0x05, 0x0C, 0x09, 0x01, 0x02, 0x16, 0x0B,
+ 0x1A, 0x17, 0x14, 0x1E, 0x0D, 0x0F, 0x19, 0x1B };
+
+unsigned char table_42[32] = {
+ 0x00, 0x08, 0x15, 0x1D, 0x05, 0x18, 0x06, 0x07,
+ 0x1F, 0x01, 0x0B, 0x03, 0x19, 0x13, 0x02, 0x1C,
+ 0x17, 0x11, 0x0E, 0x1E, 0x0C, 0x0F, 0x09, 0x1A,
+ 0x1B, 0x16, 0x10, 0x0D, 0x0A, 0x14, 0x12, 0x04 };
+
+unsigned char table_43[256] = {
+ 0x34, 0xB7, 0x36, 0x85, 0x5F, 0x93, 0x98, 0x70,
+ 0x1E, 0x59, 0x83, 0x60, 0x6F, 0xBF, 0xF9, 0xD0,
+ 0xB3, 0x22, 0x12, 0x38, 0xF5, 0x01, 0xC9, 0x5B,
+ 0xEF, 0x1D, 0x81, 0x64, 0xFA, 0x8F, 0x7F, 0xBC,
+ 0x05, 0x08, 0xE0, 0x8B, 0xE8, 0x86, 0x95, 0xCB,
+ 0xCA, 0x5A, 0xEB, 0x10, 0x92, 0xE2, 0x7E, 0x28,
+ 0xD9, 0xC7, 0x0D, 0x24, 0xA7, 0x02, 0x0B, 0xF1,
+ 0x7B, 0xD3, 0xFE, 0x2B, 0x89, 0x0E, 0xAE, 0xAD,
+ 0xC8, 0x82, 0x79, 0x43, 0x96, 0xDE, 0x0C, 0x9A,
+ 0x57, 0x84, 0xB4, 0x19, 0xF8, 0xF0, 0xAF, 0xBE,
+ 0x99, 0x9F, 0x46, 0xE4, 0x31, 0xDF, 0x30, 0x51,
+ 0xD4, 0xE5, 0xFC, 0x32, 0x04, 0x56, 0x7D, 0x33,
+ 0xF7, 0x18, 0x23, 0x4E, 0xC2, 0x7C, 0x6C, 0xD2,
+ 0xB1, 0x9B, 0x40, 0xA2, 0x88, 0x00, 0xA1, 0xAB,
+ 0xC6, 0x5C, 0x87, 0x3B, 0xD7, 0x27, 0x2E, 0x45,
+ 0xDA, 0x8E, 0x61, 0x5E, 0xFB, 0x09, 0x5D, 0x6B,
+ 0xA3, 0x29, 0x4F, 0xAC, 0xD1, 0x77, 0x4A, 0xA9,
+ 0xC4, 0x7A, 0x15, 0xD8, 0xAA, 0x17, 0xB9, 0x2D,
+ 0xE7, 0xBD, 0x2C, 0x62, 0x2F, 0xB2, 0xED, 0x3F,
+ 0x48, 0x26, 0x1B, 0x35, 0x20, 0x72, 0x4D, 0xFF,
+ 0xBB, 0x78, 0x1F, 0xCC, 0xEC, 0xA8, 0x9D, 0x90,
+ 0x4B, 0x13, 0xE1, 0xBA, 0xF3, 0x3C, 0x42, 0x65,
+ 0x14, 0xDD, 0x75, 0xE3, 0x4C, 0x74, 0x94, 0xCD,
+ 0xF2, 0x66, 0x06, 0xE9, 0x49, 0xB8, 0x71, 0x41,
+ 0xA0, 0x25, 0x55, 0x47, 0x97, 0x9E, 0x11, 0x54,
+ 0x1A, 0xB0, 0x3E, 0x37, 0x39, 0x1C, 0x8D, 0x03,
+ 0x6E, 0xF6, 0x80, 0x6D, 0x8C, 0x9C, 0xB6, 0xCF,
+ 0xC3, 0x91, 0x63, 0xC0, 0x07, 0x67, 0xE6, 0xF4,
+ 0xCE, 0x3D, 0xDB, 0x16, 0xFD, 0xEA, 0xD6, 0x68,
+ 0xD5, 0xA6, 0x0F, 0x58, 0x44, 0x52, 0xB5, 0xDC,
+ 0x0A, 0x69, 0xC5, 0xA5, 0xC1, 0x8A, 0x2A, 0xEE,
+ 0x73, 0x76, 0x3A, 0x21, 0x53, 0xA4, 0x50, 0x6A };
+
+unsigned char table_44[32] = {
+ 0x1A, 0x0E, 0x0A, 0x17, 0x1F, 0x08, 0x10, 0x14,
+ 0x0C, 0x0F, 0x09, 0x1C, 0x06, 0x18, 0x1E, 0x12,
+ 0x15, 0x00, 0x11, 0x13, 0x0D, 0x01, 0x0B, 0x03,
+ 0x16, 0x19, 0x05, 0x1D, 0x02, 0x07, 0x04, 0x1B };
+
+unsigned char table_45[256] = {
+ 0x5E, 0xD6, 0xE2, 0x54, 0x35, 0xC2, 0xAC, 0x9D,
+ 0x92, 0x64, 0x57, 0x65, 0xC8, 0xAE, 0x21, 0xA9,
+ 0x89, 0x48, 0x12, 0x59, 0xEC, 0xEF, 0x9F, 0xF7,
+ 0x19, 0x03, 0x83, 0xC0, 0x79, 0x5D, 0x4A, 0x10,
+ 0x8C, 0xEB, 0xFF, 0xB5, 0x3B, 0x51, 0x2D, 0xD1,
+ 0x6B, 0xC5, 0x24, 0x5C, 0xE6, 0x11, 0x94, 0x3F,
+ 0xD0, 0x2F, 0x0E, 0x95, 0x3C, 0xFE, 0x5B, 0x20,
+ 0x23, 0xE0, 0x91, 0x6F, 0xCA, 0x56, 0x0C, 0x73,
+ 0xDA, 0x67, 0x37, 0xA3, 0xA5, 0x70, 0x93, 0x1C,
+ 0x18, 0xD9, 0x42, 0x5F, 0x44, 0xF0, 0xF2, 0x14,
+ 0x58, 0x8A, 0x1D, 0x40, 0x4E, 0x0B, 0x74, 0x84,
+ 0x52, 0xCB, 0x60, 0xED, 0xAD, 0x66, 0x43, 0x6C,
+ 0x81, 0xA1, 0x27, 0xB9, 0xBA, 0x4D, 0xF5, 0x04,
+ 0xB8, 0x96, 0xA6, 0xA2, 0x7D, 0xD4, 0xEA, 0x45,
+ 0x4F, 0x55, 0xD3, 0x3E, 0x8E, 0x4C, 0xBF, 0x8B,
+ 0x9A, 0x06, 0x7A, 0xF4, 0x02, 0x88, 0x80, 0x22,
+ 0xF3, 0xBD, 0x78, 0xEE, 0xAF, 0xF8, 0x15, 0x09,
+ 0x0F, 0xB0, 0xDD, 0x99, 0x72, 0xE7, 0x90, 0xE1,
+ 0x25, 0x62, 0x8D, 0x9C, 0x13, 0x08, 0xC9, 0x28,
+ 0x2A, 0x47, 0x69, 0xDE, 0x77, 0x87, 0xBB, 0xE9,
+ 0xAA, 0x33, 0x05, 0x29, 0x34, 0x97, 0xFD, 0xA0,
+ 0x1E, 0xFC, 0xBE, 0xB1, 0x71, 0x9B, 0x50, 0xDC,
+ 0xB7, 0x31, 0x63, 0x3A, 0xDF, 0xC3, 0x1B, 0x7C,
+ 0x0A, 0xD7, 0xF6, 0xDB, 0x49, 0x53, 0x7F, 0xD2,
+ 0x30, 0xA4, 0xB3, 0x6E, 0xB2, 0x6D, 0xCD, 0x7E,
+ 0x26, 0xE8, 0x76, 0xCF, 0xE5, 0xCE, 0x16, 0xF1,
+ 0xC6, 0x68, 0x36, 0x46, 0x1F, 0x38, 0x0D, 0x41,
+ 0x17, 0xBC, 0x86, 0x9E, 0x6A, 0x7B, 0xB4, 0x01,
+ 0xCC, 0x2C, 0xE3, 0x5A, 0xB6, 0xFA, 0x00, 0x75,
+ 0x39, 0xA7, 0xC1, 0xD5, 0x98, 0xAB, 0x1A, 0x85,
+ 0xD8, 0xE4, 0xC4, 0xA8, 0x4B, 0x61, 0x2E, 0x3D,
+ 0xF9, 0x2B, 0x32, 0x8F, 0xFB, 0xC7, 0x07, 0x82 };
+
+unsigned char table_46[256] = {
+ 0x85, 0x78, 0xFE, 0x6C, 0x61, 0xA0, 0x71, 0xCC,
+ 0x45, 0x54, 0x7A, 0xE6, 0x82, 0x1D, 0xA6, 0x02,
+ 0x47, 0xD0, 0x23, 0x55, 0x62, 0xFA, 0x76, 0x3E,
+ 0xE3, 0x66, 0x74, 0x10, 0x5D, 0x49, 0x69, 0x0B,
+ 0x75, 0x12, 0x8D, 0x9F, 0xEE, 0x93, 0x50, 0x70,
+ 0x32, 0xBC, 0x1E, 0xD3, 0xEF, 0x7B, 0xB4, 0x92,
+ 0xFD, 0x16, 0xC2, 0xD8, 0xDE, 0x68, 0xD1, 0x64,
+ 0xC3, 0xA3, 0xB3, 0xC9, 0x08, 0xFB, 0x84, 0xC1,
+ 0x28, 0x53, 0xCF, 0xD2, 0x35, 0xD7, 0x4A, 0x01,
+ 0x44, 0xA4, 0x07, 0xAC, 0x98, 0xF1, 0xB2, 0x9A,
+ 0x94, 0x2D, 0xD4, 0x34, 0x27, 0x60, 0x1A, 0xB9,
+ 0xAF, 0x89, 0xEB, 0x8F, 0x6A, 0x13, 0x05, 0xF0,
+ 0x77, 0x5F, 0x4F, 0x58, 0x2C, 0xE7, 0xCE, 0xED,
+ 0xC0, 0x0D, 0x3A, 0xA7, 0xE2, 0x38, 0x5B, 0xE9,
+ 0x3D, 0xF2, 0xDF, 0x86, 0xE0, 0x72, 0xF7, 0x88,
+ 0xAD, 0xB7, 0x11, 0xDB, 0x73, 0x87, 0xC5, 0x22,
+ 0xE1, 0x5C, 0xD6, 0x57, 0x7E, 0x7D, 0xA2, 0xF9,
+ 0xF5, 0x9C, 0x25, 0x6F, 0x26, 0x51, 0xC8, 0x80,
+ 0x2B, 0xA8, 0x19, 0xD9, 0x65, 0xCD, 0x97, 0xEA,
+ 0xFF, 0x5E, 0x24, 0x3B, 0x4D, 0xB1, 0x1C, 0x79,
+ 0x39, 0x6B, 0xA5, 0x2A, 0x09, 0xCA, 0x04, 0xEC,
+ 0xBA, 0x18, 0x31, 0x46, 0x20, 0xBE, 0x1F, 0x3C,
+ 0x6D, 0xAA, 0xF6, 0xDD, 0xF4, 0x96, 0x03, 0x0A,
+ 0x9E, 0x83, 0xA1, 0x9D, 0xD5, 0xB0, 0x17, 0xBF,
+ 0x56, 0xAB, 0xAE, 0x1B, 0x52, 0xC6, 0x81, 0x4B,
+ 0xDC, 0x90, 0x5A, 0x9B, 0xB6, 0x0F, 0xF3, 0x67,
+ 0x30, 0x63, 0x7C, 0x40, 0x0E, 0x7F, 0x95, 0x36,
+ 0xC4, 0x4E, 0x43, 0xCB, 0x15, 0xB8, 0x00, 0x91,
+ 0x8A, 0x4C, 0x8E, 0x14, 0x06, 0x6E, 0xA9, 0x2E,
+ 0x3F, 0x48, 0x2F, 0x0C, 0xB5, 0x21, 0xBB, 0xDA,
+ 0x8B, 0x42, 0x29, 0x8C, 0x33, 0x59, 0xE8, 0xF8,
+ 0xC7, 0xE4, 0x37, 0xE5, 0xFC, 0xBD, 0x99, 0x41 };
+
+unsigned char table_47[32] = {
+ 0x18, 0x1D, 0x16, 0x10, 0x11, 0x04, 0x1E, 0x08,
+ 0x19, 0x0E, 0x0F, 0x02, 0x14, 0x1C, 0x07, 0x17,
+ 0x0D, 0x09, 0x12, 0x1A, 0x05, 0x01, 0x0B, 0x0A,
+ 0x13, 0x15, 0x0C, 0x00, 0x06, 0x1F, 0x03, 0x1B };
+
+unsigned char table_48[32] = {
+ 0x13, 0x08, 0x15, 0x01, 0x17, 0x10, 0x0F, 0x1F,
+ 0x1D, 0x0D, 0x12, 0x03, 0x06, 0x0A, 0x1C, 0x19,
+ 0x1A, 0x04, 0x1B, 0x02, 0x16, 0x1E, 0x11, 0x00,
+ 0x14, 0x09, 0x0C, 0x18, 0x05, 0x07, 0x0E, 0x0B };
+
+unsigned char table_49[32] = {
+ 0x1F, 0x0F, 0x19, 0x07, 0x18, 0x05, 0x1E, 0x1D,
+ 0x15, 0x08, 0x17, 0x10, 0x0A, 0x0E, 0x0C, 0x1B,
+ 0x02, 0x13, 0x03, 0x0D, 0x04, 0x1A, 0x06, 0x09,
+ 0x12, 0x1C, 0x0B, 0x16, 0x14, 0x01, 0x11, 0x00 };
+
+unsigned char table_50[32] = {
+ 0x16, 0x18, 0x1C, 0x0E, 0x12, 0x00, 0x04, 0x1B,
+ 0x1F, 0x13, 0x17, 0x0A, 0x1E, 0x03, 0x0C, 0x01,
+ 0x0F, 0x10, 0x02, 0x08, 0x14, 0x09, 0x19, 0x15,
+ 0x06, 0x0D, 0x0B, 0x1D, 0x05, 0x07, 0x11, 0x1A };
+
+unsigned char table_51[32] = {
+ 0x1C, 0x0D, 0x1B, 0x07, 0x17, 0x0E, 0x06, 0x01,
+ 0x12, 0x19, 0x03, 0x0B, 0x10, 0x08, 0x00, 0x1E,
+ 0x0A, 0x04, 0x1A, 0x1D, 0x0C, 0x18, 0x02, 0x13,
+ 0x0F, 0x11, 0x05, 0x09, 0x15, 0x16, 0x1F, 0x14 };
+
+unsigned char table_52[256] = {
+ 0x34, 0x0B, 0x47, 0xA3, 0x56, 0x30, 0x73, 0xD4,
+ 0x4B, 0xF6, 0xA6, 0x80, 0x22, 0x95, 0xA5, 0xBB,
+ 0xFE, 0xCD, 0x27, 0x88, 0x87, 0x18, 0x86, 0x6E,
+ 0xB9, 0x07, 0x37, 0x52, 0x0A, 0x28, 0x2C, 0xC4,
+ 0x75, 0xA1, 0x29, 0x54, 0x84, 0x08, 0x72, 0x51,
+ 0xDD, 0xF1, 0x4E, 0x1A, 0x90, 0x57, 0x20, 0xAD,
+ 0x68, 0x61, 0xAF, 0x50, 0x6B, 0x1B, 0x71, 0xEB,
+ 0x63, 0xC9, 0xB0, 0x58, 0x26, 0x40, 0xC7, 0xD9,
+ 0x70, 0xA2, 0x9A, 0x09, 0x3F, 0x92, 0x0D, 0x8C,
+ 0xC1, 0x96, 0x9F, 0x77, 0x4D, 0x5A, 0xEA, 0x11,
+ 0xD7, 0xF3, 0x33, 0x93, 0x10, 0xF2, 0x9D, 0x83,
+ 0xFF, 0x7E, 0xD2, 0x41, 0x24, 0xB4, 0x8D, 0x5C,
+ 0xCF, 0xEF, 0xE9, 0x64, 0x76, 0xD1, 0xDE, 0xE4,
+ 0x91, 0x35, 0x89, 0x19, 0x02, 0x0E, 0xF4, 0x2A,
+ 0x0F, 0xE1, 0xA8, 0x2D, 0x21, 0x23, 0xAA, 0x7C,
+ 0x78, 0x45, 0xA9, 0xDC, 0x06, 0xF9, 0xDF, 0xF7,
+ 0x03, 0xAB, 0xB5, 0x1C, 0x36, 0x7B, 0x97, 0xFA,
+ 0xE5, 0x3B, 0x2F, 0x1F, 0x9E, 0xED, 0xA7, 0x55,
+ 0x42, 0x6F, 0x1E, 0xB7, 0xE6, 0xFB, 0x12, 0xD5,
+ 0x99, 0xC6, 0x66, 0x4A, 0xE8, 0x48, 0x60, 0xB1,
+ 0x05, 0x53, 0x8A, 0xB6, 0x25, 0x8F, 0xA4, 0xD8,
+ 0x9C, 0xC0, 0x59, 0x3A, 0xBD, 0xDB, 0x44, 0x5E,
+ 0xE3, 0xDA, 0x1D, 0x32, 0xF5, 0xBA, 0x43, 0x13,
+ 0x82, 0x4C, 0xE7, 0x17, 0x15, 0x3E, 0x69, 0x2E,
+ 0xC3, 0xF0, 0x5F, 0xFD, 0xCE, 0xD3, 0xCA, 0x39,
+ 0xD6, 0x79, 0x3D, 0xC8, 0x67, 0x8B, 0x31, 0x4F,
+ 0xB3, 0xBC, 0x65, 0x00, 0x7A, 0x98, 0xC5, 0x6C,
+ 0x2B, 0x94, 0x6D, 0x74, 0x14, 0xAC, 0xCC, 0xA0,
+ 0x5B, 0xF8, 0xCB, 0x7F, 0xB2, 0xEC, 0xBF, 0x3C,
+ 0xE0, 0xAE, 0xFC, 0x62, 0x04, 0x8E, 0x85, 0x49,
+ 0x9B, 0xC2, 0x38, 0xD0, 0xEE, 0x81, 0x46, 0xE2,
+ 0x01, 0x0C, 0x5D, 0x7D, 0xB8, 0xBE, 0x6A, 0x16 };
+
+unsigned char table_53[256] = {
+ 0xE3, 0xF4, 0x8D, 0x72, 0x45, 0x32, 0x9D, 0xCE,
+ 0x1F, 0x6B, 0xBC, 0xDC, 0xF1, 0xEC, 0x5A, 0x3B,
+ 0xA5, 0xA2, 0x2B, 0xDD, 0x8A, 0xA3, 0x76, 0xE4,
+ 0xAF, 0xE9, 0xE1, 0x21, 0xDB, 0x9F, 0x19, 0xD3,
+ 0x26, 0x80, 0x15, 0xC2, 0x46, 0xB8, 0x17, 0x56,
+ 0x99, 0x81, 0x08, 0xD7, 0xEF, 0x8E, 0x04, 0x05,
+ 0x97, 0x2F, 0x78, 0xAD, 0xA1, 0x52, 0x36, 0x58,
+ 0x53, 0x68, 0x22, 0x70, 0x0B, 0x79, 0xE6, 0xFA,
+ 0xC3, 0x91, 0xE2, 0xF7, 0xF6, 0x75, 0x2D, 0x0A,
+ 0x90, 0xEB, 0xA6, 0x35, 0xA7, 0x10, 0xB5, 0xFB,
+ 0xE7, 0xAA, 0x1E, 0x43, 0xBB, 0x3C, 0x65, 0x25,
+ 0x2C, 0x59, 0x62, 0x2A, 0xF9, 0x4B, 0x95, 0x5E,
+ 0x20, 0x11, 0x42, 0x27, 0x44, 0xE8, 0x14, 0x6F,
+ 0xD1, 0xD8, 0x00, 0x3A, 0x5B, 0x18, 0x89, 0x02,
+ 0x61, 0xD6, 0xC5, 0x98, 0xD0, 0x5F, 0x34, 0x29,
+ 0xFD, 0x31, 0x1A, 0xCD, 0x0F, 0x9E, 0xCA, 0x7B,
+ 0xEA, 0x93, 0x71, 0x5C, 0x0E, 0x57, 0x33, 0xC4,
+ 0x37, 0xF5, 0x83, 0xB0, 0xDF, 0x49, 0x74, 0x54,
+ 0x1D, 0x24, 0xB9, 0x16, 0x1C, 0x28, 0xDE, 0x4A,
+ 0xF0, 0x01, 0x86, 0x82, 0xCC, 0x12, 0x8C, 0x06,
+ 0x30, 0xA8, 0x7A, 0x73, 0x66, 0x7C, 0xC6, 0xB6,
+ 0xF2, 0x13, 0xBF, 0x40, 0x85, 0x77, 0x09, 0x3D,
+ 0x67, 0x63, 0x3F, 0x7F, 0xF3, 0x87, 0x8F, 0xFF,
+ 0x92, 0xC7, 0x4C, 0x23, 0xBA, 0xCB, 0xB1, 0xED,
+ 0x0C, 0x60, 0x47, 0xFE, 0x38, 0x5D, 0xCF, 0x8B,
+ 0x4D, 0xA9, 0x2E, 0xE5, 0xA4, 0x1B, 0x88, 0x3E,
+ 0x7D, 0xF8, 0xC0, 0xD5, 0x6D, 0x6C, 0x48, 0xAC,
+ 0x9B, 0x51, 0x7E, 0x6E, 0x50, 0x0D, 0x9A, 0xB3,
+ 0xEE, 0x07, 0x4F, 0x69, 0x9C, 0x03, 0xD9, 0xD4,
+ 0xB4, 0xD2, 0xAE, 0x4E, 0x55, 0xB7, 0xC9, 0x41,
+ 0x39, 0x6A, 0xC8, 0xA0, 0xB2, 0xC1, 0x84, 0xFC,
+ 0xAB, 0x64, 0xE0, 0xBE, 0xDA, 0xBD, 0x96, 0x94 };
+
+unsigned char table_54[32] = {
+ 0x01, 0x02, 0x1D, 0x10, 0x0E, 0x11, 0x08, 0x14,
+ 0x12, 0x09, 0x15, 0x17, 0x16, 0x04, 0x06, 0x1B,
+ 0x07, 0x1A, 0x18, 0x13, 0x0A, 0x1E, 0x1C, 0x1F,
+ 0x0C, 0x0B, 0x0D, 0x05, 0x0F, 0x00, 0x19, 0x03 };
+
+unsigned char table_55[32] = {
+ 0x01, 0x12, 0x13, 0x09, 0x0B, 0x19, 0x03, 0x0E,
+ 0x02, 0x1F, 0x1D, 0x1B, 0x1E, 0x11, 0x06, 0x05,
+ 0x00, 0x16, 0x07, 0x0C, 0x15, 0x0D, 0x1A, 0x08,
+ 0x18, 0x10, 0x0F, 0x17, 0x1C, 0x0A, 0x04, 0x14 };
+
+unsigned char table_56[256] = {
+ 0xEF, 0x06, 0x5F, 0x11, 0x4B, 0x60, 0x13, 0xBB,
+ 0x79, 0xD7, 0xE4, 0x6D, 0x22, 0xB4, 0x15, 0x50,
+ 0x29, 0x17, 0xD2, 0xE3, 0x37, 0x8C, 0x46, 0x7C,
+ 0xA2, 0xF5, 0x65, 0x16, 0xCB, 0x04, 0x3E, 0xDF,
+ 0x8E, 0xDE, 0x53, 0xF1, 0xF4, 0xD1, 0x3B, 0xEE,
+ 0x9A, 0x09, 0x9B, 0x6C, 0xF6, 0xCC, 0xFB, 0x40,
+ 0xE0, 0xFD, 0x2B, 0x1D, 0x73, 0x18, 0xCD, 0x31,
+ 0x3F, 0x9E, 0xAD, 0xC9, 0x43, 0x4E, 0x99, 0x3A,
+ 0x8F, 0x92, 0x85, 0xFC, 0x12, 0x41, 0x20, 0xE8,
+ 0x2A, 0xC0, 0x1C, 0x38, 0x74, 0x0B, 0xF3, 0x05,
+ 0x0D, 0x1F, 0x94, 0x9C, 0xAC, 0x00, 0x59, 0x0C,
+ 0xB3, 0x8D, 0xA8, 0x75, 0xB7, 0x68, 0x2F, 0x27,
+ 0x6F, 0x69, 0x76, 0xD8, 0xEC, 0xA5, 0xB2, 0x6A,
+ 0x19, 0x72, 0x1A, 0xB6, 0xE5, 0x77, 0xC6, 0x44,
+ 0x9D, 0xCA, 0x82, 0x35, 0x36, 0x5E, 0xA9, 0x25,
+ 0xFA, 0x5C, 0x24, 0x30, 0x39, 0x0E, 0x2C, 0x7D,
+ 0xE6, 0x88, 0xA0, 0x63, 0xB8, 0x6B, 0x01, 0xDD,
+ 0xDA, 0x9F, 0x45, 0x83, 0xE2, 0x7F, 0x1B, 0x56,
+ 0xAF, 0x14, 0xC3, 0x49, 0xBF, 0x78, 0x70, 0x58,
+ 0x23, 0xA3, 0xBD, 0x34, 0x47, 0x2D, 0x0A, 0xD4,
+ 0x33, 0x03, 0x1E, 0xC1, 0x87, 0xAE, 0x3C, 0x95,
+ 0xB0, 0x42, 0x91, 0xB9, 0x5A, 0x61, 0xAA, 0xCF,
+ 0xF2, 0x51, 0xA6, 0xF8, 0xDC, 0x71, 0xAB, 0x48,
+ 0x66, 0x90, 0x97, 0xC4, 0x08, 0xF9, 0xD0, 0x7B,
+ 0xDB, 0xBA, 0x8B, 0xC2, 0xC5, 0x2E, 0xF7, 0x5B,
+ 0xFF, 0x21, 0x81, 0x54, 0xD3, 0x62, 0x57, 0x4C,
+ 0x6E, 0x02, 0x98, 0xFE, 0x7E, 0xE7, 0xBC, 0x07,
+ 0x28, 0x5D, 0x86, 0xCE, 0xEA, 0x84, 0xF0, 0xE1,
+ 0x93, 0x80, 0xE9, 0xC7, 0x4A, 0xED, 0xB1, 0x26,
+ 0x89, 0x3D, 0x4F, 0xA7, 0xA1, 0xD6, 0xB5, 0x4D,
+ 0x67, 0xA4, 0x55, 0x10, 0x0F, 0xD9, 0x52, 0x32,
+ 0x96, 0xD5, 0xEB, 0x64, 0x8A, 0xC8, 0x7A, 0xBE };
+
+unsigned char table_57[256] = {
+ 0xD1, 0x9B, 0x15, 0x06, 0xB4, 0xF6, 0x97, 0xF0,
+ 0xC6, 0x5B, 0x88, 0x12, 0x25, 0xFA, 0x7B, 0x79,
+ 0xD6, 0xAB, 0xDC, 0x47, 0x85, 0x61, 0x67, 0x0B,
+ 0xF3, 0x20, 0x44, 0x53, 0x2A, 0x3B, 0x2D, 0xE8,
+ 0x17, 0x71, 0xC3, 0xB7, 0x7F, 0x35, 0xEB, 0x10,
+ 0x03, 0x0D, 0x60, 0x96, 0x27, 0xBB, 0x39, 0x50,
+ 0x95, 0x55, 0xCC, 0xD4, 0x2F, 0x51, 0xB3, 0x05,
+ 0xA5, 0xAD, 0xBC, 0x18, 0xE2, 0xAE, 0x07, 0x87,
+ 0xC4, 0x8D, 0xBE, 0x77, 0xC2, 0x16, 0xFC, 0x33,
+ 0x4C, 0x4F, 0xE6, 0xA6, 0x57, 0x9F, 0x37, 0x91,
+ 0xED, 0x4A, 0xF7, 0xB5, 0x52, 0x7C, 0xBD, 0x30,
+ 0xA0, 0x2C, 0x8C, 0xB0, 0x0C, 0xDA, 0x6F, 0x9E,
+ 0xEE, 0x43, 0x40, 0x8F, 0x8B, 0x76, 0xA4, 0x68,
+ 0xFF, 0x6D, 0x58, 0xC9, 0xF9, 0x6E, 0x3F, 0x56,
+ 0xCA, 0x49, 0xC8, 0x5D, 0xCD, 0xC7, 0x99, 0xEC,
+ 0x72, 0x38, 0x0A, 0xA9, 0xC5, 0x04, 0x64, 0xBF,
+ 0xB6, 0x29, 0x80, 0x2E, 0x19, 0x0E, 0x82, 0x45,
+ 0xBA, 0xD7, 0x1E, 0x86, 0xA8, 0xD8, 0x24, 0xDB,
+ 0xCF, 0xE1, 0x54, 0xB2, 0x3E, 0x4D, 0x90, 0x42,
+ 0x5F, 0x59, 0x0F, 0xCE, 0x8E, 0xA2, 0xA7, 0x1D,
+ 0x22, 0xFD, 0x81, 0x63, 0xE5, 0x6A, 0xE7, 0x93,
+ 0x41, 0x46, 0x66, 0x89, 0x13, 0xEA, 0x69, 0x1C,
+ 0x83, 0xF2, 0x08, 0xB8, 0x01, 0x23, 0x26, 0xFB,
+ 0x78, 0xAA, 0x31, 0x11, 0x1B, 0x98, 0xDD, 0xAC,
+ 0xB9, 0xFE, 0x94, 0x74, 0xAF, 0x32, 0xD0, 0x5A,
+ 0xA1, 0xF4, 0x6B, 0x8A, 0xE3, 0x65, 0xDE, 0xCB,
+ 0x73, 0x3D, 0xA3, 0x7E, 0xDF, 0xD2, 0x6C, 0x7A,
+ 0x36, 0xD9, 0x62, 0x4B, 0xEF, 0xC1, 0x1F, 0x00,
+ 0x34, 0xB1, 0xF8, 0xE4, 0xD5, 0x09, 0x1A, 0x9A,
+ 0x70, 0x48, 0x9D, 0xF1, 0xE0, 0x9C, 0xD3, 0x5C,
+ 0x75, 0x02, 0x2B, 0x92, 0x21, 0x7D, 0xF5, 0x5E,
+ 0x4E, 0x3C, 0x84, 0x14, 0x28, 0x3A, 0xE9, 0xC0 };
+
+unsigned char table_58[256] = {
+ 0xE9, 0x81, 0x60, 0xA7, 0x18, 0xA0, 0x0F, 0x55,
+ 0x2B, 0x52, 0xE0, 0x8B, 0x9D, 0x85, 0xD2, 0xA3,
+ 0x3F, 0x6E, 0xB1, 0xAF, 0xE3, 0x36, 0xE2, 0x19,
+ 0x56, 0xB0, 0x09, 0xB5, 0x79, 0x43, 0xE1, 0x06,
+ 0x45, 0xB6, 0xC0, 0x22, 0xEE, 0x41, 0xEC, 0x01,
+ 0x66, 0x2D, 0x87, 0x38, 0x16, 0x37, 0xFA, 0x29,
+ 0x96, 0xA4, 0xC3, 0x23, 0x59, 0x7E, 0x92, 0x78,
+ 0x10, 0x2A, 0x4C, 0x0E, 0x9B, 0x4A, 0x35, 0xF4,
+ 0x42, 0x0C, 0xD8, 0xD7, 0x24, 0x2C, 0xDD, 0x8E,
+ 0x5B, 0xF5, 0x33, 0x48, 0xEF, 0xDE, 0x4B, 0xBC,
+ 0x51, 0xAB, 0x7C, 0xE4, 0x63, 0x70, 0x9A, 0xAC,
+ 0x54, 0x1D, 0x25, 0xC5, 0xEA, 0xB3, 0x05, 0xF7,
+ 0xC1, 0x1F, 0xE8, 0x97, 0xBB, 0x32, 0x6D, 0xC7,
+ 0x28, 0x61, 0xDB, 0x4D, 0x77, 0x72, 0x65, 0x8C,
+ 0x80, 0x3A, 0x76, 0x47, 0xA8, 0x03, 0x04, 0x12,
+ 0xCE, 0xA9, 0x75, 0x3C, 0x49, 0xF8, 0x64, 0xDF,
+ 0x57, 0xA2, 0x69, 0x44, 0xAD, 0x3E, 0x4F, 0x0B,
+ 0x74, 0x67, 0xC9, 0x1A, 0x17, 0xAA, 0x02, 0x6F,
+ 0xDA, 0xF2, 0xC6, 0x27, 0x53, 0xD6, 0xFD, 0xCA,
+ 0x8D, 0x93, 0x89, 0xD5, 0x6B, 0x4E, 0x90, 0x82,
+ 0x30, 0xE7, 0xC4, 0xD9, 0x8A, 0x7F, 0xB4, 0xFC,
+ 0xCF, 0xA1, 0xAE, 0x1C, 0x39, 0x1B, 0x7B, 0x5E,
+ 0x88, 0x7D, 0xD3, 0x71, 0x2E, 0x98, 0x13, 0x8F,
+ 0xCC, 0x84, 0x73, 0xCD, 0x21, 0x0D, 0x5C, 0xA5,
+ 0x3D, 0x9E, 0x99, 0xC2, 0xF3, 0x34, 0x14, 0x62,
+ 0x46, 0x0A, 0x07, 0x08, 0xFF, 0xFB, 0xB7, 0xBF,
+ 0x5D, 0x91, 0xB8, 0x83, 0xBE, 0x94, 0xBA, 0xF9,
+ 0xEB, 0xE5, 0xCB, 0x95, 0x40, 0x31, 0xE6, 0x86,
+ 0xD4, 0xFE, 0xD0, 0x7A, 0x26, 0xB9, 0xDC, 0x2F,
+ 0xBD, 0xF0, 0x5F, 0x00, 0x9C, 0x6A, 0x5A, 0x3B,
+ 0xF1, 0xC8, 0x9F, 0xED, 0x50, 0x20, 0x15, 0x11,
+ 0x68, 0x1E, 0xF6, 0xA6, 0x6C, 0xB2, 0xD1, 0x58 };
+
+unsigned char table_59[256] = {
+ 0x4C, 0x85, 0x2B, 0x14, 0xCC, 0x4D, 0x5F, 0xD7,
+ 0xCE, 0x28, 0xC5, 0x0B, 0xA1, 0x99, 0x08, 0xDE,
+ 0x42, 0xD1, 0x82, 0x5C, 0xC9, 0x8F, 0x72, 0x12,
+ 0xCB, 0x0D, 0x04, 0xFA, 0xCD, 0xE5, 0x9A, 0x6F,
+ 0xCF, 0x92, 0xB5, 0x88, 0x87, 0xBF, 0x90, 0x7C,
+ 0xAC, 0xBE, 0x36, 0x21, 0x7D, 0x7F, 0xC7, 0x9F,
+ 0x75, 0xBB, 0x61, 0x16, 0x17, 0x63, 0xAE, 0xC4,
+ 0x23, 0x89, 0xE0, 0x37, 0x91, 0x5E, 0xC8, 0xE4,
+ 0xFD, 0xD5, 0xA2, 0xC6, 0x5A, 0xEF, 0x9B, 0xD6,
+ 0x27, 0xEE, 0x60, 0x1C, 0xDF, 0xDA, 0xF1, 0xD2,
+ 0x1E, 0x01, 0x9D, 0x44, 0x03, 0xD8, 0x11, 0x53,
+ 0x4F, 0x6C, 0x8B, 0xB7, 0x40, 0xF2, 0x79, 0x20,
+ 0x74, 0x97, 0x3E, 0x3D, 0x05, 0xD4, 0x70, 0x30,
+ 0x54, 0x59, 0xE7, 0x15, 0xE1, 0xEB, 0x71, 0x83,
+ 0xFE, 0x66, 0xB1, 0xA6, 0xF7, 0x8E, 0x6A, 0xEA,
+ 0x65, 0x7E, 0xA3, 0xCA, 0x2D, 0x4B, 0xB8, 0x9C,
+ 0x35, 0xC3, 0xB6, 0x49, 0x32, 0x25, 0xB3, 0xB0,
+ 0x76, 0xC0, 0xF5, 0x00, 0x8A, 0xAF, 0x19, 0xDB,
+ 0xDD, 0x47, 0xDC, 0x07, 0xB2, 0x4A, 0x55, 0xE6,
+ 0x69, 0xEC, 0xED, 0x06, 0x94, 0xB9, 0xA7, 0x56,
+ 0x2C, 0xAA, 0xE3, 0x22, 0x3B, 0x98, 0x77, 0x52,
+ 0x3C, 0x64, 0xF8, 0x13, 0x78, 0xFC, 0xFB, 0xF3,
+ 0xD3, 0xF9, 0x29, 0x45, 0x51, 0x8C, 0xA0, 0x38,
+ 0xD9, 0xA5, 0x62, 0x3A, 0x6E, 0xD0, 0xE8, 0x7A,
+ 0x33, 0x1D, 0xB4, 0x73, 0x02, 0xFF, 0x10, 0x80,
+ 0x6B, 0xF0, 0xA4, 0xBA, 0xF6, 0xC2, 0x0E, 0xE2,
+ 0x81, 0x43, 0x84, 0x86, 0x1F, 0x31, 0x2F, 0xA9,
+ 0x1B, 0x2A, 0x4E, 0xF4, 0x95, 0x5B, 0x3F, 0x34,
+ 0x39, 0x7B, 0x0A, 0x26, 0x6D, 0x57, 0x50, 0x09,
+ 0x9E, 0xA8, 0xBC, 0x24, 0x93, 0x67, 0x41, 0x96,
+ 0x0C, 0x46, 0xBD, 0xE9, 0x68, 0x18, 0xAB, 0x2E,
+ 0x5D, 0x1A, 0x8D, 0xC1, 0x58, 0x48, 0xAD, 0x0F };
+
+unsigned char table_60[32] = {
+ 0x1C, 0x06, 0x1E, 0x10, 0x1D, 0x05, 0x00, 0x0E,
+ 0x0C, 0x02, 0x11, 0x19, 0x15, 0x18, 0x16, 0x07,
+ 0x1F, 0x0B, 0x14, 0x01, 0x0F, 0x09, 0x0D, 0x13,
+ 0x03, 0x08, 0x12, 0x04, 0x1B, 0x0A, 0x17, 0x1A };
+
+unsigned char table_61[256] = {
+ 0xC5, 0xA6, 0xF2, 0x6B, 0x4B, 0x58, 0xE0, 0x41,
+ 0xC6, 0x2F, 0x13, 0xFE, 0xC1, 0x34, 0x3F, 0x24,
+ 0x10, 0xBF, 0x8B, 0xC9, 0x26, 0x2E, 0x68, 0xBE,
+ 0x28, 0x54, 0x93, 0x11, 0x21, 0x03, 0xFF, 0x50,
+ 0x31, 0x71, 0x2C, 0x6C, 0x91, 0x8F, 0x3B, 0x40,
+ 0x3E, 0xE5, 0xA5, 0x80, 0xEA, 0x7C, 0x9D, 0x18,
+ 0x84, 0x5A, 0x73, 0x3A, 0x33, 0x43, 0xA1, 0x47,
+ 0xB1, 0xEE, 0xFB, 0x79, 0x5E, 0xAF, 0xB9, 0x48,
+ 0x0F, 0x88, 0x65, 0x67, 0x6F, 0xDB, 0x25, 0xE4,
+ 0xB0, 0x87, 0xD0, 0x46, 0xB5, 0xB7, 0x53, 0xD4,
+ 0x1E, 0x76, 0xB4, 0x90, 0xDD, 0xA3, 0xF7, 0x57,
+ 0xD2, 0xCC, 0x5D, 0xE3, 0xB3, 0xD8, 0x5F, 0x2B,
+ 0x69, 0x4A, 0x9B, 0x39, 0x1A, 0x8D, 0x05, 0x8A,
+ 0x44, 0x15, 0xAE, 0xF3, 0xA8, 0x92, 0x02, 0xAB,
+ 0xB8, 0xDA, 0x0A, 0x0C, 0xED, 0xD7, 0x77, 0x98,
+ 0x3D, 0x19, 0x95, 0x36, 0xE7, 0x7F, 0x66, 0xEF,
+ 0x86, 0xDC, 0xCB, 0x9C, 0x63, 0xE6, 0x1D, 0x14,
+ 0x9A, 0x22, 0xBD, 0xD6, 0x89, 0x2D, 0xD1, 0xF9,
+ 0xA2, 0xDE, 0xF5, 0x5C, 0x8E, 0x2A, 0x29, 0xCA,
+ 0x7A, 0x8C, 0x38, 0x9F, 0xBB, 0xDF, 0xEC, 0x30,
+ 0x00, 0xFC, 0xAC, 0x81, 0xB2, 0xE8, 0xC0, 0xA7,
+ 0x7B, 0x07, 0x52, 0x74, 0x70, 0x0E, 0x51, 0x6A,
+ 0x62, 0x0D, 0x85, 0x1B, 0x4F, 0x96, 0x55, 0x1C,
+ 0x32, 0x6E, 0x01, 0xF6, 0x08, 0xFD, 0x17, 0x35,
+ 0xF0, 0x16, 0xC8, 0x23, 0xE9, 0x59, 0x3C, 0x37,
+ 0x5B, 0x42, 0xD3, 0x49, 0x7D, 0x83, 0x78, 0xAD,
+ 0x94, 0x9E, 0x56, 0xB6, 0xF1, 0xC3, 0x75, 0xF8,
+ 0xFA, 0x09, 0x4C, 0xD9, 0x97, 0xF4, 0x7E, 0x6D,
+ 0xBC, 0x4D, 0x64, 0xCD, 0x12, 0x99, 0x45, 0xCE,
+ 0x61, 0x20, 0x0B, 0xA0, 0x82, 0xD5, 0xE1, 0x72,
+ 0xA9, 0x1F, 0x06, 0x27, 0xC7, 0x04, 0xE2, 0xBA,
+ 0xCF, 0x60, 0xAA, 0xA4, 0xEB, 0xC4, 0x4E, 0xC2 };
+
+unsigned char table_62[256] = {
+ 0x01, 0x59, 0xEC, 0xFC, 0x51, 0xD2, 0xE4, 0x9D,
+ 0xAA, 0x61, 0xD5, 0xCA, 0x63, 0x5D, 0xCE, 0x36,
+ 0xB9, 0x49, 0x76, 0xA9, 0x14, 0x4C, 0x90, 0x28,
+ 0x66, 0x17, 0x4F, 0x1E, 0x1A, 0x47, 0x30, 0xE8,
+ 0xFD, 0x86, 0x2E, 0x7B, 0x7E, 0xCC, 0x34, 0x13,
+ 0x94, 0x45, 0x38, 0x74, 0x29, 0xB0, 0x37, 0xC3,
+ 0x26, 0x6C, 0x39, 0xA3, 0x89, 0xEB, 0xA2, 0x20,
+ 0x00, 0xE0, 0x73, 0xE7, 0xB5, 0xCB, 0xED, 0x3E,
+ 0x79, 0x09, 0xFA, 0x32, 0x54, 0xBA, 0x05, 0x96,
+ 0xDE, 0x23, 0xD0, 0xA1, 0xAB, 0xFE, 0xF2, 0x22,
+ 0xB2, 0x9B, 0x7D, 0x44, 0x12, 0x3D, 0x40, 0x82,
+ 0xA0, 0xA8, 0x33, 0xDC, 0xF7, 0xFB, 0xAC, 0x41,
+ 0x8A, 0x9C, 0x60, 0x11, 0xC8, 0xF0, 0xEA, 0x57,
+ 0x3A, 0x42, 0xCD, 0x1D, 0x3C, 0xC6, 0x97, 0x62,
+ 0x55, 0x9F, 0xF3, 0x93, 0x91, 0xDA, 0x6A, 0xE5,
+ 0x27, 0x8E, 0x4E, 0xFF, 0xA4, 0x80, 0x04, 0xE1,
+ 0x2B, 0x5E, 0xC0, 0x64, 0xC2, 0xD8, 0x46, 0x8C,
+ 0xD4, 0x0F, 0xC4, 0x43, 0xD9, 0x9E, 0x4B, 0x5C,
+ 0x0A, 0x8B, 0xBF, 0xD7, 0x7A, 0x81, 0x3B, 0x4A,
+ 0x58, 0xB6, 0x21, 0x1F, 0xC1, 0xBD, 0xB1, 0x77,
+ 0x72, 0x1C, 0x4D, 0xBC, 0xA5, 0x65, 0xC7, 0xF5,
+ 0xB4, 0x2D, 0x69, 0x71, 0xE6, 0x8F, 0xBB, 0x03,
+ 0xAF, 0xD6, 0x08, 0x75, 0xB7, 0x31, 0xF4, 0x2A,
+ 0x48, 0x70, 0x0C, 0x8D, 0xD1, 0x87, 0x2F, 0x16,
+ 0x5A, 0x5B, 0x98, 0xA6, 0xC5, 0x99, 0x50, 0x07,
+ 0xDD, 0x92, 0x25, 0x68, 0x0D, 0xBE, 0x78, 0x0B,
+ 0xAD, 0x84, 0x6B, 0x19, 0x52, 0x7C, 0xF6, 0xB3,
+ 0x56, 0x83, 0x88, 0xEE, 0x2C, 0x1B, 0x6E, 0x53,
+ 0x67, 0xE2, 0x6F, 0x15, 0x06, 0x10, 0x18, 0x85,
+ 0xF1, 0x6D, 0xF9, 0xC9, 0xAE, 0x3F, 0xB8, 0x95,
+ 0x35, 0xDF, 0xEF, 0xA7, 0x7F, 0x24, 0xF8, 0xE3,
+ 0xCF, 0xE9, 0xDB, 0xD3, 0x02, 0x9A, 0x0E, 0x5F };
+
+unsigned char table_63[256] = {
+ 0x0C, 0x02, 0xEE, 0x94, 0x2D, 0x76, 0x96, 0x75,
+ 0x21, 0xDC, 0x37, 0x03, 0xC0, 0xF7, 0xDF, 0xEF,
+ 0xB1, 0x1D, 0xCF, 0x15, 0x5A, 0xB4, 0xCC, 0x81,
+ 0x89, 0x6B, 0xA5, 0x2E, 0x6D, 0xD4, 0x08, 0x44,
+ 0x2A, 0x60, 0x50, 0xBF, 0x40, 0x7D, 0x5F, 0x64,
+ 0x93, 0x70, 0xA4, 0x7F, 0xC9, 0xEB, 0x0A, 0xF8,
+ 0x9F, 0xA8, 0xBC, 0x25, 0xE5, 0xF3, 0x1B, 0xD7,
+ 0x29, 0x13, 0x0D, 0x69, 0x20, 0x5C, 0x0F, 0x91,
+ 0x4F, 0x62, 0x06, 0x26, 0x41, 0xED, 0xDA, 0x53,
+ 0x65, 0xFF, 0xCD, 0x3F, 0xF6, 0x01, 0xCE, 0xA2,
+ 0x04, 0xDE, 0x27, 0x87, 0xBA, 0x86, 0x24, 0x78,
+ 0xAF, 0xE1, 0x3D, 0xD0, 0xC8, 0x1F, 0x4A, 0x2C,
+ 0x9A, 0xF0, 0xCB, 0xAD, 0x0B, 0x59, 0xC5, 0x58,
+ 0xEA, 0x8A, 0xA1, 0x45, 0xB7, 0x5D, 0xB5, 0x77,
+ 0x2B, 0x47, 0x05, 0x00, 0xAC, 0x61, 0xFA, 0x33,
+ 0x74, 0x31, 0xCA, 0x22, 0x42, 0x8B, 0xFE, 0x09,
+ 0xB2, 0x6E, 0x1A, 0xBE, 0xAA, 0x7B, 0xEC, 0xF4,
+ 0x51, 0x66, 0x28, 0x12, 0xFC, 0x5E, 0x67, 0xF5,
+ 0xB9, 0x82, 0x90, 0x8E, 0x8D, 0x17, 0xE7, 0xE8,
+ 0xB0, 0xC3, 0x16, 0xA0, 0x4B, 0xB6, 0xFB, 0x7E,
+ 0xC4, 0x85, 0x4C, 0x1E, 0xC7, 0x39, 0x4E, 0xA9,
+ 0xE3, 0x4D, 0x32, 0x72, 0x35, 0x80, 0xE0, 0x34,
+ 0xB8, 0x73, 0x98, 0x49, 0x92, 0x30, 0xD5, 0xD2,
+ 0xA3, 0x54, 0x7A, 0x84, 0x8F, 0x6C, 0xFD, 0x43,
+ 0x3A, 0x36, 0x3B, 0xD9, 0x48, 0x6A, 0x14, 0x79,
+ 0xD1, 0x57, 0x88, 0xDB, 0xE4, 0x9B, 0xF9, 0x99,
+ 0x10, 0x71, 0xC1, 0x68, 0x9E, 0x11, 0xAB, 0xBD,
+ 0x7C, 0x3E, 0x3C, 0x18, 0x9D, 0x97, 0xF2, 0xE6,
+ 0xA6, 0xF1, 0x46, 0xC2, 0x19, 0xBB, 0x52, 0xD8,
+ 0x95, 0xD3, 0x23, 0xAE, 0x07, 0x2F, 0xE9, 0x63,
+ 0x1C, 0x55, 0x6F, 0x9C, 0x56, 0x38, 0xC6, 0x5B,
+ 0x8C, 0xE2, 0x83, 0xA7, 0xD6, 0x0E, 0xB3, 0xDD };
+
+unsigned char table_64[32] = {
+ 0x03, 0x05, 0x0D, 0x09, 0x1A, 0x16, 0x08, 0x10,
+ 0x06, 0x1E, 0x1C, 0x15, 0x02, 0x04, 0x17, 0x0C,
+ 0x18, 0x0B, 0x19, 0x11, 0x1B, 0x14, 0x13, 0x0A,
+ 0x0E, 0x00, 0x1D, 0x1F, 0x01, 0x0F, 0x07, 0x12 };
+
+unsigned char table_65[32] = {
+ 0x01, 0x0A, 0x1E, 0x14, 0x10, 0x1D, 0x0D, 0x17,
+ 0x0E, 0x0C, 0x0F, 0x12, 0x04, 0x1A, 0x05, 0x02,
+ 0x08, 0x1C, 0x09, 0x1F, 0x0B, 0x13, 0x19, 0x1B,
+ 0x11, 0x00, 0x16, 0x06, 0x03, 0x18, 0x15, 0x07 };
+
+unsigned char table_66[32] = {
+ 0x1C, 0x18, 0x0C, 0x09, 0x05, 0x03, 0x15, 0x12,
+ 0x0D, 0x02, 0x08, 0x0E, 0x19, 0x07, 0x13, 0x17,
+ 0x1E, 0x1D, 0x1F, 0x11, 0x06, 0x0A, 0x0B, 0x14,
+ 0x0F, 0x10, 0x01, 0x1B, 0x00, 0x04, 0x1A, 0x16 };
+
+unsigned char table_67[256] = {
+ 0x6B, 0x49, 0xC8, 0x86, 0xFF, 0xC0, 0x5D, 0xEF,
+ 0xF7, 0x06, 0xE0, 0x98, 0xA9, 0x72, 0x71, 0xD5,
+ 0xBA, 0x7F, 0x10, 0xD1, 0xBE, 0x41, 0x9C, 0x40,
+ 0x28, 0x8E, 0xE5, 0x74, 0x47, 0x9E, 0x3E, 0x7C,
+ 0xB5, 0xCD, 0x3F, 0x20, 0xF2, 0xA6, 0xDC, 0x97,
+ 0x32, 0x6D, 0x52, 0xF5, 0x16, 0x05, 0xFE, 0x04,
+ 0x3D, 0x53, 0x50, 0x23, 0x39, 0x77, 0x08, 0x60,
+ 0x75, 0x18, 0x4A, 0xC6, 0xBB, 0xE7, 0xF1, 0xAB,
+ 0xEB, 0x88, 0xB6, 0x82, 0x6E, 0x91, 0xF3, 0x34,
+ 0x3A, 0x42, 0x1A, 0xDF, 0xA1, 0xB3, 0x92, 0xBF,
+ 0xB7, 0x00, 0xD4, 0xDE, 0x31, 0xF0, 0x1C, 0xDA,
+ 0x4F, 0x61, 0x67, 0x2C, 0x07, 0xF9, 0x15, 0xA4,
+ 0x7A, 0x26, 0x45, 0x2A, 0x12, 0x9F, 0xF4, 0x14,
+ 0x8C, 0x90, 0xFC, 0xC5, 0x4B, 0x87, 0xE2, 0xC7,
+ 0xD0, 0x8A, 0xE8, 0xDD, 0xEE, 0x3C, 0x2F, 0x22,
+ 0x6A, 0x54, 0x37, 0x9B, 0x84, 0x25, 0x8F, 0xE3,
+ 0xD7, 0xD8, 0x4E, 0xAD, 0x0F, 0x4C, 0x56, 0xA2,
+ 0xD3, 0xB0, 0x73, 0x0B, 0xAE, 0xEA, 0x1D, 0x01,
+ 0x36, 0xB4, 0x2D, 0xC4, 0x19, 0x58, 0x1E, 0x62,
+ 0xE9, 0xB2, 0x5B, 0x5A, 0xBD, 0xD6, 0x65, 0x94,
+ 0x9A, 0x55, 0xCC, 0x99, 0x1B, 0x85, 0x2B, 0xBC,
+ 0x8D, 0x46, 0x81, 0xB8, 0xA3, 0x29, 0x5F, 0x35,
+ 0x5C, 0xB1, 0x1F, 0x13, 0x17, 0xCB, 0x51, 0x02,
+ 0x09, 0x7E, 0xA7, 0x69, 0x6F, 0x95, 0x30, 0x7B,
+ 0xCA, 0x48, 0xAF, 0xAA, 0x0E, 0x44, 0x38, 0xB9,
+ 0x0D, 0x11, 0xA0, 0xD9, 0x0C, 0xDB, 0xF8, 0x68,
+ 0x33, 0x79, 0x59, 0x66, 0x4D, 0x03, 0xE1, 0x89,
+ 0xE4, 0x3B, 0x78, 0xC2, 0x64, 0x6C, 0x27, 0xC9,
+ 0xCF, 0xAC, 0xED, 0xFA, 0x5E, 0x2E, 0x76, 0x57,
+ 0x93, 0xEC, 0x80, 0xA8, 0xE6, 0xCE, 0xC1, 0xA5,
+ 0x9D, 0xD2, 0xC3, 0x0A, 0x7D, 0x70, 0xF6, 0x63,
+ 0x24, 0x43, 0x21, 0x83, 0xFB, 0xFD, 0x8B, 0x96 };
+
+unsigned char table_68[256] = {
+ 0x93, 0xFF, 0x83, 0x70, 0x12, 0x2D, 0x1C, 0xD6,
+ 0xF9, 0xEE, 0xCF, 0x94, 0x7B, 0xB5, 0xA4, 0x84,
+ 0x99, 0xF7, 0x67, 0x32, 0xFC, 0x8A, 0xE3, 0xE4,
+ 0xCE, 0xC6, 0x77, 0x7E, 0xDA, 0x42, 0x85, 0xF0,
+ 0x7D, 0x48, 0x28, 0x79, 0xDE, 0x5B, 0xE2, 0x0F,
+ 0x75, 0xC5, 0x2C, 0x4F, 0xF3, 0xEC, 0x14, 0x10,
+ 0x9C, 0x6E, 0x59, 0x4A, 0x20, 0x34, 0xA3, 0x89,
+ 0xE0, 0x4E, 0x52, 0x88, 0x81, 0x5F, 0x6F, 0x71,
+ 0x17, 0x3B, 0x21, 0xB4, 0xCB, 0x9B, 0x18, 0x13,
+ 0xE8, 0xE1, 0x02, 0x2E, 0xED, 0x00, 0xA7, 0x1B,
+ 0x06, 0xF4, 0x27, 0xDC, 0x35, 0x2F, 0x08, 0x9D,
+ 0x7C, 0xC0, 0x36, 0xA6, 0x6B, 0xDF, 0x4C, 0xBC,
+ 0xFE, 0xDB, 0xA5, 0xA8, 0x8D, 0x73, 0x7F, 0xC7,
+ 0x8E, 0x60, 0x31, 0x61, 0x4B, 0x29, 0xD7, 0xE9,
+ 0xBD, 0xAB, 0xCC, 0xFA, 0xD9, 0xEF, 0xC2, 0xD4,
+ 0x19, 0x11, 0x15, 0xC9, 0xB1, 0xD5, 0x64, 0x97,
+ 0xE7, 0x8F, 0x05, 0x44, 0xF8, 0xF1, 0x58, 0x47,
+ 0x2A, 0x03, 0x1F, 0xAF, 0x0D, 0x04, 0x23, 0xB8,
+ 0x24, 0x51, 0xB2, 0x54, 0x41, 0x53, 0x5C, 0xAE,
+ 0xB7, 0xB3, 0xB6, 0x3D, 0x37, 0x39, 0x55, 0xBF,
+ 0x0B, 0x7A, 0x57, 0x3C, 0x0E, 0x40, 0x6A, 0xF5,
+ 0x72, 0xDD, 0xBB, 0x8B, 0xAA, 0x46, 0xA0, 0x30,
+ 0x56, 0x78, 0x38, 0xBA, 0x9E, 0x92, 0x87, 0xFB,
+ 0x66, 0x90, 0x1E, 0xB9, 0x96, 0x65, 0xA2, 0x50,
+ 0x1D, 0xC3, 0x26, 0x22, 0xD0, 0x0A, 0x43, 0xF2,
+ 0xB0, 0xEB, 0xAC, 0x62, 0x98, 0x3F, 0xD3, 0x69,
+ 0xA1, 0x9F, 0x16, 0x95, 0xE6, 0xF6, 0x2B, 0x25,
+ 0x1A, 0xD2, 0xBE, 0x09, 0x5D, 0x45, 0xC4, 0xFD,
+ 0x5A, 0x07, 0x0C, 0x82, 0x3E, 0x49, 0x74, 0x6C,
+ 0x68, 0x5E, 0xCA, 0xEA, 0xCD, 0x9A, 0xAD, 0xD1,
+ 0x33, 0x86, 0x76, 0x80, 0xE5, 0xC8, 0xD8, 0xA9,
+ 0x8C, 0x6D, 0x91, 0x63, 0x3A, 0x4D, 0xC1, 0x01 };
+
+unsigned char table_69[256] = {
+ 0x21, 0x6B, 0x9B, 0xAE, 0x11, 0x5A, 0x91, 0xC2,
+ 0x47, 0x8E, 0x87, 0x86, 0x4F, 0xFC, 0x8F, 0x66,
+ 0x97, 0x2F, 0x61, 0x9C, 0x5B, 0x4C, 0xB3, 0x14,
+ 0x77, 0x48, 0x62, 0xE1, 0x54, 0x64, 0xDD, 0xCD,
+ 0x30, 0xB7, 0x2D, 0xD2, 0xC3, 0xC0, 0x0B, 0xD8,
+ 0x53, 0x98, 0x16, 0x56, 0x7A, 0x35, 0x50, 0xD9,
+ 0xE8, 0x2C, 0x32, 0x55, 0x17, 0x5D, 0x79, 0xEB,
+ 0xC8, 0x75, 0x67, 0xE2, 0x4B, 0xBA, 0xFE, 0x57,
+ 0x10, 0xF4, 0x70, 0x2A, 0xBB, 0xA6, 0x72, 0x36,
+ 0xAF, 0x8D, 0xAB, 0x90, 0xE3, 0x2B, 0xB2, 0x26,
+ 0x93, 0x01, 0xBD, 0x71, 0xF9, 0x05, 0xC7, 0x80,
+ 0x29, 0xCC, 0x3B, 0x22, 0xF2, 0x12, 0x81, 0x34,
+ 0xF6, 0x1A, 0x8B, 0xDF, 0x28, 0x46, 0x9E, 0x6A,
+ 0x23, 0x85, 0x74, 0xE7, 0xE6, 0x52, 0xA0, 0x49,
+ 0xF0, 0x19, 0x25, 0xAC, 0x78, 0x42, 0xD6, 0xA2,
+ 0x37, 0x65, 0x4D, 0x94, 0x02, 0x6F, 0xB4, 0xC6,
+ 0x99, 0xD3, 0x9A, 0x33, 0xB8, 0x00, 0xCA, 0xE4,
+ 0x45, 0xAD, 0x1B, 0x6C, 0x03, 0xA8, 0x07, 0x8A,
+ 0x60, 0x69, 0xFF, 0xF7, 0xA7, 0x27, 0x95, 0xF5,
+ 0x82, 0xCB, 0xEC, 0xED, 0x4E, 0xFB, 0xA4, 0x59,
+ 0xDA, 0xCF, 0x2E, 0x20, 0xFA, 0x31, 0xD1, 0xEA,
+ 0x4A, 0xE9, 0x5E, 0xA9, 0xA1, 0x08, 0x1C, 0x96,
+ 0x38, 0xB9, 0xEE, 0x7F, 0xAA, 0xF1, 0x7D, 0x3A,
+ 0xA5, 0x43, 0xC5, 0xE0, 0x24, 0x39, 0x0D, 0xDE,
+ 0xB0, 0xF8, 0xBE, 0x58, 0x7E, 0x51, 0xD4, 0x89,
+ 0x15, 0x40, 0x3E, 0xB1, 0x1F, 0x5F, 0x68, 0x63,
+ 0x84, 0x3D, 0x88, 0xBC, 0x41, 0xEF, 0xB5, 0xBF,
+ 0x06, 0x6E, 0x9D, 0x3F, 0x0E, 0x76, 0x5C, 0xDC,
+ 0x13, 0xF3, 0xE5, 0x8C, 0x7C, 0x04, 0x0A, 0xD5,
+ 0x18, 0xC4, 0x44, 0x09, 0xC9, 0x1D, 0x9F, 0xFD,
+ 0xD0, 0x0F, 0x6D, 0xD7, 0x92, 0x7B, 0x0C, 0xA3,
+ 0x73, 0xDB, 0xB6, 0x83, 0xCE, 0x1E, 0xC1, 0x3C };
+
+unsigned char table_70[256] = {
+ 0x54, 0x23, 0xF1, 0x09, 0x9D, 0xEB, 0x26, 0xD9,
+ 0x6C, 0xC1, 0xBC, 0x3D, 0x6E, 0xB0, 0x5F, 0xE2,
+ 0x59, 0x4D, 0x95, 0xFA, 0xD8, 0x29, 0xAA, 0x8E,
+ 0xF5, 0xEF, 0x43, 0x76, 0xFD, 0x0D, 0x4F, 0xAD,
+ 0xB7, 0xFC, 0xA8, 0x9F, 0x62, 0xC2, 0x7B, 0x10,
+ 0x0B, 0xF2, 0x73, 0xA9, 0x46, 0x4C, 0x53, 0xD7,
+ 0x0A, 0x50, 0x89, 0x63, 0x48, 0xD6, 0xA2, 0x44,
+ 0xE6, 0x8D, 0x69, 0x2C, 0xF9, 0xC0, 0x35, 0x06,
+ 0x66, 0x21, 0x9E, 0xD2, 0x98, 0xF7, 0x9B, 0xE7,
+ 0x12, 0xB8, 0xA5, 0xBA, 0xE0, 0x79, 0x71, 0x7E,
+ 0x8C, 0x24, 0xED, 0x7C, 0x60, 0x81, 0xC3, 0x5C,
+ 0x2B, 0xE5, 0xEE, 0xB5, 0xA4, 0x05, 0x03, 0x34,
+ 0x16, 0x2A, 0xA3, 0x2D, 0x3F, 0xDF, 0x07, 0x5B,
+ 0xAE, 0x47, 0x61, 0x08, 0x18, 0xDB, 0x6D, 0x3C,
+ 0x96, 0xD5, 0xAB, 0x78, 0x94, 0x45, 0x20, 0x9A,
+ 0xE4, 0x13, 0x68, 0xDD, 0xDE, 0x31, 0x14, 0x57,
+ 0x02, 0x52, 0x56, 0x1C, 0x1B, 0xE9, 0xD0, 0xA1,
+ 0x22, 0x64, 0xB2, 0x7A, 0xCF, 0x5D, 0x00, 0x0F,
+ 0xF8, 0x5E, 0x36, 0x58, 0x40, 0xAF, 0x19, 0x32,
+ 0x2E, 0xB3, 0x72, 0xBE, 0xB9, 0xD3, 0xCD, 0x7D,
+ 0x4A, 0x1D, 0x33, 0x2F, 0xAC, 0x27, 0x41, 0xE8,
+ 0x55, 0xCB, 0x0E, 0x5A, 0x77, 0xFB, 0x8B, 0x86,
+ 0x75, 0x8A, 0x51, 0xEC, 0xDA, 0xC6, 0xA6, 0xCC,
+ 0x91, 0x4B, 0x11, 0xF6, 0xEA, 0xD1, 0xB6, 0x4E,
+ 0x82, 0x04, 0x92, 0x30, 0xF4, 0x25, 0x88, 0x1E,
+ 0x9C, 0xA0, 0xC8, 0x6A, 0x93, 0x87, 0x1F, 0xB4,
+ 0xB1, 0x8F, 0x65, 0xCA, 0xFE, 0xFF, 0x97, 0x15,
+ 0x99, 0x28, 0x80, 0x42, 0x70, 0x85, 0x0C, 0x3B,
+ 0xBD, 0xE1, 0xA7, 0x17, 0xC9, 0x3A, 0xBB, 0x6B,
+ 0x37, 0xF0, 0xC5, 0x39, 0x6F, 0x01, 0x83, 0x67,
+ 0x74, 0xCE, 0xDC, 0x90, 0x3E, 0xF3, 0x7F, 0xC4,
+ 0x49, 0x84, 0x38, 0xC7, 0xE3, 0xD4, 0x1A, 0xBF };
+
+unsigned char table_71[32] = {
+ 0x17, 0x13, 0x0E, 0x1A, 0x0D, 0x18, 0x19, 0x10,
+ 0x14, 0x11, 0x16, 0x05, 0x04, 0x00, 0x12, 0x0A,
+ 0x02, 0x07, 0x03, 0x0B, 0x09, 0x1F, 0x1C, 0x0F,
+ 0x0C, 0x06, 0x1B, 0x08, 0x1D, 0x01, 0x15, 0x1E };
+
+unsigned char table_72[256] = {
+ 0xC9, 0xA7, 0x1B, 0xEC, 0x2B, 0x8B, 0xB0, 0xEB,
+ 0x7F, 0x39, 0x25, 0xD9, 0x1D, 0xD5, 0x67, 0xA0,
+ 0xB3, 0xAC, 0x3B, 0xC8, 0x82, 0xC0, 0xE3, 0x9E,
+ 0x4C, 0x9B, 0xAF, 0xFD, 0x91, 0x86, 0x5F, 0x92,
+ 0xB4, 0x42, 0x3C, 0x45, 0x12, 0xC4, 0xE2, 0xE1,
+ 0x6C, 0x1F, 0xC6, 0x40, 0x93, 0x2A, 0xC2, 0x72,
+ 0x2E, 0x14, 0x51, 0xA5, 0x70, 0xBD, 0xA2, 0xC7,
+ 0x7D, 0xF1, 0x9F, 0x64, 0xC1, 0xF7, 0x80, 0xFF,
+ 0x50, 0x49, 0x8C, 0x66, 0x13, 0x48, 0x6A, 0x0A,
+ 0x26, 0x94, 0x83, 0x1E, 0x84, 0xBB, 0x57, 0x27,
+ 0x44, 0x5B, 0x62, 0xF6, 0x09, 0x4F, 0x77, 0x76,
+ 0x2D, 0x7E, 0xCD, 0x0B, 0x24, 0xFE, 0x81, 0xB8,
+ 0x21, 0x85, 0xCF, 0xA8, 0x75, 0x56, 0x37, 0x17,
+ 0xAA, 0x23, 0xE5, 0xE8, 0x9A, 0x9D, 0x2F, 0x04,
+ 0x31, 0x4A, 0x7C, 0xFC, 0xD6, 0xE4, 0x29, 0xC3,
+ 0xFB, 0x36, 0x1C, 0x0C, 0xCE, 0xEE, 0x0D, 0xF3,
+ 0x46, 0xF8, 0x41, 0x0E, 0x68, 0xAB, 0x2C, 0x69,
+ 0x96, 0x90, 0x28, 0xED, 0x02, 0x63, 0x07, 0xAD,
+ 0xB2, 0xDC, 0x05, 0xE6, 0x78, 0x03, 0xA4, 0x7A,
+ 0x5C, 0x52, 0x95, 0x5D, 0x88, 0x01, 0xDF, 0x35,
+ 0x5E, 0xB6, 0x06, 0x4D, 0x15, 0x89, 0x59, 0x3F,
+ 0xF0, 0xA1, 0xA3, 0x99, 0x19, 0xEA, 0xDB, 0xE0,
+ 0x6B, 0x71, 0x6E, 0xB7, 0x65, 0x54, 0x9C, 0xBC,
+ 0x98, 0xDD, 0x4B, 0x60, 0x3D, 0xBF, 0xF5, 0xD1,
+ 0xD7, 0xF9, 0x55, 0x61, 0xA9, 0xB1, 0x6D, 0xDE,
+ 0x79, 0xAE, 0x1A, 0x34, 0x3A, 0x4E, 0xCB, 0x38,
+ 0xBA, 0x97, 0x00, 0x74, 0xEF, 0xD8, 0x18, 0x33,
+ 0x7B, 0xFA, 0x22, 0x32, 0x20, 0xCA, 0x8A, 0xBE,
+ 0xA6, 0x43, 0x11, 0x10, 0xD0, 0xD3, 0x87, 0x73,
+ 0x6F, 0xF4, 0x8D, 0xCC, 0x30, 0x0F, 0x16, 0xDA,
+ 0xB5, 0xC5, 0xD4, 0x47, 0x8E, 0xE7, 0x58, 0x8F,
+ 0x08, 0x53, 0xF2, 0xB9, 0x5A, 0x3E, 0xE9, 0xD2 };
+
+unsigned char table_73[256] = {
+ 0x36, 0x37, 0xED, 0xD8, 0xBF, 0xD7, 0x12, 0xB7,
+ 0x40, 0x32, 0x19, 0x4A, 0x44, 0x2A, 0xCE, 0xA5,
+ 0x29, 0x13, 0x43, 0x51, 0x5C, 0xD0, 0x76, 0x6E,
+ 0x41, 0xD6, 0xE2, 0x4F, 0xB8, 0x27, 0x2E, 0xCF,
+ 0xD9, 0xE0, 0x69, 0xC0, 0x59, 0x77, 0x62, 0x6F,
+ 0x53, 0xE7, 0x93, 0xD4, 0xAD, 0xC8, 0x4C, 0xC2,
+ 0x2C, 0xBE, 0xAA, 0xA0, 0x22, 0x78, 0x14, 0xB3,
+ 0xB0, 0xEA, 0xBA, 0x9A, 0x33, 0x1B, 0x31, 0x6C,
+ 0xFC, 0x0A, 0x0B, 0xA1, 0xE4, 0x75, 0x7C, 0xE3,
+ 0x65, 0x21, 0xA9, 0xA4, 0x4E, 0x3C, 0x5F, 0x39,
+ 0x74, 0xA2, 0x9E, 0x03, 0x70, 0xD2, 0xFD, 0x1D,
+ 0x25, 0x72, 0x73, 0x8E, 0x7B, 0xB2, 0x6A, 0x92,
+ 0x81, 0xF3, 0xF0, 0x46, 0x08, 0x85, 0xE6, 0x30,
+ 0x05, 0x7E, 0xEC, 0x0D, 0xDD, 0x42, 0x2F, 0x5B,
+ 0xB9, 0xCB, 0x84, 0x0C, 0x16, 0xC7, 0x24, 0xFA,
+ 0xF9, 0x8F, 0x20, 0xAC, 0x10, 0x55, 0xC3, 0x1A,
+ 0x8B, 0x94, 0x3D, 0xDB, 0xC9, 0x04, 0xB5, 0xCC,
+ 0xC6, 0x98, 0xB6, 0x8D, 0x0F, 0x3A, 0x06, 0x4B,
+ 0xEF, 0x35, 0x68, 0x3F, 0xEE, 0xE5, 0x63, 0xC5,
+ 0x60, 0x88, 0x52, 0x2D, 0x6D, 0xAB, 0xCD, 0xC4,
+ 0x1F, 0xF4, 0xCA, 0x67, 0x7D, 0x1C, 0xDA, 0x34,
+ 0xDE, 0x86, 0xAE, 0xF1, 0x61, 0x09, 0xF5, 0xF6,
+ 0x49, 0xE9, 0xF2, 0x48, 0x1E, 0xD3, 0x56, 0x18,
+ 0x9B, 0xB1, 0x57, 0x9D, 0xBB, 0x5E, 0xAF, 0x87,
+ 0x9F, 0x8A, 0xC1, 0x79, 0xA7, 0xA8, 0xFB, 0xDC,
+ 0x47, 0x3E, 0x97, 0x80, 0x91, 0xA6, 0x7A, 0xA3,
+ 0x9C, 0x11, 0x02, 0x2B, 0x58, 0xD1, 0xF7, 0x00,
+ 0x83, 0x01, 0xE8, 0xFE, 0x50, 0x23, 0x66, 0x4D,
+ 0xD5, 0x82, 0x89, 0x3B, 0xEB, 0xE1, 0xF8, 0x5A,
+ 0x15, 0x7F, 0x8C, 0x17, 0x96, 0x28, 0x5D, 0x64,
+ 0x26, 0x38, 0x71, 0x0E, 0x45, 0xDF, 0xB4, 0x99,
+ 0xFF, 0x90, 0x6B, 0xBC, 0x54, 0x95, 0xBD, 0x07 };
+
+unsigned char table_74[256] = {
+ 0xA7, 0xCF, 0x99, 0x1A, 0x13, 0xC7, 0xE9, 0xC4,
+ 0xB6, 0x0E, 0x15, 0x09, 0xFF, 0xDF, 0xBE, 0x03,
+ 0xAD, 0xF1, 0xB0, 0x3C, 0x4A, 0x9B, 0xF5, 0x12,
+ 0xA1, 0x2C, 0xDB, 0x51, 0x5E, 0x6F, 0xE6, 0x49,
+ 0x27, 0xBB, 0xAE, 0x56, 0xC0, 0x0C, 0x77, 0x60,
+ 0x5B, 0x69, 0xA2, 0xF0, 0x24, 0x8E, 0xE1, 0xA4,
+ 0xBC, 0x9F, 0x50, 0xD4, 0x61, 0x19, 0x67, 0x00,
+ 0x7B, 0xAB, 0xDD, 0x26, 0xCD, 0x6C, 0xE8, 0xA8,
+ 0x7A, 0x93, 0xEF, 0x20, 0x52, 0x1F, 0x1B, 0x46,
+ 0x25, 0x3B, 0x1E, 0x65, 0xC2, 0xF9, 0x10, 0xB2,
+ 0xB3, 0xD9, 0x21, 0xD2, 0x11, 0x94, 0xE2, 0xFC,
+ 0x38, 0x9E, 0x36, 0x87, 0xAA, 0x53, 0x45, 0x68,
+ 0x2B, 0xE7, 0x07, 0xFA, 0xD3, 0x8D, 0x3F, 0x17,
+ 0xC1, 0x06, 0x72, 0x62, 0x8C, 0x55, 0x73, 0x8A,
+ 0xC9, 0x2E, 0x5A, 0x7D, 0x02, 0x6D, 0xF8, 0x4B,
+ 0xE4, 0xBF, 0xEC, 0xB7, 0x31, 0xDC, 0xF4, 0xB8,
+ 0x47, 0x64, 0x0A, 0x33, 0x48, 0xAC, 0xFB, 0x05,
+ 0x3E, 0x34, 0x1C, 0x97, 0x1D, 0x63, 0x37, 0x2D,
+ 0xB1, 0x92, 0xED, 0x9D, 0x4C, 0xD5, 0x4E, 0x9A,
+ 0x0D, 0x79, 0x0F, 0xBD, 0x95, 0xBA, 0x08, 0x2A,
+ 0xC6, 0x7E, 0x88, 0xCB, 0xA6, 0x29, 0x70, 0x35,
+ 0x66, 0xCA, 0x89, 0x75, 0x6A, 0x4F, 0xB5, 0x6B,
+ 0x74, 0xDE, 0x01, 0x04, 0x81, 0x91, 0x90, 0x18,
+ 0x32, 0x0B, 0x7F, 0x44, 0xB4, 0xAF, 0xF2, 0xEB,
+ 0x22, 0xFD, 0x14, 0xA0, 0xFE, 0x8B, 0xB9, 0x16,
+ 0x86, 0xE3, 0xD7, 0xDA, 0xC5, 0x3A, 0x41, 0x83,
+ 0xD1, 0x28, 0x54, 0x30, 0xE0, 0x40, 0xA5, 0x57,
+ 0x8F, 0x84, 0xD6, 0x96, 0x39, 0xE5, 0x42, 0x80,
+ 0xA9, 0x58, 0xCE, 0x5D, 0xEE, 0x5F, 0xA3, 0xD0,
+ 0xC8, 0x59, 0x43, 0x4D, 0x5C, 0xF7, 0xCC, 0x76,
+ 0x6E, 0xF3, 0x23, 0x3D, 0x85, 0x82, 0x78, 0xF6,
+ 0x2F, 0xD8, 0xC3, 0x7C, 0x9C, 0x98, 0xEA, 0x71 };
+
+unsigned char table_75[256] = {
+ 0xE7, 0xA5, 0x30, 0xE1, 0x9D, 0x81, 0xBE, 0x83,
+ 0xB2, 0x1E, 0xE4, 0x69, 0x2F, 0x2B, 0x0D, 0xEB,
+ 0x7C, 0x59, 0x2D, 0xAA, 0x01, 0x0C, 0xDB, 0xED,
+ 0xC4, 0xEE, 0x5D, 0x38, 0x72, 0xD8, 0x70, 0xCE,
+ 0x0B, 0xF6, 0x7F, 0x48, 0x26, 0x9E, 0xA3, 0x44,
+ 0xD6, 0xCF, 0x0F, 0x6B, 0xFD, 0x23, 0x98, 0xAB,
+ 0x11, 0xD4, 0x92, 0x91, 0x5E, 0x08, 0x4D, 0xC6,
+ 0xF0, 0xA8, 0x7E, 0x8A, 0x1D, 0xA1, 0x97, 0x76,
+ 0x3E, 0x64, 0x07, 0x24, 0xDE, 0x75, 0xA4, 0xCC,
+ 0x1A, 0x04, 0x4B, 0x6C, 0xFA, 0xB0, 0xC7, 0x35,
+ 0xE2, 0x56, 0x61, 0xA0, 0xE9, 0x27, 0xDF, 0xC3,
+ 0xE5, 0xF4, 0x8D, 0xB4, 0xD3, 0x52, 0xD7, 0x49,
+ 0xCD, 0x31, 0x6E, 0x3F, 0x4E, 0x6A, 0x5B, 0x65,
+ 0xCA, 0x14, 0x71, 0x53, 0xD9, 0x47, 0x28, 0x7D,
+ 0x17, 0x06, 0x5C, 0xFE, 0xBA, 0xB8, 0xAC, 0x15,
+ 0xE8, 0xE0, 0x9A, 0xDD, 0x1F, 0xBC, 0x95, 0x42,
+ 0xCB, 0x58, 0x00, 0x85, 0xD5, 0x62, 0xC9, 0xB6,
+ 0x05, 0x80, 0x4C, 0x3C, 0x1C, 0xF5, 0x03, 0xF8,
+ 0x96, 0x77, 0x02, 0x19, 0xF2, 0xFB, 0x5F, 0xC2,
+ 0xAE, 0x60, 0x1B, 0xAD, 0x8F, 0xC1, 0x33, 0xA6,
+ 0x20, 0xBF, 0xA7, 0xC8, 0x74, 0x18, 0x90, 0xE3,
+ 0x68, 0x09, 0x7A, 0x79, 0xB5, 0xDA, 0xF3, 0x0E,
+ 0x66, 0x84, 0xB3, 0xBB, 0xE6, 0xF7, 0xB7, 0x7B,
+ 0x39, 0x4A, 0x12, 0x4F, 0xC5, 0x41, 0x54, 0xD0,
+ 0xFF, 0x87, 0x63, 0x40, 0x99, 0x21, 0x29, 0xD2,
+ 0x3D, 0x37, 0x3A, 0x93, 0xFC, 0x25, 0xF1, 0xD1,
+ 0x2C, 0x6D, 0x8C, 0x5A, 0x8E, 0x9B, 0xBD, 0xAF,
+ 0x10, 0x55, 0xF9, 0x9F, 0x43, 0x0A, 0x50, 0x16,
+ 0x57, 0xB1, 0xC0, 0x73, 0x82, 0xEF, 0x88, 0x6F,
+ 0xEA, 0x2A, 0xEC, 0x2E, 0x86, 0x45, 0x51, 0x22,
+ 0xA9, 0x34, 0x94, 0x3B, 0xB9, 0x9C, 0xA2, 0x13,
+ 0x89, 0x46, 0x78, 0xDC, 0x32, 0x8B, 0x67, 0x36 };
+
+unsigned char table_76[256] = {
+ 0x3D, 0x66, 0x40, 0xC5, 0x1D, 0xF5, 0xE7, 0xB7,
+ 0x2C, 0x23, 0x09, 0xC2, 0x68, 0xE6, 0xD3, 0x8D,
+ 0x35, 0x94, 0x93, 0xF0, 0x43, 0x97, 0x2B, 0x4B,
+ 0x1A, 0xEB, 0x00, 0x4C, 0x6F, 0xE4, 0x92, 0xEA,
+ 0xB8, 0xA3, 0xA6, 0xEC, 0x11, 0x5E, 0x61, 0x81,
+ 0xE1, 0x48, 0xC9, 0xCB, 0xDB, 0x2E, 0x3B, 0xED,
+ 0x36, 0x52, 0x3A, 0xD2, 0x4F, 0x4E, 0x22, 0x96,
+ 0x57, 0x2D, 0x62, 0x53, 0xCF, 0xD9, 0x5B, 0x9F,
+ 0x8E, 0x78, 0xC6, 0x07, 0x7D, 0xA1, 0x02, 0xB4,
+ 0xF4, 0xB6, 0x34, 0x98, 0xDA, 0xA9, 0xD4, 0x54,
+ 0x99, 0x82, 0x0A, 0xD8, 0x88, 0x5D, 0x3C, 0xD0,
+ 0xAB, 0x31, 0xFB, 0x03, 0x17, 0x46, 0xE8, 0xE2,
+ 0xA4, 0xFF, 0xB0, 0xAA, 0xAD, 0x7C, 0x55, 0x49,
+ 0x75, 0x6B, 0x10, 0x24, 0xC0, 0x04, 0xB1, 0xBF,
+ 0x6A, 0xF6, 0x15, 0xEF, 0x5C, 0x60, 0x27, 0x3E,
+ 0x38, 0x63, 0xC1, 0x76, 0xFD, 0x84, 0xE0, 0xCD,
+ 0xFE, 0x30, 0xCE, 0xBB, 0xDC, 0x1E, 0x1B, 0xBC,
+ 0xB5, 0xE9, 0x9E, 0x8F, 0x0D, 0x3F, 0x91, 0x19,
+ 0x28, 0x37, 0x26, 0x42, 0x08, 0x9A, 0x0C, 0x83,
+ 0x90, 0x6D, 0x74, 0x65, 0xF2, 0x4A, 0xDE, 0x8B,
+ 0x67, 0x0E, 0x8C, 0x5F, 0xF9, 0x7F, 0x5A, 0x86,
+ 0x69, 0x45, 0x44, 0xD5, 0xF7, 0xE5, 0x8A, 0xA8,
+ 0xC8, 0x7E, 0x05, 0x64, 0xEE, 0x79, 0xBE, 0x7A,
+ 0x14, 0xD6, 0x50, 0x18, 0x25, 0xBD, 0x85, 0xE3,
+ 0xA2, 0x70, 0xCC, 0x59, 0x71, 0x77, 0xFA, 0x47,
+ 0x9B, 0x1F, 0x9D, 0xBA, 0x29, 0x4D, 0xF8, 0xDF,
+ 0xC4, 0x72, 0x2F, 0xAE, 0x06, 0x51, 0x41, 0xAF,
+ 0xF3, 0xDD, 0x87, 0xB2, 0x9C, 0xC7, 0x12, 0x16,
+ 0x20, 0xA7, 0x21, 0x73, 0xF1, 0x58, 0xD7, 0x7B,
+ 0xB9, 0xB3, 0x32, 0x01, 0x80, 0x1C, 0x39, 0x0B,
+ 0x13, 0x56, 0x6C, 0x89, 0x33, 0x6E, 0x2A, 0xA5,
+ 0xD1, 0x95, 0xC3, 0xA0, 0x0F, 0xCA, 0xAC, 0xFC };
+
+unsigned char table_77[32] = {
+ 0x1C, 0x0D, 0x1E, 0x01, 0x06, 0x16, 0x18, 0x17,
+ 0x0B, 0x1F, 0x04, 0x0F, 0x00, 0x19, 0x08, 0x0A,
+ 0x11, 0x03, 0x05, 0x07, 0x09, 0x0C, 0x15, 0x14,
+ 0x1A, 0x12, 0x13, 0x0E, 0x1D, 0x10, 0x02, 0x1B };
+
+unsigned char table_78[32] = {
+ 0x0E, 0x02, 0x17, 0x12, 0x1E, 0x09, 0x15, 0x03,
+ 0x01, 0x0B, 0x0F, 0x11, 0x10, 0x0A, 0x16, 0x06,
+ 0x07, 0x00, 0x1C, 0x1D, 0x1F, 0x0C, 0x18, 0x04,
+ 0x13, 0x0D, 0x1B, 0x08, 0x19, 0x14, 0x05, 0x1A };
+
+unsigned char table_79[32] = {
+ 0x12, 0x0B, 0x11, 0x01, 0x07, 0x0E, 0x1A, 0x0D,
+ 0x1E, 0x18, 0x14, 0x1F, 0x0A, 0x17, 0x19, 0x1B,
+ 0x00, 0x10, 0x0C, 0x08, 0x13, 0x02, 0x0F, 0x1D,
+ 0x09, 0x06, 0x04, 0x16, 0x15, 0x1C, 0x05, 0x03 };
+
+unsigned char table_80[256] = {
+ 0x14, 0xE7, 0x31, 0x0F, 0xD1, 0x5F, 0xED, 0x1E,
+ 0xA6, 0x77, 0x20, 0x57, 0x34, 0x64, 0x33, 0x0B,
+ 0x5A, 0xB4, 0x83, 0x62, 0xFD, 0x8E, 0xE4, 0xF3,
+ 0xBD, 0xA5, 0xC8, 0x6D, 0x3E, 0x4F, 0x01, 0x7A,
+ 0xD3, 0x45, 0x3C, 0xF2, 0x68, 0xFF, 0xE6, 0x84,
+ 0xC2, 0xC1, 0x53, 0x72, 0x8C, 0xA1, 0xC7, 0x00,
+ 0x89, 0x97, 0x69, 0xA4, 0xF8, 0xAA, 0xAD, 0x8F,
+ 0x24, 0xC6, 0x9A, 0xAC, 0xE5, 0xAB, 0x6B, 0x79,
+ 0x99, 0x60, 0x28, 0x2B, 0x3B, 0xAF, 0x1C, 0x80,
+ 0xA3, 0x8A, 0x1A, 0xB5, 0xE1, 0x9F, 0xDA, 0x78,
+ 0xD7, 0xC4, 0x87, 0x5D, 0xE9, 0x27, 0xFB, 0x18,
+ 0x94, 0x3A, 0xCE, 0x3F, 0xF6, 0x12, 0x75, 0x37,
+ 0x6E, 0x9E, 0x29, 0x6C, 0xF7, 0x7D, 0x92, 0x08,
+ 0x42, 0xB2, 0xBF, 0x0C, 0xB6, 0x25, 0xE0, 0x49,
+ 0x43, 0x91, 0x98, 0xBB, 0xDC, 0x63, 0xEA, 0xA8,
+ 0x74, 0x38, 0x35, 0xCD, 0x07, 0x70, 0x81, 0x41,
+ 0xC9, 0x51, 0xBC, 0xA9, 0x59, 0xD4, 0xB8, 0x2C,
+ 0x7C, 0x2D, 0xB3, 0x6F, 0x11, 0x86, 0x9D, 0x46,
+ 0xF0, 0x65, 0x76, 0x04, 0x0E, 0xCA, 0xBE, 0x5C,
+ 0xF9, 0x71, 0x9C, 0x21, 0x4C, 0x02, 0xFE, 0x8D,
+ 0xD5, 0x26, 0x40, 0xC3, 0x32, 0x9B, 0xB0, 0x5E,
+ 0x48, 0xC5, 0x85, 0x4B, 0x0A, 0xCC, 0x58, 0x52,
+ 0x61, 0x13, 0xEF, 0x4A, 0xEE, 0x03, 0xD9, 0xDE,
+ 0xA7, 0x19, 0x09, 0x7F, 0x5B, 0x96, 0xBA, 0x0D,
+ 0xCF, 0xD2, 0x06, 0x1F, 0xD8, 0xDB, 0xEC, 0xA0,
+ 0xDD, 0x66, 0x10, 0xA2, 0xDF, 0x30, 0xF4, 0x88,
+ 0xCB, 0x36, 0x82, 0xE3, 0x73, 0x17, 0x55, 0x15,
+ 0xF5, 0xB7, 0x23, 0xB1, 0xD6, 0xE2, 0x47, 0x7E,
+ 0x67, 0xE8, 0x1D, 0x16, 0x8B, 0xEB, 0xD0, 0x3D,
+ 0x6A, 0x54, 0x2A, 0x4E, 0x93, 0xFA, 0x44, 0x05,
+ 0x2F, 0x50, 0x2E, 0x95, 0xAE, 0x1B, 0x56, 0x7B,
+ 0x39, 0xB9, 0xC0, 0x22, 0xF1, 0x4D, 0x90, 0xFC };
+
+unsigned char table_81[32] = {
+ 0x03, 0x02, 0x1D, 0x0E, 0x09, 0x1A, 0x0C, 0x11,
+ 0x1C, 0x0D, 0x08, 0x12, 0x19, 0x10, 0x04, 0x17,
+ 0x15, 0x05, 0x0A, 0x00, 0x13, 0x16, 0x1B, 0x18,
+ 0x1E, 0x0B, 0x0F, 0x01, 0x07, 0x14, 0x1F, 0x06 };
+
+unsigned char table_82[256] = {
+ 0x53, 0xD3, 0x64, 0x89, 0x7D, 0xA5, 0x66, 0xA4,
+ 0x09, 0x46, 0x17, 0x2C, 0xAF, 0x8C, 0x21, 0x5F,
+ 0x3B, 0x22, 0xE3, 0x05, 0x07, 0x28, 0x2F, 0xAB,
+ 0xF4, 0x8E, 0x51, 0x31, 0x02, 0xC7, 0x48, 0x13,
+ 0x24, 0x12, 0xB8, 0xE5, 0xBD, 0xAE, 0x7E, 0xCC,
+ 0xC9, 0x98, 0x08, 0xEE, 0xDB, 0x1B, 0xE8, 0x3D,
+ 0x8F, 0xF2, 0xFB, 0x36, 0x4D, 0x94, 0x9C, 0x16,
+ 0xF7, 0x42, 0x9B, 0x2B, 0xFD, 0x7B, 0x77, 0x3F,
+ 0xC3, 0xFC, 0x23, 0x93, 0x50, 0x0C, 0x79, 0x18,
+ 0x47, 0xE1, 0xCB, 0xA7, 0xB6, 0x85, 0xE6, 0x61,
+ 0x2D, 0xD8, 0x9F, 0x80, 0xE9, 0x14, 0x0B, 0x1C,
+ 0x40, 0x76, 0x2A, 0x25, 0x0E, 0x99, 0xAC, 0xC4,
+ 0xEB, 0x29, 0x41, 0x8A, 0x73, 0x06, 0x57, 0xC6,
+ 0x8D, 0xFA, 0x5A, 0xCD, 0x67, 0xB2, 0xD9, 0x0A,
+ 0x1E, 0xEF, 0x3E, 0xA0, 0x45, 0x03, 0x27, 0xF1,
+ 0x38, 0x54, 0xC1, 0x7A, 0xFE, 0x52, 0x75, 0xD4,
+ 0x74, 0x7C, 0xD2, 0x68, 0xEA, 0x4C, 0x97, 0xF9,
+ 0xF5, 0x8B, 0x0F, 0x84, 0xA8, 0x6E, 0x9E, 0x11,
+ 0x6B, 0xBC, 0x4B, 0x6C, 0x9A, 0xF0, 0xA3, 0x1F,
+ 0x92, 0x19, 0xA2, 0x3A, 0x15, 0x04, 0xC5, 0x62,
+ 0xD5, 0x96, 0x90, 0x32, 0xAA, 0xD6, 0xCF, 0x35,
+ 0xB4, 0x81, 0x2E, 0x01, 0x10, 0x49, 0x70, 0xDE,
+ 0xDD, 0x88, 0xB9, 0x6D, 0x60, 0xBB, 0x44, 0xF8,
+ 0x3C, 0xEC, 0x34, 0x82, 0x95, 0x72, 0x58, 0x4E,
+ 0xE4, 0x0D, 0xBE, 0xDA, 0x83, 0x4A, 0x00, 0xBF,
+ 0xD0, 0xC8, 0x26, 0xB3, 0x65, 0x1A, 0x69, 0xCA,
+ 0xF3, 0xD7, 0x6F, 0x55, 0xE2, 0xFF, 0x5D, 0xDC,
+ 0x20, 0xF6, 0x63, 0xED, 0xE0, 0x59, 0x9D, 0xB1,
+ 0x1D, 0xAD, 0x91, 0xA1, 0xB7, 0xA9, 0xDF, 0xC0,
+ 0x39, 0xD1, 0x43, 0xCE, 0x4F, 0x5C, 0xE7, 0x37,
+ 0x5E, 0x33, 0x5B, 0xA6, 0xC2, 0xB0, 0xBA, 0x30,
+ 0x6A, 0x78, 0xB5, 0x71, 0x56, 0x87, 0x7F, 0x86 };
+
+unsigned char table_83[32] = {
+ 0x1B, 0x0A, 0x1F, 0x01, 0x10, 0x08, 0x0E, 0x18,
+ 0x06, 0x04, 0x00, 0x1C, 0x0C, 0x19, 0x0D, 0x16,
+ 0x02, 0x03, 0x09, 0x07, 0x13, 0x0F, 0x05, 0x12,
+ 0x17, 0x1E, 0x1A, 0x1D, 0x0B, 0x11, 0x14, 0x15 };
+
+unsigned char table_84[32] = {
+ 0x02, 0x1A, 0x0D, 0x15, 0x01, 0x16, 0x1E, 0x00,
+ 0x08, 0x1B, 0x04, 0x10, 0x1C, 0x18, 0x19, 0x14,
+ 0x0C, 0x11, 0x0B, 0x0E, 0x03, 0x0A, 0x07, 0x12,
+ 0x1D, 0x17, 0x13, 0x06, 0x0F, 0x05, 0x09, 0x1F };
+
+unsigned char table_85[256] = {
+ 0xC6, 0x7C, 0xCE, 0xBD, 0x84, 0x3E, 0x0B, 0xD8,
+ 0xFE, 0xCC, 0x46, 0x50, 0xD1, 0xFB, 0xA0, 0x6D,
+ 0xEA, 0xE2, 0x40, 0x51, 0x13, 0xB0, 0xD6, 0xB1,
+ 0xA8, 0xDF, 0x61, 0xA4, 0x80, 0x21, 0xB3, 0x33,
+ 0x06, 0x6B, 0xE3, 0x8C, 0xA1, 0x18, 0xBA, 0x03,
+ 0xD7, 0x8D, 0x54, 0x12, 0x4C, 0xEE, 0x9E, 0xCF,
+ 0x04, 0x2A, 0x08, 0xBB, 0xC2, 0xD4, 0xC3, 0x4A,
+ 0xD5, 0xFA, 0x36, 0x2F, 0x14, 0x3F, 0xED, 0x05,
+ 0x17, 0x28, 0x75, 0xFC, 0xA2, 0x1F, 0x4B, 0x6F,
+ 0x91, 0x7E, 0x4E, 0x96, 0x3B, 0xF3, 0x1D, 0x78,
+ 0xEB, 0x68, 0xF1, 0xA7, 0x9F, 0xC7, 0x59, 0x6C,
+ 0x92, 0xE6, 0x66, 0x07, 0x8A, 0x25, 0x26, 0x72,
+ 0x30, 0x5A, 0x81, 0x2C, 0x58, 0x32, 0xCB, 0xE0,
+ 0xF9, 0x48, 0x83, 0x9B, 0xA5, 0xE1, 0xA6, 0x64,
+ 0xFF, 0xC9, 0x8F, 0x53, 0x3D, 0x24, 0xC8, 0xDE,
+ 0x02, 0x7D, 0x09, 0xB4, 0x0A, 0x95, 0x0F, 0xE4,
+ 0xDB, 0xB7, 0x71, 0x4D, 0x1C, 0xAC, 0x35, 0xCD,
+ 0x29, 0xDD, 0xC1, 0xF2, 0xF4, 0xC0, 0x5C, 0x74,
+ 0xDC, 0x87, 0xFD, 0x4F, 0x11, 0x0E, 0x5D, 0x3C,
+ 0x01, 0x73, 0xE9, 0xD9, 0x10, 0x9A, 0x5B, 0xC5,
+ 0x98, 0x34, 0x15, 0xAE, 0xF7, 0xAA, 0x67, 0x23,
+ 0xBC, 0x8B, 0x7B, 0x65, 0xA9, 0xB6, 0x77, 0x00,
+ 0x19, 0x0C, 0x5E, 0x99, 0xF0, 0x55, 0x86, 0x97,
+ 0x69, 0xDA, 0x38, 0x9C, 0x16, 0xE8, 0x27, 0xAF,
+ 0x2E, 0x47, 0x6A, 0xD0, 0x79, 0x44, 0x45, 0x2B,
+ 0x5F, 0x85, 0xF5, 0x62, 0x70, 0x22, 0x7F, 0xF6,
+ 0x88, 0x93, 0x60, 0x42, 0x3A, 0x39, 0x49, 0x6E,
+ 0x89, 0x52, 0x20, 0xF8, 0xCA, 0xD2, 0x76, 0xB9,
+ 0xAB, 0x7A, 0x9D, 0xD3, 0xBE, 0x1A, 0xAD, 0x41,
+ 0x56, 0x31, 0x90, 0xB5, 0xB2, 0xEC, 0xA3, 0xE5,
+ 0x8E, 0x1B, 0xEF, 0xBF, 0x94, 0xC4, 0x0D, 0xB8,
+ 0x2D, 0x57, 0xE7, 0x82, 0x1E, 0x37, 0x63, 0x43 };
+
+unsigned char table_86[32] = {
+ 0x11, 0x07, 0x0F, 0x0A, 0x19, 0x1D, 0x0B, 0x09,
+ 0x1C, 0x1E, 0x14, 0x06, 0x0C, 0x16, 0x13, 0x04,
+ 0x15, 0x18, 0x00, 0x0D, 0x12, 0x05, 0x08, 0x02,
+ 0x10, 0x1A, 0x1F, 0x01, 0x17, 0x0E, 0x03, 0x1B };
+
+unsigned char table_87[32] = {
+ 0x17, 0x0E, 0x1D, 0x13, 0x0B, 0x19, 0x03, 0x06,
+ 0x09, 0x01, 0x0D, 0x15, 0x1C, 0x16, 0x18, 0x1B,
+ 0x11, 0x10, 0x00, 0x1E, 0x1F, 0x08, 0x12, 0x0F,
+ 0x02, 0x04, 0x07, 0x1A, 0x14, 0x0A, 0x0C, 0x05 };
+
+unsigned char table_88[32] = {
+ 0x09, 0x08, 0x17, 0x10, 0x0A, 0x07, 0x1C, 0x1F,
+ 0x04, 0x0E, 0x01, 0x0C, 0x0D, 0x1B, 0x03, 0x15,
+ 0x02, 0x1E, 0x18, 0x19, 0x0F, 0x06, 0x1A, 0x0B,
+ 0x05, 0x11, 0x14, 0x00, 0x16, 0x1D, 0x12, 0x13 };
+
+unsigned char table_89[32] = {
+ 0x15, 0x1C, 0x1D, 0x14, 0x0F, 0x1A, 0x05, 0x02,
+ 0x07, 0x09, 0x06, 0x08, 0x1F, 0x00, 0x10, 0x13,
+ 0x0D, 0x03, 0x0C, 0x18, 0x0E, 0x16, 0x1B, 0x1E,
+ 0x12, 0x04, 0x11, 0x0A, 0x01, 0x0B, 0x17, 0x19 };
+
+unsigned char table_90[256] = {
+ 0x62, 0x36, 0x64, 0x0E, 0x4C, 0x6C, 0xBE, 0xCF,
+ 0x25, 0x5A, 0x3D, 0x12, 0x54, 0x9F, 0xE7, 0xA5,
+ 0xDE, 0xD7, 0xB2, 0x60, 0x18, 0x8D, 0x89, 0x70,
+ 0x48, 0x66, 0x1C, 0xA6, 0x17, 0x9B, 0xDF, 0x9A,
+ 0x82, 0xB9, 0x2E, 0xFA, 0x83, 0x5B, 0x7A, 0x61,
+ 0xFC, 0x6B, 0x8B, 0x4E, 0x0F, 0xAD, 0x78, 0xE1,
+ 0xE8, 0x15, 0x1A, 0xF7, 0xA3, 0x3A, 0x04, 0xE3,
+ 0x30, 0x8C, 0x06, 0xC4, 0x05, 0x32, 0x1F, 0x6A,
+ 0xB8, 0x37, 0x58, 0xF5, 0x74, 0x63, 0xD4, 0xAC,
+ 0xA4, 0xF3, 0xEC, 0xBB, 0x8E, 0x65, 0xA0, 0xEE,
+ 0x6D, 0x11, 0xDD, 0xEA, 0x68, 0x2B, 0xDA, 0x0B,
+ 0xEF, 0xC3, 0x8F, 0x03, 0x77, 0x1B, 0xFB, 0x1E,
+ 0x5C, 0xD9, 0xCB, 0x33, 0x55, 0xF1, 0xA1, 0xF9,
+ 0x7C, 0x38, 0x95, 0x00, 0x6E, 0x85, 0xC2, 0x7F,
+ 0xBF, 0x84, 0x2A, 0x13, 0x72, 0x81, 0xE9, 0x59,
+ 0x41, 0x69, 0x3B, 0x0C, 0x90, 0xB4, 0x51, 0x2F,
+ 0xA2, 0xFE, 0xF8, 0x49, 0x57, 0xE5, 0x96, 0xFF,
+ 0xCD, 0xD5, 0xCE, 0xAA, 0x40, 0xB0, 0x4D, 0xBA,
+ 0xDB, 0xC7, 0x46, 0x86, 0xD1, 0xCA, 0xC0, 0x67,
+ 0x9C, 0x21, 0xAE, 0xB3, 0x7B, 0x87, 0xE2, 0x71,
+ 0xE6, 0x39, 0xA8, 0x22, 0x07, 0x2C, 0x44, 0x52,
+ 0xA7, 0xF0, 0x4A, 0x92, 0x56, 0x28, 0x43, 0x8A,
+ 0x5E, 0x53, 0x93, 0x47, 0x97, 0x88, 0x76, 0x79,
+ 0x91, 0x26, 0xC1, 0x3F, 0xB7, 0xF6, 0x3E, 0x80,
+ 0xA9, 0xC6, 0x01, 0xD2, 0xEB, 0x9E, 0x4B, 0xBC,
+ 0xC8, 0xB5, 0x02, 0x5F, 0x98, 0x9D, 0x5D, 0x35,
+ 0xD0, 0x16, 0xB1, 0x23, 0x7D, 0xAF, 0x10, 0x3C,
+ 0xAB, 0x14, 0x09, 0x2D, 0x0D, 0xC5, 0x1D, 0xD6,
+ 0x42, 0xF2, 0x34, 0x73, 0xF4, 0xFD, 0xE0, 0x24,
+ 0x6F, 0xD3, 0x75, 0xD8, 0xCC, 0xB6, 0x99, 0x4F,
+ 0x29, 0x0A, 0x08, 0xE4, 0x27, 0x19, 0x31, 0xC9,
+ 0x20, 0x94, 0x45, 0xED, 0xDC, 0xBD, 0x7E, 0x50 };
+
+unsigned char table_91[32] = {
+ 0x03, 0x04, 0x0C, 0x18, 0x10, 0x0D, 0x13, 0x1B,
+ 0x1F, 0x07, 0x11, 0x17, 0x1C, 0x1D, 0x05, 0x06,
+ 0x0A, 0x12, 0x02, 0x1A, 0x0B, 0x01, 0x0E, 0x08,
+ 0x14, 0x16, 0x00, 0x15, 0x19, 0x09, 0x0F, 0x1E };
+
+unsigned char table_92[32] = {
+ 0x1E, 0x10, 0x01, 0x07, 0x11, 0x16, 0x15, 0x17,
+ 0x1F, 0x14, 0x0C, 0x1C, 0x06, 0x03, 0x00, 0x18,
+ 0x08, 0x0E, 0x02, 0x1B, 0x09, 0x0D, 0x19, 0x05,
+ 0x0F, 0x12, 0x0B, 0x13, 0x0A, 0x04, 0x1D, 0x1A };
+
+unsigned char table_93[256] = {
+ 0x76, 0x78, 0xA2, 0x94, 0x0E, 0x7F, 0xDF, 0xC1,
+ 0xB9, 0xE1, 0x3D, 0x59, 0x6F, 0x1E, 0x53, 0x99,
+ 0x80, 0xE3, 0x21, 0xF8, 0x65, 0xB8, 0x08, 0xBC,
+ 0x29, 0x17, 0xFD, 0x33, 0x35, 0xF2, 0x70, 0xC7,
+ 0x25, 0xD0, 0xCD, 0x7A, 0xB7, 0x9B, 0xA5, 0xC3,
+ 0x00, 0x90, 0xDC, 0xB1, 0x0C, 0x20, 0x67, 0x8D,
+ 0x43, 0x49, 0xF3, 0x96, 0x14, 0x1A, 0xC8, 0x19,
+ 0x72, 0xD7, 0x8A, 0x38, 0x66, 0xDA, 0xDD, 0x2E,
+ 0xBE, 0xD5, 0x91, 0x7C, 0x3A, 0x92, 0x8E, 0xE7,
+ 0x51, 0xB5, 0xA8, 0xD9, 0x0B, 0x2A, 0xBA, 0x81,
+ 0x41, 0x0F, 0xBD, 0x4E, 0x31, 0x23, 0x9C, 0x8B,
+ 0x2B, 0x1D, 0x04, 0x3E, 0x8C, 0xF0, 0x45, 0xA0,
+ 0x1C, 0x44, 0x55, 0x5E, 0xF1, 0x98, 0x54, 0x5D,
+ 0x9D, 0x84, 0xAE, 0x09, 0xA9, 0xC5, 0x83, 0x60,
+ 0x86, 0x95, 0xB4, 0xFA, 0x6B, 0xA7, 0x9A, 0xCA,
+ 0x8F, 0x4F, 0x0A, 0x7B, 0xB0, 0x02, 0xEA, 0xA4,
+ 0x18, 0xDB, 0xD3, 0x64, 0xEB, 0xFC, 0xC4, 0xC9,
+ 0xF5, 0xD6, 0xCC, 0x75, 0x0D, 0x5C, 0x93, 0x4A,
+ 0x6D, 0xC0, 0x1F, 0x50, 0xE6, 0x16, 0xEE, 0x07,
+ 0xFB, 0x74, 0x56, 0x58, 0x52, 0x89, 0x79, 0x68,
+ 0xB6, 0xFE, 0x01, 0xD4, 0x7E, 0x06, 0xBF, 0xCB,
+ 0x5B, 0xC2, 0xC6, 0x32, 0xAC, 0x26, 0x22, 0xD2,
+ 0x82, 0x46, 0x69, 0x15, 0x2C, 0xF7, 0xAD, 0x13,
+ 0x4D, 0xA3, 0xF6, 0x2D, 0x48, 0x71, 0x57, 0x11,
+ 0x63, 0x05, 0x5F, 0x9E, 0x4B, 0xAB, 0xA6, 0x61,
+ 0xBB, 0xA1, 0x3C, 0x97, 0xF9, 0x03, 0x40, 0x12,
+ 0xCF, 0x37, 0xE4, 0x10, 0x6A, 0xED, 0xFF, 0x62,
+ 0x42, 0x4C, 0xAF, 0x9F, 0xE5, 0xE8, 0xD8, 0xD1,
+ 0x28, 0x3F, 0x1B, 0xE9, 0xCE, 0x6C, 0x27, 0x88,
+ 0xEF, 0x2F, 0xE0, 0x30, 0x87, 0x5A, 0x73, 0xB3,
+ 0x6E, 0x3B, 0x7D, 0x77, 0x36, 0xAA, 0x39, 0xDE,
+ 0x24, 0x34, 0xE2, 0xEC, 0x85, 0x47, 0xF4, 0xB2 };
+
+unsigned char table_94[32] = {
+ 0x1C, 0x07, 0x05, 0x1A, 0x10, 0x1D, 0x14, 0x12,
+ 0x08, 0x0F, 0x0C, 0x01, 0x04, 0x1B, 0x16, 0x0A,
+ 0x11, 0x02, 0x1F, 0x13, 0x0D, 0x1E, 0x17, 0x06,
+ 0x0E, 0x09, 0x15, 0x19, 0x03, 0x18, 0x00, 0x0B };
+
+unsigned char table_95[32] = {
+ 0x12, 0x10, 0x11, 0x15, 0x03, 0x0A, 0x14, 0x05,
+ 0x1D, 0x07, 0x17, 0x0D, 0x09, 0x08, 0x1B, 0x1F,
+ 0x0B, 0x06, 0x19, 0x0E, 0x18, 0x04, 0x00, 0x02,
+ 0x1E, 0x1C, 0x01, 0x0C, 0x1A, 0x0F, 0x13, 0x16 };
+
+unsigned char table_96[256] = {
+ 0x1C, 0x6E, 0xCD, 0xB4, 0xB3, 0x93, 0xA8, 0x2E,
+ 0x4F, 0x09, 0xE3, 0x72, 0x64, 0x13, 0x21, 0xF5,
+ 0x89, 0xB2, 0xD2, 0x22, 0x5D, 0x63, 0x90, 0xC4,
+ 0x42, 0x9B, 0x07, 0xCA, 0x16, 0x19, 0x5C, 0x2B,
+ 0x3D, 0xA0, 0x69, 0x5F, 0x52, 0x41, 0x66, 0xC0,
+ 0x55, 0xDA, 0x82, 0x40, 0x25, 0x02, 0x3C, 0xDD,
+ 0xAE, 0xD7, 0xD6, 0xDB, 0x04, 0x78, 0x05, 0x4A,
+ 0x4C, 0x81, 0x00, 0xBE, 0x45, 0xC5, 0x30, 0xB0,
+ 0x65, 0x5A, 0xA9, 0x38, 0x75, 0x26, 0x85, 0x4E,
+ 0xF0, 0xA2, 0x91, 0x8A, 0x54, 0xD0, 0x3E, 0x0D,
+ 0xFE, 0xF2, 0x0A, 0x23, 0x24, 0x37, 0x32, 0x0B,
+ 0xCB, 0xB5, 0x28, 0x6A, 0x95, 0x49, 0x53, 0x9A,
+ 0xEE, 0x2C, 0x9D, 0xD4, 0x1D, 0x46, 0xC9, 0x79,
+ 0xCC, 0xDF, 0x17, 0xE8, 0x6D, 0x29, 0x0E, 0x80,
+ 0xE0, 0x62, 0xA1, 0xFA, 0x10, 0xF6, 0x03, 0xC1,
+ 0x15, 0x14, 0x1F, 0x99, 0x97, 0xD5, 0x9E, 0x3F,
+ 0x7B, 0x2F, 0xEF, 0x2A, 0x68, 0x83, 0xE2, 0x1B,
+ 0xC8, 0x87, 0x12, 0x70, 0xC7, 0x36, 0xD3, 0x73,
+ 0x8B, 0x7D, 0x47, 0x9F, 0xD9, 0xFB, 0x6C, 0x5B,
+ 0xFC, 0xAA, 0xB9, 0xB1, 0x0C, 0x31, 0x8E, 0xF3,
+ 0x92, 0xA3, 0x4B, 0xF1, 0xC2, 0x3A, 0x67, 0xEA,
+ 0x77, 0x11, 0xB6, 0xE4, 0x1A, 0x33, 0xD1, 0xBA,
+ 0xF9, 0xAC, 0x43, 0xE5, 0xC3, 0xC6, 0xFD, 0xF4,
+ 0x44, 0x6F, 0xB7, 0x88, 0xA7, 0xF8, 0x34, 0x94,
+ 0x6B, 0x27, 0xDE, 0x1E, 0xDC, 0x01, 0x61, 0x50,
+ 0xAD, 0x74, 0x4D, 0x86, 0xF7, 0x8D, 0x9C, 0x0F,
+ 0x5E, 0xBD, 0x08, 0x84, 0x18, 0xED, 0xA5, 0x39,
+ 0xAB, 0x98, 0x48, 0xE6, 0x2D, 0x96, 0xCF, 0x7F,
+ 0xFF, 0xBB, 0x8F, 0xEC, 0xBF, 0xE7, 0x56, 0xA4,
+ 0x35, 0x76, 0xA6, 0xAF, 0xBC, 0x71, 0xE9, 0xB8,
+ 0x7E, 0x7C, 0x06, 0x3B, 0xEB, 0x60, 0x7A, 0x8C,
+ 0x59, 0xCE, 0xE1, 0x57, 0x20, 0x58, 0x51, 0xD8 };
+
+unsigned char table_97[256] = {
+ 0x15, 0x2D, 0xAF, 0x36, 0xCF, 0xD3, 0xD0, 0xED,
+ 0xB2, 0x1B, 0xFE, 0x92, 0xBD, 0xAD, 0x58, 0x0F,
+ 0x76, 0x3C, 0x47, 0x03, 0x2E, 0x4C, 0x40, 0xF7,
+ 0x39, 0xA7, 0x72, 0x22, 0x95, 0xF3, 0x8C, 0xE0,
+ 0x79, 0xB6, 0x75, 0x82, 0x94, 0x8F, 0x44, 0xFC,
+ 0xB0, 0x05, 0xE9, 0x10, 0x68, 0xE7, 0xF1, 0xA5,
+ 0xA8, 0xE2, 0x6F, 0xBE, 0xE5, 0x54, 0xA2, 0xC6,
+ 0xDB, 0x1C, 0x9E, 0x6D, 0x14, 0xA1, 0x26, 0x34,
+ 0x1E, 0x1A, 0x06, 0x53, 0xEE, 0x67, 0xA9, 0x73,
+ 0xD5, 0x59, 0x2F, 0x61, 0xE6, 0x74, 0xD6, 0x97,
+ 0xC0, 0x0C, 0xB1, 0x6E, 0x6C, 0x33, 0xC8, 0x77,
+ 0x8B, 0x49, 0x43, 0xE3, 0xB5, 0xDE, 0x6A, 0xA0,
+ 0x78, 0x2A, 0xC9, 0xF9, 0x9A, 0xDC, 0x90, 0x55,
+ 0xF4, 0x16, 0x5E, 0x3F, 0xC5, 0x7C, 0xFA, 0x09,
+ 0x8E, 0x87, 0xF2, 0x9D, 0x70, 0x27, 0x9B, 0xC4,
+ 0xCD, 0x91, 0x4B, 0xB4, 0x18, 0xE1, 0x3D, 0x5D,
+ 0x7A, 0xEA, 0xF0, 0x65, 0xB9, 0xF6, 0xC3, 0x66,
+ 0x21, 0x96, 0xD1, 0xB8, 0x56, 0x62, 0x48, 0x28,
+ 0x3A, 0x86, 0x63, 0xD4, 0xD7, 0x41, 0x8D, 0x20,
+ 0xC2, 0x98, 0x37, 0xD8, 0x85, 0x42, 0x0D, 0x31,
+ 0x84, 0x4E, 0x11, 0x46, 0x2B, 0x19, 0xCC, 0xB7,
+ 0x69, 0x13, 0x6B, 0x29, 0x38, 0x7E, 0x0E, 0xD2,
+ 0x3B, 0x60, 0x89, 0x7F, 0xEF, 0x07, 0x08, 0xCA,
+ 0xBF, 0x3E, 0xA3, 0xAA, 0x52, 0x4A, 0x45, 0x00,
+ 0xC7, 0xF8, 0x57, 0xEB, 0x93, 0x9C, 0x4D, 0x7B,
+ 0x2C, 0xBB, 0xFB, 0xFF, 0x35, 0x4F, 0x32, 0xA6,
+ 0x23, 0x8A, 0xDD, 0x12, 0xA4, 0x81, 0x17, 0x1D,
+ 0x1F, 0xCB, 0x0A, 0x71, 0x02, 0xAC, 0xDF, 0x24,
+ 0xAB, 0x7D, 0x30, 0x5C, 0x01, 0x5A, 0xBA, 0xEC,
+ 0x51, 0xF5, 0x0B, 0x64, 0xCE, 0xAE, 0x5B, 0x50,
+ 0x80, 0x88, 0xE8, 0x5F, 0x04, 0xDA, 0xE4, 0xBC,
+ 0x83, 0x25, 0x9F, 0xD9, 0x99, 0xC1, 0xFD, 0xB3 };
+
+unsigned char table_98[256] = {
+ 0xC8, 0xE6, 0x38, 0x93, 0xE5, 0x03, 0x18, 0x1F,
+ 0xE9, 0x5A, 0xB6, 0xAF, 0xC3, 0x95, 0x00, 0x51,
+ 0xC0, 0xFD, 0x32, 0xE8, 0x96, 0x57, 0xF0, 0xAA,
+ 0xDC, 0x71, 0xF8, 0x01, 0x40, 0x0A, 0x4F, 0xB0,
+ 0x1B, 0x9D, 0x16, 0x92, 0xF3, 0x5E, 0xA9, 0x3C,
+ 0xBE, 0x6A, 0xA7, 0xE3, 0x35, 0x0D, 0xAD, 0xDB,
+ 0x48, 0xE0, 0x7E, 0xC6, 0xB4, 0x6D, 0x17, 0x41,
+ 0x3E, 0xE2, 0x87, 0x12, 0xE1, 0x53, 0xD9, 0x8A,
+ 0xAC, 0xA6, 0xD8, 0xFA, 0x36, 0x0B, 0x06, 0xDF,
+ 0x6C, 0x4E, 0xA4, 0xBC, 0xC9, 0xEE, 0x44, 0x26,
+ 0xF2, 0xE4, 0x9E, 0x34, 0xEF, 0x05, 0x0F, 0x7F,
+ 0xD1, 0xCD, 0x67, 0x28, 0xC1, 0x8E, 0x7D, 0x90,
+ 0x8F, 0x60, 0x1E, 0x19, 0xBD, 0x77, 0xB8, 0xD5,
+ 0x3D, 0x8C, 0x31, 0x99, 0x08, 0xDD, 0x04, 0x30,
+ 0x61, 0xFB, 0xEB, 0x98, 0x15, 0xFC, 0x10, 0xDE,
+ 0x20, 0xBA, 0xA1, 0xB3, 0xD4, 0x91, 0x6F, 0x9F,
+ 0x94, 0x5B, 0x42, 0xCB, 0x75, 0x1C, 0xBB, 0x5C,
+ 0x5D, 0xD6, 0x66, 0x50, 0xB9, 0xF1, 0x82, 0x7B,
+ 0x33, 0x23, 0x4A, 0xA5, 0x55, 0x97, 0xEA, 0x37,
+ 0xF4, 0x64, 0x6E, 0xBF, 0x8B, 0xB1, 0x07, 0x9A,
+ 0x43, 0x11, 0x65, 0xC2, 0x02, 0xDA, 0x9B, 0x25,
+ 0xCA, 0x3B, 0x7A, 0xCE, 0xA8, 0xCF, 0xF7, 0x56,
+ 0x6B, 0xF9, 0x47, 0x2A, 0x2E, 0x1D, 0x2D, 0xE7,
+ 0x46, 0xD0, 0x62, 0x4C, 0x80, 0x4B, 0x2B, 0xF5,
+ 0x69, 0x9C, 0x45, 0xED, 0x83, 0xAB, 0x74, 0x39,
+ 0xA3, 0x85, 0xD7, 0x5F, 0xB2, 0x86, 0x22, 0x29,
+ 0x89, 0x49, 0x1A, 0xC4, 0x52, 0xEC, 0x8D, 0x73,
+ 0xD3, 0x7C, 0x79, 0xD2, 0x14, 0x4D, 0x84, 0xA2,
+ 0x0E, 0x70, 0x78, 0x72, 0xB7, 0xA0, 0xC5, 0x81,
+ 0x58, 0x0C, 0x68, 0x27, 0xFF, 0xF6, 0xAE, 0xCC,
+ 0x88, 0xFE, 0x24, 0x2F, 0x76, 0x3F, 0x59, 0x21,
+ 0x54, 0x3A, 0x13, 0x09, 0x2C, 0xB5, 0xC7, 0x63 };
+
+unsigned char table_99[32] = {
+ 0x19, 0x00, 0x10, 0x18, 0x09, 0x11, 0x13, 0x1D,
+ 0x08, 0x1A, 0x02, 0x05, 0x03, 0x17, 0x12, 0x01,
+ 0x1F, 0x14, 0x06, 0x07, 0x15, 0x0D, 0x0F, 0x0B,
+ 0x0E, 0x16, 0x1E, 0x04, 0x1B, 0x0A, 0x0C, 0x1C };
+
+unsigned char table_100[256] = {
+ 0x9B, 0x3A, 0xAE, 0x60, 0x27, 0x67, 0x1E, 0x4E,
+ 0x91, 0xDA, 0x85, 0x43, 0x5C, 0xCC, 0x89, 0x55,
+ 0x75, 0x56, 0xF2, 0x86, 0xEB, 0xC4, 0x0D, 0xE6,
+ 0x63, 0x88, 0x38, 0x59, 0x68, 0xD0, 0x18, 0xF0,
+ 0xBA, 0x28, 0xF5, 0x80, 0x02, 0x5B, 0xE1, 0xA4,
+ 0x7A, 0x4B, 0x8E, 0xF7, 0x9E, 0x99, 0x70, 0xEF,
+ 0x66, 0x50, 0xB1, 0xCD, 0x9A, 0xAF, 0x5F, 0x21,
+ 0xE5, 0x5D, 0x14, 0xD4, 0x34, 0x22, 0xC3, 0x0F,
+ 0x44, 0xB6, 0x92, 0xCE, 0xB4, 0x6E, 0xB0, 0x00,
+ 0xF9, 0xB5, 0x10, 0xEA, 0x45, 0x2F, 0x2B, 0xF4,
+ 0xF6, 0xFE, 0xCB, 0x0A, 0x42, 0xF8, 0xE7, 0xFD,
+ 0xC8, 0xC2, 0x6C, 0x9C, 0x57, 0xA1, 0x46, 0x04,
+ 0xE9, 0x97, 0x40, 0x32, 0x19, 0xFA, 0x51, 0xD1,
+ 0x6D, 0x4C, 0x2A, 0xD9, 0x95, 0x26, 0x72, 0x1B,
+ 0x83, 0x93, 0x5A, 0x15, 0x33, 0xC5, 0x77, 0x13,
+ 0xE0, 0x36, 0x37, 0xDB, 0xA7, 0xC7, 0x81, 0x62,
+ 0xC1, 0x47, 0x64, 0x74, 0x1D, 0x84, 0x29, 0x39,
+ 0x41, 0x35, 0x09, 0x90, 0x20, 0x9F, 0x8C, 0x7D,
+ 0x3E, 0x07, 0xB9, 0x76, 0x06, 0xA3, 0x31, 0x7F,
+ 0x49, 0x6F, 0x3D, 0xD5, 0x25, 0xAC, 0xDF, 0x0B,
+ 0x3C, 0x79, 0x01, 0x8F, 0x82, 0x2E, 0xFC, 0x98,
+ 0xA5, 0x58, 0xA0, 0x4A, 0x7C, 0x24, 0xDD, 0x05,
+ 0x4D, 0x12, 0xBC, 0xAA, 0xE2, 0xAB, 0xD3, 0xBF,
+ 0x94, 0x2D, 0x54, 0xBB, 0xAD, 0xB7, 0x6A, 0xE3,
+ 0xBD, 0x5E, 0x8D, 0x08, 0x3B, 0xB8, 0x73, 0x8A,
+ 0x16, 0xD2, 0x69, 0xE8, 0xEE, 0x53, 0xD8, 0xDC,
+ 0x48, 0xCF, 0xC6, 0xA9, 0x1A, 0xCA, 0x17, 0x11,
+ 0xED, 0xC0, 0xA6, 0x1F, 0x96, 0x8B, 0xFF, 0x78,
+ 0x03, 0x61, 0x1C, 0xA8, 0x3F, 0x9D, 0x0E, 0xC9,
+ 0xE4, 0xA2, 0x52, 0xEC, 0x4F, 0xD6, 0xF3, 0x6B,
+ 0x87, 0xB3, 0x7E, 0xDE, 0xD7, 0x71, 0x65, 0xF1,
+ 0x30, 0x0C, 0xB2, 0x7B, 0xBE, 0xFB, 0x23, 0x2C };
+
+unsigned char table_101[32] = {
+ 0x18, 0x08, 0x14, 0x17, 0x03, 0x10, 0x19, 0x04,
+ 0x0D, 0x1C, 0x06, 0x1D, 0x1E, 0x12, 0x11, 0x0B,
+ 0x0F, 0x02, 0x0E, 0x1B, 0x13, 0x05, 0x07, 0x16,
+ 0x15, 0x0A, 0x0C, 0x1A, 0x00, 0x01, 0x1F, 0x09 };
+
+unsigned char table_102[32] = {
+ 0x17, 0x1F, 0x0E, 0x05, 0x13, 0x0C, 0x14, 0x1A,
+ 0x0F, 0x01, 0x12, 0x1C, 0x00, 0x07, 0x0D, 0x02,
+ 0x10, 0x16, 0x04, 0x11, 0x1D, 0x03, 0x1E, 0x18,
+ 0x06, 0x15, 0x0A, 0x19, 0x09, 0x08, 0x1B, 0x0B };
+
+unsigned char table_103[32] = {
+ 0x0F, 0x09, 0x1E, 0x11, 0x0D, 0x08, 0x10, 0x00,
+ 0x01, 0x1F, 0x1D, 0x1C, 0x12, 0x04, 0x07, 0x05,
+ 0x19, 0x14, 0x1B, 0x02, 0x1A, 0x15, 0x17, 0x16,
+ 0x18, 0x0B, 0x0A, 0x13, 0x0C, 0x0E, 0x03, 0x06 };
+
+unsigned char table_104[256] = {
+ 0xA4, 0x9F, 0x78, 0x39, 0x3D, 0x81, 0x51, 0x24,
+ 0x46, 0x2A, 0x56, 0xE8, 0xDF, 0x73, 0xA8, 0xA2,
+ 0x0D, 0xDC, 0xA5, 0x4F, 0xF0, 0x93, 0xC0, 0x76,
+ 0x38, 0x70, 0xB0, 0x30, 0x98, 0x13, 0x8B, 0x14,
+ 0x26, 0x45, 0x0F, 0x7D, 0x34, 0x72, 0x6B, 0x89,
+ 0x43, 0xE2, 0x96, 0x5B, 0xEF, 0x2B, 0xF9, 0xDE,
+ 0x82, 0xB5, 0x61, 0x4A, 0x17, 0xC2, 0x5A, 0xCB,
+ 0xB2, 0x8D, 0xE4, 0xEC, 0xD9, 0x80, 0xBC, 0x62,
+ 0x67, 0x11, 0xA9, 0x3A, 0xE1, 0xC4, 0xEA, 0xD2,
+ 0x71, 0xD0, 0xDB, 0xE5, 0x7B, 0x08, 0x77, 0xD6,
+ 0x10, 0x19, 0x48, 0xEB, 0xAA, 0x2C, 0x0C, 0x59,
+ 0xBE, 0xF6, 0x28, 0x50, 0x90, 0x87, 0xCD, 0x04,
+ 0x1F, 0x79, 0x99, 0x5C, 0x49, 0x06, 0x8A, 0x3E,
+ 0x5F, 0x5E, 0x15, 0x23, 0x2D, 0xB6, 0xA6, 0x7A,
+ 0x03, 0x20, 0xDA, 0xFB, 0x35, 0x75, 0xC7, 0x47,
+ 0xB9, 0x7C, 0xA1, 0xCE, 0xC5, 0xDD, 0xFD, 0x6C,
+ 0x05, 0xAC, 0x09, 0xB4, 0x95, 0xD1, 0xB1, 0x63,
+ 0xFF, 0xAE, 0xD5, 0x25, 0x1E, 0x6E, 0x57, 0x18,
+ 0x74, 0xE6, 0x2F, 0x9A, 0xE7, 0x42, 0x65, 0xF5,
+ 0x58, 0x27, 0x33, 0x9C, 0xCF, 0xB7, 0xC3, 0xF1,
+ 0x12, 0x1D, 0xB8, 0xF4, 0x64, 0x4D, 0xD4, 0xBD,
+ 0xE3, 0xAB, 0x44, 0x60, 0xAF, 0xCC, 0x0A, 0xFC,
+ 0xD3, 0x21, 0x0B, 0x1A, 0x6D, 0x83, 0xA7, 0x8E,
+ 0x3C, 0xC1, 0xED, 0xF3, 0x2E, 0x86, 0xC9, 0x41,
+ 0x02, 0xF7, 0xC8, 0x40, 0x1B, 0xF8, 0xF2, 0x07,
+ 0x5D, 0x4E, 0xC6, 0x29, 0xD7, 0x4B, 0x7E, 0x31,
+ 0x94, 0x32, 0x01, 0x92, 0xE9, 0x36, 0x0E, 0x7F,
+ 0x85, 0x16, 0xFA, 0x00, 0x88, 0x3F, 0x68, 0x4C,
+ 0x22, 0x55, 0xBF, 0x9D, 0xE0, 0x6A, 0xAD, 0xBA,
+ 0x91, 0xCA, 0xA3, 0x1C, 0xEE, 0xD8, 0x3B, 0x66,
+ 0x69, 0x9B, 0x84, 0xA0, 0xB3, 0x6F, 0xFE, 0x52,
+ 0x97, 0xBB, 0x37, 0x8C, 0x54, 0x53, 0x9E, 0x8F };
+
+unsigned char table_105[256] = {
+ 0x7B, 0x35, 0x11, 0x79, 0x07, 0x2F, 0xF6, 0x82,
+ 0x8E, 0xB4, 0x6E, 0xD2, 0x6D, 0xC5, 0x8C, 0x1C,
+ 0xE0, 0xD6, 0x34, 0xF0, 0x4F, 0x25, 0x59, 0xE8,
+ 0xDF, 0x1D, 0xEB, 0x32, 0x86, 0x51, 0xA4, 0xF2,
+ 0x5C, 0xD1, 0xC8, 0x41, 0xEC, 0x9D, 0x62, 0xAC,
+ 0xDD, 0x3E, 0xB8, 0x65, 0x75, 0x89, 0x12, 0x6C,
+ 0x40, 0x4E, 0xC7, 0x27, 0xE1, 0x37, 0xCF, 0x09,
+ 0x16, 0x78, 0xAA, 0x58, 0x0D, 0xE6, 0x54, 0xFE,
+ 0x8F, 0xFD, 0xF9, 0x61, 0x26, 0x3F, 0x2E, 0xCD,
+ 0x2C, 0x04, 0xB2, 0x80, 0x0F, 0x14, 0x6F, 0xC6,
+ 0xAB, 0xFB, 0x13, 0xDB, 0x9A, 0x21, 0xB3, 0xC0,
+ 0xA9, 0x19, 0x70, 0xF3, 0x2B, 0xAE, 0x9B, 0x49,
+ 0xB7, 0xA8, 0x24, 0x1B, 0x48, 0xEA, 0xED, 0xD9,
+ 0x47, 0x9E, 0x9C, 0x69, 0x3C, 0x66, 0xBB, 0x06,
+ 0x46, 0x38, 0x17, 0xB5, 0xCB, 0x05, 0x4A, 0x5E,
+ 0x15, 0x20, 0xB9, 0xB6, 0x33, 0x4C, 0x7D, 0xA3,
+ 0xD7, 0xB1, 0x23, 0x72, 0xC3, 0x4B, 0x63, 0xBE,
+ 0xF7, 0x5B, 0x74, 0x64, 0x77, 0xCC, 0xD3, 0x85,
+ 0xDE, 0x1A, 0x31, 0x97, 0xA2, 0x8B, 0xFC, 0x10,
+ 0x5F, 0xDC, 0xD5, 0xB0, 0xBD, 0x55, 0xC1, 0xE7,
+ 0x0C, 0x50, 0x43, 0x39, 0x71, 0x52, 0xE5, 0xAF,
+ 0x8A, 0x60, 0x92, 0x2D, 0xD8, 0x03, 0xF5, 0x28,
+ 0xCA, 0xEF, 0xD0, 0xC2, 0x53, 0x91, 0xA6, 0x73,
+ 0x56, 0xA5, 0xF1, 0x57, 0x42, 0xF4, 0xD4, 0x36,
+ 0x8D, 0xBC, 0xE9, 0x7E, 0x02, 0x76, 0x18, 0x0B,
+ 0x84, 0x5A, 0xE2, 0xBF, 0x68, 0x95, 0x29, 0x98,
+ 0xAD, 0x88, 0x1F, 0x81, 0x67, 0xA1, 0x3A, 0xA7,
+ 0x22, 0xF8, 0x01, 0xA0, 0xCE, 0x7A, 0xDA, 0x30,
+ 0xC4, 0xE4, 0xEE, 0x7C, 0x3B, 0x4D, 0x3D, 0xE3,
+ 0xFA, 0x6A, 0x7F, 0x99, 0x00, 0x93, 0x0E, 0xFF,
+ 0x90, 0x0A, 0x2A, 0x5D, 0x96, 0x08, 0x6B, 0x83,
+ 0xBA, 0x1E, 0x44, 0x87, 0x45, 0x9F, 0xC9, 0x94 };
+
+unsigned char table_106[32] = {
+ 0x03, 0x11, 0x07, 0x1B, 0x0F, 0x14, 0x0C, 0x01,
+ 0x04, 0x02, 0x09, 0x0A, 0x05, 0x12, 0x06, 0x1F,
+ 0x1C, 0x0E, 0x0D, 0x15, 0x18, 0x08, 0x00, 0x10,
+ 0x1E, 0x1D, 0x17, 0x19, 0x13, 0x16, 0x0B, 0x1A };
+
+unsigned char table_107[32] = {
+ 0x13, 0x1B, 0x06, 0x11, 0x1C, 0x07, 0x08, 0x0E,
+ 0x10, 0x05, 0x09, 0x18, 0x04, 0x15, 0x1E, 0x0F,
+ 0x1F, 0x12, 0x02, 0x00, 0x17, 0x19, 0x1A, 0x0D,
+ 0x03, 0x0C, 0x0A, 0x1D, 0x14, 0x01, 0x16, 0x0B };
+
+unsigned char table_108[256] = {
+ 0x99, 0xA3, 0x48, 0xE8, 0x5A, 0x7D, 0x97, 0xCA,
+ 0x7F, 0x06, 0x9B, 0x04, 0xE0, 0xF3, 0x18, 0xAE,
+ 0x59, 0xA0, 0x2B, 0x15, 0x85, 0x3E, 0x12, 0x93,
+ 0x3D, 0x28, 0x32, 0xF5, 0x20, 0x5D, 0x86, 0x00,
+ 0x1B, 0x2E, 0x36, 0x10, 0x5E, 0x6C, 0xD8, 0x29,
+ 0xB6, 0x3F, 0x05, 0x1C, 0xCE, 0xC2, 0x34, 0x5F,
+ 0x5C, 0x79, 0xD1, 0x1F, 0xA2, 0xEE, 0x8A, 0x69,
+ 0xB5, 0x87, 0x96, 0x6D, 0x4D, 0xC1, 0x61, 0x2C,
+ 0x11, 0xE7, 0x8E, 0xBF, 0x1E, 0x53, 0xD0, 0x58,
+ 0x76, 0xA4, 0x60, 0xA9, 0xB0, 0xF9, 0xEA, 0x3C,
+ 0x52, 0x9A, 0x24, 0xF1, 0x9F, 0xD3, 0x40, 0x0A,
+ 0x63, 0x78, 0x6A, 0x8B, 0x08, 0x22, 0x16, 0x83,
+ 0x6B, 0xD2, 0x49, 0x19, 0xBD, 0xFD, 0x62, 0x72,
+ 0xA8, 0x55, 0xAB, 0x0C, 0xB9, 0x13, 0xD5, 0xF0,
+ 0xF2, 0x84, 0xAF, 0x2F, 0x7B, 0x2A, 0x21, 0x0F,
+ 0xDA, 0x30, 0x71, 0xD6, 0x81, 0xE6, 0xEC, 0x41,
+ 0x90, 0x50, 0x66, 0x0E, 0xA7, 0xB8, 0xF7, 0x3A,
+ 0xB2, 0xCF, 0x3B, 0xFC, 0x56, 0x6F, 0xC3, 0xA6,
+ 0xC9, 0xA1, 0x8D, 0xBB, 0x9D, 0x75, 0xF6, 0xAA,
+ 0x7E, 0xF8, 0x33, 0xEF, 0xBC, 0x7C, 0x23, 0x1A,
+ 0x92, 0x6E, 0x2D, 0x8F, 0xED, 0xB7, 0xB1, 0x1D,
+ 0x67, 0x39, 0xAC, 0x0D, 0x74, 0xDB, 0x7A, 0x94,
+ 0x07, 0x09, 0xC0, 0xD7, 0xAD, 0xFE, 0x54, 0x91,
+ 0xDE, 0x45, 0xA5, 0x77, 0xCB, 0x37, 0xC6, 0x38,
+ 0x89, 0x88, 0x17, 0xD9, 0x4F, 0xDF, 0x25, 0xFB,
+ 0xFA, 0x4C, 0x80, 0x35, 0x82, 0xF4, 0x95, 0xC8,
+ 0xFF, 0xE9, 0x31, 0x01, 0x14, 0xB3, 0x02, 0x9E,
+ 0x4E, 0x43, 0x46, 0xC7, 0xEB, 0x51, 0xE5, 0x47,
+ 0xB4, 0xE3, 0xDC, 0x57, 0xC4, 0x98, 0x03, 0xE1,
+ 0xBA, 0x68, 0xCD, 0x27, 0xC5, 0x0B, 0xD4, 0x64,
+ 0x4B, 0x9C, 0x70, 0x65, 0x4A, 0xE4, 0x42, 0xDD,
+ 0xCC, 0xE2, 0x44, 0x73, 0xBE, 0x26, 0x8C, 0x5B };
+
+unsigned char table_109[256] = {
+ 0xE3, 0x95, 0xDB, 0x09, 0x82, 0x0A, 0x8F, 0x9E,
+ 0xC9, 0xDC, 0x28, 0x35, 0x0F, 0x8B, 0xA8, 0xA5,
+ 0x7F, 0x3D, 0x8C, 0xD1, 0x93, 0x57, 0x04, 0xAA,
+ 0x6A, 0x98, 0x81, 0xDD, 0x16, 0x67, 0x2E, 0xDF,
+ 0xED, 0xF7, 0xB2, 0xBD, 0x14, 0xB6, 0x76, 0xC8,
+ 0x75, 0x9F, 0x48, 0xAE, 0xBB, 0xB0, 0xF3, 0xE2,
+ 0xD4, 0x59, 0xD8, 0x9C, 0x64, 0xC1, 0x73, 0x21,
+ 0x6D, 0x96, 0x7B, 0x62, 0x56, 0x55, 0xCC, 0xFD,
+ 0xCE, 0x41, 0xA3, 0x43, 0x33, 0xAF, 0x23, 0x9D,
+ 0x6F, 0x65, 0x19, 0x52, 0xAD, 0xC6, 0xD3, 0x3F,
+ 0x66, 0xFF, 0xD0, 0x30, 0x6C, 0xC0, 0xEB, 0xCF,
+ 0x51, 0x88, 0x38, 0x72, 0x69, 0x77, 0x3B, 0xFA,
+ 0xBA, 0xB7, 0xA1, 0x91, 0xE0, 0x89, 0xAB, 0x44,
+ 0x1B, 0x05, 0x5B, 0xB9, 0x71, 0x47, 0x7E, 0xFB,
+ 0x02, 0xC7, 0x99, 0x6E, 0x42, 0x20, 0x90, 0x1F,
+ 0x4A, 0x85, 0x1A, 0xEA, 0x0C, 0x0D, 0xB3, 0xDA,
+ 0xE7, 0x13, 0xE6, 0xD7, 0x6B, 0x12, 0x46, 0x53,
+ 0xB5, 0xF8, 0x1D, 0x83, 0x54, 0x49, 0x8A, 0x26,
+ 0x4D, 0xDE, 0xF6, 0x03, 0xA2, 0x7D, 0x0E, 0xA0,
+ 0x68, 0x79, 0xCA, 0x0B, 0x5D, 0x40, 0x4F, 0x80,
+ 0xC2, 0xD6, 0x87, 0x70, 0xF0, 0xD2, 0x92, 0xEE,
+ 0xBE, 0x74, 0x5F, 0xBC, 0xA4, 0x4B, 0xFE, 0x37,
+ 0x60, 0xA9, 0x06, 0xA7, 0xE1, 0xF5, 0x2B, 0x10,
+ 0xEF, 0x2C, 0x07, 0x86, 0x7A, 0x27, 0xE9, 0xC5,
+ 0xAC, 0x32, 0x22, 0xF2, 0xE5, 0x8D, 0x31, 0x01,
+ 0x34, 0xA6, 0xB8, 0xC3, 0x3C, 0xE4, 0x08, 0x94,
+ 0x15, 0x4E, 0xB4, 0x39, 0x58, 0x00, 0x3E, 0x29,
+ 0x45, 0x3A, 0x84, 0x36, 0xF1, 0x2A, 0x50, 0x11,
+ 0xC4, 0x5A, 0xFC, 0xBF, 0xD9, 0xF9, 0x17, 0x9B,
+ 0x8E, 0x18, 0x63, 0x4C, 0x2F, 0x78, 0x2D, 0x5E,
+ 0x9A, 0xCD, 0x24, 0xEC, 0x7C, 0x97, 0x61, 0xCB,
+ 0x1E, 0xF4, 0xD5, 0xB1, 0x5C, 0x25, 0xE8, 0x1C };
+
+unsigned char table_110[256] = {
+ 0xC3, 0x06, 0x3C, 0xCB, 0xD2, 0x44, 0x9D, 0x48,
+ 0x28, 0xAA, 0xA9, 0xD0, 0x64, 0x25, 0x56, 0xCA,
+ 0xC2, 0xF8, 0x5C, 0xAE, 0x4E, 0x63, 0xB2, 0xE9,
+ 0x35, 0x11, 0xA8, 0x1A, 0x76, 0x15, 0xE0, 0x26,
+ 0x97, 0x99, 0xD4, 0x43, 0x80, 0xEE, 0xC1, 0x69,
+ 0xA6, 0x1E, 0x7A, 0x42, 0x55, 0x38, 0xBF, 0x75,
+ 0x0E, 0x29, 0xF5, 0xF3, 0x36, 0x7D, 0x51, 0xE8,
+ 0xE5, 0xEB, 0x68, 0x60, 0x0C, 0x70, 0xFD, 0xCC,
+ 0xE3, 0x23, 0x09, 0x6D, 0x2D, 0x6C, 0x5E, 0xB6,
+ 0x98, 0x8B, 0x1F, 0x50, 0x34, 0x8D, 0x10, 0x92,
+ 0x82, 0x85, 0xD5, 0x79, 0x02, 0xA4, 0x0A, 0xBC,
+ 0x40, 0xC6, 0xA3, 0x72, 0x8F, 0xC4, 0xA5, 0xE4,
+ 0x49, 0xD6, 0xCE, 0xA1, 0x12, 0x4F, 0x30, 0x31,
+ 0xDE, 0x2A, 0xF7, 0x95, 0xB5, 0x96, 0x14, 0x08,
+ 0xE6, 0x3D, 0x86, 0xF2, 0x47, 0x74, 0xB8, 0x5D,
+ 0x1D, 0x2B, 0x3A, 0x93, 0x7C, 0x6A, 0x01, 0xA0,
+ 0x9A, 0x4D, 0xB7, 0x71, 0xA7, 0x41, 0xC5, 0x65,
+ 0xC8, 0x89, 0xD1, 0x3E, 0x0D, 0xD8, 0xFF, 0x6F,
+ 0x7F, 0xA2, 0xFE, 0xD9, 0xF0, 0x4A, 0x07, 0x1C,
+ 0x0F, 0x6E, 0x03, 0x81, 0x1B, 0x05, 0xDF, 0x52,
+ 0xF1, 0x8A, 0xF9, 0xDD, 0x91, 0x3B, 0xD7, 0xE1,
+ 0x54, 0xAD, 0x90, 0x5A, 0x7B, 0xC7, 0x32, 0x62,
+ 0x16, 0x27, 0xB9, 0x66, 0x21, 0x88, 0xBD, 0x18,
+ 0x77, 0x8E, 0x94, 0x8C, 0x9B, 0x46, 0x9C, 0xB1,
+ 0xD3, 0x53, 0xB0, 0xBE, 0xAC, 0xAF, 0x73, 0x24,
+ 0xDA, 0x58, 0xE2, 0xFC, 0x78, 0xEA, 0xCD, 0xFA,
+ 0x37, 0xED, 0x13, 0x19, 0xC0, 0x59, 0x83, 0xBA,
+ 0x3F, 0x57, 0x00, 0x7E, 0xC9, 0x2E, 0x17, 0x5B,
+ 0x84, 0xF6, 0xE7, 0x22, 0xFB, 0x5F, 0x4C, 0x2C,
+ 0x61, 0x9F, 0x45, 0x39, 0xB3, 0xEC, 0x04, 0x87,
+ 0x67, 0xDC, 0x0B, 0xF4, 0x20, 0xAB, 0x6B, 0x9E,
+ 0x4B, 0xCF, 0xB4, 0x2F, 0xBB, 0xEF, 0xDB, 0x33 };
+
+unsigned char table_111[32] = {
+ 0x09, 0x0F, 0x00, 0x15, 0x12, 0x17, 0x1A, 0x0D,
+ 0x1C, 0x0B, 0x01, 0x0A, 0x05, 0x1E, 0x1D, 0x0C,
+ 0x1B, 0x08, 0x19, 0x18, 0x14, 0x07, 0x0E, 0x03,
+ 0x10, 0x16, 0x11, 0x1F, 0x04, 0x06, 0x02, 0x13 };
+
+unsigned char table_112[256] = {
+ 0xF9, 0x7D, 0xBE, 0xD5, 0x9F, 0xB8, 0x95, 0x43,
+ 0xDB, 0xAE, 0x7E, 0xEC, 0x5B, 0x58, 0x18, 0x49,
+ 0x4B, 0x9D, 0x1C, 0x3E, 0x61, 0xD1, 0xF6, 0x2F,
+ 0x41, 0x82, 0x51, 0x37, 0x72, 0x79, 0x05, 0x2A,
+ 0xC2, 0xB0, 0xE2, 0xE7, 0xB2, 0xF3, 0x1B, 0x92,
+ 0x86, 0xBB, 0xDC, 0x90, 0x1A, 0x19, 0xD7, 0xBA,
+ 0x2C, 0x7B, 0xEF, 0xC7, 0x8A, 0x81, 0xEB, 0xDE,
+ 0x73, 0x4E, 0xB7, 0x97, 0xCA, 0x29, 0x85, 0xC1,
+ 0xA5, 0x7F, 0xFE, 0x56, 0xE9, 0x9E, 0x21, 0x76,
+ 0x3A, 0x88, 0x70, 0xC6, 0xD3, 0x8C, 0x47, 0xC8,
+ 0x83, 0x48, 0xC3, 0x6A, 0x9C, 0x80, 0x53, 0xBD,
+ 0xFD, 0x54, 0x09, 0x91, 0x94, 0xAA, 0x7A, 0x59,
+ 0x71, 0xDD, 0xA8, 0x07, 0xCB, 0x0F, 0xE0, 0x9A,
+ 0x36, 0x4C, 0x4D, 0x0D, 0xA4, 0x96, 0x6F, 0x14,
+ 0x22, 0x38, 0xAD, 0x02, 0xF4, 0x0B, 0xEA, 0x93,
+ 0x20, 0x04, 0xBC, 0xE8, 0x6C, 0xFB, 0x10, 0x6B,
+ 0x40, 0xB6, 0x24, 0x17, 0x06, 0x31, 0xD9, 0x33,
+ 0xF5, 0x99, 0x57, 0xCD, 0xAB, 0x67, 0x5C, 0x30,
+ 0x1E, 0x34, 0xB4, 0x3F, 0x16, 0x42, 0xA2, 0x68,
+ 0x27, 0xB3, 0x1D, 0xED, 0x5F, 0x52, 0xF7, 0x3C,
+ 0x65, 0x5D, 0xE5, 0x23, 0x0C, 0x6D, 0x84, 0x6E,
+ 0xDA, 0x77, 0xF8, 0x15, 0xFA, 0x69, 0xD0, 0xA7,
+ 0x11, 0xAC, 0xA6, 0xA3, 0x1F, 0x2E, 0xBF, 0x4A,
+ 0x8F, 0xFC, 0xEE, 0xC9, 0x26, 0x12, 0xC0, 0xB1,
+ 0x45, 0x0E, 0x3D, 0x7C, 0xCE, 0x13, 0x8E, 0x98,
+ 0x46, 0x2B, 0xC5, 0x66, 0x28, 0x32, 0xD2, 0x03,
+ 0xE3, 0xC4, 0x9B, 0x89, 0x5E, 0xF0, 0xCF, 0x3B,
+ 0x2D, 0x50, 0xB5, 0x00, 0x0A, 0xD6, 0x55, 0xE1,
+ 0x62, 0x63, 0x64, 0x87, 0xAF, 0x78, 0xB9, 0xF2,
+ 0x25, 0x44, 0xFF, 0x39, 0xF1, 0x08, 0x4F, 0x74,
+ 0xA9, 0x8B, 0x75, 0x01, 0xA0, 0xE4, 0x35, 0x8D,
+ 0xA1, 0xCC, 0xDF, 0x60, 0xD8, 0x5A, 0xE6, 0xD4 };
+
+unsigned char table_113[256] = {
+ 0x46, 0x9D, 0x39, 0xB2, 0x8D, 0x3B, 0x59, 0x5A,
+ 0xD0, 0x9C, 0xE4, 0x04, 0x01, 0xE2, 0xB3, 0xD2,
+ 0xD7, 0x18, 0x40, 0xD8, 0xF1, 0xEF, 0x3A, 0x1D,
+ 0x8E, 0xE5, 0xD9, 0xD3, 0xCB, 0x49, 0x4C, 0xCF,
+ 0xC0, 0xD6, 0xB5, 0x73, 0x77, 0x82, 0x54, 0xA2,
+ 0xB1, 0xB0, 0x84, 0x5D, 0xC7, 0xDE, 0x31, 0x2F,
+ 0x50, 0x78, 0xBE, 0x94, 0x64, 0x44, 0x60, 0x7A,
+ 0x1A, 0x6E, 0x09, 0x6F, 0xBF, 0x76, 0x81, 0x38,
+ 0x22, 0xC3, 0xEE, 0x8F, 0xFB, 0x32, 0xED, 0x92,
+ 0xAE, 0xE6, 0x5F, 0xAA, 0xAC, 0x0D, 0xA3, 0x47,
+ 0x1F, 0x11, 0xC1, 0x29, 0xAF, 0xFD, 0x1C, 0xDB,
+ 0x00, 0x23, 0xB9, 0xB8, 0x91, 0x41, 0x27, 0x37,
+ 0x43, 0x02, 0x26, 0xF6, 0x7D, 0x0A, 0x85, 0x93,
+ 0x97, 0x2E, 0x20, 0x55, 0x13, 0x4B, 0x6C, 0xE7,
+ 0xFC, 0x25, 0xFA, 0x9E, 0x5B, 0xA1, 0xDF, 0x2C,
+ 0x3E, 0xBC, 0xEA, 0x42, 0x7C, 0x36, 0x30, 0xEB,
+ 0xBD, 0x8B, 0x87, 0x16, 0x3D, 0x5C, 0x07, 0xBA,
+ 0xB4, 0x1B, 0xC2, 0xE3, 0x71, 0x9A, 0x5E, 0x4D,
+ 0xF2, 0xCC, 0x0E, 0xE1, 0x34, 0x75, 0x58, 0x89,
+ 0x17, 0xD4, 0x68, 0x80, 0x2B, 0x74, 0x70, 0x8A,
+ 0x63, 0xE8, 0x56, 0x24, 0xD1, 0x57, 0x35, 0x6D,
+ 0x3C, 0xA6, 0xC8, 0x7E, 0xA8, 0x4E, 0xC4, 0x33,
+ 0xA9, 0x62, 0x61, 0x7F, 0x21, 0x98, 0x2A, 0xAD,
+ 0xB6, 0xA7, 0xF5, 0x3F, 0x15, 0x45, 0xF8, 0xA4,
+ 0x95, 0x88, 0xDC, 0x96, 0x90, 0x08, 0x9B, 0xF9,
+ 0x06, 0x14, 0x05, 0xF0, 0xF7, 0xA0, 0xE0, 0x65,
+ 0xCA, 0xA5, 0x9F, 0x79, 0xCD, 0x4F, 0x72, 0xB7,
+ 0x4A, 0x0F, 0x66, 0xC5, 0x0C, 0x52, 0xF3, 0x69,
+ 0x83, 0x03, 0x99, 0x1E, 0x2D, 0xDA, 0x8C, 0x53,
+ 0x28, 0xDD, 0xE9, 0x0B, 0xC9, 0xF4, 0x48, 0x12,
+ 0x6A, 0x19, 0xCE, 0xAB, 0x51, 0xD5, 0x6B, 0xBB,
+ 0xFE, 0x7B, 0x67, 0xFF, 0x10, 0xEC, 0xC6, 0x86 };
+
+unsigned char table_114[32] = {
+ 0x11, 0x10, 0x04, 0x1D, 0x08, 0x15, 0x1A, 0x1B,
+ 0x14, 0x18, 0x0F, 0x17, 0x16, 0x07, 0x1E, 0x0E,
+ 0x12, 0x0A, 0x13, 0x0B, 0x0C, 0x00, 0x06, 0x02,
+ 0x1F, 0x19, 0x09, 0x1C, 0x01, 0x0D, 0x03, 0x05 };
+
+unsigned char table_115[256] = {
+ 0xB7, 0xBB, 0x63, 0x0D, 0xF0, 0x33, 0x5A, 0x05,
+ 0xF2, 0x7F, 0x64, 0xDB, 0x51, 0xC9, 0x2C, 0x85,
+ 0x4F, 0x41, 0xA4, 0x42, 0xCF, 0xA6, 0x52, 0x2F,
+ 0x26, 0xEF, 0xFB, 0x29, 0x40, 0x16, 0xF7, 0xED,
+ 0x23, 0x69, 0x8A, 0xDF, 0x77, 0x28, 0x93, 0x14,
+ 0x82, 0x0C, 0xBE, 0x3D, 0x20, 0xB4, 0x79, 0x94,
+ 0x54, 0xF8, 0x07, 0xB1, 0xE1, 0x66, 0x73, 0xD3,
+ 0x19, 0x15, 0xFF, 0x03, 0x6A, 0x9A, 0xDC, 0x1C,
+ 0xB3, 0x5D, 0x76, 0x68, 0x47, 0x6C, 0xF9, 0xFD,
+ 0xE9, 0xDD, 0x01, 0x65, 0xBD, 0x80, 0x0E, 0x7A,
+ 0x8D, 0x99, 0x13, 0x7C, 0xA5, 0xA7, 0x1A, 0xCC,
+ 0xB8, 0xE6, 0x2B, 0xB2, 0xB6, 0xD0, 0x62, 0x2D,
+ 0x4D, 0xD2, 0xB9, 0x04, 0x46, 0xAE, 0xAA, 0x44,
+ 0xDA, 0x92, 0x4B, 0x4E, 0xC4, 0xE2, 0xFE, 0xA2,
+ 0x75, 0x7B, 0xC3, 0xFA, 0x9F, 0x37, 0x9D, 0x1E,
+ 0x72, 0xD4, 0x1F, 0x4A, 0x9B, 0xE5, 0x6D, 0xEC,
+ 0x5C, 0x7D, 0x98, 0xE8, 0xEE, 0x86, 0xD1, 0xC8,
+ 0xEA, 0x55, 0xBF, 0xAF, 0xDE, 0x32, 0x09, 0x3A,
+ 0x8F, 0x57, 0x83, 0x43, 0x61, 0xC6, 0x8E, 0x96,
+ 0x22, 0xA3, 0x97, 0x91, 0x5F, 0x11, 0x3B, 0x5B,
+ 0x1B, 0x34, 0x49, 0x95, 0xF1, 0x6F, 0x89, 0xA8,
+ 0xC0, 0x36, 0x0A, 0x3F, 0x60, 0x50, 0xE7, 0x08,
+ 0xCE, 0x25, 0xC1, 0x71, 0xF6, 0x59, 0x58, 0x56,
+ 0x4C, 0xAB, 0x27, 0xAC, 0x06, 0xCB, 0x00, 0x30,
+ 0x84, 0x3E, 0xC2, 0x1D, 0x02, 0xE0, 0xC5, 0xD6,
+ 0x18, 0x70, 0xA9, 0x88, 0xD9, 0x39, 0x8B, 0x6E,
+ 0xF4, 0x24, 0xA0, 0x48, 0x45, 0x21, 0x87, 0x78,
+ 0x38, 0x90, 0xE3, 0xCA, 0xF5, 0xD7, 0x2A, 0x53,
+ 0x9C, 0xCD, 0x31, 0x35, 0xAD, 0x74, 0xD8, 0x12,
+ 0xBC, 0x9E, 0x6B, 0x67, 0xB0, 0xBA, 0xE4, 0x10,
+ 0x5E, 0xFC, 0xC7, 0x0F, 0x2E, 0x81, 0x7E, 0xA1,
+ 0x8C, 0x17, 0xB5, 0xEB, 0xD5, 0xF3, 0x0B, 0x3C };
+
+unsigned char table_116[32] = {
+ 0x00, 0x05, 0x10, 0x1C, 0x0C, 0x1A, 0x04, 0x1B,
+ 0x0A, 0x0D, 0x14, 0x0B, 0x07, 0x03, 0x12, 0x1E,
+ 0x06, 0x11, 0x01, 0x08, 0x15, 0x09, 0x1F, 0x0F,
+ 0x19, 0x18, 0x16, 0x02, 0x13, 0x0E, 0x17, 0x1D };
+
+unsigned char table_117[256] = {
+ 0xD0, 0x9A, 0xAB, 0xA8, 0xA7, 0xDF, 0x28, 0xCE,
+ 0x3E, 0x51, 0xBF, 0x76, 0x03, 0xA0, 0x53, 0x3F,
+ 0x90, 0x93, 0x87, 0x67, 0x98, 0x3D, 0xEA, 0x8B,
+ 0x55, 0xCF, 0x10, 0xF3, 0x25, 0xFC, 0x9F, 0x41,
+ 0x6B, 0x54, 0x6E, 0x0B, 0x83, 0x35, 0x69, 0x7D,
+ 0xE0, 0x88, 0x4B, 0xE9, 0x1E, 0x96, 0x91, 0x57,
+ 0xBD, 0x72, 0x21, 0x3C, 0xA6, 0x99, 0x6C, 0xF6,
+ 0x13, 0xFA, 0x29, 0xED, 0xDB, 0x16, 0x4D, 0x07,
+ 0x45, 0xA5, 0xE3, 0x0E, 0x31, 0xBC, 0x56, 0x5C,
+ 0xB2, 0x23, 0xDA, 0x74, 0xFF, 0x02, 0x8F, 0xF4,
+ 0x2A, 0xC9, 0x89, 0xAA, 0x05, 0xB1, 0xD1, 0x1F,
+ 0x4F, 0xB0, 0x7A, 0x2C, 0x14, 0xD9, 0xE7, 0x66,
+ 0x62, 0x1A, 0x4C, 0xC0, 0xC6, 0x63, 0x7F, 0xB4,
+ 0xF1, 0x43, 0xFE, 0x61, 0xA3, 0xCC, 0xE8, 0x6D,
+ 0xBA, 0x65, 0x42, 0x2B, 0xCA, 0xD5, 0x52, 0x3A,
+ 0xCD, 0x1D, 0x24, 0xD7, 0x47, 0xDE, 0x9E, 0x95,
+ 0x85, 0x48, 0x86, 0xE1, 0xC5, 0xD2, 0x34, 0xAF,
+ 0x40, 0xFB, 0xE6, 0x4E, 0xC8, 0xF5, 0x7B, 0x5A,
+ 0xCB, 0xD4, 0x97, 0x6F, 0x0C, 0x79, 0x9C, 0x20,
+ 0x59, 0x19, 0x68, 0x2E, 0x09, 0x64, 0x73, 0x50,
+ 0xC2, 0x2F, 0x0D, 0xEF, 0x9D, 0x94, 0x00, 0x81,
+ 0xE2, 0x46, 0x5F, 0xB8, 0x0A, 0x12, 0x75, 0x1C,
+ 0x8C, 0xB6, 0x71, 0xAC, 0x04, 0x60, 0xA9, 0x5B,
+ 0xF8, 0x30, 0x49, 0x44, 0x4A, 0xBE, 0x6A, 0xEB,
+ 0xD3, 0xD8, 0x36, 0xB3, 0x3B, 0x17, 0x80, 0xA4,
+ 0xEC, 0x26, 0x82, 0xB5, 0x37, 0x5D, 0x1B, 0x2D,
+ 0xE5, 0xA2, 0x0F, 0xB7, 0xC4, 0xF2, 0x70, 0x39,
+ 0xF9, 0xC7, 0xBB, 0x8A, 0x32, 0x78, 0xC3, 0x5E,
+ 0xD6, 0xE4, 0x22, 0x9B, 0x18, 0x8E, 0xEE, 0x27,
+ 0x8D, 0x33, 0x11, 0x77, 0x01, 0x06, 0x38, 0xF0,
+ 0x7E, 0x08, 0x15, 0xB9, 0x7C, 0xAD, 0x84, 0xDD,
+ 0xC1, 0xFD, 0x92, 0xA1, 0xF7, 0xAE, 0xDC, 0x58 };
+
+unsigned char table_118[256] = {
+ 0x38, 0xA0, 0xA6, 0xFC, 0x7C, 0x5A, 0x97, 0x1D,
+ 0xFD, 0x00, 0x20, 0xA2, 0x72, 0x10, 0x1F, 0x48,
+ 0x98, 0x7E, 0xDF, 0x2D, 0x80, 0x0A, 0x27, 0xDC,
+ 0xCF, 0xBF, 0x92, 0x94, 0x53, 0xCC, 0x0E, 0x74,
+ 0xA7, 0x60, 0x08, 0x15, 0x87, 0x6F, 0xB3, 0xA3,
+ 0xED, 0x59, 0x09, 0x4F, 0x9E, 0x9A, 0xEE, 0x83,
+ 0x56, 0x32, 0x34, 0xC7, 0x24, 0xE7, 0x96, 0x4D,
+ 0xAE, 0xE3, 0xBD, 0xE2, 0x36, 0x4A, 0xB6, 0x8B,
+ 0xF2, 0xC1, 0xD7, 0x40, 0x31, 0x4B, 0xDA, 0xF1,
+ 0xB1, 0x70, 0xA8, 0xC3, 0xC6, 0x8A, 0xE6, 0x77,
+ 0x21, 0x7D, 0xD5, 0x0C, 0x43, 0xC4, 0xF0, 0x1B,
+ 0x18, 0xA1, 0x85, 0xE1, 0xFF, 0x8D, 0xE5, 0x6E,
+ 0x9B, 0x51, 0x1C, 0xA4, 0x5C, 0x8E, 0x69, 0x49,
+ 0x23, 0xCD, 0x52, 0xF8, 0x3E, 0x91, 0x5E, 0x1E,
+ 0x25, 0xB4, 0x93, 0xCB, 0xE0, 0x47, 0xBC, 0x4E,
+ 0x33, 0xB7, 0x75, 0x1A, 0x11, 0x9C, 0x3F, 0xEC,
+ 0xD1, 0x46, 0xDD, 0xAA, 0xB8, 0x99, 0x86, 0x67,
+ 0x58, 0xF9, 0x16, 0x17, 0x6D, 0x5F, 0x2B, 0xA5,
+ 0xD3, 0x8F, 0x55, 0x71, 0xD2, 0xBA, 0x5B, 0x3C,
+ 0x82, 0xB5, 0x41, 0xE4, 0x90, 0x45, 0x6C, 0xF6,
+ 0xDE, 0xA9, 0x84, 0x62, 0x19, 0x3B, 0xB9, 0xC8,
+ 0x2C, 0xB0, 0x76, 0x57, 0xD8, 0x26, 0x9D, 0x89,
+ 0xC9, 0x54, 0xFB, 0x07, 0xCE, 0x22, 0x5D, 0x64,
+ 0x65, 0xAD, 0x01, 0xDB, 0x14, 0x4C, 0x37, 0x03,
+ 0x6B, 0xAF, 0xD0, 0x7F, 0x9F, 0xBB, 0xEB, 0xC0,
+ 0x50, 0x66, 0x68, 0x0B, 0x42, 0x2A, 0xD4, 0xF5,
+ 0x61, 0x63, 0xF3, 0x39, 0xBE, 0xC5, 0xEF, 0x28,
+ 0x3A, 0xAB, 0x79, 0x05, 0xE9, 0x12, 0x73, 0x3D,
+ 0xB2, 0x8C, 0xCA, 0x29, 0x0F, 0xF4, 0x7B, 0x13,
+ 0x88, 0x44, 0xC2, 0x2E, 0xFA, 0xFE, 0x04, 0x35,
+ 0xE8, 0x06, 0x7A, 0x78, 0x0D, 0x81, 0xF7, 0xEA,
+ 0xD9, 0x2F, 0x02, 0xAC, 0x30, 0x6A, 0xD6, 0x95 };
+
+unsigned char table_119[32] = {
+ 0x14, 0x0A, 0x1C, 0x00, 0x0C, 0x1F, 0x1E, 0x0B,
+ 0x12, 0x1D, 0x17, 0x08, 0x07, 0x04, 0x09, 0x10,
+ 0x03, 0x1B, 0x0E, 0x1A, 0x05, 0x0D, 0x11, 0x15,
+ 0x18, 0x02, 0x06, 0x01, 0x19, 0x16, 0x13, 0x0F };
+
+unsigned char table_120[256] = {
+ 0xCE, 0x89, 0xB2, 0x72, 0x04, 0x77, 0x64, 0xAE,
+ 0x80, 0x99, 0xB5, 0x00, 0x7B, 0x50, 0x9D, 0xE3,
+ 0x87, 0x37, 0x6D, 0x3D, 0x32, 0xBA, 0x20, 0xF0,
+ 0xDC, 0xBD, 0x61, 0x26, 0xD4, 0xA6, 0x70, 0x54,
+ 0xC1, 0x7D, 0x82, 0xFF, 0x81, 0x83, 0x2F, 0xF5,
+ 0x3B, 0x42, 0x08, 0x5C, 0x30, 0x59, 0xBB, 0xC2,
+ 0x33, 0x5D, 0xEE, 0xB7, 0xF7, 0x2B, 0x76, 0xD0,
+ 0x43, 0x1C, 0x48, 0xFC, 0x01, 0xCD, 0x27, 0x1D,
+ 0x5A, 0x96, 0x95, 0x03, 0xC6, 0x1F, 0x09, 0xCB,
+ 0xF6, 0x47, 0xA9, 0x93, 0xA7, 0xD2, 0xDB, 0x51,
+ 0xB0, 0x7A, 0xE6, 0x62, 0x0F, 0x12, 0x57, 0xF4,
+ 0x35, 0xFE, 0xA4, 0xDF, 0x5B, 0xF3, 0x67, 0x85,
+ 0x98, 0xE4, 0xAB, 0x75, 0x4C, 0xE2, 0x25, 0x74,
+ 0x3A, 0x45, 0xDE, 0xEF, 0x4A, 0x97, 0x86, 0x24,
+ 0xE9, 0x8F, 0xD8, 0xD7, 0x60, 0xAD, 0x36, 0x8E,
+ 0x1E, 0xB9, 0x4F, 0x6B, 0x8C, 0x06, 0x23, 0x94,
+ 0x0E, 0xD3, 0x49, 0x14, 0x90, 0xAF, 0x65, 0xEC,
+ 0xF9, 0x0D, 0xED, 0x6C, 0xBE, 0x7F, 0xA5, 0xC5,
+ 0xEA, 0x78, 0x2E, 0xBC, 0xD5, 0xDA, 0x18, 0xE1,
+ 0x10, 0x2D, 0xB4, 0x16, 0x4B, 0xE8, 0xC4, 0x8D,
+ 0x19, 0x1B, 0x02, 0x66, 0xB6, 0xE7, 0x9C, 0x7C,
+ 0xC9, 0xA0, 0x2A, 0x53, 0x13, 0xDD, 0xF8, 0xA8,
+ 0x0A, 0x6E, 0xCF, 0x6F, 0x7E, 0xE0, 0x3E, 0xE5,
+ 0x07, 0xCC, 0x38, 0xD1, 0xF2, 0x2C, 0x9A, 0xAC,
+ 0x88, 0x79, 0xB8, 0xC8, 0xBF, 0x63, 0x71, 0x69,
+ 0x52, 0x39, 0x9F, 0x22, 0x3F, 0x9E, 0x44, 0xFA,
+ 0x73, 0x6A, 0x8B, 0xA2, 0xD6, 0x1A, 0x9B, 0xB1,
+ 0x8A, 0x4D, 0x58, 0xA1, 0x46, 0x5F, 0x55, 0x56,
+ 0x21, 0x05, 0x15, 0x92, 0xAA, 0xEB, 0x31, 0x68,
+ 0xFB, 0x41, 0xC3, 0x4E, 0xB3, 0x40, 0x34, 0x17,
+ 0xD9, 0x29, 0x3C, 0x0C, 0xF1, 0x0B, 0x28, 0x84,
+ 0x5E, 0xCA, 0xFD, 0x11, 0xA3, 0xC7, 0xC0, 0x91 };
+
+unsigned char table_121[32] = {
+ 0x1E, 0x12, 0x06, 0x1D, 0x15, 0x1F, 0x13, 0x0B,
+ 0x10, 0x0D, 0x1C, 0x01, 0x0A, 0x0E, 0x02, 0x19,
+ 0x04, 0x1A, 0x03, 0x11, 0x00, 0x16, 0x0C, 0x17,
+ 0x14, 0x08, 0x18, 0x05, 0x09, 0x0F, 0x1B, 0x07 };
+
+unsigned char table_122[256] = {
+ 0x85, 0xDF, 0x7F, 0x7C, 0x56, 0xF0, 0x0C, 0x7D,
+ 0x76, 0xA8, 0x58, 0x31, 0x25, 0x8A, 0x0D, 0x23,
+ 0x05, 0x0F, 0x12, 0x64, 0x8E, 0x5D, 0xF4, 0x2C,
+ 0x18, 0xFA, 0x4B, 0xFE, 0x91, 0xBF, 0x95, 0x0B,
+ 0xF1, 0x88, 0x10, 0xD8, 0x3E, 0x53, 0x96, 0xB5,
+ 0x75, 0x24, 0x8F, 0xD6, 0x68, 0x5C, 0x93, 0x1F,
+ 0x6B, 0xC2, 0xAB, 0xED, 0x1E, 0xC0, 0xBC, 0x47,
+ 0xE9, 0xD1, 0xDE, 0xCA, 0xF6, 0x62, 0x43, 0xEB,
+ 0xA2, 0xB4, 0x08, 0xE6, 0x74, 0x0E, 0xA1, 0x72,
+ 0x66, 0x61, 0x21, 0x2E, 0x32, 0x63, 0x29, 0xD7,
+ 0x1C, 0x22, 0xAC, 0xE7, 0x54, 0xF3, 0x65, 0x17,
+ 0x9F, 0x78, 0x79, 0x4C, 0xDD, 0x27, 0x90, 0x36,
+ 0x19, 0x44, 0x03, 0xD9, 0x4A, 0x5A, 0x34, 0xF9,
+ 0x97, 0xA6, 0x70, 0x39, 0x28, 0x77, 0x6E, 0xB7,
+ 0x8C, 0x02, 0x5E, 0x9B, 0x8D, 0x59, 0x6F, 0xA5,
+ 0x07, 0xE2, 0x41, 0x51, 0xC9, 0x3C, 0xE8, 0xE1,
+ 0xB3, 0x16, 0x50, 0x04, 0xE3, 0x1D, 0x3B, 0xD2,
+ 0x4D, 0x35, 0x71, 0xDA, 0x9E, 0xA7, 0xE4, 0xE0,
+ 0xB6, 0x2B, 0xEA, 0x84, 0x55, 0xF8, 0x57, 0x3D,
+ 0x73, 0x42, 0xC6, 0x0A, 0x92, 0x6A, 0xAE, 0xF5,
+ 0xFC, 0xD5, 0x15, 0x52, 0x7E, 0x14, 0x81, 0x13,
+ 0xE5, 0x49, 0x38, 0x2A, 0x94, 0x5B, 0xA3, 0x11,
+ 0x8B, 0x80, 0xBB, 0x01, 0x9C, 0xA4, 0xDB, 0xF7,
+ 0xA9, 0x20, 0xF2, 0x1A, 0xDC, 0x33, 0x3A, 0xEF,
+ 0xD3, 0xFD, 0x30, 0xB0, 0x1B, 0xC4, 0x06, 0xD4,
+ 0x6D, 0x87, 0x2F, 0x60, 0x5F, 0xC5, 0x09, 0x37,
+ 0xAF, 0x00, 0xCB, 0x9D, 0xA0, 0xB9, 0x45, 0x86,
+ 0x4F, 0x6C, 0x67, 0xFB, 0x40, 0x3F, 0xCC, 0xB8,
+ 0xC8, 0x82, 0x98, 0x99, 0x7B, 0xB1, 0xCD, 0xD0,
+ 0xBD, 0x48, 0xAD, 0x26, 0x7A, 0x9A, 0x46, 0xFF,
+ 0x89, 0xC7, 0xC1, 0xCF, 0xBE, 0xAA, 0xEC, 0xBA,
+ 0xCE, 0x2D, 0x4E, 0x83, 0xC3, 0x69, 0xEE, 0xB2 };
+
+unsigned char table_123[256] = {
+ 0x9D, 0xFB, 0x3C, 0x81, 0xAA, 0x05, 0xB2, 0xBE,
+ 0xD1, 0x5F, 0x4C, 0xE0, 0xA3, 0xF4, 0xDE, 0x35,
+ 0xFE, 0x1B, 0x37, 0x99, 0x94, 0x7A, 0x10, 0xAB,
+ 0xC0, 0xA4, 0xB5, 0xFF, 0x8F, 0x3B, 0xB4, 0x51,
+ 0x04, 0xE9, 0xB9, 0xC1, 0x98, 0xC5, 0x82, 0x38,
+ 0x4D, 0x71, 0xFC, 0x33, 0xC4, 0x50, 0x5D, 0x88,
+ 0xB8, 0x5C, 0x32, 0xE2, 0xBB, 0xCD, 0x60, 0x2C,
+ 0xD4, 0x7E, 0x27, 0x59, 0x2B, 0x1F, 0x53, 0xF6,
+ 0x25, 0x86, 0xAE, 0x21, 0xFA, 0x31, 0xD7, 0x0F,
+ 0x17, 0xDA, 0x7F, 0xC9, 0x46, 0x19, 0x08, 0xA8,
+ 0xCF, 0x13, 0xCC, 0x03, 0x3F, 0x22, 0x6E, 0xEB,
+ 0x4A, 0x63, 0x73, 0xBD, 0x36, 0xED, 0x30, 0x57,
+ 0x65, 0xF8, 0x41, 0x61, 0x1E, 0xA0, 0xC6, 0x45,
+ 0x3E, 0x75, 0x28, 0x87, 0xCB, 0xD6, 0x16, 0xD8,
+ 0xDF, 0xEF, 0xEA, 0xA7, 0x58, 0xB0, 0x1D, 0xE6,
+ 0x47, 0x76, 0xD9, 0x96, 0xE7, 0xDC, 0x00, 0x80,
+ 0xDD, 0xB7, 0x9A, 0xE1, 0xF5, 0x9C, 0x4B, 0xE3,
+ 0xBC, 0x8D, 0xF2, 0x2F, 0x9F, 0x6C, 0x93, 0xAF,
+ 0xA9, 0xC2, 0x5E, 0x24, 0x15, 0xD2, 0x09, 0x0D,
+ 0xDB, 0x4F, 0x91, 0x0E, 0x64, 0x34, 0x4E, 0xAD,
+ 0x62, 0x44, 0x23, 0x85, 0xB6, 0xAC, 0xC7, 0xCA,
+ 0x84, 0xF9, 0x8C, 0xBF, 0x14, 0x7C, 0x8E, 0x92,
+ 0xF0, 0x0B, 0xCE, 0x90, 0x7D, 0x70, 0x9E, 0x54,
+ 0x39, 0x5B, 0x6D, 0x52, 0xEE, 0xA2, 0x6F, 0x78,
+ 0x2D, 0x95, 0x8B, 0x02, 0x3D, 0x7B, 0x69, 0xC3,
+ 0x49, 0xA5, 0x1A, 0x26, 0xD5, 0x6B, 0xE8, 0xFD,
+ 0xB3, 0xD3, 0x20, 0x55, 0x18, 0x06, 0xF3, 0xB1,
+ 0x0C, 0xC8, 0x07, 0x12, 0xF7, 0x01, 0x2E, 0x72,
+ 0x97, 0xA6, 0x11, 0x89, 0x56, 0x5A, 0x29, 0xBA,
+ 0x67, 0x42, 0x83, 0x6A, 0x2A, 0xF1, 0xA1, 0x9B,
+ 0xE5, 0xE4, 0x74, 0x66, 0x1C, 0x68, 0xEC, 0x40,
+ 0x48, 0x77, 0xD0, 0x0A, 0x8A, 0x3A, 0x43, 0x79 };
+
+unsigned char table_124[256] = {
+ 0x6C, 0xC3, 0x28, 0x2F, 0x42, 0x4B, 0x7C, 0x3C,
+ 0xCE, 0x24, 0xC8, 0x51, 0x25, 0x3F, 0x49, 0x8D,
+ 0x1E, 0x5C, 0x89, 0x3A, 0x98, 0x47, 0x0B, 0x12,
+ 0xA9, 0xB1, 0xD7, 0xB6, 0x5D, 0xF9, 0x5A, 0xBC,
+ 0xFA, 0x06, 0x7D, 0x08, 0xFC, 0x37, 0x54, 0x4F,
+ 0xD4, 0xCD, 0xA7, 0x5E, 0xE0, 0x92, 0x82, 0x56,
+ 0xF1, 0x2B, 0xC4, 0xE2, 0x29, 0xEA, 0x35, 0x57,
+ 0x33, 0x4E, 0x1A, 0x17, 0x8B, 0x85, 0xBF, 0xD5,
+ 0x18, 0xB3, 0x0D, 0x71, 0x45, 0x81, 0xB4, 0x27,
+ 0xD1, 0xE1, 0xFF, 0x44, 0x9E, 0xA4, 0x15, 0x9A,
+ 0x90, 0xC7, 0x79, 0xE3, 0x4C, 0xE9, 0x3D, 0x6B,
+ 0xF5, 0xF4, 0xEE, 0xAA, 0xDB, 0x07, 0x09, 0xCF,
+ 0x7B, 0x95, 0xA0, 0x53, 0x8F, 0xA1, 0x9D, 0xBE,
+ 0x6F, 0xAE, 0x96, 0x46, 0x59, 0x01, 0x84, 0xCC,
+ 0x3B, 0x8E, 0xF7, 0x4D, 0x6E, 0xDC, 0xE8, 0x36,
+ 0x7A, 0xE5, 0xBD, 0xE7, 0x9F, 0x2C, 0x52, 0xAB,
+ 0x55, 0x13, 0x1D, 0xFB, 0x58, 0x9C, 0xDF, 0xC0,
+ 0x30, 0x73, 0x67, 0x39, 0x74, 0xD3, 0x11, 0xD2,
+ 0x0E, 0x20, 0xB7, 0x02, 0xB9, 0x1C, 0x86, 0x76,
+ 0x10, 0x68, 0x9B, 0x63, 0x48, 0x8A, 0xB2, 0xB8,
+ 0xAF, 0x26, 0x99, 0x04, 0xB0, 0xE4, 0xEF, 0xEB,
+ 0xEC, 0x6D, 0x61, 0xC1, 0xD0, 0x38, 0xC9, 0x19,
+ 0x60, 0xA8, 0xA6, 0xF8, 0x80, 0xC5, 0x03, 0x0F,
+ 0x22, 0x2D, 0x88, 0x32, 0x77, 0x70, 0xFE, 0x0C,
+ 0x31, 0x40, 0x5F, 0xED, 0xA5, 0x93, 0x43, 0xF0,
+ 0x8C, 0xE6, 0x34, 0x21, 0xD9, 0xC2, 0xD8, 0xC6,
+ 0x6A, 0xD6, 0xCB, 0xAC, 0x75, 0xB5, 0x78, 0x0A,
+ 0xA3, 0x69, 0x16, 0xBA, 0x50, 0x2A, 0x41, 0x83,
+ 0xF6, 0x64, 0x00, 0x65, 0x7E, 0xDD, 0x5B, 0xDA,
+ 0x14, 0xFD, 0x3E, 0x7F, 0xCA, 0x66, 0x4A, 0x1F,
+ 0xA2, 0xAD, 0xF2, 0x23, 0xBB, 0x72, 0xF3, 0x94,
+ 0x62, 0x1B, 0xDE, 0x91, 0x87, 0x97, 0x05, 0x2E };
+
+unsigned char table_125[32] = {
+ 0x1A, 0x18, 0x12, 0x15, 0x00, 0x1C, 0x01, 0x0B,
+ 0x19, 0x1B, 0x1F, 0x11, 0x07, 0x10, 0x1E, 0x06,
+ 0x17, 0x04, 0x0A, 0x0E, 0x0D, 0x0C, 0x16, 0x08,
+ 0x02, 0x03, 0x13, 0x14, 0x09, 0x1D, 0x05, 0x0F };
+
+unsigned char table_126[32] = {
+ 0x1C, 0x1D, 0x07, 0x12, 0x18, 0x1A, 0x19, 0x09,
+ 0x0F, 0x14, 0x1F, 0x0B, 0x13, 0x04, 0x0E, 0x1E,
+ 0x0C, 0x0D, 0x01, 0x17, 0x1B, 0x16, 0x0A, 0x05,
+ 0x15, 0x10, 0x11, 0x08, 0x00, 0x03, 0x06, 0x02 };
+
+unsigned char table_127[256] = {
+ 0xA0, 0x66, 0xD8, 0x08, 0xEA, 0x39, 0x78, 0xAB,
+ 0x61, 0x4E, 0xC7, 0xD1, 0xA3, 0x1C, 0x9F, 0xCB,
+ 0x19, 0x51, 0x15, 0x92, 0x23, 0xFD, 0x7D, 0x1D,
+ 0x95, 0xAE, 0x0E, 0x8B, 0xE6, 0x7F, 0x86, 0x6D,
+ 0x06, 0xBD, 0x20, 0x1F, 0x3A, 0xE4, 0x54, 0x91,
+ 0x69, 0xD3, 0xE3, 0x3D, 0x4D, 0x31, 0x49, 0xA4,
+ 0x41, 0xF3, 0xE0, 0x11, 0x14, 0x9B, 0x96, 0x5A,
+ 0xC4, 0x8E, 0x34, 0xDB, 0xBA, 0x83, 0xD9, 0x81,
+ 0xAF, 0x58, 0x8A, 0x79, 0x13, 0xBC, 0x85, 0x37,
+ 0x9E, 0x6C, 0x57, 0x71, 0x8D, 0x97, 0x5F, 0x6F,
+ 0x1E, 0x74, 0x27, 0xFC, 0x5C, 0x7A, 0x64, 0x87,
+ 0xF5, 0xC6, 0xF2, 0x4F, 0xDE, 0x80, 0xAA, 0x84,
+ 0x2E, 0xDC, 0xE7, 0x40, 0x75, 0xC5, 0xB3, 0xC8,
+ 0xCE, 0x21, 0x02, 0x67, 0xB7, 0x10, 0x47, 0x6A,
+ 0xEE, 0x53, 0x2C, 0x16, 0x05, 0xC0, 0x63, 0x4C,
+ 0x0D, 0xBB, 0xC3, 0x38, 0x46, 0x68, 0x7E, 0xF9,
+ 0xB8, 0xB4, 0x3E, 0x36, 0xD5, 0xEC, 0x0B, 0xF6,
+ 0x33, 0x0A, 0x0F, 0x5B, 0xFB, 0x45, 0xEB, 0xA9,
+ 0x6E, 0x6B, 0xCF, 0x55, 0x99, 0xAC, 0x22, 0xBE,
+ 0xB1, 0xA2, 0x3F, 0x25, 0x77, 0x8F, 0x7C, 0xF1,
+ 0xD4, 0x59, 0xA8, 0xE5, 0xD7, 0xCA, 0xA1, 0x93,
+ 0xE9, 0xAD, 0xF7, 0x94, 0xEF, 0xED, 0x3C, 0x2A,
+ 0x88, 0xB5, 0x35, 0x9D, 0x9C, 0x32, 0x5E, 0xB6,
+ 0x48, 0x9A, 0x7B, 0x26, 0x50, 0x90, 0x04, 0xA7,
+ 0xDD, 0x09, 0xB9, 0x98, 0xB2, 0xFE, 0xDF, 0x44,
+ 0x89, 0x29, 0x5D, 0xE2, 0x72, 0xC9, 0x28, 0x03,
+ 0x43, 0x8C, 0x52, 0x18, 0xC1, 0x56, 0x1B, 0x1A,
+ 0x01, 0x65, 0xDA, 0xBF, 0x07, 0xFF, 0x76, 0xE8,
+ 0x30, 0xA5, 0x4A, 0xA6, 0x12, 0x62, 0x24, 0x60,
+ 0x4B, 0x73, 0x0C, 0xF0, 0xFA, 0x42, 0xF4, 0x00,
+ 0xD2, 0xD0, 0xD6, 0x3B, 0xC2, 0x2F, 0xE1, 0x2B,
+ 0x70, 0xF8, 0x17, 0xCD, 0xB0, 0xCC, 0x82, 0x2D };
+
+unsigned char table_128[32] = {
+ 0x1A, 0x1C, 0x09, 0x17, 0x1B, 0x0B, 0x16, 0x1E,
+ 0x14, 0x0C, 0x12, 0x0E, 0x05, 0x03, 0x1F, 0x15,
+ 0x19, 0x0D, 0x10, 0x13, 0x0A, 0x01, 0x00, 0x11,
+ 0x02, 0x08, 0x0F, 0x18, 0x07, 0x04, 0x1D, 0x06 };
+
+unsigned char table_129[256] = {
+ 0x9D, 0x5F, 0xE8, 0x99, 0x57, 0x07, 0x16, 0xA6,
+ 0x9F, 0xB6, 0xDE, 0xED, 0x2D, 0xB3, 0xC0, 0x8E,
+ 0xCC, 0x49, 0xCE, 0xB0, 0x1B, 0xB1, 0x7A, 0xE0,
+ 0xEB, 0x28, 0xDB, 0x7D, 0x88, 0xC8, 0x06, 0x6C,
+ 0x02, 0xD0, 0x85, 0x7E, 0xDF, 0xF5, 0x78, 0xE5,
+ 0xA9, 0x71, 0xD9, 0xDD, 0xDC, 0xEE, 0x8C, 0x54,
+ 0xA0, 0x86, 0xFE, 0x0E, 0x55, 0xF7, 0x41, 0x47,
+ 0x1D, 0x15, 0xD6, 0xA4, 0xFF, 0x1F, 0x25, 0xF8,
+ 0x12, 0xE9, 0x74, 0x7B, 0x04, 0xE6, 0x4C, 0x31,
+ 0xA2, 0xBE, 0x0C, 0xB9, 0x17, 0xBD, 0x3D, 0xF0,
+ 0x9E, 0x4D, 0x4E, 0xB2, 0xE7, 0x40, 0xC9, 0x8A,
+ 0x67, 0x5E, 0x19, 0x0F, 0xB7, 0x22, 0x8D, 0xBA,
+ 0xFC, 0x93, 0x14, 0xEA, 0xFD, 0x0D, 0xD5, 0x38,
+ 0xA1, 0x84, 0x1C, 0x35, 0x60, 0x37, 0x43, 0x9C,
+ 0xCF, 0xEF, 0x3A, 0x72, 0xF2, 0x61, 0x75, 0x6A,
+ 0x42, 0xAC, 0xD3, 0x48, 0x77, 0xC5, 0x29, 0xF6,
+ 0x58, 0x79, 0xFA, 0x5D, 0xC7, 0x70, 0x53, 0x9A,
+ 0x6F, 0xC1, 0x0A, 0x90, 0x8F, 0x3E, 0x3B, 0x8B,
+ 0xEC, 0xBC, 0x20, 0x27, 0xC3, 0x66, 0x3F, 0x33,
+ 0xA5, 0x44, 0x2E, 0x32, 0x65, 0x18, 0xFB, 0x59,
+ 0x52, 0x50, 0xE2, 0x63, 0x2B, 0xCD, 0x64, 0xCB,
+ 0xD2, 0x68, 0x10, 0xA7, 0xAE, 0x11, 0xA8, 0x96,
+ 0x69, 0xAF, 0xC2, 0x34, 0x5C, 0x56, 0xE3, 0xF9,
+ 0xDA, 0x51, 0x81, 0x4A, 0x05, 0x00, 0xB8, 0x7C,
+ 0x30, 0x2F, 0x46, 0xB4, 0xC6, 0x87, 0x4B, 0x94,
+ 0x80, 0xF4, 0x7F, 0x3C, 0x26, 0xF1, 0x5B, 0xAB,
+ 0x91, 0x6E, 0x08, 0x76, 0x98, 0xD1, 0xE1, 0x36,
+ 0x21, 0xCA, 0xD8, 0x24, 0x9B, 0x39, 0xBB, 0xAD,
+ 0x13, 0x62, 0x97, 0x1A, 0x6D, 0x2C, 0x5A, 0xC4,
+ 0xD4, 0xA3, 0x03, 0xBF, 0x1E, 0xE4, 0xF3, 0x95,
+ 0x23, 0x73, 0x92, 0xB5, 0x01, 0x83, 0x82, 0xAA,
+ 0x09, 0x45, 0x6B, 0xD7, 0x0B, 0x89, 0x4F, 0x2A };
+
+unsigned char table_130[32] = {
+ 0x07, 0x03, 0x15, 0x0B, 0x02, 0x11, 0x17, 0x14,
+ 0x05, 0x10, 0x0A, 0x0F, 0x01, 0x1C, 0x1D, 0x0E,
+ 0x12, 0x06, 0x18, 0x16, 0x1A, 0x09, 0x13, 0x19,
+ 0x1B, 0x00, 0x08, 0x0D, 0x0C, 0x1E, 0x04, 0x1F };
+
+unsigned char table_131[32] = {
+ 0x1D, 0x13, 0x1B, 0x10, 0x07, 0x03, 0x0A, 0x02,
+ 0x00, 0x0C, 0x0E, 0x0B, 0x0D, 0x18, 0x12, 0x1F,
+ 0x1A, 0x04, 0x15, 0x11, 0x1E, 0x08, 0x1C, 0x14,
+ 0x19, 0x05, 0x0F, 0x17, 0x06, 0x01, 0x09, 0x16 };
+
+unsigned char table_132[256] = {
+ 0x33, 0x8D, 0x45, 0x6F, 0xFF, 0xF5, 0xB6, 0x53,
+ 0x3B, 0xF3, 0x07, 0xA4, 0x97, 0xEB, 0x6B, 0xA5,
+ 0xD3, 0xDC, 0x7B, 0x79, 0x93, 0xE7, 0xF7, 0x67,
+ 0x9C, 0x4F, 0x88, 0xF9, 0x3A, 0x2B, 0x27, 0x48,
+ 0x47, 0x18, 0xF4, 0xAD, 0xB4, 0x8F, 0x2A, 0x76,
+ 0x17, 0xE9, 0x1F, 0x40, 0x0C, 0x59, 0xD1, 0x4C,
+ 0x20, 0x31, 0x73, 0x54, 0xCD, 0x68, 0x08, 0x52,
+ 0x10, 0x62, 0x3D, 0xD2, 0x77, 0xF2, 0xD7, 0x30,
+ 0xCA, 0x16, 0x01, 0x50, 0x9F, 0x3F, 0x75, 0xED,
+ 0x90, 0x6A, 0x34, 0xCE, 0x05, 0x78, 0x5E, 0xD6,
+ 0x85, 0xCC, 0x29, 0xB8, 0xC1, 0x0D, 0xCB, 0x80,
+ 0x2E, 0x04, 0x00, 0x44, 0x32, 0x95, 0xBF, 0xFE,
+ 0x6E, 0x7C, 0xFD, 0xA7, 0x3C, 0x5C, 0xF0, 0xEC,
+ 0xAC, 0xF8, 0xB9, 0xC0, 0x1B, 0x3E, 0xE8, 0x66,
+ 0x5D, 0xDE, 0x49, 0x71, 0xAA, 0xAF, 0x21, 0x64,
+ 0x28, 0x8A, 0x4E, 0x98, 0x58, 0xA2, 0x23, 0xCF,
+ 0x9E, 0x63, 0x61, 0x91, 0x12, 0xC6, 0x8C, 0x19,
+ 0xA8, 0xD4, 0xC7, 0xDD, 0xFC, 0xBD, 0x38, 0xDF,
+ 0xEA, 0x2D, 0x7E, 0x7D, 0xE3, 0xE0, 0xC3, 0xD9,
+ 0x8B, 0x11, 0xF1, 0x4D, 0xC8, 0xB5, 0x55, 0xAE,
+ 0xE1, 0x89, 0xE5, 0xB3, 0xBC, 0x69, 0x9D, 0xA6,
+ 0x09, 0x9A, 0x74, 0x35, 0x1A, 0xFB, 0x24, 0xB7,
+ 0x13, 0x14, 0x94, 0x0A, 0x86, 0x0F, 0x60, 0x51,
+ 0xB0, 0x84, 0x22, 0x5B, 0x87, 0x43, 0x57, 0x0B,
+ 0x2F, 0x5F, 0x02, 0xD0, 0xBB, 0xA3, 0xC9, 0x7A,
+ 0xBE, 0xC2, 0x26, 0x46, 0xDB, 0x1E, 0x1D, 0x92,
+ 0xE2, 0xB2, 0x37, 0x6D, 0xD5, 0x4A, 0x0E, 0x4B,
+ 0x8E, 0xC5, 0x42, 0x99, 0xEE, 0xE4, 0xB1, 0x06,
+ 0xAB, 0x5A, 0x56, 0x41, 0x65, 0xBA, 0xFA, 0x83,
+ 0x15, 0xDA, 0x72, 0xA1, 0x81, 0x1C, 0xA9, 0x36,
+ 0x25, 0x96, 0x6C, 0x39, 0x82, 0xE6, 0x2C, 0x9B,
+ 0xC4, 0x7F, 0xA0, 0xD8, 0xEF, 0x03, 0x70, 0xF6 };
+
+unsigned char table_133[256] = {
+ 0x02, 0xF0, 0xED, 0xC4, 0xE4, 0x67, 0x60, 0x8B,
+ 0xF3, 0x77, 0x92, 0xE0, 0x85, 0x93, 0x1E, 0x8E,
+ 0x9A, 0x38, 0x61, 0x20, 0xB7, 0x68, 0xE1, 0x5E,
+ 0xD5, 0x63, 0xA9, 0xA5, 0xBE, 0x36, 0x12, 0x4D,
+ 0x86, 0x16, 0xD6, 0xB1, 0x23, 0x64, 0x4F, 0x62,
+ 0xFC, 0xA3, 0xD3, 0x04, 0x7D, 0x8C, 0xE2, 0xFF,
+ 0x5D, 0x30, 0xF5, 0x95, 0x1B, 0x5F, 0x73, 0xAA,
+ 0xE8, 0x07, 0x87, 0xDC, 0x54, 0x7C, 0xEE, 0x00,
+ 0xB8, 0xDE, 0x55, 0xBA, 0xD0, 0x50, 0xBB, 0x89,
+ 0x1C, 0xCC, 0x0E, 0xC0, 0x42, 0x11, 0xD8, 0xA2,
+ 0x2E, 0x33, 0xFE, 0x26, 0xD4, 0x10, 0xDA, 0xC5,
+ 0xFB, 0xAF, 0x98, 0x78, 0xB5, 0xBD, 0xC8, 0x8D,
+ 0x46, 0xA0, 0xD1, 0x7B, 0xBC, 0x75, 0xAB, 0x25,
+ 0xB2, 0x43, 0x57, 0xB6, 0xEC, 0xF4, 0x66, 0x05,
+ 0x9C, 0x08, 0x53, 0x80, 0xEA, 0x21, 0x2C, 0x6C,
+ 0x17, 0x71, 0xD2, 0x70, 0x76, 0x9E, 0x6B, 0x7A,
+ 0x58, 0xA7, 0xBF, 0x29, 0x03, 0x1F, 0x06, 0xC1,
+ 0xDD, 0x2F, 0x5C, 0x0B, 0x0D, 0x8A, 0x0A, 0xCB,
+ 0xCA, 0x6F, 0x19, 0x6A, 0xFA, 0xF7, 0xA8, 0xA1,
+ 0xEB, 0x88, 0x44, 0xAC, 0x01, 0x4E, 0x59, 0x94,
+ 0x72, 0x2B, 0xE9, 0x0F, 0x22, 0x9B, 0x27, 0x37,
+ 0x41, 0xF9, 0xF2, 0xE3, 0xEF, 0xB3, 0xD9, 0x2A,
+ 0x31, 0xC2, 0x0C, 0x15, 0x90, 0x14, 0xF6, 0x83,
+ 0xFD, 0x96, 0x9D, 0x7F, 0xA4, 0x39, 0xE7, 0x3F,
+ 0xE6, 0xC7, 0xCD, 0x1A, 0xCF, 0x48, 0x3C, 0x51,
+ 0x6D, 0x5B, 0x74, 0xC3, 0xC9, 0x09, 0x3D, 0x9F,
+ 0xDB, 0x32, 0x40, 0x18, 0xD7, 0xCE, 0x69, 0x49,
+ 0x3A, 0xF1, 0xB9, 0x56, 0x91, 0x99, 0x84, 0x24,
+ 0x7E, 0x34, 0x4B, 0xA6, 0x47, 0xB4, 0x6E, 0xDF,
+ 0x65, 0x3B, 0xAD, 0x45, 0x13, 0xC6, 0x81, 0xF8,
+ 0x4A, 0x2D, 0x8F, 0x4C, 0x97, 0x28, 0x3E, 0xE5,
+ 0x5A, 0x35, 0xB0, 0xAE, 0x82, 0x79, 0x1D, 0x52 };
+
+unsigned char table_134[32] = {
+ 0x09, 0x0F, 0x10, 0x0C, 0x03, 0x15, 0x07, 0x17,
+ 0x0E, 0x0B, 0x1D, 0x08, 0x19, 0x11, 0x00, 0x0A,
+ 0x01, 0x06, 0x18, 0x16, 0x0D, 0x13, 0x14, 0x12,
+ 0x02, 0x1B, 0x1A, 0x04, 0x05, 0x1F, 0x1C, 0x1E };
+
+unsigned char table_135[256] = {
+ 0x14, 0x34, 0xEA, 0x02, 0x2B, 0x5A, 0x10, 0x51,
+ 0xF3, 0x8F, 0x28, 0xB2, 0x50, 0x8B, 0x01, 0xCC,
+ 0x80, 0x15, 0x29, 0x42, 0xF4, 0x1D, 0xFB, 0xBB,
+ 0x1F, 0x43, 0x8C, 0x17, 0x1E, 0x81, 0x04, 0x98,
+ 0x46, 0xD8, 0xD5, 0x65, 0x4C, 0x1C, 0xDB, 0x40,
+ 0x5F, 0x1A, 0x31, 0x74, 0xF1, 0x64, 0x19, 0x05,
+ 0xFC, 0xF0, 0x73, 0xB6, 0x23, 0x77, 0x9C, 0xCE,
+ 0x70, 0xEF, 0xDA, 0xE0, 0xA2, 0x78, 0x84, 0xEB,
+ 0x9E, 0xC5, 0x95, 0xA3, 0xF6, 0xCA, 0xAD, 0x52,
+ 0xD0, 0x3F, 0x54, 0xA7, 0x33, 0xA9, 0x09, 0x6A,
+ 0x89, 0x7E, 0x75, 0xA8, 0xD6, 0x79, 0x9F, 0xAB,
+ 0x8E, 0x11, 0x0E, 0x3B, 0xAA, 0xE6, 0x85, 0x53,
+ 0x0A, 0x59, 0xEC, 0x94, 0xD7, 0x41, 0x86, 0x7D,
+ 0x2F, 0xC7, 0xDE, 0x06, 0xCB, 0x13, 0xBA, 0x58,
+ 0xC8, 0xC9, 0x07, 0x67, 0x7F, 0xA5, 0xB4, 0x2C,
+ 0x48, 0x6C, 0xB8, 0xD1, 0x30, 0xD3, 0x35, 0x4F,
+ 0x88, 0x26, 0x93, 0x32, 0x71, 0x3E, 0x3D, 0xF7,
+ 0x6D, 0x03, 0xED, 0x8A, 0x36, 0x55, 0x9B, 0x66,
+ 0x8D, 0x27, 0x7C, 0xF9, 0xA6, 0xC3, 0x20, 0x69,
+ 0x4A, 0xE3, 0x99, 0x5C, 0xBC, 0x45, 0x16, 0x6B,
+ 0xB9, 0x49, 0x82, 0xFF, 0xBD, 0xDD, 0xE9, 0x0C,
+ 0xD4, 0x44, 0xFD, 0x22, 0xE5, 0xAC, 0x61, 0xC4,
+ 0x90, 0x47, 0x37, 0x72, 0xA4, 0x7A, 0x24, 0x4D,
+ 0x5B, 0x12, 0x38, 0x92, 0x87, 0x1B, 0xE1, 0xA0,
+ 0x91, 0x3C, 0xEE, 0x6F, 0xC1, 0x0F, 0x56, 0xC2,
+ 0x9A, 0xF8, 0x18, 0xE8, 0xD2, 0xDC, 0x4B, 0xCF,
+ 0x39, 0xF5, 0xFE, 0x2A, 0x2D, 0x9D, 0xA1, 0xFA,
+ 0xE7, 0xBF, 0x6E, 0xE4, 0x2E, 0xB3, 0xCD, 0xE2,
+ 0xAF, 0x7B, 0xC0, 0x68, 0x97, 0xB5, 0x5D, 0xB7,
+ 0x21, 0x57, 0x83, 0x76, 0xB1, 0xAE, 0x5E, 0x0D,
+ 0x96, 0x4E, 0x08, 0xC6, 0x0B, 0xDF, 0x3A, 0xB0,
+ 0x00, 0x63, 0xD9, 0xBE, 0xF2, 0x60, 0x25, 0x62 };
+
+unsigned char table_136[256] = {
+ 0xD3, 0x1A, 0x00, 0xED, 0x59, 0x24, 0xA3, 0xF2,
+ 0xBA, 0x58, 0x4C, 0x5C, 0x75, 0x48, 0x98, 0xB0,
+ 0xCF, 0xC3, 0xF7, 0x88, 0x70, 0xB3, 0x3D, 0x3E,
+ 0x03, 0xF9, 0xC9, 0xFD, 0x80, 0x44, 0x7F, 0x3B,
+ 0x95, 0x5F, 0x31, 0x47, 0x15, 0x07, 0xB8, 0x08,
+ 0xCE, 0xDA, 0x71, 0x9F, 0x83, 0xB1, 0x55, 0x16,
+ 0xE6, 0xB2, 0xC7, 0xBE, 0x54, 0xE7, 0x2E, 0x8D,
+ 0x12, 0x21, 0x41, 0x69, 0xFE, 0x28, 0x11, 0x56,
+ 0x5A, 0xDD, 0xB6, 0x87, 0x78, 0x82, 0x4D, 0x7B,
+ 0x50, 0x9A, 0x9E, 0x62, 0xF8, 0x0A, 0x64, 0xF1,
+ 0x4E, 0x33, 0xAD, 0xBB, 0x79, 0x76, 0xD8, 0xCD,
+ 0x86, 0x34, 0x29, 0xD5, 0x7D, 0x72, 0xC5, 0xC1,
+ 0xDF, 0x09, 0x4A, 0xB4, 0xD2, 0x7A, 0xF0, 0xCC,
+ 0x0F, 0xA7, 0xD6, 0x2B, 0x20, 0x26, 0xEF, 0xAB,
+ 0x74, 0x1E, 0xE3, 0x77, 0xCB, 0x7C, 0x73, 0x5E,
+ 0x6B, 0x0D, 0x65, 0xA6, 0x30, 0xFB, 0xD0, 0xB7,
+ 0xAA, 0x94, 0x9D, 0x85, 0x13, 0x18, 0xA8, 0xF3,
+ 0xE0, 0xBC, 0x45, 0xCA, 0xC8, 0xDC, 0xE2, 0x3C,
+ 0x23, 0xE5, 0xB9, 0x90, 0x49, 0xA5, 0xE4, 0x36,
+ 0xFC, 0x53, 0xF6, 0xE8, 0xC6, 0x2C, 0x02, 0x25,
+ 0xC0, 0x8F, 0x61, 0xA4, 0x39, 0x8C, 0x5D, 0xAE,
+ 0x22, 0x1C, 0x2F, 0xD4, 0x6C, 0xD1, 0x51, 0xEA,
+ 0x4F, 0x7E, 0xA0, 0xF5, 0x6A, 0x32, 0xA2, 0x01,
+ 0xB5, 0x10, 0x2A, 0xAC, 0xA9, 0x06, 0xC4, 0x91,
+ 0x68, 0xE1, 0xBD, 0x14, 0x38, 0xFA, 0x6E, 0x3F,
+ 0x37, 0x66, 0xDB, 0x57, 0x43, 0x1B, 0x67, 0xAF,
+ 0x1F, 0x0B, 0x6D, 0x2D, 0x89, 0x04, 0x4B, 0x52,
+ 0xC2, 0xBF, 0xA1, 0x92, 0x99, 0x6F, 0x63, 0x81,
+ 0x27, 0x05, 0x96, 0x3A, 0xEC, 0x0E, 0x97, 0xD9,
+ 0xDE, 0x46, 0x35, 0x8B, 0x8E, 0x8A, 0xF4, 0xFF,
+ 0x60, 0xD7, 0xE9, 0x17, 0xEB, 0x9C, 0x84, 0x0C,
+ 0x93, 0x1D, 0x9B, 0x5B, 0x40, 0xEE, 0x42, 0x19 };
+
+unsigned char table_137[32] = {
+ 0x0F, 0x09, 0x02, 0x06, 0x18, 0x0B, 0x1E, 0x05,
+ 0x11, 0x1D, 0x16, 0x01, 0x13, 0x10, 0x0E, 0x1A,
+ 0x1B, 0x00, 0x0D, 0x08, 0x15, 0x14, 0x19, 0x17,
+ 0x03, 0x1F, 0x0A, 0x12, 0x0C, 0x07, 0x04, 0x1C };
+
+unsigned char table_138[32] = {
+ 0x0D, 0x1C, 0x1F, 0x15, 0x0F, 0x14, 0x1B, 0x12,
+ 0x09, 0x0B, 0x19, 0x07, 0x11, 0x16, 0x0C, 0x04,
+ 0x13, 0x05, 0x1D, 0x03, 0x0E, 0x0A, 0x08, 0x1E,
+ 0x01, 0x06, 0x18, 0x17, 0x10, 0x1A, 0x02, 0x00 };
+
+unsigned char table_139[32] = {
+ 0x05, 0x15, 0x1D, 0x02, 0x0F, 0x03, 0x17, 0x1A,
+ 0x0A, 0x00, 0x1F, 0x12, 0x0E, 0x11, 0x1B, 0x13,
+ 0x0B, 0x0D, 0x09, 0x18, 0x1E, 0x08, 0x14, 0x07,
+ 0x0C, 0x04, 0x16, 0x19, 0x1C, 0x06, 0x10, 0x01 };
+
+unsigned char table_140[32] = {
+ 0x06, 0x1E, 0x0C, 0x11, 0x13, 0x08, 0x15, 0x01,
+ 0x1D, 0x03, 0x0F, 0x19, 0x18, 0x04, 0x00, 0x14,
+ 0x12, 0x1A, 0x0B, 0x0E, 0x02, 0x1B, 0x07, 0x05,
+ 0x1F, 0x17, 0x09, 0x0A, 0x0D, 0x16, 0x10, 0x1C };
+
+unsigned char table_141[256] = {
+ 0xE1, 0x0A, 0x28, 0xCD, 0x8A, 0x1E, 0x26, 0x10,
+ 0xC0, 0x6F, 0x06, 0x2C, 0xF8, 0x51, 0x6C, 0x8F,
+ 0xA8, 0x8C, 0x41, 0xF4, 0xED, 0x36, 0xAC, 0x89,
+ 0xBD, 0x9D, 0x42, 0x50, 0x95, 0x07, 0x2A, 0x9B,
+ 0x7E, 0xA3, 0x6B, 0x30, 0x72, 0x4E, 0xBE, 0xD8,
+ 0x8B, 0x5B, 0x1A, 0x56, 0x05, 0xEF, 0xEE, 0x64,
+ 0xFF, 0xFD, 0x93, 0xB5, 0xD6, 0x04, 0x57, 0xAE,
+ 0x4D, 0x6D, 0x2F, 0xBA, 0x40, 0xE0, 0xDB, 0xF2,
+ 0xCC, 0x08, 0x35, 0x02, 0xC4, 0x65, 0x66, 0x76,
+ 0xA1, 0x97, 0x9F, 0x6A, 0x90, 0xA7, 0x34, 0x1B,
+ 0x18, 0xB9, 0xA2, 0xDE, 0x23, 0x1F, 0xCB, 0xE6,
+ 0xAB, 0xCF, 0xAD, 0x4A, 0xF7, 0x24, 0xD0, 0xE8,
+ 0x8D, 0x49, 0xEA, 0x0F, 0x94, 0x22, 0xD3, 0x74,
+ 0x71, 0x0D, 0x21, 0x14, 0x39, 0x4B, 0x16, 0x25,
+ 0x5A, 0xB7, 0x17, 0x67, 0x59, 0x47, 0x27, 0x4F,
+ 0x32, 0x3B, 0x63, 0x0C, 0xF0, 0xF3, 0x7B, 0xC7,
+ 0xCA, 0x3A, 0x9A, 0xE2, 0xD5, 0xFA, 0x91, 0xFC,
+ 0x86, 0x81, 0x99, 0xB4, 0xBC, 0x7C, 0xC5, 0xBF,
+ 0xC1, 0xF5, 0x77, 0xA4, 0x79, 0x11, 0x8E, 0x75,
+ 0x55, 0x3D, 0x78, 0x20, 0x37, 0x3E, 0x85, 0xE4,
+ 0x2E, 0x82, 0xA9, 0x7A, 0x31, 0xC9, 0xB3, 0xFE,
+ 0x4C, 0x7D, 0xC3, 0xA0, 0x0E, 0x96, 0x5C, 0xC6,
+ 0x1C, 0x5F, 0xD7, 0xDD, 0x83, 0xC8, 0x9E, 0xEC,
+ 0x3F, 0xAF, 0x38, 0x9C, 0xD9, 0xB6, 0xDA, 0xD4,
+ 0x61, 0x44, 0x43, 0xAA, 0xB1, 0xCE, 0xE7, 0x84,
+ 0x00, 0x0B, 0xFB, 0x68, 0xC2, 0x3C, 0x58, 0xB2,
+ 0x69, 0x7F, 0x33, 0x2B, 0x80, 0x03, 0xE9, 0x88,
+ 0x29, 0x12, 0x01, 0x6E, 0x62, 0xF1, 0xA6, 0xF9,
+ 0x5D, 0xD2, 0xE3, 0x53, 0x09, 0x2D, 0xBB, 0x15,
+ 0xEB, 0x13, 0xA5, 0xF6, 0x73, 0x19, 0x60, 0xB0,
+ 0xD1, 0x48, 0x92, 0x1D, 0x52, 0x5E, 0x45, 0x70,
+ 0x98, 0x54, 0xB8, 0xDC, 0x46, 0xDF, 0x87, 0xE5 };
+
+unsigned char table_142[256] = {
+ 0x90, 0x94, 0xBE, 0x14, 0x99, 0xEB, 0x45, 0x0F,
+ 0x34, 0x4A, 0xE3, 0x79, 0xD2, 0x64, 0x4D, 0x69,
+ 0x91, 0xDE, 0xB9, 0x1C, 0x59, 0x20, 0x6C, 0x0B,
+ 0x16, 0xC7, 0x1D, 0x18, 0x02, 0x7D, 0x13, 0xB2,
+ 0x7B, 0x81, 0xCF, 0x61, 0xA3, 0x33, 0x00, 0x73,
+ 0x5A, 0x8A, 0xA1, 0xA8, 0x31, 0xAC, 0xF0, 0x67,
+ 0xAE, 0xA5, 0x2A, 0x96, 0x58, 0xF4, 0xB7, 0x0E,
+ 0xE1, 0x54, 0x27, 0x83, 0x09, 0x85, 0xF8, 0x84,
+ 0xEA, 0xAD, 0x06, 0xED, 0x43, 0xFF, 0xA2, 0x6E,
+ 0x68, 0x46, 0x74, 0x47, 0x3C, 0xAA, 0xBC, 0x55,
+ 0xA7, 0xC3, 0x82, 0xDC, 0xBF, 0x38, 0x80, 0x15,
+ 0xF6, 0xB3, 0x92, 0x7C, 0x93, 0x3F, 0xE9, 0x4C,
+ 0x35, 0x30, 0x32, 0xF3, 0x88, 0xC0, 0x49, 0x6D,
+ 0xCE, 0x42, 0xDF, 0xFD, 0x78, 0x6A, 0x24, 0xCA,
+ 0xB8, 0xFC, 0xA6, 0x5F, 0x29, 0xFE, 0x0C, 0x5C,
+ 0x0D, 0x23, 0x8B, 0x9D, 0xD4, 0x03, 0x2C, 0x9C,
+ 0x77, 0xD8, 0x39, 0x8C, 0x57, 0xD5, 0xE0, 0x8F,
+ 0xC6, 0xB0, 0xCD, 0x48, 0xC9, 0xA0, 0xDA, 0xC8,
+ 0xD1, 0x5B, 0xAB, 0x37, 0x5D, 0x63, 0xAF, 0xF9,
+ 0x17, 0x1B, 0xE5, 0xF1, 0x36, 0xC1, 0x04, 0x26,
+ 0x6F, 0x9E, 0xD9, 0x2F, 0x7F, 0xB5, 0x3A, 0xD6,
+ 0xE6, 0x40, 0x07, 0xCB, 0x7E, 0x3E, 0xC5, 0x22,
+ 0xEC, 0xE2, 0xD3, 0x4E, 0x65, 0x2D, 0x70, 0xE7,
+ 0x10, 0x19, 0xD0, 0xEF, 0xBD, 0xC2, 0x44, 0xB4,
+ 0xF7, 0xA4, 0x53, 0x9F, 0x86, 0xFA, 0xE8, 0x4B,
+ 0x28, 0x3D, 0x9B, 0x56, 0x89, 0x6B, 0x25, 0x71,
+ 0x60, 0x11, 0x9A, 0x5E, 0x1A, 0x52, 0x08, 0x4F,
+ 0xB1, 0xDD, 0xBB, 0x98, 0xFB, 0x12, 0x3B, 0x0A,
+ 0x2E, 0xDB, 0x62, 0x8D, 0xC4, 0x75, 0xA9, 0x2B,
+ 0xE4, 0x97, 0x72, 0xF5, 0xEE, 0xF2, 0xB6, 0x21,
+ 0xBA, 0x7A, 0x76, 0x41, 0x50, 0x66, 0x05, 0x8E,
+ 0xCC, 0x1E, 0x87, 0xD7, 0x01, 0x1F, 0x51, 0x95 };
+
+unsigned char table_143[32] = {
+ 0x0E, 0x16, 0x18, 0x11, 0x0C, 0x01, 0x12, 0x1F,
+ 0x08, 0x15, 0x0A, 0x06, 0x1C, 0x1E, 0x02, 0x1A,
+ 0x17, 0x03, 0x07, 0x13, 0x05, 0x19, 0x10, 0x0F,
+ 0x0D, 0x14, 0x09, 0x0B, 0x1B, 0x00, 0x1D, 0x04 };
+
+unsigned char table_144[32] = {
+ 0x00, 0x1B, 0x17, 0x19, 0x1D, 0x11, 0x0D, 0x1A,
+ 0x13, 0x03, 0x1E, 0x09, 0x10, 0x0E, 0x15, 0x05,
+ 0x0B, 0x1C, 0x1F, 0x08, 0x0A, 0x06, 0x01, 0x0F,
+ 0x16, 0x14, 0x02, 0x04, 0x07, 0x18, 0x12, 0x0C };
+
+unsigned char table_145[256] = {
+ 0xF9, 0x2C, 0x38, 0x74, 0xDA, 0x65, 0x85, 0x0E,
+ 0xBA, 0x64, 0xDB, 0xE3, 0xB6, 0x8B, 0x0B, 0x5E,
+ 0x01, 0x0F, 0x12, 0x8C, 0xD4, 0xCC, 0xB1, 0x7B,
+ 0xE7, 0xBC, 0x2E, 0x87, 0x84, 0x3B, 0xF8, 0x4C,
+ 0x8E, 0x59, 0x2D, 0xAA, 0xCE, 0x28, 0x1B, 0xEE,
+ 0x7F, 0x5C, 0xFB, 0x62, 0x05, 0xD9, 0xDD, 0x9D,
+ 0x49, 0x66, 0x82, 0x71, 0xD2, 0xC7, 0xEB, 0xCF,
+ 0x5B, 0x41, 0x25, 0xC8, 0x6C, 0xFF, 0x78, 0x97,
+ 0x0C, 0xA2, 0x50, 0x7A, 0xAF, 0x2F, 0xB0, 0x7E,
+ 0xBB, 0x73, 0xA0, 0x9B, 0x09, 0xDE, 0x35, 0xE9,
+ 0x5A, 0x70, 0x56, 0xC5, 0x81, 0x19, 0x55, 0xAB,
+ 0xC1, 0xB4, 0x2A, 0x30, 0x54, 0x6F, 0x3E, 0x46,
+ 0x5D, 0x37, 0xF5, 0x57, 0x6B, 0x7C, 0x43, 0xE1,
+ 0x4A, 0x3F, 0xB2, 0x4B, 0x77, 0xB5, 0x44, 0xD6,
+ 0x91, 0x11, 0x72, 0xE8, 0xBE, 0xA5, 0xA8, 0xD3,
+ 0x9A, 0x17, 0x86, 0x88, 0x16, 0x3C, 0x36, 0xD8,
+ 0x6E, 0x07, 0x8D, 0x5F, 0xFA, 0xF1, 0x24, 0x7D,
+ 0x20, 0x60, 0x0D, 0x89, 0xC9, 0x29, 0xA7, 0x2B,
+ 0x4E, 0x10, 0x9F, 0xE5, 0x61, 0x32, 0x3A, 0xBF,
+ 0x93, 0xE6, 0xF3, 0x52, 0x80, 0xC4, 0x02, 0x22,
+ 0xA4, 0xBD, 0xF0, 0x48, 0x51, 0xF2, 0xD7, 0x33,
+ 0x00, 0x53, 0x98, 0xEC, 0x47, 0x39, 0xB9, 0x90,
+ 0x76, 0x4F, 0x68, 0x3D, 0x9C, 0x92, 0xD5, 0xB8,
+ 0xAE, 0xD0, 0xF4, 0x67, 0x58, 0xC0, 0x06, 0x08,
+ 0x14, 0x31, 0xDC, 0xA1, 0x15, 0xDF, 0xCA, 0xE2,
+ 0x23, 0xFE, 0xE4, 0x8F, 0x0A, 0xFC, 0x8A, 0xA3,
+ 0xC6, 0xCD, 0x6A, 0x75, 0xFD, 0x42, 0xB7, 0x79,
+ 0x96, 0x1D, 0x63, 0x18, 0xA9, 0x1C, 0x83, 0x6D,
+ 0xE0, 0x34, 0x04, 0xA6, 0x13, 0xAC, 0xD1, 0xF7,
+ 0x26, 0xC3, 0x1F, 0x27, 0x45, 0x95, 0xCB, 0x21,
+ 0xED, 0x1A, 0x9E, 0x99, 0xEA, 0x40, 0x94, 0x4D,
+ 0x69, 0xF6, 0xEF, 0xC2, 0xAD, 0x03, 0xB3, 0x1E };
+
+unsigned char table_146[256] = {
+ 0x1C, 0xF5, 0x16, 0xD2, 0xCC, 0xDC, 0x1E, 0x29,
+ 0xE3, 0x17, 0x3B, 0x66, 0x6A, 0xF7, 0x03, 0xB2,
+ 0x92, 0x45, 0x4D, 0xD6, 0x0C, 0x5E, 0xE6, 0x01,
+ 0xDE, 0xCE, 0x83, 0xFA, 0x35, 0x02, 0x85, 0xC4,
+ 0x2E, 0x89, 0x8D, 0xE7, 0x30, 0x93, 0xDD, 0x70,
+ 0x80, 0xD9, 0x6D, 0x81, 0x07, 0x8E, 0xA9, 0xA6,
+ 0x5F, 0xC9, 0xF3, 0x9D, 0x65, 0xE8, 0x88, 0x0B,
+ 0x49, 0xAA, 0xB7, 0x6C, 0x11, 0xFC, 0x6F, 0xA3,
+ 0xF8, 0x52, 0x0E, 0xD4, 0x08, 0x25, 0x27, 0x33,
+ 0x2F, 0xF0, 0x2B, 0x47, 0xDA, 0x4C, 0x39, 0x54,
+ 0xB9, 0xC1, 0xEA, 0x7C, 0x44, 0xEB, 0x06, 0xE1,
+ 0x8C, 0x9B, 0x74, 0x42, 0x4F, 0x0A, 0x69, 0x2A,
+ 0x2D, 0xA1, 0x19, 0xD5, 0xC3, 0x87, 0x68, 0xFF,
+ 0xEC, 0xE4, 0x86, 0xCF, 0xF6, 0x79, 0x34, 0xA8,
+ 0x72, 0xF4, 0x8B, 0xAF, 0xA5, 0x00, 0xBA, 0x5C,
+ 0x23, 0xB8, 0xC8, 0x59, 0xBF, 0x6E, 0xCB, 0x20,
+ 0x1F, 0x53, 0x97, 0x4B, 0xD0, 0x55, 0x5B, 0xDF,
+ 0x8A, 0xED, 0x9A, 0x62, 0xC5, 0xD7, 0x18, 0x82,
+ 0xC7, 0x12, 0x15, 0x1B, 0xC0, 0x38, 0xCA, 0x26,
+ 0xDB, 0xAE, 0xF9, 0x90, 0x1A, 0xF2, 0x56, 0x32,
+ 0x21, 0x3C, 0x43, 0xEE, 0xA4, 0x13, 0x94, 0xA2,
+ 0x46, 0x77, 0xBC, 0xB6, 0x9C, 0x0D, 0xCD, 0x37,
+ 0x63, 0x60, 0x6B, 0x3A, 0x3E, 0xA7, 0xD8, 0xFE,
+ 0xFB, 0xEF, 0x67, 0xFD, 0xAD, 0xF1, 0x09, 0x1D,
+ 0xE9, 0x51, 0xB4, 0x95, 0x75, 0x0F, 0xB3, 0xD3,
+ 0xAB, 0x22, 0xBB, 0x61, 0x7F, 0x5A, 0x58, 0x7B,
+ 0x73, 0xC2, 0x05, 0xE0, 0x14, 0xE2, 0xAC, 0x91,
+ 0xBE, 0x4E, 0xC6, 0x7A, 0x84, 0x50, 0x28, 0x3F,
+ 0xB0, 0x04, 0x7E, 0xD1, 0x40, 0xBD, 0xE5, 0x71,
+ 0xB1, 0x78, 0x41, 0x9E, 0x57, 0x64, 0x8F, 0x24,
+ 0x4A, 0x9F, 0x3D, 0x31, 0x36, 0x5D, 0xA0, 0x2C,
+ 0x7D, 0x96, 0x76, 0x99, 0xB5, 0x48, 0x98, 0x10 };
+
+unsigned char table_147[32] = {
+ 0x17, 0x07, 0x0D, 0x16, 0x00, 0x1B, 0x1F, 0x09,
+ 0x10, 0x11, 0x14, 0x0A, 0x02, 0x06, 0x13, 0x0C,
+ 0x08, 0x1E, 0x0F, 0x12, 0x05, 0x15, 0x19, 0x01,
+ 0x1C, 0x1A, 0x03, 0x18, 0x04, 0x0B, 0x1D, 0x0E };
+
+unsigned char table_148[256] = {
+ 0xFB, 0x23, 0xBC, 0x5A, 0x8C, 0x02, 0x42, 0x3B,
+ 0x95, 0x0C, 0x21, 0x0E, 0x14, 0xDF, 0x11, 0xC0,
+ 0xDB, 0x5E, 0xD3, 0xEA, 0xCE, 0xB4, 0x32, 0x12,
+ 0x70, 0x68, 0xA3, 0x25, 0x5B, 0x4B, 0x47, 0xA5,
+ 0x84, 0x9B, 0xFA, 0xD1, 0xE1, 0x3C, 0x20, 0x93,
+ 0x41, 0x26, 0x81, 0x39, 0x17, 0xA4, 0xCF, 0xB9,
+ 0xC5, 0x5F, 0x1C, 0xB3, 0x88, 0xC2, 0x92, 0x30,
+ 0x0A, 0xB8, 0xA0, 0xE2, 0x50, 0x2B, 0x48, 0x1E,
+ 0xD5, 0x13, 0xC7, 0x46, 0x9E, 0x2A, 0xF7, 0x7E,
+ 0xE8, 0x82, 0x60, 0x7A, 0x36, 0x97, 0x0F, 0x8F,
+ 0x8B, 0x80, 0xE0, 0xEB, 0xB1, 0xC6, 0x6E, 0xAE,
+ 0x90, 0x76, 0xA7, 0x31, 0xBE, 0x9C, 0x18, 0x6D,
+ 0xAB, 0x6C, 0x7B, 0xFE, 0x62, 0x05, 0xE9, 0x66,
+ 0x2E, 0x38, 0xB5, 0xB2, 0xFD, 0xFC, 0x7F, 0xE3,
+ 0xA1, 0xF1, 0x99, 0x4D, 0x79, 0x22, 0xD2, 0x37,
+ 0x29, 0x01, 0x54, 0x00, 0xBD, 0x51, 0x1B, 0x07,
+ 0x0B, 0x4A, 0xEE, 0x57, 0xDA, 0x1A, 0x06, 0xCA,
+ 0xCB, 0x9A, 0xC9, 0x7D, 0xE4, 0xDC, 0xE5, 0x8D,
+ 0x75, 0x4F, 0xF6, 0xA2, 0x65, 0x7C, 0xD9, 0x9D,
+ 0x03, 0x27, 0x2D, 0x4C, 0x49, 0xD4, 0x5D, 0x3E,
+ 0xBA, 0x1D, 0xD8, 0x91, 0x74, 0x10, 0xF8, 0xDE,
+ 0xEF, 0xF0, 0x6A, 0x04, 0x72, 0x08, 0x78, 0x3A,
+ 0x53, 0xC4, 0x34, 0xF2, 0x64, 0xAF, 0x86, 0xC3,
+ 0xF3, 0x73, 0x67, 0xCC, 0x58, 0xF4, 0x96, 0xAC,
+ 0x3D, 0xE7, 0x15, 0x8E, 0x19, 0x61, 0xF9, 0xB6,
+ 0xCD, 0x87, 0xAA, 0xB0, 0x1F, 0x6F, 0xAD, 0x28,
+ 0xC8, 0x69, 0x56, 0xC1, 0x71, 0xED, 0xE6, 0x98,
+ 0x6B, 0x59, 0xB7, 0xF5, 0x2C, 0xEC, 0xA8, 0x94,
+ 0x89, 0xBB, 0xA9, 0xD7, 0x2F, 0x8A, 0x4E, 0xD6,
+ 0x33, 0x16, 0x0D, 0x83, 0x5C, 0x52, 0x85, 0xA6,
+ 0x40, 0x45, 0x9F, 0x44, 0x63, 0x35, 0x77, 0xFF,
+ 0x09, 0x43, 0xBF, 0xD0, 0x55, 0xDD, 0x3F, 0x24 };
+
+unsigned char table_149[32] = {
+ 0x1B, 0x0B, 0x0C, 0x06, 0x1F, 0x17, 0x04, 0x1A,
+ 0x1E, 0x02, 0x0F, 0x16, 0x0E, 0x09, 0x10, 0x01,
+ 0x13, 0x19, 0x11, 0x00, 0x0A, 0x05, 0x03, 0x1C,
+ 0x18, 0x1D, 0x14, 0x0D, 0x07, 0x08, 0x15, 0x12 };
+
+unsigned char table_150[256] = {
+ 0x57, 0xBC, 0x9D, 0x46, 0x14, 0xD0, 0x94, 0x95,
+ 0x1B, 0x12, 0xB8, 0xD4, 0x53, 0x73, 0x83, 0xE6,
+ 0x75, 0xE1, 0xD1, 0x0D, 0xDF, 0x23, 0x13, 0x40,
+ 0xF1, 0x0C, 0xA0, 0xC1, 0x22, 0xDA, 0xE8, 0xFB,
+ 0xE5, 0xC4, 0x16, 0x9C, 0x3F, 0xC3, 0x78, 0x3A,
+ 0x06, 0xC7, 0xA8, 0x79, 0xA4, 0xB3, 0x55, 0x88,
+ 0xA9, 0x82, 0xE3, 0x68, 0xFC, 0x3B, 0x26, 0x81,
+ 0xB4, 0x0A, 0x7D, 0x96, 0xDB, 0x2C, 0xE2, 0xCD,
+ 0x92, 0x5C, 0xED, 0x0E, 0x42, 0x98, 0xBE, 0xB7,
+ 0x63, 0x25, 0x7B, 0xD9, 0xEF, 0x11, 0xB9, 0xA3,
+ 0xFA, 0x00, 0x2A, 0x91, 0x71, 0xBF, 0xB2, 0x3D,
+ 0x20, 0x4C, 0xB0, 0x8C, 0x3C, 0x27, 0xAF, 0x09,
+ 0x10, 0x5D, 0x2B, 0x1D, 0xBD, 0x4B, 0x54, 0xD3,
+ 0xAB, 0x1A, 0xE7, 0xF8, 0x56, 0x65, 0xA5, 0xAD,
+ 0xEC, 0x17, 0x45, 0x28, 0xCA, 0xEA, 0x01, 0xF5,
+ 0x34, 0x84, 0x43, 0x8B, 0x03, 0x02, 0x90, 0x6B,
+ 0x60, 0xCE, 0x19, 0x86, 0x4F, 0x08, 0x35, 0x9A,
+ 0xAE, 0x07, 0xE0, 0xB6, 0xD6, 0x2D, 0xD2, 0x89,
+ 0x5F, 0xA6, 0x72, 0x05, 0x36, 0xB5, 0xC0, 0x5A,
+ 0x4D, 0xD7, 0x30, 0x37, 0x87, 0x50, 0xA2, 0x48,
+ 0x29, 0xAC, 0xDE, 0x93, 0x24, 0x6E, 0x1E, 0xF7,
+ 0x52, 0x5E, 0x41, 0xC8, 0xEB, 0x31, 0x7E, 0xE9,
+ 0x67, 0x7A, 0x47, 0x85, 0x8D, 0x74, 0x9E, 0x64,
+ 0x38, 0x9B, 0xBA, 0xCC, 0x9F, 0x8E, 0xEE, 0x0F,
+ 0xB1, 0x7C, 0x6A, 0xBB, 0x2E, 0x58, 0x70, 0x7F,
+ 0x4E, 0x4A, 0x1C, 0x5B, 0xF0, 0xA1, 0x61, 0xF6,
+ 0x15, 0x33, 0xE4, 0xF9, 0x2F, 0x62, 0x1F, 0x76,
+ 0x32, 0xCB, 0x49, 0xFE, 0x8F, 0xD5, 0xDC, 0x66,
+ 0x0B, 0x3E, 0xC5, 0x21, 0xC6, 0x6C, 0x18, 0xC2,
+ 0x6D, 0xFF, 0x51, 0x99, 0xCF, 0xFD, 0x59, 0xA7,
+ 0xAA, 0x8A, 0xF2, 0x69, 0x39, 0x6F, 0x77, 0xDD,
+ 0x97, 0xC9, 0xF3, 0x04, 0xD8, 0xF4, 0x80, 0x44 };
+
+unsigned char table_151[256] = {
+ 0x78, 0x6C, 0xC5, 0x0C, 0x2D, 0xA7, 0x97, 0x9C,
+ 0x22, 0x76, 0x3E, 0x81, 0x51, 0x47, 0x59, 0x71,
+ 0xB1, 0xA2, 0x4A, 0x3C, 0xB5, 0x16, 0x06, 0x95,
+ 0xB9, 0x01, 0xE6, 0x91, 0x96, 0x1C, 0x1B, 0xAD,
+ 0x61, 0x64, 0xB2, 0xE7, 0x29, 0x19, 0x52, 0x3B,
+ 0xFA, 0xAF, 0x30, 0xDB, 0xD4, 0x0B, 0xFE, 0x75,
+ 0x1F, 0xBE, 0xCB, 0xF6, 0xEA, 0x31, 0xF8, 0xD8,
+ 0xA3, 0x82, 0x73, 0x1D, 0x99, 0xF0, 0xCC, 0xB6,
+ 0x46, 0x26, 0xAA, 0x8C, 0x87, 0x90, 0x24, 0x8F,
+ 0x7A, 0x13, 0xEE, 0xD1, 0xA9, 0x05, 0xB3, 0xF7,
+ 0x02, 0x7C, 0x4C, 0x1E, 0xFF, 0xE5, 0x77, 0xAB,
+ 0xD6, 0x98, 0x20, 0x4D, 0xC4, 0x23, 0xF4, 0xA4,
+ 0x85, 0x9A, 0x8E, 0x1A, 0x0E, 0xF5, 0x15, 0x60,
+ 0x38, 0x72, 0xE9, 0xF1, 0xC3, 0x68, 0xF2, 0x93,
+ 0xD3, 0x2A, 0x48, 0x74, 0xC2, 0x57, 0xA1, 0x7D,
+ 0x94, 0x37, 0x92, 0x5C, 0xE1, 0x41, 0x83, 0xD5,
+ 0x65, 0x14, 0xA6, 0xDC, 0x44, 0x27, 0xEF, 0xD7,
+ 0x25, 0x10, 0x2C, 0x7F, 0x40, 0xA5, 0x55, 0xBD,
+ 0x2B, 0x0D, 0xD0, 0xFC, 0xDF, 0xA0, 0x04, 0x00,
+ 0x62, 0xB4, 0x5A, 0xEB, 0x6B, 0x84, 0x7E, 0x6A,
+ 0xDE, 0xED, 0x66, 0x03, 0xFB, 0x2E, 0x4F, 0x4E,
+ 0xBB, 0x36, 0x5B, 0x18, 0xE3, 0x69, 0x3F, 0xEC,
+ 0xE4, 0xD2, 0x0A, 0x34, 0x63, 0xCF, 0xA8, 0xF9,
+ 0x9B, 0x7B, 0x6F, 0xE8, 0x49, 0xC1, 0x09, 0x54,
+ 0xF3, 0x50, 0x67, 0x79, 0xC0, 0x9F, 0x8D, 0x5F,
+ 0x17, 0x70, 0x11, 0xC8, 0xBC, 0xC6, 0xE0, 0x35,
+ 0x39, 0xC7, 0x6E, 0x21, 0xBF, 0xDA, 0x6D, 0x28,
+ 0x0F, 0xDD, 0x33, 0xAC, 0x8A, 0x12, 0xC9, 0xCD,
+ 0xB8, 0x45, 0xAE, 0x32, 0xCE, 0xE2, 0x56, 0xFD,
+ 0x42, 0x89, 0x86, 0xCA, 0x4B, 0x3D, 0x5E, 0xBA,
+ 0x8B, 0x5D, 0xB0, 0xB7, 0xD9, 0x58, 0x2F, 0x08,
+ 0x43, 0x3A, 0x53, 0x9E, 0x80, 0x88, 0x07, 0x9D };
+
+unsigned char table_152[32] = {
+ 0x02, 0x1A, 0x17, 0x1D, 0x01, 0x03, 0x13, 0x1E,
+ 0x05, 0x18, 0x06, 0x0A, 0x0C, 0x04, 0x1B, 0x00,
+ 0x1C, 0x09, 0x1F, 0x16, 0x07, 0x0F, 0x0B, 0x0E,
+ 0x14, 0x12, 0x0D, 0x10, 0x19, 0x11, 0x08, 0x15 };
+
+unsigned char table_153[32] = {
+ 0x0E, 0x14, 0x12, 0x1E, 0x1C, 0x02, 0x06, 0x16,
+ 0x18, 0x0D, 0x17, 0x0C, 0x1D, 0x11, 0x08, 0x19,
+ 0x07, 0x0F, 0x13, 0x04, 0x03, 0x1B, 0x0B, 0x1F,
+ 0x1A, 0x0A, 0x05, 0x10, 0x00, 0x01, 0x15, 0x09 };
+
+unsigned char table_154[256] = {
+ 0x27, 0x5A, 0x08, 0x5B, 0xF4, 0x39, 0x13, 0x6F,
+ 0x67, 0xEA, 0x22, 0xCA, 0x5C, 0xCF, 0x18, 0x7C,
+ 0x05, 0x87, 0x60, 0xCC, 0x40, 0xC6, 0xE8, 0x6D,
+ 0xF5, 0x2A, 0x2D, 0xA2, 0x8C, 0x82, 0xE9, 0xDC,
+ 0xD6, 0x65, 0x74, 0x8E, 0x42, 0x4F, 0x3E, 0x55,
+ 0xFF, 0xC7, 0x9D, 0x0F, 0x81, 0xE2, 0x4C, 0xE6,
+ 0xEB, 0x4D, 0x70, 0xD1, 0x49, 0x43, 0x3D, 0x69,
+ 0x0C, 0x45, 0x28, 0x00, 0x99, 0xAE, 0xEC, 0xB8,
+ 0xC3, 0x17, 0x93, 0x8D, 0x36, 0x3C, 0x46, 0x2B,
+ 0x29, 0xC5, 0xB4, 0xB1, 0xD0, 0x0D, 0xAD, 0xFE,
+ 0xE5, 0xA8, 0x3B, 0x1A, 0x2C, 0xDF, 0x07, 0x86,
+ 0xB0, 0xD3, 0x7A, 0x59, 0x79, 0x8B, 0xC1, 0x9A,
+ 0x30, 0xDB, 0x24, 0xF3, 0xD8, 0x04, 0x25, 0xC2,
+ 0xA3, 0x98, 0x96, 0x7B, 0x71, 0x4E, 0x5E, 0x58,
+ 0xA5, 0x51, 0x88, 0xDA, 0xF8, 0xC0, 0x7D, 0xF6,
+ 0x31, 0x5F, 0x09, 0x16, 0x21, 0x62, 0x01, 0x64,
+ 0x9B, 0x3A, 0x2F, 0x61, 0x19, 0xA1, 0xB7, 0xE0,
+ 0xB9, 0x12, 0xA0, 0xBA, 0x6E, 0x8A, 0xFB, 0xD9,
+ 0x38, 0x1B, 0xD5, 0xB3, 0x10, 0xED, 0xE4, 0x6A,
+ 0x32, 0xBD, 0x75, 0xD4, 0x1C, 0xFD, 0x73, 0x77,
+ 0x54, 0xC8, 0x97, 0x47, 0x35, 0x94, 0xE3, 0xCD,
+ 0x6B, 0xBB, 0xF9, 0xAC, 0x11, 0x14, 0xAF, 0x78,
+ 0x3F, 0xCE, 0x26, 0x44, 0xEE, 0xFC, 0x15, 0x66,
+ 0x4B, 0xA6, 0x20, 0x23, 0xBE, 0x84, 0x1D, 0x7E,
+ 0x0B, 0x56, 0x92, 0x0A, 0xFA, 0xF7, 0x48, 0x33,
+ 0x9E, 0x8F, 0xAB, 0x5D, 0x41, 0x50, 0xA4, 0x7F,
+ 0x80, 0x4A, 0x68, 0x06, 0x2E, 0x6C, 0xC4, 0x02,
+ 0x0E, 0x63, 0xF0, 0xC9, 0x91, 0xB2, 0xD2, 0x03,
+ 0x37, 0xEF, 0x9C, 0x90, 0x83, 0x76, 0x1E, 0xA9,
+ 0x85, 0xB6, 0x57, 0xD7, 0xF2, 0xF1, 0xE7, 0xDE,
+ 0xCB, 0xAA, 0xBF, 0x89, 0x1F, 0xA7, 0xBC, 0x9F,
+ 0x53, 0xE1, 0xDD, 0x72, 0x95, 0x52, 0x34, 0xB5 };
+
+unsigned char table_155[256] = {
+ 0x75, 0x58, 0xC5, 0xA5, 0x83, 0x16, 0xF3, 0x7F,
+ 0x94, 0xDE, 0xA0, 0xF6, 0xFD, 0x89, 0xA8, 0x06,
+ 0x98, 0x01, 0xD9, 0x69, 0xB7, 0x0F, 0xEA, 0x73,
+ 0x32, 0xF0, 0x49, 0xBF, 0x02, 0xE7, 0x22, 0x3F,
+ 0xDB, 0x30, 0x5F, 0x20, 0x6A, 0x93, 0x07, 0xBC,
+ 0x09, 0x0D, 0x37, 0x24, 0x90, 0x15, 0x80, 0xAF,
+ 0x8F, 0x59, 0x28, 0xFF, 0x6D, 0x1E, 0x52, 0x62,
+ 0xE2, 0xDD, 0x85, 0x48, 0xB5, 0xAB, 0x68, 0xAC,
+ 0x7E, 0x26, 0x2C, 0xF9, 0x2A, 0xBE, 0x5B, 0xCE,
+ 0x87, 0x1D, 0x96, 0xBD, 0xEF, 0x29, 0xA9, 0xC3,
+ 0x9D, 0x57, 0x79, 0x6B, 0x7A, 0x82, 0x78, 0x0A,
+ 0x91, 0xF2, 0x7C, 0xC2, 0x25, 0x88, 0xE3, 0x47,
+ 0x64, 0x46, 0x8D, 0x19, 0xF4, 0xE6, 0xF1, 0x53,
+ 0x9C, 0x54, 0x23, 0xAD, 0xA3, 0x86, 0x3A, 0x04,
+ 0x67, 0x1C, 0xF5, 0x43, 0x05, 0x42, 0xD6, 0x4B,
+ 0xFB, 0xD4, 0x2B, 0x08, 0x45, 0xD8, 0xCD, 0xEB,
+ 0x31, 0x4A, 0x5A, 0x34, 0x9B, 0xEC, 0x4D, 0xB4,
+ 0xC6, 0xFE, 0xD5, 0x5E, 0xC1, 0x39, 0x81, 0xCF,
+ 0x03, 0x6E, 0x95, 0x50, 0xA1, 0x3B, 0xB3, 0xE5,
+ 0x3D, 0xB1, 0xB2, 0x41, 0x17, 0x2F, 0x2E, 0xE4,
+ 0x1F, 0xDC, 0xB0, 0xB6, 0x18, 0x6F, 0x44, 0x12,
+ 0x0B, 0xCC, 0x4E, 0xC0, 0x51, 0x14, 0x76, 0x3C,
+ 0xB9, 0x9F, 0xA4, 0xD3, 0xA7, 0xE8, 0x13, 0x55,
+ 0xC8, 0x8C, 0xD2, 0xEE, 0x65, 0xB8, 0xAA, 0x6C,
+ 0x2D, 0x4F, 0x56, 0xFA, 0x61, 0x4C, 0xE0, 0x5C,
+ 0xA6, 0x1A, 0xD1, 0x38, 0xD7, 0x72, 0x60, 0x74,
+ 0xE1, 0xBA, 0x84, 0x3E, 0x40, 0xF8, 0xC7, 0x36,
+ 0x27, 0x0C, 0x70, 0x97, 0x9A, 0x7D, 0x35, 0x71,
+ 0xCA, 0x1B, 0x99, 0x8E, 0xAE, 0x66, 0x63, 0xE9,
+ 0xC9, 0x11, 0x8A, 0x21, 0x92, 0x5D, 0x77, 0x10,
+ 0xD0, 0xC4, 0xF7, 0x7B, 0x9E, 0xCB, 0xED, 0x0E,
+ 0x8B, 0x33, 0xFC, 0xBB, 0x00, 0xA2, 0xDF, 0xDA };
+
+unsigned char table_156[256] = {
+ 0x31, 0x25, 0xB1, 0xD3, 0xAF, 0xAE, 0x84, 0x2C,
+ 0x71, 0x5E, 0xD8, 0x80, 0x6F, 0x3E, 0x48, 0x86,
+ 0xED, 0x54, 0x6A, 0xC3, 0xBC, 0xBF, 0x0E, 0xEA,
+ 0x10, 0xA2, 0x9D, 0x91, 0x32, 0xE2, 0x7E, 0x1B,
+ 0x49, 0x27, 0xFF, 0xDD, 0x8A, 0x2F, 0x8D, 0x38,
+ 0xFA, 0x3C, 0x03, 0x14, 0x0F, 0x89, 0xCC, 0x07,
+ 0x1A, 0xA0, 0x97, 0x37, 0xA6, 0xD6, 0x63, 0x87,
+ 0xA1, 0xC2, 0x4B, 0x39, 0xCB, 0xCF, 0x69, 0x4E,
+ 0xC9, 0x28, 0x1C, 0xBB, 0x42, 0x2B, 0xA9, 0x78,
+ 0x5B, 0xF6, 0xE0, 0xD0, 0x5F, 0x46, 0x98, 0xCE,
+ 0x1F, 0x7A, 0x34, 0x8B, 0xFD, 0x9B, 0xEF, 0x74,
+ 0x05, 0xF2, 0x02, 0xC6, 0xDF, 0x73, 0x5C, 0x8E,
+ 0xDE, 0x88, 0x57, 0x3B, 0x85, 0xBD, 0xC0, 0x3A,
+ 0x45, 0x4D, 0x2D, 0x72, 0x0C, 0x60, 0xCA, 0x5D,
+ 0x06, 0x04, 0x3D, 0x51, 0x15, 0xAD, 0xE8, 0x67,
+ 0xBA, 0x43, 0x7D, 0xF8, 0xB2, 0xE6, 0xAB, 0xF4,
+ 0x23, 0x6E, 0xF0, 0x6B, 0x0B, 0x2E, 0xC8, 0xC4,
+ 0x4F, 0xA8, 0x6D, 0x26, 0xE9, 0x9C, 0x22, 0xB7,
+ 0x00, 0xB3, 0x0A, 0x7C, 0x44, 0x55, 0x75, 0xD5,
+ 0xAA, 0x66, 0x56, 0x24, 0x83, 0x90, 0xA4, 0xF5,
+ 0xCD, 0xEC, 0x18, 0xDC, 0xFE, 0x96, 0xA3, 0xF7,
+ 0xD2, 0xFB, 0xD1, 0x65, 0xC5, 0x08, 0x7B, 0x70,
+ 0x16, 0x9A, 0x20, 0x09, 0x29, 0xDA, 0x52, 0x5A,
+ 0x59, 0xB4, 0x77, 0x62, 0x9E, 0x19, 0x7F, 0x82,
+ 0x4C, 0xB6, 0x0D, 0x58, 0xEE, 0x1D, 0xB9, 0x93,
+ 0x50, 0xD9, 0x30, 0xE4, 0x13, 0x01, 0x36, 0x8F,
+ 0x53, 0x3F, 0x64, 0xA5, 0xB5, 0xD7, 0x81, 0x41,
+ 0x17, 0xE5, 0x94, 0xE3, 0xF9, 0x61, 0x76, 0xE1,
+ 0x9F, 0xFC, 0x1E, 0x12, 0xDB, 0x21, 0x79, 0x2A,
+ 0xAC, 0xF3, 0x6C, 0xC1, 0x95, 0x92, 0xEB, 0xA7,
+ 0x11, 0xC7, 0xB8, 0x4A, 0x33, 0xB0, 0x99, 0xE7,
+ 0xF1, 0x68, 0xBE, 0x35, 0x40, 0x8C, 0xD4, 0x47 };
+
+unsigned char table_157[32] = {
+ 0x00, 0x0D, 0x03, 0x02, 0x11, 0x04, 0x18, 0x0B,
+ 0x14, 0x1D, 0x1C, 0x13, 0x1B, 0x17, 0x10, 0x15,
+ 0x01, 0x19, 0x07, 0x09, 0x1A, 0x16, 0x12, 0x1E,
+ 0x08, 0x06, 0x0C, 0x0E, 0x1F, 0x0F, 0x0A, 0x05 };
+
+unsigned char table_158[256] = {
+ 0x68, 0x26, 0x80, 0x0B, 0xB8, 0xD5, 0x8C, 0xB7,
+ 0x65, 0xEF, 0xBC, 0x94, 0x28, 0xB9, 0xB2, 0xD2,
+ 0x92, 0xA4, 0x55, 0x27, 0xE0, 0x40, 0x6C, 0x41,
+ 0x25, 0xBD, 0xAF, 0xEA, 0xB1, 0x19, 0xA5, 0xC9,
+ 0x0E, 0xED, 0xB4, 0xF9, 0x8B, 0x6A, 0xAE, 0xD8,
+ 0x64, 0x83, 0xC1, 0xD3, 0x04, 0xF4, 0xFA, 0xC3,
+ 0x46, 0x2C, 0xA8, 0xBB, 0x3A, 0x47, 0x33, 0x8F,
+ 0x52, 0x86, 0x08, 0x9D, 0x1D, 0x59, 0x8E, 0x91,
+ 0x32, 0xCF, 0x6B, 0x75, 0xB0, 0x7F, 0xC7, 0x24,
+ 0x05, 0x6F, 0x00, 0x1C, 0x2D, 0xAC, 0xDA, 0x45,
+ 0x73, 0xB3, 0x3E, 0xD6, 0x54, 0x61, 0x03, 0x77,
+ 0xF8, 0xD9, 0xE2, 0x4B, 0xFF, 0xF2, 0x0C, 0x4F,
+ 0x93, 0x71, 0xA7, 0x3D, 0x66, 0x88, 0x98, 0xF1,
+ 0xB6, 0x7A, 0x2B, 0xCD, 0x44, 0x3C, 0x37, 0x5A,
+ 0x96, 0x23, 0x9F, 0xBF, 0x7D, 0x5E, 0x2A, 0x35,
+ 0x72, 0x79, 0xE1, 0xA3, 0x84, 0x99, 0x38, 0x49,
+ 0xC8, 0xDB, 0x30, 0xDC, 0xAD, 0x3F, 0xF6, 0x09,
+ 0x69, 0x95, 0xE5, 0x67, 0xA1, 0xFD, 0xF7, 0x1B,
+ 0xEC, 0x17, 0xD4, 0xEB, 0x29, 0x36, 0x3B, 0x15,
+ 0xDE, 0x2E, 0xC5, 0x70, 0x6D, 0x53, 0x56, 0xAB,
+ 0xC0, 0x43, 0xC2, 0xE7, 0x31, 0xE6, 0xA6, 0x78,
+ 0x5C, 0x7C, 0x48, 0x10, 0x87, 0xCC, 0x9E, 0x7E,
+ 0x5F, 0xE9, 0x07, 0x5B, 0xF5, 0xEE, 0xB5, 0xCA,
+ 0x62, 0x18, 0xBE, 0x20, 0x16, 0xDF, 0x13, 0x4E,
+ 0x7B, 0x02, 0x11, 0x4C, 0x51, 0x85, 0x0D, 0x22,
+ 0xF3, 0x14, 0x63, 0x76, 0xD0, 0x0F, 0xE4, 0xCB,
+ 0xCE, 0xA0, 0x82, 0xE3, 0x01, 0xAA, 0x5D, 0x4A,
+ 0x4D, 0xFB, 0x39, 0x8A, 0x2F, 0xDD, 0xE8, 0x06,
+ 0x1A, 0x90, 0x81, 0x50, 0x8D, 0x89, 0x97, 0x1E,
+ 0xFC, 0x60, 0x12, 0x42, 0x9C, 0xF0, 0x34, 0xD7,
+ 0xD1, 0x1F, 0x0A, 0x21, 0xA9, 0x6E, 0xC4, 0xBA,
+ 0x9A, 0x57, 0xA2, 0x74, 0xC6, 0xFE, 0x9B, 0x58 };
+
+unsigned char table_159[256] = {
+ 0xE5, 0xBF, 0x84, 0x56, 0xD6, 0x43, 0x3E, 0xA5,
+ 0x64, 0x87, 0x44, 0x63, 0x4A, 0x4C, 0x8D, 0x24,
+ 0x1C, 0xDA, 0x89, 0x52, 0x80, 0x4F, 0xE4, 0xBC,
+ 0xC5, 0xF4, 0x27, 0x75, 0x9C, 0xF0, 0xE1, 0x06,
+ 0x99, 0x48, 0xF2, 0x57, 0x34, 0x9A, 0xA8, 0x62,
+ 0xC9, 0xD5, 0x16, 0x6D, 0x55, 0xFA, 0x37, 0x5A,
+ 0x2A, 0xC6, 0x45, 0xDD, 0x1B, 0x76, 0x50, 0xE2,
+ 0x69, 0x41, 0x6C, 0xC4, 0x3C, 0x47, 0xA9, 0x92,
+ 0x00, 0x3D, 0x6F, 0xE7, 0x7A, 0x3A, 0x33, 0x53,
+ 0xF7, 0x03, 0xA7, 0xB1, 0x15, 0x78, 0x0B, 0x67,
+ 0x2E, 0x21, 0xF1, 0xD4, 0xB3, 0x98, 0x60, 0x58,
+ 0xBB, 0x82, 0x1E, 0x70, 0x0A, 0xA2, 0x02, 0x17,
+ 0xFF, 0x9F, 0xD2, 0xAF, 0xC7, 0xDC, 0x68, 0x83,
+ 0x42, 0xCA, 0x08, 0x39, 0x20, 0xEC, 0x77, 0x96,
+ 0x5B, 0xAD, 0x09, 0x6B, 0x40, 0xC2, 0x91, 0x51,
+ 0x10, 0xD9, 0xF9, 0xC1, 0xB5, 0xDF, 0xDB, 0xC0,
+ 0x7D, 0xAB, 0xAE, 0x54, 0x35, 0xF3, 0xA1, 0xE6,
+ 0xEA, 0x14, 0xBA, 0xFC, 0xE8, 0xEB, 0xF6, 0xBD,
+ 0x8C, 0x72, 0x1F, 0xE9, 0xFB, 0x7C, 0xCF, 0x49,
+ 0xE3, 0xA3, 0x22, 0x9D, 0x46, 0x71, 0x94, 0x31,
+ 0x2D, 0x65, 0x2B, 0x32, 0x18, 0xB6, 0x90, 0xF8,
+ 0x11, 0x5F, 0xA0, 0xEF, 0xED, 0x1A, 0x25, 0x2C,
+ 0x3B, 0xFD, 0x2F, 0x73, 0xB9, 0x7E, 0xDE, 0xB4,
+ 0x97, 0x0F, 0x7F, 0x86, 0x93, 0x07, 0x19, 0xCE,
+ 0xE0, 0xB7, 0xEE, 0x26, 0xD1, 0x01, 0x59, 0x5C,
+ 0xC3, 0x79, 0x8B, 0xD3, 0x4B, 0x04, 0xD0, 0x29,
+ 0x0D, 0x3F, 0xB2, 0x30, 0xCC, 0x36, 0xFE, 0xB0,
+ 0xF5, 0x8E, 0xA6, 0x8A, 0xC8, 0xD8, 0x05, 0xB8,
+ 0x12, 0xBE, 0x81, 0x4D, 0x38, 0xAC, 0x1D, 0x9E,
+ 0x66, 0x5E, 0x7B, 0x6E, 0x0C, 0xCD, 0x6A, 0x88,
+ 0xAA, 0x0E, 0x61, 0x5D, 0x95, 0x4E, 0xD7, 0x74,
+ 0xCB, 0x9B, 0x13, 0x8F, 0xA4, 0x28, 0x23, 0x85 };
+
+unsigned char table_160[256] = {
+ 0x35, 0x44, 0x0E, 0x92, 0x75, 0x83, 0x9D, 0x53,
+ 0xA5, 0x90, 0xF8, 0xF7, 0x54, 0x74, 0xDF, 0x3D,
+ 0x5A, 0xAA, 0xC6, 0x26, 0x7A, 0xFC, 0x79, 0x6C,
+ 0x56, 0xB3, 0x32, 0xE3, 0x1C, 0xF9, 0xDC, 0xE6,
+ 0xA2, 0x93, 0x71, 0xFF, 0x1D, 0xEB, 0xB2, 0x04,
+ 0x96, 0x46, 0x0C, 0x2B, 0x17, 0xEE, 0x28, 0x25,
+ 0xD9, 0xAE, 0x11, 0xA7, 0x40, 0x45, 0xFB, 0x80,
+ 0x18, 0xF1, 0xCB, 0x2E, 0x24, 0xF3, 0xEC, 0x4F,
+ 0xAB, 0xD7, 0xD4, 0xC4, 0xFD, 0x4B, 0xAD, 0xC9,
+ 0x4C, 0x08, 0xAC, 0xF4, 0xCD, 0xB7, 0xF2, 0x15,
+ 0x02, 0x2F, 0x16, 0x34, 0x65, 0x8A, 0x87, 0xCC,
+ 0x50, 0x0F, 0x9B, 0xC2, 0xC8, 0x7B, 0xEA, 0x8E,
+ 0xE4, 0xD6, 0x97, 0x30, 0xA8, 0xA0, 0x94, 0xC5,
+ 0xE8, 0x12, 0x27, 0xCE, 0x84, 0xDD, 0xB1, 0x47,
+ 0x7E, 0xE7, 0xE1, 0x3A, 0x37, 0x21, 0x2D, 0x3B,
+ 0x20, 0x60, 0x1E, 0x1B, 0x82, 0xBE, 0xA3, 0x70,
+ 0x98, 0xBF, 0xA6, 0x4D, 0x76, 0x86, 0x42, 0x9F,
+ 0xCF, 0xE0, 0x14, 0x4A, 0x0B, 0xB4, 0x36, 0xF5,
+ 0x85, 0xB8, 0xC0, 0x6A, 0xE9, 0x7D, 0xBD, 0x4E,
+ 0x8F, 0x51, 0x0D, 0x5B, 0x6B, 0x58, 0x5F, 0x03,
+ 0x6F, 0xBC, 0x5D, 0x1F, 0x7F, 0xDB, 0x00, 0xC1,
+ 0x13, 0xF0, 0xD1, 0xFA, 0xDA, 0x05, 0x39, 0xD3,
+ 0x38, 0xD2, 0x89, 0xE2, 0x88, 0x5E, 0x5C, 0x6D,
+ 0xCA, 0xB0, 0x01, 0x63, 0x8B, 0x59, 0xA4, 0xD0,
+ 0x78, 0x19, 0xB5, 0x62, 0x1A, 0x69, 0x8D, 0x9C,
+ 0x22, 0x3F, 0x9E, 0x33, 0x72, 0x2A, 0x41, 0x29,
+ 0xFE, 0xF6, 0x64, 0x7C, 0x66, 0xB6, 0xAF, 0x23,
+ 0x8C, 0x68, 0x6E, 0x49, 0x07, 0x99, 0x77, 0x3E,
+ 0x9A, 0x73, 0xD8, 0x55, 0x0A, 0x3C, 0xBA, 0xA9,
+ 0x52, 0xED, 0x91, 0x09, 0x95, 0xC7, 0x43, 0xD5,
+ 0x57, 0x61, 0x81, 0xEF, 0x06, 0xDE, 0x48, 0x31,
+ 0xBB, 0x2C, 0xE5, 0xC3, 0x67, 0xA1, 0x10, 0xB9 };
+
+unsigned char table_161[256] = {
+ 0x8F, 0x1A, 0x81, 0xA2, 0x2C, 0x56, 0x6D, 0xCD,
+ 0x4A, 0x33, 0x50, 0xE9, 0xE0, 0x12, 0x5A, 0x43,
+ 0x2D, 0x4F, 0xEA, 0x95, 0xFD, 0x49, 0xAB, 0xA3,
+ 0x79, 0x42, 0x0B, 0xB8, 0x89, 0x40, 0x71, 0x14,
+ 0x80, 0x55, 0xAF, 0xCF, 0x3E, 0x64, 0x8B, 0x74,
+ 0xBF, 0x9C, 0x24, 0x97, 0xD1, 0xBA, 0x48, 0xD2,
+ 0x08, 0x1F, 0xDD, 0xA7, 0xDC, 0x92, 0x30, 0x75,
+ 0x31, 0x37, 0x67, 0x06, 0x68, 0x72, 0x6F, 0x05,
+ 0x8A, 0x7C, 0x4C, 0x3C, 0x19, 0x28, 0x86, 0x3D,
+ 0x93, 0xDA, 0xF4, 0xC7, 0x17, 0x85, 0xAC, 0x02,
+ 0x78, 0x04, 0xAD, 0x03, 0x8D, 0x11, 0xC5, 0x9D,
+ 0x3A, 0x73, 0x82, 0x59, 0x51, 0x9F, 0x27, 0x47,
+ 0xE7, 0xED, 0x1E, 0xFF, 0x34, 0x01, 0x5B, 0x4B,
+ 0xCA, 0x6C, 0x69, 0xBB, 0x3B, 0xC4, 0x5F, 0xDF,
+ 0x09, 0x6B, 0x7D, 0xC9, 0x88, 0x45, 0x57, 0xD3,
+ 0x2A, 0x4E, 0xF1, 0xC2, 0xA9, 0xB6, 0x18, 0xD4,
+ 0xA0, 0x1C, 0x4D, 0x0E, 0xE5, 0xE1, 0xD7, 0xB2,
+ 0x0C, 0x3F, 0x00, 0x61, 0x16, 0x0D, 0x32, 0x62,
+ 0x58, 0x63, 0xEE, 0xEF, 0x2F, 0x5D, 0xB0, 0x20,
+ 0x7A, 0x10, 0xE6, 0xA1, 0xF9, 0xD8, 0x6E, 0xCB,
+ 0xF0, 0x9B, 0x84, 0x8E, 0xF2, 0xFE, 0xC8, 0x7F,
+ 0xBD, 0xF8, 0x07, 0xC6, 0x39, 0xBC, 0xCC, 0x22,
+ 0x54, 0x15, 0x9A, 0xA4, 0xC1, 0x2B, 0x1B, 0x25,
+ 0xDE, 0x6A, 0xDB, 0x90, 0xEB, 0xB7, 0xD0, 0x44,
+ 0xA6, 0xB9, 0xB1, 0x23, 0x9E, 0x65, 0x83, 0xFA,
+ 0x96, 0xB5, 0x0F, 0xF6, 0xD6, 0xE8, 0x53, 0x13,
+ 0x76, 0xD5, 0x35, 0x87, 0xE3, 0x38, 0xF5, 0xAE,
+ 0xB3, 0xCE, 0xE2, 0x70, 0xD9, 0x66, 0x5C, 0x26,
+ 0xC3, 0xFC, 0xF7, 0x94, 0xF3, 0xEC, 0xFB, 0x99,
+ 0x91, 0x77, 0xB4, 0x46, 0xA5, 0x98, 0x7B, 0x1D,
+ 0x52, 0x2E, 0xA8, 0x60, 0x5E, 0x29, 0x21, 0x7E,
+ 0xBE, 0x0A, 0x36, 0x41, 0xC0, 0x8C, 0xE4, 0xAA };
+
+unsigned char table_162[256] = {
+ 0xF7, 0x1B, 0xC0, 0x31, 0x5A, 0x23, 0xEA, 0xE9,
+ 0xFB, 0x14, 0x6A, 0xE8, 0x04, 0x65, 0x5B, 0x2C,
+ 0x41, 0xD9, 0xEB, 0xE4, 0x8D, 0x1D, 0xCA, 0x8F,
+ 0x5E, 0x43, 0xAF, 0x46, 0x0A, 0x01, 0x0C, 0xB4,
+ 0x95, 0x52, 0x92, 0xE0, 0x10, 0x57, 0x0F, 0x71,
+ 0xB1, 0x26, 0xD8, 0x05, 0x69, 0x3C, 0x54, 0xDF,
+ 0xFF, 0x9D, 0x51, 0xA0, 0xA1, 0x0B, 0xC1, 0x20,
+ 0x6D, 0xFA, 0x47, 0x15, 0x09, 0xD3, 0xE1, 0xA9,
+ 0x66, 0x12, 0x5C, 0x49, 0x1E, 0x3B, 0xD0, 0x8B,
+ 0x62, 0xBD, 0x06, 0xE5, 0x00, 0x98, 0x4E, 0x32,
+ 0xB0, 0x2D, 0x2A, 0x7F, 0x03, 0xD5, 0x99, 0x7E,
+ 0xAB, 0x22, 0xC6, 0xC3, 0x2F, 0x4C, 0x33, 0x45,
+ 0xE3, 0x3F, 0xF9, 0xB2, 0xFE, 0x36, 0xE7, 0xF8,
+ 0x55, 0x0D, 0x56, 0x1F, 0x4B, 0xE6, 0x50, 0x81,
+ 0xCE, 0x80, 0xCD, 0x67, 0x6B, 0xCF, 0x2E, 0x9B,
+ 0xBC, 0xBE, 0x11, 0x75, 0x4D, 0xAC, 0x59, 0x40,
+ 0x85, 0x0E, 0xC9, 0x17, 0xA3, 0x60, 0xED, 0x16,
+ 0xA4, 0xDD, 0xEE, 0x96, 0x77, 0x83, 0x34, 0xD2,
+ 0xCB, 0xFC, 0x6C, 0x08, 0xEC, 0x35, 0xF2, 0x6F,
+ 0x3A, 0x7B, 0x21, 0x4A, 0x70, 0xEF, 0xAD, 0xDE,
+ 0x90, 0x9E, 0x7D, 0x64, 0x2B, 0x79, 0xF5, 0xF3,
+ 0x13, 0x1C, 0x7A, 0x07, 0x4F, 0x78, 0x89, 0xB6,
+ 0x97, 0xF1, 0xD7, 0x7C, 0x48, 0xAE, 0x39, 0xA8,
+ 0xA6, 0x86, 0x3E, 0x27, 0x87, 0x73, 0x82, 0x24,
+ 0x30, 0x74, 0x5F, 0xD1, 0x9F, 0x9C, 0x1A, 0x8C,
+ 0x42, 0x6E, 0x28, 0xB9, 0xF0, 0xC4, 0x68, 0x25,
+ 0xC5, 0xDC, 0xB8, 0x29, 0xD6, 0x84, 0x3D, 0xBB,
+ 0x88, 0x76, 0xFD, 0x61, 0x94, 0x91, 0xDA, 0xB7,
+ 0x72, 0xBA, 0xC2, 0xDB, 0xB5, 0xA5, 0xE2, 0x18,
+ 0xF6, 0xAA, 0x8A, 0x19, 0x63, 0x9A, 0xA7, 0xC8,
+ 0xD4, 0x02, 0x8E, 0x37, 0xF4, 0xB3, 0xA2, 0x53,
+ 0x38, 0xCC, 0x58, 0x44, 0xBF, 0x93, 0x5D, 0xC7 };
+
+unsigned char table_163[32] = {
+ 0x1B, 0x14, 0x12, 0x15, 0x11, 0x1D, 0x17, 0x19,
+ 0x10, 0x09, 0x08, 0x06, 0x1A, 0x16, 0x07, 0x13,
+ 0x1F, 0x0B, 0x1C, 0x05, 0x0E, 0x00, 0x18, 0x0A,
+ 0x04, 0x01, 0x03, 0x0C, 0x0D, 0x1E, 0x02, 0x0F };
+
+unsigned char table_164[32] = {
+ 0x15, 0x00, 0x10, 0x0B, 0x1D, 0x0A, 0x06, 0x1C,
+ 0x0D, 0x1F, 0x17, 0x0F, 0x03, 0x14, 0x13, 0x12,
+ 0x1B, 0x18, 0x08, 0x1E, 0x16, 0x09, 0x1A, 0x04,
+ 0x02, 0x0C, 0x0E, 0x01, 0x07, 0x19, 0x11, 0x05 };
+
+unsigned char table_165[256] = {
+ 0x98, 0xF5, 0x1D, 0xFB, 0x13, 0x20, 0x41, 0xA3,
+ 0xE3, 0x76, 0x49, 0x7E, 0x60, 0xD8, 0x68, 0x30,
+ 0x88, 0x45, 0xD5, 0x77, 0x00, 0xC3, 0x09, 0x31,
+ 0x44, 0x18, 0xD4, 0x14, 0xC8, 0x1B, 0x8B, 0x38,
+ 0x08, 0x52, 0xD1, 0xF3, 0x69, 0x9F, 0xDA, 0x61,
+ 0x16, 0x1C, 0xE4, 0x7D, 0xEE, 0xD9, 0x5E, 0x4C,
+ 0xA7, 0xAA, 0xA6, 0xF6, 0xCF, 0xA0, 0xBA, 0x10,
+ 0xE2, 0xDE, 0x0F, 0xEA, 0xBC, 0x32, 0x63, 0xC0,
+ 0x54, 0xC5, 0xBE, 0x71, 0x80, 0x56, 0x5C, 0xA4,
+ 0xAD, 0x15, 0x9D, 0x11, 0x43, 0x67, 0x95, 0xAE,
+ 0xC6, 0xC4, 0x91, 0x9C, 0xE5, 0x37, 0xE1, 0x7A,
+ 0xDB, 0xEF, 0x03, 0x65, 0x86, 0x66, 0x2A, 0xB5,
+ 0xBF, 0xB4, 0x0D, 0xB3, 0xD7, 0x2D, 0x01, 0xEB,
+ 0x8C, 0xF2, 0x5A, 0x2E, 0x64, 0x25, 0x02, 0xCB,
+ 0x4A, 0xB0, 0xCE, 0x35, 0xA8, 0x47, 0x85, 0x33,
+ 0x34, 0x24, 0x23, 0x7B, 0xB6, 0x48, 0x83, 0x40,
+ 0x87, 0x57, 0x3C, 0xD6, 0xCD, 0x2C, 0x6D, 0xE7,
+ 0xBB, 0xED, 0x81, 0x5D, 0x55, 0x46, 0xDD, 0xD3,
+ 0x70, 0xBD, 0xB8, 0x75, 0x53, 0x6E, 0xD0, 0x99,
+ 0xCA, 0x58, 0xC7, 0x4B, 0x3D, 0xA5, 0x50, 0x7C,
+ 0x93, 0x51, 0xB7, 0xFD, 0x05, 0x3A, 0xE8, 0x8F,
+ 0x28, 0x74, 0x39, 0xF0, 0x7F, 0x4F, 0x06, 0x36,
+ 0xB2, 0x19, 0x2F, 0x1F, 0x8D, 0x0C, 0xB9, 0xFC,
+ 0x89, 0x21, 0x12, 0xF7, 0x3F, 0x94, 0x6F, 0xDC,
+ 0x3E, 0x4E, 0x3B, 0xC9, 0x07, 0x9B, 0x17, 0x9A,
+ 0x73, 0x6A, 0x5B, 0xA1, 0x1E, 0x8A, 0x04, 0x72,
+ 0x6C, 0xA2, 0xEC, 0x96, 0xFE, 0xF8, 0x84, 0xC1,
+ 0x79, 0x0E, 0x62, 0x90, 0x8E, 0xF4, 0x42, 0x29,
+ 0x92, 0x9E, 0xAC, 0x82, 0x4D, 0xAF, 0x2B, 0x6B,
+ 0xA9, 0xFF, 0x0A, 0xAB, 0x22, 0x5F, 0xDF, 0xD2,
+ 0x0B, 0x78, 0xF1, 0xE6, 0x59, 0x27, 0xC2, 0xE0,
+ 0x1A, 0x26, 0xCC, 0xB1, 0xF9, 0xFA, 0x97, 0xE9 };
+
+unsigned char table_166[256] = {
+ 0xCB, 0xEA, 0x2A, 0x36, 0x6D, 0x93, 0x4E, 0xD5,
+ 0xBC, 0x6A, 0xD4, 0x68, 0xF7, 0x18, 0xAB, 0x8B,
+ 0x66, 0x95, 0x94, 0x64, 0xB7, 0x00, 0x4D, 0x97,
+ 0x38, 0xB3, 0xFC, 0xE1, 0xBB, 0x63, 0xF3, 0x1F,
+ 0x6B, 0x2C, 0x2F, 0x5E, 0xA4, 0x7E, 0xFB, 0xF4,
+ 0xA8, 0x8A, 0x65, 0x53, 0x90, 0x58, 0x40, 0x60,
+ 0x28, 0x8E, 0x35, 0x49, 0xED, 0xBD, 0x1B, 0x0B,
+ 0xBA, 0xB8, 0x61, 0x50, 0xE9, 0x39, 0xEF, 0xC3,
+ 0x74, 0xB6, 0x46, 0x8D, 0xD9, 0x32, 0x92, 0x9A,
+ 0x30, 0x01, 0xF2, 0x41, 0xB9, 0xE7, 0x3A, 0xB0,
+ 0x80, 0x15, 0xDE, 0x7D, 0x7F, 0x09, 0xC2, 0x76,
+ 0xF8, 0x12, 0x59, 0xDD, 0x1D, 0xE6, 0x75, 0xBE,
+ 0xA3, 0x04, 0xCA, 0x78, 0x7B, 0xAC, 0xD8, 0x70,
+ 0xD3, 0xC1, 0x25, 0x6F, 0x03, 0x6C, 0x14, 0x45,
+ 0xE5, 0x2B, 0x87, 0x83, 0xAA, 0x77, 0x5F, 0x4A,
+ 0x9C, 0x27, 0x0C, 0x10, 0xAE, 0x56, 0x85, 0x0D,
+ 0xE3, 0xFA, 0x71, 0xEE, 0x9F, 0x21, 0xC0, 0xCD,
+ 0xFD, 0xDC, 0x5B, 0x11, 0x02, 0x0F, 0x96, 0x3D,
+ 0x3C, 0x26, 0xEB, 0x08, 0x7A, 0x82, 0xA7, 0x19,
+ 0xD7, 0xC5, 0xF6, 0x52, 0x57, 0x88, 0xFF, 0x47,
+ 0x8F, 0xC6, 0x33, 0xB5, 0x2E, 0x8C, 0x81, 0x91,
+ 0x44, 0xA6, 0x17, 0xF0, 0x4B, 0x9D, 0x34, 0x73,
+ 0x72, 0x67, 0xD2, 0x0E, 0xA0, 0x99, 0xA5, 0xAF,
+ 0xFE, 0x9E, 0x6E, 0xDA, 0x3B, 0xE2, 0x23, 0xD6,
+ 0xD0, 0x13, 0x89, 0x5A, 0x42, 0x98, 0x5C, 0xD1,
+ 0x86, 0x24, 0xDF, 0x37, 0xF9, 0xCC, 0xF5, 0xA9,
+ 0x2D, 0xBF, 0x5D, 0xF1, 0x69, 0xE8, 0xA2, 0x06,
+ 0x48, 0xC7, 0xDB, 0x29, 0xE4, 0xAD, 0x3E, 0xA1,
+ 0xC9, 0x4C, 0x1A, 0xCE, 0x62, 0x4F, 0x7C, 0xC8,
+ 0x05, 0xC4, 0xB1, 0x1E, 0x79, 0x55, 0x84, 0xB2,
+ 0x20, 0x31, 0x9B, 0xEC, 0xB4, 0xCF, 0x54, 0x22,
+ 0x1C, 0xE0, 0x51, 0x16, 0x43, 0x07, 0x0A, 0x3F };
+
+unsigned char table_167[256] = {
+ 0x91, 0xEA, 0x4F, 0x6A, 0x6E, 0x2D, 0x27, 0x22,
+ 0x44, 0xA5, 0x6D, 0xE3, 0x45, 0x06, 0xE2, 0x87,
+ 0x9A, 0xC9, 0x2C, 0x4A, 0x93, 0x6F, 0x00, 0xEB,
+ 0x7C, 0x7F, 0xA2, 0xFE, 0x40, 0x3C, 0x3F, 0xC0,
+ 0xC7, 0xFB, 0x8B, 0xDF, 0xA3, 0x28, 0x78, 0x48,
+ 0x46, 0xD5, 0x70, 0x5C, 0x35, 0x4E, 0xD7, 0x3A,
+ 0x42, 0x47, 0x5B, 0x26, 0x8E, 0xE0, 0x21, 0xB1,
+ 0x77, 0x1E, 0x53, 0x4B, 0xCC, 0xE5, 0x65, 0xF6,
+ 0x66, 0x2A, 0xA0, 0x5E, 0x3E, 0xAD, 0xA8, 0x95,
+ 0x1B, 0x0D, 0x8A, 0x05, 0x68, 0x59, 0x0C, 0x38,
+ 0x18, 0xC3, 0x81, 0xA4, 0xFD, 0x13, 0x50, 0xCA,
+ 0xE8, 0xDD, 0xD9, 0x76, 0x8C, 0xC5, 0xF4, 0x17,
+ 0xB4, 0x3D, 0xEC, 0x0B, 0x67, 0xC6, 0x8D, 0xE1,
+ 0xBB, 0x7E, 0xCB, 0x10, 0x99, 0xE9, 0x39, 0xF3,
+ 0x75, 0xFA, 0xAC, 0x16, 0x54, 0x51, 0xBC, 0x24,
+ 0x58, 0x08, 0xA7, 0x0F, 0x5D, 0xBF, 0xBA, 0xE7,
+ 0x9D, 0x2B, 0xB5, 0x29, 0xE4, 0xCD, 0x37, 0x30,
+ 0x55, 0xAE, 0x1D, 0x4D, 0x94, 0x34, 0x92, 0x1C,
+ 0x6B, 0xBE, 0x52, 0x7B, 0x33, 0xB0, 0x0A, 0x5A,
+ 0x03, 0x23, 0x41, 0x49, 0x61, 0x64, 0x73, 0x97,
+ 0xC2, 0x9F, 0x5F, 0x07, 0x04, 0xF8, 0xC1, 0xFC,
+ 0x74, 0x02, 0x0E, 0x60, 0x9E, 0xD4, 0x85, 0x88,
+ 0xC4, 0xF5, 0x90, 0x31, 0xF7, 0xEE, 0x9B, 0xB9,
+ 0x20, 0xE6, 0xA6, 0x63, 0x79, 0x56, 0x62, 0xF0,
+ 0x2F, 0xD8, 0x4C, 0x83, 0xF9, 0x36, 0x3B, 0x84,
+ 0xDE, 0x57, 0xB8, 0xB7, 0x11, 0xF2, 0xC8, 0xD3,
+ 0xD1, 0x96, 0x19, 0x2E, 0x72, 0x9C, 0xDB, 0xB3,
+ 0xA1, 0xAA, 0xCE, 0x09, 0x98, 0xED, 0xA9, 0xDA,
+ 0xAF, 0x86, 0xD0, 0x12, 0xFF, 0xDC, 0x1F, 0xD6,
+ 0x01, 0xF1, 0xD2, 0x80, 0x43, 0x7A, 0x71, 0x82,
+ 0xB6, 0xAB, 0x89, 0xBD, 0x8F, 0xEF, 0x7D, 0xB2,
+ 0x14, 0x15, 0x25, 0x32, 0x6C, 0x69, 0x1A, 0xCF };
+
+unsigned char table_168[256] = {
+ 0x28, 0xEE, 0xB1, 0xFD, 0xB3, 0xEF, 0x36, 0x8E,
+ 0x85, 0x5D, 0x1C, 0x53, 0x1E, 0xDA, 0xBA, 0x3C,
+ 0xA8, 0x90, 0x99, 0x49, 0x45, 0xE0, 0x27, 0x8D,
+ 0x22, 0xE4, 0x51, 0x3E, 0xAB, 0xE8, 0x70, 0xF5,
+ 0x81, 0xE6, 0x34, 0x29, 0xF3, 0x11, 0x46, 0x5F,
+ 0x5C, 0xA0, 0xD1, 0xE3, 0x15, 0x68, 0x3A, 0x01,
+ 0xE9, 0xD7, 0x24, 0x5A, 0x18, 0x16, 0x88, 0x3B,
+ 0x64, 0xA1, 0xDB, 0xBF, 0xAA, 0x43, 0xEA, 0x19,
+ 0xA2, 0xD5, 0x7B, 0xBD, 0x2A, 0x0E, 0x4F, 0xB5,
+ 0x4B, 0xB7, 0x5B, 0x73, 0xC9, 0xAC, 0x1B, 0x67,
+ 0xC7, 0xB4, 0x69, 0x00, 0xBC, 0x6D, 0xC1, 0x04,
+ 0xF4, 0x74, 0xD6, 0xD0, 0x60, 0xAE, 0x17, 0xFE,
+ 0x63, 0xB6, 0x89, 0x41, 0x7C, 0x44, 0x8B, 0xDC,
+ 0x50, 0xE5, 0x79, 0x77, 0x47, 0x9F, 0xA6, 0x3D,
+ 0x09, 0x8A, 0x2F, 0xC0, 0x0F, 0xCD, 0x2B, 0x4D,
+ 0x0D, 0xC2, 0x5E, 0xB0, 0x57, 0x62, 0xAF, 0x1A,
+ 0x21, 0x82, 0x48, 0x9E, 0x38, 0xB9, 0xB8, 0xF2,
+ 0x37, 0x07, 0xCA, 0xC5, 0x84, 0xDF, 0xF9, 0xEC,
+ 0x42, 0x6B, 0x8F, 0x6C, 0x3F, 0xC4, 0x94, 0xED,
+ 0x7A, 0x2D, 0xA3, 0x83, 0xD9, 0x55, 0x02, 0x9A,
+ 0xA9, 0x75, 0x10, 0x2C, 0xCB, 0x95, 0xBB, 0x6E,
+ 0x23, 0x65, 0x35, 0x97, 0x56, 0xAD, 0xCE, 0xF8,
+ 0xF0, 0x0C, 0xE2, 0x52, 0x05, 0x91, 0xCC, 0xC8,
+ 0x78, 0x06, 0x96, 0x4E, 0x03, 0xD3, 0x98, 0xA7,
+ 0x13, 0x58, 0x93, 0xD4, 0xDD, 0xC6, 0xFC, 0x25,
+ 0x9C, 0x86, 0x1F, 0xCF, 0x76, 0xA4, 0x6A, 0xFA,
+ 0x0B, 0x4A, 0x54, 0x40, 0x59, 0xD8, 0x61, 0xFF,
+ 0x7F, 0x80, 0x6F, 0x7D, 0xF1, 0x8C, 0x92, 0xDE,
+ 0x9D, 0xC3, 0xB2, 0xE7, 0xFB, 0x20, 0x31, 0x72,
+ 0x12, 0xBE, 0x1D, 0xF6, 0x9B, 0x14, 0x26, 0x0A,
+ 0xEB, 0xF7, 0x71, 0x39, 0x30, 0xA5, 0x87, 0xD2,
+ 0x66, 0x2E, 0x08, 0x32, 0x4C, 0x33, 0x7E, 0xE1 };
+
+unsigned char table_169[256] = {
+ 0xA4, 0x31, 0xA9, 0x3F, 0x13, 0x4D, 0x1B, 0x29,
+ 0x73, 0x43, 0xF1, 0xE7, 0x9C, 0xC2, 0xF6, 0xCD,
+ 0xA1, 0x94, 0x0D, 0x27, 0xFE, 0x7B, 0x9B, 0x0B,
+ 0x89, 0xBA, 0x23, 0xEC, 0x76, 0xC3, 0x6C, 0xD8,
+ 0x8D, 0xF8, 0xF9, 0x7D, 0x68, 0x5B, 0x61, 0x87,
+ 0x28, 0x14, 0x55, 0x0C, 0xFC, 0xD9, 0x07, 0xE8,
+ 0x36, 0x88, 0x67, 0x4C, 0xEA, 0xBD, 0xF5, 0x9D,
+ 0xB6, 0xC6, 0x24, 0x32, 0x93, 0x03, 0x79, 0x8C,
+ 0x12, 0x84, 0xFF, 0x7E, 0x42, 0xE4, 0x3C, 0xF2,
+ 0x50, 0xEB, 0x1F, 0x47, 0xB0, 0xA5, 0xB1, 0x71,
+ 0x30, 0x5F, 0x5C, 0x53, 0xF7, 0x10, 0xC5, 0x6E,
+ 0xE0, 0xDE, 0xC8, 0x58, 0xB7, 0x90, 0xA6, 0x95,
+ 0x70, 0x8F, 0xFD, 0xC1, 0x48, 0xB5, 0x19, 0x92,
+ 0xBC, 0x15, 0x4E, 0xE6, 0x11, 0xDD, 0x81, 0x0E,
+ 0xBB, 0x75, 0x5D, 0x4A, 0xAB, 0x2D, 0x02, 0x54,
+ 0x4B, 0x66, 0xD6, 0x2B, 0x2A, 0xE5, 0x26, 0xE1,
+ 0xEE, 0xE9, 0x8B, 0x6A, 0x7A, 0xF4, 0x51, 0x39,
+ 0x1C, 0xC9, 0xCF, 0x77, 0x00, 0xF3, 0x25, 0xCC,
+ 0x08, 0xFB, 0x0F, 0x3E, 0xCE, 0xED, 0x3D, 0x56,
+ 0xEF, 0x1D, 0x85, 0x96, 0x52, 0xA8, 0xD3, 0xCB,
+ 0xE3, 0x33, 0x06, 0x7C, 0xAE, 0x72, 0x09, 0x04,
+ 0x91, 0xC4, 0x5A, 0x69, 0x98, 0xB4, 0x40, 0xDF,
+ 0x7F, 0x9F, 0xAA, 0x83, 0xE2, 0x78, 0x74, 0x20,
+ 0xAD, 0x6D, 0xDC, 0xD4, 0xCA, 0x60, 0xF0, 0x35,
+ 0x37, 0xD0, 0x18, 0x1A, 0x64, 0x3A, 0x99, 0xDB,
+ 0x62, 0x44, 0x2C, 0x82, 0x8E, 0xD7, 0xD1, 0xFA,
+ 0x16, 0xD5, 0x46, 0xBF, 0xA7, 0xC0, 0x2E, 0x3B,
+ 0x01, 0x63, 0xB2, 0x1E, 0x05, 0x21, 0xB8, 0x17,
+ 0x22, 0x97, 0xAF, 0x4F, 0x86, 0x34, 0xDA, 0xC7,
+ 0xA3, 0xA0, 0xB3, 0x2F, 0xAC, 0x49, 0xD2, 0x57,
+ 0x6F, 0x9A, 0x65, 0xB9, 0x41, 0xBE, 0x8A, 0xA2,
+ 0x6B, 0x0A, 0x59, 0x9E, 0x5E, 0x38, 0x45, 0x80 };
+
+unsigned char table_170[256] = {
+ 0xE3, 0x00, 0x99, 0x03, 0xF6, 0xDD, 0xD1, 0x41,
+ 0x58, 0x7E, 0xD9, 0x46, 0x04, 0xAF, 0x5C, 0x43,
+ 0xDE, 0x5E, 0xFC, 0x97, 0x3D, 0x68, 0xC8, 0x37,
+ 0x3C, 0xFB, 0x0F, 0x5A, 0xBE, 0xFA, 0x4C, 0x82,
+ 0x0C, 0xA0, 0x0A, 0xD4, 0x9D, 0xCE, 0x78, 0xA8,
+ 0x55, 0x56, 0x60, 0xAA, 0xC9, 0x96, 0x62, 0xEA,
+ 0x0D, 0xB8, 0xE2, 0x84, 0x17, 0xAE, 0x2B, 0x2C,
+ 0x91, 0x57, 0x38, 0x01, 0xA9, 0xCD, 0x34, 0xBA,
+ 0x8D, 0xC0, 0xD6, 0xFF, 0xF2, 0xD3, 0x5F, 0x26,
+ 0xCA, 0x9B, 0x21, 0x75, 0x4E, 0x49, 0x20, 0x59,
+ 0x39, 0xBF, 0x90, 0x6C, 0xFE, 0x8F, 0x2F, 0x18,
+ 0x36, 0xD7, 0xB4, 0xAC, 0xBD, 0xF3, 0x1D, 0x4F,
+ 0xA3, 0x74, 0x5B, 0x44, 0x05, 0x9C, 0x6D, 0x6B,
+ 0x1E, 0xE8, 0x25, 0x16, 0x80, 0xCC, 0x29, 0xC7,
+ 0x94, 0x4A, 0xF5, 0xF4, 0x27, 0x85, 0xBB, 0x24,
+ 0xDA, 0xB5, 0x76, 0x69, 0xA5, 0x54, 0x23, 0x31,
+ 0x11, 0xA4, 0x09, 0xE4, 0x64, 0x10, 0xC5, 0xC1,
+ 0x7D, 0xE7, 0x92, 0xF8, 0x9E, 0x6A, 0x15, 0x8B,
+ 0x98, 0x42, 0x52, 0x66, 0x0B, 0xA1, 0x35, 0x1A,
+ 0x14, 0x7C, 0xE1, 0x9F, 0x28, 0xF1, 0x1B, 0xA6,
+ 0x71, 0x73, 0x81, 0xAB, 0xE6, 0x95, 0x06, 0x1F,
+ 0xC6, 0xB0, 0x51, 0x0E, 0xEE, 0x77, 0xF0, 0xD8,
+ 0xC2, 0x89, 0x7B, 0x07, 0xA2, 0xB7, 0x19, 0x67,
+ 0x2E, 0x8E, 0x47, 0xA7, 0xEF, 0x32, 0xD2, 0x93,
+ 0xDC, 0x9A, 0xB2, 0xED, 0x45, 0xC4, 0x50, 0x3F,
+ 0xE5, 0xCF, 0x88, 0x1C, 0x7A, 0x79, 0xEB, 0x70,
+ 0x2A, 0x7F, 0xBC, 0xDB, 0xD0, 0xB1, 0xCB, 0x08,
+ 0x86, 0x5D, 0x53, 0x72, 0xB6, 0x4B, 0xB3, 0x22,
+ 0xC3, 0x6F, 0xB9, 0xD5, 0x3B, 0x13, 0x2D, 0xAD,
+ 0x33, 0xFD, 0x02, 0x40, 0x8A, 0x3A, 0xF7, 0xE0,
+ 0x8C, 0x3E, 0x61, 0x6E, 0xE9, 0x63, 0xF9, 0xEC,
+ 0x48, 0x30, 0x87, 0x83, 0x12, 0x4D, 0x65, 0xDF };
+
+unsigned char table_171[32] = {
+ 0x07, 0x06, 0x11, 0x08, 0x0C, 0x1F, 0x19, 0x02,
+ 0x14, 0x04, 0x0D, 0x18, 0x1A, 0x05, 0x17, 0x13,
+ 0x1C, 0x1B, 0x15, 0x03, 0x01, 0x0F, 0x16, 0x1E,
+ 0x1D, 0x10, 0x00, 0x12, 0x0B, 0x0E, 0x09, 0x0A };
+
+unsigned char table_172[32] = {
+ 0x11, 0x01, 0x1F, 0x06, 0x1A, 0x04, 0x02, 0x09,
+ 0x05, 0x0D, 0x0B, 0x18, 0x0E, 0x12, 0x1B, 0x17,
+ 0x07, 0x08, 0x1D, 0x1E, 0x14, 0x19, 0x16, 0x15,
+ 0x03, 0x0C, 0x00, 0x10, 0x0A, 0x1C, 0x0F, 0x13 };
+
+unsigned char table_173[32] = {
+ 0x1F, 0x0B, 0x13, 0x00, 0x16, 0x15, 0x14, 0x0A,
+ 0x1D, 0x05, 0x1E, 0x1A, 0x0F, 0x04, 0x0E, 0x01,
+ 0x19, 0x07, 0x02, 0x12, 0x0C, 0x17, 0x08, 0x09,
+ 0x03, 0x11, 0x18, 0x10, 0x1C, 0x1B, 0x06, 0x0D };
+
+unsigned char table_174[32] = {
+ 0x02, 0x1B, 0x0C, 0x17, 0x1F, 0x05, 0x15, 0x1E,
+ 0x16, 0x09, 0x1A, 0x12, 0x0F, 0x1C, 0x18, 0x0A,
+ 0x19, 0x10, 0x0D, 0x13, 0x04, 0x11, 0x08, 0x14,
+ 0x1D, 0x0E, 0x06, 0x00, 0x01, 0x07, 0x0B, 0x03 };
+
+unsigned char table_175[32] = {
+ 0x00, 0x06, 0x0B, 0x08, 0x0C, 0x04, 0x1A, 0x1C,
+ 0x05, 0x1E, 0x14, 0x03, 0x0A, 0x18, 0x12, 0x1D,
+ 0x16, 0x1F, 0x07, 0x09, 0x0F, 0x0E, 0x17, 0x13,
+ 0x11, 0x19, 0x10, 0x0D, 0x1B, 0x02, 0x01, 0x15 };
+
+unsigned char table_176[32] = {
+ 0x12, 0x03, 0x1A, 0x15, 0x04, 0x19, 0x0B, 0x1B,
+ 0x17, 0x1E, 0x0D, 0x05, 0x11, 0x14, 0x1C, 0x00,
+ 0x18, 0x10, 0x0A, 0x06, 0x0E, 0x08, 0x02, 0x07,
+ 0x13, 0x09, 0x16, 0x1D, 0x0F, 0x0C, 0x01, 0x1F };
+
+unsigned char table_177[256] = {
+ 0x5E, 0x4D, 0x76, 0xFE, 0xB5, 0x50, 0x83, 0x23,
+ 0x72, 0xDD, 0x93, 0x08, 0x69, 0xAD, 0xEC, 0x3B,
+ 0x0B, 0x9A, 0x36, 0xC9, 0xCA, 0xBE, 0xF7, 0x30,
+ 0x19, 0x39, 0x2C, 0xAB, 0xE3, 0x7B, 0xBC, 0x32,
+ 0xA0, 0xE4, 0xA6, 0xB6, 0xCB, 0xC8, 0x37, 0x07,
+ 0xD2, 0xA1, 0xD9, 0xF6, 0xBF, 0xF5, 0x88, 0x01,
+ 0x95, 0x0F, 0x03, 0xFD, 0xE6, 0x68, 0x90, 0x61,
+ 0x21, 0x6D, 0x3C, 0x62, 0x34, 0x2B, 0x71, 0x4B,
+ 0x44, 0x64, 0x75, 0xA2, 0x6A, 0xFF, 0x29, 0xBD,
+ 0x35, 0x15, 0xF9, 0xC1, 0x09, 0x45, 0xB2, 0xF2,
+ 0x3F, 0xCE, 0xB0, 0xC0, 0xB8, 0x00, 0x05, 0xD7,
+ 0x11, 0xC6, 0x78, 0x53, 0x9E, 0xB3, 0xED, 0x56,
+ 0x22, 0x5C, 0x9D, 0x6C, 0x99, 0x43, 0x2F, 0xAE,
+ 0xEB, 0x40, 0x8C, 0x1F, 0xC2, 0xDF, 0x92, 0x65,
+ 0x6F, 0x79, 0x5D, 0x5B, 0xAA, 0xDB, 0xF1, 0x96,
+ 0xD4, 0xF4, 0x8B, 0x51, 0xD5, 0xE2, 0xBB, 0x80,
+ 0x17, 0x7C, 0x2A, 0x6E, 0xDE, 0xEA, 0x94, 0x31,
+ 0xA4, 0x2D, 0xC3, 0x8D, 0x55, 0x14, 0x9B, 0x0E,
+ 0x7D, 0xC4, 0x06, 0x33, 0x73, 0xE9, 0x7A, 0x38,
+ 0x5F, 0x89, 0x84, 0xD6, 0xA8, 0x13, 0xE8, 0xCF,
+ 0x46, 0xD0, 0x7F, 0x24, 0x8F, 0xF8, 0x87, 0x1B,
+ 0x47, 0x02, 0x0C, 0x97, 0x52, 0xFB, 0x8E, 0x20,
+ 0x70, 0x3E, 0x7E, 0xD1, 0xE5, 0xEE, 0xCC, 0x91,
+ 0x74, 0xCD, 0x42, 0x04, 0x8A, 0xEF, 0xE1, 0x10,
+ 0x4F, 0x1C, 0x28, 0x9F, 0xD8, 0x0A, 0x18, 0x49,
+ 0x9C, 0x16, 0xF3, 0x82, 0x57, 0x1D, 0x26, 0x66,
+ 0x27, 0x86, 0xE7, 0x59, 0xFA, 0x25, 0x54, 0x0D,
+ 0x98, 0xDC, 0xF0, 0x3D, 0x63, 0x1E, 0x77, 0x3A,
+ 0xDA, 0xB7, 0x6B, 0x2E, 0x48, 0x4C, 0xBA, 0xC7,
+ 0x60, 0xAC, 0x1A, 0xB9, 0xFC, 0xA3, 0xA7, 0xA5,
+ 0xB4, 0x67, 0xA9, 0x81, 0xB1, 0x12, 0xD3, 0x85,
+ 0x5A, 0xC5, 0xE0, 0x58, 0x41, 0x4E, 0x4A, 0xAF };
+
+unsigned char table_178[256] = {
+ 0x33, 0xBA, 0x98, 0xDA, 0x07, 0x2C, 0x22, 0x9B,
+ 0xE0, 0xED, 0xB7, 0xA1, 0x93, 0xEB, 0xDC, 0x49,
+ 0xDF, 0xE1, 0x6C, 0xC2, 0x64, 0x52, 0xD0, 0x8F,
+ 0xA2, 0x48, 0x26, 0x21, 0x6E, 0x5E, 0x0B, 0x7C,
+ 0x0D, 0x90, 0xA4, 0xCE, 0xF5, 0x5F, 0xF9, 0x1D,
+ 0x55, 0x83, 0x8D, 0xFB, 0x38, 0xB3, 0xF2, 0x67,
+ 0xDE, 0x0A, 0xBE, 0xEC, 0x5B, 0x35, 0x08, 0x50,
+ 0xE7, 0x56, 0x4A, 0x02, 0xBC, 0x5A, 0xBD, 0x43,
+ 0x6F, 0x79, 0xB2, 0xF7, 0x60, 0xE9, 0xA0, 0x1B,
+ 0xC8, 0xDD, 0x9D, 0xA3, 0x5C, 0x61, 0x77, 0x72,
+ 0x9C, 0x31, 0x0E, 0x05, 0x1E, 0x12, 0xF1, 0xC9,
+ 0x78, 0x4E, 0x15, 0x7D, 0x54, 0xCB, 0x73, 0xEA,
+ 0xC5, 0x2B, 0x0F, 0x7E, 0x42, 0x96, 0xC6, 0x74,
+ 0x09, 0x65, 0x34, 0xE6, 0x63, 0xA6, 0x70, 0xD3,
+ 0x27, 0x87, 0x3A, 0x16, 0x7B, 0x13, 0x06, 0x40,
+ 0x46, 0x69, 0xAD, 0x88, 0x81, 0xC0, 0x37, 0x58,
+ 0xD1, 0x8A, 0x8E, 0x9A, 0x5D, 0x6D, 0xC7, 0xC3,
+ 0xD2, 0xF4, 0x3F, 0x57, 0x3C, 0x4F, 0xA9, 0x6A,
+ 0x92, 0xA5, 0x97, 0x0C, 0x2A, 0x36, 0x47, 0xDB,
+ 0x8C, 0xEE, 0x03, 0x89, 0x7F, 0x91, 0x24, 0x80,
+ 0x2F, 0x62, 0xE4, 0xAF, 0x17, 0x99, 0xD6, 0xCD,
+ 0xFE, 0x76, 0x1C, 0xD4, 0x3E, 0xFF, 0xD8, 0xC4,
+ 0x39, 0x32, 0xCF, 0xE2, 0xE3, 0x53, 0xD7, 0xCC,
+ 0xD9, 0x11, 0xAA, 0x1F, 0x01, 0x3B, 0x51, 0xB5,
+ 0x94, 0x4B, 0x28, 0xF0, 0xAC, 0x44, 0x14, 0x4C,
+ 0xB9, 0xA7, 0xB8, 0x1A, 0xD5, 0xCA, 0xE8, 0x82,
+ 0x9F, 0x2D, 0xAB, 0x2E, 0x29, 0xFD, 0x68, 0xB1,
+ 0x66, 0xC1, 0x7A, 0xFA, 0x71, 0x04, 0xA8, 0xB0,
+ 0x59, 0x18, 0xAE, 0x25, 0x3D, 0xE5, 0xF6, 0x41,
+ 0x86, 0x75, 0x6B, 0xBB, 0xFC, 0x84, 0x8B, 0x85,
+ 0x10, 0x23, 0xB6, 0xF3, 0x19, 0x30, 0x20, 0x4D,
+ 0x95, 0x9E, 0xBF, 0xEF, 0xF8, 0x45, 0x00, 0xB4 };
+
+unsigned char table_179[256] = {
+ 0x50, 0x3D, 0x41, 0x42, 0x06, 0x5B, 0xD6, 0x34,
+ 0x9D, 0x3C, 0x7B, 0x14, 0xE2, 0x9B, 0x80, 0x15,
+ 0x51, 0x01, 0x6A, 0x30, 0xD7, 0xFC, 0x61, 0x4B,
+ 0x8A, 0xEC, 0x38, 0x71, 0x70, 0x2E, 0x1C, 0x72,
+ 0x79, 0x26, 0x4C, 0x48, 0xED, 0xAD, 0x25, 0x53,
+ 0x03, 0xD9, 0xB5, 0x0D, 0x8E, 0x19, 0xCC, 0xBE,
+ 0xE1, 0x91, 0x64, 0xA6, 0x21, 0xCE, 0x76, 0xAB,
+ 0x9F, 0xD1, 0xB6, 0x23, 0x6D, 0xB0, 0x90, 0xBD,
+ 0x09, 0x3A, 0x5E, 0xD0, 0x73, 0x10, 0x44, 0x08,
+ 0xFF, 0xB8, 0x24, 0x58, 0xDB, 0x65, 0x95, 0xAA,
+ 0xE9, 0xC4, 0x32, 0x2B, 0x84, 0xC9, 0xC7, 0xB1,
+ 0x4F, 0x0C, 0xCB, 0x11, 0x4E, 0x22, 0x4A, 0x16,
+ 0xDE, 0xBC, 0xEE, 0x68, 0x13, 0xFA, 0xC3, 0x98,
+ 0xEB, 0x29, 0x43, 0x9A, 0xA1, 0xE0, 0xF0, 0x3F,
+ 0x2F, 0x1B, 0xC2, 0x66, 0x35, 0xF5, 0xC8, 0xD8,
+ 0x5A, 0xE5, 0x87, 0x47, 0xD3, 0x7A, 0xE6, 0x39,
+ 0x77, 0x81, 0xF2, 0x0E, 0x83, 0x7E, 0x17, 0x6C,
+ 0xB3, 0x5C, 0xE8, 0xD2, 0xC0, 0xA4, 0xF9, 0x86,
+ 0xCD, 0xFB, 0x54, 0x7C, 0xBF, 0x2D, 0x82, 0xDA,
+ 0x96, 0x74, 0x97, 0xC5, 0x7D, 0x27, 0x57, 0x56,
+ 0xDC, 0xBA, 0x69, 0x8C, 0x9C, 0x88, 0xB4, 0x8D,
+ 0x37, 0xEA, 0x3B, 0x33, 0x2C, 0xB2, 0x45, 0xF7,
+ 0xC1, 0x1E, 0x46, 0x02, 0x6B, 0x3E, 0xA7, 0xD5,
+ 0x05, 0x0A, 0xA9, 0x1D, 0xA3, 0x4D, 0xAE, 0x6F,
+ 0x49, 0xDD, 0x8F, 0xEF, 0xBB, 0x67, 0x0B, 0x40,
+ 0x9E, 0xF1, 0x78, 0x28, 0xDF, 0x52, 0xF4, 0x92,
+ 0x94, 0x0F, 0xB9, 0x93, 0xF6, 0x1F, 0xAF, 0xA8,
+ 0xCA, 0xE4, 0x59, 0x7F, 0x85, 0x75, 0xC6, 0xFD,
+ 0x00, 0xB7, 0x55, 0xFE, 0x8B, 0x62, 0x5F, 0x12,
+ 0xF8, 0xD4, 0x89, 0xA0, 0x20, 0xE7, 0xCF, 0x60,
+ 0x5D, 0xAC, 0x1A, 0x36, 0x63, 0x99, 0x31, 0xF3,
+ 0x2A, 0x04, 0x18, 0xA5, 0xA2, 0x6E, 0x07, 0xE3 };
+
+unsigned char table_180[256] = {
+ 0xDA, 0xCC, 0x72, 0xA6, 0xE7, 0x07, 0xFD, 0x25,
+ 0x92, 0x39, 0x49, 0x02, 0xD6, 0x09, 0xA8, 0x65,
+ 0x2E, 0x6C, 0xA1, 0x19, 0xBF, 0x21, 0x11, 0xC7,
+ 0x3F, 0x9F, 0xF4, 0x51, 0xAF, 0x8C, 0xFE, 0xCD,
+ 0x7A, 0xEB, 0x5A, 0xF7, 0x18, 0x69, 0xB9, 0xED,
+ 0x37, 0x45, 0x13, 0xB4, 0xAA, 0x75, 0x47, 0x42,
+ 0xA3, 0x81, 0x88, 0x70, 0xC1, 0x36, 0x73, 0x1D,
+ 0x3B, 0x22, 0xB6, 0x35, 0xE9, 0x31, 0x56, 0x23,
+ 0xE1, 0xF5, 0xAD, 0x46, 0x99, 0x32, 0xE4, 0x40,
+ 0x00, 0x0F, 0x05, 0xC6, 0x33, 0x84, 0x7B, 0x4D,
+ 0x4B, 0x7D, 0x91, 0x3D, 0xCE, 0x64, 0x77, 0x55,
+ 0xD7, 0x2B, 0x2F, 0x2C, 0xB8, 0xD3, 0x85, 0xD1,
+ 0xB5, 0x6A, 0xF9, 0x41, 0x08, 0xBB, 0x87, 0xEC,
+ 0x78, 0xE0, 0xEE, 0x8D, 0x01, 0x58, 0x15, 0x8F,
+ 0x06, 0xF0, 0x8B, 0x27, 0x0D, 0x0B, 0x6D, 0xBD,
+ 0xCA, 0x2A, 0xA2, 0xE6, 0xDD, 0xBC, 0x4E, 0x5D,
+ 0x74, 0x04, 0x3A, 0x96, 0x66, 0x12, 0x1E, 0xF2,
+ 0xF6, 0xC4, 0xAE, 0x3C, 0x0C, 0x90, 0x68, 0xD8,
+ 0x24, 0x5E, 0x79, 0x10, 0xAC, 0xDF, 0x9B, 0xC5,
+ 0x44, 0xC3, 0x50, 0x5C, 0xA5, 0x89, 0x60, 0x5F,
+ 0x48, 0x17, 0x34, 0xA7, 0xE2, 0xF3, 0xD9, 0x3E,
+ 0x9C, 0xB7, 0x7C, 0x1F, 0xA9, 0xD4, 0xA4, 0x0E,
+ 0x8E, 0x4C, 0xDC, 0xF8, 0xF1, 0x98, 0xDE, 0x2D,
+ 0x61, 0xCB, 0xD5, 0x43, 0x86, 0x26, 0xB0, 0x7F,
+ 0x7E, 0xFF, 0xAB, 0x83, 0x14, 0x9A, 0x80, 0x16,
+ 0x30, 0xA0, 0x53, 0x97, 0x52, 0x9E, 0xB1, 0x1B,
+ 0xD0, 0x1A, 0xC8, 0x57, 0xBA, 0x6E, 0xFA, 0x94,
+ 0xE8, 0x63, 0x5B, 0x29, 0xEF, 0x71, 0x8A, 0x03,
+ 0xB3, 0x76, 0xC9, 0xD2, 0xBE, 0xE5, 0x82, 0x1C,
+ 0x95, 0x9D, 0x4A, 0x28, 0xEA, 0x0A, 0xC0, 0xE3,
+ 0x6F, 0x20, 0x54, 0xFB, 0x93, 0xFC, 0x6B, 0x38,
+ 0x62, 0x4F, 0xCF, 0xB2, 0xC2, 0x59, 0xDB, 0x67 };
+
+unsigned char table_181[256] = {
+ 0x2B, 0xED, 0x14, 0x05, 0x80, 0xCC, 0x5A, 0xF8,
+ 0x43, 0xB7, 0x86, 0xC6, 0xEE, 0xA6, 0xD7, 0xD6,
+ 0xA0, 0xC4, 0x21, 0x34, 0xB1, 0x8C, 0xF9, 0xF4,
+ 0x7C, 0x53, 0x06, 0xD4, 0x6B, 0x3F, 0xE1, 0x12,
+ 0x6A, 0xCE, 0xCF, 0xBF, 0x74, 0x3E, 0xD5, 0xCB,
+ 0x97, 0x01, 0xA2, 0x2D, 0xAE, 0xF7, 0x17, 0x29,
+ 0x47, 0x03, 0x0E, 0xE9, 0x82, 0x46, 0x94, 0xAF,
+ 0x2A, 0x90, 0xFE, 0x4A, 0x7E, 0x0C, 0x71, 0xB6,
+ 0xA5, 0xF2, 0x67, 0x41, 0xBA, 0xC2, 0x8A, 0x9D,
+ 0x36, 0xFF, 0x50, 0x2E, 0xC3, 0x91, 0x9C, 0x37,
+ 0x66, 0xAD, 0xB2, 0x1F, 0xE4, 0xE3, 0x9F, 0xDD,
+ 0x87, 0xC0, 0xE6, 0xEF, 0x13, 0x70, 0x5B, 0xDE,
+ 0x5C, 0x75, 0x7F, 0x4F, 0x44, 0xCA, 0x55, 0x57,
+ 0xF0, 0x26, 0xA7, 0xC7, 0x10, 0x51, 0x00, 0xB3,
+ 0x5D, 0x99, 0x81, 0x3B, 0xB9, 0x1C, 0x64, 0x7B,
+ 0xFB, 0xD9, 0x8D, 0x4E, 0xAC, 0x25, 0xBB, 0x69,
+ 0xDF, 0x02, 0x9E, 0x2C, 0xAB, 0xF3, 0x65, 0x09,
+ 0xA3, 0x6C, 0xC1, 0x76, 0x52, 0x30, 0xD8, 0x3A,
+ 0x40, 0x18, 0x59, 0xD0, 0xE5, 0xB4, 0x5F, 0x33,
+ 0x68, 0x92, 0x2F, 0xB8, 0x93, 0xD1, 0xEB, 0xA4,
+ 0xFC, 0x77, 0x19, 0x62, 0xC9, 0x49, 0x84, 0x1A,
+ 0x9A, 0xE7, 0x31, 0xE8, 0xE2, 0x58, 0xF1, 0x4B,
+ 0x1E, 0x0B, 0x39, 0xFD, 0x42, 0x7A, 0x89, 0x38,
+ 0x11, 0x98, 0x63, 0x08, 0xE0, 0xEA, 0xBE, 0xB0,
+ 0x45, 0x1B, 0x4C, 0x54, 0xC8, 0x27, 0x3D, 0x73,
+ 0x04, 0x8F, 0x79, 0xBC, 0x6F, 0x0D, 0x0F, 0xA1,
+ 0x60, 0xDC, 0xC5, 0xFA, 0x8E, 0xDA, 0x15, 0x96,
+ 0xD3, 0x07, 0xF5, 0x3C, 0x88, 0x72, 0x1D, 0x4D,
+ 0x8B, 0x61, 0x0A, 0xDB, 0xAA, 0x20, 0x23, 0xEC,
+ 0x6E, 0x22, 0x48, 0x28, 0xBD, 0xA9, 0x56, 0x5E,
+ 0x85, 0xA8, 0x95, 0x6D, 0x16, 0x78, 0xB5, 0xF6,
+ 0x32, 0x24, 0x7D, 0x9B, 0xD2, 0x83, 0x35, 0xCD };
+
+unsigned char table_182[256] = {
+ 0x06, 0x7F, 0x66, 0xB5, 0xBA, 0x1E, 0xFD, 0x51,
+ 0x81, 0x8D, 0x28, 0xA3, 0x15, 0x37, 0xDC, 0x58,
+ 0xE6, 0x3D, 0xB4, 0xB9, 0x2E, 0xA0, 0x2F, 0xC4,
+ 0xCB, 0xB1, 0x25, 0xBF, 0xC1, 0x4E, 0x5A, 0xE4,
+ 0x0F, 0x10, 0x7C, 0x52, 0xA7, 0x29, 0x76, 0x55,
+ 0xAA, 0x70, 0x62, 0x54, 0x43, 0x93, 0x3A, 0x7D,
+ 0x5B, 0x56, 0x33, 0x64, 0x74, 0x2A, 0xD9, 0x9B,
+ 0x88, 0xC0, 0x3C, 0x63, 0xDE, 0xF4, 0x73, 0xDF,
+ 0x9E, 0xB2, 0xA8, 0x4F, 0x04, 0x57, 0x47, 0x87,
+ 0x14, 0xFC, 0x27, 0x53, 0x83, 0xDB, 0xD7, 0x20,
+ 0x96, 0x31, 0xD0, 0xCF, 0x30, 0x19, 0x69, 0x1A,
+ 0xAE, 0x3B, 0x11, 0x0C, 0xA6, 0x95, 0x8A, 0xF2,
+ 0x1B, 0xCC, 0x78, 0xEF, 0xB3, 0x71, 0x84, 0xA2,
+ 0xF1, 0x7A, 0x92, 0x61, 0xCA, 0x90, 0x94, 0x89,
+ 0x68, 0xEE, 0x97, 0x38, 0x0D, 0xF9, 0x1F, 0x8E,
+ 0xE9, 0x26, 0xBD, 0xC9, 0xFF, 0x4C, 0x44, 0x1D,
+ 0x98, 0xE5, 0x86, 0xF3, 0x18, 0xB6, 0x09, 0xD2,
+ 0x7E, 0xC5, 0xE7, 0x2B, 0x8C, 0x8B, 0x60, 0x3F,
+ 0x2C, 0x6A, 0x08, 0x0E, 0x50, 0x32, 0x9F, 0xF0,
+ 0x9A, 0xC2, 0x39, 0xBE, 0xEA, 0x12, 0x16, 0xBB,
+ 0x5E, 0x67, 0xE3, 0xB8, 0x79, 0x46, 0xDA, 0x00,
+ 0xD3, 0xBC, 0xCE, 0x1C, 0x80, 0xFA, 0xAB, 0x65,
+ 0x4A, 0xF8, 0xAC, 0x72, 0x01, 0xC6, 0x35, 0x85,
+ 0x3E, 0x5C, 0xA1, 0x05, 0xA5, 0xA9, 0xE1, 0x40,
+ 0xEB, 0xE8, 0x5F, 0xF5, 0xC3, 0xD1, 0x34, 0xFB,
+ 0xEC, 0xF7, 0x9C, 0xC7, 0xDD, 0x6C, 0x36, 0x9D,
+ 0x42, 0x59, 0x99, 0x5D, 0xD8, 0x82, 0x07, 0x24,
+ 0x6D, 0xAD, 0x13, 0x48, 0x6B, 0x6E, 0x75, 0x4D,
+ 0xD5, 0x02, 0xED, 0xFE, 0x91, 0xCD, 0x77, 0xB0,
+ 0xF6, 0xC8, 0x6F, 0x23, 0xAF, 0xB7, 0x2D, 0xD6,
+ 0xA4, 0xE2, 0x45, 0x8F, 0x21, 0xE0, 0x49, 0x22,
+ 0x7B, 0x17, 0x0B, 0x0A, 0x41, 0x03, 0xD4, 0x4B };
+
+unsigned char table_183[32] = {
+ 0x1E, 0x1B, 0x11, 0x07, 0x08, 0x06, 0x18, 0x17,
+ 0x0D, 0x0F, 0x12, 0x03, 0x1D, 0x04, 0x0A, 0x1A,
+ 0x0C, 0x13, 0x14, 0x1F, 0x0B, 0x19, 0x10, 0x01,
+ 0x16, 0x05, 0x1C, 0x0E, 0x02, 0x00, 0x09, 0x15 };
+
+unsigned char table_184[32] = {
+ 0x0F, 0x1D, 0x17, 0x16, 0x0D, 0x05, 0x13, 0x1F,
+ 0x1B, 0x09, 0x1C, 0x1E, 0x15, 0x01, 0x06, 0x08,
+ 0x0C, 0x10, 0x0B, 0x02, 0x04, 0x0A, 0x07, 0x1A,
+ 0x18, 0x0E, 0x03, 0x11, 0x12, 0x14, 0x19, 0x00 };
+
+unsigned char table_185[256] = {
+ 0xA5, 0xEE, 0x2E, 0x28, 0xA7, 0xAC, 0xD9, 0xB2,
+ 0x6E, 0x04, 0xB4, 0x03, 0xE8, 0x92, 0x5F, 0x4D,
+ 0x73, 0x20, 0x71, 0xE0, 0x43, 0x53, 0x3F, 0xF8,
+ 0x96, 0xA1, 0x24, 0x97, 0xAD, 0x7B, 0xE5, 0xE6,
+ 0xF2, 0xCE, 0xE3, 0x76, 0x2F, 0xA2, 0x48, 0x0E,
+ 0x4B, 0x4A, 0x8B, 0x5A, 0x81, 0x2C, 0xBF, 0xD7,
+ 0xFB, 0x7D, 0x4C, 0x16, 0xF4, 0x00, 0xF5, 0x40,
+ 0x64, 0x74, 0xA9, 0x37, 0x86, 0xD3, 0x1B, 0xCD,
+ 0xF1, 0x1A, 0x90, 0x9F, 0x54, 0x79, 0x29, 0xC3,
+ 0x77, 0x85, 0x02, 0xB1, 0x70, 0xFE, 0x5B, 0xDA,
+ 0x6B, 0x01, 0x0C, 0x07, 0xB8, 0x58, 0x47, 0x42,
+ 0x09, 0xE4, 0x27, 0xDD, 0xF3, 0x1E, 0x10, 0x9E,
+ 0x49, 0x30, 0x05, 0xBE, 0x59, 0xEB, 0xD2, 0xAA,
+ 0xC8, 0x9D, 0x8C, 0x5E, 0x14, 0x56, 0x8E, 0xF7,
+ 0x38, 0x55, 0x87, 0xA3, 0x5D, 0x41, 0x4F, 0x1F,
+ 0xF6, 0x0F, 0x57, 0x91, 0xAE, 0xBA, 0xB3, 0x95,
+ 0x9B, 0x69, 0xC1, 0x11, 0xD0, 0x25, 0x7F, 0x3B,
+ 0x62, 0xCF, 0xC0, 0xA0, 0xFC, 0xB6, 0x12, 0x6C,
+ 0xF0, 0x13, 0x93, 0xAB, 0xC6, 0x78, 0x6D, 0x88,
+ 0x22, 0x08, 0x2A, 0xE2, 0xB7, 0x65, 0x31, 0x3A,
+ 0xA6, 0x7C, 0xF9, 0xDC, 0xE7, 0xA4, 0xC9, 0x63,
+ 0xA8, 0x0B, 0xED, 0x50, 0x36, 0xD8, 0x3E, 0xB0,
+ 0x6A, 0x5C, 0x45, 0x4E, 0x23, 0x84, 0x34, 0x9A,
+ 0xCC, 0x3D, 0xB5, 0xEA, 0xDE, 0x75, 0xD6, 0xFF,
+ 0x6F, 0xC2, 0xDB, 0x8D, 0x7A, 0x1C, 0xE9, 0x61,
+ 0x0A, 0x1D, 0x32, 0x52, 0x3C, 0x19, 0xFA, 0xD1,
+ 0xD4, 0x68, 0xC7, 0x0D, 0x99, 0x83, 0xEF, 0x80,
+ 0x82, 0xBD, 0xD5, 0x7E, 0x39, 0x72, 0x51, 0xAF,
+ 0x8A, 0x2D, 0xB9, 0x89, 0xC4, 0x67, 0x35, 0xE1,
+ 0x44, 0x06, 0xEC, 0xCB, 0x8F, 0x17, 0xDF, 0x94,
+ 0x60, 0xCA, 0x26, 0xFD, 0x33, 0x46, 0x21, 0xBB,
+ 0x2B, 0xC5, 0x98, 0x18, 0x66, 0x15, 0x9C, 0xBC };
+
+unsigned char table_186[256] = {
+ 0xB7, 0xFA, 0x03, 0x7C, 0x76, 0x43, 0xA7, 0x15,
+ 0x4B, 0x4F, 0x04, 0xAA, 0x4E, 0xD2, 0x52, 0xC8,
+ 0x79, 0x16, 0xF6, 0x61, 0x01, 0x5D, 0xD6, 0x47,
+ 0xDE, 0xC5, 0x4D, 0x2F, 0xF5, 0x29, 0x21, 0xE6,
+ 0x97, 0x35, 0xDC, 0x0E, 0x8B, 0xF4, 0x0F, 0xBE,
+ 0x30, 0x07, 0x1D, 0x46, 0x75, 0xCE, 0x56, 0x42,
+ 0x28, 0x93, 0x84, 0x20, 0xA5, 0xC2, 0x87, 0x45,
+ 0x1C, 0x6B, 0x55, 0x06, 0xEB, 0xB0, 0xF9, 0x14,
+ 0x23, 0xF1, 0xFC, 0xD7, 0x98, 0xD1, 0xA4, 0xED,
+ 0x5B, 0xB1, 0x12, 0x7A, 0xD5, 0x5F, 0x53, 0x88,
+ 0x95, 0x71, 0xE7, 0x5C, 0xF8, 0x83, 0xC7, 0x49,
+ 0xDD, 0xDA, 0x0B, 0xC1, 0x70, 0xEC, 0x67, 0xE2,
+ 0xEA, 0x72, 0x4C, 0x92, 0xA6, 0xE5, 0x59, 0xA9,
+ 0x3C, 0xFE, 0x0A, 0x65, 0x6E, 0xF3, 0xA3, 0x22,
+ 0x24, 0x81, 0xF2, 0xCC, 0xD3, 0xA0, 0xDF, 0xDB,
+ 0xAB, 0x09, 0x13, 0x96, 0x36, 0x9C, 0xEE, 0xD4,
+ 0x33, 0x5E, 0x26, 0xAE, 0x48, 0x38, 0xFF, 0x08,
+ 0x1F, 0x6D, 0x02, 0xEF, 0x7E, 0x57, 0x2A, 0x8A,
+ 0xBA, 0x90, 0xAF, 0xA8, 0x37, 0x8E, 0x9B, 0xC0,
+ 0x69, 0x32, 0x86, 0xBD, 0x73, 0x6C, 0xB9, 0x31,
+ 0x66, 0xBF, 0x1B, 0x44, 0x9E, 0xB2, 0xD0, 0xE0,
+ 0xF0, 0x2C, 0x3F, 0xE1, 0x91, 0x18, 0x19, 0x50,
+ 0xCA, 0x8F, 0x54, 0xB5, 0x8D, 0x0C, 0x17, 0x39,
+ 0x8C, 0x00, 0x7F, 0x41, 0xE3, 0x2E, 0x1A, 0x9D,
+ 0x27, 0xA1, 0x10, 0x34, 0x1E, 0x3A, 0x60, 0x77,
+ 0xBB, 0xB6, 0x0D, 0x4A, 0x3E, 0x6A, 0xB4, 0xA2,
+ 0xB3, 0xFD, 0xCD, 0x80, 0x51, 0xAD, 0xCF, 0xBC,
+ 0x40, 0x74, 0x6F, 0x68, 0x2B, 0xC3, 0xF7, 0x63,
+ 0xB8, 0x25, 0xC4, 0x62, 0xE9, 0xFB, 0x58, 0x85,
+ 0x78, 0xCB, 0x9A, 0x3D, 0xE4, 0xC9, 0x89, 0x2D,
+ 0x64, 0x82, 0xC6, 0x05, 0xD8, 0xAC, 0x99, 0x9F,
+ 0x11, 0x3B, 0x94, 0xE8, 0x7D, 0x7B, 0xD9, 0x5A };
+
+unsigned char table_187[32] = {
+ 0x0F, 0x04, 0x1D, 0x1B, 0x15, 0x10, 0x01, 0x0B,
+ 0x00, 0x17, 0x13, 0x07, 0x1E, 0x1F, 0x08, 0x0A,
+ 0x19, 0x09, 0x05, 0x06, 0x0C, 0x1A, 0x14, 0x16,
+ 0x0E, 0x18, 0x03, 0x1C, 0x12, 0x11, 0x0D, 0x02 };
+
+struct yahoo_fn yahoo_fntable[5][96] =
+ {{{ IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 },
+ { IDENT, 0, 0 }},
+ {{ MULADD, 0x36056CD7, 0x4387 },
+ { LOOKUP, (long)table_0, 0 },
+ { LOOKUP, (long)table_1, 0 },
+ { BITFLD, (long)table_2, 0 },
+ { LOOKUP, (long)table_3, 0 },
+ { BITFLD, (long)table_4, 0 },
+ { MULADD, 0x4ABB534D, 0x3769 },
+ { XOR, 0x1D242DA5, 0 },
+ { MULADD, 0x3C23132D, 0x339B },
+ { XOR, 0x0191265C, 0 },
+ { XOR, 0x3DB979DB, 0 },
+ { LOOKUP, (long)table_5, 0 },
+ { XOR, 0x1A550E1E, 0 },
+ { XOR, 0x2F140A2D, 0 },
+ { MULADD, 0x7C466A4B, 0x29BF },
+ { XOR, 0x2D3F30D3, 0 },
+ { MULADD, 0x7E823B21, 0x6BB3 },
+ { BITFLD, (long)table_6, 0 },
+ { LOOKUP, (long)table_7, 0 },
+ { BITFLD, (long)table_8, 0 },
+ { LOOKUP, (long)table_9, 0 },
+ { BITFLD, (long)table_10, 0 },
+ { LOOKUP, (long)table_11, 0 },
+ { BITFLD, (long)table_12, 0 },
+ { LOOKUP, (long)table_13, 0 },
+ { BITFLD, (long)table_14, 0 },
+ { MULADD, 0x5B756AB9, 0x7E9B },
+ { LOOKUP, (long)table_15, 0 },
+ { XOR, 0x1D1C4911, 0 },
+ { LOOKUP, (long)table_16, 0 },
+ { LOOKUP, (long)table_17, 0 },
+ { XOR, 0x46BD7771, 0 },
+ { XOR, 0x51AE2B42, 0 },
+ { MULADD, 0x2417591B, 0x177B },
+ { MULADD, 0x57F27C5F, 0x2433 },
+ { LOOKUP, (long)table_18, 0 },
+ { LOOKUP, (long)table_19, 0 },
+ { XOR, 0x71422261, 0 },
+ { BITFLD, (long)table_20, 0 },
+ { MULADD, 0x58E937F9, 0x1075 },
+ { LOOKUP, (long)table_21, 0 },
+ { BITFLD, (long)table_22, 0 },
+ { LOOKUP, (long)table_23, 0 },
+ { LOOKUP, (long)table_24, 0 },
+ { MULADD, 0x0B4C3D13, 0x1597 },
+ { BITFLD, (long)table_25, 0 },
+ { XOR, 0x0FE07D38, 0 },
+ { MULADD, 0x689B4017, 0x3CFB },
+ { BITFLD, (long)table_26, 0 },
+ { LOOKUP, (long)table_27, 0 },
+ { XOR, 0x35413DF3, 0 },
+ { MULADD, 0x05B611AB, 0x570B },
+ { MULADD, 0x0DA5334F, 0x3AC7 },
+ { XOR, 0x47706008, 0 },
+ { BITFLD, (long)table_28, 0 },
+ { LOOKUP, (long)table_29, 0 },
+ { BITFLD, (long)table_30, 0 },
+ { XOR, 0x57611B36, 0 },
+ { MULADD, 0x314C2CD1, 0x2B5B },
+ { XOR, 0x1EF33946, 0 },
+ { MULADD, 0x28EA041F, 0x638F },
+ { LOOKUP, (long)table_31, 0 },
+ { LOOKUP, (long)table_32, 0 },
+ { LOOKUP, (long)table_33, 0 },
+ { MULADD, 0x511537CB, 0x7135 },
+ { MULADD, 0x1CF71007, 0x5E17 },
+ { XOR, 0x583D4BCF, 0 },
+ { LOOKUP, (long)table_34, 0 },
+ { XOR, 0x373E6856, 0 },
+ { MULADD, 0x4D595519, 0x1A7D },
+ { LOOKUP, (long)table_35, 0 },
+ { LOOKUP, (long)table_36, 0 },
+ { XOR, 0x0E2A36A7, 0 },
+ { LOOKUP, (long)table_37, 0 },
+ { LOOKUP, (long)table_38, 0 },
+ { BITFLD, (long)table_39, 0 },
+ { BITFLD, (long)table_40, 0 },
+ { XOR, 0x53F3604F, 0 },
+ { BITFLD, (long)table_41, 0 },
+ { BITFLD, (long)table_42, 0 },
+ { MULADD, 0x1EDC0BA3, 0x7531 },
+ { LOOKUP, (long)table_43, 0 },
+ { XOR, 0x10DF1038, 0 },
+ { BITFLD, (long)table_44, 0 },
+ { LOOKUP, (long)table_45, 0 },
+ { XOR, 0x4EDE0CAC, 0 },
+ { MULADD, 0x2F076EEB, 0x5BCF },
+ { XOR, 0x6D86030F, 0 },
+ { XOR, 0x3F331713, 0 },
+ { LOOKUP, (long)table_46, 0 },
+ { MULADD, 0x41CD726F, 0x3F79 },
+ { BITFLD, (long)table_47, 0 },
+ { XOR, 0x0ECE0054, 0 },
+ { MULADD, 0x19B32B03, 0x4AD1 },
+ { BITFLD, (long)table_48, 0 },
+ { BITFLD, (long)table_49, 0 }},
+ {{ MULADD, 0x39731111, 0x419B },
+ { XOR, 0x54F7757A, 0 },
+ { BITFLD, (long)table_50, 0 },
+ { BITFLD, (long)table_51, 0 },
+ { LOOKUP, (long)table_52, 0 },
+ { LOOKUP, (long)table_53, 0 },
+ { MULADD, 0x3CC0256B, 0x7CE7 },
+ { XOR, 0x79991847, 0 },
+ { MULADD, 0x228F7FB5, 0x472D },
+ { MULADD, 0x32DA290B, 0x7745 },
+ { XOR, 0x7A28180D, 0 },
+ { BITFLD, (long)table_54, 0 },
+ { BITFLD, (long)table_55, 0 },
+ { MULADD, 0x5C814F8B, 0x227F },
+ { LOOKUP, (long)table_56, 0 },
+ { MULADD, 0x0B496F6D, 0x412D },
+ { XOR, 0x6F4B62DA, 0 },
+ { LOOKUP, (long)table_57, 0 },
+ { XOR, 0x64973977, 0 },
+ { LOOKUP, (long)table_58, 0 },
+ { LOOKUP, (long)table_59, 0 },
+ { BITFLD, (long)table_60, 0 },
+ { LOOKUP, (long)table_61, 0 },
+ { LOOKUP, (long)table_62, 0 },
+ { XOR, 0x6DD14C92, 0 },
+ { LOOKUP, (long)table_63, 0 },
+ { BITFLD, (long)table_64, 0 },
+ { BITFLD, (long)table_65, 0 },
+ { BITFLD, (long)table_66, 0 },
+ { LOOKUP, (long)table_67, 0 },
+ { XOR, 0x5E6324D8, 0 },
+ { LOOKUP, (long)table_68, 0 },
+ { LOOKUP, (long)table_69, 0 },
+ { LOOKUP, (long)table_70, 0 },
+ { BITFLD, (long)table_71, 0 },
+ { XOR, 0x62745ED0, 0 },
+ { MULADD, 0x102C215B, 0x0581 },
+ { LOOKUP, (long)table_72, 0 },
+ { LOOKUP, (long)table_73, 0 },
+ { LOOKUP, (long)table_74, 0 },
+ { MULADD, 0x19511111, 0x12C1 },
+ { LOOKUP, (long)table_75, 0 },
+ { MULADD, 0x2A6E2953, 0x6977 },
+ { LOOKUP, (long)table_76, 0 },
+ { XOR, 0x55CD5445, 0 },
+ { BITFLD, (long)table_77, 0 },
+ { BITFLD, (long)table_78, 0 },
+ { MULADD, 0x646C21EB, 0x43E5 },
+ { XOR, 0x71DC4898, 0 },
+ { XOR, 0x167519CB, 0 },
+ { XOR, 0x6D3158F8, 0 },
+ { XOR, 0x7EA95BEA, 0 },
+ { BITFLD, (long)table_79, 0 },
+ { XOR, 0x47377587, 0 },
+ { XOR, 0x2D8B6E8F, 0 },
+ { MULADD, 0x5E6105DB, 0x1605 },
+ { XOR, 0x65B543C8, 0 },
+ { LOOKUP, (long)table_80, 0 },
+ { BITFLD, (long)table_81, 0 },
+ { MULADD, 0x48AF73CB, 0x0A67 },
+ { XOR, 0x4FB96154, 0 },
+ { LOOKUP, (long)table_82, 0 },
+ { BITFLD, (long)table_83, 0 },
+ { XOR, 0x622C4954, 0 },
+ { BITFLD, (long)table_84, 0 },
+ { XOR, 0x20D220F3, 0 },
+ { XOR, 0x361D4F0D, 0 },
+ { XOR, 0x2B2000D1, 0 },
+ { XOR, 0x6FB8593E, 0 },
+ { LOOKUP, (long)table_85, 0 },
+ { BITFLD, (long)table_86, 0 },
+ { XOR, 0x2B7F7DFC, 0 },
+ { MULADD, 0x5FC41A57, 0x0693 },
+ { MULADD, 0x17154387, 0x2489 },
+ { BITFLD, (long)table_87, 0 },
+ { BITFLD, (long)table_88, 0 },
+ { BITFLD, (long)table_89, 0 },
+ { LOOKUP, (long)table_90, 0 },
+ { XOR, 0x7E221470, 0 },
+ { XOR, 0x7A600061, 0 },
+ { BITFLD, (long)table_91, 0 },
+ { BITFLD, (long)table_92, 0 },
+ { LOOKUP, (long)table_93, 0 },
+ { BITFLD, (long)table_94, 0 },
+ { MULADD, 0x00E813A5, 0x2CE5 },
+ { MULADD, 0x3D707E25, 0x3827 },
+ { MULADD, 0x77A53E07, 0x6A5F },
+ { BITFLD, (long)table_95, 0 },
+ { LOOKUP, (long)table_96, 0 },
+ { LOOKUP, (long)table_97, 0 },
+ { XOR, 0x43A73788, 0 },
+ { LOOKUP, (long)table_98, 0 },
+ { BITFLD, (long)table_99, 0 },
+ { LOOKUP, (long)table_100, 0 },
+ { XOR, 0x55F4606B, 0 },
+ { BITFLD, (long)table_101, 0 }},
+ {{ BITFLD, (long)table_102, 0 },
+ { MULADD, 0x32CA58E3, 0x04F9 },
+ { XOR, 0x11756B30, 0 },
+ { MULADD, 0x218B2569, 0x5DB1 },
+ { XOR, 0x77D64B90, 0 },
+ { BITFLD, (long)table_103, 0 },
+ { LOOKUP, (long)table_104, 0 },
+ { MULADD, 0x7D1428CB, 0x3D },
+ { XOR, 0x6F872C49, 0 },
+ { XOR, 0x2E484655, 0 },
+ { MULADD, 0x1E3349F7, 0x41F5 },
+ { LOOKUP, (long)table_105, 0 },
+ { BITFLD, (long)table_106, 0 },
+ { XOR, 0x61640311, 0 },
+ { BITFLD, (long)table_107, 0 },
+ { LOOKUP, (long)table_108, 0 },
+ { LOOKUP, (long)table_109, 0 },
+ { LOOKUP, (long)table_110, 0 },
+ { XOR, 0x007044D3, 0 },
+ { BITFLD, (long)table_111, 0 },
+ { MULADD, 0x5C221625, 0x576F },
+ { LOOKUP, (long)table_112, 0 },
+ { LOOKUP, (long)table_113, 0 },
+ { XOR, 0x2D406BB1, 0 },
+ { MULADD, 0x680B1F17, 0x12CD },
+ { BITFLD, (long)table_114, 0 },
+ { MULADD, 0x12564D55, 0x32B9 },
+ { MULADD, 0x21A67897, 0x6BAB },
+ { LOOKUP, (long)table_115, 0 },
+ { MULADD, 0x06405119, 0x7143 },
+ { XOR, 0x351D01ED, 0 },
+ { MULADD, 0x46356F6B, 0x0A49 },
+ { MULADD, 0x32C77969, 0x72F3 },
+ { BITFLD, (long)table_116, 0 },
+ { LOOKUP, (long)table_117, 0 },
+ { LOOKUP, (long)table_118, 0 },
+ { BITFLD, (long)table_119, 0 },
+ { LOOKUP, (long)table_120, 0 },
+ { BITFLD, (long)table_121, 0 },
+ { MULADD, 0x74D52C55, 0x5F43 },
+ { XOR, 0x26201CA8, 0 },
+ { XOR, 0x7AEB3255, 0 },
+ { LOOKUP, (long)table_122, 0 },
+ { MULADD, 0x578F1047, 0x640B },
+ { LOOKUP, (long)table_123, 0 },
+ { LOOKUP, (long)table_124, 0 },
+ { BITFLD, (long)table_125, 0 },
+ { BITFLD, (long)table_126, 0 },
+ { XOR, 0x4A1352CF, 0 },
+ { MULADD, 0x4BFB6EF3, 0x704F },
+ { MULADD, 0x1B4C7FE7, 0x5637 },
+ { MULADD, 0x04091A3B, 0x4917 },
+ { XOR, 0x270C2F52, 0 },
+ { LOOKUP, (long)table_127, 0 },
+ { BITFLD, (long)table_128, 0 },
+ { LOOKUP, (long)table_129, 0 },
+ { BITFLD, (long)table_130, 0 },
+ { MULADD, 0x127549D5, 0x579B },
+ { MULADD, 0x0AB54121, 0x7A47 },
+ { BITFLD, (long)table_131, 0 },
+ { XOR, 0x751E6E49, 0 },
+ { LOOKUP, (long)table_132, 0 },
+ { LOOKUP, (long)table_133, 0 },
+ { XOR, 0x670C3F74, 0 },
+ { MULADD, 0x6B080851, 0x7E8B },
+ { XOR, 0x71CD789E, 0 },
+ { XOR, 0x3EB20B7B, 0 },
+ { BITFLD, (long)table_134, 0 },
+ { LOOKUP, (long)table_135, 0 },
+ { MULADD, 0x58A67753, 0x272B },
+ { MULADD, 0x1AB54AD7, 0x4D33 },
+ { MULADD, 0x07D30A45, 0x0569 },
+ { MULADD, 0x737616BF, 0x70C7 },
+ { LOOKUP, (long)table_136, 0 },
+ { MULADD, 0x45C4485D, 0x2063 },
+ { BITFLD, (long)table_137, 0 },
+ { XOR, 0x2598043D, 0 },
+ { MULADD, 0x223A4FE3, 0x49A7 },
+ { XOR, 0x1EED619F, 0 },
+ { BITFLD, (long)table_138, 0 },
+ { XOR, 0x6F477561, 0 },
+ { BITFLD, (long)table_139, 0 },
+ { BITFLD, (long)table_140, 0 },
+ { LOOKUP, (long)table_141, 0 },
+ { MULADD, 0x4BC13C4F, 0x45C1 },
+ { XOR, 0x3B547BFB, 0 },
+ { LOOKUP, (long)table_142, 0 },
+ { MULADD, 0x71406AB3, 0x7A5F },
+ { XOR, 0x2F1467E9, 0 },
+ { MULADD, 0x009366D1, 0x22D1 },
+ { MULADD, 0x587D1B75, 0x2CA5 },
+ { MULADD, 0x213A4BE7, 0x4499 },
+ { MULADD, 0x62653E89, 0x2D5D },
+ { BITFLD, (long)table_143, 0 },
+ { MULADD, 0x4F5F3257, 0x444F },
+ { MULADD, 0x4C0E2B2B, 0x19D3 }},
+ {{ MULADD, 0x3F867B35, 0x7B3B },
+ { MULADD, 0x32D25CB1, 0x3D6D },
+ { BITFLD, (long)table_144, 0 },
+ { MULADD, 0x50FA1C51, 0x5F4F },
+ { LOOKUP, (long)table_145, 0 },
+ { XOR, 0x05FE7AF1, 0 },
+ { MULADD, 0x14067C29, 0x10C5 },
+ { LOOKUP, (long)table_146, 0 },
+ { MULADD, 0x4A5558C5, 0x271F },
+ { XOR, 0x3C0861B1, 0 },
+ { BITFLD, (long)table_147, 0 },
+ { LOOKUP, (long)table_148, 0 },
+ { MULADD, 0x18837C9D, 0x6335 },
+ { BITFLD, (long)table_149, 0 },
+ { XOR, 0x7DAB5033, 0 },
+ { LOOKUP, (long)table_150, 0 },
+ { MULADD, 0x03B87321, 0x7225 },
+ { XOR, 0x7F906745, 0 },
+ { LOOKUP, (long)table_151, 0 },
+ { BITFLD, (long)table_152, 0 },
+ { XOR, 0x21C46C2C, 0 },
+ { MULADD, 0x2B36757D, 0x028D },
+ { BITFLD, (long)table_153, 0 },
+ { LOOKUP, (long)table_154, 0 },
+ { XOR, 0x106B4A85, 0 },
+ { XOR, 0x17640F11, 0 },
+ { LOOKUP, (long)table_155, 0 },
+ { XOR, 0x69E60486, 0 },
+ { LOOKUP, (long)table_156, 0 },
+ { MULADD, 0x3782017D, 0x05BF },
+ { BITFLD, (long)table_157, 0 },
+ { LOOKUP, (long)table_158, 0 },
+ { XOR, 0x6BCA53B0, 0 },
+ { LOOKUP, (long)table_159, 0 },
+ { LOOKUP, (long)table_160, 0 },
+ { LOOKUP, (long)table_161, 0 },
+ { LOOKUP, (long)table_162, 0 },
+ { XOR, 0x0B8236E3, 0 },
+ { BITFLD, (long)table_163, 0 },
+ { MULADD, 0x5EE51C43, 0x4553 },
+ { BITFLD, (long)table_164, 0 },
+ { LOOKUP, (long)table_165, 0 },
+ { LOOKUP, (long)table_166, 0 },
+ { LOOKUP, (long)table_167, 0 },
+ { MULADD, 0x42B14C6F, 0x5531 },
+ { XOR, 0x4A2548E8, 0 },
+ { MULADD, 0x5C071D85, 0x2437 },
+ { LOOKUP, (long)table_168, 0 },
+ { MULADD, 0x29195861, 0x108B },
+ { XOR, 0x24012258, 0 },
+ { LOOKUP, (long)table_169, 0 },
+ { XOR, 0x63CC2377, 0 },
+ { XOR, 0x08D04B59, 0 },
+ { MULADD, 0x3FD30CF5, 0x7027 },
+ { XOR, 0x7C3E0478, 0 },
+ { MULADD, 0x457776B7, 0x24B3 },
+ { XOR, 0x086652BC, 0 },
+ { MULADD, 0x302F5B13, 0x371D },
+ { LOOKUP, (long)table_170, 0 },
+ { MULADD, 0x58692D47, 0x0671 },
+ { XOR, 0x6601178E, 0 },
+ { MULADD, 0x0F195B9B, 0x1369 },
+ { XOR, 0x07BA21D8, 0 },
+ { BITFLD, (long)table_171, 0 },
+ { BITFLD, (long)table_172, 0 },
+ { XOR, 0x13AC3D21, 0 },
+ { MULADD, 0x5BCF3275, 0x6E1B },
+ { MULADD, 0x62725C5B, 0x16B9 },
+ { MULADD, 0x5B950FDF, 0x2D35 },
+ { BITFLD, (long)table_173, 0 },
+ { BITFLD, (long)table_174, 0 },
+ { MULADD, 0x73BA5335, 0x1C13 },
+ { BITFLD, (long)table_175, 0 },
+ { BITFLD, (long)table_176, 0 },
+ { XOR, 0x3E144154, 0 },
+ { MULADD, 0x4EED7B27, 0x38AB },
+ { LOOKUP, (long)table_177, 0 },
+ { MULADD, 0x627C7E0F, 0x7F01 },
+ { MULADD, 0x5D7E1F73, 0x2C0F },
+ { LOOKUP, (long)table_178, 0 },
+ { MULADD, 0x55C9525F, 0x4659 },
+ { XOR, 0x3765334C, 0 },
+ { MULADD, 0x5DF66DDF, 0x7C25 },
+ { LOOKUP, (long)table_179, 0 },
+ { LOOKUP, (long)table_180, 0 },
+ { XOR, 0x16AE5776, 0 },
+ { LOOKUP, (long)table_181, 0 },
+ { LOOKUP, (long)table_182, 0 },
+ { BITFLD, (long)table_183, 0 },
+ { BITFLD, (long)table_184, 0 },
+ { LOOKUP, (long)table_185, 0 },
+ { MULADD, 0x4392327B, 0x7E0D },
+ { LOOKUP, (long)table_186, 0 },
+ { MULADD, 0x3D8B0CB5, 0x640D },
+ { MULADD, 0x32865601, 0x4D43 },
+ { BITFLD, (long)table_187, 0 }}};
+
+#define A( x ) (( x ) & 0xFF )
+#define B( x ) (( x ) >> 8 & 0xFF )
+#define C( x ) (( x ) >> 16 & 0xFF )
+#define D( x ) (( x ) >> 24 & 0xFF )
+
+int yahoo_xfrm( int table, int depth, int seed )
+{
+ struct yahoo_fn *xfrm;
+ int i, j, z;
+ unsigned int n = seed;
+ unsigned char *arg;
+
+ for( i = 0; i < depth; i++ )
+ {
+ xfrm = &yahoo_fntable[table][n % 96];
+ switch( xfrm->type )
+ {
+ case IDENT:
+ return seed;
+ case XOR:
+ seed ^= xfrm->arg1;
+ break;
+ case MULADD:
+ seed = seed * xfrm->arg1 + xfrm->arg2;
+ break;
+ case LOOKUP:
+ arg = (unsigned char *)xfrm->arg1;
+ seed = arg[A( seed )] | arg[B( seed )] << 8 | arg[C( seed )] << 16
+ | arg[D( seed )] << 24;
+ break;
+ case BITFLD:
+ arg = (unsigned char *)xfrm->arg1;
+ for( j = 0, z = 0; j < 32; j++ )
+ z = ((( seed >> j ) & 1 ) << arg[j] ) | ( ~( 1 << arg[j] ) & z );
+ seed = z;
+ break;
+ }
+ if( depth - i == 1 )
+ return seed;
+ z = (((((( A( seed ) * 0x9E3779B1 ) ^ B( seed )) * 0x9E3779B1 )
+ ^ C( seed )) * 0x9E3779B1 ) ^ D( seed )) * 0x9E3779B1;
+ n = (((( z ^ ( z >> 8 )) >> 16 ) ^ z ) ^ ( z >> 8 )) & 0xFF;
+ seed *= 0x00010DCD;
+ }
+ return seed;
+}
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoo_fn.h b/kopete/protocols/yahoo/libkyahoo/yahoo_fn.h
new file mode 100644
index 00000000..9853cbee
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoo_fn.h
@@ -0,0 +1,33 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 2003
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define IDENT 1 /* identify function */
+#define XOR 2 /* xor with arg1 */
+#define MULADD 3 /* multipy by arg1 then add arg2 */
+#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */
+#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */
+
+struct yahoo_fn
+{
+ int type;
+ long arg1, arg2;
+};
+
+int yahoo_xfrm( int table, int depth, int seed );
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.cpp b/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.cpp
new file mode 100644
index 00000000..1608cd6f
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.cpp
@@ -0,0 +1,108 @@
+/*
+ yahoobuddyiconloader.cpp - Fetches YahooBuddyIcons
+
+ Copyright (c) 2005 by André Duffeck <andre@duffeck.de>
+
+ *************************************************************************
+ * *
+ * 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 "yahoobuddyiconloader.h"
+
+// QT Includes
+#include <qfile.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kurl.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+
+#include "yahootypes.h"
+#include "client.h"
+
+YahooBuddyIconLoader::YahooBuddyIconLoader( Client *c )
+: m_client( c )
+{
+}
+
+YahooBuddyIconLoader::~YahooBuddyIconLoader()
+{
+}
+
+void YahooBuddyIconLoader::fetchBuddyIcon( const QString &who, KURL url, int checksum )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ KIO::TransferJob *transfer;
+ QString Url = url.url();
+ QString ext = Url.left( Url.findRev( "?" ) );
+ ext = ext.right( ext.length() - ext.findRev( "." ) );
+
+ transfer = KIO::get( url, false, false );
+ connect( transfer, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotComplete( KIO::Job* ) ) );
+ connect( transfer, SIGNAL( data( KIO::Job*, const QByteArray& ) ), this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
+
+ m_jobs[transfer].url = url;
+ m_jobs[transfer].who = who;
+ m_jobs[transfer].checksum = checksum;
+ m_jobs[transfer].file = new KTempFile( locateLocal( "tmp", "yahoobuddyicon-" ), ext );
+ m_jobs[transfer].file->setAutoDelete( true );
+
+}
+
+void YahooBuddyIconLoader::slotData( KIO::Job *job, const QByteArray& data )
+{
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ KIO::TransferJob *transfer = static_cast< KIO::TransferJob * >(job);
+
+ if( m_jobs[transfer].file )
+ m_jobs[transfer].file->file()->writeBlock( data.data() , data.size() );
+
+}
+
+void YahooBuddyIconLoader::slotComplete( KIO::Job *job )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ KIO::TransferJob *transfer = static_cast< KIO::TransferJob * >(job);
+
+ if ( job->error () || transfer->isErrorPage () )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "An error occured while downloading buddy icon." << endl;
+ if( m_client )
+ m_client->notifyError( i18n( "An error occured while downloading buddy icon (%1)" ).arg(m_jobs[transfer].url.url()), job->errorString(), Client::Info );
+ }
+ else
+ {
+ if ( m_jobs[transfer].file )
+ {
+ m_jobs[transfer].file->close();
+ emit fetchedBuddyIcon( m_jobs[transfer].who, m_jobs[transfer].file, m_jobs[transfer].checksum );
+ }
+ else
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Fatal Error occured. IconLoadJob has an empty KTempFile pointer." << endl;
+ if( m_client )
+ m_client->notifyError( i18n( "Fatal Error occured while downloading buddy icon." ), i18n( "IconLoadJob has an empty KTempFile pointer." ), Client::Info );
+ }
+ }
+
+ m_jobs.remove( transfer );
+}
+
+
+
+#include "yahoobuddyiconloader.moc"
+
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.h b/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.h
new file mode 100644
index 00000000..c1a943c2
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoobuddyiconloader.h
@@ -0,0 +1,77 @@
+/*
+ yahoobuddyiconloader.h - Fetches YahooBuddyIcons
+
+ Copyright (c) 2005 by André Duffeck <andre@duffeck.de>
+
+ *************************************************************************
+ * *
+ * 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 YAHOOBUDDYICONLOADER_
+#define YAHOOBUDDYICONLOADER_
+
+// QT Includes
+#include <qobject.h>
+#include <qstring.h>
+#include <qmap.h>
+
+// KDE Includes
+#include <kurl.h>
+
+class KTempFile;
+class Client;
+namespace KIO {
+ class Job;
+ class TransferJob;
+}
+
+struct IconLoadJob {
+ KURL url;
+ QString who;
+ int checksum;
+ KTempFile *file;
+};
+
+/**
+ * @author André Duffeck
+ *
+ * This class handles the download of a Buddy icon.
+ * If the download was succesfull it emits a signal with a pointer
+ * to the temporary file, the icon was stored at
+ */
+class YahooBuddyIconLoader : public QObject
+{
+ Q_OBJECT
+public:
+ YahooBuddyIconLoader( Client *c );
+ ~YahooBuddyIconLoader();
+
+ /**
+ * Add a BuddyIcon for download.
+ */
+ void fetchBuddyIcon( const QString &who, KURL url, int checksum );
+
+signals:
+ /**
+ * The account can connect to this signal and append the icon
+ * stored in 'file' to the apropriate contact
+ */
+ void fetchedBuddyIcon( const QString &who, KTempFile *file, int checksum );
+
+private slots:
+ void slotData( KIO::Job *job, const QByteArray &data );
+ void slotComplete( KIO::Job *job );
+
+private:
+ typedef QMap< KIO::TransferJob *, IconLoadJob > TransferJobMap;
+ TransferJobMap m_jobs;
+ Client *m_client;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoobytestream.cpp b/kopete/protocols/yahoo/libkyahoo/yahoobytestream.cpp
new file mode 100644
index 00000000..87cf54d1
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoobytestream.cpp
@@ -0,0 +1,140 @@
+/*
+ YMSG - Yahoo Protocol Knetwork Bytestream
+
+ Copyright (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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 "yahoobytestream.h"
+
+KNetworkByteStream::KNetworkByteStream( QObject *parent, const char */*name*/ )
+ : ByteStream ( parent )
+{
+ kdDebug( 14181 ) << 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( 14181 ) << 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 ( 14181 ) << 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();
+ kdDebug( 14181 ) << k_funcinfo << "[writeData.size() = " << writeData.size() << "]" << endl;
+
+ 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( 14181 ) << k_funcinfo << "Socket has been closed." << endl;
+
+ // depending on who closed the socket, emit different signals
+ if ( mClosing )
+ {
+ kdDebug( 14181 ) << "..by ourselves!" << endl;
+ kdDebug( 14181 ) << "socket error is " << socket()->errorString( socket()->error() ) << endl;
+ emit connectionClosed ();
+ }
+ else
+ {
+ kdDebug( 14181 ) << "..by the other end" << endl;
+ emit delayedCloseFinished ();
+ }
+}
+
+void KNetworkByteStream::slotReadyRead()
+{
+ kdDebug( 14181 ) << endl;
+ // 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 )
+{
+ kdDebug( 14181 ) << "[int bytes]: " << bytes << endl;
+ emit bytesWritten(bytes);
+}
+
+void KNetworkByteStream::slotError( int code )
+{
+ kdDebug( 14181 ) << k_funcinfo << "Socket error " << code << endl;
+
+ emit error( code );
+}
+
+#include "yahoobytestream.moc"
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/yahoo/libkyahoo/yahoobytestream.h b/kopete/protocols/yahoo/libkyahoo/yahoobytestream.h
new file mode 100644
index 00000000..ac8aef63
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahoobytestream.h
@@ -0,0 +1,69 @@
+/*
+ YMSG - Yahoo Protocol Knetwork Bytestream
+
+ Copyright (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU 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
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
+
diff --git a/kopete/protocols/yahoo/libkyahoo/yahooclientstream.cpp b/kopete/protocols/yahoo/libkyahoo/yahooclientstream.cpp
new file mode 100644
index 00000000..548140b1
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahooclientstream.cpp
@@ -0,0 +1,418 @@
+/*
+ oscarclientstream.cpp - Kopete Oscar Protocol
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+
+#include <qapplication.h> // for qdebug
+#include <qguardedptr.h>
+#include <qobject.h>
+#include <qptrqueue.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+
+#include "bytestream.h"
+#include "connector.h"
+#include "coreprotocol.h"
+#include "transfer.h"
+
+#include "yahooclientstream.h"
+#include "yahootypes.h"
+
+void cs_dump( const QByteArray &bytes );
+
+enum {
+ Idle,
+ Connecting,
+ Active,
+ Closing
+};
+
+enum {
+ Client,
+ Server
+};
+
+class ClientStream::Private
+{
+public:
+ Private()
+ {
+ conn = 0;
+ bs = 0;
+
+ username = QString::null;
+ password = QString::null;
+ server = QString::null;
+ haveLocalAddr = false;
+ doBinding = true;
+
+ reset();
+ }
+ void reset()
+ {
+ state = Idle;
+ notify = 0;
+ newTransfers = false;
+ }
+
+ QString username;
+ QString password;
+ QString server;
+ bool doAuth; //send the initial login sequences to get the cookie
+ bool haveLocalAddr;
+ QHostAddress localAddr;
+ Q_UINT16 localPort;
+ bool doBinding;
+
+ Connector *conn;
+ ByteStream *bs;
+ CoreProtocol client;
+
+ QString defRealm;
+
+ int mode;
+ int state;
+ int notify;
+ bool newTransfers;
+
+ int errCond;
+ QString errText;
+
+ QPtrQueue<Transfer> in;
+
+ QTimer noopTimer; // used to send icq keepalive
+ int noop_time;
+};
+
+ClientStream::ClientStream(Connector *conn, QObject *parent)
+:Stream(parent)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ 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()));
+}
+
+ClientStream::~ClientStream()
+{
+ reset();
+ delete d;
+}
+
+void ClientStream::reset(bool all)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ d->reset();
+ d->noopTimer.stop();
+
+ // client
+ if(d->mode == Client) {
+
+ // 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();
+}
+
+void ClientStream::connectToServer(const QString& server, bool auth)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ reset(true);
+ d->state = Connecting;
+ d->doAuth = auth;
+ d->server = server;
+
+ d->conn->connectToServer( d->server );
+}
+
+void ClientStream::continueAfterWarning()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+/* unneeded?
+ if(d->state == WaitVersion) {
+ d->state = Connecting;
+ processNext();
+ }
+ else if(d->state == WaitTLS) {
+ d->state = Connecting;
+ processNext();
+ }
+*/
+}
+
+void ClientStream::accept()
+{
+
+}
+
+bool ClientStream::isActive() const
+{
+ return (d->state != Idle);
+}
+
+bool ClientStream::isAuthenticated() const
+{
+ return (d->state == Active);
+}
+
+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;
+}
+
+void ClientStream::close()
+{
+ if(d->state == Active) {
+ d->state = Closing;
+// d->client.shutdown();
+ processNext();
+ }
+ else if(d->state != Idle && d->state != Closing) {
+ reset();
+ }
+}
+
+bool ClientStream::transfersAvailable() const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ return ( !d->in.isEmpty() );
+}
+
+Transfer* ClientStream::read()
+{
+ if(d->in.isEmpty())
+ return 0; //first from queue...
+ else
+ return d->in.dequeue();
+}
+
+void ClientStream::write( Transfer *request )
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ // pass to CoreProtocol for transformation into wire format
+ d->client.outgoingTransfer( request );
+}
+
+void cs_dump( const QByteArray &bytes )
+{
+#if 0
+ qDebug( "contains: %i bytes ", 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" );
+#endif
+ Q_UNUSED( bytes );
+}
+
+void ClientStream::cp_outgoingData( const QByteArray& outgoingBytes )
+{
+ // take formatted bytes from CoreProtocol and put them on the wire
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "[data size: " << outgoingBytes.size() << "]" << endl;
+ //cs_dump( outgoingBytes );
+ d->bs->write( outgoingBytes );
+}
+
+void ClientStream::cp_incomingData()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ Transfer * incoming = d->client.incomingTransfer();
+ if ( incoming )
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - got a new transfer" << endl;
+ d->in.enqueue( incoming );
+ d->newTransfers = true;
+ emit doReadyRead();
+ }
+ else
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - client signalled incomingData but none was available, state is: "<< d->client.state() << endl;
+}
+
+/* Connector connected */
+void ClientStream::cr_connected()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+
+ d->bs = d->conn->stream();
+ connect(d->bs, SIGNAL(connectionClosed()), SLOT(bs_connectionClosed()));
+ connect(d->bs, SIGNAL(delayedCloseFinished()), SLOT(bs_delayedCloseFinished()));
+ connect(d->bs, SIGNAL(readyRead()), SLOT(bs_readyRead()));
+ connect(d->bs, SIGNAL(bytesWritten(int)), SLOT(bs_bytesWritten(int)));
+ connect(d->bs, SIGNAL(error(int)), SLOT(bs_error(int)));
+
+ QByteArray spare = d->bs->read();
+
+ QGuardedPtr<QObject> self = this;
+ emit connected();
+ if(!self)
+ return;
+}
+
+void ClientStream::cr_error()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ 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)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ // TODO
+}
+
+void ClientStream::bs_readyRead()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ QByteArray a;
+ //qDebug( "size of storage for incoming data is %i bytes.", a.size() );
+ a = d->bs->read();
+
+ //QCString cs(a.data(), a.size()+1);
+ //qDebug("ClientStream: recv: %d [%s]\n", a.size(), cs.data());
+ //kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " recv: " << a.size() <<" bytes" <<endl;
+ //cs_dump( a );
+
+ d->client.addIncomingData(a);
+}
+
+void ClientStream::bs_bytesWritten(int bytes)
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " written: " << bytes <<" bytes" <<endl;
+}
+
+void ClientStream::srvProcessNext()
+{
+}
+
+void ClientStream::doReadyRead()
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ emit readyRead();
+}
+
+void ClientStream::processNext()
+{
+ if( !d->in.isEmpty() )
+ {
+ QTimer::singleShot(0, this, SLOT(doReadyRead()));
+ }
+}
+
+bool ClientStream::handleNeed()
+{
+ return false;
+}
+
+
+void ClientStream::doNoop()
+{
+}
+
+void ClientStream::handleError()
+{
+}
+
+#include "yahooclientstream.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/yahooclientstream.h b/kopete/protocols/yahoo/libkyahoo/yahooclientstream.h
new file mode 100644
index 00000000..28301843
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahooclientstream.h
@@ -0,0 +1,159 @@
+/*
+ oscarclientstream.h - Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Matt Rogers <matt.rogers@kdemail.net>
+
+ Based on code Copyright (c) 2004 SuSE Linux AG <http://www.suse.com>
+ Based on Iris, Copyright (C) 2003 Justin Karneges
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOO_CLIENTSTREAM_H
+#define YAHOO_CLIENTSTREAM_H
+
+#include "stream.h"
+
+class QHostAddress;
+
+// forward defines
+class ByteStream;
+class Connector;
+class Transfer;
+
+class ClientStream : public Stream
+{
+ Q_OBJECT
+public:
+ enum Error {
+ ErrConnection = ErrCustom, // Connection error, ask Connector-subclass what's up
+ ErrNeg, // Negotiation error, see condition
+ ErrAuth, // Auth error, see condition
+ 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 AuthCond {
+ GenericAuthError, // all-purpose "can't login" error
+ NoMech, // No appropriate auth mech available
+ BadProto, // Bad SASL auth protocol
+ BadServ, // Server failed mutual auth
+ InvalidUserId, // bad user id
+ 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, QObject *parent=0);
+ ~ClientStream();
+
+ void connectToServer(const QString& server, 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 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( Transfer* request );
+
+ int errorCondition() const;
+ QString errorText() const;
+
+ // 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 authenticated(); // this signal is ordinarily emitted in processNext
+ void warning(int);
+ void readyRead(); //signals that there is a transfer ready to be read
+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 bs_readyRead();
+ void bs_bytesWritten(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/yahoo/libkyahoo/yahooconnector.cpp b/kopete/protocols/yahoo/libkyahoo/yahooconnector.cpp
new file mode 100644
index 00000000..0e163de8
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahooconnector.cpp
@@ -0,0 +1,111 @@
+
+/***************************************************************************
+ gwconnector.cpp - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 "yahooconnector.h"
+#include "yahoobytestream.h"
+#include "yahootypes.h"
+
+KNetworkConnector::KNetworkConnector( QObject *parent, const char */*name*/ )
+ : Connector( parent )
+{
+ kdDebug( YAHOO_RAW_DEBUG ) << 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 = 5510;
+}
+
+KNetworkConnector::~KNetworkConnector()
+{
+ delete mByteStream;
+}
+
+void KNetworkConnector::connectToServer( const QString &server )
+{
+ Q_UNUSED( server );
+ kdDebug( YAHOO_RAW_DEBUG ) << k_funcinfo << "Initiating connection to " << mHost << endl;
+ Q_ASSERT( !mHost.isNull() );
+ Q_ASSERT( mPort );
+
+ 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( YAHOO_RAW_DEBUG ) << 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( YAHOO_RAW_DEBUG ) << k_funcinfo << "Error detected: " << code << endl;
+
+ mErrorCode = code;
+ emit error ();
+}
+
+int KNetworkConnector::errorCode()
+{
+ return mErrorCode;
+}
+
+ByteStream *KNetworkConnector::stream() const
+{
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << endl;
+ return mByteStream;
+}
+
+void KNetworkConnector::done()
+{
+ kdDebug ( YAHOO_RAW_DEBUG ) << k_funcinfo << endl;
+ mByteStream->close ();
+}
+
+void KNetworkConnector::setOptHostPort( const QString &host, Q_UINT16 port )
+{
+ kdDebug ( YAHOO_RAW_DEBUG ) << k_funcinfo << "Manually specifying host " << host << " and port " << port << endl;
+
+ mHost = host;
+ mPort = port;
+
+}
+
+#include "yahooconnector.moc"
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/yahoo/libkyahoo/yahooconnector.h b/kopete/protocols/yahoo/libkyahoo/yahooconnector.h
new file mode 100644
index 00000000..09070d87
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahooconnector.h
@@ -0,0 +1,67 @@
+
+/***************************************************************************
+ oscarconnector.h - Socket Connector for KNetwork
+ -------------------
+ begin : Wed Jul 7 2004
+ copyright : (C) 2004 by Till Gerken <till@tantalo.net>
+ (C) 2004 by Matt Rogers <matt.rogers@kdemail.net>
+
+ Kopete (C) 2004 Kopete developers <kopete-devel@kde.org>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU 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 YAHOOCONNECTOR_H
+#define YAHOOCONNECTOR_H
+
+#include "connector.h"
+
+class ByteStream;
+class KNetworkByteStream;
+class KResolverEntry;
+
+/**
+@author Till Gerken
+@author Matt Rogers
+*/
+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 );
+
+ int errorCode();
+
+private slots:
+ void slotConnected();
+ void slotError( int );
+
+private:
+ QString mHost;
+ Q_UINT16 mPort;
+ int mErrorCode;
+
+ KNetworkByteStream *mByteStream;
+
+};
+
+#endif
+
+// kate: indent-width 4; replace-tabs off; tab-width 4; space-indent off;
diff --git a/kopete/protocols/yahoo/libkyahoo/yahootypes.h b/kopete/protocols/yahoo/libkyahoo/yahootypes.h
new file mode 100644
index 00000000..e254bab7
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/yahootypes.h
@@ -0,0 +1,182 @@
+/*
+ yahootypes.h - Kopete Yahoo Protocol definitions
+
+ Copyright (c) 2004 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOTYPESH
+#define YAHOOTYPESH
+
+#include <qglobal.h>
+
+const int YAHOO_RAW_DEBUG = 14181;
+const int YAHOO_GEN_DEBUG = 14180;
+
+namespace Yahoo
+{
+ enum Service
+ {
+ /* these are easier to see in hex */
+ ServiceLogon = 1,
+ ServiceLogoff,
+ ServiceIsAway,
+ ServiceIsBack,
+ ServiceIdle, /* 5 (placemarker) */
+ ServiceMessage,
+ ServiceIdAct,
+ ServiceIddeAct,
+ ServiceMailStat,
+ ServiceUserStat, /* 0xa */
+ ServiceNewMail,
+ ServiceChatInvite,
+ ServiceCalendar,
+ ServiceNewPersonalMail,
+ ServiceNewContact,
+ ServiceAddIdent, /* 0x10 */
+ ServiceAddIgnore,
+ ServicePing,
+ ServiceGotGroupRename, /* < 1, 36(old), 37(new) */
+ ServiceSysMessage = 0x14,
+ ServicePassThrough2 = 0x16,
+ ServiceConfInvite = 0x18,
+ ServiceConfLogon,
+ ServiceConfDecline,
+ ServiceConfLogoff,
+ ServiceConfAddInvite,
+ ServiceConfMsg,
+ ServiceChatLogon,
+ ServiceChatLogoff,
+ ServiceChatMsg = 0x20,
+ ServiceGameLogon = 0x28,
+ ServiceGameLogoff,
+ ServiceGameMsg = 0x2a,
+ ServiceFileTransfer = 0x46,
+ ServiceVoiceChat = 0x4A,
+ ServiceNotify,
+ ServiceVerify = 76,
+ ServiceP2PFileXfer,
+ ServicePeerToPeer = 0x4F, /* Checks if P2P possible */
+ ServiceWebcam,
+ ServiceAuthResp = 0x54,
+ ServiceList = 85,
+ ServiceAuth = 0x57,
+ ServiceAddBuddy = 0x83,
+ ServiceRemBuddy,
+ ServiceIgnoreContact, /* > 1, 7, 13 < 1, 66, 13, 0*/
+ ServiceRejectContact,
+ ServiceGroupRename = 0x89, /* > 1, 65(new), 66(0), 67(old) */
+ ServicePing7 = 0x8a,
+ ServiceChatOnline = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
+ ServiceChatGoto,
+ ServiceChatJoin, /* > 1 104-room 129-1600326591 62-2 */
+ ServiceChatleave,
+ ServiceChatExit = 0x9b,
+ ServiceChatLogout = 0xa0,
+ ServiceChatPing,
+ ServiceComment = 0xa8,
+ ServiceStealthOffline = 0xb9,
+ ServiceStealthOnline = 0xba,
+ ServicePictureChecksum = 0xbd,
+ ServicePicture = 0xbe,
+ ServicePictureUpdate = 0xc1,
+ ServicePictureUpload = 0xc2,
+ ServiceVisibility = 0xc5, /* YMSG13, key 13: 2 = invisible, 1 = visible */
+ ServiceStatus = 0xc6, /* YMSG13 */
+ ServicePictureStatus = 0xc7, /* YMSG13, key 213: 0 = none, 1 = avatar, 2 = picture */
+ ServiceContactDetails = 0xd3, /* YMSG13 */
+ ServiceChatSession = 0xd4,
+ ServiceAuthorization = 0xd6, /* YMSG13 */
+ ServiceFileTransfer7 = 0xdc, /* YMSG13 */
+ ServiceFileTransfer7Info, /* YMSG13 */
+ ServiceFileTransfer7Accept, /* YMSG13 */
+ ServiceBuddyChangeGroup = 0xe7 /* YMSG13 */
+ };
+
+ enum Status
+ {
+ StatusConnecting = -2,
+ StatusDisconnected = -1,
+ StatusAvailable = 0,
+ StatusBRB = 1,
+ StatusBusy,
+ StatusNotAtHome,
+ StatusNotAtDesk,
+ StatusNotInOffice,
+ StatusOnPhone,
+ StatusOnVacation,
+ StatusOutToLunch,
+ StatusSteppedOut,
+ StatusInvisible = 12,
+ StatusCustom = 99,
+ StatusIdle = 999,
+ StatusWebLogin = 0x5a55aa55,
+ StatusOffline = 0x5a55aa56, /* don't ask */
+ StatusNotify = 0x16
+ };
+
+ enum StatusType
+ {
+ StatusTypeAvailable = 0,
+ StatusTypeAway
+ };
+
+ enum LoginStatus {
+ LoginOk = 0,
+ LoginUname = 3,
+ LoginPasswd = 13,
+ LoginLock = 14,
+ LoginVerify = 29, // FIXME: Find the reason for this response
+ LoginDupl = 99,
+ LoginSock = -1
+ };
+
+ enum StealthMode {
+ StealthOnline,
+ StealthOffline,
+ StealthPermOffline
+ };
+
+ enum StealthStatus {
+ StealthActive = 1,
+ StealthNotActive = 2,
+ StealthClear = 3
+ };
+
+ enum Response {
+ ResponseAccept,
+ ResponseDecline
+ };
+
+ typedef Q_UINT8 BYTE;
+ typedef Q_UINT16 WORD;
+ typedef Q_UINT32 DWORD;
+}
+
+#define yahoo_put16(buf, data) ( \
+ (*(buf) = (unsigned char)((data)>>8)&0xff), \
+ (*((buf)+1) = (unsigned char)(data)&0xff), \
+ 2)
+#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
+#define yahoo_put32(buf, data) ( \
+ (*((buf)) = (unsigned char)((data)>>24)&0xff), \
+ (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+ (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+ (*((buf)+3) = (unsigned char)(data)&0xff), \
+ 4)
+#define yahoo_get32(buf) ((((*(buf) )&0xff)<<24) + \
+ (((*((buf)+1))&0xff)<<16) + \
+ (((*((buf)+2))&0xff)<< 8) + \
+ (((*((buf)+3))&0xff)))
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.cpp b/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.cpp
new file mode 100644
index 00000000..79687073
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.cpp
@@ -0,0 +1,347 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <stdlib.h>
+
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <qmap.h>
+#include <qobject.h>
+#include <qstringlist.h>
+
+#include <kdebug.h>
+
+#include "ymsgprotocol.h"
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+
+using namespace Yahoo;
+
+YMSGProtocol::YMSGProtocol(QObject *parent, const char *name)
+ : InputProtocolBase(parent, name)
+{
+}
+
+YMSGProtocol::~YMSGProtocol()
+{
+}
+
+Transfer* YMSGProtocol::parse( const QByteArray & packet, uint& bytes )
+{
+ /*
+ <------- 4B -------><------- 4B -------><---2B--->
+ +-------------------+-------------------+---------+
+ | Y M S G | version | pkt_len |
+ +---------+---------+---------+---------+---------+
+ | service | status | session_id |
+ +---------+-------------------+-------------------+
+ | |
+ : D A T A :
+ / 0 - 65535* |
+ +-------------------------------------------------+
+ */
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << packet << endl;
+
+ int pos = 0;
+ int len = 0;
+
+ Yahoo::Status status = Yahoo::StatusAvailable;
+ Yahoo::Service service = Yahoo::ServiceAuth;
+ int statusnum = 0;
+ int sessionid = 0;
+ int servicenum;
+ int version1, version2;
+
+ QMap<QString, QString> params;
+
+ // Skip the YMSG header
+ pos += 4;
+
+ // Skip the version
+ version1 = yahoo_get16(packet.data() + pos);
+ pos += 2;
+ version2 = yahoo_get16(packet.data() + pos);
+ pos += 2;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - parsed packet version " << version1 << " " << version2 << endl;
+
+ len = yahoo_get16(packet.data() + pos);
+ pos += 2;
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - parsed packet len " << len << endl;
+
+ servicenum = yahoo_get16(packet.data() + pos);
+ pos += 2;
+
+ switch (servicenum)
+ {
+ // TODO add remamining services
+ case (Yahoo::ServiceAuth) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceAuth " << servicenum << endl;
+ service = Yahoo::ServiceAuth;
+ break;
+ case (Yahoo::ServiceAuthResp) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceAuthResp " << servicenum << endl;
+ service = Yahoo::ServiceAuthResp;
+ break;
+ case (Yahoo::ServiceVerify) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceVerify " << servicenum << endl;
+ service = Yahoo::ServiceVerify;
+ break;
+ case (Yahoo::ServiceList) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceList " << servicenum << endl;
+ service = Yahoo::ServiceList;
+ break;
+ case (Yahoo::ServiceLogon) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceLogon " << servicenum << endl;
+ service = Yahoo::ServiceLogon;
+ break;
+ case (Yahoo::ServicePing) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePing " << servicenum << endl;
+ service = Yahoo::ServicePing;
+ break;
+ case (Yahoo::ServiceNewMail) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceNewMail " << servicenum << endl;
+ service = Yahoo::ServiceNewMail;
+ break;
+ case (Yahoo::ServiceLogoff) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceLogoff " << servicenum << endl;
+ service = Yahoo::ServiceLogoff;
+ break;
+ case (Yahoo::ServiceIsAway) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceIsAway " << servicenum << endl;
+ service = Yahoo::ServiceIsAway;
+ break;
+ case (Yahoo::ServiceIsBack) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceIsBack " << servicenum << endl;
+ service = Yahoo::ServiceIsBack;
+ break;
+ case (Yahoo::ServiceGameLogon) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceGameLogon " << servicenum << endl;
+ service = Yahoo::ServiceGameLogon;
+ break;
+ case (Yahoo::ServiceGameLogoff) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceGameLogoff " << servicenum << endl;
+ service = Yahoo::ServiceGameLogoff;
+ break;
+ case (Yahoo::ServiceIdAct) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceIdAct " << servicenum << endl;
+ service = Yahoo::ServiceIdAct;
+ break;
+ case (Yahoo::ServiceIddeAct) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceIddeAct " << servicenum << endl;
+ service = Yahoo::ServiceIddeAct;
+ break;
+ case (Yahoo::ServiceStatus) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceStatus " << servicenum << endl;
+ service = Yahoo::ServiceStatus;
+ break;
+ case (Yahoo::ServiceMessage) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceMessage " << servicenum << endl;
+ service = Yahoo::ServiceMessage;
+ break;
+ case (Yahoo::ServiceNotify) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceNotify " << servicenum << endl;
+ service = Yahoo::ServiceNotify;
+ break;
+ case (Yahoo::ServiceAddBuddy) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceAddBuddy " << servicenum << endl;
+ service = Yahoo::ServiceAddBuddy;
+ break;
+ case (Yahoo::ServicePictureChecksum) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePictureChecksum " << servicenum << endl;
+ service = Yahoo::ServicePictureChecksum;
+ break;
+ case (Yahoo::ServicePictureStatus) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePictureStatus " << servicenum << endl;
+ service = Yahoo::ServicePictureStatus;
+ break;
+ case (Yahoo::ServicePicture) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePicture " << servicenum << endl;
+ service = Yahoo::ServicePicture;
+ break;
+ case (Yahoo::ServiceStealthOnline) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceStealthOnline " << servicenum << endl;
+ service = Yahoo::ServiceStealthOnline;
+ break;
+ case (Yahoo::ServiceStealthOffline) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceStealthOffline " << servicenum << endl;
+ service = Yahoo::ServiceStealthOffline;
+ break;
+ case (Yahoo::ServicePictureUpload) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePictureUpload " << servicenum << endl;
+ service = Yahoo::ServicePictureUpload;
+ break;
+ case (Yahoo::ServiceWebcam) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceWebcam " << servicenum << endl;
+ service = Yahoo::ServiceWebcam;
+ break;
+ case (Yahoo::ServiceConfInvite) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfInvite " << servicenum << endl;
+ service = Yahoo::ServiceConfInvite;
+ break;
+ case (Yahoo::ServiceConfLogon) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfLogon " << servicenum << endl;
+ service = Yahoo::ServiceConfLogon;
+ break;
+ case (Yahoo::ServiceConfDecline) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfDecline " << servicenum << endl;
+ service = Yahoo::ServiceConfDecline;
+ break;
+ case (Yahoo::ServiceConfLogoff) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfLogoff " << servicenum << endl;
+ service = Yahoo::ServiceConfLogoff;
+ break;
+ case (Yahoo::ServiceConfAddInvite) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfAddInvite " << servicenum << endl;
+ service = Yahoo::ServiceConfAddInvite;
+ break;
+ case (Yahoo::ServiceConfMsg) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceConfMsg " << servicenum << endl;
+ service = Yahoo::ServiceConfMsg;
+ break;
+ case (Yahoo::ServiceAuthorization) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceAuthorization " << servicenum << endl;
+ service = Yahoo::ServiceAuthorization;
+ break;
+ case (Yahoo::ServiceContactDetails) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceContactDetails " << servicenum << endl;
+ service = Yahoo::ServiceContactDetails;
+ break;
+ case (Yahoo::ServiceFileTransfer) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceFileTransfer " << servicenum << endl;
+ service = Yahoo::ServiceFileTransfer;
+ break;
+ case (Yahoo::ServiceFileTransfer7) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceFileTransfer7 " << servicenum << endl;
+ service = Yahoo::ServiceFileTransfer7;
+ break;
+ case (Yahoo::ServiceFileTransfer7Info) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServiceFileTransfer7Info " << servicenum << endl;
+ service = Yahoo::ServiceFileTransfer7Info;
+ break;
+ case (Yahoo::ServicePeerToPeer) :
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means ServicePeerToPeer " << servicenum << endl;
+ service = Yahoo::ServicePeerToPeer;
+ break;
+ /*
+ ServiceIdle, // 5 (placemarker)
+ ServiceMailStat,
+ ServiceUserStat, // 0xa
+ ServiceChatInvite,
+ ServiceCalendar,
+ ServiceNewPersonalMail,
+ ServiceNewContact,
+ ServiceAddIdent, // 0x10
+ ServiceAddIgnore,
+ ServiceGotGroupRename, // < 1, 36(old), 37(new)
+ ServiceSysMessage = 0x14,
+ ServicePassThrough2 = 0x16,
+ ServiceChatLogon,
+ ServiceChatLogoff,
+ ServiceChatMsg = 0x20,
+ ServiceGameMsg = 0x2a,
+ ServiceFileTransfer = 0x46,
+ ServiceVoiceChat = 0x4A,
+ ServiceVerify = 76,
+ ServiceP2PFileXfer,
+ ServiceRemBuddy,
+ ServiceIgnoreContact, // > 1, 7, 13 < 1, 66, 13, 0
+ ServiceRejectContact,
+ ServiceGroupRename = 0x89, // > 1, 65(new), 66(0), 67(old)
+ ServiceChatOnline = 0x96, // > 109(id), 1, 6(abcde) < 0,1
+ ServiceChatGoto,
+ ServiceChatJoin, // > 1 104-room 129-1600326591 62-2
+ ServiceChatleave,
+ ServiceChatExit = 0x9b,
+ ServiceChatLogout = 0xa0,
+ ServiceChatPing,
+ ServiceComment = 0xa8
+ ServicePictureUpdate = 0xc1,
+ ServiceVisibility = 0xc5, // YMSG13, key 13: 2 = invisible, 1 = visible
+ ServiceStatus = 0xc6, // YMSG13
+ */
+
+ default:
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed packet service - This means an unknown service " << servicenum << endl;
+ break;
+ }
+
+ statusnum = yahoo_get32(packet.data() + pos);
+ pos += 4;
+
+ switch (statusnum)
+ {
+ // TODO add remaining status
+ case (Yahoo::StatusAvailable) :
+ status = Yahoo::StatusAvailable;
+ break;
+ case (Yahoo::StatusBRB) :
+ status = Yahoo::StatusBRB;
+ break;
+ case (Yahoo::StatusDisconnected) :
+ status = Yahoo::StatusDisconnected;
+ break;
+ /*StatusBusy
+ StatusNotAtHome
+ StatusNotAtDesk
+ StatusNotInOffice
+ StatusOnPhone
+ StatusOnVacation
+ StatusOutToLunch
+ StatusSteppedOut
+ StatusInvisible
+ StatusCustom
+ StatusIdle
+ StatusOffline
+ StatusNotify*/
+ default:
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " - unknown status " << statusnum << endl;
+ break;
+ }
+
+ sessionid = yahoo_get32(packet.data() + pos);
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Parsed session id: " << (void *)sessionid << endl;
+ pos += 4;
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Setting incoming transfer basic information." << endl;
+ YMSGTransfer *t = new YMSGTransfer();
+ t->setService(service);
+ t->setId(sessionid);
+ t->setStatus(status);
+
+ QString d = QString::fromAscii( packet.data() + pos, packet.size() - pos );
+ QStringList list;
+ list = QStringList::split( "\xc0\x80", d );
+ for( uint i = 0; i+1 < list.size() && pos+1 < len+20; i += 2 ) {
+ QString key = list[i];
+ QString value = QString::fromUtf8( list[i+1].ascii() );
+ pos += key.utf8().length() + value.utf8().length() + 4;
+ t->setParam( QString(key).toInt(), value.utf8() );
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Key: " << key << " Value: " << value << endl;
+ }
+
+ while( (uint)pos < packet.size() && packet.data()[pos] == '\x00' )
+ pos++;
+
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Returning transfer" << endl;
+ // tell them we have parsed offset bytes
+
+ bytes = pos;
+ return t;
+}
+
+#include "ymsgprotocol.moc"
diff --git a/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.h b/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.h
new file mode 100644
index 00000000..97de7477
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/ymsgprotocol.h
@@ -0,0 +1,44 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2004 Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOO_YMSGPROTOCOL_H
+#define YAHOO_YMSGPROTOCOL_H
+
+#include "inputprotocolbase.h"
+
+
+class YMSGProtocol : public InputProtocolBase
+{
+Q_OBJECT
+public:
+
+
+ YMSGProtocol( QObject *parent = 0, const char *name = 0 );
+ ~YMSGProtocol();
+
+ /**
+ * Attempt to parse the supplied data into an @ref YMSGTransfer 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 );
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.cpp b/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.cpp
new file mode 100644
index 00000000..f47a07d1
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.cpp
@@ -0,0 +1,239 @@
+/*
+ Kopete Yahoo Protocol
+ Handles logging into to the Yahoo service
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <string>
+
+#include "ymsgtransfer.h"
+#include "yahootypes.h"
+#include "kdebug.h"
+#include <qdatastream.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+
+using namespace Yahoo;
+
+class YMSGTransferPrivate
+{
+public:
+ int yflag;
+ int version;
+ int packetLength;
+ Yahoo::Service service;
+ Yahoo::Status status;
+ unsigned int id;
+ ParamList data;
+ bool valid;
+};
+
+YMSGTransfer::YMSGTransfer()
+{
+ d = new YMSGTransferPrivate;
+ d->valid = true;
+ d->id = 0;
+ d-> status = Yahoo::StatusAvailable;
+}
+
+YMSGTransfer::YMSGTransfer(Yahoo::Service service)
+{
+ d = new YMSGTransferPrivate;
+ d->valid = true;
+ d->service = service;
+ d->id = 0;
+ d->status = Yahoo::StatusAvailable;
+}
+
+YMSGTransfer::YMSGTransfer(Yahoo::Service service, Yahoo::Status status)
+{
+ d = new YMSGTransferPrivate;
+ d->valid = true;
+ d->service = service;
+ d->id = 0;
+ d->status = status;
+}
+
+YMSGTransfer::~YMSGTransfer()
+{
+ delete d;
+}
+
+Transfer::TransferType YMSGTransfer::type()
+{
+ return Transfer::YMSGTransfer;
+}
+
+bool YMSGTransfer::isValid()
+{
+ return d->valid;
+}
+
+Yahoo::Service YMSGTransfer::service()
+{
+ return d->service;
+}
+
+void YMSGTransfer::setService(Yahoo::Service service)
+{
+ d->service = service;
+}
+
+Yahoo::Status YMSGTransfer::status()
+{
+ return d->status;
+}
+
+void YMSGTransfer::setStatus(Yahoo::Status status)
+{
+ d->status = status;
+}
+
+unsigned int YMSGTransfer::id()
+{
+ return d->id;
+}
+
+void YMSGTransfer::setId(unsigned int id)
+{
+ d->id = id;
+}
+
+ParamList YMSGTransfer::paramList()
+{
+ return d->data;
+}
+
+int YMSGTransfer::paramCount( int index )
+{
+ int cnt = 0;
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ if( (*it).first == index )
+ cnt++;
+ }
+ return cnt;
+}
+
+
+QCString YMSGTransfer::nthParam( int index, int occurence )
+{
+ int cnt = 0;
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ if( (*it).first == index && cnt++ == occurence)
+ return (*it).second;
+ }
+ return QCString();
+}
+
+QCString YMSGTransfer::nthParamSeparated( int index, int occurence, int separator )
+{
+
+ int cnt = -1;
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ if( (*it).first == separator )
+ cnt++;
+ if( (*it).first == index && cnt == occurence)
+ return (*it).second;
+ }
+ return QCString();
+}
+
+QCString YMSGTransfer::firstParam( int index )
+{
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ if( (*it).first == index )
+ return (*it).second;
+ }
+ return QCString();
+}
+
+void YMSGTransfer::setParam(int index, const QCString &data)
+{
+ d->data.append( Param( index, data ) );
+}
+
+void YMSGTransfer::setParam( int index, int data )
+{
+ d->data.append( Param( index, QString::number( data ).local8Bit() ) );
+}
+
+int YMSGTransfer::length()
+{
+ int len = 0;
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ len += QString::number( (*it).first ).length();
+ len += 2;
+ len += (*it).second.length();
+ len += 2;
+ }
+ return len;
+}
+
+
+QByteArray YMSGTransfer::serialize()
+{
+ /*
+ <------- 4B -------><------- 4B -------><---2B--->
+ +-------------------+-------------------+---------+
+ | Y M S G | version | pkt_len |
+ +---------+---------+---------+---------+---------+
+ | service | status | session_id |
+ +---------+-------------------+-------------------+
+ | |
+ : D A T A :
+ / 0 - 65535* |
+ +-------------------------------------------------+
+ */
+
+ int pos = 0;
+ QStringList::ConstIterator listIt = 0;
+ QByteArray buffer;
+ QDataStream stream( buffer, IO_WriteOnly );
+
+ stream << (Q_INT8)'Y' << (Q_INT8)'M' << (Q_INT8)'S' << (Q_INT8)'G';
+ if( d->service == Yahoo::ServicePictureUpload )
+ stream << (Q_INT16)0x0e00;
+ else
+ stream << (Q_INT16)0x000e;
+ stream << (Q_INT16)0x0000;
+ if( d->service == Yahoo::ServicePictureUpload ||
+ d->service == Yahoo::ServiceFileTransfer )
+ stream << (Q_INT16)(length()+4);
+ else
+ stream << (Q_INT16)length();
+ stream << (Q_INT16)d->service;
+ stream << (Q_INT32)d->status;
+ stream << (Q_INT32)d->id;
+ for (ParamList::ConstIterator it = d->data.begin(); it != d->data.end(); ++it)
+ {
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " Serializing key " << (*it).first << " value " << (*it).second << endl;
+ stream.writeRawBytes ( QString::number( (*it).first ).local8Bit(), QString::number( (*it).first ).length() );
+ stream << (Q_INT8)0xc0 << (Q_INT8)0x80;
+ stream.writeRawBytes( (*it).second, (*it).second.length() );
+ stream << (Q_INT8)0xc0 << (Q_INT8)0x80;
+ }
+ kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << " pos=" << pos << " (packet size)" << buffer << endl;
+ return buffer;
+}
+
diff --git a/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.h b/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.h
new file mode 100644
index 00000000..79655766
--- /dev/null
+++ b/kopete/protocols/yahoo/libkyahoo/ymsgtransfer.h
@@ -0,0 +1,76 @@
+/*
+ Kopete Yahoo Protocol
+ Handles logging into to the Yahoo service
+
+ Copyright (c) 2004 Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Copyright (c) 2005 André Duffeck <andre.duffeck@kdemail.net>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YMSG_TRANSFER_H
+#define YMSG_TRANSFER_H
+
+#include "transfer.h"
+
+#include "yahootypes.h"
+#include <qcstring.h>
+#include <qpair.h>
+#include <qvaluelist.h>
+
+class YMSGTransferPrivate;
+class QString;
+
+typedef QPair< int, QCString > Param;
+typedef QValueList< Param > ParamList;
+
+/**
+@author Duncan Mac-Vicar Prett
+*/
+class YMSGTransfer : public Transfer
+{
+public:
+ YMSGTransfer(Yahoo::Service service);
+ YMSGTransfer(Yahoo::Service service, Yahoo::Status status);
+ YMSGTransfer();
+ ~YMSGTransfer();
+
+
+ TransferType type();
+
+ //! Get the validity of the transfer object
+ bool isValid();
+ Yahoo::Service service();
+ void setService(Yahoo::Service service);
+ Yahoo::Status status();
+ void setStatus(Yahoo::Status status);
+ unsigned int id();
+ void setId(unsigned int id);
+
+ ParamList paramList();
+ QCString firstParam( int index );
+ QCString nthParam( int index, int occurence );
+ QCString nthParamSeparated( int index, int occurence, int separator );
+ int paramCount( int index );
+
+
+ void setParam(int index, const QCString &data);
+ void setParam(int index, int data);
+ QByteArray serialize();
+
+ int length();
+private:
+ YMSGTransferPrivate* d;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/ui/Makefile.am b/kopete/protocols/yahoo/ui/Makefile.am
new file mode 100644
index 00000000..8d6a673e
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/Makefile.am
@@ -0,0 +1,14 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkopeteyahooui.la
+AM_CPPFLAGS = $(KOPETE_INCLUDES) \
+ -I$(srcdir)/.. \
+ $(all_includes)
+
+
+libkopeteyahooui_la_SOURCES = yahooadd.ui yahooeditaccountbase.ui \
+ yahooinvitelistbase.ui yahooinvitelistimpl.cpp empty.cpp yahooverifyaccountbase.ui \
+ yahoostealthsetting.ui yahoowebcamdialog.cpp yahoogeneralinfowidget.ui yahoouserinfodialog.cpp \
+ yahooworkinfowidget.ui yahoootherinfowidget.ui
+EXTRA_DIST = dlgrename.ui
+noinst_HEADERS = yahoouserinfodialog.h
diff --git a/kopete/protocols/yahoo/ui/empty.cpp b/kopete/protocols/yahoo/ui/empty.cpp
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/empty.cpp
@@ -0,0 +1 @@
+
diff --git a/kopete/protocols/yahoo/ui/yahooadd.ui b/kopete/protocols/yahoo/ui/yahooadd.ui
new file mode 100644
index 00000000..ff3ef8f6
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooadd.ui
@@ -0,0 +1,97 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>YahooAddContactBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>396</width>
+ <height>347</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Add Yahoo Contact</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout53</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;Yahoo username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>contactID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of the Yahoo account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of the Yahoo account you would like to add. This should be in the form of an alphanumeric string (no spaces).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>contactID</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of the Yahoo account you would like to add.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of the Yahoo account you would like to add. This should be in the form of an alphanumeric string (no spaces).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;i&gt;(for example: joe8752)&lt;/i&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </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>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahooeditaccountbase.ui b/kopete/protocols/yahoo/ui/yahooeditaccountbase.ui
new file mode 100644
index 00000000..4b98f8be
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooeditaccountbase.ui
@@ -0,0 +1,467 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooEditAccountBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooEditAccountBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>462</width>
+ <height>344</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Account Preferences - Yahoo</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>
+ <property name="minimumSize">
+ <size>
+ <width>460</width>
+ <height>0</height>
+ </size>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Basic Setup</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>mAccountInfo</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>layout81</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>label1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Yahoo username:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>mScreenName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of your Yahoo account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of your Yahoo account. This should be in the form of an alphanumeric string (no spaces).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mScreenName</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The account name of your Yahoo account.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The account name of your Yahoo account. This should be in the form of an alphanumeric string (no spaces).</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>mAutoConnect</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="QCheckBox">
+ <property name="name">
+ <cstring>mGlobalIdentity</cstring>
+ </property>
+ <property name="text">
+ <string>Exclude from &amp;Global Identity</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Registration</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>To connect to the Yahoo network, you will need a Yahoo account.&lt;br&gt;&lt;br&gt;If you do not currently have a Yahoo account, please click the button to create one.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonRegister</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Register &amp;New Account</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Register a new account on this network.</string>
+ </property>
+ </widget>
+ </hbox>
+ </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>81</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>TabPage</cstring>
+ </property>
+ <attribute name="title">
+ <string>Accoun&amp;t Preferences</string>
+ </attribute>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="2" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>110</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox73</cstring>
+ </property>
+ <property name="title">
+ <string>Connection Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>optionOverrideServer</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;verride default server information</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout58</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblServer</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ser&amp;ver:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>edtServerAddress</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the Yahoo server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the Yahoo server you wish to connect to. Normally you will want the default (scs.msg.yahoo.com).</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>editServerAddress</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>scs.msg.yahoo.com</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The IP address or hostmask of the Yahoo server you wish to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The IP address or hostmask of the Yahoo server you wish to connect to. Normally you will want the default (scs.msg.yahoo.com).</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lblPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>P&amp;ort:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>sbxServerPort</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the Yahoo server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the Yahoo server that you would like to connect to. Normally this is 5050, but Yahoo also allows port 80 in case you are behind a firewall.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>sbxServerPort</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>65534</number>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>5050</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The port on the Yahoo server that you would like to connect to.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>The port on the Yahoo server that you would like to connect to. Normally this is 5050, but Yahoo also allows port 80 in case you are behind a firewall.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Buddy Icon</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="1" column="0">
+ <property name="name">
+ <cstring>editPictureUrl</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>buttonSelectPicture</cstring>
+ </property>
+ <property name="text">
+ <string>Select Picture...</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>m_Picture</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>96</width>
+ <height>96</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>optionSendBuddyIcon</cstring>
+ </property>
+ <property name="text">
+ <string>Se&amp;nd buddy icon to other users</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+ </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>
+<connections>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lblServer</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editServerAddress</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionOverrideServer</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>sbxServerPort</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>optionSendBuddyIcon</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>editPictureUrl</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>tabWidget11</tabstop>
+ <tabstop>mScreenName</tabstop>
+ <tabstop>mAutoConnect</tabstop>
+ <tabstop>buttonRegister</tabstop>
+</tabstops>
+<slots>
+ <slot access="private" specifier="nicht virtual">slotSelectPicture()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahoogeneralinfowidget.ui b/kopete/protocols/yahoo/ui/yahoogeneralinfowidget.ui
new file mode 100644
index 00000000..b74dc94b
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoogeneralinfowidget.ui
@@ -0,0 +1,647 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooGeneralInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooGeneralInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>596</width>
+ <height>506</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Personal Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>fullNameLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>First name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fullNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>fullNameLabel_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Second name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fullNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>LastNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Last name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>fullNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>lastNameEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>nickNameEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>2</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>nickNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nickname:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickNameEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>firstNameEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>secondNameEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>yahooIdLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Yahoo ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>uinEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>yahooIdLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>Title:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>uinEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>birthdayLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>Anniversary:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>birthday</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>yahooIdEdit</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>titleEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>birthdayEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="3">
+ <property name="name">
+ <cstring>anniversaryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>birthdayLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Birthday:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>birthday</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="3" column="0">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QGroupBox" row="2" column="0">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Contact Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel6_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pager:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel10_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Homepage:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>homepageEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>emailEdit_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Email:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel9_3</cstring>
+ </property>
+ <property name="text">
+ <string>Email &amp;3:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel9_2</cstring>
+ </property>
+ <property name="text">
+ <string>Email &amp;2:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>emailEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>emailEdit_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>homepageEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>emailEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>faxEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel7_2</cstring>
+ </property>
+ <property name="text">
+ <string>Fa&amp;x:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>faxEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel6_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Additional:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="3">
+ <property name="name">
+ <cstring>pagerEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>additionalEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Phone:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>phoneEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>cellEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>textLabel6_2</cstring>
+ </property>
+ <property name="text">
+ <string>Ce&amp;ll:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cellEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>phoneEdit</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Location Information</string>
+ </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;Address:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>addressEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Countr&amp;y:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>countryEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QTextEdit" row="0" column="1" rowspan="2" colspan="3">
+ <property name="name">
+ <cstring>addressEdit</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <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>78</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;State:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>stateEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>stateEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;City:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cityEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="3">
+ <property name="name">
+ <cstring>cityEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="3">
+ <property name="name">
+ <cstring>countryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Zip:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>zipEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>zipEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>firstNameEdit</tabstop>
+ <tabstop>secondNameEdit</tabstop>
+ <tabstop>lastNameEdit</tabstop>
+ <tabstop>nickNameEdit</tabstop>
+ <tabstop>titleEdit</tabstop>
+ <tabstop>birthdayEdit</tabstop>
+ <tabstop>anniversaryEdit</tabstop>
+ <tabstop>addressEdit</tabstop>
+ <tabstop>zipEdit</tabstop>
+ <tabstop>cityEdit</tabstop>
+ <tabstop>stateEdit</tabstop>
+ <tabstop>countryEdit</tabstop>
+ <tabstop>phoneEdit</tabstop>
+ <tabstop>faxEdit</tabstop>
+ <tabstop>additionalEdit</tabstop>
+ <tabstop>cellEdit</tabstop>
+ <tabstop>pagerEdit</tabstop>
+ <tabstop>emailEdit</tabstop>
+ <tabstop>emailEdit_2</tabstop>
+ <tabstop>emailEdit_3</tabstop>
+ <tabstop>homepageEdit</tabstop>
+ <tabstop>yahooIdEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahooinvitelistbase.ui b/kopete/protocols/yahoo/ui/yahooinvitelistbase.ui
new file mode 100644
index 00000000..09a3cd15
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooinvitelistbase.ui
@@ -0,0 +1,337 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooInviteListBase</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>YahooInviteListBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>529</width>
+ <height>418</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Invite Friends to Conference</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout19</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Conference Members</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Friend List</string>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <item>
+ <property name="text">
+ <string>New Item</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>listFriends</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>180</height>
+ </size>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="2">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Chat Invitation List</string>
+ </property>
+ </widget>
+ <widget class="QListBox">
+ <item>
+ <property name="text">
+ <string>New Item</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>listInvited</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>150</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>editBuddyAdd</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnCustomAdd</cstring>
+ </property>
+ <property name="text">
+ <string>Add</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="1">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btn_Add</cstring>
+ </property>
+ <property name="text">
+ <string>Add &gt;&gt;</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btn_Remove</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;&lt; 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>90</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </grid>
+ </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>txtInvMsg</cstring>
+ </property>
+ <property name="text">
+ <string>Invitation Message</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>editMessage</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout18</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnCancel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Maximum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>350</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>btnInvite</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Invite</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>btnCancel</sender>
+ <signal>clicked()</signal>
+ <receiver>YahooInviteListBase</receiver>
+ <slot>btnCancel_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnCustomAdd</sender>
+ <signal>clicked()</signal>
+ <receiver>YahooInviteListBase</receiver>
+ <slot>btnAddCustom_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btnInvite</sender>
+ <signal>clicked()</signal>
+ <receiver>YahooInviteListBase</receiver>
+ <slot>btnInvite_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btn_Add</sender>
+ <signal>clicked()</signal>
+ <receiver>YahooInviteListBase</receiver>
+ <slot>btnAdd_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>btn_Remove</sender>
+ <signal>clicked()</signal>
+ <receiver>YahooInviteListBase</receiver>
+ <slot>btnRemove_clicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>btnAdd_clicked()</slot>
+ <slot>btnRemove_clicked()</slot>
+ <slot>btnAddCustom_clicked()</slot>
+ <slot>btnCancel_clicked()</slot>
+ <slot>btnInvite_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahooinvitelistimpl.cpp b/kopete/protocols/yahoo/ui/yahooinvitelistimpl.cpp
new file mode 100644
index 00000000..dcd6e184
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooinvitelistimpl.cpp
@@ -0,0 +1,165 @@
+/*
+ YahooInviteListImpl - conference invitation dialog
+
+ Copyright (c) 2004 by Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "yahooinvitelistimpl.h"
+
+#include <kdebug.h>
+
+#include <qlistbox.h>
+#include <qlineedit.h>
+
+YahooInviteListImpl::YahooInviteListImpl(QWidget *parent, const char *name) : YahooInviteListBase(parent,name)
+{
+ listFriends->setSelectionMode( QListBox::Extended );
+ listInvited->setSelectionMode( QListBox::Extended );
+}
+
+YahooInviteListImpl::~YahooInviteListImpl()
+{
+}
+
+void YahooInviteListImpl::setRoom( const QString &room )
+{
+ kdDebug(14180) << k_funcinfo << "Setting roomname to: " << room << endl;
+
+ m_room = room;
+}
+
+void YahooInviteListImpl::fillFriendList( const QStringList &buddies )
+{
+ kdDebug(14180) << k_funcinfo << "Adding friends: " << buddies << endl;
+
+ m_buddyList = buddies;
+ updateListBoxes();
+}
+
+void YahooInviteListImpl::updateListBoxes()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ listFriends->clear();
+ listInvited->clear();
+ listFriends->insertStringList( m_buddyList );
+ listFriends->sort();
+ listInvited->insertStringList( m_inviteeList );
+ listInvited->sort();
+}
+
+void YahooInviteListImpl::addInvitees( const QStringList &invitees )
+{
+ kdDebug(14180) << k_funcinfo << "Adding invitees: " << invitees << endl;
+
+ for( QStringList::const_iterator it = invitees.begin(); it != invitees.end(); it++ )
+ {
+ if( m_inviteeList.find( *it ) == m_inviteeList.end() )
+ m_inviteeList.push_back( *it );
+ if( m_buddyList.find( *it ) != m_buddyList.end() )
+ m_buddyList.remove( *it );
+ }
+
+ updateListBoxes();
+}
+
+void YahooInviteListImpl::removeInvitees( const QStringList &invitees )
+{
+ kdDebug(14180) << k_funcinfo << "Removing invitees: " << invitees << endl;
+
+ for( QStringList::const_iterator it = invitees.begin(); it != invitees.end(); it++ )
+ {
+ if( m_buddyList.find( *it ) == m_buddyList.end() )
+ m_buddyList.push_back( *it );
+ if( m_inviteeList.find( *it ) != m_inviteeList.end() )
+ m_inviteeList.remove( *it );
+ }
+
+ updateListBoxes();
+}
+
+void YahooInviteListImpl::addParticipant( const QString &p )
+{
+ m_participants.push_back( p );
+}
+
+void YahooInviteListImpl::btnInvite_clicked()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ if( m_inviteeList.count() )
+ emit readyToInvite( m_room, m_inviteeList,m_participants, editMessage->text() );
+ QDialog::accept();
+}
+
+
+void YahooInviteListImpl::btnCancel_clicked()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ QDialog::reject();
+}
+
+
+void YahooInviteListImpl::btnAddCustom_clicked()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ QString userId;
+ userId = editBuddyAdd->text();
+ if( userId.isEmpty() )
+ return;
+
+ addInvitees( QStringList(userId) );
+ editBuddyAdd->clear();
+}
+
+
+void YahooInviteListImpl::btnRemove_clicked()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ QStringList buddies;
+ for( uint i=0; i<listInvited->count(); i++ )
+ {
+ if (listInvited->isSelected(i))
+ {
+ buddies.push_back( listInvited->text(i) );
+ }
+ }
+ removeInvitees( buddies );
+}
+
+
+void YahooInviteListImpl::btnAdd_clicked()
+{
+ kdDebug(14180) << k_funcinfo << endl;
+
+ QStringList buddies;
+ for( uint i=0; i<listFriends->count(); i++ )
+ {
+ if (listFriends->isSelected(i))
+ {
+ buddies.push_back( listFriends->text(i) );
+ }
+ }
+ addInvitees( buddies );
+}
+
+
+#include "yahooinvitelistimpl.moc"
+
+
+
+
diff --git a/kopete/protocols/yahoo/ui/yahooinvitelistimpl.h b/kopete/protocols/yahoo/ui/yahooinvitelistimpl.h
new file mode 100644
index 00000000..76577f36
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooinvitelistimpl.h
@@ -0,0 +1,59 @@
+/*
+ YahooInviteListImpl - conference invitation dialog
+
+ Copyright (c) 2004 by Duncan Mac-Vicar P. <duncan@kde.org>
+
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOO_INVITE_LIST_IMPL
+#define YAHOO_INVITE_LIST_IMPL
+
+#include <qwidget.h>
+
+#include "yahooinvitelistbase.h"
+
+class YahooInviteListImpl : public YahooInviteListBase
+{
+ Q_OBJECT
+public:
+ YahooInviteListImpl(QWidget *parent=0, const char *name=0);
+ ~YahooInviteListImpl();
+
+ void fillFriendList( const QStringList &buddies );
+ void addInvitees( const QStringList &buddies );
+ void removeInvitees( const QStringList &buddies );
+ void setRoom( const QString &room );
+ void addParticipant( const QString &participant );
+private:
+
+signals:
+ void readyToInvite( const QString &room, const QStringList &buddies, const QStringList &participants, const QString &msg );
+protected slots:
+
+public slots:
+ virtual void btnInvite_clicked();
+ virtual void btnCancel_clicked();
+ virtual void btnAddCustom_clicked();
+ virtual void btnRemove_clicked();
+ virtual void btnAdd_clicked();
+private:
+ void updateListBoxes();
+
+ QStringList m_buddyList;
+ QStringList m_inviteeList;
+ QStringList m_participants;
+ QString m_room;
+};
+
+#endif
+
diff --git a/kopete/protocols/yahoo/ui/yahoootherinfowidget.ui b/kopete/protocols/yahoo/ui/yahoootherinfowidget.ui
new file mode 100644
index 00000000..db2e4a8f
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoootherinfowidget.ui
@@ -0,0 +1,119 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooOtherInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooOtherInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>289</width>
+ <height>439</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>textLabel13</cstring>
+ </property>
+ <property name="text">
+ <string>Contact comments:</string>
+ </property>
+ </widget>
+ <widget class="QTextEdit" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>commentsEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Note 1:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>note1Edit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Note 2:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>note2Edit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>note3Edit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Note 3:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>note4Edit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Note 4:</string>
+ </property>
+ </widget>
+ <spacer row="7" column="1">
+ <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>130</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahoostealthsetting.ui b/kopete/protocols/yahoo/ui/yahoostealthsetting.ui
new file mode 100644
index 00000000..6c9a6fc0
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoostealthsetting.ui
@@ -0,0 +1,96 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooStealthSetting</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooStealthSetting</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>195</width>
+ <height>114</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>195</width>
+ <height>75</height>
+ </size>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Show Me As</string>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioPermOffline</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>60</y>
+ <width>151</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Perma&amp;nently offline</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioOnline</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>20</y>
+ <width>151</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>&amp;Online</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>radioOffline</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>40</y>
+ <width>151</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Off&amp;line</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </widget>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>radioOnline</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp b/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp
new file mode 100644
index 00000000..28a8532d
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoouserinfodialog.cpp
@@ -0,0 +1,260 @@
+/*
+ Kopete Yahoo Protocol
+ yahoouserinfodialog.h - Display Yahoo user info
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+ Copyright (c) 2006 Andre Duffeck <andre@duffeck.de>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "yahoouserinfodialog.h"
+
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qcombobox.h>
+#include <qtextedit.h>
+#include <qobject.h>
+#include <qtextcodec.h>
+
+#include <kdatewidget.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kjanuswidget.h>
+#include <klocale.h>
+
+#include "yahooworkinfowidget.h"
+#include "yahoogeneralinfowidget.h"
+#include "yahoootherinfowidget.h"
+#include "yahoocontact.h"
+
+YahooUserInfoDialog::YahooUserInfoDialog( YahooContact *c, QWidget * parent, const char * name )
+: KDialogBase( KDialogBase::IconList, 0, parent, name, false, i18n( "Yahoo User Information" ), User2|User1|Cancel, Cancel, false, i18n("Save and Close"), i18n("Merge with existing entry") )
+{
+ kdDebug(14180) << k_funcinfo << "Creating new yahoo user info widget" << endl;
+ m_contact = c;
+ showButton( User2, false );
+ QFrame* genInfo = addPage( i18n( "General Info" ),
+ i18n( "General Yahoo Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "identity" ), KIcon::Desktop ) );
+ QVBoxLayout* genLayout = new QVBoxLayout( genInfo );
+ m_genInfoWidget = new YahooGeneralInfoWidget( genInfo, "Basic Information" );
+ genLayout->addWidget( m_genInfoWidget );
+
+ QFrame* workInfo = addPage( i18n( "Work Info" ),
+ i18n( "Work Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "attach" ), KIcon::Desktop ) );
+ QVBoxLayout* workLayout = new QVBoxLayout( workInfo );
+ m_workInfoWidget = new YahooWorkInfoWidget( workInfo, "Work Information" );
+ workLayout->addWidget( m_workInfoWidget );
+
+ QFrame* otherInfo = addPage( i18n( "Other Info" ),
+ i18n( "Other Yahoo Information" ),
+ KGlobal::iconLoader()->loadIcon( QString::fromLatin1( "email" ), KIcon::Desktop ) );
+ QVBoxLayout* otherLayout = new QVBoxLayout( otherInfo );
+ m_otherInfoWidget = new YahooOtherInfoWidget( otherInfo, "Other Information" );
+ otherLayout->addWidget( m_otherInfoWidget );
+
+ QObject::connect(this, SIGNAL(user1Clicked()), this, SLOT(slotSaveAndCloseClicked()));
+}
+
+void YahooUserInfoDialog::setAccountConnected( bool isOnline )
+{
+ enableButton( User1, isOnline );
+ enableButton( User2, isOnline );
+}
+
+void YahooUserInfoDialog::slotSaveAndCloseClicked()
+{
+ YABEntry entry;
+ entry.yahooId = m_yab.yahooId;
+ entry.YABId = m_yab.YABId;
+ entry.firstName = m_genInfoWidget->firstNameEdit->text();
+ entry.secondName = m_genInfoWidget->secondNameEdit->text();
+ entry.lastName = m_genInfoWidget->lastNameEdit->text();
+ entry.nickName = m_genInfoWidget->nickNameEdit->text();
+ entry.email = m_genInfoWidget->emailEdit->text();
+ entry.privatePhone = m_genInfoWidget->phoneEdit->text();
+ entry.workPhone = m_workInfoWidget->phoneEdit->text();
+ entry.pager = m_genInfoWidget->pagerEdit->text();
+ entry.fax = m_genInfoWidget->faxEdit->text();
+ entry.phoneMobile = m_genInfoWidget->cellEdit->text();
+ entry.additionalNumber = m_genInfoWidget->additionalEdit->text();
+ entry.altEmail1 = m_genInfoWidget->emailEdit_2->text();
+ entry.altEmail2 = m_genInfoWidget->emailEdit_3->text();
+ entry.privateURL = m_genInfoWidget->homepageEdit->text();
+ entry.title = m_genInfoWidget->titleEdit->text();
+ entry.corporation = m_workInfoWidget->companyEdit->text();
+ entry.workAdress = m_workInfoWidget->addressEdit->text();
+ entry.workCity = m_workInfoWidget->cityEdit->text();
+ entry.workState = m_workInfoWidget->stateEdit->text();
+ entry.workZIP = m_workInfoWidget->zipEdit->text();
+ entry.workCountry = m_workInfoWidget->countryEdit->text();
+ entry.workURL = m_workInfoWidget->homepageEdit->text();
+ entry.privateAdress = m_genInfoWidget->addressEdit->text();
+ entry.privateCity = m_genInfoWidget->cityEdit->text();
+ entry.privateState = m_genInfoWidget->stateEdit->text();
+ entry.privateZIP = m_genInfoWidget->zipEdit->text();
+ entry.privateCountry = m_genInfoWidget->countryEdit->text();
+ QString bi = m_genInfoWidget->birthdayEdit->text();
+ entry.birthday = QDate( bi.section("/",2,2).toInt(), bi.section("/",1,1).toInt(), bi.section("/",0,0).toInt() );
+ QString an = m_genInfoWidget->anniversaryEdit->text();
+ entry.anniversary = QDate( an.section("/",2,2).toInt(), an.section("/",1,1).toInt(), an.section("/",0,0).toInt() );
+ entry.additional1 = m_otherInfoWidget->note1Edit->text();
+ entry.additional2 = m_otherInfoWidget->note2Edit->text();
+ entry.additional3 = m_otherInfoWidget->note3Edit->text();
+ entry.additional4 = m_otherInfoWidget->note4Edit->text();
+ entry.notes = m_otherInfoWidget->commentsEdit->text();
+// entry.imAIM = m_genInfoWidget->firstNameEdit->text();
+// entry.imGoogleTalk = m_genInfoWidget->firstNameEdit->text();
+// entry.imICQ = m_genInfoWidget->firstNameEdit->text();
+// entry.imIRC = m_genInfoWidget->firstNameEdit->text();
+// entry.imMSN = m_genInfoWidget->firstNameEdit->text();
+// entry.imQQ = m_genInfoWidget->firstNameEdit->text();
+// entry.imSkype = m_genInfoWidget->firstNameEdit->text();
+
+ emit saveYABEntry( entry );
+
+ QDialog::accept();
+}
+
+void YahooUserInfoDialog::slotUser2()
+{
+ if( m_contact )
+ {
+ YABEntry entry;
+ const YABEntry *oldEntry = m_contact->yabEntry();
+
+ entry.yahooId = m_yab.yahooId;
+ entry.YABId = m_yab.YABId;
+ entry.firstName = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->firstName : m_genInfoWidget->firstNameEdit->text();
+ entry.secondName = m_genInfoWidget->secondNameEdit->text().isEmpty() ? oldEntry->secondName : m_genInfoWidget->secondNameEdit->text();
+ entry.lastName = m_genInfoWidget->lastNameEdit->text().isEmpty() ? oldEntry->lastName : m_genInfoWidget->lastNameEdit->text();
+ entry.nickName = m_genInfoWidget->nickNameEdit->text().isEmpty() ? oldEntry->nickName : m_genInfoWidget->nickNameEdit->text();
+ entry.email = m_genInfoWidget->emailEdit->text().isEmpty() ? oldEntry->email : m_genInfoWidget->emailEdit->text();
+ entry.privatePhone = m_genInfoWidget->phoneEdit->text().isEmpty() ? oldEntry->privatePhone : m_genInfoWidget->phoneEdit->text();
+ entry.workPhone = m_workInfoWidget->phoneEdit->text().isEmpty() ? oldEntry->workPhone : m_workInfoWidget->phoneEdit->text();
+ entry.pager = m_genInfoWidget->pagerEdit->text().isEmpty() ? oldEntry->pager : m_genInfoWidget->pagerEdit->text();
+ entry.fax = m_genInfoWidget->faxEdit->text().isEmpty() ? oldEntry->fax : m_genInfoWidget->faxEdit->text();
+ entry.phoneMobile = m_genInfoWidget->cellEdit->text().isEmpty() ? oldEntry->phoneMobile : m_genInfoWidget->cellEdit->text();
+ entry.additionalNumber = m_genInfoWidget->additionalEdit->text().isEmpty() ? oldEntry->additionalNumber : m_genInfoWidget->additionalEdit->text();
+ entry.altEmail1 = m_genInfoWidget->emailEdit_2->text().isEmpty() ? oldEntry->altEmail1 : m_genInfoWidget->emailEdit_2->text();
+ entry.altEmail2 = m_genInfoWidget->emailEdit_3->text().isEmpty() ? oldEntry->altEmail2 : m_genInfoWidget->emailEdit_3->text();
+ entry.privateURL = m_genInfoWidget->homepageEdit->text().isEmpty() ? oldEntry->privateURL : m_genInfoWidget->homepageEdit->text();
+ entry.title = m_genInfoWidget->titleEdit->text().isEmpty() ? oldEntry->title : m_genInfoWidget->titleEdit->text();
+ entry.corporation = m_workInfoWidget->companyEdit->text().isEmpty() ? oldEntry->corporation : m_workInfoWidget->companyEdit->text();
+ entry.workAdress = m_workInfoWidget->addressEdit->text().isEmpty() ? oldEntry->workAdress : m_workInfoWidget->addressEdit->text();
+ entry.workCity = m_workInfoWidget->cityEdit->text().isEmpty() ? oldEntry->workCity : m_workInfoWidget->cityEdit->text();
+ entry.workState = m_workInfoWidget->stateEdit->text().isEmpty() ? oldEntry->workState : m_workInfoWidget->stateEdit->text();
+ entry.workZIP = m_workInfoWidget->zipEdit->text().isEmpty() ? oldEntry->workZIP : m_workInfoWidget->zipEdit->text();
+ entry.workCountry = m_workInfoWidget->countryEdit->text().isEmpty() ? oldEntry->workCountry : m_workInfoWidget->countryEdit->text();
+ entry.workURL = m_workInfoWidget->homepageEdit->text().isEmpty() ? oldEntry->workURL : m_workInfoWidget->homepageEdit->text();
+ entry.privateAdress = m_genInfoWidget->addressEdit->text().isEmpty() ? oldEntry->privateAdress : m_genInfoWidget->addressEdit->text();
+ entry.privateCity = m_genInfoWidget->cityEdit->text().isEmpty() ? oldEntry->privateCity : m_genInfoWidget->cityEdit->text();
+ entry.privateState = m_genInfoWidget->stateEdit->text().isEmpty() ? oldEntry->privateState : m_genInfoWidget->stateEdit->text();
+ entry.privateZIP = m_genInfoWidget->zipEdit->text().isEmpty() ? oldEntry->privateZIP : m_genInfoWidget->zipEdit->text();
+ entry.privateCountry = m_genInfoWidget->countryEdit->text().isEmpty() ? oldEntry->privateCountry : m_genInfoWidget->countryEdit->text();
+
+ if( m_genInfoWidget->birthdayEdit->text().isEmpty() )
+ entry.birthday = oldEntry->birthday;
+ else
+ {
+ QString bi = m_genInfoWidget->birthdayEdit->text();
+ entry.birthday = QDate( bi.section("/",2,2).toInt(), bi.section("/",1,1).toInt(), bi.section("/",0,0).toInt() );
+ }
+
+ if( m_genInfoWidget->anniversaryEdit->text().isEmpty() )
+ entry.anniversary = oldEntry->anniversary;
+ else
+ {
+ QString an = m_genInfoWidget->anniversaryEdit->text();
+ entry.anniversary = QDate( an.section("/",2,2).toInt(), an.section("/",1,1).toInt(), an.section("/",0,0).toInt() );
+ }
+
+ entry.additional1 = m_otherInfoWidget->note1Edit->text().isEmpty() ? oldEntry->additional1 : m_otherInfoWidget->note1Edit->text();
+ entry.additional2 = m_otherInfoWidget->note2Edit->text().isEmpty() ? oldEntry->additional2 : m_otherInfoWidget->note2Edit->text();
+ entry.additional3 = m_otherInfoWidget->note3Edit->text().isEmpty() ? oldEntry->additional3 : m_otherInfoWidget->note3Edit->text();
+ entry.additional4 = m_otherInfoWidget->note4Edit->text().isEmpty() ? oldEntry->additional4 : m_otherInfoWidget->note4Edit->text();
+ entry.notes = m_otherInfoWidget->commentsEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imAIM = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imGoogleTalk = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imICQ = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imIRC = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imMSN = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imQQ = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+ // entry.imSkype = m_genInfoWidget->firstNameEdit->text().isEmpty() ? oldEntry->notes : m_otherInfoWidget->commentsEdit->text();
+
+ emit saveYABEntry( entry );
+ }
+
+ QDialog::accept();
+}
+
+void YahooUserInfoDialog::setData( const YABEntry &yab )
+{
+ m_yab = yab;
+
+ if( m_yab.source == YABEntry::SourceContact )
+ {
+ showButton( User2, true );
+ setButtonText( User1, i18n("Replace existing entry") );
+ }
+
+ m_genInfoWidget->firstNameEdit->setText( yab.firstName );
+ m_genInfoWidget->secondNameEdit->setText( yab.secondName );
+ m_genInfoWidget->lastNameEdit->setText( yab.lastName );
+ m_genInfoWidget->nickNameEdit->setText( yab.nickName );
+ m_genInfoWidget->yahooIdEdit->setText( yab.yahooId );
+ m_genInfoWidget->titleEdit->setText( yab.title );
+
+ if( yab.birthday.isValid() )
+ m_genInfoWidget->birthdayEdit->setText( QString("%1/%2/%3").arg( yab.birthday.day() ).arg( yab.birthday.month() ).arg( yab.birthday.year() ));
+ if( yab.anniversary.isValid() )
+ m_genInfoWidget->anniversaryEdit->setText( QString("%1/%2/%3").arg( yab.anniversary.day() ).arg( yab.anniversary.month() ).arg( yab.anniversary.year() ));
+
+ m_genInfoWidget->addressEdit->setText( yab.privateAdress );
+ m_genInfoWidget->cityEdit->setText( yab.privateCity );
+ m_genInfoWidget->stateEdit->setText( yab.privateState );
+ m_genInfoWidget->zipEdit->setText( yab.privateZIP );
+ m_genInfoWidget->countryEdit->setText( yab.privateCountry );
+ m_genInfoWidget->phoneEdit->setText( yab.privatePhone );
+ m_genInfoWidget->cellEdit->setText( yab.phoneMobile );
+ m_genInfoWidget->faxEdit->setText( yab.fax );
+ m_genInfoWidget->pagerEdit->setText( yab.pager );
+ m_genInfoWidget->emailEdit->setText( yab.email );
+ m_genInfoWidget->emailEdit_2->setText( yab.altEmail1 );
+ m_genInfoWidget->emailEdit_3->setText( yab.altEmail2 );
+ m_genInfoWidget->homepageEdit->setText( yab.privateURL );
+ m_genInfoWidget->additionalEdit->setText( yab.additionalNumber );
+
+ m_workInfoWidget->phoneEdit->setText( yab.workPhone );
+ m_workInfoWidget->addressEdit->setText( yab.workAdress );
+ m_workInfoWidget->cityEdit->setText( yab.workCity );
+ m_workInfoWidget->stateEdit->setText( yab.workState );
+ m_workInfoWidget->zipEdit->setText( yab.workZIP );
+ m_workInfoWidget->countryEdit->setText( yab.workCountry );
+ m_workInfoWidget->companyEdit->setText( yab.corporation );
+ m_workInfoWidget->homepageEdit->setText( yab.workURL );
+
+ m_otherInfoWidget->commentsEdit->setText( yab.notes );
+ m_otherInfoWidget->note1Edit->setText( yab.additional1 );
+ m_otherInfoWidget->note2Edit->setText( yab.additional2 );
+ m_otherInfoWidget->note3Edit->setText( yab.additional3 );
+ m_otherInfoWidget->note4Edit->setText( yab.additional4 );
+}
+
+#include "yahoouserinfodialog.moc"
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
+
diff --git a/kopete/protocols/yahoo/ui/yahoouserinfodialog.h b/kopete/protocols/yahoo/ui/yahoouserinfodialog.h
new file mode 100644
index 00000000..6500d412
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoouserinfodialog.h
@@ -0,0 +1,56 @@
+/*
+ Kopete Yahoo Protocol
+ yahoouserinfodialog.h - Display Yahoo user info
+
+ Copyright (c) 2005 Matt Rogers <mattr@kde.org>
+ Copyright (c) 2006 Andre Duffeck <mattr@kde.org>
+
+ Kopete (c) 2002-2006 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This library is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU Lesser General Public *
+ * License as published by the Free Software Foundation; either *
+ * version 2 of the License, or (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOUSERINFODIALOG_H_
+#define YAHOOUSERINFODIALOG_H_
+
+#include <kdialogbase.h>
+#include "../libkyahoo/yabentry.h"
+
+class KJanusWidget;
+class YahooWorkInfoWidget;
+class YahooGeneralInfoWidget;
+class YahooOtherInfoWidget;
+class YahooContact;
+
+class YahooUserInfoDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ YahooUserInfoDialog( YahooContact *c, QWidget* parent = 0, const char* name = 0 );
+ void setAccountConnected( bool isOnline );
+signals:
+ void saveYABEntry( YABEntry & );
+public slots:
+ void setData( const YABEntry &yab );
+private slots:
+ void slotSaveAndCloseClicked();
+ void slotUser2();
+private:
+ YahooGeneralInfoWidget* m_genInfoWidget;
+ YahooWorkInfoWidget* m_workInfoWidget;
+ YahooOtherInfoWidget* m_otherInfoWidget;
+
+ YABEntry m_yab;
+ YahooContact *m_contact;
+};
+
+#endif
+
+//kate: indent-mode csands; tab-width 4; space-indent off; replace-tabs off;
diff --git a/kopete/protocols/yahoo/ui/yahooverifyaccountbase.ui b/kopete/protocols/yahoo/ui/yahooverifyaccountbase.ui
new file mode 100644
index 00000000..73eb827a
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooverifyaccountbase.ui
@@ -0,0 +1,159 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooVerifyAccountBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooVerifyAccountBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>200</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>200</height>
+ </size>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Your Account has to be verified because of too many false login attempts.&lt;br&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout0</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Please enter the chars shown in the picture:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>mWord</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer15</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>110</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>72</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>mPicture</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>240</width>
+ <height>75</height>
+ </size>
+ </property>
+ <property name="pixmap">
+ <pixmap>image0</pixmap>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer13</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>72</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="4833">789c8597596f23470e80dfe75718c3b7c182e9eabb11ec834fc9873cbeaf601fc86ec9966df994cfc5fef794483633934d10c836fcb9582cde55fee5dbd2d9de68e9db2f5f9ee7349fb64bed153d2d7deb5e66b38fdffef3efff7ef99aa64b8baf2c5b4abffeebcb57dc5c6a9720499290640b8613e110ff22eb941b07657e7256f90be742e4a7c6a9ed076791c7a1732efbd784d3f817615a71167db0e55c0a17ce95c88f8ced3c7e74d6f306cea29fd159f583b3e8a75367d18f5bce8df0b63389be67e3ccfcd97716fd78e22cfaf1d459edefedcbedbc63e75af4cf85b33edeb8616cfef1b5b39eb7e72cf6c28b7166fb53678df7b5b3fadf388b3d503a8b3d40c6b9eaa35767d9cf37c6a5e53f73d6f3769d55bedf5f2789ac6bfcf2de1fda33ce4cdfbb716eeb6fce1acf1de3c2f2bbe7acf573605cdafe2b67f5efd359f20bb97165febe38b3c453fd2bfa7cd0a67165fea97c9db416ff1de3b1e9d77a6d12567bf8c159ed9d1ab7969fc2b8d378e2ba304579c93769bd719457fd524f2184cae221f51bd250a8fd3c35ae4cfec1b8b6fa06e3c6eaffc859d651f215b2d0c7efdab8327b969d459e9e8d7bfd57c68df587c43be4c1e20b685c1b9f0b17c1e283cfce927f96f911ca40d63fcbce7afebd31eb3a91b3fa37376ead9e64dea5759606a94f546eb23499883d9bc69932bef4acf22cf594725c1f8bfca9b3c8d3aa711e82c82bb73de3817111a4fe2171d67e1a1b97ba8e87ce5a8f12bfb48b2cfae8ceb85206894f9666135bff2e5c45d6fae894a3393a3f5be13aaeb7729eca4ff2d6ce3f5b701ef2b1c95f0aa7715dfb4de657519675d0fc8d8c9ba0f527f12faab234fd8fc695f1ab716dfa3f84ebb2089dec5f779678d381711d74beae398b7f540b3751bfd6e39ab1c9033b6bbd07e326687dcb3c2da8ecf47cfa301e9bbe37e389c94b3c0a2edba0f5f361dc194b3c8b36eed77acf9c453fab7f6d95587e6e8d83c957c25dc9668fdc5fc538b2c6e3a6e754e7adcc836252b6a9d69bccffb2ad535d67e98fb2ab538befaab3e8439947e5b84e82f6ffa17165f1bd7096fcc1ccb8b67a981b375a0f20f92d2775b078493dd54c75aaef0de9dfba75167fea2eb2f683dc5771388d753fbe396b7fca7c6b52ea2c5e57ce621fcbfdd26464f1812767ed8f63e3ced665de34c464fe9e1bb3d5b3f45bc3dca67a7fc9fba7697b46a9bf66cc13cb8fcc8b66c2960f3832b6f3988dad1e2828b799c53371d6f9766b9c5b7f4afe286983f5c3aeb3be177ace6c1ecc9cb51f6a678def8a7169f351fca710f5a93d9db3d64febacf9981aa76a2f5e3b6b7d5d1af7f6df3aebfb67e4acf93f72d6f972675cd83c7d77d67cac3bebfdfce9acf7eb87b3c66b665cdabc5feed9f44bfd51caade59395dbc4fc3f33cecc9f9b9e35bf786fdccfffc459e7ffc059eb7fe2acefcfd459df13dbceda5f4367bddffed8affdf0665c243a6f769c351f6367f51f7ad6fcefcf9dd57e72567fd959e3dd3a6bbc3b677daf34ce6affadb3dadbdb57263a5f46ce7adf8e9dd5dededfbe5e2f9cb5de5b678df7bb71a5fa59f767dccf77cd57d6f7136c185b7e79d359f3159cf53df7e8acf19e19f7f57ee5acef9b0d67f5b733b6fa844b63f38f07ceea9fcc3fca5b9bef58199b3dbce5acefa53567bd6fee8c73d54f95b3fa3b34b6fcc3b3b3f6dba1b3f6afe6a788fb6bad9f1f3f08f19b90b18ddff0f3dafefc2fe4bb284948f1b7f1e2e73fca4ff012af708ad77883b77f2f8f33bc8b9aeff1011ff1099f718e2ff88a6ff88e1ff8196da33fc92fe30aaee21aaee3060e70889bb885dbb88323dc8d7a407df941be8dd2dfa3ec5e94dac7033cc4233cc6133cc5333cc78bffb327c18029669863812556d1ef1a9ba8168080a1850ec608daaf30814bb882296ec035dc486426700b33b8837b78c0213cc2133ce367af3f7a93c01c5e700b5ee10d6fe11d093ee01396610556f104d6601d36a2d77abf0da2f41036610bb6b1841d18c12e7c873dd887033884233886133885b318297d3fc558e114cee1021208d1e01432c8a180122aa8e3c5190f2322c63bb507995aea62bc37698c9f34a14bba822d9ad235ddd02dcde80e87744f0f7fc823d1233de1363d634d737aa1577aa3ebf8047ba70ffaa4655aa1555aa375b37f0c298e6923ca0fb0a1212cd3266dd136edd08876e93bedd13e1dd0211d997e88f6031dd3099d624e67744e17f15f8b40296594cb273ef61632aa5ff34515d5d43032707c19449fd6e993db283b8217ee62fc0630fa31bf1cdf037c493bb1724ef98a162d30e56bcae185467cc3b731dfa0f935f919dff13daef1033ff2133fc77d039e73d41da55ff90d268b8afba17ea27d30a18edff9833f7999577895d7a88421aff31b6fc0e04ff5c6314acc031ef2266cc4c7ce036ff12ca66a10ff75d95eacfd2ccf3b0bfd38e651ac93f7d83b77f84e47d1e6f1222e0bf9c5ef3ff7a3f690766f4ffd0490aa85affffbf5cbef985d44a8</data>
+ </image>
+</images>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/ui/yahoowebcamdialog.cpp b/kopete/protocols/yahoo/ui/yahoowebcamdialog.cpp
new file mode 100644
index 00000000..1c7d4ef7
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoowebcamdialog.cpp
@@ -0,0 +1,113 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include "yahoowebcamdialog.h"
+
+#include <qframe.h>
+#include <qobject.h>
+#include <qwidget.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qvbox.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <webcamwidget.h>
+
+YahooWebcamDialog::YahooWebcamDialog( const QString &contactId, QWidget * parent, const char * name )
+: KDialogBase( KDialogBase::Plain, i18n( "Webcam for %1" ).arg( contactId ),
+ KDialogBase::Close, KDialogBase::Close, parent, name, false, true /*seperator*/ )
+{
+ setInitialSize( QSize(320,290), false );
+
+ setEscapeButton( KDialogBase::Close );
+ QObject::connect( this, SIGNAL( closeClicked() ), this, SIGNAL( closingWebcamDialog() ) );
+
+ contactName = contactId;
+ QWidget *page = plainPage();
+ setMainWidget(page);
+
+ QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() );
+ m_imageContainer = new Kopete::WebcamWidget( page );
+ m_imageContainer->setText( i18n( "No webcam image received" ) );
+ m_imageContainer->setMinimumSize(320,240);
+ m_imageContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ topLayout->add( m_imageContainer );
+
+ m_Viewer = new QLabel( page );
+ m_Viewer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ m_Viewer->hide();
+ topLayout->add( m_Viewer );
+
+ show();
+}
+
+YahooWebcamDialog::~ YahooWebcamDialog( )
+{
+
+}
+
+void YahooWebcamDialog::newImage( const QPixmap &image )
+{
+ m_imageContainer->updatePixmap( image );
+}
+
+void YahooWebcamDialog::webcamPaused()
+{
+ m_imageContainer->setText( QString::fromLatin1("*** Webcam paused ***") );
+}
+
+void YahooWebcamDialog::webcamClosed( int reason )
+{
+ kdDebug(14180) << k_funcinfo << "webcam closed with reason?? " << reason <<endl;
+ QString closeReason;
+ switch ( reason )
+ {
+ case 1:
+ closeReason = i18n( "%1 has stopped broadcasting" ).arg( contactName ); break;
+ case 2:
+ closeReason = i18n( "%1 has cancelled viewing permission" ).arg( contactName ); break;
+ case 3:
+ closeReason = i18n( "%1 has declined permission to view webcam" ).arg( contactName ); break;
+ case 4:
+ closeReason = i18n( "%1 does not have his/her webcam online" ).arg( contactName ); break;
+ default:
+ closeReason = i18n( "Unable to view the webcam of %1 for an unknown reason" ).arg( contactName);
+ }
+ m_imageContainer->clear();
+
+ m_imageContainer->setText( closeReason );
+}
+
+void YahooWebcamDialog::setViewer( const QStringList &viewer )
+{
+ QString s = i18n( "%1 viewer(s)" ).arg( viewer.size() );
+ if( viewer.size() )
+ {
+ s += ": ";
+ for ( QStringList::ConstIterator it = viewer.begin(); it != viewer.end(); ++it ) {
+ if( it != viewer.begin() )
+ s += ", ";
+ s += *it;
+ }
+ }
+ m_Viewer->setText( s );
+ m_Viewer->show();
+}
+
+// kate: indent-mode csands; tab-width 4;
+
+#include "yahoowebcamdialog.moc"
diff --git a/kopete/protocols/yahoo/ui/yahoowebcamdialog.h b/kopete/protocols/yahoo/ui/yahoowebcamdialog.h
new file mode 100644
index 00000000..8400e53d
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahoowebcamdialog.h
@@ -0,0 +1,56 @@
+/*
+ Kopete Yahoo Protocol
+
+ Copyright (c) 2005 by Matt Rogers <mattr@kde.org>
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOWEBCAMDIALOG_H_
+#define YAHOOWEBCAMDIALOG_H_
+
+#include <qstring.h>
+#include <kdialogbase.h>
+
+
+class QPixmap;
+class QWidget;
+class YahooContact;
+
+namespace Kopete
+{
+ class WebcamWidget;
+}
+
+class YahooWebcamDialog : public KDialogBase
+{
+Q_OBJECT
+public:
+ YahooWebcamDialog( const QString &, QWidget* parent = 0, const char* name = 0 );
+ ~YahooWebcamDialog();
+
+ void setViewer( const QStringList & );
+public slots:
+ void newImage( const QPixmap &image );
+ void webcamClosed( int );
+ void webcamPaused();
+signals:
+ void closingWebcamDialog();
+
+private:
+ Kopete::WebcamWidget *m_imageContainer;
+ QLabel *m_Viewer;
+ QString contactName;
+
+};
+
+#endif
+//kate: indent-mode csands; auto-insert-doxygen on;
diff --git a/kopete/protocols/yahoo/ui/yahooworkinfowidget.ui b/kopete/protocols/yahoo/ui/yahooworkinfowidget.ui
new file mode 100644
index 00000000..0be88f61
--- /dev/null
+++ b/kopete/protocols/yahoo/ui/yahooworkinfowidget.ui
@@ -0,0 +1,233 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>YahooWorkInfoWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>YahooWorkInfoWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>328</width>
+ <height>681</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox" row="0" column="0">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Personal Work Information</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel10</cstring>
+ </property>
+ <property name="text">
+ <string>Phone:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>phoneEdit</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Company Location Information</string>
+ </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>Name:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Homepage:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>companyEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>homepageEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>textLabel9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Country:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="7" column="1">
+ <property name="name">
+ <cstring>countryEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QTextEdit" row="2" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>addressEdit</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Address:</string>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <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>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLineEdit" row="5" column="1">
+ <property name="name">
+ <cstring>cityEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="6" column="1">
+ <property name="name">
+ <cstring>stateEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>State:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>City:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>textLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>Zip:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="4" column="1">
+ <property name="name">
+ <cstring>zipEdit</cstring>
+ </property>
+ <property name="readOnly">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer row="2" column="0">
+ <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>150</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<tabstops>
+ <tabstop>phoneEdit</tabstop>
+ <tabstop>companyEdit</tabstop>
+ <tabstop>homepageEdit</tabstop>
+ <tabstop>addressEdit</tabstop>
+ <tabstop>zipEdit</tabstop>
+ <tabstop>cityEdit</tabstop>
+ <tabstop>stateEdit</tabstop>
+ <tabstop>countryEdit</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kopete/protocols/yahoo/yahooaccount.cpp b/kopete/protocols/yahoo/yahooaccount.cpp
new file mode 100644
index 00000000..6aa7f880
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooaccount.cpp
@@ -0,0 +1,1831 @@
+/*
+ yahooaccount.cpp - Manages a single Yahoo account
+
+ Copyright (c) 2003 by Gav Wood <gav@kde.org>
+ Copyright (c) 2003-2004 by Matt Rogers <matt.rogers@kdemail.net>
+ Based on code by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+//Standard Header
+#include <ctime>
+#include <stdlib.h>
+
+//QT
+#include <qfont.h>
+#include <qdatetime.h>
+#include <qcolor.h>
+#include <qregexp.h>
+#include <qimage.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+
+// KDE
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kaction.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <krun.h>
+#include <kurl.h>
+#include <kstandarddirs.h>
+#include <kstandarddirs.h>
+
+// Kopete
+#include <kopetechatsession.h>
+#include <kopetemessage.h>
+#include <kopetepassword.h>
+#include <kopeteuiglobal.h>
+#include <knotification.h>
+#include <kopetemetacontact.h>
+#include <kopetecontactlist.h>
+#include <kopetetransfermanager.h>
+#include <kopeteview.h>
+#include <contactaddednotifydialog.h>
+
+// Yahoo
+#include "yahooaccount.h"
+#include "yahoocontact.h"
+#include "yahooconnector.h"
+#include "yahooclientstream.h"
+#include "client.h"
+#include "yahooverifyaccount.h"
+#include "yahoowebcam.h"
+#include "yahooconferencemessagemanager.h"
+#include "yahooinvitelistimpl.h"
+#include "yabentry.h"
+#include "yahoouserinfodialog.h"
+
+YahooAwayDialog::YahooAwayDialog(YahooAccount* account, QWidget *parent, const char *name) :
+ KopeteAwayDialog(parent, name)
+{
+ theAccount = account;
+}
+
+void YahooAwayDialog::setAway(int awayType)
+{
+ awayType = 0;
+ theAccount->setAway(awayType, getSelectedAwayMessage());
+}
+
+
+YahooAccount::YahooAccount(YahooProtocol *parent, const QString& accountId, const char *name)
+ : Kopete::PasswordedAccount(parent, accountId, 0, name)
+{
+
+ // first things first - initialise internals
+ stateOnConnection = 0;
+ theHaveContactList = false;
+ theAwayDialog = new YahooAwayDialog( this );
+ m_protocol = parent;
+ m_session = new Client( this );
+ m_lastDisconnectCode = 0;
+ m_currentMailCount = 0;
+ m_webcam = 0L;
+
+ m_session->setUserId( accountId.lower() );
+
+ m_openInboxAction = new KAction( i18n( "Open Inbo&x..." ), "mail_generic", 0, this, SLOT( slotOpenInbox() ), this, "m_openInboxAction" );
+ m_openYABAction = new KAction( i18n( "Open &Addressbook..." ), "contents", 0, this, SLOT( slotOpenYAB() ), this, "m_openYABAction" );
+ m_editOwnYABEntry = new KAction( i18n( "&Edit my contact details..."), "contents", 0, this, SLOT( slotEditOwnYABEntry() ), this, "m_editOwnYABEntry" );
+
+ YahooContact* _myself=new YahooContact( this, accountId.lower(), accountId, Kopete::ContactList::self()->myself() );
+ setMyself( _myself );
+ _myself->setOnlineStatus( parent->Offline );
+ myself()->setProperty( YahooProtocol::protocol()->iconRemoteUrl, configGroup()->readEntry( "iconRemoteUrl", "" ) );
+ myself()->setProperty( Kopete::Global::Properties::self()->photo(), configGroup()->readEntry( "iconLocalUrl", "" ) );
+ myself()->setProperty( YahooProtocol::protocol()->iconCheckSum, configGroup()->readNumEntry( "iconCheckSum", 0 ) );
+ myself()->setProperty( YahooProtocol::protocol()->iconExpire, configGroup()->readNumEntry( "iconExpire", 0 ) );
+
+ QObject::connect( Kopete::ContactList::self(), SIGNAL( globalIdentityChanged(const QString&, const QVariant& ) ), SLOT( slotGlobalIdentityChanged(const QString&, const QVariant& ) ));
+// initConnectionSignals( MakeConnections );
+
+ QString displayName = configGroup()->readEntry(QString::fromLatin1("displayName"));
+ if(!displayName.isEmpty())
+ _myself->setNickName(displayName);
+
+ m_YABLastMerge = configGroup()->readNumEntry( "YABLastMerge", 0 );
+ m_YABLastRemoteRevision = configGroup()->readNumEntry( "YABLastRemoteRevision", 0 );
+}
+
+YahooAccount::~YahooAccount()
+{
+ if( m_webcam )
+ m_webcam->stopTransmission();
+ delete theAwayDialog;
+}
+
+void YahooAccount::setServer( const QString &server )
+{
+ configGroup()->writeEntry( QString::fromLatin1( "Server" ), server );
+}
+
+void YahooAccount::setPort( int port )
+{
+ configGroup()->writeEntry( QString::fromLatin1( "Port" ), port );
+}
+
+void YahooAccount::slotGoStatus( int status, const QString &awayMessage)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "GoStatus: " << status << " msg: " << awayMessage <<endl;
+ if( !isConnected() )
+ {
+ connect( m_protocol->statusFromYahoo( status ) );
+ stateOnConnection = status;
+ }
+ else
+ {
+ m_session->changeStatus( Yahoo::Status( status ), awayMessage,
+ (status == Yahoo::StatusAvailable)? Yahoo::StatusTypeAvailable : Yahoo::StatusTypeAway );
+
+ //sets the awayMessage property for the owner of the account. shows up in the statusbar icon's tooltip. the property is unset when awayMessage is null
+ myself()->setProperty( m_protocol->awayMessage, awayMessage );
+
+ myself()->setOnlineStatus( m_protocol->statusFromYahoo( status ) );
+ }
+}
+
+Client *YahooAccount::yahooSession()
+{
+ return m_session ? m_session : 0L;
+}
+
+QString YahooAccount::stripMsgColorCodes(const QString& msg)
+{
+ QString filteredMsg = msg;
+
+ //Handle bold, underline and italic messages
+ filteredMsg.replace( "\033[1m", "<b>" );
+ filteredMsg.replace( "\033[x1m", "</b>" );
+ filteredMsg.replace( "\033[2m", "<i>" );
+ filteredMsg.replace( "\033[x2m", "</i>" );
+ filteredMsg.replace( "\033[4m", "<u>" );
+ filteredMsg.replace( "\033[x4m", "</u>" );
+
+ //GAIM doesn't check for ^[[3m. Does this ever get sent?
+ filteredMsg.replace( "\033[3m", "<i>" );
+ filteredMsg.replace( "\033[x3m", "</i>" );
+
+ //Strip link tags
+ filteredMsg.remove( "\033[lm" );
+ filteredMsg.remove( "\033[xlm" );
+
+ //Remove color codes and other residual formatting
+ filteredMsg.remove( QRegExp("\033\\[[^m]*m") );
+
+ return filteredMsg;
+}
+
+QColor YahooAccount::getMsgColor(const QString& msg)
+{
+ /* Yahoo sends a message either with color or without color
+ * so we have to use this really hacky method to get colors
+ */
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "msg is " << msg << endl;
+ //Please note that some of the colors are hard-coded to
+ //match the yahoo colors
+ if ( msg.find("\033[38m") != -1 )
+ return Qt::red;
+ if ( msg.find("\033[34m") != -1 )
+ return Qt::green;
+ if ( msg.find("\033[31m") != -1 )
+ return Qt::blue;
+ if ( msg.find("\033[39m") != -1 )
+ return Qt::yellow;
+ if ( msg.find("\033[36m") != -1 )
+ return Qt::darkMagenta;
+ if ( msg.find("\033[32m") != -1 )
+ return Qt::cyan;
+ if ( msg.find("\033[37m") != -1 )
+ return QColor("#FFAA39");
+ if ( msg.find("\033[35m") != -1 )
+ return QColor("#FFD8D8");
+ if ( msg.find("\033[#") != -1 )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Custom color is " << msg.mid(msg.find("\033[#")+2,7) << endl;
+ return QColor(msg.mid(msg.find("\033[#")+2,7));
+ }
+
+ //return a default value just in case
+ return Qt::black;
+}
+
+void YahooAccount::initConnectionSignals( enum SignalConnectionType sct )
+{
+ if ( !m_session )
+ return;
+
+ if ( sct == MakeConnections )
+ {
+ QObject::connect(m_session, SIGNAL(loggedIn( int, const QString &)),
+ this, SLOT(slotLoginResponse(int, const QString &)) );
+
+ QObject::connect(m_session, SIGNAL(disconnected()),
+ this, SLOT(slotDisconnected()) );
+
+ QObject::connect(m_session, SIGNAL(loginFailed()),
+ this, SLOT(slotLoginFailed()) );
+
+ QObject::connect(m_session, SIGNAL(error(int)),
+ this, SLOT(slotError(int)));
+
+ QObject::connect(m_session, SIGNAL(gotBuddy(const QString &, const QString &, const QString &)),
+ this, SLOT(slotGotBuddy(const QString &, const QString &, const QString &)));
+
+ QObject::connect(m_session, SIGNAL(authorizationAccepted( const QString & )),
+ this, SLOT(slotAuthorizationAccepted( const QString & )) );
+
+ QObject::connect(m_session, SIGNAL(authorizationRejected( const QString &, const QString & )),
+ this, SLOT(slotAuthorizationRejected( const QString &, const QString & )) );
+
+ QObject::connect(m_session, SIGNAL(gotAuthorizationRequest( const QString &, const QString &, const QString & )),
+ this, SLOT(slotgotAuthorizationRequest( const QString &, const QString &, const QString & )) );
+
+ QObject::connect(m_session, SIGNAL(statusChanged(const QString&, int, const QString&, int, int)),
+ this, SLOT(slotStatusChanged(const QString&, int, const QString&, int, int)));
+
+ QObject::connect(m_session, SIGNAL(stealthStatusChanged(const QString &, Yahoo::StealthStatus)),
+ this, SLOT(slotStealthStatusChanged( const QString &, Yahoo::StealthStatus)) );
+
+ QObject::connect(m_session, SIGNAL(gotIm(const QString&, const QString&, long, int)),
+ this, SLOT(slotGotIm(const QString &, const QString&, long, int)));
+
+ QObject::connect(m_session, SIGNAL(gotBuzz(const QString&, long)),
+ this, SLOT(slotGotBuzz(const QString &, long)));
+
+ QObject::connect(m_session, SIGNAL( gotConferenceInvite( const QString&, const QString&,
+ const QString&, const QStringList&) ),
+ this,
+ SLOT( slotGotConfInvite( const QString&, const QString&,
+ const QString&, const QStringList& ) ) );
+
+ QObject::connect(m_session, SIGNAL(confUserDeclined(const QString&, const QString &, const QString &)),
+ this,
+ SLOT(slotConfUserDecline( const QString &, const QString &, const QString &)) );
+
+ QObject::connect(m_session , SIGNAL(confUserJoined( const QString &, const QString &)), this,
+ SLOT(slotConfUserJoin( const QString &, const QString &)) );
+
+ QObject::connect(m_session , SIGNAL(confUserLeft( const QString &, const QString &)), this,
+ SLOT(slotConfUserLeave( const QString &, const QString &)) );
+
+ QObject::connect(m_session , SIGNAL(gotConferenceMessage( const QString &, const QString &, const QString &)), this,
+ SLOT(slotConfMessage( const QString &, const QString &, const QString &)) );
+
+ QObject::connect(m_session,
+ SIGNAL(incomingFileTransfer(const QString &, const QString &, long, const QString &, const QString &, unsigned long)),
+ this,
+ SLOT(slotGotFile(const QString&, const QString&, long, const QString&, const QString&, unsigned long)));
+
+ QObject::connect(m_session, SIGNAL(fileTransferComplete(unsigned int)), this,
+ SLOT(slotFileTransferComplete(unsigned int)) );
+
+ QObject::connect(m_session, SIGNAL(fileTransferBytesProcessed(unsigned int,unsigned int)), this,
+ SLOT(slotFileTransferBytesProcessed(unsigned int,unsigned int)) );
+
+ QObject::connect(m_session, SIGNAL(fileTransferError(unsigned int,int,const QString &)), this,
+ SLOT(slotFileTransferError(unsigned int,int,const QString &)) );
+
+ QObject::connect(m_session, SIGNAL(typingNotify(const QString &, int)), this ,
+ SLOT(slotTypingNotify(const QString &, int)));
+
+// QObject::connect(m_session, SIGNAL(gameNotify(const QString &, int)), this,
+// SLOT(slotGameNotify( const QString &, int)));
+
+ QObject::connect(m_session, SIGNAL(mailNotify(const QString&, const QString&, int)), this,
+ SLOT(slotMailNotify(const QString &, const QString&, int)));
+
+ QObject::connect(m_session, SIGNAL(systemMessage(const QString&)), this,
+ SLOT(slotSystemMessage(const QString &)));
+
+// QObject::connect(m_session, SIGNAL(gotIdentities(const QStringList &)), this,
+// SLOT(slotGotIdentities( const QStringList&)));
+
+ QObject::connect(m_session, SIGNAL(gotWebcamInvite(const QString&)), this, SLOT(slotGotWebcamInvite(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(webcamNotAvailable(const QString&)), this, SLOT(slotWebcamNotAvailable(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(webcamImageReceived(const QString&, const QPixmap& )), this, SLOT(slotGotWebcamImage(const QString&, const QPixmap& )));
+
+ QObject::connect(m_session, SIGNAL(webcamClosed(const QString&, int )), this, SLOT(slotWebcamClosed(const QString&, int )));
+
+ QObject::connect(m_session, SIGNAL(webcamPaused(const QString&)), this, SLOT(slotWebcamPaused(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(webcamReadyForTransmission()), this, SLOT(slotWebcamReadyForTransmission()));
+
+ QObject::connect(m_session, SIGNAL(webcamStopTransmission()), this, SLOT(slotWebcamStopTransmission()));
+
+ QObject::connect(m_session, SIGNAL(webcamViewerJoined(const QString&)), this, SLOT(slotWebcamViewerJoined(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(webcamViewerLeft(const QString&)), this, SLOT(slotWebcamViewerLeft(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(webcamViewerRequest(const QString&)), this, SLOT(slotWebcamViewerRequest( const QString&)));
+
+ QObject::connect(m_session, SIGNAL(pictureStatusNotify( const QString&, int )), SLOT(slotPictureStatusNotiy( const QString&, int)));
+
+ QObject::connect(m_session, SIGNAL(pictureDownloaded(const QString&, KTempFile*, int)), this, SLOT(slotGotBuddyIcon(const QString&, KTempFile*, int)) );
+
+ QObject::connect(m_session, SIGNAL(pictureInfoNotify(const QString&, KURL, int)), this, SLOT(slotGotBuddyIconInfo(const QString&, KURL, int )));
+
+ QObject::connect(m_session, SIGNAL(pictureChecksumNotify(const QString&, int)), this, SLOT(slotGotBuddyIconChecksum(const QString&, int )));
+
+ QObject::connect(m_session, SIGNAL(pictureRequest(const QString&)), this, SLOT(slotGotBuddyIconRequest(const QString&)) );
+
+ QObject::connect(m_session, SIGNAL(pictureUploaded( const QString &)), this, SLOT(slotBuddyIconChanged(const QString&)));
+
+ QObject::connect(m_session, SIGNAL(gotYABEntry( YABEntry * )), this, SLOT(slotGotYABEntry( YABEntry * )));
+
+ QObject::connect(m_session, SIGNAL(modifyYABEntryError( YABEntry *, const QString & )), this, SLOT(slotModifyYABEntryError( YABEntry *, const QString & )));
+
+ QObject::connect(m_session, SIGNAL(gotYABRevision( long, bool )), this, SLOT(slotGotYABRevision( long , bool )) );
+ }
+
+ if ( sct == DeleteConnections )
+ {
+ QObject::disconnect(m_session, SIGNAL(loggedIn(int, const QString &)),
+ this, SLOT(slotLoginResponse(int, const QString &)) );
+
+ QObject::disconnect(m_session, SIGNAL(disconnected()),
+ this, SLOT(slotDisconnected()) );
+
+ QObject::disconnect(m_session, SIGNAL(loginFailed()),
+ this, SLOT(slotLoginFailed()) );
+
+ QObject::disconnect(m_session, SIGNAL(error(int)),
+ this, SLOT(slotError(int)));
+
+ QObject::disconnect(m_session, SIGNAL(gotBuddy(const QString &, const QString &, const QString &)),
+ this, SLOT(slotGotBuddy(const QString &, const QString &, const QString &)));
+
+ QObject::disconnect(m_session, SIGNAL(authorizationAccepted( const QString &)),
+ this, SLOT(slotAuthorizationAccepted( const QString &)) );
+
+ QObject::disconnect(m_session, SIGNAL(authorizationRejected( const QString &, const QString &)),
+ this, SLOT(slotAuthorizationRejected( const QString &, const QString & )) );
+
+ QObject::disconnect(m_session, SIGNAL(gotAuthorizationRequest( const QString &, const QString &, const QString & )),
+ this, SLOT(slotgotAuthorizationRequest( const QString &, const QString &, const QString & )) );
+
+ QObject::disconnect(m_session, SIGNAL(statusChanged(const QString&, int, const QString&, int, int)),
+ this, SLOT(slotStatusChanged(const QString&, int, const QString&, int, int)));
+
+ QObject::disconnect(m_session, SIGNAL(stealthStatusChanged(const QString &, Yahoo::StealthStatus)),
+ this, SLOT(slotStealthStatusChanged( const QString &, Yahoo::StealthStatus)) );
+
+ QObject::disconnect(m_session, SIGNAL(gotIm(const QString&, const QString&, long, int)),
+ this, SLOT(slotGotIm(const QString &, const QString&, long, int)));
+
+ QObject::disconnect(m_session, SIGNAL(gotBuzz(const QString&, long)),
+ this, SLOT(slotGotBuzz(const QString &, long)));
+
+ QObject::disconnect(m_session,
+ SIGNAL( gotConferenceInvite( const QString&, const QString&,
+ const QString&, const QStringList&) ),
+ this,
+ SLOT( slotGotConfInvite( const QString&, const QString&,
+ const QString&, const QStringList&) ) );
+
+ QObject::disconnect(m_session,
+ SIGNAL(confUserDeclined(const QString&, const QString &, const QString &)),
+ this,
+ SLOT(slotConfUserDecline( const QString &, const QString &, const QString& ) ) );
+
+ QObject::disconnect(m_session , SIGNAL(confUserJoined( const QString &, const QString &)),
+ this, SLOT(slotConfUserJoin( const QString &, const QString &)) );
+
+ QObject::disconnect(m_session , SIGNAL(confUserLeft( const QString &, const QString &)),
+ this, SLOT(slotConfUserLeave( const QString &, const QString &)) );
+
+ QObject::disconnect(m_session , SIGNAL(gotConferenceMessage( const QString &, const QString &, const QString &)), this,
+ SLOT(slotConfMessage( const QString &, const QString &, const QString &)) );
+
+ QObject::disconnect(m_session,
+ SIGNAL(incomingFileTransfer(const QString &, const QString &,
+ long, const QString &, const QString &, unsigned long)),
+ this,
+ SLOT(slotGotFile(const QString&, const QString&,
+ long, const QString&, const QString&, unsigned long)));
+
+ QObject::disconnect(m_session, SIGNAL(fileTransferComplete(unsigned int)), this,
+ SLOT(slotFileTransferComplete(unsigned int)) );
+
+ QObject::disconnect(m_session, SIGNAL(fileTransferBytesProcessed(unsigned int,unsigned int)), this,
+ SLOT(slotFileTransferBytesProcessed(unsigned int,unsigned int)) );
+
+ QObject::disconnect(m_session, SIGNAL(fileTransferError(unsigned int,int,const QString &)), this,
+ SLOT(slotFileTransferError(unsigned int,int,const QString &)) );
+
+ QObject::disconnect(m_session, SIGNAL(typingNotify(const QString &, int)), this ,
+ SLOT(slotTypingNotify(const QString &, int)));
+
+// QObject::disconnect(m_session, SIGNAL(gameNotify(const QString &, int)), this,
+// SLOT(slotGameNotify( const QString &, int)));
+
+ QObject::disconnect(m_session, SIGNAL(mailNotify(const QString&, const QString&, int)), this,
+ SLOT(slotMailNotify(const QString &, const QString&, int)));
+
+ QObject::disconnect(m_session, SIGNAL(systemMessage(const QString&)), this,
+ SLOT(slotSystemMessage(const QString &)));
+
+// QObject::disconnect(m_session, SIGNAL(gotIdentities(const QStringList &)), this,
+// SLOT(slotGotIdentities( const QStringList&)));
+
+ QObject::disconnect(m_session, SIGNAL(gotWebcamInvite(const QString&)), this, SLOT(slotGotWebcamInvite(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(webcamNotAvailable(const QString&)), this, SLOT(slotWebcamNotAvailable(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(webcamImageReceived(const QString&, const QPixmap& )), this, SLOT(slotGotWebcamImage(const QString&, const QPixmap& )));
+
+ QObject::disconnect(m_session, SIGNAL(webcamClosed(const QString&, int )), this, SLOT(slotWebcamClosed(const QString&, int )));
+
+ QObject::disconnect(m_session, SIGNAL(webcamPaused(const QString&)), this, SLOT(slotWebcamPaused(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(webcamReadyForTransmission()), this, SLOT(slotWebcamReadyForTransmission()));
+
+ QObject::disconnect(m_session, SIGNAL(webcamStopTransmission()), this, SLOT(slotWebcamStopTransmission()));
+
+ QObject::disconnect(m_session, SIGNAL(webcamViewerJoined(const QString&)), this, SLOT(slotWebcamViewerJoined(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(webcamViewerLeft(const QString&)), this, SLOT(slotWebcamViewerLeft(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(webcamViewerRequest(const QString&)), this, SLOT(slotWebcamViewerRequest( const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(pictureDownloaded(const QString&, KTempFile*, int )), this, SLOT(slotGotBuddyIcon(const QString&, KTempFile*,int )));
+
+ QObject::disconnect(m_session, SIGNAL(pictureInfoNotify(const QString&, KURL, int)), this, SLOT(slotGotBuddyIconInfo(const QString&, KURL, int )));
+
+ QObject::disconnect(m_session, SIGNAL(gotBuddyIconRequest(const QString&)), this, SLOT(slotGotBuddyIconRequest(const QString&)) );
+
+ QObject::disconnect(m_session, SIGNAL(pictureUploaded( const QString & )), this, SLOT(slotBuddyIconChanged(const QString&)));
+
+ QObject::disconnect(m_session, SIGNAL(pictureStatusNotify( const QString&, int )), this, SLOT(slotPictureStatusNotiy( const QString&, int)));
+
+ QObject::disconnect(m_session, SIGNAL(pictureChecksumNotify(const QString&, int)), this, SLOT(slotGotBuddyIconChecksum(const QString&, int )));
+
+ QObject::disconnect(m_session, SIGNAL(gotYABEntry( YABEntry * )), this, SLOT(slotGotYABEntry( YABEntry * )));
+
+ QObject::disconnect(m_session, SIGNAL(modifyYABEntryError( YABEntry *, const QString & )), this, SLOT(slotModifyYABEntryError( YABEntry *, const QString & )));
+
+ QObject::disconnect(m_session, SIGNAL(gotYABRevision( long, bool )), this, SLOT(slotGotYABRevision( long , bool )) );
+ }
+}
+
+void YahooAccount::connectWithPassword( const QString &passwd )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if ( isAway() )
+ {
+ slotGoOnline();
+ return;
+ }
+
+ if ( isConnected() ||
+ myself()->onlineStatus() == m_protocol->Connecting )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Yahoo plugin: Ignoring connect request (already connected)." <<endl;
+ return;
+
+ }
+
+ if ( passwd.isNull() )
+ { //cancel the connection attempt
+ static_cast<YahooContact*>( myself() )->setOnlineStatus( m_protocol->Offline );
+ return;
+ }
+
+ QString server = configGroup()->readEntry( "Server", "scs.msg.yahoo.com" );
+ int port = configGroup()->readNumEntry( "Port", 5050 );
+
+ initConnectionSignals( MakeConnections );
+
+ //YahooSessionManager::manager()->setPager( server, port );
+ //m_session = YahooSessionManager::manager()->createSession( accountId(), passwd );
+ kdDebug(YAHOO_GEN_DEBUG) << "Attempting to connect to Yahoo on <" << server << ":"
+ << port << ">. user <" << accountId() << ">" << endl;
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Connecting );
+ m_session->setStatusOnConnect( Yahoo::Status( initialStatus().internalStatus() ) );
+ m_session->connect( server, port, accountId().lower(), passwd );
+}
+
+void YahooAccount::disconnect()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ m_currentMailCount = 0;
+ if ( isConnected() )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Attempting to disconnect from Yahoo server " << endl;
+
+ m_session->close();
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+
+ for ( QDictIterator<Kopete::Contact> i( contacts() ); i.current(); ++i )
+ static_cast<YahooContact *>( i.current() )->setOnlineStatus( m_protocol->Offline );
+
+ disconnected( Manual );
+ }
+ else
+ { //make sure we set everybody else offline explicitly, just for cleanup
+ kdDebug(YAHOO_GEN_DEBUG) << "Cancelling active login attempts (not fully connected)." << endl;
+ m_session->cancelConnect();
+
+ for ( QDictIterator<Kopete::Contact> i(contacts()); i.current(); ++i )
+ static_cast<YahooContact*>( i.current() )->setOnlineStatus( m_protocol->Offline );
+ }
+
+ initConnectionSignals( DeleteConnections );
+ theHaveContactList = false;
+}
+
+void YahooAccount::verifyAccount( const QString &word )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Word: s" << word << endl;
+ m_session->setVerificationWord( word );
+ disconnected( BadPassword );
+}
+
+void YahooAccount::setAway(bool status, const QString &awayMessage)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if( awayMessage.isEmpty() )
+ slotGoStatus( status ? 2 : 0 );
+ else
+ slotGoStatus( status ? 99 : 0, awayMessage );
+}
+
+void YahooAccount::slotConnected()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Moved to slotLoginResponse for the moment" << endl;
+}
+
+void YahooAccount::slotGoOnline()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !isConnected() )
+ connect( m_protocol->Online );
+ else
+ slotGoStatus(0);
+}
+
+void YahooAccount::slotGoOffline()
+{
+ if ( isConnected() )
+ disconnect();
+ else
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+}
+
+KActionMenu *YahooAccount::actionMenu()
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ KActionMenu *theActionMenu = Kopete::Account::actionMenu();
+
+ theActionMenu->popupMenu()->insertSeparator();
+ theActionMenu->insert( m_editOwnYABEntry );
+ theActionMenu->insert( m_openInboxAction );
+ theActionMenu->insert( m_openYABAction );
+
+ return theActionMenu;
+}
+
+YahooContact *YahooAccount::contact( const QString &id )
+{
+ return static_cast<YahooContact *>(contacts()[id]);
+}
+
+bool YahooAccount::createContact(const QString &contactId, Kopete::MetaContact *parentContact )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << " contactId: " << contactId << endl;
+
+ if(!contact(contactId))
+ {
+ // FIXME: New Contacts are NOT added to KABC, because:
+ // How on earth do you tell if a contact is being deserialised or added brand new here?
+ // -- actualy (oct 2004) this method is only called when new contact are added. but this will
+ // maybe change and you will be noticed --Olivier
+ YahooContact *newContact = new YahooContact( this, contactId,
+ parentContact->displayName(), parentContact );
+ return newContact != 0;
+ }
+ else
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Contact already exists" << endl;
+
+ return false;
+}
+
+void YahooAccount::slotGlobalIdentityChanged( const QString &key, const QVariant &value )
+{
+ if( !configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) )
+ {
+ if ( key == Kopete::Global::Properties::self()->photo().key() )
+ {
+ setBuddyIcon( KURL( value.toString() ) );
+ }
+ }
+}
+
+void YahooAccount::sendFile( YahooContact *to, const KURL &url )
+{
+ QFile file( url.path() );
+
+ Kopete::Transfer *transfer = Kopete::TransferManager::transferManager()->addTransfer ( to,
+ url.fileName(), file.size(), to->userId(), Kopete::FileTransferInfo::Outgoing );
+ m_session->sendFile( transfer->info().transferId(), to->userId(), QString(), url );
+
+ QObject::connect( transfer, SIGNAL(result( KIO::Job * )), this, SLOT(slotFileTransferResult( KIO::Job * )) );
+
+ m_fileTransfers.insert( transfer->info().transferId(), transfer );
+}
+
+/***************************************************************************
+ * *
+ * Slot for KYahoo signals *
+ * *
+ ***************************************************************************/
+
+void YahooAccount::slotLoginResponse( int succ , const QString &url )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << succ << ", " << url << ")]" << endl;
+ QString errorMsg;
+ if ( succ == Yahoo::LoginOk || (succ == Yahoo::LoginDupl && m_lastDisconnectCode == 2) )
+ {
+ if ( initialStatus().internalStatus() )
+ {
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( initialStatus() );
+ }
+ else
+ {
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Online );
+ }
+
+
+ setBuddyIcon( myself()->property( Kopete::Global::Properties::self()->photo() ).value().toString() );
+ m_session->getYABEntries( m_YABLastMerge, m_YABLastRemoteRevision );
+ m_lastDisconnectCode = 0;
+ theHaveContactList = true;
+ return;
+ }
+ else if(succ == Yahoo::LoginPasswd)
+ {
+ initConnectionSignals( DeleteConnections );
+ password().setWrong();
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( BadPassword );
+ return;
+ }
+ else if(succ == Yahoo::LoginLock)
+ {
+ initConnectionSignals( DeleteConnections );
+ errorMsg = i18n("Could not log into Yahoo service: your account has been locked.\nVisit %1 to reactivate it.").arg(url);
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(), KMessageBox::Error, errorMsg);
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( BadUserName ); // FIXME: add a more appropriate disconnect reason
+ return;
+ }
+ else if( succ == Yahoo::LoginUname )
+ {
+ initConnectionSignals( DeleteConnections );
+ errorMsg = i18n("Could not log into the Yahoo service: the username specified was invalid.");
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(), KMessageBox::Error, errorMsg);
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( BadUserName );
+ return;
+ }
+ else if( succ == Yahoo::LoginDupl && m_lastDisconnectCode != 2 )
+ {
+ initConnectionSignals( DeleteConnections );
+ errorMsg = i18n("You have been logged out of the Yahoo service, possibly due to a duplicate login.");
+ KMessageBox::queuedMessageBox(Kopete::UI::Global::mainWidget(), KMessageBox::Error, errorMsg);
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( Manual ); // cannot use ConnectionReset since that will auto-reconnect
+ return;
+ }
+ else if( succ == Yahoo::LoginVerify )
+ {
+ initConnectionSignals( DeleteConnections );
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ YahooVerifyAccount *verifyDialog = new YahooVerifyAccount( this );
+ verifyDialog->setUrl( KURL(url) );
+ verifyDialog->show();
+ return;
+ }
+
+ //If we get here, something went wrong, so set ourselves to offline
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( Unknown );
+}
+
+void YahooAccount::slotDisconnected()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ initConnectionSignals( DeleteConnections );
+ if( !isConnected() )
+ return;
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( ConnectionReset ); // may reconnect
+
+ QString message;
+ message = i18n( "%1 has been disconnected.\nError message:\n%2 - %3" )
+ .arg( accountId() ).arg( m_session->error() ).arg( m_session->errorString() );
+ KNotification::event( "connection_lost", message, myself()->onlineStatus().protocolIcon() );
+}
+
+void YahooAccount::slotLoginFailed()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ initConnectionSignals( DeleteConnections );
+ static_cast<YahooContact *>( myself() )->setOnlineStatus( m_protocol->Offline );
+ disconnected( Manual ); // don't reconnect
+
+ QString message;
+ message = i18n( "There was an error while connecting %1 to the Yahoo server.\nError message:\n%2 - %3" )
+ .arg( accountId() ).arg( m_session->error() ).arg( m_session->errorString() );
+ KNotification::event( "cannot_connect", message, myself()->onlineStatus().protocolIcon() );
+}
+
+void YahooAccount::slotError( int level )
+{
+ // enum LogLevel { Debug, Info, Notice, Warning, Error, Critical };
+ if( level <= Client::Notice )
+ return;
+ else if( level <= Client::Warning )
+ KMessageBox::information( Kopete::UI::Global::mainWidget(), i18n( "%1\n\nReason: %2 - %3" ).arg(m_session->errorInformation())
+ .arg(m_session->error()).arg(m_session->errorString()), i18n( "Yahoo Plugin" ) );
+ else
+ KMessageBox::error( Kopete::UI::Global::mainWidget(), i18n( "%1\n\nReason: %2 - %3" ).arg(m_session->errorInformation())
+ .arg(m_session->error()).arg(m_session->errorString()), i18n( "Yahoo Plugin" ) );
+}
+
+void YahooAccount::slotGotBuddy( const QString &userid, const QString &alias, const QString &group )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ IDs[userid] = QPair<QString, QString>(group, alias);
+
+ // Serverside -> local
+ if ( !contact( userid ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "SS Contact " << userid << " is not in the contact list. Adding..." << endl;
+ Kopete::Group *g=Kopete::ContactList::self()->findGroup(group);
+ addContact(userid, alias.isEmpty() ? userid : alias, g, Kopete::Account::ChangeKABC);
+ }
+}
+
+void YahooAccount::slotAuthorizationAccepted( const QString &who )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QString message;
+ message = i18n( "User %1 has granted your authorization request." )
+ .arg( who );
+ KNotification::event( "kopete_authorization", message, 0 , 0 , 0 );
+
+ if( contact( who ) )
+ contact( who )->setOnlineStatus( m_protocol->Online );
+}
+
+void YahooAccount::slotAuthorizationRejected( const QString &who, const QString &msg )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QString message;
+ message = i18n( "User %1 has granted your authorization request.\n%2" )
+ .arg( who ).arg( msg );
+ KNotification::event( "kopete_authorization", message, 0 , 0 , 0 );
+}
+
+void YahooAccount::slotgotAuthorizationRequest( const QString &user, const QString &msg, const QString &name )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ Q_UNUSED( msg );
+ Q_UNUSED( name );
+ YahooContact *kc = contact( user );
+ Kopete::MetaContact *metaContact=0L;
+ if(kc)
+ metaContact=kc->metaContact();
+
+ int hideFlags=Kopete::UI::ContactAddedNotifyDialog::InfoButton;
+ if( metaContact && !metaContact->isTemporary() )
+ hideFlags |= Kopete::UI::ContactAddedNotifyDialog::AddCheckBox | Kopete::UI::ContactAddedNotifyDialog::AddGroupBox ;
+
+ Kopete::UI::ContactAddedNotifyDialog *dialog=
+ new Kopete::UI::ContactAddedNotifyDialog( user,QString::null,this, hideFlags );
+ QObject::connect(dialog,SIGNAL(applyClicked(const QString&)),
+ this,SLOT(slotContactAddedNotifyDialogClosed(const QString& )));
+ dialog->show();
+}
+
+void YahooAccount::slotContactAddedNotifyDialogClosed( const QString &user )
+{
+ const Kopete::UI::ContactAddedNotifyDialog *dialog =
+ dynamic_cast<const Kopete::UI::ContactAddedNotifyDialog *>(sender());
+ if(!dialog || !isConnected())
+ return;
+
+ m_session->sendAuthReply( user, dialog->authorized(), QString::null );
+
+ if(dialog->added())
+ {
+ dialog->addContact();
+ }
+}
+
+void YahooAccount::slotGotIgnore( const QStringList & /* igns */ )
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooAccount::slotGotIdentities( const QStringList & /* ids */ )
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooAccount::slotStatusChanged( const QString &who, int stat, const QString &msg, int away, int idle )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << who << " status: " << stat << " msg: " << msg << " away: " << away << " idle: " << idle <<endl;
+ YahooContact *kc = contact( who );
+
+ if( contact( who ) == myself() )
+ return;
+
+ if ( kc )
+ {
+ Kopete::OnlineStatus newStatus = m_protocol->statusFromYahoo( stat );
+ Kopete::OnlineStatus oldStatus = kc->onlineStatus();
+
+ if( newStatus == m_protocol->Custom ) {
+ if( away == 0 )
+ newStatus =m_protocol->Online;
+ kc->setProperty( m_protocol->awayMessage, msg);
+ }
+ else
+ kc->removeProperty( m_protocol->awayMessage );
+
+ if( newStatus != m_protocol->Offline &&
+ oldStatus == m_protocol->Offline && contact(who) != myself() )
+ {
+ //m_session->requestBuddyIcon( who ); // Try to get Buddy Icon
+
+ if ( !myself()->property( Kopete::Global::Properties::self()->photo() ).isNull() &&
+ myself()->onlineStatus() != m_protocol->Invisible &&
+ !kc->stealthed() )
+ {
+ kc->sendBuddyIconUpdate( m_session->pictureFlag() );
+ kc->sendBuddyIconChecksum( myself()->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt() );
+ }
+ }
+
+ //if( newStatus == static_cast<YahooProtocol*>( m_protocol )->Idle ) {
+ if( newStatus == m_protocol->Idle )
+ kc->setIdleTime( idle ? idle : 1 );
+ else
+ kc->setIdleTime( 0 );
+
+ kc->setOnlineStatus( newStatus );
+ }
+}
+
+void YahooAccount::slotStealthStatusChanged( const QString &who, Yahoo::StealthStatus state )
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Stealth Status of " << who << "changed to " << state << endl;
+
+ YahooContact* kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->setStealthed( state == Yahoo::StealthActive );
+}
+
+QString YahooAccount::prepareIncomingMessage( const QString &messageText )
+{
+ QString newMsgText( messageText );
+ QRegExp regExp;
+ int pos = 0;
+ newMsgText = stripMsgColorCodes( newMsgText );
+
+ kdDebug(YAHOO_GEN_DEBUG) << "Message after stripping color codes '" << newMsgText << "'" << endl;
+
+ newMsgText.replace( QString::fromLatin1( "&" ), QString::fromLatin1( "&amp;" ) );
+
+ // Replace Font tags
+ regExp.setMinimal( true );
+ regExp.setPattern( "<font([^>]*)size=\"([^>]*)\"([^>]*)>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( newMsgText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsgText.replace( regExp, QString::fromLatin1("<font\\1style=\"font-size:\\2pt\">" ) );
+ }
+ }
+
+ // Remove FADE and ALT tags
+ regExp.setPattern( "<[/]*FADE([^>]*)>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( newMsgText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsgText.replace( regExp, QString::fromLatin1("" ) );
+
+ }
+ }
+ regExp.setPattern( "<[/]*ALT([^>]*)>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( newMsgText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsgText.replace( regExp, QString::fromLatin1("" ) );
+ }
+ }
+
+ // Replace < and > in text
+ regExp.setPattern( "<(?!(/*(font.*|[\"fbui])>))" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( newMsgText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsgText.replace( regExp, QString::fromLatin1("&lt;" ) );
+ }
+ }
+ regExp.setPattern( "([^\"bui])>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( newMsgText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsgText.replace( regExp, QString::fromLatin1("\\1&gt;" ) );
+ }
+ }
+
+ // add closing tags when needed
+ regExp.setMinimal( false );
+ regExp.setPattern( "(<b>.*)(?!</b>)" );
+ newMsgText.replace( regExp, QString::fromLatin1("\\1</b>" ) );
+ regExp.setPattern( "(<i>.*)(?!</i>)" );
+ newMsgText.replace( regExp, QString::fromLatin1("\\1</i>" ) );
+ regExp.setPattern( "(<u>.*)(?!</u>)" );
+ newMsgText.replace( regExp, QString::fromLatin1("\\1</u>" ) );
+ regExp.setPattern( "(<font.*)(?!</font>)" );
+ newMsgText.replace( regExp, QString::fromLatin1("\\1</font>" ) );
+
+ newMsgText.replace( QString::fromLatin1( "\r" ), QString::fromLatin1( "<br/>" ) );
+
+ return newMsgText;
+}
+
+void YahooAccount::slotGotIm( const QString &who, const QString &msg, long tm, int /*stat*/)
+{
+ QFont msgFont;
+ QDateTime msgDT;
+ Kopete::ContactPtrList justMe;
+
+ if( !contact( who ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Adding contact " << who << endl;
+ addContact( who,who, 0L, Kopete::Account::Temporary );
+ }
+
+ //Parse the message for it's properties
+ kdDebug(YAHOO_GEN_DEBUG) << "Original message is '" << msg << "'" << endl;
+ //kdDebug(YAHOO_GEN_DEBUG) << "Message color is " << getMsgColor(msg) << endl;
+ QColor fgColor = getMsgColor( msg );
+ if (tm == 0)
+ msgDT.setTime_t(time(0L));
+ else
+ msgDT.setTime_t(tm, Qt::LocalTime);
+
+ QString newMsgText = prepareIncomingMessage( msg );
+
+ kdDebug(YAHOO_GEN_DEBUG) << "Message after fixing font tags '" << newMsgText << "'" << endl;
+
+ Kopete::ChatSession *mm = contact(who)->manager(Kopete::Contact::CanCreate);
+
+ // Tell the message manager that the buddy is done typing
+ mm->receivedTypingMsg(contact(who), false);
+
+ justMe.append(myself());
+
+ Kopete::Message kmsg(msgDT, contact(who), justMe, newMsgText,
+ Kopete::Message::Inbound , Kopete::Message::RichText);
+
+ kmsg.setFg( fgColor );
+ mm->appendMessage(kmsg);
+}
+
+void YahooAccount::slotGotBuzz( const QString &who, long tm )
+{
+ QFont msgFont;
+ QDateTime msgDT;
+ Kopete::ContactPtrList justMe;
+
+ if( !contact( who ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Adding contact " << who << endl;
+ addContact( who,who, 0L, Kopete::Account::Temporary );
+ }
+
+ if (tm == 0)
+ msgDT.setTime_t(time(0L));
+ else
+ msgDT.setTime_t(tm, Qt::LocalTime);
+
+ justMe.append(myself());
+
+ QString buzzMsgText = i18n("This string is shown when the user is buzzed by a contact", "Buzz!!");
+
+ Kopete::Message kmsg(msgDT, contact(who), justMe, buzzMsgText, Kopete::Message::Inbound,
+ Kopete::Message::PlainText, QString::null, Kopete::Message::TypeAction);
+ QColor fgColor( "gold" );
+ kmsg.setFg( fgColor );
+
+ Kopete::ChatSession *mm = contact(who)->manager(Kopete::Contact::CanCreate);
+ mm->appendMessage(kmsg);
+ // Emit the buzz notification.
+ mm->emitNudgeNotification();
+}
+
+void YahooAccount::slotGotConfInvite( const QString & who, const QString & room, const QString &msg, const QStringList &members )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << who << " has invited you to join the conference \"" << room << "\" : " << msg << endl;
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Members: " << members << endl;
+
+ if( !m_pendingConfInvites.contains( room ) ) // We have to keep track of the invites as the server will send the same invite twice if it gets canceled by the host
+ m_pendingConfInvites.push_back( room );
+ else
+ {
+ return;
+ }
+
+ QString m = who;
+ QStringList myMembers;
+ myMembers.push_back( who );
+ for( QStringList::const_iterator it = ++members.begin(); it != members.end(); it++ )
+ {
+ if( *it != m_session->userId() )
+ {
+ m.append( QString(", %1").arg( *it ) );
+ myMembers.push_back( *it );
+ }
+ }
+ if( KMessageBox::Yes == KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(),
+ i18n("%1 has invited you to join a conference with %2.\n\nHis message: %3\n\n Accept?")
+ .arg(who).arg(m).arg(msg), QString::null, i18n("Accept"), i18n("Ignore") ) )
+ {
+ m_session->joinConference( room, myMembers );
+ if( !m_conferences[room] )
+ {
+ Kopete::ContactPtrList others;
+ YahooConferenceChatSession *session = new YahooConferenceChatSession( room, protocol(), myself(), others );
+ m_conferences[room] = session;
+
+ QObject::connect( session, SIGNAL(leavingConference( YahooConferenceChatSession * ) ), this, SLOT( slotConfLeave( YahooConferenceChatSession * ) ) );
+
+ for ( QValueList<QString>::ConstIterator it = myMembers.begin(); it != myMembers.end(); ++it )
+ {
+ YahooContact * c = contact( *it );
+ if ( !c )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Adding contact " << *it << " to conference." << endl;
+ addContact( *it,*it, 0L, Kopete::Account::Temporary );
+ c = contact( *it );
+ }
+ session->joined( c );
+ }
+ session->view( true )->raise( false );
+ }
+ }
+ else
+ m_session->declineConference( room, myMembers, QString::null );
+
+ m_pendingConfInvites.remove( room );
+}
+
+void YahooAccount::prepareConference( const QString &who )
+{
+ QString room;
+ for( int i = 0; i < 22; i++ )
+ {
+ char c = rand()%52;
+ room += (c > 25) ? c + 71 : c + 65;
+ }
+ room = QString("%1-%2--").arg(accountId()).arg(room);
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "The generated roomname is: " << room << endl;
+
+ QStringList buddies;
+ QDictIterator<Kopete::Contact> it( contacts() );
+ for( ; it.current(); ++it )
+ {
+ if( (*it) != myself() )
+ buddies.push_back( (*it)->contactId() );
+ }
+
+ YahooInviteListImpl *dlg = new YahooInviteListImpl( Kopete::UI::Global::mainWidget() );
+ QObject::connect( dlg, SIGNAL( readyToInvite( const QString &, const QStringList &, const QStringList &, const QString & ) ),
+ this, SLOT( slotInviteConference( const QString &, const QStringList &, const QStringList &, const QString & ) ) );
+ dlg->setRoom( room );
+ dlg->fillFriendList( buddies );
+ dlg->addInvitees( QStringList( who ) );
+ dlg->show();
+}
+
+void YahooAccount::slotInviteConference( const QString &room, const QStringList &members, const QStringList &participants, const QString &msg )
+{
+ Q_UNUSED( participants );
+kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Inviting " << members << " to the conference " << room << ". Message: " << msg << endl;
+ m_session->inviteConference( room, members, msg );
+
+ Kopete::ContactPtrList others;
+ YahooConferenceChatSession *session = new YahooConferenceChatSession( room, protocol(), myself(), others );
+ m_conferences[room] = session;
+
+ QObject::connect( session, SIGNAL(leavingConference( YahooConferenceChatSession * ) ), this, SLOT( slotConfLeave( YahooConferenceChatSession * ) ) );
+
+ session->joined( static_cast< YahooContact *>(myself()) );
+ session->view( true )->raise( false );
+}
+
+void YahooAccount::slotAddInviteConference( const QString &room, const QStringList &who, const QStringList &members, const QString &msg )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Inviting " << who << " to the conference " << room << ". Message: " << msg << endl;
+ m_session->addInviteConference( room, who, members, msg );
+}
+
+void YahooAccount::slotConfUserDecline( const QString &who, const QString &room, const QString &msg)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if( !m_conferences.contains( room ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Error. No chatsession for this conference found." << endl;
+ return;
+ }
+
+ YahooConferenceChatSession *session = m_conferences[room];
+
+ QString body = i18n( "%1 declined to join the conference: \"%2\"" ).arg( who ).arg( msg );
+ Kopete::Message message = Kopete::Message( contact( who ), myself(), body, Kopete::Message::Internal, Kopete::Message::PlainText );
+
+ session->appendMessage( message );
+}
+
+void YahooAccount::slotConfUserJoin( const QString &who, const QString &room )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !m_conferences.contains( room ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Error. No chatsession for this conference found." << endl;
+ return;
+ }
+
+ YahooConferenceChatSession *session = m_conferences[room];
+ if( !contact( who ) )
+ {
+ addContact( who, who, 0L, Kopete::Account::Temporary );
+ }
+ session->joined( contact( who ) );
+}
+
+void YahooAccount::slotConfUserLeave( const QString & who, const QString &room )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !m_conferences.contains( room ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Error. No chatsession for this conference found." << endl;
+ return;
+ }
+
+ YahooConferenceChatSession *session = m_conferences[room];
+ if( !contact( who ) )
+ {
+ addContact( who, who, 0L, Kopete::Account::Temporary );
+ }
+ session->left( contact( who ) );
+}
+
+void YahooAccount::slotConfLeave( YahooConferenceChatSession *s )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !s )
+ return;
+ QStringList members;
+ for( Kopete::ContactPtrList::iterator it = s->members().begin(); it != s->members().end(); ++it )
+ {
+ if( (*it) == myself() )
+ continue;
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Member: " << (*it)->contactId() << endl;
+ members.append( (*it)->contactId() );
+ }
+ m_session->leaveConference( s->room(), members );
+ m_conferences.remove( s->room() );
+}
+
+void YahooAccount::slotConfMessage( const QString &who, const QString &room, const QString &msg )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if( !m_conferences.contains( room ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Error. No chatsession for this conference found." << endl;
+ return;
+ }
+
+ YahooConferenceChatSession *session = m_conferences[room];
+
+ QFont msgFont;
+ QDateTime msgDT;
+ Kopete::ContactPtrList justMe;
+
+ if( !contact( who ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << "Adding contact " << who << endl;
+ addContact( who,who, 0L, Kopete::Account::Temporary );
+ }
+ kdDebug(YAHOO_GEN_DEBUG) << "Original message is '" << msg << "'" << endl;
+
+ QColor fgColor = getMsgColor( msg );
+ msgDT.setTime_t(time(0L));
+
+ QString newMsgText = prepareIncomingMessage( msg );
+
+ kdDebug(YAHOO_GEN_DEBUG) << "Message after fixing font tags '" << newMsgText << "'" << endl;
+ session->receivedTypingMsg(contact(who), false);
+
+ justMe.append(myself());
+
+ Kopete::Message kmsg(msgDT, contact(who), justMe, newMsgText,
+ Kopete::Message::Inbound , Kopete::Message::RichText);
+
+ kmsg.setFg( fgColor );
+ session->appendMessage(kmsg);
+}
+
+void YahooAccount::sendConfMessage( YahooConferenceChatSession *s, Kopete::Message &message )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QStringList members;
+ for( Kopete::ContactPtrList::iterator it = s->members().begin(); it != s->members().end(); ++it )
+ {
+ if( (*it) == myself() )
+ continue;
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Member: " << (*it)->contactId() << endl;
+ members.append( (*it)->contactId() );
+ }
+ m_session->sendConferenceMessage( s->room(), members, YahooContact::prepareMessage( message.escapedBody() ) );
+}
+
+void YahooAccount::slotGotYABRevision( long rev, bool merged )
+{
+ if( merged )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Merge Revision received: " << rev << endl;
+ configGroup()->writeEntry( "YABLastMerge", rev );
+ m_YABLastMerge = rev;
+ }
+ else
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Remote Revision received: " << rev << endl;
+ configGroup()->writeEntry( "YABLastRemoteRevision", rev );
+ m_YABLastRemoteRevision = rev;
+ }
+}
+
+void YahooAccount::slotGotYABEntry( YABEntry *entry )
+{
+ YahooContact* kc = contact( entry->yahooId );
+ if( !kc )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "YAB entry received for a contact not on our buddylist: " << entry->yahooId << endl;
+ delete entry;
+ }
+ else
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "YAB entry received for: " << entry->yahooId << endl;
+ if( entry->source == YABEntry::SourceYAB )
+ {
+ kc->setYABEntry( entry );
+ }
+ else if( entry->source == YABEntry::SourceContact )
+ {
+ entry->YABId = kc->yabEntry()->YABId;
+ YahooUserInfoDialog *dlg = new YahooUserInfoDialog( kc, Kopete::UI::Global::mainWidget(), "yahoo userinfo" );
+ dlg->setData( *entry );
+ dlg->setAccountConnected( isConnected() );
+ dlg->show();
+ QObject::connect( dlg, SIGNAL(saveYABEntry( YABEntry & )), this, SLOT(slotSaveYABEntry( YABEntry & )));
+ delete entry;
+ }
+ }
+}
+
+void YahooAccount::slotSaveYABEntry( YABEntry &entry )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "YABId: " << entry.YABId << endl;
+ if( entry.YABId > 0 )
+ m_session->saveYABEntry( entry );
+ else
+ m_session->addYABEntry( entry );
+}
+
+void YahooAccount::slotModifyYABEntryError( YABEntry *entry, const QString &msg )
+{
+ YahooContact* kc = contact( entry->yahooId );
+ if( kc )
+ kc->setYABEntry( entry, true );
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), msg, i18n( "Yahoo Plugin" ) );
+}
+
+void YahooAccount::slotGotFile( const QString & who, const QString & url , long /* expires */, const QString & msg ,
+ const QString & fname, unsigned long fesize )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Received File from " << who << ": " << msg << endl;
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Filename :" << fname << " size:" << fesize << endl;
+
+ Kopete::TransferManager::transferManager()->askIncomingTransfer( contact( who ) , fname, fesize, msg, url );
+
+ if( m_pendingFileTransfers.empty() )
+ {
+ QObject::connect( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString& ) ),
+ this, SLOT( slotReceiveFileAccepted( Kopete::Transfer *, const QString& ) ) );
+ QObject::connect( Kopete::TransferManager::transferManager(), SIGNAL( refused(const Kopete::FileTransferInfo& ) ),
+ this, SLOT( slotReceiveFileRefused( const Kopete::FileTransferInfo& ) ) );
+ }
+ m_pendingFileTransfers.append( url );
+}
+
+void YahooAccount::slotReceiveFileAccepted(Kopete::Transfer *transfer, const QString& fileName)
+{
+ if( !m_pendingFileTransfers.contains( transfer->info().internalId() ) )
+ return;
+
+ m_pendingFileTransfers.remove( transfer->info().internalId() );
+
+ //Create directory if it doesn't already exist
+ QDir dir;
+ QString path = QFileInfo( fileName ).dirPath();
+ for( int i = 1; i <= path.contains('/'); ++i )
+ {
+ if( !dir.exists( path.section( '/', 0, i ) ) )
+ {
+ dir.mkdir( path.section( '/', 0, i) );
+ }
+ }
+
+ m_session->receiveFile( transfer->info().transferId(), transfer->info().contact()->contactId(), transfer->info().internalId(), fileName );
+ m_fileTransfers.insert( transfer->info().transferId(), transfer );
+ QObject::connect( transfer, SIGNAL(result( KIO::Job * )), this, SLOT(slotFileTransferResult( KIO::Job * )) );
+
+ if( m_pendingFileTransfers.empty() )
+ {
+ QObject::disconnect( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString& ) ),
+ this, SLOT( slotReceiveFileAccepted( Kopete::Transfer *, const QString& ) ) );
+ QObject::disconnect( Kopete::TransferManager::transferManager(), SIGNAL( refused(const Kopete::FileTransferInfo& ) ),
+ this, SLOT( slotReceiveFileRefused( const Kopete::FileTransferInfo& ) ) );
+ }
+}
+
+void YahooAccount::slotReceiveFileRefused( const Kopete::FileTransferInfo& info )
+{
+ if( !m_pendingFileTransfers.contains( info.internalId() ) )
+ return;
+
+ m_pendingFileTransfers.remove( info.internalId() );
+ m_session->rejectFile( info.contact()->contactId(), info.internalId() );
+
+ if( m_pendingFileTransfers.empty() )
+ {
+ QObject::disconnect( Kopete::TransferManager::transferManager(), SIGNAL( accepted( Kopete::Transfer *, const QString& ) ),
+ this, SLOT( slotReceiveFileAccepted( Kopete::Transfer *, const QString& ) ) );
+ QObject::disconnect( Kopete::TransferManager::transferManager(), SIGNAL( refused(const Kopete::FileTransferInfo& ) ),
+ this, SLOT( slotReceiveFileRefused( const Kopete::FileTransferInfo& ) ) );
+ }
+}
+
+void YahooAccount::slotFileTransferBytesProcessed( unsigned int transferId, unsigned int bytes )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Transfer: " << transferId << " Bytes:" << bytes << endl;
+ Kopete::Transfer *t = m_fileTransfers[transferId];
+ if( !t )
+ return;
+
+ t->slotProcessed( bytes );
+}
+
+void YahooAccount::slotFileTransferComplete( unsigned int transferId )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ Kopete::Transfer *t = m_fileTransfers[transferId];
+ if( !t )
+ return;
+
+ t->slotComplete();
+ m_fileTransfers.remove( transferId );
+}
+
+void YahooAccount::slotFileTransferError( unsigned int transferId, int error, const QString &desc )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ Kopete::Transfer *t = m_fileTransfers[transferId];
+ if( !t )
+ return;
+
+ t->slotError( error, desc );
+ m_fileTransfers.remove( transferId );
+}
+
+void YahooAccount::slotFileTransferResult( KIO::Job *job )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ const Kopete::Transfer *t = dynamic_cast< const Kopete::Transfer * >( job );
+
+ if( !t )
+ return;
+
+ if( t->error() == KIO::ERR_USER_CANCELED )
+ {
+ m_session->cancelFileTransfer( t->info().transferId() );
+ m_fileTransfers.remove( t->info().transferId() );
+ }
+}
+
+void YahooAccount::slotContactAdded( const QString & /* myid */, const QString & /* who */, const QString & /* msg */ )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << myid << " " << who << " " << msg << endl;
+}
+
+void YahooAccount::slotRejected( const QString & /* who */, const QString & /* msg */ )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooAccount::slotTypingNotify( const QString &who, int what )
+{
+ emit receivedTypingMsg(who, what);
+}
+
+void YahooAccount::slotGameNotify( const QString & /* who */, int /* stat */ )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooAccount::slotMailNotify( const QString& from, const QString& /* subject */, int cnt )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Mail count: " << cnt << endl;
+
+ if ( cnt > m_currentMailCount && from.isEmpty() )
+ {
+ QObject::connect(KNotification::event( "yahoo_mail", i18n( "You have one unread message in your Yahoo inbox.",
+ "You have %n unread messages in your Yahoo inbox.", cnt ), 0 , 0 , i18n( "Open Inbox..." ) ),
+ SIGNAL(activated(unsigned int ) ) , this, SLOT( slotOpenInbox() ) );
+ m_currentMailCount = cnt;
+ }
+ else if ( cnt > m_currentMailCount )
+ { kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "attempting to trigger event" << endl;
+ QObject::connect(KNotification::event( "yahoo_mail", i18n( "You have a message from %1 in your Yahoo inbox.").arg(from)
+ , 0 , 0 , i18n( "Open Inbox..." ) ), SIGNAL(activated(unsigned int ) ) , this, SLOT( slotOpenInbox() ) );
+ m_currentMailCount = cnt;
+ }
+}
+
+void YahooAccount::slotSystemMessage( const QString & /* msg */ )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << msg << endl;
+}
+
+void YahooAccount::slotRemoveHandler( int /* fd */ )
+{
+// kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooAccount::slotGotWebcamInvite( const QString& who )
+{
+ YahooContact* kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+
+ if( m_pendingWebcamInvites.contains( who ) )
+ return;
+
+ m_pendingWebcamInvites.append( who );
+
+ if( KMessageBox::Yes == KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), i18n("%1 has invited you to view his/her webcam. Accept?")
+ .arg(who), QString::null, i18n("Accept"), i18n("Ignore") ) )
+ {
+ m_pendingWebcamInvites.remove( who );
+ m_session->requestWebcam( who );
+ }
+}
+void YahooAccount::slotWebcamNotAvailable( const QString &who )
+{
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n("Webcam for %1 is not available.").arg(who), i18n( "Yahoo Plugin" ) );
+}
+
+void YahooAccount::slotGotWebcamImage( const QString& who, const QPixmap& image )
+{
+ YahooContact* kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->receivedWebcamImage( image );
+}
+
+void YahooAccount::slotPictureStatusNotiy( const QString &who, int status)
+{
+ YahooContact *kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " changed picture status to" << status << endl;
+}
+
+void YahooAccount::slotGotBuddyIconChecksum(const QString &who, int checksum)
+{
+ YahooContact *kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+
+ if ( checksum == kc->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt() &&
+ QFile::exists( locateLocal( "appdata", "yahoopictures/"+ who.lower().replace(QRegExp("[./~]"),"-") +".png" ) ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Icon already exists. I will not request it again." << endl;
+ return;
+ } else
+ m_session->requestPicture( who );
+}
+
+void YahooAccount::slotGotBuddyIconInfo(const QString &who, KURL url, int checksum)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ YahooContact *kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+
+ if ( checksum == kc->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt() &&
+ QFile::exists( locateLocal( "appdata", "yahoopictures/"+ who.lower().replace(QRegExp("[./~]"),"-") +".png" ) ))
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Icon already exists. I will not download it again." << endl;
+ return;
+ } else
+ m_session->downloadPicture( who, url, checksum );
+}
+
+void YahooAccount::slotGotBuddyIcon( const QString &who, KTempFile *file, int checksum )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ YahooContact *kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->setDisplayPicture( file, checksum );
+}
+void YahooAccount::slotGotBuddyIconRequest( const QString & who )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ YahooContact *kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->sendBuddyIconInfo( myself()->property( YahooProtocol::protocol()->iconRemoteUrl ).value().toString(),
+ myself()->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt() );
+}
+
+void YahooAccount::setBuddyIcon( KURL url )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Url: " << url.path() << endl;
+ QString s = url.path();
+ if ( url.path().isEmpty() )
+ {
+ myself()->removeProperty( Kopete::Global::Properties::self()->photo() );
+ myself()->removeProperty( YahooProtocol::protocol()->iconRemoteUrl );
+ myself()->removeProperty( YahooProtocol::protocol()->iconExpire );
+ myself()->removeProperty( YahooProtocol::protocol()->iconCheckSum );
+ m_session->setPictureFlag( 0 );
+
+ slotBuddyIconChanged( QString::null );
+ }
+ else
+ {
+ QImage image( url.path() );
+ QString newlocation( locateLocal( "appdata", "yahoopictures/"+ url.fileName().lower() ) ) ;
+ QFile iconFile( newlocation );
+ QByteArray data;
+ uint expire = myself()->property( YahooProtocol::protocol()->iconExpire ).value().toInt();
+
+ if ( image.isNull() ) {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n( "<qt>The selected buddy icon could not be opened. <br>Please set a new buddy icon.</qt>" ), i18n( "Yahoo Plugin" ) );
+ return;
+ }
+ image = image.smoothScale( 96, 96, QImage::ScaleMin );
+ if(image.width() < image.height())
+ {
+ image = image.copy((image.width()-image.height())/2, 0, 96, 96);
+ }
+ else if(image.height() < image.width())
+ {
+ image = image.copy(0, (image.height()-image.width())/2, 96, 96);
+ }
+
+ if( !image.save( newlocation, "PNG" ) || !iconFile.open(IO_ReadOnly) )
+ {
+ KMessageBox::sorry( Kopete::UI::Global::mainWidget(), i18n( "An error occurred when trying to change the display picture." ), i18n( "Yahoo Plugin" ) );
+ return;
+ }
+
+ data = iconFile.readAll();
+ iconFile.close();
+
+ // create checksum - taken from qhash.cpp of qt4
+ const uchar *p = reinterpret_cast<const uchar *>(data.data());
+ int n = data.size();
+ uint checksum = 0;
+ uint g;
+ while (n--)
+ {
+ checksum = (checksum << 4) + *p++;
+ if ((g = (checksum & 0xf0000000)) != 0)
+ checksum ^= g >> 23;
+ checksum &= ~g;
+ }
+
+ myself()->setProperty( Kopete::Global::Properties::self()->photo() , newlocation );
+ configGroup()->writeEntry( "iconLocalUrl", newlocation );
+
+ if ( checksum != static_cast<uint>(myself()->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt()) ||
+ QDateTime::currentDateTime().toTime_t() > expire )
+ {
+ myself()->setProperty( YahooProtocol::protocol()->iconCheckSum, checksum );
+ myself()->setProperty( YahooProtocol::protocol()->iconExpire , QDateTime::currentDateTime().toTime_t() + 604800 );
+ configGroup()->writeEntry( "iconCheckSum", checksum );
+ configGroup()->writeEntry( "iconExpire", myself()->property( YahooProtocol::protocol()->iconExpire ).value().toInt() );
+ if ( m_session != 0 )
+ m_session->uploadPicture( newlocation );
+ }
+ }
+}
+
+void YahooAccount::slotBuddyIconChanged( const QString &url )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QDictIterator<Kopete::Contact> it( contacts() );
+ int checksum = myself()->property( YahooProtocol::protocol()->iconCheckSum ).value().toInt();
+
+ if ( url.isEmpty() ) // remove pictures from buddie's clients
+ {
+ checksum = 0;
+ m_session->setPictureFlag( 0 );
+ }
+ else
+ {
+ myself()->setProperty( YahooProtocol::protocol()->iconRemoteUrl, url );
+ configGroup()->writeEntry( "iconRemoteUrl", url );
+ m_session->setPictureFlag( 2 );
+ m_session->sendPictureChecksum( checksum, QString::null );
+ }
+}
+
+void YahooAccount::slotWebcamReadyForTransmission()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !m_webcam )
+ {
+ m_webcam = new YahooWebcam( this );
+ QObject::connect( m_webcam, SIGNAL(webcamClosing()), this, SLOT(slotOutgoingWebcamClosing()) );
+ }
+
+ m_webcam->startTransmission();
+}
+
+void YahooAccount::slotWebcamStopTransmission()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if( m_webcam )
+ {
+ m_webcam->stopTransmission();
+ }
+}
+
+void YahooAccount::slotOutgoingWebcamClosing()
+{
+ m_session->closeOutgoingWebcam();
+ m_webcam->deleteLater();
+ m_webcam = 0L;
+}
+
+void YahooAccount::slotWebcamViewerJoined( const QString &viewer )
+{
+ if( m_webcam )
+ {
+ m_webcam->addViewer( viewer );
+ }
+}
+
+void YahooAccount::slotWebcamViewerRequest( const QString &viewer )
+{
+ if( KMessageBox::Yes == KMessageBox::questionYesNo( Kopete::UI::Global::mainWidget(), i18n("%1 wants to view your webcam. Grant access?")
+ .arg(viewer), QString::null, i18n("Accept"), i18n("Ignore") ) )
+ m_session->grantWebcamAccess( viewer );
+}
+
+void YahooAccount::slotWebcamViewerLeft( const QString &viewer )
+{
+ if( m_webcam )
+ {
+ m_webcam->removeViewer( viewer );
+ }
+}
+
+void YahooAccount::slotWebcamClosed( const QString& who, int reason )
+{
+ YahooContact* kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->webcamClosed( reason );
+}
+
+void YahooAccount::slotWebcamPaused( const QString &who )
+{
+ YahooContact* kc = contact( who );
+ if ( kc == NULL ) {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact " << who << " doesn't exist." << endl;
+ return;
+ }
+ kc->webcamPaused();
+}
+
+void YahooAccount::setOnlineStatus( const Kopete::OnlineStatus& status , const QString &reason)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if ( myself()->onlineStatus().status() == Kopete::OnlineStatus::Offline &&
+ status.status() != Kopete::OnlineStatus::Offline )
+ {
+ if( !reason.isEmpty() )
+ m_session->setStatusMessageOnConnect( reason );
+ connect( status );
+ }
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline &&
+ status.status() == Kopete::OnlineStatus::Offline )
+ {
+ disconnect();
+ }
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline &&
+ status.internalStatus() == 2 && !reason.isEmpty())
+ {
+ slotGoStatus( 99, reason );
+ }
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline &&
+ status.internalStatus() == 99 && reason.isEmpty())
+ {
+ slotGoStatus( 2, reason );
+ }
+ else if ( myself()->onlineStatus().status() != Kopete::OnlineStatus::Offline )
+ {
+ slotGoStatus( status.internalStatus(), reason );
+ }
+}
+
+void YahooAccount::slotOpenInbox()
+{
+ KRun::runURL( KURL( QString::fromLatin1("http://mail.yahoo.com/") ) , "text/html" );
+}
+
+void YahooAccount::slotOpenYAB()
+{
+ KRun::runURL( KURL( QString::fromLatin1("http://address.yahoo.com/") ) , "text/html" );
+}
+
+void YahooAccount::slotEditOwnYABEntry()
+{
+ myself()->slotUserInfo();
+}
+
+#include "yahooaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+//kate: indent-mode csands; tab-width 4;
diff --git a/kopete/protocols/yahoo/yahooaccount.h b/kopete/protocols/yahoo/yahooaccount.h
new file mode 100644
index 00000000..cc01ff91
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooaccount.h
@@ -0,0 +1,295 @@
+/*
+ yahooaccount.h - Manages a single Yahoo account
+
+ Copyright (c) 2003 by Gav Wood <gav@kde.org>
+ Copyright (c) 2003 by Matt Rogers <mattrogers@sbcglobal.net>
+ Based on code by Olivier Goffart <ogoffart @ kde.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+
+#ifndef YAHOOIDENTITY_H
+#define YAHOOIDENTITY_H
+
+// Qt
+#include <qobject.h>
+#include <qmap.h>
+
+// Kopete
+#include "kopetepasswordedaccount.h"
+#include "kopeteawaydialog.h"
+
+// Local
+#include "yahooprotocol.h"
+#include "yahootypes.h"
+
+class QColor;
+class KAction;
+class KActionMenu;
+class YahooContact;
+class YahooAccount;
+class YahooProtocol;
+class YahooWebcam;
+class YahooConferenceChatSession;
+class KTempFile;
+struct KURL;
+namespace Kopete{
+class Transfer;
+class ChatSession;
+class FileTransferInfo;
+}
+class Client;
+class YABEntry;
+namespace KIO{
+ class Job;
+}
+class YahooAwayDialog : public KopeteAwayDialog
+{
+public:
+ YahooAwayDialog(YahooAccount *account, QWidget *parent = 0, const char *name = 0);
+ virtual void setAway(int awayType);
+
+private:
+ YahooAccount *theAccount;
+};
+
+class YahooAccount : public Kopete::PasswordedAccount
+{
+ Q_OBJECT
+
+public:
+
+ enum SignalConnectionType { MakeConnections, DeleteConnections };
+
+ YahooAccount(YahooProtocol *parent,const QString& accountID, const char *name = 0L);
+ ~YahooAccount();
+
+ /*
+ * Returns a contact of name @p id
+ */
+ YahooContact *contact(const QString &id);
+
+ virtual KActionMenu* actionMenu();
+
+ /**
+ * Sets the yahoo away status
+ */
+ virtual void setAway(bool, const QString &);
+
+ /**
+ * The session
+ */
+ Client *yahooSession();
+
+ /**
+ * Returns true if contact @p id is on the server-side contact list
+ */
+ bool isOnServer(const QString &id) { return IDs.contains(id); }
+
+ /**
+ * Returns true if we have the server-side contact list
+ */
+ bool haveContactList() const { return theHaveContactList; }
+
+ void setUseServerGroups(bool newSetting);
+
+ void setImportContacts(bool newSetting);
+
+ /**
+ * Set the pager server
+ */
+ void setServer( const QString &server );
+
+ /**
+ * Set the port of the pager server
+ */
+ void setPort( int port );
+
+ /**
+ * Set Buddy Icon
+ */
+ void setBuddyIcon( KURL url );
+
+ void verifyAccount( const QString &word );
+
+ void sendConfMessage( YahooConferenceChatSession *s, Kopete::Message &message );
+ void prepareConference( const QString &who );
+ void sendFile( YahooContact *to, const KURL &url );
+public slots:
+ /**
+ * Connect to the Yahoo service
+ */
+ virtual void connectWithPassword( const QString & );
+ /**
+ * Disconnect from the Yahoo service
+ */
+ virtual void disconnect();
+
+ /** Reimplemented from Kopete::Account */
+ void setOnlineStatus( const Kopete::OnlineStatus&, const QString &reason = QString::null);
+
+
+signals:
+ /**
+ * Emitted when we receive notification that the person we're talking to is typing
+ */
+ void receivedTypingMsg(const QString &contactId, bool isTyping);
+
+ /**
+ * Emitted when our Buddy Icon has changed
+ */
+ void signalBuddyIconChanged( int type );
+
+protected:
+ /**
+ * Adds our Yahoo contact to a metacontact
+ */
+ virtual bool createContact(const QString &contactId, Kopete::MetaContact *parentContact);
+
+ /**
+ * Gets the just-received message color
+ */
+ QColor getMsgColor(const QString& msg);
+ /**
+ * Remove color codes from a message
+ */
+ QString stripMsgColorCodes(const QString& msg);
+
+protected slots:
+ void slotConnected();
+ void slotGoOnline();
+ void slotGoOffline();
+ void slotOpenInbox(); // Open Yahoo Mailbox in browser
+ void slotOpenYAB(); // Open Yahoo Addressbook in browser
+ void slotEditOwnYABEntry(); // Show own Yahoo Addressbook entry
+
+ void slotGoStatus(int status, const QString &awayMessage = QString::null);
+ void slotLoginResponse(int succ, const QString &url);
+ void slotDisconnected();
+ void slotLoginFailed();
+ void slotGotBuddy(const QString &userid, const QString &alias, const QString &group);
+ void slotAuthorizationAccepted( const QString &who );
+ void slotAuthorizationRejected( const QString &who, const QString &msg );
+ void slotgotAuthorizationRequest( const QString &, const QString &, const QString & );
+ void slotContactAddedNotifyDialogClosed( const QString & );
+ void slotGotIgnore(const QStringList &);
+ void slotGotIdentities(const QStringList &);
+ void slotStatusChanged(const QString &who, int stat, const QString &msg, int away, int idle);
+ void slotStealthStatusChanged(const QString &who, Yahoo::StealthStatus state);
+ void slotGotIm(const QString &who, const QString &msg, long tm, int stat);
+ void slotGotBuzz(const QString &who, long tm);
+ void slotGotConfInvite(const QString &who, const QString &room, const QString &msg, const QStringList &members);
+ void slotConfUserDecline(const QString &who, const QString &room, const QString &msg);
+ void slotConfUserJoin(const QString &who, const QString &room);
+ void slotConfUserLeave(const QString &who, const QString &room);
+ void slotConfMessage(const QString &who, const QString &room, const QString &msg);
+ void slotConfLeave( YahooConferenceChatSession *s );
+ void slotInviteConference( const QString &room, const QStringList &who, const QStringList &members, const QString &msg );
+ void slotAddInviteConference( const QString &room, const QStringList &who, const QStringList &members, const QString &msg );
+ void slotGotFile(const QString &who, const QString &url, long expires, const QString &msg, const QString &fname, unsigned long fesize);
+ void slotContactAdded(const QString &myid, const QString &who, const QString &msg);
+ void slotRejected(const QString &, const QString &);
+ void slotTypingNotify(const QString &, int );
+ void slotGameNotify(const QString &, int);
+ void slotMailNotify(const QString &, const QString &, int);
+ void slotSystemMessage(const QString &);
+ void slotRemoveHandler(int fd);
+ //void slotHostConnect(const QString &host, int port);
+ void slotGotWebcamInvite(const QString &);
+ void slotWebcamNotAvailable( const QString &who );
+ void slotGotWebcamImage(const QString&, const QPixmap&);
+ void slotWebcamReadyForTransmission();
+ void slotWebcamStopTransmission();
+ void slotOutgoingWebcamClosing();
+ void slotWebcamClosed(const QString&, int);
+ void slotWebcamPaused(const QString&);
+ void slotWebcamViewerJoined( const QString & );
+ void slotWebcamViewerLeft( const QString & );
+ void slotWebcamViewerRequest( const QString & );
+ void slotPictureStatusNotiy( const QString&, int);
+ void slotGotBuddyIcon(const QString&, KTempFile*, int);
+ void slotGotBuddyIconInfo(const QString&, KURL, int);
+ void slotGotBuddyIconChecksum(const QString&, int);
+ void slotGotBuddyIconRequest(const QString &);
+ void slotBuddyIconChanged(const QString&);
+ void slotGotYABEntry( YABEntry *entry );
+ void slotGotYABRevision( long revision, bool merged );
+ void slotSaveYABEntry( YABEntry &entry );
+ void slotModifyYABEntryError( YABEntry *entry, const QString & );
+
+ void slotReceiveFileAccepted( Kopete::Transfer *trans, const QString& fileName );
+ void slotReceiveFileRefused( const Kopete::FileTransferInfo& info );
+ void slotFileTransferComplete( unsigned int id );
+ void slotFileTransferError( unsigned int id, int error, const QString &desc );
+ void slotFileTransferBytesProcessed( unsigned int id, unsigned int bytes );
+ void slotFileTransferResult( KIO::Job * );
+ void slotError( int level );
+
+private slots:
+ /**
+ * When a global identity key get changed.
+ */
+ void slotGlobalIdentityChanged( const QString &key, const QVariant &value );
+private:
+
+ /**
+ * Handle the signal and slot connections and disconnects
+ */
+ void initConnectionSignals( enum SignalConnectionType sct );
+
+ QString prepareIncomingMessage( const QString &msg );
+
+ /**
+ * internal (to the plugin) controls/flags
+ * This should be kept in sync with server - if a buddy is removed, this should be changed accordingly.
+ */
+ QMap<QString, QPair<QString, QString> > IDs;
+
+ /**
+ * Conferences list, maped by room name (id)
+ */
+ QMap<QString, YahooConferenceChatSession *> m_conferences;
+ QStringList m_pendingConfInvites;
+ QStringList m_pendingWebcamInvites;
+ QStringList m_pendingFileTransfers;
+
+ QMap<unsigned int, Kopete::Transfer *> m_fileTransfers;
+
+ bool theHaveContactList; // Do we have the full server-side contact list yet?
+ int stateOnConnection; // The state to change to on connection
+
+ /**
+ * External Settings and Descriptors
+ */
+ bool m_useServerGroups; // Use the groups on the server for import
+ bool m_importContacts; // Import the contacts from the server
+ int m_sessionId; // The Yahoo session descriptor
+ int m_lastDisconnectCode; // The last disconnect code.
+ int m_currentMailCount;
+ long m_YABLastMerge; // The YAB Revision on which the last merge was done
+ long m_YABLastRemoteRevision; // The last remote YAB Revision on which a sync was done
+ YahooProtocol *m_protocol; // The Protocol Object
+
+ YahooWebcam *m_webcam;
+
+ YahooAwayDialog *theAwayDialog; // Our away message dialog
+
+ KAction *m_openInboxAction; // Menu item openInbox
+ KAction *m_openYABAction; // Menu item openYahooAddressbook
+ KAction *m_editOwnYABEntry; // Menu item editOwnYABEntry
+
+ Client *m_session; // The Connection object
+};
+
+
+#endif
+
diff --git a/kopete/protocols/yahoo/yahooaddcontact.cpp b/kopete/protocols/yahoo/yahooaddcontact.cpp
new file mode 100644
index 00000000..909c4379
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooaddcontact.cpp
@@ -0,0 +1,72 @@
+/*
+ yahooaddcontact.cpp - UI Page for Adding a Yahoo Contact
+
+ Copyright (c) 2003 by Gav Wood <gav@kde.org>
+ Copyright (c) 2003 by Matt Rogers <mattrogers@sbcglobal.net>
+ Based on code by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+// QT Includes
+#include <qlayout.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <klineedit.h>
+
+// Kopete Includes
+#include <addcontactpage.h>
+#include <kopeteaccount.h>
+
+// Local Includes
+#include "yahooadd.h"
+#include "yahooaddcontact.h"
+#include "yahooaccount.h"
+
+// Yahoo Add Contact page
+YahooAddContact::YahooAddContact(YahooProtocol *owner, QWidget *parent, const char *name): AddContactPage(parent, name)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << "YahooAddContact::YahooAddContact(<owner>, <parent>, " << name << ")" << endl;
+
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ theDialog = new YahooAddContactBase(this);
+ theDialog->show();
+ theProtocol = owner;
+}
+
+// Destructor
+YahooAddContact::~YahooAddContact()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+bool YahooAddContact::validateData()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ return !theDialog->contactID->text().isEmpty();
+}
+
+bool YahooAddContact::apply(Kopete::Account *theAccount, Kopete::MetaContact *theMetaContact)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ QString displayName = theDialog->contactID->text();
+ YahooAccount* myAccount = static_cast<YahooAccount*>(theAccount);
+ myAccount->addContact(theDialog->contactID->text().lower(), theMetaContact, Kopete::Account::ChangeKABC );
+ return true;
+}
+
+#include "yahooaddcontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooaddcontact.h b/kopete/protocols/yahoo/yahooaddcontact.h
new file mode 100644
index 00000000..947a7dcd
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooaddcontact.h
@@ -0,0 +1,55 @@
+/*
+ yahooaddcontact.h - UI Page for Adding a Yahoo Contact
+
+ Copyright (c) 2003 by Gav Wood <gav@kde.org>
+ Copyright (c) 2003 by Matt Rogers <mattrogers@sbcglobal.net>
+ Kopete (c) 2003 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __YAHOOADDCONTACT_H
+#define __YAHOOADDCONTACT_H
+
+// Local Includes
+
+// Kopete Includes
+#include <addcontactpage.h>
+
+// QT Includes
+
+// KDE Includes
+
+class YahooProtocol;
+class YahooAddContactBase;
+namespace Kopete { class MetaContact; }
+
+class YahooAddContact: public AddContactPage
+{
+ Q_OBJECT
+
+private:
+ YahooProtocol *theProtocol;
+ YahooAddContactBase *theDialog;
+
+public:
+ YahooAddContact(YahooProtocol *owner, QWidget *parent = 0, const char *name = 0);
+ ~YahooAddContact();
+
+ virtual bool validateData();
+
+public slots:
+ virtual bool apply(Kopete::Account *theAccount, Kopete::MetaContact *theMetaContact);
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahoochatsession.cpp b/kopete/protocols/yahoo/yahoochatsession.cpp
new file mode 100644
index 00000000..0402c400
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoochatsession.cpp
@@ -0,0 +1,166 @@
+/*
+ yahoochatsession.cpp - Yahoo! Message Manager
+
+ Copyright (c) 2005 by André Duffeck <andre@duffeck.de>
+
+ *************************************************************************
+ * *
+ * 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 "yahoochatsession.h"
+
+#include <qlabel.h>
+#include <qimage.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qiconset.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kinputdialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <ktempfile.h>
+#include <kmainwindow.h>
+#include <ktoolbar.h>
+#include <krun.h>
+#include <kiconloader.h>
+
+#include "kopetecontactaction.h"
+#include "kopetemetacontact.h"
+#include "kopetecontactlist.h"
+#include "kopetechatsessionmanager.h"
+#include "kopeteuiglobal.h"
+#include "kopeteglobal.h"
+#include "kopeteview.h"
+
+#include "yahoocontact.h"
+#include "yahooaccount.h"
+
+YahooChatSession::YahooChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user,
+ Kopete::ContactPtrList others, const char *name )
+: Kopete::ChatSession( user, others, protocol, name )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ Kopete::ChatSessionManager::self()->registerChatSession( this );
+ setInstance(protocol->instance());
+
+ // Add Actions
+ new KAction( i18n( "Buzz Contact" ), QIconSet(BarIcon("bell")), "Ctrl+G", this, SLOT( slotBuzzContact() ), actionCollection(), "yahooBuzz" ) ;
+ new KAction( i18n( "Show User Info" ), QIconSet(BarIcon("idea")), 0, this, SLOT( slotUserInfo() ), actionCollection(), "yahooShowInfo" ) ;
+ new KAction( i18n( "Request Webcam" ), QIconSet(BarIcon("webcamreceive")), 0, this, SLOT( slotRequestWebcam() ), actionCollection(), "yahooRequestWebcam" ) ;
+ new KAction( i18n( "Invite to view your Webcam" ), QIconSet(BarIcon("webcamsend")), 0, this, SLOT( slotInviteWebcam() ), actionCollection(), "yahooSendWebcam" ) ;
+ new KAction( i18n( "Send File" ), QIconSet(BarIcon("attach")), 0, this, SLOT( slotSendFile() ), actionCollection(), "yahooSendFile" );
+
+ YahooContact *c = static_cast<YahooContact*>( others.first() );
+ connect( c, SIGNAL( displayPictureChanged() ), this, SLOT( slotDisplayPictureChanged() ) );
+ m_image = new QLabel( 0L, "kde toolbar widget" );
+ new KWidgetAction( m_image, i18n( "Yahoo Display Picture" ), 0, this, SLOT( slotDisplayPictureChanged() ), actionCollection(), "yahooDisplayPicture" );
+ if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()) )
+ {
+ connect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
+ }
+ else
+ {
+ m_image = 0L;
+ }
+
+ setXMLFile("yahoochatui.rc");
+}
+
+YahooChatSession::~YahooChatSession()
+{
+ delete m_image;
+}
+
+void YahooChatSession::slotBuzzContact()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<YahooContact *>(contacts.first())->buzzContact();
+}
+
+void YahooChatSession::slotUserInfo()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<YahooContact *>(contacts.first())->slotUserInfo();
+}
+
+void YahooChatSession::slotRequestWebcam()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<YahooContact *>(contacts.first())->requestWebcam();
+}
+
+void YahooChatSession::slotInviteWebcam()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<YahooContact *>(contacts.first())->inviteWebcam();
+}
+
+void YahooChatSession::slotSendFile()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact>contacts = members();
+ static_cast<YahooContact *>(contacts.first())->sendFile();
+}
+
+void YahooChatSession::slotDisplayPictureChanged()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QPtrList<Kopete::Contact> mb=members();
+ YahooContact *c = static_cast<YahooContact *>( mb.first() );
+ if ( c && m_image )
+ {
+ if(c->hasProperty(Kopete::Global::Properties::self()->photo().key()))
+ {
+ int sz=22;
+ // get the size of the toolbar were the aciton is plugged.
+ // if you know a better way to get the toolbar, let me know
+ KMainWindow *w= view(false) ? dynamic_cast<KMainWindow*>( view(false)->mainWidget()->topLevelWidget() ) : 0L;
+ if(w)
+ {
+ //We connected that in the constructor. we don't need to keep this slot active.
+ disconnect( Kopete::ChatSessionManager::self() , SIGNAL(viewActivated(KopeteView* )) , this, SLOT(slotDisplayPictureChanged()) );
+
+ QPtrListIterator<KToolBar> it=w->toolBarIterator() ;
+ KAction *imgAction=actionCollection()->action("yahooDisplayPicture");
+ if(imgAction) while(it)
+ {
+ KToolBar *tb=*it;
+ if(imgAction->isPlugged(tb))
+ {
+ sz=tb->iconSize();
+ //ipdate if the size of the toolbar change.
+ disconnect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
+ connect(tb, SIGNAL(modechange()), this, SLOT(slotDisplayPictureChanged()));
+ break;
+ }
+ ++it;
+ }
+ }
+ QString imgURL=c->property(Kopete::Global::Properties::self()->photo()).value().toString();
+ QImage scaledImg = QPixmap( imgURL ).convertToImage().smoothScale( sz, sz );
+ if(!scaledImg.isNull())
+ m_image->setPixmap( scaledImg );
+ else
+ { //the image has maybe not been transfered correctly.. force to download again
+ c->removeProperty(Kopete::Global::Properties::self()->photo());
+ //slotDisplayPictureChanged(); //don't do that or we might end in a infinite loop
+ }
+ QToolTip::add( m_image, "<qt><img src=\"" + imgURL + "\"></qt>" );
+ }
+ }
+}
+
+#include "yahoochatsession.moc"
diff --git a/kopete/protocols/yahoo/yahoochatsession.h b/kopete/protocols/yahoo/yahoochatsession.h
new file mode 100644
index 00000000..1e440e95
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoochatsession.h
@@ -0,0 +1,51 @@
+/*
+ yahoochatsession.h - Yahoo! Message Manager
+
+ Copyright (c) 2005 by Andre Duffeck <andre@duffeck.de>
+
+ *************************************************************************
+ * *
+ * 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 YAHOOCHATSESSION_H
+#define YAHOOCHATSESSION_H
+
+#include "kopetechatsession.h"
+
+class KActionCollection;
+class YahooContact;
+class KActionMenu;
+class QLabel;
+
+
+/**
+ * @author Andre Duffeck
+ */
+class KOPETE_EXPORT YahooChatSession : public Kopete::ChatSession
+{
+ Q_OBJECT
+
+public:
+ YahooChatSession( Kopete::Protocol *protocol, const Kopete::Contact *user, Kopete::ContactPtrList others, const char *name = 0 );
+ ~YahooChatSession();
+
+private slots:
+ void slotDisplayPictureChanged();
+
+ void slotBuzzContact();
+ void slotUserInfo();
+ void slotRequestWebcam();
+ void slotInviteWebcam();
+ void slotSendFile();
+
+private:
+ QLabel *m_image;
+};
+
+#endif
diff --git a/kopete/protocols/yahoo/yahoochatui.rc b/kopete/protocols/yahoo/yahoochatui.rc
new file mode 100644
index 00000000..68870dae
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoochatui.rc
@@ -0,0 +1,25 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="9" name="kopete_yahoo_chat">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <Action name="yahooRequestWebcam" />
+ <Action name="yahooSendWebcam" />
+ <Action name="yahooSendFile" />
+ <Action name="yahooBuzz" />
+ <Action name="yahooShowInfo" />
+ </Menu>
+ </MenuBar>
+
+
+ <ToolBar name="statusToolBar">
+ <Action name="yahooDisplayPicture" />
+ <Action name="yahooRequestWebcam" />
+ <Action name="yahooSendWebcam" />
+ <Action name="yahooSendFile" />
+ <Action name="yahooBuzz" />
+ <Action name="yahooShowInfo" />
+
+ </ToolBar>
+
+
+</kpartgui>
diff --git a/kopete/protocols/yahoo/yahooconferencemessagemanager.cpp b/kopete/protocols/yahoo/yahooconferencemessagemanager.cpp
new file mode 100644
index 00000000..cc173d96
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooconferencemessagemanager.cpp
@@ -0,0 +1,115 @@
+/*
+ yahooconferencemessagemanager.h - Yahoo Conference Message Manager
+
+ Copyright (c) 2003 by Duncan Mac-Vicar <duncan@kde.org>
+ Copyright (c) 2005 by André Duffeck <andre@duffeck.de>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#include <kdebug.h>
+#include <klineeditdlg.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpopupmenu.h>
+#include <kconfig.h>
+
+#include <kopetecontactaction.h>
+#include <kopetecontactlist.h>
+#include <kopetecontact.h>
+#include <kopetechatsessionmanager.h>
+#include <kopeteuiglobal.h>
+
+#include "yahooconferencemessagemanager.h"
+#include "yahoocontact.h"
+#include "yahooaccount.h"
+#include "yahooinvitelistimpl.h"
+
+YahooConferenceChatSession::YahooConferenceChatSession( const QString & yahooRoom, Kopete::Protocol *protocol, const Kopete::Contact *user,
+ Kopete::ContactPtrList others, const char *name )
+: Kopete::ChatSession( user, others, protocol, name )
+{
+
+ Kopete::ChatSessionManager::self()->registerChatSession( this );
+ setInstance(protocol->instance());
+
+ connect ( this, SIGNAL( messageSent ( Kopete::Message &, Kopete::ChatSession * ) ),
+ SLOT( slotMessageSent ( Kopete::Message &, Kopete::ChatSession * ) ) );
+
+ m_yahooRoom = yahooRoom;
+
+ m_actionInvite = new KAction( i18n( "&Invite others" ), "kontact_contacts", this, SLOT( slotInviteOthers() ), actionCollection(), "yahooInvite");
+
+ setXMLFile("yahooconferenceui.rc");
+}
+
+YahooConferenceChatSession::~YahooConferenceChatSession()
+{
+ emit leavingConference( this );
+}
+
+YahooAccount *YahooConferenceChatSession::account()
+{
+ return static_cast< YahooAccount *>( Kopete::ChatSession::account() );
+}
+
+const QString &YahooConferenceChatSession::room()
+{
+ return m_yahooRoom;
+}
+
+void YahooConferenceChatSession::joined( YahooContact *c )
+{
+ addContact( c );
+}
+
+void YahooConferenceChatSession::left( YahooContact *c )
+{
+ removeContact( c );
+}
+
+void YahooConferenceChatSession::slotMessageSent( Kopete::Message & message, Kopete::ChatSession * )
+{
+ kdDebug ( YAHOO_GEN_DEBUG ) << k_funcinfo << endl;
+
+ YahooAccount *acc = dynamic_cast< YahooAccount *>( account() );
+ if( acc )
+ acc->sendConfMessage( this, message );
+ appendMessage( message );
+ messageSucceeded();
+}
+
+void YahooConferenceChatSession::slotInviteOthers()
+{
+ QStringList buddies;
+ QDictIterator<Kopete::Contact> it( account()->contacts() );
+ Kopete::Contact *myself = account()->myself();
+ for( ; it.current(); ++it )
+ {
+ if( (*it) != myself && !members().contains( *it ) )
+ buddies.push_back( (*it)->contactId() );
+ }
+
+ YahooInviteListImpl *dlg = new YahooInviteListImpl( Kopete::UI::Global::mainWidget() );
+ QObject::connect( dlg, SIGNAL( readyToInvite( const QString &, const QStringList &, const QStringList &, const QString & ) ),
+ account(), SLOT( slotAddInviteConference( const QString &, const QStringList &, const QStringList &, const QString & ) ) );
+ dlg->setRoom( m_yahooRoom );
+ dlg->fillFriendList( buddies );
+ for( QPtrList<Kopete::Contact>::ConstIterator it = members().begin(); it != members().end(); it++ )
+ dlg->addParticipant( (*it)->contactId() );
+ dlg->show();
+}
+
+#include "yahooconferencemessagemanager.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooconferencemessagemanager.h b/kopete/protocols/yahoo/yahooconferencemessagemanager.h
new file mode 100644
index 00000000..60771fab
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooconferencemessagemanager.h
@@ -0,0 +1,58 @@
+/*
+ yahooconferencemessagemanager.h - Yahoo Conference Message Manager
+
+ Copyright (c) 2003 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2005 by André Duffeck <andre@duffeck.de>
+
+ Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOCONFERENCEMESSAGEMANAGER_H
+#define YAHOOCONFERENCEMESSAGEMANAGER_H
+
+#include "kopetechatsession.h"
+
+class KActionCollection;
+class YahooContact;
+class YahooAccount;
+class KActionMenu;
+
+/**
+ * @author Duncan Mac-Vicar Prett
+ */
+class YahooConferenceChatSession : public Kopete::ChatSession
+{
+ Q_OBJECT
+
+public:
+ YahooConferenceChatSession( const QString &m_yahooRoom, Kopete::Protocol *protocol, const Kopete::Contact *user, Kopete::ContactPtrList others, const char *name = 0 );
+ ~YahooConferenceChatSession();
+
+ void joined( YahooContact *c );
+ void left( YahooContact *c );
+ const QString &room();
+ YahooAccount *account();
+signals:
+ void leavingConference( YahooConferenceChatSession *s );
+protected slots:
+ void slotMessageSent( Kopete::Message &message, Kopete::ChatSession * );
+ void slotInviteOthers();
+private:
+ QString m_yahooRoom;
+
+ KAction *m_actionInvite;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 tw=4:
+
diff --git a/kopete/protocols/yahoo/yahooconferenceui.rc b/kopete/protocols/yahoo/yahooconferenceui.rc
new file mode 100644
index 00000000..6077dee3
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooconferenceui.rc
@@ -0,0 +1,11 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="4" name="kopete_yahoo_conference">
+ <MenuBar>
+ <Menu noMerge="1" name="file">
+ <text>&amp;Chat</text>
+ <Action name="yahooInvite" />
+ </Menu>
+ </MenuBar>
+
+</kpartgui>
+
diff --git a/kopete/protocols/yahoo/yahoocontact.cpp b/kopete/protocols/yahoo/yahoocontact.cpp
new file mode 100644
index 00000000..81838dec
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoocontact.cpp
@@ -0,0 +1,835 @@
+/*
+ yahoocontact.cpp - Yahoo Contact
+
+ Copyright (c) 2003-2004 by Matt Rogers <matt.rogers@kdemail.net>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Portions based on code by Bruno Rodrigues <bruno.rodrigues@litux.org>
+
+ Copyright (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+#include "kopetegroup.h"
+#include "kopetechatsession.h"
+#include "kopeteonlinestatus.h"
+#include "kopetemetacontact.h"
+#include "kopetechatsessionmanager.h"
+#include "kopetemetacontact.h"
+#include "kopeteuiglobal.h"
+#include "kopeteview.h"
+#include "kopetetransfermanager.h"
+
+// Local Includes
+#include "yahoocontact.h"
+#include "yahooaccount.h"
+#include "client.h"
+#include "yahoowebcamdialog.h"
+#include "yahoostealthsetting.h"
+#include "yahoochatsession.h"
+#include "yabentry.h"
+#include "yahoouserinfodialog.h"
+#include "sendfiletask.h"
+
+// QT Includes
+#include <qregexp.h>
+#include <qfile.h>
+#include <qradiobutton.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <krun.h>
+#include <kshortcut.h>
+#include <kmessagebox.h>
+#include <ktempfile.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kurl.h>
+#include <kio/jobclasses.h>
+#include <kimageio.h>
+#include <kstandarddirs.h>
+#include <kfiledialog.h>
+
+YahooContact::YahooContact( YahooAccount *account, const QString &userId, const QString &fullName, Kopete::MetaContact *metaContact )
+ : Kopete::Contact( account, userId, metaContact )
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ m_userId = userId;
+ if ( metaContact )
+ m_groupName = metaContact->groups().getFirst()->displayName();
+ m_manager = 0L;
+ m_account = account;
+ m_YABEntry = 0L;
+ m_stealthed = false;
+ m_receivingWebcam = false;
+ m_sessionActive = false;
+
+ // Update ContactList
+ setNickName( fullName );
+ setOnlineStatus( static_cast<YahooProtocol*>( m_account->protocol() )->Offline );
+ setFileCapable( true );
+
+ if ( m_account->haveContactList() )
+ syncToServer();
+
+ m_webcamDialog = 0L;
+ m_webcamAction = 0L;
+ m_stealthAction = 0L;
+ m_inviteWebcamAction = 0L;
+ m_inviteConferenceAction = 0L;
+ m_profileAction = 0L;
+
+ m_buzzAction = 0L;
+}
+
+YahooContact::~YahooContact()
+{
+ delete m_YABEntry;
+ m_YABEntry = 0L;
+}
+
+QString YahooContact::userId() const
+{
+ return m_userId;
+}
+
+void YahooContact::setOnlineStatus(const Kopete::OnlineStatus &status)
+{
+ if( m_stealthed && status.internalStatus() <= 999) // Not Stealted -> Stealthed
+ {
+ Contact::setOnlineStatus(
+ Kopete::OnlineStatus(status.status() ,
+ (status.weight()==0) ? 0 : (status.weight() -1) ,
+ protocol() ,
+ status.internalStatus()+1000 ,
+ status.overlayIcons() + QStringList("yahoo_stealthed") ,
+ i18n("%1|Stealthed").arg( status.description() ) ) );
+ }
+ else if( !m_stealthed && status.internalStatus() > 999 )// Stealthed -> Not Stealthed
+ Contact::setOnlineStatus( static_cast< YahooProtocol *>( protocol() )->statusFromYahoo( status.internalStatus() - 1000 ) );
+ else
+ Contact::setOnlineStatus( status );
+
+ if( status.status() == Kopete::OnlineStatus::Offline )
+ removeProperty( ((YahooProtocol*)(m_account->protocol()))->awayMessage);
+}
+
+void YahooContact::setStealthed( bool stealthed )
+{
+ m_stealthed = stealthed;
+ setOnlineStatus( onlineStatus() );
+}
+
+bool YahooContact::stealthed()
+{
+ return m_stealthed;
+}
+
+void YahooContact::serialize(QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData)
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ Kopete::Contact::serialize(serializedData, addressBookData);
+}
+
+void YahooContact::syncToServer()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if(!m_account->isConnected()) return;
+
+ if ( !m_account->isOnServer(m_userId) && !metaContact()->isTemporary() )
+ { kdDebug(YAHOO_GEN_DEBUG) << "Contact " << m_userId << " doesn't exist on server-side. Adding..." << endl;
+
+ Kopete::GroupList groupList = metaContact()->groups();
+ for( Kopete::Group *g = groupList.first(); g; g = groupList.next() )
+ m_account->yahooSession()->addBuddy(m_userId, g->displayName() );
+ }
+}
+
+void YahooContact::sync(unsigned int flags)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if ( !m_account->isConnected() )
+ return;
+
+ if ( !m_account->isOnServer( contactId() ) )
+ {
+ //TODO: Share this code with the above function
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Contact isn't on the server. Adding..." << endl;
+ Kopete::GroupList groupList = metaContact()->groups();
+ for ( Kopete::Group *g = groupList.first(); g; g = groupList.next() )
+ m_account->yahooSession()->addBuddy(m_userId, g->displayName() );
+ }
+ else
+ {
+ QString newGroup = metaContact()->groups().first()->displayName();
+ if ( flags & Kopete::Contact::MovedBetweenGroup )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "contact changed groups. moving on server" << endl;
+ m_account->yahooSession()->moveBuddy( contactId(), m_groupName, newGroup );
+ m_groupName = newGroup;
+ }
+ }
+}
+
+
+bool YahooContact::isOnline() const
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ return onlineStatus().status() != Kopete::OnlineStatus::Offline && onlineStatus().status() != Kopete::OnlineStatus::Unknown;
+}
+
+bool YahooContact::isReachable()
+{
+ //kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if ( m_account->isConnected() )
+ return true;
+ else
+ return false;
+}
+
+Kopete::ChatSession *YahooContact::manager( Kopete::Contact::CanCreateFlags canCreate )
+{
+ if( !m_manager && canCreate)
+ {
+ Kopete::ContactPtrList m_them;
+ m_them.append( this );
+ m_manager = new YahooChatSession( protocol(), account()->myself(), m_them );
+ connect( m_manager, SIGNAL( destroyed() ), this, SLOT( slotChatSessionDestroyed() ) );
+ connect( m_manager, SIGNAL( messageSent ( Kopete::Message&, Kopete::ChatSession* ) ), this, SLOT( slotSendMessage( Kopete::Message& ) ) );
+ connect( m_manager, SIGNAL( myselfTyping( bool) ), this, SLOT( slotTyping( bool ) ) );
+ connect( m_account, SIGNAL( receivedTypingMsg( const QString &, bool ) ), m_manager, SLOT( receivedTypingMsg( const QString&, bool ) ) );
+ connect( this, SIGNAL(displayPictureChanged()), m_manager, SLOT(slotDisplayPictureChanged()));
+ }
+
+ return m_manager;
+}
+
+QString YahooContact::prepareMessage( const QString &messageText )
+{
+ // Yahoo does not understand XML/HTML message data, so send plain text
+ // instead. (Yahoo has its own format for "rich text".)
+ QString newMsg( messageText );
+ QRegExp regExp;
+ int pos = 0;
+ regExp.setMinimal( true );
+
+ // find and replace Bold-formattings
+ regExp.setPattern( "<span([^>]*)font-weight:600([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1font-weight:600\\2>\033[1m\\3\033[x1m</span>" ) );
+ }
+ }
+
+ // find and replace Underline-formattings
+ regExp.setPattern( "<span([^>]*)text-decoration:underline([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1text-decoration:underline\\2>\033[4m\\3\033[x4m</span>" ) );
+ }
+ }
+
+ // find and replace Italic-formattings
+ regExp.setPattern( "<span([^>]*)font-style:italic([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1font-style:italic\\2>\033[2m\\3\033[x2m</span>" ) );
+ }
+ }
+
+ // find and replace Color-formattings
+ regExp.setPattern( "<span([^>]*)color:#([0-9a-zA-Z]*)([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1\\3>\033[#\\2m\\4\033[#000000m</span>" ) );
+ }
+ }
+
+ // find and replace Font-formattings
+ regExp.setPattern( "<span([^>]*)font-family:([^;\"]*)([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1\\3><font face=\"\\2\">\\4</span>" ) );
+ }
+ }
+
+ // find and replace Size-formattings
+ regExp.setPattern( "<span([^>]*)font-size:([0-9]*)pt([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("<span\\1\\3><font size=\"\\2\">\\4</span>" ) );
+ }
+ }
+
+ // remove span-tags
+ regExp.setPattern( "<span([^>]*)>(.*)</span>" );
+ pos = 0;
+ while ( pos >= 0 ) {
+ pos = regExp.search( messageText, pos );
+ if ( pos >= 0 ) {
+ pos += regExp.matchedLength();
+ newMsg.replace( regExp, QString::fromLatin1("\\2") );
+ }
+ }
+
+ // convert escaped chars
+ newMsg.replace( QString::fromLatin1( "&gt;" ), QString::fromLatin1( ">" ) );
+ newMsg.replace( QString::fromLatin1( "&lt;" ), QString::fromLatin1( "<" ) );
+ newMsg.replace( QString::fromLatin1( "&quot;" ), QString::fromLatin1( "\"" ) );
+ newMsg.replace( QString::fromLatin1( "&nbsp;" ), QString::fromLatin1( " " ) );
+ newMsg.replace( QString::fromLatin1( "&amp;" ), QString::fromLatin1( "&" ) );
+ newMsg.replace( QString::fromLatin1( "<br />" ), QString::fromLatin1( "\r" ) );
+ newMsg.replace( QString::fromLatin1( "<br/>" ), QString::fromLatin1( "\r" ) );
+
+ return newMsg;
+}
+
+void YahooContact::slotSendMessage( Kopete::Message &message )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ QString messageText = message.escapedBody();
+ kdDebug(YAHOO_GEN_DEBUG) << "Original message: " << messageText << endl;
+ messageText = prepareMessage( messageText );
+ kdDebug(YAHOO_GEN_DEBUG) << "Converted message: " << messageText << endl;
+
+ Kopete::ContactPtrList m_them = manager(Kopete::Contact::CanCreate)->members();
+ Kopete::Contact *target = m_them.first();
+
+ if( !m_sessionActive ) // Register a new chatsession
+ {
+ m_account->yahooSession()->setChatSessionState( m_userId, false );
+ m_sessionActive = true;
+ }
+
+ m_account->yahooSession()->sendMessage( static_cast<YahooContact *>(target)->m_userId, messageText );
+
+ // append message to window
+ manager(Kopete::Contact::CanCreate)->appendMessage(message);
+ manager(Kopete::Contact::CanCreate)->messageSucceeded();
+}
+
+void YahooContact::sendFile( const KURL &sourceURL, const QString &fileName, uint fileSize )
+{
+ Kopete::TransferManager::transferManager()->sendFile( sourceURL, fileName, fileSize,
+ false, this, SLOT(slotSendFile( const KURL & )) );
+}
+
+void YahooContact::slotTyping(bool isTyping_ )
+{
+ Kopete::ContactPtrList m_them = manager(Kopete::Contact::CanCreate)->members();
+ Kopete::Contact *target = m_them.first();
+
+
+ m_account->yahooSession()->sendTyping( static_cast<YahooContact*>(target)->m_userId, isTyping_ );
+}
+
+void YahooContact::slotChatSessionDestroyed()
+{
+ m_manager = 0L;
+ m_account->yahooSession()->setChatSessionState( m_userId, true ); // Unregister chatsession
+ m_sessionActive = false;
+}
+
+QPtrList<KAction> *YahooContact::customContextMenuActions()
+{
+ QPtrList<KAction> *actionCollection = new QPtrList<KAction>();
+ if ( !m_webcamAction )
+ {
+ m_webcamAction = new KAction( i18n( "View &Webcam" ), "webcamreceive", KShortcut(),
+ this, SLOT( requestWebcam() ), this, "view_webcam" );
+ }
+ if ( isReachable() )
+ m_webcamAction->setEnabled( true );
+ else
+ m_webcamAction->setEnabled( false );
+ actionCollection->append( m_webcamAction );
+
+ if( !m_inviteWebcamAction )
+ {
+ m_inviteWebcamAction = new KAction( i18n( "Invite to view your Webcam" ), "webcamsend", KShortcut(),
+ this, SLOT( inviteWebcam() ), this, "invite_webcam" );
+ }
+ if ( isReachable() )
+ m_inviteWebcamAction->setEnabled( true );
+ else
+ m_inviteWebcamAction->setEnabled( false );
+ actionCollection->append( m_inviteWebcamAction );
+
+ if ( !m_buzzAction )
+ {
+ m_buzzAction = new KAction( i18n( "&Buzz Contact" ), "bell", KShortcut(), this, SLOT( buzzContact() ), this, "buzz_contact");
+ }
+ if ( isReachable() )
+ m_buzzAction->setEnabled( true );
+ else
+ m_buzzAction->setEnabled( false );
+ actionCollection->append( m_buzzAction );
+
+ if ( !m_stealthAction )
+ {
+ m_stealthAction = new KAction( i18n( "&Stealth Setting" ), "yahoo_stealthed", KShortcut(), this, SLOT( stealthContact() ), this, "stealth_contact");
+ }
+ if ( isReachable() )
+ m_stealthAction->setEnabled( true );
+ else
+ m_stealthAction->setEnabled( false );
+ actionCollection->append( m_stealthAction );
+
+ if ( !m_inviteConferenceAction )
+ {
+ m_inviteConferenceAction = new KAction( i18n( "&Invite to Conference" ), "kontact_contacts", KShortcut(), this, SLOT( inviteConference() ), this, "invite_conference");
+ }
+ if ( isReachable() )
+ m_inviteConferenceAction->setEnabled( true );
+ else
+ m_inviteConferenceAction->setEnabled( false );
+ actionCollection->append( m_inviteConferenceAction );
+
+ if ( !m_profileAction )
+ {
+ m_profileAction = new KAction( i18n( "&View Yahoo Profile" ), "kontact_notes", KShortcut(), this, SLOT( slotUserProfile() ), this, "profile_contact");
+ }
+ m_profileAction->setEnabled( true );
+ actionCollection->append( m_profileAction );
+
+ return actionCollection;
+
+ //return 0L;
+}
+
+void YahooContact::slotUserInfo()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !m_YABEntry )
+ {
+ readYABEntry(); // No YABEntry was set, so read the one from contactlist.xml
+ }
+
+ YahooUserInfoDialog *dlg = new YahooUserInfoDialog( this, Kopete::UI::Global::mainWidget(), "yahoo userinfo" );
+ dlg->setData( *m_YABEntry );
+ dlg->setAccountConnected( m_account->isConnected() );
+ dlg->show();
+ QObject::connect( dlg, SIGNAL(saveYABEntry( YABEntry & )), m_account, SLOT(slotSaveYABEntry( YABEntry & )));
+}
+
+void YahooContact::slotUserProfile()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ QString profileSiteString = QString::fromLatin1("http://profiles.yahoo.com/") + userId();
+ KRun::runURL( KURL( profileSiteString ) , "text/html" );
+}
+
+void YahooContact::slotSendFile( const KURL &url)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ m_account->sendFile( this, url );
+}
+
+void YahooContact::stealthContact()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ KDialogBase *stealthSettingDialog = new KDialogBase( Kopete::UI::Global::mainWidget(), "stealthSettingDialog", "true",
+ i18n("Stealth Setting"), KDialogBase::Ok | KDialogBase::Cancel, KDialogBase::Ok, true );
+ YahooStealthSetting *stealthWidget = new YahooStealthSetting( stealthSettingDialog, "stealthSettingWidget" );
+ stealthSettingDialog->setMainWidget( stealthWidget );
+
+ // Prepare dialog
+ if( m_account->myself()->onlineStatus() == YahooProtocol::protocol()->Invisible )
+ {
+ stealthWidget->radioOffline->setEnabled( true );
+ stealthWidget->radioOffline->setChecked( true );
+ }
+ if( stealthed() )
+ stealthWidget->radioPermOffline->setChecked( true );
+
+
+ // Show dialog
+ if ( stealthSettingDialog->exec() == QDialog::Rejected )
+ {
+ stealthSettingDialog->delayedDestruct();
+ return;
+ }
+
+ // Apply permanent setting
+ if( stealthed() && !stealthWidget->radioPermOffline->isChecked() )
+ m_account->yahooSession()->stealthContact( m_userId, Yahoo::StealthPermOffline, Yahoo::StealthNotActive );
+ else if( !stealthed() && stealthWidget->radioPermOffline->isChecked() )
+ m_account->yahooSession()->stealthContact( m_userId, Yahoo::StealthPermOffline, Yahoo::StealthActive );
+
+ // Apply temporary setting
+ if( m_account->myself()->onlineStatus() == YahooProtocol::protocol()->Invisible )
+ {
+ if( stealthWidget->radioOnline->isChecked() )
+ {
+ m_account->yahooSession()->stealthContact( m_userId, Yahoo::StealthOnline, Yahoo::StealthActive );
+ }
+ else if( stealthWidget->radioOffline->isChecked() )
+ {
+ m_account->yahooSession()->stealthContact( m_userId, Yahoo::StealthOffline, Yahoo::StealthActive );
+ }
+ }
+
+ stealthSettingDialog->delayedDestruct();
+}
+
+void YahooContact::buzzContact()
+{
+ Kopete::ContactPtrList m_them = manager(Kopete::Contact::CanCreate)->members();
+ Kopete::Contact *target = m_them.first();
+
+ m_account->yahooSession()->sendBuzz( static_cast<YahooContact*>(target)->m_userId );
+
+ KopeteView *view = manager(Kopete::Contact::CannotCreate)->view(false);
+ if ( view )
+ {
+ Kopete::Message msg = Kopete::Message( manager(Kopete::Contact::CannotCreate)->myself() ,
+ manager(Kopete::Contact::CannotCreate)->members(), i18n("Buzzz!!!"),
+ Kopete::Message::Outbound, Kopete::Message::PlainText,
+ QString::null , Kopete::Message::TypeAction);
+ view->appendMessage( msg );
+ }
+}
+
+void YahooContact::sendBuddyIconChecksum( int checksum )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ m_account->yahooSession()->sendPictureChecksum( checksum, m_userId );
+
+}
+
+void YahooContact::sendBuddyIconInfo( const QString &url, int checksum )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ m_account->yahooSession()->sendPictureInformation( m_userId, url, checksum );
+}
+
+void YahooContact::sendBuddyIconUpdate( int type )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ m_account->yahooSession()->sendPictureStatusUpdate( m_userId, type );
+}
+
+void YahooContact::setDisplayPicture(KTempFile *f, int checksum)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( !f )
+ return;
+ // stolen from msncontact.cpp ;)
+ QString newlocation=locateLocal( "appdata", "yahoopictures/"+ contactId().lower().replace(QRegExp("[./~]"),"-") +".png" ) ;
+ setProperty( YahooProtocol::protocol()->iconCheckSum, checksum );
+
+ KIO::Job *j=KIO::file_move( KURL::fromPathOrURL( f->name() ) , KURL::fromPathOrURL( newlocation ) , -1, true /*overwrite*/ , false /*resume*/ , false /*showProgressInfo*/ );
+
+ f->setAutoDelete(false);
+ delete f;
+
+ //let the time to KIO to copy the file
+ connect(j, SIGNAL(result(KIO::Job *)) , this, SLOT(slotEmitDisplayPictureChanged() ));
+}
+
+
+void YahooContact::setYABEntry( YABEntry *entry, bool show )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << userId() << endl;
+ if( m_YABEntry )
+ delete m_YABEntry;
+
+ m_YABEntry = entry;
+ writeYABEntry(); // Store data in Contact
+
+ if( show )
+ slotUserInfo();
+}
+const YABEntry *YahooContact::yabEntry()
+{
+ if( !m_YABEntry )
+ readYABEntry();
+ return m_YABEntry;
+}
+
+void YahooContact::slotEmitDisplayPictureChanged()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ QString newlocation=locateLocal( "appdata", "yahoopictures/"+ contactId().lower().replace(QRegExp("[./~]"),"-") +".png" ) ;
+ setProperty( Kopete::Global::Properties::self()->photo(), QString::null );
+ setProperty( Kopete::Global::Properties::self()->photo() , newlocation );
+ emit displayPictureChanged();
+}
+
+void YahooContact::inviteConference()
+{
+ m_account->prepareConference( m_userId );
+}
+
+void YahooContact::inviteWebcam()
+{
+ if ( !KStandardDirs::findExe("jasper") )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("I cannot find the jasper image convert program.\njasper is required to render the yahoo webcam images."
+ "\nPlease see %1 for further information.").arg("http://wiki.kde.org/tiki-index.php?page=Kopete%20Webcam%20Support") );
+ return;
+ }
+ m_account->yahooSession()->sendWebcamInvite( m_userId );
+}
+
+void YahooContact::receivedWebcamImage( const QPixmap& image )
+{
+ if( !m_webcamDialog )
+ initWebcamViewer();
+ m_receivingWebcam = true;
+ emit signalReceivedWebcamImage( image );
+}
+
+void YahooContact::webcamClosed( int reason )
+{
+ m_receivingWebcam = false;
+ emit signalWebcamClosed( reason );
+}
+
+void YahooContact::webcamPaused()
+{
+ emit signalWebcamPaused();
+}
+
+void YahooContact::initWebcamViewer()
+{
+ //KImageIO::registerFormats();
+
+ if ( !m_webcamDialog )
+ {
+ m_webcamDialog = new YahooWebcamDialog( userId(), Kopete::UI::Global::mainWidget() );
+// QObject::connect( m_webcamDialog, SIGNAL( closeClicked() ), this, SLOT( closeWebcamDialog() ) );
+
+ QObject::connect( this, SIGNAL( signalWebcamClosed( int ) ),
+ m_webcamDialog, SLOT( webcamClosed( int ) ) );
+
+ QObject::connect( this, SIGNAL( signalWebcamPaused() ),
+ m_webcamDialog, SLOT( webcamPaused() ) );
+
+ QObject::connect( this, SIGNAL ( signalReceivedWebcamImage( const QPixmap& ) ),
+ m_webcamDialog, SLOT( newImage( const QPixmap& ) ) );
+
+ QObject::connect( m_webcamDialog, SIGNAL ( closingWebcamDialog ( ) ),
+ this, SLOT ( closeWebcamDialog ( ) ) );
+ }
+ m_webcamDialog->show();
+}
+
+void YahooContact::requestWebcam()
+{
+ if ( !KStandardDirs::findExe("jasper") )
+ {
+ KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error,
+ i18n("I cannot find the jasper image convert program.\njasper is required to render the yahoo webcam images."
+ "\nPlease see %1 for further information.").arg("http://wiki.kde.org/tiki-index.php?page=Kopete%20Webcam%20Support") );
+ return;
+ }
+
+ if( !m_webcamDialog )
+ initWebcamViewer();
+ m_account->yahooSession()->requestWebcam( contactId() );
+}
+
+void YahooContact::closeWebcamDialog()
+{
+ QObject::disconnect( this, SIGNAL( signalWebcamClosed( int ) ),
+ m_webcamDialog, SLOT( webcamClosed( int ) ) );
+
+ QObject::disconnect( this, SIGNAL( signalWebcamPaused() ),
+ m_webcamDialog, SLOT( webcamPaused( ) ) );
+
+ QObject::disconnect( this, SIGNAL ( signalReceivedWebcamImage( const QPixmap& ) ),
+ m_webcamDialog, SLOT( newImage( const QPixmap& ) ) );
+
+ QObject::disconnect( m_webcamDialog, SIGNAL ( closingWebcamDialog ( ) ),
+ this, SLOT ( closeWebcamDialog ( ) ) );
+ if( m_receivingWebcam )
+ m_account->yahooSession()->closeWebcam( contactId() );
+ m_webcamDialog->delayedDestruct();
+ m_webcamDialog = 0L;
+}
+
+void YahooContact::deleteContact()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if( !m_account->isOnServer( contactId() ) )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Contact does not exist on server-side. Not removing..." << endl;
+ }
+ else
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << "Contact is getting remove from server side contactlist...." << endl;
+ // Delete from YAB first
+ if( !m_YABEntry )
+ readYABEntry();
+ if( m_YABEntry->YABId )
+ m_account->yahooSession()->deleteYABEntry( *m_YABEntry );
+
+ // Now remove from the contactlist
+ m_account->yahooSession()->removeBuddy( contactId(), m_groupName );
+ }
+ Kopete::Contact::deleteContact();
+}
+
+void YahooContact::writeYABEntry()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ // Personal
+ setProperty( YahooProtocol::protocol()->propfirstName, m_YABEntry->firstName );
+ setProperty( YahooProtocol::protocol()->propSecondName, m_YABEntry->secondName );
+ setProperty( YahooProtocol::protocol()->propLastName, m_YABEntry->lastName );
+ setProperty( YahooProtocol::protocol()->propNickName, m_YABEntry->nickName );
+ setProperty( YahooProtocol::protocol()->propTitle, m_YABEntry->title );
+
+ // Primary Information
+ setProperty( YahooProtocol::protocol()->propPhoneMobile, m_YABEntry->phoneMobile );
+ setProperty( YahooProtocol::protocol()->propEmail, m_YABEntry->email );
+ setProperty( YahooProtocol::protocol()->propYABId, m_YABEntry->YABId );
+
+ // Additional Information
+ setProperty( YahooProtocol::protocol()->propPager, m_YABEntry->pager );
+ setProperty( YahooProtocol::protocol()->propFax, m_YABEntry->fax );
+ setProperty( YahooProtocol::protocol()->propAdditionalNumber, m_YABEntry->additionalNumber );
+ setProperty( YahooProtocol::protocol()->propAltEmail1, m_YABEntry->altEmail1 );
+ setProperty( YahooProtocol::protocol()->propAltEmail2, m_YABEntry->altEmail2 );
+ setProperty( YahooProtocol::protocol()->propImAIM, m_YABEntry->imAIM );
+ setProperty( YahooProtocol::protocol()->propImICQ, m_YABEntry->imICQ );
+ setProperty( YahooProtocol::protocol()->propImMSN, m_YABEntry->imMSN );
+ setProperty( YahooProtocol::protocol()->propImGoogleTalk, m_YABEntry->imGoogleTalk );
+ setProperty( YahooProtocol::protocol()->propImSkype, m_YABEntry->imSkype );
+ setProperty( YahooProtocol::protocol()->propImIRC, m_YABEntry->imIRC );
+ setProperty( YahooProtocol::protocol()->propImQQ, m_YABEntry->imQQ );
+
+ // Private Information
+ setProperty( YahooProtocol::protocol()->propPrivateAddress, m_YABEntry->privateAdress );
+ setProperty( YahooProtocol::protocol()->propPrivateCity, m_YABEntry->privateCity );
+ setProperty( YahooProtocol::protocol()->propPrivateState, m_YABEntry->privateState );
+ setProperty( YahooProtocol::protocol()->propPrivateZIP, m_YABEntry->privateZIP );
+ setProperty( YahooProtocol::protocol()->propPrivateCountry, m_YABEntry->privateCountry );
+ setProperty( YahooProtocol::protocol()->propPrivatePhone, m_YABEntry->privatePhone );
+ setProperty( YahooProtocol::protocol()->propPrivateURL, m_YABEntry->privateURL );
+
+ // Work Information
+ setProperty( YahooProtocol::protocol()->propCorporation, m_YABEntry->corporation );
+ setProperty( YahooProtocol::protocol()->propWorkAddress, m_YABEntry->workAdress );
+ setProperty( YahooProtocol::protocol()->propWorkCity, m_YABEntry->workCity );
+ setProperty( YahooProtocol::protocol()->propWorkState, m_YABEntry->workState );
+ setProperty( YahooProtocol::protocol()->propWorkZIP, m_YABEntry->workZIP );
+ setProperty( YahooProtocol::protocol()->propWorkCountry, m_YABEntry->workCountry );
+ setProperty( YahooProtocol::protocol()->propWorkPhone, m_YABEntry->workPhone );
+ setProperty( YahooProtocol::protocol()->propWorkURL, m_YABEntry->workURL );
+
+ // Miscellanous
+ setProperty( YahooProtocol::protocol()->propBirthday, m_YABEntry->birthday.toString( Qt::ISODate ) );
+ setProperty( YahooProtocol::protocol()->propAnniversary, m_YABEntry->anniversary.toString( Qt::ISODate ) );
+ setProperty( YahooProtocol::protocol()->propNotes, m_YABEntry->notes );
+ setProperty( YahooProtocol::protocol()->propAdditional1, m_YABEntry->additional1 );
+ setProperty( YahooProtocol::protocol()->propAdditional2, m_YABEntry->additional2 );
+ setProperty( YahooProtocol::protocol()->propAdditional3, m_YABEntry->additional3 );
+ setProperty( YahooProtocol::protocol()->propAdditional4, m_YABEntry->additional4 );
+}
+
+void YahooContact::readYABEntry()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ if( m_YABEntry )
+ delete m_YABEntry;
+
+ m_YABEntry = new YABEntry;
+ m_YABEntry->yahooId = userId();
+ // Personal
+ m_YABEntry->firstName = property( YahooProtocol::protocol()->propfirstName ).value().toString();
+ m_YABEntry->secondName = property( YahooProtocol::protocol()->propSecondName ).value().toString();
+ m_YABEntry->lastName = property( YahooProtocol::protocol()->propLastName ).value().toString();
+ m_YABEntry->nickName = property( YahooProtocol::protocol()->propNickName ).value().toString();
+ m_YABEntry->title = property( YahooProtocol::protocol()->propTitle ).value().toString();
+
+ // Primary Information
+ m_YABEntry->phoneMobile = property( YahooProtocol::protocol()->propPhoneMobile ).value().toString();
+ m_YABEntry->email = property( YahooProtocol::protocol()->propEmail ).value().toString();
+ m_YABEntry->YABId = property( YahooProtocol::protocol()->propYABId ).value().toInt();
+
+ // Additional Information
+ m_YABEntry->pager = property( YahooProtocol::protocol()->propPager ).value().toString();
+ m_YABEntry->fax = property( YahooProtocol::protocol()->propFax ).value().toString();
+ m_YABEntry->additionalNumber = property( YahooProtocol::protocol()->propAdditionalNumber ).value().toString();
+ m_YABEntry->altEmail1 = property( YahooProtocol::protocol()->propAltEmail1 ).value().toString();
+ m_YABEntry->altEmail2 = property( YahooProtocol::protocol()->propAltEmail2 ).value().toString();
+ m_YABEntry->imAIM = property( YahooProtocol::protocol()->propImAIM ).value().toString();
+ m_YABEntry->imICQ = property( YahooProtocol::protocol()->propImICQ ).value().toString();
+ m_YABEntry->imMSN = property( YahooProtocol::protocol()->propImMSN ).value().toString();
+ m_YABEntry->imGoogleTalk = property( YahooProtocol::protocol()->propImGoogleTalk ).value().toString();
+ m_YABEntry->imSkype = property( YahooProtocol::protocol()->propImSkype ).value().toString();
+ m_YABEntry->imIRC = property( YahooProtocol::protocol()->propImIRC ).value().toString();
+ m_YABEntry->imQQ = property( YahooProtocol::protocol()->propImQQ ).value().toString();
+
+ // Private Information
+ m_YABEntry->privateAdress = property( YahooProtocol::protocol()->propPrivateAddress ).value().toString();
+ m_YABEntry->privateCity = property( YahooProtocol::protocol()->propPrivateCity ).value().toString();
+ m_YABEntry->privateState = property( YahooProtocol::protocol()->propPrivateState ).value().toString();
+ m_YABEntry->privateZIP = property( YahooProtocol::protocol()->propPrivateZIP ).value().toString();
+ m_YABEntry->privateCountry = property( YahooProtocol::protocol()->propPrivateCountry ).value().toString();
+ m_YABEntry->privatePhone = property( YahooProtocol::protocol()->propPrivatePhone ).value().toString();
+ m_YABEntry->privateURL = property( YahooProtocol::protocol()->propPrivateURL ).value().toString();
+
+ // Work Information
+ m_YABEntry->corporation = property( YahooProtocol::protocol()->propCorporation ).value().toString();
+ m_YABEntry->workAdress = property( YahooProtocol::protocol()->propWorkAddress ).value().toString();
+ m_YABEntry->workCity = property( YahooProtocol::protocol()->propWorkCity ).value().toString();
+ m_YABEntry->workState = property( YahooProtocol::protocol()->propWorkState ).value().toString();
+ m_YABEntry->workZIP = property( YahooProtocol::protocol()->propWorkZIP ).value().toString();
+ m_YABEntry->workCountry = property( YahooProtocol::protocol()->propWorkCountry ).value().toString();
+ m_YABEntry->workPhone = property( YahooProtocol::protocol()->propWorkPhone ).value().toString();
+ m_YABEntry->workURL = property( YahooProtocol::protocol()->propWorkURL ).value().toString();
+
+ // Miscellanous
+ m_YABEntry->birthday = QDate::fromString( property( YahooProtocol::protocol()->propBirthday ).value().toString(), Qt::ISODate );
+ m_YABEntry->anniversary = QDate::fromString( property( YahooProtocol::protocol()->propAnniversary ).value().toString(), Qt::ISODate );
+ m_YABEntry->notes = property( YahooProtocol::protocol()->propNotes ).value().toString();
+ m_YABEntry->additional1 = property( YahooProtocol::protocol()->propAdditional1 ).value().toString();
+ m_YABEntry->additional2 = property( YahooProtocol::protocol()->propAdditional2 ).value().toString();
+ m_YABEntry->additional3 = property( YahooProtocol::protocol()->propAdditional3 ).value().toString();
+ m_YABEntry->additional4 = property( YahooProtocol::protocol()->propAdditional4 ).value().toString();
+}
+
+#include "yahoocontact.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+//kate: space-indent off; replace-tabs off; indent-mode csands;
+
diff --git a/kopete/protocols/yahoo/yahoocontact.h b/kopete/protocols/yahoo/yahoocontact.h
new file mode 100644
index 00000000..3f5e6d3b
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoocontact.h
@@ -0,0 +1,141 @@
+/*
+ yahoocontact.h - Yahoo Contact
+
+ Copyright (c) 2003-2004 by Matt Rogers <matt.rogers@kdemail.net>
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+
+ Portions based on code by Bruno Rodrigues <bruno.rodrigues@litux.org>
+
+ Copyright (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOCONTACT_H
+#define YAHOOCONTACT_H
+
+/* Kopete Includes */
+#include "kopetecontact.h"
+
+class KAction;
+class KTempFile;
+
+namespace Kopete { class ChatSession; }
+namespace Kopete { class MetaContact; }
+namespace Kopete { class OnlineStatus; }
+namespace Kopete { class Message; }
+class YahooProtocol;
+class YahooAccount;
+class YahooWebcamDialog;
+class YahooChatSession;
+class YABEntry;
+struct KURL;
+
+class YahooContact : public Kopete::Contact
+{
+ Q_OBJECT
+public:
+ YahooContact( YahooAccount *account, const QString &userId, const QString &fullName, Kopete::MetaContact *metaContact );
+ ~YahooContact();
+
+ /** Base Class Reimplementations **/
+ virtual bool isOnline() const;
+ virtual bool isReachable();
+ virtual QPtrList<KAction> *customContextMenuActions();
+ virtual Kopete::ChatSession *manager( Kopete::Contact::CanCreateFlags canCreate= Kopete::Contact::CanCreate );
+ virtual void serialize( QMap<QString, QString> &serializedData, QMap<QString, QString> &addressBookData );
+
+ void setOnlineStatus(const Kopete::OnlineStatus &status);
+ void setYahooStatus( const Kopete::OnlineStatus& );
+ void setStealthed( bool );
+ bool stealthed();
+
+
+ /** The group name getter and setter methods**/
+ QString group() const;
+ void setGroup( const QString& );
+
+ /** The userId getter method**/
+ QString userId() const;
+
+ void receivedWebcamImage( const QPixmap& );
+ void webcamClosed( int );
+ void webcamPaused();
+
+ const YABEntry *yabEntry();
+
+ static QString prepareMessage( const QString &messageText );
+
+public slots:
+ virtual void slotUserInfo();
+ virtual void slotSendFile( const KURL &file );
+ virtual void deleteContact();
+ virtual void sendFile( const KURL &sourceURL = KURL(), const QString &fileName = QString::null, uint fileSize = 0L );
+ void slotUserProfile();
+ void stealthContact();
+ void requestWebcam();
+ void inviteWebcam();
+ void buzzContact();
+ void setDisplayPicture(KTempFile *f, int checksum);
+ void sendBuddyIconInfo( const QString &url, int checksum );
+ void sendBuddyIconUpdate( int type );
+ void sendBuddyIconChecksum( int checksum );
+ void setYABEntry( YABEntry *, bool show = false );
+
+ /**
+ * Must be called after the contact list has been received
+ * or it doesn't work well!
+ */
+ void syncToServer();
+
+ void sync(unsigned int flags);
+
+signals:
+ void signalReceivedWebcamImage( const QPixmap &pic );
+ void signalWebcamClosed( int reason );
+ void signalWebcamPaused();
+ void displayPictureChanged();
+
+private slots:
+ void slotChatSessionDestroyed();
+ void slotSendMessage( Kopete::Message& );
+ void slotTyping( bool );
+ void slotEmitDisplayPictureChanged();
+
+ void closeWebcamDialog();
+ void initWebcamViewer();
+ void inviteConference();
+
+ void writeYABEntry();
+ void readYABEntry();
+
+private:
+ QString m_userId;
+ QString m_groupName;
+ YABEntry *m_YABEntry;
+ YahooChatSession *m_manager;
+ YahooWebcamDialog* m_webcamDialog;
+ YahooAccount* m_account;
+ bool m_stealthed;
+ bool m_receivingWebcam;
+ bool m_sessionActive;
+
+ KAction* m_stealthAction;
+ KAction* m_profileAction;
+ KAction* m_webcamAction;
+ KAction* m_inviteWebcamAction;
+ KAction* m_buzzAction;
+ KAction* m_inviteConferenceAction;
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooeditaccount.cpp b/kopete/protocols/yahoo/yahooeditaccount.cpp
new file mode 100644
index 00000000..c83905ed
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooeditaccount.cpp
@@ -0,0 +1,197 @@
+/*
+ yahooeditaccount.cpp - UI Page to edit a Yahoo account
+
+ Copyright (c) 2003 by Matt Rogers <mattrogers@sbcglobal.net>
+ Copyright (c) 2002 by Gav Wood <gav@kde.org>
+
+ Copyright (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+// QT Includes
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qspinbox.h>
+
+// KDE Includes
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <krun.h>
+#include <kurl.h>
+#include <kfiledialog.h>
+#include <kpassdlg.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kpixmapregionselectordialog.h>
+
+// Kopete Includes
+#include <addcontactpage.h>
+
+// Local Includes
+#include "yahooaccount.h"
+#include "yahoocontact.h"
+#include "yahooeditaccount.h"
+
+// Yahoo Add Contact page
+YahooEditAccount::YahooEditAccount(YahooProtocol *protocol, Kopete::Account *theAccount, QWidget *parent, const char* /*name*/): YahooEditAccountBase(parent), KopeteEditAccountWidget(theAccount)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ theProtocol = protocol;
+
+ mPasswordWidget = new Kopete::UI::PasswordWidget( mAccountInfo );
+ mAccountInfoLayout->add( mPasswordWidget );
+
+ if(YahooAccount *acct = dynamic_cast<YahooAccount*>(account()))
+ { mScreenName->setText(acct->accountId());
+ mScreenName->setReadOnly(true); //the accountId is Constant FIXME: remove soon!
+ mScreenName->setDisabled(true);
+ mAutoConnect->setChecked(acct->excludeConnect());
+ mPasswordWidget->load( &acct->password() );
+
+ QString pagerServer = account()->configGroup()->readEntry("Server", "scs.msg.yahoo.com");
+ int pagerPort = account()->configGroup()->readNumEntry("Port", 5050);
+ if( pagerServer != "scs.msg.yahoo.com" || pagerPort != 5050 )
+ optionOverrideServer->setChecked( true );
+ else
+ optionOverrideServer->setChecked( false );
+ editServerAddress->setText( pagerServer );
+ sbxServerPort->setValue( pagerPort );
+
+ QString iconUrl = account()->configGroup()->readEntry("pictureUrl", "");
+ bool sendPicture = account()->configGroup()->readBoolEntry("sendPicture", false);
+ optionSendBuddyIcon->setChecked( sendPicture );
+ buttonSelectPicture->setEnabled( sendPicture );
+ connect( optionSendBuddyIcon, SIGNAL( toggled( bool ) ), buttonSelectPicture, SLOT( setEnabled( bool ) ) );
+ editPictureUrl->setText( iconUrl );
+ if( !iconUrl.isEmpty() )
+ m_Picture->setPixmap( KURL( iconUrl ).path() );
+ editPictureUrl->setEnabled( sendPicture );
+
+ // Global Identity
+ mGlobalIdentity->setChecked( account()->configGroup()->readBoolEntry("ExcludeGlobalIdentity", false) );
+ }
+
+ QObject::connect(buttonRegister, SIGNAL(clicked()), this, SLOT(slotOpenRegister()));
+ QObject::connect(buttonSelectPicture, SIGNAL(clicked()), this, SLOT(slotSelectPicture()));
+
+ optionSendBuddyIcon->setEnabled( account() );
+
+ /* Set tab order to password custom widget correctly */
+ QWidget::setTabOrder( mAutoConnect, mPasswordWidget->mRemembered );
+ QWidget::setTabOrder( mPasswordWidget->mRemembered, mPasswordWidget->mPassword );
+ QWidget::setTabOrder( mPasswordWidget->mPassword, buttonRegister );
+
+ show();
+}
+
+bool YahooEditAccount::validateData()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if(mScreenName->text().isEmpty())
+ { KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>You must enter a valid screen name.</qt>"), i18n("Yahoo"));
+ return false;
+ }
+ if(!mPasswordWidget->validate())
+ { KMessageBox::queuedMessageBox(this, KMessageBox::Sorry,
+ i18n("<qt>You must enter a valid password.</qt>"), i18n("Yahoo"));
+ return false;
+ }
+ return true;
+}
+
+Kopete::Account *YahooEditAccount::apply()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ if ( !account() )
+ setAccount( new YahooAccount( theProtocol, mScreenName->text().lower() ) );
+
+ YahooAccount *yahooAccount = static_cast<YahooAccount *>( account() );
+
+ yahooAccount->setExcludeConnect( mAutoConnect->isChecked() );
+
+ mPasswordWidget->save( &yahooAccount->password() );
+
+ if ( optionOverrideServer->isChecked() )
+ {
+ yahooAccount->setServer( editServerAddress->text() );
+ yahooAccount->setPort( sbxServerPort->value() );
+ }
+ else
+ {
+ yahooAccount->setServer( "scs.msg.yahoo.com" );
+ yahooAccount->setPort( 5050 );
+ }
+
+ account()->configGroup()->writeEntry("pictureUrl", editPictureUrl->text() );
+ account()->configGroup()->writeEntry("sendPicture", optionSendBuddyIcon->isChecked() );
+ if ( optionSendBuddyIcon->isChecked() )
+ {
+ yahooAccount->setBuddyIcon( editPictureUrl->text() );
+ }
+ else
+ {
+ yahooAccount->setBuddyIcon( KURL( QString::null ) );
+ }
+
+ // Global Identity
+ account()->configGroup()->writeEntry("ExcludeGlobalIdentity", mGlobalIdentity->isChecked() );
+
+ return yahooAccount;
+}
+
+void YahooEditAccount::slotOpenRegister()
+{
+ KRun::runURL( "http://edit.yahoo.com/config/eval_register?new=1", "text/html" );
+}
+
+void YahooEditAccount::slotSelectPicture()
+{
+ KURL file = KFileDialog::getImageOpenURL( QString::null, this, i18n( "Yahoo Buddy Icon" ) );
+
+ if ( file.isEmpty() )
+ return;
+
+ QImage picture(file.path());
+ if( !picture.isNull() )
+ {
+ picture = KPixmapRegionSelectorDialog::getSelectedImage( QPixmap(picture), 96, 96, this );
+ QString newlocation( locateLocal( "appdata", "yahoopictures/"+ file.fileName().lower() ) ) ;
+ file = KURL(newlocation);
+ if( !picture.save( newlocation, "PNG" ))
+ {
+ KMessageBox::sorry( this, i18n( "An error occurred when trying to change the display picture." ), i18n( "Yahoo Plugin" ) );
+ return;
+ }
+ }
+ else
+ {
+ KMessageBox::sorry( this, i18n( "<qt>The selected buddy icon could not be opened. <br>Please set a new buddy icon.</qt>" ), i18n( "Yahoo Plugin" ) );
+ return;
+ }
+ editPictureUrl->setText( file.path() );
+
+ m_Picture->setPixmap( file.path() );
+}
+
+#include "yahooeditaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooeditaccount.h b/kopete/protocols/yahoo/yahooeditaccount.h
new file mode 100644
index 00000000..17a93752
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooeditaccount.h
@@ -0,0 +1,59 @@
+/*
+ yahooeditaccount.h - UI Page to edit a Yahoo account
+
+ Copyright (c) 2003 by Matt Rogers <mattrogers@sbcglobal.net>
+ Copyright (c) 2002 by Gav Wood <gav@kde.org>
+
+ Copyright (c) 2002 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef __YAHOOEDITIDENTITY_H
+#define __YAHOOEDITIDENTITY_H
+
+// KDE Includes
+
+// QT Includes
+
+// Kopete Includes
+#include "editaccountwidget.h"
+#include "kopetepasswordwidget.h"
+
+// Local Includes
+#include "yahooeditaccountbase.h"
+
+namespace Kopete { class Account; }
+
+class YahooEditAccount: public YahooEditAccountBase, public KopeteEditAccountWidget
+{
+ Q_OBJECT
+
+private:
+ YahooProtocol *theProtocol;
+ Kopete::UI::PasswordWidget *mPasswordWidget;
+
+public:
+ YahooEditAccount(YahooProtocol *protocol, Kopete::Account *theAccount, QWidget *parent = 0, const char *name = 0);
+
+ virtual bool validateData();
+
+public slots:
+ virtual Kopete::Account *apply();
+
+private slots:
+ void slotOpenRegister();
+ void slotSelectPicture();
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooprotocol.cpp b/kopete/protocols/yahoo/yahooprotocol.cpp
new file mode 100644
index 00000000..32c3c55c
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooprotocol.cpp
@@ -0,0 +1,209 @@
+/*
+ yahooprotocol.cpp - Yahoo Plugin for Kopete
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2003-2004 by Matt Rogers <matt@matt.rogers.name>
+
+ Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+/* QT Includes */
+
+/* KDE Includes */
+#include <kdebug.h>
+#include <kgenericfactory.h>
+#include <ksimpleconfig.h>
+
+/* Local Includes */
+#include "yahooprotocol.h"
+#include "yahooaccount.h"
+#include "yahooaddcontact.h"
+#include "yahooeditaccount.h"
+
+/* Kopete Includes */
+#include "kopeteaccountmanager.h"
+#include "kopeteonlinestatusmanager.h"
+#include "kopeteglobal.h"
+
+typedef KGenericFactory<YahooProtocol> YahooProtocolFactory;
+K_EXPORT_COMPONENT_FACTORY( kopete_yahoo, YahooProtocolFactory( "kopete_yahoo" ) )
+
+YahooProtocol::YahooProtocol( QObject *parent, const char *name, const QStringList & )
+ : Kopete::Protocol( YahooProtocolFactory::instance(), parent, name ),
+ Offline( Kopete::OnlineStatus::Offline, 0, this, 0x5a55aa56, QString::null, i18n( "Offline" ), i18n( "Offline" ), Kopete::OnlineStatusManager::Offline ),
+ Online( Kopete::OnlineStatus::Online, 25, this, 0, QString::null, i18n( "Online" ), i18n( "Online" ), Kopete::OnlineStatusManager::Online, Kopete::OnlineStatusManager::HasAwayMessage ),
+ BeRightBack( Kopete::OnlineStatus::Away, 22, this, 1, "contact_away_overlay", i18n( "Be right back" ), i18n( "Be right back" ) ),
+ Busy( Kopete::OnlineStatus::Away, 20, this, 2, "contact_busy_overlay", i18n( "Busy" ), i18n( "Busy" ), Kopete::OnlineStatusManager::Busy, Kopete::OnlineStatusManager::HasAwayMessage ),
+ NotAtHome( Kopete::OnlineStatus::Away, 17, this, 3, "contact_xa_overlay", i18n( "Not at home" ), i18n( "Not at home" ), Kopete::OnlineStatusManager::ExtendedAway ),
+ NotAtMyDesk( Kopete::OnlineStatus::Away, 18, this, 4, "contact_xa_overlay", i18n( "Not at my desk"), i18n( "Not at my desk"), Kopete::OnlineStatusManager::Away ),
+ NotInTheOffice( Kopete::OnlineStatus::Away, 16, this, 5, "contact_xa_overlay", i18n( "Not in the office" ), i18n( "Not in the office" ) ),
+ OnThePhone( Kopete::OnlineStatus::Away, 12, this, 6, "contact_phone_overlay", i18n( "On the phone" ), i18n( "On the phone" ) ),
+ OnVacation( Kopete::OnlineStatus::Away, 3, this, 7, "contact_xa_overlay", i18n( "On vacation" ), i18n( "On vacation" ) ),
+ OutToLunch( Kopete::OnlineStatus::Away, 10, this, 8, "contact_food_overlay", i18n( "Out to lunch" ), i18n( "Out to lunch" ) ),
+ SteppedOut( Kopete::OnlineStatus::Away, 14, this, 9, "contact_away_overlay", i18n( "Stepped out" ), i18n( "Stepped out" ) ),
+ Invisible( Kopete::OnlineStatus::Invisible, 3, this, 12, "contact_invisible_overlay", i18n( "Invisible" ), i18n( "Invisible" ), Kopete::OnlineStatusManager::Invisible ),
+ Custom( Kopete::OnlineStatus::Away, 25, this, 99, "contact_busy_overlay", i18n( "Custom" ), i18n( "Custom" ), Kopete::OnlineStatusManager::HideFromMenu ),
+ Idle( Kopete::OnlineStatus::Away, 15, this, 999, "yahoo_idle", i18n( "Idle" ), i18n( "Idle" ), Kopete::OnlineStatusManager::Idle ),
+ Connecting( Kopete::OnlineStatus::Connecting,2, this, 555, "yahoo_connecting", i18n( "Connecting" ) ),
+ awayMessage(Kopete::Global::Properties::self()->awayMessage()),
+ iconCheckSum("iconCheckSum", i18n("Buddy Icon Checksum"), QString::null, true, false, true),
+ iconExpire("iconExpire", i18n("Buddy Icon Expire"), QString::null, true, false, true),
+ iconRemoteUrl("iconRemoteUrl", i18n("Buddy Icon Remote Url"), QString::null, true, false, true),
+ propfirstName(Kopete::Global::Properties::self()->firstName()),
+ propSecondName(),
+ propLastName(Kopete::Global::Properties::self()->lastName()),
+ propNickName(Kopete::Global::Properties::self()->nickName()),
+ propTitle("YABTitle", i18n("Title"), QString::null, true, false),
+ propPhoneMobile(Kopete::Global::Properties::self()->privateMobilePhone()),
+ propEmail(Kopete::Global::Properties::self()->emailAddress()),
+ propYABId("YABId", i18n("YAB Id"), QString::null, true, false, true),
+ propPager("YABPager", i18n("Pager number"), QString::null, true, false),
+ propFax("YABFax", i18n("Fax number"), QString::null, true, false),
+ propAdditionalNumber("YABAdditionalNumber", i18n("Additional number"), QString::null, true, false),
+ propAltEmail1("YABAlternativeEmail1", i18n("Alternative email 1"), QString::null, true, false),
+ propAltEmail2("YABAlternativeEmail2", i18n("Alternative email 1"), QString::null, true, false),
+ propImAIM("YABIMAIM", i18n("AIM"), QString::null, true, false),
+ propImICQ("YABIMICQ", i18n("ICQ"), QString::null, true, false),
+ propImMSN("YABIMMSN", i18n("MSN"), QString::null, true, false),
+ propImGoogleTalk("YABIMGoogleTalk", i18n("GoogleTalk"), QString::null, true, false),
+ propImSkype("YABIMSkype", i18n("Skype"), QString::null, true, false),
+ propImIRC("YABIMIRC", i18n("IRC"), QString::null, true, false),
+ propImQQ("YABIMQQ", i18n("QQ"), QString::null, true, false),
+ propPrivateAddress("YABPrivateAddress", i18n("Private Address"), QString::null, true, false),
+ propPrivateCity("YABPrivateCity", i18n("Private City"), QString::null, true, false),
+ propPrivateState("YABPrivateState", i18n("Private State"), QString::null, true, false),
+ propPrivateZIP("YABPrivateZIP", i18n("Private ZIP"), QString::null, true, false),
+ propPrivateCountry("YABPrivateCountry", i18n("Private Country"), QString::null, true, false),
+ propPrivatePhone(Kopete::Global::Properties::self()->privatePhone()),
+ propPrivateURL("YABPrivateURL", i18n("Private URL"), QString::null, true, false),
+ propCorporation("YABCorporation", i18n("Corporation"), QString::null, true, false),
+ propWorkAddress("YABWorkAddress", i18n("Work Address"), QString::null, true, false),
+ propWorkCity("YABWorkCity", i18n("Work City"), QString::null, true, false),
+ propWorkState("YABWorkState", i18n("Work State"), QString::null, true, false),
+ propWorkZIP("YABWorkZIP", i18n("Work ZIP"), QString::null, true, false),
+ propWorkCountry("YABWorkCountry", i18n("Work Country"), QString::null, true, false),
+ propWorkPhone(Kopete::Global::Properties::self()->workPhone()),
+ propWorkURL("YABWorkURL", i18n("Work URL"), QString::null, true, false),
+ propBirthday("YABBirthday", i18n("Birthday"), QString::null, true, false),
+ propAnniversary("YABAnniversary", i18n("Anniversary"), QString::null, true, false),
+ propNotes("YABNotes", i18n("Notes"), QString::null, true, false),
+ propAdditional1("YABAdditional1", i18n("Additional 1"), QString::null, true, false),
+ propAdditional2("YABAdditional2", i18n("Additional 2"), QString::null, true, false),
+ propAdditional3("YABAdditional3", i18n("Additional 3"), QString::null, true, false),
+ propAdditional4("YABAdditional4", i18n("Additional 4"), QString::null, true, false)
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ s_protocolStatic_ = this;
+ setCapabilities( RichFgColor | RichFormatting | RichFont );
+ addAddressBookField( "messaging/yahoo", Kopete::Plugin::MakeIndexField );
+}
+
+
+YahooProtocol::~YahooProtocol()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ s_protocolStatic_ = 0L;
+}
+
+YahooProtocol* YahooProtocol::s_protocolStatic_ = 0L;
+
+Kopete::OnlineStatus YahooProtocol::statusFromYahoo( int status )
+{
+ switch ( status )
+ {
+ case 0 :
+ return Online;
+ case 1:
+ return BeRightBack;
+ case 2:
+ return Busy;
+ case 3:
+ return NotAtHome;
+ case 4:
+ return NotAtMyDesk;
+ case 5:
+ return NotInTheOffice;
+ case 6:
+ return OnThePhone;
+ case 7:
+ return OnVacation;
+ case 8:
+ return OutToLunch;
+ case 9:
+ return SteppedOut;
+ case 12:
+ return Invisible;
+ case 99:
+ return Custom;
+ case 999:
+ return Idle;
+ case 0x5a55aa56:
+ return Offline;
+ }
+
+ return Offline;
+}
+
+/***************************************************************************
+ * *
+ * Re-implementation of Plugin class methods *
+ * *
+ ***************************************************************************/
+
+YahooProtocol *YahooProtocol::protocol()
+{
+ return s_protocolStatic_;
+}
+
+Kopete::Contact *YahooProtocol::deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString, QString> &serializedData, const QMap<QString, QString> & /* addressBookData */ )
+{
+ QString contactId = serializedData[ "contactId" ];
+ QString accountId = serializedData[ "accountId" ];
+
+ YahooAccount *theAccount = static_cast<YahooAccount*>(Kopete::AccountManager::self()->findAccount(protocol()->pluginId(), accountId));
+
+ if(!theAccount)
+ { kdDebug( YAHOO_GEN_DEBUG ) << k_funcinfo << "Account " << accountId << " not found" << endl;
+ return 0;
+ }
+
+ if(theAccount->contact(contactId))
+ { kdDebug( YAHOO_GEN_DEBUG ) << k_funcinfo << "User " << contactId << " already in contacts map" << endl;
+ return 0;
+ }
+
+ theAccount->addContact(contactId, metaContact, Kopete::Account::DontChangeKABC);
+ return theAccount->contacts()[contactId];
+}
+
+AddContactPage *YahooProtocol::createAddContactWidget( QWidget * parent , Kopete::Account* )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << "YahooProtocol::createAddContactWidget(<parent>)" << endl;
+ return new YahooAddContact(this, parent);
+}
+
+KopeteEditAccountWidget *YahooProtocol::createEditAccountWidget(Kopete::Account *account, QWidget *parent)
+{
+ return new YahooEditAccount(this, account, parent);
+}
+
+Kopete::Account *YahooProtocol::createNewAccount(const QString &accountId)
+{
+ return new YahooAccount(this, accountId);
+}
+
+#include "yahooprotocol.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooprotocol.h b/kopete/protocols/yahoo/yahooprotocol.h
new file mode 100644
index 00000000..6f399ada
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooprotocol.h
@@ -0,0 +1,148 @@
+/*
+ yahooprotocol.h - Yahoo Plugin for Kopete
+
+ Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
+ Copyright (c) 2003-2004 by Matt Rogers <mattrogers@sbcglobal.net
+
+ Copyright (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
+
+ *************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef YAHOOPROTOCOL_H
+#define YAHOOPROTOCOL_H
+
+// Kopete Includes
+#include "kopeteonlinestatus.h"
+
+// QT Includes
+#include <qpixmap.h>
+#include <qmap.h>
+
+// KDE Includes
+#include "kopeteprotocol.h"
+#include "kopetecontactproperty.h"
+
+class YahooContact;
+class KPopupMenu;
+class KActionMenu;
+class KAction;
+namespace Kopete { class MetaContact; }
+namespace Kopete { class Message; }
+class YahooPreferences;
+namespace Kopete { class OnlineStatus; }
+
+class YahooProtocol : public Kopete::Protocol
+{
+ Q_OBJECT
+public:
+ YahooProtocol( QObject *parent, const char *name, const QStringList &args );
+ ~YahooProtocol();
+
+ //Online Statuses
+ const Kopete::OnlineStatus Offline;
+ const Kopete::OnlineStatus Online;
+ const Kopete::OnlineStatus BeRightBack;
+ const Kopete::OnlineStatus Busy;
+ const Kopete::OnlineStatus NotAtHome;
+ const Kopete::OnlineStatus NotAtMyDesk;
+ const Kopete::OnlineStatus NotInTheOffice;
+ const Kopete::OnlineStatus OnThePhone;
+ const Kopete::OnlineStatus OnVacation;
+ const Kopete::OnlineStatus OutToLunch;
+ const Kopete::OnlineStatus SteppedOut;
+ const Kopete::OnlineStatus Invisible;
+ const Kopete::OnlineStatus Custom;
+ const Kopete::OnlineStatus Idle;
+ const Kopete::OnlineStatus Connecting;
+
+ const Kopete::ContactPropertyTmpl awayMessage;
+ const Kopete::ContactPropertyTmpl iconCheckSum;
+ const Kopete::ContactPropertyTmpl iconExpire;
+ const Kopete::ContactPropertyTmpl iconRemoteUrl;
+
+ // Personal
+ const Kopete::ContactPropertyTmpl propfirstName;
+ const Kopete::ContactPropertyTmpl propSecondName;
+ const Kopete::ContactPropertyTmpl propLastName;
+ const Kopete::ContactPropertyTmpl propNickName;
+ const Kopete::ContactPropertyTmpl propTitle;
+
+ // Primary Information
+ const Kopete::ContactPropertyTmpl propPhoneMobile;
+ const Kopete::ContactPropertyTmpl propEmail;
+ const Kopete::ContactPropertyTmpl propYABId;
+
+ // Additional Information
+ const Kopete::ContactPropertyTmpl propPager;
+ const Kopete::ContactPropertyTmpl propFax;
+ const Kopete::ContactPropertyTmpl propAdditionalNumber;
+ const Kopete::ContactPropertyTmpl propAltEmail1;
+ const Kopete::ContactPropertyTmpl propAltEmail2;
+ const Kopete::ContactPropertyTmpl propImAIM;
+ const Kopete::ContactPropertyTmpl propImICQ;
+ const Kopete::ContactPropertyTmpl propImMSN;
+ const Kopete::ContactPropertyTmpl propImGoogleTalk;
+ const Kopete::ContactPropertyTmpl propImSkype;
+ const Kopete::ContactPropertyTmpl propImIRC;
+ const Kopete::ContactPropertyTmpl propImQQ;
+
+ // Private Information
+ const Kopete::ContactPropertyTmpl propPrivateAddress;
+ const Kopete::ContactPropertyTmpl propPrivateCity;
+ const Kopete::ContactPropertyTmpl propPrivateState;
+ const Kopete::ContactPropertyTmpl propPrivateZIP;
+ const Kopete::ContactPropertyTmpl propPrivateCountry;
+ const Kopete::ContactPropertyTmpl propPrivatePhone;
+ const Kopete::ContactPropertyTmpl propPrivateURL;
+
+ // Work Information
+ const Kopete::ContactPropertyTmpl propCorporation;
+ const Kopete::ContactPropertyTmpl propWorkAddress;
+ const Kopete::ContactPropertyTmpl propWorkCity;
+ const Kopete::ContactPropertyTmpl propWorkState;
+ const Kopete::ContactPropertyTmpl propWorkZIP;
+ const Kopete::ContactPropertyTmpl propWorkCountry;
+ const Kopete::ContactPropertyTmpl propWorkPhone;
+ const Kopete::ContactPropertyTmpl propWorkURL;
+
+ // Miscellanous
+ const Kopete::ContactPropertyTmpl propBirthday;
+ const Kopete::ContactPropertyTmpl propAnniversary;
+ const Kopete::ContactPropertyTmpl propNotes;
+ const Kopete::ContactPropertyTmpl propAdditional1;
+ const Kopete::ContactPropertyTmpl propAdditional2;
+ const Kopete::ContactPropertyTmpl propAdditional3;
+ const Kopete::ContactPropertyTmpl propAdditional4;
+
+ /** Protocol Accessor **/
+ static YahooProtocol *protocol();
+
+ virtual Kopete::Contact *deserializeContact( Kopete::MetaContact *metaContact,
+ const QMap<QString,QString> &serializedData,
+ const QMap<QString, QString> &addressBookData );
+
+ Kopete::OnlineStatus statusFromYahoo( int status );
+
+public slots:
+ virtual AddContactPage *createAddContactWidget(QWidget * parent, Kopete::Account* a);
+ virtual KopeteEditAccountWidget *createEditAccountWidget(Kopete::Account *account, QWidget *parent);
+ virtual Kopete::Account *createNewAccount(const QString &accountId);
+
+
+private:
+ static YahooProtocol* s_protocolStatic_;
+
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooverifyaccount.cpp b/kopete/protocols/yahoo/yahooverifyaccount.cpp
new file mode 100644
index 00000000..cfb3ede6
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooverifyaccount.cpp
@@ -0,0 +1,107 @@
+/*
+ yahooverifyaccount.cpp - UI Page for Verifying a locked account
+
+ Copyright (c) 2005 by André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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. *
+ * *
+ *************************************************************************
+*/
+
+// QT Includes
+#include <qlayout.h>
+#include <qfile.h>
+#include <qlabel.h>
+
+// KDE Includes
+#include <kdebug.h>
+#include <klineedit.h>
+#include <ktempfile.h>
+#include <klocale.h>
+#include <kio/global.h>
+#include <kio/job.h>
+#include <kio/jobclasses.h>
+#include <kstandarddirs.h>
+
+// Kopete Includes
+#include <yahooverifyaccountbase.h>
+#include <kopeteaccount.h>
+
+// Local Includes
+#include "yahooverifyaccountbase.h"
+#include "yahooverifyaccount.h"
+#include "yahooaccount.h"
+
+YahooVerifyAccount::YahooVerifyAccount(Kopete::Account *account, QWidget *parent, const char *name)
+: KDialogBase(parent, name, true, i18n("Account Verification - Yahoo"), Cancel|Apply,
+ Apply, true )
+{
+ mTheAccount = account;
+ mTheDialog = new YahooVerifyAccountBase( this );
+ mTheDialog->mPicture->hide();
+ setMainWidget( mTheDialog );
+ setEscapeButton( Cancel );
+}
+
+// Destructor
+YahooVerifyAccount::~YahooVerifyAccount()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+}
+
+void YahooVerifyAccount::setUrl( KURL url )
+{
+ mFile = new KTempFile( locateLocal( "tmp", url.fileName() ) );
+ mFile->setAutoDelete( true );
+ KIO::TransferJob *transfer = KIO::get( url, false, false );
+ connect( transfer, SIGNAL( result( KIO::Job* ) ), this, SLOT( slotComplete( KIO::Job* ) ) );
+ connect( transfer, SIGNAL( data( KIO::Job*, const QByteArray& ) ), this, SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
+}
+
+void YahooVerifyAccount::slotData( KIO::Job */*job*/, const QByteArray& data )
+{
+
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ mFile->file()->writeBlock( data.data() , data.size() );
+}
+
+void YahooVerifyAccount::slotComplete( KIO::Job */*job*/ )
+{
+
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ mFile->file()->close();
+ mTheDialog->mPicture->setPixmap( mFile->file()->name() );
+ mTheDialog->mPicture->show();
+}
+
+bool YahooVerifyAccount::validateData()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ return ( !mTheDialog->mWord->text().isEmpty() );
+}
+
+void YahooVerifyAccount::slotClose()
+{
+ QDialog::done(0);
+}
+
+void YahooVerifyAccount::slotApply()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ YahooAccount* myAccount = static_cast<YahooAccount*>(mTheAccount);
+ myAccount->verifyAccount( mTheDialog->mWord->text() );
+ QDialog::done(0);
+}
+
+#include "yahooverifyaccount.moc"
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahooverifyaccount.h b/kopete/protocols/yahoo/yahooverifyaccount.h
new file mode 100644
index 00000000..237a45a4
--- /dev/null
+++ b/kopete/protocols/yahoo/yahooverifyaccount.h
@@ -0,0 +1,57 @@
+/*
+ yahooverifyaccount.h - UI Page for Verifying a locked account
+
+ Copyright (c) 2005 by André Duffeck <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 __YAHOOVERIFYACCOUNT_H
+#define __YAHOOVERIFYACCOUNT_H
+
+// Local Includes
+
+// Kopete Includes
+// QT Includes
+
+// KDE Includes
+#include <kdialogbase.h>
+
+namespace Kopete { class Account; }
+class YahooVerifyAccountBase;
+class KTempFile;
+
+class YahooVerifyAccount : public KDialogBase
+{
+ Q_OBJECT
+private:
+ Kopete::Account *mTheAccount;
+ KTempFile *mFile;
+ YahooVerifyAccountBase *mTheDialog;
+public:
+ YahooVerifyAccount(Kopete::Account *account, QWidget *parent = 0, const char *name = 0);
+ ~YahooVerifyAccount();
+
+ virtual bool validateData();
+
+ void setUrl( KURL url );
+
+protected slots:
+ virtual void slotClose();
+ virtual void slotApply();
+public slots:
+ void slotData( KIO::Job *job, const QByteArray& data );
+ void slotComplete( KIO::Job *job );
+};
+
+#endif
+
+// vim: set noet ts=4 sts=4 sw=4:
+
diff --git a/kopete/protocols/yahoo/yahoowebcam.cpp b/kopete/protocols/yahoo/yahoowebcam.cpp
new file mode 100644
index 00000000..71ff921a
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoowebcam.cpp
@@ -0,0 +1,137 @@
+/*
+ yahoowebcam.cpp - Send webcam images
+
+ Copyright (c) 2005 by André Duffec <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 <kdebug.h>
+#include <kprocess.h>
+#include <ktempfile.h>
+#include <qtimer.h>
+
+#include "client.h"
+#include "yahoowebcam.h"
+#include "yahooaccount.h"
+#include "yahoowebcamdialog.h"
+#include "avdevice/videodevicepool.h"
+
+
+YahooWebcam::YahooWebcam( YahooAccount *account ) : QObject( 0, "yahoo_webcam" )
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+ theAccount = account;
+ theDialog = 0L;
+ origImg = new KTempFile();
+ convertedImg = new KTempFile();
+ m_img = new QImage();
+
+ m_sendTimer = new QTimer( this );
+ connect( m_sendTimer, SIGNAL(timeout()), this, SLOT(sendImage()) );
+
+ m_updateTimer = new QTimer( this );
+ connect( m_updateTimer, SIGNAL(timeout()), this, SLOT(updateImage()) );
+
+ theDialog = new YahooWebcamDialog( "YahooWebcam" );
+ connect( theDialog, SIGNAL(closingWebcamDialog()), this, SLOT(webcamDialogClosing()) );
+
+ m_devicePool = Kopete::AV::VideoDevicePool::self();
+ m_devicePool->open();
+ m_devicePool->setSize(320, 240);
+ m_devicePool->startCapturing();
+ m_updateTimer->start( 250 );
+}
+
+YahooWebcam::~YahooWebcam()
+{
+ QFile::remove( origImg->name() );
+ QFile::remove( convertedImg->name() );
+ delete origImg;
+ delete convertedImg;
+ delete m_img;
+}
+
+void YahooWebcam::stopTransmission()
+{
+ m_sendTimer->stop();
+}
+
+void YahooWebcam::startTransmission()
+{
+ m_sendTimer->start( 1000 );
+}
+
+void YahooWebcam::webcamDialogClosing()
+{
+ m_sendTimer->stop();
+ theDialog->delayedDestruct();
+ emit webcamClosing();
+ m_devicePool->stopCapturing();
+ m_devicePool->close();
+}
+
+void YahooWebcam::updateImage()
+{
+ m_devicePool->getFrame();
+ m_devicePool->getImage(m_img);
+ theDialog->newImage( *m_img );
+}
+
+void YahooWebcam::sendImage()
+{
+ kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
+
+ m_devicePool->getFrame();
+ m_devicePool->getImage(m_img);
+
+ origImg->close();
+ convertedImg->close();
+
+ m_img->save( origImg->name(), "JPEG");
+
+ KProcess p;
+ p << "jasper";
+ p << "--input" << origImg->name() << "--output" << convertedImg->name() << "--output-format" << "jpc" << "-O" <<"cblkwidth=64\ncblkheight=64\nnumrlvls=4\nrate=0.0165\nprcheight=128\nprcwidth=2048\nmode=real";
+
+
+ p.start( KProcess::Block );
+ if( p.exitStatus() != 0 )
+ {
+ kdDebug(YAHOO_GEN_DEBUG) << " jasper exited with status " << p.exitStatus() << endl;
+ }
+ else
+ {
+ QFile file( convertedImg->name() );
+ if( file.open( IO_ReadOnly ) )
+ {
+ QByteArray ar = file.readAll();
+ theAccount->yahooSession()->sendWebcamImage( ar );
+ }
+ else
+ kdDebug(YAHOO_GEN_DEBUG) << "Error opening the converted webcam image." << endl;
+ }
+}
+
+void YahooWebcam::addViewer( const QString &viewer )
+{
+ m_viewer.push_back( viewer );
+ if( theDialog )
+ theDialog->setViewer( m_viewer );
+}
+
+void YahooWebcam::removeViewer( const QString &viewer )
+{
+ m_viewer.remove( viewer );
+ if( theDialog )
+ theDialog->setViewer( m_viewer );
+}
+
+#include "yahoowebcam.moc"
diff --git a/kopete/protocols/yahoo/yahoowebcam.h b/kopete/protocols/yahoo/yahoowebcam.h
new file mode 100644
index 00000000..46032059
--- /dev/null
+++ b/kopete/protocols/yahoo/yahoowebcam.h
@@ -0,0 +1,62 @@
+/*
+ yahoowebcam.h - Send webcam images
+
+ Copyright (c) 2005 by André Duffec <andre.duffeck@kdemail.net>
+
+ *************************************************************************
+ * *
+ * 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 YAHOOWEBCAM_H
+#define YAHOOWEBCAM_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class YahooAccount;
+class YahooWebcamDialog;
+class QTimer;
+class QImage;
+class KTempFile;
+
+namespace Kopete {
+ namespace AV {
+ class VideoDevicePool;
+ }
+}
+
+class YahooWebcam : public QObject
+{
+ Q_OBJECT
+public:
+ YahooWebcam( YahooAccount *account );
+ ~YahooWebcam();
+public slots:
+ void startTransmission();
+ void stopTransmission();
+ void sendImage();
+ void updateImage();
+ void webcamDialogClosing();
+ void addViewer( const QString & );
+ void removeViewer( const QString & );
+signals:
+ void webcamClosing();
+private:
+ YahooAccount *theAccount;
+ YahooWebcamDialog *theDialog;
+ QTimer *m_sendTimer;
+ QTimer *m_updateTimer;
+ QStringList m_viewer;
+ QImage *m_img;
+ KTempFile *origImg;
+ KTempFile *convertedImg;
+ Kopete::AV::VideoDevicePool *m_devicePool;
+};
+
+#endif
diff --git a/kopete/sounds/Kopete_Event.ogg b/kopete/sounds/Kopete_Event.ogg
new file mode 100644
index 00000000..60bb8992
--- /dev/null
+++ b/kopete/sounds/Kopete_Event.ogg
Binary files differ
diff --git a/kopete/sounds/Kopete_Received.ogg b/kopete/sounds/Kopete_Received.ogg
new file mode 100644
index 00000000..9b83dc4e
--- /dev/null
+++ b/kopete/sounds/Kopete_Received.ogg
Binary files differ
diff --git a/kopete/sounds/Kopete_Sent.ogg b/kopete/sounds/Kopete_Sent.ogg
new file mode 100644
index 00000000..088ec06d
--- /dev/null
+++ b/kopete/sounds/Kopete_Sent.ogg
Binary files differ
diff --git a/kopete/sounds/Kopete_User_is_Online.ogg b/kopete/sounds/Kopete_User_is_Online.ogg
new file mode 100644
index 00000000..39475e7a
--- /dev/null
+++ b/kopete/sounds/Kopete_User_is_Online.ogg
Binary files differ
diff --git a/kopete/sounds/Makefile.am b/kopete/sounds/Makefile.am
new file mode 100644
index 00000000..83554134
--- /dev/null
+++ b/kopete/sounds/Makefile.am
@@ -0,0 +1,4 @@
+kde_sound_DATA = Kopete_Event.ogg Kopete_Received.ogg Kopete_Sent.ogg Kopete_User_is_Online.ogg
+
+EXTRA_DIST = $(kde_sound_DATA)
+
diff --git a/kopete/styles/Clean/Contents/Makefile.am b/kopete/styles/Clean/Contents/Makefile.am
new file mode 100644
index 00000000..6940fe81
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources \ No newline at end of file
diff --git a/kopete/styles/Clean/Contents/Resources/Footer.html b/kopete/styles/Clean/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Footer.html
diff --git a/kopete/styles/Clean/Contents/Resources/Header.html b/kopete/styles/Clean/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Header.html
diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/Action.html b/kopete/styles/Clean/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..ead5823e
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="IncomingAction" style="direction: %messageDirection%;">
+ <!-- Protocol Icon -->
+ <img class="inActionIcon" src="images/action.png" />
+ <!-- MetaContact display -->
+ <span class="inActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="inActionMessage">%message%</span>
+ <!-- Time Display -->
+ <span class="inTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+</div>
+
diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/Content.html b/kopete/styles/Clean/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..b9c7c90d
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,21 @@
+<div class="KopeteMessage">
+ <div class="IncomingMessageHeader" style="border-color: %senderColor%">
+ <!-- Protocol Icon -->
+ <img class="inStatusIcon" src="%senderStatusIcon%" />
+ <!-- Time Display -->
+ <div class="inTime">%time%</div>
+ <!-- MetaContact display -->
+ <div class="inMetacontact">%sender% &#160;</div>
+ </div>
+ <div class="IncomingMessage" style="direction: %messageDirection%;">
+ <!-- Contact photo -->
+ <img class="inUserPicture" src="%userIconPath%" />
+ %message%
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ <div style="clear: both;">&nbsp;</div>
+
+ </div>
+
+</div>
+
diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Clean/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..df31f61c
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Clean/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Clean/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..c4196670
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,3 @@
+<div class="NextIncomingMessage" style="direction: %messageDirection%;">%message%</div>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Clean/Contents/Resources/Incoming/buddy_icon.png b/kopete/styles/Clean/Contents/Resources/Incoming/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Incoming/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Clean/Contents/Resources/Makefile.am b/kopete/styles/Clean/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..3a949700
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = images Incoming Outgoing
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Clean/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/Action.html b/kopete/styles/Clean/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..ae5c6043
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="OutgoingAction" style="direction: %messageDirection%;">
+ <!-- Protocol Icon -->
+ <img class="outActionIcon" src="images/action.png" />
+ <!-- MetaContact display -->
+ <span class="outActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="outActionMessage">%message%</span>
+ <!-- Time Display -->
+ <span class="outTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+</div>
+
diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/Content.html b/kopete/styles/Clean/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..d08299bd
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,21 @@
+<div class="KopeteMessage">
+ <div class="OutgoingMessageHeader">
+ <!-- Protocol Icon -->
+ <img class="outStatusIcon" src="%senderStatusIcon%" />
+ <!-- Time Display -->
+ <div class="outTime">%time%</div>
+ <!-- MetaContact display -->
+ <div class="outMetacontact">%sender% &#160;</div>
+ </div>
+ <div class="OutgoingMessage" style="direction: %messageDirection%;">
+ <!-- Contact photo -->
+ <img class="outUserPicture" src="%userIconPath%" />
+ %message%
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ <div style="clear: both;">&nbsp;</div>
+ </div>
+
+</div>
+
+
diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Clean/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..39f997d3
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Clean/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Clean/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..76931ca4
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,3 @@
+<div class="NextOutgoingMessage" style="direction: %messageDirection%;">%message%</div>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Clean/Contents/Resources/Outgoing/buddy_icon.png b/kopete/styles/Clean/Contents/Resources/Outgoing/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Outgoing/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Clean/Contents/Resources/Status.html b/kopete/styles/Clean/Contents/Resources/Status.html
new file mode 100644
index 00000000..4e883fd5
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/Status.html
@@ -0,0 +1,10 @@
+<div class="KopeteMessage">
+ <div class="InternalMessageHeader" style="direction: %messageDirection%;">
+ <img class="systemLogo" src="images/internal.png" />
+
+ <span class="InternalMessage">%message%</span>
+ <div class="InternalMessageHeaderTime">%time%</div>
+
+
+ </div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Clean/Contents/Resources/images/Makefile.am b/kopete/styles/Clean/Contents/Resources/images/Makefile.am
new file mode 100644
index 00000000..6d97fcaa
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/images/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = action.png important.png internal.png
+styledir = $(kde_datadir)/kopete/styles/Clean/Contents/Resources/images
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clean/Contents/Resources/images/action.png b/kopete/styles/Clean/Contents/Resources/images/action.png
new file mode 100644
index 00000000..543710fb
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/images/action.png
Binary files differ
diff --git a/kopete/styles/Clean/Contents/Resources/images/important.png b/kopete/styles/Clean/Contents/Resources/images/important.png
new file mode 100644
index 00000000..474f63fc
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/images/important.png
Binary files differ
diff --git a/kopete/styles/Clean/Contents/Resources/images/internal.png b/kopete/styles/Clean/Contents/Resources/images/internal.png
new file mode 100644
index 00000000..838c38bf
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/images/internal.png
Binary files differ
diff --git a/kopete/styles/Clean/Contents/Resources/main.css b/kopete/styles/Clean/Contents/Resources/main.css
new file mode 100644
index 00000000..011cd298
--- /dev/null
+++ b/kopete/styles/Clean/Contents/Resources/main.css
@@ -0,0 +1,169 @@
+.KopeteMessage
+{
+ margin:1.4em .2em 0 .2em;
+}
+
+.IncomingMessageHeader
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ border-bottom: 0.1em solid;
+}
+
+.IncomingMessage
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ padding-top: 0.25em;
+}
+
+.NextIncomingMessage
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ padding-top: 0.25em;
+ padding-bottom: 0.5em;
+}
+
+.inUserPicture
+{
+ float: left;
+ border: 1px solid #888;
+ height: 4.30em;
+ margin-top: 0.25em;
+ border: 0.1em solid black;
+ margin-left: 0.2em;
+ margin-right: 1ex;
+
+}
+
+.inStatusIcon
+{
+ float: left;
+ padding-right: 1ex;
+}
+
+.inTime
+{
+ float: right;
+}
+.inMetacontact
+{
+ padding-left: 1ex;
+ font-weight: bold;
+}
+
+.IncomingAction
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ border-bottom: 0.1em dashed #ffafaf;
+}
+.inActionIcon
+{
+ float: left;
+ margin-right: 5px;
+}
+.inActionMetacontact
+{
+ margin-left: 5px;
+ font-weight: bold;
+}
+.inActionMessage
+{
+
+}
+
+.OutgoingMessageHeader
+{
+ padding-left: 1ex; padding-right: 1ex;
+ border-bottom: 0.1em solid #ffafaf;
+}
+
+.OutgoingMessage
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ padding-top: 0.25em;
+ padding-bottom: 0.5em;
+}
+
+.NextOutgoingMessage
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ padding-top: 0.25em;
+ padding-bottom: 0.5em;
+}
+
+.outUserPicture
+{
+ float: left;
+ border: 1px solid #888;
+ height: 4.30em;
+ margin-top: 0.2em;
+ margin-left: 0.2em;
+ margin-right: 1ex;
+}
+
+.outStatusIcon
+{
+ float: left;
+ padding-right: 1ex;
+}
+
+.outTime
+{
+ float: right;
+}
+.outMetacontact
+{
+ margin-left: 15px;
+ font-weight: bold;
+}
+
+.OutgoingAction
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ border-bottom: 0.1em dashed #ffafaf;
+}
+.outActionIcon
+{
+ float: left;
+ padding-right: 1ex;
+}
+.outActionMetacontact
+{
+ margin-left: 10px;
+ font-weight: bold;
+}
+.outActionMessage
+{
+}
+
+.InternalMessageHeader
+{
+ padding-left: 1ex;
+ padding-right: 1ex;
+ border-bottom: 0.1em dashed #afffaf;
+}
+
+.InternalMessage
+{
+ width: 80%;
+ text-align: left;
+}
+
+.InternalMessageHeaderTime
+{
+ float: right;
+}
+
+.systemLogo
+{
+ float: left;
+ vertical-align: middle;
+ padding-right: 1ex;
+}
+
diff --git a/kopete/styles/Clean/Makefile.am b/kopete/styles/Clean/Makefile.am
new file mode 100644
index 00000000..331c9b59
--- /dev/null
+++ b/kopete/styles/Clean/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Makefile.am b/kopete/styles/Clear/Contents/Makefile.am
new file mode 100644
index 00000000..6940fe81
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/Footer.html b/kopete/styles/Clear/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Footer.html
diff --git a/kopete/styles/Clear/Contents/Resources/Header.html b/kopete/styles/Clear/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Header.html
diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/Action.html b/kopete/styles/Clear/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..7f37423d
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="IncomingAction">
+ <span class="IncomingActionBody">
+ <!-- MetaContact display -->
+ <span class="inActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="inActionMessage">%message%</span>
+ </span>
+ <!-- Time Display -->
+ <span class="IncomingActionTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <!-- <div id="insert" /> -->
+</div>
+
diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/Content.html b/kopete/styles/Clear/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..0b17c975
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,33 @@
+<div class="KopeteMessage">
+ <div class="IncomingMessageHeader">
+ <div class="IncomingMessageHeader1">
+ <div class="IncomingMessageHeader2">
+ <!-- MetaContact display -->
+ <span class="inMetacontact">%sender% &#160;</span>
+ <!-- Time Display -->
+ <span class="inTime">%time%</span>
+ </div>
+ </div>
+ </div>
+ <div class="IncomingBody">
+ <div class="IncomingBody1">
+ <div class="IncomingBody2">
+ <div class="inUserPicture">
+ <!-- Contact photo -->
+ <img width="46" height="46" src="%userIconPath%" />
+ </div>
+ <div class="IncomingMessage" style="direction: %messageDirection%;">
+ <img src="images/body-inbound-arrow.png" style="padding-bottom: 2px; vertical-align: middle;"/>%message%
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ </div>
+ </div>
+ </div>
+ <div class="IncomingFooter">
+ <div class="IncomingFooter1">
+ <div id="IncomingFooter2" />
+ </div>
+ </div>
+
+</div> \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Clear/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..2d8694eb
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html Action.html
+styledir = $(kde_datadir)/kopete/styles/Clear/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Clear/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..e6a4b318
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,3 @@
+<div class="NextIncomingMessage"><img src="images/body-inbound-arrow.png" style="padding-bottom: 2px; vertical-align: middle;"/>%message%</div>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Clear/Contents/Resources/Incoming/buddy_icon.png b/kopete/styles/Clear/Contents/Resources/Incoming/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Incoming/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/Makefile.am b/kopete/styles/Clear/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..736c70ce
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = images Incoming Outgoing Variants
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Clear/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/Action.html b/kopete/styles/Clear/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..4113f659
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="OutgoingAction">
+ <span class="OutgoingActionBody">
+ <!-- MetaContact display -->
+ <span class="outActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="outActionMessage">%message%</span>
+ </span>
+ <!-- Time Display -->
+ <span class="OutgoingActionTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <!-- <div id="insert" /> -->
+</div>
+
diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/Content.html b/kopete/styles/Clear/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..41414e96
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,32 @@
+<div class="KopeteMessage">
+ <div class="OutgoingMessageHeader">
+ <div class="OutgoingMessageHeader1">
+ <div class="OutgoingMessageHeader2">
+ <!-- MetaContact display -->
+ <span class="outMetacontact">%sender% &#160;</span>
+ <!-- Time Display -->
+ <span class="outTime">%time%</span>
+ </div>
+ </div>
+ </div>
+ <div class="OutgoingBody">
+ <div class="OutgoingBody1">
+ <div class="OutgoingBody2">
+ <div class="outUserPicture">
+ <!-- Contact photo -->
+ <img width="46" height="46" src="%userIconPath%" />
+ </div>
+ <div class="OutgoingMessage" style="direction: %messageDirection%;">
+ <img src="images/body-outbound-arrow.png" style="padding-bottom: 2px; vertical-align: middle;"/>%message%
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ </div>
+ </div>
+ </div>
+ <div class="OutgoingFooter">
+ <div class="OutgoingFooter1">
+ <div id="OutgoingFooter2" />
+ </div>
+ </div>
+</div>
diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Clear/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..e8e7db8f
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html Action.html
+styledir = $(kde_datadir)/kopete/styles/Clear/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Clear/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..e8a7cb35
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,3 @@
+<div class="NextOutgoingMessage"><img src="images/body-outbound-arrow.png" style="padding-bottom: 2px; vertical-align: middle;"/>%message%</div>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Clear/Contents/Resources/Outgoing/buddy_icon.png b/kopete/styles/Clear/Contents/Resources/Outgoing/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Outgoing/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/Status.html b/kopete/styles/Clear/Contents/Resources/Status.html
new file mode 100644
index 00000000..9a911667
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Status.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage">
+ <div class="InternalMessageHeader">
+ <span class="InternalMessage">%message%</span>
+ <div class="InternalMessageHeaderTime">%time%</div>
+ </div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/Variants/Makefile.am b/kopete/styles/Clear/Contents/Resources/Variants/Makefile.am
new file mode 100644
index 00000000..2b494718
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Variants/Makefile.am
@@ -0,0 +1,5 @@
+style_DATA = No_avatars.css
+
+styledir = $(kde_datadir)/kopete/styles/Clear/Contents/Resources/Variants
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Clear/Contents/Resources/Variants/No_avatars.css b/kopete/styles/Clear/Contents/Resources/Variants/No_avatars.css
new file mode 100644
index 00000000..d0fc2398
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/Variants/No_avatars.css
@@ -0,0 +1,23 @@
+.IncomingBody2
+{
+ min-height: 0;
+ padding-bottom: 7px;
+}
+
+.inUserPicture
+{
+ display: none;
+ float: none;
+}
+
+.OutgoingBody2
+{
+ min-height: 0;
+ padding-bottom: 7px;
+}
+
+.outUserPicture
+{
+ display: none;
+ float: none;
+}
diff --git a/kopete/styles/Clear/Contents/Resources/images/Makefile.am b/kopete/styles/Clear/Contents/Resources/images/Makefile.am
new file mode 100644
index 00000000..51b16be2
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = body-background.png footer-outbound-right.png body-inbound-arrow.png header-inbound-background.png body-inbound-avatar.png header-inbound-left.png body-inbound-background.png header-inbound-right.png body-inbound-left.png header-outbound-background.png body-inbound-right.png header-outbound-left.png body-outbound-arrow.png header-outbound-right.png body-outbound-avatar.png icon-action.png body-outbound-left.png icon-highlighted.png body-outbound-right.png icon-internal.png footer-inbound-background.png icon-me.png footer-inbound-left.png icon-time.png footer-inbound-right.png icon-you.png footer-outbound-background.png footer-outbound-left.png
+styledir = $(kde_datadir)/kopete/styles/Clear/Contents/Resources/images
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-background.png b/kopete/styles/Clear/Contents/Resources/images/body-background.png
new file mode 100644
index 00000000..09be93d9
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-inbound-arrow.png b/kopete/styles/Clear/Contents/Resources/images/body-inbound-arrow.png
new file mode 100644
index 00000000..c78d108b
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-inbound-arrow.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-inbound-avatar.png b/kopete/styles/Clear/Contents/Resources/images/body-inbound-avatar.png
new file mode 100644
index 00000000..a94643f7
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-inbound-avatar.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-inbound-background.png b/kopete/styles/Clear/Contents/Resources/images/body-inbound-background.png
new file mode 100644
index 00000000..c5d816d1
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-inbound-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-inbound-left.png b/kopete/styles/Clear/Contents/Resources/images/body-inbound-left.png
new file mode 100644
index 00000000..caf7f1c9
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-inbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-inbound-right.png b/kopete/styles/Clear/Contents/Resources/images/body-inbound-right.png
new file mode 100644
index 00000000..8fe17de8
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-inbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-outbound-arrow.png b/kopete/styles/Clear/Contents/Resources/images/body-outbound-arrow.png
new file mode 100644
index 00000000..3ca163ff
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-outbound-arrow.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-outbound-avatar.png b/kopete/styles/Clear/Contents/Resources/images/body-outbound-avatar.png
new file mode 100644
index 00000000..4f6796d4
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-outbound-avatar.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-outbound-left.png b/kopete/styles/Clear/Contents/Resources/images/body-outbound-left.png
new file mode 100644
index 00000000..5302ff4f
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-outbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/body-outbound-right.png b/kopete/styles/Clear/Contents/Resources/images/body-outbound-right.png
new file mode 100644
index 00000000..c787699e
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/body-outbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-inbound-background.png b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-background.png
new file mode 100644
index 00000000..c5d816d1
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-inbound-left.png b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-left.png
new file mode 100644
index 00000000..be263b82
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-inbound-right.png b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-right.png
new file mode 100644
index 00000000..7f6f92f4
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-inbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-outbound-background.png b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-background.png
new file mode 100644
index 00000000..0a936a11
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-outbound-left.png b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-left.png
new file mode 100644
index 00000000..84770e83
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/footer-outbound-right.png b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-right.png
new file mode 100644
index 00000000..3ebe32de
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/footer-outbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-inbound-background.png b/kopete/styles/Clear/Contents/Resources/images/header-inbound-background.png
new file mode 100644
index 00000000..95293218
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-inbound-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-inbound-left.png b/kopete/styles/Clear/Contents/Resources/images/header-inbound-left.png
new file mode 100644
index 00000000..b4dfbaa6
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-inbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-inbound-right.png b/kopete/styles/Clear/Contents/Resources/images/header-inbound-right.png
new file mode 100644
index 00000000..29f847c2
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-inbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-outbound-background.png b/kopete/styles/Clear/Contents/Resources/images/header-outbound-background.png
new file mode 100644
index 00000000..bba88f09
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-outbound-background.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-outbound-left.png b/kopete/styles/Clear/Contents/Resources/images/header-outbound-left.png
new file mode 100644
index 00000000..d08289b0
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-outbound-left.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/header-outbound-right.png b/kopete/styles/Clear/Contents/Resources/images/header-outbound-right.png
new file mode 100644
index 00000000..0353936e
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/header-outbound-right.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-action.png b/kopete/styles/Clear/Contents/Resources/images/icon-action.png
new file mode 100644
index 00000000..ecdc9917
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-action.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-highlighted.png b/kopete/styles/Clear/Contents/Resources/images/icon-highlighted.png
new file mode 100644
index 00000000..474f63fc
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-highlighted.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-internal.png b/kopete/styles/Clear/Contents/Resources/images/icon-internal.png
new file mode 100644
index 00000000..98d4b996
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-internal.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-me.png b/kopete/styles/Clear/Contents/Resources/images/icon-me.png
new file mode 100644
index 00000000..1f9083eb
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-me.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-time.png b/kopete/styles/Clear/Contents/Resources/images/icon-time.png
new file mode 100644
index 00000000..89c063e8
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-time.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/images/icon-you.png b/kopete/styles/Clear/Contents/Resources/images/icon-you.png
new file mode 100644
index 00000000..65ea8b86
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/images/icon-you.png
Binary files differ
diff --git a/kopete/styles/Clear/Contents/Resources/main.css b/kopete/styles/Clear/Contents/Resources/main.css
new file mode 100644
index 00000000..452c6486
--- /dev/null
+++ b/kopete/styles/Clear/Contents/Resources/main.css
@@ -0,0 +1,376 @@
+.Chat
+{
+ letter-spacing: 1px;
+ font-family: arial;
+ font-size: 11px;
+ padding: 5px;
+
+}
+
+
+.KopeteMessage
+{
+ margin-left: 6px;
+ margin-right: 6px;
+ margin-bottom: 10px;
+}
+
+.IncomingMessageHeader
+{
+ background: url(images/header-inbound-background.png) repeat-x;
+ background-color: #c9d9f0;
+
+}
+.IncomingMessageHeader1
+{
+ background: url(images/header-inbound-left.png) no-repeat top left;
+ padding-left: 4px;
+
+}
+.IncomingMessageHeader2
+{
+ background: url(images/header-inbound-right.png) no-repeat top right;
+ padding-right: 4px;
+ padding-left: 4px;
+ vertical-align: middle;
+ line-height: 20px;
+ height: 20px;
+}
+.IncomingBody
+{
+ background: url(images/body-background.png) repeat-x top;
+ background-color: #f5f6fa;
+ border-bottom: 1px solid #c9d9f0;
+}
+.IncomingBody1
+{
+ background: url(images/body-inbound-left.png) no-repeat top left;
+}
+.IncomingBody2
+{
+ background: url(images/body-inbound-right.png) no-repeat top right;
+ padding: 5px;
+ min-height: 55px;
+}
+
+.IncomingMessage
+{
+ overflow: auto;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+.NextIncomingMessage
+{
+ overflow: auto;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+.inUserPicture
+{
+ float: left;
+ width: 60px;
+ height: 52px;
+ margin: -1px;
+ padding-top: 6px;
+ padding-left: 6px;
+ background: url(images/body-inbound-avatar.png) no-repeat top left;
+
+}
+
+.inStatusIcon
+{
+ float: left;
+ padding-right: 1ex;
+}
+
+.inTime
+{
+ background: url(images/icon-time.png) no-repeat center right;
+ position: relative;
+ padding-right: 18px;
+ padding-left: 5px;
+ text-align: right;
+ font-weight: bold;
+ font-size: 10px;
+ float: right;
+ z-index: 1;
+ color: #567199;
+}
+.inMetacontact
+{
+ position: absolute;
+ text-align: left; font-size: 10px; font-weight: bold;
+ padding-right: 20px;
+ padding-left: 20px;
+ overflow: hidden;
+ height: 20px;
+ background: url(images/icon-you.png) no-repeat center left;
+ color: #567199;
+}
+
+.IncomingFooter
+{
+ background: url(images/footer-inbound-background.png) repeat-x;
+ background-color: #ffffff;
+}
+.IncomingFooter1
+{
+ background: url(images/footer-inbound-left.png) no-repeat top left;
+ height: 9px;
+}
+.IncomingFooter2
+{
+ background: url(images/footer-inbound-right.png) no-repeat top right;
+ height: 9px;
+}
+
+.IncomingAction
+{
+ background: #fafafa;
+ margin-left: 6px;
+ margin-right: 6px;
+ padding-left: 3px;
+ padding-right: 3px;
+ margin-bottom: 10px;
+ border: 1px solid #e0e0e0;
+ vertical-align: middle;
+ line-height: 20px;
+ height: 20px;
+}
+
+.IncomingActionBody
+{
+ position: absolute;
+ background: url(images/icon-action.png) no-repeat center left;
+ text-align: left; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 20px;
+ padding-left: 20px;
+ overflow: hidden;
+ height: 20px;
+ float: left;
+}
+
+.inActionMetacontact
+{
+/* margin-left: 5px;
+ font-weight: bold;*/
+}
+.inActionMessage
+{
+
+}
+.IncomingActionTime
+{
+ position: relative;
+ background: url(images/icon-time.png) no-repeat center right;
+ text-align: right; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 18px;
+ padding-left: 5px;
+ float: right;
+ z-index: 1;
+}
+
+.OutgoingMessageHeader
+{
+ background: url(images/header-outbound-background.png) repeat-x;
+ background-color: #e1e1e1;
+
+}
+.OutgoingMessageHeader1
+{
+ background: url(images/header-outbound-left.png) no-repeat top left;
+ padding-left: 4px;
+}
+.OutgoingMessageHeader2
+{
+ background: url(images/header-outbound-right.png) no-repeat top right;
+ padding-right: 4px;
+ padding-left: 4px;
+ vertical-align: middle;
+ line-height: 20px;
+ height: 20px;
+}
+.OutgoingBody
+{
+ background: url(images/body-background.png) repeat-x top;
+ background-color: #f9f9f9;
+ border-bottom: 1px solid #e1e1e1;
+}
+.OutgoingBody1
+{
+ background: url(images/body-outbound-left.png) no-repeat top left;
+}
+.OutgoingBody2
+{
+ background: url(images/body-outbound-right.png) no-repeat top right;
+ padding: 5px;
+ min-height: 55px;
+}
+
+.OutgoingMessage
+{
+ overflow: auto;
+ padding-left: 2px;
+ padding-right: 2px;
+/* font:
+color:
+background-color:*/
+}
+
+.NextOutgoingMessage
+{
+ overflow: auto;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+.outUserPicture
+{
+ float: left;
+ width: 60px;
+ height: 52px;
+ margin: -1px;
+ padding-top: 6px;
+ padding-left: 6px;
+ background: url(images/body-outbound-avatar.png) no-repeat top left;
+}
+
+.outStatusIcon
+{
+ float: left;
+ padding-right: 1ex;
+}
+
+.outTime
+{
+ background: url(images/icon-time.png) no-repeat center right;
+ position: relative;
+ padding-right: 18px;
+ padding-left: 5px;
+ text-align: right;
+ font-weight: bold;
+ font-size: 10px;
+ float: right;
+ z-index: 1;
+ color: #707070;
+}
+.outMetacontact
+{
+ position: absolute;
+ text-align: left; font-size: 10px; font-weight: bold;
+ padding-right: 20px;
+ padding-left: 20px;
+ overflow: hidden;
+ height: 20px;
+ background: url(images/icon-me.png) no-repeat center left;
+ color: #707070;
+}
+
+.OutgoingFooter
+{
+ background: url(images/footer-outbound-background.png) repeat-x;
+ background-color: #ffffff;
+}
+.OutgoingFooter1
+{
+ background: url(images/footer-outbound-right.png) no-repeat top right;
+ height: 9px;
+}
+.OutgoingFooter2
+{
+ background: url(images/footer-outbound-left.png) no-repeat top right;
+ height: 9px;
+}
+
+.OutgoingAction
+{
+ background: #fafafa;
+ margin-left: 6px;
+ margin-right: 6px;
+ padding-left: 3px;
+ padding-right: 3px;
+ margin-bottom: 10px;
+ border: 1px solid #e0e0e0;
+ vertical-align: middle;
+ line-height: 20px;
+ height: 20px;
+}
+
+.OutgoingActionBody
+{
+ position: absolute;
+ background: url(images/icon-action.png) no-repeat center left;
+ text-align: left; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 20px;
+ padding-left: 20px;
+ margin-left: 4px;
+ overflow: hidden;
+ height: 20px;
+ float: left;
+}
+
+.outActionMetacontact
+{
+ /*margin-left: 10px;
+ font-weight: bold;*/
+}
+.outActionMessage
+{
+}
+.OutgoingActionTime
+{
+ position: relative;
+ background: url(images/icon-time.png) no-repeat center right;
+ text-align: right; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 18px;
+ padding-left: 5px;
+ float: right;
+ z-index: 1;
+}
+.InternalMessageHeader
+{
+ background: #fafafa;
+ margin-left: 6px;
+ margin-right: 6px;
+ padding-left: 3px;
+ padding-right: 3px;
+ margin-bottom: 10px;
+ border: 1px solid #e0e0e0;
+ vertical-align: middle;
+ line-height: 20px;
+ height: 20px;
+}
+
+.InternalMessage
+{
+ position: absolute;
+ background: url(images/icon-internal.png) no-repeat center left;
+ text-align: left; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 20px;
+ padding-left: 20px;
+ margin-left: 4px;
+ overflow: hidden;
+ height: 20px;
+ float: left;
+}
+
+.InternalMessageHeaderTime
+{
+ position: relative;
+ background: url(images/icon-time.png) no-repeat center right;
+ text-align: right; font-size: 10px; font-weight: bold; color: #808080;
+ padding-right: 18px;
+ padding-left: 5px;
+ float: right;
+ z-index: 1;
+}
+
+.systemLogo
+{
+ float: left;
+ vertical-align: middle;
+ padding-right: 1ex;
+}
+
diff --git a/kopete/styles/Clear/Makefile.am b/kopete/styles/Clear/Makefile.am
new file mode 100644
index 00000000..331c9b59
--- /dev/null
+++ b/kopete/styles/Clear/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents \ No newline at end of file
diff --git a/kopete/styles/Gaim/CREDITS b/kopete/styles/Gaim/CREDITS
new file mode 100644
index 00000000..4e523780
--- /dev/null
+++ b/kopete/styles/Gaim/CREDITS
@@ -0,0 +1,7 @@
+Original textonly Style for Adium written by Mark Fickett
+http://www.adiumxtras.com/index.php?a=xtras&xtra_id=44
+
+Modified justtext Style for Adium written by Huw Rowlands
+http://www.adiumxtras.com/index.php?a=xtras&xtra_id=990
+
+Modified Gaim Style for Kopete written by Thanos Kyritsis
diff --git a/kopete/styles/Gaim/Contents/Info.plist b/kopete/styles/Gaim/Contents/Info.plist
new file mode 100644
index 00000000..c5fb6825
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Info.plist
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Gaim Kopete chat Style</string>
+ <key>CFBundleIdentifier</key>
+ <string>Kopete.Gaim.style</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>1.0</string>
+ <key>CFBundleName</key>
+ <string>Gaim</string>
+ <key>CFBundlePackageType</key>
+ <string>KopeteChatStyle</string>
+ <key>DefaultBackgroundColor</key>
+ <string>FFFFFF</string>
+ <key>DefaultFontFamily</key>
+ <string>DejaVu Sans Mono</string>
+ <key>DefaultFontSize</key>
+ <integer>12</integer>
+ <key>DisableCustomBackground</key>
+ <false/>
+ <key>DisplayNameForNoVariant</key>
+ <string>grays</string>
+ <key>ShowsUserIcons</key>
+ <false/>
+</dict>
+</plist>
diff --git a/kopete/styles/Gaim/Contents/Makefile.am b/kopete/styles/Gaim/Contents/Makefile.am
new file mode 100644
index 00000000..6940fe81
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Footer.html b/kopete/styles/Gaim/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Footer.html
diff --git a/kopete/styles/Gaim/Contents/Resources/Header.html b/kopete/styles/Gaim/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Header.html
diff --git a/kopete/styles/Gaim/Contents/Resources/Incoming/Action.html b/kopete/styles/Gaim/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..0f2993c6
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;s"><span class="inActionTime">(%time{%H:%M:%S}%) </span>
+ <span class="inActionMetacontact">%sender%:&nbsp;</span></span>
+ <span class="inActionMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Incoming/Content.html b/kopete/styles/Gaim/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..89d07c93
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;"><span class="inContentTime">(%time{%H:%M:%S}%) </span>
+ <span class="inMetacontact">%sender%:&nbsp;</span></span>
+ <span class="IncomingMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Gaim/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..dea28106
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Action.html Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Gaim/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Gaim/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..89d07c93
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;"><span class="inContentTime">(%time{%H:%M:%S}%) </span>
+ <span class="inMetacontact">%sender%:&nbsp;</span></span>
+ <span class="IncomingMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Makefile.am b/kopete/styles/Gaim/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..75c9567d
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = Incoming Outgoing Variants
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Gaim/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Outgoing/Action.html b/kopete/styles/Gaim/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..a7d6ef00
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;"><span class="outActionTime">(%time{%H:%M:%S}%) </span>
+ <span class="outActionMetacontact">%sender%:&nbsp;</span></span>
+ <span class="outActionMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Outgoing/Content.html b/kopete/styles/Gaim/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..9ea2bb6a
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;"><span class="outContentTime">(%time{%H:%M:%S}%) </span>
+ <span class="outMetacontact">%sender%:&nbsp;</span></span>
+ <span class="OutgoingMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Gaim/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..ce2edce7
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Action.html Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Gaim/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Gaim/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Gaim/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..9ea2bb6a
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span style="color:%senderColor%;"><span class="outContentTime">(%time{%H:%M:%S}%) </span>
+ <span class="outMetacontact">%sender%:&nbsp;</span></span>
+ <span class="OutgoingMessage" style="background-color: %textbackgroundcolor{#4386cf}%;">%message%</span>
+</div>
+<div id="insert"></div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Status.html b/kopete/styles/Gaim/Contents/Resources/Status.html
new file mode 100644
index 00000000..4858785a
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Status.html
@@ -0,0 +1,5 @@
+<div class="KopeteMessage" style="direction: %messageDirection%;">
+ <span class="InternalTime">(%time{%H:%M:%S}%) </span>
+ <span class="InternalDelim">#&nbsp;</span>
+ <span class="InternalMessage">%message%</span>
+</div>
diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/Contact-Colors.css b/kopete/styles/Gaim/Contents/Resources/Variants/Contact-Colors.css
new file mode 100644
index 00000000..7a855567
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Variants/Contact-Colors.css
@@ -0,0 +1,10 @@
+
+@import url(../main.css);
+
+.inContentTime, .inMetacontact {
+ color: inherit;
+}
+
+.outContentTime, .outMetacontact {
+ color: inherit;
+} \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/Makefile.am b/kopete/styles/Gaim/Contents/Resources/Variants/Makefile.am
new file mode 100644
index 00000000..bdd48561
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Variants/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = No-Colors.css Name-Colors.css Status-Colors.css Contact-Colors.css
+styledir = $(kde_datadir)/kopete/styles/Gaim/Contents/Resources/Variants
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/Name-Colors.css b/kopete/styles/Gaim/Contents/Resources/Variants/Name-Colors.css
new file mode 100644
index 00000000..f9fce97a
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Variants/Name-Colors.css
@@ -0,0 +1,13 @@
+@import url(../main.css);
+
+.inActionTime, .inActionMetacontact, .inActionMessage {
+ color: #000000;
+}
+
+.outActionTime, .outActionMetacontact, .outActionMessage {
+ color: #000000;
+}
+
+.InternalTime, .InternalDelim, .InternalMessage {
+ color: #000000;
+} \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/No-Colors.css b/kopete/styles/Gaim/Contents/Resources/Variants/No-Colors.css
new file mode 100644
index 00000000..d847a191
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Variants/No-Colors.css
@@ -0,0 +1,22 @@
+@import url(../main.css);
+
+.inContentTime, .inMetacontact {
+ color: #000000;
+}
+
+.outContentTime, .outMetacontact {
+ color: #000000;
+}
+
+.inActionTime, .inActionMetacontact, .inActionMessage {
+ color: #000000;
+}
+
+.outActionTime, .outActionMetacontact, .outActionMessage {
+ color: #000000;
+}
+
+.InternalTime, .InternalDelim, .InternalMessage {
+ color: #000000;
+}
+
diff --git a/kopete/styles/Gaim/Contents/Resources/Variants/Status-Colors.css b/kopete/styles/Gaim/Contents/Resources/Variants/Status-Colors.css
new file mode 100644
index 00000000..373f6d77
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/Variants/Status-Colors.css
@@ -0,0 +1,9 @@
+@import url(../main.css);
+
+.inContentTime, .inMetacontact {
+ color: #000000;
+}
+
+.outContentTime, .outMetacontact {
+ color: #000000;
+} \ No newline at end of file
diff --git a/kopete/styles/Gaim/Contents/Resources/main.css b/kopete/styles/Gaim/Contents/Resources/main.css
new file mode 100644
index 00000000..393d2f10
--- /dev/null
+++ b/kopete/styles/Gaim/Contents/Resources/main.css
@@ -0,0 +1,40 @@
+/* textonly by Mark Fickett, 2004. Poke/edit/maul, but leave credit - think GPL */
+/* Generally: naib.webhop.org Adium-related: naib.webhop.org/~markfickett/adium */
+
+body {
+ background: #ffffff;
+}
+
+div {
+ overflow: auto;
+}
+
+a:link { text-decoration: none; }
+a:visited { text-decoration: none; }
+a:hover { text-decoration: underline; }
+a:active { text-decoration: none; }
+
+.inMetacontact, .outMetacontact, .inActionMetacontact, .outActionMetacontact, .InternalDelim {
+ font-weight: bold;
+}
+
+.inContentTime, .inMetacontact {
+ color: #a82f2f;
+}
+
+.outContentTime, .outMetacontact {
+ color: #16569e;
+}
+
+.inActionTime, .inActionMetacontact, .inActionMessage {
+ color: green;
+}
+
+.outActionTime, .outActionMetacontact, .outActionMessage {
+ color: green;
+}
+
+.InternalTime, .InternalDelim, .InternalMessage {
+ color: #9400d3;
+}
+
diff --git a/kopete/styles/Gaim/Makefile.am b/kopete/styles/Gaim/Makefile.am
new file mode 100644
index 00000000..331c9b59
--- /dev/null
+++ b/kopete/styles/Gaim/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents \ No newline at end of file
diff --git a/kopete/styles/Hacker/COPYRIGHT b/kopete/styles/Hacker/COPYRIGHT
new file mode 100644
index 00000000..114828ca
--- /dev/null
+++ b/kopete/styles/Hacker/COPYRIGHT
@@ -0,0 +1,18 @@
+"Hacker" Kopete chat window Style
+
+Copyright (C) 2005 Jussi Kekkonen (Tm_T)
+(see README for more information)
+
+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.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Info.plist b/kopete/styles/Hacker/Contents/Info.plist
new file mode 100755
index 00000000..c32e1118
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Info.plist
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleGetInfoString</key>
+ <string>Hacker Kopete chat style</string>
+ <key>CFBundleIdentifier</key>
+ <string>Kopete.Hacker.style</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>1.0</string>
+ <key>CFBundleName</key>
+ <string>Hacker</string>
+ <key>CFBundlePackageType</key>
+ <string>KopeteChatStyle</string>
+ <key>MessageViewVersion</key>
+ <integer>1</integer>
+ <key>DefaultFontFamily</key>
+ <string>DejaVu Sans Mono</string>
+ <key>DefaultFontSize</key>
+ <integer>12</integer>
+ <key>DisableCustomBackground</key>
+ <false/>
+ <key>DefaultBackgroundColor</key>
+ <string>000000</string>
+ <key>DisplayNameForNoVariant</key>
+ <string>Dark</string>
+ <key>AllowTextColors:Dark</key>
+ <false/>
+ <key>DefaultBackgroundColor:Dark2</key>
+ <string>000000</string>
+ <key>AllowTextColors:Dark2</key>
+ <false/>
+ <key>DefaultBackgroundColor:Light</key>
+ <string>ffffff</string>
+ <key>DefaultBackgroundColor:Light2</key>
+ <string>ffffff</string>
+
+</dict>
+</plist> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Makefile.am b/kopete/styles/Hacker/Contents/Makefile.am
new file mode 100644
index 00000000..599ca35f
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = Resources
+style_DATA = Info.plist
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Footer.html b/kopete/styles/Hacker/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Footer.html
diff --git a/kopete/styles/Hacker/Contents/Resources/Header.html b/kopete/styles/Hacker/Contents/Resources/Header.html
new file mode 100644
index 00000000..d0fab6b7
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Header.html
@@ -0,0 +1,8 @@
+<div id="Header">
+<div class="headerContainer">
+<div class="imageContainer">
+<img class="buddyIcon" src="%incomingIconPath%" width="96px"/>
+</div>
+<div class="chatName">%chatName%</div>
+</div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/Action.html b/kopete/styles/Hacker/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..10dac865
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="Buddy"> &nbsp;*&nbsp;%sender%&nbsp;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/Content.html b/kopete/styles/Hacker/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..e61ce7b1
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="Buddy"> &lt;&nbsp;%sender%&nbsp;&gt;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/Context.html b/kopete/styles/Hacker/Contents/Resources/Incoming/Context.html
new file mode 100644
index 00000000..46852f73
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/Context.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="pastBuddy"> &lt;&nbsp;%sender%&nbsp;&gt;
+ <span class="pastMessage"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Hacker/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..a913da54
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Content.html Context.html NextContent.html NextContext.html buddy_icon.png Action.html
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Hacker/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..810920c1
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="Buddy"> &nbsp;&nbsp;&nbsp;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/NextContext.html b/kopete/styles/Hacker/Contents/Resources/Incoming/NextContext.html
new file mode 100644
index 00000000..f3553497
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/NextContext.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="pastBuddy"> &nbsp;&nbsp;&nbsp;
+ <span class="pastMessage"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Incoming/buddy_icon.png b/kopete/styles/Hacker/Contents/Resources/Incoming/buddy_icon.png
new file mode 100644
index 00000000..eeeebaad
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Incoming/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Hacker/Contents/Resources/Makefile.am b/kopete/styles/Hacker/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..3c76352e
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = images Incoming Outgoing Variants
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/Action.html b/kopete/styles/Hacker/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..828bffbb
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="User"> &nbsp;*&nbsp;%sender%&nbsp;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/Content.html b/kopete/styles/Hacker/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..e648a325
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="user"> &lt;&nbsp;%sender%&nbsp;&gt;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/Context.html b/kopete/styles/Hacker/Contents/Resources/Outgoing/Context.html
new file mode 100644
index 00000000..c7796a7a
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/Context.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="pastUser"> &lt;&nbsp;%sender%&nbsp;&gt;
+ <span class="pastMessage"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Hacker/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..3019942d
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Content.html NextContent.html Context.html NextContext.html buddy_icon.png Action.html
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..e0eac486
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="user"> &nbsp;&nbsp;&nbsp;
+ <span class="Message"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContext.html b/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContext.html
new file mode 100644
index 00000000..0c6a5df6
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/NextContext.html
@@ -0,0 +1,6 @@
+<div class="container" style="direction: %messageDirection%;">
+ <span class="messageTime">%time{%H:%M:%S}%</span>
+ <div class="pastUser"> &nbsp;&nbsp;&nbsp;
+ <span class="pastMessage"> %message% </span></div>
+</div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Outgoing/buddy_icon.png b/kopete/styles/Hacker/Contents/Resources/Outgoing/buddy_icon.png
new file mode 100644
index 00000000..5a67789d
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Outgoing/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Hacker/Contents/Resources/Status.html b/kopete/styles/Hacker/Contents/Resources/Status.html
new file mode 100644
index 00000000..0efb97fc
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Status.html
@@ -0,0 +1,11 @@
+<div class="statusContainer">
+<table border=0 colspan=0>
+<tr><td>
+<div class="statusTime">--- %time% ---</div>
+</td>
+<td> : </td>
+<td>
+<div class="statusMessage">%message%</div>
+</td></tr>
+</table>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Dark-Noback.css b/kopete/styles/Hacker/Contents/Resources/Variants/Dark-Noback.css
new file mode 100644
index 00000000..87ac55c3
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Dark-Noback.css
@@ -0,0 +1,6 @@
+@import url(../main.css);
+
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background-color: black
+ } \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Dark.css b/kopete/styles/Hacker/Contents/Resources/Variants/Dark.css
new file mode 100644
index 00000000..c81527a8
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Dark.css
@@ -0,0 +1,6 @@
+@import url(../main.css);
+
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background: url("../images/kopete.png"); background-position: center; background-attachment: fixed; background-repeat: no-repeat; background-color: black
+ } \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Dark2-Noback.css b/kopete/styles/Hacker/Contents/Resources/Variants/Dark2-Noback.css
new file mode 100644
index 00000000..d1e8db82
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Dark2-Noback.css
@@ -0,0 +1,9 @@
+@import url(../main.css);
+
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background-color: black
+ }
+
+.imageContainer { margin: 4px 4px 4px 4px; display: block }
+.buddyIcon { width: 48px; float: right; border-style: dotted none; border-width: 2px medium; border-color: #666 white } \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Dark2.css b/kopete/styles/Hacker/Contents/Resources/Variants/Dark2.css
new file mode 100644
index 00000000..8075e9b1
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Dark2.css
@@ -0,0 +1,9 @@
+@import url(../main.css);
+
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background: url("../images/kopete.png"); background-position: center; background-attachment: fixed; background-repeat: no-repeat; background-color: black
+ }
+
+.imageContainer { margin: 4px 4px 4px 4px; display: block }
+.buddyIcon { width: 48px; float: right; border-style: dotted none; border-width: 2px medium; border-color: #666 white }
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Light-Noback.css b/kopete/styles/Hacker/Contents/Resources/Variants/Light-Noback.css
new file mode 100644
index 00000000..fd51af59
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Light-Noback.css
@@ -0,0 +1,40 @@
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background: white; background-color: white
+ }
+
+#Chat { padding-top: 80px; margin: 4px; overflow: hidden }
+
+#Header { background: url("../images/background2.png") repeat-x right top; position: fixed; z-index: 100; top: 0; right: 0; left: 0; height: 80px; border-bottom: 0 none }
+
+.imageContainer { display: none; }
+
+.chatName { color: #3d4f51; font-size: 14px; font-weight: bold; text-align: center; position: relative; margin-left: 2px; margin-right: 5px }
+
+.headerContainer { padding: 1px 1px; position: relative; border-style: dotted none; border-width: 2px medium; border-color: #666 black }
+
+.container { margin-bottom: 2px; overflow: auto; }
+
+a:link { color: #5099ff }
+a:visited { color: #5099ff }
+a:hover { color: #82b6ff }
+
+.messageTime { color: #3d3d3d; font-size: 10px; font-weight: normal; text-align: left; position: relative; z-index: 60; top: 0px; float: left; margin-right: 4px }
+
+.Message { color: black; font-style: normal; font-weight: normal; position: relative; margin-left: 2px; margin-right: 5px }
+
+.pastMessage { color: #a0a3a6; font-style: normal; font-weight: normal }
+
+.statusContainer { margin-bottom: 2px }
+
+.statusTime { color: #2d425f; font-size: 10px; font-weight: bold; top: 0px; text-align: right; margin-right: 5px; position: relative; width: 150px; float: left }
+
+.statusMessage { color: #4d6581; font-size: 12px; font-weight: bold; position: relative; right: 5px }
+
+.buddy { color: #5099ff; font-weight: bold; text-align: left; display: block; position: relative; margin-left: 2px }
+
+.user { color: gray; font-weight: bold; text-align: left; display: block; position: relative; margin-left: 2px }
+
+.pastBuddy { color: #7ab382; font-size: 11px; font-weight: bold; text-align: left; position: relative }
+
+.pastUser { color: #7b95b4; font-size: 11px; font-weight: bold; text-align: left; position: relative }
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Light.css b/kopete/styles/Hacker/Contents/Resources/Variants/Light.css
new file mode 100644
index 00000000..27a564cc
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Light.css
@@ -0,0 +1,6 @@
+@import url(./Light-Noback.css);
+
+body {
+ font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0;
+ background: url("../images/kopete.png"); background-position: center; background-attachment: fixed; background-repeat: no-repeat; background-color: white
+ } \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Light2-Noback.css b/kopete/styles/Hacker/Contents/Resources/Variants/Light2-Noback.css
new file mode 100644
index 00000000..65c6b103
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Light2-Noback.css
@@ -0,0 +1,4 @@
+@import url(./Light-Noback.css);
+
+.imageContainer { margin: 4px 4px 4px 4px; display: block }
+.buddyIcon { width: 48px; float: right; border-style: dotted none; border-width: 2px medium; border-color: #666 white }
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Light2.css b/kopete/styles/Hacker/Contents/Resources/Variants/Light2.css
new file mode 100644
index 00000000..4514634d
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Light2.css
@@ -0,0 +1,4 @@
+@import url(./Light.css);
+
+.imageContainer { margin: 4px 4px 4px 4px; display: block }
+.buddyIcon { width: 48px; float: right; border-style: dotted none; border-width: 2px medium; border-color: #666 white }
diff --git a/kopete/styles/Hacker/Contents/Resources/Variants/Makefile.am b/kopete/styles/Hacker/Contents/Resources/Variants/Makefile.am
new file mode 100644
index 00000000..8019c3ff
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/Variants/Makefile.am
@@ -0,0 +1,5 @@
+style_DATA = Dark2.css Dark.css Light2.css Light.css Dark2-Noback.css Dark-Noback.css Light2-Noback.css Light-Noback.css
+
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents/Resources/Variants
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Hacker/Contents/Resources/images/Makefile.am b/kopete/styles/Hacker/Contents/Resources/images/Makefile.am
new file mode 100644
index 00000000..2ec2fb53
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/images/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = background.png background2.png kopete.png
+styledir = $(kde_datadir)/kopete/styles/Hacker/Contents/Resources/images
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Hacker/Contents/Resources/images/background.png b/kopete/styles/Hacker/Contents/Resources/images/background.png
new file mode 100644
index 00000000..a895f3e7
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/images/background.png
Binary files differ
diff --git a/kopete/styles/Hacker/Contents/Resources/images/background2.png b/kopete/styles/Hacker/Contents/Resources/images/background2.png
new file mode 100644
index 00000000..68cb17b4
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/images/background2.png
Binary files differ
diff --git a/kopete/styles/Hacker/Contents/Resources/images/kopete.png b/kopete/styles/Hacker/Contents/Resources/images/kopete.png
new file mode 100644
index 00000000..689a0966
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/images/kopete.png
Binary files differ
diff --git a/kopete/styles/Hacker/Contents/Resources/main.css b/kopete/styles/Hacker/Contents/Resources/main.css
new file mode 100644
index 00000000..30481372
--- /dev/null
+++ b/kopete/styles/Hacker/Contents/Resources/main.css
@@ -0,0 +1,37 @@
+body { font-size: 12px; font-family: "DejaVu Sans Mono", monospace; font-style: normal; font-weight: normal; margin: 0; color: #5099ff; background: black; background-color: black }
+
+#Chat { padding-top: 80px; margin: 4px; overflow: hidden }
+
+#Header { background: url("images/background.png") repeat-x right top; position: fixed; z-index: 100; top: 0; right: 0; left: 0; height: 80px; border-bottom: 0 none }
+
+.imageContainer { display: none; }
+
+.chatName { color: #cdcfd1; font-size: 14px; font-weight: bold; text-align: center; position: relative; margin-left: 2px; margin-right: 5px }
+
+.headerContainer { padding: 1px 1px; position: relative; border-style: dotted none; border-width: 2px medium; border-color: #666 white }
+
+.container { margin-bottom: 2px; overflow: auto; }
+
+a:link { color: #5099ff }
+a:visited { color: #5099ff }
+a:hover { color: #82b6ff }
+
+.messageTime { color: #dcdcdc; font-size: 10px; font-weight: normal; text-align: left; position: relative; z-index: 60; top: 0px; float: left; margin-right: 4px }
+
+.Message { color: #cdcfd1; font-style: normal; font-weight: normal; position: relative; margin-left: 2px; margin-right: 5px }
+
+.pastMessage { color: #a0a3a6; font-style: normal; font-weight: normal }
+
+.statusContainer { margin-bottom: 2px }
+
+.statusTime { color: #cde2ff; font-size: 10px; font-weight: bold; top: 0px; text-align: right; margin-right: 5px; position: relative; width: 150px; float: left }
+
+.statusMessage { color: #dddfe1; font-size: 12px; font-weight: bold; position: relative; right: 5px }
+
+.buddy { color: #5099ff; font-weight: bold; text-align: left; position: relative; margin-left: 2px }
+
+.user { color: white; font-weight: bold; text-align: left; position: relative; margin-left: 2px }
+
+.pastBuddy { color: #7c91af; font-size: 11px; font-weight: bold; text-align: left; position: relative }
+
+.pastUser { color: gray; font-size: 11px; font-weight: bold; text-align: left; position: relative }
diff --git a/kopete/styles/Hacker/Makefile.am b/kopete/styles/Hacker/Makefile.am
new file mode 100644
index 00000000..d04c105b
--- /dev/null
+++ b/kopete/styles/Hacker/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = Contents
+style_DATA = COPYRIGHT README gpl.txt
+styledir = $(kde_datadir)/kopete/styles/Hacker
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Hacker/README b/kopete/styles/Hacker/README
new file mode 100644
index 00000000..ff3edeb3
--- /dev/null
+++ b/kopete/styles/Hacker/README
@@ -0,0 +1,8 @@
+This is Kopete style using new POWERFUL xhtml+css engine
+made by Tm_T with help of Linux community all over the world
+
+contact me:
+ irc: Tm_T at ircnet & freenode
+ email: tm_travolta at kapsi dot fi
+
+see COPYRIGHT and gpl.txt for copyright
diff --git a/kopete/styles/Hacker/gpl.txt b/kopete/styles/Hacker/gpl.txt
new file mode 100644
index 00000000..3912109b
--- /dev/null
+++ b/kopete/styles/Hacker/gpl.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/kopete/styles/Konqi/Contents/Makefile.am b/kopete/styles/Konqi/Contents/Makefile.am
new file mode 100644
index 00000000..6940fe81
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Footer.html b/kopete/styles/Konqi/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Footer.html
diff --git a/kopete/styles/Konqi/Contents/Resources/Header.html b/kopete/styles/Konqi/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Header.html
diff --git a/kopete/styles/Konqi/Contents/Resources/Incoming/Content.html b/kopete/styles/Konqi/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..7809c9e7
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,10 @@
+<div class="contenu_incoming">
+ <div class="corps_message" >
+ <img class="avatar" height="70" src="%userIconPath%"/>
+ <div class="texte" style="direction: %messageDirection%;">
+ <div class="nom">%sender%</div>
+ (%time{%H:%M:%S}%) : %message%<br>
+ <div id="insert"></div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Konqi/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..37693a82
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Konqi/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Konqi/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..359807b8
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,2 @@
+(%time{%H:%M:%S}%) : %message%
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Incoming/buddy_icon.png b/kopete/styles/Konqi/Contents/Resources/Incoming/buddy_icon.png
new file mode 100644
index 00000000..7438838b
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Incoming/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Makefile.am b/kopete/styles/Konqi/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..6003aa6e
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = Incoming Outgoing Variants
+style_DATA = main.css Footer.html Header.html Status.html puce.png
+styledir = $(kde_datadir)/kopete/styles/Konqi/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Outgoing/Content.html b/kopete/styles/Konqi/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..c4ae95f0
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,10 @@
+<div class="contenu_outgoing">
+ <div class="corps_message">
+ <img class="avatar" height="70" src="%userIconPath%"/>
+ <div class="texte" style="direction: %messageDirection%;">
+ <div class="nom">%sender%</div>
+ (%time{%H:%M:%S}%) : %message%<br>
+ <div id="insert"></div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Konqi/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..235b509f
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Konqi/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Konqi/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Konqi/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..359807b8
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,2 @@
+(%time{%H:%M:%S}%) : %message%
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Outgoing/buddy_icon.png b/kopete/styles/Konqi/Contents/Resources/Outgoing/buddy_icon.png
new file mode 100644
index 00000000..15956a02
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Outgoing/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Status.html b/kopete/styles/Konqi/Contents/Resources/Status.html
new file mode 100644
index 00000000..4ac486b5
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Status.html
@@ -0,0 +1,4 @@
+ <div class="status">
+ <img src="puce.png" valign="middle">%time{%H:%M:%S}% : %message%
+ </div>
+<div id="insert"></div> \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Makefile.am b/kopete/styles/Konqi/Contents/Resources/Variants/Makefile.am
new file mode 100644
index 00000000..74bf56f3
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = konqui
+style_DATA = Side_blue_moon.css Side_blue_without_transparency.css Side_green_without_trans.css Side_blue.css Side_blue_moon_without_transparency.css Side_green.css Side_green_without_transparency.css
+styledir = $(kde_datadir)/kopete/styles/Konqi/Contents/Resources/Variants
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue.css
new file mode 100644
index 00000000..9bda16d0
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue.css
@@ -0,0 +1,39 @@
+body {
+ background: #000000 url(konqui/konqui-blue.png) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre3.png");
+ border: 2px solid #365396;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre3.png");
+ border: 2px solid #365396;
+}
+
+.status
+{
+ background-color: transparent;
+ color: white;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon.css
new file mode 100644
index 00000000..d934f54f
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-moon.jpg) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre5.png");
+ border: 2px solid #2F4883;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre5.png");
+ border: 2px solid #2F4883;
+}
+
+.status
+{
+ background-color: transparent;
+ color: white;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon_without_transparency.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon_without_transparency.css
new file mode 100644
index 00000000..190948a8
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_moon_without_transparency.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-moon.jpg) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre6.png");
+ border: 2px solid #365396;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre6.png");
+ border: 2px solid #365396;
+}
+
+.status
+{
+ background-color: transparent;
+ color: white;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_without_transparency.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_without_transparency.css
new file mode 100644
index 00000000..5cc2f7a7
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_blue_without_transparency.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-blue.png) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre4.png");
+ border: 2px solid #365396;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre4.png");
+ border: 2px solid #365396;
+}
+
+.status
+{
+ background-color: transparent;
+ color: white;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_green.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green.css
new file mode 100644
index 00000000..c46cd3ec
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-green.png) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre2.png");
+ border: 2px solid #194C15;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre2.png");
+ border: 2px solid #194C15;
+}
+
+.status
+{
+ background-color: transparent;
+ color: black;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_trans.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_trans.css
new file mode 100644
index 00000000..380800d0
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_trans.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-green.png) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre1.png");
+ border: 2px solid #194C15;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre1.png");
+ border: 2px solid #194C15;
+}
+
+.status
+{
+ background-color: transparent;
+ color: black;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_transparency.css b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_transparency.css
new file mode 100644
index 00000000..380800d0
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/Side_green_without_transparency.css
@@ -0,0 +1,39 @@
+body {
+ background: #FFFFFF url(konqui/konqui-green.png) fixed;
+ background-repeat : no-repeat;
+ background-size : 100% auto;
+ background-position : bottom right;
+}
+
+.contenu_incoming .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre1.png");
+ border: 2px solid #194C15;
+}
+
+.contenu_outgoing .corps_message
+{
+ background-color: transparente;
+ color: black;
+ background: url("konqui/cadre1.png");
+ border: 2px solid #194C15;
+}
+
+.status
+{
+ background-color: transparent;
+ color: black;
+}
+
+.nom
+{
+ color: #0000FF;
+}
+
+.texte
+{
+ text-shadow: 1px 1px 3px #000000;
+ color: white;
+}
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/Makefile.am b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/Makefile.am
new file mode 100644
index 00000000..5cc70fd1
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = cadre1.png cadre2.png cadre3.png cadre4.png cadre5.png cadre6.png konqui-blue.png konqui-green.png konqui-moon.jpg
+styledir = $(kde_datadir)/kopete/styles/Konqi/Contents/Resources/Variants/konqui
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre1.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre1.png
new file mode 100644
index 00000000..40c3c909
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre1.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre2.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre2.png
new file mode 100644
index 00000000..0446ab6b
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre2.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre3.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre3.png
new file mode 100644
index 00000000..767757cc
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre3.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre4.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre4.png
new file mode 100644
index 00000000..d0fca436
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre4.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre5.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre5.png
new file mode 100644
index 00000000..15bf607e
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre5.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre6.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre6.png
new file mode 100644
index 00000000..bbb4866d
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/cadre6.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-blue.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-blue.png
new file mode 100644
index 00000000..5fd7884d
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-blue.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-green.png b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-green.png
new file mode 100644
index 00000000..6df20ee9
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-green.png
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-moon.jpg b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-moon.jpg
new file mode 100644
index 00000000..40c49d3a
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/Variants/konqui/konqui-moon.jpg
Binary files differ
diff --git a/kopete/styles/Konqi/Contents/Resources/main.css b/kopete/styles/Konqi/Contents/Resources/main.css
new file mode 100644
index 00000000..48fc1bf3
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/main.css
@@ -0,0 +1,50 @@
+body
+{
+ font-family:Verdana, sans-serif;
+ font-size : 12px;
+}
+
+.contenu_incoming .corps_message
+{
+ text-decoration: none;
+ padding-top: 2 px;
+ margin: 5px 15px 5px 15px;
+}
+
+.contenu_outgoing .corps_message
+{
+ text-decoration: none;
+ padding-top: 2 px;
+ margin: 5px 15px 5px 15px;
+}
+
+.status
+{
+ margin-left: 15px;
+ font: 11px Verdana, sans-serif;
+}
+
+.avatar
+{
+ position: relative;
+ top: 0;
+ left: 0;
+ height: 80px;
+}
+
+.nom
+{
+ position:relative;
+ top: -35px;
+ margin-bottom: -30px;
+ z-index:2;
+}
+
+.texte
+{
+ position:relative;
+ top: -35px;
+ margin-left: 100px;
+ margin-bottom: -10px;
+ z-index:1;
+} \ No newline at end of file
diff --git a/kopete/styles/Konqi/Contents/Resources/puce.png b/kopete/styles/Konqi/Contents/Resources/puce.png
new file mode 100644
index 00000000..189cb4ab
--- /dev/null
+++ b/kopete/styles/Konqi/Contents/Resources/puce.png
Binary files differ
diff --git a/kopete/styles/Konqi/Makefile.am b/kopete/styles/Konqi/Makefile.am
new file mode 100644
index 00000000..331c9b59
--- /dev/null
+++ b/kopete/styles/Konqi/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Makefile.am b/kopete/styles/Kopete/Contents/Makefile.am
new file mode 100644
index 00000000..6940fe81
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/Footer.html b/kopete/styles/Kopete/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Footer.html
diff --git a/kopete/styles/Kopete/Contents/Resources/Header.html b/kopete/styles/Kopete/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Header.html
diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/Action.html b/kopete/styles/Kopete/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..ead5823e
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="IncomingAction" style="direction: %messageDirection%;">
+ <!-- Protocol Icon -->
+ <img class="inActionIcon" src="images/action.png" />
+ <!-- MetaContact display -->
+ <span class="inActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="inActionMessage">%message%</span>
+ <!-- Time Display -->
+ <span class="inTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+</div>
+
diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/Content.html b/kopete/styles/Kopete/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..2a525c2e
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,20 @@
+<div class="KopeteMessage">
+ <div style="padding:0;margin:0;border:none;border-color:%senderColor{180}% %senderColor{140}% %senderColor{140}% %senderColor{180}%;background-color:%senderColor{155}%">
+ <div class="IncomingMessageHeader">
+ <!-- Contact photo -->
+ <img class="inUserPicture" src="%userIconPath%" />
+ <!-- Protocol Icon -->
+ <img class="inStatusIcon" src="%senderStatusIcon%" />
+ <!-- Time Display -->
+ <div class="inTime">%time%</div>
+ <!-- MetaContact display -->
+ <div class="inMetacontact">%sender% &#160;</div>
+ </div>
+ </div>
+ <ul class="IncomingList" style="direction: %messageDirection%;">
+ <li class="IncomingMessage" style="direction: %messageDirection%;">%message%</li>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ </ul>
+</div>
+
diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Kopete/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..30dcce80
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html Action.html
+styledir = $(kde_datadir)/kopete/styles/Kopete/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Kopete/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..69185fec
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,3 @@
+<li class="NextIncomingMessage" style="direction: %messageDirection%;">%message%</li>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Kopete/Contents/Resources/Incoming/buddy_icon.png b/kopete/styles/Kopete/Contents/Resources/Incoming/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Incoming/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Kopete/Contents/Resources/Makefile.am b/kopete/styles/Kopete/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..fa2fe450
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = images Incoming Outgoing Variants
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Kopete/Contents/Resources
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/Action.html b/kopete/styles/Kopete/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..ae5c6043
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,16 @@
+<div class="KopeteMessage">
+ <div class="OutgoingAction" style="direction: %messageDirection%;">
+ <!-- Protocol Icon -->
+ <img class="outActionIcon" src="images/action.png" />
+ <!-- MetaContact display -->
+ <span class="outActionMetacontact">%sender% &#160;</span>
+ <!-- Action message -->
+ <span class="outActionMessage">%message%</span>
+ <!-- Time Display -->
+ <span class="outTime">%time%</span>
+
+ </div>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+</div>
+
diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/Content.html b/kopete/styles/Kopete/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..5ace0f94
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,21 @@
+<div class="KopeteMessage">
+ <div style="padding:0;margin:0;border:none;border-color:%senderColor{180}% %senderColor{140}% %senderColor{140}% %senderColor{180}%;background-color:%senderColor{155}%">
+ <div class="OutgoingMessageHeader">
+ <!-- Contact photo -->
+ <img class="outUserPicture" src="%userIconPath%" />
+ <!-- Protocol Icon -->
+ <img class="outStatusIcon" src="%senderStatusIcon%" />
+ <!-- Time Display -->
+ <div class="outTime">%time%</div>
+ <!-- MetaContact display -->
+ <div class="outMetacontact">%sender% &#160;</div>
+ </div>
+ </div>
+ <ul class="OutgoingList" style="direction: %messageDirection%;">
+ <li class="OutgoingMessage" style="direction: %messageDirection%;">%message%</li>
+ <!-- For support of consecutive messages -->
+ <div id="insert" />
+ </ul>
+</div>
+
+
diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Kopete/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..c7cdf416
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = buddy_icon.png Content.html NextContent.html Action.html
+styledir = $(kde_datadir)/kopete/styles/Kopete/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Kopete/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..8e280a95
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,3 @@
+<li class="NextOutgoingMessage" style="direction: %messageDirection%;">%message%</li>
+<!-- For support of consecutive messages -->
+<div id="insert" />
diff --git a/kopete/styles/Kopete/Contents/Resources/Outgoing/buddy_icon.png b/kopete/styles/Kopete/Contents/Resources/Outgoing/buddy_icon.png
new file mode 100644
index 00000000..7e280b74
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Outgoing/buddy_icon.png
Binary files differ
diff --git a/kopete/styles/Kopete/Contents/Resources/Status.html b/kopete/styles/Kopete/Contents/Resources/Status.html
new file mode 100644
index 00000000..763da40a
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Status.html
@@ -0,0 +1,10 @@
+<div class="KopeteMessage">
+ <div class="InternalMessageHeader" style="direction: %messageDirection%;">
+ <img class="systemLogo" src="images/system.png" />
+
+ <span class="InternalMessage">%message%</span>
+ <div class="InternalMessageHeaderTime">%time%</div>
+
+
+ </div>
+</div> \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/Variants/Big_pictures.css b/kopete/styles/Kopete/Contents/Resources/Variants/Big_pictures.css
new file mode 100644
index 00000000..20a8bcaf
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Variants/Big_pictures.css
@@ -0,0 +1,35 @@
+ @import url(../main.css);
+
+ .inUserPicture
+{
+ height: 96px;
+}
+
+.IncomingList, .OutgoingList
+{
+ /*margin-left: 110px;*/
+ margin-left :53px;
+}
+
+.IncomingMessage, .OutgoingMessage
+{
+
+}
+
+.NextIncomingMessage, .NextOutgoingMessage
+{
+
+}
+
+.outUserPicture
+{
+ height: 96px;
+}
+
+.KopeteMessage
+{
+ /*height: 96px;*/
+}
+
+
+
diff --git a/kopete/styles/Kopete/Contents/Resources/Variants/Contact_color.css b/kopete/styles/Kopete/Contents/Resources/Variants/Contact_color.css
new file mode 100644
index 00000000..c6158028
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Variants/Contact_color.css
@@ -0,0 +1,7 @@
+ @import url(../main.css);
+
+.IncomingMessageHeader /*, .OutgoingMessageHeader, .IncomingAction, .OutgoingAction*/
+{
+ background-color:inherit;
+ border-color:inherit;
+} \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/Variants/Makefile.am b/kopete/styles/Kopete/Contents/Resources/Variants/Makefile.am
new file mode 100644
index 00000000..55d71155
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/Variants/Makefile.am
@@ -0,0 +1,5 @@
+style_DATA = Big_pictures.css Contact_color.css
+
+styledir = $(kde_datadir)/kopete/styles/Kopete/Contents/Resources/Variants
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Kopete/Contents/Resources/images/Makefile.am b/kopete/styles/Kopete/Contents/Resources/images/Makefile.am
new file mode 100644
index 00000000..57924e13
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/images/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = action.png important.png system.png
+styledir = $(kde_datadir)/kopete/styles/Kopete/Contents/Resources/images
+
+EXTRA_DIST = $(style_DATA) \ No newline at end of file
diff --git a/kopete/styles/Kopete/Contents/Resources/images/action.png b/kopete/styles/Kopete/Contents/Resources/images/action.png
new file mode 100644
index 00000000..bc7069d8
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/images/action.png
Binary files differ
diff --git a/kopete/styles/Kopete/Contents/Resources/images/important.png b/kopete/styles/Kopete/Contents/Resources/images/important.png
new file mode 100644
index 00000000..474f63fc
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/images/important.png
Binary files differ
diff --git a/kopete/styles/Kopete/Contents/Resources/images/system.png b/kopete/styles/Kopete/Contents/Resources/images/system.png
new file mode 100644
index 00000000..98d4b996
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/images/system.png
Binary files differ
diff --git a/kopete/styles/Kopete/Contents/Resources/main.css b/kopete/styles/Kopete/Contents/Resources/main.css
new file mode 100644
index 00000000..ce325163
--- /dev/null
+++ b/kopete/styles/Kopete/Contents/Resources/main.css
@@ -0,0 +1,211 @@
+.KopeteMessage
+{
+ margin:1.4em .2em 0 .2em;
+ clear: both;
+}
+
+.IncomingMessageHeader
+{
+ background-color:#dfedff;
+ padding:.1em;
+ border:solid;
+ border-color:#fafafa #d1dfef #d1dfef #fafafa;
+ border-width:2px;
+}
+
+.IncomingList, .OutgoingList
+{
+ margin-left: 5px;
+ padding-left: 40px;
+ padding-right: 5px;
+ padding-top: 0.25em;
+ padding-bottom: 0.5em;
+ line-height: 1.2em;
+ list-style: none;
+}
+
+
+.IncomingMessage, .NextIncomingMessage
+{
+ padding-left: 20px;
+ padding-right: 1ex;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ line-height: 1.2em;
+ border-style: hidden; /* fix a strange bug*/
+ border-width: 0 0 1px 0;
+}
+
+
+.NextIncomingMessage
+{
+ margin-left: 17px;
+ padding-left: 20px;
+ border: dotted #d1dfef;
+ border-width: 1px 0 0 0;
+}
+
+.inUserPicture
+{
+ float: left;
+ border: 1px solid #888;
+ height: 48px;
+ margin-top: 0.2em;
+ margin-left: 0.2em;
+ margin-right: 1ex;
+}
+
+.inStatusIcon
+{
+ float: left;
+}
+
+.inTime
+{
+ float: right;
+}
+.inMetacontact
+{
+ margin-left: 60px;
+ font-weight: bold;
+}
+
+.IncomingAction
+{
+ border-top: 2px solid #dae5f0;
+ border-right: 2px solid #aaccf0;
+ border-bottom: 2px solid #aaccf0;
+ border-left: 2px solid #dae5f0;
+ padding: 0.1em;
+ vertical-align: middle;
+ background-color:#c3d9f0;
+}
+.inActionIcon
+{
+ float: left;
+ margin-right: 5px;
+}
+.inActionMetacontact
+{
+ margin-left: 10px;
+ font-weight: bold;
+}
+.inActionMessage
+{
+ margin-left: 1ex;
+}
+
+.OutgoingMessageHeader
+{
+ background-color:#f5f5f5;
+ padding:.1em;
+ border:solid;
+ border-color:#fafafa #e3e3e3 #e3e3e3 #fafafa;
+ border-width:2px;
+}
+
+.OutgoingMessage, .NextOutgoingMessage
+{
+ padding-left: 20px;
+ padding-right: 1ex;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ line-height: 1.2em;
+ border-style: hidden; /* fix a strange bug*/
+ border-width: 0 0 1px 0;
+}
+
+.NextOutgoingMessage
+{
+ margin-left: 17px;
+ padding-left: 20px;
+ border: dotted #d1dfef;
+ border-width: 1px 0 0 0;
+}
+
+.outUserPicture
+{
+ float: left;
+ border: 1px solid #888;
+ height: 48px;
+ margin-top: 0.2em;
+ margin-left: 0.2em;
+ margin-right: 1ex;
+}
+
+.outStatusIcon
+{
+ float: left;
+}
+
+.outTime
+{
+ float: right;
+}
+.outMetacontact
+{
+ margin-left: 70px;
+ font-weight: bold;
+}
+
+.OutgoingAction
+{
+ border-top: 2px solid fafafa #cfcfcf;
+ border-right: 2px solid fafafa #afafaf;
+ border-bottom: 2px solid fafafa #afafaf;
+ border-left: 2px solid fafafa #cfcfcf;
+ padding: 0.1em;
+ vertical-align: middle;
+ background-color:#dedede;
+}
+.outActionIcon
+{
+ float: left;
+ margin-right: 5px;
+}
+.outActionMetacontact
+{
+ margin-left: 10px;
+ font-weight: bold;
+}
+.outActionMessage
+{
+ margin-left: 5px;
+}
+
+.InternalMessageHeader
+{
+ border-top: 0.1em dashed #afafaf;
+ border-right: 0.1em dashed #afafaf;
+ border-bottom: 0.1em dashed #afafaf;
+ border-left: 0.1em dashed #afafaf;
+ padding-left: 0.1em;
+ padding-bottom: 0.1em;
+ padding-right: 0.1em;
+ vertical-align: middle;
+}
+
+.InternalMessage
+{
+ width: 80%;
+ text-align: left;
+ font-size: 10px;
+ font-weight: bold;
+ color: #808080;
+}
+
+.InternalMessageHeaderTime
+{
+ font-size: 10px;
+ float: right;
+ font-weight: normal;
+ margin-right: 1ex;
+}
+
+.systemLogo
+{
+ float: left;
+ margin-right: 1ex;
+ vertical-align: middle;
+}
+
diff --git a/kopete/styles/Kopete/Makefile.am b/kopete/styles/Kopete/Makefile.am
new file mode 100644
index 00000000..331c9b59
--- /dev/null
+++ b/kopete/styles/Kopete/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents \ No newline at end of file
diff --git a/kopete/styles/Makefile.am b/kopete/styles/Makefile.am
new file mode 100644
index 00000000..39e20e7b
--- /dev/null
+++ b/kopete/styles/Makefile.am
@@ -0,0 +1,2 @@
+SUBDIRS = Kopete Hacker Clean Clear Konqi Retropete Gaim
+
diff --git a/kopete/styles/Retropete/Contents/Makefile.am b/kopete/styles/Retropete/Contents/Makefile.am
new file mode 100644
index 00000000..152d23f6
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Resources
diff --git a/kopete/styles/Retropete/Contents/Resources/Footer.html b/kopete/styles/Retropete/Contents/Resources/Footer.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Footer.html
diff --git a/kopete/styles/Retropete/Contents/Resources/Header.html b/kopete/styles/Retropete/Contents/Resources/Header.html
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Header.html
diff --git a/kopete/styles/Retropete/Contents/Resources/Incoming/Action.html b/kopete/styles/Retropete/Contents/Resources/Incoming/Action.html
new file mode 100644
index 00000000..cd89f4fc
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Incoming/Action.html
@@ -0,0 +1,10 @@
+<div class="KopeteMessage">
+ <!-- Time Display -->
+ %time{%H:%M:%S}%:
+ <!-- MetaContact name -->
+ <span class="inMetacontact">%sender% </span>
+ <!-- Message -->
+ <span class="inAction" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </span>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Incoming/Content.html b/kopete/styles/Retropete/Contents/Resources/Incoming/Content.html
new file mode 100644
index 00000000..421e4090
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Incoming/Content.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage">
+ <span class="inIntro">Message from %sender% at %time{%H:%M:%S}%</span>
+ <div class="inMessage" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </div>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Incoming/Makefile.am b/kopete/styles/Retropete/Contents/Resources/Incoming/Makefile.am
new file mode 100644
index 00000000..03fbde8d
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Incoming/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Action.html Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Retropete/Contents/Resources/Incoming
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Retropete/Contents/Resources/Incoming/NextContent.html b/kopete/styles/Retropete/Contents/Resources/Incoming/NextContent.html
new file mode 100644
index 00000000..421e4090
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Incoming/NextContent.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage">
+ <span class="inIntro">Message from %sender% at %time{%H:%M:%S}%</span>
+ <div class="inMessage" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </div>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Makefile.am b/kopete/styles/Retropete/Contents/Resources/Makefile.am
new file mode 100644
index 00000000..8e8e265c
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = Incoming Outgoing
+style_DATA = main.css Footer.html Header.html Status.html
+styledir = $(kde_datadir)/kopete/styles/Retropete/Contents/Resources
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Retropete/Contents/Resources/Outgoing/Action.html b/kopete/styles/Retropete/Contents/Resources/Outgoing/Action.html
new file mode 100644
index 00000000..6ef71e7d
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Outgoing/Action.html
@@ -0,0 +1,10 @@
+<div class="KopeteMessage">
+ <!-- Time Display -->
+ %time{%H:%M:%S}%:
+ <!-- MetaContact name -->
+ <span class="outMetacontact">%sender% </span>
+ <!-- Message -->
+ <span class="outAction" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </span>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Outgoing/Content.html b/kopete/styles/Retropete/Contents/Resources/Outgoing/Content.html
new file mode 100644
index 00000000..64bd4b19
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Outgoing/Content.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage">
+ <span class="outIntro">Message from %sender% at %time{%H:%M:%S}%</span>
+ <div class="outMessage" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </div>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Outgoing/Makefile.am b/kopete/styles/Retropete/Contents/Resources/Outgoing/Makefile.am
new file mode 100644
index 00000000..00956ea9
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Outgoing/Makefile.am
@@ -0,0 +1,4 @@
+style_DATA = Action.html Content.html NextContent.html
+styledir = $(kde_datadir)/kopete/styles/Retropete/Contents/Resources/Outgoing
+
+EXTRA_DIST = $(style_DATA)
diff --git a/kopete/styles/Retropete/Contents/Resources/Outgoing/NextContent.html b/kopete/styles/Retropete/Contents/Resources/Outgoing/NextContent.html
new file mode 100644
index 00000000..64bd4b19
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Outgoing/NextContent.html
@@ -0,0 +1,6 @@
+<div class="KopeteMessage">
+ <span class="outIntro">Message from %sender% at %time{%H:%M:%S}%</span>
+ <div class="outMessage" style="direction: %messageDirection%; background-color: %textbackgroundcolor{#4386CF}%;">
+ %message%
+ </div>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/Status.html b/kopete/styles/Retropete/Contents/Resources/Status.html
new file mode 100644
index 00000000..72d58f3b
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/Status.html
@@ -0,0 +1,3 @@
+<div class="KopeteMessage">
+ <span class="statusMessage" style="direction: %messageDirection%;">*** %time{%H:%M:%S}%: %message% ***</span>
+</div>
diff --git a/kopete/styles/Retropete/Contents/Resources/main.css b/kopete/styles/Retropete/Contents/Resources/main.css
new file mode 100644
index 00000000..e82e959b
--- /dev/null
+++ b/kopete/styles/Retropete/Contents/Resources/main.css
@@ -0,0 +1,55 @@
+.KopeteMessage
+{
+ margin: 5px;
+}
+
+.inIntro
+{
+ font-weight: bold;
+ color: blue;
+}
+
+.inMetacontact
+{
+ font-weight: bold;
+ color: blue;
+}
+
+.inMessage
+{
+ padding-left: 10px;
+}
+
+.inAction
+{
+ font-style: italic;
+ color: blue;
+}
+
+.outIntro
+{
+ font-weight: bold;
+ color: red;
+}
+
+.outMetacontact
+{
+ font-weight: bold;
+ color: red;
+}
+
+.outMessage
+{
+ padding-left: 10px;
+}
+
+.outAction
+{
+ font-style: italic;
+ color: red;
+}
+
+.statusMessage
+{
+ color: #333333;
+}
diff --git a/kopete/styles/Retropete/Makefile.am b/kopete/styles/Retropete/Makefile.am
new file mode 100644
index 00000000..1d015b69
--- /dev/null
+++ b/kopete/styles/Retropete/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = Contents
diff --git a/kpf/AUTHORS b/kpf/AUTHORS
new file mode 100644
index 00000000..7c8ad492
--- /dev/null
+++ b/kpf/AUTHORS
@@ -0,0 +1,2 @@
+Rik Hemsley (rikkus) <rik@kde.org>
+Ryan Cumming (Phalynx) - bugfixes and improvements
diff --git a/kpf/COPYING b/kpf/COPYING
new file mode 100644
index 00000000..0a4b20f1
--- /dev/null
+++ b/kpf/COPYING
@@ -0,0 +1,16 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/kpf/ChangeLog b/kpf/ChangeLog
new file mode 100644
index 00000000..26ef1dfc
--- /dev/null
+++ b/kpf/ChangeLog
@@ -0,0 +1,45 @@
+1.0.1 (post KDE 3.0)
+ * Fixed Content-Range (thanks Harald H.-J. Bongartz).
+ * Better HTML generation (directory listings) (thanks Ryan Cumming)
+ * Killed a bug that crashed kicker at startup when part of the kicker
+ process.
+ * Removed connection limiting, it was basically useless.
+
+1.0 (released with KDE 3.0)
+ * No important changes.
+
+0.5 (initial check in to KDE CVS)
+ * Comprehensive design and class documentation.
+ * Ported to KDE 3 and Qt 3.
+ * Try to avoid running when user has root priviledges.
+ * Security audit.
+
+0.4.3: (unreleased)
+ * GUI fixes.
+ * Ensure user can't create a server with the same port as an existing.
+
+0.4.2: (released as development version)
+ * GUI fixes.
+
+0.4.1: (unreleased)
+ * Limit number of transactions possible on one connection.
+ * Integration with konqueror's 'preferences' dialog.
+
+0.4: (released as stable version)
+ * Ensure user can't create a server with the same name as an existing.
+ * GMT handled properly.
+ * Drag and drop to add new server.
+ * Persistent connections.
+
+0.3: (unreleased)
+ * Multiple servers.
+ * If-(Un)Modified-Since.
+ * Timeout.
+ * It's an applet.
+
+0.2: (unreleased)
+ * Resuming.
+ * Simultaneous connection limit.
+
+0.1: (unreleased)
+ * Initial implementation.
diff --git a/kpf/DESIGN b/kpf/DESIGN
new file mode 100644
index 00000000..7f5d9fa9
--- /dev/null
+++ b/kpf/DESIGN
@@ -0,0 +1,109 @@
+Applet creation:
+
+kicker creates an Applet object via the extern "C" `init' function in
+Applet.cpp
+
+Structure:
+
+The main classes:
+
+ActiveMonitor
+
+ Shows a list of ActiveMonitorItem objects.
+
+ActiveMonitorItem
+
+ Shows details of a request belonging to a Server object.
+
+Applet
+
+ Contains a set of AppletItem.
+
+AppletItem
+
+ Contains a BandwidthGraph and additionally provides a popup menu.
+
+BandwidthGraph
+
+ Shows a graph of the bandwidth usage of a Server object.
+
+Server
+
+ Serves requests on a connection from a remote client.
+ May serve more than one request per connection (this is HTTP persistence.)
+ Creates Request object on incoming request.
+ Creates Response object when incoming request is fully received.
+ Sends Request and Response objects out via signals, so that they
+ may be used by ActiveMonitorItem objects.
+
+WebServer
+
+ Listens for incoming requests.
+ Creates and manages Server objects.
+
+WebServerManager
+
+ Singleton.
+ Creates and manages WebServer objects.
+
+Startup:
+
+1. Applet creates WebServerManager singleton (requests pointer to instance.)
+2. WebServerManager reads config and creates WebServers.
+3. Applet is informed via signal as each WebServer is created, and creates
+ AppletItem for each.
+4. AppletItem creates BandwidthGraph.
+5. BandwidthGraph connects itself to the new WebServer.
+6. Repeated until all saved WebServers have been created.
+7. Return to event loop.
+
+Creation of server by user:
+
+1. Via Konqueror properties dialog plugin, context menu on AppletItem, or
+ context menu on `bare' Applet, user is asked to configure server.
+ From the Konqueror plugin, a simple configuration interface is used.
+ From the context menu on the AppletItem or Applet, a ServerWizard is
+ created.
+2. If a WebServer is created (user accepts) then the Applet is informed
+ and the procedure continues as in Startup, step 4.
+
+Run cycle:
+
+1. Connection received by a WebServer.
+2. WebServer checks connection limit, if OK creates Server.
+3. Server creates Request object from data supplied by client.
+4. Server emits request(), passing Request object.
+5. WebServer emits request(), passing Request object.
+6. ActiveMonitor creates ActiveMonitorItem using Request object data.
+7. Server generates appropriate Response object.
+8. Server emits response(), passing Response object.
+9. WebServer emits response(), passing Response object.
+10. ActiveMonitor passes Response to relevant ActiveMonitorItem, which updates.
+11. Server passes data to client.
+12. Server emits finished().
+13. WebServer emits finished().
+14. ActiveMonitor informs relevant ActiveMonitorItem that Server is finished.
+15. WebServer deletes Server.
+16. (after delay) ActiveMonitorItem is removed by ActiveMonitor.
+
+Bandwidth limiting:
+
+Limiting really applies to outgoing traffic only. If `too much' incoming
+traffic is received by a Server object, it simply drops the connection.
+
+A WebServer object is responsible for bandwidth management.
+
+A Server object may not send data until it is told to by its parent (WebServer)
+object. A Server object sends a readyToWrite(this) signal when it has data to
+send.
+
+A WebServer object will call Server::write(number of bytes) when it wishes
+to allow a Server to send data.
+
+Connection limiting:
+
+A WebServer object is responsible for connection limiting. When there are
+`too many' Server objects, it will simply queue up incoming connections
+until its backlog is too long, at which point it will simply ignore further
+incoming connections.
+
diff --git a/kpf/Makefile.am b/kpf/Makefile.am
new file mode 100644
index 00000000..12930b43
--- /dev/null
+++ b/kpf/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = src
+
+KDE_ICON = kpf
+
+appletdir = $(kde_datadir)/kicker/applets
+applet_DATA = kpfapplet.desktop
+
+servicedir = $(kde_servicesdir)
+service_DATA = kpfpropertiesdialogplugin.desktop
+
+EXTRA_DIST = $(applet_DATA) $(service_DATA)
+
diff --git a/kpf/NEWS b/kpf/NEWS
new file mode 100644
index 00000000..58378c15
--- /dev/null
+++ b/kpf/NEWS
@@ -0,0 +1 @@
+See ChangeLog
diff --git a/kpf/README b/kpf/README
new file mode 100644
index 00000000..c5320111
--- /dev/null
+++ b/kpf/README
@@ -0,0 +1,42 @@
+Public fileserver for KDE
+
+Features:
+
+ * Runs as an applet in kicker.
+ * Uses HTTP protocol (supports 1.0 & 1.1)
+ * Realtime connection monitor and control
+ * Bandwidth capping.
+ * Connection count limiting.
+ * Designed for safety. No CGI, no PUT, no access outside virtual root.
+ * Drag and drop an URL pointing to a local dir to add a share.
+ * Integration with konqueror's ``properties'' dialog.
+ * Persistent connections.
+
+WARNING:
+
+Please note that allowing remote access to local files is inherently dangerous.
+I will not be held responsible for loss, damage, your head falling off, etc. I
+will, however, do all in my power to keep this software secure. If you don't
+trust me (and why should you ?) then simply don't run this software.
+
+Currently there are no known security holes in this software.
+
+Developers:
+
+This code allows remote access to local files. Therefore it must be secure.
+I also try to limit the effect of DoS attacks and to help the user avoid
+making silly mistakes like sharing ~.
+
+Please send patches to me for approval before committing to CVS.
+
+Shortly before a KDE release, during the feature freeze, I will do a full code
+audit. If I am unable to do this for whatever reason, I will ask for someone
+else to do the audit. If the audit cannot be performed and code changes are
+non-trivial, kpf should not be released.
+
+License terms:
+
+MIT license. See COPYING.
+
+Rik Hemsley (rikkus) <rik@kde.org>
+
diff --git a/kpf/THANKS b/kpf/THANKS
new file mode 100644
index 00000000..737f052e
--- /dev/null
+++ b/kpf/THANKS
@@ -0,0 +1,2 @@
+Lauri Watts and Carsten Pfeiffer for suggestions, bug reports.
+Thomas Zander for helping with usability problems.
diff --git a/kpf/cr16-app-kpf.png b/kpf/cr16-app-kpf.png
new file mode 100644
index 00000000..aaea674f
--- /dev/null
+++ b/kpf/cr16-app-kpf.png
Binary files differ
diff --git a/kpf/cr32-app-kpf.png b/kpf/cr32-app-kpf.png
new file mode 100644
index 00000000..c4ba7e7b
--- /dev/null
+++ b/kpf/cr32-app-kpf.png
Binary files differ
diff --git a/kpf/cr48-app-kpf.png b/kpf/cr48-app-kpf.png
new file mode 100644
index 00000000..8e10d8d0
--- /dev/null
+++ b/kpf/cr48-app-kpf.png
Binary files differ
diff --git a/kpf/kpfapplet.desktop b/kpf/kpfapplet.desktop
new file mode 100644
index 00000000..59829b94
--- /dev/null
+++ b/kpf/kpfapplet.desktop
@@ -0,0 +1,121 @@
+[Desktop Entry]
+Name=Public File Server
+Name[af]=Publiek Lêer Bediener
+Name[ar]=خادم ملÙات عام
+Name[az]=Ãœmumi Fayl Vericisi
+Name[be]=Публічны файл-Ñервер
+Name[bg]=Публичен файлов Ñървър
+Name[bn]=পাবলিক ফাইল সারà§à¦­à¦¾à¦°
+Name[bs]=Javni datoteÄni server
+Name[ca]=Servidor públic de fitxers
+Name[cs]=Veřejný souborový server
+Name[cy]=Gweinydd Ffeiliau Cyhoeddus
+Name[da]=Offentlig filserver
+Name[de]=Öffentlicher Dateiserver
+Name[el]=ΕξυπηÏετητής κοινών αÏχείων
+Name[eo]=Publika dosierservilo
+Name[es]=Servidor público de archivos
+Name[et]=Avalik failiserver
+Name[eu]=Fitxategi zerbitzari publikoa
+Name[fa]=پروندۀ عمومی کارساز
+Name[fi]=Julkinen tiedostopalvelin
+Name[fr]=Serveur de fichiers public
+Name[ga]=Freastalaí Comhad Poiblí
+Name[gl]=Servidor público de arquivos
+Name[he]=שרת ×§×‘×¦×™× ×¦×™×‘×•×¨×™
+Name[hi]=पबà¥à¤²à¤¿à¤• फ़ाइल सरà¥à¤µà¤°
+Name[hr]=Javni poslužitelj datoteka
+Name[hu]=KPF fájlkiszolgáló
+Name[is]=Almennur skráarmiðlari
+Name[it]=Server pubblico di file
+Name[ja]=公開ファイルサーãƒ
+Name[ka]=სáƒáƒ¯áƒáƒ áƒ ფáƒáƒ˜áƒšáƒ˜áƒ¡ სერვერი
+Name[kk]=Ðшық файл Ñервері
+Name[km]=ម៉ាស៊ីន​បម្រើ​ឯកសារ​សាធារណៈ
+Name[lt]=Viešas bylų serveris
+Name[lv]=Publisks Failu Serveris
+Name[mk]=Јавен Ñервер за датотеки
+Name[mn]=Файл Ñервер
+Name[ms]=Pelayan Fail Awam
+Name[mt]=Server tal-fajls pubbliċi
+Name[nb]=Offentlig filtjener
+Name[nds]=Apen Dateiserver
+Name[ne]=सारà¥à¤µà¤œà¤¨à¤¿à¤• फाइल सरà¥à¤­à¤°
+Name[nl]=Publiekelijk toegankelijke bestandsserver
+Name[nn]=Offentleg filtenar
+Name[nso]=Seabi sa Faele ya Setshaba
+Name[pa]=ਪਬਲਿਕ ਫਾਇਲ ਸਰਵਰ
+Name[pl]=Publiczny serwer plików
+Name[pt]=Servidor Público de Ficheiros
+Name[pt_BR]=Servidor de Arquivos Público
+Name[ro]=Server public de fiÅŸiere
+Name[ru]=Файловый Ñервер
+Name[se]=Almmulaš fiilabálvá
+Name[sk]=Verejný súborový server
+Name[sl]=Javni datoteÄni strežnik
+Name[sr]=Јавни Ñервер фајлова
+Name[sr@Latn]=Javni server fajlova
+Name[sv]=Publik filserver
+Name[ta]=பொத௠கோபà¯à®ªà¯ சேவையகமà¯
+Name[tg]=ХидматраÑони Файлҳои Оммавӣ
+Name[th]=เซิร์ฟเวอร์เà¸à¹‡à¸šà¹à¸Ÿà¹‰à¸¡à¸ªà¸²à¸˜à¸²à¸£à¸“ะ
+Name[tr]=Genel Dosya Sunucu
+Name[uk]=Відкритий Ñервер файлів
+Name[uz]=Ommaviy fayl serveri
+Name[uz@cyrillic]=Оммавий файл Ñервери
+Name[ven]=Siva ya faela ya tshitshavha
+Name[wa]=Sierveu di fitchîs publiks
+Name[xh]=Umncedisi Wefayile Kawonke-wonke
+Name[zh_CN]=公共文件æœåŠ¡å™¨
+Name[zh_HK]=公共檔案伺æœå™¨
+Name[zh_TW]=公共檔案伺æœå™¨
+Name[zu]=Ifayela Lomlekeleli Wovelele
+Comment=A small webserver that makes sharing files over the network easy
+Comment[be]=Маленькі Ñеціўны Ñервер Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñтай публікацыі файлаў у Ñетцы
+Comment[bg]=Малък уеб Ñървър за леÑно ÑподелÑне на файлове
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ছোট ওয়েব সারà§à¦­à¦¾à¦° যে নেটওয়ারà§à¦•à§‡à¦° ওপর ফাইল ভাগাভাগি সহজ করে
+Comment[bs]=Mali web server koji olakšava razmjenu datoteka putem mreže
+Comment[ca]=Un servidor web petit que facilita la compartició de fitxers per la xarxa
+Comment[cs]=Malý webový server usnadňující sdílení souborů
+Comment[da]=En lille web-server som gør det nemt at dele filer via netværket
+Comment[de]=Ein kleiner Webserver zum einfachen Freigeben von Dateien über das Internet
+Comment[el]=Ένας μικÏός εξυπηÏετητής που κάνει την κοινή χÏήση αÏχείων μέσω δικτÏου εÏκολη
+Comment[es]=Un pequeño servidor web que facilita compartir archivos en red
+Comment[et]=Väike veebiserver, mis võimaldab failide lihtsat jagamist võrgus
+Comment[eu]=Sarean zehar fitxtegiak partekatzea errazten duen web-zerbitzari txiki bat
+Comment[fa]=یک کارساز وب کوچک که به اشتراک گذاشتن پرونده‌ها را در شبکه آسان می‌کند
+Comment[fr]=Un petit serveur web permettant de partager facilement des fichiers sur le réseau
+Comment[gl]=Un pequeno servidor web que fai doada a compartición de ficheiros por rede
+Comment[he]=שרת ×ינטרנט קטן ×©×’×•×¨× ×œ×ª×¢×‘×•×¨×ª ×§×‘×¦×™× ×‘×¨×©×ª להיות קלה
+Comment[hu]=Mini webkiszolgáló egyszerű fájlmegosztáshoz
+Comment[is]=Lítill vefþjónn sem auðveldar deilingu af skrám yfir netið
+Comment[it]=Un piccolo server web che permette di condividere dei file attraverso la rete in modo semplice
+Comment[ja]=ç°¡å˜ã«ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ä¸Šã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’共有ã™ã‚‹ãŸã‚ã®å°ã•ãªã‚¦ã‚§ãƒ–サーãƒ
+Comment[ka]=პáƒáƒ¢áƒáƒ áƒ ვებსერვერი, რáƒáƒ›áƒ”ლიც ფáƒáƒ˜áƒšáƒ—რგáƒáƒ–იáƒáƒ áƒ”ბáƒáƒ¡ ქსელში áƒáƒáƒ“ილებს
+Comment[kk]=Файларды желіде ортақтаÑтыруды жеңілдететін шағын веб Ñервер
+Comment[km]=ម៉ាស៊ីន​បម្រើ​វ៉áŸáž”​ážáž¼áž…​មួយ ដែល​អាច​ធ្វើ​ឲ្យ​ការ​ចែក​រំលែក​ឯកសារ​លើ​បណ្ដាញ​មាន​លក្ážážŽáŸˆâ€‹áž€áž¶áž“់​ážáŸ‚​ងាយស្រួល
+Comment[lt]=Mažas žiniatinklio serveris, labai supaprastinantis dalinimąsi bylomis tinkle
+Comment[nb]=En liten vevtjener som gjør det lett å dele filer over nettet
+Comment[nds]=En lütte Nettserver för't eenfache Freegeven vun Dateien in't Internet
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ सानो वेबसरà¥à¤­à¤° जसले सजिलो सञà¥à¤œà¤¾à¤² माथि साà¤à¥‡à¤¦à¤¾à¤°à¥€ फाइल बनाउà¤à¤¦à¤›
+Comment[nl]=Een kleine webserver die het delen van bestanden over het netwerk vereenvoudigt
+Comment[nn]=Ein liten vevtenar som gjer det lett å dela filer i eit nettverk
+Comment[pl]=Mały serwer WWW do łatwego współdzielenia plików przez sieć
+Comment[pt]=Um pequeno servidor Web que torna simples a partilha de ficheiros na rede
+Comment[pt_BR]=Um pequeno servidor web que facilita o compartilhamento de arquivos na rede
+Comment[ru]=Ðебольшой веб-Ñервер, облегчающий общий доÑтуп к файлам по Ñети
+Comment[sk]=Malý web server, ktorý robí zdieľanie súborov v sieti jednoduchým
+Comment[sl]=Majhen spletni strežnik, ki poenostavi deljenje datotek prek omrežja
+Comment[sr]=Мали веб Ñервер који дељење фајлова преко мреже чини лаким
+Comment[sr@Latn]=Mali veb server koji deljenje fajlova preko mreže Äini lakim
+Comment[sv]=En liten webbserver som gör det enkelt att dela filer via nätverket
+Comment[tr]=Ağ üzerinden dosya paylaştırmayı kolaylaştırıcı ufak web sunucusu
+Comment[uk]=Малий веб-Ñервер, Ñкий Ñпрощує Ñпільний доÑтуп до файлів через мережу
+Comment[zh_CN]=一个å°çš„ Web æœåŠ¡å™¨ï¼Œå¯ä½¿å¾—在在网上共享文件更加容易
+Comment[zh_HK]=令在網絡上分享檔案變得容易的å°åž‹ç¶²é ä¼ºæœå™¨
+Comment[zh_TW]=能輕易在網路上分享檔案的å°åž‹ç¶²é ä¼ºæœå™¨
+Icon=kpf
+X-KDE-Library=kpf_panelapplet
+X-KDE-UniqueApplet=true
+DocPath=kpf/index.html
+X-KDE-Parent-App=Kicker
diff --git a/kpf/kpfpropertiesdialogplugin.desktop b/kpf/kpfpropertiesdialogplugin.desktop
new file mode 100644
index 00000000..f2b0f886
--- /dev/null
+++ b/kpf/kpfpropertiesdialogplugin.desktop
@@ -0,0 +1,56 @@
+[Desktop Entry]
+Type=Service
+Name=KPF Directory Properties Page
+Name[bg]=ÐаÑтройване на Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ñ Ð·Ð° KPF
+Name[bn]=কে-পিে-à¦à¦« ডিরেকà§à¦Ÿà¦°à§€ বৈশিষà§à¦Ÿà§à¦¯à¦¾à¦¬à¦²à§€ পাতা
+Name[bs]=Stranica sa osobinama KPF direktorija
+Name[ca]=Pàgina de propietats del directori KPF
+Name[cs]=Stránka vlastností adresáře KPF
+Name[da]=KPF-mappes egenskabsside
+Name[de]=KPF Ordner-Eigenschaftenseite
+Name[el]=Σελίδα ιδιοτήτων καταλόγου του KPF
+Name[eo]=KPF-dosierujo-eco-paÄo
+Name[es]=Página de propiedades de directorio KPF
+Name[et]=KPF kataloogi omaduste lehekülg
+Name[eu]=KPF direktorio propietaten orria
+Name[fa]=صÙØ­Û€ ویژگیهای Ùهرست راهنمای KPF
+Name[fi]=KPF-hakemiston ominaisuussivu
+Name[fr]=Page des propriétés d'un dossier KPF
+Name[gl]=Páxina coas propiedades do directorio KPF
+Name[he]=דף מ×פייני ספריה של KPF
+Name[hu]=KPF könyvtárjellemzők-adatlap
+Name[is]=KPF möppustillingasíða
+Name[it]=Pagina proprietà directory KPF
+Name[ja]=KPF ディレクトリプロパティã®ãƒšãƒ¼ã‚¸
+Name[ka]=KPF დირექტáƒáƒ áƒ˜áƒ˜áƒ¡ თვისებების გვერდი
+Name[kk]=KPF каталогының қаÑиеттер беті
+Name[km]=ទំពáŸážšâ€‹áž›áž€áŸ’ážážŽáŸˆâ€‹ážŸáž˜áŸ’áž”ážáŸ’ážáž·â€‹ážáž KPF
+Name[lt]=KPF aplanko savybių puslapis
+Name[mk]=Страна за ÑвојÑтва на именик од KPF
+Name[nb]=KPF-side med katalogegenskaper
+Name[nds]=KPF-Orner-Egenschappensiet
+Name[ne]=KRF डाइरेकà¥à¤Ÿà¤°à¥€ विशेषता पृषà¥à¤ 
+Name[nl]=KPF mapeigenschappenpagina
+Name[nn]=KPF-side med katalogeigenskapar
+Name[pa]=KPF ਡਾਇਰੈਕਟਰੀ ਵਿਸ਼ੇਸ਼ਤਾ ਸਫ਼ਾ
+Name[pl]=Strona właściwości katalogu KPF
+Name[pt]=Página de Propriedades da Directoria do KPF
+Name[pt_BR]=Página de propriedades do diretório KPF
+Name[ro]=Pagină de proprietăţi director KPF
+Name[ru]=Страница ÑвойÑтв каталога KPF
+Name[se]=KPF:a ohcoiešvuođat
+Name[sk]=Stránka vlastností prieÄinku KPF
+Name[sl]=Stran z lastnostmi imenika KPF
+Name[sr]=Страна Ñа ÑвојÑтвима KPF директоријума
+Name[sr@Latn]=Strana sa svojstvima KPF direktorijuma
+Name[sv]=KPF-sida med katalogegenskaper
+Name[ta]=KPF directory properties Page
+Name[tg]=Саҳифаи ХуÑуÑиÑтҳои ФеҳриÑти KPF
+Name[tr]=KPF Dizin Özellikleri Sayfası
+Name[uk]=Сторінка влаÑтивоÑтей каталогу KPF
+Name[zh_CN]=KPF 目录属性页
+Name[zh_HK]=KPF 目錄屬性é 
+Name[zh_TW]=KPF 目錄屬性é 
+X-KDE-Library=kpfpropertiesdialog
+X-KDE-Protocol=file
+ServiceTypes=KPropsDlg/Plugin,inode/directory
diff --git a/kpf/src/ActiveMonitor.cpp b/kpf/src/ActiveMonitor.cpp
new file mode 100644
index 00000000..8bd0c34a
--- /dev/null
+++ b/kpf/src/ActiveMonitor.cpp
@@ -0,0 +1,226 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include "Defines.h"
+#include "ActiveMonitorItem.h"
+#include "ActiveMonitor.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ActiveMonitor::ActiveMonitor
+ (
+ WebServer * server,
+ QWidget * parent,
+ const char * name
+ )
+ : QWidget (parent, name),
+ server_ (server)
+ {
+ view_ = new QListView(this);
+
+ view_->setAllColumnsShowFocus(true);
+ view_->setSelectionMode(QListView::Extended);
+
+ view_->addColumn(i18n("Status"));
+ view_->addColumn(i18n("Progress"));
+ view_->addColumn(i18n("File Size"));
+ view_->addColumn(i18n("Bytes Sent"));
+ view_->addColumn(i18n("Response"));
+ view_->addColumn(i18n("Resource"));
+ view_->addColumn(i18n("Host"));
+
+ QVBoxLayout * layout = new QVBoxLayout(this);
+
+ layout->addWidget(view_);
+
+ connect
+ (
+ view_,
+ SIGNAL(selectionChanged()),
+ SLOT(slotSelectionChanged())
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(connection(Server *)),
+ SLOT(slotConnection(Server *))
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(output(Server *, ulong)),
+ SLOT(slotOutput(Server *, ulong))
+ );
+
+ connect(server_, SIGNAL(finished(Server *)), SLOT(slotFinished(Server *)));
+ connect(server_, SIGNAL(request(Server *)), SLOT(slotRequest(Server *)));
+ connect(server_, SIGNAL(response(Server *)), SLOT(slotResponse(Server *)));
+
+ connect(&cullTimer_, SIGNAL(timeout()), SLOT(slotCull()));
+
+ cullTimer_.start(1000);
+
+ // Tell whoever cares about our selection status.
+ slotSelectionChanged();
+ }
+
+ ActiveMonitor::~ActiveMonitor()
+ {
+ }
+
+ void
+ ActiveMonitor::slotConnection(Server * s)
+ {
+ ActiveMonitorItem * i = new ActiveMonitorItem(s, view_);
+ itemMap_[s] = i;
+ }
+
+ void
+ ActiveMonitor::slotOutput(Server * s, ulong l)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->output(l);
+ }
+
+ void
+ ActiveMonitor::slotFinished(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->finished();
+
+ itemMap_.remove(s);
+ }
+
+ void
+ ActiveMonitor::slotRequest(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->request();
+ }
+
+ void
+ ActiveMonitor::slotResponse(Server * s)
+ {
+ ActiveMonitorItem * i = itemMap_[s];
+
+ if (0 != i)
+ i->response();
+ }
+
+ void
+ ActiveMonitor::slotCull()
+ {
+ QDateTime dt = QDateTime::currentDateTime();
+
+ QListViewItemIterator it(view_);
+
+ for (; it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if ((0 == i->server()) && (i->death().secsTo(dt) > 60))
+ {
+ delete i;
+ ++it;
+ }
+ }
+ }
+
+ void
+ ActiveMonitor::slotSelectionChanged()
+ {
+ for (QListViewItemIterator it(view_); it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if
+ (
+ view_->isSelected(i)
+ &&
+ (0 != i->server())
+ &&
+ (Server::Finished != i->server()->state())
+ )
+ {
+ emit(selection(true));
+ return;
+ }
+ }
+
+ emit(selection(false));
+ }
+
+ void
+ ActiveMonitor::slotKillSelected()
+ {
+ for (QListViewItemIterator it(view_); it.current(); ++it)
+ {
+ ActiveMonitorItem * i = static_cast<ActiveMonitorItem *>(it.current());
+
+ if
+ (
+ view_->isSelected(i)
+ &&
+ (0 != i->server())
+ &&
+ (Server::Finished != i->server()->state())
+ )
+ {
+ i->server()->cancel();
+ }
+ }
+ }
+
+ WebServer *
+ ActiveMonitor::server()
+ {
+ return server_;
+ }
+
+ void
+ ActiveMonitor::closeEvent(QCloseEvent * e)
+ {
+ QWidget::closeEvent(e);
+ emit(dying(this));
+ }
+
+} // End namespace KPF
+
+#include "ActiveMonitor.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitor.h b/kpf/src/ActiveMonitor.h
new file mode 100644
index 00000000..f0f62265
--- /dev/null
+++ b/kpf/src/ActiveMonitor.h
@@ -0,0 +1,165 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_H
+#define KPF_ACTIVE_MONITOR_H
+
+#include <qmap.h>
+#include <qtimer.h>
+#include <qwidget.h>
+
+class QListView;
+class QPainter;
+class QPushButton;
+
+namespace KPF
+{
+ class WebServer;
+ class Server;
+ class ActiveMonitorItem;
+
+ /**
+ * Shows a list of ActiveMonitorItem objects.
+ *
+ * Proxies signals from Server objects to ActiveMonitorItem objects.
+ * This is done to avoid making ActiveMonitorItem inherit QObject.
+ */
+ class ActiveMonitor : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server WebServer which we should connect to in order to
+ * receive signals.
+ */
+ ActiveMonitor
+ (
+ WebServer * server,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~ActiveMonitor();
+
+ /**
+ * @return WebServer object we were given at construction.
+ */
+ WebServer * server();
+
+ public slots:
+
+ /**
+ * Look for selected ActiveMonitorItem objects and kill their
+ * connections immediately.
+ */
+ void slotKillSelected();
+
+ protected slots:
+
+ /**
+ * Called when a Server object has been created to handle an incoming
+ * connection.
+ * Creates an ActiveMonitorItem and associates it with the server.
+ * @param server The Server which was created.
+ */
+ void slotConnection(Server * server);
+
+ /**
+ * Called when a Server object has sent data to the remote client.
+ * Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ * @param bytes Number of bytes sent by the server object.
+ */
+ void slotOutput(Server * server, ulong bytes);
+
+ /**
+ * Called when a Server object has finished all transactions with its
+ * remote client and is about to die. Marks the associated
+ * ActiveMonitorItem as defunct, for later culling via slotCull.
+ * @param server The Server which is handling the connection.
+ */
+ void slotFinished(Server *);
+
+ /**
+ * Called when a Server object has received a response from its remote
+ * client. Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ */
+ void slotRequest(Server *);
+
+ /**
+ * Called when a Server object has sent a response to its remote
+ * client. Updates the associated ActiveMonitorItem.
+ * @param server The Server which is handling the connection.
+ */
+ void slotResponse(Server *);
+
+ /**
+ * Called periodically to remove ActiveMonitorItem objects which are no
+ * longer associated with a Server object.
+ */
+ void slotCull();
+
+ /**
+ * Connected to the relevant signal of the contained QListView and used
+ * to update the enabled/disabled state of the button which allows
+ * killing connections.
+ */
+ void slotSelectionChanged();
+
+ protected:
+
+ /**
+ * Overridden to emit a signal when this window is closed.
+ */
+ virtual void closeEvent(QCloseEvent *);
+
+ signals:
+
+ /**
+ * Emitted when this window is closed.
+ */
+ void dying(ActiveMonitor *);
+
+ /**
+ * Emitted when the selection of the contained QListView has changed.
+ * @param selectionExists true if there is a selection.
+ */
+ void selection(bool selectionExists);
+
+ private:
+
+ QListView * view_;
+ WebServer * server_;
+
+ QMap<Server *, ActiveMonitorItem *> itemMap_;
+
+ QTimer cullTimer_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorItem.cpp b/kpf/src/ActiveMonitorItem.cpp
new file mode 100644
index 00000000..1cc8fdcb
--- /dev/null
+++ b/kpf/src/ActiveMonitorItem.cpp
@@ -0,0 +1,199 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qpainter.h>
+#include <kiconloader.h>
+
+#include "Defines.h"
+#include "ActiveMonitorItem.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ActiveMonitorItem::ActiveMonitorItem(Server * server, QListView * parent)
+ : QListViewItem (parent),
+ server_ (server),
+ size_ (0),
+ sent_ (0)
+ {
+ setText(Host, server_->peerAddress().toString());
+ setText(Resource, "...");
+ setText(Response, "...");
+ setText(Size, "...");
+ setText(Sent, "...");
+
+ updateState();
+ }
+
+ ActiveMonitorItem::~ActiveMonitorItem()
+ {
+ // Empty.
+ }
+
+ void
+ ActiveMonitorItem::paintCell
+ (
+ QPainter * p,
+ const QColorGroup & g,
+ int c,
+ int w,
+ int a
+ )
+ {
+ if (c != Progress)
+ {
+ QListViewItem::paintCell(p, g, c, w, a);
+ return;
+ }
+
+ p->setPen(g.dark());
+
+ p->setPen(g.base());
+
+ p->drawRect(0, 0, w, height());
+
+ int maxBarLength = w - 4;
+
+ int barLength = maxBarLength;
+
+ if (0 != size_)
+ barLength = int((sent_ / double(size_)) * maxBarLength);
+
+ p->fillRect(2, 2, barLength, height() - 4, g.highlight());
+ }
+
+ int
+ ActiveMonitorItem::width
+ (
+ const QFontMetrics & fm,
+ const QListView * lv,
+ int c
+ ) const
+ {
+ switch (c)
+ {
+ case Status:
+ return 16;
+ break;
+
+ case Progress:
+ return 32;
+ break;
+
+ default:
+ return QListViewItem::width(fm, lv, c);
+ break;
+ }
+ }
+
+ void
+ ActiveMonitorItem::updateState()
+ {
+ if (0 != server_)
+ {
+ switch (server_->state())
+ {
+ case Server::WaitingForRequest:
+ setPixmap(Status, SmallIcon("connect_creating"));
+ break;
+
+ case Server::WaitingForHeaders:
+ setPixmap(Status, SmallIcon("connect_creating"));
+ break;
+
+ case Server::Responding:
+ setPixmap(Status, SmallIcon("connect_established"));
+ break;
+
+ case Server::Finished:
+ setPixmap(Status, SmallIcon("connect_no"));
+ break;
+ }
+ }
+ }
+
+ Server *
+ ActiveMonitorItem::server()
+ {
+ return server_;
+ }
+
+ QDateTime
+ ActiveMonitorItem::death() const
+ {
+ return death_;
+ }
+
+ void
+ ActiveMonitorItem::request()
+ {
+ if (0 != server_)
+ {
+ setText(Resource, server_->request().path());
+ updateState();
+ }
+ }
+
+ void
+ ActiveMonitorItem::response()
+ {
+ if (0 != server_)
+ {
+ setText(Response, translatedResponseName(server_->response().code()));
+
+ size_ = server_->response().size();
+
+ setText(Size, QString::number(size_));
+
+ updateState();
+ }
+ }
+
+ void
+ ActiveMonitorItem::output(ulong l)
+ {
+ if (0 != server_)
+ {
+ sent_ += l;
+ setText(Sent, QString::number(sent_));
+ updateState();
+ repaint();
+ }
+ }
+
+ void
+ ActiveMonitorItem::finished()
+ {
+ if (0 != server_)
+ {
+ death_ = server_->death();
+ updateState();
+ }
+
+ server_ = 0L;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorItem.h b/kpf/src/ActiveMonitorItem.h
new file mode 100644
index 00000000..99cc13db
--- /dev/null
+++ b/kpf/src/ActiveMonitorItem.h
@@ -0,0 +1,144 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_ITEM_H
+#define KPF_ACTIVE_MONITOR_ITEM_H
+
+#include <qlistview.h>
+#include <qdatetime.h>
+#include <qfontmetrics.h>
+#include <qpalette.h>
+
+class QPainter;
+
+namespace KPF
+{
+ class Server;
+
+ /**
+ * Used to display the status of a Server object.
+ * Created and managed by an ActiveMonitor object.
+ * Provides some textual information, including the requested filename
+ * and the response code, plus a simple graph displaying the data transfer
+ * progress of any response.
+ */
+ class ActiveMonitorItem : public QListViewItem
+ {
+ public:
+
+ enum Column
+ {
+ Status,
+ Progress,
+ Size,
+ Sent,
+ Response,
+ Resource,
+ Host
+ };
+
+ /**
+ * @param server the associated Server object.
+ */
+ ActiveMonitorItem(Server * server, QListView * parent);
+ virtual ~ActiveMonitorItem();
+
+ /**
+ * @return the Server object passed on construction.
+ */
+ Server * server();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has received a request from its remote client. Queries
+ * the Server object for the Request object.
+ *
+ * May be called more than once, if a Server object handles more than
+ * one request (i.e. is working in `persistent' mode.)
+ */
+ void request();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has sent a response to its remote client. Queries the
+ * Server object for the Response object.
+ *
+ * May be called more than once, if a Server object handles more than
+ * one request (i.e. is working in `persistent' mode.)
+ */
+ void response();
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has sent data to its remote client.
+ *
+ * This is called every time output is sent, with the total output
+ * since the request began.
+ */
+ void output(ulong);
+
+ /**
+ * Called by the controlling ActiveMonitor object when the associated
+ * Server object has completed all transactions with its remote client.
+ */
+ void finished();
+
+ /**
+ * @return the time of death (end of transactions with remote client)
+ * of the associated Server object.
+ */
+ QDateTime death() const;
+
+ protected:
+
+ /**
+ * Updates the display to reflect the current state of the connection
+ * held by the associated Server object.
+ */
+ virtual void updateState();
+
+ /**
+ * Overridden to provide for drawing a graph in the cell which displays
+ * the number of bytes sent to the remote client by the associated
+ * Server object.
+ */
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+
+ /**
+ * Overridden to provide for giving reasonable sizes for columns which
+ * do not contain text.
+ */
+ virtual int width(const QFontMetrics &, const QListView *, int) const;
+
+ private:
+
+ Server * server_;
+ QDateTime death_;
+ ulong size_;
+ ulong sent_;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_ACTIVE_MONITOR_ITEM_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorWindow.cpp b/kpf/src/ActiveMonitorWindow.cpp
new file mode 100644
index 00000000..ae44e4be
--- /dev/null
+++ b/kpf/src/ActiveMonitorWindow.cpp
@@ -0,0 +1,90 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <kaction.h>
+#include <klocale.h>
+
+#include "ActiveMonitor.h"
+#include "ActiveMonitorWindow.h"
+#include "ActiveMonitorWindow.moc"
+#include "WebServer.h"
+
+namespace KPF
+{
+ ActiveMonitorWindow::ActiveMonitorWindow
+ (
+ WebServer * server,
+ QWidget * parent,
+ const char * name
+ )
+ : KMainWindow(parent, name)
+ {
+ setCaption(i18n("Monitoring %1 - kpf").arg(server->root()));
+
+ monitor_ = new ActiveMonitor(server, this, "ActiveMonitor");
+
+ setCentralWidget(monitor_);
+
+ killAction_ =
+ new KAction
+ (
+ i18n("&Cancel Selected Transfers"),
+ "stop",
+ 0,
+ monitor_,
+ SLOT(slotKillSelected()),
+ actionCollection(),
+ "kill"
+ );
+
+ killAction_->setEnabled(false);
+
+ killAction_->plug(toolBar());
+ }
+
+ ActiveMonitorWindow::~ActiveMonitorWindow()
+ {
+ // Empty.
+ }
+
+ WebServer *
+ ActiveMonitorWindow::server()
+ {
+ return monitor_->server();
+ }
+
+ void
+ ActiveMonitorWindow::slotMayKill(bool b)
+ {
+ killAction_->setEnabled(b);
+ }
+
+ void
+ ActiveMonitorWindow::closeEvent(QCloseEvent *)
+ {
+ emit(dying(this));
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ActiveMonitorWindow.h b/kpf/src/ActiveMonitorWindow.h
new file mode 100644
index 00000000..eddf1113
--- /dev/null
+++ b/kpf/src/ActiveMonitorWindow.h
@@ -0,0 +1,104 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ACTIVE_MONITOR_WINDOW_H
+#define KPF_ACTIVE_MONITOR_WINDOW_H
+
+#include <kmainwindow.h>
+
+class KAction;
+
+namespace KPF
+{
+ class ActiveMonitor;
+ class WebServer;
+
+ /**
+ * Wraps an ActiveMonitor (widget) in a toplevel window.
+ *
+ * A wrapper window is used to avoid forcing ActiveMonitor to be
+ * toplevel, so it can be used elsewhere if desired.
+ */
+ class ActiveMonitorWindow : public KMainWindow
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server WebServer which we should connect to in order to
+ * receive signals.
+ */
+ ActiveMonitorWindow
+ (
+ WebServer * server,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~ActiveMonitorWindow();
+
+ /**
+ * @return WebServer object we were given at construction.
+ */
+ WebServer * server();
+
+ protected slots:
+
+ /**
+ * Connected to ActiveMonitor::selection, which tells us whether we
+ * should enable the 'kill connection' action.
+ */
+ void slotMayKill(bool);
+
+ protected:
+
+ /**
+ * Overridden to emit a signal when this window is closed.
+ */
+ virtual void closeEvent(QCloseEvent *);
+
+ signals:
+
+ /**
+ * Emitted when this window is closed.
+ */
+ void dying(ActiveMonitorWindow *);
+
+ /**
+ * Emitted when the selection of the contained QListView has changed.
+ * @param selectionExists true if there is a selection.
+ */
+ void selection(bool selectionExists);
+
+ private:
+
+ ActiveMonitor * monitor_;
+
+ KAction * killAction_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Applet.cpp b/kpf/src/Applet.cpp
new file mode 100644
index 00000000..edd7a652
--- /dev/null
+++ b/kpf/src/Applet.cpp
@@ -0,0 +1,488 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qpainter.h>
+#include <qtimer.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtoolbutton.h>
+#include <qpopupmenu.h>
+#include <qfileinfo.h>
+#include <qcursor.h>
+
+#include <dcopclient.h>
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kconfig.h>
+#include <kaboutapplication.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kurldrag.h>
+
+#include "System.h"
+#include "Defines.h"
+#include "Applet.h"
+#include "AppletItem.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "ServerWizard.h"
+
+static const char kpfVersion[] = "1.0.1";
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet *
+ init(QWidget * parent, const QString & configFile)
+ {
+ if (0 == kpf::userId() || 0 == kpf::effectiveUserId())
+ {
+ // Don't run as root.
+ KMessageBox::detailedError
+ ( 0,
+ i18n("You cannot run KPF as root."),
+ i18n("Running as root exposes the whole system to "
+ "external attackers."),
+ i18n("Running as root.")
+ );
+ return NULL;
+ }
+ else
+ {
+ kpf::blockSigPipe();
+
+ KGlobal::locale()->insertCatalogue("kpf");
+
+ return new KPF::Applet
+ (
+ configFile,
+ KPanelApplet::Normal,
+ KPanelApplet::About|KPanelApplet::Help,
+ parent,
+ "kpf"
+ );
+ }
+ }
+}
+
+namespace KPF
+{
+ Applet::Applet
+ (
+ const QString & configFile,
+ Type type,
+ int actions,
+ QWidget * parent,
+ const char * name
+ )
+ : KPanelApplet (configFile, type, actions, parent, name),
+ wizard_ (0L),
+ popup_ (0L),
+ dcopClient_ (0L)
+ {
+ setAcceptDrops(true);
+
+ //setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ //setLineWidth(1);
+
+ connect
+ (
+ WebServerManager::instance(),
+ SIGNAL(serverCreated(WebServer *)),
+ SLOT(slotServerCreated(WebServer *))
+ );
+
+ connect
+ (
+ WebServerManager::instance(),
+ SIGNAL(serverDisabled(WebServer *)),
+ SLOT(slotServerDisabled(WebServer *))
+ );
+
+ WebServerManager::instance()->loadConfig();
+
+ popup_ = new QPopupMenu(this);
+
+ popup_->insertItem
+ (BarIcon("filenew"), i18n("New Server..."), NewServer, NewServer);
+
+// popup_->insertItem
+// (BarIcon("quit"), i18n("Quit"), Quit, Quit);
+
+ dcopClient_ = new DCOPClient;
+ dcopClient_->registerAs("kpf", false);
+ }
+
+ Applet::~Applet()
+ {
+ delete dcopClient_;
+ WebServerManager::instance()->shutdown();
+ }
+
+ int
+ Applet::widthForHeight(int h) const
+ {
+ uint serverCount = itemList_.count();
+
+ if (0 == serverCount)
+ serverCount = 1;
+
+ if (Vertical == orientation())
+ return h / serverCount;
+ else
+ return h * serverCount;
+ }
+
+ int
+ Applet::heightForWidth(int w) const
+ {
+ uint serverCount = itemList_.count();
+
+ if (0 == serverCount)
+ serverCount = 1;
+
+ if (Vertical == orientation())
+ return w * serverCount;
+ else
+ return w / serverCount;
+ }
+
+ void
+ Applet::help()
+ {
+ kapp->invokeHelp( QString::null, "kpf" );
+ }
+
+ void
+ Applet::about()
+ {
+ KAboutData about
+ (
+ "kpf",
+ I18N_NOOP("kpf"),
+ kpfVersion,
+ I18N_NOOP("KDE public fileserver"),
+ KAboutData::License_Custom,
+ "(C) 2001 Rik Hemsley (rikkus) <rik@kde.org>",
+ I18N_NOOP(
+ "File sharing applet, using the HTTP (Hyper Text Transfer Protocol)"
+ " standard to serve files."
+ ),
+ "http://rikkus.info/kpf.html"
+ );
+
+ about.setLicenseText
+ (
+ I18N_NOOP
+ (
+"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
+"of this software and associated documentation files (the \"Software\"), to\n"
+"deal in the Software without restriction, including without limitation the\n"
+"rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n"
+"sell copies of the Software, and to permit persons to whom the Software is\n"
+"furnished to do so, subject to the following conditions:\n"
+"\n"
+"The above copyright notice and this permission notice shall be included in\n"
+"all copies or substantial portions of the Software.\n"
+"\n"
+"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
+"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
+"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
+"AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n"
+"ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\n"
+"WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
+ )
+ );
+
+ KAboutApplication a(&about, this);
+ a.exec();
+ }
+
+ void
+ Applet::orientationChange(Orientation)
+ {
+ resetLayout();
+ }
+
+ void
+ Applet::resizeEvent(QResizeEvent *)
+ {
+ resetLayout();
+ }
+
+ void
+ Applet::moveEvent(QMoveEvent *)
+ {
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ it.current()->setBackground();
+ }
+
+ void
+ Applet::resetLayout()
+ {
+ if (0 == itemList_.count())
+ return;
+
+ switch (orientation())
+ {
+ case Vertical:
+ {
+ uint itemHeight = height() / itemList_.count();
+
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ {
+ it.current()->resize(width(), itemHeight);
+ it.current()->move(0, i * itemHeight);
+ }
+ }
+ break;
+
+ case Horizontal:
+ {
+ uint itemWidth = width() / itemList_.count();
+
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (uint i = 0; it.current(); ++it, ++i)
+ {
+ it.current()->resize(itemWidth, height());
+ it.current()->move(i * itemWidth, 0);
+ }
+ }
+ default:
+ break;
+ }
+ }
+
+ void
+ Applet::mousePressEvent(QMouseEvent * ev)
+ {
+ if (Qt::RightButton != ev->button() && Qt::LeftButton != ev->button())
+ return;
+
+ switch (popup_->exec(QCursor::pos()))
+ {
+ case NewServer:
+ slotNewServer();
+ break;
+
+ case Quit:
+ slotQuit();
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ void
+ Applet::slotNewServerAtLocation(const QString & location)
+ {
+ if (0 != wizard_)
+ {
+ wizard_->setLocation(location);
+ wizard_->show();
+ }
+
+ else
+ {
+ wizard_ = new ServerWizard;
+
+ connect
+ (
+ wizard_,
+ SIGNAL(dying(ServerWizard *)),
+ SLOT(slotWizardDying(ServerWizard *))
+ );
+
+ wizard_->setLocation(location);
+ wizard_->show();
+ }
+ }
+
+ void
+ Applet::slotNewServer()
+ {
+ if (0 != wizard_)
+ wizard_->show();
+
+ else
+ {
+ wizard_ = new ServerWizard;
+
+ connect
+ (
+ wizard_,
+ SIGNAL(dying(ServerWizard *)),
+ SLOT(slotWizardDying(ServerWizard *))
+ );
+
+ wizard_->show();
+ }
+ }
+
+ void
+ Applet::slotWizardDying(ServerWizard * wiz)
+ {
+ if (QDialog::Accepted == wiz->result())
+ {
+ WebServerManager::instance()->createServerLocal
+ (
+ wiz->root(),
+ wiz->listenPort(),
+ wiz->bandwidthLimit(),
+ wiz->connectionLimit(),
+ Config::DefaultFollowSymlinks,
+ wiz->serverName()
+ );
+ }
+
+ delete wizard_;
+ wizard_ = 0;
+ }
+
+ void
+ Applet::drawContents(QPainter * p)
+ {
+ QPixmap px;
+
+ if (width() > 48)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 48);
+ else if (width() > 32)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 32);
+ else if (width() > 16)
+ px = KGlobal::iconLoader()->loadIcon("kpf", KIcon::Panel, 16);
+ else
+ return;
+
+ QRect r(contentsRect());
+
+ p->drawPixmap
+ (
+ r.x() + r.width() / 2 - px.width() / 2,
+ r.y() + r.height() / 2 - px.height() / 2,
+ px
+ );
+ }
+
+ void
+ Applet::dragEnterEvent(QDragEnterEvent * e)
+ {
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ return;
+
+ if (l.count() != 1)
+ return;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ return;
+
+ e->accept();
+ }
+
+ void
+ Applet::dropEvent(QDropEvent * e)
+ {
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ return;
+
+ if (l.count() != 1)
+ return;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ return;
+
+ e->accept();
+
+ slotNewServerAtLocation(url.path());
+ }
+
+ void
+ Applet::slotServerCreated(WebServer * server)
+ {
+ AppletItem * i = new AppletItem(server, this);
+
+ connect
+ (
+ i,
+ SIGNAL(newServer()),
+ SLOT(slotNewServer())
+ );
+
+ connect
+ (
+ i,
+ SIGNAL(newServerAtLocation(const QString &)),
+ SLOT(slotNewServerAtLocation(const QString &))
+ );
+
+ itemList_.append(i);
+ i->show();
+ emit(updateLayout());
+ resetLayout();
+ }
+
+ void
+ Applet::slotServerDisabled(WebServer * server)
+ {
+ QPtrListIterator<AppletItem> it(itemList_);
+
+ for (; it.current(); ++it)
+ {
+ AppletItem * i = it.current();
+
+ if (i->server() == server)
+ {
+ itemList_.removeRef(i);
+ delete i;
+ emit(updateLayout());
+ resetLayout();
+ return;
+ }
+ }
+ }
+
+ void
+ Applet::slotQuit()
+ {
+ // How ?
+ }
+
+} // End namespace KPF
+
+#include "Applet.moc"
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Applet.h b/kpf/src/Applet.h
new file mode 100644
index 00000000..15802a37
--- /dev/null
+++ b/kpf/src/Applet.h
@@ -0,0 +1,180 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_APPLET_H
+#define KPF_APPLET_H
+
+#include <qptrlist.h>
+#include <kpanelapplet.h>
+
+class QPopupMenu;
+class QPainter;
+class DCOPClient;
+
+namespace KPF
+{
+ class AppletItem;
+ class ServerWizard;
+ class WebServer;
+
+ /**
+ * Main `application' class, providing an implementation of KPanelApplet
+ * and managing AppletItem objects. Also provides a popup (context) menu of
+ * its own, to allow the user to add WebServer (and therefore AppletItem)
+ * objects.
+ */
+ class Applet : public KPanelApplet
+ {
+ Q_OBJECT
+
+ public:
+
+ Applet
+ (
+ const QString & configFile,
+ Type = Normal,
+ int = 0,
+ QWidget * = 0,
+ const char * = 0
+ );
+
+ ~Applet();
+
+ /**
+ * Overridden to give correct sizing according to orientation and number
+ * of contains AppletItem objects.
+ */
+ virtual int widthForHeight(int h) const;
+
+ /**
+ * Overridden to give correct sizing according to orientation and number
+ * of contains AppletItem objects.
+ */
+ virtual int heightForWidth(int w) const;
+
+ protected slots:
+
+ /**
+ * Called to create a new server when the path to the server is already
+ * known.
+ */
+ void slotNewServerAtLocation(const QString &);
+
+ /**
+ * Called to create a new server when the path to the server is unknown.
+ */
+ void slotNewServer();
+
+ /**
+ * Called when a ServerWizard is about to close.
+ */
+ void slotWizardDying(ServerWizard *);
+
+ /**
+ * Called when a WebServer object has been created. Creates an
+ * AppletItem, associates it with the former, and updates the layout.
+ */
+ void slotServerCreated(WebServer *);
+
+ /**
+ * Called when a WebServer object has been disabled.
+ * Deletes the associated AppletItem and updates the layout.
+ */
+ void slotServerDisabled(WebServer *);
+
+ /**
+ * Called when user asks for quit (via popup menu).
+ */
+ void slotQuit();
+
+ protected:
+
+ /**
+ * Overridden to display help window
+ */
+ virtual void help();
+
+ /**
+ * Overridden to provide an `about' dialog.
+ */
+ virtual void about();
+
+ /**
+ * Overridden to keep track of orientation change and update layout
+ * accordingly.
+ */
+ virtual void orientationChange(Orientation);
+
+ /**
+ * Overridden to update layout accordingly.
+ */
+ virtual void moveEvent(QMoveEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ /**
+ * Overridden to provide a context menu.
+ */
+ virtual void mousePressEvent(QMouseEvent *);
+
+ /**
+ * Updates the layout, moving AppletItem objects into proper positions.
+ */
+ virtual void resetLayout();
+
+ /**
+ * Overridden to provide something other than a blank display when there
+ * are no existing AppletItem objects contained.
+ */
+ virtual void drawContents(QPainter *);
+
+ /**
+ * Overridden to allow testing whether the dragged object points to a
+ * local directory.
+ */
+ virtual void dragEnterEvent(QDragEnterEvent *);
+
+ /**
+ * Overridden to allow creating a new WebServer when the dropped object
+ * points to a local directory.
+ */
+ virtual void dropEvent(QDropEvent *);
+
+ private:
+
+ enum
+ {
+ NewServer,
+ Quit
+ };
+
+ ServerWizard * wizard_;
+ QPopupMenu * popup_;
+ DCOPClient * dcopClient_;
+
+ QPtrList<AppletItem> itemList_;
+ };
+}
+
+#endif // KPF_APPLET_H
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/AppletItem.cpp b/kpf/src/AppletItem.cpp
new file mode 100644
index 00000000..e605f692
--- /dev/null
+++ b/kpf/src/AppletItem.cpp
@@ -0,0 +1,385 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qtimer.h>
+#include <qfileinfo.h>
+#include <qcursor.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kurldrag.h>
+#include <kapplication.h>
+
+#include "Defines.h"
+#include "AppletItem.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "BandwidthGraph.h"
+#include "ActiveMonitorWindow.h"
+#include "SingleServerConfigDialog.h"
+
+namespace KPF
+{
+ AppletItem::AppletItem(WebServer * server, QWidget * parent)
+ : QWidget (parent, "KPF::AppletItem"),
+ server_ (server),
+ configDialog_ (0L),
+ monitorWindow_ (0L),
+ graph_ (0L),
+ popup_ (0L)
+ {
+ setBackgroundOrigin(AncestorOrigin);
+ setAcceptDrops(true);
+
+ graph_ = new BandwidthGraph(server_, BandwidthGraph::UseOverlays, this);
+
+ graph_->setAcceptDrops(true);
+
+ graph_->installEventFilter(this);
+
+ (new QVBoxLayout(this))->addWidget(graph_);
+
+ QString popupTitle(i18n("kpf - %1").arg(server_->root()));
+
+ popup_ = new KPopupMenu(this);
+
+ popup_->insertTitle
+ (SmallIcon("kpf"), popupTitle, Title, Title);
+
+ popup_->insertItem
+ (SmallIcon("filenew"), i18n("New Server..."), NewServer, NewServer);
+
+ popup_->insertSeparator(Separator);
+
+ popup_->insertItem
+ (SmallIcon("viewmag"), i18n("Monitor"), Monitor, Monitor);
+
+ popup_->insertItem
+ (SmallIcon("configure"), i18n("Preferences..."), Configure, Configure);
+
+ popup_->insertItem
+ (SmallIcon("remove"), i18n("Remove"), Remove, Remove);
+
+ popup_->insertItem
+ (SmallIcon("reload"), i18n("Restart"), Restart, Restart);
+
+ popup_->insertItem
+ (SmallIcon("player_pause"), i18n("Pause"), Pause, Pause);
+
+ monitorWindow_ = new ActiveMonitorWindow(server_);
+
+ connect
+ (
+ monitorWindow_,
+ SIGNAL(dying(ActiveMonitorWindow *)),
+ SLOT(slotActiveMonitorWindowDying(ActiveMonitorWindow *))
+ );
+ }
+
+ AppletItem::~AppletItem()
+ {
+ delete configDialog_;
+ configDialog_ = 0;
+
+ delete monitorWindow_;
+ monitorWindow_ = 0;
+ }
+
+ void AppletItem::setBackground()
+ {
+ QResizeEvent e(size(), size());
+ kapp->sendEvent(graph_, &e);
+ graph_->update();
+ }
+
+ bool
+ AppletItem::eventFilter(QObject *, QEvent * ev)
+ {
+ switch (ev->type())
+ {
+
+ case QEvent::MouseButtonRelease:
+ {
+ QMouseEvent * e = static_cast<QMouseEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ if (!rect().contains(e->pos()))
+ {
+ break;
+ }
+
+ if (Qt::LeftButton == e->button())
+ {
+ // Show monitor.
+
+ if (0 == monitorWindow_)
+ monitorServer();
+
+ else
+ {
+ if (monitorWindow_->isVisible())
+ monitorWindow_->hide();
+ else
+ monitorWindow_->show();
+ }
+ }
+
+ return true;
+ }
+ break;
+
+ case QEvent::MouseButtonPress:
+ {
+ QMouseEvent * e = static_cast<QMouseEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ if (Qt::RightButton != e->button() && Qt::LeftButton != e->button())
+ break;
+
+ if (server_->paused())
+ popup_->changeItem
+ (Pause, SmallIcon("1rightarrow"), i18n("Unpause"));
+ else
+ popup_->changeItem
+ (Pause, SmallIcon("player_pause"), i18n("Pause"));
+
+ switch (popup_->exec(QCursor::pos()))
+ {
+ case NewServer:
+ emit(newServer());
+ break;
+
+ case Monitor:
+ monitorServer();
+ break;
+
+ case Configure:
+ configureServer();
+ break;
+
+ case Remove:
+ removeServer();
+ break;
+
+ case Restart:
+ restartServer();
+ break;
+
+ case Pause:
+ pauseServer();
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+ break;
+
+ case QEvent::DragEnter:
+ {
+ QDragEnterEvent * e = static_cast<QDragEnterEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ break;
+
+ if (l.count() != 1)
+ break;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ break;
+
+ e->accept();
+ return true;
+ }
+ break;
+
+ case QEvent::Drop:
+ {
+ QDropEvent * e = static_cast<QDropEvent *>(ev);
+
+ if (0 == e)
+ {
+ kpfDebug
+ << "Hmm, should have been able to static_cast here." << endl;
+ break;
+ }
+
+ KURL::List l;
+
+ if (!KURLDrag::decode(e, l))
+ break;
+
+ if (l.count() != 1)
+ break;
+
+ const KURL &url = l[0];
+
+ if (!url.isLocalFile() || !QFileInfo(url.path()).isDir())
+ break;
+
+ e->accept();
+ emit(newServerAtLocation(url.path()));
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ void
+ AppletItem::slotActiveMonitorWindowDying(ActiveMonitorWindow *)
+ {
+ // We used to delete it here, but let's not. See if this is a CPU hog.
+#if 0
+ delete monitorWindow_;
+ monitorWindow_ = 0;
+#endif
+ monitorWindow_->hide();
+ }
+
+ void
+ AppletItem::slotConfigDialogDying(SingleServerConfigDialog *)
+ {
+ graph_->setTooltip();
+
+ configDialog_->delayedDestruct();
+ configDialog_ = 0;
+ }
+
+ void
+ AppletItem::slotNewServer()
+ {
+ emit(newServer());
+ }
+
+ void
+ AppletItem::monitorServer()
+ {
+ // We used to delete it here, but let's not. See if this is a CPU hog.
+#if 0
+ if (0 != monitorWindow_)
+ {
+ monitorWindow_->show();
+ return;
+ }
+
+ monitorWindow_ = new ActiveMonitorWindow(server_);
+
+ connect
+ (
+ monitorWindow_,
+ SIGNAL(dying(ActiveMonitorWindow *)),
+ SLOT(slotActiveMonitorWindowDying(ActiveMonitorWindow *))
+ );
+#endif
+
+ monitorWindow_->show();
+ monitorWindow_->raise();
+ }
+
+ void
+ AppletItem::removeServer()
+ {
+ QTimer::singleShot(0, this, SLOT(slotSuicide()));
+ }
+
+ void
+ AppletItem::configureServer()
+ {
+ if (0 != configDialog_)
+ {
+ configDialog_->show();
+ return;
+ }
+
+ configDialog_ = new SingleServerConfigDialog(server_, 0);
+
+ connect
+ (
+ configDialog_,
+ SIGNAL(dying(SingleServerConfigDialog *)),
+ SLOT(slotConfigDialogDying(SingleServerConfigDialog *))
+ );
+
+ configDialog_->show();
+ }
+
+ void
+ AppletItem::slotSuicide()
+ {
+ WebServerManager::instance()->disableServer(server_->root());
+ }
+
+ void
+ AppletItem::restartServer()
+ {
+ server_->restart();
+ }
+
+ void
+ AppletItem::pauseServer()
+ {
+ server_->pause(!server_->paused());
+ }
+
+ WebServer *
+ AppletItem::server()
+ {
+ return server_;
+ }
+}
+
+#include "AppletItem.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/AppletItem.h b/kpf/src/AppletItem.h
new file mode 100644
index 00000000..013a6b0a
--- /dev/null
+++ b/kpf/src/AppletItem.h
@@ -0,0 +1,167 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_APPLET_ITEM_H
+#define KPF_APPLET_ITEM_H
+
+#include <qptrlist.h>
+#include <qwidget.h>
+
+class KPopupMenu;
+
+namespace KPF
+{
+ class ConfigDialog;
+ class BandwidthGraph;
+ class ActiveMonitorWindow;
+ class SingleServerConfigDialog;
+ class WebServer;
+
+ /**
+ * Provides control of, and coarse-grained activity display for, a WebServer
+ * object. Contains a BandwidthGraph widget and provides a context menu
+ * which allows WebServer object control, plus creation of a new WebServer,
+ * for user convenience.
+ */
+ class AppletItem : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * @param server The WebServer object which will be monitored and
+ * controlled by this object.
+ */
+ AppletItem(WebServer * server, QWidget * parent);
+
+ ~AppletItem();
+
+ /**
+ * @return the WebServer object given on construction.
+ */
+ WebServer * server();
+
+ void setBackground();
+
+ protected slots:
+
+ /**
+ * Called when an ActiveMonitorWindow (created by this object) is
+ * about to close.
+ */
+ void slotActiveMonitorWindowDying(ActiveMonitorWindow *);
+
+ /**
+ * Called when a SingleServerConfigDialog (created by this object) is
+ * about to close.
+ */
+ void slotConfigDialogDying(SingleServerConfigDialog *);
+
+ /**
+ * Called when the user requests a new WebServer via the context menu.
+ */
+ void slotNewServer();
+
+ /**
+ * Called by a timer after removeServer has been called and the event
+ * loop has been processed once.
+ */
+ void slotSuicide();
+
+ signals:
+
+ /**
+ * Emitted when a new WebServer is requested from the context menu.
+ */
+ void newServer();
+
+ /**
+ * Emitted when an URL pointing to a local directory has been dropped.
+ */
+ void newServerAtLocation(const QString &);
+
+ protected:
+
+ /**
+ * Overridden to provide a context menu plus DnD capabilities.
+ */
+ bool eventFilter(QObject *, QEvent *);
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Creates an ActiveMonitorWindow.
+ */
+ void monitorServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Asks the WebServerManager instance to remove the associated
+ * WebServer.
+ */
+ void removeServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Creates a configuration dialog for the associated WebServer.
+ */
+ void configureServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Restarts the associated WebServer.
+ */
+ void restartServer ();
+
+ /**
+ * Called when the appropriate item is selected from the context menu.
+ * Pauses the associated WebServer.
+ */
+ void pauseServer ();
+
+ private:
+
+ enum
+ {
+ Title,
+ NewServer,
+ Separator,
+ Monitor,
+ Configure,
+ Remove,
+ Restart,
+ Pause
+ };
+
+
+ WebServer * server_;
+ SingleServerConfigDialog * configDialog_;
+ ActiveMonitorWindow * monitorWindow_;
+ BandwidthGraph * graph_;
+ KPopupMenu * popup_;
+ };
+}
+
+#endif // KPF_APPLET_H
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/BandwidthGraph.cpp b/kpf/src/BandwidthGraph.cpp
new file mode 100644
index 00000000..a7f6c311
--- /dev/null
+++ b/kpf/src/BandwidthGraph.cpp
@@ -0,0 +1,335 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qdrawutil.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "BandwidthGraph.h"
+#include "WebServer.h"
+
+namespace KPF
+{
+ BandwidthGraph::BandwidthGraph
+ (
+ WebServer * server,
+ OverlaySelect overlaySelect,
+ QWidget * parent,
+ const char * name
+ )
+ : QWidget (parent, name, WRepaintNoErase),
+ server_ (server),
+ max_ (0L),
+ overlaySelect_ (overlaySelect)
+ {
+ setBackgroundOrigin(AncestorOrigin);
+ history_.resize(width());
+ history_.fill(0L);
+
+ connect
+ (
+ server_,
+ SIGNAL(wholeServerOutput(ulong)),
+ SLOT(slotOutput(ulong))
+ );
+
+ if (UseOverlays == overlaySelect_)
+ {
+ connect
+ (
+ server_,
+ SIGNAL(contentionChange(bool)), this,
+ SLOT(slotServerContentionChange(bool))
+ );
+
+ connect
+ (
+ server_,
+ SIGNAL(pauseChange(bool)), this,
+ SLOT(slotServerPauseChange(bool))
+ );
+ }
+
+ setTooltip();
+ }
+
+ BandwidthGraph::~BandwidthGraph()
+ {
+ // Empty.
+ }
+
+ void
+ BandwidthGraph::setTooltip()
+ {
+ QToolTip::add(this, i18n( "%1 on port %2" )
+ .arg( server_->root() ).arg( server_->listenPort() ) );
+ }
+
+ QRect
+ BandwidthGraph::contentsRect() const
+ {
+ return QRect(1, 1, width() - 2, height() - 2);
+ }
+
+ void
+ BandwidthGraph::updateContents()
+ {
+ QRect r(contentsRect());
+
+ uint w = r.width();
+ uint h = r.height();
+
+ buffer_.fill(this, 0, 0);
+
+ QPainter p(&buffer_);
+
+ p.drawPixmap( ( width()-bgPix_.width() )/2,
+ ( height()-bgPix_.height() )/2, bgPix_ );
+
+ p.setPen(colorGroup().dark());
+
+ for (uint i = 0; i < history_.size(); i++)
+ {
+ ulong l = history_[i];
+
+ if (0 != l)
+ {
+ uint barLength =
+ static_cast<uint>(l / float(max_) * h);
+
+ p.drawLine(i + 1, h, i + 1, h - barLength);
+ }
+ }
+
+ drawOverlays(p);
+
+ update();
+ }
+
+ void
+ BandwidthGraph::paintEvent(QPaintEvent * e)
+ {
+ bitBlt(this, e->rect().topLeft(), &buffer_, e->rect());
+ }
+
+ void
+ BandwidthGraph::resizeEvent(QResizeEvent *)
+ {
+ buffer_.resize(size());
+
+ if ( width() > 48 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 48 );
+ else if ( width() > 32 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 32 );
+ else if ( width() > 16 )
+ bgPix_ = KGlobal::iconLoader()->loadIcon( "kpf", KIcon::Panel, 16 );
+ else
+ bgPix_.fill( this, QPoint( 0, 0 ) );
+
+ KIconEffect::semiTransparent( bgPix_ );
+
+ if (width() < 2)
+ {
+ // We have 0 space. Make history 0 size.
+ history_ = QMemArray<ulong>();
+ return;
+ }
+
+ uint w = width() - 2;
+
+ if (w < history_.size())
+ {
+ QMemArray<ulong> newHistory(w);
+
+ uint sizeDiff = history_.size() - w;
+
+ for (uint i = sizeDiff; i < history_.size(); i++)
+ newHistory[i - sizeDiff] = history_[i];
+
+ history_ = newHistory;
+ }
+ else if (w > history_.size())
+ {
+ QMemArray<ulong> newHistory(w);
+
+ uint sizeDiff = w - history_.size();
+
+ for (uint i = 0; i < sizeDiff; i++)
+ {
+ newHistory[i] = 0L;
+ }
+
+ for (uint i = 0; i < history_.size(); i++)
+ newHistory[sizeDiff + i] = history_[i];
+
+ history_ = newHistory;
+ }
+
+ updateContents();
+ }
+
+ void
+ BandwidthGraph::slotOutput(ulong l)
+ {
+ QRect r(contentsRect());
+
+ uint w = r.width();
+ uint h = r.height();
+
+ if (0 == w || 0 == h)
+ return;
+
+ ulong oldMax = max_;
+
+ max_ = 0L;
+
+ if (history_.size() != w)
+ return;
+
+ for (uint i = 1; i < w; i++)
+ {
+ history_[i - 1] = history_[i];
+ max_ = max(history_[i], max_);
+ }
+
+ history_[w - 1] = l;
+ max_ = max(l, max_);
+
+ if (max_ != oldMax)
+ emit(maximumChanged(max_));
+
+ updateContents();
+ }
+
+ void
+ BandwidthGraph::drawOverlays(QPainter & p)
+ {
+ if (NoOverlays == overlaySelect_)
+ return;
+
+ if (!overlayPixmap_.isNull())
+ {
+ p.drawPixmap(3, 3, overlayPixmap_);
+ }
+
+ if (width() < 32 || height() < 32)
+ return;
+
+ if (overlayPixmap_.isNull())
+ {
+ QString maxString;
+
+ QString bs(i18n("%1 b/s"));
+ QString kbs(i18n("%1 kb/s"));
+ QString mbs(i18n("%1 Mb/s"));
+
+ if (max_ > 1024)
+ if (max_ > 1024 * 1024)
+ maxString = mbs.arg(max_ / (1024 * 1024));
+ else
+ maxString = kbs.arg(max_ / 1024);
+ else if ( max_ > 0 )
+ maxString = bs.arg(max_);
+ else
+ maxString = i18n( "Idle" );
+
+ p.setPen(Qt::white);
+
+ p.drawText
+ (
+ 4,
+ 4 + fontMetrics().ascent(),
+ maxString
+ );
+
+ p.setPen(Qt::black);
+
+ p.drawText
+ (
+ 3,
+ 3 + fontMetrics().ascent(),
+ maxString
+ );
+ }
+ }
+
+ QSize
+ BandwidthGraph::sizeHint() const
+ {
+ return QSize(32, 32);
+ }
+
+ QSize
+ BandwidthGraph::minimumSizeHint() const
+ {
+ return QSize(12, 12);
+ }
+
+ WebServer *
+ BandwidthGraph::server()
+ {
+ return server_;
+ }
+
+ void
+ BandwidthGraph::slotServerContentionChange(bool)
+ {
+ updateOverlayPixmap();
+ }
+
+ void
+ BandwidthGraph::slotServerPauseChange(bool)
+ {
+ updateOverlayPixmap();
+ }
+
+ void
+ BandwidthGraph::updateOverlayPixmap()
+ {
+ if (server_->paused())
+ {
+ overlayPixmap_ = SmallIcon("player_pause");
+ }
+ else
+ {
+ if (server_->portContention())
+ {
+ overlayPixmap_ = SmallIcon("connect_creating");
+ }
+ else
+ {
+ overlayPixmap_ = QPixmap();
+ }
+ }
+ }
+
+} // End namespace KPF
+
+#include "BandwidthGraph.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/BandwidthGraph.h b/kpf/src/BandwidthGraph.h
new file mode 100644
index 00000000..e0cf51dd
--- /dev/null
+++ b/kpf/src/BandwidthGraph.h
@@ -0,0 +1,163 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_BANDWIDTH_GRAPH_H
+#define KPF_BANDWIDTH_GRAPH_H
+
+#include <qwidget.h>
+#include <qmemarray.h>
+#include <qpixmap.h>
+#include <qrect.h>
+
+class QPainter;
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Draws a graph of the bandwidth usage of a WebServer over time.
+ * May also displays an overlayed icon to show the status of a WebServer,
+ * i.e. whether it is active (no icon,) paused or in contention for a port.
+ */
+ class BandwidthGraph : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ enum OverlaySelect
+ {
+ UseOverlays,
+ NoOverlays
+ };
+
+ /**
+ * @param server WebServer to monitor.
+ * @param overlaySelect if UseOverlays, draw overlay icons to reflect
+ * server status.
+ */
+ BandwidthGraph
+ (
+ WebServer * server,
+ OverlaySelect overlaySelect,
+ QWidget * parent = 0,
+ const char * name = 0
+ );
+
+ virtual ~BandwidthGraph();
+
+ /**
+ * Set the tooltip showing shared directory name and port
+ */
+ void setTooltip();
+
+ /**
+ * Overridden to provide reasonable default size and shape.
+ */
+ virtual QSize sizeHint() const;
+
+ /**
+ * Overridden to provide reasonable minimum size and shape.
+ */
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ * @return the WebServer object given on construction.
+ */
+ WebServer * server();
+
+ protected slots:
+
+ /**
+ * Connected to associated WebServer to receive notification of output.
+ */
+ void slotOutput(ulong);
+
+ /**
+ * Connected to associated WebServer to receive notification of port
+ * contention.
+ */
+ void slotServerContentionChange(bool);
+
+ /**
+ * Connected to associated WebServer to receive notification of pause
+ * or unpause.
+ */
+ void slotServerPauseChange(bool);
+
+ protected:
+
+ /**
+ * Overridden to provide graph drawing.
+ */
+ virtual void paintEvent(QPaintEvent *);
+
+ /**
+ * Overridden to assist graph drawing.
+ */
+ virtual void resizeEvent(QResizeEvent *);
+
+ /**
+ * Draw overlay icons to reflect status of associated WebServer.
+ */
+ virtual void drawOverlays(QPainter &);
+
+ /**
+ * Provides a rectangle in which to draw the graph itself.
+ */
+ virtual QRect contentsRect() const;
+
+ /**
+ * Called when the status of the associated WebServer changes.
+ */
+ virtual void updateOverlayPixmap();
+
+ signals:
+
+ /**
+ * Emitted when the maximum displayed value has changed.
+ */
+ void maximumChanged(ulong);
+
+ private:
+
+ void updateContents();
+
+ QMemArray<ulong> history_;
+
+ WebServer * server_;
+
+ QPixmap buffer_, bgPix_;
+
+ ulong max_;
+
+ OverlaySelect overlaySelect_;
+
+ QPixmap overlayPixmap_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ByteRange.cpp b/kpf/src/ByteRange.cpp
new file mode 100644
index 00000000..cdd0624b
--- /dev/null
+++ b/kpf/src/ByteRange.cpp
@@ -0,0 +1,176 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qstringlist.h>
+
+#include "Defines.h"
+#include "ByteRange.h"
+
+namespace KPF
+{
+ ByteRange::ByteRange()
+ : first_ (0L),
+ last_ (0L),
+ haveLast_ (false)
+ {
+ // Empty.
+ }
+
+ ByteRange::ByteRange(ulong first)
+ : first_ (first),
+ last_ (0L),
+ haveLast_ (false)
+ {
+ // Empty.
+ }
+
+
+ ByteRange::ByteRange(ulong first, ulong last)
+ : first_ (first),
+ last_ (last),
+ haveLast_ (true)
+ {
+ }
+
+ ulong
+ ByteRange::first() const
+ {
+ return first_;
+ }
+
+ ulong
+ ByteRange::last() const
+ {
+ return last_;
+ }
+
+ bool
+ ByteRange::haveLast() const
+ {
+ return haveLast_;
+ }
+
+ void
+ ByteRange::setFirst(ulong l)
+ {
+ first_ = l;
+ }
+
+ void
+ ByteRange::setLast(ulong l)
+ {
+ last_ = l;
+ haveLast_ = true;
+ }
+
+ bool
+ ByteRange::valid() const
+ {
+ return haveLast_ ? (first_ < last_) : true;
+ }
+
+ void
+ ByteRange::clear()
+ {
+ first_ = last_ = 0L;
+ haveLast_ = false;
+ }
+
+ ByteRangeList::ByteRangeList()
+ {
+ // Empty.
+ }
+
+ ByteRangeList::ByteRangeList(const QString & _s, float /* protocol */)
+ {
+ kpfDebug << "ByteRangeList parsing `" << _s << "'" << endl;
+
+ // Hey, parsing time :)
+
+ QString s(_s);
+
+ if ("bytes=" == s.left(6))
+ {
+ s.remove(0, 6);
+ s = s.stripWhiteSpace();
+ }
+
+ QStringList byteRangeSpecList(QStringList::split(',', s));
+
+ QStringList::ConstIterator it;
+
+ for (it = byteRangeSpecList.begin(); it != byteRangeSpecList.end(); ++it)
+ addByteRange(*it);
+ }
+
+ void
+ ByteRangeList::addByteRange(const QString & s)
+ {
+ kpfDebug << "addByteRange(" << s << ")" << endl;
+
+ int dashPos = s.find('-');
+
+ if (-1 == dashPos)
+ {
+ kpfDebug << "No dash" << endl;
+ return;
+ }
+
+ QString firstByte(s.left(dashPos).stripWhiteSpace());
+
+ QString lastByte(s.mid(dashPos + 1).stripWhiteSpace());
+
+ ulong first;
+
+ if (firstByte.isEmpty())
+ first = 0L;
+ else
+ first = firstByte.toULong();
+
+ ulong last;
+
+ bool haveLast = !lastByte.isEmpty();
+
+ if (haveLast)
+ last = lastByte.toULong();
+ else
+ last = 0L;
+
+ if (haveLast)
+ {
+ if (first < last)
+ {
+ kpfDebug << "range: " << first << "d - " << last << "d" << endl;
+ append(ByteRange(first, last));
+ }
+ }
+ else
+ {
+ kpfDebug << "range: " << first << "d - end" << endl;
+ append(ByteRange(first));
+ }
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ByteRange.h b/kpf/src/ByteRange.h
new file mode 100644
index 00000000..4dd0b841
--- /dev/null
+++ b/kpf/src/ByteRange.h
@@ -0,0 +1,128 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_BYTE_RANGE_H
+#define KPF_BYTE_RANGE_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+namespace KPF
+{
+ /**
+ * Parse and store an HTTP 'byte range'.
+ * A range consists of a first and, optionally, a last byte.
+ * If the last byte is unspecified, it should be assumed to be
+ * the last byte of the resource being retrieved.
+ * A valid range has first <= last.
+ */
+ class ByteRange
+ {
+ public:
+
+ /**
+ * Constructs an empty range.
+ */
+ ByteRange();
+
+ /**
+ * Constructs a range with the first byte specified and the last
+ * byte set to 'none'.
+ */
+ ByteRange(ulong first);
+
+ /**
+ * Constructs a range with the first and last bytes specified.
+ */
+ ByteRange(ulong first, ulong last);
+
+ /**
+ * @return first byte in range.
+ */
+ ulong first() const;
+
+ /**
+ * @return last byte in range, if specified. Otherwise undefined.
+ */
+ ulong last() const;
+
+ /**
+ * @return true if last byte was specified.
+ */
+ bool haveLast() const;
+
+ /**
+ * Specify the first byte of the range.
+ */
+ void setFirst (ulong l);
+
+ /**
+ * Specify the last byte of the range. After setting this
+ * value, haveLast() will return true.
+ */
+ void setLast (ulong l);
+
+ /**
+ * @return true if first <= last or last is undefined.
+ */
+ bool valid() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ private:
+
+ ulong first_;
+ ulong last_;
+ bool haveLast_;
+ };
+
+ /**
+ * Encapsulates a list of ByteRange.
+ */
+ class ByteRangeList : public QValueList<ByteRange>
+ {
+ public:
+
+ ByteRangeList();
+
+ /**
+ * Contructs a ByteRangeList from a string, which is parsed.
+ * @param protocol specifies the HTTP protocol which should
+ * be assumed whilst parsing.
+ */
+ ByteRangeList(const QString &, float protocol);
+
+ /**
+ * Parses a byte range represented as a string and, if successful,
+ * appends the resultant ByteRange object to this list.
+ */
+ void addByteRange(const QString &);
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ConfigDialogPage.cpp b/kpf/src/ConfigDialogPage.cpp
new file mode 100644
index 00000000..395bb681
--- /dev/null
+++ b/kpf/src/ConfigDialogPage.cpp
@@ -0,0 +1,318 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qwhatsthis.h>
+#include <qlayout.h>
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qlineedit.h>
+
+#include <klocale.h>
+#include <kseparator.h>
+#include <kfiledialog.h>
+
+#include "Defines.h"
+#include "ErrorMessageConfigDialog.h"
+#include "ConfigDialogPage.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "Help.h"
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ ConfigDialogPage::ConfigDialogPage(WebServer * server, QWidget * parent)
+ : QWidget (parent, "KPF::ConfigDialogPage"),
+ server_ (server),
+ errorMessageConfigDialog_ (0L)
+ {
+ l_listenPort_ = new QLabel(i18n("&Listen port:"), this);
+ l_bandwidthLimit_ = new QLabel(i18n("&Bandwidth limit:"), this);
+// l_connectionLimit_ = new QLabel(i18n("Connection &limit"), this);
+
+ sb_listenPort_ = new QSpinBox(1, 65535, 1, this);
+ sb_bandwidthLimit_ = new QSpinBox(1, 999999, 1, this);
+// sb_connectionLimit_ = new QSpinBox(1, 9999, 1, this);
+
+ l_serverName_ = new QLabel(i18n("&Server name:"), this);
+ le_serverName_ = new QLineEdit(this);
+
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ l_serverName_->setEnabled(canPublish);
+ le_serverName_->setEnabled(canPublish);
+
+ cb_followSymlinks_ = new QCheckBox(i18n("&Follow symbolic links"), this);
+
+// cb_customErrorMessages_ =
+// new QCheckBox(i18n("Use custom error messages"), this);
+
+// pb_errorMessages_ = new QPushButton(i18n("&Configure..."), this);
+
+// pb_errorMessages_->setEnabled(false);
+
+ l_listenPort_ ->setBuddy(sb_listenPort_);
+ l_bandwidthLimit_ ->setBuddy(sb_bandwidthLimit_);
+ l_serverName_ ->setBuddy(le_serverName_);
+// l_connectionLimit_ ->setBuddy(sb_connectionLimit_);
+
+ sb_listenPort_
+ ->setValue(WebServerManager::instance()->nextFreePort());
+
+ sb_bandwidthLimit_ ->setValue(Config::DefaultBandwidthLimit);
+ sb_bandwidthLimit_ ->setSuffix(i18n(" kB/s"));
+// sb_connectionLimit_ ->setValue(Config::DefaultConnectionLimit);
+ cb_followSymlinks_ ->setChecked(Config::DefaultFollowSymlinks);
+
+ QVBoxLayout * l0 = new QVBoxLayout(this, 0, KDialog::spacingHint());
+
+ QGridLayout * l2 = new QGridLayout(l0);
+
+ l2->addWidget(l_listenPort_, 0, 0);
+ l2->addWidget(sb_listenPort_, 0, 1);
+ l2->addWidget(l_bandwidthLimit_, 1, 0);
+ l2->addWidget(sb_bandwidthLimit_, 1, 1);
+ l2->addWidget(l_serverName_, 2, 0);
+ l2->addWidget(le_serverName_, 2, 1);
+// l2->addWidget(l_connectionLimit_, 2, 0);
+// l2->addWidget(sb_connectionLimit_, 2, 1);
+
+ l0->addWidget(cb_followSymlinks_);
+
+#if 0
+ QHBoxLayout * l3 = new QHBoxLayout(l0);
+
+ l3->addWidget(cb_customErrorMessages_);
+ l3->addWidget(pb_errorMessages_);
+#endif
+
+ l0->addStretch(1);
+
+#if 0
+ connect
+ (
+ cb_customErrorMessages_,
+ SIGNAL(toggled(bool)),
+ SLOT(slotCustomErrorMessagesToggled(bool))
+ );
+#endif
+
+#if 0
+ connect
+ (
+ pb_errorMessages_,
+ SIGNAL(clicked()),
+ SLOT(slotConfigureErrorMessages())
+ );
+#endif
+
+ QString listenPortHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ );
+
+ QString bandwidthLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ );
+
+ QString connectionLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ );
+
+ QString followSymlinksHelp =
+ i18n
+ (
+ "<p>"
+ "Allow serving of files which have a symbolic link in"
+ " the path from / to the file, or are a symbolic link"
+ " themselves."
+ "</p>"
+ "<p>"
+ "<strong>Warning !</strong> This could be a security"
+ " risk. Use only if you understand the issues involved."
+ "</p>"
+ );
+
+ QString errorMessagesHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the text that will be sent upon an error,"
+ " such as a request for a page that does not exist"
+ " on this server."
+ "</p>"
+ );
+
+ QString serverNameHelp = KPF::HelpText::getServerNameHelp();
+ QWhatsThis::add(l_listenPort_, listenPortHelp);
+ QWhatsThis::add(sb_listenPort_, listenPortHelp);
+ QWhatsThis::add(l_bandwidthLimit_, bandwidthLimitHelp);
+ QWhatsThis::add(sb_bandwidthLimit_, bandwidthLimitHelp);
+// QWhatsThis::add(l_connectionLimit_, connectionLimitHelp);
+// QWhatsThis::add(sb_connectionLimit_, connectionLimitHelp);
+ QWhatsThis::add(cb_followSymlinks_, followSymlinksHelp);
+ QWhatsThis::add(l_serverName_, serverNameHelp);
+ QWhatsThis::add(le_serverName_, serverNameHelp);
+// QWhatsThis::add(pb_errorMessages_, errorMessagesHelp);
+
+ connect
+ (
+ sb_listenPort_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotListenPortChanged(int))
+ );
+
+ connect
+ (
+ sb_bandwidthLimit_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotBandwidthLimitChanged(int))
+ );
+
+ connect
+ (
+ cb_followSymlinks_,
+ SIGNAL(toggled(bool)),
+ SLOT(slotFollowSymlinksToggled(bool))
+ );
+
+
+ load();
+ }
+
+ ConfigDialogPage::~ConfigDialogPage()
+ {
+ // Empty.
+ }
+
+ void
+ ConfigDialogPage::load()
+ {
+ sb_listenPort_ ->setValue(server_->listenPort());
+ sb_bandwidthLimit_ ->setValue(server_->bandwidthLimit());
+// sb_connectionLimit_ ->setValue(server_->connectionLimit());
+ cb_followSymlinks_ ->setChecked(server_->followSymlinks());
+ le_serverName_ ->setText(server_->serverName());
+// cb_customErrorMessages_ ->setChecked(server_->customErrorMessages());
+ }
+
+ void
+ ConfigDialogPage::save()
+ {
+ server_->setListenPort (sb_listenPort_->value());
+ server_->setBandwidthLimit (sb_bandwidthLimit_->value());
+// server_->setConnectionLimit (sb_connectionLimit_->value());
+ server_->setFollowSymlinks (cb_followSymlinks_->isChecked());
+ server_->setCustomErrorMessages (cb_followSymlinks_->isChecked());
+ server_->setServerName (le_serverName_->text());
+ }
+
+ void
+ ConfigDialogPage::slotCustomErrorMessagesToggled(bool)
+ {
+// pb_errorMessages_->setEnabled(b);
+ }
+
+ void
+ ConfigDialogPage::slotConfigureErrorMessages()
+ {
+ if (0 == errorMessageConfigDialog_)
+ errorMessageConfigDialog_ = new ErrorMessageConfigDialog(server_, this);
+
+ errorMessageConfigDialog_->show();
+ }
+
+ void
+ ConfigDialogPage::slotListenPortChanged(int)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::checkOk()
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::slotBandwidthLimitChanged(int)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::slotFollowSymlinksToggled(bool)
+ {
+ kpfDebug << "slotBandwidthLimitChanged" << endl;
+ checkOkAndEmit();
+ }
+
+ void ConfigDialogPage::checkOkAndEmit()
+ {
+ int newPort = sb_listenPort_->value();
+
+ if (newPort <= 1024)
+ {
+ emit(ok(false));
+ return;
+ }
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current() == server_)
+ continue;
+
+ if (it.current()->listenPort() == uint(newPort))
+ {
+ emit(ok(false));
+ return;
+ }
+ }
+
+ emit(ok(true));
+ }
+}
+#include "ConfigDialogPage.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ConfigDialogPage.h b/kpf/src/ConfigDialogPage.h
new file mode 100644
index 00000000..8d41ea83
--- /dev/null
+++ b/kpf/src/ConfigDialogPage.h
@@ -0,0 +1,107 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_CONFIG_DIALOG_PAGE_H
+#define KPF_CONFIG_DIALOG_PAGE_H
+
+#include <qptrlist.h>
+#include <qwidget.h>
+
+class QLabel;
+class QSpinBox;
+class QCheckBox;
+class QPushButton;
+class QLineEdit;
+
+namespace KPF
+{
+ class WebServer;
+ class ErrorMessageConfigDialog;
+
+ /**
+ * Allows user configuration of a WebServer object.
+ */
+ class ConfigDialogPage : public QWidget
+ {
+ Q_OBJECT
+
+ public:
+
+ ConfigDialogPage(WebServer *, QWidget * parent);
+
+ virtual ~ConfigDialogPage();
+
+ /**
+ * Read settings from associated WebServer object and update controls.
+ */
+ void load();
+
+ /**
+ * Set attributes of associated WebServer object from controls.
+ */
+ void save();
+
+ void checkOk();
+
+ protected slots:
+
+ void slotConfigureErrorMessages();
+ void slotCustomErrorMessagesToggled(bool);
+ void slotListenPortChanged(int);
+ void slotBandwidthLimitChanged(int);
+ void slotFollowSymlinksToggled(bool);
+
+ protected:
+
+ void checkOkAndEmit();
+
+ signals:
+
+ void ok(bool);
+
+ private:
+
+ WebServer * server_;
+
+ QLabel * l_listenPort_;
+ QLabel * l_bandwidthLimit_;
+ QLabel * l_connectionLimit_;
+ QLabel * l_serverName_;
+
+ QSpinBox * sb_listenPort_;
+ QSpinBox * sb_bandwidthLimit_;
+ QSpinBox * sb_connectionLimit_;
+
+ QCheckBox * cb_followSymlinks_;
+
+ QLineEdit * le_serverName_;
+
+ QCheckBox * cb_customErrorMessages_;
+ QPushButton * pb_errorMessages_;
+
+ ErrorMessageConfigDialog * errorMessageConfigDialog_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defaults.cpp b/kpf/src/Defaults.cpp
new file mode 100644
index 00000000..691ca0f2
--- /dev/null
+++ b/kpf/src/Defaults.cpp
@@ -0,0 +1,97 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ namespace Config
+ {
+ const uint DefaultListenPort = 8001;
+ const uint DefaultBandwidthLimit = 4;
+ const uint DefaultConnectionLimit = 64;
+ const bool DefaultFollowSymlinks = false;
+ const bool DefaultCustomErrors = false;
+ const bool DefaultPaused = false;
+ const QString& DefaultServername = QString::null;
+
+ static const char Name[] = "kpfappletrc";
+ static const char KeyServerRootList[] = "ServerRootList";
+ static const char KeyGroupPrefix[] = "Server_";
+ static const char KeyListenPort[] = "ListenPort";
+ static const char KeyBandwidthLimit[] = "BandwidthLimit";
+ static const char KeyConnectionLimit[] = "ConnectionLimit";
+ static const char KeyFollowSymlinks[] = "FollowSymlinks";
+ static const char KeyCustomErrors[] = "CustomErrors";
+ static const char KeyPaused[] = "Paused";
+ static const char KeyServerName[] = "ServerName";
+
+ QString name()
+ {
+ return QString::fromUtf8(Name);
+ }
+
+ QString key(Option o)
+ {
+ switch (o)
+ {
+ case ServerRootList:
+ return QString::fromUtf8(KeyServerRootList);
+
+ case GroupPrefix:
+ return QString::fromUtf8(KeyGroupPrefix);
+
+ case ListenPort:
+ return QString::fromUtf8(KeyListenPort);
+
+ case BandwidthLimit:
+ return QString::fromUtf8(KeyBandwidthLimit);
+
+ case ConnectionLimit:
+ return QString::fromUtf8(KeyConnectionLimit);
+
+ case FollowSymlinks:
+ return QString::fromUtf8(KeyFollowSymlinks);
+
+ case CustomErrors:
+ return QString::fromUtf8(KeyCustomErrors);
+
+ case Paused:
+ return QString::fromUtf8(KeyPaused);
+
+ case ServerName:
+ return QString::fromUtf8(KeyServerName);
+
+ /* default intentionally left out to have the compiler generate
+ * warnings in case we add values to the enumeration but forget
+ * to extend this switch.
+ */
+ }
+ return QString::null;
+ }
+ } // End namespace Config
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defaults.h b/kpf/src/Defaults.h
new file mode 100644
index 00000000..d49339fe
--- /dev/null
+++ b/kpf/src/Defaults.h
@@ -0,0 +1,71 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DEFAULTS_H
+#define KPF_DEFAULTS_H
+
+#include <qstring.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a single point of access to config defaults and key names.
+ */
+ namespace Config
+ {
+ extern const uint DefaultListenPort;
+ extern const uint DefaultBandwidthLimit;
+ extern const uint DefaultConnectionLimit;
+ extern const bool DefaultFollowSymlinks;
+ extern const bool DefaultCustomErrors;
+ extern const bool DefaultPaused;
+
+ enum Option
+ {
+ ServerRootList,
+ GroupPrefix,
+ ListenPort,
+ BandwidthLimit,
+ ConnectionLimit,
+ FollowSymlinks,
+ CustomErrors,
+ Paused,
+ ServerName
+ };
+
+ /**
+ * Name to be used for configuration file.
+ */
+ QString name();
+
+ /**
+ * Config key to use when accessing option.
+ */
+ QString key(Option);
+
+ } // End namespace Config
+
+} // End namespace KPF
+
+#endif // KPF_DEFAULTS_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Defines.h b/kpf/src/Defines.h
new file mode 100644
index 00000000..5c724e9a
--- /dev/null
+++ b/kpf/src/Defines.h
@@ -0,0 +1,33 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DEFINES_H
+#define KPF_DEFINES_H
+
+#include <kdebug.h>
+
+#define kpfDebug kdDebug(5007) << k_lineinfo << k_funcinfo << endl
+//#define kpfDebug kndDebug()
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirSelectWidget.cpp b/kpf/src/DirSelectWidget.cpp
new file mode 100644
index 00000000..bd36ffeb
--- /dev/null
+++ b/kpf/src/DirSelectWidget.cpp
@@ -0,0 +1,115 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qdir.h>
+#include <qfileinfo.h>
+
+#include "DirSelectWidget.h"
+#include "DirSelectWidget.moc"
+
+namespace KPF
+{
+ class DirSelectWidget::Private
+ {
+ public:
+
+ QString pathToMakeVisible;
+ };
+
+ DirSelectWidget::DirSelectWidget
+ (
+ const QString & pathToMakeVisible,
+ QWidget * parent,
+ const char * name
+ )
+ : KListView(parent, name)
+ {
+ d = new Private;
+ d->pathToMakeVisible = pathToMakeVisible;
+
+ setRootIsDecorated(true);
+
+ connect
+ (
+ this,
+ SIGNAL(expanded(QListViewItem *)),
+ SLOT(slotExpanded(QListViewItem *))
+ );
+
+ QListViewItem * root = new QListViewItem(this, "/");
+
+ root->setExpandable(true);
+
+ startTimer(0);
+ }
+
+ DirSelectWidget::~DirSelectWidget()
+ {
+ delete d;
+ }
+
+ void
+ DirSelectWidget::timerEvent(QTimerEvent *)
+ {
+ killTimers();
+
+ if (0 != firstChild())
+ firstChild()->setOpen(true);
+ }
+
+ void
+ DirSelectWidget::slotExpanded(QListViewItem * item)
+ {
+ if (0 != item->firstChild())
+ return;
+
+ QString p(path(item));
+
+ QDir dir(p);
+
+ const QFileInfoList * entryInfoList =
+ dir.entryInfoList(QDir::Dirs | QDir::Readable);
+
+ for (QFileInfoListIterator it(*entryInfoList); it.current(); ++it)
+ {
+ if (it.current()->isDir() && it.current()->isReadable())
+ {
+ QListViewItem * i = new QListViewItem(item, it.current()->fileName());
+ i->setExpandable(true);
+ }
+ }
+ }
+
+ QString
+ DirSelectWidget::path(QListViewItem * item) const
+ {
+ QString ret(item->text(0));
+
+ while (0 != (item = item->parent()))
+ ret.prepend("/" + item->text(0));
+
+ return ret;
+ }
+}
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirSelectWidget.h b/kpf/src/DirSelectWidget.h
new file mode 100644
index 00000000..1f4c9eaf
--- /dev/null
+++ b/kpf/src/DirSelectWidget.h
@@ -0,0 +1,66 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DIR_SELECT_WIDGET_H
+#define KPF_DIR_SELECT_WIDGET_H
+
+#include <klistview.h>
+
+namespace KPF
+{
+ /**
+ * Allows the user to choose a directory, with some restrictions.
+ */
+ class DirSelectWidget : public KListView
+ {
+ Q_OBJECT
+
+ public:
+
+ DirSelectWidget
+ (
+ const QString & pathToMakeVisible = "/",
+ QWidget * = 0,
+ const char * = 0
+ );
+
+ virtual ~DirSelectWidget();
+
+ protected slots:
+
+ void slotExpanded(QListViewItem * item);
+
+ protected:
+
+ void timerEvent(QTimerEvent *);
+ QString path(QListViewItem * item) const;
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif // KPF_DIR_SELECT_WIDGET_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirectoryLister.cpp b/kpf/src/DirectoryLister.cpp
new file mode 100644
index 00000000..bfd0127b
--- /dev/null
+++ b/kpf/src/DirectoryLister.cpp
@@ -0,0 +1,345 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <cmath>
+
+#include <qapplication.h>
+#include <qdir.h>
+#include <qstring.h>
+#include <qstylesheet.h>
+#include <qpalette.h>
+#include <qtextstream.h>
+#include <kglobalsettings.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kurl.h>
+
+#include "Defines.h"
+#include "DirectoryLister.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ class DirectoryLister::Private
+ {
+ public:
+
+ Private()
+ {
+ }
+ };
+
+ QString colorToCSS(const QColor &c)
+ {
+ return
+ "rgb("
+ + QString::number(c.red())
+ + ", "
+ + QString::number(c.green())
+ + ", "
+ + QString::number(c.blue())
+ + ")";
+ }
+
+ QByteArray buildHTML(const QString & title, const QString & body)
+ {
+ QPalette pal = qApp->palette();
+ QByteArray temp_string;
+ QTextStream html(temp_string, IO_WriteOnly);
+
+ html.setEncoding(QTextStream::UnicodeUTF8);
+
+ html
+ << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ << endl
+ << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
+ << endl
+ << "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
+ << endl
+ << "<html xmlns=\"http://www.w3.org/1999/xhtml\">"
+ << endl
+ << "\t<head>"
+ << endl
+ << "\t\t<title>"
+ << title
+ << "</title>"
+ << endl
+ << "<style type=\"text/css\">"
+ << endl
+ << "<!--"
+ << endl
+ << "table.filelist { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Background))
+ << "; "
+ << "border: thin outset; "
+ << "width: 100%; "
+ << "}"
+ << endl
+ << "td { "
+ << "margin: 0px; "
+ << "white-space: nowrap; "
+ << "}"
+ << endl
+ << "td.norm { "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Base))
+ << "; "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "}"
+ << endl
+ << "td.alt { "
+ << "background-color: "
+ << colorToCSS
+ (
+ KGlobalSettings::calculateAlternateBackgroundColor
+ (pal.color(QPalette::Normal, QColorGroup::Base))
+ )
+ << "; "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Foreground))
+ << "; "
+ << "}"
+ << endl
+ << "a { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Text))
+ << "; "
+ << "text-decoration: none; "
+ << "}"
+ << endl
+ << "th.listheading { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::ButtonText))
+ << "; "
+ << "background-color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Button))
+ << "; "
+ << "text-align: left; "
+ << "white-space: nowrap; "
+ << "border: thin outset; "
+ << "}"
+ << endl
+ << "a.direntry { "
+ << "font-weight: bold; "
+ << "}"
+ << endl
+ << "div.sizeentry { "
+ << "color: "
+ << colorToCSS(pal.color(QPalette::Normal, QColorGroup::Text))
+ << "; "
+ << "text-align: right; "
+ << "}"
+ << endl
+ << "-->"
+ << endl
+ << "</style>"
+ << endl
+ << "\t</head>"
+ << endl
+ << "\t<body>"
+ << endl
+ << body
+ << "\t</body>"
+ << endl
+ << "</html>"
+ << endl
+ ;
+
+ return temp_string;
+ }
+
+ QString prettySize(uint size)
+ {
+ QString suffix;
+ QString temp;
+ float floated_size;
+
+ if (size > 1023)
+ {
+ if (size > 1048575)
+ {
+ floated_size = size / 1048576.0;
+ suffix = i18n(" MB");
+ }
+ else
+ {
+ floated_size = size / 1024.0;
+ suffix = i18n(" KB");
+ }
+ }
+ else
+ {
+ temp.setNum(size);
+ temp += i18n(" bytes");
+ return temp;
+ }
+
+ temp.setNum(floated_size, 'f', 1);
+ temp += suffix;
+ return temp;
+ }
+
+ DirectoryLister * DirectoryLister::instance_ = 0L;
+
+ DirectoryLister *
+ DirectoryLister::instance()
+ {
+ if (0 == instance_)
+ instance_ = new DirectoryLister;
+
+ return instance_;
+ }
+
+ DirectoryLister::DirectoryLister()
+ {
+ d = new Private;
+ }
+
+ DirectoryLister::~DirectoryLister()
+ {
+ delete d;
+ }
+
+ QByteArray
+ DirectoryLister::html(const QString & root, const QString & _path)
+ {
+ kpfDebug << "root: " << root << " path: " << _path << endl;
+
+ QString path;
+
+ if (_path.right(1) != "/")
+ path = _path + "/";
+ else
+ path = _path;
+
+ if (path[0] == '/')
+ path + "";
+
+ QDir d(root + path);
+
+ if (!d.exists())
+ {
+ return buildHTML
+ (
+ i18n("Error"),
+ i18n("Directory does not exist: %1 %2").arg(root).arg(path)
+ );
+ }
+
+ const QFileInfoList * infoList =
+ d.entryInfoList(QDir::DefaultFilter, QDir::Name | QDir::DirsFirst);
+
+ if (0 == infoList)
+ {
+ return buildHTML
+ (
+ i18n("Error"),
+ i18n("Directory unreadable: %1 %2").arg(root).arg(path)
+ );
+ }
+
+ QString html;
+
+ html += "<table";
+ html += " width=\"100%\"";
+ html += " class=\"filelist\">\n";
+
+ html += "<tr>\n";
+ html += "<th align=\"left\" class=\"listheading\">Name</th>\n";
+ html += "<th align=\"left\" class=\"listheading\">Size</th>\n";
+ html += "</tr>\n";
+
+ for (QFileInfoListIterator it(*infoList); it.current(); ++it)
+ {
+ static int counter = 0;
+
+ QFileInfo * fi(it.current());
+
+ if (
+ (fi->fileName()[0] == '.')
+ && ((fi->fileName() != "..") || path == "/")
+ )
+ {
+ // Don't show hidden files
+ continue;
+ }
+
+ ++counter;
+
+ QString td_class = (counter % 2) ? "alt" : "norm";
+
+ html += "<tr>\n";
+
+ html += "<td class=\"" + td_class + "\">";
+
+ QString item_class = QString((fi->isDir()) ? "direntry" : "fileentry");
+
+ KURL fu(path+fi->fileName());
+ html +=
+ "<a href=\""
+ + fu.encodedPathAndQuery()
+ + (fi->isDir() ? "/" : "")
+ + "\" class=\""
+ + item_class
+ + "\">";
+
+ if (fi->fileName() != "..")
+ html += QStyleSheet::escape(fi->fileName());
+ else
+ html += i18n("Parent Directory");
+
+ html += "</a>";
+
+ if (fi->isDir())
+ html += "/";
+
+ html += "</td>\n";
+
+ html += "<td class=\"" + td_class + "\">";
+
+ if (!fi->isDir())
+ html
+ += "<div class=\"sizeentry\">" + prettySize(fi->size()) + "</div>";
+
+ html += "</td>\n";
+ html += "</tr>\n";
+ }
+
+ html += "</table>\n";
+
+ return buildHTML
+ (
+ i18n("Directory listing for %1").arg(QStyleSheet::escape(path)),
+ html
+ );
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/DirectoryLister.h b/kpf/src/DirectoryLister.h
new file mode 100644
index 00000000..e1809202
--- /dev/null
+++ b/kpf/src/DirectoryLister.h
@@ -0,0 +1,72 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_DIRECTORY_LISTER_H
+#define KPF_DIRECTORY_LISTER_H
+
+#include <qstring.h>
+#include <qcstring.h>
+
+namespace KPF
+{
+ /**
+ * Creates an HTML document by listing the contents of a directory.
+ */
+ class DirectoryLister
+ {
+ public:
+
+ /**
+ * @return a pointer to the single instance of DirectoryLister.
+ */
+ static DirectoryLister * instance();
+
+ virtual ~DirectoryLister();
+
+ /**
+ * Get a directory listing (HTML) for the specified path. Uses
+ * cache if directory has not been modified since last read.
+ */
+ QByteArray html(const QString &root, const QString & path);
+
+ uint headerLength() const;
+ uint footerLength() const;
+ uint emptyEntryLength() const;
+
+ private:
+
+ /**
+ * Constructs a directory listing object.
+ */
+ DirectoryLister();
+
+ static DirectoryLister * instance_;
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ErrorMessageConfigDialog.cpp b/kpf/src/ErrorMessageConfigDialog.cpp
new file mode 100644
index 00000000..2f7cb43a
--- /dev/null
+++ b/kpf/src/ErrorMessageConfigDialog.cpp
@@ -0,0 +1,154 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "ErrorMessageConfigDialog.h"
+#include "ErrorMessageConfigDialog.moc"
+
+#include <qlabel.h>
+#include <qframe.h>
+#include <qlayout.h>
+
+#include <kurlrequester.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kdialog.h>
+
+#include "Defines.h"
+#include "Defaults.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ ErrorMessageConfigDialog::ErrorMessageConfigDialog
+ (
+ WebServer * webServer,
+ QWidget * parent
+ )
+ : KDialogBase
+ (
+ parent,
+ "ErrorMessageConfigDialog",
+ false,
+ i18n("Configure error messages"),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ true // Use a separator.
+ ),
+ server_(webServer)
+ {
+ QValueList<uint> codeList;
+
+ codeList << 400 << 403 << 404 << 412 << 416 << 500 << 501;
+
+ QFrame * w = makeMainWidget();
+
+ QVBoxLayout * layout =
+ new QVBoxLayout(w, KDialog::marginHint(), KDialog::spacingHint());
+
+ QLabel * info =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>Here you may select files to use instead of the default error"
+ " messages passed to a client.</p>"
+ "<p>The files may contain anything you wish, but by convention"
+ " you should report the error code and the English version of"
+ " the error message (e.g. \"Bad request\"). Your file should"
+ " also be valid HTML.</p>"
+ "<p>The strings ERROR_MESSAGE, ERROR_CODE and RESOURCE, if"
+ " they exist in the file, will be replaced with the English"
+ " error message, the numeric error code and the path of the"
+ " requested resource, respectively.</p>"
+ ),
+ w
+ );
+
+ layout->addWidget(info);
+
+ QGridLayout * grid = new QGridLayout(layout, codeList.count(), 2);
+
+ QString pattern(i18n("%1 %2"));
+
+ KConfig config(Config::name());
+
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QValueList<uint>::ConstIterator it;
+
+ for (it = codeList.begin(); it != codeList.end(); ++it)
+ {
+ QString originalPath =
+ config.readPathEntry(QString::number(*it));
+
+ QString responseName(translatedResponseName(*it));
+
+ KURLRequester * requester = new KURLRequester(originalPath, w);
+
+ itemList_.append(new Item(*it, requester, responseName, originalPath));
+
+ QLabel * l = new QLabel(pattern.arg(*it).arg(responseName), w);
+
+ l->setBuddy(requester);
+
+ grid->addWidget(l, *it, 0);
+ grid->addWidget(requester, *it, 1);
+ }
+ }
+
+ ErrorMessageConfigDialog::~ErrorMessageConfigDialog()
+ {
+ itemList_.setAutoDelete(true);
+ itemList_.clear();
+ }
+
+ void
+ ErrorMessageConfigDialog::slotURLRequesterTextChanged(const QString &)
+ {
+ }
+
+ void
+ ErrorMessageConfigDialog::accept()
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QPtrListIterator<Item> it(itemList_);
+
+ for (; it.current(); ++it)
+ {
+ config.writePathEntry
+ (
+ QString::number(it.current()->code),
+ it.current()->urlRequester->url()
+ );
+ }
+
+ config.sync();
+
+ KDialogBase::accept();
+ }
+}
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ErrorMessageConfigDialog.h b/kpf/src/ErrorMessageConfigDialog.h
new file mode 100644
index 00000000..51bf4830
--- /dev/null
+++ b/kpf/src/ErrorMessageConfigDialog.h
@@ -0,0 +1,88 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ERROR_MESSAGE_CONFIG_DIALOG_H
+#define KPF_ERROR_MESSAGE_CONFIG_DIALOG_H
+
+#include <qmap.h>
+#include <kdialogbase.h>
+
+class KURLRequester;
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Currently unused pending implementation.
+ */
+ class ErrorMessageConfigDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ ErrorMessageConfigDialog(WebServer *, QWidget * parent);
+
+ virtual ~ErrorMessageConfigDialog();
+
+ protected slots:
+
+ void slotURLRequesterTextChanged(const QString &);
+
+ protected:
+
+ void accept();
+
+ private:
+
+ WebServer * server_;
+
+ /**
+ * Provides a graphical interface to allow the user to pick a file
+ * to be used when reporting an error code.
+ */
+ class Item
+ {
+ public:
+
+ Item(uint i, KURLRequester * r, QString s, QString p)
+ : code (i),
+ urlRequester (r),
+ report (s),
+ originalPath (p)
+ {
+ }
+
+ uint code;
+ KURLRequester * urlRequester;
+ QString report;
+ QString originalPath;
+ };
+
+ QPtrList<Item> itemList_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Help.cpp b/kpf/src/Help.cpp
new file mode 100644
index 00000000..19c6ae26
--- /dev/null
+++ b/kpf/src/Help.cpp
@@ -0,0 +1,62 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Help.h"
+#include <klocale.h>
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ namespace HelpText
+ {
+
+ QString getServerNameHelp()
+ {
+ switch(DNSSD::ServiceBrowser::isAvailable()) {
+ case DNSSD::ServiceBrowser::Working:
+ return i18n("<p>Specify the name that will be used when announcing"
+ " this server on network.</p>");
+ case DNSSD::ServiceBrowser::Stopped:
+ return i18n("<p>The Zeroconf daemon is not running. See the Handbook"
+ " for more information.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ case DNSSD::ServiceBrowser::Unsupported:
+ return i18n("<p>Zeroconf support is not available in this version of KDE."
+ " See the Handbook for more information.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ default:
+ return i18n("<p>Unknown error with Zeroconf.<br/>"
+ "Other users will not see this system when browsing"
+ " the network via zeroconf, but sharing will still work.</p>");
+ }
+ }
+
+
+ } // End namespace HelpText
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Help.h b/kpf/src/Help.h
new file mode 100644
index 00000000..12584213
--- /dev/null
+++ b/kpf/src/Help.h
@@ -0,0 +1,44 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_HELP_H
+#define KPF_HELP_H
+
+#include <qstring.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a single point of access to config defaults and key names.
+ */
+ namespace HelpText
+ {
+
+ QString getServerNameHelp();
+
+ } // End namespace HelpText
+
+} // End namespace KPF
+
+#endif // KPF_DEFAULTS_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/KPFInterface.cpp b/kpf/src/KPFInterface.cpp
new file mode 100644
index 00000000..d12c751d
--- /dev/null
+++ b/kpf/src/KPFInterface.cpp
@@ -0,0 +1,130 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "WebServer.h"
+#include "WebServerManager.h"
+#include "KPFInterface.h"
+
+KPFInterface::KPFInterface()
+ : DCOPObject("KPFInterface")
+{
+ // Empty.
+}
+
+KPFInterface::~KPFInterface()
+{
+ // Empty.
+}
+
+ QStringList
+KPFInterface::serverRootList()
+{
+ QList<KPF::WebServer> l(KPF::WebServerManager::instance()->serverListLocal());
+
+ QStringList ret;
+
+ for (QListIterator<KPF::WebServer> it(l); it.current(); ++it)
+ ret << it.current()->root();
+
+ return ret;
+}
+
+ bool
+KPFInterface::createServer
+(
+ QString root,
+ uint port,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks
+)
+{
+ kpfDebug << "KPFInterface::createServer(" << root << ", " <<
+ port << ", " << bandwidthLimit << ", " << connectionLimit << ", "
+ << (followSymlinks ? "true" : "false") << ")" << endl;
+
+ KPF::WebServer * s =
+ KPF::WebServerManager::instance()->createServer
+ (
+ root,
+ port,
+ bandwidthLimit,
+ connectionLimit,
+ followSymlinks
+ );
+
+ if (0 == s)
+ {
+ kpfDebug << "KPFInterface::createServer(): failed" << endl;
+ return false;
+ }
+ else
+ {
+ kpfDebug << "KPFInterface::createServer(): ok" << endl;
+ return true;
+ }
+}
+
+ bool
+KPFInterface::disableServer(QString root)
+{
+ kpfDebug << "KPFInterface::disableServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->disableServer(root);
+}
+
+ bool
+KPFInterface::restartServer(QString root)
+{
+ kpfDebug << "KPFInterface::restartServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->restartServer(root);
+}
+
+ bool
+KPFInterface::reconfigureServer(QString root)
+{
+ kpfDebug << "KPFInterface::reconfigureServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->reconfigureServer(root);
+}
+
+ bool
+KPFInterface::pauseServer(QString root)
+{
+ kpfDebug << "KPFInterface::pauseServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->pauseServer(root, true);
+}
+
+ bool
+KPFInterface::unpauseServer(QString root)
+{
+ kpfDebug << "KPFInterface::unpauseServer(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->pauseServer(root, false);
+}
+
+ bool
+KPFInterface::serverPaused(QString root)
+{
+ kpfDebug << "KPFInterface::serverPaused(" << root << ")" << endl;
+ return KPF::WebServerManager::instance()->serverPaused(root);
+}
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/KPFInterface.h b/kpf/src/KPFInterface.h
new file mode 100644
index 00000000..facd482c
--- /dev/null
+++ b/kpf/src/KPFInterface.h
@@ -0,0 +1,68 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_INTERFACE_H
+#define KPF_INTERFACE_H
+
+#include <dcopobject.h>
+
+#include "Defaults.h"
+
+/**
+ * DCOP interface to kpf.
+ */
+class KPFInterface : virtual public DCOPObject
+{
+ K_DCOP
+
+ public:
+
+ KPFInterface ();
+ ~KPFInterface ();
+
+ k_dcop:
+
+ /**
+ * @return list of root directories used by WebServer objects.
+ */
+ virtual QStringList serverRootList();
+
+ virtual bool createServer
+ (
+ QString root,
+ uint port,
+ uint bandwidthLimit = KPF::Config::DefaultBandwidthLimit,
+ uint connectionLimit = KPF::Config::DefaultConnectionLimit,
+ bool followSymlinks = KPF::Config::DefaultFollowSymlinks
+ );
+
+ virtual bool disableServer (QString root);
+ virtual bool restartServer (QString root);
+ virtual bool reconfigureServer (QString root);
+ virtual bool pauseServer (QString root);
+ virtual bool unpauseServer (QString root);
+ virtual bool serverPaused (QString root);
+};
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Makefile.am b/kpf/src/Makefile.am
new file mode 100644
index 00000000..5d419f0f
--- /dev/null
+++ b/kpf/src/Makefile.am
@@ -0,0 +1,89 @@
+INCLUDES = $(all_includes)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = kpf_panelapplet.la kpfpropertiesdialog.la
+
+kpf_panelapplet_la_SOURCES = \
+ Utils.cpp \
+ DirectoryLister.cpp \
+ ByteRange.cpp \
+ DirSelectWidget.cpp \
+ PortValidator.cpp \
+ Request.cpp \
+ Response.cpp \
+ Resource.cpp \
+ RootValidator.cpp \
+ Server.cpp \
+ ServerPrivate.cpp \
+ ServerSocket.cpp \
+ WebServer.cpp \
+ WebServer.skel \
+ WebServer.stub \
+ WebServerSocket.cpp \
+ WebServerManager.cpp \
+ WebServerManager.skel \
+ SingleServerConfigDialog.cpp \
+ System.cpp \
+ ConfigDialogPage.cpp \
+ ErrorMessageConfigDialog.cpp \
+ ActiveMonitor.cpp \
+ ActiveMonitorItem.cpp \
+ ActiveMonitorWindow.cpp \
+ BandwidthGraph.cpp \
+ ServerWizard.cpp \
+ AppletItem.cpp \
+ Applet.cpp \
+ Defaults.cpp \
+ Help.cpp
+
+kpf_panelapplet_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+
+kpf_panelapplet_la_LIBADD = $(LIB_KIO) -lkdnssd
+
+kpfpropertiesdialog_la_SOURCES = \
+ PropertiesDialogPlugin.cpp \
+ StartingKPFDialog.cpp \
+ WebServer.stub \
+ WebServerManager.stub \
+ Defaults.cpp \
+ Help.cpp
+
+kpfpropertiesdialog_la_LIBADD = $(LIB_KIO) -lkdnssd
+
+kpfpropertiesdialog_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+
+noinst_HEADERS = \
+ Utils.h \
+ Defaults.h \
+ DirectoryLister.h \
+ DirSelectWidget.h \
+ ByteRange.h \
+ PortValidator.h \
+ Request.h \
+ Response.h \
+ Resource.h \
+ RootValidator.h \
+ Server.h \
+ ServerPrivate.h \
+ ServerSocket.h \
+ WebServer.h \
+ WebServerSocket.h \
+ WebServerManager.h \
+ SingleServerConfigDialog.h \
+ ConfigDialogPage.h \
+ PropertiesDialogPlugin.h \
+ StartingKPFDialog.h \
+ ErrorMessageConfigDialog.h \
+ ActiveMonitor.h \
+ ActiveMonitorItem.h \
+ ActiveMonitorWindow.h \
+ BandwidthGraph.h \
+ ServerWizard.h \
+ AppletItem.h \
+ Applet.h \
+ Help.h
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kpf.pot
+
diff --git a/kpf/src/PortValidator.cpp b/kpf/src/PortValidator.cpp
new file mode 100644
index 00000000..01c2074e
--- /dev/null
+++ b/kpf/src/PortValidator.cpp
@@ -0,0 +1,57 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "PortValidator.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+
+namespace KPF
+{
+ PortValidator::PortValidator(QObject * parent, const char * name)
+ : QValidator(parent, name)
+ {
+ // Empty.
+ }
+
+ QValidator::State
+ PortValidator::validate(QString & input, int & /* unused */) const
+ {
+ uint port(input.toUInt());
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == port)
+ {
+ return Intermediate;
+ }
+ }
+
+ return Valid;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PortValidator.h b/kpf/src/PortValidator.h
new file mode 100644
index 00000000..c86d0d01
--- /dev/null
+++ b/kpf/src/PortValidator.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_PORT_VALIDATOR_H
+#define KPF_PORT_VALIDATOR_H
+
+#include <qvalidator.h>
+
+namespace KPF
+{
+ /**
+ * Used for checking that a port input by the user is not the same as
+ * one used by an existing server.
+ */
+ class PortValidator : public QValidator
+ {
+ public:
+
+ PortValidator(QObject * parent, const char * name = 0);
+
+ virtual State validate(QString & input, int & pos) const;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_PORT_VALIDATOR_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PropertiesDialogPlugin.cpp b/kpf/src/PropertiesDialogPlugin.cpp
new file mode 100644
index 00000000..e86743e3
--- /dev/null
+++ b/kpf/src/PropertiesDialogPlugin.cpp
@@ -0,0 +1,890 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qwhatsthis.h>
+#include <qpushbutton.h>
+#include <qwidgetstack.h>
+#include <qtimer.h>
+#include <qdir.h>
+#include <qlineedit.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <dcopclient.h>
+#include <kdialogbase.h>
+#include <kmessagebox.h>
+#include <kurl.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kseparator.h>
+#include <kgenericfactory.h>
+
+#include "Defines.h"
+#include "Defaults.h"
+#include "PropertiesDialogPlugin.h"
+#include "StartingKPFDialog.h"
+#include "WebServerManager_stub.h"
+#include "WebServer_stub.h"
+#include "Help.h"
+
+#include <dnssd/servicebrowser.h>
+
+namespace KPF
+{
+ class ServerState
+ {
+ public:
+
+ ServerState()
+ : shared (false),
+ listenPort (Config::DefaultListenPort),
+ bandwidthLimit (Config::DefaultBandwidthLimit),
+// connectionLimit (Config::DefaultConnectionLimit),
+ followSymlinks (Config::DefaultFollowSymlinks)
+ {
+ }
+
+ bool operator == (const ServerState & other) const
+ {
+ return
+ (
+ other.shared == shared
+ &&
+ other.listenPort == listenPort
+ &&
+ other.bandwidthLimit == bandwidthLimit
+ &&
+// other.connectionLimit == connectionLimit
+// &&
+ other.followSymlinks == followSymlinks
+ );
+ }
+
+ bool operator != (const ServerState & other) const
+ {
+ return
+ (
+ other.shared != shared
+ ||
+ other.listenPort != listenPort
+ ||
+ other.bandwidthLimit != bandwidthLimit
+ ||
+// other.connectionLimit != connectionLimit
+// ||
+ other.followSymlinks != followSymlinks
+ );
+ }
+
+
+ bool shared;
+ uint listenPort;
+ uint bandwidthLimit;
+// uint connectionLimit;
+ QString serverName;
+ bool followSymlinks;
+ };
+
+ class PropertiesDialogPlugin::Private
+ {
+ public:
+
+ Private()
+ : l_listenPort (0L),
+ l_bandwidthLimit (0L),
+// l_connectionLimit (0L),
+ sb_listenPort (0L),
+ sb_bandwidthLimit (0L),
+// sb_connectionLimit (0L),
+ le_serverName (0L),
+ cb_followSymlinks (0L),
+ cb_share (0L),
+ stack (0L),
+ initWidget (0L),
+ configWidget (0L),
+ webServerManagerInterface (0L),
+ kpfRunning (false)
+ {
+ }
+
+ QLabel * l_listenPort;
+ QLabel * l_bandwidthLimit;
+// QLabel * l_connectionLimit;
+ QLabel * l_serverName;
+ QLabel * l_kpfStatus;
+
+ QSpinBox * sb_listenPort;
+ QSpinBox * sb_bandwidthLimit;
+// QSpinBox * sb_connectionLimit;
+ QLineEdit * le_serverName;
+
+ QCheckBox * cb_followSymlinks;
+ QCheckBox * cb_share;
+
+ QPushButton * pb_startKPF;
+
+ QWidgetStack * stack;
+ QWidget * initWidget;
+ QWidget * configWidget;
+
+ WebServerManager_stub * webServerManagerInterface;
+
+ bool kpfRunning;
+ DCOPRef webServerRef;
+ KURL url;
+
+ ServerState currentState;
+ ServerState wantedState;
+ };
+
+ PropertiesDialogPlugin::PropertiesDialogPlugin(KPropertiesDialog * dialog,
+ const char *,
+ const QStringList &)
+ : KPropsDlgPlugin(dialog)
+ {
+ d = new Private;
+
+ d->webServerManagerInterface =
+ new WebServerManager_stub("kpf", "WebServerManager");
+
+ d->url = dialog->kurl();
+
+ if (
+ d->url == QDir::homeDirPath()
+ || d->url == "file:" + QDir::homeDirPath()
+ )
+ {
+ // Don't even show ourselves if it's the home dir
+ return;
+ }
+
+ QWidget * widget = dialog->addPage(i18n("&Sharing"));
+
+ d->stack = new QWidgetStack(widget);
+
+ QVBoxLayout * stackLayout = new QVBoxLayout(widget);
+ stackLayout->addWidget(d->stack);
+
+ d->initWidget = createInitWidget(d->stack);
+ d->configWidget = createConfigWidget(d->stack);
+
+ d->stack->addWidget(d->initWidget, 0);
+ d->stack->addWidget(d->configWidget, 1);
+
+ kapp->dcopClient()->setNotifications(true);
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRegistered(const QCString &)),
+ SLOT(slotApplicationRegistered(const QCString &))
+ );
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRemoved(const QCString &)),
+ SLOT(slotApplicationUnregistered(const QCString &))
+ );
+
+ d->kpfRunning = kapp->dcopClient()->isApplicationRegistered("kpf");
+
+ if (!d->kpfRunning)
+ {
+ d->stack->raiseWidget(d->initWidget);
+ }
+ else
+ {
+ getServerRef();
+ updateGUIFromCurrentState();
+ d->stack->raiseWidget(d->configWidget);
+ }
+ }
+
+ PropertiesDialogPlugin::~PropertiesDialogPlugin()
+ {
+ delete d->webServerManagerInterface;
+ d->webServerManagerInterface = 0;
+
+ delete d;
+ d = 0;
+ }
+
+ void
+ PropertiesDialogPlugin::slotSharingToggled(bool b)
+ {
+ if (b)
+ {
+ if (!userAcceptsWarning())
+ {
+ // Avoid loop.
+ d->cb_share->blockSignals(true);
+ d->cb_share->setChecked(false);
+ d->cb_share->blockSignals(false);
+ b = false;
+ }
+ }
+
+ setControlsEnabled(b);
+ }
+
+ void
+ PropertiesDialogPlugin::setControlsEnabled(bool b)
+ {
+
+ bool canPublish = b && DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+
+ d->l_serverName->setEnabled(canPublish);
+ d->l_listenPort ->setEnabled(b);
+ d->l_bandwidthLimit ->setEnabled(b);
+// d->l_connectionLimit ->setEnabled(b);
+ d->l_serverName ->setEnabled(canPublish);
+
+ d->sb_listenPort ->setEnabled(b);
+ d->sb_bandwidthLimit ->setEnabled(b);
+// d->sb_connectionLimit ->setEnabled(b);
+ d->le_serverName ->setEnabled(canPublish);
+ d->cb_followSymlinks ->setEnabled(b);
+ }
+
+ QWidget *
+ PropertiesDialogPlugin::createInitWidget(QWidget * parent)
+ {
+ QWidget * w = new QWidget(parent);
+
+ QLabel * about =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>To share files via the web, you need to be"
+ " running an 'applet' in your KDE panel. This"
+ " 'applet' is a small program which provides"
+ " file sharing capabilities."
+ "</p>"
+ ),
+ w
+ );
+
+ d->pb_startKPF
+ = new QPushButton(i18n("Start Applet"), w);
+
+ QVBoxLayout * l = new QVBoxLayout(w);
+
+ l->addWidget(about);
+
+ d->l_kpfStatus =
+ new QLabel(i18n("Applet status: <strong>not running</strong>"), w);
+
+ l->addWidget(d->l_kpfStatus);
+
+ QHBoxLayout * l2 = new QHBoxLayout(l);
+
+ l2->addStretch(1);
+ l2->addWidget(d->pb_startKPF);
+
+ l->addStretch(1);
+
+ connect(d->pb_startKPF, SIGNAL(clicked()), SLOT(slotStartKPF()));
+
+ return w;
+ }
+
+ QWidget *
+ PropertiesDialogPlugin::createConfigWidget(QWidget * parent)
+ {
+ QWidget * w = new QWidget(parent);
+
+ d->cb_share =
+ new QCheckBox(i18n("Share this directory on the &Web"), w);
+
+ d->l_listenPort = new QLabel(i18n("&Listen port:"), w);
+ d->l_bandwidthLimit = new QLabel(i18n("&Bandwidth limit:"), w);
+// d->l_connectionLimit = new QLabel(i18n("Connection &limit"), w);
+ d->l_serverName = new QLabel(i18n("&Server name:"), w);
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ d->l_serverName->setEnabled(canPublish);
+
+ d->sb_listenPort = new QSpinBox(1000, 999999, 1, w);
+ d->sb_bandwidthLimit = new QSpinBox(1, 999999, 1, w);
+// d->sb_connectionLimit = new QSpinBox(1, 9999, 1, w);
+ d->le_serverName = new QLineEdit( w);
+ d->le_serverName->setEnabled(canPublish);
+
+ d->cb_followSymlinks =
+ new QCheckBox(i18n("&Follow symbolic links"), w);
+
+ d->l_listenPort ->setBuddy(d->sb_listenPort);
+ d->l_serverName ->setBuddy(d->le_serverName);
+ d->l_bandwidthLimit ->setBuddy(d->sb_bandwidthLimit);
+// d->l_connectionLimit ->setBuddy(d->sb_connectionLimit);
+
+ d->sb_listenPort ->setValue(Config::DefaultListenPort);
+ d->sb_bandwidthLimit ->setValue(Config::DefaultBandwidthLimit);
+ d->sb_bandwidthLimit ->setSuffix(i18n("kB/s"));
+// d->sb_connectionLimit ->setValue(Config::DefaultConnectionLimit);
+ d->cb_followSymlinks ->setChecked(Config::DefaultFollowSymlinks);
+
+ QVBoxLayout * l0 =
+ new QVBoxLayout(w, KDialog::marginHint(), KDialog::spacingHint());
+
+ l0->addWidget(d->cb_share);
+
+ l0->addWidget(new KSeparator(QFrame::HLine, w));
+
+ QGridLayout * l2 = new QGridLayout(l0);
+
+ l2->addWidget(d->l_listenPort, 0, 0);
+ l2->addWidget(d->sb_listenPort, 0, 1);
+ l2->addWidget(d->l_bandwidthLimit, 1, 0);
+ l2->addWidget(d->sb_bandwidthLimit, 1, 1);
+// l2->addWidget(d->l_connectionLimit, 2, 0);
+// l2->addWidget(d->sb_connectionLimit, 2, 1);
+ l2->addWidget(d->l_serverName, 2, 0);
+ l2->addWidget(d->le_serverName, 2, 1);
+
+ l0->addWidget(d->cb_followSymlinks);
+
+ l0->addStretch(1);
+
+ QString shareHelp =
+ i18n
+ (
+ "<p>"
+ "Setting this option makes all files in this directory and"
+ " any subdirectories available for reading to anyone"
+ " who wishes to view them."
+ "</p>"
+ "<p>"
+ "To view your files, a web browser or similar program"
+ " may be used."
+ "</p>"
+ "<p>"
+ "<strong>Warning!</strong> Before sharing a directory,"
+ " you should be sure that it does not contain sensitive"
+ " information, such as passwords, company secrets, your"
+ " addressbook, etc."
+ "</p>"
+ "<p>"
+ "Note that you cannot share your home directory"
+ " (%1)"
+ "</p>"
+ )
+ .arg(QDir::homeDirPath());
+
+ QString listenPortHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ );
+
+ QString bandwidthLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ );
+
+ QString connectionLimitHelp =
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ );
+
+ QString followSymlinksHelp =
+ i18n
+ (
+ "<p>"
+ "Allow serving of files which have a symbolic link in"
+ " the path from / to the file, or are a symbolic link"
+ " themselves."
+ "</p>"
+ "<p>"
+ "<strong>Warning!</strong> This could be a security"
+ " risk. Use only if you understand the issues involved."
+ "</p>"
+ );
+ QString serverNameHelp = KPF::HelpText::getServerNameHelp();
+
+ QWhatsThis::add(d->cb_share, shareHelp);
+ QWhatsThis::add(d->l_listenPort, listenPortHelp);
+ QWhatsThis::add(d->sb_listenPort, listenPortHelp);
+ QWhatsThis::add(d->l_bandwidthLimit, bandwidthLimitHelp);
+ QWhatsThis::add(d->sb_bandwidthLimit, bandwidthLimitHelp);
+// QWhatsThis::add(d->l_connectionLimit, connectionLimitHelp);
+// QWhatsThis::add(d->sb_connectionLimit, connectionLimitHelp);
+ QWhatsThis::add(d->l_serverName, serverNameHelp);
+ QWhatsThis::add(d->le_serverName, serverNameHelp);
+ QWhatsThis::add(d->cb_followSymlinks, followSymlinksHelp);
+
+ connect(d->cb_share, SIGNAL(toggled(bool)), SLOT(slotSharingToggled(bool)));
+
+ slotSharingToggled(false);
+
+ connect
+ (
+ d->cb_share,
+ SIGNAL(toggled(bool)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->sb_listenPort,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->sb_bandwidthLimit,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+
+#if 0
+ connect
+ (
+ d->sb_connectionLimit,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotChanged())
+ );
+#endif
+ connect
+ (
+ d->le_serverName,
+ SIGNAL(textChanged(const QString&)),
+ SLOT(slotChanged())
+ );
+
+ connect
+ (
+ d->cb_followSymlinks,
+ SIGNAL(toggled(bool)),
+ SLOT(slotChanged())
+ );
+
+ return w;
+ }
+
+ void
+ PropertiesDialogPlugin::slotStartKPF()
+ {
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>starting...</strong>"));
+
+ kapp->dcopClient()
+ ->send("kicker", "default", "addApplet(QString)", "kpfapplet.desktop");
+
+ QTimer::singleShot(4 * 1000, this, SLOT(slotStartKPFFailed()));
+ }
+
+ void
+ PropertiesDialogPlugin::slotStartKPFFailed()
+ {
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>failed to start</strong>"));
+
+ d->pb_startKPF->setEnabled(true);
+ }
+
+ void
+ PropertiesDialogPlugin::slotApplicationRegistered(const QCString & s)
+ {
+ if ("kpf" == s)
+ {
+ d->kpfRunning = true;
+
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>running</strong>"));
+
+ d->pb_startKPF->setEnabled(false);
+
+ getServerRef();
+ updateGUIFromCurrentState();
+ d->stack->raiseWidget(d->configWidget);
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::slotApplicationUnregistered(const QCString & s)
+ {
+ if ("kpf" == s)
+ {
+ d->kpfRunning = false;
+
+ d->webServerRef.clear();
+
+ d->pb_startKPF->setEnabled(true);
+
+ d->l_kpfStatus
+ ->setText(i18n("Applet status: <strong>not running</strong>"));
+
+ d->stack->raiseWidget(d->initWidget);
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::readSettings()
+ {
+ d->currentState = ServerState();
+
+ if (!d->kpfRunning || d->webServerRef.isNull())
+ return;
+
+ d->currentState.shared = true;
+
+ WebServer_stub webServer(d->webServerRef.app(), d->webServerRef.object());
+
+ d->currentState.listenPort = webServer.listenPort();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.listenPort = Config::DefaultListenPort;
+ return;
+ }
+
+ d->currentState.bandwidthLimit = webServer.bandwidthLimit();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.bandwidthLimit = Config::DefaultBandwidthLimit;
+ return;
+ }
+
+#if 0
+ d->currentState.connectionLimit = webServer.connectionLimit();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.connectionLimit = Config::DefaultConnectionLimit;
+ return;
+ }
+#endif
+
+ d->currentState.serverName = webServer.serverName();
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.serverName = "";
+ return;
+ }
+
+
+ d->currentState.followSymlinks = webServer.followSymlinks();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: warn user ?
+ kpfDebug << "WebServer_stub call failed" << endl;
+ d->currentState.followSymlinks = Config::DefaultFollowSymlinks;
+ return;
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::getServerRef()
+ {
+ QValueList<DCOPRef> serverRefList =
+ d->webServerManagerInterface->serverList();
+
+ if (DCOPStub::CallFailed == d->webServerManagerInterface->status())
+ {
+ // TODO: warn
+ kpfDebug << "webServerManagerInterface.serverList call failed" << endl;
+ return;
+ }
+
+ d->webServerRef.clear();
+
+ QValueList<DCOPRef>::ConstIterator it(serverRefList.begin());
+
+ for (; it != serverRefList.end(); ++it)
+ {
+ DCOPRef serverRef(*it);
+
+ WebServer_stub webServer(serverRef.app(), serverRef.object());
+
+ if (webServer.root() == d->url.path())
+ {
+ d->webServerRef = serverRef;
+ break;
+ }
+ }
+ }
+
+ bool
+ PropertiesDialogPlugin::userAcceptsWarning() const
+ {
+ QString noWarningKey("DoNotWarnAboutSharingDirectoriesViaHTTP");
+
+ KConfig * config(KGlobal::config());
+
+ if (config->readBoolEntry(noWarningKey, false))
+ return true;
+
+ return
+ (
+ KMessageBox::Continue
+ ==
+ KMessageBox::warningContinueCancel
+ (
+ d->configWidget,
+ i18n(
+ "<p>"
+ "Before you share a directory, be <strong>absolutely"
+ " certain</strong> that it does not contain sensitive"
+ " information."
+ "</p>"
+ "<p>"
+ "Sharing a directory makes all information"
+ " in that directory <strong>and all subdirectories</strong>"
+ " available to <strong>anyone</strong> who wishes to read it."
+ "</p>"
+ "<p>"
+ "If you have a system administrator, please ask for permission"
+ " before sharing a directory in this way."
+ "</p>"
+ ),
+ i18n("Warning - Sharing Sensitive Information?"),
+ i18n("&Share Directory"),
+ noWarningKey,
+ true
+ )
+ );
+ }
+
+ void
+ PropertiesDialogPlugin::slotChanged()
+ {
+ kpfDebug << "PropertiesDialogPlugin::slotChanged" << endl;
+ readSettings();
+ updateWantedStateFromGUI();
+
+ setDirty(d->currentState != d->wantedState);
+ kpfDebug << "Dirty: " << isDirty() << endl;
+ emit(changed());
+ }
+
+ void
+ PropertiesDialogPlugin::applyChanges()
+ {
+ readSettings();
+ updateWantedStateFromGUI();
+
+ enum Action
+ {
+ None,
+ Enable,
+ Disable,
+ Reconfigure
+ };
+
+ bool needRestart = false;
+
+ Action action = None;
+
+ if (!d->currentState.shared && d->wantedState.shared)
+ {
+// kpfDebug << "Not shared, but want to be. Action is Enable" << endl;
+ action = Enable;
+ }
+ else if (d->currentState.shared && !d->wantedState.shared)
+ {
+// kpfDebug << "Shared, but don't want to be. Action is Disable" << endl;
+ action = Disable;
+ }
+ else if
+ (
+ d->currentState.listenPort != d->wantedState.listenPort
+ ||
+ d->currentState.bandwidthLimit != d->wantedState.bandwidthLimit
+ ||
+// d->currentState.connectionLimit != d->wantedState.connectionLimit
+// ||
+ d->currentState.serverName != d->wantedState.serverName
+ ||
+ d->currentState.followSymlinks != d->wantedState.followSymlinks
+ )
+ {
+// kpfDebug << "Config changed. Action is Reconfigure" << endl;
+ action = Reconfigure;
+
+ if (d->currentState.listenPort != d->wantedState.listenPort)
+ needRestart = true;
+ }
+
+ if (None == action)
+ {
+// kpfDebug << "Nothing changed. Action = None" << endl;
+ return;
+ }
+
+ switch (action)
+ {
+ case Enable:
+ {
+ DCOPRef ref =
+ d->webServerManagerInterface->createServer
+ (
+ d->url.path(),
+ d->wantedState.listenPort,
+ d->wantedState.bandwidthLimit,
+ Config::DefaultConnectionLimit,//d->wantedState.connectionLimit,
+ d->wantedState.followSymlinks,
+ d->wantedState.serverName
+ );
+
+ if (ref.isNull())
+ {
+ // TODO: Warn user.
+ kpfDebug
+ << "kpf refused to create server - warn user here !" << endl;
+ break;
+ }
+ else
+ {
+ d->webServerRef = ref;
+ }
+ }
+ break;
+
+ case Disable:
+ if (d->webServerRef.isNull())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Disable, but d->webServerRef is null" << endl;
+ }
+ else
+ {
+ d->webServerManagerInterface->disableServer(d->webServerRef);
+ }
+ break;
+
+ case Reconfigure:
+
+ if (d->webServerRef.isNull())
+ {
+ kpfDebug << "Need restart, but d->webServerRef is null" << endl;
+ }
+ else
+ {
+ WebServer_stub webServer
+ (d->webServerRef.app(), d->webServerRef.object());
+
+ webServer.set
+ (
+ d->wantedState.listenPort,
+ d->wantedState.bandwidthLimit,
+ Config::DefaultConnectionLimit,//d->wantedState.connectionLimit,
+ d->wantedState.followSymlinks,
+ d->wantedState.serverName
+ );
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Reconfigure failed" << endl;
+ }
+
+ if (needRestart)
+ {
+ webServer.restart();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ // TODO: Warn user.
+ kpfDebug << "Restart failed" << endl;
+ }
+ }
+ }
+ break;
+
+ default:
+ kpfDebug << "Code error in KPF::PropertiesDialogPlugin." << endl;
+ break;
+ }
+ }
+
+ void
+ PropertiesDialogPlugin::updateGUIFromCurrentState()
+ {
+ readSettings();
+
+ // We don't want slotSharingToggled to be called.
+ d->cb_share->blockSignals(true);
+ d->cb_share->setChecked(d->currentState.shared);
+ d->cb_share->blockSignals(false);
+
+ d->sb_listenPort ->setValue (d->currentState.listenPort);
+ d->sb_bandwidthLimit ->setValue (d->currentState.bandwidthLimit);
+// d->sb_connectionLimit ->setValue (d->currentState.connectionLimit);
+ d->le_serverName ->setText (d->currentState.serverName);
+ d->cb_followSymlinks ->setChecked (d->currentState.followSymlinks);
+
+ setControlsEnabled(d->currentState.shared);
+ }
+
+ void
+ PropertiesDialogPlugin::updateWantedStateFromGUI()
+ {
+ d->wantedState.shared = d->cb_share->isChecked();
+ d->wantedState.listenPort = d->sb_listenPort->value();
+ d->wantedState.bandwidthLimit = d->sb_bandwidthLimit->value();
+// d->wantedState.connectionLimit = d->sb_connectionLimit->value();
+ d->wantedState.serverName = d->le_serverName->text();
+ d->wantedState.followSymlinks = d->cb_followSymlinks->isChecked();
+ }
+
+ typedef KGenericFactory<PropertiesDialogPlugin, KPropertiesDialog> PropertiesDialogPluginFactory;
+}
+
+K_EXPORT_COMPONENT_FACTORY( kpfpropertiesdialog,
+ KPF::PropertiesDialogPluginFactory( "kpf" ) )
+
+#include "PropertiesDialogPlugin.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/PropertiesDialogPlugin.h b/kpf/src/PropertiesDialogPlugin.h
new file mode 100644
index 00000000..5881080a
--- /dev/null
+++ b/kpf/src/PropertiesDialogPlugin.h
@@ -0,0 +1,82 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_PROPERTIES_DIALOG_PLUGIN_H
+#define KPF_PROPERTIES_DIALOG_PLUGIN_H
+
+#include <kpropertiesdialog.h>
+
+namespace KPF
+{
+ /**
+ * Provides an implementation of KPropsDlgPlugin which is plugged into
+ * Konqueror's directory properties dialog. Allows creating and configuring
+ * WebServer objects via DCOP conversations with the KPFInterface. Also
+ * allows starting the kpf applet if it is not already loaded by kicker.
+ */
+ class PropertiesDialogPlugin : public KPropsDlgPlugin
+ {
+ Q_OBJECT
+
+ public:
+
+ PropertiesDialogPlugin(KPropertiesDialog *, const char *, const QStringList &);
+
+ virtual ~PropertiesDialogPlugin();
+
+ virtual void applyChanges();
+
+ protected slots:
+
+ void slotSharingToggled(bool);
+ void slotStartKPF();
+ void slotStartKPFFailed();
+
+ void slotApplicationRegistered(const QCString &);
+ void slotApplicationUnregistered(const QCString &);
+
+ void slotChanged();
+
+ protected:
+
+ void getServerRef();
+
+ void updateGUIFromCurrentState();
+ void updateWantedStateFromGUI();
+
+ QWidget * createInitWidget(QWidget * parent);
+ QWidget * createConfigWidget(QWidget * parent);
+
+ void readSettings();
+ void setControlsEnabled(bool);
+ bool userAcceptsWarning() const;
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Request.cpp b/kpf/src/Request.cpp
new file mode 100644
index 00000000..01735363
--- /dev/null
+++ b/kpf/src/Request.cpp
@@ -0,0 +1,377 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <climits> // For ULONG_MAX
+
+#include <qregexp.h>
+#include <kurl.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "Request.h"
+
+namespace KPF
+{
+ Request::Request()
+ : protocolMajor_ (0),
+ protocolMinor_ (9),
+ method_ (Unsupported),
+ haveHost_ (false),
+ haveIfModifiedSince_ (false),
+ haveIfUnmodifiedSince_ (false),
+ expectContinue_ (false),
+ haveRange_ (false),
+ persist_ (false)
+ {
+ }
+
+ Request::~Request()
+ {
+ }
+
+ void
+ Request::parseHeaders(const QStringList & buf)
+ {
+ for (QStringList::ConstIterator it(buf.begin()); it != buf.end(); ++it)
+ {
+ QString line(*it);
+
+ int colonPos = line.find(':');
+
+ if (-1 != colonPos)
+ {
+ QString name(line.left(colonPos).stripWhiteSpace().lower());
+ QString value(line.mid(colonPos + 1).stripWhiteSpace());
+ handleHeader(name, value);
+ }
+ }
+ }
+
+ void
+ Request::handleHeader(const QString & name, const QString & value)
+ {
+ if ("host" == name)
+ {
+ setHost(value);
+ }
+ if ("range" == name)
+ {
+ setRange(value);
+ }
+ else if ("if-modified-since" == name)
+ {
+ QDateTime dt;
+
+ if (parseDate(value, dt))
+ setIfModifiedSince(dt);
+ }
+ else if ("if-unmodified-since" == name)
+ {
+ QDateTime dt;
+
+ if (parseDate(value, dt))
+ setIfUnmodifiedSince(dt);
+ }
+ else if ("connection" == name)
+ {
+ QString v(value.lower());
+
+ if ("keep-alive" == v)
+ {
+ setPersist(true);
+ }
+ else if ("close" == v)
+ {
+ setPersist(false);
+ }
+ }
+ }
+
+ void
+ Request::setProtocol(const QString & _s)
+ {
+ QString s(_s);
+
+ s.remove(0, 5);
+
+ int dotPos = s.find('.');
+
+ if (-1 != dotPos)
+ {
+ protocolMajor_ = s.left(dotPos).toUInt();
+ protocolMinor_ = s.mid(dotPos + 1).toUInt();
+ }
+ }
+
+ void
+ Request::setProtocol(uint major, uint minor)
+ {
+ protocolMajor_ = major;
+ protocolMinor_ = minor;
+ }
+
+ void
+ Request::setMethod(const QString & s)
+ {
+ if ("GET" == s)
+ method_ = Get;
+ else if ("HEAD" == s)
+ method_ = Head;
+ else
+ method_ = Unsupported;
+ }
+
+ void
+ Request::setMethod(Method m)
+ {
+ method_ = m;
+ }
+
+ void
+ Request::setPath(const QString & s)
+ {
+ KURL p(s);
+ path_ = clean(p.path());
+
+#if 0
+ if ('/' == path_.at(path_.length() - 1))
+ {
+ path_.append("index.html");
+ }
+#endif
+ }
+
+ void
+ Request::setHost(const QString & s)
+ {
+ host_ = s;
+ haveHost_ = true;
+ }
+
+ void
+ Request::setIfModifiedSince(const QDateTime & dt)
+ {
+ ifModifiedSince_ = dt;
+ haveIfModifiedSince_ = true;
+ }
+
+ void
+ Request::setIfUnmodifiedSince(const QDateTime & dt)
+ {
+ ifUnmodifiedSince_ = dt;
+ haveIfUnmodifiedSince_ = true;
+ }
+
+ uint
+ Request::protocolMajor() const
+ {
+ return protocolMajor_;
+ }
+
+ uint
+ Request::protocolMinor() const
+ {
+ return protocolMinor_;
+ }
+
+ float
+ Request::protocol() const
+ {
+ return (float(protocolMajor_) + float(protocolMinor_) / 10.0);
+ }
+
+ Request::Method
+ Request::method() const
+ {
+ return method_;
+ }
+
+ bool
+ Request::haveHost() const
+ {
+ return haveHost_;
+ }
+
+ bool
+ Request::haveIfModifiedSince() const
+ {
+ return haveIfModifiedSince_;
+ }
+
+ bool
+ Request::haveIfUnmodifiedSince() const
+ {
+ return haveIfUnmodifiedSince_;
+ }
+
+ QString
+ Request::path() const
+ {
+ return path_;
+ }
+
+ QString
+ Request::host() const
+ {
+ return host_;
+ }
+
+ QDateTime
+ Request::ifModifiedSince() const
+ {
+ return ifModifiedSince_;
+ }
+
+ QDateTime
+ Request::ifUnmodifiedSince() const
+ {
+ return ifUnmodifiedSince_;
+ }
+
+ QCString
+ Request::protocolString() const
+ {
+ QCString s("HTTP/");
+ s += QCString().setNum(protocolMajor_);
+ s += '.';
+ s += QCString().setNum(protocolMinor_);
+ return s;
+ }
+
+ void
+ Request::setExpectContinue(bool b)
+ {
+ expectContinue_ = b;
+ }
+
+ bool
+ Request::expectContinue() const
+ {
+ return expectContinue_;
+ }
+
+ void
+ Request::setRange(const QString & s)
+ {
+ kpfDebug << "Request::setRange(`" << s << "')" << endl;
+
+ haveRange_ = true;
+
+ ByteRangeList l(s, protocol());
+
+ ulong first (ULONG_MAX);
+ ulong last (0L);
+ bool haveLast (false);
+
+ for (ByteRangeList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ ByteRange r(*it);
+ first = min(r.first(), first);
+
+ if (r.haveLast())
+ {
+ haveLast = true;
+ last = max(r.last(), last);
+ }
+ }
+
+ kpfDebug << "Request::setRange(): first == " << first << "d" << endl;
+
+ range_.setFirst(first);
+
+ if (haveLast)
+ {
+ kpfDebug << "Request::setRange(): last == " << last << "d" << endl;
+ range_.setLast(last);
+ }
+ kpfDebug << "Request::setRange(): no last" << endl;
+ }
+
+ ByteRange
+ Request::range() const
+ {
+ return range_;
+ }
+
+ bool
+ Request::haveRange() const
+ {
+ return haveRange_;
+ }
+
+ void
+ Request::setPersist(bool b)
+ {
+ if (protocol() > 1.0) // Bad, but makes wget work.
+ {
+ persist_ = b;
+ }
+ }
+
+ bool
+ Request::persist() const
+ {
+ return persist_;
+ }
+
+ void
+ Request::clear()
+ {
+ protocolMajor_ = 0;
+ protocolMinor_ = 9;
+ method_ = Unsupported;
+ haveHost_ = false;
+ haveIfModifiedSince_ = false;
+ haveIfUnmodifiedSince_ = false;
+ expectContinue_ = false;
+ haveRange_ = false;
+ persist_ = false;
+ path_ = QString::null;
+ host_ = QString::null;
+ ifModifiedSince_ = QDateTime();
+ ifUnmodifiedSince_ = QDateTime();
+ range_.clear();
+ }
+
+ QString
+ Request::clean(const QString & _path) const
+ {
+ QString s(_path);
+
+ while (s.endsWith("/./"))
+ s.truncate(s.length() - 2);
+
+ while (s.endsWith("/."))
+ s.truncate(s.length() - 1);
+
+ // Double slash -> slash.
+
+ QRegExp r("\\/\\/+");
+ s.replace(r, "/");
+
+ return s;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Request.h b/kpf/src/Request.h
new file mode 100644
index 00000000..92034c9c
--- /dev/null
+++ b/kpf/src/Request.h
@@ -0,0 +1,252 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_REQUEST_H
+#define KPF_REQUEST_H
+
+#include <qcstring.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+
+#include "ByteRange.h"
+
+namespace KPF
+{
+ /**
+ * Represents an HTTP request.
+ */
+ class Request
+ {
+ public:
+
+ /**
+ * HTTP/1.1 specifies many request types, known as 'methods'.
+ * An HTTP/1.1 compliant server must implement HEAD and GET.
+ * We support only these two. All others are rejected either
+ * because they are of limited use or because they are a security
+ * risk.
+ */
+ enum Method { Head, Get, Unsupported };
+
+ /**
+ * Construct a Request object and set parameters to defaults.
+ */
+ Request();
+ virtual ~Request();
+
+ /**
+ * Take a list of headers and parse them, setting our values
+ * appropriately.
+ */
+ void parseHeaders(const QStringList &);
+
+ /**
+ * Parse one header line and set whatever value is appropriate.
+ */
+ void handleHeader(const QString & name, const QString & value);
+
+ /**
+ * HTTP has had a few revisions (protocols) since birth. Here you may
+ * specify the protocol which you believe the client is using. This
+ * version of setProtocol parses a string of the form
+ * "HTTP/major.minor", where major and minor are integers.
+ */
+ void setProtocol (const QString &);
+
+ /**
+ * HTTP has had a few revisions (protocols) since birth. Here you may
+ * specify the protocol which you believe the client is using.
+ */
+ void setProtocol (uint major, uint minor);
+
+ /**
+ * Specify the 'method' requested by the client. This version parses
+ * a string.
+ */
+ void setMethod (const QString &);
+
+ /**
+ * Specify the 'method' requested by the client.
+ */
+ void setMethod (Method);
+
+ /**
+ * Set the path to the requested resource. The path is
+ * immediately decoded and canonicalised.
+ */
+ void setPath (const QString &);
+
+ /**
+ * HTTP/1.1 requests must have a "Host:" header.
+ */
+ void setHost (const QString &);
+
+ /**
+ * HTTP/1.1 allows an "If-Modified-Since" header, specifying that
+ * the requested resource should only be retrieved if the resource
+ * has been modified since a certain date and time.
+ */
+ void setIfModifiedSince (const QDateTime &);
+
+ /**
+ * HTTP/1.1 allows an "If-Unmodified-Since" header, specifying that
+ * the requested resource should only be retrieved if the resource
+ * has NOT been modified since a certain date and time.
+ */
+ void setIfUnmodifiedSince (const QDateTime &);
+
+ /**
+ * HTTP/1.1 allows an "Expect: 100-continue" header, specifying
+ * that the server should immediately respond with "100 Continue".
+ */
+ void setExpectContinue (bool);
+
+ /**
+ * Specify a range of bytes which should be retrieved from the
+ * resource. See RFC 2616.
+ */
+ void setRange (const QString &);
+
+ /**
+ * HTTP/1.1 allows "persistent" connections. These may be specified
+ * by "Keep-Alive:" or "Connection: Keep-Alive" headers.
+ */
+ void setPersist (bool);
+
+ /**
+ * @return major version of protocol which was set.
+ */
+ uint protocolMajor() const;
+
+ /**
+ * @return minor version of protocol which was set.
+ */
+ uint protocolMinor() const;
+
+ /**
+ * @return version of protocol which was set, as major.minor, e.g.
+ * if the protocol was set to HTTP/0.9, this would return 0.9.
+ */
+ float protocol() const;
+
+ /**
+ * @return the (enumerated) request type ('method').
+ */
+ Method method() const;
+
+ /**
+ * @return true if @ref setHost was called previously.
+ */
+ bool haveHost() const;
+
+ /**
+ * @return true if @ref setIfModifiedSince was called previously.
+ */
+ bool haveIfModifiedSince() const;
+
+ /**
+ * @return true if @ref setIfUnmodifiedSince was called previously.
+ */
+ bool haveIfUnmodifiedSince() const;
+
+ /**
+ * @return the path that was set with (and modified by) setPath()
+ */
+ QString path() const;
+
+ /**
+ * @return the host that was set previously.
+ */
+ QString host() const;
+
+ /**
+ * @return the date/time set by @ref setIfModifiedSince previously.
+ */
+ QDateTime ifModifiedSince() const;
+
+ /**
+ * @return the date/time set by @ref setIfUnmodifiedSince previously.
+ */
+ QDateTime ifUnmodifiedSince() const;
+
+ /**
+ * @return the protocol as a string, e.g. "HTTP/1.1".
+ */
+ QCString protocolString() const;
+
+ /**
+ * @return true if @ref setExpectContinue was used previously.
+ */
+ bool expectContinue() const;
+
+ /**
+ * @return byte range set by @ref setRange previously.
+ */
+ ByteRange range() const;
+
+ /**
+ * @return true if @ref setRange was called previously.
+ */
+ bool haveRange() const;
+
+ /**
+ * @return true if @ref setPersist was called previously.
+ */
+ bool persist() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ /**
+ * Clean up the path given in a request, so it's more like what we
+ * expect.
+ */
+ QString clean(const QString & path) const;
+
+ private:
+
+ // Order dependency
+ uint protocolMajor_;
+ uint protocolMinor_;
+ Method method_;
+ bool haveHost_;
+ bool haveIfModifiedSince_;
+ bool haveIfUnmodifiedSince_;
+ bool expectContinue_;
+ bool haveRange_;
+ bool persist_;
+ // End order dependency
+
+ QString path_;
+ QString host_;
+ QDateTime ifModifiedSince_;
+ QDateTime ifUnmodifiedSince_;
+ ByteRange range_;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Resource.cpp b/kpf/src/Resource.cpp
new file mode 100644
index 00000000..d5e77072
--- /dev/null
+++ b/kpf/src/Resource.cpp
@@ -0,0 +1,346 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qstringlist.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <kglobal.h>
+
+#include <kmimetype.h>
+
+#include "Utils.h"
+#include "Defines.h"
+#include "Resource.h"
+#include "DirectoryLister.h"
+
+namespace KPF
+{
+ enum FileType { Dir, File };
+
+ class Resource::Private
+ {
+ public:
+
+ Private()
+ : size(0),
+ sizeCalculated(false),
+ offset(0)
+ {
+ }
+
+ QString root;
+ FileType fileType;
+ QString path;
+ QFile file;
+ QFileInfo fileInfo;
+ QDir dir;
+ uint size;
+ bool sizeCalculated;
+ uint offset;
+
+ QByteArray html;
+ };
+
+ Resource::Resource()
+ {
+ d = new Private;
+ }
+
+ Resource::~Resource()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ Resource::setPath(const QString & root, const QString & relativePath)
+ {
+ kpfDebug << "setPath(`" << root << "',`" << relativePath << "'" << endl;
+
+ d->root = root;
+ d->path = relativePath;
+ d->size = 0;
+ d->offset = 0;
+ d->sizeCalculated = false;
+
+ d->file.close();
+
+ // Fix root if it doesn't have a trailing slash.
+
+ if ('/' != d->root.at(d->root.length() - 1))
+ d->root += '/';
+
+ if (d->path.right(1) == "/")
+ {
+ // A directory was requested
+ kpfDebug << "Directory requested" << endl;
+
+ // Does the path actually point to a directory ?
+
+ if (QFileInfo(d->root + d->path).isDir())
+ {
+ kpfDebug << "Path points to directory" << endl;
+
+ // Does an index.html exist in that directory ?
+
+ if (QFileInfo(d->root + d->path + "index.html").exists())
+ {
+ kpfDebug << "Found index.html" << endl;
+
+ // Ok, add `index.html'.
+
+ d->path += "index.html";
+ }
+ else
+ {
+ kpfDebug << "NOT Found index.html" << endl;
+ }
+
+ }
+ else
+ {
+ kpfDebug << "NOT Path points to directory" << endl;
+ }
+ }
+ else
+ {
+ kpfDebug << "NOT Directory requested" << endl;
+ }
+
+ kpfDebug << "QFileInfo::setFile(`" << d->root << "' + `" << d->path << "'" << endl;
+ d->fileInfo.setFile(d->root + d->path);
+ }
+
+ bool
+ Resource::open()
+ {
+ if (!d->fileInfo.exists())
+ {
+ kpfDebug << "File doesn't exist" << endl;
+ return false;
+ }
+
+ if (d->fileInfo.isDir())
+ {
+ d->fileType = Dir;
+ d->dir.setPath(d->root + d->path);
+
+ if (!d->dir.isReadable())
+ {
+ kpfDebug << "Dir isn't readable" << endl;
+ return false;
+ }
+ else
+ {
+ generateHTML();
+ }
+ }
+ else
+ {
+ d->fileType = File;
+ d->file.setName(d->root + d->path);
+
+ if (!d->file.open(IO_ReadOnly))
+ {
+ kpfDebug << "File isn't readable" << endl;
+ return false;
+ }
+ }
+
+ calculateSize();
+ return true;
+ }
+
+ void
+ Resource::close()
+ {
+ if (File == d->fileType)
+ d->file.close();
+ }
+
+ bool
+ Resource::seek(int pos)
+ {
+ if (File == d->fileType)
+ {
+ return d->file.at(pos);
+ }
+ else
+ {
+ // TODO STUB
+ return false;
+ }
+ }
+
+ int
+ Resource::readBlock(char * data, uint maxlen)
+ {
+ int bytesRead(0);
+
+ if (File == d->fileType)
+ {
+ bytesRead = d->file.readBlock(data, maxlen);
+ }
+ else
+ {
+ if (d->offset < d->size)
+ {
+ uint bytesAvailable = QMIN(maxlen, d->size - d->offset);
+
+ memcpy(data, d->html.data() + d->offset, bytesAvailable);
+
+ d->offset += bytesAvailable;
+
+ return bytesAvailable;
+ }
+ else
+ {
+ // Else bytesRead is still 0, because the read was out of bounds.
+
+ kpfDebug << "Out of bounds in html" << endl;
+ }
+ }
+
+ return bytesRead;
+ }
+
+ uint
+ Resource::size() const
+ {
+ return d->size;
+ }
+
+ int
+ Resource::at() const
+ {
+ return d->offset;
+ }
+
+ bool
+ Resource::atEnd() const
+ {
+ if (File == d->fileType)
+ {
+ return d->file.atEnd();
+ }
+ else
+ {
+ return d->offset >= d->size;
+ }
+ }
+
+ void
+ Resource::calculateSize()
+ {
+ if (File == d->fileType)
+ {
+ d->size = d->fileInfo.size();
+ }
+ else
+ {
+ d->size = d->html.size() - 1;
+ }
+ }
+
+
+ bool
+ Resource::readable() const
+ {
+ return d->fileInfo.isReadable();
+ }
+
+ QDateTime
+ Resource::lastModified() const
+ {
+ return d->fileInfo.lastModified();
+ }
+
+ bool
+ Resource::exists() const
+ {
+ bool b = d->fileInfo.exists();
+
+ if (!b)
+ {
+ kpfDebug << "File doesn't exist" << endl;
+ }
+
+ return b;
+ }
+
+ bool
+ Resource::symlink() const
+ {
+ if (d->fileInfo.isSymLink())
+ return true;
+
+ QString path(d->fileInfo.dirPath());
+
+ QStringList l(QStringList::split('/', path));
+
+ QString testPath;
+
+ for (QStringList::ConstIterator it(l.begin()); it != l.end(); ++it)
+ {
+ testPath += '/';
+ testPath += *it;
+
+ if (QFileInfo(testPath).isSymLink())
+ return true;
+ }
+
+ return false;
+ }
+
+ bool
+ Resource::seekable() const
+ {
+ return !(d->fileInfo.isDir());
+ }
+
+ QString
+ Resource::mimeType() const
+ {
+ if (d->fileInfo.isDir())
+ return "text/html; charset=utf-8";
+ return KMimeType::findByPath( d->root + d->path )->name();
+ }
+
+ void
+ Resource::generateHTML()
+ {
+ d->html = DirectoryLister::instance()->html(d->root, d->path);
+ }
+
+ void
+ Resource::clear()
+ {
+ delete d;
+ d = new Private;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Resource.h b/kpf/src/Resource.h
new file mode 100644
index 00000000..870daed7
--- /dev/null
+++ b/kpf/src/Resource.h
@@ -0,0 +1,149 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_RESOURCE_H
+#define KPF_RESOURCE_H
+
+#include <qstring.h>
+#include <qdatetime.h>
+
+namespace KPF
+{
+ /**
+ * Provide any resource the client requests. This may be a file, a
+ * directory listing, or nothing at all, depending on the flabbiness
+ * of the client's arse.
+ */
+ class Resource
+ {
+ public:
+
+ /**
+ * Default ctor, object unusable until you setPath().
+ */
+ Resource();
+
+ /**
+ * Closes all open files.
+ */
+ virtual ~Resource();
+
+ /**
+ * Reset this object and tell it what the new paths are.
+ */
+ void setPath(const QString & root, const QString & relativePath);
+
+ /**
+ * @return true if the file was opened ok or the dir was readable.
+ */
+ bool open();
+
+ /**
+ * Just close.
+ */
+ void close();
+
+ /**
+ * Seek to the specified position.
+ * @return false if this is a dir.
+ */
+ bool seek(int);
+
+ /**
+ * Read a block of the file or the generated HTML.
+ */
+ int readBlock(char * data, uint maxlen);
+
+ /**
+ * @return false if the file or directory doesn't exist.
+ */
+ bool exists() const;
+
+ /**
+ * Performs a search through the entire path, looking for symbolic links.
+ *
+ * Expensive !
+ *
+ * @return true if the path contains a symbolic link.
+ */
+ bool symlink() const;
+
+ /**
+ * @return true if the resource is readable.
+ */
+ bool readable() const;
+
+ /**
+ * @return mtime of resource.
+ */
+ QDateTime lastModified() const;
+
+ /**
+ * @return size of file, or size of HTML that will be generated.
+ */
+ uint size() const;
+
+ /**
+ * @return current file position.
+ */
+ int at() const;
+
+ /**
+ * @return true if nothing left to read.
+ */
+ bool atEnd() const;
+
+ /**
+ * @return true if file, false if dir. Perhaps I'll make the HTML
+ * seekable later.
+ */
+ bool seekable() const;
+
+ /**
+ * @return mime type of file if available. If dir, returns text/html.
+ * If nothing available, returns text/plain.
+ */
+ QString mimeType() const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ private:
+
+ /**
+ * Update d->size;
+ */
+ void calculateSize();
+
+ void generateHTML();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Response.cpp b/kpf/src/Response.cpp
new file mode 100644
index 00000000..f9ce46d8
--- /dev/null
+++ b/kpf/src/Response.cpp
@@ -0,0 +1,194 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include <kconfig.h>
+
+#include "Defines.h"
+#include "Utils.h"
+#include "Response.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ Response::Response()
+ : code_(0),
+ size_(0)
+ {
+ // Empty.
+ }
+
+ void
+ Response::setCode(uint code)
+ {
+ code_ = code;
+ }
+
+ void
+ Response::setSize(ulong size)
+ {
+ size_ = size;
+ }
+
+ Response::~Response()
+ {
+ // Empty.
+ }
+
+ bool
+ Response::valid() const
+ {
+ return 0 != code_;
+ }
+
+ ulong
+ Response::size() const
+ {
+ return size_;
+ }
+
+ uint
+ Response::code() const
+ {
+ return code_;
+ }
+
+ QCString
+ Response::text(const Request & request) const
+ {
+ QString s;
+
+ // XXX: Ensure that all codes we know about are enumerated here.
+ switch (code_)
+ {
+ case 200:
+ case 206:
+ case 304:
+ if (request.protocol() >= 1.0)
+ {
+ s = QString(request.protocolString())
+ + QString(" %1 %2\r\n").arg(code_).arg(responseName(code_));
+ }
+ break;
+
+ case 400:
+ case 403:
+ case 404:
+ case 412:
+ case 416:
+ case 500:
+ case 501:
+ case 505:
+ s = QString(request.protocolString())
+ + QString(" %1 %2\r\n").arg(code_).arg(responseName(code_))
+ + data(code_, request);
+ break;
+
+ default:
+ kpfDebug << "Huh ?" << endl;
+ break;
+ }
+
+ return s.utf8();
+ }
+
+ QString
+ Response::data(uint code, const Request & request) const
+ {
+ QString contentType = "Content-Type: text/html; charset=utf-8\r\n";
+
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QString html;
+
+ if
+ (
+ config.readBoolEntry
+ (Config::key(Config::CustomErrors), Config::DefaultCustomErrors)
+ )
+ {
+ config.setGroup("ErrorMessageOverrideFiles");
+
+ QString filename = config.readPathEntry(QString::number(code));
+
+ if (!filename.isEmpty())
+ {
+ QFile f(filename);
+
+ if (f.open(IO_ReadOnly))
+ {
+ QRegExp regexpMessage ("ERROR_MESSAGE");
+ QRegExp regexpCode ("ERROR_CODE");
+ QRegExp regexpResource ("RESOURCE");
+
+ QTextStream str(&f);
+
+ while (!str.atEnd())
+ {
+ QString line(str.readLine());
+
+ line.replace(regexpMessage, responseName(code));
+ line.replace(regexpCode, QString::number(code));
+ line.replace(regexpResource, request.path());
+
+ html = line + "\r\n";
+ }
+ }
+ }
+ }
+ else
+ {
+ html = "<html>\r\n";
+ html += "<head>\r\n";
+ html += "<title>\r\n" + responseName(code) + "</title>\r\n";
+ html += "<style type=\"text/css\">\r\n";
+ html += "BODY { color: black; background-color: rgb(228, 228, 228); }\r\n";
+ html += "H1 { font-size: 1.7em; color: rgb(60, 85, 110); }\r\n";
+ html += "P { margin: 40px, 40px, 10px, 10px; }\r\n";
+ html += "</style>\r\n";
+ html += "</head>\r\n";
+ html += "<body>\r\n<h1>\r\nError: " + responseName(code) + "\r\n</h1>\r\n";
+ html += "<p>Requested resource: " + request.path() + "</p>\r\n";
+ html += "</body>\r\n</html>\r\n";
+ }
+
+ QString contentLength =
+ QString("Content-Length: %1\r\n").arg(html.length());
+
+ return (contentType + contentLength + "\r\n" + html);
+ }
+
+ void
+ Response::clear()
+ {
+ code_ = size_ = 0;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Response.h b/kpf/src/Response.h
new file mode 100644
index 00000000..c24ba69c
--- /dev/null
+++ b/kpf/src/Response.h
@@ -0,0 +1,102 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_RESPONSE_H
+#define KPF_RESPONSE_H
+
+#include "Request.h"
+
+namespace KPF
+{
+ /**
+ * Represents some of the data which is used as a reponse to an
+ * HTTP request.
+ */
+ class Response
+ {
+ public:
+
+ /**
+ *
+ */
+ Response();
+
+ /**
+ *
+ */
+ virtual ~Response();
+
+ /**
+ * Each response has a code. See the HTTP specification.
+ */
+ void setCode(uint);
+
+ /**
+ * Set the size, in bytes, of the resource that will be transferred
+ * to the client.
+ */
+ void setSize(ulong);
+
+ /**
+ * @return true if code isn't 0.
+ */
+ bool valid() const;
+
+ /**
+ * @return size of requested resource.
+ */
+ ulong size() const;
+
+ /**
+ * @return HTTP response code.
+ */
+ uint code() const;
+
+ /**
+ * @return header/body data to send to the client. This string is
+ * constructed differently depending on HTTP response code.
+ */
+ QCString text(const Request &) const;
+
+ /**
+ * Reset to initial state.
+ */
+ void clear();
+
+ protected:
+
+ /**
+ * @internal
+ * Create HTML.
+ */
+ QString data(uint, const Request &) const;
+
+ private:
+
+ uint code_;
+ uint size_;
+ };
+
+} // End namespace KPF
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/RootValidator.cpp b/kpf/src/RootValidator.cpp
new file mode 100644
index 00000000..17d6a43a
--- /dev/null
+++ b/kpf/src/RootValidator.cpp
@@ -0,0 +1,66 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qfileinfo.h>
+#include <qdir.h>
+
+#include "RootValidator.h"
+#include "WebServerManager.h"
+
+namespace KPF
+{
+ RootValidator::RootValidator(QObject * parent, const char * name)
+ : QValidator(parent, name)
+ {
+ }
+
+ QValidator::State
+ RootValidator::validate(QString & input, int & /* unused */) const
+ {
+ QString root(input);
+
+ if ('/' == root.at(root.length() - 1))
+ {
+ root.truncate(root.length() - 1);
+ }
+
+ // Duplicate ?
+
+ if (0 != WebServerManager::instance()->server(root))
+ return Intermediate;
+
+ QFileInfo fi(root);
+
+ if (!fi.isDir())
+ return Intermediate;
+
+// Disabling disallowing of ~, on request.
+// if (fi.dirPath() == QDir::homeDirPath())
+// return Intermediate;
+
+ return Acceptable;
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/RootValidator.h b/kpf/src/RootValidator.h
new file mode 100644
index 00000000..5ac96829
--- /dev/null
+++ b/kpf/src/RootValidator.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_ROOT_VALIDATOR_H
+#define KPF_ROOT_VALIDATOR_H
+
+#include <qvalidator.h>
+
+namespace KPF
+{
+ /**
+ * Used for checking that a path input by the user is acceptable
+ * as a server root directory.
+ */
+ class RootValidator : public QValidator
+ {
+ public:
+
+ RootValidator(QObject * parent, const char * name = 0);
+
+ virtual State validate(QString & input, int & pos) const;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_ROOT_VALIDATOR_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Server.cpp b/kpf/src/Server.cpp
new file mode 100644
index 00000000..3e59281a
--- /dev/null
+++ b/kpf/src/Server.cpp
@@ -0,0 +1,1137 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "DirectoryLister.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "ServerPrivate.h"
+#include "Utils.h"
+
+#undef KPF_TRAFFIC_DEBUG
+
+namespace KPF
+{
+ static const uint IncomingDataLimit = 8 * 1024; // kB.
+ static const uint Timeout = 60 * 1000; // seconds.
+ static const uint MaxKeepAlive = 20; // transactions.
+
+ Server::Server
+ (
+ const QString & dir,
+ bool followSymlinks,
+ int socket,
+ WebServer * parent
+ )
+ : QObject(parent, "Server")
+ {
+ d = new Private;
+
+ kpfDebug << "New server: " << d->id << endl;
+
+ d->dir = dir;
+
+ d->followSymlinks = followSymlinks;
+
+ d->birth = QDateTime::currentDateTime();
+
+ d->socket.setSocket(socket);
+
+ connect(&(d->socket), SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
+
+ connect
+ (
+ &(d->socket),
+ SIGNAL(bytesWritten(int)),
+ SLOT(slotBytesWritten(int))
+ );
+
+ connect
+ (
+ &(d->socket),
+ SIGNAL(connectionClosed()),
+ SLOT(slotConnectionClosed())
+ );
+
+ connect
+ (
+ &(d->idleTimer),
+ SIGNAL(timeout()),
+ SLOT(slotTimeout())
+ );
+
+ connect
+ (
+ &(d->readTimer),
+ SIGNAL(timeout()),
+ SLOT(slotRead())
+ );
+
+ // If nothing happens for a bit, cancel ourselves.
+
+ d->idleTimer.start(Timeout, true);
+ }
+
+ Server::~Server()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ Server::slotReadyRead()
+ {
+ kpfDebug << d->id << ":slotReadyRead" << endl;
+
+ // DoS protection.
+
+ d->dataRead += static_cast<uint>(d->socket.bytesAvailable());
+
+ if (d->dataRead > IncomingDataLimit)
+ {
+ kpfDebug
+ << d->id
+ << ": Read would breach limit. Assuming DoS -> finished"
+ << endl;
+
+ setFinished(NoFlush /* Don't bother flushing socket */);
+ return;
+ }
+
+ // Reset idle timer.
+
+ d->idleTimer.start(Timeout, true);
+
+ // Read all available data to incomingLineBuffer.
+
+ while (d->socket.canReadLine())
+ {
+ kpfDebug << d->id << ": socket.canReadLine" << endl;
+
+ QString line(d->socket.readLine().stripWhiteSpace());
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Adding line to incomingLineBuffer: "
+ << line
+ << endl;
+#endif
+
+ d->incomingLineBuffer.append(line);
+ }
+
+ if (!d->incomingLineBuffer.isEmpty())
+ {
+ kpfDebug
+ << d->id
+ << ": incomingLineBuffer isn't empty - calling slotRead directly"
+ << endl;
+
+ slotRead();
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": incomingLineBuffer is empty. Nothing to do."
+ << endl;
+ }
+ }
+
+ void
+ Server::slotRead()
+ {
+ kpfDebug << d->id << ": slotRead" << endl;
+
+ if (d->incomingLineBuffer.isEmpty())
+ {
+ kpfDebug << d->id << ": incomingLineBuffer is empty !" << endl;
+ return;
+ }
+
+ // There is data available in incomingLineBuffer.
+
+ switch (d->state)
+ {
+ case WaitingForRequest:
+ kpfDebug << d->id << ": I was waiting for a request" << endl;
+ (void) readRequest(d->incomingLineBuffer.first());
+ d->incomingLineBuffer.remove(d->incomingLineBuffer.begin());
+ break;
+
+ case WaitingForHeaders:
+ kpfDebug << d->id << ": I was waiting for headers" << endl;
+ readHeaders();
+ break;
+
+ case Responding:
+ case Finished:
+ default:
+ kpfDebug << d->id << ": I was responding or finished" << endl;
+ break;
+ }
+ }
+
+ bool
+ Server::readRequest(const QString & line)
+ {
+ ++d->requestCount;
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": (request #" << d->requestCount << ") readRequest: `"
+ << line << "'" << endl;
+#endif
+
+ QStringList l(QStringList::split(' ', line));
+
+ // A request usually looks like METHOD PATH PROTOCOL but we don't
+ // require PROTOCOL - we just assume HTTP/0.9 and act accordingly.
+
+ if (l.count() == 2)
+ {
+ kpfDebug << d->id << ": readRequest: HTTP/0.9 ???" << endl;
+ emit(request(this));
+ d->state = Responding;
+ respond(400);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ // The Request object handles setting parsing the strings we pass it here.
+ // It converts GET/HEAD/whatever to an enum, fixes up the path and
+ // converts the protocol string to a number.
+
+ d->request.setMethod (l[0]);
+ d->request.setPath (l[1]);
+ d->request.setProtocol (l.count() == 3 ? l[2] : QString::null);
+
+ // Before we check the request, say we received it.
+
+ emit(request(this));
+
+ return checkRequest();
+ }
+
+ bool
+ Server::checkRequest()
+ {
+ // We only handle METHOD of GET or HEAD.
+
+ if (Request::Unsupported == d->request.method())
+ {
+ kpfDebug << d->id << ": readRequest: method unsupported" << endl;
+ d->state = Responding;
+ respond(501);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ // If there's .. or ~ in the path, we disallow. Either there's a mistake
+ // or someone's trying to h@x0r us. I wouldn't have worried about ~
+ // normally, because I don't do anything with it, so the resource would
+ // simply not be found, but I'm worried that the QDir/QFile/QFileInfo
+ // stuff might try to expand it, so I'm not taking any chances.
+
+ if (d->request.path().contains("..") || d->request.path().contains('~'))
+ {
+ kpfDebug << d->id << ": readRequest: bogus path" << endl;
+ d->state = Responding;
+ respond(403);
+ emit(readyToWrite(this));
+ return false;
+ }
+
+ if (d->request.protocol() > 1.1)
+ {
+ if (d->request.protocol() >= 2.0)
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: unsupported protocol major number"
+ << endl;
+
+ d->request.setProtocol(1, 1);
+
+ d->state = Responding;
+ respond(505);
+ emit(readyToWrite(this));
+ return false;
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: unsupported protocol minor number"
+ << endl;
+
+ d->request.setProtocol(1, 1);
+ }
+ }
+
+ if (d->request.protocol() >= 1.0)
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: need to wait for headers now"
+ << endl;
+
+ if (d->request.protocol() > 1.0)
+ {
+ d->request.setPersist(true);
+ }
+
+ d->state = WaitingForHeaders;
+ d->readTimer.start(0, true);
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": readRequest: immediate response"
+ << endl;
+
+ d->state = Responding;
+ prepareResponse();
+ emit(readyToWrite(this));
+ return true;
+ }
+
+ return true;
+ }
+
+ void
+ Server::readHeaders()
+ {
+ kpfDebug << d->id << ": readHeaders" << endl;
+
+ // Pop lines from front of incomingLineBuffer and add to
+ // incomingHeaderLineBuffer until we reach the end of the headers, when we
+ // generate a response to the request.
+
+ while (!d->incomingLineBuffer.isEmpty())
+ {
+ // This would be cleaner if there was a QValueQueue.
+ // Basically we 'dequeue' the first line from incomingHeaderBuffer.
+
+ QString line(d->incomingLineBuffer.first());
+ d->incomingLineBuffer.remove(d->incomingLineBuffer.begin());
+
+ // Unless the line is empty, this is (in theory) a header.
+
+ if (!line.isEmpty())
+ {
+ kpfDebug << d->id << ": Header line: " << line << endl;
+ d->incomingHeaderLineBuffer << line;
+ }
+ else
+ {
+ kpfDebug << d->id << ": Blank line - end of headers" << endl;
+
+ // We have a buffer filled with all the header data received.
+ // First parse those headers.
+
+ d->request.parseHeaders(d->incomingHeaderLineBuffer);
+
+ // Clear out the buffer because we won't need to use it again
+ // and leaving all that data in memory is pointless.
+
+ d->incomingHeaderLineBuffer.clear();
+
+ // We've parsed the headers so the next thing we do is respond.
+
+ d->state = Responding;
+ prepareResponse();
+
+ // When the response has been prepared, we're ready to write
+ // some data back into that socket.
+
+ kpfDebug << d->id << ": Ready to write" << endl;
+
+ emit(readyToWrite(this));
+
+ return;
+ }
+ }
+
+ // Because we haven't found an empty line and therefore parsed
+ // headers + returned, we must wait for more headers.
+
+ kpfDebug
+ << d->id
+ << ": readHeaders: No lines left in header buffer."
+ << " Setting state to WaitingForHeaders"
+ << endl;
+
+ d->state = WaitingForHeaders;
+ }
+
+ void
+ Server::prepareResponse()
+ {
+ // The path to the requested resource is relative to our root.
+
+ QString filename = d->dir + '/' + d->request.path();
+
+ kpfDebug << d->id << ": filename: " << filename << endl;
+
+ d->resource.setPath(d->dir, d->request.path());
+
+ if (!d->resource.exists())
+ {
+ kpfDebug << d->id << ": Resource doesn't exist: %s" << filename << endl;
+
+ // No file ? Perhaps we should give a directory listing.
+
+ if (!(/*d->generateDirectoryListings && */ d->request.path() == "/"))
+ {
+ // Either index.html wasn't the file requested, or we're not supposed
+ // to generated listings.
+
+ respond(404);
+ return;
+ }
+ }
+
+ if (!d->followSymlinks && d->resource.symlink())
+ {
+ // If we're not supposed to follow symlinks and there's a symlink
+ // somewhere on the path, deny.
+
+ respond(403);
+ return;
+ }
+
+ if (!d->resource.readable())
+ {
+ // Deny even HEAD for unreadable files.
+
+ respond(403);
+ return;
+ }
+
+// if ((Request::Get == d->request.method()) && !d->resource.open())
+ // Open resource even if we're asked for HEAD. We need to ensure
+ // Content-Length is sent correctly.
+
+ if (!d->resource.open())
+ {
+ // Couldn't open the file. XXX why not ?
+
+ respond(403);
+ return;
+ }
+
+ if (d->request.haveRange())
+ {
+ // There was a byte range specified in the request so handleRange()
+ // to check that the range makes sense for the requested resource.
+
+ kpfDebug << d->id << ": Byte range in request" << endl;
+
+ if (!handleRange(d->request.range()))
+ {
+ // handleRange() takes care of sending the necessary response.
+ return;
+ }
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "No byte range in request."
+ << endl;
+
+ if (d->request.haveIfModifiedSince())
+ {
+ // If we saw an If-Modified-Since header and the resource hasn't
+ // been modified since that date, we respond with '304 Not modified'
+
+ if (toGMT(d->resource.lastModified()) <= d->request.ifModifiedSince())
+ {
+ kpfDebug
+ << d->id
+ << "Got IfModifiedSince and will respond with 304 (unmodified)"
+ << endl;
+
+ respond(304);
+ // We will not serve the file, so don't the size.
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "Got IfModifiedSince and will serve whole file (modified)"
+ << endl;
+
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+ }
+ else if (d->request.haveIfUnmodifiedSince())
+ {
+ // As above, except the logic is reversed.
+
+ if (toGMT(d->resource.lastModified()) > d->request.ifUnmodifiedSince())
+ {
+ kpfDebug
+ << d->id
+ << "Got IfUnmodifiedSince and will respond with 412 (modified)"
+ << endl;
+
+ respond(412);
+ // We not serve the file, so don't set the size.
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << "Got IfUnmodifiedSince and will serve whole file (unmodified)"
+ << endl;
+
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+ }
+ else
+ {
+ // We will serve the file, so set the size.
+ d->fileBytesLeft = d->resource.size();
+ }
+
+ // If we haven't set the response up yet, that means we are not using a
+ // special response due to a modification time condition. Therefore we
+ // are doing the 'usual' 200 response.
+
+ if (0 == d->response.code())
+ respond(200, d->fileBytesLeft);
+ }
+
+ kpfDebug
+ << d->id
+ << "Done setting up response. Code will be "
+ << responseName(d->response.code())
+ << endl;
+
+
+ // Send some headers back to the client, but only if the protocol
+ // they asked us to use is new enough to require this.
+
+ if (d->request.protocol() >= 1.0)
+ {
+ writeLine("Server: kpf");
+ writeLine("Date: " + dateString());
+ writeLine("Last-Modified: " + dateString(d->resource.lastModified()));
+ writeLine("Content-Type: " + d->resource.mimeType());
+
+ // Generate a Content-Range header if we are sending partial content.
+
+ if (206 == d->response.code())
+ {
+ QString line = "Content-Range: bytes ";
+
+ line += QString::number(d->request.range().first());
+
+ line += '-';
+
+ if (d->request.range().haveLast())
+ line += QString::number(d->request.range().last());
+ else
+ line += QString::number(d->resource.size() - 1);
+
+ line += '/';
+
+ line += QString::number(d->resource.size());
+
+ writeLine(line);
+ }
+
+ writeLine("Content-Length: " + QString::number(d->fileBytesLeft));
+ }
+
+ if (d->requestCount >= MaxKeepAlive && d->request.protocol() >= 1.1)
+ {
+ // We have made many transactions on this connection. Time to
+ // give up and let the client re-connect. If we don't do this,
+ // they could keep this connection open indefinitely.
+
+ writeLine("Connection: close");
+ }
+ else if (d->request.protocol() == 1.0)
+ {
+ // wget seems broken. If it sends keep-alive, it hangs waiting for
+ // nothing at all. Ignore its keep-alive request.
+ writeLine("Connection: close");
+ }
+ else if (d->request.protocol() == 1.1) {
+ writeLine("Connection: keep-alive");
+ }
+
+ // End of headers so send a newline.
+
+ if (d->request.protocol() >= 1.0)
+ {
+ writeLine("");
+ }
+ }
+
+ bool
+ Server::handleRange(const ByteRange & r)
+ {
+ // Here we check if the given ByteRange makes sense for the
+ // requested resource.
+
+ // Is the range just plain broken ?
+
+ if (!r.valid())
+ {
+ kpfDebug << d->id << ": Invalid byte range" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Does the range start before the end of our resource ?
+
+ else if (r.first() > d->resource.size())
+ {
+ kpfDebug << d->id << ": Range starts after EOF" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Does the range end after the end of our resource ?
+
+ else if (r.haveLast() && r.last() > d->resource.size())
+ {
+ kpfDebug << d->id << ": Range end after EOF" << endl;
+ respond(416);
+ return false;
+ }
+
+ // Ok, in theory the range should be satisfiable ...
+
+ else
+ {
+ // ... but maybe we can't seek to the start of the range.
+
+ if (!d->resource.seek(r.first()))
+ {
+ kpfDebug << d->id << ": Invalid byte range (couldn't seek)" << endl;
+ // Should this be 501 ?
+ respond(416);
+ return false;
+ }
+
+ kpfDebug << d->id << ": Ok, setting fileBytesLeft" << endl;
+
+ // Work out how many bytes are left to send to the client. Careful
+ // with the off-by-one errors here, eh ?
+
+ if (r.haveLast())
+ {
+ d->fileBytesLeft = r.last() + 1 - r.first();
+ }
+ else
+ {
+ d->fileBytesLeft = d->resource.size() - r.first();
+ }
+
+ kpfDebug << d->id << ": fileBytesLeft = "
+ << d->fileBytesLeft << "d" << endl;
+
+ respond(206, d->fileBytesLeft);
+ }
+
+ return true;
+ }
+
+ void
+ Server::slotBytesWritten(int i)
+ {
+ // Don't you just love it when people forget 'unsigned' ?
+
+ if (i > 0)
+ d->bytesWritten += i;
+
+ emit(output(this, i));
+
+ // Reset idle timer.
+ d->idleTimer.start(Timeout, true);
+ }
+
+ void
+ Server::slotConnectionClosed()
+ {
+ kpfDebug << d->id << ": slotConnectionClosed -> finished" << endl;
+ setFinished(Flush);
+ }
+
+ void
+ Server::writeLine(const QString & line)
+ {
+ // Fill a buffer. We're not allowed to send anything out until our
+ // controller calls write().
+
+ QCString s(line.utf8() + "\r\n");
+
+ d->headerBytesLeft += s.length();
+ d->outgoingHeaderBuffer += s;
+ }
+
+ void
+ Server::cancel()
+ {
+ kpfDebug << d->id << ": cancel -> finished" << endl;
+ setFinished(NoFlush);
+ }
+
+ void
+ Server::respond(uint code, ulong fileSize)
+ {
+ // Set the code of our Response object.
+
+ d->response.setCode(code);
+
+ // Request from the Response object the text that should be sent
+ // back to the client.
+
+ QCString s(d->response.text(d->request));
+
+ // Tell our Response object how long it will be in total (it doesn't
+ // know its total size until we tell it about the resource size.)
+
+ d->response.setSize(s.length() + fileSize);
+
+ // Tell the world we've finished setting up our response.
+
+ emit(response(this));
+
+ // Add the response text to the outgoing header buffer.
+
+ d->headerBytesLeft += s.length();
+ d->outgoingHeaderBuffer += s;
+ }
+
+ void
+ Server::setFinished(FlushSelect flushSelect)
+ {
+ if (Finished == d->state) // Already finished.
+ return;
+
+ d->state = Finished;
+
+ kpfDebug
+ << d->id
+ << ": finished("
+ << (Flush == flushSelect ? "flush" : "no flush")
+ << ")"
+ << endl;
+
+ if (Flush == flushSelect)
+ d->socket.flush();
+
+ d->socket.close();
+
+ d->death = QDateTime::currentDateTime();
+
+ emit(finished(this));
+ }
+
+ QHostAddress
+ Server::peerAddress() const
+ {
+ return d->socket.peerAddress();
+ }
+
+ ulong
+ Server::bytesLeft() const
+ {
+ // Return the combined size of the two output buffers.
+
+ return d->headerBytesLeft + d->fileBytesLeft;
+ }
+
+ ulong
+ Server::write(ulong maxBytes)
+ {
+ // We must be in 'Responding' state here. If not, there's a problem
+ // in the code.
+
+ if (Responding != d->state)
+ {
+ kpfDebug << d->id << ": write() but state != Responding -> finished";
+ setFinished(Flush);
+ return 0;
+ }
+
+ // If the socket has been closed (e.g. the remote end hung up)
+ // then we just give up.
+
+ if (QSocket::Connection != d->socket.state())
+ {
+ kpfDebug << d->id << ": Socket closed by client -> finished" << endl;
+ setFinished(Flush);
+ return 0;
+ }
+
+ kpfDebug << d->id << ": Response code is " << d->response.code() << " ("
+ << responseName(d->response.code()) << ")" << endl;
+
+ ulong bytesWritten = 0;
+
+ // Write header data.
+
+ ulong headerBytesWritten = 0;
+
+ if (!writeHeaderData(maxBytes, headerBytesWritten))
+ {
+ return 0;
+ }
+
+ maxBytes -= headerBytesWritten;
+ bytesWritten += headerBytesWritten;
+
+ // If we are only sending headers (response code != 2xx or request type
+ // was HEAD) or we reached the end of the file we were sending, give up.
+
+ if (d->response.code() < 200 || d->response.code() > 299)
+ {
+ kpfDebug << d->id << ": We are only sending headers -> finished" << endl;
+
+ // If we're sending 'Not modified' then we don't need to drop
+ // the connection just yet.
+
+ if (d->response.code() == 304 && d->request.persist())
+ {
+ kpfDebug
+ << d->id
+ << ": 304 and persist. Not dropping connection yet."
+ << endl;
+
+ reset();
+ }
+ else
+ {
+ setFinished(Flush);
+ }
+
+ return bytesWritten;
+ }
+
+ // Just HEAD ? Ok, then if we're set to persistent mode we go back
+ // and wait for another request. Otherwise we're done and can go home.
+
+ if (Request::Head == d->request.method())
+ {
+ if (d->request.persist())
+ {
+ reset();
+ }
+ else
+ {
+ setFinished(Flush);
+ }
+
+ return bytesWritten;
+ }
+
+ // If we've written our limit then wait until next time.
+
+ if (0 == maxBytes)
+ {
+ return bytesWritten;
+ }
+
+ // Write resource data.
+
+ ulong fileBytesWritten = 0;
+
+ // writeFileData() returns true if the op was successful and also
+ // returns the number of bytes written via the second parameter.
+
+ if (!writeFileData(maxBytes, fileBytesWritten))
+ {
+ return 0;
+ }
+
+ kpfDebug << "Wrote " << fileBytesWritten << " from file" << endl;
+
+ maxBytes -= fileBytesWritten;
+ bytesWritten += fileBytesWritten;
+
+ // Did we finish sending the resource data ?
+
+ if (0 == d->fileBytesLeft)
+ {
+ kpfDebug << d->id << ": No bytes left to write. Closing file." << endl;
+
+ d->resource.close();
+
+ // If we're in persistent mode, don't quit just yet.
+
+ if (d->requestCount < MaxKeepAlive && d->request.persist())
+ {
+ kpfDebug
+ << d->id
+ << ": Request included Keep-Alive, so we set state"
+ << " to WaitingForRequest and don't send finished()"
+ << endl;
+
+ reset();
+ }
+ else
+ {
+ kpfDebug
+ << d->id
+ << ": No keep-alive or hit MaxKeepAlive, so finished."
+ << endl;
+
+ setFinished(Flush);
+ }
+ }
+ else
+ {
+ // Ok, we have some data to send over the socket. Tell the world.
+
+ kpfDebug
+ << d->id
+ << "Still have data left to send."
+ << endl;
+
+ emit(readyToWrite(this));
+ }
+
+ return bytesWritten;
+ }
+
+
+ bool
+ Server::writeHeaderData(ulong max, ulong & bytesWritten)
+ {
+ // Is there some header data left to write ?
+
+ if (0 == d->headerBytesLeft)
+ return true;
+
+ // Calculate where to start reading from the buffer.
+
+ uint headerPos =
+ d->outgoingHeaderBuffer.length() - d->headerBytesLeft;
+
+ // Calculate how many bytes we should write this session.
+
+ uint bytesToWrite = min(d->headerBytesLeft, max);
+
+ // Calculate how many bytes we _may_ write.
+
+ bytesToWrite = min(bytesToWrite, d->socket.outputBufferLeft());
+
+ // Get a pointer to the data, offset by the position we start reading.
+
+ char * data = d->outgoingHeaderBuffer.data() + headerPos;
+
+ // Write the data, or at least as much as the socket buffers will
+ // take, and remember how much we wrote.
+
+ int headerBytesWritten = d->socket.writeBlock(data, bytesToWrite);
+
+ // <rant>
+ // Using -1 to signal an error is fucking evil.
+ // Return false instead or add a 'bool & ok' parameter.
+ // If you're not going to use exceptions, at least don't use
+ // crap C conventions for error handling.
+ // </rant>
+
+ if (-1 == headerBytesWritten)
+ {
+ // Socket error.
+
+ kpfDebug << d->id << ": Socket error -> finished" << endl;
+ setFinished(Flush);
+ return false;
+ }
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Wrote header data: `"
+ << d->outgoingHeaderBuffer.left(headerPos)
+ << "'"
+ << endl;
+#endif
+
+ // Subtract the number of bytes we wrote from the number of bytes
+ // left to write.
+
+ bytesWritten += headerBytesWritten;
+ d->headerBytesLeft -= headerBytesWritten;
+
+ // We may be doing a long file send next, so clear the header buffer
+ // because we don't need that data hanging around in memory anymore.
+
+ if (0 == d->headerBytesLeft)
+ d->outgoingHeaderBuffer.resize(0);
+
+ return true;
+ }
+
+ bool
+ Server::writeFileData(ulong maxBytes, ulong & bytesWritten)
+ {
+ // Nothing left in the file ?
+
+ if (d->resource.atEnd())
+ {
+ d->resource.close();
+ kpfDebug << d->id << ": file at end -> finished" << endl;
+ setFinished(Flush);
+ return false;
+ }
+
+ // Calculate how much data we may write this session.
+ // If none, give up.
+
+ uint bytesToWrite = min(d->fileBytesLeft, maxBytes);
+
+ if (0 == bytesToWrite)
+ return true;
+
+ bytesToWrite = min(bytesToWrite, d->socket.outputBufferLeft());
+
+ QByteArray a(bytesToWrite);
+
+ if (0 == bytesToWrite)
+ return true;
+
+ // Read some data (maximum = bytesToWrite) from the file.
+
+ int fileBytesRead = d->resource.readBlock(a.data(), bytesToWrite);
+
+ // Write that data to the socket and remember how much was actually
+ // written (may be less than requested if socket buffers are full.)
+
+ int fileBytesWritten = d->socket.writeBlock(a.data(), fileBytesRead);
+
+ // Was there an error writing to the socket ?
+
+ if (-1 == fileBytesWritten)
+ {
+ // Socket error.
+ kpfDebug << d->id << ": Socket error -> finished" << endl;
+ d->resource.close();
+ setFinished(Flush);
+ return false;
+ }
+
+#ifdef KPF_TRAFFIC_DEBUG
+ kpfDebug
+ << d->id
+ << ": Wrote file data: `"
+ << QCString(a.data(), fileBytesWritten)
+ << "'"
+ << endl;
+#endif
+
+ // We should have been able to write the full amount to the socket,
+ // because we tested d->socket.outputBufferLeft(). If we didn't
+ // manage to write that much, either we have a bug or QSocket does.
+
+ if (fileBytesWritten < fileBytesRead)
+ {
+ kpfDebug << d->id << ": Short write !" << endl;
+ d->resource.close();
+ setFinished(Flush);
+ return false;
+ }
+
+ // Subtract the amount of bytes written from the number left to write.
+
+ bytesToWrite -= fileBytesWritten;
+ bytesWritten += fileBytesWritten;
+ d->fileBytesLeft -= fileBytesWritten;
+
+ return true;
+ }
+
+ void
+ Server::slotTimeout()
+ {
+ kpfDebug << d->id << ": Timeout -> finished" << endl;
+ setFinished(NoFlush);
+ }
+
+ Request
+ Server::request() const
+ {
+ return d->request;
+ }
+
+ Response
+ Server::response() const
+ {
+ return d->response;
+ }
+
+ ulong
+ Server::output() const
+ {
+ return d->bytesWritten;
+ }
+
+ Server::State
+ Server::state() const
+ {
+ return d->state;
+ }
+
+ QDateTime
+ Server::birth() const
+ {
+ return d->birth;
+ }
+
+ QDateTime
+ Server::death() const
+ {
+ return d->death;
+ }
+
+ void
+ Server::reset()
+ {
+ kpfDebug << d->id << ": Resetting for another request" << endl;
+
+ d->request .clear();
+ d->response .clear();
+ d->resource .clear();
+
+ d->state = WaitingForRequest;
+ d->readTimer.start(0, true);
+ }
+
+} // End namespace KPF
+
+#include "Server.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Server.h b/kpf/src/Server.h
new file mode 100644
index 00000000..8d26a167
--- /dev/null
+++ b/kpf/src/Server.h
@@ -0,0 +1,186 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_H
+#define KPF_SERVER_H
+
+#include <qdatetime.h>
+#include <qstring.h>
+#include <qhostaddress.h>
+
+#include "Request.h"
+#include "Response.h"
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Converses with a remote client. Handles requests and generates responses.
+ * Bandwidth is controlled by parent (WebServer).
+ */
+ class Server : public QObject
+ {
+ Q_OBJECT
+
+ public:
+
+ /**
+ * A Server can be in one of four states, enumerated here.
+ */
+ enum State { WaitingForRequest, WaitingForHeaders, Responding, Finished };
+
+ /**
+ * @param dir Root directory. Files not contained in this directory
+ * and subdirectories thereof will not be server.
+ *
+ * @param followSymlinks If false, an expensive algorithm will ensure
+ * that no symbolic links are present anywhere in the path from /
+ * to the requested resource.
+ *
+ * @param socket The system's socket device, used for communication.
+ *
+ * @param parent A WebServer, which will manage this Server.
+ */
+ Server
+ (
+ const QString & dir,
+ bool followSymlinks,
+ int socket,
+ WebServer * parent
+ );
+
+ /**
+ * Free internal data and close connection if still open.
+ */
+ virtual ~Server();
+
+ /**
+ * @return address of client that has connected to us.
+ */
+ QHostAddress peerAddress() const;
+
+ /**
+ * @return Request object associated with this connection.
+ * When persistent connections are used, the object may
+ * differ depending on the current state.
+ */
+ Request request() const;
+
+ /**
+ * @return Response object associated with this connection.
+ * When persistent connections are used, the object may
+ * differ depending on the current state.
+ */
+ Response response() const;
+
+ /**
+ * @return number of bytes sent out over the socket since this object
+ * was created.
+ */
+ ulong output() const;
+
+ /**
+ * @return current state.
+ * @see State.
+ */
+ State state() const;
+
+ /**
+ * @return date and time this object was created.
+ */
+ QDateTime birth() const;
+
+ /**
+ * @return date and time all activity was completed.
+ */
+ QDateTime death() const;
+
+ /**
+ * @return number of bytes remaining to send to client.
+ */
+ ulong bytesLeft() const;
+
+ /**
+ * Send no more than maxBytes to the client.
+ *
+ * @return number of bytes sent.
+ */
+ ulong write(ulong maxBytes);
+
+ /**
+ * Stop negotiating with client and sending data. Emit @ref finished.
+ * Do not flush output.
+ */
+ void cancel();
+
+ protected slots:
+
+ void slotReadyRead ();
+ void slotRead ();
+ void slotBytesWritten (int);
+ void slotConnectionClosed ();
+ void slotTimeout ();
+
+ signals:
+
+ void readyToWrite (Server *);
+ void output (Server *, ulong);
+ void finished (Server *);
+ void response (Server *);
+ void request (Server *);
+
+ private:
+
+ // Disable copying.
+
+ Server(const Server &);
+ Server & operator = (const Server &);
+
+ enum FlushSelect
+ {
+ Flush,
+ NoFlush
+ };
+
+ void setFinished (FlushSelect);
+ void writeLine (const QString &);
+ void prepareResponse ();
+ void respond (uint code, ulong size = 0);
+ bool readRequest (const QString &);
+ bool checkRequest ();
+ void handleRequest ();
+ void readHeaders ();
+ bool handleRange (const ByteRange &);
+ bool writeHeaderData (ulong, ulong &);
+ bool writeFileData (ulong, ulong &);
+ void reset ();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerPrivate.cpp b/kpf/src/ServerPrivate.cpp
new file mode 100644
index 00000000..079fb29b
--- /dev/null
+++ b/kpf/src/ServerPrivate.cpp
@@ -0,0 +1,54 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "ServerPrivate.h"
+#include "Defaults.h"
+
+namespace KPF
+{
+ ulong Server::Private::ID = 0L;
+
+ Server::Private::Private()
+ : socket (0, "KPF::Server::Private.socket"),
+ state (WaitingForRequest),
+ bytesWritten (0L),
+ headerBytesLeft (0L),
+ fileBytesLeft (0L),
+ dataRead (0L),
+ followSymlinks (Config::DefaultFollowSymlinks),
+ generateDirectoryListings (false),
+ requestCount (0),
+ id (ID++)
+ {
+ // Empty.
+ }
+
+ Server::Private::~Private()
+ {
+ // Empty.
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerPrivate.h b/kpf/src/ServerPrivate.h
new file mode 100644
index 00000000..fcf7fd39
--- /dev/null
+++ b/kpf/src/ServerPrivate.h
@@ -0,0 +1,78 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_PRIVATE_H
+#define KPF_SERVER_PRIVATE_H
+
+#include <qcstring.h>
+#include <qtimer.h>
+
+#include "ServerSocket.h"
+#include "Server.h"
+#include "Request.h"
+#include "Response.h"
+#include "Resource.h"
+
+namespace KPF
+{
+ /**
+ * Data for Server class. Kept here to speed up recompilation.
+ */
+ class Server::Private
+ {
+ public:
+
+ Private();
+ ~Private();
+
+ // Order dependency
+ ServerSocket socket;
+ Server::State state;
+ ulong bytesWritten;
+ ulong headerBytesLeft;
+ ulong fileBytesLeft;
+ ulong dataRead;
+ bool followSymlinks;
+ bool generateDirectoryListings;
+ uint requestCount;
+ // End order dependency
+
+ QString dir;
+ Request request;
+ Response response;
+ Resource resource;
+ QStringList incomingHeaderLineBuffer;
+ QStringList incomingLineBuffer;
+ QDateTime birth;
+ QDateTime death;
+ QCString outgoingHeaderBuffer;
+ QTimer idleTimer;
+ QTimer readTimer;
+ ulong id;
+ static ulong ID;
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_PRIVATE_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerSocket.cpp b/kpf/src/ServerSocket.cpp
new file mode 100644
index 00000000..9f39ac57
--- /dev/null
+++ b/kpf/src/ServerSocket.cpp
@@ -0,0 +1,46 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qsocketdevice.h>
+
+#include "Defines.h"
+#include "ServerSocket.h"
+
+namespace KPF
+{
+ ServerSocket::ServerSocket(QObject * parent, const char * name)
+ : QSocket(parent, name)
+ {
+ // Empty.
+ }
+
+ uint
+ ServerSocket::outputBufferLeft()
+ {
+ return socketDevice()->sendBufferSize() - bytesToWrite();
+ }
+
+} // End namespace KPF
+
+// vim:ts=2:sw=2:tw=78:et
+
diff --git a/kpf/src/ServerSocket.h b/kpf/src/ServerSocket.h
new file mode 100644
index 00000000..14a2e2d8
--- /dev/null
+++ b/kpf/src/ServerSocket.h
@@ -0,0 +1,47 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_SOCKET_H
+#define KPF_SERVER_SOCKET_H
+
+#include <qsocket.h>
+
+namespace KPF
+{
+ /**
+ * Used to help calculate how much of a QSocket's output buffer we can
+ * use before data will be sent out.
+ */
+ class ServerSocket : public QSocket
+ {
+ public:
+
+ ServerSocket(QObject * parent, const char * name = 0);
+
+ uint outputBufferLeft();
+ };
+
+} // End namespace KPF
+
+#endif // KPF_SERVER_SOCKET_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerWizard.cpp b/kpf/src/ServerWizard.cpp
new file mode 100644
index 00000000..8ca47e22
--- /dev/null
+++ b/kpf/src/ServerWizard.cpp
@@ -0,0 +1,410 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qspinbox.h>
+#include <qdir.h>
+#include <qptrlist.h>
+#include <qlineedit.h>
+
+#include <kapplication.h>
+#include <klineedit.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+
+#include "Defines.h"
+#include "ServerWizard.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "Help.h"
+#include <dnssd/servicebrowser.h>
+
+#include <unistd.h>
+
+namespace KPF
+{
+ ServerWizard::ServerWizard(QWidget * parent)
+ : KWizard(parent, "KPF::ServerWizard", true)
+ {
+ setCaption(i18n("New Server - %1").arg("kpf"));
+
+ page1_ = new QWidget(this);
+ page2_ = new QWidget(this);
+ page3_ = new QWidget(this);
+// page4_ = new QWidget(this);
+ page5_ = new QWidget(this);
+
+ QLabel * l_rootDirectoryHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the directory which contains the files"
+ " you wish to share."
+ "</p>"
+ "<p>"
+ "<em>Warning</em>: Do not share any directories that contain"
+ " sensitive information!"
+ "</p>"
+ ),
+ page1_
+ );
+
+ QLabel * l_listenPortHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the network `port' on which the server should"
+ " listen for connections."
+ "</p>"
+ ),
+ page2_
+ );
+
+ QLabel * l_bandwidthLimitHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the maximum amount of data (in kilobytes) that will be"
+ " sent out per second."
+ "</p>"
+ "<p>"
+ "This allows you to keep some bandwidth for yourself instead"
+ " of allowing connections with kpf to hog your connection."
+ "</p>"
+ ),
+ page3_
+ );
+/*
+ QLabel * l_connectionLimitHelp =
+ new QLabel
+ (
+ i18n
+ (
+ "<p>"
+ "Specify the maximum number of connections allowed at"
+ " any one time."
+ "</p>"
+ ),
+ page4_
+ );
+*/
+ bool canPublish = DNSSD::ServiceBrowser::isAvailable() == DNSSD::ServiceBrowser::Working;
+ QLabel * l_serverNameHelp =
+ new QLabel
+ (
+ KPF::HelpText::getServerNameHelp(),
+ page5_
+ );
+
+ QLabel * l_root_ =
+ new QLabel(i18n("&Root directory:"), page1_);
+
+ QLabel * l_listenPort_ =
+ new QLabel(i18n("&Listen port:"), page2_);
+
+ QLabel * l_bandwidthLimit_ =
+ new QLabel(i18n("&Bandwidth limit:"), page3_);
+
+// QLabel * l_connectionLimit_ =
+// new QLabel(i18n("Connection &limit"), page4_);
+
+ QLabel * l_serverName_ =
+ new QLabel(i18n("&Server name:"), page5_);
+
+ if(!canPublish)
+ l_serverName_->setEnabled(false);
+
+ kur_root_ = new KURLRequester(page1_);
+
+ sb_listenPort_ = new QSpinBox(1, 65535, 1, page2_);
+
+ sb_bandwidthLimit_ = new QSpinBox(1, 999999, 1, page3_);
+// sb_connectionLimit_ = new QSpinBox(1, 9999, 1, page4_);
+
+ char hostname[255];
+ gethostname(hostname, 255-2);
+ hostname[255-1]=0;
+ le_serverName_ = new QLineEdit(hostname, page5_);
+
+ if(!canPublish)
+ le_serverName_->setEnabled(false);
+
+ l_root_ ->setBuddy(kur_root_);
+ l_listenPort_ ->setBuddy(sb_listenPort_);
+ l_bandwidthLimit_ ->setBuddy(sb_bandwidthLimit_);
+ l_serverName_ ->setBuddy(le_serverName_);
+// l_connectionLimit_ ->setBuddy(sb_connectionLimit_);
+
+ sb_listenPort_
+ ->setValue(WebServerManager::instance()->nextFreePort());
+
+ sb_bandwidthLimit_ ->setValue(Config::DefaultBandwidthLimit);
+ sb_bandwidthLimit_ ->setSuffix(i18n(" kB/s"));
+// sb_connectionLimit_ ->setValue(Config::DefaultConnectionLimit);
+
+ QVBoxLayout * layout1 =
+ new QVBoxLayout(page1_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout2 =
+ new QVBoxLayout(page2_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout3 =
+ new QVBoxLayout(page3_, KDialog::marginHint(), KDialog::spacingHint());
+
+// QVBoxLayout * layout4 =
+// new QVBoxLayout(page4_, KDialog::marginHint(), KDialog::spacingHint());
+
+ QVBoxLayout * layout5 =
+ new QVBoxLayout(page5_, KDialog::marginHint(), KDialog::spacingHint());
+
+ layout1->addWidget(l_rootDirectoryHelp);
+ layout2->addWidget(l_listenPortHelp);
+ layout3->addWidget(l_bandwidthLimitHelp);
+// layout4->addWidget(l_connectionLimitHelp);
+ layout5->addWidget(l_serverNameHelp);
+
+ QHBoxLayout * layout10 = new QHBoxLayout(layout1);
+
+ layout10->addWidget(l_root_);
+ layout10->addWidget(kur_root_);
+
+ layout1->addStretch(1);
+
+ QHBoxLayout * layout20 = new QHBoxLayout(layout2);
+
+ layout20->addWidget(l_listenPort_);
+ layout20->addWidget(sb_listenPort_);
+
+ layout2->addStretch(1);
+
+ QHBoxLayout * layout30 = new QHBoxLayout(layout3);
+
+ layout30->addWidget(l_bandwidthLimit_);
+ layout30->addWidget(sb_bandwidthLimit_);
+
+ layout3->addStretch(1);
+
+// QHBoxLayout * layout40 = new QHBoxLayout(layout4);
+
+// layout40->addWidget(l_connectionLimit_);
+// layout40->addWidget(sb_connectionLimit_);
+
+// layout4->addStretch(1);
+
+ QHBoxLayout * layout50 = new QHBoxLayout(layout5);
+
+ layout50->addWidget(l_serverName_);
+ layout50->addWidget(le_serverName_);
+
+ addPage(page1_, i18n("Root Directory"));
+ addPage(page2_, i18n("Listen Port"));
+ addPage(page3_, i18n("Bandwidth Limit"));
+// addPage(page4_, i18n("Connection Limit"));
+ addPage(page5_, i18n("Server Name"));
+
+ kur_root_->setURL(QDir::homeDirPath() + "/public_html");
+ kur_root_->setMode
+ (KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly);
+
+// setFinishEnabled(page4_, true);
+ setFinishEnabled(page5_, true);
+
+ // This slot sets the 'Next' button on page 1 to enabled/disabled
+ // depending on whether the path is OK.
+
+ connect
+ (
+ kur_root_,
+ SIGNAL(textChanged(const QString &)),
+ SLOT(slotServerRootChanged(const QString &))
+ );
+
+ // Used for setting the caption on the file dialog.
+
+ connect
+ (
+ kur_root_,
+ SIGNAL(openFileDialog(KURLRequester *)),
+ SLOT(slotOpenFileDialog(KURLRequester *))
+ );
+
+ // This slot sets the 'Next' button on page 2 to enabled/disabled
+ // depending on whether the port is OK.
+
+ connect
+ (
+ sb_listenPort_,
+ SIGNAL(valueChanged(int)),
+ SLOT(slotListenPortChanged(int))
+ );
+
+ // Update 'Next' button on page 1.
+
+ slotServerRootChanged(kur_root_->url());
+
+ // Update 'Next' button on page 2.
+
+ slotListenPortChanged(sb_listenPort_->value());
+ }
+
+ ServerWizard::~ServerWizard()
+ {
+ }
+
+ void
+ ServerWizard::setLocation(const QString & location)
+ {
+ kur_root_->setURL(location);
+ }
+
+ QString
+ ServerWizard::root() const
+ {
+ return kur_root_->url();
+ }
+
+ uint
+ ServerWizard::listenPort() const
+ {
+ return sb_listenPort_->value();
+ }
+
+ uint
+ ServerWizard::bandwidthLimit() const
+ {
+ return sb_bandwidthLimit_->value();
+ }
+ QString
+
+ ServerWizard::serverName() const
+ {
+ return le_serverName_->text();
+ }
+
+ uint
+ ServerWizard::connectionLimit() const
+ {
+ return Config::DefaultConnectionLimit; // sb_connectionLimit_->value();
+ }
+
+ void
+ ServerWizard::accept()
+ {
+ QWizard::accept();
+ emit(dying(this));
+ }
+
+ void
+ ServerWizard::reject()
+ {
+ QWizard::reject();
+ emit(dying(this));
+ }
+
+ void
+ ServerWizard::slotListenPortChanged(int newPort)
+ {
+ if (newPort <= 1024)
+ {
+ setNextEnabled(page2_, false);
+ return;
+ }
+
+ QPtrList<WebServer>
+ serverList(WebServerManager::instance()->serverListLocal());
+
+ for (QPtrListIterator<WebServer> it(serverList); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == uint(newPort))
+ {
+ setNextEnabled(page2_, false);
+ return;
+ }
+ }
+
+ setNextEnabled(page2_, true);
+ }
+
+ void
+ ServerWizard::slotServerRootChanged(const QString & _root)
+ {
+ QString root(_root);
+
+ kpfDebug << root << endl;
+
+ // Duplicate ?
+
+ if (WebServerManager::instance()->hasServer(root))
+ {
+ kpfDebug << "Already have a server at " << root << endl;
+ setNextEnabled(page1_, false);
+ return;
+ }
+
+ if ("/" != root.right(1))
+ root += "/";
+
+ QFileInfo fi(root);
+
+ if (!fi.isDir()) // || (fi.dirPath() == QDir::homeDirPath()))
+ {
+ kpfDebug << root << " isn't a dir" << endl; //, or it's $HOME" << endl;
+ setNextEnabled(page1_, false);
+ return;
+ }
+
+ setNextEnabled(page1_, true);
+ }
+
+ void
+ ServerWizard::slotOpenFileDialog(KURLRequester * urlRequester)
+ {
+ KFileDialog * fileDialog = urlRequester->fileDialog();
+
+ if (0 == fileDialog)
+ {
+ kpfDebug << "URL requester's file dialog is 0" << endl;
+ return;
+ }
+
+ fileDialog->setCaption(i18n("Choose Directory to Share - %1").arg("kpf"));
+ }
+
+ void
+ ServerWizard::help()
+ {
+ kapp->invokeHelp("share-config", "kpf");
+ }
+}
+
+#include "ServerWizard.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/ServerWizard.h b/kpf/src/ServerWizard.h
new file mode 100644
index 00000000..a484b8eb
--- /dev/null
+++ b/kpf/src/ServerWizard.h
@@ -0,0 +1,90 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SERVER_WIZARD_H
+#define KPF_SERVER_WIZARD_H
+
+#include <kwizard.h>
+
+class QSpinBox;
+class KURLRequester;
+class QLineEdit;
+
+namespace KPF
+{
+ /**
+ * Used to allow easy creation and configuration of a WebServer object.
+ */
+ class ServerWizard : public KWizard
+ {
+ Q_OBJECT
+
+ public:
+
+ ServerWizard(QWidget * parent = 0);
+
+ virtual ~ServerWizard();
+
+ void setLocation(const QString &);
+
+ QString root() const;
+ uint listenPort() const;
+ uint bandwidthLimit() const;
+ uint connectionLimit() const;
+ QString serverName() const;
+
+ signals:
+
+ void dying(ServerWizard *);
+
+ protected slots:
+
+ virtual void accept();
+ virtual void reject();
+ void slotServerRootChanged(const QString &);
+ void slotListenPortChanged(int);
+ void slotOpenFileDialog(KURLRequester *);
+
+ protected:
+
+ virtual void help();
+
+ private:
+
+
+ KURLRequester * kur_root_;
+ QSpinBox * sb_listenPort_;
+ QSpinBox * sb_bandwidthLimit_;
+ QSpinBox * sb_connectionLimit_;
+ QLineEdit * le_serverName_;
+
+ QWidget * page1_;
+ QWidget * page2_;
+ QWidget * page3_;
+ QWidget * page4_;
+ QWidget * page5_;
+ };
+}
+
+#endif // KPF_SERVER_WIZARD_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/SingleServerConfigDialog.cpp b/kpf/src/SingleServerConfigDialog.cpp
new file mode 100644
index 00000000..6fa1792b
--- /dev/null
+++ b/kpf/src/SingleServerConfigDialog.cpp
@@ -0,0 +1,98 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <klocale.h>
+
+#include "Defines.h"
+#include "WebServer.h"
+#include "ConfigDialogPage.h"
+#include "SingleServerConfigDialog.h"
+
+namespace KPF
+{
+ SingleServerConfigDialog::SingleServerConfigDialog
+ (
+ WebServer * server,
+ QWidget * parent
+ )
+ : KDialogBase
+ (
+ parent,
+ "KPF::SingleServerConfigDialog",
+ false,
+ i18n("Configuring Server %1 - kpf").arg(server->root()),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Ok,
+ true
+ ),
+ server_(server)
+ {
+ widget_ = new ConfigDialogPage(server_, this);
+
+ connect
+ (
+ widget_,
+ SIGNAL(ok(bool)),
+ SLOT(slotOk(bool))
+ );
+
+ setMainWidget(widget_);
+
+ connect(this, SIGNAL(finished()), SLOT(slotFinished()));
+
+ widget_->checkOk();
+ }
+
+ SingleServerConfigDialog::~SingleServerConfigDialog()
+ {
+ // Empty.
+ }
+
+ WebServer *
+ SingleServerConfigDialog::server()
+ {
+ return server_;
+ }
+
+ void
+ SingleServerConfigDialog::accept()
+ {
+ widget_->save();
+ KDialogBase::accept();
+ }
+
+ void
+ SingleServerConfigDialog::slotFinished()
+ {
+ emit(dying(this));
+ }
+
+ void
+ SingleServerConfigDialog::slotOk(bool ok)
+ {
+ enableButtonOK(ok);
+ }
+}
+
+#include "SingleServerConfigDialog.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/SingleServerConfigDialog.h b/kpf/src/SingleServerConfigDialog.h
new file mode 100644
index 00000000..a6f1c17f
--- /dev/null
+++ b/kpf/src/SingleServerConfigDialog.h
@@ -0,0 +1,68 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SINGLE_SERVER_CONFIG_DIALOG_H
+#define KPF_SINGLE_SERVER_CONFIG_DIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KPF
+{
+ class WebServer;
+ class ConfigDialogPage;
+
+ /**
+ * Used to allow configuration of a WebServer object.
+ */
+ class SingleServerConfigDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ SingleServerConfigDialog(WebServer *, QWidget * parent);
+
+ virtual ~SingleServerConfigDialog();
+
+ WebServer * server();
+
+ protected slots:
+
+ void slotFinished();
+ void accept();
+ void slotOk(bool);
+
+ signals:
+
+ void dying(SingleServerConfigDialog *);
+
+ private:
+
+ WebServer * server_;
+
+ ConfigDialogPage * widget_;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/StartingKPFDialog.cpp b/kpf/src/StartingKPFDialog.cpp
new file mode 100644
index 00000000..19b073bd
--- /dev/null
+++ b/kpf/src/StartingKPFDialog.cpp
@@ -0,0 +1,139 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qtimer.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <klocale.h>
+
+#include "Defines.h"
+#include "StartingKPFDialog.h"
+
+namespace KPF
+{
+ class StartingKPFDialog::Private
+ {
+ public:
+
+ Private()
+ {
+ // Empty.
+ }
+
+ QTimer timer;
+ };
+
+ StartingKPFDialog::StartingKPFDialog(QWidget * parent)
+ :
+ KDialogBase
+ (
+ parent,
+ "StartingKPFDialog",
+ true, /* modal */
+ i18n("Starting KDE public fileserver applet"),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Cancel,
+ true
+ )
+ {
+ d = new Private;
+
+ QFrame * mainWidget = makeMainWidget();
+
+ QLabel * about =
+ new QLabel
+ (
+ i18n("Starting kpf..."),
+ mainWidget
+ );
+
+ QVBoxLayout * layout = new QVBoxLayout(mainWidget);
+
+ layout->addWidget(about);
+
+ kapp->dcopClient()->setNotifications(true);
+
+ connect
+ (
+ kapp->dcopClient(),
+ SIGNAL(applicationRegistered(const QCString &)),
+ SLOT(slotApplicationRegistered(const QCString &))
+ );
+
+ kapp->dcopClient()
+ ->send("kicker", "default", "addApplet(QString)", "kpfapplet.desktop");
+
+ connect(&d->timer, SIGNAL(timeout()), SLOT(slotTimeout()));
+
+ enableButtonOK(false);
+ enableButtonCancel(true);
+
+ d->timer.start(8 * 1000 /* 8 seconds */, true /* single shot */);
+ }
+
+ StartingKPFDialog::~StartingKPFDialog()
+ {
+ delete d;
+ d = 0;
+ }
+
+ void
+ StartingKPFDialog::slotTimeout()
+ {
+ enableButtonOK(true);
+ enableButtonCancel(false);
+
+ if (kpfRunning())
+ {
+ kpfDebug << "slotTimeout: kpf is running now" << endl;
+ }
+ else
+ {
+ kpfDebug << "slotTimeout: kpf still not running" << endl;
+ }
+ }
+
+ bool
+ StartingKPFDialog::kpfRunning()
+ {
+ return kapp->dcopClient()->isApplicationRegistered("kpf");
+ }
+
+ void
+ StartingKPFDialog::slotApplicationRegistered(const QCString & id)
+ {
+ if ("kpf" == id)
+ {
+ kpfDebug << "kpf just started up" << endl;
+ enableButtonOK(true);
+ enableButtonCancel(false);
+ }
+ }
+}
+
+#include "StartingKPFDialog.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/StartingKPFDialog.h b/kpf/src/StartingKPFDialog.h
new file mode 100644
index 00000000..cbeca549
--- /dev/null
+++ b/kpf/src/StartingKPFDialog.h
@@ -0,0 +1,62 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_STARTING_KPF_DIALOG_H
+#define KPF_STARTING_KPF_DIALOG_H
+
+#include <kdialogbase.h>
+
+namespace KPF
+{
+ /**
+ * Used to provide a simply display which informs the user that an attempt
+ * to start the kpf applet is being made.
+ */
+ class StartingKPFDialog : public KDialogBase
+ {
+ Q_OBJECT
+
+ public:
+
+ StartingKPFDialog(QWidget * parent);
+
+ virtual ~StartingKPFDialog();
+
+ protected slots:
+
+ void slotTimeout();
+ void slotApplicationRegistered(const QCString &);
+
+ protected:
+
+ bool kpfRunning();
+
+ private:
+
+ class Private;
+ Private * d;
+ };
+}
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/System.cpp b/kpf/src/System.cpp
new file mode 100644
index 00000000..f8f2c78b
--- /dev/null
+++ b/kpf/src/System.cpp
@@ -0,0 +1,71 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifdef __unix__
+
+// System includes
+#include <signal.h> // For signal(2).
+#include <unistd.h> // For get?uid.
+#include <sys/types.h> // For get?uid.
+
+namespace kpf
+{
+ void blockSigPipe()
+ {
+ ::signal(SIGPIPE, SIG_IGN);
+ }
+
+ int userId()
+ {
+ return ::getuid();
+ }
+
+ int effectiveUserId()
+ {
+ return ::geteuid();
+ }
+}
+
+#else // non-UNIX
+
+namespace kpf
+{
+ void blockSigPipe()
+ {
+ return;
+ }
+
+ int userId()
+ {
+ return -1;
+ }
+
+ int effectiveUserId()
+ {
+ return -1;
+ }
+
+}
+
+#endif
+
diff --git a/kpf/src/System.h b/kpf/src/System.h
new file mode 100644
index 00000000..b740e0c7
--- /dev/null
+++ b/kpf/src/System.h
@@ -0,0 +1,35 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_SYSTEM_H
+#define KPF_SYSTEM_H
+
+namespace kpf
+{
+ void blockSigPipe();
+
+ int userId();
+ int effectiveUserId();
+}
+
+#endif
diff --git a/kpf/src/Utils.cpp b/kpf/src/Utils.cpp
new file mode 100644
index 00000000..a867a998
--- /dev/null
+++ b/kpf/src/Utils.cpp
@@ -0,0 +1,414 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <ctime>
+#include <clocale>
+
+#include <qfile.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+
+#include "Defines.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ static bool dateInitDone = false;
+
+ static QStringList monthList;
+
+ static const char rfc1123Format[] = "%a, %d %b %Y %H:%M:%S GMT";
+
+ static time_t qDateTimeToTimeT(const QDateTime & t)
+ {
+ struct tm tempTm;
+
+ tempTm.tm_year = t.date().year();
+ tempTm.tm_mon = t.date().month();
+ tempTm.tm_mday = t.date().day();
+ tempTm.tm_hour = t.time().hour();
+ tempTm.tm_min = t.time().minute();
+ tempTm.tm_sec = t.time().second();
+ tempTm.tm_isdst = -1;
+
+ // Fix up differences between QDateTime and tm.
+
+ tempTm.tm_year -= 1900;
+
+ --tempTm.tm_mon;
+
+ return ::mktime(&tempTm);
+ }
+
+ QDateTime
+ toGMT(const QDateTime & dt)
+ {
+ time_t dtAsTimeT = qDateTimeToTimeT(dt);
+
+ struct tm * dtAsGmTm = ::gmtime(&dtAsTimeT);
+
+ if (0 == dtAsGmTm)
+ return QDateTime();
+
+ time_t dtAsGmTimeT = ::mktime(dtAsGmTm);
+
+ QDateTime ret;
+
+ ret.setTime_t(dtAsGmTimeT);
+
+ return ret;
+ }
+
+ void dateInit()
+ {
+ if (dateInitDone)
+ return;
+
+ dateInitDone = true;
+
+ monthList
+ << "Jan"
+ << "Feb"
+ << "Mar"
+ << "Apr"
+ << "May"
+ << "Jun"
+ << "Jul"
+ << "Aug"
+ << "Sep"
+ << "Oct"
+ << "Nov"
+ << "Dec"
+ ;
+ }
+
+ QString dateString()
+ {
+ return dateString(QDateTime::currentDateTime());
+ }
+
+
+ QString dateString(const QDateTime & t)
+ {
+ time_t asTimeT = qDateTimeToTimeT(t);
+
+ struct tm * asTm = ::gmtime(&asTimeT);
+
+ if (0 == asTm)
+ {
+ kpfDebug << "::gmtime() failed" << endl;
+ return QString::null;
+ }
+
+ asTm->tm_isdst = -1;
+
+ const int len = 128;
+
+ char buf[len];
+
+ // Ensure we use locale "C" for strftime.
+
+ QCString oldLC_ALL = ::strdup(::setlocale(LC_TIME, "C"));
+ QCString oldLC_TIME = ::strdup(::setlocale(LC_ALL, "C"));
+ {
+ ::strftime(static_cast<char *>(buf), len, rfc1123Format, asTm);
+ }
+ ::setlocale(LC_TIME, oldLC_TIME.data());
+ ::setlocale(LC_ALL, oldLC_ALL.data());
+
+ return QString::fromUtf8(buf);
+ }
+
+ bool
+ parseDate(const QString & s, QDateTime & dt)
+ {
+ dateInit();
+
+// kpfDebug << "Parsing date `" << s << "'" << endl;
+
+ QStringList l(QStringList::split(' ', s));
+
+ switch (l.count())
+ {
+ case 4:
+// kpfDebug << "Date type is RFC 850" << endl;
+ return parseDateRFC850(l, dt);
+ break;
+ case 5:
+// kpfDebug << "Date type is asctime" << endl;
+ return parseDateAscTime(l, dt);
+ break;
+ case 6:
+// kpfDebug << "Date type is RFC1123" << endl;
+ return parseDateRFC1123(l, dt);
+ break;
+ default:
+// kpfDebug << "Date type is unknown" << endl;
+ return false;
+ break;
+ }
+
+ return false;
+ }
+
+ bool
+ parseDateRFC1123(const QStringList & l, QDateTime & dt)
+ {
+ if ("GMT" != l[5])
+ return false;
+
+ uint day(l[1].toUInt());
+
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == l[2])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint year(l[3].toUInt());
+
+ QStringList timeTokenList(QStringList::split(':', l[4]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ bool
+ parseDateRFC850(const QStringList & l, QDateTime & dt)
+ {
+ if ("GMT" != l[3])
+ return false;
+
+ QStringList dateTokenList(QStringList::split('-', l[1]));
+
+ if (3 != dateTokenList.count())
+ return false;
+
+ uint day(dateTokenList[0].toUInt());
+
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == dateTokenList[1])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint year(dateTokenList[2].toUInt());
+
+ if (year < 50)
+ year += 2000;
+ else if (year < 100)
+ year += 1900;
+
+ QStringList timeTokenList(QStringList::split(':', l[2]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ bool
+ parseDateAscTime(const QStringList & l, QDateTime & dt)
+ {
+ bool haveMonth = false;
+ uint month = 0;
+
+ QStringList::ConstIterator it;
+
+ for (it = monthList.begin(); it != monthList.end(); ++it)
+ {
+ if (*it == l[1])
+ {
+ haveMonth = true;
+ break;
+ }
+
+ ++month;
+ }
+
+ if (!haveMonth)
+ return false;
+
+ uint day(l[2].toUInt());
+
+ QStringList timeTokenList(QStringList::split(':', l[3]));
+
+ if (3 != timeTokenList.count())
+ return false;
+
+ uint hours (timeTokenList[0].toUInt());
+ uint minutes (timeTokenList[1].toUInt());
+ uint seconds (timeTokenList[2].toUInt());
+
+ uint year(l[4].toUInt());
+
+ dt.setDate(QDate(year, month + 1, day));
+ dt.setTime(QTime(hours, minutes, seconds));
+
+ return dt.isValid();
+ }
+
+ QString
+ translatedResponseName(uint code)
+ {
+ QString s;
+
+ switch (code)
+ {
+ case 200:
+ s = i18n("OK");
+ break;
+ case 206:
+ s = i18n("Partial content");
+ break;
+ case 304:
+ s = i18n("Not modified");
+ break;
+ case 400:
+ s = i18n("Bad request");
+ break;
+ case 403:
+ s = i18n("Forbidden");
+ break;
+ case 404:
+ s = i18n("Not found");
+ break;
+ case 412:
+ s = i18n("Precondition failed");
+ break;
+ case 416:
+ s = i18n("Bad range");
+ break;
+ case 500:
+ s = i18n("Internal error");
+ break;
+ case 501:
+ s = i18n("Not implemented");
+ break;
+ case 505:
+ s = i18n("HTTP version not supported");
+ break;
+ default:
+ s = i18n("Unknown");
+ break;
+ }
+
+ return s;
+ }
+
+ QString
+ responseName(uint code)
+ {
+ QString s;
+
+ switch (code)
+ {
+ case 200:
+ s = "OK";
+ break;
+ case 206:
+ s = "Partial content";
+ break;
+ case 304:
+ s = "Not modified";
+ break;
+ case 400:
+ s = "Bad request";
+ break;
+ case 403:
+ s = "Forbidden";
+ break;
+ case 404:
+ s = "Not found";
+ break;
+ case 412:
+ s = "Precondition failed";
+ break;
+ case 416:
+ s = "Bad range";
+ break;
+ case 500:
+ s = "Internal error";
+ break;
+ case 501:
+ s = "Not implemented";
+ break;
+ case 505:
+ s = "HTTP version not supported";
+ break;
+ default:
+ s = "Unknown";
+ break;
+ }
+
+ return s;
+ }
+
+
+} // End namespace KPF
+
+
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/Utils.h b/kpf/src/Utils.h
new file mode 100644
index 00000000..6a81dfa8
--- /dev/null
+++ b/kpf/src/Utils.h
@@ -0,0 +1,113 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_UTILS_H
+#define KPF_UTILS_H
+
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qfileinfo.h>
+
+/**
+ * Used to keep all parts of the kpf application out of the global namespace.
+ */
+namespace KPF
+{
+ /**
+ * Safe version of QMIN
+ * @return minimum of a and b.
+ */
+ template<class T> T min(T a, T b) { return a < b ? a : b; }
+
+ /**
+ * Safe version of QMAX
+ * @return minimum of a and b.
+ */
+ template<class T> T max(T a, T b) { return b < a ? a : b; }
+
+ /**
+ * @return the current date and time as an HTTP/1.1 compliant date string.
+ */
+ QString dateString();
+
+ /**
+ * @return the passed QDateTime as an HTTP/1.1 compliant date string.
+ */
+ QString dateString(const QDateTime & dt);
+
+ /**
+ * Parse an HTTP/1.1 date to a QDateTime.
+ * @param ret the QDateTime representation (result of parsing)
+ * @return true if the date is an an acceptable format.
+ */
+ bool parseDate(const QString &, QDateTime & ret);
+
+ /**
+ * Parse an RFC 1123 format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateRFC1123(const QStringList &, QDateTime &);
+
+ /**
+ * Parse an RFC 850 format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateRFC850(const QStringList &, QDateTime &);
+
+ /**
+ * Parse an asctime(3) format date to a QDateTime. Usually called by
+ * @ref parseDate.
+ */
+ bool parseDateAscTime(const QStringList &, QDateTime &);
+
+ /**
+ * @return i18n(HTTP response message for code)
+ */
+ QString translatedResponseName(uint code);
+
+ /**
+ * @return HTTP response message for code
+ */
+ QString responseName(uint code);
+
+ /**
+ * @return the passed QDateTime converted GMT, honouring daylight
+ * saving time if necessary.
+ */
+ QDateTime toGMT(const QDateTime &);
+
+ /**
+ * Unquote hex quoted chars in string.
+ */
+ QString unquote(const QString &);
+
+ /**
+ * Quote naughty chars in %xx format.
+ */
+ QString quote(const QString &);
+
+} // End namespace KPF
+
+#endif
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServer.cpp b/kpf/src/WebServer.cpp
new file mode 100644
index 00000000..37301f09
--- /dev/null
+++ b/kpf/src/WebServer.cpp
@@ -0,0 +1,644 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+// System includes
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+// Qt includes
+#include <qsocket.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+
+// KDE includes
+#include "config.h"
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <dnssd/publicservice.h>
+
+// Local includes
+#include "Defines.h"
+#include "Defaults.h"
+#include "WebServerSocket.h"
+#include "WebServer.h"
+#include "Server.h"
+#include "Utils.h"
+
+namespace KPF
+{
+ static const uint SamplesPerSecond = 10;
+ static const uint MaxBacklog = 1024;
+
+ class WebServer::Private
+ {
+ public:
+
+ Private()
+ : socket (0L),
+ listenPort (Config::DefaultListenPort),
+ connectionLimit (Config::DefaultConnectionLimit),
+ bandwidthLimit (Config::DefaultBandwidthLimit),
+ lastTotalOutput (0L),
+ totalOutput (0L),
+ portContention (true),
+ paused (false),
+ followSymlinks (Config::DefaultFollowSymlinks),
+ customErrorMessages (false)
+ {
+ }
+
+ ~Private()
+ {
+ delete socket;
+ delete service;
+ service = 0;
+ socket = 0;
+ }
+
+ WebServerSocket * socket;
+ uint listenPort;
+ uint connectionLimit;
+ QPtrList<Server> serverList;
+ QString root;
+ QString serverName;
+ QTimer writeTimer;
+ QTimer resetOutputTimer;
+ QTimer bindTimer;
+ QTimer backlogTimer;
+ ulong bandwidthLimit;
+ ulong lastTotalOutput;
+ ulong totalOutput;
+ bool portContention;
+ bool paused;
+ bool followSymlinks;
+ bool customErrorMessages;
+ QValueList<int> backlog;
+ DNSSD::PublicService* service;
+
+ };
+
+ WebServer::WebServer(const QString & root)
+ : DCOPObject(QCString("WebServer_") + root.utf8()),
+ QObject()
+ {
+ d = new Private;
+
+ d->root = root;
+ loadConfig();
+ publish();
+
+ connect(&d->bindTimer, SIGNAL(timeout()), SLOT(slotBind()));
+ connect(&d->writeTimer, SIGNAL(timeout()), SLOT(slotWrite()));
+ connect(&d->resetOutputTimer, SIGNAL(timeout()), SLOT(slotCheckOutput()));
+ connect(&d->backlogTimer, SIGNAL(timeout()), SLOT(slotClearBacklog()));
+
+ d->bindTimer .start( 0, true);
+ d->resetOutputTimer .start(1000 / SamplesPerSecond, false);
+ }
+
+
+ WebServer::WebServer
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ )
+ : DCOPObject(QCString("WebServer_") + root.utf8()),
+ QObject()
+ {
+ d = new Private;
+
+ d->root = root;
+
+ d->listenPort = listenPort;
+ d->bandwidthLimit = bandwidthLimit;
+ d->connectionLimit = connectionLimit;
+ d->followSymlinks = followSymlinks;
+ d->serverName = serverName;
+
+ saveConfig();
+ publish();
+ connect(&d->bindTimer, SIGNAL(timeout()), SLOT(slotBind()));
+ connect(&d->writeTimer, SIGNAL(timeout()), SLOT(slotWrite()));
+ connect(&d->resetOutputTimer, SIGNAL(timeout()), SLOT(slotCheckOutput()));
+ connect(&d->backlogTimer, SIGNAL(timeout()), SLOT(slotClearBacklog()));
+
+ d->bindTimer .start( 0, true);
+ d->resetOutputTimer .start(1000 / SamplesPerSecond, false);
+ }
+
+ WebServer::~WebServer()
+ {
+ killAllConnections();
+
+ delete d;
+ d = 0;
+ }
+
+ void WebServer::publish()
+ {
+ d->service = new DNSSD::PublicService(d->serverName,"_http._tcp",d->listenPort);
+ connect(d->service,SIGNAL(published(bool)),this,SLOT(wasPublished(bool)));
+ d->service->publishAsync();
+ }
+
+ void WebServer::wasPublished(bool ok)
+ {
+ if(ok) {
+ KMessageBox::information( NULL, i18n("Successfully published this new service to the network (ZeroConf)."), i18n( "Successfully Published the Service" ), "successfullypublished" );
+ kpfDebug << "Published to dnssd successfully" << endl;
+ }
+ else {
+ KMessageBox::information( NULL, i18n("Failed to publish this new service to the network (ZeroConf). The server will work fine without this, however."), i18n( "Failed to Publish the Service" ), "failedtopublish" );
+ }
+ }
+
+ void
+ WebServer::slotBind()
+ {
+ if (0 != d->socket)
+ {
+ qWarning("Uhhh, socket isn't 0, but I'm told to bind ?");
+ return;
+ }
+
+ d->socket = new WebServerSocket(d->listenPort, d->connectionLimit);
+
+ d->portContention = !d->socket->ok();
+
+ emit(contentionChange(d->portContention));
+
+ if (!d->portContention)
+ connect(d->socket, SIGNAL(connection(int)), SLOT(slotConnection(int)));
+
+ else
+ {
+ delete d->socket;
+ d->socket = 0;
+ d->bindTimer.start(1000, true);
+ }
+ }
+
+ void
+ WebServer::slotConnection(int fd)
+ {
+ if (!d->backlog.isEmpty())
+ {
+ if (d->backlog.count() < MaxBacklog)
+ {
+ kpfDebug << "Adding this connection to the backlog." << endl;
+ d->backlog.append(fd);
+ }
+ else
+ {
+ kpfDebug << "Backlog full. Ignoring this connection." << endl;
+ }
+ return;
+ }
+
+ if (!handleConnection(fd))
+ {
+ if (d->backlog.count() < MaxBacklog)
+ {
+ kpfDebug << "Adding this connection to the backlog." << endl;
+ d->backlog.append(fd);
+ d->backlogTimer.start(10, true);
+ }
+ else
+ {
+ kpfDebug << "Backlog full. Ignoring this connection." << endl;
+ }
+ }
+ }
+
+ bool
+ WebServer::handleConnection(int fd)
+ {
+ if (d->paused)
+ {
+ kpfDebug << "Paused." << endl;
+ return false;
+ }
+
+ if (d->serverList.count() >= d->connectionLimit)
+ {
+// kpfDebug << "Hit connection limit." << endl;
+ return false;
+ }
+
+ int on = 1;
+
+ ::setsockopt
+ (
+ fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ ( char* )&on,
+ sizeof( on ) );
+
+ on = 0;
+
+ ::setsockopt
+ (
+ fd,
+ SOL_SOCKET,
+ SO_LINGER,
+ ( char* ) &on,
+ sizeof( on ) );
+
+ Server * s = new Server(d->root, d->followSymlinks, fd, this);
+
+ connect
+ (
+ s,
+ SIGNAL(output(Server *, ulong)),
+ SLOT(slotOutput(Server *, ulong))
+ );
+
+ connect(s, SIGNAL(finished(Server *)), SLOT(slotFinished(Server *)));
+ connect(s, SIGNAL(request(Server *)), SIGNAL(request(Server *)));
+ connect(s, SIGNAL(response(Server *)), SIGNAL(response(Server *)));
+
+ d->serverList.append(s);
+
+ connect
+ (s, SIGNAL(readyToWrite(Server *)), SLOT(slotReadyToWrite(Server *)));
+
+ emit(connection(s));
+
+ return true;
+ }
+
+ void
+ WebServer::restart()
+ {
+ d->bindTimer.stop();
+
+ killAllConnections();
+ delete d->socket;
+ d->socket = 0;
+ d->service->setServiceName(d->serverName);
+ d->service->setPort(d->listenPort);
+ d->bindTimer.start(0, true);
+ }
+
+ void
+ WebServer::killAllConnections()
+ {
+ QPtrListIterator<Server> it(d->serverList);
+
+ for (; it.current(); ++it)
+ it.current()->cancel();
+
+ }
+
+ void
+ WebServer::slotOutput(Server * s, ulong l)
+ {
+ emit(output(s, l));
+ }
+
+ void
+ WebServer::slotFinished(Server * s)
+ {
+ emit(finished(s));
+ d->serverList.removeRef(s);
+ delete s;
+ s = 0;
+ }
+
+ void
+ WebServer::setBandwidthLimit(ulong l)
+ {
+ d->bandwidthLimit = l;
+ saveConfig();
+ }
+
+ ulong
+ WebServer::bandwidthLimit()
+ {
+ return d->bandwidthLimit;
+ }
+
+ void
+ WebServer::setFollowSymlinks(bool b)
+ {
+ d->followSymlinks = b;
+ saveConfig();
+ }
+
+ bool
+ WebServer::followSymlinks()
+ {
+ return d->followSymlinks;
+ }
+
+ QString
+ WebServer::root()
+ {
+ return d->root;
+ }
+
+ uint
+ WebServer::connectionLimit()
+ {
+ return d->connectionLimit;
+ }
+
+ void
+ WebServer::setConnectionLimit(uint i)
+ {
+ d->connectionLimit = i;
+ saveConfig();
+ }
+
+ uint
+ WebServer::listenPort()
+ {
+ return d->listenPort;
+ }
+
+ void
+ WebServer::setListenPort(uint i)
+ {
+ d->listenPort = i;
+ saveConfig();
+ }
+
+ bool
+ WebServer::customErrorMessages()
+ {
+ return d->customErrorMessages;
+ }
+
+ void
+ WebServer::setCustomErrorMessages(bool b)
+ {
+ d->customErrorMessages = b;
+ saveConfig();
+ }
+
+ ulong
+ WebServer::bytesLeft() const
+ {
+#if 0
+ // Multiply the bandwidth limit by 10 so we can do 10 checks per second.
+
+ ulong l =
+ (d->bandwidthLimit * 10240) - (d->totalOutput - d->lastTotalOutput);
+#endif
+
+ ulong l =
+ ulong(d->bandwidthLimit * (1024 / double(SamplesPerSecond)))
+ - (d->totalOutput - d->lastTotalOutput);
+
+ return l;
+ }
+
+ ulong
+ WebServer::bandwidthPerClient() const
+ {
+ ulong l = 0L;
+
+ if (!d->serverList.isEmpty())
+ {
+ l = bytesLeft() / d->serverList.count();
+ }
+
+ kpfDebug << l << endl;
+
+ return l;
+ }
+
+ void
+ WebServer::slotReadyToWrite(Server *)
+ {
+ d->writeTimer.stop();
+ d->writeTimer.start(0, true);
+ }
+
+ void
+ WebServer::slotWrite()
+ {
+ if (d->serverList.isEmpty())
+ return;
+
+ QPtrListIterator<Server> it(d->serverList);
+
+ for (; it.current(); ++it)
+ {
+ if (0 == bytesLeft())
+ break;
+
+ Server * s = it.current();
+
+ if (0 == s->bytesLeft())
+ continue;
+
+ ulong bytesAvailable = 0;
+
+ if (0 == bandwidthPerClient())
+ bytesAvailable = bytesLeft();
+ else
+ bytesAvailable = min(s->bytesLeft(), bandwidthPerClient());
+
+ if (0 != bytesAvailable)
+ d->totalOutput += s->write(bytesAvailable);
+ }
+
+ d->writeTimer.start(1000 / SamplesPerSecond, true);
+ }
+
+ void
+ WebServer::slotCheckOutput()
+ {
+ emit(connectionCount(d->serverList.count()));
+ ulong lastOutput = d->totalOutput - d->lastTotalOutput;
+ emit(wholeServerOutput(ulong(lastOutput * SamplesPerSecond)));
+ d->lastTotalOutput = d->totalOutput;
+ }
+
+ void
+ WebServer::slotClearBacklog()
+ {
+// kpfDebug << "WebServer::slotClearBacklog" << endl;
+
+ if (!d->backlog.isEmpty())
+ {
+ uint currentBacklogCount = d->backlog.count();
+
+ for (uint i = 0; i < currentBacklogCount; i++)
+ {
+ int fd = d->backlog.first();
+
+ if (handleConnection(fd))
+ {
+ kpfDebug
+ << "Ah, we can now handle this connection. Removing from backlog."
+ << endl;
+
+ d->backlog.remove(d->backlog.begin());
+ }
+ else
+ {
+// kpfDebug
+// << "Still can't handle this connection. Leaving in backlog"
+// << endl;
+
+ break;
+ }
+ }
+ }
+
+ if (!d->backlog.isEmpty())
+ {
+ d->backlogTimer.start(10, true);
+ }
+ }
+
+ uint
+ WebServer::connectionCount()
+ {
+ return d->serverList.count();
+ }
+
+ void
+ WebServer::pause(bool b)
+ {
+ if(b == d->paused) return;
+
+ d->paused = b;
+ if (b) d->service->stop();
+ else d->service->publishAsync(); //published() should be already connected
+ emit pauseChange(d->paused);
+ saveConfig();
+ }
+
+ bool
+ WebServer::paused()
+ {
+ return d->paused;
+ }
+ QString
+ WebServer::serverName()
+ {
+ return d->serverName;
+ }
+ void
+ WebServer::setServerName(const QString& serverName)
+ {
+ d->serverName=serverName;
+ }
+
+ bool
+ WebServer::portContention()
+ {
+ return d->portContention;
+ }
+
+ void
+ WebServer::set
+ (
+ uint listenPort,
+ ulong bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString& serverName
+ )
+ {
+ d->listenPort = listenPort;
+ d->bandwidthLimit = bandwidthLimit;
+ d->connectionLimit = connectionLimit;
+ d->followSymlinks = followSymlinks;
+ d->serverName = serverName;
+
+ saveConfig();
+ }
+
+ void
+ WebServer::loadConfig()
+ {
+ kpfDebug << "WebServer(" << d->root << "): Loading configuration" << endl;
+ KConfig c(Config::name());
+
+ c.setGroup(Config::key(Config::GroupPrefix) + d->root);
+
+ d->listenPort =
+ c.readUnsignedNumEntry
+ (Config::key(Config::ListenPort), d->listenPort);
+
+ d->bandwidthLimit =
+ c.readUnsignedNumEntry
+ (Config::key(Config::BandwidthLimit), d->bandwidthLimit);
+
+ d->connectionLimit =
+ c.readUnsignedNumEntry
+ (Config::key(Config::ConnectionLimit), d->connectionLimit);
+
+ d->followSymlinks =
+ c.readBoolEntry
+ (Config::key(Config::FollowSymlinks), d->followSymlinks);
+
+ d->customErrorMessages =
+ c.readBoolEntry
+ (Config::key(Config::CustomErrors), d->customErrorMessages);
+
+ d->paused =
+ c.readBoolEntry
+ (Config::key(Config::Paused), d->paused);
+
+ d->serverName =
+ c.readEntry
+ (Config::key(Config::ServerName), d->serverName);
+
+ }
+
+ void
+ WebServer::saveConfig()
+ {
+ kpfDebug << "WebServer(" << d->root << "): Saving configuration" << endl;
+ KConfig c(Config::name());
+
+ c.setGroup(Config::key(Config::GroupPrefix) + d->root);
+
+ c.writeEntry(Config::key(Config::ListenPort), d->listenPort);
+ c.writeEntry(Config::key(Config::BandwidthLimit), d->bandwidthLimit);
+ c.writeEntry(Config::key(Config::ConnectionLimit), d->connectionLimit);
+ c.writeEntry(Config::key(Config::FollowSymlinks), d->followSymlinks);
+ c.writeEntry(Config::key(Config::CustomErrors), d->customErrorMessages);
+ c.writeEntry(Config::key(Config::Paused), d->paused);
+ c.writeEntry(Config::key(Config::ServerName), d->serverName);
+
+ c.sync();
+ }
+
+} // End namespace KPF
+
+#include "WebServer.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServer.h b/kpf/src/WebServer.h
new file mode 100644
index 00000000..ae9d9383
--- /dev/null
+++ b/kpf/src/WebServer.h
@@ -0,0 +1,346 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_WEB_SERVER_H
+#define KPF_WEB_SERVER_H
+
+#include <dcopobject.h>
+#include <qserversocket.h>
+
+#include "Defaults.h"
+#include "Request.h"
+#include "Response.h"
+
+namespace KPF
+{
+ class Server;
+
+ /**
+ * Listens on a port for incoming connections.
+ * Creates and manages Server objects.
+ * Manages bandwidth limit, dealing out bandwidth to Server objects.
+ * Maintains concurrent connection limit, using a backlog to queue incoming
+ * connections which cannot be served immediately.
+ */
+ class WebServer : public QObject, virtual public DCOPObject
+ {
+ K_DCOP
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Only root dir specified - this causes an immediate loadConfig.
+ *
+ * @param root Virtual root directory for servers. Passed to all created
+ * Server objects, which much ensure that only files from the root and
+ * its child directories are served.
+ */
+ WebServer(const QString & root);
+
+ /**
+ * @param root Virtual root directory for servers. Passed to all created
+ * Server objects, which much ensure that only files from the root and
+ * its child directories are served.
+ */
+ WebServer
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ );
+
+ virtual ~WebServer();
+
+ /**
+ * Load the configuration, but do not kill existing connections even if
+ * listen port is changed. Do not change listen port yet - only when
+ * asked to restart.
+ */
+ void loadConfig();
+
+ k_dcop:
+
+ /**
+ * @return virtual root.
+ */
+ QString root();
+
+ /**
+ * @return server name
+ */
+ QString serverName();
+
+ /**
+ * @return amount of bytes that may be sent out per second, in total
+ * (adding the output of all contained Server objects.)
+ */
+ ulong bandwidthLimit();
+
+ /**
+ * @return number of concurrent connections that will be served (by
+ * creating Server objects. More connections may wait in a backlog.)
+ */
+ uint connectionLimit();
+
+ /**
+ * @return port on which to listen for incoming connections.
+ */
+ uint listenPort();
+
+ /**
+ * @return true if requests may include symbolic links in their path.
+ */
+ bool followSymlinks();
+
+ /**
+ * @return true if custom error messages (set by the user) should be
+ * sent.
+ */
+ bool customErrorMessages();
+
+ /**
+ * Set the maximum amount of bytes that may be sent out per second, in
+ * total (adding the output of all contained Server objects.)
+ */
+ void setBandwidthLimit (ulong);
+
+ /**
+ * Set the number of concurrent connections that will be served (by
+ * creating Server objects. More connections may wait in a backlog.)
+ */
+ void setConnectionLimit (uint);
+
+ /**
+ * Set the port on which to listen for incoming connections. Does not
+ * take effect until restart() is called.
+ */
+ void setListenPort (uint);
+
+ /**
+ * Set server name
+ */
+ void setServerName (const QString&);
+
+ /**
+ * Set whether requests may include symbolic links in their path.
+ */
+ void setFollowSymlinks (bool);
+
+ /**
+ * Set whether custom error messages (set by the user) should be
+ * sent.
+ */
+ void setCustomErrorMessages (bool);
+
+ /**
+ * Convenience method for setting many attributes at once.
+ */
+ void set
+ (
+ uint listenPort,
+ ulong bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString& serverName
+ );
+
+ /**
+ * Kill all connections, stop listening on port, and start listening on
+ * (possibly different) port.
+ */
+ void restart();
+
+ /**
+ * Start / stop accepting new connections.
+ */
+ void pause(bool);
+
+ /**
+ * @return true if no new connections are accepted.
+ */
+ bool paused();
+
+ /**
+ * @return true if this WebServer is unable to listen on the requested
+ * port.
+ */
+ bool portContention();
+
+ /**
+ * @return number of Server objects serving requests.
+ */
+ uint connectionCount();
+
+ protected slots:
+
+ /**
+ * Called repeatedly by a timer when this WebServer is in contention for
+ * its listen port.
+ */
+ void slotBind ();
+
+ /**
+ * Called by contained socket object when a new incoming connection is
+ * made.
+ */
+ void slotConnection (int);
+
+ /**
+ * Called by a Server when it has finished all transactions and is ready
+ * to die.
+ */
+ void slotFinished (Server *);
+
+ /**
+ * Called by a Server when it has sent data to the remote client.
+ */
+ void slotOutput (Server *, ulong);
+
+ /**
+ * Called by a Server when it wishes to send data to the remote client.
+ */
+ void slotReadyToWrite (Server *);
+
+ /**
+ * Called regularly by a timer to start output allocation (to Server
+ * objects.)
+ */
+ void slotWrite ();
+
+ /**
+ * Called regularly by a timer to check output for current time slice.
+ */
+ void slotCheckOutput ();
+
+ /**
+ * Called regularly by a timer to handle connections queued in the
+ * backlog.
+ */
+ void slotClearBacklog ();
+
+ /**
+ * Called when this succesfully publishes via zeroconf, or there was an
+ * error doing so.
+ */
+ void wasPublished(bool ok);
+
+ protected:
+
+ /**
+ * Attempt to create a Server to handle a new connection.
+ * @param fd file descriptor.
+ */
+ bool handleConnection(int fd);
+
+ void saveConfig();
+
+ signals:
+
+ /**
+ * @param bytes number of bytes sent by this server during the last
+ * time slice.
+ */
+ void wholeServerOutput (ulong bytes);
+
+ /**
+ * Emitted when a Server object has received a request from its remote
+ * client.
+ */
+ void request (Server *);
+
+ /**
+ * Emitted when a Server object has created a response which it wishes
+ * to send to its remote client.
+ */
+ void response (Server *);
+
+ /**
+ * Emitted when a Server object when data has been send to remote client.
+ * @param bytes number of bytes sent to remote client.
+ */
+ void output (Server *, ulong bytes);
+
+ /**
+ * Emitted when a new Server has been created.
+ */
+ void connection (Server *);
+
+ /**
+ * Emitted when a Server has finished all transactions.
+ */
+ void finished (Server *);
+
+ /**
+ * Emitted when this WebServer is now contending, or has stopped
+ * contending, its listen port.
+ */
+ void contentionChange (bool);
+
+ /**
+ * Emitted when this WebServer is now accepting, or has stopped
+ * accepting, incoming connections.
+ */
+ void pauseChange (bool);
+
+ /**
+ * Emitted when the number active Server objects (served connections)
+ * changes.
+ */
+ void connectionCount (uint);
+
+ private:
+
+ /**
+ * @return number of bytes that may be sent out. Changes over time,
+ * depending on bandwidth limit.
+ */
+ ulong bytesLeft() const;
+
+ /**
+ * @return number of bytes that may be sent out by each Server, with the
+ * total split between the existing Server objects.
+ */
+ ulong bandwidthPerClient() const;
+
+
+ /** publish this to dns-sd (zeroconf)
+ */
+ void publish();
+ /**
+ * Cause all existing Server objects to close their connections by
+ * calling Server::cancel.
+ */
+ void killAllConnections();
+
+ class Private;
+ Private * d;
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerManager.cpp b/kpf/src/WebServerManager.cpp
new file mode 100644
index 00000000..feec661d
--- /dev/null
+++ b/kpf/src/WebServerManager.cpp
@@ -0,0 +1,295 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+// KDE includes
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kapplication.h>
+
+// Local includes
+#include "Defines.h"
+#include "WebServerManager.h"
+#include "WebServer.h"
+#include "WebServer_stub.h"
+
+namespace KPF
+{
+ WebServerManager * WebServerManager::instance_ = 0L;
+
+ WebServerManager *
+ WebServerManager::instance()
+ {
+ if (0 == instance_)
+ instance_ = new WebServerManager;
+
+ return instance_;
+ }
+
+ void
+ WebServerManager::shutdown()
+ {
+ delete instance_;
+ instance_ = 0;
+ }
+
+ WebServerManager::WebServerManager()
+ : DCOPObject("WebServerManager"),
+ QObject()
+ {
+ serverList_.setAutoDelete(true);
+ }
+
+ WebServerManager::~WebServerManager()
+ {
+ // Empty.
+ }
+
+ QPtrList<WebServer>
+ WebServerManager::serverListLocal()
+ {
+ return serverList_;
+ }
+
+ WebServer *
+ WebServerManager::createServerLocal
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ const QString & serverName
+ )
+ {
+ if (0 != server(root))
+ return 0;
+ if ( listenPort == 0)
+ listenPort = nextFreePort();
+ WebServer * server =
+ new WebServer
+ (
+ root,
+ listenPort,
+ bandwidthLimit,
+ connectionLimit,
+ followSymlinks,
+ serverName
+ );
+
+ serverList_.append(server);
+
+ saveConfig();
+
+ emit(serverCreated(server));
+
+ return server;
+ }
+
+ void
+ WebServerManager::loadConfig()
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QStringList serverRootList = config.readListEntry("ServerRootList");
+
+ QStringList::ConstIterator it;
+
+ for (it = serverRootList.begin(); it != serverRootList.end(); ++it)
+ {
+ WebServer * s = new WebServer(*it);
+ serverList_.append(s);
+ s->loadConfig();
+ emit(serverCreated(s));
+ }
+ }
+
+ void
+ WebServerManager::saveConfig() const
+ {
+ KConfig config(Config::name());
+
+ config.setGroup("General");
+
+ QPtrListIterator<WebServer> it(serverList_);
+
+ QStringList serverRootList;
+
+ for (; it.current(); ++it)
+ serverRootList << it.current()->root();
+
+ config.writeEntry("ServerRootList", serverRootList);
+
+ config.sync();
+ }
+
+ WebServer *
+ WebServerManager::server(const QString & root)
+ {
+ QPtrListIterator<WebServer> it(serverList_);
+
+ for (; it.current(); ++it)
+ {
+ kpfDebug << "WebServerManager::server(): found root of " <<
+ "\"" << it.current()->root() << "\"" << endl;
+
+ if (it.current()->root() == root)
+ {
+ kpfDebug
+ << "WebServerManager::server(" << root << "): found" << endl;
+ return it.current();
+ }
+ }
+
+ kpfDebug
+ << "WebServerManager::server(" << root << "): not found" << endl;
+ return 0;
+ }
+
+ bool
+ WebServerManager::disableServer(const QString & root)
+ {
+ WebServer * existing = server(root);
+
+ if (0 == existing)
+ {
+ return false;
+ }
+ else
+ {
+ emit(serverDisabled(existing));
+ // Auto-deleted by list.
+ serverList_.removeRef(existing);
+ saveConfig();
+ return true;
+ }
+ }
+
+ QValueList<DCOPRef>
+ WebServerManager::serverList()
+ {
+ QValueList<DCOPRef> l;
+
+ QPtrListIterator<WebServer> it(serverList_);
+
+ for (; it.current(); ++it)
+ l << DCOPRef(it.current());
+
+ return l;
+ }
+
+ DCOPRef
+ WebServerManager::createServer
+ (
+ QString root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ QString serverName
+ )
+ {
+ WebServer * server = createServerLocal
+ (root, listenPort, bandwidthLimit, connectionLimit, followSymlinks, serverName);
+
+ if (0 == server)
+ return DCOPRef();
+ else
+ return DCOPRef(server);
+ }
+
+ void
+ WebServerManager::disableServer(DCOPRef serverRef)
+ {
+ if (serverRef.isNull())
+ return;
+
+ WebServer_stub webServer
+ (serverRef.app(), serverRef.object());
+
+ QString root = webServer.root();
+
+ if (DCOPStub::CallFailed == webServer.status())
+ {
+ kpfDebug << "Real shitty mess here" << endl;
+ return;
+ }
+
+ bool ok = disableServer(root);
+
+ if (!ok)
+ {
+ kpfDebug << "Definitely a real shitty mess here" << endl;
+ return;
+ }
+ }
+
+ void
+ WebServerManager::quit()
+ {
+// kapp->quit();
+ }
+
+ bool
+ WebServerManager::hasServer(const QString & s)
+ {
+ QString root(s);
+
+ if ('/' == root.at(root.length() - 1))
+ {
+ root.truncate(root.length() - 1);
+ }
+
+ return (0 != server(root) || 0 != server(root + "/"));
+ }
+
+ uint WebServerManager::nextFreePort() const
+ {
+ for (uint port = Config::DefaultListenPort; port < 65536; ++port)
+ {
+ bool ok = true;
+
+ for (QPtrListIterator<WebServer> it(serverList_); it.current(); ++it)
+ {
+ if (it.current()->listenPort() == port)
+ {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok)
+ {
+ return port;
+ }
+ }
+
+ // Not much we can do.
+ return Config::DefaultListenPort;
+ }
+
+} // End namespace KPF
+
+#include "WebServerManager.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerManager.h b/kpf/src/WebServerManager.h
new file mode 100644
index 00000000..6faa8a30
--- /dev/null
+++ b/kpf/src/WebServerManager.h
@@ -0,0 +1,173 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef KPF_WEB_SERVER_MANAGER_H
+#define KPF_WEB_SERVER_MANAGER_H
+
+#include <dcopobject.h>
+#include <dcopref.h>
+
+#include "Defaults.h"
+
+#include <qptrlist.h>
+
+namespace KPF
+{
+ class WebServer;
+
+ /**
+ * Singleton, encapsulating a set of WebServer objects. Handles
+ * creating WebServer objects at startup (based on settings) and
+ * on demand. Destroys WebServer objects on demand.
+ */
+ class WebServerManager : public QObject, virtual public DCOPObject
+ {
+ Q_OBJECT
+ K_DCOP
+
+ public:
+
+ static WebServerManager * instance();
+
+ /**
+ * Calls delete(this).
+ */
+ void shutdown();
+
+ /**
+ * @return a list of pointers to WebServer objects managed
+ * by this object.
+ */
+ QPtrList<WebServer> serverListLocal();
+
+ /**
+ * @return a pointer to a new WebServer object, with the root
+ * as specified, or 0 if creation was impossible. Updates
+ * the configuration.
+ */
+ WebServer * createServerLocal
+ (
+ const QString & root,
+ uint listenPort,
+ uint bandwidthLimit = Config::DefaultBandwidthLimit,
+ uint connectionLimit = Config::DefaultConnectionLimit,
+ bool followSymlinks = Config::DefaultFollowSymlinks,
+ const QString & serverName = QString::null
+ );
+
+ /**
+ * Disables a WebServer and updates the configuration.
+ */
+ bool disableServer(const QString & root);
+
+ /**
+ * Loads the configuration.
+ * Creates WebServer objects to match the configuration and
+ * ensures each object loads its configuration.
+ */
+ void loadConfig();
+
+ /**
+ * Saves the configuration.
+ * Also ensures each WebServer object saves its configuration.
+ */
+ void saveConfig() const;
+
+ /**
+ * Find a WebServer or return 0.
+ */
+ WebServer * server(const QString & root);
+
+ /**
+ * Ask a server to re-read its configuration.
+ */
+ bool reconfigureServer(const QString & root);
+
+ /**
+ * Pause/unpause a server.
+ */
+ bool pauseServer(const QString & root, bool);
+
+ /**
+ * @return whether the server is paused.
+ */
+ bool serverPaused(const QString & root);
+
+ /**
+ * Restart a server.
+ */
+ bool restartServer(const QString & root);
+
+ /**
+ * @return if a Server object with the specified root exists. Handles
+ * the two possible variations of trailing slash, i.e. existing and not
+ * existing.
+ */
+ bool hasServer(const QString & root);
+
+ uint nextFreePort() const;
+
+ k_dcop:
+
+ QValueList<DCOPRef> serverList();
+
+ DCOPRef createServer
+ (
+ QString root,
+ uint listenPort,
+ uint bandwidthLimit,
+ uint connectionLimit,
+ bool followSymlinks,
+ QString serverName
+ );
+
+ void disableServer(DCOPRef);
+
+ void quit();
+
+ protected:
+
+ /**
+ * Not used, as this is a singleton.
+ */
+ WebServerManager();
+
+ virtual ~WebServerManager();
+
+ signals:
+
+ void serverCreated(WebServer *);
+ void serverDisabled(WebServer *);
+
+ private:
+
+ static WebServerManager * instance_;
+
+ void load();
+ QPtrList<WebServer> serverList_;
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_MANAGER_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerSocket.cpp b/kpf/src/WebServerSocket.cpp
new file mode 100644
index 00000000..4dfa6626
--- /dev/null
+++ b/kpf/src/WebServerSocket.cpp
@@ -0,0 +1,44 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "Defines.h"
+#include "WebServerSocket.h"
+
+namespace KPF
+{
+ WebServerSocket::WebServerSocket(Q_UINT16 port, uint maxconn)
+ : QServerSocket(port, maxconn, 0L)
+ {
+ // Empty.
+ }
+
+ void
+ WebServerSocket::newConnection(int fd)
+ {
+ emit(connection(fd));
+ }
+
+} // End namespace KPF
+
+#include "WebServerSocket.moc"
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kpf/src/WebServerSocket.h b/kpf/src/WebServerSocket.h
new file mode 100644
index 00000000..f45e0d70
--- /dev/null
+++ b/kpf/src/WebServerSocket.h
@@ -0,0 +1,51 @@
+/*
+ KPF - Public fileserver for KDE
+
+ Copyright 2001 Rik Hemsley (rikkus) <rik@kde.org>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef WEB_SERVER_SOCKET_H
+#define WEB_SERVER_SOCKET_H
+
+#include <qserversocket.h>
+
+namespace KPF
+{
+ /**
+ * Overridden to emit a signal on a new connection.
+ */
+ class WebServerSocket : public QServerSocket
+ {
+ Q_OBJECT
+
+ public:
+
+ WebServerSocket(Q_UINT16 port, uint maxconn);
+ virtual void newConnection(int fd);
+
+ signals:
+
+ void connection(int);
+ };
+
+} // End namespace KPF
+
+#endif // WEB_SERVER_SOCKET_H
+// vim:ts=2:sw=2:tw=78:et
diff --git a/kppp/AUTHORS b/kppp/AUTHORS
new file mode 100644
index 00000000..7c2540ec
--- /dev/null
+++ b/kppp/AUTHORS
@@ -0,0 +1,8 @@
+Developers:
+* Bernd Wuebben <wuebben@kde.org> : started kppp
+* Mario Weilguni <mweilguni@kde.org> : co-author
+* Harri Porten <porten@kde.org> : co-author, current maintainer
+
+Documentation Translators:
+
+Translators:
diff --git a/kppp/ChangeLog b/kppp/ChangeLog
new file mode 100644
index 00000000..58dfc849
--- /dev/null
+++ b/kppp/ChangeLog
@@ -0,0 +1,1711 @@
+2005-07-03 Harri Porten <porten@kde.org>
+
+ * pppstats.cpp: patch by Ivan that makes the PPP interface check
+ more portable. On the BSD IFF_RUNNING is already set right after
+ pppd starts - too early for our purposes. Avoids potential
+ authentication problems.
+
+2005-07-01 Harri Porten <porten@kde.org>
+
+ * connect.cpp: fixing the flow control fix. Ivan Vasilyev spotted
+ yet another issue with translated strings.
+
+2005-04-12 Harri Porten <porten@kde.org>
+
+ * pppstatdlg.cpp: improved byte number formatting patch by 'Walter'
+
+2005-03-18 Harri Porten <porten@kde.org>
+
+ * connect.cpp: fixed another occurence of a i18n bug with the flow
+ control setting as reported by Brendon Higgins in #95502. Will surely
+ fix some connection problems.
+
+2004-11-15 Harri Porten <porten@kde.org>
+
+ * edit.cpp (DialWidget): increased max length of callback number
+
+2004-07-11 Harri Porten <porten@kde.org>
+
+ * kpppwidget.cpp: to save some screen space: show the modem combo
+ box only if more than one modem has been set up.
+
+ * general.cpp (ModemWidget): fixed name label of modem dialog
+
+2004-05-28 Harri Porten <porten@kde.org>
+
+ * devices.h: added /dev/ttyS4 as requested by Christopher Martin for
+ PCI hardware modem in 2.6 kernels (#82345)
+
+ * general.cpp: made modem combo box read-only again. Editing device
+ names didn't really work (#82345).
+
+2004-05-19 Harri Porten <porten@kde.org>
+
+ * modem.cpp: fixed flow control for non-English users. Bug found by
+ Elias Jahn (#73646).
+
+2004-05-14 Simone Gotti <simone.gotti@email.it>
+ * Now kppp has the multiple modem profile features, so it finally
+ can manage a lot of modems and not only one!
+ It can use the old kppp config copying the [Modem] group to the
+ [Modem0] group if it doesn't already exists.
+ See Bugs: #52028
+
+2004-04-24 Harri Porten <porten@kde.org>
+
+ * connect.cpp: added patch by Dmitri that allows automatic redials
+ on NO CARRIER.
+
+ * provide automatic CBCP (Windows) callback support. Patch by Nick
+ Shafff.
+
+2004-04-12 Gerardo Puga <gpuga@gioia.ing.unlp.edu.ar>
+
+ * Now kppp will notify other applications using DCOP about
+ connection status changes.
+
+2004-01-07 Harri Porten <porten@kde.org>
+
+ * modem.cpp: fix a bug in writing of modem commands. EAGAIN errors
+ were not handled. Thanks to Matteo Croce who helped tracking this
+ down.
+
+2003-12-13 Harri Porten <porten@kde.org>
+
+ * edit.cpp: disallow colons in connection name. breaks the loginfo
+ format
+
+2003-07-16 Harri Porten <porten@froglogic.com>
+
+ * connect.cpp: Digital Line Protection (DLP) support contributed
+ by Dorian Araneda <dorian.araneda at intel.com>
+
+2003-02-18 Harri Porten <porten@kde.org>
+
+ * added keyboard accelerators, layout fix (patch from
+ Mikolaj Machowski)
+
+2002-12-22 Harri Porten <porten@kde.org>
+
+ * acctselect.cpp: allow %xx escapes in ruleset file names.
+
+2002-10-27 Harri Porten <porten@trolltech.com>
+
+ * kpppconfig.h: increased MAX_ACCOUNTS to 100
+ * connect.cpp: fix for race condition in Expect command by
+ Dag Nygren dag at newtech.fi
+
+2002-04-12 Nadeem Hasan <nhasan@kde.org>
+ * Lots of GUI and layout cleanups. Added accels.
+
+2002-01-27 Per Winkvist <nd96pwt@student.hig.se>
+
+ * Add option to tune ATS11 "tone duration".
+
+2002-01-22 Per Winkvist <nd96pwt@student.hig.se>
+
+ * Make "Connect To" button default.
+ * Move KPPPWidget in main.cpp to an own file which reduced
+ the size of main.cpp with almost 1000 lines.
+
+2002-01-21 Chris Howells <howells@kde.org>
+ * Made the QMultiLineEdit on the Login Debug Window
+ read-only
+ * Fixed the bug in the connected window that meant
+ that the volume valued didn't display when volume
+ was >~2.5GB
+
+2002-01-21 Per Winkvist <nd96pwt@student.hig.se>
+
+ * Set focus correctly to ID/PWD on startup if one of the fields
+ are empty.
+
+2002-01-01 Harri Porten <porten@kde.org>
+
+ * don't ignore SIGHUP anymore. Forbidding Ctrl-Z is crazy.
+ * a bit more respect for Session Managment by adding handlers
+ for KApplication::saveYourSelf() and shutDown(). A user
+ had reported corrupted config data.
+ * made 'Log' a toggle button to represent debug window state
+
+2001-11-30 Harri Porten <porten@kde.org>
+
+ * main.cpp: set focus to password field if it's missing on startup.
+ Patch by Per Winkvist <nd96pwt at adeptus.student.hig.se>.
+
+2001-11-26 Harri Porten <porten@kde.org>
+
+ * applied patch for USB modems by Oliver Gantz <Oliver.Gantz@epost.de>
+
+2001-11-23 Harri Porten <porten@kde.org>
+
+ * connect.cpp: finally implemented often requested feature:
+ multiple init strings. Only two for now but it's now easy
+ to increase (PPPData::NumInitStrings + GUI)
+
+2001-11-08 Harri Porten <porten@kde.org>
+
+ * pppdata.cpp: fixed renaming of default account. Bug reported
+ by william.stephenson at ncl.ac.uk.
+
+2001-05-21 Harri Porten <porten@kde.org>
+
+ * modem.cpp: 460800 speed fix from msade@cs.pitt.edu
+
+2001-04-13 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: fixed startup crash if 'pppd --version' failed
+ * combined PAP/CHAP patch by Ingo Heeskens <ingo@fivemile.org>
+
+2001-04-08 Harri Porten <porten@kde.org>
+
+ * option for turning off dial tone detection. Modem string
+ defaults to ATX3.
+ * Solaris fixes: added missing include, less restrictive compile check
+ * main.cpp: deactivated KNotify::beep() in pppd-died signal handler,
+ due to appearant race conditions when working remote
+ * ppplog.cpp: hardcoded list of syslog files, including my Debian one
+ * pppdata.cpp: i18n'ed name of account copy
+ * ...
+
+>>>>>>>>>>>>>>>>>>>>>>>>>>>> KDE 2.1.0 released <<<<<<<<<<<<<<<<<<<<<<<<<<<
+2001-01-05 Harri Porten <porten@kde.org>
+
+ * Solaris patches by Keith Refson <Keith.Refson@earth.ox.ac.uk>
+ and Stephen Usher <Stephen.Usher@earth.ox.ac.uk>. Still needs some
+ tweaking.
+ * logview/monthly.cpp: sensible sort order for each column. cleanup.
+
+>>>>>>>>>>>>>>>>>>>>>>>>>>>> KDE 2.0.1 released <<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+2000-11-27 Harri Porten <harri@trolltech.com>
+
+ * applied 7 bit fix from KDE 1.x for CompuServe users.
+
+2000-11-21 Harri Porten <harri@trolltech.com>
+
+ * modem.cpp: writeLine() fix from Wolfgang Grandegger. Hit USB modem
+ owners and probably more.
+
+>>>>>>>>>>>>>>>>>>>>>>>>>>>> KDE 2.0 released <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+1999-11-18 Harri Porten <porten@kde.org>
+
+ * main.cpp: cleaned up signal handlers by posting a user defined
+ event to Qt's event loop. The real X stuff will be done in member
+ functions of KPPPWidget now.
+
+1999-11-16 Harri Porten <porten@kde.org>
+
+ * docking.cpp: ported to make use of new KDockWidget
+
+1999-10-03 Harri Porten <porten@kde.org>
+
+ * support automatic DNS detection if pppd >= 2.3.7.
+ * pppdata.cpp: fixed default account
+
+1999-10-02 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: determine pppd version. Will be needed to check
+ for support of new features
+
+1999-09-26 Harri Porten <porten@kde.org>
+
+ * runtests.cpp (ppp_available): Linux kernels >= 2.2.13 will have
+ a security hole fixed that breaks our check. Let's simply return true.
+
+1999-08-31 Harri Porten <porten@kde.org>
+
+ * opener.cpp: unified PAP and CHAP code
+ * use KMessageBox::Yes enum
+
+1999-07-22 Harri Porten <porten@kde.org>
+
+ * connect.cpp: visualize \n as <LF> in setExpect()'s debug messages
+ * logviewer: rename `log' variable to `logList' to avoid clash with
+ mathematical function on some systems.
+ * logview/loginfo.cpp: added mystrsep() function as strsep()
+ replacement which is not POSIX compliant.
+
+1999-07-19 Harri Porten <porten@kde.org>
+
+ * pppdata.cpp: fixed a cause of those "Timeout expired" errors: if
+ the user never set the speed of the serial port gpppdata would return
+ an empty string as speed(). The modem initialization code would
+ fall back to 57600 but no speed would be passed to pppd. Whatever
+ pppd does in this case (I haven't checked the sources yet) breaks the
+ negotiation process.
+
+1999-07-13 Harri Porten <porten@kde.org>
+
+ * added "Mode 7bit" and "Mode 8bit" script commands for CompuServe
+ users. 7bit will simply strip off the highest bit to deal with the
+ incoming 7E1 data. Write operations are not affected.
+ * modem.cpp: try a tcsendbreak() if tcgetattr() fails. Ask for
+ feedback if that solved the "modem is busy" error for anyone.
+ * modem.cpp: read data in multiple chunks of up to 200 bytes. This
+ should speed up things and avoid problems with the socket notifier.
+
+1999-07-11 Harri Porten <porten@kde.org>
+
+ * connect.cpp: moved some code from readChar() into checkBuffers()
+ to clean up the setExpect() race condition fix.
+
+1999-07-07 Mario Weilguni <mweilguni@kde.org>
+
+ * kppp now finds the icons again
+
+1999-07-06 Harri Porten <porten@kde.org>
+
+ * modem.cpp: adhere to the lock file format described in the
+ Serial HOWTO (10 digits pid and a newline).
+ * connect.cpp: fixed race condition that might have lead to data not
+ being caught by "Expect" (found by Arch Harris <arch@cs.jmu.edu>)
+
+1999-06-05 Harri Porten <porten@kde.org>
+
+ * pppstats.cpp: dropping the ifr_name #define hack allows compilation
+ with --enable-final (saves 20 kB size)
+
+1999-05-30 Harri Porten <porten@kde.org>
+
+ * connect.cpp: skip setting the volume if command is empty
+ * switched from KWizard to QWizard
+ * adaption to new KConfig iterators
+ * partly switched to KStandardDirs
+
+1999-05-07 Harri Porten <porten@kde.org>
+
+ * main.cpp: added public showStats() slot. Saved methods and p_kppp
+ pointers in DockWidget and ConWindow.
+
+1999-05-07 Harri Porten <porten@kde.org>
+
+ * renamed shutDown to myShutDown to avoid name clash with KApp
+ * runtests.cpp: better error message for missing s-bit.
+
+1999-05-04 Harri Porten <porten@kde.org>
+
+ * conwindow.cpp: fixed deleting of old geometry layout
+ * pppstats.cpp: bundled statistic in a module. Removed unused
+ get_ppp_cstats() and some variables. The modem animations are triggered
+ by signals from the PPPStats object.
+ * logview/main.cpp: had to replace QTabDialog with QTabWidget
+
+1999-04-25 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: test for `lock' in /etc/ppp/options is not needed any-
+ more, thanks to a hint from Paul Mackeras (pppd). Leaving away ttySx
+ (99/04/10) from the command line and binding stdin/stdout to the
+ device (99/01/12) stops pppd from trying to lock the device itself.
+
+1999-04-12 Harri Porten <porten@kde.org>
+
+ * opener.cpp: introduced MY_ASSERT macro. We don't want to see our
+ security checks disabled by the NDEBUG flag.
+ * connect.cpp: fixed timeout errors that were caused by a re-enabled
+ socket notifier that took away data from pppd.
+ Thanks to Ian Gordon <ig@cs.strath.ac.uk> for tracking down this bug.
+ * requester.cpp: setpppdRunning(false) _before_ killing the pppd.
+ * main.cpp: added flushX() before every command_execute()
+ * increased maximum modem string size
+ * connect.cpp: stop the main loop from skipping characters. Parsing
+ the connect speed should work now.
+
+1999-03-11 Harri Porten <porten@kde.org>
+
+ * modem.cpp: dropped ISTRIP option and made everything else "8-bit
+ clean" by using unsigned char and removing 0x7F AND'ing. Characters
+ like [§´°öäü] will work now.
+ * modem.cpp: use usleep() replacement from kdecore/fakes.cpp
+
+1999-03-10 Harri Porten <porten@kde.org>
+
+ * conwindow.cpp (ConWindow): initialize volinfo with "" do prevent
+ segfault. Strange.
+ * more porting to Qt 2.0 (layout and QString)
+
+1999-03-05 Harri Porten <porten@kde.org>
+
+ * geometry layout: got rid of some freeze() and setMinimum*() calls.
+ Qt 2.0 seems to make life a bit easier. A lot of work left.
+
+1999-03-03 Harri Porten <porten@kde.org>
+
+ * kdelibs have been switched to Qt 2.0. Replaced a lot of const char *
+ with QString (or QString&) and removed ~100 .data() calls.
+
+1999-02-16 Harri Porten <porten@kde.org>
+
+ * providerdb.cpp: don't segfault if database can't be located
+
+1999-02-15 Mario Weilguni <mweilguni@kde.org>
+
+ * the phonenumber widget had a german "Abbrechen" button instead
+ of a "Cancel" button
+
+ * new Script command "SendNoEcho". Usefull for sending passwords,
+ the command works like "Send", but the actual content is shown
+ as "*" in the status line.
+
+ * added a DialPrefix to the provider dialog; kppp now handles the
+ DialPrefix value for every connection, defaults to ""
+
+ * if a script argument contains %PASSWORD% or %USERNAME%, it is
+ replaced by the corresponding value
+
+ * fixed a bug in ChangeLog (don't laugh, that happens). I
+ commited all the changes above as Harri Porten :-)
+
+1999-02-13 Harri Porten <porten@kde.org>
+
+ * there´s no a provider database (just one entry for now) and a
+ wizard for this.
+
+1999-02-11 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: removed local header file if_ppp.h
+
+ * pppstats.cpp: cleaned up
+
+1999-02-10 Mario Weilguni <mweilguni@sime.com>
+
+ * the PPP interface name is now autodetected
+
+ * replaced the "Help" button with a questionmark. Makes the window
+ much smaller, especially in other languages than english
+
+ * the version number raised to 2.0pre4 (from 1.6.4). This version
+ is mainly the development version, whilest there is still a
+ "stable" 1.6.3 in the KDE 1.1 branch. Once KDE 2.0 is out (or
+ however it will named), the release will be 2.0
+
+1999-02-09 Mario Weilguni <mweilguni@sime.com>
+
+ * fixed a bug in the accounting module. The last character of the
+ logfile was overwritten each time a log file was
+ generated. Strange that nobody complained...
+
+ * reworked the layout of the AccountWidget
+
+ * removed some old, no longer needed variables from edit.h
+
+ * reworked the layout of the ModemWidget
+
+ * reworked the layout of the Modem2Widget. Now all Tab-Widgets are
+ reworked to use KGroupBox (which adds the title and takes part
+ of the size managment), which makes adding or deleting widgets
+ in the tab-dialogs much easier (especially for other people)
+
+ * if a user enters to domain name with a leading dot, this is
+ silently ignored now.
+
+1999-02-08 Harri Porten <porten@kde.org>
+
+ * removed some doubled and obsolete code. Some simplifications.
+
+ * fixed "cannot find pppd" error message
+
+1999-02-08 Mario Weilguni <mweilguni@sime.com>
+
+ * Changed the tabs "DNS" and "Gateway". Somehow, it feels more
+ natural this way.
+
+ * reworked the layout of the "DNS" widget
+
+ * pretty printed volume accounting. Using the terms KB, MB and GB
+ now when appropriate.
+
+ * Now it is possible to reset phone costs, volume accounting or both
+
+ * reworked the IPWidget, the GatewayWidget and the DialWidget
+
+1999-02-07 Mario Weilguni <mweilguni@sime.com>
+
+ * reworked the phone accounting selector widget, and reworked the
+ groupbox widget to adhere minimumSize() of the peer()
+
+ * reworked the layouting of the ScriptWidget and some usability
+ improvements
+
+ * removed the disturbing "Show log window" button. The log window
+ now remembers if it was shown or hidden last time. Now I´m
+ waiting for feedback, if everybody is happy I´ll remove it
+ completly.
+
+ * removed some #include from header files and moved them to the
+ cpp files. Thus compiling for developers will be somewhat faster
+ now, because of fewer dependencies
+
+1999-02-06 Mario Weilguni <mweilguni@sime.com>
+
+ * added a new tab "Execute" to the accounting dialog, including
+ an explanation how to use it.
+
+ * the pppd-arguments were modified (in the "Customize ppp
+ arguments" dialog if "Default" was pressed, no matter if "Cancel"
+ was pressed afterwards. Fixed.
+
+ * the phone number entry was removed and replaced by a listbox and
+ some buttons to allow easy selection of multiple phone numbers
+
+ * the label "Edit pppd arguments" and the button after are now
+ replaced with the button "Customize pppd arguments..."
+
+ * some small usability improvements
+
+ * a big, fat warning is now displayed when selecting the
+ "Auto-configure hostname" option.
+
+ * The log window is now hidden if we are connected. If a
+ connection is up and running, there´s no need for a debug
+ window
+
+ * The "/dev/cua*" devices are no longer available under Linux
+
+ * fixed a warning in requester.cpp
+
+ * pressing enter when on the "Connect" button now starts dialing
+
+1999-02-04 Harri Porten <porten@kde.org>
+
+ * main.cpp: remove stale pidfile before creating new the
+ one. Otherwise creation with O_EXCL fails.
+ * runtests.cpp: removed securityTest(). $HOME check wasn't fully
+ functional and not needed anymore.
+ * runtests.cpp: re-enabled test for pppd's s-bit if eff. uid != 0.
+
+1999-02-03 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a bug in my testing code
+
+ * fixed broken Hungarian ruleset
+
+1999-02-02 Mario Weilguni <mweilguni@kde.org>
+
+ * the value of the environment var $HOME is no longer trusted,
+ kppp now uses getpwent() to find out the home directory.
+
+ * the pidfile is now created with O_EXCL
+
+ * added new portugal rules
+
+ * executable accounting rulesets are now disabled (will re-enable
+ after KDE 1.1)
+
+ * fixed a bug that caused the throughput graph display
+ unreasonable high numbers
+
+1999-01-31 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a security bug (stdout or stderr closed on program start)
+
+1999-01-28 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a bug in opener.cpp. recvmsg() was interrupted, and thus
+ the SUID part died.
+
+1999-01-28 Harri Porten <porten@kde.org>
+
+ * modem.cpp: use sigsetjmp() instead of just setjmp() to solve
+ freeze when modem is powered off (bug#267)
+
+1999-01-27 Harri Porten <porten@kde.org>
+
+ * pppstats.cpp: removed debug output
+ * opener.cpp: delay closing the tty fd. Otherwise the ioctl() in
+ execpppd() can't succeed.
+ * opener.cpp: ignore TERM and INT signals. This is probably the
+ best until a safe clean-up & terminate routine is written.
+ * connect.cpp: added a removedns() call to cancelbutton().
+
+1999-01-25 Mario Weilguni <mweilguni@kde.org>
+
+ * updated and regenerated documentation
+
+1999-01-23 Harri Porten <porten@kde.org>
+
+ * main.cpp: config data wasn't saved on immediate exit.
+
+1999-01-22 Harri Porten <porten@kde.org>
+
+ * moved pppd call to the server process. It should be safer and
+ much more comfortable that pppd doesn't need to be installed SUID
+ root anymore. If I don't get any negative feedback I'll remove
+ some doubled code later.
+
+1999-01-17 Harri Porten <porten@kde.org>
+
+ * modeminfo.cpp: ATI1 was left out. Wait 0.1 secs to process the ATE0.
+
+1999-01-14 Mario Weilguni <mweilguni@kde.org>
+
+ * Most windows should now have the mini-icon
+ * the lineedit for the modem timeout was too narrow to see at
+ least 2 characters. Fixed it, now shows at least 4 chars
+ * double-clicking an account in the account-listbox now opens
+ the account for editing, as expected
+ * for some weird reason, the lineedit showing costs and volume
+ were both editable, thus letting users think they can edit
+ it. Fixed that.
+
+1999-01-12 Harri Porten <porten@kde.org>
+
+ * connect.cpp: become the session leader and let ttySx be the
+ controlling terminal before launching pppd. This might solve
+ timeout problems several people reported, ie. when kppp was
+ started from kpanel instead of a terminal window.
+ * main.cpp: the kppp.sh workaround for make_directories() isn't
+ needed anymore and confused a few people.
+ * requester.cpp: convert cmsglen to size_t to make the compiler happy.
+ * modem.cpp: fixed usleep() replacement in case #ifndef HAVE_USLEEP
+ * connect.cpp: saving of old_hostname was broken
+
+1999-01-10 Mario Weilguni <mweilguni@kde.org>
+
+ * complete rework of the accounting system. The old implementation
+ did only allow one type of accounting (=via rulefiles). The new
+ will allow executable rulefiles too.
+
+1999-01-06 Mario Weilguni <mweilguni@kde.org>
+
+ * added new hungarian rules
+ * the title of the main window was "kppp.bin". Fixed this.
+ * the combobox for selecting the account to dial in was
+ editable for some strange reason. Fixed.
+ * modified the testing stuff (-T now)
+
+1999-01-04 Mario Weilguni <mweilguni@kde.org>
+
+ * began to add a modem database. See README.ModemDB for
+ details. The modem DB will not make it into KDE 1.1 (sorry)
+
+1999-01-05 Harri Porten <porten@kde.org>
+
+ * opener.cpp: set O_NOCTTY flag when opening the modem device to
+ hinder the server process from being assigned a controlling terminal
+ (and 'stealing' it from pppd ?)
+ * pppdata.h: renamed "LockFile" key to "UseLockFile" to prevent a
+ clash with kppprc's from KDE 1.0. (reported by Seth Rothberg
+ <sethr@crocker.com>)
+ * umask(0) in suid child to avoid permission conflicts with lock file.
+ * connect.cpp,opener.*,requester.*: moved call to sethostname()
+ into Opener. Bug discovered by Matthew Ando <ando@math.jhu.edu>
+
+
+1999-01-04 Mario Weilguni <mweilguni@kde.org>
+
+ * Updated Hungarian rules
+ * removed all Qt calls from log to make it safe to use in
+ opener.cpp
+ * replace Debug() calls in opener.cpp with the function
+ provided by log.cpp
+ * clarified comments in opener.cpp
+ * replaced the button "Ok" with a "Exit" button when another
+ instance of kppp was found. This clarifies the action when
+ pressing the button.
+ * added a .kdelnk file for the log-viewer
+
+1998-12-28 Harri Porten <porten@kde.org>
+
+ * opener.cpp: netinet/in.h is needed on FreeBSD
+ * reverse order of dropping suid rights (first setgid, then
+ setuid) to avoid an EPERM error (doesn't make a difference on Linux)
+ * requester/opener: removed transmission of filename. I discovered
+ problems with the variable string length and it wasn't used
+ anyway.
+
+1998-12-27 Mario Weilguni <mweilguni@kde.org>
+
+ * moved gpppdata.setpppdError(0) to end of dieppp()
+ * the "Don´t show this hint again" option is now immediatly saved
+
+1998-12-26 Mario Weilguni <mweilguni@kde.org>
+
+ * gpppdata.pppdError() was never reset, causing a bunch of error
+ windows to appear. Should be fixed.
+ * cosmetic changes in various files for better readability
+
+1998-12-25 Harri Porten <porten@kde.org>
+
+ * kpppconfig.h: include <sys/param.h> to get the BSD define.
+ #include <config.h> was missing too. This prevented several #ifdefs
+ from doing the right thing.
+
+1998-12-24 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: relaxed my previously enforced suid check for pppd
+
+1998-12-23 Harri Porten <porten@kde.org>
+
+ * runtests.cpp, opener.cpp: fixed FreeBSD compile problem reported by Hans
+ Petter Bieker
+
+1998-12-22 Harri Porten <porten@kde.org>
+
+ * pppdata.cpp: Give users upgrading from KDE 1.0 instructions how
+ to modify their kppprc if it is owned by root (KConfig bug back then).
+
+1998-12-21 Mario Weilguni <mweilguni@kde.org>
+
+ * opener.cpp: using umask now instead of chmod (more safe)
+ * opener.cpp: regfree was called too late. Fixed.
+ * re-enable scripts when using CHAP or PAP
+ * cosmetic changes in the "Accounts" widget
+
+1998-12-21 Harri Porten <porten@kde.org>
+
+ * opener.h: define system dependend type cast for struct iovec's
+ iov_base. FreeBSD needs (char *) instead (void *).
+
+1998-12-20 Mario Weilguni <mweilguni@kde.org>
+
+ * added Germany Mannesmann Arcor ruleset
+
+1998-12-20 Harri Porten <porten@kde.org>
+
+ * docking.cpp: call KWM::activate() on connection window after
+ being re-opened from docking icon (recommended by Matthias on
+ kde-devel)
+ * ppplog.cpp: re-introduced reading messages with fgets() instead
+ of single chars. Should be faster.
+ * ppplog.cpp: worked on diagnostic messages
+
+1998-12-18 Harri Porten <porten@kde.org>
+
+ * #include "auth.h" was needed after pap.h was removed
+ * opener.h: workaround for glibc bug in RedHat 5.0. Since there is
+ presumably no way to detect the exact release version we'll simply
+ define SCM_RIGHTS to be 1 for every glibc2.0 on Linux. This should
+ solve the conflict with the kernel.
+
+1998-12-18 Harri Porten <porten@kde.org>
+
+ * removed pap.* and chap.* files and moved their code in
+ requester.cpp where all setuid root stuff is gathered now.
+ * requester.cpp: rewrote PAP/CHAP so that the setuid part is free
+ of any Qt/X calls.
+
+1998-12-16 Mario Weilguni <mweilguni@kde.org>
+
+ * added Indonesia rule files
+ * added some new Denmark rule files
+ * added a patch from Filip Larsen <filip@post4.tele.dk> to allow a
+ rule to be dependent on connection time. See Rules/TEMPLATE for
+ an example. This will allow much better danish rulesets
+
+1998-12-14 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: fixed test for pppd's suid bit
+ * requester.cpp: initialize struct control (may be necessary for
+ some kernel versions)
+ * runtests.cpp: use Requester to check write access to modem (PPP Test)
+
+1998-12-13 Harri Porten <porten@kde.org>
+
+ * use hardcoded lock file path (kpppconfig.h)
+ * tighten interface between Requester/Opener by transmitting
+ device index instead of device path
+ * do a fchown(fd, 0, 0) on lock file
+
+1998-12-08 Mario Weilguni <mweilguni@kde.org>
+
+ * added Denmark rules
+ * added Luxembourg rules
+ * commented out the no-longer need CHAP_* functions
+ * removed the old/never used setup program
+ * cleaned up accounting (set umask, no chown/chmod)
+ * cleaned up PPP log (set umask, no SUID check)
+
+1998-12-05 Harri Porten <porten@kde.org>
+
+ * connect.cpp: killpppd() and stop if_timeout_timer in cancelbutton()
+ * main.cpp: ignore SIGCHLDs in shutDown()
+ * main.cpp: interface timeout error wasn't displayed anymore
+ * modem.cpp: check for possible existance of stale lockfile before
+ trying it to open via Requester::rq->openLockFile(). The resulting
+ error message on failure was too confusing.
+ * main.cpp: ignore SIGHUPs in child process
+
+1998-12-04 Harri Porten <porten@kde.org>
+
+ * requester.cpp: disable alarm() for now, silence some debug messages
+
+1998-12-01 Harri Porten <porten@kde.org>
+
+ * main.cpp: notify user about death of helper process and abort
+
+1998-11-28 Harri Porten <porten@kde.org>
+
+ * main.cpp: restored accidentally deleted line (setPassword())
+ * connect.cpp: suppress warnings from QStrList
+
+1998-11-26 Harri Porten <porten@kde.org>
+
+ * included errno.h in opener.cpp and modified CMSG_DATA()'s
+ argument (as suggested by Andreas Pour).
+ * had to revert to SOCK_DGRAM since I keep getting failed
+ assertions. Using a stream seems to require some attention in
+ terms of ensuring that the correct length is received.
+
+1998-11-26 Mario Weilguni <mweilguni@kde.org>
+
+ * added missing rules for Switzerland, and added kppp.sh
+ * replace SOCK_DGRAM by SOCK_STREAM, and hope that it works
+
+1998-11-26 Harri Porten <porten@kde.org>
+
+ * requester.cpp: made the ifdef's for cmsg_data more portable
+ * requester.cpp, opener.cpp: include sys/uio.h to define struct
+ iovec. As far as I can see this should work for systems with and
+ without glibc. If it doesn't, please mail me directly.
+
+1998-11-25 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a lot of rulefiles. Pentecost Monday is NOT easter+60,
+ but easter+50. My TEMPLATE file contained this bug,
+ and unfortunatly a lot of people copied it.
+ * improved and new Switzerland rulefiles
+
+1998-11-24 Harri Porten <porten@kde.org>
+
+ * main.cpp: store pid of setuid child in PPPData object
+ * define KERNEL_VERSION()
+
+1998-11-24 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed opener.cpp for Linux >= 2.1.0
+ * removed PAP_UsePAP(), no longer needed
+
+1998-11-23 Harri Porten <porten@kde.org>
+
+ * requester.cpp: removed ancient #include "requester.moc"
+
+1998-11-22 Harri Porten <porten@kde.org>
+
+ * main.cpp: fixed freeze on disconnect by using waitpid(...,
+ WNOHANG) instead of wait()
+ * main.cpp: forgot to convert gpppdata.password to char*
+ * opener.cpp: close file descriptors after being sent. Left out
+ dup() call. Did it serve any special purpose ?
+ * opener.h: increased allow size for username and passwords
+ * modem.cpp: included config.h for HAVE_USLEEP
+ (thanks to hpj.lisa@t-online.de (hpj) for reporting this)
+ * PAP/CHAP: fixed typo and return value
+
+1998-11-20 Harri Porten <porten@kde.org>
+
+ * First draft of a client&server archicture consisting of setuid
+ child process that will serve requests for file manipulation from
+ the parent. The parent is doing all the GUI operations is running
+ with normal user privileges. Assuming the approx. 300 lines still
+ running setuid root will be intensively reviewed this should
+ result in a kppp far less vulnerable to security exploits.
+ * added requester.* and opener.*
+ * TODO: - handle signals (SIGCHLD, SIGPIPE?)
+ - get ppp_available() running (presumably broken now)
+ - speed up reading the syslog (reading single chars now)
+
+1998-11-17 Mario Weilguni <mweilguni@kde.org>
+
+ * another security hole fixed: the function make_directories() is
+ no longer used, instead of a shell script make those directores.
+ Since shell scripts do not run suid, this should be safe
+ * fixed a bug in the main.cpp, at the calls to setPAPSecret() and
+ setCHAPSecret().
+ * fixed (void*)new Opener(...) to (void)new Opener(...)
+
+1998-11-17 Mario Weilguni <mweilguni@kde.org>
+
+ * Security fix: fixed possible buffer overflow in findFileInPath()
+ * Security fixes: re-checked all char[] on stack to be suid-safe
+ If it is considere to be safe, a comment was added to the end of
+ line
+
+1998-11-17 Mario Weilguni <mweilguni@kde.org>
+
+ * removed another "extern" by making Accounting::getCosts(...) static
+
+1998-11-17 Mario Weilguni <mweilguni@kde.org>
+
+ * further improvements in ppp-log wizard
+ * runtests.cpp: test for Qt >= 1.40 before include header files
+
+1998-11-11 Harri Porten <porten@kde.org>
+
+ * connect.cpp: allow a little delay after sending the volume
+ string. Prevents it from getting eaten by the next command.
+ Thanks to Craig Goodrich <craig@airnet.net> for providing the patch.
+
+1998-11-11 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: added header file to solve compilation problems
+
+1998-11-04 Harri Porten <porten@kde.org>
+
+ * got rid of two global variables
+
+1998-11-04 Mario Weilguni <mweilguni@kde.org>
+
+ * kppp now checks itself if the kernel supports PPP (Linux only
+ for now. If anyone wants this for another system - implement it!)
+ * before dialing out, the check for PPP support is repeated, if
+ the kernel module was unloaded between running the tests and the
+ actual dialing, it will reloaded again
+
+1998-11-01 Harri Porten <porten@kde.org>
+
+ * modemcmds.cpp: added a wait&CR&wait sequence before sending the
+ init string. This seems to be necessary to avoid locking some modems.
+
+1998-10-29 Harri Porten <porten@kde.org>
+
+ * connect.cpp: fixed search&replace typo
+
+1998-10-26 Mario Weilguni <mweilguni@kde.org>
+
+ * incorporated new Netherland rules (NetPoint)
+
+1998-10-25 Mario Weilguni <mweilguni@kde.org>
+
+ * added updated rules for Spain
+
+1998-10-06 Mario Weilguni <mweilguni@kde.org>
+
+ * repaired Makefile.am and reverted Hans Petter Bieker's changes
+ to something more usefull.
+ * added SecurityWidget, but currently there's not much use for
+ this widget. Later on, it will allow root to specify who may use
+ kppp and pppd.
+ * removed the global variable miniIcon
+ * removed the global variable dock_widget, replaced with a static
+ class variable
+ * added lockdevice() and unlockdevice() to the class modem. This
+ will finally allow us to have a modem class which handles the
+ device locking itself (yeah...)
+ * removed the global var "modem", now a static member of the class
+ "Modem"
+ * modem.cpp: removed some global vars
+
+1998-10-14 Harri Porten <porten@kde.org>
+
+ * pppdata.cpp: don't care about CD line by default. We'll receive
+ too many "bug reports" otherwise.
+
+1998-10-13 Harri Porten <porten@kde.org>
+
+ * ppplog.cpp: offer some advice based on keywords found in the log
+ (very primitive so far, but may be extended later)
+
+1998-10-09 Harri Porten <porten@kde.org>
+
+ * modem.cpp: additional data flushes
+ * pppdata.cpp: don't need workaround for '$' with recent kde libraries
+
+1998-10-05 Mario Weilguni <mweilguni@kde.org>
+
+ * better QuickHelp text, proof-read by a native english
+ speaker. Thanks a lot to Martin A. Brown for doing this!
+
+1998-10-04 Harri Porten <porten@kde.org>
+
+ * miniterm.cpp: fixed toolbar
+
+1998-10-03 Harri Porten <porten@kde.org>
+
+ * modem.cpp: disentangled Modem class from other Widgets, i.e.
+ removed the Multiple Inheritance I introduced earlier.
+ Drawback: another global pointer :(
+ * main.cpp: since KConfig neglects the real user id when creating
+ config files we have to give kppprc back to the user ourself.
+ * connect.cpp: pass something more useful to parseModemSpeed()
+ * removed lots of unused/redundant code and variables
+
+1998-10-02 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed that stupid segfault combined with the throughput graph
+ * "make" did not set the suid bit anymore. Why? However, I fixed
+ that
+
+1998-09-29 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a bug in the graphing code
+ * runtests.cpp: fixed typos
+
+1998-09-28 Harri Porten <porten@kde.org>
+
+ * ppplog.cpp: reduce memory consumption, small fixes
+ * pppdata.cpp: fixed segfaults caused by non-readable kppprc
+
+1998-09-28 Harri Porten <porten@kde.org>
+
+ * runtests.cpp: bail out if some evil-doer has manipulated $HOME
+
+1998-09-26 Mario Weilguni <mweilguni@kde.org>
+
+ * cosmetic changes in ppplog.cpp
+
+1998-09-24 Mario Weilguni <mweilguni@kde.org>
+
+ * Ownership of PPP logfiles corrected to the real, not the
+ effective user id
+
+1998-09-23 Harri Porten <porten@kde.org>
+
+ * ppplog.cpp: skip remote message if empty
+ * ppplog.cpp: fixed segfault triggered by virgin log files
+
+1998-09-21 Mario Weilguni <mweilguni@kde.org>
+
+ * introducing quickhelp
+ * forgot to add i18n to quickhelp entries, fixed this
+
+1998-09-20 Mario Weilguni <mweilguni@kde.org>
+
+ * PPP-arguments dialog reworked
+
+1998-09-19 Mario Weilguni <mweilguni@kde.org>
+
+ * more QuickHelp
+ * began to clean up layout management, it's a mess
+ * added setup widget for the throughput graph
+ * some code cleanups
+
+1998-09-16 Mario Weilguni <mweilguni@kde.org>
+
+ * Added some more QuickHelp
+ * The QLineEdit for fixed IP and netmask is not re-initialized
+ with "0.0.0.0" whenever you select "dynamic IP"
+ * the scripting widget is disabled if authentication PAP or CHAP
+ is selected
+
+1998-09-15 Mario Weilguni <mweilguni@kde.org>
+
+ * replaced strings that span several lines with multiple
+ strings, it seems that this was a GCC-only feature
+
+1998-09-11 Mario Weilguni <mweilguni@kde.org>
+
+ * more code cleanups
+ * fixed a bug in log.h (semicolon after a macro definition --
+ brrr)
+ * added quickhelp to many setup dialogs
+
+1998-09-05 Harri Porten <porten@kde.org>
+
+ * pppdata.cpp: fixed warnings
+ * replaced readListConfig calls with calls to readWholeListConfig
+ * and removed 'Whole' from function names afterwards
+ * connect.cpp: sped up scripting loop by reducing calls to gpppdata
+
+1998-08-29 Mario Weilguni <mweilguni@kde.org>
+
+ * In ConnectWidget::readtty(): after a newline or CR is received,
+ the QSocketnotifier is disabled for 20ms to avoid reading too
+ much, e.g. the start characters of the ppp protocol.
+ * added compile option "NO_GRAPH" to disable the graph
+ * removed the compile options "COMPILE_PIX" in pppstatsdlg.cpp,
+ this is no longer needed or supported
+
+1998-08-27 Harri Porten <porten@kde.org>
+
+ * ppplog.cpp: use short named header file to keep Qt 1.33 users happy
+
+1998-08-25 Mario Weilguni <mweilguni@kde.org>
+
+ * removed all QTimers for reading tty output, a QSocketNotifier is
+ now used. Much better response time now, lower CPU usage!
+ * "prompt" was deleted in the ConnectWidget destructor. Removed
+ that
+ * added telephone rule for Malaysia
+
+1998-08-24 Harri Porten <porten@kde.org>
+
+ * connect.cpp: re-enabled closeEvent()
+ * pppdata.cpp: dumped writeListConfig(). Will do the same with
+ readListConfig() too ... next week.
+
+1998-08-22 Mario Weilguni <mweilguni@kde.org>
+
+ * some KDE style guide conformant changes
+ * some code cleanups
+ * removed -lkfile and -lkfm from Makefile.am
+ * added debug switch to pppd command line when debugging
+ is turned on
+ * Improved the response time for modem output. Modem output is
+ polled in longer intervals, if data is pending the polling time
+ is shortened, otherwise the polling time is set back to it´s
+ original, rather high value
+ * re-worked the "About" widget. It was time to save poor old Bernd
+ from receiving bug-reports.
+ * miniterm.cpp: code cleanups
+ * improved responseness of MiniTerm, reduced CPU load
+ * MiniTerm adheres to KDE Standards now (Help-Menu, ...)
+ * The ATI query was broken. Fixed.
+ * Improved the ATI query result window
+ * modeminfo.cpp: code cleanups
+ * main.cpp: code cleanup, banner updated
+ * repaired the german "Tele-2" ruleset (was broken due to invalid
+ characters)
+ * added improved ruleset for Italy (thanks to Michele Manzato for
+ supplying this ruleset)
+ * fixed a few typos in the TEMPLATE
+ * prepared a replacement for the debug-window
+ * connwindow.cpp: code cleanups
+ * began to remove the code dependencies between main.cpp
+ (KPPPWidget) and the setup GUI components. This will help
+ to separate the dialer and the setup later
+ * general.cpp: code cleanups
+ * accounts.cpp: removed as many references to p_kppp as possible,
+ using signal/slots instead of. There´s only on occurence of p_kppp
+ left
+ * general.cpp: removed the mysterious function itoa (nowhere used)
+ * various files: a lot of code cleanups, removed many references
+ to extern variables, reduced the amount of functions called from
+ p_kppp directly (done with signal/slots now)
+ * Added a new parser for modem speeds. The results are much better
+ now, especially for the new K56flex/X2/V.90 modems (where
+ different speeds are used for RX/TX)
+ * removed tons of "#ifdef MY_DEBUG" and replaced them with
+ functions "Debug", which is provided in log.cpp and is a
+ "printf()" like function
+ * added modem volume selector to the setup.
+
+1998-08-21 Harri Porten <porten@kde.org>
+
+ * use SIGINT instead of SIGHUP to end a connection
+ * implemented command line option 'kppp -k' that will
+ terminate a connection
+
+1998-08-21 Mario Weilguni <mweilguni@kde.org>
+
+ * Added a graphical display of the current modem line
+ throughput.
+ * Added an assistent for those "ppp has died" messages
+ * Evaluating remote system messages now
+
+1998-08-19 Mario Weilguni <mweilguni@kde.org>
+
+ * added function to create a ppp - log
+ * connect speed improvements
+
+1998-08-18 Harri Porten <porten@kde.org>
+
+ * fixed security leak in execute_command(): drop setgid root and
+ close file descriptors
+
+1998-08-17 Mario Weilguni <mweilguni@kde.org>
+
+ * fixed a bug in runtest.cpp that caused a segfault (fclose()
+ called twice)
+
+1998-08-17 Harri Porten <porten@tu-harburg.de>
+
+ * main.cpp: added SIGHUP handler that will cleanly disconnect an
+ existing connection.
+
+1998-08-09 Harri Porten <porten@tu-harburg.de>
+
+ * miniterm.cpp: switched to QMenuBar, fixed memory leak
+
+1998-08-08 Harri Porten <porten@tu-harburg.de>
+
+ * modem.cpp: fixed hangup that occured if the modem power was off.
+ hangup() aborts after 2 seconds if it doesn't suceed.
+
+1998-08-06 Harri Porten <porten@tu-harburg.de>
+
+ * runtests.cpp: scan up to 100 lines when searching for the
+ unwanted 'lock' in /etc/ppp/options and recognize the '#' sign
+ to avoid a false alarm
+
+1998-08-04 Harri Porten <porten@tu-harburg.de>
+
+ * fixed replacement for systems without a BSD 4.2 style usleep()
+ * modem.cpp: continued to move modem related stuff to modem.cpp.
+ The new Modem class is inherited by the Connect-, MiniTerm- and
+ ATI-Query Widget for now. This should be way easier to maintain and
+ will reduce the binary's size.
+ * modeminfo.cpp: cleaned up
+
+1998-08-02 Harri Porten <porten@tu-harburg.de>
+
+ * opentty(): fixed XON/XOFF flow control
+ * closetty(): flush data not read or transmitted
+ * main.cpp: enforce killpppd()
+ * modem.cpp: moved lockdevice(), unlockdevice() and modemspeed()
+ into this newly created file. opentty(), closetty() and hangup()
+ should follow soon to let them be shared between widgets.
+
+1998-07-30 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: take the burden of specifying the modem lock file
+ from the user. Just entering the directory should be easier and
+ safer. Presets for different systems can be found in kpppconfig.h
+
+1998-07-29 Harri Porten <porten@tu-harburg.de>
+
+ * macros.h: replaced remaining MAX with QMAX
+ * main.cpp: followed recommendation of the Unix Programming FAQ to
+ use _exit() instead of exit() in child processes
+
+1998-07-25 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: rewrote check for stale lock files. After dropping
+ the use of /proc it should work on other platforms now, too.
+
+1998-07-19 Harri Porten <porten@tu-harburg.de>
+
+ * merged killppp() and terminatepppd() in killpppd()
+ * main.cpp: added execute_command() to launch external programms
+ * added "Command Before Disconnect" for people who have to clean
+ up before they leave the net
+
+1998-07-11 Harri Porten <porten@tu-harburg.de>
+
+ * main.cpp: replaced ~/.kde with kdelocaldir()
+ * connect.cpp: hardcoded ´-detach´ as pppd argument
+
+>>>>>>>>>>>>>>>>>>>>>>>>>>>> KDE 1.0 released <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+1998-06-21 Harri Porten <porten@tu-harburg.de>
+
+ * general.cpp (GeneralWidget): modified checkbox-size to avoid
+ warnings from QGManager
+
+1998-06-18 Harri Porten <porten@tu-harburg.de>
+
+ * edit.cpp (ScriptWidget): deactivated "Use default script"-option
+ as long as someone comes up with a default script
+
+1998-06-02 Harri Porten <porten@tu-harburg.de>
+
+ * replaced klocale->translate() with i18n() and every reference to
+ XPPP with KPPP
+ * utilize ´kapp´ (defined in kapp.h) instead of the global ´extern
+ KApplication *app´
+ (Sorry about the large amount of changes, but I couldn´t resist to
+ clean up the code)
+
+1998-06-02 Harri Porten <porten@tu-harburg.de>
+
+ * pppdata.cpp: removed workaround, since KConfig fix is in place now
+
+Mon Jun 1 16:13:06 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * kppp did´nt like "$" in modem strings, fixed that
+
+Sun May 24 23:39:42 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * Applied FreeBSD patch for the modem devices
+ Affected File:general.cpp pppdata.cpp
+ Look for __FreeBSD__
+
+Mon May 18 19:51:56 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * introduced a separate log-viewer "kppplogview"
+ * removed the no-more needed entry widget for the log-viewer
+ * removed test code checking for log viewer
+
+1998-05-15 Harri Porten <porten@tu-harburg.de>
+
+ * pppdata.cpp: temporary workaround for KConfig bug concerning
+ lists without a trailing separator
+
+Fri May 15 22:27:02 1998 Mario Weilguni <mweilguni@kde.org>
+ * removed the writeline("") once again, it does not work
+
+1998-05-12 Harri Porten <porten@tu-harburg.de>
+
+ * added support for multiple telephone numbers (separated by ':')
+ Patch contributed by Sebastian Rittau <srittau@jroger.in-berlin.de>
+
+Tue May 12 18:55:02 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * the format of the accounting logfile has changed to allow
+ easier parsing from external files:
+
+ the format is:
+ CONNECT_TIME_FROM:CONNECTION_PEER:CURRENCY_SYMBOL: \
+ CONNECT_TIME_UNTIL:SUBTOTAL:TOTAL:BYTES_IN_BYTES_OUT
+
+ there´s a small tcl script which converts the log files. Simply
+ use "tclsh convlog.tcl" will convert all log-files, but NOT
+ delete the old ones.
+
+Mon May 4 17:19:12 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * Austrian rule files updated
+ * LSM updated
+ * line-limit for /etc/resolv.conf raised to 128
+ * fixed the hard-coded line-limit in removedns() to use
+ MAX_RESOLV_CONF instead (128)
+ * added writeLine("") in connect.cpp to make sure that the modem
+ is in ready state (waiting for AT*)
+ * support for internal ISDN cards on Linux via modem emulation
+ (/dev/ttyI*)
+
+1998-04-30 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: undid my stupid PAP/CHAP changes from yesterday
+
+1998-04-29 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: prevented segfault on invalid lockfiles
+ * runtests.cpp: /etc/resolv.conf will be created if missing
+ * connect.cpp: fixed reversal of command line options for PAP/CHAP
+
+Tue Apr 28 20:57:04 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * added CHAP (client side only) authentication
+
+1998-04-27 Harri Porten <porten@tu-harburg.de>
+
+ * general.cpp (GeneralWidget): command line option "quit after
+ end of connection" is availabe via GUI, too.
+ * hangup(): made another usleep() adjustable to prevent hanging
+ (thanks to A.D.Y. Cheng <adyc100@hkstar.com> for pointing that out)
+
+Fri Apr 24 18:57:46 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * general.cpp (GeneralWidget): removed the entry field for
+ the path to the pppd binary
+
+Fri Apr 24 13:03:02 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * runtests.cpp: kppp now does not allow anymore the user
+ to specify a path to the pppd binary, but searches for
+ it
+ * kppp now checks if pppd is owned by root
+ * kppp does not anymore drop suid before starting pppd
+
+Thu Apr 23 22:35:31 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * Fixed a few security holes: kppp drops suid before executing pppd,
+ checks for buffer overflow with the -c command line switch,
+ drops suid before checking a file with -r
+
+Thu Apr 16 21:21:03 1998 Harri Porten <porten@tu-harburg.de>
+
+ * main.cpp: set portable locale settings for numerical values,
+ since the rulefiles will always have a point as decimal delimiter.
+
+1998-04-15 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: extended parseargs() to deal with quoted arguments
+ * fixed bug in 'Save' that made multiple use impossible.
+ * enclosed username and password with quotes to ensure proper
+ handling of whitespace characters
+ * pap.cpp: fixed regexp to recognize entries in pap-secrets with
+ usernames that are either quoted or preceeded by whitspace. Phew !
+ That should eliminate one source of these "pppd died" errors.
+
+Mon Apr 6 20:18:40 1998 <mweilguni@kde.org>
+
+ * fixed a volume accounting bug
+ * volume accounting is now configureable (bytes in, out, in/out)
+ * added new feature (very usefull for sysadmins). If there´s a
+ file /etc/kppp.allow, only users from that file may use kppp to
+ dial out. Format is one entry per line, comments begin with "#",
+ users specified by their login names
+ * fixed a bug where the PAP authetication record was only removed
+ from /etc/ppp/pap-secrets if an external program is started on
+ login (stupid me :-)
+
+Wed Apr 1 21:09:05 1998 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: small fixes for 'Scan'
+
+Tue Mar 31 18:54:13 1998 Harri Porten <porten@tu-harburg.de>
+
+ * connect.cpp: corrected stripping of whitespace from 'scanvar'
+ * main scripting loop idles until 'Scan' is completed
+ * added new keyword 'Save' to store scanned strings
+ * cleaned up add_domain(), adddns(), removedns() by adding data()
+ calls where QString variables are passed as const char* parameters
+
+Mon Mar 30 22:02:07 1998 <mweilguni@kde.org>
+
+ * added volume accounting (currently, only inbytes are counted,
+ but should be very easy to change). Unfortunatly, there´s no
+ method yet to reset the number of bytes, so you have to fiddle
+ around in kppprc.
+
+Wed Mar 18 21:47:20 1998 Harri Porten <porten@tu-harburg.de>
+
+ * runtests.cpp: added check for 'lock' option in /etc/ppp/options.
+ Hopefully, this check isn't too harsh and won't cause any false alarms.
+
+Wed Mar 18 01:13:50 1998 Harri Porten <porten@tu-harburg.de>
+
+ * modemcmds.cpp: replaced 'Modem Sustains Fast Init' (from general.cpp)
+ option by an adjustable 'Init Delay'. Some users reported that they
+ had to increase the delay time to prevent kppp from hanging
+ after ATZ.
+
+Mon Mar 16 03:37:04 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * command line options need to be parse after creating a KApplications
+ due to some changes by Matthias in kapp I think.
+
+1998-03-15 Mario Weilguni <mweilguni@kde.org>
+
+ * main.cpp: added mini-icon for all kppp windows, not just
+ the main window
+
+Mon Mar 9 00:17:20 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * added new script keyword 'scan', see the documentation.
+ It allows for password challenges etc.
+
+Tue Feb 24 19:19:44 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * pap.cpp, connect.cpp: removed the need for a "+ua" option
+ for PAP. Unfortunatly this REQUIRES that kppp can write
+ /etc/ppp/pap-secrets (either by running SUID root or
+ by group permissions)
+
+Fri Feb 13 01:12:19 1998 Harri Porten <porten@tu-harburg.de>
+
+ * loginterm.cpp: implemented the possibility to log in via
+ a simple terminal window. Should have developed a generic widget
+ that could have been shared by Miniterm, the debug window and
+ loginterm, but I didn't want to touch the rest of the stuff yet.
+ * kpppconfig.h: increased max. script size to 20
+
+Thu Feb 5 22:00:55 1998 Harri Porten <porten@tu-harburg.de>
+
+ * pppdata.cpp: added pppdError() and setpppdError()
+ * main.cpp: added new error message for timeout
+ * removed last remaining command_on_disconnect bug (see Jan 24)
+
+Sat Jan 31 19:56:03 1998 Harri Porten <porten@tu-harburg.de>
+
+ * main.cpp: in some cases kppp connected to an account
+ different from the one selected in the combo box. Added
+ setAccount() in connectbutton() to fix that, hopefully.
+
+Sun Jan 25 00:45:45 1998 Harri Porten <porten@tu-harburg.de>
+
+ * docking.cpp: included a hide() statement to cleanly undock
+ the widget from the panel. Otherwise the window stayed invisible
+ after one dock/undock-cycle.
+
+Sat Jan 24 03:40:44 1998 Harri Porten <porten@tu-harburg.de>
+
+ * fixed a bug (occuring twice) that caused the if-statement for
+ command_on_connect and command_on_disconnect to be always true.
+
+Fri Jan 23 01:32:57 1998 Harri Porten <porten@tu-harburg.de>
+
+ * fixed alignment in conwindow.cpp's layout.
+
+Wed Jan 21 03:22:43 1998 Harri Porten <porten@tu-harburg.de>
+
+ * prevented the password from being left in kppprc after the
+ option 'Store password' has just been turned off
+
+ * the ID & password field will be disabled if no account has
+ been created yet
+
+ * pppdata.cpp: removed absolute path for default log viewer
+ to prevent warning messages upon first startup if KDE's location
+ differs from /usr/local/kde
+
+ * removed gpppdata.Id() and gpppdata.setId() since they have been
+ replaced by storedUsername() and setStoredUsername()
+
+Tue Jan 20 15:30:08 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * Sigh. After unintentionally deleting my account for the sixth
+ time, I added a yesNo() dialog to confirm deleting an
+ account. This is VERY important, because if you click on "Show
+ log" and double-click onto a file to view it and you have the bad
+ luck that the filename is just over the "Delete" button, the
+ account will be gone.
+
+ * runtests.cpp: gpppdata is now saved after modifications in
+ runtests(), and the default log viewer is set correctly
+
+Tue Jan 20 03:10:35 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * tried to get the docking window to display a tooltip with transfer
+ bytes statistics. Failed. Need to contact Matthias about it.
+
+ * Removed the 'Dock' button. It is superfluous. Use the window
+ close button to hide the window.
+
+ * fixed the flicker problem that was reintroduced in pppstatdlg.cpp
+
+ * fixed ( hopefully ) a hangup problem on busy redial caused
+ by the modem_in_connect_state varible in connect.cpp
+
+Mon Jan 19 17:51:16 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * main.cpp: the setup dialog is now created on demand. Since in
+ most cases we only dial out, creating the setup dialog is a waste
+ of CPU and memory. This reduces memory by approx 200KB.
+
+ * geometry managment added to PPP statistic window
+
+ * tried to minimize dependencies on "main.h" by removing unneeded
+ #includes
+
+ * removed homedir.(cpp|h). It can be easily replaced with
+ QDir::homeDirPath().
+
+ * before doing anything else, a few tests will be started to
+ ensure that i.e. the pppd binary is found...
+
+Mon Jan 19 12:05:00 1998 Harri Porten <porten@tu-harburg.de>
+
+ * set default for get_dock_into_panel() to false as safety
+ precaution
+
+ * introduced docking. It allows kppp to be run as a small
+ animated icon on the panel. Clicking on the icon with the left
+ mouse button restores the regular window. The right button
+ opens up a popup menu with other options.
+
+Mon Jan 19 04:46:39 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * reset PHONENUMBER_SIZE to 60. People with phone cards need lots
+ space.
+
+Fri Jan 16 21:41:08 1998 Stephan Kulow <coolo@kde.org>
+
+ * some improvements to the toplevel Makefile.am. rpath is very
+ important for setuid programs
+
+Fri Jan 16 19:04:35 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * Improved Makefiles. Now documentation and rulesets are correctly
+ uninstalled with "make uninstall"
+
+ * Toplevel Makefile.am improved. Subdir traversal is only done for
+ installing, not for compiling
+
+ * fixed that "segfault-on-rulecheck" bug introduced by coolo's
+ modifications for locale support.
+
+ * RuleSet::checkRuleFile() now returns 0 on success, otherwise
+ 1. kppp uses this as exit value.
+
+ * fixed a bug in the swiss rulesets
+
+ * added file "checkrules" in Rules. It autochecks all rules found
+ and reports bad rulesets.
+
+Thu Jan 15 20:31:14 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * Ported kppp to use automake.
+
+ * Since the rulefiles now have separate subdirectories, I have
+ removed the prefix (i.e. Spain/Spain_Inovia.rst -->
+ Spain/Inovia.rst). Since the old files still exist it should be no
+ problem for users of former kppp versions.
+
+ * renamed config.h to kpppconfig.h because it conflicts with the
+ default include file config.h, generated from autoconf
+
+Wed Jan 14 12:12:29 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * main.cpp: pressing Enter in ID_Edit moves focus to PW_Edit, in
+ PW_Edit moves focus to the connect-button
+
+Wed Jan 14 04:13:57 1998 Harri Porten <porten@tu-harburg.de>
+
+ * fixed a bug that caused passwords to be spread among other accounts.
+ Triggering XPPPWidget::entryChanged() had a nasty side effect.
+
+Tue Jan 13 17:50:07 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * fixed a bug introduced with the creation of the 'execute command
+ on disconnect' option
+
+ * CD is by default off. It doesn't work on my internal modem
+ and I suspect it might not even work on most (all) internal modems ..
+
+Tue Jan 13 12:02:33 1998 Mario Weilguni <mweilguni@sime.com>
+ * the PAP authentication file is now created in the users
+ homedirectory. Otherwise I'd have to create the directory
+ myself and fix the permissions if kppp runs suid root -
+ I'm too lazy for that.
+
+ * fixed a security bug in connect.cpp, a typical buffer
+ overflow. Very bad for setuid root programs
+
+Thu Jan 13 06:58:29 1998 Peter Silva <peter.silva@videotron.ca>
+
+ * make new behaviour optional (in response to Bernd's worry
+ about cheap modems.)
+
+Thu Jan 13 06:58:29 1998 Peter Silva <peter.silva@videotron.ca>
+
+ * patch to prevent kppp hang if modem is off when connecting.
+
+
+Tue Jan 13 02:48:55 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * applied BSD patch from Alex <garbanzo@hooked.net>
+ (just a few missing includes)
+
+ * various fixes and geometry improvements
+
+ * added a linedit for a command to be executed on disconnect
+
+
+Mon Jan 12 17:18:41 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * username and password are now remembered between
+ session. Remembering the password can be switched off for each
+ account.
+
+ * connect.cpp: fixed 2 security bugs in lockfile(). Now it's
+ checked if the lockfile is a regular file and no more than
+ 32 characters are read from this file, no matter how long it is.
+
+ * Added PAP authentication option and code. Using PAP (at least
+ client-side-authentication should now be VERY easy. Sorry,
+ no CHAP support so far.
+
+ * edit.cpp: Added a checkbox if to use kppp's default script.
+ If this checkbox is checked, all scriptedit controls are disabled.
+
+Sun Jan 11 21:03:00 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu>
+
+ * lslisder in modemcmd.cpp wasn't set correctely on widget
+ creation.
+
+
+Thu Jan 11 14:58:29 1998 Peter Silva <peter.silva@videotron.ca>
+ * (0.8.10) incorporate iconify_on_connect patch.
+
+
+Sat Jan 10 21:23:10 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * main.cpp: fixed a bug that cause kppp to go wild and display
+ hundreds of windows when it cannot load a ruleset file
+
+Wed Jan 7 13:00:24 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * edit.cpp: improved the IP and Gateway widgets
+
+ * various improvements for the geometry managements
+
+Tue Jan 6 12:56:14 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * main.cpp: the main kppp window has now layout control
+
+ * connect.cpp: layouted the connection window.
+
+ * modeminfo.cpp (ModemInfo): layoutet the modeminfo dialog
+ and layouted the ModemTransfer dialog (ATI query)
+
+Mon Jan 5 20:59:00 1998 Harri Porten <porten@tu-harburg.de>
+
+ * accounting.cpp: telephone costs are stored in kppprc now.
+ ~/.kde/share/apps/kppp/Costs is obsolete.
+ * pppdata.cpp: added totalCosts() and setTotalCosts().
+ Several small cosmetic changes.
+
+Mon Jan 5 14:25:37 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * docs/kppp.sgml: updated the docs for the new KDE file system
+ standard. Ispell'ed the docu.
+
+ * main.cpp: added the "-q" command line switch. This is very
+ usefull in combination with "-c". Instead of returning to
+ the kppp main window kppp will exit after disconnecting.
+
+ * Makefile.in: removed the external moc_*.cpp files and
+ included them directly with "#include". This saves
+ approx. 30 KB in the binary and greatly speeds up
+ compilation
+
+Mon Jan 5 13:59:20 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * Rules/TEMPLATE: fixed a typo: the extension of ruleset
+ files is ".rst", not ".rts"
+
+Mon Jan 5 13:42:28 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * general.cpp: replaced the KIntLineEdit class with the
+ class KIntegerLine from libkdeui. Replaced the progress
+ widget with that from libkdeui.
+
+Mon Jan 5 12:51:43 1998 Mario Weilguni <mweilguni@sime.com>
+
+ * edit.cpp: now all major dialogs use Qt layout
+
+Mon Jan 5 04:55:58 1998 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * fixed the while(*it) problem in pppdata.cpp it should be
+ while(it->current()).
+
+Sun Jan 4 13:54:12 1998 Mario Weilguni <mweilguni@sime.com>
+ * modified the modemcommands dialog to use the layout
+ classes of Qt. Replaced the scrollbar with a slider,
+ because scrollbars are not the right widget to change
+ values.
+
+Thu Jan 2 00:25:03 1998 Peter Silva <peter.silva@videotron.ca>
+ * added modem_in_connect_state, and checks for it
+ to avoid unneccessary delays on modem initialization.
+ delay of 2 seconds will still occur on hangup by default.
+ I think that's bearable.
+ * Added modem tricks section, gentle introduction.
+
+Thu Dec 25 11:46:34 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * Added new script keywords: PwPrompt and ID
+ * Added ID and Password LineEdits on the main dialog window
+ * KConfig transition is now complete, one more time the data
+ storage format has changed. ( Hopefully the last time )
+
+ * All sorts of little cosmetic changes
+ * fixed up the mini-terminal a bit
+
+Sun Dec 22 23:00:03 1997 Peter Silva <peter.silva@videotron.ca>
+
+ * To fix hangup problems:
+ * Added programmable guard time around escape sequence
+ * Separated escape sequence from hangup command
+
+Sun Dec 21 10:19:18 1997 Peter Silva <peter.silva@videotron.ca>
+
+ * Changed maxloopnest to MAXLOOPNEST for portability reasons
+ (See developers guide, about const int)
+ * Set window titles when connected to account name.
+
+Fri Dec 19 22:49:18 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * Extended the grammar of the tarif rules.
+ New keyword: flat_init_costs necessitated by the new french
+ accounting rules.
+
+Tue Dec 16 01:54:50 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * extended scripting capabilities to allow password dialogs
+
+Sun Nov 2 14:02:31 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * accounts.cpp: applied mario's patches to the rulessets. Now deals
+ with fractions too.
+ * accounts.cpp: fixed the bug that caused kppp to reset the costs
+ even if you chose cancel.
+ * docs/kppp.sgml: doc updates and corrections
+
+Fri Aug 15 21:25:05 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * connect.cpp: leaving the lock file name edit blank will cause
+ kppp not to create lock files. That is this turn the use of lock files off
+
+ * added option to not disconnect on xserver exit/crash
+
+Sun Aug 10 19:07:07 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * kppp will no automatically disconnect on X-server exit
+
+Sun Jul 20 17:26:58 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu>
+
+ * fixed bug in phone cost accounting
diff --git a/kppp/DB/Makefile.am b/kppp/DB/Makefile.am
new file mode 100644
index 00000000..6f5a373f
--- /dev/null
+++ b/kppp/DB/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = Provider
+
+pkgdir = $(kde_datadir)/kppp/Provider
diff --git a/kppp/DB/Modem/modemDB.rc b/kppp/DB/Modem/modemDB.rc
new file mode 100644
index 00000000..e5ce011b
--- /dev/null
+++ b/kppp/DB/Modem/modemDB.rc
@@ -0,0 +1,31 @@
+[Common]
+Name=Hayes Compatible
+Reset=Z
+Init=ATV1
+Volume0=M0L0
+Volume1=M1L1
+Volume2=M1L3
+PreInitDelay=50
+PostInitDelay=30
+InitResponse=OK
+BlindDialOn=X3
+BlindDialOff=X4
+ToneDial=DT
+PulseDial=DP
+ConnectResponse=CONNECT
+BusyResponse=BUSY
+NoCarrierResponse=NO CARRIER
+NoDialTone=NO DIALTONE
+SupportsCD=true
+
+[!Zyxel Omni TA128]
+Vendor=Zyxel
+Reset=&F
+
+[Zyxel Omni TA128/X75]
+Parent=!Zyxel Omni TA128
+Init=B20 V1 E1
+
+[Zyxel Omni TA128/sync.PPP]
+Parent=!Zyxel Omni TA128
+Init=B40 V1 E1
diff --git a/kppp/DB/Provider/Austria/.directory b/kppp/DB/Provider/Austria/.directory
new file mode 100644
index 00000000..9fdf220f
--- /dev/null
+++ b/kppp/DB/Provider/Austria/.directory
@@ -0,0 +1,50 @@
+[Desktop Entry]
+Name=Austria
+Name[af]=Oostenryk
+Name[ar]=النمسا
+Name[az]=Avstriya
+Name[bg]=ÐвÑтриÑ
+Name[br]=Aostria
+Name[ca]=Àustria
+Name[cs]=Rakousko
+Name[cy]=Awstria
+Name[da]=Østrig
+Name[de]=Österreich
+Name[el]=ΑυστÏία
+Name[eo]=AÅ­strio
+Name[fo]=Eysturríki
+Name[fr]=Autriche
+Name[ga]=An Ostair
+Name[hi]=आसà¥à¤Ÿà¥à¤°à¤¿à¤¯à¤¾
+Name[hr]=Austrija
+Name[hu]=Ausztria
+Name[ja]=オーストリア
+Name[km]=អូទ្រីស
+Name[ko]=오스트리아
+Name[lt]=Austrija
+Name[lv]=Austrija
+Name[mk]=ÐвÑтрија
+Name[mn]=ÐвÑтри
+Name[mt]=Awtrija
+Name[nds]=Österriek
+Name[nl]=Oostenrijk
+Name[nso]=Australia
+Name[pt]=Ãustria
+Name[pt_BR]=Ãustria
+Name[ru]=ÐвÑтриÑ
+Name[se]=Nuortariika
+Name[sk]=Rakúsko
+Name[sl]=Avstrija
+Name[sr]=ÐуÑтрија
+Name[sr@Latn]=Austrija
+Name[sv]=Österrike
+Name[ta]=ஆஸà¯à®¤à®¿à®°à®¿à®¯à®¾
+Name[tg]=ÐвÑтриÑ
+Name[tr]=Avusturya
+Name[uk]=ÐвÑтріÑ
+Name[ven]=Ositiria
+Name[wa]=Ôtriche
+Name[zh_CN]=奥地利
+Name[zh_HK]=奧地利
+Name[zh_TW]=奧地利
+Name[zu]=I-Austriyiya
diff --git a/kppp/DB/Provider/Austria/Makefile.am b/kppp/DB/Provider/Austria/Makefile.am
new file mode 100644
index 00000000..1d7d9092
--- /dev/null
+++ b/kppp/DB/Provider/Austria/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Simon%032Media
+
+pkgdir = $(kde_datadir)/kppp/Provider/Austria/
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Austria/Simon%032Media b/kppp/DB/Provider/Austria/Simon%032Media
new file mode 100644
index 00000000..deacbcfa
--- /dev/null
+++ b/kppp/DB/Provider/Austria/Simon%032Media
@@ -0,0 +1,25 @@
+# KDE Config File
+Name=SimonMedia
+DNS=193.228.80.12,193.228.80.13,
+Domain=sime.com
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Austria/Standard/Online.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0718915250
+Username=%USERNAME%
+Password=%PASSWORD%
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Belarus/.directory b/kppp/DB/Provider/Belarus/.directory
new file mode 100644
index 00000000..834a5fbb
--- /dev/null
+++ b/kppp/DB/Provider/Belarus/.directory
@@ -0,0 +1,43 @@
+[Desktop Entry]
+Name=Belarus
+Name[ar]=بيلاروسيا
+Name[bg]=БеларуÑ
+Name[br]=Belarusi
+Name[ca]=Bielorússia
+Name[cs]=Bělorusko
+Name[da]=Hviderusland
+Name[de]=Weißrussland
+Name[el]=ΛευκοÏωσία
+Name[eo]=Bjelorusio
+Name[es]=Bielorrusia
+Name[et]=Valgevene
+Name[fr]=Biélorussie
+Name[ga]=An Bhealarúis
+Name[hi]=बेलारूस
+Name[hr]=Bjelorusija
+Name[hu]=Belarusz
+Name[it]=Bielorussia
+Name[ja]=ベラルーシ
+Name[km]=áž”áŸáž¡áž¶ážšáž»ážŸáŸ’ស
+Name[lt]=Baltarusija
+Name[lv]=Baltkrievu
+Name[mk]=БелоруÑија
+Name[nds]=Wittrussland
+Name[nl]=Wit Rusland
+Name[pl]=Białoruś
+Name[pt]=Bielorrússia
+Name[ru]=БелоруÑÑиÑ
+Name[se]=Vilges-Ruošša
+Name[sk]=Bielorusko
+Name[sl]=Belorusija
+Name[sr]=БелоруÑија
+Name[sr@Latn]=Belorusija
+Name[sv]=Vitryssland
+Name[ta]=பெலாரூஸà¯
+Name[tg]=БеларуÑиÑ
+Name[tr]=Beyaz Rusya
+Name[uk]=БілоруÑÑŒ
+Name[wa]=Belaruss
+Name[zh_CN]=白俄罗斯
+Name[zh_TW]=白俄羅斯
+Name[zu]=I Bhelarusi
diff --git a/kppp/DB/Provider/Belarus/AtlantTelecom b/kppp/DB/Provider/Belarus/AtlantTelecom
new file mode 100644
index 00000000..f6e4c1af
--- /dev/null
+++ b/kppp/DB/Provider/Belarus/AtlantTelecom
@@ -0,0 +1,29 @@
+# KDE Config File
+[Account0]
+AccountingEnabled=1
+AccountingFile=/Belarus/AtlantTelecom_12900.rst
+Authentication=4
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=AtlantTelecom
+Password=%PASSWORD%
+Phonenumber=2092676
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+TotalBytes=0
+TotalCosts=0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Belarus/Makefile.am b/kppp/DB/Provider/Belarus/Makefile.am
new file mode 100644
index 00000000..7ce9dc26
--- /dev/null
+++ b/kppp/DB/Provider/Belarus/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = .directory AtlantTelecom
+
+pkgdir = $(kde_datadir)/kppp/Provider/Belarus
+
+EXTRA_DIST = $(pkg_DATA)
+
diff --git a/kppp/DB/Provider/Czech_Republic/.directory b/kppp/DB/Provider/Czech_Republic/.directory
new file mode 100644
index 00000000..b3db40a1
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/.directory
@@ -0,0 +1,52 @@
+[Desktop Entry]
+Name=Czechia
+Name[af]=Czechië
+Name[ar]=التشيك
+Name[az]=Çex Respublikası
+Name[bg]=ЧехиÑ
+Name[br]=Tchekia
+Name[ca]=Txèquia
+Name[cs]=ÄŒesko
+Name[cy]=Tsiecia
+Name[da]=Tjekkiet
+Name[de]=Tschechien
+Name[el]=Τσεχία
+Name[eo]=Ĉeĥio
+Name[es]=República Checa
+Name[et]=TÅ¡ehhi
+Name[fr]=Tchéquie
+Name[ga]=Poblacht na Seice
+Name[hi]=चेक
+Name[hr]=Češka
+Name[hu]=Csehország
+Name[it]=Repubblica Ceca
+Name[ja]=ãƒã‚§ã‚³
+Name[km]=ឆáŸáž€
+Name[ko]=ì²´ì½”
+Name[lt]=ÄŒekija
+Name[lv]=ÄŒehija
+Name[mk]=Чешка
+Name[mn]=Чех
+Name[mt]=Cżekia
+Name[nds]=Tschechien
+Name[nl]=Tsjechië
+Name[pl]=Czechy
+Name[pt]=República Checa
+Name[pt_BR]=República Tcheca
+Name[ru]=ЧехиÑ
+Name[se]=ÄŒeahkka
+Name[sk]=ÄŒesko
+Name[sl]=Češka
+Name[sr]=Чешка
+Name[sr@Latn]=Češka
+Name[sv]=Tjeckien
+Name[ta]=செகà¯à®¯à®¾
+Name[tg]=ЧехиÑ
+Name[tr]=Çek Cumhuriyeti
+Name[uk]=ЧехіÑ
+Name[wa]=Tchekeye
+Name[zh_CN]=æ·å…‹å…±å’Œå›½
+Name[zh_HK]=æ·å…‹
+Name[zh_TW]=æ·å…‹
+Name[zu]=Isi-Czechiya
+
diff --git a/kppp/DB/Provider/Czech_Republic/AICOM b/kppp/DB/Provider/Czech_Republic/AICOM
new file mode 100644
index 00000000..d96cb2b3
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/AICOM
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=LR Premium
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971204211
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/ARsystem b/kppp/DB/Provider/Czech_Republic/ARsystem
new file mode 100644
index 00000000..5f36bc4b
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/ARsystem
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=ARsys.Net
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971214111
+Username=demo
+Password=777
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/ASYS b/kppp/DB/Provider/Czech_Republic/ASYS
new file mode 100644
index 00000000..c869f5d6
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/ASYS
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Internet ASYS
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971202911
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/ApexNet b/kppp/DB/Provider/Czech_Republic/ApexNet
new file mode 100644
index 00000000..5ee9af29
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/ApexNet
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Internet Super
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971206911
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/BohemiaNet b/kppp/DB/Provider/Czech_Republic/BohemiaNet
new file mode 100644
index 00000000..56e18e09
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/BohemiaNet
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971205211
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Brailcom b/kppp/DB/Provider/Czech_Republic/Brailcom
new file mode 100644
index 00000000..d44e7c1a
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Brailcom
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=internet zababku BASIC
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971214011
+Username=internet
+Password=zababku
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/CITYNET b/kppp/DB/Provider/Czech_Republic/CITYNET
new file mode 100644
index 00000000..bc807149
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/CITYNET
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=STANDARD
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971202611
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Contactel b/kppp/DB/Provider/Czech_Republic/Contactel
new file mode 100644
index 00000000..5f6f9ec6
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Contactel
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Internet Raz Dva
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971101211
+Username=Raz
+Password=Dva
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/ES-servis b/kppp/DB/Provider/Czech_Republic/ES-servis
new file mode 100644
index 00000000..8e352470
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/ES-servis
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Internet noc
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971208511
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Econnect b/kppp/DB/Provider/Czech_Republic/Econnect
new file mode 100644
index 00000000..35a6a22f
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Econnect
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Plne pripojeni
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971208411
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Falco_computer b/kppp/DB/Provider/Czech_Republic/Falco_computer
new file mode 100644
index 00000000..99f04039
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Falco_computer
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Bez omezeni
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971203111
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Fortech b/kppp/DB/Provider/Czech_Republic/Fortech
new file mode 100644
index 00000000..c65899d6
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Fortech
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971202311
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/HP-NET b/kppp/DB/Provider/Czech_Republic/HP-NET
new file mode 100644
index 00000000..c8b5d99d
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/HP-NET
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Internet standard
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971212111
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/INTERNET_OnLine b/kppp/DB/Provider/Czech_Republic/INTERNET_OnLine
new file mode 100644
index 00000000..bceb0979
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/INTERNET_OnLine
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=IOL Dial-up
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971103311
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/INTERNEXT b/kppp/DB/Provider/Czech_Republic/INTERNEXT
new file mode 100644
index 00000000..d6b8eed4
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/INTERNEXT
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=INTERNEXT 2000
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971207211
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/IQNET b/kppp/DB/Provider/Czech_Republic/IQNET
new file mode 100644
index 00000000..bec76cbd
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/IQNET
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=WWW Registrace
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971101711
+Username=iqnet
+Password=registrace
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/KPNQuest b/kppp/DB/Provider/Czech_Republic/KPNQuest
new file mode 100644
index 00000000..fc4d49d0
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/KPNQuest
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=DialEUnet
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971204611
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/M-soft b/kppp/DB/Provider/Czech_Republic/M-soft
new file mode 100644
index 00000000..c08a7b29
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/M-soft
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Neomezeny
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971201611
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Makefile.am b/kppp/DB/Provider/Czech_Republic/Makefile.am
new file mode 100644
index 00000000..e6cb709e
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Makefile.am
@@ -0,0 +1,18 @@
+pkg_DATA = .directory \
+ AICOM ARsystem ASYS ApexNet \
+ BohemiaNet Brailcom \
+ CITYNET Contactel \
+ ES-servis Econnect \
+ Falco_computer Fortech \
+ HP-NET \
+ INTERNET_OnLine INTERNEXT IQNET \
+ KPNQuest \
+ M-soft \
+ Nextra \
+ ProfiNet \
+ SeverNET \
+ Video_OnLine Volny
+
+pkgdir = $(kde_datadir)/kppp/Provider/Czech_Republic
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Czech_Republic/Nextra b/kppp/DB/Provider/Czech_Republic/Nextra
new file mode 100644
index 00000000..9e569479
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Nextra
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Nextra Business
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971100311
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/ProfiNet b/kppp/DB/Provider/Czech_Republic/ProfiNet
new file mode 100644
index 00000000..a01cfe69
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/ProfiNet
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Dial-up Standard
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971215311
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/SeverNET b/kppp/DB/Provider/Czech_Republic/SeverNET
new file mode 100644
index 00000000..3eb09a39
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/SeverNET
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=Free
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971206811
+Username=free
+Password=severnet
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Video_OnLine b/kppp/DB/Provider/Czech_Republic/Video_OnLine
new file mode 100644
index 00000000..7af64ef4
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Video_OnLine
@@ -0,0 +1,32 @@
+# KDE Config File
+#Permission for inclusion of the czech ISP list for KDE 2.0.1 and
+#further versions is hereby granted to the KDE team as long as they
+#maintain the apropriate copyright notice "(c) by SuSE CR, s.r.o.".
+#
+#This permission is also valid for future versions of the czech ISP
+#list until stated otherwise.
+#
+Name=VOLny
+DNS=
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971200111
+Username=
+Password=
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
diff --git a/kppp/DB/Provider/Czech_Republic/Volny b/kppp/DB/Provider/Czech_Republic/Volny
new file mode 100644
index 00000000..e3e2ee3f
--- /dev/null
+++ b/kppp/DB/Provider/Czech_Republic/Volny
@@ -0,0 +1,26 @@
+# KDE Config File
+Name=Volný
+DNS=212.20.96.34,212.20.96.38,
+Domain=
+AccountingEnabled=1
+ScriptArguments=
+ScriptCommands=
+AccountingFile=/Czechia/Czech_Telecom_Internet2000.rst
+VolumeAccountingEnabled=1
+AutoName=0
+pppdArguments=-detach,defaultroute,modem,debug,crtscts,
+Authentication=3
+Gateway=0.0.0.0
+Phonenumber=0971200111
+Username=%USERNAME%
+Password=%PASSWORD%
+BeforeConnect=
+DisconnectCommand=
+BeforeDisconnect=
+Command=
+StorePassword=1
+DefaultRoute=1
+ExDNSDisabled=0
+IPAddr=0.0.0.0
+SubnetMask=0.0.0.0
+
diff --git a/kppp/DB/Provider/Denmark/.directory b/kppp/DB/Provider/Denmark/.directory
new file mode 100644
index 00000000..a2f66e20
--- /dev/null
+++ b/kppp/DB/Provider/Denmark/.directory
@@ -0,0 +1,52 @@
+[Desktop Entry]
+Name=Denmark
+Name[af]=Denemarke
+Name[ar]=الدنمارك
+Name[az]=Danimarka
+Name[bg]=ДаниÑ
+Name[br]=Danmark
+Name[ca]=Dinamarca
+Name[cs]=Dánsko
+Name[cy]=Denmarc
+Name[da]=Danmark
+Name[de]=Dänemark
+Name[el]=Δανία
+Name[eo]=Danlando
+Name[es]=Dinamarca
+Name[et]=Taani
+Name[fo]=Danmark
+Name[fr]=Danemark
+Name[ga]=An Danmhairg
+Name[hi]=डेनमारà¥à¤•
+Name[hr]=Danska
+Name[hu]=Dánia
+Name[it]=Danimarca
+Name[ja]=デンマーク
+Name[km]=ដាណឺម៉ាក
+Name[ko]=ë´ë§ˆí¬
+Name[lt]=Danija
+Name[lv]=DÄnija
+Name[mk]=ДанÑка
+Name[mn]=Дани
+Name[mt]=Danimarka
+Name[nds]=Dänmark
+Name[nl]=Denemarken
+Name[pl]=Dania
+Name[pt]=Dinamarca
+Name[pt_BR]=Dinamarca
+Name[ru]=ДаниÑ
+Name[se]=Dánmárku
+Name[sk]=Dánsko
+Name[sl]=Danska
+Name[sr]=ДанÑка
+Name[sr@Latn]=Danska
+Name[sv]=Danmark
+Name[ta]=டெனà¯à®®à®¾à®°à¯à®•à¯
+Name[tg]=ДаниÑ
+Name[tr]=Danimarka
+Name[uk]=ДаніÑ
+Name[wa]=Daenmåtche
+Name[zh_CN]=丹麦
+Name[zh_HK]=丹麥
+Name[zh_TW]=丹麥
+Name[zu]=Isi-Denimaki
diff --git a/kppp/DB/Provider/Denmark/Get2Net b/kppp/DB/Provider/Denmark/Get2Net
new file mode 100644
index 00000000..f2427f2e
--- /dev/null
+++ b/kppp/DB/Provider/Denmark/Get2Net
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=0
+Phonenumber=77887735
+IPAddr=0.0.0.0
+Domain=
+Name=Get2Net
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=195.82.195.101,129.142.7.101,
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Denmark/Makefile.am b/kppp/DB/Provider/Denmark/Makefile.am
new file mode 100644
index 00000000..793c1074
--- /dev/null
+++ b/kppp/DB/Provider/Denmark/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Get2Net
+
+pkgdir = $(kde_datadir)/kppp/Provider/Denmark
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/France/.directory b/kppp/DB/Provider/France/.directory
new file mode 100644
index 00000000..d874df05
--- /dev/null
+++ b/kppp/DB/Provider/France/.directory
@@ -0,0 +1,48 @@
+[Desktop Entry]
+Name=France
+Name[ar]=Ùرنسا
+Name[az]=Fransa
+Name[bg]=ФранциÑ
+Name[br]=Frañs
+Name[ca]=França
+Name[cs]=Francie
+Name[cy]=Ffrainc
+Name[da]=Frankrig
+Name[de]=Frankreich
+Name[el]=Γαλλία
+Name[eo]=Francio
+Name[es]=Francia
+Name[et]=Prantsusmaa
+Name[ga]=An Fhrainc
+Name[hi]=फà¥à¤°à¤¾à¤‚स
+Name[hr]=Francuska
+Name[hu]=Franciaország
+Name[id]=Prancis
+Name[it]=Francia
+Name[ja]=フランス
+Name[km]=បារាំង
+Name[lt]=Prancūzija
+Name[lv]=Francija
+Name[mk]=Франција
+Name[mt]=Franza
+Name[nds]=Frankriek
+Name[nl]=Frankrijk
+Name[pl]=Francja
+Name[pt]=França
+Name[pt_BR]=França
+Name[ru]=ФранциÑ
+Name[se]=Fránkriika
+Name[sk]=Francúzsko
+Name[sl]=Francija
+Name[sr]=ФранцуÑка
+Name[sr@Latn]=Francuska
+Name[sv]=Frankrike
+Name[ta]=பிரானà¯à®šà¯
+Name[tg]=ФаронÑа
+Name[tr]=Fransa
+Name[uk]=ФранціÑ
+Name[ven]=Fura
+Name[xh]=Franisi
+Name[zh_CN]=法国
+Name[zh_TW]=法國
+Name[zu]=Isi Frentshi
diff --git a/kppp/DB/Provider/France/ClubInternetFull b/kppp/DB/Provider/France/ClubInternetFull
new file mode 100644
index 00000000..da121a8d
--- /dev/null
+++ b/kppp/DB/Provider/France/ClubInternetFull
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=
+Authentication=1
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=club-internet.fr
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=ClubInternet
+Password=%PASSWORD%
+Phonenumber=0860301020
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/France/Makefile.am b/kppp/DB/Provider/France/Makefile.am
new file mode 100644
index 00000000..282315e8
--- /dev/null
+++ b/kppp/DB/Provider/France/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory ClubInternetFull
+
+pkgdir = $(kde_datadir)/kppp/Provider/France
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Germany/.directory b/kppp/DB/Provider/Germany/.directory
new file mode 100644
index 00000000..dfda7bdc
--- /dev/null
+++ b/kppp/DB/Provider/Germany/.directory
@@ -0,0 +1,54 @@
+[Desktop Entry]
+Name=Germany
+Name[af]=Duitsland
+Name[ar]=ألمانيا
+Name[az]=Almaniya
+Name[bg]=ГерманиÑ
+Name[br]=Alamagn
+Name[ca]=Alemanya
+Name[cs]=Německo
+Name[cy]=Yr Almaen
+Name[da]=Tyskland
+Name[de]=Deutschland
+Name[el]=ΓεÏμανία
+Name[eo]=Germanio
+Name[es]=Alemania
+Name[et]=Saksamaa
+Name[fo]=Týskland
+Name[fr]=Allemagne
+Name[ga]=An Ghearmáin
+Name[hi]=जरà¥à¤®à¤¨à¥€
+Name[hr]=NjemaÄka
+Name[hu]=Németország
+Name[id]=Jerman
+Name[it]=Germania
+Name[ja]=ドイツ
+Name[km]=អាល្លឺម៉ង់
+Name[ko]=ë…ì¼
+Name[lt]=Vokietija
+Name[lv]=VÄcija
+Name[mk]=Германија
+Name[mn]=Германи
+Name[mt]=Ä ermanja
+Name[nds]=Düütschland
+Name[nl]=Duitsland
+Name[pl]=Niemcy
+Name[pt]=Alemanha
+Name[pt_BR]=Alemanha
+Name[ru]=ГерманиÑ
+Name[se]=Duiska
+Name[sk]=Nemecko
+Name[sl]=NemÄija
+Name[sr]=Ðемачка
+Name[sr@Latn]=NemaÄka
+Name[sv]=Tyskland
+Name[ta]=ஜெரà¯à®®à®©à®¿
+Name[tg]=Олмон
+Name[tr]=Almanya
+Name[uk]=Ðімеччина
+Name[ven]=Geremani
+Name[wa]=Almagne
+Name[zh_CN]=德国
+Name[zh_HK]=德國
+Name[zh_TW]=德國
+Name[zu]=Isi-Jalimani
diff --git a/kppp/DB/Provider/Germany/CityWeb b/kppp/DB/Provider/Germany/CityWeb
new file mode 100644
index 00000000..9f53218d
--- /dev/null
+++ b/kppp/DB/Provider/Germany/CityWeb
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=0029315267000
+IPAddr=0.0.0.0
+Name=cityweb
+Domain=cityweb.de
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=193.189.224.2,193.189.231.2,
+AccountingFile=/Germany/Telekom_City.rst
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Germany/FH%032Rhein%032Sieg%032%040Informatik%041 b/kppp/DB/Provider/Germany/FH%032Rhein%032Sieg%032%040Informatik%041
new file mode 100644
index 00000000..a53ba8a3
--- /dev/null
+++ b/kppp/DB/Provider/Germany/FH%032Rhein%032Sieg%032%040Informatik%041
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0224193400
+IPAddr=0.0.0.0
+Domain=fh-rhein-sieg.de
+Name=FH Rhein Sieg
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=
+Authentication=1
+DNS=194.95.66.9
+SubnetMask=0.0.0.0
+AccountingFile=/Germany/Telekom_City.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Germany/MUC%046DE b/kppp/DB/Provider/Germany/MUC%046DE
new file mode 100644
index 00000000..e26c1750
--- /dev/null
+++ b/kppp/DB/Provider/Germany/MUC%046DE
@@ -0,0 +1,26 @@
+# KDE Config File
+Authentication=1
+AccountingFile=
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DialPrefix=
+DisconnectCommand=
+Domain=muc.de
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=MUC.DE
+Password=%PASSWORD%
+Phonenumber=32479752
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
diff --git a/kppp/DB/Provider/Germany/Makefile.am b/kppp/DB/Provider/Germany/Makefile.am
new file mode 100644
index 00000000..53153d6c
--- /dev/null
+++ b/kppp/DB/Provider/Germany/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = .directory CityWeb Netsurf \
+ FH%032Rhein%032Sieg%032%040Informatik%041 MUC%046DE
+
+pkgdir = $(kde_datadir)/kppp/Provider/Germany
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Germany/Netsurf b/kppp/DB/Provider/Germany/Netsurf
new file mode 100644
index 00000000..78673602
--- /dev/null
+++ b/kppp/DB/Provider/Germany/Netsurf
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0228964590
+IPAddr=0.0.0.0
+Domain=ivm.de
+Name=IVM
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=192.168.1.1
+SubnetMask=0.0.0.0
+AccountingFile=/Germany/Telekom_City.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Ireland/.directory b/kppp/DB/Provider/Ireland/.directory
new file mode 100644
index 00000000..a80c4851
--- /dev/null
+++ b/kppp/DB/Provider/Ireland/.directory
@@ -0,0 +1,43 @@
+[Desktop Entry]
+Name=Ireland
+Name[ar]=إيرلندا
+Name[bg]=ИрландиÑ
+Name[br]=Iwerzhon
+Name[ca]=Irlanda
+Name[cs]=Irsko
+Name[cy]=Iwerddon
+Name[da]=Irland
+Name[de]=Irland
+Name[el]=ΙÏλανδία
+Name[eo]=Irlando
+Name[es]=Irlanda
+Name[et]=Iirimaa
+Name[fr]=Irlande
+Name[ga]=Éire
+Name[hi]=आयरलैंड
+Name[hr]=Irska
+Name[hu]=Ãrország
+Name[it]=Irlanda
+Name[ja]=アイルランド
+Name[km]=អៀរឡង់
+Name[lt]=Airija
+Name[mk]=ИрÑка
+Name[nds]=Irland
+Name[nl]=Ierland
+Name[pl]=Irlandia
+Name[pt]=Irlanda
+Name[pt_BR]=Irlanda
+Name[ru]=ИрландиÑ
+Name[se]=Irlánda
+Name[sk]=Ãrsko
+Name[sl]=Irska
+Name[sr]=ИрÑка
+Name[sr@Latn]=Irska
+Name[sv]=Irland
+Name[ta]=அயரà¯à®²à®¾à®¨à¯à®¤à¯
+Name[tg]=ИрландиÑ
+Name[tr]=Ä°rlanda
+Name[uk]=ІрландіÑ
+Name[wa]=Irlande
+Name[zh_CN]=爱尔兰
+Name[zh_TW]=愛爾蘭
diff --git a/kppp/DB/Provider/Ireland/Eircom b/kppp/DB/Provider/Ireland/Eircom
new file mode 100644
index 00000000..3068e665
--- /dev/null
+++ b/kppp/DB/Provider/Ireland/Eircom
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=
+Authentication=1
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=Eircom.ie
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Eircom(ROI)
+Password=%PASSWORD%
+Phonenumber=1892150150
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Ireland/IOL b/kppp/DB/Provider/Ireland/IOL
new file mode 100644
index 00000000..4716d60d
--- /dev/null
+++ b/kppp/DB/Provider/Ireland/IOL
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=
+Authentication=1
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=IOL.ie
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=IOL(ROI)
+Password=%PASSWORD%
+Phonenumber=1890924042
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Ireland/Makefile.am b/kppp/DB/Provider/Ireland/Makefile.am
new file mode 100644
index 00000000..597a481d
--- /dev/null
+++ b/kppp/DB/Provider/Ireland/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Eircom IOL
+
+pkgdir = $(kde_datadir)/kppp/Provider/Irland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Makefile.am b/kppp/DB/Provider/Makefile.am
new file mode 100644
index 00000000..94db26fd
--- /dev/null
+++ b/kppp/DB/Provider/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = Austria Denmark Germany Netherlands New_Zealand Norway Portugal \
+ United_Kingdom Taiwan Slovenia Sweden Czech_Republic Yugoslavia Ukraine \
+ Switzerland Belarus France Ireland
diff --git a/kppp/DB/Provider/Netherlands/.directory b/kppp/DB/Provider/Netherlands/.directory
new file mode 100644
index 00000000..d8d3bbea
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/.directory
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Name=Netherlands
+Name[af]=Nederland
+Name[ar]=هولندا
+Name[az]=Hollandiya
+Name[bg]=ХоландиÑ
+Name[br]=Izelvroioù
+Name[ca]=Països Baixos
+Name[cs]=Nizozemí
+Name[cy]=Yr Iseldiroedd
+Name[da]=Holland
+Name[de]=Niederlande
+Name[el]=Κάτω ΧώÏες
+Name[eo]=Nederlando
+Name[es]=Países Bajos
+Name[et]=Holland
+Name[fo]=Háland
+Name[fr]=Pays-Bas
+Name[ga]=An Ãsiltír
+Name[hi]=नीदरलैंडà¥à¤¸
+Name[hr]=Nizozemska
+Name[hu]=Hollandia
+Name[id]=Belanda
+Name[it]=Paesi Bassi
+Name[ja]=オランダ
+Name[km]=ហូល្លង់
+Name[ko]=네ëœëž€ë“œ
+Name[lt]=Olandija
+Name[lv]=NÄ«derlande
+Name[mk]=Холандија
+Name[mn]=Ðидерланд
+Name[ms]=Belanda
+Name[nds]=Nedderlannen
+Name[nl]=Nederland
+Name[pl]=Holandia
+Name[pt]=Holanda
+Name[pt_BR]=Holanda
+Name[ru]=Ðидерланды
+Name[se]=Hollánda
+Name[sk]=Holandsko
+Name[sl]=Nizozemska
+Name[sr]=Холандија
+Name[sr@Latn]=Holandija
+Name[sv]=Nederländerna
+Name[ta]=நெதரà¯à®²à®¾à®¨à¯à®¤à¯
+Name[tg]=Ðидерландҳо
+Name[tr]=Hollanda
+Name[uk]=Ðідерланди
+Name[wa]=Bas Payis
+Name[zh_CN]=è·å…°
+Name[zh_HK]=è·è˜­
+Name[zh_TW]=è·è˜­
+Name[zu]=I-Netherlands
diff --git a/kppp/DB/Provider/Netherlands/12Move b/kppp/DB/Provider/Netherlands/12Move
new file mode 100644
index 00000000..7dd00c1b
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/12Move
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoDNS=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676077777
+IPAddr=0.0.0.0
+Domain=
+Name=12Move
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.240.240.254,195.240.240.222
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/12Move.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Bart b/kppp/DB/Provider/Netherlands/Bart
new file mode 100644
index 00000000..5805216f
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Bart
@@ -0,0 +1,26 @@
+# KDE Config File
+# www.bart.nl
+ExDNSDisabled=0
+AutoDNS=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676077111
+IPAddr=0.0.0.0
+Domain=
+Name=Bart
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.158.160.10,194.158.191.254
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Betuwenet%032BFree b/kppp/DB/Provider/Netherlands/Betuwenet%032BFree
new file mode 100644
index 00000000..e99355ba
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Betuwenet%032BFree
@@ -0,0 +1,27 @@
+# KDE Config File
+#informatie van http://www.betuwe.net/kop/inbel.html
+# en van http://www.betuwe.net/kop/faq.html#F
+
+ExDNSDisabled=0
+DNS=212.29.160.1,212.29.161.254
+AccountingEnabled=0
+AccountingFile=
+AutoName=0
+ScriptArguments=
+Phonenumber=0877880001
+IPAddr=0.0.0.0
+Domain=
+Name=Betuwenet BFree
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+SubnetMask=0.0.0.0
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Betuwenet%032BQuality b/kppp/DB/Provider/Netherlands/Betuwenet%032BQuality
new file mode 100644
index 00000000..db7a6261
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Betuwenet%032BQuality
@@ -0,0 +1,26 @@
+# KDE Config File
+#informatie van http://www.betuwe.net/kop/inbel.html
+# en van http://www.betuwe.net/kop/faq.html#F
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676000805
+IPAddr=0.0.0.0
+Domain=
+Name=Betuwenet BQuality
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=212.29.160.1,212.29.161.254
+SubnetMask=0.0.0.0
+AccountingFile=Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Cistron b/kppp/DB/Provider/Netherlands/Cistron
new file mode 100644
index 00000000..d3fa5a07
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Cistron
@@ -0,0 +1,29 @@
+# KDE Config File
+# gegevens van
+# http://www.cistron.nl/node.php?id=70&sqlcistron=b9093c0a65c4836c62715eb05bcf71f4
+# cistron is nu xs4all, echter onderstaand inbelnummer werkt nog steeds
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676070809
+IPAddr=0.0.0.0
+Domain=
+Name=Cistron
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AccountingFile=/Netherlands/BelBasis_Regio_Nummervoordeel.rst
diff --git a/kppp/DB/Provider/Netherlands/Concepts%032ICT b/kppp/DB/Provider/Netherlands/Concepts%032ICT
new file mode 100644
index 00000000..f4b38e68
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Concepts%032ICT
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.concepts-ict.nl
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=213.197.28.3,213.197.24.3
+DefaultRoute=1
+DisconnectCommand=
+Domain=concepts.nl
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Concepts_ICT
+Password=%PASSWORD%
+Phonenumber=0676050500
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
+AutoDNS=1 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Cubic%032Circle b/kppp/DB/Provider/Netherlands/Cubic%032Circle
new file mode 100644
index 00000000..b6db22a4
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Cubic%032Circle
@@ -0,0 +1,28 @@
+# KDE Config File
+# gegevens van http://www.cuci.nl/guide/faq.n.html
+
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676070809
+IPAddr=0.0.0.0
+Domain=cm.cuci.nl
+Name=Cubic Circle
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AccountingFile=/Netherlands/BelBasis_Regio_Nummervoordeel.rst
diff --git a/kppp/DB/Provider/Netherlands/Dataweb b/kppp/DB/Provider/Netherlands/Dataweb
new file mode 100644
index 00000000..a8df703c
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Dataweb
@@ -0,0 +1,25 @@
+# KDE Config File
+# gegevens van http://www.dataweb.nl/
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676088988
+IPAddr=0.0.0.0
+Domain=dataweb.nl
+Name=Dataweb
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=62.166.128.10,62.166.128.20
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Daxis%032Internet b/kppp/DB/Provider/Netherlands/Daxis%032Internet
new file mode 100644
index 00000000..dc376334
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Daxis%032Internet
@@ -0,0 +1,25 @@
+# KDE Config File
+# http://www.daxis.nl/ServiceCenter.aspx
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=195.35.192.131,195.35.192.132
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Daxis Internet
+Password=%PASSWORD%
+Phonenumber=0676085324
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
diff --git a/kppp/DB/Provider/Netherlands/Demon%032Internet b/kppp/DB/Provider/Netherlands/Demon%032Internet
new file mode 100644
index 00000000..36677a5f
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Demon%032Internet
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.demon.nl/support/inbelverbinding/inbelnummers.html#nummers
+# http://www.demon.nl/support/inbelverbinding/intro.html
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676033666:0676077666
+IPAddr=0.0.0.0
+Domain=demon.nl
+Name=Demon Internet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.159.73.135,194.159.73.136
+SubnetMask=255.255.255.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=194.159.73.222
+StorePassword=1
+DisconnectCommand=
+pppdArguments=-detach,mru 576,mtu 576
diff --git a/kppp/DB/Provider/Netherlands/Energis-Ision b/kppp/DB/Provider/Netherlands/Energis-Ision
new file mode 100644
index 00000000..3573d9d7
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Energis-Ision
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.energis-ision.nl/klantenservice/index.html
+
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676088888
+IPAddr=0.0.0.0
+Domain=
+Name=Energis-Ision
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.64.32.3,195.64.32.43
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Euronet%032anytime b/kppp/DB/Provider/Netherlands/Euronet%032anytime
new file mode 100644
index 00000000..03b68ed0
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Euronet%032anytime
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://home.euronet.nl/tools/help/algemeen.php?lang=ned&textonly=no
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=0
+AutoDNS=1
+Phonenumber=0676022203
+IPAddr=0.0.0.0
+Domain=euronet.nl
+Name=Euronet Anytime
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.134.5.5,194.134.0.97
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Euronet%032professional b/kppp/DB/Provider/Netherlands/Euronet%032professional
new file mode 100644
index 00000000..d591269d
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Euronet%032professional
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676022206
+IPAddr=0.0.0.0
+Domain=euronet.nl
+Name=Euronet Professional
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.134.5.5,194.134.0.97
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/FlakkeeNet b/kppp/DB/Provider/Netherlands/FlakkeeNet
new file mode 100644
index 00000000..c5b2c089
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/FlakkeeNet
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.flakkee.net/flakkeenet/helpdesk/helpdesk.htm
+# http://www.flakkee.net/flakkeenet/helpdesk/inbelnrs.htm
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676000805
+IPAddr=0.0.0.0
+Domain=
+Name=FlakkeeNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/FreeAcces b/kppp/DB/Provider/Netherlands/FreeAcces
new file mode 100644
index 00000000..f0337171
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/FreeAcces
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.freeaccess.nl/informatie-hoe-win95.html
+
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676085000
+IPAddr=0.0.0.0
+Domain=
+Name=Freeacces
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.85.130.70,195.85.130.69
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%PASSWORD%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Freeler%032basis b/kppp/DB/Provider/Netherlands/Freeler%032basis
new file mode 100644
index 00000000..b96650fe
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Freeler%032basis
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.freeler.nl/service/service_instellingen.html
+AutoDNS=1
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676056789
+IPAddr=0.0.0.0
+Domain=
+Name=Freeler
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=213.218.75.90,213.218.75.91
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/Freeler_Basis.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Freeler%032compleet b/kppp/DB/Provider/Netherlands/Freeler%032compleet
new file mode 100644
index 00000000..d4989c89
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Freeler%032compleet
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.freeler.nl/service/service_instellingen.html
+#
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676043210
+IPAddr=0.0.0.0
+Domain=
+Name=Freeler Compleet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=213.218.75.90,213.218.75.91
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Freeler%032voordelig b/kppp/DB/Provider/Netherlands/Freeler%032voordelig
new file mode 100644
index 00000000..7547e2e0
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Freeler%032voordelig
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.freeler.nl/service/service_instellingen.html
+#
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676020202
+IPAddr=0.0.0.0
+Domain=
+Name=Freeler Compleet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=213.218.76.150,213.218.76.151
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/Freeler_Voordelig.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/HCC%032NET b/kppp/DB/Provider/Netherlands/HCC%032NET
new file mode 100644
index 00000000..473d63af
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/HCC%032NET
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www3.hccnet.nl/info/support/overzicht.cfm?onderwerp=65&code_1=F&code_2=B&code_3=B&code_4=A
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=4
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=HCC Net
+Password=%PASSWORD%
+Phonenumber=0676042222
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
+AutoDNS=1
diff --git a/kppp/DB/Provider/Netherlands/Hacom b/kppp/DB/Provider/Netherlands/Hacom
new file mode 100644
index 00000000..610f260b
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Hacom
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.hacom.nl/info/internetdoc.html
+
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676090000
+IPAddr=0.0.0.0
+Domain=hacom.nl
+Name=Hacom
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/HetNet%032Basis%032Surfen b/kppp/DB/Provider/Netherlands/HetNet%032Basis%032Surfen
new file mode 100644
index 00000000..d9a260b9
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/HetNet%032Basis%032Surfen
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676001505
+IPAddr=0.0.0.0
+Domain=
+Name=HetNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=195.121.1.34,195.121.1.66
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/HetNet%032Frequent%032Surfen b/kppp/DB/Provider/Netherlands/HetNet%032Frequent%032Surfen
new file mode 100644
index 00000000..a3e22a32
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/HetNet%032Frequent%032Surfen
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676001505
+IPAddr=0.0.0.0
+Domain=
+Name=HetNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=195.121.1.34,195.121.1.66
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/HetNet%032Regelmatig%032Surfen b/kppp/DB/Provider/Netherlands/HetNet%032Regelmatig%032Surfen
new file mode 100644
index 00000000..cd392b7c
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/HetNet%032Regelmatig%032Surfen
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676001505
+IPAddr=0.0.0.0
+Domain=
+Name=HetNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=195.121.1.34,195.121.1.66
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/HetNet_Regelmatig_Surfen.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/IAE b/kppp/DB/Provider/Netherlands/IAE
new file mode 100644
index 00000000..3f527ee9
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/IAE
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoDNS=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676077111
+IPAddr=0.0.0.0
+Domain=
+Name=IAE
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=212.61.15.8,212.61.25.226
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/ILimburg b/kppp/DB/Provider/Netherlands/ILimburg
new file mode 100644
index 00000000..5a6b9251
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/ILimburg
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://home.ilimburg.nl/b/totaalb2.3.3.5.html
+
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0464280030
+IPAddr=0.0.0.0
+Domain=
+Name=Internet Limburg
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/InterNLnet b/kppp/DB/Provider/Netherlands/InterNLnet
new file mode 100644
index 00000000..5c8b2488
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/InterNLnet
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676044000
+IPAddr=0.0.0.0
+Domain=
+Name=InterNLnet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=217.149.196.6,217.149.192.6
+SubnetMask=0.0.0.0
+AccountingFile=Netherlands/InterNlnet.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Interbox b/kppp/DB/Provider/Netherlands/Interbox
new file mode 100644
index 00000000..a7410ee4
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Interbox
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.box.nl/helpdesk/inbelnummers/
+#
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676000803
+IPAddr=0.0.0.0
+Domain=
+Name=
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1
diff --git a/kppp/DB/Provider/Netherlands/Internet%032Acces%032Facilities b/kppp/DB/Provider/Netherlands/Internet%032Acces%032Facilities
new file mode 100644
index 00000000..be14e329
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Internet%032Acces%032Facilities
@@ -0,0 +1,25 @@
+# KDE Config File
+# http://www.iaf.nl/support/instellingen.html
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=80.89.224.64,195.108.17.20
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=80.89.224.1
+IPAddr=0.0.0.0
+Name=Internet Acces Facilities
+Password=%PASSWORD%
+Phonenumber=0676042328
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
diff --git a/kppp/DB/Provider/Netherlands/Internet%032Online b/kppp/DB/Provider/Netherlands/Internet%032Online
new file mode 100644
index 00000000..dc11becc
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Internet%032Online
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.io.nl/support.html
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676088030
+IPAddr=0.0.0.0
+Domain=
+Name=Internet Online
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Interstroom b/kppp/DB/Provider/Netherlands/Interstroom
new file mode 100644
index 00000000..300cd318
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Interstroom
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Interstroom
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/IntroWeb%032Hengelo%032e.o. b/kppp/DB/Provider/Netherlands/IntroWeb%032Hengelo%032e.o.
new file mode 100644
index 00000000..55bf590b
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/IntroWeb%032Hengelo%032e.o.
@@ -0,0 +1,24 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=195.86.14.10,195.86.120.12
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Introweb
+Password=%PASSWORD%
+Phonenumber=074-2503972
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
diff --git a/kppp/DB/Provider/Netherlands/IntroWeb%032met%0323-cijferig%032kengetal b/kppp/DB/Provider/Netherlands/IntroWeb%032met%0323-cijferig%032kengetal
new file mode 100644
index 00000000..284ba516
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/IntroWeb%032met%0323-cijferig%032kengetal
@@ -0,0 +1,25 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=195.86.14.10,195.86.120.12
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Introweb
+Password=%PASSWORD%
+Phonenumber=8800830
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
+
diff --git a/kppp/DB/Provider/Netherlands/IntroWeb%032met%0324-cijferig%032kengetal b/kppp/DB/Provider/Netherlands/IntroWeb%032met%0324-cijferig%032kengetal
new file mode 100644
index 00000000..f029483c
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/IntroWeb%032met%0324-cijferig%032kengetal
@@ -0,0 +1,25 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+Authentication=1
+AutoName=0
+BeforeDisconnect=
+Command=
+DNS=195.86.14.10,195.86.120.12
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Introweb
+Password=%PASSWORD%
+Phonenumber=880830
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=1
+pppdArguments=
+
diff --git a/kppp/DB/Provider/Netherlands/Kabelfoon b/kppp/DB/Provider/Netherlands/Kabelfoon
new file mode 100644
index 00000000..443d5e1f
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Kabelfoon
@@ -0,0 +1,66 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Demos 1 Network
+Password=%PASSWORD%
+Phonenumber=4
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=asyncmap 0,noauth,lock,crtscts,noipdefault,-proxyarp
+[Desktop Entry]
+Icon=
+[General]
+DefaultAccount=Demos 1 Network
+NumberOfAccounts=1
+PPPDebug=0
+ShowLogWindow=0
+pppdTimeout=15
+[Graph]
+Background=255,255,255
+Enabled=true
+InBytes=0,0,255
+OutBytes=255,0,0
+Text=0,0,0
+[Modem]
+AnswerResponse=CONNECT
+AnswerString=ATA
+BusyResponse=BUSY
+ConnectResponse=CONNECT
+DialString=AT&D0D
+EscapeGuardTime=50
+EscapeResponse=OK
+EscapeString=+++
+FlowControl=Geen
+HangUpResponse=OK
+HangupString=ATH
+InitDelay=50
+InitResponse=OK
+InitString=ATZ
+NoCarrierResponse=NO CARRIER
+NoDialToneResp=NO DIALTONE
+PreInitDelay=50
+RingResponse=RING
+Speed=115200
+Timeout=30
+UseLockFile=0
+Volume=0
+VolumeHigh=M0L0
+VolumeMedium=M0L0
+VolumeOff=M0L0
diff --git a/kppp/DB/Provider/Netherlands/KeyAcces%032met%0323%032cijferig%032kengetal b/kppp/DB/Provider/Netherlands/KeyAcces%032met%0323%032cijferig%032kengetal
new file mode 100644
index 00000000..d23b3332
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/KeyAcces%032met%0323%032cijferig%032kengetal
@@ -0,0 +1,26 @@
+# KDE Config File
+#
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=8500015
+IPAddr=0.0.0.0
+Domain=
+Name=KeyAcces
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/KeyAcces%032met%0324%032cijferig%032kengetal b/kppp/DB/Provider/Netherlands/KeyAcces%032met%0324%032cijferig%032kengetal
new file mode 100644
index 00000000..a49a4373
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/KeyAcces%032met%0324%032cijferig%032kengetal
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.keyaccess.nl/
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=850015
+IPAddr=0.0.0.0
+Domain=
+Name=KeyAcces
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Luna b/kppp/DB/Provider/Netherlands/Luna
new file mode 100644
index 00000000..ce82c7c3
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Luna
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.luna.nl/in/helpdesk/index.html
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Luna
+AutoDNS=1
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=217.77.129.12,212.204.207.221
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Macom b/kppp/DB/Provider/Netherlands/Macom
new file mode 100644
index 00000000..f67c8146
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Macom
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.macon.nl/info/landelijk.htm#Windows95
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Macom
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Makefile.am b/kppp/DB/Provider/Netherlands/Makefile.am
new file mode 100644
index 00000000..d578c09a
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Makefile.am
@@ -0,0 +1,67 @@
+pkg_DATA = .directory \
+ Kabelfoon \
+ HCC%032NET \
+ 12Move \
+ FlakkeeNet \
+ IntroWeb%032Hengelo%032e.o. \
+ Support%032Net \
+ FreeAcces \
+ IntroWeb%032met%0323-cijferig%032kengetal \
+ Telebyte \
+ Bart \
+ Freeler%032basis \
+ IntroWeb%032met%0324-cijferig%032kengetal \
+ Tiscali%032Compleet \
+ Betuwenet%032BFree \
+ Freeler%032compleet \
+ KeyAcces%032met%0323%032cijferig%032kengetal \
+ Tiscali%032Gratis \
+ Betuwenet%032BQuality \
+ Freeler%032voordelig \
+ KeyAcces%032met%0324%032cijferig%032kengetal \
+ UwNet \
+ Hacom \
+ Luna \
+ Via%032Networks \
+ Cistron \
+ HetNet%032Basis%032Surfen \
+ Macom \
+ Wannadoo%032budget \
+ Concepts%032ICT \
+ HetNet%032Frequent%032Surfen \
+ Wannadoo%032budget%320plus \
+ Cubic%032Circle \
+ HetNet%032Regelmatig%032Surfen \
+ Nederland.net \
+ Wannadoo%032smartpack \
+ IAE \
+ Planet%032Internet%032Premium \
+ Wirehub \
+ Dataweb \
+ ILimburg \
+ Planet%032Internet%032Standaard \
+ XS4All \
+ Daxis%032Internet \
+ InterNLnet \
+ Plant%032Acces \
+ Zeelandnet \
+ Demon%032Internet \
+ Interbox \
+ Popin \
+ Zon%0322 \
+ Energis-Ision \
+ Internet%032Acces%032Facilities \
+ PublishNet \
+ Zon%032Gratis \
+ Euronet%032anytime \
+ Internet%032Online \
+ Raketnet \
+ Zon%032Inclusief \
+ Euronet%032professional \
+ Interstroom \
+ Solcon
+
+
+pkgdir = $(kde_datadir)/kppp/Provider/Netherlands
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Netherlands/Nederland.net b/kppp/DB/Provider/Netherlands/Nederland.net
new file mode 100644
index 00000000..f8d50641
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Nederland.net
@@ -0,0 +1,28 @@
+# KDE Config File
+# Nederland.net werkt via wirehub
+# http://support.wirehub.net/cgi-bin/page/dialup.cgi?NetNr=*
+# http://support.wirehub.net/pub/dialup/servers
+ExDNSDisabled=0
+AutoDNS=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676090909
+IPAddr=0.0.0.0
+Domain=
+Name=Nederland.net
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.165.94.1,194.165.94.5
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Planet%032Internet%032Premium b/kppp/DB/Provider/Netherlands/Planet%032Internet%032Premium
new file mode 100644
index 00000000..9ebefa3b
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Planet%032Internet%032Premium
@@ -0,0 +1,28 @@
+# KDE Config File
+# http://web.planet.nl/klantenservice/helpdesk/serverinstellingen.html
+# http://web.planet.nl/klantenservice/helpdesk/inbelpunten.html
+#
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676003505
+IPAddr=0.0.0.0
+Domain=
+Name=Planet Internet Premium
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.121.1.34,195.121.1.66
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/Planet_Internet.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Planet%032Internet%032Standaard b/kppp/DB/Provider/Netherlands/Planet%032Internet%032Standaard
new file mode 100644
index 00000000..0bf82dc2
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Planet%032Internet%032Standaard
@@ -0,0 +1,28 @@
+# KDE Config File
+# http://web.planet.nl/klantenservice/helpdesk/serverinstellingen.html
+# http://web.planet.nl/klantenservice/helpdesk/inbelpunten.html
+#
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676002505
+IPAddr=0.0.0.0
+Domain=
+Name=Planet Internet Standaard
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.121.1.34,195.121.1.66
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/Planet_Internet.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Plant%032Acces b/kppp/DB/Provider/Netherlands/Plant%032Acces
new file mode 100644
index 00000000..add39379
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Plant%032Acces
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.plant.nl/instellingen/inbellen/win95/win95.asp
+#
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676044244
+IPAddr=0.0.0.0
+Domain=
+Name=Plant Acces
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1
diff --git a/kppp/DB/Provider/Netherlands/Popin b/kppp/DB/Provider/Netherlands/Popin
new file mode 100644
index 00000000..23c8f341
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Popin
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.popin.nl/helpdeskinbel.htm
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0748507010
+IPAddr=0.0.0.0
+Domain=
+Name=Popin
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.229.143.15,194.229.143.5
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=0
diff --git a/kppp/DB/Provider/Netherlands/PublishNet b/kppp/DB/Provider/Netherlands/PublishNet
new file mode 100644
index 00000000..897f2abc
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/PublishNet
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.publishnet.nl/index.asp?button=access&keuze=inbelnummers
+# http://www.publishnet.nl/asp/access.asp?keuze=instellingen
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676000999
+IPAddr=0.0.0.0
+Domain=
+Name=PublishNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Raketnet b/kppp/DB/Provider/Netherlands/Raketnet
new file mode 100644
index 00000000..e04216cf
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Raketnet
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.raketnet.nl/
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676050300
+IPAddr=0.0.0.0
+Domain=
+Name=Raketnet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Solcon b/kppp/DB/Provider/Netherlands/Solcon
new file mode 100644
index 00000000..bc7ac61a
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Solcon
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://helpdesk.solcon.net/faqonderdeel.php?Categorie=5
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676034583
+IPAddr=0.0.0.0
+Domain=
+Name=Solcon
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=212.45.33.3,212.45.32.3.
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1
diff --git a/kppp/DB/Provider/Netherlands/Support%032Net b/kppp/DB/Provider/Netherlands/Support%032Net
new file mode 100644
index 00000000..63d4e831
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Support%032Net
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.support.nl/pagina/helpdesk/ipaddr.html
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Support Net
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.114.231.200,195.114.230.254
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=0 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Telebyte b/kppp/DB/Provider/Netherlands/Telebyte
new file mode 100644
index 00000000..0984022e
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Telebyte
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://home.telebyte.nl/?op=help&sec_id=1
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Telebyte Internet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=213.211.129.21,213.211.129.22
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=1 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Tiscali%032Compleet b/kppp/DB/Provider/Netherlands/Tiscali%032Compleet
new file mode 100644
index 00000000..85fecc88
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Tiscali%032Compleet
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://web.tiscali.nl/servicecentre/content/article.asp?id=571000
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676060001
+IPAddr=0.0.0.0
+Domain=
+Name=Tiscali Compleet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.241.48.33,195.241.49.33
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio_Nummervoordeel.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=0
diff --git a/kppp/DB/Provider/Netherlands/Tiscali%032Gratis b/kppp/DB/Provider/Netherlands/Tiscali%032Gratis
new file mode 100644
index 00000000..eef668e2
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Tiscali%032Gratis
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://web.tiscali.nl/servicecentre/content/article.asp?id=571000
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676060000
+IPAddr=0.0.0.0
+Domain=
+Name=Tiscali Compleet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=195.241.162.35,195.241.49.33
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=0
diff --git a/kppp/DB/Provider/Netherlands/UwNet b/kppp/DB/Provider/Netherlands/UwNet
new file mode 100644
index 00000000..5d2286f6
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/UwNet
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676088053
+IPAddr=0.0.0.0
+Domain=
+Name=UwNet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=213.227.141.10,213.227.130.5
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Via%032Networks b/kppp/DB/Provider/Netherlands/Via%032Networks
new file mode 100644
index 00000000..98454fc4
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Via%032Networks
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.vianetworks.nl/helpdesk/eindhoven/info_inbelnummers_text.shtml?regio=0517&inbelnummer=06760+77111
+# http://www.vianetworks.nl/helpdesk/eindhoven/info_ip_text.shtml
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=Via Networks
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=212.61.15.8,212.61.25.226
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AutoDNS=0 \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Wannadoo%032budget b/kppp/DB/Provider/Netherlands/Wannadoo%032budget
new file mode 100644
index 00000000..c9a217c4
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Wannadoo%032budget
@@ -0,0 +1,26 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676022207
+IPAddr=0.0.0.0
+Domain=
+Name=Wannadoo budget
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.134.5.55,194.134.5.5
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AccountingFile=/Netherlands/BelBasis_Regio_Nummervoordeel.rst
diff --git a/kppp/DB/Provider/Netherlands/Wannadoo%032budget%320plus b/kppp/DB/Provider/Netherlands/Wannadoo%032budget%320plus
new file mode 100644
index 00000000..12cfd6ed
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Wannadoo%032budget%320plus
@@ -0,0 +1,26 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676022208
+IPAddr=0.0.0.0
+Domain=
+Name=Wannadoo budget plus
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.134.5.55,194.134.5.5
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+AccountingFile=/Netherlands/Wannadoo_Budget_Plus.rst
diff --git a/kppp/DB/Provider/Netherlands/Wannadoo%032smartpack b/kppp/DB/Provider/Netherlands/Wannadoo%032smartpack
new file mode 100644
index 00000000..af94310e
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Wannadoo%032smartpack
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=1
+Phonenumber=0676022201
+IPAddr=0.0.0.0
+Domain=
+Name=Wannadoo smartpack
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+DNS=194.134.5.55,194.134.5.5
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Wirehub b/kppp/DB/Provider/Netherlands/Wirehub
new file mode 100644
index 00000000..dadb6dbb
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Wirehub
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://support.wirehub.net/pub/win98/setuptxt
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+AutoDNS=0
+Phonenumber=0676090909
+IPAddr=0.0.0.0
+Domain=
+Name=WireHub
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=194.165.94.1,194.165.94.5
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/XS4All b/kppp/DB/Provider/Netherlands/XS4All
new file mode 100644
index 00000000..8781a54c
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/XS4All
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.xs4all.nl/helpdesk/besturingssysteem/linux/connectie/kde.html
+ExDNSDisabled=0
+AutoName=0
+AccountingEnabled=1
+AutoDNS=0
+Phonenumber=
+IPAddr=0.0.0.0
+Domain=
+Name=XS4All
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+Authentication=1
+DNS=194.109.6.66,194.109.9.99
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%
+ScriptCommands=Expect,Send,Expect,Send
diff --git a/kppp/DB/Provider/Netherlands/Zeelandnet b/kppp/DB/Provider/Netherlands/Zeelandnet
new file mode 100644
index 00000000..e452dcdd
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Zeelandnet
@@ -0,0 +1,27 @@
+# KDE Config File
+# http://www.zeelandnet.nl/helpdesk/index.php?page=4&grp=1&cat=7&faq_id=68
+# http://www.zeelandnet.nl/helpdesk/index.php?page=4&grp=1&cat=16&faq_id=56
+ExDNSDisabled=0
+AutoName=0
+AccountingEnabled=1
+AutoDNS=0
+Phonenumber=0676088954
+IPAddr=0.0.0.0
+Domain=
+Name=Zeelandnet
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+Authentication=4
+DNS=212.115.192.193,212.115.192.195
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
+ScriptArguments=
+ScriptCommands= \ No newline at end of file
diff --git a/kppp/DB/Provider/Netherlands/Zon%0322 b/kppp/DB/Provider/Netherlands/Zon%0322
new file mode 100644
index 00000000..81e7add5
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Zon%0322
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.zonnet.nl/helpdesk/0,1862,1523,00.html
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676000035
+IPAddr=0.0.0.0
+Domain=zonnet.nl
+Name=Zon2
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=62.58.50.5,62.58.50.6
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Zon%032Gratis b/kppp/DB/Provider/Netherlands/Zon%032Gratis
new file mode 100644
index 00000000..a35beac2
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Zon%032Gratis
@@ -0,0 +1,26 @@
+# KDE Config File
+# http://www.zonnet.nl/helpdesk/topic/0,1890,l43,00.html#43
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676075030
+IPAddr=0.0.0.0
+Domain=zonnet.nl
+Name=Zon Gratis
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=62.58.50.5,62.58.50.6
+SubnetMask=0.0.0.0
+AccountingFile=/Netherlands/BelBasis_Regio.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Netherlands/Zon%032Inclusief b/kppp/DB/Provider/Netherlands/Zon%032Inclusief
new file mode 100644
index 00000000..a7e163bd
--- /dev/null
+++ b/kppp/DB/Provider/Netherlands/Zon%032Inclusief
@@ -0,0 +1,25 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoDNS=1
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=0676001010
+IPAddr=0.0.0.0
+Domain=zonnet.nl
+Name=Zon Inclusief
+VolumeAccountingEnabled=1
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=62.58.50.5,62.58.50.6
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/New_Zealand/.directory b/kppp/DB/Provider/New_Zealand/.directory
new file mode 100644
index 00000000..bac00b6e
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/.directory
@@ -0,0 +1,50 @@
+[Desktop Entry]
+Name=New Zealand
+Name[af]=Nuwe Seeland
+Name[ar]=نيوزيلاندا
+Name[az]=Yeni Zellandiya
+Name[bg]=Ðова ЗеландиÑ
+Name[br]=Zeland nevez
+Name[ca]=Nova Zelanda
+Name[cs]=Nový Zéland
+Name[cy]=Seland Newydd
+Name[de]=Neuseeland
+Name[el]=Îέα Ζηλανδία
+Name[eo]=Novzelando
+Name[es]=Nueva Zelanda
+Name[et]=Uus-Meremaa
+Name[fr]=Nouvelle-Zélande
+Name[ga]=An Nua-Shéalainn
+Name[hi]=नà¥à¤¯à¥‚जीलैंड
+Name[hr]=Novi Zeland
+Name[hu]=Új-Zéland
+Name[id]=Selandia Baru
+Name[it]=Nuova Zelanda
+Name[ja]=ニュージーランド
+Name[km]=នូវែលហ៊្សáŸáž¡áž„់
+Name[ko]=뉴질란드
+Name[lt]=Naujoji Zelandija
+Name[lv]=JaunZÄ“lande
+Name[mk]=Ðов Зеланд
+Name[mn]=Ð¨Ð¸Ð½Ñ Ð—ÐµÐ»Ð°Ð½Ð´
+Name[nds]=Niegseeland
+Name[nl]=Nieuw Zeeland
+Name[pl]=Nowa Zelandia
+Name[pt]=Nova Zelândia
+Name[pt_BR]=Nova Zelândia
+Name[ru]=ÐÐ¾Ð²Ð°Ñ Ð—ÐµÐ»Ð°Ð½Ð´Ð¸Ñ
+Name[se]=Ođđa Zealánda
+Name[sk]=Nový Zéland
+Name[sl]=Nova Zelandija
+Name[sr]=Ðови Зеланд
+Name[sr@Latn]=Novi Zeland
+Name[sv]=Nya Zeeland
+Name[ta]=நியூசிலாநà¯à®¤à¯
+Name[tg]=ЗеландиÑи Ðав
+Name[tr]=Yeni Zellanda
+Name[uk]=Ðова ЗеландіÑ
+Name[wa]=Nouve Zelande
+Name[zh_CN]=新西兰
+Name[zh_HK]=ç´è¥¿è˜­
+Name[zh_TW]=ç´è¥¿è˜­
+Name[zu]=I-New Zealand
diff --git a/kppp/DB/Provider/New_Zealand/Makefile.am b/kppp/DB/Provider/New_Zealand/Makefile.am
new file mode 100644
index 00000000..c800b379
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Paradise Voyager XTRA OrconInternet ihug
+
+pkgdir = $(kde_datadir)/kppp/Provider/NewZealand
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/New_Zealand/OrconInternet b/kppp/DB/Provider/New_Zealand/OrconInternet
new file mode 100644
index 00000000..c5def85a
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/OrconInternet
@@ -0,0 +1,25 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=
+AccountingEnabled=0
+Phonenumber=086755666
+IPAddr=0.0.0.0
+Name=Orcon Internet
+Domain=orcon.net.nz
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+SubnetMask=0.0.0.0
+DNS=210.55.12.1,210.55.12.2,
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
+
diff --git a/kppp/DB/Provider/New_Zealand/Paradise b/kppp/DB/Provider/New_Zealand/Paradise
new file mode 100644
index 00000000..7aebe0c9
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/Paradise
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=086727235
+IPAddr=0.0.0.0
+Name=paradise
+Domain=paradise.net.nz
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=203.96.152.4,203.96.152.12,
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/New_Zealand/Voyager b/kppp/DB/Provider/New_Zealand/Voyager
new file mode 100644
index 00000000..95f92284
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/Voyager
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=087300015
+IPAddr=0.0.0.0
+Name=voyager
+Domain=voyager.co.nz
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=203.21.30.124,203.21.30.125,
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/New_Zealand/XTRA b/kppp/DB/Provider/New_Zealand/XTRA
new file mode 100644
index 00000000..6192a1c7
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/XTRA
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=087303030
+IPAddr=0.0.0.0
+Name=xtra
+Domain=xtra.co.nz
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=202.27.184.3,202.27.184.5,
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/New_Zealand/ihug b/kppp/DB/Provider/New_Zealand/ihug
new file mode 100644
index 00000000..666cf01d
--- /dev/null
+++ b/kppp/DB/Provider/New_Zealand/ihug
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=087300777
+IPAddr=0.0.0.0
+Name=ihug
+Domain=ihug.co.nz
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=203.109.252.42,203.109.252.43
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Norway/.directory b/kppp/DB/Provider/Norway/.directory
new file mode 100644
index 00000000..48b0c178
--- /dev/null
+++ b/kppp/DB/Provider/Norway/.directory
@@ -0,0 +1,53 @@
+[Desktop Entry]
+Name=Norway
+Name[af]=Noorweë
+Name[ar]=النرويج
+Name[az]=Norveç
+Name[bg]=ÐорвегиÑ
+Name[br]=Norvegia
+Name[ca]=Noruega
+Name[cs]=Norsko
+Name[cy]=Norwy
+Name[da]=Norge
+Name[de]=Norwegen
+Name[el]=ÎοÏβηγία
+Name[eo]=Norvegio
+Name[es]=Noruega
+Name[et]=Norra
+Name[fo]=Norra
+Name[fr]=Norvège
+Name[ga]=An Iorua
+Name[hi]=नारà¥à¤µà¥‡
+Name[hr]=Norveška
+Name[hu]=Norvégia
+Name[id]=Norwegia
+Name[it]=Norvegia
+Name[ja]=ノルウェー
+Name[km]=áž“áŸážšážœáŸ‚ស
+Name[ko]=노르웨ì´
+Name[lt]=Norvegija
+Name[lv]=Norvēģija
+Name[mk]=Ðорвешка
+Name[mn]=Ðорвеги
+Name[mt]=Norveġja
+Name[nds]=Norwegen
+Name[nl]=Noorwegen
+Name[pl]=Norwegia
+Name[pt]=Noruega
+Name[pt_BR]=Noruega
+Name[ru]=ÐорвегиÑ
+Name[se]=Norga
+Name[sk]=Nórsko
+Name[sl]=Norveška
+Name[sr]=Ðорвешка
+Name[sr@Latn]=Norveška
+Name[sv]=Norge
+Name[ta]=நாரà¯à®µà¯‡
+Name[tg]=Ðорвегӣ
+Name[tr]=Norveç
+Name[uk]=ÐорвегіÑ
+Name[wa]=Norvedje
+Name[zh_CN]=挪å¨
+Name[zh_HK]=挪å¨
+Name[zh_TW]=挪å¨
+Name[zu]=I-Noweyi
diff --git a/kppp/DB/Provider/Norway/BGNett b/kppp/DB/Provider/Norway/BGNett
new file mode 100644
index 00000000..ac577ad5
--- /dev/null
+++ b/kppp/DB/Provider/Norway/BGNett
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=1
+ScriptArguments=ogin:,%USERNAME% ppp,assword:,%PASSWORD%,
+AccountingEnabled=0
+Phonenumber=55104290
+IPAddr=0.0.0.0
+Domain=bgnett.no
+Name=bgnett
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,Send,Expect,Send,
+Authentication=0
+DNS=194.19.41.3,
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=194.19.41.1
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Norway/Institutt%032for%032informatikk b/kppp/DB/Provider/Norway/Institutt%032for%032informatikk
new file mode 100644
index 00000000..a8f022f6
--- /dev/null
+++ b/kppp/DB/Provider/Norway/Institutt%032for%032informatikk
@@ -0,0 +1,22 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=,username>,%USERNAME%,password>,%PASSWORD%,arrow keys:,7,
+AccountingEnabled=0
+Phonenumber=55584750
+IPAddr=0.0.0.0
+Domain=ii.uib.no
+Name=Institutt for informatikk
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Send,Expect,Send,Expect,Send,Expect,Send,
+Authentication=0
+DNS=129.177.16.3,129.177.19.6,
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Norway/Makefile.am b/kppp/DB/Provider/Norway/Makefile.am
new file mode 100644
index 00000000..1435d795
--- /dev/null
+++ b/kppp/DB/Provider/Norway/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Institutt%032for%032informatikk BGNett
+
+pkgdir = $(kde_datadir)/kppp/Provider/Norway
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Portugal/.directory b/kppp/DB/Provider/Portugal/.directory
new file mode 100644
index 00000000..c0c00d27
--- /dev/null
+++ b/kppp/DB/Provider/Portugal/.directory
@@ -0,0 +1,34 @@
+[Desktop Entry]
+Name=Portugal
+Name[ar]=البرتغال
+Name[az]=Portuqaliya
+Name[bg]=ПортугалиÑ
+Name[cs]=Portugalsko
+Name[cy]=Portiwgal
+Name[el]=ΠοÏτογαλία
+Name[eo]=Portugalo
+Name[ga]=An Phortaingéil
+Name[hi]=पà¥à¤°à¥à¤¤à¤—ाल
+Name[hu]=Portugália
+Name[it]=Portogallo
+Name[ja]=ãƒãƒ«ãƒˆã‚¬ãƒ«
+Name[km]=áž–áŸážšáž‘ុយហ្គាល់
+Name[ko]=í¬ë¥´íˆ¬ê°ˆ
+Name[lt]=Portugalija
+Name[lv]=PortugÄle
+Name[mk]=Португалија
+Name[mn]=Португали
+Name[mt]=Portugall
+Name[pl]=Portugalia
+Name[ru]=ПортугалиÑ
+Name[sk]=Portugalsko
+Name[sl]=Portugalska
+Name[sr]=Португал
+Name[ta]=போரà¯à®šà¯à®šà¯à®•à®²à¯
+Name[tg]=ПортугалиÑ
+Name[tr]=Portekiz
+Name[uk]=ПортугаліÑ
+Name[zh_CN]=è‘¡è„牙
+Name[zh_HK]=è‘¡è„牙
+Name[zh_TW]=è‘¡è„牙
+Name[zu]=I-Phothugali
diff --git a/kppp/DB/Provider/Portugal/Clix b/kppp/DB/Provider/Portugal/Clix
new file mode 100644
index 00000000..44e437c2
--- /dev/null
+++ b/kppp/DB/Provider/Portugal/Clix
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=679300000
+IPAddr=0.0.0.0
+Name=Clix
+Domain=clix.pt
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=194.79.69.222,194.79.69.129,
+AccountingFile=Portugal/PT_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Portugal/Makefile.am b/kppp/DB/Provider/Portugal/Makefile.am
new file mode 100644
index 00000000..57f3be9d
--- /dev/null
+++ b/kppp/DB/Provider/Portugal/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory OniNet Clix Netc
+
+pkgdir = $(kde_datadir)/kppp/Provider/Portugal
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Portugal/Netc b/kppp/DB/Provider/Portugal/Netc
new file mode 100644
index 00000000..69b16791
--- /dev/null
+++ b/kppp/DB/Provider/Portugal/Netc
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=679190000
+IPAddr=0.0.0.0
+Name=Netc
+Domain=netc.pt
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=212.18.160.133,212.18.160.134,
+AccountingFile=Portugal/PT_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Portugal/OniNet b/kppp/DB/Provider/Portugal/OniNet
new file mode 100644
index 00000000..d8d498f3
--- /dev/null
+++ b/kppp/DB/Provider/Portugal/OniNet
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,word:,%PASSWORD%,ppp,
+AccountingEnabled=1
+Phonenumber=679595000
+IPAddr=0.0.0.0
+Name=OniNet
+Domain=oninet.pt
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=,
+AccountingFile=Portugal/PT_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Slovenia/.directory b/kppp/DB/Provider/Slovenia/.directory
new file mode 100644
index 00000000..01dc5247
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/.directory
@@ -0,0 +1,45 @@
+[Desktop Entry]
+Name=Slovenia
+Name[ar]=سلوÙينيا
+Name[az]=Sloveniya
+Name[bg]=СловениÑ
+Name[br]=Sloveni
+Name[ca]=Eslovènia
+Name[cs]=Slovinsko
+Name[da]=Slovenien
+Name[de]=Slowenien
+Name[el]=Σλοβενία
+Name[eo]=Slovenio
+Name[es]=Eslovenia
+Name[et]=Sloveenia
+Name[fr]=Slovénie
+Name[ga]=An tSlóivéin
+Name[hi]=सà¥à¤²à¥‹à¤µà¥‡à¤¨à¤¿à¤¯à¤¾
+Name[hr]=Slovenija
+Name[hu]=Szlovénia
+Name[ja]=スロベニア
+Name[km]=ស្លូវ៉ានី
+Name[lt]=SlovÄ—nija
+Name[lv]=Slovēnija
+Name[mk]=Словенија
+Name[mt]=Slovenja
+Name[nds]=Slowenien
+Name[nl]=Slovenië
+Name[pl]=SÅ‚owenia
+Name[pt]=Eslovénia
+Name[pt_BR]=Eslovênia
+Name[ru]=СловениÑ
+Name[sk]=Slovinsko
+Name[sl]=Slovenija
+Name[sr]=Словенија
+Name[sr@Latn]=Slovenija
+Name[sv]=Slovenien
+Name[ta]=ஸà¯à®²à¯‹à®µà¯†à®©à®¿à®¯à®¾
+Name[tg]=СловениÑ
+Name[tr]=Slovenya
+Name[uk]=СловеніÑ
+Name[wa]=Esloveneye
+Name[zh_CN]=斯洛文尼亚
+Name[zh_TW]=斯洛ä¼å°¼äºž
+Name[zu]=Isi-Sloveniya
+
diff --git a/kppp/DB/Provider/Slovenia/AmisNet b/kppp/DB/Provider/Slovenia/AmisNet
new file mode 100644
index 00000000..08e3a272
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/AmisNet
@@ -0,0 +1,27 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=AmisNet
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0889.rst
+Authentication=1
+AutoName=1
+AutoDNS=1
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=amis.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088932410
diff --git a/kppp/DB/Provider/Slovenia/Arnes b/kppp/DB/Provider/Slovenia/Arnes
new file mode 100644
index 00000000..96f18dc6
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/Arnes
@@ -0,0 +1,26 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=Arnes
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0889.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=193.2.1.66
+DefaultRoute=1
+DisconnectCommand=
+Domain=arnes.si
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088932330
diff --git a/kppp/DB/Provider/Slovenia/Kiss b/kppp/DB/Provider/Slovenia/Kiss
new file mode 100644
index 00000000..a4bbbfae
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/Kiss
@@ -0,0 +1,26 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=Kiss
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0889.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=193.2.98.10,193.2.98.11
+DefaultRoute=1
+DisconnectCommand=
+Domain=kiss.uni-lj.si
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088932520
diff --git a/kppp/DB/Provider/Slovenia/Makefile.am b/kppp/DB/Provider/Slovenia/Makefile.am
new file mode 100644
index 00000000..e4456458
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = AmisNet Arnes Kiss MojNet SiOL Volja
+
+pkgdir = $(kde_datadir)/kppp/Provider/Slovenia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Slovenia/MojNet b/kppp/DB/Provider/Slovenia/MojNet
new file mode 100644
index 00000000..a2890f98
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/MojNet
@@ -0,0 +1,27 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=MojNet
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0889.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=
+AutoDNS=1
+DefaultRoute=1
+DisconnectCommand=
+Domain=moj.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088932440
diff --git a/kppp/DB/Provider/Slovenia/SiOL b/kppp/DB/Provider/Slovenia/SiOL
new file mode 100644
index 00000000..8f2eb3ea
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/SiOL
@@ -0,0 +1,26 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=SiOL
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0880.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=193.189.160.11
+DefaultRoute=1
+DisconnectCommand=
+Domain=siol.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088032320
diff --git a/kppp/DB/Provider/Slovenia/Volja b/kppp/DB/Provider/Slovenia/Volja
new file mode 100644
index 00000000..e8926cf4
--- /dev/null
+++ b/kppp/DB/Provider/Slovenia/Volja
@@ -0,0 +1,27 @@
+# KDE Config File
+# $Id$
+# $Source$
+#
+Name=Volja
+AccountingEnabled=1
+AccountingFile=/Slovenia/omrezje_0889.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=
+AutoDNS=1
+DefaultRoute=1
+DisconnectCommand=
+Domain=volja.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=088932480
diff --git a/kppp/DB/Provider/Sweden/.directory b/kppp/DB/Provider/Sweden/.directory
new file mode 100644
index 00000000..9207acd5
--- /dev/null
+++ b/kppp/DB/Provider/Sweden/.directory
@@ -0,0 +1,51 @@
+[Desktop Entry]
+Name=Sweden
+Name[af]=Swede
+Name[ar]=السويد
+Name[az]=İsveç
+Name[bg]=ШвециÑ
+Name[ca]=Suècia
+Name[cs]=Švédsko
+Name[da]=Sverrige
+Name[de]=Schweden
+Name[el]=Σουηδία
+Name[eo]=Svedio
+Name[es]=Suecia
+Name[et]=Rootsi
+Name[fo]=Svøriki
+Name[fr]=Suède
+Name[ga]=An tSualainn
+Name[hi]=सà¥à¤µà¥€à¤¡à¤¨
+Name[hr]=Å vedska
+Name[hu]=Svédország
+Name[id]=Swedia
+Name[it]=Svezia
+Name[ja]=スウェーデン
+Name[km]=ស៊ុយអែáž
+Name[ko]=스웨ë´
+Name[lt]=Å vedija
+Name[lv]=Zviedrija
+Name[mk]=ШведÑка
+Name[mn]=Швед
+Name[mt]=Svezja
+Name[nl]=Zweden
+Name[pl]=Szwecja
+Name[pt]=Suécia
+Name[pt_BR]=Suécia
+Name[ru]=ШвециÑ
+Name[se]=Ruoŧŧa
+Name[sk]=Švédsko
+Name[sl]=Å vedska
+Name[sr]=ШведÑка
+Name[sr@Latn]=Å vedska
+Name[sv]=Sverige
+Name[ta]=சà¯à®µà¯€à®Ÿà®©à¯
+Name[tg]=ШветÑиÑ
+Name[tr]=İsveç
+Name[uk]=ШвеціÑ
+Name[ven]=Swidene
+Name[wa]=Suwede
+Name[zh_CN]=ç‘žå…¸
+Name[zh_HK]=ç‘žå…¸
+Name[zh_TW]=ç‘žå…¸
+Name[zu]=Isi-Swideni
diff --git a/kppp/DB/Provider/Sweden/Makefile.am b/kppp/DB/Provider/Sweden/Makefile.am
new file mode 100644
index 00000000..0551cacd
--- /dev/null
+++ b/kppp/DB/Provider/Sweden/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Utfors Tiscali
+
+pkgdir = $(kde_datadir)/kppp/Provider/Sweden
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Sweden/Tiscali b/kppp/DB/Provider/Sweden/Tiscali
new file mode 100644
index 00000000..1be1baa1
--- /dev/null
+++ b/kppp/DB/Provider/Sweden/Tiscali
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Sweden/Tiscali.rst
+Authentication=1
+AutoDNS=0
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=213.204.128.170,213.204.128.171
+DefaultRoute=1
+DisconnectCommand=
+Domain=tiscali.se
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Tiscali
+Password=%PASSWORD%
+Phonenumber=0200785070
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Sweden/Utfors b/kppp/DB/Provider/Sweden/Utfors
new file mode 100644
index 00000000..47cbd203
--- /dev/null
+++ b/kppp/DB/Provider/Sweden/Utfors
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Sweden/Utfors.rst
+Authentication=3
+AutoDNS=0
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=195.58.103.124,195.58.103.18
+DefaultRoute=1
+DisconnectCommand=
+Domain=utfors.se
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=Utfors
+Password=%PASSWORD%
+Phonenumber=0200787878
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Switzerland/.directory b/kppp/DB/Provider/Switzerland/.directory
new file mode 100644
index 00000000..ebbbd633
--- /dev/null
+++ b/kppp/DB/Provider/Switzerland/.directory
@@ -0,0 +1,48 @@
+[Desktop Entry]
+Name=Switzerland
+Name[ar]=سويسرا
+Name[az]=İsveçrə
+Name[bg]=ШвейцариÑ
+Name[br]=Suis
+Name[ca]=Suïssa
+Name[cs]=Švýcarsko
+Name[cy]=Y Swistir
+Name[da]=Svejts
+Name[de]=Schweitz
+Name[el]=Ελβετία
+Name[eo]=Svislando
+Name[es]=Suiza
+Name[et]=Å veits
+Name[fr]=Suisse
+Name[ga]=An Eilvéis
+Name[hi]=सà¥à¤µà¤¿à¤Ÿà¥à¤œà¤°à¤²à¥ˆà¤‚ड
+Name[hr]=Å vicarska
+Name[hu]=Svájc
+Name[id]=Swiss
+Name[it]=Svizzera
+Name[ja]=スイス
+Name[km]=ស្វ៊ីស
+Name[lt]=Å veicarija
+Name[lv]=Å veice
+Name[mk]=Швајцарија
+Name[mt]=Svizzera
+Name[nds]=Swiez
+Name[nl]=Zwitserland
+Name[pl]=Szwajcaria
+Name[pt]=Suíça
+Name[pt_BR]=Suíça
+Name[ru]=ШвейцариÑ
+Name[se]=Å veica
+Name[sk]=Å vajÄiarsko
+Name[sl]=Å vica
+Name[sr]=ШвајцарÑка
+Name[sr@Latn]=Å vajcarska
+Name[sv]=Schweiz
+Name[ta]=சà¯à®µà®¿à®Ÿà¯à®šà®°à¯à®²à®¾à®¨à¯à®¤à¯
+Name[tg]=ШвейтÑариÑ
+Name[tr]=İsviçre
+Name[uk]=ШвейцаріÑ
+Name[wa]=Swisse
+Name[zh_CN]=瑞士
+Name[zh_TW]=瑞士
+Name[zu]=I-Switzerland
diff --git a/kppp/DB/Provider/Switzerland/Bluewin b/kppp/DB/Provider/Switzerland/Bluewin
new file mode 100644
index 00000000..60ae44cb
--- /dev/null
+++ b/kppp/DB/Provider/Switzerland/Bluewin
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=10741 0840 840 222
+IPAddr=0.0.0.0
+Name=Bluewin
+Domain=bluewin.ch
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=4
+SubnetMask=0.0.0.0
+DNS=
+AccountingFile=/Switzerland/Swisscom_Surf.rst
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Switzerland/Makefile.am b/kppp/DB/Provider/Switzerland/Makefile.am
new file mode 100644
index 00000000..9cd4f016
--- /dev/null
+++ b/kppp/DB/Provider/Switzerland/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Bluewin
+
+pkgdir = $(kde_datadir)/kppp/Provider/Switzerland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Taiwan/.directory b/kppp/DB/Provider/Taiwan/.directory
new file mode 100644
index 00000000..809fd51e
--- /dev/null
+++ b/kppp/DB/Provider/Taiwan/.directory
@@ -0,0 +1,36 @@
+[Desktop Entry]
+Name=Taiwan
+Name[ar]=تايوان
+Name[az]=Tayvan
+Name[bg]=Тайван
+Name[el]=Ταϊβάν
+Name[eo]=Tajvano
+Name[es]=Taiwán
+Name[fr]=Taïwan
+Name[ga]=An Téaváin
+Name[hi]=ताईवान
+Name[hr]=Tajvan
+Name[hu]=Tajvan
+Name[ja]=å°æ¹¾
+Name[km]=ážáŸƒážœáŸ‰áž¶áž“់
+Name[ko]=대만
+Name[lt]=Taivanis
+Name[lv]=TaivÄna
+Name[mk]=Тајван
+Name[mn]=Тайвань
+Name[mt]=Tajwan
+Name[pl]=Tajwan
+Name[pt]=Formosa
+Name[ru]=Тайвань
+Name[sk]=Tajvan
+Name[sl]=Tajvan
+Name[sr]=Тајван
+Name[sr@Latn]=Tajvan
+Name[ta]=தாயà¯à®µà®¾à®©à¯
+Name[tg]=Тайван
+Name[tr]=Tayvan
+Name[uk]=Тайвань
+Name[zh_CN]=中国å°æ¹¾
+Name[zh_HK]=å°ç£
+Name[zh_TW]=å°ç£
+Name[zu]=I-Tayiwani
diff --git a/kppp/DB/Provider/Taiwan/EraNet b/kppp/DB/Provider/Taiwan/EraNet
new file mode 100644
index 00000000..2a8cb406
--- /dev/null
+++ b/kppp/DB/Provider/Taiwan/EraNet
@@ -0,0 +1,23 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=4126888
+IPAddr=0.0.0.0
+Domain=
+Name=EraNet
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=168.95.1.1
+SubnetMask=0.0.0.0
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Taiwan/HiNet b/kppp/DB/Provider/Taiwan/HiNet
new file mode 100644
index 00000000..23c32790
--- /dev/null
+++ b/kppp/DB/Provider/Taiwan/HiNet
@@ -0,0 +1,23 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=4125678
+IPAddr=0.0.0.0
+Domain=
+Name=HiNet
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=168.95.1.1
+SubnetMask=0.0.0.0
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Taiwan/Makefile.am b/kppp/DB/Provider/Taiwan/Makefile.am
new file mode 100644
index 00000000..9141ab8c
--- /dev/null
+++ b/kppp/DB/Provider/Taiwan/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory HiNet SeedNet EraNet
+
+pkgdir = $(kde_datadir)/kppp/Provider/Taiwan
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Taiwan/SeedNet b/kppp/DB/Provider/Taiwan/SeedNet
new file mode 100644
index 00000000..2a71f25b
--- /dev/null
+++ b/kppp/DB/Provider/Taiwan/SeedNet
@@ -0,0 +1,23 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=4123000
+IPAddr=0.0.0.0
+Domain=
+Name=SeedNet
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=168.95.1.1
+SubnetMask=0.0.0.0
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Ukraine/.directory b/kppp/DB/Provider/Ukraine/.directory
new file mode 100644
index 00000000..f9f7220a
--- /dev/null
+++ b/kppp/DB/Provider/Ukraine/.directory
@@ -0,0 +1,67 @@
+[Desktop Entry]
+Name=Ukraine
+Name[af]=Ukraïne
+Name[ar]=أوكرانيا
+Name[az]=Ukrayna
+Name[bg]=Украйна
+Name[br]=Ukraina
+Name[ca]=Ucraïna
+Name[cs]=Ukrajina
+Name[cy]=Wcrain
+Name[el]=ΟυκÏανία
+Name[eo]=Ukrainio
+Name[es]=Ucrania
+Name[et]=Ukraina
+Name[ga]=An Úcráin
+Name[hi]=उकà¥à¤°à¥‡à¤¨
+Name[hr]=Ukrajina
+Name[hu]=Ukrajna
+Name[id]=Ukraina
+Name[it]=Ucraina
+Name[ja]=ウクライナ
+Name[km]=អ៊ុយក្រែន
+Name[ko]=ìš°í¬ë¼ì´ë‚˜
+Name[lt]=Ukraina
+Name[lv]=Ukraina
+Name[mk]=Украина
+Name[mn]=Украин
+Name[mt]=Ukranja
+Name[nl]=Oekraïne
+Name[pl]=Ukraina
+Name[pt]=Ucrânia
+Name[pt_BR]=Ucrânia
+Name[ru]=Украина
+Name[se]=Ukraina
+Name[sk]=Ukrajina
+Name[sl]=Ukrajina
+Name[sr]=Украјина
+Name[sr@Latn]=Ukrajina
+Name[sv]=Ukraina
+Name[ta]=உகà¯à®°à¯†à®¯à¯à®©à¯
+Name[tg]=Украина
+Name[tr]=Ukrayna
+Name[uk]=Україна
+Name[wa]=Ucrinne
+Name[zh_CN]=乌克兰
+Name[zh_HK]=çƒå…‹è˜­
+Name[zh_TW]=çƒå…‹è˜­
+Name[zu]=I-Ukraine
+Region=easteurope
+Languages=uk
+DecimalSymbol=,
+ThousandsSeparator=.
+CurrencySymbol=гр
+MonetaryDecimalSymbol=.
+MonetaryThousandsSeparator=$0 $0
+PositiveSign=
+NegativeSign=-
+FracDigits=2
+PositivePrefixCurrencySymbol=false
+NegativePrefixCurrencySymbol=false
+PositiveMonetarySignPosition=1
+NegativeMonetarySignPosition=1
+DateFormat[uk]=%a %d %b %Y
+DateFormat[ru]=%a %d %b %Y
+DateFormat[eo]=%A, la %ea de %B %Y
+DateFormatShort=%d.%m.%Y
+TimeFormat=%H:%M:%S
diff --git a/kppp/DB/Provider/Ukraine/Adamant b/kppp/DB/Provider/Ukraine/Adamant
new file mode 100644
index 00000000..3f1c4aee
--- /dev/null
+++ b/kppp/DB/Provider/Ukraine/Adamant
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=0
+Phonenumber=5667939,4595577
+IPAddr=0.0.0.0
+Domain=
+Name=Adamant
+VolumeAccountingEnabled=0
+pppdArguments=
+BeforeDisconnect=
+Password=%PASSWORD%
+Command=
+ScriptCommands=
+Authentication=1
+DNS=212.26.128.2,212.26.128.3,212.26.130.240
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/Ukraine/IPTelecom b/kppp/DB/Provider/Ukraine/IPTelecom
new file mode 100644
index 00000000..6376bba8
--- /dev/null
+++ b/kppp/DB/Provider/Ukraine/IPTelecom
@@ -0,0 +1,29 @@
+# KDE Config File
+[Account0]
+AccountingEnabled=1
+AccountingFile=/Ukraine/IPTelecom_hourly.rst
+Authentication=4
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=IPTelecom
+Password=%PASSWORD%
+Phonenumber=5333333:5722112:2388888:2382828:2449555
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+TotalBytes=0
+TotalCosts=0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Ukraine/Makefile.am b/kppp/DB/Provider/Ukraine/Makefile.am
new file mode 100644
index 00000000..c8d33aa5
--- /dev/null
+++ b/kppp/DB/Provider/Ukraine/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = .directory Adamant IPTelecom NuVse
+
+pkgdir = $(kde_datadir)/kppp/Provider/Ukraine
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Ukraine/NuVse b/kppp/DB/Provider/Ukraine/NuVse
new file mode 100644
index 00000000..916b9cc5
--- /dev/null
+++ b/kppp/DB/Provider/Ukraine/NuVse
@@ -0,0 +1,27 @@
+[Account2]
+AccountingEnabled=0
+AccountingFile=/Ukraine/NuVse_hourly.rst
+Authentication=4
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DialPrefix=
+DisconnectCommand=
+Domain=
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=NuVse
+Password=
+Phonenumber=4674444
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/United_Kingdom/.directory b/kppp/DB/Provider/United_Kingdom/.directory
new file mode 100644
index 00000000..9c89ef42
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/.directory
@@ -0,0 +1,54 @@
+[Desktop Entry]
+Name=United Kingdom
+Name[af]=Vereenigde Koninkryk
+Name[ar]=المملكة المتحدة
+Name[az]=Birləşmiş Krallıq
+Name[bg]=Обединено кралÑтво
+Name[br]=Rouantelezh Unanet
+Name[ca]=Regna Unit
+Name[cs]=Spojené království
+Name[cy]=Y Deyrnas Unedig
+Name[de]=Vereinigtes Königreich
+Name[el]=Ηνωμένο Βασίλειο
+Name[eo]=Britio
+Name[es]=Reino Unido
+Name[et]=Suurbritannia
+Name[fo]=Stórabretland
+Name[fr]=Royaume-Uni
+Name[ga]=An Ríocht Aontaithe
+Name[hi]=यूनाइटेड किंगडम
+Name[hr]=Ujedinjeno Kraljevstvo
+Name[hu]=Egyesült Királyság
+Name[id]=Inggris
+Name[it]=Regno Unito
+Name[ja]=英国
+Name[km]=ចក្រភព​អង់គ្លáŸážŸ
+Name[ko]=ì˜êµ­
+Name[lt]=JungtinÄ— KaralystÄ—
+Name[lv]=ApvienotÄ Karaliste
+Name[mk]=Велика Британија
+Name[mn]=Их британи
+Name[mt]=Renju Unit
+Name[nds]=Grootbritannien
+Name[nl]=Verenigd Koninkrijk
+Name[pl]=Wielka Brytania
+Name[pt]=Reino Unido
+Name[pt_BR]=Reino Unido
+Name[ru]=ВеликобританиÑ
+Name[se]=Stuorrabrittania
+Name[sk]=Spojené kráľovstvo
+Name[sl]=Združeno Kraljestvo
+Name[sr]=Уједињено КраљевÑтво
+Name[sr@Latn]=Ujedinjeno Kraljevstvo
+Name[sv]=Storbritannien
+Name[ta]=à®à®•à¯à®•à®¿à®¯ இராஜà¯à®œà®¿à®¯à®®à¯
+Name[tg]=ВеликобританиÑ
+Name[tr]=Ä°ngiltere
+Name[uk]=ВеликобританіÑ
+Name[ven]=Biritheini
+Name[wa]=Rweyôme Uni
+Name[xh]=United Kingdom
+Name[zh_CN]=英国
+Name[zh_HK]=è¯åˆçŽ‹åœ‹
+Name[zh_TW]=英國
+Name[zu]=United Kingdom (Ubukhosi obuhlangeneyo)
diff --git a/kppp/DB/Provider/United_Kingdom/Demon%032Green%0322120666 b/kppp/DB/Provider/United_Kingdom/Demon%032Green%0322120666
new file mode 100644
index 00000000..99fd8fca
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/Demon%032Green%0322120666
@@ -0,0 +1,23 @@
+# KDE Config File
+AutoName=0
+ExDNSDisabled=0
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,otocol:,ppp,
+AccountingEnabled=0
+Phonenumber=08452120666
+IPAddr=0.0.0.0
+Name=Demon Green 2120666
+Domain=demon.co.uk
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Expect,Send,
+Authentication=0
+SubnetMask=0.0.0.0
+DNS=158.152.1.43,158.152.1.58,
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+StorePassword=1
+Gateway=0.0.0.0
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/Demon%032Purple%0322121666 b/kppp/DB/Provider/United_Kingdom/Demon%032Purple%0322121666
new file mode 100644
index 00000000..859dd18c
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/Demon%032Purple%0322121666
@@ -0,0 +1,23 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,otocol:,ppp,
+AccountingEnabled=0
+Phonenumber=08452121666
+IPAddr=0.0.0.0
+Domain=demon.co.uk
+Name=Demon Purple 2121666
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Expect,Send,
+Authentication=0
+DNS=158.152.1.43,158.152.1.58,
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/Demon%032Red%0320798666 b/kppp/DB/Provider/United_Kingdom/Demon%032Red%0320798666
new file mode 100644
index 00000000..cd445230
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/Demon%032Red%0320798666
@@ -0,0 +1,23 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,otocol:,ppp,
+AccountingEnabled=0
+Phonenumber=08450798666
+IPAddr=0.0.0.0
+Domain=demon.co.uk
+Name=Demon Red 0798666
+VolumeAccountingEnabled=0
+pppdArguments=-detach,
+Password=%PASSWORD%
+Command=
+ScriptCommands=Expect,Send,Expect,Send,Expect,Send,
+Authentication=0
+DNS=158.152.1.43,158.152.1.58,
+SubnetMask=0.0.0.0
+AccountingFile=
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/FreeServe b/kppp/DB/Provider/United_Kingdom/FreeServe
new file mode 100644
index 00000000..594822c8
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/FreeServe
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=ogin:,,word:,,
+AccountingEnabled=1
+Phonenumber=08450796699
+IPAddr=0.0.0.0
+Domain=
+Name=FreeServe
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,ID,Expect,Password,
+Authentication=0
+DNS=195.92.177.3,
+SubnetMask=0.0.0.0
+AccountingFile=/England/BritishTelecom_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/Makefile.am b/kppp/DB/Provider/United_Kingdom/Makefile.am
new file mode 100644
index 00000000..e7abf781
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/Makefile.am
@@ -0,0 +1,9 @@
+pkg_DATA = .directory FreeServe Demon%032Green%0322120666 Demon%032Purple%0322121666 \
+ Demon%032Red%0320798666 \
+ UK%032Free%032Software%032Network%032ISDN UKPOST%032ISDN \
+ UK%032Free%032Software%032Network%032Modem UKPOST%032Modem \
+ UTV
+
+pkgdir = $(kde_datadir)/kppp/Provider/United_Kingdom
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032ISDN b/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032ISDN
new file mode 100644
index 00000000..054163bf
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032ISDN
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=08456651576
+IPAddr=0.0.0.0
+Domain=
+Name=UK Free Software Network ISDN
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,ID,Expect,Password,
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/England/BritishTelecom_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032Modem b/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032Modem
new file mode 100644
index 00000000..c5cebf23
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/UK%032Free%032Software%032Network%032Modem
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=08456651575
+IPAddr=0.0.0.0
+Domain=
+Name=UK Free Software Network Modem
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,ID,Expect,Password,
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/England/BritishTelecom_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/UKPOST%032ISDN b/kppp/DB/Provider/United_Kingdom/UKPOST%032ISDN
new file mode 100644
index 00000000..c9456a4c
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/UKPOST%032ISDN
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=08456609011
+IPAddr=0.0.0.0
+Domain=
+Name=UKPOST.COM ISDN
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,ID,Expect,Password,
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/England/BritishTelecom_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/UKPOST%032Modem b/kppp/DB/Provider/United_Kingdom/UKPOST%032Modem
new file mode 100644
index 00000000..3f07f0ed
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/UKPOST%032Modem
@@ -0,0 +1,24 @@
+# KDE Config File
+ExDNSDisabled=0
+AutoName=0
+ScriptArguments=
+AccountingEnabled=1
+Phonenumber=08456609010
+IPAddr=0.0.0.0
+Domain=
+Name=UKPOST.COM Modem
+VolumeAccountingEnabled=0
+pppdArguments=
+Password=%PASSWORD%
+BeforeDisconnect=
+Command=
+ScriptCommands=Expect,ID,Expect,Password,
+Authentication=1
+DNS=
+SubnetMask=0.0.0.0
+AccountingFile=/England/BritishTelecom_Local.rst
+DefaultRoute=1
+Username=%USERNAME%
+Gateway=0.0.0.0
+StorePassword=1
+DisconnectCommand=
diff --git a/kppp/DB/Provider/United_Kingdom/UTV b/kppp/DB/Provider/United_Kingdom/UTV
new file mode 100644
index 00000000..86e9752a
--- /dev/null
+++ b/kppp/DB/Provider/United_Kingdom/UTV
@@ -0,0 +1,26 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoDNS=1
+AutoName=0
+BeforeConnect=
+BeforeDisconnect=
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=UTVIP.com
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=UTVIP
+Password=%PASSWORD%
+Phonenumber=1893242242
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
diff --git a/kppp/DB/Provider/Yugoslavia/.directory b/kppp/DB/Provider/Yugoslavia/.directory
new file mode 100644
index 00000000..fe784846
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/.directory
@@ -0,0 +1,42 @@
+[Desktop Entry]
+Name=Yugoslavia
+Name[ar]=يوغوسلاÙيا
+Name[bg]=ЮгоÑлавиÑ
+Name[br]=Yougoslavi
+Name[ca]=Iugoslàvia
+Name[cs]=Jugoslávie
+Name[cy]=Iwgoslavia
+Name[da]=Jugoslavien
+Name[de]=Jugoslawien
+Name[el]=Γιουκοσλαβία
+Name[eo]=Jugoslavio
+Name[et]=Jugoslaavia
+Name[fr]=Yougoslavie
+Name[ga]=An Iúgslaiv
+Name[hi]=यूगोसà¥à¤²à¤¾à¤µà¤¿à¤¯à¤¾
+Name[hr]=Jugoslavija
+Name[hu]=Jugoszlávia
+Name[it]=Iugoslavia
+Name[ja]=ユーゴスラビア
+Name[km]=យូហ្គោស្លាវី
+Name[lt]=Jugoslavija
+Name[mk]=Србија и Црна Гора
+Name[nds]=Jugoslawien
+Name[nl]=Joegoslavië
+Name[pl]=Jugosławia
+Name[pt]=Jugoslávia
+Name[pt_BR]=Iugoslávia
+Name[ru]=ЮгоÑлавиÑ
+Name[se]=Jugoslavia
+Name[sk]=Juhoslávia
+Name[sl]=Jugoslavija
+Name[sr]=ЈугоÑлавија
+Name[sr@Latn]=Jugoslavija
+Name[sv]=Jugoslavien
+Name[ta]=யà¯à®•à¯‹à®¸à¯à®²à¯‹à®µà®¿à®¯à®¾
+Name[tg]=ЮгоÑлавиÑ
+Name[tr]=Yugoslavya
+Name[uk]=ЮгоÑлавіÑ
+Name[wa]=Yougoslaveye
+Name[zh_CN]=å—斯拉夫
+Name[zh_TW]=å—斯拉夫
diff --git a/kppp/DB/Provider/Yugoslavia/041Net b/kppp/DB/Provider/Yugoslavia/041Net
new file mode 100644
index 00000000..7c465500
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/041Net
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Yugoslavia/041_9xx_xxx.rst
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=041Net
+Password=net
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=041
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=041910910
diff --git a/kppp/DB/Provider/Yugoslavia/BeoTelNet b/kppp/DB/Provider/Yugoslavia/BeoTelNet
new file mode 100644
index 00000000..18550f73
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/BeoTelNet
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.106.162.2,194.106.162.3,
+DefaultRoute=1
+DisconnectCommand=
+Domain=beotel.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=BeoTelNet
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3080000:3080022:3404222:3404555
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Bar.yu b/kppp/DB/Provider/Yugoslavia/CG.Bar.yu
new file mode 100644
index 00000000..e051a468
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Bar.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Bar
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=301010 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Berane.yu b/kppp/DB/Provider/Yugoslavia/CG.Berane.yu
new file mode 100644
index 00000000..14c498fa
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Berane.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Berane
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=134672 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/CG.BijeloPolje.CG.yu b/kppp/DB/Provider/Yugoslavia/CG.BijeloPolje.CG.yu
new file mode 100644
index 00000000..22dd274a
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.BijeloPolje.CG.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@BijeloPolje
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=34644 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Budva.yu b/kppp/DB/Provider/Yugoslavia/CG.Budva.yu
new file mode 100644
index 00000000..7f32e6b0
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Budva.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=Internet Crna Gora
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Cetinje.yu b/kppp/DB/Provider/Yugoslavia/CG.Cetinje.yu
new file mode 100644
index 00000000..d7e10e2b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Cetinje.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Cetinje
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=34444
diff --git a/kppp/DB/Provider/Yugoslavia/CG.HercegNovi.yu b/kppp/DB/Provider/Yugoslavia/CG.HercegNovi.yu
new file mode 100644
index 00000000..35d98acf
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.HercegNovi.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@HercegNovi
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=50000
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Kotor.yu b/kppp/DB/Provider/Yugoslavia/CG.Kotor.yu
new file mode 100644
index 00000000..cf559c4a
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Kotor.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Kotor
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Niksic.yu b/kppp/DB/Provider/Yugoslavia/CG.Niksic.yu
new file mode 100644
index 00000000..6aa0d736
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Niksic.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Niksic
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Pljevlja.yu b/kppp/DB/Provider/Yugoslavia/CG.Pljevlja.yu
new file mode 100644
index 00000000..66418512
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Pljevlja.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Pljevlja
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=274444
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Podgorica.yu b/kppp/DB/Provider/Yugoslavia/CG.Podgorica.yu
new file mode 100644
index 00000000..4cb9cee8
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Podgorica.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Podgorica
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Tivat.yu b/kppp/DB/Provider/Yugoslavia/CG.Tivat.yu
new file mode 100644
index 00000000..a6c411ef
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Tivat.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Tivat
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/CG.Ulcinj.yu b/kppp/DB/Provider/Yugoslavia/CG.Ulcinj.yu
new file mode 100644
index 00000000..37c39e82
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.Ulcinj.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InternetCrnaGora@Ulcinj
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/CG.yu b/kppp/DB/Provider/Yugoslavia/CG.yu
new file mode 100644
index 00000000..520b69b1
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/CG.yu
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.66.160.1,195.66.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=cg.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=Internet Crna Gora
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=9802
diff --git a/kppp/DB/Provider/Yugoslavia/DrenikNet b/kppp/DB/Provider/Yugoslavia/DrenikNet
new file mode 100644
index 00000000..e45686c5
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/DrenikNet
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.252.112.5,
+DefaultRoute=1
+DisconnectCommand=
+Domain=drenik.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=DrenikNet
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3060600:3060615 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnet@Full b/kppp/DB/Provider/Yugoslavia/EUnet@Full
new file mode 100644
index 00000000..10ca7eb0
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnet@Full
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Full
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3010101:3020202:3105050:184212:186444:2351600:3281249 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnet@Lite b/kppp/DB/Provider/Yugoslavia/EUnet@Lite
new file mode 100644
index 00000000..18dd6ddc
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnet@Lite
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Lite
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%@lite,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3010101:3020202:3105050:184212:186444:2351600:3281249 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetBeograd b/kppp/DB/Provider/Yugoslavia/EUnetBeograd
new file mode 100644
index 00000000..4e50676d
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetBeograd
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Beograd
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3010101:3020202:3105050:184212:186444:2351600:3281249 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetCacak b/kppp/DB/Provider/Yugoslavia/EUnetCacak
new file mode 100644
index 00000000..364cde65
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetCacak
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Cacak
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=303030 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetKragujevac b/kppp/DB/Provider/Yugoslavia/EUnetKragujevac
new file mode 100644
index 00000000..71a5d90b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetKragujevac
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Kragujevac
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=303777 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetNis b/kppp/DB/Provider/Yugoslavia/EUnetNis
new file mode 100644
index 00000000..c5e82510
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetNis
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Nis
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=528500
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetNoviSad b/kppp/DB/Provider/Yugoslavia/EUnetNoviSad
new file mode 100644
index 00000000..ad34549b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetNoviSad
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@NoviSad
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=423333:623777:422008:4897777 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetPristina b/kppp/DB/Provider/Yugoslavia/EUnetPristina
new file mode 100644
index 00000000..1949890d
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetPristina
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Pristina
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=503010
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetS b/kppp/DB/Provider/Yugoslavia/EUnetS
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetS
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetSombor b/kppp/DB/Provider/Yugoslavia/EUnetSombor
new file mode 100644
index 00000000..079bcd65
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetSombor
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Sombor
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=463366 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/EUnetSubotica b/kppp/DB/Provider/Yugoslavia/EUnetSubotica
new file mode 100644
index 00000000..baf6b688
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/EUnetSubotica
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.247.192.33,194.247.192.1,
+DefaultRoute=1
+DisconnectCommand=
+Domain=eunet.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=EUnet@Subotica
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=620000:553311 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/InfoSKY b/kppp/DB/Provider/Yugoslavia/InfoSKY
new file mode 100644
index 00000000..4cea2c64
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/InfoSKY
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=195.250.98.5,
+DefaultRoute=1
+DisconnectCommand=
+Domain=infosky.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=InfoSky
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3020444 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/Makefile.am b/kppp/DB/Provider/Yugoslavia/Makefile.am
new file mode 100644
index 00000000..0255cceb
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/Makefile.am
@@ -0,0 +1,13 @@
+pkg_DATA = 041Net CG.Berane.yu CG.HercegNovi.yu CG.Podgorica.yu \
+ DrenikNet EUnetCacak EUnetPristina InfoSKY Sezampro \
+ CG.BijeloPolje.CG.yu CG.Kotor.yu CG.Tivat.yu \
+ EUnet@Full EUnetKragujevac VeratNet \
+ BeoTelNet CG.Budva.yu CG.Niksic.yu CG.Ulcinj.yu \
+ EUnet@Lite EUnetNis EUnetSombor PTT YUBCnet \
+ CG.Bar.yu CG.Cetinje.yu CG.Pljevlja.yu CG.yu \
+ TippNet SuOnline \
+ EUnetBeograd EUnetNoviSad EUnetSubotica SCnet
+
+pkgdir = $(kde_datadir)/kppp/Provider/Yugoslavia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/DB/Provider/Yugoslavia/PTT b/kppp/DB/Provider/Yugoslavia/PTT
new file mode 100644
index 00000000..2f04db8d
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/PTT
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=212.62.32.1,212.62.32.5,
+DefaultRoute=1
+DisconnectCommand=
+Domain=ptt.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=PTT net
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3013333:3023333:3063333:3219333 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/SCnet b/kppp/DB/Provider/Yugoslavia/SCnet
new file mode 100644
index 00000000..efb0258b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/SCnet
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=net.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=SCnet
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3010141:3111641
diff --git a/kppp/DB/Provider/Yugoslavia/Sezampro b/kppp/DB/Provider/Yugoslavia/Sezampro
new file mode 100644
index 00000000..3b2bf90a
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/Sezampro
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=194.106.188.17,194.106.188.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=sezampro.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=Sezampro
+Password=%PASSWORD%
+ScriptArguments=rname:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3020234:3341451:186333:3222-592 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/SuOnline b/kppp/DB/Provider/Yugoslavia/SuOnline
new file mode 100644
index 00000000..89d5b90b
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/SuOnline
@@ -0,0 +1,24 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Yugoslavia/Lokalni_poziv.rst
+Authentication=1
+AutoDNS=1
+AutoName=0
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=suonline.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=SuOnline
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=626400
diff --git a/kppp/DB/Provider/Yugoslavia/TippNet b/kppp/DB/Provider/Yugoslavia/TippNet
new file mode 100644
index 00000000..b1a915f2
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/TippNet
@@ -0,0 +1,24 @@
+# KDE Config File
+AccountingEnabled=1
+AccountingFile=/Yugoslavia/Lokalni_poziv.rst
+Authentication=1
+AutoDNS=1
+AutoName=0
+Command=
+DNS=
+DefaultRoute=1
+DisconnectCommand=
+Domain=tippnet.co.yu
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=0.0.0.0
+Name=TippNet
+Password=%PASSWORD%
+ScriptArguments=
+ScriptCommands=
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=654321
diff --git a/kppp/DB/Provider/Yugoslavia/VeratNet b/kppp/DB/Provider/Yugoslavia/VeratNet
new file mode 100644
index 00000000..ba775fc2
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/VeratNet
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=212.200.40.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=verat.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=VeratNet
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3060606 \ No newline at end of file
diff --git a/kppp/DB/Provider/Yugoslavia/YUBCnet b/kppp/DB/Provider/Yugoslavia/YUBCnet
new file mode 100644
index 00000000..3e9e3013
--- /dev/null
+++ b/kppp/DB/Provider/Yugoslavia/YUBCnet
@@ -0,0 +1,23 @@
+# KDE Config File
+AccountingEnabled=0
+AccountingFile=
+Authentication=1
+AutoName=0
+Command=
+DNS=212.124.160.1,212.124.160.2,
+DefaultRoute=1
+DisconnectCommand=
+Domain=yubc.net
+ExDNSDisabled=0
+Gateway=0.0.0.0
+IPAddr=%IPADDR%
+Name=YUBCnet
+Password=%PASSWORD%
+ScriptArguments=ogin:,%USERNAME%,assword:,%PASSWORD%,
+ScriptCommands=Expect,Send,Expect,Send,
+StorePassword=1
+SubnetMask=0.0.0.0
+Username=%USERNAME%
+VolumeAccountingEnabled=0
+pppdArguments=
+Phonenumber=3200700:3020280
diff --git a/kppp/HISTORY b/kppp/HISTORY
new file mode 100644
index 00000000..fe784cad
--- /dev/null
+++ b/kppp/HISTORY
@@ -0,0 +1,37 @@
+
+kppp History ( In particular it's relation to Ezppp )
+=====================================================
+
+kppp 1.4 Jul 1998
+
+Harri Porten and Mario Weilguni have taken over the further development
+of kppp.
+
+
+
+kppp 0.6 Nov 26 97:
+
+The data savings scheme has been switched over to to the use of KConfig.
+I consider kppp virtually free of any ezppp code. I challenge anyone
+who doubts this to verify my claim by doing a 'diff' on the sources.
+
+
+kppp 0.3 Tue Apr 22:
+
+With exception of the data savings scheme, which will follow next, kppp
+has almost completely been rewritten.
+
+kppp 0.2:
+
+kppp is based in part on ezppp beta 6, Copyright 1997 Jay Painter.
+I have tried to convince Jay to contribute his ezppp to the kde project.
+Jay responded by saying that he had no time to make this program
+suitable for the kde project. He added we should not hesitate to
+modify ezppp according to our tastes and merge it with the kde project.
+So I took ezppp, reworte most of the user interface and added many
+internal changes. We hope kppp will become a useful part of the KDE project.
+
+Bernd Wuebben
+wuebben@math.cornell.edu
+wuebben@kde.org
+
diff --git a/kppp/INSTALL b/kppp/INSTALL
new file mode 100644
index 00000000..a207c1e1
--- /dev/null
+++ b/kppp/INSTALL
@@ -0,0 +1,46 @@
+$Id$
+
+It is assumed that you have already successfully installed pppd.
+For more info see the PPP-HOWTO.
+
+INSTALLATION:
+
+./configure
+
+Now check the kppp/config.h file ( Linux users should be fine )
+then:
+
+make
+make install
+
+o If you run "make install" as root the kppp binary will be installed
+ setuid root. Otherwise you can do this on your own:
+ cd $(KDEDIR)/bin ; chown root kppp; chmod +s kppp. This might not
+ be absolutely necessary ( depending on the permissions
+ set on the modem device, /etc/resolv.conf etc. but if you can
+ afford to, this will make your life much easier.
+
+o if you can't give kppp setuid permission, here is the deal:
+ In order for kppp to be able to update /etc/resolv.conf,
+ /etc/ppp/pap-secrets or /etc/ppp/chap-secrets, respectively,
+ the users of kppp must have write permissions to these files
+ and of course the modem device. It's up to you to figure
+ the right permissions. ;-)
+
+o make sure that the pppd's option file which usually resides
+ in /etc/ppp/ exists, but is empty. pppd wants it to exist,
+ but we don't want pppd to take any options from it. kppp
+ will hand pppd the necessary options.
+
+o when you run kppp for the first time you need to create and
+ configure kppp for you ISP account. I hope kppp was written
+ so that you will have no difficutlies understanding the many
+ options offered. Please make use of the html help documentation
+ if you have questions.
+
+
+that's it,
+happy dialing!
+
+Bernd Wuebben Mario Weilguni Harri Porten
+wuebben@kde.org mweilguni@kde.org porten@kde.org
diff --git a/kppp/KPPPIface.h b/kppp/KPPPIface.h
new file mode 100644
index 00000000..2b5a6cb5
--- /dev/null
+++ b/kppp/KPPPIface.h
@@ -0,0 +1,39 @@
+#ifndef KPPPIFACE_H
+#define KPPPIFACE_H
+
+#include <dcopobject.h>
+
+/* IMPORTANT: when using the aboutToDisconnect signal bear in mind that
+
+ - if pppd dies, aboutToDisconnect will never be emitted because the
+ connection is already dead by the time kppp knows about it.
+ disconnected() will be emitted regardless of the cause of
+ disconnection.
+
+ - during a normal disconnection aboutToDisconnect will be emitted
+ shortly before disconnection, but in systems under heavy load there's
+ no warranty that the signal will be delivered to the applications
+ before the disconnection is performed. DCOP works this way,
+ sorry.
+
+*/
+
+class KpppIface : virtual public DCOPObject
+{
+ K_DCOP
+
+ k_dcop:
+ virtual void beginConnect() = 0;
+ virtual void disconnect() = 0;
+ virtual bool isConnected() const = 0;
+
+ k_dcop_signals:
+
+ void aboutToConnect();
+ void connected();
+ void aboutToDisconnect(); // see the note above
+ void disconnected();
+
+};
+
+#endif
diff --git a/kppp/Kppp.desktop b/kppp/Kppp.desktop
new file mode 100644
index 00000000..2cbe91c9
--- /dev/null
+++ b/kppp/Kppp.desktop
@@ -0,0 +1,70 @@
+[Desktop Entry]
+GenericName=Internet Dial-Up Tool
+GenericName[be]=Праграма злучÑÐ½Ð½Ñ Ð· ІнтÑрнÑтам праз мадÑм
+GenericName[bg]=Включване в Интернет
+GenericName[bn]=ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ ডায়াল-আপ সরঞà§à¦œà¦¾à¦®
+GenericName[bs]=Program za spajanje na Internet
+GenericName[ca]=Eina per a la connexió telefònica a Internet
+GenericName[cs]=PÅ™ipojení pÅ™es vytáÄenou linku
+GenericName[da]=Internet-opkaldsværktøj
+GenericName[de]=Einwahl ins Internet
+GenericName[el]=ΕÏγαλείο σÏνδεσης μέσω τηλεφώνου στο διαδίκτυο
+GenericName[eo]=Ilo por konekti al Interreto
+GenericName[es]=Herramienta de conexión telefónica a Internet
+GenericName[et]=Sissehelistamine
+GenericName[eu]=Internet markatzaile tresna
+GenericName[fa]=ابزار شماره‌گیری اینترنت
+GenericName[fi]=Internet-yhteyden soitto-ohjelma
+GenericName[fr]=Connexion internet par modem
+GenericName[ga]=Uirlis Diailithe Idirlín
+GenericName[gl]=Ferramenta de Conexión Telefónica a Internet
+GenericName[he]=כלי חיוג ל×ינטרנט
+GenericName[hr]=Program za spajanje na Internet
+GenericName[hu]=Internetes tárcsázó
+GenericName[is]=Tengjast Netinu með upphringisambandi
+GenericName[it]=Strumento per la connessione telefonica ad Internet
+GenericName[ja]=インターãƒãƒƒãƒˆãƒ€ã‚¤ã‚¢ãƒ«ã‚¢ãƒƒãƒ—ツール
+GenericName[ka]=ინტერნეტის Dial-Up ხელსáƒáƒ¬áƒ§áƒ
+GenericName[kk]=Телефондық желі арқылы Интернетке қоÑылу құралы
+GenericName[km]=ឧបករណáŸâ€‹áž…ូល​ដំណើរការ​អ៊ីនធឺណិហážáž¶áž˜â€‹ážšáž™áŸˆâ€‹áž‘ូរសáŸáž–្ទ​លើ​ážáž»
+GenericName[lt]=Interneto skambinimo priemonÄ—
+GenericName[mk]=Ðлатка за поврзување на Интернет
+GenericName[nb]=Verktøy for oppringing til Internet
+GenericName[nds]=Internetinwahl-Warktüüch
+GenericName[ne]=इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ डायल-अप उपकरण
+GenericName[nl]=Inbelprogramma
+GenericName[nn]=Verktøy for Internett-oppringing
+GenericName[pl]=Narzędzie do łączenia telefonicznego z Internetem
+GenericName[pt]=Ferramenta de Ligação à Internet
+GenericName[pt_BR]=Ferramenta de Conexão a Internet
+GenericName[ro]=Utilitar de conectare la Internet prin telefon
+GenericName[ru]=Подключение по диалапу
+GenericName[sk]=Pripojenie na internet cez modem
+GenericName[sl]=Orodje za klicni priklop na internet
+GenericName[sr]=Ðлат за повезивање на Интернет
+GenericName[sr@Latn]=Alat za povezivanje na Internet
+GenericName[sv]=Uppringningsverktyg för Internet
+GenericName[ta]=இணைய அழைபà¯à®ªà¯à®•à¯ கரà¯à®µà®¿
+GenericName[tg]=ÐÑбоб барои Зангзанӣ ба Интернет
+GenericName[tr]=Çevirmeli Ağ Aracı
+GenericName[uk]=ЗаÑіб дозвону в Інтернет
+GenericName[zh_CN]=Internet 拨å·å·¥å…·
+GenericName[zh_HK]=互è¯ç¶²æ’¥è™Ÿå·¥å…·
+GenericName[zh_TW]=Internet 撥號工具
+Name=KPPP
+Name[af]=Kppp
+Name[bn]=কে-পি-পি-পি
+Name[hi]=केपीपीपी
+Name[ne]=के पी पी पी
+Name[sv]=Kppp
+Name[zh_TW]=KPPP 撥號工具
+MimeType=
+DocPath=kppp/index.html
+Exec=kppp %i %m
+Icon=kppp
+Path=
+Type=Application
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Network;Dialup;
diff --git a/kppp/Makefile.am b/kppp/Makefile.am
new file mode 100644
index 00000000..4548f5f7
--- /dev/null
+++ b/kppp/Makefile.am
@@ -0,0 +1,153 @@
+# Yow Emacs, this is a -*- makefile -*-
+
+# if you use a variable *dir and have *_DATA, it will be installed by
+# make install
+xdg_apps_DATA = Kppp.desktop
+
+EXTRA_DIST = $(xdg_apps_DATA)
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+# claim, which subdirectories you want to install
+SUBDIRS = pixmaps icons logview Rules DB
+
+# This one gets installed
+bin_PROGRAMS = kppp
+
+# Which sources should be compiled for kppp.
+kppp_SOURCES = kpppwidget.cpp \
+ general.cpp \
+ accounts.cpp \
+ connect.cpp \
+ conwindow.cpp \
+ debug.cpp \
+ edit.cpp \
+ iplined.cpp \
+ main.cpp \
+ modem.cpp \
+ modemcmds.cpp \
+ pppdargs.cpp \
+ pppdata.cpp \
+ scriptedit.cpp\
+ pwentry.cpp \
+ modeminfo.cpp\
+ pppstatdlg.cpp\
+ pppstats.cpp\
+ miniterm.cpp\
+ accounting.cpp \
+ acctselect.cpp\
+ ruleset.cpp \
+ docking.cpp \
+ runtests.cpp \
+ loginterm.cpp \
+ ppplog.cpp \
+ newwidget.cpp \
+ requester.cpp \
+ opener.cpp \
+ modemdb.cpp \
+ utils.cpp \
+ providerdb.cpp \
+ modems.cpp \
+ KPPPIface.skel
+
+# the library search path
+kppp_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+kppp_LDADD = -lm $(LIB_KIO) $(LIB_KDEUI)
+
+# this option you can leave out. Just, if you use "make dist", you need it
+noinst_HEADERS = kpppwidget.h \
+ accounts.h \
+ kpppconfig.h \
+ connect.h \
+ conwindow.h \
+ debug.h \
+ edit.h \
+ general.h \
+ iplined.h \
+ main.h \
+ modem.h \
+ modemcmds.h \
+ pppdargs.h \
+ pppdata.h \
+ scriptedit.h\
+ pwentry.h \
+ modeminfo.h\
+ pppstatdlg.h\
+ miniterm.h\
+ accounting.h \
+ acctselect.h\
+ ruleset.h \
+ docking.h \
+ runtests.h \
+ loginterm.h \
+ auth.h \
+ version.h \
+ macros.h \
+ ppplog.h \
+ newwidget.h \
+ modemdb.h \
+ utils.h \
+ providerdb.h \
+ modems.h \
+ KPPPIface.h
+
+METASOURCES = accounting.moc \
+ accounts.moc \
+ acctselect.moc \
+ connect.moc \
+ conwindow.moc \
+ debug.moc \
+ docking.moc \
+ edit.moc \
+ general.moc \
+ loginterm.moc \
+ kpppwidget.moc \
+ miniterm.moc \
+ modem.moc \
+ modemcmds.moc \
+ modemdb.moc \
+ modeminfo.moc \
+ pppdargs.moc \
+ pppstatdlg.moc \
+ pppstats.moc \
+ pwentry.moc \
+ scriptedit.moc \
+ providerdb.moc \
+ modems.moc
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kppp.pot
+
+# just install datas here. Use install-exec-data for scripts and etc.
+# the binary itself is already installed from automake
+# use mkinstalldirs, not "install -d"
+# don't install a list of file. Just one file per install.
+# if you have more of them, create a subdirectory with an extra Makefile
+install-data-local:
+ @echo ""
+ @echo "Don't forget to send me a phone tarif rules file for your"
+ @echo "telecom company/country/region. Thanks."
+ @echo ""
+ @echo "We will be maintaining an archive on the kppp page at"
+ @echo "http://devel-home.kde.org/~kppp/index.html"
+ @echo "WARNING: kppp is by default installed with SETUID bit on!"
+ @echo "If you are concerned about the SETUID bit, create a modem"
+ @echo "group instead."
+ @echo ""
+ @echo "Happy Dialing! -- the KPPP team"
+ @echo ""
+
+# kppp wants to be installed suid root
+install-exec-hook:
+ @(chown root $(DESTDIR)/$(bindir)/kppp && chmod 4755 $(DESTDIR)/$(bindir)/kppp) || echo "Was not able to make kppp setuid root"
+
+# remove ALL you have installed in install-data-local or install-exec-local
+#uninstall-local:
+# @(cd Rules && $(MAKE) uninstall)
+
+#distclean-local:
+# @(cd Rules && $(MAKE) distclean)
+
diff --git a/kppp/NEWS b/kppp/NEWS
new file mode 100644
index 00000000..c732f438
--- /dev/null
+++ b/kppp/NEWS
@@ -0,0 +1,28 @@
+Jul 13 1999:
+ * `Mode {7bit|8bit}' script command for CompuServe's 7E1 mode
+ * speed up in reading from the modem
+
+Jun 11 1999:
+ * detection of stale lock files works again
+ * "PPP support missing" warning is no longer fatal
+ * line termination defaults to CR instead of CR/LF
+
+Apr 10 1999:
+ * support for pppd 2.3.6 and 2.3.7
+ * solved crash on SuSE and Caldera systems
+ * eliminated most prominent cause for timeout errors
+ * should run on Alphas again
+ * `lock' in /etc/ppp/options is no longer in kppp's way
+
+Aug 21 1998:
+ * Syslog viewer
+ * graph showing transfer rates
+ * 'kppp -k' command line option
+
+May 12 1998:
+ * Multiple phone-numbers supported
+
+April 6 1998:
+ * Volume accounting
+ * /etc/kppp.allow file for superusers who want to have control
+ who may dial out
diff --git a/kppp/README b/kppp/README
new file mode 100644
index 00000000..c6c28d18
--- /dev/null
+++ b/kppp/README
@@ -0,0 +1,105 @@
+ kppp - a dialer for pppd
+
+kppp is a dialer and front end for pppd. It allows for interactive script
+generation and network connection setup.
+
+Version 0.7.1
+
+o New option to disable existing nameservers in /etc/resolv.conf
+
+o Looping, prompt and password dialogs in the scripting module
+
+This is version 0.5.9
+
+o a bunch of little fixes.
+
+This is version 0.5.8
+
+o all sorts of little fixes, in particular the 'Costs' file is
+ now generated again correctly
+
+
+New in Verions 0.5.6
+
+o configurable busy redial pause
+
+New in Verions 0.5.5
+
+o new copy function on the account dialog
+
+
+New in Verions 0.5.4
+
+o nothing new, sorry. Just bug fixes and work to get kppp to compile
+ with the new kdelibs and Qt 1.3
+
+New in Verions 0.5.3
+
+o option to not have kppp terminate the ppp connection on X-server exit
+o leaving the lockfile edit box empty will cause kppp not to create a
+ lockfile
+
+New in Verions 0.5.2
+
+o if the X-server exits, kppp will terminate in a controlled fashion:
+ accounting will be terminated
+ ppp connection will be terminated
+
+New in Version 0.5
+
+o Phone cost accounting
+o Configurable Modem Timeout
+o Configurable pppd Timeout
+o Dynamic Scripting Timeout
+o Optional Automatic configuration of host and domainname
+o modem device locking (Kppp should now work with mgetty)
+o domain name configuration
+o vastly improved documentation
+o bug fixes
+
+Special thank to Mario Weilguni and Jesus Fuentes Saavedra for their
+contributions towards kppp-0.5!!
+
+New in Version 0.4
+
+o Better Window Placement
+o KDE compliance -- Color Schemes etc.
+
+New in Verion 0.3:
+
+o mini-terminal: you no longer need a terminal communications program
+ such as minicom or seyon to setup and test you modem and isp connection.
+
+o Statistics Dialog: Ever wanted to know the number of bytes and packets
+ that leave and enter your ppp interface? A complete set of ppp statistics
+ together with your local and remote IP addresses are displayed on the
+ statistics dialog.
+
+o animation: Yes, you may trust your eyes!
+
+o ATI query dialog: Ever wanted to know what sort of modem you own or
+ use?
+
+o optional display of total time connected on the kppp icon
+
+o Much improved dialing and modem handling: kppp is now ROCK-SOLID on my
+ machine. I hope this will be the case for you too. If not please mail me
+ and describe the problems you are experiencing. Also, see the Makefile
+ for instructions on how to have kppp give you more debugging info.
+
+o Finally: kppp accepts commandline options! For the impatient, you
+ can have kppp start dialing immediately after start-up
+
+For full help functionality you need "kdehelp" from the kde project.
+For further info about kppp plese review the files in the docs
+directory. For installation instructions read INSTALL.
+There is still much to be done, however I believe you will find kppp
+very useful already !
+
+happy dialing !
+
+Bernd Wuebben
+
+wuebben@math.cornell.edu
+wuebben@kde.org
+
diff --git a/kppp/README.ModemDB b/kppp/README.ModemDB
new file mode 100644
index 00000000..448912c2
--- /dev/null
+++ b/kppp/README.ModemDB
@@ -0,0 +1,77 @@
+Modem Database
+==============
+
+The Modem Database (MD) will further simplify kppp by allowing the user to
+select a modem from a database. The MD will be organized by the name of the
+manufacturer. Each modem will only have those entries not common to the
+generic Hayes Modem command set. The following entries will be available
+(defaults in brackets):
+
+[Group]: Can be freely chosen, may not be "Common"
+Vendor: The modem vendor
+Name: The name of the modem, without vendor. Needed
+Parent: All settings of this modem are inherited. Parent works
+ recursive, so if the parent has another parent, this will
+ be inherited too and so on.
+Reset: This should reset the modem (ATZ)
+Init: The init string (AT V1 E1)
+Volume0: Volume off (ATL0)
+Volume1: Medium volume (ATL1)
+Volume2: Loud volume (ATL3)
+PreInitDelay: Delay after opening the modem, before sending the Init
+ command (50)
+PostInitDelay: Delay after sending the Init command (30)
+InitResponse: Modem response to the Init command (OK)
+BlindDialOn: To dial without waiting for line carrier (ATX3)
+BlindDialOff: Don´t dial without waiting for line carrier (ATX4)
+ToneDial: The command to dial out, number is appended (ATDT)
+PulseDial: The command to use impulse dialing (ATDP)
+ConnectResponse:The modems answer to "Dial" when connection is established
+BusyResponse: Response to a busy line (BUSY)
+NoCarrierResponse: Response when carrier was not establ. (NO CARRIER)
+NoDialTone: Response when no dial tone was found (NO DIALTONE)
+SupportsCD: Modem supports the "Modem asserts CD line" feature (1==yes)
+AutoAnswerOn: Turn on auto answer (for callback) (ATS0=1)
+AutoAnswerOff: Turn off auto answer ATS0=0
+
+If the name begins with "!", this is NOT shown in the modem list but can be
+taken as parent. Every modem initially inherits from "Common".
+
+Example:
+========
+
+[Common]
+Vendor=Generic
+Name=Hayes Compatible
+Reset=Z
+Init=ATV1
+Volume0=M0L0
+Volume1=M1L1
+Volume2=M1L3
+PreInitDelay=50
+PostInitDelay=30
+InitResponse=OK
+BlindDialOn=X3
+BlindDialOff=X4
+ToneDial=DT
+PulseDial=DP
+ConnectResponse=CONNECT
+BusyResponse=BUSY
+NoCarrierResponese=NO CARRIER
+NoDialTone=NO DIALTONE
+SupportsCD=true
+
+[!Zyxel Omni TA128]
+Vendor=Zyxel
+Name=Omni TA 128
+Reset=&F
+
+[Zyxel Omni TA128/X75]
+Parent=Zyxel Omni TA128
+Name=Zyxel Omni TA128/X75
+Init=B20 V1 E1
+
+[Zyxel Omni TA128/sync.PPP]
+Parent=!Zyxel Omni TA128
+Name=Zyxel Omni TA128/sync.PPP
+Init=B40 V1 E1
diff --git a/kppp/Rules/Argentina/Argentina_0610.rst b/kppp/Rules/Argentina/Argentina_0610.rst
new file mode 100644
index 00000000..70b558b1
--- /dev/null
+++ b/kppp/Rules/Argentina/Argentina_0610.rst
@@ -0,0 +1,26 @@
+################################################################
+#
+# This is the cost rule for Internet (0610) calls in Argentina,
+# if your carrier are Telecom or Telefonica, as of Sep.2000.
+#
+# Matías Alejo García
+# matiu00@yahoo.com
+################################################################
+
+name=Argentina_0610
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+default=(0.007865, 60)
+
+on (monday..friday) between (08:00..19:59) use (0.01573, 60)
+on (saturday) between (08:00..12:59) use (0.01573, 60)
+
diff --git a/kppp/Rules/Argentina/Argentina_Local.rst b/kppp/Rules/Argentina/Argentina_Local.rst
new file mode 100644
index 00000000..ad5c750d
--- /dev/null
+++ b/kppp/Rules/Argentina/Argentina_Local.rst
@@ -0,0 +1,26 @@
+################################################################
+#
+# This is the cost rule for Local calls in Argentina,
+# if your carrier are Telecom or Telefonica, as of Sep.2000.
+#
+# Matías Alejo García
+# matiu00@yahoo.com
+################################################################
+
+name=Argentina_local
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+default=(0.024, 120)
+
+on (monday..friday) between (08:00..19:59) use (0.012, 120)
+on (saturday) between (08:00..12:59) use (0.012, 120)
+
diff --git a/kppp/Rules/Argentina/Makefile.am b/kppp/Rules/Argentina/Makefile.am
new file mode 100644
index 00000000..173b70f3
--- /dev/null
+++ b/kppp/Rules/Argentina/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = Argentina_Local.rst Argentina_0610.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Argentina
+
+EXTRA_DIST = $(pkg_DATA)
+
+
+
diff --git a/kppp/Rules/Australia/Local.rst b/kppp/Rules/Australia/Local.rst
new file mode 100644
index 00000000..eb7b314f
--- /dev/null
+++ b/kppp/Rules/Australia/Local.rst
@@ -0,0 +1,18 @@
+###############################################################
+#
+# Australian Local Call Rate - Telstra
+#
+# created by Collin Baillie <gvlink@tropinet.com>
+#
+################################################################
+
+name=Australia_Local_Area
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+per_connection=0.25
+default=(0.0, 1)
diff --git a/kppp/Rules/Australia/Makefile.am b/kppp/Rules/Australia/Makefile.am
new file mode 100644
index 00000000..5b0ba2cb
--- /dev/null
+++ b/kppp/Rules/Australia/Makefile.am
@@ -0,0 +1,10 @@
+pkg_DATA = Optus_Residential.rst \
+ Local.rst \
+ STD_Zone_1_-_25-50_kms.rst \
+ STD_Zone_2_-_50-85_kms.rst \
+ STD_Zone_3_-_85-165_kms.rst \
+ STD_Zone_4_-_165+_kms.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Australia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Australia/Optus_Residential.rst b/kppp/Rules/Australia/Optus_Residential.rst
new file mode 100644
index 00000000..97fe9282
--- /dev/null
+++ b/kppp/Rules/Australia/Optus_Residential.rst
@@ -0,0 +1,132 @@
+################################################################
+#
+# This is a sample rule set for kppp. You can use it as a
+# template when you have to create your own ruleset. If you do
+# so, remove all comments and add your own. This will allow
+# other users to check your ruleset more easily.
+#
+# Please sign the the tarif file with your name an email address
+# so that I can contact you if necessary.
+#
+# NOTE: the rules in this rule set do not make much sense and
+# are only for demonstration purposes
+#
+# NOTE ON FILENAMES:
+# when you create your own ruleset, use "_" in filename
+# instead of spaces and use ".rst as extension
+# i.e. "Austria city calls"
+# --> file should be saved as "Austria_city_calls.rst"
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=default
+
+################################################################
+# currency settings
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=ATS
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.1, 72)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+on () between () use (0.2, 2)
+
+# same as above
+on (monday..sunday) between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# applies on friday, saturday, sunday and monday 8am until 1pm
+on (friday..monday) between (8:00..13:00) use(0.3,72)
+
+# ATTENTION:
+on(monday..friday) between (21:00..5:00) use (0.4,2)
+# does NOT include saturday 0:00-5:00, just monday..friday, as it says.
+
+# applies on a given date (christmas)
+on (12/25) between () use (0.3,72)
+
+# a range of dates and one weekday
+on (12/25..12/27, 12/31, 07/04, monday) between () use (0.4, 72)
+
+# use this for easter
+on (easter) between () use (0.3,72)
+
+# easter + 50 days (Pfingstmontag/ Pentecost Monday )
+on (easter+50) between () use (0.3,72)
+
+on (thursday) between (20:00..21:52) use (8.2, 1)
+
+
+# The "on()" rules above all relates to current time only. You can also
+# make a rule depend on the number of seconds you have been connected
+# by specifying this time as a third argument to "use()".
+# For instance, let's say normal rate in the evening is 0.20 per minute,
+# and it drops by 20% after one hour of connect time. This can be modelled
+# like:
+
+on () between (19:30..08:00) use (0.20, 60)
+on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# Note that these rules, just like other rules, are sensitive to the
+# order in which they appear.
+
diff --git a/kppp/Rules/Australia/STD_Zone_1_-_25-50_kms.rst b/kppp/Rules/Australia/STD_Zone_1_-_25-50_kms.rst
new file mode 100644
index 00000000..fa86cd66
--- /dev/null
+++ b/kppp/Rules/Australia/STD_Zone_1_-_25-50_kms.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Australian STD Zone 1 - Telstra
+#
+# created by Collin Baillie (gvlink@tropinet.com) 16 June 1999
+#
+################################################################
+name=STD_(25-50km)
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.15
+default=(0.09, 60)
+
+# more complicated rules:
+#
+on (monday..friday) between (07:00..12:59) use (0.09, 60)
+on (monday..friday) between (13:00..18:59) use (0.08, 60)
+on (monday..friday) between (19:00..06:59) use (0.05, 60)
+on (saturday..sunday) between () use (0.05, 60)
+
+# NOTE: The Australian carrier Telstra, actually has a special tarrif during
+# weekdays (Monday - Friday), from 7pm - 12 midnight, during which the normal
+# tarrif applies up to, but not exceeding $3.00. This feature doesn't seem
+# to be implimented with kppp yet.
diff --git a/kppp/Rules/Australia/STD_Zone_2_-_50-85_kms.rst b/kppp/Rules/Australia/STD_Zone_2_-_50-85_kms.rst
new file mode 100644
index 00000000..79906110
--- /dev/null
+++ b/kppp/Rules/Australia/STD_Zone_2_-_50-85_kms.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Australian STD Zone 2 - Telstra
+#
+# created by Collin Baillie (gvlink@tropinet.com) 16 June 1999
+#
+################################################################
+name=STD_(50-85)
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.15
+default=(0.18, 60)
+
+# more complicated rules:
+#
+on (monday..friday) between (07:00..12:59) use (0.18, 60)
+on (monday..friday) between (13:00..18:59) use (0.16, 60)
+on (monday..friday) between (19:00..06:59) use (0.09, 60)
+on (saturday..sunday) between () use (0.09, 60)
+
+# NOTE: The Australian carrier Telstra, actually has a special tarrif during
+# weekdays (Monday - Friday), from 7pm - 12 midnight, during which the normal
+# tarrif applies up to, but not exceeding $3.00. This feature doesn't seem
+# to be implimented with kppp yet.
diff --git a/kppp/Rules/Australia/STD_Zone_3_-_85-165_kms.rst b/kppp/Rules/Australia/STD_Zone_3_-_85-165_kms.rst
new file mode 100644
index 00000000..4b97d343
--- /dev/null
+++ b/kppp/Rules/Australia/STD_Zone_3_-_85-165_kms.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Australian STD Zone 3 - Telstra
+#
+# created by Collin Baillie (gvlink@tropinet.com) 16 June 1999
+#
+################################################################
+name=STD_(85-165km)
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.15
+default=(0.24, 60)
+
+# more complicated rules:
+#
+on (monday..friday) between (07:00..12:59) use (0.24, 60)
+on (monday..friday) between (13:00..18:59) use (0.22, 60)
+on (monday..friday) between (19:00..06:59) use (0.12, 60)
+on (saturday..sunday) between () use (0.12, 60)
+
+# NOTE: The Australian carrier Telstra, actually has a special tarrif during
+# weekdays (Monday - Friday), from 7pm - 12 midnight, during which the normal
+# tarrif applies up to, but not exceeding $3.00. This feature doesn't seem
+# to be implimented with kppp yet.
diff --git a/kppp/Rules/Australia/STD_Zone_4_-_165+_kms.rst b/kppp/Rules/Australia/STD_Zone_4_-_165+_kms.rst
new file mode 100644
index 00000000..73a91a35
--- /dev/null
+++ b/kppp/Rules/Australia/STD_Zone_4_-_165+_kms.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Australian STD Zone 4 - Telstra
+#
+# created by Collin Baillie (gvlink@tropinet.com) 16 June 1999
+#
+################################################################
+name=STD_(165+km)
+currency_symbol=$
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.15
+default=(0.28, 60)
+
+# more complicated rules:
+#
+on (monday..friday) between (07:00..12:59) use (0.28, 60)
+on (monday..friday) between (13:00..18:59) use (0.25, 60)
+on (monday..friday) between (19:00..06:59) use (0.14, 60)
+on (saturday..sunday) between () use (0.14, 60)
+
+# NOTE: The Australian carrier Telstra, actually has a special tarrif during
+# weekdays (Monday - Friday), from 7pm - 12 midnight, during which the normal
+# tarrif applies up to, but not exceeding $3.00. This feature doesn't seem
+# to be implimented with kppp yet.
diff --git a/kppp/Rules/Austria/Business_1/Local.rst b/kppp/Rules/Austria/Business_1/Local.rst
new file mode 100644
index 00000000..020ed661
--- /dev/null
+++ b/kppp/Rules/Austria/Business_1/Local.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria City rate ruleset
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Local_Area
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.996, 158.4)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (0.996, 72)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.996, 158.4)
diff --git a/kppp/Rules/Austria/Business_1/Long_Distance.rst b/kppp/Rules/Austria/Business_1/Long_Distance.rst
new file mode 100644
index 00000000..1dccdcff
--- /dev/null
+++ b/kppp/Rules/Austria/Business_1/Long_Distance.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria Zone 1 ruleset (50km - 200km)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Zone
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.996, 72)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (0.996, 25)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.996, 72)
diff --git a/kppp/Rules/Austria/Business_1/Makefile.am b/kppp/Rules/Austria/Business_1/Makefile.am
new file mode 100644
index 00000000..746d4552
--- /dev/null
+++ b/kppp/Rules/Austria/Business_1/Makefile.am
@@ -0,0 +1,7 @@
+rules_DATA = Local.rst \
+ Long_Distance.rst \
+ Online.rst
+
+rulesdir = $(kde_datadir)/kppp/Rules/Austria/Business_1
+
+EXTRA_DIST = $(rules_DATA)
diff --git a/kppp/Rules/Austria/Business_1/Online.rst b/kppp/Rules/Austria/Business_1/Online.rst
new file mode 100644
index 00000000..875008fe
--- /dev/null
+++ b/kppp/Rules/Austria/Business_1/Online.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Austria City rate ruleset (special "online" numbers)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+# Version 2
+# lt. österreichischer Post gelten an Feiertagen anders als auf der
+# PTA Homepage angekündigt der Spar- bzw. Superspartarif. Daher habe ich
+# einige der österreichischen Feiertage eingetragen. Sollte mir einer
+# entgangen sein, so bitte an mich melden.
+#
+# Sollte die Information nicht stimmen, so flames bitte an:
+# marketing@pta.at :-))
+#
+################################################################
+
+name=Austria_Online
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.996, 352)
+
+# tagestarif
+on (monday..friday) between (08:00..12:00) use (0.996, 120)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.996, 352)
+
+
diff --git a/kppp/Rules/Austria/Business_2/Local.rst b/kppp/Rules/Austria/Business_2/Local.rst
new file mode 100644
index 00000000..e2683524
--- /dev/null
+++ b/kppp/Rules/Austria/Business_2/Local.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria City rate ruleset
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Local_Area
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.936, 158.4)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (0.936, 72)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.936, 158.4)
diff --git a/kppp/Rules/Austria/Business_2/Long_Distance.rst b/kppp/Rules/Austria/Business_2/Long_Distance.rst
new file mode 100644
index 00000000..56569acb
--- /dev/null
+++ b/kppp/Rules/Austria/Business_2/Long_Distance.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria Zone 1 ruleset (50km - 200km)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Zone
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.936, 72)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (0.936, 25)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.936, 72)
diff --git a/kppp/Rules/Austria/Business_2/Makefile.am b/kppp/Rules/Austria/Business_2/Makefile.am
new file mode 100644
index 00000000..48bcb9b9
--- /dev/null
+++ b/kppp/Rules/Austria/Business_2/Makefile.am
@@ -0,0 +1,7 @@
+rules_DATA = Local.rst \
+ Long_Distance.rst \
+ Online.rst
+
+rulesdir = $(kde_datadir)/kppp/Rules/Austria/Business_2
+
+EXTRA_DIST = $(rules_DATA)
diff --git a/kppp/Rules/Austria/Business_2/Online.rst b/kppp/Rules/Austria/Business_2/Online.rst
new file mode 100644
index 00000000..4b7ea752
--- /dev/null
+++ b/kppp/Rules/Austria/Business_2/Online.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Austria City rate ruleset (special "online" numbers)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+# Version 2
+# lt. österreichischer Post gelten an Feiertagen anders als auf der
+# PTA Homepage angekündigt der Spar- bzw. Superspartarif. Daher habe ich
+# einige der österreichischen Feiertage eingetragen. Sollte mir einer
+# entgangen sein, so bitte an mich melden.
+#
+# Sollte die Information nicht stimmen, so flames bitte an:
+# marketing@pta.at :-))
+#
+################################################################
+
+name=Austria_Online
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(0.936, 352)
+
+# tagestarif
+on (monday..friday) between (08:00..12:00) use (0.936, 120)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.936, 352)
+
+
diff --git a/kppp/Rules/Austria/Makefile.am b/kppp/Rules/Austria/Makefile.am
new file mode 100644
index 00000000..5da251d0
--- /dev/null
+++ b/kppp/Rules/Austria/Makefile.am
@@ -0,0 +1,6 @@
+SUBDIRS = Business_1 \
+ Business_2 \
+ Minimum \
+ Standard
+
+# Business_3
diff --git a/kppp/Rules/Austria/Minimum/Local.rst b/kppp/Rules/Austria/Minimum/Local.rst
new file mode 100644
index 00000000..13169fea
--- /dev/null
+++ b/kppp/Rules/Austria/Minimum/Local.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria City rate ruleset
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Local_Area
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.166, 158.4)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (1.116, 72)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.116, 158.4)
diff --git a/kppp/Rules/Austria/Minimum/Long_Distance.rst b/kppp/Rules/Austria/Minimum/Long_Distance.rst
new file mode 100644
index 00000000..0e7ab027
--- /dev/null
+++ b/kppp/Rules/Austria/Minimum/Long_Distance.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria Zone 1 ruleset (50km - 200km)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Zone
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.116, 72)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (1.116, 25)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.116, 72)
diff --git a/kppp/Rules/Austria/Minimum/Makefile.am b/kppp/Rules/Austria/Minimum/Makefile.am
new file mode 100644
index 00000000..f44d49cd
--- /dev/null
+++ b/kppp/Rules/Austria/Minimum/Makefile.am
@@ -0,0 +1,7 @@
+rules_DATA = Local.rst \
+ Long_Distance.rst \
+ Online.rst
+
+rulesdir = $(kde_datadir)/kppp/Rules/Austria/Minimum
+
+EXTRA_DIST = $(rules_DATA)
diff --git a/kppp/Rules/Austria/Minimum/Online.rst b/kppp/Rules/Austria/Minimum/Online.rst
new file mode 100644
index 00000000..19a37dc5
--- /dev/null
+++ b/kppp/Rules/Austria/Minimum/Online.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Austria City rate ruleset (special "online" numbers)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+# Version 2
+# lt. österreichischer Post gelten an Feiertagen anders als auf der
+# PTA Homepage angekündigt der Spar- bzw. Superspartarif. Daher habe ich
+# einige der österreichischen Feiertage eingetragen. Sollte mir einer
+# entgangen sein, so bitte an mich melden.
+#
+# Sollte die Information nicht stimmen, so flames bitte an:
+# marketing@pta.at :-))
+#
+################################################################
+
+name=Austria_Online
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.116, 352)
+
+# tagestarif
+on (monday..friday) between (08:00..12:00) use (1.116, 120)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.116, 352)
+
+
diff --git a/kppp/Rules/Austria/Standard/Local.rst b/kppp/Rules/Austria/Standard/Local.rst
new file mode 100644
index 00000000..d7016382
--- /dev/null
+++ b/kppp/Rules/Austria/Standard/Local.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria City rate ruleset
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Local_Area
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.056, 158.4)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (1.056, 72)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.056, 158.4)
diff --git a/kppp/Rules/Austria/Standard/Long_Distance.rst b/kppp/Rules/Austria/Standard/Long_Distance.rst
new file mode 100644
index 00000000..6ed51595
--- /dev/null
+++ b/kppp/Rules/Austria/Standard/Long_Distance.rst
@@ -0,0 +1,19 @@
+################################################################
+#
+# Austria Zone 1 ruleset (50km - 200km)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+################################################################
+
+name=Austria_Zone
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.056, 72)
+
+# tagestarif
+on (monday..friday) between (08:00..18:00) use (1.056, 25)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.056, 72)
diff --git a/kppp/Rules/Austria/Standard/Makefile.am b/kppp/Rules/Austria/Standard/Makefile.am
new file mode 100644
index 00000000..21e48856
--- /dev/null
+++ b/kppp/Rules/Austria/Standard/Makefile.am
@@ -0,0 +1,8 @@
+rules_DATA = Local.rst \
+ Long_Distance.rst \
+ Online.rst \
+ UTA_easyinternet.rst
+
+rulesdir = $(kde_datadir)/kppp/Rules/Austria/Standard
+
+EXTRA_DIST = $(rules_DATA)
diff --git a/kppp/Rules/Austria/Standard/Online.rst b/kppp/Rules/Austria/Standard/Online.rst
new file mode 100644
index 00000000..35621a02
--- /dev/null
+++ b/kppp/Rules/Austria/Standard/Online.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# Austria City rate ruleset (special "online" numbers)
+#
+# created by Mario Weilguni <mweilguni@sime.com>
+#
+# Version 2
+# lt. österreichischer Post gelten an Feiertagen anders als auf der
+# PTA Homepage angekündigt der Spar- bzw. Superspartarif. Daher habe ich
+# einige der österreichischen Feiertage eingetragen. Sollte mir einer
+# entgangen sein, so bitte an mich melden.
+#
+# Sollte die Information nicht stimmen, so flames bitte an:
+# marketing@pta.at :-))
+#
+################################################################
+
+name=Austria_Online
+currency_symbol=ATS
+currency_position=right
+currency_digits=2
+default=(1.056, 352)
+
+# tagestarif
+on (monday..friday) between (08:00..12:00) use (1.056, 120)
+
+# Feiertage
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (1.056, 352)
+
+
diff --git a/kppp/Rules/Austria/Standard/UTA_easyinternet.rst b/kppp/Rules/Austria/Standard/UTA_easyinternet.rst
new file mode 100644
index 00000000..956933b9
--- /dev/null
+++ b/kppp/Rules/Austria/Standard/UTA_easyinternet.rst
@@ -0,0 +1,50 @@
+############################################################################
+# #
+# Ruleset for UTA Easyinternet #
+# (also applyable for the UTA Internet Unlimited, but I'm an #
+# Easyinternet customer = no basic fee ;-)) #
+# Created on August 26, 2000 according to the latest UTA tarifs #
+# by Patrick Eixelsberger (pat.eix@utanet.at) #
+# #
+# (All following comments are German, because IMHO only Austrians #
+# will be interested in this file and I'm too lazy for English #
+# now ;-) ) #
+# #
+############################################################################
+
+
+name=UTA Easyinternet
+
+# Währungssymbol= ATS für österreichische Schillinge
+# Bitte ändern, wenn der EURO da ist ;-)
+currency_symbol=ATS
+
+currency_position=right
+
+currency_digits=2
+
+per_connection=0.0
+
+minimum_costs=0.0
+
+
+# = Geschäftstarif: ATS 0.33 pro Minute, aber mit sekundengenauer
+# Abrechnung, daher wird jede Sekunde um ATS 0.0055 weitergezählt
+default=(0.0055, 1)
+
+
+# = Freizeittarif: ATS 0.16 pro Minute, aber mit sekundengenauer Abrechnung,
+# daher wird jede Sekunde um ATS 0.002667 weitergezählt
+# Freizeit = Montag-Freitag 18:00-8:00,
+# Samstag 0:00-23:59
+# Sonntag 0:00-23:59
+on (monday..friday) between (18:00..8:00) use (0.002667, 1)
+on (saturday..sunday) between (0:00..23:59) use (0.002667, 1)
+
+# Feiertage, übernommen aus
+# $KDEDIR/share/apps/kppp/Rules/Austria/Standard/Online.rst
+# (von Mario Weilguni <mweilguni@sime.com>)
+on (5/1,12/25,12/26,11/1,easter+1,easter+39,easter+50,1/1,10/26,1/6,12/8) between (00:00..23:59) use (0.002667, 1)
+
+# = wiederum Freizeittarif, siehe oben
+
diff --git a/kppp/Rules/Bangladesh/ATT00007.rst b/kppp/Rules/Bangladesh/ATT00007.rst
new file mode 100644
index 00000000..60af4362
--- /dev/null
+++ b/kppp/Rules/Bangladesh/ATT00007.rst
@@ -0,0 +1,63 @@
+################################################################
+#
+# This RULESET is based on the TEMPLATE file found in
+# /usr/share/apps/kppp/Rules written by
+# Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+#
+################################################################
+
+
+################################################################
+#
+# Agni Systems Limited, Dhaka, Bangladesh (helpdesk@agni.com)
+#
+################################################################
+name=agni
+
+################################################################
+# currency settings
+################################################################
+
+
+currency_symbol=Tk
+
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+# VAT (15%) is included
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# Local call charge (T&T)
+per_connection=1.7
+
+minimum_costs=0.0
+
+# This is the default rule which is used when no other rule
+# applies. The first component "1.15" is the price of one
+# "unit", while "60" is the duration in seconds. It is here
+# only for *.rst file compatibilty.
+default=(1.15, 60)
+
+#
+# THE RULES
+#
+
+# AGNI Systems( http://www.agni.net ) VAT (15%) included
+on (friday) between (0:00..23:59) use (1.15, 60)
+on (saturday..thursday) between (0:00..7:59) use (1.15, 60)
+on (saturday..thursday) between (8:00..17:59) use (2.87, 60)
+on (saturday..thursday) between (18:00..23:59) use (2.3, 60)
+
+#This rules file was written by Ionmon247 sas@startrekmail.com
diff --git a/kppp/Rules/Bangladesh/ATT00010.rst b/kppp/Rules/Bangladesh/ATT00010.rst
new file mode 100644
index 00000000..a3ea71e6
--- /dev/null
+++ b/kppp/Rules/Bangladesh/ATT00010.rst
@@ -0,0 +1,59 @@
+################################################################
+#
+# This ruleset is based on /usr/share/apps/kppp/Rules/TEMPLATE
+# written by
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# Bangladesh Telephone and Telegraph Board
+#
+################################################################
+name=BTTB_Dhaka
+
+################################################################
+# currency settings
+################################################################
+
+# Vurrency symbol for taka
+currency_symbol=Tk
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+
+per_connection=1.7
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+default=(1, 60)
+
+#VAT (15%) INCLUDED
+
+on () between (00:00..05:59) use (0.57, 1)
+on () between (06:00..21:59) use (1.15, 1)
+on () between (22:00..23:59) use (0.57, 1)
+
+#This RST file is written by sas@startrekmail.com
diff --git a/kppp/Rules/Bangladesh/Makefile.am b/kppp/Rules/Bangladesh/Makefile.am
new file mode 100644
index 00000000..27393ea1
--- /dev/null
+++ b/kppp/Rules/Bangladesh/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = ATT00007.rst \
+ ATT00010.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Bangladesh
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Belgium/Belgium_internet_euro.rst b/kppp/Rules/Belgium/Belgium_internet_euro.rst
new file mode 100644
index 00000000..443ff9d3
--- /dev/null
+++ b/kppp/Rules/Belgium/Belgium_internet_euro.rst
@@ -0,0 +1,83 @@
+##########################################################################
+# Ruleset file for the Belgium Internet calls from Belgacom
+# Thers rules apply on on may 23 2001
+#
+# Rules:
+# - Peak hours: 08:00 -> 18:00.
+# - Off-peak : the rest + national holidays.
+# - Entry costs are 2 BEF = 0.0496 EUR
+# - Off-Peak: 40BEF/hour = 0.67 BEF/min = 0.0166 EUR
+# - Peak: 100BEF/hour= 1.67 BEF/min = 0.0414 EUR
+#
+# This file uses EURO as currency unit. The EURO will replace the
+# Belgian Frank as of Januari 1st, 2002
+#
+# Philippe.Faes@rug.ac.be
+# (after the summer of 2003 this addres might have expired, try one of the
+# following: Philippe.Faes@faesvideo.be or Philippe.Faes@faes.net)
+#
+# Thanks to Guy Zelck <guy.zelck@eds.com> for letting me use his 1998 version
+# of this file as a template.
+#########################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+name=Belgium_internet_euro
+
+################################################################
+# currency settings
+################################################################
+
+# define BEF (Begische Frank) to be used as currency
+currency_symbol=EUR
+
+# Position of the currency symbol, default is "right".
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=4
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# I did the rounding as precise as I thought was nescessary, however
+# I don't know the official rates in EURO from Belgacom.
+# I'm affraid this will be resolved pretty soon, cuz Belgacom always
+# raises its rates anyway.
+# (I just like to say HI to all those smart people using Telenet or ADSL)
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0496
+
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+# Let's take the off-peak hour rate as the default.
+default=(0.0166,60)
+
+
+# Peak hours.
+on (monday..friday) between (08:00..18:00) use (0.0414,60)
+
+# Holidays (mm/dd) in '98 have the off-peak rate.
+on (01/01, easter, easter+1, 05/01, easter+39, easter+49,easter+50,07/21,08/15, 11/01, 11/11, 12/25) between () use (0.0166,60)
+#
+# 01/01 Nieuwjaar.
+# easter Pasen
+# easter+1 Paasmaandag
+# 05/01 Feest van de arbeid
+# easter+39 O.H.-Hemelvaart
+# easter+49 Pinksteren
+# easter+50 Pinkstermaandag
+# 07/21 Nationale Feestdag
+# 08/15 O.L.V.-Hemelvaart
+# 11/01 Allerheiligen
+# 11/11 Wapenstilstand 1918
+# 12/25 Kerstmis
diff --git a/kppp/Rules/Belgium/Belgium_internet_frank.rst b/kppp/Rules/Belgium/Belgium_internet_frank.rst
new file mode 100644
index 00000000..e8a5ed56
--- /dev/null
+++ b/kppp/Rules/Belgium/Belgium_internet_frank.rst
@@ -0,0 +1,74 @@
+##########################################################################
+# Ruleset file for the Belgium Internet calls from Belgacom
+# Thers rules apply on on may 23 2001
+#
+# Rules:
+# - Peak hours: 08:00 -> 18:00.
+# - Off-peak : the rest + national holidays.
+# - Entry costs are 2 BEF = 0.0496 EUR
+# - Off-Peak: 40BEF/hour = 0.67 BEF/min = 0.0166 EUR
+# - Peak: 100BEF/hour= 1.67 BEF/min = 0.0414 EUR
+#
+#
+# Philippe.Faes@rug.ac.be
+# (after the summer of 2003 this addres might have expired, try one of the
+# following: Philippe.Faes@faesvideo.be or Philippe.Faes@faes.net)
+#########################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+name=Belgium_internet_frank
+
+################################################################
+# currency settings
+################################################################
+
+# define BEF (Begische Frank) to be used as currency
+# you should use the EURO version of this file, since the Frank won't last
+# much longer :-)
+currency_symbol=BEF
+
+# Position of the currency symbol, default is "right".
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=3
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=2
+
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+# Let's take the off-peak hour rate as the default.
+default=(0.67,60)
+
+
+# Peak hours.
+on (monday..friday) between (08:00..18:00) use (1.67,60)
+
+# Holidays (mm/dd) in '98 have the off-peak rate.
+on (01/01, easter, easter+1, 05/01, easter+39, easter+49, easter+50,07/21,08/15, 11/01, 11/11, 12/25) between () use (0.67,60)
+#
+# 01/01 Nieuwjaar ;Jour de l'An
+# easter Pasen ;Pâques
+# easter+1 Paasmaandag ;Lundi de Pâques
+# 05/01 Feest van de arbeid ;Fête du Travail
+# easter+39 O.H.-Hemelvaart ;Ascension
+# easter+49 Pinksteren ;Pentecôte
+# easter+50 Pinkstermaandag ;Lundi de Pentecôte
+# 07/21 Nationale Feestdag ;Fête Nationale
+# 08/15 O.L.V.-Hemelvaart ;Assomption
+# 11/01 Allerheiligen ;Toussaint
+# 11/11 Wapenstilstand 1918 ;Armistice
+# 12/25 Kerstmis ;NoEl
diff --git a/kppp/Rules/Belgium/Belgium_interzonal.rst b/kppp/Rules/Belgium/Belgium_interzonal.rst
new file mode 100644
index 00000000..d58be84a
--- /dev/null
+++ b/kppp/Rules/Belgium/Belgium_interzonal.rst
@@ -0,0 +1,81 @@
+##########################################################################
+# Ruleset file for the Belgium Interzonal calls applicable since 98/03/15.
+#
+# Rules:
+# - Peak hours: 08:00 -> 18:00.
+# - Off-peak : the rest + national holidays.
+# - Minimum entry costs (flat_init_costs) are halfed when off-peak.
+# (how to implement?)
+# - Calls longer than 10' have 30% reduction when off-peak to a bbs.
+# Calls longer than 10' have 50% reduction when off-peak to the net!
+# (how to implement?)
+#
+# guy.zelck@eds.com
+################################################################
+
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+name=Belgium_interzonal
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol, default = "$".
+# define Bf (Begische Frank) to be used as currency
+currency_symbol=Bf
+
+# Position of the currency symbol, default is "right".
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=3
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# This is the minimum time slice you allways have to pay.
+# It has priority over the default rule.
+# Trouble here is that during off-peak hours it's half the price!
+# How can this be implemented?
+flat_init_costs=(7,60)
+
+# Let's take the off-peak hour rate as the default.
+default=(3.5,60)
+
+# During off-peak hours when a call takes > 10' you have 30% reduction.
+# ?
+
+# Peak hours.
+on (monday..friday) between (08:00..18:00) use (7,60)
+
+# Holidays (mm/dd) in '98 have the off-peak rate.
+on (01/01, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 07/21, 08/15, 11/01, 11/11, 12/25) between () use (1,60)
+#
+# 01/01 Nieuwjaar.
+# easter Pasen
+# easter+1 Paasmaandag
+# 05/01 Feest van de arbeid
+# easter+39 O.H.-Hemelvaart
+# easter+49 Pinksteren
+# easter+50 Pinkstermaandag
+# 07/21 Nationale Feestdag
+# 08/15 O.L.V.-Hemelvaart
+# 11/01 Allerheiligen
+# 11/11 Wapenstilstand 1918
+# 12/25 Kerstmis
diff --git a/kppp/Rules/Belgium/Belgium_zonal.rst b/kppp/Rules/Belgium/Belgium_zonal.rst
new file mode 100644
index 00000000..fa63e3ee
--- /dev/null
+++ b/kppp/Rules/Belgium/Belgium_zonal.rst
@@ -0,0 +1,81 @@
+##########################################################################
+# Ruleset file for the Belgium Interzonal calls applicable since 98/03/15.
+#
+# Rules:
+# - Peak hours: 08:00 -> 18:00.
+# - Off-peak : the rest + national holidays.
+# - Minimum entry costs (flat_init_costs) are halfed when off-peak.
+# (how to implement?)
+# - Calls longer than 10' have 30% reduction when off-peak to a bbs.
+# Calls longer than 10' have 50% reduction when off-peak to the net!
+# (how to implement?)
+#
+# Guy Zelck, guy.zelck@eds.com
+################################################################
+
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+name=Belgium_zonal
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol, default = "$".
+# define Bf (Begische Frank) to be used as currency
+currency_symbol=Bf
+
+# Position of the currency symbol, default is "right".
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=3
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# This is the minimum time slice you allways have to pay, here its 180 sec.
+# It has priority over the default rule.
+# Trouble here is that during peak-hours it's 6 Bf, during off-peak hours it's 3 Bf.
+# How can this be implemented?
+flat_init_costs=(6,180)
+
+# Let's take the off-peak hour rate as the default.
+default=(1,60)
+
+# During off-peak hours when a call takes > 10' you have 30% reduction.
+# ?
+
+# Peak hours.
+on (monday..friday) between (08:00..18:00) use (2,60)
+
+# Holidays (mm/dd) in '98 have the off-peak rate.
+on (01/01, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 07/21, 08/15, 11/01, 11/11, 12/25) between () use (1,60)
+
+# 01/01 Nieuwjaar.
+# easter Pasen
+# easter+1 Paasmaandag
+# 05/01 Feest van de arbeid
+# easter+39 O.H.-Hemelvaart
+# easter+49 Pinksteren
+# easter+50 Pinkstermaandag
+# 07/21 Nationale Feestdag
+# 08/15 O.L.V.-Hemelvaart
+# 11/01 Allerheiligen
+# 11/11 Wapenstilstand 1918
+# 12/25 Kerstmis
diff --git a/kppp/Rules/Belgium/Makefile.am b/kppp/Rules/Belgium/Makefile.am
new file mode 100644
index 00000000..ede4299c
--- /dev/null
+++ b/kppp/Rules/Belgium/Makefile.am
@@ -0,0 +1,6 @@
+rules_DATA = Belgium_interzonal.rst Belgium_zonal.rst \
+ Belgium_internet_frank.rst Belgium_internet_euro.rst
+
+rulesdir = $(kde_datadir)/kppp/Rules/Belgium
+
+EXTRA_DIST = $(rules_DATA)
diff --git a/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_home_-_bez_impulsa.rst b/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_home_-_bez_impulsa.rst
new file mode 100644
index 00000000..ef7259d5
--- /dev/null
+++ b/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_home_-_bez_impulsa.rst
@@ -0,0 +1,67 @@
+ ################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=BiHnet_-_home_-_bez_impulsa.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=KM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(7,36000)
+#default=(0.0151, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.0121, 60)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on () between (1:00..6:59) use (0,0)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+#on (monday..saturday) between (0:00..6:59) use (0.1677, 360)
+#on (monday..saturday) between (7:00..15:59) use (0.1677, 180)
+#on (monday..saturday) between (16:00..21:59) use (0.1677, 240)
+#on (monday..saturday) between (22:00..23:59) use (0.1677, 360)
+#on (sunday) between () use (0.1677, 360)
+
+# Sluzbeni praznici, najjeftinija tarifa
+# 1.1. Nova Godina
+# Easter - Uskrs
+# 1.5. -
+# 30.5. - Dan drzavnosti
+# 22.6. - Dan borbe protiv fasizma
+# 4.8. - Dan domovinske zahvalnosti
+# 15.8. - Velika Gospa
+# 1.11. - Dan svih svetih
+# 25.12. - Bozic
+#on (01/01, easter, 05/01, 05/30, 06/22, 08/04, 08/15, 11/01, 12/25) between () use (0.1677,360)
+
diff --git a/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_student_-_bez_impulsa.rst b/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_student_-_bez_impulsa.rst
new file mode 100644
index 00000000..d8b397a4
--- /dev/null
+++ b/kppp/Rules/Bosnia_and_Herzegovina/BiHnet_-_student_-_bez_impulsa.rst
@@ -0,0 +1,67 @@
+ ################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=BiHnet_-_student_-_bez_impulsa.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=KM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(5,54000)
+#default=(0.0097, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.0099, 60)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on () between (1:00..6:59) use (0, 60)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+#on (monday..saturday) between (0:00..6:59) use (0.1677, 360)
+#on (monday..saturday) between (7:00..15:59) use (0.1677, 180)
+#on (monday..saturday) between (16:00..21:59) use (0.1677, 240)
+#on (monday..saturday) between (22:00..23:59) use (0.1677, 360)
+#on (sunday) between () use (0.1677, 360)
+
+# Sluzbeni praznici, najjeftinija tarifa
+# 1.1. Nova Godina
+# Easter - Uskrs
+# 1.5. -
+# 30.5. - Dan drzavnosti
+# 22.6. - Dan borbe protiv fasizma
+# 4.8. - Dan domovinske zahvalnosti
+# 15.8. - Velika Gospa
+# 1.11. - Dan svih svetih
+# 25.12. - Bozic
+#on (01/01, easter, 05/01, 05/30, 06/22, 08/04, 08/15, 11/01, 12/25) between () use (0.1677,360)
+
diff --git a/kppp/Rules/Bosnia_and_Herzegovina/Makefile.am b/kppp/Rules/Bosnia_and_Herzegovina/Makefile.am
new file mode 100644
index 00000000..6a89fb59
--- /dev/null
+++ b/kppp/Rules/Bosnia_and_Herzegovina/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = BiHnet_-_home_-_bez_impulsa.rst \
+ BiHnet_-_student_-_bez_impulsa.rst \
+ SmartNet_PERSONAL_bez_impulsa.rst \
+ samo_impulsi.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Bosnia_and_Herzegovina
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Bosnia_and_Herzegovina/SmartNet_PERSONAL_bez_impulsa.rst b/kppp/Rules/Bosnia_and_Herzegovina/SmartNet_PERSONAL_bez_impulsa.rst
new file mode 100644
index 00000000..30122206
--- /dev/null
+++ b/kppp/Rules/Bosnia_and_Herzegovina/SmartNet_PERSONAL_bez_impulsa.rst
@@ -0,0 +1,65 @@
+ ################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=SmartNet_S-NET-01_bez_impulsa.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=KM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(18,90000)
+#default=(0.0151, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.007, 36)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+#on (monday..saturday) between (0:00..6:59) use (0.1677, 360)
+#on (monday..saturday) between (7:00..15:59) use (0.1677, 180)
+#on (monday..saturday) between (16:00..21:59) use (0.1677, 240)
+#on (monday..saturday) between (22:00..23:59) use (0.1677, 360)
+#on (sunday) between () use (0.1677, 360)
+
+# Sluzbeni praznici, najjeftinija tarifa
+# 1.1. Nova Godina
+# Easter - Uskrs
+# 1.5. -
+# 30.5. - Dan drzavnosti
+# 22.6. - Dan borbe protiv fasizma
+# 4.8. - Dan domovinske zahvalnosti
+# 15.8. - Velika Gospa
+# 1.11. - Dan svih svetih
+# 25.12. - Bozic
+#on (01/01, easter, 05/01, 05/30, 06/22, 08/04, 08/15, 11/01, 12/25) between () use (0.1677,360)
+
diff --git a/kppp/Rules/Bosnia_and_Herzegovina/samo_impulsi.rst b/kppp/Rules/Bosnia_and_Herzegovina/samo_impulsi.rst
new file mode 100644
index 00000000..ac60aec0
--- /dev/null
+++ b/kppp/Rules/Bosnia_and_Herzegovina/samo_impulsi.rst
@@ -0,0 +1,66 @@
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=samo_impulsi.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=KM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.0180,120)
+#default=(0.0097, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.0049, 60)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+on (monday..saturday) between (7:00..18:59) use (0.0097, 60)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+#on (monday..saturday) between (0:00..6:59) use (0.1677, 360)
+#on (monday..saturday) between (7:00..15:59) use (0.1677, 180)
+#on (monday..saturday) between (16:00..21:59) use (0.1677, 240)
+#on (monday..saturday) between (22:00..23:59) use (0.1677, 360)
+#on (sunday) between () use (0.1677, 360)
+
+# Sluzbeni praznici, najjeftinija tarifa
+# 1.1. Nova Godina
+# Easter - Uskrs
+# 1.5. -
+# 30.5. - Dan drzavnosti
+# 22.6. - Dan borbe protiv fasizma
+# 4.8. - Dan domovinske zahvalnosti
+# 15.8. - Velika Gospa
+# 1.11. - Dan svih svetih
+# 25.12. - Bozic
+#on (01/01, easter, 05/01, 05/30, 06/22, 08/04, 08/15, 11/01, 12/25) between () use (0.1677,360)
+
diff --git a/kppp/Rules/Brasil/Brasil.rst b/kppp/Rules/Brasil/Brasil.rst
new file mode 100644
index 00000000..238af4ef
--- /dev/null
+++ b/kppp/Rules/Brasil/Brasil.rst
@@ -0,0 +1,21 @@
+# I don't know if it's the same rule for the rest of Brazil!
+# Cobranca telefonica da Telebahia, Brasil. Nao sei se a cobranca e a mesma
+# no resto do Brasil. Nao estranho o custe de 0 aos sabados e domingos.
+# Nestes horarios, cobra-se apenas um pulso (8 centavos) por ligacao. Como
+# este valor eh o custo minimo, ele nao cobra nada pelo resto da ligacao.
+#
+# Marcus Brito
+# Mailto: sadpazu@base.com.br
+
+name=Telebahia
+currency_position=right
+currency_digits=3
+per_connection=0.0
+minimum_costs=0.08
+default=(0.08, 240)
+
+on (monday..friday) between (0:00..05:59) use (0.0, 100)
+on (saturday) between (14:00..23:59) use (0.0, 100)
+on (sunday) between (00:00..23:59) use (0.0, 100)
+
+
diff --git a/kppp/Rules/Brasil/Brasil_Ligbr.rst b/kppp/Rules/Brasil/Brasil_Ligbr.rst
new file mode 100644
index 00000000..5817fada
--- /dev/null
+++ b/kppp/Rules/Brasil/Brasil_Ligbr.rst
@@ -0,0 +1,19 @@
+# Suitable for State of Rio de Janeiro - Brazil
+#
+# Cobranca aplicada pela Intelig para conexões via Ligbr em todo o território
+# nacional. Os valores foram obtidos na pagina da Ligbr (www.ligbr.com.br) e
+# sao de 27 de março de 2002.
+#
+# Renato G. dos Santos
+# mailto:linuxiado@rba.omnqf.nom.br
+
+name=Ligbr
+currency_symbol=R$
+currency_position=left
+currency_digits=5
+
+per_connection=0.07000
+#flat_init_costs=(0.08571,240)
+minimum_costs=0.07000
+default=(0.07000, 60)
+
diff --git a/kppp/Rules/Brasil/Makefile.am b/kppp/Rules/Brasil/Makefile.am
new file mode 100644
index 00000000..f54b861b
--- /dev/null
+++ b/kppp/Rules/Brasil/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = Brasil.rst \
+ SaoPaulo.rst \
+ Brasil_Ligbr.rst \
+ Rio_de_Janeiro.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Brasil
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Brasil/Rio_de_Janeiro.rst b/kppp/Rules/Brasil/Rio_de_Janeiro.rst
new file mode 100644
index 00000000..09ae69eb
--- /dev/null
+++ b/kppp/Rules/Brasil/Rio_de_Janeiro.rst
@@ -0,0 +1,33 @@
+# Suitable for State of Rio de Janeiro - Brazil
+#
+# Cobranca aplicada pela Telemar no Rio de Janeiro. Os valores foram obtidos
+# na pagina da Telemar (www.telemar.com.br) e sao de 27 de março de 2002.
+#
+# Renato G. dos Santos
+# mailto:linuxiado@rba.omnqf.nom.br
+#
+
+name=Telemar
+currency_symbol=R$
+currency_position=left
+currency_digits=5
+
+per_connection=0.09258
+#flat_init_costs=(0.09258,240)
+minimum_costs=0.09258
+default=(0.09258, 240)
+
+on (monday..saturday) between (0:00..06:00) use (0.00000, 100)
+on (saturday) between (14:00..23:59) use (0.00000, 100)
+on (sunday) between () use (0.00000, 100)
+
+# Feriados nacionais:
+# Ano novo
+# Tiradentes
+# Dia do trabalho
+# Proclamacao da Independencia
+# Padroeira do Brasil
+# Proclamacao da Republica
+# Natal
+
+on (01/01, 04/21, 05/01, 09/07, 10/12, 11/15, 12/25) between () use (0.00000,100)
diff --git a/kppp/Rules/Brasil/SaoPaulo.rst b/kppp/Rules/Brasil/SaoPaulo.rst
new file mode 100644
index 00000000..748f653e
--- /dev/null
+++ b/kppp/Rules/Brasil/SaoPaulo.rst
@@ -0,0 +1,33 @@
+# Suitable for State of Sao Paulo - Brazil
+#
+# Cobranca aplicada pela Telefonica em Sao Paulo. Os valores foram
+# obtidos na pagina da Telefonica (www.telefonica.net.br) e sao
+# de 13 de abril de 2000.
+#
+# Ricardo Biloti
+# mailto:biloti@ime.unicamp.br
+#
+
+name=Telefonica
+currency_position=left
+currency_digits=5
+
+per_connection=0.08571
+#flat_init_costs=(0.08571,240)
+minimum_costs=0.08571
+default=(0.08571, 240)
+
+on (monday..saturday) between (0:00..06:00) use (0.00000, 100)
+on (saturday) between (14:00..23:59) use (0.00000, 100)
+on (sunday) between () use (0.00000, 100)
+
+# Feriados nacionais:
+# Ano novo
+# Tiradentes
+# Dia do trabalho
+# Proclamacao da Independencia
+# Padroeira do Brasil
+# Proclamacao da Republica
+# Natal
+on (01/01, 04/21, 05/01, 09/07, 10/12, 11/15, 12/25) between () use (0.00000,100)
+
diff --git a/kppp/Rules/Croatia/CARNet.rst b/kppp/Rules/Croatia/CARNet.rst
new file mode 100644
index 00000000..e67e4e6b
--- /dev/null
+++ b/kppp/Rules/Croatia/CARNet.rst
@@ -0,0 +1,67 @@
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+# Updated by zapov (zapov at yahoo com)
+#
+################################################################
+name=CARNet.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=HRK
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.061, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.061, 60)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+on (monday..saturday) between (0:00..6:59) use (0.0305, 60)
+on (monday..saturday) between (7:00..18:59) use (0.061, 60)
+on (monday..saturday) between (19:00..23:59) use (0.0305, 60)
+on (sunday) between () use (0.0305, 60)
+
+# Sluzbeni praznici, najjeftinija tarifa
+# 1.1. Nova Godina
+# Easter - Uskrs
+# 6.1. - Tri kralja
+# 1.5. - Medjunarodni praznik rada
+# Easter+60 - Tijelovo
+# 22.6. - Dan antifasisticke borbe
+# 25.6. - Dan drzavnosti
+# 5.8. - Dan domovinske zahvalnosti
+# 15.8. - Velika Gospa
+# 8.10. - Dan neovisnosti
+# 1.11. - Svi sveti
+# 25.12. - Bozic
+# 26.12. - Dan iza :)
+on (01/01, 01/06, easter+1, 05/1, easter+60, 06/22, 06/25, 08/05, 08/15, 11/01, 12/25, 12/26) between () use (0.0305, 60)
diff --git a/kppp/Rules/Croatia/Makefile.am b/kppp/Rules/Croatia/Makefile.am
new file mode 100644
index 00000000..47e048c8
--- /dev/null
+++ b/kppp/Rules/Croatia/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = CARNet.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Croatia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Business_Internet.rst b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Business_Internet.rst
new file mode 100644
index 00000000..a887fc60
--- /dev/null
+++ b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Business_Internet.rst
@@ -0,0 +1,23 @@
+#***********************************************************#
+# Czech Telecom rate ruleset #
+# Internet Business 2004 #
+# Created 2004/11/01 Jan Klepek <j.klepek@centrum.cz> #
+#***********************************************************#
+
+name=Czech_Telecom_Internet_2004_Business_Ineternet
+currency_symbol=Kc
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+default=(1.40, 60)
+
+on (monday..friday) between (6:00..17:59) use (1.51, 60)
+on (monday..friday) between (6:00..17:59) use (0.84, 60, 600)
+on (monday..friday) between (18:00..5:59) use (0.70, 60)
+on (monday..friday) between (18:00..5:59) use (0.33, 60, 600)
+on (saturday..sunday) between () use (0.70, 60)
+on (saturday..sunday) between () use (0.33, 60, 600)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.70, 60)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.33, 60, 600)
diff --git a/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Home_Internet.rst b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Home_Internet.rst
new file mode 100644
index 00000000..8b6652e3
--- /dev/null
+++ b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_Home_Internet.rst
@@ -0,0 +1,23 @@
+#***********************************************************#
+# Czech Telecom rate ruleset #
+# Internet Home 2004 #
+# Created 2004/11/01 Jan Klepek <j.klepek@centrum.cz> #
+#***********************************************************#
+
+name=Czech_Telecom_Internet_2004_Home_Internet
+currency_symbol=Kc
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+default=(1.40, 60)
+
+on (monday..friday) between (6:00..17:59) use (1.62, 60)
+on (monday..friday) between (6:00..17:59) use (0.87, 60, 600)
+on (monday..friday) between (18:00..5:59) use (0.63, 60)
+on (monday..friday) between (18:00..5:59) use (0.26, 60, 600)
+on (saturday..sunday) between () use (0.63, 60)
+on (saturday..sunday) between () use (0.26, 60, 600)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.63, 60)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.26, 60, 600)
diff --git a/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_180_+_660.rst b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_180_+_660.rst
new file mode 100644
index 00000000..55bcd9dd
--- /dev/null
+++ b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_180_+_660.rst
@@ -0,0 +1,23 @@
+#***********************************************************#
+# Czech Telecom rate ruleset #
+# Telefon_a_internet_180_+_660 2004 #
+# Created 2004/11/01 Jan Klepek <j.klepek@centrum.cz> #
+#***********************************************************#
+
+name=Czech_Telecom_Internet_2004_Telefon_a_internet_180_+_660
+currency_symbol=Kc
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+default=(1.40, 60)
+
+on (monday..friday) between (6:00..17:59) use (1.62, 60)
+on (monday..friday) between (6:00..17:59) use (0.86, 60, 600)
+on (monday..friday) between (18:00..5:59) use (0.63, 60)
+on (monday..friday) between (18:00..5:59) use (0.26, 60, 600)
+on (saturday..sunday) between () use (0.63, 60)
+on (saturday..sunday) between () use (0.26, 60, 600)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.63, 60)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.26, 60, 600)
diff --git a/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_90_+_300.rst b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_90_+_300.rst
new file mode 100644
index 00000000..4db34b8c
--- /dev/null
+++ b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_a_Internet_90_+_300.rst
@@ -0,0 +1,23 @@
+#***********************************************************#
+# Czech Telecom rate ruleset #
+# Telefon_a_internet_90_+_300 2004 #
+# Created 2004/11/01 Jan Klepek <j.klepek@centrum.cz> #
+#***********************************************************#
+
+name=Czech_Telecom_Internet_2004_Telefon_a_internet_90_+_300
+currency_symbol=Kc
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+default=(1.40, 60)
+
+on (monday..friday) between (6:00..17:59) use (1.62, 60)
+on (monday..friday) between (6:00..17:59) use (0.86, 60, 600)
+on (monday..friday) between (18:00..5:59) use (0.63, 60)
+on (monday..friday) between (18:00..5:59) use (0.29, 60, 600)
+on (saturday..sunday) between () use (0.63, 60)
+on (saturday..sunday) between () use (0.29, 60, 600)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.63, 60)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.29, 60, 600)
diff --git a/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_universal.rst b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_universal.rst
new file mode 100644
index 00000000..5504fc94
--- /dev/null
+++ b/kppp/Rules/Czechia/Czech_Telecom_Internet_2004_telefon_universal.rst
@@ -0,0 +1,23 @@
+#***********************************************************#
+# Czech Telecom rate ruleset #
+# Telefon universal 2004 #
+# Created 2004/11/01 Jan Klepek <j.klepek@centrum.cz> #
+#***********************************************************#
+
+name=Czech_Telecom_Internet_2004_Telefon_universal
+currency_symbol=Kc
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+default=(1.40, 60)
+
+on (monday..friday) between (6:00..17:59) use (1.62, 60)
+on (monday..friday) between (6:00..17:59) use (0.86, 60, 600)
+on (monday..friday) between (18:00..5:59) use (0.63, 60)
+on (monday..friday) between (18:00..5:59) use (0.27, 60, 600)
+on (saturday..sunday) between () use (0.63, 60)
+on (saturday..sunday) between () use (0.27, 60, 600)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.63, 60)
+on (01/01, 04/20, 04/21, 05/01, 05/08, 07/05, 07/06, 09/28, 10/28, 11/17, 12/24..12/26) between () use (0.27, 60, 600)
diff --git a/kppp/Rules/Czechia/Makefile.am b/kppp/Rules/Czechia/Makefile.am
new file mode 100644
index 00000000..dc6fedca
--- /dev/null
+++ b/kppp/Rules/Czechia/Makefile.am
@@ -0,0 +1,9 @@
+pkg_DATA = Czech_Telecom_Internet_2004_telefon_a_Internet_180_+_660.rst \
+ Czech_Telecom_Internet_2004_Business_Internet.rst \
+ Czech_Telecom_Internet_2004_telefon_a_Internet_90_+_300.rst \
+ Czech_Telecom_Internet_2004_Home_Internet.rst \
+ Czech_Telecom_Internet_2004_telefon_universal.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Czechia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Denmark/12Move_Analog.rst b/kppp/Rules/Denmark/12Move_Analog.rst
new file mode 100644
index 00000000..f0fcc1d8
--- /dev/null
+++ b/kppp/Rules/Denmark/12Move_Analog.rst
@@ -0,0 +1,51 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : 12Move (Worldonline+Shell)
+# PRODUCT : N/A
+# CURRENCY : DKR
+# PHONENO : 1049 3883 3883
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.12move.dk/content/content1.html
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_12MOVE_ANALOG
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.15 DKR
+# Minimum expense: 0.00 DKR
+# Additional timeexpense (25sec) = 0.055kr (max)
+#
+# Peak hours: 0.22 DKR/min ( 06:00-18:59 on weekdays )
+# Off-peak hours: 0.11 DKR/min ( 19:00-23:59 on weekdays,
+# 06:00-23:59 on Saturdays and Sundays )
+# Night rates: 0.06 DKR/min ( 00:00-05:59 all days )
+# Time rebate: all rates are reduced to 50% after 1 hour
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.15
+ minimum_costs=0.055
+
+# OFF-PEAK and OFFPEAK +1 hour
+ default=(0.001833, 1)
+ default=(0.000917, 1, 3600)
+
+# PEAK and PEAK +1 hour
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1)
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1, 3600)
+
+# NIGHT and NIGHT +1 hour
+ on (monday..sunday) between (00:00..05:59) use (0.001000, 1)
+ on (monday..sunday) between (00:00..05:59) use (0.000500, 1, 3600)
+
diff --git a/kppp/Rules/Denmark/12Move_ISDN.rst b/kppp/Rules/Denmark/12Move_ISDN.rst
new file mode 100644
index 00000000..4bd59ed8
--- /dev/null
+++ b/kppp/Rules/Denmark/12Move_ISDN.rst
@@ -0,0 +1,52 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : 12Move (Worldonline+Shell)
+# PRODUCT : N/A
+# CURRENCY : DKR
+# PHONENO : 1049 3883 3883
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.12move.dk/content/content1.html
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_12MOVE_ISDN
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.10 DKR
+# Minimum expense: 0.00 DKR
+# Additional timeexpense (3sec) = 0.011kr (max)
+#
+# Peak hours: 0.22 DKR/min ( 06:00-18:59 on weekdays )
+# Off-peak hours: 0.11 DKR/min ( 19:00-23:59 on weekdays,
+# 06:00-23:59 on Saturdays and Sundays )
+# Night rates: 0.06 DKR/min ( 00:00-05:59 all days )
+# Time rebate: all rates are reduced to 50% after 1 hour
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+
+# INITIAL EXPENSE
+ per_connection=0.15
+ minimum_costs=0.011
+
+# OFF-PEAK and OFFPEAK +1 hour
+ default=(0.001833, 1)
+ default=(0.000917, 1, 3600)
+
+# PEAK and PEAK +1 hour
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1)
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1, 3600)
+
+# NIGHT and NIGHT +1 hour
+ on (monday..sunday) between (00:00..05:59) use (0.001000, 1)
+ on (monday..sunday) between (00:00..05:59) use (0.000500, 1, 3600)
+
diff --git a/kppp/Rules/Denmark/Cybercity_Friabonnement.rst b/kppp/Rules/Denmark/Cybercity_Friabonnement.rst
new file mode 100644
index 00000000..ea4022bf
--- /dev/null
+++ b/kppp/Rules/Denmark/Cybercity_Friabonnement.rst
@@ -0,0 +1,42 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : Cybercity
+# PRODUCT : Friabonnement
+# CURRENCY : DKR
+# PHONENO : 1033 ?
+# AUTHOR : Michael Falk Hansen
+# SOURCE : http://www.cybercity.dk/produkter/privat/friabonnement/
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_CYBERCITY_FRI
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: Septmeber 14, 2002
+# Initial expense: 0.25 DKR
+# Minimum expense: 0.25 DKR
+#
+# Peak hours: 0.22 DKR/min ( 08:00-19:00 on weekdays )
+# Off-peak hours: 0.11 DKR/min ( 19:00-08:00 on weekdays,
+# all Saturday and Sunday )
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.25
+ minimum_costs=0.25
+
+# OFF-PEAK
+ default=(0.0018333, 1)
+ default=(0.0018333, 1, 3600)
+
+# PEAK
+ on (monday..friday) between (8:00..18:59) use (0.00366667, 1)
+
diff --git a/kppp/Rules/Denmark/Get2net_Betaling.rst b/kppp/Rules/Denmark/Get2net_Betaling.rst
new file mode 100644
index 00000000..eab3e262
--- /dev/null
+++ b/kppp/Rules/Denmark/Get2net_Betaling.rst
@@ -0,0 +1,56 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : Get2net
+# PRODUCT : Betaling
+# CURRENCY : DKR
+# PHONENO : 1001 ?
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.get2net.dk/get2net/plsql/PackageSupport.PageSupportMain?i_Function=DISPLAYPAGE&i_ID=takster-betaling
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_GET2NET_BETAL
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.25 DKR
+# Minimum expense: 0.00 DKR
+#
+# Peak hours: 0.27 DKR/min ( 08:00-16:00 on weekdays )
+# Reduced peak hours: 0.20 DKR/min ( 16:00-19:00 on weekdays,
+# 08:00-16:00 on Saturdays )
+# Off-peak hours: 0.10 DKR/min ( 19:00-08:00 on weekdays,
+# 16:00-00:00 on Saturdays,
+# all Sunday )
+# Time rebate: Off-peak rates are reduced to 0.08DKR after 1 hour
+#
+### REBATES ######################################################
+# (not included in cost-calculations!)
+# Dialup attempts shorter than 20s are free (not for ISDN)
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.25
+ minimum_costs=0.00
+
+# OFF-PEAK and OFF-PEAK +1 hour
+ default=(0.001667, 1)
+ default=(0.001333, 1, 3600)
+
+# PEAK
+ on (monday..friday) between (8:00..15:59) use (0.004500, 1)
+
+# REDUCED PEAK
+ on (monday..friday) between (16:00..18:59) use (0.003333, 1)
+ on (saturday..saturday) between (8:00..15:59) use (0.003333, 1)
+
+
diff --git a/kppp/Rules/Denmark/Get2net_Gratis.rst b/kppp/Rules/Denmark/Get2net_Gratis.rst
new file mode 100644
index 00000000..fda044e3
--- /dev/null
+++ b/kppp/Rules/Denmark/Get2net_Gratis.rst
@@ -0,0 +1,48 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : Get2net
+# PRODUCT : Gratis
+# CURRENCY : DKR
+# PHONENO : 1001 ?
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.get2net.dk/get2net/plsql/PackageSupport.PageSupportMain?i_Function=DISPLAYPAGE&i_ID=takster-gratis
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_GET2NET_GRATIS
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.25 DKR
+# Minimum expense: 0.00 DKR
+#
+# Peak hours: 0.27 DKR/min ( 08:00-19:00 on weekdays )
+# Off-peak hours: 0.135 DKR/min ( 19:00-08:00 on weekdays,
+# all Saturday and Sunday )
+# Time rebate: Off-peak rates are reduced to 0.10DKR after 1 hour
+#
+### NOTES ########################################################
+# (not included in cost-calculations!)
+# Dialup attempts shorter than 20s are free (not for ISDN)
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.25
+ minimum_costs=0.00
+
+# OFF-PEAK
+ default=(0.002250, 1)
+ default=(0.001667, 1, 3600)
+
+# PEAK
+ on (monday..friday) between (8:00..18:59) use (0.004500, 1)
+
diff --git a/kppp/Rules/Denmark/Makefile.am b/kppp/Rules/Denmark/Makefile.am
new file mode 100644
index 00000000..90477080
--- /dev/null
+++ b/kppp/Rules/Denmark/Makefile.am
@@ -0,0 +1,10 @@
+pkg_DATA = 12Move_Analog.rst 12Move_ISDN.rst \
+ Cybercity_Friabonnement.rst \
+ Get2net_Betaling.rst Get2net_Gratis.rst \
+ Mobilix_Wanadoo.rst \
+ Teledanmark_Basis.rst Teledanmark_Favoritinternet.rst \
+ Worldonline-Analog.rst Worldonline-ISDN.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Denmark
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Denmark/Mobilix_Wanadoo.rst b/kppp/Rules/Denmark/Mobilix_Wanadoo.rst
new file mode 100644
index 00000000..6537a6cc
--- /dev/null
+++ b/kppp/Rules/Denmark/Mobilix_Wanadoo.rst
@@ -0,0 +1,62 @@
+################################################################
+# Danish Mobilix Wanadoo internet rate ruleset per 2000.1.1
+#
+# by Jens Svalgaard Frederiksen <svalle@imada.sdu.dk>
+################################################################
+
+name=Mobilix_Wanadoo
+currency_symbol=DKK
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.25
+
+# Cheap rate:
+default=(0.010, 6)
+default=(0.008, 6, 3600)
+# Note: For Night rate at 0.07 DKK/minute look at the end
+
+# Weekday rates:
+on (monday..friday) between (08:00..16:00) use (0.027, 6)
+#on (monday..friday) between (08:00..16:00) use (0.027, 6)
+#on (monday..friday) between (16:00..19:00) use (0.018, 6)
+
+# Saturday rates:
+on (saturday) between (08:00..19:00) use (0.018, 6)
+
+# Sunday rates:
+# (same as cheap rate)
+
+# National holidays:
+
+# Newyear (nytårsdag)
+on (01/01) between () use (0.010, 6)
+on (01/01) between () use (0.008, 6, 3600)
+# Easter (påske)
+on (easter -3) between () use (0.010, 6)
+on (easter -3) between () use (0.008, 6, 3600)
+on (easter -2) between () use (0.010, 6)
+on (easter -2) between () use (0.008, 6, 3600)
+on (easter +1) between () use (0.010, 6)
+on (easter +1) between () use (0.008, 6, 3600)
+# Store bededag
+on (easter+26) between () use (0.010, 6)
+on (easter+26) between () use (0.008, 6, 3600)
+# Kristi himmelfart
+on (easter+39) between () use (0.010, 6)
+on (easter+39) between () use (0.008, 6, 3600)
+# Pinse
+on (easter+50) between () use (0.010, 6)
+on (easter+50) between () use (0.008, 6, 3600)
+# Christmas
+on (12/25) between () use (0.010, 6)
+on (12/25) between () use (0.008, 6, 3600)
+on (12/26) between () use (0.010, 6)
+on (12/26) between () use (0.008, 6, 3600)
+
+# Night rate
+on () between (00:00..08:00) use (0.007, 6)
diff --git a/kppp/Rules/Denmark/Teledanmark_Basis.rst b/kppp/Rules/Denmark/Teledanmark_Basis.rst
new file mode 100644
index 00000000..b4eaca0a
--- /dev/null
+++ b/kppp/Rules/Denmark/Teledanmark_Basis.rst
@@ -0,0 +1,42 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : Teledanmark
+# PRODUCT : Basis
+# CURRENCY : DKR
+# PHONENO : 16101
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.opasia.dk/kundeservice/abonnement/info_basis.shtml
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_TDK_BASIS
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.25 DKR
+# Minimum expense: 0.00 DKR
+#
+# Peak hours: 0.29 DKR/min ( 08:00-19:30 on weekdays + Saturday )
+# Off-peak hours: 0.145 DKR/min ( 19:30-08:00 on weekdays + Saturdays,
+# 00:00-24:00 on Sundays )
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.25
+ minimum_costs=0.00
+
+# OFF-PEAK
+ default=(0.002417, 1)
+
+# PEAK
+ on (monday..saturday) between (8:00..19:29) use (0.004833, 1)
+
diff --git a/kppp/Rules/Denmark/Teledanmark_Favoritinternet.rst b/kppp/Rules/Denmark/Teledanmark_Favoritinternet.rst
new file mode 100644
index 00000000..830c3725
--- /dev/null
+++ b/kppp/Rules/Denmark/Teledanmark_Favoritinternet.rst
@@ -0,0 +1,85 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : Teledanmark
+# PRODUCT : Favoritinternet
+# CURRENCY : DKR
+# PHONENO : 16110
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.favoritinternet.dk
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_TDK_FAVORIT
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.25 DKR
+# Minimum expense: 0.00 DKR
+#
+# Peak hours: 0.28 DKR/min ( 08:00-16:00 on weekdays )
+# Reduced peak hours: 0.20 DKR/min ( 16:00-19:30 on weekdays,
+# 08:00-16:00 on Saturdays )
+# Off-peak hours: 0.10 DKR/min ( 19:30-08:00 on weekdays,
+# 16:00-00:00 on Saturdays,
+# 00:00-24:00 on Sundays and national holidays )
+# Time rebate: Off-peak rates are reduced to 0.08DKR after 1 hour
+##################################################################
+
+# INITIAL EXPENSE
+ per_connection=0.25
+ minimum_costs=0.00
+
+# OFF-PEAK and OFF-PEAK +1 hour
+ default=(0.001667, 1)
+ default=(0.001333, 1, 3600)
+
+# PEAK
+ on (monday..friday) between (8:00..15:59) use (0.004667, 1)
+
+# REDUCED PEAK
+ on (monday..friday) between (16:00..19:29) use (0.003333, 1)
+ on (saturday..saturday) between (8:00..15:59) use (0.003333, 1)
+
+# NATIONAL HOLIDAYS ( like off-peak hours )
+# Newyear
+ on (01/01) between () use (0.001667, 1)
+ on (01/01) between () use (0.001333, 1, 3600)
+
+# "Skærtorsdag"
+ on (easter-3) between () use (0.001667, 1)
+ on (easter-3) between () use (0.001333, 1, 3600)
+
+# "Langfredag"
+ on (easter-2) between () use (0.001667, 1)
+ on (easter-2) between () use (0.001333, 1, 3600)
+
+# "2. påskedag"
+ on (easter+1) between () use (0.001667, 1)
+ on (easter+1) between () use (0.001333, 1, 3600)
+
+# "St. bededag"
+ on (easter+26) between () use (0.001667, 1)
+ on (easter+26) between () use (0.001333, 1, 3600)
+
+# "Kristi himmelfart"
+ on (easter+39) between () use (0.001667, 1)
+ on (easter+39) between () use (0.001333, 1, 3600)
+
+# "2. pinsedag"
+ on (easter+50) between () use (0.001667, 1)
+ on (easter+50) between () use (0.001333, 1, 3600)
+
+# Christmas day
+ on (12/25) between () use (0.001667, 1)
+ on (12/25) between () use (0.001333, 1, 3600)
+
+# 2.nd christmas day
+ on (12/26) between () use (0.001667, 1)
+ on (12/26) between () use (0.001333, 1, 3600)
+
diff --git a/kppp/Rules/Denmark/Worldonline-Analog.rst b/kppp/Rules/Denmark/Worldonline-Analog.rst
new file mode 100644
index 00000000..d29aaccf
--- /dev/null
+++ b/kppp/Rules/Denmark/Worldonline-Analog.rst
@@ -0,0 +1,59 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : WorldOnline
+# PRODUCT : World Access (Analog)
+# CURRENCY : DKR
+# PHONENO : 1049 3883 3883
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.worldonline.dk/home_nyeminutpriser.html
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_WORLDONLINE_ANALOG
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.15 DKR
+# Minimum expense: 0.00 DKR
+# Additional timeexpense (25sec) = 0.055kr (max)
+#
+# Peak hours: 0.22 DKR/min ( 06:00-18:59 on weekdays )
+# Off-peak hours: 0.11 DKR/min ( 19:00-23:59 on weekdays,
+# 06:00-23:59 on Saturdays and Sundays )
+# Night rates: 0.06 DKR/min ( 00:00-05:59 all days )
+# Time rebate: all rates are reduced to 50% after 1 hour
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+#
+### REBATES ######################################################
+# (not included in cost-calculations!)
+# Volume Rebate: 250-500 DKR = 5%
+# +500 DKR = 10%
+# Students: Members of World Student Club receives a 10%
+# discount on all minute rates
+##################################################################
+
+
+# INITIAL EXPENSE
+ per_connection=0.15
+ minimum_costs=0.055
+
+# OFF-PEAK and OFFPEAK +1 hour
+ default=(0.001833, 1)
+ default=(0.000917, 1, 3600)
+
+# PEAK and PEAK +1 hour
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1)
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1, 3600)
+
+# NIGHT and NIGHT +1 hour
+ on (monday..sunday) between (00:00..05:59) use (0.001000, 1)
+ on (monday..sunday) between (00:00..05:59) use (0.000500, 1, 3600)
+
diff --git a/kppp/Rules/Denmark/Worldonline-ISDN.rst b/kppp/Rules/Denmark/Worldonline-ISDN.rst
new file mode 100644
index 00000000..2f2e0142
--- /dev/null
+++ b/kppp/Rules/Denmark/Worldonline-ISDN.rst
@@ -0,0 +1,60 @@
+### INFO #########################################################
+# COUNTRY : Denmark
+# ISP : WorldOnline
+# PRODUCT : World Access (ISDN)
+# CURRENCY : DKR
+# PHONENO : 1049 3883 3883
+# AUTHOR : Peter Thorsager <thorsager@post.tele.dk>
+# SOURCE : http://www.worldonline.dk/home_nyeminutpriser.html
+#
+# Since the ISP market changes fast these rates may be outdated
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##################################################################
+
+ name=DK_WORLDONLINE_ISDN
+ currency_symbol=kr
+ currency_position=right
+ currency_digits=2
+
+### RATES ########################################################
+# Date: February 21st - 2000
+# Initial expense: 0.10 DKR
+# Minimum expense: 0.00 DKR
+# Additional timeexpense (3sec) = 0.011kr (max)
+#
+# Peak hours: 0.22 DKR/min ( 06:00-18:59 on weekdays )
+# Off-peak hours: 0.11 DKR/min ( 19:00-23:59 on weekdays,
+# 06:00-23:59 on Saturdays and Sundays )
+# Night rates: 0.06 DKR/min ( 00:00-05:59 all days )
+# Time rebate: all rates are reduced to 50% after 1 hour
+#
+# NOTE: No special rates for national holidays!
+### REBATES ######################################################
+# (not included in cost-calculations!)
+# Volume Rebate: 250-500 DKR = 5%
+# +500 DKR = 10%
+# Students: Members of World Student Club receives a 10%
+# discount on all minute rates
+#
+### NOTES ########################################################
+# No special rates for national holidays!
+##################################################################
+
+
+# INITIAL EXPENSE
+ per_connection=0.10
+ minimum_costs=0.011
+
+# OFF-PEAK and OFFPEAK +1 hour
+ default=(0.001833, 1)
+ default=(0.000917, 1, 3600)
+
+# PEAK and PEAK +1 hour
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1)
+ on (monday..friday) between (6:00..18:59) use (0.003667, 1, 3600)
+
+# NIGHT and NIGHT +1 hour
+ on (monday..sunday) between (00:00..05:59) use (0.001000, 1)
+ on (monday..sunday) between (00:00..05:59) use (0.000500, 1, 3600)
+
diff --git a/kppp/Rules/Estonia/Eesti_Telefon.rst b/kppp/Rules/Estonia/Eesti_Telefon.rst
new file mode 100644
index 00000000..4f9b3e15
--- /dev/null
+++ b/kppp/Rules/Estonia/Eesti_Telefon.rst
@@ -0,0 +1,58 @@
+################################################################
+# Viimati muudetud : 31/10/2002
+# Autor : Rivo Laks <rivolaks@hot.ee>
+# Tariifid Eesti Telefonile (kohalik kõne)
+################################################################
+
+
+# Nimi
+name=Eesti_Telefon
+
+### Valuuta
+#Sümbol
+currency_symbol=kr
+# Positsioon
+currency_position=right
+# Kohti peale koma
+currency_digits=2
+
+# Ühenduse loomise tasu (pole)
+per_connection=0.0
+# Miinimumtasu ühenduse eest (pole)
+minimum_costs=0.0
+
+### Kõnealustustasu 48 senti peale 1. sekundit
+flat_init_costs=(0.48,1)
+
+### Normaaltariif 34 senti/minutis (0.56666666... senti/sekundis)
+default=(0.005666, 1)
+
+### Soodustariif 28 senti/minutis (0.4666... senti/sek)
+# Tööpäevadel 19 - 01
+on (monday..friday) between (19:00..00:59) use (0.004666, 1)
+# Puhkepäevadel 07 - 01
+on (saturday..sunday) between (07:00..00:59) use (0.004666, 1)
+
+### Öötariif 14 senti/minutis (0.23333333... senti/sek)
+# Iga päev 01 - 07
+on (monday..sunday) between (01:00..06:59) use (0.002333, 1)
+
+### Pühad (soodustariif 07 - 01)
+# Iseseisvuspäev
+on (02/24) between (07:00..00:59) use (0.004666, 1)
+# Uusaasta
+on (01/01) between (07:00..00:59) use (0.004666, 1)
+# Kevadpüha
+on (05/01) between (07:00..00:59) use (0.004666, 1)
+# Võidupüha
+on (06/23) between (07:00..00:59) use (0.004666, 1)
+# Jaanipäev
+on (06/24) between (07:00..00:59) use (0.004666, 1)
+# Taasiseseisvumispäev
+on (08/20) between (07:00..00:59) use (0.004666, 1)
+# Esimene jõulupüha
+on (12/25) between (07:00..00:59) use (0.004666, 1)
+# Teine jõulupüha
+on (12/26) between (07:00..00:59) use (0.004666, 1)
+# Suur reede
+on (easter-2) between (07:00..00:59) use (0.004666, 1)
diff --git a/kppp/Rules/Estonia/Makefile.am b/kppp/Rules/Estonia/Makefile.am
new file mode 100644
index 00000000..542be054
--- /dev/null
+++ b/kppp/Rules/Estonia/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = Eesti_Telefon.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Estonia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Finland/Makefile.am b/kppp/Rules/Finland/Makefile.am
new file mode 100644
index 00000000..abc9cf4b
--- /dev/null
+++ b/kppp/Rules/Finland/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = VLP.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Finland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Finland/VLP.rst b/kppp/Rules/Finland/VLP.rst
new file mode 100644
index 00000000..cf3a0392
--- /dev/null
+++ b/kppp/Rules/Finland/VLP.rst
@@ -0,0 +1,64 @@
+# VLP:n hinnat 2.1.2003 VLP:n asiakaspalvelun mukaan
+# Mikko Korhonen mjkorhon@cc.hut.fi
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=VLP
+
+################################################################
+# currency settings
+################################################################
+
+# this is the euro sign in UTF-8
+currency_symbol=¤
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0821
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+#minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+#flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+# cost is 1.34 cents per minute with accuracy of 1 second
+# 8 significant digits should be enough
+default=(0.00022333333, 1)
+
diff --git a/kppp/Rules/France/Cegetel_Local.rst b/kppp/Rules/France/Cegetel_Local.rst
new file mode 100644
index 00000000..d3a4b7d3
--- /dev/null
+++ b/kppp/Rules/France/Cegetel_Local.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Cegetel, connexion locale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=cegetel_local
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/comparatif.asp
+# soit 1,7 c d'euro la minute
+flat_init_costs=(0.122,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.017, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Cegetel_National.rst b/kppp/Rules/France/Cegetel_National.rst
new file mode 100644
index 00000000..47a70253
--- /dev/null
+++ b/kppp/Rules/France/Cegetel_National.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Cegetel, connexion nationale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=cegetel_national
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/offres.asp
+# soit 4,1 c d'euro la minute
+flat_init_costs=(0.122,20)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.041, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/France_Telecom_Internet.rst b/kppp/Rules/France/France_Telecom_Internet.rst
new file mode 100644
index 00000000..5886c39b
--- /dev/null
+++ b/kppp/Rules/France/France_Telecom_Internet.rst
@@ -0,0 +1,59 @@
+################################################################
+#
+# Règles pour France Télécom
+# Appels "Internet"
+# Valable vers tous les numéros Internet
+# commençant par : 08 36 06 13 1. , 08 36 01 9. .. ,
+# 08 60 .. .. ..,
+# et aux numéros : 08 36 01 13 13 et 08 36 01 30 13
+#
+# Fichier original (je pense...) par
+# $Id$
+# (C) 1997 Czo <sirol@asim.lip6.fr>
+#
+# Modifié par Pascal Benito <pascal.benito@free.fr>
+# le 11 Août 2000.
+#
+# Passé a l'euro par David Faure <david@mandrakesoft.com> avec
+# indications de Frederic Delaporte <fredericdelaporte@free.fr>
+#
+# Modifié par Cyril Bosselut <bosselut@b1project.com>
+# Renomé en France_Telecom_Internet.rst
+# le 22 Juillet 2004.
+#
+################################################################
+
+
+name=France_Telecom_Internet
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connection
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+flat_init_costs=(0.10,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.02, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
+
diff --git a/kppp/Rules/France/France_Telecom_Local.rst b/kppp/Rules/France/France_Telecom_Local.rst
new file mode 100644
index 00000000..47192059
--- /dev/null
+++ b/kppp/Rules/France/France_Telecom_Local.rst
@@ -0,0 +1,70 @@
+################################################################
+#
+# Règles pour France Télécom
+# Appels locaux
+#
+# Fichier original (je pense...) par
+# $Id$
+# (C) 1997 Czo <sirol@asim.lip6.fr>
+#
+# Modifié par Pascal Benito <pascal.benito@free.fr>
+# le 11 Août 2000.
+#
+# Passé a l'euro par David Faure <david@mandrakesoft.com>
+# le 8 Avril 2002.
+#
+# Modifié par Cyril Bosselut <bosselut@b1project.com>
+# le 22 Juillet 2004.
+#
+################################################################
+
+# Pour les appels locaux, France Télécom indique qu'une minute
+# de communication devrait coûter:
+# ( ceci ne doit pas tenir compte des arrondis )
+#
+# 0.018 EUR TTC en tarif réduit
+# 0.033 EUR TTC in tarif normal
+#
+
+name=France_Telecom_Local
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connection
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+flat_init_costs=(0.091,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.033, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
+
+on (monday..friday) between (00:00..07:59) use (0.018, 60)
+on (monday..friday) between (08:00..18:59) use (0.033, 60)
+on (monday..friday) between (19:00..23:59) use (0.018, 60)
+
+on (saturday..sunday) between (00:00..23:59) use (0.018, 60)
+
+# Jours fériés :-))
+on (01/01, easter+1, 05/01, 05/08, easter+38, easter+50, 07/14, 08/15, 11/01, 11/11, 12/25) between (00:00..23:59) use (0.018, 60)
+
diff --git a/kppp/Rules/France/France_Telecom_National.rst b/kppp/Rules/France/France_Telecom_National.rst
new file mode 100644
index 00000000..4b1bac2d
--- /dev/null
+++ b/kppp/Rules/France/France_Telecom_National.rst
@@ -0,0 +1,70 @@
+################################################################
+#
+# Règles pour France Télécom
+# Appels nationaux
+#
+# Fichier original (je pense...) par
+# $Id$
+# (C) 1997 Czo <sirol@asim.lip6.fr>
+#
+# Modifié par Pascal Benito <pascal.benito@free.fr>
+# le 11 Août 2000.
+#
+# Passé a l'euro par David Faure <david@mandrakesoft.com>
+# le 8 Avril 2002.
+#
+# Modifié par Cyril Bosselut <bosselut@b1project.com>
+# le 22 Juillet 2004.
+#
+################################################################
+
+# Pour les appels nationaux, France Télécom indique qu'une minute
+# de communication devrait coûter:
+# ( ceci ne doit pas tenir compte des arrondis )
+#
+# 0.061 EUR TTC en tarif réduit
+# 0.091 EUR TTC in tarif normal
+# (et 0.11 EUR pour les premieres 39 secondes)
+
+name=France_Telecom_National
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connection
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour les 39 premières secondes de
+# connexion, peu importe si l'appel dure 1 ou 39 secondes.
+flat_init_costs=(0.11,39)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.091, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
+
+on (monday..friday) between (00:00..07:59) use (0.061, 60)
+on (monday..friday) between (08:00..18:59) use (0.091, 60)
+on (monday..friday) between (19:00..23:59) use (0.061, 60)
+
+on (saturday..sunday) between (00:00..23:59) use (0.061, 60)
+
+# Jours fériés :-))
+on (01/01, easter+1, 05/01, 05/08, easter+38, easter+50, 07/14, 08/15, 11/01, 11/11, 12/25) between (00:00..23:59) use (0.061, 60)
+
diff --git a/kppp/Rules/France/Le_9_Local.rst b/kppp/Rules/France/Le_9_Local.rst
new file mode 100644
index 00000000..d47c0376
--- /dev/null
+++ b/kppp/Rules/France/Le_9_Local.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Le 9, connexion locale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=le_9_local
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/comparatif.asp
+# soit 1,2 c d'euro
+flat_init_costs=(0.12,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.012, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Le_9_National.rst b/kppp/Rules/France/Le_9_National.rst
new file mode 100644
index 00000000..7cf84d45
--- /dev/null
+++ b/kppp/Rules/France/Le_9_National.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Le 9, connexion nationale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=le_9_national
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/offres.asp
+# soit 3,3 c d'euro la minute
+flat_init_costs=(0.12,20)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.033, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Makefile.am b/kppp/Rules/France/Makefile.am
new file mode 100644
index 00000000..5612fb43
--- /dev/null
+++ b/kppp/Rules/France/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = France_Telecom_Local.rst France_Telecom_Internet.rst France_Telecom_National.rst \
+ Cegetel_Local.rst Cegetel_National.rst Le_9_Local.rst Le_9_National.rst Tele2_Local.rst Tele2_National.rst \
+ OneTel.rst Wanadoo_Free.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/France
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/France/OneTel.rst b/kppp/Rules/France/OneTel.rst
new file mode 100644
index 00000000..427cd95d
--- /dev/null
+++ b/kppp/Rules/France/OneTel.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Onetel, connexion locale ou nationale
+#
+# Fait le 28 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=OneTel
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/comparatif.asp
+# soit 1,7 c d'euro la minute
+flat_init_costs=(0.01,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.01, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Tele2_Local.rst b/kppp/Rules/France/Tele2_Local.rst
new file mode 100644
index 00000000..61cb0f6f
--- /dev/null
+++ b/kppp/Rules/France/Tele2_Local.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Télé2, connexion locale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=tele2_local
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/comparatif.asp
+# soit 1,4 c d'euro la minute
+flat_init_costs=(0.122,60)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.014, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Tele2_National.rst b/kppp/Rules/France/Tele2_National.rst
new file mode 100644
index 00000000..cfeea5e4
--- /dev/null
+++ b/kppp/Rules/France/Tele2_National.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Règles pour Télé2, connexion nationale
+#
+# Fait le 27 octobre 2002 par Gérard Delafond
+# d'après David Faure, lui-même d'après, etc.
+################################################################
+
+
+name=tele2_national
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# Paramètres de connexion
+################################################################
+
+# NOTE: les règles sont parcourues du dèbut vers la fin du fichier
+# donc seulement la dernière règle appropriée est prise en
+# considération. Placez donc les règles les plus générales
+# avant les plus spécifiques.
+
+# Ceci est un coût supplémentaire éventuel par appel. Si vous
+# n'êtes pas concerné, posez-le égal à zéro ou commentez-le.
+per_connection=0.0
+
+# Frais minimum par appel. Si le coût d'un appel est inférieur
+# à cette valeur, alors cette dernière est le coût retenu.
+minimum_costs=0.0
+
+# C'est ce que vous payez pour la première minute de
+# connexion, peu importe si l'appel dure 1 ou 59 secondes.
+# tarifs établis d'après
+# http://www.9online.fr/espaceabonnes/telephonie/comparatif.asp
+# soit 3,4 c d'euro la minute
+flat_init_costs=(0.122,20)
+
+# Ceci est la règle utilisée par défaut lorsqu'aucune autre ne
+# s'applique. Le premier nombre est le prix correspondant à la
+# durée en secondes qui est le second nombre.
+default=(0.034, 60)
+# (quelle est l'utilité de ce paramètre ?!?!)
diff --git a/kppp/Rules/France/Wanadoo_Free.rst b/kppp/Rules/France/Wanadoo_Free.rst
new file mode 100644
index 00000000..401bde3b
--- /dev/null
+++ b/kppp/Rules/France/Wanadoo_Free.rst
@@ -0,0 +1,35 @@
+################################################################
+#
+# kppp rules voor het Wanadoo Free abonnement per minuut
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 3.25 eurocpm=0.0542 eurocps 08:00-18:59
+# daltarief 1,77 eurocpm=0.0295 eurocps 19:00-23:59
+# nacht 1,29 eurocpm=0.0215 eurocps 00:00-07:59
+# Zaterdag 1,29 eurocpm=0.0215 eurocps 00:00-23:59
+# Zondag 1,29 eurocpm=0.0215 eurocps 00:00-23:59
+#
+# Starttarief 3,5 euroct per gesprek
+#
+#
+# Eelco van Kuik
+# kppp-accounting@vankuik.com
+#
+# Created on 23 june 2002
+#
+################################################################
+
+name=Wanadoo Free
+currency_symbol=EUR
+currency_position=left
+currency_digits=2
+per_connection=0.0350
+minimum_costs=0.0
+
+default=(0.000542, 1)
+on (monday..friday) between (19:00..23:59) use (0.000295, 1)
+on (monday..friday) between (00:00..07:59) use (0.000215, 1)
+on (saturday) between (00:00..23:59) use (0.000215, 1)
+on (sunday) between (00:00..23:59) use (0.000215, 1)
diff --git a/kppp/Rules/Germany/1und1_InternetZugang.rst b/kppp/Rules/Germany/1und1_InternetZugang.rst
new file mode 100644
index 00000000..8f8dda93
--- /dev/null
+++ b/kppp/Rules/Germany/1und1_InternetZugang.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# This is a rule set for kppp.
+#
+# Created by Jesco Topp (jesco@users.sourceforge.net)
+#
+# Ruleset for 1&1 Internet.Zugang
+#
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=1und1_InternetZugang
+
+################################################################
+# currency settings
+################################################################
+
+# defines 'EUR' (EURO) as currency symbol
+#
+currency_symbol=EUR
+
+# The position of the currency symbol.
+#
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# hourly accounting rules
+on (monday..sunday) between (9:00..22:59) use (0.0099, 60)
+on (monday..sunday) between (23:00..8:59) use (0.0049, 60)
diff --git a/kppp/Rules/Germany/2.5min.rst b/kppp/Rules/Germany/2.5min.rst
new file mode 100644
index 00000000..e36e766a
--- /dev/null
+++ b/kppp/Rules/Germany/2.5min.rst
@@ -0,0 +1,60 @@
+################################################################
+#
+# Disclaimer/License
+# This rule file ist (c) by Ingolf Jandt <i.jandt@web.de>
+#
+# Redistribute it; change it according to your needs.
+#
+################################################################
+#
+# 2,5 Pfennige ganztags sind heute Standard für byCall-
+# Verbindungen in Deutschland. Darum dieser Rule-Datei.
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET.
+#
+################################################################
+name=2,5 Pf ganztags, 1-min-Takt
+
+################################################################
+# currency settings
+################################################################
+
+# currency symbol (default is "$")
+currency_symbol=DM
+
+# currency position (default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2")
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect.
+# Uncomment it if needed.
+# per_connection=0.0
+
+# Minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# minimum_costs=0.0
+
+# The first minute is always fully billed.
+# This rule will take priority during the first 60 seconds
+# over any other rule, in particular the 'default' rule.
+flat_init_costs=(0.025,60)
+
+# This is the default rule which is used when no other rule
+# applies. The following rule means: "Every 60 seconds 0.025
+# DM are added to the bill"
+default=(0.025, 60)
diff --git a/kppp/Rules/Germany/2.5s.rst b/kppp/Rules/Germany/2.5s.rst
new file mode 100644
index 00000000..a8d4e5be
--- /dev/null
+++ b/kppp/Rules/Germany/2.5s.rst
@@ -0,0 +1,60 @@
+################################################################
+#
+# Disclaimer/License
+# This rule file ist (c) by Ingolf Jandt <i.jandt@web.de>
+#
+# Redistribute it; change it according to your needs.
+#
+################################################################
+#
+# 2,5 Pfennige ganztags sind heute Standard für byCall-
+# Verbindungen in Deutschland. Darum dieser Rule-Datei.
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET.
+#
+################################################################
+name=2,5 Pf ganztags, 1-s-Takt
+
+################################################################
+# currency settings
+################################################################
+
+# currency symbol (default is "$")
+currency_symbol=DM
+
+# currency position (default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2")
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect.
+# Uncomment it if needed.
+# per_connection=0.0
+
+# Minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.01
+
+# The first minute is always fully billed.
+# This rule will take priority during the first 60 seconds
+# over any other rule, in particular the 'default' rule.
+# flat_init_costs=(0.025,60)
+
+# This is the default rule which is used when no other rule
+# applies. The following rule means: "Every 1 seconds 0.025 / 60
+# DM are added to the bill"
+default=(0.000416667, 1)
diff --git a/kppp/Rules/Germany/AddCom_by_Call.rst b/kppp/Rules/Germany/AddCom_by_Call.rst
new file mode 100644
index 00000000..efb23d14
--- /dev/null
+++ b/kppp/Rules/Germany/AddCom_by_Call.rst
@@ -0,0 +1,50 @@
+################################################################
+#
+# created 99/09/27 by M.Jerger
+# email:micha@mjerger.de
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=AddCom by Call
+
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.059, 60)
+on () between (21:00..08:59) use (0.039, 60)
+on (saturday..sunday, 01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between (09:00..20:59) use (0.055, 60)
+on (saturday..sunday, 01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between (21:00..08:59) use ( 0.039, 60)
+
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
+
diff --git a/kppp/Rules/Germany/Addcom.rst b/kppp/Rules/Germany/Addcom.rst
new file mode 100644
index 00000000..cd8f7f13
--- /dev/null
+++ b/kppp/Rules/Germany/Addcom.rst
@@ -0,0 +1,23 @@
+################################################################
+#
+# Addcom rules
+#
+# created on 99/10/14, Holger Witthaut <Holger@tapage.de>
+#
+################################################################
+
+name=addcom
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.039, 60)
+
+# Montag bis Freitag
+on (monday..friday) between (09:00..20:59) use (0.059, 60)
+on (monday..friday) between (21:00..08:59) use (0.039, 60)
+
+# Samstag und Sonntag
+on (saturday..sunday) between (09:00..20:59) use (0.055, 60)
+on (saturday..sunday) between (21:00..08:59) use (0.039, 60)
diff --git a/kppp/Rules/Germany/Arcor_Internet_by_Call_easy.rst b/kppp/Rules/Germany/Arcor_Internet_by_Call_easy.rst
new file mode 100644
index 00000000..ae82c795
--- /dev/null
+++ b/kppp/Rules/Germany/Arcor_Internet_by_Call_easy.rst
@@ -0,0 +1,52 @@
+################################################################
+#
+# kppp ruleset for Arcor-Internet by Call easy accounts
+# created 01/04/01 by Harald Bongartz <Harald@Bongartz.cx>
+# modified 02/01/26 by Alexander Heide <alexander.heide@gmx.net>
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Arcor-Internet by Call easy
+
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=¤
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.0148, 60)
+on () between (18:00..08:59) use (0.0097, 60)
+on (saturday..sunday, 01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between () use ( 0.0097, 60)
+
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
+
+
+
diff --git a/kppp/Rules/Germany/CallOkaynet.rst b/kppp/Rules/Germany/CallOkaynet.rst
new file mode 100644
index 00000000..844ecf3c
--- /dev/null
+++ b/kppp/Rules/Germany/CallOkaynet.rst
@@ -0,0 +1,39 @@
+#################################################################################
+# AddCom accounting rules
+# http://www.CallOkay.net
+#
+# derived 26/09/99 by Frank Meier <Frank.Meier@deDanaan.de>
+# from CityCall by Stefan Markgraf and Stefan Troeger
+#
+#################################################################################
+
+name=CallOkay.net
+currency_symbol=DM
+currency_position=right
+currency_digits=3
+per_connection=0.0
+minimum_costs=0.0
+default=(0.039, 1)
+
+# Montag bis Freitag
+on (monday..sunday) between (8:00..7:59) use (0.049, 1)
+
+# Bundeseinheitliche Feiertage, Heiligabend und Silvester
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50,10/03, 12/24, 12/25, 12/26, 12/31) between (8:00..7:59) use (0.049, 1)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
+
+# Zusaetzliche Tage mit Feiertagstarif:
+# 12/24 Heiligabend
+# 12/31 Silvester
diff --git a/kppp/Rules/Germany/Callino_Surf_Basic.rst b/kppp/Rules/Germany/Callino_Surf_Basic.rst
new file mode 100644
index 00000000..c7fb8d5e
--- /dev/null
+++ b/kppp/Rules/Germany/Callino_Surf_Basic.rst
@@ -0,0 +1,51 @@
+################################################################
+#
+# German Callino Surf Basic accounting rules
+# created 2000-11-24 by thomas holst (tho), holstt@thoserve.de
+#
+################################################################
+#
+# name of ruleset
+#
+################################################################
+
+name=Callino_Surf_Basic
+
+################################################################
+#
+# currency settings
+#
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+################################################################
+#
+# connection settings
+#
+################################################################
+
+per_connection=0.06
+minimum_costs=0.0
+default=(0.029,60)
+
+on (monday..friday) between (22:00..08:00) use (0.025,60)
+
+on (saturday..sunday) between() use (0.025,60)
+
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between () use (0.025, 60)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
diff --git a/kppp/Rules/Germany/Callino_Surf_Plus.rst b/kppp/Rules/Germany/Callino_Surf_Plus.rst
new file mode 100644
index 00000000..c0cb8cd0
--- /dev/null
+++ b/kppp/Rules/Germany/Callino_Surf_Plus.rst
@@ -0,0 +1,33 @@
+################################################################
+#
+# German Callino Surf Plus accounting rules
+# created 2000-11-24 by thomas holst (tho), holstt@thoserve.de
+#
+################################################################
+#
+# name of ruleset
+#
+################################################################
+
+name=Callino_Surf_Plus
+
+################################################################
+#
+# currency settings
+#
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+################################################################
+#
+# connection settings
+#
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.019,60)
+
diff --git a/kppp/Rules/Germany/Callisa_City.rst b/kppp/Rules/Germany/Callisa_City.rst
new file mode 100644
index 00000000..b5d3a218
--- /dev/null
+++ b/kppp/Rules/Germany/Callisa_City.rst
@@ -0,0 +1,56 @@
+################################################################
+#
+# German Callisa City accounting rules
+# created 2001-10-13 Herbert Nachbagauer (herby74@freemail.de)
+#
+# NOTE: Please check at http://www.callisa.de/callisacities.php3
+# if the rate applies to your city.
+#
+################################################################
+#
+# name of ruleset
+#
+################################################################
+
+name=Callisa_City
+
+################################################################
+#
+# currency settings
+#
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+################################################################
+#
+# connection settings
+#
+################################################################
+
+default=(0.0244,60)
+
+on (monday..friday) between (09:00..18:00) use (0.0244,60)
+
+on (monday..friday) between (18:00..24:00) use (0.0189,60)
+
+on (monday..friday) between (00:00..09:00) use (0.0189,60)
+
+on (saturday..sunday) between() use (0.0189,60)
+
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between () use (0.0189, 60)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
diff --git a/kppp/Rules/Germany/City_Activ_Plus_Option.rst b/kppp/Rules/Germany/City_Activ_Plus_Option.rst
new file mode 100644
index 00000000..7dca6c86
--- /dev/null
+++ b/kppp/Rules/Germany/City_Activ_Plus_Option.rst
@@ -0,0 +1,21 @@
+###########################################################################
+#
+# German Telekom accounting rules
+#
+# City mit Activ Plus Option
+#
+# created 99/08/27 Moritz Moeller-Herrmann mmh@gmx.net (NO WARRANTY!)
+#
+###########################################################################
+
+name=3DGerman_Telekom_Activplus_City
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.06, 60)
+
+# Einfache Regeln gell?
+on (monday..sunday) between (9:00..17:59) use (0.06, 60)
+on (monday..sunday) between (18:00..8:59) use (0.03, 60)
diff --git a/kppp/Rules/Germany/Cityweb.rst b/kppp/Rules/Germany/Cityweb.rst
new file mode 100644
index 00000000..7efcfd30
--- /dev/null
+++ b/kppp/Rules/Germany/Cityweb.rst
@@ -0,0 +1,25 @@
+################################################################
+# Ruleset for Cityweb Tarif (Standard German Internetprovider)
+# based on the KDE Template
+# created 10/07/2001 by dunkelelf (stefan jurisch)
+# <dunkelelf@cityweb.de>
+################################################################
+
+name=Cityweb
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+# 3,3 Pf / min mit minutengenauer Abrechnung
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.033, 60)
diff --git a/kppp/Rules/Germany/CompuservePro.rst b/kppp/Rules/Germany/CompuservePro.rst
new file mode 100644
index 00000000..4de6afd1
--- /dev/null
+++ b/kppp/Rules/Germany/CompuservePro.rst
@@ -0,0 +1,16 @@
+######################################################################
+# #
+# German Compuserve Pro Online Rules at 05.December 2001 #
+# #
+# created 01/12/05 by Timo <timo@userland.de> #
+######################################################################
+
+name=Compuserve Pro
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+default=(0.009, 60)
+
+# Täglich von 18:00 bis 08:59
+on (monday..sunday) between (18:00..08:59) use (0.014, 60)
+
diff --git a/kppp/Rules/Germany/E-Plus-Online_Jedermann.rst b/kppp/Rules/Germany/E-Plus-Online_Jedermann.rst
new file mode 100644
index 00000000..a8293d25
--- /dev/null
+++ b/kppp/Rules/Germany/E-Plus-Online_Jedermann.rst
@@ -0,0 +1,60 @@
+################################################################
+#
+# E-Plus-Online accounting rules for everybody
+# although without a handy!!
+#
+# created 02/03/31 by Robert Klier<Robert.Klier@eplus-online.de>
+#
+################################################################
+#
+#
+# Name:
+#
+name=E-Plus-Online_für_Jedermann
+#
+#
+# Währungseinstellungen
+#
+currency_symbol=EUR
+currency_position=right
+currency_digits=4
+#
+#
+# Kosten pro Verbindung
+#
+per_connection=0.0
+minimum_costs=0.0
+#
+#
+# Kosten pro Zeit EUR/Min
+# Mo. - Fr. 9 -18 Uhr 0,0175
+# übrige Zeit sowie an bundeseinh. Feiertagen 0,0125
+#
+default=(0.002083, 10)
+on (monday..friday) between (9:00..18:00) use (0.002917, 10)
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/24..12/26, 12/31) between (0:00..23:59) use (0.002083, 10)
+#
+#
+#
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
+#
+# Zusaetzliche Tage mit Feiertagstarif:
+# 12/24 Heiligabend
+# 12/31 Silvester
+
+
+
+
+
+
diff --git a/kppp/Rules/Germany/Easynet_easy-call.rst b/kppp/Rules/Germany/Easynet_easy-call.rst
new file mode 100644
index 00000000..95a71b7d
--- /dev/null
+++ b/kppp/Rules/Germany/Easynet_easy-call.rst
@@ -0,0 +1,36 @@
+################################################################
+#
+# kppp ruleset for Easynet easy-call accounts
+# created 02/02/01 by Jochen Fecht
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Easynet easy-call-Tarif
+
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=EUR
+currency_position=right
+currency_digits=3
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.0148, 60)
+on () between (18:00..8:59) use (0.0097, 60)
+
+
+
diff --git a/kppp/Rules/Germany/Freenet_Enterprise.rst b/kppp/Rules/Germany/Freenet_Enterprise.rst
new file mode 100644
index 00000000..8ceb482b
--- /dev/null
+++ b/kppp/Rules/Germany/Freenet_Enterprise.rst
@@ -0,0 +1,28 @@
+################################################################
+# Ruleset for Freenet Enterprice Tarif (Internet by Call with
+# Application)
+# based on the KDE Template
+# created 2000/04/15 by Ingo von Borstel <ingo.vonborstel@web.de>
+#
+# Again the cheapest call-by-call provider in Germany
+# though it requires application
+################################################################
+
+name=Freenet_Enterprise
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+# 2,5 Pf / min mit minutengenauer Abrechnung
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.025, 60)
diff --git a/kppp/Rules/Germany/Freenet_Sorglos.rst b/kppp/Rules/Germany/Freenet_Sorglos.rst
new file mode 100644
index 00000000..cb8c0612
--- /dev/null
+++ b/kppp/Rules/Germany/Freenet_Sorglos.rst
@@ -0,0 +1,33 @@
+################################################################
+# Ruleset for Freenet Sorglos
+# Internet by Call with Application
+#
+# Created 2002-03-07 by Valentin Funk <valentin.funk@computerfabrik.de>
+#
+# One of the cheapest call-by-call provider with application in
+# Germany.
+# Note: Balanced as Freenet StandardTarif until application.
+################################################################
+
+################################################################
+# Name of the ruleset - absolutly requiered!
+################################################################
+
+name=Freenet_Sorglos
+
+################################################################
+# Currency settings
+################################################################
+
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+################################################################
+# Connection settings
+# 1,29 ¢/Min mit minutengenauer Abrechnung
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.0129, 60)
diff --git a/kppp/Rules/Germany/Freenet_StandardTarif.rst b/kppp/Rules/Germany/Freenet_StandardTarif.rst
new file mode 100644
index 00000000..0eed3a95
--- /dev/null
+++ b/kppp/Rules/Germany/Freenet_StandardTarif.rst
@@ -0,0 +1,32 @@
+################################################################
+# Ruleset for Freenet StandardTarif
+# Internet by Call _without_ Application
+#
+# Created 2002-04-29 by Valentin Funk <valentin.funk@computerfabrik.de>
+#
+# An ordinary german internet call-by-call rate. Also used as
+# fallback rate until application for other Freenet rates.
+################################################################
+
+################################################################
+# Name of the ruleset - absolutly requiered!
+################################################################
+
+name=Freenet_StandardTarif
+
+################################################################
+# Currency settings
+################################################################
+
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+################################################################
+# Connection settings
+# 2,505 ¢/Min mit minutengenauer Abrechnung
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.02505, 60)
diff --git a/kppp/Rules/Germany/Freenet_Super_CbC.rst b/kppp/Rules/Germany/Freenet_Super_CbC.rst
new file mode 100644
index 00000000..f1f4c2be
--- /dev/null
+++ b/kppp/Rules/Germany/Freenet_Super_CbC.rst
@@ -0,0 +1,40 @@
+################################################################
+# Ruleset for Freenet Super CbC
+# Internet by Call _without_ Application
+#
+# Created 2002-04-29 by Valentin Funk <valentin.funk@computerfabrik.de>
+#
+# One of the cheapest internet call-by-call rates.
+# Notes:
+# Once, you have to use a special dial in number: 019231760!
+# Twice, you could be stressed by additional HTML code for
+# advertisement.
+################################################################
+
+################################################################
+# Name of the ruleset - absolutly requiered!
+################################################################
+
+name=Freenet_Super_CbC
+
+################################################################
+# Currency settings
+################################################################
+
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+################################################################
+# Connection settings
+# 1,45 ¢/Min 8-18 Uhr
+# 0,89 ¢/Min 18-8 Uhr
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+default=(0.0145, 60)
+
+on () between (08:00..18:00) use (0.0145, 60)
+on () between (18:00..08:00) use (0.0089, 60)
diff --git a/kppp/Rules/Germany/Freenet_special_call_by_call.rst b/kppp/Rules/Germany/Freenet_special_call_by_call.rst
new file mode 100644
index 00000000..7cebaa51
--- /dev/null
+++ b/kppp/Rules/Germany/Freenet_special_call_by_call.rst
@@ -0,0 +1,57 @@
+################################################################
+#
+# German Freenet accounting rules
+# created 2000-11-24 by thomas holst (tho), holstt@thoserve.de
+# up-to-date 2000-11-01 by volker schlechter, schlecht@selk-stuttgart.de
+# up-to-date 2002-02-11 by Friedemann Schorer, friedemann.schorer@gmx.de
+#
+################################################################
+#
+# name of ruleset
+#
+################################################################
+
+name=Freenet
+
+################################################################
+#
+# currency settings
+#
+################################################################
+
+currency_symbol=¤
+currency_position=left
+currency_digits=2
+
+################################################################
+#
+# connection settings
+#
+################################################################
+
+#per_connection=0.06
+#minimum_costs=0.0
+
+# Default - falls mal was nicht klappt, wird sicherheitshalber
+# der teuerste Tarif genommen
+default=(0.0145,60)
+
+on (monday..sunday) between (22:00..09:00) use (0.0089,60)
+on (monday..sunday) between (09:00..18:00) use (0.0145,60)
+on (monday..sunday) between (18:00..22:00) use (0.0115,60)
+
+
+# on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between () use (0.025, 60)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
diff --git a/kppp/Rules/Germany/MSN.rst b/kppp/Rules/Germany/MSN.rst
new file mode 100644
index 00000000..a8f0e0ea
--- /dev/null
+++ b/kppp/Rules/Germany/MSN.rst
@@ -0,0 +1,18 @@
+################################################################
+# MSN (Microsoft NetWork) Ruleset
+#
+# created on 30.07.2000 by Tobias Toedter
+# <t.toedter@web.de>
+#
+################################################################
+
+name=MSN
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# one rate for every date and time...
+default=(0.00055, 1)
+
diff --git a/kppp/Rules/Germany/Makefile.am b/kppp/Rules/Germany/Makefile.am
new file mode 100644
index 00000000..f96201b4
--- /dev/null
+++ b/kppp/Rules/Germany/Makefile.am
@@ -0,0 +1,34 @@
+pkg_DATA = 2.5min.rst 2.5s.rst \
+ Arcor_Internet_by_Call_easy.rst \
+ AddCom_by_Call.rst Addcom.rst \
+ Callino_Surf_Basic.rst Callino_Surf_Plus.rst \
+ Callisa_City.rst \
+ CallOkaynet.rst \
+ Cityweb.rst \
+ City_Activ_Plus_Option.rst \
+ CompuservePro.rst \
+ Easynet_easy-call.rst \
+ expressnet.rst Netcom_Kassel.rst \
+ Freenet_Sorglos.rst \
+ Freenet_StandardTarif.rst \
+ Freenet_Super_CbC.rst \
+ Mobilcom_Freenet.rst \
+ Nikoma.rst Nikoma_Internet_by_Call.rst Nikoma_Study_and_Surf.rst \
+ Telekom_City_Select_5_30.rst \
+ knUUt-by-Call.rst \
+ talkline_by_call.rst \
+ vossnet_fun.rst vossnet_fun_light.rst vossnet_kompl.rst \
+ Planet-Interkom_Internet_by_call.rst \
+ Puretec.rst \
+ NGI_Call_By_Call.rst \
+ MSN.rst \
+ Freenet_special_call_by_call.rst \
+ Freenet_Enterprise.rst \
+ E-Plus-Online_Jedermann.rst \
+ 1und1_InternetZugang.rst \
+ VR-Web.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Germany
+
+EXTRA_DIST = $(pkg_DATA)
+
diff --git a/kppp/Rules/Germany/Mobilcom_Freenet.rst b/kppp/Rules/Germany/Mobilcom_Freenet.rst
new file mode 100644
index 00000000..8c60cd39
--- /dev/null
+++ b/kppp/Rules/Germany/Mobilcom_Freenet.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# German Mobilcom Freenet accounting rules
+#
+# created 1999/11/27 by Sebastian Linz <Sebastian.Linz@gmx.net>
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+
+name=Mobilcom_Freenet
+
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=0.0
+# flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.05, 60)
+
+
+
diff --git a/kppp/Rules/Germany/NGI_Call_By_Call.rst b/kppp/Rules/Germany/NGI_Call_By_Call.rst
new file mode 100644
index 00000000..dafb8275
--- /dev/null
+++ b/kppp/Rules/Germany/NGI_Call_By_Call.rst
@@ -0,0 +1,14 @@
+################################################################
+# Rule for NGI (Next Generation Internet) Call By Call
+# bablokb@gmx.de (Bernhard Bablok)
+################################################################
+
+name=NGI Call By Call
+
+currency_symbol=DM
+currency_position=right
+currency_digits=8
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.000415,1)
diff --git a/kppp/Rules/Germany/Netcom_Kassel.rst b/kppp/Rules/Germany/Netcom_Kassel.rst
new file mode 100644
index 00000000..d1e06744
--- /dev/null
+++ b/kppp/Rules/Germany/Netcom_Kassel.rst
@@ -0,0 +1,20 @@
+########################################################
+# Netcom-Kassel (Hercules L-XL) Hotline-Tel:9202020
+# internet:9202000
+#
+# createt on 13.05.2001 by Stefan Zintel
+# <zintel@netcomcity.de>
+#
+########################################################
+
+name=Netcom-Kassel
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+# Standardtarif ist 3 Pf pro Minute
+default=(0.0005, 1)
+# Taeglich zwischen 18 Uhr und 19 Uhr ist es Kostenlos
+on () between (18:00..19:00) use (0.0, 2)
+
diff --git a/kppp/Rules/Germany/Nikoma.rst b/kppp/Rules/Germany/Nikoma.rst
new file mode 100644
index 00000000..0d80e963
--- /dev/null
+++ b/kppp/Rules/Germany/Nikoma.rst
@@ -0,0 +1,34 @@
+######################################################################
+# Nikoma Internet by Call Ruleset
+# Gebuehren: 08:00 - 01:00 4.79Pf/min, 01:00 - 08:00 2.99Pf/min
+# created on 18.10.1999 by Thomas Escher
+# <thomas-escher@notesdev.de>
+# Please visit my Linux Homepage: http://www.linux-power.notrix.de
+#######################################################################
+
+name=Nikoma
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# default cost auf Maximum setzen
+default=(0.000793, 1)
+
+# Deutschlandweiter Tarif
+on (monday..sunday) between (1:00..7:59) use (0.000498, 1)
+on (monday..sunday) between (8:00..0:59) use (0.000793, 1)
+
+# Bundesweite Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
diff --git a/kppp/Rules/Germany/Nikoma_Internet_by_Call.rst b/kppp/Rules/Germany/Nikoma_Internet_by_Call.rst
new file mode 100644
index 00000000..c404d329
--- /dev/null
+++ b/kppp/Rules/Germany/Nikoma_Internet_by_Call.rst
@@ -0,0 +1,35 @@
+################################################################
+#
+# created 99/09/27 by M.Jerger
+# email:micha@mjerger.de
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Nikoma Internet by Call
+
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.0479, 60)
+on () between (01:00..08:00) use (0.0299, 60)
+
+
diff --git a/kppp/Rules/Germany/Nikoma_Study_and_Surf.rst b/kppp/Rules/Germany/Nikoma_Study_and_Surf.rst
new file mode 100644
index 00000000..a286f9f1
--- /dev/null
+++ b/kppp/Rules/Germany/Nikoma_Study_and_Surf.rst
@@ -0,0 +1,41 @@
+#################################################################################
+# German Nikoma Study & Surf - Internet by Call accounting rules
+#
+# Study & Surf is especially for students
+#
+#
+# created 99/10/14 by Claus Atzenbeck <claus.atzenbeck@stud.uni-regensburg.de>
+# based on costs of 99/10/14
+# look at http://www.nikoma.de/
+#
+# This file is based on German_Arcor_Internet_by_Call
+#################################################################################
+
+name=German_Nikoma_Study_and_Surf
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.0007983333333333, 1)
+
+# Montag bis Sonntag
+on (monday..sunday) between (8:00..20:59) use (0.0007983333333333, 1)
+on (monday..sunday) between (21:00..0:59) use (0.0006483333333333, 1)
+on (monday..sunday) between (1:00..7:59) use (0.0004983333333333, 1)
+
+# Bundeseinheitliche Feiertage (kein Unterschied zu Werktagen)
+#on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/25, 12/26) between (0:00..23:59) use (0.10, 60)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
diff --git a/kppp/Rules/Germany/Planet-Interkom_Internet_by_call.rst b/kppp/Rules/Germany/Planet-Interkom_Internet_by_call.rst
new file mode 100644
index 00000000..dbf509cb
--- /dev/null
+++ b/kppp/Rules/Germany/Planet-Interkom_Internet_by_call.rst
@@ -0,0 +1,23 @@
+#=========================================================================
+# Planet Interkom by call
+# http://www.planet-interkom.de/planet/access/access_index.asp
+#
+# Authentifikation: chap
+# Username: internet@planetbycall.de
+# Passwort: internet
+# in /etc/ppp/chap-secrets eintragen: internet@planetbycall.de * internet
+#
+# 05-Jul-2000 numbermumbler <numbermumbler@i.am> http://i.am/numbermumbler
+# 16-Aug-2000 tages-tarif korrigiert; zugangsdaten geaendert
+#=========================================================================
+name=Planet Interkom by call
+currency_position=right
+currency_symbol=DM
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.00041666666667, 1)
+on (monday..friday) between (18:00..07:59) use (0.00041666666667, 1)
+on (monday..friday) between (08:00..17:59) use (0.00053333333333, 1)
+on (easter-2, easter, easter+1, easter+39, easter+49, easter+50) between (0:00..23:59) use (0.00041666666667, 1)
+on (01/01, 05/01, 10/03, 12/24, 12/25, 12/26, 12/31) between (0:00..23:59) use (0.00041666666667, 1)
diff --git a/kppp/Rules/Germany/Puretec.rst b/kppp/Rules/Germany/Puretec.rst
new file mode 100644
index 00000000..a47dc130
--- /dev/null
+++ b/kppp/Rules/Germany/Puretec.rst
@@ -0,0 +1,13 @@
+######################################################################
+# #
+# Puretec Customer Tariff Online Rules at 05.December 2001 #
+# #
+# created 01/12/05 by Timo <timo@userland.de> #
+######################################################################
+
+name=Puretec Customer Tariff
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+default=(0.012, 60)
+
diff --git a/kppp/Rules/Germany/Telekom_City_Select_5_30.rst b/kppp/Rules/Germany/Telekom_City_Select_5_30.rst
new file mode 100644
index 00000000..d6c0636c
--- /dev/null
+++ b/kppp/Rules/Germany/Telekom_City_Select_5_30.rst
@@ -0,0 +1,48 @@
+#################################################################################
+# German Telekom accounting rules
+#
+# CityCall Select 5/30
+#
+# based on "Telekom_City.rst"
+# created 97/07/18 by Stefan Markgraf <S_Markgraf@hotmail.com>
+# modified 98/05/01 by Stefan Troeger <stefan.troeger@wirtschaft.tu-chemnitz.de>
+# modified to Select 5/30 99/10/17 by Reinhard Kunert<reinhard.kunert@gmx.de>
+#################################################################################
+
+name=German_Telekom_City_Call_Select_5_30
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.084, 90)
+
+# Montag bis Freitag
+on (monday..friday) between (5:00..8:59) use (0.084, 150)
+on (monday..friday) between (18:00..20:59) use (0.084, 150)
+on (monday..friday) between (21:00..4:59) use (0.084, 240)
+
+# Sonnabend, Sonntag
+on (saturday..sunday) between (5:00..20:59) use (0.084, 150)
+on (saturday..sunday) between (21:00..4:59) use (0.084, 240)
+
+# Bundeseinheitliche Feiertage, Heiligabend und Silvester
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/24, 12/25, 12/26, 12/31) between (5:00..20:59) use (0.121, 150)
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/24, 12/25, 12/26, 12/31) between (21:00..4:59) use (0.121, 240)
+
+# Bundeseinheitliche Feiertage:
+# 01/01 Neujahr
+# easter-2 Karfreitag
+# easter Ostersonntag
+# easter+1 Ostermontag
+# 05/01 Maifeiertag Tag der Arbeit
+# easter+39 Christi Himmelfahrt
+# easter+49 Pfingstsonntag
+# easter+50 Pfingstmontag
+# 10/03 Tag der Deutschen Einheit
+# 12/25 1. Weihnachtstag
+# 12/26 2. Weihnachtstag
+
+# Zusaetzliche Tage mit Feiertagstarif:
+# 12/24 Heiligabend
+# 12/31 Silvester
diff --git a/kppp/Rules/Germany/VR-Web.rst b/kppp/Rules/Germany/VR-Web.rst
new file mode 100644
index 00000000..78c3c651
--- /dev/null
+++ b/kppp/Rules/Germany/VR-Web.rst
@@ -0,0 +1,33 @@
+
+################################################################
+#
+# German Vr-Web
+# created 2001-12-09 by frank großmann,f.grossi@web.de
+#
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=VR-Web
+################################################################
+#
+# currency settings
+#
+################################################################
+
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+
+
+################################################################
+#
+# connection settings
+#
+################################################################
+
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.024, 60)
diff --git a/kppp/Rules/Germany/expressnet.rst b/kppp/Rules/Germany/expressnet.rst
new file mode 100644
index 00000000..1f94a67b
--- /dev/null
+++ b/kppp/Rules/Germany/expressnet.rst
@@ -0,0 +1,20 @@
+########################################################
+# expressnet hotline:01801-556556
+# internet:019256310
+# Username:expressnet
+# Password:Internet
+#
+# createt on 13.05.2001 by Stefan Zintel
+# <zintel@netcomcity.de>
+#
+########################################################
+
+name=expressnet
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+# Standardtarif ist 2,79 Pf pro Minute
+default=(0.0279, 60)
+
diff --git a/kppp/Rules/Germany/knUUt-by-Call.rst b/kppp/Rules/Germany/knUUt-by-Call.rst
new file mode 100644
index 00000000..48595939
--- /dev/null
+++ b/kppp/Rules/Germany/knUUt-by-Call.rst
@@ -0,0 +1,22 @@
+
+################################################################
+#German UUNET - Internet By Call accounting rules
+#
+#UUNET Deutschland GmbH
+#
+#knUUt-by-Call
+#
+#created 99/10/17 by Reinhard Kunert <reinhard.kunert@gmx.de>
+#costs based on "www.knuut.de/call" Sunday 99/10/17
+################################################################
+
+name=knUUt-by-Call
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.055, 60)
+
+# Abendtarif
+on (monday..sunday) between (21:00..8:59) use (0.049, 60)
diff --git a/kppp/Rules/Germany/talkline_by_call.rst b/kppp/Rules/Germany/talkline_by_call.rst
new file mode 100644
index 00000000..047c75ad
--- /dev/null
+++ b/kppp/Rules/Germany/talkline_by_call.rst
@@ -0,0 +1,19 @@
+##########################################################################
+# #
+# German talknet call by call Online Rules at 12.August 1999 #
+# #
+# created 99/08/17 by olke<olke@tfz.net> #
+# #
+# #
+# #
+##########################################################################
+
+name=talknet_by_call
+currency_symbol=DM
+currency_position=right
+currency_digits=3
+
+default=(0.144, 180)
+
+# Montag bis Sonntag
+on (monday..sunday) between (0:00..23:59) use (0.144, 180)
diff --git a/kppp/Rules/Germany/vossnet_fun.rst b/kppp/Rules/Germany/vossnet_fun.rst
new file mode 100644
index 00000000..37e76476
--- /dev/null
+++ b/kppp/Rules/Germany/vossnet_fun.rst
@@ -0,0 +1,19 @@
+##########################################################################
+# #
+# German vossnet Fun Online Rules at 12.October 1999 #
+# #
+# created 99/08/17 by olke<olke@tfz.net> #
+# #
+# #
+# #
+##########################################################################
+
+name=vossnet Fun
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+default=(0.06, 60)
+
+# Montag bis Sonntag
+on (monday..sunday) between (9:00..18:00) use (0.06, 60)
+
diff --git a/kppp/Rules/Germany/vossnet_fun_light.rst b/kppp/Rules/Germany/vossnet_fun_light.rst
new file mode 100644
index 00000000..e63dd76b
--- /dev/null
+++ b/kppp/Rules/Germany/vossnet_fun_light.rst
@@ -0,0 +1,19 @@
+##########################################################################
+# #
+# German vossnet Fun-Light Online Rules at 12.October 1999 #
+# #
+# created 99/08/17 by olke<olke@tfz.net> #
+# #
+# #
+# #
+##########################################################################
+
+name=vossnet Fun Light
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+default=(0.06, 60)
+
+# Montag bis Sonntag
+on (monday..sunday) between (9:00..18:00) use (0.06, 60)
+
diff --git a/kppp/Rules/Germany/vossnet_kompl.rst b/kppp/Rules/Germany/vossnet_kompl.rst
new file mode 100644
index 00000000..cdd80c13
--- /dev/null
+++ b/kppp/Rules/Germany/vossnet_kompl.rst
@@ -0,0 +1,29 @@
+##########################################################################
+# #
+# German vossnet-Komplett Online Rules at 11.October 1999 #
+# #
+# created 99/10/11 by olke<olke@tfz.net> #
+# #
+# der Tarif, welchen man mit Strom- und/oder Telefon-Vertrag bekommt #
+# #
+##########################################################################
+
+name=vossnet-komplett
+currency_symbol=DM
+currency_position=right
+currency_digits=2
+default=(0.12, 90)
+
+# Montag bis Freitag
+on (monday..friday) between (9:00..18:00) use (0.12, 90)
+on (monday..friday) between (18:00..21:00) use (0.12, 150)
+on (monday..friday) between (21:00..5:00) use (0.12, 240)
+
+# Wochenende
+on (saturday..sunday) between (21:00..5:00) use (0.12, 240)
+on (saturday..sunday) between (5:00..21:00) use (0.12, 150)
+
+# Feiertage ein Dankeschön an Joachim Wesner,Stefan Markgraf,Stefan Troeger für die Feiertage
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/24, 12/25, 12/26, 12/31) between (21:00..5:00) use (0.12, 240)
+on (01/01, easter-2, easter, easter+1, 05/01, easter+39, easter+49, easter+50, 10/03, 12/24, 12/25, 12/26, 12/31) between (5:00..21:00) use (0.12, 150)
+
diff --git a/kppp/Rules/Greece/Hellas_EPAK_Zone1_in_euro.rst b/kppp/Rules/Greece/Hellas_EPAK_Zone1_in_euro.rst
new file mode 100644
index 00000000..d6058d37
--- /dev/null
+++ b/kppp/Rules/Greece/Hellas_EPAK_Zone1_in_euro.rst
@@ -0,0 +1,56 @@
+################################################################
+# NOTES:
+# These rules were made at May 25th, 2001
+# These rules are valid since March, 2001
+# You can check for changes in prices at http://www.ote.gr/
+# These rules apply to you if you are accessing an ISP using an EPAK or PEAK
+# number (0965-) in a city that is up to 45 km from your own, or in the same perfecture.
+# The costs for an EPAK Zone 1 long distance phone-call are:
+# prices are in euro, with the convention 10,5 drs = 0.03084 euro
+# 1 Unit (0.03084) every 1 minute (60secs).
+# Exception1: 22:00 - 08:00 :1 Unit every 630 sec.
+# mail d.kamenopoulos@mail.ntua.gr for comments.
+################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+################################################################
+name=Hellas_EPAK_Zone1_in_euro.rts
+
+################################################################
+# currency settings
+################################################################
+
+# defines DRS (Hellenic draxmi) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=euro
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is the default rule which is used when no other rule
+# applies. The first component "10.5" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.03084
+# euro are added to the bill"
+default=(0.03084, 60)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 22:00 until 08:00 the costs
+# are 0.03084 drs each 630 seconds"
+on (monday..sunday) between (22:00..08:00) use (0.03084, 630)
+
diff --git a/kppp/Rules/Greece/Hellas_EPAK_Zone2_in_euro.rst b/kppp/Rules/Greece/Hellas_EPAK_Zone2_in_euro.rst
new file mode 100644
index 00000000..cc4e0b45
--- /dev/null
+++ b/kppp/Rules/Greece/Hellas_EPAK_Zone2_in_euro.rst
@@ -0,0 +1,51 @@
+################################################################
+# NOTES:
+# these rules were made at May 25th 2001 (25/2/2001)
+# they are valid since March 2001
+# prices are in Euro. 10.5 drs = 0.03084 euro
+################################################################
+# Dimitris Kamenopoulos, el97146@mail.ntua.gr
+################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+################################################################
+name=Hellas_EPAK_Zone2_in_euro.rts
+
+################################################################
+# currency settings
+################################################################
+
+# defines Euro to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=euro
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is the default rule which is used when no other rule
+# applies. The first component "10.5" is the price of one
+# "unit", while "0.03084" is the duration in seconds.
+# Therefore the following rule means: "Every 22.5 seconds 0.03084
+# euro are added to the bill"
+default=(0.03084, 22.5)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 22:00 until 08:00 the costs
+# are 0.03084 each 630 seconds"
+on (monday..sunday) between (22:00..08:00) use (0.03084, 630)
+
diff --git a/kppp/Rules/Greece/Hellas_EPAK_local_in_euro.rst b/kppp/Rules/Greece/Hellas_EPAK_local_in_euro.rst
new file mode 100644
index 00000000..ef140040
--- /dev/null
+++ b/kppp/Rules/Greece/Hellas_EPAK_local_in_euro.rst
@@ -0,0 +1,64 @@
+################################################################
+# NOTES:
+# These rules were made at May 25th, 2001 (25/5/2001)
+# These rules are valid since March, 2001
+# You can check for changes in prices at http://www.ote.gr/
+# These rules apply to you if you are accessing an ISP using an EPAK or PEAK
+# number (0965-) in the same city as you.
+# H an o ISP vrisketai stin prwtevousa tou Nomou sas (plin
+# Aitwloakarnanias kai Argolidas)
+# The costs for an EPAK local phone-call are:
+# 1 Unit (0.03084 euro) every 315 sec.
+# Exception: 22:00 - 08:00 :1 Unit every 630 sec .
+# prices are in euro. I assumed
+# 10.5 drs = 0.03084 though 10.5drs = 0.030843.... euro
+# this leads to an underestimation of the costs by less than
+# 1 Euro per year, say 50 Hrs of dialup usage per week.
+# email me if you think something should be corrected.
+# creator:
+# Dimitris Kamenopoulos, d.kamenopoulos@mail.ntua.gr
+# studying Electr. and Computer Engineering in National Technical
+# University of Athens
+################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+################################################################
+name=Hellas_EPAK_local_in_euro.rts
+
+################################################################
+# currency settings
+################################################################
+
+# defines euro to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=euro
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.03084" is the price of one
+# "unit", while "315" is the duration in seconds.
+# Therefore the following rule means: "Every 315 seconds 0.03084
+# euro is added to the bill"
+default=(0.03084, 315)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 22:00 until 08:00 the costs
+# are half as above"
+on (monday..sunday) between (22:00..08:00) use (0.03084, 630)
diff --git a/kppp/Rules/Greece/Hellas_analog_local_in_euro.rst b/kppp/Rules/Greece/Hellas_analog_local_in_euro.rst
new file mode 100644
index 00000000..88e7c5d0
--- /dev/null
+++ b/kppp/Rules/Greece/Hellas_analog_local_in_euro.rst
@@ -0,0 +1,45 @@
+################################################################
+# NOTES:
+# You can check for changes in prices at http://www.ote.gr/
+# These rules apply to you if you are accessing an ISP without using an EPAK or PEAK
+# number (0965-) in the same city as you, from an analog phone (lucky you).
+# The costs for an analog local phone-call are:
+# 0.03084 euro per connection.
+################################################################
+# Dimitris Kamenopoulos d.kamenopoulos@mail.ntua.gr
+################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+################################################################
+name=Hellas_analog_local_in_euro.rts
+
+################################################################
+# currency settings
+################################################################
+
+# defines DRS (Hellenic draxmi) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=euro
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.03084
+
+# This is the default rule which is used when no other rule
+# applies.
+default=(0.0, 0)
diff --git a/kppp/Rules/Greece/Hellas_digital_local_in_euro.rst b/kppp/Rules/Greece/Hellas_digital_local_in_euro.rst
new file mode 100644
index 00000000..6545d393
--- /dev/null
+++ b/kppp/Rules/Greece/Hellas_digital_local_in_euro.rst
@@ -0,0 +1,43 @@
+################################################################
+# NOTES:
+# You can check for changes in prices at http://www.ote.gr/
+# These rules apply to you if you are accessing an ISP without using an EPAK or PEAK
+# number (0965-) in the same city as you, from a digital phone.
+# The costs for a digital local phone-call are:
+# 1 Unit (0.03084 euro) every 1 minute (60 secs).
+# Exception: 22:00 - 08:00 :1 Unit every 2 minutes (120secs).
+################################################################
+# Dimitris Kamenopoulos el97146@mail.ntua.gr
+################################################################
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+################################################################
+name=Hellas_digital_local_in_euro.rts
+
+################################################################
+# currency settings
+################################################################
+
+# defines euro to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=euro
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+
+default=(0.03084, 60)
+
+on (monday..sunday) between (22:00..08:00) use (0.03084, 120)
diff --git a/kppp/Rules/Greece/Makefile.am b/kppp/Rules/Greece/Makefile.am
new file mode 100644
index 00000000..e665b1b6
--- /dev/null
+++ b/kppp/Rules/Greece/Makefile.am
@@ -0,0 +1,9 @@
+pkg_DATA = Hellas_digital_local_in_euro.rst \
+ Hellas_EPAK_Zone1_in_euro.rst \
+ Hellas_EPAK_Zone2_in_euro.rst \
+ Hellas_EPAK_local_in_euro.rst \
+ Hellas_analog_local_in_euro.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Greece
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/HongKong/Hong_Kong_Telecom.rst b/kppp/Rules/HongKong/Hong_Kong_Telecom.rst
new file mode 100644
index 00000000..9fcbec03
--- /dev/null
+++ b/kppp/Rules/HongKong/Hong_Kong_Telecom.rst
@@ -0,0 +1,18 @@
+name=Hong Kong Telecom
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=HK$
+currency_position=right
+currency_digits=4
+
+
+
+################################################################
+# connection settings
+################################################################
+per_connection=0
+minimum_costs=0
+default=(0.0007, 1)
+
diff --git a/kppp/Rules/HongKong/Makefile.am b/kppp/Rules/HongKong/Makefile.am
new file mode 100644
index 00000000..6d60772f
--- /dev/null
+++ b/kppp/Rules/HongKong/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = Hong_Kong_Telecom.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Hong_Kong
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Hungary/Local.rst b/kppp/Rules/Hungary/Local.rst
new file mode 100644
index 00000000..7c1b05bc
--- /dev/null
+++ b/kppp/Rules/Hungary/Local.rst
@@ -0,0 +1,42 @@
+################################################################
+#
+# Local calls in Hungary
+# Valid all way in 1999 before 31Th March (I really hope)
+# by Laszlo Megyer <abulla@mail.elender.hu> from Hungary, Szekesfehervar
+# I like motorcycles, hacking and Linux.
+#
+# Please DON'T erase anything from this document, instead of correcting
+# mistakes in the sentences and rules. Thanks.
+#
+################################################################
+name=Hungary_local
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=Ft
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=8.25
+minimum_costs=0
+
+default=(1.75, 10)
+
+on (monday..friday) between (0:00..4:59) use (0.4383, 10)
+on (monday..friday) between (5:00..6:59) use (0.43833, 10)
+on (monday..friday) between (7:00..17:59) use (1.75, 10)
+on (monday..friday) between (18:00..21:59) use (0.43833, 10)
+on (monday..friday) between (22:00..23:59) use (0.4383, 10)
+
+on (saturday..sunday) between (0:00..4:59) use (0.4383, 10)
+on (saturday..sunday) between (5:00..21:59) use (0.6883, 10)
+on (saturday..sunday) between (22:00..23:59) use (0.4383, 10)
+
+# Well, there is a maximum cost of 150 Ft if the call is started any day after
+# 18:00 and finished before 7:00 or it is started after 5:00 at weekend and
+# finished before 22:00.
diff --git a/kppp/Rules/Hungary/LocalTop_MATAV.rst b/kppp/Rules/Hungary/LocalTop_MATAV.rst
new file mode 100644
index 00000000..67268f8d
--- /dev/null
+++ b/kppp/Rules/Hungary/LocalTop_MATAV.rst
@@ -0,0 +1,67 @@
+###################################################################
+#
+# B I G F A T W A R N I N G ! ! !
+#
+# T h i s i s n o t a r e a l l y f u n c t i o n a l
+# r s t f i l e
+#
+###################################################################
+
+################################################################
+#
+# Local calls in Hungary in MATAV districts
+# tarifs based on secundums!!!!! (processor eating settings)
+# Valid all way in 1999 (I hope)
+# by Gabor Jant <jamm@mail.interware.hu> (this will change soon)
+#
+################################################################
+name=Hungary_MATAV_local
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=Ft
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+per_connection=8.25
+minimum_costs=0
+default=(0.04375,1)
+
+#######################default=(0.175, 1)
+
+on (monday..friday) between (0:00..4:59) use (0.04375, 1) max (150)
+on (monday..friday) between (5:00..6:59) use (0.06875, 1) max (150)
+
+# in this peek hours interwall there is not a maximum per call....
+on (monday..friday) between (7:00..17:59) use (0.175, 1) max ()
+
+on (monday..friday) between (18:00..21:59) use (0.06875, 1) max (150)
+on (monday..friday) between (22:00..23:59) use (0.04375, 1) max (150)
+
+on (saturday..sunday) between (0:00..4:59) use (0.04375, 1) max (150)
+on (saturday..sunday) between (5:00..21:59) use (0.06875, 1) max (150)
+on (saturday..sunday) between (22:00..23:59) use (0.04375, 1) max (150)
+
+#############################################################
+# hours |2 2#0 | | 1 |1 2 |#
+# of day |2 3 4#1 2 3 4 5|6 7|8 9 0 1 2 3 4 5 6 7|8 9 0 1|#
+#############################################################
+#monday | | | | |#
+#..........| | | #
+#tuesday | 8.25/call | | 8.25 per call | |#
+#..........| | | #
+#wednesday | + | | + | |#
+#..........| | | #
+#thursday | 2.63/60sec | | 10.50 / 60 sec | |#
+#..........| | | #
+#friday | but | | | |#
+#..........| max --------------------- #
+#saturday | 150 | 8.25 per call + |#
+#..........| per call ---> 4.13 / 60 sec #
+#sunday | | but max 150 per call ------>
+#############################################################
diff --git a/kppp/Rules/Hungary/Local_MATAV.rst b/kppp/Rules/Hungary/Local_MATAV.rst
new file mode 100644
index 00000000..702a3e55
--- /dev/null
+++ b/kppp/Rules/Hungary/Local_MATAV.rst
@@ -0,0 +1,35 @@
+################################################################
+#
+# Local calls in Hungary in MATAV districts
+# tarifs based on 10 seconds
+# Valid all way in 1999 (I hope)
+# by Gabor Jant <jamm@mail.interware.hu> (this will change soon)
+#
+################################################################
+name=Hungary_MATAV_local
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=Ft
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+per_connection=8.25
+minimum_costs=0
+
+default=(1.75, 10)
+
+on (monday..friday) between (0:00..4:59) use (0.4375, 10)
+on (monday..friday) between (5:00..6:59) use (0.6875, 10)
+on (monday..friday) between (7:00..17:59) use (1.75, 10)
+on (monday..friday) between (18:00..21:59) use (0.6875, 10)
+on (monday..friday) between (22:00..23:59) use (0.4375, 10)
+
+on (saturday..sunday) between (0:00..4:59) use (0.4375, 10)
+on (saturday..sunday) between (5:00..21:59) use (0.6875, 10)
+on (saturday..sunday) between (22:00..23:59) use (0.4375, 10)
diff --git a/kppp/Rules/Hungary/Makefile.am b/kppp/Rules/Hungary/Makefile.am
new file mode 100644
index 00000000..cf6a726d
--- /dev/null
+++ b/kppp/Rules/Hungary/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = Local.rst \
+ Local_MATAV.rst \
+ LocalTop_MATAV.rst \
+ PapaTel.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Hungary
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Hungary/PapaTel.rst b/kppp/Rules/Hungary/PapaTel.rst
new file mode 100644
index 00000000..f8929083
--- /dev/null
+++ b/kppp/Rules/Hungary/PapaTel.rst
@@ -0,0 +1,51 @@
+################################################################
+#
+# For Hungarian text scroll down
+# Internet calls in the areacode 89 in Hungary
+# Phone number: 0651389301
+# Valid by 1st March 2001
+# By Miklos Pataki <pataki.miklos.1@freemail.hu> from Hungary, Papa
+# Date: 10th Feb 2002
+#
+# If you find some bugs or if you have any questions
+# please feel free to send me an e-mail
+#
+################################################################
+name=PapaTel
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=Ft
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+default=(0.17, 1)
+
+on (monday..friday) between (18:00..6:59) use (0.05, 1)
+on (saturday..sunday) between () use (0.05, 1)
+
+#public holidays
+on (01/01, 03/15, 05/01, 08/20, 10/23, 11/01, 12/25, 12/26) between () use (0.05, 1)
+on (easter+1) between () use (0.05, 1)
+on (easter+50) between () use (0.05, 1)
+
+################################################################
+#
+# Internet hivasok a 89-es korzetben Magyarorszagon (PapaTel korzete)
+# Behivoszam: 0651389301
+# Ervenyes: 2001. marc. 1-tol
+# Keszitette: Pataki Miklos <pataki.miklos.1@freemail.hu> Papan
+# Datum: 2002. feb. 10.
+#
+# Ha hibat eszlel vagy kerdese van
+# kerem irjon nekem egy e-mail -t
+#
+################################################################ \ No newline at end of file
diff --git a/kppp/Rules/Iceland/Iceland_general.rst b/kppp/Rules/Iceland/Iceland_general.rst
new file mode 100644
index 00000000..2c40cd07
--- /dev/null
+++ b/kppp/Rules/Iceland/Iceland_general.rst
@@ -0,0 +1,110 @@
+################################################################
+# #
+# Phone cost rules for Landsimi Islands (Iceland Telecom) #
+# #
+# Authors: Thorarinn R. Einarsson (thori@mindspring.com) #
+# Bjarni R. Einarsson (bre@netverjar.is) #
+# #
+# Created on February 2, 1999 #
+# Last updated on 5. September, 1999 #
+# #
+# Ath. Höfundar bera EKKI ábyrgð á röngum útreikningum ! #
+# #
+# Vitað er að útreikningar eru ónákvæmir, því Landssíminn #
+# rukkar fyrir skrefið, ekki mínútuna. Þessi gjaldskrá miðast #
+# hinsvegar við mínútur, og byggir á upplýsingum úr síma- #
+# skránni frá 1999. #
+# #
+################################################################
+
+### Name of ruleset ###
+name=Island_almennur_taxti
+
+### Currency symbol ### (ekki það alþjóðlega heldur það sem birtist)
+currency_symbol=kr.
+
+# Define the position of the currency symbol.
+currency_position=right
+
+# Define the number of significant digits.
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+#
+# Kostnaður við tengingu (1 skref)
+#
+
+per_connection=3.32
+
+#
+# Lágmarkskostnaður (þarf ekki að nota því 3.32 kemur á hverja tengingu)
+#
+
+minimum_costs=0.0
+
+#
+# Sjálfgefinn kostnaður - Alla virka daga á daginn.
+# Virka daga bætast 1.56 kr. við á hverjum 60 sekúndum
+# (eða 128 sek. í skrefi)
+#
+default=(1.56, 60)
+
+
+###################
+# Aukareglurnar: #################################
+###################
+
+#
+# Kvöld og næturtaxti alla virka daga
+#
+# 0.78 kr. á mín. (eða 256 sek. í skrefi)
+#
+
+on (monday..friday) between (19:00..07:59) use (0.78, 60)
+
+#
+# Helgartaxti
+#
+# Sama og K&N taxti eða 0.78 kr. á mín.
+#
+
+on (saturday..sunday) between () use (0.78, 60)
+
+
+#
+# Hátíðisdagar: Nota kvöld- og næturtaxta
+#
+# Jóladagur
+#
+
+on (12/25) between () use (0.78, 60)
+
+#
+# Hátíðisdagar nota kvöld- og næturtaxta
+#
+# Hér eru nokkrir...
+
+on (12/1, 12/24..12/26, 12/31, 01/01, 05/01, 06/17) between () use (0.78, 60)
+
+#
+# og aðrir tengdir páskum
+#
+
+# Skírdagur
+on (easter-3) between() use (0.78, 60)
+
+# Föstudagurinn langi
+on (easter-2) between() use (0.78, 60)
+
+# Annar í Páskum
+on (easter+1) between () use (0.78, 60)
+
+# Hvítasunnudagur
+on (easter+50) between () use (0.78, 60)
diff --git a/kppp/Rules/Iceland/Makefile.am b/kppp/Rules/Iceland/Makefile.am
new file mode 100644
index 00000000..a000983b
--- /dev/null
+++ b/kppp/Rules/Iceland/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = Iceland_general.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Iceland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/India/BSNL_Local.rst b/kppp/Rules/India/BSNL_Local.rst
new file mode 100644
index 00000000..1f6008a3
--- /dev/null
+++ b/kppp/Rules/India/BSNL_Local.rst
@@ -0,0 +1,113 @@
+#####################################################################
+#
+# This is the latest rule set for Bharat Sanchar Nigam Limited,
+# It works arround 50 k.m. from any major cities in India.
+# This rule set is for M-Call to Internet Service Provider,
+# (numbers dialing with the 17XXXX format).
+# This revised tariff is applicable from 1st May 2003.
+# This is based on the BSNL Alternate packages and is applicable
+# to all the subcatogaries in this package scheme.
+#
+# Name : Thomas Mathew
+# email : thomasmathewk@netscape.net
+#
+#
+# NOTE: THIS IS MADE UNDER THE LATEST INDIAN TELECOM TARIFF.
+# I AM NOT RESPONSIBLE FOR ANY DAMAGE DUE TO USE OF THIS RULE SET.
+#
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+#####################################################################
+
+
+#####################################################################
+#
+# BSNL_local.rst Applicable only arround 50 k.m of any major
+# cities in India. Numbers dialing with 17XXXX format without
+# any prefix.
+#
+# Salient features of Revised Telecom Tariff.
+# Local call charging 1 unit per 3 minutes during peak hours.
+# Peak hours - 06:30AM to 10:30PM.
+# Local call charging 1 unit per 10 minutes during Off peak hours.
+# Off peak hours - 10:30PM to 06:30AM.
+# Off peak rates are applicable for 24 hrs. on National Holidays
+# & Sundays.
+# Unit of call charge Rs.1.20 per unit
+#####################################################################
+name=default
+
+#####################################################################
+# Rs.1.00= Paise100
+#####################################################################
+
+# defines Rs. (Indian Rupee) to be used as currency
+# symbol (default = "$")
+currency_symbol=Rs.
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+#####################################################################
+# connection settings
+#####################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=1.20
+
+
+# You pay 1.20 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+# flat_init_costs=(1.20, 180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "1.20" is the price of one
+# "unit", while "180" is the duration in seconds.
+# Therefore the following rule means: "Every 180 seconds 1.20
+# Rs. are added to the bill"
+# default=(1.20, 180)
+
+#
+# more complicated rules:
+#
+
+# On peak hours from 06:30AM until 10:30PM the costs
+# are 1.20 each 180 seconds.
+on (monday..sunday) between (06:30..22:30) use (1.20, 180)
+# On Off peak hours from 10:30PM until 06:30AM the costs
+# are 1.20 each 600 seconds.
+on (monday..sunday) between (22:30..06:30) use (1.20, 600)
+# On all the Sundays and the three National Holidays
+# viz. Republic Day (Jan 26th), Independance Day (Aug 15th)
+# and Mahatma Gandhi's Birthday (Oct 2nd)the costs
+# are 1.20 each 600 seconds.
+on (sunday, 1/26, 8/15, 10/2) between () use (1.20,600)
+#
+# NOTE: This rule set is applicable only if your system date and
+# time are correct to your local settings.
+# End of Rules.
+# Updates to this rule set and suggessions are most welcome.
+#####################################################################
+
diff --git a/kppp/Rules/India/BSNL_Long_101_To_200.rst b/kppp/Rules/India/BSNL_Long_101_To_200.rst
new file mode 100644
index 00000000..bd4590e9
--- /dev/null
+++ b/kppp/Rules/India/BSNL_Long_101_To_200.rst
@@ -0,0 +1,86 @@
+################################################################
+#
+# This is the latest rule set for Bharat Sanchar Nigam Limited,
+# It works between 101k.m. to 200k.m. from any major cities in
+# India. This revised tariff is applicable from 01-02-2001.
+#
+# Name : Rex
+# email : rex@caltiger.com
+#
+#
+# NOTE: THIS IS MADE UNDER LATEST INDIAN TELECOM TARIFF. I AM
+# NOT RESPONSIBLE FOR ANY DAMAGE DUE TO USE OF THIS RULE SET.
+#
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# BSNL_Long_101_To_200.rst Applicable between 101 k.m to 200k.m.
+# from any major cities in India.
+#
+# Salient features of Revised Telecom Tariff.
+# Medium-long distance (101k.m. - 200k.m.) call charging 1 unit
+# per 30 seconds.
+# Unit of call charge Rs.1.20 per unit.
+# for all types of charges : Service Tax @ 5% to be paid extra.
+################################################################
+name=default
+
+################################################################
+# Rs.1.00= Paise100
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=Rs.
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(1.26, 30)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(1.26, 30)
+
+#
+# more complicated rules:
+#
diff --git a/kppp/Rules/India/BSNL_Medium_51_To_100.rst b/kppp/Rules/India/BSNL_Medium_51_To_100.rst
new file mode 100644
index 00000000..65241e62
--- /dev/null
+++ b/kppp/Rules/India/BSNL_Medium_51_To_100.rst
@@ -0,0 +1,86 @@
+################################################################
+#
+# This is the latest rule set for Bharat Sanchar Nigam Limited,
+# It works between 51 k.m. to 100k.m. from any major cities in
+# India. This revised tariff is applicable from 01-02-2001.
+#
+# Name : Rex
+# email : rex@caltiger.com
+#
+#
+# NOTE: THIS IS MADE UNDER LATEST INDIAN TELECOM TARIFF. I AM
+# NOT RESPONSIBLE FOR ANY DAMAGE DUE TO USE OF THIS RULE SET.
+#
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# BSNL_Medium_51_To_100.rst Applicable between 51 k.m to 101k.m.
+# from any major cities in India.
+#
+# Salient features of Revised Telecom Tariff.
+# Medium distance (51k.m.-100k.m.) calls charging 1 unit per 2
+# minutes.
+# Unit of call charge Rs.1.20 per unit.
+# for all types of charges : Service Tax @ 5% to be paid extra.
+################################################################
+name=default
+
+################################################################
+# Rs.1.00= Paise100
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=Rs.
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(1.26, 120)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(1.26, 120)
+
+#
+# more complicated rules:
+#
diff --git a/kppp/Rules/India/Makefile.am b/kppp/Rules/India/Makefile.am
new file mode 100644
index 00000000..a7f85ba1
--- /dev/null
+++ b/kppp/Rules/India/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = Vsnl_local.rst BSNL_Local.rst \
+ BSNL_Long_101_To_200.rst \
+ BSNL_Medium_51_To_100.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/India
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/India/Vsnl_local.rst b/kppp/Rules/India/Vsnl_local.rst
new file mode 100644
index 00000000..914e4e0f
--- /dev/null
+++ b/kppp/Rules/India/Vsnl_local.rst
@@ -0,0 +1,102 @@
+################################################################
+# India's Vsnl Accounting rules (for local phone dial up connections).
+# Created 18th June, 1999 by Madhusudan Singh (kchhabra@ch1.dot.net.in)
+# Valid till : next second or next great civilization (as your judgement
+# thinks !!!)
+################################################################
+#DISCLAIMER:
+#There is no guarantee that these rules are valid for all users.
+#They work for me and I can't care more. In case, you find another rule
+#set appropriate to your region, please mail it to wuebben@kde.org
+#I do not accept any responsibility for any damage/inconvenience caused by
+#the use of these rules. Use at your own risk.
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=vsnl
+
+################################################################
+# currency settings
+################################################################
+#defines Indian rupees as the currency.
+# Re 1/- = 100 paise.
+#Plural for Re. is Rs.
+#Paise is the lowest unit of coinage (Though it is hard to find any change
+#in the market, I think that we should believe the GOI that it is really the lowest
+#monetary unit)
+# symbol (not absolutely needed, default = "Rs")
+currency_symbol=Rs.
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+#flat_init_costs=(1.25,120)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "1.40" is the price of one
+# "unit", while "180" is the duration in seconds.
+# Therefore the following rule means: "Every 180 seconds 1.40
+# Rupees are added to the bill"
+default=(1.40, 180)
+
+# This is a major simplification.
+# Local phone call rates vary with the number of local calls made.
+# I am assuming the worst case scenario.
+# Upto 150 phone calls : Free !
+# 151-500 phone calls : 0.80
+# 501-1000 phone calls : 1.00
+# 1001-2000 phone calls : 1.25
+# >2000 phone calls : 1.40
+
+#I have not attempted to code this as it is possible that you are using
+# your phone line for ordinary voice calls/fax as well. In that case, it would
+# really be fajool di gall to code all that.
+
+#This rule set also does not do justice to those unlucky ones amongst us who have to
+#dial an outstation STD number to get to the nearest VSNL/DOT node. For them : aape karo !
+#Ate navin .rst file Wuebben nu ghall deo. (wuebben@kde.org).
+#Rabb Rakha.
+#
+# more complicated rules:
+#
+
+# The "on()" rules above all relates to current time only. You can also
+# make a rule depend on the number of seconds you have been connected
+# by specifying this time as a third argument to "use()".
+# For instance, let's say normal rate in the evening is 0.20 per minute,
+# and it drops by 20% after one hour of connect time. This can be modelled
+# like:
+
+#on () between (19:30..08:00) use (0.20, 60)
+#on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# Note that these rules, just like other rules, are sensitive to the
+# order in which they appear.
+
diff --git a/kppp/Rules/Indonesia/Lokal_1_Metropolitan.rst b/kppp/Rules/Indonesia/Lokal_1_Metropolitan.rst
new file mode 100644
index 00000000..c9c71611
--- /dev/null
+++ b/kppp/Rules/Indonesia/Lokal_1_Metropolitan.rst
@@ -0,0 +1,60 @@
+################################################################
+# This is the rule set for Indonesia local calls,
+# including tax, according to Petunjuk Telepon Bandung
+# Juni 1998-1999
+# by Priyadi Iman Nurcahyo <priyadi@priyadi.ml.org>
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Lokal-1-metropolitan
+
+################################################################
+# currency settings
+################################################################
+
+# defines IDR (Indonesian Rupiah) to be used as currency
+currency_symbol=IDR
+
+# Define the position of the currency symbol.
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=0
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=160
+
+
+# This is the default rule which is used when no other rule
+# applies. The first component "160" is the price of one
+# "unit", while "120" is the duration in seconds.
+# Therefore the following rule means: "Every 120 seconds 160
+# IDR are added to the bill"
+default=(160, 120)
+
+#
+# more complicated rules:
+#
+
+on () between (00:00..08:59) use (160, 180)
+on () between (09:00..14:59) use (160, 120)
+on () between (15:00..23:59) use (160, 180)
+
diff --git a/kppp/Rules/Indonesia/Lokal_2_Metropolitan.rst b/kppp/Rules/Indonesia/Lokal_2_Metropolitan.rst
new file mode 100644
index 00000000..b437842c
--- /dev/null
+++ b/kppp/Rules/Indonesia/Lokal_2_Metropolitan.rst
@@ -0,0 +1,60 @@
+################################################################
+# This is the rule set for Indonesia local calls,
+# including tax, according to Petunjuk Telepon Bandung
+# Juni 1998-1999
+# by Priyadi Iman Nurcahyo <priyadi@priyadi.ml.org>
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Lokal-non-metropolitan
+
+################################################################
+# currency settings
+################################################################
+
+# defines IDR (Indonesian Rupiah) to be used as currency
+currency_symbol=IDR
+
+# Define the position of the currency symbol.
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=0
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=160
+
+
+# This is the default rule which is used when no other rule
+# applies. The first component "160" is the price of one
+# "unit", while "120" is the duration in seconds.
+# Therefore the following rule means: "Every 120 seconds 160
+# IDR are added to the bill"
+default=(160, 120)
+
+#
+# more complicated rules:
+#
+
+on () between (00:00..08:59) use (160, 120)
+on () between (09:00..14:59) use (160, 90)
+on () between (15:00..23:59) use (160, 120)
+
diff --git a/kppp/Rules/Indonesia/Lokal_non_metropolitan.rst b/kppp/Rules/Indonesia/Lokal_non_metropolitan.rst
new file mode 100644
index 00000000..7b293d10
--- /dev/null
+++ b/kppp/Rules/Indonesia/Lokal_non_metropolitan.rst
@@ -0,0 +1,60 @@
+################################################################
+# This is the rule set for Indonesia local calls,
+# including tax, according to Petunjuk Telepon Bandung
+# Juni 1998-1999
+# by Priyadi Iman Nurcahyo <priyadi@priyadi.ml.org>
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Lokal-non-metropolitan
+
+################################################################
+# currency settings
+################################################################
+
+# defines IDR (Indonesian Rupiah) to be used as currency
+currency_symbol=IDR
+
+# Define the position of the currency symbol.
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=0
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=160
+
+
+# This is the default rule which is used when no other rule
+# applies. The first component "160" is the price of one
+# "unit", while "120" is the duration in seconds.
+# Therefore the following rule means: "Every 120 seconds 160
+# IDR are added to the bill"
+default=(160, 120)
+
+#
+# more complicated rules:
+#
+
+on () between (00:00..08:59) use (160, 180)
+on () between (09:00..14:59) use (160, 120)
+on () between (15:00..23:59) use (160, 180)
+
diff --git a/kppp/Rules/Indonesia/Makefile.am b/kppp/Rules/Indonesia/Makefile.am
new file mode 100644
index 00000000..1de8c5c6
--- /dev/null
+++ b/kppp/Rules/Indonesia/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = Lokal_1_Metropolitan.rst \
+ Lokal_non_metropolitan.rst \
+ Lokal_2_Metropolitan.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Indonesia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Ireland/Eircom_Internet.rst b/kppp/Rules/Ireland/Eircom_Internet.rst
new file mode 100644
index 00000000..9aadfdae
--- /dev/null
+++ b/kppp/Rules/Ireland/Eircom_Internet.rst
@@ -0,0 +1,164 @@
+################################################################
+#
+# This is a kppp ruleset for Eircom (formerly Telecom Eireann)
+# for special-rate Internet 1891 calls ONLY.
+#
+# Unbelievably, Eircom has now dropped the former (ludicrously
+# irrelevant) distance-based charge-bands on direct-dialled calls.
+# They still remain for operator-connected calls but these are
+# (a) a rarity and (b) unusable for modems anyway. Calls in Ireland
+# are therefore in one of the following categories:
+#
+# 1. Local calls
+# 2. Special-rate Internet calls (ISPs with 1891 numbers)
+# 3. National calls (ie all other trunk or long-distance calls)
+#
+# Note that some Telcos offer special deals of a fixed-rate per-month
+# charge which gives you unlimited, uncharged local calls in off-peak
+# times. At other times, your standard Telco rates apply.
+#
+# "Local" is as hard to define as in any other Telco administration,
+# as it can cross area codes, even when they are in different regions,
+# in order to allow people to call their neighbours 100 yards away
+# even though they may technically be in an area code which would
+# normally qualify as "long-distance", because such calls don't go
+# onto the trunk, just the local exchange.
+#
+# Note all values here include Value-Added Tax at 21% current
+# at 31-Dec-1999
+#
+# Peter Flynn <peter@silmaril.ie>
+################################################################
+
+name=Ireland_Eircom_Internet
+
+# Define IEP (Irish Pounds) to be used as currency symbol
+# ??? There is no way to define the currency code AND the symbol !!!
+# WARNING this will have to be changed to EUR from 2002-01-01
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+# ??? Curious default, why not left, which is _way_ more common? !!!
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# It costs 11.5p the moment a call connects. This covers the first
+# 450 secs (peak hours, 8am-6pm M-F) or first 15 mins (off-peak)
+per_connection=0.115
+
+# Therefore the minimum cost is the same as the per-connection cost
+minimum_costs=0.115
+
+# Therefore the first 450 secs costs this much no matter what.
+flat_init_costs=(0.115,450)
+# A pity there's no peak/offpeak differential for this one.
+
+# All subsequent charging is done per-second, based on the unit
+# charge of 11.5p for 450 secs (peak hours) or 11.5p for 15mins
+# (off-peak), which works out at £0.00025556/sec and £0.000127778p/sec
+# respectively...that's what they claim, anyway.
+
+# Rather than expect kppp to check the rate every second and add
+# tiny fractions, I've expressed these rates in terms of the amount
+# needed to clock up half a penny (or the closest amount exceeding
+# that value obtainable by multiplying the per-second rate by an
+# integer). Not a whole penny, because you may be damn certain the
+# bean-counters will round up half-penny amounts to the nearest
+# whole penny anyway (anal-retentive, are we? :-)
+
+# Thus the base rate for peak-time calls is £0.005111 for 20 secs
+# (0.115 / 450 = 0.00025556 / 0.005 = 0.051111 inv = 19.56521)
+# and off-peak is £0.005111 for 40 secs
+# (0.115 / 900 = 0.000127778 / 0.005 = 0.25556 inv = 39.1304)
+# so accounting should happen in approx 1/2p increments...
+
+# OK, here we go...
+
+# Because of the need to detect time-of-day as well as initial-period,
+# this default should never actually get applied, but we assume that
+# connections are made in the peak rate period...
+default=(0.00511,20)
+
+# PEAK-TIME CALLS are 8am to 6pm Mon-Fri, so after flat_init_costs
+# this rule should apply:
+on (monday..friday) between (08:00..18:00) use (0.005111,20,450)
+
+# OFF-PEAK CALLS are 6pm to 8am Mon-Fri plus all day weekends and holidays
+# This needs to supersede the flat_init_costs on time, because that
+# only applies to the first 450 secs of PEAK-TIME calls
+on (monday..friday) between (00:00..08:00) use (0.115,900)
+on (monday..friday) between (18:00..23:59) use (0.115,900)
+on (saturday..sunday) between (00:00..23:59) use (0.115,900)
+# Thereafter the default applies after the first 15mins
+on (monday..friday) between (00:00..08:00) use (0.005111,40,900)
+on (monday..friday) between (18:00..23:59) use (0.005111,40,900)
+on (saturday..sunday) between (00:00..23:59) use (0.005111,40,900)
+
+# KNOWN HOLIDAYS
+
+# New Year's Day
+on (01/01) between (00:00..23:59) use (0.115,900)
+on (01/01) between (00:00..23:59) use (0.005111,40,900)
+
+# St Patrick's Day
+on (03/17) between (00:00..23:59) use (0.115,900)
+on (03/17) between (00:00..23:59) use (0.005111,40,900)
+
+# Easter Monday
+on (easter+1) between (00:00..23:59) use (0.115,900)
+on (easter+1) between (00:00..23:59) use (0.005111,40,900)
+
+# May Day (Bealtaine)
+on (05/01) between (00:00..23:59) use (0.115,900)
+on (05/01) between (00:00..23:59) use (0.005111,40,900)
+
+# Christmas Day and St Stephen's Day
+on (12/25) between (00:00..23:59) use (0.115,900)
+on (12/25) between (00:00..23:59) use (0.005111,40,900)
+on (12/26) between (00:00..23:59) use (0.115,900)
+on (12/26) between (00:00..23:59) use (0.005111,40,900)
+
+# This file should be refreshed every year to take account of the
+# moveable public holidays we inherited from the British practice,
+# known as "Bank Holidays" (originally the quarter-days when banks
+# had to close for accounting purposes, but now almost unpredictable).
+# These happen several times a year, always on a Monday. Dates
+# for 2000 are June 5th, August 7th, and October 30th.
+# The exact dates are known several years in advance and are fixed
+# by the Taoiseach's Office and the Dept of Local Government.
+# They are NOT the same days as British Bank Holidays, which are
+# fixed on a different basis.
+
+# June Bank Holiday 2000 (in lieu of Oimelc/Imbolc, which was in Feb)
+on (06/05) between (00:00..23:59) use (0.115,900)
+on (06/05) between (00:00..23:59) use (0.005111,40,900)
+
+# August Bank Holiday 2000 (Lughnasa)
+on (08/07) between (00:00..23:59) use (0.115,900)
+on (08/07) between (00:00..23:59) use (0.005111,40,900)
+
+# October Bank Holiday 2000 (Samhain)
+on (10/30) between (00:00..23:59) use (0.115,900)
+on (10/30) between (00:00..23:59) use (0.005111,40,900)
+
+# No automatic account is taken of Transference, when a fixed public
+# holiday occurs on a weekend, which means the following Monday becomes
+# a holiday in compensation. (1/1/2000 is a good example!!)
+
+# Transfer New Year's Day holiday 2000 to first working day afterwards
+on (01/03) between (00:00..23:59) use (0.115,900)
+on (01/03) between (00:00..23:59) use (0.005111,40,900)
+
+# None of the other fixed holidays in 2000 needs this doing.
+
+# When Christmas occurs on a Saturday (and St Stephen's Day therefore
+# on a Sunday), ONLY the following Monday is a holiday, not the Tuesday
+# as well (sorry, guys :-)
diff --git a/kppp/Rules/Ireland/Eircom_Local.rst b/kppp/Rules/Ireland/Eircom_Local.rst
new file mode 100644
index 00000000..5c898cdb
--- /dev/null
+++ b/kppp/Rules/Ireland/Eircom_Local.rst
@@ -0,0 +1,164 @@
+################################################################
+#
+# This is a kppp ruleset for Eircom (formerly Telecom Eireann)
+# for standard local calls (NOT special-rate Internet 1891 calls).
+#
+# Unbelievably, Eircom has now dropped the former (ludicrously
+# irrelevant) distance-based charge-bands on direct-dialled calls.
+# They still remain for operator-connected calls but these are
+# (a) a rarity and (b) unusable for modems anyway. Calls in Ireland
+# are therefore in one of the following categories:
+#
+# 1. Local calls
+# 2. Special-rate Internet calls (ISPs with 1891 numbers)
+# 3. National calls (ie all other trunk or long-distance calls)
+#
+# Note that some Telcos offer special deals of a fixed-rate per-month
+# charge which gives you unlimited, uncharged local calls in off-peak
+# times. At other times, your standard Telco rates apply.
+#
+# "Local" is as hard to define as in any other Telco administration,
+# as it can cross area codes, even when they are in different regions,
+# in order to allow people to call their neighbours 100 yards away
+# even though they may technically be in an area code which would
+# normally qualify as "long-distance", because such calls don't go
+# onto the trunk, just the local exchange.
+#
+# Note all values here include Value-Added Tax at 21% current
+# at 31-Dec-1999
+#
+# Peter Flynn <peter@silmaril.ie>
+################################################################
+
+name=Ireland_Eircom_Local
+
+# Define IEP (Irish Pounds) to be used as currency symbol
+# ??? There is no way to define the currency code AND the symbol !!!
+# WARNING this will have to be changed to EUR from 2002-01-01
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+# ??? Curious default, why not left, which is _way_ more common? !!!
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# It costs 11.5p the moment a call connects. This covers the first
+# 3 mins (peak hours, 8am-6pm M-F) or first 15 mins (off-peak)
+per_connection=0.115
+
+# Therefore the minimum cost is the same as the per-connection cost
+minimum_costs=0.115
+
+# Therefore the first 180 secs costs this much no matter what.
+flat_init_costs=(0.115,180)
+# A pity there's no peak/offpeak differential for this one.
+
+# All subsequent charging is done per-second, based on the unit
+# charge of 11.5p for 3mins (peak hours) or 11.5p for 15mins
+# (off-peak), which works out at £0.000638889/sec and £0.000127778p/sec
+# respectively...that's what they claim, anyway.
+
+# Rather than expect kppp to check the rate every second and add
+# tiny fractions, I've expressed these rates in terms of the amount
+# needed to clock up half a penny (or the closest amount exceeding
+# that value obtainable by multiplying the per-second rate by an
+# integer). Not a whole penny, because you may be damn certain the
+# bean-counters will round up half-penny amounts to the nearest
+# whole penny anyway (anal-retentive, are we? :-)
+
+# Thus the base rate for peak-time calls is £0.005111 for 8 secs
+# (0.115 / 180 = 0.000638889 / 0.005 = 0.127778 inv = 7.8261)
+# and off-peak is £0.005111 for 40 secs
+# (0.115 / 900 = 0.000127778 / 0.005 = 0.25556 inv = 39.1304)
+# so accounting should happen in approx 1/2p increments...
+
+# OK, here we go...
+
+# Because of the need to detect time-of-day as well as initial-period,
+# this default should never actually get applied, but we assume that
+# connections are made in the peak rate period...
+default=(0.005111,8)
+
+# PEAK-TIME CALLS are 8am to 6pm Mon-Fri, so after flat_init_costs
+# this rule should apply:
+on (monday..friday) between (08:00..18:00) use (0.005111,8,180)
+
+# OFF-PEAK CALLS are 6pm to 8am Mon-Fri plus all day weekends and holidays
+# This needs to supersede the flat_init_costs on time, because that
+# only applies to the first 180 secs of PEAK-TIME calls
+on (monday..friday) between (00:00..08:00) use (0.115,900)
+on (monday..friday) between (18:00..23:59) use (0.115,900)
+on (saturday..sunday) between (00:00..23:59) use (0.115,900)
+# Thereafter the default applies after the first 15mins
+on (monday..friday) between (00:00..08:00) use (0.005111,40,900)
+on (monday..friday) between (18:00..23:59) use (0.005111,40,900)
+on (saturday..sunday) between (00:00..23:59) use (0.005111,40,900)
+
+# KNOWN HOLIDAYS
+
+# New Year's Day
+on (01/01) between (00:00..23:59) use (0.115,900)
+on (01/01) between (00:00..23:59) use (0.005111,40,900)
+
+# St Patrick's Day
+on (03/17) between (00:00..23:59) use (0.115,900)
+on (03/17) between (00:00..23:59) use (0.005111,40,900)
+
+# Easter Monday
+on (easter+1) between (00:00..23:59) use (0.115,900)
+on (easter+1) between (00:00..23:59) use (0.005111,40,900)
+
+# May Day (Bealtaine)
+on (05/01) between (00:00..23:59) use (0.115,900)
+on (05/01) between (00:00..23:59) use (0.005111,40,900)
+
+# Christmas Day and St Stephen's Day
+on (12/25) between (00:00..23:59) use (0.115,900)
+on (12/25) between (00:00..23:59) use (0.005111,40,900)
+on (12/26) between (00:00..23:59) use (0.115,900)
+on (12/26) between (00:00..23:59) use (0.005111,40,900)
+
+# This file should be refreshed every year to take account of the
+# moveable public holidays we inherited from the British practice,
+# known as "Bank Holidays" (originally the quarter-days when banks
+# had to close for accounting purposes, but now almost unpredictable).
+# These happen several times a year, always on a Monday. Dates
+# for 2000 are June 5th, August 7th, and October 30th.
+# The exact dates are known several years in advance and are fixed
+# by the Taoiseach's Office and the Dept of Local Government.
+# They are NOT the same days as British Bank Holidays, which are
+# fixed on a different basis.
+
+# June Bank Holiday 2000 (in lieu of Oimelc/Imbolc, which was in Feb)
+on (06/05) between (00:00..23:59) use (0.115,900)
+on (06/05) between (00:00..23:59) use (0.005111,40,900)
+
+# August Bank Holiday 2000 (Lughnasa)
+on (08/07) between (00:00..23:59) use (0.115,900)
+on (08/07) between (00:00..23:59) use (0.005111,40,900)
+
+# October Bank Holiday 2000 (Samhain)
+on (10/30) between (00:00..23:59) use (0.115,900)
+on (10/30) between (00:00..23:59) use (0.005111,40,900)
+
+# No automatic account is taken of Transference, when a fixed public
+# holiday occurs on a weekend, which means the following Monday becomes
+# a holiday in compensation. (1/1/2000 is a good example!!)
+
+# Transfer New Year's Day holiday 2000 to first working day afterwards
+on (01/03) between (00:00..23:59) use (0.115,900)
+on (01/03) between (00:00..23:59) use (0.005111,40,900)
+
+# None of the other fixed holidays in 2000 needs this doing.
+
+# When Christmas occurs on a Saturday (and St Stephen's Day therefore
+# on a Sunday), ONLY the following Monday is a holiday, not the Tuesday
+# as well (sorry, guys :-)
diff --git a/kppp/Rules/Ireland/Eircom_National.rst b/kppp/Rules/Ireland/Eircom_National.rst
new file mode 100644
index 00000000..640c9a96
--- /dev/null
+++ b/kppp/Rules/Ireland/Eircom_National.rst
@@ -0,0 +1,173 @@
+################################################################
+#
+# This is a kppp ruleset for Eircom (formerly Telecom Eireann)
+# for standard national calls (NOT special-rate Internet 1891 calls).
+#
+# Unbelievably, Eircom has now dropped the former (ludicrously
+# irrelevant) distance-based charge-bands on direct-dialled calls.
+# They still remain for operator-connected calls but these are
+# (a) a rarity and (b) unusable for modems anyway. Calls in Ireland
+# are therefore in one of the following categories:
+#
+# 1. Local calls
+# 2. Special-rate Internet calls (ISPs with 1891 numbers)
+# 3. National calls (ie all other trunk or long-distance calls)
+#
+# Note that some Telcos offer special deals of a fixed-rate per-month
+# charge which gives you unlimited, uncharged local calls in off-peak
+# times. At other times, your standard Telco rates apply.
+#
+# "Local" is as hard to define as in any other Telco administration,
+# as it can cross area codes, even when they are in different regions,
+# in order to allow people to call their neighbours 100 yards away
+# even though they may technically be in an area code which would
+# normally qualify as "long-distance", because such calls don't go
+# onto the trunk, just the local exchange.
+#
+# Note all values here include Value-Added Tax at 21% current
+# at 31-Dec-1999
+#
+# Peter Flynn <peter@silmaril.ie>
+################################################################
+
+name=Ireland_Eircom_National
+
+# Define IEP (Irish Pounds) to be used as currency symbol
+# ??? There is no way to define the currency code AND the symbol !!!
+# WARNING this will have to be changed to EUR from 2002-01-01
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+# ??? Curious default, why not left, which is _way_ more common? !!!
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# It costs 11.5p the moment a call connects. This covers the first
+# 69.01 secs (peak hours, 8am-6pm M-F) or first 103.64 secs (evenings)
+# or 10 mins (weekends). Yes, they calculate to the 1/100th sec...
+per_connection=0.115
+
+# Therefore the minimum cost is the same as the per-connection cost
+minimum_costs=0.115
+
+# Therefore the first 69 secs costs this much no matter what.
+flat_init_costs=(0.115,69)
+# A pity there's no peak/offpeak differential for this one.
+
+# All subsequent charging is done per-second, based on the unit
+# charge of 11.5p for 69.01 sec (peak hours) or 11.5p for 103.64 sec
+# (evenings) or 11.5p for 600 sec (weekends), which works out at
+# £0.0016664/sec, £0.00110961p/sec, and £0.0001916667p/sec
+# respectively...that's what they claim, anyway.
+
+# Rather than expect kppp to check the rate every second and add
+# tiny fractions, I've expressed these rates in terms of the amount
+# needed to clock up half a penny (or the closest amount exceeding
+# that value obtainable by multiplying the per-second rate by an
+# integer). Not a whole penny, because you may be damn certain the
+# bean-counters will round up half-penny amounts to the nearest
+# whole penny anyway (anal-retentive, are we? :-)
+
+# Thus the base rate for peak-time calls is £0.004999 for 3 secs
+# (0.115 / 69.01 = 0.0016664251 / 0.005 = 0.3332850 inv = 3.0004)
+# evenings is £0.0055481 for 5 secs
+# (0.115 / 103.64 = 0.00110961 / 0.005 = 0.2219220 inv = 4.5060869565)
+# and weekends is £0.005175 for 27 secs
+# (0.115 / 600 = 0.0001916667 / 0.005 = 0.0383333 inv = 26.086956522)
+# so accounting should happen in approx 1/2p increments...
+
+# OK, here we go...
+
+# Because of the need to detect time-of-day as well as initial-period,
+# this default should never actually get applied, but we assume that
+# connections are made in the peak rate period...
+default=(0.004999,3)
+
+# PEAK-TIME CALLS are 8am to 6pm Mon-Fri, so after flat_init_costs
+# this rule should apply:
+on (monday..friday) between (08:00..18:00) use (0.004999,3,69)
+
+# EVENING CALLS are 6pm to 8am Mon-Fri
+# This needs to supersede the flat_init_costs on time, because that
+# only applies to the first 69.01 secs of PEAK-TIME calls
+on (monday..friday) between (00:00..08:00) use (0.115,104)
+on (monday..friday) between (18:00..23:59) use (0.115,104)
+# Thereafter the per-second rate applies after the first 104 secs
+on (monday..friday) between (00:00..08:00) use (0.0055481,5,104)
+on (monday..friday) between (18:00..23:59) use (0.0055481,5,104)
+
+# WEEKEND CALLS are midnight Friday to midnight Sunday
+# This needs to supersede the flat_init_costs on time, because that
+# only applies to the first 69.01 secs of PEAK-TIME calls
+on (saturday..sunday) between (00:00..23:59) use (0.115,600)
+# Thereafter the per-second rate applies after the first 104 secs
+on (saturday..sunday) between (00:00..23:59) use (0.005175,27,600)
+
+# KNOWN HOLIDAYS are all at weekend rates
+
+# New Year's Day
+on (01/01) between (00:00..23:59) use (0.115,600)
+on (01/01) between (00:00..23:59) use (0.005175,27,600)
+
+# St Patrick's Day
+on (03/17) between (00:00..23:59) use (0.115,600)
+on (03/17) between (00:00..23:59) use (0.005175,27,600)
+
+# Easter Monday
+on (easter+1) between (00:00..23:59) use (0.115,600)
+on (easter+1) between (00:00..23:59) use (0.005175,27,600)
+
+# May Day (Bealtaine)
+on (05/01) between (00:00..23:59) use (0.115,600)
+on (05/01) between (00:00..23:59) use (0.005175,27,600)
+
+# Christmas Day and St Stephen's Day
+on (12/25) between (00:00..23:59) use (0.115,600)
+on (12/25) between (00:00..23:59) use (0.005175,27,600)
+on (12/26) between (00:00..23:59) use (0.115,600)
+on (12/26) between (00:00..23:59) use (0.005175,27,600)
+
+# This file should be refreshed every year to take account of the
+# moveable public holidays we inherited from the British practice,
+# known as "Bank Holidays" (originally the quarter-days when banks
+# had to close for accounting purposes, but now almost unpredictable).
+# These happen several times a year, always on a Monday. Dates
+# for 2000 are June 5th, August 7th, and October 30th.
+# The exact dates are known several years in advance and are fixed
+# by the Taoiseach's Office and the Dept of Local Government.
+# They are NOT the same days as British Bank Holidays, which are
+# fixed on a different basis.
+
+# June Bank Holiday 2000 (in lieu of Oimelc/Imbolc, which was in Feb)
+on (06/05) between (00:00..23:59) use (0.115,600)
+on (06/05) between (00:00..23:59) use (0.005175,27,600)
+
+# August Bank Holiday 2000 (Lughnasa)
+on (08/07) between (00:00..23:59) use (0.115,600)
+on (08/07) between (00:00..23:59) use (0.005175,27,600)
+
+# October Bank Holiday 2000 (Samhain)
+on (10/30) between (00:00..23:59) use (0.115,600)
+on (10/30) between (00:00..23:59) use (0.005175,27,600)
+
+# No automatic account is taken of Transference, when a fixed public
+# holiday occurs on a weekend, which means the following Monday becomes
+# a holiday in compensation. (1/1/2000 is a good example!!)
+
+# Transfer New Year's Day holiday 2000 to first working day afterwards
+on (01/03) between (00:00..23:59) use (0.115,600)
+on (01/03) between (00:00..23:59) use (0.005175,27,600)
+
+# None of the other fixed holidays in 2000 needs this doing.
+
+# When Christmas occurs on a Saturday (and St Stephen's Day therefore
+# on a Sunday), ONLY the following Monday is a holiday, not the Tuesday
+# as well (sorry, guys :-)
diff --git a/kppp/Rules/Ireland/Eircom_Special.rst b/kppp/Rules/Ireland/Eircom_Special.rst
new file mode 100644
index 00000000..4db7ee1e
--- /dev/null
+++ b/kppp/Rules/Ireland/Eircom_Special.rst
@@ -0,0 +1,173 @@
+################################################################
+#
+# This is a kppp ruleset for Eircom (formerly Telecom Eireann)
+# for a special Esat deal of £17/month flat rate to get you their
+# ISP calls uncharged and unrated evenings and weekends. Other times
+# they cost a regular phone call from your Telco (eg Eircom)
+#
+# Unbelievably, Eircom has now dropped the former (ludicrously
+# irrelevant) distance-based charge-bands on direct-dialled calls.
+# They still remain for operator-connected calls but these are
+# (a) a rarity and (b) unusable for modems anyway. Calls in Ireland
+# are therefore in one of the following categories:
+#
+# 1. Local calls
+# 2. Special-rate Internet calls (ISPs with 1891 numbers)
+# 3. National calls (ie all other trunk or long-distance calls)
+#
+# Note that some Telcos offer special deals of a fixed-rate per-month
+# charge which gives you unlimited, uncharged local calls in off-peak
+# times. At other times, your standard Telco rates apply. *THIS FILE*
+#
+# "Local" is as hard to define as in any other Telco administration,
+# as it can cross area codes, even when they are in different regions,
+# in order to allow people to call their neighbours 100 yards away
+# even though they may technically be in an area code which would
+# normally qualify as "long-distance", because such calls don't go
+# onto the trunk, just the local exchange.
+#
+# Note all values here include Value-Added Tax at 21% current
+# at 31-Dec-1999
+#
+# Peter Flynn <peter@silmaril.ie>
+################################################################
+
+name=Ireland_Eircom_Special
+
+# Define IEP (Irish Pounds) to be used as currency symbol
+# ??? There is no way to define the currency code AND the symbol !!!
+# WARNING this will have to be changed to EUR from 2002-01-01
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+# ??? Curious default, why not left, which is _way_ more common? !!!
+currency_position=left
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# It costs 11.5p the moment a call connects. This covers the first
+# 3 mins (peak hours, 8am-6pm M-F) or first 15 mins (off-peak)
+# For this special deal, this should be covered by the rules below
+per_connection=0.0
+
+# Therefore the minimum cost is the same as the per-connection cost
+minimum_costs=0.0
+
+# Therefore the first 180 secs costs this much no matter what.
+flat_init_costs=(0,0)
+# A pity there's no peak/offpeak differential for this one.
+
+# All subsequent charging is done per-second, based on the unit
+# charge of 11.5p for 3mins (peak hours) or 11.5p for 15mins
+# (off-peak), which works out at £0.000638889/sec and £0.000127778p/sec
+# respectively...that's what they claim, anyway.
+
+# Rather than expect kppp to check the rate every second and add
+# tiny fractions, I've expressed these rates in terms of the amount
+# needed to clock up half a penny (or the closest amount exceeding
+# that value obtainable by multiplying the per-second rate by an
+# integer). Not a whole penny, because you may be damn certain the
+# bean-counters will round up half-penny amounts to the nearest
+# whole penny anyway (anal-retentive, are we? :-)
+
+# Thus the base rate for peak-time calls is £0.005111 for 8 secs
+# (0.115 / 180 = 0.000638889 / 0.005 = 0.127778 inv = 7.8261)
+# and off-peak is £0.005111 for 40 secs
+# (0.115 / 900 = 0.000127778 / 0.005 = 0.25556 inv = 39.1304)
+# so accounting should happen in approx 1/2p increments...
+
+# OK, here we go...
+
+# Because of the need to detect time-of-day as well as initial-period,
+# this default should never actually get applied, but we assume that
+# connections are made in the peak rate period...
+default=(0.005111,8)
+
+# PEAK-TIME CALLS are 8am to 6pm Mon-Fri, so after flat_init_costs
+# this rule should apply:
+on (monday..friday) between (08:00..18:00) use (0.115,180)
+on (monday..friday) between (08:00..18:00) use (0.005111,8,180)
+
+# OFF-PEAK CALLS are 6pm to 8am Mon-Fri plus all day weekends and holidays
+# This needs to supersede the flat_init_costs on time, because that
+# only applies to the first 180 secs of PEAK-TIME calls
+on (monday..friday) between (00:00..08:00) use (0.115,900)
+on (monday..friday) between (18:00..23:59) use (0.115,900)
+on (saturday..sunday) between (00:00..23:59) use (0.115,900)
+# Thereafter the default applies after the first 15mins
+on (monday..friday) between (00:00..08:00) use (0.005111,40,900)
+on (monday..friday) between (18:00..23:59) use (0.005111,40,900)
+on (saturday..sunday) between (00:00..23:59) use (0.005111,40,900)
+
+# KNOWN HOLIDAYS
+
+# New Year's Day
+on (01/01) between (00:00..23:59) use (0.115,900)
+on (01/01) between (00:00..23:59) use (0.005111,40,900)
+
+# St Patrick's Day
+on (03/17) between (00:00..23:59) use (0.115,900)
+on (03/17) between (00:00..23:59) use (0.005111,40,900)
+
+# Easter Monday
+on (easter+1) between (00:00..23:59) use (0.115,900)
+on (easter+1) between (00:00..23:59) use (0.005111,40,900)
+
+# May Day (Bealtaine)
+on (05/01) between (00:00..23:59) use (0.115,900)
+on (05/01) between (00:00..23:59) use (0.005111,40,900)
+
+# Christmas Day and St Stephen's Day
+on (12/25) between (00:00..23:59) use (0.115,900)
+on (12/25) between (00:00..23:59) use (0.005111,40,900)
+on (12/26) between (00:00..23:59) use (0.115,900)
+on (12/26) between (00:00..23:59) use (0.005111,40,900)
+
+# This file should be refreshed every year to take account of the
+# moveable public holidays we inherited from the British practice,
+# known as "Bank Holidays" (originally the quarter-days when banks
+# had to close for accounting purposes, but now almost unpredictable).
+# These happen several times a year, always on a Monday. Dates
+# for 2000 are June 5th, August 7th, and October 30th.
+# The exact dates are known several years in advance and are fixed
+# by the Taoiseach's Office and the Dept of Local Government.
+# They are NOT the same days as British Bank Holidays, which are
+# fixed on a different basis.
+
+# June Bank Holiday 2000 (in lieu of Oimelc/Imbolc, which was in Feb)
+on (06/05) between (00:00..23:59) use (0.115,900)
+on (06/05) between (00:00..23:59) use (0.005111,40,900)
+
+# August Bank Holiday 2000 (Lughnasa)
+on (08/07) between (00:00..23:59) use (0.115,900)
+on (08/07) between (00:00..23:59) use (0.005111,40,900)
+
+# October Bank Holiday 2000 (Samhain)
+on (10/30) between (00:00..23:59) use (0.115,900)
+on (10/30) between (00:00..23:59) use (0.005111,40,900)
+
+# No automatic account is taken of Transference, when a fixed public
+# holiday occurs on a weekend, which means the following Monday becomes
+# a holiday in compensation. (1/1/2000 is a good example!!)
+
+# Transfer New Year's Day holiday 2000 to first working day afterwards
+on (01/03) between (00:00..23:59) use (0.115,900)
+on (01/03) between (00:00..23:59) use (0.005111,40,900)
+
+# None of the other fixed holidays in 2000 needs this doing.
+
+# When Christmas occurs on a Saturday (and St Stephen's Day therefore
+# on a Sunday), ONLY the following Monday is a holiday, not the Tuesday
+# as well (sorry, guys :-)
+
+# This is where the Esat deal bites:
+on (monday..friday) between (18:00..23:59) use (0,0)
+on (monday..friday) between (00:00..08:00) use (0,0)
+on (saturday..sunday) between (00:00..23:59) use (0,0)
diff --git a/kppp/Rules/Ireland/Makefile.am b/kppp/Rules/Ireland/Makefile.am
new file mode 100644
index 00000000..54c65e0a
--- /dev/null
+++ b/kppp/Rules/Ireland/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = Eircom_Internet.rst \
+ Eircom_Local.rst \
+ Eircom_National.rst \
+ Eircom_Special.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Ireland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Israel/Bezeq_Interurban.rst b/kppp/Rules/Israel/Bezeq_Interurban.rst
new file mode 100644
index 00000000..3b006713
--- /dev/null
+++ b/kppp/Rules/Israel/Bezeq_Interurban.rst
@@ -0,0 +1,76 @@
+################################################################
+# Kppp phone cost ruleset for local calls in Israel (Sicha BeinIronit)
+#
+# Created on April 20, 2000 by Meni Livne <meni@mail.com>
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION. IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR TELEPHONE PROVIDER, CONFIGURE THIS YOURSELF AND DO
+# NOT USE THIS FILE.
+#
+# Rates valid from May 1, 2000
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Israel_Interurban
+
+################################################################
+# currency settings
+################################################################
+
+# Define NIS (New Israeli Shekel) to be used as currency symbol
+currency_symbol=NIS
+
+# Define the position of the currency symbol.
+currency_position=left
+
+# Define the number of significant digits.
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+default=(0.26, 60)
+
+# Minimum costs for a call are 23.1 Agorot. If the costs of a
+# call are less than this value, this value is used instead
+minimum_costs=0.231
+
+
+# On weekdays from 8:00 to 18:00, and on fridays from
+# 8:00 to 13:00 costs are 26 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.26, 60)
+on (friday) between (8:00..13:00) use (0.26, 60)
+
+# On weekdays from 18:00 to 22:00
+# costs are 4.5 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.045, 60)
+
+# On weekdays from 22:00 to 8:00, on fridays from 13:00,
+# and on saturdays costs are 1.5 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.015, 60)
+on (friday) between (13:00..23:59) use (0.015, 60)
+on (saturday) between () use (0.015, 60)
+
+
+# On holiday eves from 13:00 and on holidays
+# costs are 1.5 Agorot per minute
+
+# Holidays for 2001:
+# Passover (8,14/4) Independence day (26/4), Lag Ba'Omer (11/5), Shavuot (28/5)
+on (04/07, 04/13, 04/25, 05/10, 05/27) between (13:00..23:59) use (0.015, 60)
+on (04/08, 04/14, 04/26, 05/11, 05/28) between () use (0.015, 60)
+# Rosh HaShana (18-19/9), Yom Kippur (27/9)
+on (09/17, 09/26) between (13:00..23:59) use (0.015, 60)
+on (09/18, 08/19, 09/27) between () use (0.015, 60)
diff --git a/kppp/Rules/Israel/Bezeq_Local.rst b/kppp/Rules/Israel/Bezeq_Local.rst
new file mode 100644
index 00000000..77321f0b
--- /dev/null
+++ b/kppp/Rules/Israel/Bezeq_Local.rst
@@ -0,0 +1,76 @@
+################################################################
+# Kppp phone cost ruleset for local calls in Israel (Sicha Mekomit)
+#
+# Created on April 20, 2000 by Meni Livne <meni@mail.com>
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION. IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR TELEPHONE PROVIDER, CONFIGURE THIS YOURSELF AND DO
+# NOT USE THIS FILE.
+#
+# Rates valid from May 1, 2000
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Israel_Local
+
+################################################################
+# currency settings
+################################################################
+
+# Define NIS (New Israeli Shekel) to be used as currency symbol
+currency_symbol=NIS
+
+# Define the position of the currency symbol.
+currency_position=left
+
+# Define the number of significant digits.
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+default=(0.09, 60)
+
+# Minimum costs for a call are 23.1 Agorot. If the costs of a
+# call are less than this value, this value is used instead
+minimum_costs=0.231
+
+
+# On weekdays from 8:00 to 18:00, and on fridays from
+# 8:00 to 13:00 costs are 9 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.09, 60)
+on (friday) between (8:00..13:00) use (0.09, 60)
+
+# On weekdays from 18:00 to 22:00
+# costs are 4.5 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.045, 60)
+
+# On weekdays from 22:00 to 8:00, on fridays from 13:00,
+# and on saturdays costs are 1.5 Agorot per minute
+on (sunday..thursday) between (8:00..18:00) use (0.015, 60)
+on (friday) between (13:00..23:59) use (0.015, 60)
+on (saturday) between () use (0.015, 60)
+
+
+# On holiday eves from 13:00 and on holidays
+# costs are 1.5 Agorot per minute
+
+# Holidays for 2001:
+# Passover (8,14/4) Independence day (26/4), Lag Ba'Omer (11/5), Shavuot (28/5)
+on (04/07, 04/13, 04/25, 05/10, 05/27) between (13:00..23:59) use (0.015, 60)
+on (04/08, 04/14, 04/26, 05/11, 05/28) between () use (0.015, 60)
+# Rosh HaShana (18-19/9), Yom Kippur (27/9)
+on (09/17, 09/26) between (13:00..23:59) use (0.015, 60)
+on (09/18, 08/19, 09/27) between () use (0.015, 60)
diff --git a/kppp/Rules/Israel/Makefile.am b/kppp/Rules/Israel/Makefile.am
new file mode 100644
index 00000000..33c16585
--- /dev/null
+++ b/kppp/Rules/Israel/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = Bezeq_Local.rst \
+ Bezeq_Interurban.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Israel
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Italy/Atlanet.rst b/kppp/Rules/Italy/Atlanet.rst
new file mode 100644
index 00000000..410c0162
--- /dev/null
+++ b/kppp/Rules/Italy/Atlanet.rst
@@ -0,0 +1,59 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Pino Toscano (toscano.pino@tiscali.it)
+#
+# Atlanet_Internet.rst
+#
+# COLLEGAMENTI AD INTERNET
+#
+# Ultimo aggiornamento: 16 Luglio 2003.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0 EUR.(!!) ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: 0,01 EUR ##
+## Tariffa INTERA: 0,017 EUR ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Sabato e FESTIVI: ridotta tutto il giorno ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Atlanet_Internet
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0
+
+# Tariffa ridotta
+default=(0.01, 60)
+
+# Giorni feriali, sabato e domenica
+on (monday..friday) between (8:00..18:30) use (0.017, 60)
+
+# Giorni festivi
+on (01/01) between () use (0.01, 60)
+on (01/06) between () use (0.01, 60)
+on (04/25) between () use (0.01, 60)
+on (05/01) between () use (0.01, 60)
+on (08/15) between () use (0.01, 60)
+on (11/01) between () use (0.01, 60)
+on (12/08) between () use (0.01, 60)
+on (12/25) between () use (0.01, 60)
+on (12/26) between () use (0.01, 60)
+on (easter) between () use (0.01, 60)
+on (easter + 1) between () use (0.01, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Cheapnet.rst b/kppp/Rules/Italy/Cheapnet.rst
new file mode 100644
index 00000000..89629238
--- /dev/null
+++ b/kppp/Rules/Italy/Cheapnet.rst
@@ -0,0 +1,79 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Giovanni Venturi (jumpyj@tiscali.it)
+#
+# Cheapnet.rst
+#
+# CHIAMATE URBANE
+#
+# Ultimo aggiornamento: 28 Settembre 2005.
+#
+#################################################################
+## ##
+## Addebito alla risposta di 0,06192 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 15 min = 0,01092 oltre = 0,00984 ##
+## Tariffa INTERA: fino a 15 min = 0,01896 oltre = 0,01716 ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Giorni FESTIVI: ridotta tutto il giorno ##
+## ##
+## Sabato: | intera dalle 08:00 alle 13:00 ##
+## | ridotta dalle 13:00 alle 08:00 ##
+## ##
+#################################################################
+
+# Nome del ruleset
+name=Cheapnet
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.06192
+
+# Tariffa ridotta fino a 15 min
+default=(0.01092, 60)
+
+# Giorni feriali, sabato e domenica
+on (monday..friday) between (18:30..8:00) use (0.00984, 60, 900)
+on (monday..friday) between (8:00..18:30) use (0.01896, 60)
+on (monday..friday) between (8:00..18:30) use (0.01716, 60, 900)
+on (saturday) between (13:00..8:00) use (0.00984, 60, 900)
+on (saturday) between (8:00..13:00) use (0.01896, 60)
+on (saturday) between (8:00..13:00) use (0.01716, 60, 900)
+on (sunday) between () use (0.00984, 60, 900)
+
+# Giorni festivi
+on (01/01) between () use (0.01092, 60)
+on (01/01) between () use (0.00984, 60, 900)
+on (01/06) between () use (0.01092, 60)
+on (01/06) between () use (0.00984, 60, 900)
+on (04/25) between () use (0.01092, 60)
+on (04/25) between () use (0.00984, 60, 900)
+on (05/01) between () use (0.01092, 60)
+on (05/01) between () use (0.00984, 60, 900)
+on (08/15) between () use (0.01092, 60)
+on (08/15) between () use (0.00984, 60, 900)
+on (11/01) between () use (0.01092, 60)
+on (11/01) between () use (0.00984, 60, 900)
+on (12/08) between () use (0.01092, 60)
+on (12/08) between () use (0.00984, 60, 900)
+on (12/25) between () use (0.01092, 60)
+on (12/25) between () use (0.00984, 60, 900)
+on (12/26) between () use (0.01092, 60)
+on (12/26) between () use (0.00984, 60, 900)
+on (easter) between () use (0.01092, 60)
+on (easter) between () use (0.00984, 60, 900)
+on (easter + 1) between () use (0.01092, 60)
+on (easter + 1) between () use (0.00984, 60, 900)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Internet_SpZero.rst b/kppp/Rules/Italy/Infostrada_Internet_SpZero.rst
new file mode 100644
index 00000000..8ba3ad9c
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Internet_SpZero.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Internet_SpZero.rst
+#
+# INTERNET
+# Collegamenti tramite numero 1055-421010
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0620 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA compresa. ##
+## ##
+## ##
+## Tariffa UNICA : 0,0095 EUR ##
+## ##
+## ##
+## Tutti i giorni : dalle 00:00 alle 24:00 ##
+## ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Internet_SpZero
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0620
+
+# Tutti i giorni a tutte le ore
+default=(0.0095, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Libero1055_Base.rst b/kppp/Rules/Italy/Infostrada_Libero1055_Base.rst
new file mode 100644
index 00000000..17502626
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Libero1055_Base.rst
@@ -0,0 +1,68 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Libero1055_Base.rst
+#
+# INTERNET
+# Collegamenti Internet con Libero 1055.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0775 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## ##
+## Tariffa RIDOTTA: 0,0190 EUR ##
+## Tariffa INTERA : 0,0350 EUR ##
+## Tariffa MINIMA : 0,0095 EUR ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Minima dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Libero1055_Base
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0775
+
+# Giorni feriali a tariffa ridotta
+default=(0.0190, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.0350, 60)
+
+# Giorni festivi e sabato: tariffa minima
+on (saturday) between () use (0.0095, 60)
+on (sunday) between () use (0.0095, 60)
+on (01/01) between () use (0.0095, 60)
+on (01/06) between () use (0.0095, 60)
+on (04/25) between () use (0.0095, 60)
+on (05/01) between () use (0.0095, 60)
+on (08/15) between () use (0.0095, 60)
+on (11/01) between () use (0.0095, 60)
+on (12/08) between () use (0.0095, 60)
+on (12/25) between () use (0.0095, 60)
+on (12/26) between () use (0.0095, 60)
+on (easter) between () use (0.0095, 60)
+on (easter + 1) between () use (0.0095, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Libero1055_SpZero.rst b/kppp/Rules/Italy/Infostrada_Libero1055_SpZero.rst
new file mode 100644
index 00000000..4ae56497
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Libero1055_SpZero.rst
@@ -0,0 +1,73 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Libero1055_SpZero.rst
+#
+# NOTA:
+# Questa tariffa e' identica a Infostrada_Libero1055_Base.
+# E' stata inserita per maggior chiarezza, dato che l'operatore
+# la applica anche per i contratti SpazioZero.
+#
+# INTERNET
+# Collegamenti Internet con Libero 1055.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0775 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## ##
+## Tariffa RIDOTTA: 0,0190 EUR ##
+## Tariffa INTERA : 0,0350 EUR ##
+## Tariffa MINIMA : 0,0095 EUR ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Minima dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Libero1055_SpZero
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0775
+
+# Giorni feriali a tariffa ridotta
+default=(0.0190, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.0350, 60)
+
+# Giorni festivi e sabato: tariffa minima
+on (saturday) between () use (0.0095, 60)
+on (sunday) between () use (0.0095, 60)
+on (01/01) between () use (0.0095, 60)
+on (01/06) between () use (0.0095, 60)
+on (04/25) between () use (0.0095, 60)
+on (05/01) between () use (0.0095, 60)
+on (08/15) between () use (0.0095, 60)
+on (11/01) between () use (0.0095, 60)
+on (12/08) between () use (0.0095, 60)
+on (12/25) between () use (0.0095, 60)
+on (12/26) between () use (0.0095, 60)
+on (easter) between () use (0.0095, 60)
+on (easter + 1) between () use (0.0095, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Loc_Reg_Naz_SpZero.rst b/kppp/Rules/Italy/Infostrada_Loc_Reg_Naz_SpZero.rst
new file mode 100644
index 00000000..f50befce
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Loc_Reg_Naz_SpZero.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Loc_Reg_Naz_SpZero.rst
+#
+# LOCALI, REGIONALI e NAZIONALI
+# Chiamate verso rete fissa italiana
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0620 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA compresa. ##
+## ##
+## ##
+## Tariffa UNICA : 0,0125 EUR ##
+## ##
+## ##
+## Tutti i giorni : dalle 00:00 alle 24:00 ##
+## ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Locali_Regionali_Nazionali_SpZero
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0620
+
+# Tutti i giorni a tutte le ore
+default=(0.0125, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Locali_Base.rst b/kppp/Rules/Italy/Infostrada_Locali_Base.rst
new file mode 100644
index 00000000..ab06d100
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Locali_Base.rst
@@ -0,0 +1,65 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Locali_Base.rst
+#
+# LOCALI
+# Chiamate verso localita' con lo stesso prefisso del chiamante.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0620 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## ##
+## Tariffa RIDOTTA: 0,0095 EUR ##
+## Tariffa INTERA : 0,0175 EUR ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Ridotta dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Locali_Base
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0620
+
+# Giorni feriali a tariffa ridotta, sabato e domenica
+default=(0.0095, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.0175, 60)
+
+# Giorni festivi: tariffa ridotta
+on (01/01) between () use (0.0095, 60)
+on (01/06) between () use (0.0095, 60)
+on (04/25) between () use (0.0095, 60)
+on (05/01) between () use (0.0095, 60)
+on (08/15) between () use (0.0095, 60)
+on (11/01) between () use (0.0095, 60)
+on (12/08) between () use (0.0095, 60)
+on (12/25) between () use (0.0095, 60)
+on (12/26) between () use (0.0095, 60)
+on (easter) between () use (0.0095, 60)
+on (easter + 1) between () use (0.0095, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Infostrada_Reg_Naz_Base.rst b/kppp/Rules/Italy/Infostrada_Reg_Naz_Base.rst
new file mode 100644
index 00000000..4e424c0f
--- /dev/null
+++ b/kppp/Rules/Italy/Infostrada_Reg_Naz_Base.rst
@@ -0,0 +1,66 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Infostrada_Reg_Naz_Base.rst
+#
+# REGIONALI e NAZIONALI
+# Chiamate verso localita' con prefisso diverso dal chiamante
+# sia all'interno della stessa regione sia in una regione diversa.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0775 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA compresa. ##
+## ##
+## Tariffa RIDOTTA: 0,0295 EUR ##
+## Tariffa INTERA : 0,1085 EUR ##
+## ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Ridotta dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Infostrada_Regionali_Nazionali_Base
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0775
+
+# Giorni feriali a tariffa ridotta, sabato e domenica
+default=(0.0295, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.1085, 60)
+
+# Giorni festivi: tariffa ridotta
+on (01/01) between () use (0.0295, 60)
+on (01/06) between () use (0.0295, 60)
+on (04/25) between () use (0.0295, 60)
+on (05/01) between () use (0.0295, 60)
+on (08/15) between () use (0.0295, 60)
+on (11/01) between () use (0.0295, 60)
+on (12/08) between () use (0.0295, 60)
+on (12/25) between () use (0.0295, 60)
+on (12/26) between () use (0.0295, 60)
+on (easter) between () use (0.0295, 60)
+on (easter + 1) between () use (0.0295, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Makefile.am b/kppp/Rules/Italy/Makefile.am
new file mode 100644
index 00000000..4039dc79
--- /dev/null
+++ b/kppp/Rules/Italy/Makefile.am
@@ -0,0 +1,34 @@
+pkg_DATA = Atlanet.rst \
+ Cheapnet.rst \
+ Infostrada_Internet_SpZero.rst \
+ Infostrada_Libero1055_Base.rst \
+ Infostrada_Libero1055_SpZero.rst \
+ Infostrada_Loc_Reg_Naz_SpZero.rst \
+ Infostrada_Locali_Base.rst \
+ Infostrada_Reg_Naz_Base.rst \
+ Tele2_Altri_ISP.rst \
+ Tele2_Internet_Tele2.rst \
+ Telecom_Interurbane_Fino15Km.rst \
+ Telecom_Interurbane_Oltre15Km.rst \
+ Telecom_Locali.rst \
+ Teleconomy24_Internet.rst \
+ Teleconomy24_Nazionali.rst \
+ Teleconomy_NoStop_Internet.rst \
+ Wind_24ore_Internet_AltriISP.rst \
+ Wind_24ore_Internet_InWind.rst \
+ Wind_24ore_Interurbane.rst \
+ Wind_24ore_Urbane.rst \
+ Wind_Family+SuperLight_Internet_InWind.rst \
+ Wind_Family+SuperLight_Urbane_Interurbane.rst \
+ Wind_Family_Internet_AltriISP.rst \
+ Wind_Family_Internet_InWind.rst \
+ Wind_Family_Interurbane.rst \
+ Wind_Family_Urbane.rst \
+ Wind_Flat_Internet_AltriISP.rst \
+ Wind_Flat_Internet_InWind.rst \
+ Wind_Urbana_1088_Light.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Italy
+
+EXTRA_DIST = $(pkg_DATA)
+
diff --git a/kppp/Rules/Italy/Tele2_Altri_ISP.rst b/kppp/Rules/Italy/Tele2_Altri_ISP.rst
new file mode 100644
index 00000000..4ba34d9d
--- /dev/null
+++ b/kppp/Rules/Italy/Tele2_Altri_ISP.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Giovanni Venturi (gventuri73@tiscali.it)
+#
+# Tele2_Alti_ISP.rst
+#
+# URBANE.
+#
+# Ultimo aggiornamento: 26 Giugno 2003.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## ##
+## Tariffa RIDOTTA: 0,017 EUR ##
+## Tariffa INTERA : 0,026 EUR ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Ridotta dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Tele2_Altri_ISP
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Giorni feriali, sabato e domenica a tariffa ridotta
+default=(0.017, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (08:00..18:30) use (0.026, 60)
+
+# Giorni festivi a tariffa ridotta
+on (01/01) between () use (0.017, 60)
+on (01/06) between () use (0.017, 60)
+on (04/25) between () use (0.017, 60)
+on (05/01) between () use (0.017, 60)
+on (08/15) between () use (0.017, 60)
+on (11/01) between () use (0.017, 60)
+on (12/08) between () use (0.017, 60)
+on (12/25) between () use (0.017, 60)
+on (12/26) between () use (0.017, 60)
+on (easter) between () use (0.017, 60)
+on (easter + 1) between () use (0.017, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Tele2_Internet_Tele2.rst b/kppp/Rules/Italy/Tele2_Internet_Tele2.rst
new file mode 100644
index 00000000..5a7b6205
--- /dev/null
+++ b/kppp/Rules/Italy/Tele2_Internet_Tele2.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Giovanni Venturi (gventuri73@tiscali.it)
+#
+# Tele2_Internet_Tele2.rst
+#
+# URBANE.
+#
+# Ultimo aggiornamento: 29 Marzo 2005.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0787 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## ##
+## Tariffa RIDOTTA: 0,0070 EUR ##
+## Tariffa INTERA : 0,0120 EUR ##
+## ##
+## | Ridotta dalle 18:30 alle 08:00 ##
+## Giorni FERIALI : | ##
+## | Intera dalle 08:00 alle 18:30 ##
+## ##
+## ##
+## Giorni FESTIVI : | Ridotta dalle 00:00 alle 24:00 ##
+## e SABATO : | ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Tele2_Internet_Tele2
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0787
+
+# Giorni feriali, sabato e domenica a tariffa ridotta
+default=(0.0070, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (08:00..18:30) use (0.0120, 60)
+
+# Giorni festivi a tariffa ridotta
+on (01/01) between () use (0.0070, 60)
+on (01/06) between () use (0.0070, 60)
+on (04/25) between () use (0.0070, 60)
+on (05/01) between () use (0.0070, 60)
+on (08/15) between () use (0.0070, 60)
+on (11/01) between () use (0.0070, 60)
+on (12/08) between () use (0.0070, 60)
+on (12/25) between () use (0.0070, 60)
+on (12/26) between () use (0.0070, 60)
+on (easter) between () use (0.0070, 60)
+on (easter + 1) between () use (0.0070, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Telecom_Interurbane_Fino15Km.rst b/kppp/Rules/Italy/Telecom_Interurbane_Fino15Km.rst
new file mode 100644
index 00000000..3b078176
--- /dev/null
+++ b/kppp/Rules/Italy/Telecom_Interurbane_Fino15Km.rst
@@ -0,0 +1,61 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it, jungbn@netsacape.net)
+#
+# Telecom_Interurbane_Fino15Km.rst
+#
+# CHIAMATE INTERURBANE ENTRO 15 KM
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0787 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: 0,0310 EUR ##
+## Tariffa INTERA : 0,0619 EUR ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+
+# Nome ruleset
+name=Telecom_Interurbane_Fino15Km
+
+# Impostazione valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0787
+
+# Giorni feriali, sabato e domenica a tariffa ridotta
+default=(0.0310, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.0619, 60)
+
+# Giorni festivi a tariffa ridotta
+on (01/01) between () use (0.0310, 60)
+on (01/06) between () use (0.0310, 60)
+on (04/25) between () use (0.0310, 60)
+on (05/01) between () use (0.0310, 60)
+on (08/15) between () use (0.0310, 60)
+on (11/01) between () use (0.0310, 60)
+on (12/08) between () use (0.0310, 60)
+on (12/25) between () use (0.0310, 60)
+on (12/26) between () use (0.0310, 60)
+on (easter) between () use (0.0310, 60)
+on (easter + 1) between () use (0.0310, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Telecom_Interurbane_Oltre15Km.rst b/kppp/Rules/Italy/Telecom_Interurbane_Oltre15Km.rst
new file mode 100644
index 00000000..d5371c98
--- /dev/null
+++ b/kppp/Rules/Italy/Telecom_Interurbane_Oltre15Km.rst
@@ -0,0 +1,61 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it, jungbn@netsacape.net)
+#
+# Telecom_Interurbane_Oltre15Km.rst
+#
+# CHIAMATE INTERURBANE OLTRE I 15 KM
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0787 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: 0,0310 EUR ##
+## Tariffa INTERA : 0,1146 EUR ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+
+# Nome ruleset
+name=Telecom_Interurbane_Oltre15Km
+
+# Impostazione valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0787
+
+# Giorni feriali, sabato e domenica a tariffa ridotta
+default=(0.0310, 60)
+
+# Giorni feriali a tariffa intera
+on (monday..friday) between (8:00..18:30) use (0.1146, 60)
+
+# Giorni festivi a tariffa ridotta
+on (01/01) between () use (0.0310, 60)
+on (01/06) between () use (0.0310, 60)
+on (04/25) between () use (0.0310, 60)
+on (05/01) between () use (0.0310, 60)
+on (08/15) between () use (0.0310, 60)
+on (11/01) between () use (0.0310, 60)
+on (12/08) between () use (0.0310, 60)
+on (12/25) between () use (0.0310, 60)
+on (12/26) between () use (0.0310, 60)
+on (easter) between () use (0.0310, 60)
+on (easter + 1) between () use (0.0310, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Telecom_Locali.rst b/kppp/Rules/Italy/Telecom_Locali.rst
new file mode 100644
index 00000000..a3e88e80
--- /dev/null
+++ b/kppp/Rules/Italy/Telecom_Locali.rst
@@ -0,0 +1,79 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Telecom_Locali.rst
+#
+# CHIAMATE URBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 15 min = 0,0109 oltre = 0,0098 ##
+## Tariffa INTERA: fino a 15 min = 0,0190 oltre = 0,0172 ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Giorni FESTIVI: ridotta tutto il giorno ##
+## ##
+## Sabato: | intera dalle 08:00 alle 13:00 ##
+## | ridotta dalle 13:00 alle 08:00 ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Telecom_Locali
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Tariffa ridotta fino a 15 min
+default=(0.0109, 60)
+
+# Giorni feriali, sabato e domenica
+on (monday..friday) between (18:30..8:00) use (0.0098, 60, 900)
+on (monday..friday) between (8:00..18:30) use (0.0190, 60)
+on (monday..friday) between (8:00..18:30) use (0.0172, 60, 900)
+on (saturday) between (13:00..8:00) use (0.0098, 60, 900)
+on (saturday) between (8:00..13:00) use (0.0190, 60)
+on (saturday) between (8:00..13:00) use (0.0172, 60, 900)
+on (sunday) between () use (0.0098, 60, 900)
+
+# Giorni festivi
+on (01/01) between () use (0.0190, 60)
+on (01/01) between () use (0.0098, 60, 900)
+on (01/06) between () use (0.0190, 60)
+on (01/06) between () use (0.0098, 60, 900)
+on (04/25) between () use (0.0190, 60)
+on (04/25) between () use (0.0098, 60, 900)
+on (05/01) between () use (0.0190, 60)
+on (05/01) between () use (0.0098, 60, 900)
+on (08/15) between () use (0.0190, 60)
+on (08/15) between () use (0.0098, 60, 900)
+on (11/01) between () use (0.0190, 60)
+on (11/01) between () use (0.0098, 60, 900)
+on (12/08) between () use (0.0190, 60)
+on (12/08) between () use (0.0098, 60, 900)
+on (12/25) between () use (0.0190, 60)
+on (12/25) between () use (0.0098, 60, 900)
+on (12/26) between () use (0.0190, 60)
+on (12/26) between () use (0.0098, 60, 900)
+on (easter) between () use (0.0190, 60)
+on (easter) between () use (0.0098, 60, 900)
+on (easter + 1) between () use (0.0190, 60)
+on (easter + 1) between () use (0.0098, 60, 900)
+
+# Fine
diff --git a/kppp/Rules/Italy/Teleconomy24_Internet.rst b/kppp/Rules/Italy/Teleconomy24_Internet.rst
new file mode 100644
index 00000000..0cb9de05
--- /dev/null
+++ b/kppp/Rules/Italy/Teleconomy24_Internet.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Teleconomy24_Internet.rst
+#
+# Chiamate dirette ad un ISP qualsiasi (con numero appartenente
+# allo stesso distretto telefonico del chiamante o ad addebito
+# ripartito del tipo 848 anche su linea ISDN o offerta BB.B).
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: 0,0092 tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Teleconomy24_Internet
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Tariffa unica
+default=(0.0092, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Teleconomy24_Nazionali.rst b/kppp/Rules/Italy/Teleconomy24_Nazionali.rst
new file mode 100644
index 00000000..28937a27
--- /dev/null
+++ b/kppp/Rules/Italy/Teleconomy24_Nazionali.rst
@@ -0,0 +1,39 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Teleconomy24_Nazionali.rst
+#
+# CHIAMATE NAZIONALI (urbane, distrettuali e interdistrettuali
+# verso telefoni fissi) anche su linea ISDN e offerta BB.B.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: 0,0149 tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Teleconomy24_Nazionali
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Tariffa unica
+default=(0.0149, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Teleconomy_NoStop_Internet.rst b/kppp/Rules/Italy/Teleconomy_NoStop_Internet.rst
new file mode 100644
index 00000000..f3b1169d
--- /dev/null
+++ b/kppp/Rules/Italy/Teleconomy_NoStop_Internet.rst
@@ -0,0 +1,48 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Teleconomy_NoStop_Internet.rst
+#
+# Chiamate dirette ad un ISP qualsiasi (con numero appartenente
+# allo stesso distretto telefonico del chiamante o ad addebito
+# ripartito del tipo 848 anche su linea ISDN o offerta BB.B).
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: 0,0092 tutti i giorni a tutte le ore ##
+## ##
+## ##
+## ATTENZIONE: ##
+## ----------- ##
+## La tariffa prevede 72000 secondi (20 ore) gratis al mese. ##
+## Kppp pero' non e' in grado di tenerne conto. ##
+## Quindi ai fini del calcolo della spesa, questa tariffa e' ##
+## identica a "Teloconomy24_Internet.rst". ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Teleconomy_NoStop_Internet
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Tariffa unica
+default=(0.0092, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Tiscali_Urbane.rst b/kppp/Rules/Italy/Tiscali_Urbane.rst
new file mode 100644
index 00000000..e6845b6f
--- /dev/null
+++ b/kppp/Rules/Italy/Tiscali_Urbane.rst
@@ -0,0 +1,63 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Pino Toscano (toscano.pino@tiscali.it)
+#
+# Tiscali_Urbane.rst
+#
+# CHIAMATE URBANE
+#
+# Ultimo aggiornamento: 16 Luglio 2003.
+#
+################################################################
+## ##
+## Addebito alla risposta di 0,0619 EUR. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: 0,0095 ##
+## Tariffa INTERA: 0,0169 ##
+## ##
+## | intera dalle 08:00 alle 18:30 ##
+## Giorni FERIALI: | ridotta dalle 18:30 alle 08:00 ##
+## ##
+## Giorni FESTIVI: ridotta tutto il giorno ##
+## ##
+## Sabato: | intera dalle 08:00 alle 13:00 ##
+## | ridotta dalle 13:00 alle 08:00 ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Tiscali_Urbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Addebito alla risposta
+per_connection=0.0619
+
+# Tariffa ridotta
+default=(0.0095, 60)
+
+# Giorni feriali, sabato e domenica
+on (monday..friday) between (8:00..18:30) use (0.0169, 60)
+on (saturday) between (8:00..13:00) use (0.0169, 60)
+
+# Giorni festivi
+on (01/01) between () use (0.0095, 60)
+on (01/06) between () use (0.0095, 60)
+on (04/25) between () use (0.0095, 60)
+on (05/01) between () use (0.0095, 60)
+on (08/15) between () use (0.0095, 60)
+on (11/01) between () use (0.0095, 60)
+on (12/08) between () use (0.0095, 60)
+on (12/25) between () use (0.0095, 60)
+on (12/26) between () use (0.0095, 60)
+on (easter) between () use (0.0095, 60)
+on (easter + 1) between () use (0.0095, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_24ore_Internet_AltriISP.rst b/kppp/Rules/Italy/Wind_24ore_Internet_AltriISP.rst
new file mode 100644
index 00000000..a4e8421f
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_24ore_Internet_AltriISP.rst
@@ -0,0 +1,38 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_InWind.rst
+#
+# Chiamate dirette ad un ISP diverso da Wind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: 0,0341 tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=24ore_Internet_AltriISP
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.0341, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_24ore_Internet_InWind.rst b/kppp/Rules/Italy/Wind_24ore_Internet_InWind.rst
new file mode 100644
index 00000000..79d26db7
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_24ore_Internet_InWind.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_InWind.rst
+#
+# Chiamate dirette ad un POP InWind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: fino a 3 min = 0,0155 oltre = 0,0124 ##
+## Tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=24ore_Internet_InWind
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.0155, 60)
+default=(0.0124, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_24ore_Interurbane.rst b/kppp/Rules/Italy/Wind_24ore_Interurbane.rst
new file mode 100644
index 00000000..c48dae84
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_24ore_Interurbane.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Interurbane.rst
+#
+# CHIAMATE INTERURBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: fino a 3 min = 0,1116 oltre = 0,0107 ##
+## Tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=24ore_Interurbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.1116, 60)
+default=(0.0107, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_24ore_Urbane.rst b/kppp/Rules/Italy/Wind_24ore_Urbane.rst
new file mode 100644
index 00000000..2262f872
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_24ore_Urbane.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Urbane.rst
+#
+# CHIAMATE URBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: fino a 3 min = 0,0341 oltre = 0,0273 ##
+## Tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=24ore_Urbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.0341, 60)
+default=(0.0273, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family+SuperLight_Internet_InWind.rst b/kppp/Rules/Italy/Wind_Family+SuperLight_Internet_InWind.rst
new file mode 100644
index 00000000..29a37867
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family+SuperLight_Internet_InWind.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_InWind.rst
+#
+# Chiamate dirette ad un POP InWind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 3 min = 0,0106 oltre = 0,0085 ##
+## Tariffa INTERA: fino a 3 min = 0,0205 oltre = 0,0164 ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Family+SuperLight_Internet_InWind
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta fino a 3 min
+default=(0.0106, 60)
+
+# Giorni feriali
+on (monday..friday) between (19:00..9:00) use (0.0085, 60, 180)
+on (monday..friday) between (9:00..19:00) use (0.0205, 60)
+on (monday..friday) between (9:00..19:00) use (0.0164, 60, 180)
+
+# Giorni festivi, sabato e domenica oltre 3 minuti
+on (saturday) between () use (0.0085, 60, 180)
+on (sunday) between () use (0.0085, 60, 180)
+on (01/01) between () use (0.0085, 60, 180)
+on (01/06) between () use (0.0085, 60, 180)
+on (04/25) between () use (0.0085, 60, 180)
+on (05/01) between () use (0.0085, 60, 180)
+on (08/15) between () use (0.0085, 60, 180)
+on (11/01) between () use (0.0085, 60, 180)
+on (12/08) between () use (0.0085, 60, 180)
+on (12/25) between () use (0.0085, 60, 180)
+on (12/26) between () use (0.0085, 60, 180)
+on (easter) between () use (0.0085, 60, 180)
+on (easter + 1) between () use (0.0085, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family+SuperLight_Urbane_Interurbane.rst b/kppp/Rules/Italy/Wind_Family+SuperLight_Urbane_Interurbane.rst
new file mode 100644
index 00000000..ce5f46d0
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family+SuperLight_Urbane_Interurbane.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Urbane_Interurbane.rst
+#
+# CHIAMATE URBANE E INTERURBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 3 min = 0,0248 oltre = 0,0198 ##
+## Tariffa INTERA: fino a 3 min = 0,0341 oltre = 0,0273 ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Family+SuperLight_Urbane_Interurbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta fino a 3 min
+default=(0.0248, 60)
+
+# Giorni feriali
+on (monday..friday) between (19:00..9:00) use (0.0198, 60, 180)
+on (monday..friday) between (9:00..19:00) use (0.0341, 60)
+on (monday..friday) between (9:00..19:00) use (0.0273, 60, 180)
+
+# Giorni festivi, sabato e domenica oltre 3 minuti
+on (saturday) between () use (0.0198, 60, 180)
+on (sunday) between () use (0.0198, 60, 180)
+on (01/01) between () use (0.0198, 60, 180)
+on (01/06) between () use (0.0198, 60, 180)
+on (04/25) between () use (0.0198, 60, 180)
+on (05/01) between () use (0.0198, 60, 180)
+on (08/15) between () use (0.0198, 60, 180)
+on (11/01) between () use (0.0198, 60, 180)
+on (12/08) between () use (0.0198, 60, 180)
+on (12/25) between () use (0.0198, 60, 180)
+on (12/26) between () use (0.0198, 60, 180)
+on (easter) between () use (0.0198, 60, 180)
+on (easter + 1) between () use (0.0198, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family_Internet_AltriISP.rst b/kppp/Rules/Italy/Wind_Family_Internet_AltriISP.rst
new file mode 100644
index 00000000..0d9322f1
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family_Internet_AltriISP.rst
@@ -0,0 +1,60 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_AltriISP.rst
+#
+# Chiamate dirette ad un ISP diverso da Wind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: 0,0248 EUR ##
+## Tariffa INTERA : 0,0341 EUR ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Wind_Family_Internet_AltriISP
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta
+default=(0.0248, 60)
+
+# Giorni feriali
+on (monday..friday) between (9:00..19:00) use (0.0341, 60)
+
+# Giorni festivi
+on (01/01) between () use (0.0248, 60)
+on (01/06) between () use (0.0248, 60)
+on (04/25) between () use (0.0248, 60)
+on (05/01) between () use (0.0248, 60)
+on (08/15) between () use (0.0248, 60)
+on (11/01) between () use (0.0248, 60)
+on (12/08) between () use (0.0248, 60)
+on (12/25) between () use (0.0248, 60)
+on (12/26) between () use (0.0248, 60)
+on (easter) between () use (0.0248, 60)
+on (easter + 1) between () use (0.0248, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family_Internet_InWind.rst b/kppp/Rules/Italy/Wind_Family_Internet_InWind.rst
new file mode 100644
index 00000000..9cd7291a
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family_Internet_InWind.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_InWind.rst
+#
+# Chiamate dirette ad un POP InWind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 3 min = 0,0106 oltre = 0,0085 ##
+## Tariffa INTERA: fino a 3 min = 0,0205 oltre = 0,0164 ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Wind_Family_Internet_InWind
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta fino a 3 min
+default=(0.0106, 60)
+
+# Giorni feriali
+on (monday..friday) between (19:00..9:00) use (0.0085, 60, 180)
+on (monday..friday) between (9:00..19:00) use (0.0205, 60)
+on (monday..friday) between (9:00..19:00) use (0.0164, 60, 180)
+
+# Giorni festivi, sabato e domenica oltre 3 minuti
+on (saturday) between () use (0.0085, 60, 180)
+on (sunday) between () use (0.0085, 60, 180)
+on (01/01) between () use (0.0085, 60, 180)
+on (01/06) between () use (0.0085, 60, 180)
+on (04/25) between () use (0.0085, 60, 180)
+on (05/01) between () use (0.0085, 60, 180)
+on (08/15) between () use (0.0085, 60, 180)
+on (11/01) between () use (0.0085, 60, 180)
+on (12/08) between () use (0.0085, 60, 180)
+on (12/25) between () use (0.0085, 60, 180)
+on (12/26) between () use (0.0085, 60, 180)
+on (easter) between () use (0.0085, 60, 180)
+on (easter + 1) between () use (0.0085, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family_Interurbane.rst b/kppp/Rules/Italy/Wind_Family_Interurbane.rst
new file mode 100644
index 00000000..144d64a3
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family_Interurbane.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Interurbane.rst
+#
+# CHIAMATE INTERURBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 3 min = 0,0775 oltre = 0,0620 ##
+## Tariffa INTERA: fino a 3 min = 0,1240 oltre = 0,0992 ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Wind_Family_Interurbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta fino a 3 min
+default=(0.0775, 60)
+
+# Giorni feriali
+on (monday..friday) between (19:00..9:00) use (0.0620, 60, 180)
+on (monday..friday) between (9:00..19:00) use (0.1240, 60)
+on (monday..friday) between (9:00..19:00) use (0.0992, 60, 180)
+
+# Giorni festivi, sabato e domenica oltre 3 minuti
+on (saturday) between () use (0.0620, 60, 180)
+on (sunday) between () use (0.0620, 60, 180)
+on (01/01) between () use (0.0620, 60, 180)
+on (01/06) between () use (0.0620, 60, 180)
+on (04/25) between () use (0.0620, 60, 180)
+on (05/01) between () use (0.0620, 60, 180)
+on (08/15) between () use (0.0620, 60, 180)
+on (11/01) between () use (0.0620, 60, 180)
+on (12/08) between () use (0.0620, 60, 180)
+on (12/25) between () use (0.0620, 60, 180)
+on (12/26) between () use (0.0620, 60, 180)
+on (easter) between () use (0.0620, 60, 180)
+on (easter + 1) between () use (0.0620, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Family_Urbane.rst b/kppp/Rules/Italy/Wind_Family_Urbane.rst
new file mode 100644
index 00000000..c2cacfd3
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Family_Urbane.rst
@@ -0,0 +1,64 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Urbane.rst
+#
+# CHIAMATE URBANE
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa RIDOTTA: fino a 3 min = 0,0248 oltre = 0,0198 ##
+## Tariffa INTERA: fino a 3 min = 0,0341 oltre = 0,0273 ##
+## ##
+## | intera dalle 09:00 alle 19:00 ##
+## Giorni FERIALI: | ridotta dalle 19:00 alle 09:00 ##
+## ##
+## Giorni FESTIVI: | ridotta tutto il giorno ##
+## e SABATO: | ##
+## ##
+################################################################
+
+# Nome del ruleset
+name=Wind_Family_Urbane
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa ridotta fino a 3 min
+default=(0.0248, 60)
+
+# Giorni feriali
+on (monday..friday) between (19:00..9:00) use (0.0198, 60, 180)
+on (monday..friday) between (9:00..19:00) use (0.0341, 60)
+on (monday..friday) between (9:00..19:00) use (0.0273, 60, 180)
+
+# Giorni festivi, sabato e domenica oltre 3 minuti
+on (saturday) between () use (0.0198, 60, 180)
+on (sunday) between () use (0.0198, 60, 180)
+on (01/01) between () use (0.0198, 60, 180)
+on (01/06) between () use (0.0198, 60, 180)
+on (04/25) between () use (0.0198, 60, 180)
+on (05/01) between () use (0.0198, 60, 180)
+on (08/15) between () use (0.0198, 60, 180)
+on (11/01) between () use (0.0198, 60, 180)
+on (12/08) between () use (0.0198, 60, 180)
+on (12/25) between () use (0.0198, 60, 180)
+on (12/26) between () use (0.0198, 60, 180)
+on (easter) between () use (0.0198, 60, 180)
+on (easter + 1) between () use (0.0198, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Flat_Internet_AltriISP.rst b/kppp/Rules/Italy/Wind_Flat_Internet_AltriISP.rst
new file mode 100644
index 00000000..68b1c522
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Flat_Internet_AltriISP.rst
@@ -0,0 +1,38 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_AltriISP.rst
+#
+# Chiamate dirette ad un ISP diverso da Wind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: 0,0341 tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Flat_Internet_AltriISP
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.0341, 60)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Flat_Internet_InWind.rst b/kppp/Rules/Italy/Wind_Flat_Internet_InWind.rst
new file mode 100644
index 00000000..1a1cbb03
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Flat_Internet_InWind.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp ruleset for Italy
+#
+# by Luca Boni (me9139@mclink.it)
+#
+# Internet_InWind.rst
+#
+# Chiamate dirette ad un POP InWind.
+#
+# Ultimo aggiornamento: 7 Marzo 2002.
+#
+################################################################
+## ##
+## Nessun addebito alla risposta. ##
+## ##
+## Tutti i prezzi si intendono in EUR/min IVA COMPRESA. ##
+## ##
+## Tariffa unica: fino a 3 min = 0,0155 oltre = 0,0093 ##
+## Tutti i giorni a tutte le ore ##
+## ##
+################################################################
+
+
+# Nome del ruleset
+name=Flat_Internet_InWind
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+# Nessun addebito alla risposta
+per_connection=0
+
+# Tariffa unica
+default=(0.0155, 60)
+default=(0.0093, 60, 180)
+
+# Fine
diff --git a/kppp/Rules/Italy/Wind_Urbana_1088_Light.rst b/kppp/Rules/Italy/Wind_Urbana_1088_Light.rst
new file mode 100644
index 00000000..969e38d0
--- /dev/null
+++ b/kppp/Rules/Italy/Wind_Urbana_1088_Light.rst
@@ -0,0 +1,53 @@
+################################################################
+# kppp ruleset for Italy
+#
+# Wind con piano telefonico '1088 light'
+# Tariffa: urbana
+#
+# by Giovanni Venturi (jumpyj@tiscali.it)
+################################################################
+
+# Nome del ruleset
+name=Wind_1088_light
+
+# Impostazioni della valuta
+currency_symbol=EUR
+currency_position=right
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+#
+# The cost is ¤ 0,3719 every hour of connection.
+# Il costo e` ¤ 0,3719 ogni ora di connessione.
+################################################################
+
+# Nessun addebito alla risposta
+per_connection=0.0
+minimum_costs=0.0
+
+default=(0.0001, 1)
+
+
+# Il costo e` di ¤ 0,7437 l'ora nei giorni feriali
+on (monday..friday) between (09:00..19:00) use (0.0002, 1)
+
+# Giorni festivi, sabato e domenica
+on (saturday) between () use (0.0001, 1)
+on (sunday) between () use (0.0001, 1)
+on (01/01) between () use (0.0001, 1)
+on (01/06) between () use (0.0001, 1)
+on (04/25) between () use (0.0001, 1)
+on (05/01) between () use (0.0001, 1)
+on (08/15) between () use (0.0001, 1)
+on (11/01) between () use (0.0001, 1)
+on (12/08) between () use (0.0001, 1)
+on (12/25) between () use (0.0001, 1)
+on (12/26) between () use (0.0001, 1)
+on (easter) between () use (0.0001, 1)
+on (easter + 1) between () use (0.0001, 1)
+# NOTA: IVA inclusa
+
+# End of file
diff --git a/kppp/Rules/Jamaica/CWJ_InterParish.rst b/kppp/Rules/Jamaica/CWJ_InterParish.rst
new file mode 100644
index 00000000..f6393eec
--- /dev/null
+++ b/kppp/Rules/Jamaica/CWJ_InterParish.rst
@@ -0,0 +1,120 @@
+################################################################
+# This is the Ruleset for Jamaica.
+# 8 Mar 1998 (Valid until CWJamaica disides to do some deaper
+# price gaoging ?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE READ THE
+# RELEVANT SECTION OF THE PHONE BOOK AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+# Kevin Forge
+# <forgeltd@usa.net>
+#
+# If you use an ISP in another parish, these rules aply
+#
+# CWJ_InterParish.rst
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=default
+
+################################################################
+# currency settings
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=$
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.38
+
+
+# You pay .76 for the first 60 seconds ( 1minute ) no matter
+# whether you are connected for 1 second or 60 seconds.
+# This rule will take priority during the first 60 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(0.76, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.76" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.76, 60)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 7:00 pm until 6:59 am the costs
+# are 0.38 each 60 seconds"
+on () between (19:00..11:59) use (0.38, 60)
+on () between (0:0..6:59) use (0.38, 60)
+
+
+# same as above
+# on (monday..sunday) between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+# on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# applies on sunday
+on (sunday) between () use(0.38, 60)
+
+# ATTENTION:
+# on(monday..friday) between (21:00..5:00) use (0.4,2)
+# does NOT include saturday 0:00-5:00, just monday..friday, as it says.
+
+# applies on a given date (christmas)
+# on (12/25) between () use (0.3,72)
+
+# This is most of the holidays
+on (1/1, 8/4, 12/25, 12/26, ) between () use (0.38, 60)
+
+# use this for easter
+on (easter) between () use (0.38, 60)
+
+# easter + 60 days (Pfingstmontag/ Pentecost Monday )
+# easter - 44 days ( Ash Wedensday )
+on (easter-44) between () use (0.38, 60)
+
+# ATTENTION:
+# Enable this if within your program easter is just good friday
+# and change "(easter+3)" to "(easter-3)" if it's Easter Monday
+# on (easter+3) between () use (0.38, 60)
+
+# on (thursday) between (20:00..21:52) use (8.2, 1)
diff --git a/kppp/Rules/Jamaica/CWJ_Local.rst b/kppp/Rules/Jamaica/CWJ_Local.rst
new file mode 100644
index 00000000..48cf198a
--- /dev/null
+++ b/kppp/Rules/Jamaica/CWJ_Local.rst
@@ -0,0 +1,118 @@
+################################################################
+# This is the Ruleset for Jamaica.
+# 8 Mar 1998 (Valid until CWJamaica disides to do some deaper
+# price gaoging ?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE READ THE
+# RELEVANT SECTION OF THE PHONE BOOK AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+# Kevin Forge
+# <forgeltd@usa.net>
+#
+# If you use an ISP in the same parish, these rules aply
+#
+# CWJ_Local.rst
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=default
+
+################################################################
+# currency settings
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=$
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.15
+
+
+# You pay .76 for the first 60 secons ( 1minute ) no matter
+# whether you are connected for 1 second or 60 seconds.
+# This rule will take priority during the first 60 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(0.15, 60)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.15" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.15
+# Cents are added to the bill"
+default=(0.15, 60)
+
+#
+# more complicated rules:
+# do not aply since for you there is NO MERCY!!
+
+# "on monday until sunday from 7:00 pm until 6:59 am the costs
+# are 0.38 each 60 seconds"
+# on () between () use (0.15, 60)
+
+# same as above
+# on (monday..sunday) between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+# on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# applies on sunday
+# on (sunday) between () use(0.38, 60)
+
+# ATTENTION:
+# on(monday..friday) between (21:00..5:00) use (0.4,2)
+# does NOT include saturday 0:00-5:00, just monday..friday, as it says.
+
+# applies on a given date (christmas)
+# on (12/25) between () use (0.3,72)
+
+# This is most of the holidays
+# on (1/1, 8/4, 12/25, 12/26, ) between () use (0.38, 60)
+
+# use this for easter
+# on (easter) between () use (0.38, 60)
+
+# easter + 60 days (Pfingstmontag/ Pentecost Monday )
+# easter - 44 days ( Ash Wedensday )
+# on (easter-44) between () use (0.38, 60)
+
+# ATTENTION:
+# Enable this if within your program easter is just good friday
+# and change "(easter+3)" to "(easter-3)" if it's Easter Monday
+# on (easter+3) between () use (0.38, 60)
+
+# on (thursday) between (20:00..21:52) use (8.2, 1)
diff --git a/kppp/Rules/Jamaica/Makefile.am b/kppp/Rules/Jamaica/Makefile.am
new file mode 100644
index 00000000..4d05e297
--- /dev/null
+++ b/kppp/Rules/Jamaica/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = CWJ_InterParish.rst \
+ CWJ_Local.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Jamaica
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Japan/Makefile.am b/kppp/Rules/Japan/Makefile.am
new file mode 100644
index 00000000..486f523f
--- /dev/null
+++ b/kppp/Rules/Japan/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = NTT_Local.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Japan
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Japan/NTT_Local.rst b/kppp/Rules/Japan/NTT_Local.rst
new file mode 100644
index 00000000..c9ded14e
--- /dev/null
+++ b/kppp/Rules/Japan/NTT_Local.rst
@@ -0,0 +1,58 @@
+################################################################
+# This is the rule set for Japan, local NTT charge
+#
+# Jacek Cwielong <cwielong@annie.co.jp>
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=NTT-Local
+
+################################################################
+# currency settings
+################################################################
+
+# defines JPY (Japanese Yen) to be used as currency
+currency_symbol=JPY
+
+# Define the position of the currency symbol.
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+# This is the default rule which is used when no other rule
+# applies. The first component "10" is the price of one
+# "unit", while "180" is the duration in seconds.
+# Therefore the following rule means: "Every 180 seconds
+# 10 YPJ are added to the bill"
+default=(10,180)
+
+# applies 8am until 11pm: every 180 seconds 10 JPY
+# are added to the bill
+on () between (8:00..22:59) use(10,180)
+
+# applies 23pm until 8am: every 240 seconds 10 JPY
+# are added to the bill
+on () between (23:00..7:59) use(10,240)
diff --git a/kppp/Rules/Kazakhstan/Akparat_Sprint.rst b/kppp/Rules/Kazakhstan/Akparat_Sprint.rst
new file mode 100644
index 00000000..2e929114
--- /dev/null
+++ b/kppp/Rules/Kazakhstan/Akparat_Sprint.rst
@@ -0,0 +1,95 @@
+################################################################
+#
+# This is a sample rule set for kppp. You can use it as a
+# template when you have to create your own ruleset. If you do
+# so, remove all comments and add your own. This will allow
+# other users to check your ruleset more easily.
+#
+# Please sign the the tarif file with your name an email address
+# so that I can contact you if necessary.
+#
+# NOTE: the rules in this rule set do not make much sense and
+# are only for demonstration purposes
+#
+# NOTE ON FILENAMES:
+# when you create your own ruleset, use "_" in filename
+# instead of spaces and use ".rst as extension
+# i.e. "Austria city calls"
+# --> file should be saved as "Austria_city_calls.rst"
+#
+# Thanks, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Akparat Sprint
+
+################################################################
+# currency settings
+################################################################
+
+# defines ATS (Austrian Schilling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol="$"
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+# flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.1333, 60)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+# same as above
+on (monday..sunday) between (0:00..10:00) use (0.016, 60)
+on (monday..sunday) between (23:00..23:59) use (0.016, 60)
+on (monday..friday) between (20:00..23:00) use (0.0666, 60)
+on (saturday..sunday) between (10:00..23:00) use (0.0666, 60)
diff --git a/kppp/Rules/Kazakhstan/Makefile.am b/kppp/Rules/Kazakhstan/Makefile.am
new file mode 100644
index 00000000..27aa305b
--- /dev/null
+++ b/kppp/Rules/Kazakhstan/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = Akparat_Sprint.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Kazakhstan
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Luxembourg/CMD_InternetGratuit.rst b/kppp/Rules/Luxembourg/CMD_InternetGratuit.rst
new file mode 100644
index 00000000..747d3710
--- /dev/null
+++ b/kppp/Rules/Luxembourg/CMD_InternetGratuit.rst
@@ -0,0 +1,71 @@
+################################################################
+#
+# KPPP accounting rules for "Internet Gratuit" by CMD
+#
+# The information is taken from www.cmd.lu. The rules concerning
+# legal holidays have been confirmed by CMD. (There was no
+# mention of holidays on their website.)
+# Taxes are included.
+#
+# 11.02.2005
+# Gilles Schintgen <gilles@vonet.lu>
+#
+# ##############################################################
+
+################################################################
+# name of the ruleset
+################################################################
+name=CMD_InternetGratuit
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=€
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+# per_connection=0.0
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# minimum_costs=0.0
+
+# This is what you pay for the first unit.
+# flat_init_costs=(0.0, 0)
+
+# This is the default rule which is used when no other rule
+# applies. One unit (60 seconds) accounts for 0.031 €
+# (= 1,25 LUF)
+default=(0.031, 60)
+
+# Normal Costs
+on (monday..friday) between (06:00..17:59) use (0.031, 60)
+on (monday..friday) between (18:00..22:59) use (0.0155, 60)
+on (saturday..sunday) between (06:00..22:59) use (0.0155, 60)
+on () between (00:00..05:59) use (0.0077, 60)
+on () between (23:00..23:59) use (0.0077, 60)
+
+# Legal Holidays
+# these days are billed just as if they were sundays
+on (01/01, easter+1, 05/01, easter+39, easter+50) between (06:00..22:59) use (0.0155, 60)
+on (06/23, 08/15, 11/01, 12/25) between (06:00..22:59) use (0.0155, 60)
+
+# 01/01: Nouvel An (New Year)
+# easter+1: Lundi de Pâques (Easter Monday)
+# 05/01: Fête du travail (Labor Day)
+# easter+39: Ascension
+# easter+50: Lundi de Pentecôte (Whit Monday)
+# 06/23: Fête nationale (national holiday)
+# 08/15: Assomption (Assumption)
+# 11/01: Toussaint (All Saint's Day)
+# 12/25: Noël (Christmas)
diff --git a/kppp/Rules/Luxembourg/LuxembourgOnline_FreeInternet.rst b/kppp/Rules/Luxembourg/LuxembourgOnline_FreeInternet.rst
new file mode 100644
index 00000000..5431c918
--- /dev/null
+++ b/kppp/Rules/Luxembourg/LuxembourgOnline_FreeInternet.rst
@@ -0,0 +1,72 @@
+################################################################
+#
+# KPPP accounting rules for "Free Internet" by Luxembourg Online
+# (www.internet.lu; dialup number 27300030)
+#
+# For this ISP you'll only have to pay the costs of a local phone
+# call.
+# The information is taken from the current (as of 11.02.2005)
+# official price list of the "Entreprise des Postes et
+# Télécommunications Luxembourg". (Taxes are included.)
+#
+# 11.02.2005
+# Gilles Schintgen <gilles@vonet.lu>
+#
+# ##############################################################
+
+################################################################
+# name of the ruleset
+################################################################
+name=LuxembourgOnline_FreeInternet
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=€
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+# per_connection=0.0
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# minimum_costs=0.0
+
+# This is what you pay for the first unit.
+# flat_init_costs=(0.0, 0)
+
+# This is the default rule which is used when no other rule
+# applies. One unit (60 seconds) accounts for 0.0309 €
+# (= 1,25 LUF)
+default=(0.0309, 60)
+
+# Normal Costs
+on (monday..friday) between (00:00..07:59) use (0.0154, 60)
+on (monday..friday) between (08:00..18:59) use (0.0309, 60)
+on (monday..friday) between (19:00..23:59) use (0.0154, 60)
+on (saturday..sunday) between () use (0.0154, 60)
+
+# Legal Holidays
+# these days are billed just as if they were sundays
+on (01/01, easter+1, 05/01, easter+39, easter+50) between () use (0.0154, 60)
+on (06/23, 08/15, 11/01, 12/25) between () use (0.0154, 60)
+
+# 01/01: Nouvel An (New Year)
+# easter+1: Lundi de Pâques (Easter Monday)
+# 05/01: Fête du travail (Labor Day)
+# easter+39: Ascension
+# easter+50: Lundi de Pentecôte (Whit Monday)
+# 06/23: Fête nationale (national holiday)
+# 08/15: Assomption (Assumption)
+# 11/01: Toussaint (All Saint's Day)
+# 12/25: Noël (Christmas)
diff --git a/kppp/Rules/Luxembourg/Makefile.am b/kppp/Rules/Luxembourg/Makefile.am
new file mode 100644
index 00000000..0c8005fd
--- /dev/null
+++ b/kppp/Rules/Luxembourg/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = CMD_InternetGratuit.rst LuxembourgOnline_FreeInternet.rst \
+ PetT_ClassicSurf.rst PetT_KioskSurf.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Luxembourg
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Luxembourg/PetT_ClassicSurf.rst b/kppp/Rules/Luxembourg/PetT_ClassicSurf.rst
new file mode 100644
index 00000000..329d4382
--- /dev/null
+++ b/kppp/Rules/Luxembourg/PetT_ClassicSurf.rst
@@ -0,0 +1,70 @@
+################################################################
+#
+# KPPP accounting rules for "ClassicSurf" by "Entreprise des Postes
+# et Télécommunications Luxembourg".
+#
+# The information is taken from the current (as of 11.02.2005)
+# official price list of the "Entreprise des Postes et
+# Télécommunications Luxembourg". (Taxes are included.)
+#
+# 11.02.2005
+# Gilles Schintgen <gilles@vonet.lu>
+#
+# ##############################################################
+
+################################################################
+# name of the ruleset
+################################################################
+name=PetT_ClassicSurf
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=€
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+# per_connection=0.0
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# minimum_costs=0.0
+
+# This is what you pay for the first unit.
+# flat_init_costs=(0.0, 0)
+
+# This is the default rule which is used when no other rule
+# applies.
+default=(0.0285, 60)
+
+# Normal Costs
+on (monday..friday) between (06:00..17:59) use (0.0285, 60)
+on (monday..friday) between (18:00..22:59) use (0.0155, 60)
+on (saturday..sunday) between (06:00..22:59) use (0.0155, 60)
+on () between (00:00..05:59) use (0.0092, 60)
+on () between (23:00..23:59) use (0.0092, 60)
+
+# Legal Holidays
+# these days are billed just as if they were sundays
+on (easter+1, 05/01, easter+39, easter+50) between (06:00..22:59) use (0.0155, 60)
+on (01/01, 06/23, 08/15, 11/01, 12/25) between (06:00..22:59) use (0.0155, 60)
+
+# 01/01: Nouvel An (New Year)
+# easter+1: Lundi de Pâques (Easter Monday)
+# 05/01: Fête du travail (Labor Day)
+# easter+39: Ascension
+# easter+50: Lundi de Pentecôte (Whit Monday)
+# 06/23: Fête nationale (national holiday)
+# 08/15: Assomption (Assumption)
+# 11/01: Toussaint (All Saint's Day)
+# 12/25: Noël (Christmas)
diff --git a/kppp/Rules/Luxembourg/PetT_KioskSurf.rst b/kppp/Rules/Luxembourg/PetT_KioskSurf.rst
new file mode 100644
index 00000000..e17afded
--- /dev/null
+++ b/kppp/Rules/Luxembourg/PetT_KioskSurf.rst
@@ -0,0 +1,70 @@
+################################################################
+#
+# KPPP accounting rules for "KioskSurf" by "Entreprise des Postes
+# et Télécommunications Luxembourg".
+#
+# The information is taken from the current (as of 11.02.2005)
+# official price list of the "Entreprise des Postes et
+# Télécommunications Luxembourg". (Taxes are included.)
+#
+# 11.02.2005
+# Gilles Schintgen <gilles@vonet.lu>
+#
+# ##############################################################
+
+################################################################
+# name of the ruleset
+################################################################
+name=PetT_KioskSurf
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=€
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+# per_connection=0.0
+
+# Minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# minimum_costs=0.0
+
+# This is what you pay for the first unit.
+# flat_init_costs=(0.0, 0)
+
+# This is the default rule which is used when no other rule
+# applies.
+default=(0.0496, 60)
+
+# Normal Costs
+on (monday..friday) between (06:00..17:59) use (0.0496, 60)
+on (monday..friday) between (18:00..22:59) use (0.0248, 60)
+on (saturday..sunday) between (06:00..22:59) use (0.0248, 60)
+on () between (00:00..05:59) use (0.0155, 60)
+on () between (23:00..23:59) use (0.0155, 60)
+
+# Legal Holidays
+# these days are billed just as if they were sundays
+on (easter+1, 05/01, easter+39, easter+50) between (06:00..22:59) use (0.0248, 60)
+on (01/01, 06/23, 08/15, 11/01, 12/25) between (06:00..22:59) use (0.0248, 60)
+
+# 01/01: Nouvel An (New Year)
+# easter+1: Lundi de Pâques (Easter Monday)
+# 05/01: Fête du travail (Labor Day)
+# easter+39: Ascension
+# easter+50: Lundi de Pentecôte (Whit Monday)
+# 06/23: Fête nationale (national holiday)
+# 08/15: Assomption (Assumption)
+# 11/01: Toussaint (All Saint's Day)
+# 12/25: Noël (Christmas)
diff --git a/kppp/Rules/Macedonia/Macedonia_GenericISP_interurban.rst b/kppp/Rules/Macedonia/Macedonia_GenericISP_interurban.rst
new file mode 100644
index 00000000..17ce3306
--- /dev/null
+++ b/kppp/Rules/Macedonia/Macedonia_GenericISP_interurban.rst
@@ -0,0 +1,30 @@
+##########################################################
+# kppp ruleset for all Macedonian ISPs (except MTnet)
+# This ruleset is for interurban connections
+# For the latest prices, call the phone number 971
+#
+# Created on 4-Jun-2001
+# Last modified on 1-Jul-2004
+#
+# Darko Spasovski, darkos@mt.net.mk
+# Georgi Stanojevski, georgi@unet.com.mk
+##########################################################
+
+name=Macedonia_GenericISP_interurban
+
+currency_symbol=DEN
+currency_position=right
+currency_digits=2
+
+per_connection=0.0
+minimum_costs=0.0
+default=(1.00, 20)
+
+#############################################################
+# 06:00-17:59 3.00 den. za 1 minuta (1 den za 20 sekundi)
+# 18:00-05:59 1.50 den. za 1 minuta (.05 den za 20 sekundi)
+# Vo cenite ne e presmetan DDV (+18%)
+#############################################################
+
+on () between (6:00..17:59) use (1.00,20)
+on () between (18:00..5:59) use (0.50,20)
diff --git a/kppp/Rules/Macedonia/Macedonia_GenericISP_local.rst b/kppp/Rules/Macedonia/Macedonia_GenericISP_local.rst
new file mode 100644
index 00000000..aa0f3005
--- /dev/null
+++ b/kppp/Rules/Macedonia/Macedonia_GenericISP_local.rst
@@ -0,0 +1,30 @@
+##########################################################
+# kppp ruleset for all Macedonian ISPs (except MTnet)
+# This ruleset is for local connections
+# For the latest prices, call the phone number 971
+#
+# Created on 4-Jun-2001
+# Last modified on 1-Jul-2004
+#
+# Darko Spasovski, darkos@mt.net.mk
+# Georgi Stanojevski, georgi@mt.net.mk
+##########################################################
+
+name=Macedonia_GenericISP_local
+
+currency_symbol=DEN
+currency_position=right
+currency_digits=2
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0.33, 20)
+
+#############################################################
+# 06:00-17:59 1 den. za 1 minuta (0,33 na 20 sekundi)
+# 18:00-05:59 0,6 den. za 1 minuta (0,2 na 20 sekundi)
+# Vo cenite ne e presmetan DDV (+18%)
+#############################################################
+
+on () between (6:00..17:59) use (0.33,20)
+on () between (18:00..5:59) use (0.20,20)
diff --git a/kppp/Rules/Macedonia/Macedonia_MTnet.rst b/kppp/Rules/Macedonia/Macedonia_MTnet.rst
new file mode 100644
index 00000000..dc2e8217
--- /dev/null
+++ b/kppp/Rules/Macedonia/Macedonia_MTnet.rst
@@ -0,0 +1,33 @@
+##########################################################
+# kppp ruleset for MTnet, local Macedonian ISP
+# See www.mt.net.mk for price updates
+#
+# Last updated on 4-Jun-2001
+#
+# Darko Spasovski, darkos@mt.net.mk
+##########################################################
+
+name=Macedonia_MTnet
+
+currency_symbol=DEN
+currency_position=right
+currency_digits=2
+
+per_connection=0.0
+minimum_costs=0.0
+default=(1.60, 60)
+
+#############################################################
+# 06:00-17:59 od ponedelnik do petok, 1.60 den. za minuta
+# 18:00-23:59 od ponedelnik do petok, 0.80 den. za minuta
+# 06:00-23:59 vikend i praznici, 0.80 den. za minuta
+# (za praznici se smetaat samo 1.01 i 2.05)
+# 00:00-05:59 sekoj den vo nedelata, 0.35 den. za minuta
+# Vo cenite ne e presmetan DDV (+18%)
+#############################################################
+
+on (monday..friday) between (6:00..17:59) use (1.60,60)
+on (monday..friday) between (18:00..23:59) use (0.80,60)
+on (saturday..sunday) between (6:00..23:59) use (0.80,60)
+on (01/01, 05/02) between (6:00..23:59) use (0.80,60)
+on () between (0:00..5:59) use (0.35,60)
diff --git a/kppp/Rules/Macedonia/Makefile.am b/kppp/Rules/Macedonia/Makefile.am
new file mode 100644
index 00000000..68515e48
--- /dev/null
+++ b/kppp/Rules/Macedonia/Makefile.am
@@ -0,0 +1,7 @@
+emo_DATA = Macedonia_GenericISP_interurban.rst \
+ Macedonia_GenericISP_local.rst \
+ Macedonia_MTnet.rst
+
+emodir = $(kde_datadir)/kppp/Rules/Macedonia
+
+EXTRA_DIST = $(emo_DATA)
diff --git a/kppp/Rules/Makefile.am b/kppp/Rules/Makefile.am
new file mode 100644
index 00000000..eb449306
--- /dev/null
+++ b/kppp/Rules/Makefile.am
@@ -0,0 +1,9 @@
+SUBDIRS = $(AUTODIRS)
+
+pkg_DATA = TEMPLATE
+pkg_SCRIPTS = checkrules
+
+pkgdir = $(kde_datadir)/kppp/Rules
+
+EXTRA_DIST = $(pkg_DATA)
+
diff --git a/kppp/Rules/Malaysia/Makefile.am b/kppp/Rules/Malaysia/Makefile.am
new file mode 100644
index 00000000..107f61d8
--- /dev/null
+++ b/kppp/Rules/Malaysia/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = malaysia.rst TMNet_Jaring.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Malaysia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Malaysia/TMNet_Jaring.rst b/kppp/Rules/Malaysia/TMNet_Jaring.rst
new file mode 100644
index 00000000..34d9aba6
--- /dev/null
+++ b/kppp/Rules/Malaysia/TMNet_Jaring.rst
@@ -0,0 +1,33 @@
+################################################################
+# This is the cost rule for local calls in Malaysia.
+# This cost rule include Dial-Up charge from Telekom and
+# Access charges from both Jaring or TMnet
+#
+# Paul Tan
+# root@vicert.eu.org | petra@shellyeah.org
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Malaysia_TelekomISP
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=RM
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.025
+minimum_costs=0.025
+flat_init_costs=(0.025,60)
+default=(0.025, 60)
diff --git a/kppp/Rules/Malaysia/malaysia.rst b/kppp/Rules/Malaysia/malaysia.rst
new file mode 100644
index 00000000..bf07b894
--- /dev/null
+++ b/kppp/Rules/Malaysia/malaysia.rst
@@ -0,0 +1,38 @@
+################################################################
+# This is the cost rule for local calls in Malaysia, if you
+# using Telekom, TMnet or Jaring as of 03/01/98.
+# This cost rule include only Dial-Up charge from Telekom and
+# not Access charges from both Jaring or TMnet
+# Acess : RM0.01 per minute (from TMnet and Jaring)
+# Dial-up : RM0.015 per minute (from Telekom)
+# Total : RM0.025 per minute
+# Or in other words the cost rule only calculate phone bill !
+#
+# Choong Hong Cheng
+# chc@tm.net.my OR chc@rocketmail.com
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Malaysia_Telekom
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=RM
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.015
+minimum_costs=0.015
+flat_init_costs=(0.015,60)
+default=(0.015, 60)
diff --git a/kppp/Rules/Netherlands/12Move.rst b/kppp/Rules/Netherlands/12Move.rst
new file mode 100644
index 00000000..83fa9af2
--- /dev/null
+++ b/kppp/Rules/Netherlands/12Move.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# kppp rules voor 12Move
+# informatie gehaald van http://www.12move.nl/content/article/376150.htm
+# laatste bijwerking op 26-11-2002
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 3.08 cpm = 0.0513333 cps 08:00-18:59
+# daltarief 1.65 cpm = 0.0275 cps 19:00-23:59
+# nacht 1.09 cpm = 0.0181667 cps 00:00-07:59
+# Zaterdag 1.09 cpm = 0.0181667 cps 00:00-23:59
+# Zondag 1.09 cpm = 0.0181667 cps 00:00-23:59
+#
+# Starttarief 4.14 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBudget Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+# standaardtarief:
+default=(0.000513333, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000275, 1)
+
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000181667, 1)
+
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.000181667, 1)
+on (sunday) between (00:00..23:59) use (0.000181667, 1)
diff --git a/kppp/Rules/Netherlands/BelBasis_Buiten_Regio.rst b/kppp/Rules/Netherlands/BelBasis_Buiten_Regio.rst
new file mode 100644
index 00000000..90032c6f
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBasis_Buiten_Regio.rst
@@ -0,0 +1,31 @@
+################################################################
+#
+# kppp rules voor KPN BelBasis abonnement per seconde
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Kosten:
+# standaardtarief doordeweeks 4.25 cpm=.0708333 cps 08:00-18:59
+# daltarief 'savonds 2.01 cpm= 0.335 cps 19.00-23.59
+# 's-nachts en in weekend 4.44 cpm=0.074 cps
+#
+# Starttarief 5 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.05
+minimum_costs=0.0
+
+default=(0.000335, 1)
+on (monday..friday) between (08:00..18:59) use (0.000708333, 1)
+on (monday..friday) between (19:00..23:59) use (0.000335, 1)
diff --git a/kppp/Rules/Netherlands/BelBasis_Buiten_Regio_Nummervoordeel.rst b/kppp/Rules/Netherlands/BelBasis_Buiten_Regio_Nummervoordeel.rst
new file mode 100644
index 00000000..2ccb26fc
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBasis_Buiten_Regio_Nummervoordeel.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# kppp rules voor KPN BelBasis abonnement per seconde + nummervoordeel
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Kosten:
+# standaardtarief doordeweeks 3.83 cpm=0.000638333 cps 08:00-18:59
+# 's-avonds en in weekend 1.81 cpm=0.000301667 cps
+#
+# Starttarief 4.5 ct per gesprek #
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Buiten Regio Nummervoordeel
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.045
+minimum_costs=0.0
+
+default=(0.000301667, 1)
+on (monday..friday) between (08:00..18:59) use (0.000638333, 1)
+
diff --git a/kppp/Rules/Netherlands/BelBasis_Regio.rst b/kppp/Rules/Netherlands/BelBasis_Regio.rst
new file mode 100644
index 00000000..b249c9df
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBasis_Regio.rst
@@ -0,0 +1,35 @@
+################################################################
+#
+# kppp rules voor KPN BelBasis abonnement per seconde
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2,80 cpm=0.0466667 cps 08:00-18:59
+# daltarief 1.5 cpm=0.025 cps 19:00-23:59
+# nacht 1 cpm=0.0166667 cps 00:00-07:59
+# Zaterdag 1 cpm=0.0166667 cps 00:00-23:59
+# Zondag 1 cpm=0.0166667 cps 00:00-23:59
+#
+# Starttarief 4.14 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+default=(0.000466667, 1)
+on (monday..friday) between (19:00..23:59) use (0.00025, 1)
+on (monday..friday) between (00:00..07:59) use (0.000166667, 1)
+on (saturday) between (00:00..23:59) use (0.000166667, 1)
+on (sunday) between (00:00..23:59) use (0.000166667, 1)
diff --git a/kppp/Rules/Netherlands/BelBasis_Regio_Nummervoordeel.rst b/kppp/Rules/Netherlands/BelBasis_Regio_Nummervoordeel.rst
new file mode 100644
index 00000000..a2100c6f
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBasis_Regio_Nummervoordeel.rst
@@ -0,0 +1,35 @@
+################################################################
+#
+# kppp rules voor KPN BelBasis abonnement + nummervoordeel per seconde
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2.52 cpm=0.042 cps 08:00-18:59
+# daltarief 1.35 cpm=0.0225 cps 19:00-23:59
+# nacht 0.9 cpm=0.015 cps 00:00-07:59
+# Zaterdag 0.9 cpm=0.015 cps 00:00-23:59
+# Zondag 0.9 cpm=0.015 cps 00:00-23:59
+#
+# Starttarief 3.73 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Regio Nummervoordeel per seconde
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0373
+minimum_costs=0.0
+
+default=(0.00042, 1)
+on (monday..friday) between (19:00..23:59) use (0.000225, 1)
+on (monday..friday) between (00:00..07:59) use (0.00015, 1)
+on (saturday) between (00:00..23:59) use (0.00015, 1)
+on (sunday) between (00:00..23:59) use (0.00015, 1)
diff --git a/kppp/Rules/Netherlands/BelBudget_Buiten_Regio.rst b/kppp/Rules/Netherlands/BelBudget_Buiten_Regio.rst
new file mode 100644
index 00000000..0a4b696b
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBudget_Buiten_Regio.rst
@@ -0,0 +1,30 @@
+################################################################
+#
+# kppp rules voor KPN BelBudget abonnement per seconde
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Kosten:
+# standaardtarief doordeweeks 21,25 cpm=0.354167 cps 08:00-18:59
+# 's-avonds en in weekend 10,06 cpm=0.167667 cps
+#
+# Starttarief 4.9 ct per gesprek #
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands Belbudget Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.049
+minimum_costs=0.0
+
+default=(0.00167667, 1)
+on (monday..friday) between (08:00..18:59) use (0.00354167, 1)
+
diff --git a/kppp/Rules/Netherlands/BelBudget_Regio.rst b/kppp/Rules/Netherlands/BelBudget_Regio.rst
new file mode 100644
index 00000000..7fff5134
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelBudget_Regio.rst
@@ -0,0 +1,36 @@
+################################################################
+#
+# kppp rules voor KPN BelBudget abonnement
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 9.18 cpm=0.153 cps 08:00-18:59
+# daltarief 4.54 cpm=0.0756667 cps 19:00-23:59
+# nacht 3.28 cpm=0.0546667 cps 00:00-07:59
+# Zaterdag 3.28 cpm=0.0546667 cps 00:00-23:59
+# Zondag 3.28 cpm=0.0546667 cps 00:00-23:59
+#
+# Starttarief 4.14 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBudget Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+default=(0.00153, 1)
+on (monday..friday) between (19:00..23:59) use (0.000756667, 1)
+on (monday..friday) between (00:00..07:59) use (0.000546667, 1)
+on (saturday) between (00:00..23:59) use (0.000546667, 1)
+on (sunday) between (00:00..23:59) use (0.000546667, 1)
diff --git a/kppp/Rules/Netherlands/BelPlus_Buiten_Regio.rst b/kppp/Rules/Netherlands/BelPlus_Buiten_Regio.rst
new file mode 100644
index 00000000..3bc7a742
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelPlus_Buiten_Regio.rst
@@ -0,0 +1,32 @@
+################################################################
+#
+# kppp rules voor KPN Belplus abonnement per seconde
+#
+# Alleen voor buiten de regio gesprekken
+# Laatste wijziging: 09-07-2003
+#
+# Kosten:
+# standaardtarief doordeweeks 4.39 cpm= 0.07316666667 cps 08:00-18:59
+# daltarief 'savonds 2,08 cpm= 0.03466666667 cps 19.00-23.59
+# 's-nachts en in weekend 2,08 cpm= 0.03466666667 cps
+#
+# Starttarief 0,0518 euro per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0518
+minimum_costs=0.0
+
+default=(0.03466666667, 1)
+on (monday..friday) between (08:00..18:59) use (0.07316666667, 1)
+on (monday..friday) between (19:00..23:59) use (0.03466666667, 1)
diff --git a/kppp/Rules/Netherlands/BelPlus_Buiten_Regio_Nummervoordeel.rst b/kppp/Rules/Netherlands/BelPlus_Buiten_Regio_Nummervoordeel.rst
new file mode 100644
index 00000000..114761e8
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelPlus_Buiten_Regio_Nummervoordeel.rst
@@ -0,0 +1,31 @@
+################################################################
+#
+# kppp rules voor KPN Belplus abonnement per seconde + nummervoordeel
+#
+# Alleen voor buiten de regio gesprekken
+# Laatste wijziging: 09-07-2003
+#
+# Kosten:
+# standaardtarief doordeweeks 3.51 cpm = 0.000585 euro ps 08:00-18:59
+# 's-avonds en in weekend 1,66 cpm = 0.0002766666667 euro ps
+#
+# Starttarief 4.14 ct per gesprek #
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Buiten Regio Nummervoordeel
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+default=(0.0002766666667, 1)
+on (monday..friday) between (08:00..18:59) use (0.000585, 1)
+
diff --git a/kppp/Rules/Netherlands/BelPlus_Regio.rst b/kppp/Rules/Netherlands/BelPlus_Regio.rst
new file mode 100644
index 00000000..385ac99d
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelPlus_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor KPN BelPlus abonnement per seconde
+# Laatste wijziging 09-07-2003
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2,89 cpm=0.0007583333333 euro ps 08:00-18:59
+# daltarief 1,55 cpm=0.0002583333333 euro ps 19:00-23:59
+# nacht 1,04 cpm=0.0001733333333 euro ps 00:00-07:59
+# Zaterdag 1,04 cpm=0.0001733333333 euro ps 00:00-23:59
+# Zondag 1,04 cpm=0.0001733333333 euro ps 00:00-23:59
+#
+# Starttarief 4.27 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelPlus Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0427
+minimum_costs=0.0
+
+default=(0.0007583333333, 1)
+on (monday..friday) between (19:00..23:59) use (0.0002583333333, 1)
+on (monday..friday) between (00:00..07:59) use (0.0001733333333, 1)
+on (saturday) between (00:00..23:59) use (0.0001733333333, 1)
+on (sunday) between (00:00..23:59) use (0.0001733333333, 1)
diff --git a/kppp/Rules/Netherlands/BelPlus_Regio_Nummervoordeel.rst b/kppp/Rules/Netherlands/BelPlus_Regio_Nummervoordeel.rst
new file mode 100644
index 00000000..bbb3d5ad
--- /dev/null
+++ b/kppp/Rules/Netherlands/BelPlus_Regio_Nummervoordeel.rst
@@ -0,0 +1,37 @@
+######################################################################
+#
+# kppp rules voor KPN BelPlus abonnement+ nummervoordeel per seconde
+# laatste wijziging 30-07-2003
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2.60 cpm = 0.0004333333333 euro ps 08:00-18:59
+# daltarief 1.40 cpm = 0.0002333333333 euro ps 19:00-23:59
+# nacht 0.94 cpm = 0.0001566666667 euro ps 00:00-07:59
+# Zaterdag 0.94 cpm = 0.0001566666667 euro ps 00:00-23:59
+# Zondag 0.94 cpm = 0.0001566666667 euro ps 00:00-23:59
+#
+# Starttarief 3.84 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+######################################################################
+
+name=Nederlands BelPlus Regio Nummervoordeel
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0384
+minimum_costs=0.0
+
+default=(0.0004333333333, 1)
+on (monday..friday) between (19:00..23:59) use (0.0002333333333, 1)
+on (monday..friday) between (00:00..07:59) use (0.0001566666667, 1)
+on (saturday) between (00:00..23:59) use (0.0001566666667, 1)
+on (sunday) between (00:00..23:59) use (0.0001566666667, 1)
diff --git a/kppp/Rules/Netherlands/Cistron b/kppp/Rules/Netherlands/Cistron
new file mode 100644
index 00000000..68adc4b0
--- /dev/null
+++ b/kppp/Rules/Netherlands/Cistron
@@ -0,0 +1,36 @@
+######################################################################
+#
+# kppp rules voor KPN BelPlus abonnement+ nummervoordeel per seconde
+# Gegevens gebruikt voor gespreksosten voor Cistron
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2.52 cpm=0.042 cps 08:00-18:59
+# daltarief 1.13 cpm=0.0188333 cps 19:00-23:59
+# nacht 0.9 cpm=0.015 cps 00:00-07:59
+# Zaterdag 0.9 cpm=0.015 cps 00:00-23:59
+# Zondag 0.9 cpm=0.015 cps 00:00-23:59
+#
+# Starttarief 3.73 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+######################################################################
+
+name=Nederlands BelPlus Regio Nummervoordeel
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0373
+minimum_costs=0.0
+
+default=(0.00042, 1)
+on (monday..friday) between (19:00..23:59) use (0.000188333, 1)
+on (monday..friday) between (00:00..07:59) use (0.00015, 1)
+on (saturday) between (00:00..23:59) use (0.00015, 1)
+on (sunday) between (00:00..23:59) use (0.00015, 1)
diff --git a/kppp/Rules/Netherlands/Freeler_Basis.rst b/kppp/Rules/Netherlands/Freeler_Basis.rst
new file mode 100644
index 00000000..d96cb207
--- /dev/null
+++ b/kppp/Rules/Netherlands/Freeler_Basis.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp rules voor Freeler Basis abonnement per seconde
+# http://www.freeler.nl/service/tarieven.html
+# bijgewerkt op 30-11-2002
+# Alleen voor lokale gesprekken
+# Let op! bedragen in euro per seconde!!
+# Kosten:
+# Lokaal: piektarief 0.0325 euro pm = 0.00054167 cps 08:00-18:59
+# daltarief 0.0177 euro pm = 0.000295 cps 19:00-23:59
+# nacht 0.0129 euro pm = 0.00215 cps 00:00-07:59
+# Zaterdag 0.0129 euro pm = 0.00215 cps 00:00-23:59
+# Zondag 0.0129 euro pm = 0.00215 cps 00:00-23:59
+#
+# Starttarief 0.0414 euro per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Freeler Basis
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+# piektarief
+default=(0.00054167, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000295, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.00215, 1)
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.00215, 1)
+on (sunday) between (00:00..23:59) use (0.00215, 1)
diff --git a/kppp/Rules/Netherlands/Freeler_Voordelig.rst b/kppp/Rules/Netherlands/Freeler_Voordelig.rst
new file mode 100644
index 00000000..93c58a9b
--- /dev/null
+++ b/kppp/Rules/Netherlands/Freeler_Voordelig.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp rules voor Freeler Voordelig per seconde
+# http://www.freeler.nl/service/tarieven.html
+# bijgewerkt op 30-11-2002
+# Alleen voor lokale gesprekken
+# Let op! bedragen in euro per seconde!!
+# Kosten:
+# Lokaal: piektarief 0.0196 euro pm = 0.00032667 euro ps 08:00-18:59
+# daltarief 0.0105 euro pm = 0.000175 euro ps 19:00-23:59
+# nacht 0.0070 euro pm = 0.00011667 euro ps 00:00-07:59
+# Zaterdag 0.0070 euro pm = 0.00011667 euro ps 00:00-23:59
+# Zondag 0.0070 euro pm = 0.00011667 euro ps 00:00-23:59
+#
+# Starttarief 0.0290 euro per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Freeler Voordelig
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0290
+minimum_costs=0.0
+
+# piektarief
+default=(0.00032667, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000175, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.00011667, 1)
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.00011667, 1)
+on (sunday) between (00:00..23:59) use (0.00011667, 1)
diff --git a/kppp/Rules/Netherlands/HetNet_Regelmatig_Surfen.rst b/kppp/Rules/Netherlands/HetNet_Regelmatig_Surfen.rst
new file mode 100644
index 00000000..80cff58d
--- /dev/null
+++ b/kppp/Rules/Netherlands/HetNet_Regelmatig_Surfen.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# kppp rules voor HetNet Regelmatig Surfen
+# HetNet Regelmatig Surfen is 20% goekoper dan KPN BelBasis.
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten KPN BelBasis Regio: -20%:
+# Lokaal: standaardtarief 2,80 cpm=0.0466667 cps 08:00-18:59 0.0373334 cps
+# daltarief 1.5 cpm=0.025 cps 19:00-23:59 0.02 cps
+# nacht 1 cpm=0.0166667 cps 00:00-07:59 0.0133334 cps
+# Zaterdag 1 cpm=0.0166667 cps 00:00-23:59 0.0133334 cps
+# Zondag 1 cpm=0.0166667 cps 00:00-23:59 0.0133334 cps
+#
+# Starttarief 4.14 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=HetNet Regelmatig Surfen
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+# standaardtarief
+default=(0.000373334, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.0002, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000133334, 1)
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.000133334, 1)
+on (sunday) between (00:00..23:59) use (0.000133334, 1)
diff --git a/kppp/Rules/Netherlands/InterNLnet.rst b/kppp/Rules/Netherlands/InterNLnet.rst
new file mode 100644
index 00000000..0cf64214
--- /dev/null
+++ b/kppp/Rules/Netherlands/InterNLnet.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# kppp rules voor InterNLnet
+# gegevens van http://www.internl.net/telefoontarieven/
+# laatste wijziging op 27-11-2002
+#
+# Alleen voor lokale gesprekken
+# Let op!! InterNLnet rekent in euro's per uur!!
+#
+# Kosten InterNLnet:
+# Lokaal: standaardtarief 1.25 euro pu = 0.000347222 euro ps 08:00-18:59
+# daltarief 0.60 euro pu = 0.000166667 euro ps 19:00-23:59
+# nacht 0.45 euro pu = 0.000125 euro ps 00:00-07:59
+# Zaterdag 0.45 euro pu = 0.000125 euro ps 00:00-23:59
+# Zondag 0.45 euro pu = 0.000125 euro ps 00:00-23:59
+#
+# Starttarief 0,03 euro per geslaagde verbinding
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=HetNet Regelmatig Surfen
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+flat_init_costs=(0.03, 5)
+
+# standaardtarief
+default=(0.000347222, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000166667, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000125, 1)
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.000125, 1)
+on (sunday) between (00:00..23:59) use (0.000125, 1)
diff --git a/kppp/Rules/Netherlands/Makefile.am b/kppp/Rules/Netherlands/Makefile.am
new file mode 100644
index 00000000..c1f98657
--- /dev/null
+++ b/kppp/Rules/Netherlands/Makefile.am
@@ -0,0 +1,34 @@
+pkg_DATA = 12Move.rst \
+ Cistron \
+ Priority_Telecom_Nationaal.rst \
+ BelBasis_Buiten_Regio.rst \
+ Freeler_Basis.rst \
+ Priority_Telecom_Regionaal.rst \
+ BelBasis_Buiten_Regio_Nummervoordeel.rst \
+ Freeler_Voordelig.rst \
+ Tele2_Extra_Buiten_Regio.rst \
+ BelBasis_Regio.rst \
+ HetNet_Regelmatig_Surfen.rst \
+ Tele2_Extra_Regio.rst \
+ BelBasis_Regio_Nummervoordeel.rst \
+ InterNLnet.rst \
+ Tele2_Preselect_Buiten_Regio.rst \
+ BelBudget_Buiten_Regio.rst \
+ Tele2_Preselect_Regio.rst \
+ BelBudget_Regio.rst \
+ OneTel_Spaarstand_Buiten_Regio.rst \
+ Tele2_Toets_1609_Buiten_Regio.rst \
+ BelPlus_Buiten_Regio.rst \
+ OneTel_Spaarstand_Regio.rst \
+ Tele2_Toets_1609_Regio.rst \
+ BelPlus_Regio.rst \
+ OneTel_Toets_1658_Buiten_Regio.rst \
+ Wannadoo_Budget_Plus \
+ BelPlus_Regio_Nummervoordeel.rst \
+ OneTel_Toets_1658_Regio.rst \
+ Planet_Internet.rst
+
+
+pkgdir = $(kde_datadir)/kppp/Rules/Netherlands
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Netherlands/OneTel_Spaarstand_Buiten_Regio.rst b/kppp/Rules/Netherlands/OneTel_Spaarstand_Buiten_Regio.rst
new file mode 100644
index 00000000..9fdddd98
--- /dev/null
+++ b/kppp/Rules/Netherlands/OneTel_Spaarstand_Buiten_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor OneTel Spaarstand Buiten Regio abonnement
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Tarieven gedownload van http://www.onetel.nl/tarieven_nationaal.php
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# piektarief doordeweeks 3.7 cpm = 0.0616667 cps 08:00-18:59
+# 's-avonds en in weekend 1.6 cpm = 0.0266667 cps
+#
+# Starttarief 25 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=OneTel Spaarstand Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.25
+minimum_costs=0.0
+
+#daltarief
+default=(0.0266667, 1)
+
+#piektarief
+on (monday..friday) between (08:00..18:59) use (0.000616667, 1)
+
diff --git a/kppp/Rules/Netherlands/OneTel_Spaarstand_Regio.rst b/kppp/Rules/Netherlands/OneTel_Spaarstand_Regio.rst
new file mode 100644
index 00000000..16be4327
--- /dev/null
+++ b/kppp/Rules/Netherlands/OneTel_Spaarstand_Regio.rst
@@ -0,0 +1,42 @@
+################################################################
+# kppp rules voor Onetel Spaarstand Regio abonnement
+# Alleen voor lokale gesprekken
+# Tarieven gedownload van http://www.onetel.nl/tarieven_nationaal.php
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# Lokaal: piektarief 2.4 cpm = 0.04 cps 08:00-18:59
+# daltarief 1.2 cpm = 0.02 cps 19:00-23:59
+# nacht 0.9 cpm = 0.015 cps 00:00-07:59
+# Zaterdag 0.9 cpm = 0.015 cps 00:00-23:59
+# Zondag 0.9 cpm = 0.015 cps 00:00-23:59
+#
+# Starttarief 25 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Onetel Spaarstand Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.25
+minimum_costs=0.0
+
+# piektarief
+default=(0.0004, 1)
+
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.0002, 1)
+
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.00015, 1)
+
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.00015, 1)
+on (sunday) between (00:00..23:59) use (0.00015, 1)
diff --git a/kppp/Rules/Netherlands/OneTel_Toets_1658_Buiten_Regio.rst b/kppp/Rules/Netherlands/OneTel_Toets_1658_Buiten_Regio.rst
new file mode 100644
index 00000000..d432dbb5
--- /dev/null
+++ b/kppp/Rules/Netherlands/OneTel_Toets_1658_Buiten_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor OneTel Spaarstand Buiten Regio abonnement
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Tarieven gedownload van http://www.onetel.nl/tarieven_nationaal.php
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# piektarief doordeweeks 4.1 cpm = 0.0683333 cps 08:00-18:59
+# 's-avonds en in weekend 1.8 cpm = 0.03 cps
+#
+# Starttarief 25 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=OneTel Toets 1658 Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.25
+minimum_costs=0.0
+
+#daltarief
+default=(0.0003, 1)
+
+#piektarief
+on (monday..friday) between (08:00..18:59) use (0.000683333, 1)
+
diff --git a/kppp/Rules/Netherlands/OneTel_Toets_1658_Regio.rst b/kppp/Rules/Netherlands/OneTel_Toets_1658_Regio.rst
new file mode 100644
index 00000000..e0e1e3ab
--- /dev/null
+++ b/kppp/Rules/Netherlands/OneTel_Toets_1658_Regio.rst
@@ -0,0 +1,42 @@
+################################################################
+# kppp rules voor Onetel Spaarstand Regio abonnement
+# Alleen voor lokale gesprekken
+#
+# Tarieven gedownload van http://www.onetel.nl/tarieven_nationaal.php
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# Lokaal: piektarief 4.1 cpm = 0.0683333 cps 08:00-18:59
+# daltarief 1.8 cpm = 0.03 cps 19:00-23:59
+# nacht 1.8 cpm = 0.03 cps 00:00-07:59
+# Zaterdag 1.8 cpm = 0.03 cps 00:00-23:59
+# Zondag 1.8 cpm = 0.03 cps 00:00-23:59
+#
+# Starttarief 25 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#################################################################
+
+name=Onetel Spaarstand Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.25
+minimum_costs=0.0
+
+#piektarief
+default=(0.000683333, 1)
+
+#daltarief
+on (monday..friday) between (19:00..23:59) use (0.0003, 1)
+
+#nachttarief
+on (monday..friday) between (00:00..07:59) use (0.0003, 1)
+
+#zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.0003, 1)
+on (sunday) between (00:00..23:59) use (0.0003, 1)
diff --git a/kppp/Rules/Netherlands/Planet_Internet.rst b/kppp/Rules/Netherlands/Planet_Internet.rst
new file mode 100644
index 00000000..f8b2cc4f
--- /dev/null
+++ b/kppp/Rules/Netherlands/Planet_Internet.rst
@@ -0,0 +1,41 @@
+################################################################
+#
+# kppp rules voor Planet Internet per seconde
+# http://web.planet.nl/klantenservice/helpdesk/inbelpunten.html#3
+# tarieven vanaf 1 januari 2003
+# laatste bijwerking op 28-11-2003
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten:
+# Lokaal: standaardtarief 2,74 cpm = 0.0456667 cps 08:00-18:59
+# daltarief 1.47 cpm = 0.0245 cps 19:00-23:59
+# nacht 0.98 cpm = 0.0163333 cps 00:00-07:59
+# Zaterdag 0.98 cpm = 0.0163333 cps 00:00-23:59
+# Zondag 0.98 cpm = 0.0163333 cps 00:00-23:59
+#
+# Starttarief 4.05 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0405
+minimum_costs=0.0
+
+# standaardtarief
+default=(0.000456667, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000245, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000163333, 1)
+on (saturday) between (00:00..23:59) use (0.000163333, 1)
+on (sunday) between (00:00..23:59) use (0.000163333, 1)
diff --git a/kppp/Rules/Netherlands/Priority_Telecom_Nationaal.rst b/kppp/Rules/Netherlands/Priority_Telecom_Nationaal.rst
new file mode 100644
index 00000000..e88c5d41
--- /dev/null
+++ b/kppp/Rules/Netherlands/Priority_Telecom_Nationaal.rst
@@ -0,0 +1,37 @@
+# Kppp rules voor Priority Telecom nationaal, naar niet Priority-nummers
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 20-11-2002
+# Oorspronkelijk script voor Nedpoint gemaakt door:
+# Mike Klinkert (michael@cs.vu.nl)
+#
+# Piektarief: 3 cpm, 08:00-18:59 (maandag t/m vrijdag)
+# Daltarief: 1.5 cpm, 19:00-23:59 (maandag t/m vrijdag)
+# Nachttarief 1.5 cpm, 00:00-07:59 (maandag t/m vrijdag en weekend)
+# Starttarief: 5 cent
+
+name=Priority Telecom Nationaal
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.05
+minimum_costs=0.0
+
+# Piektarief (0.03 / 60 = 0.0005)
+on (monday..friday) between (8:00..18:59) use (0.0005, 1)
+
+# Daltarief (0,015 / 60 = 0.00025)
+on (monday..friday) between (19:00..23:59) use (0.00025, 1)
+
+
+# Nachttarief (0.015 / 60 = 0.00025)
+default=(0.00015, 1)
+# Kerstmis
+# on (12/25..12/26, 12/31) between () use (0.00025, 1)
+# Nieuwjaar
+# on (01/01) between () use (0.00025, 1)
+# Koninginnedag
+# on (04/30) between () use (0.00025, 1)
+# Pasen
+on (easter) between () use (0.00025, 1)
+# Pinksteren
+on (easter+56) between () use (0.00025, 1)
diff --git a/kppp/Rules/Netherlands/Priority_Telecom_Regionaal.rst b/kppp/Rules/Netherlands/Priority_Telecom_Regionaal.rst
new file mode 100644
index 00000000..42692742
--- /dev/null
+++ b/kppp/Rules/Netherlands/Priority_Telecom_Regionaal.rst
@@ -0,0 +1,32 @@
+# Kppp rules voor Priority Telecom Regionaal, naar niet Priority-nummers
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 20-11-2002
+# Oorspronkelijk script voor Nedpoint gemaakt door:
+# Mike Klinkert (michael@cs.vu.nl)
+#
+# Piektarief: 5,40 cpm, 08:00-18:00 (maandag t/m vrijdag)
+# Daltarief: 2,70 cpm, 18:00-08:00 (maandag t/m vrijdag en in het weekend)
+# Starttarief: 10 cent
+
+name=Priority Telecom Regionaal
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.035
+minimum_costs=0.0
+
+# Piektarief (0.054 / 60)
+on (monday..friday) between (8:00..18:00) use (0.0009, 1)
+
+# Daltarief (0.027 / 60)
+default=(0.00045, 1)
+# Kerstmis
+on (12/25..12/26, 12/31) between () use (0.00045, 1)
+# Nieuwjaar
+on (01/01) between () use (0.00045, 1)
+# Koninginnedag
+on (04/30) between () use (0.00045, 1)
+# Pasen
+on (easter) between () use (0.00045,1)
+# Pinksteren
+on (easter+56) between () use (0.00045,1)
diff --git a/kppp/Rules/Netherlands/Tele2_Extra_Buiten_Regio.rst b/kppp/Rules/Netherlands/Tele2_Extra_Buiten_Regio.rst
new file mode 100644
index 00000000..4e118d92
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Extra_Buiten_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor Tele2 Extra Buiten Regio abonnement
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Tarieven gedownload van http://www.tele2.nl/chap02/c020201.html
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# piektarief doordeweeks 3.4 cpm = 0.0566667 cps 08:00-18:59
+# 's-avonds en in weekend 1.5 cpm = 0.025 cps
+#
+# Starttarief 4.5 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Tele2 Extra Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.045
+minimum_costs=0.0
+
+#daltarief
+default=(0.00025, 1)
+
+#piektarief
+on (monday..friday) between (08:00..18:59) use (0.000566667, 1)
+
diff --git a/kppp/Rules/Netherlands/Tele2_Extra_Regio.rst b/kppp/Rules/Netherlands/Tele2_Extra_Regio.rst
new file mode 100644
index 00000000..87f5229d
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Extra_Regio.rst
@@ -0,0 +1,42 @@
+################################################################
+# kppp rules voor Tele2 Extra Regio abonnement
+# Alleen voor lokale gesprekken
+# Tarieven gedownload van http://www.tele2.nl/chap02/c020201.html
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# Lokaal: piektarief 2.25 cpm = 0.0375 cps 08:00-18:59
+# daltarief 1.17 cpm = 0.0195 cps 19:00-23:59
+# nacht 0.81 cpm = 0.0135 cps 00:00-07:59
+# Zaterdag 0.81 cpm = 0.0135 cps 00:00-23:59
+# Zondag 0.81 cpm = 0.0135 cps 00:00-23:59
+#
+# Starttarief 3.51 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Tele2 Extra 1602 Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0351
+minimum_costs=0.0
+
+# piektarief
+default=(0.000375, 1)
+
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000195, 1)
+
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000135, 1)
+
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.000135, 1)
+on (sunday) between (00:00..23:59) use (0.000135, 1)
diff --git a/kppp/Rules/Netherlands/Tele2_Preselect_Buiten_Regio.rst b/kppp/Rules/Netherlands/Tele2_Preselect_Buiten_Regio.rst
new file mode 100644
index 00000000..bd558494
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Preselect_Buiten_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor Tele2 Preselect Buiten Regio abonnement
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Tarieven gedownload van http://www.tele2.nl/chap02/c020201.html
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# piektarief doordeweeks 3.8 cpm = 0.0633333 cps 08:00-18:59
+# 's-avonds en in weekend 1.7 cpm = 0.0283333 cps
+#
+# Starttarief 4.9 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Tele2 Preselect Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.049
+minimum_costs=0.0
+
+#daltarief
+default=(0.000283333, 1)
+
+#piektarief
+on (monday..friday) between (08:00..18:59) use (0.000633333, 1)
+
diff --git a/kppp/Rules/Netherlands/Tele2_Preselect_Regio.rst b/kppp/Rules/Netherlands/Tele2_Preselect_Regio.rst
new file mode 100644
index 00000000..a45d4376
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Preselect_Regio.rst
@@ -0,0 +1,42 @@
+################################################################
+# kppp rules voor Preselect Regio abonnement
+# Alleen voor lokale gesprekken
+# Tarieven gedownload van http://www.tele2.nl/chap02/c020201.html
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# Lokaal: piektarief 2.5 cpm = 0.0416667 cps 08:00-18:59
+# daltarief 1.3 cpm = 0.0216667 cps 19:00-23:59
+# nacht 0.9 cpm = 0.015 cps 00:00-07:59
+# Zaterdag 0.9 cpm = 0.015 cps 00:00-23:59
+# Zondag 0.9 cpm = 0.015 cps 00:00-23:59
+#
+# Starttarief 3.9 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Tele2 Preselect 1602 Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.039
+minimum_costs=0.0
+
+# piektarief
+default=(0.000416667, 1)
+
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000216667, 1)
+
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.00015, 1)
+
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.00015, 1)
+on (sunday) between (00:00..23:59) use (0.00015, 1)
diff --git a/kppp/Rules/Netherlands/Tele2_Toets_1609_Buiten_Regio.rst b/kppp/Rules/Netherlands/Tele2_Toets_1609_Buiten_Regio.rst
new file mode 100644
index 00000000..921c3013
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Toets_1609_Buiten_Regio.rst
@@ -0,0 +1,37 @@
+################################################################
+#
+# kppp rules voor Tele2 Toets 1602 Buiten Regio abonnement
+#
+# Alleen voor buiten de regio gesprekken
+#
+# Tarieven gedownload van http://www.tele2.nl/chap02/c020201.html
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# piektarief doordeweeks 3.8 cpm = 0.0633333 cps 08:00-18:59
+# 's-avonds en in weekend 1.7 cpm = 0.0283333 cps
+#
+# Starttarief 4.9 ct per gesprek
+#
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Tele2 Toets 1602 Buiten Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.049
+minimum_costs=0.0
+
+#daltarief
+default=(0.000283333, 1)
+
+#piektarief
+on (monday..friday) between (08:00..18:59) use (0.000633333, 1)
+
diff --git a/kppp/Rules/Netherlands/Tele2_Toets_1609_Regio.rst b/kppp/Rules/Netherlands/Tele2_Toets_1609_Regio.rst
new file mode 100644
index 00000000..91b3d297
--- /dev/null
+++ b/kppp/Rules/Netherlands/Tele2_Toets_1609_Regio.rst
@@ -0,0 +1,42 @@
+################################################################
+# kppp rules voor Onetel Spaarstand Regio abonnement
+# Alleen voor lokale gesprekken
+# Tarieven gedownload van http://www.onetel.nl/tarieven_nationaal.php
+# Laatste wijziging op 24-11-2002
+#
+# Kosten:
+# Lokaal: piektarief 2.5 cpm = 0.0416667 cps 08:00-18:59
+# daltarief 1.3 cpm = 0.0216667 cps 19:00-23:59
+# nacht 0.9 cpm = 0.015 cps 00:00-07:59
+# Zaterdag 0.9 cpm = 0.015 cps 00:00-23:59
+# Zondag 0.9 cpm = 0.015 cps 00:00-23:59
+#
+# Starttarief 3.9 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Onetel Spaarstand Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.039
+minimum_costs=0.0
+
+# piektarief
+default=(0.000416667, 1)
+
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.000216667, 1)
+
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.00015, 1)
+
+# zaterdag en zondag
+on (saturday) between (00:00..23:59) use (0.00015, 1)
+on (sunday) between (00:00..23:59) use (0.00015, 1)
diff --git a/kppp/Rules/Netherlands/Wannadoo_Budget_Plus b/kppp/Rules/Netherlands/Wannadoo_Budget_Plus
new file mode 100644
index 00000000..23282f03
--- /dev/null
+++ b/kppp/Rules/Netherlands/Wannadoo_Budget_Plus
@@ -0,0 +1,41 @@
+################################################################
+#
+# kppp rules voor Wannadoor Budget Plus per seconde
+# Wannadoor Budget Plus is 25% goedkoper dan KBN BelBasis Regio
+# laatste bijwerking op 28-11-2002
+#
+# Alleen voor lokale gesprekken
+#
+# Kosten KPN BelBasis Regio: -25%
+# Lokaal: standaardtarief 2,80 cpm=0.0466667 cps 08:00-18:59 0.0349992
+# daltarief 1.5 cpm=0.025 cps 19:00-23:59 0.01875
+# nacht 1 cpm=0.0166667 cps 00:00-07:59 0.0125
+# Zaterdag 1 cpm=0.0166667 cps 00:00-23:59 0.0125
+# Zondag 1 cpm=0.0166667 cps 00:00-23:59 0.0125
+#
+# Starttarief 4.14 ct per gesprek
+#
+# Gemaakt door: Rinse de Vries <rinse@kde.nl>
+# Datum: 21-11-2002
+#
+# Oorspronkelijk script gemaakt door: Michel Weijts <mweijts@yahoo.com>
+# Created on 1 October 2000
+#
+################################################################
+
+name=Nederlands BelBasis Regio
+currency_symbol=euro
+currency_position=left
+currency_digits=2
+per_connection=0.0414
+minimum_costs=0.0
+
+# standaardtarief:
+default=(0.000349992, 1)
+# daltarief
+on (monday..friday) between (19:00..23:59) use (0.0001875, 1)
+# nachttarief
+on (monday..friday) between (00:00..07:59) use (0.000125, 1)
+# weekendtarief
+on (saturday) between (00:00..23:59) use (0.000125, 1)
+on (sunday) between (00:00..23:59) use (0.000125, 1)
diff --git a/kppp/Rules/Norway/Local_Area.rst b/kppp/Rules/Norway/Local_Area.rst
new file mode 100644
index 00000000..f45e0be8
--- /dev/null
+++ b/kppp/Rules/Norway/Local_Area.rst
@@ -0,0 +1,16 @@
+################################################################
+#
+# Ruleset for Telenor Norway
+# Local Area
+# Created 97/09/20 by Arne Coucheron <arneco@online.no>
+# Updated 99/05/23 by Arne Coucheron <arneco@online.no>
+#
+################################################################
+name=Norway_Local_Area
+currency_symbol=Kr
+currency_position=left
+currency_digits=2
+per_connection=0.45
+minimum_costs=0.0
+default=(0.14,60)
+on (monday..friday) between (8:00..17:00) use (0.22,60)
diff --git a/kppp/Rules/Norway/Long_Distance.rst b/kppp/Rules/Norway/Long_Distance.rst
new file mode 100644
index 00000000..f2c0f529
--- /dev/null
+++ b/kppp/Rules/Norway/Long_Distance.rst
@@ -0,0 +1,16 @@
+################################################################
+#
+# Ruleset for Telenor Norway
+# Long Distance
+# Created 97/09/20 by Arne Coucheron <arneco@online.no>
+# Updated 99/05/23 by Arne Coucheron <arneco@online.no>
+#
+################################################################
+name=Norway_Long_Distance
+currency_symbol=Kr
+currency_position=left
+currency_digits=2
+per_connection=0.45
+minimum_costs=0.0
+default=(0.35,60)
+on (monday..friday) between (8:00..17:00) use (0.40,60)
diff --git a/kppp/Rules/Norway/Makefile.am b/kppp/Rules/Norway/Makefile.am
new file mode 100644
index 00000000..95c9127e
--- /dev/null
+++ b/kppp/Rules/Norway/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = Local_Area.rst \
+ Long_Distance.rst \
+ Netcom.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Norway
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Norway/Netcom.rst b/kppp/Rules/Norway/Netcom.rst
new file mode 100644
index 00000000..a8855b8d
--- /dev/null
+++ b/kppp/Rules/Norway/Netcom.rst
@@ -0,0 +1,15 @@
+################################################################
+#
+# Ruleset for Netcom Internett
+#
+# Created 02/01/23 by Jostein Henriksen <josteihe@online.no>
+#
+################################################################
+name=Norway_Netcom_Internett
+currency_symbol=Kr
+currency_position=left
+currency_digits=2
+per_connection=0.45
+minimum_costs=0.0
+default=(0.10,60)
+on (monday..sunday) between (24:00..06:00) use (0,5,60) \ No newline at end of file
diff --git a/kppp/Rules/Poland/Internetia.rst b/kppp/Rules/Poland/Internetia.rst
new file mode 100644
index 00000000..1b5357dd
--- /dev/null
+++ b/kppp/Rules/Poland/Internetia.rst
@@ -0,0 +1,19 @@
+################################################################
+# Regu³y obliczania kosztów po³±czeñ modemowych w Netii dla u¿ytkowników TP S.A.
+# Aktualizacja: 4.03.2002
+# autor: Dariusz Dobosz <D.Dobosz@pik-net.pl>
+# aktualizacje: http:/www.dobex.prv.pl/
+################################################################
+
+name=Internetia
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# Co 360 sekund bêdzie naliczany impuls o warto¶ci 25 grosze.
+# To znaczy miêdzy 22,00 a 8,00
+default=(0.25, 360)
+# poza tym co 180 sekund.
+on (monday..friday) between (8:00..21:59) use (0.25, 180)
diff --git a/kppp/Rules/Poland/Makefile.am b/kppp/Rules/Poland/Makefile.am
new file mode 100644
index 00000000..12d1d82b
--- /dev/null
+++ b/kppp/Rules/Poland/Makefile.am
@@ -0,0 +1,12 @@
+pkg_DATA = Netia_Lokalne_Niebieska.rst \
+ Netia_Lokalne_Zielona.rst \
+ Netia_100km_Niebieska.rst \
+ Netia_100km_Zielona.rst \
+ TPSA.rst \
+ Internetia.rst \
+ TP_Lokalne.rst \
+ TP_100km.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Poland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Poland/Netia_100km_Niebieska.rst b/kppp/Rules/Poland/Netia_100km_Niebieska.rst
new file mode 100644
index 00000000..958b0c2c
--- /dev/null
+++ b/kppp/Rules/Poland/Netia_100km_Niebieska.rst
@@ -0,0 +1,40 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ miêdzymiastowych w sieci Netia,
+# na odleg³o¶æ do 100 km miêdzy centralami wojewódzkimi.
+# Taryfa niebieska (o tañszym abonamencie)
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=Netia_100km_Niebieska
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.59, 60)
+
+# taryfa do 100km
+on (monday..friday) between (8:00..18:00) use (0.59, 60)
+on (monday..friday) between (18:00..22:00) use (0.44, 60)
+on (monday..friday) between (22:00..8:00) use (0.29, 60)
+on (saturday..sunday) between (8:00..22:00) use (0.44, 60)
+on (saturday..sunday) between (22:00..8:00) use (0.29, 60)
+
+# ¦wiêta (taryfa sobotnio-niedzielna)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (8:00..22:00) use (0.44, 60)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (22:00..8:00) use (0.29, 60)
+
+#obja¶nienia dni ¶wi±tecznych
+# 01/01 Nowy Rok
+# easter Niedziela Wielkanocna
+# easter+1 Poniedzia³ek Wielkanocny
+# 05/01 ¦wiêto Pracy
+# 05/03 Dzieñ Konstytucji 3 Maja
+# 08/15 Wniebowziêcie
+# 11/01 Wszystkich ¦wiêtych
+# 11/11 ¦wiêto Niepodleg³o¶ci
+# 12/25 Bo¿e Narodzenie
+# 12/26 Bo¿e Narodzenie
+# niestety na li¶cie brakuje Bo¿ego Cia³a - nie wiem jaka regu³a rz±dzi jego
+# wyborem tego czwartku
+
diff --git a/kppp/Rules/Poland/Netia_100km_Zielona.rst b/kppp/Rules/Poland/Netia_100km_Zielona.rst
new file mode 100644
index 00000000..7ecbc3a9
--- /dev/null
+++ b/kppp/Rules/Poland/Netia_100km_Zielona.rst
@@ -0,0 +1,39 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ miêdzymiastowych w sieci Netia,
+# na odleg³o¶æ do 100 km miêdzy centralami wojewódzkimi.
+# Taryfa zielona
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=Netia_100km_Zielona
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.66, 60)
+
+# taryfa do 100km
+on (monday..friday) between (8:00..18:00) use (0.56, 60)
+on (monday..friday) between (18:00..22:00) use (0.43, 60)
+on (monday..friday) between (22:00..8:00) use (0.28, 60)
+on (saturday..sunday) between (8:00..22:00) use (0.43, 60)
+on (saturday..sunday) between (22:00..8:00) use (0.28, 60)
+
+# ¦wiêta (taryfa sobotnio-niedzielna)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (8:00..22:00) use (0.43, 60)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (22:00..8:00) use (0.28, 60)
+
+#obja¶nienia dni ¶wi±tecznych
+# 01/01 Nowy Rok
+# easter Niedziela Wielkanocna
+# easter+1 Poniedzia³ek Wielkanocny
+# 05/01 ¦wiêto Pracy
+# 05/03 Dzieñ Konstytucji 3 Maja
+# 08/15 Wniebowziêcie
+# 11/01 Wszystkich ¦wiêtych
+# 11/11 ¦wiêto Niepodleg³o¶ci
+# 12/25 Bo¿e Narodzenie
+# 12/26 Bo¿e Narodzenie
+# niestety na li¶cie brakuje Bo¿ego Cia³a - nie wiem jaka regu³a rz±dzi jego
+# wyborem tego czwartku
diff --git a/kppp/Rules/Poland/Netia_Lokalne_Niebieska.rst b/kppp/Rules/Poland/Netia_Lokalne_Niebieska.rst
new file mode 100644
index 00000000..59b6dd5f
--- /dev/null
+++ b/kppp/Rules/Poland/Netia_Lokalne_Niebieska.rst
@@ -0,0 +1,20 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ lokalnych w sieci Netia
+# Taryfa niebieska (abonament 24.40PLN)
+# Napisane na podstawie http://www.netia.pl/dom/taryfa.html
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=Netia_Lokalne_Niebieska
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# Co 360 sekund dodawane bedzie 33 groszy do rachunku.
+# Jest to taryfa nocna
+default=(0.33, 360)
+# niestety w ci±gu dnia czas miêdzy impulsami jest 2x krótszy
+on (monday..sunday) between (8:00..21:59) use (0.33, 180)
+
diff --git a/kppp/Rules/Poland/Netia_Lokalne_Zielona.rst b/kppp/Rules/Poland/Netia_Lokalne_Zielona.rst
new file mode 100644
index 00000000..8e71743f
--- /dev/null
+++ b/kppp/Rules/Poland/Netia_Lokalne_Zielona.rst
@@ -0,0 +1,21 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ lokalnych w sieci Netia
+# Taryfa zielona (abonament 28.06PLN)
+# Napisane na podstawie http://www.netia.pl/dom/taryfa.html
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=Netia_Lokalne_Zielona
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# Co 180 sekund dodawane bêd± 32 grosze do rachunku.
+# Jest to taryfa nocna
+default=(0.32, 360)
+# niestety w ci±gu dnia czas miêdzy impulsami jest 2x krótszy
+on (monday..sunday) between (8:00..21:59) use (0.32, 180)
+
+
diff --git a/kppp/Rules/Poland/TPSA.rst b/kppp/Rules/Poland/TPSA.rst
new file mode 100644
index 00000000..719873bc
--- /dev/null
+++ b/kppp/Rules/Poland/TPSA.rst
@@ -0,0 +1,22 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ lokalnych w sieci TP S.A.
+# Aktualizacja: 4.03.2002
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+# poprawki: Dariusz Dobosz <D.Dobosz@pik-net.pl>
+# aktualizacje: http://www.dobex.prv.pl
+################################################################
+
+name=TPSA
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# Co 360 sekund dodawane bêdzie 31 groszy do rachunku.
+# Jest to tak zwana taryfa nocna.
+default=(0.31, 360)
+# niestety w ci±gu dnia czas miêdzy impulsami jest 2x krótszy
+on (monday..friday) between (8:00..21:59) use (0.31, 180)
+# ¦wiêta
+on (1/01, 1/05, 3/05, 1/11, 11/11, 25/12, 26/12) between () use (0.31, 360)
diff --git a/kppp/Rules/Poland/TP_100km.rst b/kppp/Rules/Poland/TP_100km.rst
new file mode 100644
index 00000000..c7874eff
--- /dev/null
+++ b/kppp/Rules/Poland/TP_100km.rst
@@ -0,0 +1,39 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ miêdzymiastowych w sieci TP S.A.,
+# na odleg³o¶æ do 100 km miêdzy centralami wojewódzkimi.
+# Taryfa obowi±zuj±ca od 1 lipca 1999
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=TP_100km
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+default=(0.59,60)
+
+# taryfa do 100km
+on (monday..friday) between (8:00..18:00) use (0.59, 60)
+on (monday..friday) between (18:00..22:00) use (0.44, 60)
+on (monday..friday) between (22:00..8:00) use (0.29, 60)
+on (saturday..sunday) between (8:00..22:00) use (0.44, 60)
+on (saturday..sunday) between (22:00..8:00) use (0.29, 60)
+
+# ¦wiêta (taryfa sobotnio-niedzielna)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (8:00..22:00) use (0.44, 60)
+on (01/01, easter, easter+1, 05/01, 05/03, 08/15, 11/01, 11/11, 12/25, 12/26) between (22:00..8:00) use (0.29, 60)
+
+#obja¶nienia dni ¶wi±tecznych
+# 01/01 Nowy Rok
+# easter Niedziela Wielkanocna
+# easter+1 Poniedzia³ek Wielkanocny
+# 05/01 ¦wiêto Pracy
+# 05/03 Dzieñ Konstytucji 3 Maja
+# 08/15 Wniebowziêcie
+# 11/01 Wszystkich ¦wiêtych
+# 11/11 ¦wiêto Niepodleg³o¶ci
+# 12/25 Bo¿e Narodzenie
+# 12/26 Bo¿e Narodzenie
+# niestety na li¶cie brakuje Bo¿ego Cia³a - nie wiem jaka regu³a rz±dzi jego
+# wyborem tego czwartku
diff --git a/kppp/Rules/Poland/TP_Lokalne.rst b/kppp/Rules/Poland/TP_Lokalne.rst
new file mode 100644
index 00000000..3192bbdc
--- /dev/null
+++ b/kppp/Rules/Poland/TP_Lokalne.rst
@@ -0,0 +1,19 @@
+################################################################
+# Regu³ki obliczania kosztów po³±czeñ lokalnych w sieci TP S.A.
+# Obowi±zuj± od 1 lipca 1999, podwy¿ka 16 stycznia 2000
+# autor: Jacek Stolarczyk <jacek@mer.chemia.polsl.gliwice.pl>
+################################################################
+
+name=TP_Lokalne
+currency_symbol=PLN
+currency_position=right
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.0
+
+# Co 360 sekund dodawane bedzie 33 groszy do rachunku.
+# Jest to tak zwana taryfa nocna.
+default=(0.33, 360)
+# niestety w ci±gu dnia czas miêdzy impulsami jest 2x krótszy
+on (monday..sunday) between (8:00..21:59) use (0.33, 180)
+
diff --git a/kppp/Rules/Portugal/Makefile.am b/kppp/Rules/Portugal/Makefile.am
new file mode 100644
index 00000000..1118456d
--- /dev/null
+++ b/kppp/Rules/Portugal/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = PT_Local.rst PT_Local_Ilhas.rst PT_Regional.rst \
+ PT_Regional_Ilhas.rst PT_YesNET.rst PT_YesNET_Ilhas.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Portugal
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Portugal/PT_Local.rst b/kppp/Rules/Portugal/PT_Local.rst
new file mode 100644
index 00000000..92c873dc
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_Local.rst
@@ -0,0 +1,54 @@
+################################################################
+#
+# Portugal Telecom Ruleset for local calls
+# origem: Continente
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT Local (Continente)
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (60 s)
+minimum_costs=18.72
+flat_init_costs=(18.72,60)
+
+# Economico: semana das 21:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.03705, 1)
+
+# desconto de 30% após o 10º minuto
+on (saturday..sunday) between () use (0.025935, 1, 600)
+on () between (21:00..09:00) use (0.025935, 1, 600)
+
+# Daylight rates
+on (monday..friday) between (09:00..21:00) use (0.078, 1)
+on (monday..friday) between (09:00..21:00) use (0.0546, 1, 600)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.03705, 1)
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.025935, 1, 600)
+
diff --git a/kppp/Rules/Portugal/PT_Local_Ilhas.rst b/kppp/Rules/Portugal/PT_Local_Ilhas.rst
new file mode 100644
index 00000000..eca624da
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_Local_Ilhas.rst
@@ -0,0 +1,54 @@
+################################################################
+#
+# Portugal Telecom Ruleset for local calls
+# origem: Ilhas
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT Local (Ilhas)
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (60 s)
+minimum_costs=17.92
+flat_init_costs=(17.92, 60)
+
+# Economico: semana das 21:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.1064, 3)
+
+# desconto de 30% após o 10º minuto
+on (saturday..sunday) between () use (0.07448, 3, 600)
+on () between (21:00..09:00) use (0.07448, 3, 600)
+
+# Daylight rates
+on (monday..friday) between (09:00..21:00) use (0.224, 3)
+on (monday..friday) between (09:00..21:00) use (0.1568, 3, 600)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.1064, 3)
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.07448, 3, 600)
+
diff --git a/kppp/Rules/Portugal/PT_Regional.rst b/kppp/Rules/Portugal/PT_Regional.rst
new file mode 100644
index 00000000..3de69213
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_Regional.rst
@@ -0,0 +1,47 @@
+################################################################
+#
+# Portugal Telecom Ruleset for regional calls
+# origem: Continente
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT Regional (Continente)
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (20 s)
+minimum_costs=18.72
+flat_init_costs=(18.72,20)
+
+# Economico: semana das 21:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.156, 1)
+
+# Daylight rates
+on (monday..friday) between (9:00..21:00) use (0.30225, 1)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.156, 1)
diff --git a/kppp/Rules/Portugal/PT_Regional_Ilhas.rst b/kppp/Rules/Portugal/PT_Regional_Ilhas.rst
new file mode 100644
index 00000000..fd4d3a67
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_Regional_Ilhas.rst
@@ -0,0 +1,47 @@
+################################################################
+#
+# Portugal Telecom Ruleset for regional calls
+# origem: Ilhas
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT Regional (Ilhas)
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (20 s)
+minimum_costs=17.92
+flat_init_costs=(17.92,20)
+
+# Economico: semana das 21:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.448, 3)
+
+# Daylight rates
+on (monday..friday) between (9:00..21:00) use (0.868, 3)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.448, 3)
diff --git a/kppp/Rules/Portugal/PT_YesNET.rst b/kppp/Rules/Portugal/PT_YesNET.rst
new file mode 100644
index 00000000..4ddd514a
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_YesNET.rst
@@ -0,0 +1,49 @@
+################################################################
+#
+# Portugal Telecom Ruleset for local calls for
+# Internet numbers 67
+# origem: Continente
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT YesNET 67
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (180 s)
+minimum_costs=10.53
+flat_init_costs=(10.53,180)
+
+# Economico: semana das 18:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.027495, 1)
+
+# Daylight rates
+on (monday..friday) between (9:00..18:00) use (0.05655, 1)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.027495, 1)
+
diff --git a/kppp/Rules/Portugal/PT_YesNET_Ilhas.rst b/kppp/Rules/Portugal/PT_YesNET_Ilhas.rst
new file mode 100644
index 00000000..90e848c4
--- /dev/null
+++ b/kppp/Rules/Portugal/PT_YesNET_Ilhas.rst
@@ -0,0 +1,49 @@
+################################################################
+#
+# Portugal Telecom Ruleset for local calls for
+# Internet numbers 67
+# origem: Ilhas
+#
+# changes 2000-01-02
+# José Carlos Monteiro
+# jcm@oninet.pt
+################################################################
+
+name=PT YesNET 67 (Ilhas)
+
+currency_symbol=$
+currency_position=right
+currency_digits=0
+
+# No Activation
+per_connection=0
+
+# Preço inicial (180 s)
+minimum_costs=10.08
+flat_init_costs=(10.08,180)
+
+# Economico: semana das 18:00 às 09:00; fim-de-semana todo o dia
+#
+default=(0.02632, 1)
+
+# Daylight rates
+on (monday..friday) between (9:00..18:00) use (0.1624, 3)
+
+# Feriados Nacionais (tarifa económica):
+# 1/1 Ano Novo
+# easter-47 Carnaval
+# easter-2 Sexta-feira Santa
+# easter Páscoa
+# 4/25 25 de Abril
+# 5/1 Dia do Trabalhador
+# easter+60 Corpo de Deus
+# 6/10 Dia de Portugal
+# 8/15 Nossa Senhora da Assunção
+# 10/5 Dia da República
+# 11/1 Dia de Todos os Santos
+# 12/1 Dia da Independência
+# 12/8 Imaculada Conceição
+# 12/25 Natal
+#
+on (1/1, easter-47, easter-2, easter, 4/25, 5/1, easter+60, 6/10, 8/15, 10/5, 11/1, 12/1,12/8, 12/25) between () use (0.02632, 1)
+
diff --git a/kppp/Rules/Romania/ClickNet.rst b/kppp/Rules/Romania/ClickNet.rst
new file mode 100644
index 00000000..5238ad22
--- /dev/null
+++ b/kppp/Rules/Romania/ClickNet.rst
@@ -0,0 +1,33 @@
+##################################################################
+# RomTelecom ClickNet Start (Tarife cu TVA 20 martie 2005) #
+# acces internet la 0870 222 222 #
+# actualizat de Sorin Batariuc <sorin@bonbon.net> #
+##################################################################
+
+# Nota:
+# 1) Tarif I (normal): 60 secunde = 0.0125 Euro (fara TVA)
+# 2) Tarif II (redus): 60 secunde = 0.0070 Euro (fara TVA)
+#
+# TVA = 19%
+#
+
+name=ClickNet Start
+currency_symbol=Euro
+currency_position=right
+currency_digits=6
+per_connection=0.00
+minimum_costs=0.00833
+default=(0.014875, 60)
+
+# TARIFUL I
+on (monday..friday) between (08:00..19:59) use (0.014875, 60)
+
+# TARIFUL II
+on (1/1) between () use (0.00833, 60)
+on (1/2) between () use (0.00833, 60)
+on (5/1) between () use (0.00833, 60)
+on (12/1) between () use (0.00833, 60)
+on (12/31) between () use (0.00833, 60)
+on (monday..friday) between (20:00..07:59) use (0.00833, 60)
+on (saturday..sunday) between (00:00..23:59) use (0.00833, 60)
+
diff --git a/kppp/Rules/Romania/Easynet.rst b/kppp/Rules/Romania/Easynet.rst
new file mode 100644
index 00000000..fb5ff88d
--- /dev/null
+++ b/kppp/Rules/Romania/Easynet.rst
@@ -0,0 +1,20 @@
+################################################################
+# Ruleset for Easynet (inclusiv TVA) #
+# written by Sorin Batariuc <sorin@bonbon.net> #
+# actualizat la 20 Martie 2005, pentru convorbiri 89(3)0123 #
+################################################################
+
+name=Easynet
+currency_symbol=Euro
+currency_position=right
+currency_digits=4
+per_connection=0.0
+minimum_costs=0.01428
+default=(0.022372, 60)
+
+# TARIFUL I
+on (monday..sunday) between (08:00..19:59) use (0.022372,60)
+
+# TARIFUL II
+on (monday..sunday) between (20:00..07:59) use (0.01428,60)
+
diff --git a/kppp/Rules/Romania/Makefile.am b/kppp/Rules/Romania/Makefile.am
new file mode 100644
index 00000000..122456e5
--- /dev/null
+++ b/kppp/Rules/Romania/Makefile.am
@@ -0,0 +1,9 @@
+pkg_DATA = Romtelecom_Acces_Special_Internet.rst \
+ Romtelecom_Interjudetean.rst \
+ Romtelecom_Local.rst \
+ RomTelecom_GSM.rst \
+ Zapp-Mobile.rst Easynet.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Romania
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Romania/RomTelecom_GSM.rst b/kppp/Rules/Romania/RomTelecom_GSM.rst
new file mode 100644
index 00000000..7ab791df
--- /dev/null
+++ b/kppp/Rules/Romania/RomTelecom_GSM.rst
@@ -0,0 +1,23 @@
+##################################################################
+# RomTelecom GSM - Tarife cu TVA (25 mai 2004) #
+# Sorin Batariuc <sorin@bonbon.net> #
+# actualizari de la Sorin Batariuc <sorin@bonbon.net> #
+##################################################################
+
+# Nota:
+# 1) 60 secunde = 0.145 Euro (fara TVA)
+# 2) Tariful este valabil de la 1 Iunie 2004
+#
+# TVA = 19%
+#
+#
+# Referinta: "Anexa II Apeluri de la abonat in centrale automate cu Call Colection si Acces Internet.doc"
+#
+
+name=RomTelecom_GSM
+currency_symbol=Euro
+currency_position=right
+currency_digits=6
+per_connection=0.0
+minimum_costs=0.17255
+default=(0.17255, 60)
diff --git a/kppp/Rules/Romania/Romtelecom_Acces_Special_Internet.rst b/kppp/Rules/Romania/Romtelecom_Acces_Special_Internet.rst
new file mode 100644
index 00000000..725729b6
--- /dev/null
+++ b/kppp/Rules/Romania/Romtelecom_Acces_Special_Internet.rst
@@ -0,0 +1,37 @@
+##################################################################
+# RomTelecom A.S.I. - Tarife cu TVA (8 martie 2004) #
+# Claudiu Costin <claudiuc@kde.org> #
+# actualizari de la Sorin Batariuc <sorin@bonbon.net> #
+##################################################################
+
+# Nota:
+# 1) Tarif I (normal): 60 secunde = 0.0095 Euro (fara TVA)
+# 2) Tarif II (redus): 60 secunde = 0.0045 Euro (fara TVA)
+# 3) Tariful este valabil de la 1 Iunie 2004
+#
+# TVA = 19%
+#
+#
+# Referinta: "Anexa II Apeluri de la abonat in centrale automate cu Call Colection si Acces Internet.doc"
+#
+
+name=Romtelecom Acces Special Internet
+currency_symbol=Euro
+currency_position=right
+currency_digits=6
+per_connection=0.00
+minimum_costs=0.005355
+default=(0.011305, 60)
+
+# TARIFUL I
+on (monday..friday) between (08:00..19:59) use (0.011305, 60)
+
+# TARIFUL II
+on (1/1) between () use (0.005355, 60)
+on (1/2) between () use (0.005355, 60)
+on (5/1) between () use (0.005355, 60)
+on (12/1) between () use (0.005355, 60)
+on (12/31) between () use (0.005355, 60)
+on (monday..friday) between (20:00..07:59) use (0.005355, 60)
+on (saturday..sunday) between (00:00..23:59) use (0.005355, 60)
+
diff --git a/kppp/Rules/Romania/Romtelecom_Interjudetean.rst b/kppp/Rules/Romania/Romtelecom_Interjudetean.rst
new file mode 100644
index 00000000..33d06da1
--- /dev/null
+++ b/kppp/Rules/Romania/Romtelecom_Interjudetean.rst
@@ -0,0 +1,31 @@
+##################################################################
+# RomTelecom Interjudetean - Tarife cu TVA (20 martie 2005) #
+# #
+# actualizat de Sorin Batariuc <sorin@bonbon.net> #
+##################################################################
+
+# Nota:
+# 1) Tarif I (normal): 60 secunde = 0.054 Euro (fara TVA)
+# 2) Tarif II (redus): 60 secunde = 0.040 Euro (fara TVA)
+#
+# TVA = 19%
+
+name=Romtelecom Interjudetean
+currency_symbol=Euro
+currency_position=right
+currency_digits=5
+per_connection=0.00
+minimum_costs=0.0476
+default=(0.06426, 60)
+
+# TARIFUL I
+on (monday..friday) between (08:00..19:59) use (0.06426, 60)
+
+# TARIFUL II
+on (1/1) between () use (0.0476, 60)
+on (1/2) between () use (0.0476, 60)
+on (5/1) between () use (0.0476, 60)
+on (12/1) between () use (0.0476, 60)
+on (12/31) between () use (0.0476, 60)
+on (monday..friday) between (20:00..07:59) use (0.0476, 60)
+on (saturday..sunday) between (00:00..23:59) use (0.0476, 60)
diff --git a/kppp/Rules/Romania/Romtelecom_Local.rst b/kppp/Rules/Romania/Romtelecom_Local.rst
new file mode 100644
index 00000000..e391269a
--- /dev/null
+++ b/kppp/Rules/Romania/Romtelecom_Local.rst
@@ -0,0 +1,36 @@
+##################################################################
+# RomTelecom Local - Tarife cu TVA (25 mai 2004) #
+# Claudiu Costin <claudiuc@kde.org> #
+# actualizari de la Sorin Batariuc <sorin@bonbon.net> #
+##################################################################
+
+# Nota:
+# 1) Tarif I (normal): 60 secunde = 0.029 Euro (fara TVA)
+# 2) Tarif II (redus): 60 secunde = 0.010 Euro (fara TVA)
+# 3) Tariful este valabil de la 1 Iunie 2004
+#
+# TVA = 19%
+#
+#
+# Referinta: "Anexa II Apeluri de la abonat in centrale automate cu Call Colection si Acces Internet.doc"
+#
+
+name=Romtelecom Local
+currency_symbol=Euro
+currency_position=right
+currency_digits=6
+per_connection=0.00
+minimum_costs=0.0119
+default=(0.03451, 60)
+
+# TARIFUL I
+on (monday..friday) between (08:00..19:59) use (0.03451, 60)
+
+# TARIFUL II
+on (1/1) between () use (0.0119, 60)
+on (1/2) between () use (0.0119, 60)
+on (5/1) between () use (0.0119, 60)
+on (12/1) between () use (0.0119, 60)
+on (12/31) between () use (0.0119, 60)
+on (monday..friday) between (20:00..07:59) use (0.0119, 60)
+on (saturday..sunday) between (00:00..23:59) use (0.0119, 60)
diff --git a/kppp/Rules/Romania/Zapp-Mobile.rst b/kppp/Rules/Romania/Zapp-Mobile.rst
new file mode 100644
index 00000000..ecc64431
--- /dev/null
+++ b/kppp/Rules/Romania/Zapp-Mobile.rst
@@ -0,0 +1,19 @@
+##################################################################
+# Zapp Online - Tarife cu TVA (8 martie 2004) #
+# Andras Mantia <amantia@freemail.hu> #
+# modificari de Claudiu Costin <claudiuc@kde.org> #
+##################################################################
+
+# Nota:
+# 1) Cost: 0.02$/minut
+# 2) Taxarea se face la secunda
+# TVA = 19%
+#
+
+name=Zapp-Mobile
+currency_symbol=$
+currency_position=right
+currency_digits=4
+per_connection=0.00
+minimum_costs=0.00
+default=(0.0004, 1)
diff --git a/kppp/Rules/Russia/Makefile.am b/kppp/Rules/Russia/Makefile.am
new file mode 100644
index 00000000..4cb1a149
--- /dev/null
+++ b/kppp/Rules/Russia/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = TEMPLATE.ru mtu-intel_standart.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Russia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Russia/TEMPLATE.ru b/kppp/Rules/Russia/TEMPLATE.ru
new file mode 100644
index 00000000..e50a7890
--- /dev/null
+++ b/kppp/Rules/Russia/TEMPLATE.ru
@@ -0,0 +1,163 @@
+################################################################
+# There is a russian translation of same file named
+# "$prefix/kppp/Rules/TEMPLATE"
+# Code page of this file is KOI8-R
+################################################################
+#
+# Disclaimer/ìÉÃÅÎÚÉÑ
+# üÔÏÔ ÛÁÂÌÏÎ ÏÔ (c) Mario Weilguni <mweilguni@kde.org>
+# ïÎ ÌÉÃÅÎÚÉÒÕÅÔÓÑ ÎÁ ÔÅÈ ÖÅ ÕÓÌÏ×ÉÑÈ, ÞÔÏ É ÐÁËÅÔ kppp,
+# ÞÁÓÔØÀ ËÏÔÏÒÏÇÏ ÏÎ Ñ×ÌÑÅÔÓÑ.
+#
+################################################################
+#
+# ðòéíåþáîéå ðåòå÷ïäþéëá:
+# üÔÏÔ ÆÁÊÌ Ñ×ÌÑÅÔÓÑ ÐÅÒÅ×ÏÄÏÍ ÆÁÊÌÁ "TEMPLATE", ÒÁÓÐÏÌÏÖÅÎÎÏÇÏ
+# ×ÅÒÏÑÔÎÏ × ÄÉÒÅËÔÏÒÉÉ "/usr/share/apps/kppp/Rules"
+#
+# üÔÏ ÐÒÉÍÅÒ ÕÓÔÁÎÏ×ÌÅÎÎÙÈ ÐÒÁ×ÉÌ ÄÌÑ kppp. ÷Ù ÍÏÖÅÔÅ ÉÓÐÏÌØÚÏ×ÁÔØ
+# ÅÇÏ ËÁË ÛÁÂÌÏÎ, ËÏÇÄÁ ÷Ù ÂÕÄÅÔÅ ÓÏÚÄÁ×ÁÔØ Ó×ÏÊ ÎÁÂÏÒ ÐÒÁ×ÉÌ. åÓÌÉ ÷Ù
+# ÂÕÄÅÔÅ ÜÔÏ ÄÅÌÁÔØ, ÕÄÁÌÉÔÅ ×ÓÅ ËÏÍÍÅÎÔÁÒÉÉ É ÄÏÂÁ×ØÔÅ Ó×ÏÉ. üÔÏ ÚÎÁÞÉÔÅÌØÎÏ
+# ÕÐÒÏÓÔÉÔ ÄÒÕÇÉÍ ÐÏÌØÚÏ×ÁÔÅÌÑÍ ÐÒÏ×ÅÒËÕ ÜÔÏÇÏ ÎÁÂÏÒÁ ÐÒÁ×ÉÌ.
+#
+# ðÏÖÁÌÕÊÓÔÁ, ÐÏÄÐÉÛÉÔÅ ÜÔÏÔ ÔÁÒÉÆÎÙÊ ÐÌÁÎ Ó×ÏÉÍ ÉÍÅÎÅÍ Ó e-mail ÁÄÒÅÓÏÍ
+# ÞÔÏÂÙ Ñ ÍÏÇ Ó×ÑÚÁÔØÓÑ Ó ÷ÁÍÉ × ÓÌÕÞÁÅ ÎÅÏÂÈÏÄÉÍÏÓÔÉ.
+#
+# ðòéíåþáîéå: ðÒÁ×ÉÌÁ × ÜÔÏÍ ÛÁÂÌÏÎÅ ÎÅ ÉÍÅÀÔ ÏÓÏÂÏÇÏ ÓÍÙÓÌÁ É
+# ÐÒÉ×ÅÄÅÎÙ ÔÏÌØËÏ × ÄÅÍÏÎÓÔÒÁÃÉÏÎÎÙÈ ÃÅÌÑÈ
+#
+# ðòéíåþáîéå ðï éíåîáí æáêìï÷:
+# ëÏÇÄÁ ×Ù ÓÏÚÄÁÅÔÅ Ó×ÏÊ ÎÁÂÏÒ ÐÒÁ×ÉÌ, ÉÓÐÏÌØÚÕÊÔÅ "_" × ÉÍÅÎÉ ÆÁÊÌÁ
+# ×ÍÅÓÔÏ ÐÒÏÂÅÌÏ× É ÉÓÐÏÌØÚÕÊÔÅ ÒÁÓÛÉÒÅÎÉÅ ".rst"
+# Ô.Å. "Austria city calls"
+# --> ÆÁÊÌ ÄÏÌÖÅÎ ÂÙÔØ ÓÏÈÒÁÎÅÎ ËÁË "Austria_city_calls.rst"
+#
+# âÌÁÇÏÄÁÒÀ, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+#
+# ðÅÒÅ×ÏÄ: (c) áÌÅËÓÁÎÄÒ á. ðÕÞËÏ×, 03.08.2001
+# Translation: (c) Alex A. Puchkov, 03.08.2001
+# ôÅËÕÝÉÊ e-mail: mailex@nm.ru / mailex@mail.ru
+#
+# ðòéíåþáîéå ðåòå÷ïäþéëá: åÓÌÉ ÷Ù ÏÂÎÁÒÕÖÉÌÉ ÏÛÉÂËÉ ÉÌÉ ÎÅÔÏÞÎÏÓÔÉ,
+# ÉÌÉ ÖÅ ÈÏÔÉÔÅ ×ÎÅÓÔÉ Ó×ÏÉ ÐÏÖÅÌÁÎÉÑ - ÐÉÛÉÔÅ ÍÎÅ ÐÏ
+# ÕËÁÚÁÎÎÏÍÕ ×ÙÛÅ e-mail ÁÄÒÅÓÕ.
+################################################################
+
+
+################################################################
+#
+# îáú÷áîéå ôáòéæá. ïÎÏ ÎÕÖÎÏ ÄÌÑ ÓÞÅÔÁ (accounting purposes).
+#
+################################################################
+name=default
+
+################################################################
+# õÓÔÁÎÏ×ËÉ ×ÁÌÀÔÙ
+################################################################
+
+# úÁÄÁÅÍ ATS (á×ÓÔÒÉÊÓËÉÊ ÛÉÌÌÉÎÇ) ÄÌÑ ÉÓÐÏÌØÚÏ×ÁÎÉÑ ÅÇÏ ËÁË ÓÉÍ×ÏÌ
+# ×ÁÌÀÔÙ (ÎÅ ÏÂÑÚÁÔÅÌØÎÏ ÎÕÖÅÎ, ÐÏ ÕÍÏÌÞÁÎÉÀ = "$")
+# currency_symbol=ATS
+currency_symbol=$
+
+# úÁÄÁÅÍ ÐÏÚÉÃÉÀ ÓÉÍ×ÏÌÁ ×ÁÌÀÔÙ.
+# (ÎÅ ÏÂÑÚÁÔÅÌØÎÏ ÎÕÖÎÏ, ÐÏ ÕÍÏÌÞÁÎÉÀ ÜÔÏ "right" /ÓÐÒÁ×Á/)
+currency_position=right
+
+# úÁÄÁÅÍ ÞÉÓÌÏ ÚÎÁÞÉÍÙÈ ÃÉÆÒ.
+# (ÎÅ ÏÂÑÚÁÔÅÌØÎÏ ÎÕÖÎÏ, ÐÏ ÕÍÏÌÞÁÎÉÀ ÜÔÏ "2")
+currency_digits=2
+
+
+
+################################################################
+# õÓÔÁÎÏ×ËÉ ÓÏÅÄÉÎÅÎÉÑ
+################################################################
+
+# ðòéíåþáîéå: ÐÒÁ×ÉÌÁ ÐÒÉÍÅÎÑÀÔÓÑ ÉÚ ÎÁÞÁÌÁ × ËÏÎÅà - ÜÔÏ ÚÎÁÞÉÔ,
+# ÞÔÏ ÔÏÌØËÏ ðïóìåäîåå ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÅÅ ÐÒÁ×ÉÌÏ ÉÓÐÏÌØÚÕÅÔÓÑ
+# ÄÌÑ ×ÙÞÉÓÌÅÎÉÑ ÚÁÔÒÁÔ.
+
+# üÔÏ ÏÐÌÁÞÉ×ÁÅÔÓÑ ËÁÖÄÙÊ ÒÁÚ, ËÏÇÄÁ ÷Ù ÓÏÅÄÉÎÑÅÔÅÓØ Ó ÐÒÏ×ÁÊÄÅÒÏÍ.
+# åÓÌÉ ÷Ù ÎÅ ÐÌÁÔÉÔÅ ÐÒÉ ÓÏÅÄÉÎÅÎÉÉ, ÉÓÐÏÌØÚÕÊÔÅ "0" ÚÄÅÓØ ÉÌÉ
+# ÚÁËÏÍÍÅÎÔÉÒÕÊÔÅ ÜÔÕ ÓÔÒÏËÕ.
+per_connection=0.0
+
+
+# íÉÎÉÍÁÌØÎÙÊ ×ÚÎÏÓ ÐÒÉ ÓÏÅÄÉÎÅÎÉÉ. åÓÌÉ ×ÚÎÏÓ ÚÁ ÔÅÌÅÆÏÎÎÙÊ
+# Ú×ÏÎÏË ÍÅÎØÛÅ ÞÅÍ ÜÔÏ ÚÎÁÞÅÎÉÅ, ÔÏ ×ÚÁÍÅÎ ÉÓÐÏÌØÚÕÅÔÓÑ ÜÔÏ ÚÎÁÞÅÎÉÅ (?)
+# ðÒÉÍÅÞÁÎÉÅ ÐÅÒÅ×ÏÄÞÉËÁ: ÓÍ. × ÏÒÉÇÉÎÁÌÅ, ÅÓÌÉ ÎÅÐÏÎÑÔÎÏ.
+minimum_costs=0.0
+
+
+# ÷Ù ÐÌÁÔÉÔÅ .74 ÚÁ ÐÅÒ×ÙÅ 180 ÓÅËÕÎÄ (3 ÍÉÎÕÔÙ), ÐÒÉ ÜÔÏÍ ÂÅÚÒÁÚÌÉÞÎÏ,
+# ÓËÏÌØËÏ ÷Ù ÐÏÄËÌÀÞÅÎÙ - 1 ÓÅËÕÎÄÕ ÉÌÉ 180 ÓÅËÕÎÄ.
+# üÔÏ ÐÒÁ×ÉÌÏ ÐÏÌÕÞÁÅÔ ÐÒÉÏÒÉÔÅÔ × ÔÅÞÅÎÉÉ ÐÅÒ×ÙÈ 180 ÓÅËÕÎÄ
+# ÎÁÄ ÌÀÂÙÍ ÄÒÕÇÉÍ ÐÒÁ×ÉÌÏÍ, × ÏÓÏÂÅÎÎÏÓÔÉ ÎÁÄ ÐÒÁ×ÉÌÏÍ ÐÏ ÕÍÏÌÞÁÎÉÀ.
+# ðÏÓÍÏÔÒÉÔÅ ÎÁ ÆÁÊÌ costgraphs.gif × ÄÉÒÅËÔÏÒÉÉ docs ÄÉÓÔÒÉÂÕÔÉ×Á
+# kppp ÄÌÑ ÇÒÁÆÉÞÅÓËÏÊ ÉÌÌÀÓÔÒÁÃÉÉ ×ÙÛÅ ÎÁÐÉÓÁÎÎÏÇÏ.
+flat_init_costs=(0.74,180)
+
+# üÔÏ ÐÒÁ×ÉÌÏ ÐÏ ÕÍÏÌÞÁÎÉÀ, ËÏÔÏÒÏÅ ÉÓÐÏÌØÚÕÅÔÓÑ ËÏÇÄÁ ÎÅ ÐÒÉÍÅÎÑÀÔÓÑ
+# ÄÒÕÇÉÅ ÐÒÁ×ÉÌÁ. ðÅÒ×ÙÊ ËÏÍÐÏÎÅÎÔ "0.1" ÜÔÏ ÃÅÎÁ ÚÁ ÏÄÎÕ "ÅÄÉÎÉÃÕ"
+# /"unit"/, ÇÄÅ "72" ÜÔÏ ×ÒÅÍÑ × ÓÅËÕÎÄÁÈ.
+# óÌÅÄÏ×ÁÔÅÌØÎÏ, ÓÌÅÄÕÀÝÅÅ ÐÒÁ×ÉÌÏ ÏÚÎÁÞÁÅÔ: "ëÁÖÄÙÅ 72 ÓÅËÕÎÄÙ 0.1
+# ATS ÐÒÉÂÁ×ÌÑÅÔÓÑ Ë ÓÞÅÔÕ"
+default=(0.1, 72)
+
+#
+# âÏÌÅÅ ÓÌÏÖÎÙÅ ÐÒÁ×ÉÌÁ:
+#
+
+# "Ó ÐÏÎÅÄÅÌØÎÉËÁ ÄÏ ×ÏÓËÒÅÓÅÎØÑ Ó 00:00 ÄÏ 23:59 ×ÚÎÏÓ
+# 0.2 ËÁÖÄÙÅ 72 ÓÅËÕÎÄÙ"
+# ðÒÉÍÅÞÁÎÉÅ ÐÅÒÅ×ÏÄÞÉËÁ: × ÏÒÉÇÉÎÁÌÅ Ó 12:00 am ÄÏ 11:59 pm
+on () between () use (0.2, 2)
+
+# ôÏÖÅ ÓÁÍÏÅ, ÞÔÏ É ×ÙÛÅ
+on (monday..sunday) between () use (0.2, 2)
+
+# ôÏÖÅ ÓÁÍÏÅ, ÞÔÏ É ×ÙÛÅ. ÷Ù ÄÏÌÖÎÙ ÉÓÐÏÌØÚÏ×ÁÔØ 24-È ÞÁÓÏ×ÏÅ ×ÒÅÍÑ,
+# ÉÌÉ ×ÙÞÉÓÌÅÎÉÑ ÎÅ ÂÕÄÕÔ ËÏÒÒÅËÔÎÙÍÉ. (ðÒÉÍÅÒ: ÐÉÛÉÔÅ 15:00 ÄÌÑ 3 pm)
+on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# ÐÒÉÍÅÎÉÔÅÌØÎÏ Ë ÐÑÔÎÉÃÅ, ÓÕÂÂÏÔÅ, ×ÏÓËÒÅÓÅÎØÀ É ÐÏÎÅÄÅÌØÎÉËÕ Ó 8:00 ÄÏ 13:00
+on (friday..monday) between (8:00..13:00) use(0.3,72)
+
+# ÷îéíáîéå:
+on(monday..friday) between (21:00..5:00) use (0.4,2)
+# îå ×ËÌÀÞÁÑ ÓÕÂÂÏÔÕ 0:00-5:00, ÐÏÎÅÄÅÌØÎÉË..ÐÑÔÎÉÃÁ, ËÁË ÕËÁÚÁÎÏ.
+
+# ðÒÉÍÅÎÉÔÅÌØÎÏ Ë ÕËÁÚÁÎÎÏÊ ÄÁÔÅ (ËÁÔÏÌÉÞÅÓËÏÍÕ òÏÖÄÅÓÔ×Õ)
+on (12/25) between () use (0.3,72)
+
+# éÎÔÅÒ×ÁÌ ÄÁÔ É ÏÄÉÎ ÄÅÎØ ÎÅÄÅÌÉ
+on (12/25..12/27, 12/31, 07/04, monday) between () use (0.4, 72)
+
+# éÓÐÏÌØÚÕÊÔÅ ÜÔÏ ÄÌÑ ðÁÓÈÉ
+# ðÒÉÍÅÞÁÎÉÅ ÐÅÒÅ×ÏÄÞÉËÁ: äÌÑ òÏÓÓÉÉ, ×ÅÒÏÑÔÎÏ, ÌÕÞÛÅ ÎÅ ÉÓÐÏÌØÚÏ×ÁÔØ ÜÔÏ
+# ÚÎÁÞÅÎÉÅ, ÔÁË ËÁË ÐÒÁ×ÏÓÌÁ×ÎÁÑ ðÁÓÈÁ ÍÏÖÅÔ ÎÅ ÓÏ×ÐÁÄÁÔØ
+# Ó ËÁÔÏÌÉÞÅÓËÏÊ ÐÏ ÄÁÔÅ
+on (easter) between () use (0.3,72)
+
+# ðÁÓÈÁ + 50 ÄÎÅÊ (ôÒÏÉÃÉÎ ÄÅÎØ) [Pfingstmontag/ Pentecost Monday]
+# ðÒÉÍÅÞÁÎÉÅ ÐÅÒÅ×ÏÄÞÉËÁ: ÓÍ. ×ÙÛÅ
+on (easter+50) between () use (0.3,72)
+
+on (thursday) between (20:00..21:52) use (8.2, 1)
+
+
+# ðÒÁ×ÉÌÁ "on()" ÐÒÅÖÄÅ ×ÓÅÇÏ Ó×ÑÚÁÎÙ ÔÏÌØËÏ Ó ÔÅËÕÝÉÍ ×ÒÅÍÅÎÅÍ. ÷Ù ÍÏÖÅÔÅ
+# ÔÁËÖÅ ÓÏÚÄÁÔØ ÐÒÁ×ÉÌÁ, ÚÁ×ÉÓÑÝÉÅ ÏÔ ËÏÌÉÞÅÓÔ×Á ÓÅËÕÎÄ, × ÔÅÞÅÎÉÉ ËÏÔÏÒÙÈ
+# ÷Ù ÂÙÌÉ ÐÏÄËÌÀÞÅÎÙ Ë Ó×ÏÅÍÕ ÐÒÏ×ÁÊÄÅÒÕ, ÚÁÄÁ×ÁÑ ÜÔÏ ×ÒÅÍÑ ËÁË ÔÒÅÔÉÊ ÁÒÇÕÍÅÎÔ
+# Ë "use()".
+# ë ÐÒÉÍÅÒÕ, ÐÕÓÔØ ÂÕÄÅÔ ÎÏÒÍÁÌØÎÙÊ ÔÁÒÉÆ ×ÅÞÅÒÏÍ - 0.20 ÚÁ ÍÉÎÕÔÕ,
+# É ÏÎ ÕÍÅÎØÛÁÅÔÓÑ ÎÁ 20% ÐÏÓÌÅ 1-ÇÏ ÞÁÓÁ ÓÏÅÄÉÎÅÎÉÑ. üÔÏ ÍÏÖÅÔ ÂÙÔØ
+# ÓÍÏÄÅÌÉÒÏ×ÁÎÏ ÔÁË:
+
+on () between (19:30..08:00) use (0.20, 60)
+on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# ðÒÉÍÅÞÁÎÉÅ Ë ÜÔÉÍ ÐÒÁ×ÉÌÁÍ, ÓÐÒÁ×ÅÄÌÉ×ÏÅ É Ë ÄÒÕÇÉÍ ÐÒÁ×ÉÌÁÍ:
+# ðÒÁ×ÉÌÁ ÞÕ×ÓÔ×ÉÔÅÌØÎÙ Ë ÐÏÒÑÄËÕ, × ËÏÔÏÒÏÍ ÏÎÉ ÓÌÅÄÕÀÔ
diff --git a/kppp/Rules/Russia/mtu-intel_standart.rst b/kppp/Rules/Russia/mtu-intel_standart.rst
new file mode 100644
index 00000000..eeba0fd9
--- /dev/null
+++ b/kppp/Rules/Russia/mtu-intel_standart.rst
@@ -0,0 +1,51 @@
+################################################################
+# "MTU-Intel" (Russia, Moscow) rate ruleset
+# Created by "Alex A. Puchkov" <mailex@nm.ru>
+#
+# Code page of this file is KOI8-R
+# Date: 03.08.2001, Version 0.1b
+################################################################
+#
+# Disclaimer/ìÉÃÅÎÚÉÑ
+# üÔÏÔ ÔÁÒÉÆ ÏÓÎÏ×ÁÎ ÎÁ ÛÁÂÌÏÎÅ ÏÔ (c) Mario Weilguni <mweilguni@kde.org>,
+# É Ó×ÏÅÍ ÓÏÂÓÔ×ÅÎÎÏÍ ÐÅÒÅ×ÏÄÅ ÜÔÏÇÏ ÛÁÂÌÏÎÁ, ËÏÔÏÒÙÊ ÍÏÖÎÏ ÎÁÊÔÉ × ÄÉÒÅËÔÏÒÉÉ
+# "/usr/share/apps/kppp/Rules"
+# ïÎ ÌÉÃÅÎÚÉÒÕÅÔÓÑ ÎÁ ÔÅÈ ÖÅ ÕÓÌÏ×ÉÑÈ, ÞÔÏ É ÐÁËÅÔ kppp.
+# ëÏÒÒÅËÔÎÏÓÔØ ÒÁÓÞÅÔÏ× ÎÅ ÇÁÒÁÎÔÉÒÏ×ÁÎÁ.
+#
+# ðòéíåþáîéå: åÓÌÉ ÷Ù ÏÂÎÁÒÕÖÉÌÉ ÏÛÉÂËÉ ÉÌÉ ÎÅÔÏÞÎÏÓÔÉ,
+# ÉÌÉ ÖÅ ÈÏÔÉÔÅ ×ÎÅÓÔÉ Ó×ÏÉ ÐÏÖÅÌÁÎÉÑ - ÐÉÛÉÔÅ ÍÎÅ ÐÏ
+# ÕËÁÚÁÎÎÏÍÕ ×ÙÛÅ e-mail ÁÄÒÅÓÕ.
+#
+################################################################
+
+################################################################
+# îáú÷áîéå ôáòéæá. ïÎÏ ÎÕÖÎÏ ÄÌÑ ÓÞÅÔÁ (accounting purposes).
+################################################################
+name=MTU_INTEL_standart
+
+################################################################
+# õÓÔÁÎÏ×ËÉ ÓÏÅÄÉÎÅÎÉÑ
+################################################################
+
+# ôÅËÕÝÉÊ ÔÁÒÉÆ ÎÁ ËÏÍÍÕÔÉÒÕÅÍÙÊ ÄÏÓÔÕÐ × éÎÔÅÒÎÅÔ ÏÔ ËÏÍÐÁÎÉÉ íôõ-éÎÔÅÌ
+# ðÏ×ÒÅÍÅÎÎÏÊ ÄÏÓÔÕÐ Ç.íÏÓË×Á, ÍÏÄÅÍÎÙÅ ÐÕÌÙ 995-55-55/56, 721-33-11 É ÄÒÕÇÉÅ
+# äÎÅ×ÎÏÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 9:30 ÄÏ 20:00 ÞÁÓÏ×. 0,75 Õ.Å./þÁÓ
+# ÷ÅÞÅÒÎÉÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 20:00 ÄÏ 02:00 ÞÁÓÏ×. 0,90 Õ.Å./þÁÓ
+# îÏÞÎÏÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 02:00 ÄÏ 09:30 ÞÁÓÏ×. 0,35 Õ.Å./þÁÓ
+
+# åÓÌÉ ÷Ù ÒÁÂÏÔÁÅÔÅ ÐÅÒ×ÙÅ 30 ÓÅËÕÎÄ, ÔÏ ÷Ù ÎÅ ÐÌÁÔÉÔÅ ÎÉÞÅÇÏ :-)
+flat_init_costs=(0.0, 30)
+
+# äÎÅ×ÎÏÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 9:30 ÄÏ 20:00 ÞÁÓÏ×. - 0,75 $/þÁÓ
+# üÔÏ ÐÒÁ×ÉÌÏ ÐÏ ÕÍÏÌÞÁÎÉÀ, É × ÄÁÎÎÏÍ ÓÌÕÞÁÅ ÏÎÏ ÏÚÎÁÞÁÅÔ:
+# "ëÁÖÄÙÅ 30 ÓÅËÕÎÄ 0.00625 ÐÒÉÂÁ×ÌÑÅÔÓÑ Ë ÓÞÅÔÕ (ÄÎÅ×ÎÏÊ ÔÁÒÉÆ - 0,75 $ × ÞÁÓ)"
+default=(0.00625, 30)
+
+# ÷ÅÞÅÒÎÉÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 20:00 ÄÏ 02:00 ÞÁÓÏ×. - 0,90 $/þÁÓ
+# "ëÁÖÄÙÊ ÄÅÎØ ÎÅÄÅÌÉ Ó 20:00 ÄÏ 01:59 ×ÚÎÏÓ 0.0075 ËÁÖÄÙÅ 30 ÓÅËÕÎÄ"
+on () between (20:00..01:59) use (0.0075, 30)
+
+# îÏÞÎÏÊ ÔÁÒÉÆ, ÄÅÊÓÔ×ÕÅÔ Ó 02:00 ÄÏ 09:30 ÞÁÓÏ×. - 0,35 $/þÁÓ
+# "ëÁÖÄÙÊ ÄÅÎØ ÎÅÄÅÌÉ Ó 02:00 ÄÏ 09:29 ×ÚÎÏÓ 0.0029166 ËÁÖÄÙÅ 30 ÓÅËÕÎÄ"
+on () between (02:00..09:29) use (0.0029166, 30)
diff --git a/kppp/Rules/Singapore/Makefile.am b/kppp/Rules/Singapore/Makefile.am
new file mode 100644
index 00000000..52597b79
--- /dev/null
+++ b/kppp/Rules/Singapore/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = SingTel_Local.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Singapore
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Singapore/SingTel_Local.rst b/kppp/Rules/Singapore/SingTel_Local.rst
new file mode 100644
index 00000000..a70269a2
--- /dev/null
+++ b/kppp/Rules/Singapore/SingTel_Local.rst
@@ -0,0 +1,59 @@
+################################################################
+# This is the rule set for Singapore, local SingTel charge
+#
+# Boh Cheh Wee <bohcw@singnet.com.sg>
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=SingTel_Local
+
+################################################################
+# currency settings
+################################################################
+
+# defines Sing Dollar to be used as currency
+currency_symbol=S$
+
+# Define the position of the currency symbol.
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+# This is the default rule which is used when no other rule
+# applies.
+default=(0.00721,60)
+
+# applies 8am until 6pm: every 30 seconds 0.721 cents
+# are added to the bill
+on () between (8:00..18:00) use(0.00721,30)
+
+# applies 6pm until 8am: every 60 seconds 0.721 cents
+# are added to the bill
+on () between (18:00..7:59) use(0.00721,60)
+
+# applies weekends: every 60 seconds 0.721 cents
+# are added to the bill
+on (saturday..sunday) between (00:00..23:59) use (0.00721, 60) \ No newline at end of file
diff --git a/kppp/Rules/Slovakia/Internetovy_tarif_019XY.rst b/kppp/Rules/Slovakia/Internetovy_tarif_019XY.rst
new file mode 100644
index 00000000..2daf749c
--- /dev/null
+++ b/kppp/Rules/Slovakia/Internetovy_tarif_019XY.rst
@@ -0,0 +1,28 @@
+################################################################
+# Slovak Telecom rate ruleset for 019XY numbers after 1.7.2001
+#
+# created 01/07/31 by kayle <kayle@szm.sk>
+#
+# DISCLAIMER: Use at your own risk ;)
+################################################################
+
+name=Internetovy_tarif_019XY
+currency_symbol=Sk
+currency_position=right
+currency_digits=2
+per_connection=1.50
+minimum_costs=0.0
+default=(1.50, 240)
+
+# on (monday..friday) between (0:00..7:00) use (1.50, 240)
+on (monday..friday) between (7:00..19:00) use (1.50, 90)
+# on (monday..friday) between (19:00..24:00) use (1.50, 240)
+
+on (saturday..sunday) between () use (1.50, 290)
+
+on (1/1, 1/6, 5/1, 5/8, 7/5, 8/29) between () use (1.50, 290)
+on (9/1, 9/15, 11/1, 12/24..12/26) between () use (1.50, 290)
+
+on (easter) between () use (1.50, 290)
+
+
diff --git a/kppp/Rules/Slovakia/Makefile.am b/kppp/Rules/Slovakia/Makefile.am
new file mode 100644
index 00000000..fd0403b0
--- /dev/null
+++ b/kppp/Rules/Slovakia/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = Internetovy_tarif_019XY.rst \
+ ST_medzimesto.rst \
+ ST_mesto.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Slovakia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Slovakia/ST_medzimesto.rst b/kppp/Rules/Slovakia/ST_medzimesto.rst
new file mode 100644
index 00000000..04d3e629
--- /dev/null
+++ b/kppp/Rules/Slovakia/ST_medzimesto.rst
@@ -0,0 +1,25 @@
+################################################################
+# Slovak Telecom rate ruleset
+#
+# created 98/01/29 by Juraj Bednár <juraj@bednar.sk>
+#
+# no extensive testing...
+# updated 00/03/04 by carcass <carcass@gmx.net>
+# updates 01/07/03 by Juraj Bednar <juraj@bednar.sk>
+################################################################
+
+name=ST_medzimesto
+currency_symbol=Sk
+currency_position=right
+currency_digits=2
+per_connection=1.476
+minimum_costs=0.0
+default=(1.476, 20)
+
+on (monday..friday) between (19:00..7:00) use (1.476, 40)
+on (saturday..sunday) between () use (1.476, 60)
+
+on (1/1, 1/6, 5/1, 5/8, 7/5, 8/29) between () use (1.476, 60)
+on (9/1, 9/15, 11/1, 12/24..12/26) between () use (1.476, 60)
+
+on (easter) between () use (1.476, 60)
diff --git a/kppp/Rules/Slovakia/ST_mesto.rst b/kppp/Rules/Slovakia/ST_mesto.rst
new file mode 100644
index 00000000..b4d8572c
--- /dev/null
+++ b/kppp/Rules/Slovakia/ST_mesto.rst
@@ -0,0 +1,25 @@
+################################################################
+# Slovak Telecom rate ruleset
+#
+# created 98/01/29 by Juraj Bednár <juraj@bednar.sk>
+# updated 01/07/03 by Juraj Bednár <juraj@bednar.sk>
+#
+# no extensive testing...
+# updated 00/03/04 by carcass <carcass@gmx.net>
+################################################################
+
+name=ST_mesto
+currency_symbol=Sk
+currency_position=right
+currency_digits=2
+per_connection=1.476
+minimum_costs=0.0
+default=(1.476, 60)
+
+on (monday..friday) between (19:00..7:00) use (1.476, 120)
+on (saturday..sunday) between () use (1.476, 180)
+
+on (1/1, 1/6, 5/1, 5/8, 7/5, 8/29) between () use (1.476, 180)
+on (9/1, 9/15, 11/1, 12/24..12/26) between () use (1.476, 180)
+
+on (easter) between () use (1.476, 180)
diff --git a/kppp/Rules/Slovenia/Makefile.am b/kppp/Rules/Slovenia/Makefile.am
new file mode 100644
index 00000000..2d257b7c
--- /dev/null
+++ b/kppp/Rules/Slovenia/Makefile.am
@@ -0,0 +1,7 @@
+pkg_DATA = omrezje_0880.rst \
+ omrezje_0889.rst \
+ stacionarno_omrezje.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Slovenia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Slovenia/omrezje_0880.rst b/kppp/Rules/Slovenia/omrezje_0880.rst
new file mode 100644
index 00000000..437f0ba3
--- /dev/null
+++ b/kppp/Rules/Slovenia/omrezje_0880.rst
@@ -0,0 +1,67 @@
+################################################################
+# $Id$
+# $Source$
+#
+# RULES FOR KPPP WITH SLOVENIAN PHONE NETWORK 0880 (ISP PROVIDER).
+#
+# 2002-02-02:
+# Updated by roman.maurer@amis.net,
+# based on data provided by Primoz Hrvatin.
+#
+# 2000-09-25:
+# Prepared by Ales.Kosir@hermes.si,
+# based on data provided by Srdjan Cvjetovic.
+#
+# Calls from stationary phone network to the ISP numbers 0880:
+# A user pays to the phone company the fixed price for connection, and
+# the ISP will charge you per second for the duration of the call.
+# The cost is 2.04 SIT per minute on normal rate,
+# and 1.02 SIT on discounted.
+#
+################################################################
+name=omrezje_0880.rst
+
+################################################################
+# currency settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+currency_symbol=SIT
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# This is charged whenever you connect. The amount is payed to the
+# phone company.
+
+per_connection=6.30
+
+# "on monday until sunday the costs
+# are 2.04 on normal and 1.02 on discounted per each 60 seconds"
+
+on (monday..saturday) between (0:00..6:59) use (1.02, 60)
+on (monday..saturday) between (7:00..18:59) use (2.04, 60)
+on (monday..saturday) between (19:00..23:59) use (1.02, 60)
+on (sunday) between () use (1.02, 60)
+
+# Drzavni prazniki:
+# 1.1., 2.1 - Novo leto
+# 8.2. - Slovenski kulturni praznik
+# Easter - Velika noc
+# Easter Monday - Velikonocni ponedeljek
+# 27.4. - Dan upora proti okupatorju
+# 1.5.,2.5. - Praznik dela
+# - Binkosti
+# 25.6. - Dan drzavnosti
+# 15.8. - Marijino vnebovzetje
+# 31.10. - Dan reformacije
+# 1.11. - Dan spomina na mrtve
+# 25.12. - Bozic
+# 26.12. - Dan samostojnosti
+on (01/01, 01/02, 02/08, easter, easter+1, 04/26, 05/01, 05/02, easter+50, 06/25, 08/15, 10/31, 11/01, 12/25, 12/26) between () use (1.02, 60)
diff --git a/kppp/Rules/Slovenia/omrezje_0889.rst b/kppp/Rules/Slovenia/omrezje_0889.rst
new file mode 100644
index 00000000..e5f8d08f
--- /dev/null
+++ b/kppp/Rules/Slovenia/omrezje_0889.rst
@@ -0,0 +1,58 @@
+################################################################
+# $Id$
+# $Source$
+#
+# RULES FOR KPPP WITH SLOVENIAN PHONE NETWORK 0889 (ISP PROVIDERS).
+#
+# 2002-02-02:
+# Updated by roman.maurer@amis.net,
+# based on data provided by Primoz Hrvatin.
+#
+# 2000-09-25:
+# Prepared by Ales.Kosir@hermes.si,
+# based on data, provided by Srdjan Cvjetovic.
+#
+# Calls from stationary phone network to the ISP numbers 0889:
+#
+################################################################
+name=omrezje-0889.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=SIT
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=2.52
+
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+
+on (monday..saturday) between (0:00..6:59) use (1.26, 60)
+on (monday..saturday) between (7:00..18:59) use (2.52, 60)
+on (monday..saturday) between (19:00..23:59) use (1.26, 60)
+on (sunday) between () use (1.26, 60)
+
+# Drzavni prazniki:
+# 1.1., 2.1 - Novo leto
+# 8.2. - Slovenski kulturni praznik
+# Easter - Velika noc
+# Easter Monday - Velikonocni ponedeljek
+# 27.4. - Dan upora proti okupatorju
+# 1.5.,2.5. - Praznik dela
+# - Binkosti
+# 25.6. - Dan drzavnosti
+# 15.8. - Marijino vnebovzetje
+# 31.10. - Dan reformacije
+# 1.11. - Dan spomina na mrtve
+# 25.12. - Bozic
+# 26.12. - Dan samostojnosti
+on (01/01, 01/02, 02/08, easter, easter+1, 04/26, 05/01, 05/02, easter+50, 06/25, 08/15, 10/31, 11/01, 12/25, 12/26) between () use (1.26, 60)
diff --git a/kppp/Rules/Slovenia/stacionarno_omrezje.rst b/kppp/Rules/Slovenia/stacionarno_omrezje.rst
new file mode 100644
index 00000000..c4eb3305
--- /dev/null
+++ b/kppp/Rules/Slovenia/stacionarno_omrezje.rst
@@ -0,0 +1,53 @@
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+# Prepared by Ales.Kosir@hermes.si,
+# based on data, provided by Srdjan Cvjetovic
+#
+# This accounting information includes all calls from stationary
+# phone network to the numbers starting with 01, 02, 03 (except 031),
+# 04 (except 040 in 041), 05, and 07.
+#
+################################################################
+name=stacionarno_omrezje.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=SIT
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=4.11
+
+on (monday..saturday) between (0:00..6:59) use (4.11, 120)
+on (monday..saturday) between (7:00..18:59) use (4.11, 60)
+on (monday..saturday) between (19:00..23:59) use (4.11, 120)
+on (sunday) between () use (4.11, 120)
+
+# Drzavni prazniki:
+# 1.1., 2.1 - Novo leto
+# 8.2. - Slovenski kulturni praznik
+# Easter - Velika noc
+# Easter Monday - Velikonocni ponedeljek
+# 27.4. - Dan upora proti okupatorju
+# 1.5.,2.5. - Praznik dela
+# - Binkosti
+# 25.6. - Dan drzavnosti
+# 15.8. - Marijino vnebovzetje
+# 31.10. - Dan reformacije
+# 1.11. - Dan spomina na mrtve
+# 25.12. - Bozic
+# 26.12. - Dan samostojnosti
+on (01/01, 01/02, 02/08, easter, easter+1, 04/26, 05/01, 05/02, easter+50, 06/25, 08/15, 10/31, 11/01, 12/25, 12/26) between () use (4.11, 120)
+
+
+
+
diff --git a/kppp/Rules/SouthAfrica/Makefile.am b/kppp/Rules/SouthAfrica/Makefile.am
new file mode 100644
index 00000000..473e703f
--- /dev/null
+++ b/kppp/Rules/SouthAfrica/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = South_Africa_local.rst South_Africa_long_distance.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/South_Africa
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/SouthAfrica/South_Africa_local.rst b/kppp/Rules/SouthAfrica/South_Africa_local.rst
new file mode 100644
index 00000000..7df20914
--- /dev/null
+++ b/kppp/Rules/SouthAfrica/South_Africa_local.rst
@@ -0,0 +1,73 @@
+################################################################
+#
+# This is an accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the 2003/2004 directory and are based on local phone calls
+# being made. All calls are charged per second.
+# Callmore time is implemented in this rule set.
+# Call rates may change and I hold no responsibility for improper
+# use of this rule set.
+#
+# This rule set was last updated: 29 September 2003
+# by Males Tomlinson <vorsicht@webmail.co.za>
+# Distance:________________Local Call (0-50km)
+# Charges: Standard time:__0,37c per/min
+# Callmore time:__0,14c per/min
+# Minimum call charges:____55c
+#
+#
+# - If unsure, contact your Internet service provider to
+# determain your call distance and Telkom SA for up to date
+# charge rates.
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Local_Call_0-50km
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.55
+
+#flat_init_costs=(0)
+#default=(0)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.(R0,0024 every second)
+#
+on (saturday..sunday) between (0:00..23:59) use (0.0024,1)
+on (monday..friday) between (0:00..06:59) use (0.0024,1)
+on (monday..friday) between (19:00..23:59) use (0.0024,1)
+
+# This is used for the so called 'Normal Time' schedule
+# (R0,0062 every second)
+on (monday..friday) between (07:00..18:59) use (0.0062,1)
diff --git a/kppp/Rules/SouthAfrica/South_Africa_long_distance.rst b/kppp/Rules/SouthAfrica/South_Africa_long_distance.rst
new file mode 100644
index 00000000..657708e2
--- /dev/null
+++ b/kppp/Rules/SouthAfrica/South_Africa_long_distance.rst
@@ -0,0 +1,73 @@
+################################################################
+#
+# This is an accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the 2003 directory and are based on long distance phone calls
+# being made. All calls are charged per second.
+# Callmore time is implemented in this rule set.
+# Call rates may change and I hold no responsibility for improper
+# use of this rule set.
+#
+# This rule set was last updated: 26 September 2003
+# by Males Tomlinson <vorsicht@webmail.co.za>
+# Distance:________________Long distance Call +50km
+# Charges: Standard time:__99c per/min
+# Callmore time:__50c per/min
+# Minimum call charges:____99c
+#
+#
+# - If unsure, contact your Internet service provider to
+# determain your call distance and Telkom SA for up to date
+# charge rates.
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_Africa_Long_distance_50km_plus
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.99
+
+#flat_init_costs=(0)
+#default=(0)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.(R0,0084 every second)
+#
+on (saturday..sunday) between (0:00..23:59) use (0.0084,1)
+on (monday..friday) between (0:00..06:59) use (0.0084,1)
+on (monday..friday) between (19:00..23:59) use (0.0084,1)
+
+# This is used for the so called 'Normal Time' schedule
+# (R0,0165 every second)
+on (monday..friday) between (07:00..18:59) use (0.0165,1)
diff --git a/kppp/Rules/SouthAfrika/Makefile.am b/kppp/Rules/SouthAfrika/Makefile.am
new file mode 100644
index 00000000..94c018fa
--- /dev/null
+++ b/kppp/Rules/SouthAfrika/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = South_Afrika.rst South_Afrika_Justin.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/South_Afrika
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/SouthAfrika/South_Afrika.rst b/kppp/Rules/SouthAfrika/South_Afrika.rst
new file mode 100644
index 00000000..cc45aba9
--- /dev/null
+++ b/kppp/Rules/SouthAfrika/South_Afrika.rst
@@ -0,0 +1,328 @@
+################################################################
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Distance_A
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+flat_init_costs=(0.31,180)
+default=(0.31,180)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.
+on (saturday..sunday) between (0:00..23:59) use (0.31,480)
+on (monday..friday) between (0:00..06:59) use (0.31,480)
+on (monday..friday) between (19:00..23:59) use (0.31,480)
+
+# This is used for the so called 'Normal Time' schedule
+on (monday..friday) between (07:00..18:59) use (0.31,180)
+################################################################
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Distance_B
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+flat_init_costs=(0.31,37.6)
+default=(0.31,37.6)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.
+on (saturday..sunday) between (0:00..23:59) use (0.31,75.2)
+on (monday..friday) between (0:00..06:59) use (0.31,75.2)
+on (monday..friday) between (19:00..23:59) use (0.31,75.2)
+
+# This is used for the so called 'Normal Time' schedule
+on (monday..friday) between (07:00..18:59) use (0.31,37.6)
+################################################################
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Distance_C
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+flat_init_costs=(0.31,18.8)
+default=(0.31,18.8)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.
+on (saturday..sunday) between (0:00..23:59) use (0.31,45.2)
+on (monday..friday) between (0:00..06:59) use (0.31,45.2)
+on (monday..friday) between (19:00..23:59) use (0.31,45.2)
+
+# This is used for the so called 'Normal Time' schedule
+on (monday..friday) between (07:00..18:59) use (0.31,18.8)
+################################################################
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Distance_D
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+flat_init_costs=(0.31,13.6)
+default=(0.31,13.6)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.
+on (saturday..sunday) between (0:00..23:59) use (0.31,28.8)
+on (monday..friday) between (0:00..06:59) use (0.31,28.8)
+on (monday..friday) between (19:00..23:59) use (0.31,28.8)
+
+# This is used for the so called 'Normal Time' schedule
+on (monday..friday) between (07:00..18:59) use (0.31,13.6)
diff --git a/kppp/Rules/SouthAfrika/South_Afrika_Justin.rst b/kppp/Rules/SouthAfrika/South_Afrika_Justin.rst
new file mode 100644
index 00000000..a8214bdf
--- /dev/null
+++ b/kppp/Rules/SouthAfrika/South_Afrika_Justin.rst
@@ -0,0 +1,123 @@
+################################################################
+#
+#
+#This was updated for personal use by and for Justin Porteous
+# I only Needed it for less than 50Km's IE a local call
+# Date 12/12/2001
+# Email nozlab@hotmail.com
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=South_African_Distance_A
+
+################################################################
+# currency settings
+################################################################
+
+# Currency symbol R ('Rand') eg, R 1.20 would imply 1 Rand and 20 cents
+currency_symbol=R
+currency_position=left
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.58
+
+flat_init_costs=(0.58,86)
+
+default=(0.24,30)
+
+#
+# more complicated rules:
+#
+
+# This is used for the so called 'CallMore Time' schedule that was
+# introduced by Telkom.
+on (saturday..sunday) between (13:00..7:00) use (0.09,30)
+on (monday..friday) between (19:00..07:59) use (0.09,30)
+#on (monday..friday) between (19:00..23:59) use (0.09,30)
+
+# This is used for the so called 'Normal Time' schedule
+on (monday..friday) between (07:00..18:59) use (0.24,30)
+################################################################
+#
+# This is a accounting set for Telkom South Africa
+# Please note that the costs used are those corresponding to
+# the '97/'98 directory and are based on local phone calls
+# being made. Strictly speaking, the call charges are not 31c
+# but 30,9c per unit. The odd cost is due to the inclusion
+# of VAT. I've rounded it off to keep things simple.
+#
+# The postfix 'A'..'D' is related to the distance maps
+# and will affect the duration of 1 call unit
+#
+# Distance Table
+# Type Distance Std Time CallMore Time
+# ---- -------- -------- -------------
+# A 0-50 km 180 sec 480 sec
+# B >50-100 km 37,6 75,2
+# C >100-200 km 18,8 45,2
+# D >200 km 13,6 28,8
+#
+# South African residents, please note the following:
+# - Consult your local directory to determine which distance type
+# to use
+# - Most ISP providers in S.A. have pop's (point of pressence) in
+# the major cities, so it's very likely type A will be applicable
+# - If unsure, contact a local Telkom office in your area
+#
+# Any problems, please email me
+# Regards
+#
+# Jacques Eloff,
+# email: repstosd@global.co.za
+#
+################################################################
diff --git a/kppp/Rules/Spain/Infovia.rst b/kppp/Rules/Spain/Infovia.rst
new file mode 100644
index 00000000..b9c85c11
--- /dev/null
+++ b/kppp/Rules/Spain/Infovia.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Infovia at Spain
+#
+################################################################
+name=Spain Infovia
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=11.40
+flat_init_costs=(11.40,160)
+
+# Por Defecto reducida
+default=(0.0274, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.0754, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.0754, 1)
+on (saturday) between (8:00..13:59) use (0.0754, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.0274, 1)
+on (1/6) between () use (0.0274, 1)
+on (5/1) between () use (0.0274, 1)
+on (10/12) between () use (0.0274, 1)
+on (11/1) between () use (0.0274, 1)
+on (12/6) between () use (0.0274, 1)
+on (12/8) between () use (0.0274, 1)
+on (12/25) between () use (0.0274, 1)
+on (easter) between () use (0.0274, 1)
+on (easter+50) between () use (0.0274, 1)
diff --git a/kppp/Rules/Spain/Infovia_IVA.rst b/kppp/Rules/Spain/Infovia_IVA.rst
new file mode 100644
index 00000000..b978599f
--- /dev/null
+++ b/kppp/Rules/Spain/Infovia_IVA.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Infovia at Spain
+#
+################################################################
+name=Spain Infovia IVA
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=13.224
+flat_init_costs=(13.224,160)
+
+# Por Defecto reducida
+default=(0.0318, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.0875, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.0875, 1)
+on (saturday) between (8:00..13:59) use (0.0875, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.0318, 1)
+on (1/6) between () use (0.0318, 1)
+on (5/1) between () use (0.0318, 1)
+on (10/12) between () use (0.0318, 1)
+on (11/1) between () use (0.0318, 1)
+on (12/6) between () use (0.0318, 1)
+on (12/8) between () use (0.0318, 1)
+on (12/25) between () use (0.0318, 1)
+on (easter) between () use (0.0318, 1)
+on (easter+50) between () use (0.0318, 1)
diff --git a/kppp/Rules/Spain/Makefile.am b/kppp/Rules/Spain/Makefile.am
new file mode 100644
index 00000000..951487a6
--- /dev/null
+++ b/kppp/Rules/Spain/Makefile.am
@@ -0,0 +1,9 @@
+pkg_DATA = Infovia.rst Telefonica_Interprovincial.rst \
+ Infovia_IVA.rst Telefonica_Interprovincial_IVA.rst \
+ Telefonica_Metropolitana.rst Telefonica_Metropolitana_IVA.rst \
+ Telefonica_Nacional.rst Telefonica_Provincial.rst \
+ Telefonica_Provincial_IVA.rst Telefonica_Local.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Spain
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Spain/Telefonica_Interprovincial.rst b/kppp/Rules/Spain/Telefonica_Interprovincial.rst
new file mode 100644
index 00000000..8f7bcb89
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Interprovincial.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Telefonica at Spain
+#
+################################################################
+name=Spain Telefonica Interprovincial
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=15
+minimum_costs=15
+
+# Por Defecto reducida
+default=(0.174, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.4169, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.6575, 1)
+on (saturday) between (8:00..13:59) use (0.6575, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.174, 1)
+on (1/6) between () use (0.174, 1)
+on (5/1) between () use (0.174, 1)
+on (10/12) between () use (0.174, 1)
+on (11/1) between () use (0.174, 1)
+on (12/6) between () use (0.174, 1)
+on (12/8) between () use (0.174, 1)
+on (12/25) between () use (0.174, 1)
+on (easter) between () use (0.174, 1)
+on (easter+50) between () use (0.174, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Interprovincial_IVA.rst b/kppp/Rules/Spain/Telefonica_Interprovincial_IVA.rst
new file mode 100644
index 00000000..cae45154
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Interprovincial_IVA.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Telefonica at Spain
+#
+################################################################
+name=Spain Telefonica Interprovincial IVA
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=17.4
+minimum_costs=17.4
+
+# Por Defecto reducida
+default=(0.2018, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.4836, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.7627, 1)
+on (saturday) between (8:00..13:59) use (0.7627, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.2018, 1)
+on (1/6) between () use (0.2018, 1)
+on (5/1) between () use (0.2018, 1)
+on (10/12) between () use (0.2018, 1)
+on (11/1) between () use (0.2018, 1)
+on (12/6) between () use (0.2018, 1)
+on (12/8) between () use (0.2018, 1)
+on (12/25) between () use (0.2018, 1)
+on (easter) between () use (0.2018, 1)
+on (easter+50) between () use (0.2018, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Local.rst b/kppp/Rules/Spain/Telefonica_Local.rst
new file mode 100644
index 00000000..b086565d
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Local.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Infovia at Spain
+#
+################################################################
+name=Spain Telefonica Metropolitana
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=11.40
+flat_init_costs=(11.40,160)
+
+# Por Defecto reducida
+default=(0.0274, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.0754, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.0754, 1)
+on (saturday) between (8:00..13:59) use (0.0754, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.0274, 1)
+on (1/6) between () use (0.0274, 1)
+on (5/1) between () use (0.0274, 1)
+on (10/12) between () use (0.0274, 1)
+on (11/1) between () use (0.0274, 1)
+on (12/6) between () use (0.0274, 1)
+on (12/8) between () use (0.0274, 1)
+on (12/25) between () use (0.0274, 1)
+on (easter) between () use (0.0274, 1)
+on (easter+50) between () use (0.0274, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Metropolitana.rst b/kppp/Rules/Spain/Telefonica_Metropolitana.rst
new file mode 100644
index 00000000..b086565d
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Metropolitana.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Infovia at Spain
+#
+################################################################
+name=Spain Telefonica Metropolitana
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=11.40
+flat_init_costs=(11.40,160)
+
+# Por Defecto reducida
+default=(0.0274, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.0754, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.0754, 1)
+on (saturday) between (8:00..13:59) use (0.0754, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.0274, 1)
+on (1/6) between () use (0.0274, 1)
+on (5/1) between () use (0.0274, 1)
+on (10/12) between () use (0.0274, 1)
+on (11/1) between () use (0.0274, 1)
+on (12/6) between () use (0.0274, 1)
+on (12/8) between () use (0.0274, 1)
+on (12/25) between () use (0.0274, 1)
+on (easter) between () use (0.0274, 1)
+on (easter+50) between () use (0.0274, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Metropolitana_IVA.rst b/kppp/Rules/Spain/Telefonica_Metropolitana_IVA.rst
new file mode 100644
index 00000000..e646ca50
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Metropolitana_IVA.rst
@@ -0,0 +1,44 @@
+################################################################
+#
+# Infovia at Spain
+#
+################################################################
+name=Spain Telefonica Metropolitana IVA
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0
+minimum_costs=13.224
+flat_init_costs=(13.224,160)
+
+# Por Defecto reducida
+default=(0.0318, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.0875, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.0875, 1)
+on (saturday) between (8:00..13:59) use (0.0875, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.0318, 1)
+on (1/6) between () use (0.0318, 1)
+on (5/1) between () use (0.0318, 1)
+on (10/12) between () use (0.0318, 1)
+on (11/1) between () use (0.0318, 1)
+on (12/6) between () use (0.0318, 1)
+on (12/8) between () use (0.0318, 1)
+on (12/25) between () use (0.0318, 1)
+on (easter) between () use (0.0318, 1)
+on (easter+50) between () use (0.0318, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Nacional.rst b/kppp/Rules/Spain/Telefonica_Nacional.rst
new file mode 100644
index 00000000..8f7bcb89
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Nacional.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Telefonica at Spain
+#
+################################################################
+name=Spain Telefonica Interprovincial
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=15
+minimum_costs=15
+
+# Por Defecto reducida
+default=(0.174, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.4169, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.6575, 1)
+on (saturday) between (8:00..13:59) use (0.6575, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.174, 1)
+on (1/6) between () use (0.174, 1)
+on (5/1) between () use (0.174, 1)
+on (10/12) between () use (0.174, 1)
+on (11/1) between () use (0.174, 1)
+on (12/6) between () use (0.174, 1)
+on (12/8) between () use (0.174, 1)
+on (12/25) between () use (0.174, 1)
+on (easter) between () use (0.174, 1)
+on (easter+50) between () use (0.174, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Provincial.rst b/kppp/Rules/Spain/Telefonica_Provincial.rst
new file mode 100644
index 00000000..3121674a
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Provincial.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Telefonica at Spain
+#
+################################################################
+name=Spain Telefonica Interprovincial
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=15
+minimum_costs=15
+
+# Por Defecto reducida
+default=(0.1129, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.224, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.2575, 1)
+on (saturday) between (8:00..13:59) use (0.2575, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.1129, 1)
+on (1/6) between () use (0.1129, 1)
+on (5/1) between () use (0.1129, 1)
+on (10/12) between () use (0.1129, 1)
+on (11/1) between () use (0.1129, 1)
+on (12/6) between () use (0.1129, 1)
+on (12/8) between () use (0.1129, 1)
+on (12/25) between () use (0.1129, 1)
+on (easter) between () use (0.1129, 1)
+on (easter+50) between () use (0.1129, 1)
diff --git a/kppp/Rules/Spain/Telefonica_Provincial_IVA.rst b/kppp/Rules/Spain/Telefonica_Provincial_IVA.rst
new file mode 100644
index 00000000..0b8dd7e1
--- /dev/null
+++ b/kppp/Rules/Spain/Telefonica_Provincial_IVA.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Telefonica at Spain
+#
+################################################################
+name=Spain Telefonica Interprovincial IVA
+
+################################################################
+# currency settings
+################################################################
+currency_symbol=PTS
+currency_position=right
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=17.4
+minimum_costs=17.4
+
+# Por Defecto reducida
+default=(0.131, 1)
+
+# Tarifa Normal
+on (monday..friday) between (17:00..21:59) use (0.2598, 1)
+
+# Tarifa Punta
+on (monday..friday) between (8:00..16:59) use (0.2987, 1)
+on (saturday) between (8:00..13:59) use (0.2987, 1)
+
+# Festivos: Reducida
+on (1/1) between () use (0.131, 1)
+on (1/6) between () use (0.131, 1)
+on (5/1) between () use (0.131, 1)
+on (10/12) between () use (0.131, 1)
+on (11/1) between () use (0.131, 1)
+on (12/6) between () use (0.131, 1)
+on (12/8) between () use (0.131, 1)
+on (12/25) between () use (0.131, 1)
+on (easter) between () use (0.131, 1)
+on (easter+50) between () use (0.131, 1)
diff --git a/kppp/Rules/Sweden/ACN.rst b/kppp/Rules/Sweden/ACN.rst
new file mode 100644
index 00000000..9a82d3b8
--- /dev/null
+++ b/kppp/Rules/Sweden/ACN.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with ACN
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=ACN_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 17 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0028333333333, 1)
+
+# Låg taxa: 9,4 öre/min (räknat per sekund)
+default=(0.0015666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0015666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0015666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0015666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0015666666667, 1)
+# första maj
+on (05/01) between () use (0.0015666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0015666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0015666666667, 1)
+# juldagen
+on (12/25) between () use (0.0015666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0015666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0015666666667, 1)
+# julafton
+on (12/24) between () use (0.0015666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0015666666667, 1)
diff --git a/kppp/Rules/Sweden/Abonnera_com.rst b/kppp/Rules/Sweden/Abonnera_com.rst
new file mode 100644
index 00000000..85a64025
--- /dev/null
+++ b/kppp/Rules/Sweden/Abonnera_com.rst
@@ -0,0 +1,69 @@
+##############################################################
+# Swedish rate ruleset for calls with Abonnera.com
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Abonnera_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 48 öre
+per_connection=0.48
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Specialpris över sommaren 2000 (6 öre/min)
+on (06/01..08/31) between () use (0.001, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/CNEAB-Route66.rst b/kppp/Rules/Sweden/CNEAB-Route66.rst
new file mode 100644
index 00000000..53fec7b5
--- /dev/null
+++ b/kppp/Rules/Sweden/CNEAB-Route66.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with CNEAB-Route66
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=CNEAB-Route66_Lokalt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Crossnet-Affinity.rst b/kppp/Rules/Sweden/Crossnet-Affinity.rst
new file mode 100644
index 00000000..a2f0cef5
--- /dev/null
+++ b/kppp/Rules/Sweden/Crossnet-Affinity.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Crossnet-Affinity
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Crossnet-Affinity_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Glocalnet.rst b/kppp/Rules/Sweden/Glocalnet.rst
new file mode 100644
index 00000000..7c720a0a
--- /dev/null
+++ b/kppp/Rules/Sweden/Glocalnet.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Glocalnet
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Glocalnet_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 9 öre/min (räknat per sekund)
+default=(0.0015, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0015, 1)
+# trettondag jul
+on (01/06) between () use (0.0015, 1)
+# långfredagen
+on (easter-2) between () use (0.0015, 1)
+# annandag påsk
+on (easter+1) between () use (0.0015, 1)
+# första maj
+on (05/01) between () use (0.0015, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0015, 1)
+# annandag pingst
+on (easter+50) between () use (0.0015, 1)
+# juldagen
+on (12/25) between () use (0.0015, 1)
+# annandag jul
+on (12/26) between () use (0.0015, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0015, 1)
+# julafton
+on (12/24) between () use (0.0015, 1)
+# nyårsafton
+on (12/31) between () use (0.0015, 1)
diff --git a/kppp/Rules/Sweden/Gts.rst b/kppp/Rules/Sweden/Gts.rst
new file mode 100644
index 00000000..1451498d
--- /dev/null
+++ b/kppp/Rules/Sweden/Gts.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with Gts
+# Created 2000-Aug-30 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Gts_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 21 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0035, 1)
+
+# Låg taxa: 11,5 öre/min (räknat per sekund)
+default=(0.0019166666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0019166666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0019166666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0019166666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0019166666667, 1)
+# första maj
+on (05/01) between () use (0.0019166666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0019166666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0019166666667, 1)
+# juldagen
+on (12/25) between () use (0.0019166666667, 1)
+# annandag jul
+on (12/26) between () use (0.0019166666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0019166666667, 1)
+# julafton
+on (12/24) between () use (0.0019166666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0019166666667, 1)
diff --git a/kppp/Rules/Sweden/Home_se.rst b/kppp/Rules/Sweden/Home_se.rst
new file mode 100644
index 00000000..7ec43579
--- /dev/null
+++ b/kppp/Rules/Sweden/Home_se.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with Home.se
+# Created 2000-Aug-30 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Home.se_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 9 öre/min (räknat per sekund)
+default=(0.0015, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0015, 1)
+# trettondag jul
+on (01/06) between () use (0.0015, 1)
+# långfredagen
+on (easter-2) between () use (0.0015, 1)
+# annandag påsk
+on (easter+1) between () use (0.0015, 1)
+# första maj
+on (05/01) between () use (0.0015, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0015, 1)
+# annandag pingst
+on (easter+50) between () use (0.0015, 1)
+# juldagen
+on (12/25) between () use (0.0015, 1)
+# annandag jul
+on (12/26) between () use (0.0015, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0015, 1)
+# julafton
+on (12/24) between () use (0.0015, 1)
+# nyårsafton
+on (12/31) between () use (0.0015, 1)
diff --git a/kppp/Rules/Sweden/Makefile.am b/kppp/Rules/Sweden/Makefile.am
new file mode 100644
index 00000000..29fe64be
--- /dev/null
+++ b/kppp/Rules/Sweden/Makefile.am
@@ -0,0 +1,12 @@
+pkg_DATA = ACN.rst Abonnera_com.rst \
+ CNEAB-Route66.rst Crossnet-Affinity.rst Glocalnet.rst Gts.rst \
+ Home_se.rst Nemtel.rst Plusenergi.rst RSLCom.rst \
+ Rix_Telecom.rst Supertel.rst Svensk_Telekom.rst \
+ Tele1_Europe.rst Tele2.rst Tele8.rst Teleman.rst \
+ Telenordia.rst Telerian.rst Telia.rst Telia_Telebonus1.rst \
+ Telia_Telebonus2.rst Telitel.rst Transnet.rst \
+ Universal_Telecom.rst Utfors.rst Vattenfall.rst Tiscali.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Sweden
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Sweden/Nemtel.rst b/kppp/Rules/Sweden/Nemtel.rst
new file mode 100644
index 00000000..025d8b39
--- /dev/null
+++ b/kppp/Rules/Sweden/Nemtel.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Nemtel
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Nemtel_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Plusenergi.rst b/kppp/Rules/Sweden/Plusenergi.rst
new file mode 100644
index 00000000..17d62841
--- /dev/null
+++ b/kppp/Rules/Sweden/Plusenergi.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with Plusenergi.se
+# Created 2000-Aug-30 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Plusenergi_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 49 öre
+per_connection=0.49
+minimum_costs=0.0
+
+# Hög taxa: 19,5 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.00325, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/RSLCom.rst b/kppp/Rules/Sweden/RSLCom.rst
new file mode 100644
index 00000000..90d91b46
--- /dev/null
+++ b/kppp/Rules/Sweden/RSLCom.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with RSLCom
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=RSLCom_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 9,5 öre/min (räknat per sekund)
+default=(0.0015833333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0015833333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0015833333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0015833333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0015833333333, 1)
+# första maj
+on (05/01) between () use (0.0015833333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0015833333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0015833333333, 1)
+# juldagen
+on (12/25) between () use (0.0015833333333, 1)
+# annandag jul
+on (12/26) between () use (0.0015833333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0015833333333, 1)
+# julafton
+on (12/24) between () use (0.0015833333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0015833333333, 1)
diff --git a/kppp/Rules/Sweden/Rix_Telecom.rst b/kppp/Rules/Sweden/Rix_Telecom.rst
new file mode 100644
index 00000000..e1128af6
--- /dev/null
+++ b/kppp/Rules/Sweden/Rix_Telecom.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Rix Telecom
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Rix_Telecom_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/Supertel.rst b/kppp/Rules/Sweden/Supertel.rst
new file mode 100644
index 00000000..d1d595da
--- /dev/null
+++ b/kppp/Rules/Sweden/Supertel.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Supertel
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Supertel_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Svensk_Telekom.rst b/kppp/Rules/Sweden/Svensk_Telekom.rst
new file mode 100644
index 00000000..4737522b
--- /dev/null
+++ b/kppp/Rules/Sweden/Svensk_Telekom.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Svensk Telekom
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Svensk_Telekom_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Tele1_Europe.rst b/kppp/Rules/Sweden/Tele1_Europe.rst
new file mode 100644
index 00000000..84514804
--- /dev/null
+++ b/kppp/Rules/Sweden/Tele1_Europe.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Tele1 Europe
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Tele1_Europe_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 23 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0038333333333, 1)
+
+# Låg taxa: 11,5 öre/min (räknat per sekund)
+default=(0.0019166666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0019166666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0019166666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0019166666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0019166666667, 1)
+# första maj
+on (05/01) between () use (0.0019166666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0019166666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0019166666667, 1)
+# juldagen
+on (12/25) between () use (0.0019166666667, 1)
+# annandag jul
+on (12/26) between () use (0.0019166666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0019166666667, 1)
+# julafton
+on (12/24) between () use (0.0019166666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0019166666667, 1)
diff --git a/kppp/Rules/Sweden/Tele2.rst b/kppp/Rules/Sweden/Tele2.rst
new file mode 100644
index 00000000..714a0d46
--- /dev/null
+++ b/kppp/Rules/Sweden/Tele2.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Tele2
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Tele2_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/Tele8.rst b/kppp/Rules/Sweden/Tele8.rst
new file mode 100644
index 00000000..b6ab1313
--- /dev/null
+++ b/kppp/Rules/Sweden/Tele8.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Tele8
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Tele8_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 0 öre
+per_connection=0.0
+minimum_costs=0.0
+
+# Hög taxa: 33 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0055, 1)
+
+# Låg taxa: 19 öre/min (räknat per sekund)
+default=(0.0031666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0031666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0031666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0031666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0031666666667, 1)
+# första maj
+on (05/01) between () use (0.0031666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0031666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0031666666667, 1)
+# juldagen
+on (12/25) between () use (0.0031666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0031666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0031666666667, 1)
+# julafton
+on (12/24) between () use (0.0031666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0031666666667, 1)
diff --git a/kppp/Rules/Sweden/Teleman.rst b/kppp/Rules/Sweden/Teleman.rst
new file mode 100644
index 00000000..f94bdeff
--- /dev/null
+++ b/kppp/Rules/Sweden/Teleman.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Teleman.com
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Teleman_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 35 öre
+per_connection=0.35
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Telenordia.rst b/kppp/Rules/Sweden/Telenordia.rst
new file mode 100644
index 00000000..2d3617b1
--- /dev/null
+++ b/kppp/Rules/Sweden/Telenordia.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Telenordia
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telenordia_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Telerian.rst b/kppp/Rules/Sweden/Telerian.rst
new file mode 100644
index 00000000..b0e108ca
--- /dev/null
+++ b/kppp/Rules/Sweden/Telerian.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Telerian
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telerian_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 19 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0031666666667, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/Telia.rst b/kppp/Rules/Sweden/Telia.rst
new file mode 100644
index 00000000..5becdcdc
--- /dev/null
+++ b/kppp/Rules/Sweden/Telia.rst
@@ -0,0 +1,68 @@
+##############################################################
+# Swedish rate ruleset for calls with Telia
+# Created 1997-09-16 by Anders Widell <d95-awi@nada.kth.se>
+# Updated 1998-04-26 by Anders Widell <d95-awi@nada.kth.se>
+# Updated 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telia_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 23 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0038333333333, 1)
+
+# Låg taxa: 11,5 öre/min (räknat per sekund)
+default=(0.0019166666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0019166666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0019166666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0019166666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0019166666667, 1)
+# första maj
+on (05/01) between () use (0.0019166666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0019166666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0019166666667, 1)
+# juldagen
+on (12/25) between () use (0.0019166666667, 1)
+# annandag jul
+on (12/26) between () use (0.0019166666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0019166666667, 1)
+# julafton
+on (12/24) between () use (0.0019166666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0019166666667, 1)
diff --git a/kppp/Rules/Sweden/Telia_Telebonus1.rst b/kppp/Rules/Sweden/Telia_Telebonus1.rst
new file mode 100644
index 00000000..9fc634c7
--- /dev/null
+++ b/kppp/Rules/Sweden/Telia_Telebonus1.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Telia /w Telebonus 1 (10% discount)
+# Created 1998-04-26 by Anders Widell <d95-awi@nada.kth.se>
+# Updated 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telia_Lokalt_Telebonus1
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40,5 öre
+per_connection=0.405
+minimum_costs=0.0
+
+# Hög taxa: 20,7 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.00345, 1)
+
+# Låg taxa: 10,35 öre/min (räknat per sekund)
+default=(0.001725, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.001725, 1)
+# trettondag jul
+on (01/06) between () use (0.001725, 1)
+# långfredagen
+on (easter-2) between () use (0.001725, 1)
+# annandag påsk
+on (easter+1) between () use (0.001725, 1)
+# första maj
+on (05/01) between () use (0.001725, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.001725, 1)
+# annandag pingst
+on (easter+50) between () use (0.001725, 1)
+# juldagen
+on (12/25) between () use (0.001725, 1)
+# annandag jul
+on (12/26) between () use (0.001725, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.001725, 1)
+# julafton
+on (12/24) between () use (0.001725, 1)
+# nyårsafton
+on (12/31) between () use (0.001725, 1)
diff --git a/kppp/Rules/Sweden/Telia_Telebonus2.rst b/kppp/Rules/Sweden/Telia_Telebonus2.rst
new file mode 100644
index 00000000..55bb0aa0
--- /dev/null
+++ b/kppp/Rules/Sweden/Telia_Telebonus2.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Telia /w Telebonus 2 (15% discount)
+# Created 1998-04-26 by Anders Widell <d95-awi@nada.kth.se>
+# Updated 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telia_Lokalt_Telebonus2
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 38,25 öre
+per_connection=0.3825
+minimum_costs=0.0
+
+# Hög taxa: 19,55 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0032583333333, 1)
+
+# Låg taxa: 9,775 öre/min (räknat per sekund)
+default=(0.0016291666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016291666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016291666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016291666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016291666667, 1)
+# första maj
+on (05/01) between () use (0.0016291666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016291666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016291666667, 1)
+# juldagen
+on (12/25) between () use (0.0016291666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016291666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016291666667, 1)
+# julafton
+on (12/24) between () use (0.0016291666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016291666667, 1)
diff --git a/kppp/Rules/Sweden/Telitel.rst b/kppp/Rules/Sweden/Telitel.rst
new file mode 100644
index 00000000..d4487c92
--- /dev/null
+++ b/kppp/Rules/Sweden/Telitel.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Telitel
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Telitel_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 50 öre
+per_connection=0.50
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 9 öre/min (räknat per sekund)
+default=(0.0015, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0015, 1)
+# trettondag jul
+on (01/06) between () use (0.0015, 1)
+# långfredagen
+on (easter-2) between () use (0.0015, 1)
+# annandag påsk
+on (easter+1) between () use (0.0015, 1)
+# första maj
+on (05/01) between () use (0.0015, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0015, 1)
+# annandag pingst
+on (easter+50) between () use (0.0015, 1)
+# juldagen
+on (12/25) between () use (0.0015, 1)
+# annandag jul
+on (12/26) between () use (0.0015, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0015, 1)
+# julafton
+on (12/24) between () use (0.0015, 1)
+# nyårsafton
+on (12/31) between () use (0.0015, 1)
diff --git a/kppp/Rules/Sweden/Tiscali.rst b/kppp/Rules/Sweden/Tiscali.rst
new file mode 100644
index 00000000..1a91b0cb
--- /dev/null
+++ b/kppp/Rules/Sweden/Tiscali.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with Tiscali
+# Created 2001-01-01 by Anders Widell <awl@hem.passagen.se>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Tiscali
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 14 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0023333333333, 1)
+
+# Låg taxa: 8,5 öre/min (räknat per sekund)
+default=(0.0014166666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0014166666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0014166666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0014166666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0014166666667, 1)
+# första maj
+on (05/01) between () use (0.0014166666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0014166666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0014166666667, 1)
+# juldagen
+on (12/25) between () use (0.0014166666667, 1)
+# annandag jul
+on (12/26) between () use (0.0014166666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0014166666667, 1)
+# julafton
+on (12/24) between () use (0.0014166666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0014166666667, 1)
diff --git a/kppp/Rules/Sweden/Transnet.rst b/kppp/Rules/Sweden/Transnet.rst
new file mode 100644
index 00000000..cc68c970
--- /dev/null
+++ b/kppp/Rules/Sweden/Transnet.rst
@@ -0,0 +1,65 @@
+##############################################################
+# Swedish rate ruleset for calls with Transnet.nl
+# Created 2000-Aug-30 by Fredrik Ismyren <myrn@despammed.com>
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Transnet_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 17 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0028333333333, 1)
+
+# Låg taxa: 8 öre/min (räknat per sekund)
+default=(0.0013333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0013333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0013333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0013333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0013333333333, 1)
+# första maj
+on (05/01) between () use (0.0013333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0013333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0013333333333, 1)
+# juldagen
+on (12/25) between () use (0.0013333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0013333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0013333333333, 1)
+# julafton
+on (12/24) between () use (0.0013333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0013333333333, 1)
diff --git a/kppp/Rules/Sweden/Universal_Telecom.rst b/kppp/Rules/Sweden/Universal_Telecom.rst
new file mode 100644
index 00000000..1996f609
--- /dev/null
+++ b/kppp/Rules/Sweden/Universal_Telecom.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Universal Telecom
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Universal_Telecom_Lokalt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 30 öre
+per_connection=0.30
+minimum_costs=0.0
+
+# Hög taxa: 16 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0026666666667, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Sweden/Utfors.rst b/kppp/Rules/Sweden/Utfors.rst
new file mode 100644
index 00000000..fe3f9f30
--- /dev/null
+++ b/kppp/Rules/Sweden/Utfors.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Utfors
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Utfors_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 40 öre
+per_connection=0.40
+minimum_costs=0.0
+
+# Hög taxa: 20 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.0033333333333, 1)
+
+# Låg taxa: 10 öre/min (räknat per sekund)
+default=(0.0016666666667, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0016666666667, 1)
+# trettondag jul
+on (01/06) between () use (0.0016666666667, 1)
+# långfredagen
+on (easter-2) between () use (0.0016666666667, 1)
+# annandag påsk
+on (easter+1) between () use (0.0016666666667, 1)
+# första maj
+on (05/01) between () use (0.0016666666667, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0016666666667, 1)
+# annandag pingst
+on (easter+50) between () use (0.0016666666667, 1)
+# juldagen
+on (12/25) between () use (0.0016666666667, 1)
+# annandag jul
+on (12/26) between () use (0.0016666666667, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0016666666667, 1)
+# julafton
+on (12/24) between () use (0.0016666666667, 1)
+# nyårsafton
+on (12/31) between () use (0.0016666666667, 1)
diff --git a/kppp/Rules/Sweden/Vattenfall.rst b/kppp/Rules/Sweden/Vattenfall.rst
new file mode 100644
index 00000000..1a6bfc2a
--- /dev/null
+++ b/kppp/Rules/Sweden/Vattenfall.rst
@@ -0,0 +1,66 @@
+##############################################################
+# Swedish rate ruleset for calls with Vattenfall
+# Created 2000-Aug-01 by Fredrik Ismyren <myrn@despammed.com>
+# Updated 2000-Aug-30 by Fredrik Ismyren
+# May be distributed freely. I take no responsibility for
+# the correctness of the information in this file.
+##############################################################
+
+name=Vattenfall_Nationellt
+currency_symbol=SEK
+currency_position=right
+currency_digits=2
+
+# Så här beräknar Telia samtalskostnaden (enligt vad jag förstår...)
+#
+# * en öppningsavgift debiteras för alla samtal så fort man fått svar
+# * därefter beräknas kostnaden per sekund
+#
+# Kostnaden per sekund beror av tiden:
+# * hög taxa vardagar kl 08:00-18:00
+# * låg taxa övrig tid
+#
+# Som vardagar räknas måndag till fredag. Dock ej:
+# * röda dagar i almanackan
+# * Vissa övriga dagar. Vilka dessa är verkar något oklart, och kan kanske
+# variera från år till år efter Telias tycke och smak. Men en tumregel är
+# att "aftnar" typ julafton och nyårsafton inte räknas som vardagar.
+
+
+# Öppningsavgift: 45 öre
+per_connection=0.45
+minimum_costs=0.0
+
+# Hög taxa: 19,5 öre/min måndag-fredag 8-18
+on (monday..friday) between (8:00..17:59) use (0.00325, 1)
+
+# Låg taxa: 11 öre/min (räknat per sekund)
+default=(0.0018333333333, 1)
+
+# Röda helgdagar (de som infaller på en lördag/söndag utelämnade)
+# nyårsdagen
+on (01/01) between () use (0.0018333333333, 1)
+# trettondag jul
+on (01/06) between () use (0.0018333333333, 1)
+# långfredagen
+on (easter-2) between () use (0.0018333333333, 1)
+# annandag påsk
+on (easter+1) between () use (0.0018333333333, 1)
+# första maj
+on (05/01) between () use (0.0018333333333, 1)
+# kristi himmelfärds dag
+on (easter+39) between () use (0.0018333333333, 1)
+# annandag pingst
+on (easter+50) between () use (0.0018333333333, 1)
+# juldagen
+on (12/25) between () use (0.0018333333333, 1)
+# annandag jul
+on (12/26) between () use (0.0018333333333, 1)
+
+# "aftnar" (osäkert om följande är korrekt & fullständigt)
+# trettondagsafton
+on (01/05) between () use (0.0018333333333, 1)
+# julafton
+on (12/24) between () use (0.0018333333333, 1)
+# nyårsafton
+on (12/31) between () use (0.0018333333333, 1)
diff --git a/kppp/Rules/Switzerland/Makefile.am b/kppp/Rules/Switzerland/Makefile.am
new file mode 100644
index 00000000..87582e64
--- /dev/null
+++ b/kppp/Rules/Switzerland/Makefile.am
@@ -0,0 +1,10 @@
+pkg_DATA = Swisscom_Local.rst \
+ Swisscom_Remote.rst \
+ Swisscom_Surf.rst \
+ Sunrise_Freetime.rst \
+ Sunrise_Select_Internet.rst \
+ Sunrise_Local.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Switzerland
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Switzerland/Sunrise_Freetime.rst b/kppp/Rules/Switzerland/Sunrise_Freetime.rst
new file mode 100644
index 00000000..ebf15c2e
--- /dev/null
+++ b/kppp/Rules/Switzerland/Sunrise_Freetime.rst
@@ -0,0 +1,43 @@
+################################################################
+#
+# Sunrise Freetime (Unbeschränkt surfen mit 30.-/Monat)
+# Created by Gregor Zumstein (zumstein@ssd.ethz.ch)
+# Sep 29 2000
+#
+################################################################
+
+name=Sunrise_Freetime
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=CHF
+currency_position=right
+
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+default=(0, 1)
+
+# Niedertarif, tarif réduit
+# +30 Fr. Basisgebühr pro Monat, tarif de base par mois
+on (monday..friday) between (8:00..22:59) use (0.00046111, 1)
+
+on (01/01, 01/02, easter-2, easter+1, easter+39, easter+50, 08/01) between (0:00..23:59) use (0, 1)
+
+# Feiertage / Jours fériés:
+# 01/01 Neujahr / Nouvel an
+# 01/02 2. Januar / 2 janvier
+# easter-2 Karfreitag / Vendredi saint
+# easter+1 Ostermontag / Lundi de pâques
+# easter+39 Auffahrt / Ascenscion
+# easter+50 Pfingstmontag / Lundi de pentecôte
+# 08/01 Nationalfeiertag / Fête nationale
+# 12/25 Weihnachten / Noël
+# 12/26 Stephanstag / 2ème jour de noël \ No newline at end of file
diff --git a/kppp/Rules/Switzerland/Sunrise_Local.rst b/kppp/Rules/Switzerland/Sunrise_Local.rst
new file mode 100644
index 00000000..7486c5c7
--- /dev/null
+++ b/kppp/Rules/Switzerland/Sunrise_Local.rst
@@ -0,0 +1,51 @@
+################################################################
+#
+# Sunrise local
+# Created by Daniel Brönnimann (dbroenni@g26.ethz.ch)
+# Nov 16 1998
+# Updated by Gregor Zumstein (zumstein@ssd.ethz.ch)
+# Sep 29 2000.
+#
+################################################################
+
+name=Sunrise_Local
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=CHF
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# Nachttarif, tarif de nuit
+default=(0.00019444, 1)
+
+# Normaltarif, tarif normal
+on (monday..friday) between (8:00..16:59) use (0.0011111, 1)
+
+# Niedertarif, tarif réduit
+# easter+39 = Ascension Day (Auffahrt), easter+50 = Whit Monday (Pfingstmontag)
+on (monday..friday) between (17:00..22:59) use (0.0005, 1)
+on (saturday..sunday) between (8:00..22:59) use (0.0005, 1)
+on (01/01, 01/02, easter-2, easter+1) between (8:00..22:59) use (0.0005, 1)
+on (easter+39, easter+50) between (8:00..22:59) use(0.0005, 1)
+on (08/01, 12/25, 12/26) between (8:00..22:59) use (0.0005, 1)
+
+# Feiertage / Jours fériés:
+# 01/01 Neujahr / Nouvel an
+# 01/02 2. Januar / 2 janvier
+# easter-2 Karfreitag / Vendredi saint
+# easter+1 Ostermontag / Lundi de pâques
+# easter+39 Auffahrt / Ascenscion
+# easter+50 Pfingstmontag / Lundi de pentecôte
+# 08/01 Nationalfeiertag / Fête nationale
+# 12/25 Weihnachten / Noël
+# 12/26 Stephanstag / 2ème jour de noël
diff --git a/kppp/Rules/Switzerland/Sunrise_Select_Internet.rst b/kppp/Rules/Switzerland/Sunrise_Select_Internet.rst
new file mode 100644
index 00000000..0cda65f9
--- /dev/null
+++ b/kppp/Rules/Switzerland/Sunrise_Select_Internet.rst
@@ -0,0 +1,49 @@
+################################################################
+#
+# Sunrise Select Internet
+# Created by Daniel Brönnimann (dbroenni@g26.ethz.ch) Nov 19. 1998
+# Changed by Philipp Gressly (phi@gressly.ch) Sep. 16. 2000
+# Changed by Gregor Zumstein (zumstein@ssd.ethz.ch) Sep 29 2000
+#
+################################################################
+
+name=Sunrise_Select_Internet
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=CHF
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# Nachttarif, tarif de nuit
+default=(0.00023056, 1)
+
+# Normaltarif, Niedertarif tarif normal
+on (monday..sunday) between (8:00..22:59) use (0.0004639, 1)
+
+# "Nationale Feiertage"
+# easter+39 = Ascension Day (Auffahrt), easter+50 = Whit Monday (Pfingstmontag)
+on (01/01, 01/02, easter-2, easter+1) between (8:00..22:59) use (0.0004639, 1)
+on (easter+39, easter+50) between (8:00..22:59) use(0.0004639, 1)
+on (08/01, 12/25, 12/26) between (8:00..22:59) use (0.0004639, 1)
+
+
+# Feiertage / Jours fériés:
+# 01/01 Neujahr / Nouvel an
+# 01/02 2. Januar / 2 janvier
+# easter-2 Karfreitag / Vendredi saint
+# easter+1 Ostermontag / Lundi de pâques
+# easter+39 Auffahrt / Ascenscion
+# easter+50 Pfingstmontag / Lundi de pentecôte
+# 08/01 Nationalfeiertag / Fête nationale
+# 12/25 Weihnachten / Noël
+# 12/26 Stephanstag / 2ème jour de noël
diff --git a/kppp/Rules/Switzerland/Swisscom_Local.rst b/kppp/Rules/Switzerland/Swisscom_Local.rst
new file mode 100644
index 00000000..4a4e446b
--- /dev/null
+++ b/kppp/Rules/Switzerland/Swisscom_Local.rst
@@ -0,0 +1,38 @@
+# Changed by Daniel Brönnimann (dbroenni@g26.ethz.ch)# Thu Nov 19 1998
+# Changed by Fritz Zaucker (zaucker@ee.ethz.ch)
+# Sun Oct 24 1999
+# Valid from 1-Oct-1999
+
+name=Swisscom_Local_2
+
+currency_symbol=CHF
+
+currency_position=left
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.00
+default=(0.10, 90)
+
+# Special dates:
+# easter - 2 days (Karfreitag/ ??? )
+# easter + 39 days (Auffahrt/ ??? )
+# easter + 50 days (Pfingstmontag/ Pentecost Monday )
+# 08/01 (August 1st, Swiss National Holiday)
+
+# Note: the previous version of this rule set erroneously had
+# 02/01 instead of 01/02 (January 2nd)
+# and 01/08 instead of 08/01 (August 1st)
+
+# Normal tarif
+on (monday..friday) between (8:00..17:00) use (0.10, 90)
+
+# Low tarif
+on (monday..friday) between (6:00..8:00) use (0.10, 180)
+on (monday..friday) between (17:00..22:00) use (0.10, 180)
+on (saturday..sunday) between (6:00..22:00) use (0.10, 180)
+on (01/01, 01/02, easter-2, easter+1) between (6:00..22:00) use(0.10, 180)
+on (easter+39, easter+50, 08/01, 12/25,12/26) between (6:00..22:00) use (0.10, 180)
+
+# Night tarif
+on (monday..sunday) between (22:00..0:00) use (0.10, 360)
+on (monday..sunday) between (0:00..6:00) use (0.10, 360)
diff --git a/kppp/Rules/Switzerland/Swisscom_Remote.rst b/kppp/Rules/Switzerland/Swisscom_Remote.rst
new file mode 100644
index 00000000..a614786e
--- /dev/null
+++ b/kppp/Rules/Switzerland/Swisscom_Remote.rst
@@ -0,0 +1,43 @@
+# Calls outside own area code
+# Created by J. Wezel (jwezel@access.ch)
+# Sun Sep 14 01:30:33 1997
+# Changed by Daniel Brönnimann (dbreonni@g26.ethz.ch)
+# Thu Nov 19 1998
+# Changed by Fritz Zaucker (zaucker@ee.ethz.ch)
+# Sun Oct 24 1999
+# Valid from 1-Oct-1999
+
+name=Swisscom_Remote
+
+currency_symbol=CHF
+
+currency_position=left
+currency_digits=2
+per_connection=0.00
+minimum_costs=0.00
+default=(0.10, 24)
+
+# Special dates:
+# easter - 2 days (Karfreitag/ ??? )
+# easter + 39 days (Auffahrt/ ??? )
+# easter + 50 days (Pfingstmontag/ Pentecost Monday )
+# 08/01 (August 1st, Swiss National Holiday)
+
+# Note: the previous version of this rule set erroneously had
+# 02/01 instead of 01/02 (January 2nd)
+# and 01/08 instead of 08/01 (August 1st)
+
+# Normal tarif
+on (monday..friday) between (8:00..17:00) use (0.10, 24)
+
+# Low tarif
+on (monday..friday) between (6:00..8:00) use (0.10, 48)
+on (monday..friday) between (17:00..22:00) use (0.10, 48)
+on (saturday..sunday) between (6:00..22:00) use (0.10, 48)
+
+on (01/01, 01/02, easter-2, easter+1) between (6:00..22:00) use (0.10, 48)
+on (easter+39, easter+50, 08/01, 12/25, 12/26) between (6:00..22:00) use (0.10, 48)
+
+# Night tarif
+on (monday..sunday) between (22:00..0:00) use (0.10, 96)
+on (monday..sunday) between (0:00..6:00) use (0.10, 96)
diff --git a/kppp/Rules/Switzerland/Swisscom_Surf.rst b/kppp/Rules/Switzerland/Swisscom_Surf.rst
new file mode 100644
index 00000000..5d1bb0c9
--- /dev/null
+++ b/kppp/Rules/Switzerland/Swisscom_Surf.rst
@@ -0,0 +1,35 @@
+# Swisscom surfing rates of 0840 Business NumbersISP, Switzerland
+# Created by J. Wezel (jwezel@access.ch)
+# Sun Sep 14 01:30:33 1997
+# Changed by Daniel Brönnimann (dbroenni@g26.ethz.ch)
+# Thu Nov 19 1998
+# Valid from 1-Aug-97
+# Changed by Uli Pfeiffer (uli@bluewin.ch), 29/04/2000
+# Changed by Günther Palfinger (guenther.palfinger@gmx.net), 2001-09-08
+# see http://www.swisscom.ch/gd/services/voice_com/call_charges/charges_internet-en.html
+# corrected Business surf 90 -> 129 s/0.1 CHF
+
+name=Swisscom_Surf
+
+currency_symbol=CHF
+
+currency_position=left
+currency_digits=2
+per_connection=0.0
+minimum_costs=0.00
+default=(0.10, 129)
+
+# Business-Surf (CHF 2.80/h)
+on (monday..friday) between (8:00..16:00) use (0.10, 129)
+
+# Evening-Surf (CHF 1.80/h)
+on (monday..friday) between (16:00..22:00) use (0.10, 200)
+
+# Moonlight-Surf / Weekend-night Surf (CHF 0.70/h)
+on (monday..sunday) between (22:00..8:00) use (0.10, 514)
+
+# Weekend-day Surf
+on (saturday..sunday) between (8:00..22:00) use (0.10, 200)
+on (01/01, 01/02, easter-2, easter+1) between (8:00..22:00) use(0.10, 200)
+on (easter+39, easter+50, 08/01, 12/25,12/26) between (8:00..22:00) use (0.10, 200)
+
diff --git a/kppp/Rules/TEMPLATE b/kppp/Rules/TEMPLATE
new file mode 100644
index 00000000..0c3114f5
--- /dev/null
+++ b/kppp/Rules/TEMPLATE
@@ -0,0 +1,147 @@
+################################################################
+#
+# Disclaimer/License
+# This Template ist (c) by Mario Weilguni <mweilguni@kde.org>
+# It ist licenced under the same terms as the kppp package,
+# which it is part of
+#
+################################################################
+#
+# This is a sample rule set for kppp. You can use it as a
+# template when you have to create your own ruleset. If you do
+# so, remove all comments and add your own. This will allow
+# other users to check your ruleset more easily.
+#
+# Please sign the the tarif file with your name an email address
+# so that we can contact you if necessary.
+#
+# NOTE: the rules in this rule set do not make much sense and
+# are only for demonstration purposes
+#
+# NOTE ON FILENAMES:
+# when you create your own ruleset, use "_" in filename
+# instead of spaces and use ".rst as extension
+# i.e. "Austria city calls"
+# --> file should be saved as "Austria_city_calls.rst"
+# As of KDE 3.2 non-ascii characters can be encoded
+# with the %xy escapes known from URLs.
+#
+# NOTE ON ENCODING:
+# As of KDE 3.1 kppp assumes rule set files to be in UTF-8
+# encoding. See the currency_symbol entry for an example
+# of a non-ASCII character.
+#
+# Thanks, Bernd Wuebben
+# wuebben@kde.org
+# Current maintainer: Harri Porten, porten@kde.org
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=default
+
+################################################################
+# currency settings
+################################################################
+
+# defines ¤ (Euro) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=¤
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.0
+
+
+# You pay .74 for the first 180 seconds ( 3 minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+flat_init_costs=(0.74,180)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+default=(0.1, 72)
+
+#
+# more complicated rules:
+#
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+on () between () use (0.2, 2)
+
+# same as above
+on (monday..sunday) between () use (0.2, 2)
+
+# same as above. You must use 24 hour notation, or the accounting
+# will not work correctly. (Example: write 15:00 for 3 pm)
+on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# applies on friday, saturday, sunday and monday 8am until 1pm
+on (friday..monday) between (8:00..13:00) use(0.3,72)
+
+# ATTENTION:
+on(monday..friday) between (21:00..5:00) use (0.4,2)
+# does NOT include saturday 0:00-5:00, just monday..friday, as it says.
+
+# applies on a given date (christmas)
+on (12/25) between () use (0.3,72)
+
+# a range of dates and one weekday
+on (12/25..12/27, 12/31, 07/04, monday) between () use (0.4, 72)
+
+# use this for easter
+on (easter) between () use (0.3,72)
+
+# easter + 50 days (Pfingstmontag/ Pentecost Monday )
+on (easter+50) between () use (0.3,72)
+
+on (thursday) between (20:00..21:52) use (8.2, 1)
+
+
+# The "on()" rules above all relates to current time only. You can also
+# make a rule depend on the number of seconds you have been connected
+# by specifying this time as a third argument to "use()".
+# For instance, let's say normal rate in the evening is 0.20 per minute,
+# and it drops by 20% after one hour of connect time. This can be modelled
+# like:
+
+on () between (19:30..08:00) use (0.20, 60)
+on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# Note that these rules, just like other rules, are sensitive to the
+# order in which they appear.
+
diff --git a/kppp/Rules/Turkey/Makefile.am b/kppp/Rules/Turkey/Makefile.am
new file mode 100644
index 00000000..56ba2701
--- /dev/null
+++ b/kppp/Rules/Turkey/Makefile.am
@@ -0,0 +1,6 @@
+pkg_DATA = Turk_Telekom_Internet.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Turkey
+
+EXTRA_DIST = $(pkg_DATA)
+
diff --git a/kppp/Rules/Turkey/Turk_Telekom_Internet.rst b/kppp/Rules/Turkey/Turk_Telekom_Internet.rst
new file mode 100644
index 00000000..c2250059
--- /dev/null
+++ b/kppp/Rules/Turkey/Turk_Telekom_Internet.rst
@@ -0,0 +1,74 @@
+################################################################
+# 21 Jul 2003 (Please change when TT increases the costs)
+#
+# I take no responsibility for the validity and accuracy of
+# this information, if you are so concerned please contact
+# your own telephone provider and configure this yourself
+# and do not use this file.
+################################################################
+# Bu dosya sadece 0822'li hatlar icin gecerlidir.
+#
+# Lutfen TT zam yaptiginda use(xxxxx,yyy) ibarelerindeki xxxxx'i
+# yeni kontur ucreti ile degistirin.
+# Bu dosyanin nasil hazirlandigini ogrenmek istiyorsaniz
+# /usr/share/apps/kppp/rules dizinindeki TEMPLATE dosyasini
+# inceleyin.
+#
+#
+# Mesut Sismanoglu
+# mesuts@usa.net
+#
+# İsmail Dönmez
+# ismail.donmez@boun.edu.tr
+#
+################################################################
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Turk_Telekom_Internet
+################################################################
+# currency settings
+################################################################
+# defines TL (Turkish Lira) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=TL
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=right
+
+# Define the number of significant digits.
+# (not absolutely needed, default is "2"
+currency_digits=0
+
+# This is the default rule which is used when no other rule
+# applies.
+default=(72000, 324)
+################################################################
+# connection settings
+################################################################
+
+# Hafta arasi 1. indirim
+on(monday..friday) between (7:00..7:59) use(72000,309)
+
+ # Hafta arasi normal tarife
+on(monday..friday) between (8:00..19:59) use(72000,216)
+
+ # Hafta arasi 1. indirim
+on(monday..friday) between (20:00..22:29) use(72000,309)
+
+ # Hafta arasi 2. indirim
+on(monday..friday) between (22:30..6:59) use(72000,360)
+
+ # Cumartesi 1. indirim
+on(saturday) between (7:00..22:29) use(72000,309)
+
+ # Cumartesi 2. indirim
+on(saturday) between (22:30..6:59) use(72000,360)
+
+ # Pazar ve resmi tatil gunleri 2. indirim
+on(1/1, 04/23, 05/19, 08/30, 10/29, sunday) between () use(72000,360)
+
+################################################################
diff --git a/kppp/Rules/Ukraine/IPTelecom_hourly.rst b/kppp/Rules/Ukraine/IPTelecom_hourly.rst
new file mode 100644
index 00000000..2f020af9
--- /dev/null
+++ b/kppp/Rules/Ukraine/IPTelecom_hourly.rst
@@ -0,0 +1,86 @@
+################################################################
+# This file is in UTF-8 encoding.
+################################################################
+#
+# Права/ліцензіÑ
+# РозповÑюджуєтьÑÑ Ð¿Ñ–Ð´ ліценцзію, Ñку викориÑтовує пакунок kppp
+# Ðндрій РиÑін <arysin@yahoo.com>
+# 18 Вер 2002 12:21:06
+#
+################################################################
+
+################################################################
+#
+# ІМ'Я ÐÐБОРУ ПРÐВИЛ. Це ÐЕОБХІДÐО Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÑƒ коштів.
+#
+################################################################
+name=IPTelecom
+
+################################################################
+# уÑтановки валюти
+################################################################
+
+# ВикориÑтовуєтьÑÑ Ñƒ.о. (умовні одиниці або юніти)
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "$")
+currency_symbol=у.о.
+
+# Визначає Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°ÐºÑƒ валюти.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "right")
+currency_position=right
+
+# Визначає кількіÑÑ‚ÑŒ цифр піÑÐ»Ñ ÐºÐ¾Ð¼Ð¸.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ - "2")
+currency_digits=2
+
+
+################################################################
+# уÑтановки з'єднаннÑ
+################################################################
+
+# ПРИМІТКÐ: правила заÑтоÑовуютьÑÑ Ð·Ð²ÐµÑ€Ñ…Ñƒ вниз
+# ОСТÐÐÐЄ відповідне правило викориÑтовуєтьÑÑ
+# при розрахунку коштів.
+
+# Це ціна з'єднаннÑ. Якщо у Ð’Ð°Ñ Ñ—Ñ— немає,
+# виÑтавте "0", або закоментуйте.
+per_connection=0.0
+
+
+# Мінімальна Ñумма при з'єднні. Якщо Ñумма менше,
+# ніж це значеннÑ, платитьÑÑ Ñ†Ñ Ñумма.
+minimum_costs=0.0
+
+
+# Ви платите 0.74 за перші 180 Ñекунд (3 хв.) у будь Ñкому випадку,
+# навіть, Ñкщо Ви були з'єднані вÑього одну Ñекунду.
+# Це правило буде мати пріоритет впродовж перших 180 Ñекунд
+# над будь-Ñким іншим правилом.
+# ПереглÑньте малюнок costgraphs.gif в каталогу docs
+# комплекту kppp щодо наочного зображеннÑ.
+#flat_init_costs=(0.74,180)
+
+# Це - типове правило, Ñке викориÑтовуєтьÑÑ, коли інші правила
+# не працюють. Перший аргумент, "0.00115" - це ціна однієї "одиниці"
+# ("unit"), а "60" - довжина у Ñекундах.
+default=(0.000575, 3)
+
+#
+# більш Ñкладні правила:
+#
+
+on (monday..friday) between (00:00..08:59) use (0.00008055, 1)
+on (saturday..sunday) between () use (0.00008055, 1)
+
+# діапазон дат та окремий день тижнÑ
+#on (1/1,1/7,3/8,5/1,5/2,5/9,6/28,8/24) between () use (0.00008055, 1)
+on (1.1,7.1,8.3,1.5,2.5,9.5,28.6,24.8) between () use (0.00008055, 1)
+
+# це Ð´Ð»Ñ ÐŸÐ°Ñхи - але правоÑлавна ПаÑха відрізнÑєтьÑÑ Ð²Ñ–Ð´ католицької
+on (easter+14) between () use (0.00008055, 1)
+
+# ПаÑха + 50 днів (Троїцин день/П'ÑтидеÑÑтницÑ)
+on (easter+64) between () use (0.00008055, 1)
+
+# Зауважте, що ці правила, Ñк Ñ– інші, залежать від
+# поÑлідовноÑÑ‚Ñ–, у Ñкій вони запиÑані.
+
diff --git a/kppp/Rules/Ukraine/Makefile.am b/kppp/Rules/Ukraine/Makefile.am
new file mode 100644
index 00000000..695a2d34
--- /dev/null
+++ b/kppp/Rules/Ukraine/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = TEMPLATE.uk Utel_Unet.rst IPTelecom_hourly.rst NuVse_hourly.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Ukraine
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Ukraine/NuVse_hourly.rst b/kppp/Rules/Ukraine/NuVse_hourly.rst
new file mode 100644
index 00000000..a03b15cd
--- /dev/null
+++ b/kppp/Rules/Ukraine/NuVse_hourly.rst
@@ -0,0 +1,91 @@
+################################################################
+# This file is in UTF-8 encoding.
+################################################################
+#
+# Права/ліцензіÑ
+# РозповÑюджуєтьÑÑ Ð¿Ñ–Ð´ ліценцзію, Ñку викориÑтовує пакунок kppp
+# Ðндрій РиÑін <arysin@yahoo.com>
+# 19 Вер 2002 12:21:06
+#
+################################################################
+
+################################################################
+#
+# ІМ'Я ÐÐБОРУ ПРÐВИЛ. Це ÐЕОБХІДÐО Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÑƒ коштів.
+#
+################################################################
+name=NuVse
+
+################################################################
+# уÑтановки валюти
+################################################################
+
+# ВикориÑтовуєтьÑÑ Ñƒ.о. (умовні одиниці або юніти)
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "$")
+currency_symbol=гр.
+
+# Визначає Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°ÐºÑƒ валюти.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "right")
+currency_position=right
+
+# Визначає кількіÑÑ‚ÑŒ цифр піÑÐ»Ñ ÐºÐ¾Ð¼Ð¸.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ - "2")
+currency_digits=2
+
+
+################################################################
+# уÑтановки з'єднаннÑ
+################################################################
+
+# ПРИМІТКÐ: правила заÑтоÑовуютьÑÑ Ð·Ð²ÐµÑ€Ñ…Ñƒ вниз
+# ОСТÐÐÐЄ відповідне правило викориÑтовуєтьÑÑ
+# при розрахунку коштів.
+
+# Це ціна з'єднаннÑ. Якщо у Ð’Ð°Ñ Ñ—Ñ— немає,
+# виÑтавте "0", або закоментуйте.
+per_connection=0.0
+
+
+# Мінімальна Ñумма при з'єднні. Якщо Ñумма менше,
+# ніж це значеннÑ, платитьÑÑ Ñ†Ñ Ñумма.
+minimum_costs=0.0
+
+
+# Ви платите 0.74 за перші 180 Ñекунд (3 хв.) у будь Ñкому випадку,
+# навіть, Ñкщо Ви були з'єднані вÑього одну Ñекунду.
+# Це правило буде мати пріоритет впродовж перших 180 Ñекунд
+# над будь-Ñким іншим правилом.
+# ПереглÑньте малюнок costgraphs.gif в каталогу docs
+# комплекту kppp щодо наочного зображеннÑ.
+#flat_init_costs=(0.74,180)
+
+# Це - типове правило, Ñке викориÑтовуєтьÑÑ, коли інші правила
+# не працюють. Перший аргумент, "0.00115" - це ціна однієї "одиниці"
+# ("unit"), а "60" - довжина у Ñекундах.
+default=(0.005, 9)
+
+#
+# більш Ñкладні правила:
+#
+
+on () between (01:00..02:59) use (0.0025, 9)
+on () between (07:00..08:59) use (0.0025, 9)
+on () between (03:00..06:59) use (0.0001, 9)
+
+# здаєтьÑÑ Ð²Ð¸Ñ…Ñ–Ð´Ð½Ñ– та ÑвÑта не відрізнÑÑŽÑ‚ÑŒÑÑ Ð²Ñ–Ð´ будней
+#on (monday..friday) between (00:00..08:59) use (0.004833, 60)
+#on (saturday..sunday) between () use (0.004833, 60)
+
+# діапазон дат та окремий день тижнÑ
+#on (1/1,1/7,3/8,5/1,5/2,5/9,6/28,8/24) between () use (0.004833, 60)
+#on (1.1,7.1,8.3,1.5,2.5,9.5,28.6,24.8) between () use (0.004833, 60)
+
+# це Ð´Ð»Ñ ÐŸÐ°Ñхи - але правоÑлавна ПаÑха відрізнÑєтьÑÑ Ð²Ñ–Ð´ католицької
+#on (easter+14) between () use (0.004833, 60)
+
+# ПаÑха + 50 днів (Троїцин день/П'ÑтидеÑÑтницÑ)
+#on (easter+64) between () use (0.004833, 60)
+
+# Зауважте, що ці правила, Ñк Ñ– інші, залежать від
+# поÑлідовноÑÑ‚Ñ–, у Ñкій вони запиÑані.
+
diff --git a/kppp/Rules/Ukraine/TEMPLATE.uk b/kppp/Rules/Ukraine/TEMPLATE.uk
new file mode 100644
index 00000000..15073aae
--- /dev/null
+++ b/kppp/Rules/Ukraine/TEMPLATE.uk
@@ -0,0 +1,149 @@
+################################################################
+# This file is in UTF-8 encoding.
+################################################################
+#
+# Права/ліцензіÑ
+# Цей шаблон Ñтворено (c) Mario Weilguni <mweilguni@kde.org>
+# РозповÑюджуєтьÑÑ Ð¿Ñ–Ð´ ліценцзію, Ñку викориÑтовує пакунок kppp,
+# Ñкладовою Ñкого цей шаблон Ñ– Ñ”.
+#
+################################################################
+#
+# Це приклад набору правил Ð´Ð»Ñ kppp. Ви можете викориÑтовувати
+# цей шаблон Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð»Ð°Ñних наборів правил. При викориÑтанні
+# вилучіть вÑÑ– коментарі та додайте влаÑні. Це дозволить іншим
+# кориÑтувачам легше перевірÑти ваши правила.
+#
+# Будь лаÑка, підпишіть файл тарифів Вашим ім'Ñм та адреÑою ел. пошти,
+# щоб при необхідноÑÑ‚Ñ– Ñ Ð·Ð¼Ñ–Ð³ з Вами зв'ÑзатиÑÑ.
+#
+# ПРИМІТКÐ: правила в цьому файлі не Ñ” реальними Ñ– наведені
+# виключно у демонÑтраційному порÑдку
+#
+# ЩОДО ІМЕРФÐЙЛІВ:
+# коли Ви Ñтворюєте файл правил, вживайте "_" у імені файлу
+# заміÑÑ‚ÑŒ пропуÑків та викориÑтовуйте Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ ".rst"
+# напр. "Провайдер 1 міÑта Києва"
+# --> ім'Ñ Ñ„Ð°Ð¹Ð»Ð° буде "Провайдер_1_міÑта_Києва.rst"
+#
+# ДÑкую, Bernd Wuebben
+# wuebben@math.cornell.edu / wuebben@kde.org
+################################################################
+
+
+################################################################
+#
+# ІМ'Я ÐÐБОРУ ПРÐВИЛ. Це ÐЕОБХІДÐО Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÑƒ коштів.
+#
+################################################################
+name=default
+
+################################################################
+# уÑтановки валюти
+################################################################
+
+# ВикориÑтовуєтьÑÑ Ð“Ñ€. (ГривнÑ)
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "$")
+currency_symbol=Гр.
+
+# Визначає Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð°ÐºÑƒ валюти.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ = "right")
+currency_position=right
+
+# Визначає кількіÑÑ‚ÑŒ значущих цифр.
+# (не Ñ” необхідним, типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ - "2")
+currency_digits=2
+
+
+
+################################################################
+# уÑтановки з'єднаннÑ
+################################################################
+
+# ПРИМІТКÐ: правила заÑтоÑовуютьÑÑ Ð·Ð²ÐµÑ€Ñ…Ñƒ вниз
+# ОСТÐÐÐЄ відповідне правило викориÑтовуєтьÑÑ
+# при розрахунку коштів.
+
+# Це ціна з'єднаннÑ. Якщо у Ð’Ð°Ñ Ñ—Ñ— немає,
+# виÑтавте "0", або закоментуйте.
+per_connection=0.0
+
+
+# Мінімальна Ñумма при з'єднні. Якщо Ñумма менше,
+# ніж це значеннÑ, платитьÑÑ Ñ†Ñ Ñумма.
+minimum_costs=0.0
+
+
+# Ви платите 0.74 за перші 180 Ñекунд (3 хв.) у будь Ñкому випадку,
+# навіть, Ñкщо Ви були з'єднані вÑього одну Ñекунду.
+# Це правило буде мати пріоритет впродовж перших 180 Ñекунд
+# над будь-Ñким іншим правилом.
+# ПереглÑньте малюнок costgraphs.gif в каталогу docs
+# комплекту kppp щодо наочного зображеннÑ.
+flat_init_costs=(0.74,180)
+
+# Це - типове правило, Ñке викориÑтовуєтьÑÑ, коли інші правила
+# не працюють. Перший аргумент, "0.1" - це ціна однієї "одиниці"
+# ("unit"), а "72" - довжина у Ñекундах.
+# Таким чином, наÑтупне правило означає: "Кожні 72 Ñекунд 0.1
+# Гр. додаєтьÑÑ Ð´Ð¾ Вашого рахунку"
+default=(0.1, 72)
+
+#
+# більш Ñкладні правила:
+#
+
+# "з понеділка по неділю з 12:00 до 23:59 ціна
+# 0.2 за кожні 72 Ñекунд"
+on () between () use (0.2, 2)
+
+# теж Ñаме
+on (monday..sunday) between () use (0.2, 2)
+
+# теж Ñаме. Ви повинні вживати 24-годинний запиÑ, інакше
+# облік не буде працювати. (Ðаприклад: запиÑуйте 15:00 Ð´Ð»Ñ 3 pm)
+on (monday..sunday) between (0:00..23:59) use (0.2, 2)
+
+# заÑтоÑовуєтьÑÑ Ð´Ð¾ п'Ñтниці, Ñуботи, неділі та понедіка з 8am до 1pm
+on (friday..monday) between (8:00..13:00) use(0.3,72)
+
+# УВÐГÐ:
+on(monday..friday) between (21:00..5:00) use (0.4,2)
+# не включає в Ñебе Ñуботу 0:00-5:00, тільки з понеділка по п'Ñтницю.
+
+# заÑтоÑовуєтьÑÑ Ð½Ð° окрему дату (Різдво)
+on (1/07) between () use (0.3,72)
+# дату також можна запиÑувати Ñк <день>.<міÑÑць>
+on (7.1) between () use (0.3,72)
+
+# діапазон дат та окремий день тижнÑ
+on (12/25..12/27, 12/31, 07/04, monday) between () use (0.4, 72)
+# дату також можна запиÑувати Ñк <день>.<міÑÑць>
+on (25.12..27.12, 31.12, 04.07, monday) between () use (0.4, 72)
+
+# це Ð´Ð»Ñ (католицької) ПаÑхи
+on (easter) between () use (0.3,72)
+# це Ð´Ð»Ñ (правоÑлавної) ПаÑхи
+on (easter+14) between () use (0.3,72)
+
+# ПаÑха + 50 днів (Троїцин день/П'ÑтидеÑÑтницÑ)
+on (easter+50) between () use (0.3,72)
+# (правоÑлавна) ПаÑха + 50 днів (Троїцин день/П'ÑтидеÑÑтницÑ)
+on (easter+64) between () use (0.3,72)
+
+on (thursday) between (20:00..21:52) use (8.2, 1)
+
+
+# Правила "on()", наведені вгорі, відноÑÑÑ‚ÑŒÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ до поточного чаÑу.
+# Ви також можете Ñтворювати правила, що залежать від тривалоÑÑ‚Ñ– з'єднаннÑ
+# вказуючі цей Ñ‡Ð°Ñ Ñ‚Ñ€ÐµÑ‚Ñ–Ð¼ параметром у "use()".
+# Ðаприклад, Ñкажімо, нормальний тариф Ð´Ð»Ñ Ð²ÐµÑ‡Ð¾Ñ€Ð° - 0.20 за хвилину,
+# Ñ– він знижуєтьÑÑ Ð½Ð° 20% піÑÐ»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ— години з'єднаннÑ. Це може бути
+# запиÑано наÑтупним чином:
+
+on () between (19:30..08:00) use (0.20, 60)
+on () between (19:30..08:00) use (0.16, 60, 3600)
+
+# Зауважте, що ці правила, Ñк Ñ– інші, залежать від
+# поÑлідовноÑÑ‚Ñ–, у Ñкій вони запиÑані.
+
diff --git a/kppp/Rules/Ukraine/Utel_Unet.rst b/kppp/Rules/Ukraine/Utel_Unet.rst
new file mode 100644
index 00000000..fc73d533
--- /dev/null
+++ b/kppp/Rules/Ukraine/Utel_Unet.rst
@@ -0,0 +1,23 @@
+##############################################################
+# This file is in UTF-8 encoding
+# Ukrainnish rate ruleset for calls with Utel Unet
+# Created 2002-28-02 by Serhy O.Reshetnyuk <reshtnk7@unet.net.ua>
+##############################################################
+
+name=Utel_Unet
+currency_symbol=Гр.
+currency_position=right
+currency_digits=2
+
+
+# Це загальний Ð´Ð»Ñ Ð£ÐºÑ€Ð°Ñ—Ð½Ð¸ тариф,
+# Ð´Ð»Ñ ÐšÐ¸Ñ”Ð²Ð° Ñ– КиїівÑької облаÑÑ‚Ñ–
+# потрібно змінити
+# 10.80 Грн./год,
+# 0.18 Грн./MIN,
+# 0.003 Грн/SEK
+
+per_connection=0.0
+minimum_costs=0.0
+
+default=(0.003, 1)
diff --git a/kppp/Rules/United_Kingdom/BirminghamCable_Local.rst b/kppp/Rules/United_Kingdom/BirminghamCable_Local.rst
new file mode 100644
index 00000000..a9da69fc
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BirminghamCable_Local.rst
@@ -0,0 +1,106 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Under 35 miles (approx)
+# British_BirminghamCable_Local
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BirminghamCable_Local
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.04
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.033" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.033
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.033, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 3.3p/min (peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.0055, 10)
+
+# Change: 1.5p/min (off-peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.0025, 10)
+
+# Charge: 0.9p/min (weekend rate)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.0015, 10)
diff --git a/kppp/Rules/United_Kingdom/BirminghamCable_National.rst b/kppp/Rules/United_Kingdom/BirminghamCable_National.rst
new file mode 100644
index 00000000..1f32b36d
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BirminghamCable_National.rst
@@ -0,0 +1,111 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY OR ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF;
+# AND DO NOT USE THIS FILE.
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Over 35 miles (approx)
+# British_BirminghamCable_National
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BirminghamCable_National
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.04
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.075" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.075
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.075, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 7.5p/min (peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+on (monday..friday) between (8:00..17:59) use (0.0125, 10)
+
+# Change: 3.5p/min (off-peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+# Notes: This one doesn't nicely divide up for 10 seconds, so 12
+# is being used. The value for 10 seconds turns into a
+# recurring fraction. (0.0058333333/10secs)
+#
+on (monday..friday) between (18:00..7:59) use (0.007, 12)
+
+# Charge: 2.9p/min (weekend rate)
+# Min Charge: 4p/call
+# Unit Time: per second
+# Notes: This one doesn't nicely divide up for 10 seconds, so 12
+# is being used. The value for 10 seconds turns into a
+# recurring fraction. (0.0048333333/10secs)
+#
+on (saturday..sunday) between () use (0.0058, 12)
diff --git a/kppp/Rules/United_Kingdom/BirminghamCable_SameTelco.rst b/kppp/Rules/United_Kingdom/BirminghamCable_SameTelco.rst
new file mode 100644
index 00000000..8909e740
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BirminghamCable_SameTelco.rst
@@ -0,0 +1,111 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# SameTelco as a generic name (???) for BirminghamCable to
+# BirminghamCable call. i.e. within the "same telephone
+# company".
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Under 35 miles (approx)
+# British_BirminghamCable_SameTelco
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BirminghamCable_SameTelco
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.04
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.033" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.033
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.033, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 3.3p/min (peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.0055, 10)
+
+# Change: 1.5p/min (off-peak time)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.0025, 10)
+
+# Charge: 0.5p/min (weekend rate)
+# Min Charge: 4p/call
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.001, 12)
diff --git a/kppp/Rules/United_Kingdom/BritishTelecom_Local.rst b/kppp/Rules/United_Kingdom/BritishTelecom_Local.rst
new file mode 100644
index 00000000..e567f40d
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BritishTelecom_Local.rst
@@ -0,0 +1,110 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Under 35 miles (approx) and WITHIN same STD code (for many regions
+# the local band will cover a few adjecent STD code areas as well).
+# British_BritishTelecom_Local
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BritishTelecom_Local
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# They quote 5p _*inclusive*_ of VAT (Value Added Tax, whose
+# current rate is 17.5%). All other charges are exclusive
+# of VAT.
+minimum_costs=0.04256
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.04" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.04
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.04, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 4p/min (peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.008, 12)
+
+# Change: 1.7p/min (off-peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.0034, 12)
+
+# Charge: 1p/min (weekend rate)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.002, 12)
diff --git a/kppp/Rules/United_Kingdom/BritishTelecom_National.rst b/kppp/Rules/United_Kingdom/BritishTelecom_National.rst
new file mode 100644
index 00000000..f1580be4
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BritishTelecom_National.rst
@@ -0,0 +1,109 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Over 35 miles (approx)
+# British_BritishTelecom_National
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BritishTelecom_National
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# They quote 5p _*inclusive*_ of VAT (Value Added Tax, whose
+# current rate is 17.5%). All other charges are exclusive
+# of VAT.
+minimum_costs=0.04256
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.08" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.08
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.08, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 8p/min (peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.016, 12)
+
+# Change: 4.2p/min (off-peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.007, 10)
+
+# Charge: 3.3p/min (weekend rate)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.0055, 10)
diff --git a/kppp/Rules/United_Kingdom/BritishTelecom_Regional.rst b/kppp/Rules/United_Kingdom/BritishTelecom_Regional.rst
new file mode 100644
index 00000000..6d6ed5f9
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/BritishTelecom_Regional.rst
@@ -0,0 +1,109 @@
+################################################################
+#
+# 27 Oct 1997 (Valid until who knows?)
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# Under 35 miles (approx)
+# British_BritishTelecom_Regional
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_BritishTelecom_Regional
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pund and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+# They quote 5p _*inclusive*_ of VAT (Value Added Tax, whose
+# current rate is 17.5%). All other charges are exclusive
+# of VAT.
+minimum_costs=0.04256
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.08" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.08
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.08, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# We are actually charged to the second, but I don't really
+# need the PPP program to be so accurate myself (all that
+# CPU power working out costs, when it can be browsing the
+# Web, ho ho ho :-). To the nearest 10 (or so) seconds is
+# fine for me.
+
+# Charge: 8p/min (peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.016, 12)
+
+# Change: 4p/min (off-peak time)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.008, 12)
+
+# Charge: 3.3p/min (weekend rate)
+# Min Charge: 4.256p/call
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.0055, 10)
diff --git a/kppp/Rules/United_Kingdom/British_OneTel.rst b/kppp/Rules/United_Kingdom/British_OneTel.rst
new file mode 100644
index 00000000..d8f39f21
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/British_OneTel.rst
@@ -0,0 +1,83 @@
+
+################################################################
+#
+# 6 Jan 2001
+#
+# I TAKE NO RESPONSIBILITY FOR THE VALIDITY AND ACCURACY OF
+# THIS INFORMATION, IF YOU ARE SO CONCERNED PLEASE CONTACT
+# YOUR OWN TELEPHONE PROVIDER AND CONFIGURE THIS YOURSELF.
+# AND DO NOT USE THIS FILE.
+#
+#
+# Alain Trembleau
+# <alain@platodesigns.com
+#
+# OneTel have a flat rate of 1p per minute at all times
+# British_OneTel
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=British_OneTel
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+#
+# British monetary format examples:
+#
+# £1.57 = 1 pound and 57 pence.
+# 157p = 1 pound and 57 pence.
+#
+# defines GBP (Pound Sterling) to be used as currency symbol
+# (default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (default is "2")
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per connection. If the costs of a phone call
+# are less than this value, this value is used instead.
+# I haven't checked this, but I believe that there is a minimum
+# cost per call of 5p.
+minimum_costs=0.05
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.01" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.01
+# Pounds Sterling are added to the bill"
+#
+# Considering it is a flat rate, that's all that's needed!
+default=(0.01, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# No charge bands are needed since this uses a flat rate.
diff --git a/kppp/Rules/United_Kingdom/Connaught_Telecom.rst b/kppp/Rules/United_Kingdom/Connaught_Telecom.rst
new file mode 100644
index 00000000..bca78a54
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/Connaught_Telecom.rst
@@ -0,0 +1,100 @@
+################################################################
+#
+# Prices valid on 1st April 2001
+#
+# I am not responsible for the accuracy of this information. If
+# you know this file to be out of date or inaccurate, please
+# modify it.
+#
+# Jonathan Melhuish <jonathanmelhuish@email.com>
+#
+# Some comments copied from BT ruleset by Darryl L Miles
+# <dlm@g7led.demon.co.uk>
+#
+# This ruleset covers numbers covered by the Connaught Telecom
+# 'Internet' tarif - ie. 0845 numbers.
+#
+# All prices are *exclusive* of V.A.T.
+#
+################################################################
+
+
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Connaught_Connaught_Telecom
+
+
+################################################################
+# currency settings
+################################################################
+
+# 1 pound sterling = 100 pence sterling
+# 1 pence sterling is our lowest unit of coinage, but
+# I suspect telephone companys may remember the
+# fractions of pence for billing.
+#
+# Our monetry format:
+#
+# e.g. £1.57 = 1 pound and 57 pence.
+# e.g. 157p = 1 pound and 57 pence.
+#
+# defines UKP (PoundSterling) to be used as currency
+# symbol (not absolutely needed, default = "$")
+currency_symbol=£
+
+# Define the position of the currency symbol.
+# (not absolutely needed, default is "right")
+currency_position=left
+
+# Define the number of significat digits.
+# (not absolutely needed, default is "2"
+currency_digits=2
+
+
+################################################################
+# connection settings
+################################################################
+
+# NOTE: rules are applied from top to bottom - the
+# LAST matching rule is the one used for the
+# cost computations.
+
+# This is charged whenever you connect. If you don't have to
+# pay per-connection, use "0" here or comment it out.
+per_connection=0.0
+
+# minimum costs per per connection. If the costs of a phone
+# call are less than this value, this value is used instead
+minimum_costs=0.01
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.04" is the price of one
+# "unit", while "60" is the duration in seconds.
+# Therefore the following rule means: "Every 60 seconds 0.04
+# PoundsSterling are added to the bill"
+#
+# I've simply taken the worst case price for this, peak time
+# calls. Just in case I miss a period of time out.
+default=(0.04, 60)
+
+################################################################
+# charge bands
+################################################################
+
+# Charge: 3.3p/min (peak time)
+# Unit Time: per second
+#
+on (monday..friday) between (8:00..17:59) use (0.00055, 1)
+
+# Change: 1.7p/min (off-peak time)
+# Unit Time: per second
+#
+on (monday..friday) between (18:00..7:59) use (0.0002, 1)
+
+# Charge: 0.8p/min (weekend rate)
+# Unit Time: per second
+#
+on (saturday..sunday) between () use (0.0001, 1)
diff --git a/kppp/Rules/United_Kingdom/Makefile.am b/kppp/Rules/United_Kingdom/Makefile.am
new file mode 100644
index 00000000..7c69e97f
--- /dev/null
+++ b/kppp/Rules/United_Kingdom/Makefile.am
@@ -0,0 +1,12 @@
+pkg_DATA = BirminghamCable_Local.rst \
+ BirminghamCable_National.rst \
+ BirminghamCable_SameTelco.rst \
+ BritishTelecom_Local.rst \
+ BritishTelecom_National.rst \
+ BritishTelecom_Regional.rst \
+ British_OneTel.rst \
+ Connaught_Telecom.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/United_Kingdom
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Uruguay/Adinet_cIVA.rst b/kppp/Rules/Uruguay/Adinet_cIVA.rst
new file mode 100644
index 00000000..eb1a4a1a
--- /dev/null
+++ b/kppp/Rules/Uruguay/Adinet_cIVA.rst
@@ -0,0 +1,40 @@
+################################################################
+#
+# Disclaimer/License
+# This Template ist (c) by Mario Weilguni <mweilguni@kde.org>
+# It ist licenced under the same terms as the kppp package,
+# which it is part of
+#
+################################################################
+#
+# Cost rules for Adinet including VAT
+#
+# Otto Duarte
+# duvoel@adinet.com.uy
+################################################################
+
+################################################################
+name=Adinet_cIVA
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=$
+currency_position=right
+currency_digits=2
+
+################################################################
+default=(0.8612, 60)
+
+# tarifa reducida
+on (monday..friday) between (00:00..08:59) use (0.8612, 100)
+on (monday..friday) between (21:00..23:59) use (0.8612, 100)
+on (saturday..sunday) between (00:00..23:59) use (0.8612, 100)
+
+# feriados
+on (1/1) between (00:00..23:59) use (0.8612, 100)
+on (1/5) between (00:00..23:59) use (0.8612, 100)
+on (7/8) between (00:00..23:59) use (0.8612, 100)
+on (8/25) between (00:00..23:59) use (0.8612, 100)
+on (12/25) between (00:00..23:59) use (0.8612, 100)
diff --git a/kppp/Rules/Uruguay/Makefile.am b/kppp/Rules/Uruguay/Makefile.am
new file mode 100644
index 00000000..8c2b55cb
--- /dev/null
+++ b/kppp/Rules/Uruguay/Makefile.am
@@ -0,0 +1,5 @@
+pkg_DATA = Adinet_cIVA.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Uruguay
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/Yugoslavia/041_1xx_xxx.rst b/kppp/Rules/Yugoslavia/041_1xx_xxx.rst
new file mode 100644
index 00000000..cf59c819
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_1xx_xxx.rst
@@ -0,0 +1,64 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 1xx xxx) tarifni interval je 1 sekunde
+# u jakom i 2 sekunde u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_1xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,1)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 1)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 2)
+on (monday..friday) between (15:00..17:00) use (0.3644, 2)
+on (monday..friday) between (21:00..23:59) use (0.3644, 2)
+on (saturday) between (21:00..23:59) use (0.3644, 2)
+on (sunday) between () use (0.3644, 2)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+#on (01/01, 02/01, 07/01, 27/03, 28/4, 01/05, 02/05) between () use (0.3644, 2)
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 2)
diff --git a/kppp/Rules/Yugoslavia/041_2xx_xxx.rst b/kppp/Rules/Yugoslavia/041_2xx_xxx.rst
new file mode 100644
index 00000000..8f70a537
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_2xx_xxx.rst
@@ -0,0 +1,63 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 2xx xxx) tarifni interval je 1 sekunde
+# u jakom i 2 sekunde u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_2xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,1)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 1)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 2)
+on (monday..friday) between (15:00..17:00) use (0.3644, 2)
+on (monday..friday) between (21:00..23:59) use (0.3644, 2)
+on (saturday) between (21:00..23:59) use (0.3644, 2)
+on (sunday) between () use (0.3644, 2)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 2)
diff --git a/kppp/Rules/Yugoslavia/041_3xx_xxx.rst b/kppp/Rules/Yugoslavia/041_3xx_xxx.rst
new file mode 100644
index 00000000..7b1d60c0
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_3xx_xxx.rst
@@ -0,0 +1,63 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 3xx xxx) tarifni interval je 2 sekunde
+# u jakom i 4 sekunde u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_3xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,2)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 2)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 4)
+on (monday..friday) between (15:00..17:00) use (0.3644, 4)
+on (monday..friday) between (21:00..23:59) use (0.3644, 4)
+on (saturday) between (21:00..23:59) use (0.3644, 4)
+on (sunday) between () use (0.3644, 4)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 4)
diff --git a/kppp/Rules/Yugoslavia/041_4xx_xxx.rst b/kppp/Rules/Yugoslavia/041_4xx_xxx.rst
new file mode 100644
index 00000000..0d776edc
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_4xx_xxx.rst
@@ -0,0 +1,63 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 4xx xxx) tarifni interval je 3 sekunde
+# u jakom i 6 sekundi u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_4xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,3)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 3)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 6)
+on (monday..friday) between (15:00..17:00) use (0.3644, 6)
+on (monday..friday) between (21:00..23:59) use (0.3644, 6)
+on (saturday) between (21:00..23:59) use (0.3644, 6)
+on (sunday) between () use (0.3644, 6)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 6)
diff --git a/kppp/Rules/Yugoslavia/041_5xx_xxx.rst b/kppp/Rules/Yugoslavia/041_5xx_xxx.rst
new file mode 100644
index 00000000..180b38af
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_5xx_xxx.rst
@@ -0,0 +1,63 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 5xx xxx) tarifni interval je 15 sekundi
+# u jakom i 30 sekundi u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_5xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,15)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 15)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 30)
+on (monday..friday) between (15:00..17:00) use (0.3644, 30)
+on (monday..friday) between (21:00..23:59) use (0.3644, 30)
+on (saturday) between (21:00..23:59) use (0.3644, 30)
+on (sunday) between () use (0.3644, 30)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 30)
diff --git a/kppp/Rules/Yugoslavia/041_9xx_xxx.rst b/kppp/Rules/Yugoslavia/041_9xx_xxx.rst
new file mode 100644
index 00000000..3ae93b40
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/041_9xx_xxx.rst
@@ -0,0 +1,63 @@
+################################################################
+# POSEBAN SAOBRACAJ
+# Telefonski brojevi sa negeografskim kodom (041)
+# sa prvom cifrom 9 (041 9xx xxx) tarifni interval je 8 sekundi
+# u jakom i 16 sekundi u slabom saobraæaju
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=041_9xx_xxx.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,8)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 8)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+# on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 16)
+on (monday..friday) between (15:00..17:00) use (0.3644, 16)
+on (monday..friday) between (21:00..23:59) use (0.3644, 16)
+on (saturday) between (21:00..23:59) use (0.3644, 16)
+on (sunday) between () use (0.3644, 16)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 16)
diff --git a/kppp/Rules/Yugoslavia/III_zona-preko_200km.rst b/kppp/Rules/Yugoslavia/III_zona-preko_200km.rst
new file mode 100644
index 00000000..b6fb0edb
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/III_zona-preko_200km.rst
@@ -0,0 +1,65 @@
+################################################################
+# III ZONA
+# Obuhvata telefonske razgovore izmeðu mre¾nih grupa meðusobn
+# o udaljenih preko 200 km impulsni interval u jakom saobraæaju
+# je 8 sekundi, a u slabom 16 sekundi
+# Primer (za fizièka lica): 1 minut u jakom saobraæaju:
+# 7,5 impulsa x 0,3644 = 2,73 din
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=III_zona-preko_200km.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,8)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 8)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 16)
+on (monday..friday) between (15:00..17:00) use (0.3644, 16)
+on (monday..friday) between (21:00..23:59) use (0.3644, 16)
+on (saturday) between (21:00..23:59) use (0.3644, 16)
+on (sunday) between () use (0.3644, 16)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 16)
diff --git a/kppp/Rules/Yugoslavia/II_zona-do_200km.rst b/kppp/Rules/Yugoslavia/II_zona-do_200km.rst
new file mode 100644
index 00000000..37f97ffe
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/II_zona-do_200km.rst
@@ -0,0 +1,65 @@
+################################################################
+# II ZONA
+# Obuhvata telefonske razgovore izmeðu mre¾nih grupa meðusobno
+# udaljenih do 200 km impulsni interval u jakom saobraæaju je
+# 15 sekundi, a u slabom 15 sekundi.
+# Primer (za fizièka lica): 1 minut u jakom saobraæaju:
+# 4 impulsa x 0,3644 = 1,46 din
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=II_zona-do_200km.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,15)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 15)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 30)
+on (monday..friday) between (15:00..17:00) use (0.3644, 30)
+on (monday..friday) between (21:00..23:59) use (0.3644, 30)
+on (saturday) between (21:00..23:59) use (0.3644, 30)
+on (sunday) between () use (0.3644, 30)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 30)
diff --git a/kppp/Rules/Yugoslavia/I_zona-ista_mreza.rst b/kppp/Rules/Yugoslavia/I_zona-ista_mreza.rst
new file mode 100644
index 00000000..cbb5e268
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/I_zona-ista_mreza.rst
@@ -0,0 +1,65 @@
+################################################################
+# I ZONA
+# Obuhvata telefonske razgovore u okviru iste mre¾ne grupe
+# impulsni interval u jakom saobraæaju je 30 sekundi, a u
+# slabom 60 sekundi.
+# Primer (za fizièka lica): 1 minut u jakom saobraæaju: 2 impulsa
+# x 0,3644 = 0,73 din
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=I_zona-ista_mreza.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,30)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 30)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 60)
+on (monday..friday) between (15:00..17:00) use (0.3644, 60)
+on (monday..friday) between (21:00..23:59) use (0.3644, 60)
+on (saturday) between (21:00..23:59) use (0.3644, 60)
+on (sunday) between () use (0.3644, 60)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 60)
diff --git a/kppp/Rules/Yugoslavia/Lokalni_poziv.rst b/kppp/Rules/Yugoslavia/Lokalni_poziv.rst
new file mode 100644
index 00000000..858fd25c
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/Lokalni_poziv.rst
@@ -0,0 +1,80 @@
+################################################################
+# LOKALNI SAOBRACAJ
+# Obuhvata razgovore obavljene u okviru mesne mre¾e.
+# Ovi razgovori tarifiraju se impulsnim intervalom od 2 minuta
+# u jakom saobraæaju i impulsnim intervalom od 4 minuta u period
+# u slabog saobraæaja.
+#
+# JAK SAOBRACAJ
+# Podrazumeva razgovore obavljene u vremenskom periodu
+# od 07-15 h i od 17-21 h
+#
+# SLAB SAOBRACAJ
+# * Podrazumeva razgovore obavljene u vremenskom periodu od
+# 15-17 h i od 21-07 h narednog dana
+# * Razgovore obavljene vikendom:
+# od subote u 21 h do ponedeljka do 07 h
+# * Razgovore obavljene u dane dr¾avnih praznika
+#
+# CENA IMPULSA
+# za fizièka lica iznosi 0,3644 din
+# za pravna lica iznosi 0,6023 din.
+#
+################################################################
+#
+# NAME OF THE RULESET. This is NEEDED for accounting purposes.
+#
+################################################################
+name=Lokalni_poziv.rst
+
+################################################################
+# currency settings
+################################################################
+
+currency_symbol=DIN
+currency_position=right
+currency_digits=2
+
+################################################################
+# connection settings
+################################################################
+
+per_connection=0.0
+minimum_costs=0.0
+
+# You pay .74 for the first 180 secons ( 3minutes) no matter
+# whether you are connected for 1 second or 180 seconds.
+# This rule will take priority during the first 180 seconds
+# over any other rule, in particular the 'default' rule.
+# have a look at costgraphs.gif in the docs directory
+# of the kppp distribution for a graphic illustration.
+
+flat_init_costs=(0.3644,120)
+
+# This is the default rule which is used when no other rule
+# applies. The first component "0.1" is the price of one
+# "unit", while "72" is the duration in seconds.
+# Therefore the following rule means: "Every 72 seconds 0.1
+# ATS are added to the bill"
+
+default=(0.3644, 120)
+
+# more complicated rules:
+
+# "on monday until sunday from 12:00 am until 11:59 pm the costs
+# are 0.2 each 72 seconds"
+#on () between () use (0.2, 2)
+
+on (monday..friday) between (0:00..7:00) use (0.3644, 240)
+on (monday..friday) between (15:00..16:17) use (0.3644, 240)
+on (monday..friday) between (21:00..23:59) use (0.3644, 240)
+on (saturday) between (21:00..23:59) use (0.3644, 240)
+on (sunday) between () use (0.3644, 240)
+
+# Praznici
+# 1.1, 2.1 Nova Godina
+# 7.1. Bozic
+# 27.3. SRJ
+# 28.4. Srbija
+# 1.5, 2.5 Prvi Maj
+on (01.01, 02.01, 07.01, 27.03, 28.4, 01.05, 02.05) between () use (0.3644, 240)
diff --git a/kppp/Rules/Yugoslavia/Makefile.am b/kppp/Rules/Yugoslavia/Makefile.am
new file mode 100644
index 00000000..26f103fd
--- /dev/null
+++ b/kppp/Rules/Yugoslavia/Makefile.am
@@ -0,0 +1,8 @@
+pkg_DATA = 041_1xx_xxx.rst 041_5xx_xxx.rst II_zona-do_200km.rst \
+ 041_2xx_xxx.rst 041_9xx_xxx.rst I_zona-ista_mreza.rst \
+ 041_3xx_xxx.rst Lokalni_poziv.rst 041_4xx_xxx.rst \
+ III_zona-preko_200km.rst
+
+pkgdir = $(kde_datadir)/kppp/Rules/Yugoslavia
+
+EXTRA_DIST = $(pkg_DATA)
diff --git a/kppp/Rules/checkrules b/kppp/Rules/checkrules
new file mode 100755
index 00000000..9e695d5b
--- /dev/null
+++ b/kppp/Rules/checkrules
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+KPPP=`which kppp` || (echo "cannot find kppp"; exit 1)
+
+cd `dirname $0 2> /dev/null` || (echo "\"dirname\" required"; exit 1)
+
+FILESTOCHECK=`find -name "*.rst" -type f 2> /dev/null` || \
+ (echo "\"find\" required\""; exit 1)
+
+for i in ${FILESTOCHECK}
+do
+ echo -n "checking $i ..."
+ if ${KPPP} -r $i > /dev/null 2>&1
+ then
+ echo ok
+ else
+ echo "failed"
+ echo -n " "
+ ${KPPP} -r $i
+ #exit 1
+ fi
+done
+exit 0
+
diff --git a/kppp/TODO b/kppp/TODO
new file mode 100644
index 00000000..2fbad962
--- /dev/null
+++ b/kppp/TODO
@@ -0,0 +1,25 @@
+TODO
+====
+o modem-setup wizard
+o provider-setup wizard
+o Create an own dialer communicating with slots. This will allow non-X kppp´s
+o Separate GUI and non-GUI code in different classes
+o Enhance "Send" to allow special (control) characters
+o version detection of pppd fails on debian (rwsr-x---). Allow manual conf.
+
+Dropped
+=======
+o MS-CHAP support (since this relies on pppd, we cannot do it)
+o separate setup and dialer (very difficult, both parts would require to be
+ be SUID)
+
+Done
+====
+o signal catching for accounting
+o help users with those "pppd died unexpected..." bugs
+o CHAP support
+o All modem related code should be put in one class
+o replace "$" in config entries with "$$"
+o button for clearing volume accounting
+o better FAQ (it´s better now, still not perfect, and will never be)
+o deal with interfaces different from ppp0
diff --git a/kppp/accounting.cpp b/kppp/accounting.cpp
new file mode 100644
index 00000000..36751190
--- /dev/null
+++ b/kppp/accounting.cpp
@@ -0,0 +1,477 @@
+/* -*- C++ -*-
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file contributed by: Mario Weilguni, <mweilguni@sime.com>
+ * Thanks Mario!
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <qdir.h>
+
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <time.h>
+
+#include "accounting.h"
+#include "pppdata.h"
+#include "pppstats.h"
+
+// defines the maximum duration until the current costs
+// are saved again (to prevent loss due to "kill")
+// specifying -1 disables the features
+#define UPDATE_TIME (5*60*1000)
+
+extern PPPData gpppdata;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Helper functions
+//
+/////////////////////////////////////////////////////////////////////////////
+static QString timet2qstring(time_t t) {
+ QString s;
+
+ s.sprintf("%lu", t);
+ return s;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// The base class for the accounting system provides a base set of usefull
+// and common functions, but does not do any accounting by itself. The
+// accounting is accomplished withing it's derived classes
+//
+/////////////////////////////////////////////////////////////////////////////
+AccountingBase::AccountingBase(QObject *parent) :
+ QObject(parent),
+ _total(0),
+ _session(0)
+{
+ QDate dt = QDate::currentDate();
+ LogFileName = QString("%1-%2.log")
+ .arg(QDate::shortMonthName(dt.month()))
+ .arg(dt.year(), 4);
+
+ LogFileName = KGlobal::dirs()->saveLocation("appdata", "Log")
+ + "/" + LogFileName;
+
+ kdDebug(5002) << "LogFileName: " << LogFileName << endl;
+}
+
+AccountingBase::~AccountingBase() {
+ if(running())
+ slotStop();
+}
+
+
+double AccountingBase::total() const {
+ return _total + _session;
+}
+
+
+
+double AccountingBase::session() const {
+ return _session;
+}
+
+
+// set costs back to zero ( typically once per month)
+void AccountingBase::resetCosts(const QString & accountname){
+ QString prev_account = gpppdata.accname();
+
+ gpppdata.setAccount(accountname);
+ gpppdata.setTotalCosts("");
+
+ gpppdata.setAccount(prev_account);
+}
+
+
+void AccountingBase::resetVolume(const QString & accountname){
+ QString prev_account = gpppdata.accname();
+
+ gpppdata.setAccount(accountname);
+ gpppdata.setTotalBytes(0);
+
+ gpppdata.setAccount(prev_account);
+}
+
+
+void AccountingBase::logMessage(QString s, bool newline) {
+ int old_umask = umask(0077);
+
+ QFile f(LogFileName);
+
+ bool result = f.open(IO_ReadWrite);
+ if(result) {
+ // move to eof, and place \n if necessary
+ if(f.size() > 0) {
+ if(newline) {
+ f.at(f.size());
+ char c = 0;
+ f.readBlock(&c, 1);
+ if(c != '\n')
+ f.writeBlock("\n", 1);
+ } else
+ f.at(f.size());
+ }
+
+ QCString tmp = s.local8Bit();
+ f.writeBlock(tmp, tmp.length());
+ f.close();
+ }
+
+ // restore umask
+ umask(old_umask);
+}
+
+
+QString AccountingBase::getCosts(const QString & accountname) {
+ QString prev_account = gpppdata.accname();
+
+ gpppdata.setAccount(accountname);
+ QString val = gpppdata.totalCosts();
+ // ### currency from rule file
+ // QString val = KGlobal::locale()->formatMoney(gpppdata.totalCosts().toDouble(), currency);
+
+ gpppdata.setAccount(prev_account);
+
+ return val;
+}
+
+
+
+bool AccountingBase::saveCosts() {
+ if(!_name.isNull() && _name.length() > 0) {
+ QString val;
+ val.setNum(total());
+
+ gpppdata.setTotalCosts(val);
+ gpppdata.save();
+
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+
+bool AccountingBase::loadCosts() {
+ QString val = gpppdata.totalCosts();
+
+ if(val.isNull()) // QString will segfault if isnull and toDouble called
+ _total = 0.0;
+ else {
+ bool ok;
+ _total = val.toDouble(&ok);
+ if(!ok)
+ _total = 0.0;
+ }
+
+ return TRUE;
+}
+
+
+QString AccountingBase::getAccountingFile(const QString &accountname) {
+ QString f = "kppp/Rules/";
+ f += accountname;
+ QString d = locate("data", f);
+
+ if(d.isNull())
+ return "";
+ else
+ return d;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accounting class for ruleset files
+//
+/////////////////////////////////////////////////////////////////////////////
+Accounting::Accounting(QObject *parent, PPPStats *st) :
+ AccountingBase(parent),
+ acct_timer_id(0),
+ update_timer_id(0),
+ stats(st)
+{
+}
+
+
+bool Accounting::running() const {
+ return (bool)(acct_timer_id != 0);
+}
+
+
+void Accounting::timerEvent(QTimerEvent *t) {
+ if(t->timerId() == acct_timer_id) {
+
+ double newCosts;
+ double newLen;
+ double connect_time = difftime(time(0), start_time);
+
+ rules.getActiveRule(QDateTime::currentDateTime(), connect_time, newCosts, newLen);
+ if(newLen < 1) { // changed to < 1
+ slotStop();
+ return; // no default rule found
+ }
+
+ // check if we have a new rule. If yes,
+ // kill the timer and restart it with new
+ // duration
+ if((newCosts != _lastcosts) || (newLen != _lastlen)) {
+
+ kdDebug(5002).form("SWITCHING RULES, new costs = %0.2f, new len = %0.2f\n",
+ newCosts, newLen);
+
+ killTimer(acct_timer_id);
+ if(newLen > 0)
+ acct_timer_id = startTimer((int)(newLen * 1000.0));
+
+ _lastlen = newLen;
+ _lastcosts = newCosts;
+ }
+
+ // emit changed() signal if necessary
+ if(newCosts != 0) {
+ _session += newCosts;
+ emit changed(rules.currencyString(total()),
+ rules.currencyString(session()));
+
+
+ }
+ } // if(t->timerId() == acct_timer_id)...
+
+ if(t->timerId() == update_timer_id) {
+ // just to be sure, save the current costs
+ // every n seconds (see UPDATE_TIME)
+ saveCosts();
+ }
+}
+
+
+void Accounting::slotStart() {
+ if(!running()) {
+ loadCosts();
+ _lastcosts = 0.0;
+ _lastlen = 0.0;
+ _session = rules.perConnectionCosts();
+ rules.setStartTime(QDateTime::currentDateTime());
+ acct_timer_id = startTimer(1);
+ if(UPDATE_TIME > 0)
+ update_timer_id = startTimer(UPDATE_TIME);
+
+ start_time = time(0);
+ QString s;
+ s = timet2qstring(start_time);
+ s += ":";
+ s += gpppdata.accname();
+ s += ":";
+ s += rules.currencySymbol();
+
+ logMessage(s, TRUE);
+ }
+}
+
+
+void Accounting::slotStop() {
+ if(running()) {
+ killTimer(acct_timer_id);
+ if(update_timer_id != 0)
+ killTimer(update_timer_id);
+ acct_timer_id = 0;
+ update_timer_id = 0;
+
+ QString s;
+ s.sprintf(":%s:%0.4e:%0.4e:%u:%u\n",
+ timet2qstring(time(0)).utf8().data(),
+ session(),
+ total(),
+ stats->ibytes,
+ stats->obytes);
+
+ logMessage(s, FALSE);
+ saveCosts();
+ }
+}
+
+
+bool Accounting::loadRuleSet(const QString & name) {
+
+ if (name.isEmpty()) {
+ rules.load(""); // delete old rules
+ return TRUE;
+ }
+
+ QString d = AccountingBase::getAccountingFile(name);
+
+ QFileInfo fg(d);
+ if(fg.exists()) {
+ int ret = rules.load(d);
+ _name = rules.name();
+ return (bool)(ret == 0);
+ }
+
+ return FALSE;
+}
+
+
+double Accounting::total() const {
+ if(rules.minimumCosts() <= _session)
+ return _total + _session;
+ else
+ return _total + rules.minimumCosts();
+}
+
+
+
+double Accounting::session() const {
+ if(rules.minimumCosts() <= _session)
+ return _session;
+ else
+ return rules.minimumCosts();
+}
+
+
+
+
+ExecutableAccounting::ExecutableAccounting(PPPStats *st, QObject *parent) :
+ AccountingBase(parent),
+ proc(0),
+ stats(st)
+{
+}
+
+
+bool ExecutableAccounting::running() const {
+ return (proc != 0) || proc->isRunning();
+}
+
+
+bool ExecutableAccounting::loadRuleSet(const QString &) {
+ QString s = AccountingBase::getAccountingFile(gpppdata.accountingFile());
+ return (access(QFile::encodeName(s), X_OK) == 0);
+}
+
+
+void ExecutableAccounting::gotData(KProcess */*proc*/, char *buffer, int /*buflen*/) {
+ QString field[8];
+ int nFields = 0;
+ int pos, last_pos = 0;
+
+ // split string
+ QString b(buffer);
+ pos = b.find(':');
+ while(pos != -1 && nFields < 8) {
+ field[nFields++] = b.mid(last_pos, pos-last_pos);
+ last_pos = pos+1;
+ pos = b.find(':', last_pos);
+ }
+
+ for(int i = 0; i < nFields;i++)
+ fprintf(stderr, "FIELD[%d] = %s\n", i, field[i].local8Bit().data());
+
+ QString __total, __session;
+ QString s(buffer);
+ int del1, del2, del3;
+
+ del1 = s.find(':');
+ del2 = s.find(':', del1+1);
+ del3 = s.find(':', del2+1);
+ if(del1 == -1 || del2 == -1 || del3 == -1) {
+ // TODO: do something usefull here
+ return;
+ }
+
+ provider = s.left(del1);
+ currency = s.mid(del1, del2-del1);
+ __total = s.mid(del2, del2-del1);
+ __session = s.mid(del3, s.length()-del3+1);
+
+ bool ok1, ok2;
+ _total = __total.toDouble(&ok1);
+ _session = __session.toDouble(&ok2);
+
+ if(!ok1 || !ok2) {
+ // TODO: do something usefull here
+ return;
+ }
+
+ printf("PROVIDER=%s, CURRENCY=%s, TOTAL=%0.3e, SESSION=%0.3e\n",
+ provider.local8Bit().data(),
+ currency.local8Bit().data(),
+ _total,
+ _session);
+}
+
+
+void ExecutableAccounting::slotStart() {
+ if(proc != 0)
+ slotStop(); // just to make sure
+
+ loadCosts();
+ QString s = AccountingBase::getAccountingFile(gpppdata.accountingFile());
+ proc = new KProcess;
+
+ QString s_total;
+ s_total.sprintf("%0.8f", total());
+ *proc << s << s_total;
+ connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(gotData(KProcess *, char *, int)));
+ proc->start();
+
+ time_t start_time = time(0);
+ s = timet2qstring(start_time);
+ s += ":";
+ s += gpppdata.accname();
+ s += ":";
+ s += currency;
+
+ logMessage(s, TRUE);
+}
+
+
+void ExecutableAccounting::slotStop() {
+ if(proc != 0) {
+ proc->kill();
+ delete proc;
+ proc = 0;
+
+ QString s;
+ s.sprintf(":%s:%0.4e:%0.4e:%u:%u\n",
+ timet2qstring(time(0)).local8Bit().data(),
+ session(),
+ total(),
+ stats->ibytes,
+ stats->obytes);
+
+ logMessage(s, FALSE);
+ saveCosts();
+ }
+}
+
+#include "accounting.moc"
+
diff --git a/kppp/accounting.h b/kppp/accounting.h
new file mode 100644
index 00000000..2d3a299c
--- /dev/null
+++ b/kppp/accounting.h
@@ -0,0 +1,144 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ * This file contributed by: Mario Weilguni, <mweilguni@sime.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ACCOUNTING__H__
+#define __ACCOUNTING__H__
+
+#include <qobject.h>
+#include <kprocess.h>
+#include "ruleset.h"
+
+class PPPStats;
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accounting base class
+//
+/////////////////////////////////////////////////////////////////////////////
+class AccountingBase : public QObject {
+ Q_OBJECT
+public:
+ AccountingBase(QObject *parent);
+ virtual ~AccountingBase();
+
+ virtual double total() const;
+ virtual double session() const;
+
+ virtual bool running() const { return false; };
+ virtual bool loadRuleSet(const QString & name) = 0;
+
+public slots:
+ virtual void slotStart() {};
+ virtual void slotStop() {};
+
+signals:
+ void changed(QString total, QString session);
+
+protected:
+ void logMessage(QString, bool = FALSE);
+ bool saveCosts();
+ bool loadCosts();
+
+ QString LogFileName;
+ double _total, _session;
+ QString _name;
+
+ // static members
+public:
+ static void resetCosts(const QString & accountname);
+ static void resetVolume(const QString & accountname);
+ static QString getCosts(const QString & accountname);
+ static QString getAccountingFile(const QString &accountname);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accounting based on ruleset files
+//
+/////////////////////////////////////////////////////////////////////////////
+class Accounting : public AccountingBase {
+ Q_OBJECT
+public:
+ Accounting(QObject *parent, PPPStats *st);
+
+ virtual double total() const;
+ virtual double session() const;
+
+ virtual bool loadRuleSet(const QString & name);
+ virtual bool running() const;
+
+private:
+ virtual void timerEvent(QTimerEvent *t);
+
+public slots:
+ virtual void slotStart();
+ virtual void slotStop();
+
+signals:
+ void changed(QString total, QString session);
+
+private:
+ int acct_timer_id;
+ int update_timer_id;
+ time_t start_time;
+ double _lastcosts;
+ double _lastlen;
+ RuleSet rules;
+ PPPStats *stats;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accounting based on executable files
+//
+/////////////////////////////////////////////////////////////////////////////
+class ExecutableAccounting : public AccountingBase {
+ Q_OBJECT
+public:
+ ExecutableAccounting(PPPStats *st, QObject *parent = 0);
+
+ virtual bool loadRuleSet(const QString & );
+ virtual bool running() const;
+
+public slots:
+ virtual void slotStart();
+ virtual void slotStop();
+
+private slots:
+ void gotData(KProcess *proc, char *buffer, int buflen);
+
+signals:
+ void changed(QString total, QString session);
+
+private:
+ KProcess *proc;
+ QString currency;
+ QString provider;
+ PPPStats *stats;
+};
+
+#endif
diff --git a/kppp/accounts.cpp b/kppp/accounts.cpp
new file mode 100644
index 00000000..be5a5996
--- /dev/null
+++ b/kppp/accounts.cpp
@@ -0,0 +1,484 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qdir.h>
+#include <stdlib.h>
+#include <qlayout.h>
+#include <qtabdialog.h>
+#include <qwhatsthis.h>
+#include <qmessagebox.h>
+
+#include <kapplication.h>
+#include <kbuttonbox.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kwin.h>
+#include <kdialogbase.h>
+#include <kstdguiitem.h>
+#include <qvgroupbox.h>
+
+#include <errno.h>
+
+#include "pppdata.h"
+#include "accounts.h"
+#include "accounting.h"
+#include "providerdb.h"
+#include "edit.h"
+
+void parseargs(char* buf, char** args);
+
+AccountWidget::AccountWidget( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ int min = 0;
+ QVBoxLayout *l1 = new QVBoxLayout(parent, 10, 10);
+
+ // add a hbox
+ QHBoxLayout *l11 = new QHBoxLayout;
+ l1->addLayout(l11);
+
+ accountlist_l = new QListBox(parent);
+ accountlist_l->setMinimumSize(160, 128);
+ connect(accountlist_l, SIGNAL(highlighted(int)),
+ this, SLOT(slotListBoxSelect(int)));
+ connect(accountlist_l, SIGNAL(selected(int)),
+ this, SLOT(editaccount()));
+ l11->addWidget(accountlist_l, 10);
+
+ QVBoxLayout *l111 = new QVBoxLayout;
+ l11->addLayout(l111, 1);
+ edit_b = new QPushButton(i18n("&Edit..."), parent);
+ connect(edit_b, SIGNAL(clicked()), SLOT(editaccount()));
+ QWhatsThis::add(edit_b, i18n("Allows you to modify the selected account"));
+
+ min = edit_b->sizeHint().width();
+ min = QMAX(70,min);
+ edit_b->setMinimumWidth(min);
+
+ l111->addWidget(edit_b);
+
+ new_b = new QPushButton(i18n("&New..."), parent);
+ connect(new_b, SIGNAL(clicked()), SLOT(newaccount()));
+ l111->addWidget(new_b);
+ QWhatsThis::add(new_b, i18n("Create a new dialup connection\n"
+ "to the Internet"));
+
+ copy_b = new QPushButton(i18n("Co&py"), parent);
+ connect(copy_b, SIGNAL(clicked()), SLOT(copyaccount()));
+ l111->addWidget(copy_b);
+ QWhatsThis::add(copy_b,
+ i18n("Makes a copy of the selected account. All\n"
+ "settings of the selected account are copied\n"
+ "to a new account that you can modify to fit your\n"
+ "needs"));
+
+ delete_b = new QPushButton(i18n("De&lete"), parent);
+ connect(delete_b, SIGNAL(clicked()), SLOT(deleteaccount()));
+ l111->addWidget(delete_b);
+ QWhatsThis::add(delete_b,
+ i18n("<p>Deletes the selected account\n\n"
+ "<font color=\"red\"><b>Use with care!</b></font>"));
+
+ QHBoxLayout *l12 = new QHBoxLayout;
+ l1->addStretch(1);
+ l1->addLayout(l12);
+
+ QVBoxLayout *l121 = new QVBoxLayout;
+ l12->addLayout(l121);
+ l121->addStretch(1);
+ costlabel = new QLabel(i18n("Phone costs:"), parent);
+ costlabel->setEnabled(FALSE);
+ l121->addWidget(costlabel);
+
+ costedit = new QLineEdit(parent);
+ costedit->setFocusPolicy(QWidget::NoFocus);
+ costedit->setFixedHeight(costedit->sizeHint().height());
+ costedit->setEnabled(FALSE);
+ l121->addWidget(costedit);
+ l121->addStretch(1);
+ QString tmp = i18n("<p>This shows the accumulated phone costs\n"
+ "for the selected account.\n"
+ "\n"
+ "<b>Important</b>: If you have more than one \n"
+ "account - beware, this is <b>NOT</b> the sum \n"
+ "of the phone costs of all your accounts!");
+ QWhatsThis::add(costlabel, tmp);
+ QWhatsThis::add(costedit, tmp);
+
+ vollabel = new QLabel(i18n("Volume:"), parent);
+ vollabel->setEnabled(FALSE);
+ l121->addWidget(vollabel);
+
+ voledit = new QLineEdit(parent,"voledit");
+ voledit->setFocusPolicy(QWidget::NoFocus);
+ voledit->setFixedHeight(voledit->sizeHint().height());
+ voledit->setEnabled(FALSE);
+ l121->addWidget(voledit);
+ tmp = i18n("<p>This shows the number of bytes transferred\n"
+ "for the selected account (not for all of your\n"
+ "accounts. You can select what to display in\n"
+ "the accounting dialog.\n"
+ "\n"
+ "<a href=\"#volaccounting\">More on volume accounting</a>");
+
+ QWhatsThis::add(vollabel,tmp);
+ QWhatsThis::add(voledit, tmp);
+
+ QVBoxLayout *l122 = new QVBoxLayout;
+ l12->addStretch(1);
+ l12->addLayout(l122);
+
+ l122->addStretch(1);
+ reset = new QPushButton(i18n("&Reset..."), parent);
+ reset->setEnabled(FALSE);
+ connect(reset, SIGNAL(clicked()),
+ this, SLOT(resetClicked()));
+ l122->addWidget(reset);
+
+ log = new QPushButton(i18n("&View Logs"), parent);
+ connect(log, SIGNAL(clicked()),
+ this, SLOT(viewLogClicked()));
+ l122->addWidget(log);
+ l122->addStretch(1);
+
+ //load up account list from gppdata to the list box
+ if(gpppdata.accountCount() > 0) {
+ for(int i=0; i <= gpppdata.accountCount()-1; i++) {
+ gpppdata.setAccountByIndex(i);
+ accountlist_l->insertItem(gpppdata.accname());
+ }
+ }
+
+ slotListBoxSelect(accountlist_l->currentItem());
+
+ l1->activate();
+}
+
+
+
+void AccountWidget::slotListBoxSelect(int idx) {
+ delete_b->setEnabled((bool)(idx != -1));
+ edit_b->setEnabled((bool)(idx != -1));
+ copy_b->setEnabled((bool)(idx != -1));
+ if(idx!=-1) {
+ QString account = gpppdata.accname();
+ gpppdata.setAccountByIndex(accountlist_l->currentItem());
+ reset->setEnabled(TRUE);
+ costlabel->setEnabled(TRUE);
+ costedit->setEnabled(TRUE);
+ costedit->setText(AccountingBase::getCosts(accountlist_l->text(accountlist_l->currentItem())));
+
+ vollabel->setEnabled(TRUE);
+ voledit->setEnabled(TRUE);
+ int bytes = gpppdata.totalBytes();
+ voledit->setText(prettyPrintVolume(bytes));
+ gpppdata.setAccount(account);
+ } else{
+ reset->setEnabled(FALSE);
+ costlabel->setEnabled(FALSE);
+ costedit->setText("");
+ costedit->setEnabled(FALSE);
+ vollabel->setEnabled(FALSE);
+ voledit->setText("");
+ voledit->setEnabled(FALSE);
+ }
+}
+
+
+void AccountWidget::viewLogClicked(){
+
+ QApplication::flushX();
+ if(fork() == 0) {
+ if (setgid(getgid()) < 0 && errno != EPERM)
+ _exit(2);
+ setuid(getuid());
+ if( geteuid() != getuid() )
+ _exit(1);
+ // TODO: use execvp
+ system("kppplogview -kppp");
+ _exit(0);
+ }
+}
+
+
+void AccountWidget::resetClicked(){
+ if(accountlist_l->currentItem() == -1)
+ return;
+
+ QueryReset dlg(this);
+ int what = dlg.exec();
+
+ if((what & QueryReset::COSTS)) {
+ emit resetCosts(accountlist_l->text(accountlist_l->currentItem()));
+ costedit->setText("0");
+ }
+
+ if((what & QueryReset::VOLUME)) {
+ emit resetVolume(accountlist_l->text(accountlist_l->currentItem()));
+ voledit->setText(prettyPrintVolume(0));
+ }
+}
+
+
+void AccountWidget::editaccount() {
+ gpppdata.setAccount(accountlist_l->text(accountlist_l->currentItem()));
+
+ int result = doTab();
+
+ if(result == QDialog::Accepted) {
+ accountlist_l->changeItem(gpppdata.accname(),accountlist_l->currentItem());
+ emit resetaccounts();
+ gpppdata.save();
+ }
+}
+
+
+void AccountWidget::newaccount() {
+ if(accountlist_l->count() == MAX_ACCOUNTS) {
+ KMessageBox::sorry(this, i18n("Maximum number of accounts reached."));
+ return;
+ }
+
+ int result;
+ int query = KMessageBox::questionYesNoCancel(this,
+ i18n("Do you want to use the wizard to create the new account or the "
+ "standard, dialog-based setup?\n"
+ "The wizard is easier and sufficient in most cases. If you need "
+ "very special settings, you might want to try the standard, "
+ "dialog-based setup."),
+ i18n("Create New Account"),
+ i18n("&Wizard"), i18n("&Manual Setup"));
+
+ switch(query) {
+ case KMessageBox::Yes:
+ {
+ if (gpppdata.newaccount() == -1)
+ return;
+ ProviderDB pdb(this);
+ result = pdb.exec();
+ break;
+ }
+ case KMessageBox::No:
+ if (gpppdata.newaccount() == -1)
+ return;
+ result = doTab();
+ break;
+ default:
+ return;
+ }
+
+ if(result == QDialog::Accepted) {
+ accountlist_l->insertItem(gpppdata.accname());
+ accountlist_l->setSelected(accountlist_l->findItem(gpppdata.accname()),
+ true);
+ emit resetaccounts();
+ gpppdata.save();
+ } else
+ gpppdata.deleteAccount();
+}
+
+
+void AccountWidget::copyaccount() {
+ if(accountlist_l->count() == MAX_ACCOUNTS) {
+ KMessageBox::sorry(this, i18n("Maximum number of accounts reached."));
+ return;
+ }
+
+ if(accountlist_l->currentItem()<0) {
+ KMessageBox::sorry(this, i18n("No account selected."));
+ return;
+ }
+
+ gpppdata.copyaccount(accountlist_l->currentItem());
+
+ accountlist_l->insertItem(gpppdata.accname());
+ emit resetaccounts();
+ gpppdata.save();
+}
+
+
+void AccountWidget::deleteaccount() {
+
+ QString s = i18n("Are you sure you want to delete\nthe account \"%1\"?")
+ .arg(accountlist_l->text(accountlist_l->currentItem()));
+
+ if(KMessageBox::warningYesNo(this, s, i18n("Confirm"), KGuiItem(i18n("Delete"), "editdelete"), KStdGuiItem::cancel()) != KMessageBox::Yes)
+ return;
+
+ if(gpppdata.deleteAccount(accountlist_l->text(accountlist_l->currentItem())))
+ accountlist_l->removeItem(accountlist_l->currentItem());
+
+ emit resetaccounts();
+ gpppdata.save();
+
+ slotListBoxSelect(accountlist_l->currentItem());
+
+}
+
+
+int AccountWidget::doTab(){
+ tabWindow = new KDialogBase( KDialogBase::Tabbed, QString::null,
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok,
+ 0, 0, true);
+ KWin::setIcons(tabWindow->winId(), kapp->icon(), kapp->miniIcon());
+ bool isnewaccount;
+
+ if(gpppdata.accname().isEmpty()) {
+ tabWindow->setCaption(i18n("New Account"));
+ isnewaccount = true;
+ } else {
+ QString tit = i18n("Edit Account: ");
+ tit += gpppdata.accname();
+ tabWindow->setCaption(tit);
+ isnewaccount = false;
+ }
+
+ dial_w = new DialWidget(tabWindow->addPage(i18n("Dial"), i18n("Dial Setup")), isnewaccount);
+ ip_w = new IPWidget(tabWindow->addPage(i18n("IP"), i18n("IP Setup")), isnewaccount);
+ gateway_w = new GatewayWidget(tabWindow->addPage(i18n("Gateway"), i18n("Gateway Setup")), isnewaccount);
+ dns_w = new DNSWidget(tabWindow->addPage(i18n("DNS"), i18n("DNS Servers")), isnewaccount);
+ script_w = new ScriptWidget(tabWindow->addPage(i18n("Login Script"), i18n("Edit Login Script")), isnewaccount);
+ ExecWidget *exec_w = new ExecWidget(tabWindow->addPage(i18n("Execute"), i18n("Execute Programs")), isnewaccount);
+ acct = new AccountingSelector(tabWindow->addPage(i18n("Accounting")), isnewaccount);
+
+ int result = 0;
+ bool ok = false;
+ while (!ok){
+
+ result = tabWindow->exec();
+ ok = true;
+
+ if(result == QDialog::Accepted) {
+ if (script_w->check()) {
+ if(dial_w->save()) {
+ ip_w->save();
+ dns_w->save();
+ gateway_w->save();
+ script_w->save();
+ exec_w->save();
+ acct->save();
+ } else {
+ // ### add: "and valid"
+ KMessageBox::error(this, i18n( "You must enter a unique\n"
+ "account name"));
+ ok = false;
+ }
+ } else {
+ KMessageBox::error(this, i18n("Login script has unbalanced "
+ "loop Start/End"));
+ ok = false;
+ }
+ }
+ }
+
+ delete tabWindow;
+ return result;
+}
+
+
+QString AccountWidget::prettyPrintVolume(unsigned int n) {
+ int idx = 0;
+ const QString quant[] = {i18n("Byte"), i18n("KB"),
+ i18n("MB"), i18n("GB"), QString::null};
+
+ float n1 = n;
+ while(n >= 1024 && !quant[idx].isNull()) {
+ idx++;
+ n /= 1024;
+ }
+
+ int i = idx;
+ while(i--)
+ n1 = n1 / 1024.0;
+
+ QString s = KGlobal::locale()->formatNumber( n1, idx==0 ? 0 : 1 );
+ s += " " + quant[idx];
+ return s;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Queries the user what to reset: costs, volume or both
+//
+/////////////////////////////////////////////////////////////////////////////
+QueryReset::QueryReset(QWidget *parent) : QDialog(parent, 0, true) {
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+ setCaption(i18n("Reset Accounting"));
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QVGroupBox *f = new QVGroupBox(i18n("What to Reset"), this);
+
+ QVBoxLayout *l1 = new QVBoxLayout(parent, 10, 10);
+ costs = new QCheckBox(i18n("Reset the accumulated p&hone costs"), f);
+ costs->setChecked(true);
+ l1->addWidget(costs);
+ QWhatsThis::add(costs, i18n("Check this to set the phone costs\n"
+ "to zero. Typically you will want to\n"
+ "do this once a month."));
+
+ volume = new QCheckBox(i18n("Reset &volume accounting"), f);
+ volume->setChecked(true);
+ l1->addWidget(volume);
+ QWhatsThis::add(volume, i18n("Check this to set the volume accounting\n"
+ "to zero. Typically you will want to do this\n"
+ "once a month."));
+
+ l1->activate();
+
+ // this activates the f-layout and sets minimumSize()
+ f->show();
+
+ tl->addWidget(f);
+
+ KButtonBox *bbox = new KButtonBox(this);
+ bbox->addStretch(1);
+ QPushButton *ok = bbox->addButton(KStdGuiItem::ok());
+ ok->setDefault(true);
+ QPushButton *cancel = bbox->addButton(KStdGuiItem::cancel());
+
+ connect(ok, SIGNAL(clicked()),
+ this, SLOT(accepted()));
+ connect(cancel, SIGNAL(clicked()),
+ this, SLOT(reject()));
+
+ bbox->layout();
+ tl->addWidget(bbox);
+
+ // TODO: activate if KGroupBox is fixed
+ // setFixedSize(sizeHint());
+}
+
+
+void QueryReset::accepted() {
+ int result = costs->isChecked() ? COSTS : 0;
+ result += volume->isChecked() ? VOLUME : 0;
+
+ done(result);
+}
+
+#include "accounts.moc"
diff --git a/kppp/accounts.h b/kppp/accounts.h
new file mode 100644
index 00000000..fe290f85
--- /dev/null
+++ b/kppp/accounts.h
@@ -0,0 +1,109 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _ACCOUNTS_H_
+#define _ACCOUNTS_H_
+
+#include <qwidget.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include "acctselect.h"
+
+class KDialogBase;
+class QCheckBox;
+class QLineEdit;
+class QTabDialog;
+class DialWidget;
+class ScriptWidget;
+class IPWidget;
+class DNSWidget;
+class GatewayWidget;
+
+class AccountWidget : public QWidget {
+ Q_OBJECT
+public:
+ AccountWidget( QWidget *parent=0, const char *name=0 );
+ ~AccountWidget() {}
+
+private slots:
+ void editaccount();
+ void copyaccount();
+ void newaccount();
+ void deleteaccount();
+ void slotListBoxSelect(int);
+ void resetClicked();
+ void viewLogClicked();
+
+private:
+ int doTab();
+
+signals:
+ void resetaccounts();
+ void resetCosts(const QString &);
+ void resetVolume(const QString &);
+
+private:
+ QString prettyPrintVolume(unsigned int);
+
+ KDialogBase *tabWindow;
+ DialWidget *dial_w;
+ AccountingSelector *acct;
+ IPWidget *ip_w;
+ DNSWidget *dns_w;
+ GatewayWidget *gateway_w;
+ ScriptWidget *script_w;
+
+ QPushButton *reset;
+ QPushButton *log;
+ QLabel *costlabel;
+ QLineEdit *costedit;
+ QLabel *vollabel;
+ QLineEdit *voledit;
+
+ QListBox *accountlist_l;
+ QPushButton *edit_b;
+ QPushButton *copy_b;
+ QPushButton *new_b;
+ QPushButton *delete_b;
+};
+
+
+class QueryReset : public QDialog {
+ Q_OBJECT
+public:
+ QueryReset(QWidget *parent);
+
+ enum {COSTS=1, VOLUME=2};
+
+private slots:
+ void accepted();
+
+private:
+ QCheckBox *costs, *volume;
+};
+
+#endif
+
diff --git a/kppp/acctselect.cpp b/kppp/acctselect.cpp
new file mode 100644
index 00000000..4fa16605
--- /dev/null
+++ b/kppp/acctselect.cpp
@@ -0,0 +1,330 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <kurllabel.h>
+#include <qlayout.h>
+#include <qlistview.h>
+#include <qdir.h>
+#include <qregexp.h>
+#include <qwmatrix.h>
+#include <qcheckbox.h>
+#include <kdialog.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <krun.h>
+
+#include "acctselect.h"
+#include "pppdata.h"
+
+
+AccountingSelector::AccountingSelector(QWidget *parent, bool _isnewaccount, const char *name)
+ : QWidget(parent, name),
+ isnewaccount(_isnewaccount)
+{
+ QVBoxLayout *l1 = new QVBoxLayout(parent, 0, KDialog::spacingHint());
+
+ enable_accounting = new QCheckBox(i18n("&Enable accounting"), parent);
+ l1->addWidget(enable_accounting, 1);
+ connect(enable_accounting, SIGNAL(toggled(bool)), this, SLOT(enableItems(bool)));
+
+ // insert the tree widget
+ tl = new QListView(parent, "treewidget");
+
+ connect(tl, SIGNAL(selectionChanged(QListViewItem*)), this,
+ SLOT(slotSelectionChanged(QListViewItem*)));
+ tl->setMinimumSize(220, 200);
+ l1->addWidget(tl, 1);
+
+ KURLLabel *up = new KURLLabel(parent);
+ up->setText(i18n("Check for rule updates"));
+ up->setURL("http://developer.kde.org/~kppp/rules.html");
+ connect(up, SIGNAL(leftClickedURL(const QString&)), SLOT(openURL(const QString&)));
+
+ l1->addWidget(up, 1);
+
+ // label to display the currently selected ruleset
+ QHBoxLayout *l11 = new QHBoxLayout;
+ l1->addSpacing(10);
+ l1->addLayout(l11);
+ QLabel *lsel = new QLabel(i18n("Selected:"), parent);
+ selected = new QLabel(parent);
+ selected->setFrameStyle(QFrame::Sunken | QFrame::WinPanel);
+ selected->setLineWidth(1);
+ selected->setFixedHeight(selected->sizeHint().height() + 16);
+ l11->addWidget(lsel, 0);
+ l11->addSpacing(10);
+ l11->addWidget(selected, 1);
+
+ // volume accounting
+ l1->addStretch(1);
+ QHBoxLayout *l12 = new QHBoxLayout;
+ l1->addLayout(l12);
+ QLabel *usevol_l = new QLabel(i18n("Volume accounting:"), parent);
+ use_vol = new QComboBox(parent);
+ use_vol->insertItem(i18n("No Accounting"), 0);
+ use_vol->insertItem(i18n("Bytes In"), 1);
+ use_vol->insertItem(i18n("Bytes Out"), 2);
+ use_vol->insertItem(i18n("Bytes In & Out"), 3);
+ use_vol->setCurrentItem(gpppdata.VolAcctEnabled());
+ l12->addWidget(usevol_l);
+ l12->addWidget(use_vol);
+
+ // load the pmfolder pixmap from KDEdir
+ pmfolder = UserIcon("folder");
+
+ // scale the pixmap
+ if(pmfolder.width() > 0) {
+ QWMatrix wm;
+ wm.scale(16.0/pmfolder.width(), 16.0/pmfolder.width());
+ pmfolder = pmfolder.xForm(wm);
+ }
+
+ // load the pmfolder pixmap from KDEdir
+ pmfile = UserIcon("phone");
+
+ // scale the pixmap
+ if(pmfile.width() > 0) {
+ QWMatrix wm;
+ wm.scale(16.0/pmfile.width(), 16.0/pmfile.width());
+ pmfile = pmfile.xForm(wm);
+ }
+
+ enable_accounting->setChecked(gpppdata.AcctEnabled());
+
+ setupTreeWidget();
+
+ l1->activate();
+}
+
+
+QString AccountingSelector::fileNameToName(QString s) {
+
+ s.replace('_', " ");
+ return KURL::decode_string(s);
+
+}
+
+
+QString AccountingSelector::nameToFileName(QString s) {
+
+ s.replace(' ', "_");
+ return s;
+
+}
+
+QListViewItem *AccountingSelector::findByName(QString name)
+{
+ QListViewItem *ch = tl->firstChild();
+ while(ch) {
+ if(ch->text(0) == name)
+ return ch;
+ ch = ch->nextSibling();
+ }
+ return 0;
+}
+
+
+void AccountingSelector::insertDir(QDir d, QListViewItem *root) {
+
+ QListViewItem* tli = 0;
+
+ // sanity check
+ if(!d.exists() || !d.isReadable())
+ return;
+
+
+ // set up filter
+ d.setNameFilter("*.rst");
+ d.setFilter(QDir::Files);
+ d.setSorting(QDir::Name);
+
+ // read the list of files
+ const QFileInfoList *list = d.entryInfoList();
+ QFileInfoListIterator it( *list );
+ QFileInfo *fi;
+
+ // traverse the list and insert into the widget
+ while((fi = it.current())) {
+ ++it;
+
+ QString samename = fi->fileName();
+
+ QListViewItem *i = findByName(samename);
+
+ // skip this file if already in tree
+ if(i)
+ continue;
+
+ // check if this is the file we should mark
+ QString name = fileNameToName(fi->baseName(true));
+ if(root)
+ tli = new QListViewItem(root, name);
+ else
+ tli = new QListViewItem(tl, name);
+
+ tli->setPixmap(0, pmfile);
+
+ // check if this is the item we are searching for
+ // (only in "Edit"-mode, not in "New"-mode
+ if(!isnewaccount && !edit_s.isEmpty() &&
+ (edit_s == QString(fi->filePath()).right(edit_s.length()))) {
+ edit_item = tli;
+ }
+ }
+
+ // set up a filter for the directories
+ d.setFilter(QDir::Dirs);
+ d.setNameFilter("*");
+ const QFileInfoList *dlist = d.entryInfoList();
+ QFileInfoListIterator dit(*dlist);
+
+ while((fi = dit.current())) {
+ // skip "." and ".." directories
+ if(fi->fileName().left(1) != ".") {
+ // convert to human-readable name
+ QString name = fileNameToName(fi->fileName());
+
+ // if the tree already has an item with this name,
+ // skip creation and use this one, otherwise
+ // create a new entry
+ QListViewItem *i = findByName(name);
+ if(!i) {
+ QListViewItem* item;
+
+ if(root)
+ item = new QListViewItem(root, name);
+ else
+ item = new QListViewItem(tl, name);
+
+ item->setPixmap(0, pmfolder);
+
+ insertDir(QDir(fi->filePath()), item);
+ } else
+ insertDir(QDir(fi->filePath()), i);
+ }
+ ++dit;
+ }
+}
+
+
+void AccountingSelector::setupTreeWidget() {
+ // search the item
+ edit_item = 0;
+ if(!isnewaccount) {
+ edit_s = gpppdata.accountingFile();
+ }
+ else
+ edit_s = "";
+
+ tl->addColumn( i18n("Available Rules") );
+ tl->setColumnWidth(0, 205);
+ tl->setRootIsDecorated(true);
+
+ // look in ~/.kde/share/apps/kppp/Rules and $KDEDIR/share/apps/kppp/Rules
+ QStringList dirs = KGlobal::dirs()->resourceDirs("appdata");
+ for (QStringList::ConstIterator it = dirs.begin();
+ it != dirs.end(); it++) {
+ insertDir(QDir((*it) + "Rules"), 0);
+ }
+
+ // when mode is "Edit", then hightlight the
+ // appropriate item
+ if(!isnewaccount && edit_item) {
+ tl->setSelected(edit_item, true);
+ tl->setOpen(edit_item->parent(), true);
+ tl->ensureItemVisible(edit_item);
+ }
+
+ enableItems(enable_accounting->isChecked());
+}
+
+
+void AccountingSelector::enableItems(bool enabled) {
+
+ tl->setEnabled(enabled);
+
+ if(!enabled || (!tl->currentItem()))
+ selected->setText(i18n("(none)"));
+ else {
+ QListViewItem* i = tl->currentItem();
+ QString s;
+ while(i) {
+ s = "/" + i->text(0) + s;
+ i = i->parent();
+ }
+ selected->setText(s.mid(1));
+
+ s += ".rst";
+ edit_s = nameToFileName(s);
+ }
+}
+
+
+void AccountingSelector::slotSelectionChanged(QListViewItem* i) {
+
+ if(!i || i->childCount())
+ return;
+
+ if(!enable_accounting->isChecked())
+ return;
+
+ enableItems(true);
+}
+
+
+bool AccountingSelector::save() {
+
+ if(enable_accounting->isChecked()) {
+ gpppdata.setAccountingFile(edit_s);
+ gpppdata.setAcctEnabled(true);
+ } else {
+ gpppdata.setAccountingFile("");
+ gpppdata.setAcctEnabled(false);
+ }
+
+ gpppdata.setVolAcctEnabled(use_vol->currentItem());
+
+ return true;
+}
+
+void AccountingSelector::openURL(const QString &url) {
+ new KRun( KURL( url ) );
+}
+
+#include "acctselect.moc"
+
diff --git a/kppp/acctselect.h b/kppp/acctselect.h
new file mode 100644
index 00000000..3e48199b
--- /dev/null
+++ b/kppp/acctselect.h
@@ -0,0 +1,81 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __ACCTSELECT__H__
+#define __ACCTSELECT__H__
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qdir.h>
+
+class QCheckBox;
+class QComboBox;
+class QLabel;
+class QListView;
+class QListViewItem;
+
+class AccountingSelector : public QWidget {
+ Q_OBJECT
+public:
+ AccountingSelector(QWidget *parent = 0, bool _isnewaccount = false, const char *name = 0);
+ ~AccountingSelector() {}
+
+ bool save();
+
+private:
+ QListViewItem *findByName(QString name);
+ void setupTreeWidget();
+ void insertDir(QDir, QListViewItem * = 0);
+ QString fileNameToName(QString);
+ QString nameToFileName(QString);
+
+private slots:
+ void openURL(const QString &);
+ void enableItems(bool);
+ void slotSelectionChanged(QListViewItem* i);
+
+private:
+ QCheckBox *enable_accounting;
+ QListView *tl;
+ QComboBox *use_vol;
+ QPixmap pmfolder, pmfile;
+ QLabel *selected;
+
+ QListViewItem *edit_item;
+ QString edit_s;
+ bool isnewaccount;
+};
+
+#endif
+
diff --git a/kppp/auth.h b/kppp/auth.h
new file mode 100644
index 00000000..aef26946
--- /dev/null
+++ b/kppp/auth.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Mario Weilguni <mweilguni@sime.com>
+ * Thanks Mario!
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __AUTH__H__
+#define __AUTH__H__
+
+#define AUTH_SCRIPT 0
+#define AUTH_PAP 1
+#define AUTH_TERMINAL 2
+#define AUTH_CHAP 3
+#define AUTH_PAPCHAP 4
+
+#endif
diff --git a/kppp/configure.in.in b/kppp/configure.in.in
new file mode 100644
index 00000000..867d5f07
--- /dev/null
+++ b/kppp/configure.in.in
@@ -0,0 +1,53 @@
+AC_MSG_CHECKING(if kppp's headers are installed)
+# this is a little piece of code out of kppp. If it doesn't compile,
+# kppp is not good for this system. It doesn't do anything, but enough
+# to fail ;)
+AC_LANG_SAVE
+AC_LANG_CPLUSPLUS
+AC_TRY_LINK([
+#if defined(__osf__) || defined(__svr4__)
+ #define _POSIX_PII_SOCKET
+ extern "C" int sethostname(char *name, int name_len);
+#endif
+#include <unistd.h>
+#include <arpa/inet.h>
+#ifdef __DragonFly__
+#include <net/ppp_layer/ppp_defs.h>
+#else
+#include <net/ppp_defs.h>
+#endif
+#include <netinet/in.h>
+
+#ifdef __svr4__
+ #include <sys/stropts.h>
+ #include <net/pppio.h> /* SVR4, Solaris 2, etc. */
+
+#else
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <net/if.h>
+
+ #ifndef STREAMS
+
+ #if defined(linux)
+ #include <linux/if_ppp.h>
+ #elif defined(__DragonFly__)
+ #include <net/ppp/if_ppp.h>
+ #else
+ #include <net/if_ppp.h> /* BSD, NeXT, etc. */
+ #endif
+
+ #else /* SunOS 4, AIX 4, OSF/1, etc. */
+ #include <sys/stream.h>
+ #include <net/ppp_str.h>
+ #endif
+#endif
+],[
+ sethostname("", 1); /* never run this program! :*/
+],
+[ AC_MSG_RESULT(yes) ],
+[ DO_NOT_COMPILE="$DO_NOT_COMPILE kppp"
+ AC_MSG_RESULT(no)
+])
+AC_LANG_RESTORE
+AC_CHECK_HEADERS(sys/param.h string.h)
diff --git a/kppp/connect.cpp b/kppp/connect.cpp
new file mode 100644
index 00000000..1203f4ba
--- /dev/null
+++ b/kppp/connect.cpp
@@ -0,0 +1,1535 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ * Copyright (C) 1998-2001 Harri Porten <porten@kde.org>
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <qlayout.h>
+#include <qregexp.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+
+#ifdef _XPG4_2
+#define __xnet_connect connect
+#endif
+
+#include <errno.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef __linux__
+#include "runtests.h"
+#endif
+
+#include "auth.h"
+#include "connect.h"
+#include "docking.h"
+#include "main.h"
+#include "modem.h"
+#include "kpppconfig.h"
+#include "pppdata.h"
+#include "pppstats.h"
+#include "requester.h"
+#include "utils.h"
+
+extern KPPPWidget *p_kppp;
+
+QString old_hostname;
+bool modified_hostname;
+
+
+ConnectWidget::ConnectWidget(QWidget *parent, const char *name, PPPStats *st)
+ : QWidget(parent, name),
+ // initialize some important variables
+ myreadbuffer(""),
+ main_timer_ID(0),
+ vmain(0),
+ substate(-1),
+ scriptindex(0),
+ loopnest(0),
+ loopend(false),
+ semaphore(false),
+ expecting(false),
+ readbuffer(""),
+ scanvar(""),
+ scanning(false),
+ pausing(false),
+ termwindow(0),
+ dialnumber(0),
+ stats(st)
+{
+ modified_hostname = false;
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 8, 10);
+ QString tit = i18n("Connecting to: ");
+ setCaption(tit);
+
+ QHBoxLayout *l0 = new QHBoxLayout(10);
+ tl->addLayout(l0);
+ l0->addSpacing(10);
+ messg = new QLabel(this, "messg");
+ messg->setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ messg->setAlignment(AlignCenter);
+ messg->setText(i18n("Unable to create modem lock file."));
+ messg->setMinimumHeight(messg->sizeHint().height() + 5);
+ int messw = (messg->sizeHint().width() * 12) / 10;
+ messw = QMAX(messw,280);
+ messg->setMinimumWidth(messw);
+ messg->setText(i18n("Looking for modem..."));
+ l0->addWidget(messg);
+ l0->addSpacing(10);
+
+ QHBoxLayout *l1 = new QHBoxLayout(10);
+ tl->addLayout(l1);
+ l1->addStretch(1);
+
+ debug = new QPushButton(i18n("&Log"), this);
+ debug->setToggleButton(true);
+ connect(debug, SIGNAL(clicked()), SIGNAL(toggleDebugWindow()));
+
+ cancel = new KPushButton(KStdGuiItem::cancel(), this);
+ cancel->setFocus();
+ connect(cancel, SIGNAL(clicked()), SLOT(cancelbutton()));
+
+ int maxw = QMAX(cancel->sizeHint().width(),
+ debug->sizeHint().width());
+ maxw = QMAX(maxw,65);
+ debug->setFixedWidth(maxw);
+ cancel->setFixedWidth(maxw);
+ l1->addWidget(debug);
+ l1->addWidget(cancel);
+ l1->addSpacing(10);
+
+ setFixedSize(sizeHint());
+
+ pausetimer = new QTimer(this);
+ connect(pausetimer, SIGNAL(timeout()), SLOT(pause()));
+
+ kapp->processEvents();
+
+ timeout_timer = new QTimer(this);
+ connect(timeout_timer, SIGNAL(timeout()), SLOT(script_timed_out()));
+
+ inittimer = new QTimer(this);
+ connect(inittimer, SIGNAL(timeout()), SLOT(init()));
+
+ if_timeout_timer = new QTimer(this);
+ connect(if_timeout_timer, SIGNAL(timeout()), SLOT(if_waiting_timed_out()));
+
+ connect(this,SIGNAL(if_waiting_signal()),this,SLOT(if_waiting_slot()));
+
+ prompt = new PWEntry( this, "pw" );
+ if_timer = new QTimer(this);
+ connect(if_timer,SIGNAL(timeout()), SLOT(if_waiting_slot()));
+}
+
+
+ConnectWidget::~ConnectWidget() {
+}
+
+
+void ConnectWidget::disableButtons()
+{
+ debug->setEnabled(false);
+ cancel->setEnabled(false);
+}
+
+void ConnectWidget::enableButtons()
+{
+ debug->setEnabled(true);
+ cancel->setEnabled(true);
+}
+
+void ConnectWidget::preinit() {
+ // this is all just to keep the GUI nice and snappy ....
+ // you have to see to believe ...
+ messg->setText(i18n("Looking for modem..."));
+ inittimer->start(100);
+}
+
+void ConnectWidget::init() {
+ gpppdata.setpppdError(0);
+ inittimer->stop();
+ vmain = 0;
+ substate = -1;
+ expecting = false;
+ pausing = false;
+ scriptindex = 0;
+ myreadbuffer = "";
+ scanning = false;
+ scanvar = "";
+ firstrunID = true;
+ firstrunPW = true;
+ stats->totalbytes = 0;
+ dialnumber = 0;
+
+ p_kppp->con_speed = "";
+
+ p_kppp->setQuitOnDisconnect (p_kppp->quitOnDisconnect() || gpppdata.quit_on_disconnect());
+
+ comlist = &gpppdata.scriptType();
+ arglist = &gpppdata.script();
+
+ QString tit = i18n("Connecting to: %1").arg(gpppdata.accname());
+ setCaption(tit);
+
+ kapp->processEvents();
+
+ // signal other applications that we are about to get connected
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "aboutToConnect()", QByteArray());
+
+ // run the "before-connect" command
+ if (!gpppdata.command_before_connect().isEmpty()) {
+ messg->setText(i18n("Running pre-startup command..."));
+ emit debugMessage(i18n("Running pre-startup command..."));
+
+ kapp->processEvents();
+ QApplication::flushX();
+ pid_t id = execute_command(gpppdata.command_before_connect());
+ int i, status;
+
+ do {
+ kapp->processEvents();
+ i = waitpid(id, &status, WNOHANG);
+ usleep(100000);
+ } while (i == 0 && errno == 0);
+ }
+
+ int lock = Modem::modem->lockdevice();
+
+ if (lock == 1) {
+ messg->setText(i18n("Modem device is locked."));
+ vmain = 20; // wait until cancel is pressed
+ return;
+ }
+
+ if (lock == -1) {
+ messg->setText(i18n("Unable to create modem lock file."));
+ vmain = 20; // wait until cancel is pressed
+ return;
+ }
+
+ if(Modem::modem->opentty()) {
+ messg->setText(Modem::modem->modemMessage());
+ kapp->processEvents();
+ if(Modem::modem->hangup()) {
+
+ kapp->processEvents();
+
+ semaphore = false;
+
+ Modem::modem->stop();
+ Modem::modem->notify(this, SLOT(readChar(unsigned char)));
+
+ // if we are stuck anywhere we will time out
+ timeout_timer->start(gpppdata.modemTimeout()*1000);
+
+ // this timer will run the script etc.
+ main_timer_ID = startTimer(10);
+
+ return;
+ }
+ }
+
+ // initialization failed
+ messg->setText(Modem::modem->modemMessage());
+ vmain = 20; // wait until cancel is pressed
+ Modem::modem->unlockdevice();
+}
+
+
+void ConnectWidget::timerEvent(QTimerEvent *) {
+ if (semaphore || pausing)
+ return;
+
+ if(vmain == 0) {
+#ifdef DEBUG_WO_DIALING
+ vmain = 10;
+ return;
+#endif
+
+ assert(PPPData::NumInitStrings > 0);
+ // first init string ?
+ if(substate == -1) {
+ messg->setText(i18n("Initializing modem..."));
+ emit debugMessage(i18n("Initializing modem..."));
+ substate = 0;
+ }
+
+ QString initStr = gpppdata.modemInitStr(substate);
+ if (!initStr.isEmpty()) {
+ // send a carriage return and then wait a bit so that the modem will
+ // let us issue commands.
+ if(gpppdata.modemPreInitDelay() > 0) {
+ usleep(gpppdata.modemPreInitDelay() * 5000);
+ writeline("");
+ usleep(gpppdata.modemPreInitDelay() * 5000);
+ }
+ setExpect(gpppdata.modemInitResp());
+ writeline(initStr);
+ usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
+ }
+
+ substate++;
+
+ /*
+ * FIXME after 3.0: Make it possible to disable ATS11 since it
+ * seems to be incompatible with some ISDN adapters (e.g. DataBox
+ * Speed Dragon). Even better would be to detect this when doing
+ * a "Modem Query"
+ */
+ if (MODEM_TONEDURATION != gpppdata.modemToneDuration())
+ vmain = 5;
+ else
+ vmain = 3;
+
+ return;
+ }
+
+ if (vmain == 5) {
+ if(!expecting) {
+ QString sToneDuration = "ATS11=" + QString::number(gpppdata.modemToneDuration());
+ QString msg = i18n("Setting ") + sToneDuration;
+ messg->setText(msg);
+ emit debugMessage(msg);
+ setExpect(gpppdata.modemInitResp());
+ writeline(sToneDuration);
+ }
+ vmain = 3;
+ return;
+ }
+
+ if(vmain == 3) {
+ if(!expecting) {
+ // done with all init strings ?
+ if(substate < PPPData::NumInitStrings) {
+ vmain = 0;
+ return;
+ }
+ substate = -1;
+ // skip setting the volume if command is empty
+ if(gpppdata.volumeInitString().isEmpty()) {
+ vmain = 4;
+ return;
+ }
+ messg->setText(i18n("Setting speaker volume..."));
+ emit debugMessage(i18n("Setting speaker volume..."));
+
+ setExpect(gpppdata.modemInitResp());
+ QString vol("AT");
+ vol += gpppdata.volumeInitString();
+ writeline(vol);
+ usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
+ vmain = 4;
+ return;
+ }
+ }
+
+ if(vmain == 4) {
+ if(!expecting) {
+ if(!gpppdata.waitForDialTone() || gpppdata.waitCallback()) {
+ QString msg = i18n("Turning off dial tone waiting...");
+ messg->setText(msg);
+ emit debugMessage(msg);
+ setExpect(gpppdata.modemInitResp());
+ writeline(gpppdata.modemNoDialToneDetectionStr());
+ }
+ vmain = 1;
+ return;
+ }
+ }
+
+ // dial the number and wait to connect
+ if(vmain == 1) {
+ if(!expecting) {
+
+ timeout_timer->stop();
+ timeout_timer->start(gpppdata.modemTimeout()*1000);
+
+ if(gpppdata.waitCallback()) {
+ QString msg = i18n("Waiting for callback...");
+ messg->setText(msg);
+ emit debugMessage(msg);
+ setExpect(gpppdata.modemRingResp());
+ vmain = 102;
+ return;
+ }
+
+ QStringList &plist = gpppdata.phonenumbers();
+ QString bmarg= gpppdata.dialPrefix();
+ bmarg += *plist.at(dialnumber);
+ QString bm = i18n("Dialing %1").arg(bmarg);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ QString pn = gpppdata.modemDialStr();
+ pn += gpppdata.dialPrefix();
+ pn += *plist.at(dialnumber);
+ if(++dialnumber >= plist.count())
+ dialnumber = 0;
+ writeline(pn);
+
+ setExpect(gpppdata.modemConnectResp());
+ vmain = 100;
+ return;
+ }
+ }
+
+ // wait for connect, but redial if BUSY or wait for user cancel
+ // if NO CARRIER or NO DIALTONE
+ if(vmain == 100) {
+ if(!expecting) {
+ myreadbuffer = gpppdata.modemConnectResp();
+ setExpect("\n");
+ vmain = 101;
+ return;
+ }
+
+ if(readbuffer.contains(gpppdata.modemBusyResp())) {
+ timeout_timer->stop();
+ timeout_timer->start(gpppdata.modemTimeout()*1000);
+
+ messg->setText(i18n("Line busy. Hanging up..."));
+ emit debugPutChar('\n');
+ Modem::modem->hangup();
+
+ if(gpppdata.busyWait() > 0) {
+ QString bm = i18n("Line busy. Waiting: %1 seconds").arg(gpppdata.busyWait());
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ pausing = true;
+
+ pausetimer->start(gpppdata.busyWait()*1000, true);
+ timeout_timer->stop();
+ }
+
+ Modem::modem->setDataMode(false);
+ vmain = 0;
+ substate = -1;
+ gpppdata.setWaitCallback(false);
+ return;
+ }
+
+ if(readbuffer.contains(gpppdata.modemNoDialtoneResp())) {
+ timeout_timer->stop();
+
+ messg->setText(i18n("No Dial Tone"));
+ vmain = 20;
+ Modem::modem->unlockdevice();
+ gpppdata.setWaitCallback(false);
+ return;
+ }
+
+ if(readbuffer.contains(gpppdata.modemNoCarrierResp())) {
+ if (gpppdata.get_redial_on_nocarrier()) {
+ timeout_timer->stop();
+ timeout_timer->start(gpppdata.modemTimeout()*1000);
+
+ if(gpppdata.busyWait() > 0) {
+ QString bm = i18n("No carrier. Waiting: %1 seconds").arg(gpppdata.busyWait());
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ pausing = true;
+
+ pausetimer->start(gpppdata.busyWait()*1000, true);
+ timeout_timer->stop();
+ }
+
+ Modem::modem->setDataMode(false);
+ vmain = 0;
+ substate = -1;
+ return;
+ } else {
+ timeout_timer->stop();
+
+ messg->setText(i18n("No Carrier"));
+ vmain = 20;
+ Modem::modem->unlockdevice();
+ gpppdata.setWaitCallback(false);
+ }
+ return;
+ }
+
+ if(readbuffer.contains(gpppdata.modemDLPResp())) {
+ timeout_timer->stop();
+
+ messg->setText(i18n("Digital Line Protection Detected."));
+ vmain = 20;
+ Modem::modem->unlockdevice();
+ KMessageBox::error(this,
+ i18n("A Digital Line Protection (DLP) error response "
+ "has been detected.\n"
+ "Please disconnect the phone line.\n\n"
+ "Do NOT connect this modem to a digital phone "
+ "line or the modem could get permanently "
+ "damaged"));
+ gpppdata.setWaitCallback(false);
+ return;
+ }
+
+
+ }
+
+ // wait for newline after CONNECT response (so we get the speed)
+ if(vmain == 101) {
+ if(!expecting) {
+ Modem::modem->setDataMode(true); // modem will no longer respond to AT commands
+
+ emit startAccounting();
+ p_kppp->con_win->startClock();
+
+ vmain = 2;
+ scriptTimeout=gpppdata.modemTimeout()*1000;
+ return;
+ }
+ }
+
+ // send answer on callback phase
+ if(vmain == 102) {
+ if(!expecting) {
+ writeline(gpppdata.modemAnswerStr());
+ setExpect(gpppdata.modemConnectResp());
+ vmain = 100;
+ return;
+ }
+ }
+
+ // execute the script
+ if(vmain == 2) {
+ if(!expecting && !pausing && !scanning) {
+
+ timeout_timer->stop();
+ timeout_timer->start(scriptTimeout);
+
+ if((unsigned) scriptindex < comlist->count()) {
+ scriptCommand = *(comlist->at(scriptindex));
+ scriptArgument = *(arglist->at(scriptindex));
+ } else {
+ kdDebug(5002) << "End of script" << endl;
+ vmain = 10;
+ return;
+ }
+
+ if (scriptCommand == "Scan") {
+ QString bm = i18n("Scanning %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ setScan(scriptArgument);
+ scriptindex++;
+ return;
+ }
+
+ if (scriptCommand == "Save") {
+ QString bm = i18n("Saving %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ if (scriptArgument.lower() == "password") {
+ gpppdata.setPassword(scanvar);
+ p_kppp->setPW_Edit(scanvar);
+ if(gpppdata.storePassword())
+ gpppdata.setStoredPassword(scanvar);
+ firstrunPW = true;
+ }
+
+ scriptindex++;
+ return;
+ }
+
+
+ if (scriptCommand == "Send" || scriptCommand == "SendNoEcho") {
+ QString bm = i18n("Sending %1");
+
+ // replace %USERNAME% and %PASSWORD%
+ QString arg = scriptArgument;
+ QRegExp re1("%USERNAME%");
+ QRegExp re2("%PASSWORD%");
+ arg = arg.replace(re1, gpppdata.storedUsername());
+ arg = arg.replace(re2, gpppdata.storedPassword());
+
+ if (scriptCommand == "Send")
+ bm = bm.arg(scriptArgument);
+ else {
+ for(uint i = 0; i < scriptArgument.length(); i++)
+ bm = bm.arg("*");
+ }
+
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ writeline(scriptArgument);
+ scriptindex++;
+ return;
+ }
+
+ if (scriptCommand == "Expect") {
+ QString bm = i18n("Expecting %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ // The incrementing of the scriptindex MUST be before the
+ // call to setExpect otherwise the expect will miss a string that is
+ // already in the buffer.
+ scriptindex++;
+ setExpect(scriptArgument);
+ return;
+ }
+
+
+ if (scriptCommand == "Pause") {
+ QString bm = i18n("Pause %1 seconds").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ pausing = true;
+
+ pausetimer->start(scriptArgument.toInt()*1000, true);
+ timeout_timer->stop();
+
+ scriptindex++;
+ return;
+ }
+
+ if (scriptCommand == "Timeout") {
+
+ timeout_timer->stop();
+
+ QString bm = i18n("Timeout %1 seconds").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ scriptTimeout=scriptArgument.toInt()*1000;
+ timeout_timer->start(scriptTimeout);
+
+ scriptindex++;
+ return;
+ }
+
+ if (scriptCommand == "Hangup") {
+ messg->setText(i18n("Hangup"));
+ emit debugMessage(i18n("Hangup"));
+
+ writeline(gpppdata.modemHangupStr());
+ setExpect(gpppdata.modemHangupResp());
+
+ scriptindex++;
+ return;
+ }
+
+ if (scriptCommand == "Answer") {
+
+ timeout_timer->stop();
+
+ messg->setText(i18n("Answer"));
+ emit debugMessage(i18n("Answer"));
+
+ setExpect(gpppdata.modemRingResp());
+ vmain = 150;
+ return;
+ }
+
+ if (scriptCommand == "ID") {
+ QString bm = i18n("ID %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ QString idstring = gpppdata.storedUsername();
+
+ if(!idstring.isEmpty() && firstrunID) {
+ // the user entered an Id on the main kppp dialog
+ writeline(idstring);
+ firstrunID = false;
+ scriptindex++;
+ }
+ else {
+ // the user didn't enter and Id on the main kppp dialog
+ // let's query for an ID
+ /* if not around yet, then post window... */
+ if (prompt->Consumed()) {
+ if (!(prompt->isVisible())) {
+ prompt->setPrompt(scriptArgument);
+ prompt->setEchoModeNormal();
+ prompt->show();
+ }
+ } else {
+ /* if prompt withdrawn ... then, */
+ if(!(prompt->isVisible())) {
+ writeline(prompt->text());
+ prompt->setConsumed();
+ scriptindex++;
+ return;
+ }
+ /* replace timeout value */
+ }
+ }
+ }
+
+ if (scriptCommand == "Password") {
+ QString bm = i18n("Password %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ QString pwstring = gpppdata.password();
+
+ if(!pwstring.isEmpty() && firstrunPW) {
+ // the user entered a password on the main kppp dialog
+ writeline(pwstring);
+ firstrunPW = false;
+ scriptindex++;
+ }
+ else {
+ // the user didn't enter a password on the main kppp dialog
+ // let's query for a password
+ /* if not around yet, then post window... */
+ if (prompt->Consumed()) {
+ if (!(prompt->isVisible())) {
+ prompt->setPrompt(scriptArgument);
+ prompt->setEchoModePassword();
+ prompt->show();
+ }
+ } else {
+ /* if prompt withdrawn ... then, */
+ if(!(prompt->isVisible())) {
+ p_kppp->setPW_Edit(prompt->text());
+ writeline(prompt->text());
+ prompt->setConsumed();
+ scriptindex++;
+ return;
+ }
+ /* replace timeout value */
+ }
+ }
+ }
+
+ if (scriptCommand == "Prompt") {
+ QString bm = i18n("Prompting %1");
+
+ // if the scriptindex (aka the prompt text) includes a ## marker
+ // this marker should get substituted with the contents of our stored
+ // variable (from the subsequent scan).
+
+ QString ts = scriptArgument;
+ int vstart = ts.find( "##" );
+ if( vstart != -1 ) {
+ ts.remove( vstart, 2 );
+ ts.insert( vstart, scanvar );
+ }
+
+ bm = bm.arg(ts);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ /* if not around yet, then post window... */
+ if (prompt->Consumed()) {
+ if (!(prompt->isVisible())) {
+ prompt->setPrompt( ts );
+ prompt->setEchoModeNormal();
+ prompt->show();
+ }
+ } else {
+ /* if prompt withdrawn ... then, */
+ if (!(prompt->isVisible())) {
+ writeline(prompt->text());
+ prompt->setConsumed();
+ scriptindex++;
+ return;
+ }
+ /* replace timeout value */
+ }
+ }
+
+ if (scriptCommand == "PWPrompt") {
+ QString bm = i18n("PW Prompt %1").arg(scriptArgument);
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ /* if not around yet, then post window... */
+ if (prompt->Consumed()) {
+ if (!(prompt->isVisible())) {
+ prompt->setPrompt(scriptArgument);
+ prompt->setEchoModePassword();
+ prompt->show();
+ }
+ } else {
+ /* if prompt withdrawn ... then, */
+ if (!(prompt->isVisible())) {
+ writeline(prompt->text());
+ prompt->setConsumed();
+ scriptindex++;
+ return;
+ }
+ /* replace timeout value */
+ }
+ }
+
+ if (scriptCommand == "LoopStart") {
+
+ QString bm = i18n("Loop Start %1").arg(scriptArgument);
+
+ // The incrementing of the scriptindex MUST be before the
+ // call to setExpect otherwise the expect will miss a string that is
+ // already in the buffer.
+ scriptindex++;
+
+ if ( loopnest > (MAXLOOPNEST-2) ) {
+ bm += i18n("ERROR: Nested too deep, ignored.");
+ vmain=20;
+ cancelbutton();
+ KMessageBox::error(0, i18n("Loops nested too deeply."));
+ } else {
+ setExpect(scriptArgument);
+ loopstartindex[loopnest] = scriptindex;
+ loopstr[loopnest] = scriptArgument;
+ loopend = false;
+ loopnest++;
+ }
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ }
+
+ if (scriptCommand == "LoopEnd") {
+ QString bm = i18n("Loop End %1").arg(scriptArgument);
+ if ( loopnest <= 0 ) {
+ bm = i18n("LoopEnd without matching Start. Line: %1").arg(bm);
+ vmain=20;
+ cancelbutton();
+ KMessageBox::error(0, bm);
+ return;
+ } else {
+ // NB! The incrementing of the scriptindex MUST be before the
+ // call to setExpect otherwise the expect will miss a string
+ // that is already in the buffer.
+ scriptindex++;
+ setExpect(scriptArgument);
+ loopnest--;
+ loopend = true;
+ }
+ messg->setText(bm);
+ emit debugMessage(bm);
+
+ }
+ }
+ }
+
+ // this is a subroutine for the "Answer" script option
+
+ if(vmain == 150) {
+ if(!expecting) {
+ writeline(gpppdata.modemAnswerStr());
+ setExpect(gpppdata.modemAnswerResp());
+
+ vmain = 2;
+ scriptindex++;
+ return;
+ }
+ }
+
+ if(vmain == 30) {
+ if (termwindow->isVisible())
+ return;
+ if (termwindow->pressedContinue())
+ vmain = 10;
+ else
+ cancelbutton();
+ }
+
+ if(vmain == 10) {
+ if(!expecting) {
+
+ int result;
+
+ timeout_timer->stop();
+ if_timeout_timer->stop(); // better be sure.
+
+ // stop reading of data
+ Modem::modem->stop();
+
+ if(gpppdata.authMethod() == AUTH_TERMINAL) {
+ if (termwindow) {
+ delete termwindow;
+ termwindow = 0L;
+ show();
+ } else {
+ termwindow = new LoginTerm(0L, 0L);
+ hide();
+ termwindow->show();
+ vmain = 30;
+ return;
+ }
+ }
+
+ // Close the tty. This prevents the QTimer::singleShot() in
+ // Modem::readtty() from re-enabling the socket notifier.
+ // The port is still held open by the helper process.
+ Modem::modem->closetty();
+
+ killTimer( main_timer_ID );
+
+ if_timeout_timer->start(gpppdata.pppdTimeout()*1000);
+ kdDebug(5002) << "started if timeout timer with " << gpppdata.pppdTimeout()*1000 << endl;
+
+ // find out PPP interface and notify the stats module
+ stats->setUnit(pppInterfaceNumber());
+
+ kapp->flushX();
+ semaphore = true;
+ result = execppp();
+
+ emit debugMessage(i18n("Starting pppd..."));
+ kdDebug(5002) << "execppp() returned with return-code " << result << endl;
+
+ if(result) {
+ if(!gpppdata.autoDNS())
+ adddns();
+
+ // O.K we are done here, let's change over to the if_waiting loop
+ // where we wait for the ppp if (interface) to come up.
+
+ emit if_waiting_signal();
+ } else {
+
+ // starting pppd wasn't successful. Error messages were
+ // handled by execppp();
+ if_timeout_timer->stop();
+
+ hide();
+ messg->setText("");
+ p_kppp->quit_b->setFocus();
+ p_kppp->show();
+ kapp->processEvents();
+ Modem::modem->hangup();
+ emit stopAccounting();
+ p_kppp->con_win->stopClock();
+ Modem::modem->closetty();
+ Modem::modem->unlockdevice();
+
+ }
+
+ return;
+ }
+ }
+
+ // this is a "wait until cancel" entry
+
+ if(vmain == 20) {
+ gpppdata.setWaitCallback(false);
+ }
+}
+
+
+void ConnectWidget::set_con_speed_string() {
+ // Here we are trying to determine the speed at which we are connected.
+ // Usually the modem responds after connect with something like
+ // CONNECT 115200, so all we need to do is find the number after CONNECT
+ // or whatever the modemConnectResp() is.
+ p_kppp->con_speed = Modem::modem->parseModemSpeed(myreadbuffer);
+}
+
+
+
+void ConnectWidget::readChar(unsigned char c) {
+ if(semaphore)
+ return;
+
+ readbuffer += c;
+ myreadbuffer += c;
+
+ // While in scanning mode store each char to the scan buffer
+ // for use in the prompt command
+ if( scanning )
+ scanbuffer += c;
+
+ // add to debug window
+ emit debugPutChar(c);
+
+ checkBuffers();
+}
+
+
+void ConnectWidget::checkBuffers() {
+ // Let's check if we are finished with scanning:
+ // The scanstring have to be in the buffer and the latest character
+ // was a carriage return or an linefeed (depending on modem setup)
+ if( scanning && scanbuffer.contains(scanstr) &&
+ ( scanbuffer.right(1) == "\n" || scanbuffer.right(1) == "\r") ) {
+ scanning = false;
+
+ int vstart = scanbuffer.find( scanstr ) + scanstr.length();
+ scanvar = scanbuffer.mid( vstart, scanbuffer.length() - vstart);
+ scanvar = scanvar.stripWhiteSpace();
+
+ // Show the Variabel content in the debug window
+ QString sv = i18n("Scan Var: %1").arg(scanvar);
+ emit debugMessage(sv);
+ }
+
+ if(expecting) {
+ if(readbuffer.contains(expectstr)) {
+ expecting = false;
+ // keep everything after the expected string
+ readbuffer.remove(0, readbuffer.find(expectstr) + expectstr.length());
+
+ QString ts = i18n("Found: %1").arg(expectstr);
+ emit debugMessage(ts);
+
+ if (loopend) {
+ loopend=false;
+ }
+ }
+
+ if (loopend && readbuffer.contains(loopstr[loopnest])) {
+ expecting = false;
+ readbuffer = "";
+ QString ts = i18n("Looping: %1").arg(loopstr[loopnest]);
+ emit debugMessage(ts);
+ scriptindex = loopstartindex[loopnest];
+ loopend = false;
+ loopnest++;
+ }
+ // notify event loop if expected string was found
+ if(!expecting)
+ timerEvent((QTimerEvent *) 0);
+ }
+}
+
+
+
+void ConnectWidget::pause() {
+ pausing = false;
+ pausetimer->stop();
+}
+
+
+void ConnectWidget::cancelbutton() {
+ gpppdata.setWaitCallback(false);
+ Modem::modem->stop();
+ killTimer(main_timer_ID);
+ timeout_timer->stop();
+ if_timer->stop();
+ if_timeout_timer->stop();
+
+ if (termwindow) {
+ delete termwindow;
+ termwindow = 0L;
+ show();
+ }
+
+ messg->setText(i18n("One moment please..."));
+
+ // just to be sure
+ Requester::rq->removeSecret(AUTH_PAP);
+ Requester::rq->removeSecret(AUTH_CHAP);
+ removedns();
+
+ kapp->processEvents();
+
+ Requester::rq->killPPPDaemon();
+ Modem::modem->hangup();
+
+ hide();
+ messg->setText("");
+ p_kppp->quit_b->setFocus();
+ p_kppp->show();
+ emit stopAccounting(); // just to be sure
+ p_kppp->con_win->stopClock();
+ Modem::modem->closetty();
+ Modem::modem->unlockdevice();
+
+ //abort prompt window...
+ if (prompt->isVisible()) {
+ prompt->hide();
+ }
+ prompt->setConsumed();
+
+ if(p_kppp->quitOnDisconnect())
+ kapp->exit(0);
+}
+
+
+void ConnectWidget::script_timed_out() {
+ if(vmain == 20) { // we are in the 'wait for the user to cancel' state
+ timeout_timer->stop();
+ emit stopAccounting();
+ p_kppp->con_win->stopClock();
+ return;
+ }
+
+ if (prompt->isVisible())
+ prompt->hide();
+
+ prompt->setConsumed();
+ messg->setText(i18n("Script timed out."));
+ Modem::modem->hangup();
+ emit stopAccounting();
+ p_kppp->con_win->stopClock();
+
+ vmain = 0; // let's try again.
+ substate = -1;
+}
+
+
+void ConnectWidget::setScan(const QString &n) {
+ scanning = true;
+ scanstr = n;
+ scanbuffer = "";
+
+ QString ts = i18n("Scanning: %1").arg(n);
+ emit debugMessage(ts);
+}
+
+
+void ConnectWidget::setExpect(const QString &n) {
+ expecting = true;
+ expectstr = n;
+
+ QString ts = i18n("Expecting: %1").arg(n);
+ ts.replace(QRegExp("\n"), "<LF>");
+ emit debugMessage(ts);
+
+ // check if the expected string is in the read buffer already.
+ checkBuffers();
+}
+
+
+void ConnectWidget::if_waiting_timed_out() {
+ if_timer->stop();
+ if_timeout_timer->stop();
+ kdDebug(5002) << "if_waiting_timed_out()" << endl;
+
+ gpppdata.setpppdError(E_IF_TIMEOUT);
+
+ // let's kill the stuck pppd
+ Requester::rq->killPPPDaemon();
+
+ emit stopAccounting();
+ p_kppp->con_win->stopClock();
+
+
+ // killing ppp will generate a SIGCHLD which will be caught in pppdie()
+ // in main.cpp what happens next will depend on the boolean
+ // reconnect_on_disconnect which is set in ConnectWidget::init();
+}
+
+void ConnectWidget::pppdDied()
+{
+ if_timer->stop();
+ if_timeout_timer->stop();
+}
+
+void ConnectWidget::if_waiting_slot() {
+ messg->setText(i18n("Logging on to network..."));
+
+ if(!stats->ifIsUp()) {
+
+ if(gpppdata.pppdError() != 0) {
+ // we are here if pppd died immediately after starting it.
+ pppdDied();
+ // error message handled in main.cpp: sigPPPDDied()
+ return;
+ }
+
+ if_timer->start(100, TRUE); // single shot
+ return;
+ }
+
+ // O.K the ppp interface is up and running
+ // give it a few time to come up completly (0.2 seconds)
+ if_timeout_timer->stop();
+ if_timer->stop();
+ usleep(200000);
+
+ if(gpppdata.autoDNS())
+ addpeerdns();
+
+ // Close the debugging window. If we are connected, we
+ // are not really interested in debug output
+ emit closeDebugWindow();
+ p_kppp->statdlg->take_stats(); // start taking ppp statistics
+ auto_hostname();
+
+ // signal other applications that we are connected now
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "connected()", QByteArray());
+
+ if(!gpppdata.command_on_connect().isEmpty()) {
+ messg->setText(i18n("Running startup command..."));
+
+ // make sure that we don't get any async errors
+ kapp->flushX();
+ execute_command(gpppdata.command_on_connect());
+ messg->setText(i18n("Done"));
+ }
+
+ // remove the authentication file
+ Requester::rq->removeSecret(AUTH_PAP);
+ Requester::rq->removeSecret(AUTH_CHAP);
+
+ emit debugMessage(i18n("Done"));
+ set_con_speed_string();
+
+ p_kppp->con_win->setConnectionSpeed(p_kppp->con_speed);
+ hide();
+ messg->setText("");
+
+ // prepare the con_win so as to have the right size for
+ // accounting / non-accounting mode
+ if(p_kppp->acct != 0)
+ p_kppp->con_win->accounting(p_kppp->acct->running());
+ else
+ p_kppp->con_win->accounting(false);
+
+ if (gpppdata.get_dock_into_panel()) {
+ DockWidget::dock_widget->show();
+ DockWidget::dock_widget->take_stats();
+ hide();
+ }
+ else {
+ p_kppp->con_win->show();
+
+ if(gpppdata.get_iconify_on_connect()) {
+ p_kppp->con_win->showMinimized();
+ }
+ }
+
+ Modem::modem->closetty();
+}
+
+
+bool ConnectWidget::execppp() {
+ QString command;
+
+ command = "pppd";
+
+ // as of version 2.3.6 pppd falls back to the real user rights when
+ // opening a device given in a command line. To avoid permission conflicts
+ // we'll simply leave this argument away. pppd will then use the default tty
+ // which is the serial port we connected stdin/stdout to in opener.cpp.
+ // command += " ";
+ // command += gpppdata.modemDevice();
+
+ command += " " + gpppdata.speed();
+
+ command += " -detach";
+
+ if(gpppdata.ipaddr() != "0.0.0.0" ||
+ gpppdata.gateway() != "0.0.0.0") {
+ if(gpppdata.ipaddr() != "0.0.0.0") {
+ command += " ";
+ command += gpppdata.ipaddr();
+ command += ":";
+ }
+ else {
+ command += " ";
+ command += ":";
+ }
+
+ if(gpppdata.gateway() != "0.0.0.0")
+ command += gpppdata.gateway();
+ }
+
+ if(gpppdata.subnetmask() != "0.0.0.0")
+ command += " netmask " + gpppdata.subnetmask();
+
+ // the english/i18n mix below is ugly but we want to keep working
+ // after someone changed the code to use i18n'ed config values
+ QString flowCtrl = gpppdata.flowcontrol();
+ if(flowCtrl != "None" && flowCtrl != i18n("None")) {
+ if(flowCtrl == "CRTSCTS" || flowCtrl == "Hardware [CRTSCTS]" ||
+ flowCtrl == i18n("Hardware [CRTSCTS]"))
+ command += " crtscts";
+ else
+ command += " xonxoff";
+ }
+
+ if(gpppdata.defaultroute())
+ command += " defaultroute";
+
+ if(gpppdata.autoDNS())
+ command += " usepeerdns";
+
+ QStringList &arglist = gpppdata.pppdArgument();
+ for ( QStringList::Iterator it = arglist.begin();
+ it != arglist.end();
+ ++it )
+ {
+ command += " " + *it;
+ }
+
+ // Callback settings
+ if(gpppdata.callbackType() && !gpppdata.waitCallback()) {
+ if(!gpppdata.pppdVersionMin(2, 4, 2)) {
+ command += " +callback";
+ if(gpppdata.callbackType() == CBTYPE_USER)
+ command += " callback " + gpppdata.callbackPhone();
+ } else {
+ command += " callback ";
+ command += gpppdata.callbackType() == CBTYPE_ADMIN ?
+ QString("0") : gpppdata.callbackPhone();
+ }
+ } else
+ gpppdata.setWaitCallback(false);
+
+ // PAP settings
+ if(gpppdata.authMethod() == AUTH_PAP) {
+ command += " -chap user ";
+ command = command + "\"" + gpppdata.storedUsername() + "\"";
+ }
+
+ // CHAP settings
+ if(gpppdata.authMethod() == AUTH_CHAP) {
+ command += " -pap user ";
+ command = command + "\"" + gpppdata.storedUsername() + "\"";
+ }
+
+ // PAP/CHAP settings
+ if(gpppdata.authMethod() == AUTH_PAPCHAP) {
+ command += " user ";
+ command = command + "\"" + gpppdata.storedUsername() + "\"";
+ }
+
+ // check for debug
+ if(gpppdata.getPPPDebug())
+ command += " debug";
+
+ if (command.length() > MAX_CMDLEN) {
+ KMessageBox::error(this, i18n(
+ "pppd command + command-line arguments exceed "
+ "2024 characters in length."
+ ));
+
+ return false; // nonsensically long command which would bust my buffer buf.
+ }
+
+ kapp->flushX();
+
+ return Requester::rq->execPPPDaemon(command);
+}
+
+
+void ConnectWidget::closeEvent( QCloseEvent *e ) {
+ e->ignore();
+ emit cancelbutton();
+}
+
+
+void ConnectWidget::setMsg(const QString &msg) {
+ messg->setText(msg);
+}
+
+void ConnectWidget::writeline(const QString &s) {
+ Modem::modem->writeLine(s.local8Bit());
+}
+
+// Set the hostname and domain from DNS Server
+void auto_hostname() {
+ struct in_addr local_ip;
+ struct hostent *hostname_entry;
+ QString new_hostname;
+ int dot;
+ char tmp_str[100]; // buffer overflow safe
+
+ gethostname(tmp_str, sizeof(tmp_str));
+ tmp_str[sizeof(tmp_str)-1]=0; // panic
+ old_hostname=tmp_str; // copy to QString
+
+ if (!p_kppp->stats->local_ip_address.isEmpty() && gpppdata.autoname()) {
+ local_ip.s_addr=inet_addr(p_kppp->stats->local_ip_address.ascii());
+ hostname_entry=gethostbyaddr((const char *)&local_ip,sizeof(in_addr),AF_INET);
+
+ if (hostname_entry != 0L) {
+ new_hostname=hostname_entry->h_name;
+ dot=new_hostname.find('.');
+ new_hostname=new_hostname.remove(dot,new_hostname.length()-dot);
+ Requester::rq->setHostname(new_hostname);
+ modified_hostname = TRUE;
+
+ new_hostname=hostname_entry->h_name;
+ new_hostname.remove(0,dot+1);
+
+ add_domain(new_hostname);
+ }
+ }
+
+}
+
+// Replace the DNS domain entry in the /etc/resolv.conf file and
+// disable the nameserver entries if option is enabled
+void add_domain(const QString &domain) {
+
+ int fd;
+ char c;
+ QString resolv[MAX_RESOLVCONF_LINES];
+
+ if (domain.isEmpty())
+ return;
+
+ if((fd = Requester::rq->openResolv(O_RDONLY)) >= 0) {
+
+ int i=0;
+ while((read(fd, &c, 1) == 1) && (i < MAX_RESOLVCONF_LINES)) {
+ if(c == '\n') {
+ i++;
+ }
+ else {
+ resolv[i] += c;
+ }
+ }
+ close(fd);
+ if ((c != '\n') && (i < MAX_RESOLVCONF_LINES)) i++;
+
+ if((fd = Requester::rq->openResolv(O_WRONLY|O_TRUNC)) >= 0) {
+ QCString tmp = "domain " + domain.local8Bit() +
+ " \t\t#kppp temp entry\n";
+ write(fd, tmp.data(), tmp.length());
+
+ for(int j=0; j < i; j++) {
+ if((resolv[j].contains("domain") ||
+ ( resolv[j].contains("nameserver")
+ && !resolv[j].contains("#kppp temp entry")
+ && gpppdata.exDNSDisabled()))
+ && !resolv[j].contains("#entry disabled by kppp")) {
+ QCString tmp = "# " + resolv[j].local8Bit() +
+ " \t#entry disabled by kppp\n";
+ write(fd, tmp, tmp.length());
+ }
+ else {
+ QCString tmp = resolv[j].local8Bit() + "\n";
+ write(fd, tmp, tmp.length());
+ }
+ }
+ }
+ close(fd);
+ }
+}
+
+
+// adds the DNS entries in the /etc/resolv.conf file
+void adddns()
+{
+ int fd;
+
+ if ((fd = Requester::rq->openResolv(O_WRONLY|O_APPEND)) >= 0) {
+ QStringList &dnslist = gpppdata.dns();
+ for ( QStringList::Iterator it = dnslist.begin();
+ it != dnslist.end();
+ ++it )
+ {
+ QCString dns = "nameserver " + (*it).local8Bit() +
+ " \t#kppp temp entry\n";
+ write(fd, dns.data(), dns.length());
+ }
+ close(fd);
+ }
+ add_domain(gpppdata.domain());
+}
+
+void addpeerdns() {
+ int fd, fd2;
+
+ if((fd = Requester::rq->openResolv(O_WRONLY|O_APPEND)) >= 0) {
+ if((fd2 = open("/etc/ppp/resolv.conf", O_RDONLY)) >= 0) {
+ char c;
+ int i = 0;
+ while(i++ < 100 && read(fd2, &c, 1) == 1) {
+ if(c == '\n')
+ write(fd, "\t#kppp temp entry\n", 18);
+ else
+ write(fd, &c, 1);
+ }
+ close(fd2);
+ } else
+ fprintf(stderr, "failed to read from /etc/ppp/resolv.conf\n");
+ close(fd);
+ }
+ add_domain(gpppdata.domain());
+}
+
+// remove the dns entries from the /etc/resolv.conf file
+void removedns() {
+
+ int fd;
+ char c;
+ QString resolv[MAX_RESOLVCONF_LINES];
+
+ if((fd = Requester::rq->openResolv(O_RDONLY)) >= 0) {
+
+ int i=0;
+ while(read(fd, &c, 1) == 1 && i < MAX_RESOLVCONF_LINES) {
+ if(c == '\n') {
+ i++;
+ }
+ else {
+ resolv[i] += c;
+ }
+ }
+ close(fd);
+
+ if((fd = Requester::rq->openResolv(O_WRONLY|O_TRUNC)) >= 0) {
+ for(int j=0; j < i; j++) {
+ if(resolv[j].contains("#kppp temp entry")) continue;
+ if(resolv[j].contains("#entry disabled by kppp")) {
+ QCString tmp = resolv[j].local8Bit();
+ write(fd, tmp.data()+2, tmp.length() - 27);
+ write(fd, "\n", 1);
+ }
+ else {
+ QCString tmp = resolv[j].local8Bit() + "\n";
+ write(fd, tmp, tmp.length());
+ }
+ }
+ }
+ close(fd);
+
+ }
+
+ if ( modified_hostname ) {
+ Requester::rq->setHostname(old_hostname);
+ modified_hostname = FALSE;
+ }
+
+}
+
+#include "connect.moc"
diff --git a/kppp/connect.h b/kppp/connect.h
new file mode 100644
index 00000000..129a50fa
--- /dev/null
+++ b/kppp/connect.h
@@ -0,0 +1,157 @@
+/* -*- C++ -*-
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONNECT_H_
+#define _CONNECT_H_
+
+#include <qtimer.h>
+#include <kpushbutton.h>
+#include <qlabel.h>
+#include <qevent.h>
+
+#include "kpppconfig.h"
+#include "pwentry.h"
+#include "docking.h"
+#include "loginterm.h"
+
+#define MAXLOOPNEST (MAX_SCRIPT_ENTRIES/2)
+
+class PPPStats;
+
+class ConnectWidget : public QWidget {
+ Q_OBJECT
+public:
+ ConnectWidget(QWidget *parent, const char *name, PPPStats *st);
+ ~ConnectWidget();
+
+public:
+ void set_con_speed_string();
+ void setMsg(const QString &);
+ void pppdDied();
+
+ // these are only necessary to prevent the user from clicking in the cancel
+ // button during the disconnect delay in KPPPWidget
+ void disableButtons();
+ void enableButtons();
+
+protected:
+ void timerEvent(QTimerEvent *);
+ void closeEvent( QCloseEvent *e );
+
+private slots:
+ void readChar(unsigned char);
+ void pause();
+ void if_waiting_slot();
+
+public slots:
+ void init();
+ void preinit();
+ void script_timed_out();
+ void if_waiting_timed_out();
+ void cancelbutton();
+
+signals:
+ void if_waiting_signal();
+ void debugMessage(const QString &);
+ void toggleDebugWindow();
+ void closeDebugWindow();
+ void debugPutChar(unsigned char);
+ void startAccounting();
+ void stopAccounting();
+
+public:
+ QString myreadbuffer; // we want to keep every thing in order to fish for the
+
+ // connection speed later on
+ QPushButton *debug;
+ int main_timer_ID;
+
+private:
+ int vmain;
+ int substate;
+ int scriptindex;
+ QString scriptCommand, scriptArgument;
+ QStringList *comlist, *arglist;
+
+ // static const int maxloopnest=(MAX_SCRIPT_ENTRIES/2);
+ int loopnest;
+ int loopstartindex[MAXLOOPNEST];
+ bool loopend;
+ QString loopstr[MAXLOOPNEST];
+
+ bool semaphore;
+ QTimer *inittimer;
+
+ QTimer *timeout_timer;
+ bool execppp();
+ void writeline(const QString &);
+ void checkBuffers();
+
+ void setExpect(const QString &);
+ bool expecting;
+ QString expectstr;
+
+ QString readbuffer;
+
+ void setScan(const QString &);
+ QString scanvar;
+ QString scanstr;
+ QString scanbuffer;
+ bool scanning;
+
+ bool pausing;
+ PWEntry *prompt;
+ LoginTerm *termwindow;
+
+ int scriptTimeout;
+ QTimer *pausetimer;
+ QTimer *if_timer;
+ QTimer *if_timeout_timer;
+
+ QLabel *messg;
+ KPushButton *cancel;
+
+ bool firstrunID;
+ bool firstrunPW;
+
+ unsigned int dialnumber; // the current number to dial
+
+ PPPStats *stats;
+};
+
+
+// non-member function to kill&wait on the pppd child process
+extern void killppp();
+void adddns();
+void addpeerdns();
+void removedns();
+void add_domain(const QString & newdomain);
+void auto_hostname();
+
+#endif
+
diff --git a/kppp/convlog.tcl b/kppp/convlog.tcl
new file mode 100644
index 00000000..d5e97a1b
--- /dev/null
+++ b/kppp/convlog.tcl
@@ -0,0 +1,64 @@
+#!/usr/bin/tclsh
+
+proc err { msg } {
+ global argv0
+
+ puts stderr "[lindex $argv0 0]: $msg"
+}
+
+foreach i [glob -nocomplain ~/.kde/share/apps/kppp/Log/*-199?] {
+
+ if {[catch { set fin [open $i "r"] }]} {
+ err "cannot open $i for reading"
+ continue
+ }
+
+ if {[catch { set fout [open ${i}.log "a"] }]} {
+ err "cannot open ${i}.log for writing"
+ continue
+ }
+
+ puts -nonewline "converting $i... "
+ flush stdout
+
+ set PHASE 1
+ while {[eof $fin] == 0} {
+ gets $fin line
+
+ if {[regexp {(.*:.*:.*):.*:(.*):.*} $line s s1 s2]} {
+ set date [clock scan $s1]
+ if {$PHASE == 2} {
+ # newline
+ puts $fout ""
+ }
+ puts -nonewline $fout "$date:$s2"
+ set PHASE 2
+ } else {
+ set PHASE 1
+ if {[regexp {(.*:.*:.*):} $line s s1]} {
+ set date [clock scan $s1]
+
+ gets $fin line1
+ gets $fin line2
+ regexp {.*:\ *([0-9.]+)\ *(.*)} $line1 s s1 s2
+ regexp {.*:\ *([0-9.]+)\ *(.*)} $line2 s s3 s4
+ puts $fout ":$s2:$date:$s1:$s3:-1:-1"
+ }
+ }
+ }
+ close $fin
+ close $fout
+
+ # remove duplicate entries
+ if {[catch { exec cat ${i}.log | sort -n | uniq | egrep {^[0-9]} > ${i}.log.new} ret]} {
+ err "cannot sort ${i}.log $ret"
+ } else {
+ catch { exec mv ${i}.log.new ${i}.log }
+ }
+
+ catch {
+ exec chmod 600 ${i}.log
+ }
+
+ puts "done"
+} \ No newline at end of file
diff --git a/kppp/conwindow.cpp b/kppp/conwindow.cpp
new file mode 100644
index 00000000..ae63de85
--- /dev/null
+++ b/kppp/conwindow.cpp
@@ -0,0 +1,340 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qtooltip.h>
+#include "conwindow.h"
+#include "docking.h"
+#include "pppdata.h"
+#include "pppstats.h"
+#include <klocale.h>
+#include <kglobal.h>
+
+extern PPPData gpppdata;
+
+ConWindow::ConWindow(QWidget *parent, const char *name,QWidget *mainwidget,
+ PPPStats *st)
+ : QWidget(parent, name, 0),
+ minutes(0),
+ seconds(0),
+ hours(0),
+ days(0),
+ tl1(0),
+ stats(st),
+ accountingEnabled(false),
+ volumeAccountingEnabled(false)
+{
+ info1 = new QLabel(i18n("Connected at:"), this);
+ info2 = new QLabel("", this);
+
+ timelabel1 = new QLabel(i18n("Time connected:"), this);
+ timelabel2 = new QLabel("000:00:00", this);
+
+ vollabel = new QLabel(i18n("Volume:"), this);
+ volinfo = new QLabel("", this);
+
+ // now the stuff for accounting
+ session_bill_l = new QLabel(i18n("Session bill:"), this);
+ session_bill = new QLabel("", this);
+ total_bill_l = new QLabel(i18n("Total bill:"), this);
+ total_bill = new QLabel("", this);
+
+ setCaption("kppp");
+
+ cancelbutton = new QPushButton(this);
+ cancelbutton->setText(i18n("&Disconnect"));
+ connect(cancelbutton, SIGNAL(clicked()), mainwidget, SLOT(disconnect()));
+
+ statsbutton = new QPushButton(this);
+ statsbutton->setText(i18n("De&tails"));
+ statsbutton->setFocus();
+ connect(statsbutton, SIGNAL(clicked()), mainwidget, SLOT(showStats()));
+
+ clocktimer = new QTimer(this);
+ connect(clocktimer, SIGNAL(timeout()), SLOT(timeclick()));
+
+ // read window position from config file
+ int p_x, p_y;
+ gpppdata.winPosConWin(p_x, p_y);
+ setGeometry(p_x, p_y, 320, 110);
+}
+
+ConWindow::~ConWindow() {
+ stopClock();
+}
+
+// save window position when window was closed
+bool ConWindow::event(QEvent *e) {
+ if (e->type() == QEvent::Hide)
+ {
+ gpppdata.setWinPosConWin(x(), y());
+ return true;
+ }
+ else
+ return QWidget::event(e);
+}
+
+QString ConWindow::prettyPrintVolume(unsigned int n) {
+ int idx = 0;
+ const QString quant[] = {i18n("Byte"), i18n("KB"),
+ i18n("MB"), i18n("GB"), QString::null};
+
+ float n1 = n;
+ while(n >= 1024 && !quant[idx].isNull()) {
+ idx++;
+ n /= 1024;
+ }
+
+ int i = idx;
+ while(i--)
+ n1 = n1 / 1024.0;
+
+ QString s = KGlobal::locale()->formatNumber( n1, idx==0 ? 0 : 1 );
+ s += " " + quant[idx];
+ return s;
+}
+
+void ConWindow::accounting(bool on) {
+ // cache accounting settings
+ accountingEnabled = on;
+ volumeAccountingEnabled = gpppdata.VolAcctEnabled();
+
+ // delete old layout
+ if(tl1 != 0)
+ delete tl1;
+
+ // add layout now
+ tl1 = new QVBoxLayout(this, 10, 10);
+ tl1->addSpacing(5);
+ QHBoxLayout *tl = new QHBoxLayout;
+ tl1->addLayout(tl);
+ tl->addSpacing(20);
+ QGridLayout *l1;
+
+ int vol_lines = 0;
+ if(gpppdata.VolAcctEnabled())
+ vol_lines = 1;
+
+ if(accountingEnabled)
+ l1 = new QGridLayout(4 + vol_lines, 2, 5);
+ else
+ l1 = new QGridLayout(2 + vol_lines, 2, 5);
+ tl->addLayout(l1);
+ l1->setColStretch(0, 0);
+ l1->setColStretch(1, 1);
+
+ info2->setAlignment(AlignRight|AlignVCenter);
+ timelabel2->setAlignment(AlignRight|AlignVCenter);
+ session_bill->setAlignment(AlignRight|AlignVCenter);
+ total_bill->setAlignment(AlignRight|AlignVCenter);
+ volinfo->setAlignment(AlignRight|AlignVCenter);
+ // make sure that there's enough space for the bills
+ QString s1 = session_bill->text();
+ QString s2 = total_bill->text();
+ QString s3 = volinfo->text();
+
+ session_bill->setText("888888.88 XXX");
+ total_bill->setText("888888.88 XXX");
+ volinfo->setText("8888.8 MB");
+ session_bill->setFixedSize(session_bill->sizeHint());
+ total_bill->setFixedSize(total_bill->sizeHint());
+ volinfo->setFixedSize(volinfo->sizeHint());
+ session_bill->setText(s1);
+ total_bill->setText(s2);
+ volinfo->setText(s3);
+
+ l1->addWidget(info1, 0, 0);
+ l1->addWidget(info2, 0, 1);
+ l1->addWidget(timelabel1, 1, 0);
+ l1->addWidget(timelabel2, 1, 1);
+ if(accountingEnabled) {
+ session_bill_l->show();
+ session_bill->show();
+ total_bill_l->show();
+ total_bill->show();
+ l1->addWidget(session_bill_l, 2, 0);
+ l1->addWidget(session_bill, 2, 1);
+ l1->addWidget(total_bill_l, 3, 0);
+ l1->addWidget(total_bill, 3, 1);
+
+ if(volumeAccountingEnabled) {
+ vollabel->show();
+ volinfo->show();
+ l1->addWidget(vollabel, 4, 0);
+ l1->addWidget(volinfo, 4, 1);
+ } else {
+ vollabel->hide();
+ volinfo->hide();
+ }
+
+ } else {
+ session_bill_l->hide();
+ session_bill->hide();
+ total_bill_l->hide();
+ total_bill->hide();
+
+ if(volumeAccountingEnabled) {
+ vollabel->show();
+ volinfo->show();
+ l1->addWidget(vollabel, 2, 0);
+ l1->addWidget(volinfo, 2, 1);
+ } else {
+ vollabel->hide();
+ volinfo->hide();
+ }
+ }
+
+ tl->addSpacing(10);
+ QVBoxLayout *l2 = new QVBoxLayout(5);
+ tl->addLayout(l2);
+ l2->addStretch(1);
+ l2->addWidget(statsbutton);
+ l2->addWidget(cancelbutton);
+
+ l2->addStretch(1);
+
+ tl1->addSpacing(5);
+
+ setFixedSize(sizeHint());
+/*
+ do not overwrite position read from config
+ // If this gets re-enabled, fix it for Xinerama before committing to CVS.
+ setGeometry((QApplication::desktop()->width() - width()) / 2,
+ (QApplication::desktop()->height() - height())/2,
+ width(),
+ height());
+*/
+}
+
+
+void ConWindow::dock() {
+ DockWidget::dock_widget->show();
+ hide();
+}
+
+
+void ConWindow::startClock() {
+ minutes = 0;
+ seconds = 0;
+ hours = 0;
+ QString title ;
+
+ title = gpppdata.accname();
+
+ if(gpppdata.get_show_clock_on_caption()){
+ title += " 00:00" ;
+ }
+ setCaption(title);
+
+ timelabel2->setText("00:00:00");
+ clocktimer->start(1000);
+}
+
+
+void ConWindow::setConnectionSpeed(const QString &speed) {
+ info2->setText(speed);
+}
+
+
+void ConWindow::stopClock() {
+ clocktimer->stop();
+}
+
+
+void ConWindow::timeclick() {
+ QString tooltip = i18n("Connection: %1\n"
+ "Connected at: %2\n"
+ "Time connected: %3")
+ .arg(gpppdata.accname()).arg(info2->text())
+ .arg(time_string2);
+
+ if(accountingEnabled)
+ tooltip += i18n("\nSession Bill: %1\nTotal Bill: %2")
+ .arg(session_bill->text()).arg(total_bill->text());
+ // volume accounting
+ if(volumeAccountingEnabled) {
+
+ volinfo->setEnabled(TRUE);
+ int bytes = gpppdata.totalBytes();
+ volinfo->setText(prettyPrintVolume(bytes));
+ }
+
+ seconds++;
+
+ if(seconds >= 60 ) {
+ minutes ++;
+ seconds = 0;
+ }
+
+ if (minutes >= 60){
+ minutes = 0;
+ hours ++;
+ }
+
+ if( hours >= 24){
+ days ++;
+ hours = 0;
+ }
+
+ time_string.sprintf("%02d:%02d",hours,minutes);
+ time_string2 = "";
+ if (days)
+ time_string2.sprintf("%d d %02d:%02d:%02d",
+ days,hours,minutes,seconds);
+
+ else
+ time_string2.sprintf("%02d:%02d:%02d",hours,minutes,seconds);
+
+ caption_string = gpppdata.accname();
+ caption_string += " ";
+ caption_string += time_string;
+
+
+ timelabel2->setText(time_string2);
+
+ if(gpppdata.get_show_clock_on_caption() && (seconds == 1)){
+ // we update the Caption only once per minute not every second
+ // otherwise I get a flickering icon
+ setCaption(caption_string);
+ }
+
+ QToolTip::add(DockWidget::dock_widget, tooltip);
+}
+
+
+void ConWindow::closeEvent( QCloseEvent *e ){
+ // we don't want to lose the
+ // conwindow since this is our last connection kppp.
+ // if we lost it we could only kill the program by hand to get on with life.
+ e->ignore();
+
+ if(gpppdata.get_dock_into_panel())
+ dock();
+}
+
+
+void ConWindow::slotAccounting(QString total, QString session) {
+ total_bill->setText(total);
+ session_bill->setText(session);
+}
+
+#include "conwindow.moc"
diff --git a/kppp/conwindow.h b/kppp/conwindow.h
new file mode 100644
index 00000000..4faaa0ed
--- /dev/null
+++ b/kppp/conwindow.h
@@ -0,0 +1,96 @@
+/* -*- C++ -*-
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CONWINDOW_H_
+#define _CONWINDOW_H_
+
+
+#include <qtimer.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qdialog.h>
+#include <qframe.h>
+#include <qevent.h>
+#include <qlayout.h>
+
+class PPPStats;
+
+class ConWindow : public QWidget {
+Q_OBJECT
+
+public:
+ ConWindow(QWidget *parent, const char *name,QWidget *main, PPPStats *st);
+ ~ConWindow();
+
+protected:
+ void closeEvent( QCloseEvent *e );
+ bool event( QEvent *e );
+
+private slots:
+ void timeclick();
+ void dock();
+
+public:
+ void setConnectionSpeed(const QString&);
+ void startClock();
+ void stopClock();
+ void accounting(bool); // show/ hide accounting info
+
+public slots:
+ void slotAccounting(QString, QString);
+
+private:
+ QLabel *info1;
+ QLabel *info2;
+ QPushButton *cancelbutton;
+ QPushButton *statsbutton;
+ QString prettyPrintVolume(unsigned int);
+ int minutes;
+ int seconds;
+ int hours;
+ int days;
+ QFrame *fline;
+ QLabel *timelabel1;
+ QLabel *timelabel2;
+ QLabel *total_bill, *total_bill_l;
+ QLabel *session_bill, *session_bill_l;
+ QString caption_string;
+ QString time_string2;
+ QString time_string;
+ QTimer *clocktimer;
+ QVBoxLayout *tl1;
+ QLabel *vollabel;
+ QLabel *volinfo;
+ PPPStats *stats;
+ bool accountingEnabled;
+ bool volumeAccountingEnabled;
+};
+
+
+#endif
+
+
+
+
+
diff --git a/kppp/debug.cpp b/kppp/debug.cpp
new file mode 100644
index 00000000..3a0939d3
--- /dev/null
+++ b/kppp/debug.cpp
@@ -0,0 +1,140 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ * Copyright (C) 1997 Bernd Wuebben
+ * wuebben@math.cornel.edu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "debug.h"
+#include "main.h"
+#include "pppdata.h"
+#include <klocale.h>
+
+#include <assert.h>
+
+extern KPPPWidget *p_kppp;
+
+myMultiEdit::myMultiEdit(QWidget *parent, const char *name)
+ : QMultiLineEdit(parent, name)
+{
+ setReadOnly(true);
+}
+
+void myMultiEdit::insertChar(unsigned char c) {
+ QMultiLineEdit::insert(QChar(c));
+}
+
+
+void myMultiEdit::newLine() {
+ QMultiLineEdit::newLine();
+}
+
+
+DebugWidget::DebugWidget(QWidget *parent, const char *name)
+ : QDialog(parent, name, FALSE)
+{
+ setCaption(i18n("Login Script Debug Window"));
+
+ text_window = new myMultiEdit(this,"debugwindow");
+ text_window->setGeometry(2,5,400, 300);
+ // text_window->setReadOnly(FALSE);
+
+ statuslabel = new QLabel("", this, "statuslabel");
+
+ statuslabel->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ statuslabel->setAlignment( AlignLeft|AlignVCenter );
+ statuslabel->setGeometry(2, 307, 400, 20);
+ //statusPageLabel->setFont( KGlobalSettings::generalFont() );
+
+ dismiss = new QPushButton(this);
+ dismiss->setGeometry(330,340,70,30);
+ dismiss->setText(i18n("&Close"));
+ dismiss->setFocus();
+ connect(dismiss, SIGNAL(clicked()), SLOT(hide()));
+
+
+ /* fline = new QFrame(this,"line");
+ fline->setFrameStyle(QFrame::HLine |QFrame::Sunken);
+ fline->setGeometry(2,332,398,5);*/
+ adjustSize();
+ setMinimumSize(width(),height());
+
+}
+
+void DebugWidget::hideEvent(QHideEvent *)
+{
+ assert(p_kppp);
+ p_kppp->con->debug->setOn(false);
+}
+
+void DebugWidget::clear() {
+ text_window->clear();
+}
+
+
+void DebugWidget::addChar(unsigned char c) {
+ QString stuff;
+
+ if(c == '\r' || c == '\n') {
+ if(c == '\n')
+ text_window->newLine();
+ } else
+ text_window->insertChar(c);
+}
+
+
+void DebugWidget::statusLabel(const QString &s) {
+ statuslabel->setText(s);
+}
+
+
+/*
+void DebugWidget::keyPressEvent(QKeyEvent *k) {
+}
+
+*/
+void DebugWidget::resizeEvent(QResizeEvent *e){
+ int w = width() ;
+ int h = height();
+ e = e;
+
+ text_window->setGeometry(2,5,w - 2 ,h - 63);
+ statuslabel->setGeometry(2, h - 56 , w -2 , 20);
+ dismiss->setGeometry(w - 72 , h - 32, 70, 30);
+ // fline->setGeometry(2,h -70 ,w - 4,5);
+}
+
+
+void DebugWidget::enter() {
+ text_window->append("\r\n");
+}
+
+
+void DebugWidget::toggleVisibility() {
+ if(isVisible())
+ hide();
+ else
+ show();
+
+ bool showlog = isVisible();
+ gpppdata.set_show_log_window(showlog);
+}
+
+
+#include "debug.moc"
+
diff --git a/kppp/debug.h b/kppp/debug.h
new file mode 100644
index 00000000..e2454f0c
--- /dev/null
+++ b/kppp/debug.h
@@ -0,0 +1,70 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ * Copyright (C) 1997 Bernd Wuebben
+ * wuebben@math.cornel.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DEBUGWIDGET_
+#define _DEBUGWIDGET_
+
+#include <qdialog.h>
+#include <qstring.h>
+#include <qlabel.h>
+#include <qmultilineedit.h>
+#include <qpushbutton.h>
+
+
+class myMultiEdit : public QMultiLineEdit {
+public:
+
+ myMultiEdit(QWidget *parent=0, const char *name=0);
+
+ void newLine();
+ void insertChar(unsigned char c);
+};
+
+
+class DebugWidget : public QDialog {
+ Q_OBJECT
+public:
+ DebugWidget(QWidget *parent=0, const char *name=0);
+
+ void clear();
+
+public slots:
+ void statusLabel(const QString &);
+ void toggleVisibility();
+ void addChar(unsigned char);
+
+protected:
+ virtual void hideEvent(QHideEvent *);
+ virtual void resizeEvent(QResizeEvent *e);
+
+private:
+ void enter();
+ QFrame *fline;
+ QPushButton *dismiss;
+ myMultiEdit *text_window;
+
+ QLabel *statuslabel;
+};
+
+#endif
+
diff --git a/kppp/devices.h b/kppp/devices.h
new file mode 100644
index 00000000..9a78b5b0
--- /dev/null
+++ b/kppp/devices.h
@@ -0,0 +1,98 @@
+#ifndef _DEVICES_H_
+#define _DEVICES_H_
+
+#include "kpppconfig.h"
+
+static const char *devices[] = {
+#ifdef __FreeBSD__
+#if __FreeBSD_version > 600004
+ "/dev/cuad0",
+ "/dev/cuad1",
+ "/dev/cuad2",
+ "/dev/cuad3",
+ "/dev/cuad4",
+#else
+ "/dev/cuaa0",
+ "/dev/cuaa1",
+ "/dev/cuaa2",
+ "/dev/cuaa3",
+ "/dev/cuaa4",
+#endif
+#elif defined(__NetBSD__)
+ "/dev/tty00", /* "normal" modem lines */
+ "/dev/tty01",
+ "/dev/tty02",
+ "/dev/tty03",
+ "/dev/dty00", /* Dial out devices */
+ "/dev/dty01",
+ "/dev/dty02",
+ "/dev/dty03",
+ "/dev/ttyU0", /* USB stuff modems */
+ "/dev/ttyU1",
+ "/dev/ttyU2",
+ "/dev/ttyU3",
+ "/dev/dtyU0", /* USB stuff, too (dial out device) */
+ "/dev/dtyU1",
+ "/dev/dtyU2",
+ "/dev/dtyU3",
+#elif defined (__linux__)
+ "/dev/modem",
+ "/dev/ttyS0",
+ "/dev/ttyS1",
+ "/dev/ttyS2",
+ "/dev/ttyS3",
+ "/dev/ttyS4",
+#ifdef ISDNSUPPORT
+ "/dev/ttyI0",
+ "/dev/ttyI1",
+ "/dev/ttyI2",
+ "/dev/ttyI3",
+#endif
+ "/dev/usb/ttyACM0", /* USB stuff modems */
+ "/dev/usb/ttyACM1",
+ "/dev/usb/ttyACM2",
+ "/dev/usb/ttyACM3",
+ "/dev/usb/ttyUSB0", /* USB stuff modems */
+ "/dev/usb/ttyUSB1",
+ "/dev/usb/ttyUSB2",
+ "/dev/usb/ttyUSB3",
+ "/dev/ttyACM0", /* USB stuff modems with udev */
+ "/dev/ttyACM1",
+ "/dev/ttyACM2",
+ "/dev/ttyACM3",
+ "/dev/ttyUSB0",
+ "/dev/ttyUSB1",
+ "/dev/ttyUSB2",
+ "/dev/ttyUSB3",
+ "/dev/usb/tts/0", /* USB stuff modems with devfs*/
+ "/dev/usb/tts/1",
+ "/dev/usb/tts/2",
+ "/dev/usb/tts/3",
+ "/dev/rfcomm0", /* BlueTooth */
+ "/dev/rfcomm1",
+ "/dev/rfcomm2",
+ "/dev/rfcomm3",
+ "/dev/ircomm0", /* IrDA */
+ "/dev/ircomm1",
+ "/dev/ircomm2",
+ "/dev/ircomm3",
+ "/dev/ttySL0", /* necessary for slmodem driver http://www.smlink.com*/
+ "/dev/ttySL1",
+ "/dev/ttySL2",
+ "/dev/ttySL3",
+ "/dev/ttySHSF0",
+ "/dev/ttySHSF1",
+ "/dev/ttySHSF2",
+ "/dev/ttySHSF3", /* necessary for conexant modem which use hsfserial commercial module */
+#elif defined(__svr4__)
+ "/dev/cua/a",
+ "/dev/cua/b",
+ "/dev/ttya",
+ "/dev/ttyb",
+#endif
+ 0};
+
+// default device number from the list above
+const int DEV_DEFAULT = 0;
+
+#endif
diff --git a/kppp/docking.cpp b/kppp/docking.cpp
new file mode 100644
index 00000000..5375dd74
--- /dev/null
+++ b/kppp/docking.cpp
@@ -0,0 +1,148 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <kwin.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+
+#include "docking.h"
+#include "main.h"
+#include "pppstats.h"
+
+extern KPPPWidget *p_kppp;
+
+// static member
+DockWidget *DockWidget::dock_widget = 0;
+
+DockWidget::DockWidget(QWidget *parent, const char *name, PPPStats *st)
+ : KSystemTray(parent, name), stats(st) {
+
+ // load pixmaps
+ dock_none_pixmap = UserIcon("dock_none");
+ dock_left_pixmap = UserIcon("dock_left");
+ dock_right_pixmap = UserIcon("dock_right");
+ dock_both_pixmap = UserIcon("dock_both");
+
+ // popup menu for right mouse button
+ popup_m = contextMenu();
+ toggleID = popup_m->insertItem(i18n("Restore"),
+ this, SLOT(toggle_window_state()));
+ popup_m->insertItem(i18n("Details"), p_kppp, SLOT(showStats()));
+ popup_m->insertSeparator();
+ popup_m->insertItem(i18n("Disconnect"), p_kppp, SLOT(disconnect()));
+ // connect to stats for little modem animation
+ connect(stats, SIGNAL(statsChanged(int)), SLOT(paintIcon(int)));
+
+ DockWidget::dock_widget = this;
+}
+
+
+DockWidget::~DockWidget() {
+ DockWidget::dock_widget = 0;
+}
+
+
+void DockWidget::paintEvent (QPaintEvent *) {
+ paintIcon(PPPStats::BytesNone);
+}
+
+
+void DockWidget::paintIcon (int status) {
+ // animate modem lights
+
+ const QPixmap *pixmap;
+
+ if(isVisible()) {
+ switch(status)
+ {
+ case PPPStats::BytesBoth:
+ pixmap = &dock_both_pixmap;
+ break;
+ case PPPStats::BytesIn:
+ pixmap = &dock_left_pixmap;
+ break;
+ case PPPStats::BytesOut:
+ pixmap = &dock_right_pixmap;
+ break;
+ case PPPStats::BytesNone:
+ default:
+ pixmap = &dock_none_pixmap;
+ break;
+ }
+
+ bitBlt(this, 0, 0, pixmap);
+ }
+}
+
+
+void DockWidget::take_stats() {
+ if (isVisible()) {
+ stats->initStats();
+ stats->start();
+ }
+}
+
+
+void DockWidget::stop_stats() {
+ stats->stop();
+}
+
+
+void DockWidget::mousePressEvent(QMouseEvent *e) {
+ // open/close connect-window on right mouse button
+ if ( e->button() == LeftButton ) {
+ toggle_window_state();
+ }
+
+ // open popup menu on left mouse button
+ if ( e->button() == RightButton ) {
+ QString text;
+ if(p_kppp->con_win->isVisible())
+ text = i18n("Minimize");
+ else
+ text = i18n("Restore");
+
+ popup_m->changeItem(text, toggleID);
+ popup_m->popup(e->globalPos());
+ popup_m->exec();
+ }
+}
+
+
+void DockWidget::toggle_window_state() {
+ // restore/hide connect-window
+ if(p_kppp != 0L) {
+ if (p_kppp->con_win->isVisible())
+ p_kppp->con_win->hide();
+ else {
+ p_kppp->con_win->show();
+ KWin::activateWindow(p_kppp->con_win->winId());
+ }
+ }
+}
+
+#include "docking.moc"
diff --git a/kppp/docking.h b/kppp/docking.h
new file mode 100644
index 00000000..870a5125
--- /dev/null
+++ b/kppp/docking.h
@@ -0,0 +1,70 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _DOCKING_H_
+#define _DOCKING_H_
+
+#include <qpixmap.h>
+#include <ksystemtray.h>
+
+class PPPStats;
+
+class DockWidget : public KSystemTray {
+ Q_OBJECT
+public:
+ DockWidget(QWidget * parent, const char *name, PPPStats *st);
+ ~DockWidget();
+
+protected:
+ void paintEvent(QPaintEvent *e);
+ void showEvent(QShowEvent *) { }
+
+private slots:
+ void toggle_window_state();
+ void mousePressEvent(QMouseEvent *e);
+
+public slots:
+ void paintIcon(int);
+ void take_stats();
+ void stop_stats();
+
+public:
+ static DockWidget *dock_widget;
+
+private:
+ int toggleID;
+ PPPStats *stats;
+
+ KPopupMenu *popup_m;
+
+ QPixmap dock_none_pixmap;
+ QPixmap dock_left_pixmap;
+ QPixmap dock_right_pixmap;
+ QPixmap dock_both_pixmap;
+};
+
+#endif
diff --git a/kppp/edit.cpp b/kppp/edit.cpp
new file mode 100644
index 00000000..36df7c50
--- /dev/null
+++ b/kppp/edit.cpp
@@ -0,0 +1,1239 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <termios.h>
+#include <qlayout.h>
+#include <kmessagebox.h>
+#include <qwhatsthis.h>
+#include <qregexp.h>
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kbuttonbox.h>
+#include <klocale.h>
+#include <qvgroupbox.h>
+#include <qhbox.h>
+#include <kdialog.h>
+#include <kwin.h>
+
+#include "edit.h"
+#include "pppdata.h"
+#include "newwidget.h"
+#include "iplined.h"
+#include "auth.h"
+
+DialWidget::DialWidget( QWidget *parent, bool isnewaccount, const char *name )
+ : QWidget(parent, name)
+{
+ const int GRIDROWS = 8;
+
+ QGridLayout *tl = new QGridLayout(parent, GRIDROWS, 2, 0, KDialog::spacingHint());
+
+ connect_label = new QLabel(i18n("Connection &name:"), parent);
+ tl->addWidget(connect_label, 0, 0);
+
+ connectname_l = new QLineEdit(parent);
+ connectname_l->setMaxLength(ACCNAME_SIZE);
+ connect_label->setBuddy(connectname_l);
+
+ tl->addWidget(connectname_l, 0, 1);
+ QString tmp = i18n("Type in a unique name for this connection");
+
+ QWhatsThis::add(connect_label,tmp);
+ QWhatsThis::add(connectname_l,tmp);
+
+
+ number_label = new QLabel(i18n("P&hone number:"), parent);
+ number_label->setAlignment(AlignTop|AlignLeft);
+ tl->addWidget(number_label, 1, 0);
+
+ QHBoxLayout *lpn = new QHBoxLayout(5);
+ tl->addLayout(lpn, 1, 1);
+ numbers = new QListBox(parent);
+ number_label->setBuddy(numbers);
+ numbers->setMinimumSize(120, 70);
+ lpn->addWidget(numbers);
+ QVBoxLayout *lpn1 = new QVBoxLayout;
+ lpn->addLayout(lpn1);
+ add = new QPushButton(i18n("&Add..."), parent);
+ del = new QPushButton(i18n("&Remove"), parent);
+
+ up = new QPushButton(parent);
+ up->setIconSet(BarIconSet("up"));
+ down = new QPushButton(parent);
+ down->setIconSet(BarIconSet("down"));
+ lpn1->addWidget(add);
+ lpn1->addWidget(del);
+ lpn1->addStretch(1);
+ lpn1->addWidget(up);
+ lpn1->addWidget(down);
+ connect(add, SIGNAL(clicked()),
+ this, SLOT(addNumber()));
+ connect(del, SIGNAL(clicked()),
+ this, SLOT(delNumber()));
+ connect(up, SIGNAL(clicked()),
+ this, SLOT(upNumber()));
+ connect(down, SIGNAL(clicked()),
+ this, SLOT(downNumber()));
+ connect(numbers, SIGNAL(highlighted(int)),
+ this, SLOT(selectionChanged(int)));
+ numbersChanged();
+
+ tmp = i18n("<p>Specifies the phone numbers to dial. You\n"
+ "can supply multiple numbers here, simply\n"
+ "click on \"Add\". You can arrange the\n"
+ "order the numbers are tried by using the\n"
+ "arrow buttons.\n\n"
+ "When a number is busy or fails, <i>kppp</i> will \n"
+ "try the next number and so on");
+
+ QWhatsThis::add(number_label,tmp);
+ QWhatsThis::add(numbers,tmp);
+
+ auth_l = new QLabel(i18n("A&uthentication:"), parent);
+ tl->addWidget(auth_l, 3, 0);
+
+ auth = new QComboBox(parent);
+ auth_l->setBuddy(auth);
+ auth->insertItem(i18n("Script-based"));
+ auth->insertItem(i18n("PAP"));
+ auth->insertItem(i18n("Terminal-based"));
+ auth->insertItem(i18n("CHAP"));
+ auth->insertItem(i18n("PAP/CHAP"));
+ tl->addWidget(auth, 3, 1);
+ tmp = i18n("<p>Specifies the method used to identify yourself to\n"
+ "the PPP server. Most universities still use\n"
+ "<b>Terminal</b>- or <b>Script</b>-based authentication,\n"
+ "while most ISP use <b>PAP</b> and/or <b>CHAP</b>. If\n"
+ "unsure, contact your ISP.\n"
+ "\n"
+ "If you can choose between PAP and CHAP,\n"
+ "choose CHAP, because it's much safer. If you don't know\n"
+ "whether PAP or CHAP is right, choose PAP/CHAP.");
+
+ QWhatsThis::add(auth_l,tmp);
+ QWhatsThis::add(auth,tmp);
+
+ store_password = new QCheckBox(i18n("Store &password"), parent);
+ store_password->setChecked(true);
+ tl->addMultiCellWidget(store_password, 4, 4, 0, 1, AlignRight);
+ QWhatsThis::add(store_password,
+ i18n("<p>When this is turned on, your ISP password\n"
+ "will be saved in <i>kppp</i>'s config file, so\n"
+ "you do not need to type it in every time.\n"
+ "\n"
+ "<b><font color=\"red\">Warning:</font> your password will be stored as\n"
+ "plain text in the config file, which is\n"
+ "readable only to you. Make sure nobody\n"
+ "gains access to this file!"));
+
+ cbtype_l = new QLabel(i18n("&Callback type:"), parent);
+ tl->addWidget(cbtype_l, 5, 0);
+
+ cbtype = new QComboBox(parent);
+ cbtype_l->setBuddy(cbtype);
+ cbtype->insertItem(i18n("None"));
+ cbtype->insertItem(i18n("Administrator-defined"));
+ cbtype->insertItem(i18n("User-defined"));
+ connect(cbtype, SIGNAL(highlighted(int)),
+ this, SLOT(cbtypeChanged(int)));
+ tl->addWidget(cbtype, 5, 1);
+ tmp = i18n("Callback type");
+
+ QWhatsThis::add(cbtype_l,tmp);
+ QWhatsThis::add(cbtype,tmp);
+
+ cbphone_l = new QLabel(i18n("Call&back number:"), parent);
+ tl->addWidget(cbphone_l, 6, 0);
+
+ cbphone = new QLineEdit(parent);
+ cbphone_l->setBuddy(cbphone);
+ cbphone->setMaxLength(140);
+ tl->addWidget(cbphone, 6, 1);
+ tmp = i18n("Callback phone number");
+
+ QWhatsThis::add(cbphone_l,tmp);
+ QWhatsThis::add(cbphone,tmp);
+
+ pppdargs = new QPushButton(i18n("Customize &pppd Arguments..."), parent);
+ connect(pppdargs, SIGNAL(clicked()), SLOT(pppdargsbutton()));
+ tl->addMultiCellWidget(pppdargs, 7, 7, 0, 1, AlignCenter);
+
+ // Set defaults if editing an existing connection
+ if(!isnewaccount) {
+ connectname_l->setText(gpppdata.accname());
+
+ // insert the phone numbers into the listbox
+ QString n = gpppdata.phonenumber();
+ QString tmp = "";
+ uint idx = 0;
+ while(idx != n.length()) {
+ if(n[idx] == ':') {
+ if(tmp.length() > 0)
+ numbers->insertItem(tmp);
+ tmp = "";
+ } else
+ tmp += n[idx];
+ idx++;
+ }
+ if(tmp.length() > 0)
+ numbers->insertItem(tmp);
+
+ auth->setCurrentItem(gpppdata.authMethod());
+ store_password->setChecked(gpppdata.storePassword());
+ cbtype->setCurrentItem(gpppdata.callbackType());
+ cbphone->setText(gpppdata.callbackPhone());
+ } else {
+ // select PAP/CHAP as default
+ auth->setCurrentItem(AUTH_PAPCHAP);
+ // select NONE as default
+ cbtype->setCurrentItem(CBTYPE_NONE);
+ }
+
+ emit cbtypeChanged(cbtype->currentItem());
+ numbersChanged();
+ tl->activate();
+}
+
+
+bool DialWidget::save() {
+ //first check to make sure that the account name is unique!
+ if(connectname_l->text().isEmpty() ||
+ !gpppdata.isUniqueAccname(connectname_l->text())) {
+ return false;
+ } else {
+ gpppdata.setAccname(connectname_l->text());
+
+ QString number = "";
+ for(uint i = 0; i < numbers->count(); i++) {
+ if(i != 0)
+ number += ":";
+ number += numbers->text(i);
+ }
+
+ gpppdata.setPhonenumber(number);
+ gpppdata.setAuthMethod(auth->currentItem());
+ gpppdata.setStorePassword(store_password->isChecked());
+ gpppdata.setCallbackType(cbtype->currentItem());
+ gpppdata.setCallbackPhone(cbphone->text());
+ return true;
+ }
+}
+
+
+void DialWidget::numbersChanged() {
+ int sel = numbers->currentItem();
+
+ del->setEnabled(sel != -1);
+ up->setEnabled(sel != -1 && sel != 0);
+ down->setEnabled(sel != -1 && sel != (int)numbers->count()-1);
+}
+
+void DialWidget::cbtypeChanged(int value) {
+ cbphone_l->setEnabled(value == CBTYPE_USER);
+ cbphone->setEnabled(value == CBTYPE_USER);
+}
+
+void DialWidget::selectionChanged(int) {
+ numbersChanged();
+}
+
+
+void DialWidget::addNumber() {
+ PhoneNumberDialog dlg(this);
+ if(dlg.exec()) {
+ numbers->insertItem(dlg.phoneNumber());
+ numbersChanged();
+ }
+}
+
+
+void DialWidget::delNumber() {
+ if(numbers->currentItem() != -1) {
+ numbers->removeItem(numbers->currentItem());
+ numbersChanged();
+ }
+}
+
+
+void DialWidget::upNumber() {
+ int idx = numbers->currentItem();
+ if(idx != -1) {
+ QString item = numbers->text(idx);
+ numbers->removeItem(idx);
+ numbers->insertItem(item, idx-1);
+ numbers->setCurrentItem(idx-1);
+ numbersChanged();
+ }
+}
+
+
+void DialWidget::downNumber() {
+ int idx = numbers->currentItem();
+ if(idx != -1) {
+ QString item = numbers->text(idx);
+ numbers->removeItem(idx);
+ numbers->insertItem(item, idx+1);
+ numbers->setCurrentItem(idx+1);
+ numbersChanged();
+ }
+}
+
+
+void DialWidget::pppdargsbutton() {
+ PPPdArguments pa(this);
+ pa.exec();
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ExecWidget
+/////////////////////////////////////////////////////////////////////////////
+ExecWidget::ExecWidget(QWidget *parent, bool isnewaccount, const char *name) :
+ QWidget(parent, name)
+{
+ QVBoxLayout *tl = new QVBoxLayout(parent, 0, KDialog::spacingHint());
+
+ QLabel *l = new QLabel(\
+i18n("Here you can select commands to run at certain stages of the\n"
+ "connection. The commands are run with your real user id, so\n"
+ "you cannot run any commands here requiring root permissions\n"
+ "(unless, of course, you are root).\n\n"
+ "Be sure to supply the whole path to the program otherwise\n"
+ "kppp might be unable to find it."), parent);
+ l->setMinimumHeight(l->sizeHint().height());
+ tl->addWidget(l);
+ tl->addStretch(1);
+
+ QGridLayout *l1 = new QGridLayout(4, 2, 10);
+ tl->addLayout(l1);
+ l1->setColStretch(0, 0);
+ l1->setColStretch(1, 1);
+
+ before_connect_l = new QLabel(i18n("&Before connect:"), parent);
+ before_connect_l->setAlignment(AlignVCenter);
+ l1->addWidget(before_connect_l, 0, 0);
+ before_connect = new QLineEdit(parent);
+ before_connect_l->setBuddy(before_connect);
+ before_connect->setMaxLength(COMMAND_SIZE);
+ l1->addWidget(before_connect, 0, 1);
+ QString tmp = i18n("Allows you to run a program <b>before</b> a connection\n"
+ "is established. It is called immediately before\n"
+ "dialing has begun.\n\n"
+ "This might be useful, e.g. to stop HylaFAX blocking the\n"
+ "modem.");
+
+ QWhatsThis::add(before_connect_l,tmp);
+ QWhatsThis::add(before_connect,tmp);
+
+ command_label = new QLabel(i18n("&Upon connect:"), parent);
+ command_label->setAlignment(AlignVCenter);
+ l1->addWidget(command_label, 1, 0);
+ command = new QLineEdit(parent);
+ command_label->setBuddy(command);
+ command->setMaxLength(COMMAND_SIZE);
+ l1->addWidget(command, 1, 1);
+ tmp = i18n("Allows you to run a program <b>after</b> a connection\n"
+ "is established. When your program is called, all\n"
+ "preparations for an Internet connection are finished.\n"
+ "\n"
+ "Very useful for fetching mail and news");
+
+ QWhatsThis::add(command_label,tmp);
+ QWhatsThis::add(command,tmp);
+
+ predisconnect_label = new QLabel(i18n("Before &disconnect:"),
+ parent);
+ predisconnect_label->setAlignment(AlignVCenter);
+ l1->addWidget(predisconnect_label, 2, 0);
+ predisconnect = new QLineEdit(parent);
+ predisconnect_label->setBuddy(predisconnect);
+ predisconnect->setMaxLength(COMMAND_SIZE);
+ l1->addWidget(predisconnect, 2, 1);
+ tmp = i18n("Allows you to run a program <b>before</b> a connection\n"
+ "is closed. The connection will stay open until\n"
+ "the program exits.");
+
+ QWhatsThis::add(predisconnect_label,tmp);
+ QWhatsThis::add(predisconnect,tmp);
+
+ discommand_label = new QLabel(i18n("U&pon disconnect:"),
+ parent);
+ discommand_label->setAlignment(AlignVCenter);
+ l1->addWidget(discommand_label, 3, 0);
+
+ discommand = new QLineEdit(parent);
+ discommand_label->setBuddy(discommand);
+ discommand->setMaxLength(COMMAND_SIZE);
+ l1->addWidget(discommand, 3, 1);
+ tmp = i18n("Allows you to run a program <b>after</b> a connection\n"
+ "has been closed.");
+
+ QWhatsThis::add(discommand_label,tmp);
+ QWhatsThis::add(discommand,tmp);
+
+ // extra space between entries
+ l1->addRowSpacing(1, 5);
+ l1->addRowSpacing(3, 5);
+
+ tl->addStretch(1);
+ tl->activate();
+
+ // Set defaults if editing an existing connection
+ if(!isnewaccount) {
+ before_connect->setText(gpppdata.command_before_connect());
+ command->setText(gpppdata.command_on_connect());
+ discommand->setText(gpppdata.command_on_disconnect());
+ predisconnect->setText(gpppdata.command_before_disconnect());
+ }
+}
+
+
+bool ExecWidget::save() {
+ gpppdata.setCommand_before_connect(before_connect->text());
+ gpppdata.setCommand_on_connect(command->text());
+ gpppdata.setCommand_before_disconnect(predisconnect->text());
+ gpppdata.setCommand_on_disconnect(discommand->text());
+ return true;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// IPWidget
+//
+/////////////////////////////////////////////////////////////////////////////
+IPWidget::IPWidget( QWidget *parent, bool isnewaccount, const char *name )
+ : QWidget(parent, name)
+{
+ QVBoxLayout *topLayout = new QVBoxLayout(parent);
+ topLayout->setSpacing(KDialog::spacingHint());
+
+ box = new QVGroupBox(i18n("C&onfiguration"), parent);
+ box->setInsideSpacing(KDialog::spacingHint());
+
+ rb = new QButtonGroup(parent);
+ rb->hide();
+ connect(rb, SIGNAL(clicked(int)),
+ SLOT(hitIPSelect(int)));
+
+ dynamicadd_rb = new QRadioButton(box);
+ dynamicadd_rb->setText(i18n("Dynamic IP address"));
+ QWhatsThis::add(dynamicadd_rb,
+ i18n("Select this option when your computer gets an\n"
+ "internet address (IP) every time a\n"
+ "connection is made.\n"
+ "\n"
+ "Almost every Internet Service Provider uses\n"
+ "this method, so this should be turned on."));
+
+ staticadd_rb = new QRadioButton(box);
+ staticadd_rb->setText(i18n("Static IP address"));
+ rb->insert(dynamicadd_rb, 0);
+ rb->insert(staticadd_rb, 1);
+ QWhatsThis::add(staticadd_rb,
+ i18n("Select this option when your computer has a\n"
+ "fixed internet address (IP). Most computers\n"
+ "don't have this, so you should probably select\n"
+ "dynamic IP addressing unless you know what you\n"
+ "are doing."));
+
+ QWidget *ipWidget = new QWidget(box);
+ QGridLayout *ipLayout = new QGridLayout(ipWidget, 2, 2);
+ ipLayout->setSpacing(KDialog::spacingHint());
+
+ ipaddress_label = new QLabel(i18n("&IP address:"), ipWidget);
+ QString tmp = i18n("If your computer has a permanent internet\n"
+ "address, you must supply your IP address here.");
+ ipLayout->addWidget(ipaddress_label, 0, 0);
+
+ ipaddress_l = new IPLineEdit(ipWidget);
+ ipaddress_label->setBuddy(ipaddress_l);
+ ipLayout->addWidget(ipaddress_l, 0, 1);
+
+ QWhatsThis::add(ipaddress_label,tmp);
+ QWhatsThis::add(ipaddress_l,tmp);
+
+ sub_label = new QLabel(i18n("&Subnet mask:"), ipWidget);
+ tmp = i18n("<p>If your computer has a static Internet address,\n"
+ "you must supply a network mask here. In almost\n"
+ "all cases this netmask will be <b>255.255.255.0</b>,\n"
+ "but your mileage may vary.\n"
+ "\n"
+ "If unsure, contact your Internet Service Provider");
+ ipLayout->addWidget(sub_label, 1, 0);
+
+ subnetmask_l = new IPLineEdit(ipWidget);
+ sub_label->setBuddy(subnetmask_l);
+ ipLayout->addWidget(subnetmask_l, 1, 1);
+
+ QWhatsThis::add(sub_label,tmp);
+ QWhatsThis::add(subnetmask_l,tmp);
+
+ autoname = new QCheckBox(i18n("&Auto-configure hostname from this IP"), parent);
+ autoname->setChecked(gpppdata.autoname());
+ connect(autoname,SIGNAL(toggled(bool)),
+ this,SLOT(autoname_t(bool)));
+
+ QWhatsThis::add(autoname,
+ i18n("<p>Whenever you connect, this reconfigures\n"
+ "your hostname to match the IP address you\n"
+ "got from the PPP server. This may be useful\n"
+ "if you need to use a protocol which depends\n"
+ "on this information, but it can also cause several\n"
+ "<a href=\"kppp-7.html#autohostname\">problems</a>.\n"
+ "\n"
+ "Do not enable this unless you really need it."));
+
+ topLayout->addWidget(box);
+ topLayout->addWidget(autoname);
+ topLayout->addStretch();
+
+ //load info from gpppdata
+ if(!isnewaccount) {
+ if(gpppdata.ipaddr() == "0.0.0.0" &&
+ gpppdata.subnetmask() == "0.0.0.0") {
+ dynamicadd_rb->setChecked(true);
+ hitIPSelect(0);
+ autoname->setChecked(gpppdata.autoname());
+ }
+ else {
+ ipaddress_l->setText(gpppdata.ipaddr());
+ subnetmask_l->setText(gpppdata.subnetmask());
+ staticadd_rb->setChecked(true);
+ autoname->setChecked(false);
+ }
+ }
+ else {
+ dynamicadd_rb->setChecked(true);
+ hitIPSelect(0);
+ }
+
+}
+
+void IPWidget::autoname_t(bool on) {
+ static bool was_warned = false;
+
+ // big-fat warning when selecting the auto configure hostname option
+ if(on && !was_warned) {
+ KMessageBox::information(this,
+ i18n("Selecting this option might cause some weird "
+ "problems with the X-server and applications "
+ "while kppp is connected. Don't use it until "
+ "you know what you are doing!\n"
+ "For more information take a look at the "
+ "handbook (or help) in the section \"Frequently "
+ "asked questions\"."),
+ i18n("Warning"));
+ was_warned = true;
+ }
+}
+
+
+void IPWidget::save() {
+ if(dynamicadd_rb->isChecked()) {
+ gpppdata.setIpaddr("0.0.0.0");
+ gpppdata.setSubnetmask("0.0.0.0");
+ } else {
+ gpppdata.setIpaddr(ipaddress_l->text());
+ gpppdata.setSubnetmask(subnetmask_l->text());
+ }
+ gpppdata.setAutoname(autoname->isChecked());
+}
+
+
+void IPWidget::hitIPSelect( int i ) {
+ if(i == 0) {
+ ipaddress_label->setEnabled(false);
+ sub_label->setEnabled(false);
+ ipaddress_l->setEnabled(false);
+ subnetmask_l->setEnabled(false);
+ }
+ else {
+ ipaddress_label->setEnabled(true);
+ sub_label->setEnabled(true);
+ ipaddress_l->setEnabled(true);
+ subnetmask_l->setEnabled(true);
+ }
+}
+
+
+
+DNSWidget::DNSWidget( QWidget *parent, bool isnewaccount, const char *name )
+ : QWidget(parent, name)
+{
+ // box = new QGroupBox(parent);
+ QGridLayout *tl = new QGridLayout(parent, 7, 2, 0, KDialog::spacingHint());
+
+ dnsdomain_label = new QLabel(i18n("Domain &name:"), parent);
+ tl->addWidget(dnsdomain_label, 0, 0);
+
+ dnsdomain = new QLineEdit(parent);
+ dnsdomain_label->setBuddy(dnsdomain);
+ dnsdomain->setMaxLength(DOMAIN_SIZE);
+ tl->addWidget(dnsdomain, 0, 1);
+ QString tmp = i18n("If you enter a domain name here, this domain\n"
+ "name is used for your computer while you are\n"
+ "connected. When the connection is closed, the\n"
+ "original domain name of your computer is\n"
+ "restored.\n"
+ "\n"
+ "If you leave this field blank, no changes are\n"
+ "made to the domain name.");
+
+ QWhatsThis::add(dnsdomain_label,tmp);
+ QWhatsThis::add(dnsdomain,tmp);
+
+ conf_label = new QLabel(i18n("C&onfiguration:"), parent);
+ tl->addWidget(conf_label, 1, 0);
+
+ bg = new QButtonGroup("Group", this);
+ conf_label->setBuddy(bg);
+ connect(bg, SIGNAL(clicked(int)), SLOT(DNS_Mode_Selected(int)));
+ bg->hide();
+
+ autodns = new QRadioButton(i18n("Automatic"), parent);
+ bg->insert(autodns, 0);
+ tl->addWidget(autodns, 1, 1);
+ // no automatic DNS detection for pppd < 2.3.7
+ if(!gpppdata.pppdVersionMin(2, 3, 7))
+ autodns->setEnabled(false);
+
+ mandns = new QRadioButton(i18n("Manual"), parent);
+ bg->insert(mandns, 1);
+ tl->addWidget(mandns, 2, 1);
+
+ dns_label = new QLabel(i18n("DNS &IP address:"), parent);
+ tl->addWidget(dns_label, 3, 0);
+
+ QHBoxLayout *l2 = new QHBoxLayout;
+ tl->addLayout(l2, 3, 1);
+ dnsipaddr = new IPLineEdit(parent);
+ dns_label->setBuddy(dnsipaddr);
+ connect(dnsipaddr, SIGNAL(returnPressed()),
+ SLOT(adddns()));
+ connect(dnsipaddr, SIGNAL(textChanged(const QString &)),
+ SLOT(DNS_Edit_Changed(const QString &)));
+ l2->addWidget(dnsipaddr, 1);
+ l2->addStretch(1);
+ tmp = i18n("<p>Allows you to specify a new DNS server to be\n"
+ "used while you are connected. When the\n"
+ "connection is closed, this DNS entry will be\n"
+ "removed again.\n"
+ "\n"
+ "To add a DNS server, type in the IP address of\n"
+ "the DNS server here and click on <b>Add</b>");
+
+ QWhatsThis::add(dns_label, tmp);
+ QWhatsThis::add(dnsipaddr, tmp);
+
+ QHBoxLayout *l1 = new QHBoxLayout;
+ tl->addLayout(l1, 4, 1);
+ add = new QPushButton(i18n("&Add"), parent);
+ connect(add, SIGNAL(clicked()), SLOT(adddns()));
+ int width = add->sizeHint().width();
+ width = QMAX(width,60);
+ add->setMinimumWidth(width);
+ l1->addWidget(add);
+ l1->addStretch(1);
+ QWhatsThis::add(add,
+ i18n("Click this button to add the DNS server\n"
+ "specified in the field above. The entry\n"
+ "will then be added to the list below"));
+
+ remove = new QPushButton(i18n("&Remove"), parent);
+ connect(remove, SIGNAL(clicked()), SLOT(removedns()));
+ width = remove->sizeHint().width();
+ width = QMAX(width,60);
+ remove->setMinimumWidth(width);
+ l1->addWidget(remove);
+ QWhatsThis::add(remove,
+ i18n("Click this button to remove the selected DNS\n"
+ "server entry from the list below"));
+
+ servers_label = new QLabel(i18n("DNS address &list:"), parent);
+ servers_label->setAlignment(AlignTop|AlignLeft);
+ tl->addWidget(servers_label, 5, 0);
+
+ dnsservers = new QListBox(parent);
+ servers_label->setBuddy(dnsservers);
+ dnsservers->setMinimumSize(150, 80);
+ connect(dnsservers, SIGNAL(highlighted(int)),
+ SLOT(DNS_Entry_Selected(int)));
+ tl->addWidget(dnsservers, 5, 1);
+ tmp = i18n("<p>This shows all defined DNS servers to use\n"
+ "while you are connected. Use the <b>Add</b> and\n"
+ "<b>Remove</b> buttons to modify the list");
+
+ QWhatsThis::add(servers_label,tmp);
+ QWhatsThis::add(dnsservers,tmp);
+
+ exdnsdisabled_toggle = new QCheckBox(i18n( \
+"&Disable existing DNS servers during connection"),
+ parent);
+ exdnsdisabled_toggle->setChecked(gpppdata.exDNSDisabled());
+ tl->addMultiCellWidget(exdnsdisabled_toggle, 6, 6, 0, 1, AlignCenter);
+ QWhatsThis::add(exdnsdisabled_toggle,
+ i18n("<p>When this option is selected, all DNS\n"
+ "servers specified in <tt>/etc/resolv.conf</tt> are\n"
+ "temporary disabled while the dialup connection\n"
+ "is established. After the connection is\n"
+ "closed, the servers will be re-enabled\n"
+ "\n"
+ "Typically, there is no reason to use this\n"
+ "option, but it may become useful under \n"
+ "some circumstances."));
+
+
+ // restore data if editing
+ if(!isnewaccount) {
+ dnsservers->insertStringList(gpppdata.dns());
+ dnsdomain->setText(gpppdata.domain());
+ }
+
+ int mode = gpppdata.autoDNS() ? 0 : 1;
+ bg->setButton(mode);
+ DNS_Mode_Selected(mode);
+
+ tl->activate();
+}
+
+void DNSWidget::DNS_Edit_Changed(const QString &text) {
+ QRegExp r("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
+ add->setEnabled(text.find(r) != -1);
+}
+
+void DNSWidget::DNS_Entry_Selected(int) {
+ remove->setEnabled(true);
+}
+
+void DNSWidget::DNS_Mode_Selected(int mode) {
+ bool on = (mode == 1);
+ dns_label->setEnabled(on);
+ servers_label->setEnabled(on);
+ dnsipaddr->setText("");
+ dnsipaddr->setEnabled(on);
+ add->setEnabled(false);
+ remove->setEnabled(dnsservers->count()>0 && on);
+ dnsservers->clearSelection();
+ dnsservers->setEnabled(on);
+ dnsservers->triggerUpdate(false);
+}
+
+void DNSWidget::save() {
+ gpppdata.setAutoDNS(bg->id(bg->selected()) == 0);
+ QStringList serverlist;
+ for(uint i=0; i < dnsservers->count(); i++)
+ serverlist.append(dnsservers->text(i));
+ gpppdata.setDns(serverlist);
+
+ // strip leading dot
+ QString s(dnsdomain->text());
+ if(s.left(1) == ".")
+ gpppdata.setDomain(s.mid(1));
+ else
+ gpppdata.setDomain(dnsdomain->text());
+
+ gpppdata.setExDNSDisabled(exdnsdisabled_toggle->isChecked());
+}
+
+
+void DNSWidget::adddns() {
+ if(dnsservers->count() < MAX_DNS_ENTRIES) {
+ dnsservers->insertItem(dnsipaddr->text());
+ dnsipaddr->setText("");
+ }
+}
+
+
+void DNSWidget::removedns() {
+ int i;
+ i = dnsservers->currentItem();
+ if(i != -1)
+ dnsservers->removeItem(i);
+ remove->setEnabled(dnsservers->count()>0);
+}
+
+
+//
+// GatewayWidget
+//
+GatewayWidget::GatewayWidget( QWidget *parent, bool isnewaccount, const char *name )
+ : QWidget(parent, name)
+{
+ QVBoxLayout *topLayout = new QVBoxLayout(parent);
+ topLayout->setSpacing(KDialog::spacingHint());
+
+ box = new QVGroupBox(i18n("C&onfiguration"), parent);
+ box->setInsideSpacing(KDialog::spacingHint());
+
+ rb = new QButtonGroup(parent);
+ rb->hide();
+ connect(rb, SIGNAL(clicked(int)), SLOT(hitGatewaySelect(int)));
+
+ defaultgateway = new QRadioButton(box);
+ defaultgateway->setText(i18n("Default gateway"));
+ rb->insert(defaultgateway, 0);
+ QWhatsThis::add(defaultgateway,
+ i18n("This makes the PPP peer computer (the computer\n"
+ "you are connected to with your modem) to act as\n"
+ "a gateway. Your computer will send all packets not\n"
+ "going to a computer inside your local net to this\n"
+ "computer, which will route these packets.\n"
+ "\n"
+ "This is the default for most ISPs, so you should\n"
+ "probably leave this option on."));
+
+
+ staticgateway = new QRadioButton(box);
+ staticgateway->setText(i18n("Static gateway"));
+ rb->insert(staticgateway, 1);
+ QWhatsThis::add(staticgateway,
+ i18n("<p>Allows you to specify which computer you want\n"
+ "to use as gateway (see <i>Default Gateway</i> above)"));
+
+ QHBox *gateBox = new QHBox(box);
+ gate_label = new QLabel(i18n("Gateway &IP address:"), gateBox);
+ gatewayaddr = new IPLineEdit(gateBox);
+ gate_label->setBuddy(gatewayaddr);
+
+ defaultroute = new QCheckBox(i18n("&Assign the default route to this gateway"),
+ parent);
+ QWhatsThis::add(defaultroute,
+ i18n("If this option is enabled, all packets not\n"
+ "going to the local net are routed through\n"
+ "the PPP connection.\n"
+ "\n"
+ "Normally, you should turn this on"));
+
+ topLayout->addWidget(box);
+ topLayout->addWidget(defaultroute);
+ topLayout->addStretch();
+
+ //load info from gpppdata
+ if(!isnewaccount) {
+ if(gpppdata.gateway() == "0.0.0.0") {
+ defaultgateway->setChecked(true);
+ hitGatewaySelect(0);
+ }
+ else {
+ gatewayaddr->setText(gpppdata.gateway());
+ staticgateway->setChecked(true);
+ }
+ defaultroute->setChecked(gpppdata.defaultroute());
+ }
+ else {
+ defaultgateway->setChecked(true);
+ hitGatewaySelect(0);
+ defaultroute->setChecked(true);
+ }
+}
+
+void GatewayWidget::save() {
+ gpppdata.setGateway(gatewayaddr->text());
+ gpppdata.setDefaultroute(defaultroute->isChecked());
+}
+
+
+void GatewayWidget::hitGatewaySelect( int i ) {
+ if(i == 0) {
+ gatewayaddr->setText("0.0.0.0");
+ gatewayaddr->setEnabled(false);
+ gate_label->setEnabled(false);
+ }
+ else {
+ gatewayaddr->setEnabled(true);
+ gatewayaddr->setText("");
+ gate_label->setEnabled(true);
+ }
+}
+
+
+
+ScriptWidget::ScriptWidget( QWidget *parent, bool isnewaccount, const char *name )
+ : QWidget(parent, name)
+{
+ QVBoxLayout *tl = new QVBoxLayout(parent, 0, KDialog::spacingHint());
+ se = new ScriptEdit(parent);
+ connect(se, SIGNAL(returnPressed()), SLOT(addButton()));
+ tl->addWidget(se);
+
+ // insert equal-sized buttons
+ KButtonBox *bbox = new KButtonBox(parent);
+ add = bbox->addButton(i18n("&Add"));
+ connect(add, SIGNAL(clicked()), SLOT(addButton()));
+ bbox->addStretch(1);
+ insert = bbox->addButton(i18n("&Insert"));
+ connect(insert, SIGNAL(clicked()), SLOT(insertButton()));
+ bbox->addStretch(1);
+ remove = bbox->addButton(i18n("&Remove"));
+ connect(remove, SIGNAL(clicked()), SLOT(removeButton()));
+ bbox->layout();
+ tl->addWidget(bbox);
+
+ QHBoxLayout *l12 = new QHBoxLayout(0);
+ tl->addLayout(l12);
+ stl = new QListBox(parent);
+ stl->setVScrollBarMode( QScrollView::AlwaysOff );
+ connect(stl, SIGNAL(highlighted(int)), SLOT(stlhighlighted(int)));
+ stl->setMinimumSize(QSize(70, 140));
+
+ sl = new QListBox(parent);
+ sl->setVScrollBarMode( QScrollView::AlwaysOff );
+ connect(sl, SIGNAL(highlighted(int)), SLOT(slhighlighted(int)));
+ sl->setMinimumSize(QSize(150, 140));
+
+ slb = new QScrollBar(parent);
+ slb->setFixedWidth(slb->sizeHint().width());
+ connect(slb, SIGNAL(valueChanged(int)), SLOT(scrolling(int)));
+
+ l12->addWidget(stl, 1);
+ l12->addWidget(sl, 3);
+ l12->addWidget(slb, 0);
+
+ //load data from gpppdata
+ if(!isnewaccount) {
+ QStringList &comlist = gpppdata.scriptType();
+ QStringList &arglist = gpppdata.script();
+ QStringList::Iterator itcom = comlist.begin();
+ QStringList::Iterator itarg = arglist.begin();
+
+ for ( ;
+ itcom != comlist.end() && itarg != arglist.end();
+ ++itcom, ++itarg )
+ {
+ stl->insertItem(*itcom);
+ sl->insertItem(*itarg);
+ }
+ }
+
+ insert->setEnabled(false);
+ remove->setEnabled(false);
+ adjustScrollBar();
+ tl->activate();
+}
+
+bool ScriptWidget::check() {
+ uint lstart = 0;
+ uint lend = 0;
+ uint errcnt = 0;
+
+ if(sl->count() > 0) {
+ for( uint i=0; i <= sl->count()-1; i++) {
+ if(stl->text(i) == "LoopStart") {
+ lstart++;
+ }
+ if (stl->text(i) == "LoopEnd") {
+ lend++;
+ }
+ if ( lend > lstart ) errcnt++;
+ }
+ return ( (errcnt == 0 ) && (lstart == lend) );
+ }
+ return true;
+}
+
+
+void ScriptWidget::save() {
+ QStringList typelist, arglist;
+ for(uint i=0; i < sl->count(); i++) {
+ typelist.append(stl->text(i));
+ arglist.append(sl->text(i));
+ }
+ gpppdata.setScriptType(typelist);
+ gpppdata.setScript(arglist);
+}
+
+
+
+void ScriptWidget::adjustScrollBar() {
+ if((int)sl->count() <= sl->numItemsVisible())
+ slb->setRange(0, 0);
+ else
+ slb->setRange(0, (sl->count() - sl->numItemsVisible())+1);
+}
+
+
+void ScriptWidget::scrolling(int i) {
+ sl->setTopItem(i);
+ stl->setTopItem(i);
+}
+
+
+void ScriptWidget::slhighlighted(int i) {
+ insert->setEnabled(true);
+ remove->setEnabled(true);
+ stl->setCurrentItem(i);
+}
+
+
+void ScriptWidget::stlhighlighted(int i) {
+ insert->setEnabled(true);
+ remove->setEnabled(true);
+ sl->setCurrentItem(i);
+}
+
+
+void ScriptWidget::addButton() {
+ //don't allow more than the maximum script entries
+ if(sl->count() == MAX_SCRIPT_ENTRIES-1)
+ return;
+
+ switch(se->type()) {
+ case ScriptEdit::Expect:
+ stl->insertItem("Expect");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Send:
+ stl->insertItem("Send");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::SendNoEcho:
+ stl->insertItem("SendNoEcho");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Pause:
+ stl->insertItem("Pause");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Hangup:
+ stl->insertItem("Hangup");
+ sl->insertItem("");
+ break;
+
+ case ScriptEdit::Answer:
+ stl->insertItem("Answer");
+ sl->insertItem("");
+ break;
+
+ case ScriptEdit::Timeout:
+ stl->insertItem("Timeout");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Password:
+ stl->insertItem("Password");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::ID:
+ stl->insertItem("ID");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Prompt:
+ stl->insertItem("Prompt");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::PWPrompt:
+ stl->insertItem("PWPrompt");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::LoopStart:
+ stl->insertItem("LoopStart");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::LoopEnd:
+ stl->insertItem("LoopEnd");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Scan:
+ stl->insertItem("Scan");
+ sl->insertItem(se->text());
+ break;
+
+ case ScriptEdit::Save:
+ stl->insertItem("Save");
+ sl->insertItem(se->text());
+ break;
+
+ default:
+ break;
+ }
+
+ //get the scrollbar adjusted, and scroll the list so we can see what
+ //we're adding to
+ adjustScrollBar();
+ slb->setValue(slb->maxValue());
+
+ //clear the text in the entry box
+ se->setText("");
+}
+
+
+void ScriptWidget::insertButton() {
+ //exit if there is no highlighted item, or we've reached the
+ //maximum entries in the script list
+ if(sl->currentItem() < 0 || (sl->count() == MAX_SCRIPT_ENTRIES-1))
+ return;
+
+ switch(se->type()) {
+ case ScriptEdit::Expect:
+ stl->insertItem("Expect", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Send:
+ stl->insertItem("Send", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::SendNoEcho:
+ stl->insertItem("SendNoEcho", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Pause:
+ stl->insertItem("Pause", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Hangup:
+ stl->insertItem("Hangup", stl->currentItem());
+ sl->insertItem("", sl->currentItem());
+ break;
+
+ case ScriptEdit::Answer:
+ stl->insertItem("Answer", stl->currentItem());
+ sl->insertItem("", sl->currentItem());
+ break;
+
+ case ScriptEdit::Timeout:
+ stl->insertItem("Timeout", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Password:
+ stl->insertItem("Password", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::ID:
+ stl->insertItem("ID", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Prompt:
+ stl->insertItem("Prompt", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::PWPrompt:
+ stl->insertItem("PWPrompt", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::LoopStart:
+ stl->insertItem("LoopStart", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::LoopEnd:
+ stl->insertItem("LoopEnd", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Scan:
+ stl->insertItem("Scan", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ case ScriptEdit::Save:
+ stl->insertItem("Save", stl->currentItem());
+ sl->insertItem(se->text(), sl->currentItem());
+ break;
+
+ default:
+ break;
+ }
+ adjustScrollBar();
+ se->setText("");
+}
+
+
+void ScriptWidget::removeButton() {
+ if(sl->currentItem() >= 0) {
+ int stlc = stl->currentItem();
+ sl->removeItem(sl->currentItem());
+ stl->removeItem(stlc);
+ adjustScrollBar();
+ insert->setEnabled(sl->currentItem() != -1);
+ remove->setEnabled(sl->currentItem() != -1);
+ }
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Used to specify a new phone number
+//
+/////////////////////////////////////////////////////////////////////////////
+PhoneNumberDialog::PhoneNumberDialog(QWidget *parent) : KDialogBase(parent, 0, true, i18n("Add Phone Number"), Ok|Cancel) {
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ QHBox *hbox = new QHBox(this);
+ setMainWidget(hbox);
+
+ hbox->setSpacing(KDialog::spacingHint());
+
+ new QLabel(i18n("Enter a phone number:"), hbox);
+
+ le = newLineEdit(14, hbox);
+ le->setMinimumWidth(125);
+
+ connect(le, SIGNAL(textChanged(const QString &)),
+ this, SLOT(textChanged(const QString &)));
+
+ le->setFocus();
+ textChanged("");
+
+ enableButtonSeparator(true);
+}
+
+
+QString PhoneNumberDialog::phoneNumber() {
+ QString s = le->text();
+
+ return s;
+}
+
+
+void PhoneNumberDialog::textChanged(const QString &s) {
+ enableButtonOK(s.length() > 0);
+}
+
+
+#include "edit.moc"
diff --git a/kppp/edit.h b/kppp/edit.h
new file mode 100644
index 00000000..dec7b11d
--- /dev/null
+++ b/kppp/edit.h
@@ -0,0 +1,256 @@
+/* -*- C++ -*-
+ *
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _EDIT_H_
+#define _EDIT_H_
+
+#include <qdialog.h>
+#include <qpushbutton.h>
+#include <qgroupbox.h>
+#include <qscrollbar.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qradiobutton.h>
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <kdialogbase.h>
+#include "scriptedit.h"
+#include "kpppconfig.h"
+#include "pppdargs.h"
+
+class IPLineEdit;
+
+class DialWidget : public QWidget {
+ Q_OBJECT
+public:
+ DialWidget( QWidget *parent=0, bool isnewaccount = true, const char *name=0 );
+ ~DialWidget() {}
+
+public slots:
+ bool save();
+ void pppdargsbutton();
+ void numbersChanged();
+ void cbtypeChanged(int);
+ void selectionChanged(int);
+ void addNumber();
+ void delNumber();
+ void upNumber();
+ void downNumber();
+
+private:
+ QLineEdit *connectname_l;
+ QLabel *connect_label;
+ QLabel *number_label;
+ QPushButton *pppdargs;
+ QComboBox *auth;
+ QLabel *auth_l;
+ QCheckBox *store_password;
+
+ // callback support
+ QComboBox *cbtype;
+ QLabel *cbtype_l;
+ QLineEdit *cbphone;
+ QLabel *cbphone_l;
+ // for the phonenumber selection
+ QPushButton *add, *del, *up, *down;
+ QListBox *numbers;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// tab-window to select what to execute when
+//
+/////////////////////////////////////////////////////////////////////////////
+class ExecWidget : public QWidget {
+ Q_OBJECT
+public:
+ ExecWidget(QWidget *parent=0, bool isnewaccount=true, const char *name=0);
+
+public slots:
+ bool save();
+
+private:
+ QLineEdit *before_connect;
+ QLabel *before_connect_l;
+
+ QLineEdit *command;
+ QLabel *command_label;
+
+ QLineEdit *predisconnect;
+ QLabel *predisconnect_label;
+
+ QLineEdit *discommand;
+ QLabel *discommand_label;
+};
+
+
+class IPWidget : public QWidget {
+ Q_OBJECT
+public:
+ IPWidget( QWidget *parent=0, bool isnewaccount = true, const char *name=0 );
+ ~IPWidget() {}
+
+public slots:
+ void save();
+
+protected slots:
+ void hitIPSelect( int );
+ void autoname_t(bool on);
+
+private:
+ QLabel *ipaddress_label;
+ QLabel *sub_label;
+ QGroupBox *box1;
+ QVGroupBox *box;
+
+ QButtonGroup *rb;
+ QRadioButton *dynamicadd_rb;
+ QRadioButton *staticadd_rb;
+
+ IPLineEdit *ipaddress_l;
+ IPLineEdit *subnetmask_l;
+
+ QCheckBox *autoname;
+};
+
+
+class DNSWidget : public QWidget {
+ Q_OBJECT
+public:
+ DNSWidget( QWidget *parent=0, bool isnewaccount = true, const char *name=0 );
+ ~DNSWidget() {}
+
+public slots:
+ void save();
+
+protected slots:
+ void adddns();
+ void removedns();
+ void DNS_Edit_Changed(const QString &);
+ void DNS_Entry_Selected(int);
+ void DNS_Mode_Selected(int);
+
+private:
+ QLabel *conf_label;
+ QButtonGroup *bg;
+ QRadioButton *autodns, *mandns;
+ QLabel *dns_label;
+ QLabel *servers_label;
+ IPLineEdit *dnsipaddr;
+ QPushButton *add;
+ QPushButton *remove;
+ QListBox *dnsservers;
+ QLineEdit *dnsdomain;
+ QLabel *dnsdomain_label;
+ QCheckBox *exdnsdisabled_toggle;
+};
+
+
+class GatewayWidget : public QWidget {
+ Q_OBJECT
+public:
+ GatewayWidget( QWidget *parent=0, bool isnewaccount = true, const char *name=0 );
+ ~GatewayWidget() {}
+
+public slots:
+ void save();
+
+private slots:
+ void hitGatewaySelect( int );
+
+private:
+ QGroupBox *box;
+ QLabel *gate_label;
+ QGroupBox *box1;
+ QButtonGroup *rb;
+ QRadioButton *defaultgateway;
+ QRadioButton *staticgateway;
+ IPLineEdit *gatewayaddr;
+ QCheckBox *defaultroute;
+};
+
+
+class ScriptWidget : public QWidget {
+ Q_OBJECT
+public:
+ ScriptWidget( QWidget *parent=0, bool isnewaccount = true, const char *name=0 );
+ ~ScriptWidget() {}
+
+public slots:
+ void save();
+ bool check();
+
+private slots:
+ void addButton();
+ void insertButton();
+ void removeButton();
+
+ //signals linked to the scroll bar
+ void scrolling(int);
+
+ //signals to keep the two listboxes highlighted in sync
+ void slhighlighted(int);
+ void stlhighlighted(int);
+
+private:
+ void adjustScrollBar();
+
+ ScriptEdit *se;
+ QPushButton *add;
+ QPushButton *remove;
+ QPushButton *insert;
+ QListBox *sl, *stl;
+
+ QScrollBar *slb;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Used to specify a new phone number
+//
+/////////////////////////////////////////////////////////////////////////////
+class PhoneNumberDialog : public KDialogBase {
+ Q_OBJECT
+public:
+ PhoneNumberDialog(QWidget *parent = 0);
+
+ QString phoneNumber();
+
+private slots:
+ void textChanged(const QString &);
+
+private:
+ QLineEdit *le;
+};
+
+
+#endif
diff --git a/kppp/general.cpp b/kppp/general.cpp
new file mode 100644
index 00000000..1ae31092
--- /dev/null
+++ b/kppp/general.cpp
@@ -0,0 +1,684 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <termios.h>
+#include <string.h>
+#include <qwhatsthis.h>
+
+#include <knuminput.h>
+#include <qslider.h>
+#include "general.h"
+#include "version.h"
+#include "miniterm.h"
+#include "modeminfo.h"
+#include "modemcmds.h"
+#include "devices.h"
+#include "pppdata.h"
+#include <klocale.h>
+#include <qlayout.h>
+#include <qgrid.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Widget containing misc. configuration options
+//
+/////////////////////////////////////////////////////////////////////////////
+GeneralWidget::GeneralWidget( QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *tl = new QVBoxLayout(parent, 0, KDialog::spacingHint());
+
+ QHBoxLayout *hbox = new QHBoxLayout(tl);
+ QLabel *label;
+ label = new QLabel(i18n("pppd version:"), parent);
+ hbox->addWidget(label);
+ QString version = gpppdata.pppdVersion();
+ if(version == "0.0.0")
+ version = "unknown";
+ label = new QLabel(version, parent);
+ label->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
+ hbox->addWidget(label);
+
+ KIntNumInput *pppdTimeout = new KIntNumInput(gpppdata.pppdTimeout(), parent);
+ pppdTimeout->setLabel(i18n("pppd &timeout:"));
+ pppdTimeout->setRange(1, TIMEOUT_SIZE, 5, true);
+ pppdTimeout->setSuffix(i18n(" sec"));
+ connect(pppdTimeout, SIGNAL(valueChanged(int)),
+ SLOT(pppdtimeoutchanged(int)));
+ tl->addWidget(pppdTimeout);
+ QString tmp = i18n("<i>kppp</i> will wait this number of seconds\n"
+ "to see if a PPP connection is established.\n"
+ "If no connection is made in this time frame,\n"
+ "<i>kppp</i> will give up and kill pppd.");
+ QWhatsThis::add(pppdTimeout,tmp);
+ label->setBuddy(pppdTimeout);
+
+ tl->addSpacing(10);
+
+ QCheckBox *chkBox;
+ chkBox = new QCheckBox(i18n("Doc&k into panel on connect"), parent);
+ QWhatsThis::add(chkBox,
+ i18n("<p>After a connection is established, the\n"
+ "window is minimized and a small icon\n"
+ "in the KDE panel represents this window.\n"
+ "\n"
+ "Clicking on this icon will restore the\n"
+ "window to its original location and\n"
+ "size."));
+
+ chkBox->setChecked(gpppdata.get_dock_into_panel());
+ connect(chkBox,SIGNAL(toggled(bool)),
+ this, SLOT(docking_toggled(bool)));
+ tl->addWidget(chkBox);
+
+ chkBox = new QCheckBox(i18n("A&utomatic redial on disconnect"), parent);
+ chkBox->setChecked(gpppdata.automatic_redial());
+ connect(chkBox,SIGNAL(toggled(bool)),
+ this, SLOT(redial_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("<p>When a connection is established and\n"
+ "it somehow gets disconnected, <i>kppp</i>\n"
+ "will try to reconnect to the same account.\n"
+ "\n"
+ "See <a href=\"#redial\">here</a> for more on this topic."));
+
+ chkBox = new QCheckBox(i18n("Automatic redial on NO &CARRIER"), parent);
+ chkBox->setChecked(gpppdata.get_redial_on_nocarrier());
+ connect(chkBox,SIGNAL(toggled(bool)),
+ this, SLOT(nocarrier_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("<p>When dialing if modem returns NO CARRIER\n"
+ "the program will make a new attempt to redial\n"
+ "instead of waiting for user to click <CANCEL>\n"
+ "button."));
+
+ chkBox = new QCheckBox(i18n("&Show clock on caption"), parent);
+ chkBox->setChecked(gpppdata.get_show_clock_on_caption());
+ connect(chkBox, SIGNAL(toggled(bool)),
+ this, SLOT(caption_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("When this option is checked, the window\n"
+ "title shows the time since a connection\n"
+ "was established. Very useful, so you \n"
+ "should turn this on"));
+
+ chkBox = new QCheckBox(i18n("Disco&nnect on X server shutdown"), parent);
+ chkBox->setChecked(gpppdata.get_xserver_exit_disconnect());
+ connect(chkBox, SIGNAL(toggled(bool)),
+ this, SLOT(xserver_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("<p>Checking this option will close any\n"
+ "open connection when the X-server is\n"
+ "shut down. You should enable this option\n"
+ "unless you know what you are doing.\n"
+ "\n"
+ "See <a href=\"#disxserver\">here</a> for more on this."));
+
+ chkBox = new QCheckBox(i18n("&Quit on disconnect"), parent);
+ chkBox->setChecked(gpppdata.quit_on_disconnect());
+ connect(chkBox, SIGNAL(toggled(bool)),
+ this, SLOT(quit_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("When this option is turned on, <i>kppp</i>\n"
+ "will be closed when you disconnect"));
+
+ chkBox = new QCheckBox(i18n("Minimi&ze window on connect"), parent);
+ chkBox->setChecked(gpppdata.get_iconify_on_connect());
+ connect(chkBox,SIGNAL(toggled(bool)),
+ this,SLOT(iconify_toggled(bool)));
+ tl->addWidget(chkBox);
+ QWhatsThis::add(chkBox,
+ i18n("Iconifies <i>kppp</i>'s window when a\n"
+ "connection is established"));
+
+ tl->addStretch();
+
+}
+
+
+void GeneralWidget::docking_toggled(bool on){
+ gpppdata.set_dock_into_panel(on);
+}
+
+
+void GeneralWidget::iconify_toggled(bool on){
+ gpppdata.set_iconify_on_connect(on);
+}
+
+
+void GeneralWidget::caption_toggled(bool on){
+ gpppdata.set_show_clock_on_caption(on);
+}
+
+
+void GeneralWidget::redial_toggled(bool on){
+ gpppdata.set_automatic_redial(on);
+}
+
+void GeneralWidget::nocarrier_toggled(bool on){
+ gpppdata.set_redial_on_nocarrier(on);
+}
+
+void GeneralWidget::xserver_toggled(bool on){
+ gpppdata.set_xserver_exit_disconnect(on);
+}
+
+
+void GeneralWidget::quit_toggled(bool on){
+ gpppdata.set_quit_on_disconnect(on);
+}
+
+
+void GeneralWidget::pppdtimeoutchanged(int n) {
+ gpppdata.setpppdTimeout(n);
+
+}
+
+
+ModemWidget::ModemWidget(QWidget *parent, bool isnewmodem, const char *name)
+ : QWidget(parent, name)
+{
+ QGridLayout *tl = new QGridLayout(parent, 9, 2, 0, KDialog::spacingHint());
+
+ connect_label = new QLabel(i18n("Modem &name:"), parent);
+ tl->addWidget(connect_label, 0, 0);
+
+ connectname_l = new QLineEdit(parent);
+ connectname_l->setMaxLength(ACCNAME_SIZE);
+ connect_label->setBuddy(connectname_l);
+
+ tl->addWidget(connectname_l, 0, 1);
+ QString tmp = i18n("Type in a unique name for this modem");
+
+ QWhatsThis::add(connect_label,tmp);
+ QWhatsThis::add(connectname_l,tmp);
+
+ label1 = new QLabel(i18n("Modem de&vice:"), parent);
+ tl->addWidget(label1, 1, 0);
+
+ modemdevice = new QComboBox(false, parent);
+ label1->setBuddy(modemdevice);
+ // ### deviceExist mechanism not functional right now
+ bool deviceExist = false;
+ for(int k = 0; devices[k]; k++)
+ {
+ if ( devices[k] == gpppdata.modemDevice())
+ deviceExist = true;
+ modemdevice->insertItem(devices[k]);
+ }
+ if ( !deviceExist )
+ modemdevice->insertItem(gpppdata.modemDevice());
+
+ tl->addWidget(modemdevice, 1, 1);
+/* connect(modemdevice, SIGNAL(activated(int)),
+ SLOT(setmodemdc(int)));*/
+ tmp = i18n("This specifies the serial port your modem is attached \n"
+ "to. On Linux/x86, typically this is either /dev/ttyS0 \n"
+ "(COM1 under DOS) or /dev/ttyS1 (COM2 under DOS).\n"
+ "\n"
+ "If you have an internal ISDN card with AT command\n"
+ "emulation (most cards under Linux support this), you\n"
+ "should select one of the /dev/ttyIx devices.");
+
+ QWhatsThis::add(label1,tmp);
+ QWhatsThis::add(modemdevice,tmp);
+
+
+ label2 = new QLabel(i18n("&Flow control:"), parent);
+ tl->addWidget(label2, 2, 0);
+
+ flowcontrol = new QComboBox(false, parent);
+ label2->setBuddy(flowcontrol);
+ flowcontrol->insertItem(i18n("Hardware [CRTSCTS]")); // sync with pppdata.cpp
+ flowcontrol->insertItem(i18n("Software [XON/XOFF]"));
+ flowcontrol->insertItem(i18n("None"));
+
+ flowListItem << "Hardware [CRTSCTS]";
+ flowListItem << "Software [XON/XOFF]";
+ flowListItem << "None";
+
+ tl->addWidget(flowcontrol, 2, 1);
+ /*connect(flowcontrol, SIGNAL(activated(int)),
+ SLOT(setflowcontrol(int)));*/
+
+ tmp = i18n("<p>Specifies how the serial port and modem\n"
+ "communicate. You should not change this unless\n"
+ "you know what you are doing.\n"
+ "\n"
+ "<b>Default</b>: CRTSCTS");
+
+ QWhatsThis::add(label2,tmp);
+ QWhatsThis::add(flowcontrol,tmp);
+
+ labelenter = new QLabel(i18n("&Line termination:"), parent);
+ tl->addWidget(labelenter, 3, 0);
+
+ enter = new QComboBox(false, parent);
+ labelenter->setBuddy(enter);
+ enter->insertItem("CR");
+ enter->insertItem("LF");
+ enter->insertItem("CR/LF");
+ tl->addWidget(enter, 3, 1);
+ /* connect(enter, SIGNAL(activated(int)), SLOT(setenter(int)));*/
+ tmp = i18n("<p>Specifies how AT commands are sent to your\n"
+ "modem. Most modems will work fine with the\n"
+ "default <i>CR/LF</i>. If your modem does not react\n"
+ "to the init string, you should try different\n"
+ "settings here\n"
+ "\n"
+ "<b>Default</b>: CR/LF");
+
+ QWhatsThis::add(labelenter,tmp);
+ QWhatsThis::add(enter, tmp);
+
+ baud_label = new QLabel(i18n("Co&nnection speed:"), parent);
+ tl->addWidget(baud_label, 4, 0);
+ baud_c = new QComboBox(parent);
+ baud_label->setBuddy(baud_c);
+
+ static const char *baudrates[] = {
+
+#ifdef B921600
+ "921600",
+#endif
+
+#ifdef B460800
+ "460800",
+#endif
+
+#ifdef B230400
+ "230400",
+#endif
+
+#ifdef B115200
+ "115200",
+#endif
+
+#ifdef B57600
+ "57600",
+#endif
+
+ "38400",
+ "19200",
+ "9600",
+ "2400",
+ 0};
+
+ for(int k = 0; baudrates[k]; k++)
+ baud_c->insertItem(baudrates[k]);
+
+ baud_c->setCurrentItem(3);
+ /*connect(baud_c, SIGNAL(activated(int)),
+ this, SLOT(speed_selection(int)));*/
+ tl->addWidget(baud_c, 4, 1);
+
+ tmp = i18n("Specifies the speed your modem and the serial\n"
+ "port talk to each other. You should begin with\n"
+ "at least 115200 bits/sec (or more if you know\n"
+ "that your serial port supports higher speeds).\n"
+ "If you have connection problems, try to reduce\n"
+ "this value.");
+
+ QWhatsThis::add(baud_label,tmp);
+ QWhatsThis::add(baud_c,tmp);
+
+ for(int i=0; i <= enter->count()-1; i++) {
+ if(gpppdata.enter() == enter->text(i))
+ enter->setCurrentItem(i);
+ }
+
+ tl->addRowSpacing(4, 10);
+
+ //Modem Lock File
+ modemlockfile = new QCheckBox(i18n("&Use lock file"), parent);
+
+ modemlockfile->setChecked(gpppdata.modemLockFile());
+/* connect(modemlockfile, SIGNAL(toggled(bool)),
+ SLOT(modemlockfilechanged(bool)));*/
+ tl->addMultiCellWidget(modemlockfile, 5, 5, 0, 1);
+ // l12->addStretch(1);
+ QWhatsThis::add(modemlockfile,
+ i18n("<p>To prevent other programs from accessing the\n"
+ "modem while a connection is established, a\n"
+ "file can be created to indicate that the modem\n"
+ "is in use. On Linux an example file would be\n"
+ "<tt>/var/lock/LCK..ttyS1</tt>\n"
+ "Here you can select whether this locking will\n"
+ "be done.\n"
+ "\n"
+ "<b>Default</b>: On"));
+
+ // Modem Timeout Line Edit Box
+
+ modemtimeout = new KIntNumInput(gpppdata.modemTimeout(), parent);
+ modemtimeout->setLabel(i18n("Modem &timeout:"));
+ modemtimeout->setRange(1, 120, 1);
+ modemtimeout->setSuffix(i18n(" sec"));
+/* connect(modemtimeout, SIGNAL(valueChanged(int)),
+ SLOT(modemtimeoutchanged(int)));*/
+ tl->addMultiCellWidget(modemtimeout, 6, 6, 0, 1);
+
+ QWhatsThis::add(modemtimeout,
+ i18n("This specifies how long <i>kppp</i> waits for a\n"
+ "<i>CONNECT</i> response from your modem. The\n"
+ "recommended value is 30 seconds."));
+
+// Set defaults if editing an existing connection
+ if(!isnewmodem) {
+ connectname_l->setText(gpppdata.modname());
+
+ //set stuff from gpppdata
+ for(int i=0; i <= enter->count()-1; i++) {
+ if(gpppdata.enter() == enter->text(i))
+ enter->setCurrentItem(i);
+ }
+
+ for(int i=0; i <= modemdevice->count()-1; i++) {
+ if(gpppdata.modemDevice() == modemdevice->text(i))
+ modemdevice->setCurrentItem(i);
+ }
+
+ for(int i=0; i <= flowcontrol->count()-1; i++)
+ if(gpppdata.flowcontrol() == flowListItem[i])
+ flowcontrol->setCurrentItem(i);
+
+ //set the modem speed
+ for(int i=0; i < baud_c->count(); i++)
+ if(baud_c->text(i) == gpppdata.speed())
+ baud_c->setCurrentItem(i);
+
+ } else {
+ //Set the standard Items
+ enter->setCurrentItem(0);
+ modemdevice->setCurrentItem(0);
+ flowcontrol->setCurrentItem(0);
+ baud_c->setCurrentItem(0);
+ }
+
+ tl->setRowStretch(7, 1);
+}
+
+bool ModemWidget::save()
+{
+ //first check to make sure that the modem name is unique!
+ if(connectname_l->text().isEmpty() ||
+ !gpppdata.isUniqueModname(connectname_l->text())) {
+ return false;
+ } else {
+ gpppdata.setModname(connectname_l->text());
+ gpppdata.setSpeed(baud_c->text(baud_c->currentItem()));
+ gpppdata.setEnter(enter->text(enter->currentItem()));
+ gpppdata.setModemDevice(modemdevice->text(modemdevice->currentItem()));
+ gpppdata.setFlowcontrol(flowListItem[flowcontrol->currentItem()]);
+ gpppdata.setModemLockFile(modemlockfile->isOn());
+ gpppdata.setModemTimeout(modemtimeout->value());
+ return true;
+ }
+}
+
+
+ModemWidget2::ModemWidget2(QWidget *parent, const char *name)
+ : QWidget(parent, name)
+{
+ QVBoxLayout *l1 = new QVBoxLayout(parent, 0, KDialog::spacingHint());
+
+
+ waitfordt = new QCheckBox(i18n("&Wait for dial tone before dialing"), parent);
+ waitfordt->setChecked(gpppdata.waitForDialTone());
+ // connect(waitfordt, SIGNAL(toggled(bool)), SLOT(waitfordtchanged(bool)));
+ l1->addWidget(waitfordt);
+ QWhatsThis::add(waitfordt,
+ i18n("<p>Normally the modem waits for a dial tone\n"
+ "from your phone line, indicating that it can\n"
+ "start to dial a number. If your modem does not\n"
+ "recognize this sound, or your local phone system\n"
+ "does not emit such a tone, uncheck this option\n"
+ "\n"
+ "<b>Default:</b>: On"));
+
+ busywait = new KIntNumInput(gpppdata.busyWait(), parent);
+ busywait->setLabel(i18n("B&usy wait:"));
+ busywait->setRange(0, 300, 5, true);
+ busywait->setSuffix(i18n(" sec"));
+ // connect(busywait, SIGNAL(valueChanged(int)), SLOT(busywaitchanged(int)));
+ l1->addWidget(busywait);
+
+ QWhatsThis::add(busywait,
+ i18n("Specifies the number of seconds to wait before\n"
+ "redial if all dialed numbers are busy. This is\n"
+ "necessary because some modems get stuck if the\n"
+ "same number is busy too often.\n"
+ "\n"
+ "The default is 0 seconds, you should not change\n"
+ "this unless you need to."));
+
+ l1->addSpacing(10);
+
+ QHBoxLayout *hbl = new QHBoxLayout;
+ hbl->setSpacing(KDialog::spacingHint());
+
+ QLabel *volumeLabel = new QLabel(i18n("Modem &volume:"), parent);
+ hbl->addWidget(volumeLabel);
+ volume = new QSlider(0, 2, 1, gpppdata.volume(), QSlider::Horizontal, parent);
+ volumeLabel->setBuddy(volume);
+ volume->setTickmarks(QSlider::Below);
+ hbl->addWidget(volume);
+
+ l1->addLayout(hbl);
+
+ /* connect(volume, SIGNAL(valueChanged(int)),
+ this, SLOT(volumeChanged(int)));*/
+ QString tmp = i18n("Most modems have a speaker which makes\n"
+ "a lot of noise when dialing. Here you can\n"
+ "either turn this completely off or select a\n"
+ "lower volume.\n"
+ "\n"
+ "If this does not work for your modem,\n"
+ "you must modify the modem volume command.");
+
+ QWhatsThis::add(volumeLabel,tmp);
+ QWhatsThis::add(volume, tmp);
+
+ l1->addSpacing(20);
+
+#if 0
+ chkbox1 = new QCheckBox(i18n("Modem asserts CD line"), parent);
+ chkbox1->setChecked(gpppdata.UseCDLine());
+ connect(chkbox1,SIGNAL(toggled(bool)),
+ this,SLOT(use_cdline_toggled(bool)));
+ l12->addWidget(chkbox1);
+ l12->addStretch(1);
+ l1->addStretch(1);
+ QWhatsThis::add(chkbox1,
+ i18n("This controls how <i>kppp</i> detects that the modem\n"
+ "is not responding. Unless you are having\n"
+ "problems with this, do not modify this setting.\n"
+ "\n"
+ "<b>Default</b>: Off"));
+#endif
+
+ modemcmds = new QPushButton(i18n("Mod&em Commands..."), parent);
+ QWhatsThis::add(modemcmds,
+ i18n("Allows you to change the AT command for\n"
+ "your modem."));
+
+ modeminfo_button = new QPushButton(i18n("&Query Modem..."), parent);
+ QWhatsThis::add(modeminfo_button,
+ i18n("Most modems support the ATI command set to\n"
+ "find out vendor and revision of your modem.\n"
+ "\n"
+ "Press this button to query your modem for\n"
+ "this information. It can be useful to help\n"
+ "you set up the modem"));
+
+ terminal_button = new QPushButton(i18n("&Terminal..."), parent);
+ QWhatsThis::add(terminal_button,
+ i18n("Opens the built-in terminal program. You\n"
+ "can use this if you want to play around\n"
+ "with your modem's AT command set"));
+
+ QHBoxLayout *hbox = new QHBoxLayout();
+ l1->addLayout(hbox);
+ hbox->addStretch(1);
+ QVBoxLayout *vbox = new QVBoxLayout();
+ hbox->addLayout(vbox);
+
+ vbox->addWidget(modemcmds);
+ vbox->addWidget(modeminfo_button);
+ vbox->addWidget(terminal_button);
+
+ hbox->addStretch(1);
+ l1->addStretch(1);
+
+ connect(modemcmds, SIGNAL(clicked()),
+ SLOT(modemcmdsbutton()));
+ connect(modeminfo_button, SIGNAL(clicked()),
+ SLOT(query_modem()));
+ connect(terminal_button, SIGNAL(clicked()),
+ SLOT(terminal()));
+
+ // Create the Modem Command so if the window is not opened they are autosaved anyway
+ mc = new ModemCommands(this);
+}
+
+
+void ModemWidget2::modemcmdsbutton() {
+ mc->exec();
+}
+
+
+void ModemWidget2::query_modem() {
+ ModemTransfer mt(this);
+ mt.exec();
+}
+
+
+void ModemWidget2::terminal() {
+ MiniTerm terminal(NULL,NULL);
+ terminal.exec();
+}
+
+
+#if 0
+void ModemWidget2::use_cdline_toggled(bool on) {
+ gpppdata.setUseCDLine(on);
+}
+#endif
+
+bool ModemWidget2::save()
+{
+ gpppdata.setWaitForDialTone(waitfordt->isOn());
+ gpppdata.setbusyWait(busywait->value());
+ gpppdata.setVolume(volume->value());
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Setup widget for the graph
+//
+/////////////////////////////////////////////////////////////////////////////
+GraphSetup::GraphSetup(QWidget *parent, const char *name) :
+ QWidget(parent, name)
+{
+ QVBoxLayout *tl = new QVBoxLayout(parent);
+
+ bool enable;
+ QColor bg, text, in, out;
+ gpppdata.graphingOptions(enable, bg, text, in, out);
+
+ enable_check = new QCheckBox(i18n("&Enable throughput graph"), parent);
+ tl->addWidget(enable_check);
+
+ grpColor = new QGroupBox(2, Qt::Horizontal,
+ i18n("Graph Colors"), parent);
+ tl->addWidget(grpColor);
+
+ QLabel *label;
+
+ label = new QLabel(i18n("Bac&kground:"), grpColor);
+ bg_color = new KColorButton(bg, grpColor);
+ bg_color->setFixedSize(80, 24);
+ label->setBuddy(bg_color);
+
+ label = new QLabel(i18n("&Text:"), grpColor);
+ text_color = new KColorButton(text, grpColor);
+ text_color->setFixedSize(80, 24);
+ label->setBuddy(text_color);
+
+ label = new QLabel(i18n("I&nput bytes:"), grpColor);
+ in_color = new KColorButton(in, grpColor);
+ in_color->setFixedSize(80, 24);
+ label->setBuddy(in_color);
+
+ label = new QLabel(i18n("O&utput bytes:"), grpColor);
+ out_color = new KColorButton(out, grpColor);
+ out_color->setFixedSize(80, 24);
+ label->setBuddy(out_color);
+
+ tl->addStretch();
+
+ connect(enable_check, SIGNAL(toggled(bool)), this, SLOT(enableToggled(bool)));
+ connect(bg_color, SIGNAL(changed(const QColor &)),
+ SLOT(colorChanged(const QColor&)));
+ connect(text_color, SIGNAL(changed(const QColor &)),
+ SLOT(colorChanged(const QColor&)));
+ connect(in_color, SIGNAL(changed(const QColor &)),
+ SLOT(colorChanged(const QColor&)));
+ connect(out_color, SIGNAL(changed(const QColor &)),
+ SLOT(colorChanged(const QColor&)));
+
+ tl->activate();
+
+ enable_check->setChecked(enable);
+ enableToggled(enable);
+}
+
+void GraphSetup::enableToggled(bool b) {
+ grpColor->setEnabled(b);
+ save();
+}
+
+
+void GraphSetup::colorChanged(const QColor &) {
+ save();
+}
+
+void GraphSetup::save() {
+ gpppdata.setGraphingOptions(enable_check->isChecked(),
+ bg_color->color(),
+ text_color->color(),
+ in_color->color(),
+ out_color->color());
+}
+
+#include "general.moc"
diff --git a/kppp/general.h b/kppp/general.h
new file mode 100644
index 00000000..2781ac50
--- /dev/null
+++ b/kppp/general.h
@@ -0,0 +1,137 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GENERAL_H_
+#define _GENERAL_H_
+
+#include <qwidget.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qpushbutton.h>
+#include <kcolorbutton.h>
+
+class QSlider;
+class KIntNumInput;
+class ModemCommands;
+
+class GeneralWidget : public QWidget {
+ Q_OBJECT
+public:
+ GeneralWidget( QWidget *parent=0, const char *name=0 );
+
+private slots:
+ void pppdtimeoutchanged(int);
+ void caption_toggled(bool);
+ void iconify_toggled(bool on);
+ void redial_toggled(bool on);
+ void nocarrier_toggled(bool on);
+ void xserver_toggled(bool on);
+ void quit_toggled(bool);
+ void docking_toggled(bool on);
+
+};
+
+
+class ModemWidget : public QWidget {
+ Q_OBJECT
+public:
+ ModemWidget(QWidget *parent=0, bool isnewmodem=true, const char *name=0);
+ bool save();
+ QLineEdit *connectName() { return connectname_l;}
+private slots:
+
+private:
+ QLineEdit *connectname_l;
+ QLabel *connect_label;
+
+ QComboBox *enter;
+ QLabel *label1;
+ QLabel *label2;
+ QLabel *labeltmp;
+ QLabel *labelenter;
+ QComboBox *modemdevice;
+ QComboBox *flowcontrol;
+
+ QComboBox *baud_c;
+ QLabel *baud_label;
+
+ KIntNumInput *modemtimeout;
+ QCheckBox *modemlockfile;
+ QStringList flowListItem;
+};
+
+
+class ModemWidget2 : public QWidget {
+ Q_OBJECT
+public:
+ ModemWidget2(QWidget *parent=0, const char *name=0);
+ bool save();
+
+private slots:
+// void use_cdline_toggled(bool);
+ void modemcmdsbutton();
+ void terminal();
+ void query_modem();
+
+private:
+ QLabel *labeltmp;
+ QPushButton *modemcmds;
+ QPushButton *modeminfo_button;
+ QPushButton *terminal_button;
+ QFrame *fline;
+ QCheckBox *waitfordt;
+ KIntNumInput *busywait;
+ QCheckBox *chkbox1;
+ QSlider *volume;
+
+ ModemCommands* mc;
+};
+
+class GraphSetup : public QWidget {
+ Q_OBJECT
+public:
+ GraphSetup(QWidget *parent = 0, const char *name = 0);
+
+private slots:
+ void enableToggled(bool);
+ void colorChanged(const QColor &);
+
+private:
+ void save();
+
+ QGroupBox *grpColor;
+ QCheckBox *enable_check;
+
+ KColorButton *bg_color;
+ KColorButton *text_color;
+ KColorButton *in_color;
+ KColorButton *out_color;
+
+};
+
+#endif
+
+
diff --git a/kppp/icons/Makefile.am b/kppp/icons/Makefile.am
new file mode 100644
index 00000000..19595145
--- /dev/null
+++ b/kppp/icons/Makefile.am
@@ -0,0 +1 @@
+KDE_ICON = kppp
diff --git a/kppp/icons/hi128-app-kppp.png b/kppp/icons/hi128-app-kppp.png
new file mode 100644
index 00000000..245db427
--- /dev/null
+++ b/kppp/icons/hi128-app-kppp.png
Binary files differ
diff --git a/kppp/icons/hi16-app-kppp.png b/kppp/icons/hi16-app-kppp.png
new file mode 100644
index 00000000..9de4c896
--- /dev/null
+++ b/kppp/icons/hi16-app-kppp.png
Binary files differ
diff --git a/kppp/icons/hi22-app-kppp.png b/kppp/icons/hi22-app-kppp.png
new file mode 100644
index 00000000..664388a4
--- /dev/null
+++ b/kppp/icons/hi22-app-kppp.png
Binary files differ
diff --git a/kppp/icons/hi32-app-kppp.png b/kppp/icons/hi32-app-kppp.png
new file mode 100644
index 00000000..cfb9129e
--- /dev/null
+++ b/kppp/icons/hi32-app-kppp.png
Binary files differ
diff --git a/kppp/icons/hi48-app-kppp.png b/kppp/icons/hi48-app-kppp.png
new file mode 100644
index 00000000..68b8c9f5
--- /dev/null
+++ b/kppp/icons/hi48-app-kppp.png
Binary files differ
diff --git a/kppp/icons/hi64-app-kppp.png b/kppp/icons/hi64-app-kppp.png
new file mode 100644
index 00000000..e10d360e
--- /dev/null
+++ b/kppp/icons/hi64-app-kppp.png
Binary files differ
diff --git a/kppp/iplined.cpp b/kppp/iplined.cpp
new file mode 100644
index 00000000..43367b16
--- /dev/null
+++ b/kppp/iplined.cpp
@@ -0,0 +1,42 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "iplined.h"
+
+IPLineEdit::IPLineEdit( QWidget *parent, const char *name )
+ : KRestrictedLine(parent, name, "0123456789.")
+{
+ setMaxLength(3 * 4 + 1 * 3);
+}
+
+QSize IPLineEdit::sizeHint() const {
+ QFontMetrics fm = fontMetrics();
+
+ QSize s;
+ s.setHeight(QLineEdit::sizeHint().height());
+ s.setWidth(fm.boundingRect("888.888.888.888XX").width());
+ return s;
+}
diff --git a/kppp/iplined.h b/kppp/iplined.h
new file mode 100644
index 00000000..e7d47787
--- /dev/null
+++ b/kppp/iplined.h
@@ -0,0 +1,40 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _IPLINED_H_
+#define _IPLINED_H_
+
+#include <krestrictedline.h>
+
+class IPLineEdit : public KRestrictedLine {
+public:
+ IPLineEdit( QWidget *parent=0, const char *name=0 );
+ ~IPLineEdit() {}
+
+ virtual QSize sizeHint() const;
+};
+
+#endif
diff --git a/kppp/kpppconfig.h b/kppp/kpppconfig.h
new file mode 100644
index 00000000..c1741fd0
--- /dev/null
+++ b/kppp/kpppconfig.h
@@ -0,0 +1,139 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _KPPPCONFIG_H_
+#define _KPPPCONFIG_H_
+
+#if defined(__svr4__)
+#define STREAMS
+#define _XOPEN_SOURCE 1
+#define _XOPEN_SOURCE_EXTENDED 1
+#define __EXTENSIONS__
+#endif
+
+#include <config.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+// Warning: If you fiddle with the following directories you have
+// to adjust make_directories() in main.cpp()
+
+// file used for PAP authetication purposes
+#define PAP_AUTH_FILE "/etc/ppp/pap-secrets"
+
+// file used for CHAP authetication purposes
+#define CHAP_AUTH_FILE "/etc/ppp/chap-secrets"
+
+// Define the default modem response timeout
+#define MODEM_TIMEOUT 60
+
+// Define the default modem tone duration (ATS11=)
+#define MODEM_TONEDURATION 70
+
+// Define the default time for pppd to get the interface up
+#define PPPD_TIMEOUT 30
+
+// Define the default time to wait after a busy signal before redialing
+#define BUSY_WAIT 0
+
+
+
+// Every PPP_STATS_INTERVAL milli seconds kppp will read
+// and display the ppp statistics IF the stats window
+// is visible. If the stats window is not visible
+// the stats are not taken.
+// 200 milli secs is 5 times per second and results in
+// 0 load on my machine. Play with this parameter if
+// you feel like it.
+
+#define PPP_STATS_INTERVAL 200
+// comment this out to get some more debugging info
+/*
+#define MY_DEBUG
+*/
+
+// Define the maximum number of accounts
+#define MAX_ACCOUNTS 100
+
+// Define the maximum number of modems
+#define MAX_MODEMS 100
+
+// Define the mamimum number of script entries
+#define MAX_SCRIPT_ENTRIES 20
+
+// Define the maximun number of DNS entries
+#define MAX_DNS_ENTRIES 5
+
+// Maximum size of the command executing pppd
+const unsigned int MAX_CMDLEN = 2024;
+
+// Define the maximum number of arguments passed to the pppd daemon
+#define MAX_PPPD_ARGUMENTS 20
+
+// Define the maximun number of lines of /etc/resolv.conf
+#define MAX_RESOLVCONF_LINES 128
+
+// Directory for modem lock files (Needed by mgetty users)
+#ifdef __linux__
+# define LOCK_DIR "/var/lock"
+#else /* linux */
+# ifdef BSD
+# define LOCK_DIR "/var/spool/lock"
+# else /* BSD */
+# define LOCK_DIR "/var/spool/locks"
+# endif /* BSD */
+#endif /* linux */
+
+// search path for pppd binary
+#define PPPDSEARCHPATH "/sbin:/usr/sbin:/usr/local/sbin:/usr/bin:/usr/local/bin"
+
+// name of the pppd binary
+#define PPPDNAME "pppd"
+
+// support for internal ISDN cards and modem emulation
+#ifdef __linux__
+#define ISDNSUPPORT
+#endif
+
+#if defined(__linux__) || defined(BSD)
+#define PPP_PID_DIR "/var/run/"
+#else
+#define PPP_PID_DIR "/etc/ppp/"
+#endif
+
+// defined in opener.cpp
+extern const char * const kppp_syslog[];
+
+#ifdef _XPG4_2
+#define __xnet_connect connect
+#endif
+
+#define CBTYPE_NONE 0
+#define CBTYPE_ADMIN 1
+#define CBTYPE_USER 2
+
+#endif
diff --git a/kppp/kpppwidget.cpp b/kppp/kpppwidget.cpp
new file mode 100644
index 00000000..9e635815
--- /dev/null
+++ b/kppp/kpppwidget.cpp
@@ -0,0 +1,1073 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "kpppwidget.h"
+
+#include <qapplication.h>
+#include <qcombobox.h>
+#include <qdir.h>
+#include <qevent.h>
+#include <qdialog.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <kdialogbase.h>
+#include <qwhatsthis.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kbuttonbox.h>
+#include <kglobalsettings.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kiconloader.h> // For BarIcon
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kseparator.h>
+#include <kstandarddirs.h>
+#include <kwin.h>
+#include <khelpmenu.h>
+#include <kpushbutton.h>
+#include <kguiitem.h>
+#include <kstdguiitem.h>
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "runtests.h"
+
+#include "main.h"
+#include "auth.h"
+#include "modem.h"
+#include "ppplog.h"
+#include "opener.h"
+#include "requester.h"
+#include "pppstats.h"
+#include "pppdata.h"
+#include "general.h"
+#include "modems.h"
+
+// delay disconnection for a second
+#define DISCONNECT_DELAY 1000
+
+extern KPPPWidget *p_kppp;
+
+KPPPWidget::KPPPWidget( QWidget *parent, const char *name )
+ : DCOPObject( "KpppIface" ), QWidget(parent, name)
+ , acct(0)
+ , m_bCmdlAccount (false)
+ , m_bCmdlModem (false)
+{
+ tabWindow = 0;
+
+ // before doing anything else, run a few tests
+
+ int result = runTests();
+ if(result == TEST_CRITICAL)
+ exit(4);
+
+ installEventFilter(this);
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+
+ l1 = new QGridLayout(4, 4);
+ tl->addLayout(l1);
+ l1->addColSpacing(0, 10);
+ l1->addColSpacing(3, 10);
+ l1->setColStretch(1, 3);
+ l1->setColStretch(2, 4);
+
+ label1 = new QLabel(i18n("C&onnect to: "), this);
+ l1->addWidget(label1, 0, 1);
+
+ connectto_c = new QComboBox(false, this);
+ label1->setBuddy(connectto_c);
+
+ connect(connectto_c, SIGNAL(activated(int)),
+ SLOT(newdefaultaccount(int)));
+ l1->addWidget(connectto_c, 0, 2);
+
+ label7 = new QLabel(i18n("Use &modem: "), this);
+ // l1->addWidget(label7, 1, 1); (done in resetmodems())
+ modem_c = new QComboBox(false, this);
+ label7->setBuddy(connectto_c);
+ m_bModemCShown = false;
+
+ connect(modem_c, SIGNAL(activated(int)),
+ SLOT(newdefaultmodem(int)));
+ // l1->addWidget(modem_c, 1, 2); (done in resetmodems())
+
+ ID_Label = new QLabel(i18n("&Login ID:"), this);
+ l1->addWidget(ID_Label, 2, 1);
+
+ // the entry line for usernames
+ ID_Edit = new QLineEdit(this);
+ ID_Label->setBuddy(ID_Edit);
+ l1->addWidget(ID_Edit, 2, 2);
+ connect(ID_Edit, SIGNAL(returnPressed()),
+ this, SLOT(enterPressedInID()));
+ QString tmp = i18n("<p>Type in the username that you got from your\n"
+ "ISP. This is especially important for PAP\n"
+ "and CHAP. You may omit this when you use\n"
+ "terminal-based or script-based authentication.\n"
+ "\n"
+ "<b>Important</b>: case is important here:\n"
+ "<i>myusername</i> is not the same as <i>MyUserName</i>.");
+
+ QWhatsThis::add(ID_Label,tmp);
+ QWhatsThis::add(ID_Edit,tmp);
+
+ PW_Label = new QLabel(i18n("&Password:"), this);
+ l1->addWidget(PW_Label, 3, 1);
+
+ PW_Edit= new QLineEdit(this);
+ PW_Label->setBuddy(PW_Edit);
+ PW_Edit->setEchoMode(QLineEdit::Password);
+ l1->addWidget(PW_Edit, 3, 2);
+ connect(PW_Edit, SIGNAL(returnPressed()),
+ this, SLOT(enterPressedInPW()));
+
+ tmp = i18n("<p>Type in the password that you got from your\n"
+ "ISP. This is especially important for PAP\n"
+ "and CHAP. You may omit this when you use\n"
+ "terminal-based or script-based authentication.\n"
+ "\n"
+ "<b>Important</b>: case is important here:\n"
+ "<i>mypassword</i> is not the same as <i>MyPassword</i>.");
+
+ QWhatsThis::add(PW_Label,tmp);
+ QWhatsThis::add(PW_Edit,tmp);
+
+ QHBoxLayout *l3 = new QHBoxLayout;
+ tl->addSpacing(5);
+ tl->addLayout(l3);
+ tl->addSpacing(5);
+ l3->addSpacing(10);
+ log = new QCheckBox(i18n("Show lo&g window"), this);
+ connect(log, SIGNAL(toggled(bool)),
+ this, SLOT(log_window_toggled(bool)));
+ log->setChecked(gpppdata.get_show_log_window());
+ l3->addWidget(log);
+
+ QWhatsThis::add(log,
+ i18n("<p>This controls whether a log window is shown.\n"
+ "A log window shows the communication between\n"
+ "<i>kppp</i> and your modem. This will help you\n"
+ "in tracking down problems.\n"
+ "\n"
+ "Turn it off if <i>kppp</i> routinely connects without\n"
+ "problems"));
+
+ fline = new KSeparator( KSeparator::HLine, this);
+ tl->addWidget(fline);
+
+ QHBoxLayout *l2 = new QHBoxLayout;
+ tl->addLayout(l2);
+
+ int minw = 0;
+ quit_b = new KPushButton(KStdGuiItem::quit(), this);
+ connect( quit_b, SIGNAL(clicked()), SLOT(quitbutton()));
+ if(quit_b->sizeHint().width() > minw)
+ minw = quit_b->sizeHint().width();
+
+ setup_b = new KPushButton(KGuiItem(i18n("Co&nfigure..."), "configure"), this);
+ connect( setup_b, SIGNAL(clicked()), SLOT(expandbutton()));
+ if(setup_b->sizeHint().width() > minw)
+ minw = setup_b->sizeHint().width();
+
+ if(gpppdata.access() != KConfig::ReadWrite)
+ setup_b->setEnabled(false);
+
+ help_b = new KPushButton(KStdGuiItem::help(), this);
+ connect( help_b, SIGNAL(clicked()), SLOT(helpbutton()));
+
+ KHelpMenu *helpMenu = new KHelpMenu(this, KGlobal::instance()->aboutData(), true);
+ help_b->setPopup((QPopupMenu*)helpMenu->menu());
+
+ if(help_b->sizeHint().width() > minw)
+ minw = help_b->sizeHint().width();
+
+ connect_b = new QPushButton(i18n("&Connect"), this);
+ connect_b->setDefault(true);
+ connect_b->setFocus();
+ connect(connect_b, SIGNAL(clicked()), SLOT(beginConnect()));
+ if(connect_b->sizeHint().width() > minw)
+ minw = connect_b->sizeHint().width();
+
+ quit_b->setFixedWidth(minw);
+ setup_b->setFixedWidth(minw);
+ help_b->setFixedWidth(help_b->sizeHint().width());
+ connect_b->setFixedWidth(minw);
+
+ l2->addWidget(quit_b);
+ l2->addWidget(setup_b);
+ l2->addWidget(help_b);
+ l2->addSpacing(20);
+ l2->addWidget(connect_b);
+
+ setFixedSize(sizeHint());
+
+ (void)new Modem;
+
+ // we also connect cmld_start to the beginConnect so that I can run
+ // the dialer through a command line argument
+ connect(this,SIGNAL(cmdl_start()),this,SLOT(beginConnect()));
+
+ stats = new PPPStats;
+
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ // constructor of con_win reads position from config file
+ con_win = new ConWindow(0, "conw", this, stats);
+ KWin::setIcons(con_win->winId(), kapp->icon(), kapp->miniIcon());
+
+ statdlg = new PPPStatsDlg(0, "stats", this, stats);
+ statdlg->hide();
+
+ debugwindow = new DebugWidget(0,"debugwindow");
+ KWin::setIcons(debugwindow->winId(), kapp->icon(), kapp->miniIcon());
+ debugwindow->hide();
+
+ // load up the accounts combo box
+
+ resetaccounts();
+ resetmodems();
+ con = new ConnectWidget(0, "con", stats);
+ KWin::setIcons(con->winId(), kapp->icon(), kapp->miniIcon() );
+ connect(this, SIGNAL(begin_connect()),con, SLOT(preinit()));
+
+ QRect desk = KGlobalSettings::desktopGeometry(topLevelWidget());
+ con->setGeometry(desk.center().x()-175, desk.center().y()-55, 350,110);
+
+ // connect the ConnectWidgets various signals
+ connect(con, SIGNAL(closeDebugWindow()),
+ debugwindow, SLOT(hide()));
+ connect(con, SIGNAL(debugMessage(const QString &)),
+ debugwindow, SLOT(statusLabel(const QString &)));
+ connect(con, SIGNAL(toggleDebugWindow()),
+ debugwindow, SLOT(toggleVisibility()));
+ connect(con, SIGNAL(debugPutChar(unsigned char)),
+ debugwindow, SLOT(addChar(unsigned char)));
+ connect(con, SIGNAL(startAccounting()),
+ this, SLOT(startAccounting()));
+ connect(con, SIGNAL(stopAccounting()),
+ this, SLOT(stopAccounting()));
+ connect(KApplication::kApplication(), SIGNAL(saveYourself()),
+ this, SLOT(saveMyself()));
+ connect(KApplication::kApplication(), SIGNAL(shutDown()),
+ this, SLOT(shutDown()));
+
+ debugwindow->setGeometry(desk.center().x()+190, desk.center().y()-55,
+ debugwindow->width(),debugwindow->height());
+
+ move(desk.center().x()-width()/2, desk.center().y()-height()/2);
+
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ m_strCmdlAccount = args->getOption("c");
+ m_strCmdlModem = args->getOption("m");
+ m_bQuitOnDisconnect = args->isSet("q");
+
+ if(!m_strCmdlAccount.isEmpty()) {
+ m_bCmdlAccount = true;
+ kdDebug(5002) << "cmdl_account: " << m_bCmdlAccount << endl;
+ }
+
+ if(!m_strCmdlModem.isEmpty()) {
+ m_bCmdlModem = true;
+ kdDebug(5002) << "cmdl_modem: " << m_bCmdlModem << endl;
+ }
+
+ if(m_bCmdlModem){
+ bool result = gpppdata.setModem(m_strCmdlModem);
+ if (!result){
+ QString string;
+ string = i18n("No such Modem:\n%1\nFalling back to default").arg(m_strCmdlModem);
+ KMessageBox::error(this, string);
+ m_bCmdlModem = false;
+ }
+ }
+
+ if(m_bCmdlAccount){
+ bool result = gpppdata.setAccount(m_strCmdlAccount);
+ if (!result){
+ QString string;
+ string = i18n("No such Account:\n%1").arg(m_strCmdlAccount);
+ KMessageBox::error(this, string);
+ m_bCmdlAccount = false;
+ show();
+ } else {
+ beginConnect();
+ }
+ } else
+ show();
+
+
+//#define KPPP_SHOW_NEWS
+#ifdef KPPP_SHOW_NEWS
+ // keep user informed about recent changes
+ if(!m_bCmdlAccount)
+ showNews();
+#endif
+
+ // attach to the DCOP server, if possible
+ if (!kapp->dcopClient()->attach())
+ kdDebug(5002) << "Error: Could not connect to the DCOP server" << endl;
+ else
+ kapp->dcopClient()->registerAs(kapp->name(), true);
+
+ // this timer will delay the actual disconnection DISCONNECTION_DELAY ms
+ // to give applications time to shutdown, logout, whatever..
+ disconnectTimer = new QTimer(this);
+ connect(disconnectTimer, SIGNAL(timeout()), this, SLOT(delayedDisconnect()));
+}
+
+KPPPWidget::~KPPPWidget()
+{
+ delete stats;
+}
+
+bool KPPPWidget::eventFilter(QObject *o, QEvent *e) {
+ if(e->type() == QEvent::User) {
+ switch(((SignalEvent*)e)->sigType()) {
+ case SIGINT:
+ kdDebug(5002) << "Received a SIGINT" << endl;
+ interruptConnection();
+ break;
+ case SIGCHLD:
+ sigChld();
+ break;
+ case SIGUSR1:
+ sigPPPDDied();
+ break;
+ }
+ return true;
+ }
+
+ if(o == connect_b) {
+ if(e->type() == QEvent::KeyPress) {
+ if(connect_b->hasFocus() && ((QKeyEvent *)e)->key() == Qt::Key_Return) {
+ beginConnect();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void KPPPWidget::prepareSetupDialog() {
+ if(tabWindow == 0) {
+ tabWindow = new KDialogBase( KDialogBase::Tabbed, i18n("KPPP Configuration"),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok,
+ kapp->mainWidget(), 0, true);
+
+
+ KWin::setIcons(tabWindow->winId(), kapp->icon(), kapp->miniIcon());
+
+ // tabWindow->setFixedSize( 365, 375 );
+
+ accounts = new AccountWidget(tabWindow->addPage( i18n("&Accounts"), i18n("Account Setup") ) );
+ connect(accounts, SIGNAL(resetaccounts()),
+ this, SLOT(resetaccounts()));
+ connect(accounts, SIGNAL(resetCosts(const QString &)),
+ this, SLOT(resetCosts(const QString &)));
+ connect(accounts, SIGNAL(resetVolume(const QString &)),
+ this, SLOT(resetVolume(const QString &)));
+
+ modems = new ModemsWidget(tabWindow->addPage( i18n("&Modems"), i18n("Modems Setup") ) );
+ connect(modems, SIGNAL(resetmodems()),
+ this, SLOT(resetmodems()));
+
+ graph = new GraphSetup( tabWindow->addPage( i18n("&Graph"), i18n("Throughput Graph" ) ) );
+ general = new GeneralWidget( tabWindow->addPage( i18n("M&isc"), i18n("Miscellaneous Settings") ) );
+ }
+}
+
+
+void KPPPWidget::enterPressedInID() {
+ PW_Edit->setFocus();
+}
+
+
+void KPPPWidget::enterPressedInPW() {
+ connect_b->setFocus();
+}
+
+// triggered by the session manager
+void KPPPWidget::saveMyself() {
+ gpppdata.save();
+}
+
+void KPPPWidget::shutDown() {
+ interruptConnection();
+ saveMyself();
+}
+
+void KPPPWidget::log_window_toggled(bool on) {
+ gpppdata.set_show_log_window(on);
+}
+
+
+void KPPPWidget::setup() {
+ prepareSetupDialog();
+
+ if(tabWindow->exec())
+ gpppdata.save();
+ else
+ gpppdata.cancel();
+}
+
+
+void KPPPWidget::resetaccounts() {
+ connectto_c->clear();
+
+ int count = gpppdata.accountCount();
+
+ // enable/disable controls
+ connectto_c->setEnabled(count > 0);
+ setButtons();
+
+ //load the accounts
+ for(int i=0; i < count; i++) {
+ gpppdata.setAccountByIndex(i);
+ connectto_c->insertItem(gpppdata.accname());
+ }
+
+ //set the default account
+ if(!gpppdata.defaultAccount().isEmpty()) {
+ for(int i=0; i < count; i++)
+ if(gpppdata.defaultAccount() == connectto_c->text(i)) {
+ connectto_c->setCurrentItem(i);
+ gpppdata.setAccountByIndex(i);
+
+ ID_Edit->setText(gpppdata.storedUsername());
+ PW_Edit->setText(gpppdata.storedPassword());
+ }
+ }
+ else
+ if(count > 0) {
+ gpppdata.setDefaultAccount(connectto_c->text(0));
+ gpppdata.save();
+ ID_Edit->setText(gpppdata.storedUsername());
+ PW_Edit->setText(gpppdata.storedPassword());
+ }
+
+ connect(ID_Edit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(usernameChanged(const QString &)));
+
+ connect(PW_Edit, SIGNAL(textChanged(const QString &)),
+ this, SLOT(passwordChanged(const QString &)));
+
+ if (ID_Edit->text().isEmpty())
+ ID_Edit->setFocus();
+ else if (PW_Edit->text().isEmpty())
+ PW_Edit->setFocus();
+
+}
+
+void KPPPWidget::resetmodems() {
+ modem_c->clear();
+
+ int count = gpppdata.modemCount();
+
+ // enable/disable controls
+ modem_c->setEnabled(count > 0);
+ setButtons();
+
+ //load the modems
+ for(int i=0; i < count; i++) {
+ gpppdata.setModemByIndex(i);
+ modem_c->insertItem(gpppdata.modname());
+ }
+
+ if (count > 1 && !m_bModemCShown) {
+ l1->addWidget(label7, 1, 1);
+ l1->addWidget(modem_c, 1, 2);
+ m_bModemCShown = true;
+ } else if (count <= 1 && m_bModemCShown){
+ l1->remove(label7);
+ l1->remove(modem_c);
+ m_bModemCShown = false;
+ }
+ label7->setShown(m_bModemCShown);
+ modem_c->setShown(m_bModemCShown);
+ layout()->invalidate();
+ setFixedSize(sizeHint());
+
+ //set the default modem
+ if(!gpppdata.defaultModem().isEmpty()) {
+ for(int i=0; i < count; i++)
+ if(gpppdata.defaultModem() == modem_c->text(i)) {
+ modem_c->setCurrentItem(i);
+ gpppdata.setModemByIndex(i);
+
+ }
+ }
+ else
+ if(count > 0) {
+ gpppdata.setDefaultModem(modem_c->text(0));
+ gpppdata.save();
+ }
+}
+
+void KPPPWidget::setButtons()
+{
+ int acccount = gpppdata.accountCount();
+ int modcount = gpppdata.modemCount();
+
+ // enable/disable controls
+ connect_b->setEnabled(acccount > 0 && modcount > 0);
+ log->setEnabled(acccount > 0 && modcount > 0);
+ ID_Edit->setEnabled(acccount > 0 && modcount > 0);
+ PW_Edit->setEnabled(acccount > 0 && modcount > 0);
+
+}
+
+
+void KPPPWidget::interruptConnection() {
+ // interrupt dial up
+ if (con->isVisible())
+ emit con->cancelbutton();
+
+ // disconnect if online
+ if (gpppdata.pppdRunning())
+ emit disconnect(); /* FIXME: are we emitting a slot here!!!??? */
+}
+
+void KPPPWidget::sigPPPDDied() {
+ kdDebug(5002) << "Received a SIGUSR1" << endl;
+
+ // if we are not connected pppdpid is -1 so have have to check for that
+ // in the followin line to make sure that we don't raise a false alarm
+ // such as would be the case when the log file viewer exits.
+ if(gpppdata.pppdRunning() || gpppdata.pppdError()) {
+ kdDebug(5002) << "It was pppd that died" << endl;
+
+ // when we killpppd() on Cancel in ConnectWidget
+ // we set pppid to -1 so we won't
+ // enter this block
+
+ // just to be sure
+ Requester::rq->removeSecret(AUTH_PAP);
+ Requester::rq->removeSecret(AUTH_CHAP);
+
+ gpppdata.setpppdRunning(false);
+
+ // stop the disconnect timer (just in case)
+ disconnectTimer->stop();
+ // signal other applications that we are disconnected now
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "disconnected()", QByteArray());
+
+ kdDebug(5002) << "Executing command on disconnect since pppd has died." << endl;
+ QApplication::flushX();
+ execute_command(gpppdata.command_on_disconnect());
+
+ stopAccounting();
+
+ con_win->stopClock();
+ DockWidget::dock_widget->stop_stats();
+ DockWidget::dock_widget->hide();
+
+ if(!gpppdata.pppdError())
+ gpppdata.setpppdError(E_PPPD_DIED);
+ removedns();
+ Modem::modem->unlockdevice();
+ con->pppdDied();
+
+ Requester::rq->pppdExitStatus();
+ gpppdata.setWaitCallback(gpppdata.callbackType() && Requester::rq->lastStatus == E_CBCP_WAIT);
+
+ if(!gpppdata.automatic_redial() && !gpppdata.waitCallback()) {
+ quit_b->setFocus();
+ show();
+ con_win->stopClock();
+ stopAccounting();
+ con_win->hide();
+ con->hide();
+
+ gpppdata.setpppdRunning(false);
+ // // not in a signal handler !!! KNotifyClient::beep();
+ QString msg;
+ if (gpppdata.pppdError() == E_IF_TIMEOUT)
+ msg = i18n("Timeout expired while waiting for the PPP interface "
+ "to come up.");
+ else {
+ msg = i18n("<p>The pppd daemon died unexpectedly!</p>");
+ Requester::rq->pppdExitStatus();
+ if (Requester::rq->lastStatus != 99) { // more recent pppds only
+ msg += i18n("<p>Exit status: %1").arg(Requester::rq->lastStatus);
+ msg += i18n("</p><p>See 'man pppd' for an explanation of the error "
+ "codes or take a look at the kppp FAQ on "
+ " <a href=\"%1\">%2</a></p>")
+ .arg("http://developer.kde.org/~kppp/index.html")
+ .arg("http://developer.kde.org/~kppp/index.html");
+ }
+ }
+
+ if(KMessageBox::warningYesNo(0, msg, i18n("Error"), KStdGuiItem::ok(), i18n("&Details")) == KMessageBox::No)
+ PPPL_ShowLog();
+ } else { /* reconnect on disconnect */
+ if(gpppdata.waitCallback())
+ kdDebug(5002) << "Waiting for callback... " << endl;
+ else
+ kdDebug(5002) << "Trying to reconnect... " << endl;
+
+ if(gpppdata.authMethod() == AUTH_PAP ||
+ gpppdata.authMethod() == AUTH_CHAP ||
+ gpppdata.authMethod() == AUTH_PAPCHAP)
+ Requester::rq->setSecret(gpppdata.authMethod(),
+ encodeWord(gpppdata.storedUsername()),
+ encodeWord(gpppdata.password()));
+
+ con_win->hide();
+ con_win->stopClock();
+ stopAccounting();
+ gpppdata.setpppdRunning(false);
+ // not in a signal handler !!! KNotifyClient::beep();
+ emit cmdl_start();
+ }
+ }
+ gpppdata.setpppdError(0);
+}
+
+
+void KPPPWidget::sigChld() {
+ kdDebug(5002) << "sigchld()" << endl;
+ // pid_t id = wait(0L);
+ // if(id == helperPid && helperPid != -1) {
+ // kdDebug(5002) << "It was the setuid child that died" << endl;
+ // helperPid = -1;
+ QString msg = i18n("kppp's helper process just died.\n"
+ "Since further execution would be pointless, "
+ "kppp will shut down now.");
+ KMessageBox::error(0L, msg);
+ remove_pidfile();
+ exit(1);
+ // }
+}
+
+
+void KPPPWidget::newdefaultaccount(int i) {
+ gpppdata.setDefaultAccount(connectto_c->text(i));
+ gpppdata.save();
+ ID_Edit->setText(gpppdata.storedUsername());
+ PW_Edit->setText(gpppdata.storedPassword());
+}
+
+void KPPPWidget::newdefaultmodem(int i) {
+ gpppdata.setDefaultModem(modem_c->text(i));
+ gpppdata.save();
+}
+
+
+
+void KPPPWidget::expandbutton() {
+ setup();
+}
+
+
+void KPPPWidget::beginConnect() {
+ // make sure to connect to the account that is selected in the combo box
+ // (exeption: an account given by a command line argument)
+ if(!m_bCmdlAccount) {
+ gpppdata.setAccount(connectto_c->currentText());
+ gpppdata.setPassword(PW_Edit->text());
+ } else {
+ gpppdata.setPassword(gpppdata.storedPassword());
+ }
+
+ QFileInfo info(pppdPath());
+
+ if(!info.exists()){
+ KMessageBox::error(this, i18n("Cannot find the PPP daemon!\n"
+ "Make sure that pppd is installed and "
+ "that you have entered the correct path."));
+ return;
+ }
+#if 0
+ if(!info.isExecutable()){
+
+ QString string;
+ string = i18n("kppp cannot execute:\n %1\n"
+ "Please make sure that you have given kppp "
+ "setuid permission and that "
+ "pppd is executable.").arg(gpppdata.pppdPath());
+ KMessageBox::error(this, string);
+ return;
+
+ }
+#endif
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ QString device = "";
+ if (args->isSet("dev"))
+ device = args->getOption("dev");
+ else
+ device = gpppdata.modemDevice();
+
+ QFileInfo info2(device);
+
+ if(!info2.exists()){
+ QString string;
+ string = i18n("kppp can not find:\n %1\nPlease make sure you have setup "
+ "your modem device properly "
+ "and/or adjust the location of the modem device on "
+ "the modem tab of "
+ "the setup dialog.").arg(device);
+ KMessageBox::error(this, string);
+ return;
+ }
+
+ // if this is a PAP or CHAP account, ensure that username is
+ // supplied
+ if(gpppdata.authMethod() == AUTH_PAP ||
+ gpppdata.authMethod() == AUTH_CHAP ||
+ gpppdata.authMethod() == AUTH_PAPCHAP ) {
+ if(ID_Edit->text().isEmpty()) {
+ KMessageBox::error(this,
+ i18n(
+ "You have selected the authentication "
+ "method PAP or CHAP. This requires that you "
+ "supply a username and a password."));
+ return;
+ } else {
+ if(!Requester::rq->setSecret(gpppdata.authMethod(),
+ encodeWord(gpppdata.storedUsername()),
+ encodeWord(gpppdata.password()))) {
+ QString s;
+ s = i18n("Cannot create PAP/CHAP authentication\n"
+ "file \"%1\"").arg(PAP_AUTH_FILE);
+ KMessageBox::error(this, s);
+ return;
+ }
+ }
+ }
+
+ if (gpppdata.phonenumber().isEmpty()) {
+ QString s = i18n("You must specify a telephone number.");
+ KMessageBox::error(this, s);
+ return;
+ }
+
+ hide();
+
+ QString tit = i18n("Connecting to: %1").arg(gpppdata.accname());
+ con->setCaption(tit);
+ con->enableButtons();
+ con->show();
+
+ bool show_debug = gpppdata.get_show_log_window();
+ con->debug->setOn(show_debug); // toggle button
+ debugwindow->clear();
+ if (!show_debug)
+ debugwindow->hide();
+ else {
+ debugwindow->show();
+ con->raise();
+ }
+
+ emit begin_connect();
+}
+
+void KPPPWidget::disconnect() {
+ if (disconnectTimer->isActive()) return; // you had already pressed disconnect before
+
+ // signal other applications that we are about to go offline now
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "aboutToDisconnect()", QByteArray());
+ con_win->hide();
+ con->show();
+ con->disableButtons(); // will reenable them later in delayedDisconnect()
+ con->setCaption(i18n("Disconnecting..."));
+
+ if (!gpppdata.command_before_disconnect().isEmpty()) {
+ con->setMsg(i18n("Executing command before disconnection."));
+
+ kapp->processEvents();
+ QApplication::flushX();
+ pid_t id = execute_command(gpppdata.command_before_disconnect());
+ int i, status;
+
+ do {
+ kapp->processEvents();
+ i = waitpid(id, &status, WNOHANG);
+ usleep(500000);
+ } while (i == 0 && errno == 0);
+ }
+
+ con->setMsg(i18n("Announcing disconnection."));
+
+ // this is no longer necessary since I'm delaying disconnection usign a QTimer
+ // kapp->processEvents();
+
+ // set the timer to call delayedDisconnect() in DISCONNECT_DELAY ms
+ disconnectTimer->start(DISCONNECT_DELAY, true);
+}
+
+void KPPPWidget::delayedDisconnect() {
+ // this is where the actual disconnection takes place
+ con->hide();
+
+ statdlg->stop_stats();
+ Requester::rq->killPPPDaemon();
+
+ // signal other applications that we are disconnected now
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "disconnected()", QByteArray());
+
+ QApplication::flushX();
+ execute_command(gpppdata.command_on_disconnect());
+
+ Requester::rq->removeSecret(AUTH_PAP);
+ Requester::rq->removeSecret(AUTH_CHAP);
+
+ removedns();
+ Modem::modem->unlockdevice();
+
+ con_win->stopClock();
+ p_kppp->stopAccounting();
+ con_win->hide();
+
+ DockWidget::dock_widget->stop_stats();
+ DockWidget::dock_widget->hide();
+
+ if(m_bQuitOnDisconnect)
+ kapp->exit(0);
+ else {
+ quit_b->setFocus();
+ show();
+ }
+}
+
+
+void KPPPWidget::helpbutton() {
+ kapp->invokeHelp();
+}
+
+
+void KPPPWidget::quitbutton() {
+ if(gpppdata.pppdRunning()) {
+ int ok = KMessageBox::warningContinueCancel(this,
+ i18n("Exiting kPPP will close your PPP Session."),
+ i18n("Quit kPPP?"), KStdGuiItem::quit());
+ if(ok == KMessageBox::Continue) {
+ Requester::rq->killPPPDaemon();
+
+ // stop the disconnect delay timer
+ disconnectTimer->stop();
+
+ // signal other applications that we are disconnected now
+ kapp->dcopClient()->emitDCOPSignal("KpppIface", "disconnected()", QByteArray());
+
+ QApplication::flushX();
+ execute_command(gpppdata.command_on_disconnect());
+ removedns();
+ Modem::modem->unlockdevice();
+ }
+ } else {
+ if (!gpppdata.accname().isEmpty() && !gpppdata.storePassword())
+ gpppdata.setStoredPassword("");
+ }
+ gpppdata.save();
+ kapp->quit();
+}
+
+
+void KPPPWidget::rulesetLoadError() {
+ KMessageBox::error(this, ruleset_load_errmsg);
+}
+
+
+void KPPPWidget::startAccounting() {
+ // volume accounting
+ stats->totalbytes = 0;
+
+ kdDebug(5002) << "AcctEnabled: " << gpppdata.AcctEnabled() << endl;
+
+ // load the ruleset
+ if(!gpppdata.AcctEnabled())
+ return;
+
+ QString d = AccountingBase::getAccountingFile(gpppdata.accountingFile());
+ // if(::access(d.data(), X_OK) != 0)
+ acct = new Accounting(this, stats);
+ // else
+ // acct = new ExecutableAccounting(this);
+
+ // connect to the accounting object
+ connect(acct, SIGNAL(changed(QString, QString)),
+ con_win, SLOT(slotAccounting(QString, QString)));
+
+ if(!acct->loadRuleSet(gpppdata.accountingFile())) {
+ QString s= i18n("Can not load the accounting "
+ "ruleset \"%1\".").arg(gpppdata.accountingFile());
+
+ // starting the messagebox with a timer will prevent us
+ // from blocking the calling function ConnectWidget::timerEvent
+ ruleset_load_errmsg = s;
+ QTimer::singleShot(0, this, SLOT(rulesetLoadError()));
+ return;
+ } else
+ acct->slotStart();
+}
+
+void KPPPWidget::stopAccounting() {
+ // store volume accounting
+ if(stats->totalbytes != 0)
+ gpppdata.setTotalBytes(stats->totalbytes);
+
+ if(!gpppdata.AcctEnabled())
+ return;
+
+ if(acct != 0) {
+ acct->slotStop();
+ delete acct;
+ acct = 0;
+ }
+}
+
+
+void KPPPWidget::showStats() {
+ if(statdlg) {
+ statdlg->toCurrentDesktop();
+ statdlg->show();
+ statdlg->raise();
+ }
+}
+
+
+void KPPPWidget::usernameChanged(const QString &) {
+ // store username for later use
+ gpppdata.setStoredUsername(ID_Edit->text());
+}
+
+
+void KPPPWidget::passwordChanged(const QString &) {
+ // store the password if so requested
+ if(gpppdata.storePassword())
+ gpppdata.setStoredPassword(PW_Edit->text());
+ else
+ gpppdata.setStoredPassword("");
+}
+
+
+void KPPPWidget::setPW_Edit(const QString &pw) {
+ PW_Edit->setText(pw);
+}
+
+
+void KPPPWidget::resetCosts(const QString &s) {
+ AccountingBase::resetCosts(s);
+}
+
+
+void KPPPWidget::resetVolume(const QString &s) {
+ AccountingBase::resetVolume(s);
+}
+
+/**
+ * pppd's getword() function knows about escape characters.
+ * If we write the username and password to the secrets file
+ * we'll therefore have to escape back slashes.
+ */
+QString KPPPWidget::encodeWord(const QString &s) {
+ QString r = s;
+ r.replace(QRegExp("\\"), "\\\\");
+ return r;
+}
+
+void KPPPWidget::setQuitOnDisconnect (bool b)
+{
+ m_bQuitOnDisconnect = b;
+}
+
+void KPPPWidget::showNews() {
+#ifdef KPPP_SHOW_NEWS
+ /*
+ * Introduce the QuickHelp feature to new users of this version
+ */
+ #define QUICKHELP_HINT "Hint_QuickHelp"
+ if(gpppdata.readNumConfig(GENERAL_GRP, QUICKHELP_HINT, 0) == 0) {
+ QDialog dlg(0, 0, true);
+ dlg.setCaption(i18n("Recent Changes in KPPP"));
+
+ QVBoxLayout *tl = new QVBoxLayout(&dlg, 10, 10);
+ QHBoxLayout *l1 = new QHBoxLayout(10);
+ QVBoxLayout *l2 = new QVBoxLayout(10);
+ tl->addLayout(l1);
+
+ QLabel *icon = new QLabel(&dlg);
+ icon->setPixmap(BarIcon("exclamation"));
+ icon->setFixedSize(icon->sizeHint());
+ l1->addWidget(icon);
+ l1->addLayout(l2);
+
+ QLabel *l = new QLabel(i18n("From version 1.4.8 on, kppp has a new feature\n"
+ "called \"Quickhelp\". It's similar to a tooltip,\n"
+ "but you can activate it whenever you want.\n"
+ "\n"
+ "To activate it, simply click on a control like\n"
+ "a button or a label with the right mouse button.\n"
+ "If the item supports Quickhelp, a popup menu\n"
+ "will appear leading to Quickhelp.\n"
+ "\n"
+ "To test it, right-click somewhere in this text."),
+ &dlg);
+
+ QCheckBox *cb = new QCheckBox(i18n("Don't show this hint again"), &dlg);
+ cb->setFixedSize(cb->sizeHint());
+
+ KButtonBox *bbox = new KButtonBox(&dlg);
+ bbox->addStretch(1);
+ QPushButton *ok = bbox->addButton(KStdGuiItem::ok());
+ ok->setDefault(true);
+ dlg.connect(ok, SIGNAL(clicked()),
+ &dlg, SLOT(accept()));
+ bbox->addStretch(1);
+ bbox->layout();
+
+ l2->addWidget(l);
+ l2->addWidget(cb);
+ tl->addWidget(bbox);
+
+ QString tmp = i18n("This is an example of <b>QuickHelp</b>.\n"
+ "This window will stay open until you\n"
+ "click a mouse button or a press a key.\n");
+
+ QWhatsThis::add(cb,tmp);
+ QWhatsThis::add(l, tmp);
+
+ dlg.exec();
+ if(cb->isChecked()) {
+ gpppdata.writeConfig(GENERAL_GRP, QUICKHELP_HINT, 1);
+ gpppdata.save();
+ }
+ }
+#endif
+}
+
+
+#include "kpppwidget.moc"
+
diff --git a/kppp/kpppwidget.h b/kppp/kpppwidget.h
new file mode 100644
index 00000000..5ed4f85c
--- /dev/null
+++ b/kppp/kpppwidget.h
@@ -0,0 +1,174 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KPPPWIDGET_H__
+#define __KPPPWIDGET_H__
+
+#include <qstring.h>
+
+#include <dcopclient.h>
+
+#include "accounting.h"
+#include "conwindow.h"
+#include "accounts.h"
+#include "connect.h"
+#include "debug.h"
+#include "pppstatdlg.h"
+
+#include "KPPPIface.h"
+
+class GeneralWidget;
+class ModemsWidget;
+class ModemWidget;
+class ModemWidget2;
+class GraphSetup;
+class AboutWidget;
+class PPPStats;
+class KDialogBase;
+class KPushButton;
+
+class SignalEvent : public QEvent {
+public:
+ SignalEvent(int s) : QEvent(User), sig(s) { }
+ int sigType() const { return sig; }
+private:
+ int sig;
+};
+
+
+class KPPPWidget : public QWidget, virtual public KpppIface {
+ Q_OBJECT
+public:
+
+ KPPPWidget( QWidget *parent=0, const char *name=0 );
+ ~KPPPWidget();
+
+ void setPW_Edit(const QString &);
+ virtual bool eventFilter(QObject *, QEvent *);
+
+ void setQuitOnDisconnect (bool b);
+ bool quitOnDisconnect () {return m_bQuitOnDisconnect;};
+
+private slots:
+ void newdefaultaccount(int);
+ void newdefaultmodem(int);
+ void expandbutton();
+ void quitbutton();
+ void helpbutton();
+ void setup();
+ void rulesetLoadError();
+ void usernameChanged(const QString &);
+ void passwordChanged(const QString &);
+ void enterPressedInID();
+ void enterPressedInPW();
+ void saveMyself();
+ void shutDown();
+
+ void delayedDisconnect();
+
+public slots:
+ void beginConnect();
+ void resetaccounts();
+ void resetmodems();
+ void resetCosts(const QString &);
+ void resetVolume(const QString &);
+ void disconnect();
+ void log_window_toggled(bool on);
+ void startAccounting();
+ void stopAccounting();
+ void showStats();
+ bool isConnected() const {return connected;};
+signals:
+ void begin_connect();
+ void cmdl_start();
+
+public:
+ QCheckBox *log;
+ bool connected;
+ DebugWidget *debugwindow;
+ QString con_speed;
+ ConnectWidget *con;
+ ConWindow *con_win;
+ PPPStatsDlg *statdlg;
+ AccountingBase *acct;
+ KPushButton *quit_b;
+ PPPStats *stats;
+
+private:
+ void prepareSetupDialog();
+ void interruptConnection();
+ void sigChld();
+ void sigPPPDDied();
+ QString encodeWord(const QString &s);
+ void showNews ();
+ void setButtons();
+
+ QString ruleset_load_errmsg;
+
+ QGridLayout *l1;
+ KPushButton *help_b;
+ KPushButton *setup_b;
+ QFrame *fline;
+ QFrame *fline1;
+ QPushButton *connect_b;
+ QComboBox *connectto_c;
+ QComboBox *modem_c;
+ QLabel *ID_Label;
+ QLabel *PW_Label;
+ QLineEdit *ID_Edit;
+ QLineEdit *PW_Edit;
+ QLabel *label1;
+ QLabel *label2;
+ QLabel *label3;
+ QLabel *label4;
+ QLabel *label5;
+ QLabel *label6;
+ QLabel *label7;
+ QLabel *radio_label;
+
+
+ KDialogBase *tabWindow;
+ AccountWidget *accounts;
+ GeneralWidget *general;
+ ModemsWidget *modems;
+ GraphSetup *graph;
+ AboutWidget *about;
+
+
+ QString m_strCmdlAccount;
+ QString m_strCmdlModem;
+ bool m_bQuitOnDisconnect;
+ bool m_bCmdlAccount;
+ bool m_bCmdlModem;
+ bool m_bModemCShown;
+
+
+ QTimer *disconnectTimer;
+};
+
+
+#endif
+
diff --git a/kppp/loginterm.cpp b/kppp/loginterm.cpp
new file mode 100644
index 00000000..a01fcf2d
--- /dev/null
+++ b/kppp/loginterm.cpp
@@ -0,0 +1,168 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997-98 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "kstdguiitem.h"
+
+#include "loginterm.h"
+#include "main.h"
+#include "modem.h"
+
+#include <stdio.h>
+#include <klocale.h>
+
+extern KPPPWidget *p_kppp;
+
+LoginMultiLineEdit::LoginMultiLineEdit(QWidget *parent, const char *name)
+ : QMultiLineEdit(parent, name)
+{
+}
+
+
+LoginMultiLineEdit::~LoginMultiLineEdit() {
+ Modem::modem->stop();
+}
+
+
+void LoginMultiLineEdit::insertChar(unsigned char c) {
+ QMultiLineEdit::insert(QChar(c));
+ p_kppp->debugwindow->addChar(c);
+}
+
+
+void LoginMultiLineEdit::myreturn() {
+ QMultiLineEdit::home();
+}
+
+
+void LoginMultiLineEdit::mynewline() {
+ QMultiLineEdit::end(FALSE);
+ QMultiLineEdit::newLine();
+
+ p_kppp->debugwindow->addChar('\n');
+}
+
+
+void LoginMultiLineEdit::keyPressEvent(QKeyEvent *k) {
+ unsigned char c = (unsigned char) k->ascii();
+
+ if ((int)c == 0) return;
+
+ if((int)c == 13)
+ Modem::modem->writeLine("");
+ else
+ Modem::modem->writeChar(c);
+}
+
+
+void LoginMultiLineEdit::readChar(unsigned char c) {
+
+ if(((int)c != 13) && ((int)c != 10) && ((int)c != 8))
+ insertChar(c);
+
+ if((int)c == 8)
+ backspace();
+ if((int)c == 127)
+ backspace();
+ if((int)c == 10)
+ mynewline();
+ if((int)c == 13)
+ myreturn();
+}
+
+
+LoginTerm::LoginTerm (QWidget *parent, const char *name)
+ : QDialog(parent, name, FALSE)
+{
+ setCaption(i18n("Login Terminal Window"));
+ setMinimumSize(300, 200);
+ setMaximumSize(600, 400);
+ resize(400, 300);
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 2);
+ QGridLayout *vgr = new QGridLayout(2, 1);
+ QGridLayout *hgr = new QGridLayout(1, 2, 30);
+
+ tl->addLayout(vgr);
+ vgr->addLayout(hgr, 1, 0);
+ vgr->setRowStretch(0, 1);
+ vgr->addRowSpacing(1, 40);
+
+ text_window = new LoginMultiLineEdit(this, "term");
+ text_window->setFocus();
+ vgr->addWidget(text_window, 0, 0);
+
+ cancel_b = new KPushButton(KStdGuiItem::cancel(), this, "cancel");
+ cancel_b->setFixedHeight(25);
+ connect(cancel_b, SIGNAL(clicked()), SLOT(cancelbutton()));
+
+ continue_b = new KPushButton(KStdGuiItem::cont(), this, "continue");
+ continue_b->setFixedHeight(25);
+ connect(continue_b, SIGNAL(clicked()), SLOT(continuebutton()));
+
+ int mwidth;
+ if (cancel_b->sizeHint().width() > continue_b->sizeHint().width())
+ mwidth = cancel_b->sizeHint().width();
+ else
+ mwidth = continue_b->sizeHint().width();
+
+ cancel_b->setFixedWidth(mwidth + 20);
+ continue_b->setFixedWidth(mwidth + 20);
+
+ hgr->addWidget(cancel_b, 0, 0, AlignCenter);
+ hgr->addWidget(continue_b, 0, 1, AlignCenter);
+
+ cont = false;
+
+ Modem::modem->notify(text_window, SLOT(readChar(unsigned char)));
+}
+
+
+void LoginTerm::cancelbutton () {
+ hide();
+}
+
+
+void LoginTerm::continuebutton() {
+ cont = true;
+ hide();
+}
+
+
+bool LoginTerm::pressedContinue() {
+ return cont;
+}
+
+
+#include "loginterm.moc"
+
+
+
+
+
+
+
+
+
diff --git a/kppp/loginterm.h b/kppp/loginterm.h
new file mode 100644
index 00000000..1fa3f05d
--- /dev/null
+++ b/kppp/loginterm.h
@@ -0,0 +1,50 @@
+#ifndef _LOGINTERM_H_
+#define _LOGINTERM_H_
+
+#include <qdialog.h>
+#include <qmultilineedit.h>
+#include <qapplication.h>
+#include <qtimer.h>
+
+class LoginMultiLineEdit : public QMultiLineEdit {
+
+ Q_OBJECT
+
+public:
+
+ LoginMultiLineEdit(QWidget *parent, const char *name);
+ ~LoginMultiLineEdit();
+
+ void keyPressEvent(QKeyEvent *k);
+ void insertChar(unsigned char c);
+ void myreturn();
+ void mynewline();
+
+public slots:
+ void readChar(unsigned char);
+};
+
+
+class LoginTerm : public QDialog {
+ Q_OBJECT
+public:
+ LoginTerm(QWidget *parent, const char *name);
+
+ bool pressedContinue();
+
+public slots:
+ void cancelbutton();
+ void continuebutton();
+
+private:
+ LoginMultiLineEdit *text_window;
+ QPushButton *cancel_b;
+ QPushButton *continue_b;
+
+ bool cont;
+};
+
+#endif
+
+
+
diff --git a/kppp/logview/Makefile.am b/kppp/logview/Makefile.am
new file mode 100644
index 00000000..e95b7c73
--- /dev/null
+++ b/kppp/logview/Makefile.am
@@ -0,0 +1,45 @@
+
+# this 10 paths are KDE specific. Use them:
+# kde_htmldir Where your docs should go to. (contains lang subdirs)
+# kde_appsdir Where your application file (.desktop) should go to.
+# kde_icondir Where your icon should go to.
+# kde_minidir Where your mini icon should go to.
+# kde_datadir Where you install application data. (Use a subdir)
+# kde_locale Where translation files should go to.(contains lang subdirs)
+# kde_cgidir Where cgi-bin executables should go to.
+# kde_confdir Where config files should go to.
+# kde_mimedir Where mimetypes should go to.
+# kde_toolbardir Where general toolbar icons should go to.
+# kde_wallpaperdir Where general wallpapers should go to.
+
+# if you use a variable *dir and have *_DATA, it will be installed by
+# make install
+xdg_apps_DATA = kppplogview.desktop
+
+EXTRA_DIST = $(xdg_apps_DATA)
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+# claim, which subdirectories you want to install
+
+# This one gets installed
+bin_PROGRAMS = kppplogview
+
+# Which sources should be compiled for kppp.
+kppplogview_SOURCES = main.cpp loginfo.cpp log.cpp monthly.cpp export.cpp
+
+# the library search path
+kppplogview_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+kppplogview_LDADD = -lm $(LIB_KDEUI) $(LIB_KFILE)
+
+# this option you can leave out. Just, if you use "make dist", you need it
+noinst_HEADERS = export.h loginfo.h log.h monthly.h main.h
+
+METASOURCES = main.moc monthly.moc export.moc
+
+messages:
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kppplogview.pot
+
diff --git a/kppp/logview/export.cpp b/kppp/logview/export.cpp
new file mode 100644
index 00000000..6bbf2159
--- /dev/null
+++ b/kppp/logview/export.cpp
@@ -0,0 +1,276 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This file has been contributed by Tilo Ulbrich <TiloUlbrich@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "export.h"
+
+#include <qpushbutton.h>
+#include <qtextcodec.h>
+
+class Export;
+
+/***** ExportFormats *****/
+static struct {
+ int id;
+ QString name;
+ QString desc;
+ QString ext;
+ }
+
+ExportFormats [] = {
+ { 1, I18N_NOOP("CSV"),
+ I18N_NOOP("Export to a text file, using semicolons as separators.<p></p>Can be used for spreadsheet programs like <i>KSpread</i>."),
+ "csv" },
+ { 2, I18N_NOOP("HTML"),
+ I18N_NOOP("Export to a HTML Page.<p></p>Can be used for easy exchange over the <i>Internet</i>."),
+ "html" },
+ { 0, 0, 0, 0 } /* !! don't forget !! */
+};
+
+
+/***** ExportWizard *****/
+ExportWizard::ExportWizard(QWidget *parent, const QString &_date)
+ : KWizard(parent, "", true) {
+ date = _date;
+
+ filterID = 0;
+
+ setCaption(i18n("Export Wizard for kPPP Logs"));
+
+ /* format-page */
+ formatPage = new QWidget();
+ QHBoxLayout *formatLayout = new QHBoxLayout(formatPage);
+
+ typeList = new QListBox(formatPage);
+ connect(typeList, SIGNAL(highlighted(int)), SLOT (typeHighlighted(int)));
+ typeList->setMinimumSize(50, 200);
+ QToolTip::add(typeList, i18n("List with possible output formats"));
+ int i=0;
+ while (ExportFormats[i].id) { // add each format to the list
+ typeList->insertItem(i18n(ExportFormats[i].name.utf8()));
+ i++;
+ }
+
+ formatLayout->addWidget(typeList);
+ formatLayout->addSpacing(10);
+
+ typeInfo = new QLabel(formatPage);
+ typeInfo->setAlignment(Qt::AlignTop | Qt::WordBreak);
+ typeInfo->setText(i18n("<qt><b>Please choose the output format on the left side.</b></qt>"));
+ typeInfo->setMinimumSize(350, 200);
+ formatLayout->addWidget(typeInfo);
+
+ addPage(formatPage, i18n("Selection of Filetype"));
+
+
+ /* filename-page */
+ filenamePage = new QWidget();
+ QVBoxLayout *filenameLayout = new QVBoxLayout( filenamePage );
+
+ QLabel *fnLbl = new QLabel(filenamePage);
+ fnLbl->setText(i18n("Filename:"));
+ filenameLayout->addWidget(fnLbl);
+
+ fnLine = new QLineEdit(filenamePage);
+ fnLine->setText(i18n("[No file selected]"));
+ fnLine->setReadOnly(true);
+ filenameLayout->addWidget(fnLine);
+ filenameLayout->addStretch(1);
+
+ fnGet = new QPushButton(filenamePage);
+ fnGet->setText(i18n("&Select File..."));
+ fnGet->setMaximumWidth(200);
+ QToolTip::add(fnGet, i18n("Select the filename of the exported output file"));
+ filenameLayout->addWidget(fnGet);
+ connect(fnGet, SIGNAL(clicked()), SLOT(getFilename()));
+ filenameLayout->addStretch(2);
+
+ addPage(filenamePage, i18n("Selection of Filename"));
+ setNextEnabled( filenamePage, false );
+ setHelpEnabled( filenamePage, false );
+
+ setNextEnabled( formatPage, false );
+ setHelpEnabled( formatPage, false );
+}
+
+Export * ExportWizard::createExportFilter() {
+ switch (filterID) { // IDs: see data-struct ExportFormats
+ case 1 : return new CSVExport(filename, ";");
+ case 2 : return new HTMLExport(filename, date);
+ default : return NULL; // oops..
+ };
+}
+
+void ExportWizard::typeHighlighted(int index) {
+ typeInfo->setText("<qt><b>"+i18n(ExportFormats[index].name.utf8())+" " +
+ i18n("File Format") + "</b><p></p>" + i18n(ExportFormats[index].desc.utf8())
+ +"</qt>");
+ setNextEnabled(formatPage, true );
+}
+
+void ExportWizard::getFilename() {
+ int i = typeList->currentItem();
+ if ( i == -1 )
+ return;
+ // prepare filter: e.g.: HTML (*.html *.HTML)
+ QString filter = "*." + ExportFormats[i].ext + " *." + ExportFormats[i].ext.upper() + "|" +
+ i18n(ExportFormats[i].name.utf8()) + " (*." + ExportFormats[i].ext + " *." +
+ ExportFormats[i].ext.upper() + ")";
+
+ filename = KFileDialog::getSaveFileName(date + "." + ExportFormats[i].ext, filter, 0, i18n("Please Choose File"));
+ if (filename.isEmpty()) // no file selected
+ return;
+ fnLine->setText(filename);
+ setFinishEnabled(filenamePage, true);
+}
+
+void ExportWizard::reject() {
+ hide();
+ filename = QString::null;
+}
+
+void ExportWizard::accept() {
+ filterID = typeList->currentItem() + 1; // translate to ID-count in ExportFormats
+ hide();
+}
+
+
+/***** Export *****/
+Export::Export(const QString &_filename)
+ : filename(_filename),
+ buffer("")
+{
+}
+
+Export::~Export()
+{
+}
+
+bool Export::openFile() {
+ file.setName(filename);
+ return file.open(IO_WriteOnly);
+}
+
+bool Export::closeFile() {
+ bool ok = true;
+ if (file.writeBlock(buffer.local8Bit(), buffer.length())<0)
+ ok = false;
+ file.close();
+ return ok;
+}
+
+
+/***** CSVExport *****/
+CSVExport::CSVExport(const QString &filename, const QString &_separator)
+ : Export(filename),
+ separator(_separator)
+{
+}
+
+void CSVExport::addHeadline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) {
+ // no especially style
+ addDataline(a, b, c, d, e, f, g, h);
+}
+
+void CSVExport::addDataline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) {
+ buffer+=a + separator +
+ b + separator +
+ c + separator +
+ d + separator +
+ e + separator +
+ f + separator +
+ g + separator +
+ h + separator + "\n";
+}
+
+void CSVExport::addEmptyLine() {
+ // not needed
+}
+
+void CSVExport::setFinishCode() {
+ // not needed
+}
+
+
+/***** HTMLExport *****/
+HTMLExport::HTMLExport(const QString &filename, const QString &date)
+ : Export(filename) {
+ QString title = i18n("Connection log for %1").arg(date);
+ buffer = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
+ buffer.append("<html>\n<head>\n <title>"+title+"</title>\n");
+ buffer.append(QString::fromLatin1(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=")
+ + QTextCodec::codecForLocale()->mimeName() +
+ QString::fromLatin1("\">"));
+ buffer.append("\n</head>\n<body>\n<h1>"+title+"</h1>\n\n");
+ buffer.append("<table width=\"100%\" border=\"1\">\n");
+
+ trStartCode = "<tr>";
+ trEndCode = "</tr>\n";
+ tdStartCode = "<td>";
+ tdEndCode = "</td>";
+}
+
+void HTMLExport::addHeadline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) {
+ // simply bold font
+ QString bak1 = tdStartCode; tdStartCode.append("<b>");
+ QString bak2 = tdEndCode; tdEndCode.prepend("</b>");
+
+ addDataline(a, b, c, d, e, f, g, h);
+
+ // reset font
+ tdStartCode = bak1;
+ tdEndCode = bak2;
+}
+
+void HTMLExport::addDataline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) {
+ buffer+= trStartCode +
+ tdStartCode + a + tdEndCode +
+ tdStartCode + b + tdEndCode +
+ tdStartCode + c + tdEndCode +
+ tdStartCode + d + tdEndCode +
+ tdStartCode + e + tdEndCode +
+ tdStartCode + f + tdEndCode +
+ tdStartCode + g + tdEndCode +
+ tdStartCode + h + tdEndCode +
+ trEndCode;
+}
+
+void HTMLExport::addEmptyLine() {
+ addDataline("&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;", "&nbsp;");
+}
+
+void HTMLExport::setFinishCode() {
+ buffer+= "</table>\n</body>\n</html>\n";
+}
+
+#include "export.moc"
diff --git a/kppp/logview/export.h b/kppp/logview/export.h
new file mode 100644
index 00000000..306b6fc6
--- /dev/null
+++ b/kppp/logview/export.h
@@ -0,0 +1,201 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This file has been contributed by Tilo Ulbrich <TiloUlbrich@web.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KPPPEXPORT_H
+#define KPPPEXPORT_H
+
+#include <qwidget.h>
+#include <qfile.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qlineedit.h>
+#include <qtooltip.h>
+
+#include <kwizard.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+
+class Export;
+
+/***** ExportWizard *****/
+class ExportWizard : public KWizard {
+ Q_OBJECT
+public:
+ ExportWizard(QWidget *parent, const QString &_date);
+ Export *createExportFilter();
+
+ int filterID;
+ QString filename;
+
+public slots:
+ void typeHighlighted(int);
+ void getFilename();
+
+ void accept();
+ void reject();
+
+private:
+ QWidget *formatPage;
+ QListBox *typeList;
+ QLabel *typeInfo;
+
+ QWidget *filenamePage;
+ QLineEdit *fnLine;
+ QPushButton *fnGet;
+
+ QString date;
+};
+
+
+/***** Export (abstract)*****/
+class Export {
+public:
+ Export();
+ Export(const QString &filename);
+ virtual ~Export();
+ bool openFile();
+ bool closeFile();
+ virtual void addHeadline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) = 0;
+ virtual void addDataline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h) = 0;
+ virtual void addEmptyLine() = 0;
+ virtual void setFinishCode() = 0;
+
+protected:
+ QFile file;
+ QString filename;
+ QString buffer;
+};
+
+
+/***** CSVExport *****/
+class CSVExport : public Export {
+public:
+ CSVExport(const QString &filename, const QString &separator);
+ virtual void addHeadline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h);
+ virtual void addDataline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h);
+ virtual void addEmptyLine();
+ virtual void setFinishCode();
+
+private:
+ QString separator;
+};
+
+
+/***** HTMLExport *****/
+class HTMLExport : public Export {
+public:
+ HTMLExport(const QString &filename, const QString &date);
+ virtual void addHeadline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h);
+ virtual void addDataline(const QString &a, const QString &b,
+ const QString &c, const QString &d,
+ const QString &e, const QString &f,
+ const QString &g, const QString &h);
+ virtual void addEmptyLine();
+ virtual void setFinishCode();
+
+private:
+ QString trStartCode;
+ QString trEndCode;
+ QString tdStartCode;
+ QString tdEndCode;
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kppp/logview/kppplogview.desktop b/kppp/logview/kppplogview.desktop
new file mode 100644
index 00000000..45b0ffa6
--- /dev/null
+++ b/kppp/logview/kppplogview.desktop
@@ -0,0 +1,77 @@
+[Desktop Entry]
+Name=KPPPLogview
+Name[bn]=কে-পি-পি-পি লগভিউ
+Name[bs]=Preglednik Kppp dnevnika
+Name[de]=KPPP-Protokoll-Betrachter
+Name[el]=ΠÏοβολή καταγÏαφής KPPP
+Name[eo]=PPP-protokolorigardilo
+Name[fa]=نمای ثبت KPPP
+Name[he]=KPPP תצוגת רישו×
+Name[is]=KPPP annálaskoðari
+Name[it]=Visualizzatore log KPPP
+Name[ja]=KPPP - ログビューア
+Name[lt]=KPPP – žurnalo žiūriklis
+Name[nb]=KPPP loggviser
+Name[ne]=के पी पी पी लग दृशà¥à¤¯
+Name[nn]=KPPP-loggvisar
+Name[pa]=KPPP ਲਾਗ ਦਰਸ਼ਕ
+Name[pl]=Dziennik połączeń KPPP
+Name[sk]=KPPP prehliadaÄ záznamov
+Name[sl]=Ogled dnevnika KPPP
+Name[sv]=Kppp - Loggvisare
+Name[tr]=KPPP Kayıt Görüntüleyici
+Name[zh_TW]=KPPP - 紀錄檔檢視器
+GenericName=Internet Dial-Up Tool Log Viewer
+GenericName[be]=ÐглÑдальнік дзённікаў KPPP
+GenericName[bg]=Преглед на журнала на KPPP
+GenericName[bn]=ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ ডায়াল-আপ সরঞà§à¦œà¦¾à¦® কারà§à¦¯à¦¬à¦¿à¦¬à¦°à¦£à§€ পà§à¦°à¦¦à¦°à§à¦¶à¦•
+GenericName[bs]=Program za pregled dnevnika spajanja na Internet
+GenericName[ca]=Visor de la bitàcola de l'eina de connexió telefònica a Internet
+GenericName[cs]=ProhlížeÄ záznamů pÅ™ipojení pÅ™es vytáÄenou linku
+GenericName[da]=Internet-opkaldsværktøj-logviser
+GenericName[de]=Protokoll-Betrachter für die Einwahl ins Internet
+GenericName[el]=ΠÏοβολή καταγÏαφής εÏγαλείου σÏνδεσης μέσω τηλεφώνου στο διαδίκτυο
+GenericName[es]=Visor del registro de conexión telefónica a Internet
+GenericName[et]=Interneti sissehelistamise rakenduse logide näitaja
+GenericName[eu]=Internet markatzaile tresnaren erregistroaren ikusgailua
+GenericName[fa]=ابزار مشاهده‌گر ثبت شماره‌گیری اینترنت
+GenericName[fi]=Internet-yhteyden soitto-ohjelman lokin näytin
+GenericName[fr]=Afficheur de l'historique des connexion internet par modem
+GenericName[gl]=Visor de Rexistros da Ferramenta de Conexión a Internet por Teléfono
+GenericName[he]=תצוגת ×¨×™×©×•× ×©×œ כלי חיוג ל×ינטרנט
+GenericName[hu]=Tárcsázó-napló
+GenericName[is]=Upphringisambands annálaskoðari
+GenericName[it]=Visualizzatore di log della connessione telefonica ad Internet
+GenericName[ja]=インターãƒãƒƒãƒˆãƒ€ã‚¤ã‚¢ãƒ«ã‚¢ãƒƒãƒ—ツール ログビューア
+GenericName[ka]=ინტერნეტი Dial-Up ხელსáƒáƒ¬áƒ§áƒáƒ¡ ჟურნáƒáƒšáƒ˜áƒ¡ მხილველი
+GenericName[kk]=Телефондық желі арқылы қоÑылудың журналын қарау құралы
+GenericName[km]=កម្មវិធី​មើល​កំណážáŸ‹áž áŸážáž»ážšáž”ស់ឧបករណáŸâ€‹ážŠáŸ‚ល​ážáž—្ជាប់​ទៅ​អ៊ីនធឺណិážâ€‹ážáž¶áž˜â€‹ážšáž™áŸˆâ€‹áž‘ូរសáŸáž–្ទ​លើ​ážáž»
+GenericName[lt]=Interneto skambinimo priemonės žurnalo žiūriklis
+GenericName[nb]=Loggviser for verktøyet for oppringt Internet
+GenericName[nds]=Protokollkieker för Internetinwahl
+GenericName[ne]=इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ डायल-अप उपकरण लग दरà¥à¤¶à¤•
+GenericName[nl]=Logviewer voor het inbelprogramma kppp
+GenericName[nn]=Loggvisar for verktøyet for Internett-oppringing
+GenericName[pl]=Narzędzie do przeglądania dziennika programu do łączenia z Internetem
+GenericName[pt]=Visualizador de Registos da Ferramenta de Ligação à Internet
+GenericName[pt_BR]=Visualizador de Logs do KPPP
+GenericName[ro]=Jurnal conectare Internet
+GenericName[ru]=ПроÑмотр журнала ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚Ð¾Ð¼ поÑредÑтвом телефонной линии
+GenericName[sk]=PrehliadaÄ záznamov nástroja pre pripojenie na internet cez modem
+GenericName[sl]=Orodje za ogled dnevnika KPPP
+GenericName[sr]=Приказивач дневника модемÑке везе Ñа Интернетом
+GenericName[sr@Latn]=PrikazivaÄ dnevnika modemske veze sa Internetom
+GenericName[sv]=Loggvisning för Internetuppringningsverktyg
+GenericName[tr]=İnternet Çevirmeli Ağ Aracı Kayıt Görüntüleyici
+GenericName[uk]=ПереглÑдач журналу заÑобу дозвону в Інтернет
+GenericName[zh_CN]=Internet 拨å·å·¥å…·æ—¥å¿—查看器
+GenericName[zh_HK]=互è¯ç¶²æ’¥è™Ÿå·¥å…·è¨˜éŒ„檢視器
+GenericName[zh_TW]=Internet 撥號工具紀錄檢視器
+MimeType=
+Exec=kppplogview
+Icon=kppp
+Path=
+Type=Application
+Terminal=false
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Network;X-KDE-More;Dialup;
diff --git a/kppp/logview/log.cpp b/kppp/logview/log.cpp
new file mode 100644
index 00000000..66ff7c9c
--- /dev/null
+++ b/kppp/logview/log.cpp
@@ -0,0 +1,129 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "log.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <qprogressdialog.h>
+#include <qdir.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+QPtrList<LogInfo> logList;
+QProgressDialog *dlg;
+
+int loadLogs() {
+ int logsize = 0;
+
+ QString logdirname = locateLocal("data", "kppp/Log/");
+ QDir logdir(logdirname, "*.log");
+
+ kdDebug(5002) << "logdirname: " << logdirname << endl;
+
+ // get log file size
+ const QFileInfoList *list = logdir.entryInfoList();
+ QFileInfoListIterator it( *list );
+ QFileInfo *fi;
+
+ while ((fi = it.current()) != 0) {
+ logsize += fi->size();
+ ++it;
+ }
+
+ dlg = new QProgressDialog(i18n("Loading log files"),
+ QString::null,
+ logsize);
+ dlg->setProgress(0);
+
+ // load logs
+ list = logdir.entryInfoList();
+ QFileInfoListIterator it1( *list );
+
+ int retval = 0;
+ while ((fi = it1.current()) != 0) {
+ retval += loadLog(fi->absFilePath());
+ ++it1;
+ }
+
+ delete dlg;
+ return retval;
+}
+
+int loadLog(QString fname) {
+ FILE *f;
+ int warning=0;
+
+ f = fopen(QFile::encodeName(fname), "r");
+ if(f == NULL)
+ return 1;
+
+ char buffer[2048+1];
+ int lineno = 0;
+ while(fgets(buffer, sizeof(buffer), f) != NULL) {
+ ++lineno;
+ buffer[sizeof(buffer) - 1] = 0;
+
+ int slen = strlen(buffer);
+
+ // skip blank lines
+ if(slen < 10)
+ continue;
+
+ dlg->setProgress(dlg->progress() + slen);
+ kapp->processEvents();
+
+ LogInfo *li = new LogInfo(buffer);
+ if(li->error()) {
+
+ // check if the connection has been terminated abnormally
+ if(li->error() != 3) {
+ warning++;
+ kdError() << "ERROR IN FILE " << fname << " LINE " << lineno << "\"" << buffer << "\" (" << li->error() << ")" << endl;
+ delete li;
+ } else
+ logList.append(li);
+ } else
+ logList.append(li);
+ }
+
+ fclose(f);
+
+ if(warning)
+ return 2;
+ else
+ return 0;
+}
+
+int QLogList::compareItems(Item a, Item b) {
+ LogInfo *la = (LogInfo *)a;
+ LogInfo *lb = (LogInfo *)b;
+
+ if(la->from() < lb->from())
+ return -1;
+ else if(la->from() > lb->from())
+ return 1;
+ else
+ return 0;
+}
diff --git a/kppp/logview/log.h b/kppp/logview/log.h
new file mode 100644
index 00000000..74d573e5
--- /dev/null
+++ b/kppp/logview/log.h
@@ -0,0 +1,40 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOG__H__
+#define __LOG__H__
+
+#include "loginfo.h"
+#include <qptrlist.h>
+
+typedef QPtrList<LogInfo> QLogInfoBase;
+typedef QPtrListIterator<LogInfo> QLogInfoIterator;
+
+class QLogList : public QLogInfoBase {
+public:
+ virtual int compareItems(Item, Item);
+};
+
+extern QPtrList<LogInfo> logList;
+
+int loadLogs();
+int loadLog(QString);
+
+#endif
diff --git a/kppp/logview/loginfo.cpp b/kppp/logview/loginfo.cpp
new file mode 100644
index 00000000..1e5a089a
--- /dev/null
+++ b/kppp/logview/loginfo.cpp
@@ -0,0 +1,197 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "loginfo.h"
+#include <stdlib.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <kdebug.h>
+
+char *mystrsep (char **stringp, const char *delim);
+
+LogInfo::LogInfo(QCString data) {
+ parse(data);
+}
+
+QDateTime LogInfo::from() const {
+ QDateTime tm;
+ tm.setTime_t(_from);
+ return tm;
+}
+
+QDateTime LogInfo::until() const {
+ QDateTime tm;
+ tm.setTime_t(_until);
+ return tm;
+}
+
+QString LogInfo::connectionName() const {
+ return _conname;
+}
+
+QString LogInfo::currency() const {
+ return _currency;
+}
+
+double LogInfo::sessionCosts() const {
+ return _session_cost;
+}
+
+double LogInfo::totalCosts() const {
+ return _total_cost;
+}
+
+double LogInfo::bytesIn() const {
+ return _bytes_in;
+}
+
+double LogInfo::bytesOut() const {
+ return _bytes_out;
+}
+
+double LogInfo::bytes() const {
+ if(bytesIn() == -1 || bytesOut() == -1)
+ return -1;
+ else
+ return bytesIn() + bytesOut();
+}
+
+int LogInfo::error() const {
+ return errorfield;
+}
+
+void LogInfo::parse(QCString s) {
+ errorfield = 0;
+ char *c = (char *)malloc(s.length() + 1), *csep;
+ strcpy(c, s);
+
+ // init data
+ _from = _until = 0;
+ _conname = "";
+ _currency = "";
+ _bytes_in = _bytes_out = -1;
+ _session_cost = _total_cost = -1;
+
+ // start of connection time
+ csep = c;
+ char *p = mystrsep(&csep, ":");
+ int i = 0;
+ while(i < 8 && p != 0) {
+ QString token = QString::fromLocal8Bit(p);
+
+ switch(i) {
+ case 0:
+ _from = token.toULong();
+ break;
+
+ case 1:
+ _conname = token;
+ break;
+
+ case 2:
+ _currency = token;
+ break;
+
+ case 3:
+ _until = token.toULong();
+ break;
+
+ case 4:
+ _session_cost = token.toFloat();
+ break;
+
+ case 5:
+ _total_cost = token.toFloat();
+ break;
+
+ case 6:
+ _bytes_in = token.toLong();
+ break;
+
+ case 7:
+ _bytes_out = token.toLong();
+ break;
+ }
+
+ i++;
+ p = mystrsep(&csep, ":");
+ }
+
+ free(c);
+
+ if(i == 8)
+ errorfield = 0;
+ else
+ errorfield = i+1;
+}
+
+
+int LogInfo::duration() const {
+ if( _until - _from < 0)
+ return 0;
+ else
+ return _until - _from;
+}
+
+#ifdef MYDEBUG
+void LogInfo::dump() {
+ kdDebug(5002) << "LOGINFO " << this << endl;
+ kdDebug(5002) << "connection name : " << connectionName() << endl;
+ kdDebug(5002) << "currency symbol : " << currency() << endl;
+ kdDebug(5002) << "begin : " << ctime(&_from) << endl;
+ kdDebug(5002) << "end : " << ctime(&_until) << endl;
+ kdDebug(5002) << "duration : " << (int)_until - (int)_from << " seconds" << endl;
+ kdDebug(5002) << "session cost : " << sessionCosts() << endl;
+ kdDebug(5002) << "total cost : " << totalCosts() << endl;
+ kdDebug(5002) << "bytes in : " << bytesIn() << endl;
+ kdDebug(5002) << "bytes out : " << bytesOut() << endl;
+ kdDebug(5002) << "bytes total : " << bytes() << endl;
+ kdDebug(5002) << endl;
+}
+#endif
+
+char *mystrsep (char **stringp, const char *delim) {
+ char *begin, *end;
+
+ begin = *stringp;
+ if (begin == 0L)
+ return 0L;
+
+ /* Find the end of the token. */
+ end = strpbrk (begin, delim);
+ if (end) {
+ /* Terminate the token and set *STRINGP past NUL character. */
+ *end++ = '\0';
+ *stringp = end;
+ } else
+ /* No more delimiters; this is the last token. */
+ *stringp = 0L;
+
+ return begin;
+}
+
+
diff --git a/kppp/logview/loginfo.h b/kppp/logview/loginfo.h
new file mode 100644
index 00000000..6b2e992e
--- /dev/null
+++ b/kppp/logview/loginfo.h
@@ -0,0 +1,63 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LOGINFO__H__
+#define __LOGINFO__H__
+#define MYDEBUG
+
+#include <qdatetime.h>
+#include <qstring.h>
+#include <time.h>
+
+class LogInfo {
+public:
+ LogInfo(QCString data);
+
+ int error() const;
+
+ QDateTime from() const;
+ time_t from_t() const { return _from; }
+ QDateTime until() const;
+ int duration() const;
+ QString connectionName() const;
+ QString currency() const;
+ double sessionCosts() const;
+ double totalCosts() const;
+ double bytesIn() const;
+ double bytesOut() const;
+ double bytes() const;
+
+#ifdef MYDEBUG
+ void dump();
+#endif
+
+private:
+ void parse(QCString );
+
+ int errorfield;
+
+ time_t _from, _until;
+ QString _conname, _currency;
+ double _session_cost, _total_cost;
+ double _bytes_in, _bytes_out;
+};
+
+
+#endif
diff --git a/kppp/logview/main.cpp b/kppp/logview/main.cpp
new file mode 100644
index 00000000..4e54236c
--- /dev/null
+++ b/kppp/logview/main.cpp
@@ -0,0 +1,127 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <kapplication.h>
+#include "log.h"
+#include "monthly.h"
+#include "main.h"
+#include <klocale.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+#include <kiconloader.h>
+
+#define F_EXIT 101
+
+
+static const char description[] =
+ I18N_NOOP("KPPP log viewer");
+
+static const char version[] = "v0.0.2";
+
+static KCmdLineOptions option[] =
+{
+ { "kppp", I18N_NOOP("Run in KPPP mode"), 0 },
+ KCmdLineLastOption
+};
+
+
+TopWidget::TopWidget() : KMainWindow(0, "") {
+ // Check command line args for "-kppp"
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ bool kpppmode = args->isSet("kppp");
+ args->clear();
+
+ setCaption(i18n("KPPP Log Viewer"));
+
+ w = new QWidget(this);
+
+ QBoxLayout *l = new QVBoxLayout(w, 5);
+
+ td = new QTabWidget(w, "");
+ mw = new MonthlyWidget(td);
+ td->addTab(mw, i18n("Monthly Log"));
+ l->addWidget(td);
+
+ // remove buttons
+ if(!kpppmode) {
+ // create menu
+ mb = new KMenuBar(this);
+ QPopupMenu *fm = new QPopupMenu;
+ fm->insertItem(SmallIcon("exit"),KStdGuiItem::quit().text(), F_EXIT);
+ mb->insertItem(i18n("&File"), fm);
+
+ mb->setAccel(CTRL + Key_Q, F_EXIT);
+ connect(mb, SIGNAL(activated(int)),
+ this, SLOT(menuCallback(int)));
+ } else {
+ mb = 0;
+ QPushButton *but = new KPushButton(KStdGuiItem::close(),w);
+ QHBoxLayout *lh = new QHBoxLayout(l);
+ lh->addStretch(10);
+ lh->addWidget(but);
+
+ connect(but, SIGNAL(clicked()),
+ kapp, SLOT(quit()));
+ }
+
+ setMinimumSize(mw->sizeHint().width() + 15,
+ mw->sizeHint().height() + 120);
+ setCentralWidget(w);
+}
+
+TopWidget::~TopWidget() {
+}
+
+void TopWidget::menuCallback(int id) {
+ switch(id) {
+ case F_EXIT:
+ exit(0);
+ break;
+ }
+}
+
+int main(int argc, char **argv) {
+ KAboutData aboutData("kppplogview", I18N_NOOP("KPPP Log Viewer"),
+ version, description, KAboutData::License_GPL,
+ I18N_NOOP("(c) 1999-2002, The KPPP Developers"));
+ aboutData.addAuthor("Bernd Wuebben",0, "wuebben@kde.org");
+ aboutData.addAuthor("Mario Weilguni",0, "");
+ aboutData.addAuthor("Harri Porten",0, "porten@kde.org");
+ KCmdLineArgs::init(argc, argv, &aboutData);
+
+ KCmdLineArgs::addCmdLineOptions( option );
+
+ KApplication a;
+
+ loadLogs();
+
+ TopWidget *w = new TopWidget;
+ w->show();
+ a.setMainWidget(w);
+
+ return a.exec();
+}
+
+#include "main.moc"
diff --git a/kppp/logview/main.h b/kppp/logview/main.h
new file mode 100644
index 00000000..eabe66df
--- /dev/null
+++ b/kppp/logview/main.h
@@ -0,0 +1,45 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN__H__
+#define __MAIN__H__
+
+#include <kmainwindow.h>
+#include <qtabwidget.h>
+#include <kmenubar.h>
+#include "monthly.h"
+
+class TopWidget : public KMainWindow {
+ Q_OBJECT
+public:
+ TopWidget();
+ ~TopWidget();
+
+private slots:
+ void menuCallback(int);
+
+private:
+ MonthlyWidget *mw;
+ KMenuBar *mb;
+ QWidget *w;
+ QTabWidget *td;
+};
+
+#endif
diff --git a/kppp/logview/monthly.cpp b/kppp/logview/monthly.cpp
new file mode 100644
index 00000000..668381e4
--- /dev/null
+++ b/kppp/logview/monthly.cpp
@@ -0,0 +1,729 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qpainter.h>
+#include <qcombobox.h>
+#include <qfile.h>
+#include <qheader.h>
+
+#include <kcalendarsystem.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <klistview.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include "monthly.h"
+#include "export.h"
+#include <qstringlist.h>
+
+static void formatBytes(double bytes, QString &result) {
+ if(bytes < 1024)
+ result.setNum(bytes);
+ else if(bytes < 1024*1024)
+ result = i18n("%1 KB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0, 1));
+ else
+ result = i18n("%1 MB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0 / 1024.0, 1));
+}
+
+static void formatBytesMonth(double bytes, QString &result) {
+
+ int day, days;
+ day = QDate::currentDate().day();
+ days = QDate::currentDate().daysInMonth();
+
+ bytes = (bytes / day) * days;
+
+ if(bytes < 1024)
+ result.setNum(bytes);
+ else if(bytes < 1024*1024)
+ result = i18n("%1 KB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0, 1));
+ else
+ result = i18n("%1 MB").arg(KGlobal::locale()->formatNumber((float)bytes / 1024.0 / 1024.0, 1));
+}
+
+static void formatDuration(int seconds, QString &result) {
+ QString sec;
+ sec.sprintf("%02d", seconds%60);
+ if(seconds < 60)
+ result = i18n("%1s").arg(sec);
+ else if(seconds < 3600)
+ result = i18n("%1m %2s").arg(seconds/60).arg(sec);
+ else
+ result = i18n("%1h %2m %3s")
+ .arg(seconds/3600)
+ .arg((seconds % 3600)/60)
+ .arg(sec);
+}
+
+static void formatDurationMonth(int seconds, QString &result) {
+
+ int day, days;
+ day = QDate::currentDate().day();
+ days = QDate::currentDate().daysInMonth();
+
+ seconds = (seconds / day) * days;
+
+ QString sec;
+ sec.sprintf("%02d", seconds%60);
+ if(seconds < 60)
+ result = i18n("%1s").arg(sec);
+ else if(seconds < 3600)
+ result = i18n("%1m %2s").arg(seconds/60).arg(sec);
+ else
+ result = i18n("%1h %2m %3s")
+ .arg(seconds/3600)
+ .arg((seconds % 3600)/60)
+ .arg(sec);
+}
+
+static void costsMonth(double costs, double &result) {
+
+ int day, days;
+ day = QDate::currentDate().day();
+ days = QDate::currentDate().daysInMonth();
+
+ result = (costs / day) * days;
+
+}
+
+class LogListItem : public QListViewItem {
+public:
+ LogListItem(LogInfo *l,
+ QListView * parent,
+ QString s1, QString s2,
+ QString s3, QString s4,
+ QString s5, QString s6,
+ QString s7, QString s8)
+ : QListViewItem(parent, s1, s2, s3, s4, s5, s6, s7, s8),
+ li(l)
+ {
+ }
+ virtual void paintCell( QPainter *p, const QColorGroup & cg,
+ int column, int width, int alignment );
+
+ virtual QString key(int, bool) const;
+
+ LogInfo *li;
+};
+
+void LogListItem::paintCell( QPainter *p, const QColorGroup & cg,
+ int column, int width, int alignment )
+{
+ QListViewItem::paintCell(p, cg, column, width, alignment);
+
+ // double line above sum
+ //if(!li) {
+ // p->drawLine(0, 0, width, 0);
+ //p->drawLine(0, 2, width, 2);
+ //}
+}
+
+QString LogListItem::key(int c, bool ascending) const
+{
+ if (!li) // we want the sum to be always at the bottom
+ return ascending ? "z" : " ";
+
+ QString k;
+ switch (c) {
+ case 0:
+ k = li->connectionName();
+ break;
+ case 1:
+ case 2:
+ case 3:
+ k.sprintf("%012u", (uint)li->from_t());
+ break;
+ case 4:
+ k.sprintf("%012d", li->duration());
+ break;
+ case 5:
+ k.sprintf("%012.2f", li->sessionCosts());
+ break;
+ case 6:
+ k.sprintf("%012f", li->bytesIn());
+ break;
+ case 7:
+ k.sprintf("%012f", li->bytesOut());
+ break;
+ }
+ return k;
+}
+
+MonthlyWidget::MonthlyWidget(QWidget *parent) :
+ QWidget(parent)
+{
+ tl = 0;
+
+ lv = new KListView(this);
+ lv->addColumn(i18n("Connection"));
+ lv->addColumn(i18n("Day"));
+ lv->addColumn(i18n("From"));
+ lv->addColumn(i18n("Until"));
+ lv->addColumn(i18n("Duration"));
+ lv->addColumn(i18n("Costs"));
+ lv->addColumn(i18n("Bytes In"));
+ lv->addColumn(i18n("Bytes Out"));
+ lv->setColumnAlignment(1, AlignRight);
+ lv->setColumnAlignment(2, AlignRight);
+ lv->setColumnAlignment(3, AlignRight);
+ lv->setColumnAlignment(4, AlignRight);
+ lv->setColumnAlignment(5, AlignRight);
+ lv->setColumnAlignment(6, AlignRight);
+ lv->setColumnAlignment(7, AlignRight);
+ lv->setAllColumnsShowFocus(true);
+ lv->setShowSortIndicator(true);
+ lv->setItemMargin(1);
+ lv->setSorting(1);
+ lv->setMinimumWidth(180);
+ lv->setMinimumHeight(280);
+ lv->setSelectionMode(QListView::Extended);
+ selectionItem = 0L;
+ connect(lv, SIGNAL(selectionChanged()), SLOT(slotSelectionChanged()));
+
+ lv2 = new KListView(this);
+ lv2->addColumn(i18n("Connection"));
+ lv2->addColumn(i18n("Duration"));
+ lv2->addColumn(i18n("Costs"));
+ lv2->addColumn(i18n("Bytes In"));
+ lv2->addColumn(i18n("Bytes Out"));
+ lv2->setColumnAlignment(1, AlignRight);
+ lv2->setColumnAlignment(2, AlignRight);
+ lv2->setColumnAlignment(3, AlignRight);
+ lv2->setColumnAlignment(4, AlignRight);
+ lv2->setAllColumnsShowFocus(true);
+ lv2->setSorting(-1);
+ lv2->setItemMargin(2);
+ lv2->setMaximumHeight(100);
+ lv2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum);
+ lv2->setSelectionMode(QListView::NoSelection);
+
+ title = new QLabel("X", this);
+ QFont f = title->font();
+ f.setPointSize(f.pointSize() + 2);
+ f.setBold(TRUE);
+ title->setFont(f);
+ title->setFixedHeight(title->sizeHint().height()*2);
+
+ cboConnections = new QComboBox(false, this); // add a combo box to select connections
+ cboConnections->setMaximumWidth(200); // a resonable size
+ cboConnections->insertItem(i18n("All Connections")); // default to all connections
+ connect(cboConnections, SIGNAL(activated(int)),
+ this, SLOT(slotConnections(int)));
+
+ bbox = new KButtonBox(this, Qt::Vertical);
+ prev = bbox->addButton(i18n("&Prev Month"));
+ next = bbox->addButton(i18n("&Next Month"));
+ bbox->addStretch(1);
+ today = bbox->addButton(i18n("C&urrent Month"));
+ bbox->addStretch(1);
+ exportBttn = bbox->addButton(i18n("&Export..."));
+
+ connect(prev, SIGNAL(released()),
+ this, SLOT(prevMonth()));
+ connect(next, SIGNAL(released()),
+ this, SLOT(nextMonth()));
+ connect(today, SIGNAL(released()),
+ this, SLOT(currentMonth()));
+ connect(exportBttn, SIGNAL(clicked()),
+ this, SLOT(exportWizard()));
+
+ bbox->addStretch(8);
+ bbox->layout();
+
+ currentMonth();
+ layoutWidget();
+}
+
+void MonthlyWidget::layoutWidget() {
+ if(tl)
+ delete tl;
+
+ tl = new QGridLayout(this, 1, 1, 11, 16, "MainLayout");
+ tl->addWidget(title, 0, 0);
+ tl->addWidget(cboConnections, 0, 1);
+ QLabel *l = new QLabel(this);
+ l->setText(i18n("Statistics:"));
+ QFont f2 = l->font();
+ f2.setPointSize(f2.pointSize() + 1);
+ f2.setBold(TRUE);
+ l->setFont(f2);
+ l->setFixedHeight(l->sizeHint().height());
+ l->setAlignment( AlignLeft );
+ tl->addWidget(l, 5, 0);
+ tl->addWidget(bbox, 1, 2);
+ tl->addMultiCellWidget(lv, 1, 4, 0, 1);
+ tl->addMultiCellWidget(lv2, 6, 6, 0, 1);
+
+ tl->activate();
+}
+
+int bestlen(QWidget *w, const char *s) {
+ return w->fontMetrics().boundingRect(s).width() + 8;
+}
+
+void MonthlyWidget::plotMonth() {
+ // name of the current connection
+ QString con;
+
+ // for collecting monthly statistics
+ int count = 0;
+ double costs = 0;
+ double bin = 0, bout = 0;
+ int duration = 0;
+ lv->clear();
+ selectionItem = 0L;
+ lv2->clear();
+
+ const KCalendarSystem * calendar = KGlobal::locale()->calendar();
+ QDate startDate = periodeFirst();
+
+ for(int i = 0; i < (int)logList.count(); i++) {
+ LogInfo *li = logList.at(i);
+
+ QDate logDate = li->from().date();
+ if ( periodeFirst() <= logDate && periodeLast() >= logDate ) {
+ // get connection name for this line
+ con = li->connectionName();
+
+ // this connection name not in the list and combo box
+ if(lstConnections.findIndex(con) == -1) {
+ lstConnections.append(con);
+ cboConnections->insertItem(con);
+ }
+ // if all connections or the selected one
+ if(cboConnections->currentText() != con &&
+ cboConnections->currentItem() != 0)
+ continue;
+ count++;
+ costs += li->sessionCosts();
+ if(bin >= 0) {
+ if(li->bytesIn() < 0)
+ bin = -1;
+ else
+ bin += li->bytesIn();
+ }
+
+ if(bout >= 0) {
+ if(li->bytesOut() < 0)
+ bout = -1;
+ else
+ bout += li->bytesOut();
+ }
+
+ duration += li->from().secsTo(li->until());
+
+ QString _bin, _bout, b;
+ if(li->bytesIn() >= 0)
+ formatBytes(li->bytesIn(), _bin);
+ else
+ _bin = i18n("n/a");
+
+ if(li->bytesOut() >= 0)
+ formatBytes(li->bytesOut(), _bout);
+ else
+ _bout = i18n("n/a");
+
+ if(li->bytes() > 0)
+ formatBytes(li->bytes(), b);
+ else
+ b = i18n("n/a");
+
+ QString day;
+ day.sprintf("%2d", li->from().date().day());
+
+ QString s_duration;
+ formatDuration(li->from().secsTo(li->until()),
+ s_duration);
+
+ QString s_lifrom, s_liuntil, s_costs;
+ s_lifrom = KGlobal::locale()->formatTime(li->from().time(), false);
+ s_liuntil = KGlobal::locale()->formatTime(li->until().time(), false);
+ s_costs = KGlobal::locale()->formatMoney(li->sessionCosts());
+
+ (void) new LogListItem(li, lv, con, day, s_lifrom, s_liuntil, s_duration, s_costs, _bin, _bout);
+ }
+ }
+
+ if(count) {
+ QString _bin, _bout, _b;
+
+ if(bin < 0)
+ _bin = i18n("n/a");
+ else
+ formatBytes(bin, _bin);
+
+ if(bout < 0)
+ _bout = i18n("n/a");
+ else
+ formatBytes(bout, _bout);
+
+ if(bin < 0 || bout < 0)
+ _b = i18n("n/a");
+ else
+ formatBytes(bout + bin, _b);
+
+ QString s_duration;
+ formatDuration(duration,
+ s_duration);
+
+ QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
+
+ selectionItem = new LogListItem(0, lv2,
+ i18n("Selection (%n connection)", "Selection (%n connections)", 0),
+ QString::null, QString::null, QString::null,
+ QString::null, QString::null, QString::null, QString::null);
+ (void) new LogListItem(0, lv2,
+ i18n("%n connection", "%n connections", count),
+ s_duration, s_costs, _bin, _bout, QString::null, QString::null, QString::null);
+
+ const KCalendarSystem * calendar = KGlobal::locale()->calendar();
+
+ if(calendar->month(periodeFirst()) == calendar->month(QDate::currentDate())) {
+
+ QString m_bin, m_bout;
+
+ if(bin < 0)
+ _bin = i18n("n/a");
+ else
+ formatBytesMonth(bin, m_bin);
+
+ if(bout < 0)
+ _bout = i18n("n/a");
+ else
+ formatBytesMonth(bout, m_bout);
+
+ QString m_duration;
+ formatDurationMonth(duration, m_duration);
+
+ costsMonth(costs, costs);
+ QString m_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
+
+ (void) new QListViewItem(lv2, selectionItem,
+ i18n("Monthly estimates"), m_duration, m_costs, m_bin, m_bout,
+ QString::null, QString::null, QString::null);
+ }
+ }
+
+ QString t;
+ if(lv->childCount() > 0) {
+ exportBttn->setEnabled(true); // export possibility
+ t = i18n("Connection log for %1 %2")
+ .arg(calendar->monthName(startDate))
+ .arg(calendar->year(startDate));
+ } else {
+ exportBttn->setEnabled(false); // nothing to export
+ t = i18n("No connection log for %1 %2 available")
+ .arg(calendar->monthName(startDate))
+ .arg(calendar->year(startDate));
+ }
+
+ title->setText(t);
+}
+
+void MonthlyWidget::slotConnections(int) {
+ plotMonth();
+}
+
+void MonthlyWidget::nextMonth() {
+ m_periodeFirst = KGlobal::locale()->calendar()->addMonths(m_periodeFirst, 1);
+
+ plotMonth();
+}
+
+void MonthlyWidget::prevMonth() {
+ m_periodeFirst = KGlobal::locale()->calendar()->addMonths(m_periodeFirst, -1);
+
+ plotMonth();
+}
+
+void MonthlyWidget::currentMonth() {
+ const KCalendarSystem * calendar = KGlobal::locale()->calendar();
+ QDate dt = QDate::currentDate();
+ calendar->setYMD(m_periodeFirst, calendar->year(dt), calendar->month(dt), 1);
+
+ plotMonth();
+}
+
+void MonthlyWidget::exportWizard() {
+ const KCalendarSystem * calendar = KGlobal::locale()->calendar();
+ QString date = QString::fromLatin1("%1-%2") // e.g.: June-2001
+ .arg(calendar->monthName(periodeFirst()))
+ .arg(calendar->year(periodeFirst()));
+
+ ExportWizard *wizard = new ExportWizard(0, date);
+ wizard->exec();
+ if (wizard->filename.isEmpty()) { // wizard aborted...
+ return;
+ }
+ if (QFile::exists(wizard->filename)) { // overwrite?
+ if (KMessageBox::Continue!=KMessageBox::warningContinueCancel(0, i18n("A document with this name already exists."), i18n("Overwrite file?"), i18n("&Overwrite") /*, true*/)) { // no
+ return;
+ }
+ }
+
+ // open file
+ Export *exportIFace = wizard->createExportFilter();
+ if (exportIFace == NULL) { // error
+ return;
+ }
+
+ if (!exportIFace->openFile()) { // error opening
+ KMessageBox::sorry(0, i18n("An error occurred while trying to open this file"), i18n("Sorry"), true);
+ delete exportIFace;
+ return; // abort...
+ }
+
+ // start writing data
+ exportIFace->addHeadline(i18n("Connection"), i18n("Day"), i18n("From"), i18n("Until"),
+ i18n("Duration"), i18n("Costs"), i18n("Bytes In"), i18n("Bytes Out") );
+
+ // name of the current connection
+ QString con;
+
+ // for collecting monthly statistics
+ int count = 0;
+ double costs = 0;
+ double bin = 0, bout = 0;
+ int duration = 0;
+
+ for(int i = 0; i < (int)logList.count(); i++) {
+ LogInfo *li = logList.at(i);
+
+ QDate logDate = li->from().date();
+ if (periodeFirst() <= logDate && periodeLast() >= logDate ) {
+ // get connection name for this line
+ con = li->connectionName();
+
+ // this connection name not in the list and combo box
+ if(lstConnections.findIndex(con) == -1) {
+ lstConnections.append(con);
+ cboConnections->insertItem(con);
+ }
+ // if all connections or the selected one
+ if(cboConnections->currentText() != con &&
+ cboConnections->currentItem() != 0)
+ continue;
+
+ count++;
+ costs += li->sessionCosts();
+ if(bin >= 0) {
+ if(li->bytesIn() < 0)
+ bin = -1;
+ else
+ bin += li->bytesIn();
+ }
+
+ if(bout >= 0) {
+ if(li->bytesOut() < 0)
+ bout = -1;
+ else
+ bout += li->bytesOut();
+ }
+
+ duration += li->from().secsTo(li->until());
+
+ QString _bin, _bout, b;
+ if(li->bytesIn() >= 0)
+ formatBytes(li->bytesIn(), _bin);
+ else
+ _bin = i18n("n/a");
+
+ if(li->bytesOut() >= 0)
+ formatBytes(li->bytesOut(), _bout);
+ else
+ _bout = i18n("n/a");
+
+ if(li->bytes() > 0)
+ formatBytes(li->bytes(), b);
+ else
+ b = i18n("n/a");
+
+ QString day;
+ day.sprintf("%2d", li->from().date().day());
+ QString con = li->connectionName();
+
+ QString s_duration;
+ formatDuration(li->from().secsTo(li->until()),
+ s_duration);
+
+ QString s_lifrom, s_liuntil, s_costs;
+ s_lifrom = KGlobal::locale()->formatTime(li->from().time(), false);
+ s_liuntil = KGlobal::locale()->formatTime(li->until().time(), false);
+ s_costs = KGlobal::locale()->formatMoney(li->sessionCosts());
+
+ // call export method
+ exportIFace->addDataline(con, day, s_lifrom, s_liuntil, s_duration,
+ s_costs, _bin, _bout);
+
+ }
+ }
+
+ if(calendar->month(periodeFirst()) == calendar->month(QDate::currentDate())) {
+
+ QString m_bin, m_bout;
+ if(bin < 0)
+ m_bin = i18n("n/a");
+ else
+ formatBytesMonth(bin, m_bin);
+
+ if(bout < 0)
+ m_bout = i18n("n/a");
+ else
+ formatBytesMonth(bout, m_bout);
+
+ QString m_duration;
+ formatDurationMonth(duration, m_duration);
+
+ costsMonth(costs, costs);
+ QString m_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
+
+ QString datetime = KGlobal::locale()->formatDateTime( QDateTime::currentDateTime(), true);
+
+ exportIFace->addEmptyLine();
+ exportIFace->addDataline(i18n("Monthly estimates (%1)").arg(datetime),
+ QString::null, QString::null, QString::null, m_duration, m_costs, m_bin, m_bout);
+ }
+
+ if(count) {
+ QString _bin, _bout, _b;
+
+ if(bin < 0)
+ _bin = i18n("n/a");
+ else
+ formatBytes(bin, _bin);
+
+ if(bout < 0)
+ _bout = i18n("n/a");
+ else
+ formatBytes(bout, _bout);
+
+ if(bin < 0 || bout < 0)
+ _b = i18n("n/a");
+ else
+ formatBytes(bout + bin, _b);
+
+ QString s_duration;
+ formatDuration(duration,
+ s_duration);
+
+ QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
+
+ // call export methods
+ exportIFace->addEmptyLine();
+ exportIFace->addDataline(i18n("%n connection", "%n connections", count), QString::null, QString::null, QString::null, s_duration,
+ s_costs, _bin, _bout);
+ exportIFace->setFinishCode();
+
+ // write buffer to file and close file
+ if (!exportIFace->closeFile()) {
+ KMessageBox::sorry(0, i18n("An error occurred while trying to write to this file."), i18n("Sorry"), true);
+ delete exportIFace;
+ return;
+ }
+
+ }
+ delete exportIFace;
+}
+
+QDate MonthlyWidget::periodeFirst() const
+{
+ return m_periodeFirst;
+}
+
+QDate MonthlyWidget::periodeLast() const
+{
+ const KCalendarSystem * calendar = KGlobal::locale()->calendar();
+
+ // One month minus one day
+ return calendar->addDays(calendar->addMonths(m_periodeFirst, 1), -1);
+}
+
+void MonthlyWidget::slotSelectionChanged()
+{
+ if (selectionItem)
+ {
+ int count = 0;
+ double costs = 0;
+ double bin = 0, bout = 0;
+ int duration = 0;
+ LogListItem *item;
+ LogInfo *li;
+ QListViewItemIterator it(lv);
+ while ( it.current() )
+ {
+ item = dynamic_cast<LogListItem*>(it.current());
+ if ( item && item->isSelected() && item->li)
+ {
+ li = item->li;
+ costs += li->sessionCosts();
+ if(bin >= 0) {
+ if(li->bytesIn() < 0)
+ bin = -1;
+ else
+ bin += li->bytesIn();
+ }
+
+ if(bout >= 0) {
+ if(li->bytesOut() < 0)
+ bout = -1;
+ else
+ bout += li->bytesOut();
+ }
+
+ duration += li->from().secsTo(li->until());
+ count++;
+ }
+ ++it;
+ }
+ if(count)
+ {
+ QString _bin, _bout, _b;
+
+ if(bin < 0)
+ _bin = i18n("n/a");
+ else
+ formatBytes(bin, _bin);
+
+ if(bout < 0)
+ _bout = i18n("n/a");
+ else
+ formatBytes(bout, _bout);
+
+ if(bin < 0 || bout < 0)
+ _b = i18n("n/a");
+ else
+ formatBytes(bout + bin, _b);
+
+ QString s_duration;
+ formatDuration(duration,
+ s_duration);
+
+ QString s_costs(KGlobal::locale()->formatMoney(costs, QString::null, 2));
+ selectionItem->setText(0, i18n("Selection (%n connection)", "Selection (%n connections)", count));
+ selectionItem->setText(1, s_duration);
+ selectionItem->setText(2, s_costs);
+ selectionItem->setText(3, _bin);
+ selectionItem->setText(4, _bout);
+ }
+ }
+}
+
+#include "monthly.moc"
diff --git a/kppp/logview/monthly.h b/kppp/logview/monthly.h
new file mode 100644
index 00000000..2a3cefc8
--- /dev/null
+++ b/kppp/logview/monthly.h
@@ -0,0 +1,80 @@
+/*
+ * kPPPlogview: a accounting log system for kPPP
+ *
+ * Copyright (C) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __MONTHLY__H__
+#define __MONTHLY__H__
+
+#include "log.h"
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qvaluelist.h>
+#include <qdatetime.h>
+
+#include <kbuttonbox.h>
+
+class KListView;
+class QComboBox;
+class LogListItem;
+
+class MonthlyWidget : public QWidget {
+ Q_OBJECT
+public:
+ MonthlyWidget(QWidget *parent = 0);
+
+private slots:
+ void prevMonth();
+ void nextMonth();
+ void currentMonth();
+ void slotConnections(int);
+ void exportWizard();
+ void slotSelectionChanged();
+
+private:
+ void layoutWidget();
+ void plotMonth();
+
+ /**
+ * Returns the first day in the period
+ */
+ QDate periodeFirst() const;
+ /**
+ * Returns the last day in the period
+ */
+ QDate periodeLast() const;
+
+ QDate m_periodeFirst; // First day in month
+ //int _month, _year;
+
+ QComboBox *cboConnections;
+ KButtonBox *bbox;
+ KListView *lv;
+ KListView *lv2;
+ LogListItem* selectionItem;
+ QLabel *title;
+ QPushButton *next, *prev, *today, *exportBttn;
+ QValueList<QString> lstConnections;
+ QGridLayout *tl;
+ QDateTime *dt;
+};
+
+#endif
diff --git a/kppp/macros.h b/kppp/macros.h
new file mode 100644
index 00000000..134ec2cf
--- /dev/null
+++ b/kppp/macros.h
@@ -0,0 +1,15 @@
+// helper macros for layouting
+
+#ifndef __MACROS__H__
+#define __MACROS__H__
+
+#include <qlayout.h>
+
+#define MIN_WIDTH(w) w->setMinimumWidth(w->sizeHint().width());
+#define MIN_HEIGHT(w) w->setMinimumHeight(w->sizeHint().height());
+#define MIN_SIZE(w) w->setMinimumSize(w->sizeHint());
+#define FIXED_SIZE(w) w->setFixedSize(w->sizeHint());
+#define FIXED_WIDTH(w) w->setFixedWidth(w->sizeHint().width());
+#define FIXED_HEIGHT(w) w->setFixedHeight(w->sizeHint().height());
+
+#endif
diff --git a/kppp/main.cpp b/kppp/main.cpp
new file mode 100644
index 00000000..96476851
--- /dev/null
+++ b/kppp/main.cpp
@@ -0,0 +1,453 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include "main.h"
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifdef _XPG4_2
+#define __xnet_connect connect
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+
+#include "kpppwidget.h"
+#include "opener.h"
+#include "pppdata.h"
+#include "providerdb.h"
+#include "version.h"
+#include "requester.h"
+
+#include <X11/Xlib.h>
+
+static const char description[] =
+ I18N_NOOP("A dialer and front-end to pppd");
+
+static const KCmdLineOptions options[] =
+{
+ { "c <account_name>", I18N_NOOP("Connect using 'account_name'"), 0 },
+ { "m <modem_name>", I18N_NOOP("Connect using 'modem_name'"), 0 },
+ { "k", I18N_NOOP("Terminate an existing connection"), 0 },
+ { "q", I18N_NOOP("Quit after end of connection"), 0 },
+ { "r <rule_file>", I18N_NOOP("Check syntax of rule_file"), 0 },
+ { "T", I18N_NOOP("Enable test-mode"), 0 },
+ { "dev <device_name>", I18N_NOOP("Use the specified device"), 0 },
+ KCmdLineLastOption
+};
+
+
+KPPPWidget* p_kppp;
+
+// for testing purposes
+bool TESTING=0;
+
+// initial effective user id before possible suid status is dropped
+uid_t euid;
+// helper process' pid
+pid_t helperPid = -1;
+
+QString local_ip_address;
+QString remote_ip_address;
+QString pidfile;
+
+#if 0
+extern "C" {
+ static int kppp_x_errhandler( Display *dpy, XErrorEvent *err ) {
+ char errstr[256]; // safe
+
+ /*
+ if(gpppdata.pppdpid() >= 0) {
+ kill(gpppdata.pppdpid(), SIGTERM);
+ }
+
+ p_kppp->stopAccounting();
+ removedns();
+ unlockdevice();*/
+
+ XGetErrorText( dpy, err->error_code, errstr, 256 );
+ kdFatal() << "X Error: " << errstr << endl;
+ kdFatal() << "Major opcode: " << err->request_code << endl;
+ exit(256);
+ return 0;
+ }
+
+
+ static int kppp_xio_errhandler( Display * ) {
+ if(gpppdata.get_xserver_exit_disconnect()) {
+ fprintf(stderr, "X11 Error!\n");
+ if(gpppdata.pppdRunning())
+ Requester::rq->killPPPDaemon();
+
+ p_kppp->stopAccounting();
+ removedns();
+ Modem::modem->unlockdevice();
+ return 0;
+ } else{
+ kdFatal() << "Fatal IO error: client killed" << endl;
+ exit(256);
+ return 0;
+ }
+ }
+} /* extern "C" */
+#endif
+
+int main( int argc, char **argv ) {
+ // make sure that open/fopen and so on NEVER return 1 or 2 (stdout and stderr)
+ // Expl: if stdout/stderr were closed on program start (by parent), open()
+ // would return a FD of 1, 2 (or even 0 if stdin was closed too)
+ if(fcntl(0, F_GETFL) == -1)
+ (void)open("/dev/null", O_RDONLY);
+
+ if(fcntl(1, F_GETFL) == -1)
+ (void)open("/dev/null", O_WRONLY);
+
+ if(fcntl(2, F_GETFL) == -1)
+ (void)open("/dev/null", O_WRONLY);
+
+ // Don't insert anything above this line unless you really know what
+ // you're doing. We're most likely running setuid root here,
+ // until we drop this status a few lines below.
+ int sockets[2];
+ if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) != 0) {
+ fprintf(stderr, "error creating socketpair !\n");
+ return 1;
+ }
+
+ switch(helperPid = fork()) {
+ case 0:
+ // child process
+ // make process leader of new group
+ setsid();
+ umask(0);
+ close(sockets[0]);
+ signal(SIGHUP, SIG_IGN);
+ (void) new Opener(sockets[1]);
+ // we should never get here
+ _exit(1);
+
+ case -1:
+ perror("fork() failed");
+ exit(1);
+ }
+
+ // parent process
+ close(sockets[1]);
+
+ // drop setuid status
+ euid = geteuid();
+ if (setgid(getgid()) < 0 && errno != EPERM) {
+ perror("setgid() failed");
+ exit(1);
+ }
+ setuid(getuid());
+ if (geteuid() != getuid()) {
+ perror("setuid() failed");
+ exit(1);
+ }
+
+ //
+ // end of setuid-dropping block.
+ //
+
+ // install exit handler that will kill the helper process
+ atexit(myShutDown);
+
+ // not needed anymore, just causes problems with broken setup
+ // if(getHomeDir() != 0)
+ // setenv("HOME", getHomeDir(), 1);
+
+ (void) new Requester(sockets[0]);
+
+ KAboutData aboutData("kppp", I18N_NOOP("KPPP"),
+ KPPPVERSION, description, KAboutData::License_GPL,
+ I18N_NOOP("(c) 1999-2002, The KPPP Developers"));
+ aboutData.addAuthor("Harri Porten", I18N_NOOP("Current maintainer"), "porten@kde.org");
+ aboutData.addAuthor("Bernd Wuebben", I18N_NOOP("Original author"), "wuebben@kde.org");
+ aboutData.addAuthor("Mario Weilguni",0, "");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options );
+
+
+
+ KApplication a;
+
+ // set portable locale for decimal point
+ setlocale(LC_NUMERIC ,"C");
+
+ // open configuration file
+ gpppdata.open();
+
+ kdDebug(5002) << "helperPid: " << (int) helperPid << endl;
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ bool terminate_connection = args->isSet("k");
+ if (args->isSet("r"))
+ return RuleSet::checkRuleFile(args->getOption("r"));
+
+ TESTING = args->isSet("T");
+
+ // make sure that nobody can read the password from the
+ // config file
+ QString configFile = KGlobal::dirs()->saveLocation("config")
+ + QString(kapp->name()) + "rc";
+ if(access(QFile::encodeName(configFile), F_OK) == 0)
+ chmod(QFile::encodeName(configFile), S_IRUSR | S_IWUSR);
+
+ // do we really need to generate an empty directory structure here ?
+ KGlobal::dirs()->saveLocation("appdata", "Rules");
+
+ int pid = create_pidfile();
+ QString err_msg = i18n("kppp can't create or read from\n%1.").arg(pidfile);
+
+ if(pid < 0) {
+ KMessageBox::error(0L, err_msg);
+ return 1;
+ }
+
+ if (terminate_connection) {
+ setgid(getgid());
+ setuid(getuid());
+ if (pid > 0)
+ kill(pid, SIGINT);
+ else
+ remove_pidfile();
+ return 0;
+ }
+
+ // Mario: testing
+ if(TESTING) {
+ gpppdata.open();
+ gpppdata.setAccountByIndex(0);
+
+ QString s = argv[2];
+ urlEncode(s);
+ kdDebug(5002) << s << endl;
+
+ remove_pidfile();
+ return 0;
+ }
+
+ if (pid > 0) {
+ QString msg = i18n("kppp has detected a %1 file.\n"
+ "Another instance of kppp seems to be "
+ "running under process-ID %2.\n"
+ "Please click Exit, make sure that you are "
+ "not running another kppp, delete the pid "
+ "file, and restart kppp.\n"
+ "Alternatively, if you have determined that "
+ "there is no other kppp running, please "
+ "click Continue to begin.")
+ .arg(pidfile).arg(pid);
+ int button = KMessageBox::warningYesNo(0, msg, i18n("Error"),
+ i18n("Exit"), KStdGuiItem::cont());
+ if (button == KMessageBox::Yes) /* exit */
+ return 1;
+
+ remove_pidfile();
+ pid = create_pidfile();
+ if(pid) {
+ KMessageBox::error(0L, err_msg);
+ return 1;
+ }
+ }
+
+ KPPPWidget kppp;
+ p_kppp = &kppp;
+
+ (void)new DockWidget(p_kppp->con_win, "dockw", p_kppp->stats);
+
+ a.setMainWidget(&kppp);
+ a.setTopWidget(&kppp);
+
+ // we really don't want to die accidentally, since that would leave the
+ // modem connected. If you really really want to kill me you must send
+ // me a SIGKILL.
+ signal(SIGINT, sighandler);
+ signal(SIGCHLD, sighandler);
+ signal(SIGUSR1, sighandler);
+ signal(SIGTERM, SIG_IGN);
+
+ // XSetErrorHandler( kppp_x_errhandler );
+ // XSetIOErrorHandler( kppp_xio_errhandler );
+
+ int ret = a.exec();
+
+ remove_pidfile();
+
+ return ret;
+}
+
+
+pid_t execute_command (const QString & cmd) {
+ QCString command = QFile::encodeName(cmd);
+ if (command.isEmpty() || command.length() > COMMAND_SIZE)
+ return (pid_t) -1;
+
+ pid_t id;
+
+ kdDebug(5002) << "Executing command: " << command << endl;
+
+ QApplication::flushX();
+ if((id = fork()) == 0) {
+ // don't bother dieppp()
+ signal(SIGCHLD, SIG_IGN);
+
+ // close file descriptors
+ const int open_max = sysconf( _SC_OPEN_MAX );
+ for (int fd = 3; fd < open_max; ++fd)
+ close(fd);
+
+ // drop privileges if running setuid root
+ setgid(getgid());
+ setuid(getuid());
+
+ system(command);
+ _exit(0);
+ }
+
+ return id;
+}
+
+
+// Create a file containing the current pid. Returns 0 on success,
+// -1 on failure or the pid of an already running kppp process.
+pid_t create_pidfile() {
+ int fd = -1;
+ char pidstr[40]; // safe
+
+ pidfile = KGlobal::dirs()->saveLocation("appdata") + "kppp.pid";
+
+ if(access(QFile::encodeName(pidfile), F_OK) == 0) {
+
+ if((access(QFile::encodeName(pidfile), R_OK) < 0) ||
+ (fd = open(QFile::encodeName(pidfile), O_RDONLY)) < 0)
+ return -1;
+
+ int sz = read(fd, &pidstr, 32);
+ close (fd);
+ if (sz < 0)
+ return -1;
+ pidstr[sz] = '\0';
+
+ kdDebug(5002) << "found kppp.pid containing: " << pidstr << endl;
+
+ // non-empty file ?
+ if (sz > 0) {
+ int oldpid;
+ int match = sscanf(pidstr, "%d", &oldpid);
+
+ // found a pid in pidfile ?
+ if (match < 1 || oldpid <= 0)
+ return -1;
+
+ // check if process exists
+ if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH)
+ return oldpid;
+ }
+
+ kdDebug(5002) << "pidfile is stale\n" << endl;
+ remove_pidfile();
+ }
+
+ if((fd = open(QFile::encodeName(pidfile), O_WRONLY | O_CREAT | O_EXCL,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
+ return -1;
+
+ fchown(fd, getuid(), getgid());
+
+ sprintf(pidstr, "%d\n", getpid());
+ write(fd, pidstr, strlen(pidstr));
+ close(fd);
+
+ return 0;
+}
+
+
+bool remove_pidfile() {
+ struct stat st;
+
+ // only remove regular files with user write permissions
+ if(stat(QFile::encodeName(pidfile), &st) == 0 )
+ if(S_ISREG(st.st_mode) && (access(QFile::encodeName(pidfile), W_OK) == 0)) {
+ unlink(QFile::encodeName(pidfile));
+ return true;
+ }
+
+ fprintf(stderr, "error removing pidfile.\n");
+ return false;
+}
+
+
+void myShutDown() {
+ pid_t pid;
+ // don't bother about SIGCHLDs anymore
+ signal(SIGCHLD, SIG_IGN);
+ // fprintf(stderr, "myShutDown(%i)\n", status);
+ pid = helperPid;
+ if(pid > 0) {
+ helperPid = -1;
+ // fprintf(stderr, "killing child process %i", pid);
+ kill(pid, SIGKILL);
+ }
+}
+
+void sighandler(int sig) {
+ QEvent *e = 0L;
+ if(sig == SIGCHLD) {
+ pid_t id = wait(0L);
+ if(id >= 0 && id == helperPid) // helper process died
+ e = new SignalEvent(sig);
+ } else if(sig == SIGINT || sig == SIGUSR1)
+ e = new SignalEvent(sig);
+
+ // let eventFilter() deal with this when we're back in the loop
+ if (e)
+ QApplication::postEvent(p_kppp, e);
+
+ signal(sig, sighandler); // reinstall signal handler
+}
+
diff --git a/kppp/main.h b/kppp/main.h
new file mode 100644
index 00000000..72a00a4a
--- /dev/null
+++ b/kppp/main.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef _MAIN_H_
+#define _MAIN_H_
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "kpppwidget.h"
+
+void killpppd();
+void sighandler(int);
+pid_t execute_command(const QString &);
+pid_t create_pidfile();
+bool remove_pidfile();
+void myShutDown();
+
+#endif
diff --git a/kppp/maintainer.h b/kppp/maintainer.h
new file mode 100644
index 00000000..74543260
--- /dev/null
+++ b/kppp/maintainer.h
@@ -0,0 +1,7 @@
+#ifndef __MAINTAINER__H__
+#define __MAINTAINER__H__
+
+#define PRIMARY_MAINTAINER "Harri Porten"
+#define PRIMARY_MAINTAINER_MAIL "<porten@kde.org>"
+
+#endif
diff --git a/kppp/miniterm.cpp b/kppp/miniterm.cpp
new file mode 100644
index 00000000..bd426be7
--- /dev/null
+++ b/kppp/miniterm.cpp
@@ -0,0 +1,281 @@
+/*
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <kwin.h>
+#include <khelpmenu.h>
+#include <kiconloader.h>
+#include "pppdata.h"
+#include "modem.h"
+#include "miniterm.h"
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kglobalsettings.h>
+#include <kapplication.h>
+#include <kmenubar.h>
+#include <kstdguiitem.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <qpopupmenu.h>
+
+
+extern PPPData gpppdata;
+
+MiniTerm::MiniTerm(QWidget *parent, const char *name)
+ : QDialog(parent, name, true)
+{
+ setCaption(i18n("Kppp Mini-Terminal"));
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ m_file = new QPopupMenu(this);
+ m_file->insertItem( SmallIcon("fileclose"), i18n("&Close"), this, SLOT(cancelbutton()) );
+ m_options = new QPopupMenu(this);
+ m_options->insertItem(i18n("&Reset Modem"),this,SLOT(resetModem()));
+ m_help =
+ new KHelpMenu(this,
+ i18n("MiniTerm - A terminal emulation for KPPP\n\n"
+ "(c) 1997 Bernd Johannes Wuebben <wuebben@kde.org>\n"
+ "(c) 1998 Harri Porten <porten@kde.org>\n"
+ "(c) 1998 Mario Weilguni <mweilguni@kde.org>\n\n"
+ "This program is published under the GNU GPL\n"
+ "(GNU General Public License)"
+ ));
+
+ menubar = new KMenuBar(this);
+ menubar->insertItem( i18n("&File"), m_file );
+ menubar->insertItem( i18n("&Modem"), m_options );
+ menubar->insertItem( KStdGuiItem::help().text(), m_help->menu());
+
+ statusbar = new QLabel(this);
+ statusbar->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+
+ terminal = new MyTerm(this, "term");
+
+ setupToolbar();
+
+ QVBoxLayout *layout=new QVBoxLayout(this);
+ layout->addWidget(menubar);
+ layout->addWidget(toolbar);
+ layout->addWidget(terminal);
+ layout->addWidget(statusbar);
+
+ inittimer = new QTimer(this);
+ connect(inittimer,SIGNAL(timeout()),this,SLOT(init()));
+ inittimer->start(500);
+
+ resize(550,400);
+}
+
+
+MiniTerm::~MiniTerm() {
+ delete toolbar;
+ delete statusbar;
+}
+
+
+void MiniTerm::setupToolbar() {
+ toolbar = new KToolBar( this );
+
+ toolbar->insertButton("exit", 0,
+ SIGNAL(clicked()), this,
+ SLOT(cancelbutton()), TRUE, i18n("Close MiniTerm"));
+
+ toolbar->insertButton("back", 0,
+ SIGNAL(clicked()), this,
+ SLOT(resetModem()), TRUE, i18n("Reset Modem"));
+
+ toolbar->insertButton("help", 0,
+ SIGNAL(clicked()), this,
+ SLOT(help()), TRUE, i18n("Help"));
+
+ toolbar->setBarPos( KToolBar::Top );
+ toolbar->setMovingEnabled(false);
+ toolbar->updateRects(true);
+}
+
+
+void MiniTerm::init() {
+ inittimer->stop();
+ statusbar->setText(i18n("Initializing Modem"));
+ kapp->processEvents();
+
+ int lock = Modem::modem->lockdevice();
+ if (lock == 1) {
+ statusbar->setText(i18n("Modem device is locked."));
+ return;
+ }
+
+ if (lock == -1) {
+ statusbar->setText(i18n("Unable to create modem lock file."));
+ return;
+ }
+
+ if(Modem::modem->opentty()) {
+ if(Modem::modem->hangup()) {
+ // send a carriage return and then wait a bit so that the modem will
+ // let us issue commands.
+ if(gpppdata.modemPreInitDelay() > 0) {
+ usleep(gpppdata.modemPreInitDelay() * 5000);
+ Modem::modem->writeLine("");
+ usleep(gpppdata.modemPreInitDelay() * 5000);
+ }
+ Modem::modem->writeLine(gpppdata.modemInitStr(0).local8Bit());
+ usleep(gpppdata.modemInitDelay() * 10000);
+
+ statusbar->setText(i18n("Modem Ready"));
+ terminal->setFocus();
+
+ kapp->processEvents();
+ kapp->processEvents();
+
+ Modem::modem->notify(this, SLOT(readChar(unsigned char)));
+ return;
+ }
+ }
+
+ // opentty() or hangup() failed
+ statusbar->setText(Modem::modem->modemMessage());
+ Modem::modem->unlockdevice();
+}
+
+
+void MiniTerm::readChar(unsigned char c) {
+
+ switch((int)c) {
+ case 8:
+ terminal->backspace();
+ break;
+ case 10:
+ terminal->mynewline();
+ break;
+ case 13:
+ terminal->myreturn();
+ break;
+ case 127:
+ terminal->backspace();
+ break;
+ default:
+ terminal->insertChar(c);
+ }
+}
+
+
+void MiniTerm::cancelbutton() {
+ Modem::modem->stop();
+
+ statusbar->setText(i18n("Hanging up..."));
+ kapp->processEvents();
+ KApplication::flushX();
+
+ Modem::modem->hangup();
+
+ Modem::modem->closetty();
+ Modem::modem->unlockdevice();
+
+ reject();
+}
+
+
+void MiniTerm::resetModem() {
+ statusbar->setText(i18n("Resetting Modem"));
+ terminal->newLine();
+ kapp->processEvents();
+ KApplication::flushX();
+
+ Modem::modem->hangup();
+
+ statusbar->setText(i18n("Modem Ready"));
+}
+
+
+void MiniTerm::closeEvent( QCloseEvent *e ) {
+ cancelbutton();
+ e->accept();
+}
+
+
+void MiniTerm::help() {
+ kapp->invokeHelp();
+}
+
+
+MyTerm::MyTerm(QWidget *parent, const char* name)
+ : QMultiLineEdit(parent, name)
+{
+ setFont(KGlobalSettings::fixedFont());
+}
+
+void MyTerm::keyPressEvent(QKeyEvent *k) {
+ // ignore meta keys
+ if (k->ascii() == 0)
+ return;
+
+ if(k->ascii() == 13)
+ myreturn();
+
+ Modem::modem->writeChar((unsigned char) k->ascii());
+}
+
+
+void MyTerm::insertChar(unsigned char c) {
+ QMultiLineEdit::insert(QChar(c));
+}
+
+
+void MyTerm::newLine() {
+ QMultiLineEdit::newLine();
+}
+
+
+void MyTerm::del() {
+ QMultiLineEdit::del();
+}
+
+
+void MyTerm::backspace() {
+ QMultiLineEdit::backspace();
+}
+
+
+void MyTerm::myreturn() {
+ int column;
+ int line;
+
+ getCursorPosition(&line,&column);
+ for (int i = 0; i < column;i++)
+ QMultiLineEdit::cursorLeft();
+}
+
+
+void MyTerm::mynewline() {
+ QMultiLineEdit::end(FALSE);
+ QMultiLineEdit::newLine();
+}
+
+#include "miniterm.moc"
diff --git a/kppp/miniterm.h b/kppp/miniterm.h
new file mode 100644
index 00000000..aef973cb
--- /dev/null
+++ b/kppp/miniterm.h
@@ -0,0 +1,103 @@
+/*
+ *
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _MINITERM_H_
+#define _MINITERM_H_
+
+#include <qdialog.h>
+#include <qpixmap.h>
+#include <qevent.h>
+#include <qmultilineedit.h>
+#include <ktoolbar.h>
+
+class KHelpMenu;
+
+class QTimer;
+class QMenuBar;
+class KToolBar;
+class KHelpMenu;
+class QPushButton;
+class QLabel;
+
+class MyTerm : public QMultiLineEdit {
+ Q_OBJECT
+public:
+ MyTerm(QWidget *parent=0, const char *name=0);
+
+ void keyPressEvent (QKeyEvent*);
+ void insertChar(unsigned char c);
+ void newLine();
+ void backspace();
+ void del();
+ void myreturn();
+ void mynewline();
+};
+
+
+class MiniTerm : public QDialog {
+ Q_OBJECT
+public:
+
+ MiniTerm(QWidget *parent=0, const char *name=0);
+ ~MiniTerm();
+
+ void closeEvent( QCloseEvent *e );
+
+public slots:
+ void cancelbutton();
+ void init();
+ void readChar(unsigned char);
+ void help();
+ void resetModem();
+
+protected:
+ void setupToolbar();
+
+ QPushButton *cancel;
+ MyTerm *terminal;
+ QTimer *inittimer;
+
+ QMenuBar * menubar;
+ KToolBar * toolbar;
+ QPopupMenu * m_file;
+ QPopupMenu * m_edit;
+ QPopupMenu * m_options;
+ KHelpMenu * m_help;
+ QLabel * statusbar;
+ QPushButton *pb1;
+ QPushButton *pb2;
+ QPushButton *pb3;
+ QPushButton *pb4;
+
+ QPixmap pb1_pixmap;
+ QPixmap pb2_pixmap;
+ QPixmap pb3_pixmap;
+ QPixmap pb4_pixmap;
+};
+
+
+#endif
diff --git a/kppp/modem.cpp b/kppp/modem.cpp
new file mode 100644
index 00000000..0e8b016c
--- /dev/null
+++ b/kppp/modem.cpp
@@ -0,0 +1,619 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was added by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <setjmp.h>
+#include <qregexp.h>
+#include <assert.h>
+
+#include "modem.h"
+#include "pppdata.h"
+#include "requester.h"
+#include <klocale.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+#include <config.h>
+
+static sigjmp_buf jmp_buffer;
+
+Modem *Modem::modem = 0;
+
+Modem::Modem() :
+ modemfd(-1),
+ sn(0L),
+ data_mode(false),
+ modem_is_locked(false)
+{
+ assert(modem==0);
+ modem = this;
+ args = KCmdLineArgs::parsedArgs();
+}
+
+
+Modem::~Modem() {
+ modem = 0;
+}
+
+
+speed_t Modem::modemspeed() {
+ // convert the string modem speed int the gpppdata object to a t_speed type
+ // to set the modem. The constants here should all be ifdef'd because
+ // other systems may not have them
+ int i = gpppdata.speed().toInt()/100;
+
+ switch(i) {
+ case 24:
+ return B2400;
+ break;
+ case 96:
+ return B9600;
+ break;
+ case 192:
+ return B19200;
+ break;
+ case 384:
+ return B38400;
+ break;
+#ifdef B57600
+ case 576:
+ return B57600;
+ break;
+#endif
+
+#ifdef B115200
+ case 1152:
+ return B115200;
+ break;
+#endif
+
+#ifdef B230400
+ case 2304:
+ return B230400;
+ break;
+#endif
+
+#ifdef B460800
+ case 4608:
+ return B460800;
+ break;
+#endif
+
+#ifdef B921600
+ case 9216:
+ return B921600;
+ break;
+#endif
+
+ default:
+ return B38400;
+ break;
+ }
+}
+
+
+bool Modem::opentty() {
+ // int flags;
+ QString device = "";
+ if (args->isSet("dev"))
+ device = args->getOption("dev");
+ else
+ device = gpppdata.modemDevice();
+ kdDebug() << "Opening Device: " << device << endl;
+
+ if((modemfd = Requester::rq->openModem(device))<0) {
+ errmsg = i18n("Unable to open modem.");
+ return false;
+ }
+
+#if 0
+ if(gpppdata.UseCDLine()) {
+ if(ioctl(modemfd, TIOCMGET, &flags) == -1) {
+ errmsg = i18n("Unable to detect state of CD line.");
+ ::close(modemfd);
+ modemfd = -1;
+ return false;
+ }
+ if ((flags&TIOCM_CD) == 0) {
+ errmsg = i18n("The modem is not ready.");
+ ::close(modemfd);
+ modemfd = -1;
+ return false;
+ }
+ }
+#endif
+
+ tcdrain (modemfd);
+ tcflush (modemfd, TCIOFLUSH);
+
+ if(tcgetattr(modemfd, &tty) < 0){
+ // this helps in some cases
+ tcsendbreak(modemfd, 0);
+ sleep(1);
+ if(tcgetattr(modemfd, &tty) < 0){
+ errmsg = i18n("The modem is busy.");
+ ::close(modemfd);
+ modemfd = -1;
+ return false;
+ }
+ }
+
+ memset(&initial_tty,'\0',sizeof(initial_tty));
+
+ initial_tty = tty;
+
+ tty.c_cc[VMIN] = 0; // nonblocking
+ tty.c_cc[VTIME] = 0;
+ tty.c_oflag = 0;
+ tty.c_lflag = 0;
+
+ tty.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tty.c_cflag |= CS8 | CREAD;
+ tty.c_cflag |= CLOCAL; // ignore modem status lines
+ tty.c_iflag = IGNBRK | IGNPAR /* | ISTRIP */ ;
+ tty.c_lflag &= ~ICANON; // non-canonical mode
+ tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHOKE);
+
+
+ // the english/i18n mix below is ugly but we want to keep working
+ // after someone changed the code to use i18n'ed config values
+ QString flowCtrl = gpppdata.flowcontrol();
+ if(flowCtrl != "None" && flowCtrl != i18n("None")) {
+ if(flowCtrl == "CRTSCTS" || flowCtrl == i18n("Hardware [CRTSCTS]")) {
+ tty.c_cflag |= CRTSCTS;
+ }
+ else {
+ tty.c_iflag |= IXON | IXOFF;
+ tty.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tty.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ }
+ }
+ else {
+ tty.c_cflag &= ~CRTSCTS;
+ tty.c_iflag &= ~(IXON | IXOFF);
+ }
+
+ cfsetospeed(&tty, modemspeed());
+ cfsetispeed(&tty, modemspeed());
+
+ tcdrain(modemfd);
+
+ if(tcsetattr(modemfd, TCSANOW, &tty) < 0){
+ errmsg = i18n("The modem is busy.");
+ ::close(modemfd);
+ modemfd=-1;
+ return false;
+ }
+
+ errmsg = i18n("Modem Ready.");
+ return true;
+}
+
+
+bool Modem::closetty() {
+ if(modemfd >=0 ) {
+ stop();
+ /* discard data not read or transmitted */
+ tcflush(modemfd, TCIOFLUSH);
+
+ if(tcsetattr(modemfd, TCSANOW, &initial_tty) < 0){
+ errmsg = i18n("Can't restore tty settings: tcsetattr()\n");
+ ::close(modemfd);
+ modemfd = -1;
+ return false;
+ }
+ ::close(modemfd);
+ modemfd = -1;
+ }
+
+ return true;
+}
+
+
+void Modem::readtty(int) {
+ char buffer[200];
+ unsigned char c;
+ int len;
+
+ // read data in chunks of up to 200 bytes
+ if((len = ::read(modemfd, buffer, 200)) > 0) {
+ // split buffer into single characters for further processing
+ for(int i = 0; i < len; i++) {
+ c = buffer[i] & 0x7F;
+ emit charWaiting(c);
+ }
+ }
+}
+
+
+void Modem::notify(const QObject *receiver, const char *member) {
+ connect(this, SIGNAL(charWaiting(unsigned char)), receiver, member);
+ startNotifier();
+}
+
+
+void Modem::stop() {
+ disconnect(SIGNAL(charWaiting(unsigned char)));
+ stopNotifier();
+}
+
+
+void Modem::startNotifier() {
+ if(modemfd >= 0) {
+ if(sn == 0) {
+ sn = new QSocketNotifier(modemfd, QSocketNotifier::Read, this);
+ connect(sn, SIGNAL(activated(int)), SLOT(readtty(int)));
+ kdDebug(5002) << "QSocketNotifier started!" << endl;
+ } else {
+ // Debug("QSocketNotifier re-enabled!");
+ sn->setEnabled(true);
+ }
+ }
+}
+
+
+void Modem::stopNotifier() {
+ if(sn != 0) {
+ sn->setEnabled(false);
+ disconnect(sn);
+ delete sn;
+ sn = 0;
+ kdDebug(5002) << "QSocketNotifier stopped!" << endl;
+ }
+}
+
+
+void Modem::flush() {
+ char c;
+ while(read(modemfd, &c, 1) == 1);
+}
+
+
+bool Modem::writeChar(unsigned char c) {
+ int s;
+ do {
+ s = write(modemfd, &c, 1);
+ if (s < 0) {
+ kdError(5002) << "write() in Modem::writeChar failed" << endl;
+ return false;
+ }
+ } while(s == 0);
+
+ return true;
+}
+
+
+bool Modem::writeLine(const char *buf) {
+ int len = strlen(buf);
+ char *b = new char[len+2];
+ memcpy(b, buf, len);
+ // different modems seem to need different line terminations
+ QString term = gpppdata.enter();
+ if(term == "LF")
+ b[len++]='\n';
+ else if(term == "CR")
+ b[len++]='\r';
+ else if(term == "CR/LF") {
+ b[len++]='\r';
+ b[len++]='\n';
+ }
+ int l = len;
+ while(l) {
+ int wr = write(modemfd, &b[len-l], l);
+ if(wr < 0) {
+ if (errno == EAGAIN)
+ continue;
+ // TODO do something meaningful with the error code (or ignore it
+ kdError(5002) << "write() in Modem::writeLine failed" << endl;
+ delete[] b;
+ return false;
+ }
+ l -= wr;
+ }
+ delete[] b;
+ return true;
+}
+
+
+bool Modem::hangup() {
+ // this should really get the modem to hang up and go into command mode
+ // If anyone sees a fault in the following please let me know, since
+ // this is probably the most imporant snippet of code in the whole of
+ // kppp. If people complain about kppp being stuck, this piece of code
+ // is most likely the reason.
+ struct termios temptty;
+
+ if(modemfd >= 0) {
+
+ // is this Escape & HangupStr stuff really necessary ? (Harri)
+
+ if (data_mode) escape_to_command_mode();
+
+ // Then hangup command
+ writeLine(gpppdata.modemHangupStr().local8Bit());
+
+ usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
+
+
+#ifndef DEBUG_WO_DIALING
+ if (sigsetjmp(jmp_buffer, 1) == 0) {
+ // set alarm in case tcsendbreak() hangs
+ signal(SIGALRM, alarm_handler);
+ alarm(2);
+
+ tcsendbreak(modemfd, 0);
+
+ alarm(0);
+ signal(SIGALRM, SIG_IGN);
+ } else {
+ kdWarning(5002) << "Modem did not respond properly." << endl;
+#if 0 // observed false alarms with some modems
+ // we reach this point if the alarm handler got called
+ closetty();
+ close(modemfd);
+ modemfd = -1;
+ errmsg = i18n("The modem does not respond.");
+ return false;
+#endif
+ }
+
+#ifndef __svr4__ // drops DTR but doesn't set it afterwards again. not good for init.
+ tcgetattr(modemfd, &temptty);
+ cfsetospeed(&temptty, B0);
+ cfsetispeed(&temptty, B0);
+ tcsetattr(modemfd, TCSAFLUSH, &temptty);
+#else
+ int modemstat;
+ ioctl(modemfd, TIOCMGET, &modemstat);
+ modemstat &= ~TIOCM_DTR;
+ ioctl(modemfd, TIOCMSET, &modemstat);
+ ioctl(modemfd, TIOCMGET, &modemstat);
+ modemstat |= TIOCM_DTR;
+ ioctl(modemfd, TIOCMSET, &modemstat);
+#endif
+
+ usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 secs
+
+ cfsetospeed(&temptty, modemspeed());
+ cfsetispeed(&temptty, modemspeed());
+ tcsetattr(modemfd, TCSAFLUSH, &temptty);
+#endif
+ return true;
+ } else
+ return false;
+}
+
+
+void Modem::escape_to_command_mode() {
+ // Send Properly bracketed escape code to put the modem back into command state.
+ // A modem will accept AT commands only when it is in command state.
+ // When a modem sends the host the CONNECT string, that signals
+ // that the modem is now in the connect state (no long accepts AT commands.)
+ // Need to send properly timed escape sequence to put modem in command state.
+ // Escape codes and guard times are controlled by S2 and S12 values.
+ //
+ tcflush(modemfd, TCIOFLUSH);
+
+ // +3 because quiet time must be greater than guard time.
+ usleep((gpppdata.modemEscapeGuardTime()+3)*20000);
+ QCString tmp = gpppdata.modemEscapeStr().local8Bit();
+ write(modemfd, tmp.data(), tmp.length());
+ tcflush(modemfd, TCIOFLUSH);
+ usleep((gpppdata.modemEscapeGuardTime()+3)*20000);
+
+ data_mode = false;
+}
+
+
+const QString Modem::modemMessage() {
+ return errmsg;
+}
+
+
+QString Modem::parseModemSpeed(const QString &s) {
+ // this is a small (and bad) parser for modem speeds
+ int rx = -1;
+ int tx = -1;
+ int i;
+ QString result;
+
+ kdDebug(5002) << "Modem reported result string: " << s << endl;
+
+ const int RXMAX = 7;
+ const int TXMAX = 2;
+ QRegExp rrx[RXMAX] = {
+ QRegExp("[0-9]+[:/ ]RX", false),
+ QRegExp("[0-9]+RX", false),
+ QRegExp("[/: -][0-9]+[/: ]", false),
+ QRegExp("[/: -][0-9]+$", false),
+ QRegExp("CARRIER [^0-9]*[0-9]+", false),
+ QRegExp("CONNECT [^0-9]*[0-9]+", false),
+ QRegExp("[0-9]+") // panic mode
+ };
+
+ QRegExp trx[TXMAX] = {
+ QRegExp("[0-9]+[:/ ]TX", false),
+ QRegExp("[0-9]+TX", false)
+ };
+
+ for(i = 0; i < RXMAX; i++) {
+ int len, idx, result;
+ if((idx = rrx[i].search(s)) > -1) {
+ len = rrx[i].matchedLength();
+
+ //
+ // rrx[i] has been matched, idx contains the start of the match
+ // and len contains how long the match is. Extract the match.
+ //
+ QString sub = s.mid(idx, len);
+
+ //
+ // Now extract the digits only from the match, which will
+ // then be converted to an int.
+ //
+ if ((idx = rrx[RXMAX-1].search( sub )) > -1) {
+ len = rrx[RXMAX-1].matchedLength();
+ sub = sub.mid(idx, len);
+ result = sub.toInt();
+ if(result > 0) {
+ rx = result;
+ break;
+ }
+ }
+ }
+ }
+
+ for(i = 0; i < TXMAX; i++) {
+ int len, idx, result;
+ if((idx = trx[i].search(s)) > -1) {
+ len = trx[i].matchedLength();
+
+ //
+ // trx[i] has been matched, idx contains the start of the match
+ // and len contains how long the match is. Extract the match.
+ //
+ QString sub = s.mid(idx, len);
+
+ //
+ // Now extract the digits only from the match, which will then
+ // be converted to an int.
+ //
+ if((idx = rrx[RXMAX-1].search(sub)) > -1) {
+ len = rrx[RXMAX-1].matchedLength();
+ sub = sub.mid(idx, len);
+ result = sub.toInt();
+ if(result > 0) {
+ tx = result;
+ break;
+ }
+ }
+ }
+ }
+
+ if(rx == -1 && tx == -1)
+ result = i18n("Unknown speed");
+ else if(tx == -1)
+ result.setNum(rx);
+ else if(rx == -1) // should not happen
+ result.setNum(tx);
+ else
+ result.sprintf("%d/%d", rx, tx);
+
+ kdDebug(5002) << "The parsed result is: " << result << endl;
+
+ return result;
+}
+
+
+// Lock modem device. Returns 0 on success 1 if the modem is locked and -1 if
+// a lock file can't be created ( permission problem )
+int Modem::lockdevice() {
+ int fd;
+ char newlock[80]=""; // safe
+
+ if(!gpppdata.modemLockFile()) {
+ kdDebug(5002) << "The user doesn't want a lockfile." << endl;
+ return 0;
+ }
+
+ if (modem_is_locked)
+ return 1;
+
+ QString device = "";
+ if (args->isSet("dev"))
+ device = args->getOption("dev");
+ else
+ device = gpppdata.modemDevice();
+
+ QString lockfile = LOCK_DIR"/LCK..";
+ lockfile += device.mid(5); // append everything after /dev/
+
+ if(access(QFile::encodeName(lockfile), F_OK) == 0) {
+ if ((fd = Requester::rq->openLockfile(QFile::encodeName(lockfile), O_RDONLY)) >= 0) {
+ // Mario: it's not necessary to read more than lets say 32 bytes. If
+ // file has more than 32 bytes, skip the rest
+ char oldlock[33]; // safe
+ int sz = read(fd, &oldlock, 32);
+ close (fd);
+ if (sz <= 0)
+ return 1;
+ oldlock[sz] = '\0';
+
+ kdDebug(5002) << "Device is locked by: " << &oldlock << endl;
+
+ int oldpid;
+ int match = sscanf(oldlock, "%d", &oldpid);
+
+ // found a pid in lockfile ?
+ if (match < 1 || oldpid <= 0)
+ return 1;
+
+ // check if process exists
+ if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH)
+ return 1;
+
+ kdDebug(5002) << "lockfile is stale" << endl;
+ }
+ }
+
+ fd = Requester::rq->openLockfile(device,
+ O_WRONLY|O_TRUNC|O_CREAT);
+ if(fd >= 0) {
+ sprintf(newlock,"%010d\n", getpid());
+ kdDebug(5002) << "Locking Device: " << newlock << endl;
+
+ write(fd, newlock, strlen(newlock));
+ close(fd);
+ modem_is_locked=true;
+
+ return 0;
+ }
+
+ return -1;
+
+}
+
+
+// UnLock modem device
+void Modem::unlockdevice() {
+ if (modem_is_locked) {
+ kdDebug(5002) << "UnLocking Modem Device" << endl;
+ Requester::rq->removeLockfile();
+ modem_is_locked=false;
+ }
+}
+
+void alarm_handler(int) {
+ // fprintf(stderr, "alarm_handler(): Received SIGALRM\n");
+
+ // jump
+ siglongjmp(jmp_buffer, 1);
+}
+
+#include "modem.moc"
diff --git a/kppp/modem.h b/kppp/modem.h
new file mode 100644
index 00000000..3c744b38
--- /dev/null
+++ b/kppp/modem.h
@@ -0,0 +1,94 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was added by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MODEM_H_
+#define _MODEM_H_
+
+#include <qdir.h>
+
+#include <sys/types.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <qsocketnotifier.h>
+
+#include <config.h>
+
+class KCmdLineArgs;
+
+void alarm_handler(int);
+
+class Modem : public QObject {
+ Q_OBJECT
+public:
+ Modem();
+ ~Modem();
+
+ bool opentty();
+ bool closetty();
+ bool hangup();
+ bool writeChar(unsigned char);
+ bool writeLine(const char *);
+ bool dataMode() const { return data_mode; }
+ void setDataMode(bool set) { data_mode = set; }
+ const QString modemMessage();
+ speed_t modemspeed();
+ static QString parseModemSpeed(const QString &);
+ void notify(const QObject *, const char *);
+ void stop();
+ void flush();
+
+ int lockdevice();
+ void unlockdevice();
+
+public:
+ static Modem *modem;
+
+signals:
+ void charWaiting(unsigned char);
+
+private slots:
+ void startNotifier();
+ void stopNotifier();
+ void readtty(int);
+
+private:
+ void escape_to_command_mode();
+ KCmdLineArgs *args;
+
+private:
+ int modemfd;
+ QSocketNotifier *sn;
+ bool data_mode;
+ QString errmsg;
+ struct termios initial_tty;
+ struct termios tty;
+ bool modem_is_locked;
+};
+
+#endif
+
+
diff --git a/kppp/modemcmds.cpp b/kppp/modemcmds.cpp
new file mode 100644
index 00000000..3f6e7af7
--- /dev/null
+++ b/kppp/modemcmds.cpp
@@ -0,0 +1,384 @@
+/*
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with dummyWidget program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qlayout.h>
+#include <qslider.h>
+#include <kbuttonbox.h>
+#include <stdlib.h>
+#include <kapplication.h> // for getMiniIcon()
+#include <klocale.h>
+#include "modemcmds.h"
+#include "pppdata.h"
+#include <kwin.h>
+
+#include <qgroupbox.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+
+
+#define ADJUSTEDIT(e) e->setText("XXXXXXXXqy"); e->setMinimumSize(e->sizeHint()); e->setFixedHeight(e->sizeHint().height()); e->setText(""); e->setMaxLength(MODEMSTR_SIZE);
+
+// a little trick to make the label look like a disabled lineedit
+#define FORMATSLIDERLABEL(l) l->setFixedWidth(l->sizeHint().width()); l->setFixedHeight(QLineEdit(dummyWidget).sizeHint().height()); l->setAlignment(AlignCenter); l->setFrameStyle(QFrame::WinPanel|QFrame::Sunken); l->setLineWidth(2);
+
+ModemCommands::ModemCommands(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n("Edit Modem Commands"), Ok|Cancel)
+{
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+ QWidget *dummyWidget = new QWidget(this);
+ setMainWidget(dummyWidget);
+
+ const int GRIDROWS = 22;
+ int row = 0;
+
+ // toplevel layout
+ QVBoxLayout *tl = new QVBoxLayout(dummyWidget, 10, 4);
+
+ // add grid + frame
+ QGridLayout *l1 = new QGridLayout(GRIDROWS, 4);
+ tl->addLayout(l1);
+ box = new QGroupBox(dummyWidget, "box");
+ l1->addMultiCellWidget(box, row++, GRIDROWS, 0, 3);
+
+ // put slider and label into a separate H-Box
+ QHBoxLayout *l2 = new QHBoxLayout;
+ l1->addLayout(l2, row, 2);
+ lpreinitslider = new QLabel("MMMM", dummyWidget);
+ FORMATSLIDERLABEL(lpreinitslider);
+
+ preinitslider = new QSlider(0, 300, 1, 0,
+ QSlider::Horizontal, dummyWidget);
+ preinitslider->setFixedHeight(preinitslider->sizeHint().height());
+ connect(preinitslider, SIGNAL(valueChanged(int)),
+ lpreinitslider, SLOT(setNum(int)));
+ l2->addWidget(lpreinitslider, 0);
+ l2->addWidget(preinitslider, 1);
+
+ lpreinit = new QLabel(i18n("Pre-init delay (sec/100):"), dummyWidget);
+ l1->addWidget(lpreinit, row++, 1);
+
+ for(int i = 0; i < PPPData::NumInitStrings; i++) {
+ initstr[i] = new QLineEdit(dummyWidget);
+ QLabel *initLabel = new QLabel(i18n("Initialization string %1:").arg(i + 1),
+ dummyWidget);
+ ADJUSTEDIT(initstr[i]);
+ l1->addWidget(initLabel, row, 1);
+ l1->addWidget(initstr[i], row++, 2);
+ }
+
+ QHBoxLayout *l3 = new QHBoxLayout;
+ l1->addLayout(l3, row, 2);
+ linitslider = new QLabel("MMMM", dummyWidget);
+ FORMATSLIDERLABEL(linitslider);
+ initslider = new QSlider(1, 300, 1, 0,
+ QSlider::Horizontal, dummyWidget);
+ initslider->setFixedHeight(initslider->sizeHint().height());
+ connect(initslider, SIGNAL(valueChanged(int)),
+ linitslider, SLOT(setNum(int)));
+ l3->addWidget(linitslider, 0);
+ l3->addWidget(initslider, 1);
+
+ label3 = new QLabel(i18n("Post-init delay (sec/100):"), dummyWidget);
+ l1->addWidget(label3, row++, 1);
+
+ /* Set ATS11 (Dial tone duration) between 0-255 (Default ~ 70) */
+ QHBoxLayout *l4 = new QHBoxLayout;
+ l1->addLayout(l4, row, 2);
+ ldurationslider = new QLabel("MMMM", dummyWidget);
+ FORMATSLIDERLABEL(ldurationslider);
+ durationslider = new QSlider(1, 255, 1, 0,
+ QSlider::Horizontal, dummyWidget);
+ durationslider->setFixedHeight(durationslider->sizeHint().height());
+ connect(durationslider, SIGNAL(valueChanged(int)),
+ ldurationslider, SLOT(setNum(int)));
+ l4->addWidget(ldurationslider, 0);
+ l4->addWidget(durationslider, 1);
+
+ lduration = new QLabel(i18n("Dialing speed (sec/100):"), dummyWidget);
+ l1->addWidget(lduration, row++, 1);
+
+
+ initresp = new QLineEdit(dummyWidget);
+ label2 = new QLabel(i18n("Init &response:"), dummyWidget);
+ label2->setBuddy(initresp);
+ ADJUSTEDIT(initresp);
+ l1->addWidget(label2, row, 1);
+ l1->addWidget(initresp, row++, 2);
+
+ nodetectdialtone = new QLineEdit(dummyWidget);
+ lnodetectdialtone = new QLabel(i18n("No di&al tone detection:"), dummyWidget);
+ lnodetectdialtone->setBuddy(nodetectdialtone);
+ ADJUSTEDIT(nodetectdialtone);
+ l1->addWidget(lnodetectdialtone, row, 1);
+ l1->addWidget(nodetectdialtone, row++, 2);
+
+ dialstr = new QLineEdit(dummyWidget);
+ label4 = new QLabel(i18n("Dial &string:"),dummyWidget);
+ label4->setBuddy(dialstr);
+ ADJUSTEDIT(dialstr);
+ l1->addWidget(label4, row, 1);
+ l1->addWidget(dialstr, row++, 2);
+
+ connectresp = new QLineEdit(dummyWidget);
+ label5 = new QLabel(i18n("Co&nnect response:"), dummyWidget);
+ label5->setBuddy(connectresp);
+ ADJUSTEDIT(connectresp);
+ l1->addWidget(label5, row, 1);
+ l1->addWidget(connectresp, row++, 2);
+
+ busyresp = new QLineEdit(dummyWidget);
+ label6 = new QLabel(i18n("Busy response:"), dummyWidget);
+ ADJUSTEDIT(busyresp);
+ l1->addWidget(label6, row, 1);
+ l1->addWidget(busyresp, row++, 2);
+
+ nocarrierresp = new QLineEdit(dummyWidget);
+ label7 = new QLabel(i18n("No carr&ier response:"), dummyWidget);
+ label7->setBuddy(nocarrierresp);
+ ADJUSTEDIT(nocarrierresp);
+ l1->addWidget(label7, row, 1);
+ l1->addWidget(nocarrierresp, row++, 2);
+
+ nodialtoneresp = new QLineEdit(dummyWidget);
+ label8 = new QLabel(i18n("No dial tone response:"), dummyWidget);
+ ADJUSTEDIT(nodialtoneresp);
+ l1->addWidget(label8, row, 1);
+ l1->addWidget(nodialtoneresp, row++, 2);
+
+ hangupstr = new QLineEdit(dummyWidget);
+ label9 = new QLabel(i18n("&Hangup string:"), dummyWidget);
+ label9->setBuddy(hangupstr);
+ ADJUSTEDIT(hangupstr);
+ l1->addWidget(label9, row, 1);
+ l1->addWidget(hangupstr, row++, 2);
+
+ hangupresp = new QLineEdit(dummyWidget);
+ label10 = new QLabel(i18n("Hangup response:"), dummyWidget);
+ ADJUSTEDIT(hangupresp);
+ l1->addWidget(label10, row, 1);
+ l1->addWidget(hangupresp, row++, 2);
+
+ answerstr = new QLineEdit(dummyWidget);
+ label11 = new QLabel(i18n("Answ&er string:"), dummyWidget);
+ label11->setBuddy(answerstr);
+ ADJUSTEDIT(answerstr);
+ l1->addWidget(label11, row, 1);
+ l1->addWidget(answerstr, row++, 2);
+
+ ringresp = new QLineEdit(dummyWidget);
+ label12 = new QLabel(i18n("Ring response:"), dummyWidget);
+ ADJUSTEDIT(ringresp);
+ l1->addWidget(label12, row, 1);
+ l1->addWidget(ringresp, row++, 2);
+
+ answerresp = new QLineEdit(dummyWidget);
+ label13 = new QLabel(i18n("Ans&wer response:"), dummyWidget);
+ label13->setBuddy(answerresp);
+ ADJUSTEDIT(answerresp);
+ l1->addWidget(label13, row, 1);
+ l1->addWidget(answerresp, row++, 2);
+
+ dlpresp = new QLineEdit(dummyWidget);
+ label17 = new QLabel(i18n("DLP response:"), dummyWidget);
+ ADJUSTEDIT(dlpresp);
+ l1->addWidget(label17, row, 1);
+ l1->addWidget(dlpresp, row++, 2);
+
+ escapestr = new QLineEdit(dummyWidget);
+ label14 = new QLabel(i18n("Escape strin&g:"), dummyWidget);
+ label14->setBuddy(escapestr);
+ ADJUSTEDIT(escapestr);
+ l1->addWidget(label14, row, 1);
+ l1->addWidget(escapestr, row++, 2);
+
+ escaperesp = new QLineEdit(dummyWidget);
+ label15 = new QLabel(i18n("Escape response:"), dummyWidget);
+ ADJUSTEDIT(escaperesp);
+ l1->addWidget(label15, row, 1);
+ l1->addWidget(escaperesp, row++, 2);
+
+ QHBoxLayout *l5 = new QHBoxLayout;
+ l1->addLayout(l5, row, 2);
+ lslider = new QLabel("MMMM", dummyWidget);
+ FORMATSLIDERLABEL(lslider);
+
+ slider = new QSlider(0, 255, 1, 0,
+ QSlider::Horizontal, dummyWidget);
+ slider->setFixedHeight(slider->sizeHint().height());
+ connect(slider, SIGNAL(valueChanged(int)),
+ lslider, SLOT(setNum(int)));
+ l5->addWidget(lslider, 0);
+ l5->addWidget(slider, 1);
+
+ label16 = new QLabel(i18n("Guard time (sec/50):"), dummyWidget);
+ l1->addWidget(label16, row++, 1);
+
+ QLabel *l = new QLabel(i18n("Volume off/low/high:"), dummyWidget);
+ l1->addWidget(l, row, 1);
+ QHBoxLayout *l6 = new QHBoxLayout;
+ l1->addLayout(l6, row++, 2);
+ volume_off = new QLineEdit(dummyWidget);
+ volume_off->setFixedHeight(volume_off->sizeHint().height());
+ volume_off->setMinimumWidth((int)(volume_off->sizeHint().width() / 2));
+ volume_medium = new QLineEdit(dummyWidget);
+ volume_medium->setFixedHeight(volume_medium->sizeHint().height());
+ volume_medium->setMinimumWidth((int)(volume_medium->sizeHint().width() / 2));
+ volume_high = new QLineEdit(dummyWidget);
+ volume_high->setFixedHeight(volume_high->sizeHint().height());
+ volume_high->setMinimumWidth((int)(volume_high->sizeHint().width() / 2));
+ l6->addWidget(volume_off);
+ l6->addWidget(volume_medium);
+ l6->addWidget(volume_high);
+
+ initstr[0]->setFocus();
+
+ l1->addColSpacing(0, 10);
+ l1->addColSpacing(3, 10);
+ l1->addRowSpacing(0, 5);
+ l1->addRowSpacing(GRIDROWS, 5);
+
+ //set stuff from gpppdata
+ preinitslider->setValue(gpppdata.modemPreInitDelay());
+ lpreinitslider->setNum(gpppdata.modemPreInitDelay());
+ for(int i = 0; i < PPPData::NumInitStrings; i++)
+ initstr[i]->setText(gpppdata.modemInitStr(i));
+ initslider->setValue(gpppdata.modemInitDelay());
+ linitslider->setNum(gpppdata.modemInitDelay());
+ initresp->setText(gpppdata.modemInitResp());
+
+ durationslider->setValue(gpppdata.modemToneDuration());
+ ldurationslider->setNum(gpppdata.modemToneDuration());
+
+ nodetectdialtone->setText(gpppdata.modemNoDialToneDetectionStr());
+ dialstr->setText(gpppdata.modemDialStr());
+ dlpresp->setText(gpppdata.modemDLPResp());
+
+ connectresp->setText(gpppdata.modemConnectResp());
+ busyresp->setText(gpppdata.modemBusyResp());
+ nocarrierresp->setText(gpppdata.modemNoCarrierResp());
+ nodialtoneresp->setText(gpppdata.modemNoDialtoneResp());
+
+ escapestr->setText(gpppdata.modemEscapeStr());
+ escaperesp->setText(gpppdata.modemEscapeResp());
+
+ hangupstr->setText(gpppdata.modemHangupStr());
+ hangupresp->setText(gpppdata.modemHangupResp());
+
+ answerstr->setText(gpppdata.modemAnswerStr());
+ ringresp->setText(gpppdata.modemRingResp());
+ answerresp->setText(gpppdata.modemAnswerResp());
+
+ slider->setValue(gpppdata.modemEscapeGuardTime());
+ lslider->setNum(gpppdata.modemEscapeGuardTime());
+
+ volume_off->setText(gpppdata.volumeOff());
+ volume_medium->setText(gpppdata.volumeMedium());
+ volume_high->setText(gpppdata.volumeHigh());
+
+ // Save the data, so if it not exist we save the default
+ gpppdata.save();
+}
+
+
+void ModemCommands::slotOk() {
+ gpppdata.setModemPreInitDelay(lpreinitslider->text().toInt());
+ for(int i = 0; i < PPPData::NumInitStrings; i++)
+ gpppdata.setModemInitStr(i, initstr[i]->text());
+ gpppdata.setModemInitResp(initresp->text());
+ gpppdata.setModemInitDelay(linitslider->text().toInt());
+
+ gpppdata.setModemToneDuration(ldurationslider->text().toInt());
+ gpppdata.setModemNoDialToneDetectionStr(nodetectdialtone->text());
+ gpppdata.setModemDialStr(dialstr->text());
+ gpppdata.setModemDLPResp(dlpresp->text());
+
+ gpppdata.setModemConnectResp(connectresp->text());
+ gpppdata.setModemBusyResp(busyresp->text());
+ gpppdata.setModemNoCarrierResp(nocarrierresp->text());
+ gpppdata.setModemNoDialtoneResp(nodialtoneresp->text());
+
+ gpppdata.setModemEscapeStr(escapestr->text());
+ gpppdata.setModemEscapeResp(escaperesp->text());
+ gpppdata.setModemEscapeGuardTime(lslider->text().toInt());
+ gpppdata.setModemHangupStr(hangupstr->text());
+ gpppdata.setModemHangupResp(hangupresp->text());
+
+ gpppdata.setModemAnswerStr(answerstr->text());
+ gpppdata.setModemRingResp(ringresp->text());
+ gpppdata.setModemAnswerResp(answerresp->text());
+
+ gpppdata.setVolumeHigh(volume_high->text());
+ gpppdata.setVolumeMedium(volume_medium->text());
+ gpppdata.setVolumeOff(volume_off->text());
+
+ gpppdata.save();
+ accept();
+}
+
+
+void ModemCommands::slotCancel() {
+ //set stuff from gpppdata
+ preinitslider->setValue(gpppdata.modemPreInitDelay());
+ lpreinitslider->setNum(gpppdata.modemPreInitDelay());
+ for(int i = 0; i < PPPData::NumInitStrings; i++)
+ initstr[i]->setText(gpppdata.modemInitStr(i));
+ initslider->setValue(gpppdata.modemInitDelay());
+ linitslider->setNum(gpppdata.modemInitDelay());
+ initresp->setText(gpppdata.modemInitResp());
+
+ durationslider->setValue(gpppdata.modemToneDuration());
+ ldurationslider->setNum(gpppdata.modemToneDuration());
+
+ nodetectdialtone->setText(gpppdata.modemNoDialToneDetectionStr());
+ dialstr->setText(gpppdata.modemDialStr());
+ dlpresp->setText(gpppdata.modemDLPResp());
+
+ connectresp->setText(gpppdata.modemConnectResp());
+ busyresp->setText(gpppdata.modemBusyResp());
+ nocarrierresp->setText(gpppdata.modemNoCarrierResp());
+ nodialtoneresp->setText(gpppdata.modemNoDialtoneResp());
+
+ escapestr->setText(gpppdata.modemEscapeStr());
+ escaperesp->setText(gpppdata.modemEscapeResp());
+
+ hangupstr->setText(gpppdata.modemHangupStr());
+ hangupresp->setText(gpppdata.modemHangupResp());
+
+ answerstr->setText(gpppdata.modemAnswerStr());
+ ringresp->setText(gpppdata.modemRingResp());
+ answerresp->setText(gpppdata.modemAnswerResp());
+
+ slider->setValue(gpppdata.modemEscapeGuardTime());
+ lslider->setNum(gpppdata.modemEscapeGuardTime());
+
+ volume_off->setText(gpppdata.volumeOff());
+ volume_medium->setText(gpppdata.volumeMedium());
+ volume_high->setText(gpppdata.volumeHigh());
+ reject();
+}
+
+#include "modemcmds.moc"
diff --git a/kppp/modemcmds.h b/kppp/modemcmds.h
new file mode 100644
index 00000000..5dd48b67
--- /dev/null
+++ b/kppp/modemcmds.h
@@ -0,0 +1,131 @@
+
+/*
+ *
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _MODEMCMDS_H_
+#define _MODEMCMDS_H_
+
+#include <qgroupbox.h>
+#include <kdialogbase.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <pppdata.h>
+
+class QLineEdit;
+class QLabel;
+class QPushButton;
+class QGroupBox;
+
+class ModemCommands : public KDialogBase {
+
+Q_OBJECT
+
+public:
+
+ ModemCommands(QWidget *parent=0, const char *name=0);
+ ~ModemCommands() {}
+
+private slots:
+ void slotCancel();
+ void slotOk();
+
+private:
+
+ QGroupBox *box;
+
+ QLineEdit *initstr[int(PPPData::NumInitStrings)];
+
+ QLineEdit *initresp;
+ QLabel *label2;
+
+ QSlider *preinitslider;
+ QLabel *lpreinitslider;
+ QLabel *lpreinit;
+
+ QSlider *initslider;
+ QLabel *linitslider;
+ QLabel *label3;
+
+ QSlider *durationslider;
+ QLabel *ldurationslider;
+ QLabel *lduration;
+
+ QLineEdit *nodetectdialtone;
+ QLabel *lnodetectdialtone;
+
+ QLineEdit *dialstr;
+ QLabel *label4;
+
+ QLineEdit *connectresp;
+ QLabel *label5;
+
+ QLineEdit *busyresp;
+ QLabel *label6;
+
+ QLineEdit *nocarrierresp;
+ QLabel *label7;
+
+ QLineEdit *nodialtoneresp;
+ QLabel *label8;
+
+ QLineEdit *hangupstr;
+ QLabel *label9;
+
+ QLineEdit *hangupresp;
+ QLabel *label10;
+
+ QLineEdit *answerstr;
+ QLabel *label11;
+
+ QLineEdit *ringresp;
+ QLabel *label12;
+
+ QLineEdit *answerresp;
+ QLabel *label13;
+
+ QLineEdit *escapestr;
+ QLabel *label14;
+
+ QLineEdit *escaperesp;
+ QLineEdit *dlpresp;
+ QLabel *label17;
+
+ QLabel *label15;
+
+ QSlider *slider;
+ QLabel *lslider;
+ QLabel *label16;
+
+ QLineEdit *volume_off, *volume_medium, *volume_high;
+};
+#endif
+
+
+
diff --git a/kppp/modemdb.cpp b/kppp/modemdb.cpp
new file mode 100644
index 00000000..2afcf26e
--- /dev/null
+++ b/kppp/modemdb.cpp
@@ -0,0 +1,244 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#include <qlabel.h>
+#include <kbuttonbox.h>
+#include <qlayout.h>
+#include "modemdb.h"
+#include <klocale.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <kconfig.h>
+#include <kstdguiitem.h>
+
+ModemSelector::ModemSelector(QWidget *parent) : QDialog(parent, 0, true) {
+ // set up widgets and such
+ setCaption(i18n("Select Modem Type"));
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QLabel *l1 = new QLabel(i18n("To set up your modem, first choose its vendor in the "
+ "list to the left, and then select the model from the "
+ "right list. If you don't know which modem you have, "
+ "you can try out one of the \"Generic\" modems."),
+ this);
+ l1->setAlignment(AlignLeft | WordBreak);
+ l1->setFixedWidth(400);
+ l1->setMinimumHeight(50);
+ tl->addWidget(l1, 0);
+
+ tl->addSpacing(10);
+
+ QHBoxLayout *tl1 = new QHBoxLayout(10);
+ tl->addLayout(tl1, 1);
+ vendor = new QListBox(this);
+ model = new QListBox(this);
+ vendor->setMinimumSize(200, 130);
+ model->setMinimumSize(200, 130);
+ tl1->addWidget(vendor, 2);
+ tl1->addWidget(model, 3);
+
+ KButtonBox *bbox = new KButtonBox(this);
+ bbox->addStretch(1);
+ ok = bbox->addButton(KStdGuiItem::ok());
+ ok->setDefault(true);
+ ok->setEnabled(false);
+ cancel = bbox->addButton(KStdGuiItem::cancel());
+ bbox->layout();
+ tl->addWidget(bbox);
+ setFixedSize(sizeHint());
+
+ // set up modem database
+ db = new ModemDatabase();
+
+ // set up signal/slots
+ connect(ok, SIGNAL(clicked()),
+ this, SLOT(reject()));
+ connect(cancel, SIGNAL(clicked()),
+ this, SLOT(reject()));
+ connect(vendor, SIGNAL(highlighted(int)),
+ this, SLOT(vendorSelected(int)));
+ connect(model, SIGNAL(highlighted(int)),
+ this, SLOT(modelSelected(int)));
+ connect(model, SIGNAL(selected(int)),
+ this, SLOT(selected(int)));
+
+ // fill vendor list with life
+ vendor->insertStringList(*db->vendors());
+
+ vendor->setCurrentItem(0);
+}
+
+
+ModemSelector::~ModemSelector() {
+ delete db;
+}
+
+
+void ModemSelector::vendorSelected(int idx) {
+ ok->setEnabled(false);
+
+ QString name = vendor->text(idx);
+ QStringList *models = db->models(name);
+ model->clear();
+ model->insertStringList(*models);
+
+ // FIXME: work around Qt bug
+ if(models->count() == 0)
+ model->update();
+ delete models;
+}
+
+
+void ModemSelector::modelSelected(int) {
+ ok->setEnabled(true);
+}
+
+void ModemSelector::selected(int) {
+ accept();
+}
+
+
+ModemDatabase::ModemDatabase() {
+ load();
+}
+
+
+ModemDatabase::~ModemDatabase() {
+ delete lvendors;
+ delete modemDB;
+}
+
+
+const QStringList *ModemDatabase::vendors() {
+ return lvendors;
+}
+
+
+QStringList *ModemDatabase::models(QString vendor) {
+ QStringList *sl = new QStringList;
+ QString s = i18n("<Generic>");
+ if(vendor == s)
+ vendor = i18n("<Generic>");
+
+ for(uint i = 0; i < modems.count(); i++) {
+ CharDict *dict = modems.at(i);
+ if(dict->find("Vendor") != 0) {
+ if(vendor == *(*dict)["Vendor"] && (*(*dict)["Name"]).at(0) != '!')
+ sl->append(*(*dict)["Name"]);
+ }
+ }
+ sl->sort();
+
+ return sl;
+}
+
+
+void ModemDatabase::loadModem(const QString &key, CharDict &dict) {
+ // KEntryIterator *it = modemDB->entryIterator(key);
+ // KEntryDictEntry *e;
+ QMap <QString, QString> map;
+ QMap <QString, QString>::Iterator it;
+ // KEntryMapConstIterator e;
+ KEntry e;
+ map = modemDB->entryMap(key);
+ it = map.begin();
+
+ // remove parent attribute
+ dict.setAutoDelete(true);
+ dict.remove("Parent");
+
+ // e = it->current();
+ while(!it.key().isNull()) {
+ if(dict.find(it.key()) == 0) {
+ dict.insert(it.key(), new QString(it.data()));
+ }
+ it++;
+ }
+
+ // check name attribute
+ if(dict["Name"] == 0 || key[0]=='!') {
+ dict.replace("Name", new QString(key));
+ }
+
+ // check parent attribute
+ if(dict["Parent"] != 0)
+ loadModem(*dict["Parent"], dict);
+ else
+ // inherit common at last
+ if (key != "Common")
+ loadModem("Common", dict);
+
+}
+
+
+void ModemDatabase::load() {
+ modemDB = new KConfig("DB/modemDB.rc", 0);
+ lvendors = new QStringList;
+ modems.setAutoDelete(true);
+
+ QStringList list = modemDB->groupList();
+ QStringList::Iterator it = list.begin();
+ while(it != list.end()) {
+ modemDB->setGroup(*it);
+ CharDict *c = new CharDict;
+ c->setAutoDelete(true);
+ loadModem(*it, *c);
+
+ // if(strcmp(it->latin1(), "Common") == 0) {
+ if(*it == "Common") {
+ QString s = i18n("Hayes(tm) compatible modem");
+ c->replace("Name", new QString (s));
+
+ s = i18n("<Generic>");
+ c->replace("Vendor", new QString(s));
+ }
+ modems.append(c);
+
+ if(modemDB->hasKey("Vendor")) {
+ QString vendor = modemDB->readEntry("Vendor");
+ if(lvendors->findIndex(vendor) == -1)
+ lvendors->append(vendor);
+ }
+ ++it;
+ }
+
+ lvendors->sort();
+
+ lvendors->insert(0, i18n("<Generic>"));
+}
+
+
+void ModemDatabase::save(KConfig *) {
+}
+
+#include "modemdb.moc"
diff --git a/kppp/modemdb.h b/kppp/modemdb.h
new file mode 100644
index 00000000..ca743281
--- /dev/null
+++ b/kppp/modemdb.h
@@ -0,0 +1,85 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __MODEMDB__H__
+#define __MODEMDB__H__
+
+#include <qdialog.h>
+#include <qstringlist.h>
+#include <qdict.h>
+#include <qptrlist.h>
+class KConfig;
+class QListBox;
+
+typedef QDict<QString> CharDict;
+
+class ModemDatabase {
+public:
+ ModemDatabase();
+ ~ModemDatabase();
+
+ const QStringList *vendors();
+ QStringList *models(QString vendor);
+
+ void save(KConfig *);
+
+private:
+ void load();
+ void loadModem(const QString & key, CharDict &dict);
+ QPtrList<CharDict> modems;
+
+ QStringList *lvendors;
+
+ KConfig *modemDB;
+};
+
+
+class ModemSelector : public QDialog {
+ Q_OBJECT
+public:
+ ModemSelector(QWidget *parent = 0);
+ ~ModemSelector();
+
+private slots:
+ void vendorSelected(int idx);
+ void modelSelected(int idx);
+ void selected(int idx);
+
+private:
+ QPushButton *ok, *cancel;
+ QListBox *vendor, *model;
+ ModemDatabase *db;
+};
+
+#endif
diff --git a/kppp/modeminfo.cpp b/kppp/modeminfo.cpp
new file mode 100644
index 00000000..a2338aac
--- /dev/null
+++ b/kppp/modeminfo.cpp
@@ -0,0 +1,292 @@
+/*
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file contributed by: Markus Wuebben, mwuebben@fiwi02.wiwi.uni-tuebingen.de
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <unistd.h>
+#include <qregexp.h>
+#include <qlayout.h>
+#include <kwin.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kpushbutton.h>
+#include "modeminfo.h"
+#include "modem.h"
+#include <klocale.h>
+
+ModemTransfer::ModemTransfer(QWidget *parent, const char *name)
+ : QDialog(parent, name,TRUE, WStyle_Customize|WStyle_NormalBorder)
+{
+ setCaption(i18n("ATI Query"));
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+
+ progressBar = new KProgress(this, "bar");
+ progressBar->setTotalSteps(8);
+
+ statusBar = new QLabel(this,"sBar");
+ statusBar->setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ statusBar->setAlignment(AlignCenter);
+
+ // This is a rather complicated case. Since we do not know which
+ // message is the widest in the national language, we'd to
+ // search all these messages. This is a little overkill, so I take
+ // the longest english message, translate it and give it additional
+ // 20 percent space. Hope this is enough.
+ statusBar->setText(i18n("Unable to create modem lock file."));
+ statusBar->setFixedWidth((statusBar->sizeHint().width() * 12) / 10);
+ statusBar->setFixedHeight(statusBar->sizeHint().height() + 4);
+
+ // set original text
+ statusBar->setText(i18n("Looking for modem..."));
+ progressBar->setFixedHeight(statusBar->minimumSize().height());
+ tl->addWidget(progressBar);
+ tl->addWidget(statusBar);
+
+ cancel = new KPushButton(KStdGuiItem::cancel(), this);
+ cancel->setFocus();
+ connect(cancel, SIGNAL(clicked()), SLOT(cancelbutton()));
+
+ QHBoxLayout *l1 = new QHBoxLayout;
+ tl->addLayout(l1);
+ l1->addStretch(1);
+ l1->addWidget(cancel);
+
+ setFixedSize(sizeHint());
+
+ step = 0;
+
+ ////////////////////////////////////////////////
+
+ timeout_timer = new QTimer(this);
+ connect(timeout_timer, SIGNAL(timeout()), SLOT(time_out_slot()));
+
+ scripttimer = new QTimer(this);
+ connect(scripttimer, SIGNAL(timeout()), SLOT(do_script()));
+
+ timeout_timer->start(15000,TRUE); // 15 secs single shot
+ QTimer::singleShot(500, this, SLOT(init()));
+
+}
+
+
+void ModemTransfer::ati_done() {
+ scripttimer->stop();
+ timeout_timer->stop();
+ Modem::modem->closetty();
+ Modem::modem->unlockdevice();
+ hide();
+
+ // open the result window
+ ModemInfo *mi = new ModemInfo(this);
+ for(int i = 0; i < NUM_OF_ATI; i++)
+ mi->setAtiString(i, ati_query_strings[i]);
+ mi->exec();
+ delete mi;
+
+ accept();
+}
+
+
+void ModemTransfer::time_out_slot() {
+ timeout_timer->stop();
+ scripttimer->stop();
+
+ KMessageBox::error(this, i18n("Modem query timed out."));
+ reject();
+}
+
+
+void ModemTransfer::init() {
+
+ kapp->processEvents();
+
+ int lock = Modem::modem->lockdevice();
+ if (lock == 1) {
+
+ statusBar->setText(i18n("Modem device is locked."));
+ return;
+ }
+
+ if (lock == -1) {
+
+ statusBar->setText(i18n("Unable to create modem lock file."));
+ return;
+ }
+
+
+ if(Modem::modem->opentty()) {
+ if(Modem::modem->hangup()) {
+ usleep(100000); // wait 0.1 secs
+ Modem::modem->writeLine("ATE0Q1V1"); // E0 don't echo the commands I send ...
+
+ statusBar->setText(i18n("Modem Ready"));
+ kapp->processEvents();
+ usleep(100000); // wait 0.1 secs
+ kapp->processEvents();
+ scripttimer->start(1000); // this one does the ati query
+
+ // clear modem buffer
+ Modem::modem->flush();
+
+ Modem::modem->notify(this, SLOT(readChar(unsigned char)));
+ return;
+ }
+ }
+
+ // opentty() or hangup() failed
+ statusBar->setText(Modem::modem->modemMessage());
+ step = 99; // wait until cancel is pressed
+ Modem::modem->unlockdevice();
+}
+
+
+void ModemTransfer::do_script() {
+ QString msg;
+ QString query;
+
+ switch(step) {
+ case 0:
+ readtty();
+ statusBar->setText("ATI...");
+ progressBar->advance(1);
+ Modem::modem->writeLine("ATI\n");
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ readtty();
+ msg.sprintf("ATI %d ...", step);
+ query.sprintf("ATI%d\n", step);
+ statusBar->setText(msg);
+ progressBar->advance(1);
+ Modem::modem->writeLine(query.local8Bit());
+ break;
+
+ default:
+ readtty();
+ ati_done();
+ }
+ step++;
+}
+
+void ModemTransfer::readChar(unsigned char c) {
+ if(readbuffer.length() < 255)
+ readbuffer += c;
+}
+
+void ModemTransfer::readtty() {
+
+ if (step == 0)
+ return;
+
+ readbuffer.replace(QRegExp("[\n\r]")," "); // remove stray \n and \r
+ readbuffer = readbuffer.stripWhiteSpace(); // strip of leading or trailing white
+ // space
+
+ if(step <= NUM_OF_ATI)
+ ati_query_strings[step-1] = readbuffer.copy();
+
+ readbuffer = "";
+}
+
+
+void ModemTransfer::cancelbutton() {
+ scripttimer->stop();
+ Modem::modem->stop();
+ timeout_timer->stop();
+
+ statusBar->setText(i18n("One moment please..."));
+ kapp->processEvents();
+
+ Modem::modem->hangup();
+
+ Modem::modem->closetty();
+ Modem::modem->unlockdevice();
+ reject();
+}
+
+
+void ModemTransfer::closeEvent( QCloseEvent *e ) {
+ cancelbutton();
+ e->accept();
+}
+
+
+ModemInfo::ModemInfo(QWidget *parent, const char* name)
+ : QDialog(parent, name, TRUE, WStyle_Customize|WStyle_NormalBorder)
+{
+ QString label_text;
+
+ setCaption(i18n("Modem Query Results"));
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+
+ QGridLayout *l1 = new QGridLayout(NUM_OF_ATI, 2, 5);
+ tl->addLayout(l1, 1);
+ for(int i = 0 ; i < NUM_OF_ATI ; i++) {
+
+ label_text = "";
+ if ( i == 0)
+ label_text.sprintf("ATI :");
+ else
+ label_text.sprintf("ATI %d:", i );
+
+ ati_label[i] = new QLabel(label_text, this);
+ l1->addWidget(ati_label[i], i, 0);
+
+ ati_label_result[i] = new QLineEdit(this);
+ ati_label_result[i]->setMinimumWidth(fontMetrics().width('H') * 24);
+ l1->addWidget(ati_label_result[i], i, 1);
+ }
+ //tl->addSpacing(1);
+
+ QHBoxLayout *l2 = new QHBoxLayout;
+ QPushButton *ok = new KPushButton(KStdGuiItem::close(), this);
+ ok->setDefault(TRUE);
+ ok->setFocus();
+
+ tl->addLayout(l2);
+ l2->addStretch(1);
+
+ connect(ok, SIGNAL(clicked()), SLOT(accept()));
+ l2->addWidget(ok);
+
+ setMinimumSize(sizeHint());
+}
+
+
+void ModemInfo::setAtiString(int i, QString s) {
+ if(i < NUM_OF_ATI)
+ ati_label_result[i]->setText(s);
+}
+
+#include "modeminfo.moc"
diff --git a/kppp/modeminfo.h b/kppp/modeminfo.h
new file mode 100644
index 00000000..ccd3b6ea
--- /dev/null
+++ b/kppp/modeminfo.h
@@ -0,0 +1,88 @@
+/*
+ *
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _MODEMINFO_H_
+#define _MODEMINFO_H_
+
+#include <qdialog.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qevent.h>
+#include <qtimer.h>
+#include <kprogress.h>
+
+const int NUM_OF_ATI = 8;
+
+
+class ModemTransfer : public QDialog {
+ Q_OBJECT
+public:
+ ModemTransfer(QWidget *parent=0, const char *name=0);
+
+public slots:
+ void init();
+ void readtty();
+ void do_script();
+ void time_out_slot();
+ void cancelbutton();
+ void readChar(unsigned char);
+
+private:
+ void ati_done();
+
+protected:
+ void closeEvent(QCloseEvent *e);
+
+private:
+ int step;
+ QString readbuffer;
+
+ QPushButton *cancel;
+ KProgress *progressBar;
+ QLabel *statusBar;
+
+ QTimer *timeout_timer;
+ QTimer *scripttimer;
+ QString ati_query_strings[NUM_OF_ATI];
+};
+
+
+class ModemInfo : public QDialog {
+ Q_OBJECT
+public:
+ ModemInfo(QWidget *parent=0, const char *name=0);
+
+public:
+ void setAtiString(int num, QString s);
+
+private:
+ QLabel *ati_label[NUM_OF_ATI];
+ QLineEdit *ati_label_result[NUM_OF_ATI];
+};
+
+#endif
diff --git a/kppp/modems.cpp b/kppp/modems.cpp
new file mode 100644
index 00000000..570ca994
--- /dev/null
+++ b/kppp/modems.cpp
@@ -0,0 +1,277 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ *
+ *
+ *
+ * Copyright (C) 2004 Simone Gotti
+ * <simone.gotti@email.it>
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qdir.h>
+#include <stdlib.h>
+#include <qlayout.h>
+#include <qtabdialog.h>
+#include <qwhatsthis.h>
+#include <qmessagebox.h>
+
+#include <kapplication.h>
+#include <kbuttonbox.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kwin.h>
+#include <kdialogbase.h>
+#include <qvgroupbox.h>
+
+#include "general.h"
+#include "pppdata.h"
+#include "modems.h"
+#include "accounting.h"
+#include "providerdb.h"
+#include "edit.h"
+
+void parseargs(char* buf, char** args);
+
+ModemsWidget::ModemsWidget( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ int min = 0;
+ QVBoxLayout *l1 = new QVBoxLayout(parent, 10, 10);
+
+ // add a hbox
+ QHBoxLayout *l11 = new QHBoxLayout;
+ l1->addLayout(l11);
+
+ modemlist_l = new QListBox(parent);
+ modemlist_l->setMinimumSize(160, 128);
+ connect(modemlist_l, SIGNAL(highlighted(int)),
+ this, SLOT(slotListBoxSelect(int)));
+ connect(modemlist_l, SIGNAL(selected(int)),
+ this, SLOT(editmodem()));
+ l11->addWidget(modemlist_l, 10);
+
+ QVBoxLayout *l111 = new QVBoxLayout;
+ l11->addLayout(l111, 1);
+ edit_b = new QPushButton(i18n("&Edit..."), parent);
+ connect(edit_b, SIGNAL(clicked()), SLOT(editmodem()));
+ QWhatsThis::add(edit_b, i18n("Allows you to modify the selected account"));
+
+ min = edit_b->sizeHint().width();
+ min = QMAX(70,min);
+ edit_b->setMinimumWidth(min);
+
+ l111->addWidget(edit_b);
+
+ new_b = new QPushButton(i18n("&New..."), parent);
+ connect(new_b, SIGNAL(clicked()), SLOT(newmodem()));
+ l111->addWidget(new_b);
+ QWhatsThis::add(new_b, i18n("Create a new dialup connection\n"
+ "to the Internet"));
+
+ copy_b = new QPushButton(i18n("Co&py"), parent);
+ connect(copy_b, SIGNAL(clicked()), SLOT(copymodem()));
+ l111->addWidget(copy_b);
+ QWhatsThis::add(copy_b,
+ i18n("Makes a copy of the selected account. All\n"
+ "settings of the selected account are copied\n"
+ "to a new account that you can modify to fit your\n"
+ "needs"));
+
+ delete_b = new QPushButton(i18n("De&lete"), parent);
+ connect(delete_b, SIGNAL(clicked()), SLOT(deletemodem()));
+ l111->addWidget(delete_b);
+ QWhatsThis::add(delete_b,
+ i18n("<p>Deletes the selected account\n\n"
+ "<font color=\"red\"><b>Use with care!</b></font>"));
+
+ //load up account list from gppdata to the list box
+ // but keep the current one selected in gpppdata
+ if(gpppdata.modemCount() > 0) {
+ const QString currentmodem = gpppdata.modname();
+ for(int i=0; i <= gpppdata.modemCount()-1; i++) {
+ gpppdata.setModemByIndex(i);
+ modemlist_l->insertItem(gpppdata.modname());
+ }
+ gpppdata.setModem(currentmodem);
+ }
+
+ slotListBoxSelect(modemlist_l->currentItem());
+
+ l1->activate();
+}
+
+
+
+void ModemsWidget::slotListBoxSelect(int idx) {
+ delete_b->setEnabled((bool)(idx != -1));
+ edit_b->setEnabled((bool)(idx != -1));
+ copy_b->setEnabled((bool)(idx != -1));
+ if(idx!=-1) {
+ QString modem = gpppdata.modname();
+ gpppdata.setModemByIndex(modemlist_l->currentItem());
+ gpppdata.setModem(modem);
+ }
+}
+
+void ModemsWidget::editmodem() {
+ gpppdata.setModem(modemlist_l->text(modemlist_l->currentItem()));
+
+ int result = doTab();
+
+ if(result == QDialog::Accepted) {
+ modemlist_l->changeItem(gpppdata.modname(),modemlist_l->currentItem());
+ emit resetmodems();
+ gpppdata.save();
+ }
+}
+
+
+void ModemsWidget::newmodem() {
+ if(modemlist_l->count() == MAX_MODEMS) {
+ KMessageBox::sorry(this, i18n("Maximum number of modems reached."));
+ return;
+ }
+
+ int result;
+
+ if (gpppdata.newmodem() == -1)
+ return;
+ result = doTab();
+
+
+ if(result == QDialog::Accepted) {
+ modemlist_l->insertItem(gpppdata.modname());
+ modemlist_l->setSelected(modemlist_l->findItem(gpppdata.modname()),
+ true);
+ emit resetmodems();
+ gpppdata.save();
+ } else
+ gpppdata.deleteModem();
+}
+
+
+void ModemsWidget::copymodem() {
+ if(modemlist_l->count() == MAX_MODEMS) {
+ KMessageBox::sorry(this, i18n("Maximum number of modems reached."));
+ return;
+ }
+
+ if(modemlist_l->currentItem()<0) {
+ KMessageBox::sorry(this, i18n("No modem selected."));
+ return;
+ }
+
+ gpppdata.copymodem(modemlist_l->currentItem());
+
+ modemlist_l->insertItem(gpppdata.modname());
+ emit resetmodems();
+ gpppdata.save();
+}
+
+
+void ModemsWidget::deletemodem() {
+
+ QString s = i18n("Are you sure you want to delete\nthe modem \"%1\"?")
+ .arg(modemlist_l->text(modemlist_l->currentItem()));
+
+ if(KMessageBox::warningContinueCancel(this, s, i18n("Confirm"), KStdGuiItem::del()) != KMessageBox::Continue)
+ return;
+
+ if(gpppdata.deleteModem(modemlist_l->text(modemlist_l->currentItem())))
+ modemlist_l->removeItem(modemlist_l->currentItem());
+
+ emit resetmodems();
+ gpppdata.save();
+
+ slotListBoxSelect(modemlist_l->currentItem());
+
+}
+
+
+int ModemsWidget::doTab(){
+ tabWindow = new KDialogBase( KDialogBase::Tabbed, QString::null,
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok,
+ 0, 0, true);
+ KWin::setIcons(tabWindow->winId(), kapp->icon(), kapp->miniIcon());
+ bool isnewmodem;
+
+ if(gpppdata.modname().isEmpty()) {
+ tabWindow->setCaption(i18n("New Modem"));
+ isnewmodem = true;
+ } else {
+ QString tit = i18n("Edit Modem: ");
+ tit += gpppdata.modname();
+ tabWindow->setCaption(tit);
+ isnewmodem = false;
+ }
+ modem1 = new ModemWidget(tabWindow->addPage( i18n("&Device"), i18n("Serial Device")), isnewmodem );
+ modem2 = new ModemWidget2(tabWindow->addPage( i18n("&Modem"), i18n("Modem Settings")));
+ connect ( modem1->connectName(), SIGNAL(textChanged ( const QString & )), this, SLOT(modemNameChanged(const QString & )));
+
+ modemNameChanged(modem1->connectName()->text());
+ int result = 0;
+ bool ok = false;
+ while (!ok){
+
+ result = tabWindow->exec();
+ ok = true;
+
+ if(result == QDialog::Accepted) {
+ if(modem1->save()) {
+ modem2->save();
+ } else {
+ KMessageBox::error(this, i18n( "You must enter a unique\n"
+ "modem name"));
+ ok = false;
+ }
+ }
+ }
+
+ delete tabWindow;
+ return result;
+}
+
+void ModemsWidget::modemNameChanged(const QString & text)
+{
+ tabWindow->enableButtonOK( !text.isEmpty() );
+}
+
+QString ModemsWidget::prettyPrintVolume(unsigned int n) {
+ int idx = 0;
+ const QString quant[] = {i18n("Byte"), i18n("KB"),
+ i18n("MB"), i18n("GB"), QString::null};
+
+ float n1 = n;
+ while(n >= 1024 && !quant[idx].isNull()) {
+ idx++;
+ n /= 1024;
+ }
+
+ int i = idx;
+ while(i--)
+ n1 = n1 / 1024.0;
+
+ QString s = KGlobal::locale()->formatNumber( n1, idx==0 ? 0 : 1 );
+ s += " " + quant[idx];
+ return s;
+}
+
+#include "modems.moc"
diff --git a/kppp/modems.h b/kppp/modems.h
new file mode 100644
index 00000000..67d0c6fd
--- /dev/null
+++ b/kppp/modems.h
@@ -0,0 +1,92 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ *
+ *
+ *
+ * Copyright (C) 2004 Simone Gotti
+ * <simone.gotti@email.it>
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MODEMS_H_
+#define _MODEMS_H_
+
+#include <qwidget.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include "acctselect.h"
+
+class KDialogBase;
+class QCheckBox;
+class QLineEdit;
+class QTabDialog;
+class DialWidget;
+class ScriptWidget;
+class IPWidget;
+class DNSWidget;
+class GatewayWidget;
+
+class ModemsWidget : public QWidget {
+ Q_OBJECT
+public:
+ ModemsWidget( QWidget *parent=0, const char *name=0 );
+ ~ModemsWidget() {}
+
+private slots:
+ void editmodem();
+ void copymodem();
+ void newmodem();
+ void deletemodem();
+ void slotListBoxSelect(int);
+ void modemNameChanged(const QString &);
+
+private:
+ int doTab();
+
+signals:
+ void resetmodems();
+
+private:
+ QString prettyPrintVolume(unsigned int);
+
+ KDialogBase *tabWindow;
+ ModemWidget *modem1;
+ ModemWidget2 *modem2;
+ /* DialWidget *dial_w;
+ AccountingSelector *acct;
+ IPWidget *ip_w;
+ DNSWidget *dns_w;
+ GatewayWidget *gateway_w;
+ ScriptWidget *script_w;*/
+
+ QLabel *costlabel;
+ QLineEdit *costedit;
+ QLabel *vollabel;
+ QLineEdit *voledit;
+
+ QListBox *modemlist_l;
+ QPushButton *edit_b;
+ QPushButton *copy_b;
+ QPushButton *new_b;
+ QPushButton *delete_b;
+};
+
+#endif
+
diff --git a/kppp/newwidget.cpp b/kppp/newwidget.cpp
new file mode 100644
index 00000000..d2182fba
--- /dev/null
+++ b/kppp/newwidget.cpp
@@ -0,0 +1,17 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// functions generating layout-aware widgets
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#include "newwidget.h"
+
+QLineEdit *newLineEdit(int visiblewidth, QWidget *parent) {
+ QLineEdit *l = new QLineEdit(parent);
+ if(visiblewidth == 0)
+ l->setMinimumWidth(l->sizeHint().width());
+ else
+ l->setFixedWidth(l->fontMetrics().width('H') * visiblewidth);
+
+ return l;
+}
diff --git a/kppp/newwidget.h b/kppp/newwidget.h
new file mode 100644
index 00000000..52f2ec76
--- /dev/null
+++ b/kppp/newwidget.h
@@ -0,0 +1,19 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// functions generating layout-aware widgets
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef __NEWWIDGET__H__
+#define __NEWWIDGET__H__
+
+#include <qwidget.h>
+#include <qlineedit.h>
+
+#define L_FIXEDW 1
+#define L_FIXEDH 2
+#define L_FIXED (L_FIXEDW | L_FIXEDH)
+
+QLineEdit *newLineEdit(int visiblewidth, QWidget *parent);
+
+#endif
diff --git a/kppp/opener.cpp b/kppp/opener.cpp
new file mode 100644
index 00000000..be72f631
--- /dev/null
+++ b/kppp/opener.cpp
@@ -0,0 +1,722 @@
+
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997,98 Bernd Johannes Wuebben,
+ * Mario Weilguni
+ * Copyright (C) 1998-2002 Harri Porten <porten@kde.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* A note to developers:
+ *
+ * Apart from the first dozen lines in main() the following code represents
+ * the setuid root part of kppp. So please be careful !
+ * o restrain from using X, Qt or KDE library calls
+ * o check for possible buffer overflows
+ * o handle requests from the parent process with care. They might be forged.
+ * o be paranoid and think twice about everything you change.
+ */
+
+#include <config.h>
+
+#if defined(__osf__) || defined(__svr4__)
+#define _POSIX_PII_SOCKET
+extern "C" int sethostname(char *name, int name_len);
+#if !defined(__osf__)
+extern "C" int _Psendmsg(int, void*, int);
+extern "C" int _Precvmsg(int, void*, int);
+#endif
+#endif
+
+#include "kpppconfig.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+
+
+#include <netinet/in.h>
+
+#ifdef __FreeBSD__
+# include <sys/linker.h> // for kldload
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef HAVE_NET_IF_PPP_H
+# if defined(__DragonFly__)
+# include <net/ppp_layer/ppp_defs.h>
+# include <net/if.h>
+# include <net/ppp/if_ppp.h>
+# elif defined HAVE_LINUX_IF_PPP_H
+# include <linux/if_ppp.h>
+# endif
+#else
+# include <net/ppp_defs.h>
+# include <net/if.h>
+# include <net/if_ppp.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+
+#include "opener.h"
+#include "devices.h"
+
+#ifdef HAVE_RESOLV_H
+# include <arpa/nameser.h>
+# include <resolv.h>
+#endif
+
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+#ifdef _XPG4_2
+extern "C" {
+ ssize_t recvmsg(int, struct msghdr *, int);
+ ssize_t sendmsg(int, const struct msghdr *, int);
+}
+#endif
+
+#define MY_ASSERT(x) if (!(x)) { \
+ fprintf(stderr, "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
+ exit(1); }
+
+#define MY_DEBUG
+#ifndef MY_DEBUG
+#define Debug(s) ((void)0);
+#define Debug2(s, i) ((void)0);
+#else
+#define Debug(s) fprintf(stderr, (s "\n"));
+#define Debug2(s, i) fprintf(stderr, (s), (i));
+#endif
+
+static void sighandler_child(int);
+static pid_t pppdPid = -1;
+static int pppdExitStatus = -1;
+static int checkForInterface();
+
+// processing will stop at first file that could be opened successfully
+const char * const kppp_syslog[] = { "/var/log/syslog.ppp",
+ "/var/log/syslog",
+ "/var/log/messages",
+ 0 };
+
+Opener::Opener(int s) : socket(s), ttyfd(-1) {
+ lockfile[0] = '\0';
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGCHLD, sighandler_child);
+ mainLoop();
+}
+
+void Opener::mainLoop() {
+
+ int len;
+ int fd = -1;
+ int flags, mode;
+ const char *device, * const *logFile;
+ union AllRequests request;
+ struct ResponseHeader response;
+ struct msghdr msg;
+ struct iovec iov;
+
+ iov.iov_base = IOV_BASE_CAST &request;
+ iov.iov_len = sizeof(request);
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+
+ // loop forever
+ while(1) {
+ len = recvmsg(socket, &msg, 0);
+ if(len < 0) {
+ switch(errno) {
+ case EINTR:
+ Debug("Opener: interrupted system call, continuing");
+ break;
+ default:
+ perror("Opener: error reading from socket");
+ _exit(1);
+ }
+ } else {
+ switch(request.header.type) {
+
+ case OpenDevice:
+ Debug("Opener: received OpenDevice");
+ MY_ASSERT(len == sizeof(struct OpenModemRequest));
+ close(ttyfd);
+ device = deviceByIndex(request.modem.deviceNum);
+ response.status = 0;
+ if ((ttyfd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) == -1) {
+ Debug("error opening modem device !");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ sendFD(fd, &response);
+ close(fd);
+ } else
+ sendFD(ttyfd, &response);
+ break;
+
+ case OpenLock:
+ Debug("Opener: received OpenLock\n");
+ MY_ASSERT(len == sizeof(struct OpenLockRequest));
+ flags = request.lock.flags;
+ MY_ASSERT(flags == O_RDONLY || flags == O_WRONLY|O_TRUNC|O_CREAT);
+ if(flags == O_WRONLY|O_TRUNC|O_CREAT)
+ mode = 0644;
+ else
+ mode = 0;
+
+ device = deviceByIndex(request.lock.deviceNum);
+ MY_ASSERT(strlen(LOCK_DIR)+strlen(device) < MaxPathLen);
+ strlcpy(lockfile, LOCK_DIR"/LCK..", MaxPathLen);
+ strlcat(lockfile, strrchr(device, '/') + 1, MaxPathLen );
+ response.status = 0;
+ // TODO:
+ // struct stat st;
+ // if(stat(lockfile.data(), &st) == -1) {
+ // if(errno == EBADF)
+ // return -1;
+ // } else {
+ // // make sure that this is a regular file
+ // if(!S_ISREG(st.st_mode))
+ // return -1;
+ // }
+ if ((fd = open(lockfile, flags, mode)) == -1) {
+ Debug("error opening lockfile!");
+ lockfile[0] = '\0';
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ } else
+ fchown(fd, 0, 0);
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case RemoveLock:
+ Debug("Opener: received RemoveLock");
+ MY_ASSERT(len == sizeof(struct RemoveLockRequest));
+ close(ttyfd);
+ ttyfd = -1;
+ response.status = unlink(lockfile);
+ lockfile[0] = '\0';
+ sendResponse(&response);
+ break;
+
+ case OpenResolv:
+ Debug("Opener: received OpenResolv");
+ MY_ASSERT(len == sizeof(struct OpenResolvRequest));
+ flags = request.resolv.flags;
+ response.status = 0;
+ if ((fd = open(_PATH_RESCONF, flags)) == -1) {
+ Debug("error opening resolv.conf!");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ }
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case OpenSysLog:
+ Debug("Opener: received OpenSysLog");
+ MY_ASSERT(len == sizeof(struct OpenLogRequest));
+ response.status = 0;
+ logFile = &kppp_syslog[0];
+ while (*logFile) {
+ if ((fd = open(*logFile, O_RDONLY)) >= 0)
+ break;
+ logFile++;
+ }
+ if (!*logFile) {
+ Debug("No success opening a syslog file !");
+ fd = open(DEVNULL, O_RDONLY);
+ response.status = -errno;
+ }
+ sendFD(fd, &response);
+ close(fd);
+ break;
+
+ case SetSecret:
+ Debug("Opener: received SetSecret");
+ MY_ASSERT(len == sizeof(struct SetSecretRequest));
+ response.status = !createAuthFile(request.secret.method,
+ request.secret.username,
+ request.secret.password);
+ sendResponse(&response);
+ break;
+
+ case RemoveSecret:
+ Debug("Opener: received RemoveSecret");
+ MY_ASSERT(len == sizeof(struct RemoveSecretRequest));
+ response.status = !removeAuthFile(request.remove.method);
+ sendResponse(&response);
+ break;
+
+ case SetHostname:
+ Debug("Opener: received SetHostname");
+ MY_ASSERT(len == sizeof(struct SetHostnameRequest));
+ response.status = 0;
+ if(sethostname(request.host.name, strlen(request.host.name)))
+ response.status = -errno;
+ sendResponse(&response);
+ break;
+
+ case ExecPPPDaemon:
+ Debug("Opener: received ExecPPPDaemon");
+ MY_ASSERT(len == sizeof(struct ExecDaemonRequest));
+ response.status = execpppd(request.daemon.arguments);
+ sendResponse(&response);
+ break;
+
+ case KillPPPDaemon:
+ Debug("Opener: received KillPPPDaemon");
+ MY_ASSERT(len == sizeof(struct KillDaemonRequest));
+ response.status = killpppd();
+ sendResponse(&response);
+ break;
+
+ case PPPDExitStatus:
+ Debug("Opener: received PPPDExitStatus");
+ MY_ASSERT(len == sizeof(struct PPPDExitStatusRequest));
+ response.status = pppdExitStatus;
+ sendResponse(&response);
+ break;
+
+ case Stop:
+ Debug("Opener: received STOP command");
+ _exit(0);
+ break;
+
+ default:
+ Debug("Opener: unknown command type. Exiting ...");
+ _exit(1);
+ }
+ } // else
+ }
+}
+
+
+//
+// Send an open fd over a UNIX socket pair
+//
+int Opener::sendFD(int fd, struct ResponseHeader *response) {
+
+ struct { struct cmsghdr cmsg; int fd; } control;
+ struct msghdr msg;
+ struct iovec iov;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ // Send data
+ iov.iov_base = IOV_BASE_CAST response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+
+ // Send a (duplicate of) the file descriptor
+ control.cmsg.cmsg_len = sizeof(struct cmsghdr) + sizeof(int);
+ control.cmsg.cmsg_level = SOL_SOCKET;
+ control.cmsg.cmsg_type = MY_SCM_RIGHTS;
+
+ msg.msg_control = (char *) &control;
+ msg.msg_controllen = control.cmsg.cmsg_len;
+
+#ifdef CMSG_DATA
+ *((int *)CMSG_DATA(&control.cmsg)) = fd;
+#else
+ *((int *) &control.cmsg.cmsg_data) = fd;
+#endif
+
+ if (sendmsg(socket, &msg, 0) < 0) {
+ perror("unable to send file descriptors");
+ return -1;
+ }
+
+ return 0;
+}
+
+int Opener::sendResponse(struct ResponseHeader *response) {
+
+ struct msghdr msg;
+ struct iovec iov;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+
+ // Send data
+ iov.iov_base = IOV_BASE_CAST response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+
+ if (sendmsg(socket, &msg, 0) < 0) {
+ perror("unable to send response");
+ return -1;
+ }
+
+ return 0;
+}
+
+const char* Opener::deviceByIndex(int idx) {
+
+ const char *device = 0L;
+
+ for(int i = 0; devices[i]; i++)
+ if(i == idx)
+ device = devices[i];
+ MY_ASSERT(device);
+ return device;
+}
+
+bool Opener::createAuthFile(Auth method, char *username, char *password) {
+ const char *authfile, *oldName, *newName;
+ char line[100];
+ char regexp[2*MaxStrLen+30];
+ regex_t preg;
+
+ if(!(authfile = authFile(method)))
+ return false;
+
+ if(!(newName = authFile(method, New)))
+ return false;
+
+ // look for username, "username" or 'username'
+ // if you modify this RE you have to adapt regexp's size above
+ snprintf(regexp, sizeof(regexp), "^[ \t]*%s[ \t]\\|^[ \t]*[\"\']%s[\"\']",
+ username,username);
+ MY_ASSERT(regcomp(&preg, regexp, 0) == 0);
+
+ // copy to new file pap- or chap-secrets
+ int old_umask = umask(0077);
+ FILE *fout = fopen(newName, "w");
+ if(fout) {
+ // copy old file
+ FILE *fin = fopen(authfile, "r");
+ if(fin) {
+ while(fgets(line, sizeof(line), fin)) {
+ if(regexec(&preg, line, 0, 0L, 0) == 0)
+ continue;
+ fputs(line, fout);
+ }
+ fclose(fin);
+ }
+
+ // append user/pass pair
+ fprintf(fout, "\"%s\"\t*\t\"%s\"\n", username, password);
+ fclose(fout);
+ }
+
+ // restore umask
+ umask(old_umask);
+
+ // free memory allocated by regcomp
+ regfree(&preg);
+
+ if(!(oldName = authFile(method, Old)))
+ return false;
+
+ // delete old file if any
+ unlink(oldName);
+
+ rename(authfile, oldName);
+ rename(newName, authfile);
+
+ return true;
+}
+
+
+bool Opener::removeAuthFile(Auth method) {
+ const char *authfile, *oldName;
+
+ if(!(authfile = authFile(method)))
+ return false;
+ if(!(oldName = authFile(method, Old)))
+ return false;
+
+ if(access(oldName, F_OK) == 0) {
+ unlink(authfile);
+ return (rename(oldName, authfile) == 0);
+ } else
+ return false;
+}
+
+
+const char* Opener::authFile(Auth method, int version) {
+ switch(method|version) {
+ case PAP|Original:
+ return PAP_AUTH_FILE;
+ break;
+ case PAP|New:
+ return PAP_AUTH_FILE".new";
+ break;
+ case PAP|Old:
+ return PAP_AUTH_FILE".old";
+ break;
+ case CHAP|Original:
+ return CHAP_AUTH_FILE;
+ break;
+ case CHAP|New:
+ return CHAP_AUTH_FILE".new";
+ break;
+ case CHAP|Old:
+ return CHAP_AUTH_FILE".old";
+ break;
+ default:
+ return 0L;
+ }
+}
+
+
+bool Opener::execpppd(const char *arguments) {
+ char buf[MAX_CMDLEN];
+ char *args[MaxArgs];
+ pid_t pgrpid;
+
+ if(ttyfd<0)
+ return false;
+
+ pppdExitStatus = -1;
+
+ switch(pppdPid = fork())
+ {
+ case -1:
+ fprintf(stderr,"In parent: fork() failed\n");
+ return false;
+ break;
+
+ case 0:
+ // let's parse the arguments the user supplied into UNIX suitable form
+ // that is a list of pointers each pointing to exactly one word
+ strlcpy(buf, arguments, sizeof(buf));
+ parseargs(buf, args);
+ // become a session leader and let /dev/ttySx
+ // be the controlling terminal.
+ pgrpid = setsid();
+#ifdef TIOCSCTTY
+ if(ioctl(ttyfd, TIOCSCTTY, 0)<0)
+ fprintf(stderr, "ioctl() failed.\n");
+#elif defined (TIOCSPGRP)
+ if(ioctl(ttyfd, TIOCSPGRP, &pgrpid)<0)
+ fprintf(stderr, "ioctl() failed.\n");
+#endif
+ if(tcsetpgrp(ttyfd, pgrpid)<0)
+ fprintf(stderr, "tcsetpgrp() failed.\n");
+
+ dup2(ttyfd, 0);
+ dup2(ttyfd, 1);
+
+ switch (checkForInterface()) {
+ case 1:
+ fprintf(stderr, "Cannot determine if kernel supports ppp.\n");
+ break;
+ case -1:
+ fprintf(stderr, "Kernel does not support ppp, oops.\n");
+ break;
+ case 0:
+ fprintf(stderr, "Kernel supports ppp alright.\n");
+ break;
+ }
+
+ execve(pppdPath(), args, 0L);
+ _exit(0);
+ break;
+
+ default:
+ Debug2("In parent: pppd pid %d\n",pppdPid);
+ close(ttyfd);
+ ttyfd = -1;
+ return true;
+ break;
+ }
+}
+
+
+bool Opener::killpppd()const {
+ if(pppdPid > 0) {
+ Debug2("In killpppd(): Sending SIGTERM to %d\n", pppdPid);
+ if(kill(pppdPid, SIGTERM) < 0) {
+ Debug2("Error terminating %d. Sending SIGKILL\n", pppdPid);
+ if(kill(pppdPid, SIGKILL) < 0) {
+ Debug2("Error killing %d\n", pppdPid);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+void Opener::parseargs(char* buf, char** args) {
+ int nargs = 0;
+ int quotes;
+
+ while(nargs < MaxArgs-1 && *buf != '\0') {
+
+ quotes = 0;
+
+ // Strip whitespace. Use nulls, so that the previous argument is
+ // terminated automatically.
+
+ while ((*buf == ' ' ) || (*buf == '\t' ) || (*buf == '\n' ) )
+ *buf++ = '\0';
+
+ // detect begin of quoted argument
+ if (*buf == '"' || *buf == '\'') {
+ quotes = *buf;
+ *buf++ = '\0';
+ }
+
+ // save the argument
+ if(*buf != '\0') {
+ *args++ = buf;
+ nargs++;
+ }
+
+ if (!quotes)
+ while ((*buf != '\0') && (*buf != '\n') &&
+ (*buf != '\t') && (*buf != ' '))
+ buf++;
+ else {
+ while ((*buf != '\0') && (*buf != quotes))
+ buf++;
+ *buf++ = '\0';
+ }
+ }
+
+ *args = 0L;
+}
+
+
+const char* pppdPath() {
+ // wasting a few bytes
+ static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)];
+ static char *pppdPath = 0L;
+ char *p;
+
+ if(pppdPath == 0L) {
+ const char *c = PPPDSEARCHPATH;
+ while(*c != '\0') {
+ while(*c == ':')
+ c++;
+ p = buffer;
+ while(*c != '\0' && *c != ':')
+ *p++ = *c++;
+ *p = '\0';
+ strcat(p, "/");
+ strcat(p, PPPDNAME);
+ if(access(buffer, F_OK) == 0)
+ return (pppdPath = buffer);
+ }
+ }
+
+ return pppdPath;
+}
+
+int checkForInterface()
+{
+// I don't know if Linux needs more initialization to get the ioctl to
+// work, pppd seems to hint it does. But BSD doesn't, and the following
+// code should compile.
+#if (defined(HAVE_NET_IF_PPP_H) || defined(HAVE_LINUX_IF_PPP_H)) && !defined(__svr4__)
+ int s, ok;
+ struct ifreq ifr;
+ // extern char *no_ppp_msg;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return 1; /* can't tell */
+
+ strlcpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+ close(s);
+
+ if (ok == -1) {
+// This is ifdef'd FreeBSD, because FreeBSD is the only BSD that supports
+// KLDs, the old LKM interface couldn't handle loading devices
+// dynamically, and thus can't load ppp support on the fly
+#ifdef __FreeBSD__
+ // If we failed to load ppp support and don't have it already.
+ if (kldload("if_ppp") == -1) {
+ return -1;
+ }
+ return 0;
+#else
+ return -1;
+#endif
+ }
+ return 0;
+#else
+// We attempt to use the SunOS/SysVr4 method and stat /dev/ppp
+ struct stat buf;
+
+ memset(&buf, 0, sizeof(buf));
+ return stat("/dev/ppp", &buf);
+#endif
+}
+
+
+void sighandler_child(int) {
+ pid_t pid;
+ int status;
+
+ signal(SIGCHLD, sighandler_child);
+ if(pppdPid>0) {
+ pid = waitpid(pppdPid, &status, WNOHANG);
+ if(pid != pppdPid) {
+ fprintf(stderr, "received SIGCHLD from unknown origin.\n");
+ } else {
+ Debug("It was pppd that died");
+ pppdPid = -1;
+ if((WIFEXITED(status))) {
+ pppdExitStatus = (WEXITSTATUS(status));
+ Debug2("pppd exited with return value %d\n", pppdExitStatus);
+ } else {
+ pppdExitStatus = 99;
+ Debug("pppd exited abnormally.");
+ }
+ Debug2("Sending %i a SIGUSR1\n", getppid());
+ kill(getppid(), SIGUSR1);
+ }
+ } else
+ fprintf(stderr, "received unexpected SIGCHLD.\n");
+}
diff --git a/kppp/opener.h b/kppp/opener.h
new file mode 100644
index 00000000..812ec9ba
--- /dev/null
+++ b/kppp/opener.h
@@ -0,0 +1,147 @@
+#ifndef _FILEOPENER_H_
+#define _FILEOPENER_H_
+
+#define DEVNULL "/dev/null"
+
+// workaround for bug in glibc on RedHat 5.0 and Debian 2.1
+#if defined(__GLIBC__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0 && defined(__linux__))
+# define MY_SCM_RIGHTS 1
+#else
+# define MY_SCM_RIGHTS SCM_RIGHTS
+#endif
+
+// ### add by bhughes - FreeBSD defines 'BSD' in sys/param.h
+#include <sys/param.h>
+
+#if defined(BSD) || defined(__svr4__)
+# define IOV_BASE_CAST (char *)
+#else
+# define IOV_BASE_CAST (void *)
+#endif
+
+const char *pppdPath();
+
+class Opener {
+
+public:
+ Opener(int);
+ ~Opener();
+
+ enum { OpenDevice = 1,
+ OpenLock, RemoveLock,
+ OpenResolv,
+ OpenSysLog,
+ SetSecret, RemoveSecret,
+ SetHostname,
+ ExecPPPDaemon, KillPPPDaemon,
+ PPPDExitStatus,
+ Stop };
+ enum Auth { PAP = 1, CHAP };
+ enum { MaxPathLen = 30, MaxStrLen = 40, MaxArgs = 100 };
+
+private:
+ enum { Original=0x100, New=0x200, Old=0x400 } Version;
+ void mainLoop();
+ int sendFD(int ttyfd, struct ResponseHeader *response);
+ int sendResponse(struct ResponseHeader *response);
+ const char *deviceByIndex(int idx);
+ bool createAuthFile(Auth method, char *username, char *password);
+ bool removeAuthFile(Auth method);
+ const char* authFile(Auth method, int version = Original);
+ bool execpppd(const char *arguments);
+ bool killpppd()const;
+ void parseargs(char* buf, char** args);
+
+ int socket;
+ int ttyfd;
+ char lockfile[MaxPathLen+1];
+};
+
+
+struct RequestHeader {
+ int type;
+ int len;
+ // int id; // TODO: Use a transmission id and check whether
+ // response matches request
+};
+
+struct ResponseHeader {
+ int status; /* 0 or errno */
+ // int id;
+};
+
+struct OpenModemRequest {
+ struct RequestHeader header;
+ int deviceNum;
+};
+
+struct RemoveLockRequest {
+ struct RequestHeader header;
+};
+
+struct OpenLockRequest {
+ struct RequestHeader header;
+ int deviceNum;
+ int flags;
+};
+
+struct OpenResolvRequest {
+ struct RequestHeader header;
+ int flags;
+};
+
+struct OpenLogRequest {
+ struct RequestHeader header;
+};
+
+struct SetSecretRequest {
+ struct RequestHeader header;
+ Opener::Auth method; // PAP or CHAP
+ char username[Opener::MaxStrLen+1];
+ char password[Opener::MaxStrLen+1];
+};
+
+struct RemoveSecretRequest {
+ struct RequestHeader header;
+ Opener::Auth method; // PAP or CHAP
+};
+
+struct SetHostnameRequest {
+ struct RequestHeader header;
+ char name[Opener::MaxStrLen+1];
+};
+
+struct ExecDaemonRequest {
+ struct RequestHeader header;
+ char arguments[MAX_CMDLEN+1];
+};
+
+struct KillDaemonRequest {
+ struct RequestHeader header;
+};
+
+struct PPPDExitStatusRequest {
+ struct RequestHeader header;
+};
+
+struct StopRequest {
+ struct RequestHeader header;
+};
+
+union AllRequests {
+ struct RequestHeader header;
+ struct OpenModemRequest modem;
+ struct OpenLockRequest lock;
+ struct RemoveLockRequest unlock;
+ struct OpenResolvRequest resolv;
+ struct SetSecretRequest secret;
+ struct RemoveSecretRequest remove;
+ struct SetHostnameRequest host;
+ struct OpenLogRequest log;
+ struct ExecDaemonRequest daemon;
+ struct ExecDaemonRequest kill;
+ struct PPPDExitStatusRequest status;
+ struct StopRequest stop;
+};
+
+#endif
diff --git a/kppp/pixmaps/Makefile.am b/kppp/pixmaps/Makefile.am
new file mode 100644
index 00000000..f7a89316
--- /dev/null
+++ b/kppp/pixmaps/Makefile.am
@@ -0,0 +1,10 @@
+
+# add here all files
+# leaving out kppplogo.png until it gets used
+emo_DATA = folder.png phone.png \
+ dock_both.png dock_left.png dock_none.png dock_right.png \
+ modemboth.png modemleft.png modemnone.png modemright.png
+
+emodir = $(kde_datadir)/kppp/pics
+
+EXTRA_DIST = $(emo_DATA)
diff --git a/kppp/pixmaps/dock_both.png b/kppp/pixmaps/dock_both.png
new file mode 100644
index 00000000..55ea265e
--- /dev/null
+++ b/kppp/pixmaps/dock_both.png
Binary files differ
diff --git a/kppp/pixmaps/dock_left.png b/kppp/pixmaps/dock_left.png
new file mode 100644
index 00000000..79811c5f
--- /dev/null
+++ b/kppp/pixmaps/dock_left.png
Binary files differ
diff --git a/kppp/pixmaps/dock_none.png b/kppp/pixmaps/dock_none.png
new file mode 100644
index 00000000..0656a555
--- /dev/null
+++ b/kppp/pixmaps/dock_none.png
Binary files differ
diff --git a/kppp/pixmaps/dock_right.png b/kppp/pixmaps/dock_right.png
new file mode 100644
index 00000000..fcd62180
--- /dev/null
+++ b/kppp/pixmaps/dock_right.png
Binary files differ
diff --git a/kppp/pixmaps/folder.png b/kppp/pixmaps/folder.png
new file mode 100644
index 00000000..e79a1b51
--- /dev/null
+++ b/kppp/pixmaps/folder.png
Binary files differ
diff --git a/kppp/pixmaps/kppplogo.png b/kppp/pixmaps/kppplogo.png
new file mode 100644
index 00000000..d5bb21d7
--- /dev/null
+++ b/kppp/pixmaps/kppplogo.png
Binary files differ
diff --git a/kppp/pixmaps/modemboth.png b/kppp/pixmaps/modemboth.png
new file mode 100644
index 00000000..a6024ec5
--- /dev/null
+++ b/kppp/pixmaps/modemboth.png
Binary files differ
diff --git a/kppp/pixmaps/modemleft.png b/kppp/pixmaps/modemleft.png
new file mode 100644
index 00000000..c88d7ae8
--- /dev/null
+++ b/kppp/pixmaps/modemleft.png
Binary files differ
diff --git a/kppp/pixmaps/modemnone.png b/kppp/pixmaps/modemnone.png
new file mode 100644
index 00000000..f10d0089
--- /dev/null
+++ b/kppp/pixmaps/modemnone.png
Binary files differ
diff --git a/kppp/pixmaps/modemright.png b/kppp/pixmaps/modemright.png
new file mode 100644
index 00000000..c3f87cf0
--- /dev/null
+++ b/kppp/pixmaps/modemright.png
Binary files differ
diff --git a/kppp/pixmaps/phone.png b/kppp/pixmaps/phone.png
new file mode 100644
index 00000000..ca416931
--- /dev/null
+++ b/kppp/pixmaps/phone.png
Binary files differ
diff --git a/kppp/pppdargs.cpp b/kppp/pppdargs.cpp
new file mode 100644
index 00000000..ce3f22e6
--- /dev/null
+++ b/kppp/pppdargs.cpp
@@ -0,0 +1,171 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qlayout.h>
+#include <kbuttonbox.h>
+#include <kwin.h>
+#include <kapplication.h>
+#include "pppdargs.h"
+#include "pppdata.h"
+#include <klocale.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlabel.h>
+#include <kstdguiitem.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+PPPdArguments::PPPdArguments(QWidget *parent, const char *name)
+ : QDialog(parent, name, TRUE)
+{
+ setCaption(i18n("Customize pppd Arguments"));
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+ QVBoxLayout *l = new QVBoxLayout(this, 10, 10);
+ QHBoxLayout *tl = new QHBoxLayout(10);
+ l->addLayout(tl);
+ QVBoxLayout *l1 = new QVBoxLayout();
+ QVBoxLayout *l2 = new QVBoxLayout();
+ tl->addLayout(l1, 1);
+ tl->addLayout(l2, 0);
+
+ QHBoxLayout *l11 = new QHBoxLayout(10);
+ l1->addLayout(l11);
+
+ argument_label = new QLabel(i18n("Arg&ument:"), this);
+ l11->addWidget(argument_label);
+
+ argument = new QLineEdit(this);
+ argument_label->setBuddy(argument);
+ connect(argument, SIGNAL(returnPressed()),
+ SLOT(addbutton()));
+ l11->addWidget(argument);
+ connect(argument, SIGNAL(textChanged(const QString &)),
+ this, SLOT(textChanged(const QString &)));
+
+ arguments = new QListBox(this);
+ arguments->setMinimumSize(1, fontMetrics().lineSpacing()*10);
+ connect(arguments, SIGNAL(highlighted(int)),
+ this, SLOT(itemSelected(int)));
+ l1->addWidget(arguments, 1);
+
+ add = new QPushButton(i18n("&Add"), this);
+ connect(add, SIGNAL(clicked()), SLOT(addbutton()));
+ l2->addWidget(add);
+ l2->addStretch(1);
+
+ remove = new QPushButton(i18n("&Remove"), this);
+ connect(remove, SIGNAL(clicked()), SLOT(removebutton()));
+ l2->addWidget(remove);
+
+ defaults = new KPushButton(KStdGuiItem::defaults(), this);
+ connect(defaults, SIGNAL(clicked()), SLOT(defaultsbutton()));
+ l2->addWidget(defaults);
+
+ l->addSpacing(5);
+
+ KButtonBox *bbox = new KButtonBox(this);
+ bbox->addStretch(1);
+ closebtn = bbox->addButton(KStdGuiItem::ok());
+ connect(closebtn, SIGNAL(clicked()), SLOT(closebutton()));
+ QPushButton *cancel = bbox->addButton(KStdGuiItem::cancel());
+ connect(cancel, SIGNAL(clicked()),
+ this, SLOT(reject()));
+ bbox->layout();
+ l->addWidget(bbox);
+
+ setFixedSize(sizeHint());
+
+ //load info from gpppdata
+ init();
+
+ add->setEnabled(false);
+ remove->setEnabled(false);
+ argument->setFocus();
+}
+
+
+void PPPdArguments::addbutton() {
+ if(!argument->text().isEmpty() && arguments->count() < MAX_PPPD_ARGUMENTS) {
+ arguments->insertItem(argument->text());
+ argument->setText("");
+ }
+}
+
+
+void PPPdArguments::removebutton() {
+ if(arguments->currentItem() >= 0)
+ arguments->removeItem(arguments->currentItem());
+}
+
+
+void PPPdArguments::defaultsbutton() {
+ // all of this is a hack
+ // save current list
+ QStringList arglist(gpppdata.pppdArgument());
+
+ // get defaults
+ gpppdata.setpppdArgumentDefaults();
+ init();
+
+ // restore old list
+ gpppdata.setpppdArgument(arglist);
+}
+
+
+void PPPdArguments::closebutton() {
+ QStringList arglist;
+ for(uint i=0; i < arguments->count(); i++)
+ arglist.append(arguments->text(i));
+ gpppdata.setpppdArgument(arglist);
+
+ done(0);
+}
+
+
+void PPPdArguments::init() {
+ while(arguments->count())
+ arguments->removeItem(0);
+
+ QStringList &arglist = gpppdata.pppdArgument();
+ for ( QStringList::Iterator it = arglist.begin();
+ it != arglist.end();
+ ++it )
+ arguments->insertItem(*it);
+}
+
+
+void PPPdArguments::textChanged(const QString &s) {
+ add->setEnabled(s.length() > 0);
+}
+
+
+void PPPdArguments::itemSelected(int idx) {
+ remove->setEnabled(idx != -1);
+}
+
+#include "pppdargs.moc"
diff --git a/kppp/pppdargs.h b/kppp/pppdargs.h
new file mode 100644
index 00000000..43daa017
--- /dev/null
+++ b/kppp/pppdargs.h
@@ -0,0 +1,79 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PPPDARGS_H_
+#define _PPPDARGS_H_
+
+#include <qdialog.h>
+
+class QListBox;
+class QLabel;
+class QPushButton;
+class QLineEdit;
+class KPushButton;
+
+
+class PPPdArguments : public QDialog {
+Q_OBJECT
+public:
+ PPPdArguments(QWidget *parent=0, const char *name=0);
+ ~PPPdArguments() {}
+
+private slots:
+ void addbutton();
+ void removebutton();
+ void defaultsbutton();
+ void closebutton();
+ void textChanged(const QString &);
+ void itemSelected(int);
+
+private:
+ void init();
+
+ QLabel *argument_label;
+
+ QLineEdit *argument;
+
+ QPushButton *add;
+ QPushButton *remove;
+ KPushButton *defaults;
+
+ QListBox *arguments;
+
+ QPushButton *closebtn;
+};
+#endif
+
+
+
+
+
+
+
+
+
+
diff --git a/kppp/pppdata.cpp b/kppp/pppdata.cpp
new file mode 100644
index 00000000..80bd79be
--- /dev/null
+++ b/kppp/pppdata.cpp
@@ -0,0 +1,1486 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pppdata.h"
+#include "runtests.h"
+#include "devices.h"
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kglobalsettings.h>
+#include <assert.h>
+
+PPPData gpppdata;
+
+
+PPPData::PPPData()
+ : config(0L),
+ accounthighcount(-1), // start out with no account entries
+ caccount(-1), // set the current account index also
+ modemhighcount(-1), // start out with no modem entries
+ cmodem(-1), // set the current modem index also
+ suidprocessid(-1), // process ID of setuid child
+ pppdisrunning(false),
+ pppderror(0),
+ waitcallback(false)
+{
+}
+
+
+//
+// open configuration file
+//
+bool PPPData::open() {
+
+ config = kapp->config();
+
+ if (config->getConfigState() == KConfig::NoAccess) {
+ KMessageBox::error(0L,
+ i18n("The application-specific config file could not "
+ "be opened in either read-write or read-only mode.\n"
+ "The superuser might have to change its ownership "
+ "by issuing the following command in your home directory:\n"
+ "chown {YourUsername} .kde/share/config/kppprc"),
+ kapp->name());
+ return false;
+ }
+
+ // don't expand shell variables
+ config->setDollarExpansion(false);
+
+ accounthighcount = readNumConfig(GENERAL_GRP, NUMACCOUNTS_KEY, 0) - 1;
+
+ if (accounthighcount > MAX_ACCOUNTS)
+ accounthighcount = MAX_ACCOUNTS;
+
+ if(accounthighcount >= 0 && defaultAccount().isEmpty()) {
+ setAccountByIndex(0);
+ setDefaultAccount(accname());
+ } else if(!setAccount(defaultAccount()))
+ setDefaultAccount(accname());
+
+ modemhighcount = readNumConfig(GENERAL_GRP, NUMMODEMS_KEY, 0) - 1;
+
+ if (modemhighcount > MAX_MODEMS)
+ modemhighcount = MAX_MODEMS;
+
+ // if there aren't no ModemX setted and exists the [Modem] group,
+ // probably it's the first time we are using this new version
+ // with multiple modem profiles.
+ // So we copy the old [Modem] to the new [Modem0]
+ if(modemhighcount < 0 && defaultModem().isEmpty() && config->hasGroup("Modem"))
+ {
+ config->setGroup("Modem");
+
+ QMap <QString, QString> map = config->entryMap("Modem");
+ QMap <QString, QString>::ConstIterator it = map.begin();
+
+ newmodem();
+
+ while (it != map.end()) {
+ config->setGroup(cmodemgroup);
+ config->writeEntry(it.key(), *it);
+ it++;
+ }
+
+ QString newname("Modem0");
+ setModname(newname);
+ }
+
+ if(modemhighcount >= 0 && defaultModem().isEmpty()) {
+ setModemByIndex(0);
+ setDefaultModem(modname());
+ } else if(!setModem(defaultModem()))
+ setDefaultModem(modname());
+
+
+ // start out with internal debugging disabled
+ // the user is still free to specify `debug' on his own
+ setPPPDebug(false);
+
+ ::pppdVersion(&pppdVer, &pppdMod, &pppdPatch);
+
+ return true;
+}
+
+
+//
+// save configuration
+//
+void PPPData::save() {
+
+ if (config) {
+ writeConfig(GENERAL_GRP, NUMACCOUNTS_KEY, accountCount());
+ writeConfig(GENERAL_GRP, NUMMODEMS_KEY, modemCount());
+ config->sync();
+ }
+
+}
+
+
+//
+// cancel changes
+//
+void PPPData::cancel() {
+
+ if (config) {
+ config->rollback();
+ config->reparseConfiguration();
+ }
+
+}
+
+
+// currently differentiates between READWRITE and NONE only
+int PPPData::access() const {
+
+ return config->getConfigState();
+}
+
+
+// functions to read/write date to configuration file
+QString PPPData::readConfig(const QString &group, const QString &key,
+ const QString &defvalue = "")
+{
+ if (config) {
+ config->setGroup(group);
+ return config->readEntry(key, defvalue);
+ } else
+ return defvalue;
+}
+
+
+int PPPData::readNumConfig(const QString &group, const QString &key,
+ int defvalue) {
+ if (config) {
+ config->setGroup(group);
+ return config->readNumEntry(key, defvalue);
+ } else
+ return defvalue;
+
+}
+
+
+bool PPPData::readListConfig(const QString &group, const QString &key,
+ QStringList &list, char sep) {
+ list.clear();
+ if (config) {
+ config->setGroup(group);
+ list = config->readListEntry(key, sep);
+ return true;
+ } else
+ return false;
+}
+
+
+void PPPData::writeConfig(const QString &group, const QString &key,
+ const QString &value) {
+ if (config) {
+ config->setGroup(group);
+ config->writeEntry(key, value);
+ }
+}
+
+
+void PPPData::writeConfig(const QString &group, const QString &key, int value) {
+ if (config) {
+ config->setGroup(group);
+ config->writeEntry(key, value);
+ }
+}
+
+
+void PPPData::writeListConfig(const QString &group, const QString &key,
+ QStringList &list, char sep) {
+ if (config) {
+ config->setGroup(group);
+ config->writeEntry(key, list, sep);
+ }
+}
+
+
+//
+// functions to set/return general information
+//
+QString PPPData::password() const {
+ return passwd;
+}
+
+
+void PPPData::setPassword(const QString &pw) {
+ passwd = pw;
+}
+
+
+const QString PPPData::defaultAccount() {
+ return readConfig(GENERAL_GRP, DEFAULTACCOUNT_KEY);
+}
+
+
+void PPPData::setDefaultAccount(const QString &n) {
+ writeConfig(GENERAL_GRP, DEFAULTACCOUNT_KEY, n);
+
+ //now set the current account index to the default account
+ setAccount(defaultAccount());
+}
+
+
+const QString PPPData::defaultModem() {
+ return readConfig(GENERAL_GRP, DEFAULTMODEM_KEY);
+}
+
+
+void PPPData::setDefaultModem(const QString &n) {
+ writeConfig(GENERAL_GRP, DEFAULTMODEM_KEY, n);
+
+ //now set the current modem index to the default modem
+ setModem(defaultModem());
+}
+
+bool PPPData::get_show_clock_on_caption() {
+ return (bool) readNumConfig(GENERAL_GRP, SHOWCLOCK_KEY, true);
+}
+
+
+void PPPData::set_show_clock_on_caption(bool set) {
+ writeConfig(GENERAL_GRP, SHOWCLOCK_KEY, (int) set);
+}
+
+
+bool PPPData::get_xserver_exit_disconnect() {
+ return (bool) readNumConfig(GENERAL_GRP, DISCONNECT_KEY, true);
+}
+
+bool PPPData::get_redial_on_nocarrier() {
+ return (bool) readNumConfig(GENERAL_GRP, REDIALONNOCARR_KEY, false);
+}
+
+
+void PPPData::setPPPDebug(bool set) {
+ writeConfig(GENERAL_GRP, PPP_DEBUG_OPTION, (int)set);
+}
+
+
+bool PPPData::getPPPDebug() {
+ return (bool)readNumConfig(GENERAL_GRP, PPP_DEBUG_OPTION, false);
+}
+
+
+void PPPData::set_xserver_exit_disconnect(bool set) {
+ writeConfig(GENERAL_GRP, DISCONNECT_KEY, (int) set);
+}
+
+void PPPData::set_redial_on_nocarrier(bool set) {
+ writeConfig(GENERAL_GRP, REDIALONNOCARR_KEY, (int) set);
+}
+
+
+bool PPPData::quit_on_disconnect() {
+ return (bool) readNumConfig(GENERAL_GRP, QUITONDISCONNECT_KEY, false);
+}
+
+
+void PPPData::set_quit_on_disconnect(bool set) {
+ writeConfig(GENERAL_GRP, QUITONDISCONNECT_KEY, (int) set);
+}
+
+
+bool PPPData::get_show_log_window() {
+ return (bool) readNumConfig (GENERAL_GRP, SHOWLOGWIN_KEY, false);
+}
+
+
+void PPPData::set_show_log_window(bool set) {
+ writeConfig(GENERAL_GRP, SHOWLOGWIN_KEY, (int) set);
+}
+
+
+bool PPPData::automatic_redial() {
+ return (bool) readNumConfig(GENERAL_GRP, AUTOREDIAL_KEY, FALSE);
+}
+
+
+void PPPData::set_automatic_redial(bool set) {
+ writeConfig(GENERAL_GRP, AUTOREDIAL_KEY, (int) set);
+}
+
+
+bool PPPData::get_iconify_on_connect() {
+ return (bool) readNumConfig(GENERAL_GRP, ICONIFY_ON_CONNECT_KEY, TRUE);
+}
+
+
+void PPPData::set_iconify_on_connect(bool set) {
+ writeConfig(GENERAL_GRP, ICONIFY_ON_CONNECT_KEY, (int) set);
+}
+
+
+bool PPPData::get_dock_into_panel() {
+ return (bool) readNumConfig(GENERAL_GRP, DOCKING_KEY, false);
+}
+
+
+void PPPData::set_dock_into_panel(bool set) {
+ writeConfig(GENERAL_GRP, DOCKING_KEY, (int) set);
+}
+
+
+QString PPPData::pppdVersion() {
+ return QString("%1.%2.%3").arg(pppdVer).arg(pppdMod).arg(pppdPatch);
+}
+
+bool PPPData::pppdVersionMin(int ver, int mod, int patch) {
+ // check if pppd version fulfills minimum requirement
+ return (pppdVer > ver
+ || (pppdVer == ver && pppdMod > mod)
+ || (pppdVer == ver && pppdMod == mod && pppdPatch >= patch));
+}
+
+int PPPData::pppdTimeout() {
+ return readNumConfig(GENERAL_GRP, PPPDTIMEOUT_KEY, PPPD_TIMEOUT);
+}
+
+
+void PPPData::setpppdTimeout(int n) {
+ writeConfig(GENERAL_GRP, PPPDTIMEOUT_KEY, n);
+}
+
+//
+// functions to set/return modem information
+//
+
+
+//returns number of modems
+int PPPData::modemCount() const {
+ return modemhighcount + 1;
+}
+
+
+bool PPPData::setModem(const QString &mname) {
+ for(int i = 0; i <= modemhighcount; i++) {
+ setModemByIndex(i);
+ if(modname() == mname) {
+ cmodem = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool PPPData::setModemByIndex(int i) {
+ if(i >= 0 && i <= modemhighcount) {
+ cmodem = i;
+ cmodemgroup.sprintf("%s%i", MODEM_GRP, i);
+ return true;
+ }
+ return false;
+}
+
+
+bool PPPData::isUniqueModname(const QString &n) {
+ int current = cmodem;
+ for(int i=0; i <= modemhighcount; i++) {
+ setModemByIndex(i);
+ if(modname() == n && i != current) {
+ setModemByIndex(current);
+ return false;
+ }
+ }
+ setModemByIndex(current);
+ return true;
+}
+
+
+bool PPPData::deleteModem() {
+ if(cmodem < 0)
+ return false;
+
+ QMap <QString, QString> map;
+ QMap <QString, QString>::Iterator it;
+
+ // set all entries of the current modem to ""
+ map = config->entryMap(cmodemgroup);
+ it = map.begin();
+ while (it != map.end()) {
+ config->writeEntry(it.key(), "");
+ it++;
+ }
+
+ // shift the succeeding modems
+ for(int i = cmodem+1; i <= modemhighcount; i++) {
+ setModemByIndex(i);
+ map = config->entryMap(cmodemgroup);
+ it = map.begin();
+ setModemByIndex(i-1);
+ config->setGroup(cmodemgroup);
+ while (it != map.end()) {
+ config->writeEntry(it.key(), *it);
+ it++;
+ }
+ }
+
+ // make sure the top modem is cleared
+ setModemByIndex(modemhighcount);
+ map = config->entryMap(cmodemgroup);
+ it = map.begin();
+ config->setGroup(cmodemgroup);
+ while (!it.key().isNull()) {
+ config->writeEntry(it.key(), "");
+ it++;
+ }
+
+ modemhighcount--;
+ if(cmodem > modemhighcount)
+ cmodem = modemhighcount;
+
+ setModemByIndex(cmodem);
+
+ return true;
+}
+
+bool PPPData::deleteModem(const QString &mname) {
+ if(!setModem(mname))
+ return false;
+
+ deleteModem();
+
+ return true;
+}
+
+
+int PPPData::newmodem() {
+
+ if(!config || modemhighcount >= MAX_MODEMS)
+ return -1;
+
+ modemhighcount++;
+ setModemByIndex(modemhighcount);
+
+ setpppdArgumentDefaults();
+
+ return cmodem;
+}
+
+int PPPData::copymodem(int i) {
+
+ config->setGroup(cmodemgroup);
+
+ if(modemhighcount >= MAX_MODEMS)
+ return -1;
+
+ setModemByIndex(i);
+
+ QMap <QString, QString> map = config->entryMap(cmodemgroup);
+ QMap <QString, QString>::ConstIterator it = map.begin();
+
+ QString newname = i18n("%1_copy").arg(modname());
+
+ newmodem();
+
+ while (it != map.end()) {
+ config->setGroup(cmodemgroup);
+ config->writeEntry(it.key(), *it);
+ it++;
+ }
+
+ setModname(newname);
+
+ return cmodem;
+}
+
+
+const QString PPPData::modname() {
+ return readConfig(cmodemgroup, MOD_NAME_KEY);
+}
+
+void PPPData::setModname(const QString &n) {
+ if(!cmodemgroup.isNull()) {
+ // are we manipulating the default modem's name ? then change it, too.
+ bool def = modname() == defaultModem();
+ writeConfig(cmodemgroup, MOD_NAME_KEY, n);
+ if (def)
+ setDefaultModem(n);
+ }
+}
+
+
+
+
+const QString PPPData::modemDevice() {
+ return readConfig (cmodemgroup, MODEMDEV_KEY, devices[DEV_DEFAULT]);
+}
+
+
+void PPPData::setModemDevice(const QString &n) {
+ writeConfig(cmodemgroup, MODEMDEV_KEY, n);
+}
+
+
+QString PPPData::flowcontrol() {
+ // keep default value in sync with general.cpp
+ return readConfig(cmodemgroup, FLOWCONTROL_KEY, i18n("Hardware [CRTSCTS]"));
+}
+
+
+void PPPData::setFlowcontrol(const QString &n) {
+ writeConfig(cmodemgroup, FLOWCONTROL_KEY, n);
+}
+
+
+const QString PPPData::speed() {
+ QString s = readConfig(cmodemgroup, SPEED_KEY, "57600");
+ // undo the damage of a bug in former versions. It left an empty Speed=
+ // entry in kppprc. kppp did set the serial port to 57600 as default but
+ // pppd wouldn't receive the speed via the command line.
+ if(s.toUInt() == 0)
+ s = "57600";
+ return s;
+}
+
+
+void PPPData::setSpeed(const QString &n) {
+ writeConfig(cmodemgroup, SPEED_KEY, n);
+}
+
+
+#if 0
+void PPPData::setUseCDLine(const int n) {
+ writeConfig(cmodemgroup,USECDLINE_KEY,n);
+}
+
+
+int PPPData::UseCDLine() {
+ return readNumConfig(cmodemgroup,USECDLINE_KEY,0);
+}
+#endif
+
+const QString PPPData::modemEscapeStr() {
+ return readConfig(cmodemgroup,ESCAPESTR_KEY,"+++");
+}
+
+
+void PPPData::setModemEscapeStr(const QString &n) {
+ writeConfig(cmodemgroup,ESCAPESTR_KEY,n);
+}
+
+
+const QString PPPData::modemEscapeResp() {
+ return readConfig(cmodemgroup,ESCAPERESP_KEY,"OK");
+}
+
+
+void PPPData::setModemEscapeResp(const QString &n) {
+ writeConfig(cmodemgroup,ESCAPERESP_KEY,n);
+}
+
+
+int PPPData::modemEscapeGuardTime() {
+ return readNumConfig(cmodemgroup,ESCAPEGUARDTIME_KEY,50);
+}
+
+
+void PPPData::setModemEscapeGuardTime(int n) {
+ writeConfig(cmodemgroup,ESCAPEGUARDTIME_KEY,n);
+}
+
+
+bool PPPData::modemLockFile() {
+ return readNumConfig(cmodemgroup, LOCKFILE_KEY, 1);
+}
+
+
+void PPPData::setModemLockFile(bool set) {
+ writeConfig(cmodemgroup, LOCKFILE_KEY, set);
+}
+
+
+int PPPData::modemTimeout() {
+ return readNumConfig(cmodemgroup, TIMEOUT_KEY, MODEM_TIMEOUT);
+}
+
+
+void PPPData::setModemTimeout(int n) {
+ writeConfig(cmodemgroup, TIMEOUT_KEY, n);
+}
+
+
+int PPPData::modemToneDuration() {
+ return readNumConfig(cmodemgroup, TONEDURATION_KEY,MODEM_TONEDURATION);
+}
+
+
+void PPPData::setModemToneDuration(int n) {
+ writeConfig(cmodemgroup, TONEDURATION_KEY, n);
+}
+
+
+int PPPData::busyWait() {
+ return readNumConfig(cmodemgroup, BUSYWAIT_KEY, BUSY_WAIT);
+}
+
+
+void PPPData::setbusyWait(int n) {
+ writeConfig(cmodemgroup, BUSYWAIT_KEY, n);
+}
+
+
+//
+//Advanced "Modem" dialog
+//
+// defaults: InitString=ATZ, InitString1="" etc.
+const QString PPPData::modemInitStr(int i) {
+ assert(i >= 0 && i < NumInitStrings);
+ if(i == 0)
+ return readConfig(cmodemgroup, INITSTR_KEY, "ATZ");
+ else
+ return readConfig(cmodemgroup, INITSTR_KEY + QString::number(i), "");
+}
+
+
+void PPPData::setModemInitStr(int i, const QString &n) {
+ assert(i >= 0 && i < NumInitStrings);
+ QString k = INITSTR_KEY + (i > 0 ? QString::number(i) : "");
+ writeConfig(cmodemgroup, k, n);
+}
+
+
+const QString PPPData::modemInitResp() {
+ return readConfig(cmodemgroup, INITRESP_KEY, "OK");
+}
+
+
+void PPPData::setModemInitResp(const QString &n) {
+ writeConfig(cmodemgroup, INITRESP_KEY, n);
+}
+
+
+int PPPData::modemPreInitDelay() {
+ return readNumConfig(cmodemgroup, PREINITDELAY_KEY, 50);
+}
+
+
+void PPPData::setModemPreInitDelay(int n) {
+ writeConfig(cmodemgroup, PREINITDELAY_KEY, n);
+}
+
+
+int PPPData::modemInitDelay() {
+ return readNumConfig(cmodemgroup, INITDELAY_KEY, 50);
+}
+
+
+void PPPData::setModemInitDelay(int n) {
+ writeConfig(cmodemgroup, INITDELAY_KEY, n);
+}
+
+QString PPPData::modemNoDialToneDetectionStr() {
+ return readConfig(cmodemgroup, NODTDETECT_KEY, "ATX3");
+}
+
+void PPPData::setModemNoDialToneDetectionStr(const QString &n) {
+ writeConfig(cmodemgroup, NODTDETECT_KEY, n);
+}
+
+const QString PPPData::modemDialStr() {
+ return readConfig(cmodemgroup, DIALSTR_KEY, "ATDT");
+}
+
+
+void PPPData::setModemDialStr(const QString &n) {
+ writeConfig(cmodemgroup, DIALSTR_KEY, n);
+}
+
+
+const QString PPPData::modemConnectResp() {
+ return readConfig(cmodemgroup, CONNECTRESP_KEY, "CONNECT");
+}
+
+
+void PPPData::setModemConnectResp(const QString &n) {
+ writeConfig(cmodemgroup, CONNECTRESP_KEY, n);
+}
+
+
+const QString PPPData::modemBusyResp() {
+ return readConfig(cmodemgroup, BUSYRESP_KEY, "BUSY");
+}
+
+
+void PPPData::setModemBusyResp(const QString &n) {
+ writeConfig(cmodemgroup, BUSYRESP_KEY, n);
+}
+
+
+const QString PPPData::modemNoCarrierResp() {
+ return readConfig(cmodemgroup, NOCARRIERRESP_KEY, "NO CARRIER");
+}
+
+
+void PPPData::setModemNoCarrierResp(const QString &n) {
+ writeConfig(cmodemgroup, NOCARRIERRESP_KEY, n);
+}
+
+
+const QString PPPData::modemNoDialtoneResp() {
+ return readConfig(cmodemgroup, NODIALTONERESP_KEY, "NO DIALTONE");
+}
+
+
+void PPPData::setModemNoDialtoneResp(const QString &n) {
+ writeConfig(cmodemgroup, NODIALTONERESP_KEY, n);
+}
+
+
+const QString PPPData::modemHangupStr() {
+ return readConfig(cmodemgroup, HANGUPSTR_KEY, "+++ATH");
+}
+
+void PPPData::setModemHangupStr(const QString &n) {
+ writeConfig(cmodemgroup, HANGUPSTR_KEY, n);
+}
+
+
+const QString PPPData::modemHangupResp() {
+ return readConfig(cmodemgroup, HANGUPRESP_KEY, "OK");
+}
+
+void PPPData::setModemHangupResp(const QString &n) {
+ writeConfig(cmodemgroup, HANGUPRESP_KEY, n);
+}
+
+
+QString PPPData::modemDLPResp() {
+ return readConfig(cmodemgroup, DLPRESP_KEY, "DIGITAL LINE DETECTED");
+}
+
+void PPPData::setModemDLPResp(const QString &n) {
+ writeConfig(cmodemgroup, DLPRESP_KEY, n);
+}
+
+
+
+
+const QString PPPData::modemAnswerStr() {
+ return readConfig(cmodemgroup, ANSWERSTR_KEY, "ATA");
+}
+
+
+QString PPPData::volumeOff() {
+ return readConfig(cmodemgroup, VOLUME_OFF, "M0L0");
+}
+
+
+void PPPData::setVolumeOff(const QString &s) {
+ writeConfig(cmodemgroup, VOLUME_OFF, s);
+}
+
+
+QString PPPData::volumeMedium() {
+ return readConfig(cmodemgroup, VOLUME_MEDIUM, "M1L1");
+}
+
+
+void PPPData::setVolumeMedium(const QString &s) {
+ writeConfig(cmodemgroup, VOLUME_MEDIUM, s);
+}
+
+
+QString PPPData::volumeHigh() {
+ QString tmp = readConfig(cmodemgroup, VOLUME_HIGH, "M1L3");
+ if(tmp == "M1L4")
+ tmp = "M1L3";
+ return tmp;
+}
+
+
+void PPPData::setVolumeHigh(const QString &s) {
+ writeConfig(cmodemgroup, VOLUME_HIGH, s);
+}
+
+
+QString PPPData::volumeInitString() {
+ QString s;
+
+ switch(volume()) {
+ case 0:
+ s = volumeOff();
+ break;
+ case 1:
+ s = volumeMedium();
+ break;
+ case 2:
+ s = volumeHigh();
+ break;
+ default:
+ s = volumeMedium();
+ }
+
+ return s;
+}
+
+
+int PPPData::volume() {
+ return readNumConfig(cmodemgroup, VOLUME_KEY, 1);
+}
+
+
+void PPPData::setVolume(int i) {
+ writeConfig(cmodemgroup, VOLUME_KEY, i);
+}
+
+int PPPData::waitForDialTone() {
+ return readNumConfig(cmodemgroup, DIALTONEWAIT_KEY, 1);
+}
+
+void PPPData::setWaitForDialTone(int i) {
+ writeConfig(cmodemgroup, DIALTONEWAIT_KEY, i);
+}
+
+void PPPData::setModemAnswerStr(const QString &n) {
+ writeConfig(cmodemgroup, ANSWERSTR_KEY, n);
+}
+
+
+const QString PPPData::modemRingResp() {
+ return readConfig(cmodemgroup, RINGRESP_KEY, "RING");
+}
+
+
+void PPPData::setModemRingResp(const QString &n) {
+ writeConfig(cmodemgroup, RINGRESP_KEY, n);
+}
+
+
+const QString PPPData::modemAnswerResp() {
+ return readConfig(cmodemgroup, ANSWERRESP_KEY, "CONNECT");
+}
+
+
+void PPPData::setModemAnswerResp(const QString &n) {
+ writeConfig(cmodemgroup, ANSWERRESP_KEY, n);
+}
+
+
+const QString PPPData::enter() {
+ return readConfig(cmodemgroup, ENTER_KEY, "CR");
+}
+
+
+void PPPData::setEnter(const QString &n) {
+ writeConfig(cmodemgroup, ENTER_KEY, n);
+}
+
+
+//
+// functions to set/return account information
+//
+
+//returns number of accounts
+int PPPData::accountCount() const {
+ return accounthighcount + 1;
+}
+
+
+bool PPPData::setAccount(const QString &aname) {
+ for(int i = 0; i <= accounthighcount; i++) {
+ setAccountByIndex(i);
+ if(accname() == aname) {
+ caccount = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool PPPData::setAccountByIndex(int i) {
+ if(i >= 0 && i <= accounthighcount) {
+ caccount = i;
+ caccountgroup.sprintf("%s%i", ACCOUNT_GRP, i);
+ return true;
+ }
+ return false;
+}
+
+
+bool PPPData::isUniqueAccname(const QString &n) {
+ if(n.contains(':'))
+ return false;
+ int current = caccount;
+ for(int i=0; i <= accounthighcount; i++) {
+ setAccountByIndex(i);
+ if(accname() == n && i != current) {
+ setAccountByIndex(current);
+ return false;
+ }
+ }
+ setAccountByIndex(current);
+ return true;
+}
+
+
+bool PPPData::deleteAccount() {
+ if(caccount < 0)
+ return false;
+
+ QMap <QString, QString> map;
+ QMap <QString, QString>::Iterator it;
+
+ // set all entries of the current account to ""
+ map = config->entryMap(caccountgroup);
+ it = map.begin();
+ while (it != map.end()) {
+ config->writeEntry(it.key(), "");
+ it++;
+ }
+
+ // shift the succeeding accounts
+ for(int i = caccount+1; i <= accounthighcount; i++) {
+ setAccountByIndex(i);
+ map = config->entryMap(caccountgroup);
+ it = map.begin();
+ setAccountByIndex(i-1);
+ config->setGroup(caccountgroup);
+ while (it != map.end()) {
+ config->writeEntry(it.key(), *it);
+ it++;
+ }
+ }
+
+ // make sure the top account is cleared
+ setAccountByIndex(accounthighcount);
+ map = config->entryMap(caccountgroup);
+ it = map.begin();
+ config->setGroup(caccountgroup);
+ while (!it.key().isNull()) {
+ config->writeEntry(it.key(), "");
+ it++;
+ }
+
+ accounthighcount--;
+ if(caccount > accounthighcount)
+ caccount = accounthighcount;
+
+ setAccountByIndex(caccount);
+
+ return true;
+}
+
+bool PPPData::deleteAccount(const QString &aname) {
+ if(!setAccount(aname))
+ return false;
+
+ deleteAccount();
+
+ return true;
+}
+
+
+int PPPData::newaccount() {
+
+ if(!config || accounthighcount >= MAX_ACCOUNTS)
+ return -1;
+
+ accounthighcount++;
+ setAccountByIndex(accounthighcount);
+
+ setpppdArgumentDefaults();
+
+ return caccount;
+}
+
+int PPPData::copyaccount(int i) {
+
+ config->setGroup(caccountgroup);
+
+ if(accounthighcount >= MAX_ACCOUNTS)
+ return -1;
+
+ setAccountByIndex(i);
+
+ QMap <QString, QString> map = config->entryMap(caccountgroup);
+ QMap <QString, QString>::ConstIterator it = map.begin();
+
+ QString newname = i18n("%1_copy").arg(accname());
+
+ newaccount();
+
+ while (it != map.end()) {
+ config->setGroup(caccountgroup);
+ config->writeEntry(it.key(), *it);
+ it++;
+ }
+
+ setAccname(newname);
+
+ return caccount;
+}
+
+
+const QString PPPData::accname() {
+ return readConfig(caccountgroup, ACC_NAME_KEY);
+}
+
+void PPPData::setAccname(const QString &n) {
+ if(!caccountgroup.isNull()) {
+ // are we manipulating the default account's name ? then change it, too.
+ bool def = accname() == defaultAccount();
+ writeConfig(caccountgroup, ACC_NAME_KEY, n);
+ if (def)
+ setDefaultAccount(n);
+ }
+}
+
+
+#define SEPARATOR_CHAR ':'
+QStringList &PPPData::phonenumbers() {
+
+ readListConfig(caccountgroup, PHONENUMBER_KEY, phonelist, SEPARATOR_CHAR);
+ return phonelist;
+
+}
+
+
+const QString PPPData::phonenumber() {
+ return readConfig(caccountgroup, PHONENUMBER_KEY);
+}
+
+
+void PPPData::setPhonenumber(const QString &n) {
+ writeConfig(caccountgroup, PHONENUMBER_KEY, n);
+}
+
+
+const QString PPPData::dialPrefix() {
+ return readConfig(caccountgroup, DIAL_PREFIX_KEY, "");
+}
+
+
+void PPPData::setDialPrefix(const QString &s) {
+ writeConfig(caccountgroup, DIAL_PREFIX_KEY, s);
+}
+
+
+int PPPData::authMethod() {
+ return readNumConfig(caccountgroup, AUTH_KEY, 0);
+}
+
+
+void PPPData::setAuthMethod(int value) {
+ writeConfig(caccountgroup, AUTH_KEY, value);
+}
+
+
+const QString PPPData::storedUsername() {
+ return readConfig(caccountgroup, STORED_USERNAME_KEY, "");
+}
+
+
+void PPPData::setStoredUsername(const QString &b) {
+ writeConfig(caccountgroup, STORED_USERNAME_KEY, b);
+}
+
+
+const QString PPPData::storedPassword() {
+ return readConfig(caccountgroup, STORED_PASSWORD_KEY, "");
+}
+
+
+void PPPData::setStoredPassword(const QString &b) {
+ writeConfig(caccountgroup, STORED_PASSWORD_KEY, b);
+}
+
+
+bool PPPData::storePassword() {
+ return (bool)readNumConfig(caccountgroup, STORE_PASSWORD_KEY, 1);
+}
+
+int PPPData::callbackType() {
+ return readNumConfig(caccountgroup, CALLBACK_TYPE_KEY, 0);
+}
+
+void PPPData::setCallbackType(int value) {
+ writeConfig(caccountgroup, CALLBACK_TYPE_KEY, value);
+}
+
+QString PPPData::callbackPhone() {
+ return readConfig(caccountgroup, CALLBACK_PHONE_KEY, "");
+}
+
+void PPPData::setCallbackPhone(const QString &b) {
+ writeConfig(caccountgroup, CALLBACK_PHONE_KEY, b);
+}
+
+bool PPPData::waitCallback() {
+ return waitcallback;
+}
+
+void PPPData::setWaitCallback(bool value) {
+ waitcallback = value;
+}
+
+const QString PPPData::command_before_connect() {
+ return readConfig(caccountgroup, BEFORE_CONNECT_KEY);
+}
+
+
+void PPPData::setCommand_before_connect(const QString &n) {
+ writeConfig(caccountgroup, BEFORE_CONNECT_KEY, n);
+}
+
+
+void PPPData::setStorePassword(bool b) {
+ writeConfig(caccountgroup, STORE_PASSWORD_KEY, (int)b);
+}
+
+
+const QString PPPData::command_on_connect() {
+ return readConfig(caccountgroup, COMMAND_KEY);
+}
+
+
+void PPPData::setCommand_on_connect(const QString &n) {
+ writeConfig(caccountgroup, COMMAND_KEY, n);
+}
+
+
+const QString PPPData::command_on_disconnect() {
+ return readConfig(caccountgroup, DISCONNECT_COMMAND_KEY);
+}
+
+
+void PPPData::setCommand_on_disconnect(const QString &n) {
+ writeConfig(caccountgroup, DISCONNECT_COMMAND_KEY, n);
+}
+
+
+const QString PPPData::command_before_disconnect() {
+ return readConfig(caccountgroup, BEFORE_DISCONNECT_KEY);
+}
+
+
+void PPPData::setCommand_before_disconnect(const QString &n) {
+ writeConfig(caccountgroup, BEFORE_DISCONNECT_KEY, n);
+}
+
+
+const QString PPPData::ipaddr() {
+ return readConfig(caccountgroup, IPADDR_KEY);
+}
+
+
+void PPPData::setIpaddr(const QString &n) {
+ writeConfig(caccountgroup, IPADDR_KEY, n);
+}
+
+
+const QString PPPData::subnetmask() {
+ return readConfig(caccountgroup, SUBNETMASK_KEY);
+}
+
+
+void PPPData::setSubnetmask(const QString &n) {
+ writeConfig(caccountgroup, SUBNETMASK_KEY, n);
+}
+
+
+bool PPPData::autoname() {
+ return (bool) readNumConfig(caccountgroup, AUTONAME_KEY, false);
+}
+
+
+void PPPData::setAutoname(bool set) {
+ writeConfig(caccountgroup, AUTONAME_KEY, (int) set);
+}
+
+
+bool PPPData::AcctEnabled() {
+ return (bool) readNumConfig(caccountgroup, ACCTENABLED_KEY, false);
+}
+
+
+void PPPData::setAcctEnabled(bool set) {
+ writeConfig(caccountgroup, ACCTENABLED_KEY, (int) set);
+}
+
+
+int PPPData::VolAcctEnabled() {
+ return readNumConfig(caccountgroup, VOLACCTENABLED_KEY, 0);
+}
+
+
+void PPPData::setVolAcctEnabled(int set) {
+ writeConfig(caccountgroup, VOLACCTENABLED_KEY, set);
+}
+
+
+const QString PPPData::gateway() {
+ return readConfig(caccountgroup, GATEWAY_KEY);
+}
+
+
+void PPPData::setGateway(const QString &n ) {
+ writeConfig(caccountgroup, GATEWAY_KEY, n);
+}
+
+
+bool PPPData::defaultroute() {
+ // default route is by default 'on'.
+ return (bool) readNumConfig(caccountgroup, DEFAULTROUTE_KEY, true);
+}
+
+
+void PPPData::setDefaultroute(bool set) {
+ writeConfig(caccountgroup, DEFAULTROUTE_KEY, (int) set);
+}
+
+
+bool PPPData::autoDNS() {
+ bool set = (bool) readNumConfig(caccountgroup, AUTODNS_KEY, true);
+ return (set && gpppdata.pppdVersionMin(2, 3, 7));
+}
+
+
+void PPPData::setAutoDNS(bool set) {
+ writeConfig(caccountgroup, AUTODNS_KEY, (int) set);
+}
+
+
+void PPPData::setExDNSDisabled(bool set) {
+ writeConfig(caccountgroup, EXDNSDISABLED_KEY, (int) set);
+}
+
+
+bool PPPData::exDNSDisabled() {
+ return (bool) readNumConfig(caccountgroup, EXDNSDISABLED_KEY,0);
+}
+
+
+QStringList &PPPData::dns() {
+ static QStringList dnslist;
+
+ readListConfig(caccountgroup, DNS_KEY, dnslist);
+ while(dnslist.count() > MAX_DNS_ENTRIES)
+ dnslist.remove(dnslist.last());
+
+ return dnslist;
+}
+
+
+void PPPData::setDns(QStringList &list) {
+ writeListConfig(caccountgroup, DNS_KEY, list);
+}
+
+
+const QString PPPData::domain() {
+ return readConfig(caccountgroup, DOMAIN_KEY);
+}
+
+
+void PPPData::setDomain(const QString &n ) {
+ writeConfig(caccountgroup, DOMAIN_KEY, n);
+}
+
+
+QStringList &PPPData::scriptType() {
+ static QStringList typelist;
+
+ readListConfig(caccountgroup, SCRIPTCOM_KEY, typelist);
+ while(typelist.count() > MAX_SCRIPT_ENTRIES)
+ typelist.remove(typelist.last());
+
+ return typelist;
+}
+
+
+void PPPData::setScriptType(QStringList &list) {
+ writeListConfig(caccountgroup, SCRIPTCOM_KEY, list);
+}
+
+
+QStringList &PPPData::script() {
+ static QStringList scriptlist;
+
+ readListConfig(caccountgroup, SCRIPTARG_KEY, scriptlist);
+ while(scriptlist.count() > MAX_SCRIPT_ENTRIES)
+ scriptlist.remove(scriptlist.last());
+
+ return scriptlist;
+}
+
+
+void PPPData::setScript(QStringList &list) {
+ writeListConfig(caccountgroup, SCRIPTARG_KEY, list);
+}
+
+
+const QString PPPData::accountingFile() {
+ return readConfig(caccountgroup, ACCTFILE_KEY);
+}
+
+
+void PPPData::setAccountingFile(const QString &n) {
+ writeConfig(caccountgroup, ACCTFILE_KEY, n);
+}
+
+
+const QString PPPData::totalCosts() {
+ return readConfig(caccountgroup, TOTALCOSTS_KEY);
+}
+
+
+void PPPData::setTotalCosts(const QString &n) {
+ writeConfig(caccountgroup, TOTALCOSTS_KEY, n);
+}
+
+
+int PPPData::totalBytes() {
+ return readNumConfig(caccountgroup, TOTALBYTES_KEY, 0);
+}
+
+void PPPData::setTotalBytes(int n) {
+ writeConfig(caccountgroup, TOTALBYTES_KEY, n);
+}
+
+
+QStringList &PPPData::pppdArgument() {
+ static QStringList arglist;
+
+ while(arglist.count() > MAX_PPPD_ARGUMENTS)
+ arglist.remove(arglist.last());
+ readListConfig(caccountgroup, PPPDARG_KEY, arglist);
+
+ return arglist;
+}
+
+
+void PPPData::setpppdArgument(QStringList &args) {
+ writeListConfig(caccountgroup, PPPDARG_KEY, args);
+}
+
+
+void PPPData::setpppdArgumentDefaults() {
+ QStringList arg;
+ setpppdArgument(arg);
+}
+
+
+// graphing widget
+void PPPData::setGraphingOptions(bool enable,
+ QColor bg,
+ QColor text,
+ QColor in,
+ QColor out)
+{
+ if(config) {
+ config->setGroup(GRAPH_GRP);
+ config->writeEntry(GENABLED, enable);
+ config->writeEntry(GCOLOR_BG, bg);
+ config->writeEntry(GCOLOR_TEXT, text);
+ config->writeEntry(GCOLOR_IN, in);
+ config->writeEntry(GCOLOR_OUT, out);
+ }
+}
+
+void PPPData::graphingOptions(bool &enable,
+ QColor &bg,
+ QColor &text,
+ QColor &in,
+ QColor &out)
+{
+ QColor c;
+
+ if(config) {
+ config->setGroup(GRAPH_GRP);
+ enable = config->readBoolEntry(GENABLED, true);
+ c = Qt::white;
+ bg = config->readColorEntry(GCOLOR_BG, &c);
+ c = Qt::black;
+ text = config->readColorEntry(GCOLOR_TEXT, &c);
+ c = Qt::blue;
+ in = config->readColorEntry(GCOLOR_IN, &c);
+ c = Qt::red;
+ out = config->readColorEntry(GCOLOR_OUT, &c);
+ }
+}
+
+
+bool PPPData::graphingEnabled() {
+ if(config) {
+ config->setGroup(GRAPH_GRP);
+ return config->readBoolEntry(GENABLED, true);
+ }
+ else return true;
+}
+
+
+
+//
+//functions to change/set the child pppd process info
+//
+bool PPPData::pppdRunning() const {
+ return pppdisrunning;
+}
+
+void PPPData::setpppdRunning(bool set) {
+ pppdisrunning = set;
+}
+
+int PPPData::pppdError() const {
+ return pppderror;
+}
+
+void PPPData::setpppdError(int err) {
+ pppderror = err;
+}
+
+
+//
+// window position
+//
+void PPPData::winPosConWin(int& p_x, int& p_y) {
+ QRect desk = KGlobalSettings::splashScreenDesktopGeometry();
+ p_x = readNumConfig(WINPOS_GRP, WINPOS_CONWIN_X, desk.center().x()-160);
+ p_y = readNumConfig(WINPOS_GRP, WINPOS_CONWIN_Y, desk.center().y()-55);
+}
+
+void PPPData::setWinPosConWin(int p_x, int p_y) {
+ writeConfig(WINPOS_GRP, WINPOS_CONWIN_X, p_x);
+ writeConfig(WINPOS_GRP, WINPOS_CONWIN_Y, p_y);
+}
+
+void PPPData::winPosStatWin(int& p_x, int& p_y) {
+ QRect desk = KGlobalSettings::splashScreenDesktopGeometry();
+ p_x = readNumConfig(WINPOS_GRP, WINPOS_STATWIN_X, desk.center().x()-160);
+ p_y = readNumConfig(WINPOS_GRP, WINPOS_STATWIN_Y, desk.center().y()-55);
+}
+
+void PPPData::setWinPosStatWin(int p_x, int p_y) {
+ writeConfig(WINPOS_GRP, WINPOS_STATWIN_X, p_x);
+ writeConfig(WINPOS_GRP, WINPOS_STATWIN_Y, p_y);
+}
diff --git a/kppp/pppdata.h b/kppp/pppdata.h
new file mode 100644
index 00000000..d9a77ae7
--- /dev/null
+++ b/kppp/pppdata.h
@@ -0,0 +1,513 @@
+/* -*- C++ -*-
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PPPDATA_H_
+#define _PPPDATA_H_
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcolor.h>
+
+#include "kpppconfig.h"
+
+class KConfig;
+
+// string lengths
+
+#define PATH_SIZE 120
+#define MODEMSTR_SIZE 80
+#define ACCNAME_SIZE 50
+#define PHONENUMBER_SIZE 60
+#define COMMAND_SIZE 255
+#define IPADDR_SIZE 15
+#define DOMAIN_SIZE 50
+#define TIMEOUT_SIZE 60
+
+//
+// keys for config file
+//
+
+// groups
+#define GENERAL_GRP "General"
+#define MODEM_GRP "Modem"
+#define ACCOUNT_GRP "Account"
+#define GRAPH_GRP "Graph"
+#define WINPOS_GRP "WindowPosition"
+
+// general
+#define DEFAULTACCOUNT_KEY "DefaultAccount"
+#define DEFAULTMODEM_KEY "DefaultModem"
+#define PPPDVERSION_KEY "pppdVersion"
+#define PPPDTIMEOUT_KEY "pppdTimeout"
+#define SHOWCLOCK_KEY "ShowClock"
+#define SHOWLOGWIN_KEY "ShowLogWindow"
+#define AUTOREDIAL_KEY "AutomaticRedial"
+#define DISCONNECT_KEY "DisconnectOnXServerExit"
+#define QUITONDISCONNECT_KEY "QuitOnDisconnect"
+#define NUMACCOUNTS_KEY "NumberOfAccounts"
+#define NUMMODEMS_KEY "NumberOfModems"
+#define REDIALONNOCARR_KEY "RedialOnNoCarrier"
+#define ID_KEY "ID"
+
+// modem
+#define MOD_NAME_KEY "Name"
+#define MODEMDEV_KEY "Device"
+#define LOCKFILE_KEY "UseLockFile"
+#define FLOWCONTROL_KEY "FlowControl"
+#define SPEED_KEY "Speed"
+#define TIMEOUT_KEY "Timeout"
+#define TONEDURATION_KEY "ToneDuration"
+#define BUSYWAIT_KEY "BusyWait"
+#define INITSTR_KEY "InitString"
+#define INITRESP_KEY "InitResponse"
+#define PREINITDELAY_KEY "PreInitDelay"
+#define INITDELAY_KEY "InitDelay"
+#define NODTDETECT_KEY "NoDialToneDetection"
+#define DIALTONEWAIT_KEY "WaitForDialTone"
+#define DIALSTR_KEY "DialString"
+#define CONNECTRESP_KEY "ConnectResponse"
+#define BUSYRESP_KEY "BusyResponse"
+#define NOCARRIERRESP_KEY "NoCarrierResponse"
+#define NODIALTONERESP_KEY "NoDialToneResp"
+#define HANGUPSTR_KEY "HangupString"
+#define HANGUPRESP_KEY "HangUpResponse"
+#define DLPRESP_KEY "DLPResponse"
+#define ANSWERSTR_KEY "AnswerString"
+#define RINGRESP_KEY "RingResponse"
+#define ANSWERRESP_KEY "AnswerResponse"
+#define ENTER_KEY "Enter"
+#define ESCAPESTR_KEY "EscapeString"
+#define ESCAPERESP_KEY "EscapeResponse"
+#define ESCAPEGUARDTIME_KEY "EscapeGuardTime"
+#define USECDLINE_KEY "UseCDLine"
+#define VOLUME_HIGH "VolumeHigh"
+#define VOLUME_MEDIUM "VolumeMedium"
+#define VOLUME_OFF "VolumeOff"
+#define VOLUME_KEY "Volume"
+
+// account
+#define ACC_NAME_KEY "Name"
+#define PHONENUMBER_KEY "Phonenumber"
+#define DIAL_PREFIX_KEY "DialPrefix"
+#define AUTH_KEY "Authentication"
+#define STORED_PASSWORD_KEY "Password"
+#define STORED_USERNAME_KEY "Username"
+#define STORE_PASSWORD_KEY "StorePassword"
+#define CALLBACK_TYPE_KEY "CallbackType"
+#define CALLBACK_PHONE_KEY "CallbackPhone"
+#define BEFORE_CONNECT_KEY "BeforeConnect"
+#define COMMAND_KEY "Command"
+#define DISCONNECT_COMMAND_KEY "DisconnectCommand"
+#define BEFORE_DISCONNECT_KEY "BeforeDisconnect"
+#define IPADDR_KEY "IPAddr"
+#define SUBNETMASK_KEY "SubnetMask"
+#define ACCTENABLED_KEY "AccountingEnabled"
+#define VOLACCTENABLED_KEY "VolumeAccountingEnabled"
+#define ACCTFILE_KEY "AccountingFile"
+#define AUTONAME_KEY "AutoName"
+#define GATEWAY_KEY "Gateway"
+#define DEFAULTROUTE_KEY "DefaultRoute"
+#define DOMAIN_KEY "Domain"
+#define DNS_KEY "DNS"
+#define AUTODNS_KEY "AutoDNS"
+#define EXDNSDISABLED_KEY "ExDNSDisabled"
+#define SCRIPTCOM_KEY "ScriptCommands"
+#define SCRIPTARG_KEY "ScriptArguments"
+#define PPPDARG_KEY "pppdArguments"
+#define PPP_DEBUG_OPTION "PPPDebug"
+#define ICONIFY_ON_CONNECT_KEY "iconifyOnConnect"
+#define DOCKING_KEY "DockIntoPanel"
+#define TOTALCOSTS_KEY "TotalCosts"
+#define TOTALBYTES_KEY "TotalBytes"
+
+// graph colors
+#define GENABLED "Enabled"
+#define GCOLOR_BG "Background"
+#define GCOLOR_TEXT "Text"
+#define GCOLOR_IN "InBytes"
+#define GCOLOR_OUT "OutBytes"
+
+// pppd errors
+#define E_IF_TIMEOUT 1
+#define E_PPPD_DIED 2
+#define E_CBCP_WAIT 14
+
+// window position
+#define WINPOS_CONWIN_X "WindowPositionConWinX"
+#define WINPOS_CONWIN_Y "WindowPositionConWinY"
+#define WINPOS_STATWIN_X "WindowPositionStatWinX"
+#define WINPOS_STATWIN_Y "WindowPositionStatWinY"
+
+class PPPData {
+public:
+ PPPData();
+ ~PPPData() {};
+
+ enum { NumInitStrings = 2 };
+
+ // general functions
+ bool open();
+ void save();
+ void cancel();
+ int access() const; // read/write access
+
+ // function to read/write date to configuration file
+ QString readConfig(const QString &, const QString &, const QString &);
+ int readNumConfig(const QString &, const QString &, int);
+ bool readListConfig(const QString &, const QString &,
+ QStringList &, char sep = ',');
+ void writeConfig(const QString &, const QString &, const QString &);
+ void writeConfig(const QString &, const QString &, int);
+ void writeListConfig(const QString &, const QString &,
+ QStringList &, char sep = ',');
+
+ // return the current account group
+ QString currentAccountGroup() { return caccountgroup; }
+ // return the current modem group
+ QString currentModemGroup() { return cmodemgroup; }
+
+ // functions to set/get general kppp info
+ QString password() const;
+ void setPassword(const QString &);
+
+ const QString defaultAccount();
+ void setDefaultAccount(const QString &);
+
+ const QString defaultModem();
+ void setDefaultModem(const QString &);
+
+ void set_xserver_exit_disconnect(bool set);
+ bool get_xserver_exit_disconnect();
+
+ void set_redial_on_nocarrier(bool set);
+ bool get_redial_on_nocarrier();
+
+ void setPPPDebug(bool set);
+ bool getPPPDebug();
+
+ void set_quit_on_disconnect(bool);
+ bool quit_on_disconnect();
+
+ void set_show_clock_on_caption(bool set);
+ bool get_show_clock_on_caption();
+
+ void set_show_log_window(bool set);
+ bool get_show_log_window();
+
+ void set_automatic_redial(bool set);
+ bool automatic_redial();
+
+ void set_iconify_on_connect(bool set);
+ bool get_iconify_on_connect();
+
+ void set_dock_into_panel(bool set);
+ bool get_dock_into_panel();
+
+ const QString enter();
+ void setEnter(const QString &);
+
+ QString pppdVersion();
+ bool pppdVersionMin(int ver, int mod, int patch);
+
+ int pppdTimeout();
+ void setpppdTimeout(int);
+
+ // functions to set/get account information
+ int modemCount() const;
+ bool setModem(const QString &);
+ bool setModemByIndex(int);
+
+ bool isUniqueModname(const QString &);
+
+ bool deleteModem();
+ bool deleteModem(const QString &);
+ int newmodem();
+ int copymodem(int i);
+
+ const QString modname();
+ void setModname(const QString &);
+
+
+ int busyWait();
+ void setbusyWait(int);
+
+ bool modemLockFile();
+ void setModemLockFile(bool set);
+
+ int modemEscapeGuardTime();
+ void setModemEscapeGuardTime(int i);
+
+ void setModemEscapeStr(const QString &);
+ const QString modemEscapeStr();
+
+ void setModemEscapeResp(const QString &);
+ const QString modemEscapeResp();
+
+ const QString modemDevice();
+ void setModemDevice(const QString &);
+
+ QString flowcontrol();
+ void setFlowcontrol(const QString &);
+
+ int modemTimeout();
+ void setModemTimeout(int);
+
+ int modemToneDuration();
+ void setModemToneDuration(int);
+
+ QString volumeInitString();
+ int volume();
+ void setVolume(int);
+
+ int waitForDialTone();
+ void setWaitForDialTone(int i);
+
+ // modem command strings/responses
+ const QString modemInitStr(int i);
+ void setModemInitStr(int i, const QString &);
+
+ const QString modemInitResp();
+ void setModemInitResp(const QString &);
+
+ int modemPreInitDelay();
+ void setModemPreInitDelay(int);
+
+ int modemInitDelay();
+ void setModemInitDelay(int);
+
+ QString modemNoDialToneDetectionStr();
+ void setModemNoDialToneDetectionStr(const QString &);
+
+ const QString modemDialStr();
+ void setModemDialStr(const QString &);
+
+ const QString modemConnectResp();
+ void setModemConnectResp(const QString &);
+
+ const QString modemBusyResp();
+ void setModemBusyResp(const QString &);
+
+ const QString modemNoCarrierResp();
+ void setModemNoCarrierResp(const QString &);
+
+ const QString modemNoDialtoneResp();
+ void setModemNoDialtoneResp(const QString &);
+
+ const QString modemHangupStr();
+ void setModemHangupStr(const QString &);
+
+ const QString modemHangupResp();
+ void setModemHangupResp(const QString &);
+
+ QString modemDLPResp();
+ void setModemDLPResp(const QString &);
+
+ const QString modemAnswerStr();
+ void setModemAnswerStr(const QString &);
+
+ const QString modemRingResp();
+ void setModemRingResp(const QString &);
+
+ const QString modemAnswerResp();
+ void setModemAnswerResp(const QString &);
+
+ QString volumeOff();
+ void setVolumeOff(const QString &);
+
+ QString volumeMedium();
+ void setVolumeMedium(const QString &);
+
+ QString volumeHigh();
+ void setVolumeHigh(const QString &);
+
+#if 0
+ void setUseCDLine(const int n);
+ int UseCDLine();
+#endif
+
+ // functions to set/get account information
+ int accountCount() const;
+ bool setAccount(const QString &);
+ bool setAccountByIndex(int);
+
+ bool isUniqueAccname(const QString &);
+
+ bool deleteAccount();
+ bool deleteAccount(const QString &);
+ int newaccount();
+ int copyaccount(int i);
+
+ const QString accname();
+ void setAccname(const QString &);
+
+ QStringList &phonenumbers();
+ const QString phonenumber();
+ void setPhonenumber(const QString &);
+
+ const QString dialPrefix();
+ void setDialPrefix(const QString &);
+
+ int authMethod();
+ void setAuthMethod(int);
+
+ const QString storedUsername();
+ void setStoredUsername(const QString &);
+
+ const QString storedPassword();
+ void setStoredPassword(const QString &);
+
+ bool storePassword();
+ void setStorePassword(bool);
+
+ int callbackType();
+ void setCallbackType(int);
+
+ QString callbackPhone();
+ void setCallbackPhone(const QString &);
+
+ bool waitCallback();
+ void setWaitCallback(bool);
+
+ const QString speed();
+ void setSpeed(const QString &);
+
+ const QString command_before_connect();
+ void setCommand_before_connect(const QString &);
+
+ const QString command_on_connect();
+ void setCommand_on_connect(const QString &);
+
+ const QString command_on_disconnect();
+ void setCommand_on_disconnect(const QString &);
+
+ const QString command_before_disconnect();
+ void setCommand_before_disconnect(const QString &);
+
+ const QString ipaddr();
+ void setIpaddr(const QString &);
+
+ const QString subnetmask();
+ void setSubnetmask(const QString &);
+
+ bool AcctEnabled();
+ void setAcctEnabled(bool set);
+
+ int VolAcctEnabled();
+ void setVolAcctEnabled(int set);
+
+ bool autoDNS();
+ void setAutoDNS(bool set);
+
+ bool exDNSDisabled();
+ void setExDNSDisabled(bool set);
+
+ bool autoname();
+ void setAutoname(bool set);
+
+ const QString gateway();
+ void setGateway(const QString &);
+
+ bool defaultroute();
+ void setDefaultroute(bool set);
+
+ QStringList &dns();
+ void setDns(QStringList &);
+
+ const QString domain();
+ void setDomain(const QString &);
+
+ QStringList &scriptType();
+ void setScriptType(QStringList &);
+
+ QStringList &script();
+ void setScript(QStringList &);
+
+ QStringList &pppdArgument();
+ void setpppdArgumentDefaults();
+ void setpppdArgument(QStringList &);
+
+ //functions to change/set the child pppd process info
+ bool pppdRunning() const;
+ void setpppdRunning(bool set);
+
+ int pppdError() const;
+ void setpppdError(int err);
+
+ // functions to set/query the accounting info
+ const QString accountingFile();
+ void setAccountingFile(const QString &);
+
+ const QString totalCosts();
+ void setTotalCosts(const QString &);
+
+ int totalBytes();
+ void setTotalBytes(int);
+
+ // graphing widget
+ void setGraphingOptions(bool enabled,
+ QColor bg,
+ QColor text,
+ QColor in,
+ QColor out);
+ void graphingOptions(bool &enabled,
+ QColor &bg,
+ QColor &text,
+ QColor &in,
+ QColor &out);
+ bool graphingEnabled();
+
+ // window positions
+ void winPosConWin(int &, int &);
+ void setWinPosConWin(int, int);
+ void winPosStatWin(int &, int &);
+ void setWinPosStatWin(int, int);
+
+private:
+ QString passwd;
+ KConfig* config; // configuration object
+ int accounthighcount; // index of highest account
+ int caccount; // index of the current account
+ int modemhighcount; // index of highest modem
+ int cmodem; // index of the current modem
+ QString cmodemgroup; // name of current modem group
+ QString caccountgroup; // name of current account group
+ pid_t suidprocessid; // process ID of setuid child
+ bool pppdisrunning; // pppd process
+ // daemon
+ int pppderror; // error encounterd running pppd
+ int pppdVer, pppdMod, pppdPatch; // pppd version
+ bool waitcallback; // callback waiting flag
+
+ QStringList phonelist;
+};
+
+extern PPPData gpppdata;
+
+#endif
diff --git a/kppp/ppplog.cpp b/kppp/ppplog.cpp
new file mode 100644
index 00000000..76b0903b
--- /dev/null
+++ b/kppp/ppplog.cpp
@@ -0,0 +1,285 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * (c) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qlabel.h>
+#include <qdir.h>
+#include <qpushbutton.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <qdialog.h>
+#include <qregexp.h>
+#include <qmultilineedit.h>
+#include <qlayout.h>
+
+#include <kbuttonbox.h>
+#include <kmessagebox.h>
+
+#include "pppdata.h"
+#include "requester.h"
+#include <klocale.h>
+
+
+int PPPL_MakeLog(QStringList &list) {
+ int pid = -1, newpid;
+ char buffer[1024], *p;
+ const char *pidp;
+ int fd;
+
+ fd = Requester::rq->openSysLog();
+ if(fd < 0) {
+ list.append(i18n("Cannot open any of the following logfiles:"));
+ const char * const * logFile = &kppp_syslog[0];
+ while(*logFile) {
+ list.append(*logFile);
+ logFile++;
+ }
+ return 1;
+ }
+
+ FILE *f = fdopen(fd, "r");
+ while(fgets(buffer, sizeof(buffer), f) != 0) {
+ // pppd line ?
+ p = (char *)strstr(buffer, "pppd[");
+ if(p == 0)
+ continue;
+ pidp = p += strlen("pppd[");
+ while(*p && isdigit(*p))
+ p++;
+ if(*p != ']')
+ continue;
+
+ /* find out pid of pppd */
+ sscanf(pidp, "%d", &newpid);
+ if(newpid != pid) {
+ pid = newpid;
+ list.clear();
+ }
+ if(buffer[strlen(buffer)-1] == '\n')
+ buffer[strlen(buffer)-1] = '\0';
+ list.append(buffer);
+ }
+ close(fd);
+
+ if(list.isEmpty())
+ return 2;
+
+ /* clear security related info */
+
+ const char *keyword[] = {"name = \"",
+ "user=\"",
+ "password=\"",
+ 0};
+
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ QCString tmp = (*it).local8Bit();
+ for(int j = 0; keyword[j] != 0; j++) {
+ char *p;
+
+ if( (p = (char *)strstr(tmp.data(), keyword[j])) != 0) {
+ p += strlen(keyword[j]);
+ while(*p && *p != '"')
+ *p++ = 'X';
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+
+void PPPL_AnalyseLog(QStringList &list, QStringList &result) {
+ QString msg;
+ const char *rmsg = "Remote message: ";
+
+ result.clear();
+
+ // setup the analysis database
+ struct {
+ const char *regexp;
+ const char *answer;
+ } hints[] = {
+ {"Receive serial link is not 8-bit clean",
+ I18N_NOOP("You have launched pppd before the remote server " \
+ "was ready to establish a PPP connection.\n"
+ "Please use the terminal-based login to verify") },
+
+ {"Serial line is looped back",
+ I18N_NOOP("You haven't started the PPP software on the peer system.") },
+
+ {"AP authentication failed",
+ I18N_NOOP("Check that you supplied the correct username and password.")} ,
+
+ {"is locked by pid",
+ I18N_NOOP("You shouldn't pass 'lock' as an argument to pppd. "
+ "Check /etc/ppp/options and ~/.ppprc") },
+
+ {"CP: timeout sending",
+ I18N_NOOP("The remote system does not seem to answer to\n"
+ "configuration request. Contact your provider.") },
+
+ {"unrecognized option",
+ I18N_NOOP("You have passed an invalid option to pppd. See 'man pppd' "
+ "for a complete list of valid arguments.") },
+
+ // terminator
+ {0,0}
+ };
+
+
+ // scan the log for keywords and try to offer any help
+ for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // look for remote message
+ int pos = (*it).find(rmsg);
+ if (pos != -1)
+ {
+ QString str = (*it);
+ str.remove(0, pos + strlen(rmsg));
+ if(!str.isEmpty()) {
+ msg = i18n("Notice that the remote system has sent the following"
+ " message:\n\"%1\"\nThis may give you a hint why the"
+ " the connection has failed.").arg(str);
+ result.append(msg);
+ }
+ }
+
+ // check in the hint database
+ for(uint k = 0; hints[k].regexp != 0; k++) {
+ QRegExp rx(hints[k].regexp);
+ QString l(*it);
+ if(l.contains(rx)) {
+ result.append(i18n(hints[k].answer));
+ break;
+ }
+ }
+ }
+
+ if (result.isEmpty())
+ result.append(i18n("Unable to provide help."));
+}
+
+
+void PPPL_ShowLog() {
+ QStringList sl, result;
+
+ PPPL_MakeLog(sl);
+
+ bool foundConnect = false;
+ bool foundLCP = gpppdata.getPPPDebug();
+ QString lcp = QString::fromLatin1("[LCP");
+ QString conn = QString::fromLatin1("Connect:");
+ QStringList::ConstIterator it = sl.begin();
+ for( ; it != sl.end(); it++) {
+ if((*it).find(lcp) >= 0) {
+ foundLCP = true;
+ break;
+ }
+ if((*it).find(conn) >= 0)
+ foundConnect = true;
+ }
+ if(foundConnect && !foundLCP) {
+ int result = KMessageBox::warningYesNo(0,
+ i18n("KPPP could not prepare a PPP log. It's very likely "
+ "that pppd was started without the \"debug\" option.\n"
+ "Without this option it's difficult to find out PPP "
+ "problems, so you should turn on the debug option.\n"
+ "Shall I turn it on now?"), QString::null, i18n("Restart pppd"), i18n("Do Not Restart"));
+
+ if(result == KMessageBox::Yes) {
+ gpppdata.setPPPDebug(TRUE);
+ KMessageBox::information(0,
+ i18n("The \"debug\" option has been added. You "
+ "should now try to reconnect. If that fails "
+ "again, you will get a PPP log that may help "
+ "you to track down the connection problem."));
+ // return;
+ }
+
+ // return;
+ }
+
+ PPPL_AnalyseLog(sl, result);
+
+ QDialog *dlg = new QDialog(0, "", TRUE);
+
+ dlg->setCaption(i18n("PPP Log"));
+ QVBoxLayout *tl = new QVBoxLayout(dlg, 10, 10);
+ QMultiLineEdit *edit = new QMultiLineEdit(dlg);
+ edit->setReadOnly(TRUE);
+ QLabel *label = new QLabel(i18n("kppp's diagnosis (just guessing):"), dlg);
+ QMultiLineEdit *diagnosis = new QMultiLineEdit(dlg);
+ diagnosis->setReadOnly(TRUE);
+ KButtonBox *bbox = new KButtonBox(dlg);
+ bbox->addStretch(1);
+ QPushButton *write = bbox->addButton(i18n("Write to File"));
+ QPushButton *close = bbox->addButton(KStdGuiItem::close());
+ bbox->layout();
+ edit->setMinimumSize(600, 250);
+ label->setMinimumSize(600, 15);
+ diagnosis->setMinimumSize(600, 60);
+
+ tl->addWidget(edit, 1);
+ tl->addWidget(label);
+ tl->addWidget(diagnosis, 1);
+ tl->addWidget(bbox);
+ dlg->setFixedSize(dlg->sizeHint());
+
+ for(uint i = 0; i < sl.count(); i++)
+ edit->append(*sl.at(i));
+ for(uint i = 0; i < result.count(); i++)
+ diagnosis->append(*result.at(i));
+
+ dlg->connect(close, SIGNAL(clicked()),
+ dlg, SLOT(reject()));
+ dlg->connect(write, SIGNAL(clicked()),
+ dlg, SLOT(accept()));
+
+ if(dlg->exec()) {
+ QDir d = QDir::home();
+ QString s = d.absPath() + "/PPP-logfile";
+ int old_umask = umask(0077);
+
+ FILE *f = fopen(QFile::encodeName(s), "w");
+ for(uint i = 0; i < sl.count(); i++)
+ fprintf(f, "%s\n", (*sl.at(i)).local8Bit().data());
+ fclose(f);
+ umask(old_umask);
+
+ QString msg = i18n("The PPP log has been saved\nas \"%1\"!\n\nIf you want to send a bug report, or have\nproblems connecting to the Internet, please\nattach this file. It will help the maintainers\nto find the bug and to improve KPPP").arg(s);
+ KMessageBox::information(0, msg);
+ }
+ delete dlg;
+}
diff --git a/kppp/ppplog.h b/kppp/ppplog.h
new file mode 100644
index 00000000..5de9484b
--- /dev/null
+++ b/kppp/ppplog.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * (c) 1998 Mario Weilguni <mweilguni@kde.org>
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PPPL__H__
+#define __PPPL__H__
+
+class QStringList;
+
+int PPPL_MakeLog(QStringList &list);
+void PPPL_AnalyseLog(QStringList &list, QStringList &result);
+void PPPL_ShowLog();
+
+#endif
+
diff --git a/kppp/pppstatdlg.cpp b/kppp/pppstatdlg.cpp
new file mode 100644
index 00000000..d466f305
--- /dev/null
+++ b/kppp/pppstatdlg.cpp
@@ -0,0 +1,428 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include <qlayout.h>
+#include <qpainter.h>
+#include <kwin.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <qlabel.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+#include <qlineedit.h>
+#include <qtimer.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include "pppdata.h"
+#include "pppstatdlg.h"
+#include "iplined.h"
+#include "pppstats.h"
+
+extern PPPData gpppdata;
+
+PPPStatsDlg::PPPStatsDlg(QWidget *parent, const char *name, QWidget *,
+ PPPStats *st)
+ : QWidget(parent, name, 0),
+ stats(st)
+{
+ int i;
+ max = 1024;
+
+ setCaption(i18n("kppp Statistics"));
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ QVBoxLayout *tl = new QVBoxLayout(this, 10);
+ QGridLayout *l1 = new QGridLayout(4, 4);
+ tl->addLayout(l1, 1);
+ box = new QGroupBox(i18n("Statistics"), this);
+ l1->addMultiCellWidget(box, 0, 3, 0, 3);
+ l1->addRowSpacing(0, fontMetrics().lineSpacing() - 10);
+ l1->setRowStretch(1, 1);
+ l1->setColStretch(1, 1);
+
+ // inner part of the grid
+ QVBoxLayout *l11 = new QVBoxLayout;
+ l1->addLayout(l11, 1, 1);
+
+ // modem pixmap and IP labels
+ QHBoxLayout *l111 = new QHBoxLayout;
+ l11->addLayout(l111);
+
+ big_modem_both_pixmap = UserIcon("modemboth");
+ big_modem_left_pixmap = UserIcon("modemleft");
+ big_modem_right_pixmap = UserIcon("modemright");
+ big_modem_none_pixmap = UserIcon("modemnone");
+
+ pixmap_l = new QLabel(this);
+ pixmap_l->setMinimumSize(big_modem_both_pixmap.size());
+ l111->addWidget(pixmap_l, 1);
+ pixmap_l->setAlignment(AlignVCenter|AlignLeft);
+
+ QGridLayout *l1112 = new QGridLayout(3, 2);
+ l111->addLayout(l1112);
+
+ ip_address_label1 = new QLabel(this);
+ ip_address_label1->setText(i18n("Local Addr:"));
+
+ ip_address_label2 = new IPLineEdit(this);
+ ip_address_label2->setFocusPolicy(QWidget::NoFocus);
+
+ ip_address_label3 = new QLabel(this);
+ ip_address_label3->setText(i18n("Remote Addr:"));
+
+ ip_address_label4 = new IPLineEdit(this);
+ ip_address_label4->setFocusPolicy(QWidget::NoFocus);
+
+ l1112->addWidget(ip_address_label1, 0, 0);
+ l1112->addWidget(ip_address_label2, 0, 1);
+ l1112->addWidget(ip_address_label3, 1, 0);
+ l1112->addWidget(ip_address_label4, 1, 1);
+
+ // consumes space on bottom
+ l1112->setRowStretch(2, 1);
+
+ QGridLayout *l112 = new QGridLayout(5, 4);
+ l11->addLayout(l112);
+ for(i =0 ; i < 5; i++) {
+ labela1[i] = new QLabel(this);
+
+ labela2[i] = new QLabel(this);
+ labela2[i]->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+
+ labelb1[i] = new QLabel(this);
+
+ labelb2[i] = new QLabel(this);
+ labelb2[i]->setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
+ }
+
+ labela1[0]->setText(i18n("bytes in"));
+ labelb1[0]->setText(i18n("bytes out"));
+
+ labela1[1]->setText(i18n("packets in"));
+ labelb1[1]->setText(i18n("packets out"));
+
+ labela1[2]->setText(i18n("vjcomp in"));
+ labelb1[2]->setText(i18n("vjcomp out"));
+
+ labela1[3]->setText(i18n("vjunc in"));
+ labelb1[3]->setText(i18n("vjunc out"));
+
+ labela1[4]->setText(i18n("vjerr"));
+ labelb1[4]->setText(i18n("non-vj"));
+
+ for(i = 0; i < 5; i++) {
+ labela2[i]->setText("888888888"); // TODO: resize automatically
+ labelb2[i]->setText("888888888");
+ labela2[i]->setAlignment(Qt::AlignRight);
+ labelb2[i]->setAlignment(Qt::AlignRight);
+ labela2[i]->setFixedSize(labela2[i]->sizeHint());
+ labelb2[i]->setFixedSize(labelb2[i]->sizeHint());
+ labela2[i]->setText("");
+ labelb2[i]->setText("");
+
+ // add to layout
+ l112->addWidget(labela1[i], i, 0);
+ l112->addWidget(labela2[i], i, 1);
+ l112->addWidget(labelb1[i], i, 2);
+ l112->addWidget(labelb2[i], i, 3);
+ }
+
+ l112->setColStretch(1, 1);
+ l112->setColStretch(3, 1);
+
+ tl->addSpacing(5);
+ QHBoxLayout *l12 = new QHBoxLayout;
+ tl->addLayout(l12);
+ l12->addStretch(1);
+
+ if(gpppdata.graphingEnabled()) {
+ bool dummy;
+
+ gpppdata.graphingOptions(dummy, bg, text, in, out);
+
+ graph = new QFrame(this);
+ graph->setFrameStyle(QFrame::Box | QFrame::Sunken);
+ l1->addMultiCellWidget(graph, 2, 2, 1, 2);
+ graph->setMinimumWidth(300);
+ graph->setFixedHeight(76+4);
+ graph->setBackgroundColor(bg);
+ }
+
+ cancelbutton = new KPushButton(KStdGuiItem::close(),this, "cancelbutton");
+ cancelbutton->setFocus();
+ connect(cancelbutton, SIGNAL(clicked()), this,SLOT(cancel()));
+ cancelbutton->setFixedHeight(cancelbutton->sizeHint().height());
+ cancelbutton->setMinimumWidth(QMAX(cancelbutton->sizeHint().width(), 70));
+ l12->addWidget(cancelbutton);
+
+ if(gpppdata.graphingEnabled()) {
+ graphTimer = new QTimer(this);
+ connect(graphTimer, SIGNAL(timeout()), SLOT(updateGraph()));
+ }
+
+ setFixedSize(sizeHint());
+
+ connect(stats, SIGNAL(statsChanged(int)), SLOT(paintIcon(int)));
+
+ // read window position from config file
+ int p_x, p_y;
+ gpppdata.winPosStatWin(p_x, p_y);
+ move(p_x, p_y);
+}
+
+
+PPPStatsDlg::~PPPStatsDlg() {
+}
+
+
+// save window position when window was closed
+bool PPPStatsDlg::event(QEvent *e) {
+ if (e->type() == QEvent::Hide)
+ {
+ gpppdata.setWinPosStatWin(x(), y());
+ return true;
+ }
+ else
+ return QWidget::event(e);
+}
+
+void PPPStatsDlg::cancel() {
+ hide();
+}
+
+
+void PPPStatsDlg::take_stats() {
+ stats->initStats();
+ bin_last = stats->ibytes;
+ bout_last = stats->obytes;
+ ringIdx = 0;
+ for(int i = 0; i < MAX_GRAPH_WIDTH; i++) {
+ bin[i] = -1;
+ bout[i] = -1;
+ }
+
+ update_data();
+
+ stats->start();
+ if(gpppdata.graphingEnabled())
+ graphTimer->start(GRAPH_UPDATE_TIME);
+}
+
+
+void PPPStatsDlg::stop_stats() {
+ stats->stop();
+ if(gpppdata.graphingEnabled())
+ graphTimer->stop();
+}
+
+void PPPStatsDlg::paintGraph() {
+ // why draw that stuff if not visible?
+ if(!isVisible())
+ return;
+
+ QPixmap pm(graph->width() - 4, graph->height() - 4);
+ QPainter p;
+ pm.fill(graph->backgroundColor());
+ p.begin(&pm);
+
+ int x;
+ int idx = ringIdx - pm.width() + 1;
+ if(idx < 0)
+ idx += MAX_GRAPH_WIDTH;
+
+ // find good scaling factor
+ int last_h_in =
+ pm.height() - (int)((float)bin[idx]/max * (pm.height() - 8))-1;
+ int last_h_out =
+ pm.height() - (int)((float)bout[idx]/max * (pm.height() - 8))-1;
+
+ // plot data
+ int last_idx = 0;
+ for(x = 1; x < pm.width(); x++) {
+ int h_in, h_out;
+
+ h_in = pm.height() - (int)((float)bin[idx]/max * (pm.height() - 8))-1;
+ h_out = pm.height() - (int)((float)bout[idx]/max * (pm.height() - 8))-1;
+
+ p.setPen(out);
+ if(bout[idx]!=-1)
+ p.drawLine(x-1, last_h_out, x, h_out);
+ p.setPen(in);
+ if(bin[idx]!=-1)
+ p.drawLine(x-1, last_h_in, x, h_in);
+ last_h_in = h_in;
+ last_h_out = h_out;
+
+ last_idx = idx;
+ idx = (idx + 1) % MAX_GRAPH_WIDTH;
+ }
+
+ // take last value
+ int last_max = bin[last_idx]>bout[last_idx] ? bin[last_idx] : bout[last_idx];
+
+ // plot scale line
+ p.setPen(text);
+ p.setFont(QFont(KGlobalSettings::fixedFont().family(), 8));
+
+ QRect r;
+ QString s = i18n("%1 (max. %2) kb/sec")
+ .arg(KGlobal::locale()->formatNumber((float)last_max / 1024.0, 1))
+ .arg(KGlobal::locale()->formatNumber((float)max / 1024.0, 1));
+ p.drawText(0, 0, pm.width(), 2*8, AlignRight|AlignVCenter, s, -1, &r);
+ p.drawLine(0, 8, r.left() - 8, 8);
+
+ p.end();
+ bitBlt(graph, 2, 2, &pm, 0, 0, pm.width(), pm.height(), CopyROP);
+}
+
+void PPPStatsDlg::updateGraph() {
+ bin[ringIdx] = stats->ibytes - bin_last;
+ bout[ringIdx] = stats->obytes - bout_last;
+ if(bin[ringIdx] > max)
+ max = ((bin[ringIdx] / 1024) + 1) * 1024;
+
+ if(bout[ringIdx] > max)
+ max = ((bout[ringIdx] / 1024) + 1) * 1024;
+
+ bin_last = stats->ibytes;
+ bout_last = stats->obytes;
+ ringIdx = (ringIdx + 1) % MAX_GRAPH_WIDTH;
+ paintGraph();
+}
+
+
+void PPPStatsDlg::paintEvent (QPaintEvent *) {
+ paintIcon(PPPStats::BytesNone); // correct ?
+ if(gpppdata.graphingEnabled())
+ paintGraph();
+}
+
+
+void PPPStatsDlg::paintIcon(int status) {
+
+ const QPixmap *pixmap;
+
+ switch(status)
+ {
+ case PPPStats::BytesIn:
+ pixmap = &big_modem_left_pixmap;
+ break;
+ case PPPStats::BytesOut:
+ pixmap = &big_modem_right_pixmap;
+ break;
+ case PPPStats::BytesBoth:
+ pixmap = &big_modem_both_pixmap;
+ break;
+ case PPPStats::BytesNone:
+ default:
+ pixmap = &big_modem_none_pixmap;
+ break;
+ }
+
+ bitBlt(pixmap_l, 0, 0, pixmap);
+
+ update_data();
+}
+
+
+void PPPStatsDlg::timeclick() {
+ // volume accounting
+ switch(gpppdata.VolAcctEnabled()) {
+ case 0: // no accounting
+ break;
+
+ case 1: // bytes in
+ stats->totalbytes = gpppdata.totalBytes() + stats->ibytes;
+ break;
+
+ case 2:
+ stats->totalbytes = gpppdata.totalBytes() + stats->obytes;
+ break;
+
+ case 3:
+ stats->totalbytes = gpppdata.totalBytes() + stats->ibytes + stats->obytes;
+ break;
+ }
+}
+
+
+void PPPStatsDlg::closeEvent(QCloseEvent *) {
+ emit cancel();
+}
+
+
+void PPPStatsDlg::update_data() {
+ timeclick();
+
+ ibytes_string = KGlobal::locale()->formatNumber(stats->ibytes, 0);
+ ipackets_string.setNum(stats->ipackets);
+ compressedin_string.setNum(stats->compressedin);
+ uncompressedin_string.setNum(stats->uncompressedin);
+ errorin_string.setNum(stats->errorin);
+ obytes_string = KGlobal::locale()->formatNumber(stats->obytes, 0);
+ opackets_string.setNum(stats->opackets);
+ compressed_string.setNum(stats->compressed);
+ packetsunc_string.setNum(stats->packetsunc);
+ packetsoutunc_string.setNum(stats->packetsoutunc);
+
+ labela2[0]->setText(ibytes_string);
+ labela2[1]->setText(ipackets_string);
+ labela2[2]->setText(compressedin_string);
+ labela2[3]->setText(uncompressedin_string);
+ labela2[4]->setText(errorin_string);
+
+ labelb2[0]->setText(obytes_string);
+ labelb2[1]->setText(opackets_string);
+ labelb2[2]->setText(compressed_string);
+ labelb2[3]->setText(packetsunc_string);
+ labelb2[4]->setText(packetsoutunc_string);
+
+ // if I don't resort to this trick it is imposible to
+ // copy/paste the ip out of the lineedits due to
+ // reset of cursor position on setText()
+ QString local_addr = ( stats->local_ip_address.isEmpty() ?
+ i18n("unavailable") :
+ stats->local_ip_address );
+
+ if( ip_address_label2->text() != local_addr )
+ ip_address_label2->setText(local_addr);
+
+ QString remote_addr = ( stats->remote_ip_address.isEmpty() ?
+ i18n("unavailable") :
+ stats->remote_ip_address );
+
+ if( ip_address_label4->text() != remote_addr )
+ ip_address_label4->setText(remote_addr);
+}
+
+
+void PPPStatsDlg::toCurrentDesktop() {
+ KWin::setOnDesktop(winId(), KWin::currentDesktop());
+}
+
+#include "pppstatdlg.moc"
+
diff --git a/kppp/pppstatdlg.h b/kppp/pppstatdlg.h
new file mode 100644
index 00000000..cb80cc96
--- /dev/null
+++ b/kppp/pppstatdlg.h
@@ -0,0 +1,131 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PPPSTATSDLG_H_
+#define _PPPSTATSDLG_H_
+
+
+#include <qdialog.h>
+#include <qevent.h>
+#include <qpixmap.h>
+
+class QLabel;
+class QFrame;
+class QGroupBox;
+class QLineEdit;
+class QTimer;
+class QPushButton;
+
+const int MAX_GRAPH_WIDTH = 600;
+const int GRAPH_UPDATE_TIME = 1000;
+
+class PPPStats;
+
+class PPPStatsDlg : public QWidget {
+
+ Q_OBJECT
+
+public:
+
+ PPPStatsDlg(QWidget *parent, const char *name,QWidget *main,
+ PPPStats *st);
+ ~PPPStatsDlg();
+
+protected:
+
+ void closeEvent( QCloseEvent *e );
+ void paintEvent (QPaintEvent *e) ;
+ bool event( QEvent *e );
+
+public slots:
+
+ void timeclick();
+ void cancel();
+ void take_stats();
+ void stop_stats();
+ void paintIcon(int);
+ void updateGraph();
+
+public:
+
+ void update_data();
+ void toCurrentDesktop();
+
+private:
+ void paintGraph();
+ PPPStats *stats;
+
+ QLabel *pixmap_l;
+ QWidget *main;
+ QPushButton *cancelbutton;
+
+ QLabel *labela1[5];
+ QLabel *labela2[5];
+ QLabel *labelb1[5];
+ QLabel *labelb2[5];
+
+ QLabel *ip_address_label1;
+ QLineEdit *ip_address_label2;
+ QLabel *ip_address_label3;
+ QLineEdit *ip_address_label4;
+
+ QLabel *modem_pic_label;
+ QPixmap modem_pixmap;
+ QPixmap modem_left_pixmap;
+ QPixmap modem_right_pixmap;
+ QPixmap modem_both_pixmap;
+ QPixmap big_modem_both_pixmap;
+ QPixmap big_modem_left_pixmap;
+ QPixmap big_modem_right_pixmap;
+ QPixmap big_modem_none_pixmap;
+
+ int ibytes_last;
+ int obytes_last;
+ bool need_to_paint;
+
+ QString ibytes_string;
+ QString ipackets_string;
+ QString compressedin_string;
+ QString uncompressedin_string;
+ QString errorin_string;
+ QString obytes_string;
+ QString opackets_string;
+ QString compressed_string;
+ QString packetsunc_string;
+ QString packetsoutunc_string;
+ QGroupBox *box;
+
+ // graph widget
+ QFrame *graph;
+ int bin[MAX_GRAPH_WIDTH];
+ int bout[MAX_GRAPH_WIDTH];
+ int bin_last;
+ int bout_last;
+ int ringIdx;
+ int max;
+ QTimer *graphTimer;
+ QColor bg, text, in, out;
+};
+
+#endif
diff --git a/kppp/pppstats.cpp b/kppp/pppstats.cpp
new file mode 100644
index 00000000..b5199e60
--- /dev/null
+++ b/kppp/pppstats.cpp
@@ -0,0 +1,343 @@
+/*
+ *
+ * $Id$
+ *
+ * History:
+ *
+ * Bernd Wuebben, wuebben@math.cornell.edu:
+ *
+ * Much of this is taken from the pppd sources in particular
+ * /pppstat/pppstat.c, and modified to suit the needs of kppp.
+ *
+ *
+ * Here the original history of pppstat.c:
+ *
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ *
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jaconson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+
+#include <config.h>
+
+#include <ctype.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#ifdef __DragonFly__
+#include <net/ppp_layer/ppp_defs.h>
+#else
+#include <net/ppp_defs.h>
+#endif
+
+#include "config.h"
+#include "pppstats.h"
+
+#ifndef STREAMS
+ #if defined(__linux__) && defined(__powerpc__) \
+ && (__GLIBC__ == 2 && __GLIBC_MINOR__ == 0)
+ /* kludge alert! */
+ #undef __GLIBC__
+ #endif
+ #include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+ #ifndef HAVE_NET_IF_PPP_H
+ #ifdef HAVE_LINUX_IF_PPP_H
+ #include <linux/if.h>
+ #include <linux/if_ppp.h>
+ #elif defined(__DragonFly__)
+ #include <net/if.h>
+ #include <net/ppp/if_ppp.h>
+ #endif
+ #else
+ #include <net/if.h>
+ #include <net/if_ppp.h>
+ #endif
+
+#else /* STREAMS */
+ #include <sys/socket.h>
+ #include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
+ #include <net/ppp_defs.h>
+ #include <net/pppio.h>
+ #include <net/if.h>
+ #include <sys/sockio.h>
+
+#endif /* STREAMS */
+
+#include <qtimer.h>
+#include <kdebug.h>
+
+PPPStats::PPPStats()
+{
+ clear();
+ timer = new QTimer;
+ connect(timer, SIGNAL(timeout()), SLOT(timerClick()));
+}
+
+
+PPPStats::~PPPStats() {
+ stop();
+ delete timer;
+}
+
+
+void PPPStats::clear()
+{
+ ibytes = 0;
+ ipackets = 0;
+ ibytes_last = 0;
+ obytes_last = 0;
+ compressedin = 0;
+ uncompressedin = 0;
+ errorin = 0;
+ obytes = 0;
+ opackets = 0;
+ compressed = 0;
+ packetsunc = 0;
+ packetsoutunc = 0;
+ ioStatus = BytesNone;
+}
+
+void PPPStats::timerClick() {
+ enum IOStatus newStatus;
+
+ doStats();
+
+ if((ibytes != ibytes_last) && (obytes != obytes_last))
+ newStatus = BytesBoth;
+ else if(ibytes != ibytes_last)
+ newStatus = BytesIn;
+ else if(obytes != obytes_last)
+ newStatus = BytesOut;
+ else
+ newStatus = BytesNone;
+
+ if(newStatus != ioStatus)
+ emit statsChanged(ioStatus = newStatus);
+
+ ibytes_last = ibytes;
+ obytes_last = obytes;
+}
+
+void PPPStats::setUnit(int u) {
+ unit = u;
+ sprintf(unitName, "ppp%d", unit);
+}
+
+
+void PPPStats::start() {
+ timer->start(PPP_STATS_INTERVAL);
+}
+
+
+void PPPStats::stop() {
+ emit statsChanged(BytesNone);
+ timer->stop();
+}
+
+
+bool PPPStats::ifIsUp() {
+ bool is_up;
+ struct ifreq ifr;
+
+#if defined(__svr4__ )
+ usleep(1000000); // Needed for Solaris ?!
+#endif
+
+#ifdef STREAMS
+ if ((t = open("/dev/ppp", O_RDONLY)) < 0) {
+ perror("pppstats: Couldn't open /dev/ppp: ");
+ return false;
+ }
+ if (!strioctl(t, PPPIO_ATTACH, (char*)&unit, sizeof(int), 0)) {
+ fprintf(stderr, "pppstats: ppp%d is not available\n", unit);
+ ::close(t);
+ return false;
+ }
+ // TODO: close t somewhere again
+#endif
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("Couldn't create IP socket");
+ return false;
+ }
+
+ strlcpy(ifr.ifr_name, unitName, sizeof(ifr.ifr_name));
+
+ if(ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ if (errno)
+ fprintf(stderr, "Couldn't find interface %s: %s\n",
+ unitName, strerror(errno));
+ ::close(s);
+ s = 0;
+ return 0;
+ }
+
+ if ((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING)) {
+ is_up = true;
+ kdDebug(5002) << "Interface is up" << endl;
+ }
+ else{
+ is_up = false;
+ ::close(s);
+ s = 0;
+ kdDebug(5002) << "Interface is down" << endl;
+ }
+
+ return is_up;
+}
+
+
+bool PPPStats::initStats() {
+
+ struct sockaddr_in *sinp;
+ struct ifreq ifr;
+
+ clear();
+
+ strlcpy(ifr.ifr_name, unitName, sizeof(ifr.ifr_name));
+
+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+ }
+
+ sinp = (struct sockaddr_in*)&ifr.ifr_addr;
+
+ if(sinp->sin_addr.s_addr)
+ local_ip_address = inet_ntoa(sinp->sin_addr);
+ else
+ local_ip_address = "";
+ kdDebug(5002) << "Local IP: " << local_ip_address << endl;
+
+ (void) ioctl(s, SIOCGIFDSTADDR, &ifr);
+
+ sinp = (struct sockaddr_in*)&ifr.ifr_dstaddr;
+
+ if(sinp->sin_addr.s_addr)
+ remote_ip_address = inet_ntoa(sinp->sin_addr);
+ else
+ remote_ip_address = "";
+ kdDebug(5002) << "Remote IP: " << remote_ip_address << endl;
+
+ return true;
+
+}
+
+
+bool PPPStats::doStats() {
+ struct ppp_stats cur;
+
+ if(! get_ppp_stats(&cur)){
+ return false;
+ }
+
+ // "in" "pack" "comp" "uncomp" "err"
+ // IN PACK VJCOMP VJUNC VJERR
+
+ ibytes = cur.p.ppp_ibytes; // bytes received
+ ipackets = cur.p.ppp_ipackets; // packets recieved
+ compressedin = cur.vj.vjs_compressedin; // inbound compressed packets
+ uncompressedin = cur.vj.vjs_uncompressedin; // inbound uncompressed packets
+ errorin = cur.vj.vjs_errorin; //receive errors
+
+ // "out" "pack" "comp" "uncomp" "ip"
+ // OUT PACK JCOMP VJUNC NON-VJ
+
+ obytes = cur.p.ppp_obytes; // raw bytes sent
+ opackets = cur.p.ppp_opackets; // packets sent
+ compressed = cur.vj.vjs_compressed; //outbound compressed packets
+
+ // outbound packets - outbound compressed packets
+ packetsunc = cur.vj.vjs_packets - cur.vj.vjs_compressed;
+
+ // packets sent - oubount compressed
+ packetsoutunc = cur.p.ppp_opackets - cur.vj.vjs_packets;
+
+ return true;
+}
+
+
+#ifndef STREAMS
+bool PPPStats::get_ppp_stats(struct ppp_stats *curp){
+
+ struct ifpppstatsreq req;
+
+ if(s==0)
+ return false;
+
+#ifdef __linux__
+ req.stats_ptr = (caddr_t) &req.stats;
+ sprintf(req.ifr__name, "ppp%d", unit);
+#else
+ sprintf(req.ifr_name, "ppp%d", unit);
+#endif
+ if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
+ if (errno == ENOTTY)
+ fprintf(stderr, "pppstats: kernel support missing\n");
+ else
+ perror("ioctl(SIOCGPPPSTATS)");
+ return false;
+ }
+ *curp = req.stats;
+ return true;
+}
+
+#else /* STREAMS */
+bool PPPStats::get_ppp_stats( struct ppp_stats *curp){
+
+ if (!strioctl(t, PPPIO_GETSTAT, (char*)curp, 0, sizeof(*curp))) {
+ if (errno == EINVAL)
+ fprintf(stderr, "pppstats: kernel support missing\n");
+ else
+ perror("pppstats: Couldn't get statistics");
+ return false;
+ }
+ return true;
+}
+
+bool PPPStats::strioctl(int fd, int cmd, char* ptr, int ilen, int olen){
+
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return false;
+ if (str.ic_len != olen)
+ fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return true;
+}
+#endif /* STREAMS */
+
+#include "pppstats.moc"
+
diff --git a/kppp/pppstats.h b/kppp/pppstats.h
new file mode 100644
index 00000000..2757bca5
--- /dev/null
+++ b/kppp/pppstats.h
@@ -0,0 +1,84 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+ * (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+ * (c) 1998-1999 Harri Porten <porten@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PPPSTATS_H_
+#define _PPPSTATS_H_
+
+
+#include "kpppconfig.h"
+#include <qobject.h>
+
+class QTimer;
+
+class PPPStats : public QObject {
+ Q_OBJECT
+public:
+ PPPStats();
+ ~PPPStats();
+ void clear();
+ bool initStats();
+ bool doStats();
+ bool ifIsUp();
+ void setUnit(int u);
+ void start();
+ void stop();
+
+signals:
+ void statsChanged(int);
+
+private slots:
+ void timerClick();
+
+public:
+ int ibytes, obytes;
+ int totalbytes;
+ int ipackets, opackets;
+ int compressedin;
+ int uncompressedin;
+ int compressed;
+ int errorin;
+ int packetsunc, packetsoutunc;
+
+ QString local_ip_address;
+ QString remote_ip_address;
+
+ enum IOStatus { BytesNone = 0, BytesIn, BytesOut, BytesBoth };
+
+private:
+ bool get_ppp_stats(struct ppp_stats *curp);
+ bool strioctl(int fd, int cmd, char* ptr,int ilen, int olen);
+
+ int ibytes_last, obytes_last;
+ int s; // socket file descriptor
+#ifdef STREAMS
+ int t;
+#endif
+ int unit;
+ char unitName[5];
+ enum IOStatus ioStatus;
+ QTimer *timer;
+};
+
+#endif
diff --git a/kppp/providerdb.cpp b/kppp/providerdb.cpp
new file mode 100644
index 00000000..e6885e44
--- /dev/null
+++ b/kppp/providerdb.cpp
@@ -0,0 +1,473 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qregexp.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include "providerdb.h"
+#include "newwidget.h"
+#include "pppdata.h"
+#include <qlistbox.h>
+#include <qlineedit.h>
+#include <ksimpleconfig.h>
+
+
+#define UNENCODED_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
+
+QWizard* ProviderDB::wiz = 0L;
+
+ProviderDB::ProviderDB(QWidget *parent) :
+ KWizard(parent, "", true),
+ cfg(0)
+{
+ setCaption(i18n("Create New Account"));
+
+ wiz = this;
+
+ page1 = new PDB_Intro(this);
+ addPage(page1, "");
+ setHelpEnabled(page1, false);
+ // TODO p1->w->setFocusPolicy(StrongFocus);
+
+ page2 = new PDB_Country(this);
+ addPage(page2, "");
+ setHelpEnabled(page2, false);
+
+ page3 = new PDB_Provider(this);
+ addPage(page3, "");
+ setHelpEnabled(page3, false);
+
+ page4 = new PDB_UserInfo(this);
+ addPage(page4, "");
+ setHelpEnabled(page4, false);
+
+ page5 = new PDB_DialPrefix(this);
+ addPage(page5, "");
+ setHelpEnabled(page5, false);
+
+ page9 = new PDB_Finished(this);
+ addPage(page9, "");
+ setHelpEnabled(page9, false);
+ setFinish(page9, true);
+ setFinishEnabled(page9, true);
+
+ connect((const QObject *)nextButton(), SIGNAL(clicked()),
+ this, SLOT(pageSelected()));
+ connect((const QObject *)backButton(), SIGNAL(clicked()),
+ this, SLOT(pageSelected()));
+
+ // resize(minimumSize());
+ adjustSize();
+}
+
+
+ProviderDB::~ProviderDB() {
+ delete cfg;
+}
+
+
+void ProviderDB::pageSelected() {
+ bool prev = true;
+ bool next = true;
+
+ QWidget *page = currentPage();
+ if(page == page2) {
+ next = page2->lb->currentItem() != -1;
+ } else if(page == page3) {
+ page3->setDir(*page2->list->at(page2->lb->currentItem()));
+ next = page3->lb->currentItem() != -1;
+ } else if(page == page4) {
+ loadProviderInfo();
+ next = !page4->username().isEmpty() &&
+ !page4->password().isEmpty();
+ }
+
+ setBackEnabled(page, prev);
+ setNextEnabled(page, next);
+}
+
+
+void ProviderDB::loadProviderInfo() {
+ delete cfg;
+
+ QString loc = *page2->list->at(page2->lb->currentItem());
+ QString provider = page3->lb->text(page3->lb->currentItem());
+ urlEncode(provider);
+ QString prov = "Provider/" + loc;
+ prov += "/" + provider;
+ QString fname = locate("appdata", prov);
+ kdDebug(5002) << "Providerfile=" << fname << endl;
+
+ cfg = new KSimpleConfig(fname, true);
+}
+
+
+void ProviderDB::accept() {
+ QRegExp re_username("%USERNAME%");
+ QRegExp re_password("%PASSWORD%");
+
+ QMap <QString, QString> map(cfg->entryMap("<default>"));
+ QMap <QString, QString>::Iterator it(map.begin());
+ while(it != map.end()) {
+ QString key = it.key();
+ QString value = *it;
+ if(value.contains(re_username))
+ value.replace(re_username, page4->username());
+
+ if(value.contains(re_password))
+ value.replace(re_password, page4->password());
+
+ gpppdata.writeConfig(gpppdata.currentAccountGroup(), key, value);
+
+ if(key == "Name")
+ gpppdata.setAccname(value);
+ ++it;
+ }
+
+ gpppdata.writeConfig(gpppdata.currentAccountGroup(), "DialPrefix", page5->prefix());
+ done(Accepted);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_Intro::PDB_Intro(QWidget *parent) : QWidget(parent) {
+ QLabel *l = new QLabel(i18n("You will be asked a few questions on information\n"
+ "which is needed to establish an Internet connection\n"
+ "with your Internet Service Provider (ISP).\n\n"
+ "Make sure you have the registration form from your\n"
+ "ISP handy. If you have any problems, try the online\n"
+ "help first. If any information is missing, contact\n"
+ "your ISP."),
+ this);
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ tl->addWidget(l);
+ tl->activate();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_Country::PDB_Country(QWidget *parent) : QWidget(parent) {
+ QLabel *l = new QLabel(i18n("Select the location where you plan to use this\n"
+ "account from the list below. If your country or\n"
+ "location is not listed, you have to create the\n"
+ "account with the normal, dialog based setup.\n\n"
+ "If you click \"Cancel\", the dialog based setup\n"
+ "will start."),
+ this);
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ tl->addWidget(l);
+
+ QHBoxLayout *l1 = new QHBoxLayout;
+ tl->addLayout(l1);
+ l1->addStretch(1);
+
+ lb = new QListBox(this);
+ connect(lb, SIGNAL(highlighted(int)),
+ this, SLOT(selectionChanged(int)));
+ lb->setMinimumSize(220, 100);
+ l1->addWidget(lb, 2);
+ l1->addStretch(1);
+
+ list = new QStringList;
+
+ // fill the listbox
+ // set up filter
+ QDir d(KGlobal::dirs()->findDirs("appdata", "Provider").first());
+ d.setFilter(QDir::Dirs);
+ d.setSorting(QDir::Name);
+
+ // read the list of files
+ const QFileInfoList *flist = d.entryInfoList();
+ if(flist) {
+ QFileInfoListIterator it( *flist );
+ QFileInfo *fi;
+ // traverse the flist and insert into a map for sorting
+ QMap<QString, QString> countries;
+ for(; (fi = it.current()) != 0; ++it) {
+ if(fi->fileName() != "." && fi->fileName() != "..") {
+ QString dirFile(fi->absFilePath()+"/.directory");
+ QString entryName;
+ if(QFile::exists(dirFile)){
+ KSimpleConfig config(dirFile);
+ config.setDesktopGroup();
+ entryName = config.readEntry("Name");
+ }
+ if (entryName.isNull()) entryName = fi->fileName();
+ countries.insert(entryName, fi->fileName());
+ }
+ }
+ // insert sorted entries into list box and string list
+ QMap<QString, QString>::const_iterator mit = countries.begin();
+ QMap<QString, QString>::const_iterator mend = countries.end();
+ while(mit != mend) {
+ lb->insertItem(mit.key());
+ list->append(*mit);
+ ++mit;
+ }
+ }
+
+ tl->activate();
+}
+
+PDB_Country::~PDB_Country()
+{
+ delete list;
+}
+
+void PDB_Country::selectionChanged(int idx) {
+ // QWizard *wizard = (QWizard *)parent(); Why doesn't this work ?
+ ProviderDB::wiz->setNextEnabled(this, (idx != -1));
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_Provider::PDB_Provider(QWidget *parent) : QWidget(parent) {
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QLabel *l = new QLabel(i18n("Select your Internet Service Provider (ISP) from\n"
+ "the list below. If the ISP is not in this list,\n"
+ "you have to click on \"Cancel\" and create this\n"
+ "account using the normal, dialog-based setup.\n\n"
+ "Click on \"Next\" when you have finished your\n"
+ "selection."), this);
+ tl->addWidget(l);
+
+ QHBoxLayout *l1 = new QHBoxLayout;
+ tl->addLayout(l1);
+ l1->addStretch(1);
+
+ lb = new QListBox(this);
+ connect(lb, SIGNAL(highlighted(int)),
+ this, SLOT(selectionChanged(int)));
+ lb->setMinimumSize(220, 100);
+ l1->addWidget(lb, 2);
+ l1->addStretch(1);
+}
+
+void PDB_Provider::selectionChanged(int idx) {
+ ProviderDB::wiz->setNextEnabled(this, idx != -1);
+}
+
+
+void PDB_Provider::setDir(const QString &_dir) {
+ if(dir != _dir) {
+ lb->clear();
+
+ // fill the listbox
+ // set up filter
+ dir = _dir;
+
+ QString dir1 = KGlobal::dirs()->findDirs("appdata", "Provider").first();
+ QRegExp re1(" ");
+ dir = dir.replace(re1, "_");
+ dir1 += dir;
+
+ QDir d(dir1);
+ d.setFilter(QDir::Files);
+ d.setSorting(QDir::Name);
+
+ // read the list of files
+ const QFileInfoList *list = d.entryInfoList();
+ QFileInfoListIterator it( *list );
+ QFileInfo *fi;
+
+ // traverse the list and insert into the widget
+ QRegExp re("_");
+ while((fi = it.current()) != NULL) {
+ QString fname = fi->fileName();
+ if(fname.length() && fname[0] != '.') {
+ urlDecode(fname);
+ lb->insertItem(fname);
+ }
+ ++it;
+ }
+
+ // TODO: Qt 1.x needs this if list is empty
+ lb->update();
+ }
+}
+
+
+QString PDB_Provider::getDir() {
+ return dir;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_UserInfo::PDB_UserInfo(QWidget *parent) : QWidget(parent) {
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QLabel *l = new QLabel(i18n("To log on to your ISP, kppp needs the username\n"
+ "and the password you got from your ISP. Type\n"
+ "in this information in the fields below.\n\n"
+ "Word case is important here."),
+ this);
+ tl->addWidget(l);
+
+ QGridLayout *l1 = new QGridLayout(2, 2);
+ tl->addLayout(l1);
+ l = new QLabel(i18n("Username:"), this);
+ l1->addWidget(l, 0, 0);
+ l = new QLabel(i18n("Password:"), this);
+ l1->addWidget(l, 1, 0);
+ _username = newLineEdit(24, this);
+ connect(_username, SIGNAL(textChanged(const QString &)),
+ this, SLOT(textChanged(const QString &)));
+ l1->addWidget(_username, 0, 1);
+ _password = newLineEdit(24, this);
+ _password->setEchoMode(QLineEdit::Password);
+ connect(_password, SIGNAL(textChanged(const QString &)),
+ this, SLOT(textChanged(const QString &)));
+ l1->addWidget(_password, 1, 1);
+ tl->activate();
+}
+
+
+void PDB_UserInfo::textChanged(const QString &) {
+ ProviderDB::wiz->setNextEnabled(this, !_password->text().isEmpty() &&
+ !_username->text().isEmpty());
+}
+
+
+QString PDB_UserInfo::username() {
+ QString s = _username->text();
+ return s;
+}
+
+
+QString PDB_UserInfo::password() {
+ QString s = _password->text();
+ return s;
+}
+
+
+void PDB_UserInfo::activate() {
+ _username->setFocus();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_DialPrefix::PDB_DialPrefix(QWidget *parent) : QWidget(parent) {
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QLabel *l = new QLabel(i18n("If you need a special dial prefix (e.g. if you\n"
+ "are using a telephone switch) you can specify\n"
+ "it here. This prefix is dialed just before the\n"
+ "phone number.\n\n"
+ "If you have a telephone switch, you probably need\n"
+ "to write \"0\" or \"0,\" here."),
+ this);
+ tl->addWidget(l);
+
+ QGridLayout *l1 = new QGridLayout(1, 2);
+ tl->addLayout(l1);
+ l = new QLabel(i18n("Dial prefix:"), this);
+ l1->addWidget(l, 0, 0);
+ _prefix = newLineEdit(24, this);
+ l1->addWidget(_prefix, 0, 1);
+ tl->activate();
+}
+
+
+QString PDB_DialPrefix::prefix() {
+ QString s = _prefix->text();
+
+ return s;
+}
+
+
+void PDB_DialPrefix::activate() {
+ _prefix->setFocus();
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+/////////////////////////////////////////////////////////////////////////////
+PDB_Finished::PDB_Finished(QWidget *parent) : QWidget(parent) {
+ QVBoxLayout *tl = new QVBoxLayout(this, 10, 10);
+ QLabel *l = new QLabel(i18n("Finished!\n\n"
+ "A new account has been created. Click \"Finish\" to\n"
+ "go back to the setup dialog. If you want to\n"
+ "check the settings of the newly created account,\n"
+ "you can use \"Edit\" in the setup dialog."),
+ this);
+ tl->addWidget(l);
+ tl->addStretch(1);
+}
+
+
+void urlDecode(QString &s) {
+ QString s1;
+
+ for(uint i = 0; i < s.length(); i++) {
+ if(s[i] == '%') {
+ s1 += 100*s[i+1].digitValue() + 10*s[i+2].digitValue()
+ + s[i+3].digitValue();
+ i += 3;
+ } else {
+ s1 += s[i];
+ }
+ }
+
+ s = s1;
+}
+
+
+void urlEncode(QString &s) {
+ QString s1, tmp;
+
+ for(uint i = 0; i < s.length(); i++) {
+ if(QString(UNENCODED_CHARS).find(s[i]) >= 0)
+ s1 += s[i];
+ else {
+ tmp.sprintf("%%%03i", s[i].unicode());
+ s1 += tmp;
+ }
+ }
+ s = s1;
+}
+
+
+#include "providerdb.moc"
+
diff --git a/kppp/providerdb.h b/kppp/providerdb.h
new file mode 100644
index 00000000..56a5e506
--- /dev/null
+++ b/kppp/providerdb.h
@@ -0,0 +1,152 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#ifndef PROVIDER_DB
+#define PROVIDER_DB
+
+#include <kwizard.h>
+#include <qwidget.h>
+
+class QLineEdit;
+class QListBox;
+class KSimpleConfig;
+
+class PDB_Intro : public QWidget {
+ Q_OBJECT
+public:
+ PDB_Intro(QWidget *parent);
+};
+
+
+class PDB_Country : public QWidget {
+ Q_OBJECT
+public:
+ PDB_Country(QWidget *parent);
+ ~PDB_Country();
+ QListBox *lb;
+ QStringList *list;
+
+public slots:
+ void selectionChanged(int);
+};
+
+
+class PDB_Provider : public QWidget {
+ Q_OBJECT
+public:
+ PDB_Provider(QWidget *parent);
+
+ void setDir(const QString &d);
+ QString getDir();
+
+ QListBox *lb;
+
+public slots:
+ void selectionChanged(int);
+
+private:
+ QString dir;
+};
+
+
+class PDB_UserInfo : public QWidget {
+ Q_OBJECT
+public:
+ PDB_UserInfo(QWidget *parent);
+ QString username();
+ QString password();
+ void activate();
+
+public slots:
+ void textChanged(const QString &);
+
+private:
+ QLineEdit *_username;
+ QLineEdit *_password;
+};
+
+
+class PDB_DialPrefix : public QWidget {
+ Q_OBJECT
+public:
+ PDB_DialPrefix(QWidget *parent);
+ QString prefix();
+ void activate();
+
+private:
+ QLineEdit *_prefix;
+};
+
+
+class PDB_Finished : public QWidget {
+ Q_OBJECT
+public:
+ PDB_Finished(QWidget *parent);
+};
+
+
+class ProviderDB : public KWizard {
+ Q_OBJECT
+public:
+ ProviderDB(QWidget *parent);
+ ~ProviderDB();
+ static QWizard *wiz;
+
+public slots:
+ void pageSelected();
+ void accept();
+
+private:
+ void loadProviderInfo();
+ KSimpleConfig *cfg;
+
+ PDB_Intro *page1;
+ PDB_Country *page2;
+ PDB_Provider *page3;
+ PDB_UserInfo *page4;
+ PDB_DialPrefix *page5;
+ PDB_Finished *page9;
+};
+
+
+// Decodes a (some sort of)URL-encoded filename to a human-readable name.
+// This is used for the provider database
+void urlDecode(QString &);
+
+// Encodes a (some sort of)URL-encoded filename from a human-readable name.
+// This is used for the provider database
+void urlEncode(QString &);
+
+
+#endif
diff --git a/kppp/pwentry.cpp b/kppp/pwentry.cpp
new file mode 100644
index 00000000..21704fcb
--- /dev/null
+++ b/kppp/pwentry.cpp
@@ -0,0 +1,115 @@
+/*
+ *
+ * Kppp: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+
+#include <stdio.h>
+#include <qapplication.h>
+#include <kglobalsettings.h>
+#include "pwentry.h"
+
+PWEntry::PWEntry( QWidget *parent, const char *name )
+ : QWidget(NULL, name) {
+
+ if(parent){
+
+ QPoint point = mapToGlobal (QPoint (0,0));
+ QRect pos = geometry();
+
+ setGeometry(point.x() + pos.width()/2 - 300/2,
+ point.y() + pos.height()/2 - 90/2,
+ 300,
+ 90);
+ } else {
+ QRect desk = KGlobalSettings::desktopGeometry(parent);
+ setGeometry( desk.center().x() - 150, desk.center().y() - 50, 300, 90 );
+ }
+
+ frame = new QGroupBox(name, this );
+
+ setFocusPolicy( QWidget::StrongFocus );
+
+ pw = new QLineEdit( this, "le" );
+ pw->setEchoMode( QLineEdit::Password );
+ connect( pw, SIGNAL(returnPressed()), this, SLOT(hide()) );
+
+ isconsumed = TRUE;
+}
+
+QString PWEntry::text() { return (pw->text()); }
+
+void PWEntry::focusInEvent( QFocusEvent *){
+
+ pw->setFocus();
+
+}
+
+void PWEntry::setEchoModeNormal() {
+
+ pw->setEchoMode(QLineEdit::Normal);
+
+}
+
+void PWEntry::setEchoModePassword() {
+
+ pw->setEchoMode(QLineEdit::Password);
+
+}
+
+void PWEntry::setPrompt(const QString &p) {
+
+ frame->setTitle(p);
+
+}
+
+void PWEntry::resizeEvent(QResizeEvent* ){
+
+ pw->setGeometry( 15,35, width() - 30, 25 );
+ frame->setGeometry(5,5, width() - 10, height() - 10 );
+
+}
+
+
+void PWEntry::show() {
+
+ pw->setText("");
+ isconsumed = FALSE;
+ QWidget::show();
+}
+
+bool PWEntry::Consumed() {
+ return(isconsumed);
+}
+
+void PWEntry::setConsumed() {
+ isconsumed = TRUE;
+}
+
+void PWEntry::hide() {
+ QWidget::hide();
+ return;
+}
+
+#include "pwentry.moc"
diff --git a/kppp/pwentry.h b/kppp/pwentry.h
new file mode 100644
index 00000000..5d9a398c
--- /dev/null
+++ b/kppp/pwentry.h
@@ -0,0 +1,72 @@
+/*
+ *
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef PWENTRY_H
+#define PWENTRY_H
+
+#include <qwidget.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+
+class PWEntry : public QWidget {
+
+ Q_OBJECT
+
+public:
+
+ PWEntry( QWidget *parent=0, const char *name=0 );
+ bool Consumed();
+ void setConsumed();
+ QString text();
+ void setPrompt(const QString &);
+
+public slots:
+
+ void setEchoModeNormal();
+ void setEchoModePassword();
+ void hide();
+ void show();
+
+signals:
+
+ void returnPressed();
+
+protected:
+
+ void resizeEvent(QResizeEvent* qre);
+ void focusInEvent( QFocusEvent *);
+
+private:
+
+ QGroupBox *frame;
+ QLineEdit *pw;
+ QLabel *pl;
+ bool isconsumed;
+
+};
+
+#endif
diff --git a/kppp/requester.cpp b/kppp/requester.cpp
new file mode 100644
index 00000000..bca7f63a
--- /dev/null
+++ b/kppp/requester.cpp
@@ -0,0 +1,365 @@
+/*
+ * kPPP: A pppd Front End for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997,98 Bernd Johannes Wuebben,
+ * Mario Weilguni,
+ * Harri Porten
+ *
+ *
+ * This file was contributed by Harri Porten <porten@tu-harburg.de>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef __osf__
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/uio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#ifdef __osf__
+#undef accept
+extern "C" unsigned int alarm(unsigned int);
+#endif
+
+#ifdef _XPG4_2
+extern "C" {
+ ssize_t sendmsg(int, const struct msghdr *, int);
+ ssize_t recvmsg(int, struct msghdr *, int);
+}
+#endif
+
+#include <kdebug.h>
+#include <qfile.h>
+
+#include "auth.h"
+#include "pppdata.h"
+#include "opener.h"
+#include "requester.h"
+#include "devices.h"
+
+Requester *Requester::rq = 0L;
+
+Requester::Requester(int s) : socket(s) {
+ assert(rq==0L);
+ rq = this;
+ lastStatus = -1;
+}
+
+Requester::~Requester() {
+}
+
+//
+// Receive file name and file descriptors from envoy
+//
+int Requester::recvFD() {
+ struct { struct cmsghdr cmsg; int fd; } control;
+ struct msghdr msg;
+ struct ResponseHeader response;
+
+ struct iovec iov;
+ int flags = 0, fd, len;
+ size_t cmsglen;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ iov.iov_base = IOV_BASE_CAST &response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+#ifdef CMSG_LEN
+ cmsglen = CMSG_LEN(sizeof(int));
+#else
+ cmsglen = sizeof(struct cmsghdr) + sizeof(int);
+#endif
+ control.cmsg.cmsg_len = cmsglen;
+ control.cmsg.cmsg_level = SOL_SOCKET;
+ control.cmsg.cmsg_type = MY_SCM_RIGHTS;
+
+ msg.msg_control = (char *) &control;
+ msg.msg_controllen = control.cmsg.cmsg_len;
+
+ fd = -1;
+
+ // set alarm in case recvmsg() hangs
+ signal(SIGALRM, recv_timeout);
+ alarm(2);
+
+ len = recvmsg(socket, &msg, flags);
+
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ if(len <= 0) {
+ kdError(5002) << "recvmsg failed " << strerror(errno) << endl;
+ return -1;
+ } else if (msg.msg_controllen < cmsglen) {
+ kdError(5002) << "recvmsg: truncated message " << strerror(errno) << endl;
+ exit(1);
+ } else {
+#ifdef CMSG_DATA
+ fd = *((int *)CMSG_DATA(&control.cmsg));
+#else
+ fd = *((int *) control.cmsg.cmsg_data);
+#endif
+ kdDebug(5002) << "response.status: " << response.status << endl;
+ assert(response.status <= 0);
+ if(response.status < 0)
+ return response.status;
+ }
+
+ return fd;
+}
+
+bool Requester::recvResponse() {
+
+ struct msghdr msg;
+ struct iovec iov;
+ struct ResponseHeader response;
+ int flags = 0, len;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+
+ iov.iov_base = IOV_BASE_CAST &response;
+ iov.iov_len = sizeof(struct ResponseHeader);
+ kdDebug(5002) << "recvResponse(): waiting for message" << endl;
+ len = recvmsg(socket, &msg, flags);
+ kdDebug(5002) << "recvResponse(): received message" << endl;
+ if (len <= 0) {
+ if (errno == EINTR)
+ kdDebug(5002) << "Interrupted system call. Continuing." << endl;
+ else
+ perror("recvmsg failed");
+ } else {
+ kdDebug(5002) << "response.status: " << response.status << endl;
+ }
+
+ lastStatus = response.status;
+ return (response.status == 0);
+}
+
+int Requester::openModem(const QString & dev) {
+
+ struct OpenModemRequest req;
+ req.header.type = Opener::OpenDevice;
+ if((req.deviceNum = indexDevice(dev)) < 0)
+ return -1;
+
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvFD();
+}
+
+
+int Requester::openLockfile(const QString &dev, int flags) {
+
+ struct OpenLockRequest req;
+
+ req.header.type = Opener::OpenLock;
+ if((req.deviceNum = indexDevice(dev)) < 0)
+ return -1;
+ req.flags = flags;
+
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvFD();
+}
+
+
+bool Requester::removeLockfile() {
+
+ struct RemoveLockRequest req;
+
+ req.header.type = Opener::RemoveLock;
+
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+
+int Requester::openResolv(int flags) {
+
+ struct OpenResolvRequest req;
+
+ req.header.type = Opener::OpenResolv;
+ req.flags = flags;
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvFD();
+}
+
+
+int Requester::openSysLog() {
+
+ struct OpenLogRequest req;
+
+ req.header.type = Opener::OpenSysLog;
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvFD();
+}
+
+
+bool Requester::setSecret(int method, const QString &name, const QString &password) {
+ assert(name!=0);
+ assert(password!=0);
+
+ if(method == AUTH_PAPCHAP)
+ return setSecret(AUTH_PAP, name, password) &&
+ setSecret(AUTH_CHAP, name, password);
+
+ struct SetSecretRequest req;
+ req.header.type = Opener::SetSecret;
+ switch(method) {
+ case AUTH_PAP:
+ req.method = Opener::PAP;
+ break;
+ case AUTH_CHAP:
+ req.method = Opener::CHAP;
+ break;
+ default:
+ return false;
+ }
+ strncpy(req.username, QFile::encodeName(name), Opener::MaxStrLen);
+ req.username[Opener::MaxStrLen] = '\0';
+ strncpy(req.password, QFile::encodeName(password), Opener::MaxStrLen);
+ req.password[Opener::MaxStrLen] = '\0';
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+bool Requester::removeSecret(int authMethod) {
+ struct RemoveSecretRequest req;
+ req.header.type = Opener::RemoveSecret;
+ if(authMethod == AUTH_PAP)
+ req.method = Opener::PAP;
+ else
+ if(authMethod == AUTH_CHAP)
+ req.method = Opener::CHAP;
+ else
+ return false;
+
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+bool Requester::setHostname(const QString &name) {
+ if (name.isEmpty())
+ return false;
+ struct SetHostnameRequest req;
+ req.header.type = Opener::SetHostname;
+ strncpy(req.name, QFile::encodeName(name), Opener::MaxStrLen);
+ req.name[Opener::MaxStrLen] = '\0';
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+
+bool Requester::execPPPDaemon(const QString &arguments) {
+ struct ExecDaemonRequest req;
+ req.header.type = Opener::ExecPPPDaemon;
+ strncpy(req.arguments, QFile::encodeName(arguments), MAX_CMDLEN);
+ req.arguments[MAX_CMDLEN] = '\0';
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ if(recvResponse()==0) {
+ gpppdata.setpppdRunning(true);
+ return true;
+ } else
+ return false;
+}
+
+
+bool Requester::killPPPDaemon() {
+ struct KillDaemonRequest req;
+ gpppdata.setpppdRunning(false);
+ req.header.type = Opener::KillPPPDaemon;
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+int Requester::pppdExitStatus()
+{
+ struct PPPDExitStatusRequest req;
+ req.header.type = Opener::PPPDExitStatus;
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+ return recvResponse();
+}
+
+bool Requester::stop() {
+
+ struct StopRequest req;
+ req.header.type = Opener::Stop;
+ sendRequest((struct RequestHeader *) &req, sizeof(req));
+
+ // return recvResponse();
+ return true;
+}
+
+
+bool Requester::sendRequest(struct RequestHeader *request, int len) {
+
+ request->len = len - sizeof(struct RequestHeader);
+
+ struct msghdr msg;
+ struct iovec iov;
+
+ iov.iov_base = IOV_BASE_CAST request;
+ iov.iov_len = len;
+
+ msg.msg_name = 0L;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = 0L;
+ msg.msg_controllen = 0;
+ kdDebug(5002) << "sendRequest: trying to send msg type " << request->type << endl;
+ sendmsg(socket, &msg, 0);
+ kdDebug(5002) << "sendRequest: sent message" << endl;
+
+ return true;
+}
+
+
+int Requester::indexDevice(const QString &dev) {
+
+ int index = -1;
+
+ for(int i = 0; devices[i]; i++)
+ if (dev == devices[i])
+ index = i;
+ return index;
+}
+
+
+void recv_timeout(int) {
+ kdDebug(5002) << "timeout()" << endl;
+}
diff --git a/kppp/requester.h b/kppp/requester.h
new file mode 100644
index 00000000..cb2d0e04
--- /dev/null
+++ b/kppp/requester.h
@@ -0,0 +1,38 @@
+#ifndef _REQUESTER_H_
+#define _REQUESTER_H_
+
+class Requester {
+
+public:
+ Requester(int);
+ ~Requester();
+
+ int openModem(const QString & dev);
+ int openLockfile(const QString & dev, int flags = 0);
+ bool removeLockfile();
+ int openResolv(int flags);
+ int openSysLog();
+ bool setSecret(int method, const QString & name, const QString & password);
+ bool removeSecret(int authMethode);
+ bool setHostname(const QString & name);
+ bool execPPPDaemon(const QString & arguments);
+ bool killPPPDaemon();
+ int pppdExitStatus();
+ bool stop();
+
+public:
+ static Requester *rq;
+ int lastStatus;
+
+private:
+ bool sendRequest(struct RequestHeader *request, int len);
+ bool recvResponse();
+ int recvFD();
+ int indexDevice(const QString & dev);
+ int socket;
+ bool expect_alarm;
+};
+
+void recv_timeout(int);
+
+#endif
diff --git a/kppp/ruleset.cpp b/kppp/ruleset.cpp
new file mode 100644
index 00000000..1775b5c2
--- /dev/null
+++ b/kppp/ruleset.cpp
@@ -0,0 +1,581 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Mario Weilguni <mweilguni@sime.com>
+ * Thanks Mario !
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "ruleset.h"
+
+#include <qregexp.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+
+RuleSet::RuleSet() {
+ default_costs = -1;
+ default_len = -1;
+ _currency_symbol = "$";
+ _currency_digits = 2;
+ _minimum_costs = 0;
+ flat_init_costs = 0.0;
+ flat_init_duration = 0;
+ have_flat_init_costs = false;
+
+ pcf = 0.0;
+}
+
+// this function is shamelessly stolen from pppcosts 0.5 :-)
+/* calculates the easter sunday in day_of_year style */
+QDate RuleSet::get_easter(int year) {
+ /* not optimized, I took the original names */
+ signed int a,b,m,q,w,p,n,tt,mm;
+
+ /* calculating easter is really funny */
+ /* this is O'Beirne's algorithm, only valid 1900-2099 */
+ n = year - 1900;
+ a = n % 19;
+ b = (int)((7*a+1)/19);
+ m = (11*a+4-b) % 29;
+ q = (int)(n/4);
+ w = (n+q+31-m) % 7;
+ p = 25-m-w;
+ if (p>0)
+ {tt=p;
+ mm=4;}
+ else
+ {tt=p+31;
+ mm=3;}
+
+ return QDate(year, mm, tt);
+}
+
+int RuleSet::dayNameToInt(const char *s) {
+ const char *const name[] = {"monday", "tuesday", "wednesday",
+ "thursday", "friday", "saturday",
+ "sunday", NULL};
+
+ for(int i = 0; name[i] != NULL; i++)
+ if(qstricmp(name[i], s) == 0)
+ return i;
+
+ return -1;
+}
+
+int RuleSet::load(const QString &filename) {
+
+ flat_init_costs = 0.0;
+ flat_init_duration = 0;
+ have_flat_init_costs = false;
+
+ QFile f(filename);
+
+ // delete old rules
+ rules.resize(0);
+ _name = "";
+
+ // ignore "No Accounting"
+ if(filename.isEmpty())
+ return 0;
+
+ if(!f.exists())
+ return -1;
+
+ if(!f.open(IO_ReadOnly))
+ return -1;
+
+ char buffer[2048]; // safe
+ int lineno=0;
+
+ while(!f.atEnd()) {
+ // read continued lines
+ QString line;
+ bool backslashed;
+ do {
+ int br = f.readLine(buffer, sizeof(buffer));
+ if((br > 0) && (buffer[br-1] == '\n'))
+ buffer[br-1] = 0;
+ else
+ buffer[br] = 0;
+ lineno++;
+ line.append(QString::fromUtf8(buffer));
+ backslashed = (line.right(1) == "\\");
+ } while(!f.atEnd() && backslashed);
+
+ // strip whitespace
+ line = line.replace(QRegExp("[ \t\r]"), "");
+ // skip comment lines
+ if((line.left(1) == "#") || line.isEmpty())
+ continue;
+
+ // parse line
+ if(!parseLine(line)) {
+ f.close();
+ kdError(5002) << "ERROR IN LINE " << lineno << endl;
+ return lineno;
+ }
+ }
+
+ f.close();
+
+ if(_name.length() > 0)
+ return 0;
+ else {
+ kdError(5002) << "NO NAME DEFINED" << endl;
+ return -1;
+ }
+}
+
+void RuleSet::addRule(RULE r) {
+ // check for a default rule
+ if((r.type == 2) &&
+ (r.weekday.from == 0) && (r.weekday.until == 6) &&
+ (r.from == midnight()) &&
+ (r.until == beforeMidnight()))
+ {
+ default_costs = r.costs;
+ default_len = r.len;
+ return;
+ }
+
+ // if from < until (i.e on (monday..friday)
+ // from (21:00..05:00) use (0.2,16)
+ // split it into two rules
+ // ... between (21:00..23:59) use ...
+ // ... between (00:00..05:00) use ...
+ if(r.from > r.until) {
+ RULE r1, r2;
+ r1 = r;
+ r2 = r;
+ r1.until = beforeMidnight();
+ r2.from = midnight();
+ rules.resize(rules.size()+2);
+ rules[rules.size()-2] = r1;
+ rules[rules.size()-1] = r2;
+ } else {
+ rules.resize(rules.size()+1);
+ rules[rules.size()-1] = r;
+ }
+}
+
+bool RuleSet::parseEntry(RULE &ret, QString s, int year) {
+ if(s.contains(QRegExp("^[0-9]+/[0-9]+$"))) {
+ int d, m;
+ sscanf(s.ascii(), "%d/%d", &m, &d);
+ ret.type = 1;
+ ret.date.from = QDate(year, m, d);
+ ret.date.until = QDate(year, m, d);
+ return TRUE;
+ }
+
+ if(s.contains(QRegExp("^[0-9]+\\.[0-9]+$"))) {
+ int d, m;
+ sscanf(s.ascii(), "%d.%d", &d, &m);
+ ret.type = 1;
+ ret.date.from = QDate(year, m, d);
+ ret.date.until = QDate(year, m, d);
+ return TRUE;
+ }
+
+ if(s.right(3) == "day") {
+ int d = dayNameToInt(s.ascii());
+ if(d != -1) {
+ ret.type = 2;
+ ret.weekday.from = d;
+ ret.weekday.until = d;
+ return TRUE;
+ }
+ }
+
+ if(s.left(6) == "easter") {
+ QDate d = get_easter(year);
+ int off;
+ bool ok = TRUE;
+ QString val = s.mid(6, 1000);
+ if(val.isEmpty())
+ off = 0;
+ else
+ off = val.toInt(&ok);
+
+ if(ok) {
+ d = d.addDays(off);
+ ret.type = 1;
+ ret.date.from = d;
+ ret.date.until = d;
+ return TRUE;
+ }
+ }
+
+ ret.type = 0;
+ return FALSE;
+}
+
+
+
+bool RuleSet::parseEntries(QString s, int year,
+ QTime t1, QTime t2,
+ double costs, double len, double after)
+{
+ // special rule: on() is the same as on(monday..sunday)
+ if(s.isEmpty())
+ s = "monday..sunday";
+
+ while(s.length()) {
+ int pos = s.find(',');
+ QString token;
+ if(pos == -1) {
+ token = s;
+ s = "";
+ } else {
+ token = s.left(pos);
+ s = s.right(s.length()-pos-1);
+ }
+
+ // we've a token, now check if it defines a
+ // range
+ RULE r;
+ if(token.contains("..")) {
+ QString left, right;
+ left = token.left(token.find(".."));
+ right = token.right(token.length()-2-left.length());
+ RULE lr, rr;
+ if(parseEntry(lr, left, year) && parseEntry(rr, right, year)) {
+ if(lr.type == rr.type) {
+ r.type = lr.type;
+ switch(lr.type) {
+ case 1:
+ r.date.from = lr.date.from;
+ r.date.until = rr.date.from;
+ break;
+ case 2:
+ r.weekday.from = lr.weekday.from;
+ r.weekday.until = rr.weekday.from;
+ }
+ } else
+ return FALSE;
+ }
+ } else
+ if(!parseEntry(r, token, year))
+ return FALSE;
+
+ r.costs = costs;
+ r.len = len;
+ r.after = after;
+ r.from = t1;
+ r.until = t2;
+ addRule(r);
+ }
+
+ return TRUE;
+}
+
+bool RuleSet::parseTime(QTime &t1, QTime &t2, QString s) {
+ if(s.isEmpty()) {
+ t1 = midnight();
+ t2 = beforeMidnight();
+ return TRUE;
+ } else {
+ int t1m, t1h, t2m, t2h;
+ if(sscanf(s.ascii(), "%d:%d..%d:%d", &t1h, &t1m, &t2h, &t2m) == 4) {
+ t1.setHMS(t1h, t1m, 0);
+ t2.setHMS(t2h, t2m, 0);
+ return TRUE;
+ } else
+ return FALSE;
+ }
+}
+
+bool RuleSet::parseRate(double &costs, double &len, double &after, QString s) {
+ after = 0;
+ int fields = sscanf(s.ascii(), "%lf,%lf,%lf", &costs, &len, &after);
+ return (fields == 2) || (fields == 3);
+}
+
+bool RuleSet::parseLine(const QString &s) {
+
+ // ### use QRegExp::cap() instead of mid() and find()
+
+ // for our french friends -- Bernd
+ if(s.contains(QRegExp("flat_init_costs=\\(.*"))) {
+ // parse the time fields
+ QString token = s.mid(s.find("flat_init_costs=(") + 17,
+ s.find(")")-s.find("flat_init_costs=(") - 17);
+ // printf("TOKEN=%s\n",token.ascii());
+
+ double after;
+ if(!parseRate(flat_init_costs, flat_init_duration, after, token))
+ return FALSE;
+
+ //printf("COST %f DURATION %f\n",flat_init_costs,flat_init_duration);
+
+ if(! (flat_init_costs >= 0.0) )
+ return FALSE;
+ if(! (flat_init_duration >= 0.0))
+ return FALSE;
+
+ have_flat_init_costs = true;
+ return TRUE;
+ }
+
+
+ if(s.contains(QRegExp("on\\(.*\\)between\\(.*\\)use\\(.*\\)"))) {
+ // parse the time fields
+ QString token = s.mid(s.find("between(") + 8,
+ s.find(")use")-s.find("between(") - 8);
+ QTime t1, t2;
+ if(!parseTime(t1, t2, token))
+ return FALSE;
+
+ // parse the rate fields
+ token = s.mid(s.find("use(") + 4,
+ s.findRev(")")-s.find("use(") - 4);
+ double costs;
+ double len;
+ double after;
+ if(!parseRate(costs, len, after, token))
+ return FALSE;
+
+ // parse the days
+ token = s.mid(s.find("on(") + 3,
+ s.find(")betw")-s.find("on(") - 3);
+ if(!parseEntries(token, QDate::currentDate().year(),
+ t1, t2, costs, len, after))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ // check for the name
+ if(s.contains(QRegExp("name=.*"))) {
+ _name = s.right(s.length()-5);
+ return !_name.isEmpty();
+ }
+
+
+ // check default entry
+ if(s.contains(QRegExp("default=\\(.*\\)"))) {
+ QString token = s.mid(9, s.length() - 10);
+ double after;
+ if(parseRate(default_costs, default_len, after, token))
+ return TRUE;
+ }
+
+ // check for "minimum costs"
+ if(s.contains(QRegExp("minimum_costs=.*"))) {
+ QString token = s.right(s.length() - strlen("minimum_costs="));
+ bool ok;
+ _minimum_costs = token.toDouble(&ok);
+ return ok;
+ }
+
+ // check currency settings
+ if(s.startsWith("currency_symbol=")) {
+ _currency_symbol = s.mid(16);
+ return TRUE;
+ }
+
+ if(s.contains(QRegExp("currency_digits=.*"))) {
+ QString token = s.mid(16, s.length() - 16);
+ bool ok;
+ _currency_digits = token.toInt(&ok);
+ return ok && (_currency_digits >= 0);
+ }
+
+ // "currency_position" is deprecated so we'll simply ignore it
+ if(s.contains(QRegExp("currency_position=.*")))
+ return TRUE;
+
+ // check per connection fee
+ if(s.contains(QRegExp("per_connection="))) {
+ QString token = s.mid(15, s.length()-15);
+ bool ok;
+ pcf = token.toDouble(&ok);
+ return ok;
+ }
+
+ return FALSE;
+}
+
+void RuleSet::setStartTime(QDateTime dt){
+
+ starttime = dt;
+
+}
+
+void RuleSet::getActiveRule(QDateTime dt, double connect_time, double &costs, double &len) {
+ // use default costs first
+ costs = default_costs;
+ len = default_len;
+
+ //printf("In getActiveRule\n");
+ if(have_flat_init_costs){
+
+ costs = flat_init_costs;
+ len = flat_init_duration;
+ have_flat_init_costs = false;
+ //printf("getActiveRule FLATINITCOSTS\n");
+ return;
+ }
+
+ // check every rule
+ for(int i = 0; i < (int)rules.size(); i++) {
+ RULE r = rules[i];
+
+ switch(r.type) {
+ case 1: // a date
+ {
+ // since rules do not have a year's entry, use the one
+ // from dt
+ QDate from = r.date.from;
+ QDate until = r.date.until;
+ from.setYMD(dt.date().year(), from.month(), from.day());
+ until.setYMD(dt.date().year(), until.month(), until.day());
+ if((from <= dt.date()) && (dt.date() <= until)) {
+ // check time
+ if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) {
+ costs = r.costs;
+ len = r.len;
+ }
+ }
+ }
+ break;
+
+ case 2: // one or more weekdays
+ // check if the range overlaps sunday.
+ // (i.e. "on(saturday..monday)")
+ if(r.weekday.from <= r.weekday.until) {
+ if((r.weekday.from <= dt.date().dayOfWeek() - 1) &&
+ (r.weekday.until >= dt.date().dayOfWeek() - 1))
+ {
+ // check time
+ if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) {
+ costs = r.costs;
+ len = r.len;
+ }
+ }
+ } else { // yes, they overlap sunday
+ if((r.weekday.from >= dt.date().dayOfWeek() - 1) &&
+ (dt.date().dayOfWeek() - 1 <= r.weekday.until))
+ {
+ // check time
+ if((r.from <= dt.time()) && (dt.time() <= r.until) && (connect_time >= r.after)) {
+ costs = r.costs;
+ len = r.len;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+#if 0
+static double round(double d, int digits) {
+ d *= pow(10, digits);
+ d = rint(d);
+ d /= pow(10, digits);
+ return d;
+}
+#endif
+
+QString RuleSet::currencySymbol() const {
+ return _currency_symbol.copy();
+}
+
+QString RuleSet::currencyString(double f) const {
+ return KGlobal::locale()->formatMoney(f, _currency_symbol, _currency_digits);
+}
+
+
+double RuleSet::perConnectionCosts() const {
+ return pcf;
+}
+
+
+QString RuleSet::name() const {
+ return _name;
+}
+
+
+double RuleSet::minimumCosts() const {
+ return _minimum_costs;
+}
+
+QTime RuleSet::midnight() const {
+ return QTime(0, 0, 0, 0);
+}
+
+QTime RuleSet::beforeMidnight() const {
+ return QTime(23,59,59,999);
+}
+
+int RuleSet::checkRuleFile(const QString &rulefile) {
+ if(rulefile == NULL) {
+ fputs(i18n("kppp: no rulefile specified\n").local8Bit(), stderr);
+ return 1;
+ }
+
+ QFile fl(rulefile);
+ if(!fl.exists()) {
+ fprintf(stderr, i18n("kppp: rulefile \"%s\" not found\n").local8Bit(), rulefile.local8Bit().data());
+ return 1;
+ }
+
+ if(rulefile.right(4) != ".rst") {
+ fputs(i18n("kppp: rulefiles must have the extension \".rst\"\n").local8Bit(), stderr);
+ return 1;
+ }
+
+ RuleSet r;
+ int err = r.load(rulefile);
+ fl.close();
+
+ if(err == -1) {
+ fputs(i18n("kppp: error parsing the ruleset\n").local8Bit(), stderr);
+ return 1;
+ }
+
+ if(err > 0) {
+ fprintf(stderr, i18n("kppp: parse error in line %d\n").local8Bit(), err);
+ return 1;
+ }
+
+ // check for the existance of a default rule
+ if((r.default_costs < 0) || (r.default_len < 0)) {
+ fputs(i18n("kppp: rulefile does not contain a default rule\n").local8Bit(), stderr);
+ return 1;
+ }
+
+ if(r.name().length() == 0) {
+ fputs(i18n("kppp: rulefile does not contain a \"name=...\" line\n").local8Bit(), stderr);
+ return 1;
+ }
+
+ fputs(i18n("kppp: rulefile is ok\n").local8Bit(), stderr);
+ return 0;
+}
+
diff --git a/kppp/ruleset.h b/kppp/ruleset.h
new file mode 100644
index 00000000..a6881ef2
--- /dev/null
+++ b/kppp/ruleset.h
@@ -0,0 +1,146 @@
+/* -*- C++ -*-
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Mario Weilguni <mweilguni@sime.com>
+ * Thanks Mario !
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RULESET__H__
+#define __RULESET__H__
+
+#include <qmemarray.h>
+#include <qdatetime.h>
+#include <qstring.h>
+
+// this structure is used to save
+// accounting rules
+typedef struct RULE {
+ int type;
+ double costs;
+ double len;
+ double after;
+ QTime from, until;
+ struct {
+ QDate from, until;
+ } date;
+ struct {
+ int from, until;
+ } weekday;
+};
+
+// this class is used for loading and parsing of rules
+class RuleSet {
+public:
+ /// create an empty rule
+ RuleSet();
+
+ /// gcc needs a destructor (otherwise compiler error)
+ ~RuleSet() {};
+
+ /// returns the name of the ruleset
+ QString name() const;
+
+ /** Load a ruleset from a file. If an error occurs,
+ * returns the linenumber the error was in,
+ * otherwise 0. If the file could not be opened,
+ * returns -1
+ */
+ int load(const QString &filename);
+
+ /// returns the currency symbol
+ QString currencySymbol() const;
+
+ /** returns a string representation of the
+ * of a doubleingpoint number using the
+ * currency-settings
+ */
+ QString currencyString(double val) const;
+
+ /// sets the start time -- must be called when the connection has bee established
+ void setStartTime(QDateTime dt);
+
+ /// returns the "per-connection" costs
+ double perConnectionCosts() const;
+
+ /** returns the minimum number of costs (some
+ * phony companies have this
+ */
+ double minimumCosts() const;
+
+ /// returns the currently valid rule settings
+ void getActiveRule(QDateTime dt, double connect_time, double &costs, double &len);
+
+ /// checks if a rulefile is ok (no parse errors...)
+ static int checkRuleFile(const QString &);
+
+protected:
+ /** converts an english name of a day to integer,
+ * beginning with monday=0 .. sunday=6
+ */
+ int dayNameToInt(const char *s);
+
+ /// returns the date of easter-sunday for a year
+ static QDate get_easter(int year);
+
+ /// add a rule to this ruleset
+ void addRule(RULE r);
+
+ /// parses on entry of the "on(...)" fields
+ bool parseEntry(RULE &ret, QString s, int year);
+
+ /// parses the "on(...)" fields
+ bool parseEntries(QString s, int year,
+ QTime t1, QTime t2,
+ double costs, double len, double after);
+
+ /// parses the "between(...)" time fields
+ bool parseTime(QTime &t1, QTime &t2, QString s);
+
+ /// parses the "use(...)" fields
+ bool parseRate(double &costs, double &len, double &after, QString s);
+
+ /// parses a whole line
+ bool parseLine(const QString &line);
+
+ /// returns midnight time (00:00:00.000)
+ QTime midnight() const;
+
+ /// returns the last valid time BEFORE midnight
+ QTime beforeMidnight() const;
+
+protected:
+ QString _name;
+ QString _currency_symbol;
+ QDateTime starttime;
+ int _currency_digits;
+ double default_costs;
+ double _minimum_costs;
+ double default_len;
+ double pcf;
+ bool have_flat_init_costs;
+ double flat_init_duration;
+ double flat_init_costs;
+
+ QMemArray<RULE> rules;
+};
+
+#endif
diff --git a/kppp/runtests.cpp b/kppp/runtests.cpp
new file mode 100644
index 00000000..63fa0794
--- /dev/null
+++ b/kppp/runtests.cpp
@@ -0,0 +1,278 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Mario Weilguni <mweilguni@sime.com>
+ * Thanks Mario !
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <qdir.h>
+#include "runtests.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <kmessagebox.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_RESOLV_H
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#ifndef _PATH_RESCONF
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+#include <klocale.h>
+#include "pppdata.h"
+
+// initial effective uid (main.cpp)
+extern uid_t euid;
+
+// secure pppd location (opener.cpp)
+extern const char* pppdPath();
+
+// shamelessly stolen from pppd-2.3.5
+/********************************************************************
+ *
+ * Internal routine to decode the version.modification.patch level
+ */
+
+static void decode_version (const char *_buf, int *version,
+ int *modification, int *patch)
+ {
+ char *buffer = qstrdup(_buf);
+ char *buf = buffer;
+ *version = (int) strtoul (buf, &buf, 10);
+ *modification = 0;
+ *patch = 0;
+
+ if (*buf == '.')
+ {
+ ++buf;
+ *modification = (int) strtoul (buf, &buf, 10);
+ if (*buf == '.')
+ {
+ ++buf;
+ *patch = (int) strtoul (buf, &buf, 10);
+ }
+ }
+
+ if (*buf != '\0')
+ {
+ *version =
+ *modification =
+ *patch = 0;
+ }
+
+ delete [] buffer;
+ }
+
+
+void pppdVersion(int *version, int *modification, int *patch) {
+ char buffer[30];
+ const char *pppd;
+ char *query;
+
+ *version = *modification = *patch = 0;
+
+ // locate pppd
+ if(!(pppd = pppdPath()))
+ return;
+
+ // call pppd with --version option
+ if(!(query = new char[strlen(pppd)+25]))
+ return;
+ strcpy(query, pppd);
+ // had to add a dummy device to prevent a "no device specified
+ // and stdin is not a tty" error from newer pppd versions.
+ strcat(query, " --version /dev/tty 2>&1");
+ fflush(0L);
+ FILE *output = popen(query, "r");
+ delete [] query;
+ if(!output)
+ return;
+
+ // read output
+ int size = fread(buffer, sizeof(char), 29, output);
+
+ if(ferror(output)) {
+ pclose(output);
+ return;
+ }
+ pclose(output);
+ buffer[size] = '\0';
+
+ // find position of version number x.y.z
+ char *p = buffer;
+ while(*p && !isdigit(*p))
+ p++;
+ if (*p == 0)
+ return;
+ char *p2 = p;
+ while(*p2 == '.' || isdigit(*p2))
+ p2++;
+ *p2 = '\0';
+
+ decode_version(p, version, modification, patch);
+}
+
+
+int uidFromName(const char *uname) {
+ struct passwd *pw;
+
+ setpwent();
+ while((pw = getpwent()) != NULL) {
+ if(strcmp(uname, pw->pw_name) == 0) {
+ int uid = pw->pw_uid;
+ endpwent();
+ return uid;
+ }
+ }
+
+ endpwent();
+ return -1;
+}
+
+
+const char *homedirFromUid(uid_t uid) {
+ struct passwd *pw;
+ char *d = 0;
+
+ setpwent();
+ while((pw = getpwent()) != NULL) {
+ if(pw->pw_uid == uid) {
+ d = strdup(pw->pw_dir);
+ endpwent();
+ return d;
+ }
+ }
+
+ endpwent();
+ return d;
+}
+
+
+const char* getHomeDir() {
+ static const char *hd = 0;
+ static bool ranTest = false;
+ if(!ranTest) {
+ hd = homedirFromUid(getuid());
+ ranTest = true;
+ }
+
+ return hd;
+}
+
+
+int runTests() {
+ int warning = 0;
+
+ // Test pre-1: check if the user is allowed to dial-out
+ if(access("/etc/kppp.allow", R_OK) == 0 && getuid() != 0) {
+ bool access = FALSE;
+ FILE *f;
+ if((f = fopen("/etc/kppp.allow", "r")) != NULL) {
+ char buf[2048]; // safe
+ while(f != NULL && !feof(f)) {
+ if(fgets(buf, sizeof(buf), f) != NULL) {
+ QString s(buf);
+
+ s = s.stripWhiteSpace();
+ if(s[0] == '#' || s.length() == 0)
+ continue;
+
+ if((uid_t)uidFromName(QFile::encodeName(s)) == getuid()) {
+ access = TRUE;
+ fclose(f);
+ f = NULL;
+ }
+ }
+ }
+ if(f)
+ fclose(f);
+ }
+
+ if(!access) {
+ KMessageBox::error(0,
+ i18n("You're not allowed to dial out with "
+ "kppp.\nContact your system administrator."));
+ return TEST_CRITICAL;
+ }
+ }
+
+ // Test 1: search the pppd binary
+ const char *f = pppdPath();
+
+ if(!f) {
+ KMessageBox::error(0,
+ i18n("Cannot find the PPP daemon!\n"
+ "Make sure that pppd is installed."));
+ warning++;
+ }
+
+ // Test 2: check access to the pppd binary
+ if(f) {
+#if 0
+ if(access(f, X_OK) != 0 /* && geteuid() != 0 */) {
+ KMessageBox::error(0,
+ i18n("You do not have the permission "
+ "to start pppd!\n"
+ "Contact your system administrator "
+ "and ask to get access to pppd."));
+ return TEST_CRITICAL;
+ }
+#endif
+
+ if(euid != 0) {
+ struct stat st;
+ stat(f, &st);
+ if(st.st_uid != 0 || (st.st_mode & S_ISUID) == 0) {
+ KMessageBox::error(0,
+ i18n("You don't have sufficient permission to run\n"
+ "%1\n"
+ "Please make sure that kppp is owned by root "
+ "and has the SUID bit set.").arg(f));
+ warning++;
+ }
+ }
+ }
+
+ // Test 5: check for existence of /etc/resolv.conf
+ if (access(_PATH_RESCONF, R_OK) != 0) {
+ QString file = _PATH_RESCONF" ";
+ QString msgstr = i18n("%1 is missing or can't be read!\n"
+ "Ask your system administrator to create "
+ "this file (can be empty) with appropriate "
+ "read and write permissions.").arg(file);
+ KMessageBox::error(0, msgstr);
+ warning ++;
+ }
+
+ if(warning == 0)
+ return TEST_OK;
+ else
+ return TEST_WARNING;
+}
+
diff --git a/kppp/runtests.h b/kppp/runtests.h
new file mode 100644
index 00000000..1b8fc65a
--- /dev/null
+++ b/kppp/runtests.h
@@ -0,0 +1,47 @@
+/*
+ * kPPP: A pppd front end for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * This file was contributed by Mario Weilguni <mweilguni@sime.com>
+ * Thanks Mario !
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RUNTESTS__H__
+#define __RUNTESTS__H__
+
+#define SYSOPTIONS "/etc/ppp/options"
+
+const int TEST_OK = 0;
+const int TEST_WARNING = 1;
+const int TEST_NOCONNECT = 2;
+const int TEST_CRITICAL = 3;
+
+// Various tests to be run at starttime
+int runTests();
+const char* getHomeDir();
+void pppdVersion(int *version, int *modification, int *patch);
+
+#ifdef __linux__
+bool ppp_available(void);
+#endif
+
+#endif
+
diff --git a/kppp/scriptedit.cpp b/kppp/scriptedit.cpp
new file mode 100644
index 00000000..56487ce1
--- /dev/null
+++ b/kppp/scriptedit.cpp
@@ -0,0 +1,174 @@
+
+/*
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "scriptedit.h"
+#include <qlayout.h>
+#include <qcombobox.h>
+#include <qlineedit.h>
+
+ScriptEdit::ScriptEdit( QWidget *parent, const char *name )
+ : QWidget(parent, name)
+{
+ QHBoxLayout *tl = new QHBoxLayout(this, 0, 10);
+
+ st = new QComboBox(this, "st");
+ st->insertItem("Expect");
+ st->insertItem("Send");
+ st->insertItem("Pause (sec)");
+ st->insertItem("Hangup");
+ st->insertItem("Answer");
+ st->insertItem("Timeout (sec)");
+ st->insertItem("Password");
+ st->insertItem("ID");
+ st->insertItem("Prompt");
+ st->insertItem("PWPrompt");
+ st->insertItem("LoopStart");
+ st->insertItem("LoopEnd");
+ st->insertItem("Scan");
+ st->insertItem("Save");
+ st->insertItem("SendNoEcho");
+ connect(st, SIGNAL(activated(int)), SLOT(setType(int)));
+
+ se = new QLineEdit(this, "se");
+ se->setGeometry(120, 5, 140, 25);
+ se->setMaxLength(50);
+ connect(se, SIGNAL(returnPressed()), SLOT(seReturnPressed()));
+
+ tl->addWidget(st, 3);
+ tl->addWidget(se, 7);
+
+ setType(0);
+
+ tl->activate();
+}
+
+
+void ScriptEdit::setEnabled(bool b) {
+ se->setEnabled(b);
+ st->setEnabled(b);
+}
+
+void ScriptEdit::seReturnPressed() {
+ emit returnPressed();
+}
+
+
+QString ScriptEdit::text()const {
+ return se->text();
+}
+
+void ScriptEdit::setText(const QString &t) {
+ se->setText(t);
+}
+
+
+int ScriptEdit::type()const {
+ return st->currentItem();
+}
+
+void ScriptEdit::setType(int i) {
+ switch(i) {
+ case Expect:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Send:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Pause:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Hangup:
+ se->setText("");
+ se->setEnabled(FALSE);
+ break;
+
+ case Answer:
+ se->setText("");
+ se->setEnabled(FALSE);
+ break;
+
+ case Timeout:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Password:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case ID:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Prompt:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case PWPrompt:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case LoopStart:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case LoopEnd:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Scan:
+ se->setText("");
+ se->setEnabled(TRUE);
+ break;
+
+ case Save:
+ se->setText("password");
+ se->setEnabled(FALSE);
+ break;
+
+ default: break;
+ }
+}
+
+#include "scriptedit.moc"
+
+
+
+
+
diff --git a/kppp/scriptedit.h b/kppp/scriptedit.h
new file mode 100644
index 00000000..3f2a4535
--- /dev/null
+++ b/kppp/scriptedit.h
@@ -0,0 +1,79 @@
+
+/*
+ * kPPP: A front end for pppd for the KDE project
+ *
+ * $Id$
+ *
+ * Copyright (C) 1997 Bernd Johannes Wuebben
+ * wuebben@math.cornell.edu
+ *
+ * based on EzPPP:
+ * Copyright (C) 1997 Jay Painter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _SCRIPTEDIT_H_
+#define _SCRIPTEDIT_H_
+
+#include <qwidget.h>
+
+class QComboBox;
+class QLineEdit;
+
+class ScriptEdit : public QWidget {
+Q_OBJECT
+public:
+ ScriptEdit( QWidget *parent=0, const char *name=0 );
+ ~ScriptEdit() {}
+
+ QString text()const;
+ void setText(const QString &);
+ int type()const;
+
+ virtual void setEnabled(bool);
+
+signals:
+ void returnPressed();
+
+private slots:
+ void setType(int);
+ void seReturnPressed();
+
+private:
+ QComboBox *st;
+ QLineEdit *se;
+
+public:
+ enum setypes { Expect = 0,
+ Send = 1,
+ Pause = 2,
+ Hangup = 3,
+ Answer = 4,
+ Timeout = 5,
+ Password = 6,
+ ID = 7,
+ Prompt = 8,
+ PWPrompt = 9,
+ LoopStart = 10,
+ LoopEnd = 11,
+ Scan = 12,
+ Save = 13,
+ SendNoEcho = 14
+ };
+};
+
+#endif
+
diff --git a/kppp/utils.cpp b/kppp/utils.cpp
new file mode 100644
index 00000000..17fe62ea
--- /dev/null
+++ b/kppp/utils.cpp
@@ -0,0 +1,66 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <limits.h>
+
+#include "utils.h"
+#include "kpppconfig.h"
+
+// in case of error, we simply return "0" and hope it's ok :-)
+int pppInterfaceNumber() {
+ char ifname[PATH_MAX];
+
+ for(int i = 0; i < 8; i++) {
+ snprintf(ifname, sizeof(ifname), "%s/ppp%d.pid", PPP_PID_DIR, i);
+ if(access(ifname, F_OK) == -1)
+ return i;
+ }
+
+ // panic
+ fprintf(stderr, "pppInterfaceNumber: cannot detect ppp interface number!\n");
+ return 0;
+}
+
diff --git a/kppp/utils.h b/kppp/utils.h
new file mode 100644
index 00000000..1186eb5e
--- /dev/null
+++ b/kppp/utils.h
@@ -0,0 +1,44 @@
+//---------------------------------------------------------------------------
+//
+// kPPP: A pppd front end for the KDE project
+//
+//---------------------------------------------------------------------------
+//
+// (c) 1997-1998 Bernd Johannes Wuebben <wuebben@kde.org>
+// (c) 1997-1999 Mario Weilguni <mweilguni@kde.org>
+// (c) 1998-1999 Harri Porten <porten@kde.org>
+//
+// derived from Jay Painters "ezppp"
+//
+//---------------------------------------------------------------------------
+//
+// $Id$
+//
+//---------------------------------------------------------------------------
+//
+// This program is free software; you can redistribute it and-or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this program; if not, write to the Free
+// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+//---------------------------------------------------------------------------
+
+#ifndef __UTILS__H__
+#define __UTILS__H__
+
+// return the number the PPP interface will get (HACK: of course this
+// may fail, if another interface comes up between the result of this
+// function and the desired ppp interface to come up. If used rightly,
+// it´s very unlikely that this fails)
+int pppInterfaceNumber();
+
+#endif
diff --git a/kppp/version.h b/kppp/version.h
new file mode 100644
index 00000000..57566fc7
--- /dev/null
+++ b/kppp/version.h
@@ -0,0 +1,3 @@
+#ifndef KPPPVERSION
+#define KPPPVERSION "2.3.2"
+#endif
diff --git a/krdc/Makefile.am b/krdc/Makefile.am
new file mode 100644
index 00000000..bf24d823
--- /dev/null
+++ b/krdc/Makefile.am
@@ -0,0 +1,42 @@
+KDE_CXXFLAGS = $(USE_THREADS)
+
+SUBDIRS = vnc rdp
+
+METASOURCES = AUTO
+
+bin_PROGRAMS = krdc
+krdc_SOURCES = main.cpp krdc.cpp vidmode.cpp kfullscreenpanel.cpp \
+ hostprofiles.ui maindialogbase.ui keycapturewidget.ui kservicelocator.cpp \
+ keycapturedialog.cpp kremoteview.cpp smartptr.cpp hostpreferences.cpp \
+ preferencesdialog.cpp maindialogwidget.cpp maindialog.cpp
+
+noinst_HEADERS = main.h krdc.h vidmode.h kfullscreenpanel.h events.h \
+ kservicelocator.h preferencesdialog.h \
+ keycapturedialog.h kremoteview.h smartptr.h hostpreferences.h
+
+EXTRA_DIST = README TODO
+
+appdatadir = $(kde_datadir)/krdc/pics
+appdata_DATA = pointcursor.png pointcursormask.png pinup.png pindown.png \
+ iconify.png close.png
+
+dnssddatadir = $(kde_datadir)/zeroconf
+dnssddata_DATA = _rfb._tcp
+
+krdc_LDADD = vnc/libvnc.la rdp/librdp.la $(LIB_KDEUI) $(LIBXF86VIDMODE) $(LIB_SLP) $(LIB_KDNSSD) $(X_LDFLAGS) $(LIB_X11) -lkwalletclient
+krdc_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+xdg_apps_DATA = krdc.desktop
+
+servicedir = $(kde_datadir)/konqueror/servicemenus
+service_DATA = smb2rdc.desktop
+
+kde_services_DATA = vnc.protocol rdp.protocol
+
+KDE_ICON = krdc
+
+INCLUDES= $(all_includes)
+
+messages: rc.cpp
+ $(EXTRACTRC) *.ui */*.ui >> rc.cpp
+ $(XGETTEXT) *.cpp */*.cpp *.h -o $(podir)/krdc.pot
diff --git a/krdc/README b/krdc/README
new file mode 100644
index 00000000..40b6db0d
--- /dev/null
+++ b/krdc/README
@@ -0,0 +1,18 @@
+KDE Remote Desktop Connection
+=============================
+
+KDE Remote Desktop Connection (krdc) is a small VNC-compatible client
+that is based on the TightVNC Unix client. You can use it to access
+VNC-compatible servers like KDE's Desktop Sharing (krfb).
+
+Its focus is on being small and as easy to use as possible. Therefore
+it does not have many capabilities that may be useful in classrooms or
+for tasks like system administration (like receiving 'broadcasts' of
+a remote desktop, viewing several desktops at once, tabs to switch between
+desktops). In the future I plan to make the backend a KPart so that
+it is possible to write a more complex clients while sharing code with
+krdc.
+
+tim@tjansen.de
+
+
diff --git a/krdc/TODO b/krdc/TODO
new file mode 100644
index 00000000..be7930a1
--- /dev/null
+++ b/krdc/TODO
@@ -0,0 +1,17 @@
+For 3.2:
+- new preferences system
+- merge SLP services with identical serviceid
+- better scaling algorithm
+- SASL authentication
+- X11 support
+ - patch xnest to allow embedding it into a QXEmbed
+ - add exec feature to kio_fish or maybe use KSshProcess?
+
+Future:
+- SSL/TLS?
+- make KVncView a KPart
+- possibly reduce flickering by bundling bitblts
+
+
+Known bugs/problems:
+
diff --git a/krdc/_rfb._tcp b/krdc/_rfb._tcp
new file mode 100644
index 00000000..ad7f6db3
--- /dev/null
+++ b/krdc/_rfb._tcp
@@ -0,0 +1,4 @@
+Name=Shared desktops
+Type=_rfb._tcp
+Protocol=vnc
+Helper=krdc
diff --git a/krdc/close.png b/krdc/close.png
new file mode 100644
index 00000000..2415dfc7
--- /dev/null
+++ b/krdc/close.png
Binary files differ
diff --git a/krdc/configure.in.in b/krdc/configure.in.in
new file mode 100644
index 00000000..23a47a75
--- /dev/null
+++ b/krdc/configure.in.in
@@ -0,0 +1,14 @@
+KDE_CHECK_HEADER(X11/extensions/xf86vmode.h, [
+ AC_DEFINE(HAVE_VIDMODE_EXTENSION, 1, [defined if XFree86's VidMode extension is supported])
+ LIBXF86VIDMODE="-lXext -lXxf86vm"
+], ,
+[
+#include <X11/Xlib.h>
+])
+AC_SUBST(LIBXF86VIDMODE)
+
+KDE_CHECK_SSL()
+
+if test "$have_ssl" != yes || test "x$kde_use_qt_mac" = "xyes"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE krdc"
+fi
diff --git a/krdc/cr32-app-krdc.png b/krdc/cr32-app-krdc.png
new file mode 100644
index 00000000..981267f1
--- /dev/null
+++ b/krdc/cr32-app-krdc.png
Binary files differ
diff --git a/krdc/cr48-app-krdc.png b/krdc/cr48-app-krdc.png
new file mode 100644
index 00000000..1e7d236e
--- /dev/null
+++ b/krdc/cr48-app-krdc.png
Binary files differ
diff --git a/krdc/events.h b/krdc/events.h
new file mode 100644
index 00000000..7ac262a7
--- /dev/null
+++ b/krdc/events.h
@@ -0,0 +1,204 @@
+/***************************************************************************
+ events.h - QCustomEvents
+ -------------------
+ begin : Wed Jun 05 01:13:42 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <stdlib.h>
+
+#ifndef EVENTS_H
+#define EVENTS_H
+
+#include <qevent.h>
+
+/**
+ * State of the connection. The state of the connection is returned
+ * by @ref KRemoteView::status().
+ *
+ * Not every state transition is allowed. You are only allowed to transition
+ * a state to the following state, with three exceptions:
+ * @li You can move from every state directly to REMOTE_VIEW_DISCONNECTED
+ * @li You can move from every state except REMOTE_VIEW_DISCONNECTED to
+ * REMOTE_VIEW_DISCONNECTING
+ * @li You can move from REMOTE_VIEW_DISCONNECTED to REMOTE_VIEW_CONNECTING
+ *
+ * @ref KRemoteView::setStatus() will follow this rules for you.
+ * (If you add/remove a state here, you must adapt it)
+ */
+enum RemoteViewStatus {
+ REMOTE_VIEW_CONNECTING = 0,
+ REMOTE_VIEW_AUTHENTICATING = 1,
+ REMOTE_VIEW_PREPARING = 2,
+ REMOTE_VIEW_CONNECTED = 3,
+ REMOTE_VIEW_DISCONNECTING = -1,
+ REMOTE_VIEW_DISCONNECTED = -2
+};
+
+enum ErrorCode {
+ ERROR_NONE = 0,
+ ERROR_INTERNAL,
+ ERROR_CONNECTION,
+ ERROR_PROTOCOL,
+ ERROR_IO,
+ ERROR_NAME,
+ ERROR_NO_SERVER,
+ ERROR_SERVER_BLOCKED,
+ ERROR_AUTHENTICATION
+};
+
+const int ScreenResizeEventType = 41001;
+
+class ScreenResizeEvent : public QCustomEvent
+{
+private:
+ int m_width, m_height;
+public:
+ ScreenResizeEvent(int w, int h) :
+ QCustomEvent(ScreenResizeEventType),
+ m_width(w),
+ m_height(h)
+ {};
+ int width() const { return m_width; };
+ int height() const { return m_height; };
+};
+
+const int StatusChangeEventType = 41002;
+
+class StatusChangeEvent : public QCustomEvent
+{
+private:
+ RemoteViewStatus m_status;
+public:
+ StatusChangeEvent(RemoteViewStatus s) :
+ QCustomEvent(StatusChangeEventType),
+ m_status(s)
+ {};
+ RemoteViewStatus status() const { return m_status; };
+};
+
+const int PasswordRequiredEventType = 41003;
+
+class PasswordRequiredEvent : public QCustomEvent
+{
+public:
+ PasswordRequiredEvent() :
+ QCustomEvent(PasswordRequiredEventType)
+ {};
+};
+
+const int FatalErrorEventType = 41004;
+
+class FatalErrorEvent : public QCustomEvent
+{
+ ErrorCode m_error;
+public:
+ FatalErrorEvent(ErrorCode e) :
+ QCustomEvent(FatalErrorEventType),
+ m_error(e)
+ {};
+
+ ErrorCode errorCode() const { return m_error; }
+};
+
+const int DesktopInitEventType = 41005;
+
+class DesktopInitEvent : public QCustomEvent
+{
+public:
+ DesktopInitEvent() :
+ QCustomEvent(DesktopInitEventType)
+ {};
+};
+
+const int ScreenRepaintEventType = 41006;
+
+class ScreenRepaintEvent : public QCustomEvent
+{
+private:
+ int m_x, m_y, m_width, m_height;
+public:
+ ScreenRepaintEvent(int x, int y, int w, int h) :
+ QCustomEvent(ScreenRepaintEventType),
+ m_x(x),
+ m_y(y),
+ m_width(w),
+ m_height(h)
+ {};
+ int x() const { return m_x; };
+ int y() const { return m_y; };
+ int width() const { return m_width; };
+ int height() const { return m_height; };
+};
+
+const int BeepEventType = 41007;
+
+class BeepEvent : public QCustomEvent
+{
+public:
+ BeepEvent() :
+ QCustomEvent(BeepEventType)
+ {};
+};
+
+const int ServerCutEventType = 41008;
+
+class ServerCutEvent : public QCustomEvent
+{
+private:
+ char *m_bytes;
+ int m_length;
+public:
+ ServerCutEvent(char *bytes, int length) :
+ QCustomEvent(ServerCutEventType),
+ m_bytes(bytes),
+ m_length(length)
+ {};
+ ~ServerCutEvent() {
+ free(m_bytes);
+ }
+ int length() const { return m_length; };
+ char *bytes() const { return m_bytes; };
+};
+
+const int MouseStateEventType = 41009;
+
+class MouseStateEvent : public QCustomEvent
+{
+private:
+ int m_x, m_y, m_buttonMask;
+public:
+ MouseStateEvent(int x, int y, int buttonMask) :
+ QCustomEvent(MouseStateEventType),
+ m_x(x),
+ m_y(y),
+ m_buttonMask(buttonMask)
+ {};
+ ~MouseStateEvent() {
+ }
+ int x() const { return m_x; };
+ int y() const { return m_y; };
+ int buttonMask() const { return m_buttonMask; };
+};
+
+const int WalletOpenEventType = 41010;
+
+class WalletOpenEvent : public QCustomEvent
+{
+public:
+ WalletOpenEvent() :
+ QCustomEvent(WalletOpenEventType)
+ {};
+};
+
+#endif
diff --git a/krdc/hostpreferences.cpp b/krdc/hostpreferences.cpp
new file mode 100644
index 00000000..4b02191b
--- /dev/null
+++ b/krdc/hostpreferences.cpp
@@ -0,0 +1,192 @@
+/***************************************************************************
+ hostpreferences.cpp - per host preferences
+ -------------------
+ begin : Fri May 09 22:33 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ : (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "hostpreferences.h"
+#include "vnc/vnchostpref.h"
+#include "rdp/rdphostpref.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kstaticdeleter.h>
+
+#include <qregexp.h>
+#include <qmap.h>
+
+HostPreferences *HostPreferences::m_instance = 0;
+static KStaticDeleter<HostPreferences> sd;
+
+HostPreferences *HostPreferences::instance()
+{
+ if ( m_instance == 0 )
+ sd.setObject( m_instance, new HostPreferences() );
+
+ return m_instance;
+}
+
+HostPref::HostPref(KConfig *conf, const QString &host, const QString &type) :
+ m_host(host),
+ m_type(type),
+ m_config(conf) {
+}
+
+HostPref::~HostPref() {
+ m_config->sync();
+}
+
+QString HostPref::host() const {
+ return m_host;
+}
+
+QString HostPref::type() const {
+ return m_type;
+}
+
+QString HostPref::prefix() const {
+ return prefix(m_host, m_type);
+}
+
+QString HostPref::prefix(const QString &host, const QString &type) {
+ return QString("PerHost-%1-%2-").arg(type).arg(host);
+}
+
+
+HostPreferences::HostPreferences() {
+ m_config = kapp->config();
+}
+
+HostPreferences::~HostPreferences() {
+ if ( m_instance == this )
+ sd.setObject( m_instance, 0, false );
+}
+
+HostPrefPtr HostPreferences::getHostPref(const QString &host, const QString &type) {
+ m_config->setGroup("PerHostSettings");
+ if (!m_config->readBoolEntry(HostPref::prefix(host, type)+"exists"))
+ return 0;
+
+ if (type == VncHostPref::VncType) {
+ HostPrefPtr hp = new VncHostPref(m_config, host, type);
+ hp->load();
+ return hp;
+ }
+ else if(type == RdpHostPref::RdpType) {
+ HostPrefPtr hp = new RdpHostPref(m_config, host, type);
+ hp->load();
+ return hp;
+ }
+ Q_ASSERT(true);
+ return 0;
+}
+
+HostPrefPtr HostPreferences::createHostPref(const QString &host, const QString &type) {
+ HostPrefPtr hp = getHostPref(host, type);
+ if (hp)
+ return hp;
+
+ if(type == VncHostPref::VncType)
+ {
+ hp = new VncHostPref(m_config, host, type);
+ }
+ else if(type == RdpHostPref::RdpType)
+ {
+ hp = new RdpHostPref(m_config, host, type);
+ }
+ hp->setDefaults();
+ return hp;
+}
+
+HostPrefPtr HostPreferences::vncDefaults()
+{
+ HostPrefPtr hp = new VncHostPref( m_config );
+ hp->load();
+
+ return hp;
+}
+
+HostPrefPtr HostPreferences::rdpDefaults()
+{
+ HostPrefPtr hp = new RdpHostPref( m_config );
+ hp->load();
+
+ return hp;
+}
+
+HostPrefPtrList HostPreferences::getAllHostPrefs() {
+ HostPrefPtrList r;
+ QMap<QString, QString> map = m_config->entryMap("PerHostSettings");
+ QStringList keys = map.keys();
+ QStringList::iterator it = keys.begin();
+ while (it != keys.end()) {
+ QString key = *it;
+ if (key.endsWith("-exists")) {
+ QRegExp re("PerHost-([^-]+)-(.*)-exists");
+ if (re.exactMatch(key)) {
+ HostPrefPtr hp = getHostPref(re.cap(2), re.cap(1));
+ if (hp)
+ r += hp;
+ }
+
+ }
+ it++;
+ }
+ return r;
+}
+
+void HostPreferences::removeHostPref(HostPref *hostPref) {
+ hostPref->remove();
+}
+
+void HostPreferences::setShowBrowsingPanel( bool b )
+{
+ m_config->setGroup( QString::null );
+ m_config->writeEntry( "browsingPanel", b );
+}
+
+void HostPreferences::setServerCompletions( const QStringList &list )
+{
+ m_config->setGroup( QString::null );
+ m_config->writeEntry( "serverCompletions", list );
+}
+
+void HostPreferences::setServerHistory( const QStringList &list )
+{
+ m_config->setGroup( QString::null );
+ m_config->writeEntry( "serverHistory", list );
+}
+
+bool HostPreferences::showBrowsingPanel()
+{
+ m_config->setGroup( QString::null );
+ return m_config->readBoolEntry( "browsingPanel" );
+}
+
+QStringList HostPreferences::serverCompletions()
+{
+ m_config->setGroup( QString::null );
+ return m_config->readListEntry( "serverCompletions" );
+}
+
+QStringList HostPreferences::serverHistory()
+{
+ m_config->setGroup( QString::null );
+ return m_config->readListEntry( "serverHistory" );
+}
+
+void HostPreferences::sync() {
+ m_config->sync();
+}
diff --git a/krdc/hostpreferences.h b/krdc/hostpreferences.h
new file mode 100644
index 00000000..09b4abe6
--- /dev/null
+++ b/krdc/hostpreferences.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ hostpreferences.h - per host preferences
+ -------------------
+ begin : Fri May 09 19:02 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ : (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 HOSTPREFERENCES_H
+#define HOSTPREFERENCES_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include "smartptr.h"
+
+class HostPreferences;
+class KConfig;
+
+class HostPref {
+protected:
+ friend class HostPreferences;
+ QString m_host;
+ QString m_type;
+ KConfig *m_config;
+
+ HostPref(KConfig *conf, const QString &host, const QString &type);
+
+ virtual void load() = 0;
+ virtual void setDefaults() = 0;
+ virtual void save() = 0;
+ virtual void remove() = 0;
+public:
+ virtual ~HostPref();
+
+ virtual QString prefDescription() const = 0;
+ QString host() const;
+ QString type() const;
+ QString prefix() const;
+ static QString prefix(const QString &host, const QString &type);
+};
+
+typedef SmartPtr<HostPref> HostPrefPtr;
+typedef QValueList<HostPrefPtr> HostPrefPtrList;
+
+class HostPreferences {
+public:
+ static HostPreferences *instance();
+ ~HostPreferences();
+
+ HostPrefPtr getHostPref(const QString &host, const QString &type);
+ HostPrefPtr createHostPref(const QString &host, const QString &type);
+ HostPrefPtrList getAllHostPrefs();
+ HostPrefPtr vncDefaults();
+ HostPrefPtr rdpDefaults();
+ void removeHostPref(HostPref *hostPref);
+
+ void setShowBrowsingPanel( bool b );
+ void setServerCompletions( const QStringList &list );
+ void setServerHistory( const QStringList &list );
+
+ bool showBrowsingPanel();
+ QStringList serverCompletions();
+ QStringList serverHistory();
+
+ void sync();
+
+private:
+ HostPreferences();
+
+ KConfig *m_config;
+ static HostPreferences *m_instance;
+
+};
+
+#endif
diff --git a/krdc/hostprofiles.ui b/krdc/hostprofiles.ui
new file mode 100644
index 00000000..2b36919b
--- /dev/null
+++ b/krdc/hostprofiles.ui
@@ -0,0 +1,168 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>HostProfiles</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>HostProfiles</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>451</width>
+ <height>222</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView" row="0" column="0" rowspan="1" colspan="3">
+ <column>
+ <property name="text">
+ <string>Host</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Settings</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>hostListView</cstring>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>Multi</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="itemsMovable">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This list shows all hosts that you have visited and a summary of your settings for them. If you want to reset the setting for a host, you can delete it using the buttons below. When you connect again you can then re-configure them.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <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>117</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>removeHostButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove Selected Host</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Deletes the hosts that you have selected in the list above.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>removeAllButton</cstring>
+ </property>
+ <property name="text">
+ <string>Remove &amp;All Hosts</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Removes all hosts from the list.</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>removeHostButton</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProfiles</receiver>
+ <slot>removeHost()</slot>
+ </connection>
+ <connection>
+ <sender>removeAllButton</sender>
+ <signal>clicked()</signal>
+ <receiver>HostProfiles</receiver>
+ <slot>removeAllHosts()</slot>
+ </connection>
+ <connection>
+ <sender>hostListView</sender>
+ <signal>selectionChanged()</signal>
+ <receiver>HostProfiles</receiver>
+ <slot>selectionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>hostListView</sender>
+ <signal>doubleClicked(QListViewItem*,const QPoint&amp;,int)</signal>
+ <receiver>HostProfiles</receiver>
+ <slot>slotHostDoubleClicked(QListViewItem*)</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in declaration">hostpreferences.h</include>
+ <include location="local" impldecl="in implementation">hostprofiles.ui.h</include>
+</includes>
+<variables>
+ <variable>HostPrefPtrList deletedHosts;</variable>
+</variables>
+<signals>
+ <signal>hostDoubleClicked(HostPrefPtr)</signal>
+</signals>
+<slots>
+ <slot>removeHost()</slot>
+ <slot access="protected" specifier="non virtual">removeAllHosts()</slot>
+ <slot access="protected" specifier="non virtual">selectionChanged()</slot>
+ <slot>slotHostDoubleClicked( QListViewItem * )</slot>
+</slots>
+<functions>
+ <function specifier="non virtual">load()</function>
+ <function specifier="non virtual">save()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/krdc/hostprofiles.ui.h b/krdc/hostprofiles.ui.h
new file mode 100644
index 00000000..56c63f23
--- /dev/null
+++ b/krdc/hostprofiles.ui.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+void HostProfiles::removeHost()
+{
+ HostPreferences *hps = HostPreferences::instance();
+
+ QListViewItemIterator it(hostListView);
+ while (it.current())
+ {
+ QListViewItem *vi = it.current();
+ if (vi->isSelected())
+ {
+ HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1));
+ if (hp)
+ deletedHosts += hp;
+ delete vi;
+ }
+ else
+ ++it;
+ }
+ removeAllButton->setEnabled(hostListView->childCount() > 0);
+}
+
+void HostProfiles::removeAllHosts()
+{
+ HostPreferences *hps = HostPreferences::instance();
+
+ QListViewItemIterator it(hostListView);
+ while (it.current())
+ {
+ QListViewItem *vi = it.current();
+ HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1));
+ if (hp)
+ deletedHosts += hp;
+ ++it;
+ }
+ hostListView->clear();
+ removeAllButton->setEnabled(false);
+}
+
+
+void HostProfiles::selectionChanged()
+{
+ QListViewItemIterator it(hostListView);
+ while (it.current())
+ {
+ if (it.current()->isSelected())
+ {
+ removeHostButton->setEnabled(true);
+ return;
+ }
+ ++it;
+ }
+ removeHostButton->setEnabled(false);
+}
+
+
+void HostProfiles::load()
+{
+ HostPreferences *hps = HostPreferences::instance();
+
+ HostPrefPtrList hplist = hps->getAllHostPrefs();
+ HostPrefPtrList::iterator it = hplist.begin();
+ while ( it != hplist.end() )
+ {
+ HostPref *hp = *it;
+ new KListViewItem( hostListView, hp->host(), hp->type(),
+ hp->prefDescription() );
+ ++it;
+ }
+}
+
+
+void HostProfiles::save()
+{
+ HostPreferences *hps = HostPreferences::instance();
+
+ HostPrefPtrList::iterator it = deletedHosts.begin();
+ while (it != deletedHosts.end())
+ {
+ hps->removeHostPref(*it);
+ it++;
+ }
+
+ hps->sync();
+}
+
+void HostProfiles::slotHostDoubleClicked( QListViewItem *vi )
+{
+ HostPreferences *hps = HostPreferences::instance();
+ HostPrefPtr hp = hps->getHostPref(vi->text(0), vi->text(1));
+ emit( hostDoubleClicked( hp ));
+}
diff --git a/krdc/iconify.png b/krdc/iconify.png
new file mode 100644
index 00000000..d9bd0550
--- /dev/null
+++ b/krdc/iconify.png
Binary files differ
diff --git a/krdc/keycapturedialog.cpp b/krdc/keycapturedialog.cpp
new file mode 100644
index 00000000..39a68304
--- /dev/null
+++ b/krdc/keycapturedialog.cpp
@@ -0,0 +1,143 @@
+/***************************************************************************
+ keycapturedialog.cpp - KeyCaptureDialog
+ -------------------
+ begin : Wed Dec 25 01:20:22 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ (C) unknown (whoever wrote kshortcutdialog.cpp)
+ (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+//
+// based on key capture code from kdelibs/kdeui/kshortcutdialog.cpp
+//
+
+#include "keycapturedialog.h"
+#include "keycapturewidget.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <klocale.h>
+
+#define XK_XKB_KEYS
+#define XK_MISCELLANY
+#include <X11/Xlib.h> // For x11Event()
+#include <X11/keysymdef.h> // For XK_...
+
+#ifdef KeyPress
+const int XFocusOut = FocusOut;
+const int XFocusIn = FocusIn;
+const int XKeyPress = KeyPress;
+const int XKeyRelease = KeyRelease;
+#undef KeyRelease
+#undef KeyPress
+#undef FocusOut
+#undef FocusIn
+#endif
+
+
+KeyCaptureDialog::KeyCaptureDialog(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true, i18n( "Enter Key Combination" ),
+ Cancel, Cancel, true), m_grabbed(false) {
+ QFrame *main = makeMainWidget();
+ QVBoxLayout *layout = new QVBoxLayout( main, 0, KDialog::spacingHint() );
+ m_captureWidget = new KeyCaptureWidget( main, "m_captureWidget" );
+ layout->addWidget( m_captureWidget );
+ layout->addStretch();
+}
+
+KeyCaptureDialog::~KeyCaptureDialog() {
+ if (m_grabbed)
+ releaseKeyboard();
+}
+
+void KeyCaptureDialog::execute() {
+ m_captureWidget->keyLabel->setText("");
+ exec();
+ if (m_grabbed)
+ releaseKeyboard();
+}
+
+bool KeyCaptureDialog::x11Event(XEvent *pEvent)
+{
+ switch( pEvent->type ) {
+ case XKeyPress:
+ case XKeyRelease:
+ x11EventKeyPress( pEvent );
+ return true;
+ case XFocusIn:
+ if (!m_grabbed)
+ grabKeyboard();
+ return true;
+ case XFocusOut:
+ if (m_grabbed)
+ releaseKeyboard();
+ return true;
+ default:
+ break;
+ }
+ return QWidget::x11Event( pEvent );
+}
+
+void KeyCaptureDialog::x11EventKeyPress( XEvent *pEvent )
+{
+ // taken from kshortcutdialog.h
+ KKeyNative keyNative( pEvent );
+ uint keyModX = keyNative.mod(), keySymX = keyNative.sym();
+ if ((keySymX == XK_Escape) && !keyModX) {
+ accept();
+ return;
+ }
+
+ switch( keySymX ) {
+ // Don't allow setting a modifier key as an accelerator.
+ // Also, don't release the focus yet. We'll wait until
+ // we get a 'normal' key.
+ case XK_Shift_L: case XK_Shift_R: keyModX = KKeyNative::modX(KKey::SHIFT); break;
+ case XK_Control_L: case XK_Control_R: keyModX = KKeyNative::modX(KKey::CTRL); break;
+ case XK_Alt_L: case XK_Alt_R: keyModX = KKeyNative::modX(KKey::ALT); break;
+ // FIXME: check whether the Meta or Super key are for the Win modifier
+ case XK_Meta_L: case XK_Meta_R:
+ case XK_Super_L: case XK_Super_R: keyModX = KKeyNative::modX(KKey::WIN); break;
+ case XK_Hyper_L: case XK_Hyper_R:
+ case XK_Mode_switch:
+ case XK_Num_Lock:
+ case XK_Caps_Lock:
+ break;
+ default:
+ if( pEvent->type == XKeyPress && keyNative.sym() ) {
+ emit keyPressed(pEvent);
+ reject();
+ }
+ return;
+ }
+
+ // If we are editing the first key in the sequence,
+ // display modifier keys which are held down
+ if( pEvent->type == XKeyPress )
+ keyModX |= pEvent->xkey.state;
+ else
+ keyModX = pEvent->xkey.state & ~keyModX;
+
+ QString keyModStr;
+ if( keyModX & KKeyNative::modX(KKey::WIN) ) keyModStr += KKey::modFlagLabel(KKey::WIN) + "+";
+ if( keyModX & KKeyNative::modX(KKey::ALT) ) keyModStr += KKey::modFlagLabel(KKey::ALT) + "+";
+ if( keyModX & KKeyNative::modX(KKey::CTRL) ) keyModStr += KKey::modFlagLabel(KKey::CTRL) + "+";
+ if( keyModX & KKeyNative::modX(KKey::SHIFT) ) keyModStr += KKey::modFlagLabel(KKey::SHIFT) + "+";
+
+ // Display currently selected modifiers, or redisplay old key.
+ m_captureWidget->keyLabel->setText( keyModStr );
+}
+
+#include "keycapturedialog.moc"
+
diff --git a/krdc/keycapturedialog.h b/krdc/keycapturedialog.h
new file mode 100644
index 00000000..6f3aa312
--- /dev/null
+++ b/krdc/keycapturedialog.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ keycapturedialog2.h - KeyCaptureDialog
+ -------------------
+ begin : Wed Dec 25 01:16:23 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ : (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KEYCAPTUREDIALOG_H
+#define KEYCAPTUREDIALOG_H
+
+class KeyCaptureWidget;
+
+#include "keycapturedialog.h"
+
+#include <kshortcut.h>
+#include <kkeynative.h>
+#include <kdialogbase.h>
+
+class KeyCaptureDialog : public KDialogBase {
+ Q_OBJECT
+
+ bool m_grabbed;
+public:
+ KeyCaptureDialog(QWidget * parent= 0, const char *name = 0);
+ ~KeyCaptureDialog();
+
+public slots:
+ void execute();
+
+signals:
+ void keyPressed(XEvent *key);
+
+protected:
+ bool x11Event(XEvent*);
+ void x11EventKeyPress(XEvent*);
+
+ KeyCaptureWidget *m_captureWidget;
+};
+
+
+#endif
diff --git a/krdc/keycapturewidget.ui b/krdc/keycapturewidget.ui
new file mode 100644
index 00000000..aa2865ce
--- /dev/null
+++ b/krdc/keycapturewidget.ui
@@ -0,0 +1,105 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>KeyCaptureWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KeyCaptureWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>358</width>
+ <height>119</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Enter a special key or key combination to send to the remote side:</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>112</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>keyLabel</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="lineWidth">
+ <number>2</number>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <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>111</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>This function allows you to send a key combination like Ctrl+Alt+Del to the remote side. Press Esc to cancel.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter</set>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<includes>
+ <include location="global" impldecl="in implementation">kdialogbase.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/krdc/kfullscreenpanel.cpp b/krdc/kfullscreenpanel.cpp
new file mode 100644
index 00000000..84067359
--- /dev/null
+++ b/krdc/kfullscreenpanel.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ kfullscreenpanel.cpp - auto-hideable toolbar
+ -------------------
+ begin : Tue May 13 23:07:42 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kfullscreenpanel.h"
+#include <kdebug.h>
+
+Counter::Counter(float start) :
+ m_currentValue(start) {
+ connect(&m_timer, SIGNAL(timeout()), SLOT(timeout()));
+}
+
+void Counter::count(float stop, float stepSize, float frequency) {
+ m_timer.stop();
+ m_stopValue = stop;
+ m_stepSize = stepSize;
+ m_timeoutMs = (int)(1000.0 / frequency);
+ timeout();
+}
+
+void Counter::timeout() {
+ if (m_stepSize < 0) {
+ if (m_currentValue <= m_stopValue) {
+ m_currentValue = m_stopValue;
+ emit counted(m_currentValue);
+ emit countingDownFinished();
+ return;
+ }
+ } else {
+ if (m_currentValue >= m_stopValue) {
+ m_currentValue = m_stopValue;
+ emit counted(m_currentValue);
+ emit countingUpFinished();
+ return;
+ }
+ }
+ emit counted(m_currentValue);
+
+ m_currentValue += m_stepSize;
+ m_timer.start(m_timeoutMs, true);
+}
+
+
+KFullscreenPanel::KFullscreenPanel(QWidget* parent,
+ const char *name,
+ const QSize &resolution) :
+ QWidget(parent, name),
+ m_child(0),
+ m_layout(0),
+ m_fsResolution(resolution),
+ m_counter(0)
+{
+ connect(&m_counter, SIGNAL(countingDownFinished()), SLOT(hide()));
+ connect(&m_counter, SIGNAL(counted(float)), SLOT(movePanel(float)));
+}
+
+KFullscreenPanel::~KFullscreenPanel() {
+}
+
+void KFullscreenPanel::movePanel(float posY) {
+ move(x(), (int)posY);
+ if (!isVisible())
+ show();
+}
+
+void KFullscreenPanel::setChild(QWidget *child) {
+
+ if (m_layout)
+ delete m_layout;
+ m_child = child;
+
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(child);
+ doLayout();
+}
+
+void KFullscreenPanel::doLayout() {
+ QSize s = sizeHint();
+ setFixedSize(s);
+ setGeometry((m_fsResolution.width() - s.width())/2, 0,
+ s.width(), s.height());
+}
+
+void KFullscreenPanel::startShow() {
+ m_counter.count(0, height()/3.0, 24);
+}
+
+void KFullscreenPanel::startHide() {
+ m_counter.count(-height(), -height()/12.0, 24);
+}
+
+void KFullscreenPanel::enterEvent(QEvent*) {
+ emit mouseEnter();
+}
+
+void KFullscreenPanel::leaveEvent(QEvent*) {
+ emit mouseLeave();
+}
+
+
+#include "kfullscreenpanel.moc"
+
diff --git a/krdc/kfullscreenpanel.h b/krdc/kfullscreenpanel.h
new file mode 100644
index 00000000..43b4944d
--- /dev/null
+++ b/krdc/kfullscreenpanel.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ kfullscreenpanel.cpp - auto-hideable toolbar
+ -------------------
+ begin : Tue May 13 23:07:42 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KFULLSCREENPANEL_H
+#define KFULLSCREENPANEL_H
+
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qevent.h>
+#include <qtimer.h>
+
+class Counter : public QObject {
+ Q_OBJECT
+private:
+ QTimer m_timer;
+ float m_stopValue, m_currentValue, m_stepSize;
+ int m_timeoutMs;
+
+public:
+ Counter(float start);
+
+ void count(float stop, float stepSize, float frequency);
+private slots:
+ void timeout();
+
+signals:
+ void countingDownFinished();
+ void countingUpFinished();
+ void counted(float value);
+};
+
+
+class KFullscreenPanel : public QWidget {
+ Q_OBJECT
+private:
+ QWidget *m_child;
+ QVBoxLayout *m_layout;
+ QSize m_fsResolution;
+ Counter m_counter;
+
+ void doLayout();
+
+public:
+ KFullscreenPanel(QWidget* parent, const char *name,
+ const QSize &resolution);
+ ~KFullscreenPanel();
+
+ void setChild(QWidget *child);
+ void startShow();
+ void startHide();
+
+protected:
+ void enterEvent(QEvent *e);
+ void leaveEvent(QEvent *e);
+
+private slots:
+ void movePanel(float posY);
+
+signals:
+ void mouseEnter();
+ void mouseLeave();
+};
+
+#endif
diff --git a/krdc/krdc.cpp b/krdc/krdc.cpp
new file mode 100644
index 00000000..d7ded0fe
--- /dev/null
+++ b/krdc/krdc.cpp
@@ -0,0 +1,871 @@
+/***************************************************************************
+ krdc.cpp - main window
+ -------------------
+ begin : Tue May 13 23:07:42 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ (C) 2003 Nadeem Hasan <nhasan@kde.org>
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "krdc.h"
+#include "maindialog.h"
+#include "hostpreferences.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kcombobox.h>
+#include <kurl.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <ktoolbar.h>
+#include <ktoolbarbutton.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <kwin.h>
+#include <kstartupinfo.h>
+
+#include <qdockarea.h>
+#include <qlabel.h>
+#include <qwhatsthis.h>
+#include <qtooltip.h>
+
+#define BUMP_SCROLL_CONSTANT (200)
+
+const int VIEW_ONLY_ID = 10;
+const int SHOW_LOCAL_CURSOR_ID = 20;
+
+const int FS_AUTOHIDE_ID = 1;
+const int FS_FULLSCREEN_ID = 2;
+const int FS_SCALE_ID = 3;
+const int FS_HOSTLABEL_ID = 4;
+const int FS_ADVANCED_ID = 5;
+const int FS_ICONIFY_ID = 6;
+const int FS_CLOSE_ID = 7;
+
+const int KRDC::TOOLBAR_AUTOHIDE_TIMEOUT = 1000;
+const int KRDC::TOOLBAR_FPS_1000 = 10000;
+const int KRDC::TOOLBAR_SPEED_DOWN = 34;
+const int KRDC::TOOLBAR_SPEED_UP = 20;
+
+QScrollView2::QScrollView2(QWidget *w, const char *name) :
+ QScrollView(w, name) {
+ setMouseTracking(true);
+ viewport()->setMouseTracking(true);
+ horizontalScrollBar()->setMouseTracking(true);
+ verticalScrollBar()->setMouseTracking(true);
+}
+
+void QScrollView2::mouseMoveEvent( QMouseEvent *e )
+{
+ e->ignore();
+}
+
+
+QString KRDC::m_lastHost = "";
+
+KRDC::KRDC(WindowMode wm, const QString &host,
+ Quality q, const QString &encodings,
+ const QString &password,
+ bool scale,
+ bool localCursor,
+ QSize initialWindowSize) :
+ QWidget(0, 0, Qt::WStyle_ContextHelp),
+ m_layout(0),
+ m_scrollView(0),
+ m_view(0),
+ m_fsToolbar(0),
+ m_toolbar(0),
+ m_dockArea(0),
+ m_popup(0),
+ m_ftAutoHide(false),
+ m_showProgress(false),
+ m_host(host),
+ m_protocol(PROTOCOL_AUTO),
+ m_quality(q),
+ m_encodings(encodings),
+ m_password(password),
+ m_isFullscreen(wm),
+ m_oldResolution(),
+ m_fullscreenMinimized(false),
+ m_windowScaling(scale),
+ m_localCursor(localCursor),
+ m_initialWindowSize(initialWindowSize)
+{
+ connect(&m_autoHideTimer, SIGNAL(timeout()), SLOT(hideFullscreenToolbarNow()));
+ connect(&m_bumpScrollTimer, SIGNAL(timeout()), SLOT(bumpScroll()));
+
+ m_pindown = UserIcon("pindown");
+ m_pinup = UserIcon("pinup");
+
+ m_keyCaptureDialog = new KeyCaptureDialog(0, 0);
+
+ setMouseTracking(true);
+
+ KStartupInfo::appStarted();
+}
+
+bool KRDC::start()
+{
+ QString userName, password;
+ QString serverHost;
+ int serverPort = 5900;
+
+ if (!m_host.isNull() &&
+ (m_host != "vnc:/") &&
+ (m_host != "rdp:/")) {
+ if (m_host.startsWith("vnc:/"))
+ m_protocol = PROTOCOL_VNC;
+ if (m_host.startsWith("rdp:/"))
+ m_protocol = PROTOCOL_RDP;
+ if (!parseHost(m_host, m_protocol, serverHost, serverPort,
+ userName, password)) {
+ KMessageBox::error(0,
+ i18n("The entered host does not have the required form."),
+ i18n("Malformed URL or Host"));
+ emit disconnectedError();
+ return true;
+ }
+ } else {
+
+ MainDialog mainDlg(this, "MainDialog");
+ mainDlg.setRemoteHost(m_lastHost);
+
+ if (mainDlg.exec() == QDialog::Rejected) {
+ return false;
+ }
+
+ QString m_host = mainDlg.remoteHost();
+ m_lastHost = m_host;
+ if (m_host.startsWith("vnc:/"))
+ m_protocol = PROTOCOL_VNC;
+ if (m_host.startsWith("rdp:/"))
+ m_protocol = PROTOCOL_RDP;
+ if (!parseHost(m_host, m_protocol, serverHost, serverPort,
+ userName, password)) {
+ KMessageBox::error(0,
+ i18n("The entered host does not have the required form."),
+ i18n("Malformed URL or Host"));
+ emit disconnectedError();
+ return true;
+ }
+ }
+
+ setCaption(i18n("%1 - Remote Desktop Connection").arg(serverHost));
+
+ m_scrollView = new QScrollView2(this, "remote scrollview");
+ m_scrollView->setFrameStyle(QFrame::NoFrame);
+ m_scrollView->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,
+ QSizePolicy::Expanding));
+
+ switch(m_protocol)
+ {
+ case PROTOCOL_AUTO:
+ // fall through
+
+ case PROTOCOL_VNC:
+ m_view = new KVncView(this, 0, serverHost, serverPort,
+ m_password.isNull() ? password : m_password,
+ m_quality,
+ m_localCursor ? DOT_CURSOR_ON : DOT_CURSOR_AUTO,
+ m_encodings);
+ break;
+
+ case PROTOCOL_RDP:
+ m_view = new KRdpView(this, 0, serverHost, serverPort,
+ userName, m_password.isNull() ? password : m_password);
+ break;
+ }
+
+ m_view->setViewOnly(kapp->config()->readBoolEntry("viewOnly", false));
+
+ m_scrollView->addChild(m_view);
+ QWhatsThis::add(m_view, i18n("Here you can see the remote desktop. If the other side allows you to control it, you can also move the mouse, click or enter keystrokes. If the content does not fit your screen, click on the toolbar's full screen button or scale button. To end the connection, just close the window."));
+
+ connect(m_view, SIGNAL(changeSize(int,int)), SLOT(setSize(int,int)));
+ connect(m_view, SIGNAL(connected()), SLOT(show()));
+ connect(m_view, SIGNAL(disconnected()), SIGNAL(disconnected()));
+ // note that the disconnectedError() will be disconnected when kremoteview
+ // is completely initialized
+ connect(m_view, SIGNAL(disconnectedError()), SIGNAL(disconnectedError()));
+ connect(m_view, SIGNAL(statusChanged(RemoteViewStatus)),
+ SLOT(changeProgress(RemoteViewStatus)));
+ connect(m_view, SIGNAL(showingPasswordDialog(bool)),
+ SLOT(showingPasswordDialog(bool)));
+ connect(m_keyCaptureDialog, SIGNAL(keyPressed(XEvent*)),
+ m_view, SLOT(pressKey(XEvent*)));
+
+ return m_view->start();
+}
+
+void KRDC::changeProgress(RemoteViewStatus s) {
+ if (!m_progressDialog) {
+ m_progressDialog = new KProgressDialog(0, 0, QString::null,
+ "1234567890", false);
+ m_progressDialog->showCancelButton(true);
+ m_progressDialog->setMinimumDuration(0x7fffffff);//disable effectively
+ m_progress = m_progressDialog->progressBar();
+ m_progress->setTextEnabled(false);
+ m_progress->setTotalSteps(3);
+ connect(m_progressDialog, SIGNAL(cancelClicked()),
+ SIGNAL(disconnectedError()));
+ }
+
+ if (s == REMOTE_VIEW_CONNECTING) {
+ m_progress->setValue(0);
+ m_progressDialog->setLabel(i18n("Establishing connection..."));
+ m_progressDialog->setAllowCancel(false);
+ showProgressDialog();
+ }
+ else if (s == REMOTE_VIEW_AUTHENTICATING) {
+ m_progress->setValue(1);
+ m_progressDialog->setLabel(i18n("Authenticating..."));
+ m_progressDialog->setAllowCancel(true);
+ }
+ else if (s == REMOTE_VIEW_PREPARING) {
+ m_progress->setValue(2);
+ m_progressDialog->setLabel(i18n("Preparing desktop..."));
+ }
+ else if ((s == REMOTE_VIEW_CONNECTED) ||
+ (s == REMOTE_VIEW_DISCONNECTED)) {
+ m_progress->setValue(3);
+ hideProgressDialog();
+ if (s == REMOTE_VIEW_CONNECTED) {
+ QObject::disconnect(m_view, SIGNAL(disconnectedError()),
+ this, SIGNAL(disconnectedError()));
+ connect(m_view, SIGNAL(disconnectedError()),
+ SIGNAL(disconnected()));
+ }
+ else if (m_isFullscreen == WINDOW_MODE_FULLSCREEN)
+ switchToNormal(m_view->scaling());
+ }
+}
+
+void KRDC::showingPasswordDialog(bool b) {
+ if (!m_progressDialog)
+ return;
+ if (b)
+ hideProgressDialog();
+ else
+ showProgressDialog();
+}
+
+void KRDC::showProgressDialog() {
+ m_showProgress = true;
+ QTimer::singleShot(400, this, SLOT(showProgressTimeout()));
+}
+
+void KRDC::hideProgressDialog() {
+ m_showProgress = false;
+ m_progressDialog->hide();
+}
+
+void KRDC::showProgressTimeout() {
+ if (!m_showProgress)
+ return;
+
+ m_progressDialog->setMinimumSize(300, 50);
+ m_progressDialog->show();
+}
+
+void KRDC::quit() {
+ m_view->releaseKeyboard();
+ hide();
+ vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution);
+ if (m_view)
+ m_view->startQuitting();
+ emit disconnected();
+}
+
+bool KRDC::parseHost(QString &str, Protocol &prot, QString &serverHost, int &serverPort,
+ QString &userName, QString &password) {
+ QString s = str;
+ userName = QString::null;
+ password = QString::null;
+
+ if (prot == PROTOCOL_AUTO) {
+ if(s.startsWith("smb:/")>0) { //we know it's more likely to be windows..
+ s = "rdp:/" + s.section("smb:/", 1);
+ prot = PROTOCOL_RDP;
+ } else if(s.contains("://") > 0) {
+ s = s.section("://",1);
+ } else if(s.contains(":/") > 0) {
+ s = s.section(":/", 1);
+ }
+ }
+ if (prot == PROTOCOL_AUTO || prot == PROTOCOL_VNC) {
+ if (s.startsWith(":"))
+ s = "localhost" + s;
+ if (!s.startsWith("vnc:/"))
+ s = "vnc://" + s;
+ else if (!s.startsWith("vnc://")) // TODO: fix this in KURL!
+ s.insert(4, '/');
+ }
+ if (prot == PROTOCOL_RDP) {
+ if (!s.startsWith("rdp:/"))
+ s = "rdp://" + s;
+ else if (!s.startsWith("rdp://")) // TODO: fix this in KURL!
+ s.insert(4, '/');
+ }
+
+ KURL url(s);
+ if (!url.isValid())
+ return false;
+ serverHost = url.host();
+ if (serverHost.isEmpty())
+ serverHost = "localhost";
+ serverPort = url.port();
+ if ((prot == PROTOCOL_AUTO || prot == PROTOCOL_VNC) && serverPort < 100)
+ serverPort += 5900;
+ if (url.hasUser())
+ userName = url.user();
+ if (url.hasPass())
+ password = url.pass();
+
+ if (url.port()) {
+ if (url.hasUser())
+ str = QString("%1@%2:%3").arg(userName).arg(serverHost).arg(url.port());
+ else
+ str = QString("%1:%2").arg(serverHost).arg(url.port());
+ }
+ else {
+ if (url.hasUser())
+ str = QString("%1@%2").arg(userName).arg(serverHost);
+ else
+ str = QString("%1").arg(serverHost);
+ }
+ return true;
+}
+
+KRDC::~KRDC()
+{
+ // kill explicitly to avoid xlib calls by the threads after closing the window!
+ if (m_view)
+ delete m_view;
+}
+
+void KRDC::enableFullscreen(bool on)
+{
+ if (on) {
+ if (m_isFullscreen != WINDOW_MODE_FULLSCREEN)
+ switchToFullscreen(m_view->scaling());
+ }
+ else {
+ if (m_isFullscreen != WINDOW_MODE_NORMAL)
+ switchToNormal(m_view->scaling() || m_windowScaling);
+ }
+ m_view->switchFullscreen(on);
+}
+
+QSize KRDC::sizeHint()
+{
+ if ((m_isFullscreen != WINDOW_MODE_FULLSCREEN) && m_toolbar) {
+ int dockHint = m_dockArea->sizeHint().height();
+ dockHint = dockHint < 1 ? 1 : dockHint; // fix wrong size hint
+ return QSize(m_view->framebufferSize().width(),
+ dockHint + m_view->framebufferSize().height());
+ }
+ else
+ return m_view->framebufferSize();
+}
+
+QPopupMenu *KRDC::createPopupMenu(QWidget *parent) const {
+ KPopupMenu *pu = new KPopupMenu(parent);
+ pu->insertItem(i18n("View Only"), this, SLOT(viewOnlyToggled()), 0, VIEW_ONLY_ID);
+ pu->setCheckable(true);
+ pu->setItemChecked(VIEW_ONLY_ID, m_view->viewOnly());
+ if (m_view->supportsLocalCursor()) {
+ pu->insertItem(i18n("Always Show Local Cursor"), this,
+ SLOT(showLocalCursorToggled()), 0,
+ SHOW_LOCAL_CURSOR_ID);
+ pu->setCheckable(true);
+ pu->setItemChecked(SHOW_LOCAL_CURSOR_ID,
+ m_view->dotCursorState() == DOT_CURSOR_ON);
+ }
+ return pu;
+}
+
+void KRDC::switchToFullscreen(bool scaling)
+{
+ int x, y;
+
+ bool fromFullscreen = (m_isFullscreen == WINDOW_MODE_FULLSCREEN);
+
+ QWidget *desktop = QApplication::desktop();
+ QSize ds = desktop->size();
+ QSize fbs = m_view->framebufferSize();
+ bool scalingPossible = m_view->supportsScaling() &&
+ ((fbs.width() >= ds.width()) || (fbs.height() >= ds.height()));
+
+ if (!fromFullscreen) {
+ hide();
+ m_oldResolution = vidmodeFullscreenSwitch(qt_xdisplay(),
+ m_desktopWidget.screenNumber(this),
+ fbs.width(),
+ fbs.height(),
+ x, y);
+ if (m_oldResolution.valid)
+ m_fullscreenResolution = QSize(x, y);
+ else
+ m_fullscreenResolution = QApplication::desktop()->size();
+ m_isFullscreen = WINDOW_MODE_FULLSCREEN;
+ if (!scalingPossible)
+ m_windowScaling = m_view->scaling();
+ }
+
+ if (m_toolbar) {
+ m_toolbar->hide();
+ m_toolbar->deleteLater();
+ m_toolbar = 0;
+ m_dockArea->hide();
+ m_dockArea->deleteLater();
+ m_dockArea = 0;
+ }
+ if (m_popup) {
+ m_popup->deleteLater();
+ m_popup = 0;
+ }
+ if (m_fsToolbar) {
+ m_fsToolbar->hide();
+ m_fsToolbar->deleteLater();
+ m_fsToolbar = 0;
+ }
+
+ if (m_layout)
+ delete m_layout;
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(m_scrollView);
+
+ if (scalingPossible) {
+ m_view->enableScaling(scaling);
+ if (scaling)
+ m_view->resize(ds);
+ else
+ m_view->resize(fbs);
+ repositionView(true);
+ }
+ else
+ m_view->enableScaling(false);
+
+ m_fsToolbar = new KFullscreenPanel(this, "fstoolbar", m_fullscreenResolution);
+ connect(m_fsToolbar, SIGNAL(mouseEnter()), SLOT(showFullscreenToolbar()));
+ connect(m_fsToolbar, SIGNAL(mouseLeave()), SLOT(hideFullscreenToolbarDelayed()));
+
+ KToolBar *t = new KToolBar(m_fsToolbar);
+ m_fsToolbarWidget = t;
+
+ QIconSet pinIconSet;
+ pinIconSet.setPixmap(m_pinup, QIconSet::Automatic, QIconSet::Normal, QIconSet::On);
+ pinIconSet.setPixmap(m_pindown, QIconSet::Automatic, QIconSet::Normal, QIconSet::Off);
+ t->insertButton("pinup", FS_AUTOHIDE_ID);
+ KToolBarButton *pinButton = t->getButton(FS_AUTOHIDE_ID);
+ pinButton->setIconSet(pinIconSet);
+ QToolTip::add(pinButton, i18n("Autohide on/off"));
+ t->setToggle(FS_AUTOHIDE_ID);
+ t->setButton(FS_AUTOHIDE_ID, false);
+ t->addConnection(FS_AUTOHIDE_ID, SIGNAL(clicked()), this, SLOT(toggleFsToolbarAutoHide()));
+
+ t->insertButton("window_nofullscreen", FS_FULLSCREEN_ID);
+ KToolBarButton *fullscreenButton = t->getButton(FS_FULLSCREEN_ID);
+ QToolTip::add(fullscreenButton, i18n("Fullscreen"));
+ t->setToggle(FS_FULLSCREEN_ID);
+ t->setButton(FS_FULLSCREEN_ID, true);
+ t->addConnection(FS_FULLSCREEN_ID, SIGNAL(toggled(bool)), this, SLOT(enableFullscreen(bool)));
+
+ m_popup = createPopupMenu(t);
+ t->insertButton("configure", FS_ADVANCED_ID, m_popup, true, i18n("Advanced options"));
+ KToolBarButton *advancedButton = t->getButton(FS_ADVANCED_ID);
+ QToolTip::add(advancedButton, i18n("Advanced options"));
+ //advancedButton->setPopupDelay(0);
+
+ QLabel *hostLabel = new QLabel(t);
+ hostLabel->setName("kde toolbar widget");
+ hostLabel->setAlignment(Qt::AlignCenter);
+ hostLabel->setText(" "+m_host+" ");
+ t->insertWidget(FS_HOSTLABEL_ID, 150, hostLabel);
+ t->setItemAutoSized(FS_HOSTLABEL_ID, true);
+
+ if (scalingPossible) {
+ t->insertButton("viewmagfit", FS_SCALE_ID);
+ KToolBarButton *scaleButton = t->getButton(FS_SCALE_ID);
+ QToolTip::add(scaleButton, i18n("Scale view"));
+ t->setToggle(FS_SCALE_ID);
+ t->setButton(FS_SCALE_ID, scaling);
+ t->addConnection(FS_SCALE_ID, SIGNAL(toggled(bool)), this, SLOT(switchToFullscreen(bool)));
+ }
+
+ t->insertButton("iconify", FS_ICONIFY_ID);
+ KToolBarButton *iconifyButton = t->getButton(FS_ICONIFY_ID);
+ QToolTip::add(iconifyButton, i18n("Minimize"));
+ t->addConnection(FS_ICONIFY_ID, SIGNAL(clicked()), this, SLOT(iconify()));
+
+ t->insertButton("close", FS_CLOSE_ID);
+ KToolBarButton *closeButton = t->getButton(FS_CLOSE_ID);
+ QToolTip::add(closeButton, i18n("Close"));
+ t->addConnection(FS_CLOSE_ID, SIGNAL(clicked()), this, SLOT(quit()));
+
+ m_fsToolbar->setChild(t);
+
+ repositionView(true);
+ showFullScreen();
+
+ setMaximumSize(m_fullscreenResolution.width(),
+ m_fullscreenResolution.height());
+ setGeometry(0, 0, m_fullscreenResolution.width(),
+ m_fullscreenResolution.height());
+
+ KWin::setState(winId(), NET::StaysOnTop);
+
+ m_ftAutoHide = !m_ftAutoHide;
+ setFsToolbarAutoHide(!m_ftAutoHide);
+
+ if (!fromFullscreen) {
+ if (m_oldResolution.valid)
+ grabInput(qt_xdisplay(), winId());
+ m_view->grabKeyboard();
+ }
+
+ m_view->switchFullscreen( true );
+}
+
+void KRDC::switchToNormal(bool scaling)
+{
+ bool fromFullscreen = (m_isFullscreen == WINDOW_MODE_FULLSCREEN);
+ bool scalingChanged = (scaling != m_view->scaling());
+ m_windowScaling = false; // delete remembered scaling value
+ if (fromFullscreen) {
+ KWin::clearState(winId(), NET::StaysOnTop);
+ hide();
+ }
+ m_isFullscreen = WINDOW_MODE_NORMAL;
+ m_view->enableScaling(scaling);
+
+ m_view->releaseKeyboard();
+ if (m_oldResolution.valid) {
+ ungrabInput(qt_xdisplay());
+ vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution);
+ m_oldResolution = Resolution();
+ }
+
+ if (m_fsToolbar) {
+ m_fsToolbar->hide();
+ m_fsToolbar->deleteLater();
+ m_fsToolbar = 0;
+ }
+
+ if (!m_toolbar) {
+ m_dockArea = new QDockArea(Qt::Horizontal, QDockArea::Normal, this);
+ m_dockArea->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
+ QSizePolicy::Fixed));
+ KToolBar *t = new KToolBar(m_dockArea);
+ m_toolbar = t;
+ t->setIconText(KToolBar::IconTextRight);
+ connect(t, SIGNAL(placeChanged(QDockWindow::Place)), SLOT(toolbarChanged()));
+ t->insertButton("window_fullscreen", 0, true, i18n("Fullscreen"));
+ KToolBarButton *fullscreenButton = t->getButton(0);
+ QToolTip::add(fullscreenButton, i18n("Fullscreen"));
+ QWhatsThis::add(fullscreenButton, i18n("Switches to full screen. If the remote desktop has a different screen resolution, Remote Desktop Connection will automatically switch to the nearest resolution."));
+ t->setToggle(0);
+ t->setButton(0, false);
+ t->addConnection(0, SIGNAL(toggled(bool)), this, SLOT(enableFullscreen(bool)));
+
+ if (m_view->supportsScaling()) {
+ t->insertButton("viewmagfit", 1, true, i18n("Scale"));
+ KToolBarButton *scaleButton = t->getButton(1);
+ QToolTip::add(scaleButton, i18n("Scale view"));
+ QWhatsThis::add(scaleButton, i18n("This option scales the remote screen to fit your window size."));
+ t->setToggle(1);
+ t->setButton(1, scaling);
+ t->addConnection(1, SIGNAL(toggled(bool)), this, SLOT(switchToNormal(bool)));
+ }
+
+ t->insertButton("key_enter", 2, true, i18n("Special Keys"));
+ KToolBarButton *skButton = t->getButton(2);
+ QToolTip::add(skButton, i18n("Enter special keys."));
+ QWhatsThis::add(skButton, i18n("This option allows you to send special key combinations like Ctrl-Alt-Del to the remote host."));
+ t->addConnection(2, SIGNAL(clicked()), m_keyCaptureDialog, SLOT(execute()));
+
+ if (m_popup) {
+ m_popup->deleteLater();
+ m_popup = 0;
+ }
+
+ m_popup = createPopupMenu(t);
+ t->insertButton("configure", 3, m_popup, true, i18n("Advanced"));
+ KToolBarButton *advancedButton = t->getButton(3);
+ QToolTip::add(advancedButton, i18n("Advanced options"));
+ //advancedButton->setPopupDelay(0);
+
+ if (m_layout)
+ delete m_layout;
+ m_layout = new QVBoxLayout(this);
+ m_layout->addWidget(m_dockArea);
+ m_layout->addWidget(m_scrollView);
+ m_layout->setGeometry(QRect(0, 0, m_scrollView->width(),
+ m_dockArea->height() + m_scrollView->height()));
+ }
+
+ if (scaling) {
+ m_scrollView->installEventFilter(this);
+ m_view->resize(m_scrollView->size());
+ }
+ else {
+ m_scrollView->removeEventFilter(this);
+ m_view->resize(m_view->framebufferSize());
+ }
+
+ setMaximumSize(sizeHint());
+
+ repositionView(false);
+
+
+ if (!fromFullscreen) {
+ if (m_initialWindowSize.isValid()) {
+ resize(m_initialWindowSize);
+ m_initialWindowSize = QSize();
+ }
+ else if (!scalingChanged)
+ resize(sizeHint());
+ show();
+ if (scalingChanged)
+ m_view->update();
+ }
+ else
+ showNormal();
+}
+
+void KRDC::viewOnlyToggled() {
+ bool s = !m_view->viewOnly();
+ m_popup->setItemChecked(VIEW_ONLY_ID, s);
+ m_view->setViewOnly(s);
+}
+
+void KRDC::showLocalCursorToggled() {
+ bool s = (m_view->dotCursorState() != DOT_CURSOR_ON);
+ m_popup->setItemChecked(SHOW_LOCAL_CURSOR_ID, s);
+ m_view->showDotCursor(s ? DOT_CURSOR_ON : DOT_CURSOR_AUTO);
+}
+
+void KRDC::iconify()
+{
+ KWin::clearState(winId(), NET::StaysOnTop);
+
+ m_view->releaseKeyboard();
+ if (m_oldResolution.valid)
+ ungrabInput(qt_xdisplay());
+
+ vidmodeNormalSwitch(qt_xdisplay(), m_oldResolution);
+ m_oldResolution = Resolution();
+ showNormal();
+ showMinimized();
+ m_fullscreenMinimized = true;
+}
+
+void KRDC::toolbarChanged() {
+ setMaximumSize(sizeHint());
+
+ // resize window when toolbar is docked and it was maximized
+ QSize fs = m_view->framebufferSize();
+ QSize cs = size();
+ QSize cs1(cs.width(), cs.height()-1); // adjusted for QDockArea.height()==1
+ if ((fs == cs) || (fs == cs1))
+ resize(sizeHint());
+}
+
+
+bool KRDC::event(QEvent *e) {
+/* used to change resolution when fullscreen was minimized */
+ if ((!m_fullscreenMinimized) || (e->type() != QEvent::WindowActivate))
+ return QWidget::event(e);
+
+ m_fullscreenMinimized = false;
+ int x, y;
+ m_oldResolution = vidmodeFullscreenSwitch(qt_xdisplay(),
+ m_desktopWidget.screenNumber(this),
+ m_view->width(),
+ m_view->height(),
+ x, y);
+ if (m_oldResolution.valid)
+ m_fullscreenResolution = QSize(x, y);
+ else
+ m_fullscreenResolution = QApplication::desktop()->size();
+
+ showFullScreen();
+ setGeometry(0, 0, m_fullscreenResolution.width(),
+ m_fullscreenResolution.height());
+ if (m_oldResolution.valid)
+ grabInput(qt_xdisplay(), winId());
+ m_view->switchFullscreen( true );
+ KWin::setState(winId(), NET::StaysOnTop);
+
+ return QWidget::event(e);
+}
+
+bool KRDC::eventFilter(QObject *watched, QEvent *e) {
+/* used to get events from QScrollView on resize for scale mode*/
+ if (watched != m_scrollView)
+ return false;
+ if (e->type() != QEvent::Resize)
+ return false;
+
+ QResizeEvent *re = (QResizeEvent*) e;
+ m_view->resize(re->size());
+ return false;
+}
+
+void KRDC::setSize(int w, int h)
+{
+ int dw, dh;
+
+ QWidget *desktop = QApplication::desktop();
+ dw = desktop->width();
+ dh = desktop->height();
+
+ switch (m_isFullscreen) {
+ case WINDOW_MODE_AUTO:
+ if ((w > dw) || (h > dh))
+ switchToFullscreen(m_windowScaling);
+ else
+ switchToNormal(m_windowScaling);
+ break;
+ case WINDOW_MODE_NORMAL:
+ switchToNormal(m_windowScaling);
+ break;
+ case WINDOW_MODE_FULLSCREEN:
+ switchToFullscreen(m_windowScaling);
+ break;
+ }
+}
+
+void KRDC::repositionView(bool fullscreen) {
+ int ox = 0;
+ int oy = 0;
+
+ if (!m_scrollView)
+ return;
+
+ QSize s = m_view->size();
+
+ if (fullscreen) {
+ QSize d = m_fullscreenResolution;
+ bool margin = false;
+ if (d.width() > s.width())
+ ox = (d.width() - s.width()) / 2;
+ else if (d.width() < s.width())
+ margin = true;
+
+ if (d.height() > s.height())
+ oy = (d.height() - s.height()) / 2;
+ else if (d.height() < s.height())
+ margin = true;
+
+ if (margin)
+ m_layout->setMargin(1);
+ }
+
+ m_scrollView->moveChild(m_view, ox, oy);
+}
+
+void KRDC::toggleFsToolbarAutoHide() {
+ setFsToolbarAutoHide(!m_ftAutoHide);
+}
+
+void KRDC::setFsToolbarAutoHide(bool on) {
+
+ if (on == m_ftAutoHide)
+ return;
+ if (m_isFullscreen != WINDOW_MODE_FULLSCREEN)
+ return;
+
+ m_ftAutoHide = on;
+ if (!on)
+ showFullscreenToolbar();
+}
+
+void KRDC::hideFullscreenToolbarNow() {
+ if (m_fsToolbar && m_ftAutoHide)
+ m_fsToolbar->startHide();
+}
+
+void KRDC::bumpScroll() {
+ int x = QCursor::pos().x();
+ int y = QCursor::pos().y();
+ QSize s = m_view->size();
+ QSize d = m_fullscreenResolution;
+
+ if (d.width() < s.width()) {
+ if (x == 0)
+ m_scrollView->scrollBy(-BUMP_SCROLL_CONSTANT, 0);
+ else if (x == d.width()-1)
+ m_scrollView->scrollBy(BUMP_SCROLL_CONSTANT, 0);
+ }
+ if (d.height() < s.height()) {
+ if (y == 0)
+ m_scrollView->scrollBy(0, -BUMP_SCROLL_CONSTANT);
+ else if (y == d.height()-1)
+ m_scrollView->scrollBy(0, BUMP_SCROLL_CONSTANT);
+ }
+
+ m_bumpScrollTimer.start(333, true);
+}
+
+void KRDC::showFullscreenToolbar() {
+ m_fsToolbar->startShow();
+ m_autoHideTimer.stop();
+}
+
+void KRDC::hideFullscreenToolbarDelayed() {
+ if (!m_autoHideTimer.isActive())
+ m_autoHideTimer.start(TOOLBAR_AUTOHIDE_TIMEOUT, true);
+}
+
+void KRDC::mouseMoveEvent(QMouseEvent *e) {
+ if (m_isFullscreen != WINDOW_MODE_FULLSCREEN)
+ return;
+
+ int x = e->x();
+ int y = e->y();
+
+ /* Bump Scrolling */
+
+ QSize s = m_view->size();
+ QSize d = m_fullscreenResolution;
+ if ((d.width() < s.width()) || d.height() < s.height()) {
+ if ((x == 0) || (x >= d.width()-1) ||
+ (y == 0) || (y >= d.height()-1))
+ bumpScroll();
+ else
+ m_bumpScrollTimer.stop();
+ }
+
+ /* Toolbar autohide */
+
+ if (!m_ftAutoHide)
+ return;
+
+ int dw = d.width();
+ int w = m_fsToolbar->width();
+ int x1 = (dw - w)/4;
+ int x2 = dw - x1;
+
+ if (((y <= 0) && (x >= x1) && (x <= x2)))
+ showFullscreenToolbar();
+ else
+ hideFullscreenToolbarDelayed();
+ e->accept();
+}
+
+void KRDC::setLastHost(const QString &lastHost) {
+ m_lastHost = lastHost;
+}
+
+#include "krdc.moc"
diff --git a/krdc/krdc.desktop b/krdc/krdc.desktop
new file mode 100644
index 00000000..2d1a13cd
--- /dev/null
+++ b/krdc/krdc.desktop
@@ -0,0 +1,63 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Exec=krdc -caption "%c" %i %m %u
+Icon=krdc
+Terminal=false
+Name=Krdc
+Name[bn]=কে-আর-ডি-সি
+Name[ne]=के आर डी सी
+Name[zh_TW]=Krdc é ç«¯æ¡Œé¢é€£ç·š
+GenericName=Remote Desktop Connection
+GenericName[be]=ЗлучÑнне з аддаленым кампутарам
+GenericName[bg]=Връзка Ñ Ð¾Ñ‚Ð´. раб. мÑÑто
+GenericName[bn]=পà§à¦°à¦¤à§à¦¯à¦¨à§à¦¤ ডেসà§à¦•à¦Ÿà¦ª সংযোগ
+GenericName[bs]=Veza na udaljeni desktop
+GenericName[ca]=Connexió remota entre escriptoris
+GenericName[cs]=Vzdálené připojení pracovní plochy
+GenericName[cy]=Cysylltiad Penbwrdd Pell
+GenericName[da]=Ekstern desktopforbindelse
+GenericName[de]=Verbindung zu Fremdrechner
+GenericName[el]=ΣÏνδεση σε απομακÏυσμένη επιφάνεια εÏγασίας
+GenericName[eo]=Konektilo al foraj tabuloj
+GenericName[es]=Conexión remota de escritorio
+GenericName[et]=Kaugtöölaua ühendus
+GenericName[eu]=Urruneko mahaigain konexioa
+GenericName[fa]=اتصال رومیزی راه دور
+GenericName[fi]=Etätyöpöytäyhteys
+GenericName[fr]=Connexion à un bureau distant
+GenericName[ga]=Nasc Deisce Cianda
+GenericName[gl]=Conexión Remota de Escritorio
+GenericName[he]=חיבור לשולחן עבודה מרוחק
+GenericName[hu]=Távoli munkaasztal elérése
+GenericName[is]=Tengingar við fjarlæg skjáborð
+GenericName[it]=Connessione a desktop remoto
+GenericName[ja]=リモートデスクトップ接続
+GenericName[ka]=დáƒáƒ¨áƒáƒ áƒ”ბული სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜
+GenericName[kk]=Қашықтан Ò¯Ñтелге қоÑылу
+GenericName[km]=ការ​ážâ€‹áž—្ជាប់​ផ្ទៃ​ážáž»â€‹áž–ី​ចម្ងាយ
+GenericName[lt]=Nutolusio darbastalio prijungimas
+GenericName[nb]=Tilkobling til annet skrivebord
+GenericName[nds]=Schriefdisch-Feernverbinnen
+GenericName[ne]=टाढाको डेसà¥à¤•à¤Ÿà¤ª जडान
+GenericName[nl]=Verbinding met extern bureaublad
+GenericName[nn]=Skrivebordssamband over nettverket
+GenericName[pa]=ਰਿਮੋਟ ਡੈਸਕਟਾਪ ਕà©à¨¨à¨•à©ˆà¨¸à¨¼à¨¨
+GenericName[pl]=Zdalny dostęp do pulpitu
+GenericName[pt]=Ligação a Ecrãs Remotos
+GenericName[pt_BR]=Conexões com Ambientes de Trabalho Remotos
+GenericName[ru]=Общий рабочий Ñтол
+GenericName[sk]=Pripojenie na vzdialenú pracovnú plochu
+GenericName[sl]=Povezava z oddaljenim namizjem
+GenericName[sr]=Удаљено повезивање на радну површину
+GenericName[sr@Latn]=Udaljeno povezivanje na radnu površinu
+GenericName[sv]=Fjärranslutning till skrivbord
+GenericName[tr]=Uzak Masaüstü Bağlantısı
+GenericName[uk]=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· віддаленою Ñтільницею
+GenericName[uz]=Masofadagi ish stoliga ulanish
+GenericName[uz@cyrillic]=МаÑофадаги иш Ñтолига уланиш
+GenericName[zh_CN]=远程桌é¢è¿žæŽ¥
+GenericName[zh_HK]=é ç«¯æ¡Œé¢é€£ç·š
+GenericName[zh_TW]=連接é ç«¯æ¡Œé¢
+DocPath=krdc/index.html
+Categories=Qt;KDE;Network;RemoteAccess;
diff --git a/krdc/krdc.h b/krdc/krdc.h
new file mode 100644
index 00000000..5bf9a4bd
--- /dev/null
+++ b/krdc/krdc.h
@@ -0,0 +1,175 @@
+/***************************************************************************
+ krdc.h - main window
+ -------------------
+ begin : Tue May 13 23:10:42 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KRDC_H
+#define KRDC_H
+
+#include <kprogress.h>
+#include <qscrollview.h>
+#include <qlayout.h>
+#include <qsize.h>
+#include <qrect.h>
+#include <qtimer.h>
+#include <qdesktopwidget.h>
+
+#include "vnc/kvncview.h"
+#include "rdp/krdpview.h"
+#include "kfullscreenpanel.h"
+#include "vidmode.h"
+#include "smartptr.h"
+#include "keycapturedialog.h"
+
+class QPixmap;
+class KToolBar;
+class QPopupMenu;
+class QDockArea;
+
+enum WindowMode {
+ WINDOW_MODE_AUTO,
+ WINDOW_MODE_NORMAL,
+ WINDOW_MODE_FULLSCREEN
+};
+
+// known protocols
+enum Protocol {
+ PROTOCOL_AUTO,
+ PROTOCOL_VNC,
+ PROTOCOL_RDP
+};
+
+// Overloaded QScrollView, to let mouse move events through to remote widget
+class QScrollView2 : public QScrollView {
+public:
+ QScrollView2(QWidget *w, const char *name);
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *e);
+};
+
+
+class KRDC : public QWidget
+{
+ Q_OBJECT
+private:
+ SmartPtr<KProgressDialog> m_progressDialog; // dialog, displayed while connecting
+ QVBoxLayout *m_layout; // the layout for autosizing the scrollview
+ QScrollView *m_scrollView; // scrollview that contains the remote widget
+ KProgress *m_progress; // progress bar for the dialog
+ KRemoteView *m_view; // the remote widget (e.g. KVncView)
+
+ SmartPtr<KeyCaptureDialog> m_keyCaptureDialog; // dialog for key capturing
+ KFullscreenPanel *m_fsToolbar; // toolbar for fullscreen (0 in normal mode)
+ QWidget *m_fsToolbarWidget; // qt designer widget for fs toolbar
+ // (invalid in normal mode)
+ QPixmap m_pinup, m_pindown; // fs toolbar imaged for autohide button
+ KToolBar *m_toolbar; // toolbar in normal mode (0 in fs mode)
+ QDockArea *m_dockArea; // dock area for toolbar in normal mode (0 in fs mode)
+ QPopupMenu *m_popup; // advanced options popup (0 in fs mode)
+ QDesktopWidget m_desktopWidget;
+
+ static const int TOOLBAR_AUTOHIDE_TIMEOUT;
+ bool m_ftAutoHide; // if true auto hide in fs is activated
+ QTimer m_autoHideTimer; // timer for autohide
+
+ QTimer m_bumpScrollTimer; // timer for bump scrolling (in fs, when res too large)
+
+ bool m_showProgress; // can disable showing the progress dialog temporary
+ QString m_host; // host string as given from user
+ Protocol m_protocol; // the used protocol
+ Quality m_quality; // current quality setting
+ QString m_encodings; // string containing the encodings, space separated,
+ // used for config before connection
+ QString m_password; // if not null, contains the password to use
+ QString m_resolution;// contains an alternative resolution
+ QString m_keymap; // keymap on the terminal server
+
+ WindowMode m_isFullscreen; // fs/normal state
+ Resolution m_oldResolution; // conatins encoded res before fs
+ bool m_fullscreenMinimized; // true if window is currently minimized from fs
+ QSize m_fullscreenResolution; // xvidmode size (valid only in fs)
+ bool m_windowScaling; // used in startup and fullscreen to determine
+ // whether scaling should be enabled in norm mode.
+ // The current state is m_view->scaled().
+ bool m_localCursor; // show local cursor no matter what
+ QSize m_initialWindowSize; // initial window size (windowed mode only),
+ // invalid after first use
+ static QString m_lastHost; // remembers last value of host input
+
+ bool parseHost(QString &s, Protocol &prot, QString &serverHost, int &serverPort,
+ QString &userName, QString &password);
+
+ void repositionView(bool fullscreen);
+
+ void showProgressDialog();
+ void hideProgressDialog();
+
+ static const int TOOLBAR_FPS_1000;
+ static const int TOOLBAR_SPEED_DOWN;
+ static const int TOOLBAR_SPEED_UP;
+ void fsToolbarScheduleHidden();
+ QPopupMenu *createPopupMenu(QWidget *parent) const;
+
+protected:
+ virtual void mouseMoveEvent(QMouseEvent *e);
+ virtual bool event(QEvent *e);
+ virtual bool eventFilter(QObject *watched, QEvent *e);
+ virtual QSize sizeHint();
+
+public:
+ KRDC(WindowMode wm = WINDOW_MODE_AUTO,
+ const QString &host = QString::null,
+ Quality q = QUALITY_UNKNOWN,
+ const QString &encodings = QString::null,
+ const QString &password = QString::null,
+ bool scale = false,
+ bool localCursor = false,
+ QSize initialWindowSize = QSize());
+ ~KRDC();
+
+ bool start();
+
+ static void setLastHost(const QString &host);
+
+private slots:
+ void changeProgress(RemoteViewStatus s);
+ void showingPasswordDialog(bool b);
+ void showProgressTimeout();
+
+ void setSize(int w, int h);
+ void iconify();
+ void toolbarChanged();
+ void bumpScroll();
+
+ void toggleFsToolbarAutoHide();
+ void setFsToolbarAutoHide(bool on);
+ void showFullscreenToolbar();
+ void hideFullscreenToolbarDelayed();
+ void hideFullscreenToolbarNow();
+
+public slots:
+ void quit();
+ void enableFullscreen(bool full = false);
+ void switchToNormal(bool scaling = false);
+ void switchToFullscreen(bool scaling = false);
+ void viewOnlyToggled();
+ void showLocalCursorToggled();
+
+signals:
+ void disconnected();
+ void disconnectedError();
+};
+
+#endif
diff --git a/krdc/kremoteview.cpp b/krdc/kremoteview.cpp
new file mode 100644
index 00000000..fab9ed29
--- /dev/null
+++ b/krdc/kremoteview.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ kremoteview.cpp - widget that shows the remote framebuffer
+ -------------------
+ begin : Wed Dec 26 00:21:14 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kremoteview.h"
+
+KRemoteView::KRemoteView(QWidget *parent,
+ const char *name,
+ WFlags f) :
+ QWidget(parent, name, f),
+ m_status(REMOTE_VIEW_DISCONNECTED) {
+}
+
+enum RemoteViewStatus KRemoteView::status() {
+ return m_status;
+}
+
+void KRemoteView::setStatus(RemoteViewStatus s) {
+ if (m_status == s)
+ return;
+
+ if (((1+(int)m_status) != (int)s) &&
+ (s != REMOTE_VIEW_DISCONNECTED)) {
+ // follow state transition rules
+
+ if (s == REMOTE_VIEW_DISCONNECTING) {
+ if (m_status == REMOTE_VIEW_DISCONNECTED)
+ return;
+ }
+ else {
+ Q_ASSERT(((int) s) >= 0);
+ if (((int)m_status) > ((int)s) ) {
+ m_status = REMOTE_VIEW_DISCONNECTED;
+ emit statusChanged(REMOTE_VIEW_DISCONNECTED);
+ }
+ // smooth state transition
+ int origState = (int)m_status;
+ for (int i = origState; i < (int)s; i++) {
+ m_status = (RemoteViewStatus) i;
+ emit statusChanged((RemoteViewStatus) i);
+ }
+ }
+ }
+ m_status = s;
+ emit statusChanged(m_status);
+}
+
+KRemoteView::~KRemoteView() {
+}
+
+bool KRemoteView::supportsScaling() const {
+ return false;
+}
+
+bool KRemoteView::supportsLocalCursor() const {
+ return false;
+}
+
+void KRemoteView::showDotCursor(DotCursorState) {
+}
+
+DotCursorState KRemoteView::dotCursorState() const {
+ return DOT_CURSOR_OFF;
+}
+
+bool KRemoteView::scaling() const {
+ return false;
+}
+
+void KRemoteView::enableScaling(bool) {
+}
+
+void KRemoteView::switchFullscreen(bool) {
+}
+
+#include "kremoteview.moc"
diff --git a/krdc/kremoteview.h b/krdc/kremoteview.h
new file mode 100644
index 00000000..d07b82a1
--- /dev/null
+++ b/krdc/kremoteview.h
@@ -0,0 +1,289 @@
+/***************************************************************************
+ kremoteview.h - widget that shows the remote framebuffer
+ -------------------
+ begin : Wed Dec 25 23:58:12 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KREMOTEVIEW_H
+#define KREMOTEVIEW_H
+
+
+#include <qwidget.h>
+#include <kkeynative.h>
+#include "events.h"
+
+typedef enum {
+ QUALITY_UNKNOWN=0,
+ QUALITY_HIGH=1,
+ QUALITY_MEDIUM=2,
+ QUALITY_LOW=3
+} Quality;
+
+/**
+ * Describes the state of a local cursor, if there is such a concept in the backend.
+ * With local cursors, there are two cursors: the cursor on the local machine (client),
+ * and the cursor on the remote machine (server). Because there is usually some lag,
+ * some backends show both cursors simultanously. In the VNC backend the local cursor
+ * is a dot and the remote cursor is the 'real' cursor, usually an arrow.
+ */
+enum DotCursorState {
+ DOT_CURSOR_ON, ///< Always show local cursor (and the remote one).
+ DOT_CURSOR_OFF, ///< Never show local cursor, only the remote one.
+ /// Try to measure the lag and enable the local cursor if the latency is too high.
+ DOT_CURSOR_AUTO
+};
+
+/**
+ * Generic widget that displays a remote framebuffer.
+ * Implement this if you want to add another backend.
+ *
+ * Things to take care of:
+ * @li The KRemoteView is responsible for its size. In
+ * non-scaling mode, set the fixed size of the widget
+ * to the remote resolution. In scaling mode, set the
+ * maximum size to the remote size and minimum size to the
+ * smallest resolution that your scaler can handle.
+ * @li if you override mouseMoveEvent()
+ * you must ignore the QEvent, because the KRDC widget will
+ * need it for stuff like toolbar auto-hide and bump
+ * scrolling. If you use x11Event(), make sure that
+ * MotionNotify events will be forwarded.
+ *
+ */
+class KRemoteView : public QWidget
+{
+ Q_OBJECT
+public:
+ KRemoteView(QWidget *parent = 0,
+ const char *name = 0,
+ WFlags f = 0);
+
+ virtual ~KRemoteView();
+
+ /**
+ * Checks whether the backend supports scaling. The
+ * default implementation returns false.
+ * @return true if scaling is supported
+ * @see scaling()
+ */
+ virtual bool supportsScaling() const;
+
+ /**
+ * Checks whether the widget is in scale mode. The
+ * default implementation always returns false.
+ * @return true if scaling is activated. Must always be
+ * false if @ref supportsScaling() returns false
+ * @see supportsScaling()
+ */
+ virtual bool scaling() const;
+
+ /**
+ * Checks whether the backend supports the concept of local cursors. The
+ * default implementation returns false.
+ * @return true if local cursors are supported/known
+ * @see DotCursorState
+ * @see showDotCursor()
+ * @see dotCursorState()
+ */
+ virtual bool supportsLocalCursor() const;
+
+ /**
+ * Sets the state of the dot cursor, if supported by the backend.
+ * The default implementation does nothing.
+ * @param state the new state (DOT_CURSOR_ON, DOT_CURSOR_OFF or
+ * DOT_CURSOR_AUTO)
+ * @see dotCursorState()
+ * @see supportsLocalCursor()
+ */
+ virtual void showDotCursor(DotCursorState state);
+
+ /**
+ * Returns the state of the local cursor. The default implementation returns
+ * always DOT_CURSOR_OFF.
+ * @return true if local cursors are supported/known
+ * @see showDotCursor()
+ * @see supportsLocalCursor()
+ */
+ virtual DotCursorState dotCursorState() const;
+
+ /**
+ * Checks whether the view is in view-only mode. This means
+ * that all input is ignored.
+ */
+ virtual bool viewOnly() = 0;
+
+ /**
+ * Returns the resolution of the remote framebuffer.
+ * It should return a null @ref QSize when the size
+ * is not known.
+ * The backend must also emit a @ref changeSize()
+ * when the size of the framebuffer becomes available
+ * for the first time or the size changed.
+ * @return the remote framebuffer size, a null QSize
+ * if unknown
+ */
+ virtual QSize framebufferSize() = 0;
+
+ /**
+ * Initiate the disconnection. This doesn't need to happen
+ * immediately. The call must not block.
+ * @see isQuitting()
+ */
+ virtual void startQuitting() = 0;
+
+ /**
+ * Checks whether the view is currently quitting.
+ * @return true if it is quitting
+ * @see startQuitting()
+ * @see setStatus()
+ */
+ virtual bool isQuitting() = 0;
+
+ /**
+ * Returns the host the view is connected to.
+ * @return the host the view is connected to
+ */
+ virtual QString host() = 0;
+
+ /**
+ * Returns the port the view is connected to.
+ * @return the port the view is connected to
+ */
+ virtual int port() = 0;
+
+ /**
+ * Initialize the view (for example by showing configuration
+ * dialogs to the user) and start connecting. Should not block
+ * without running the event loop (so displaying a dialog is ok).
+ * When the view starts connecting the application must call
+ * @ref setStatus() with the status REMOTE_VIEW_CONNECTING.
+ * @return true if successful (so far), false
+ * otherwise
+ * @see connected()
+ * @see disconnected()
+ * @see disconnectedError()
+ * @see statusChanged()
+ */
+ virtual bool start() = 0;
+
+ /**
+ * Returns the current status of the connection.
+ * @return the status of the connection
+ * @see setStatus()
+ */
+ enum RemoteViewStatus status();
+
+public slots:
+ /**
+ * Called to enable or disable scaling.
+ * Ignored if @ref supportsScaling() is false.
+ * The default implementation does nothing.
+ * @param s true to enable, false to disable.
+ * @see supportsScaling()
+ * @see scaling()
+ */
+ virtual void enableScaling(bool s);
+
+ /**
+ * Enables/disables the view-only mode.
+ * Ignored if @ref supportsScaling() is false.
+ * The default implementation does nothing.
+ * @param s true to enable, false to disable.
+ * @see supportsScaling()
+ * @see viewOnly()
+ */
+ virtual void setViewOnly(bool s) = 0;
+
+ /**
+ * Called to let the backend know it when
+ * we switch from/to fullscreen.
+ * @param on true when switching to fullscreen,
+ * false when switching from fullscreen.
+ */
+ virtual void switchFullscreen(bool on);
+
+ /**
+ * Sends a key to the remote server.
+ * @param k the key to send
+ */
+ virtual void pressKey(XEvent *k) = 0;
+
+signals:
+ /**
+ * Emitted when the size of the remote screen changes. Also
+ * called when the size is known for the first time.
+ * @param x the width of the screen
+ * @param y the height of the screen
+ */
+ void changeSize(int w, int h);
+
+ /**
+ * Emitted when the view connected successfully.
+ */
+ void connected();
+
+ /**
+ * Emitted when the view disconnected without error.
+ */
+ void disconnected();
+
+ /**
+ * Emitted when the view disconnected with error.
+ */
+ void disconnectedError();
+
+ /**
+ * Emitted when the status of the view changed.
+ * @param s the new status
+ */
+ void statusChanged(RemoteViewStatus s);
+
+ /**
+ * Emitted when the password dialog is shown or hidden.
+ * @param b true when the dialog is shown, false when it has
+ * been hidden
+ */
+ void showingPasswordDialog(bool b);
+
+ /**
+ * Emitted when the mouse on the remote side has been moved.
+ * @param x the new x coordinate
+ * @param y the new y coordinate
+ * @param buttonMask the mask of mouse buttons (bit 0 for first mouse
+ * button, 1 for second button etc)a
+ */
+ void mouseStateChanged(int x, int y, int buttonMask);
+
+protected:
+ /**
+ * The status of the remote view.
+ */
+ enum RemoteViewStatus m_status;
+
+ /**
+ * Set the status of the connection.
+ * Emits a statusChanged() signal.
+ * Note that the states need to be set in a certain order,
+ * see @ref RemoteViewStatus. setStatus() will try to do this
+ * transition automatically, so if you are in REMOTE_VIEW_CONNECTING
+ * and call setStatus(REMOTE_VIEW_PREPARING), setStatus() will
+ * emit a REMOTE_VIEW_AUTHENTICATING and then REMOTE_VIEW_PREPARING.
+ * If you transition backwards, it will emit a
+ * REMOTE_VIEW_DISCONNECTED before doing the transition.
+ * @param s the new status
+ */
+ virtual void setStatus(RemoteViewStatus s);
+};
+
+#endif
diff --git a/krdc/kservicelocator.cpp b/krdc/kservicelocator.cpp
new file mode 100644
index 00000000..b8382db1
--- /dev/null
+++ b/krdc/kservicelocator.cpp
@@ -0,0 +1,760 @@
+/*
+ * Interface to find SLP services.
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "kservicelocator.h"
+#include <kdebug.h>
+#include <qregexp.h>
+#include <qapplication.h>
+
+#ifdef HAVE_SLP
+#include <slp.h>
+#include <qevent.h>
+#include <qthread.h>
+#endif
+
+const QString KServiceLocator::DEFAULT_AUTHORITY = "";
+const QString KServiceLocator::ALL_AUTHORITIES = QString::null;
+
+class AsyncThread;
+
+class KServiceLocatorPrivate {
+public:
+ KServiceLocator *m_ksl;
+ bool m_opened;
+ volatile bool m_abort;
+ QString m_lang;
+ bool m_async;
+
+ bool ensureOpen();
+
+ KServiceLocatorPrivate(KServiceLocator *ksl,
+ const QString &lang,
+ bool async);
+ ~KServiceLocatorPrivate();
+
+#ifdef HAVE_SLP
+ SLPHandle m_handle;
+ AsyncThread *m_thread;
+ QString m_operationServiceUrl; // for findAttributes()/foundAttributes()
+
+ friend class FindSrvTypesThread;
+ friend class FindSrvsThread;
+ friend class FindAttrsThread;
+ friend class FindScopesThread;
+
+ friend SLPBoolean srvTypeCallback(SLPHandle hslp,
+ const char* srvtypes,
+ SLPError errcode,
+ void* cookie);
+ friend SLPBoolean srvTypeCallbackAsync(SLPHandle hslp,
+ const char* srvtypes,
+ SLPError errcode,
+ void* cookie);
+ friend SLPBoolean srvUrlCallback(SLPHandle hslp,
+ const char* srvurl,
+ unsigned short lifetime,
+ SLPError errcode,
+ void* cookie);
+ friend SLPBoolean srvUrlCallbackAsync(SLPHandle hslp,
+ const char* srvurl,
+ unsigned short lifetime,
+ SLPError errcode,
+ void* cookie);
+ friend SLPBoolean srvAttrCallback(SLPHandle hslp,
+ const char* attrlist,
+ SLPError errcode,
+ void* cookie);
+ friend SLPBoolean srvAttrCallbackAsync(SLPHandle hslp,
+ const char* attrlist,
+ SLPError errcode,
+ void* cookie);
+
+ SLPBoolean handleSrvTypeCallback(const char* srvtypes,
+ SLPError errcode);
+ SLPBoolean handleSrvTypeCallbackAsync(const char* srvtypes,
+ SLPError errcode);
+ SLPBoolean handleSrvUrlCallback(const char* srvUrl,
+ unsigned short lifetime,
+ SLPError errcode);
+ SLPBoolean handleSrvUrlCallbackAsync(const char* srvUrl,
+ unsigned short lifetime,
+ SLPError errcode);
+ SLPBoolean handleAttrCallback(const char* attrlist,
+ SLPError errcode);
+ SLPBoolean handleAttrCallbackAsync(const char* attrlist,
+ SLPError errcode);
+#endif
+};
+
+KServiceLocator::KServiceLocator(const QString &lang, bool async) :
+ QObject(0, "KServiceLocator") {
+
+ d = new KServiceLocatorPrivate(this, lang, async);
+}
+
+KServiceLocatorPrivate::KServiceLocatorPrivate(KServiceLocator *ksl,
+ const QString &lang,
+ bool async) :
+ m_ksl(ksl),
+ m_opened(false),
+ m_abort(false),
+ m_lang(lang),
+ m_async(async) {
+
+#ifdef HAVE_SLP
+ m_thread = 0;
+#endif
+}
+
+
+#ifdef HAVE_SLP /** The real SLP implementations ********************** */
+
+
+/* ****** *** ****** *** ****** */
+/* Signals for async events */
+/* ****** *** ****** *** ****** */
+
+const int MinLastSignalEventType = 45001;
+const int LastServiceTypeSignalEventType = 45001;
+const int LastServiceSignalEventType = 45002;
+const int LastAttributesSignalEventType = 45003;
+const int MaxLastSignalEventType = 45003;
+class LastSignalEvent : public QCustomEvent
+{
+private:
+ bool m_success;
+public:
+ LastSignalEvent(int type, bool s) :
+ QCustomEvent(type),
+ m_success(s)
+ {};
+ int success() const { return m_success; };
+};
+
+const int FoundServiceTypesEventType = 45012;
+class FoundServiceTypesEvent : public QCustomEvent
+{
+private:
+ QString m_srvtypes;
+public:
+ FoundServiceTypesEvent(const char *srvtypes) :
+ QCustomEvent(FoundServiceTypesEventType),
+ m_srvtypes(srvtypes)
+ {};
+ QString srvtypes() const { return m_srvtypes; };
+};
+
+const int FoundServiceEventType = 45013;
+class FoundServiceEvent : public QCustomEvent
+{
+private:
+ QString m_srvUrl;
+ unsigned short m_lifetime;
+public:
+ FoundServiceEvent(const char *srvUrl, unsigned short lifetime) :
+ QCustomEvent(FoundServiceEventType),
+ m_srvUrl(srvUrl),
+ m_lifetime(lifetime)
+ {};
+ QString srvUrl() const { return m_srvUrl; };
+ unsigned short lifetime() const { return m_lifetime; };
+};
+
+const int FoundAttributesEventType = 45014;
+class FoundAttributesEvent : public QCustomEvent
+{
+private:
+ QString m_attributes;
+public:
+ FoundAttributesEvent(const char *attributes) :
+ QCustomEvent(FoundAttributesEventType),
+ m_attributes(attributes)
+ {};
+ QString attributes() const { return m_attributes; };
+};
+
+const int FoundScopesEventType = 45015;
+class FoundScopesEvent : public QCustomEvent
+{
+private:
+ QString m_scopes;
+public:
+ FoundScopesEvent(const char *scopes) :
+ QCustomEvent(FoundScopesEventType),
+ m_scopes(scopes)
+ {};
+ QString scopes() const { return m_scopes; };
+};
+
+
+
+/* ****** *** ****** *** ****** */
+/* Callbacks */
+/* ****** *** ****** *** ****** */
+
+SLPBoolean srvTypeCallback(SLPHandle,
+ const char* srvtypes,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ return ksl->handleSrvTypeCallback(srvtypes, errcode);
+}
+SLPBoolean srvTypeCallbackAsync(SLPHandle,
+ const char* srvtypes,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ if ((errcode != SLP_OK) || ksl->m_abort) {
+ QApplication::postEvent(ksl->m_ksl, new LastSignalEvent(LastServiceTypeSignalEventType,
+ (errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ ksl->m_abort));
+ return SLP_FALSE;
+ }
+ QApplication::postEvent(ksl->m_ksl, new FoundServiceTypesEvent(srvtypes));
+ return SLP_TRUE;
+}
+
+SLPBoolean srvUrlCallback(SLPHandle,
+ const char* srvurl,
+ unsigned short lifetime,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ return ksl->handleSrvUrlCallback(srvurl, lifetime, errcode);
+}
+SLPBoolean srvUrlCallbackAsync(SLPHandle,
+ const char* srvurl,
+ unsigned short lifetime,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ if ((errcode != SLP_OK) || ksl->m_abort) {
+ QApplication::postEvent(ksl->m_ksl, new LastSignalEvent(LastServiceSignalEventType,
+ (errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ ksl->m_abort));
+ return SLP_FALSE;
+ }
+ QApplication::postEvent(ksl->m_ksl,
+ new FoundServiceEvent(srvurl, lifetime));
+ return SLP_TRUE;
+}
+
+SLPBoolean srvAttrCallback(SLPHandle,
+ const char* attrlist,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ return ksl->handleAttrCallback(attrlist, errcode);
+}
+SLPBoolean srvAttrCallbackAsync(SLPHandle,
+ const char* attrlist,
+ SLPError errcode,
+ void* cookie) {
+ KServiceLocatorPrivate *ksl = (KServiceLocatorPrivate*) cookie;
+ if ((errcode != SLP_OK) || ksl->m_abort) {
+ QApplication::postEvent(ksl->m_ksl,
+ new LastSignalEvent(LastAttributesSignalEventType,
+ (errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ ksl->m_abort));
+ return SLP_FALSE;
+ }
+ QApplication::postEvent(ksl->m_ksl,
+ new FoundAttributesEvent(attrlist));
+ return SLP_TRUE;
+}
+
+
+
+/* ****** *** ****** *** ****** */
+/* Threads for async events */
+/* ****** *** ****** *** ****** */
+
+class AsyncThread : public QThread {
+protected:
+ SLPHandle m_handle;
+ KServiceLocatorPrivate *m_parent;
+ AsyncThread(SLPHandle handle, KServiceLocatorPrivate *parent) :
+ m_handle(handle),
+ m_parent(parent) {
+ }
+};
+class FindSrvTypesThread : public AsyncThread {
+ QString m_namingAuthority, m_scopeList;
+public:
+ FindSrvTypesThread(SLPHandle handle,
+ KServiceLocatorPrivate *parent,
+ const char *namingAuthority,
+ const char *scopeList) :
+ AsyncThread(handle, parent),
+ m_namingAuthority(namingAuthority),
+ m_scopeList(scopeList){
+ }
+ virtual void run() {
+ SLPError e;
+ e = SLPFindSrvTypes(m_handle,
+ m_namingAuthority.latin1(),
+ m_scopeList.latin1(),
+ srvTypeCallbackAsync,
+ m_parent);
+ if (e != SLP_OK)
+ QApplication::postEvent(m_parent->m_ksl,
+ new LastSignalEvent(LastServiceTypeSignalEventType,
+ false));
+ }
+};
+class FindSrvsThread : public AsyncThread {
+ QString m_srvUrl, m_scopeList, m_filter;
+public:
+ FindSrvsThread(SLPHandle handle,
+ KServiceLocatorPrivate *parent,
+ const char *srvUrl,
+ const char *scopeList,
+ const char *filter) :
+ AsyncThread(handle, parent),
+ m_srvUrl(srvUrl),
+ m_scopeList(scopeList),
+ m_filter(filter) {
+ }
+ virtual void run() {
+ SLPError e;
+
+ e = SLPFindSrvs(m_handle,
+ m_srvUrl.latin1(),
+ m_scopeList.isNull() ? "" : m_scopeList.latin1(),
+ m_filter.isNull() ? "" : m_filter.latin1(),
+ srvUrlCallbackAsync,
+ m_parent);
+ if (e != SLP_OK)
+ QApplication::postEvent(m_parent->m_ksl,
+ new LastSignalEvent(LastServiceSignalEventType,
+ false));
+ }
+};
+class FindAttrsThread : public AsyncThread {
+ QString m_srvUrl, m_scopeList, m_attrIds;
+public:
+ FindAttrsThread(SLPHandle handle,
+ KServiceLocatorPrivate *parent,
+ const char *srvUrl,
+ const char *scopeList,
+ const char *attrIds) :
+ AsyncThread(handle, parent),
+ m_srvUrl(srvUrl),
+ m_scopeList(scopeList),
+ m_attrIds(attrIds) {
+ }
+ virtual void run() {
+ SLPError e;
+ e = SLPFindAttrs(m_handle,
+ m_srvUrl.latin1(),
+ m_scopeList.isNull() ? "" : m_scopeList.latin1(),
+ m_attrIds.isNull() ? "" : m_attrIds.latin1(),
+ srvAttrCallbackAsync,
+ m_parent);
+ if (e != SLP_OK)
+ QApplication::postEvent(m_parent->m_ksl,
+ new LastSignalEvent(LastAttributesSignalEventType,
+ false));
+ }
+};
+class FindScopesThread : public AsyncThread {
+public:
+ FindScopesThread(SLPHandle handle,
+ KServiceLocatorPrivate *parent) :
+ AsyncThread(handle, parent){
+ }
+ virtual void run() {
+ SLPError e;
+ char *_scopelist;
+
+ e = SLPFindScopes(m_handle, &_scopelist);
+ if (e != SLP_OK) {
+ QApplication::postEvent(m_parent->m_ksl,
+ new FoundScopesEvent(""));
+ return;
+ }
+
+ QString scopeList(_scopelist);
+ SLPFree(_scopelist);
+ QApplication::postEvent(m_parent->m_ksl,
+ new FoundScopesEvent(scopeList.latin1()));
+ }
+};
+
+KServiceLocatorPrivate::~KServiceLocatorPrivate() {
+ if (m_thread) {
+ m_abort = true;
+ m_thread->wait();
+ delete m_thread;
+ m_thread = 0; // important, because event handler will run
+ }
+ if (m_opened)
+ SLPClose(m_handle);
+}
+
+KServiceLocator::~KServiceLocator() {
+ delete d;
+}
+
+bool KServiceLocator::available() {
+ return d->ensureOpen();
+}
+
+void KServiceLocator::abortOperation() {
+ d->m_abort = true;
+}
+
+bool KServiceLocatorPrivate::ensureOpen() {
+ SLPError e;
+
+ if (m_opened)
+ return true;
+ e = SLPOpen(m_lang.latin1(), SLP_FALSE, &m_handle);
+ if (e != SLP_OK) {
+ kdError() << "KServiceLocator: error while opening:" << e <<endl;
+ return false;
+ }
+ m_opened = true;
+ return true;
+}
+
+bool KServiceLocator::findServiceTypes(const QString &namingAuthority,
+ const QString &scopeList) {
+ if (!d->ensureOpen())
+ return false;
+ if (d->m_thread)
+ return false;
+ d->m_abort = false;
+
+ if (d->m_async) {
+ d->m_thread = new FindSrvTypesThread(d->m_handle,
+ d,
+ namingAuthority.isNull() ? "*" : namingAuthority.latin1(),
+ scopeList.isNull() ? "" : scopeList.latin1());
+ d->m_thread->start();
+ return true;
+ }
+ else {
+ SLPError e;
+ e = SLPFindSrvTypes(d->m_handle,
+ namingAuthority.isNull() ? "*" : namingAuthority.latin1(),
+ scopeList.isNull() ? "" : scopeList.latin1(),
+ srvTypeCallback,
+ d);
+ return e == SLP_OK;
+ }
+}
+
+bool KServiceLocator::findServices(const QString &srvtype,
+ const QString &filter,
+ const QString &scopeList) {
+ if (!d->ensureOpen())
+ return false;
+ if (d->m_thread)
+ return false;
+ d->m_abort = false;
+
+ if (d->m_async) {
+ d->m_thread = new FindSrvsThread(d->m_handle,
+ d,
+ srvtype.latin1(),
+ scopeList.isNull() ? "" : scopeList.latin1(),
+ filter.isNull() ? "" : filter.latin1());
+ d->m_thread->start();
+ return true;
+ }
+ else {
+ SLPError e;
+ e = SLPFindSrvs(d->m_handle,
+ srvtype.latin1(),
+ scopeList.isNull() ? "" : scopeList.latin1(),
+ filter.isNull() ? "" : filter.latin1(),
+ srvUrlCallback,
+ d);
+ return e == SLP_OK;
+ }
+}
+
+bool KServiceLocator::findAttributes(const QString &serviceUrl,
+ const QString &attributeIds) {
+ if (!d->ensureOpen())
+ return false;
+ if (d->m_thread)
+ return false;
+ d->m_abort = false;
+
+ d->m_operationServiceUrl = serviceUrl;
+ if (d->m_async) {
+ d->m_thread = new FindAttrsThread(d->m_handle,
+ d,
+ serviceUrl.latin1(),
+ "",
+ attributeIds.isNull() ? "" : attributeIds.latin1());
+ d->m_thread->start();
+ return true;
+ }
+ else {
+ SLPError e;
+ e = SLPFindAttrs(d->m_handle,
+ serviceUrl.latin1(),
+ "",
+ attributeIds.isNull() ? "" : attributeIds.latin1(),
+ srvAttrCallback,
+ d);
+ return e == SLP_OK;
+ }
+}
+
+bool KServiceLocator::findScopes() {
+ if (!d->ensureOpen())
+ return false;
+ if (d->m_thread)
+ return false;
+ d->m_abort = false;
+
+ if (d->m_async) {
+ d->m_thread = new FindScopesThread(d->m_handle, d);
+ d->m_thread->start();
+ return true;
+ }
+ else {
+ SLPError e;
+ char *_scopelist;
+ QStringList scopeList;
+ e = SLPFindScopes(d->m_handle, &_scopelist);
+ if (e != SLP_OK)
+ return false;
+ scopeList = parseCommaList(_scopelist);
+ SLPFree(_scopelist);
+ emit foundScopes(scopeList);
+ return true;
+ }
+}
+
+SLPBoolean KServiceLocatorPrivate::handleSrvTypeCallback(const char* srvtypes,
+ SLPError errcode) {
+ if ((errcode != SLP_OK) || m_abort) {
+ m_ksl->emitLastServiceTypeSignal((errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ m_abort);
+ return SLP_FALSE;
+ }
+ m_ksl->emitFoundServiceTypes(srvtypes);
+ return SLP_TRUE;
+}
+
+SLPBoolean KServiceLocatorPrivate::handleSrvUrlCallback(const char* srvurl,
+ unsigned short lifetime,
+ SLPError errcode) {
+ if ((errcode != SLP_OK) || m_abort) {
+ m_ksl->emitLastServiceSignal((errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ m_abort);
+ return SLP_FALSE;
+ }
+ m_ksl->emitFoundService(srvurl, lifetime);
+ return SLP_TRUE;
+}
+
+SLPBoolean KServiceLocatorPrivate::handleAttrCallback(const char* attrlist,
+ SLPError errcode) {
+ if ((errcode != SLP_OK) || m_abort) {
+ m_ksl->emitLastAttributesSignal((errcode == SLP_OK) ||
+ (errcode == SLP_LAST_CALL) ||
+ m_abort);
+ return SLP_FALSE;
+ }
+ m_ksl->emitFoundAttributes(m_operationServiceUrl, attrlist);
+ return SLP_TRUE;
+}
+
+
+void KServiceLocator::customEvent(QCustomEvent *e) {
+ if ((e->type() >= MinLastSignalEventType) &&
+ (e->type() <= MaxLastSignalEventType)){
+ bool s = true;
+ if (d->m_thread) {
+ d->m_thread->wait();
+ delete d->m_thread;
+ d->m_thread = 0;
+ s = ((LastSignalEvent*)e)->success();
+ }
+ if (e->type() == LastServiceTypeSignalEventType)
+ emit lastServiceTypeSignal(s);
+ else if (e->type() == LastServiceSignalEventType)
+ emit lastServiceSignal(s);
+ else if (e->type() == LastAttributesSignalEventType)
+ emit lastAttributesSignal(s);
+ else
+ kdFatal() << "unmapped last signal type " << e->type()<< endl;
+ }
+ else if (e->type() == FoundAttributesEventType) {
+ emit foundAttributes(d->m_operationServiceUrl,
+ ((FoundAttributesEvent*)e)->attributes());
+ }
+ else if (e->type() == FoundServiceEventType) {
+ FoundServiceEvent *fse = (FoundServiceEvent*)e;
+ emit foundService(fse->srvUrl(), fse->lifetime());
+ }
+ else if (e->type() == FoundServiceTypesEventType) {
+ emit foundServiceTypes(((FoundServiceTypesEvent*)e)->srvtypes());
+ }
+ else if (e->type() == FoundScopesEventType) {
+ if (d->m_thread) {
+ d->m_thread->wait();
+ delete d->m_thread;
+ d->m_thread = 0;
+ emit foundScopes(KServiceLocator::parseCommaList(((FoundScopesEvent*)e)->scopes()));
+ }
+ }
+}
+
+QString KServiceLocator::decodeAttributeValue(const QString &value) {
+ char *n;
+ if (value.isNull())
+ return value;
+ if (SLPUnescape(value.latin1(), &n, SLP_TRUE) != SLP_OK)
+ return QString::null;
+ QString r(n);
+ SLPFree(n);
+ return r;
+}
+
+#else /** Empty dummy functions is SLP is not available ************************* */
+
+
+KServiceLocator::~KServiceLocator() {
+}
+bool KServiceLocator::available() {
+ return false;
+}
+void KServiceLocator::abortOperation() {
+}
+bool KServiceLocator::findServiceTypes(const QString &,const QString &) {
+ return false;
+}
+bool KServiceLocator::findServices(const QString &,const QString &,const QString &) {
+ return false;
+}
+bool KServiceLocator::findAttributes(const QString &,const QString &) {
+ return false;
+}
+bool KServiceLocator::findScopes() {
+ return false;
+}
+void KServiceLocator::customEvent(QCustomEvent *) {
+}
+QString KServiceLocator::decodeAttributeValue(const QString &value) {
+ return value;
+}
+
+#endif
+
+
+/*** Private emit-helpers ***/
+
+void KServiceLocator::emitFoundServiceTypes(QString serviceTypes) {
+ emit foundServiceTypes(serviceTypes);
+}
+void KServiceLocator::emitFoundService(QString serviceUrl,
+ int lifetime) {
+ emit foundService(serviceUrl, lifetime);
+}
+void KServiceLocator::emitFoundAttributes(QString serviceUrl,
+ QString attributes) {
+ emit foundAttributes(serviceUrl, attributes);
+}
+void KServiceLocator::emitFoundScopes(QStringList scopeList) {
+ emit foundScopes(scopeList);
+}
+void KServiceLocator::emitLastServiceTypeSignal(bool success) {
+ emit lastServiceTypeSignal(success);
+}
+void KServiceLocator::emitLastServiceSignal(bool success) {
+ emit lastServiceSignal(success);
+}
+void KServiceLocator::emitLastAttributesSignal(bool success) {
+ emit lastAttributesSignal(success);
+}
+
+
+
+/*** Static helpers ***/
+
+void KServiceLocator::parseAttributeList(const QString &attributes,
+ QMap<QString,QString> &attributeMap) {
+ QRegExp r("\\((.*)=(.*)\\)");
+ r.setMinimal(true);
+ int pos = 0;
+ while (pos >= 0) {
+ pos = r.search(attributes, pos);
+ if (pos != -1) {
+ attributeMap[r.cap(1)] = r.cap(2);
+ pos += r.matchedLength();
+ }
+ }
+}
+
+QStringList KServiceLocator::parseCommaList(const QString &list) {
+ return QStringList::split(QChar(','), list);
+}
+
+QString KServiceLocator::createCommaList(const QStringList &values) {
+ return values.join(",");
+}
+
+QString KServiceLocator::escapeFilter(const QString &str) {
+ QString f;
+ int s = str.length();
+ for (int i = 0; i < s; i++) {
+ char c = str[i];
+ switch(c) {
+ case '*':
+ f.append("\2a");
+ break;
+ case '(':
+ f.append("\28");
+ break;
+ case ')':
+ f.append("\29");
+ break;
+ case '\\':
+ f.append("\5c");
+ break;
+ case 0:
+ f.append("\2a");
+ break;
+ default:
+ f.append(c);
+ break;
+ }
+ }
+ return f;
+}
+
+#include "kservicelocator.moc"
+
diff --git a/krdc/kservicelocator.h b/krdc/kservicelocator.h
new file mode 100644
index 00000000..1177000f
--- /dev/null
+++ b/krdc/kservicelocator.h
@@ -0,0 +1,309 @@
+/*
+ * Interface to find SLP services.
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KSERVICELOCATOR_H
+#define __KSERVICELOCATOR_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstringlist.h>
+
+class KServiceLocatorPrivate;
+
+/**
+ * KServiceLocator allows you to search the network for service types,
+ * services and their attributes using SLP.
+ * Note that most find methods can work in a synchronous and a asynchronous
+ * mode. In synchronous mode the find method emits a number of signals and
+ * returns. In asynchronous mode the method returns as soon as possible and
+ * will emit the signals either during or after the find method. You can have
+ * only one find search at a given time.
+ *
+ * @version $Id$
+ * @author Tim Jansen, tim@tjansen.de
+ */
+class KServiceLocator : public QObject {
+ Q_OBJECT
+ private:
+ friend class KServiceLocatorPrivate;
+ KServiceLocatorPrivate *d;
+
+ public:
+ /**
+ * Creates a new KServiceLocator.
+ * @param lang the language to search in, or QString::null for the
+ * default language
+ * @param async true to create the service locator in asynchronous
+ * mode, false otherwise
+ */
+ KServiceLocator(const QString &lang = QString::null,
+ bool async = true);
+
+ virtual ~KServiceLocator();
+
+
+ /**
+ * Parses a list of attributes that has been encoded in a string, as
+ * returned by foundServiceAttributes() and foundAttributes().
+ * The format of the encoded attributes is "(name=value), (name=value)".
+ * Note that some attributes contain lists that must be parsed using
+ * parseCommaList(), and that all values must be decoded using
+ * decodeAttributeValue().
+ * @param attributes a list of encoded attributes
+ * @param attributeMap the attributes will be added to this map
+ */
+ static void parseAttributeList(const QString &attributes,
+ QMap<QString,QString> &attributeMap);
+
+ /**
+ * Decodes the value of an attribute (removes the escape codes). This
+ * is neccessary even when you parsed an attribute list.
+ * This function requires the presence of the SLP library, otherwise it
+ * will return the original value.
+ * @param value the attribute value to decode
+ * @return the decoded value. If @p value was QString::null or decoding
+ * failed, QString::null will be returned
+ */
+ static QString decodeAttributeValue(const QString &value);
+
+ /**
+ * Parses a comma-separated string of lists, as returned by many signals.
+ * @param list the comma-separated list
+ * @return the items as a QStringList
+ */
+ static QStringList parseCommaList(const QString &list);
+
+ /**
+ * Creates a comma-separated string of lists, as required by many functions.
+ * @param map the items of this list will be converted
+ * @return the comma-separated list
+ */
+ static QString createCommaList(const QStringList &values);
+
+ /**
+ * Escapes a string for use as part of a filter, as described in
+ * RFC 2254. This will replace all occurrences of special
+ * characters like paranthesis, backslash and "*", so you can use
+ * the converted string as part of the query. Never escape the whole
+ * query because then even the neccessary paranthesis characters
+ * will be escaped.
+ * @param str the string to escape
+ * @return the escaped string
+ */
+ static QString escapeFilter(const QString &str);
+
+ /**
+ * Returns true if service location is generally possible.
+ * It will fail if SLP libraries are not installed.
+ * @return true if service location seems to be possible
+ */
+ bool available();
+
+ /**
+ * Use this constant for findServiceTypes()'s namingAuthority argument
+ * to get only services from the default (IANA) naming authority.
+ */
+ static const QString DEFAULT_AUTHORITY;
+
+ /**
+ * Use this constant for findServiceTypes()'s namingAuthority argument
+ * to get all services,
+ */
+ static const QString ALL_AUTHORITIES;
+
+ /**
+ * Finds all service types in the given scope with the given naming
+ * authority. This function emits the signal foundServiceTypes()
+ * each time it discovered one or more service types. When the last
+ * service type has been found lastServiceTypeSignal() will be emitted.
+ * When KServiceLocator is in synchronous mode the function will not be
+ * returned before lastServiceTypeSignal() has been emitted, in
+ * asynchronous mode lastServiceTypeSignal() can be emitted later. If
+ * you call this function while another asynchronous operation is
+ * running it will fail.
+ *
+ * @param namingAuthority the naming authorities of the service
+ * types to be found. If DEFAULT_AUTHORITY
+ * only IANA service types will be returned,
+ * if it is ALL_AUTHORITIES or the
+ * argument has been omitted all service types
+ * will be returned.
+ * @param scopelist a comma-separated list of all scopes that will
+ * be searched, or QString:null to search in all
+ * scopes
+ * @return true if the operation was successful
+ */
+ bool findServiceTypes(const QString &namingAuthority = QString::null,
+ const QString &scopelist = QString::null);
+
+ /**
+ * Finds all services in the given scope with the given service type.
+ * Examples for service types are "service:ftp" to find all ftp servers
+ * or "service:remotedesktop:" to find all remote desktop services.
+ * You can also specify a filter to match services depending their
+ * attributes. The filter uses the LDAP Search Filter syntax as
+ * described in RFC 2254, "String Representation of LDAP Search
+ * Filters".
+ * The function emits the signal foundService() each time it
+ * discovered a service types. When the last service has been found
+ * lastServiceSignal() will be emitted. When KServiceLocator is in
+ * synchronous mode the function will not be returned before
+ * lastServiceSignal() has been emitted, in asynchronous mode
+ * lastServiceSignal() can be emitted later. If you call this function
+ * while another asynchronous operation is running it will fail.
+ *
+ * @param srvtype the type of the service to search.
+ * @param filter a filter in LDAP Search Filter syntax, as described
+ * in RFC 2254.
+ * @param scopelist a comma-separated list of all scopes that will
+ * be searched, or QString:null to search in all
+ * scopes
+ * @return true if the operation was successful
+ */
+ bool findServices(const QString &srvtype,
+ const QString &filter = QString::null,
+ const QString &scopelist = QString::null);
+
+ /**
+ * Finds the attributes of the service with the given URL.
+ * The function emits the signal foundAttributes() if the service
+ * has been found, followed by lastAttributesSignal(). When
+ * KServiceLocator is in synchronous mode the function will not be
+ * returned before lastAttributesSignal() has been emitted, in
+ * asynchronous mode lastAttributesSignal() can be emitted later. If
+ * you call this function while another asynchronous operation is
+ * running it will fail.
+ *
+ * @param serviceURL the URL of the service to search
+ * @param attributeIds a comma-separated list of attributes to
+ * retrieve, or QString::null to retrieve all
+ * attributes
+ * @return true if the operation was successful
+ */
+ bool findAttributes(const QString &serviceUrl,
+ const QString &attributeIds = QString::null);
+
+ /**
+ * Finds all scopes that can be searched. Always finds at least
+ * one scope (the default scope).
+ * The function emits the signal foundScopes() if the service
+ * has been found. When KServiceLocator is in synchronous mode
+ * the function will not be returned before foundScopes() has been
+ * emitted, in asynchronous mode it can be emitted later. If
+ * you call this function while another asynchronous operation is
+ * running it will fail.
+ *
+ * @return true if the operation was successful
+ */
+ bool findScopes();
+
+ /**
+ * If there is a asynchronous operation running, it will aborted.
+ * It is not guaranteed that you dont receive any signals after
+ * calling abortOperation. You will always get a lastSignal call
+ * even after aborting.
+ */
+ void abortOperation();
+
+
+ signals:
+ /**
+ * Called by findServiceTypes() each time one or more service
+ * types have been discovered.
+ * @param serviceTypes a comma-separated list of service types
+ */
+ void foundServiceTypes(QString serviceTypes);
+
+ /**
+ * Called by findServices() each time a service has been
+ * found.
+ * @param serviceUrl the service url
+ * @param lifetime the lifetime of the service in seconds
+ */
+ void foundService(QString serviceUrl,
+ int lifetime);
+
+ /**
+ * Called by findAttributes() when the service's attributes
+ * have been found.
+ * @param serviceUrl the service url
+ * @param attributes an attribute map (see parseAttributeList() and
+ * decodeAttributeValue())
+ */
+ void foundAttributes(QString serviceUrl,
+ QString attributes);
+
+ /**
+ * Called by findScopes() when the scopes have been discovered.
+ * @param scopeList a list of valid scopes, empty if an error
+ * occurred
+ */
+ void foundScopes(QStringList scopeList);
+
+
+ /**
+ * Emitted when a service type discovery operation finished. If
+ * you are in async mode it is guaranteed that the operation is
+ * finished when you receive the signal, so you can start a new
+ * one. In synced mode the operation is still running, so you
+ * must wait until the find function returns.
+ * @param success true if all items have been found, false when
+ * an error occurred during the operation
+ */
+ void lastServiceTypeSignal(bool success);
+
+ /**
+ * Emitted when a service discovery operation finished. If you are
+ * in async mode it is guaranteed that the operation is finished
+ * when you receive the signal, so you can start a new one. In
+ * synced mode the operation is still running, so you must wait
+ * until the find function returns.
+ * @param success true if all items have been found, false when
+ * an error occurred during the operation
+ */
+ void lastServiceSignal(bool success);
+
+ /**
+ * Emitted when a attributes reqyest operation finished. If you are
+ * in async mode it is guaranteed that the operation is finished
+ * when you receive the signal, so you can start a new one. In synced
+ * mode the operation is still running, so you must wait until the
+ * find function returns.
+ * @param success true if all items have been found, false when
+ * an error occurred during the operation
+ */
+ void lastAttributesSignal(bool success);
+
+ protected:
+ virtual void customEvent(QCustomEvent *);
+
+ private:
+ void emitFoundServiceTypes(QString serviceTypes);
+ void emitFoundService(QString serviceUrl,
+ int lifetime);
+ void emitFoundAttributes(QString serviceUrl,
+ QString attributes);
+ void emitFoundScopes(QStringList scopeList);
+ void emitLastServiceTypeSignal(bool success);
+ void emitLastServiceSignal(bool success);
+ void emitLastAttributesSignal(bool success);
+};
+
+#endif
diff --git a/krdc/main.cpp b/krdc/main.cpp
new file mode 100644
index 00000000..d0cd3e4c
--- /dev/null
+++ b/krdc/main.cpp
@@ -0,0 +1,224 @@
+/***************************************************************************
+ main.cpp - main control
+ -------------------
+ begin : Thu Dec 20 15:11:42 CET 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kwallet.h>
+#include <qwindowdefs.h>
+#include <qtimer.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+
+#include "../config.h"
+#include "main.h"
+
+// NOTE: I'm not comfortable with the wallet being global data and this high up
+// in the heirarchy, but there are 3 reasons for its current placement here:
+// 1) There are some important threading issues where it comes to the password
+// handling code, and a lot of it is done outside of the objects.
+// 2) Different backends need access to the same wallet. so that it is not
+// opened multiple times.
+// 3) MainController is about the only thing that isn't deleted in between connection
+// attempts.
+KWallet::Wallet *wallet = 0;
+
+static const char description[] = I18N_NOOP("Remote desktop connection");
+
+static KCmdLineOptions options[] =
+{
+ { "f", 0, 0 },
+ { "fullscreen", I18N_NOOP("Start in fullscreen mode"), 0 },
+ { "w", 0, 0 },
+ { "window", I18N_NOOP("Start in regular window"), 0 },
+ { "l", 0, 0 },
+ { "low-quality", I18N_NOOP("Low quality mode (Tight Encoding, 8 bit color)"), 0 },
+ { "m", 0, 0 },
+ { "medium-quality", I18N_NOOP("Medium quality mode (Tight Encoding, lossy)"), 0 },
+ { "h", 0, 0 },
+ { "high-quality", I18N_NOOP("High quality mode, default (Hextile Encoding)"), 0 },
+ { "s", 0, 0 },
+ { "scale", I18N_NOOP("Start VNC in scaled mode"), 0 },
+ { "c", 0, 0 },
+ { "local-cursor", I18N_NOOP("Show local cursor (VNC only)"), 0 },
+ { "e", 0, 0 },
+ { "encodings ", I18N_NOOP("Override VNC encoding list (e.g. 'hextile raw')"), 0 },
+ { "p", 0, 0 },
+ { "password-file ", I18N_NOOP("Provide the password in a file"), 0 },
+ { "+[host]", I18N_NOOP("The name of the host, e.g. 'localhost:1'"), 0 },
+ KCmdLineLastOption
+};
+
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutData( "krdc", I18N_NOOP("Remote Desktop Connection"),
+ VERSION, description, KAboutData::License_GPL,
+ "(c) 2001-2003, Tim Jansen"
+ "(c) 2002-2003, Arend van Beelen jr."
+ "(c) 2000-2002, Const Kaplinsky\n"
+ "(c) 2000, Tridia Corporation\n"
+ "(c) 1999, AT&T Laboratories Cambridge\n"
+ "(c) 1999-2003, Matthew Chapman\n", 0, 0,
+ "tim@tjansen.de");
+ aboutData.addAuthor("Tim Jansen",0, "tim@tjansen.de");
+ aboutData.addAuthor("Arend van Beelen jr.",
+ I18N_NOOP("RDP backend"), "arend@auton.nl");
+ aboutData.addCredit("AT&T Laboratories Cambridge",
+ I18N_NOOP("Original VNC viewer and protocol design"));
+ aboutData.addCredit("Const Kaplinsky",
+ I18N_NOOP("TightVNC encoding"));
+ aboutData.addCredit("Tridia Corporation",
+ I18N_NOOP("ZLib encoding"));
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KApplication a;
+
+ QString host = QString::null;
+ Quality quality = QUALITY_UNKNOWN;
+ QString encodings = QString::null;
+ QString password = QString::null;
+ QString resolution = QString::null;
+ QString keymap = QString::null;
+ WindowMode wm = WINDOW_MODE_AUTO;
+ bool scale = false;
+ bool localCursor = kapp->config()->readBoolEntry("alwaysShowLocalCursor", false);
+ QSize initialWindowSize;
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if (args->isSet("low-quality"))
+ quality = QUALITY_LOW;
+ else if (args->isSet("medium-quality"))
+ quality = QUALITY_MEDIUM;
+ else if (args->isSet("high-quality"))
+ quality = QUALITY_HIGH;
+
+ if (args->isSet("fullscreen"))
+ wm = WINDOW_MODE_FULLSCREEN;
+ else if (args->isSet("window"))
+ wm = WINDOW_MODE_NORMAL;
+
+ if (args->isSet("scale"))
+ scale = true;
+
+ if (args->isSet("local-cursor"))
+ localCursor = true;
+
+ if (args->isSet("encodings"))
+ encodings = args->getOption("encodings");
+
+ if (args->isSet("password-file")) {
+ QString passwordFile = args->getOption("password-file");
+ QFile f(passwordFile);
+ if (!f.open(IO_ReadOnly)) {
+ KMessageBox::error(0, i18n("The password file '%1' does not exist.").arg(passwordFile));
+ return 1;
+ }
+ password = QTextStream(&f).readLine();
+ f.close();
+ }
+
+ if (args->count() > 0)
+ host = args->arg(0);
+
+ QString is = a.geometryArgument();
+ if (!is.isNull()) {
+ QRegExp re("([0-9]+)[xX]([0-9]+)");
+ if (!re.exactMatch(is))
+ args->usage(i18n("Wrong geometry format, must be widthXheight"));
+ initialWindowSize = QSize(re.cap(1).toInt(), re.cap(2).toInt());
+ }
+
+ MainController mc(&a, wm, host, quality, encodings, password,
+ scale, localCursor, initialWindowSize);
+ return mc.main();
+}
+
+MainController::MainController(KApplication *app, WindowMode wm,
+ const QString &host,
+ Quality quality,
+ const QString &encodings,
+ const QString &password,
+ bool scale,
+ bool localCursor,
+ QSize initialWindowSize) :
+ m_windowMode(wm),
+ m_host(host),
+ m_encodings(encodings),
+ m_password(password),
+ m_scale(scale),
+ m_localCursor(localCursor),
+ m_initialWindowSize(initialWindowSize),
+ m_quality(quality),
+ m_app(app) {
+}
+
+MainController::~MainController() {
+ if ( wallet ) {
+ delete wallet; wallet = 0;
+ }
+}
+
+int MainController::main() {
+
+ if (start())
+ return m_app->exec();
+ else
+ return 0;
+}
+
+void MainController::errorRestartRequested() {
+ QTimer::singleShot(0, this, SLOT(errorRestart()));
+}
+
+bool MainController::start() {
+ m_krdc = new KRDC(m_windowMode, m_host,
+ m_quality, m_encodings, m_password,
+ m_scale, m_localCursor, m_initialWindowSize);
+ m_app->setMainWidget(m_krdc);
+
+ QObject::connect(m_krdc, SIGNAL(disconnected()),
+ m_app, SLOT(quit()));
+ connect(m_krdc, SIGNAL(disconnectedError()),
+ SLOT(errorRestartRequested()));
+
+ return m_krdc->start();
+}
+
+void MainController::errorRestart() {
+ if (!m_host.isEmpty())
+ KRDC::setLastHost(m_host);
+ m_host = QString::null; // only auto-connect once
+
+ // unset KRDC as main widget, to avoid quit on delete
+ m_app->setMainWidget(0);
+
+ m_krdc = 0;
+
+ if (!start())
+ m_app->quit();
+}
+
+#include "main.moc"
diff --git a/krdc/main.h b/krdc/main.h
new file mode 100644
index 00000000..d95f079e
--- /dev/null
+++ b/krdc/main.h
@@ -0,0 +1,60 @@
+/***************************************************************************
+ main.h - controller object
+ -------------------
+ begin : Sat Jun 15 02:12:00 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 MAIN_H
+#define MAIN_H
+
+#include <qobject.h>
+#include "krdc.h"
+#include "kremoteview.h"
+#include "smartptr.h"
+
+class KApplication;
+
+class MainController : public QObject {
+ Q_OBJECT
+private:
+ SmartPtr<KRDC> m_krdc;
+ WindowMode m_windowMode;
+ QString m_host, m_encodings, m_password, m_resolution;
+ bool m_scale;
+ bool m_localCursor;
+ QSize m_initialWindowSize;
+ QString m_keymap;
+ Quality m_quality;
+
+ KApplication *m_app;
+
+public:
+ MainController(KApplication *app, WindowMode wm,
+ const QString &host,
+ Quality quality,
+ const QString &encodings,
+ const QString &password,
+ bool scale,
+ bool localCursor,
+ QSize initialWindowSize);
+ ~MainController();
+ int main();
+ bool start();
+
+private slots:
+ void errorRestartRequested();
+ void errorRestart();
+};
+
+#endif
diff --git a/krdc/maindialog.cpp b/krdc/maindialog.cpp
new file mode 100644
index 00000000..61895c65
--- /dev/null
+++ b/krdc/maindialog.cpp
@@ -0,0 +1,78 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003-2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "maindialog.h"
+#include "maindialogwidget.h"
+#include "preferencesdialog.h"
+
+#include <kapplication.h>
+#include <kguiitem.h>
+#include <klocale.h>
+
+MainDialog::MainDialog( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "Remote Desktop Connection" ),
+ Ok|Close|Help|User1, Ok, true, KGuiItem( i18n( "&Preferences" ),
+ "configure" ) )
+{
+ m_dialogWidget = new MainDialogWidget( this, "m_dialogWidget" );
+ setMainWidget( m_dialogWidget );
+
+ setButtonText( Ok, i18n( "Connect" ) );
+ enableButtonOK( false );
+
+ connect( m_dialogWidget, SIGNAL( hostValid( bool ) ),
+ SLOT( enableButtonOK( bool ) ) );
+}
+
+void MainDialog::setRemoteHost( const QString &host )
+{
+ m_dialogWidget->setRemoteHost( host );
+}
+
+QString MainDialog::remoteHost()
+{
+ return m_dialogWidget->remoteHost();
+}
+
+void MainDialog::slotHelp()
+{
+ kapp->invokeHelp();
+}
+
+void MainDialog::slotUser1()
+{
+ PreferencesDialog p( this );
+ p.exec();
+}
+
+void MainDialog::slotOk()
+{
+ m_dialogWidget->save();
+
+ KDialogBase::accept();
+}
+
+void MainDialog::slotClose()
+{
+ m_dialogWidget->save();
+
+ KDialogBase::reject();
+}
+
+#include "maindialog.moc"
diff --git a/krdc/maindialog.h b/krdc/maindialog.h
new file mode 100644
index 00000000..2106e091
--- /dev/null
+++ b/krdc/maindialog.h
@@ -0,0 +1,48 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003-2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef MAINDIALOG_H
+#define MAINDIALOG_H
+
+#include <kdialogbase.h>
+
+class MainDialogWidget;
+
+class MainDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ MainDialog( QWidget *parent, const char *name=0 );
+ ~MainDialog() {}
+
+ void setRemoteHost( const QString & );
+ QString remoteHost();
+
+ protected slots:
+ virtual void slotOk();
+ virtual void slotClose();
+ virtual void slotUser1();
+ virtual void slotHelp();
+
+ protected:
+ MainDialogWidget *m_dialogWidget;
+};
+
+#endif // MAINDIALOG_H
+
diff --git a/krdc/maindialogbase.ui b/krdc/maindialogbase.ui
new file mode 100644
index 00000000..e2aab1fb
--- /dev/null
+++ b/krdc/maindialogbase.ui
@@ -0,0 +1,351 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>MainDialogBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>MainDialogBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>544</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>m_serverLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>MShape</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>MShadow</enum>
+ </property>
+ <property name="text">
+ <string>Remote &amp;desktop:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_serverInput</cstring>
+ </property>
+ </widget>
+ <widget class="KHistoryCombo" row="1" column="1">
+ <property name="name">
+ <cstring>m_serverInput</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>250</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maxCount">
+ <number>12</number>
+ </property>
+ <property name="duplicatesEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter the hostname and display number</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter the name and display number of the computer that you want to connect to, separated by a colon, e.g. 'mycomputer:1'. The address can be any valid Internet address. The display numbers usually start at 0. If you do not have a display number, try 0 or 1.
+Remote Desktop Connection only supports systems that use VNC.</string>
+ </property>
+ </widget>
+ <widget class="KPushButton" row="1" column="2">
+ <property name="name">
+ <cstring>m_browseButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Browse &lt;&lt;</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Turn on/off the network browsing panel.</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kActiveLabel2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Enter the address of the computer to connect to, or browse the network and select one. VNC and RDP compatible servers will be supported. &lt;a href="whatsthis:&lt;h3&gt;Examples&lt;/h3&gt;for a computer called 'megan':&lt;p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;megan:1&lt;/td&gt;&lt;td&gt;connect to the VNC server on 'megan' with display number 1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;vnc:/megan:1&lt;/td&gt;&lt;td&gt;longer form for the same thing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;rdp:/megan&lt;/td&gt;&lt;td&gt;connect to the RDP server on 'megan'&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;"&gt;Examples&lt;/a&gt;</string>
+ </property>
+ </widget>
+ <widget class="QFrame" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>m_browsingPanel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>m_rescanButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Rescan</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Rescans the network. Depending on the network configuration this may take a few seconds until all systems have responded.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="1">
+ <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>34</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Search:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_searchInput</cstring>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="3">
+ <property name="name">
+ <cstring>m_searchInput</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter a search term</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enter a search term here if you want to search for a specific system, then press Enter or click Rescan. All systems, whose description matches the search term, will be displayed. The search is not case sensitive. If you leave the field empty all systems will be displayed.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="4">
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>34</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="5">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Scop&amp;e:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>m_scopeCombo</cstring>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="0" column="6">
+ <property name="name">
+ <cstring>m_scopeCombo</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>An administrator can configure the network to have several scopes. If this is the case, you can select the scope to scan here.</string>
+ </property>
+ </widget>
+ <widget class="KListView" row="1" column="0" rowspan="1" colspan="7">
+ <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>Type</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Address</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Protocol</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>m_browsingView</cstring>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="showToolTips">
+ <bool>false</bool>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can see the systems on the network that allow you to connect. Note that an administrator can hide systems, so the list is not always complete. Click on an item to select it, double-click it to connect immediately.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>m_browseButton</sender>
+ <signal>clicked()</signal>
+ <receiver>MainDialogBase</receiver>
+ <slot>toggleBrowsingArea()</slot>
+ </connection>
+ <connection>
+ <sender>m_rescanButton</sender>
+ <signal>clicked()</signal>
+ <receiver>MainDialogBase</receiver>
+ <slot>rescan()</slot>
+ </connection>
+ <connection>
+ <sender>m_serverInput</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>MainDialogBase</receiver>
+ <slot>hostChanged(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>m_searchInput</sender>
+ <signal>returnPressed()</signal>
+ <receiver>MainDialogBase</receiver>
+ <slot>rescan()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="pure virtual">hostChanged( const QString &amp; text )</slot>
+ <slot access="protected" specifier="pure virtual">toggleBrowsingArea()</slot>
+ <slot access="protected" specifier="pure virtual">itemSelected( QListViewItem * item )</slot>
+ <slot access="protected" specifier="pure virtual">itemDoubleClicked( QListViewItem * item )</slot>
+ <slot access="protected" specifier="pure virtual">scopeSelected( const QString &amp; scope )</slot>
+ <slot access="protected" specifier="pure virtual">rescan()</slot>
+</slots>
+<functions>
+ <function access="protected" specifier="non virtual">enableBrowsingArea( bool enable )</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>kcombobox.h</includehint>
+</includehints>
+</UI>
diff --git a/krdc/maindialogwidget.cpp b/krdc/maindialogwidget.cpp
new file mode 100644
index 00000000..8bc86c39
--- /dev/null
+++ b/krdc/maindialogwidget.cpp
@@ -0,0 +1,359 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Tim Jansen <tim@tjansen.de>
+ Copyright (C) 2003-2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <klineedit.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+
+#include "hostpreferences.h"
+#include "maindialogwidget.h"
+
+static const QString DEFAULT_SCOPE = "default";
+
+class UrlListViewItem : public KListViewItem
+{
+ public:
+ UrlListViewItem( QListView *v, const QString &url, const QString &host,
+ const QString &protocol, const QString &type, const QString &userid,
+ const QString &fullname, const QString &desc,
+ const QString &serviceid )
+ : KListViewItem( v, host, i18n( "unknown" ), host, protocol ),
+ m_url( url ), m_serviceid( serviceid )
+ {
+ if ( !type.isNull() )
+ {
+ //User connects to somebody else's desktop, used for krfb
+ if ( type.lower() == "shared" )
+ setText( 1, i18n( "Shared Desktop" ) );
+ //User connects to desktop that exists only on the network
+ else if ( type.lower() == "private" )
+ setText( 1, i18n( "Standalone Desktop" ) );
+ }
+ if ( !desc.isNull() )
+ setText( 0, desc );
+ if ( ( !userid.isEmpty() ) && ( !fullname.isEmpty() ) )
+ setText( 0, QString( "%1 (%2)" ).arg( fullname ).arg( userid ) );
+ else if ( !userid.isNull() )
+ setText( 0, userid );
+ else if ( !fullname.isNull() )
+ setText( 0, fullname );
+ }
+
+ QString url()
+ {
+ return m_url;
+ }
+ const QString& serviceid() const
+ {
+ return m_serviceid;
+ }
+
+ protected:
+ QString m_url;
+ QString m_serviceid;
+};
+
+MainDialogWidget::MainDialogWidget( QWidget *parent, const char *name )
+ : MainDialogBase( parent, name ),
+ m_scanning( false )
+{
+ HostPreferences *hp = HostPreferences::instance();
+ QStringList list;
+
+ list = hp->serverCompletions();
+ m_serverInput->completionObject()->setItems( list );
+ list = hp->serverHistory();
+ m_serverInput->setHistoryItems( list );
+
+ m_searchInput->setTrapReturnKey( true );
+
+ connect( m_browsingView,
+ SIGNAL( selectionChanged( QListViewItem * ) ),
+ SLOT( itemSelected( QListViewItem * ) ) );
+ connect( m_browsingView,
+ SIGNAL( doubleClicked( QListViewItem *, const QPoint &, int ) ),
+ SLOT( itemDoubleClicked( QListViewItem * ) ) );
+ connect( m_scopeCombo,
+ SIGNAL( activated( const QString & ) ),
+ SLOT( scopeSelected( const QString & ) ) );
+ connect( m_serverInput,
+ SIGNAL( returnPressed( const QString & ) ),
+ SLOT( rescan() ) );
+
+ bool showBrowse = hp->showBrowsingPanel();
+ enableBrowsingArea( showBrowse );
+
+ m_locator_dnssd = new DNSSD::ServiceBrowser(QStringList::split(',',"_rfb._tcp,_rdp._tcp"),0,DNSSD::ServiceBrowser::AutoResolve);
+ connect(m_locator_dnssd,SIGNAL(serviceAdded(DNSSD::RemoteService::Ptr)),
+ SLOT(addedService(DNSSD::RemoteService::Ptr)));
+ connect(m_locator_dnssd,SIGNAL(serviceRemoved(DNSSD::RemoteService::Ptr)),
+ SLOT(removedService(DNSSD::RemoteService::Ptr)));
+ m_locator_dnssd->startBrowse();
+
+ adjustSize();
+}
+
+MainDialogWidget::~MainDialogWidget()
+{
+ delete m_locator_dnssd;
+}
+
+void MainDialogWidget::save()
+{
+ HostPreferences *hp = HostPreferences::instance();
+ QStringList list;
+
+ m_serverInput->addToHistory( m_serverInput->currentText() );
+ list = m_serverInput->completionObject()->items();
+ hp->setServerCompletions( list );
+ list = m_serverInput->historyItems();
+ hp->setServerHistory( list );
+
+ hp->setShowBrowsingPanel( m_browsingPanel->isVisible() );
+}
+
+void MainDialogWidget::setRemoteHost( const QString &host )
+{
+ m_serverInput->setEditText( host );
+}
+
+QString MainDialogWidget::remoteHost()
+{
+ return m_serverInput->currentText();
+}
+
+void MainDialogWidget::hostChanged( const QString &text )
+{
+ emit hostValid(text.contains(QRegExp(":[0-9]+$")) ||
+ text.contains(QRegExp("^vnc:/.+")) ||
+ text.contains(QRegExp("^rdp:/.+")));
+}
+
+void MainDialogWidget::toggleBrowsingArea()
+{
+ enableBrowsingArea(!m_browsingPanel->isVisible());
+}
+
+void MainDialogWidget::enableBrowsingArea( bool enable )
+{
+ int hOffset = 0;
+ if (enable)
+ {
+ m_browsingPanel->show();
+ m_browsingPanel->setMaximumSize(1000, 1000);
+ m_browsingPanel->setEnabled(true);
+ m_browseButton->setText(m_browseButton->text().replace(">>", "<<"));
+ }
+ else
+ {
+ hOffset = m_browsingPanel->height();
+ m_browsingPanel->hide();
+ m_browsingPanel->setMaximumSize(0, 0);
+ m_browsingPanel->setEnabled(false);
+ m_browseButton->setText(m_browseButton->text().replace("<<", ">>"));
+ int h = minimumSize().height()-hOffset;
+ setMinimumSize(minimumSize().width(), (h > 0) ? h : 0);
+ resize(width(), height()-hOffset);
+
+ QTimer::singleShot( 0, parentWidget(), SLOT( adjustSize() ) );
+ }
+
+ if (enable)
+ rescan();
+}
+
+void MainDialogWidget::itemSelected( QListViewItem *item )
+{
+ UrlListViewItem *u = ( UrlListViewItem* ) item;
+ QRegExp rx( "^service:remotedesktop\\.kde:([^;]*)" );
+ if ( rx.search( u->url() ) < 0 )
+ m_serverInput->setCurrentText( u->url());
+ else m_serverInput->setCurrentText( rx.cap( 1 ) );
+}
+
+void MainDialogWidget::itemDoubleClicked( QListViewItem *item )
+{
+ itemSelected( item );
+ emit accept();
+}
+
+void MainDialogWidget::scopeSelected( const QString &scope )
+{
+ QString s = scope;
+ if ( s == i18n( "default" ) )
+ s = DEFAULT_SCOPE;
+
+ if ( m_scope == s )
+ return;
+ m_scope = s;
+ rescan();
+}
+
+void MainDialogWidget::rescan()
+{
+ QStringList scopeList;
+
+ if ( m_scanning )
+ return;
+ m_scanning = true;
+ m_rescanButton->setEnabled( false );
+ m_scopeCombo->setEnabled( false );
+ if ( !ensureLocatorOpen() )
+ return;
+
+ m_browsingView->clear();
+
+ QString filter;
+ if ( !m_searchInput->text().stripWhiteSpace().isEmpty() ) {
+ QString ef = KServiceLocator::escapeFilter(
+ m_searchInput->text().stripWhiteSpace() );
+ filter = "(|(|(description=*"+ef+"*)(username=*"+ef+"*))(fullname=*"+ef+"*))";
+ }
+
+ if ( !m_locator->findServices( "service:remotedesktop.kde",
+ filter, m_scope ) ) {
+ kdWarning() << "Failure in findServices()" << endl;
+ errorScanning();
+ return;
+ }
+}
+
+bool MainDialogWidget::ensureLocatorOpen()
+{
+ if ( m_locator )
+ return true;
+
+ m_locator = new KServiceLocator();
+
+ if ( !m_locator->available() ) {
+#ifdef HAVE_SLP
+ KMessageBox::error( 0,
+ i18n( "Browsing the network is not possible. You probably "
+ "did not install SLP support correctly." ),
+ i18n( "Browsing Not Possible" ), false );
+#endif
+ return false;
+ }
+
+ connect( m_locator, SIGNAL( foundService( QString,int ) ),
+ SLOT( foundService( QString,int ) ) );
+ connect( m_locator, SIGNAL( lastServiceSignal( bool ) ),
+ SLOT( lastSignalServices( bool ) ) );
+ connect( m_locator, SIGNAL( foundScopes( QStringList ) ),
+ SLOT( foundScopes( QStringList ) ) );
+ return true;
+}
+
+void MainDialogWidget::errorScanning()
+{
+ KMessageBox::error( 0,
+ i18n( "An error occurred while scanning the network." ),
+ i18n( "Error While Scanning" ), false );
+ finishScanning();
+}
+
+void MainDialogWidget::finishScanning()
+{
+ m_rescanButton->setEnabled( true );
+ m_scopeCombo->setEnabled( true );
+ m_scanning = false;
+}
+
+void MainDialogWidget::foundService( QString url, int )
+{
+ QRegExp rx( "^service:remotedesktop\\.kde:(\\w+)://([^;]+);(.*)$" );
+
+ if ( rx.search( url ) < 0 )
+ {
+ rx = QRegExp( "^service:remotedesktop\\.kde:(\\w+)://(.*)$" );
+ if ( rx.search( url ) < 0 )
+ return;
+ }
+
+ QMap<QString,QString> map;
+ KServiceLocator::parseAttributeList( rx.cap( 3 ), map );
+
+ new UrlListViewItem( m_browsingView, url, rx.cap( 2 ), rx.cap( 1 ),
+ KServiceLocator::decodeAttributeValue( map[ "type" ] ),
+ KServiceLocator::decodeAttributeValue( map[ "username" ] ),
+ KServiceLocator::decodeAttributeValue( map[ "fullname" ] ),
+ KServiceLocator::decodeAttributeValue( map[ "description" ] ),
+ KServiceLocator::decodeAttributeValue( map[ "serviceid" ] ) );
+}
+
+void MainDialogWidget::addedService( DNSSD::RemoteService::Ptr service )
+{
+QString type = service->type().mid(1,3);
+if (type == "rfb") type = "vnc";
+QString url = type+"://"+service->hostName()+":"+QString::number(service->port());
+new UrlListViewItem( m_browsingView, url, service->serviceName(),
+ type.upper(),service->textData()["type"],
+ service->textData()["u"],service->textData()["fullname"],
+ service->textData()["description"],service->serviceName()+service->domain());
+}
+
+void MainDialogWidget::removedService( DNSSD::RemoteService::Ptr service )
+{
+ QListViewItemIterator it( m_browsingView );
+ while ( it.current() ) {
+ if ( ((UrlListViewItem*)it.current())->serviceid() == service->serviceName()+service->domain() )
+ delete it.current();
+ else ++it;
+ }
+}
+
+
+void MainDialogWidget::lastSignalServices( bool success )
+{
+ if ( !success )
+ {
+ errorScanning();
+ return;
+ }
+
+ if ( !m_locator->findScopes() )
+ {
+ kdWarning() << "Failure in findScopes()" << endl;
+ errorScanning();
+ }
+}
+
+void MainDialogWidget::foundScopes( QStringList scopeList )
+{
+ int di = scopeList.findIndex( DEFAULT_SCOPE );
+ if ( di >= 0 )
+ scopeList[ di ] = i18n( "default" );
+
+ int ct = scopeList.findIndex( m_scopeCombo->currentText() );
+ m_scopeCombo->clear();
+ m_scopeCombo->insertStringList( scopeList );
+ if ( ct >= 0 )
+ m_scopeCombo->setCurrentItem( ct );
+ finishScanning();
+}
+
+#include "maindialogwidget.moc"
diff --git a/krdc/maindialogwidget.h b/krdc/maindialogwidget.h
new file mode 100644
index 00000000..ec8acb01
--- /dev/null
+++ b/krdc/maindialogwidget.h
@@ -0,0 +1,74 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Tim Jansen <tim@tjansen.de>
+ Copyright (C) 2003-2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or ( at your option ) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef MAINDIALOGWIDGET_H
+#define MAINDIALOGWIDGET_H
+
+#include "kservicelocator.h"
+#include "maindialogbase.h"
+#include <dnssd/servicebrowser.h>
+#include <dnssd/remoteservice.h>
+#include "smartptr.h"
+
+class MainDialogWidget : public MainDialogBase
+{
+ Q_OBJECT
+
+ public:
+ MainDialogWidget( QWidget *parent, const char *name );
+ ~MainDialogWidget();
+
+ void setRemoteHost( const QString & );
+ QString remoteHost();
+ void save();
+
+ protected:
+ void enableBrowsingArea( bool );
+ bool ensureLocatorOpen();
+ void errorScanning();
+ void finishScanning();
+
+ signals:
+ void hostValid( bool b );
+ void accept();
+
+ protected slots:
+ void hostChanged( const QString & );
+ void toggleBrowsingArea();
+ void itemSelected( QListViewItem * );
+ void itemDoubleClicked( QListViewItem * );
+ void scopeSelected( const QString & );
+ void rescan();
+
+ void foundService( QString url, int );
+ void lastSignalServices( bool success );
+ void foundScopes( QStringList scopeList );
+ void addedService( DNSSD::RemoteService::Ptr );
+ void removedService( DNSSD::RemoteService::Ptr );
+
+
+ protected:
+ QString m_scope;
+ bool m_scanning;
+ SmartPtr<KServiceLocator> m_locator;
+ DNSSD::ServiceBrowser *m_locator_dnssd;
+};
+
+#endif // MAINDIALOGWIDGET_H
diff --git a/krdc/pindown.png b/krdc/pindown.png
new file mode 100644
index 00000000..20be0650
--- /dev/null
+++ b/krdc/pindown.png
Binary files differ
diff --git a/krdc/pinup.png b/krdc/pinup.png
new file mode 100644
index 00000000..e2256b27
--- /dev/null
+++ b/krdc/pinup.png
Binary files differ
diff --git a/krdc/pointcursor.png b/krdc/pointcursor.png
new file mode 100644
index 00000000..f0382ab3
--- /dev/null
+++ b/krdc/pointcursor.png
Binary files differ
diff --git a/krdc/pointcursormask.png b/krdc/pointcursormask.png
new file mode 100644
index 00000000..69306d08
--- /dev/null
+++ b/krdc/pointcursormask.png
Binary files differ
diff --git a/krdc/preferencesdialog.cpp b/krdc/preferencesdialog.cpp
new file mode 100644
index 00000000..bb1b9c97
--- /dev/null
+++ b/krdc/preferencesdialog.cpp
@@ -0,0 +1,128 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "preferencesdialog.h"
+
+#include "hostprofiles.h"
+#include "vnc/vncprefs.h"
+#include "rdp/rdpprefs.h"
+
+#include "rdp/krdpview.h"
+#include "vnc/kvncview.h"
+
+#include <qcheckbox.h>
+#include <qvbox.h>
+
+#include <klistview.h>
+#include <klocale.h>
+
+PreferencesDialog::PreferencesDialog( QWidget *parent, const char *name )
+ : KDialogBase( Tabbed, i18n( "Preferences" ), Ok|Cancel, Ok,
+ parent, name, true )
+{
+ QVBox *page;
+ QWidget *spacer;
+
+ page = addVBoxPage( i18n( "&Host Profiles" ) );
+ m_hostProfiles = new HostProfiles( page, "m_hostProfiles" );
+
+ connect( m_hostProfiles, SIGNAL( hostDoubleClicked(HostPrefPtr) ), this, SLOT( slotHostDoubleClicked(HostPrefPtr) ));
+
+ page = addVBoxPage( i18n( "&VNC Defaults" ) );
+ m_vncPrefs = new VncPrefs( page, "m_vncPrefs" );
+ spacer = new QWidget( page );
+ page->setStretchFactor( spacer, 10 );
+
+ m_vncPrefs->cbShowPrefs->setText( i18n( "Do not &show the preferences "
+ "dialog on new connections" ) );
+
+ page = addVBoxPage( i18n( "RD&P Defaults" ) );
+ m_rdpPrefs = new RdpPrefs( page, "m_rdpPrefs" );
+ spacer = new QWidget( page );
+ page->setStretchFactor( spacer, 10 );
+
+ m_rdpPrefs->cbShowPrefs->setText( i18n( "Do not &show the preferences "
+ "dialog on new connections" ) );
+
+ HostPreferences *hp = HostPreferences::instance();
+ m_vncDefaults = SmartPtr<VncHostPref>( hp->vncDefaults() );
+ m_rdpDefaults = SmartPtr<RdpHostPref>( hp->rdpDefaults() );
+
+ load();
+}
+
+void PreferencesDialog::load()
+{
+ m_hostProfiles->load();
+
+ m_vncPrefs->setQuality( m_vncDefaults->quality() );
+ m_vncPrefs->setShowPrefs( m_vncDefaults->askOnConnect() );
+ m_vncPrefs->setUseKWallet( m_vncDefaults->useKWallet() );
+
+ m_rdpPrefs->setRdpWidth( m_rdpDefaults->width() );
+ m_rdpPrefs->setRdpHeight( m_rdpDefaults->height() );
+ m_rdpPrefs->setShowPrefs( m_rdpDefaults->askOnConnect() );
+ m_rdpPrefs->setUseKWallet( m_rdpDefaults->useKWallet() );
+ m_rdpPrefs->setColorDepth( m_rdpDefaults->colorDepth() );
+ m_rdpPrefs->setKbLayout( keymap2int( m_rdpDefaults->layout() ));
+ m_rdpPrefs->setResolution();
+}
+
+void PreferencesDialog::save()
+{
+ m_hostProfiles->save();
+
+ m_vncDefaults->setQuality( m_vncPrefs->quality() );
+ m_vncDefaults->setAskOnConnect( m_vncPrefs->showPrefs() );
+ m_vncDefaults->setUseKWallet( m_vncPrefs->useKWallet() );
+
+ m_rdpDefaults->setWidth( m_rdpPrefs->rdpWidth() );
+ m_rdpDefaults->setHeight( m_rdpPrefs->rdpHeight() );
+ m_rdpDefaults->setLayout( int2keymap( m_rdpPrefs->kbLayout() ));
+ m_rdpDefaults->setAskOnConnect( m_rdpPrefs->showPrefs() );
+ m_rdpDefaults->setUseKWallet( m_rdpPrefs->useKWallet() );
+ m_rdpDefaults->setColorDepth( m_rdpPrefs->colorDepth() );
+
+ HostPreferences *hp = HostPreferences::instance();
+ hp->sync();
+}
+
+void PreferencesDialog::slotOk()
+{
+ save();
+ accept();
+}
+
+void PreferencesDialog::slotHostDoubleClicked( HostPrefPtr hp )
+{
+ bool hostChanged = false;
+
+ if( hp->type() == RdpHostPref::RdpType )
+ hostChanged = KRdpView::editPreferences( hp );
+ else if( hp->type() == VncHostPref::VncType )
+ hostChanged = KVncView::editPreferences( hp );
+
+ if( hostChanged )
+ {
+ m_hostProfiles->hostListView->clear();
+ m_hostProfiles->load();
+ }
+}
+
+#include "preferencesdialog.moc"
diff --git a/krdc/preferencesdialog.h b/krdc/preferencesdialog.h
new file mode 100644
index 00000000..e6d3ea8b
--- /dev/null
+++ b/krdc/preferencesdialog.h
@@ -0,0 +1,57 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-2003 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PREFERENCESDIALOG_H
+#define PREFERENCESDIALOG_H
+
+#include <kdialogbase.h>
+
+#include "smartptr.h"
+#include "vnc/vnchostpref.h"
+#include "rdp/rdphostpref.h"
+
+class HostProfiles;
+class VncPrefs;
+class RdpPrefs;
+
+class PreferencesDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ PreferencesDialog( QWidget *parent, const char *name=0 );
+ ~PreferencesDialog() {};
+
+ protected slots:
+ void slotOk();
+ void slotHostDoubleClicked( HostPrefPtr );
+
+ protected:
+ void load();
+ void save();
+
+ HostProfiles *m_hostProfiles;
+ VncPrefs *m_vncPrefs;
+ RdpPrefs *m_rdpPrefs;
+ SmartPtr<VncHostPref> m_vncDefaults;
+ SmartPtr<RdpHostPref> m_rdpDefaults;
+};
+
+#endif // PREFERENCESDIALOG_H
+
diff --git a/krdc/rdp.protocol b/krdc/rdp.protocol
new file mode 100644
index 00000000..20a3c63e
--- /dev/null
+++ b/krdc/rdp.protocol
@@ -0,0 +1,12 @@
+[Protocol]
+exec=krdc '%u'
+protocol=rdp
+input=none
+output=none
+helper=true
+listing=false
+reading=false
+writing=false
+makedir=false
+deleting=false
+Icon=krdc
diff --git a/krdc/rdp/Makefile.am b/krdc/rdp/Makefile.am
new file mode 100644
index 00000000..51f35e89
--- /dev/null
+++ b/krdc/rdp/Makefile.am
@@ -0,0 +1,11 @@
+INCLUDES=-I$(top_srcdir)/krdc $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = librdp.la
+
+librdp_la_SOURCES = krdpview.cpp rdphostpref.cpp rdpprefs.ui
+
+noinst_HEADERS = krdpview.h rdphostpref.h
+librdp_la_LIBADD = $(LIB_KDEUI) $(LIBJPEG)
+librdp_la_LDFLAGS = $(all_libraries) $(KDE_RPATH)
diff --git a/krdc/rdp/README.patch b/krdc/rdp/README.patch
new file mode 100644
index 00000000..a3e705d8
--- /dev/null
+++ b/krdc/rdp/README.patch
@@ -0,0 +1,12 @@
+Read this for how to use the rdesktop patch:
+
+This patch is intended for rdesktop 1.3.1 only. It has not been tested
+with other versions, and future versions should (hopefully) include it
+by default.
+
+- Get a copy of the rdesktop 1.3.1 sources and unpack it.
+- Place the rdesktop.patch file in the top directory of the unpacked
+ sources.
+- Go to the directory where you placed the patch and type the following
+ command: patch -p 0 -i rdesktop.patch
+- You can now compile the sources as usual.
diff --git a/krdc/rdp/krdpview.cpp b/krdc/rdp/krdpview.cpp
new file mode 100644
index 00000000..dc388e29
--- /dev/null
+++ b/krdc/rdp/krdpview.cpp
@@ -0,0 +1,369 @@
+/*
+ krdpview.h, implementation of the KRdpView class
+ Copyright (C) 2002 Arend van Beelen jr.
+
+ 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.
+
+ This program 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 General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ For any questions, comments or whatever, you may mail me at: arend@auton.nl
+*/
+
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kwallet.h>
+#include <kpassdlg.h>
+
+#include <qvbox.h>
+#include <qxembed.h>
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+
+#undef Bool
+
+#include "krdpview.h"
+#include "rdphostpref.h"
+#include "rdpprefs.h"
+
+bool rdpAppDataConfigured = false;
+extern KWallet::Wallet *wallet;
+
+static KRdpView *krdpview;
+
+RdpContainer::RdpContainer(QWidget *parent, const char *name, WFlags f) :
+ QXEmbed(parent, name, f),
+ m_viewOnly(false)
+{
+}
+
+RdpContainer::~RdpContainer()
+{
+}
+
+void RdpContainer::windowChanged(WId window)
+{
+ if(window == 0)
+ {
+ emit embeddedWindowDestroyed();
+ }
+ else
+ {
+ emit newEmbeddedWindow(window);
+ }
+}
+
+bool RdpContainer::x11Event(XEvent *e)
+{
+ // FIXME: mouse events still get through in view-only
+ if(m_viewOnly && (e->type == KeyPress || e->type == KeyRelease || e->type == ButtonPress ||
+ e->type == ButtonRelease || e->type == MotionNotify || e->type == FocusIn ||
+ e->type == FocusOut || e->type == EnterNotify || e->type == LeaveNotify))
+ {
+ return true;
+ }
+
+ return QXEmbed::x11Event(e);
+}
+
+
+// constructor
+KRdpView::KRdpView(QWidget *parent, const char *name,
+ const QString &host, int port,
+ const QString &user, const QString &password,
+ int flags, const QString &domain,
+ const QString &shell, const QString &directory) :
+ KRemoteView(parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase | Qt::WStaticContents),
+ m_name(name),
+ m_host(host),
+ m_port(port),
+ m_user(user),
+ m_password(password),
+ m_flags(flags),
+ m_domain(domain),
+ m_shell(shell),
+ m_directory(directory),
+ m_quitFlag(false),
+ m_process(NULL)
+{
+ krdpview = this;
+ setFixedSize(16, 16);
+ if(m_port == 0)
+ {
+ m_port = TCP_PORT_RDP;
+ }
+
+ m_container = new RdpContainer(this);
+}
+
+// destructor
+KRdpView::~KRdpView()
+{
+ startQuitting();
+ delete m_container;
+}
+
+// returns the size of the framebuffer
+QSize KRdpView::framebufferSize()
+{
+ return m_container->sizeHint();
+}
+
+// returns the suggested size
+QSize KRdpView::sizeHint()
+{
+ return maximumSize();
+}
+
+// start closing the connection
+void KRdpView::startQuitting()
+{
+ m_quitFlag = true;
+ if(m_process != NULL)
+ {
+ m_container->sendDelete();
+ }
+}
+
+// are we currently closing the connection?
+bool KRdpView::isQuitting()
+{
+ return m_quitFlag;
+}
+
+// return the host we're connected to
+QString KRdpView::host()
+{
+ return m_host;
+}
+
+// return the port number we're connected on
+int KRdpView::port()
+{
+ return m_port;
+}
+
+bool KRdpView::editPreferences( HostPrefPtr host )
+{
+ SmartPtr<RdpHostPref> hp( host );
+
+ int wv = hp->width();
+ int hv = hp->height();
+ int cd = hp->colorDepth();
+ QString kl = hp->layout();
+ bool kwallet = hp->useKWallet();
+
+ // show preferences dialog
+ KDialogBase *dlg = new KDialogBase( 0L, "dlg", true,
+ i18n( "RDP Host Preferences for %1" ).arg( host->host() ),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true );
+
+ QVBox *vbox = dlg->makeVBoxMainWidget();
+ RdpPrefs *prefs = new RdpPrefs( vbox );
+ QWidget *spacer = new QWidget( vbox );
+ vbox->setStretchFactor( spacer, 10 );
+
+ prefs->setRdpWidth( wv );
+ prefs->setRdpHeight( hv );
+ prefs->setResolution();
+ prefs->setColorDepth(cd);
+ prefs->setKbLayout( keymap2int( kl ) );
+ prefs->setShowPrefs( true );
+ prefs->setUseKWallet(kwallet);
+
+ if ( dlg->exec() == QDialog::Rejected )
+ return false;
+
+ wv = prefs->rdpWidth();
+ hv = prefs->rdpHeight();
+ kl = int2keymap( prefs->kbLayout() );
+ hp->setAskOnConnect( prefs->showPrefs() );
+ hp->setWidth(wv);
+ hp->setHeight(hv);
+ hp->setColorDepth( prefs->colorDepth() );
+ hp->setLayout(kl);
+ hp->setUseKWallet(prefs->useKWallet());
+
+ delete dlg;
+ return true;
+}
+
+// open a connection
+bool KRdpView::start()
+{
+ SmartPtr<RdpHostPref> hp, rdpDefaults;
+ bool useKWallet = false;
+
+ if(!rdpAppDataConfigured)
+ {
+ HostPreferences *hps = HostPreferences::instance();
+ hp = SmartPtr<RdpHostPref>(hps->createHostPref(m_host,
+ RdpHostPref::RdpType));
+ if(hp->askOnConnect())
+ {
+ if( !editPreferences( hp ))
+ return false;
+ hps->sync();
+ }
+
+ useKWallet = hp->useKWallet();
+ }
+
+ m_container->show();
+
+ m_process = new KProcess(m_container);
+ *m_process << "rdesktop";
+ *m_process << "-g" << (QString::number(hp->width()) + "x" + QString::number(hp->height()));
+ *m_process << "-k" << hp->layout();
+ if(!m_user.isEmpty()) { *m_process << "-u" << m_user; }
+
+ if(m_password.isEmpty() && useKWallet ) {
+ QString krdc_folder = "KRDC-RDP";
+
+ // Bugfix: Check if wallet has been closed by an outside source
+ if ( wallet && !wallet->isOpen() ) {
+ delete wallet; wallet=0;
+ }
+
+ // Do we need to open the wallet?
+ if ( !wallet ) {
+ QString walletName = KWallet::Wallet::NetworkWallet();
+ wallet = KWallet::Wallet::openWallet(walletName);
+ }
+
+ if (wallet && wallet->isOpen()) {
+ bool walletOK = wallet->hasFolder(krdc_folder);
+ if (walletOK == false) {
+ walletOK = wallet->createFolder(krdc_folder);
+ }
+
+ if (walletOK == true) {
+ wallet->setFolder(krdc_folder);
+ if ( wallet->hasEntry(m_host) ) {
+ wallet->readPassword(m_host, m_password);
+ }
+ }
+
+ if ( m_password.isEmpty() ) {
+ //There must not be an existing entry. Let's make one.
+ QCString newPassword;
+ if (KPasswordDialog::getPassword(newPassword, i18n("Please enter the password.")) == KPasswordDialog::Accepted) {
+ m_password = newPassword;
+ wallet->writePassword(m_host, m_password);
+ }
+ }
+ }
+ }
+
+ if(!m_password.isEmpty()) {
+ *m_process << "-p" << m_password;
+ }
+
+ *m_process << "-X" << ("0x" + QString::number(m_container->winId(), 16));
+ *m_process << "-a" << QString::number(hp->colorDepth());
+ *m_process << (m_host + ":" + QString::number(m_port));
+ connect(m_process, SIGNAL(processExited(KProcess *)), SLOT(processDied(KProcess *)));
+ connect(m_process, SIGNAL(receivedStderr(KProcess *, char *, int)), SLOT(receivedStderr(KProcess *, char *, int)));
+ connect(m_container, SIGNAL(embeddedWindowDestroyed()), SLOT(connectionClosed()));
+ connect(m_container, SIGNAL(newEmbeddedWindow(WId)), SLOT(connectionOpened(WId)));
+ qDebug("Color depth: %d", hp->colorDepth());
+ if(!m_process->start(KProcess::NotifyOnExit, KProcess::Stderr))
+ {
+ KMessageBox::error(0, i18n("Could not start rdesktop; make sure rdesktop is properly installed."),
+ i18n("rdesktop Failure"));
+ return false;
+ }
+
+ setStatus(REMOTE_VIEW_CONNECTING);
+
+ return true;
+}
+
+void KRdpView::switchFullscreen(bool on)
+{
+ if(on == true)
+ {
+ m_container->grabKeyboard();
+ }
+}
+
+// captures pressed keys
+void KRdpView::pressKey(XEvent *e)
+{
+ m_container->x11Event(e);
+ m_container->grabKeyboard();
+}
+
+bool KRdpView::viewOnly()
+{
+ return m_container->m_viewOnly;
+}
+
+void KRdpView::setViewOnly(bool s)
+{
+ m_container->m_viewOnly = s;
+}
+
+void KRdpView::connectionOpened(WId /*window*/)
+{
+ QSize size = m_container->sizeHint();
+
+ setStatus(REMOTE_VIEW_CONNECTED);
+ setFixedSize(size);
+ m_container->setFixedSize(size);
+ emit changeSize(size.width(), size.height());
+ emit connected();
+ setFocus();
+}
+
+void KRdpView::connectionClosed()
+{
+ emit disconnected();
+ setStatus(REMOTE_VIEW_DISCONNECTED);
+ m_quitFlag = true;
+}
+
+void KRdpView::processDied(KProcess */*proc*/)
+{
+ if(m_status == REMOTE_VIEW_CONNECTING)
+ {
+ setStatus(REMOTE_VIEW_DISCONNECTED);
+ if(m_clientVersion.isEmpty())
+ {
+ KMessageBox::error(0, i18n("Connection attempt to host failed."),
+ i18n("Connection Failure"));
+ }
+ emit disconnectedError();
+ }
+}
+
+void KRdpView::receivedStderr(KProcess */*proc*/, char *buffer, int /*buflen*/)
+{
+ QString output(buffer);
+ QString line;
+ int i = 0;
+ while(!(line = output.section('\n', i, i)).isEmpty())
+ {
+ if(line.startsWith("Version "))
+ {
+ m_clientVersion = line.section(' ', 1, 1);
+ m_clientVersion = m_clientVersion.left(m_clientVersion.length() - 1);
+ return;
+ }
+ i++;
+ }
+}
+
+#include "krdpview.moc"
diff --git a/krdc/rdp/krdpview.h b/krdc/rdp/krdpview.h
new file mode 100644
index 00000000..a54c2d96
--- /dev/null
+++ b/krdc/rdp/krdpview.h
@@ -0,0 +1,113 @@
+/*
+ krdpview.h, declaration of the KRdpView class
+ Copyright (C) 2002 Arend van Beelen jr.
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with this program; if
+ not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+
+ For any questions, comments or whatever, you may mail me at: arend@auton.nl
+*/
+
+#ifndef KRDPVIEW_H
+#define KRDPVIEW_H
+
+#include <qxembed.h>
+
+#include "hostpreferences.h"
+#include "kremoteview.h"
+
+#define TCP_PORT_RDP 3389
+#define RDP_LOGON_NORMAL 0x33
+
+class KProcess;
+class KRdpView;
+
+class RdpContainer : public QXEmbed
+{
+ Q_OBJECT
+
+ friend class KRdpView;
+
+ public:
+ RdpContainer(QWidget *parent = 0, const char *name = 0, WFlags f = 0);
+ ~RdpContainer();
+
+ signals:
+ void newEmbeddedWindow(WId window);
+
+ protected:
+ virtual void windowChanged(WId window);
+ virtual bool x11Event(XEvent *e);
+
+ private:
+ bool m_viewOnly; // if set: ignore all input
+};
+
+class KRdpView : public KRemoteView
+{
+ Q_OBJECT
+
+ public:
+ // constructor and destructor
+ KRdpView(QWidget *parent = 0, const char *name = 0,
+ const QString &host = QString::null, int port = TCP_PORT_RDP,
+ const QString &user = QString::null, const QString &password = QString::null,
+ int flags = RDP_LOGON_NORMAL, const QString &domain = QString::null,
+ const QString &shell = QString::null, const QString &directory = QString::null);
+ virtual ~KRdpView();
+
+ // functions regarding the window
+ virtual QSize framebufferSize(); // returns the size of the remote view
+ QSize sizeHint(); // returns the suggested size
+ virtual bool viewOnly();
+
+ // functions regarding the connection
+ virtual void startQuitting(); // start closing the connection
+ virtual bool isQuitting(); // are we currently closing the connection?
+ virtual QString host(); // return the host we're connected to
+ virtual int port(); // return the port number we're connected on
+ virtual bool start(); // open a connection
+
+ static bool editPreferences( HostPrefPtr );
+
+ public slots:
+ virtual void switchFullscreen(bool on);
+ virtual void pressKey(XEvent *k); // send a generated key to the server
+ virtual void setViewOnly(bool s);
+
+ private:
+ // properties used for setting up the connection
+ QString m_name; // name of the connection
+ QString m_host; // the host to connect to
+ int m_port; // the port on the host
+ QString m_user; // the user to use to log in
+ QString m_password; // the password to use
+ int m_flags; // flags which determine how the connection is set up
+ QString m_domain; // the domain where the host is on
+ QString m_shell; // the shell to use
+ QString m_directory; // the working directory on the server
+
+ // other properties
+ bool m_quitFlag; // if set: die
+ QString m_clientVersion; // version number returned by rdesktop
+ RdpContainer *m_container; // container for the rdesktop window
+ KProcess *m_process; // rdesktop process
+
+ private slots:
+ void connectionOpened(WId window); // called if rdesktop started
+ void connectionClosed(); // called if rdesktop quits
+ void processDied(KProcess *); // called if rdesktop dies
+ void receivedStderr(KProcess *proc, char *buffer, int buflen);
+ // catches rdesktop debug output
+};
+
+#endif
diff --git a/krdc/rdp/rdesktop.patch b/krdc/rdp/rdesktop.patch
new file mode 100644
index 00000000..a027799c
--- /dev/null
+++ b/krdc/rdp/rdesktop.patch
@@ -0,0 +1,76 @@
+--- rdesktop.c.orig 2004-03-08 18:02:58.000000000 +0100
++++ rdesktop.c 2004-03-09 22:32:19.000000000 +0100
+@@ -27,6 +27,7 @@
+ #include <sys/time.h> /* gettimeofday */
+ #include <sys/times.h> /* times */
+ #include <errno.h>
++#include <X11/Xlib.h> /* Window */
+ #include "rdesktop.h"
+
+ #ifdef EGD_SOCKET
+@@ -68,6 +69,7 @@
+ BOOL g_console_session = False;
+ BOOL g_numlock_sync = False;
+ extern BOOL g_owncolmap;
++extern Window g_embed_wnd;
+
+ #ifdef WITH_RDPSND
+ BOOL g_rdpsnd = False;
+@@ -112,6 +114,7 @@
+ fprintf(stderr, " -S: caption button size (single application mode)\n");
+ fprintf(stderr, " -T: window title\n");
+ fprintf(stderr, " -N: enable numlock synchronisation\n");
++ fprintf(stderr, " -X: embed into another window with a given id.\n");
+ fprintf(stderr, " -a: connection colour depth\n");
+ fprintf(stderr, " -r: enable specified device redirection (currently: sound)\n");
+ fprintf(stderr, " -0: attach to console\n");
+@@ -224,6 +227,7 @@
+ prompt_password = False;
+ domain[0] = password[0] = shell[0] = directory[0] = 0;
+ strcpy(keymapname, "en-us");
++ g_embed_wnd = 0;
+
+ #ifdef RDP2VNC
+ #define VNCOPT "V:Q:"
+@@ -231,7 +235,7 @@
+ #define VNCOPT
+ #endif
+
+- while ((c = getopt(argc, argv, VNCOPT "u:d:s:c:p:n:k:g:fbeEmCDKS:T:Na:r:045h?")) != -1)
++ while ((c = getopt(argc, argv, VNCOPT "u:d:s:c:p:n:k:g:fbeEmCDKS:T:NX:a:r:045h?")) != -1)
+ {
+ switch (c)
+ {
+@@ -374,6 +378,10 @@
+ g_numlock_sync = True;
+ break;
+
++ case 'X':
++ g_embed_wnd = strtod(optarg, NULL);
++ break;
++
+ case 'a':
+ g_server_bpp = strtol(optarg, NULL, 10);
+ if (g_server_bpp != 8 && g_server_bpp != 16 && g_server_bpp != 15
+--- xwin.c.orig 2004-03-08 18:02:58.000000000 +0100
++++ xwin.c 2004-03-10 16:17:59.000000000 +0100
+@@ -41,6 +41,7 @@
+ static int g_x_socket;
+ static Screen *g_screen;
+ Window g_wnd;
++Window g_embed_wnd;
+ BOOL g_enable_compose = False;
+ static GC g_gc;
+ static BOOL g_gc_initialized = False;
+@@ -929,6 +930,11 @@
+ XFree(sizehints);
+ }
+
++ if ( g_embed_wnd )
++ {
++ XReparentWindow(g_display, g_wnd, g_embed_wnd, 0, 0);
++ }
++
+ input_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
+ VisibilityChangeMask | FocusChangeMask;
+
diff --git a/krdc/rdp/rdphostpref.cpp b/krdc/rdp/rdphostpref.cpp
new file mode 100644
index 00000000..783c41e4
--- /dev/null
+++ b/krdc/rdp/rdphostpref.cpp
@@ -0,0 +1,180 @@
+/*
+ rdphostpref.cpp, handles preferences for RDP hosts
+ Copyright (C) 2003 Arend van Beelen jr.
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with this program; if
+ not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+
+ For any questions, comments or whatever, you may mail me at: arend@auton.nl
+*/
+
+#include "rdphostpref.h"
+#include <kconfig.h>
+#include <klocale.h>
+
+const QString RdpHostPref::RdpType = "RDP";
+
+RdpHostPref::RdpHostPref(KConfig *conf, const QString &host, const QString &type) :
+ HostPref(conf, host, type),
+ m_width(800),
+ m_height(600),
+ m_colorDepth(8),
+ m_layout("en-us"),
+ m_askOnConnect(true),
+ m_useKWallet(true)
+{
+}
+
+RdpHostPref::~RdpHostPref()
+{
+}
+
+void RdpHostPref::save()
+{
+ if ( !m_host.isEmpty() && !m_type.isEmpty() )
+ {
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_config->writeEntry(p+"exists", true);
+ m_config->writeEntry(p+"width", m_width);
+ m_config->writeEntry(p+"height", m_height);
+ m_config->writeEntry(p+"colorDepth", m_colorDepth);
+ m_config->writeEntry(p+"layout", m_layout);
+ m_config->writeEntry(p+"askOnConnect", m_askOnConnect);
+ m_config->writeEntry(p+"useKWallet", m_useKWallet);
+ }
+ else
+ {
+ m_config->setGroup( "RdpDefaultSettings" );
+ m_config->writeEntry( "rdpWidth", m_width );
+ m_config->writeEntry( "rdpHeight", m_height );
+ m_config->writeEntry( "rdpColorDepth", m_colorDepth);
+ m_config->writeEntry( "rdpKeyboardLayout", m_layout );
+ m_config->writeEntry( "rdpShowHostPreferences", m_askOnConnect );
+ m_config->writeEntry( "rdpUseKWallet", m_useKWallet );
+ }
+}
+
+void RdpHostPref::load()
+{
+ if ( !m_host.isEmpty() && !m_type.isEmpty() )
+ {
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_width = m_config->readNumEntry(p+"width", 800);
+ m_height = m_config->readNumEntry(p+"height", 600);
+ m_colorDepth = m_config->readNumEntry(p+"colorDepth", 8);
+ m_layout = m_config->readEntry(p+"layout", "en-us");
+ m_askOnConnect = m_config->readBoolEntry(p+"askOnConnect", true);
+ m_useKWallet = m_config->readBoolEntry(p+"useKWallet", true);
+ }
+ else
+ {
+ setDefaults();
+ }
+}
+
+void RdpHostPref::remove()
+{
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_config->deleteEntry(p+"exists");
+ m_config->deleteEntry(p+"width");
+ m_config->deleteEntry(p+"height");
+ m_config->deleteEntry(p+"colorDepth");
+ m_config->deleteEntry(p+"layout");
+ m_config->deleteEntry(p+"askOnConnect");
+ m_config->deleteEntry(p+"useKWallet");
+}
+
+void RdpHostPref::setDefaults()
+{
+ m_config->setGroup("RdpDefaultSettings");
+ m_width = m_config->readNumEntry("rdpWidth", 800);
+ m_height = m_config->readNumEntry("rdpHeight", 600);
+ m_colorDepth = m_config->readNumEntry("rdpColorDepth", 8);
+ m_layout = m_config->readEntry("rdpKeyboardLayout", "en-us");
+ m_askOnConnect = m_config->readBoolEntry("rdpShowHostPreferences", true);
+ m_useKWallet = m_config->readBoolEntry("rdpUseKWallet", true);
+}
+
+QString RdpHostPref::prefDescription() const
+{
+ return i18n("Show Preferences: %1, Resolution: %2x%3, Color Depth: %4, Keymap: %5, KWallet: %6")
+ .arg(m_askOnConnect ? i18n("yes") : i18n("no")).arg(m_width).arg(m_height)
+ .arg(m_colorDepth).arg(m_layout).arg(m_useKWallet ? i18n("yes") : i18n("no"));
+}
+
+void RdpHostPref::setWidth(int w)
+{
+ m_width = w;
+ save();
+}
+
+int RdpHostPref::width() const
+{
+ return m_width;
+}
+
+void RdpHostPref::setHeight(int h)
+{
+ m_height = h;
+ save();
+}
+
+int RdpHostPref::height() const
+{
+ return m_height;
+}
+
+void RdpHostPref::setColorDepth(int d)
+{
+ m_colorDepth = d;
+ save();
+}
+
+int RdpHostPref::colorDepth() const
+{
+ return m_colorDepth;
+}
+
+
+void RdpHostPref::setLayout(const QString &l)
+{
+ m_layout = l;
+ save();
+}
+
+QString RdpHostPref::layout() const
+{
+ return m_layout;
+}
+
+void RdpHostPref::setAskOnConnect(bool ask)
+{
+ m_askOnConnect = ask;
+ save();
+}
+
+bool RdpHostPref::askOnConnect() const
+{
+ return m_askOnConnect;
+}
+
+void RdpHostPref::setUseKWallet(bool use) {
+ m_useKWallet = use;
+ save();
+}
+
+bool RdpHostPref::useKWallet() const {
+ return m_useKWallet;
+}
diff --git a/krdc/rdp/rdphostpref.h b/krdc/rdp/rdphostpref.h
new file mode 100644
index 00000000..355102f3
--- /dev/null
+++ b/krdc/rdp/rdphostpref.h
@@ -0,0 +1,127 @@
+/*
+ rdphostpref.h, handles preferences for RDP hosts
+ Copyright (C) 2003 Arend van Beelen jr.
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with this program; if
+ not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ MA 02111-1307 USA
+
+ For any questions, comments or whatever, you may mail me at: arend@auton.nl
+*/
+
+#ifndef RDPHOSTPREF_H
+#define RDPHOSTPREF_H
+
+#include "hostpreferences.h"
+
+static const QString rdpKeymaps[] = { "ar",
+ "cs",
+ "da",
+ "de",
+ "de-ch",
+ "en-gb",
+ "en-us",
+ "es",
+ "et",
+ "fi",
+ "fr",
+ "fr-be",
+ "fr-ca",
+ "fr-ch",
+ "hr",
+ "hu",
+ "is",
+ "it",
+ "ja",
+ "lt",
+ "lv",
+ "mk",
+ "nl",
+ "nl-be",
+ "no",
+ "pl",
+ "pt",
+ "pt-br",
+ "ru",
+ "sl",
+ "sv",
+ "th",
+ "tr" };
+static const int rdpNumKeymaps = 33;
+static const int rdpDefaultKeymap = 6; // en-us
+
+inline int keymap2int(const QString &keymap)
+{
+ int layout;
+ for(layout = 0; layout < rdpNumKeymaps; layout++)
+ {
+ if(keymap == rdpKeymaps[layout])
+ {
+ break;
+ }
+ }
+ if(layout == rdpNumKeymaps)
+ {
+ layout = rdpDefaultKeymap;
+ }
+ return layout;
+}
+
+inline QString int2keymap(int layout)
+{
+ if(layout < 0 || layout >= rdpNumKeymaps)
+ {
+ return rdpKeymaps[rdpDefaultKeymap];
+ }
+
+ return rdpKeymaps[layout];
+}
+
+class RdpHostPref : public HostPref
+{
+ protected:
+ friend class HostPreferences;
+
+ int m_width;
+ int m_height;
+ int m_colorDepth;
+ QString m_layout;
+ bool m_askOnConnect;
+ bool m_useKWallet;
+
+ virtual void load();
+ virtual void setDefaults();
+ virtual void save();
+ virtual void remove();
+
+ public:
+ static const QString RdpType;
+
+ RdpHostPref(KConfig *conf, const QString &host=QString::null,
+ const QString &type=QString::null);
+ virtual ~RdpHostPref();
+
+ virtual QString prefDescription() const;
+ void setWidth(int w);
+ int width() const;
+ void setHeight(int h);
+ int height() const;
+ void setColorDepth(int depth);
+ int colorDepth() const;
+ void setLayout(const QString &l);
+ QString layout() const;
+ void setAskOnConnect(bool ask);
+ bool askOnConnect() const;
+ bool useKWallet() const;
+ void setUseKWallet(bool);
+};
+
+#endif
diff --git a/krdc/rdp/rdpprefs.ui b/krdc/rdp/rdpprefs.ui
new file mode 100644
index 00000000..8e493d7a
--- /dev/null
+++ b/krdc/rdp/rdpprefs.ui
@@ -0,0 +1,509 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>RdpPrefs</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>RdpPrefs</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>449</width>
+ <height>177</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>rdpGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Connection</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>70</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QComboBox" row="0" column="1" rowspan="1" colspan="4">
+ <item>
+ <property name="text">
+ <string>Small (640x480)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Medium (800x600)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Big (1024x768)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Custom (...)</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmbResolution</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>280</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="currentItem">
+ <number>1</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Here you can specify the resolution of the remote desktop. This resolution determines the size of the desktop that will be presented to you.</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="2">
+ <property name="name">
+ <cstring>spinWidth</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>9999</number>
+ </property>
+ <property name="value">
+ <number>800</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the width of the remote desktop. You can only change this value manually if you select Custom as desktop resolution above.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="3">
+ <property name="name">
+ <cstring>heightLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>H&amp;eight:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinHeight</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox" row="1" column="4">
+ <property name="name">
+ <cstring>spinHeight</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maxValue">
+ <number>9999</number>
+ </property>
+ <property name="value">
+ <number>600</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the height of the remote desktop. You can only change this value manually if you select Custom as desktop resolution above.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="3" column="1" rowspan="1" colspan="4">
+ <item>
+ <property name="text">
+ <string>Arabic (ar)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Czech (cs)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Danish (da)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>German (de)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Swiss German (de-ch)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>British English (en-gb)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>US English (en-us)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Spanish (es)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Estonian (et)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Finnish (fi)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>French (fr)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Belgium (fr-be)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>French Canadian (fr-ca)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Swiss French (fr-ch)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Croatian (hr)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Hungarian (hu)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Icelandic (is)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Italian (it)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Japanese (ja)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Lithuanian (lt)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Latvian (lv)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Macedonian (mk)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Dutch (nl)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Belgian Dutch (nl-be)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Norwegian (no)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Polish (pl)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Portuguese (pt)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Brazilian (pt-br)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Russian (ru)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Slovenian (sl)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Swedish (sv)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Thai (th)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Turkish (tr)</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmbKbLayout</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>280</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="editable">
+ <bool>false</bool>
+ </property>
+ <property name="currentItem">
+ <number>4</number>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this to specify your keyboard layout. This layout setting is used to send the correct keyboard codes to the server.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>layoutLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&amp;Keyboard layout:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbKbLayout</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>cbUseKWallet</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Use K&amp;Wallet for passwords</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable this option to store your passwords with KWallet.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>resolutionLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Desktop &amp;resolution:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbResolution</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>colorDepthLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Color &amp;depth:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbKbLayout</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>widthLabel</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Width:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>spinWidth</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="2" column="1" rowspan="1" colspan="4">
+ <item>
+ <property name="text">
+ <string>Low Color (8 Bit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>High Color (16 Bit)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>True Color (24 Bit)</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmbColorDepth</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbShowPrefs</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show this dialog again for this host</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Select this option if you do not want to be asked for the settings when connecting to a host. For hosts with existing profiles these profiles will be taken. New hosts will be configured with the defaults.</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>cmbResolution</sender>
+ <signal>activated(int)</signal>
+ <receiver>RdpPrefs</receiver>
+ <slot>resolutionChanged(int)</slot>
+ </connection>
+ <connection>
+ <sender>cmbColorDepth</sender>
+ <signal>activated(int)</signal>
+ <receiver>RdpPrefs</receiver>
+ <slot>colorDepth()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>cmbResolution</tabstop>
+ <tabstop>spinWidth</tabstop>
+ <tabstop>spinHeight</tabstop>
+ <tabstop>cmbColorDepth</tabstop>
+ <tabstop>cmbKbLayout</tabstop>
+ <tabstop>cbShowPrefs</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in implementation">rdpprefs.ui.h</include>
+</includes>
+<slots>
+ <slot access="protected" specifier="non virtual">resolutionChanged( int selection )</slot>
+ <slot returnType="int">colorDepth()</slot>
+ <slot>setColorDepth( int depth )</slot>
+</slots>
+<functions>
+ <function specifier="non virtual">setRdpWidth( int w )</function>
+ <function specifier="non virtual" returnType="int">rdpWidth()</function>
+ <function specifier="non virtual">setRdpHeight( int h )</function>
+ <function specifier="non virtual" returnType="int">rdpHeight()</function>
+ <function specifier="non virtual">setResolution()</function>
+ <function specifier="non virtual" returnType="int">resolution()</function>
+ <function specifier="non virtual">setKbLayout( int i )</function>
+ <function specifier="non virtual" returnType="int">kbLayout()</function>
+ <function specifier="non virtual">setShowPrefs( bool b )</function>
+ <function specifier="non virtual" returnType="bool">showPrefs()</function>
+ <function specifier="non virtual">setUseKWallet( bool b )</function>
+ <function specifier="non virtual" returnType="bool">useKWallet()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/krdc/rdp/rdpprefs.ui.h b/krdc/rdp/rdpprefs.ui.h
new file mode 100644
index 00000000..06efe7f4
--- /dev/null
+++ b/krdc/rdp/rdpprefs.ui.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+
+void RdpPrefs::resolutionChanged( int selection )
+{
+ bool enable = (selection==3);
+ spinWidth->setEnabled(enable);
+ spinHeight->setEnabled(enable);
+ widthLabel->setEnabled(enable);
+ heightLabel->setEnabled(enable);
+
+ switch(selection)
+ {
+ case 0:
+ spinWidth->setValue(640);
+ spinHeight->setValue(480);
+ break;
+
+ case 1:
+ spinWidth->setValue(800);
+ spinHeight->setValue(600);
+ break;
+
+ case 2:
+ spinWidth->setValue(1024);
+ spinHeight->setValue(768);
+ break;
+
+ case 3:
+ default:
+ break;
+ }
+}
+
+
+void RdpPrefs::setRdpWidth( int w )
+{
+ spinWidth->setValue(w);
+}
+
+
+int RdpPrefs::rdpWidth()
+{
+ return spinWidth->value();
+}
+
+
+void RdpPrefs::setRdpHeight( int h )
+{
+ spinHeight->setValue(h);
+}
+
+
+int RdpPrefs::rdpHeight()
+{
+ return spinHeight->value();
+}
+
+
+int RdpPrefs::colorDepth()
+{
+ qDebug("current depth: %i", cmbColorDepth->currentItem() );
+ switch (cmbColorDepth->currentItem())
+ {
+ case 0:
+ return 8;
+ case 1:
+ return 16;
+ case 2:
+ return 24;
+ default:
+ // shouldn't happen, but who knows..
+ return 8;
+ break;
+ }
+}
+
+
+void RdpPrefs::setColorDepth(int depth)
+{
+ switch (depth)
+ {
+ case 8:
+ cmbColorDepth->setCurrentItem(0);
+ break;
+ case 16:
+ cmbColorDepth->setCurrentItem(1);
+ break;
+ case 24:
+ cmbColorDepth->setCurrentItem(2);
+ break;
+ default:
+ break;
+ }
+}
+
+void RdpPrefs::setResolution()
+{
+ if (rdpWidth()==640 && rdpHeight()==480)
+ {
+ cmbResolution->setCurrentItem(0);
+ }
+ else if (rdpWidth()==800 && rdpHeight()==600)
+ {
+ cmbResolution->setCurrentItem(1);
+ }
+ else if (rdpWidth()==1024 && rdpHeight()==768)
+ {
+ cmbResolution->setCurrentItem(2);
+ }
+ else
+ {
+ cmbResolution->setCurrentItem(3);
+ }
+ resolutionChanged( cmbResolution->currentItem() );
+}
+
+
+int RdpPrefs::resolution()
+{
+ return cmbResolution->currentItem();
+}
+
+
+void RdpPrefs::setKbLayout( int i )
+{
+ cmbKbLayout->setCurrentItem( i );
+}
+
+
+int RdpPrefs::kbLayout()
+{
+ return cmbKbLayout->currentItem();
+}
+
+
+void RdpPrefs::setShowPrefs( bool b )
+{
+ cbShowPrefs->setChecked( b );
+}
+
+
+bool RdpPrefs::showPrefs()
+{
+ return cbShowPrefs->isChecked();
+}
+
+void RdpPrefs::setUseKWallet( bool b )
+{
+ cbUseKWallet->setChecked(b);
+}
+
+bool RdpPrefs::useKWallet()
+{
+ return cbUseKWallet->isChecked();
+}
diff --git a/krdc/smartptr.cpp b/krdc/smartptr.cpp
new file mode 100644
index 00000000..e11ffb41
--- /dev/null
+++ b/krdc/smartptr.cpp
@@ -0,0 +1,17 @@
+/***************************************************************************
+ begin : Wed Jan 19 02:19 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <smartptr.h>
+
diff --git a/krdc/smartptr.h b/krdc/smartptr.h
new file mode 100644
index 00000000..d80f258e
--- /dev/null
+++ b/krdc/smartptr.h
@@ -0,0 +1,433 @@
+/***************************************************************************
+ begin : Wed Jan 1 17:56 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 SMARTPTR_H
+#define SMARTPTR_H
+
+#include <qstring.h>
+
+class WeakPtr;
+
+/**
+ * @internal
+ */
+struct SmartPtrRefCount {
+ SmartPtrRefCount(int toObj, int toThis) :
+ refsToObject(toObj),
+ refsToThis(toThis) {
+ }
+ int refsToObject; // number of pointers to the object, 0 if released
+ int refsToThis; // number of pointer to the ref count
+};
+
+/**
+ * SmartPtr is a reference counting smart pointer. When you create
+ * the first instance it will create a new counter for the pointee
+ * and it share it with all other SmartPtr instances for that pointee.
+ * The reference count can only be kept accurate when you do not create
+ * a second 'realm' of references by converting a SmartPtr into a
+ * regular pointer and then create a new SmartPtr from that pointer.
+ * When the last instance of a SmartPtr for the given object has been
+ * deleted the object itself will be deleted. You can stop the SmartPtr
+ * system to manage an object by calling @ref release() on any of
+ * the pointers pointing to that object. All SmartPtrs will then stop
+ * managing the object, and you can also safely create a second 'realm'.
+ *
+ * SmartPtr can be combined with @ref WeakPtr. A WeakPtr
+ * does not influence its life cycle, but notices when a SmartPtr
+ * deletes the object.
+ *
+ * The recommended way to use SmartPtr and @ref WeakPtr is to use SmartPtr
+ * for all aggregations and WeakPtr for associations. Unlike auto_ptr,
+ * SmartPtr can be used in collections.
+ *
+ * SmartPtr is not thread-safe. All instances of SmartPtrs pointing
+ * to a pointee must always be in the same thread, unless you break
+ * the 'realm' by calling @ref release() in one thread and give the
+ * original pointer the other thread. It can then create a new SmartPtr
+ * and control the lifecycle of the object.
+ * @see WeakPtr
+ */
+template <class T>
+class SmartPtr
+{
+ public: // members are public because of problems with gcc 3.2
+ friend class WeakPtr;
+
+ /// @internal
+ T* ptr;
+ /// @internal
+ mutable SmartPtrRefCount *rc; // if !rc, refcount=1 is assumed
+
+protected:
+ void freePtr() {
+ if (!ptr)
+ return;
+ if (!rc)
+ delete ptr;
+ else {
+ if (rc->refsToObject > 0) {
+ Q_ASSERT(rc->refsToObject >= rc->refsToThis);
+ if (rc->refsToObject == 1) {
+ delete ptr;
+ rc->refsToObject = -1;
+ }
+ else
+ rc->refsToObject--;
+ }
+ rc->refsToThis--;
+ if (rc->refsToThis < 1)
+ delete rc;
+ }
+ }
+
+ void init(T *sptr, SmartPtrRefCount *&orc)
+ {
+ ptr = sptr;
+ if (!sptr)
+ rc = 0;
+ else if (!orc) {
+ orc = new SmartPtrRefCount(2, 2);
+ rc = orc;
+ }
+ else {
+ rc = orc;
+ rc->refsToThis++;
+ if (rc->refsToObject) {
+ // prevent initialization from invalid WeakPtr
+ Q_ASSERT(rc->refsToObject > 0);
+ rc->refsToObject++;
+ }
+ }
+ }
+
+ SmartPtr(T *p, SmartPtrRefCount *&orc)
+ {
+ init(p, orc);
+ }
+
+public:
+ /**
+ * Creates a SmartPtr that refers to the given pointer @p.
+ * SmartPtr will take control over the object and delete it
+ * when the last SmartPtr that referes to the object
+ * has been deleted.
+ * @param p the pointer to the object to manage, or the null pointer
+ */
+ SmartPtr(T* p = 0) :
+ ptr(p),
+ rc(0)
+ {
+ }
+
+ /**
+ * Copies the given SmartPtr, sharing ownership with the other
+ * pointer. Increases the reference count by 1 (if the object
+ * has not been @ref release()d).
+ * @param sptr the object pointer to copy
+ */
+ SmartPtr(const SmartPtr<T> &sptr)
+ {
+ init(sptr.ptr, sptr.rc);
+ }
+
+ /**
+ * Copies the given SmartPtr, sharing ownership with the other
+ * pointer. Increases the reference count by 1 (if the object
+ * has not been @ref release()d).
+ * @param sptr the object pointer to copy
+ */
+ template<class T2>
+ SmartPtr(const SmartPtr<T2> &sptr)
+ {
+ init((T*)sptr.ptr, sptr.rc);
+ }
+
+ /**
+ * Delete the pointer and, if the reference count is one and the object has not
+ * been released, deletes the object.
+ */
+ ~SmartPtr() {
+ freePtr();
+ }
+
+ /**
+ * Copies the given SmartPtr, sharing ownership with the other
+ * pointer. Increases the reference count by 1 (if the object
+ * has not been @ref release()d). The original object will be dereferenced
+ * and thus deleted, if the reference count is 1.
+ * @param sptr the object pointer to copy
+ * @return this SmartPtr object
+ */
+ SmartPtr &operator=(const SmartPtr<T> &sptr) {
+ if (this == &sptr)
+ return *this;
+
+ freePtr();
+ init(sptr.ptr, sptr.rc);
+ return *this;
+ }
+
+ /**
+ * Copies the given SmartPtr, sharing ownership with the other
+ * pointer. Increases the reference count by 1 (if the object
+ * has not been @ref release()d). The original object will be dereferenced
+ * and thus deleted, if the reference count is 1.
+ * @param sptr the object pointer to copy
+ * @return this SmartPtr object
+ */
+ template<class T2>
+ SmartPtr &operator=(const SmartPtr<T2> &sptr) {
+ if (this == static_cast<SmartPtr<T> >(&sptr))
+ return *this;
+
+ freePtr();
+ init(static_cast<T>(sptr.ptr), sptr.rc);
+ return *this;
+ }
+
+ /**
+ * Sets the SmartPointer to the given value. The original object
+ * will be dereferenced and thus deleted, if the reference count is 1.
+ * @param p the value of the new pointer
+ */
+ void set(T *p) {
+ if (ptr == p)
+ return;
+ freePtr();
+
+ ptr = p;
+ rc = 0;
+ }
+
+ /**
+ * Releases the ptr. This means it will not be memory-managed
+ * anymore, neither by this SmartPtr nor by any other pointer that
+ * shares the object. The caller is responsible for freeing the
+ * object. It is possible to assign the plain pointer (but not the
+ * SmartPtr!) to another SmartPtr that will then start memory
+ * management. This may be useful, for example, to let another
+ * thread manage the lifecyle.
+ * @return the pointer, must be freed by the user
+ * @see data()
+ */
+ T* release() {
+ if (!rc)
+ rc = new SmartPtrRefCount(0, 1);
+ else
+ rc->refsToObject = 0;
+ return ptr;
+ }
+
+ /**
+ * Sets the SmartPointer to the given value. The original object
+ * will be dereferenced and thus deleted, if the reference count is 1.
+ * @param p the value of the new pointer
+ * @return this SmartPtr object
+ */
+ SmartPtr &operator=(T *p) {
+ set(p);
+ return *this;
+ }
+
+ /**
+ * Returns true if the SmartPtr points to an actual object, false
+ * if it is the null pointer.
+ * @return true for an actual pointer, false for the null pointer
+ */
+ operator bool() const {
+ return ptr != 0;
+ }
+
+ /**
+ * Returns the plain pointer to the pointed object. The object will
+ * still be managed by the SmartPtr. You must ensure that the pointer
+ * is valid (so don't delete the SmartPtr before you are done with the
+ * plain pointer).
+ * @return the plain pointer
+ * @see data()
+ * @see release()
+ * @see WeakPtr
+ */
+ template<class T2>
+ operator T2*() const {
+ return static_cast<T2*>(ptr);
+ }
+
+ /**
+ * Returns the plain pointer to the pointed object. The object will
+ * still be managed by the SmartPtr. You must ensure that the pointer
+ * is valid (so don't delete the SmartPtr before you are done with the
+ * plain pointer).
+ * @return the plain pointer
+ * @see data()
+ * @see release()
+ * @see WeakPtr
+ */
+ template<class T2>
+ operator const T2*() const {
+ return static_cast<const T2*>(ptr);
+ }
+
+ /**
+ * Returns a reference to the pointed object. This works exactly
+ * like on a regular pointer.
+ * @return the pointer object
+ */
+ T& operator*() {
+ return *ptr;
+ }
+
+ /**
+ * Returns a reference to the pointed object. This works exactly
+ * like on a regular pointer.
+ * @return the pointer object
+ */
+ const T& operator*() const {
+ return *ptr;
+ }
+
+ /**
+ * Access a member of the pointed object. This works exactly
+ * like on a regular pointer.
+ * @return the pointer
+ */
+ T* operator->() {
+ return ptr;
+ }
+
+ /**
+ * Access a member of the pointed object. This works exactly
+ * like on a regular pointer.
+ * @return the pointer
+ */
+ const T* operator->() const {
+ return ptr;
+ }
+
+ /**
+ * Compares two SmartPtrs. They are equal if both point to the
+ * same object.
+ * @return true if both point to the same object
+ */
+ bool operator==(const SmartPtr<T>& sptr) const {
+ return ptr == sptr.ptr;
+ }
+
+ /**
+ * Compares two SmartPtrs. They are unequal if both point to
+ * different objects.
+ * @return true if both point to different objects
+ */
+ bool operator!=(const SmartPtr<T>& sptr) const {
+ return ptr != sptr.ptr;
+ }
+
+ /**
+ * Compares a SmartPtr with a plain pointer. They are equal if
+ * both point to the same object.
+ * @return true if both point to the same object
+ */
+ bool operator==(const T* p) const {
+ return ptr == p;
+ }
+
+ /**
+ * Compares a SmartPtr with a plain pointer. They are unequal if
+ * both point to different objects.
+ * @return true if both point to different objects
+ */
+ bool operator!=(const T* p) const {
+ return ptr != p;
+ }
+
+ /**
+ * Negates the pointer. True if the pointer is the null pointer
+ * @return true for the null pointer, false otherwise
+ */
+ bool operator!() const {
+ return ptr == 0;
+ }
+
+ /**
+ * Returns the pointer. The object will still be managed
+ * by the SmartPtr. You must ensure that the pointer
+ * is valid (so don't delete the SmartPtr before you are done with the
+ * plain pointer).
+ * @return the plain pointer
+ * @see release()
+ * @see WeakPtr
+ */
+ T* data() {
+ return ptr;
+ }
+
+ /**
+ * Returns the pointer. The object will still be managed
+ * by the SmartPtr. You must ensure that the pointer
+ * is valid (so don't delete the SmartPtr before you are done with the
+ * plain pointer).
+ * @return the plain pointer
+ * @see release()
+ * @see WeakPtr
+ */
+ const T* data() const {
+ return ptr;
+ }
+
+ /**
+ * Checks whether both SmartPtrs use the same pointer but two
+ * different reference counts.
+ * If yes, one of them must be 0 (object released), otherwise
+ * it is an error.
+ * @return true if the pointers are used correctly
+ */
+ bool isRCCorrect(const SmartPtr<T> &p2) const {
+ if (ptr == p2.ptr)
+ return true;
+ if (rc == p2.rc)
+ return true;
+ return (rc->refsToObject == 0) || (p2.rc->refsToObject == 0);
+ }
+
+ /**
+ * Returns the reference count of the object. The count is 0 if
+ * the object has been released (@ref release()). For the null pointer
+ * the reference count is always 1.
+ * @return the reference count, or 0 for released objects
+ */
+ int referenceCount() const {
+ return rc ? rc->refsToObject : 1;
+ }
+
+ /**
+ * Returns a string representation of the pointer.
+ * @return a string representation
+ */
+ QString toString() const {
+ int objrcount = 1;
+ int rcrcount = 0;
+
+ if (rc) {
+ objrcount = rc->refsToObject;
+ rcrcount = rc->refsToThis;
+ }
+ return QString("SmartPtr: ptr=%1, refcounts=%2, ptrnum=%3")
+ .arg((int)ptr).arg(objrcount).arg(rcrcount);
+ }
+
+};
+
+#endif
diff --git a/krdc/smb2rdc.desktop b/krdc/smb2rdc.desktop
new file mode 100644
index 00000000..0e6bd845
--- /dev/null
+++ b/krdc/smb2rdc.desktop
@@ -0,0 +1,56 @@
+# KDE Config File
+[Desktop Entry]
+ServiceTypes=application/x-smb-server
+Actions=smb2rdc
+
+[Desktop Action smb2rdc]
+Name=Open Remote Desktop Connection to This Machine
+Name[be]=Ðдкрыць аддаленае злучÑнне з кампутарам
+Name[bg]=Връзка Ñ Ð¾Ñ‚Ð´Ð°Ð»ÐµÑ‡ÐµÐ½ работно мÑÑто Ñ Ð¸Ð·Ð±Ñ€Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¿ÑŽÑ‚ÑŠÑ€
+Name[bn]=à¦à¦‡ মেশিনে পà§à¦°à¦¤à§à¦¯à¦¨à§à¦¤ ডেসà§à¦•à¦Ÿà¦ªà§‡à¦° সংযোগ খোলো
+Name[bs]=Otvori Remote Desktop vezu na ovaj raÄunar
+Name[ca]=Obre una connexió remota d'escriptori a aquesta màquina
+Name[cs]=Otevřít vzdálené pÅ™ipojení plochy k tomuto poÄítaÄi
+Name[da]=Ã…bn ekstern desktopforbindelse til denne maskine
+Name[de]=Verbindung zur Arbeitsfläche dieses Rechners herstellen
+Name[el]=ΔημιουÏγία σÏνδεσης σε απομακÏυσμένη επιφάνεια εÏγασίας σε αυτό το μηχάνημα
+Name[es]=Abrir conexión remota de escritorio a este sistema
+Name[et]=Kaugtöölaua ühenduse avamine sellesse arvutisse
+Name[eu]=Open Remote Desktop konexioa makina honetara
+Name[fa]=باز کردن اتصال رومیزی راه دور برای این ماشین
+Name[fi]=Avaa etätyöpöytäyhteys tähän koneeseen
+Name[fr]=Ouvrir une connexion distante au bureau de cette machine
+Name[gl]=Abrir Conexión Remota de Escritorio a Esta Máquina
+Name[he]=פתח חיבור שולחן עבודה מרוחק למכונה זו
+Name[hu]=Távoli munkaasztal nyitása itt
+Name[is]=Opna fjarlæga skjáborðtengingu til þessarar vélar
+Name[it]=Apri connessione a desktop remoto a questa macchina
+Name[ja]=ã“ã®ãƒ›ã‚¹ãƒˆã¸ãƒªãƒ¢ãƒ¼ãƒˆãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—接続を開ã
+Name[ka]=დáƒáƒ¨áƒáƒ áƒ”ბული სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ გáƒáƒ®áƒ¡áƒœáƒ áƒáƒ› მáƒáƒœáƒ¥áƒáƒœáƒáƒ¡áƒ—áƒáƒœ
+Name[kk]=ОÑÑ‹ компьютердегі Ò¯Ñтелге қашық қоÑылымды ашу
+Name[km]=បើក​ការ​ážâ€‹áž—្ជាប​ផ្ទៃ​ážáž»â€‹áž–ី​ចម្ងាយ​ទៅ​ម៉ាស៊ីន​នáŸáŸ‡
+Name[lt]=Užmegzti nutolusio darbastalio prijungimą prie šio kompiuterio
+Name[nb]=Ã…pne fjerntilkobling til skrivebord til denne maskinen
+Name[nds]=Schriefdisch-Feernverbinnen na dissen Reekner opmaken
+Name[ne]=यो मेशिनमा टाढाको डेसà¥à¤•à¤Ÿà¤ª जडान खोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Name[nl]=Externe bureaubladverbinding met deze computer openen
+Name[nn]=Opna samband til skrivebordet over nettverket til denne maskina
+Name[pa]=ਇਹ ਮਸ਼ੀਨ ਲਈ ਰਿਮੋਟ ਡੈਸਕਟਾਪ ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਖੋਲà©à¨¹à©‹
+Name[pl]=Otwórz zdalne połączenie z pulpitem na tej maszynie
+Name[pt]=Abrir Ligação Remota a Ecrã para Este Computador
+Name[pt_BR]=Abre Conexões Remotas para Essa Máquina
+Name[ru]=Открыть Ñоединение Remote Desktop Ñ Ñтой машиной
+Name[sk]=Otvorí spojenie na vzdialenú pracovnú plochu na tomto poÄítaÄi
+Name[sl]=Odpri povezavo z oddaljenim namizjem na tem raÄunalniku
+Name[sr]=Отвори удаљену везу Ñа радном површином на овој машини
+Name[sr@Latn]=Otvori udaljenu vezu sa radnom površinom na ovoj mašini
+Name[sv]=Öppna fjärrskrivbordsanslutning till den här datorn
+Name[tr]=Bu makinaya Uzak Masaüstü Bağlantısı aç
+Name[uk]=Відкрити з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· віддаленою Ñтільницею до цього комп'ютера
+Name[zh_CN]=打开到此计算机的远程桌é¢è¿žæŽ¥
+Name[zh_HK]=開放é ç«¯æ¡Œé¢é€£ç·šåˆ°é€™é›»è…¦
+Name[zh_TW]=在此主機上開啟é ç«¯æ¡Œé¢
+Exec=krdc %u
+Icon=krdc
+
+
diff --git a/krdc/vidmode.cpp b/krdc/vidmode.cpp
new file mode 100644
index 00000000..4c7762fb
--- /dev/null
+++ b/krdc/vidmode.cpp
@@ -0,0 +1,159 @@
+/***************************************************************************
+ vidmode.cpp - video mode switching
+ -------------------
+ begin : Tue June 3 03:08:00 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <config.h>
+#include <X11/Xlib.h>
+
+#ifdef HAVE_VIDMODE_EXTENSION
+#include <X11/extensions/xf86vmode.h>
+#endif
+
+#include "vidmode.h"
+
+#ifdef HAVE_VIDMODE_EXTENSION
+
+void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution)
+{
+ if (!oldResolution.valid)
+ return;
+
+ XF86VidModeModeInfo **modes;
+ int modecount;
+ int eventB, errorB;
+
+ if (!XF86VidModeQueryExtension(dpy, &eventB, &errorB))
+ return;
+
+ if (!XF86VidModeGetAllModeLines(dpy, oldResolution.screen, &modecount, &modes))
+ return;
+
+ for (int i = 0; i < modecount; i++) {
+ int w = (*modes[i]).hdisplay;
+ int h = (*modes[i]).vdisplay;
+
+ if ((oldResolution.width == w) &&
+ (oldResolution.height == h)) {
+ XF86VidModeSwitchToMode(dpy,oldResolution.screen,modes[i]);
+ XFlush(dpy);
+ XF86VidModeSetViewPort(dpy,DefaultScreen(dpy),0,0);
+ XFlush(dpy);
+ return;
+ }
+ }
+}
+
+Resolution vidmodeFullscreenSwitch(Display *dpy, int screen,
+ int sw, int sh, int &nx, int &ny)
+{
+ XF86VidModeModeInfo **modes;
+ int modecount;
+ int bestmode = -1;
+ int bestw, besth;
+ int eventB, errorB;
+
+ if (screen < 0)
+ return Resolution();
+
+ if (!XF86VidModeQueryExtension(dpy, &eventB, &errorB))
+ return Resolution();
+
+ if (!XF86VidModeGetAllModeLines(dpy,screen,&modecount, &modes))
+ return Resolution();
+
+ int cw = (*modes[0]).hdisplay;
+ int ch = (*modes[0]).vdisplay;
+ nx = cw;
+ ny = ch;
+ if ((cw == sw) && (ch == sh))
+ return Resolution();
+ bool foundLargeEnoughRes = (cw>=sw) && (ch>=sh);
+ bestw = cw;
+ besth = ch;
+
+ for (int i = 1; i < modecount; i++) {
+ int w = (*modes[i]).hdisplay;
+ int h = (*modes[i]).vdisplay;
+
+ if ((w == cw) && (h == ch))
+ continue;
+
+ /* If resolution matches framebuffer, take it */
+ if ((w == sw) && (h == sh)) {
+ bestw = w;
+ besth = h;
+ bestmode = i;
+ break;
+ }
+ /* if resolution larger than framebuffer... */
+ if ((w>=sw) && (h>=sh)) {
+ /* and no other previously found resoltion was smaller or
+ this is smaller than the best resolution so far, take it*/
+ if ((!foundLargeEnoughRes) ||
+ (w*h < bestw*besth)) {
+ bestw = w;
+ besth = h;
+ bestmode = i;
+ foundLargeEnoughRes = true;
+ }
+ }
+ /* If all resolutions so far were smaller than framebuffer... */
+ else if (!foundLargeEnoughRes) {
+ /* take this one it it is bigger then they were */
+ if (w*h > bestw*besth) {
+ bestw = w;
+ besth = h;
+ bestmode = i;
+ }
+ }
+ }
+
+ if (bestmode == -1)
+ return Resolution();
+
+ nx = bestw;
+ ny = besth;
+ XF86VidModeSwitchToMode(dpy,screen,modes[bestmode]);
+ XF86VidModeSetViewPort(dpy,screen,0,0);
+ XFlush(dpy);
+
+ return Resolution(cw, ch, screen);
+}
+
+#else
+
+void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution)
+{
+}
+
+Resolution vidmodeFullscreenSwitch(Display *dpy, int screen, int sw, int sh, int &nx, int &ny)
+{
+ return Resolution();
+}
+
+#endif
+
+void grabInput(Display *dpy, unsigned int winId) {
+ XGrabPointer(dpy, winId, True, 0,
+ GrabModeAsync, GrabModeAsync,
+ winId, None, CurrentTime);
+ XFlush(dpy);
+}
+
+void ungrabInput(Display *dpy) {
+ XUngrabPointer(dpy, CurrentTime);
+}
+
diff --git a/krdc/vidmode.h b/krdc/vidmode.h
new file mode 100644
index 00000000..ab17e1b5
--- /dev/null
+++ b/krdc/vidmode.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ vidmode.h - video mode switching
+ -------------------
+ begin : Tue June 3 03:11:00 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 VIDMODE_H
+#define VIDMODE_H
+
+struct Resolution {
+ Resolution(int w, int h, int s) :
+ valid(true), width(w), height(h), screen(s) {
+ }
+ Resolution() :
+ valid(false), width(0), height(0), screen(0) {
+ }
+ bool valid;
+ int width;
+ int height;
+ int screen;
+};
+
+void vidmodeNormalSwitch(Display *dpy, Resolution oldResolution);
+Resolution vidmodeFullscreenSwitch(Display *dpy, int screen, int sw, int sh, int &nx, int &ny);
+
+void grabInput(Display *dpy, unsigned int winId);
+void ungrabInput(Display *dpy);
+
+#endif
diff --git a/krdc/vnc.protocol b/krdc/vnc.protocol
new file mode 100644
index 00000000..b34bd4ac
--- /dev/null
+++ b/krdc/vnc.protocol
@@ -0,0 +1,13 @@
+[Protocol]
+exec=krdc '%u'
+protocol=vnc
+input=none
+output=none
+helper=true
+listing=false
+reading=false
+writing=false
+makedir=false
+deleting=false
+Icon=vnc
+
diff --git a/krdc/vnc/Makefile.am b/krdc/vnc/Makefile.am
new file mode 100644
index 00000000..23956297
--- /dev/null
+++ b/krdc/vnc/Makefile.am
@@ -0,0 +1,16 @@
+KDE_CXXFLAGS = $(USE_THREADS)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libvnc.la
+
+libvnc_la_SOURCES = kvncview.cpp threads.cpp colour.c d3des.c desktop.c \
+ rfbproto.c sockets.c vncauth.c vncprefs.ui vnchostpref.cpp
+
+noinst_HEADERS = kvncview.h rfbproto.h vncviewer.h vnctypes.h vncauth.h \
+ pointerlatencyometer.h threads.h d3des.h vnchostpref.h
+
+libvnc_la_LIBADD = $(LIB_KDEUI) $(LIBJPEG)
+libvnc_la_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+INCLUDES= -I$(top_srcdir)/krdc -I.. $(all_includes)
diff --git a/krdc/vnc/colour.c b/krdc/vnc/colour.c
new file mode 100644
index 00000000..a51d6e61
--- /dev/null
+++ b/krdc/vnc/colour.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * colour.c - functions to deal with colour - i.e. RFB pixel formats, X visuals
+ * and colormaps. Thanks to Grant McDorman for some of the ideas used here.
+ */
+
+#include "vncviewer.h"
+#include <limits.h>
+
+
+#define INVALID_PIXEL 0xffffffff
+#define MAX_CMAP_SIZE 256
+#define BGR233_SIZE 256
+unsigned long BGR233ToPixel[BGR233_SIZE];
+
+Colormap cmap;
+Visual *vis;
+unsigned int visdepth, visbpp;
+Bool allocColorFailed = False;
+
+static int nBGR233ColoursAllocated;
+
+static int GetBPPForDepth(int depth);
+static void SetupBGR233Map(void);
+static void AllocateExactBGR233Colours(void);
+static Bool AllocateBGR233Colour(int r, int g, int b);
+
+
+/*
+ * SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are
+ * equivalent to the RFB protocol's "pixel format"). Having decided on the
+ * best visual, it also creates a colormap if necessary, sets the appropriate
+ * resources on the toplevel widget, and sets up the myFormat structure to
+ * describe the pixel format in terms that the RFB server will be able to
+ * understand.
+ *
+ * The algorithm for deciding which visual to use is as follows:
+ *
+ * If forceOwnCmap is true then we try to use a PseudoColor visual - we first
+ * see if there's one of the same depth as the RFB server, followed by an 8-bit
+ * deep one.
+ *
+ * If forceTrueColour is true then we try to use a TrueColor visual - if
+ * requestedDepth is set then it must be of that depth, otherwise any depth
+ * will be used.
+ *
+ * Otherwise, we use the X server's default visual and colormap. If this is
+ * TrueColor then we just ask the RFB server for this format. If the default
+ * isn't TrueColor, or if useBGR233 is true, then we ask the RFB server for
+ * BGR233 pixel format and use a lookup table to translate to the nearest
+ * colours provided by the X server.
+ */
+
+void
+SetVisualAndCmap()
+{
+ /* just use default visual and colormap */
+
+ vis = DefaultVisual(dpy,DefaultScreen(dpy));
+ visdepth = DefaultDepth(dpy,DefaultScreen(dpy));
+ visbpp = GetBPPForDepth(visdepth);
+ cmap = DefaultColormap(dpy,DefaultScreen(dpy));
+
+ if (!appData.useBGR233 && (vis->class == TrueColor)) {
+
+ myFormat.bitsPerPixel = visbpp;
+ myFormat.depth = visdepth;
+ myFormat.trueColour = 1;
+ myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst);
+ myFormat.redShift = ffs(vis->red_mask) - 1;
+ myFormat.greenShift = ffs(vis->green_mask) - 1;
+ myFormat.blueShift = ffs(vis->blue_mask) - 1;
+ myFormat.redMax = vis->red_mask >> myFormat.redShift;
+ myFormat.greenMax = vis->green_mask >> myFormat.greenShift;
+ myFormat.blueMax = vis->blue_mask >> myFormat.blueShift;
+
+ fprintf(stderr,
+ "Using default colormap which is TrueColor. Pixel format:\n");
+ PrintPixelFormat(&myFormat);
+ return;
+ }
+
+ appData.useBGR233 = True;
+
+ myFormat.bitsPerPixel = 8;
+ myFormat.depth = 8;
+ myFormat.trueColour = 1;
+ myFormat.bigEndian = 0;
+ myFormat.redMax = 7;
+ myFormat.greenMax = 7;
+ myFormat.blueMax = 3;
+ myFormat.redShift = 0;
+ myFormat.greenShift = 3;
+ myFormat.blueShift = 6;
+
+ fprintf(stderr,
+ "Using default colormap and translating from BGR233. Pixel format:\n");
+ PrintPixelFormat(&myFormat);
+
+ SetupBGR233Map();
+}
+
+
+/*
+ * GetBPPForDepth looks through the "pixmap formats" to find the bits-per-pixel
+ * for the given depth.
+ */
+
+static int
+GetBPPForDepth(int depth)
+{
+ XPixmapFormatValues *format;
+ int nformats;
+ int i;
+ int bpp;
+
+ format = XListPixmapFormats(dpy, &nformats);
+
+ for (i = 0; i < nformats; i++) {
+ if (format[i].depth == depth)
+ break;
+ }
+
+ if (i == nformats) {
+ fprintf(stderr,"no pixmap format for depth %d???\n", depth);
+ exit(1);
+ }
+
+ bpp = format[i].bits_per_pixel;
+
+ XFree(format);
+
+ if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) {
+ fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp);
+ exit(1);
+ }
+
+ return bpp;
+}
+
+
+
+/*
+ * SetupBGR233Map() sets up the BGR233ToPixel array.
+ *
+ * It calls AllocateExactBGR233Colours to allocate some exact BGR233 colours
+ * (limited by space in the colormap and/or by the value of the nColours
+ * resource). If the number allocated is less than BGR233_SIZE then it fills
+ * the rest in using the "nearest" colours available. How this is done depends
+ * on the value of the useSharedColours resource. If it's false, we use only
+ * colours from the exact BGR233 colours we've just allocated. If it's true,
+ * then we also use other clients' "shared" colours available in the colormap.
+ */
+
+static void
+SetupBGR233Map(void)
+{
+ int r, g, b;
+ long i;
+ unsigned long nearestPixel = 0;
+ int cmapSize;
+ XColor cmapEntry[MAX_CMAP_SIZE];
+ Bool exactBGR233[MAX_CMAP_SIZE];
+ Bool shared[MAX_CMAP_SIZE];
+ Bool usedAsNearest[MAX_CMAP_SIZE];
+ int nSharedUsed = 0;
+
+ if (visdepth > 8) {
+ appData.nColours = 256; /* ignore nColours setting for > 8-bit deep */
+ }
+
+ for (i = 0; i < BGR233_SIZE; i++) {
+ BGR233ToPixel[i] = INVALID_PIXEL;
+ }
+
+ AllocateExactBGR233Colours();
+
+ fprintf(stderr,"Got %d exact BGR233 colours out of %d\n",
+ nBGR233ColoursAllocated, appData.nColours);
+
+ if (nBGR233ColoursAllocated < BGR233_SIZE) {
+
+ if (visdepth > 8) { /* shouldn't get here */
+ fprintf(stderr,"Error: couldn't allocate BGR233 colours even though "
+ "depth is %d\n", visdepth);
+ exit(1);
+ }
+
+ cmapSize = (1 << visdepth);
+
+ for (i = 0; i < cmapSize; i++) {
+ cmapEntry[i].pixel = i;
+ exactBGR233[i] = False;
+ shared[i] = False;
+ usedAsNearest[i] = False;
+ }
+
+ XQueryColors(dpy, cmap, cmapEntry, cmapSize);
+
+ /* mark all our exact BGR233 pixels */
+
+ for (i = 0; i < BGR233_SIZE; i++) {
+ if (BGR233ToPixel[i] != INVALID_PIXEL)
+ exactBGR233[BGR233ToPixel[i]] = True;
+ }
+
+ if (appData.useSharedColours) {
+
+ /* Try to find existing shared colours. This is harder than it sounds
+ because XQueryColors doesn't tell us whether colours are shared,
+ private or unallocated. What we do is go through the colormap and for
+ each pixel try to allocate exactly its RGB values. If this returns a
+ different pixel then it's definitely either a private or unallocated
+ pixel, so no use to us. If it returns us the same pixel again, then
+ it's likely that it's a shared colour - however, it is possible that
+ it was actually an unallocated pixel, which we've now allocated. We
+ minimise this possibility by going through the pixels in reverse order
+ - this helps becuse the X server allocates new pixels from the lowest
+ number up, so it should only be a problem for the lowest unallocated
+ pixel. Got that? */
+
+ for (i = cmapSize-1; i >= 0; i--) {
+ if (!exactBGR233[i] &&
+ XAllocColor(dpy, cmap, &cmapEntry[i])) {
+
+ if (cmapEntry[i].pixel == (unsigned long) i) {
+
+ shared[i] = True; /* probably shared */
+
+ } else {
+
+ /* "i" is either unallocated or private. We have now unnecessarily
+ allocated cmapEntry[i].pixel. Free it. */
+
+ XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0);
+ }
+ }
+ }
+ }
+
+ /* Now fill in the nearest colours */
+
+ for (r = 0; r < 8; r++) {
+ for (g = 0; g < 8; g++) {
+ for (b = 0; b < 4; b++) {
+ if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) {
+
+ unsigned long minDistance = ULONG_MAX;
+
+ for (i = 0; i < cmapSize; i++) {
+ if (exactBGR233[i] || shared[i]) {
+ unsigned long distance
+ = (abs(cmapEntry[i].red - r * 65535 / 7)
+ + abs(cmapEntry[i].green - g * 65535 / 7)
+ + abs(cmapEntry[i].blue - b * 65535 / 3));
+
+ if (distance < minDistance) {
+ minDistance = distance;
+ nearestPixel = i;
+ }
+ }
+ }
+
+ BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel;
+ if (shared[nearestPixel] && !usedAsNearest[nearestPixel])
+ nSharedUsed++;
+ usedAsNearest[nearestPixel] = True;
+ }
+ }
+ }
+ }
+
+ /* Tidy up shared colours which we allocated but aren't going to use */
+
+ for (i = 0; i < cmapSize; i++) {
+ if (shared[i] && !usedAsNearest[i]) {
+ XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0);
+ }
+ }
+
+ fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed);
+ }
+}
+
+
+/*
+ * AllocateExactBGR233Colours() attempts to allocate each of the colours in the
+ * BGR233 colour cube, stopping when an allocation fails. The order it does
+ * this in is such that we should get a fairly well spread subset of the cube,
+ * however many allocations are made. There's probably a neater algorithm for
+ * doing this, but it's not obvious to me anyway. The way this algorithm works
+ * is:
+ *
+ * At each stage, we introduce a new value for one of the primaries, and
+ * allocate all the colours with the new value of that primary and all previous
+ * values of the other two primaries. We start with r=0 as the "new" value
+ * for r, and g=0, b=0 as the "previous" values of g and b. So we get:
+ *
+ * New primary value Previous values of other primaries Colours allocated
+ * ----------------- ---------------------------------- -----------------
+ * r=0 g=0 b=0 r0 g0 b0
+ * g=7 r=0 b=0 r0 g7 b0
+ * b=3 r=0 g=0,7 r0 g0 b3
+ * r0 g7 b3
+ * r=7 g=0,7 b=0,3 r7 g0 b0
+ * r7 g0 b3
+ * r7 g7 b0
+ * r7 g7 b3
+ * g=3 r=0,7 b=0,3 r0 g3 b0
+ * r0 g3 b3
+ * r7 g3 b0
+ * r7 g3 b3
+ * ....etc.
+ * */
+
+static void
+AllocateExactBGR233Colours(void)
+{
+ int rv[] = {0,7,3,5,1,6,2,4};
+ int gv[] = {0,7,3,5,1,6,2,4};
+ int bv[] = {0,3,1,2};
+ int rn = 0;
+ int gn = 1;
+ int bn = 1;
+ int ri, gi, bi;
+
+ nBGR233ColoursAllocated = 0;
+
+ while (1) {
+ if (rn == 8)
+ break;
+
+ ri = rn;
+ for (gi = 0; gi < gn; gi++) {
+ for (bi = 0; bi < bn; bi++) {
+ if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
+ return;
+ }
+ }
+ rn++;
+
+ if (gn == 8)
+ break;
+
+ gi = gn;
+ for (ri = 0; ri < rn; ri++) {
+ for (bi = 0; bi < bn; bi++) {
+ if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
+ return;
+ }
+ }
+ gn++;
+
+ if (bn < 4) {
+
+ bi = bn;
+ for (ri = 0; ri < rn; ri++) {
+ for (gi = 0; gi < gn; gi++) {
+ if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi]))
+ return;
+ }
+ }
+ bn++;
+ }
+ }
+}
+
+
+/*
+ * AllocateBGR233Colour() attempts to allocate the given BGR233 colour as a
+ * shared colormap entry, storing its pixel value in the BGR233ToPixel array.
+ * r is from 0 to 7, g from 0 to 7 and b from 0 to 3. It fails either when the
+ * allocation fails or when we would exceed the number of colours specified in
+ * the nColours resource.
+ */
+
+static Bool
+AllocateBGR233Colour(int r, int g, int b)
+{
+ XColor c;
+
+ if (nBGR233ColoursAllocated >= appData.nColours)
+ return False;
+
+ c.red = r * 65535 / 7;
+ c.green = g * 65535 / 7;
+ c.blue = b * 65535 / 3;
+
+ if (!XAllocColor(dpy, cmap, &c))
+ return False;
+
+ BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel;
+
+ nBGR233ColoursAllocated++;
+
+ return True;
+}
diff --git a/krdc/vnc/d3des.c b/krdc/vnc/d3des.c
new file mode 100644
index 00000000..8a358ce6
--- /dev/null
+++ b/krdc/vnc/d3des.c
@@ -0,0 +1,440 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC. Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+static unsigned long KnR[32] = { 0L };
+static unsigned long Kn3[32] = { 0L };
+static unsigned char Df_Key[24] = {
+ 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+ 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+ 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 };
+
+static unsigned short bytebit[8] = {
+ 01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+ 0x800000L, 0x400000L, 0x200000L, 0x100000L,
+ 0x80000L, 0x40000L, 0x20000L, 0x10000L,
+ 0x8000L, 0x4000L, 0x2000L, 0x1000L,
+ 0x800L, 0x400L, 0x200L, 0x100L,
+ 0x80L, 0x40L, 0x20L, 0x10L,
+ 0x8L, 0x4L, 0x2L, 0x1L };
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
+
+static unsigned char totrot[16] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+ register int i, j, l, m, n;
+ unsigned char pc1m[56], pcr[56];
+ unsigned long kn[32];
+
+ for ( j = 0; j < 56; j++ ) {
+ l = pc1[j];
+ m = l & 07;
+ pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+ }
+ for( i = 0; i < 16; i++ ) {
+ if( edf == DE1 ) m = (15 - i) << 1;
+ else m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for( j = 0; j < 28; j++ ) {
+ l = j + totrot[i];
+ if( l < 28 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 28; j < 56; j++ ) {
+ l = j + totrot[i];
+ if( l < 56 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 0; j < 24; j++ ) {
+ if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+ if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+ }
+ }
+ cookey(kn);
+ return;
+ }
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+ register unsigned long *cook, *raw0;
+ unsigned long dough[32];
+ register int i;
+
+ cook = dough;
+ for( i = 0; i < 16; i++, raw1++ ) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+ usekey(dough);
+ return;
+ }
+
+void cpkey(into)
+register unsigned long *into;
+{
+ register unsigned long *from, *endp;
+
+ from = KnL, endp = &KnL[32];
+ while( from < endp ) *into++ = *from++;
+ return;
+ }
+
+void usekey(from)
+register unsigned long *from;
+{
+ register unsigned long *to, *endp;
+
+ to = KnL, endp = &KnL[32];
+ while( to < endp ) *to++ = *from++;
+ return;
+ }
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+ unsigned long work[2];
+
+ scrunch(inblock, work);
+ desfunc(work, KnL);
+ unscrun(work, outblock);
+ return;
+ }
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into++ |= (*outof++ & 0xffL);
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into |= (*outof & 0xffL);
+ return;
+ }
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+ *into++ = (*outof >> 24) & 0xffL;
+ *into++ = (*outof >> 16) & 0xffL;
+ *into++ = (*outof >> 8) & 0xffL;
+ *into++ = *outof++ & 0xffL;
+ *into++ = (*outof >> 24) & 0xffL;
+ *into++ = (*outof >> 16) & 0xffL;
+ *into++ = (*outof >> 8) & 0xffL;
+ *into = *outof & 0xffL;
+ return;
+ }
+
+static unsigned long SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+ register unsigned long fval, work, right, leftt;
+ register int round;
+
+ leftt = block[0];
+ right = block[1];
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ leftt ^= fval;
+ work = (leftt << 28) | (leftt >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = (leftt << 31) | (leftt >> 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+ *block++ = right;
+ *block = leftt;
+ return;
+ }
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/krdc/vnc/d3des.h b/krdc/vnc/d3des.h
new file mode 100644
index 00000000..ea3da44c
--- /dev/null
+++ b/krdc/vnc/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* d3des.h -
+ *
+ * Headers and defines for d3des.c
+ * Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ * (GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0 0 /* MODE == encrypt */
+#define DE1 1 /* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/* hexkey[8] MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/* cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/* cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/* from[8] to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'. They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/krdc/vnc/desktop.c b/krdc/vnc/desktop.c
new file mode 100644
index 00000000..f5a60966
--- /dev/null
+++ b/krdc/vnc/desktop.c
@@ -0,0 +1,1613 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ * Copyright (C) 2002 Tim Jansen. All Rights Reserved.
+ * Copyright (C) 1999-2001 Anders Lindström
+ *
+ *
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * tim@tjansen.de: - removed stuff for krdc
+ * - merged with shm.c and misc.c
+ * - added FillRectangle and Sync methods to draw only on
+ * the image
+ * - added Zoom functionality, based on rotation funcs from
+ * SGE by Anders Lindström)
+ * - added support for softcursor encoding
+ *
+ */
+
+/*
+ * desktop.c - functions to deal with "desktop" window.
+ */
+
+#include <X11/Xlib.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <math.h>
+#include <limits.h>
+#include "vncviewer.h"
+
+static XShmSegmentInfo shminfo;
+static Bool caughtShmError = False;
+static Bool needShmCleanup = False;
+
+static XShmSegmentInfo zoomshminfo;
+static Bool caughtZoomShmError = False;
+static Bool needZoomShmCleanup = False;
+
+static Bool gcInited = False;
+GC gc;
+GC srcGC, dstGC; /* used for debugging copyrect */
+Dimension dpyWidth, dpyHeight;
+
+static XImage *image = NULL;
+Bool useShm = True;
+
+static Bool zoomActive = False;
+static int zoomWidth, zoomHeight;
+static XImage *zoomImage = NULL;
+static Bool useZoomShm = True;
+
+/* for softcursor */
+static char *savedArea = NULL;
+
+typedef enum {
+ SOFTCURSOR_UNDER,
+ SOFTCURSOR_PART_UNDER,
+ SOFTCURSOR_UNAFFECTED
+} SoftCursorState;
+
+typedef int Sint32;
+typedef short Sint16;
+typedef char Sint8;
+typedef unsigned int Uint32;
+typedef unsigned short Uint16;
+typedef unsigned char Uint8;
+
+typedef struct {
+ int w, h;
+ unsigned int pitch;
+ void *pixels;
+ int BytesPerPixel;
+} Surface;
+
+typedef struct {
+ Sint16 x, y;
+ Uint16 w, h;
+} Rect;
+
+static void bgr233cpy(CARD8 *dst, CARD8 *src, int len);
+static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height);
+static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height);
+static void FillRectangleBGR233(CARD8 buf, int x, int y, int width,int height);
+static int CheckRectangle(int x, int y, int width, int height);
+static SoftCursorState getSoftCursorState(int x, int y, int width, int height);
+static void discardCursorSavedArea(void);
+static void saveCursorSavedArea(void);
+
+static void ZoomInit(void);
+static void transformZoomSrc(int six, int siy, int siw, int sih,
+ int *dix, int *diy, int *diw, int *dih,
+ int srcW, int dstW, int srcH, int dstH);
+static void transformZoomDst(int *six, int *siy, int *siw, int *sih,
+ int dix, int diy, int diw, int dih,
+ int srcW, int dstW, int srcH, int dstH);
+static void ZoomSurfaceSrcCoords(int x, int y, int w, int h,
+ int *dix, int *diy, int *diw, int *dih,
+ Surface * src, Surface * dst);
+static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh,
+ int dx, int dy, Surface * src, Surface * dst);
+static void sge_transform(Surface *src, Surface *dst, float xscale, float yscale,
+ Uint16 qx, Uint16 qy);
+
+
+void
+DesktopInit(Window win)
+{
+ XGCValues gcv;
+
+ image = CreateShmImage();
+
+ if (!image) {
+ useShm = False;
+ image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL,
+ si.framebufferWidth, si.framebufferHeight,
+ BitmapPad(dpy), 0);
+
+ image->data = calloc(image->bytes_per_line * image->height, 1);
+ if (!image->data) {
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ }
+
+ gc = XCreateGC(dpy,win,0,NULL);
+
+ gcv.function = GXxor;
+ gcv.foreground = 0x0f0f0f0f;
+ srcGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv);
+ gcv.foreground = 0xf0f0f0f0;
+ dstGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv);
+ gcInited = True;
+}
+
+/*
+ * DrawScreenRegionX11Thread
+ * Never call from any other desktop.c function, only for X11 thread
+ */
+
+void
+DrawScreenRegionX11Thread(Window win, int x, int y, int width, int height) {
+ zoomActive = False;
+ zoomWidth = 0;
+ zoomHeight = 0;
+
+ if (!image)
+ return;
+
+ if (useShm)
+ XShmPutImage(dpy, win, gc, image, x, y, x, y, width, height, False);
+ else
+ XPutImage(dpy, win, gc, image, x, y, x, y, width, height);
+}
+
+/*
+ * CheckRectangle
+ */
+
+static int CheckRectangle(int x, int y, int width, int height) {
+ if ((x < 0) || (y < 0))
+ return 0;
+
+ if (((x+width) > si.framebufferWidth) || ((y+height) > si.framebufferHeight))
+ return 0;
+
+ return 1;
+}
+
+static
+void bgr233cpy(CARD8 *dst, CARD8 *src, int len) {
+ int i;
+ CARD16 *d16;
+ CARD32 *d32;
+
+ switch (visbpp) {
+ case 8:
+ for (i = 0; i < len; i++)
+ *(dst++) = (CARD8) BGR233ToPixel[*(src++)];
+ break;
+ case 16:
+ d16 = (CARD16*) dst;
+ for (i = 0; i < len; i++)
+ *(d16++) = (CARD16) BGR233ToPixel[*(src++)];
+ break;
+ case 32:
+ d32 = (CARD32*) dst;
+ for (i = 0; i < len; i++)
+ *(d32++) = (CARD32) BGR233ToPixel[*(src++)];
+ break;
+ default:
+ fprintf(stderr, "Unsupported softcursor depth %d\n", visbpp);
+ }
+}
+
+
+/*
+ * CopyDataToScreen.
+ */
+
+void
+CopyDataToScreen(char *buf, int x, int y, int width, int height)
+{
+ SoftCursorState s;
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ LockFramebuffer();
+ s = getSoftCursorState(x, y, width, height);
+ if (s == SOFTCURSOR_PART_UNDER)
+ undrawCursor();
+
+ if (!appData.useBGR233)
+ CopyDataToScreenRaw(buf, x, y, width, height);
+ else
+ CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height);
+
+ if (s != SOFTCURSOR_UNAFFECTED)
+ drawCursor();
+
+ UnlockFramebuffer();
+ SyncScreenRegion(x, y, width, height);
+}
+
+/*
+ * CopyDataToScreenRaw.
+ */
+
+static void
+CopyDataToScreenRaw(char *buf, int x, int y, int width, int height)
+{
+ int h;
+ int widthInBytes = width * visbpp / 8;
+ int scrWidthInBytes = image->bytes_per_line;
+ char *scr = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+
+ for (h = 0; h < height; h++) {
+ memcpy(scr, buf, widthInBytes);
+ buf += widthInBytes;
+ scr += scrWidthInBytes;
+ }
+}
+
+/*
+ * CopyBGR233ToScreen.
+ */
+
+static void
+CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height)
+{
+ int p, q;
+ int xoff = 7 - (x & 7);
+ int xcur;
+ int fbwb = si.framebufferWidth / 8;
+ CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8;
+ CARD8 *scrt;
+ CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x;
+ CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x;
+ CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x;
+
+ switch (visbpp) {
+
+ /* thanks to Chris Hooper for single bpp support */
+
+ case 1:
+ for (q = 0; q < height; q++) {
+ xcur = xoff;
+ scrt = scr1;
+ for (p = 0; p < width; p++) {
+ *scrt = ((*scrt & ~(1 << xcur))
+ | (BGR233ToPixel[*(buf++)] << xcur));
+
+ if (xcur-- == 0) {
+ xcur = 7;
+ scrt++;
+ }
+ }
+ scr1 += fbwb;
+ }
+ break;
+
+ case 8:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr8++) = BGR233ToPixel[*(buf++)];
+ }
+ scr8 += si.framebufferWidth - width;
+ }
+ break;
+
+ case 16:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr16++) = BGR233ToPixel[*(buf++)];
+ }
+ scr16 += si.framebufferWidth - width;
+ }
+ break;
+
+ case 32:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr32++) = BGR233ToPixel[*(buf++)];
+ }
+ scr32 += si.framebufferWidth - width;
+ }
+ break;
+ }
+}
+
+/*
+ * FillRectangle8.
+ */
+
+void
+FillRectangle8(CARD8 fg, int x, int y, int width, int height)
+{
+ SoftCursorState s;
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ s = getSoftCursorState(x, y, width, height);
+ if (s == SOFTCURSOR_PART_UNDER)
+ undrawCursor();
+
+ if (!appData.useBGR233) {
+ int h;
+ int widthInBytes = width * visbpp / 8;
+ int scrWidthInBytes = image->bytes_per_line;
+
+ char *scr = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+
+ for (h = 0; h < height; h++) {
+ memset(scr, fg, widthInBytes);
+ scr += scrWidthInBytes;
+ }
+ } else {
+ FillRectangleBGR233(fg, x, y, width, height);
+ }
+
+ if (s != SOFTCURSOR_UNAFFECTED)
+ drawCursor();
+}
+
+/*
+ * FillRectangleBGR233.
+ */
+
+static void
+FillRectangleBGR233(CARD8 fg, int x, int y, int width, int height)
+{
+ int p, q;
+ int xoff = 7 - (x & 7);
+ int xcur;
+ int fbwb = si.framebufferWidth / 8;
+ CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8;
+ CARD8 *scrt;
+ CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x;
+ CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x;
+ CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x;
+
+ unsigned long fg233 = BGR233ToPixel[fg];
+
+ switch (visbpp) {
+
+ /* thanks to Chris Hooper for single bpp support */
+
+ case 1:
+ for (q = 0; q < height; q++) {
+ xcur = xoff;
+ scrt = scr1;
+ for (p = 0; p < width; p++) {
+ *scrt = ((*scrt & ~(1 << xcur))
+ | (fg233 << xcur));
+
+ if (xcur-- == 0) {
+ xcur = 7;
+ scrt++;
+ }
+ }
+ scr1 += fbwb;
+ }
+ break;
+
+ case 8:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr8++) = fg233;
+ }
+ scr8 += si.framebufferWidth - width;
+ }
+ break;
+
+ case 16:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr16++) = fg233;
+ }
+ scr16 += si.framebufferWidth - width;
+ }
+ break;
+
+ case 32:
+ for (q = 0; q < height; q++) {
+ for (p = 0; p < width; p++) {
+ *(scr32++) = fg233;
+ }
+ scr32 += si.framebufferWidth - width;
+ }
+ break;
+ }
+}
+
+/*
+ * FillRectangle16
+ */
+
+void
+FillRectangle16(CARD16 fg, int x, int y, int width, int height)
+{
+ int i, h;
+ int scrWidthInBytes = image->bytes_per_line;
+
+ char *scr = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+ CARD16 *scr16;
+ SoftCursorState s;
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ s = getSoftCursorState(x, y, width, height);
+ if (s == SOFTCURSOR_PART_UNDER)
+ undrawCursor();
+
+ for (h = 0; h < height; h++) {
+ scr16 = (CARD16*) scr;
+ for (i = 0; i < width; i++)
+ scr16[i] = fg;
+ scr += scrWidthInBytes;
+ }
+
+ if (s != SOFTCURSOR_UNAFFECTED)
+ drawCursor();
+}
+
+/*
+ * FillRectangle32
+ */
+
+void
+FillRectangle32(CARD32 fg, int x, int y, int width, int height)
+{
+ int i, h;
+ int scrWidthInBytes = image->bytes_per_line;
+ SoftCursorState s;
+
+ char *scr = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+ CARD32 *scr32;
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ s = getSoftCursorState(x, y, width, height);
+ if (s == SOFTCURSOR_PART_UNDER)
+ undrawCursor();
+
+ for (h = 0; h < height; h++) {
+ scr32 = (CARD32*) scr;
+ for (i = 0; i < width; i++)
+ scr32[i] = fg;
+ scr += scrWidthInBytes;
+ }
+
+ if (s != SOFTCURSOR_UNAFFECTED)
+ drawCursor();
+}
+
+/*
+ * CopyDataFromScreen.
+ */
+
+void
+CopyDataFromScreen(char *buf, int x, int y, int width, int height)
+{
+ int widthInBytes = width * visbpp / 8;
+ int scrWidthInBytes = image->bytes_per_line;
+ char *src = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+ int h;
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ for (h = 0; h < height; h++) {
+ memcpy(buf, src, widthInBytes);
+ src += scrWidthInBytes;
+ buf += widthInBytes;
+ }
+}
+
+/*
+ * CopyArea
+ */
+
+void
+CopyArea(int srcX, int srcY, int width, int height, int x, int y)
+{
+ int widthInBytes = width * visbpp / 8;
+ SoftCursorState sSrc, sDst;
+
+ LockFramebuffer();
+ sSrc = getSoftCursorState(srcX, srcY, width, height);
+ sDst = getSoftCursorState(x, y, width, height);
+ if ((sSrc != SOFTCURSOR_UNAFFECTED) ||
+ (sDst == SOFTCURSOR_PART_UNDER))
+ undrawCursor();
+
+ if ((srcY+height < y) || (y+height < srcY) ||
+ (srcX+width < x) || (x+width < srcX)) {
+
+ int scrWidthInBytes = image->bytes_per_line;
+ char *src = (image->data + srcY * scrWidthInBytes
+ + srcX * visbpp / 8);
+ char *dst = (image->data + y * scrWidthInBytes
+ + x * visbpp / 8);
+ int h;
+
+ if (!CheckRectangle(srcX, srcY, width, height)) {
+ UnlockFramebuffer();
+ return;
+ }
+ if (!CheckRectangle(x, y, width, height)) {
+ UnlockFramebuffer();
+ return;
+ }
+
+ for (h = 0; h < height; h++) {
+ memcpy(dst, src, widthInBytes);
+ src += scrWidthInBytes;
+ dst += scrWidthInBytes;
+ }
+ }
+ else {
+ char *buf = malloc(widthInBytes*height);
+ if (!buf) {
+ UnlockFramebuffer();
+ fprintf(stderr, "Out of memory, CopyArea impossible\n");
+ return;
+ }
+ CopyDataFromScreen(buf, srcX, srcY, width, height);
+ CopyDataToScreenRaw(buf, x, y, width, height);
+ free(buf);
+ }
+ if ((sSrc != SOFTCURSOR_UNAFFECTED) ||
+ (sDst != SOFTCURSOR_UNAFFECTED))
+ drawCursor();
+ UnlockFramebuffer();
+ SyncScreenRegion(x, y, width, height);
+}
+
+void SyncScreenRegion(int x, int y, int width, int height) {
+ int dx, dy, dw, dh;
+
+ if (zoomActive) {
+ Surface src, dest;
+ src.w = si.framebufferWidth;
+ src.h = si.framebufferHeight;
+ src.pitch = image->bytes_per_line;
+ src.pixels = image->data;
+ src.BytesPerPixel = visbpp / 8;
+ dest.w = zoomWidth;
+ dest.h = zoomHeight;
+ dest.pitch = zoomImage->bytes_per_line;
+ dest.pixels = zoomImage->data;
+ dest.BytesPerPixel = visbpp / 8;
+ ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest);
+ }
+ else {
+ dx = x; dy = y;
+ dw = width; dh = height;
+ }
+ DrawScreenRegion(dx, dy, dw, dh);
+}
+
+void SyncScreenRegionX11Thread(int x, int y, int width, int height) {
+ int dx, dy, dw, dh;
+
+ if (zoomActive) {
+ Surface src, dest;
+ src.w = si.framebufferWidth;
+ src.h = si.framebufferHeight;
+ src.pitch = image->bytes_per_line;
+ src.pixels = image->data;
+ src.BytesPerPixel = visbpp / 8;
+ dest.w = zoomWidth;
+ dest.h = zoomHeight;
+ dest.pitch = zoomImage->bytes_per_line;
+ dest.pixels = zoomImage->data;
+ dest.BytesPerPixel = visbpp / 8;
+ ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest);
+ }
+ else {
+ dx = x; dy = y;
+ dw = width; dh = height;
+ }
+ DrawAnyScreenRegionX11Thread(dx, dy, dw, dh);
+}
+
+/*
+ * ToplevelInitBeforeRealization sets the title, geometry and other resources
+ * on the toplevel window.
+ */
+
+void
+ToplevelInit()
+{
+ dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy));
+ dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy));
+}
+
+/*
+ * Cleanup - perform shm cleanup operations prior to exiting.
+ */
+
+void
+Cleanup()
+{
+ if (useShm || useZoomShm)
+ ShmCleanup();
+}
+
+void
+ShmCleanup()
+{
+ fprintf(stderr,"ShmCleanup called\n");
+ if (needShmCleanup) {
+ shmdt(shminfo.shmaddr);
+ shmctl(shminfo.shmid, IPC_RMID, 0);
+ needShmCleanup = False;
+ }
+ if (needZoomShmCleanup) {
+ shmdt(zoomshminfo.shmaddr);
+ shmctl(zoomshminfo.shmid, IPC_RMID, 0);
+ needZoomShmCleanup = False;
+ }
+}
+
+static int
+ShmCreationXErrorHandler(Display *d, XErrorEvent *e)
+{
+ caughtShmError = True;
+ return 0;
+}
+
+XImage *
+CreateShmImage()
+{
+ XImage *_image;
+ XErrorHandler oldXErrorHandler;
+
+ if (!XShmQueryExtension(dpy))
+ return NULL;
+
+ _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo,
+ si.framebufferWidth, si.framebufferHeight);
+ if (!_image) return NULL;
+
+ shminfo.shmid = shmget(IPC_PRIVATE,
+ _image->bytes_per_line * _image->height,
+ IPC_CREAT|0777);
+
+ if (shminfo.shmid == -1) {
+ XDestroyImage(_image);
+ return NULL;
+ }
+
+ shminfo.shmaddr = _image->data = shmat(shminfo.shmid, 0, 0);
+
+ if (shminfo.shmaddr == (char *)-1) {
+ XDestroyImage(_image);
+ shmctl(shminfo.shmid, IPC_RMID, 0);
+ return NULL;
+ }
+
+ shminfo.readOnly = True;
+
+ oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler);
+ XShmAttach(dpy, &shminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldXErrorHandler);
+
+ if (caughtShmError) {
+ XDestroyImage(_image);
+ shmdt(shminfo.shmaddr);
+ shmctl(shminfo.shmid, IPC_RMID, 0);
+ return NULL;
+ }
+
+ needShmCleanup = True;
+
+ fprintf(stderr,"Using shared memory PutImage\n");
+
+ return _image;
+}
+
+void undrawCursor() {
+ int x, y, w, h;
+
+ if ((imageIndex < 0) || !savedArea)
+ return;
+
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &x, &y, &w, &h);
+
+ if ((w < 1) || (h < 1))
+ return;
+
+ CopyDataToScreenRaw(savedArea, x, y, w, h);
+ discardCursorSavedArea();
+}
+
+static void drawCursorImage() {
+ int x, y, w, h, pw, pixelsLeft, processingMask;
+ int skipLeft, skipRight;
+ PointerImage *pi = &pointerImages[imageIndex];
+ CARD8 *img = (CARD8*) pi->image;
+ CARD8 *imgEnd = &img[pi->len];
+ CARD8 *fb;
+
+ /* check whether the source image has ended (image broken) */
+#define CHECK_IMG(x) if (&img[x] > imgEnd) goto imgError
+
+/* check whether the end of the framebuffer has been reached (last line) */
+#define CHECK_END() if ((wl == 0) && (h == 1)) return
+
+/* skip x pixels in the source (x must be < pixelsLeft!) */
+#define SKIP_IMG(x) if ((x > 0) && !processingMask) { \
+ CHECK_END(); \
+ img += pw * x; \
+ CHECK_IMG(0); \
+ }
+
+/* skip x pixels in source and destination */
+#define SKIP_PIXELS(x) { int wl = x; \
+ while (pixelsLeft <= wl) { \
+ wl -= pixelsLeft; \
+ SKIP_IMG(pixelsLeft); \
+ CHECK_END(); \
+ pixelsLeft = *(img++); \
+ CHECK_IMG(0); \
+ processingMask = processingMask ? 0 : 1; \
+ } \
+ pixelsLeft -= wl; \
+ SKIP_IMG(wl); \
+ }
+
+ if (!img)
+ return;
+
+ x = cursorX - pi->hotX;
+ y = cursorY - pi->hotY;
+ w = pi->w;
+ h = pi->h;
+
+ if (!rectsIntersect(x, y, w, h,
+ 0, 0, si.framebufferWidth, si.framebufferHeight)) {
+ fprintf(stderr, "intersect abort\n");
+ return;
+ }
+
+ pw = myFormat.bitsPerPixel / 8;
+ processingMask = 1;
+ pixelsLeft = *(img++);
+
+/* at this point everything is initialized for the macros */
+
+ /* skip/clip bottom lines */
+ if ((y+h) > si.framebufferHeight)
+ h = si.framebufferHeight - y;
+
+ /* Skip invisible top lines */
+ while (y < 0) {
+ SKIP_PIXELS(w);
+ y++;
+ h--;
+ }
+
+ /* calculate left/right clipping */
+ if (x < 0) {
+ skipLeft = -x;
+ w += x;
+ x = 0;
+ }
+ else
+ skipLeft = 0;
+
+ if ((x+w) > si.framebufferWidth) {
+ skipRight = (x+w) - si.framebufferWidth;
+ w = si.framebufferWidth - x;
+ }
+ else
+ skipRight = 0;
+
+ fb = (CARD8*) image->data + y * image->bytes_per_line + x * visbpp / 8;
+
+ /* Paint the thing */
+ while (h > 0) {
+ SKIP_PIXELS(skipLeft);
+
+ {
+ CARD8 *fbx = fb;
+ int wl = w;
+ while (pixelsLeft <= wl) {
+ wl -= pixelsLeft;
+ if ((pixelsLeft > 0) && !processingMask) {
+ int pl = pw * pixelsLeft;
+ CHECK_IMG(pl);
+ if (!appData.useBGR233)
+ memcpy(fbx, img, pl);
+ else
+ bgr233cpy(fbx, img, pixelsLeft);
+ img += pl;
+ }
+
+ CHECK_END();
+ fbx += pixelsLeft * visbpp / 8;
+ pixelsLeft = *(img++);
+
+ CHECK_IMG(0);
+ processingMask = processingMask ? 0 : 1;
+ }
+ pixelsLeft -= wl;
+ if ((wl > 0) && !processingMask) {
+ int pl = pw * wl;
+ CHECK_IMG(pl);
+ if (!appData.useBGR233)
+ memcpy(fbx, img, pl);
+ else
+ bgr233cpy(fbx, img, wl);
+ img += pl;
+ }
+ }
+
+ SKIP_PIXELS(skipRight);
+ fb += image->bytes_per_line;
+ h--;
+ }
+ return;
+
+imgError:
+ fprintf(stderr, "Error in softcursor image %d\n", imageIndex);
+ pointerImages[imageIndex].set = 0;
+}
+
+static void discardCursorSavedArea() {
+ if (savedArea)
+ free(savedArea);
+ savedArea = 0;
+}
+
+static void saveCursorSavedArea() {
+ int x, y, w, h;
+
+ if (imageIndex < 0)
+ return;
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &x, &y, &w, &h);
+ if ((w < 1) || (h < 1))
+ return;
+ discardCursorSavedArea();
+ savedArea = malloc(h*image->bytes_per_line);
+ if (!savedArea) {
+ fprintf(stderr,"malloc failed, saving cursor not possible\n");
+ exit(1);
+ }
+ CopyDataFromScreen(savedArea, x, y, w, h);
+}
+
+void drawCursor() {
+ saveCursorSavedArea();
+ drawCursorImage();
+}
+
+void getBoundingRectCursor(int cx, int cy, int _imageIndex,
+ int *x, int *y, int *w, int *h) {
+ int nx, ny, nw, nh;
+
+ if ((_imageIndex < 0) || !pointerImages[_imageIndex].set) {
+ *x = 0;
+ *y = 0;
+ *w = 0;
+ *h = 0;
+ return;
+ }
+
+ nx = cx - pointerImages[_imageIndex].hotX;
+ ny = cy - pointerImages[_imageIndex].hotY;
+ nw = pointerImages[_imageIndex].w;
+ nh = pointerImages[_imageIndex].h;
+ if (nx < 0) {
+ nw += nx;
+ nx = 0;
+ }
+ if (ny < 0) {
+ nh += ny;
+ ny = 0;
+ }
+ if ((nx+nw) > si.framebufferWidth)
+ nw = si.framebufferWidth - nx;
+ if ((ny+nh) > si.framebufferHeight)
+ nh = si.framebufferHeight - ny;
+ if ((nw <= 0) || (nh <= 0)) {
+ *x = 0;
+ *y = 0;
+ *w = 0;
+ *h = 0;
+ return;
+ }
+
+ *x = nx;
+ *y = ny;
+ *w = nw;
+ *h = nh;
+}
+
+static SoftCursorState getSoftCursorState(int x, int y, int w, int h) {
+ int cx, cy, cw, ch;
+
+ if (imageIndex < 0)
+ return SOFTCURSOR_UNAFFECTED;
+
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &cx, &cy, &cw, &ch);
+
+ if ((cw == 0) || (ch == 0))
+ return SOFTCURSOR_UNAFFECTED;
+
+ if (!rectsIntersect(x, y, w, h, cx, cy, cw, ch))
+ return SOFTCURSOR_UNAFFECTED;
+ if (rectContains(x, y, w, h, cx, cy, cw, ch))
+ return SOFTCURSOR_UNDER;
+ else
+ return SOFTCURSOR_PART_UNDER;
+}
+
+int rectsIntersect(int x, int y, int w, int h,
+ int x2, int y2, int w2, int h2) {
+ if (x2 >= (x+w))
+ return 0;
+ if (y2 >= (y+h))
+ return 0;
+ if ((x2+w2) <= x)
+ return 0;
+ if ((y2+h2) <= y)
+ return 0;
+ return 1;
+}
+
+int rectContains(int outX, int outY, int outW, int outH,
+ int inX, int inY, int inW, int inH) {
+ if (inX < outX)
+ return 0;
+ if (inY < outY)
+ return 0;
+ if ((inX+inW) > (outX+outW))
+ return 0;
+ if ((inY+inH) > (outY+outH))
+ return 0;
+ return 1;
+}
+
+void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1,
+ int x2, int y2, int w2, int h2) {
+ int ox, oy, ow, oh;
+ ox = *nx1;
+ oy = *ny1;
+ ow = *nw1;
+ oh = *nh1;
+
+ if (x2 < ox) {
+ ow += ox - x2;
+ ox = x2;
+ }
+ if (y2 < oy) {
+ oh += oy - y2;
+ oy = y2;
+ }
+ if ((x2+w2) > (ox+ow))
+ ow = (x2+w2) - ox;
+ if ((y2+h2) > (oy+oh))
+ oh = (y2+h2) - oy;
+
+ *nx1 = ox;
+ *ny1 = oy;
+ *nw1 = ow;
+ *nh1 = oh;
+}
+
+XImage *
+CreateShmZoomImage()
+{
+ XImage *_image;
+ XErrorHandler oldXErrorHandler;
+
+ if (!XShmQueryExtension(dpy))
+ return NULL;
+
+ _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &zoomshminfo,
+ si.framebufferWidth, si.framebufferHeight);
+ if (!_image) return NULL;
+
+ zoomshminfo.shmid = shmget(IPC_PRIVATE,
+ _image->bytes_per_line * _image->height,
+ IPC_CREAT|0777);
+
+ if (zoomshminfo.shmid == -1) {
+ XDestroyImage(_image);
+ return NULL;
+ }
+
+ zoomshminfo.shmaddr = _image->data = shmat(zoomshminfo.shmid, 0, 0);
+
+ if (zoomshminfo.shmaddr == (char *)-1) {
+ XDestroyImage(_image);
+ shmctl(zoomshminfo.shmid, IPC_RMID, 0);
+ return NULL;
+ }
+
+ zoomshminfo.readOnly = True;
+
+ oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler);
+ XShmAttach(dpy, &zoomshminfo);
+ XSync(dpy, False);
+ XSetErrorHandler(oldXErrorHandler);
+
+ if (caughtZoomShmError) {
+ XDestroyImage(_image);
+ shmdt(zoomshminfo.shmaddr);
+ shmctl(zoomshminfo.shmid, IPC_RMID, 0);
+ return NULL;
+ }
+
+ needZoomShmCleanup = True;
+
+ fprintf(stderr,"Using shared memory PutImage\n");
+
+ return _image;
+}
+
+
+/*
+ * DrawZoomedScreenRegionX11Thread
+ * Never call from any other desktop.c function, only for X11 thread
+ */
+
+void
+DrawZoomedScreenRegionX11Thread(Window win, int zwidth, int zheight,
+ int x, int y, int width, int height) {
+ if (!image)
+ return;
+
+ if (zwidth > si.framebufferWidth)
+ zwidth = si.framebufferWidth;
+ if (zheight > si.framebufferHeight)
+ zheight = si.framebufferHeight;
+
+ if (!zoomActive) {
+ ZoomInit();
+ zoomActive = True;
+ }
+
+ if ((zoomWidth != zwidth) || (zoomHeight != zheight)) {
+ Surface src, dest;
+
+ zoomWidth = zwidth;
+ zoomHeight = zheight;
+
+ src.w = si.framebufferWidth;
+ src.h = si.framebufferHeight;
+ src.pitch = image->bytes_per_line;
+ src.pixels = image->data;
+ src.BytesPerPixel = visbpp / 8;
+ dest.w = zwidth;
+ dest.h = zheight;
+ dest.pitch = zoomImage->bytes_per_line;
+ dest.pixels = zoomImage->data;
+ dest.BytesPerPixel = visbpp / 8;
+ sge_transform(&src, &dest,
+ (float)dest.w/(float)src.w, (float)dest.h/(float)src.h,
+ 0, 0);
+
+ if (useZoomShm)
+ XShmPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight, False);
+ else
+ XPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight);
+ return;
+ }
+
+ if (useZoomShm)
+ XShmPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height, False);
+ else
+ XPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height);
+}
+
+
+static void
+ZoomInit()
+{
+ if (zoomImage)
+ return;
+
+ zoomImage = CreateShmZoomImage();
+
+ if (!zoomImage) {
+ useZoomShm = False;
+ zoomImage = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL,
+ si.framebufferWidth, si.framebufferHeight,
+ BitmapPad(dpy), 0);
+
+ zoomImage->data = calloc(zoomImage->bytes_per_line * zoomImage->height, 1);
+ if (!zoomImage->data) {
+ fprintf(stderr,"malloc failed\n");
+ exit(1);
+ }
+ }
+}
+
+static void transformZoomSrc(int six, int siy, int siw, int sih,
+ int *dix, int *diy, int *diw, int *dih,
+ int srcW, int dstW, int srcH, int dstH) {
+ double sx, sy, sw, sh;
+ double dx, dy, dw, dh;
+ double wq, hq;
+
+ sx = six; sy = siy;
+ sw = siw; sh = sih;
+
+ wq = ((double)dstW) / (double) srcW;
+ hq = ((double)dstH) / (double) srcH;
+
+ dx = sx * wq;
+ dy = sy * hq;
+ dw = sw * wq;
+ dh = sh * hq;
+
+ *dix = dx;
+ *diy = dy;
+ *diw = dw+(dx-(int)dx)+0.5;
+ *dih = dh+(dy-(int)dy)+0.5;
+}
+
+static void transformZoomDst(int *six, int *siy, int *siw, int *sih,
+ int dix, int diy, int diw, int dih,
+ int srcW, int dstW, int srcH, int dstH) {
+ double sx, sy, sw, sh;
+ double dx, dy, dw, dh;
+ double wq, hq;
+
+ dx = dix; dy = diy;
+ dw = diw; dh = dih;
+
+ wq = ((double)dstW) / (double) srcW;
+ hq = ((double)dstH) / (double) srcH;
+
+ sx = dx / wq;
+ sy = dy / hq;
+ sw = dw / wq;
+ sh = dh / hq;
+
+ *six = sx;
+ *siy = sy;
+ *siw = sw+(sx-(int)sx)+0.5;
+ *sih = sh+(sy-(int)sy)+0.5;
+}
+
+
+static void ZoomSurfaceSrcCoords(int six, int siy, int siw, int sih,
+ int *dix, int *diy, int *diw, int *dih,
+ Surface * src, Surface * dst)
+{
+ int dx, dy, dw, dh;
+ int sx, sy, sw, sh;
+
+ transformZoomSrc(six, siy, siw, sih,
+ &dx, &dy, &dw, &dh,
+ src->w, dst->w, src->h, dst->h);
+ dx-=2;
+ dy-=2;
+ dw+=4;
+ dh+=4;
+
+ if (dx < 0)
+ dx = 0;
+ if (dy < 0)
+ dy = 0;
+ if (dx+dw > dst->w)
+ dw = dst->w - dx;
+ if (dy+dh > dst->h)
+ dh = dst->h - dy;
+
+ transformZoomDst(&sx, &sy, &sw, &sh,
+ dx, dy, dw, dh,
+ src->w, dst->w, src->h, dst->h);
+
+ if (sx+sw > src->w)
+ sw = src->w - sx;
+ if (sy+sh > src->h)
+ sh = src->h - sy;
+
+ ZoomSurfaceCoords32(sx, sy, sw, sh, dx, dy, src, dst);
+
+ *dix = dx;
+ *diy = dy;
+ *diw = dw;
+ *dih = dh;
+}
+
+static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh,
+ int dx, int dy,
+ Surface * src, Surface * dst)
+{
+ Surface s2;
+
+ s2 = *src;
+ s2.pixels = ((char*)s2.pixels) + (sx * s2.BytesPerPixel) + (sy * src->pitch);
+ s2.w = sw;
+ s2.h = sh;
+ sge_transform(&s2, dst,
+ (float)dst->w/(float)src->w, (float)dst->h/(float)src->h,
+ dx, dy);
+}
+
+
+#define sge_clip_xmin(pnt) 0
+#define sge_clip_xmax(pnt) pnt->w
+#define sge_clip_ymin(pnt) 0
+#define sge_clip_ymax(pnt) pnt->h
+
+/*==================================================================================
+// Helper function to sge_transform()
+// Returns the bounding box
+//==================================================================================
+*/
+static void _calcRect(Surface *src, Surface *dst, float xscale, float yscale,
+ Uint16 qx, Uint16 qy,
+ Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax)
+{
+ Sint16 x, y, rx, ry;
+ int i;
+
+ /* Clip to src surface */
+ Sint16 sxmin = sge_clip_xmin(src);
+ Sint16 sxmax = sge_clip_xmax(src);
+ Sint16 symin = sge_clip_ymin(src);
+ Sint16 symax = sge_clip_ymax(src);
+ Sint16 sx[5];
+ Sint16 sy[4];
+
+ /* We don't really need fixed-point here
+ * but why not? */
+ Sint32 ictx = (Sint32) (xscale * 8192.0);
+ Sint32 icty = (Sint32) (yscale * 8192.0);
+
+ sx[0] = sxmin;
+ sx[1] = sxmax;
+ sx[2] = sxmin;
+ sx[3] = sxmax;
+ sy[0] = symin;
+ sy[1] = symax;
+ sy[2] = symax;
+ sy[3] = symin;
+
+ /* Calculate the four corner points */
+ for(i=0; i<4; i++){
+ rx = sx[i];
+ ry = sy[i];
+
+ x = (Sint16)(((ictx*rx) >> 13) + qx);
+ y = (Sint16)(((icty*ry) >> 13) + qy);
+
+
+ if(i==0){
+ *xmax = *xmin = x;
+ *ymax = *ymin = y;
+ }else{
+ if(x>*xmax)
+ *xmax=x;
+ else if(x<*xmin)
+ *xmin=x;
+
+ if(y>*ymax)
+ *ymax=y;
+ else if(y<*ymin)
+ *ymin=y;
+ }
+ }
+
+ /* Better safe than sorry...*/
+ *xmin -= 1;
+ *ymin -= 1;
+ *xmax += 1;
+ *ymax += 1;
+
+ /* Clip to dst surface */
+ if( !dst )
+ return;
+ if( *xmin < sge_clip_xmin(dst) )
+ *xmin = sge_clip_xmin(dst);
+ if( *xmax > sge_clip_xmax(dst) )
+ *xmax = sge_clip_xmax(dst);
+ if( *ymin < sge_clip_ymin(dst) )
+ *ymin = sge_clip_ymin(dst);
+ if( *ymax > sge_clip_ymax(dst) )
+ *ymax = sge_clip_ymax(dst);
+}
+
+
+/*==================================================================================
+** Scale by scale and place at position (qx,qy).
+**
+**
+** Developed with the help from Terry Hancock (hancock@earthlink.net)
+**
+**==================================================================================*/
+/* First we need some macros to handle different bpp
+ * I'm sorry about this...
+ */
+#define TRANSFORM(UintXX, DIV) \
+ Sint32 src_pitch=src->pitch/DIV; \
+ Sint32 dst_pitch=dst->pitch/DIV; \
+ UintXX *src_row = (UintXX *)src->pixels; \
+ UintXX *dst_row; \
+\
+ for (y=ymin; y<ymax; y++){ \
+ dy = y - qy; \
+\
+ sx = (Sint32)ctdx; /* Compute source anchor points */ \
+ sy = (Sint32)(cty*dy); \
+\
+ /* Calculate pointer to dst surface */ \
+ dst_row = (UintXX *)dst->pixels + y*dst_pitch; \
+\
+ for (x=xmin; x<xmax; x++){ \
+ rx=(Sint16)(sx >> 13); /* Convert from fixed-point */ \
+ ry=(Sint16)(sy >> 13); \
+\
+ /* Make sure the source pixel is actually in the source image. */ \
+ if( (rx>=sxmin) && (rx<sxmax) && (ry>=symin) && (ry<symax) ) \
+ *(dst_row + x) = *(src_row + ry*src_pitch + rx); \
+\
+ sx += ctx; /* Incremental transformations */ \
+ } \
+ }
+
+
+/* Interpolated transform */
+#define TRANSFORM_AA(UintXX, DIV) \
+ Sint32 src_pitch=src->pitch/DIV; \
+ Sint32 dst_pitch=dst->pitch/DIV; \
+ UintXX *src_row = (UintXX *)src->pixels; \
+ UintXX *dst_row; \
+ UintXX c1, c2, c3, c4;\
+ Uint32 R, G, B, A=0; \
+ UintXX Rmask = image->red_mask;\
+ UintXX Gmask = image->green_mask;\
+ UintXX Bmask = image->blue_mask;\
+ UintXX Amask = 0;\
+ Uint32 wx, wy;\
+ Uint32 p1, p2, p3, p4;\
+\
+ /*
+ * Interpolation:
+ * We calculate the distances from our point to the four nearest pixels, d1..d4.
+ * d(a,b) = sqrt(a²+b²) ~= 0.707(a+b) (Pythagoras (Taylor) expanded around (0.5;0.5))
+ *
+ * 1 wx 2
+ * *-|-* (+ = our point at (x,y))
+ * | | | (* = the four nearest pixels)
+ * wy --+ | wx = float(x) - int(x)
+ * | | wy = float(y) - int(y)
+ * *---*
+ * 3 4
+ * d1 = d(wx,wy) d2 = d(1-wx,wy) d3 = d(wx,1-wy) d4 = d(1-wx,1-wy)
+ * We now want to weight each pixels importance - it's vicinity to our point:
+ * w1=d4 w2=d3 w3=d2 w4=d1 (Yes it works... just think a bit about it)
+ *
+ * If the pixels have the colors c1..c4 then our point should have the color
+ * c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4) (the weighted average)
+ * but w1+w2+w3+w4 = 4*0.707 so we might as well write it as
+ * c = p1*c1 + p2*c2 + p3*c3 + p4*c4 where p1..p4 = (w1..w4)/(4*0.707)
+ *
+ * But p1..p4 are fixed point so we can just divide the fixed point constant!
+ * 8192/(4*0.71) = 2897 and we can skip 0.71 too (the division will cancel it everywhere)
+ * 8192/4 = 2048
+ *
+ * 020102: I changed the fixed-point representation for the variables in the weighted average
+ * to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This
+ * does however not solve the problem with 32bit RGBA colors...
+ */\
+\
+ Sint32 one = 2048>>6; /* 1 in Fixed-point */ \
+ Sint32 two = 2*2048>>6; /* 2 in Fixed-point */ \
+\
+ for (y=ymin; y<ymax; y++){ \
+ dy = y - qy; \
+\
+ sx = (Sint32)(ctdx); /* Compute source anchor points */ \
+ sy = (Sint32)(cty*dy); \
+\
+ /* Calculate pointer to dst surface */ \
+ dst_row = (UintXX *)dst->pixels + y*dst_pitch; \
+\
+ for (x=xmin; x<xmax; x++){ \
+ rx=(Sint16)(sx >> 13); /* Convert from fixed-point */ \
+ ry=(Sint16)(sy >> 13); \
+\
+ /* Make sure the source pixel is actually in the source image. */ \
+ if( (rx>=sxmin) && (rx+1<sxmax) && (ry>=symin) && (ry+1<symax) ){ \
+ wx = (sx & 0x00001FFF) >>8; /* (float(x) - int(x)) / 4 */ \
+ wy = (sy & 0x00001FFF) >>8;\
+\
+ p4 = wx+wy;\
+ p3 = one-wx+wy;\
+ p2 = wx+one-wy;\
+ p1 = two-wx-wy;\
+\
+ c1 = *(src_row + ry*src_pitch + rx);\
+ c2 = *(src_row + ry*src_pitch + rx+1);\
+ c3 = *(src_row + (ry+1)*src_pitch + rx);\
+ c4 = *(src_row + (ry+1)*src_pitch + rx+1);\
+\
+ /* Calculate the average */\
+ R = ((p1*(c1 & Rmask) + p2*(c2 & Rmask) + p3*(c3 & Rmask) + p4*(c4 & Rmask))>>7) & Rmask;\
+ G = ((p1*(c1 & Gmask) + p2*(c2 & Gmask) + p3*(c3 & Gmask) + p4*(c4 & Gmask))>>7) & Gmask;\
+ B = ((p1*(c1 & Bmask) + p2*(c2 & Bmask) + p3*(c3 & Bmask) + p4*(c4 & Bmask))>>7) & Bmask;\
+ if(Amask)\
+ A = ((p1*(c1 & Amask) + p2*(c2 & Amask) + p3*(c3 & Amask) + p4*(c4 & Amask))>>7) & Amask;\
+ \
+ *(dst_row + x) = R | G | B | A;\
+ } \
+ sx += ctx; /* Incremental transformations */ \
+ } \
+ }
+
+void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy)
+{
+ Sint32 dy, sx, sy;
+ Sint16 x, y, rx, ry;
+ Rect r;
+
+ Sint32 ctx, cty;
+ Sint16 xmin, xmax, ymin, ymax;
+ Sint16 sxmin, sxmax, symin, symax;
+ Sint32 dx, ctdx;
+
+
+ /* Here we use 18.13 fixed point integer math
+ // Sint32 should have 31 usable bits and one for sign
+ // 2^13 = 8192
+ */
+
+ /* Check scales */
+ Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13)); /* 2^(31-13) */
+
+ r.x = r.y = r.w = r.h = 0;
+
+ if( xscale == 0 || yscale == 0)
+ return;
+
+ if( 8192.0/xscale > maxint )
+ xscale = (float)(8192.0/maxint);
+ else if( 8192.0/xscale < -maxint )
+ xscale = (float)(-8192.0/maxint);
+
+ if( 8192.0/yscale > maxint )
+ yscale = (float)(8192.0/maxint);
+ else if( 8192.0/yscale < -maxint )
+ yscale = (float)(-8192.0/maxint);
+
+
+ /* Fixed-point equivalents */
+ ctx = (Sint32)(8192.0/xscale);
+ cty = (Sint32)(8192.0/yscale);
+
+ /* Compute a bounding rectangle */
+ xmin=0; xmax=dst->w; ymin=0; ymax=dst->h;
+ _calcRect(src, dst, xscale, yscale,
+ qx, qy, &xmin, &ymin, &xmax, &ymax);
+
+ /* Clip to src surface */
+ sxmin = sge_clip_xmin(src);
+ sxmax = sge_clip_xmax(src);
+ symin = sge_clip_ymin(src);
+ symax = sge_clip_ymax(src);
+
+ /* Some terms in the transform are constant */
+ dx = xmin - qx;
+ ctdx = ctx*dx;
+
+ /* Use the correct bpp */
+ if( src->BytesPerPixel == dst->BytesPerPixel){
+ switch( src->BytesPerPixel ){
+ case 1: { /* Assuming 8-bpp */
+ TRANSFORM(Uint8, 1)
+ }
+ break;
+ case 2: { /* Probably 15-bpp or 16-bpp */
+ TRANSFORM_AA(Uint16, 2)
+ }
+ break;
+ case 4: { /* Probably 32-bpp */
+ TRANSFORM_AA(Uint32, 4)
+ }
+ break;
+ }
+ }
+}
+
+void freeDesktopResources() {
+ Cleanup();
+ if (image) {
+ XDestroyImage(image);
+ }
+ if (zoomImage) {
+ XDestroyImage(zoomImage);
+ }
+ if (savedArea)
+ free(savedArea);
+
+ if (gcInited) {
+ XFreeGC(dpy, gc);
+ XFreeGC(dpy, srcGC);
+ XFreeGC(dpy, dstGC);
+ }
+
+ caughtShmError = False;
+ needShmCleanup = False;
+ caughtZoomShmError = False;
+ needZoomShmCleanup = False;
+ gcInited = False;
+ image = NULL;
+ useShm = True;
+ zoomActive = False;
+ zoomImage = NULL;
+ useZoomShm = True;
+ savedArea = NULL;
+}
+
+
+/*
+ * ColorRectangle32
+ * Only used for debugging / visualizing output
+ */
+/*
+static void
+ColorRectangle32(XImage *img, CARD32 fg, int x, int y, int width, int height)
+{
+ int i, h;
+ int scrWidthInBytes = img->bytes_per_line;
+ char *scr;
+ CARD32 *scr32;
+
+ if ((!img) || (!img->data))
+ return;
+
+ scr = (img->data + y * scrWidthInBytes + x * 4);
+
+ if (!CheckRectangle(x, y, width, height))
+ return;
+
+ for (h = 0; h < height; h++) {
+ scr32 = (CARD32*) scr;
+ for (i = 0; i < width; i++) {
+ CARD32 n = 0;
+ CARD32 p = scr32[i];
+ if (0xff & fg)
+ n |= ((( 0xff & p)+( 0xff & fg)) >> 2) & 0xff;
+ else
+ n |= (0xff & p);
+ if (0xff00 & fg)
+ n |= ((( 0xff00 & p)+( 0xff00 & fg)) >> 2) & 0xff00;
+ else
+ n |= (0xff00 & p);
+ if (0xff0000 & fg)
+ n |= (((0xff0000 & p)+(0xff0000 & fg)) >> 2) & 0xff0000;
+ else
+ n |= (0xff0000 & p);
+ scr32[i] = n;
+ }
+ scr += scrWidthInBytes;
+ }
+}
+*/
+
diff --git a/krdc/vnc/hextile.c b/krdc/vnc/hextile.c
new file mode 100644
index 00000000..002880af
--- /dev/null
+++ b/krdc/vnc/hextile.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * hextile.c - handle hextile encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles a hextile
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleHextileBPP CONCAT2E(HandleHextile,BPP)
+#define CARDBPP CONCAT2E(CARD,BPP)
+#define GET_PIXEL CONCAT2E(GET_PIXEL,BPP)
+#define FillRectangleBPP CONCAT2E(FillRectangle,BPP)
+
+static Bool
+HandleHextileBPP (int rx, int ry, int rw, int rh)
+{
+ CARDBPP bg, fg;
+ int i;
+ CARD8 *ptr;
+ int x, y, w, h;
+ int sx, sy, sw, sh;
+ CARD8 subencoding;
+ CARD8 nSubrects;
+
+ for (y = ry; y < ry+rh; y += 16) {
+ for (x = rx; x < rx+rw; x += 16) {
+ w = h = 16;
+ if (rx+rw - x < 16)
+ w = rx+rw - x;
+ if (ry+rh - y < 16)
+ h = ry+rh - y;
+
+ if (!ReadFromRFBServer((char *)&subencoding, 1))
+ return False;
+
+ if (subencoding & rfbHextileRaw) {
+ if (!ReadFromRFBServer(buffer, w * h * (BPP / 8)))
+ return False;
+
+ CopyDataToScreen(buffer, x, y, w, h);
+ continue;
+ }
+
+ if (subencoding & rfbHextileBackgroundSpecified)
+ if (!ReadFromRFBServer((char *)&bg, sizeof(bg)))
+ return False;
+
+ LockFramebuffer();
+ FillRectangleBPP(bg, x, y, w, h);
+
+ if (subencoding & rfbHextileForegroundSpecified)
+ if (!ReadFromRFBServer((char *)&fg, sizeof(fg))) {
+ UnlockFramebuffer();
+ return False;
+ }
+
+ if (!(subencoding & rfbHextileAnySubrects)) {
+ UnlockFramebuffer();
+ SyncScreenRegion(x, y, w, h);
+ continue;
+ }
+
+ if (!ReadFromRFBServer((char *)&nSubrects, 1)) {
+ UnlockFramebuffer();
+ return False;
+ }
+
+ ptr = (CARD8 *)buffer;
+
+ if (subencoding & rfbHextileSubrectsColoured) {
+ if (!ReadFromRFBServer(buffer, nSubrects * (2 + (BPP / 8)))) {
+ UnlockFramebuffer();
+ return False;
+ }
+
+ for (i = 0; i < nSubrects; i++) {
+ GET_PIXEL(fg, ptr);
+ sx = rfbHextileExtractX(*ptr);
+ sy = rfbHextileExtractY(*ptr);
+ ptr++;
+ sw = rfbHextileExtractW(*ptr);
+ sh = rfbHextileExtractH(*ptr);
+ ptr++;
+ FillRectangleBPP(fg, x+sx, y+sy, sw, sh);
+ }
+
+ } else {
+ if (!ReadFromRFBServer(buffer, nSubrects * 2)) {
+ UnlockFramebuffer();
+ return False;
+ }
+
+ for (i = 0; i < nSubrects; i++) {
+ sx = rfbHextileExtractX(*ptr);
+ sy = rfbHextileExtractY(*ptr);
+ ptr++;
+ sw = rfbHextileExtractW(*ptr);
+ sh = rfbHextileExtractH(*ptr);
+ ptr++;
+ FillRectangleBPP(fg, x+sx, y+sy, sw, sh);
+ }
+ }
+ UnlockFramebuffer();
+ SyncScreenRegion(x, y, w, h);
+ }
+ }
+
+ return True;
+}
diff --git a/krdc/vnc/kvncview.cpp b/krdc/vnc/kvncview.cpp
new file mode 100644
index 00000000..1b6a8de2
--- /dev/null
+++ b/krdc/vnc/kvncview.cpp
@@ -0,0 +1,828 @@
+/***************************************************************************
+ kvncview.cpp - main widget
+ -------------------
+ begin : Thu Dec 20 15:11:42 CET 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kvncview.h"
+#include "vncprefs.h"
+#include "vnchostpref.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstandarddirs.h>
+#include <kpassdlg.h>
+#include <kdialogbase.h>
+#include <kwallet.h>
+
+#include <qdatastream.h>
+#include <dcopclient.h>
+#include <qclipboard.h>
+#include <qbitmap.h>
+#include <qmutex.h>
+#include <qvbox.h>
+#include <qwaitcondition.h>
+
+#include "vncviewer.h"
+
+#include <X11/Xlib.h>
+
+/*
+ * appData is our application-specific data which can be set by the user with
+ * application resource specs. The AppData structure is defined in the header
+ * file.
+ */
+AppData appData;
+bool appDataConfigured = false;
+
+Display* dpy;
+
+static KVncView *kvncview;
+
+//Passwords and KWallet data
+extern KWallet::Wallet *wallet;
+bool useKWallet = false;
+static QCString password;
+static QMutex passwordLock;
+static QWaitCondition passwordWaiter;
+
+const unsigned int MAX_SELECTION_LENGTH = 4096;
+
+
+KVncView::KVncView(QWidget *parent,
+ const char *name,
+ const QString &_host,
+ int _port,
+ const QString &_password,
+ Quality quality,
+ DotCursorState dotCursorState,
+ const QString &encodings) :
+ KRemoteView(parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase | Qt::WStaticContents),
+ m_cthread(this, m_wthread, m_quitFlag),
+ m_wthread(this, m_quitFlag),
+ m_quitFlag(false),
+ m_enableFramebufferLocking(false),
+ m_scaling(false),
+ m_remoteMouseTracking(false),
+ m_viewOnly(false),
+ m_buttonMask(0),
+ m_host(_host),
+ m_port(_port),
+ m_dontSendCb(false),
+ m_cursorState(dotCursorState)
+{
+ kvncview = this;
+ password = _password.latin1();
+ dpy = qt_xdisplay();
+ setFixedSize(16,16);
+ setFocusPolicy(QWidget::StrongFocus);
+
+ m_cb = QApplication::clipboard();
+ connect(m_cb, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
+ connect(m_cb, SIGNAL(dataChanged()), this, SLOT(clipboardChanged()));
+
+ KStandardDirs *dirs = KGlobal::dirs();
+ QBitmap cursorBitmap(dirs->findResource("appdata",
+ "pics/pointcursor.png"));
+ QBitmap cursorMask(dirs->findResource("appdata",
+ "pics/pointcursormask.png"));
+ m_cursor = QCursor(cursorBitmap, cursorMask);
+
+ if ((quality != QUALITY_UNKNOWN) ||
+ !encodings.isNull())
+ configureApp(quality, encodings);
+}
+
+void KVncView::showDotCursor(DotCursorState state) {
+ if (state == m_cursorState)
+ return;
+
+ m_cursorState = state;
+ showDotCursorInternal();
+}
+
+DotCursorState KVncView::dotCursorState() const {
+ return m_cursorState;
+}
+
+void KVncView::showDotCursorInternal() {
+ switch (m_cursorState) {
+ case DOT_CURSOR_ON:
+ setCursor(m_cursor);
+ break;
+ case DOT_CURSOR_OFF:
+ setCursor(QCursor(Qt::BlankCursor));
+ break;
+ case DOT_CURSOR_AUTO:
+ if (m_enableClientCursor)
+ setCursor(QCursor(Qt::BlankCursor));
+ else
+ setCursor(m_cursor);
+ break;
+ }
+}
+
+QString KVncView::host() {
+ return m_host;
+}
+
+int KVncView::port() {
+ return m_port;
+}
+
+void KVncView::startQuitting() {
+ m_quitFlag = true;
+ m_wthread.kick();
+ m_cthread.kick();
+}
+
+bool KVncView::isQuitting() {
+ return m_quitFlag;
+}
+
+void KVncView::configureApp(Quality q, const QString specialEncodings) {
+ appDataConfigured = true;
+ appData.shareDesktop = 1;
+ appData.viewOnly = 0;
+
+ if (q == QUALITY_LOW) {
+ appData.useBGR233 = 1;
+ appData.encodingsString = "background copyrect softcursor tight zlib hextile raw";
+ appData.compressLevel = -1;
+ appData.qualityLevel = 1;
+ appData.dotCursor = 1;
+ }
+ else if (q == QUALITY_MEDIUM) {
+ appData.useBGR233 = 0;
+ appData.encodingsString = "background copyrect softcursor tight zlib hextile raw";
+ appData.compressLevel = -1;
+ appData.qualityLevel = 7;
+ appData.dotCursor = 1;
+ }
+ else if ((q == QUALITY_HIGH) || (q == QUALITY_UNKNOWN)) {
+ appData.useBGR233 = 0;
+ appData.encodingsString = "copyrect softcursor hextile raw";
+ appData.compressLevel = -1;
+ appData.qualityLevel = 9;
+ appData.dotCursor = 1;
+ }
+
+ if (!specialEncodings.isNull())
+ appData.encodingsString = specialEncodings.latin1();
+
+ appData.nColours = 256;
+ appData.useSharedColours = 1;
+ appData.requestedDepth = 0;
+
+ appData.rawDelay = 0;
+ appData.copyRectDelay = 0;
+
+ if (!appData.dotCursor)
+ m_cursorState = DOT_CURSOR_OFF;
+ showDotCursorInternal();
+}
+
+bool KVncView::checkLocalKRfb() {
+ if ( m_host != "localhost" && !m_host.isEmpty() )
+ return true;
+ DCOPClient *d = KApplication::dcopClient();
+
+ int portNum;
+ QByteArray sdata, rdata;
+ QCString replyType;
+ QDataStream arg(sdata, IO_WriteOnly);
+ arg << QString("krfb");
+ if (!d->call ("kded", "kinetd", "port(QString)", sdata, replyType, rdata))
+ return true;
+
+ if (replyType != "int")
+ return true;
+
+ QDataStream answer(rdata, IO_ReadOnly);
+ answer >> portNum;
+
+ if (m_port != portNum)
+ return true;
+
+ setStatus(REMOTE_VIEW_DISCONNECTED);
+ KMessageBox::error(0,
+ i18n("It is not possible to connect to a local desktop sharing service."),
+ i18n("Connection Failure"));
+ emit disconnectedError();
+ return false;
+}
+
+bool KVncView::editPreferences( HostPrefPtr host )
+{
+ SmartPtr<VncHostPref> hp( host );
+
+ int ci = hp->quality();
+ bool kwallet = hp->useKWallet();
+
+ // show preferences dialog
+ KDialogBase *dlg = new KDialogBase( 0L, "dlg", true,
+ i18n( "VNC Host Preferences for %1" ).arg( host->host() ),
+ KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true );
+
+ QVBox *vbox = dlg->makeVBoxMainWidget();
+ VncPrefs *prefs = new VncPrefs( vbox );
+ QWidget *spacer = new QWidget( vbox );
+ vbox->setStretchFactor( spacer, 10 );
+
+ prefs->setQuality( ci );
+ prefs->setShowPrefs(true);
+ prefs->setUseKWallet(kwallet);
+
+ if ( dlg->exec() == QDialog::Rejected )
+ return false;
+
+ ci = prefs->quality();
+ hp->setAskOnConnect(prefs->showPrefs());
+ hp->setQuality(ci);
+ hp->setUseKWallet(prefs->useKWallet());
+
+ delete dlg;
+ return true;
+}
+
+bool KVncView::start() {
+
+ if (!checkLocalKRfb())
+ return false;
+
+ if (!appDataConfigured) {
+
+ HostPreferences *hps = HostPreferences::instance();
+ SmartPtr<VncHostPref> hp =
+ SmartPtr<VncHostPref>(hps->createHostPref(m_host,
+ VncHostPref::VncType));
+ if (hp->askOnConnect()) {
+ if (!editPreferences(hp))
+ return false;
+ hps->sync();
+ }
+
+ int ci = hp->quality();
+
+ Quality quality;
+ if (ci == 0)
+ quality = QUALITY_HIGH;
+ else if (ci == 1)
+ quality = QUALITY_MEDIUM;
+ else if (ci == 2)
+ quality = QUALITY_LOW;
+ else {
+ kdDebug() << "Unknown quality";
+ return false;
+ }
+
+ configureApp(quality);
+ useKWallet = hp->useKWallet();
+ }
+
+ setStatus(REMOTE_VIEW_CONNECTING);
+
+ m_cthread.start();
+ setBackgroundMode(Qt::NoBackground);
+ return true;
+}
+
+KVncView::~KVncView()
+{
+ startQuitting();
+ m_cthread.wait();
+ m_wthread.wait();
+ freeResources();
+}
+
+bool KVncView::supportsLocalCursor() const {
+ return true;
+}
+
+bool KVncView::supportsScaling() const {
+ return true;
+}
+
+bool KVncView::scaling() const {
+ return m_scaling;
+}
+
+bool KVncView::viewOnly() {
+ return m_viewOnly;
+}
+
+QSize KVncView::framebufferSize() {
+ return m_framebufferSize;
+}
+
+void KVncView::setViewOnly(bool s) {
+ m_viewOnly = s;
+
+ if (s)
+ setCursor(Qt::ArrowCursor);
+ else
+ showDotCursorInternal();
+}
+
+void KVncView::enableScaling(bool s) {
+ bool os = m_scaling;
+ m_scaling = s;
+ if (s != os) {
+ if (s) {
+ setMaximumSize(m_framebufferSize);
+ setMinimumSize(m_framebufferSize.width()/16,
+ m_framebufferSize.height()/16);
+ }
+ else
+ setFixedSize(m_framebufferSize);
+ }
+}
+
+void KVncView::paintEvent(QPaintEvent *e) {
+ drawRegion(e->rect().x(),
+ e->rect().y(),
+ e->rect().width(),
+ e->rect().height());
+}
+
+void KVncView::drawRegion(int x, int y, int w, int h) {
+ if (m_scaling)
+ DrawZoomedScreenRegionX11Thread(winId(), width(), height(),
+ x, y, w, h);
+ else
+ DrawScreenRegionX11Thread(winId(), x, y, w, h);
+}
+
+void KVncView::customEvent(QCustomEvent *e)
+{
+ if (e->type() == ScreenRepaintEventType) {
+ ScreenRepaintEvent *sre = (ScreenRepaintEvent*) e;
+ drawRegion(sre->x(), sre->y(),sre->width(), sre->height());
+ }
+ else if (e->type() == ScreenResizeEventType) {
+ ScreenResizeEvent *sre = (ScreenResizeEvent*) e;
+ m_framebufferSize = QSize(sre->width(), sre->height());
+ setFixedSize(m_framebufferSize);
+ emit changeSize(sre->width(), sre->height());
+ }
+ else if (e->type() == DesktopInitEventType) {
+ m_cthread.desktopInit();
+ }
+ else if (e->type() == StatusChangeEventType) {
+ StatusChangeEvent *sce = (StatusChangeEvent*) e;
+ setStatus(sce->status());
+ if (m_status == REMOTE_VIEW_CONNECTED) {
+ emit connected();
+ setFocus();
+ setMouseTracking(true);
+ }
+ else if (m_status == REMOTE_VIEW_DISCONNECTED) {
+ setMouseTracking(false);
+ emit disconnected();
+ }
+ else if (m_status == REMOTE_VIEW_PREPARING) {
+ //Login was successfull: Write KWallet password if necessary.
+ if ( useKWallet && !password.isNull() && wallet && wallet->isOpen() && !wallet->hasEntry(host())) {
+ wallet->writePassword(host(), password);
+ }
+ delete wallet; wallet=0;
+ }
+ }
+ else if (e->type() == PasswordRequiredEventType) {
+ emit showingPasswordDialog(true);
+
+ if (KPasswordDialog::getPassword(password, i18n("Access to the system requires a password.")) != KPasswordDialog::Accepted)
+ password = QCString();
+
+ emit showingPasswordDialog(false);
+
+ passwordLock.lock(); // to guarantee that thread is waiting
+ passwordWaiter.wakeAll();
+ passwordLock.unlock();
+ }
+ else if (e->type() == WalletOpenEventType) {
+ QString krdc_folder = "KRDC-VNC";
+ emit showingPasswordDialog(true); //Bad things happen if you don't do this.
+
+ // Bugfix: Check if wallet has been closed by an outside source
+ if ( wallet && !wallet->isOpen() ) {
+ delete wallet; wallet=0;
+ }
+
+ // Do we need to open the wallet?
+ if ( !wallet ) {
+ QString walletName = KWallet::Wallet::NetworkWallet();
+ wallet = KWallet::Wallet::openWallet(walletName);
+ }
+
+ if (wallet && wallet->isOpen()) {
+ bool walletOK = wallet->hasFolder(krdc_folder);
+ if (walletOK == false) {
+ walletOK = wallet->createFolder(krdc_folder);
+ }
+
+ if (walletOK == true) {
+ wallet->setFolder(krdc_folder);
+ QString newPass;
+ if ( wallet->hasEntry(kvncview->host()) && !wallet->readPassword(kvncview->host(), newPass) ) {
+ password=newPass.latin1();
+ }
+ }
+ }
+
+ passwordLock.lock(); // to guarantee that thread is waiting
+ passwordWaiter.wakeAll();
+ passwordLock.unlock();
+
+ emit showingPasswordDialog(false);
+ }
+ else if (e->type() == FatalErrorEventType) {
+ FatalErrorEvent *fee = (FatalErrorEvent*) e;
+ setStatus(REMOTE_VIEW_DISCONNECTED);
+ switch (fee->errorCode()) {
+ case ERROR_CONNECTION:
+ KMessageBox::error(0,
+ i18n("Connection attempt to host failed."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_PROTOCOL:
+ KMessageBox::error(0,
+ i18n("Remote host is using an incompatible protocol."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_IO:
+ KMessageBox::error(0,
+ i18n("The connection to the host has been interrupted."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_SERVER_BLOCKED:
+ KMessageBox::error(0,
+ i18n("Connection failed. The server does not accept new connections."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_NAME:
+ KMessageBox::error(0,
+ i18n("Connection failed. A server with the given name cannot be found."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_NO_SERVER:
+ KMessageBox::error(0,
+ i18n("Connection failed. No server running at the given address and port."),
+ i18n("Connection Failure"));
+ break;
+ case ERROR_AUTHENTICATION:
+ //Login failed: Remove wallet entry if there is one.
+ if ( useKWallet && wallet && wallet->isOpen() && wallet->hasEntry(host()) ) {
+ wallet->removeEntry(host());
+ }
+ KMessageBox::error(0,
+ i18n("Authentication failed. Connection aborted."),
+ i18n("Authentication Failure"));
+ break;
+ default:
+ KMessageBox::error(0,
+ i18n("Unknown error."),
+ i18n("Unknown Error"));
+ break;
+ }
+ emit disconnectedError();
+ }
+ else if (e->type() == BeepEventType) {
+ QApplication::beep();
+ }
+ else if (e->type() == ServerCutEventType) {
+ ServerCutEvent *sce = (ServerCutEvent*) e;
+ QString ctext = QString::fromUtf8(sce->bytes(), sce->length());
+ m_dontSendCb = true;
+ m_cb->setText(ctext, QClipboard::Clipboard);
+ m_cb->setText(ctext, QClipboard::Selection);
+ m_dontSendCb = false;
+ }
+ else if (e->type() == MouseStateEventType) {
+ MouseStateEvent *mse = (MouseStateEvent*) e;
+ emit mouseStateChanged(mse->x(), mse->y(), mse->buttonMask());
+ bool show = m_plom.handlePointerEvent(mse->x(), mse->y());
+ if (m_cursorState != DOT_CURSOR_ON)
+ showDotCursor(show ? DOT_CURSOR_AUTO : DOT_CURSOR_OFF);
+ }
+}
+
+void KVncView::mouseEvent(QMouseEvent *e) {
+ if (m_status != REMOTE_VIEW_CONNECTED)
+ return;
+ if (m_viewOnly)
+ return;
+
+ if ( e->type() != QEvent::MouseMove ) {
+ if ( (e->type() == QEvent::MouseButtonPress) ||
+ (e->type() == QEvent::MouseButtonDblClick)) {
+ if ( e->button() & LeftButton )
+ m_buttonMask |= 0x01;
+ if ( e->button() & MidButton )
+ m_buttonMask |= 0x02;
+ if ( e->button() & RightButton )
+ m_buttonMask |= 0x04;
+ }
+ else if ( e->type() == QEvent::MouseButtonRelease ) {
+ if ( e->button() & LeftButton )
+ m_buttonMask &= 0xfe;
+ if ( e->button() & MidButton )
+ m_buttonMask &= 0xfd;
+ if ( e->button() & RightButton )
+ m_buttonMask &= 0xfb;
+ }
+ }
+
+ int x = e->x();
+ int y = e->y();
+ m_plom.registerPointerState(x, y);
+ if (m_scaling) {
+ x = (x * m_framebufferSize.width()) / width();
+ y = (y * m_framebufferSize.height()) / height();
+ }
+ m_wthread.queueMouseEvent(x, y, m_buttonMask);
+
+ if (m_enableClientCursor)
+ DrawCursorX11Thread(x, y); // in rfbproto.c
+}
+
+void KVncView::mousePressEvent(QMouseEvent *e) {
+ mouseEvent(e);
+ e->accept();
+}
+
+void KVncView::mouseDoubleClickEvent(QMouseEvent *e) {
+ mouseEvent(e);
+ e->accept();
+}
+
+void KVncView::mouseReleaseEvent(QMouseEvent *e) {
+ mouseEvent(e);
+ e->accept();
+}
+
+void KVncView::mouseMoveEvent(QMouseEvent *e) {
+ mouseEvent(e);
+ e->ignore();
+}
+
+void KVncView::wheelEvent(QWheelEvent *e) {
+ if (m_status != REMOTE_VIEW_CONNECTED)
+ return;
+ if (m_viewOnly)
+ return;
+
+ int eb = 0;
+ if ( e->delta() < 0 )
+ eb |= 0x10;
+ else
+ eb |= 0x8;
+
+ int x = e->pos().x();
+ int y = e->pos().y();
+ if (m_scaling) {
+ x = (x * m_framebufferSize.width()) / width();
+ y = (y * m_framebufferSize.height()) / height();
+ }
+ m_wthread.queueMouseEvent(x, y, eb|m_buttonMask);
+ m_wthread.queueMouseEvent(x, y, m_buttonMask);
+ e->accept();
+}
+
+void KVncView::pressKey(XEvent *xe) {
+ KKeyNative k(xe);
+ uint mod = k.mod();
+ if (mod & KKeyNative::modX(KKey::SHIFT))
+ m_wthread.queueKeyEvent(XK_Shift_L, true);
+ if (mod & KKeyNative::modX(KKey::CTRL))
+ m_wthread.queueKeyEvent(XK_Control_L, true);
+ if (mod & KKeyNative::modX(KKey::ALT))
+ m_wthread.queueKeyEvent(XK_Alt_L, true);
+ if (mod & KKeyNative::modX(KKey::WIN))
+ m_wthread.queueKeyEvent(XK_Meta_L, true);
+
+ m_wthread.queueKeyEvent(k.sym(), true);
+ m_wthread.queueKeyEvent(k.sym(), false);
+
+ if (mod & KKeyNative::modX(KKey::WIN))
+ m_wthread.queueKeyEvent(XK_Meta_L, false);
+ if (mod & KKeyNative::modX(KKey::ALT))
+ m_wthread.queueKeyEvent(XK_Alt_L, false);
+ if (mod & KKeyNative::modX(KKey::CTRL))
+ m_wthread.queueKeyEvent(XK_Control_L, false);
+ if (mod & KKeyNative::modX(KKey::SHIFT))
+ m_wthread.queueKeyEvent(XK_Shift_L, false);
+
+ m_mods.clear();
+}
+
+bool KVncView::x11Event(XEvent *e) {
+ bool pressed;
+ if (e->type == KeyPress)
+ pressed = true;
+ else if (e->type == KeyRelease)
+ pressed = false;
+ else
+ return QWidget::x11Event(e);
+
+ if (!m_viewOnly) {
+ unsigned int s = KKeyNative(e).sym();
+
+ switch (s) {
+ case XK_Meta_L:
+ case XK_Alt_L:
+ case XK_Control_L:
+ case XK_Shift_L:
+ case XK_Meta_R:
+ case XK_Alt_R:
+ case XK_Control_R:
+ case XK_Shift_R:
+ if (pressed)
+ m_mods[s] = true;
+ else if (m_mods.contains(s))
+ m_mods.remove(s);
+ else
+ unpressModifiers();
+ }
+ m_wthread.queueKeyEvent(s, pressed);
+ }
+ return true;
+}
+
+void KVncView::unpressModifiers() {
+ QValueList<unsigned int> keys = m_mods.keys();
+ QValueList<unsigned int>::const_iterator it = keys.begin();
+ while (it != keys.end()) {
+ m_wthread.queueKeyEvent(*it, false);
+ it++;
+ }
+ m_mods.clear();
+}
+
+void KVncView::focusOutEvent(QFocusEvent *) {
+ unpressModifiers();
+}
+
+QSize KVncView::sizeHint() {
+ return maximumSize();
+}
+
+void KVncView::setRemoteMouseTracking(bool s) {
+ m_remoteMouseTracking = s;
+}
+
+bool KVncView::remoteMouseTracking() {
+ return m_remoteMouseTracking;
+}
+
+void KVncView::clipboardChanged() {
+ if (m_status != REMOTE_VIEW_CONNECTED)
+ return;
+
+ if (m_cb->ownsClipboard() || m_dontSendCb)
+ return;
+
+ QString text = m_cb->text(QClipboard::Clipboard);
+ if (text.length() > MAX_SELECTION_LENGTH)
+ return;
+
+ m_wthread.queueClientCut(text);
+}
+
+void KVncView::selectionChanged() {
+ if (m_status != REMOTE_VIEW_CONNECTED)
+ return;
+
+ if (m_cb->ownsSelection() || m_dontSendCb)
+ return;
+
+ QString text = m_cb->text(QClipboard::Selection);
+ if (text.length() > MAX_SELECTION_LENGTH)
+ return;
+
+ m_wthread.queueClientCut(text);
+}
+
+
+void KVncView::lockFramebuffer() {
+ if (m_enableFramebufferLocking)
+ m_framebufferLock.lock();
+}
+
+void KVncView::unlockFramebuffer() {
+ if (m_enableFramebufferLocking)
+ m_framebufferLock.unlock();
+}
+
+void KVncView::enableClientCursor(bool enable) {
+ if (enable) {
+ m_enableFramebufferLocking = true; // cant be turned off
+ }
+ m_enableClientCursor = enable;
+ lockFramebuffer();
+ showDotCursorInternal();
+ unlockFramebuffer();
+}
+
+/*!
+ \brief Get a password for this host.
+ Tries to get a password from the url or wallet if at all possible. If
+ both of these fail, it then asks the user to enter a password.
+ \note Lots of dialogs can be popped up during this process. The thread
+ locks and signals are there to protect against deadlocks and other
+ horribleness. Be careful making changes here.
+*/
+int getPassword(char *passwd, int pwlen) {
+ int retV = 0;
+
+ //Prepare the system
+ passwordLock.lock();
+
+ //Try #1: Did the user give a password in the URL?
+ if (!password.isNull()) {
+ retV = 1; //got it!
+ }
+
+ //Try #2: Is there something in the wallet?
+ if ( !retV && useKWallet ) {
+ QApplication::postEvent(kvncview, new WalletOpenEvent());
+ passwordWaiter.wait(&passwordLock); //block
+ if (!password.isNull()) retV = 1; //got it!
+ }
+
+ //Last try: Ask the user
+ if (!retV) {
+ QApplication::postEvent(kvncview, new PasswordRequiredEvent());
+ passwordWaiter.wait(&passwordLock); //block
+ if (!password.isNull()) retV = 1; //got it!
+ }
+
+ //Process the password if we got it, clear it if we didn't
+ if (retV) {
+ strncpy(passwd, (const char*)password, pwlen);
+ } else {
+ passwd[0] = 0;
+ }
+
+ //Pack up and go home
+ passwordLock.unlock();
+ if (!retV) kvncview->startQuitting();
+
+ return retV;
+}
+
+extern int isQuitFlagSet() {
+ return kvncview->isQuitting() ? 1 : 0;
+}
+
+extern void DrawScreenRegion(int x, int y, int width, int height) {
+/* KApplication::kApplication()->lock();
+ kvncview->drawRegion(x, y, width, height);
+ KApplication::kApplication()->unlock();
+*/
+ QApplication::postEvent(kvncview, new ScreenRepaintEvent(x, y, width, height));
+}
+
+// call only from x11 thread!
+extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height) {
+ kvncview->drawRegion(x, y, width, height);
+}
+
+extern void EnableClientCursor(int enable) {
+ kvncview->enableClientCursor(enable);
+}
+
+extern void LockFramebuffer() {
+ kvncview->lockFramebuffer();
+}
+
+extern void UnlockFramebuffer() {
+ kvncview->unlockFramebuffer();
+}
+
+extern void beep() {
+ QApplication::postEvent(kvncview, new BeepEvent());
+}
+
+extern void newServerCut(char *bytes, int length) {
+ QApplication::postEvent(kvncview, new ServerCutEvent(bytes, length));
+}
+
+extern void postMouseEvent(int x, int y, int buttonMask) {
+ QApplication::postEvent(kvncview, new MouseStateEvent(x, y, buttonMask));
+}
+
+#include "kvncview.moc"
diff --git a/krdc/vnc/kvncview.h b/krdc/vnc/kvncview.h
new file mode 100644
index 00000000..1b961f3d
--- /dev/null
+++ b/krdc/vnc/kvncview.h
@@ -0,0 +1,121 @@
+/***************************************************************************
+ kvncview.h - widget that shows the vnc client
+ -------------------
+ begin : Thu Dec 20 15:11:42 CET 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KVNCVIEW_H
+#define KVNCVIEW_H
+
+#include "kremoteview.h"
+#include <qcursor.h>
+#include <qmap.h>
+
+#include "pointerlatencyometer.h"
+#include "hostpreferences.h"
+#include "vnctypes.h"
+#include "threads.h"
+
+class QClipBoard;
+
+class KVncView : public KRemoteView
+{
+ Q_OBJECT
+private:
+ ControllerThread m_cthread;
+ WriterThread m_wthread;
+ volatile bool m_quitFlag; // if set: all threads should die ASAP
+ QMutex m_framebufferLock;
+ bool m_enableFramebufferLocking;
+ bool m_enableClientCursor;
+
+ QSize m_framebufferSize;
+ bool m_scaling;
+ bool m_remoteMouseTracking;
+ bool m_viewOnly;
+
+ int m_buttonMask;
+ QMap<unsigned int,bool> m_mods;
+
+ QString m_host;
+ int m_port;
+
+ QClipboard *m_cb;
+ bool m_dontSendCb;
+ QCursor m_cursor;
+ DotCursorState m_cursorState;
+ PointerLatencyOMeter m_plom;
+
+ void mouseEvent(QMouseEvent*);
+ unsigned long toKeySym(QKeyEvent *k);
+ bool checkLocalKRfb();
+ void paintMessage(const QString &msg);
+ void showDotCursorInternal();
+ void unpressModifiers();
+
+protected:
+ void paintEvent(QPaintEvent*);
+ void customEvent(QCustomEvent*);
+ void mousePressEvent(QMouseEvent*);
+ void mouseDoubleClickEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+ void wheelEvent(QWheelEvent *);
+ void focusOutEvent(QFocusEvent *);
+ bool x11Event(XEvent*);
+
+public:
+ KVncView(QWidget* parent=0, const char *name=0,
+ const QString &host = QString(""), int port = 5900,
+ const QString &password = QString::null,
+ Quality quality = QUALITY_UNKNOWN,
+ DotCursorState dotCursorState = DOT_CURSOR_AUTO,
+ const QString &encodings = QString::null);
+ ~KVncView();
+ QSize sizeHint();
+ void drawRegion(int x, int y, int w, int h);
+ void lockFramebuffer();
+ void unlockFramebuffer();
+ void enableClientCursor(bool enable);
+ virtual bool scaling() const;
+ virtual bool supportsScaling() const;
+ virtual bool supportsLocalCursor() const;
+ virtual QSize framebufferSize();
+ void setRemoteMouseTracking(bool s);
+ bool remoteMouseTracking();
+ void configureApp(Quality q, const QString specialEncodings = QString::null);
+ void showDotCursor(DotCursorState state);
+ DotCursorState dotCursorState() const;
+ virtual void startQuitting();
+ virtual bool isQuitting();
+ virtual QString host();
+ virtual int port();
+ virtual bool start();
+
+ virtual bool viewOnly();
+
+ static bool editPreferences( HostPrefPtr );
+
+public slots:
+ virtual void enableScaling(bool s);
+ virtual void setViewOnly(bool s);
+ virtual void pressKey(XEvent *k);
+
+
+private slots:
+ void clipboardChanged();
+ void selectionChanged();
+};
+
+#endif
diff --git a/krdc/vnc/pointerlatencyometer.h b/krdc/vnc/pointerlatencyometer.h
new file mode 100644
index 00000000..559536b2
--- /dev/null
+++ b/krdc/vnc/pointerlatencyometer.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ pointerlatencyometer.h - measuring pointer latency
+ -------------------
+ begin : Wed Jun 30 12:04:44 CET 2002
+ copyright : (C) 2002-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qdatetime.h>
+#include <kdebug.h>
+
+struct PointerState {
+ int x, y;
+ QTime timestamp;
+};
+
+class PointerLatencyOMeter {
+private:
+ enum { stateCapacity = 30, maximumLatency = 1000 };
+ PointerState states[stateCapacity];
+ int firstState, stateNum;
+ float last3Latency, last20Latency;
+
+public:
+ PointerLatencyOMeter() :
+ firstState(0),
+ stateNum(0),
+ last3Latency(125),
+ last20Latency(25) {
+ }
+
+ // registers a client pointer state
+ void registerPointerState(int x, int y) {
+ if (stateNum == stateCapacity)
+ stateNum--;
+ if (firstState == 0)
+ firstState = stateCapacity-1;
+ else
+ firstState--;
+ states[firstState].x = x;
+ states[firstState].y = y;
+ states[firstState].timestamp.start();
+ stateNum++;
+ }
+
+ /* Returns true if pointer should be visible */
+ bool registerLatency(int msecs) {
+ last3Latency = ((last3Latency * 2.0) + msecs) / 3.0;
+ last20Latency = ((last20Latency * 19.0) + msecs) / 20.0;
+
+ if (msecs >= maximumLatency)
+ return true;
+ if (last3Latency > (1000/4))
+ return true;
+ return last20Latency > (1000/12);
+ }
+
+ // Called with server-side coordinates.
+ // Returns true if pointer should be visible
+ bool handlePointerEvent(int x, int y) {
+ for (int i = stateNum-1; i >= 0; i--) {
+ int idx = (i+firstState) % stateCapacity;
+ if ((states[idx].x != x) ||
+ (states[idx].y != y))
+ continue;
+
+ stateNum = i;
+ int l = states[idx].timestamp.elapsed();
+ return registerLatency((l > maximumLatency) ? maximumLatency : l);
+ }
+ return true;
+ }
+
+};
diff --git a/krdc/vnc/rfbproto.c b/krdc/vnc/rfbproto.c
new file mode 100644
index 00000000..e9ed5764
--- /dev/null
+++ b/krdc/vnc/rfbproto.c
@@ -0,0 +1,1335 @@
+/*
+ * Copyright (C) 2002, Tim Jansen. All Rights Reserved.
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * rfbproto.c - functions to deal with client side of RFB protocol.
+ * tim@tjansen.de: - added softcursor encoding
+ * - changed various things for krdc
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include "vncviewer.h"
+#include "vncauth.h"
+#include <zlib.h>
+#include <jpeglib.h>
+
+static Bool HandleHextile8(int rx, int ry, int rw, int rh);
+static Bool HandleHextile16(int rx, int ry, int rw, int rh);
+static Bool HandleHextile32(int rx, int ry, int rw, int rh);
+static Bool HandleZlib8(int rx, int ry, int rw, int rh);
+static Bool HandleZlib16(int rx, int ry, int rw, int rh);
+static Bool HandleZlib32(int rx, int ry, int rw, int rh);
+static Bool HandleTight8(int rx, int ry, int rw, int rh);
+static Bool HandleTight16(int rx, int ry, int rw, int rh);
+static Bool HandleTight32(int rx, int ry, int rw, int rh);
+
+static long ReadCompactLen (void);
+
+static void JpegInitSource(j_decompress_ptr cinfo);
+static boolean JpegFillInputBuffer(j_decompress_ptr cinfo);
+static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes);
+static void JpegTermSource(j_decompress_ptr cinfo);
+static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData,
+ int compressedLen);
+
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \
+ << myFormat.redShift | \
+ (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \
+ << myFormat.greenShift | \
+ (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \
+ << myFormat.blueShift)
+
+int rfbsock;
+char *desktopName;
+rfbPixelFormat myFormat;
+rfbServerInitMsg si;
+
+int endianTest = 1;
+
+/*
+ * Softcursor variables
+ */
+
+int cursorX, cursorY;
+int imageIndex = -1;
+
+PointerImage pointerImages[rfbSoftCursorMaxImages];
+
+
+/* Hextile assumes it is big enough to hold 16 * 16 * 32 bits.
+ Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */
+
+#define BUFFER_SIZE (16384)
+static char buffer[BUFFER_SIZE];
+
+
+/* The zlib encoding requires expansion/decompression/deflation of the
+ compressed data in the "buffer" above into another, result buffer.
+ However, the size of the result buffer can be determined precisely
+ based on the bitsPerPixel, height and width of the rectangle. We
+ allocate this buffer one time to be the full size of the buffer. */
+
+static int raw_buffer_size = -1;
+static char *raw_buffer = NULL;
+
+static z_stream decompStream;
+static Bool decompStreamInited = False;
+
+
+/*
+ * Variables for the ``tight'' encoding implementation.
+ */
+
+/* Separate buffer for compressed data. */
+#define ZLIB_BUFFER_SIZE 512
+static char zlib_buffer[ZLIB_BUFFER_SIZE];
+
+/* Four independent compression streams for zlib library. */
+static z_stream zlibStream[4];
+static Bool zlibStreamActive[4] = {
+ False, False, False, False
+};
+
+/* Filter stuff. Should be initialized by filter initialization code. */
+static Bool cutZeros;
+static int rectWidth, rectColors;
+static char tightPalette[256*4];
+static CARD8 tightPrevRow[2048*3*sizeof(CARD16)];
+
+/* JPEG decoder state. */
+static Bool jpegError;
+
+/* Maximum length for the cut buffer (16 MB)*/
+#define MAX_CUTBUFFER (1024*1024*16)
+
+/* Maximum length for the strings (64 kB)*/
+#define MAX_STRING (1024*64)
+
+/* Maximum length for the strings (32 MB)*/
+#define MAX_JPEG_SIZE (1024*1024*32)
+
+
+/*
+ * ConnectToRFBServer.
+ */
+
+int
+ConnectToRFBServer(const char *hostname, int port)
+{
+ unsigned int host;
+
+ if (!StringToIPAddr(hostname, &host)) {
+ fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname);
+ return -(int)INIT_NAME_RESOLUTION_FAILURE;
+ }
+
+ rfbsock = ConnectToTcpAddr(host, port);
+ if (rfbsock < 0) {
+ fprintf(stderr,"Unable to connect to VNC server\n");
+ }
+
+ return rfbsock;
+}
+
+
+/*
+ * InitialiseRFBConnection.
+ */
+
+enum InitStatus
+InitialiseRFBConnection()
+{
+ rfbProtocolVersionMsg pv;
+ int major,minor;
+ CARD32 authScheme, reasonLen, authResult;
+ char *reason;
+ CARD8 challenge[CHALLENGESIZE];
+ char passwd[9];
+ int i;
+ rfbClientInitMsg ci;
+
+ /* if the connection is immediately closed, don't report anything, so
+ that pmw's monitor can make test connections */
+
+ if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) return INIT_SERVER_BLOCKED;
+
+ errorMessageOnReadFailure = True;
+
+ pv[sz_rfbProtocolVersionMsg] = 0;
+
+ if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) {
+ fprintf(stderr,"Not a valid VNC server\n");
+ return INIT_PROTOCOL_FAILURE;
+ }
+
+ fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n",
+ major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+
+ major = rfbProtocolMajorVersion;
+ minor = rfbProtocolMinorVersion;
+
+ sprintf(pv,rfbProtocolVersionFormat,major,minor);
+
+ if (!WriteExact(rfbsock, pv, sz_rfbProtocolVersionMsg)) return INIT_CONNECTION_FAILED;
+
+ if (!ReadFromRFBServer((char *)&authScheme, 4)) return INIT_CONNECTION_FAILED;
+
+ authScheme = Swap32IfLE(authScheme);
+
+ switch (authScheme) {
+
+ case rfbConnFailed:
+ if (!ReadFromRFBServer((char *)&reasonLen, 4)) return INIT_CONNECTION_FAILED;
+ reasonLen = Swap32IfLE(reasonLen);
+
+ if (reasonLen > MAX_STRING) {
+ fprintf(stderr, "Connection failure reason too long.\n");
+ return INIT_CONNECTION_FAILED;
+ }
+
+ reason = malloc(reasonLen);
+ if (!reason)
+ return INIT_CONNECTION_FAILED;
+
+ if (!ReadFromRFBServer(reason, reasonLen)) return INIT_CONNECTION_FAILED;
+
+ fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason);
+ free(reason);
+ return INIT_CONNECTION_FAILED;
+
+ case rfbNoAuth:
+ fprintf(stderr,"No authentication needed\n");
+ break;
+
+ case rfbVncAuth:
+ if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED;
+
+ if (!getPassword(passwd, 8))
+ return INIT_ABORTED;
+
+ passwd[8] = '\0';
+
+ vncEncryptBytes(challenge, passwd);
+
+ /* Lose the password from memory */
+ for (i = strlen(passwd); i >= 0; i--) {
+ passwd[i] = '\0';
+ }
+
+ if (!WriteExact(rfbsock, (char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED;
+
+ if (!ReadFromRFBServer((char *)&authResult, 4)) return INIT_CONNECTION_FAILED;
+
+ authResult = Swap32IfLE(authResult);
+
+ switch (authResult) {
+ case rfbVncAuthOK:
+ fprintf(stderr,"VNC authentication succeeded\n");
+ break;
+ case rfbVncAuthFailed:
+ fprintf(stderr,"VNC authentication failed\n");
+ return INIT_AUTHENTICATION_FAILED;
+ case rfbVncAuthTooMany:
+ fprintf(stderr,"VNC authentication failed - too many tries\n");
+ return INIT_AUTHENTICATION_FAILED;
+ default:
+ fprintf(stderr,"Unknown VNC authentication result: %d\n",
+ (int)authResult);
+ return INIT_CONNECTION_FAILED;
+ }
+ break;
+
+ default:
+ fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n",
+ (int)authScheme);
+ return INIT_CONNECTION_FAILED;
+ }
+
+ ci.shared = (appData.shareDesktop ? 1 : 0);
+
+ if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) return INIT_CONNECTION_FAILED;
+
+ if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return INIT_CONNECTION_FAILED;
+
+ si.framebufferWidth = Swap16IfLE(si.framebufferWidth);
+ si.framebufferHeight = Swap16IfLE(si.framebufferHeight);
+ si.format.redMax = Swap16IfLE(si.format.redMax);
+ si.format.greenMax = Swap16IfLE(si.format.greenMax);
+ si.format.blueMax = Swap16IfLE(si.format.blueMax);
+ si.nameLength = Swap32IfLE(si.nameLength);
+
+ if ((si.framebufferWidth*si.framebufferHeight) > (4096*4096))
+ return INIT_CONNECTION_FAILED;
+
+ if (si.nameLength > MAX_STRING) {
+ fprintf(stderr, "Display name too long.\n");
+ return INIT_CONNECTION_FAILED;
+ }
+
+ desktopName = malloc(si.nameLength + 1);
+ if (!desktopName) {
+ fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n",
+ (unsigned long)si.nameLength);
+ return INIT_CONNECTION_FAILED;
+ }
+
+ if (!ReadFromRFBServer(desktopName, si.nameLength)) return INIT_CONNECTION_FAILED;
+
+ desktopName[si.nameLength] = 0;
+
+ fprintf(stderr,"Desktop name \"%s\"\n",desktopName);
+
+ fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n",
+ rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+
+ fprintf(stderr,"VNC server default format:\n");
+ PrintPixelFormat(&si.format);
+
+ return INIT_OK;
+}
+
+
+/*
+ * SetFormatAndEncodings.
+ */
+
+Bool
+SetFormatAndEncodings()
+{
+ rfbSetPixelFormatMsg spf;
+ char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
+ rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf;
+ CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]);
+ int len = 0;
+ Bool requestCompressLevel = False;
+ Bool requestQualityLevel = False;
+ Bool requestLastRectEncoding = False;
+
+ spf.type = rfbSetPixelFormat;
+ spf.pad1 = 0;
+ spf.pad2 = 0;
+ spf.format = myFormat;
+ spf.format.redMax = Swap16IfLE(spf.format.redMax);
+ spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
+ spf.format.blueMax = Swap16IfLE(spf.format.blueMax);
+
+ if (!WriteExact(rfbsock, (char *)&spf, sz_rfbSetPixelFormatMsg))
+ return False;
+
+ se->type = rfbSetEncodings;
+ se->pad = 0;
+ se->nEncodings = 0;
+
+ if (appData.encodingsString) {
+ const char *encStr = appData.encodingsString;
+ int encStrLen;
+ do {
+ char *nextEncStr = strchr(encStr, ' ');
+ if (nextEncStr) {
+ encStrLen = nextEncStr - encStr;
+ nextEncStr++;
+ } else {
+ encStrLen = strlen(encStr);
+ }
+
+ if (strncasecmp(encStr,"raw",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw);
+ } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect);
+ } else if (strncasecmp(encStr,"softcursor",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor);
+ /* if server supports SoftCursor, it will ignore X/RichCursor
+ * and PointerPos */
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos);
+ } else if (strncasecmp(encStr,"background",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingBackground);
+ } else if (strncasecmp(encStr,"tight",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight);
+ requestLastRectEncoding = True;
+ if (appData.compressLevel >= 0 && appData.compressLevel <= 9)
+ requestCompressLevel = True;
+ if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9)
+ requestQualityLevel = True;
+ } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
+ } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib);
+ if (appData.compressLevel >= 0 && appData.compressLevel <= 9)
+ requestCompressLevel = True;
+ } else {
+ fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr);
+ }
+
+ encStr = nextEncStr;
+ } while (encStr && se->nEncodings < MAX_ENCODINGS);
+
+ if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) {
+ encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel +
+ rfbEncodingCompressLevel0);
+ }
+
+ if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) {
+ encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel +
+ rfbEncodingQualityLevel0);
+ }
+
+ if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect);
+ }
+ }
+ else {
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw);
+
+ if (appData.compressLevel >= 0 && appData.compressLevel <= 9) {
+ encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel +
+ rfbEncodingCompressLevel0);
+ }
+
+ if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9) {
+ encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel +
+ rfbEncodingQualityLevel0);
+ }
+
+ if (si.format.depth >= 8)
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor);
+ encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect);
+ }
+
+ len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
+
+ se->nEncodings = Swap16IfLE(se->nEncodings);
+
+ if (!WriteExact(rfbsock, buf, len)) return False;
+
+ return True;
+}
+
+
+/*
+ * SendIncrementalFramebufferUpdateRequest.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+SendIncrementalFramebufferUpdateRequest()
+{
+ return SendFramebufferUpdateRequest(0, 0, si.framebufferWidth,
+ si.framebufferHeight, True);
+}
+
+
+/*
+ * SendFramebufferUpdateRequest.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+SendFramebufferUpdateRequest(int x, int y, int w, int h, Bool incremental)
+{
+ rfbFramebufferUpdateRequestMsg fur;
+
+ fur.type = rfbFramebufferUpdateRequest;
+ fur.incremental = incremental ? 1 : 0;
+ fur.x = Swap16IfLE(x);
+ fur.y = Swap16IfLE(y);
+ fur.w = Swap16IfLE(w);
+ fur.h = Swap16IfLE(h);
+
+ if (!WriteExact(rfbsock, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg))
+ return False;
+
+ return True;
+}
+
+
+/*
+ * SendPointerEvent.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+SendPointerEvent(int x, int y, int buttonMask)
+{
+ rfbPointerEventMsg pe;
+
+ pe.type = rfbPointerEvent;
+ pe.buttonMask = buttonMask;
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ pe.x = Swap16IfLE(x);
+ pe.y = Swap16IfLE(y);
+ return WriteExact(rfbsock, (char *)&pe, sz_rfbPointerEventMsg);
+}
+
+
+/*
+ * SendKeyEvent.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+SendKeyEvent(CARD32 key, Bool down)
+{
+ rfbKeyEventMsg ke;
+
+ ke.type = rfbKeyEvent;
+ ke.down = down ? 1 : 0;
+ ke.key = Swap32IfLE(key);
+ return WriteExact(rfbsock, (char *)&ke, sz_rfbKeyEventMsg);
+}
+
+
+/*
+ * SendClientCutText.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+SendClientCutText(const char *str, int len)
+{
+ rfbClientCutTextMsg cct;
+
+ cct.type = rfbClientCutText;
+ cct.length = Swap32IfLE((unsigned int)len);
+ return (WriteExact(rfbsock, (char *)&cct, sz_rfbClientCutTextMsg) &&
+ WriteExact(rfbsock, str, len));
+}
+
+
+static Bool
+HandleSoftCursorSetImage(rfbSoftCursorSetImage *msg, rfbRectangle *rect)
+{
+ int iindex = msg->imageIndex - rfbSoftCursorSetIconOffset;
+ PointerImage *pi = &pointerImages[iindex];
+ if (iindex >= rfbSoftCursorMaxImages) {
+ fprintf(stderr, "Received invalid soft cursor image index %d for SetImage\n", iindex);
+ return False;
+ }
+ EnableClientCursor(0);
+
+ if (pi->set && pi->image)
+ free(pi->image);
+
+ pi->w = rect->w;
+ pi->h = rect->h;
+ pi->hotX = rect->x;
+ pi->hotY = rect->y;
+ pi->len = Swap16IfLE(msg->imageLength);
+ pi->image = malloc(pi->len);
+ if (!pi->image) {
+ fprintf(stderr, "out of memory (size=%d)\n", pi->len);
+ return False;
+ }
+
+ if (!ReadFromRFBServer(pi->image, pi->len))
+ return False;
+ pi->set = 1;
+ return True;
+}
+
+/* framebuffer must be locked when calling this!!! */
+static Bool
+PointerMove(unsigned int x, unsigned int y, unsigned int mask,
+ int ox, int oy, int ow, int oh)
+{
+ int nx, ny, nw, nh;
+
+ if (x >= si.framebufferWidth)
+ x = si.framebufferWidth - 1;
+ if (y >= si.framebufferHeight)
+ y = si.framebufferHeight - 1;
+
+ cursorX = x;
+ cursorY = y;
+ drawCursor();
+ UnlockFramebuffer();
+
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &nx, &ny, &nw, &nh);
+
+ if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) {
+ rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh);
+ SyncScreenRegion(ox, oy, ow, oh);
+ }
+ else {
+ SyncScreenRegion(ox, oy, ow, oh);
+ SyncScreenRegion(nx, ny, nw, nh);
+ }
+
+ postMouseEvent(cursorX, cursorY, mask);
+
+ return True;
+}
+
+static Bool
+HandleSoftCursorMove(rfbSoftCursorMove *msg, rfbRectangle *rect)
+{
+ int ii, ox, oy, ow, oh;
+
+ /* get old cursor rect to know what to update */
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &ox, &oy, &ow, &oh);
+
+ ii = msg->imageIndex;
+ if (ii >= rfbSoftCursorMaxImages) {
+ fprintf(stderr, "Received invalid soft cursor image index %d for Move\n", ii);
+ return False;
+ }
+
+ if (!pointerImages[ii].set)
+ return True;
+
+ LockFramebuffer();
+ undrawCursor();
+ imageIndex = ii;
+
+ return PointerMove(rect->w, rect->h, msg->buttonMask, ox, oy, ow, oh);
+}
+
+static Bool
+HandleCursorPos(unsigned int x, unsigned int y)
+{
+ int ox, oy, ow, oh;
+
+ /* get old cursor rect to know what to update */
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &ox, &oy, &ow, &oh);
+ if (!pointerImages[0].set)
+ return True;
+
+ LockFramebuffer();
+ undrawCursor();
+ imageIndex = 0;
+ return PointerMove(x, y, 0, ox, oy, ow, oh);
+}
+
+/* call only from X11 thread. Only updates framebuffer, does not sync! */
+void DrawCursorX11Thread(int x, int y) {
+ int ox, oy, ow, oh, nx, ny, nw, nh;
+ if (!pointerImages[0].set)
+ return True;
+ imageIndex = 0;
+
+ if (x >= si.framebufferWidth)
+ x = si.framebufferWidth - 1;
+ if (y >= si.framebufferHeight)
+ y = si.framebufferHeight - 1;
+
+ LockFramebuffer();
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &ox, &oy, &ow, &oh);
+ undrawCursor();
+ cursorX = x;
+ cursorY = y;
+ drawCursor();
+ UnlockFramebuffer();
+
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &nx, &ny, &nw, &nh);
+ if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) {
+ rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh);
+ SyncScreenRegionX11Thread(ox, oy, ow, oh);
+ }
+ else {
+ SyncScreenRegionX11Thread(ox, oy, ow, oh);
+ SyncScreenRegionX11Thread(nx, ny, nw, nh);
+ }
+}
+
+/**
+ * Create a softcursor in the "compressed alpha" format.
+ * Returns the softcursor, caller owns the object
+ */
+static void *MakeSoftCursor(int bpp, int cursorWidth, int cursorHeight,
+ CARD8 *cursorData, CARD8 *cursorMask, short *imageLen)
+{
+ int w = (cursorWidth+7)/8;
+ unsigned char *cp, *sp, *dstData;
+ int state; /* 0 = transparent, 1 otherwise */
+ CARD8 *counter;
+ unsigned char bit;
+ int i,j;
+
+ sp = (unsigned char*)cursorData;
+ dstData = cp = (unsigned char*)calloc(cursorWidth*(bpp+2),cursorHeight);
+ if (!dstData)
+ return 0;
+
+ state = 0;
+ counter = cp++;
+ *counter = 0;
+
+ for(j=0;j<cursorHeight;j++)
+ for(i=0,bit=0x80;i<cursorWidth;i++,bit=(bit&1)?0x80:bit>>1)
+ if(cursorMask[j*w+i/8]&bit) {
+ if (state) {
+ memcpy(cp,sp,bpp);
+ cp += bpp;
+ sp += bpp;
+ (*counter)++;
+ if (*counter == 255) {
+ state = 0;
+ counter = cp++;
+ *counter = 0;
+ }
+ }
+ else {
+ state = 1;
+ counter = cp++;
+ *counter = 1;
+ memcpy(cp,sp,bpp);
+ cp += bpp;
+ sp += bpp;
+ }
+ }
+ else {
+ if (!state) {
+ (*counter)++;
+ if (*counter == 255) {
+ state = 1;
+ counter = cp++;
+ *counter = 0;
+ }
+ }
+ else {
+ state = 0;
+ counter = cp++;
+ *counter = 1;
+ }
+ sp += bpp;
+ }
+
+ *imageLen = cp - dstData;
+ return (void*) dstData;
+}
+
+
+/*********************************************************************
+ * HandleCursorShape(). Support for XCursor and RichCursor shape
+ * updates. We emulate cursor operating on the frame buffer (that is
+ * why we call it "software cursor").
+ ********************************************************************/
+
+static Bool HandleCursorShape(int xhot, int yhot, int width, int height, CARD32 enc)
+{
+ int bytesPerPixel;
+ size_t bytesPerRow, bytesMaskData;
+ rfbXCursorColors rgb;
+ CARD32 colors[2];
+ CARD8 *ptr, *rcSource, *rcMask;
+ void *softCursor;
+ int x, y, b;
+ int ox, oy, ow, oh;
+ PointerImage *pi;
+ short imageLen;
+
+ bytesPerPixel = myFormat.bitsPerPixel / 8;
+ bytesPerRow = (width + 7) / 8;
+ bytesMaskData = bytesPerRow * height;
+
+ if (width * height == 0)
+ return True;
+
+ /* Allocate memory for pixel data and temporary mask data. */
+
+ rcSource = malloc(width * height * bytesPerPixel);
+ if (rcSource == NULL)
+ return False;
+
+ rcMask = malloc(bytesMaskData);
+ if (rcMask == NULL) {
+ free(rcSource);
+ return False;
+ }
+
+ /* Read and decode cursor pixel data, depending on the encoding type. */
+
+ if (enc == rfbEncodingXCursor) {
+ /* Read and convert background and foreground colors. */
+ if (!ReadFromRFBServer((char *)&rgb, sz_rfbXCursorColors)) {
+ free(rcSource);
+ free(rcMask);
+ return False;
+ }
+ colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue);
+ colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue);
+
+ /* Read 1bpp pixel data into a temporary buffer. */
+ if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) {
+ free(rcSource);
+ free(rcMask);
+ return False;
+ }
+
+ /* Convert 1bpp data to byte-wide color indices. */
+ ptr = rcSource;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width / 8; x++) {
+ for (b = 7; b >= 0; b--) {
+ *ptr = rcMask[y * bytesPerRow + x] >> b & 1;
+ ptr += bytesPerPixel;
+ }
+ }
+ for (b = 7; b > 7 - width % 8; b--) {
+ *ptr = rcMask[y * bytesPerRow + x] >> b & 1;
+ ptr += bytesPerPixel;
+ }
+ }
+
+ /* Convert indices into the actual pixel values. */
+ switch (bytesPerPixel) {
+ case 1:
+ for (x = 0; x < width * height; x++)
+ rcSource[x] = (CARD8)colors[rcSource[x]];
+ break;
+ case 2:
+ for (x = 0; x < width * height; x++)
+ ((CARD16 *)rcSource)[x] = (CARD16)colors[rcSource[x * 2]];
+ break;
+ case 4:
+ for (x = 0; x < width * height; x++)
+ ((CARD32 *)rcSource)[x] = colors[rcSource[x * 4]];
+ break;
+ }
+
+
+ } else {
+ if (!ReadFromRFBServer((char *)rcSource, width * height * bytesPerPixel)) {
+ free(rcSource);
+ free(rcMask);
+ return False;
+ }
+ }
+
+ /* Read mask data. */
+
+ if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) {
+ free(rcSource);
+ free(rcMask);
+ return False;
+ }
+
+
+ /* Set the soft cursor. */
+ softCursor = MakeSoftCursor(bytesPerPixel, width, height, rcSource, rcMask, &imageLen);
+ if (!softCursor) {
+ free(rcMask);
+ free(rcSource);
+ return False;
+ }
+
+ /* get old cursor rect to know what to update */
+ EnableClientCursor(1);
+ LockFramebuffer();
+ getBoundingRectCursor(cursorX, cursorY, imageIndex,
+ &ox, &oy, &ow, &oh);
+ undrawCursor();
+
+ pi = &pointerImages[0];
+ if (pi->set && pi->image)
+ free(pi->image);
+ pi->w = width;
+ pi->h = height;
+ pi->hotX = xhot;
+ pi->hotY = yhot;
+ pi->len = imageLen;
+ pi->image = softCursor;
+ pi->set = 1;
+
+ imageIndex = 0;
+
+ free(rcMask);
+ free(rcSource);
+
+ return PointerMove(cursorX, cursorY, 0, ox, oy, ow, oh);
+}
+
+
+
+/*
+ * HandleRFBServerMessage.
+ */
+
+Bool
+HandleRFBServerMessage()
+{
+ rfbServerToClientMsg msg;
+ if (!ReadFromRFBServer((char *)&msg, 1))
+ return False;
+
+ switch (msg.type) {
+
+ case rfbSetColourMapEntries:
+ {
+ int i;
+ CARD16 rgb[3];
+ XColor xc;
+
+ if (!ReadFromRFBServer(((char *)&msg) + 1,
+ sz_rfbSetColourMapEntriesMsg - 1))
+ return False;
+
+ msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour);
+ msg.scme.nColours = Swap16IfLE(msg.scme.nColours);
+
+ for (i = 0; i < msg.scme.nColours; i++) {
+ if (!ReadFromRFBServer((char *)rgb, 6))
+ return False;
+ xc.pixel = msg.scme.firstColour + i;
+ xc.red = Swap16IfLE(rgb[0]);
+ xc.green = Swap16IfLE(rgb[1]);
+ xc.blue = Swap16IfLE(rgb[2]);
+ xc.flags = DoRed|DoGreen|DoBlue;
+ /* Disable colormaps
+ lockQt();
+ XStoreColor(dpy, cmap, &xc);
+ unlockQt();
+ */
+ }
+
+ break;
+ }
+
+ case rfbFramebufferUpdate:
+ {
+ rfbFramebufferUpdateRectHeader rect;
+ int linesToRead;
+ int bytesPerLine;
+ int i;
+
+ announceIncrementalUpdateRequest();
+
+ if (!ReadFromRFBServer(((char *)&msg.fu) + 1,
+ sz_rfbFramebufferUpdateMsg - 1))
+ return False;
+
+ msg.fu.nRects = Swap16IfLE(msg.fu.nRects);
+
+ for (i = 0; i < msg.fu.nRects; i++) {
+ if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader))
+ return False;
+
+ rect.encoding = Swap32IfLE(rect.encoding);
+ if (rect.encoding == rfbEncodingLastRect)
+ break;
+
+ rect.r.x = Swap16IfLE(rect.r.x);
+ rect.r.y = Swap16IfLE(rect.r.y);
+ rect.r.w = Swap16IfLE(rect.r.w);
+ rect.r.h = Swap16IfLE(rect.r.h);
+
+ if (rect.encoding == rfbEncodingPointerPos) {
+ if (!HandleCursorPos(rect.r.x, rect.r.y)) {
+ return False;
+ }
+ continue;
+ }
+
+ if (rect.encoding == rfbEncodingXCursor ||
+ rect.encoding == rfbEncodingRichCursor) {
+ if (!HandleCursorShape(rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+ rect.encoding)) {
+ return False;
+ }
+ continue;
+ }
+
+ if ((rect.r.x + rect.r.w > si.framebufferWidth) ||
+ (rect.r.y + rect.r.h > si.framebufferHeight))
+ {
+ fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n",
+ rect.r.w, rect.r.h, rect.r.x, rect.r.y);
+ return False;
+ }
+
+ if ((rect.r.h * rect.r.w == 0) &&
+ (rect.encoding != rfbEncodingSoftCursor)) {
+ fprintf(stderr,"Zero size rect - ignoring\n");
+ continue;
+ }
+
+ switch (rect.encoding) {
+
+ case rfbEncodingRaw:
+
+ bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8;
+ linesToRead = BUFFER_SIZE / bytesPerLine;
+
+ while (rect.r.h > 0) {
+ if (linesToRead > rect.r.h)
+ linesToRead = rect.r.h;
+
+ if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead))
+ return False;
+
+ CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w,
+ linesToRead);
+
+ rect.r.h -= linesToRead;
+ rect.r.y += linesToRead;
+
+ }
+ break;
+
+ case rfbEncodingCopyRect:
+ {
+ rfbCopyRect cr;
+
+ if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect))
+ return False;
+
+ cr.srcX = Swap16IfLE(cr.srcX);
+ cr.srcY = Swap16IfLE(cr.srcY);
+
+ CopyArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h, rect.r.x, rect.r.y);
+
+ break;
+ }
+
+ case rfbEncodingHextile:
+ {
+ switch (myFormat.bitsPerPixel) {
+ case 8:
+ if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 16:
+ if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 32:
+ if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingZlib:
+ {
+ switch (myFormat.bitsPerPixel) {
+ case 8:
+ if (!HandleZlib8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 16:
+ if (!HandleZlib16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 32:
+ if (!HandleZlib32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingTight:
+ {
+ switch (myFormat.bitsPerPixel) {
+ case 8:
+ if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 16:
+ if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ case 32:
+ if (!HandleTight32(rect.r.x,rect.r.y,rect.r.w,rect.r.h))
+ return False;
+ break;
+ }
+ break;
+ }
+
+ case rfbEncodingSoftCursor:
+ {
+ rfbSoftCursorMsg scmsg;
+ if (!ReadFromRFBServer((char *)&scmsg, 1))
+ return False;
+ if (scmsg.type < rfbSoftCursorMaxImages) {
+ if (!ReadFromRFBServer(((char *)&scmsg)+1,
+ sizeof(rfbSoftCursorMove)- 1))
+ return False;
+ if (!HandleSoftCursorMove(&scmsg.move, &rect.r))
+ return False;
+ }
+ else if((scmsg.type >= rfbSoftCursorSetIconOffset) &&
+ (scmsg.type < rfbSoftCursorSetIconOffset+rfbSoftCursorMaxImages)) {
+ if (!ReadFromRFBServer(((char *)&scmsg)+1,
+ sizeof(rfbSoftCursorSetImage)- 1))
+ return False;
+ if (!HandleSoftCursorSetImage(&scmsg.setImage, &rect.r))
+ return False;
+ }
+ else {
+ fprintf(stderr,"Unknown soft cursor image index %d\n",
+ (int)scmsg.type);
+ return False;
+ }
+ break;
+ }
+
+ default:
+ fprintf(stderr,"Unknown rect encoding %d\n",
+ (int)rect.encoding);
+ return False;
+ }
+
+ }
+
+ queueIncrementalUpdateRequest();
+
+ break;
+ }
+
+ case rfbBell:
+ {
+ beep();
+ break;
+ }
+
+ case rfbServerCutText:
+ {
+ char *serverCutText;
+ if (!ReadFromRFBServer(((char *)&msg) + 1,
+ sz_rfbServerCutTextMsg - 1))
+ return False;
+
+ msg.sct.length = Swap32IfLE(msg.sct.length);
+
+ if (msg.sct.length > MAX_CUTBUFFER) {
+ fprintf(stderr, "Cutbuffer too long.\n");
+ return False;
+ }
+
+ serverCutText = malloc(msg.sct.length+1);
+
+ if (!serverCutText) {
+ fprintf(stderr, "Out-of-memory, cutbuffer too long.\n");
+ return False;
+ }
+
+ if (!ReadFromRFBServer(serverCutText, msg.sct.length))
+ return False;
+
+ serverCutText[msg.sct.length] = 0;
+ newServerCut(serverCutText, msg.sct.length); /* takes ownership of serverCutText */
+
+ break;
+ }
+
+ default:
+ fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type);
+ return False;
+ }
+
+ return True;
+}
+
+
+#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++)
+
+#define GET_PIXEL16(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \
+ ((CARD8*)&(pix))[1] = *(ptr)++)
+
+#define GET_PIXEL32(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \
+ ((CARD8*)&(pix))[1] = *(ptr)++, \
+ ((CARD8*)&(pix))[2] = *(ptr)++, \
+ ((CARD8*)&(pix))[3] = *(ptr)++)
+
+/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also
+ expands its arguments if they are macros */
+
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+
+#define BPP 8
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+#define BPP 16
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+#define BPP 32
+#include "hextile.c"
+#include "zlib.c"
+#include "tight.c"
+#undef BPP
+
+
+/*
+ * PrintPixelFormat.
+ */
+
+void
+PrintPixelFormat(format)
+ rfbPixelFormat *format;
+{
+ if (format->bitsPerPixel == 1) {
+ fprintf(stderr," Single bit per pixel.\n");
+ fprintf(stderr,
+ " %s significant bit in each byte is leftmost on the screen.\n",
+ (format->bigEndian ? "Most" : "Least"));
+ } else {
+ fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel);
+ if (format->bitsPerPixel != 8) {
+ fprintf(stderr," %s significant byte first in each pixel.\n",
+ (format->bigEndian ? "Most" : "Least"));
+ }
+ if (format->trueColour) {
+ fprintf(stderr," True colour: max red %d green %d blue %d",
+ format->redMax, format->greenMax, format->blueMax);
+ fprintf(stderr,", shift red %d green %d blue %d\n",
+ format->redShift, format->greenShift, format->blueShift);
+ } else {
+ fprintf(stderr," Colour map (not true colour).\n");
+ }
+ }
+}
+
+static long
+ReadCompactLen (void)
+{
+ long len;
+ CARD8 b;
+
+ if (!ReadFromRFBServer((char *)&b, 1))
+ return -1;
+ len = (int)b & 0x7F;
+ if (b & 0x80) {
+ if (!ReadFromRFBServer((char *)&b, 1))
+ return -1;
+ len |= ((int)b & 0x7F) << 7;
+ if (b & 0x80) {
+ if (!ReadFromRFBServer((char *)&b, 1))
+ return -1;
+ len |= ((int)b & 0xFF) << 14;
+ }
+ }
+ return len;
+}
+
+void freeRFBProtoResources() {
+ int i;
+
+ if (desktopName)
+ free(desktopName);
+ if (raw_buffer)
+ free(raw_buffer);
+ for (i = 0; i < rfbSoftCursorMaxImages; i++)
+ if (pointerImages[i].set && pointerImages[i].image)
+ free(pointerImages[i].image);
+
+ raw_buffer_size = -1;
+ raw_buffer = NULL;
+ decompStreamInited = False;
+ zlibStreamActive[0] = False;
+ zlibStreamActive[1] = False;
+ zlibStreamActive[2] = False;
+ zlibStreamActive[3] = False;
+ for (i = 0; i < rfbSoftCursorMaxImages; i++)
+ pointerImages[i].set = 0;
+ imageIndex = -1;
+}
+
+void freeResources() {
+ freeSocketsResources();
+ freeDesktopResources();
+ freeRFBProtoResources();
+}
+
+/*
+ * JPEG source manager functions for JPEG decompression in Tight decoder.
+ */
+
+static struct jpeg_source_mgr jpegSrcManager;
+static JOCTET *jpegBufferPtr;
+static size_t jpegBufferLen;
+
+static void
+JpegInitSource(j_decompress_ptr cinfo)
+{
+ jpegError = False;
+}
+
+static boolean
+JpegFillInputBuffer(j_decompress_ptr cinfo)
+{
+ jpegError = True;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+
+ return TRUE;
+}
+
+static void
+JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
+{
+ if (num_bytes < 0 || num_bytes > jpegSrcManager.bytes_in_buffer) {
+ jpegError = True;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+ jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr;
+ } else {
+ jpegSrcManager.next_input_byte += (size_t) num_bytes;
+ jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+static void
+JpegTermSource(j_decompress_ptr cinfo)
+{
+ /* No work necessary here. */
+}
+
+static void
+JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData,
+ int compressedLen)
+{
+ jpegBufferPtr = (JOCTET *)compressedData;
+ jpegBufferLen = (size_t)compressedLen;
+
+ jpegSrcManager.init_source = JpegInitSource;
+ jpegSrcManager.fill_input_buffer = JpegFillInputBuffer;
+ jpegSrcManager.skip_input_data = JpegSkipInputData;
+ jpegSrcManager.resync_to_restart = jpeg_resync_to_restart;
+ jpegSrcManager.term_source = JpegTermSource;
+ jpegSrcManager.next_input_byte = jpegBufferPtr;
+ jpegSrcManager.bytes_in_buffer = jpegBufferLen;
+
+ cinfo->src = &jpegSrcManager;
+}
+
diff --git a/krdc/vnc/rfbproto.h b/krdc/vnc/rfbproto.h
new file mode 100644
index 00000000..61ceb903
--- /dev/null
+++ b/krdc/vnc/rfbproto.h
@@ -0,0 +1,957 @@
+/*
+ * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * rfbproto.h - header file for the RFB protocol version 3.3
+ *
+ * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed
+ * integer (for n = 8, 16 and 32).
+ *
+ * All multiple byte integers are in big endian (network) order (most
+ * significant byte first). Unless noted otherwise there is no special
+ * alignment of protocol structures.
+ *
+ *
+ * Once the initial handshaking is done, all messages start with a type byte,
+ * (usually) followed by message-specific data. The order of definitions in
+ * this file is as follows:
+ *
+ * (1) Structures used in several types of message.
+ * (2) Structures used in the initial handshaking.
+ * (3) Message types.
+ * (4) Encoding types.
+ * (5) For each message type, the form of the data following the type byte.
+ * Sometimes this is defined by a single structure but the more complex
+ * messages have to be explained by comments.
+ */
+
+#include "vnctypes.h"
+
+/*****************************************************************************
+ *
+ * Structures used in several messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify a rectangle. This structure is a multiple of 4
+ * bytes so that it can be interspersed with 32-bit pixel data without
+ * affecting alignment.
+ */
+
+typedef struct {
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbRectangle;
+
+#define sz_rfbRectangle 8
+
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify pixel format.
+ */
+
+typedef struct {
+
+ CARD8 bitsPerPixel; /* 8,16,32 only */
+
+ CARD8 depth; /* 8 to 32 */
+
+ CARD8 bigEndian; /* True if multi-byte pixels are interpreted
+ as big endian, or if single-bit-per-pixel
+ has most significant bit of the byte
+ corresponding to first (leftmost) pixel. Of
+ course this is meaningless for 8 bits/pix */
+
+ CARD8 trueColour; /* If false then we need a "colour map" to
+ convert pixels to RGB. If true, xxxMax and
+ xxxShift specify bits used for red, green
+ and blue */
+
+ /* the following fields are only meaningful if trueColour is true */
+
+ CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the
+ number of bits used for red). Note this
+ value is always in big endian order. */
+
+ CARD16 greenMax; /* similar for green */
+
+ CARD16 blueMax; /* and blue */
+
+ CARD8 redShift; /* number of shifts needed to get the red
+ value in a pixel to the least significant
+ bit. To find the red value from a given
+ pixel, do the following:
+ 1) Swap pixel value according to bigEndian
+ (e.g. if bigEndian is false and host byte
+ order is big endian, then swap).
+ 2) Shift right by redShift.
+ 3) AND with redMax (in host byte order).
+ 4) You now have the red value between 0 and
+ redMax. */
+
+ CARD8 greenShift; /* similar for green */
+
+ CARD8 blueShift; /* and blue */
+
+ CARD8 pad1;
+ CARD16 pad2;
+
+} rfbPixelFormat;
+
+#define sz_rfbPixelFormat 16
+
+
+
+/*****************************************************************************
+ *
+ * Initial handshaking messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Protocol Version
+ *
+ * The server always sends 12 bytes to start which identifies the latest RFB
+ * protocol version number which it supports. These bytes are interpreted
+ * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where
+ * xxx and yyy are the major and minor version numbers (for version 3.3
+ * this is "RFB 003.003\n").
+ *
+ * The client then replies with a similar 12-byte message giving the version
+ * number of the protocol which should actually be used (which may be different
+ * to that quoted by the server).
+ *
+ * It is intended that both clients and servers may provide some level of
+ * backwards compatibility by this mechanism. Servers in particular should
+ * attempt to provide backwards compatibility, and even forwards compatibility
+ * to some extent. For example if a client demands version 3.1 of the
+ * protocol, a 3.0 server can probably assume that by ignoring requests for
+ * encoding types it doesn't understand, everything will still work OK. This
+ * will probably not be the case for changes in the major version number.
+ *
+ * The format string below can be used in sprintf or sscanf to generate or
+ * decode the version string respectively.
+ */
+
+#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
+#define rfbProtocolMajorVersion 3
+#define rfbProtocolMinorVersion 3
+
+typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
+
+#define sz_rfbProtocolVersionMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * Authentication
+ *
+ * Once the protocol version has been decided, the server then sends a 32-bit
+ * word indicating whether any authentication is needed on the connection.
+ * The value of this word determines the authentication scheme in use. For
+ * version 3.0 of the protocol this may have one of the following values:
+ */
+
+#define rfbConnFailed 0
+#define rfbNoAuth 1
+#define rfbVncAuth 2
+
+/*
+ * rfbConnFailed: For some reason the connection failed (e.g. the server
+ * cannot support the desired protocol version). This is
+ * followed by a string describing the reason (where a
+ * string is specified as a 32-bit length followed by that
+ * many ASCII characters).
+ *
+ * rfbNoAuth: No authentication is needed.
+ *
+ * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte
+ * challenge follows, which the client encrypts as
+ * appropriate using the password and sends the resulting
+ * 16-byte response. If the response is correct, the
+ * server sends the 32-bit word rfbVncAuthOK. If a simple
+ * failure happens, the server sends rfbVncAuthFailed and
+ * closes the connection. If the server decides that too
+ * many failures have occurred, it sends rfbVncAuthTooMany
+ * and closes the connection. In the latter case, the
+ * server should not allow an immediate reconnection by
+ * the client.
+ */
+
+#define rfbVncAuthOK 0
+#define rfbVncAuthFailed 1
+#define rfbVncAuthTooMany 2
+
+
+/*-----------------------------------------------------------------------------
+ * Client Initialisation Message
+ *
+ * Once the client and server are sure that they're happy to talk to one
+ * another, the client sends an initialisation message. At present this
+ * message only consists of a boolean indicating whether the server should try
+ * to share the desktop by leaving other clients connected, or give exclusive
+ * access to this client by disconnecting all other clients.
+ */
+
+typedef struct {
+ CARD8 shared;
+} rfbClientInitMsg;
+
+#define sz_rfbClientInitMsg 1
+
+
+/*-----------------------------------------------------------------------------
+ * Server Initialisation Message
+ *
+ * After the client initialisation message, the server sends one of its own.
+ * This tells the client the width and height of the server's framebuffer,
+ * its pixel format and the name associated with the desktop.
+ */
+
+typedef struct {
+ CARD16 framebufferWidth;
+ CARD16 framebufferHeight;
+ rfbPixelFormat format; /* the server's preferred pixel format */
+ CARD32 nameLength;
+ /* followed by char name[nameLength] */
+} rfbServerInitMsg;
+
+#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+
+
+/*
+ * Following the server initialisation message it's up to the client to send
+ * whichever protocol messages it wants. Typically it will send a
+ * SetPixelFormat message and a SetEncodings message, followed by a
+ * FramebufferUpdateRequest. From then on the server will send
+ * FramebufferUpdate messages in response to the client's
+ * FramebufferUpdateRequest messages. The client should send
+ * FramebufferUpdateRequest messages with incremental set to true when it has
+ * finished processing one FramebufferUpdate and is ready to process another.
+ * With a fast client, the rate at which FramebufferUpdateRequests are sent
+ * should be regulated to avoid hogging the network.
+ */
+
+
+
+/*****************************************************************************
+ *
+ * Message types
+ *
+ *****************************************************************************/
+
+/* server -> client */
+
+#define rfbFramebufferUpdate 0
+#define rfbSetColourMapEntries 1
+#define rfbBell 2
+#define rfbServerCutText 3
+
+
+/* client -> server */
+
+#define rfbSetPixelFormat 0
+#define rfbFixColourMapEntries 1 /* not currently supported */
+#define rfbSetEncodings 2
+#define rfbFramebufferUpdateRequest 3
+#define rfbKeyEvent 4
+#define rfbPointerEvent 5
+#define rfbClientCutText 6
+
+
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define rfbEncodingRaw 0
+#define rfbEncodingCopyRect 1
+#define rfbEncodingRRE 2
+#define rfbEncodingCoRRE 4
+#define rfbEncodingHextile 5
+#define rfbEncodingZlib 6
+#define rfbEncodingTight 7
+#define rfbEncodingZlibHex 8
+
+/*
+ * Special encoding numbers:
+ * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels;
+ * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data;
+ * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions;
+ * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet;
+ * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor;
+ * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels.
+ */
+
+#define rfbEncodingCompressLevel0 0xFFFFFF00
+#define rfbEncodingCompressLevel1 0xFFFFFF01
+#define rfbEncodingCompressLevel2 0xFFFFFF02
+#define rfbEncodingCompressLevel3 0xFFFFFF03
+#define rfbEncodingCompressLevel4 0xFFFFFF04
+#define rfbEncodingCompressLevel5 0xFFFFFF05
+#define rfbEncodingCompressLevel6 0xFFFFFF06
+#define rfbEncodingCompressLevel7 0xFFFFFF07
+#define rfbEncodingCompressLevel8 0xFFFFFF08
+#define rfbEncodingCompressLevel9 0xFFFFFF09
+
+#define rfbEncodingXCursor 0xFFFFFF10
+#define rfbEncodingRichCursor 0xFFFFFF11
+#define rfbEncodingSoftCursor 0xFFFFFF12
+#define rfbEncodingPointerPos 0xFFFFFF18
+
+#define rfbEncodingLastRect 0xFFFFFF20
+#define rfbEncodingBackground 0xFFFFFF25
+
+#define rfbEncodingQualityLevel0 0xFFFFFFE0
+#define rfbEncodingQualityLevel1 0xFFFFFFE1
+#define rfbEncodingQualityLevel2 0xFFFFFFE2
+#define rfbEncodingQualityLevel3 0xFFFFFFE3
+#define rfbEncodingQualityLevel4 0xFFFFFFE4
+#define rfbEncodingQualityLevel5 0xFFFFFFE5
+#define rfbEncodingQualityLevel6 0xFFFFFFE6
+#define rfbEncodingQualityLevel7 0xFFFFFFE7
+#define rfbEncodingQualityLevel8 0xFFFFFFE8
+#define rfbEncodingQualityLevel9 0xFFFFFFE9
+
+
+/*****************************************************************************
+ *
+ * Server -> client message definitions
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdate - a block of rectangles to be copied to the framebuffer.
+ *
+ * This message consists of a header giving the number of rectangles of pixel
+ * data followed by the rectangles themselves. The header is padded so that
+ * together with the type byte it is an exact multiple of 4 bytes (to help
+ * with alignment of 32-bit pixels):
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdate */
+ CARD8 pad;
+ CARD16 nRects;
+ /* followed by nRects rectangles */
+} rfbFramebufferUpdateMsg;
+
+#define sz_rfbFramebufferUpdateMsg 4
+
+/*
+ * Each rectangle of pixel data consists of a header describing the position
+ * and size of the rectangle and a type word describing the encoding of the
+ * pixel data, followed finally by the pixel data. Note that if the client has
+ * not sent a SetEncodings message then it will only receive raw pixel data.
+ * Also note again that this structure is a multiple of 4 bytes.
+ */
+
+typedef struct {
+ rfbRectangle r;
+ CARD32 encoding; /* one of the encoding types rfbEncoding... */
+} rfbFramebufferUpdateRectHeader;
+
+#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Raw Encoding. Pixels are sent in top-to-bottom scanline order,
+ * left-to-right within a scanline with no padding in between.
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CopyRect Encoding. The pixels are specified simply by the x and y position
+ * of the source rectangle.
+ */
+
+typedef struct {
+ CARD16 srcX;
+ CARD16 srcY;
+} rfbCopyRect;
+
+#define sz_rfbCopyRect 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure
+ * giving the number of subrectangles following. Finally the data follows in
+ * the form [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbRectangle>].
+ */
+
+typedef struct {
+ CARD32 nSubrects;
+} rfbRREHeader;
+
+#define sz_rfbRREHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving
+ * the number of subrectangles following. Finally the data follows in the form
+ * [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbCoRRERectangle>]. This means that
+ * the whole rectangle must be at most 255x255 pixels.
+ */
+
+typedef struct {
+ CARD8 x;
+ CARD8 y;
+ CARD8 w;
+ CARD8 h;
+} rfbCoRRERectangle;
+
+#define sz_rfbCoRRERectangle 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels,
+ * starting at the top left going in left-to-right, top-to-bottom order. If
+ * the width of the rectangle is not an exact multiple of 16 then the width of
+ * the last tile in each row will be correspondingly smaller. Similarly if the
+ * height is not an exact multiple of 16 then the height of each tile in the
+ * final row will also be smaller. Each tile begins with a "subencoding" type
+ * byte, which is a mask made up of a number of bits. If the Raw bit is set
+ * then the other bits are irrelevant; w*h pixel values follow (where w and h
+ * are the width and height of the tile). Otherwise the tile is encoded in a
+ * similar way to RRE, except that the position and size of each subrectangle
+ * can be specified in just two bytes. The other bits in the mask are as
+ * follows:
+ *
+ * BackgroundSpecified - if set, a pixel value follows which specifies
+ * the background colour for this tile. The first non-raw tile in a
+ * rectangle must have this bit set. If this bit isn't set then the
+ * background is the same as the last tile.
+ *
+ * ForegroundSpecified - if set, a pixel value follows which specifies
+ * the foreground colour to be used for all subrectangles in this tile.
+ * If this bit is set then the SubrectsColoured bit must be zero.
+ *
+ * AnySubrects - if set, a single byte follows giving the number of
+ * subrectangles following. If not set, there are no subrectangles (i.e.
+ * the whole tile is just solid background colour).
+ *
+ * SubrectsColoured - if set then each subrectangle is preceded by a pixel
+ * value giving the colour of that subrectangle. If not set, all
+ * subrectangles are the same colour, the foreground colour; if the
+ * ForegroundSpecified bit wasn't set then the foreground is the same as
+ * the last tile.
+ *
+ * The position and size of each subrectangle is specified in two bytes. The
+ * Pack macros below can be used to generate the two bytes from x, y, w, h,
+ * and the Extract macros can be used to extract the x, y, w, h values from
+ * the two bytes.
+ */
+
+#define rfbHextileRaw (1 << 0)
+#define rfbHextileBackgroundSpecified (1 << 1)
+#define rfbHextileForegroundSpecified (1 << 2)
+#define rfbHextileAnySubrects (1 << 3)
+#define rfbHextileSubrectsColoured (1 << 4)
+
+#define rfbHextilePackXY(x,y) (((x) << 4) | (y))
+#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1))
+#define rfbHextileExtractX(byte) ((byte) >> 4)
+#define rfbHextileExtractY(byte) ((byte) & 0xf)
+#define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
+#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure
+ * giving the number of bytes following. Finally the data follows is
+ * zlib compressed version of the raw pixel data as negotiated.
+ */
+
+typedef struct {
+ CARD32 nBytes;
+} rfbZlibHeader;
+
+#define sz_rfbZlibHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Tight Encoding.
+ *
+ *-- The first byte of each Tight-encoded rectangle is a "compression control
+ * byte". Its format is as follows (bit 0 is the least significant one):
+ *
+ * bit 0: if 1, then compression stream 0 should be reset;
+ * bit 1: if 1, then compression stream 1 should be reset;
+ * bit 2: if 1, then compression stream 2 should be reset;
+ * bit 3: if 1, then compression stream 3 should be reset;
+ * bits 7-4: if 1000 (0x08), then the compression type is "fill",
+ * if 1001 (0x09), then the compression type is "jpeg",
+ * if 0xxx, then the compression type is "basic",
+ * values greater than 1001 are not valid.
+ *
+ * If the compression type is "basic", then bits 6..4 of the
+ * compression control byte (those xxx in 0xxx) specify the following:
+ *
+ * bits 5-4: decimal representation is the index of a particular zlib
+ * stream which should be used for decompressing the data;
+ * bit 6: if 1, then a "filter id" byte is following this byte.
+ *
+ *-- The data that follows after the compression control byte described
+ * above depends on the compression type ("fill", "jpeg" or "basic").
+ *
+ *-- If the compression type is "fill", then the only pixel value follows, in
+ * client pixel format (see NOTE 1). This value applies to all pixels of the
+ * rectangle.
+ *
+ *-- If the compression type is "jpeg", the following data stream looks like
+ * this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: JPEG image.
+ *
+ * Data size is compactly represented in one, two or three bytes, according
+ * to the following scheme:
+ *
+ * 0xxxxxxx (for values 0..127)
+ * 1xxxxxxx 0yyyyyyy (for values 128..16383)
+ * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303)
+ *
+ * Here each character denotes one bit, xxxxxxx are the least significant 7
+ * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the
+ * most significant 8 bits (bits 14-21). For example, decimal value 10000
+ * should be represented as two bytes: binary 10010000 01001110, or
+ * hexadecimal 90 4E.
+ *
+ *-- If the compression type is "basic" and bit 6 of the compression control
+ * byte was set to 1, then the next (second) byte specifies "filter id" which
+ * tells the decoder what filter type was used by the encoder to pre-process
+ * pixel data before the compression. The "filter id" byte can be one of the
+ * following:
+ *
+ * 0: no filter ("copy" filter);
+ * 1: "palette" filter;
+ * 2: "gradient" filter.
+ *
+ *-- If bit 6 of the compression control byte is set to 0 (no "filter id"
+ * byte), or if the filter id is 0, then raw pixel values in the client
+ * format (see NOTE 1) will be compressed. See below details on the
+ * compression.
+ *
+ *-- The "gradient" filter pre-processes pixel data with a simple algorithm
+ * which converts each color component to a difference between a "predicted"
+ * intensity and the actual intensity. Such a technique does not affect
+ * uncompressed data size, but helps to compress photo-like images better.
+ * Pseudo-code for converting intensities to differences is the following:
+ *
+ * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
+ * if (P[i,j] < 0) then P[i,j] := 0;
+ * if (P[i,j] > MAX) then P[i,j] := MAX;
+ * D[i,j] := V[i,j] - P[i,j];
+ *
+ * Here V[i,j] is the intensity of a color component for a pixel at
+ * coordinates (i,j). MAX is the maximum value of intensity for a color
+ * component.
+ *
+ *-- The "palette" filter converts true-color pixel data to indexed colors
+ * and a palette which can consist of 2..256 colors. If the number of colors
+ * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to
+ * encode one pixel. 1-bit encoding is performed such way that the most
+ * significant bits correspond to the leftmost pixels, and each raw of pixels
+ * is aligned to the byte boundary. When "palette" filter is used, the
+ * palette is sent before the pixel data. The palette begins with an unsigned
+ * byte which value is the number of colors in the palette minus 1 (i.e. 1
+ * means 2 colors, 255 means 256 colors in the palette). Then follows the
+ * palette itself which consist of pixel values in client pixel format (see
+ * NOTE 1).
+ *
+ *-- The pixel data is compressed using the zlib library. But if the data
+ * size after applying the filter but before the compression is less then 12,
+ * then the data is sent as is, uncompressed. Four separate zlib streams
+ * (0..3) can be used and the decoder should read the actual stream id from
+ * the compression control byte (see NOTE 2).
+ *
+ * If the compression is not used, then the pixel data is sent as is,
+ * otherwise the data stream looks like this:
+ *
+ * 1..3 bytes: data size (N) in compact representation;
+ * N bytes: zlib-compressed data.
+ *
+ * Data size is compactly represented in one, two or three bytes, just like
+ * in the "jpeg" compression method (see above).
+ *
+ *-- NOTE 1. If the color depth is 24, and all three color components are
+ * 8-bit wide, then one pixel in Tight encoding is always represented by
+ * three bytes, where the first byte is red component, the second byte is
+ * green component, and the third byte is blue component of the pixel color
+ * value. This applies to colors in palettes as well.
+ *
+ *-- NOTE 2. The decoder must reset compression streams' states before
+ * decoding the rectangle, if some of bits 0,1,2,3 in the compression control
+ * byte are set to 1. Note that the decoder must reset zlib streams even if
+ * the compression type is "fill" or "jpeg".
+ *
+ *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only
+ * when bits-per-pixel value is either 16 or 32, not 8.
+ *
+ *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048
+ * pixels. If a rectangle is wider, it must be split into several rectangles
+ * and each one should be encoded separately.
+ *
+ */
+
+#define rfbTightExplicitFilter 0x04
+#define rfbTightFill 0x08
+#define rfbTightJpeg 0x09
+#define rfbTightMaxSubencoding 0x09
+
+/* Filters to improve compression efficiency */
+#define rfbTightFilterCopy 0x00
+#define rfbTightFilterPalette 0x01
+#define rfbTightFilterGradient 0x02
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * XCursor encoding. This is a special encoding used to transmit X-style
+ * cursor shapes from server to clients. Note that for this encoding,
+ * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot
+ * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB
+ * samples are sent after header in the rfbXCursorColors structure. They
+ * denote foreground and background colors of the cursor. If a client
+ * supports only black-and-white cursors, it should ignore these colors and
+ * assume that foreground is black and background is white. Next, two bitmaps
+ * (1 bits per pixel) follow: first one with actual data (value 0 denotes
+ * background color, value 1 denotes foreground color), second one with
+ * transparency data (bits with zero value mean that these pixels are
+ * transparent). Both bitmaps represent cursor data in a byte stream, from
+ * left to right, from top to bottom, and each row is byte-aligned. Most
+ * significant bits correspond to leftmost pixels. The number of bytes in
+ * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor
+ * should be hidden (or default local cursor should be set by the client).
+ */
+
+typedef struct {
+ CARD8 foreRed;
+ CARD8 foreGreen;
+ CARD8 foreBlue;
+ CARD8 backRed;
+ CARD8 backGreen;
+ CARD8 backBlue;
+} rfbXCursorColors;
+
+#define sz_rfbXCursorColors 6
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RichCursor encoding. This is a special encoding used to transmit cursor
+ * shapes from server to clients. It is similar to the XCursor encoding but
+ * uses client pixel format instead of two RGB colors to represent cursor
+ * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader
+ * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h).
+ * After header, two pixmaps follow: first one with cursor image in current
+ * client pixel format (like in raw encoding), second with transparency data
+ * (1 bit per pixel, exactly the same format as used for transparency bitmap
+ * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or
+ * default local cursor should be set by the client).
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * SoftCursor encoding. This encoding is used to transmit image and position
+ * of the remote cursor. It has two sub-messages: SetImage is used to upload
+ * one of 16 images, and Move selects the image and sets the position of the
+ * cursor.
+ * Each SoftCursor message starts with a CARD8. If it is in the 0-15 range
+ * it specifies the number of the cursor image and is followed by the
+ * rfbSoftCursorMove message. If the given cursor has not been set yet the
+ * message will be ignored. If the first CARD8 is in the 128-143 range it
+ * specifies the cursor that will be set in the following
+ * rfbSoftCursorSetImage message. To hide the cursor send a SetImage
+ * message with width and height 0 and imageLength 0.
+ * SetImage transmits the hotspot coordinates in the x/y fields of the
+ * rfbFramebufferUpdateRectHeader, width and height of the image are in the
+ * header's width and height fields.
+ * Move transmits the pointer coordinates in the w/h fields of the
+ * header, x/y are always 0.
+ */
+
+typedef struct {
+ CARD8 imageIndex;
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+} rfbSoftCursorMove;
+
+typedef struct {
+ CARD8 imageIndex;
+ CARD8 padding;
+ CARD16 imageLength;
+ /*
+ * Followed by an image of the cursor in the client's image format
+ * with the following RLE mask compression. It begins with CARD8 that
+ * specifies the number of mask'ed pixels that will be NOT transmitted.
+ * Then follows a CARD8 that specified by the number of unmask'd pixels
+ * that will be transmitted next. Then a CARD8 with the number of mask'd
+ * pixels and so on.
+ */
+} rfbSoftCursorSetImage;
+
+typedef union {
+ CARD8 type;
+ rfbSoftCursorMove move;
+ rfbSoftCursorSetImage setImage;
+} rfbSoftCursorMsg;
+
+#define rfbSoftCursorMaxImages 16
+#define rfbSoftCursorSetIconOffset 128
+
+/*-----------------------------------------------------------------------------
+ * SetColourMapEntries - these messages are only sent if the pixel
+ * format uses a "colour map" (i.e. trueColour false) and the client has not
+ * fixed the entire colour map using FixColourMapEntries. In addition they
+ * will only start being sent after the client has sent its first
+ * FramebufferUpdateRequest. So if the client always tells the server to use
+ * trueColour then it never needs to process this type of message.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbSetColourMapEntriesMsg;
+
+#define sz_rfbSetColourMapEntriesMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * Bell - ring a bell on the client if it has one.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbBell */
+} rfbBellMsg;
+
+#define sz_rfbBellMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ServerCutText - the server has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbServerCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbServerCutTextMsg;
+
+#define sz_rfbServerCutTextMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all server->client messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbFramebufferUpdateMsg fu;
+ rfbSetColourMapEntriesMsg scme;
+ rfbBellMsg b;
+ rfbServerCutTextMsg sct;
+} rfbServerToClientMsg;
+
+
+
+/*****************************************************************************
+ *
+ * Message definitions (client -> server)
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * SetPixelFormat - tell the RFB server the format in which the client wants
+ * pixels sent.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetPixelFormat */
+ CARD8 pad1;
+ CARD16 pad2;
+ rfbPixelFormat format;
+} rfbSetPixelFormatMsg;
+
+#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4)
+
+
+/*-----------------------------------------------------------------------------
+ * FixColourMapEntries - when the pixel format uses a "colour map", fix
+ * read-only colour map entries.
+ *
+ * ***************** NOT CURRENTLY SUPPORTED *****************
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFixColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbFixColourMapEntriesMsg;
+
+#define sz_rfbFixColourMapEntriesMsg 6
+
+
+/*-----------------------------------------------------------------------------
+ * SetEncodings - tell the RFB server which encoding types we accept. Put them
+ * in order of preference, if we have any. We may always receive raw
+ * encoding, even if we don't specify it here.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetEncodings */
+ CARD8 pad;
+ CARD16 nEncodings;
+ /* followed by nEncodings * CARD32 encoding types */
+} rfbSetEncodingsMsg;
+
+#define sz_rfbSetEncodingsMsg 4
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdateRequest - request for a framebuffer update. If incremental
+ * is true then the client just wants the changes since the last update. If
+ * false then it wants the whole of the specified rectangle.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdateRequest */
+ CARD8 incremental;
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbFramebufferUpdateRequestMsg;
+
+#define sz_rfbFramebufferUpdateRequestMsg 10
+
+
+/*-----------------------------------------------------------------------------
+ * KeyEvent - key press or release
+ *
+ * Keys are specified using the "keysym" values defined by the X Window System.
+ * For most ordinary keys, the keysym is the same as the corresponding ASCII
+ * value. Other common keys are:
+ *
+ * BackSpace 0xff08
+ * Tab 0xff09
+ * Return or Enter 0xff0d
+ * Escape 0xff1b
+ * Insert 0xff63
+ * Delete 0xffff
+ * Home 0xff50
+ * End 0xff57
+ * Page Up 0xff55
+ * Page Down 0xff56
+ * Left 0xff51
+ * Up 0xff52
+ * Right 0xff53
+ * Down 0xff54
+ * F1 0xffbe
+ * F2 0xffbf
+ * ... ...
+ * F12 0xffc9
+ * Shift 0xffe1
+ * Control 0xffe3
+ * Meta 0xffe7
+ * Alt 0xffe9
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbKeyEvent */
+ CARD8 down; /* true if down (press), false if up */
+ CARD16 pad;
+ CARD32 key; /* key is specified as an X keysym */
+} rfbKeyEventMsg;
+
+#define sz_rfbKeyEventMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * PointerEvent - mouse/pen move and/or button press.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbPointerEvent */
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+ CARD16 x;
+ CARD16 y;
+} rfbPointerEventMsg;
+
+#define rfbButton1Mask 1
+#define rfbButton2Mask 2
+#define rfbButton3Mask 4
+
+#define sz_rfbPointerEventMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * ClientCutText - the client has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbClientCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbClientCutTextMsg;
+
+#define sz_rfbClientCutTextMsg 8
+
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all client->server messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbSetPixelFormatMsg spf;
+ rfbFixColourMapEntriesMsg fcme;
+ rfbSetEncodingsMsg se;
+ rfbFramebufferUpdateRequestMsg fur;
+ rfbKeyEventMsg ke;
+ rfbPointerEventMsg pe;
+ rfbClientCutTextMsg cct;
+} rfbClientToServerMsg;
diff --git a/krdc/vnc/sockets.c b/krdc/vnc/sockets.c
new file mode 100644
index 00000000..797dd22d
--- /dev/null
+++ b/krdc/vnc/sockets.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * 03-05-2002 tim@tjansen.de: removed Xt event processing for krdc
+ */
+
+/*
+ * sockets.c - functions to deal with sockets.
+ */
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <assert.h>
+#include "vncviewer.h"
+
+void PrintInHex(char *buf, int len);
+
+Bool errorMessageOnReadFailure = True;
+
+#define BUF_SIZE 8192
+static char buf[BUF_SIZE];
+static char *bufoutptr = buf;
+static unsigned int buffered = 0;
+
+/* Wait duration of select in seconds */
+#define SELECT_PERIOD 3
+
+
+/*
+ * ReadFromRFBServer is called whenever we want to read some data from the RFB
+ * server.
+ */
+Bool
+ReadFromRFBServer(char *out, unsigned int n)
+{
+ fd_set fds;
+ int e;
+ struct timeval tx;
+
+ if (isQuitFlagSet())
+ return False;
+
+ if (n <= buffered) {
+ memcpy(out, bufoutptr, n);
+ bufoutptr += n;
+ buffered -= n;
+ return True;
+ }
+
+ memcpy(out, bufoutptr, buffered);
+
+ out += buffered;
+ n -= buffered;
+
+ bufoutptr = buf;
+ buffered = 0;
+
+ if (n <= BUF_SIZE) {
+
+ while (buffered < n) {
+ int i;
+ if (isQuitFlagSet())
+ return False;
+ i = read(rfbsock, buf + buffered, BUF_SIZE - buffered);
+
+ if (i <= 0) {
+ if (i < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ FD_ZERO(&fds);
+ FD_SET(rfbsock,&fds);
+
+ tx.tv_sec = SELECT_PERIOD;
+ tx.tv_usec = 0;
+ if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) {
+ perror("krdc: select read");
+ return False;
+ }
+ i = 0;
+ } else {
+ perror("krdc: read");
+ return False;
+ }
+ } else {
+ fprintf(stderr,"VNC server closed connection\n");
+ return False;
+ }
+ }
+ buffered += i;
+ }
+
+ memcpy(out, bufoutptr, n);
+ bufoutptr += n;
+ buffered -= n;
+ return isQuitFlagSet() ? False : True;
+
+ } else {
+
+ while (n > 0) {
+ int i;
+ if (isQuitFlagSet())
+ return False;
+ i = read(rfbsock, out, n);
+ if (i <= 0) {
+ if (i < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ FD_ZERO(&fds);
+ FD_SET(rfbsock,&fds);
+
+ tx.tv_sec = SELECT_PERIOD;
+ tx.tv_usec = 0;
+ if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) {
+ perror("krdc: select");
+ return False;
+ }
+ i = 0;
+ } else {
+ perror("krdc: read");
+ return False;
+ }
+ } else {
+ fprintf(stderr,"VNC server closed connection\n");
+ return False;
+ }
+ }
+ out += i;
+ n -= i;
+ }
+
+ return isQuitFlagSet() ? False : True;
+ }
+}
+
+
+/*
+ * Write an exact number of bytes, and don't return until you've sent them.
+ * Note: this should only be called by the WriterThread
+ */
+
+Bool
+WriteExact(int sock, const char *_buf, int n)
+{
+ fd_set fds;
+ int i = 0;
+ int j;
+ int e;
+ struct timeval tx;
+
+ while (i < n) {
+ if (isQuitFlagSet())
+ return False;
+ j = write(sock, _buf + i, (n - i));
+ if (j <= 0) {
+ if (j < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN) {
+ FD_ZERO(&fds);
+ FD_SET(rfbsock,&fds);
+
+ tx.tv_sec = SELECT_PERIOD;
+ tx.tv_usec = 0;
+ if ((e=select(rfbsock+1, NULL, &fds, NULL, &tx)) < 0) {
+ perror("krdc: select write");
+ return False;
+ }
+ j = 0;
+ } else {
+ perror("krdc: write");
+ return False;
+ }
+ } else {
+ fprintf(stderr,"write failed\n");
+ return False;
+ }
+ }
+ i += j;
+ }
+ return True;
+}
+
+
+/*
+ * ConnectToTcpAddr connects to the given TCP port.
+ */
+
+int
+ConnectToTcpAddr(unsigned int host, int port)
+{
+ int sock;
+ struct sockaddr_in addr;
+ int one = 1;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = host;
+
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("krdc: ConnectToTcpAddr: socket");
+ return -(int)INIT_CONNECTION_FAILED;
+ }
+
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("krdc: ConnectToTcpAddr: connect");
+ close(sock);
+ return -(int)INIT_NO_SERVER;
+ }
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ perror("krdc: ConnectToTcpAddr: setsockopt");
+ close(sock);
+ return -(int)INIT_CONNECTION_FAILED;
+ }
+
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ perror(": AcceptTcpConnection: fcntl");
+ close(sock);
+ return -(int)INIT_CONNECTION_FAILED;
+ }
+
+ return sock;
+}
+
+
+/*
+ * StringToIPAddr - convert a host string to an IP address.
+ */
+
+Bool
+StringToIPAddr(const char *str, unsigned int *addr)
+{
+ struct hostent *hp;
+
+ if (strcmp(str,"") == 0) {
+ *addr = 0; /* local */
+ return True;
+ }
+
+ *addr = inet_addr(str);
+
+ if (*addr != -1)
+ return True;
+
+ hp = gethostbyname(str);
+
+ if (hp) {
+ *addr = *(unsigned int *)hp->h_addr;
+ return True;
+ }
+
+ return False;
+}
+
+
+/*
+ * Print out the contents of a packet for debugging.
+ */
+
+void
+PrintInHex(char *_buf, int len)
+{
+ int i, j;
+ char c, str[17];
+
+ str[16] = 0;
+
+ fprintf(stderr,"ReadExact: ");
+
+ for (i = 0; i < len; i++)
+ {
+ if ((i % 16 == 0) && (i != 0)) {
+ fprintf(stderr," ");
+ }
+ c = _buf[i];
+ str[i % 16] = (((c > 31) && (c < 127)) ? c : '.');
+ fprintf(stderr,"%02x ",(unsigned char)c);
+ if ((i % 4) == 3)
+ fprintf(stderr," ");
+ if ((i % 16) == 15)
+ {
+ fprintf(stderr,"%s\n",str);
+ }
+ }
+ if ((i % 16) != 0)
+ {
+ for (j = i % 16; j < 16; j++)
+ {
+ fprintf(stderr," ");
+ if ((j % 4) == 3) fprintf(stderr," ");
+ }
+ str[i % 16] = 0;
+ fprintf(stderr,"%s\n",str);
+ }
+
+ fflush(stderr);
+}
+
+void freeSocketsResources() {
+ close(rfbsock);
+
+ errorMessageOnReadFailure = True;
+ bufoutptr = buf;
+ buffered = 0;
+}
+
diff --git a/krdc/vnc/threads.cpp b/krdc/vnc/threads.cpp
new file mode 100644
index 00000000..fe5a1d62
--- /dev/null
+++ b/krdc/vnc/threads.cpp
@@ -0,0 +1,392 @@
+/***************************************************************************
+ threads.cpp - threads
+ -------------------
+ begin : Thu May 09 17:01:44 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kvncview.h"
+
+#include <kdebug.h>
+#include <kapplication.h>
+
+#include "vncviewer.h"
+#include "threads.h"
+
+#include <qcstring.h>
+
+// Maximum idle time for writer thread in ms. When it timeouts, it will request
+// another incremental update. Must be smaller than the timeout of the server
+// (krfb's is 20s).
+static const int MAXIMUM_WAIT_PERIOD = 8000;
+
+// time to postpone incremental updates that have not been requested explicitly
+static const int POSTPONED_INCRRQ_WAIT_PERIOD = 110;
+
+static const int MOUSEPRESS_QUEUE_SIZE = 5;
+static const int MOUSEMOVE_QUEUE_SIZE = 3;
+static const int KEY_QUEUE_SIZE = 8192;
+
+
+ControllerThread::ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag) :
+ m_view(v),
+ m_status(REMOTE_VIEW_CONNECTING),
+ m_wthread(wt),
+ m_quitFlag(quitFlag),
+ m_desktopInitialized(false)
+{
+}
+
+void ControllerThread::changeStatus(RemoteViewStatus s) {
+ m_status = s;
+ QApplication::postEvent(m_view, new StatusChangeEvent(s));
+}
+
+void ControllerThread::sendFatalError(ErrorCode s) {
+ m_quitFlag = true;
+ QApplication::postEvent(m_view, new FatalErrorEvent(s));
+ m_wthread.kick();
+}
+
+/*
+ * Calls this from the X11 thread
+ */
+void ControllerThread::desktopInit() {
+ SetVisualAndCmap();
+ ToplevelInit();
+ DesktopInit(m_view->winId());
+ m_desktopInitialized = true;
+ m_waiter.wakeAll();
+}
+
+void ControllerThread::kick() {
+ m_waiter.wakeAll();
+}
+
+void ControllerThread::run() {
+ int fd;
+ fd = ConnectToRFBServer(m_view->host().latin1(), m_view->port());
+ if (fd < 0) {
+ if (fd == -(int)INIT_NO_SERVER)
+ sendFatalError(ERROR_NO_SERVER);
+ else if (fd == -(int)INIT_NAME_RESOLUTION_FAILURE)
+ sendFatalError(ERROR_NAME);
+ else
+ sendFatalError(ERROR_CONNECTION);
+ return;
+ }
+ if (m_quitFlag) {
+ changeStatus(REMOTE_VIEW_DISCONNECTED);
+ return;
+ }
+
+ changeStatus(REMOTE_VIEW_AUTHENTICATING);
+
+ enum InitStatus s = InitialiseRFBConnection();
+ if (s != INIT_OK) {
+ if (s == INIT_CONNECTION_FAILED)
+ sendFatalError(ERROR_IO);
+ else if (s == INIT_SERVER_BLOCKED)
+ sendFatalError(ERROR_SERVER_BLOCKED);
+ else if (s == INIT_PROTOCOL_FAILURE)
+ sendFatalError(ERROR_PROTOCOL);
+ else if (s == INIT_AUTHENTICATION_FAILED)
+ sendFatalError(ERROR_AUTHENTICATION);
+ else if (s == INIT_ABORTED)
+ changeStatus(REMOTE_VIEW_DISCONNECTED);
+ else
+ sendFatalError(ERROR_INTERNAL);
+ return;
+ }
+
+ QApplication::postEvent(m_view,
+ new ScreenResizeEvent(si.framebufferWidth,
+ si.framebufferHeight));
+ m_wthread.queueUpdateRequest(QRegion(QRect(0,0,si.framebufferWidth,
+ si.framebufferHeight)));
+
+ QApplication::postEvent(m_view, new DesktopInitEvent());
+ while ((!m_quitFlag) && (!m_desktopInitialized))
+ m_waiter.wait(1000);
+
+ if (m_quitFlag) {
+ changeStatus(REMOTE_VIEW_DISCONNECTED);
+ return;
+ }
+
+ changeStatus(REMOTE_VIEW_PREPARING);
+
+ if (!SetFormatAndEncodings()) {
+ sendFatalError(ERROR_INTERNAL);
+ return;
+ }
+
+ changeStatus(REMOTE_VIEW_CONNECTED);
+
+ m_wthread.start();
+
+ while (!m_quitFlag) {
+ if ((!HandleRFBServerMessage()) && (!m_quitFlag)) {
+ sendFatalError(ERROR_IO);
+ return;
+ }
+ }
+
+ m_quitFlag = true;
+ changeStatus(REMOTE_VIEW_DISCONNECTED);
+ m_wthread.kick();
+}
+
+enum RemoteViewStatus ControllerThread::status() {
+ return m_status;
+}
+
+
+
+
+
+static WriterThread *writerThread;
+void queueIncrementalUpdateRequest() {
+ writerThread->queueIncrementalUpdateRequest();
+}
+
+void announceIncrementalUpdateRequest() {
+ writerThread->announceIncrementalUpdateRequest();
+}
+
+
+WriterThread::WriterThread(KVncView *v, volatile bool &quitFlag) :
+ m_quitFlag(quitFlag),
+ m_view(v),
+ m_lastIncrUpdatePostponed(false),
+ m_incrementalUpdateRQ(false),
+ m_incrementalUpdateAnnounced(false),
+ m_mouseEventNum(0),
+ m_keyEventNum(0),
+ m_clientCut(QString::null)
+{
+ writerThread = this;
+ m_lastIncrUpdate.start();
+}
+
+bool WriterThread::sendIncrementalUpdateRequest() {
+ m_lastIncrUpdate.restart();
+ return SendIncrementalFramebufferUpdateRequest();
+}
+
+bool WriterThread::sendUpdateRequest(const QRegion &region) {
+ QMemArray<QRect> r = region.rects();
+ for (unsigned int i = 0; i < r.size(); i++)
+ if (!SendFramebufferUpdateRequest(r[i].x(),
+ r[i].y(),
+ r[i].width(),
+ r[i].height(), False))
+ return false;
+ return true;
+}
+
+bool WriterThread::sendInputEvents(const QValueList<InputEvent> &events) {
+ QValueList<InputEvent>::const_iterator it = events.begin();
+ while (it != events.end()) {
+ if ((*it).type == KeyEventType) {
+ if (!SendKeyEvent((*it).e.k.k, (*it).e.k.down ? True : False))
+ return false;
+ }
+ else
+ if (!SendPointerEvent((*it).e.m.x, (*it).e.m.y, (*it).e.m.buttons))
+ return false;
+ it++;
+ }
+ return true;
+}
+
+void WriterThread::queueIncrementalUpdateRequest() {
+ m_lock.lock();
+ m_incrementalUpdateRQ = true;
+ m_waiter.wakeAll();
+ m_lock.unlock();
+}
+
+void WriterThread::announceIncrementalUpdateRequest() {
+ m_lock.lock();
+ m_incrementalUpdateAnnounced = true;
+ m_lock.unlock();
+}
+
+
+void WriterThread::queueUpdateRequest(const QRegion &r) {
+ m_lock.lock();
+ m_updateRegionRQ += r;
+ m_waiter.wakeAll();
+ m_lock.unlock();
+}
+
+void WriterThread::queueMouseEvent(int x, int y, int buttonMask) {
+ InputEvent e;
+ e.type = MouseEventType;
+ e.e.m.x = x;
+ e.e.m.y = y;
+ e.e.m.buttons = buttonMask;
+
+ m_lock.lock();
+ if (m_mouseEventNum > 0) {
+ if ((e.e.m.x == m_lastMouseEvent.x) &&
+ (e.e.m.y == m_lastMouseEvent.y) &&
+ (e.e.m.buttons == m_lastMouseEvent.buttons)) {
+ m_lock.unlock();
+ return;
+ }
+ if (m_mouseEventNum >= MOUSEPRESS_QUEUE_SIZE) {
+ m_lock.unlock();
+ return;
+ }
+ if ((m_lastMouseEvent.buttons == buttonMask) &&
+ (m_mouseEventNum >= MOUSEMOVE_QUEUE_SIZE)) {
+ m_lock.unlock();
+ return;
+ }
+ }
+
+ m_mouseEventNum++;
+ m_lastMouseEvent = e.e.m;
+
+ m_inputEvents.push_back(e);
+ m_waiter.wakeAll();
+ m_lock.unlock();
+}
+
+void WriterThread::queueKeyEvent(unsigned int k, bool down) {
+ InputEvent e;
+ e.type = KeyEventType;
+ e.e.k.k = k;
+ e.e.k.down = down;
+
+ m_lock.lock();
+ if (m_keyEventNum >= KEY_QUEUE_SIZE) {
+ m_lock.unlock();
+ return;
+ }
+
+ m_keyEventNum++;
+ m_inputEvents.push_back(e);
+ m_waiter.wakeAll();
+ m_lock.unlock();
+}
+
+void WriterThread::queueClientCut(const QString &text) {
+ m_lock.lock();
+
+ m_clientCut = text;
+
+ m_waiter.wakeAll();
+ m_lock.unlock();
+}
+
+void WriterThread::kick() {
+ m_waiter.wakeAll();
+}
+
+void WriterThread::run() {
+ bool incrementalUpdateRQ = false;
+ bool incrementalUpdateAnnounced = false;
+ QRegion updateRegionRQ;
+ QValueList<InputEvent> inputEvents;
+ QString clientCut;
+
+ while (!m_quitFlag) {
+ m_lock.lock();
+ incrementalUpdateRQ = m_incrementalUpdateRQ;
+ incrementalUpdateAnnounced = m_incrementalUpdateAnnounced;
+ updateRegionRQ = m_updateRegionRQ;
+ inputEvents = m_inputEvents;
+ clientCut = m_clientCut;
+
+ if ((!incrementalUpdateRQ) &&
+ (updateRegionRQ.isNull()) &&
+ (inputEvents.size() == 0) &&
+ (clientCut.isNull())) {
+ if (!m_waiter.wait(&m_lock,
+ m_lastIncrUpdatePostponed ?
+ POSTPONED_INCRRQ_WAIT_PERIOD : MAXIMUM_WAIT_PERIOD))
+ m_incrementalUpdateRQ = true;
+ m_lock.unlock();
+ }
+ else {
+ m_incrementalUpdateRQ = false;
+ m_incrementalUpdateAnnounced = false;
+ m_updateRegionRQ = QRegion();
+ m_inputEvents.clear();
+ m_keyEventNum = 0;
+ m_mouseEventNum = 0;
+ m_clientCut = QString::null;
+ m_lock.unlock();
+
+ // always send incremental update, unless
+ // a) a framebuffer update is done ATM and will do the request later, or
+ // b) the last unrequested update has been done less than 0.1s ago
+ //
+ // if the update has not been done because of b, postpone it.
+ if (incrementalUpdateRQ || !incrementalUpdateAnnounced) {
+ bool sendUpdate;
+ if (incrementalUpdateRQ) {
+ sendUpdate = true;
+ m_lastIncrUpdatePostponed = false;
+ }
+ else {
+ if (m_lastIncrUpdate.elapsed() < 100) {
+ sendUpdate = false;
+ m_lastIncrUpdatePostponed = true;
+ }
+ else {
+ sendUpdate = true;
+ m_lastIncrUpdatePostponed = false;
+ }
+ }
+
+ if (sendUpdate)
+ if (!sendIncrementalUpdateRequest()) {
+ sendFatalError(ERROR_IO);
+ break;
+ }
+ }
+ else
+ m_lastIncrUpdatePostponed = false;
+
+ if (!updateRegionRQ.isNull())
+ if (!sendUpdateRequest(updateRegionRQ)) {
+ sendFatalError(ERROR_IO);
+ break;
+ }
+ if (inputEvents.size() != 0)
+ if (!sendInputEvents(inputEvents)) {
+ sendFatalError(ERROR_IO);
+ break;
+ }
+ if (!clientCut.isNull()) {
+ QCString cutTextUtf8(clientCut.utf8());
+ if (!SendClientCutText(cutTextUtf8.data(),
+ (int)cutTextUtf8.length())) {
+ sendFatalError(ERROR_IO);
+ break;
+ }
+ }
+ }
+ }
+ m_quitFlag = true;
+}
+
+void WriterThread::sendFatalError(ErrorCode s) {
+ m_quitFlag = true;
+ QApplication::postEvent(m_view, new FatalErrorEvent(s));
+}
+
diff --git a/krdc/vnc/threads.h b/krdc/vnc/threads.h
new file mode 100644
index 00000000..5f38b71f
--- /dev/null
+++ b/krdc/vnc/threads.h
@@ -0,0 +1,126 @@
+/***************************************************************************
+ threads.h - threads for kvncview
+ -------------------
+ begin : Thu May 09 16:01:42 CET 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 THREADS_H
+#define THREADS_H
+
+#include <qthread.h>
+#include <qregion.h>
+#include <qrect.h>
+#include <qmutex.h>
+#include <qwaitcondition.h>
+#include <qevent.h>
+#include <qvaluelist.h>
+#include <qdatetime.h>
+
+#include <stdlib.h>
+
+#include "events.h"
+#include "vnctypes.h"
+
+class KVncView;
+
+enum EventType {
+ MouseEventType,
+ KeyEventType
+};
+
+
+struct MouseEvent {
+ int x, y, buttons;
+};
+
+struct KeyEvent {
+ unsigned int k;
+ bool down;
+};
+
+struct InputEvent {
+ EventType type;
+ union {
+ MouseEvent m;
+ KeyEvent k;
+ } e;
+};
+
+
+class WriterThread : public QThread {
+private:
+ QMutex m_lock;
+ QWaitCondition m_waiter;
+ volatile bool &m_quitFlag;
+ KVncView *m_view;
+
+ QTime m_lastIncrUpdate; // start()ed when a incr update is sent
+ bool m_lastIncrUpdatePostponed;
+
+ // all things that can be send follow:
+ bool m_incrementalUpdateRQ; // for sending an incremental request
+ bool m_incrementalUpdateAnnounced; // set when a RQ will come soon
+ QRegion m_updateRegionRQ; // for sending updates, null if it is done
+ QValueList<InputEvent> m_inputEvents; // list of unsent input events
+ MouseEvent m_lastMouseEvent;
+ int m_mouseEventNum, m_keyEventNum;
+ QString m_clientCut;
+
+ void sendFatalError(ErrorCode s);
+
+public:
+ WriterThread(KVncView *v, volatile bool &quitFlag);
+
+ void queueIncrementalUpdateRequest();
+ void announceIncrementalUpdateRequest();
+ void queueUpdateRequest(const QRegion &r);
+ void queueMouseEvent(int x, int y, int buttonMask);
+ void queueKeyEvent(unsigned int k, bool down);
+ void queueClientCut(const QString &text);
+ void kick();
+
+protected:
+ void run();
+ bool sendIncrementalUpdateRequest();
+ bool sendUpdateRequest(const QRegion &r);
+ bool sendInputEvents(const QValueList<InputEvent> &events);
+};
+
+
+
+class ControllerThread : public QThread {
+private:
+ KVncView *m_view;
+ enum RemoteViewStatus m_status;
+ WriterThread &m_wthread;
+ volatile bool &m_quitFlag;
+ volatile bool m_desktopInitialized;
+ QWaitCondition m_waiter;
+
+ void changeStatus(RemoteViewStatus s);
+ void sendFatalError(ErrorCode s);
+
+public:
+ ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag);
+ enum RemoteViewStatus status();
+ void desktopInit();
+ void kick();
+
+protected:
+ void run();
+};
+
+
+
+#endif
diff --git a/krdc/vnc/tight.c b/krdc/vnc/tight.c
new file mode 100644
index 00000000..2f08cfb0
--- /dev/null
+++ b/krdc/vnc/tight.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * tight.c - handle ``tight'' encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple
+ * times by rfbproto.c, each time with a different definition of the
+ * macro BPP. For each value of BPP, this file defines a function
+ * which handles a tight-encoded rectangle with BPP bits per pixel.
+ *
+ */
+
+#define TIGHT_MIN_TO_COMPRESS 12
+
+#define CARDBPP CONCAT2E(CARD,BPP)
+#define filterPtrBPP CONCAT2E(filterPtr,BPP)
+
+#define HandleTightBPP CONCAT2E(HandleTight,BPP)
+#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP)
+#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP)
+#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP)
+#define FilterCopyBPP CONCAT2E(FilterCopy,BPP)
+#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP)
+#define FilterGradientBPP CONCAT2E(FilterGradient,BPP)
+#define FillRectangleBPP CONCAT2E(FillRectangle,BPP)
+
+#if BPP != 8
+#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP)
+#endif
+
+#ifndef RGB_TO_PIXEL
+
+#define RGB_TO_PIXEL(bpp,r,g,b) \
+ (((CARD##bpp)(r) & myFormat.redMax) << myFormat.redShift | \
+ ((CARD##bpp)(g) & myFormat.greenMax) << myFormat.greenShift | \
+ ((CARD##bpp)(b) & myFormat.blueMax) << myFormat.blueShift)
+
+#define RGB24_TO_PIXEL(bpp,r,g,b) \
+ ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \
+ << myFormat.redShift | \
+ (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \
+ << myFormat.greenShift | \
+ (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \
+ << myFormat.blueShift)
+
+#define RGB24_TO_PIXEL32(r,g,b) \
+ (((CARD32)(r) & 0xFF) << myFormat.redShift | \
+ ((CARD32)(g) & 0xFF) << myFormat.greenShift | \
+ ((CARD32)(b) & 0xFF) << myFormat.blueShift)
+
+#endif
+
+/* Type declarations */
+
+typedef void (*filterPtrBPP)(int, CARDBPP *);
+
+/* Prototypes */
+
+static int InitFilterCopyBPP (int rw, int rh);
+static int InitFilterPaletteBPP (int rw, int rh);
+static int InitFilterGradientBPP (int rw, int rh);
+static void FilterCopyBPP (int numRows, CARDBPP *destBuffer);
+static void FilterPaletteBPP (int numRows, CARDBPP *destBuffer);
+static void FilterGradientBPP (int numRows, CARDBPP *destBuffer);
+
+static Bool DecompressJpegRectBPP(int x, int y, int w, int h);
+
+/* Definitions */
+
+static Bool
+HandleTightBPP (int rx, int ry, int rw, int rh)
+{
+ CARDBPP fill_colour;
+ XGCValues gcv;
+ CARD8 comp_ctl;
+ CARD8 filter_id;
+ filterPtrBPP filterFn;
+ z_streamp zs;
+ char *buffer2;
+ int err, stream_id, compressedLen, bitsPixel;
+ int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
+ CARDBPP *rawData;
+
+ if (!ReadFromRFBServer((char *)&comp_ctl, 1))
+ return False;
+
+ /* Flush zlib streams if we are told by the server to do so. */
+ for (stream_id = 0; stream_id < 4; stream_id++) {
+ if ((comp_ctl & 1) && zlibStreamActive[stream_id]) {
+ if (inflateEnd (&zlibStream[stream_id]) != Z_OK &&
+ zlibStream[stream_id].msg != NULL)
+ fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg);
+ zlibStreamActive[stream_id] = False;
+ }
+ comp_ctl >>= 1;
+ }
+
+ /* Handle solid rectangles. */
+ if (comp_ctl == rfbTightFill) {
+#if BPP == 32
+ if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+ myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+ if (!ReadFromRFBServer(buffer, 3))
+ return False;
+ fill_colour = RGB24_TO_PIXEL32(buffer[0], buffer[1], buffer[2]);
+ } else {
+ if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour)))
+ return False;
+ }
+#else
+ if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour)))
+ return False;
+#endif
+
+ LockFramebuffer();
+ FillRectangleBPP(fill_colour, rx, ry, rw, rh);
+ UnlockFramebuffer();
+ SyncScreenRegion(rx, ry, rw, rh);
+ return True;
+ }
+
+#if BPP == 8
+ if (comp_ctl == rfbTightJpeg) {
+ fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n");
+ return False;
+ }
+#else
+ if (comp_ctl == rfbTightJpeg) {
+ return DecompressJpegRectBPP(rx, ry, rw, rh);
+ }
+#endif
+
+ /* Quit on unsupported subencoding value. */
+ if (comp_ctl > rfbTightMaxSubencoding) {
+ fprintf(stderr, "Tight encoding: bad subencoding value received.\n");
+ return False;
+ }
+
+ /*
+ * Here primary compression mode handling begins.
+ * Data was processed with optional filter + zlib compression.
+ */
+
+ /* First, we should identify a filter to use. */
+ if ((comp_ctl & rfbTightExplicitFilter) != 0) {
+ if (!ReadFromRFBServer((char*)&filter_id, 1))
+ return False;
+
+ switch (filter_id) {
+ case rfbTightFilterCopy:
+ filterFn = FilterCopyBPP;
+ bitsPixel = InitFilterCopyBPP(rw, rh);
+ break;
+ case rfbTightFilterPalette:
+ filterFn = FilterPaletteBPP;
+ bitsPixel = InitFilterPaletteBPP(rw, rh);
+ break;
+ case rfbTightFilterGradient:
+ filterFn = FilterGradientBPP;
+ bitsPixel = InitFilterGradientBPP(rw, rh);
+ break;
+ default:
+ fprintf(stderr, "Tight encoding: unknown filter code received.\n");
+ return False;
+ }
+ } else {
+ filterFn = FilterCopyBPP;
+ bitsPixel = InitFilterCopyBPP(rw, rh);
+ }
+ if (bitsPixel == 0) {
+ fprintf(stderr, "Tight encoding: error receiving palette.\n");
+ return False;
+ }
+
+ /* Determine if the data should be decompressed or just copied. */
+ rowSize = (rw * bitsPixel + 7) / 8;
+ if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) {
+ if (!ReadFromRFBServer((char*)buffer, rh * rowSize))
+ return False;
+
+ buffer2 = &buffer[TIGHT_MIN_TO_COMPRESS * 4];
+ filterFn(rh, (CARDBPP *)buffer2);
+ CopyDataToScreen(buffer2, rx, ry, rw, rh);
+
+ return True;
+ }
+
+ /* Read the length (1..3 bytes) of compressed data following. */
+ compressedLen = (int)ReadCompactLen();
+ if (compressedLen <= 0) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return False;
+ }
+
+ /* Now let's initialize compression stream if needed. */
+ stream_id = comp_ctl & 0x03;
+ zs = &zlibStream[stream_id];
+ if (!zlibStreamActive[stream_id]) {
+ zs->zalloc = Z_NULL;
+ zs->zfree = Z_NULL;
+ zs->opaque = Z_NULL;
+ err = inflateInit(zs);
+ if (err != Z_OK) {
+ if (zs->msg != NULL)
+ fprintf(stderr, "InflateInit error: %s.\n", zs->msg);
+ return False;
+ }
+ zlibStreamActive[stream_id] = True;
+ }
+
+ /* Read, decode and draw actual pixel data in a loop. */
+
+ bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
+ buffer2 = &buffer[bufferSize];
+ if (rowSize > bufferSize) {
+ /* Should be impossible when BUFFER_SIZE >= 16384 */
+ fprintf(stderr, "Internal error: incorrect buffer size.\n");
+ return False;
+ }
+
+ rowsProcessed = 0;
+ extraBytes = 0;
+
+ while (compressedLen > 0) {
+ if (compressedLen > ZLIB_BUFFER_SIZE)
+ portionLen = ZLIB_BUFFER_SIZE;
+ else
+ portionLen = compressedLen;
+
+ if (!ReadFromRFBServer((char*)zlib_buffer, portionLen))
+ return False;
+
+ compressedLen -= portionLen;
+
+ zs->next_in = (Bytef *)zlib_buffer;
+ zs->avail_in = portionLen;
+
+ do {
+ zs->next_out = (Bytef *)&buffer[extraBytes];
+ zs->avail_out = bufferSize - extraBytes;
+
+ err = inflate(zs, Z_SYNC_FLUSH);
+ if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */
+ break;
+ if (err != Z_OK && err != Z_STREAM_END) {
+ if (zs->msg != NULL) {
+ fprintf(stderr, "Inflate error: %s.\n", zs->msg);
+ } else {
+ fprintf(stderr, "Inflate error: %d.\n", err);
+ }
+ return False;
+ }
+
+ numRows = (bufferSize - zs->avail_out) / rowSize;
+
+ filterFn(numRows, (CARDBPP *)buffer2);
+
+ extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
+ if (extraBytes > 0)
+ memcpy(buffer, &buffer[numRows * rowSize], extraBytes);
+
+ CopyDataToScreen(buffer2, rx, ry + rowsProcessed, rw, numRows);
+ rowsProcessed += numRows;
+ }
+ while (zs->avail_out == 0);
+ }
+
+ if (rowsProcessed != rh) {
+ fprintf(stderr, "Incorrect number of scan lines after decompression.\n");
+ return False;
+ }
+
+ return True;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * Filter stuff.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool cutZeros;
+ static int rectWidth, rectColors;
+ static CARD8 tightPalette[256*4];
+ static CARD8 tightPrevRow[2048*3*sizeof(CARD16)];
+*/
+
+static int
+InitFilterCopyBPP (int rw, int rh)
+{
+ rectWidth = rw;
+
+#if BPP == 32
+ if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+ myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+ cutZeros = True;
+ return 24;
+ } else {
+ cutZeros = False;
+ }
+#endif
+
+ return BPP;
+}
+
+static void
+FilterCopyBPP (int numRows, CARDBPP *dst)
+{
+
+#if BPP == 32
+ int x, y;
+
+ if (cutZeros) {
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth; x++) {
+ dst[y*rectWidth+x] =
+ RGB24_TO_PIXEL32(buffer[(y*rectWidth+x)*3],
+ buffer[(y*rectWidth+x)*3+1],
+ buffer[(y*rectWidth+x)*3+2]);
+ }
+ }
+ return;
+ }
+#endif
+
+ memcpy (dst, buffer, numRows * rectWidth * (BPP / 8));
+}
+
+static int
+InitFilterGradientBPP (int rw, int rh)
+{
+ int bits;
+
+ bits = InitFilterCopyBPP(rw, rh);
+ if (cutZeros)
+ memset(tightPrevRow, 0, rw * 3);
+ else
+ memset(tightPrevRow, 0, rw * 3 * sizeof(CARD16));
+
+ return bits;
+}
+
+#if BPP == 32
+
+static void
+FilterGradient24 (int numRows, CARD32 *dst)
+{
+ int x, y, c;
+ CARD8 thisRow[2048*3];
+ CARD8 pix[3];
+ int est[3];
+
+ for (y = 0; y < numRows; y++) {
+
+ /* First pixel in a row */
+ for (c = 0; c < 3; c++) {
+ pix[c] = tightPrevRow[c] + buffer[y*rectWidth*3+c];
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < rectWidth; x++) {
+ for (c = 0; c < 3; c++) {
+ est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] -
+ (int)tightPrevRow[(x-1)*3+c];
+ if (est[c] > 0xFF) {
+ est[c] = 0xFF;
+ } else if (est[c] < 0x00) {
+ est[c] = 0x00;
+ }
+ pix[c] = (CARD8)est[c] + buffer[(y*rectWidth+x)*3+c];
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+ }
+
+ memcpy(tightPrevRow, thisRow, rectWidth * 3);
+ }
+}
+
+#endif
+
+static void
+FilterGradientBPP (int numRows, CARDBPP *dst)
+{
+ int x, y, c;
+ CARDBPP *src = (CARDBPP *)buffer;
+ CARD16 *thatRow = (CARD16 *)tightPrevRow;
+ CARD16 thisRow[2048*3];
+ CARD16 pix[3];
+ CARD16 max[3];
+ int shift[3];
+ int est[3];
+
+#if BPP == 32
+ if (cutZeros) {
+ FilterGradient24(numRows, dst);
+ return;
+ }
+#endif
+
+ max[0] = myFormat.redMax;
+ max[1] = myFormat.greenMax;
+ max[2] = myFormat.blueMax;
+
+ shift[0] = myFormat.redShift;
+ shift[1] = myFormat.greenShift;
+ shift[2] = myFormat.blueShift;
+
+ for (y = 0; y < numRows; y++) {
+
+ /* First pixel in a row */
+ for (c = 0; c < 3; c++) {
+ pix[c] = (CARD16)((src[y*rectWidth] >> shift[c]) + thatRow[c] & max[c]);
+ thisRow[c] = pix[c];
+ }
+ dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+
+ /* Remaining pixels of a row */
+ for (x = 1; x < rectWidth; x++) {
+ for (c = 0; c < 3; c++) {
+ est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c];
+ if (est[c] > (int)max[c]) {
+ est[c] = (int)max[c];
+ } else if (est[c] < 0) {
+ est[c] = 0;
+ }
+ pix[c] = (CARD16)((src[y*rectWidth+x] >> shift[c]) + est[c] & max[c]);
+ thisRow[x*3+c] = pix[c];
+ }
+ dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+ }
+ memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(CARD16));
+ }
+}
+
+static int
+InitFilterPaletteBPP (int rw, int rh)
+{
+ int i;
+ CARD8 numColors;
+ CARDBPP *palette = (CARDBPP *)tightPalette;
+
+ rectWidth = rw;
+
+ if (!ReadFromRFBServer((char*)&numColors, 1))
+ return 0;
+
+ rectColors = (int)numColors;
+ if (++rectColors < 2)
+ return 0;
+
+#if BPP == 32
+ if (myFormat.depth == 24 && myFormat.redMax == 0xFF &&
+ myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) {
+ if (!ReadFromRFBServer((char*)&tightPalette, rectColors * 3))
+ return 0;
+ for (i = rectColors - 1; i >= 0; i--) {
+ palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3],
+ tightPalette[i*3+1],
+ tightPalette[i*3+2]);
+ }
+ return (rectColors == 2) ? 1 : 8;
+ }
+#endif
+
+ if (!ReadFromRFBServer((char*)&tightPalette, rectColors * (BPP / 8)))
+ return 0;
+
+ return (rectColors == 2) ? 1 : 8;
+}
+
+static void
+FilterPaletteBPP (int numRows, CARDBPP *dst)
+{
+ int x, y, b, w;
+ CARD8 *src = (CARD8 *)buffer;
+ CARDBPP *palette = (CARDBPP *)tightPalette;
+
+ if (rectColors == 2) {
+ w = (rectWidth + 7) / 8;
+ for (y = 0; y < numRows; y++) {
+ for (x = 0; x < rectWidth / 8; x++) {
+ for (b = 7; b >= 0; b--)
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ for (b = 7; b >= 8 - rectWidth % 8; b--) {
+ dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ }
+ }
+ } else {
+ for (y = 0; y < numRows; y++)
+ for (x = 0; x < rectWidth; x++)
+ dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]];
+ }
+}
+
+#if BPP != 8
+
+/*----------------------------------------------------------------------------
+ *
+ * JPEG decompression.
+ *
+ */
+
+/*
+ The following variables are defined in rfbproto.c:
+ static Bool jpegError;
+ static struct jpeg_source_mgr jpegSrcManager;
+ static JOCTET *jpegBufferPtr;
+ static size_t *jpegBufferLen;
+*/
+
+static Bool
+DecompressJpegRectBPP(int x, int y, int w, int h)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int compressedLen;
+ CARD8 *compressedData;
+ CARDBPP *pixelPtr;
+ JSAMPROW rowPointer[1];
+ int dx, dy;
+
+ compressedLen = (int)ReadCompactLen();
+ if (compressedLen <= 0) {
+ fprintf(stderr, "Incorrect data received from the server.\n");
+ return False;
+ }
+
+ if (compressedLen > MAX_JPEG_SIZE) {
+ fprintf(stderr, "To large data announced by the server.\n");
+ return False;
+ }
+
+ compressedData = malloc(compressedLen);
+ if (compressedData == NULL) {
+ fprintf(stderr, "Memory allocation error.\n");
+ return False;
+ }
+
+ if (!ReadFromRFBServer((char*)compressedData, compressedLen)) {
+ free(compressedData);
+ return False;
+ }
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_decompress(&cinfo);
+
+ JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+
+ jpeg_read_header(&cinfo, TRUE);
+ cinfo.out_color_space = JCS_RGB;
+
+ jpeg_start_decompress(&cinfo);
+ if (cinfo.output_width != w || cinfo.output_height != h ||
+ cinfo.output_components != 3) {
+ fprintf(stderr, "Tight Encoding: Wrong JPEG data received.\n");
+ jpeg_destroy_decompress(&cinfo);
+ free(compressedData);
+ return False;
+ }
+
+ rowPointer[0] = (JSAMPROW)buffer;
+ dy = 0;
+ while (cinfo.output_scanline < cinfo.output_height) {
+ jpeg_read_scanlines(&cinfo, rowPointer, 1);
+ if (jpegError) {
+ break;
+ }
+ pixelPtr = (CARDBPP *)&buffer[BUFFER_SIZE / 2];
+ for (dx = 0; dx < w; dx++) {
+ *pixelPtr++ =
+ RGB24_TO_PIXEL(BPP, buffer[dx*3], buffer[dx*3+1], buffer[dx*3+2]);
+ }
+ CopyDataToScreen(&buffer[BUFFER_SIZE / 2], x, y + dy, w, 1);
+ dy++;
+ }
+
+ if (!jpegError)
+ jpeg_finish_decompress(&cinfo);
+
+ jpeg_destroy_decompress(&cinfo);
+ free(compressedData);
+
+ return !jpegError;
+}
+
+#endif
+
diff --git a/krdc/vnc/vncauth.c b/krdc/vnc/vncauth.c
new file mode 100644
index 00000000..b5d42836
--- /dev/null
+++ b/krdc/vnc/vncauth.c
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * vncauth.c - Functions for VNC password management and authentication.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <vncauth.h>
+#include <d3des.h>
+
+
+/*
+ * We use a fixed key to store passwords, since we assume that our local
+ * file system is secure but nonetheless don't want to store passwords
+ * as plaintext.
+ */
+
+unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7};
+
+
+/*
+ * Encrypt a password and store it in a file. Returns 0 if successful,
+ * 1 if the file could not be written.
+ */
+
+int
+vncEncryptAndStorePasswd(char *passwd, char *fname)
+{
+ FILE *fp;
+ int i;
+ unsigned char encryptedPasswd[8];
+
+ if ((fp = fopen(fname,"w")) == NULL) return 1;
+
+ chmod(fname, S_IRUSR|S_IWUSR);
+
+ /* pad password with nulls */
+
+ for (i = 0; i < 8; i++) {
+ if (i < strlen(passwd)) {
+ encryptedPasswd[i] = passwd[i];
+ } else {
+ encryptedPasswd[i] = 0;
+ }
+ }
+
+ /* Do encryption in-place - this way we overwrite our copy of the plaintext
+ password */
+
+ deskey(fixedkey, EN0);
+ des(encryptedPasswd, encryptedPasswd);
+
+ for (i = 0; i < 8; i++) {
+ putc(encryptedPasswd[i], fp);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+
+/*
+ * Decrypt a password from a file. Returns a pointer to a newly allocated
+ * string containing the password or a null pointer if the password could
+ * not be retrieved for some reason.
+ */
+
+char *
+vncDecryptPasswdFromFile(char *fname)
+{
+ FILE *fp;
+ int i, ch;
+ unsigned char *passwd = (unsigned char *)malloc(9);
+
+ if ((fp = fopen(fname,"r")) == NULL) return NULL;
+
+ for (i = 0; i < 8; i++) {
+ ch = getc(fp);
+ if (ch == EOF) {
+ fclose(fp);
+ return NULL;
+ }
+ passwd[i] = ch;
+ }
+
+ fclose(fp);
+
+ deskey(fixedkey, DE1);
+ des(passwd, passwd);
+
+ passwd[8] = 0;
+
+ return (char *)passwd;
+}
+
+
+/*
+ * Generate CHALLENGESIZE random bytes for use in challenge-response
+ * authentication.
+ */
+
+void
+vncRandomBytes(unsigned char *bytes)
+{
+ int i;
+ unsigned int seed = (unsigned int) time(0);
+
+ srandom(seed);
+ for (i = 0; i < CHALLENGESIZE; i++) {
+ bytes[i] = (unsigned char)(random() & 255);
+ }
+}
+
+
+/*
+ * Encrypt CHALLENGESIZE bytes in memory using a password.
+ */
+
+void
+vncEncryptBytes(unsigned char *bytes, char *passwd)
+{
+ unsigned char key[8];
+ int i;
+
+ /* key is simply password padded with nulls */
+
+ for (i = 0; i < 8; i++) {
+ if (i < strlen(passwd)) {
+ key[i] = passwd[i];
+ } else {
+ key[i] = 0;
+ }
+ }
+
+ deskey(key, EN0);
+
+ for (i = 0; i < CHALLENGESIZE; i += 8) {
+ des(bytes+i, bytes+i);
+ }
+}
diff --git a/krdc/vnc/vncauth.h b/krdc/vnc/vncauth.h
new file mode 100644
index 00000000..86dc455c
--- /dev/null
+++ b/krdc/vnc/vncauth.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * vncauth.h - describes the functions provided by the vncauth library.
+ */
+
+#define MAXPWLEN 8
+#define CHALLENGESIZE 16
+
+extern int vncEncryptAndStorePasswd(char *passwd, char *fname);
+extern char *vncDecryptPasswdFromFile(char *fname);
+extern void vncRandomBytes(unsigned char *bytes);
+extern void vncEncryptBytes(unsigned char *bytes, char *passwd);
diff --git a/krdc/vnc/vnchostpref.cpp b/krdc/vnc/vnchostpref.cpp
new file mode 100644
index 00000000..a17f8ea1
--- /dev/null
+++ b/krdc/vnc/vnchostpref.cpp
@@ -0,0 +1,127 @@
+/***************************************************************************
+ vnchostprefs.cpp - vnc host preferences
+ -------------------
+ begin : Fri May 09 22:32 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "vnchostpref.h"
+#include <kconfig.h>
+#include <klocale.h>
+
+const QString VncHostPref::VncType = "VNC";
+
+VncHostPref::VncHostPref(KConfig *conf, const QString &host, const QString &type) :
+ HostPref(conf, host, type),
+ m_quality(0),
+ m_useKWallet(true),
+ m_askOnConnect(true) {
+}
+
+VncHostPref::~VncHostPref() {
+}
+
+void VncHostPref::save() {
+ if ( !m_host.isEmpty() && !m_type.isEmpty() )
+ {
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_config->writeEntry(p+"exists", true);
+ m_config->writeEntry(p+"quality", m_quality);
+ m_config->writeEntry(p+"askOnConnect", m_askOnConnect);
+ m_config->writeEntry(p+"useKWallet", m_useKWallet);
+ }
+ else
+ {
+ m_config->setGroup( "VncDefaultSettings" );
+ m_config->writeEntry( "vncQuality", m_quality );
+ m_config->writeEntry( "vncShowHostPreferences", m_askOnConnect );
+ m_config->writeEntry( "vncUseKWallet", m_useKWallet );
+ }
+}
+
+void VncHostPref::load() {
+ if ( !m_host.isEmpty() && !m_type.isEmpty() )
+ {
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_quality = m_config->readNumEntry(p+"quality", 0);
+ m_askOnConnect = m_config->readBoolEntry(p+"askOnConnect", true);
+ m_useKWallet = m_config->readBoolEntry(p+"useKWallet", true);
+ }
+ else
+ {
+ setDefaults();
+ }
+}
+
+void VncHostPref::remove() {
+ m_config->setGroup("PerHostSettings");
+ QString p = prefix();
+ m_config->deleteEntry(p+"exists");
+ m_config->deleteEntry(p+"quality");
+ m_config->deleteEntry(p+"askOnConnect");
+}
+
+void VncHostPref::setDefaults() {
+ m_config->setGroup("VncDefaultSettings");
+ m_quality = m_config->readNumEntry("vncQuality", 0);
+ m_askOnConnect = m_config->readBoolEntry("vncShowHostPreferences", true);
+ m_useKWallet = m_config->readBoolEntry("vncUseKWallet", true);
+}
+
+QString VncHostPref::prefDescription() const {
+ QString q;
+ switch(m_quality) {
+ case 0:
+ q = i18n("High");
+ break;
+ case 1:
+ q = i18n("Medium");
+ break;
+ case 2:
+ q = i18n("Low");
+ break;
+ default:
+ Q_ASSERT(true);
+ }
+ return i18n("Show Preferences: %1, Quality: %2, KWallet: %3")
+ .arg(m_askOnConnect ? i18n("yes") : i18n("no")).arg(q).arg(m_useKWallet ? i18n("yes") : i18n("no"));
+}
+
+void VncHostPref::setQuality(int q) {
+ m_quality = q;
+ save();
+}
+
+int VncHostPref::quality() const {
+ return m_quality;
+}
+
+void VncHostPref::setAskOnConnect(bool ask) {
+ m_askOnConnect = ask;
+ save();
+}
+
+bool VncHostPref::askOnConnect() const {
+ return m_askOnConnect;
+}
+
+void VncHostPref::setUseKWallet(bool use) {
+ m_useKWallet = use;
+ save();
+}
+
+bool VncHostPref::useKWallet() const {
+ return m_useKWallet;
+}
diff --git a/krdc/vnc/vnchostpref.h b/krdc/vnc/vnchostpref.h
new file mode 100644
index 00000000..33a20600
--- /dev/null
+++ b/krdc/vnc/vnchostpref.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ vnchostprefs.h - vnc host preferences
+ -------------------
+ begin : Fri May 09 22:32 CET 2003
+ copyright : (C) 2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 VNCHOSTPREF_H
+#define VNCHOSTPREF_H
+
+#include "hostpreferences.h"
+
+class VncHostPref : public HostPref {
+protected:
+ friend class HostPreferences;
+
+ int m_quality;
+ bool m_askOnConnect;
+ bool m_useKWallet;
+
+ virtual void load();
+ virtual void setDefaults();
+ virtual void save();
+ virtual void remove();
+
+public:
+ static const QString VncType;
+
+ VncHostPref(KConfig *conf, const QString &host=QString::null,
+ const QString &type=QString::null);
+ virtual ~VncHostPref();
+
+ virtual QString prefDescription() const;
+ void setQuality(int q);
+ int quality() const;
+ void setAskOnConnect(bool ask);
+ bool askOnConnect() const;
+ void setUseKWallet(bool);
+ bool useKWallet() const;
+};
+
+#endif
diff --git a/krdc/vnc/vncprefs.ui b/krdc/vnc/vncprefs.ui
new file mode 100644
index 00000000..034b2b1a
--- /dev/null
+++ b/krdc/vnc/vncprefs.ui
@@ -0,0 +1,165 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>VncPrefs</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>VncPrefs</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>472</width>
+ <height>126</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox</cstring>
+ </property>
+ <property name="title">
+ <string>Connection</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cbUseEncryption</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Enable encryption (secure, but slow and not always possible)</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable this option to encrypt the connection. Only newer servers support this option. Encrypting prevents others from eavesdropping, but can slow down the connection considerably.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cbUseKWallet</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Use K&amp;Wallet for passwords</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable this option to store your passwords with KWallet.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>connectionTypeLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Connection &amp;type:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>cmbQuality</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>High Quality (LAN, direct connection)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Medium Quality (DSL, Cable, fast Internet)</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Low Quality (Modem, ISDN, slow Internet)</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmbQuality</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>280</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this to specify the performance of your connection. Note that you should select the speed of the weakest link - even if you have a high speed connection, it will not help you if the remote computer uses a slow modem. Choosing a level of quality that is too high on a slow link will cause slower response times. Choosing a lower quality will increase latencies in high speed connections and results in lower image quality, especially in 'Low Quality' mode.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>Spacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>84</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cbShowPrefs</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Show this dialog again for this host</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Select this option if you do not want to be asked for the settings when connecting to a host. For hosts with existing profiles these profiles will be taken. New hosts will be configured with the defaults.</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+ <include location="local" impldecl="in implementation">vncprefs.ui.h</include>
+</includes>
+<functions>
+ <function specifier="non virtual">setQuality( int quality )</function>
+ <function specifier="non virtual" returnType="int">quality()</function>
+ <function specifier="non virtual">setShowPrefs( bool b )</function>
+ <function specifier="non virtual" returnType="int">showPrefs()</function>
+ <function specifier="non virtual">setUseEncryption( bool b )</function>
+ <function specifier="non virtual" returnType="bool">useEncryption()</function>
+ <function specifier="non virtual">setUseKWallet( bool b )</function>
+ <function specifier="non virtual" returnType="bool">useKWallet()</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/krdc/vnc/vncprefs.ui.h b/krdc/vnc/vncprefs.ui.h
new file mode 100644
index 00000000..a3438f05
--- /dev/null
+++ b/krdc/vnc/vncprefs.ui.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+void VncPrefs::setQuality( int quality )
+{
+ cmbQuality->setCurrentItem(quality);
+}
+
+
+int VncPrefs::quality()
+{
+ return cmbQuality->currentItem();
+}
+
+
+void VncPrefs::setShowPrefs( bool b )
+{
+ cbShowPrefs->setChecked(b);
+}
+
+
+int VncPrefs::showPrefs()
+{
+ return cbShowPrefs->isChecked();
+}
+
+
+void VncPrefs::setUseEncryption( bool b )
+{
+ cbUseEncryption->setChecked(b);
+}
+
+
+bool VncPrefs::useEncryption()
+{
+ return cbUseEncryption->isChecked();
+}
+
+void VncPrefs::setUseKWallet( bool b )
+{
+ cbUseKWallet->setChecked(b);
+}
+
+bool VncPrefs::useKWallet()
+{
+ return cbUseKWallet->isChecked();
+}
diff --git a/krdc/vnc/vnctypes.h b/krdc/vnc/vnctypes.h
new file mode 100644
index 00000000..15329ee2
--- /dev/null
+++ b/krdc/vnc/vnctypes.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2002 Tim Jansen. All Rights Reserved.
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef VNCTYPES_H
+#define VNCTYPES_H
+
+#if(defined __cplusplus)
+extern "C"
+{
+#endif
+
+#include <X11/Xmd.h>
+
+
+typedef struct {
+ int shareDesktop; /* bool */
+ int viewOnly; /* bool */
+
+ const char* encodingsString;
+
+ int useBGR233; /* bool */
+ int nColours;
+ int useSharedColours; /* bool */
+ int requestedDepth;
+
+ int rawDelay;
+ int copyRectDelay;
+
+ int debug; /* bool */
+
+ int compressLevel;
+ int qualityLevel;
+ int dotCursor; /* bool */
+
+} AppData;
+
+
+enum InitStatus {
+ INIT_OK = 0,
+ INIT_NAME_RESOLUTION_FAILURE = 1,
+ INIT_PROTOCOL_FAILURE = 2,
+ INIT_CONNECTION_FAILED = 3,
+ INIT_AUTHENTICATION_FAILED = 4,
+ INIT_NO_SERVER = 5,
+ INIT_SERVER_BLOCKED = 6,
+ INIT_ABORTED = 7
+};
+
+
+#if(defined __cplusplus)
+}
+#endif
+
+#endif
diff --git a/krdc/vnc/vncviewer.h b/krdc/vnc/vncviewer.h
new file mode 100644
index 00000000..dcc66cde
--- /dev/null
+++ b/krdc/vnc/vncviewer.h
@@ -0,0 +1,186 @@
+#ifndef VNCVIEWER_H
+#define VNCVIEWER_H
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * vncviewer.h
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xmu/StdSel.h>
+#include "vnctypes.h"
+
+#if(defined __cplusplus)
+extern "C"
+{
+#endif
+
+#include "rfbproto.h"
+
+extern int endianTest;
+
+#define Swap16IfLE(s) \
+ (*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s))
+
+#define Swap32IfLE(l) \
+ (*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \
+ (((l) & 0x00ff0000) >> 8) | \
+ (((l) & 0x0000ff00) << 8) | \
+ (((l) & 0x000000ff) << 24)) : (l))
+
+#define MAX_ENCODINGS 20
+
+
+/** kvncview.cpp **/
+
+extern AppData appData;
+
+extern Display* dpy;
+extern const char *vncServerHost;
+extern int vncServerPort;
+
+extern int isQuitFlagSet();
+extern int getPassword(char *passwd, int pwlen);
+extern void DrawScreenRegion(int x, int y, int width, int height);
+extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height);
+extern void LockFramebuffer();
+extern void UnlockFramebuffer();
+extern void EnableClientCursor(int state);
+extern void beep();
+extern void newServerCut(char *bytes, int len);
+extern void postMouseEvent(int x, int y, int buttonMask);
+
+/** threads.cpp **/
+
+extern void queueIncrementalUpdateRequest();
+extern void announceIncrementalUpdateRequest();
+
+/* colour.c */
+
+extern unsigned long BGR233ToPixel[];
+
+extern Colormap cmap;
+extern Visual *vis;
+extern unsigned int visdepth, visbpp;
+
+extern void SetVisualAndCmap(void);
+
+/* desktop.c */
+
+extern Widget form, viewport, desktop;
+extern GC gc;
+extern GC srcGC, dstGC;
+extern Dimension dpyWidth, dpyHeight;
+
+extern void DesktopInit(Window win);
+extern void ToplevelInit(void);
+extern void SendRFBEvent(XEvent *event, String *params, Cardinal *num_params);
+extern void CopyDataToScreen(char *buf, int x, int y, int width, int height);
+extern void CopyDataFromScreen(char *buf, int x, int y, int width, int height);
+extern void FillRectangle8(CARD8, int x, int y, int width, int height);
+extern void FillRectangle16(CARD16, int x, int y, int width, int height);
+extern void FillRectangle32(CARD32, int x, int y, int width, int height);
+extern void CopyArea(int srcX, int srcY, int width, int height, int x, int y);
+extern void SyncScreenRegion(int x, int y, int width, int height);
+extern void SyncScreenRegionX11Thread(int x, int y, int width, int height);
+extern void drawCursor(void);
+extern void DrawCursorX11Thread(int x, int y);
+extern void undrawCursor(void);
+extern void getBoundingRectCursor(int cx, int cy, int _imageIndex,
+ int *x, int *y, int *w, int *h);
+extern int rectsIntersect(int x, int y, int w, int h,
+ int x2, int y2, int w2, int h2);
+extern int rectContains(int outX, int outY, int outW, int outH,
+ int inX, int inY, int inW, int inH);
+extern void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1,
+ int x2, int y2, int w2, int h2);
+extern void DrawZoomedScreenRegionX11Thread(Window win, int zwidth,
+ int zheight,
+ int x, int y,
+ int width, int height);
+extern void DrawScreenRegionX11Thread(Window win, int x, int y,
+ int width, int height);
+extern void Cleanup(void);
+extern XImage *CreateShmZoomImage(void);
+extern XImage *CreateShmImage(void);
+extern void ShmCleanup(void);
+extern void freeDesktopResources(void);
+
+/* rfbproto.c */
+
+extern int rfbsock;
+extern Bool canUseCoRRE;
+extern Bool canUseHextile;
+extern char *desktopName;
+extern rfbPixelFormat myFormat;
+extern rfbServerInitMsg si;
+
+extern int cursorX, cursorY;
+extern int imageIndex;
+typedef struct {
+ int set;
+ int w, h;
+ int hotX, hotY;
+ int len;
+ char *image;
+} PointerImage;
+extern PointerImage pointerImages[];
+
+extern int ConnectToRFBServer(const char *hostname, int port);
+extern enum InitStatus InitialiseRFBConnection(void);
+extern Bool SetFormatAndEncodings(void);
+extern Bool SendIncrementalFramebufferUpdateRequest(void);
+extern Bool SendFramebufferUpdateRequest(int x, int y, int w, int h,
+ Bool incremental);
+extern Bool SendPointerEvent(int x, int y, int buttonMask);
+extern Bool SendKeyEvent(CARD32 key, Bool down);
+extern Bool SendClientCutText(const char *str, int len);
+extern Bool HandleRFBServerMessage(void);
+
+extern void PrintPixelFormat(rfbPixelFormat *format);
+extern void freeRFBProtoResources(void);
+extern void freeResources(void);
+
+/* sockets.c */
+
+extern Bool errorMessageOnReadFailure;
+
+extern Bool ReadFromRFBServer(char *out, unsigned int n);
+extern Bool WriteExact(int sock, const char *buf, int n);
+extern int ConnectToTcpAddr(unsigned int host, int port);
+
+extern int StringToIPAddr(const char *str, unsigned int *addr);
+extern void freeSocketsResources(void);
+
+#if(defined __cplusplus)
+}
+#endif
+#endif
diff --git a/krdc/vnc/zlib.c b/krdc/vnc/zlib.c
new file mode 100644
index 00000000..80c4eeea
--- /dev/null
+++ b/krdc/vnc/zlib.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * zlib.c - handle zlib encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles an zlib
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#define HandleZlibBPP CONCAT2E(HandleZlib,BPP)
+#define CARDBPP CONCAT2E(CARD,BPP)
+
+static Bool
+HandleZlibBPP (int rx, int ry, int rw, int rh)
+{
+ rfbZlibHeader hdr;
+ int remaining;
+ int inflateResult;
+ int toRead;
+
+ /* First make sure we have a large enough raw buffer to hold the
+ * decompressed data. In practice, with a fixed BPP, fixed frame
+ * buffer size and the first update containing the entire frame
+ * buffer, this buffer allocation should only happen once, on the
+ * first update.
+ */
+ if ( raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) {
+
+ if ( raw_buffer != NULL ) {
+
+ free( raw_buffer );
+
+ }
+
+ raw_buffer_size = (( rw * rh ) * ( BPP / 8 ));
+ raw_buffer = (char*) malloc( raw_buffer_size );
+
+ }
+
+ if (!ReadFromRFBServer((char *)&hdr, sz_rfbZlibHeader))
+ return False;
+
+ remaining = Swap32IfLE(hdr.nBytes);
+
+ /* Need to initialize the decompressor state. */
+ decompStream.next_in = ( Bytef * )buffer;
+ decompStream.avail_in = 0;
+ decompStream.next_out = ( Bytef * )raw_buffer;
+ decompStream.avail_out = raw_buffer_size;
+ decompStream.data_type = Z_BINARY;
+
+ /* Initialize the decompression stream structures on the first invocation. */
+ if ( decompStreamInited == False ) {
+
+ inflateResult = inflateInit( &decompStream );
+
+ if ( inflateResult != Z_OK ) {
+ fprintf(stderr,
+ "inflateInit returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return False;
+ }
+
+ decompStreamInited = True;
+
+ }
+
+ inflateResult = Z_OK;
+
+ /* Process buffer full of data until no more to process, or
+ * some type of inflater error, or Z_STREAM_END.
+ */
+ while (( remaining > 0 ) &&
+ ( inflateResult == Z_OK )) {
+
+ if ( remaining > BUFFER_SIZE ) {
+ toRead = BUFFER_SIZE;
+ }
+ else {
+ toRead = remaining;
+ }
+
+ /* Fill the buffer, obtaining data from the server. */
+ if (!ReadFromRFBServer(buffer,toRead))
+ return False;
+
+ decompStream.next_in = ( Bytef * )buffer;
+ decompStream.avail_in = toRead;
+
+ /* Need to uncompress buffer full. */
+ inflateResult = inflate( &decompStream, Z_SYNC_FLUSH );
+
+ /* We never supply a dictionary for compression. */
+ if ( inflateResult == Z_NEED_DICT ) {
+ fprintf(stderr,"zlib inflate needs a dictionary!\n");
+ return False;
+ }
+ if ( inflateResult < 0 ) {
+ fprintf(stderr,
+ "zlib inflate returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return False;
+ }
+
+ /* Result buffer allocated to be at least large enough. We should
+ * never run out of space!
+ */
+ if (( decompStream.avail_in > 0 ) &&
+ ( decompStream.avail_out <= 0 )) {
+ fprintf(stderr,"zlib inflate ran out of space!\n");
+ return False;
+ }
+
+ remaining -= toRead;
+
+ } /* while ( remaining > 0 ) */
+
+ if ( inflateResult == Z_OK ) {
+
+ /* Put the uncompressed contents of the update on the screen. */
+ CopyDataToScreen(raw_buffer, rx, ry, rw, rh);
+
+ }
+ else {
+
+ fprintf(stderr,
+ "zlib inflate returned error: %d, msg: %s\n",
+ inflateResult,
+ decompStream.msg);
+ return False;
+
+ }
+
+ return True;
+}
diff --git a/krfb/AUTHORS b/krfb/AUTHORS
new file mode 100644
index 00000000..adf3d018
--- /dev/null
+++ b/krfb/AUTHORS
@@ -0,0 +1,2 @@
+Tim Jansen <tim@tjansen.de>
+Ian Reinhart Geiser <geiseri@kde.org> \ No newline at end of file
diff --git a/krfb/DCOP-INTERFACE b/krfb/DCOP-INTERFACE
new file mode 100644
index 00000000..1bab1ea2
--- /dev/null
+++ b/krfb/DCOP-INTERFACE
@@ -0,0 +1,9 @@
+DCOP Interfaces:
+
+// Exits the application, disconnects clients
+void exit()
+// Set the desktop to be controlable from remote clients
+void setAllowDesktopControl(bool)
+
+
+
diff --git a/krfb/Makefile.am b/krfb/Makefile.am
new file mode 100644
index 00000000..baa494d9
--- /dev/null
+++ b/krfb/Makefile.am
@@ -0,0 +1,8 @@
+SUBDIRS = srvloc kinetd libvncserver krfb kcm_krfb krfb_httpd
+
+EXTRA_DIST = AUTHORS README TODO NOTES DCOP-INTERFACE
+
+# not a GNU package. You can remove this line, if
+# have all needed files, that a GNU package needs
+AUTOMAKE_OPTIONS = foreign
+
diff --git a/krfb/NOTES b/krfb/NOTES
new file mode 100644
index 00000000..24b30178
--- /dev/null
+++ b/krfb/NOTES
@@ -0,0 +1,27 @@
+Comments on various aspects of KRfb:
+
+- KRfb has been designed for three use cases:
+ * a user who needs help from an administrator or friend. The adminstrator can
+ connect to the user and change settings and so on while both are talking
+ on the telephone or using VoIP.
+ * a user who wants to show something to a friend, so he lets his friend
+ connect to his computer
+ * (advanced use case) somebody with several computers, that are running
+ GUIs, wants to control them.
+- cases 1&2 are probably more mainstream and more important for novice users,
+ so KRfb is pre-configured for them. Case 3 is for advanced users and
+ therefore a little bit more difficult to configure.
+- design goal of KRfb is to make it as easy to use as possible. I tried to
+ limit functionality whereever possible.
+- the new-connection-dialog is extra large and has the pixmap on the left
+ side to capture the attention of the user before allowing a connection.
+- the RFBController class is a mess. The interactions between the threaded,
+ callback-using libvncserver and the event-driven, single thread qt GUI are
+ quite complicated and I can only hope that it works.
+- most limitations and problems of KRfb are caused either by limitations of
+ Rfb (for example no proper authentication of users, no encryption) or
+ by lack of a framework in Linux in general (no way to connect through a
+ NAT device). In the next months I am going to concentrate on improving the
+ latter.
+
+tim@tjansen.de
diff --git a/krfb/README b/krfb/README
new file mode 100644
index 00000000..ada74b6d
--- /dev/null
+++ b/krfb/README
@@ -0,0 +1,19 @@
+KDE Desktop Sharing (krfb)
+==========================
+
+KDE Desktop Sharing (krfb) is a small server for the RFB protocol, better
+known as VNC. Unlike most other Unix/Linux RFB servers, KRfb allows you to
+share your X11 session instead of creating a new X11 session.
+It was originally based on x0rfbserver
+(ttp://www.hexonet.de/software/x0rfbserver/), but there is not much code of
+x0rfbserver left. Since version 0.6 it uses libvncserver
+(http://libvncserver.sf.net) as backend.
+
+
+Guide to documentation:
+TODO - things to be done
+INSTALL - Very short installation instructions
+NOTES - reasons for various decisions
+DCOP-INTERFACE - short documentation of the DCOP interface
+
+
diff --git a/krfb/TODO b/krfb/TODO
new file mode 100644
index 00000000..1258e590
--- /dev/null
+++ b/krfb/TODO
@@ -0,0 +1,28 @@
+
+For 3.2:
+- write SLP service template for remote desktop protocols
+ (documentation)
+- enhance RFB with SASL authentication (Kerberos)
+- encrypted connections (using SASL and/or SSL/TLS)
+- with kerberos/ssl: display name of remote user in connection dialog,
+ kpassivepopup and systray (if name is available)
+- mention that invitations are one-time on personal invitation dialog
+
+Todo (unscheduled features):
+- when krfb is started with URL arguments and without connection
+ quality, add some kind of smart algorithm to determine whether the
+ other host is local (maybe using SLP to announce the connectivity
+ of a LAN)
+- NAT traversal support if I can find an acceptable implementation
+ (probably using TURN, as soon as there is a server and newer spec for that)
+- when OpenSLP supports this, allow scope configuration
+- split krfb into 2 separate programs (server and invitation)
+- look into adding an extension to xfree to improve speed (get noticed of
+ screen updates)
+- cut & paste support
+
+Known bugs/problems:
+- the IP address sent in invitation may be wrong on multi-homed machines,
+ and it is always incorrect behind a NAT. Right now it is not possible
+ to solve these problems.
+
diff --git a/krfb/configure.in.in b/krfb/configure.in.in
new file mode 100644
index 00000000..d3715c31
--- /dev/null
+++ b/krfb/configure.in.in
@@ -0,0 +1,21 @@
+KDE_CHECK_HEADER(X11/extensions/XTest.h,
+ [],
+ AC_MSG_ERROR([XTest extension header not found / no xlib headers]))
+
+#check for getifaddrs(3) (as in glibc >= 2.3 and newer bsds)
+AC_MSG_CHECKING(for getifaddrs support)
+AC_TRY_LINK( [
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <ifaddrs.h>
+ ],[
+ getifaddrs(0);
+ ],[
+ AC_DEFINE(HAVE_GETIFADDRS,1,[Define if getifaddrs is available])
+ AC_MSG_RESULT(yes)
+ COMPILE_GETIFADDRS=""
+ ],[
+ AC_MSG_RESULT(no)
+ COMPILE_GETIFADDRS="getifaddrs.cpp"
+])
+AC_SUBST(COMPILE_GETIFADDRS)
diff --git a/krfb/kcm_krfb/Makefile.am b/krfb/kcm_krfb/Makefile.am
new file mode 100644
index 00000000..18dede23
--- /dev/null
+++ b/krfb/kcm_krfb/Makefile.am
@@ -0,0 +1,20 @@
+METASOURCES = AUTO
+
+# Code
+kde_module_LTLIBRARIES = kcm_krfb.la
+
+kcm_krfb_la_SOURCES = configurationwidget.ui kcm_krfb.cpp
+kcm_krfb_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kcm_krfb_la_LIBADD = ../krfb/libkrfbconfig.la $(LIB_KDEUI) -lkio
+
+# Services
+xdg_apps_DATA = kcmkrfb.desktop
+
+EXTRA_DIST = $(kcm_krfb_la_SOURCES)\
+ $(xdg_apps_DATA)
+
+# set the include path for X, qt and KDE
+INCLUDES= -I../krfb $(all_includes)
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/kcm_krfb.pot
diff --git a/krfb/kcm_krfb/configurationwidget.ui b/krfb/kcm_krfb/configurationwidget.ui
new file mode 100644
index 00000000..66198269
--- /dev/null
+++ b/krfb/kcm_krfb/configurationwidget.ui
@@ -0,0 +1,503 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ConfigurationWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigurationWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>421</width>
+ <height>445</height>
+ </rect>
+ </property>
+ <property name="icon">
+ <pixmap>image0</pixmap>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>TabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>Acc&amp;ess</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="title">
+ <string>Invitations</string>
+ </property>
+ <property name="alignment">
+ <set>AlignAuto</set>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>invitationNumLabel</cstring>
+ </property>
+ <property name="text">
+ <string>You have no open invitations.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>manageInvitations</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Create &amp;&amp; &amp;Manage Invitations...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click to view or delete the open invitations.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>ButtonGroup7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Uninvited Connections</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allowUninvitedCB</cstring>
+ </property>
+ <property name="text">
+ <string>Allow &amp;uninvited connections</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Select this option to allow connecting without inviting. This is useful if you want to access your desktop remotely.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>enableSLPCB</cstring>
+ </property>
+ <property name="text">
+ <string>Announce service &amp;on the network</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you allow uninvited connections and enable this option, Desktop Sharing will announce the service and your identity on the local network, so people can find you and your computer.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>confirmConnectionsCB</cstring>
+ </property>
+ <property name="text">
+ <string>Confirm uninvited connections &amp;before accepting</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If enabled, a dialog will appear when somebody attempts to connect, asking you whether you want to accept the connection.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allowDesktopControlCB</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;llow uninvited connections to control the desktop</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Enable this option to allow uninvited user to control your desktop (using mouse and keyboard).</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="layoutMargin" stdset="0">
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>6</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>passwordInput</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>passwordInput</cstring>
+ </property>
+ <property name="maxLength">
+ <number>9</number>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you allow uninvited connections, it is highly recommended to set a password in order to protect your computer from unauthorized access.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>50</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Session</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Session Preferences</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>disableBackgroundCB</cstring>
+ </property>
+ <property name="text">
+ <string>Always disable &amp;background image</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this option to always disable the background image during a remote session. Otherwise the client decides whether the background will be enabled or disabled.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Network</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox3</cstring>
+ </property>
+ <property name="title">
+ <string>Network Port</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>autoPortCB</cstring>
+ </property>
+ <property name="text">
+ <string>Assi&amp;gn port automatically</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Check this option to assign the network port automatically. This is recommended unless your network setup requires you to use a fixed port, for example because of a firewall.</string>
+ </property>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>portInputFrame</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>TextLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>P&amp;ort:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>portInput</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>portInput</cstring>
+ </property>
+ <property name="value">
+ <number>5900</number>
+ </property>
+ <property name="minValue">
+ <number>1024</number>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter the TCP port number here</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Use this field to set a static port number for the desktop sharing service. Note that if the port is already in use the Desktop Sharing service will not be accessible until you free it. It is recommended to assign the port automatically unless you know what you are doing.
+Most VNC clients use a display number instead of the actual port. This display number is the offset to port 5900, so port 5901 has the display number 1.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="8078">789cdd98c7721cc91186ef7c0a04f3c650d4b637a1d001de5b8200880d1db2da604038c29b0dbdbb32ffeceec582b30448015a85fa2726e69b32592633ab9abf7c98d8db589df8f0cbbbcb2bbe3aac26aa115f4c7ca8af4f4eee7ffde73f7e7bf73e2926e45f144413d1fbbfbd7bbf7935514dac9d9d360a540a50204f91827707f6e09d9ecb045c0c8c726e7be600e579cfde787960eb7f69e01cbc38b08d67a6e7cadaef0d8cc15336b0f5b73270095e1898c1d33dd7d65f3a30ca997b6eac7c75e00a3cdf738bf9f36dc789f5cfdd78c3a8e3bb9ebbfa67c651d0b4e059e3b4ecca0fc06110e636ff29701224b5958fc07990e7982f1f838ba0c863d4bfeb990bf06dcfb65eecc0555065980faff45cdafc6e94c57c5085e0497012266cfd7f06a761da98fd7be3346d6d3e042ec222857d5eee39afc14b3d97369e4d30879cc31e1f82abb04a8dbf2847611426d6df09b8888a18fdd145cf564ee7039bbd4fe0322a131baf0773c4dd78578de3dcfc81bf1a6741577e04aea22a36ffb4f2366a538cdf61be71a002c37f623c688f7889b3986d3c740dce55ca1efb19972a947f04b30a7c05ae5460ac775cabd0ff3eb851c13ee697a8c398ff263d77fe760ad60db5f5c57e26910aed2b70ac42f95ec789ad1f617f123c28dfedd8a7660ff3497215cae14f89ec4e6bf383ff279cb0ad1f617f13af427dc46bd2a850bedf73b71f5bc659d2f9f38331e7564e985faa0e6af35b372eb8b6f961fc69a44239e22b8d55e035e33ca96dbf108f291e30fc2fcd5560c45f5aa49dbfd343cfa5cd7f71602b877fa4a50ae3413e49598572f853ea5528477e936013a11cf34f5b1518fb95052ad4477916aac073e04805463ece6215da07e04485f20d70aa02637db24285f94c0f0cfb0ef3cd4a15fac3fa659c716bf9cfec3377f1e642709575f9c7213eb24605b6f1b42af477a98c70c1781dfc01ee65feb7615c8a03a17c1b8c0d427bc4439ea9c0c87f391eb4afc1850aede17f79a902233fe75e0546fce5950a6ced1b1518f92c6f5560c46fa109d8e263aee7ce5fd1bed08465fb83f82f621518fe05f7b2fd803f633b301f1781310130fcadc083fe91bfc4f94428473e90c34104463e2abc0aeb61e5f5b01e8db10a8cfd281a15ea6f1bcbf6dbfa20df16ad0a8c7c50062a30e2bfd4846cf385bf20fd59fc7d34f67965ed11cf65a2c27c62b0844fe74fc83f703fac8747fc9479ef1fd41aabd03fe683e5b0f9e1bc2ef18011dfa55781719e9655d99d8f0ef9a0ac55b067fd377dff0ee74fd9aac028e780bbfb9047bc72d8f7478db10af5b15fac0794e527c4030febe30f3aeec7931aab501ff1cdc9b0bf38df905ecc7f70bf630d50ac175bfba2efdf4d1b73777f7038afb954c13efc957def8fdec65f0dfe3569ac42ff8877ae5311eab3f130be0b631518e7918f5460ecb78f55609cbf382e2cfe71fff4591cc6b67e386fe0ce367e9c5fd84e1b1fee239e55606b5fa9c0b82ff95a85fe111fbef56d63fb8178ab8234b0f3cee1fe54e985c5ce1b8cb78afaf3c0e17cc3f16dfb83f8a9121518f9bd2afaf561ac078663f1087fa9e5f6d79d7fe71db71d633feb34f4a1ddcf118f75a502aff7dcd59f346eea8e919f6a3cd81fe4c7bad11b0f18f9b389faf5a559631518ebd170d3e577c27e3778c0c8cf4807666fbe63dfd547be40b8d8f981fcd9e251debc7a5bfd3fd970f4b6ea6cb0f3ae7a1379c79d0def6ad7bc896ae73b1b9550fb266a5cf5071b076ef48a3318497fdfda18b9c357dc8943e9ef5b1bc2afe84f439fdfda18efe3eecb78fd594cfc6fd9403f47eed89db85377e6beba737c5ec86f47ee12ba72d7eee65b7b2fb1d18df356dadfc1c6bd7b7093d2df949b76336e56becdb979b720658b6ec92dbb15b7ea7ecec68d5b73eb9d8d0de97bd36db98f6e5b2cad49ff7332ab4f52b2e376dd9eb4fe211bd2fb9e8cebb3accb2c32c0a6db7781f4baef42c8beebe7ba8bc4eeb1cc65512c0d6bf6021bb72e76898c3b95fe16e4db9cacd4becb9e7c6ebb5c4a75fd1624166a996d2163bb79a18d52685dfa08c9c96865e5895c46fce433971a819b222f9f3955549346f7f5f33630df496aa5fd9cf4f3209f0734a2c331fa227bd4d721cc6ec10574841ebe6fe356b4a4eb43c774e20ee994cee82b9dd3c5139dcbaf67526a3aa64bf7402c6b78891ebe6fa3141d637d8ec5c2155d4b5f37744b774f742bbf7e9552d3bd58913594b98cd0c3f76d8c44fbb63e92d7127aa0499aa2699a79a269f975524a5b95ccbb5fbd7df1e6fbefdba04b99f59c3b84029aa5399aa705e973919668b9d392d00cadd02aadd13a6dd026cdcabea38dcc678bb69eb1712ffa28b557685b2c7c1a6c2cd10eed76da119aa13db1f299f6c5caa6d49ba56d8976b1213bbff0cc5a8928704b144acb88624a28a58c722aa864c7e2b4eca814ca75c5e4a5de7325354da15be25a6672fcbc0d6eac8dbc257866f4d5f2018ff810a587f2ed80db3136b44df3fcf9f1a73646fc858f507a24df466f62e3984ffa338e4f84feaa79c8ce4b0efa791b2fd90ff1603e7d990dc955f0abdf6d8cf5ab8c523ee3af7cde8de8c2ad0f169ecfbb44a77cc9577ccd3712012b120963e2836ff98eeff98127bb55bab4de5f98dbe517b132c5d37c2d91bc2a56c6c439cff02ccff124cf77abb4f023365047577ec15df2222ff132af3ccd57bcca6bbcce1bac2f5f8dd45b787cdff8817bc97023e3cda77997b7f823eb4938d4f9c9bbcfef363e3d3d3f788777794f6efcffa18d71d69eeaf5ee893faabfdec6ab69bc0dbc7ff067de7f95b7c3f1ef1f788fa223b9535db9377a8fe280438e38e6c48d38e54cbe871cd87b1d37b84b8ce49e7b80d1ac0ddf1f973eae33f67d90732eb8f4ce935bf3ec3d97c27957774dee43aa44eef1adfc25c3f7c7a58feb8cb5e12bce7ded1b2fe3f40762a516aebaba07729b55ddc93b482b7f77c3f7c7a58feb3cb1e147725b997a0bd1961ffd17ff2fe36d6dfcebefeffe0dd22dc353</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>autoPortCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>portInputFrame</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowUninvitedCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>confirmConnectionsCB</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowUninvitedCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>allowDesktopControlCB</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>allowUninvitedCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>Frame4</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>autoPortCB</tabstop>
+ <tabstop>TabWidget2</tabstop>
+ <tabstop>manageInvitations</tabstop>
+ <tabstop>allowUninvitedCB</tabstop>
+ <tabstop>confirmConnectionsCB</tabstop>
+ <tabstop>allowDesktopControlCB</tabstop>
+ <tabstop>passwordInput</tabstop>
+ <tabstop>portInput</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/krfb/kcm_krfb/kcm_krfb.cpp b/krfb/kcm_krfb/kcm_krfb.cpp
new file mode 100644
index 00000000..5647d557
--- /dev/null
+++ b/krfb/kcm_krfb/kcm_krfb.cpp
@@ -0,0 +1,180 @@
+
+/***************************************************************************
+ kcm_krfb.cpp
+ --------------
+ begin : Sat Mar 02 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kcm_krfb.h"
+#include "kcm_krfb.moc"
+
+#include <qlayout.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qlineedit.h>
+#include <qbuttongroup.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <kapplication.h>
+#include <kdialog.h>
+#include <knuminput.h>
+#include <klocale.h>
+#include <kaboutdata.h>
+#include <kconfig.h>
+#include <kgenericfactory.h>
+#include <kdatastream.h>
+#include <kdebug.h>
+#include <dcopclient.h>
+
+#undef VERSION
+#define VERSION "0.7"
+
+
+typedef KGenericFactory<KcmKRfb, QWidget> KcmKRfbFactory;
+
+// Can't use K_EXPORT_COMPONENT_FACTORY, since insertCatalogue necessary
+extern "C" {
+ KDE_EXPORT void *init_kcm_krfb() {
+ KGlobal::locale()->insertCatalogue("krfb"); // For invitation translations
+ return new KcmKRfbFactory("kcm_krfb");
+ }
+}
+
+
+KcmKRfb::KcmKRfb(QWidget *p, const char *name, const QStringList &) :
+ KCModule(KcmKRfbFactory::instance(), p, name),
+ m_configuration(KRFB_CONFIGURATION_MODE) {
+
+ m_confWidget = new ConfigurationWidget(this);
+
+ QVBoxLayout *l = new QVBoxLayout(this, 0, KDialog::spacingHint());
+ l->add(m_confWidget);
+
+ setButtons(Default|Apply|Reset);
+
+ KAboutData* about = new KAboutData( "kcm_krfb", I18N_NOOP("Desktop Sharing Control Module"),
+ VERSION,
+ I18N_NOOP("Configure desktop sharing"), KAboutData::License_GPL,
+ "(c) 2002, Tim Jansen\n",
+ 0, "http://www.tjansen.de/krfb", "tim@tjansen.de");
+ about->addAuthor("Tim Jansen", 0, "tim@tjansen.de");
+ setAboutData( about );
+
+ load();
+
+ connect(m_confWidget->passwordInput, SIGNAL(textChanged(const QString&)), SLOT(configChanged()) );
+ connect(m_confWidget->allowUninvitedCB, SIGNAL(clicked()), SLOT(configChanged()) );
+ connect(m_confWidget->enableSLPCB, SIGNAL(clicked()), SLOT(configChanged()) );
+ connect(m_confWidget->confirmConnectionsCB, SIGNAL(clicked()), SLOT(configChanged()) );
+ connect(m_confWidget->allowDesktopControlCB, SIGNAL(clicked()), SLOT(configChanged()) );
+ connect(m_confWidget->autoPortCB, SIGNAL(clicked()), SLOT(configChanged()) );
+ connect(m_confWidget->portInput, SIGNAL(valueChanged(int)), SLOT(configChanged()) );
+ connect((QObject*)m_confWidget->manageInvitations, SIGNAL(clicked()),
+ &m_configuration, SLOT(showManageInvitationsDialog()) );
+ connect(&m_configuration, SIGNAL(invitationNumChanged(int)),
+ this, SLOT(setInvitationNum(int)));
+ setInvitationNum(m_configuration.invitations().size());
+ connect(m_confWidget->disableBackgroundCB, SIGNAL(clicked()), SLOT(configChanged()) );
+}
+
+void KcmKRfb::configChanged() {
+ emit changed(true);
+}
+
+void KcmKRfb::setInvitationNum(int num) {
+ if (num == 0)
+ m_confWidget->invitationNumLabel->setText(i18n("You have no open invitation."));
+ else
+ m_confWidget->invitationNumLabel->setText(i18n("Open invitations: %1").arg(num));
+}
+
+void KcmKRfb::checkKInetd(bool &kinetdAvailable, bool &krfbAvailable) {
+ kinetdAvailable = false;
+ krfbAvailable = false;
+
+ DCOPClient *d = KApplication::dcopClient();
+
+ QByteArray sdata, rdata;
+ QCString replyType;
+ QDataStream arg(sdata, IO_WriteOnly);
+ arg << QString("krfb");
+ if (!d->call ("kded", "kinetd", "isInstalled(QString)", sdata, replyType, rdata))
+ return;
+
+ if (replyType != "bool")
+ return;
+
+ QDataStream answer(rdata, IO_ReadOnly);
+ answer >> krfbAvailable;
+ kinetdAvailable = true;
+}
+
+void KcmKRfb::load() {
+ bool kinetdAvailable, krfbAvailable;
+ checkKInetd(kinetdAvailable, krfbAvailable);
+
+ m_confWidget->allowUninvitedCB->setChecked(m_configuration.allowUninvitedConnections());
+ m_confWidget->enableSLPCB->setChecked(m_configuration.enableSLP());
+ m_confWidget->confirmConnectionsCB->setChecked(m_configuration.askOnConnect());
+ m_confWidget->allowDesktopControlCB->setChecked(m_configuration.allowDesktopControl());
+ m_confWidget->passwordInput->setText(m_configuration.password());
+ m_confWidget->autoPortCB->setChecked(m_configuration.preferredPort()<0);
+ m_confWidget->portInput->setValue(m_configuration.preferredPort()> 0 ?
+ m_configuration.preferredPort() : 5900);
+ m_confWidget->disableBackgroundCB->setChecked(m_configuration.disableBackground());
+ emit changed(false);
+}
+
+void KcmKRfb::save() {
+
+ m_configuration.update();
+ bool allowUninvited = m_confWidget->allowUninvitedCB->isChecked();
+ m_configuration.setAllowUninvited(allowUninvited);
+ m_configuration.setEnableSLP(m_confWidget->enableSLPCB->isChecked());
+ m_configuration.setAskOnConnect(m_confWidget->confirmConnectionsCB->isChecked());
+ m_configuration.setAllowDesktopControl(m_confWidget->allowDesktopControlCB->isChecked());
+ m_configuration.setPassword(m_confWidget->passwordInput->text());
+ if (m_confWidget->autoPortCB->isChecked())
+ m_configuration.setPreferredPort(-1);
+ else
+ m_configuration.setPreferredPort(m_confWidget->portInput->value());
+ m_configuration.setDisableBackground(m_confWidget->disableBackgroundCB->isChecked());
+ m_configuration.save();
+ kapp->dcopClient()->emitDCOPSignal("KRFB::ConfigChanged", "KRFB_ConfigChanged()", QByteArray());
+ emit changed(false);
+}
+
+void KcmKRfb::defaults() {
+ bool kinetdAvailable, krfbAvailable;
+ checkKInetd(kinetdAvailable, krfbAvailable);
+
+ m_confWidget->allowUninvitedCB->setChecked(false);
+ m_confWidget->enableSLPCB->setChecked(true);
+ m_confWidget->confirmConnectionsCB->setChecked(false);
+ m_confWidget->allowDesktopControlCB->setChecked(false);
+ m_confWidget->passwordInput->setText("");
+ m_confWidget->autoPortCB->setChecked(true);
+ m_confWidget->portInput->setValue(5900);
+ m_confWidget->disableBackgroundCB->setChecked(false);
+ emit changed(true);
+}
+
+QString KcmKRfb::quickHelp() const
+{
+ return i18n("<h1>Desktop Sharing</h1> This module allows you to configure"
+ " the KDE desktop sharing.");
+}
+
+
diff --git a/krfb/kcm_krfb/kcm_krfb.h b/krfb/kcm_krfb/kcm_krfb.h
new file mode 100644
index 00000000..e9271289
--- /dev/null
+++ b/krfb/kcm_krfb/kcm_krfb.h
@@ -0,0 +1,47 @@
+
+/***************************************************************************
+ kcm_krfb.h
+ ------------
+ begin : Sat Mar 02 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 _KCM_KRFB_H_
+#define _KCM_KRFB_H_
+
+#include <qobject.h>
+#include <qdatetime.h>
+#include <kcmodule.h>
+#include "configurationwidget.h"
+#include "../krfb/configuration.h"
+
+class KcmKRfb : public KCModule {
+ Q_OBJECT
+private:
+ Configuration m_configuration;
+ ConfigurationWidget *m_confWidget;
+ void checkKInetd(bool&, bool&);
+public:
+ KcmKRfb(QWidget *p, const char *name, const QStringList &);
+
+ void load();
+ void save();
+ void defaults();
+ QString quickHelp() const;
+private slots:
+ void setInvitationNum(int num);
+ void configChanged();
+};
+
+
+#endif
diff --git a/krfb/kcm_krfb/kcmkrfb.desktop b/krfb/kcm_krfb/kcmkrfb.desktop
new file mode 100644
index 00000000..3b95e734
--- /dev/null
+++ b/krfb/kcm_krfb/kcmkrfb.desktop
@@ -0,0 +1,192 @@
+[Desktop Entry]
+Icon=krfb
+Type=Application
+Exec=kcmshell kcmkrfb
+DocPath=krfb/krfb-configuration.html
+
+X-KDE-ModuleType=Library
+X-KDE-Library=krfb
+X-KDE-ParentApp=kcontrol
+
+Name=Desktop Sharing
+Name[af]=Werkskerm Deeling
+Name[ar]=مشاركة سطح المكتب
+Name[be]=Ðгульнае выкарыÑтанне Ñтальніцы
+Name[bg]=СподелÑне на работното мÑÑто
+Name[bn]=ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি
+Name[br]=Rannañ ar vurev
+Name[bs]=Dijeljenje desktopa
+Name[ca]=Compartició de l'escriptori
+Name[cs]=Sdílení pracovní plochy
+Name[cy]=Rhannu Penbwrdd
+Name[da]=Desktopdeling
+Name[de]=Arbeitsfläche freigeben
+Name[el]=Κοινή χÏήση επιφάνειας εÏγασίας
+Name[eo]=Tabuloj komunaj
+Name[es]=Escritorio compartido
+Name[et]=Töölaua jagamine
+Name[eu]=Mahaigain partekatzea
+Name[fa]=اشتراک رومیزی
+Name[fi]=Työpöydän jakaminen
+Name[fr]=Partage de bureau
+Name[ga]=Roinnt Deisce
+Name[gl]=Compartición do escritorio
+Name[he]=שיתוף שולחנות עבודה
+Name[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Name[hr]=Dijeljenje radne površine
+Name[hu]=Munkaasztal-megosztás
+Name[is]=Skjáborðamiðlun
+Name[it]=Condivisione Desktop
+Name[ja]=デスクトップ共有
+Name[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ–იáƒáƒ áƒ”ბáƒ
+Name[kk]=Ò®Ñтелді ортақтаÑтыру
+Name[km]=ការ​ចែក​រំលែក​ផ្ទៃ​ážáž»
+Name[lt]=Dalinimasis darbastaliu
+Name[mk]=Делење на работната површина
+Name[mn]=Ðжлын байрыг хамтран ÑзÑмших
+Name[ms]=Perkongsian Ruang Kerja
+Name[mt]=Qsim tad-desktop
+Name[nb]=Delte skrivebord
+Name[nds]=Schriefdisch-Freegaav
+Name[ne]=डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Name[nl]=Bureaublad delen
+Name[nn]=Skrivebordsdeling
+Name[nso]=Kabagano ya Desktop
+Name[pa]=ਡੈਸਕਟਾਪ ਸਾਂà¨
+Name[pl]=Współdzielenie pulpitu
+Name[pt]=Partilha do Ambiente de Trabalho
+Name[pt_BR]=Compartilhamento do Desktop
+Name[ro]=Partajare ecran
+Name[ru]=Общий рабочий Ñтол
+Name[se]=Čállinbeavdejuohkkin
+Name[sk]=Zdieľanie pracovnej plochy
+Name[sl]=Deljenje namizja
+Name[sr]=Дељење радне површине
+Name[sr@Latn]=Deljenje radne površine
+Name[sv]=Dela ut skrivbordet
+Name[ta]=பணிமேடை பகிரà¯à®µà¯
+Name[tg]=ИÑтифодабарии муштараки Мизи корӣ
+Name[th]=ใช้งานพื้นที่ทำงานร่วมà¸à¸±à¸™
+Name[tr]=Masaüstü Paylaşımı
+Name[uk]=Спільні Ñтільниці
+Name[ven]=U kovhekana ha Desikithopo
+Name[xh]=Ukwehlulelana kwe Desktop
+Name[zh_CN]=æ¡Œé¢å…±äº«
+Name[zh_HK]=æ¡Œé¢åˆ†äº«
+Name[zh_TW]=æ¡Œé¢åˆ†äº«
+Name[zu]=Ukwahlulelana kwe-Desktop
+
+Comment=Configure Desktop Sharing
+Comment[af]=Konfigureer Werkskerm Deeling
+Comment[ar]=تعديل مشاركة سطح المكتب
+Comment[be]=ÐаÑтаўленне агульнага выкарыÑÑ‚Ð°Ð½Ð½Ñ Ñтальніцы
+Comment[bg]=ÐаÑтройване ÑподелÑнето на работното мÑÑто
+Comment[bn]=ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি কনফিগার করà§à¦¨
+Comment[br]=Kefluniañ rannañ ar vurev
+Comment[bs]=Podesite dijeljenje desktopa
+Comment[ca]=Lupa de l'escriptori
+Comment[cs]=Nastavit sdílení pracovní plochy
+Comment[cy]=Ffurfweddu Rhannu Penbwrdd
+Comment[da]=Indstil desktopdeling
+Comment[de]=Freigabe der Arbeitsfläche einrichten
+Comment[el]=ΡÏθμιση της κοινής χÏήσης της επιφάνειας εÏγασίας
+Comment[eo]=Agordu fordonadon de viaj tabuloj
+Comment[es]=Configure su escritorio compartido
+Comment[et]=Töölaua jagamise seadistamine
+Comment[eu]=Konfiguratu mahaigain partekatzea
+Comment[fa]=پیکربندی اشتراک رومیزی
+Comment[fi]=Aseta työpöydän jakaminen
+Comment[fr]=Configuration du partage du bureau
+Comment[ga]=Cumraigh Roinnt Deisce
+Comment[gl]=Configura-la compartición do escritorio
+Comment[he]=שינוי הגדרות שיתוף שולחנות העבודה
+Comment[hi]=कॉनà¥à¤«à¤¼à¤¿à¤—र डेसà¥à¤•à¤Ÿà¥‰à¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Comment[hr]=Podešavanje dijeljenja radne površine
+Comment[hu]=A munkaasztal-megosztás beállításai
+Comment[is]=Stilla skjáborðsmiðlun
+Comment[it]=Configura condivisione desktop
+Comment[ja]=デスクトップ共有ã®è¨­å®š
+Comment[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ
+Comment[kk]=Ò®Ñтелді ортақтаÑтыруды баптау
+Comment[km]=កំណážáŸ‹â€‹ážšáž…នា​សម្ពáŸáž“្ធ​ការ​ចែក​រំលែក​ផ្ទៃ​ážáž»
+Comment[lt]=Derinti dalinimÄ…si darbastaliu
+Comment[mk]=Конфигурирајте делење на површината
+Comment[mn]=Ðжлын байрны хамтран ÑзÑмших тохируулга
+Comment[ms]= Selaraskan Perkongsian Desktop
+Comment[nb]=Tilpass delte skrivebord
+Comment[nds]=Schriefdisch-Freegaav instellen
+Comment[ne]=डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€ कनà¥à¤«à¤¿à¤—र गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Bureaublad delen instellen
+Comment[nn]=Set opp skrivebordsdeling
+Comment[nso]=Beakanya Kabagano ya Desktop
+Comment[pa]=ਡੈਸਕਟਾਪ ਸਾਂਠਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja współdzielenia pulpitu
+Comment[pt]=Configura a Partilha do Ecrã
+Comment[pt_BR]=Configurar Compartilhamento do Desktop
+Comment[ru]=Параметры общего рабочего Ñтола
+Comment[se]=Heivet Äállinbeavdejuohkkima
+Comment[sk]=Nastavenie zdieľania pracovnej plochy
+Comment[sl]=Nastavi deljenje namizja
+Comment[sr]=Подешавање дељења радне површине
+Comment[sr@Latn]=Podešavanje deljenja radne površine
+Comment[sv]=Anpassa utdelning av skrivbord
+Comment[ta]=பணிமேடை பகிரà¯à®µà¯ˆ உளà¯à®³à®®à¯ˆ
+Comment[tg]=Батанзимдарории ИÑтифодабарии муштараки Мизи корӣ
+Comment[tr]=Masaüstü Paylaşımını Yapılandır
+Comment[uk]=Ðалаштувати Ñпільні Ñтільниці
+Comment[ven]=Dzudzanyani u kovhekana ha desikithopo
+Comment[xh]=Qwalasela Ukusebenzisa ngokuhlangeneyo kwe Desktop
+Comment[zh_CN]=é…置桌é¢å…±äº«
+Comment[zh_HK]=設定桌é¢åˆ†äº«
+Comment[zh_TW]=設定桌é¢åˆ†äº«
+Comment[zu]=Hlela kahle Ukwahlulelana kwe-Desktop
+Keywords=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited
+Keywords[ar]=مشاركة سطح المكتب,krfb,vnc,مشاركة,krdc,اتصال سطح مكتب بعيد,دعوة,منÙØ°,slp,غير مدعو
+Keywords[be]=агульнае выкарыÑтанне Ñтальніцы,злучÑнне,аддалены кампутар,запрашÑнне,порт,desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited
+Keywords[bg]=ÑподелÑне, работно, мÑÑто, деÑктоп, покана, връзка, desktop sharing, krfb, vnc, sharing, krdc, remote desktop connection, invitation, port, slp, uninvited
+Keywords[bs]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,dijeljenje desktopa,dijeljenje,udaljeni dekstop,poziv,nepozvan
+Keywords[ca]=compartició de l'escriptori,krfb,vnc,compartir,krdc,connexió a l'escriptori remot,invitació,port,slp,no invitat
+Keywords[cs]=sdílení plochy,krfb,vnc,sdílení,rdp,krdc,připojení vzdálené plochy,pozvánka,port,slp
+Keywords[cy]=rhannu penbwrdd,krfb,vnc,rhannu,rdp,krdc,cysylltiad penbwrdd pell,gwahoddiad,porth,slp,heb wahoddiad
+Keywords[da]=desktopdeling,krfb,vnc,deling,krdc,ekstern desktopforbindelse,invitation,port,slp,ikke inviteret
+Keywords[de]=Arbeitsfläche freigeben,krfb,VNC,freigeben,krdc,Entfernte Arbeitsfläche,Einladung,Port,slp,uneingeladen
+Keywords[el]=κοινή χÏήση επιφάνειας εÏγασίας,krfb,vnc,κοινή χÏήση,krdc,σÏνδεση σε απομακÏυσμένη επιφάνεια εÏγασίας,Ï€Ïόσκληση,θÏÏα,slp,απÏόσκλητο
+Keywords[eo]=tabulo,tabulokomunigado,foraj tabuloj
+Keywords[es]=escritorio compartido,krfb,vnc,compartir,rdp,krdc, conexión escritorio remoto,invitación,puerto,slp,no invitado
+Keywords[et]=töölaua jagamine,krfb,vnc,jagamine,rdp,krdc,kaugtöölaua ühendus,rdp,kutse,port,slp,kutsumata
+Keywords[eu]=mahaigain partekatzea,krfb,vnc,partekatzea,krdc,urruneko mahaigain konexioa,gonbidapena,ataka,slp,gonbidapen gabea
+Keywords[fa]=اشتراک رومیزی، krfb، vnc، اتصال رومیزی راه دور، دعوت، درگاه، slp، دعوت‌نشده
+Keywords[fi]=työpöydän jakaminen,krfb,vnc,jakaminen,krdc,etätyöpöytäyhteys, kutsu,portti,slp,kutsumaton, työpöytä
+Keywords[fr]=partage de bureau,krfb,vnc,partage,rdp,krdc,connexion à un bureau distant,invitation,port,slp,non invité
+Keywords[gl]=compartición de escritorio, vnc, compartir, krdc, conexión escritorio remoto, invitación, porto, slt
+Keywords[he]=שיתוף שולחן עבודה,שיתוף,חיבור לשולחן עבודה מרוחק,desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection, invitation, port, slp, uninvited
+Keywords[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€, केआरà¤à¤«à¤¬à¥€,वीà¤à¤¨à¤¸à¥€,साà¤à¥‡à¤¦à¤¾à¤°à¥€,केआरडीसी,रिमोट डेसà¥à¤•à¤Ÿà¥‰à¤ª कनेकà¥à¤¶à¤¨,निमंतà¥à¤°à¤£,पोरà¥à¤Ÿ,à¤à¤¸à¤à¤²à¤ªà¥€,बिन बà¥à¤²à¤¾à¤
+Keywords[hu]=munkaasztal-megosztás,krfb,vnc,megosztás,krdc,csatlakozás távoli munkaasztalhoz,meghívás,port,slp,meghívás nélkül
+Keywords[is]=skjáborðsmiðlun,miðlun,krfb,vnc,rdp,krdc,fjarvinnsla,remote desktop connection,rdp
+Keywords[it]=condivisione desktop,krfb,vnc,condivisione,krdc,connessione desktop remoto,invito,porta,slp,non invitato
+Keywords[ja]=デスクトップ共有,krfb,vnc,共有,rdp,krdc,リモートデスクトップ接続,招待,ãƒãƒ¼ãƒˆ,slp,uninvited
+Keywords[km]=ការ​ចែក​រំ​លែក​ផ្ទៃ​ážáž»,krfb,vnc,ការ​ចែក​រំលែក,krdc,ការ​ážâ€‹áž—្ជាប់​ផ្ទៃ​ážáž»â€‹áž–ី​ចម្ងាយ,អញ្ជើញ,ច្រក,slp,មិន​បាន​អញ្ជើញ
+Keywords[lt]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,dalinimasis darbastaliu,nutolusio darbastalio prijungimas,kvietimas,ryšys,nekviestas,jungtis,prievadas,dalintis,pasidalinti,jungimasis,prisijungti,kviesti
+Keywords[mk]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,делење на површината,krfb,vnc,делење,krdc,поврзување Ñо оддалечена површина,покана,порта,slp,непоканет
+Keywords[ms]= perkongsian ruang kerja, sambungan, liang, desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited
+Keywords[nb]=skrivebordsdeling,krfb,vnc,deling,krdc,tilkobling til fjernt skrivebord,invitasjon,port,slp,ikke invitert
+Keywords[nds]=schriefdisch,freegeven,freegaav,krfb,vnc,delen,krdc,feern Schriefdisch,verbinnen,inladen,port,slp,nich inlaadt
+Keywords[ne]=डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€,krfb,vnc,साà¤à¥‡à¤¦à¤¾à¤°à¥€,krdc, टाढाको डेसà¥à¤•à¤Ÿà¤ª जडान,निमनà¥à¤¤à¥à¤°à¤£à¤¾,पोरà¥à¤Ÿ,slp,निमनà¥à¤¤à¥à¤°à¤£à¤¾ नगरिà¤à¤•à¥‹
+Keywords[nl]=desktop sharing,krfb,vnc,sharing,rdp,krdc,remote desktop connection,uitnodiging,slp,rdp,verbinding, bureaublad delen,bureaublad op afstand
+Keywords[nn]=skrivebordsdeling,krfb,vnc,deling,krdc,nettverksskrivebord,invitasjon,port,slp,ikkje invitert
+Keywords[pl]=współdzielenie pulpitu,krfb,vnc,współdzielenie,rdp,krdc,zdalne połączenie, zdalne biurko, zdalny pulpit, zaproszenie,port,slp
+Keywords[pt]=partilha do ecrã,krfb,vnc,partilha,krdc,ligação a um ecrã remoto,convite,porto,slp,sem convite
+Keywords[pt_BR]=compartilhamento de desktop,krfb,vnc,compartilhamento,krdc,conexão a desktop remoto,convite,porta,slp,não convidado
+Keywords[ru]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,ÑовмеÑтные реÑурÑÑ‹,рабочий Ñтол,ÑƒÐ´Ð°Ð»Ñ‘Ð½Ð½Ð°Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ð°
+Keywords[sk]=zdieľanie plochy,krfb,vnc,zdieľanie,rdp,krdc,pripojenie vzdialenej pracovnej plochy,rdp
+Keywords[sl]=namizje,souporaba,krfb,vnc,krdc,povezava oddaljenega namizja, povabilo,port,slp,nepovabljen
+Keywords[sr]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,радна површина,дељење,позив,порт,непозван,удаљено
+Keywords[sr@Latn]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,radna površina,deljenje,poziv,port,nepozvan,udaljeno
+Keywords[sv]=dela skrivbord,krfb,vnc,dela,krdc,fjärrskrivbordsanslutning,inbjudan,port,slp
+Keywords[ta]=பணிமேடை பகிரà¯à®µà¯,krfb,vnc,பகிரà¯à®µà¯,krdc,தொலை பணிமேடை இணைபà¯à®ªà¯,அழைபà¯à®ªà®¿à®¤à®´à¯, தà¯à®±à¯ˆ, slp, uninvited
+Keywords[tr]=masaüstü paylaşımı,krfb,vnc,paylaşım,krdc,uzak masaüstü bağlantısı,davet,port,slp
+Keywords[uk]=Ñпільні Ñтільниці,krfb,vnc,Ñпільний,rdp,krdc,з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð´Ð°Ð»ÐµÐ½Ð¾Ñ— Ñтільниці,запрошеннÑ,rdp,slp,без запрошеннÑ
+Keywords[zh_CN]=desktop sharing,krfb,vnc,sharing,krdc,remote desktop connection,invitation,port,slp,uninvited,æ¡Œé¢å…±äº«,共享,远程桌é¢è¿žæŽ¥,邀请,端å£,未邀请
+Keywords[zh_TW]=desktop sharing,krfb,vnc,sharing,rdp,krdc,remote desktop connection,rdp,æ¡Œé¢åˆ†äº«,分享,é ç«¯æ¡Œé¢é€£ç·š,invitation,port,slp,uninvited
+
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
diff --git a/krfb/kinetd/Makefile.am b/krfb/kinetd/Makefile.am
new file mode 100644
index 00000000..52008d8c
--- /dev/null
+++ b/krfb/kinetd/Makefile.am
@@ -0,0 +1,28 @@
+METASOURCES = AUTO
+
+# Code
+kde_module_LTLIBRARIES = kded_kinetd.la
+
+kded_kinetd_la_SOURCES = kinetd.cpp kinetd.skel
+kded_kinetd_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_kinetd_la_LIBADD = ../srvloc/libsrvloc.la $(LIB_KIO) $(LIB_KDNSSD)
+
+# Services
+kde_servicetypes_DATA = kinetdmodule.desktop
+kdeddir = $(kde_servicesdir)/kded
+kded_DATA = kinetd.desktop
+
+EXTRA_DIST = $(kded_kinetd_la_SOURCES)\
+ $(kded_DATA) \
+ $(kde_servicetypes_DATA) \
+ eventsrc README.debugging
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(kde_datadir)/kinetd
+ $(INSTALL_DATA) $(srcdir)/eventsrc $(DESTDIR)$(kde_datadir)/kinetd/eventsrc
+
+# set the include path for X, qt and KDE
+INCLUDES= -I$(top_srcdir)/krfb/srvloc $(all_includes)
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kinetd.pot
diff --git a/krfb/kinetd/README.debugging b/krfb/kinetd/README.debugging
new file mode 100644
index 00000000..359496b8
--- /dev/null
+++ b/krfb/kinetd/README.debugging
@@ -0,0 +1,13 @@
+Recommended practice to debug a kinetd program
+==============================================
+
+Instead of executing your program directly, start a wrapper script that
+looks like this:
+
+#!/bin/sh
+exec xterm -e gdb --args /opt/kde/bin/krfb $1 $2
+
+Note that you cannot use konsole because it closes the file descriptor. You need
+a very recent gdb version for the "--args" option (>= 5.2, only available as
+CVS snapshot at this time).
+
diff --git a/krfb/kinetd/eventsrc b/krfb/kinetd/eventsrc
new file mode 100644
index 00000000..658d2412
--- /dev/null
+++ b/krfb/kinetd/eventsrc
@@ -0,0 +1,259 @@
+[!Global!]
+IconName=kinetd
+Comment=KInetD
+Comment[bn]=কে-আইনেট-ডি
+Comment[hi]=के-इनिट-डी
+Comment[sv]=Kinetd
+
+[IncomingConnection]
+Name=IncomingConnection
+Name[ar]=اتصال وارد
+Name[bg]=Получена е входÑща връзка
+Name[bn]=অনà§à¦¤à¦°à§à¦®à§à¦–ী সংযোগ
+Name[br]=Kevreadenn resev
+Name[ca]=Connexió entrant
+Name[cs]=Příchozí spojení
+Name[cy]=CysylltiadCyrraedd
+Name[da]=IndkommendeForbindelse
+Name[de]=Eingehende Verbindung
+Name[el]=ΕισεÏχόμενη σÏνδεση
+Name[eo]=EnvenantaKonekto
+Name[es]=Conexión entrante
+Name[et]=Sissetulev ühendus
+Name[eu]=Sarrerako konexioa
+Name[fa]=اتصال وارد‌شده
+Name[fi]=Saapuva yhteys
+Name[fr]=Connexion entrante
+Name[ga]=CeangalIsteach
+Name[gl]=Conexión Entrante
+Name[he]=חיבור נכנס
+Name[hi]=आवक-कनेकà¥à¤¶à¤¨
+Name[hr]=DolaznaVeza
+Name[hu]=Bejövő kapcsolat
+Name[it]=Connessioni in entrata
+Name[ja]=外部ã‹ã‚‰ã®æŽ¥ç¶š
+Name[kk]=ÐšÑ–Ñ€Ñ–Ñ Ò›Ð¾Ñылым
+Name[km]=ការ​ážáž—្ជាប់​ចូល
+Name[lt]=Gautas kvietimas ryšiui
+Name[mk]=Дојдовно поврзување
+Name[mn]=Орж ирÑÑн Холболт
+Name[ms]=Sambungan Masuk
+Name[mt]=KonnessjonijiDieħla
+Name[nb]=Innkommende forbindelse
+Name[nds]=RinkamenVerbinnen
+Name[ne]=आगमन जडान
+Name[nl]=Inkomende verbinding
+Name[nn]=Innkomande samband
+Name[nso]=Kopantsho yeo e Tsenago
+Name[pl]=Połączenia przychodzące
+Name[pt]=Ligação Recebida
+Name[pt_BR]=Conexões de Entrada
+Name[ro]=Conexiune de intrare
+Name[ru]=ВходÑщее Ñоединение
+Name[se]=Boahtti oktavuohta
+Name[sk]=Prichádzajúce spojenia
+Name[sl]=PrihajajoÄa povezava
+Name[sr]=Долазећа веза
+Name[sr@Latn]=Dolazeća veza
+Name[sv]=Inkommande anslutning
+Name[ta]=உளà¯à®µà®°à¯à®®à¯ இணைபà¯à®ªà¯à®•à®³à¯
+Name[tg]=ПайваÑтшавии Воридшаванда
+Name[th]=มีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹€à¸‚้ามา
+Name[tr]=Gelen Bağlantı
+Name[uk]=ВхіднеЗ'єднаннÑ
+Name[ven]=Vhukwamanihanga ngomu
+Name[xh]=Uxhumaniso Olungenayo
+Name[zh_CN]=进入的连接
+Name[zh_HK]=進入連線
+Name[zh_TW]=進來的連接
+Name[zu]=UkuxhumanaOkuzayo
+Comment=Received incoming connection
+Comment[af]=Ontvang inkomende verbinding
+Comment[ar]=تم استلام اتصال وارد
+Comment[be]=Ðтрыманы запыт злучÑннÑ
+Comment[bg]=Получена е входÑща връзка
+Comment[bn]=অনà§à¦¤à¦°à§à¦®à§à¦–ী সংযোগ গà§à¦°à¦¹à¦£ করল
+Comment[bs]=Primio dolaznu konekciju
+Comment[ca]=Rebuda connexió entrant
+Comment[cs]=Obdrženo příchozí spojení
+Comment[cy]=Derbynwyd cysylltiad a gyrhaeddodd
+Comment[da]=Modtog indkommende forbindelse
+Comment[de]=Verbindungsanfrage eingegangen
+Comment[el]=Λήψη εισεÏχόμενης σÏνδεσης
+Comment[eo]=Ricevantaj envenantaj konektoj
+Comment[es]=Recibida conexión entrante
+Comment[et]=Saadi sissetulev ühendus
+Comment[eu]=Konexio bat jaso da
+Comment[fa]=اتصال واردشده دریاÙت شد
+Comment[fi]=Vastaanotettiin saapuva yhteys
+Comment[fr]=Reçu une connexion entrante
+Comment[gl]=Recibíuse unha conexión entrante
+Comment[he]=נתקבל חיבור נכנס
+Comment[hi]=आवक कनेकà¥à¤¶à¤¨ पà¥à¤°à¤¾à¤ªà¥à¤¤
+Comment[hr]=Primio dolaznu vezu
+Comment[hu]=Csatlakozási kérés érkezett
+Comment[is]=Tók á móti uppkalli
+Comment[it]=Connessione in entrata stabilita
+Comment[ja]=ç€ä¿¡ã—ãŸå¤–部ã‹ã‚‰ã®æŽ¥ç¶š
+Comment[ka]=შემáƒáƒ›áƒáƒ•áƒáƒšáƒ˜ კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ მáƒáƒ•áƒ˜áƒ“áƒ
+Comment[kk]=ÐšÑ–Ñ€Ñ–Ñ Ò›Ð¾Ñылым қабылданды
+Comment[km]=បាន​ទទួល​ការ​ážáž—្ជាប់​ចូល
+Comment[lt]=Gautas kvietimas ryšiui
+Comment[mk]=Примено е дојдовно поврзување
+Comment[mn]=Орж ирÑÑн Холболтыг хүлÑÑн авав
+Comment[ms]=Menerima sambungan masuk
+Comment[mt]=Irċevejt konnessjoni dieħla
+Comment[nb]=Motta innkommende forbindelse
+Comment[nds]=Tokoppelanfraag kregen
+Comment[ne]=आगमन जडान पà¥à¤°à¤¾à¤ªà¥à¤¤ गरà¥à¤¯à¥‹
+Comment[nl]=Ontving een inkomende verbinding
+Comment[nn]=Motta innkomande samband
+Comment[nso]=Kopantsho yeo e amogetswego ya tseo di tsenago
+Comment[pl]=Otrzymano połącznie przychodzące
+Comment[pt]=Foi recebida uma ligação
+Comment[pt_BR]=Recebendo conexão de entrada
+Comment[ro]=Conexiune de intrare recepţionată
+Comment[ru]=ВходÑщее Ñоединение завершилоÑÑŒ уÑпешно
+Comment[se]=OaÄÄui boahtti oktavuoÄ‘a
+Comment[sk]=Prijaté príchodzie spojenia
+Comment[sl]=Prejeta je prihajajoÄa povezava
+Comment[sr]=Примљена је долазећа веза
+Comment[sr@Latn]=Primljena je dolazeća veza
+Comment[sv]=Tar emot inkommande anslutning
+Comment[tg]=ПайваÑтшавии воридшаванда қабул гардид
+Comment[th]=ได้รับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹€à¸‚้ามา
+Comment[tr]=Gelen bağlantı alınıyor
+Comment[uk]=Отримано вхідне з'єднаннÑ
+Comment[ven]=Vhukwamani hau dzhena ho tanganedzhwaho
+Comment[xh]=Uxhulumano olungenayo olufunyenweyo
+Comment[zh_CN]=收到进入的连接
+Comment[zh_HK]=已接收的進入連線
+Comment[zh_TW]=已接收的進來的連線
+Comment[zu]=Ukuxhumanisa okungenayo okutholakele
+default_presentation=4
+
+[ProcessFailed]
+Name=ProcessFailed
+Name[ar]=Ùشلت العملية
+Name[bg]=ПроцеÑÑŠÑ‚ за обработка връзки не може да бъде Ñтартиран
+Name[bn]=পà§à¦°à¦¸à§‡à¦¸ বà§à¦¯à¦°à§à¦¥
+Name[ca]=Procés fallit
+Name[cs]=Proces selhal
+Name[cy]=MethoddProses
+Name[da]=ProcesMislykkedes
+Name[de]=Prozess fehlgeschlagen
+Name[el]=Η επεξεÏγασία απέτυχε
+Name[eo]=ProcezoFiaskis
+Name[es]=Proceso fallido
+Name[et]=Protsess ebaõnnestus
+Name[eu]=Prozesuak huts egin du
+Name[fa]=خرابی پردازه
+Name[fi]=Prosessi epäonnistui
+Name[fr]=Échec du processus
+Name[ga]=PróiseasTeipthe
+Name[gl]=Proceso fallido
+Name[he]=תהליך נכשל
+Name[hi]=पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾-असफल
+Name[hr]=ProcesNeuspješan
+Name[hu]=Hibás folyamat
+Name[it]=Processo fallito
+Name[ja]=プロセス失敗
+Name[kk]=ПроцеÑÑ Ð¶Ð°Ò£Ñ‹Ð»Ð´Ñ‹
+Name[km]=ដំណើរ​ការ​បាន​បរាជáŸáž™
+Name[lt]=Procesas nepavyko
+Name[mk]=ПроцеÑот не уÑпеа
+Name[mn]=Ðжиллагаа ÑүйрÑв
+Name[ms]=Proses Gagal
+Name[mt]=ProċessFalla
+Name[nb]=Prosess mislyktes
+Name[nds]=PerzessFehlslaan
+Name[ne]=पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ असफल भयो
+Name[nl]=Proces mislukt
+Name[nn]=Prosess mislukkast
+Name[nso]=Tiragalo e Paletswe
+Name[pa]=ਕਾਰਜ ਫੇਲà©à¨¹ ਹੋਈ
+Name[pl]=BÅ‚Ä…d procesu
+Name[pt]=Processo Mal-Sucedido
+Name[pt_BR]=Falha de Processo
+Name[ro]=Proces eÅŸuat
+Name[ru]=Ошибка процеÑÑа
+Name[se]=Proseassa filtii
+Name[sk]=Proces neúspešny
+Name[sl]=Proces ni uspel
+Name[sr]=ÐŸÑ€Ð¾Ñ†ÐµÑ Ð½Ð¸Ñ˜Ðµ уÑпео
+Name[sr@Latn]=Proces nije uspeo
+Name[sv]=Process misslyckades
+Name[ta]=செயல௠தோலà¯à®µà®¿à®¯à¯à®±à¯à®±à®¤à¯
+Name[tg]=Ҷараён бо Ðокомӣ анҷомид
+Name[th]=โปรเซสล้มเหลว
+Name[tr]=Başarısız İşlemler
+Name[uk]=ПомилкаПроцеÑу
+Name[ven]=Tshitenwa tsho bala
+Name[xh]=Inkqubo Yahlulekile
+Name[zh_CN]=处ç†å¤±è´¥
+Name[zh_HK]=程åºå¤±æ•—
+Name[zh_TW]=程åºå¤±æ•—
+Name[zu]=UkwenzekaKuhlulekile
+Comment=Could not call process to handle connection
+Comment[af]=Kon nie roep proses na handvatsel verbinding
+Comment[ar]=لم أستطع استحضار العملية لحمل المكالمة
+Comment[be]=Ðемагчыма выклікаць працÑÑ Ð´Ð»Ñ Ð°Ð¿Ñ€Ð°Ñ†Ð¾ÑžÐ²Ð°Ð½Ð½Ñ Ð·Ð»ÑƒÑ‡ÑннÑ
+Comment[bg]=ПроцеÑÑŠÑ‚ за обработка връзки не може да бъде Ñтартиран
+Comment[bn]=সংযোগ পরিচালনা করতে পà§à¦°à¦¸à§‡à¦¸ শà§à¦°à§ করতে পারল না
+Comment[bs]=Ne mogu pokrenuti proces koji upravlja konekcijom
+Comment[ca]=No es pot cridar al procés per a manejar la connexió
+Comment[cs]=Nelze spustit proces k obsluze spojení
+Comment[cy]=Methu galw proses i drin y cysylltiad
+Comment[da]=Kunne ikke kalde proces til at håndtere forbindelse
+Comment[de]=Der Prozess zur Bearbeitung der Verbindungsanfrage wurde nicht gefunden
+Comment[el]=Δεν ήταν δυνατή η κλήση της διεÏγασίας για τον έλεγχο της σÏνδεσης
+Comment[eo]=Ne eblis lanĉi instancon por prizorgi la konekton
+Comment[es]=Imposible lanzar proceso para manejar conexión
+Comment[et]=Ei suuda käivitada protsessi ühenduse käsitsemiseks
+Comment[eu]=Ezin izan da konexioa kudeatzeko prozesua deitu
+Comment[fa]=نتوانست پردازه را برای گرداندن اتصال Ùراخوانی کند
+Comment[fi]=Ei voitu kutsua prosessia yhteyden hoitamiseksi
+Comment[fr]=Impossible d'appeler le processus pour gérer la connexion
+Comment[gl]=Non se puidcho chamar ó proceso para que atendese á conexión
+Comment[he]=×ין ×פשרות ×œ×§×¨×•× ×œ×ª×”×œ×™×š על מנת לטפל בחיבור
+Comment[hi]=कनेकà¥à¤¶à¤¨ हैंडल करने के लिठपà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ काल नहीं कर सका
+Comment[hr]=Nisam mogao pozvati proces za kontrolu veze
+Comment[hu]=A kapcsolat kezelése nem sikerült
+Comment[is]=Gat ekki kallað á forrit til að höndla tengingu
+Comment[it]=Impossibile richiamare il processo per gestire la connessione
+Comment[ja]=接続を扱ã†ãƒ—ロセスを呼ã³å‡ºã›ã¾ã›ã‚“
+Comment[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ დáƒáƒ›áƒ›áƒ£áƒ¨áƒáƒ•áƒ”ბელი პრáƒáƒªáƒ”სის გáƒáƒ›áƒáƒ«áƒáƒ®áƒ”ბრვერ გáƒáƒœáƒ®áƒáƒ áƒªáƒ˜áƒ”ლდáƒ
+Comment[kk]=ҚоÑылымды қолдайтын процеÑÑ Ð¶ÐµÐ³Ñ–Ð»Ð¼ÐµÐ´Ñ–
+Comment[km]=មិន​អាច​ហៅ​ដំណើរ​ការ ដើម្បីដោះស្រាយ​ការ​ážâ€‹áž—្ជាប់​បាន​ឡើយ
+Comment[lt]=Nepavyko iškviesti proceso prisijungimui apdoroti
+Comment[mk]=Ðе може да Ñе повика процеÑот за ракување Ñо поврзувањето
+Comment[mn]=Холболтыг заах ажиллагаагдуудаж чадаагүй
+Comment[ms]=Tidak dapat memanggil proses untuk mengendalikan sambungan
+Comment[mt]=Ma stajtx insejjaħ proċess biex jieħu ħsieb il-konnessjoni
+Comment[nb]=Kunne ikke kalle en prosess for å håndtere forbindelsen
+Comment[nds]=Perzess för't Verarbeiden vun Tokoppelanfragen lett sik nich opropen
+Comment[ne]=जडान हà¥à¤¯à¤¾à¤¨à¥à¤¡à¤² गरà¥à¤¨ पà¥à¤°à¤•à¥à¤°à¤¿à¤¯à¤¾ आहà¥à¤µà¤¾à¤¨ गरà¥à¤¨ सकेन
+Comment[nl]=Het proces om de verbinding af te handelen kon niet worden aangeroepen
+Comment[nn]=Klarte ikkje kalla prosess for sambandshandtering
+Comment[nso]=Ebe ekase bitse tiragalo go swara kopantsho
+Comment[pl]=Nie można było uruchomić procesu obsługi połączenia
+Comment[pt]=Não foi possível invocar o processo para tratar da ligação
+Comment[pt_BR]=Não foi possível chamar o processo de controle da conexão
+Comment[ru]=Ðе удаётÑÑ Ð·Ð°Ð¿ÑƒÑтить процеÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ ÑоединениÑ
+Comment[sk]=Nemohol som zavolať proces pre spracovanie spojenia
+Comment[sl]=Ni možno poklicati procesa za upravljanje s povezavo
+Comment[sr]=ÐиÑам могао да позовем Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð´Ð° опÑлужи везу
+Comment[sr@Latn]=Nisam mogao da pozovem proces da opsluži vezu
+Comment[sv]=Kunde inte anropa process för att hantera anslutning
+Comment[ta]=இணைபà¯à®ªà¯ˆ கையாள செயறà¯à®ªà®¾à®Ÿà¯à®Ÿà¯ˆ அழைகà¯à®• à®®à¯à®Ÿà®¿à®¯à®µà®¿à®²à¯à®²à¯ˆ
+Comment[tg]=Барои даÑкории пайваÑтшавӣ ҷараён бозхонда нашуд
+Comment[th]=ไม่สามารถเรียà¸à¹‚ปรเซสเพื่อรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹„ด้
+Comment[tr]=Elle bağlantıda başarısız çağrı işlemleri
+Comment[uk]=Ðеможливо викликати Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð´Ð»Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ з'єднаннÑ
+Comment[ven]=Ingasi vhidze tshitenwa uitela u fara vhukwamani
+Comment[xh]=Ayikwazanga ukubiza inkqubo ezakuphatha uxhulumano
+Comment[zh_CN]=无法调用进程处ç†è¿žæŽ¥
+Comment[zh_HK]=無法呼å«è™•ç†é€£ç·šçš„程åº
+Comment[zh_TW]=無法呼å«è™•ç†é€£ç·šçš„程åº
+Comment[zu]=Ayikwazanga ukubiza umsebenzi ukuzophatha ukuxhumanisa
+default_presentation=4
diff --git a/krfb/kinetd/kinetd.cpp b/krfb/kinetd/kinetd.cpp
new file mode 100644
index 00000000..5094712e
--- /dev/null
+++ b/krfb/kinetd/kinetd.cpp
@@ -0,0 +1,658 @@
+
+/***************************************************************************
+ kinetd.cpp
+ --------------
+ begin : Mon Feb 11 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "kinetd.h"
+#include "kinetd.moc"
+#include "kinetinterface.h"
+#include "kuser.h"
+#include "uuid.h"
+#include <qregexp.h>
+#include <kservicetype.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <knotifyclient.h>
+#include <ksockaddr.h>
+#include <kextsock.h>
+#include <klocale.h>
+#include <kglobal.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+PortListener::PortListener(KService::Ptr s,
+ KConfig *config,
+ KServiceRegistry *srvreg) :
+ m_port(-1),
+ m_serviceRegistered(false),
+ m_socket(0),
+ m_config(config),
+ m_srvreg(srvreg),
+ m_dnssdreg(0)
+{
+ m_dnssdRegistered = false;
+
+ m_uuid = createUUID();
+ loadConfig(s);
+
+ if (m_valid && m_enabled)
+ acquirePort();
+}
+
+bool PortListener::acquirePort() {
+ if (m_socket) {
+ if ((m_port >= m_portBase) &&
+ (m_port < (m_portBase + m_autoPortRange)))
+ return true;
+ else
+ delete m_socket;
+ }
+ m_port = m_portBase;
+ m_socket = new KServerSocket(m_port, false);
+ while (!m_socket->bindAndListen()) {
+ m_port++;
+ if (m_port >= (m_portBase+m_autoPortRange)) {
+ kdDebug() << "Kinetd cannot load service "<<m_serviceName
+ <<": unable to get port" << endl;
+ m_port = -1;
+ delete m_socket;
+ m_socket = 0;
+ return false;
+ }
+ delete m_socket;
+ m_socket = new KServerSocket(m_port, false);
+ }
+ connect(m_socket, SIGNAL(accepted(KSocket*)),
+ SLOT(accepted(KSocket*)));
+
+ bool s = m_registerService;
+ bool sd =m_dnssdRegister;
+ setServiceRegistrationEnabledInternal(false);
+ dnssdRegister(false);
+ setServiceRegistrationEnabledInternal(s);
+ dnssdRegister(sd);
+ return true;
+}
+
+void PortListener::freePort() {
+ m_port = -1;
+ if (m_socket)
+ delete m_socket;
+ m_socket = 0;
+ setServiceRegistrationEnabledInternal(m_registerService);
+ dnssdRegister(false);
+}
+
+void PortListener::loadConfig(KService::Ptr s) {
+ m_valid = true;
+ m_autoPortRange = 0;
+ m_enabled = true;
+ m_argument = QString::null;
+ m_multiInstance = false;
+
+ QVariant vid, vport, vautoport, venabled, vargument, vmultiInstance, vurl,
+ vsattributes, vslifetime, vdname, vdtype, vddata;
+
+ m_execPath = s->exec().utf8();
+ vid = s->property("X-KDE-KINETD-id");
+ vport = s->property("X-KDE-KINETD-port");
+ vautoport = s->property("X-KDE-KINETD-autoPortRange");
+ venabled = s->property("X-KDE-KINETD-enabled");
+ vargument = s->property("X-KDE-KINETD-argument");
+ vmultiInstance = s->property("X-KDE-KINETD-multiInstance");
+ vurl = s->property("X-KDE-KINETD-serviceURL");
+ vsattributes = s->property("X-KDE-KINETD-serviceAttributes");
+ vslifetime = s->property("X-KDE-KINETD-serviceLifetime");
+ vdname = s->property("X-KDE-KINETD-DNSSD-Name");
+ vdtype = s->property("X-KDE-KINETD-DNSSD-Type");
+ vddata = s->property("X-KDE-KINETD-DNSSD-Properties");
+
+ if (!vid.isValid()) {
+ kdDebug() << "Kinetd cannot load service "<<m_serviceName
+ <<": no id set" << endl;
+ m_valid = false;
+ return;
+ }
+
+ if (!vport.isValid()) {
+ kdDebug() << "Kinetd cannot load service "<<m_serviceName
+ <<": invalid port" << endl;
+ m_valid = false;
+ return;
+ }
+
+ m_serviceName = vid.toString();
+ m_serviceLifetime = vslifetime.toInt();
+ if (m_serviceLifetime < 120) // never less than 120 s
+ m_serviceLifetime = 120;
+ m_portBase = vport.toInt();
+ if (vautoport.isValid())
+ m_autoPortRange = vautoport.toInt();
+ if (venabled.isValid())
+ m_enabled = venabled.toBool();
+ if (vargument.isValid())
+ m_argument = vargument.toString();
+ if (vmultiInstance.isValid())
+ m_multiInstance = vmultiInstance.toBool();
+ if (vurl.isValid()) {
+ m_serviceURL = vurl.toString();
+ m_registerService = true;
+ }
+ else {
+ m_serviceURL = QString::null;
+ m_registerService = false;
+ }
+ if (vsattributes.isValid()) {
+ m_serviceAttributes = vsattributes.toString();
+ }
+ else
+ m_serviceAttributes = "";
+ if (vddata.isValid()) {
+ QStringList attrs = vddata.toStringList();
+ for (QStringList::iterator it=attrs.begin();
+ it!=attrs.end();it++) {
+ QString key = (*it).section('=',0,0);
+ QString value = processServiceTemplate((*it).section('=',1))[0];
+ if (!key.isEmpty()) m_dnssdData[key]=value;
+ }
+ }
+ if (vdname.isValid() && vdtype.isValid()) {
+ m_dnssdName = processServiceTemplate(vdname.toString())[0];
+ m_dnssdType = vdtype.toString();
+ m_dnssdRegister = true;
+ kdDebug() << "DNS-SD register is enabled\n";
+ }
+ else
+ m_dnssdRegister = false;
+
+
+ m_slpLifetimeEnd = QDateTime::currentDateTime().addSecs(m_serviceLifetime);
+ m_defaultPortBase = m_portBase;
+ m_defaultAutoPortRange = m_autoPortRange;
+
+ m_config->setGroup("ListenerConfig");
+ m_enabled = m_config->readBoolEntry("enabled_" + m_serviceName,
+ m_enabled);
+ m_portBase = m_config->readNumEntry("port_base_" + m_serviceName,
+ m_portBase);
+ m_autoPortRange = m_config->readNumEntry("auto_port_range_" + m_serviceName,
+ m_autoPortRange);
+ QDateTime nullTime;
+ m_expirationTime = m_config->readDateTimeEntry("enabled_expiration_"+m_serviceName,
+ &nullTime);
+ if ((!m_expirationTime.isNull()) && (m_expirationTime < QDateTime::currentDateTime()))
+ m_enabled = false;
+ m_registerService = m_config->readBoolEntry("enabled_srvreg_"+m_serviceName,
+ m_registerService);
+}
+
+void PortListener::accepted(KSocket *sock) {
+ QString host, port;
+ KSocketAddress *ksa = KExtendedSocket::peerAddress(sock->socket());
+ if ((!ksa) || !ksa->address()) {
+ delete sock;
+ return;
+ }
+ KExtendedSocket::resolve(ksa, host, port);
+ KNotifyClient::event("IncomingConnection",
+ i18n("Connection from %1").arg(host));
+ delete ksa;
+
+ if ((!m_enabled) ||
+ ((!m_multiInstance) && m_process.isRunning())) {
+ delete sock;
+ return;
+ }
+
+ // disable CLOEXEC flag, fixes #77412
+ fcntl(sock->socket(), F_SETFD, fcntl(sock->socket(), F_GETFD) & ~FD_CLOEXEC);
+
+ m_process.clearArguments();
+ m_process << m_execPath << m_argument << QString::number(sock->socket());
+ if (!m_process.start(KProcess::DontCare)) {
+ KNotifyClient::event("ProcessFailed",
+ i18n("Call \"%1 %2 %3\" failed").arg(m_execPath)
+ .arg(m_argument)
+ .arg(sock->socket()));
+ }
+
+ delete sock;
+}
+
+bool PortListener::isValid() {
+ return m_valid;
+}
+
+bool PortListener::isEnabled() {
+ return m_enabled && m_valid;
+}
+
+int PortListener::port() {
+ return m_port;
+}
+
+QStringList PortListener::processServiceTemplate(const QString &a) {
+ QStringList l;
+ QValueVector<KInetInterface> v = KInetInterface::getAllInterfaces(false);
+ QValueVector<KInetInterface>::Iterator it = v.begin();
+ while (it != v.end()) {
+ KInetSocketAddress *address = (*(it++)).address();
+ if (!address)
+ continue;
+ QString hostName = address->nodeName();
+ KUser u;
+ QString x = a; // replace does not work in const QString. Why??
+ l.append(x.replace(QRegExp("%h"), KServiceRegistry::encodeAttributeValue(hostName))
+ .replace(QRegExp("%p"), QString::number(m_port))
+ .replace(QRegExp("%u"), KServiceRegistry::encodeAttributeValue(u.loginName()))
+ .replace(QRegExp("%i"), KServiceRegistry::encodeAttributeValue(m_uuid))
+ .replace(QRegExp("%f"), KServiceRegistry::encodeAttributeValue(u.fullName())));
+ }
+ return l;
+}
+
+bool PortListener::setPort(int port, int autoPortRange) {
+ if ((port == m_portBase) && (autoPortRange == m_autoPortRange))
+ return (m_port != -1);
+
+ m_config->setGroup("ListenerConfig");
+ if (port > 0) {
+ m_portBase = port;
+ m_autoPortRange = autoPortRange;
+
+ m_config->writeEntry("port_base_" + m_serviceName, m_portBase);
+ m_config->writeEntry("auto_port_range_"+m_serviceName, m_autoPortRange);
+ }
+ else {
+ m_portBase = m_defaultPortBase;
+ m_autoPortRange = m_defaultAutoPortRange;
+
+ m_config->deleteEntry("port_base_" + m_serviceName);
+ m_config->deleteEntry("auto_port_range_"+m_serviceName);
+ }
+
+ m_config->sync();
+ if (m_enabled)
+ return acquirePort();
+ else
+ return false;
+}
+
+void PortListener::setEnabled(bool e) {
+ setEnabledInternal(e, QDateTime());
+}
+
+void PortListener::setEnabledInternal(bool e, const QDateTime &ex) {
+ m_config->setGroup("ListenerConfig");
+ m_config->writeEntry("enabled_" + m_serviceName, e);
+ m_config->writeEntry("enabled_expiration_"+m_serviceName, ex);
+ m_config->sync();
+
+ m_expirationTime = ex;
+
+ if (e) {
+ if (m_port < 0)
+ acquirePort();
+ m_enabled = m_port >= 0;
+
+ }
+ else {
+ freePort();
+ m_enabled = false;
+ }
+}
+
+void PortListener::setEnabled(const QDateTime &ex) {
+ setEnabledInternal(true, ex);
+}
+
+bool PortListener::isServiceRegistrationEnabled() {
+ return m_registerService;
+}
+
+void PortListener::setServiceRegistrationEnabled(bool e) {
+ setServiceRegistrationEnabledInternal(e);
+ dnssdRegister(e && m_enabled);
+ m_config->setGroup("ListenerConfig");
+ m_config->writeEntry("enable_srvreg_" + m_serviceName, e);
+ m_config->sync();
+}
+
+void PortListener::setServiceRegistrationEnabledInternal(bool e) {
+ m_registerService = e;
+
+ if ((!m_srvreg) || m_serviceURL.isNull())
+ return;
+ if (m_serviceRegistered == (m_enabled && e))
+ return;
+
+ if (m_enabled && e) {
+ m_registeredServiceURLs = processServiceTemplate(m_serviceURL);
+ QStringList attributes = processServiceTemplate(m_serviceAttributes);
+ QStringList::Iterator it = m_registeredServiceURLs.begin();
+ QStringList::Iterator it2 = attributes.begin();
+ while ((it != m_registeredServiceURLs.end()) &&
+ (it2 != attributes.end())) {
+ if (!m_srvreg->registerService(
+ *(it++),
+ *(it2++),
+ m_serviceLifetime))
+ kdDebug(7021) << "Failure registering SLP service (no slpd running?)"<< endl;
+ }
+ m_serviceRegistered = true;
+ // make lifetime 30s shorter, because the timeout is not precise
+ m_slpLifetimeEnd = QDateTime::currentDateTime().addSecs(m_serviceLifetime-30);
+ } else {
+ QStringList::Iterator it = m_registeredServiceURLs.begin();
+ while (it != m_registeredServiceURLs.end())
+ m_srvreg->unregisterService(*(it++));
+ m_serviceRegistered = false;
+ }
+}
+
+void PortListener::dnssdRegister(bool e) {
+ if (m_dnssdName.isNull() || m_dnssdType.isNull())
+ return;
+ if (m_dnssdRegistered == e)
+ return;
+ if (e) {
+ m_dnssdRegistered=true;
+ m_dnssdreg = new DNSSD::PublicService(m_dnssdName,m_dnssdType,m_port);
+ m_dnssdreg->setTextData(m_dnssdData);
+ m_dnssdreg->publishAsync();
+ } else {
+ m_dnssdRegistered=false;
+ delete m_dnssdreg;
+ m_dnssdreg=0;
+ }
+}
+
+void PortListener::refreshRegistration() {
+ if (m_serviceRegistered && (m_slpLifetimeEnd.addSecs(-90) < QDateTime::currentDateTime())) {
+ setServiceRegistrationEnabledInternal(false);
+ setServiceRegistrationEnabledInternal(true);
+ }
+}
+
+QDateTime PortListener::expiration() {
+ return m_expirationTime;
+}
+
+QDateTime PortListener::serviceLifetimeEnd() {
+ if (m_serviceRegistered)
+ return m_slpLifetimeEnd;
+ else
+ return QDateTime();
+}
+
+QString PortListener::name() {
+ return m_serviceName;
+}
+
+PortListener::~PortListener() {
+ setServiceRegistrationEnabledInternal(false);
+ delete m_socket;
+}
+
+
+KInetD::KInetD(QCString &n) :
+ KDEDModule(n)
+{
+ m_config = new KConfig("kinetdrc");
+ m_srvreg = new KServiceRegistry();
+ if (!m_srvreg->available()) {
+ kdDebug(7021) << "SLP not available"<< endl;
+ delete m_srvreg;
+ m_srvreg = 0;
+ }
+ m_portListeners.setAutoDelete(true);
+ connect(&m_expirationTimer, SIGNAL(timeout()), SLOT(setExpirationTimer()));
+ connect(&m_portRetryTimer, SIGNAL(timeout()), SLOT(portRetryTimer()));
+ connect(&m_reregistrationTimer, SIGNAL(timeout()), SLOT(reregistrationTimer()));
+ loadServiceList();
+}
+
+void KInetD::loadServiceList()
+{
+ m_portListeners.clear();
+
+
+ KService::List kinetdModules =
+ KServiceType::offers("KInetDModule");
+ for(KService::List::ConstIterator it = kinetdModules.begin();
+ it != kinetdModules.end();
+ it++) {
+ KService::Ptr s = *it;
+ PortListener *pl = new PortListener(s, m_config, m_srvreg);
+ if (pl->isValid())
+ m_portListeners.append(pl);
+ else
+ delete pl;
+ }
+
+ setExpirationTimer();
+ setPortRetryTimer(true);
+ setReregistrationTimer();
+}
+
+void KInetD::expirationTimer() {
+ setExpirationTimer();
+ setReregistrationTimer();
+}
+
+void KInetD::setExpirationTimer() {
+ QDateTime nextEx = getNextExpirationTime(); // disables expired portlistener!
+ if (!nextEx.isNull())
+ m_expirationTimer.start(QDateTime::currentDateTime().secsTo(nextEx)*1000 + 30000,
+ false);
+ else
+ m_expirationTimer.stop();
+}
+
+void KInetD::portRetryTimer() {
+ setPortRetryTimer(true);
+ setReregistrationTimer();
+}
+
+void KInetD::setReregistrationTimer() {
+ QDateTime d;
+ PortListener *pl = m_portListeners.first();
+ while (pl) {
+ QDateTime d2 = pl->serviceLifetimeEnd();
+ if (!d2.isNull()) {
+ if (d2 < QDateTime::currentDateTime()) {
+ m_reregistrationTimer.start(0, true);
+ return;
+ }
+ else if (d.isNull() || (d2 < d))
+ d = d2;
+ }
+ pl = m_portListeners.next();
+ }
+
+ if (!d.isNull()) {
+ int s = QDateTime::currentDateTime().secsTo(d);
+ if (s < 30)
+ s = 30; // max frequency 30s
+ m_reregistrationTimer.start(s*1000, true);
+ }
+ else
+ m_reregistrationTimer.stop();
+}
+
+void KInetD::reregistrationTimer() {
+ PortListener *pl = m_portListeners.first();
+ while (pl) {
+ pl->refreshRegistration();
+ pl = m_portListeners.next();
+ }
+ setReregistrationTimer();
+}
+
+void KInetD::setPortRetryTimer(bool retry) {
+ int unmappedPorts = 0;
+
+ PortListener *pl = m_portListeners.first();
+ while (pl) {
+ if (pl->isEnabled() && (pl->port() < 0))
+ if (retry) {
+ if (!pl->acquirePort())
+ unmappedPorts++;
+ }
+ else if (pl->port() < 0)
+ unmappedPorts++;
+ pl = m_portListeners.next();
+ }
+
+ if (unmappedPorts > 0)
+ m_portRetryTimer.start(30000, false);
+ else
+ m_portRetryTimer.stop();
+}
+
+PortListener *KInetD::getListenerByName(QString name)
+{
+ PortListener *pl = m_portListeners.first();
+ while (pl) {
+ if (pl->name() == name)
+ return pl;
+ pl = m_portListeners.next();
+ }
+ return pl;
+}
+
+// gets next expiration timer, SIDEEFFECT: disables expired portlisteners while doing this
+QDateTime KInetD::getNextExpirationTime()
+{
+ PortListener *pl = m_portListeners.first();
+ QDateTime d;
+ while (pl) {
+ QDateTime d2 = pl->expiration();
+ if (!d2.isNull()) {
+ if (d2 < QDateTime::currentDateTime())
+ pl->setEnabled(false);
+ else if (d.isNull() || (d2 < d))
+ d = d2;
+ }
+ pl = m_portListeners.next();
+ }
+ return d;
+}
+
+QStringList KInetD::services()
+{
+ QStringList list;
+ PortListener *pl = m_portListeners.first();
+ while (pl) {
+ list.append(pl->name());
+ pl = m_portListeners.next();
+ }
+ return list;
+}
+
+bool KInetD::isEnabled(QString service)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return false;
+
+ return pl->isEnabled();
+}
+
+int KInetD::port(QString service)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return -1;
+
+ return pl->port();
+}
+
+bool KInetD::setPort(QString service, int port, int autoPortRange)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return false;
+
+ bool s = pl->setPort(port, autoPortRange);
+ setPortRetryTimer(false);
+ setReregistrationTimer();
+ return s;
+}
+
+bool KInetD::isInstalled(QString service)
+{
+ PortListener *pl = getListenerByName(service);
+ return (pl != 0);
+}
+
+void KInetD::setEnabled(QString service, bool enable)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return;
+
+ pl->setEnabled(enable);
+ setExpirationTimer();
+ setReregistrationTimer();
+}
+
+void KInetD::setEnabled(QString service, QDateTime expiration)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return;
+
+ pl->setEnabled(expiration);
+ setExpirationTimer();
+ setReregistrationTimer();
+}
+
+void KInetD::setServiceRegistrationEnabled(QString service, bool enable)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return;
+
+ pl->setServiceRegistrationEnabled(enable);
+ setReregistrationTimer();
+}
+
+bool KInetD::isServiceRegistrationEnabled(QString service)
+{
+ PortListener *pl = getListenerByName(service);
+ if (!pl)
+ return false;
+
+ return pl->isServiceRegistrationEnabled();
+}
+
+KInetD::~KInetD() {
+ m_portListeners.clear();
+ delete m_config;
+ if (m_srvreg)
+ delete m_srvreg;
+}
+
+extern "C" {
+ KDE_EXPORT KDEDModule *create_kinetd(QCString &name)
+ {
+ KGlobal::locale()->insertCatalogue("kinetd");
+ return new KInetD(name);
+ }
+}
diff --git a/krfb/kinetd/kinetd.desktop b/krfb/kinetd/kinetd.desktop
new file mode 100644
index 00000000..80aa3567
--- /dev/null
+++ b/krfb/kinetd/kinetd.desktop
@@ -0,0 +1,139 @@
+[Desktop Entry]
+Type=Service
+
+ServiceTypes=KDEDModule
+X-KDE-ModuleType=Library
+X-KDE-Library=kinetd
+X-KDE-FactoryName=kinetd
+X-KDE-Kded-autoload=true
+
+Name=KDE Internet Daemon
+Name[af]=Kde Internet Bediener
+Name[ar]=مراقب انترنت كيدي
+Name[be]=Ð¡ÐµÑ€Ð²Ñ–Ñ Ð†Ð½Ñ‚ÑрнÑÑ‚ KDE
+Name[bg]=Интернет демон
+Name[bn]=কে-ডি-ই ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ ডিমন
+Name[br]=Diaoul kenrouedad KDE
+Name[ca]=Dimoni d'Internet per al KDE
+Name[cs]=KDE Internet démon
+Name[cy]=Daemon Rhyngrwyd KDE
+Name[da]=KDE Internet-dæmon
+Name[de]=Internet-Dienst
+Name[el]=Δαίμονας διαδικτÏου για το KDE
+Name[eo]=Retodemono
+Name[es]=Demonio de Internet de KDE
+Name[et]=KDE internetideemon
+Name[eu]=KDE internet deabrua
+Name[fa]=شبح اینترنتی KDE
+Name[fi]=KDE Internet-palvelin
+Name[fr]=Démon Internet de KDE
+Name[ga]=Deamhan Idirlín KDE
+Name[gl]=O demo de Internet de KDE
+Name[he]=תהליך הרקע ×”×ינטרנטי של KDE
+Name[hi]=केडीई इंटरनेट डेमन
+Name[hu]=KDE internetes szolgáltatás
+Name[is]=KDE Internetþjónn
+Name[it]=Demone internet di KDE
+Name[ja]=KDE インターãƒãƒƒãƒˆãƒ‡ãƒ¼ãƒ¢ãƒ³
+Name[ka]=KDE ინტერნეტის დემáƒáƒœáƒ˜
+Name[kk]=KDE Интернет қызметі
+Name[km]=ដáŸáž˜áž·áž“​អ៊ីនធឺណិážâ€‹ážšáž”ស់ KDE
+Name[lt]=KDE interneto tarnyba
+Name[mk]=Даемон за Интернет на KDE
+Name[mn]=KDE Интернет-демон
+Name[ms]=Daemon Internet KDE
+Name[mt]=Daemon tal-internet KDE
+Name[nb]=KDEs internett-nisse
+Name[nds]=KDE-Internetdämoon
+Name[ne]=केडीई इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ डेइमन
+Name[nn]=KDE-Internett-nisse
+Name[nso]=Daemon ya Internet ya KDE
+Name[pa]=KDE ਇੰਟਰਨੈੱਟ ਡੈਮਨ
+Name[pl]=Internet/sieć
+Name[pt]=Servidor de Internet do KDE
+Name[pt_BR]=Servidor Internet do KDE
+Name[ro]=Demon internet KDE
+Name[ru]=ДоÑтуп к Интернету
+Name[se]=KDE-Interneahtta-duogášprográmma
+Name[sk]=KDE Internet démon
+Name[sl]=Internetni strežnik za KDE
+Name[sr]=KDE-ов интернет демон
+Name[sr@Latn]=KDE-ov internet demon
+Name[sv]=KDE:s Internetdemon
+Name[ta]=கேடிஇ இணைய டேமொனà¯
+Name[tg]=KDE Ðзозили Интернет
+Name[th]=เดมอนอินเตอร์เน็ต KDE
+Name[tr]=KDE İnternet Programı
+Name[uk]=Демон Інтернет KDE
+Name[uz]=KDE Internet demoni
+Name[uz@cyrillic]=KDE Интернет демони
+Name[zh_CN]=KDE Internet 守护进程
+Name[zh_HK]=KDE 互è¯ç¶²ç³»çµ±ç¨‹å¼
+Name[zh_TW]=KDE Internet 伺æœç¨‹å¼
+Name[zu]=KDE Internet ye-Daemon
+Comment=An Internet daemon that starts network services on demand
+Comment[ar]=مراقب انترنت يقوم ببدء خدمات الشبكة عند الطلب
+Comment[be]=Ð¡ÐµÐ²Ñ–Ñ Ð†Ð½Ñ‚ÑрнÑÑ‚, Ñкі Ñтартуе ÑÐµÑ‚ÐºÐ°Ð²Ñ‹Ñ ÑервіÑÑ‹ па запыце
+Comment[bg]=Интернет демон, който Ñлужи за Ñтартиране на мрежови уÑлуги при поиÑкване
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ইনà§à¦Ÿà¦¾à¦°à¦¨à§‡à¦Ÿ ডিমন যে চাহিদা ভিতà§à¦¤à¦¿à¦• নেটওয়ারà§à¦• সারà§à¦­à¦¿à¦¸ আরমà§à¦­ করে
+Comment[bs]=Internet daemon koji pokreće mrežne servise po potrebi
+Comment[ca]=Un dimoni d'Internet que arrenca els serveis de xarxa sota demanda
+Comment[cs]=Internetový démon spouštějící síťové služby na požádání
+Comment[cy]=Daemon Rhyngrwyd sy'n cychwyn gwasanaethau rhwydwaith ar alw
+Comment[da]=En internet-dæmon der starter netværkstjenester efter forespørgsel
+Comment[de]=Startet Netzwerkdienste bei Bedarf
+Comment[el]=Ένας δαίμονας για το διαδίκτυο ο οποίος ξεκινάει τις υπηÏεσίες δικτÏου όταν απαιτείται
+Comment[eo]=retdemono kiu lanĉas retajn servojn laŭ bezono
+Comment[es]=Un demonio de Internet que inicia los servicios de red a demanda
+Comment[et]=Internetideemon, mis käivitab nõudmisel võrguteenused
+Comment[eu]=Sare zerbitzuak eskatzean abiarazten dituen deabrua
+Comment[fa]=یک شبح اینترنتی که خدمات شبکه را بر اساس تقاضا آغاز می‌کند
+Comment[fi]=Internet-palvelin, joka käynnistää verkkopalveluita
+Comment[fr]=Un démon Internet qui démarre le service réseau à la demande
+Comment[ga]=Deamhan Idirlín a thosaíonn seirbhísí gréasáin ar éileamh
+Comment[gl]=Un demo de Internet que comenza servicios de rede según demanda
+Comment[he]=תהליך רקע של ×ינטרנט שמתחיל שירותי רשת לפי דרישה
+Comment[hi]=à¤à¤• इंटरनेट डेमन जो मांग पर नेटवरà¥à¤• सेवा पà¥à¤°à¤¾à¤°à¤‚भ करता है
+Comment[hr]=Internet daemon koji pokreće mrežne usluge kada su zatražene
+Comment[hu]=A hálózati szolgáltatások vezérlését biztosító program
+Comment[is]=Internetþjónn sem ræsir tengingar við Internetið eftir þörfum
+Comment[it]=Un demone internet che avvia i servizi di rete a richiesta
+Comment[ja]=è¦æ±‚時ã«ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚µãƒ¼ãƒ“スを起動ã™ã‚‹ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆãƒ‡ãƒ¼ãƒ¢ãƒ³
+Comment[ka]=ინტერნეტის დემáƒáƒœáƒ˜, რáƒáƒ›áƒ”ლიც ქსელის სერვისებს მáƒáƒ—ხáƒáƒ•áƒœáƒ˜áƒ— უშვებს
+Comment[kk]=Талап етілгенде керек желі қызметтерді жегетін қызмет
+Comment[km]=ដáŸáž˜áž·áž“​អ៊ីនធឺណិážâ€‹ážŠáŸ‚ល​ចាប់ផ្ដើម​សáŸážœáž¶â€‹áž”ណ្ដាញ​នៅ​ពáŸáž›â€‹ážáŸ’រូវការ
+Comment[lt]=Interneto tarnyba, paleidžianti tinklo tarnybas esant poreikiui
+Comment[mk]=Даемон за Интернет кој ги вклучува мрежните ÑервиÑи на барање
+Comment[mn]=СүлжÑÑ-үйлчилгÑÑний ÑÑ€ÑлтÑÑÑ€ аÑÑан СүлжÑÑний демон
+Comment[ms]=Daemon Internet yang memulakan servis jaringan di atas permintaan
+Comment[mt]=Proċess tal-internet li jħaddem servizzi skond il-bżonn
+Comment[nb]=internett-nisse som starter nettverkstjenester ved behov
+Comment[nds]=En Internet-Dämoon, wat Nettwarkdeensten op Anfraag start
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ इनà¥à¤Ÿà¤°à¤¨à¥‡à¤Ÿ डेइमन जसले मागमा सञà¥à¤œà¤¾à¤² सेवा सà¥à¤°à¥ गरà¥à¤¦à¤›
+Comment[nl]=Een internetdaemon die netwerkdiensten op afroep start
+Comment[nn]=Internett-nisse som startar nettverkstenester når dei trengst
+Comment[nso]=Daemon ya Internet yeo e thomisago ditirelo tsa kgokagano ge e nyakilwe
+Comment[pl]=Uruchamianie usług na żądanie
+Comment[pt]=Um servidor da Internet que inicia os serviços de rede a pedido
+Comment[pt_BR]=Um servidor Internet que inicia os serviços de rede por demanda
+Comment[ro]=Un demon de internet care porneşte serviciile de reţea la cerere
+Comment[ru]=Служба уÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¸ запроÑе Ñетевых реÑурÑов
+Comment[se]=Interneahtta-duogášprográmma mii álggaha fierpmádatbálvalusaid go dárbbašuvvo
+Comment[sk]=Internetový démon ktorý spúšťa sieťové služby na požiadanie
+Comment[sl]=Internetni strežnik, ki zažene omrežne storitve na zahtevo
+Comment[sr]=Интернет демон који покреће мрежне ÑервиÑе по захтеву
+Comment[sr@Latn]=Internet demon koji pokreće mrežne servise po zahtevu
+Comment[sv]=Internetdemon som startar nätverkstjänster vid behov
+Comment[ta]=ஒர௠இணைய டேமொனà¯, டேமொன௠பிணைய சேவையை ஆரமà¯à®ªà®¿à®•à¯à®•à®¿à®±à®¤à¯
+Comment[tg]=Ðзозили шабакавие, ки хидматраÑони шабакавиро бо дархоÑÑ‚ Ñар медиҳад
+Comment[th]=เดมอนอินเตอร์เน็ตซึ่งจะเริ่มทำงานบริà¸à¸²à¸£à¹€à¸„รือข่ายเมื่อมีความต้องà¸à¸²à¸£
+Comment[tr]=Başlangıçta ağ servisleri tarafından istenen internet hayalet programı
+Comment[uk]=Демон Інтернет, що запуÑкає Ñлужби мережі при запиті
+Comment[uz]=Talab qilinganda tarmoq xizmatlarini ishga tushuruvchi Internet demoni
+Comment[uz@cyrillic]=Талаб қилинганда тармоқ хизматларини ишга тушурувчи Интернет демони
+Comment[ven]=Internet daemon ine ya thoma tshumelo ya vhukwamani kha muthetho
+Comment[xh]=Internet daemon eqala iinkonzo zomsebenzi wonatha xa zifunwa
+Comment[zh_CN]=按需å¯åŠ¨ç½‘络æœåŠ¡çš„守护进程
+Comment[zh_HK]=自動ä¾éœ€æ±‚起動網絡æœå‹™çš„互è¯ç¶²ç³»çµ±ç¨‹å¼
+Comment[zh_TW]=ä¾è¦æ±‚起動網路æœå‹™çš„ Internet 伺æœç¨‹å¼
+Comment[zu]=I-Internet ye-daemon eqala ama-sevisi we-network adingekayo
diff --git a/krfb/kinetd/kinetd.h b/krfb/kinetd/kinetd.h
new file mode 100644
index 00000000..e6283e83
--- /dev/null
+++ b/krfb/kinetd/kinetd.h
@@ -0,0 +1,200 @@
+
+/***************************************************************************
+ kinetd.h
+ ------------
+ begin : Mon Feb 11 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 _KINETD_H_
+#define _KINETD_H_
+
+#include <kdedmodule.h>
+#include <kservice.h>
+#include <ksock.h>
+#include <kprocess.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qtimer.h>
+#include <dnssd/publicservice.h>
+
+#include "kserviceregistry.h"
+
+class PortListener : public QObject {
+ Q_OBJECT
+private:
+ bool m_valid;
+ QString m_serviceName;
+ QString m_serviceURL, m_serviceAttributes;
+ QStringList m_registeredServiceURLs;
+ QString m_dnssdName, m_dnssdType;
+ QMap<QString,QString> m_dnssdData;
+ int m_serviceLifetime;
+ int m_port;
+ int m_portBase, m_autoPortRange;
+ int m_defaultPortBase, m_defaultAutoPortRange;
+ bool m_multiInstance;
+ QString m_execPath;
+ QString m_argument;
+ bool m_enabled;
+ bool m_serviceRegistered, m_registerService;
+ bool m_dnssdRegister, m_dnssdRegistered;
+ QDateTime m_expirationTime;
+ QDateTime m_slpLifetimeEnd;
+ QString m_uuid;
+
+ KServerSocket *m_socket;
+ KProcess m_process;
+
+ KConfig *m_config;
+ KServiceRegistry *m_srvreg;
+ DNSSD::PublicService *m_dnssdreg;
+
+ void freePort();
+ void loadConfig(KService::Ptr s);
+ void setEnabledInternal(bool e, const QDateTime &ex);
+ void dnssdRegister(bool enabled);
+ void setServiceRegistrationEnabledInternal(bool enabled);
+
+public:
+ PortListener(KService::Ptr s, KConfig *c, KServiceRegistry *srvreg);
+ ~PortListener();
+
+ bool acquirePort();
+ bool isValid();
+ QString name();
+ void setEnabled(bool enabled);
+ void setEnabled(const QDateTime &expiration);
+ void setServiceRegistrationEnabled(bool enabled);
+ bool isServiceRegistrationEnabled();
+ QDateTime expiration();
+ QDateTime serviceLifetimeEnd();
+ bool isEnabled();
+ int port();
+ QStringList processServiceTemplate(const QString &a);
+ bool setPort(int port = -1, int autoProbeRange = 1);
+ void refreshRegistration();
+
+private slots:
+ void accepted(KSocket*);
+};
+
+class KInetD : public KDEDModule {
+ Q_OBJECT
+ K_DCOP
+
+k_dcop:
+ /**
+ * Returns a list of all registered services in KInetd.
+ * To add a service you need to add a .desktop file with
+ * the servicetype "KInetDModule" into the services director
+ * (see kinetdmodule.desktop in servicetypes dir).
+ * @return a list with the names of all services
+ */
+ QStringList services();
+
+ /**
+ * Returns true if the service exists and is available.
+ * @param service name of a service as specified in its .desktop file
+ * @return true if a service with the given name exists and is enabled
+ */
+ bool isEnabled(QString service);
+
+ /**
+ * Enables or disabled the given service. Ignored if the given service
+ * does not exist.
+ * @param service name of a service as specified in its .desktop file
+ * @param enable true to enable, false to disable.
+ */
+ void setEnabled(QString service, bool enable);
+
+ /**
+ * Enables the given service until the given time. Ignored if the given
+ * service does not exist.
+ * @param service name of a service as specified in its .desktop file
+ * @param expiration the time the service will be disabled at
+ */
+ void setEnabled(QString service, QDateTime expiration);
+
+ /**
+ * Returns the port of the service, or -1 if not listening.
+ * @param service name of a service as specified in its .desktop file
+ * @return the port or -1 if no port used or service does not exist
+ */
+ int port(QString service);
+
+ /**
+ * Sets the port of the service, and possibly a range of ports to try.
+ * It will return true if a port could be found. If it didnt find one but is
+ * enabled it will start a timer that probes that port every 30s.
+ * @param service name of a service as specified in its .desktop file
+ * @param port the first port number to try or -1 to restore defaults
+ * @param autoPortRange the number of ports to try
+ * @return true if a port could be found or service is disabled, false
+ * otherwise.
+ */
+ bool setPort(QString service, int port = -1, int autoPortRange = 1);
+
+ /**
+ * Tests whether the given service is installed..
+ * @param service name of a service as specified in its .desktop file
+ * @return true if installed, false otherwise
+ */
+ bool isInstalled(QString service);
+
+ /**
+ * Enables or disables the SLP registration. Ignored if the service does
+ * not have a service URL. If the service is disabled the service will
+ * registered as soon as it is enabled.
+ * @param service name of a service as specified in its .desktop file
+ * @param enable true to enable, false to disable.
+ */
+ void setServiceRegistrationEnabled(QString service, bool enabled);
+
+ /**
+ * Returns true if service registration for the given service is enabled.
+ * Note that this does not mean that the service is currently registered,
+ * because the service may be disabled.
+ * @param service name of a service as specified in its .desktop file
+ * @return true if service registration is enabled
+ */
+ bool isServiceRegistrationEnabled(QString service);
+
+ private:
+ QDateTime getNextExpirationTime();
+ void setPortRetryTimer(bool retry);
+ void setReregistrationTimer();
+
+ KConfig *m_config;
+ KServiceRegistry *m_srvreg;
+ QPtrList<PortListener> m_portListeners;
+ QTimer m_expirationTimer;
+ QTimer m_portRetryTimer;
+ QTimer m_reregistrationTimer;
+
+ private slots:
+ void setExpirationTimer();
+ void expirationTimer();
+ void portRetryTimer();
+ void reregistrationTimer();
+
+ public:
+ KInetD(QCString &n);
+ virtual ~KInetD();
+ void loadServiceList();
+ PortListener *getListenerByName(QString name);
+};
+
+
+#endif
diff --git a/krfb/kinetd/kinetdmodule.desktop b/krfb/kinetd/kinetdmodule.desktop
new file mode 100644
index 00000000..865fecf3
--- /dev/null
+++ b/krfb/kinetd/kinetdmodule.desktop
@@ -0,0 +1,148 @@
+# describes the servicetype that you need to implement in order to use
+# kinetd.
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KInetDModule
+Name=KInetD Module Type
+Name[ar]=KInetD نوع وحدة
+Name[be]=Тып Ð¼Ð¾Ð´ÑƒÐ»Ñ KInetD
+Name[bg]=Модул на KInetD
+Name[bn]=কে-আইনেট-ডি মডিউল ধরন
+Name[br]=Seurt ar mollad KInetD
+Name[bs]=KInetD tip modula
+Name[ca]=Tipus de mòdul del KInetD
+Name[cs]=Typ modulu KInetD
+Name[cy]=Math Modiwl KInetD
+Name[da]=KInetD-modultype
+Name[de]=KInetD Modultyp
+Name[el]=ΤÏπος αÏθÏώματος KInetD
+Name[eo]=KInetD-modulotipo
+Name[es]=Tipo de módulo KInetD
+Name[et]=KInetD mooduli tüüp
+Name[eu]=KInetD modulu mota
+Name[fa]=نوع پیمانۀ KInetD
+Name[fi]=KInetD-moduulityyppi
+Name[fr]=Type de module de KInetD
+Name[ga]=Cineál Modúil KInetD
+Name[gl]=Tipo de módulo KInetD
+Name[he]=סוג מודול של KInetD
+Name[hi]=के-इनिट-डी मॉडà¥à¤¯à¥‚ल पà¥à¤°à¤•à¤¾à¤°
+Name[hr]=Tip KInetD Modula
+Name[hu]=KInetD modultípus
+Name[is]=KInetD Module tegund
+Name[it]=Tipo modulo KInetD
+Name[ja]=KInetD モジュールタイプ
+Name[ka]=KInetD მáƒáƒ“ულის ტიპი
+Name[kk]=KInetD модулі
+Name[km]=ប្រភáŸáž‘​ម៉ូលឌុល KInetD
+Name[lt]=KInetD modulio tipas
+Name[mk]=Тип на модул за KInetD
+Name[mn]=Модуль KInetD
+Name[ms]=Jenis Modul KInetD
+Name[mt]=Tip ta' modulu KInetD
+Name[nb]=KInetd-programtillegstype
+Name[nds]=KInetD-Moduultyp
+Name[ne]=KInetD मोडà¥à¤¯à¥à¤² पà¥à¤°à¤•à¤¾à¤°
+Name[nl]=KInetD-moduletype
+Name[nn]=KInetD-programtilleggstype
+Name[nso]=Mohuta wa Seripa sa KInetD
+Name[pa]=KInetD ਮੋਡੀਊਲ ਕਿਸਮ
+Name[pl]=Typ modułu KInetD
+Name[pt]=Tipo de Módulo do KInetD
+Name[pt_BR]=Módulo KInetD
+Name[ro]=Tip modul KInetD
+Name[ru]=Модуль KInetD
+Name[se]=KInetD-moduvlašládja
+Name[sk]=Typ modulu KInetD
+Name[sl]=Vrsta modula KInetD
+Name[sr]=KInetD врÑта модула
+Name[sr@Latn]=KInetD vrsta modula
+Name[sv]=Kinetd-modultyp
+Name[ta]=KInetD கூற௠வகை
+Name[tg]=Ðавъи Модули KInetD
+Name[tr]=KDED Modül Türü
+Name[uk]=Тип модулю KinetD
+Name[ven]=Lushaka lwa Modulu ya KInetD
+Name[xh]=Udidi Lomqongo womlinganiselo we KInetD
+Name[zh_CN]=KInetD 模å—类型
+Name[zh_HK]=KInetD 模組類型
+Name[zh_TW]=KInetD 模組類型
+Name[zu]=KInetD Uhlobo Lokwenza
+
+# id to manipulate the service
+[PropertyDef::X-KDE-KINETD-id]
+Type=QString
+
+# describes the TCP port kinetd should listen to
+[PropertyDef::X-KDE-KINETD-port]
+Type=int
+
+# if set and >0, the number of ports kinetd should probe if the port is in use
+[PropertyDef::X-KDE-KINETD-autoPortRange]
+Type=int
+
+# if enabled, kinetd will listen on the port. Can be overridden using the
+# dcop interface.
+[PropertyDef::X-KDE-KINETD-enabled]
+Type=bool
+
+# if set, this argument is given to the app to start, followed by the number
+# of the socket's fd
+[PropertyDef::X-KDE-KINETD-argument]
+Type=QString
+
+# if true, kinetd can accepts several connections at the same time. Otherwise
+# it will block the port when a connection has been established.
+[PropertyDef::X-KDE-KINETD-multiInstance]
+Type=bool
+
+# if set, kinetd will register the given URL at the local SLP SA while
+# the port is open. It will register one URL for each IP address of the
+# host.
+# The following strings will be substituted:
+# %h with the local IP address
+# %p with the port number
+# %u with the user's login name
+# %f with the user's full name
+# %i with a UUID thats identical in all URLs of this service
+[PropertyDef::X-KDE-KINETD-serviceURL]
+Type=QString
+
+# if kinetd registers a service URL, this string will be used for its attributes.
+# The following strings will be substituted:
+# %h with the local IP address
+# %p with the port number
+# %u with the user's login name
+# %f with the user's full name
+# %i with a UUID thats identical in all URLs of this service
+[PropertyDef::X-KDE-KINETD-serviceAttributes]
+Type=QString
+
+# the lifetime of a service in seconds. kinets will renew the service
+# automatically. Max 65535, never use anything under 2 min. Something like
+# 5-20 minutes is a sane value for most desktop applications.
+[PropertyDef::X-KDE-KINETD-serviceLifetime]
+Type=int
+
+# if set, kinetd will announce service with given name on local network while
+# the port is open.
+# The following strings will be substituted:
+# %h with the local IP address
+# %f with the user's full name
+# %p with the port number
+
+[PropertyDef::X-KDE-KINETD-DNSSD-Name]
+Type=QString
+
+# if kinetd announces service on network using DNS-SD, this string will be used as service
+# type. It must be in form _yourservice._udp or _yourservice._tcp
+# it uses the same substitution rules as X-KDE-KINETD-DNSSD-Name
+
+[PropertyDef::X-KDE-KINETD-DNSSD-Type]
+Type=QString
+
+# defines list of text properties for service announced via DNS-SD
+# it uses the same substitution rules as X-KDE-KINETD-DNSSD-Name
+[PropertyDef::X-KDE-KINETD-DNSSD-Properties]
+Type=QStringList
+
diff --git a/krfb/krfb/Makefile.am b/krfb/krfb/Makefile.am
new file mode 100644
index 00000000..31feb286
--- /dev/null
+++ b/krfb/krfb/Makefile.am
@@ -0,0 +1,34 @@
+KDE_CXXFLAGS = $(USE_THREADS)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libkrfbconfig.la
+libkrfbconfig_la_SOURCES = configuration.cc configuration.skel invitedialog.cc invitation.cc \
+ manageinvitations.ui personalinvitewidget.ui \
+ invitewidget.ui personalinvitedialog.cc
+libkrfbconfig_la_LIBADD = ../srvloc/libsrvloc.la $(LIB_KDEUI)
+
+bin_PROGRAMS = krfb
+krfb_SOURCES = rfbcontroller.cc xupdatescanner.cc main.cpp \
+ connectionwidget.ui krfbifaceimpl.cc krfbiface.skel \
+ trayicon.cpp connectiondialog.cc
+krfb_LDADD = libkrfbconfig.la ../libvncserver/libvncserver.la ../srvloc/libsrvloc.la -lXtst $(LIB_KDEUI) $(LIBJPEG) -lkio
+krfb_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+appdatadir = $(kde_datadir)/krfb/pics
+appdata_DATA = eyes-open24.png eyes-closed24.png connection-side-image.png
+
+kde_services_DATA = kinetd_krfb.desktop
+
+xdg_apps_DATA = krfb.desktop
+
+appdir = $(kde_datadir)/krfb
+app_DATA = eventsrc
+
+KDE_ICON = krfb
+
+INCLUDES= -I$(top_srcdir)/krfb/libvncserver -I$(top_srcdir)/krfb/srvloc \
+ $(all_includes)
+
+messages: rc.cpp
+ $(XGETTEXT) rc.cpp *.cpp *.cc -o $(podir)/krfb.pot
diff --git a/krfb/krfb/configuration.cc b/krfb/krfb/configuration.cc
new file mode 100644
index 00000000..75f8c31d
--- /dev/null
+++ b/krfb/krfb/configuration.cc
@@ -0,0 +1,474 @@
+/***************************************************************************
+ configuration.cpp
+ -------------------
+ begin : Tue Dec 11 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "configuration.h"
+#include "kinetinterface.h"
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <ksockaddr.h>
+#include <kactivelabel.h>
+
+#include <qdatastream.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qlineedit.h>
+#include <qcheckbox.h>
+
+#include <krun.h>
+
+/**
+ * Note that this class is used and provides GUI in every mode:
+ * - for the invitation dialogs
+ * - for the kcontrol module
+ * - for the running krfb instance
+ */
+Configuration::Configuration(krfb_mode mode) :
+ m_mode(mode),
+ invMngDlg(0, 0, true),
+ invDlg(0, "InviteDialog"),
+ persInvDlg(0, "PersonalInviteDialog"),
+ portNum(-1),
+ kinetdRef("kded", "kinetd")
+{
+ kinetdRef.setDCOPClient(KApplication::dcopClient());
+ loadFromKConfig();
+ saveToDialogs();
+ doKinetdConf();
+
+ connectDCOPSignal( 0, "KRFB::ConfigChanged", "KRFB_ConfigChanged()",
+ "updateKConfig()", false );
+ connect(invMngDlg.newPersonalInvitationButton, SIGNAL(clicked()),
+ SLOT(showPersonalInvitationDialog()));
+ connect(invMngDlg.newEmailInvitationButton, SIGNAL(clicked()), SLOT(inviteEmail()));
+ connect(invMngDlg.deleteOneButton, SIGNAL(clicked()), SLOT(invMngDlgDeleteOnePressed()));
+ connect(invMngDlg.deleteAllButton, SIGNAL(clicked()), SLOT(invMngDlgDeleteAllPressed()));
+ invMngDlg.listView->setSelectionMode(QListView::Extended);
+ invMngDlg.listView->setMinimumSize(QSize(400, 100)); // QTs size is much to small
+
+ connect(&invDlg, SIGNAL(createInviteClicked()),
+ SLOT(showPersonalInvitationDialog()));
+ connect(&invDlg, SIGNAL(emailInviteClicked()),
+ SLOT(inviteEmail()));
+ connect(&invDlg, SIGNAL(manageInviteClicked()),
+ SLOT(showManageInvitationsDialog()));
+ connect(&invDlg, SIGNAL(configureClicked()),
+ SLOT(showConfigurationModule()));
+ connect(this, SIGNAL(invitationNumChanged(int)),
+ &invDlg, SLOT(setInviteCount(int)));
+ connect(this, SIGNAL(invitationNumChanged(int)),
+ &invMngDlg, SLOT(listSizeChanged(int)));
+ emit invitationNumChanged(invitationList.size());
+
+ connect(&refreshTimer, SIGNAL(timeout()), SLOT(refreshTimeout()));
+ refreshTimer.start(1000*60);
+}
+
+Configuration::~Configuration() {
+ save();
+}
+
+void Configuration::updateKConfig()
+{
+ loadFromKConfig();
+}
+
+void Configuration::setKInetdEnabled(bool enabled) {
+ kinetdRef.send("setEnabled", QString("krfb"), enabled);
+ kinetdRef.send("setEnabled", QString("krfb_httpd"), enabled);
+}
+
+void Configuration::setKInetdEnabled(const QDateTime &date) {
+ kinetdRef.send("setEnabled", QString("krfb"), date);
+ kinetdRef.send("setEnabled", QString("krfb_httpd"), date);
+}
+
+void Configuration::setKInetdServiceRegistrationEnabled(bool enabled) {
+ kinetdRef.send("setServiceRegistrationEnabled",
+ QString("krfb"), enabled);
+ kinetdRef.send("setServiceRegistrationEnabled",
+ QString("krfb_httpd"), enabled);
+}
+
+void Configuration::getPortFromKInetd() {
+ DCOPReply r = kinetdRef.call("port", QString("krfb"));
+ if (!r.isValid())
+ return; // nice error msg here?
+ r.get(portNum);
+}
+
+void Configuration::setKInetdPort(int p) {
+ DCOPReply r = kinetdRef.call("setPort",
+ QString("krfb"), p, 1);
+ // nice error msg here?
+}
+
+
+void Configuration::removeInvitation(QValueList<Invitation>::iterator it) {
+ invitationList.remove(it);
+ save();
+}
+
+void Configuration::doKinetdConf() {
+ setKInetdPort(preferredPortNum);
+
+ if (allowUninvitedFlag) {
+ setKInetdEnabled(true);
+ setKInetdServiceRegistrationEnabled(enableSLPFlag);
+ getPortFromKInetd();
+ return;
+ }
+
+ QDateTime lastExpiration;
+ QValueList<Invitation>::iterator it = invitationList.begin();
+ while (it != invitationList.end()) {
+ Invitation &ix = (*it);
+ QDateTime t = ix.expirationTime();
+ if (t > lastExpiration)
+ lastExpiration = t;
+ it++;
+ }
+ if (lastExpiration.isNull() || (lastExpiration < QDateTime::currentDateTime())) {
+ setKInetdEnabled(false);
+ portNum = -1;
+ }
+ else {
+ setKInetdServiceRegistrationEnabled(false);
+ setKInetdEnabled(lastExpiration);
+ getPortFromKInetd();
+ }
+}
+
+void Configuration::loadFromKConfig() {
+
+ KConfig c("krfbrc");
+ allowUninvitedFlag = c.readBoolEntry("allowUninvited", false);
+ enableSLPFlag = c.readBoolEntry("enableSLP", true);
+ askOnConnectFlag = c.readBoolEntry("confirmUninvitedConnection", true);
+ allowDesktopControlFlag = c.readBoolEntry("allowDesktopControl", false);
+ preferredPortNum = c.readNumEntry("preferredPort", -1);
+ disableBackgroundFlag = c.readBoolEntry("disableBackground", false);
+ disableXShmFlag = c.readBoolEntry("disableXShm", false);
+ if (c.hasKey("uninvitedPasswordCrypted"))
+ passwordString = cryptStr(c.readEntry("uninvitedPasswordCrypted", ""));
+ else
+ passwordString = c.readEntry("uninvitedPassword", "");
+
+ unsigned int invNum = invitationList.size();
+ invitationList.clear();
+ c.setGroup("invitations");
+ int num = c.readNumEntry("invitation_num", 0);
+ for (int i = 0; i < num; i++)
+ invitationList.push_back(Invitation(&c, i));
+
+ invalidateOldInvitations();
+ if (invNum != invitationList.size())
+ emit invitationNumChanged(invitationList.size());
+
+}
+
+void Configuration::saveToKConfig() {
+
+ KConfig c("krfbrc");
+ c.writeEntry("confirmUninvitedConnection", askOnConnectFlag);
+ c.writeEntry("allowDesktopControl", allowDesktopControlFlag);
+ c.writeEntry("allowUninvited", allowUninvitedFlag);
+ c.writeEntry("enableSLP", enableSLPFlag);
+ c.writeEntry("preferredPort", preferredPortNum);
+ c.writeEntry("disableBackground", disableBackgroundFlag);
+ c.writeEntry("disableXShm", disableXShmFlag);
+ c.writeEntry("uninvitedPasswordCrypted", cryptStr(passwordString));
+ c.deleteEntry("uninvitedPassword");
+
+ c.setGroup("invitations");
+ int num = invitationList.count();
+ c.writeEntry("invitation_num", num);
+ int i = 0;
+ while (i < num) {
+ invitationList[i].save(&c, i);
+ i++;
+ }
+
+}
+
+void Configuration::saveToDialogs() {
+ invalidateOldInvitations();
+ QValueList<Invitation>::iterator it = invitationList.begin();
+ while (it != invitationList.end()) {
+ Invitation &inv = *(it++);
+ if (!inv.getViewItem())
+ inv.setViewItem(new KListViewItem(invMngDlg.listView,
+ inv.creationTime().toString(Qt::LocalDate),
+ inv.expirationTime().toString(Qt::LocalDate)));
+ }
+ invMngDlg.adjustSize();
+}
+
+void Configuration::save() {
+ saveToKConfig();
+ saveToDialogs();
+ doKinetdConf();
+}
+
+void Configuration::update() {
+ loadFromKConfig();
+ saveToDialogs();
+}
+
+Invitation Configuration::createInvitation() {
+ Invitation inv;
+ invitationList.push_back(inv);
+ return inv;
+}
+
+void Configuration::invalidateOldInvitations() {
+ QValueList<Invitation>::iterator it = invitationList.begin();
+ while (it != invitationList.end()) {
+ if (!(*it).isValid())
+ it = invitationList.remove(it);
+ else
+ it++;
+ }
+}
+
+void Configuration::refreshTimeout() {
+ unsigned int invNum = invitationList.size();
+ loadFromKConfig();
+ saveToDialogs();
+ if (invNum != invitationList.size())
+ emit invitationNumChanged(invitationList.size());
+}
+
+QString Configuration::hostname() const
+{
+ KInetSocketAddress *a = KInetInterface::getPublicInetAddress();
+ QString hostName;
+ if (a) {
+ hostName = a->nodeName();
+ delete a;
+ }
+ else
+ hostName = "localhost";
+ return hostName;
+}
+
+///////// properties ///////////////////////////
+
+krfb_mode Configuration::mode() const {
+ return m_mode;
+}
+
+bool Configuration::askOnConnect() const {
+ return askOnConnectFlag;
+}
+
+bool Configuration::allowDesktopControl() const {
+ return allowDesktopControlFlag;
+}
+
+bool Configuration::allowUninvitedConnections() const {
+ return allowUninvitedFlag;
+}
+
+bool Configuration::enableSLP() const {
+ return enableSLPFlag;
+}
+
+QString Configuration::password() const {
+ return passwordString;
+}
+
+QValueList<Invitation> &Configuration::invitations() {
+ return invitationList;
+}
+
+bool Configuration::disableBackground() const {
+ return disableBackgroundFlag;
+}
+
+bool Configuration::disableXShm() const {
+ return disableXShmFlag;
+}
+
+void Configuration::setAllowUninvited(bool allowUninvited) {
+ allowUninvitedFlag = allowUninvited;
+}
+
+void Configuration::setEnableSLP(bool e) {
+ enableSLPFlag = e;
+}
+
+void Configuration::setAskOnConnect(bool askOnConnect)
+{
+ askOnConnectFlag = askOnConnect;
+}
+
+void Configuration::setAllowDesktopControl(bool allowDesktopControl)
+{
+ allowDesktopControlFlag = allowDesktopControl;
+}
+
+void Configuration::setPassword(QString password)
+{
+ passwordString = password;
+}
+
+int Configuration::port() const
+{
+ if ((portNum < 5900) || (portNum >= 6000))
+ return portNum;
+ else
+ return portNum - 5900;
+}
+
+// use p=-1 for defaults
+void Configuration::setPreferredPort(int p)
+{
+ preferredPortNum = p;
+}
+
+int Configuration::preferredPort() const
+{
+ return preferredPortNum;
+}
+
+void Configuration::setDisableBackground(bool disable) {
+ disableBackgroundFlag = disable;
+}
+
+void Configuration::setDisableXShm(bool disable) {
+ disableXShmFlag = disable;
+}
+
+////////////// invitation manage dialog //////////////////////////
+
+void Configuration::showManageInvitationsDialog() {
+ loadFromKConfig();
+ saveToDialogs();
+ invMngDlg.exec();
+}
+
+void Configuration::invMngDlgDeleteOnePressed() {
+ QValueList<Invitation>::iterator it = invitationList.begin();
+ while (it != invitationList.end()) {
+ Invitation &ix = (*it);
+ KListViewItem *iv = ix.getViewItem();
+ if (iv && iv->isSelected())
+ it = invitationList.remove(it);
+ else
+ it++;
+ }
+ saveToKConfig();
+ doKinetdConf();
+ emit invitationNumChanged(invitationList.size());
+}
+
+void Configuration::invMngDlgDeleteAllPressed() {
+ invitationList.clear();
+ saveToKConfig();
+ doKinetdConf();
+ emit invitationNumChanged(invitationList.size());
+}
+
+////////////// invitation dialog //////////////////////////
+
+void Configuration::showInvitationDialog() {
+ invDlg.exec();
+ emit invitationFinished();
+ saveToKConfig();
+}
+
+////////////// personal invitation dialog //////////////////////////
+
+void Configuration::showPersonalInvitationDialog() {
+ loadFromKConfig();
+ Invitation inv = createInvitation();
+ save();
+ emit invitationNumChanged(invitationList.size());
+
+ invDlg.enableInviteButton(false);
+ invMngDlg.newPersonalInvitationButton->setEnabled(false);
+
+ persInvDlg.setHost(hostname(), port());
+ persInvDlg.setPassword(inv.password());
+ persInvDlg.setExpiration(inv.expirationTime());
+
+ persInvDlg.exec();
+ invDlg.enableInviteButton(true);
+ invMngDlg.newPersonalInvitationButton->setEnabled(true);
+}
+
+////////////// invite email //////////////////////////
+
+void Configuration::inviteEmail() {
+ int r = KMessageBox::warningContinueCancel(0,
+ i18n("When sending an invitation by email, note that everybody who reads this email "
+ "will be able to connect to your computer for one hour, or until the first "
+ "successful connection took place, whichever comes first. \n"
+ "You should either encrypt the email or at least send it only in a "
+ "secure network, but not over the Internet."),
+ i18n("Send Invitation via Email"),
+ KStdGuiItem::cont(),
+ "showEmailInvitationWarning");
+ if (r == KMessageBox::Cancel)
+ return;
+
+ loadFromKConfig();
+ Invitation inv = createInvitation();
+ save();
+ emit invitationNumChanged(invitationList.size());
+
+ KApplication *app = KApplication::kApplication();
+ app->invokeMailer(QString::null, QString::null, QString::null,
+ i18n("Desktop Sharing (VNC) invitation"),
+ i18n("You have been invited to a VNC session. If you have the KDE Remote "
+ "Desktop Connection installed, just click on the link below.\n\n"
+ "vnc://invitation:%1@%2:%3\n\n"
+ "Otherwise you can use any VNC client with the following parameters:\n\n"
+ "Host: %4:%5\n"
+ "Password: %6\n\n"
+ "Alternatively you can click on the link below to start the VNC session\n"
+ "within your web browser.\n"
+ "\n"
+ " http://%7:%8/\n"
+ "\n"
+ "For security reasons this invitation will expire at %9.")
+ .arg(inv.password())
+ .arg(hostname())
+ .arg(port())
+ .arg(hostname())
+ .arg(port())
+ .arg(inv.password())
+ .arg(hostname())
+ .arg(5800) // determine with dcop ... later ...
+ .arg(KGlobal::locale()->formatDateTime(inv.expirationTime())));
+}
+
+////////////// invoke kcontrol module //////////////////////////
+
+void Configuration::showConfigurationModule() {
+ KRun::run( "kcmshell kcmkrfb", KURL::List() );
+}
+
+
+#include "configuration.moc"
diff --git a/krfb/krfb/configuration.h b/krfb/krfb/configuration.h
new file mode 100644
index 00000000..cec7a2ed
--- /dev/null
+++ b/krfb/krfb/configuration.h
@@ -0,0 +1,139 @@
+/***************************************************************************
+ configuration.h
+ -------------------
+ begin : Tue Dec 11 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 CONFIGURATION_H
+#define CONFIGURATION_H
+
+#include "invitation.h"
+
+#include "manageinvitations.h"
+#include "personalinvitedialog.h"
+#include "invitedialog.h"
+
+#include <dcopref.h>
+#include <kconfig.h>
+#include <qtimer.h>
+#include <qobject.h>
+#include <qvalidator.h>
+#include <qstring.h>
+
+#include <dcopobject.h>
+
+enum krfb_mode {
+ KRFB_UNKNOWN_MODE = 0,
+ KRFB_KINETD_MODE,
+ KRFB_INVITATION_MODE,
+ KRFB_CONFIGURATION_MODE
+};
+
+
+/**
+ * This class stores the app's configuration, manages the
+ * standalone-config-dialog and all the invitation dialogs
+ * @author Tim Jansen
+ */
+class Configuration : public QObject, public DCOPObject {
+ K_DCOP
+ Q_OBJECT
+public:
+ Configuration(krfb_mode mode);
+ virtual ~Configuration();
+
+ krfb_mode mode() const;
+ bool askOnConnect() const;
+ bool allowDesktopControl() const;
+ bool allowUninvitedConnections() const;
+ bool enableSLP() const;
+ QString password() const;
+ QString hostname() const;
+ int port() const;
+ int preferredPort() const;
+ bool disableBackground() const;
+ bool disableXShm() const;
+
+ void setAllowUninvited(bool allowUninvited);
+ void setEnableSLP(bool e);
+ void setAskOnConnect(bool askOnConnect);
+ void setPassword(QString password);
+ void setPreferredPort(int p);
+ void setDisableBackground(bool disable);
+ void setDisableXShm(bool disable);
+ void save();
+ void update();
+
+ QValueList<Invitation> &invitations();
+ void removeInvitation(QValueList<Invitation>::iterator it);
+signals:
+ void invitationFinished();
+ void invitationNumChanged(int num);
+
+public slots:
+ void setAllowDesktopControl(bool allowDesktopControl);
+ void showManageInvitationsDialog();
+ void showInvitationDialog();
+ void showPersonalInvitationDialog();
+ void showConfigurationModule();
+ void inviteEmail();
+
+private:
+ void loadFromKConfig();
+ void loadFromDialogs();
+ void saveToKConfig();
+ void saveToDialogs();
+ Invitation createInvitation();
+ void closeInvDlg();
+ void invalidateOldInvitations();
+ void setKInetdEnabled(const QDateTime &date);
+ void setKInetdEnabled(bool enabled);
+ void setKInetdServiceRegistrationEnabled(bool enabled);
+ void getPortFromKInetd();
+ void setKInetdPort(int port);
+ void doKinetdConf();
+
+ krfb_mode m_mode;
+
+ ManageInvitationsDialog invMngDlg;
+ InviteDialog invDlg;
+ PersonalInviteDialog persInvDlg;
+ QTimer refreshTimer;
+
+ bool askOnConnectFlag;
+ bool allowDesktopControlFlag;
+ bool allowUninvitedFlag;
+ bool enableSLPFlag;
+
+ int portNum, preferredPortNum;
+
+ DCOPRef kinetdRef;
+
+ QString passwordString;
+ QValueList<Invitation> invitationList;
+
+ bool disableBackgroundFlag;
+ bool disableXShmFlag;
+
+k_dcop:
+ // Connected to the DCOP signal
+ void updateKConfig();
+private slots:
+ void refreshTimeout();
+
+ void invMngDlgDeleteOnePressed();
+ void invMngDlgDeleteAllPressed();
+};
+
+#endif
diff --git a/krfb/krfb/connection-side-image.png b/krfb/krfb/connection-side-image.png
new file mode 100644
index 00000000..f3d9db67
--- /dev/null
+++ b/krfb/krfb/connection-side-image.png
Binary files differ
diff --git a/krfb/krfb/connectiondialog.cc b/krfb/krfb/connectiondialog.cc
new file mode 100644
index 00000000..eeb80479
--- /dev/null
+++ b/krfb/krfb/connectiondialog.cc
@@ -0,0 +1,63 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "connectiondialog.h"
+#include "connectionwidget.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+
+ConnectionDialog::ConnectionDialog( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "New Connection" ),
+ Ok|Cancel, Cancel, true )
+{
+ m_connectWidget = new ConnectionWidget( this, "ConnectWidget" );
+ m_connectWidget->pixmapLabel->setPixmap(
+ UserIcon( "connection-side-image.png" ) );
+
+ KGuiItem accept = KStdGuiItem::ok();
+ accept.setText( i18n( "Accept Connection" ) );
+ setButtonOK( accept );
+
+ KGuiItem refuse = KStdGuiItem::cancel();
+ refuse.setText( i18n( "Refuse Connection" ) );
+ setButtonCancel( refuse );
+
+ setMainWidget( m_connectWidget );
+}
+
+void ConnectionDialog::setRemoteHost( const QString &host )
+{
+ m_connectWidget->remoteHost->setText( host );
+}
+
+void ConnectionDialog::setAllowRemoteControl( bool b )
+{
+ m_connectWidget->cbAllowRemoteControl->setChecked( b );
+}
+
+bool ConnectionDialog::allowRemoteControl()
+{
+ return m_connectWidget->cbAllowRemoteControl->isChecked();
+}
+
+#include "connectiondialog.moc"
diff --git a/krfb/krfb/connectiondialog.h b/krfb/krfb/connectiondialog.h
new file mode 100644
index 00000000..35deae29
--- /dev/null
+++ b/krfb/krfb/connectiondialog.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CONNECTIONDIALOG_H
+#define CONNECTIONDIALOG_H
+
+#include <kdialogbase.h>
+
+class ConnectionWidget;
+
+class ConnectionDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ConnectionDialog( QWidget *parent, const char *name );
+ ~ConnectionDialog() {};
+
+ void setRemoteHost( const QString &host );
+ void setAllowRemoteControl( bool b );
+ bool allowRemoteControl();
+
+ protected:
+ ConnectionWidget *m_connectWidget;
+};
+
+#endif // CONNECTIONDIALOG_H
+
diff --git a/krfb/krfb/connectionwidget.ui b/krfb/krfb/connectionwidget.ui
new file mode 100644
index 00000000..e053adf9
--- /dev/null
+++ b/krfb/krfb/connectionwidget.ui
@@ -0,0 +1,208 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ConnectionWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>NewConnectWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>521</width>
+ <height>328</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>13</pointsize>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Attention</string>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>mainTextLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="lineWidth">
+ <number>-1</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="midLineWidth">
+ <number>5</number>
+ </property>
+ <property name="text">
+ <string>Somebody is requesting a connection to your computer. Granting this will allow the remote user to watch your desktop. </string>
+ </property>
+ <property name="textFormat">
+ <enum>AutoText</enum>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="6" colspan="1">
+ <property name="name">
+ <cstring>pixmapLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>WinPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="indent">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>remoteHost</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>123.234.123.234</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>cbAllowRemoteControl</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Allow remote user to &amp;control keyboard and mouse</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you turn this option on, the remote user can enter keystrokes and use your mouse pointer. This gives them full control over your computer, so be careful. When the option is disabled the remote user can only watch your screen.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Remote system:</string>
+ </property>
+ </widget>
+ <spacer row="4" column="1">
+ <property name="name">
+ <cstring>spacer23</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>84</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer22</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/krfb/krfb/cr16-app-krfb.png b/krfb/krfb/cr16-app-krfb.png
new file mode 100644
index 00000000..9bbbe097
--- /dev/null
+++ b/krfb/krfb/cr16-app-krfb.png
Binary files differ
diff --git a/krfb/krfb/cr32-app-krfb.png b/krfb/krfb/cr32-app-krfb.png
new file mode 100644
index 00000000..b5a6328b
--- /dev/null
+++ b/krfb/krfb/cr32-app-krfb.png
Binary files differ
diff --git a/krfb/krfb/cr48-app-krfb.png b/krfb/krfb/cr48-app-krfb.png
new file mode 100644
index 00000000..19c31a71
--- /dev/null
+++ b/krfb/krfb/cr48-app-krfb.png
Binary files differ
diff --git a/krfb/krfb/eventsrc b/krfb/krfb/eventsrc
new file mode 100644
index 00000000..ba18c766
--- /dev/null
+++ b/krfb/krfb/eventsrc
@@ -0,0 +1,1219 @@
+[!Global!]
+IconName=krfb
+Comment=Desktop Sharing
+Comment[af]=Werkskerm Deeling
+Comment[ar]=مشاركة سطح المكتب
+Comment[be]=Ðгульнае выкарыÑтанне кампутара
+Comment[bg]=СподелÑне на работното мÑÑто
+Comment[bn]=ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি
+Comment[br]=Rannañ ar vurev
+Comment[bs]=Dijeljenje desktopa
+Comment[ca]=Compartició de l'escriptori
+Comment[cs]=Sdílení pracovní plochy
+Comment[cy]=Rhannu Penbwrdd
+Comment[da]=Desktopdeling
+Comment[de]=Arbeitsfläche freigeben
+Comment[el]=Κοινή χÏήση επιφάνειας εÏγασίας
+Comment[eo]=Tabula fordonado
+Comment[es]=Escritorio compartido
+Comment[et]=Töölaua jagamine
+Comment[eu]=Mahaigain partekatzea
+Comment[fa]=اشتراک رومیزی
+Comment[fi]=Työpöydän jakaminen
+Comment[fr]=Partage du bureau
+Comment[ga]=Roinnt Deisce
+Comment[gl]=Compartición do escritorio
+Comment[he]=שיתוף שולחנות עבודה
+Comment[hi]=डेसà¥à¤•à¤Ÿà¥‰à¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Comment[hr]=Dijeljenje radne površine
+Comment[hu]=Munkaasztal-megosztás
+Comment[is]=Skjáborðamiðlun
+Comment[it]=Condivisione desktop
+Comment[ja]=デスクトップ共有
+Comment[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ–იáƒáƒ áƒ”ბáƒ
+Comment[kk]=Ò®Ñтелді ортақтаÑтыру
+Comment[km]=ការ​ចែក​រំលែក​ផ្ទែ​ážáž»
+Comment[lt]=Dalinimasis darbastaliu
+Comment[mk]=Делење на работната површина
+Comment[mn]=Ðжлын байрыг хамтран ÑзÑмших
+Comment[ms]=Perkongsian Ruang Kerja
+Comment[mt]=Qsim tad-desktop
+Comment[nb]=Skrivebordsdeling
+Comment[nds]=Schriefdisch-Freegaav
+Comment[ne]=डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Comment[nl]=Bureaublad delen
+Comment[nn]=Skrivebordsdeling
+Comment[nso]=Kabagano ya Desktop
+Comment[pa]=ਡੈਸਕਟਾਪ ਸਾਂà¨
+Comment[pl]=Współdzielenie pulpitu
+Comment[pt]=Partilha do Ecrã
+Comment[pt_BR]=Compartilhamento do Ambiente de Trabalho
+Comment[ro]=Partajare ecran
+Comment[ru]=Параметры общего рабочего Ñтола
+Comment[se]=Čállinbeavdejuogadeapmi
+Comment[sk]=Zdieľanie pracovnej plochy
+Comment[sl]=Deljenje namizja
+Comment[sr]=Дељење радне површине
+Comment[sr@Latn]=Deljenje radne površine
+Comment[sv]=Dela ut skrivbord
+Comment[ta]=பணிமேடை பகிரà¯à®µà¯
+Comment[tg]=ИÑтифодаи Муштараки Мизи Корӣ
+Comment[th]=ใช้งานพื้นที่ทำงานร่วมà¸à¸±à¸™
+Comment[tr]=Masaüstü Paylaşımı
+Comment[uk]=Спільні Ñтільниці
+Comment[ven]=U kovhekana ha desikithopo
+Comment[xh]=Ulwahlulelano lwe Desktop
+Comment[zh_CN]=æ¡Œé¢å…±äº«
+Comment[zh_HK]=æ¡Œé¢åˆ†äº«
+Comment[zh_TW]=æ¡Œé¢åˆ†äº«
+Comment[zu]=Ukuhlukaniselana kwe-Desktop
+
+[UserAcceptsConnection]
+Name=UserAcceptsConnection
+Name[ar]=المستخدم قبل الاتصال
+Name[bg]=Приета е връзка от потребител
+Name[bn]=বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ সংযোগ গà§à¦°à¦¹à¦£ করে
+Name[ca]=L'usuari accepta la connexió
+Name[cs]=Uživatel přijímá spojení
+Name[cy]=DefnyddiwrDerbynCysylltiad
+Name[da]=BrugerAcceptererForbindelse
+Name[de]=BenutzerBestätigtVerbindung
+Name[el]=Ο χÏήστης αποδέχεται σÏνδεση
+Name[eo]=UzantoAkceptasKonektojn
+Name[es]=El usuario acepta la conexión
+Name[et]=Kasutaja ühendusega nõus
+Name[eu]=Erabiltzaileak konexioa onartu du
+Name[fa]=پذیرش اتصال توسط کاربر
+Name[fr]=L'utilisateur accepte les connexions
+Name[ga]=GlacannÚsáideoirLeCeangal
+Name[gl]=O usuario acepta a conexión
+Name[he]=משתמש מקבל חיבור
+Name[hi]=उपयोकà¥à¤¤à¤¾-कनेकà¥à¤¶à¤¨-सà¥à¤µà¥€à¤•à¤¾à¤°à¤¾
+Name[hr]=KorisnikPrihvaćaVezu
+Name[hu]=KapcsolatElfogadva
+Name[is]=NotandiSamþykkirTengingar
+Name[it]=L'utente accetta la connessione
+Name[ja]=ユーザãŒè¨±å¯ã—ãŸæŽ¥ç¶š
+Name[kk]=Пайдаланушы қоÑылымды қабылдайды
+Name[km]=អ្នក​ប្រើ​ទទួលយក​ការ​ážâ€‹áž—្ជាប់
+Name[lt]=Naudotojas priima kvietimÄ…
+Name[mk]=КориÑникот го прифаќа поврзувањето
+Name[mn]=Ð¥ÑÑ€ÑглÑгч зөвшөөрÑөн холболт
+Name[ms]=Pengguna Menerima Sambungan
+Name[mt]=UserJaċċettaKonnessjoni
+Name[nb]=Bruker tar i mot oppkobling
+Name[nds]=BrukerLettTokoppelnTo
+Name[ne]=पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ जडान सà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¦à¤›
+Name[nl]=Gebruiker accepteert verbinding
+Name[nn]=Brukar godtek samband
+Name[nso]=Modirisi o Amogela Kgokagano
+Name[pl]=Połączenie akceptowane przez użytkownika
+Name[pt_BR]=Aceita Conexões do Usuário
+Name[ro]=Conexiune acceptată de utilizator
+Name[ru]=Пользователь принимает ÑоединениÑ
+Name[se]=Geavaheaddji dohkkeha oktavuođa
+Name[sk]=Užívateľ akceptoval spojenie
+Name[sl]=Uporabnik sprejel povezavo
+Name[sr]=КориÑник прихвата везе
+Name[sr@Latn]=Korisnik prihvata veze
+Name[sv]=Användare accepterar anslutning
+Name[ta]=பயனர௠இணைபà¯à®ªà¯ à®à®±à¯à®±à¯à®•à¯à®•à¯Šà®³à¯à®³à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Name[tg]=Корванд ПайваÑтшавиро Қабул мекунад
+Name[th]=ผู้ใช้ยอมรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Name[tr]=Kullanıcı Kabul Eden Bağlantı
+Name[uk]=КориÑтувач приймає з'єднаннÑ
+Name[ven]=Mushumisi o tanganedza vhukwamani
+Name[xh]=Umsebenzisi Wamkela Uxhulumaniso
+Name[zh_CN]=用户接å—连接
+Name[zh_HK]=用戶接å—連線
+Name[zh_TW]=使用者接å—連線
+Name[zu]=UmsebenziUvumelaUkuxhumana
+Comment=User accepts connection
+Comment[af]=Gebruiker aanvaar verbinding
+Comment[ar]=المستخدم قبل الإتصال
+Comment[be]=КарыÑтальнік дазволіў злучÑнне
+Comment[bg]=Приета е връзка от потребител
+Comment[bn]=বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ সংযোগ গà§à¦°à¦¹à¦£ করে
+Comment[bs]=Korisnik prihvata konekciju
+Comment[ca]=L'usuari accepta la connexió
+Comment[cs]=Uživatel přijímá spojení
+Comment[cy]=Mae'r defnyddiwr yn derbyn y cysylltiad
+Comment[da]=Bruger accepterer forbindelse
+Comment[de]=Der Benutzer bestätigt die Verbindung
+Comment[el]=Ο χÏήστης αποδέχεται σÏνδεση
+Comment[eo]=Uzanto akceptas konektojn
+Comment[es]=El usuario acepta la conexión
+Comment[et]=Kasutaja nõustub ühendusega
+Comment[eu]=Erabiltzaileak konexioa onartu du
+Comment[fa]=کاربر اتصال را پذیرÙت
+Comment[fi]=Käyttäjä hyväksyy yhteyden
+Comment[fr]=l'utilisateur accepte les connexions
+Comment[ga]=Glacann úsáideoir le ceangal
+Comment[gl]=O usuario aceptou a conexión
+Comment[he]=המשתמש מקבל ×ת החיבור
+Comment[hi]=उपयोकà¥à¤¤à¤¾ कनेकà¥à¤¶à¤¨ सà¥à¤µà¥€à¤•à¤¾à¤°à¤¾
+Comment[hr]=Korisnik prihvaća vezu
+Comment[hu]=A felhasználó elfogadja a csatlakozási kérést
+Comment[is]=Notandi samþykkir tengingu
+Comment[it]=L'utente accetta la connessione
+Comment[ja]=ユーザãŒæŽ¥ç¶šã‚’許å¯
+Comment[ka]=მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბლმრმიიღრკáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜
+Comment[kk]=Пайдаланушы қоÑылымды қабылдайды
+Comment[km]=អ្នក​ប្រើ​ទទួលយក​ការ​ážâ€‹áž—្ជាប់
+Comment[lt]=Naudotojas priima kvietimÄ…
+Comment[mk]=КориÑникот прифаќа поврзување
+Comment[mn]=Ð¥ÑÑ€ÑглÑгч зөвшөөрÑөн холболт
+Comment[ms]= Pengguna menerima sambungan
+Comment[mt]=User jaċċetta l-konnessjoni
+Comment[nb]=Bruker tar imot oppkobling
+Comment[nds]=Bruker nimmt Tokoppelanfraag an
+Comment[ne]=पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ जडान सà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¦à¤›
+Comment[nl]=Gebruiker accepteert verbinding
+Comment[nn]=Brukar godtek samband
+Comment[nso]=Modirisi o Amogela Kgokagano
+Comment[pl]=Użytkownik akceptuje połączenie
+Comment[pt]=O utilizador aceita a ligação
+Comment[pt_BR]=O usuário aceita a conexão
+Comment[ro]=Utilizatorul acceptă conexiunea
+Comment[ru]=Пользователь принимает ÑоединениÑ
+Comment[se]=Geavaheaddji dohkkeha oktavuođa
+Comment[sk]=Užívateľ akceptoval spojenie
+Comment[sl]=Uporabnik sprejel povezavo
+Comment[sr]=КориÑник прихвата везу
+Comment[sr@Latn]=Korisnik prihvata vezu
+Comment[sv]=Användaren accepterar anslutning
+Comment[ta]=பயனர௠இணைபà¯à®ªà¯ à®à®±à¯à®±à¯à®•à¯à®•à¯Šà®³à¯à®³à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Корванд пайваÑтшавиро қабул мекунад
+Comment[th]=ผู้ใช้ยอมรับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Comment[tr]=Kullanıcı bağlantıyı kabul etti
+Comment[uk]=КориÑтувач приймає з'єднаннÑ
+Comment[ven]=Mushumisi o tanganedza vhukwamani
+Comment[xh]=Umsebenzisi wamkela uxhulumaniso
+Comment[zh_CN]=用户接å—连接
+Comment[zh_HK]=用戶接å—連線
+Comment[zh_TW]=使用者接å—的連線
+Comment[zu]=Umsebenzi uyakuvumela ukuxhumana
+default_presentation=4
+
+[UserRefusesConnection]
+Name=UserRefusesConnection
+Name[ar]=المستخدم رÙض الاتصال
+Name[bg]=Отказана връзка от потребител
+Name[bn]=বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ সংযোগ অসà§à¦¬à§€à¦•à¦¾à¦° করে
+Name[ca]=L'usuari refusa la connexió
+Name[cs]=Uživatel odmítá spojení
+Name[cy]=DefnyddiwrGwrthodCysylltiad
+Name[da]=BrugerAfslårForbindelse
+Name[de]=BenutzerLehntVerbindungAb
+Name[el]=Ο χÏήστης αποÏÏίπτει σÏνδεση
+Name[eo]=UzantoRifuzasKonektojn
+Name[es]=El usuario rechaza la conexión
+Name[et]=Kasutaja keeldub ühendusest
+Name[eu]=Erabiltzaileak konexioa ukatu du
+Name[fa]=نپذیرÙتن اتصال توسط کاربر
+Name[fr]=L'utilisateur refuse les connexions
+Name[ga]=DiúltíonnÚsáideoirLeCeangal
+Name[gl]=O usuario rexeita a conexión
+Name[he]=משתמש דוחה חיבור
+Name[hi]=उपयोकà¥à¤¤à¤¾-कनेकà¥à¤¶à¤¨-असà¥à¤µà¥€à¤•à¤¾à¤°à¤¾
+Name[hr]=KorisnikOdbijaVezu
+Name[hu]=KapcsolatVisszautasítva
+Name[is]=NotandiHafnarTengingum
+Name[it]=L'utente rifiuta la connessione
+Name[ja]=ユーザãŒæ‹’å¦ã—ãŸæŽ¥ç¶š
+Name[kk]=Пайдаланушы қоÑылымды қабылдамайды
+Name[km]=អ្នក​ប្រើ​បដិសáŸáž’​ការ​ážâ€‹áž—្ជាប់
+Name[lt]=Naudotojas atmeta kvietimÄ…
+Name[mk]=КориÑникот го одбива поврзувањето
+Name[mn]=Ð¥ÑÑ€ÑглÑгч зөвшөөрөөгүй холболт
+Name[ms]=Pengguna Menolak Sambungan
+Name[mt]=UserJirrifjutaKonnessjoni
+Name[nb]=Bruker avviser oppkobling
+Name[nds]=BrukerWiestTokoppelnAf
+Name[ne]=पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ जडान असà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¦à¤›
+Name[nl]=Gebruiker weigert verbinding
+Name[nn]=Brukar nektar samband
+Name[nso]=Modirisi o Gana Kgokagano
+Name[pl]=Połączenie odrzucone przez użytkownika
+Name[pt_BR]=Rejeita Conexões do Usuário
+Name[ro]=Conexiune respinsă de utilizator
+Name[ru]=Пользователь не принимает ÑоединениÑ
+Name[se]=Geavaheaddji hilgo oktavuođa
+Name[sk]=Užívateľ zamietol spojenie
+Name[sl]=Uporabnik zavrnil povezavo
+Name[sr]=КориÑник одбија везе
+Name[sr@Latn]=Korisnik odbija veze
+Name[sv]=Användaren vägrar anslutning
+Name[ta]=பயனர௠இணைபà¯à®ªà¯ à®à®±à¯à®•à®ªà¯à®ªà®Ÿà®µà®¿à®²à¯à®²à¯ˆ
+Name[tg]=Корванд ПайваÑтшавиро Рад мекунад
+Name[th]=ผู้ใช้ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Name[tr]=Kullanıcı Bağlantıyı Reddetti
+Name[uk]=КориÑтувачВідмовлÑєУЗ'єднанні
+Name[ven]=Mushumisi o hana Vhukwamani
+Name[xh]=Umsebenzisi Uyalwala Uxhulumaniso
+Name[zh_CN]=用户拒ç»è¿žæŽ¥
+Name[zh_HK]=用戶拒絕連線
+Name[zh_TW]=使用者拒絕連線
+Name[zu]=UmsebenzisiWalaUxhumaniso
+Comment=User refuses connection
+Comment[af]=Gebruiker weier verbinding
+Comment[ar]=المستخدم رÙض الاتصال
+Comment[be]=КарыÑтальнік адмовіў злучÑнню
+Comment[bg]=Отказана връзка от потребител
+Comment[bn]=বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€ সংযোগ অসà§à¦¬à§€à¦•à¦¾à¦° করে
+Comment[bs]=Korisnik odbija konekciju
+Comment[ca]=L'usuari refusa la connexió
+Comment[cs]=Uživatel odmítá spojení
+Comment[cy]=Mae'r defnyddiwr yn gwrthod y cysylltiad
+Comment[da]=Bruger afslår forbindelse
+Comment[de]=Der Benutzer lehnt die Verbindung ab
+Comment[el]=Ο χÏήστης αποÏÏίπτει σÏνδεση
+Comment[eo]=Uzanto rifuzas konektojn
+Comment[es]=El usuario rechaza la conexión
+Comment[et]=Kasutaja keeldub ühendusest
+Comment[eu]=Erabiltzaileak konexioa ukatu du
+Comment[fa]=کاربر اتصال را نپذیرÙت
+Comment[fi]=Käyttäjä hylkää yhteyden
+Comment[fr]=L'utilisateur refuse les connexions
+Comment[ga]=Diúltaíonn úsáideoir ceangal
+Comment[gl]=O usuario rexeitou a conexión
+Comment[he]=המשתמש מסרב לחיבור
+Comment[hi]=उपयोकà¥à¤¤à¤¾ कनेकà¥à¤¶à¤¨ असà¥à¤µà¥€à¤•à¤¾à¤°à¤¾
+Comment[hr]=Korisnik odbija vezu
+Comment[hu]=A felhasználó visszautasítja a csatlakozási kérést
+Comment[is]=Notandi hafnar tengingu
+Comment[it]=L'utente rifiuta la connessione
+Comment[ja]=ユーザãŒæŽ¥ç¶šã‚’æ‹’å¦
+Comment[ka]=მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბელმრუáƒáƒ áƒ§áƒ კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜
+Comment[kk]=Пайдаланушы қоÑылымды қабылдамайды
+Comment[km]=អ្នក​ប្រើ​បដិសáŸáž’​ការ​ážâ€‹áž—្ជាប់
+Comment[lt]=Naudotojas atmeta kvietimÄ…
+Comment[mk]=КориÑникот одбива поврзување
+Comment[mn]=Ð¥ÑÑ€ÑглÑгч зөвшөөрөөгүй холболт
+Comment[ms]=Pengguna menolak sambungan
+Comment[mt]=User jiċħad il-konnessjoni
+Comment[nb]=Bruker avviser oppkobling
+Comment[nds]=Bruker wiest Tokoppelanfraag af
+Comment[ne]=पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ जडान असà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¦à¤›
+Comment[nl]=Gebruiker weigert verbinding
+Comment[nn]=Brukar nektar samband
+Comment[nso]=Modirisi o gana kgokagano
+Comment[pl]=Użytkownik odrzuca połączenie
+Comment[pt]=O utilizador recusa a ligação
+Comment[pt_BR]=O usuário rejeita a conexão
+Comment[ro]=Utilizatorul refuză conexiunea
+Comment[ru]=Пользователь не принимает ÑоединениÑ
+Comment[se]=Geavaheaddji hilgo oktavuođa
+Comment[sk]=Užívateľ odmietol spojenie
+Comment[sl]=Uporabnik zavrnil povezavo
+Comment[sr]=КориÑник одбија везу
+Comment[sr@Latn]=Korisnik odbija vezu
+Comment[sv]=Användaren vägrar anslutning
+Comment[ta]=பயனர௠இணைபà¯à®ªà¯ à®à®±à¯à®• மறà¯à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Корванд пайваÑтшавиро рад мекунад
+Comment[th]=ผู้ใช้ปà¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Comment[tr]=Kullanıcı bağlantıyı iptal etti
+Comment[uk]=КориÑтувач відмовлÑÑ” у з'єднанні
+Comment[ven]=Mushumisi o hana vhukwamani
+Comment[xh]=Umsebenzisi wala uxhulumaniso
+Comment[zh_CN]=用户拒ç»è¿žæŽ¥
+Comment[zh_HK]=用戶拒絕連線
+Comment[zh_TW]=使用者拒絕的連線
+Comment[zu]=Umsebenzi awukuvumeli ukuxhumana
+default_presentation=4
+
+[ConnectionClosed]
+Name=ConnectionClosed
+Name[ar]=اتصال مغلق
+Name[bg]=Връзката е прекъÑната
+Name[bn]=সংযোগ বনà§à¦§ করা হল
+Name[br]=Kevreadur serret
+Name[ca]=Connexió tancada
+Name[cs]=Spojení ukonÄeno
+Name[cy]=CysylltiadArGau
+Name[da]=ForbindelseLukket
+Name[de]=VerbindungGeschlossen
+Name[el]=Η σÏνδεση έκλεισε
+Name[eo]=KonektoFermita
+Name[es]=Conexión cerrada
+Name[et]=Ãœhendus suletud
+Name[eu]=Konexioa itxi da
+Name[fa]=اتصال بسته
+Name[fr]=Connexion fermée
+Name[ga]=CeangalDúnta
+Name[gl]=Conexión pechada
+Name[he]=חיבור נסגר
+Name[hi]=कनेकà¥à¤¶à¤¨-बनà¥à¤¦
+Name[hr]=VezaPrekinuta
+Name[hu]=KapcsolatBezárva
+Name[is]=TenginguLokað
+Name[it]=Connessione chiusa
+Name[ja]=接続切断
+Name[kk]=ҚоÑылым жабылды
+Name[km]=បាន​បិទ​ការ​ážâ€‹áž—្ជាប់
+Name[lt]=Ryšys baigtas
+Name[mk]=Поврзувањето е затворено
+Name[mn]=Холболт хаагдав
+Name[ms]=Sambungan Ditutup
+Name[mt]=KonnessjonijiMagħluqa
+Name[nb]=Kobling stengt
+Name[nds]=Afkoppelt
+Name[ne]=जडना बनà¥à¤¦ भयो
+Name[nl]=Verbinding gesloten
+Name[nn]=Samband stengt
+Name[nso]=Kgokagano e Tswaletswe
+Name[pl]=Połączenia zakończone
+Name[pt_BR]=Conexão fechada
+Name[ro]=Conexiune închisă
+Name[ru]=Соединение закрыто
+Name[se]=Oktavuohta giddejuvui
+Name[sk]=Spojenie ukonÄené
+Name[sl]=Povezava zaprta
+Name[sr]=Веза је затворена
+Name[sr@Latn]=Veza je zatvorena
+Name[sv]=Anslutning stängd
+Name[ta]=இணைபà¯à®ªà¯ மூடபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Name[tg]=ПайваÑтшавӣ Пӯшида шудааÑÑ‚
+Name[th]=à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸¢à¸¸à¸•à¸´
+Name[tr]=Bağlantı Kapatıldı
+Name[uk]=З'єднаннÑЗакрито
+Name[ven]=Vhukwamani ho valwa
+Name[xh]=Uxhulumaniso Luvaliwe
+Name[zh_CN]=连接关闭
+Name[zh_HK]=連線已關閉
+Name[zh_TW]=連線已關閉
+Name[zu]=UkuxhumanisaKuvaliwe
+Comment=Connection closed
+Comment[af]=Verbinding gesluit
+Comment[ar]=الاتصال Ù‚Ùطع
+Comment[be]=ЗлучÑнне закрытае
+Comment[bg]=Връзката е прекъÑната
+Comment[bn]=সংযোগ বনà§à¦§ করা হল
+Comment[br]=Serret eo ar gevreadenn
+Comment[bs]=Konekcija prekinuta
+Comment[ca]=Connexió tancada
+Comment[cs]=Spojení ukonÄeno
+Comment[cy]=Mae'r cysylltiad ar gau
+Comment[da]=Forbindelse lukket
+Comment[de]=Verbindung geschlossen
+Comment[el]=Η σÏνδεση έκλεισε
+Comment[eo]=Konekto fermita
+Comment[es]=Conexión rechazada
+Comment[et]=Ãœhendus suletud
+Comment[eu]=Konexioa itxi da
+Comment[fa]=اتصال بسته شد
+Comment[fi]=Yhteys suljettu
+Comment[fr]=Connexion coupée
+Comment[ga]=Ceangal dúnta
+Comment[gl]=Conexión pechada
+Comment[he]=החיבור נסגר
+Comment[hi]=कनेकà¥à¤¶à¤¨ बनà¥à¤¦
+Comment[hr]=Veza prekinuta
+Comment[hu]=A kapcsolat bezárva
+Comment[is]=Tengingu lokað
+Comment[it]=Connessione chiusa
+Comment[ja]=接続ãŒé–‰ã˜ã‚‰ã‚Œã¾ã—ãŸ
+Comment[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ დáƒáƒ˜áƒ®áƒ£áƒ áƒ
+Comment[kk]=ҚоÑылым жабылды
+Comment[km]=បាន​បិទ​ការ​ážâ€‹áž—្ជាប់
+Comment[lt]=Ryšys baigtas
+Comment[mk]=Поврзувањето е затворено
+Comment[mn]=Холболт хаагдав
+Comment[ms]=Sambungan ditutup
+Comment[mt]=Konnessjoni magħluqa
+Comment[nb]=Oppkobling stengt
+Comment[nds]=Afkoppelt
+Comment[ne]=जडान बनà¥à¤¦ भयो
+Comment[nl]=Verbinding verbroken
+Comment[nn]=Samband stengt
+Comment[nso]=Kopantsho e tswaletswe
+Comment[pa]=ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਬੰਦ ਕੀਤਾ
+Comment[pl]=Połączenie zakończone
+Comment[pt]=Ligação fechada
+Comment[pt_BR]=conexão encerrada
+Comment[ro]=Conexiune închisă
+Comment[ru]=Соединение закрыто
+Comment[se]=Oktavuohta giddejuvvui
+Comment[sk]=Spojenie ukonÄené
+Comment[sl]=Povezava zaprta
+Comment[sr]=Веза је затворена
+Comment[sr@Latn]=Veza je zatvorena
+Comment[sv]=Anslutning stängd
+Comment[ta]=இணைபà¯à®ªà¯à®•à®³à¯ மூடபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=ПайваÑтшавӣ пӯшида аÑÑ‚
+Comment[th]=à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸¢à¸¸à¸•à¸´
+Comment[tr]=Bağlantı kesildi
+Comment[uk]=З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¾
+Comment[uz]=Aloqa uzildi
+Comment[uz@cyrillic]=Ðлоқа узилди
+Comment[ven]=Vhukwamani ho valwa
+Comment[xh]=Uxhulumaniso luvaliwe
+Comment[zh_CN]=连接关闭
+Comment[zh_HK]=連線已關閉
+Comment[zh_TW]=連線已關閉
+Comment[zu]=Ukuxhumana kuvaliwe
+default_presentation=4
+
+[InvalidPassword]
+Name=InvalidPassword
+Name[ar]=كلمة مرور غير صالحة
+Name[bg]=Ðевалидна парола
+Name[bn]=অবৈধ পাসওয়ারà§à¦¡
+Name[br]=N'eo ket mat an tremenger
+Name[ca]=Contrasenya no vàlida
+Name[cs]=Neplatné heslo
+Name[cy]=CyfrinairAnnilys
+Name[da]=UgyldigtKodeord
+Name[de]=UngültigesPasswort
+Name[el]=Μη έγκυÏος κωδικός Ï€Ïόσβασης
+Name[eo]=NevalidaPasvorto
+Name[es]=Contraseña errónea
+Name[et]=Vale parool
+Name[eu]=Baliogabeko pasahitza
+Name[fa]=اسم‌ رمز نامعتبر
+Name[fr]=Mots de passe non valable
+Name[ga]=FocalFaireNeamhbhailí
+Name[gl]=Contraseña inválida
+Name[he]=סיסמה שגויה
+Name[hi]=अवैध-पासवरà¥à¤¡
+Name[hr]=NevažećaLozinka
+Name[hu]=ÉrvénytelenJelszó
+Name[is]=ÓgiltLykilorð
+Name[it]=Password non valida
+Name[ja]=ä¸æ­£ãªãƒ‘スワード
+Name[kk]=ЖарамÑыз пароль
+Name[km]=ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ
+Name[lt]=Neteisingas slaptažodžis
+Name[mk]=Ðевалидна лозинка
+Name[mn]=Буруу нууц үг
+Name[ms]=Kata Laluan Tidak Sah
+Name[mt]=PasswordĦażin
+Name[nb]=Ugyldig passord
+Name[nds]=LeegPasswoort
+Name[ne]=अवैध पासवरà¥à¤¡
+Name[nl]=Ongeldig wachtwoord
+Name[nn]=Ugyldig passord
+Name[nso]=Lentsuphetiso gase la Nnete
+Name[pa]=ਗਲਤ ਗà©à¨ªà¨¤-ਕੋਡ
+Name[pl]=Błędne hasło
+Name[pt_BR]=Senha inválida
+Name[ro]=Parolă eronată
+Name[ru]=Ðеверный пароль
+Name[se]=Gustomeahttun beassansátni
+Name[sk]=Zlé heslo
+Name[sl]=Neveljavno geslo
+Name[sr]=Погрешна лозинка
+Name[sr@Latn]=Pogrešna lozinka
+Name[sv]=Ogiltigt lösenord
+Name[ta]=செலà¯à®²à®¾à®¤ கடவà¯à®šà¯à®šà¯Šà®²à¯
+Name[tg]=Гузарвожаи ÐодуруÑÑ‚
+Name[th]=รหัสผ่านไม่ถูà¸à¸•à¹‰à¸­à¸‡
+Name[tr]=Geçersiz Parola
+Name[uk]=ÐеправильнийПароль
+Name[ven]=Phasiwede asi yone
+Name[xh]=Igama lokugqitha Elingasebenziyo
+Name[zh_CN]=无效密ç 
+Name[zh_HK]=無效的密碼
+Name[zh_TW]=無效的密碼
+Name[zu]=IgamaEliyimfihloLokudlulaOkungasiyilona
+Comment=Invalid password
+Comment[af]=Ongeldige wagwoord
+Comment[ar]=كلمة مرور غير صالحة
+Comment[be]=ÐÑправільны пароль
+Comment[bg]=Ðевалидна парола
+Comment[bn]=অবৈধ পাসওয়ারà§à¦¡
+Comment[br]=Tremenger siek
+Comment[bs]=Neispravna Å¡ifra
+Comment[ca]=Contrasenya no vàlida
+Comment[cs]=Neplatné heslo
+Comment[cy]=Cyfrinair annilys
+Comment[da]=Ugyldigt kodeord
+Comment[de]=Ungültiges Passwort
+Comment[el]=Μη έγκυÏος κωδικός Ï€Ïόσβασης
+Comment[eo]=nevalida pasvorto
+Comment[es]=Contraseña errónea
+Comment[et]=Vale parool
+Comment[eu]=Baliogabeko pasahitza
+Comment[fa]=اسم‌ رمز نامعتبر
+Comment[fi]=Virheellinen salasana
+Comment[fr]=Mot de passe non valable
+Comment[ga]=Focal faire neamhbhailí
+Comment[gl]=Contraseña errónea
+Comment[he]=הסיסמה שגויה
+Comment[hi]=अवैध पासवरà¥à¤¡
+Comment[hr]=Nevažeća šifra
+Comment[hu]=Érvénytelen jelszó
+Comment[is]=Lykilorð ógilt
+Comment[it]=Password non valida
+Comment[ja]=ä¸æ­£ãªãƒ‘スワード
+Comment[ka]=áƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ პáƒáƒ áƒáƒšáƒ˜
+Comment[kk]=ЖарамÑыз пароль
+Comment[km]=ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ
+Comment[lt]=Neteisingas slaptažodis
+Comment[mk]=Ðевалидна лозинка
+Comment[mn]=Буруу нууц үг
+Comment[ms]=Kata laluan tidak sah
+Comment[mt]=Password ħażin
+Comment[nb]=Ugyldig passord
+Comment[nds]=Leeg Passwoort
+Comment[ne]=अवैध पासवरà¥à¤¡
+Comment[nl]=Ongeldig wachtwoord
+Comment[nn]=Ugyldig passord
+Comment[nso]=Lentsuphetiso gase la nnete
+Comment[pa]=ਗਲਤ ਗà©à¨ªà¨¤-ਕੋਡ
+Comment[pl]=Błędne hasło
+Comment[pt]=Senha inválida
+Comment[pt_BR]=senha inválida
+Comment[ro]=Parolă eronată
+Comment[ru]=Ðеверный пароль
+Comment[se]=Gustomeahttun beassansátni
+Comment[sk]=Zlé heslo
+Comment[sl]=Neveljavno geslo
+Comment[sr]=Погрешна лозинка
+Comment[sr@Latn]=Pogrešna lozinka
+Comment[sv]=Ogiltigt lösenord
+Comment[ta]=செலà¯à®²à®¾à®¤ கடவà¯à®šà¯à®šà¯Šà®²à¯
+Comment[tg]=Гузарвожаи нодуруÑÑ‚
+Comment[th]=รหัสผ่านไม่ถูà¸à¸•à¹‰à¸­à¸‡
+Comment[tr]=Geçersiz parola
+Comment[uk]=Ðевірний пароль
+Comment[uz]=Maxfiy soʻz haqiqiy emas
+Comment[uz@cyrillic]=Махфий Ñўз ҳақиқий ÑмаÑ
+Comment[ven]=Phasiwede isa shumi
+Comment[wa]=Sicret nén valide
+Comment[xh]=Igama lokugqitha elingasebenziyo
+Comment[zh_CN]=无效密ç 
+Comment[zh_HK]=無效的密碼
+Comment[zh_TW]=無效的密碼
+Comment[zu]=Igama elifihlikeli
+default_presentation=4
+
+[InvalidPasswordInvitations]
+Name=InvalidPasswordInvitations
+Name[ar]=دعوات كلمات مرور غير صالحة
+Name[bg]=Получи Ñе покана Ñ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ð° парола
+Name[bn]=অবৈধ পাসওয়ারà§à¦¡ আমনà§à¦¤à§à¦°à¦£
+Name[ca]=Contrasenya convidats no vàlides
+Name[cs]=Neplatné hesla výzev
+Name[cy]=GwahoddiadauCyfrinairAnnilys
+Name[da]=UgyldigtKodeordInvitationer
+Name[de]=UngültigePasswortAnfragen
+Name[el]=Μη έγκυÏες Ï€Ïοσκλήσεις ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης
+Name[eo]=NevalidaPasvortoInvito
+Name[es]=Invitación de contraseñas erróneas
+Name[et]=Vale parool kutsed
+Name[eu]=Baliogabeko pasahitz gonbidapena
+Name[fa]=دعوتهای اسم‌ رمز نامعتبر
+Name[fr]=Invitations de mot de passe non valable
+Name[gl]=Invitación de Contraseñas inválidas
+Name[he]=סיסמת הזמנות שגויה
+Name[hi]=अवैध-पासवरà¥à¤¡-निमंतà¥à¤°à¤£
+Name[hr]=KrivePizovniceÅ ifri
+Name[hu]=ÉrvénytelenJelszóMeghívások
+Name[is]=ÓgildLykilorðsBoð
+Name[it]=Invito password non valida
+Name[ja]=ä¸æ­£ãªãƒ‘スワードã®æ‹›å¾…
+Name[kk]=ЖарамÑыз Ñұралған пароль
+Name[km]=ពាក្យ​សម្ងាážáŸ‹â€‹áž¢áž‰áŸ’ជើញ​មិន​ážáŸ’រឹមážáŸ’រូវ
+Name[lt]=Neteisingo slaptažodžio kvietimai
+Name[mk]=Ðевалидна лозинка на поканите
+Name[mn]=Буруу нууц үгÑÑÑ€ орох
+Name[ms]=Jemputan Kata Laluan Tidak Sah
+Name[mt]=PasswordĦażinaStediniet
+Name[nb]=Ugyldig passord ved invitasjon
+Name[nds]=LeegPasswoortinladen
+Name[ne]=अवैध पासवरà¥à¤¡ निमनà¥à¤¤à¥à¤°à¤£à¤¾
+Name[nl]=Ongeldige wachtwoordaanvragen
+Name[nn]=Ugyldige passordinvitasjonar
+Name[nso]=Ditaletso tsa Mantsuphetiso tseo esego tsa Nnete
+Name[pl]=Informacja o błędnym haśle
+Name[pt_BR]=Aviso de senha inválida
+Name[ru]=Ðеверный Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ
+Name[se]=Gustomeahttun beassansátnebovdehusat
+Name[sk]=Zlé heslo pozvánky
+Name[sl]=Povabila z neveljavnimi gesli
+Name[sr]=Погрешни позиви Ñа лозинкама
+Name[sr@Latn]=Pogrešni pozivi sa lozinkama
+Name[sv]=Ogiltigt lösenord vid inbjudan
+Name[ta]=செலà¯à®²à®¾à®¤ அழைபà¯à®ªà®¿à®¤à®´à¯ கடவà¯à®šà¯à®šà¯Šà®²à¯
+Name[tg]=ДархоÑти ÐодуруÑти Гузарвожа
+Name[tr]=Geçersiz Parola İsteği
+Name[uk]=ЗапрошеннÑЗÐевірнимПаролем
+Name[ven]=Mbidzo ya phasiwede isi yone
+Name[xh]=IzimemoZegamalokugqithaEzingasebebenziyo
+Name[zh_CN]=无效密ç é‚€è¯·
+Name[zh_HK]=無效的密碼邀請函
+Name[zh_TW]=無效的密碼邀請函
+Name[zu]=IsimemoSegamaEliyimfihloLokudlulaOkungasiyilona
+Comment=The invited party sent an invalid password. Connection refused.
+Comment[af]=Die uitgenooi party gestuur 'n ongeldige wagwoord. Verbinding geweier.
+Comment[ar]=الجهة المدعوة أرسلت كلمة مرور غير صالحة. الاتصال رÙÙض
+Comment[bg]=Получи Ñе покана Ñ Ð½ÐµÐ²Ð°Ð»Ð¸Ð´Ð½Ð° парола
+Comment[bn]=আমনà§à¦¤à§à¦°à¦¿à¦¤ দল à¦à¦•à¦Ÿà¦¿ অবৈধ পাসওয়ারà§à¦¡ পাঠাল। সংযোগ অসà§à¦¬à§€à¦•à¦¾à¦° করা হল।
+Comment[bs]=Pozvana strana je poslala neispravnu Å¡ifru. Konekcija je odbijena.
+Comment[ca]=La part invitada ha enviat una contrasenya no vàlida. Connexió refusada.
+Comment[cs]=Pozvaná strana poslala neplatné heslo. Spojení odmítnuto.
+Comment[cy]=Anfonodd y person gwahodd cyfrinair annilys. Gwrthodwyd y cysylltiad.
+Comment[da]=Den inviterede part sendte et ugyldigt kodeord. Forbindelse afslået.
+Comment[de]=Die eingeladene Partei hat ein ungültiges Passwort gesendet: Verbindung abgelehnt.
+Comment[el]=Η πλευÏά που Ï€Ïοσκλήθηκε έστειλε μη έγκυÏο κωδικό Ï€Ïόσβασης. Η σÏνδεση αποÏÏίφθηκε.
+Comment[eo]=La invitita kliento sendis nevalidan pasvorton. Konekto rifuzita.
+Comment[es]=La parte invitada envió una contraseña incorrecta. Conexión rechazada.
+Comment[et]=Kutsutu saatis vigase parooli. Ãœhendusest keelduti.
+Comment[eu]=Gonbidatutako parekoak baliogabeko pasahitza bidali du. Konexioa ukatu da.
+Comment[fa]=شخص دعوت‌شده اسم‌ رمز نامعتبری را ارسال کرد. اتصال پذیرÙته نشد.
+Comment[fi]=Kutsuttu taho lähetti virheellisen salasanan. Yhteys hylättiin.
+Comment[fr]=La partie invitée a envoyé un mot de passe non valable. Connexion refusée.
+Comment[gl]=O invitado mandou unha contraseña inválida. A conexión foi rexeitada
+Comment[he]=הצד המוזמן שלח סיסמה שגויה. החיבור נדחה.
+Comment[hi]=निमंतà¥à¤°à¤¿à¤¤ पारà¥à¤Ÿà¥€ ने अवैध पासवरà¥à¤¡ भेजा. कनेकà¥à¤¶à¤¨ असà¥à¤µà¥€à¤•à¥ƒà¤¤.
+Comment[hr]=Stranka koju ste pozvali je poslala nevažeću šifru. Veza odbijena.
+Comment[hu]=A meghívott fél érvénytelen jelszót küldött. A csatlakozás nem sikerült.
+Comment[is]=Boðinn aðili sendi ógilt lykilorð. Tengingu hafnað
+Comment[it]=La parte invitata ha inviato una password non valida. Connessione rifiutata.
+Comment[ja]=招待ã•ã‚ŒãŸäººãŒä¸æ­£ãªãƒ‘スワードをé€ã£ã¦ãã¾ã—ãŸã€‚接続を拒å¦ã—ã¾ã—ãŸã€‚
+Comment[ka]=დáƒáƒžáƒáƒ¢áƒ˜áƒŸáƒ”ბულმრáƒáƒ áƒáƒ¡áƒ¬áƒáƒ áƒ˜ პáƒáƒ áƒáƒšáƒ˜ გáƒáƒ›áƒáƒáƒ’ზáƒáƒ•áƒœáƒ.
+Comment[kk]=Кірмек тұлға жарамÑыз парольді келтірді. ҚоÑылым болмады.
+Comment[km]=ភាគី​ដែល​បាន​អញ្ជើញ បាន​ផ្ញើ​ពាក្យ​សម្ងាážáŸ‹â€‹áž˜áž·áž“​ážáŸ’រឹមážáŸ’រូវ ។ ការ​ážáž—្ជាប់​ážáŸ’រូវ​បាន​បដិសáŸáž’ ។
+Comment[lt]=Pakviestoji pusė atsiuntė neteisingą slaptažodį. Ryšys nutrauktas.
+Comment[mk]=Поканетата Ñтрана иÑпрати невалидна лозинка. Поврзувањето е одбиено.
+Comment[mn]=Буруу нууц үгийг уригдÑан Ñ…ÑÑÑг илгÑÑв. Холболт зөвшөөрөгдÑөнгүй
+Comment[ms]=Pihak yang dijemput telah menghantar kata laluan yang salah. Sambungan ditolak.
+Comment[mt]=Il-persuna mistiedna bagħtet password ħażin. Konnessjoni miċħuda.
+Comment[nb]=Den inviterte parten sendte ugyldig passord. Oppkobling avvist.
+Comment[nds]=De inlaadt Deel hett en leeg Passwoort sendt. Verbinnen torüchwiest.
+Comment[ne]=निमनà¥à¤¤à¥à¤°à¤£à¤¾ गरेको पारà¥à¤Ÿà¥€à¤²à¥‡ à¤à¤‰à¤Ÿà¤¾ अवैध पासवरà¥à¤¡ पठायो । जडान असà¥à¤µà¥€à¤•à¤¾à¤° गरियो ।
+Comment[nl]=De uitgenodigde partij stuurde een ongeldig wachtwoord. Verbinding geweigerd.
+Comment[nn]=Den inviterte parten sende eit ugyldig passord. Sambandet vart nekta.
+Comment[nso]=Sehlopha seo se memilwego se romela lentsuphetiso leo esego la nnete. Kgokagano e gannwe.
+Comment[pl]=Z drugiej strony podano błędne hasło. Połączenie odrzucone.
+Comment[pt]=O convidado enviou uma senha inválida. A ligação foi recusada.
+Comment[pt_BR]=A parte "convidada" enviou uma senha inválida. Conexão recusada.
+Comment[ru]=Удалённый пользователь ввёл неверный пароль. Ð’ доÑтупе отказано.
+Comment[se]=Bovdejuvvon bealli sáddii gustomeahttun beassansáni. Oktavuohta hilgojuvui.
+Comment[sk]=Pozvaný úÄastnik poslal zlé heslo. Spojenie zamietnuté.
+Comment[sl]=Povabljena stranka je poslala neveljavno geslo. Povezava zavrnjena.
+Comment[sr]=Позвана Ñтранка је поÑлала погрешну лозинку. Веза је одбијена.
+Comment[sr@Latn]=Pozvana stranka je poslala pogrešnu lozinku. Veza je odbijena.
+Comment[sv]=Den inbjudna personen skickade ett ogiltigt lösenord. Anslutning vägrades.
+Comment[ta]=அழைதà¯à®¤ நபர௠தவறான கடவà¯à®šà¯à®šà¯Šà®²à¯à®²à¯ˆ அனà¯à®ªà¯à®ªà®¿à®¯à¯à®³à¯à®³à®¾à®°à¯. இணைபà¯à®ªà¯ நிராகரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯.
+Comment[tg]=Корванди дурдаÑÑ‚ гузарвожаи нодуруÑтро фириÑтод. ПайваÑтшавӣ манъ шудааÑÑ‚.
+Comment[th]=ผู้เข้าร่วมà¸à¸²à¸£à¹€à¸Šà¸´à¸à¸Šà¸§à¸™à¸ªà¹ˆà¸‡à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸¡à¸²à¹„ม่ถูà¸à¸•à¹‰à¸­à¸‡ ทำà¸à¸²à¸£à¸›à¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Comment[tr]=Davet edilenden gönderilmiş geçersiz parola. Bağlantı rededildi.
+Comment[uk]=Запрошена Ñторона надіÑлала невірний пароль. У з'єднанні відмовлено.
+Comment[ven]=Murado o rambiwaho o rumela phasiwede isa shumi. Vhukwamani ho hanwa.
+Comment[xh]=Umhlangano omenyiweyo uthumele igama lokugqitha elisebenzayo. Uxhulumano lwa liwe.
+Comment[zh_CN]=å—邀请方å‘é€çš„密ç ä¸å¯¹ã€‚连接被拒ç»ã€‚
+Comment[zh_HK]=被邀請的一方é€å‡ºç„¡æ•ˆçš„密碼。已拒絕連線。
+Comment[zh_TW]=被邀請的一方é€å‡ºç„¡æ•ˆçš„密碼。已拒絕連線。
+Comment[zu]=Ingxenye emenyiwe ithumele igama lokungena alivunyelwanga. Ukuxhumana kwaliwe.
+default_presentation=2
+
+[NewConnectionOnHold]
+Name=NewConnectionOnHold
+Name[ar]=اتصال جديد على الانتظار
+Name[bg]=Получи Ñе нова заÑвка за връзка
+Name[bn]=নতà§à¦¨ সংযোগ ধরে রাখা
+Name[ca]=Nova connexió en espera
+Name[cs]=Nové spojení pozdrženo
+Name[cy]=CysylltiadNewyddArArfael
+Name[da]=NyForbindelseSatPÃ¥Hold
+Name[de]=NeueVerbindungWartet
+Name[el]=Îέα σÏνδεση σε αναμονή
+Name[eo]=NovaKonektoAtendante
+Name[es]=Nueva conexión en espera
+Name[et]=Uus ühendus ootel
+Name[eu]=Konexio berria itxarote moduan
+Name[fa]=اتصال جدید نگه‌داشته
+Name[fr]=Nouvelle connexion au raccroché
+Name[ga]=CeangalNuaAgFanacht
+Name[gl]=Conexión en espera
+Name[he]=חיבור חדש בהמתנה
+Name[hi]=नया-कनेकà¥à¤¶à¤¨-आन-होलà¥à¤¡
+Name[hr]=NovaVezaNaÄŒekanju
+Name[hu]=ÚjKapcsolatTartva
+Name[is]=NýTengingÃBið
+Name[it]=Nuova connessione da tenere
+Name[ja]=ä¿ç•™ä¸­ã®æ–°è¦æŽ¥ç¶š
+Name[kk]=Жаңа қоÑылым талап етілуде
+Name[km]=ការ​ážâ€‹áž—្ជាប់​ážáŸ’មី កំពុង​ស្ážáž·ážâ€‹áž“ៅ​ក្នុង​ការ​រង់​ចាំ
+Name[lt]=Naujas kvietimas ryšiui sulaikytas
+Name[mk]=Ðова врÑка на чекање
+Name[mn]=Ð¨Ð¸Ð½Ñ Ñ…Ð¾Ð»Ð±Ð¾Ð»Ñ‚ тогтоов
+Name[ms]=Sambungan Baru Menunggu
+Name[mt]=KonnessjoniĠdidaMiżmuma
+Name[nb]=Ny oppkobling venter
+Name[nds]=NiegVerbinnenTöövt
+Name[ne]=समातिà¤à¤•à¥‹ नयाठजडान
+Name[nl]=Nieuwe verbinding is wachtende
+Name[nn]=Nytt samband ventar
+Name[nso]=Kgokagano ye Ntshwa e Emisitswe
+Name[pl]=Nowe połączenie wstrzymane
+Name[pt_BR]=Nova Conexão
+Name[ro]=Conexiune nouă în aşteptare
+Name[ru]=Ðовое Ñоединение отключено
+Name[se]=OÄ‘Ä‘a oktavuohta vuordimin
+Name[sk]=Nové spojenie podržané
+Name[sl]=Nova povezava na Äakanju
+Name[sr]=Ðова веза је на чекању
+Name[sr@Latn]=Nova veza je na Äekanju
+Name[sv]=Ny anslutning väntar
+Name[ta]=பà¯à®¤à®¿à®¯ இணைபà¯à®ªà¯ வைகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Name[tg]=ПайваÑтшавии Ðав Ðигоҳ дошта мешавад
+Name[tr]=Açık Yeni Bağlantı
+Name[uk]=ОчікуютьÐовіЗ'єднаннÑ
+Name[ven]=Vhukwamani vhuswa ho imiswa
+Name[xh]=Uxhulumano Olutsha Lumisiwe
+Name[zh_CN]=新连接暂时æç½®
+Name[zh_HK]=ä¿æŒçš„新連線
+Name[zh_TW]=ä¿æŒçš„新連線
+Name[zu]=UkuxhumanisaOkushaKubanjiwe
+Comment=Connection requested, user must accept
+Comment[af]=Verbinding versoekte, gebruiker moet aanvaar
+Comment[ar]=طلب اتصال, يجب أن يقبل المستخدم
+Comment[be]=Запыт злучÑннÑ, неабходнае пацвÑрджÑнне
+Comment[bg]=Получи Ñе нова заÑвка за връзка
+Comment[bn]=সংযোগ অনà§à¦°à§‹à¦§ করা হল, বà§à¦¯à¦¬à¦¹à¦¾à¦°à¦•à¦¾à¦°à§€à¦•à§‡ অবশà§à¦¯à¦‡ সà§à¦¬à§€à¦•à¦¾à¦° করতে হবে
+Comment[bs]=Zatražena konekcija, korisnik mora prihvatiti
+Comment[ca]=Connexió sol·licitada, l'usuari ha d'acceptar-la
+Comment[cs]=Vyžadováno spojení, uživatel musí přijmout
+Comment[cy]=Cais wedi'i wneud am gysylltiad,rhaid i'r ddefnyddiwr ei dderbyn
+Comment[da]=Forbindelse forespurgt, bruger skal acceptere
+Comment[de]=Verbindungsanfrage, Benutzer muss bestätigen
+Comment[el]=Αιτήθηκε σÏνδεση, ο χÏήστης Ï€Ïέπει να αποδεχθεί
+Comment[eo]=Konekto pridemandita, uzanto devas akcepti
+Comment[es]=Conexión solicitada, el usuario debe aceptar
+Comment[et]=Nõutakse ühendust, kasutaja peab seda lubama
+Comment[eu]=Konexioa eskatu da, erabiltzaileak onartu behar du
+Comment[fa]=اتصال درخواست شد، کاربر باید بپذیرد
+Comment[fi]=Pyydettiin yhteyttä, käyttäjän tulee hyväksyä
+Comment[fr]=Connexion demandée, l'utilisateur doit accepter
+Comment[ga]=Ceangal iarrtha; ní mór leis an úsáideoir glacadh leis
+Comment[gl]=Petición de conexión en curo. O usuario ten que aceptar
+Comment[he]=נתבקש חיבור, על המשתמש לקבלו
+Comment[hi]=कनेकà¥à¤¶à¤¨ निवेदित. उपयोकà¥à¤¤à¤¾ को सà¥à¤µà¥€à¤•à¤¾à¤° होना चाहिà¤
+Comment[hr]=Veza je zatražena, korisnik mora prihvatiti
+Comment[hu]=Csatlakozási kérés, megerősítés szükséges
+Comment[is]=Beiðni um tengingu, notandi verður að samþykkja
+Comment[it]=Connessione richiesta, l'utente deve accettare
+Comment[ja]=接続ãŒè¦æ±‚ã•ã‚Œã¦ã„ã¾ã™ã€‚ユーザãŒè¨±å¯ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。
+Comment[ka]=კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ მáƒáƒ—ხáƒáƒ•áƒœáƒ, მáƒáƒ›áƒ®áƒ›áƒáƒ áƒ”ბელმრუნდრდáƒáƒáƒ“áƒáƒ¡áƒ¢áƒ£áƒ áƒáƒ¡
+Comment[kk]=ҚоÑылым талабы келді, пайдаланушы қабылдау керек
+Comment[km]=បាន​ស្នើ​ការ​ážâ€‹áž—្ជាប់​,​ អ្នក​ប្រើ​ážáŸ’រូវ​ážáŸ‚​ទទួលយក
+Comment[lt]=Kvietimas ryšiui išsiųstas, naudotojas turi priimti kvietimą
+Comment[mk]=Побарано е поврзување, кориÑникот мора да прифати
+Comment[mn]=Холболт Ñ…Ò¯ÑÑж байна, Ñ…ÑÑ€ÑглÑгч зөвшөөрөх Ñ‘Ñтой
+Comment[ms]=Sambungan diminta, pengguna mesti menerima
+Comment[mt]=Konnessjoni mitluba, user irid jaċċetta
+Comment[nb]=Oppkobling ønskes, bruker må akseptere
+Comment[nds]=Tokoppelanfraag, Nafraag bi Bruker
+Comment[ne]=जडान अनà¥à¤°à¥‹à¤§ गरियो, पà¥à¤°à¤¯à¥‹à¤—करà¥à¤¤à¤¾à¤²à¥‡ सà¥à¤µà¥€à¤•à¤¾à¤° गरà¥à¤¨à¥à¤ªà¤°à¥à¤›
+Comment[nl]=Verbinding verzocht, gebruiker dient te accepteren
+Comment[nn]=Samband førespurd, brukar må godta
+Comment[nso]=Kgokagano e kgopetswe, modirisi o swanetse go dumela
+Comment[pl]=Próba połączenia, musi być zaakceptowana przez użytkownika
+Comment[pt]=A ligação foi pedida e o utilizador deve aceitar
+Comment[pt_BR]=Conexão requisitada; o usuário deve aceitar
+Comment[ro]=Cerere de conectare; utilizatorul trebuie să accepte
+Comment[ru]=Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° Ñоединение, требуетÑÑ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ðµ пользователÑ
+Comment[se]=Oktavuohta jearahuvui, geavaheaddji ferte dohkkehit
+Comment[sk]=Vyžiadané spojenie, užívateľ musí akceptovať
+Comment[sl]=Povezava zahtevana, uporabnik mora sprejeti
+Comment[sr]=Захтевана је веза, кориÑник мора да је прихвати
+Comment[sr@Latn]=Zahtevana je veza, korisnik mora da je prihvati
+Comment[sv]=Anslutning begärd, användaren måste acceptera
+Comment[ta]=இணைபà¯à®ªà¯ கோரபà¯à®ªà®Ÿà¯à®Ÿà®¤à¯, பயனர௠கணà¯à®Ÿà®¿à®ªà¯à®ªà®¾à®• à®à®±à¯à®±à¯à®•à¯à®•à¯Šà®³à¯à®³ வேணà¯à®Ÿà¯à®®à¯
+Comment[tg]=ПайваÑтшавӣ дархоÑта шудааÑÑ‚, корванд боÑд қабул кунад
+Comment[th]=มีà¸à¸²à¸£à¸£à¹‰à¸­à¸‡à¸‚อเชื่อมต่อ ผู้ใช้ต้องทำà¸à¸²à¸£à¸¢à¸­à¸¡à¸£à¸±à¸š
+Comment[tr]=Bağlantı isteği, kullanıcı kabul etmeli
+Comment[uk]=Запрошено з'єднаннÑ, кориÑтувач має прийнÑти
+Comment[ven]=Vhukwamani ho humbelwa, mushumisi u fanela u tanganedza
+Comment[xh]=Uxhulumaniso luceliwe, umsebenzisi kufanele amkele
+Comment[zh_CN]=连接已请求,用户必须接å—
+Comment[zh_HK]=已請求連線,用戶必須接å—
+Comment[zh_TW]=已請求連線,使用者必須接å—
+Comment[zu]=Ukuxhumanisa kuceliwe, umsebenzi kumele ivunyelwe
+default_presentation=4
+
+[NewConnectionAutoAccepted]
+Name=NewConnectionAutoAccepted
+Name[ar]=اتصال جديد مقبول تلقائياً
+Name[bg]=Ðова връзка е уÑтановена автоматично
+Name[bn]=নতà§à¦¨ সংযোগ সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ সà§à¦¬à§€à¦•à§ƒà¦¤
+Name[ca]=Nova connexió auto-acceptada
+Name[cs]=Nové spojení automaticky přijato
+Name[cy]=CysylltiadNewyddAwtoDerbyn
+Name[da]=NyForbindelseAutomatiskAccepteret
+Name[de]=NeueVerbindungAutomatischAkzeptiert
+Name[el]=Αυτόματη αποδοχή νέας σÏνδεσης
+Name[eo]=NovaKonektoAÅ­tomateAkceptita
+Name[es]=Nueva conexión auto aceptada
+Name[et]=Uue ühendusega automaatselt nõus
+Name[eu]=Konexio berria auto onartu da
+Name[fa]=پذیرش خودکار اتصال جدید
+Name[fr]=Nouvelle connexion auto-acceptée
+Name[gl]=Nova conexión aceptada automáticamente
+Name[he]=חיבור חדש נתקבל ×וטומטית
+Name[hi]=नया-कनेकà¥à¤¶à¤¨-सà¥à¤µà¤šà¤²à¤¿à¤¤-सà¥à¤µà¥€à¤•à¤¾à¤°à¤¾
+Name[hr]=NovaVezaAutoPrihvaćena
+Name[hu]=ÚjKapcsolatAutoElfogadva
+Name[is]=NýTengingSjálfvirktSamþykkt
+Name[it]=Accettata nuova connessione automatica
+Name[ja]=æ–°è¦æŽ¥ç¶šã®è‡ªå‹•å—ã‘入れ
+Name[kk]=Жаңа қоÑылым авто қабылданды
+Name[km]=បាន​ទទួល​យក​ការ​ážâ€‹áž—្ជាប់​ážáŸ’មី​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž·
+Name[lt]=Naujas kvietimas ryšiui automatiškai priimtas
+Name[mk]=Ðово поврзување автоматÑки прифатено
+Name[mn]=Ð¨Ð¸Ð½Ñ Ñ…Ð¾Ð»Ð±Ð¾Ð»Ñ‚Ñ‹Ð³ автоматаар зөвшөөрөв
+Name[ms]=Sambungan Baru Diterima Auto
+Name[mt]=KonnessjoniÄ didaAwtoAÄ‹Ä‹ettata
+Name[nb]=Ny oppkobling tas imot automatisk
+Name[nds]=NiegVerbinnenAutomaatschTolaten
+Name[ne]=नयाठजडान सà¥à¤µà¤¤: सà¥à¤µà¥€à¤•à¤¾à¤° गरियो
+Name[nl]=Nieuwe verbinding automatisch geaccepteerd
+Name[nn]=Nytt samband automatisk godteke
+Name[nso]=Kgokagano ye Ntshwa yago Itirisa e Amogetswe
+Name[pl]=Nowe połączenie automatycznie przyjęte
+Name[pt_BR]=Nova Conexão com aceitação automática
+Name[ro]=Conexiune nouă acceptată automat
+Name[ru]=Соединение ÑоздаётÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки
+Name[se]=OÄ‘Ä‘a oktavuohta dohkkehuvui automáhtalaÄÄat
+Name[sk]=Nové spojenie automaticky akceptované
+Name[sl]=Nova povezava samodejno sprejeta
+Name[sr]=Ðова веза је аутоматÑки прихваћена
+Name[sr@Latn]=Nova veza je automatski prihvaćena
+Name[sv]=Ny anslutning accepterades automatiskt
+Name[ta]=பà¯à®¤à®¿à®¯ இணைபà¯à®ªà¯à®•à¯à®•à®³à¯ தானாக à®à®±à¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Name[tg]=ПайваÑтшавии Ðав ба таври Худкор Пазируфта мешавад
+Name[th]=รับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸­à¸±à¸•à¹‚นมัติ
+Name[tr]=Otomatik Kabul Edilen Yeni Bağlantı
+Name[uk]=ÐовіЗ'єднаннÑÐвтоматичноПрийнÑÑ‚Ñ–
+Name[ven]=Vhukwamani vhuswa ho tanganedzhwa
+Name[xh]=Uxhulumano Olutsha Lwamkelwe Ngokuzenzekelayo
+Name[zh_CN]=新连接自动接å—
+Name[zh_HK]=自動接å—的新連線
+Name[zh_TW]=自動接å—的新連線
+Name[zu]=UkuxhumanisaOkushaKokuzenzakalelaKuvunyelwe
+Comment=New connection automatically established
+Comment[af]=Nuwe verbinding automaties vasgestel
+Comment[ar]=اتصالات جديدة Ø£Ùنشئت تلقائياً
+Comment[be]=Ðовае злучÑнне аўтаматычна ÑžÑтаноўленае
+Comment[bg]=Ðова връзка е уÑтановена автоматично
+Comment[bn]=নতà§à¦¨ সংযোগ সà§à¦¬à§Ÿà¦‚কà§à¦°à§€à§Ÿà¦­à¦¾à¦¬à§‡ সà§à¦¥à¦¾à¦ªà¦¨ করা হল
+Comment[bs]=Automatski uspostavljena nova konekcija
+Comment[ca]=Nova connexió establerta automàticament
+Comment[cs]=Automaticky navázáno nové spojení
+Comment[cy]=Sefydlwyd cysylltiad newydd yn awtomatig
+Comment[da]=Ny forbindelse automatisk etableret
+Comment[de]=Neue Verbindung automatisch hergestellt
+Comment[el]=Îέα σÏνδεση αυτόματα αποκαταστάθηκε
+Comment[eo]=Nova konekto aÅ­tomate akceptita
+Comment[es]=Nueva conexión establecida automáticamente
+Comment[et]=Uus ühendus automaatselt loodud
+Comment[eu]=Konexio berria automatikoki ezarri da
+Comment[fa]=اتصال جدید به طور خودکار برقرار شد
+Comment[fi]=Uusi yhteys muodostettu automaattisesti
+Comment[fr]=Nouvelle connexion établie automatiquement
+Comment[ga]=Ceangal nua bunaithe go huathoibríoch
+Comment[gl]=Nova conexión automáticamente establecida
+Comment[he]=נוצר חיבור חדש ב×ופן ×וטומטי
+Comment[hi]=नया कनेकà¥à¤¶à¤¨ सà¥à¤µà¤šà¤²à¤¿à¤¤ सà¥à¤¥à¤¾à¤ªà¤¿à¤¤
+Comment[hr]=Nova veza automatski prihvaćena
+Comment[hu]=Automatikusan létrejött az új kapcsolat
+Comment[is]=Nýjar tengingar sjálfkrafa samþykktar
+Comment[it]=Stabilita nuova connessione automaticamente
+Comment[ja]=æ–°è¦æŽ¥ç¶šã‚’自動的ã«ç¢ºç«‹ã—ã¾ã—ãŸ
+Comment[ka]=áƒáƒ®áƒáƒšáƒ˜ კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ áƒáƒ•áƒ¢áƒáƒ›áƒáƒ¢áƒ£áƒ áƒáƒ“ დáƒáƒ›áƒ§áƒáƒ áƒ“áƒ
+Comment[kk]=Жаңа қоÑылым автоматты түрде орнатылды
+Comment[km]=បាន​បង្កើážâ€‹áž€áž¶ážšâ€‹ážâ€‹áž—្ជាប់​ážáŸ’មី​ដោយ​ស្វáŸáž™â€‹áž”្រវážáŸ’ážáž·
+Comment[lt]=Naujas ryšys užmegztas automatiškai
+Comment[mk]=ÐвтоматÑки е воÑпоÑтавено ново поврзување
+Comment[mn]=Ð¨Ð¸Ð½Ñ Ñ…Ð¾Ð»Ð±Ð¾Ð»Ñ‚ автоматаар тавигдав
+Comment[ms]=Sambungan baru secara automatik terjalin
+Comment[mt]=Konnessjoni ġdida aċċettata awtomatikament
+Comment[nb]=Ny oppkobling automatisk opprettet
+Comment[nds]=Nieg Verbinnen automaatsch inricht
+Comment[ne]=नयाठजडान सà¥à¤µà¤šà¤¾à¤²à¤¿à¤¤ रूपमा सà¥à¤¥à¤¾à¤ªà¤¿à¤¤ भयो
+Comment[nl]=Nieuwe verbinding automatisch opgebouwd
+Comment[nn]=Nytt samband automatisk oppretta
+Comment[nso]=Kgokagano ye ntshwa e hlagisitswe kago itirsa
+Comment[pl]=Nowe połączenie ustanowiono automatycznie
+Comment[pt]=A nova ligação foi estabelecida automaticamente
+Comment[pt_BR]=Nova conexão estabelecida automaticamente
+Comment[ro]=Conexiune nouă stabilită automat
+Comment[ru]=Ðовое Ñоединение уÑтанавливаетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки
+Comment[se]=OÄ‘Ä‘a oktavuohta automáhtalaÄÄat váldui
+Comment[sk]=Nové spojenie automaticky vytvorené
+Comment[sl]=Nova povezava samodejno vzpostavljena
+Comment[sr]=Ðова веза је аутоматÑки уÑпоÑтављена
+Comment[sr@Latn]=Nova veza je automatski uspostavljena
+Comment[sv]=Ny anslutning automatiskt upprättad
+Comment[ta]=இணைபà¯à®ªà¯à®•à®³à¯ தானாக உரà¯à®µà®¾à®•à¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=ПайваÑтшавии нав ба таври худкор барпо мегардад
+Comment[th]=เปิดà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹ƒà¸«à¸¡à¹ˆà¸­à¸±à¸•à¹‚นมัติ
+Comment[tr]=Yeni bağlantı otomatik olarak kuruldu
+Comment[uk]=Ðвтоматично вÑтановлено нове з'єднаннÑ
+Comment[ven]=Vhukwamani vhuswa ho itwa na zwenezwo
+Comment[xh]=Uxhulumaniso olutsha lufunyenwe ngokuzenzekelayo
+Comment[zh_CN]=自动建立新连接
+Comment[zh_HK]=已自動建立新連線
+Comment[zh_TW]=已自動建立新連線
+Comment[zu]=Ukuxhumana okusha kuyazisungulela
+default_presentation=4
+
+[TooManyConnections]
+Name=TooManyConnections
+Name[ar]=اتصالات عديدة جداً
+Name[bg]=Получиха Ñе твърде много връзки
+Name[bn]=অতà§à¦¯à¦¾à¦§à¦¿à¦• সংযোগ
+Name[br]=Re gevreadurioù zo
+Name[ca]=Masses connexions
+Name[cs]=Příliš mnoho spojení
+Name[cy]=GormodOGysylltiadau
+Name[da]=ForMangeForbindelser
+Name[de]=ZuVieleVerbindungen
+Name[el]=ΠάÏα πολλές συνδέσεις
+Name[eo]=TroMultajKonektoj
+Name[es]=Demasiadas conexiones
+Name[et]=Liiga palju ühendusi
+Name[eu]=Konexio gehiegi
+Name[fa]=اتصالات زیاد
+Name[fr]=Trop de connexions
+Name[ga]=AnIomarcaCeangal
+Name[gl]=Demasiadas conexións
+Name[he]=יותר מדיי חיבורי×
+Name[hi]=Tबहà¥à¤¤-सारे-कनेकà¥à¤¶à¤¨
+Name[hr]=PrevišeVeza
+Name[hu]=TúlSokKapcsolat
+Name[is]=OfMargarTengingar
+Name[it]=Troppe Connessioni
+Name[ja]=多ã™ãŽã‚‹æŽ¥ç¶š
+Name[kk]=ҚоÑылымдар тым көп
+Name[km]=ការ​ážâ€‹áž—្ជាប់​ច្រើន​ពáŸáž€
+Name[lt]=Per daug užmegztų ryšių
+Name[mk]=Премногу поврзувања
+Name[mn]=ДÑндүү олон холболт
+Name[ms]=Terlalu Banyak Sambungan
+Name[mt]=WisqKonnessjonijiet
+Name[nb]=For mange oppkoblinger
+Name[nds]=ToVeleVerbinnen
+Name[ne]=अति धेरै जडान
+Name[nl]=Teveel verbindingen
+Name[nn]=For mange samband
+Name[nso]=Dikgokagano tse Ntshi Kudu
+Name[pl]=Zbyt wiele połączeń
+Name[pt_BR]=Conexões em excesso
+Name[ro]=Prea multe conexiuni
+Name[ru]=Слишком много Ñоединений
+Name[se]=Menddo ollu oktavuođat
+Name[sk]=Príliš veľa spojení
+Name[sl]=PreveÄ povezav
+Name[sr]=ИÑувише много веза
+Name[sr@Latn]=Isuviše mnogo veza
+Name[sv]=För många anslutningar
+Name[ta]=பல இணைபà¯à®ªà¯à®•à¯à®•à®³à¯
+Name[tg]=Ðз Ҳад Зиёд ПайваÑтагиҳо
+Name[th]=มีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸¡à¸²à¸à¹€à¸à¸´à¸™à¹„ป
+Name[tr]=ÇokFazlaBağlantı
+Name[uk]=ЗабагатоЗ'єднань
+Name[ven]=Vhukwamani vhunzhi
+Name[xh]=Uxhulumaniso Oluninzi Kakhulu
+Name[zh_CN]=连接太多
+Name[zh_HK]=太多連線
+Name[zh_TW]=太多連線
+Name[zu]=UkuxhumanaOkuningi
+Comment=Busy, connection refused
+Comment[af]=Besig, verbinding geweier
+Comment[ar]=مشغول , الاتصال رÙÙض
+Comment[be]=ЗанÑта, адмоўлена злучÑнню
+Comment[bg]=Получиха Ñе твърде много връзки
+Comment[bn]=বà§à¦¯à¦¸à§à¦¤, সংযোগ অসà§à¦¬à§€à¦•à¦¾à¦° করল
+Comment[br]=Dalc'het, kevreadenn disteuleret
+Comment[bs]=Zauzet, konekcija odbijena
+Comment[ca]=Ocupat, connexió refusada
+Comment[cs]=Zaneprázdněn, spojení odmítnuto
+Comment[cy]=Prysur, gwrthodwyd y cysylltiad
+Comment[da]=Optaget, forbindelse afslået
+Comment[de]=Beschäftigt, Verbindung abgelehnt
+Comment[el]=Απασχολημένο, η σÏνδεση αποÏÏίφθηκε
+Comment[eo]=Laborante, konekto rifuzita
+Comment[es]=Ocupado, conexión rechazada
+Comment[et]=Hõivatud, ühendusest keelduti
+Comment[eu]=Lanpetuta, konexioa ukatu da
+Comment[fa]=اشغال، اتصال پذیرÙته نشد
+Comment[fi]=Varattu, yhteys hylättiin
+Comment[fr]=Occupé, connexion refusée
+Comment[ga]=Gnóthach; ceangal diúltaithe
+Comment[gl]=Ocupado, conexión rexeitada
+Comment[he]=תפוס, החיבור נדחה
+Comment[hi]=वà¥à¤¯à¤¸à¥à¤¤, कनेकà¥à¤¶à¤¨ असà¥à¤µà¥€à¤•à¥ƒà¤¤
+Comment[hr]=Zauzeto, veza odbijena
+Comment[hu]=Foglalt, a csatlakozási kérés visszautasítva
+Comment[is]=Uptekinn, tengingu hafnað
+Comment[it]=Occupato, connessione rifiutata
+Comment[ja]=ビジーã§ã™ã€æŽ¥ç¶šã‚’æ‹’å¦ã—ã¾ã—ãŸ
+Comment[ka]=დáƒáƒ™áƒáƒ•áƒ”ბული, კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ უáƒáƒ áƒ§áƒáƒ¤áƒ˜áƒšáƒ˜áƒ
+Comment[kk]=Ð‘Ð¾Ñ ÐµÐ¼ÐµÑ, қоÑылым болмады
+Comment[km]=រវល់​,​បោះបង់​ការ​ážâ€‹áž—្ជាប់
+Comment[lt]=Užimta, kvietimas ryšiui atmestas
+Comment[mk]=Зафатено, поврзувањето е одбиено
+Comment[mn]=Шугам Чөлөөгүй, холболт зөвшөөрөгдÑөнгүй
+Comment[ms]=Sibuk, sambungan ditolak
+Comment[mt]=Okkupat, konnessjoni miċħuda
+Comment[nb]=Opptatt, oppkobling avvist
+Comment[nds]=Bunnen, Verbinnen torüchwiest
+Comment[ne]=वà¥à¤¯à¤¸à¥à¤¤, जडान असà¥à¤µà¥€à¤•à¤¾à¤° गरियो
+Comment[nl]=Bezig, verbinding geweigerd
+Comment[nn]=Oppteken, samband nekta
+Comment[nso]=E swaregile, kgokagano e gannwe
+Comment[pl]=Zajęte, połączenie odrzucone
+Comment[pt]=Ocupado, a ligação foi recusada
+Comment[pt_BR]=Ocupado; conexão recusada
+Comment[ro]=Ocupat; conexiune refuzată
+Comment[ru]=ЗанÑто, Ñоединение закрыто
+Comment[sk]=Zaneprázdneny, spojenie odmietnuté
+Comment[sl]=Zaposlen, povezava zavrnjena
+Comment[sr]=Заузето, веза је одбијена
+Comment[sr@Latn]=Zauzeto, veza je odbijena
+Comment[sv]=Upptagen, anslutning vägras
+Comment[ta]=வேலையில௠உளà¯à®³à®¤à¯, இணைபà¯à®ªà¯ நிராகரிகà¯à®•à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=Банд, пайваÑтшавӣ рад гардидааÑÑ‚
+Comment[th]=ยังไม่ว่าง ทำà¸à¸²à¸£à¸›à¸à¸´à¹€à¸ªà¸˜à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­
+Comment[tr]=Meşgul, bağlantı rededildi
+Comment[uk]=ЗайнÑто, у з'єднанні відмовлено
+Comment[uz]=Band, aloqa rad etildi
+Comment[uz@cyrillic]=Банд, алоқа рад Ñтилди
+Comment[ven]=U farakanea, vhukwamani ho hanwa
+Comment[xh]=Uxhulumaniso, olu xakekileyo lwaliwe
+Comment[zh_CN]=å çº¿ï¼Œè¿žæŽ¥æ‹’ç»
+Comment[zh_HK]=忙碌,已拒絕連線
+Comment[zh_TW]=忙碌,已拒絕連線
+Comment[zu]=Imatasa,ukuxhumana kwaliwe
+default_presentation=4
+default_logfile=
+
+[UnexpectedConnection]
+Name=UnexpectedConnection
+Name[ar]=اتصال غير متوقع
+Name[bg]=Получи Ñе неочаквана връзка
+Name[bn]=অপà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ সংযোগ
+Name[ca]=Connexió inesperada
+Name[cs]=NeoÄekávané spojení
+Name[cy]=CysylltiadAnnisgwyl
+Name[da]=UventetForbindelse
+Name[de]=UnerwarteteVerbindung
+Name[el]=Μη αναμενόμενη σÏνδεση
+Name[eo]=NeatenditaKonekto
+Name[es]=Conexión inesperada
+Name[et]=Ootamatu ühendus
+Name[eu]=Ustekabeko konexioa
+Name[fa]=اتصال غیرمنتظره
+Name[fr]=Connexion inattendue
+Name[gl]=Conexión inesperada
+Name[he]=חיבור בלתי צפוי
+Name[hi]=अपà¥à¤°à¤¤à¥à¤¯à¤¾à¤¶à¤¿à¤¤-कनेकà¥à¤¶à¤¨
+Name[hr]=NeoÄekivanaVeza
+Name[hu]=NemVártKapcsolat
+Name[is]=ÓvæntTenging
+Name[it]=Connessione inaspettata
+Name[ja]=予期ã—ãªã„接続
+Name[kk]=Күтілмеген қоÑылым
+Name[km]=ការ​ážâ€‹áž—្ជាប់​ដែល​មិន​បាន​រំពឹង​ទុក
+Name[lt]=NetikÄ—tas prisijungimas
+Name[mk]=Ðеочекувано поврзување
+Name[mn]=ГÑнÑтийн Холболт
+Name[ms]=Sambungan Luar Jangka
+Name[mt]=KonnessjonijietMhuxMistennija
+Name[nb]=Uventet oppkobling
+Name[nds]=UnverwachtVerbinnen
+Name[ne]=अनपेकà¥à¤·à¤¿à¤¤ जडान
+Name[nl]=Onverwachte verbinding
+Name[nn]=Uventa samband
+Name[nso]=Kgokagano yeo ebego esa Emelwa
+Name[pl]=Niespodziewane połączenie
+Name[pt_BR]=Conexão não-aceita
+Name[ro]=Conexiune neaşteptată
+Name[ru]=Ðеожиданное Ñоединение
+Name[se]=Vuordekeahtes oktavuohta
+Name[sk]=NeoÄakávane spojenie
+Name[sl]=NepriÄakovana povezava
+Name[sr]=Ðеочекивана веза
+Name[sr@Latn]=NeoÄekivana veza
+Name[sv]=Oväntad anslutning
+Name[ta]=பயனர௠இணைபà¯à®ªà¯ à®à®±à¯à®±à¯à®•à¯à®•à¯Šà®³à¯à®³à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Name[tg]=ПайваÑтшавии Ғайричашмдошт
+Name[th]=à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ไม่คาดหวัง
+Name[tr]=Beklenmedik Bağlantı
+Name[uk]=ÐеочікуванеЗ'єднаннÑ
+Name[ven]=Vhukwamani vhu songo lavhelelwaho
+Name[xh]=Uxhulumaniso Olungalindelwanga
+Name[zh_CN]=未料到的连接
+Name[zh_HK]=éžé æœŸçš„連線
+Name[zh_TW]=éžé æœŸçš„連線
+Name[zu]=UkuxhumanaOkungalindelwe
+Comment=Received unexpected connection, abort
+Comment[af]=Ontvang onverwagte verbinding, staak
+Comment[ar]=تم استلام اتصال غير متوقع , اقطع
+Comment[be]=Ðтрыманае нечаканае злучÑнне
+Comment[bg]=Получи Ñе неочаквана връзка
+Comment[bn]=অপà§à¦°à¦¤à§à¦¯à¦¾à¦¶à¦¿à¦¤ সংযোগ গà§à¦°à¦¹à¦£ করল, বাতিল করà§à¦¨
+Comment[bs]=Primio neoÄekivanu konekciju, prekidam
+Comment[ca]=Rebuda una connexió inesperada, avortant
+Comment[cs]=Obdrženo neoÄekávané spojení, pÅ™eruÅ¡eno
+Comment[cy]=Derbynwyd cysylltiad annisgwyl,terfynu
+Comment[da]=Modtog uventet forbindelse, afbrød
+Comment[de]=Unerwartete Verbindung hergestellt, Abbruch
+Comment[el]=Λήψη μη αναμενόμενης σÏνδεσης, εγκατάλειψη
+Comment[eo]=Ricevis neatenditan konekton, haltis
+Comment[es]=Recibida conexión inesperada, abortar
+Comment[et]=Saadi ootamatu ühendus, loobuti
+Comment[eu]=Ustegabeko konexioa jaso da, abortatzen
+Comment[fa]=اتصال غیرمنتظره پذیرÙته شد، ساقط شد
+Comment[fi]=Vastaanotettiin odottamaton yhteys, lopeta
+Comment[fr]=Reçu une connexion inattendue, interruption
+Comment[gl]=Recibíuse unha conexión inesperada.
+Comment[he]=נתקבל חיבור בלתי צפוי, בוטל
+Comment[hi]=अपà¥à¤°à¤¤à¥à¤¯à¤¾à¤¶à¤¿à¤¤ कनेकà¥à¤¶à¤¨ पà¥à¤°à¤¾à¤ªà¥à¤¤. छोड़ा
+Comment[hr]=Primio sam neoÄekivanu vezu, prekid
+Comment[hu]=Nem várt csatlakozási kérés, kilépés
+Comment[is]=Tók á móti óvæntri tengingu, hætti
+Comment[it]=Ricevuta connessione inaspettata, termina
+Comment[ja]=予期ã—ãªã„接続をå—ä¿¡ã—ã¾ã—ãŸã€‚廃棄ã—ã¾ã™ã€‚
+Comment[ka]=მáƒáƒ£áƒšáƒáƒ“ნელი კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜ მáƒáƒ•áƒ˜áƒ“áƒ, შესáƒáƒ®áƒ”ბ
+Comment[kk]=Күтілмеген қоÑылым келді, доғарылды
+Comment[km]=បាន​ទទួលយក​ការ​ážâ€‹áž—្ជាប់​ដែល​មិន​បាន​រំពឹង​ទុក, ​បោះបង់
+Comment[lt]=Sulaukta netikÄ—to prisijungimo, nutraukiama
+Comment[mk]=Примено е неочекувано поврзување, Ñе прекинува
+Comment[mn]=ГÑнÑтийн холболтийг хүлÑÑн авав.Ðборт
+Comment[ms]=Menerima sambungan luar jangka, menamatkan
+Comment[mt]=Irċevejt konnessjoni mhux mistennija, ieqaf
+Comment[nb]=Mottok uventet oppkobling, avbryt
+Comment[nds]=Unverwacht Verbinnen kregen, afbraken
+Comment[ne]=अनपेकà¥à¤·à¤¿à¤¤ जडान पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो, परितà¥à¤¯à¤¾à¤— गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Ontving een onverwachte verbinding, gestopt
+Comment[nn]=Mottok uventa samband, avbryt
+Comment[nso]=Amogetse kgokagano yeo ebego esa emelwa, bolaya
+Comment[pl]=Otrzymano niespodziewane połączenie. Przerwane.
+Comment[pt]=Foi recebida uma ligação inesperada, a interromper
+Comment[pt_BR]=conexão recebida inesperadamente; abortar
+Comment[ro]=A fost recepţionată o conexiune neaşteptată şi a fost anulată
+Comment[ru]=Получено неожиданное Ñоединение. Отключение
+Comment[se]=OaÄÄui vuordekeahtes oktavuoÄ‘a, gaskkalduhte
+Comment[sk]=Prijaté neoÄakávane spojenie, ukonÄujem
+Comment[sl]=Prejeta nepriÄakovana povezava, prekinjeno
+Comment[sr]=Примљена је неочекивана веза, прекидам
+Comment[sr@Latn]=Primljena je neoÄekivana veza, prekidam
+Comment[sv]=Tog emot oväntad anslutning, avbryter
+Comment[ta]=எதிரà¯à®ªà®¾à®°à®¾à®¤ இணைபà¯à®ªà¯, நிறà¯à®¤à¯à®¤à®ªà¯à®ªà®Ÿà¯à®Ÿà®¤à¯
+Comment[tg]=ПайваÑтшавии ғайричашмдош қабул гардид, кандашавӣ
+Comment[th]=ได้รับà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¸—ี่ไม่คาดหวัง ทำà¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸
+Comment[tr]=Babul edilmiş beklenmedik bağlantı, durdur
+Comment[uk]=Отримано неочікуване з'єднаннÑ, ÑкаÑовую
+Comment[ven]=Vhukwamani vhu songo lavhelelwaho ho tanganedzhwaho, litsha
+Comment[xh]=Ufumene uxhulumaniso olungalindelekanga, lahla
+Comment[zh_CN]=收到æ„外连接,中止
+Comment[zh_HK]=接收到éžé æœŸçš„連線,中止
+Comment[zh_TW]=接收到éžé æœŸçš„連線,放棄
+Comment[zu]=Isithole ukuxhumana okungalindelekanga, hushula
+default_presentation=4
+
diff --git a/krfb/krfb/eyes-closed24.png b/krfb/krfb/eyes-closed24.png
new file mode 100644
index 00000000..f8cd9adb
--- /dev/null
+++ b/krfb/krfb/eyes-closed24.png
Binary files differ
diff --git a/krfb/krfb/eyes-open.png b/krfb/krfb/eyes-open.png
new file mode 100644
index 00000000..b5a6328b
--- /dev/null
+++ b/krfb/krfb/eyes-open.png
Binary files differ
diff --git a/krfb/krfb/eyes-open24.png b/krfb/krfb/eyes-open24.png
new file mode 100644
index 00000000..860d6c7e
--- /dev/null
+++ b/krfb/krfb/eyes-open24.png
Binary files differ
diff --git a/krfb/krfb/invitation.cc b/krfb/krfb/invitation.cc
new file mode 100644
index 00000000..75c4758a
--- /dev/null
+++ b/krfb/krfb/invitation.cc
@@ -0,0 +1,125 @@
+/***************************************************************************
+ invitation.cpp
+ -------------------
+ begin : Sat Mar 30 2002
+ copyright : (C) 2002 by Tim Jansen
+ (C) Stefan Taferner (password encryption)
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "invitation.h"
+
+/*
+ * Function for (en/de)crypting strings for config file, taken from KMail
+ * Author: Stefan Taferner <taferner@alpin.or.at>
+ */
+QString cryptStr(const QString &aStr) {
+ QString result;
+ for (unsigned int i = 0; i < aStr.length(); i++)
+ result += (aStr[i].unicode() < 0x20) ? aStr[i] :
+ QChar(0x1001F - aStr[i].unicode());
+ return result;
+}
+
+// a random string that doesn't contain i, I, o, O, 1, 0
+// based on KApplication::randomString()
+static QString readableRandomString(int length) {
+ QString str;
+ while (length)
+ {
+ int r = KApplication::random() % 62;
+ r += 48;
+ if (r > 57)
+ r += 7;
+ if (r > 90)
+ r += 6;
+ char c = char(r);
+ if ((c == 'i') ||
+ (c == 'I') ||
+ (c == '1') ||
+ (c == 'o') ||
+ (c == 'O') ||
+ (c == '0'))
+ continue;
+ str += c;
+ length--;
+ }
+ return str;
+}
+
+Invitation::Invitation() :
+ m_viewItem(0) {
+ m_password = readableRandomString(4)+"-"+readableRandomString(3);
+ m_creationTime = QDateTime::currentDateTime();
+ m_expirationTime = QDateTime::currentDateTime().addSecs(INVITATION_DURATION);
+}
+
+Invitation::Invitation(const Invitation &x) :
+ m_password(x.m_password),
+ m_creationTime(x.m_creationTime),
+ m_expirationTime(x.m_expirationTime),
+ m_viewItem(0) {
+}
+
+Invitation::Invitation(KConfig* config, int num) {
+ m_password = cryptStr(config->readEntry(QString("password%1").arg(num), ""));
+ m_creationTime = config->readDateTimeEntry(QString("creation%1").arg(num));
+ m_expirationTime = config->readDateTimeEntry(QString("expiration%1").arg(num));
+ m_viewItem = 0;
+}
+
+Invitation::~Invitation() {
+ if (m_viewItem)
+ delete m_viewItem;
+}
+
+Invitation &Invitation::operator= (const Invitation&x) {
+ m_password = x.m_password;
+ m_creationTime = x.m_creationTime;
+ m_expirationTime = x.m_expirationTime;
+ if (m_viewItem)
+ delete m_viewItem;
+ m_viewItem = 0;
+ return *this;
+}
+
+void Invitation::save(KConfig *config, int num) const {
+ config->writeEntry(QString("password%1").arg(num), cryptStr(m_password));
+ config->writeEntry(QString("creation%1").arg(num), m_creationTime);
+ config->writeEntry(QString("expiration%1").arg(num), m_expirationTime);
+}
+
+QString Invitation::password() const {
+ return m_password;
+}
+
+QDateTime Invitation::expirationTime() const {
+ return m_expirationTime;
+}
+
+QDateTime Invitation::creationTime() const {
+ return m_creationTime;
+}
+
+bool Invitation::isValid() const {
+ return m_expirationTime > QDateTime::currentDateTime();
+}
+
+void Invitation::setViewItem(KListViewItem *i) {
+ if (m_viewItem)
+ delete m_viewItem;
+ m_viewItem = i;
+}
+
+KListViewItem *Invitation::getViewItem() const{
+ return m_viewItem;
+}
diff --git a/krfb/krfb/invitation.h b/krfb/krfb/invitation.h
new file mode 100644
index 00000000..4f1dd826
--- /dev/null
+++ b/krfb/krfb/invitation.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ invitation.h
+ -------------------
+ begin : Sat Mar 30 2002
+ copyright : (C) 2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 INVITATION_H
+#define INVITATION_H
+
+#include <kapplication.h>
+#include <klistview.h>
+#include <kconfig.h>
+#include <qobject.h>
+#include <qstring.h>
+#include <qdatetime.h>
+
+
+const int INVITATION_DURATION = 60*60;
+
+QString cryptStr(const QString &aStr);
+
+class Invitation {
+public:
+ Invitation();
+ ~Invitation();
+ Invitation(KConfig* config, int num);
+ Invitation(const Invitation &x);
+ Invitation &operator= (const Invitation&x);
+
+ QString password() const;
+ QDateTime expirationTime() const;
+ QDateTime creationTime() const;
+ bool isValid() const;
+
+ void setViewItem(KListViewItem*);
+ KListViewItem* getViewItem() const;
+ void save(KConfig *config, int num) const;
+private:
+ QString m_password;
+ QDateTime m_creationTime;
+ QDateTime m_expirationTime;
+
+ KListViewItem *m_viewItem;
+};
+
+#endif
diff --git a/krfb/krfb/invitedialog.cc b/krfb/krfb/invitedialog.cc
new file mode 100644
index 00000000..a1177efa
--- /dev/null
+++ b/krfb/krfb/invitedialog.cc
@@ -0,0 +1,65 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "invitedialog.h"
+#include "invitewidget.h"
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kstdguiitem.h>
+
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+InviteDialog::InviteDialog( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "Invitation" ),
+ User1|Close|Help, NoDefault, true )
+{
+ m_inviteWidget = new InviteWidget( this, "InviteWidget" );
+ m_inviteWidget->pixmapLabel->setPixmap(
+ UserIcon( "connection-side-image.png" ) );
+ setMainWidget( m_inviteWidget );
+
+ setButtonGuiItem( User1, KStdGuiItem::configure() );
+
+ connect( m_inviteWidget->btnCreateInvite, SIGNAL( clicked() ),
+ SIGNAL( createInviteClicked() ) );
+ connect( m_inviteWidget->btnEmailInvite, SIGNAL( clicked() ),
+ SIGNAL( emailInviteClicked() ) );
+ connect( m_inviteWidget->btnManageInvite, SIGNAL( clicked() ),
+ SIGNAL( manageInviteClicked() ) );
+}
+
+void InviteDialog::slotUser1()
+{
+ emit configureClicked();
+}
+
+void InviteDialog::enableInviteButton( bool enable )
+{
+ m_inviteWidget->btnCreateInvite->setEnabled( enable );
+}
+
+void InviteDialog::setInviteCount( int count )
+{
+ m_inviteWidget->btnManageInvite->setText(
+ i18n( "&Manage Invitations (%1)..." ).arg( count ) );
+}
+
+#include "invitedialog.moc"
diff --git a/krfb/krfb/invitedialog.h b/krfb/krfb/invitedialog.h
new file mode 100644
index 00000000..2db1e413
--- /dev/null
+++ b/krfb/krfb/invitedialog.h
@@ -0,0 +1,54 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef INVITEDIALOG_H
+#define INVITEDIALOG_H
+
+class InviteWidget;
+
+#include <kdialogbase.h>
+
+class InviteDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+ InviteDialog( QWidget *parent, const char *name );
+ ~InviteDialog() {}
+
+ void enableInviteButton( bool enable );
+
+ public slots:
+ void setInviteCount( int count );
+
+ signals:
+ void createInviteClicked();
+ void emailInviteClicked();
+ void manageInviteClicked();
+ void configureClicked();
+
+ protected slots:
+ void slotUser1();
+
+ protected:
+ InviteWidget *m_inviteWidget;
+};
+
+#endif // INVITEDIALOG_H
+
diff --git a/krfb/krfb/invitewidget.ui b/krfb/krfb/invitewidget.ui
new file mode 100644
index 00000000..b0d80c36
--- /dev/null
+++ b/krfb/krfb/invitewidget.ui
@@ -0,0 +1,197 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>InviteWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>InviteWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>521</width>
+ <height>328</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Welcome to KDE Desktop Sharing</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="1" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kActiveLabel1</cstring>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>KDE Desktop Sharing allows you to invite somebody at a remote location to watch and possibly control your desktop.
+&lt;a href="whatsthis:&lt;p&gt;An invitation creates a one-time password that allows the receiver to connect to your desktop. It is valid for only one successful connection and will expire after an hour if it has not been used. When somebody connects to your computer a dialog will appear and ask you for permission. The connection will not be established before you accept it. In this dialog you can also restrict the other person to view your desktop only, without the ability to move your mouse pointer or press keys.&lt;/p&gt;&lt;p&gt;If you want to create a permanent password for Desktop Sharing, allow 'Uninvited Connections' in the configuration.&lt;/p&gt;"&gt;More about invitations...&lt;/a&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="7" colspan="1">
+ <property name="name">
+ <cstring>pixmapLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>WinPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ <property name="alignment">
+ <set>AlignTop</set>
+ </property>
+ </widget>
+ <spacer row="4" column="3">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="3" column="2">
+ <property name="name">
+ <cstring>btnCreateInvite</cstring>
+ </property>
+ <property name="text">
+ <string>Create &amp;Personal Invitation...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Create a new invitation and display the connection data. Use this option if you want to invite somebody personally, for example, to give the connection data over the phone.</string>
+ </property>
+ </widget>
+ <spacer row="6" column="2">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>24</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="4" column="1">
+ <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>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton" row="5" column="2">
+ <property name="name">
+ <cstring>btnManageInvite</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Manage Invitations (%1)...</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="4" column="2">
+ <property name="name">
+ <cstring>btnEmailInvite</cstring>
+ </property>
+ <property name="text">
+ <string>Invite via &amp;Email...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This button will start your email application with a pre-configured text that explains to the recipient how to connect to your computer. </string>
+ </property>
+ </widget>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>89</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<signals>
+ <signal>createInviteClicked()</signal>
+ <signal>emailInviteClicked()</signal>
+ <signal>manageInviteClicked()</signal>
+</signals>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/krfb/krfb/kinetd_krfb.desktop b/krfb/krfb/kinetd_krfb.desktop
new file mode 100644
index 00000000..673a46d3
--- /dev/null
+++ b/krfb/krfb/kinetd_krfb.desktop
@@ -0,0 +1,150 @@
+[Desktop Entry]
+Type=Service
+
+ServiceTypes=KInetDModule
+Exec=krfb
+X-KDE-FactoryName=kinetd
+X-KDE-KINETD-id=krfb
+X-KDE-KINETD-port=5900
+X-KDE-KINETD-autoPortRange=100
+X-KDE-KINETD-enabled=false
+X-KDE-KINETD-argument=--kinetd
+X-KDE-KINETD-multiInstance=false
+X-KDE-KINETD-serviceURL=service:remotedesktop.kde:vnc://%h:%p;(type=shared),(username=%u),(fullname=%f),(serviceid=%i)
+X-KDE-KINETD-serviceAttributes=(type=shared),(username=%u),(fullname=%f),(serviceid=%i)
+X-KDE-KINETD-serviceLifetime=1200
+X-KDE-KINETD-DNSSD-Name=Remote Desktop of %u
+X-KDE-KINETD-DNSSD-Type=_rfb._tcp
+X-KDE-KINETD-DNSSD-Properties=type=shared,u=%u,fullname=%f
+
+Name=KRfb Desktop Sharing
+Name[ar]=KRfb مشاركة سطح المكتب
+Name[be]=Ðгульнае выкарыÑтанне кампутара KRfb
+Name[bg]=СподелÑне на работното мÑÑто (KRfb)
+Name[bn]=কে-আর-à¦à¦«-বি ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি
+Name[br]=Rannañ burev KRfb
+Name[bs]=KRfb dijeljenje desktopa
+Name[ca]=Compartició de l'escriptori KRfb
+Name[cs]=Sdílení pracovní plochy KRfb
+Name[cy]=Rhannu Penbwrdd KRfb
+Name[da]=KRfb desktopdeling
+Name[de]=KRfb Arbeitsfläche freigeben
+Name[el]=Κοινή χÏήση επιφάνειας εÏγασίας KRfb
+Name[eo]=KRfb Tabula fordonado
+Name[es]=Escritorio compartido KRfb
+Name[et]=KRfb Töölaua jagamine
+Name[eu]=KRfb mahaigain partekatzea
+Name[fa]=اشتراک رومیزی KRfb
+Name[fi]=KRfb työpöydän jakaminen
+Name[fr]=Partage de bureau KRfb
+Name[ga]=Roinnt Deisce KRfb
+Name[gl]=KRfb Compartición de Escritorios
+Name[he]=שיתוף שולחנות עבודה של KRfb
+Name[hi]=KRfb डेसà¥à¤•à¤Ÿà¥‰à¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Name[hr]=KRfb dijeljenje radne površine
+Name[hu]=KRfb munkaasztal-megosztás
+Name[is]=KRfb Skjáborðsmiðlun
+Name[it]=Condivisione desktop KRfb
+Name[ja]=KRfb デスクトップ共有
+Name[ka]=KRfb სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ–იáƒáƒ áƒ”ბáƒ
+Name[kk]=KRfb ортақ Ò¯Ñтелі
+Name[km]=ការ​ចែក​រំលែក​ផ្ទៃ​ážáž» KRfb
+Name[lt]=KRfb Dalinimasis darbastaliu
+Name[mk]=Делење на површина Ñо KRfb
+Name[mn]=KRfb ажлын байр
+Name[ms]=Perkongsian Ruang Kerja KRfb
+Name[mt]=Qsim tad-desktop KRfb
+Name[nb]=KRfb skrivebordsdeling
+Name[nds]=KRfb-Schriefdischfreegaav
+Name[ne]=KRfb डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+Name[nl]=KRfb Bureaublad delen
+Name[nn]=KRfb-skrivebordsdeling
+Name[nso]=Kabagano ya Desktop ya KRfp
+Name[pa]=KRfb ਡੈਸਕਟਾਪ ਸਾਂà¨
+Name[pl]=KRfb Współdzielenie pulpitu
+Name[pt]=Partilha do Ecrã KRfb
+Name[pt_BR]=Compartilhamento do Ambiente de Trabalho KRfb
+Name[ro]=Partajare ecran KRfb
+Name[ru]=Общий рабочий Ñтол KRfb
+Name[se]=KRfb-Äállinbeavdejuohkin
+Name[sk]=KRfb zdieľanie pracovnej plochy
+Name[sl]=Deljenje namizja z KRfb
+Name[sr]=KRfb дељење радне површине
+Name[sr@Latn]=KRfb deljenje radne površine
+Name[sv]=Krfb dela ut skrivbord
+Name[ta]=KRfb பணிமேடை பகிரà¯à®µà¯
+Name[tg]=ИÑтифодабарии муштакари Мизи кории KRfb
+Name[th]=à¹à¸šà¹ˆà¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸žà¸·à¹‰à¸™à¸—ี่ทำงานร่วมà¸à¸±à¸™
+Name[tr]=KRfb Masaüstü Paylaşımı
+Name[uk]=Спільні ÑÑ‚Ñ–Ð»ÑŒÐ½Ð¸Ñ†Ñ KRfb
+Name[ven]=U kovhekana ha Desikithopo ya KRfb
+Name[xh]=Desktop ye KRfb Isebenza ngokuhlangeneyo
+Name[zh_CN]=KRfb æ¡Œé¢å…±äº«
+Name[zh_HK]=KRfb æ¡Œé¢åˆ†äº«
+Name[zh_TW]=KRfb æ¡Œé¢åˆ†äº«
+Name[zu]=KRfb ukwahlukanisela kwe-Desktop Sharing
+Comment=A daemon that allows you to share your desktop
+Comment[af]='n bediener wat laat toe jy na deel jou werkskerm
+Comment[ar]=مراقب يسمح لك بمشاركة سطح مكتبك
+Comment[be]=СервіÑ, Ñкі дае магчымаÑць ÐºÑ–Ñ€Ð°Ð²Ð°Ð½Ð½Ñ Ñтальніцай з іншага кампутара
+Comment[bg]=Демон за ÑподелÑне на работното мÑÑто
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ডিমন যে আপনার ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি করতে আপনাকে অনà§à¦®à¦¤à¦¿ দেয়
+Comment[bs]=Daemon koji vam omogućuje da dijelite vaš desktop
+Comment[ca]=Un dimoni que us permetrà compartir el vostre escriptori
+Comment[cs]=Démon umožňující sdílení vaší plochy
+Comment[cy]=Daemon sy'n gadael i chi rannu eich penbwrdd
+Comment[da]=En dæmon der tillader dig at dele din desktop
+Comment[de]=Ein Dienst, der die Freigabe der Arbeitsfläche erlaubt
+Comment[el]=Ένας δαίμονας που σας επιτÏέπει να μοιÏαστείτε την επιφάνεια εÏγασίας σας
+Comment[eo]=demono por permesi retan fordonadon de via tabulo
+Comment[es]=Un demonio que le permite compartir su escritorio
+Comment[et]=Deemon, mis lubab sul töölauda jagada
+Comment[eu]=Zure mahaigaina partekatzeko aukera ematen duen deabrua da
+Comment[fa]=یک شبح که اجازۀ مشترک ساختن رومیزی را به شما می‌دهد
+Comment[fi]=Palvelin, joka mahdollistaa työpöydän jakamisen
+Comment[fr]=Un démon qui vous permet de partager votre bureau
+Comment[ga]=Deamhan a chuireann ar do chumas do dheasca a roinnt
+Comment[gl]=Un demo que permite a compartición do teu escritorio
+Comment[he]=תהליך רקע שמ×פשר לך לשתף ×ת שולחן העבודה שלך
+Comment[hi]=आपके डेसà¥à¤•à¤Ÿà¥‰à¤ª को साà¤à¥‡à¤¦à¤¾à¤°à¥€ करने देने वाला डेमन
+Comment[hr]=Daemon koji vam omogućuje da dijelite svoju radnu površinu s drugima
+Comment[hu]=Munkaasztal-megosztási szolgáltatás
+Comment[is]=Þjónn sem leyfir þér að miðla skjáborðinu þínu
+Comment[it]=Un demone che permette di condividere il tuo desktop
+Comment[ja]=デスクトップ共有をå¯èƒ½ã«ã™ã‚‹ãƒ‡ãƒ¼ãƒ¢ãƒ³
+Comment[ka]=დემáƒáƒœáƒ˜, რáƒáƒ›áƒ”ლიც სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ–იáƒáƒ áƒ”ბის სáƒáƒ¨áƒ£áƒáƒšáƒ”ბáƒáƒ¡ იძლევáƒ
+Comment[kk]=Ò®Ñтеліңізді ортақтаÑтыруға мүмкіндік беретін қызмет
+Comment[km]=ដáŸáž˜áž·áž“​ដែល​អនុញ្ញាážâ€‹áž²áŸ’យ​អ្នក​ចែក​រំលែក​ផ្ទៃážáž»â€‹ážšáž”ស់​អ្នក
+Comment[lt]=Tarnyba, leidžianti dalintis savo darbastaliu
+Comment[mk]=Даемон кој ви дозволува да ја делите вашата работна површина
+Comment[mn]=Таны ажлын байрыг хамтран ÑзÑмшихийгзөвшөөрÑөн демон
+Comment[ms]=Daemon yang membenarkan anda berkongsi ruang kerja
+Comment[mt]=Daemon li jħallik taqsam id-desktop ma' ħaddieħor
+Comment[nb]=En programnisse som tillater deling av ditt skrivebord med andre
+Comment[nds]=En Dämoon, wo Du Dien Schriefdisch mit delen kannst
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ डेइमन जसले तपाईà¤à¤•à¥‹ डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤° गरà¥à¤¨ अनà¥à¤®à¤¤à¤¿ दिनà¥à¤›
+Comment[nl]=Een daemon waarmee u uw bureaublad kunt vrijgeven om te delen
+Comment[nn]=Ein nisse som let deg dela skrivebordet
+Comment[nso]=Daemon yeo ego dumelelago go abelana ka desktop ya gago
+Comment[pl]=Usługa, która pozwala na współdzielenie pulpitu
+Comment[pt]=Um servidor que lhe permite partilhar o seu ecrã
+Comment[pt_BR]=Um servidor que permite a você compartilhar o seu ambiente de trabalho
+Comment[ro]=Un demon care vă permite saă partajaţi sistemul dumneavoastră
+Comment[ru]=Служба ÑовмеÑтного доÑтупа к рабочему Ñтолу
+Comment[se]=Duogášprográmma mii diktá du juohkit Äállinbeavddi earáiguin
+Comment[sk]=Démon ktorý umožní zdieľať vašu pracovnú plochu
+Comment[sl]=Strežnik, s katerim lahko delite vaše namizje
+Comment[sr]=Демон који вам дозвољава да делите вашу радну површину
+Comment[sr@Latn]=Demon koji vam dozvoljava da delite vašu radnu površinu
+Comment[sv]=Demon som låter dig dela ut skrivbordet
+Comment[ta]=ஒர௠டேமொன௠உஙà¯à®•à®³à¯ பணிமேடை பகிரà¯à®µà¯ˆ அளிகà¯à®•à¯à®®à¯
+Comment[tg]=Ðзозиле, ки ба шумо иÑтифодабарии муштараки мизи кориро медиҳад
+Comment[th]=เดมอนอนุà¸à¸²à¸•à¹ƒà¸«à¹‰à¸„ุณà¹à¸šà¹ˆà¸‡à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸žà¸·à¹‰à¸™à¸—ี่ทำงานร่วมà¸à¸±à¸™
+Comment[tr]=Masaüstünüzde paylaşılmasına izin verilmiş hayalet program
+Comment[uk]=Демон, що дозволÑÑ” Ñпільне викориÑÑ‚Ð°Ð½Ð½Ñ Ñтільниць
+Comment[ven]=Daemon ine yani tendela nitshi kovhekana desktop yanu
+Comment[xh]=Daemon ekuvumela ukuba wabe i desktop yakho
+Comment[zh_CN]=å…许您共享桌é¢çš„守护进程
+Comment[zh_HK]=讓您分享您的桌é¢çš„系統程å¼
+Comment[zh_TW]=讓您分享您的桌é¢çš„伺æœç¨‹å¼
+Comment[zu]=I-daemoni ekuvumela uhlukaniselana nge-desktop
diff --git a/krfb/krfb/krfb.desktop b/krfb/krfb/krfb.desktop
new file mode 100644
index 00000000..6a39da53
--- /dev/null
+++ b/krfb/krfb/krfb.desktop
@@ -0,0 +1,64 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Exec=krfb -caption "%c" %i %m
+Icon=krfb
+DocPath=krfb/index.html
+Terminal=false
+Name=Krfb
+Name[bn]=কে-আর-à¦à¦«-বি
+Name[zh_TW]=Krfb æ¡Œé¢åˆ†äº«
+GenericName=Desktop Sharing
+GenericName[be]=Ðгульнае выкарыÑтанне Ñтальніцы
+GenericName[bg]=СподелÑне на раб. мÑÑто
+GenericName[bn]=ডেসà§à¦•à¦Ÿà¦ª ভাগাভাগি
+GenericName[br]=Rannañ ar vurev
+GenericName[bs]=Dijeljenje desktopa
+GenericName[ca]=Compartició de l'escriptori
+GenericName[cs]=Sdílení pracovní plochy
+GenericName[cy]=Rhannu Penbwrdd
+GenericName[da]=Desktopdeling
+GenericName[de]=Arbeitsfläche freigeben
+GenericName[el]=Κοινή χÏήση επιφάνειας εÏγασίας
+GenericName[eo]=Tabulkomunigado
+GenericName[es]=Escritorio compartido
+GenericName[et]=Töölaua jagamine
+GenericName[eu]=Mahaigain partekatzea
+GenericName[fa]=اشتراک رومیزی
+GenericName[fi]=Työpöydän jakaminen
+GenericName[fr]=Partage de bureau
+GenericName[ga]=Roinnt Deisce
+GenericName[gl]=Compartidor de Escritorio
+GenericName[he]=שיתוף שולחנות עבודה
+GenericName[hu]=Munkaasztal-megosztás
+GenericName[is]=Skjáborðsmiðlun
+GenericName[it]=Condivisione desktop
+GenericName[ja]=デスクトップ共有
+GenericName[ka]=სáƒáƒ›áƒ£áƒ¨áƒáƒ მáƒáƒ’იდის გáƒáƒ–იáƒáƒ áƒ”ბáƒ
+GenericName[kk]=Ò®Ñтелді ортақтаÑтыру
+GenericName[km]=ការ​ចែក​រំលែក​ផ្ទៃ​ážáž»
+GenericName[lt]=Dalinimasis darbastaliu
+GenericName[mt]=Qsim tad-desktop
+GenericName[nb]=Delte skrivebord
+GenericName[nds]=Schriefdisch-Freegaav
+GenericName[ne]=डेसà¥à¤•à¤Ÿà¤ª साà¤à¥‡à¤¦à¤¾à¤°à¥€
+GenericName[nl]=Bureaublad delen
+GenericName[nn]=Skrivebordsdeling
+GenericName[pa]=ਡੈਸਕਟਾਪ ਸਾਂà¨
+GenericName[pl]=Współdzielenie pulpitu
+GenericName[pt]=Partilha do Ambiente de Trabalho
+GenericName[pt_BR]=Compartilhamento de Ambiente de Trabalho
+GenericName[ru]=Общий рабочий Ñтол
+GenericName[sk]=Zdieľanie pracovnej plochy
+GenericName[sl]=Deljenje namizja
+GenericName[sr]=Дељење радне површине
+GenericName[sr@Latn]=Deljenje radne površine
+GenericName[sv]=Dela ut skrivbordet
+GenericName[tr]=Masaüstü Paylaşımı
+GenericName[uk]=Спільні Ñтільниці
+GenericName[uz]=Ish stoli bilan boʻlishish
+GenericName[uz@cyrillic]=Иш Ñтоли билан бўлишиш
+GenericName[zh_CN]=æ¡Œé¢å…±äº«
+GenericName[zh_HK]=æ¡Œé¢åˆ†äº«
+GenericName[zh_TW]=æ¡Œé¢åˆ†äº«
+Categories=Qt;KDE;System;RemoteAccess;Network;
diff --git a/krfb/krfb/krfbiface.h b/krfb/krfb/krfbiface.h
new file mode 100644
index 00000000..365c4b7a
--- /dev/null
+++ b/krfb/krfb/krfbiface.h
@@ -0,0 +1,24 @@
+#ifndef __KRFB_IFACE_H
+#define __KRFB_IFACE_H
+
+#include <dcopobject.h>
+
+class krfbIface : virtual public DCOPObject
+{
+ K_DCOP
+k_dcop:
+
+ /**
+ * Quits krfb, connected clients will be disconnected.
+ */
+ virtual void exit() = 0;
+
+ /**
+ * If this feature is activated krfb allows the connecting client to
+ * control the desktop (pointer & keyboard).
+ * @return a true to activate desktop control
+ */
+ virtual void setAllowDesktopControl(bool a) = 0;
+
+};
+#endif
diff --git a/krfb/krfb/krfbifaceimpl.cc b/krfb/krfb/krfbifaceimpl.cc
new file mode 100644
index 00000000..a79b1437
--- /dev/null
+++ b/krfb/krfb/krfbifaceimpl.cc
@@ -0,0 +1,27 @@
+/***************************************************************************
+ * *
+ * 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 "krfbifaceimpl.h"
+
+KRfbIfaceImpl::KRfbIfaceImpl(RFBController *c) :
+ DCOPObject("krfbIface"),
+ controller(c)
+{
+}
+
+void KRfbIfaceImpl::exit()
+{
+ emit exitApp();
+}
+void KRfbIfaceImpl::setAllowDesktopControl(bool b)
+{
+ controller->enableDesktopControl(b);
+}
+
+#include "krfbifaceimpl.moc"
diff --git a/krfb/krfb/krfbifaceimpl.h b/krfb/krfb/krfbifaceimpl.h
new file mode 100644
index 00000000..430a0652
--- /dev/null
+++ b/krfb/krfb/krfbifaceimpl.h
@@ -0,0 +1,22 @@
+#ifndef __KRFB_IFACE_IMPL_H
+#define __KRFB_IFACE_IMPL_H
+
+#include <qobject.h>
+#include "rfbcontroller.h"
+#include "krfbiface.h"
+
+class KRfbIfaceImpl : public QObject, public virtual krfbIface
+{
+ Q_OBJECT
+private:
+ RFBController *controller;
+public:
+ KRfbIfaceImpl(RFBController *c);
+signals:
+ void exitApp();
+
+public:
+ void exit();
+ void setAllowDesktopControl(bool);
+};
+#endif
diff --git a/krfb/krfb/lo16-app-krfb.png b/krfb/krfb/lo16-app-krfb.png
new file mode 100644
index 00000000..3eb33109
--- /dev/null
+++ b/krfb/krfb/lo16-app-krfb.png
Binary files differ
diff --git a/krfb/krfb/lo32-app-krfb.png b/krfb/krfb/lo32-app-krfb.png
new file mode 100644
index 00000000..ef3d45d2
--- /dev/null
+++ b/krfb/krfb/lo32-app-krfb.png
Binary files differ
diff --git a/krfb/krfb/main.cpp b/krfb/krfb/main.cpp
new file mode 100644
index 00000000..4428e983
--- /dev/null
+++ b/krfb/krfb/main.cpp
@@ -0,0 +1,191 @@
+/***************************************************************************
+ main.cpp
+ -------------------
+ begin : Sat Dec 8 03:23:02 CET 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "trayicon.h"
+#include "configuration.h"
+#include "krfbifaceimpl.h"
+#include "rfbcontroller.h"
+
+#include <kpixmap.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <knotifyclient.h>
+#include <ksystemtray.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <kaboutapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qobject.h>
+#include <qwindowdefs.h>
+#include <qcstring.h>
+#include <qdatastream.h>
+#include <dcopref.h>
+
+#include <signal.h>
+
+#undef VERSION
+#define VERSION "1.0"
+
+static const char description[] = I18N_NOOP("VNC-compatible server to share "
+ "KDE desktops");
+#define ARG_KINETD "kinetd"
+
+
+static KCmdLineOptions options[] =
+{
+ { ARG_KINETD " ", I18N_NOOP("Used for calling from kinetd"), 0},
+ KCmdLineLastOption
+};
+
+void checkKInetd(bool &kinetdAvailable, bool &krfbAvailable) {
+ DCOPRef ref("kded", "kinetd");
+ ref.setDCOPClient(KApplication::dcopClient());
+
+ DCOPReply r = ref.call("isInstalled", QString("krfb"));
+ if (!r.isValid()) {
+ kinetdAvailable = false;
+ krfbAvailable = false;
+ return;
+ }
+
+ r.get(krfbAvailable);
+ kinetdAvailable = true;
+}
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutData( "krfb", I18N_NOOP("Desktop Sharing"),
+ VERSION, description, KAboutData::License_GPL,
+ "(c) 2001-2003, Tim Jansen\n"
+ "(c) 2001, Johannes E. Schindelin\n"
+ "(c) 2000, heXoNet Support GmbH, D-66424 Homburg\n"
+ "(c) 2000-2001, Const Kaplinsky\n"
+ "(c) 2000, Tridia Corporation\n"
+ "(c) 1999, AT&T Laboratories Cambridge\n",
+ 0, "", "tim@tjansen.de");
+ aboutData.addAuthor("Tim Jansen", "", "tim@tjansen.de");
+ aboutData.addAuthor("Ian Reinhart Geiser", "DCOP interface", "geiseri@kde.org");
+ aboutData.addCredit("Johannes E. Schindelin",
+ I18N_NOOP("libvncserver"));
+ aboutData.addCredit("Const Kaplinsky",
+ I18N_NOOP("TightVNC encoder"));
+ aboutData.addCredit("Tridia Corporation",
+ I18N_NOOP("ZLib encoder"));
+ aboutData.addCredit("AT&T Laboratories Cambridge",
+ I18N_NOOP("original VNC encoders and "
+ "protocol design"));
+ aboutData.addCredit("Jens Wagner (heXoNet Support GmbH)",
+ I18N_NOOP("X11 update scanner, "
+ "original code base"));
+ aboutData.addCredit("Jason Spisak",
+ I18N_NOOP("Connection side image"),
+ "kovalid@yahoo.com");
+ aboutData.addCredit("Karl Vogel",
+ I18N_NOOP("KDesktop background deactivation"));
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KApplication app;
+
+ Configuration *config;
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ QString fdString;
+ if (!args->isSet(ARG_KINETD)) {
+ bool kinetdA, krfbA;
+ checkKInetd(kinetdA, krfbA);
+ if (!kinetdA) {
+ KMessageBox::error(0,
+ i18n("Cannot find KInetD. "
+ "The KDE daemon (kded) may have crashed or has not been started at all, or the installation failed."),
+ i18n("Desktop Sharing Error"));
+ return 1;
+ }
+ if (!krfbA) {
+ KMessageBox::error(0,
+ i18n("Cannot find KInetD service for Desktop Sharing (krfb). "
+ "The installation is incomplete or failed."),
+ i18n("Desktop Sharing Error"));
+ return 1;
+ }
+
+ config = new Configuration(KRFB_INVITATION_MODE);
+ config->showInvitationDialog();
+ return 0;
+ }
+ fdString = args->getOption(ARG_KINETD);
+ config = new Configuration(KRFB_KINETD_MODE);
+ args->clear();
+
+ if ((!config->allowUninvitedConnections()) && (config->invitations().size() == 0)) {
+ KNotifyClient::event("UnexpectedConnection");
+ return 1;
+ }
+
+ if (!RFBController::checkX11Capabilities())
+ return 1;
+
+ TrayIcon trayicon(new KAboutApplication(&aboutData),
+ config);
+ RFBController controller(config);
+ KRfbIfaceImpl dcopiface(&controller);
+
+ QObject::connect(&app, SIGNAL(lastWindowClosed()), // dont show passivepopup
+ &trayicon, SLOT(prepareQuit()));
+ QObject::connect(&app, SIGNAL(lastWindowClosed()),
+ &controller, SLOT(closeConnection()));
+
+ QObject::connect(&trayicon, SIGNAL(showManageInvitations()),
+ config, SLOT(showManageInvitationsDialog()));
+ QObject::connect(&trayicon, SIGNAL(enableDesktopControl(bool)),
+ &controller, SLOT(enableDesktopControl(bool)));
+ QObject::connect(&trayicon, SIGNAL(diconnectedMessageDisplayed()),
+ &app, SLOT(quit()));
+
+ QObject::connect(&dcopiface, SIGNAL(exitApp()),
+ &controller, SLOT(closeConnection()));
+ QObject::connect(&dcopiface, SIGNAL(exitApp()),
+ &app, SLOT(quit()));
+
+ QObject::connect(&controller, SIGNAL(sessionRefused()),
+ &app, SLOT(quit()));
+ QObject::connect(&controller, SIGNAL(sessionEstablished(QString)),
+ &trayicon, SLOT(showConnectedMessage(QString)));
+ QObject::connect(&controller, SIGNAL(sessionFinished()),
+ &trayicon, SLOT(showDisconnectedMessage()));
+ QObject::connect(&controller, SIGNAL(desktopControlSettingChanged(bool)),
+ &trayicon, SLOT(setDesktopControlSetting(bool)));
+ QObject::connect(&controller, SIGNAL(quitApp()),
+ &app, SLOT(quit()));
+
+ sigset_t sigs;
+ sigemptyset(&sigs);
+ sigaddset(&sigs, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &sigs, 0);
+
+ bool ok;
+ int fdNum = fdString.toInt(&ok);
+ if (!ok) {
+ kdError() << "kinetd fd was not numeric." << endl;
+ return 2;
+ }
+ controller.startServer(fdNum);
+
+ return app.exec();
+}
+
diff --git a/krfb/krfb/manageinvitations.ui b/krfb/krfb/manageinvitations.ui
new file mode 100644
index 00000000..eae26507
--- /dev/null
+++ b/krfb/krfb/manageinvitations.ui
@@ -0,0 +1,216 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>ManageInvitationsDialog</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>ManageInvitationsDialog</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>680</width>
+ <height>350</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Manage Invitations - Desktop Sharing</string>
+ </property>
+ <property name="icon">
+ <pixmap>image0</pixmap>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer row="7" column="0">
+ <property name="name">
+ <cstring>Spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="0" column="1">
+ <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>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KListView" row="0" column="0" rowspan="5" colspan="1">
+ <column>
+ <property name="text">
+ <string>Created</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Expiration</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>listView</cstring>
+ </property>
+ <property name="hScrollBarMode">
+ <enum>AlwaysOff</enum>
+ </property>
+ <property name="allColumnsShowFocus">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Displays the open invitations. Use the buttons on the right to delete them or create a new invitation.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>newPersonalInvitationButton</cstring>
+ </property>
+ <property name="text">
+ <string>New &amp;Personal Invitation...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Create a new personal invitation...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to create a new personal invitation.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>newEmailInvitationButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New Email Invitation...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Send a new invitation via email...</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Click this button to send a new invitation via email.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>deleteAllButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Delete All</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Delete all invitations</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Deletes all open invitations.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="4" column="1">
+ <property name="name">
+ <cstring>deleteOneButton</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Delete</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Delete the selected invitation</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Delete the selected invitation. The invited person will not be able to connect using this invitation anymore.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="6" column="1" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>closeButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Close</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Closes this window.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Closes this window.</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="6958">789cdd9859531b490cc7dff914aee82db5a5784e7b6a6b1f38c219ee9bad7d98d30718830f8c9ddaefbe6a493d10e3002178b3b55118fae7eed1bf5b52f78cf9f4b172b6b75df9f869a13f8807adb49236e35ee56336ec74c67ffef5c7d7850f7ebd42ff1dcfadb81f7e5bf8b03fa8a4959dee756e00ae08a04aff6a09736eb9ee19c65ac9dc8fa1e5c8e7f159c929f3a5e598fbf1dc72227c5672c67c68390d984f4b96fe03cb19f7435ab2ccbf69390f79fc45c9dc8f27960bee878eb25fe3f5c1c4b2eaed5b56bdcfc28ece07162dabbfbeb0ab7ab02a1c44da7fcb5cafd6450f772cc71c2fdcb62ceb8742390d63e62f9625bec8ec98f948bcf6947d8d77ddb2c403968483a028981bcc75a71e88ff7dcb1a8fad92a51e362d6bbeaf95d3808b09ba865d5abf2ffe7acc915bf722e663cbbec3f35b2f59c6b72cebfc6e94534fc6af5956bd6d61cfe667201c3a723f8261cff1aa7aff96b22f0c27caaa8f3e73e4e97c715339558e0dfb8e5f95f9436259eb6b45d9d7fe53e5bacc17b97e2839566fd5b2ae676c59d7bfae9ceaf8c8b2f61f088781d6cbb270acf945ce6fe00655d55f560e546f47b816e8fed915ae47124fbc638e8348ea03772d6bfe374a967a5c52ce74fcd070e8864e28faf7ca8132d747188791d437562debfc27ca99d6ff9a701c6b3febd7bc9aa3fb69ac1c68fdee0947819e375c1fb5a416493d63a29c29733dd73d2a70f1776c59f55aca81f483c3fce0af2b5c8ff47c4c95336138128e023d1f797f46be9d2f16c29123f1c54be540e31d5bd6fc1f0a27a19c4731af274aed786c2bdbfce4ca99ea73bd53b9e97ce14cd8eac7a0ace331138e1d3d5f46ca8130f0f91ea736ff712c5cc62b52b67aae70b9debe302558f43acab930048613bfdccf8bc289abf9bd520e95f9fc48b224d6e7cbad722e0cf796f57ce4e7451ad8fd0255655bff4de1d4d5fd36520e953de6ccae1fee84d3589f17fcbccc02738232b785b350f33954ce95f9799be56eeacaf913081b63762cebf815e15c3946c37958c6eb48387775bd9c5f1aaefb095039d7f38cf3531485dd6f9e7221bc3f98affd9f3410e66ba5463c37b31a312698cec5128c4b8d14b3b9583aa59163f18e2b28c8df538d021bef988706f97baa919a0cbd9b953e9f6accae716cceb6efed89ff9606fbc9b0856dbca4e85e71555c61472bc6d83576f1e6a9de6b34749eb7747fce1a09f6c87797ae7da201b5867847bf5bd4eab3ca08afdfa47143f36ea9c63d5f8734f3312915e47fc8b59ee084567963d6f4231aec7d848b1497019f0009f99d50db5cc506fa8999c1907efab49ecb8798bd42e396fc7769de19fb3171e991cfa527d721f7f6e85ad03cda1cdb9b576a8c38d366a6e6c744fe8ebc4e5f87dcdbd331268a661f775fd6e0f57668dd63f6dda3eb32aeccb4cf94233be68eb506a4d6650fcf6bdc92f5b56e96e94468e02aaee1fa0c5ba39e86da1d6e908a5961ca1e9ed71891b5343e0ddcc42df2f505b77167cab6e9d335ea15dbd4188e295ac6c3f31a05d958e3d3a0f5ece21eeee3011e4ed9017dba47bd976c5d1a2bd11b53f693173436c8861a81091ee1319ee029f93cc3f34776469f9ce20556d141173d1a3729a3b644f6bcc626994f639b18d09d6ea9718e21d6d442a24356a963442a1e8d3ba2f14dd618e04b7565ec1efb80e801400c09a4460132c8a180065941ad4c54a0092d6893061bddd327ff26332f6be85dd0260f4df6750957d0816bd30bd7d4ba82cba71a6ced979f1fdfd5e840176e58e3865a9db968dc42cf3ee3a047f4abd66132dffa098dd7e4c35470fc4a8db1d655a931bbae20853e0c60a833baa3353cbc75bc74ee0236608401dcc39876c0a9a84cef0f98c0222cc132ac689452f1fecab31df8fda8813db8a79d7c317b9fc36758853552d07d0eeb3fa2c1634ce4d731850dd8842df8327d5ec136ecc02eec51ee8ca546e14def25e51b19ec4f9fbb7000877044275b39e68def3e0f1ac7d34f0f3881533a69e39fd598a5366deff79ef8a3f6eb35decd666bd8ef1f9b6ffecef1f2f70ff91ed5a593e41ae7f43d0acee01c2ea00a0e16e08247ed7338d3ef756d7e9728f4cefc51fbdbde6f3f7faae1430021d4a04e1a11a52624f675acbc5b9bb774d1e896edc7bd8fc7ccd488c1fc9196bc2798533ba2b61f838ecdf5ccc855232fdb8f7b1f8f99d6f0f91d791eb684febffab78c796afcfdfbc23fde05c197</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>listView</sender>
+ <signal>selectionChanged()</signal>
+ <receiver>ManageInvitationsDialog</receiver>
+ <slot>listSelectionChanged()</slot>
+ </connection>
+ <connection>
+ <sender>closeButton</sender>
+ <signal>clicked()</signal>
+ <receiver>ManageInvitationsDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+</connections>
+<includes>
+ <include location="global" impldecl="in declaration">klistview.h</include>
+ <include location="local" impldecl="in implementation">manageinvitations.ui.h</include>
+</includes>
+<slots>
+ <slot>listSizeChanged( int i )</slot>
+ <slot>listSelectionChanged()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+</includehints>
+</UI>
diff --git a/krfb/krfb/manageinvitations.ui.h b/krfb/krfb/manageinvitations.ui.h
new file mode 100644
index 00000000..86c1fa4b
--- /dev/null
+++ b/krfb/krfb/manageinvitations.ui.h
@@ -0,0 +1,15 @@
+void ManageInvitationsDialog::listSizeChanged(int i) {
+ deleteAllButton->setEnabled(i);
+}
+
+void ManageInvitationsDialog::listSelectionChanged() {
+ QListViewItem *i = listView->firstChild();
+ while(i) {
+ if (i->isSelected()) {
+ deleteOneButton->setEnabled(true);
+ return;
+ }
+ i = i->nextSibling();
+ }
+ deleteOneButton->setEnabled(false);
+}
diff --git a/krfb/krfb/personalinvitedialog.cc b/krfb/krfb/personalinvitedialog.cc
new file mode 100644
index 00000000..aa00cc24
--- /dev/null
+++ b/krfb/krfb/personalinvitedialog.cc
@@ -0,0 +1,54 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "personalinvitedialog.h"
+#include "personalinvitewidget.h"
+
+#include <qlabel.h>
+
+#include <kactivelabel.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+PersonalInviteDialog::PersonalInviteDialog( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true, i18n( "Personal Invitation" ),
+ Close, Close, true )
+{
+ m_inviteWidget = new PersonalInviteWidget( this, "PersonalInviteWidget" );
+ m_inviteWidget->pixmapLabel->setPixmap(
+ UserIcon( "connection-side-image.png" ) );
+
+ setMainWidget( m_inviteWidget );
+}
+
+void PersonalInviteDialog::setHost( const QString &host, uint port )
+{
+ m_inviteWidget->hostLabel->setText( QString( "%1:%2" )
+ .arg( host ).arg( port ) );
+}
+
+void PersonalInviteDialog::setPassword( const QString &passwd )
+{
+ m_inviteWidget->passwordLabel->setText( passwd );
+}
+
+void PersonalInviteDialog::setExpiration( const QDateTime &expire )
+{
+ m_inviteWidget->expirationLabel->setText( expire.toString( Qt::LocalDate ) );
+}
diff --git a/krfb/krfb/personalinvitedialog.h b/krfb/krfb/personalinvitedialog.h
new file mode 100644
index 00000000..85a5966c
--- /dev/null
+++ b/krfb/krfb/personalinvitedialog.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PERSONALINVITEDIALOG_H
+#define PERSONALINVITEDIALOG_H
+
+class PersonalInviteWidget;
+
+#include <qdatetime.h>
+
+#include <kdialogbase.h>
+
+class PersonalInviteDialog : public KDialogBase
+{
+ public:
+ PersonalInviteDialog( QWidget *parent, const char *name );
+ virtual ~PersonalInviteDialog() {}
+
+ void setHost( const QString &host, uint port );
+ void setPassword( const QString &passwd );
+ void setExpiration( const QDateTime &expire );
+
+ protected:
+ PersonalInviteWidget *m_inviteWidget;
+};
+
+#endif // PERSONALINVITEDIALOG_H
+
diff --git a/krfb/krfb/personalinvitewidget.ui b/krfb/krfb/personalinvitewidget.ui
new file mode 100644
index 00000000..9ac68eb7
--- /dev/null
+++ b/krfb/krfb/personalinvitewidget.ui
@@ -0,0 +1,241 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>PersonalInviteWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>519</width>
+ <height>328</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string></string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="resizeMode">
+ <enum>Fixed</enum>
+ </property>
+ <widget class="KActiveLabel" row="0" column="1" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>mainTextLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>&lt;h2&gt;Personal Invitation&lt;/h2&gt;
+Give the information below to the person that you want to invite (&lt;a href="whatsthis:Desktop Sharing uses the VNC protocol. You can use any VNC client to connect. In KDE the client is called 'Remote Desktop Connection'. Enter the host information into the client and it will connect.."&gt;how to connect&lt;/a&gt;). Note that everybody who gets the password can connect, so be careful.</string>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <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>34</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0" rowspan="6" colspan="1">
+ <property name="name">
+ <cstring>pixmapLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>108</width>
+ <height>318</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>WinPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="5" column="2">
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>30</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KActiveLabel" row="2" column="2">
+ <property name="name">
+ <cstring>hostLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>cookie.tjansen.de:0</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>kActiveLabel6</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Password:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="1">
+ <property name="name">
+ <cstring>kActiveLabel7</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Expiration time:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="3" column="2">
+ <property name="name">
+ <cstring>passwordLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>12345</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="4" column="2">
+ <property name="name">
+ <cstring>expirationLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>NoFocus</enum>
+ </property>
+ <property name="text">
+ <string>17:12</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>kActiveLabel5</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;b&gt;Host:&lt;/b&gt;</string>
+ </property>
+ </widget>
+ <widget class="KActiveLabel" row="2" column="3">
+ <property name="name">
+ <cstring>hostHelpLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>(&lt;a href="whatsthis:This field contains the address of your computer and the display number, separated by a colon. The address is just a hint - you can use any address that can reach your computer. Desktop Sharing tries to guess your address from your network configuration, but does not always succeed in doing so. If your computer is behind a firewall it may have a different address or be unreachable for other computers."&gt;Help&lt;/a&gt;)</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+</UI>
diff --git a/krfb/krfb/rfbcontroller.cc b/krfb/krfb/rfbcontroller.cc
new file mode 100644
index 00000000..e6277b59
--- /dev/null
+++ b/krfb/krfb/rfbcontroller.cc
@@ -0,0 +1,902 @@
+/***************************************************************************
+ rfbcontroller.cpp
+ -------------------
+ begin : Sun Dec 9 2001
+ copyright : (C) 2001-2003 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * Contains keyboard & pointer handling from libvncserver's x11vnc.c
+ */
+
+#include "rfbcontroller.h"
+#include "kuser.h"
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef USE_SOLARIS
+#include <strings.h>
+#endif
+
+#include <kapplication.h>
+#include <knotifyclient.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kextsock.h>
+#include <qstring.h>
+#include <qcursor.h>
+#include <qwindowdefs.h>
+#include <qtimer.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qglobal.h>
+#include <qlabel.h>
+#include <qmutex.h>
+#include <qdeepcopy.h>
+#include <qclipboard.h>
+#include <qdesktopwidget.h>
+
+#include <X11/Xutil.h>
+#include <X11/extensions/XTest.h>
+
+#ifndef ASSERT
+#define ASSERT(x) Q_ASSERT(x)
+#endif
+
+#define IDLE_PAUSE (1000/50)
+#define MAX_SELECTION_LENGTH (4096)
+
+static XTestDisabler disabler;
+
+static const char* cur=
+" "
+" x "
+" xx "
+" xxx "
+" xxxx "
+" xxxxx "
+" xxxxxx "
+" xxxxxxx "
+" xxxxxxxx "
+" xxxxxxxxx "
+" xxxxxxxxxx "
+" xxxxx "
+" xx xxx "
+" x xxx "
+" xxx "
+" xxx "
+" xxx "
+" xxx "
+" ";
+
+static const char* mask=
+"xx "
+"xxx "
+"xxxx "
+"xxxxx "
+"xxxxxx "
+"xxxxxxx "
+"xxxxxxxx "
+"xxxxxxxxx "
+"xxxxxxxxxx "
+"xxxxxxxxxxx "
+"xxxxxxxxxxxx "
+"xxxxxxxxxx "
+"xxxxxxxx "
+"xxxxxxxx "
+"xx xxxxx "
+" xxxxx "
+" xxxxx "
+" xxxxx "
+" xxx ";
+
+static rfbCursorPtr myCursor;
+
+// only one controller exists, so we can do this workaround for functions:
+static RFBController *self;
+
+class AppLocker
+{
+public:
+ AppLocker() {
+ KApplication::kApplication()->lock();
+ }
+
+ ~AppLocker() {
+ KApplication::kApplication()->unlock();
+ }
+};
+
+static enum rfbNewClientAction newClientHook(struct _rfbClientRec *cl)
+{
+ AppLocker a;
+ return self->handleNewClient(cl);
+}
+
+static Bool passwordCheck(rfbClientPtr cl,
+ const char* encryptedPassword,
+ int len)
+{
+ AppLocker a;
+ return self->handleCheckPassword(cl, encryptedPassword, len);
+}
+
+static void keyboardHook(Bool down, KeySym keySym, rfbClientPtr)
+{
+ self->handleKeyEvent(down ? true : false, keySym);
+}
+
+static void pointerHook(int bm, int x, int y, rfbClientPtr)
+{
+ self->handlePointerEvent(bm, x, y);
+}
+
+static void clientGoneHook(rfbClientPtr)
+{
+ self->handleClientGone();
+}
+
+static void negotiationFinishedHook(rfbClientPtr cl)
+{
+ self->handleNegotiationFinished(cl);
+}
+
+static void inetdDisconnectHook()
+{
+ self->handleClientGone();
+}
+
+static void clipboardHook(char* str,int len, rfbClientPtr)
+{
+ self->clipboardToServer(QString::fromUtf8(str, len));
+}
+
+VNCEvent::~VNCEvent() {
+}
+
+Display *KeyboardEvent::dpy;
+signed char KeyboardEvent::modifiers[0x100];
+KeyCode KeyboardEvent::keycodes[0x100];
+KeyCode KeyboardEvent::leftShiftCode;
+KeyCode KeyboardEvent::rightShiftCode;
+KeyCode KeyboardEvent::altGrCode;
+const int KeyboardEvent::LEFTSHIFT = 1;
+const int KeyboardEvent::RIGHTSHIFT = 2;
+const int KeyboardEvent::ALTGR = 4;
+char KeyboardEvent::ModifierState;
+
+KeyboardEvent::KeyboardEvent(bool d, KeySym k) :
+ down(d),
+ keySym(k) {
+}
+
+void KeyboardEvent::initKeycodes() {
+ KeySym key,*keymap;
+ int i,j,minkey,maxkey,syms_per_keycode;
+
+ dpy = qt_xdisplay();
+
+ memset(modifiers,-1,sizeof(modifiers));
+
+ XDisplayKeycodes(dpy,&minkey,&maxkey);
+ ASSERT(minkey >= 8);
+ ASSERT(maxkey < 256);
+ keymap = (KeySym*) XGetKeyboardMapping(dpy, minkey,
+ (maxkey - minkey + 1),
+ &syms_per_keycode);
+ ASSERT(keymap);
+
+ for (i = minkey; i <= maxkey; i++)
+ for (j=0; j<syms_per_keycode; j++) {
+ key = keymap[(i-minkey)*syms_per_keycode+j];
+ if (key>=' ' && key<0x100 && i==XKeysymToKeycode(dpy,key)) {
+ keycodes[key]=i;
+ modifiers[key]=j;
+ }
+ }
+
+ leftShiftCode = XKeysymToKeycode(dpy, XK_Shift_L);
+ rightShiftCode = XKeysymToKeycode(dpy, XK_Shift_R);
+ altGrCode = XKeysymToKeycode(dpy, XK_Mode_switch);
+
+ XFree ((char *)keymap);
+}
+
+/* this function adjusts the modifiers according to mod (as from modifiers) and ModifierState */
+void KeyboardEvent::tweakModifiers(signed char mod, bool down) {
+
+ bool isShift = ModifierState & (LEFTSHIFT|RIGHTSHIFT);
+ if(mod < 0)
+ return;
+
+ if(isShift && mod != 1) {
+ if(ModifierState & LEFTSHIFT)
+ XTestFakeKeyEvent(dpy, leftShiftCode,
+ !down, CurrentTime);
+ if(ModifierState & RIGHTSHIFT)
+ XTestFakeKeyEvent(dpy, rightShiftCode,
+ !down, CurrentTime);
+ }
+
+ if(!isShift && mod==1)
+ XTestFakeKeyEvent(dpy, leftShiftCode,
+ down, CurrentTime);
+
+ if((ModifierState&ALTGR) && mod != 2)
+ XTestFakeKeyEvent(dpy, altGrCode,
+ !down, CurrentTime);
+ if(!(ModifierState&ALTGR) && mod==2)
+ XTestFakeKeyEvent(dpy, altGrCode,
+ down, CurrentTime);
+}
+
+void KeyboardEvent::exec() {
+#define ADJUSTMOD(sym,state) \
+ if(keySym==sym) { if(down) ModifierState|=state; else ModifierState&=~state; }
+
+ ADJUSTMOD(XK_Shift_L,LEFTSHIFT);
+ ADJUSTMOD(XK_Shift_R,RIGHTSHIFT);
+ ADJUSTMOD(XK_Mode_switch,ALTGR);
+
+ if(keySym>=' ' && keySym<0x100) {
+ KeyCode k;
+ if (down)
+ tweakModifiers(modifiers[keySym],True);
+ k = keycodes[keySym];
+ if (k != NoSymbol)
+ XTestFakeKeyEvent(dpy, k, down, CurrentTime);
+
+ if (down)
+ tweakModifiers(modifiers[keySym],False);
+ } else {
+ KeyCode k = XKeysymToKeycode(dpy, keySym );
+ if (k != NoSymbol)
+ XTestFakeKeyEvent(dpy, k, down, CurrentTime);
+ }
+}
+
+bool PointerEvent::initialized = false;
+Display *PointerEvent::dpy;
+int PointerEvent::buttonMask = 0;
+
+PointerEvent::PointerEvent(int b, int _x, int _y) :
+ button_mask(b),
+ x(_x),
+ y(_y) {
+ if (!initialized) {
+ initialized = true;
+ dpy = qt_xdisplay();
+ buttonMask = 0;
+ }
+}
+
+void PointerEvent::exec() {
+ QDesktopWidget *desktopWidget = QApplication::desktop();
+
+ int screen = desktopWidget->screenNumber();
+ if (screen < 0)
+ screen = 0;
+ XTestFakeMotionEvent(dpy, screen, x, y, CurrentTime);
+ for(int i = 0; i < 5; i++)
+ if ((buttonMask&(1<<i))!=(button_mask&(1<<i)))
+ XTestFakeButtonEvent(dpy,
+ i+1,
+ (button_mask&(1<<i))?True:False,
+ CurrentTime);
+
+ buttonMask = button_mask;
+}
+
+
+ClipboardEvent::ClipboardEvent(RFBController *c, const QString &ctext) :
+ controller(c),
+ text(QDeepCopy<QString>(ctext)) {
+}
+
+void ClipboardEvent::exec() {
+ if ((controller->lastClipboardDirection == RFBController::LAST_SYNC_TO_CLIENT) &&
+ (controller->lastClipboardText == text)) {
+ return;
+ }
+ controller->lastClipboardDirection = RFBController::LAST_SYNC_TO_SERVER;
+ controller->lastClipboardText = text;
+
+ controller->clipboard->setText(text, QClipboard::Clipboard);
+ controller->clipboard->setText(text, QClipboard::Selection);
+}
+
+
+KNotifyEvent::KNotifyEvent(const QString &n, const QString &d) :
+ name(n),
+ desc(d) {
+}
+
+KNotifyEvent::~KNotifyEvent() {
+}
+
+void KNotifyEvent::exec() {
+ KNotifyClient::event(name, desc);
+}
+
+SessionEstablishedEvent::SessionEstablishedEvent(RFBController *c) :
+ controller(c)
+{ }
+
+void SessionEstablishedEvent::exec() {
+ controller->sendSessionEstablished();
+}
+
+RFBController::RFBController(Configuration *c) :
+ allowDesktopControl(false),
+ lastClipboardDirection(LAST_SYNC_TO_SERVER),
+ configuration(c),
+ dialog( 0, "ConnectionDialog" ),
+ disableBackgroundPending(false),
+ disableBackgroundState(false),
+ closePending(false),
+ forcedClose(false)
+{
+ self = this;
+ connect(&dialog, SIGNAL(okClicked()), SLOT(dialogAccepted()));
+ connect(&dialog, SIGNAL(cancelClicked()), SLOT(dialogRefused()));
+ connect(&initIdleTimer, SIGNAL(timeout()), SLOT(checkAsyncEvents()));
+ connect(&idleTimer, SIGNAL(timeout()), SLOT(idleSlot()));
+
+ clipboard = QApplication::clipboard();
+ connect(clipboard, SIGNAL(selectionChanged()), this, SLOT(selectionChanged()));
+ connect(clipboard, SIGNAL(dataChanged()), this, SLOT(clipboardChanged()));
+
+ asyncQueue.setAutoDelete(true);
+
+ KeyboardEvent::initKeycodes();
+
+ char hostname[256];
+ if (gethostname(hostname, 255))
+ hostname[0] = 0;
+ hostname[255] = 0;
+ desktopName = i18n("%1@%2 (shared desktop)").arg(KUser().loginName()).arg(hostname);
+}
+
+RFBController::~RFBController()
+{
+ stopServer();
+}
+
+
+
+void RFBController::startServer(int inetdFd, bool xtestGrab)
+{
+ framebufferImage = XGetImage(qt_xdisplay(),
+ QApplication::desktop()->winId(),
+ 0,
+ 0,
+ QApplication::desktop()->width(),
+ QApplication::desktop()->height(),
+ AllPlanes,
+ ZPixmap);
+
+ int w = framebufferImage->width;
+ int h = framebufferImage->height;
+ char *fb = framebufferImage->data;
+
+ rfbLogEnable(0);
+ server = rfbGetScreen(0, 0, w, h,
+ framebufferImage->bits_per_pixel,
+ 8,
+ framebufferImage->bits_per_pixel/8);
+
+ server->paddedWidthInBytes = framebufferImage->bytes_per_line;
+
+ server->rfbServerFormat.bitsPerPixel = framebufferImage->bits_per_pixel;
+ server->rfbServerFormat.depth = framebufferImage->depth;
+ server->rfbServerFormat.trueColour = (CARD8) TRUE;
+ server->rfbServerFormat.bigEndian = (CARD8) ((framebufferImage->bitmap_bit_order == MSBFirst) ? TRUE : FALSE);
+
+ if ( server->rfbServerFormat.bitsPerPixel == 8 ) {
+ server->rfbServerFormat.redShift = 0;
+ server->rfbServerFormat.greenShift = 3;
+ server->rfbServerFormat.blueShift = 6;
+ server->rfbServerFormat.redMax = 7;
+ server->rfbServerFormat.greenMax = 7;
+ server->rfbServerFormat.blueMax = 3;
+ } else {
+ server->rfbServerFormat.redShift = 0;
+ if ( framebufferImage->red_mask )
+ while ( ! ( framebufferImage->red_mask & (1 << server->rfbServerFormat.redShift) ) )
+ server->rfbServerFormat.redShift++;
+ server->rfbServerFormat.greenShift = 0;
+ if ( framebufferImage->green_mask )
+ while ( ! ( framebufferImage->green_mask & (1 << server->rfbServerFormat.greenShift) ) )
+ server->rfbServerFormat.greenShift++;
+ server->rfbServerFormat.blueShift = 0;
+ if ( framebufferImage->blue_mask )
+ while ( ! ( framebufferImage->blue_mask & (1 << server->rfbServerFormat.blueShift) ) )
+ server->rfbServerFormat.blueShift++;
+ server->rfbServerFormat.redMax = framebufferImage->red_mask >> server->rfbServerFormat.redShift;
+ server->rfbServerFormat.greenMax = framebufferImage->green_mask >> server->rfbServerFormat.greenShift;
+ server->rfbServerFormat.blueMax = framebufferImage->blue_mask >> server->rfbServerFormat.blueShift;
+ }
+
+ server->frameBuffer = fb;
+ server->autoPort = TRUE;
+ server->inetdSock = inetdFd;
+
+ server->kbdAddEvent = keyboardHook;
+ server->ptrAddEvent = pointerHook;
+ server->newClientHook = newClientHook;
+ server->inetdDisconnectHook = inetdDisconnectHook;
+ server->passwordCheck = passwordCheck;
+ server->setXCutText = clipboardHook;
+
+ server->desktopName = desktopName.latin1();
+
+ if (!myCursor)
+ myCursor = rfbMakeXCursor(19, 19, (char*) cur, (char*) mask);
+ server->cursor = myCursor;
+
+ passwordChanged();
+
+ scanner = new XUpdateScanner(qt_xdisplay(),
+ QApplication::desktop()->winId(),
+ (unsigned char*)fb, w, h,
+ server->rfbServerFormat.bitsPerPixel,
+ server->paddedWidthInBytes,
+ !configuration->disableXShm());
+
+ rfbInitServer(server);
+ state = RFB_WAITING;
+
+ if (xtestGrab) {
+ disabler.disable = false;
+ XTestGrabControl(qt_xdisplay(), true);
+ }
+
+ rfbRunEventLoop(server, -1, TRUE);
+ initIdleTimer.start(IDLE_PAUSE);
+}
+
+void RFBController::stopServer(bool xtestUngrab)
+{
+ rfbScreenCleanup(server);
+ state = RFB_STOPPED;
+ delete scanner;
+
+ XDestroyImage(framebufferImage);
+
+ if (xtestUngrab) {
+ disabler.disable = true;
+ QTimer::singleShot(0, &disabler, SLOT(exec()));
+ }
+}
+
+void RFBController::connectionAccepted(bool aRC)
+{
+ if (state != RFB_CONNECTING)
+ return;
+
+ allowDesktopControl = aRC;
+ emit desktopControlSettingChanged(aRC);
+ initIdleTimer.stop();
+ idleTimer.start(IDLE_PAUSE);
+
+ server->rfbClientHead->clientGoneHook = clientGoneHook;
+ state = RFB_CONNECTED;
+ if (!server->rfbAuthPasswdData)
+ emit sessionEstablished(remoteIp);
+}
+
+void RFBController::acceptConnection(bool aRemoteControl)
+{
+ KNotifyClient::event("UserAcceptsConnection",
+ i18n("User accepts connection from %1")
+ .arg(remoteIp));
+
+ if (state != RFB_CONNECTING)
+ return;
+
+ connectionAccepted(aRemoteControl);
+ rfbStartOnHoldClient(server->rfbClientHead);
+}
+
+void RFBController::refuseConnection()
+{
+ KNotifyClient::event("UserRefusesConnection",
+ i18n("User refuses connection from %1")
+ .arg(remoteIp));
+
+ if (state != RFB_CONNECTING)
+ return;
+ rfbRefuseOnHoldClient(server->rfbClientHead);
+ state = RFB_WAITING;
+}
+
+// checks async events, returns true if client disconnected
+bool RFBController::checkAsyncEvents()
+{
+ bool closed = false;
+ bool backgroundActionRequired = false;
+ asyncMutex.lock();
+ VNCEvent *e;
+ for (e = asyncQueue.first(); e; e = asyncQueue.next())
+ e->exec();
+ asyncQueue.clear();
+ if (closePending) {
+ connectionClosed();
+ closed = true;
+ closePending = false;
+ }
+ if (disableBackgroundPending != disableBackgroundState)
+ backgroundActionRequired = true;
+ asyncMutex.unlock();
+
+ if (backgroundActionRequired && (!closed) && !configuration->disableBackground())
+ disableBackground(disableBackgroundPending);
+
+ return closed;
+}
+
+void RFBController::disableBackground(bool state) {
+ if (disableBackgroundState == state)
+ return;
+
+ disableBackgroundState = state;
+ DCOPRef ref("kdesktop", "KBackgroundIface");
+ ref.setDCOPClient(KApplication::dcopClient());
+
+ ref.send("setBackgroundEnabled(bool)", bool(!state));
+}
+
+void RFBController::connectionClosed()
+{
+ KNotifyClient::event("ConnectionClosed",
+ i18n("Closed connection: %1.")
+ .arg(remoteIp));
+
+ idleTimer.stop();
+ initIdleTimer.stop();
+ disableBackground(false);
+ state = RFB_WAITING;
+ if (forcedClose)
+ emit quitApp();
+ else
+ emit sessionFinished();
+}
+
+void RFBController::closeConnection()
+{
+ forcedClose = true;
+ if (state == RFB_CONNECTED) {
+ disableBackground(false);
+
+ if (!checkAsyncEvents()) {
+ asyncMutex.lock();
+ if (!closePending)
+ rfbCloseClient(server->rfbClientHead);
+ asyncMutex.unlock();
+ }
+ }
+ else if (state == RFB_CONNECTING)
+ refuseConnection();
+}
+
+void RFBController::enableDesktopControl(bool b) {
+ if (b != allowDesktopControl)
+ emit desktopControlSettingChanged(b);
+ allowDesktopControl = b;
+}
+
+void RFBController::idleSlot()
+{
+ if (state != RFB_CONNECTED)
+ return;
+ if (checkAsyncEvents() || forcedClose)
+ return;
+
+ rfbUndrawCursor(server);
+
+ QPtrList<Hint> v;
+ v.setAutoDelete(true);
+ QPoint p = QCursor::pos();
+ scanner->searchUpdates(v, p.y());
+
+ Hint *h;
+
+ for (h = v.first(); h != 0; h = v.next())
+ rfbMarkRectAsModified(server, h->left(),
+ h->top(),
+ h->right(),
+ h->bottom());
+
+ asyncMutex.lock();
+ if (!closePending)
+ defaultPtrAddEvent(0, p.x(),p.y(), server->rfbClientHead);
+ asyncMutex.unlock();
+
+ checkAsyncEvents(); // check 2nd time (see 3rd line)
+}
+
+void RFBController::dialogAccepted()
+{
+ dialog.hide();
+ acceptConnection(dialog.allowRemoteControl());
+}
+
+void RFBController::dialogRefused()
+{
+ refuseConnection();
+ dialog.hide();
+ emit sessionRefused();
+}
+
+bool checkPassword(const QString &p,
+ unsigned char *ochallenge,
+ const char *response,
+ int len) {
+
+ if ((len == 0) && (p.length() == 0))
+ return true;
+
+ char passwd[MAXPWLEN];
+ unsigned char challenge[CHALLENGESIZE];
+
+ memcpy(challenge, ochallenge, CHALLENGESIZE);
+ bzero(passwd, MAXPWLEN);
+ if (!p.isNull())
+ strncpy(passwd, p.latin1(),
+ (MAXPWLEN <= p.length()) ? MAXPWLEN : p.length());
+
+ vncEncryptBytes(challenge, passwd);
+ return memcmp(challenge, response, len) == 0;
+}
+
+bool RFBController::handleCheckPassword(rfbClientPtr cl,
+ const char *response,
+ int len)
+{
+
+ bool authd = false;
+
+ if (configuration->allowUninvitedConnections())
+ authd = checkPassword(configuration->password(),
+ cl->authChallenge, response, len);
+
+ if (!authd) {
+ QValueList<Invitation>::iterator it =
+ configuration->invitations().begin();
+ while (it != configuration->invitations().end()) {
+ if (checkPassword((*it).password(),
+ cl->authChallenge, response, len) &&
+ (*it).isValid()) {
+ authd = true;
+ configuration->removeInvitation(it);
+ break;
+ }
+ it++;
+ }
+ }
+
+ if (!authd) {
+ if (configuration->invitations().size() > 0) {
+ sendKNotifyEvent("InvalidPasswordInvitations",
+ i18n("Failed login attempt from %1: wrong password")
+ .arg(remoteIp));
+}
+ else
+ sendKNotifyEvent("InvalidPassword",
+ i18n("Failed login attempt from %1: wrong password")
+ .arg(remoteIp));
+ return FALSE;
+ }
+
+ asyncMutex.lock();
+ asyncQueue.append(new SessionEstablishedEvent(this));
+ asyncMutex.unlock();
+
+ return TRUE;
+}
+
+enum rfbNewClientAction RFBController::handleNewClient(rfbClientPtr cl)
+{
+ int socket = cl->sock;
+ cl->negotiationFinishedHook = negotiationFinishedHook;
+
+ QString host, port;
+ KSocketAddress *ksa = KExtendedSocket::peerAddress(socket);
+ if (ksa) {
+ hostent *he = 0;
+ KInetSocketAddress *kisa = (KInetSocketAddress*) ksa;
+ in_addr ia4 = kisa->hostV4();
+ he = gethostbyaddr((const char*)&ia4,
+ sizeof(ia4),
+ AF_INET);
+
+ if (he && he->h_name)
+ host = QString(he->h_name);
+ else
+ host = ksa->nodeName();
+ delete ksa;
+ }
+
+ if (state != RFB_WAITING) {
+ sendKNotifyEvent("TooManyConnections",
+ i18n("Connection refused from %1, already connected.")
+ .arg(host));
+ return RFB_CLIENT_REFUSE;
+ }
+ remoteIp = host;
+ state = RFB_CONNECTING;
+
+ if ((!configuration->askOnConnect()) &&
+ (configuration->invitations().size() == 0)) {
+ sendKNotifyEvent("NewConnectionAutoAccepted",
+ i18n("Accepted uninvited connection from %1")
+ .arg(remoteIp));
+
+ connectionAccepted(configuration->allowDesktopControl());
+ return RFB_CLIENT_ACCEPT;
+ }
+
+ sendKNotifyEvent("NewConnectionOnHold",
+ i18n("Received connection from %1, on hold (waiting for confirmation)")
+ .arg(remoteIp));
+
+ dialog.setRemoteHost(remoteIp);
+ dialog.setAllowRemoteControl( true );
+ dialog.setFixedSize(dialog.sizeHint());
+ dialog.show();
+ return RFB_CLIENT_ON_HOLD;
+}
+
+void RFBController::handleClientGone()
+{
+ asyncMutex.lock();
+ closePending = true;
+ asyncMutex.unlock();
+}
+
+void RFBController::handleNegotiationFinished(rfbClientPtr cl)
+{
+ asyncMutex.lock();
+ disableBackgroundPending = cl->disableBackground;
+ asyncMutex.unlock();
+}
+
+void RFBController::handleKeyEvent(bool down, KeySym keySym) {
+ if (!allowDesktopControl)
+ return;
+
+ asyncMutex.lock();
+ asyncQueue.append(new KeyboardEvent(down, keySym));
+ asyncMutex.unlock();
+}
+
+void RFBController::handlePointerEvent(int button_mask, int x, int y) {
+ if (!allowDesktopControl)
+ return;
+
+ asyncMutex.lock();
+ asyncQueue.append(new PointerEvent(button_mask, x, y));
+ asyncMutex.unlock();
+}
+
+
+void RFBController::clipboardToServer(const QString &ctext) {
+ if (!allowDesktopControl)
+ return;
+
+ asyncMutex.lock();
+ asyncQueue.append(new ClipboardEvent(this, ctext));
+ asyncMutex.unlock();
+}
+
+void RFBController::clipboardChanged() {
+ if (state != RFB_CONNECTED)
+ return;
+ if (clipboard->ownsClipboard())
+ return;
+
+ QString text = clipboard->text(QClipboard::Clipboard);
+
+ // avoid ping-pong between client&server
+ if ((lastClipboardDirection == LAST_SYNC_TO_SERVER) &&
+ (lastClipboardText == text))
+ return;
+ if ((text.length() > MAX_SELECTION_LENGTH) || text.isNull())
+ return;
+
+ lastClipboardDirection = LAST_SYNC_TO_CLIENT;
+ lastClipboardText = text;
+ QCString ctext = text.utf8();
+ rfbSendServerCutText(server, ctext.data(), ctext.length());
+}
+
+void RFBController::selectionChanged() {
+ if (state != RFB_CONNECTED)
+ return;
+ if (clipboard->ownsSelection())
+ return;
+
+ QString text = clipboard->text(QClipboard::Selection);
+ // avoid ping-pong between client&server
+ if ((lastClipboardDirection == LAST_SYNC_TO_SERVER) &&
+ (lastClipboardText == text))
+ return;
+ if ((text.length() > MAX_SELECTION_LENGTH) || text.isNull())
+ return;
+
+ lastClipboardDirection = LAST_SYNC_TO_CLIENT;
+ lastClipboardText = text;
+ QCString ctext = text.utf8();
+ rfbSendServerCutText(server, ctext.data(), ctext.length());
+}
+
+void RFBController::passwordChanged() {
+ bool authRequired = (!configuration->allowUninvitedConnections()) ||
+ (configuration->password().length() != 0) ||
+ (configuration->invitations().count() > 0);
+
+ server->rfbAuthPasswdData = (void*) (authRequired ? 1 : 0);
+}
+
+void RFBController::sendKNotifyEvent(const QString &n, const QString &d)
+{
+ asyncMutex.lock();
+ asyncQueue.append(new KNotifyEvent(n, d));
+ asyncMutex.unlock();
+}
+
+void RFBController::sendSessionEstablished()
+{
+ if (configuration->disableBackground())
+ disableBackground(true);
+ emit sessionEstablished(remoteIp);
+}
+
+#ifdef __osf__
+extern "C" Bool XShmQueryExtension(Display*);
+#endif
+
+bool RFBController::checkX11Capabilities() {
+ int bp1, bp2, majorv, minorv;
+ Bool r = XTestQueryExtension(qt_xdisplay(), &bp1, &bp2,
+ &majorv, &minorv);
+ if ((!r) || (((majorv*1000)+minorv) < 2002)) {
+ KMessageBox::error(0,
+ i18n("Your X11 Server does not support the required XTest extension version 2.2. Sharing your desktop is not possible."),
+ i18n("Desktop Sharing Error"));
+ return false;
+ }
+
+ return true;
+}
+
+
+XTestDisabler::XTestDisabler() :
+ disable(false) {
+}
+
+void XTestDisabler::exec() {
+ if (disable)
+ XTestDiscard(qt_xdisplay());
+}
+
+#include "rfbcontroller.moc"
diff --git a/krfb/krfb/rfbcontroller.h b/krfb/krfb/rfbcontroller.h
new file mode 100644
index 00000000..948456f2
--- /dev/null
+++ b/krfb/krfb/rfbcontroller.h
@@ -0,0 +1,221 @@
+/***************************************************************************
+ rfbcontroller.h
+ -------------------
+ begin : Sun Dec 9 2001
+ copyright : (C) 2001 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * Contains portions & concept from rfb's x0rfbserver.cc
+ * Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 RFBCONTROLLER_H
+#define RFBCONTROLLER_H
+
+#include "configuration.h"
+#include "connectiondialog.h"
+#include "xupdatescanner.h"
+#include <ksock.h>
+#include <qobject.h>
+#include <qtimer.h>
+#include <qmutex.h>
+
+#define HAVE_PTHREADS
+#include "rfb.h"
+
+#include <X11/Xlib.h>
+
+
+
+class QCloseEvent;
+class QClipboard;
+class RFBController;
+
+typedef enum {
+ RFB_STOPPED,
+ RFB_WAITING,
+ RFB_CONNECTING,
+ RFB_CONNECTED
+} RFBState;
+
+class VNCEvent {
+public:
+ virtual void exec() = 0;
+ virtual ~VNCEvent();
+};
+
+
+class KeyboardEvent : public VNCEvent {
+ bool down;
+ KeySym keySym;
+
+ static Display *dpy;
+ static signed char modifiers[0x100];
+ static KeyCode keycodes[0x100], leftShiftCode, rightShiftCode, altGrCode;
+ static const int LEFTSHIFT;
+ static const int RIGHTSHIFT;
+ static const int ALTGR;
+ static char ModifierState;
+
+ static void tweakModifiers(signed char mod, bool down);
+public:
+ static void initKeycodes();
+
+ KeyboardEvent(bool d, KeySym k);
+ virtual void exec();
+};
+
+class PointerEvent : public VNCEvent {
+ int button_mask, x, y;
+
+ static bool initialized;
+ static Display *dpy;
+ static int buttonMask;
+public:
+ PointerEvent(int b, int _x, int _y);
+ virtual void exec();
+};
+
+class ClipboardEvent : public VNCEvent {
+ RFBController *controller;
+ QString text;
+public:
+ ClipboardEvent(RFBController *c, const QString &text);
+ virtual void exec();
+};
+
+class KNotifyEvent : public VNCEvent {
+ QString name;
+ QString desc;
+public:
+ KNotifyEvent(const QString &n, const QString &d);
+ virtual ~KNotifyEvent();
+ virtual void exec();
+};
+
+class SessionEstablishedEvent : public VNCEvent {
+ RFBController *controller;
+public:
+ SessionEstablishedEvent(RFBController *c);
+ virtual void exec();
+};
+
+/**
+ * Manages sockets, drives the RGBConnection and triggers the connection
+ * dialog.
+ * The controller has three states: 'waiting for connection',
+ * 'waiting for confirmation' and 'connected'. In the first state socket and
+ * connection are null, in the second socket is set and in the last both are
+ * set.
+ * @author Tim Jansen
+ */
+class RFBController : public QObject {
+ Q_OBJECT
+
+ friend class SessionEstablishedEvent;
+ friend class ClipboardEvent;
+public:
+ RFBController(Configuration *c);
+ virtual ~RFBController();
+
+ RFBState state;
+
+ void acceptConnection(bool allowRemoteConnection);
+ void connectionAccepted(bool allowRemoteConnection);
+ void refuseConnection();
+ void connectionClosed();
+ bool handleCheckPassword(rfbClientPtr, const char *, int);
+ void handleKeyEvent(bool down, KeySym keySym);
+ void handlePointerEvent(int button_mask, int x, int y);
+ enum rfbNewClientAction handleNewClient(rfbClientPtr cl);
+ void clipboardToServer(const QString &text);
+ void handleClientGone();
+ void handleNegotiationFinished(rfbClientPtr cl);
+ int getPort();
+ void startServer(int inetdFd = -1, bool xtestGrab = true);
+
+ static bool checkX11Capabilities();
+
+public slots:
+ void passwordChanged();
+ void closeConnection();
+ void enableDesktopControl(bool c);
+
+signals:
+ void sessionEstablished(QString host);
+ void sessionFinished();
+ void sessionRefused();
+ void quitApp();
+ void desktopControlSettingChanged(bool);
+
+private:
+ void stopServer(bool xtestUngrab = true);
+ void sendKNotifyEvent(const QString &name, const QString &desc);
+ void sendSessionEstablished();
+ void disableBackground(bool state);
+
+ QString remoteIp;
+ volatile bool allowDesktopControl;
+
+ QTimer initIdleTimer;
+ QTimer idleTimer;
+
+ enum {
+ LAST_SYNC_TO_SERVER,
+ LAST_SYNC_TO_CLIENT
+ } lastClipboardDirection;
+ QString lastClipboardText;
+ QClipboard *clipboard;
+
+ Configuration *configuration;
+ XUpdateScanner *scanner;
+ ConnectionDialog dialog;
+
+ QString desktopName;
+
+ rfbScreenInfoPtr server;
+
+ XImage *framebufferImage;
+
+ QMutex asyncMutex;
+ QPtrList<VNCEvent> asyncQueue;
+
+ bool disableBackgroundPending; // background, as desired by libvncserver
+ bool disableBackgroundState; // real background state
+ bool closePending; // set when libvncserver detected close
+ bool forcedClose; // set when user closed connection
+private slots:
+ bool checkAsyncEvents();
+ void idleSlot();
+ void dialogAccepted();
+ void dialogRefused();
+ void selectionChanged();
+ void clipboardChanged();
+};
+
+/*
+ * Class to call XTestDiscard at idle time (because otherwise
+ * it will not work with QT)
+ */
+class XTestDisabler : public QObject {
+ Q_OBJECT
+public:
+ XTestDisabler();
+ bool disable;
+ Display *dpy;
+public slots:
+ void exec();
+};
+
+#endif
diff --git a/krfb/krfb/templates/cpp_template b/krfb/krfb/templates/cpp_template
new file mode 100644
index 00000000..6afef5d4
--- /dev/null
+++ b/krfb/krfb/templates/cpp_template
@@ -0,0 +1,16 @@
+/***************************************************************************
+ |FILENAME| - description
+ -------------------
+ begin : |DATE|
+ copyright : (C) |YEAR| by |AUTHOR|
+ email : |EMAIL|
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
diff --git a/krfb/krfb/templates/header_template b/krfb/krfb/templates/header_template
new file mode 100644
index 00000000..6afef5d4
--- /dev/null
+++ b/krfb/krfb/templates/header_template
@@ -0,0 +1,16 @@
+/***************************************************************************
+ |FILENAME| - description
+ -------------------
+ begin : |DATE|
+ copyright : (C) |YEAR| by |AUTHOR|
+ email : |EMAIL|
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
diff --git a/krfb/krfb/trayicon.cpp b/krfb/krfb/trayicon.cpp
new file mode 100644
index 00000000..220ff7c0
--- /dev/null
+++ b/krfb/krfb/trayicon.cpp
@@ -0,0 +1,138 @@
+/***************************************************************************
+ trayicon.cpp
+ -------------------
+ begin : Tue Dec 11 2001
+ copyright : (C) 2001-2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "trayicon.h"
+#include <qtooltip.h>
+#include <kstdaction.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kpopupmenu.h>
+
+KPassivePopup2::KPassivePopup2(QWidget *parent) :
+ KPassivePopup(parent){
+}
+
+void KPassivePopup2::hideEvent( QHideEvent *e )
+{
+ KPassivePopup::hideEvent(e);
+ emit hidden();
+}
+
+KPassivePopup2 *KPassivePopup2::message( const QString &caption, const QString &text,
+ const QPixmap &icon,
+ QWidget *parent)
+{
+ KPassivePopup2 *pop = new KPassivePopup2( parent);
+ pop->setView( caption, text, icon );
+ pop->show();
+
+ return pop;
+}
+
+
+TrayIcon::TrayIcon(KDialog *d, Configuration *c) :
+ KSystemTray(0, "krfb trayicon"),
+ configuration(c),
+ aboutDialog(d),
+ actionCollection(this),
+ quitting(false)
+{
+ KIconLoader *loader = KGlobal::iconLoader();
+ trayIconOpen = loader->loadIcon("eyes-open24", KIcon::User);
+ trayIconClosed = loader->loadIcon("eyes-closed24", KIcon::User);
+ setPixmap(trayIconClosed);
+ QToolTip::add(this, i18n("Desktop Sharing - connecting"));
+
+ manageInvitationsAction = new KAction(i18n("Manage &Invitations"), QString::null,
+ 0, this, SIGNAL(showManageInvitations()),
+ &actionCollection);
+ manageInvitationsAction->plug(contextMenu());
+
+ contextMenu()->insertSeparator();
+
+ enableControlAction = new KToggleAction(i18n("Enable Remote Control"));
+ enableControlAction->setCheckedState(i18n("Disable Remote Control"));
+ enableControlAction->plug(contextMenu());
+ enableControlAction->setEnabled(false);
+ connect(enableControlAction, SIGNAL(toggled(bool)), SIGNAL(enableDesktopControl(bool)));
+
+ contextMenu()->insertSeparator();
+
+ aboutAction = KStdAction::aboutApp(this, SLOT(showAbout()), &actionCollection);
+ aboutAction->plug(contextMenu());
+
+ show();
+}
+
+TrayIcon::~TrayIcon(){
+}
+
+void TrayIcon::showAbout() {
+ aboutDialog->show();
+}
+
+void TrayIcon::prepareQuit() {
+ quitting = true;
+}
+
+
+
+void TrayIcon::showConnectedMessage(QString host) {
+
+ setPixmap(trayIconOpen);
+ KPassivePopup2::message(i18n("Desktop Sharing"),
+ i18n("The remote user has been authenticated and is now connected."),
+ trayIconOpen,
+ this);
+ QToolTip::add(this, i18n("Desktop Sharing - connected with %1").arg(host));
+}
+
+void TrayIcon::showDisconnectedMessage() {
+ if (quitting)
+ return;
+
+ QToolTip::add(this, i18n("Desktop Sharing - disconnected"));
+ setPixmap(trayIconClosed);
+ KPassivePopup2 *p = KPassivePopup2::message(i18n("Desktop Sharing"),
+ i18n("The remote user has closed the connection."),
+ trayIconClosed,
+ this);
+ connect(p, SIGNAL(hidden()), this, SIGNAL(diconnectedMessageDisplayed()));
+}
+
+void TrayIcon::setDesktopControlSetting(bool b) {
+ enableControlAction->setEnabled(true);
+ enableControlAction->setChecked(b);
+}
+
+void TrayIcon::mousePressEvent(QMouseEvent *e)
+{
+ if (!rect().contains(e->pos()))
+ return;
+
+ if (e->button() == LeftButton) {
+ contextMenuAboutToShow(contextMenu());
+ contextMenu()->popup(e->globalPos());
+ }
+ else
+ KSystemTray::mousePressEvent(e);
+}
+
+#include "trayicon.moc"
diff --git a/krfb/krfb/trayicon.h b/krfb/krfb/trayicon.h
new file mode 100644
index 00000000..4e586efa
--- /dev/null
+++ b/krfb/krfb/trayicon.h
@@ -0,0 +1,90 @@
+/***************************************************************************
+ trayicon.h - description
+ -------------------
+ begin : Tue Dec 11 2001
+ copyright : (C) 2001-2002 by Tim Jansen
+ email : tim@tjansen.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 TRAYICON_H
+#define TRAYICON_H
+
+#include "configuration.h"
+
+#include <qwidget.h>
+#include <kpixmap.h>
+#include <kaction.h>
+#include <ksystemtray.h>
+#include <kpassivepopup.h>
+
+class KDialog;
+
+class KPassivePopup2 : public KPassivePopup {
+ Q_OBJECT
+public:
+ KPassivePopup2(QWidget *parent);
+ static KPassivePopup2 *message( const QString &caption, const QString &text,
+ const QPixmap &icon,
+ QWidget *parent);
+
+signals:
+ void hidden();
+
+protected:
+ /**
+ * Reimplemented to detect hide events.
+ */
+ virtual void hideEvent( QHideEvent *e );
+};
+
+/**
+ * Implements the trayicon.
+ * @author Tim Jansen
+ */
+
+class TrayIcon : public KSystemTray {
+ Q_OBJECT
+public:
+ TrayIcon(KDialog*, Configuration*);
+ ~TrayIcon();
+
+signals:
+ void showManageInvitations();
+ void diconnectedMessageDisplayed();
+ void enableDesktopControl(bool);
+
+public slots:
+ void prepareQuit();
+ void showConnectedMessage(QString host);
+ void showDisconnectedMessage();
+ void setDesktopControlSetting(bool);
+
+protected:
+ void mousePressEvent(QMouseEvent *e);
+
+private:
+
+ KPixmap trayIconOpen;
+ KPixmap trayIconClosed;
+ Configuration *configuration;
+ KDialog* aboutDialog;
+ KActionCollection actionCollection;
+ KAction* manageInvitationsAction;
+ KAction* aboutAction;
+ KToggleAction* enableControlAction;
+ bool quitting;
+
+private slots:
+ void showAbout();
+};
+
+#endif
diff --git a/krfb/krfb/xupdatescanner.cc b/krfb/krfb/xupdatescanner.cc
new file mode 100644
index 00000000..1c4a0fad
--- /dev/null
+++ b/krfb/krfb/xupdatescanner.cc
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+/*
+ * December 15th 2001: removed coments, mouse pointer options and some
+ * other stuff
+ * January 10th 2002: improved hint creation (join adjacent hints)
+ * February 20th: use only partial tiles
+ * January 21st 2003: remember last modified scanlines, and scan them and
+ * in every cycle, reduce scanlines to every 35th
+ * January 21st 2003: scan lines around the cursor in every cycle
+ *
+ * Tim Jansen <tim@tjansen.de>
+ */
+
+#include <kdebug.h>
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <stdlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+#include <string.h>
+#include <assert.h>
+
+#include "xupdatescanner.h"
+
+/* ../../krfb/libvncserver/rfb.h */
+#ifdef Bool
+#undef Bool
+#endif
+#define Bool int
+
+
+#define SCANLINES 35
+unsigned int scanlines[SCANLINES] = { 0, 16, 8, 24,
+ 33, 4, 20, 12, 28,
+ 10, 26, 18, 34, 2,
+ 22, 6, 30, 14,
+ 1, 17, 32, 9, 25,
+ 7, 23, 15, 31,
+ 19, 3, 27, 11,
+ 29, 13, 5, 21 };
+#define MAX_ADJ_TOLERANCE 8
+
+#define MAX_RECENT_HITS 12
+unsigned int recentHitScanlines[MAX_RECENT_HITS];
+
+#define CURSOR_SCANLINES 5
+int cursorScanlines[CURSOR_SCANLINES] = {
+ -10, -4, 0, 4, 10
+};
+
+
+
+
+XUpdateScanner::XUpdateScanner(Display *_dpy,
+ Window _window,
+ unsigned char *_fb,
+ int _width,
+ int _height,
+ int _bitsPerPixel,
+ int _bytesPerLine,
+ bool useXShm) :
+ dpy(_dpy),
+ window(_window),
+ fb(_fb),
+ width(_width),
+ height(_height),
+ bitsPerPixel(_bitsPerPixel),
+ bytesPerLine(_bytesPerLine),
+ tileWidth(32),
+ tileHeight(32),
+ count (0),
+ scanline(NULL),
+ tile(NULL)
+{
+ useShm = useXShm && XShmQueryExtension(dpy);
+ if (useShm) {
+ int major, minor;
+ Bool pixmaps;
+ if ((!XShmQueryVersion(dpy, &major, &minor, &pixmaps)) || !pixmaps)
+ useShm = false;
+ }
+
+ if (useShm) {
+ tile = XShmCreateImage(dpy,
+ DefaultVisual( dpy, 0 ),
+ bitsPerPixel,
+ ZPixmap,
+ NULL,
+ &shminfo_tile,
+ tileWidth,
+ tileHeight);
+
+ shminfo_tile.shmid = shmget(IPC_PRIVATE,
+ tile->bytes_per_line * tile->height,
+ IPC_CREAT | 0777);
+ shminfo_tile.shmaddr = tile->data = (char *)
+ shmat(shminfo_tile.shmid, 0, 0);
+ shminfo_tile.readOnly = False;
+
+ XShmAttach(dpy, &shminfo_tile);
+ }
+ else {
+ int tlen = tileWidth*(bitsPerPixel/8);
+ void *data = malloc(tlen*tileHeight);
+
+ tile = XCreateImage(dpy,
+ DefaultVisual(dpy, 0),
+ bitsPerPixel,
+ ZPixmap,
+ 0,
+ (char*)data,
+ tileWidth,
+ tileHeight,
+ 8,
+ tlen);
+ }
+
+ tilesX = (width + tileWidth - 1) / tileWidth;
+ tilesY = (height + tileHeight - 1) / tileHeight;
+ tileMap = new bool[tilesX * tilesY];
+ tileRegionMap = new struct TileChangeRegion[tilesX * tilesY];
+
+ unsigned int i;
+ for (i = 0; i < tilesX * tilesY; i++)
+ tileMap[i] = false;
+
+ if (useShm) {
+ scanline = XShmCreateImage(dpy,
+ DefaultVisual(dpy, 0),
+ bitsPerPixel,
+ ZPixmap,
+ NULL,
+ &shminfo_scanline,
+ width,
+ 1);
+
+ shminfo_scanline.shmid = shmget(IPC_PRIVATE,
+ scanline->bytes_per_line,
+ IPC_CREAT | 0777);
+ shminfo_scanline.shmaddr = scanline->data = (char *)
+ shmat( shminfo_scanline.shmid, 0, 0 );
+ shminfo_scanline.readOnly = False;
+
+ XShmAttach(dpy, &shminfo_scanline);
+ }
+ else {
+ int slen = width*(bitsPerPixel/8);
+ void *data = malloc(slen);
+ scanline = XCreateImage(dpy,
+ DefaultVisual(dpy, 0),
+ bitsPerPixel,
+ ZPixmap,
+ 0,
+ (char*)data,
+ width,
+ 1,
+ 8,
+ slen);
+ }
+
+ for (int i = 0; i < MAX_RECENT_HITS; i++)
+ recentHitScanlines[i] = i;
+}
+
+
+XUpdateScanner::~XUpdateScanner()
+{
+ if (useShm) {
+ XShmDetach(dpy, &shminfo_scanline);
+ XDestroyImage(scanline);
+ shmdt(shminfo_scanline.shmaddr);
+ shmctl(shminfo_scanline.shmid, IPC_RMID, 0);
+ XShmDetach(dpy, &shminfo_tile);
+ XDestroyImage(tile);
+ shmdt(shminfo_tile.shmaddr);
+ shmctl(shminfo_tile.shmid, IPC_RMID, 0);
+ }
+ else {
+ free(tile->data);
+ free(scanline->data);
+ XDestroyImage(scanline);
+ XDestroyImage(tile);
+ }
+ delete [] tileMap;
+ delete [] tileRegionMap;
+}
+
+
+// returns true if last line changed. this is used to re-scan the tile under
+// this one because it is likely to be modified but missed by the probe
+bool XUpdateScanner::copyTile(int x, int y, int tx, int ty)
+{
+ unsigned int maxWidth = width - x;
+ unsigned int maxHeight = height - y;
+ if (maxWidth > tileWidth)
+ maxWidth = tileWidth;
+ if (maxHeight > tileHeight)
+ maxHeight = tileHeight;
+
+ if (useShm) {
+ if ((maxWidth == tileWidth) && (maxHeight == tileHeight)) {
+ XShmGetImage(dpy, window, tile, x, y, AllPlanes);
+ } else {
+ XGetSubImage(dpy, window, x, y, maxWidth, maxHeight,
+ AllPlanes, ZPixmap, tile, 0, 0);
+ }
+ }
+ else
+ XGetSubImage(dpy, window, x, y, maxWidth, maxHeight,
+ AllPlanes, ZPixmap, tile, 0, 0);
+
+ unsigned int line;
+ int pixelsize = bitsPerPixel >> 3;
+ unsigned char *src = (unsigned char*) tile->data;
+ unsigned char *dest = fb + y * bytesPerLine + x * pixelsize;
+
+ unsigned char *ssrc = src;
+ unsigned char *sdest = dest;
+ int firstLine = maxHeight;
+
+ for (line = 0; line < maxHeight; line++) {
+ if (memcmp(sdest, ssrc, maxWidth * pixelsize)) {
+ firstLine = line;
+ break;
+ }
+ ssrc += tile->bytes_per_line;
+ sdest += bytesPerLine;
+ }
+
+ if (firstLine == maxHeight) {
+ tileMap[tx + ty * tilesX] = false;
+ return false;
+ }
+
+ unsigned char *msrc = src + (tile->bytes_per_line * maxHeight);
+ unsigned char *mdest = dest + (bytesPerLine * maxHeight);
+ int lastLine = firstLine;
+
+ for (line = maxHeight-1; line > firstLine; line--) {
+ msrc -= tile->bytes_per_line;
+ mdest -= bytesPerLine;
+ if (memcmp(mdest, msrc, maxWidth * pixelsize)) {
+ lastLine = line;
+ break;
+ }
+ }
+
+ for (line = firstLine; line <= lastLine; line++) {
+ memcpy(sdest, ssrc, maxWidth * pixelsize );
+ ssrc += tile->bytes_per_line;
+ sdest += bytesPerLine;
+ }
+
+ struct TileChangeRegion *r = &tileRegionMap[tx + (ty * tilesX)];
+ r->firstLine = firstLine;
+ r->lastLine = lastLine;
+
+ return lastLine == (maxHeight-1);
+}
+
+void XUpdateScanner::copyAllTiles()
+{
+ for (unsigned int y = 0; y < tilesY; y++) {
+ for (unsigned int x = 0; x < tilesX; x++) {
+ if (tileMap[x + y * tilesX])
+ if (copyTile(x*tileWidth, y*tileHeight, x, y) &&
+ ((y+1) < tilesY))
+ tileMap[x + (y+1) * tilesX] = true;
+ }
+ }
+
+}
+
+void XUpdateScanner::createHintFromTile(int x, int y, int th, Hint &hint)
+{
+ unsigned int w = width - x;
+ unsigned int h = height - y;
+ if (w > tileWidth)
+ w = tileWidth;
+ if (h > th)
+ h = th;
+
+ hint.x = x;
+ hint.y = y;
+ hint.w = w;
+ hint.h = h;
+}
+
+void XUpdateScanner::addTileToHint(int x, int y, int th, Hint &hint)
+{
+ unsigned int w = width - x;
+ unsigned int h = height - y;
+ if (w > tileWidth)
+ w = tileWidth;
+ if (h > th)
+ h = th;
+
+ if (hint.x > x) {
+ hint.w += hint.x - x;
+ hint.x = x;
+ }
+
+ if (hint.y > y) {
+ hint.h += hint.y - y;
+ hint.y = y;
+ }
+
+ if ((hint.x+hint.w) < (x+w)) {
+ hint.w = (x+w) - hint.x;
+ }
+
+ if ((hint.y+hint.h) < (y+h)) {
+ hint.h = (y+h) - hint.y;
+ }
+}
+
+static void printStatistics(Hint &hint) {
+ static int snum = 0;
+ static float ssum = 0.0;
+
+ int oX0 = hint.x & 0xffffffe0;
+ int oY0 = hint.y & 0xffffffe0;
+ int oX2 = (hint.x+hint.w) & 0x1f;
+ int oY2 = (hint.y+hint.h) & 0x1f;
+ int oX3 = (((hint.x+hint.w) | 0x1f) + ((oX2 == 0) ? 0 : 1)) & 0xffffffe0;
+ int oY3 = (((hint.y+hint.h) | 0x1f) + ((oY2 == 0) ? 0 : 1)) & 0xffffffe0;
+ float s0 = hint.w*hint.h;
+ float s1 = (oX3-oX0)*(oY3-oY0);
+ float p = (100*s0/s1);
+ ssum += p;
+ snum++;
+ float avg = ssum / snum;
+ kdDebug() << "avg size: "<< avg <<"%"<<endl;
+}
+
+void XUpdateScanner::flushHint(int x, int y, int &x0,
+ Hint &hint, QPtrList<Hint> &hintList)
+{
+ if (x0 < 0)
+ return;
+
+ x0 = -1;
+
+ assert (hint.w > 0);
+ assert (hint.h > 0);
+
+ //printStatistics(hint);
+
+ hintList.append(new Hint(hint));
+}
+
+void XUpdateScanner::createHints(QPtrList<Hint> &hintList)
+{
+ Hint hint;
+ int x0 = -1;
+
+ for (int y = 0; y < tilesY; y++) {
+ int x;
+ for (x = 0; x < tilesX; x++) {
+ int idx = x + y * tilesX;
+ if (tileMap[idx]) {
+ int ty = tileRegionMap[idx].firstLine;
+ int th = tileRegionMap[idx].lastLine - ty +1;
+ if (x0 < 0) {
+ createHintFromTile(x * tileWidth,
+ (y * tileHeight) + ty,
+ th,
+ hint);
+ x0 = x;
+
+ } else {
+ addTileToHint(x * tileWidth,
+ (y * tileHeight) + ty,
+ th,
+ hint);
+ }
+ }
+ else
+ flushHint(x, y, x0, hint, hintList);
+ }
+ flushHint(x, y, x0, hint, hintList);
+ }
+}
+
+void XUpdateScanner::testScanline(int y, bool rememberHits) {
+ if (y < 0)
+ return;
+ if (y >= (int)height)
+ return;
+
+ int x = 0;
+ bool hit = false;
+ if (useShm)
+ XShmGetImage(dpy, window, scanline, 0, y, AllPlanes);
+ else
+ XGetSubImage(dpy, window, 0, y, width, 1,
+ AllPlanes, ZPixmap, scanline, 0, 0);
+
+ while (x < width) {
+ int pixelsize = bitsPerPixel >> 3;
+ unsigned char *src = (unsigned char*) scanline->data +
+ x * pixelsize;
+ unsigned char *dest = fb +
+ y * bytesPerLine + x * pixelsize;
+ int w = (x + 32) > width ? (width-x) : 32;
+ if (memcmp(dest, src, w * pixelsize)) {
+ hit = true;
+ tileMap[(x / tileWidth) +
+ (y / tileHeight) * tilesX] = true;
+ }
+ x += 32;
+ }
+
+ if (!rememberHits)
+ return;
+
+ for (int i = 1; i < MAX_RECENT_HITS; i++)
+ recentHitScanlines[i-1] = recentHitScanlines[i];
+ recentHitScanlines[MAX_RECENT_HITS-1] = y;
+}
+
+void XUpdateScanner::searchUpdates(QPtrList<Hint> &hintList, int ptrY)
+{
+ count++;
+ count %= SCANLINES;
+
+ unsigned int i;
+ unsigned int y;
+
+ for (i = 0; i < (tilesX * tilesY); i++) {
+ tileMap[i] = false;
+ }
+
+ // test last scanlines with hits
+ for (i = 0; i < MAX_RECENT_HITS; i++)
+ testScanline(recentHitScanlines[i], true);
+
+ // test scanlines around the cursor
+ for (i = 0; i < CURSOR_SCANLINES; i++)
+ testScanline(ptrY+cursorScanlines[i], false);
+ // test last/first line of the tiles around the cursor
+ // (assumes tileHeight = 32)
+ testScanline((ptrY&0xffe0)-1, false);
+ testScanline((ptrY|0x1f)+1, false);
+
+ // test every SCANLINESth scanline
+ y = scanlines[count];
+ while (y < (int)height) {
+ testScanline(y, true);
+ y += SCANLINES;
+ }
+
+ copyAllTiles();
+
+ createHints(hintList);
+}
+
+
+
diff --git a/krfb/krfb/xupdatescanner.h b/krfb/krfb/xupdatescanner.h
new file mode 100644
index 00000000..535b2ac9
--- /dev/null
+++ b/krfb/krfb/xupdatescanner.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2000 heXoNet Support GmbH, D-66424 Homburg.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef _hexonet_rfb_XUpdateScanner_h_
+#define _hexonet_rfb_XUpdateScanner_h_
+
+#include <qptrlist.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XShm.h>
+
+
+class Hint {
+ public:
+ int x, y, w, h;
+
+ Hint() :
+ x(0),
+ y(0),
+ w(0),
+ h(0)
+ {}
+ Hint(Hint &h) :
+ x(h.x),
+ y(h.y),
+ w(h.w),
+ h(h.h)
+ {
+ }
+ int left() {
+ return x;
+ }
+ int right() {
+ return x+w;
+ }
+ int top() {
+ return y;
+ }
+ int bottom() {
+ return y+h;
+ }
+};
+
+struct TileChangeRegion {
+ short firstLine, lastLine;
+};
+
+
+class XUpdateScanner
+{
+ public:
+ XUpdateScanner( Display *_dpy,
+ Window _window,
+ unsigned char *_fb,
+ int _width, int _height,
+ int _bitsPerPixel, int _bytesPerLine,
+ bool useXShm);
+
+ ~XUpdateScanner();
+
+ // hitList: returns list of changes
+ // ptrY: ptrY: position of the cursor
+ void searchUpdates( QPtrList<Hint> &hintList, int ptrY);
+
+ private:
+ void testScanline(int y, bool rememberHits);
+ bool copyTile(int x, int y, int tx, int ty);
+ void copyAllTiles();
+ void flushHint(int x, int y, int &x0, Hint &hint,
+ QPtrList<Hint> &hintList);
+ void createHints(QPtrList<Hint> &hintList);
+ void addTileToHint(int x, int y, int th, Hint &hint);
+ void createHintFromTile(int x, int y, int th, Hint &hint);
+
+ Display *dpy;
+ Window window;
+ unsigned char *fb;
+ int width, height;
+ int bitsPerPixel, bytesPerLine;
+ unsigned int tileWidth, tileHeight;
+ unsigned int count;
+ bool useShm;
+
+ XImage *scanline;
+ XShmSegmentInfo shminfo_scanline;
+
+ XImage *tile;
+ XShmSegmentInfo shminfo_tile;
+
+ unsigned int tilesX, tilesY;
+ bool *tileMap;
+ struct TileChangeRegion *tileRegionMap;
+};
+
+
+#endif // _hexonet_rfb_XUpdateScanner_h_
diff --git a/krfb/krfb_httpd/Makefile.am b/krfb/krfb_httpd/Makefile.am
new file mode 100644
index 00000000..0ab9015c
--- /dev/null
+++ b/krfb/krfb_httpd/Makefile.am
@@ -0,0 +1,3 @@
+bin_SCRIPTS = krfb_httpd
+kde_services_DATA = kinetd_krfb_httpd.desktop
+
diff --git a/krfb/krfb_httpd/kinetd_krfb_httpd.desktop b/krfb/krfb_httpd/kinetd_krfb_httpd.desktop
new file mode 100644
index 00000000..b19b62a1
--- /dev/null
+++ b/krfb/krfb_httpd/kinetd_krfb_httpd.desktop
@@ -0,0 +1,96 @@
+[Desktop Entry]
+Type=Service
+
+ServiceTypes=KInetDModule
+Exec=krfb_httpd
+X-KDE-FactoryName=kinetd
+X-KDE-KINETD-id=krfb_httpd
+X-KDE-KINETD-port=5800
+X-KDE-KINETD-autoPortRange=10
+X-KDE-KINETD-enabled=false
+X-KDE-KINETD-argument=--kinetd
+X-KDE-KINETD-multiInstance=false
+X-KDE-KINETD-serviceLifetime=1200
+
+Name=KRfb Micro Httpd
+Name[ar]=KRfb ميكرو httpd
+Name[bg]=Микро уеб Ñървър (KRfb - httpd)
+Name[bn]=কে-আর-à¦à¦«-বি মাইকà§à¦°à§‹ à¦à¦‡à¦šà¦Ÿà¦¿à¦Ÿà¦¿à¦ªà¦¿à¦¡à¦¿
+Name[cs]=KRfb mikro Httpd
+Name[cy]=Meicro-Httpd KRfb
+Name[da]=KRfb Micro-Httpd
+Name[de]=KRfb Mikro-Httpd
+Name[et]=KRfb mikro-httpd
+Name[fa]=ریزشبح قام KRfb
+Name[fr]=Micro serveur httpd de KRfb
+Name[gl]=KRfg Micro-Httpd
+Name[hi]=KRfb माइकà¥à¤°à¥‹ Httpd
+Name[hu]=KRfb mini-httpd
+Name[is]=KRfb Micro vefþjónn
+Name[ja]=KRfb マイクロ Httpd
+Name[kk]=KRfb шағын Httpd
+Name[ms]=Httpd Mikro KRfb
+Name[nb]=KRfb mikro- http-nisse
+Name[nds]=KRfb-Micro-Httpd
+Name[ne]=KRfb माइकà¥à¤°à¥‹ Httpd
+Name[nl]=KRfb micro webserver
+Name[nn]=KRfb-mikro-http-nisse
+Name[pl]=Mikroskopijny serwer HTTP dla KRfb
+Name[pt]=Micro Httpd do KRfb
+Name[ru]=KRfb микро Httpd
+Name[sv]=Krfb mikro-HTTP demon
+Name[tr]=KRfb Mini Httpd
+Name[uk]=KRfb мікро Httpd
+Name[zh_CN]=KRfb å¾® Httpd
+Name[zh_HK]=KRfb 微型網é ä¼ºæœå™¨
+Comment=A micro http daemon for krfb that serves the VNC viewer applet.
+Comment[ar]=مراقب httpd صغير لـkrfb الذي يخدم بريمج عرض VNC.
+Comment[bg]=Малък уеб Ñървър, който обÑлужва аплета за преглед на VNC
+Comment[bn]=কে-আর-à¦à¦«-বির জনà§à¦¯ à¦à¦•à¦Ÿà¦¿ মাইকà§à¦°à§‹ à¦à¦‡à¦šà¦Ÿà¦¿à¦Ÿà¦¿à¦ªà¦¿ ডিমন যে ভি-à¦à¦¨-সি পà§à¦°à¦¦à¦°à§à¦¶à¦• অà§à¦¯à¦¾à¦ªà¦²à§‡à¦Ÿ সরবরাহ করে।
+Comment[bs]=Mikro HTTP demon za krfb koji služi za applet VNC preglednika.
+Comment[ca]=Un micro-dimoni http per a krfb que serveix l'aplet visor VNC.
+Comment[cs]=Mikro HTTP server pro krfb sloužící VNC prohlížeÄi.
+Comment[cy]=Meicro-daemon http sy'n gwasanaethu'r rhaglennig gwelydd VNC
+Comment[da]=En mikro http-dæmon for krfb der betjener VNC visningsappletten.
+Comment[de]=Ein Mikro-HTTP-Server für krfb, der als Miniprogramm zu VNC-Anzeige dient.
+Comment[el]=Ένας μικÏός δαίμονας http για το krfb που εξυπηÏετεί τη μικÏοεφαÏμογή Ï€Ïοβολής VNC.
+Comment[es]=Un micro demonio http para krfb que sirve de applet de visor VNC.
+Comment[et]=Mikro-HTTP deemon KRfb jaoks, mis teenindab VNC apletti.
+Comment[eu]=VNC ikustaile applet-a zerbitzatzen duen krfb-rako micro http deabrua.
+Comment[fa]=یک ریزشبح قام برای krfb، برای خدمت دادن به برنامک مشاهده‌گر VNC
+Comment[fi]=Pieni http-palvelin krfb-ohjelmalle, joka jakaa VNC-näyttäjäsovelmaa
+Comment[fr]=Un mini serveur http pour KRfb dédié à l'applet d'affichage de bureaux VNC.
+Comment[gl]=Un micro demo de HTTP para krfb que serve unha applet de visualización de VNC
+Comment[he]=שרת http זעיר עבור krfb שמשרת ×ת יישומון תצוגת VNC.
+Comment[hi]=krfb के लिठà¤à¤• माइकà¥à¤°à¥‹ à¤à¤šà¤Ÿà¥€à¤Ÿà¥€à¤ªà¥€ डीमॉन जो वीà¤à¤¨à¤¸à¥€ वà¥à¤¯à¥‚अर à¤à¤ªà¥à¤²à¥‡à¤Ÿ को सरà¥à¤µ करता है.
+Comment[hu]=Mini HTTP-szolgáltatás a KRfb-hez, a VNC nézegető használatához.
+Comment[is]=Verulega lítill vefþjónn sem veitir aðgang í VNC.
+Comment[it]=Un micro demone http per krfb che server l'applet di VNC viewer.
+Comment[ja]=krfb 用ã®ãƒžã‚¤ã‚¯ãƒ­ http デーモン (VNC ビューアアプレットをæä¾›)
+Comment[ka]=მიკრრhttp დემáƒáƒœáƒ˜ krfbსთვის, რáƒáƒ›áƒ”ლიც ემსáƒáƒ®áƒ£áƒ áƒ”ბრVNC მხილველის áƒáƒžáƒšáƒ”ტს.
+Comment[kk]=VNC қарау апплетті қамтамаÑыз ететін krfb үшін шағын http қызметі.
+Comment[km]=ដáŸáž˜áž·áž“ http ážáž¼áž…​មួយ​សម្រាប់ krfb ដែល​បម្រើ​អាប់ភ្លáŸážâ€‹áž˜áž¾áž› VNC ។
+Comment[lt]=Micro http tarnyba skirta krfb ir aptarnaujanti VNC žiūriklį.
+Comment[mk]=http-микродаемон за krfb кој Ñлужи на аплетот на VNC-прегледувачот.
+Comment[ms]=Daemon http miKro untuk krfb yang melayan aplet pelihat VNC.
+Comment[nb]=En bitteliten http-nisse for krfb som hjelper visningsprogrammet for VNC.
+Comment[nds]=En lierlütt HTTP-Dämoon för krfb, de as VNC-Kiekerlüttprogramm bruukt warrt.
+Comment[ne]=krfb का लागि माइकà¥à¤°à¥‹ http डेइमन जसले VNC दरà¥à¤¶à¤• à¤à¤ªà¥à¤²à¥‡à¤Ÿ सेवा गरà¥à¤¦à¤›
+Comment[nl]=Een micro webserver voor krfb dat de VNC weergave-applet beschikbaar stelt.
+Comment[nn]=Ein ørliten http-nisse for krfb som hjelper visingsprogrammet for VNC.
+Comment[pl]=Mikroskopijny serwer HTTP dla KRfb obsługującego aplet przeglądarki VNC.
+Comment[pt]=Um micro-servidor de HTTP para o krfb que serve a 'applet' de visualização de VNC.
+Comment[pt_BR]=Um micro daemon de http, para o krfb, que serve o miniaplicativo de visualização do VNC.
+Comment[ru]=Микро-Ñервер http Ð´Ð»Ñ krfb, который обÑлуживает аплет проÑмотра VNC.
+Comment[sk]=Mikro http démon pre krfb ktorý ovláda prehliadací applet VNC.
+Comment[sl]=Mikro http strežnik za krfb, ki streže ogledovalnikom VNC.
+Comment[sr]=Микро http демон за krfb који опÑлужује аплет VNC приказивача.
+Comment[sr@Latn]=Mikro http demon za krfb koji opslužuje aplet VNC prikazivaÄa.
+Comment[sv]=En mikro-HTTP demon för Krfb som hanterar VNC-visningsminiprogrammet
+Comment[ta]=krfb கà¯à®•à®¾à®© நà¯à®£à¯à®£à®¿à®¯ http அத௠VNC சேவையை கà¯à®±à¯à®ªà¯à®ªà®¯à®©à¯ பாரà¯à®µà¯ˆà®¯à®¾à®³à®°à¯.
+Comment[tg]=Ðзозили micro http барои krfb, ки хидматраÑони VNC апплети хидматраÑон мебошад.
+Comment[tr]=VNC izleyici programcığını çalıştıran mini bir web sunucu.
+Comment[uk]=Мікродаемон http Ð´Ð»Ñ krfb, Ñкий обÑлуговує аплет переглÑдача VNC.
+Comment[zh_CN]=æ”¯æŒ VNC 查看器å°ç¨‹åºçš„ krfb å° http 守护程åº
+Comment[zh_HK]=用於 krfb,æœå‹™ VNC 檢視å°ç¨‹å¼çš„微型網é ä¼ºæœå™¨
+Comment[zh_TW]=æœå‹™ VNC çš„å°åž‹ç¶²é ä¼ºæœå™¨
diff --git a/krfb/krfb_httpd/krfb_httpd b/krfb/krfb_httpd/krfb_httpd
new file mode 100644
index 00000000..ee30fd9e
--- /dev/null
+++ b/krfb/krfb_httpd/krfb_httpd
@@ -0,0 +1,75 @@
+#! /usr/bin/env bash
+
+if [ "$1" = "--kinetd" ]; then
+ # redirect stdin and stdout to the inetd socket.
+ exec <&$2 >&$2
+fi
+
+read request url httptype || exit 0
+url="${url/
+/}"
+httptype="${httptype/
+/}"
+
+if [ "x$httptype" != "x" ]; then
+ line="x"
+ while [ -n "$line" ]; do
+ read line || exit 0
+ line="${line/
+/}"
+ done
+fi
+# echo "url = $url, request = $request" >> /tmp/httpd.log
+case "$url" in
+/)
+ # We need the size of the display for the current applet.
+ size=`xdpyinfo -display :0| grep dimensions:|head -n 1|sed -e "s/.*dimensions: *//" -e "s/ pixels.*//"`
+ width=`echo $size|sed -e "s/x.*//"`
+ height=`echo $size|sed -e "s/.*x//"`
+ # The VNC menubar is 20 pixels high ...
+ height=$((height+20))
+
+ port=`dcop kded kinetd port krfb`
+ if [ "$port" == "-1" ]; then
+ port=5900
+ fi
+
+ ctype="text/html"
+ content="
+<HTML><HEAD><TITLE>$LOGNAME's desktop</TITLE></HEAD>
+<BODY>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar WIDTH=$width HEIGHT=$height>
+ <param name=PORT value=$port>
+</APPLET>
+</BODY></HTML>"
+ ;;
+*.jar|*.class)
+ # Use basename to make sure we have just a filename, not ../../...
+ url="`basename "$url"`"
+ ctype="application/octet-stream"
+ cfile="/usr/share/vnc/classes/$url"
+ content="FILE"
+ ;;
+esac
+
+if [ "x$httptype" != "x" ]; then
+ echo "HTTP/1.0 200 OK"
+ echo "Content-Type: $ctype"
+ if [ "$content" == "FILE" ]; then
+ clen=`wc -c "$cfile"`
+ else
+ clen=`echo "$content"|wc -c`
+ fi
+ echo "Content-Length: $clen"
+ echo "Connection: close"
+ echo
+fi
+
+if [ "$request" == "GET" ]; then
+ if [ "$content" == "FILE" ]; then
+ cat "$cfile"
+ else
+ echo "$content"
+ fi
+fi
+exit 0
diff --git a/krfb/libvncserver/1instance.c b/krfb/libvncserver/1instance.c
new file mode 100644
index 00000000..8feadb53
--- /dev/null
+++ b/krfb/libvncserver/1instance.c
@@ -0,0 +1,141 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+typedef struct {
+ char* filename; /* this file is the pipe (set by user) */
+ char is_server; /* this is set by open_control_file */
+ int fd; /* this is set by open_control_file */
+} single_instance_struct;
+
+/* returns fd, is_server is set to -1 if server, 0 if client */
+int open_control_file(single_instance_struct* str)
+{
+ struct stat buf;
+
+ if(stat(str->filename,&buf)) {
+ mkfifo(str->filename,128|256);
+ str->is_server=-1;
+ str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
+ } else {
+ str->fd=open(str->filename,O_NONBLOCK|O_WRONLY);
+ if(errno==ENXIO) {
+ str->is_server=-1;
+ str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
+ } else
+ str->is_server=0;
+ }
+
+ return(str->fd);
+}
+
+void delete_control_file(single_instance_struct* str)
+{
+ remove(str->filename);
+}
+
+void close_control_file(single_instance_struct* str)
+{
+ close(str->fd);
+}
+
+typedef void (*event_dispatcher)(char* message);
+
+int get_next_message(char* buffer,int len,single_instance_struct* str,int usecs)
+{
+ struct timeval tv;
+ fd_set fdset;
+ int num_fds;
+
+ FD_ZERO(&fdset);
+ FD_SET(str->fd,&fdset);
+ tv.tv_sec=0;
+ tv.tv_usec=usecs;
+
+ num_fds=select(str->fd+1,&fdset,NULL,NULL,&tv);
+ if(num_fds) {
+ int reallen;
+
+ reallen=read(str->fd,buffer,len);
+ if(reallen==0) {
+ close(str->fd);
+ str->fd=open(str->filename,O_NONBLOCK|O_RDONLY);
+ num_fds--;
+ }
+ buffer[reallen]=0;
+#ifdef DEBUG_1INSTANCE
+ if(reallen!=0) fprintf(stderr,"message received: %s.\n",buffer);
+#endif
+ }
+
+ return(num_fds);
+}
+
+int dispatch_event(single_instance_struct* str,event_dispatcher dispatcher,int usecs)
+{
+ char buffer[1024];
+ int num_fds;
+
+ if((num_fds=get_next_message(buffer,1024,str,usecs)) && buffer[0])
+ dispatcher(buffer);
+
+ return(num_fds);
+}
+
+int loop_if_server(single_instance_struct* str,event_dispatcher dispatcher)
+{
+ open_control_file(str);
+ if(str->is_server) {
+ while(1)
+ dispatch_event(str,dispatcher,50);
+ }
+
+ return(str->fd);
+}
+
+void send_message(single_instance_struct* str,char* message)
+{
+#ifdef DEBUG_1INSTANCE
+ int i=
+#endif
+ write(str->fd,message,strlen(message));
+#ifdef DEBUG_1INSTANCE
+ fprintf(stderr,"send: %s => %d(%d)\n",message,i,strlen(message));
+#endif
+}
+
+#ifdef DEBUG_MAIN
+
+#include <stdio.h>
+#include <stdlib.h>
+
+single_instance_struct str1 = { "/tmp/1instance" };
+
+void my_dispatcher(char* message)
+{
+#ifdef DEBUG_1INSTANCE
+ fprintf(stderr,"Message arrived: %s.\n",message);
+#endif
+ if(!strcmp(message,"quit")) {
+ delete_control_file(str1);
+ exit(0);
+ }
+}
+
+int main(int argc,char** argv)
+{
+ int i;
+
+ loop_if_server(str1,my_dispatcher);
+
+ for(i=1;i<argc;i++)
+ send_event(str1,argv[i]);
+
+ return(0);
+}
+
+#endif
diff --git a/krfb/libvncserver/CHANGES b/krfb/libvncserver/CHANGES
new file mode 100644
index 00000000..8767a12a
--- /dev/null
+++ b/krfb/libvncserver/CHANGES
@@ -0,0 +1,75 @@
+ memory leaks squashed (localtime pseudo leak is still there :-)
+ small improvements for OSXvnc (still not working correctly)
+ synced with TightVNC 1.2.3
+ solaris compile cleanups
+ many x11vnc improvements
+ added backchannel, an encoding which needs special clients to pass
+ arbitrary data to the client
+ changes from Tim Jansen regarding multi threading and client blocking
+ as well as C++ compliancy
+ x11vnc can be controlled by starting again with special options if compiling
+ with LOCAL_CONTROL defined
+0.3
+ added x11vnc, a x0rfbserver clone
+ regard deferUpdateTime in processEvents, if usec<0
+ initialize deferUpdateTime (memory "leak"!)
+ changed command line handling (arguments are parsed and then removed)
+ added very simple example: zippy
+ added rfbDrawLine, rfbDrawPixel
+0.2
+ inserted a deferUpdate mechanism (X11 independent).
+ removed deletion of requestedRegion
+ added rfbLoadConsoleFont
+ fixed font colour handling.
+ added rfbSelectBox
+ added rfbDrawCharWithClip to allow for clipping and a background colour.
+ fixed font colours
+ added rfbFillRect
+ added IO function to check password.
+ rfbNewClient now sets the socket in the fd_set (for the select() call)
+ when compiling the library with HAVE_PTHREADS and an application
+ which includes "rfb.h" without, the structures got mixed up.
+ So, the pthreads section is now always at the end, and also
+ you get a linker error for rfbInitServer when using two different
+ pthread setups.
+ fixed two deadlocks: when setting a cursor and when using CopyRect
+ fixed CopyRect when copying modified regions (they lost the modified
+ property)
+ WIN32 target compiles and works for example :-)
+ fixed CopyRect (was using the wrong order of rectangles...)
+ should also work with pthreads, because copyrects are
+ always sent immediately (so that two consecutive copy rects
+ cannot conflict).
+ changed rfbUndrawCursor(rfbClientPtr) to (rfbScreenInfoPtr), because
+ this makes more sense!
+ flag backgroundLoop in rfbScreenInfo (if having pthreads)
+ CopyRect & CopyRegion were implemented.
+ if you use a rfbDoCopyR* function, it copies the data in the
+ framebuffer. If you prefer to do that yourself, use rfbScheduleCopyR*
+ instead; this doesn't modify the frameBuffer.
+ added flag to optionally not send XCursor updates, but only RichCursor,
+ or if that is not possible, fall back to server side cursor.
+ This is useful if your cursor has many nice colours.
+ fixed java viewer on server side:
+ SendCursorUpdate would send data even before the client pixel format
+ was set, but the java applet doesn't like the server's format.
+ fixed two pthread issues:
+ rfbSendFramebuffer was sent by a ProcessClientMessage function
+ (unprotected by updateMutex).
+ cursor coordinates were set without protection by cursorMutex
+ source is now equivalent to TridiaVNC 1.2.1
+ pthreads now work (use iterators!)
+ cursors are supported (rfbSetCursor automatically undraws cursor)
+ support for 3 bytes/pixel (slow!)
+ server side colourmap support
+ fixed rfbCloseClient not to close the connection (pthreads!)
+ this is done lazily (and with proper signalling).
+ cleaned up mac.c (from original OSXvnc); now compiles (untested!)
+ compiles cleanly on Linux, IRIX, BSD, Apple (Darwin)
+ fixed prototypes
+0.1
+ rewrote API to use pseudo-methods instead of required functions.
+ lots of clean up.
+ Example can show symbols now.
+ All encodings
+ HTTP
diff --git a/krfb/libvncserver/COPYING b/krfb/libvncserver/COPYING
new file mode 100644
index 00000000..a3f6b12e
--- /dev/null
+++ b/krfb/libvncserver/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/krfb/libvncserver/Makefile.am b/krfb/libvncserver/Makefile.am
new file mode 100644
index 00000000..ed8e22d2
--- /dev/null
+++ b/krfb/libvncserver/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libvncserver.la
+
+libvncserver_la_SOURCES = main.c rfbserver.c sraRegion.c auth.c sockets.c \
+ stats.c corre.c hextile.c rre.c translate.c cutpaste.c \
+ zlib.c tight.c httpd.c cursor.c font.c \
+ draw.c selbox.c d3des.c vncauth.c cargs.c
+
+EXTRA_DIST = CHANGES COPYING README TODO
+
+AM_CPPFLAGS = -DHAVE_PTHREADS -DALLOW24BPP
+AM_CFLAGS = -Wno-unused
+
diff --git a/krfb/libvncserver/README b/krfb/libvncserver/README
new file mode 100644
index 00000000..fe6d0c01
--- /dev/null
+++ b/krfb/libvncserver/README
@@ -0,0 +1,417 @@
+LibVNCServer: a library for easy implementation of a RDP/VNC server.
+Copyright (C) 2001 Johannes E. Schindelin
+
+What is it?
+-----------
+
+VNC is a set of programs using the RFB (Remote Frame Buffer) protocol. They
+are designed to "export" a frame buffer via net (if you don't know VNC, I
+suggest you read "Basics" below). It is already in wide use for
+administration, but it is not that easy to program a server yourself.
+
+This has been changed by LibVNCServer.
+
+There are two examples included:
+ - example, a shared scribble sheet
+ - pnmshow, a program to show PNMs (pictures) over the net.
+
+The examples are not too well documented, but easy straight forward and a
+good starting point.
+
+Try example: it outputs on which port it listens (default: 5900), so it is
+display 0. To view, call
+ vncviewer :0
+You should see a sheet with a gradient and "Hello World!" written on it. Try
+to paint something. Note that everytime you click, there is some bigger blot.
+The size depends on the mouse button you click. Open a second vncviewer with
+the same parameters and watch it as you paint in the other window. This also
+works over internet. You just have to know either the name or the IP of your
+machine. Then it is
+ vncviewer machine.where.example.runs.com:0
+or similar for the remote client. Now you are ready to type something. Be sure
+that your mouse sits still, because everytime the mouse moves, the cursor is
+reset! If you are done with that demo, just press Escape in the viewer. Note
+that the server still runs, even if you closed both windows. When you
+reconnect now, everything you painted and wrote is still there. So you press
+"Page Up" for a blank page.
+
+The demo pnmshow is much simpler: you either provide a filename as argument
+or pipe a file through stdin. Note that the file has to be a raw pnm/ppm file,
+i.e. a truecolour graphics. Only the Escape key is implemented. This may be
+the best starting point if you want to learn how to use LibVNCServer. You
+are confronted with the fact that the bytes per pixel can only be 8, 16 or 32.
+
+How to use
+----------
+
+To make a server, you just have to initialise a server structure using the
+function rfbDefaultScreenInit, like
+ rfbScreenInfoPtr rfbScreen =
+ rfbGetScreen(argc,argv,width,height,8,3,bpp);
+where byte per pixel should be 1, 2 or 4. If performance doesn't matter,
+you may try bpp=3 (internally one cannot use native data types in this
+case; if you want to use this, look at pnmshow24).
+
+
+You then can set hooks and io functions (see below) or other
+options (see below).
+
+And you allocate the frame buffer like this:
+ rfbScreen->frameBuffer = (char*)malloc(width*height*bpp);
+
+After that, you initialize the server, like
+ rfbInitServer(rfbScreen);
+
+You can use a blocking event loop, a background (pthread based) event loop,
+or implement your own using the rfbProcessEvents function.
+
+Making it interactive
+---------------------
+
+Input is handled by IO functions (see below).
+
+Whenever you change something in the frame buffer, call rfbMarkRectAsModified.
+You should make sure that the cursor is not drawn before drawing yourself
+by calling rfbUndrawCursor. You can also draw the cursor using rfbDrawCursor,
+but it hardly seems necessary. For cursor details, see below.
+
+Utility functions
+-----------------
+
+Whenever you draw something, you have to call
+ rfbMarkRectAsModified(screen,x1,y1,x2,y2).
+This tells LibVNCServer to send updates to all connected clients.
+
+Before you draw something, be sure to call
+ rfbUndrawCursor(screen).
+This tells LibVNCServer to hide the cursor.
+Remark: There are vncviewers out there, which know a cursor encoding, so
+that network traffic is low, and also the cursor doesn't need to be
+drawn the cursor everytime an update is sent. LibVNCServer handles
+all the details. Just set the cursor and don't bother any more.
+
+To set the mouse coordinates (or emulate mouse clicks), call
+ defaultPtrAddEvent(buttonMask,x,y,cl);
+However, this works only if your client doesn't do local cursor drawing. There
+is no way (to my knowledge) to set the pointer of a client via RFB protocol.
+IMPORTANT: do this at the end of your function, because this actually draws
+the cursor if no cursor encoding is active.
+
+What is the difference between rfbScreenInfoPtr and rfbClientPtr?
+-----------------------------------------------------------------
+
+The rfbScreenInfoPtr is a pointer to a rfbScreenInfo structure, which
+holds information about the server, like pixel format, io functions,
+frame buffer etc.
+
+The rfbClientPtr is a pointer to an rfbClientRec structure, which holds
+information about a client, like pixel format, socket of the
+connection, etc.
+
+A server can have several clients, but needn't have any. So, if you
+have a server and three clients are connected, you have one instance
+of a rfbScreenInfo and three instances of rfbClientRec's.
+
+The rfbClientRec structure holds a member
+ rfbScreenInfoPtr screen
+which points to the server and a member
+ rfbClientPtr next
+to the next client.
+
+The rfbScreenInfo structure holds a member
+ rfbClientPtr rfbClientHead
+which points to the first client.
+
+So, to access the server from the client structure, you use client->screen.
+To access all clients from a server, get screen->rfbClientHead and
+iterate using client->next.
+
+If you change client settings, be sure to use the provided iterator
+ rfbGetClientIterator(rfbScreen)
+with
+ rfbClientIteratorNext(iterator)
+and
+ rfbReleaseClientIterator
+to prevent thread clashes.
+
+Other options
+-------------
+
+These options have to be set between rfbGetScreen and rfbInitServer.
+
+If you already have a socket to talk to, just set rfbScreen->inetdSock
+(originally this is for inetd handling, but why not use it for your purpose?).
+
+To also start an HTTP server (running on port 5800+display_number), you have
+to set rfbScreen->httpdDir to a directory containing vncviewer.jar and
+index.vnc (like the included "classes" directory).
+
+Hooks and IO functions
+----------------------
+
+There exist the following IO functions as members of rfbScreen:
+kbdAddEvent, kbdReleaseAllKeys, ptrAddEvent and setXCutText
+
+kbdAddEvent(Bool down,KeySym key,rfbClientPtr cl)
+ is called when a key is pressed.
+kbdReleaseAllKeys(rfbClientPtr cl)
+ is not called at all (maybe in the future).
+ptrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)
+ is called when the mouse moves or a button is pressed.
+ WARNING: if you want to have proper cursor handling, call
+ defaultPtrAddEvent(buttonMask,x,y,cl)
+ in your own function. This sets the coordinates of the cursor.
+setXCutText(char* str,int len,rfbClientPtr cl)
+ is called when the selection changes.
+
+There are only two hooks:
+newClientHook(rfbClientPtr cl)
+ is called when a new client has connected.
+displayHook
+ is called just before a frame buffer update is sent.
+
+You can also override the following methods:
+getCursorPtr(rfbClientPtr cl)
+ This could be used to make an animated cursor (if you really want ...)
+setTranslateFunction(rfbClientPtr cl)
+ If you insist on colour maps or something more obscure, you have to
+ implement this. Default is a trueColour mapping.
+
+Cursor handling
+---------------
+
+The screen holds a pointer
+ rfbCursorPtr cursor
+to the current cursor. Whenever you set it, remember that any dynamically
+created cursor (like return value from rfbMakeXCursor) is not free'd!
+
+The rfbCursor structure consists mainly of a mask and a source. The mask
+describes, which pixels are drawn for the cursor (a cursor needn't be
+rectangular). The source describes, which colour those pixels should have.
+
+The standard is an XCursor: a cursor with a foreground and a background
+colour (stored in backRed,backGreen,backBlue and the same for foreground
+in a range from 0-0xffff). Therefore, the arrays "mask" and "source"
+contain pixels as single bits stored in bytes in MSB order. The rows are
+padded, such that each row begins with a new byte (i.e. a 10x4
+cursor's mask has 2x4 bytes, because 2 bytes are needed to hold 10 bits).
+
+It is however very easy to make a cursor like this:
+
+char* cur=" "
+ " xx "
+ " x "
+ " ";
+char* mask="xxxx"
+ "xxxx"
+ "xxxx"
+ "xxx ";
+rfbCursorPtr c=rfbMakeXCursor(4,4,cur,mask);
+
+You can even set "mask" to NULL in this call and LibVNCServer will calculate
+a mask for you (dynamically, so you have to free it yourself).
+
+There is also an array named "richSource" for colourful cursors. They have
+the same format as the frameBuffer (i.e. if the server is 32 bit,
+a 10x4 cursor has 4x10x4 bytes).
+
+History
+-------
+
+LibVNCServer is based on Tridia VNC and OSXvnc, which in turn are based on
+the original code from ORL/AT&T.
+
+When I began hacking with computers, my first interest was speed. So, when I
+got around assembler, I programmed the floppy to do much of the work, because
+it's clock rate was higher than that of my C64. This was my first experience
+with client/server techniques.
+
+When I came around Xwindows (much later), I was at once intrigued by the
+elegance of such connectedness between the different computers. I used it
+a lot - not the least priority lay on games. However, when I tried it over
+modem from home, it was no longer that much fun.
+
+When I started working with ASP (Application Service Provider) programs, I
+tumbled across Tarantella and Citrix. Being a security fanatic, the idea of
+running a server on windows didn't appeal to me, so Citrix went down the
+basket. However, Tarantella has it's own problems (security as well as the
+high price). But at the same time somebody told me about this "great little
+administrator's tool" named VNC. Being used to windows programs' sizes, the
+surprise was reciprocal inverse to the size of VNC!
+
+At the same time, the program "rdesktop" (a native Linux client for the
+Terminal Services of Windows servers) came to my attention. There where even
+works under way to make a protocol converter "rdp2vnc" out of this. However,
+my primary goal was a slow connection and rdp2vnc could only speak RRE
+encoding, which is not that funny with just 5kB/s. Tim Edmonds, the original
+author of rdp2vnc, suggested that I adapt it to Hextile Encoding, which is
+better. I first tried that, but had no success at all (crunchy pictures).
+
+Also, I liked the idea of an HTTP server included and possibly other
+encodings like the Tight Encodings from Const Kaplinsky. So I started looking
+for libraries implementing a VNC server where I could steal what I can't make.
+I found some programs based on the demo server from AT&T, which was also the
+basis for rdp2vnc (can only speak Raw and RRE encoding). There were some
+rumors that GGI has a VNC backend, but I didn't find any code, so probably
+there wasn't a working version anyway.
+
+All of a sudden, everything changed: I read on freshmeat that "OSXvnc" was
+released. I looked at the code and it was not much of a problem to work out
+a simple server - using every functionality there is in Xvnc. It became clear
+to me that I *had* to build a library out of it, so everybody can use it.
+Every change, every new feature can propagate to every user of it.
+
+It also makes everything easier:
+ You don't care about the cursor, once set (or use the standard cursor).
+You don't care about those sockets. You don't care about encodings.
+You just change your frame buffer and inform the library about it. Every once
+in a while you call rfbProcessEvents and that's it.
+
+Basics
+------
+
+VNC (Virtual network computing) works like this: You set up a server and can
+connect to it via vncviewers. The communication uses a protocol named RFB
+(Remote Frame Buffer). If the server supports HTTP, you can also connect
+using a java enabled browser. In this case, the server sends back a
+vncviewer applet with the correct settings.
+
+There exist several encodings for VNC, which are used to compress the regions
+which have changed before they are sent to the client. A client need not be
+able to understand every encoding, but at least Raw encoding. Which encoding
+it understands is negotiated by the RFB protocol.
+
+The following encodings are known to me:
+Raw, RRE, CoRRE, Hextile, CopyRect from the original AT&T code and
+Tight, ZLib, LastRect, XCursor, RichCursor from Const Kaplinsky et al.
+
+If you are using a modem, you want to try the "new" encodings. Especially
+with my 56k modem I like ZLib or Tight with Quality 0. In my tests, it even
+beats Tarantella.
+
+There is the possibility to set a password, which is also negotiated by the
+RFB protocol, but IT IS NOT SECURE. Anybody sniffing your net can get the
+password. You really should tunnel through SSH.
+
+Windows or: why do you do that to me?
+--------------------------------------------
+
+If you love products from Redmod, you better skip this paragraph.
+I am always amazed how people react whenever Microsoft(tm) puts in some
+features into their products which were around for a long time. Especially
+reporters seem to not know dick about what they are reporting about! But
+what is everytime annoying again, is that they don't do it right. Every
+concept has it's new name (remember what enumerators used to be until
+Mickeysoft(tm) claimed that enumerators are what we thought were iterators.
+Yeah right, enumerators are also containers. They are not separate. Muddy.)
+
+There are three packages you want to get hold of: zlib, jpeg and pthreads.
+The latter is not strictly necessary, but when you put something like this
+into your source:
+
+#define MUTEX(s)
+ struct {
+ int something;
+ MUTEX(latex);
+ }
+
+Microsoft's C++ compiler doesn't do it. It complains that this is an error.
+
+You can find the packages at
+http://www.gimp.org/win32/extralibs-dev-20001007.zip
+
+Thanks go to all the GIMP team!
+
+What are those other targets in the Makefile?
+---------------------------------------------
+
+OSXvnc-server is the original OSXvnc adapted to use the library, which was in
+turn adapted from OSXvnc. As you easily can see, the OSX dependend part is
+minimal.
+
+storepasswd is the original program to save a vnc style password in a file.
+Unfortunately, authentication as every vncviewer speaks it means the server
+has to know the plain password. You really should tunnel via ssh or use
+your own PasswordCheck to build a PIN/TAN system.
+
+sratest is a test unit. Run it to assert correct behaviour of sraRegion. I
+wrote this to test my iterator implementation.
+
+blooptest is a test of pthreads. It is just the example, but with a background
+loop to hunt down thread lockups.
+
+pnmshow24 is like pnmshow, but it uses 3 bytes/pixel internally, which is not
+as efficient as 4 bytes/pixel for translation, because there is no native data
+type of that size, so you have to memcpy pixels and be real cautious with
+endianness. Anyway, it works.
+
+fontsel is a test for rfbSelectBox and rfbLoadConsoleFont. If you have Linux
+console fonts, you can browse them via VNC. Directory browsing not implemented
+yet :-(
+
+Why I don't feel bad about GPL
+------------------------------
+
+At the beginning of this projects I would have liked to make it a BSD
+license. However, it is based on plenty of GPL'ed code, so it has to be
+a GPL. I hear BeeGee complaining: "but that's invasive, every derivative
+work, even just linking, makes my software GPL!"
+
+Yeah. That's right. It is because there are nasty jarheads out there who
+would take anybody's work and claim it their own, selling it for much too
+much money, stealing freedom and innovation from others, saying they were
+the maintainers of innovation, lying, making money with that.
+
+The people at AT&T worked really well to produce something as clean and lean
+as VNC. The managers decided that for their fame, they would release the
+program for free. But not only that! They realized that by releasing also
+the code for free, VNC would become an evolving little child, conquering
+new worlds, making it's parents very proud. As well they can be! To protect
+this innovation, they decided to make it GPL, not BSD. The principal
+difference is: You can make closed source programs deriving from BSD, not
+from GPL. You have to give proper credit with both.
+
+Now, why not BSD? Well, imagine your child being some famous actor. Along
+comes a manager who exploits your child exclusively, that is: nobody else
+can profit from the child, it itself included. Got it?
+
+What reason do you have now to use this library commercially?
+
+Several: You don't have to give away your product. Then you have effectively
+circumvented the GPL, because you have the benefits of other's work and you
+don't give back anything and you will be in hell for that. In fact, this
+library, as my other projects, is a payback for all the free software I can
+use (and sometimes, make better). For example, just now, I am using XEmacs
+on top X11, all running under Linux.
+
+Better: Use a concept like MySQL. This is free software, however, they make
+money with it. If you want something implemented, you have the choice:
+Ask them to do it (and pay a fair price), or do it yourself, normally giving
+back your enhancements to the free world of computing.
+
+Learn from it: If you like the style this is written, learn how to imitate
+it. If you don't like the style, learn how to avoid those things you don't
+like. I learnt so much, just from looking at code like Linux, XEmacs,
+LilyPond, STL, etc.
+
+License
+-------
+
+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.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.dfdf
+
+Contact
+-------
+
+To contact me, mail me: Johannes dot Schindelin at gmx dot de
diff --git a/krfb/libvncserver/TODO b/krfb/libvncserver/TODO
new file mode 100644
index 00000000..70dae90d
--- /dev/null
+++ b/krfb/libvncserver/TODO
@@ -0,0 +1,80 @@
+immediate:
+----------
+
+x11vnc: clipboard, cursor, updates interruptible by input (doesn't work yet)
+extra_bytes in rfbDrawCharWithClip.
+tested mouse buttons make copy rect, but text is not marked as mod.
+cursor drawing: set optional grain to mark bigger rectangles as drawn (else
+ you end up with thousands of one-pixel-rectangles to encode).
+selectbox: scroll bars
+documentation
+ hint that to mark very tiny regions as
+ modified is possibly inefficient for the encodings.
+ (a trail of points could better be a small rectangle).
+
+later:
+------
+
+authentification schemes (secure vnc)
+ IO function ptr exists; now explain how to tunnel and implement a
+ client address restriction scheme.
+autoconf? at least Sun Solaris and Windows compilation
+ (maybe Michael makes a small autconf)
+using Hermes library for fast colour translations.
+CORBA
+internal HTTP tunnelling feature (needs a special GET target and a few
+ changes to java applet).
+
+done:
+-----
+
+.x11vnc: sometimes XTest fails (but doesn't with x0rfbserver)
+.DeferUpdateTime (timing problems!)
+.empty cursor sending doesn't work.
+.udp (need an rfbClientPtr udpClient in rfbScreen)
+ input only; nearly untested (don't have the clients).
+.font handling: bpp>1
+.test copyRect and pthreads.
+.optionally dont draw rich cursors as xcursors
+.cursor smears on IRIX with pthreads, then has bus error. has to be a mutex
+ problem in cursor routines.
+.fix bug in http (java) client with big endian server: byte swapping is broken
+ (was a cursorshape which was sent too soon; java vncviewer assumes
+ a rich cursor shape to be always 1 byte per pixel, however, framebuffer
+ updates before setting the pixel format can be server format)
+.rfbConnect, ConnectToTcpAddr
+.update to newest TridiaVNC version (1.2.1).
+.adapt rdp2vnc (rdesktop)
+.pthreads concept: How to iterate over rfbClientPtr's? So that it can be
+ either called from rfbProcessEvents (which locks the list mutex)
+ or from the main thread (where the background loop sometimes
+ locks the list mutex).
+ - cursor drawing!
+ - cursor setting!
+ - rfbMarkRectAsModified
+ (did that by adding a refcount to clients secured by refCountMutex;
+ it also was necessary to check for cl->sock<0 in SendUpdateBuf)
+.translate.c: warning about non 8-bit colourmaps
+ 16-bit colourmaps are 192k -> no use without fast net.
+.rfbCloseClient
+.set colourmap
+.support 3 bytes per pixel
+.cursors
+.cutpaste
+.httpd
+.other encodings
+.test drawing of cursors when not using xcursor or rich cursor encoding
+fix bug with odd width (depends on client depth: width has to be multiple of server.bytesPerPixel/client.bytesPerPixel). only raw!! -> bug of vncviewer!
+.use sraRegion from Wez instead of miregion, because it is much smaller
+. - connection gone and then reconnect is a problem
+ the reason: there are in fact three threads accessing
+ the clientPtr: input, output and the application thread.
+ if you kill the viewer or do rfbCloseClient, all of those
+ three have to be warned that this is happening.
+ -> rfbClientConnectionGone can only be called by the outer loop
+ (with background loop, it is input, else it is processEvents).
+. fixed pthreads issues:
+ cursor deadlock,
+ CopyRect deadlock.
+. when copying a region with modified parts, they were not marked
+ as modified
diff --git a/krfb/libvncserver/auth.c b/krfb/libvncserver/auth.c
new file mode 100644
index 00000000..35657b8f
--- /dev/null
+++ b/krfb/libvncserver/auth.c
@@ -0,0 +1,109 @@
+/*
+ * auth.c - deal with authentication.
+ *
+ * This file implements the VNC authentication protocol when setting up an RFB
+ * connection.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfb.h"
+
+static int rfbMaxPasswordWait = 120000; /* password timeout (ms) */
+
+/*
+ * rfbAuthNewClient is called when we reach the point of authenticating
+ * a new client. If authentication isn't being used then we simply send
+ * rfbNoAuth. Otherwise we send rfbVncAuth plus the challenge.
+ */
+
+void
+rfbAuthNewClient(cl)
+ rfbClientPtr cl;
+{
+ char buf[4 + CHALLENGESIZE];
+ int len;
+
+ cl->state = RFB_AUTHENTICATION;
+
+ if (cl->screen->rfbAuthPasswdData && !cl->reverseConnection) {
+ *(CARD32 *)buf = Swap32IfLE(rfbVncAuth);
+ vncRandomBytes(cl->authChallenge);
+ memcpy(&buf[4], (char *)cl->authChallenge, CHALLENGESIZE);
+ len = 4 + CHALLENGESIZE;
+ } else {
+ *(CARD32 *)buf = Swap32IfLE(rfbNoAuth);
+ len = 4;
+ cl->state = RFB_INITIALISATION;
+ }
+
+ if (WriteExact(cl, buf, len) < 0) {
+ rfbLogPerror("rfbAuthNewClient: write");
+ rfbCloseClient(cl);
+ return;
+ }
+}
+
+
+/*
+ * rfbAuthProcessClientMessage is called when the client sends its
+ * authentication response.
+ */
+
+void
+rfbAuthProcessClientMessage(cl)
+ rfbClientPtr cl;
+{
+ int n;
+ CARD8 response[CHALLENGESIZE];
+ CARD32 authResult;
+
+ if ((n = ReadExactTimeout(cl, (char *)response, CHALLENGESIZE,
+ rfbMaxPasswordWait)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbAuthProcessClientMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ if(!cl->screen->passwordCheck(cl,response,CHALLENGESIZE)) {
+ rfbLog("rfbAuthProcessClientMessage: password check failed\n");
+ authResult = Swap32IfLE(rfbVncAuthFailed);
+ if (WriteExact(cl, (char *)&authResult, 4) < 0) {
+ rfbLogPerror("rfbAuthProcessClientMessage: write");
+ }
+ rfbCloseClient(cl);
+ return;
+ }
+
+ authResult = Swap32IfLE(rfbVncAuthOK);
+
+ if (WriteExact(cl, (char *)&authResult, 4) < 0) {
+ rfbLogPerror("rfbAuthProcessClientMessage: write");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ cl->state = RFB_INITIALISATION;
+}
diff --git a/krfb/libvncserver/bdf2c.pl b/krfb/libvncserver/bdf2c.pl
new file mode 100644
index 00000000..fc437127
--- /dev/null
+++ b/krfb/libvncserver/bdf2c.pl
@@ -0,0 +1,60 @@
+#!/usr/bin/perl
+
+@encodings=();
+for($i=0;$i<256*5;$i++) {
+ $encodings[$i]="0";
+}
+
+$out="";
+$counter=0;
+$fontname="default";
+
+$i=0;
+$searchfor="";
+$nullx="0x";
+
+while(<>) {
+ if(/^FONT (.*)$/) {
+ $fontname=$1;
+ $fontname=~y/\"//d;
+ } elsif(/^ENCODING (.*)$/) {
+ $glyphindex=$1;
+ $searchfor="BBX";
+ $dwidth=0;
+ } elsif(/^DWIDTH (.*) (.*)/) {
+ $dwidth=$1;
+ } elsif(/^BBX (.*) (.*) (.*) (.*)$/) {
+ ($width,$height,$x,$y)=($1,$2,$3,$4);
+ @encodings[$glyphindex*5..($glyphindex*5+4)]=($counter,$width,$height,$x,$y);
+ if($dwidth != 0) {
+ $encodings[$glyphindex*5+1]=$dwidth;
+ } else {
+ $dwidth=$width;
+ }
+ $searchfor="BITMAP";
+ } elsif(/^BITMAP/) {
+ $i=1;
+ } elsif($i>0) {
+ if($i>$height) {
+ $i=0;
+ $out.=" /* $glyphindex */\n";
+ } else {
+ if(int(($dwidth+7)/8) > int(($width+7)/8)) {
+ $_ .= "00"x(int(($dwidth+7)/8)-int(($width+7)/8));
+ }
+ $_=substr($_,0,(int(($dwidth+7)/8)*2));
+ $counter+=(int(($dwidth+7)/8));
+ s/(..)/$nullx$1,/g;
+ $out.=$_;
+ $i++;
+ }
+ }
+}
+
+print "unsigned char " . $fontname . "FontData[$counter]={\n" . $out;
+print "};\nint " . $fontname . "FontMetaData[256*5]={\n";
+for($i=0;$i<256*5;$i++) {
+ print $encodings[$i] . ",";
+}
+print "};\nrfbFontData " . $fontname . "Font={" .
+ $fontname . "FontData, " . $fontname . "FontMetaData};\n";
diff --git a/krfb/libvncserver/cargs.c b/krfb/libvncserver/cargs.c
new file mode 100644
index 00000000..03696a31
--- /dev/null
+++ b/krfb/libvncserver/cargs.c
@@ -0,0 +1,135 @@
+/*
+ * This parses the command line arguments. It was seperated from main.c by
+ * Justin Dearing <jdeari01@longisland.poly.edu>.
+ */
+
+/*
+ * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
+ * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * see GPL (latest version) for full details
+ */
+
+#include "rfb.h"
+
+void
+rfbUsage(void)
+{
+ fprintf(stderr, "-rfbport port TCP port for RFB protocol\n");
+ fprintf(stderr, "-rfbwait time max time in ms to wait for RFB client\n");
+ fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n"
+ " (use 'storepasswd' to create a password file)\n");
+ fprintf(stderr, "-passwd plain-password use authentication \n"
+ " (use plain-password as password, USE AT YOUR RISK)\n");
+ fprintf(stderr, "-deferupdate time time in ms to defer updates "
+ "(default 40)\n");
+ fprintf(stderr, "-desktop name VNC desktop name (default \"LibVNCServer\")\n");
+ fprintf(stderr, "-alwaysshared always treat new clients as shared\n");
+ fprintf(stderr, "-nevershared never treat new clients as shared\n");
+ fprintf(stderr, "-dontdisconnect don't disconnect existing clients when a "
+ "new non-shared\n"
+ " connection comes in (refuse new connection "
+ "instead)\n");
+ exit(1);
+}
+
+/* purges COUNT arguments from ARGV at POSITION and decrements ARGC.
+ POSITION points to the first non purged argument afterwards. */
+void rfbPurgeArguments(int* argc,int* position,int count,char *argv[])
+{
+ int amount=(*argc)-(*position)-count;
+ if(amount)
+ memmove(argv+(*position),argv+(*position)+count,sizeof(char*)*amount);
+ (*argc)-=count;
+ (*position)--;
+}
+
+void
+rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[])
+{
+ int i,i1;
+
+ if(!argc) return;
+
+ for (i = i1 = 1; i < *argc; i++) {
+ if (strcmp(argv[i], "-help") == 0) {
+ rfbUsage();
+ exit(1);
+ } else if (strcmp(argv[i], "-rfbport") == 0) { /* -rfbport port */
+ if (i + 1 >= *argc) rfbUsage();
+ rfbScreen->rfbPort = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */
+ if (i + 1 >= *argc) rfbUsage();
+ rfbScreen->rfbMaxClientWait = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-rfbauth") == 0) { /* -rfbauth passwd-file */
+ if (i + 1 >= *argc) rfbUsage();
+ rfbScreen->rfbAuthPasswdData = argv[++i];
+ } else if (strcmp(argv[i], "-passwd") == 0) { /* -passwd password */
+ char **passwds = malloc(sizeof(char**)*2);
+ if (i + 1 >= *argc) rfbUsage();
+ passwds[0] = argv[++i];
+ passwds[1] = 0;
+ rfbScreen->rfbAuthPasswdData = (void*)passwds;
+ rfbScreen->passwordCheck = rfbCheckPasswordByList;
+ } else if (strcmp(argv[i], "-deferupdate") == 0) { /* -desktop desktop-name */
+ if (i + 1 >= *argc) rfbUsage();
+ rfbScreen->rfbDeferUpdateTime = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-desktop") == 0) { /* -desktop desktop-name */
+ if (i + 1 >= *argc) rfbUsage();
+ rfbScreen->desktopName = argv[++i];
+ } else if (strcmp(argv[i], "-alwaysshared") == 0) {
+ rfbScreen->rfbAlwaysShared = TRUE;
+ } else if (strcmp(argv[i], "-nevershared") == 0) {
+ rfbScreen->rfbNeverShared = TRUE;
+ } else if (strcmp(argv[i], "-dontdisconnect") == 0) {
+ rfbScreen->rfbDontDisconnect = TRUE;
+ } else if (strcmp(argv[i], "-width") == 0) {
+ rfbScreen->width = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-height") == 0) {
+ rfbScreen->height = atoi(argv[++i]);
+ } else {
+ /* we just remove the processed arguments from the list */
+ if(i != i1)
+ rfbPurgeArguments(argc,&i,i1-i,argv);
+ i1++;
+ i++;
+ }
+ }
+ *argc -= i-i1;
+}
+
+void rfbSizeUsage()
+{
+ fprintf(stderr, "-width sets the width of the framebuffer\n");
+ fprintf(stderr, "-height sets the height of the framebuffer\n");
+ exit(1);
+}
+
+void
+rfbProcessSizeArguments(int* width,int* height,int* bpp,int* argc, char *argv[])
+{
+ int i,i1;
+
+ if(!argc) return;
+ for (i = i1 = 1; i < *argc-1; i++) {
+ if (strcmp(argv[i], "-bpp") == 0) {
+ *bpp = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-width") == 0) {
+ *width = atoi(argv[++i]);
+ } else if (strcmp(argv[i], "-height") == 0) {
+ *height = atoi(argv[++i]);
+ } else {
+ /* we just remove the processed arguments from the list */
+ if(i != i1) {
+ memmove(argv+i1,argv+i,sizeof(char*)*(*argc-i));
+ *argc -= i-i1;
+ }
+ i1++;
+ i = i1-1;
+ }
+ }
+ *argc -= i-i1;
+}
+
diff --git a/krfb/libvncserver/corre.c b/krfb/libvncserver/corre.c
new file mode 100644
index 00000000..d6329a10
--- /dev/null
+++ b/krfb/libvncserver/corre.c
@@ -0,0 +1,355 @@
+/*
+ * corre.c
+ *
+ * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This
+ * code is based on krw's original javatel rfbserver.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfb.h"
+
+/*
+ * rreBeforeBuf contains pixel data in the client's format.
+ * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is
+ * larger than the raw data or if it exceeds rreAfterBufSize then
+ * raw encoding is used instead.
+ */
+
+static int rreBeforeBufSize = 0;
+static char *rreBeforeBuf = NULL;
+
+static int rreAfterBufSize = 0;
+static char *rreAfterBuf = NULL;
+static int rreAfterBufLen;
+
+static int subrectEncode8(CARD8 *data, int w, int h);
+static int subrectEncode16(CARD16 *data, int w, int h);
+static int subrectEncode32(CARD32 *data, int w, int h);
+static CARD32 getBgColour(char *data, int size, int bpp);
+static Bool rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, int x, int y,
+ int w, int h);
+
+
+/*
+ * rfbSendRectEncodingCoRRE - send an arbitrary size rectangle using CoRRE
+ * encoding.
+ */
+
+Bool
+rfbSendRectEncodingCoRRE(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ if (h > cl->correMaxHeight) {
+ rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight );
+ rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, w,
+ h - cl->correMaxHeight);
+ return FALSE;
+ }
+
+ if (w > cl->correMaxWidth) {
+ rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h);
+ rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y,
+ w - cl->correMaxWidth, h);
+ return FALSE;
+ }
+
+ rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h);
+ return TRUE;
+}
+
+
+
+/*
+ * rfbSendSmallRectEncodingCoRRE - send a small (guaranteed < 256x256)
+ * rectangle using CoRRE encoding.
+ */
+
+static Bool
+rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+ rfbRREHeader hdr;
+ int nSubrects;
+ int i;
+ char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
+ + (x * (cl->screen->bitsPerPixel / 8)));
+
+ int maxRawSize = (cl->screen->width * cl->screen->height
+ * (cl->format.bitsPerPixel / 8));
+
+ if (rreBeforeBufSize < maxRawSize) {
+ rreBeforeBufSize = maxRawSize;
+ if (rreBeforeBuf == NULL)
+ rreBeforeBuf = (char *)malloc(rreBeforeBufSize);
+ else
+ rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize);
+ }
+
+ if (rreAfterBufSize < maxRawSize) {
+ rreAfterBufSize = maxRawSize;
+ if (rreAfterBuf == NULL)
+ rreAfterBuf = (char *)malloc(rreAfterBufSize);
+ else
+ rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize);
+ }
+
+ (*cl->translateFn)(cl->translateLookupTable,&(cl->screen->rfbServerFormat),
+ &cl->format, fbptr, rreBeforeBuf,
+ cl->screen->paddedWidthInBytes, w, h);
+
+ switch (cl->format.bitsPerPixel) {
+ case 8:
+ nSubrects = subrectEncode8((CARD8 *)rreBeforeBuf, w, h);
+ break;
+ case 16:
+ nSubrects = subrectEncode16((CARD16 *)rreBeforeBuf, w, h);
+ break;
+ case 32:
+ nSubrects = subrectEncode32((CARD32 *)rreBeforeBuf, w, h);
+ break;
+ default:
+ rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel);
+ exit(1);
+ }
+
+ if (nSubrects < 0) {
+
+ /* RRE encoding was too large, use raw */
+
+ return rfbSendRectEncodingRaw(cl, x, y, w, h);
+ }
+
+ cl->rfbRectanglesSent[rfbEncodingCoRRE]++;
+ cl->rfbBytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader
+ + sz_rfbRREHeader + rreAfterBufLen);
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader
+ > UPDATE_BUF_SIZE)
+ {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingCoRRE);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ hdr.nSubrects = Swap32IfLE(nSubrects);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader);
+ cl->ublen += sz_rfbRREHeader;
+
+ for (i = 0; i < rreAfterBufLen;) {
+
+ int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
+
+ if (i + bytesToCopy > rreAfterBufLen) {
+ bytesToCopy = rreAfterBufLen - i;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy);
+
+ cl->ublen += bytesToCopy;
+ i += bytesToCopy;
+
+ if (cl->ublen == UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ * subrectEncode() encodes the given multicoloured rectangle as a background
+ * colour overwritten by single-coloured rectangles. It returns the number
+ * of subrectangles in the encoded buffer, or -1 if subrect encoding won't
+ * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The
+ * single-colour rectangle partition is not optimal, but does find the biggest
+ * horizontal or vertical rectangle top-left anchored to each consecutive
+ * coordinate position.
+ *
+ * The coding scheme is simply [<bgcolour><subrect><subrect>...] where each
+ * <subrect> is [<colour><x><y><w><h>].
+ */
+
+#define DEFINE_SUBRECT_ENCODE(bpp) \
+static int \
+subrectEncode##bpp(data,w,h) \
+ CARD##bpp *data; \
+ int w; \
+ int h; \
+{ \
+ CARD##bpp cl; \
+ rfbCoRRERectangle subrect; \
+ int x,y; \
+ int i,j; \
+ int hx=0,hy,vx=0,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \
+ \
+ *((CARD##bpp*)rreAfterBuf) = bg; \
+ \
+ rreAfterBufLen = (bpp/8); \
+ \
+ for (y=0; y<h; y++) { \
+ line = data+(y*w); \
+ for (x=0; x<w; x++) { \
+ if (line[x] != bg) { \
+ cl = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) { \
+ seg = data+(j*w); \
+ if (seg[x] != cl) {break;} \
+ i = x; \
+ while ((seg[i] == cl) && (i < w)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \
+ * We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) { \
+ thew = hw; \
+ theh = hh; \
+ } else { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ subrect.x = thex; \
+ subrect.y = they; \
+ subrect.w = thew; \
+ subrect.h = theh; \
+ \
+ newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \
+ if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \
+ return -1; \
+ \
+ numsubs += 1; \
+ *((CARD##bpp*)(rreAfterBuf + rreAfterBufLen)) = cl; \
+ rreAfterBufLen += (bpp/8); \
+ memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \
+ rreAfterBufLen += sz_rfbCoRRERectangle; \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) { \
+ for (i=thex; i < (thex+thew); i++) { \
+ data[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ return numsubs; \
+}
+
+DEFINE_SUBRECT_ENCODE(8)
+DEFINE_SUBRECT_ENCODE(16)
+DEFINE_SUBRECT_ENCODE(32)
+
+
+/*
+ * getBgColour() gets the most prevalent colour in a byte array.
+ */
+static CARD32
+getBgColour(data,size,bpp)
+ char *data;
+ int size;
+ int bpp;
+{
+
+#define NUMCLRS 256
+
+ static int counts[NUMCLRS];
+ int i,j,k;
+
+ int maxcount = 0;
+ CARD8 maxclr = 0;
+
+ if (bpp != 8) {
+ if (bpp == 16) {
+ return ((CARD16 *)data)[0];
+ } else if (bpp == 32) {
+ return ((CARD32 *)data)[0];
+ } else {
+ rfbLog("getBgColour: bpp %d?\n",bpp);
+ exit(1);
+ }
+ }
+
+ for (i=0; i<NUMCLRS; i++) {
+ counts[i] = 0;
+ }
+
+ for (j=0; j<size; j++) {
+ k = (int)(((CARD8 *)data)[j]);
+ if (k >= NUMCLRS) {
+ rfbLog("getBgColour: unusual colour = %d\n", k);
+ exit(1);
+ }
+ counts[k] += 1;
+ if (counts[k] > maxcount) {
+ maxcount = counts[k];
+ maxclr = ((CARD8 *)data)[j];
+ }
+ }
+
+ return maxclr;
+}
diff --git a/krfb/libvncserver/cursor.c b/krfb/libvncserver/cursor.c
new file mode 100644
index 00000000..13175118
--- /dev/null
+++ b/krfb/libvncserver/cursor.c
@@ -0,0 +1,678 @@
+/*
+ * cursor.c - support for cursor shape updates.
+ */
+
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include "rfb.h"
+
+/*
+ * Send cursor shape either in X-style format or in client pixel format.
+ */
+
+Bool
+rfbSendCursorShape(cl)
+ rfbClientPtr cl;
+{
+ rfbCursorPtr pCursor;
+ rfbFramebufferUpdateRectHeader rect;
+ rfbXCursorColors colors;
+ int saved_ublen;
+ int bitmapRowBytes, maskBytes, dataBytes;
+ int i, j;
+ CARD8 *bitmapData;
+ CARD8 bitmapByte;
+
+ pCursor = cl->screen->getCursorPtr(cl);
+ /*if(!pCursor) return TRUE;*/
+
+ if (cl->useRichCursorEncoding) {
+ if(pCursor && !pCursor->richSource)
+ MakeRichCursorFromXCursor(cl->screen,pCursor);
+ rect.encoding = Swap32IfLE(rfbEncodingRichCursor);
+ } else {
+ if(pCursor && !pCursor->source)
+ MakeXCursorFromRichCursor(cl->screen,pCursor);
+ rect.encoding = Swap32IfLE(rfbEncodingXCursor);
+ }
+
+ /* If there is no cursor, send update with empty cursor data. */
+
+ if ( pCursor && pCursor->width == 1 &&
+ pCursor->height == 1 &&
+ pCursor->mask[0] == 0 ) {
+ pCursor = NULL;
+ }
+
+ if (pCursor == NULL) {
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ rect.r.x = rect.r.y = 0;
+ rect.r.w = rect.r.h = 0;
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cl->rfbCursorBytesSent += sz_rfbFramebufferUpdateRectHeader;
+ cl->rfbCursorUpdatesSent++;
+
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ /* Calculate data sizes. */
+
+ bitmapRowBytes = (pCursor->width + 7) / 8;
+ maskBytes = bitmapRowBytes * pCursor->height;
+ dataBytes = (cl->useRichCursorEncoding) ?
+ (pCursor->width * pCursor->height *
+ (cl->format.bitsPerPixel / 8)) : maskBytes;
+
+ /* Send buffer contents if needed. */
+
+ if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
+ sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ if ( cl->ublen + sz_rfbFramebufferUpdateRectHeader +
+ sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) {
+ return FALSE; /* FIXME. */
+ }
+
+ saved_ublen = cl->ublen;
+
+ /* Prepare rectangle header. */
+
+ rect.r.x = Swap16IfLE(pCursor->xhot);
+ rect.r.y = Swap16IfLE(pCursor->yhot);
+ rect.r.w = Swap16IfLE(pCursor->width);
+ rect.r.h = Swap16IfLE(pCursor->height);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ /* Prepare actual cursor data (depends on encoding used). */
+
+ if (!cl->useRichCursorEncoding) {
+ /* XCursor encoding. */
+ colors.foreRed = (char)(pCursor->foreRed >> 8);
+ colors.foreGreen = (char)(pCursor->foreGreen >> 8);
+ colors.foreBlue = (char)(pCursor->foreBlue >> 8);
+ colors.backRed = (char)(pCursor->backRed >> 8);
+ colors.backGreen = (char)(pCursor->backGreen >> 8);
+ colors.backBlue = (char)(pCursor->backBlue >> 8);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&colors, sz_rfbXCursorColors);
+ cl->ublen += sz_rfbXCursorColors;
+
+ bitmapData = (CARD8 *)pCursor->source;
+
+ for (i = 0; i < pCursor->height; i++) {
+ for (j = 0; j < bitmapRowBytes; j++) {
+ bitmapByte = bitmapData[i * bitmapRowBytes + j];
+ cl->updateBuf[cl->ublen++] = (char)bitmapByte;
+ }
+ }
+ } else {
+ /* RichCursor encoding. */
+ int bpp1=cl->screen->rfbServerFormat.bitsPerPixel/8,
+ bpp2=cl->format.bitsPerPixel/8;
+ (*cl->translateFn)(cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat),
+ &cl->format, pCursor->richSource,
+ &cl->updateBuf[cl->ublen],
+ pCursor->width*bpp1, pCursor->width, pCursor->height);
+
+ cl->ublen += pCursor->width*bpp2*pCursor->height;
+ }
+
+ /* Prepare transparency mask. */
+
+ bitmapData = (CARD8 *)pCursor->mask;
+
+ for (i = 0; i < pCursor->height; i++) {
+ for (j = 0; j < bitmapRowBytes; j++) {
+ bitmapByte = bitmapData[i * bitmapRowBytes + j];
+ cl->updateBuf[cl->ublen++] = (char)bitmapByte;
+ }
+ }
+
+ /* Send everything we have prepared in the cl->updateBuf[]. */
+
+ cl->rfbCursorBytesSent += (cl->ublen - saved_ublen);
+ cl->rfbCursorUpdatesSent++;
+
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*
+ * Send soft cursor state and possibly image
+ */
+
+Bool
+rfbSendSoftCursor(rfbClientPtr cl, Bool cursorWasChanged)
+{
+ rfbCursorPtr pCursor;
+ rfbSoftCursorSetImage setImage;
+ rfbSoftCursorMove moveMsg;
+ rfbFramebufferUpdateRectHeader rect;
+ int saved_ublen, i, scOindex, scNindex, nlen, imgLen;
+
+ pCursor = cl->screen->getCursorPtr(cl);
+ if (cursorWasChanged && cl->softSource) {
+ free(cl->softSource);
+ cl->softSource = 0;
+ }
+ if (!cl->softSource)
+ MakeSoftCursor(cl, pCursor);
+
+ imgLen = cl->softSourceLen;
+
+ /* If there is no cursor, send update with empty cursor data. */
+
+ if ( pCursor && pCursor->width <= 1 &&
+ pCursor->height <= 1 &&
+ pCursor->mask[0] == 0 ) {
+ pCursor = NULL;
+ }
+
+ setImage.imageLength = Swap16IfLE(imgLen);
+
+ scOindex = -1;
+ scNindex = -1;
+ for (i = 0; i < rfbSoftCursorMaxImages; i++) {
+ rfbSoftCursorSetImage *scsi = cl->softCursorImages[i];
+ if (!scsi) {
+ scNindex = i;
+ break;
+ }
+
+ setImage.imageIndex = scsi->imageIndex;
+ if (!memcmp((char*)scsi, (char*)&setImage, sizeof(setImage))) {
+ if (imgLen && !memcmp(((char*)scsi)+sizeof(rfbSoftCursorSetImage),
+ cl->softSource,
+ imgLen)) {
+ scOindex = i;
+ break;
+ }
+ }
+ }
+
+ nlen = 0;
+ if (scOindex < 0) {
+ if (scNindex < 0) {
+ scNindex = cl->nextUnusedSoftCursorImage;
+ cl->nextUnusedSoftCursorImage = (cl->nextUnusedSoftCursorImage+1)
+ % rfbSoftCursorMaxImages;
+ free(cl->softCursorImages[scNindex]);
+ }
+
+ scOindex = scNindex;
+ setImage.imageIndex = scNindex + rfbSoftCursorSetIconOffset;
+ nlen = sizeof(setImage) + imgLen;
+ cl->softCursorImages[scNindex] = calloc(1, nlen);
+ memcpy((char*)cl->softCursorImages[scNindex], (char*)&setImage,
+ sizeof(setImage));
+ if (imgLen)
+ memcpy(((char*)cl->softCursorImages[scNindex])+sizeof(setImage),
+ (char*)cl->softSource, imgLen);
+ }
+
+ /* Send buffer contents if needed. */
+
+ if ( cl->ublen + sizeof(rfbSoftCursorMove) +
+ ((scNindex >= 0 && cursorWasChanged) ?
+ (sizeof(rfbSoftCursorSetImage) + imgLen) : 0) > UPDATE_BUF_SIZE) {
+
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ saved_ublen = cl->ublen;
+
+ if (scNindex >= 0 && cursorWasChanged) {
+ rect.encoding = Swap32IfLE(rfbEncodingSoftCursor);
+ if (pCursor) {
+ rect.r.x = Swap16IfLE(pCursor->xhot);
+ rect.r.y = Swap16IfLE(pCursor->yhot);
+ rect.r.w = Swap16IfLE(pCursor->width);
+ rect.r.h = Swap16IfLE(pCursor->height);
+ }
+ else {
+ rect.r.x = 0;
+ rect.r.y = 0;
+ rect.r.w = 0;
+ rect.r.h = 0;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen],
+ (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+ memcpy(&cl->updateBuf[cl->ublen], (char*)cl->softCursorImages[scNindex], nlen);
+ cl->ublen += nlen;
+ cl->rfbCursorUpdatesSent++;
+ }
+
+ rect.encoding = Swap32IfLE(rfbEncodingSoftCursor);
+ rect.r.x = 0;
+ rect.r.y = 0;
+ rect.r.w = Swap16IfLE(cl->screen->cursorX);
+ rect.r.h = Swap16IfLE(cl->screen->cursorY);
+ moveMsg.imageIndex = scOindex;
+ moveMsg.buttonMask = 0; /* todo */
+ memcpy(&cl->updateBuf[cl->ublen],
+ (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+ memcpy(&cl->updateBuf[cl->ublen], (char*)&moveMsg, sizeof(moveMsg));
+ cl->ublen += sizeof(moveMsg);
+
+ /* Send everything we have prepared in the cl->updateBuf[]. */
+
+ cl->rfbCursorBytesSent += (cl->ublen - saved_ublen);
+ cl->rfbCursorUpdatesSent++;
+
+ return rfbSendUpdateBuf(cl);
+}
+
+/* conversion routine for predefined cursors in LSB order */
+unsigned char rfbReverseByte[0x100] = {
+ /* copied from Xvnc/lib/font/util/utilbitmap.c */
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap)
+{
+ int i,t=(width+7)/8*height;
+ for(i=0;i<t;i++)
+ bitmap[i]=rfbReverseByte[(int)bitmap[i]];
+}
+
+/* Cursor creation. You "paint" a cursor and let these routines do the work */
+
+rfbCursorPtr rfbMakeXCursor(int width,int height,char* cursorString,char* maskString)
+{
+ int i,j,w=(width+7)/8;
+ rfbCursorPtr cursor = (rfbCursorPtr)calloc(1,sizeof(rfbCursor));
+ char* cp;
+ unsigned char bit;
+
+ cursor->width=width;
+ cursor->height=height;
+ /*cursor->backRed=cursor->backGreen=cursor->backBlue=0xffff;*/
+ cursor->foreRed=cursor->foreGreen=cursor->foreBlue=0xffff;
+
+ cursor->source = (unsigned char*)calloc(w,height);
+ for(j=0,cp=cursorString;j<height;j++)
+ for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
+ if(*cp!=' ') cursor->source[j*w+i/8]|=bit;
+
+ if(maskString) {
+ cursor->mask = (unsigned char*)calloc(w,height);
+ for(j=0,cp=maskString;j<height;j++)
+ for(i=0,bit=0x80;i<width;i++,bit=(bit&1)?0x80:bit>>1,cp++)
+ if(*cp!=' ') cursor->mask[j*w+i/8]|=bit;
+ } else
+ cursor->mask = (unsigned char*)rfbMakeMaskForXCursor(width,height,cursor->source);
+
+ return(cursor);
+}
+
+char* rfbMakeMaskForXCursor(int width,int height,char* source)
+{
+ int i,j,w=(width+7)/8;
+ char* mask=(char*)calloc(w,height);
+ unsigned char c;
+
+ for(j=0;j<height;j++)
+ for(i=w-1;i>=0;i--) {
+ c=source[j*w+i];
+ if(j>0) c|=source[(j-1)*w+i];
+ if(j<height-1) c|=source[(j+1)*w+i];
+
+ if(i>0 && (c&0x80))
+ mask[j*w+i-1]|=0x01;
+ if(i<w-1 && (c&0x01))
+ mask[j*w+i+1]|=0x80;
+
+ mask[j*w+i]|=(c<<1)|c|(c>>1);
+ }
+
+ return(mask);
+}
+
+void rfbFreeCursor(rfbCursorPtr cursor)
+{
+ if(cursor) {
+ if(cursor->richSource)
+ free(cursor->richSource);
+ free(cursor->source);
+ free(cursor->mask);
+ free(cursor);
+ }
+
+}
+
+/* background and foregroud colour have to be set beforehand */
+void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
+{
+ rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
+ int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8,
+ width=cursor->width*bpp;
+ CARD32 background;
+ char *back=(char*)&background;
+ unsigned char bit;
+
+ cursor->source=(unsigned char*)calloc(w,cursor->height);
+
+ if(format->bigEndian)
+ back+=4-bpp;
+
+ background=cursor->backRed<<format->redShift|
+ cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift;
+
+ for(j=0;j<cursor->height;j++)
+ for(i=0,bit=0x80;i<cursor->width;i++,bit=(bit&1)?0x80:bit>>1)
+ if(memcmp(cursor->richSource+j*width+i*bpp,back,bpp))
+ cursor->source[j*w+i/8]|=bit;
+}
+
+void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor)
+{
+ rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
+ int i,j,w=(cursor->width+7)/8,bpp=format->bitsPerPixel/8;
+ CARD32 background,foreground;
+ char *back=(char*)&background,*fore=(char*)&foreground;
+ unsigned char *cp;
+ unsigned char bit;
+
+ cp=cursor->richSource=(unsigned char*)calloc(cursor->width*bpp,cursor->height);
+
+ if(format->bigEndian) {
+ back+=4-bpp;
+ fore+=4-bpp;
+ }
+
+ background=cursor->backRed<<format->redShift|
+ cursor->backGreen<<format->greenShift|cursor->backBlue<<format->blueShift;
+ foreground=cursor->foreRed<<format->redShift|
+ cursor->foreGreen<<format->greenShift|cursor->foreBlue<<format->blueShift;
+
+ for(j=0;j<cursor->height;j++)
+ for(i=0,bit=0x80;i<cursor->height;i++,bit=(bit&1)?0x80:bit>>1,cp+=bpp)
+ if(cursor->source[j*w+i/8]&bit) memcpy(cp,fore,bpp);
+ else memcpy(cp,back,bpp);
+}
+
+void MakeSoftCursor(rfbClientPtr cl, rfbCursorPtr cursor)
+{
+ int w = (cursor->width+7)/8;
+ int bpp = cl->format.bitsPerPixel/8;
+ int sbpp= cl->screen->rfbServerFormat.bitsPerPixel/8;
+ unsigned char *cp, *sp, *translatedCursor;
+ int state; /* 0 = transparent, 1 otherwise */
+ CARD8 *counter;
+ unsigned char bit;
+ int i,j;
+
+ if ((!cursor) || (cl->softSource)) {
+ cl->softSourceLen = 0;
+ return;
+ }
+
+ if (!cursor->richSource)
+ MakeRichCursorFromXCursor(cl->screen, cursor);
+
+ sp = malloc(cursor->width*bpp*cursor->height);
+
+ (*cl->translateFn)(cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat),
+ &cl->format, cursor->richSource,
+ sp, cursor->width*sbpp,
+ cursor->width, cursor->height);
+
+ translatedCursor = sp;
+ cp=cl->softSource=(unsigned char*)calloc(cursor->width*(bpp+2),cursor->height);
+
+ state = 0;
+ counter = cp++;
+ *counter = 0;
+
+ for(j=0;j<cursor->height;j++)
+ for(i=0,bit=0x80;i<cursor->width;i++,bit=(bit&1)?0x80:bit>>1)
+ if(cursor->mask[j*w+i/8]&bit) {
+ if (state) {
+ memcpy(cp,sp,bpp);
+ cp += bpp;
+ sp += bpp;
+ (*counter)++;
+ if (*counter == 255) {
+ state = 0;
+ counter = cp++;
+ *counter = 0;
+ }
+ }
+ else {
+ state = 1;
+ counter = cp++;
+ *counter = 1;
+ memcpy(cp,sp,bpp);
+ cp += bpp;
+ sp += bpp;
+ }
+ }
+ else {
+ if (!state) {
+ (*counter)++;
+ if (*counter == 255) {
+ state = 1;
+ counter = cp++;
+ *counter = 0;
+ }
+ }
+ else {
+ state = 0;
+ counter = cp++;
+ *counter = 1;
+ }
+ sp += bpp;
+ }
+
+ free(translatedCursor);
+ cl->softSourceLen = cp - cl->softSource;
+}
+
+
+/* functions to draw/hide cursor directly in the frame buffer */
+
+void rfbUndrawCursor(rfbScreenInfoPtr s)
+{
+ rfbCursorPtr c=s->cursor;
+ int j,x1,x2,y1,y2,bpp=s->rfbServerFormat.bitsPerPixel/8,
+ rowstride=s->paddedWidthInBytes;
+ LOCK(s->cursorMutex);
+ if(!s->cursorIsDrawn) {
+ UNLOCK(s->cursorMutex);
+ return;
+ }
+
+ /* restore what is under the cursor */
+ x1=s->cursorX-c->xhot;
+ x2=x1+c->width;
+ if(x1<0) x1=0;
+ if(x2>=s->width) x2=s->width-1;
+ x2-=x1; if(x2<=0) {
+ UNLOCK(s->cursorMutex);
+ return;
+ }
+ y1=s->cursorY-c->yhot;
+ y2=y1+c->height;
+ if(y1<0) y1=0;
+ if(y2>=s->height) y2=s->height-1;
+ y2-=y1; if(y2<=0) {
+ UNLOCK(s->cursorMutex);
+ return;
+ }
+
+ /* get saved data */
+ for(j=0;j<y2;j++)
+ memcpy(s->frameBuffer+(y1+j)*rowstride+x1*bpp,
+ s->underCursorBuffer+j*x2*bpp,
+ x2*bpp);
+
+ rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2);
+ s->cursorIsDrawn = FALSE;
+ UNLOCK(s->cursorMutex);
+}
+
+void rfbDrawCursor(rfbScreenInfoPtr s)
+{
+ rfbCursorPtr c=s->cursor;
+ int i,j,x1,x2,y1,y2,i1,j1,bpp=s->rfbServerFormat.bitsPerPixel/8,
+ rowstride=s->paddedWidthInBytes,
+ bufSize,w;
+ if(!c) return;
+ LOCK(s->cursorMutex);
+ if(s->cursorIsDrawn) {
+ /* is already drawn */
+ UNLOCK(s->cursorMutex);
+ return;
+ }
+ bufSize=c->width*c->height*bpp;
+ w=(c->width+7)/8;
+ if(s->underCursorBufferLen<bufSize) {
+ if(s->underCursorBuffer!=NULL)
+ free(s->underCursorBuffer);
+ s->underCursorBuffer=malloc(bufSize);
+ s->underCursorBufferLen=bufSize;
+ }
+ /* save what is under the cursor */
+ i1=j1=0; /* offset in cursor */
+ x1=s->cursorX-c->xhot;
+ x2=x1+c->width;
+ if(x1<0) { i1=-x1; x1=0; }
+ if(x2>=s->width) x2=s->width-1;
+ x2-=x1; if(x2<=0) {
+ UNLOCK(s->cursorMutex);
+ return; /* nothing to do */
+ }
+ y1=s->cursorY-c->yhot;
+ y2=y1+c->height;
+ if(y1<0) { j1=-y1; y1=0; }
+ if(y2>=s->height) y2=s->height-1;
+ y2-=y1; if(y2<=0) {
+ UNLOCK(s->cursorMutex);
+ return; /* nothing to do */
+ }
+
+ /* save data */
+ for(j=0;j<y2;j++)
+ memcpy(s->underCursorBuffer+j*x2*bpp,
+ s->frameBuffer+(y1+j)*rowstride+x1*bpp,
+ x2*bpp);
+
+ if(!c->richSource)
+ MakeRichCursorFromXCursor(s,c);
+
+ /* now the cursor has to be drawn */
+ for(j=0;j<y2;j++)
+ for(i=0;i<x2;i++)
+ if((c->mask[(j+j1)*w+(i+i1)/8]<<((i+i1)&7))&0x80)
+ memcpy(s->frameBuffer+(j+y1)*rowstride+(i+x1)*bpp,
+ c->richSource+(j+j1)*c->width*bpp+(i+i1)*bpp,bpp);
+
+
+ rfbMarkRectAsModified(s,x1,y1,x1+x2,y1+y2);
+ s->cursorIsDrawn = TRUE;
+ UNLOCK(s->cursorMutex);
+}
+
+/* for debugging */
+
+void rfbPrintXCursor(rfbCursorPtr cursor)
+{
+ int i,i1,j,w=(cursor->width+7)/8;
+ unsigned char bit;
+ for(j=0;j<cursor->height;j++) {
+ for(i=0,i1=0,bit=0x80;i1<cursor->width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1)
+ if(cursor->source[j*w+i]&bit) putchar('#'); else putchar(' ');
+ putchar(':');
+ for(i=0,i1=0,bit=0x80;i1<cursor->width;i1++,i+=(bit&1)?1:0,bit=(bit&1)?0x80:bit>>1)
+ if(cursor->mask[j*w+i]&bit) putchar('#'); else putchar(' ');
+ putchar('\n');
+ }
+}
+
+extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld)
+{
+ LOCK(rfbScreen->cursorMutex);
+ while(rfbScreen->cursorIsDrawn) {
+ UNLOCK(rfbScreen->cursorMutex);
+ rfbUndrawCursor(rfbScreen);
+ LOCK(rfbScreen->cursorMutex);
+ }
+
+ if(freeOld && rfbScreen->cursor)
+ rfbFreeCursor(rfbScreen->cursor);
+
+ rfbScreen->cursor = c;
+
+ UNLOCK(rfbScreen->cursorMutex);
+}
diff --git a/krfb/libvncserver/cutpaste.c b/krfb/libvncserver/cutpaste.c
new file mode 100644
index 00000000..7dcefe7f
--- /dev/null
+++ b/krfb/libvncserver/cutpaste.c
@@ -0,0 +1,39 @@
+/*
+ * cutpaste.c - routines to deal with cut & paste buffers / selection.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+
+/*
+ * rfbSetXCutText sets the cut buffer to be the given string. We also clear
+ * the primary selection. Ideally we'd like to set it to the same thing, but I
+ * can't work out how to do that without some kind of helper X client.
+ */
+
+void rfbGotXCutText(rfbScreenInfoPtr rfbScreen, char *str, int len)
+{
+ rfbSendServerCutText(rfbScreen, str, len);
+}
diff --git a/krfb/libvncserver/d3des.c b/krfb/libvncserver/d3des.c
new file mode 100644
index 00000000..4994afb1
--- /dev/null
+++ b/krfb/libvncserver/d3des.c
@@ -0,0 +1,442 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC. Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+/*
+static unsigned long KnR[32] = { 0L };
+static unsigned long Kn3[32] = { 0L };
+static unsigned char Df_Key[24] = {
+ 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
+ 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10,
+ 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 };
+*/
+
+static unsigned short bytebit[8] = {
+ 01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static unsigned long bigbyte[24] = {
+ 0x800000L, 0x400000L, 0x200000L, 0x100000L,
+ 0x80000L, 0x40000L, 0x20000L, 0x10000L,
+ 0x8000L, 0x4000L, 0x2000L, 0x1000L,
+ 0x800L, 0x400L, 0x200L, 0x100L,
+ 0x80L, 0x40L, 0x20L, 0x10L,
+ 0x8L, 0x4L, 0x2L, 0x1L };
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static unsigned char pc1[56] = {
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
+
+static unsigned char totrot[16] = {
+ 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static unsigned char pc2[48] = {
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+ register int i, j, l, m, n;
+ unsigned char pc1m[56], pcr[56];
+ unsigned long kn[32];
+
+ for ( j = 0; j < 56; j++ ) {
+ l = pc1[j];
+ m = l & 07;
+ pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+ }
+ for( i = 0; i < 16; i++ ) {
+ if( edf == DE1 ) m = (15 - i) << 1;
+ else m = i << 1;
+ n = m + 1;
+ kn[m] = kn[n] = 0L;
+ for( j = 0; j < 28; j++ ) {
+ l = j + totrot[i];
+ if( l < 28 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 28; j < 56; j++ ) {
+ l = j + totrot[i];
+ if( l < 56 ) pcr[j] = pc1m[l];
+ else pcr[j] = pc1m[l - 28];
+ }
+ for( j = 0; j < 24; j++ ) {
+ if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+ if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+ }
+ }
+ cookey(kn);
+ return;
+ }
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+ register unsigned long *cook, *raw0;
+ unsigned long dough[32];
+ register int i;
+
+ cook = dough;
+ for( i = 0; i < 16; i++, raw1++ ) {
+ raw0 = raw1++;
+ *cook = (*raw0 & 0x00fc0000L) << 6;
+ *cook |= (*raw0 & 0x00000fc0L) << 10;
+ *cook |= (*raw1 & 0x00fc0000L) >> 10;
+ *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+ *cook = (*raw0 & 0x0003f000L) << 12;
+ *cook |= (*raw0 & 0x0000003fL) << 16;
+ *cook |= (*raw1 & 0x0003f000L) >> 4;
+ *cook++ |= (*raw1 & 0x0000003fL);
+ }
+ usekey(dough);
+ return;
+ }
+
+void cpkey(into)
+register unsigned long *into;
+{
+ register unsigned long *from, *endp;
+
+ from = KnL, endp = &KnL[32];
+ while( from < endp ) *into++ = *from++;
+ return;
+ }
+
+void usekey(from)
+register unsigned long *from;
+{
+ register unsigned long *to, *endp;
+
+ to = KnL, endp = &KnL[32];
+ while( to < endp ) *to++ = *from++;
+ return;
+ }
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+ unsigned long work[2];
+
+ scrunch(inblock, work);
+ desfunc(work, KnL);
+ unscrun(work, outblock);
+ return;
+ }
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into++ |= (*outof++ & 0xffL);
+ *into = (*outof++ & 0xffL) << 24;
+ *into |= (*outof++ & 0xffL) << 16;
+ *into |= (*outof++ & 0xffL) << 8;
+ *into |= (*outof & 0xffL);
+ return;
+ }
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into++ = (unsigned char)( *outof++ & 0xffL);
+ *into++ = (unsigned char)((*outof >> 24) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 16) & 0xffL);
+ *into++ = (unsigned char)((*outof >> 8) & 0xffL);
+ *into = (unsigned char)( *outof & 0xffL);
+ return;
+ }
+
+static unsigned long SP1[64] = {
+ 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+ 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+ 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+ 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+ 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+ 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+ 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+ 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+ 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+ 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+ 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+ 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+ 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+ 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+ 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+ 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+ 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+ 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+ 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+ 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+ 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+ 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+ 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+ 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+ 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+ 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+ 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+ 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+ 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+ 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+ 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+ 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+ 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+ 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+ 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+ 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+ 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+ 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+ 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+ 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+ 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+ 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+ 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+ 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+ 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+ 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+ 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+ 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+ 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+ 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+ 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+ 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+ 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+ 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+ 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+ 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+ 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+ 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+ 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+ 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+ 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+ 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+ 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+ 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+ 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+ 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+ 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+ 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+ 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+ 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+ 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+ 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+ 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+ 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+ 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+ 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+ 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+ 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+ 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+ 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+ 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+ 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+ 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+ 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+ 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+ 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+ 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+ 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+ 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+ 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+ 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+ 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+ 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+ 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+ 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+ 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+ 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+ 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+ 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+ 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+ 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+ 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+ 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+ 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+ 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+ 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+ 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+ 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+ 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+ 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+ 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+ 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+ 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+ 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+ 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+ 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+ 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+ 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+ 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+ 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+ 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+ 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+ register unsigned long fval, work, right, leftt;
+ register int round;
+
+ leftt = block[0];
+ right = block[1];
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+ right ^= work;
+ leftt ^= (work << 4);
+ work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+ right ^= work;
+ leftt ^= (work << 16);
+ work = ((right >> 2) ^ leftt) & 0x33333333L;
+ leftt ^= work;
+ right ^= (work << 2);
+ work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+ leftt ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+ for( round = 0; round < 8; round++ ) {
+ work = (right << 28) | (right >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = right ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ leftt ^= fval;
+ work = (leftt << 28) | (leftt >> 4);
+ work ^= *keys++;
+ fval = SP7[ work & 0x3fL];
+ fval |= SP5[(work >> 8) & 0x3fL];
+ fval |= SP3[(work >> 16) & 0x3fL];
+ fval |= SP1[(work >> 24) & 0x3fL];
+ work = leftt ^ *keys++;
+ fval |= SP8[ work & 0x3fL];
+ fval |= SP6[(work >> 8) & 0x3fL];
+ fval |= SP4[(work >> 16) & 0x3fL];
+ fval |= SP2[(work >> 24) & 0x3fL];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >> 1);
+ work = (leftt ^ right) & 0xaaaaaaaaL;
+ leftt ^= work;
+ right ^= work;
+ leftt = (leftt << 31) | (leftt >> 1);
+ work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+ right ^= work;
+ leftt ^= (work << 8);
+ work = ((leftt >> 2) ^ right) & 0x33333333L;
+ right ^= work;
+ leftt ^= (work << 2);
+ work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+ leftt ^= work;
+ right ^= (work << 16);
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+ leftt ^= work;
+ right ^= (work << 4);
+ *block++ = right;
+ *block = leftt;
+ return;
+ }
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/krfb/libvncserver/d3des.h b/krfb/libvncserver/d3des.h
new file mode 100644
index 00000000..b2f97247
--- /dev/null
+++ b/krfb/libvncserver/d3des.h
@@ -0,0 +1,56 @@
+#ifndef D3DES_H
+#define D3DES_H
+
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This software 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.
+ */
+
+/* d3des.h -
+ *
+ * Headers and defines for d3des.c
+ * Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ * (GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0 0 /* MODE == encrypt */
+#define DE1 1 /* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/* hexkey[8] MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/* cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/* cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/* from[8] to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'. They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
+
+#endif
diff --git a/krfb/libvncserver/default8x16.h b/krfb/libvncserver/default8x16.h
new file mode 100644
index 00000000..d557d478
--- /dev/null
+++ b/krfb/libvncserver/default8x16.h
@@ -0,0 +1,261 @@
+unsigned char default8x16FontData[4096+1]={
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7e,0x81,0xa5,0x81,0x81,0xbd,0x99,0x81,0x81,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7e,0xff,0xdb,0xff,0xff,0xc3,0xe7,0xff,0xff,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x6c,0xfe,0xfe,0xfe,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x10,0x38,0x7c,0xfe,0x7c,0x38,0x10,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x18,0x3c,0x3c,0xe7,0xe7,0xe7,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x18,0x3c,0x7e,0xff,0xff,0x7e,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+0xff,0xff,0xff,0xff,0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0xff,0xff,0xff,0xff,
+0x00,0x00,0x00,0x00,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,0x00,0x00,0x00,0x00,
+0xff,0xff,0xff,0xff,0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0xff,0xff,0xff,0xff,
+0x00,0x00,0x1e,0x0e,0x1a,0x32,0x78,0xcc,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x66,0x66,0x66,0x66,0x3c,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3f,0x33,0x3f,0x30,0x30,0x30,0x30,0x70,0xf0,0xe0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7f,0x63,0x7f,0x63,0x63,0x63,0x63,0x67,0xe7,0xe6,0xc0,0x00,0x00,0x00,
+0x00,0x00,0x00,0x18,0x18,0xdb,0x3c,0xe7,0x3c,0xdb,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfe,0xf8,0xf0,0xe0,0xc0,0x80,0x00,0x00,0x00,0x00,
+0x00,0x02,0x06,0x0e,0x1e,0x3e,0xfe,0x3e,0x1e,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x66,0x66,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7f,0xdb,0xdb,0xdb,0x7b,0x1b,0x1b,0x1b,0x1b,0x1b,0x00,0x00,0x00,0x00,
+0x00,0x7c,0xc6,0x60,0x38,0x6c,0xc6,0xc6,0x6c,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xfe,0xfe,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x7e,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x3c,0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x3c,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x18,0x0c,0xfe,0x0c,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x30,0x60,0xfe,0x60,0x30,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0xc0,0xc0,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x24,0x66,0xff,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x10,0x38,0x38,0x7c,0x7c,0xfe,0xfe,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xfe,0xfe,0x7c,0x7c,0x38,0x38,0x10,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x3c,0x3c,0x3c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x6c,0x6c,0xfe,0x6c,0x6c,0x6c,0xfe,0x6c,0x6c,0x00,0x00,0x00,0x00,
+0x18,0x18,0x7c,0xc6,0xc2,0xc0,0x7c,0x06,0x06,0x86,0xc6,0x7c,0x18,0x18,0x00,0x00,
+0x00,0x00,0x00,0x00,0xc2,0xc6,0x0c,0x18,0x30,0x60,0xc6,0x86,0x00,0x00,0x00,0x00,
+0x00,0x00,0x38,0x6c,0x6c,0x38,0x76,0xdc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x30,0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x0c,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x30,0x18,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x18,0x30,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x66,0x3c,0xff,0x3c,0x66,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x02,0x06,0x0c,0x18,0x30,0x60,0xc0,0x80,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xce,0xde,0xf6,0xe6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0x06,0x0c,0x18,0x30,0x60,0xc0,0xc6,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0x06,0x06,0x3c,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x0c,0x1c,0x3c,0x6c,0xcc,0xfe,0x0c,0x0c,0x0c,0x1e,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xfc,0x06,0x06,0x06,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x38,0x60,0xc0,0xc0,0xfc,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfe,0xc6,0x06,0x06,0x0c,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7c,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xc6,0x7e,0x06,0x06,0x06,0x0c,0x78,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x06,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x06,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0x0c,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xde,0xde,0xde,0xdc,0xc0,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x66,0x66,0x66,0x66,0xfc,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xf8,0x6c,0x66,0x66,0x66,0x66,0x66,0x66,0x6c,0xf8,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfe,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xde,0xc6,0xc6,0x66,0x3a,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x1e,0x0c,0x0c,0x0c,0x0c,0x0c,0xcc,0xcc,0xcc,0x78,0x00,0x00,0x00,0x00,
+0x00,0x00,0xe6,0x66,0x66,0x6c,0x78,0x78,0x6c,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+0x00,0x00,0xf0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0xe7,0xff,0xff,0xdb,0xc3,0xc3,0xc3,0xc3,0xc3,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xd6,0xde,0x7c,0x0c,0x0e,0x00,0x00,
+0x00,0x00,0xfc,0x66,0x66,0x66,0x7c,0x6c,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x7c,0xc6,0xc6,0x60,0x38,0x0c,0x06,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xff,0xdb,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0xc3,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x66,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x3c,0x66,0xc3,0xc3,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xff,0xc3,0x86,0x0c,0x18,0x30,0x60,0xc1,0xc3,0xff,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x80,0xc0,0xe0,0x70,0x38,0x1c,0x0e,0x06,0x02,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00,0x00,0x00,0x00,
+0x10,0x38,0x6c,0xc6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,
+0x30,0x30,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0xe0,0x60,0x60,0x78,0x6c,0x66,0x66,0x66,0x66,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x1c,0x0c,0x0c,0x3c,0x6c,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0xcc,0x78,0x00,
+0x00,0x00,0xe0,0x60,0x60,0x6c,0x76,0x66,0x66,0x66,0x66,0xe6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x06,0x06,0x00,0x0e,0x06,0x06,0x06,0x06,0x06,0x06,0x66,0x66,0x3c,0x00,
+0x00,0x00,0xe0,0x60,0x60,0x66,0x6c,0x78,0x78,0x6c,0x66,0xe6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xe6,0xff,0xdb,0xdb,0xdb,0xdb,0xdb,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xf0,0x00,
+0x00,0x00,0x00,0x00,0x00,0x76,0xcc,0xcc,0xcc,0xcc,0xcc,0x7c,0x0c,0x0c,0x1e,0x00,
+0x00,0x00,0x00,0x00,0x00,0xdc,0x76,0x66,0x60,0x60,0x60,0xf0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7c,0xc6,0x60,0x38,0x0c,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x10,0x30,0x30,0xfc,0x30,0x30,0x30,0x30,0x36,0x1c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xc3,0x66,0x3c,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xc3,0xc3,0xc3,0xdb,0xdb,0xff,0x66,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xc3,0x66,0x3c,0x18,0x3c,0x66,0xc3,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0xf8,0x00,
+0x00,0x00,0x00,0x00,0x00,0xfe,0xcc,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0x0e,0x18,0x18,0x18,0x70,0x18,0x18,0x18,0x18,0x0e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x70,0x18,0x18,0x18,0x0e,0x18,0x18,0x18,0x18,0x70,0x00,0x00,0x00,0x00,
+0x00,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xc6,0xfe,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3c,0x66,0xc2,0xc0,0xc0,0xc0,0xc2,0x66,0x3c,0x0c,0x06,0x7c,0x00,0x00,
+0x00,0x00,0xcc,0x00,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x0c,0x18,0x30,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x10,0x38,0x6c,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0xcc,0x00,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x60,0x30,0x18,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x38,0x6c,0x38,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x66,0x3c,0x0c,0x06,0x3c,0x00,0x00,0x00,
+0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xfe,0xc0,0xc0,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x66,0x00,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x18,0x3c,0x66,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x60,0x30,0x18,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0xc6,0x00,0x10,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x38,0x6c,0x38,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x18,0x30,0x60,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0x66,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x6e,0x3b,0x1b,0x7e,0xd8,0xdc,0x77,0x00,0x00,0x00,0x00,
+0x00,0x00,0x3e,0x6c,0xcc,0xcc,0xfe,0xcc,0xcc,0xcc,0xcc,0xce,0x00,0x00,0x00,0x00,
+0x00,0x10,0x38,0x6c,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x60,0x30,0x18,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x30,0x78,0xcc,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x60,0x30,0x18,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc6,0x00,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7e,0x06,0x0c,0x78,0x00,
+0x00,0xc6,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0xc6,0x00,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x18,0x18,0x7e,0xc3,0xc0,0xc0,0xc0,0xc3,0x7e,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x38,0x6c,0x64,0x60,0xf0,0x60,0x60,0x60,0x60,0xe6,0xfc,0x00,0x00,0x00,0x00,
+0x00,0x00,0xc3,0x66,0x3c,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0xfc,0x66,0x66,0x7c,0x62,0x66,0x6f,0x66,0x66,0x66,0xf3,0x00,0x00,0x00,0x00,
+0x00,0x0e,0x1b,0x18,0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0x18,0xd8,0x70,0x00,0x00,
+0x00,0x18,0x30,0x60,0x00,0x78,0x0c,0x7c,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x0c,0x18,0x30,0x00,0x38,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x18,0x30,0x60,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x18,0x30,0x60,0x00,0xcc,0xcc,0xcc,0xcc,0xcc,0xcc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0x76,0xdc,0x00,0xdc,0x66,0x66,0x66,0x66,0x66,0x66,0x00,0x00,0x00,0x00,
+0x76,0xdc,0x00,0xc6,0xe6,0xf6,0xfe,0xde,0xce,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x00,0x3c,0x6c,0x6c,0x3e,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x38,0x6c,0x6c,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x30,0x30,0x00,0x30,0x30,0x60,0xc0,0xc6,0xc6,0x7c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,
+0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x60,0xce,0x9b,0x06,0x0c,0x1f,0x00,0x00,
+0x00,0xc0,0xc0,0xc2,0xc6,0xcc,0x18,0x30,0x66,0xce,0x96,0x3e,0x06,0x06,0x00,0x00,
+0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x18,0x3c,0x3c,0x3c,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x36,0x6c,0xd8,0x6c,0x36,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xd8,0x6c,0x36,0x6c,0xd8,0x00,0x00,0x00,0x00,0x00,0x00,
+0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,
+0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,0x55,0xaa,
+0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,0xdd,0x77,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x00,0x00,0x00,0x00,0x00,0xfe,0x06,0xf6,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0xf6,0x06,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x18,0x18,0x18,0x18,0x18,0xf8,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x3f,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0x37,0x30,0x37,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x36,0x36,0x36,0x36,0x36,0xf7,0x00,0xf7,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x18,0x18,0x18,0x18,0x18,0xff,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x3f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x18,0x18,0x18,0x18,0x18,0x1f,0x18,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x36,0x36,0x36,0x36,0x36,0x36,0x36,0xff,0x36,0x36,0x36,0x36,0x36,0x36,0x36,0x36,
+0x18,0x18,0x18,0x18,0x18,0xff,0x18,0xff,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,
+0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0xd8,0xd8,0xd8,0xdc,0x76,0x00,0x00,0x00,0x00,
+0x00,0x00,0x78,0xcc,0xcc,0xcc,0xd8,0xcc,0xc6,0xc6,0xc6,0xcc,0x00,0x00,0x00,0x00,
+0x00,0x00,0xfe,0xc6,0xc6,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xfe,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0xfe,0xc6,0x60,0x30,0x18,0x30,0x60,0xc6,0xfe,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7e,0xd8,0xd8,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x66,0x66,0x66,0x66,0x66,0x7c,0x60,0x60,0xc0,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x76,0xdc,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x7e,0x18,0x3c,0x66,0x66,0x66,0x3c,0x18,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x38,0x6c,0xc6,0xc6,0xfe,0xc6,0xc6,0x6c,0x38,0x00,0x00,0x00,0x00,
+0x00,0x00,0x38,0x6c,0xc6,0xc6,0xc6,0x6c,0x6c,0x6c,0x6c,0xee,0x00,0x00,0x00,0x00,
+0x00,0x00,0x1e,0x30,0x18,0x0c,0x3e,0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x7e,0xdb,0xdb,0xdb,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x03,0x06,0x7e,0xdb,0xdb,0xf3,0x7e,0x60,0xc0,0x00,0x00,0x00,0x00,
+0x00,0x00,0x1c,0x30,0x60,0x60,0x7c,0x60,0x60,0x60,0x30,0x1c,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00,0xff,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00,0x7e,0x00,0x00,0x00,0x00,
+0x00,0x00,0x0e,0x1b,0x1b,0x1b,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
+0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xd8,0xd8,0xd8,0x70,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x7e,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x76,0xdc,0x00,0x76,0xdc,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x38,0x6c,0x6c,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x0f,0x0c,0x0c,0x0c,0x0c,0x0c,0xec,0x6c,0x6c,0x3c,0x1c,0x00,0x00,0x00,0x00,
+0x00,0xd8,0x6c,0x6c,0x6c,0x6c,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x70,0xd8,0x30,0x60,0xc8,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+};
+int default8x16FontMetaData[256*5+1]={
+0,8,16,0,0,16,8,16,0,0,32,8,16,0,0,48,8,16,0,0,64,8,16,0,0,80,8,16,0,0,96,8,16,0,0,112,8,16,0,0,128,8,16,0,0,144,8,16,0,0,160,8,16,0,0,176,8,16,0,0,192,8,16,0,0,208,8,16,0,0,224,8,16,0,0,240,8,16,0,0,256,8,16,0,0,272,8,16,0,0,288,8,16,0,0,304,8,16,0,0,320,8,16,0,0,336,8,16,0,0,352,8,16,0,0,368,8,16,0,0,384,8,16,0,0,400,8,16,0,0,416,8,16,0,0,432,8,16,0,0,448,8,16,0,0,464,8,16,0,0,480,8,16,0,0,496,8,16,0,0,512,8,16,0,0,528,8,16,0,0,544,8,16,0,0,560,8,16,0,0,576,8,16,0,0,592,8,16,0,0,608,8,16,0,0,624,8,16,0,0,640,8,16,0,0,656,8,16,0,0,672,8,16,0,0,688,8,16,0,0,704,8,16,0,0,720,8,16,0,0,736,8,16,0,0,752,8,16,0,0,768,8,16,0,0,784,8,16,0,0,800,8,16,0,0,816,8,16,0,0,832,8,16,0,0,848,8,16,0,0,864,8,16,0,0,880,8,16,0,0,896,8,16,0,0,912,8,16,0,0,928,8,16,0,0,944,8,16,0,0,960,8,16,0,0,976,8,16,0,0,992,8,16,0,0,1008,8,16,0,0,1024,8,16,0,0,1040,8,16,0,0,1056,8,16,0,0,1072,8,16,0,0,1088,8,16,0,0,1104,8,16,0,0,1120,8,16,0,0,1136,8,16,0,0,1152,8,16,0,0,1168,8,16,0,0,1184,8,16,0,0,1200,8,16,0,0,1216,8,16,0,0,1232,8,16,0,0,1248,8,16,0,0,1264,8,16,0,0,1280,8,16,0,0,1296,8,16,0,0,1312,8,16,0,0,1328,8,16,0,0,1344,8,16,0,0,1360,8,16,0,0,1376,8,16,0,0,1392,8,16,0,0,1408,8,16,0,0,1424,8,16,0,0,1440,8,16,0,0,1456,8,16,0,0,1472,8,16,0,0,1488,8,16,0,0,1504,8,16,0,0,1520,8,16,0,0,1536,8,16,0,0,1552,8,16,0,0,1568,8,16,0,0,1584,8,16,0,0,1600,8,16,0,0,1616,8,16,0,0,1632,8,16,0,0,1648,8,16,0,0,1664,8,16,0,0,1680,8,16,0,0,1696,8,16,0,0,1712,8,16,0,0,1728,8,16,0,0,1744,8,16,0,0,1760,8,16,0,0,1776,8,16,0,0,1792,8,16,0,0,1808,8,16,0,0,1824,8,16,0,0,1840,8,16,0,0,1856,8,16,0,0,1872,8,16,0,0,1888,8,16,0,0,1904,8,16,0,0,1920,8,16,0,0,1936,8,16,0,0,1952,8,16,0,0,1968,8,16,0,0,1984,8,16,0,0,2000,8,16,0,0,2016,8,16,0,0,2032,8,16,0,0,2048,8,16,0,0,2064,8,16,0,0,2080,8,16,0,0,2096,8,16,0,0,2112,8,16,0,0,2128,8,16,0,0,2144,8,16,0,0,2160,8,16,0,0,2176,8,16,0,0,2192,8,16,0,0,2208,8,16,0,0,2224,8,16,0,0,2240,8,16,0,0,2256,8,16,0,0,2272,8,16,0,0,2288,8,16,0,0,2304,8,16,0,0,2320,8,16,0,0,2336,8,16,0,0,2352,8,16,0,0,2368,8,16,0,0,2384,8,16,0,0,2400,8,16,0,0,2416,8,16,0,0,2432,8,16,0,0,2448,8,16,0,0,2464,8,16,0,0,2480,8,16,0,0,2496,8,16,0,0,2512,8,16,0,0,2528,8,16,0,0,2544,8,16,0,0,2560,8,16,0,0,2576,8,16,0,0,2592,8,16,0,0,2608,8,16,0,0,2624,8,16,0,0,2640,8,16,0,0,2656,8,16,0,0,2672,8,16,0,0,2688,8,16,0,0,2704,8,16,0,0,2720,8,16,0,0,2736,8,16,0,0,2752,8,16,0,0,2768,8,16,0,0,2784,8,16,0,0,2800,8,16,0,0,2816,8,16,0,0,2832,8,16,0,0,2848,8,16,0,0,2864,8,16,0,0,2880,8,16,0,0,2896,8,16,0,0,2912,8,16,0,0,2928,8,16,0,0,2944,8,16,0,0,2960,8,16,0,0,2976,8,16,0,0,2992,8,16,0,0,3008,8,16,0,0,3024,8,16,0,0,3040,8,16,0,0,3056,8,16,0,0,3072,8,16,0,0,3088,8,16,0,0,3104,8,16,0,0,3120,8,16,0,0,3136,8,16,0,0,3152,8,16,0,0,3168,8,16,0,0,3184,8,16,0,0,3200,8,16,0,0,3216,8,16,0,0,3232,8,16,0,0,3248,8,16,0,0,3264,8,16,0,0,3280,8,16,0,0,3296,8,16,0,0,3312,8,16,0,0,3328,8,16,0,0,3344,8,16,0,0,3360,8,16,0,0,3376,8,16,0,0,3392,8,16,0,0,3408,8,16,0,0,3424,8,16,0,0,3440,8,16,0,0,3456,8,16,0,0,3472,8,16,0,0,3488,8,16,0,0,3504,8,16,0,0,3520,8,16,0,0,3536,8,16,0,0,3552,8,16,0,0,3568,8,16,0,0,3584,8,16,0,0,3600,8,16,0,0,3616,8,16,0,0,3632,8,16,0,0,3648,8,16,0,0,3664,8,16,0,0,3680,8,16,0,0,3696,8,16,0,0,3712,8,16,0,0,3728,8,16,0,0,3744,8,16,0,0,3760,8,16,0,0,3776,8,16,0,0,3792,8,16,0,0,3808,8,16,0,0,3824,8,16,0,0,3840,8,16,0,0,3856,8,16,0,0,3872,8,16,0,0,3888,8,16,0,0,3904,8,16,0,0,3920,8,16,0,0,3936,8,16,0,0,3952,8,16,0,0,3968,8,16,0,0,3984,8,16,0,0,4000,8,16,0,0,4016,8,16,0,0,4032,8,16,0,0,4048,8,16,0,0,4064,8,16,0,0,4080,8,16,0,0,};
+rfbFontData default8x16Font = { default8x16FontData, default8x16FontMetaData };
diff --git a/krfb/libvncserver/draw.c b/krfb/libvncserver/draw.c
new file mode 100644
index 00000000..24c41bd4
--- /dev/null
+++ b/krfb/libvncserver/draw.c
@@ -0,0 +1,61 @@
+#include "rfb.h"
+
+void rfbFillRect(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,Pixel col)
+{
+ int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3;
+ int i,j;
+ char* colour=(char*)&col;
+
+ if(!rfbEndianTest)
+ colour += 4-bpp;
+ for(j=y1;j<y2;j++)
+ for(i=x1;i<x2;i++)
+ memcpy(s->frameBuffer+j*rowstride+i*bpp,colour,bpp);
+ rfbMarkRectAsModified(s,x1,y1,x2,y2);
+}
+
+#define SETPIXEL(x,y) \
+ memcpy(s->frameBuffer+(y)*rowstride+(x)*bpp,colour,bpp)
+
+void rfbDrawPixel(rfbScreenInfoPtr s,int x,int y,Pixel col)
+{
+ int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3;
+ char* colour=(char*)&col;
+
+ if(!rfbEndianTest)
+ colour += 4-bpp;
+ SETPIXEL(x,y);
+ rfbMarkRectAsModified(s,x,y,x+1,y+1);
+}
+
+void rfbDrawLine(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,Pixel col)
+{
+ int rowstride = s->paddedWidthInBytes, bpp = s->bitsPerPixel>>3;
+ int i;
+ char* colour=(char*)&col;
+
+ if(!rfbEndianTest)
+ colour += 4-bpp;
+
+#define SWAPPOINTS { i=x1; x1=x2; x2=i; i=y1; y1=y2; y2=i; }
+ if(abs(x1-x2)<abs(y1-y2)) {
+ if(y1>y2)
+ SWAPPOINTS
+ for(i=y1;i<=y2;i++)
+ SETPIXEL(x1+(i-y1)*(x2-x1)/(y2-y1),i);
+ /* TODO: Maybe make this more intelligently? */
+ if(x2<x1) { i=x1; x1=x2; x2=i; }
+ rfbMarkRectAsModified(s,x1,y1,x2+1,y2+1);
+ } else {
+ if(x1>x2)
+ SWAPPOINTS
+ else if(x1==x2) {
+ rfbDrawPixel(s,x1,y1,col);
+ return;
+ }
+ for(i=x1;i<=x2;i++)
+ SETPIXEL(i,y1+(i-x1)*(y2-y1)/(x2-x1));
+ if(y2<y1) { i=y1; y1=y2; y2=i; }
+ rfbMarkRectAsModified(s,x1,y1,x2+1,y2+1);
+ }
+}
diff --git a/krfb/libvncserver/example.c b/krfb/libvncserver/example.c
new file mode 100644
index 00000000..41165c7b
--- /dev/null
+++ b/krfb/libvncserver/example.c
@@ -0,0 +1,279 @@
+/*
+ *
+ * This is an example of how to use libvncserver.
+ *
+ * libvncserver example
+ * Copyright (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef WIN32
+#define sleep Sleep
+#else
+#include <unistd.h>
+#endif
+
+#ifdef __IRIX__
+#include <netdb.h>
+#endif
+
+#include "rfb.h"
+#include "keysym.h"
+
+const int maxx=640, maxy=480, bpp=4;
+/* TODO: odd maxx doesn't work (vncviewer bug) */
+
+/* This initializes a nice (?) background */
+
+void initBuffer(unsigned char* buffer)
+{
+ int i,j;
+ for(j=0;j<maxy;++j) {
+ for(i=0;i<maxx;++i) {
+ buffer[(j*maxx+i)*bpp+0]=(i+j)*128/(maxx+maxy); /* red */
+ buffer[(j*maxx+i)*bpp+1]=i*128/maxx; /* green */
+ buffer[(j*maxx+i)*bpp+2]=j*256/maxy; /* blue */
+ }
+ buffer[j*maxx*bpp+0]=0xff;
+ buffer[j*maxx*bpp+1]=0xff;
+ buffer[j*maxx*bpp+2]=0xff;
+ buffer[j*maxx*bpp+3]=0xff;
+ }
+}
+
+/* Here we create a structure so that every client has it's own pointer */
+
+typedef struct ClientData {
+ Bool oldButton;
+ int oldx,oldy;
+} ClientData;
+
+void clientgone(rfbClientPtr cl)
+{
+ free(cl->clientData);
+}
+
+enum rfbNewClientAction newclient(rfbClientPtr cl)
+{
+ cl->clientData = (void*)calloc(sizeof(ClientData),1);
+ cl->clientGoneHook = clientgone;
+ return RFB_CLIENT_ACCEPT;
+}
+
+/* aux function to draw a line */
+
+void drawline(unsigned char* buffer,int rowstride,int bpp,int x1,int y1,int x2,int y2)
+{
+ int i,j;
+ i=x1-x2; j=y1-y2;
+ if(i==0 && j==0) {
+ for(i=0;i<bpp;i++)
+ buffer[y1*rowstride+x1*bpp+i]=0xff;
+ return;
+ }
+ if(i<0) i=-i;
+ if(j<0) j=-j;
+ if(i<j) {
+ if(y1>y2) { i=y2; y2=y1; y1=i; i=x2; x2=x1; x1=i; }
+ if(y2==y1) { if(y2>0) y1--; else y2++; }
+ for(j=y1;j<=y2;j++)
+ for(i=0;i<bpp;i++)
+ buffer[j*rowstride+(x1+(j-y1)*(x2-x1)/(y2-y1))*bpp+i]=0xff;
+ } else {
+ if(x1>x2) { i=y2; y2=y1; y1=i; i=x2; x2=x1; x1=i; }
+ for(i=x1;i<=x2;i++)
+ for(j=0;j<bpp;j++)
+ buffer[(y1+(i-x1)*(y2-y1)/(x2-x1))*rowstride+i*bpp+j]=0xff;
+ }
+}
+
+/* Here the pointer events are handled */
+
+void doptr(int buttonMask,int x,int y,rfbClientPtr cl)
+{
+ ClientData* cd=cl->clientData;
+
+ if(cl->screen->cursorIsDrawn)
+ rfbUndrawCursor(cl->screen);
+
+ if(x>=0 && y>=0 && x<maxx && y<maxy) {
+ if(buttonMask) {
+ int i,j,x1,x2,y1,y2;
+
+ if(cd->oldButton==buttonMask) { /* draw a line */
+ drawline(cl->screen->frameBuffer,cl->screen->paddedWidthInBytes,bpp,
+ x,y,cd->oldx,cd->oldy);
+ rfbMarkRectAsModified(cl->screen,x,y,cd->oldx,cd->oldy);
+ } else { /* draw a point (diameter depends on button) */
+ int w=cl->screen->paddedWidthInBytes;
+ x1=x-buttonMask; if(x1<0) x1=0;
+ x2=x+buttonMask; if(x2>maxx) x2=maxx;
+ y1=y-buttonMask; if(y1<0) y1=0;
+ y2=y+buttonMask; if(y2>maxy) y2=maxy;
+
+ for(i=x1*bpp;i<x2*bpp;i++)
+ for(j=y1;j<y2;j++)
+ cl->screen->frameBuffer[j*w+i]=(char)0xff;
+ rfbMarkRectAsModified(cl->screen,x1,y1,x2-1,y2-1);
+ }
+
+ /* we could get a selection like that:
+ rfbGotXCutText(cl->screen,"Hallo",5);
+ */
+ } else
+ cd->oldButton=0;
+
+ cd->oldx=x; cd->oldy=y; cd->oldButton=buttonMask;
+ }
+ defaultPtrAddEvent(buttonMask,x,y,cl);
+}
+
+/* aux function to draw a character to x, y */
+
+#include "radon.h"
+
+/* Here the key events are handled */
+
+void dokey(Bool down,KeySym key,rfbClientPtr cl)
+{
+ if(down) {
+ if(key==XK_Escape)
+ rfbCloseClient(cl);
+ else if(key==XK_Page_Up) {
+ if(cl->screen->cursorIsDrawn)
+ rfbUndrawCursor(cl->screen);
+ initBuffer(cl->screen->frameBuffer);
+ rfbMarkRectAsModified(cl->screen,0,0,maxx,maxy);
+ } else if(key>=' ' && key<0x100) {
+ ClientData* cd=cl->clientData;
+ int x1=cd->oldx,y1=cd->oldy,x2,y2;
+ if(cl->screen->cursorIsDrawn)
+ rfbUndrawCursor(cl->screen);
+ cd->oldx+=rfbDrawChar(cl->screen,&radonFont,cd->oldx,cd->oldy,(char)key,0x00ffffff);
+ rfbFontBBox(&radonFont,(char)key,&x1,&y1,&x2,&y2);
+ rfbMarkRectAsModified(cl->screen,x1,y1,x2-1,y2-1);
+ }
+ }
+}
+
+/* Example for an XCursor (foreground/background only) */
+
+int exampleXCursorWidth=9,exampleXCursorHeight=7;
+char exampleXCursor[]=
+ " "
+ " xx xx "
+ " xx xx "
+ " xxx "
+ " xx xx "
+ " xx xx "
+ " ";
+
+/* Example for a rich cursor (full-colour) */
+
+void MakeRichCursor(rfbScreenInfoPtr rfbScreen)
+{
+ int i,j,w=32,h=32;
+ rfbCursorPtr c = rfbScreen->cursor;
+ char bitmap[]=
+ " "
+ " xxxxxx "
+ " xxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxx xxxxxxxx xxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxx xxxxxxxxxxx xxxxxxx "
+ " xxxx xxxxxxxxx xxxxxx "
+ " xxxxx xxxxxxxxxxx xxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxx xxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxx xxxxxxxxxxxxxx "
+ " xxxxxxxxxx xxxxxxxxxxxx "
+ " xxxxxxxxx xxxxxxxxx "
+ " xxxxxxxxxx xxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxx "
+ " xxxx xxxxxxxxxxxxx "
+ " xx x xxxxxxxxxxx "
+ " xxx xxxxxxxxxxx "
+ " xxxx xxxxxxxxxxx "
+ " xxxxxx xxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxxxxxxxx "
+ " xxxxxxxxxxxxxxxx "
+ " ";
+ c=rfbScreen->cursor = rfbMakeXCursor(w,h,bitmap,bitmap);
+ c->xhot = 16; c->yhot = 24;
+
+ c->richSource = malloc(w*h*bpp);
+ for(j=0;j<h;j++) {
+ for(i=0;i<w;i++) {
+ c->richSource[j*w*bpp+i*bpp+0]=i*0xff/w;
+ c->richSource[j*w*bpp+i*bpp+1]=(i+j)*0xff/(w+h);
+ c->richSource[j*w*bpp+i*bpp+2]=j*0xff/h;
+ c->richSource[j*w*bpp+i*bpp+3]=0;
+ }
+ }
+}
+
+/* Initialization */
+
+int main(int argc,char** argv)
+{
+ rfbScreenInfoPtr rfbScreen =
+ rfbGetScreen(&argc,argv,maxx,maxy,8,3,bpp);
+ rfbScreen->desktopName = "LibVNCServer Example";
+ rfbScreen->frameBuffer = (char*)malloc(maxx*maxy*bpp);
+ rfbScreen->rfbAlwaysShared = TRUE;
+ rfbScreen->ptrAddEvent = doptr;
+ rfbScreen->kbdAddEvent = dokey;
+ rfbScreen->newClientHook = newclient;
+ rfbScreen->httpDir = "./classes";
+
+ initBuffer(rfbScreen->frameBuffer);
+ rfbDrawString(rfbScreen,&radonFont,20,100,"Hello, World!",0xffffff);
+
+ /* This call creates a mask and then a cursor: */
+ /* rfbScreen->defaultCursor =
+ rfbMakeXCursor(exampleCursorWidth,exampleCursorHeight,exampleCursor,0);
+ */
+
+ MakeRichCursor(rfbScreen);
+
+ /* initialize the server */
+ rfbInitServer(rfbScreen);
+
+#ifndef BACKGROUND_LOOP_TEST
+ /* this is the blocking event loop, i.e. it never returns */
+ /* 40000 are the microseconds, i.e. 0.04 seconds */
+ rfbRunEventLoop(rfbScreen,40000,FALSE);
+#elif !defined(HAVE_PTHREADS)
+#error "I need pthreads for that."
+#endif
+
+ /* this is the non-blocking event loop; a background thread is started */
+ rfbRunEventLoop(rfbScreen,-1,TRUE);
+ /* now we could do some cool things like rendering */
+ while(1) sleep(5); /* render(); */
+
+ return(0);
+}
diff --git a/krfb/libvncserver/font.c b/krfb/libvncserver/font.c
new file mode 100644
index 00000000..b9d68d0f
--- /dev/null
+++ b/krfb/libvncserver/font.c
@@ -0,0 +1,192 @@
+#include "rfb.h"
+
+int rfbDrawChar(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
+ int x,int y,unsigned char c,Pixel col)
+{
+ int i,j,width,height;
+ unsigned char* data=font->data+font->metaData[c*5];
+ unsigned char d=*data;
+ int rowstride=rfbScreen->paddedWidthInBytes;
+ int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8;
+ char *colour=(char*)&col;
+
+ if(!rfbEndianTest)
+ colour += 4-bpp;
+
+ width=font->metaData[c*5+1];
+ height=font->metaData[c*5+2];
+ x+=font->metaData[c*5+3];
+ y+=-font->metaData[c*5+4]-height+1;
+
+ for(j=0;j<height;j++) {
+ for(i=0;i<width;i++) {
+ if((i&7)==0) {
+ d=*data;
+ data++;
+ }
+ if(d&0x80)
+ memcpy(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)*bpp,colour,bpp);
+ d<<=1;
+ }
+ /* if((i&7)!=0) data++; */
+ }
+ return(width);
+}
+
+void rfbDrawString(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
+ int x,int y,const char* string,Pixel colour)
+{
+ while(*string) {
+ x+=rfbDrawChar(rfbScreen,font,x,y,*string,colour);
+ string++;
+ }
+}
+
+/* TODO: these two functions need to be more efficient */
+int rfbDrawCharWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
+ int x,int y,unsigned char c,
+ int x1,int y1,int x2,int y2,
+ Pixel col,Pixel bcol)
+{
+ int i,j,width,height;
+ unsigned char* data=font->data+font->metaData[c*5];
+ unsigned char d;
+ int rowstride=rfbScreen->paddedWidthInBytes;
+ int bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8,extra_bytes=0;
+ char* colour=(char*)&col;
+ char* bcolour=(char*)&bcol;
+
+ if(!rfbEndianTest) {
+ colour+=4-bpp;
+ bcolour+=4-bpp;
+ }
+
+ width=font->metaData[c*5+1];
+ height=font->metaData[c*5+2];
+ x+=font->metaData[c*5+3];
+ y+=-font->metaData[c*5+4]-height+1;
+
+ /* after clipping, x2 will be count of bytes between rows,
+ * x1 start of i, y1 start of j, width and height will be adjusted. */
+ if(y1>y) { y1-=y; data+=(width+7)/8; height-=y1; y+=y1; } else y1=0;
+ if(x1>x) { x1-=x; data+=x1; width-=x1; x+=x1; extra_bytes+=x1/8; } else x1=0;
+ if(y2<y+height) height-=y+height-y2;
+ if(x2<x+width) { extra_bytes+=(x1+width)/8-(x+width-x2+7)/8; width-=x+width-x2; }
+
+ d=*data;
+ for(j=y1;j<height;j++) {
+ if((x1&7)!=0)
+ d=data[-1]; /* TODO: check if in this case extra_bytes is correct! */
+ for(i=x1;i<width;i++) {
+ if((i&7)==0) {
+ d=*data;
+ data++;
+ }
+ /* if(x+i>=x1 && x+i<x2 && y+j>=y1 && y+j<y2) */ {
+ if(d&0x80) {
+ memcpy(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)*bpp,
+ colour,bpp);
+ } else if(bcol!=col) {
+ memcpy(rfbScreen->frameBuffer+(y+j)*rowstride+(x+i)*bpp,
+ bcolour,bpp);
+ }
+ }
+ d<<=1;
+ }
+ /* if((i&7)==0) data++; */
+ data += extra_bytes;
+ }
+ return(width);
+}
+
+void rfbDrawStringWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
+ int x,int y,const char* string,
+ int x1,int y1,int x2,int y2,
+ Pixel colour,Pixel backColour)
+{
+ while(*string) {
+ x+=rfbDrawCharWithClip(rfbScreen,font,x,y,*string,x1,y1,x2,y2,
+ colour,backColour);
+ string++;
+ }
+}
+
+int rfbWidthOfString(rfbFontDataPtr font,const char* string)
+{
+ int i=0;
+ while(*string) {
+ i+=font->metaData[*string*5+1];
+ string++;
+ }
+ return(i);
+}
+
+int rfbWidthOfChar(rfbFontDataPtr font,unsigned char c)
+{
+ return(font->metaData[c*5+1]+font->metaData[c*5+3]);
+}
+
+void rfbFontBBox(rfbFontDataPtr font,unsigned char c,int* x1,int* y1,int* x2,int* y2)
+{
+ *x1+=font->metaData[c*5+3];
+ *y1+=-font->metaData[c*5+4]-font->metaData[c*5+2]+1;
+ *x2=*x1+font->metaData[c*5+1];
+ *y2=*y1+font->metaData[c*5+2];
+}
+
+#ifndef INT_MAX
+#define INT_MAX 0x7fffffff
+#endif
+
+void rfbWholeFontBBox(rfbFontDataPtr font,
+ int *x1, int *y1, int *x2, int *y2)
+{
+ int i;
+ int* m=font->metaData;
+
+ (*x1)=(*y1)=INT_MAX; (*x2)=(*y2)=-INT_MAX+1;
+ for(i=0;i<256;i++) {
+ if(m[i*5+1]-m[i*5+3]>(*x2))
+ (*x2)=m[i*5+1]-m[i*5+3];
+ if(-m[i*5+2]+m[i*5+4]<(*y1))
+ (*y1)=-m[i*5+2]+m[i*5+4];
+ if(m[i*5+3]<(*x1))
+ (*x1)=m[i*5+3];
+ if(-m[i*5+4]>(*y2))
+ (*y2)=-m[i*5+4];
+ }
+}
+
+rfbFontDataPtr rfbLoadConsoleFont(char *filename)
+{
+ FILE *f=fopen(filename,"rb");
+ rfbFontDataPtr p;
+ int i;
+
+ if(!f) return(0);
+
+ p=(rfbFontDataPtr)malloc(sizeof(rfbFontData));
+ p->data=(unsigned char*)malloc(4096);
+ if(1!=fread(p->data,4096,1,f)) {
+ free(p->data);
+ free(p);
+ return(0);
+ }
+ fclose(f);
+ p->metaData=(int*)malloc(256*5*sizeof(int));
+ for(i=0;i<256;i++) {
+ p->metaData[i*5+0]=i*16; /* offset */
+ p->metaData[i*5+1]=8; /* width */
+ p->metaData[i*5+2]=16; /* height */
+ p->metaData[i*5+3]=0; /* xhot */
+ p->metaData[i*5+4]=0; /* yhot */
+ }
+ return(p);
+}
+
+void rfbFreeFont(rfbFontDataPtr f)
+{
+ free(f->data);
+ free(f->metaData);
+ free(f);
+}
diff --git a/krfb/libvncserver/fontsel.c b/krfb/libvncserver/fontsel.c
new file mode 100644
index 00000000..100db817
--- /dev/null
+++ b/krfb/libvncserver/fontsel.c
@@ -0,0 +1,71 @@
+#include "rfb.h"
+
+#define FONTDIR "/usr/lib/kbd/consolefonts/"
+#define DEFAULTFONT FONTDIR "default8x16"
+
+char *fontlist[50]={
+"8x16alt", "b.fnt", "c.fnt", "default8x16", "m.fnt", "ml.fnt", "mod_d.fnt",
+"mod_s.fnt", "mr.fnt", "mu.fnt", "r.fnt", "rl.fnt", "ro.fnt", "s.fnt",
+"sc.fnt", "scrawl_s.fnt", "scrawl_w.fnt", "sd.fnt", "t.fnt",
+ 0
+};
+
+rfbScreenInfoPtr rfbScreen = 0;
+rfbFontDataPtr curFont = 0;
+void showFont(int index)
+{
+ char buffer[1024];
+
+ if(!rfbScreen) return;
+
+ if(curFont)
+ rfbFreeFont(curFont);
+
+ strcpy(buffer,FONTDIR);
+ strcat(buffer,fontlist[index]);
+ curFont = rfbLoadConsoleFont(buffer);
+
+ rfbFillRect(rfbScreen,210,30-20,210+10*16,30-20+256*20/16,0xb77797);
+ if(curFont) {
+ int i,j;
+ for(j=0;j<256;j+=16)
+ for(i=0;i<16;i++)
+ rfbDrawCharWithClip(rfbScreen,curFont,210+10*i,30+j*20/16,j+i,
+ 0,0,640,480,0xffffff,0x000000);
+ }
+}
+
+int main(int argc,char** argv)
+{
+ rfbFontDataPtr font;
+ rfbScreenInfoPtr s=rfbGetScreen(&argc,argv,640,480,8,3,3);
+ int i,j;
+
+ s->frameBuffer=(char*)malloc(640*480*3);
+ rfbInitServer(s);
+
+ for(j=0;j<480;j++)
+ for(i=0;i<640;i++) {
+ s->frameBuffer[(j*640+i)*3+0]=j*256/480;
+ s->frameBuffer[(j*640+i)*3+1]=i*256/640;
+ s->frameBuffer[(j*640+i)*3+2]=(i+j)*256/(480+640);
+ }
+
+ rfbScreen = s;
+ font=rfbLoadConsoleFont(DEFAULTFONT);
+ if(!font) {
+ fprintf(stderr,"Couldn't find %s\n",DEFAULTFONT);
+ exit(1);
+ }
+
+ for(j=0;j<0;j++)
+ rfbProcessEvents(s,900000);
+
+ i = rfbSelectBox(s,font,fontlist,10,20,200,300,0xffdfdf,0x602040,2,showFont);
+ fprintf(stderr,"Selection: %d: %s\n",i,(i>=0)?fontlist[i]:"cancelled");
+
+ rfbFreeFont(font);
+
+ return(0);
+}
+
diff --git a/krfb/libvncserver/hextile.c b/krfb/libvncserver/hextile.c
new file mode 100644
index 00000000..6166844e
--- /dev/null
+++ b/krfb/libvncserver/hextile.c
@@ -0,0 +1,347 @@
+/*
+ * hextile.c
+ *
+ * Routines to implement Hextile Encoding
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+static Bool sendHextiles8(rfbClientPtr cl, int x, int y, int w, int h);
+static Bool sendHextiles16(rfbClientPtr cl, int x, int y, int w, int h);
+static Bool sendHextiles32(rfbClientPtr cl, int x, int y, int w, int h);
+
+
+/*
+ * rfbSendRectEncodingHextile - send a rectangle using hextile encoding.
+ */
+
+Bool
+rfbSendRectEncodingHextile(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingHextile);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cl->rfbRectanglesSent[rfbEncodingHextile]++;
+ cl->rfbBytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader;
+
+ switch (cl->format.bitsPerPixel) {
+ case 8:
+ return sendHextiles8(cl, x, y, w, h);
+ case 16:
+ return sendHextiles16(cl, x, y, w, h);
+ case 32:
+ return sendHextiles32(cl, x, y, w, h);
+ }
+
+ rfbLog("rfbSendRectEncodingHextile: bpp %d?\n", cl->format.bitsPerPixel);
+ return FALSE;
+}
+
+
+#define PUT_PIXEL8(pix) (cl->updateBuf[cl->ublen++] = (pix))
+
+#define PUT_PIXEL16(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \
+ cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1])
+
+#define PUT_PIXEL32(pix) (cl->updateBuf[cl->ublen++] = ((char*)&(pix))[0], \
+ cl->updateBuf[cl->ublen++] = ((char*)&(pix))[1], \
+ cl->updateBuf[cl->ublen++] = ((char*)&(pix))[2], \
+ cl->updateBuf[cl->ublen++] = ((char*)&(pix))[3])
+
+
+#define DEFINE_SEND_HEXTILES(bpp) \
+ \
+ \
+static Bool subrectEncode##bpp(rfbClientPtr cli, CARD##bpp *data, int w, int h, \
+ CARD##bpp bg, CARD##bpp fg, Bool mono); \
+static void testColours##bpp(CARD##bpp *data, int size, Bool *mono, \
+ Bool *solid, CARD##bpp *bg, CARD##bpp *fg); \
+ \
+ \
+/* \
+ * rfbSendHextiles \
+ */ \
+ \
+static Bool \
+sendHextiles##bpp(cl, rx, ry, rw, rh) \
+ rfbClientPtr cl; \
+ int rx, ry, rw, rh; \
+{ \
+ int x, y, w, h; \
+ int startUblen; \
+ char *fbptr; \
+ CARD##bpp bg = 0, fg = 0, newBg, newFg; \
+ Bool mono, solid; \
+ Bool validBg = FALSE; \
+ Bool validFg = FALSE; \
+ CARD##bpp clientPixelData[16*16*(bpp/8)]; \
+ \
+ for (y = ry; y < ry+rh; y += 16) { \
+ for (x = rx; x < rx+rw; x += 16) { \
+ w = h = 16; \
+ if (rx+rw - x < 16) \
+ w = rx+rw - x; \
+ if (ry+rh - y < 16) \
+ h = ry+rh - y; \
+ \
+ if ((cl->ublen + 1 + (2 + 16 * 16) * (bpp/8)) > \
+ UPDATE_BUF_SIZE) { \
+ if (!rfbSendUpdateBuf(cl)) \
+ return FALSE; \
+ } \
+ \
+ fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y) \
+ + (x * (cl->screen->bitsPerPixel / 8))); \
+ \
+ (*cl->translateFn)(cl->translateLookupTable, &(cl->screen->rfbServerFormat), \
+ &cl->format, fbptr, (char *)clientPixelData, \
+ cl->screen->paddedWidthInBytes, w, h); \
+ \
+ startUblen = cl->ublen; \
+ cl->updateBuf[startUblen] = 0; \
+ cl->ublen++; \
+ \
+ testColours##bpp(clientPixelData, w * h, \
+ &mono, &solid, &newBg, &newFg); \
+ \
+ if (!validBg || (newBg != bg)) { \
+ validBg = TRUE; \
+ bg = newBg; \
+ cl->updateBuf[startUblen] |= rfbHextileBackgroundSpecified; \
+ PUT_PIXEL##bpp(bg); \
+ } \
+ \
+ if (solid) { \
+ cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \
+ continue; \
+ } \
+ \
+ cl->updateBuf[startUblen] |= rfbHextileAnySubrects; \
+ \
+ if (mono) { \
+ if (!validFg || (newFg != fg)) { \
+ validFg = TRUE; \
+ fg = newFg; \
+ cl->updateBuf[startUblen] |= rfbHextileForegroundSpecified; \
+ PUT_PIXEL##bpp(fg); \
+ } \
+ } else { \
+ validFg = FALSE; \
+ cl->updateBuf[startUblen] |= rfbHextileSubrectsColoured; \
+ } \
+ \
+ if (!subrectEncode##bpp(cl, clientPixelData, w, h, bg, fg, mono)) { \
+ /* encoding was too large, use raw */ \
+ validBg = FALSE; \
+ validFg = FALSE; \
+ cl->ublen = startUblen; \
+ cl->updateBuf[cl->ublen++] = rfbHextileRaw; \
+ (*cl->translateFn)(cl->translateLookupTable, \
+ &(cl->screen->rfbServerFormat), &cl->format, fbptr, \
+ (char *)clientPixelData, \
+ cl->screen->paddedWidthInBytes, w, h); \
+ \
+ memcpy(&cl->updateBuf[cl->ublen], (char *)clientPixelData, \
+ w * h * (bpp/8)); \
+ \
+ cl->ublen += w * h * (bpp/8); \
+ } \
+ \
+ cl->rfbBytesSent[rfbEncodingHextile] += cl->ublen - startUblen; \
+ } \
+ } \
+ \
+ return TRUE; \
+} \
+ \
+ \
+static Bool \
+subrectEncode##bpp(rfbClientPtr cl, CARD##bpp *data, int w, int h, \
+ CARD##bpp bg, CARD##bpp fg, Bool mono) \
+{ \
+ CARD##bpp cl2; \
+ int x,y; \
+ int i,j; \
+ int hx=0,hy,vx=0,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ int nSubrectsUblen; \
+ \
+ nSubrectsUblen = cl->ublen; \
+ cl->ublen++; \
+ \
+ for (y=0; y<h; y++) { \
+ line = data+(y*w); \
+ for (x=0; x<w; x++) { \
+ if (line[x] != bg) { \
+ cl2 = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) { \
+ seg = data+(j*w); \
+ if (seg[x] != cl2) {break;} \
+ i = x; \
+ while ((seg[i] == cl2) && (i < w)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) { \
+ hy += 1; \
+ } else { \
+ hyflag = 0; \
+ } \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and \
+ * (x,y,vx,vy). We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) { \
+ thew = hw; \
+ theh = hh; \
+ } else { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ if (mono) { \
+ newLen = cl->ublen - nSubrectsUblen + 2; \
+ } else { \
+ newLen = cl->ublen - nSubrectsUblen + bpp/8 + 2; \
+ } \
+ \
+ if (newLen > (w * h * (bpp/8))) \
+ return FALSE; \
+ \
+ numsubs += 1; \
+ \
+ if (!mono) PUT_PIXEL##bpp(cl2); \
+ \
+ cl->updateBuf[cl->ublen++] = rfbHextilePackXY(thex,they); \
+ cl->updateBuf[cl->ublen++] = rfbHextilePackWH(thew,theh); \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) { \
+ for (i=thex; i < (thex+thew); i++) { \
+ data[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ cl->updateBuf[nSubrectsUblen] = numsubs; \
+ \
+ return TRUE; \
+} \
+ \
+ \
+/* \
+ * testColours() tests if there are one (solid), two (mono) or more \
+ * colours in a tile and gets a reasonable guess at the best background \
+ * pixel, and the foreground pixel for mono. \
+ */ \
+ \
+static void \
+testColours##bpp(data,size,mono,solid,bg,fg) \
+ CARD##bpp *data; \
+ int size; \
+ Bool *mono; \
+ Bool *solid; \
+ CARD##bpp *bg; \
+ CARD##bpp *fg; \
+{ \
+ CARD##bpp colour1 = 0, colour2 = 0; \
+ int n1 = 0, n2 = 0; \
+ *mono = TRUE; \
+ *solid = TRUE; \
+ \
+ for (; size > 0; size--, data++) { \
+ \
+ if (n1 == 0) \
+ colour1 = *data; \
+ \
+ if (*data == colour1) { \
+ n1++; \
+ continue; \
+ } \
+ \
+ if (n2 == 0) { \
+ *solid = FALSE; \
+ colour2 = *data; \
+ } \
+ \
+ if (*data == colour2) { \
+ n2++; \
+ continue; \
+ } \
+ \
+ *mono = FALSE; \
+ break; \
+ } \
+ \
+ if (n1 > n2) { \
+ *bg = colour1; \
+ *fg = colour2; \
+ } else { \
+ *bg = colour2; \
+ *fg = colour1; \
+ } \
+}
+
+DEFINE_SEND_HEXTILES(8)
+DEFINE_SEND_HEXTILES(16)
+DEFINE_SEND_HEXTILES(32)
diff --git a/krfb/libvncserver/httpd.c b/krfb/libvncserver/httpd.c
new file mode 100644
index 00000000..72019b64
--- /dev/null
+++ b/krfb/libvncserver/httpd.c
@@ -0,0 +1,423 @@
+/*
+ * httpd.c - a simple HTTP server
+ */
+
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock.h>
+#define close closesocket
+#else
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#ifdef __osf__
+typedef int socklen_t;
+#endif
+
+#ifdef USE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#include "rfb.h"
+
+#define NOT_FOUND_STR "HTTP/1.0 404 Not found\n\n" \
+ "<HEAD><TITLE>File Not Found</TITLE></HEAD>\n" \
+ "<BODY><H1>File Not Found</H1></BODY>\n"
+
+#define OK_STR "HTTP/1.0 200 OK\nContent-Type: text/html\n\n"
+
+static void httpProcessInput();
+static Bool compareAndSkip(char **ptr, const char *str);
+
+/*
+int httpPort = 0;
+char *httpDir = NULL;
+
+int httpListenSock = -1;
+int httpSock = -1;
+FILE* httpFP = NULL;
+*/
+
+#define BUF_SIZE 32768
+
+static char buf[BUF_SIZE];
+static size_t buf_filled=0;
+
+
+/*
+ * httpInitSockets sets up the TCP socket to listen for HTTP connections.
+ */
+
+void
+httpInitSockets(rfbScreenInfoPtr rfbScreen)
+{
+ if (rfbScreen->httpInitDone)
+ return;
+
+ rfbScreen->httpInitDone = TRUE;
+
+ if (!rfbScreen->httpDir)
+ return;
+
+ if (rfbScreen->httpPort == 0) {
+ rfbScreen->httpPort = rfbScreen->rfbPort-100;
+ }
+
+ rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort);
+
+ rfbLog(" URL http://%s:%d\n",rfbScreen->rfbThisHost,rfbScreen->httpPort);
+
+ if ((rfbScreen->httpListenSock = ListenOnTCPPort(rfbScreen->httpPort)) < 0) {
+ rfbLogPerror("ListenOnTCPPort");
+ exit(1);
+ }
+
+ /*AddEnabledDevice(httpListenSock);*/
+}
+
+
+/*
+ * httpCheckFds is called from ProcessInputEvents to check for input on the
+ * HTTP socket(s). If there is input to process, httpProcessInput is called.
+ */
+
+void
+httpCheckFds(rfbScreenInfoPtr rfbScreen)
+{
+ int nfds;
+ fd_set fds;
+ struct timeval tv;
+ struct sockaddr_in addr;
+ size_t addrlen = sizeof(addr);
+
+ if (!rfbScreen->httpDir)
+ return;
+
+ FD_ZERO(&fds);
+ FD_SET(rfbScreen->httpListenSock, &fds);
+ if (rfbScreen->httpSock >= 0) {
+ FD_SET(rfbScreen->httpSock, &fds);
+ }
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv);
+ if (nfds == 0) {
+ return;
+ }
+ if (nfds < 0) {
+#ifdef WIN32
+ errno = WSAGetLastError();
+#endif
+ rfbLogPerror("httpCheckFds: select");
+ return;
+ }
+
+ if ((rfbScreen->httpSock >= 0) && FD_ISSET(rfbScreen->httpSock, &fds)) {
+ httpProcessInput(rfbScreen);
+ }
+
+ if (FD_ISSET(rfbScreen->httpListenSock, &fds)) {
+ int flags;
+ if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock);
+
+ if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock,
+ (struct sockaddr *)&addr, &addrlen)) < 0) {
+ rfbLogPerror("httpCheckFds: accept");
+ return;
+ }
+#ifdef USE_LIBWRAP
+ if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr),
+ STRING_UNKNOWN)) {
+ rfbLog("Rejected connection from client %s\n",
+ inet_ntoa(addr.sin_addr));
+#else
+ if ((rfbScreen->httpFP = fdopen(rfbScreen->httpSock, "r+")) == NULL) {
+ rfbLogPerror("httpCheckFds: fdopen");
+#endif
+ close(rfbScreen->httpSock);
+ rfbScreen->httpSock = -1;
+ return;
+ }
+ flags=fcntl(rfbScreen->httpSock,F_GETFL);
+ if(flags==-1 ||
+ fcntl(rfbScreen->httpSock,F_SETFL,flags|O_NONBLOCK)==-1) {
+ rfbLogPerror("httpCheckFds: fcntl");
+ close(rfbScreen->httpSock);
+ rfbScreen->httpSock=-1;
+ return;
+ }
+
+ /*AddEnabledDevice(httpSock);*/
+ }
+}
+
+
+static void
+httpCloseSock(rfbScreenInfoPtr rfbScreen)
+{
+ fclose(rfbScreen->httpFP);
+ rfbScreen->httpFP = NULL;
+ /*RemoveEnabledDevice(httpSock);*/
+ rfbScreen->httpSock = -1;
+}
+
+static rfbClientRec cl;
+
+/*
+ * httpProcessInput is called when input is received on the HTTP socket.
+ */
+
+static void
+httpProcessInput(rfbScreenInfoPtr rfbScreen)
+{
+ struct sockaddr_in addr;
+ size_t addrlen = sizeof(addr);
+ char fullFname[256];
+ char *fname;
+ unsigned int maxFnameLen;
+ FILE* fd;
+ Bool performSubstitutions = FALSE;
+ char str[256];
+#ifndef WIN32
+ struct passwd *user = getpwuid(getuid());
+#endif
+
+ cl.sock=rfbScreen->httpSock;
+
+ if (strlen(rfbScreen->httpDir) > 200) {
+ rfbLog("-httpd directory too long\n");
+ httpCloseSock(rfbScreen);
+ return;
+ }
+ strcpy(fullFname, rfbScreen->httpDir);
+ fname = &fullFname[strlen(fullFname)];
+ maxFnameLen = 255 - strlen(fullFname);
+
+ /* Read data from the HTTP client until we get a complete request. */
+ while (1) {
+ ssize_t got = read (rfbScreen->httpSock, buf + buf_filled,
+ sizeof (buf) - buf_filled - 1);
+
+ if (got <= 0) {
+ if (got == 0) {
+ rfbLog("httpd: premature connection close\n");
+ } else {
+ if (errno == EAGAIN) {
+ return;
+ }
+ rfbLogPerror("httpProcessInput: read");
+ }
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ buf_filled += got;
+ buf[buf_filled] = '\0';
+
+ /* Is it complete yet (is there a blank line)? */
+ if (strstr (buf, "\r\r") || strstr (buf, "\n\n") ||
+ strstr (buf, "\r\n\r\n") || strstr (buf, "\n\r\n\r"))
+ break;
+ }
+
+
+ /* Process the request. */
+ if (strncmp(buf, "GET ", 4)) {
+ rfbLog("no GET line\n");
+ httpCloseSock(rfbScreen);
+ return;
+ } else {
+ /* Only use the first line. */
+ buf[strcspn(buf, "\n\r")] = '\0';
+ }
+
+ if (strlen(buf) > maxFnameLen) {
+ rfbLog("GET line too long\n");
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ if (sscanf(buf, "GET %s HTTP/1.0", fname) != 1) {
+ rfbLog("couldn't parse GET line\n");
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ if (fname[0] != '/') {
+ rfbLog("filename didn't begin with '/'\n");
+ WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ if (strchr(fname+1, '/') != NULL) {
+ rfbLog("asking for file in other directory\n");
+ WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen);
+ rfbLog("httpd: get '%s' for %s\n", fname+1,
+ inet_ntoa(addr.sin_addr));
+
+ /* If we were asked for '/', actually read the file index.vnc */
+
+ if (strcmp(fname, "/") == 0) {
+ strcpy(fname, "/index.vnc");
+ rfbLog("httpd: defaulting to '%s'\n", fname+1);
+ }
+
+ /* Substitutions are performed on files ending .vnc */
+
+ if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) {
+ performSubstitutions = TRUE;
+ }
+
+ /* Open the file */
+
+ if ((fd = fopen(fullFname, "r")) <= 0) {
+ rfbLogPerror("httpProcessInput: open");
+ WriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ WriteExact(&cl, OK_STR, strlen(OK_STR));
+
+ while (1) {
+ int n = fread(buf, 1, BUF_SIZE-1, fd);
+ if (n < 0) {
+ rfbLogPerror("httpProcessInput: read");
+ fclose(fd);
+ httpCloseSock(rfbScreen);
+ return;
+ }
+
+ if (n == 0)
+ break;
+
+ if (performSubstitutions) {
+
+ /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values.
+ This won't quite work properly if the .vnc file is longer than
+ BUF_SIZE, but it's reasonable to assume that .vnc files will
+ always be short. */
+
+ char *ptr = buf;
+ char *dollar;
+ buf[n] = 0; /* make sure it's null-terminated */
+
+ while ((dollar = strchr(ptr, '$'))!=NULL) {
+ WriteExact(&cl, ptr, (dollar - ptr));
+
+ ptr = dollar;
+
+ if (compareAndSkip(&ptr, "$WIDTH")) {
+
+ sprintf(str, "%d", rfbScreen->width);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$HEIGHT")) {
+
+ sprintf(str, "%d", rfbScreen->height);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) {
+
+ sprintf(str, "%d", rfbScreen->width);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) {
+
+ sprintf(str, "%d", rfbScreen->height + 32);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$PORT")) {
+
+ sprintf(str, "%d", rfbScreen->rfbPort);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$DESKTOP")) {
+
+ WriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName));
+
+ } else if (compareAndSkip(&ptr, "$DISPLAY")) {
+
+ sprintf(str, "%s:%d", rfbScreen->rfbThisHost, rfbScreen->rfbPort-5900);
+ WriteExact(&cl, str, strlen(str));
+
+ } else if (compareAndSkip(&ptr, "$USER")) {
+#ifndef WIN32
+ if (user) {
+ WriteExact(&cl, user->pw_name,
+ strlen(user->pw_name));
+ } else
+#endif
+ WriteExact(&cl, "?", 1);
+ } else {
+ if (!compareAndSkip(&ptr, "$$"))
+ ptr++;
+
+ if (WriteExact(&cl, "$", 1) < 0) {
+ fclose(fd);
+ httpCloseSock(rfbScreen);
+ return;
+ }
+ }
+ }
+ if (WriteExact(&cl, ptr, (&buf[n] - ptr)) < 0)
+ break;
+
+ } else {
+
+ /* For files not ending .vnc, just write out the buffer */
+
+ if (WriteExact(&cl, buf, n) < 0)
+ break;
+ }
+ }
+
+ fclose(fd);
+ httpCloseSock(rfbScreen);
+}
+
+
+static Bool
+compareAndSkip(char **ptr, const char *str)
+{
+ if (strncmp(*ptr, str, strlen(str)) == 0) {
+ *ptr += strlen(str);
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/krfb/libvncserver/keysym.h b/krfb/libvncserver/keysym.h
new file mode 100644
index 00000000..dc165b3b
--- /dev/null
+++ b/krfb/libvncserver/keysym.h
@@ -0,0 +1,1639 @@
+#ifndef KEYSYM_H
+#define KEYSYM_H
+
+/* $XConsortium: keysym.h,v 1.15 94/04/17 20:10:55 rws Exp $ */
+
+/***********************************************************
+
+Copyright (c) 1987 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/* default keysyms */
+#define XK_MISCELLANY
+#define XK_XKB_KEYS
+#define XK_LATIN1
+#define XK_LATIN2
+#define XK_LATIN3
+#define XK_LATIN4
+#define XK_GREEK
+
+/* $TOG: keysymdef.h /main/25 1997/06/21 10:54:51 kaleb $ */
+
+/***********************************************************
+Copyright (c) 1987, 1994 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the X Consortium.
+
+
+Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+#define XK_VoidSymbol 0xFFFFFF /* void symbol */
+
+#ifdef XK_MISCELLANY
+/*
+ * TTY Functions, cleverly chosen to map to ascii, for convenience of
+ * programming, but could have been arbitrary (at the cost of lookup
+ * tables in client code.
+ */
+
+#define XK_BackSpace 0xFF08 /* back space, back char */
+#define XK_Tab 0xFF09
+#define XK_Linefeed 0xFF0A /* Linefeed, LF */
+#define XK_Clear 0xFF0B
+#define XK_Return 0xFF0D /* Return, enter */
+#define XK_Pause 0xFF13 /* Pause, hold */
+#define XK_Scroll_Lock 0xFF14
+#define XK_Sys_Req 0xFF15
+#define XK_Escape 0xFF1B
+#define XK_Delete 0xFFFF /* Delete, rubout */
+
+
+
+/* International & multi-key character composition */
+
+#define XK_Multi_key 0xFF20 /* Multi-key character compose */
+#define XK_SingleCandidate 0xFF3C
+#define XK_MultipleCandidate 0xFF3D
+#define XK_PreviousCandidate 0xFF3E
+
+/* Japanese keyboard support */
+
+#define XK_Kanji 0xFF21 /* Kanji, Kanji convert */
+#define XK_Muhenkan 0xFF22 /* Cancel Conversion */
+#define XK_Henkan_Mode 0xFF23 /* Start/Stop Conversion */
+#define XK_Henkan 0xFF23 /* Alias for Henkan_Mode */
+#define XK_Romaji 0xFF24 /* to Romaji */
+#define XK_Hiragana 0xFF25 /* to Hiragana */
+#define XK_Katakana 0xFF26 /* to Katakana */
+#define XK_Hiragana_Katakana 0xFF27 /* Hiragana/Katakana toggle */
+#define XK_Zenkaku 0xFF28 /* to Zenkaku */
+#define XK_Hankaku 0xFF29 /* to Hankaku */
+#define XK_Zenkaku_Hankaku 0xFF2A /* Zenkaku/Hankaku toggle */
+#define XK_Touroku 0xFF2B /* Add to Dictionary */
+#define XK_Massyo 0xFF2C /* Delete from Dictionary */
+#define XK_Kana_Lock 0xFF2D /* Kana Lock */
+#define XK_Kana_Shift 0xFF2E /* Kana Shift */
+#define XK_Eisu_Shift 0xFF2F /* Alphanumeric Shift */
+#define XK_Eisu_toggle 0xFF30 /* Alphanumeric toggle */
+#define XK_Zen_Koho 0xFF3D /* Multiple/All Candidate(s) */
+#define XK_Mae_Koho 0xFF3E /* Previous Candidate */
+
+/* 0xFF31 thru 0xFF3F are under XK_KOREAN */
+
+/* Cursor control & motion */
+
+#define XK_Home 0xFF50
+#define XK_Left 0xFF51 /* Move left, left arrow */
+#define XK_Up 0xFF52 /* Move up, up arrow */
+#define XK_Right 0xFF53 /* Move right, right arrow */
+#define XK_Down 0xFF54 /* Move down, down arrow */
+#define XK_Prior 0xFF55 /* Prior, previous */
+#define XK_Page_Up 0xFF55
+#define XK_Next 0xFF56 /* Next */
+#define XK_Page_Down 0xFF56
+#define XK_End 0xFF57 /* EOL */
+#define XK_Begin 0xFF58 /* BOL */
+
+
+/* Misc Functions */
+
+#define XK_Select 0xFF60 /* Select, mark */
+#define XK_Print 0xFF61
+#define XK_Execute 0xFF62 /* Execute, run, do */
+#define XK_Insert 0xFF63 /* Insert, insert here */
+#define XK_Undo 0xFF65 /* Undo, oops */
+#define XK_Redo 0xFF66 /* redo, again */
+#define XK_Menu 0xFF67
+#define XK_Find 0xFF68 /* Find, search */
+#define XK_Cancel 0xFF69 /* Cancel, stop, abort, exit */
+#define XK_Help 0xFF6A /* Help */
+#define XK_Break 0xFF6B
+#define XK_Mode_switch 0xFF7E /* Character set switch */
+#define XK_script_switch 0xFF7E /* Alias for mode_switch */
+#define XK_Num_Lock 0xFF7F
+
+/* Keypad Functions, keypad numbers cleverly chosen to map to ascii */
+
+#define XK_KP_Space 0xFF80 /* space */
+#define XK_KP_Tab 0xFF89
+#define XK_KP_Enter 0xFF8D /* enter */
+#define XK_KP_F1 0xFF91 /* PF1, KP_A, ... */
+#define XK_KP_F2 0xFF92
+#define XK_KP_F3 0xFF93
+#define XK_KP_F4 0xFF94
+#define XK_KP_Home 0xFF95
+#define XK_KP_Left 0xFF96
+#define XK_KP_Up 0xFF97
+#define XK_KP_Right 0xFF98
+#define XK_KP_Down 0xFF99
+#define XK_KP_Prior 0xFF9A
+#define XK_KP_Page_Up 0xFF9A
+#define XK_KP_Next 0xFF9B
+#define XK_KP_Page_Down 0xFF9B
+#define XK_KP_End 0xFF9C
+#define XK_KP_Begin 0xFF9D
+#define XK_KP_Insert 0xFF9E
+#define XK_KP_Delete 0xFF9F
+#define XK_KP_Equal 0xFFBD /* equals */
+#define XK_KP_Multiply 0xFFAA
+#define XK_KP_Add 0xFFAB
+#define XK_KP_Separator 0xFFAC /* separator, often comma */
+#define XK_KP_Subtract 0xFFAD
+#define XK_KP_Decimal 0xFFAE
+#define XK_KP_Divide 0xFFAF
+
+#define XK_KP_0 0xFFB0
+#define XK_KP_1 0xFFB1
+#define XK_KP_2 0xFFB2
+#define XK_KP_3 0xFFB3
+#define XK_KP_4 0xFFB4
+#define XK_KP_5 0xFFB5
+#define XK_KP_6 0xFFB6
+#define XK_KP_7 0xFFB7
+#define XK_KP_8 0xFFB8
+#define XK_KP_9 0xFFB9
+
+
+
+/*
+ * Auxilliary Functions; note the duplicate definitions for left and right
+ * function keys; Sun keyboards and a few other manufactures have such
+ * function key groups on the left and/or right sides of the keyboard.
+ * We've not found a keyboard with more than 35 function keys total.
+ */
+
+#define XK_F1 0xFFBE
+#define XK_F2 0xFFBF
+#define XK_F3 0xFFC0
+#define XK_F4 0xFFC1
+#define XK_F5 0xFFC2
+#define XK_F6 0xFFC3
+#define XK_F7 0xFFC4
+#define XK_F8 0xFFC5
+#define XK_F9 0xFFC6
+#define XK_F10 0xFFC7
+#define XK_F11 0xFFC8
+#define XK_L1 0xFFC8
+#define XK_F12 0xFFC9
+#define XK_L2 0xFFC9
+#define XK_F13 0xFFCA
+#define XK_L3 0xFFCA
+#define XK_F14 0xFFCB
+#define XK_L4 0xFFCB
+#define XK_F15 0xFFCC
+#define XK_L5 0xFFCC
+#define XK_F16 0xFFCD
+#define XK_L6 0xFFCD
+#define XK_F17 0xFFCE
+#define XK_L7 0xFFCE
+#define XK_F18 0xFFCF
+#define XK_L8 0xFFCF
+#define XK_F19 0xFFD0
+#define XK_L9 0xFFD0
+#define XK_F20 0xFFD1
+#define XK_L10 0xFFD1
+#define XK_F21 0xFFD2
+#define XK_R1 0xFFD2
+#define XK_F22 0xFFD3
+#define XK_R2 0xFFD3
+#define XK_F23 0xFFD4
+#define XK_R3 0xFFD4
+#define XK_F24 0xFFD5
+#define XK_R4 0xFFD5
+#define XK_F25 0xFFD6
+#define XK_R5 0xFFD6
+#define XK_F26 0xFFD7
+#define XK_R6 0xFFD7
+#define XK_F27 0xFFD8
+#define XK_R7 0xFFD8
+#define XK_F28 0xFFD9
+#define XK_R8 0xFFD9
+#define XK_F29 0xFFDA
+#define XK_R9 0xFFDA
+#define XK_F30 0xFFDB
+#define XK_R10 0xFFDB
+#define XK_F31 0xFFDC
+#define XK_R11 0xFFDC
+#define XK_F32 0xFFDD
+#define XK_R12 0xFFDD
+#define XK_F33 0xFFDE
+#define XK_R13 0xFFDE
+#define XK_F34 0xFFDF
+#define XK_R14 0xFFDF
+#define XK_F35 0xFFE0
+#define XK_R15 0xFFE0
+
+/* Modifiers */
+
+#define XK_Shift_L 0xFFE1 /* Left shift */
+#define XK_Shift_R 0xFFE2 /* Right shift */
+#define XK_Control_L 0xFFE3 /* Left control */
+#define XK_Control_R 0xFFE4 /* Right control */
+#define XK_Caps_Lock 0xFFE5 /* Caps lock */
+#define XK_Shift_Lock 0xFFE6 /* Shift lock */
+
+#define XK_Meta_L 0xFFE7 /* Left meta */
+#define XK_Meta_R 0xFFE8 /* Right meta */
+#define XK_Alt_L 0xFFE9 /* Left alt */
+#define XK_Alt_R 0xFFEA /* Right alt */
+#define XK_Super_L 0xFFEB /* Left super */
+#define XK_Super_R 0xFFEC /* Right super */
+#define XK_Hyper_L 0xFFED /* Left hyper */
+#define XK_Hyper_R 0xFFEE /* Right hyper */
+#endif /* XK_MISCELLANY */
+
+/*
+ * ISO 9995 Function and Modifier Keys
+ * Byte 3 = 0xFE
+ */
+
+#ifdef XK_XKB_KEYS
+#define XK_ISO_Lock 0xFE01
+#define XK_ISO_Level2_Latch 0xFE02
+#define XK_ISO_Level3_Shift 0xFE03
+#define XK_ISO_Level3_Latch 0xFE04
+#define XK_ISO_Level3_Lock 0xFE05
+#define XK_ISO_Group_Shift 0xFF7E /* Alias for mode_switch */
+#define XK_ISO_Group_Latch 0xFE06
+#define XK_ISO_Group_Lock 0xFE07
+#define XK_ISO_Next_Group 0xFE08
+#define XK_ISO_Next_Group_Lock 0xFE09
+#define XK_ISO_Prev_Group 0xFE0A
+#define XK_ISO_Prev_Group_Lock 0xFE0B
+#define XK_ISO_First_Group 0xFE0C
+#define XK_ISO_First_Group_Lock 0xFE0D
+#define XK_ISO_Last_Group 0xFE0E
+#define XK_ISO_Last_Group_Lock 0xFE0F
+
+#define XK_ISO_Left_Tab 0xFE20
+#define XK_ISO_Move_Line_Up 0xFE21
+#define XK_ISO_Move_Line_Down 0xFE22
+#define XK_ISO_Partial_Line_Up 0xFE23
+#define XK_ISO_Partial_Line_Down 0xFE24
+#define XK_ISO_Partial_Space_Left 0xFE25
+#define XK_ISO_Partial_Space_Right 0xFE26
+#define XK_ISO_Set_Margin_Left 0xFE27
+#define XK_ISO_Set_Margin_Right 0xFE28
+#define XK_ISO_Release_Margin_Left 0xFE29
+#define XK_ISO_Release_Margin_Right 0xFE2A
+#define XK_ISO_Release_Both_Margins 0xFE2B
+#define XK_ISO_Fast_Cursor_Left 0xFE2C
+#define XK_ISO_Fast_Cursor_Right 0xFE2D
+#define XK_ISO_Fast_Cursor_Up 0xFE2E
+#define XK_ISO_Fast_Cursor_Down 0xFE2F
+#define XK_ISO_Continuous_Underline 0xFE30
+#define XK_ISO_Discontinuous_Underline 0xFE31
+#define XK_ISO_Emphasize 0xFE32
+#define XK_ISO_Center_Object 0xFE33
+#define XK_ISO_Enter 0xFE34
+
+#define XK_dead_grave 0xFE50
+#define XK_dead_acute 0xFE51
+#define XK_dead_circumflex 0xFE52
+#define XK_dead_tilde 0xFE53
+#define XK_dead_macron 0xFE54
+#define XK_dead_breve 0xFE55
+#define XK_dead_abovedot 0xFE56
+#define XK_dead_diaeresis 0xFE57
+#define XK_dead_abovering 0xFE58
+#define XK_dead_doubleacute 0xFE59
+#define XK_dead_caron 0xFE5A
+#define XK_dead_cedilla 0xFE5B
+#define XK_dead_ogonek 0xFE5C
+#define XK_dead_iota 0xFE5D
+#define XK_dead_voiced_sound 0xFE5E
+#define XK_dead_semivoiced_sound 0xFE5F
+#define XK_dead_belowdot 0xFE60
+
+#define XK_First_Virtual_Screen 0xFED0
+#define XK_Prev_Virtual_Screen 0xFED1
+#define XK_Next_Virtual_Screen 0xFED2
+#define XK_Last_Virtual_Screen 0xFED4
+#define XK_Terminate_Server 0xFED5
+
+#define XK_AccessX_Enable 0xFE70
+#define XK_AccessX_Feedback_Enable 0xFE71
+#define XK_RepeatKeys_Enable 0xFE72
+#define XK_SlowKeys_Enable 0xFE73
+#define XK_BounceKeys_Enable 0xFE74
+#define XK_StickyKeys_Enable 0xFE75
+#define XK_MouseKeys_Enable 0xFE76
+#define XK_MouseKeys_Accel_Enable 0xFE77
+#define XK_Overlay1_Enable 0xFE78
+#define XK_Overlay2_Enable 0xFE79
+#define XK_AudibleBell_Enable 0xFE7A
+
+#define XK_Pointer_Left 0xFEE0
+#define XK_Pointer_Right 0xFEE1
+#define XK_Pointer_Up 0xFEE2
+#define XK_Pointer_Down 0xFEE3
+#define XK_Pointer_UpLeft 0xFEE4
+#define XK_Pointer_UpRight 0xFEE5
+#define XK_Pointer_DownLeft 0xFEE6
+#define XK_Pointer_DownRight 0xFEE7
+#define XK_Pointer_Button_Dflt 0xFEE8
+#define XK_Pointer_Button1 0xFEE9
+#define XK_Pointer_Button2 0xFEEA
+#define XK_Pointer_Button3 0xFEEB
+#define XK_Pointer_Button4 0xFEEC
+#define XK_Pointer_Button5 0xFEED
+#define XK_Pointer_DblClick_Dflt 0xFEEE
+#define XK_Pointer_DblClick1 0xFEEF
+#define XK_Pointer_DblClick2 0xFEF0
+#define XK_Pointer_DblClick3 0xFEF1
+#define XK_Pointer_DblClick4 0xFEF2
+#define XK_Pointer_DblClick5 0xFEF3
+#define XK_Pointer_Drag_Dflt 0xFEF4
+#define XK_Pointer_Drag1 0xFEF5
+#define XK_Pointer_Drag2 0xFEF6
+#define XK_Pointer_Drag3 0xFEF7
+#define XK_Pointer_Drag4 0xFEF8
+#define XK_Pointer_Drag5 0xFEFD
+
+#define XK_Pointer_EnableKeys 0xFEF9
+#define XK_Pointer_Accelerate 0xFEFA
+#define XK_Pointer_DfltBtnNext 0xFEFB
+#define XK_Pointer_DfltBtnPrev 0xFEFC
+
+#endif
+
+/*
+ * 3270 Terminal Keys
+ * Byte 3 = 0xFD
+ */
+
+#ifdef XK_3270
+#define XK_3270_Duplicate 0xFD01
+#define XK_3270_FieldMark 0xFD02
+#define XK_3270_Right2 0xFD03
+#define XK_3270_Left2 0xFD04
+#define XK_3270_BackTab 0xFD05
+#define XK_3270_EraseEOF 0xFD06
+#define XK_3270_EraseInput 0xFD07
+#define XK_3270_Reset 0xFD08
+#define XK_3270_Quit 0xFD09
+#define XK_3270_PA1 0xFD0A
+#define XK_3270_PA2 0xFD0B
+#define XK_3270_PA3 0xFD0C
+#define XK_3270_Test 0xFD0D
+#define XK_3270_Attn 0xFD0E
+#define XK_3270_CursorBlink 0xFD0F
+#define XK_3270_AltCursor 0xFD10
+#define XK_3270_KeyClick 0xFD11
+#define XK_3270_Jump 0xFD12
+#define XK_3270_Ident 0xFD13
+#define XK_3270_Rule 0xFD14
+#define XK_3270_Copy 0xFD15
+#define XK_3270_Play 0xFD16
+#define XK_3270_Setup 0xFD17
+#define XK_3270_Record 0xFD18
+#define XK_3270_ChangeScreen 0xFD19
+#define XK_3270_DeleteWord 0xFD1A
+#define XK_3270_ExSelect 0xFD1B
+#define XK_3270_CursorSelect 0xFD1C
+#define XK_3270_PrintScreen 0xFD1D
+#define XK_3270_Enter 0xFD1E
+#endif
+
+/*
+ * Latin 1
+ * Byte 3 = 0
+ */
+#ifdef XK_LATIN1
+#define XK_space 0x020
+#define XK_exclam 0x021
+#define XK_quotedbl 0x022
+#define XK_numbersign 0x023
+#define XK_dollar 0x024
+#define XK_percent 0x025
+#define XK_ampersand 0x026
+#define XK_apostrophe 0x027
+#define XK_quoteright 0x027 /* deprecated */
+#define XK_parenleft 0x028
+#define XK_parenright 0x029
+#define XK_asterisk 0x02a
+#define XK_plus 0x02b
+#define XK_comma 0x02c
+#define XK_minus 0x02d
+#define XK_period 0x02e
+#define XK_slash 0x02f
+#define XK_0 0x030
+#define XK_1 0x031
+#define XK_2 0x032
+#define XK_3 0x033
+#define XK_4 0x034
+#define XK_5 0x035
+#define XK_6 0x036
+#define XK_7 0x037
+#define XK_8 0x038
+#define XK_9 0x039
+#define XK_colon 0x03a
+#define XK_semicolon 0x03b
+#define XK_less 0x03c
+#define XK_equal 0x03d
+#define XK_greater 0x03e
+#define XK_question 0x03f
+#define XK_at 0x040
+#define XK_A 0x041
+#define XK_B 0x042
+#define XK_C 0x043
+#define XK_D 0x044
+#define XK_E 0x045
+#define XK_F 0x046
+#define XK_G 0x047
+#define XK_H 0x048
+#define XK_I 0x049
+#define XK_J 0x04a
+#define XK_K 0x04b
+#define XK_L 0x04c
+#define XK_M 0x04d
+#define XK_N 0x04e
+#define XK_O 0x04f
+#define XK_P 0x050
+#define XK_Q 0x051
+#define XK_R 0x052
+#define XK_S 0x053
+#define XK_T 0x054
+#define XK_U 0x055
+#define XK_V 0x056
+#define XK_W 0x057
+#define XK_X 0x058
+#define XK_Y 0x059
+#define XK_Z 0x05a
+#define XK_bracketleft 0x05b
+#define XK_backslash 0x05c
+#define XK_bracketright 0x05d
+#define XK_asciicircum 0x05e
+#define XK_underscore 0x05f
+#define XK_grave 0x060
+#define XK_quoteleft 0x060 /* deprecated */
+#define XK_a 0x061
+#define XK_b 0x062
+#define XK_c 0x063
+#define XK_d 0x064
+#define XK_e 0x065
+#define XK_f 0x066
+#define XK_g 0x067
+#define XK_h 0x068
+#define XK_i 0x069
+#define XK_j 0x06a
+#define XK_k 0x06b
+#define XK_l 0x06c
+#define XK_m 0x06d
+#define XK_n 0x06e
+#define XK_o 0x06f
+#define XK_p 0x070
+#define XK_q 0x071
+#define XK_r 0x072
+#define XK_s 0x073
+#define XK_t 0x074
+#define XK_u 0x075
+#define XK_v 0x076
+#define XK_w 0x077
+#define XK_x 0x078
+#define XK_y 0x079
+#define XK_z 0x07a
+#define XK_braceleft 0x07b
+#define XK_bar 0x07c
+#define XK_braceright 0x07d
+#define XK_asciitilde 0x07e
+
+#define XK_nobreakspace 0x0a0
+#define XK_exclamdown 0x0a1
+#define XK_cent 0x0a2
+#define XK_sterling 0x0a3
+#define XK_currency 0x0a4
+#define XK_yen 0x0a5
+#define XK_brokenbar 0x0a6
+#define XK_section 0x0a7
+#define XK_diaeresis 0x0a8
+#define XK_copyright 0x0a9
+#define XK_ordfeminine 0x0aa
+#define XK_guillemotleft 0x0ab /* left angle quotation mark */
+#define XK_notsign 0x0ac
+#define XK_hyphen 0x0ad
+#define XK_registered 0x0ae
+#define XK_macron 0x0af
+#define XK_degree 0x0b0
+#define XK_plusminus 0x0b1
+#define XK_twosuperior 0x0b2
+#define XK_threesuperior 0x0b3
+#define XK_acute 0x0b4
+#define XK_mu 0x0b5
+#define XK_paragraph 0x0b6
+#define XK_periodcentered 0x0b7
+#define XK_cedilla 0x0b8
+#define XK_onesuperior 0x0b9
+#define XK_masculine 0x0ba
+#define XK_guillemotright 0x0bb /* right angle quotation mark */
+#define XK_onequarter 0x0bc
+#define XK_onehalf 0x0bd
+#define XK_threequarters 0x0be
+#define XK_questiondown 0x0bf
+#define XK_Agrave 0x0c0
+#define XK_Aacute 0x0c1
+#define XK_Acircumflex 0x0c2
+#define XK_Atilde 0x0c3
+#define XK_Adiaeresis 0x0c4
+#define XK_Aring 0x0c5
+#define XK_AE 0x0c6
+#define XK_Ccedilla 0x0c7
+#define XK_Egrave 0x0c8
+#define XK_Eacute 0x0c9
+#define XK_Ecircumflex 0x0ca
+#define XK_Ediaeresis 0x0cb
+#define XK_Igrave 0x0cc
+#define XK_Iacute 0x0cd
+#define XK_Icircumflex 0x0ce
+#define XK_Idiaeresis 0x0cf
+#define XK_ETH 0x0d0
+#define XK_Eth 0x0d0 /* deprecated */
+#define XK_Ntilde 0x0d1
+#define XK_Ograve 0x0d2
+#define XK_Oacute 0x0d3
+#define XK_Ocircumflex 0x0d4
+#define XK_Otilde 0x0d5
+#define XK_Odiaeresis 0x0d6
+#define XK_multiply 0x0d7
+#define XK_Ooblique 0x0d8
+#define XK_Ugrave 0x0d9
+#define XK_Uacute 0x0da
+#define XK_Ucircumflex 0x0db
+#define XK_Udiaeresis 0x0dc
+#define XK_Yacute 0x0dd
+#define XK_THORN 0x0de
+#define XK_Thorn 0x0de /* deprecated */
+#define XK_ssharp 0x0df
+#define XK_agrave 0x0e0
+#define XK_aacute 0x0e1
+#define XK_acircumflex 0x0e2
+#define XK_atilde 0x0e3
+#define XK_adiaeresis 0x0e4
+#define XK_aring 0x0e5
+#define XK_ae 0x0e6
+#define XK_ccedilla 0x0e7
+#define XK_egrave 0x0e8
+#define XK_eacute 0x0e9
+#define XK_ecircumflex 0x0ea
+#define XK_ediaeresis 0x0eb
+#define XK_igrave 0x0ec
+#define XK_iacute 0x0ed
+#define XK_icircumflex 0x0ee
+#define XK_idiaeresis 0x0ef
+#define XK_eth 0x0f0
+#define XK_ntilde 0x0f1
+#define XK_ograve 0x0f2
+#define XK_oacute 0x0f3
+#define XK_ocircumflex 0x0f4
+#define XK_otilde 0x0f5
+#define XK_odiaeresis 0x0f6
+#define XK_division 0x0f7
+#define XK_oslash 0x0f8
+#define XK_ugrave 0x0f9
+#define XK_uacute 0x0fa
+#define XK_ucircumflex 0x0fb
+#define XK_udiaeresis 0x0fc
+#define XK_yacute 0x0fd
+#define XK_thorn 0x0fe
+#define XK_ydiaeresis 0x0ff
+#endif /* XK_LATIN1 */
+
+/*
+ * Latin 2
+ * Byte 3 = 1
+ */
+
+#ifdef XK_LATIN2
+#define XK_Aogonek 0x1a1
+#define XK_breve 0x1a2
+#define XK_Lstroke 0x1a3
+#define XK_Lcaron 0x1a5
+#define XK_Sacute 0x1a6
+#define XK_Scaron 0x1a9
+#define XK_Scedilla 0x1aa
+#define XK_Tcaron 0x1ab
+#define XK_Zacute 0x1ac
+#define XK_Zcaron 0x1ae
+#define XK_Zabovedot 0x1af
+#define XK_aogonek 0x1b1
+#define XK_ogonek 0x1b2
+#define XK_lstroke 0x1b3
+#define XK_lcaron 0x1b5
+#define XK_sacute 0x1b6
+#define XK_caron 0x1b7
+#define XK_scaron 0x1b9
+#define XK_scedilla 0x1ba
+#define XK_tcaron 0x1bb
+#define XK_zacute 0x1bc
+#define XK_doubleacute 0x1bd
+#define XK_zcaron 0x1be
+#define XK_zabovedot 0x1bf
+#define XK_Racute 0x1c0
+#define XK_Abreve 0x1c3
+#define XK_Lacute 0x1c5
+#define XK_Cacute 0x1c6
+#define XK_Ccaron 0x1c8
+#define XK_Eogonek 0x1ca
+#define XK_Ecaron 0x1cc
+#define XK_Dcaron 0x1cf
+#define XK_Dstroke 0x1d0
+#define XK_Nacute 0x1d1
+#define XK_Ncaron 0x1d2
+#define XK_Odoubleacute 0x1d5
+#define XK_Rcaron 0x1d8
+#define XK_Uring 0x1d9
+#define XK_Udoubleacute 0x1db
+#define XK_Tcedilla 0x1de
+#define XK_racute 0x1e0
+#define XK_abreve 0x1e3
+#define XK_lacute 0x1e5
+#define XK_cacute 0x1e6
+#define XK_ccaron 0x1e8
+#define XK_eogonek 0x1ea
+#define XK_ecaron 0x1ec
+#define XK_dcaron 0x1ef
+#define XK_dstroke 0x1f0
+#define XK_nacute 0x1f1
+#define XK_ncaron 0x1f2
+#define XK_odoubleacute 0x1f5
+#define XK_udoubleacute 0x1fb
+#define XK_rcaron 0x1f8
+#define XK_uring 0x1f9
+#define XK_tcedilla 0x1fe
+#define XK_abovedot 0x1ff
+#endif /* XK_LATIN2 */
+
+/*
+ * Latin 3
+ * Byte 3 = 2
+ */
+
+#ifdef XK_LATIN3
+#define XK_Hstroke 0x2a1
+#define XK_Hcircumflex 0x2a6
+#define XK_Iabovedot 0x2a9
+#define XK_Gbreve 0x2ab
+#define XK_Jcircumflex 0x2ac
+#define XK_hstroke 0x2b1
+#define XK_hcircumflex 0x2b6
+#define XK_idotless 0x2b9
+#define XK_gbreve 0x2bb
+#define XK_jcircumflex 0x2bc
+#define XK_Cabovedot 0x2c5
+#define XK_Ccircumflex 0x2c6
+#define XK_Gabovedot 0x2d5
+#define XK_Gcircumflex 0x2d8
+#define XK_Ubreve 0x2dd
+#define XK_Scircumflex 0x2de
+#define XK_cabovedot 0x2e5
+#define XK_ccircumflex 0x2e6
+#define XK_gabovedot 0x2f5
+#define XK_gcircumflex 0x2f8
+#define XK_ubreve 0x2fd
+#define XK_scircumflex 0x2fe
+#endif /* XK_LATIN3 */
+
+
+/*
+ * Latin 4
+ * Byte 3 = 3
+ */
+
+#ifdef XK_LATIN4
+#define XK_kra 0x3a2
+#define XK_kappa 0x3a2 /* deprecated */
+#define XK_Rcedilla 0x3a3
+#define XK_Itilde 0x3a5
+#define XK_Lcedilla 0x3a6
+#define XK_Emacron 0x3aa
+#define XK_Gcedilla 0x3ab
+#define XK_Tslash 0x3ac
+#define XK_rcedilla 0x3b3
+#define XK_itilde 0x3b5
+#define XK_lcedilla 0x3b6
+#define XK_emacron 0x3ba
+#define XK_gcedilla 0x3bb
+#define XK_tslash 0x3bc
+#define XK_ENG 0x3bd
+#define XK_eng 0x3bf
+#define XK_Amacron 0x3c0
+#define XK_Iogonek 0x3c7
+#define XK_Eabovedot 0x3cc
+#define XK_Imacron 0x3cf
+#define XK_Ncedilla 0x3d1
+#define XK_Omacron 0x3d2
+#define XK_Kcedilla 0x3d3
+#define XK_Uogonek 0x3d9
+#define XK_Utilde 0x3dd
+#define XK_Umacron 0x3de
+#define XK_amacron 0x3e0
+#define XK_iogonek 0x3e7
+#define XK_eabovedot 0x3ec
+#define XK_imacron 0x3ef
+#define XK_ncedilla 0x3f1
+#define XK_omacron 0x3f2
+#define XK_kcedilla 0x3f3
+#define XK_uogonek 0x3f9
+#define XK_utilde 0x3fd
+#define XK_umacron 0x3fe
+#endif /* XK_LATIN4 */
+
+/*
+ * Katakana
+ * Byte 3 = 4
+ */
+
+#ifdef XK_KATAKANA
+#define XK_overline 0x47e
+#define XK_kana_fullstop 0x4a1
+#define XK_kana_openingbracket 0x4a2
+#define XK_kana_closingbracket 0x4a3
+#define XK_kana_comma 0x4a4
+#define XK_kana_conjunctive 0x4a5
+#define XK_kana_middledot 0x4a5 /* deprecated */
+#define XK_kana_WO 0x4a6
+#define XK_kana_a 0x4a7
+#define XK_kana_i 0x4a8
+#define XK_kana_u 0x4a9
+#define XK_kana_e 0x4aa
+#define XK_kana_o 0x4ab
+#define XK_kana_ya 0x4ac
+#define XK_kana_yu 0x4ad
+#define XK_kana_yo 0x4ae
+#define XK_kana_tsu 0x4af
+#define XK_kana_tu 0x4af /* deprecated */
+#define XK_prolongedsound 0x4b0
+#define XK_kana_A 0x4b1
+#define XK_kana_I 0x4b2
+#define XK_kana_U 0x4b3
+#define XK_kana_E 0x4b4
+#define XK_kana_O 0x4b5
+#define XK_kana_KA 0x4b6
+#define XK_kana_KI 0x4b7
+#define XK_kana_KU 0x4b8
+#define XK_kana_KE 0x4b9
+#define XK_kana_KO 0x4ba
+#define XK_kana_SA 0x4bb
+#define XK_kana_SHI 0x4bc
+#define XK_kana_SU 0x4bd
+#define XK_kana_SE 0x4be
+#define XK_kana_SO 0x4bf
+#define XK_kana_TA 0x4c0
+#define XK_kana_CHI 0x4c1
+#define XK_kana_TI 0x4c1 /* deprecated */
+#define XK_kana_TSU 0x4c2
+#define XK_kana_TU 0x4c2 /* deprecated */
+#define XK_kana_TE 0x4c3
+#define XK_kana_TO 0x4c4
+#define XK_kana_NA 0x4c5
+#define XK_kana_NI 0x4c6
+#define XK_kana_NU 0x4c7
+#define XK_kana_NE 0x4c8
+#define XK_kana_NO 0x4c9
+#define XK_kana_HA 0x4ca
+#define XK_kana_HI 0x4cb
+#define XK_kana_FU 0x4cc
+#define XK_kana_HU 0x4cc /* deprecated */
+#define XK_kana_HE 0x4cd
+#define XK_kana_HO 0x4ce
+#define XK_kana_MA 0x4cf
+#define XK_kana_MI 0x4d0
+#define XK_kana_MU 0x4d1
+#define XK_kana_ME 0x4d2
+#define XK_kana_MO 0x4d3
+#define XK_kana_YA 0x4d4
+#define XK_kana_YU 0x4d5
+#define XK_kana_YO 0x4d6
+#define XK_kana_RA 0x4d7
+#define XK_kana_RI 0x4d8
+#define XK_kana_RU 0x4d9
+#define XK_kana_RE 0x4da
+#define XK_kana_RO 0x4db
+#define XK_kana_WA 0x4dc
+#define XK_kana_N 0x4dd
+#define XK_voicedsound 0x4de
+#define XK_semivoicedsound 0x4df
+#define XK_kana_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_KATAKANA */
+
+/*
+ * Arabic
+ * Byte 3 = 5
+ */
+
+#ifdef XK_ARABIC
+#define XK_Arabic_comma 0x5ac
+#define XK_Arabic_semicolon 0x5bb
+#define XK_Arabic_question_mark 0x5bf
+#define XK_Arabic_hamza 0x5c1
+#define XK_Arabic_maddaonalef 0x5c2
+#define XK_Arabic_hamzaonalef 0x5c3
+#define XK_Arabic_hamzaonwaw 0x5c4
+#define XK_Arabic_hamzaunderalef 0x5c5
+#define XK_Arabic_hamzaonyeh 0x5c6
+#define XK_Arabic_alef 0x5c7
+#define XK_Arabic_beh 0x5c8
+#define XK_Arabic_tehmarbuta 0x5c9
+#define XK_Arabic_teh 0x5ca
+#define XK_Arabic_theh 0x5cb
+#define XK_Arabic_jeem 0x5cc
+#define XK_Arabic_hah 0x5cd
+#define XK_Arabic_khah 0x5ce
+#define XK_Arabic_dal 0x5cf
+#define XK_Arabic_thal 0x5d0
+#define XK_Arabic_ra 0x5d1
+#define XK_Arabic_zain 0x5d2
+#define XK_Arabic_seen 0x5d3
+#define XK_Arabic_sheen 0x5d4
+#define XK_Arabic_sad 0x5d5
+#define XK_Arabic_dad 0x5d6
+#define XK_Arabic_tah 0x5d7
+#define XK_Arabic_zah 0x5d8
+#define XK_Arabic_ain 0x5d9
+#define XK_Arabic_ghain 0x5da
+#define XK_Arabic_tatweel 0x5e0
+#define XK_Arabic_feh 0x5e1
+#define XK_Arabic_qaf 0x5e2
+#define XK_Arabic_kaf 0x5e3
+#define XK_Arabic_lam 0x5e4
+#define XK_Arabic_meem 0x5e5
+#define XK_Arabic_noon 0x5e6
+#define XK_Arabic_ha 0x5e7
+#define XK_Arabic_heh 0x5e7 /* deprecated */
+#define XK_Arabic_waw 0x5e8
+#define XK_Arabic_alefmaksura 0x5e9
+#define XK_Arabic_yeh 0x5ea
+#define XK_Arabic_fathatan 0x5eb
+#define XK_Arabic_dammatan 0x5ec
+#define XK_Arabic_kasratan 0x5ed
+#define XK_Arabic_fatha 0x5ee
+#define XK_Arabic_damma 0x5ef
+#define XK_Arabic_kasra 0x5f0
+#define XK_Arabic_shadda 0x5f1
+#define XK_Arabic_sukun 0x5f2
+#define XK_Arabic_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_ARABIC */
+
+/*
+ * Cyrillic
+ * Byte 3 = 6
+ */
+#ifdef XK_CYRILLIC
+#define XK_Serbian_dje 0x6a1
+#define XK_Macedonia_gje 0x6a2
+#define XK_Cyrillic_io 0x6a3
+#define XK_Ukrainian_ie 0x6a4
+#define XK_Ukranian_je 0x6a4 /* deprecated */
+#define XK_Macedonia_dse 0x6a5
+#define XK_Ukrainian_i 0x6a6
+#define XK_Ukranian_i 0x6a6 /* deprecated */
+#define XK_Ukrainian_yi 0x6a7
+#define XK_Ukranian_yi 0x6a7 /* deprecated */
+#define XK_Cyrillic_je 0x6a8
+#define XK_Serbian_je 0x6a8 /* deprecated */
+#define XK_Cyrillic_lje 0x6a9
+#define XK_Serbian_lje 0x6a9 /* deprecated */
+#define XK_Cyrillic_nje 0x6aa
+#define XK_Serbian_nje 0x6aa /* deprecated */
+#define XK_Serbian_tshe 0x6ab
+#define XK_Macedonia_kje 0x6ac
+#define XK_Byelorussian_shortu 0x6ae
+#define XK_Cyrillic_dzhe 0x6af
+#define XK_Serbian_dze 0x6af /* deprecated */
+#define XK_numerosign 0x6b0
+#define XK_Serbian_DJE 0x6b1
+#define XK_Macedonia_GJE 0x6b2
+#define XK_Cyrillic_IO 0x6b3
+#define XK_Ukrainian_IE 0x6b4
+#define XK_Ukranian_JE 0x6b4 /* deprecated */
+#define XK_Macedonia_DSE 0x6b5
+#define XK_Ukrainian_I 0x6b6
+#define XK_Ukranian_I 0x6b6 /* deprecated */
+#define XK_Ukrainian_YI 0x6b7
+#define XK_Ukranian_YI 0x6b7 /* deprecated */
+#define XK_Cyrillic_JE 0x6b8
+#define XK_Serbian_JE 0x6b8 /* deprecated */
+#define XK_Cyrillic_LJE 0x6b9
+#define XK_Serbian_LJE 0x6b9 /* deprecated */
+#define XK_Cyrillic_NJE 0x6ba
+#define XK_Serbian_NJE 0x6ba /* deprecated */
+#define XK_Serbian_TSHE 0x6bb
+#define XK_Macedonia_KJE 0x6bc
+#define XK_Byelorussian_SHORTU 0x6be
+#define XK_Cyrillic_DZHE 0x6bf
+#define XK_Serbian_DZE 0x6bf /* deprecated */
+#define XK_Cyrillic_yu 0x6c0
+#define XK_Cyrillic_a 0x6c1
+#define XK_Cyrillic_be 0x6c2
+#define XK_Cyrillic_tse 0x6c3
+#define XK_Cyrillic_de 0x6c4
+#define XK_Cyrillic_ie 0x6c5
+#define XK_Cyrillic_ef 0x6c6
+#define XK_Cyrillic_ghe 0x6c7
+#define XK_Cyrillic_ha 0x6c8
+#define XK_Cyrillic_i 0x6c9
+#define XK_Cyrillic_shorti 0x6ca
+#define XK_Cyrillic_ka 0x6cb
+#define XK_Cyrillic_el 0x6cc
+#define XK_Cyrillic_em 0x6cd
+#define XK_Cyrillic_en 0x6ce
+#define XK_Cyrillic_o 0x6cf
+#define XK_Cyrillic_pe 0x6d0
+#define XK_Cyrillic_ya 0x6d1
+#define XK_Cyrillic_er 0x6d2
+#define XK_Cyrillic_es 0x6d3
+#define XK_Cyrillic_te 0x6d4
+#define XK_Cyrillic_u 0x6d5
+#define XK_Cyrillic_zhe 0x6d6
+#define XK_Cyrillic_ve 0x6d7
+#define XK_Cyrillic_softsign 0x6d8
+#define XK_Cyrillic_yeru 0x6d9
+#define XK_Cyrillic_ze 0x6da
+#define XK_Cyrillic_sha 0x6db
+#define XK_Cyrillic_e 0x6dc
+#define XK_Cyrillic_shcha 0x6dd
+#define XK_Cyrillic_che 0x6de
+#define XK_Cyrillic_hardsign 0x6df
+#define XK_Cyrillic_YU 0x6e0
+#define XK_Cyrillic_A 0x6e1
+#define XK_Cyrillic_BE 0x6e2
+#define XK_Cyrillic_TSE 0x6e3
+#define XK_Cyrillic_DE 0x6e4
+#define XK_Cyrillic_IE 0x6e5
+#define XK_Cyrillic_EF 0x6e6
+#define XK_Cyrillic_GHE 0x6e7
+#define XK_Cyrillic_HA 0x6e8
+#define XK_Cyrillic_I 0x6e9
+#define XK_Cyrillic_SHORTI 0x6ea
+#define XK_Cyrillic_KA 0x6eb
+#define XK_Cyrillic_EL 0x6ec
+#define XK_Cyrillic_EM 0x6ed
+#define XK_Cyrillic_EN 0x6ee
+#define XK_Cyrillic_O 0x6ef
+#define XK_Cyrillic_PE 0x6f0
+#define XK_Cyrillic_YA 0x6f1
+#define XK_Cyrillic_ER 0x6f2
+#define XK_Cyrillic_ES 0x6f3
+#define XK_Cyrillic_TE 0x6f4
+#define XK_Cyrillic_U 0x6f5
+#define XK_Cyrillic_ZHE 0x6f6
+#define XK_Cyrillic_VE 0x6f7
+#define XK_Cyrillic_SOFTSIGN 0x6f8
+#define XK_Cyrillic_YERU 0x6f9
+#define XK_Cyrillic_ZE 0x6fa
+#define XK_Cyrillic_SHA 0x6fb
+#define XK_Cyrillic_E 0x6fc
+#define XK_Cyrillic_SHCHA 0x6fd
+#define XK_Cyrillic_CHE 0x6fe
+#define XK_Cyrillic_HARDSIGN 0x6ff
+#endif /* XK_CYRILLIC */
+
+/*
+ * Greek
+ * Byte 3 = 7
+ */
+
+#ifdef XK_GREEK
+#define XK_Greek_ALPHAaccent 0x7a1
+#define XK_Greek_EPSILONaccent 0x7a2
+#define XK_Greek_ETAaccent 0x7a3
+#define XK_Greek_IOTAaccent 0x7a4
+#define XK_Greek_IOTAdiaeresis 0x7a5
+#define XK_Greek_OMICRONaccent 0x7a7
+#define XK_Greek_UPSILONaccent 0x7a8
+#define XK_Greek_UPSILONdieresis 0x7a9
+#define XK_Greek_OMEGAaccent 0x7ab
+#define XK_Greek_accentdieresis 0x7ae
+#define XK_Greek_horizbar 0x7af
+#define XK_Greek_alphaaccent 0x7b1
+#define XK_Greek_epsilonaccent 0x7b2
+#define XK_Greek_etaaccent 0x7b3
+#define XK_Greek_iotaaccent 0x7b4
+#define XK_Greek_iotadieresis 0x7b5
+#define XK_Greek_iotaaccentdieresis 0x7b6
+#define XK_Greek_omicronaccent 0x7b7
+#define XK_Greek_upsilonaccent 0x7b8
+#define XK_Greek_upsilondieresis 0x7b9
+#define XK_Greek_upsilonaccentdieresis 0x7ba
+#define XK_Greek_omegaaccent 0x7bb
+#define XK_Greek_ALPHA 0x7c1
+#define XK_Greek_BETA 0x7c2
+#define XK_Greek_GAMMA 0x7c3
+#define XK_Greek_DELTA 0x7c4
+#define XK_Greek_EPSILON 0x7c5
+#define XK_Greek_ZETA 0x7c6
+#define XK_Greek_ETA 0x7c7
+#define XK_Greek_THETA 0x7c8
+#define XK_Greek_IOTA 0x7c9
+#define XK_Greek_KAPPA 0x7ca
+#define XK_Greek_LAMDA 0x7cb
+#define XK_Greek_LAMBDA 0x7cb
+#define XK_Greek_MU 0x7cc
+#define XK_Greek_NU 0x7cd
+#define XK_Greek_XI 0x7ce
+#define XK_Greek_OMICRON 0x7cf
+#define XK_Greek_PI 0x7d0
+#define XK_Greek_RHO 0x7d1
+#define XK_Greek_SIGMA 0x7d2
+#define XK_Greek_TAU 0x7d4
+#define XK_Greek_UPSILON 0x7d5
+#define XK_Greek_PHI 0x7d6
+#define XK_Greek_CHI 0x7d7
+#define XK_Greek_PSI 0x7d8
+#define XK_Greek_OMEGA 0x7d9
+#define XK_Greek_alpha 0x7e1
+#define XK_Greek_beta 0x7e2
+#define XK_Greek_gamma 0x7e3
+#define XK_Greek_delta 0x7e4
+#define XK_Greek_epsilon 0x7e5
+#define XK_Greek_zeta 0x7e6
+#define XK_Greek_eta 0x7e7
+#define XK_Greek_theta 0x7e8
+#define XK_Greek_iota 0x7e9
+#define XK_Greek_kappa 0x7ea
+#define XK_Greek_lamda 0x7eb
+#define XK_Greek_lambda 0x7eb
+#define XK_Greek_mu 0x7ec
+#define XK_Greek_nu 0x7ed
+#define XK_Greek_xi 0x7ee
+#define XK_Greek_omicron 0x7ef
+#define XK_Greek_pi 0x7f0
+#define XK_Greek_rho 0x7f1
+#define XK_Greek_sigma 0x7f2
+#define XK_Greek_finalsmallsigma 0x7f3
+#define XK_Greek_tau 0x7f4
+#define XK_Greek_upsilon 0x7f5
+#define XK_Greek_phi 0x7f6
+#define XK_Greek_chi 0x7f7
+#define XK_Greek_psi 0x7f8
+#define XK_Greek_omega 0x7f9
+#define XK_Greek_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_GREEK */
+
+/*
+ * Technical
+ * Byte 3 = 8
+ */
+
+#ifdef XK_TECHNICAL
+#define XK_leftradical 0x8a1
+#define XK_topleftradical 0x8a2
+#define XK_horizconnector 0x8a3
+#define XK_topintegral 0x8a4
+#define XK_botintegral 0x8a5
+#define XK_vertconnector 0x8a6
+#define XK_topleftsqbracket 0x8a7
+#define XK_botleftsqbracket 0x8a8
+#define XK_toprightsqbracket 0x8a9
+#define XK_botrightsqbracket 0x8aa
+#define XK_topleftparens 0x8ab
+#define XK_botleftparens 0x8ac
+#define XK_toprightparens 0x8ad
+#define XK_botrightparens 0x8ae
+#define XK_leftmiddlecurlybrace 0x8af
+#define XK_rightmiddlecurlybrace 0x8b0
+#define XK_topleftsummation 0x8b1
+#define XK_botleftsummation 0x8b2
+#define XK_topvertsummationconnector 0x8b3
+#define XK_botvertsummationconnector 0x8b4
+#define XK_toprightsummation 0x8b5
+#define XK_botrightsummation 0x8b6
+#define XK_rightmiddlesummation 0x8b7
+#define XK_lessthanequal 0x8bc
+#define XK_notequal 0x8bd
+#define XK_greaterthanequal 0x8be
+#define XK_integral 0x8bf
+#define XK_therefore 0x8c0
+#define XK_variation 0x8c1
+#define XK_infinity 0x8c2
+#define XK_nabla 0x8c5
+#define XK_approximate 0x8c8
+#define XK_similarequal 0x8c9
+#define XK_ifonlyif 0x8cd
+#define XK_implies 0x8ce
+#define XK_identical 0x8cf
+#define XK_radical 0x8d6
+#define XK_includedin 0x8da
+#define XK_includes 0x8db
+#define XK_intersection 0x8dc
+#define XK_union 0x8dd
+#define XK_logicaland 0x8de
+#define XK_logicalor 0x8df
+#define XK_partialderivative 0x8ef
+#define XK_function 0x8f6
+#define XK_leftarrow 0x8fb
+#define XK_uparrow 0x8fc
+#define XK_rightarrow 0x8fd
+#define XK_downarrow 0x8fe
+#endif /* XK_TECHNICAL */
+
+/*
+ * Special
+ * Byte 3 = 9
+ */
+
+#ifdef XK_SPECIAL
+#define XK_blank 0x9df
+#define XK_soliddiamond 0x9e0
+#define XK_checkerboard 0x9e1
+#define XK_ht 0x9e2
+#define XK_ff 0x9e3
+#define XK_cr 0x9e4
+#define XK_lf 0x9e5
+#define XK_nl 0x9e8
+#define XK_vt 0x9e9
+#define XK_lowrightcorner 0x9ea
+#define XK_uprightcorner 0x9eb
+#define XK_upleftcorner 0x9ec
+#define XK_lowleftcorner 0x9ed
+#define XK_crossinglines 0x9ee
+#define XK_horizlinescan1 0x9ef
+#define XK_horizlinescan3 0x9f0
+#define XK_horizlinescan5 0x9f1
+#define XK_horizlinescan7 0x9f2
+#define XK_horizlinescan9 0x9f3
+#define XK_leftt 0x9f4
+#define XK_rightt 0x9f5
+#define XK_bott 0x9f6
+#define XK_topt 0x9f7
+#define XK_vertbar 0x9f8
+#endif /* XK_SPECIAL */
+
+/*
+ * Publishing
+ * Byte 3 = a
+ */
+
+#ifdef XK_PUBLISHING
+#define XK_emspace 0xaa1
+#define XK_enspace 0xaa2
+#define XK_em3space 0xaa3
+#define XK_em4space 0xaa4
+#define XK_digitspace 0xaa5
+#define XK_punctspace 0xaa6
+#define XK_thinspace 0xaa7
+#define XK_hairspace 0xaa8
+#define XK_emdash 0xaa9
+#define XK_endash 0xaaa
+#define XK_signifblank 0xaac
+#define XK_ellipsis 0xaae
+#define XK_doubbaselinedot 0xaaf
+#define XK_onethird 0xab0
+#define XK_twothirds 0xab1
+#define XK_onefifth 0xab2
+#define XK_twofifths 0xab3
+#define XK_threefifths 0xab4
+#define XK_fourfifths 0xab5
+#define XK_onesixth 0xab6
+#define XK_fivesixths 0xab7
+#define XK_careof 0xab8
+#define XK_figdash 0xabb
+#define XK_leftanglebracket 0xabc
+#define XK_decimalpoint 0xabd
+#define XK_rightanglebracket 0xabe
+#define XK_marker 0xabf
+#define XK_oneeighth 0xac3
+#define XK_threeeighths 0xac4
+#define XK_fiveeighths 0xac5
+#define XK_seveneighths 0xac6
+#define XK_trademark 0xac9
+#define XK_signaturemark 0xaca
+#define XK_trademarkincircle 0xacb
+#define XK_leftopentriangle 0xacc
+#define XK_rightopentriangle 0xacd
+#define XK_emopencircle 0xace
+#define XK_emopenrectangle 0xacf
+#define XK_leftsinglequotemark 0xad0
+#define XK_rightsinglequotemark 0xad1
+#define XK_leftdoublequotemark 0xad2
+#define XK_rightdoublequotemark 0xad3
+#define XK_prescription 0xad4
+#define XK_minutes 0xad6
+#define XK_seconds 0xad7
+#define XK_latincross 0xad9
+#define XK_hexagram 0xada
+#define XK_filledrectbullet 0xadb
+#define XK_filledlefttribullet 0xadc
+#define XK_filledrighttribullet 0xadd
+#define XK_emfilledcircle 0xade
+#define XK_emfilledrect 0xadf
+#define XK_enopencircbullet 0xae0
+#define XK_enopensquarebullet 0xae1
+#define XK_openrectbullet 0xae2
+#define XK_opentribulletup 0xae3
+#define XK_opentribulletdown 0xae4
+#define XK_openstar 0xae5
+#define XK_enfilledcircbullet 0xae6
+#define XK_enfilledsqbullet 0xae7
+#define XK_filledtribulletup 0xae8
+#define XK_filledtribulletdown 0xae9
+#define XK_leftpointer 0xaea
+#define XK_rightpointer 0xaeb
+#define XK_club 0xaec
+#define XK_diamond 0xaed
+#define XK_heart 0xaee
+#define XK_maltesecross 0xaf0
+#define XK_dagger 0xaf1
+#define XK_doubledagger 0xaf2
+#define XK_checkmark 0xaf3
+#define XK_ballotcross 0xaf4
+#define XK_musicalsharp 0xaf5
+#define XK_musicalflat 0xaf6
+#define XK_malesymbol 0xaf7
+#define XK_femalesymbol 0xaf8
+#define XK_telephone 0xaf9
+#define XK_telephonerecorder 0xafa
+#define XK_phonographcopyright 0xafb
+#define XK_caret 0xafc
+#define XK_singlelowquotemark 0xafd
+#define XK_doublelowquotemark 0xafe
+#define XK_cursor 0xaff
+#endif /* XK_PUBLISHING */
+
+/*
+ * APL
+ * Byte 3 = b
+ */
+
+#ifdef XK_APL
+#define XK_leftcaret 0xba3
+#define XK_rightcaret 0xba6
+#define XK_downcaret 0xba8
+#define XK_upcaret 0xba9
+#define XK_overbar 0xbc0
+#define XK_downtack 0xbc2
+#define XK_upshoe 0xbc3
+#define XK_downstile 0xbc4
+#define XK_underbar 0xbc6
+#define XK_jot 0xbca
+#define XK_quad 0xbcc
+#define XK_uptack 0xbce
+#define XK_circle 0xbcf
+#define XK_upstile 0xbd3
+#define XK_downshoe 0xbd6
+#define XK_rightshoe 0xbd8
+#define XK_leftshoe 0xbda
+#define XK_lefttack 0xbdc
+#define XK_righttack 0xbfc
+#endif /* XK_APL */
+
+/*
+ * Hebrew
+ * Byte 3 = c
+ */
+
+#ifdef XK_HEBREW
+#define XK_hebrew_doublelowline 0xcdf
+#define XK_hebrew_aleph 0xce0
+#define XK_hebrew_bet 0xce1
+#define XK_hebrew_beth 0xce1 /* deprecated */
+#define XK_hebrew_gimel 0xce2
+#define XK_hebrew_gimmel 0xce2 /* deprecated */
+#define XK_hebrew_dalet 0xce3
+#define XK_hebrew_daleth 0xce3 /* deprecated */
+#define XK_hebrew_he 0xce4
+#define XK_hebrew_waw 0xce5
+#define XK_hebrew_zain 0xce6
+#define XK_hebrew_zayin 0xce6 /* deprecated */
+#define XK_hebrew_chet 0xce7
+#define XK_hebrew_het 0xce7 /* deprecated */
+#define XK_hebrew_tet 0xce8
+#define XK_hebrew_teth 0xce8 /* deprecated */
+#define XK_hebrew_yod 0xce9
+#define XK_hebrew_finalkaph 0xcea
+#define XK_hebrew_kaph 0xceb
+#define XK_hebrew_lamed 0xcec
+#define XK_hebrew_finalmem 0xced
+#define XK_hebrew_mem 0xcee
+#define XK_hebrew_finalnun 0xcef
+#define XK_hebrew_nun 0xcf0
+#define XK_hebrew_samech 0xcf1
+#define XK_hebrew_samekh 0xcf1 /* deprecated */
+#define XK_hebrew_ayin 0xcf2
+#define XK_hebrew_finalpe 0xcf3
+#define XK_hebrew_pe 0xcf4
+#define XK_hebrew_finalzade 0xcf5
+#define XK_hebrew_finalzadi 0xcf5 /* deprecated */
+#define XK_hebrew_zade 0xcf6
+#define XK_hebrew_zadi 0xcf6 /* deprecated */
+#define XK_hebrew_qoph 0xcf7
+#define XK_hebrew_kuf 0xcf7 /* deprecated */
+#define XK_hebrew_resh 0xcf8
+#define XK_hebrew_shin 0xcf9
+#define XK_hebrew_taw 0xcfa
+#define XK_hebrew_taf 0xcfa /* deprecated */
+#define XK_Hebrew_switch 0xFF7E /* Alias for mode_switch */
+#endif /* XK_HEBREW */
+
+/*
+ * Thai
+ * Byte 3 = d
+ */
+
+#ifdef XK_THAI
+#define XK_Thai_kokai 0xda1
+#define XK_Thai_khokhai 0xda2
+#define XK_Thai_khokhuat 0xda3
+#define XK_Thai_khokhwai 0xda4
+#define XK_Thai_khokhon 0xda5
+#define XK_Thai_khorakhang 0xda6
+#define XK_Thai_ngongu 0xda7
+#define XK_Thai_chochan 0xda8
+#define XK_Thai_choching 0xda9
+#define XK_Thai_chochang 0xdaa
+#define XK_Thai_soso 0xdab
+#define XK_Thai_chochoe 0xdac
+#define XK_Thai_yoying 0xdad
+#define XK_Thai_dochada 0xdae
+#define XK_Thai_topatak 0xdaf
+#define XK_Thai_thothan 0xdb0
+#define XK_Thai_thonangmontho 0xdb1
+#define XK_Thai_thophuthao 0xdb2
+#define XK_Thai_nonen 0xdb3
+#define XK_Thai_dodek 0xdb4
+#define XK_Thai_totao 0xdb5
+#define XK_Thai_thothung 0xdb6
+#define XK_Thai_thothahan 0xdb7
+#define XK_Thai_thothong 0xdb8
+#define XK_Thai_nonu 0xdb9
+#define XK_Thai_bobaimai 0xdba
+#define XK_Thai_popla 0xdbb
+#define XK_Thai_phophung 0xdbc
+#define XK_Thai_fofa 0xdbd
+#define XK_Thai_phophan 0xdbe
+#define XK_Thai_fofan 0xdbf
+#define XK_Thai_phosamphao 0xdc0
+#define XK_Thai_moma 0xdc1
+#define XK_Thai_yoyak 0xdc2
+#define XK_Thai_rorua 0xdc3
+#define XK_Thai_ru 0xdc4
+#define XK_Thai_loling 0xdc5
+#define XK_Thai_lu 0xdc6
+#define XK_Thai_wowaen 0xdc7
+#define XK_Thai_sosala 0xdc8
+#define XK_Thai_sorusi 0xdc9
+#define XK_Thai_sosua 0xdca
+#define XK_Thai_hohip 0xdcb
+#define XK_Thai_lochula 0xdcc
+#define XK_Thai_oang 0xdcd
+#define XK_Thai_honokhuk 0xdce
+#define XK_Thai_paiyannoi 0xdcf
+#define XK_Thai_saraa 0xdd0
+#define XK_Thai_maihanakat 0xdd1
+#define XK_Thai_saraaa 0xdd2
+#define XK_Thai_saraam 0xdd3
+#define XK_Thai_sarai 0xdd4
+#define XK_Thai_saraii 0xdd5
+#define XK_Thai_saraue 0xdd6
+#define XK_Thai_sarauee 0xdd7
+#define XK_Thai_sarau 0xdd8
+#define XK_Thai_sarauu 0xdd9
+#define XK_Thai_phinthu 0xdda
+#define XK_Thai_maihanakat_maitho 0xdde
+#define XK_Thai_baht 0xddf
+#define XK_Thai_sarae 0xde0
+#define XK_Thai_saraae 0xde1
+#define XK_Thai_sarao 0xde2
+#define XK_Thai_saraaimaimuan 0xde3
+#define XK_Thai_saraaimaimalai 0xde4
+#define XK_Thai_lakkhangyao 0xde5
+#define XK_Thai_maiyamok 0xde6
+#define XK_Thai_maitaikhu 0xde7
+#define XK_Thai_maiek 0xde8
+#define XK_Thai_maitho 0xde9
+#define XK_Thai_maitri 0xdea
+#define XK_Thai_maichattawa 0xdeb
+#define XK_Thai_thanthakhat 0xdec
+#define XK_Thai_nikhahit 0xded
+#define XK_Thai_leksun 0xdf0
+#define XK_Thai_leknung 0xdf1
+#define XK_Thai_leksong 0xdf2
+#define XK_Thai_leksam 0xdf3
+#define XK_Thai_leksi 0xdf4
+#define XK_Thai_lekha 0xdf5
+#define XK_Thai_lekhok 0xdf6
+#define XK_Thai_lekchet 0xdf7
+#define XK_Thai_lekpaet 0xdf8
+#define XK_Thai_lekkao 0xdf9
+#endif /* XK_THAI */
+
+/*
+ * Korean
+ * Byte 3 = e
+ */
+
+#ifdef XK_KOREAN
+
+#define XK_Hangul 0xff31 /* Hangul start/stop(toggle) */
+#define XK_Hangul_Start 0xff32 /* Hangul start */
+#define XK_Hangul_End 0xff33 /* Hangul end, English start */
+#define XK_Hangul_Hanja 0xff34 /* Start Hangul->Hanja Conversion */
+#define XK_Hangul_Jamo 0xff35 /* Hangul Jamo mode */
+#define XK_Hangul_Romaja 0xff36 /* Hangul Romaja mode */
+#define XK_Hangul_Codeinput 0xff37 /* Hangul code input mode */
+#define XK_Hangul_Jeonja 0xff38 /* Jeonja mode */
+#define XK_Hangul_Banja 0xff39 /* Banja mode */
+#define XK_Hangul_PreHanja 0xff3a /* Pre Hanja conversion */
+#define XK_Hangul_PostHanja 0xff3b /* Post Hanja conversion */
+#define XK_Hangul_SingleCandidate 0xff3c /* Single candidate */
+#define XK_Hangul_MultipleCandidate 0xff3d /* Multiple candidate */
+#define XK_Hangul_PreviousCandidate 0xff3e /* Previous candidate */
+#define XK_Hangul_Special 0xff3f /* Special symbols */
+#define XK_Hangul_switch 0xFF7E /* Alias for mode_switch */
+
+/* Hangul Consonant Characters */
+#define XK_Hangul_Kiyeog 0xea1
+#define XK_Hangul_SsangKiyeog 0xea2
+#define XK_Hangul_KiyeogSios 0xea3
+#define XK_Hangul_Nieun 0xea4
+#define XK_Hangul_NieunJieuj 0xea5
+#define XK_Hangul_NieunHieuh 0xea6
+#define XK_Hangul_Dikeud 0xea7
+#define XK_Hangul_SsangDikeud 0xea8
+#define XK_Hangul_Rieul 0xea9
+#define XK_Hangul_RieulKiyeog 0xeaa
+#define XK_Hangul_RieulMieum 0xeab
+#define XK_Hangul_RieulPieub 0xeac
+#define XK_Hangul_RieulSios 0xead
+#define XK_Hangul_RieulTieut 0xeae
+#define XK_Hangul_RieulPhieuf 0xeaf
+#define XK_Hangul_RieulHieuh 0xeb0
+#define XK_Hangul_Mieum 0xeb1
+#define XK_Hangul_Pieub 0xeb2
+#define XK_Hangul_SsangPieub 0xeb3
+#define XK_Hangul_PieubSios 0xeb4
+#define XK_Hangul_Sios 0xeb5
+#define XK_Hangul_SsangSios 0xeb6
+#define XK_Hangul_Ieung 0xeb7
+#define XK_Hangul_Jieuj 0xeb8
+#define XK_Hangul_SsangJieuj 0xeb9
+#define XK_Hangul_Cieuc 0xeba
+#define XK_Hangul_Khieuq 0xebb
+#define XK_Hangul_Tieut 0xebc
+#define XK_Hangul_Phieuf 0xebd
+#define XK_Hangul_Hieuh 0xebe
+
+/* Hangul Vowel Characters */
+#define XK_Hangul_A 0xebf
+#define XK_Hangul_AE 0xec0
+#define XK_Hangul_YA 0xec1
+#define XK_Hangul_YAE 0xec2
+#define XK_Hangul_EO 0xec3
+#define XK_Hangul_E 0xec4
+#define XK_Hangul_YEO 0xec5
+#define XK_Hangul_YE 0xec6
+#define XK_Hangul_O 0xec7
+#define XK_Hangul_WA 0xec8
+#define XK_Hangul_WAE 0xec9
+#define XK_Hangul_OE 0xeca
+#define XK_Hangul_YO 0xecb
+#define XK_Hangul_U 0xecc
+#define XK_Hangul_WEO 0xecd
+#define XK_Hangul_WE 0xece
+#define XK_Hangul_WI 0xecf
+#define XK_Hangul_YU 0xed0
+#define XK_Hangul_EU 0xed1
+#define XK_Hangul_YI 0xed2
+#define XK_Hangul_I 0xed3
+
+/* Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_Kiyeog 0xed4
+#define XK_Hangul_J_SsangKiyeog 0xed5
+#define XK_Hangul_J_KiyeogSios 0xed6
+#define XK_Hangul_J_Nieun 0xed7
+#define XK_Hangul_J_NieunJieuj 0xed8
+#define XK_Hangul_J_NieunHieuh 0xed9
+#define XK_Hangul_J_Dikeud 0xeda
+#define XK_Hangul_J_Rieul 0xedb
+#define XK_Hangul_J_RieulKiyeog 0xedc
+#define XK_Hangul_J_RieulMieum 0xedd
+#define XK_Hangul_J_RieulPieub 0xede
+#define XK_Hangul_J_RieulSios 0xedf
+#define XK_Hangul_J_RieulTieut 0xee0
+#define XK_Hangul_J_RieulPhieuf 0xee1
+#define XK_Hangul_J_RieulHieuh 0xee2
+#define XK_Hangul_J_Mieum 0xee3
+#define XK_Hangul_J_Pieub 0xee4
+#define XK_Hangul_J_PieubSios 0xee5
+#define XK_Hangul_J_Sios 0xee6
+#define XK_Hangul_J_SsangSios 0xee7
+#define XK_Hangul_J_Ieung 0xee8
+#define XK_Hangul_J_Jieuj 0xee9
+#define XK_Hangul_J_Cieuc 0xeea
+#define XK_Hangul_J_Khieuq 0xeeb
+#define XK_Hangul_J_Tieut 0xeec
+#define XK_Hangul_J_Phieuf 0xeed
+#define XK_Hangul_J_Hieuh 0xeee
+
+/* Ancient Hangul Consonant Characters */
+#define XK_Hangul_RieulYeorinHieuh 0xeef
+#define XK_Hangul_SunkyeongeumMieum 0xef0
+#define XK_Hangul_SunkyeongeumPieub 0xef1
+#define XK_Hangul_PanSios 0xef2
+#define XK_Hangul_KkogjiDalrinIeung 0xef3
+#define XK_Hangul_SunkyeongeumPhieuf 0xef4
+#define XK_Hangul_YeorinHieuh 0xef5
+
+/* Ancient Hangul Vowel Characters */
+#define XK_Hangul_AraeA 0xef6
+#define XK_Hangul_AraeAE 0xef7
+
+/* Ancient Hangul syllable-final (JongSeong) Characters */
+#define XK_Hangul_J_PanSios 0xef8
+#define XK_Hangul_J_KkogjiDalrinIeung 0xef9
+#define XK_Hangul_J_YeorinHieuh 0xefa
+
+/* Korean currency symbol */
+#define XK_Korean_Won 0xeff
+
+#endif /* XK_KOREAN */
+
+/* Euro currency symbol */
+#define XK_EuroSign 0x20ac
+
+#endif
diff --git a/krfb/libvncserver/mac.c b/krfb/libvncserver/mac.c
new file mode 100644
index 00000000..a3c4f3c6
--- /dev/null
+++ b/krfb/libvncserver/mac.c
@@ -0,0 +1,604 @@
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * Cut in two parts by Johannes Schindelin (2001): libvncserver and OSXvnc.
+ *
+ *
+ * This file implements every system specific function for Mac OS X.
+ *
+ * It includes the keyboard functions:
+ *
+ void KbdAddEvent(down, keySym, cl)
+ Bool down;
+ KeySym keySym;
+ rfbClientPtr cl;
+ void KbdReleaseAllKeys()
+ *
+ * the mouse functions:
+ *
+ void PtrAddEvent(buttonMask, x, y, cl)
+ int buttonMask;
+ int x;
+ int y;
+ rfbClientPtr cl;
+ *
+ */
+
+#define LOCAL_CONTROL
+
+#ifdef LOCAL_CONTROL
+#include "1instance.c"
+#endif
+
+#include <unistd.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <Carbon/Carbon.h>
+/* zlib doesn't like Byte already defined */
+#undef Byte
+#undef TRUE
+#undef Bool
+#include "rfb.h"
+#include "keysym.h"
+
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/pwr_mgt/IOPM.h>
+#include <stdio.h>
+#include <signal.h>
+#include <pthread.h>
+
+Bool rfbNoDimming = FALSE;
+Bool rfbNoSleep = TRUE;
+
+static pthread_mutex_t dimming_mutex;
+static unsigned long dim_time;
+static unsigned long sleep_time;
+static mach_port_t master_dev_port;
+static io_connect_t power_mgt;
+static Bool initialized = FALSE;
+static Bool dim_time_saved = FALSE;
+static Bool sleep_time_saved = FALSE;
+
+static int
+saveDimSettings(void)
+{
+ if (IOPMGetAggressiveness(power_mgt,
+ kPMMinutesToDim,
+ &dim_time) != kIOReturnSuccess)
+ return -1;
+
+ dim_time_saved = TRUE;
+ return 0;
+}
+
+static int
+restoreDimSettings(void)
+{
+ if (!dim_time_saved)
+ return -1;
+
+ if (IOPMSetAggressiveness(power_mgt,
+ kPMMinutesToDim,
+ dim_time) != kIOReturnSuccess)
+ return -1;
+
+ dim_time_saved = FALSE;
+ dim_time = 0;
+ return 0;
+}
+
+static int
+saveSleepSettings(void)
+{
+ if (IOPMGetAggressiveness(power_mgt,
+ kPMMinutesToSleep,
+ &sleep_time) != kIOReturnSuccess)
+ return -1;
+
+ sleep_time_saved = TRUE;
+ return 0;
+}
+
+static int
+restoreSleepSettings(void)
+{
+ if (!sleep_time_saved)
+ return -1;
+
+ if (IOPMSetAggressiveness(power_mgt,
+ kPMMinutesToSleep,
+ sleep_time) != kIOReturnSuccess)
+ return -1;
+
+ sleep_time_saved = FALSE;
+ sleep_time = 0;
+ return 0;
+}
+
+
+int
+rfbDimmingInit(void)
+{
+ pthread_mutex_init(&dimming_mutex, NULL);
+
+ if (IOMasterPort(bootstrap_port, &master_dev_port) != kIOReturnSuccess)
+ return -1;
+
+ if (!(power_mgt = IOPMFindPowerManagement(master_dev_port)))
+ return -1;
+
+ if (rfbNoDimming) {
+ if (saveDimSettings() < 0)
+ return -1;
+ if (IOPMSetAggressiveness(power_mgt,
+ kPMMinutesToDim, 0) != kIOReturnSuccess)
+ return -1;
+ }
+
+ if (rfbNoSleep) {
+ if (saveSleepSettings() < 0)
+ return -1;
+ if (IOPMSetAggressiveness(power_mgt,
+ kPMMinutesToSleep, 0) != kIOReturnSuccess)
+ return -1;
+ }
+
+ initialized = TRUE;
+ return 0;
+}
+
+
+int
+rfbUndim(void)
+{
+ int result = -1;
+
+ pthread_mutex_lock(&dimming_mutex);
+
+ if (!initialized)
+ goto DONE;
+
+ if (!rfbNoDimming) {
+ if (saveDimSettings() < 0)
+ goto DONE;
+ if (IOPMSetAggressiveness(power_mgt, kPMMinutesToDim, 0) != kIOReturnSuccess)
+ goto DONE;
+ if (restoreDimSettings() < 0)
+ goto DONE;
+ }
+
+ if (!rfbNoSleep) {
+ if (saveSleepSettings() < 0)
+ goto DONE;
+ if (IOPMSetAggressiveness(power_mgt, kPMMinutesToSleep, 0) != kIOReturnSuccess)
+ goto DONE;
+ if (restoreSleepSettings() < 0)
+ goto DONE;
+ }
+
+ result = 0;
+
+ DONE:
+ pthread_mutex_unlock(&dimming_mutex);
+ return result;
+}
+
+
+int
+rfbDimmingShutdown(void)
+{
+ int result = -1;
+
+ if (!initialized)
+ goto DONE;
+
+ pthread_mutex_lock(&dimming_mutex);
+ if (dim_time_saved)
+ if (restoreDimSettings() < 0)
+ goto DONE;
+ if (sleep_time_saved)
+ if (restoreSleepSettings() < 0)
+ goto DONE;
+
+ result = 0;
+
+ DONE:
+ pthread_mutex_unlock(&dimming_mutex);
+ return result;
+}
+
+rfbScreenInfoPtr rfbScreen;
+
+void rfbShutdown(rfbClientPtr cl);
+
+/* some variables to enable special behaviour */
+int startTime = -1, maxSecsToConnect = 0;
+Bool disconnectAfterFirstClient = TRUE;
+
+/* Where do I get the "official" list of Mac key codes?
+ Ripped these out of a Mac II emulator called Basilisk II
+ that I found on the net. */
+static int keyTable[] = {
+ /* The alphabet */
+ XK_A, 0, /* A */
+ XK_B, 11, /* B */
+ XK_C, 8, /* C */
+ XK_D, 2, /* D */
+ XK_E, 14, /* E */
+ XK_F, 3, /* F */
+ XK_G, 5, /* G */
+ XK_H, 4, /* H */
+ XK_I, 34, /* I */
+ XK_J, 38, /* J */
+ XK_K, 40, /* K */
+ XK_L, 37, /* L */
+ XK_M, 46, /* M */
+ XK_N, 45, /* N */
+ XK_O, 31, /* O */
+ XK_P, 35, /* P */
+ XK_Q, 12, /* Q */
+ XK_R, 15, /* R */
+ XK_S, 1, /* S */
+ XK_T, 17, /* T */
+ XK_U, 32, /* U */
+ XK_V, 9, /* V */
+ XK_W, 13, /* W */
+ XK_X, 7, /* X */
+ XK_Y, 16, /* Y */
+ XK_Z, 6, /* Z */
+ XK_a, 0, /* a */
+ XK_b, 11, /* b */
+ XK_c, 8, /* c */
+ XK_d, 2, /* d */
+ XK_e, 14, /* e */
+ XK_f, 3, /* f */
+ XK_g, 5, /* g */
+ XK_h, 4, /* h */
+ XK_i, 34, /* i */
+ XK_j, 38, /* j */
+ XK_k, 40, /* k */
+ XK_l, 37, /* l */
+ XK_m, 46, /* m */
+ XK_n, 45, /* n */
+ XK_o, 31, /* o */
+ XK_p, 35, /* p */
+ XK_q, 12, /* q */
+ XK_r, 15, /* r */
+ XK_s, 1, /* s */
+ XK_t, 17, /* t */
+ XK_u, 32, /* u */
+ XK_v, 9, /* v */
+ XK_w, 13, /* w */
+ XK_x, 7, /* x */
+ XK_y, 16, /* y */
+ XK_z, 6, /* z */
+
+ /* Numbers */
+ XK_0, 29, /* 0 */
+ XK_1, 18, /* 1 */
+ XK_2, 19, /* 2 */
+ XK_3, 20, /* 3 */
+ XK_4, 21, /* 4 */
+ XK_5, 23, /* 5 */
+ XK_6, 22, /* 6 */
+ XK_7, 26, /* 7 */
+ XK_8, 28, /* 8 */
+ XK_9, 25, /* 9 */
+
+ /* Symbols */
+ XK_exclam, 18, /* ! */
+ XK_at, 19, /* @ */
+ XK_numbersign, 20, /* # */
+ XK_dollar, 21, /* $ */
+ XK_percent, 23, /* % */
+ XK_asciicircum, 22, /* ^ */
+ XK_ampersand, 26, /* & */
+ XK_asterisk, 28, /* * */
+ XK_parenleft, 25, /* ( */
+ XK_parenright, 29, /* ) */
+ XK_minus, 27, /* - */
+ XK_underscore, 27, /* _ */
+ XK_equal, 24, /* = */
+ XK_plus, 24, /* + */
+ XK_grave, 10, /* ` */ /* XXX ? */
+ XK_asciitilde, 10, /* ~ */
+ XK_bracketleft, 33, /* [ */
+ XK_braceleft, 33, /* { */
+ XK_bracketright, 30, /* ] */
+ XK_braceright, 30, /* } */
+ XK_semicolon, 41, /* ; */
+ XK_colon, 41, /* : */
+ XK_apostrophe, 39, /* ' */
+ XK_quotedbl, 39, /* " */
+ XK_comma, 43, /* , */
+ XK_less, 43, /* < */
+ XK_period, 47, /* . */
+ XK_greater, 47, /* > */
+ XK_slash, 44, /* / */
+ XK_question, 44, /* ? */
+ XK_backslash, 42, /* \ */
+ XK_bar, 42, /* | */
+
+ /* "Special" keys */
+ XK_space, 49, /* Space */
+ XK_Return, 36, /* Return */
+ XK_Delete, 117, /* Delete */
+ XK_Tab, 48, /* Tab */
+ XK_Escape, 53, /* Esc */
+ XK_Caps_Lock, 57, /* Caps Lock */
+ XK_Num_Lock, 71, /* Num Lock */
+ XK_Scroll_Lock, 107, /* Scroll Lock */
+ XK_Pause, 113, /* Pause */
+ XK_BackSpace, 51, /* Backspace */
+ XK_Insert, 114, /* Insert */
+
+ /* Cursor movement */
+ XK_Up, 126, /* Cursor Up */
+ XK_Down, 125, /* Cursor Down */
+ XK_Left, 123, /* Cursor Left */
+ XK_Right, 124, /* Cursor Right */
+ XK_Page_Up, 116, /* Page Up */
+ XK_Page_Down, 121, /* Page Down */
+ XK_Home, 115, /* Home */
+ XK_End, 119, /* End */
+
+ /* Numeric keypad */
+ XK_KP_0, 82, /* KP 0 */
+ XK_KP_1, 83, /* KP 1 */
+ XK_KP_2, 84, /* KP 2 */
+ XK_KP_3, 85, /* KP 3 */
+ XK_KP_4, 86, /* KP 4 */
+ XK_KP_5, 87, /* KP 5 */
+ XK_KP_6, 88, /* KP 6 */
+ XK_KP_7, 89, /* KP 7 */
+ XK_KP_8, 91, /* KP 8 */
+ XK_KP_9, 92, /* KP 9 */
+ XK_KP_Enter, 76, /* KP Enter */
+ XK_KP_Decimal, 65, /* KP . */
+ XK_KP_Add, 69, /* KP + */
+ XK_KP_Subtract, 78, /* KP - */
+ XK_KP_Multiply, 67, /* KP * */
+ XK_KP_Divide, 75, /* KP / */
+
+ /* Function keys */
+ XK_F1, 122, /* F1 */
+ XK_F2, 120, /* F2 */
+ XK_F3, 99, /* F3 */
+ XK_F4, 118, /* F4 */
+ XK_F5, 96, /* F5 */
+ XK_F6, 97, /* F6 */
+ XK_F7, 98, /* F7 */
+ XK_F8, 100, /* F8 */
+ XK_F9, 101, /* F9 */
+ XK_F10, 109, /* F10 */
+ XK_F11, 103, /* F11 */
+ XK_F12, 111, /* F12 */
+
+ /* Modifier keys */
+ XK_Shift_L, 56, /* Shift Left */
+ XK_Shift_R, 56, /* Shift Right */
+ XK_Control_L, 59, /* Ctrl Left */
+ XK_Control_R, 59, /* Ctrl Right */
+ XK_Meta_L, 58, /* Logo Left (-> Option) */
+ XK_Meta_R, 58, /* Logo Right (-> Option) */
+ XK_Alt_L, 55, /* Alt Left (-> Command) */
+ XK_Alt_R, 55, /* Alt Right (-> Command) */
+
+ /* Weirdness I can't figure out */
+ /* XK_3270_PrintScreen, 105, /* PrintScrn */ /* XXX ? */
+ /* ??? 94, 50, /* International */
+ XK_Menu, 50, /* Menu (-> International) */
+};
+
+void
+KbdAddEvent(Bool down, KeySym keySym, struct _rfbClientRec* cl)
+{
+ int i;
+ CGKeyCode keyCode = -1;
+ int found = 0;
+
+ if(((int)cl->clientData)==-1) return; /* viewOnly */
+
+ rfbUndim();
+
+ for (i = 0; i < (sizeof(keyTable) / sizeof(int)); i += 2) {
+ if (keyTable[i] == keySym) {
+ keyCode = keyTable[i+1];
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ rfbLog("warning: couldn't figure out keycode for X keysym %d (0x%x)\n",
+ (int)keySym, (int)keySym);
+ } else {
+ /* Hopefully I can get away with not specifying a CGCharCode.
+ (Why would you need both?) */
+ CGPostKeyboardEvent((CGCharCode)0, keyCode, down);
+ }
+}
+
+void
+PtrAddEvent(buttonMask, x, y, cl)
+ int buttonMask;
+ int x;
+ int y;
+ rfbClientPtr cl;
+{
+ CGPoint position;
+
+ if(((int)cl->clientData)==-1) return; /* viewOnly */
+
+ rfbUndim();
+
+ position.x = x;
+ position.y = y;
+
+ CGPostMouseEvent(position, TRUE, 8,
+ (buttonMask & (1 << 0)) ? TRUE : FALSE,
+ (buttonMask & (1 << 1)) ? TRUE : FALSE,
+ (buttonMask & (1 << 2)) ? TRUE : FALSE,
+ (buttonMask & (1 << 3)) ? TRUE : FALSE,
+ (buttonMask & (1 << 4)) ? TRUE : FALSE,
+ (buttonMask & (1 << 5)) ? TRUE : FALSE,
+ (buttonMask & (1 << 6)) ? TRUE : FALSE,
+ (buttonMask & (1 << 7)) ? TRUE : FALSE);
+}
+
+Bool viewOnly = FALSE, sharedMode = FALSE;
+
+void
+ScreenInit(int argc, char**argv)
+{
+ int bitsPerSample=CGDisplayBitsPerSample(kCGDirectMainDisplay);
+ rfbScreen = rfbGetScreen(&argc,argv,
+ CGDisplayPixelsWide(kCGDirectMainDisplay),
+ CGDisplayPixelsHigh(kCGDirectMainDisplay),
+ bitsPerSample,
+ CGDisplaySamplesPerPixel(kCGDirectMainDisplay),4);
+ rfbScreen->rfbServerFormat.redShift = bitsPerSample*2;
+ rfbScreen->rfbServerFormat.greenShift = bitsPerSample*1;
+ rfbScreen->rfbServerFormat.blueShift = 0;
+
+ gethostname(rfbScreen->rfbThisHost, 255);
+ rfbScreen->paddedWidthInBytes = CGDisplayBytesPerRow(kCGDirectMainDisplay);
+ rfbScreen->frameBuffer =
+ (char *)CGDisplayBaseAddress(kCGDirectMainDisplay);
+
+ rfbScreen->ptrAddEvent = PtrAddEvent;
+ rfbScreen->kbdAddEvent = KbdAddEvent;
+
+ if(sharedMode) {
+ rfbScreen->rfbAlwaysShared = TRUE;
+ }
+
+ rfbInitServer(rfbScreen);
+}
+
+#ifdef LOCAL_CONTROL
+single_instance_struct single_instance = { "/tmp/OSXvnc_control" };
+#endif
+
+static void
+refreshCallback(CGRectCount count, const CGRect *rectArray, void *ignore)
+{
+ int i;
+
+#ifdef LOCAL_CONTROL
+ char message[1024];
+
+ if(get_next_message(message,1024,&single_instance,50)) {
+ if(message[0]=='l' && message[1]==0) {
+ rfbClientPtr cl;
+ int i;
+ for(i=0,cl=rfbScreen->rfbClientHead;cl;cl=cl->next,i++)
+ fprintf(stderr,"%02d: %s\n",i,cl->host);
+ } else if(message[0]=='t') {
+ rfbClientPtr cl;
+ for(cl=rfbScreen->rfbClientHead;cl;cl=cl->next)
+ if(!strcmp(message+1,cl->host)) {
+ cl->clientData=(void*)((cl->clientData==0)?-1:0);
+ break;
+ }
+ }
+#ifdef BACKCHANNEL
+ else if(message[0]=='b')
+ rfbSendBackChannel(rfbScreen,message+1,strlen(message+1));
+#endif
+ }
+#endif
+
+ if(startTime>0 && time(0)>startTime+maxSecsToConnect)
+ rfbShutdown(0);
+
+ for (i = 0; i < count; i++)
+ rfbMarkRectAsModified(rfbScreen,
+ rectArray[i].origin.x,rectArray[i].origin.y,
+ rectArray[i].origin.x + rectArray[i].size.width,
+ rectArray[i].origin.y + rectArray[i].size.height);
+}
+
+void clientGone(rfbClientPtr cl)
+{
+ rfbShutdown(cl);
+}
+
+enum rfbNewClientAction newClient(rfbClientPtr cl)
+{
+ if(startTime>0 && time(0)>startTime+maxSecsToConnect)
+ rfbShutdown(cl);
+
+ if(disconnectAfterFirstClient)
+ cl->clientGoneHook = clientGone;
+
+ cl->clientData=(void*)((viewOnly)?-1:0);
+
+ return(RFB_CLIENT_ACCEPT);
+}
+
+int main(int argc,char *argv[])
+{
+ int i;
+
+#ifdef LOCAL_CONTROL
+ char message[1024];
+
+ open_control_file(&single_instance);
+#endif
+
+ for(i=argc-1;i>0;i--)
+#ifdef LOCAL_CONTROL
+ if(i<argc-1 && !strcmp(argv[i],"-toggleviewonly")) {
+ snprintf(message, sizeof(message), "t%s",argv[i+1]);
+ send_message(&single_instance,message);
+ exit(0);
+ } else if(!strcmp(argv[i],"-listclients")) {
+ fprintf(stderr,"list clients\n");
+ send_message(&single_instance,"l");
+ exit(0);
+ } else
+#ifdef BACKCHANNEL
+ if(i<argc-1 && !strcmp(argv[i],"-backchannel")) {
+ snprintf(message, sizeof(message), "b%s",argv[i+1]);
+ send_message(&single_instance,message);
+ exit(0);
+ } else
+#endif
+#endif
+ if(i<argc-1 && strcmp(argv[i],"-wait4client")==0) {
+ maxSecsToConnect = atoi(argv[i+1])/1000;
+ startTime = time(0);
+ } else if(strcmp(argv[i],"-runforever")==0) {
+ disconnectAfterFirstClient = FALSE;
+ } else if(strcmp(argv[i],"-viewonly")==0) {
+ viewOnly=TRUE;
+ } else if(strcmp(argv[i],"-shared")==0) {
+ sharedMode=TRUE;
+ }
+
+ rfbDimmingInit();
+
+ ScreenInit(argc,argv);
+ rfbScreen->newClientHook = newClient;
+
+ /* enter background event loop */
+ rfbRunEventLoop(rfbScreen,40,TRUE);
+
+ /* enter OS X loop */
+ CGRegisterScreenRefreshCallback(refreshCallback, NULL);
+ RunApplicationEventLoop();
+
+ rfbDimmingShutdown();
+
+ return(0); /* never ... */
+}
+
+void rfbShutdown(rfbClientPtr cl)
+{
+ rfbScreenCleanup(rfbScreen);
+ rfbDimmingShutdown();
+ exit(0);
+}
diff --git a/krfb/libvncserver/main.c b/krfb/libvncserver/main.c
new file mode 100644
index 00000000..207e512d
--- /dev/null
+++ b/krfb/libvncserver/main.c
@@ -0,0 +1,729 @@
+/*
+ * This file is called main.c, because it contains most of the new functions
+ * for use with LibVNCServer.
+ *
+ * LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
+ * Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * see GPL (latest version) for full details
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+
+#ifndef false
+#define false 0
+#define true -1
+#endif
+
+#include <sys/types.h>
+#ifdef __osf__
+typedef int socklen_t;
+#endif
+#ifndef WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <time.h>
+
+#include "rfb.h"
+#include "sraRegion.h"
+
+/* minimum interval between attempts to send something */
+#define PING_MS 10000
+
+MUTEX(logMutex);
+
+int rfbEnableLogging=1;
+
+/* we cannot compare to _LITTLE_ENDIAN, because some systems
+ (as Solaris) assume little endian if _LITTLE_ENDIAN is
+ defined, even if _BYTE_ORDER is not _LITTLE_ENDIAN */
+char rfbEndianTest = (_BYTE_ORDER == 1234);
+
+/* from rfbserver.c */
+void rfbIncrClientRef(rfbClientPtr cl);
+void rfbDecrClientRef(rfbClientPtr cl);
+
+void rfbLogEnable(int enabled) {
+ rfbEnableLogging=enabled;
+}
+
+/*
+ * rfbLog prints a time-stamped message to the log file (stderr).
+ */
+
+void
+rfbLog(const char *format, ...)
+{
+ va_list args;
+ char buf[256];
+ time_t log_clock;
+
+ if(!rfbEnableLogging)
+ return;
+
+ LOCK(logMutex);
+ va_start(args, format);
+
+ time(&log_clock);
+ strftime(buf, 255, "%d/%m/%Y %T ", localtime(&log_clock));
+ fprintf(stderr, "%s", buf);
+
+ vfprintf(stderr, format, args);
+ fflush(stderr);
+
+ va_end(args);
+ UNLOCK(logMutex);
+}
+
+void rfbLogPerror(const char *str)
+{
+ rfbLog("%s: %s\n", str, strerror(errno));
+}
+
+void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
+{
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl;
+
+ rfbUndrawCursor(rfbScreen);
+
+ iterator=rfbGetClientIterator(rfbScreen);
+ while((cl=rfbClientIteratorNext(iterator))) {
+ LOCK(cl->updateMutex);
+ if(cl->useCopyRect) {
+ sraRegionPtr modifiedRegionBackup;
+ if(!sraRgnEmpty(cl->copyRegion)) {
+ if(cl->copyDX!=dx || cl->copyDY!=dy) {
+ /* if a copyRegion was not yet executed, treat it as a
+ * modifiedRegion. The idea: in this case it could be
+ * source of the new copyRect or modified anyway. */
+ sraRgnOr(cl->modifiedRegion,cl->copyRegion);
+ sraRgnMakeEmpty(cl->copyRegion);
+ } else {
+ /* we have to set the intersection of the source of the copy
+ * and the old copy to modified. */
+ modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
+ sraRgnOffset(modifiedRegionBackup,-dx,-dy);
+ sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
+ sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
+ sraRgnDestroy(modifiedRegionBackup);
+ }
+ }
+
+ sraRgnOr(cl->copyRegion,copyRegion);
+ cl->copyDX = dx;
+ cl->copyDY = dy;
+
+ /* if there were modified regions, which are now copied,
+ * mark them as modified, because the source of these can be overlapped
+ * either by new modified or now copied regions. */
+ modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
+ sraRgnOffset(modifiedRegionBackup,dx,dy);
+ sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
+ sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
+ sraRgnDestroy(modifiedRegionBackup);
+
+#if 0
+//TODO: is this needed? Or does it mess up deferring?
+ /* while(!sraRgnEmpty(cl->copyRegion)) */ {
+#ifdef HAVE_PTHREADS
+ if(!cl->screen->backgroundLoop)
+#endif
+ {
+ sraRegionPtr updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
+ sraRgnOr(updateRegion,cl->copyRegion);
+ UNLOCK(cl->updateMutex);
+ rfbSendFramebufferUpdate(cl,updateRegion);
+ sraRgnDestroy(updateRegion);
+ continue;
+ }
+ }
+#endif
+ } else {
+ sraRgnOr(cl->modifiedRegion,copyRegion);
+ }
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ }
+
+ rfbReleaseClientIterator(iterator);
+}
+
+void rfbDoCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
+{
+ sraRectangleIterator* i;
+ sraRect rect;
+ int j,widthInBytes,bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8,
+ rowstride=rfbScreen->paddedWidthInBytes;
+ char *in,*out;
+
+ rfbUndrawCursor(rfbScreen);
+
+ /* copy it, really */
+ i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
+ while(sraRgnIteratorNext(i,&rect)) {
+ widthInBytes = (rect.x2-rect.x1)*bpp;
+ out = rfbScreen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
+ in = rfbScreen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
+ if(dy<0)
+ for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
+ memmove(out,in,widthInBytes);
+ else {
+ out += rowstride*(rect.y2-rect.y1-1);
+ in += rowstride*(rect.y2-rect.y1-1);
+ for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
+ memmove(out,in,widthInBytes);
+ }
+ }
+
+ rfbScheduleCopyRegion(rfbScreen,copyRegion,dx,dy);
+}
+
+void rfbDoCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy)
+{
+ sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbDoCopyRegion(rfbScreen,region,dx,dy);
+}
+
+void rfbScheduleCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy)
+{
+ sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbScheduleCopyRegion(rfbScreen,region,dx,dy);
+}
+
+void rfbMarkRegionAsModified(rfbScreenInfoPtr rfbScreen,sraRegionPtr modRegion)
+{
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl;
+
+ iterator=rfbGetClientIterator(rfbScreen);
+ while((cl=rfbClientIteratorNext(iterator))) {
+ LOCK(cl->updateMutex);
+ sraRgnOr(cl->modifiedRegion,modRegion);
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ }
+
+ rfbReleaseClientIterator(iterator);
+}
+
+void rfbMarkRectAsModified(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2)
+{
+ sraRegionPtr region;
+ int i;
+
+ if(x1>x2) { i=x1; x1=x2; x2=i; }
+ if(x1<0) x1=0;
+ if(x2>=rfbScreen->width) x2=rfbScreen->width-1;
+ if(x1==x2) return;
+
+ if(y1>y2) { i=y1; y1=y2; y2=i; }
+ if(y1<0) y1=0;
+ if(y2>=rfbScreen->height) y2=rfbScreen->height-1;
+ if(y1==y2) return;
+
+ region = sraRgnCreateRect(x1,y1,x2,y2);
+ rfbMarkRegionAsModified(rfbScreen,region);
+ sraRgnDestroy(region);
+}
+
+#ifdef HAVE_PTHREADS
+static void *
+clientOutput(void *data)
+{
+ rfbClientPtr cl = (rfbClientPtr)data;
+ Bool haveUpdate;
+ sraRegion* updateRegion;
+
+ while (1) {
+ haveUpdate = false;
+ while (!haveUpdate) {
+ if (cl->sock == -1) {
+ /* Client has disconnected. */
+ return NULL;
+ }
+ LOCK(cl->updateMutex);
+ haveUpdate = FB_UPDATE_PENDING(cl);
+ if(!haveUpdate) {
+ updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
+ haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
+ sraRgnDestroy(updateRegion);
+ }
+ UNLOCK(cl->updateMutex);
+
+ if (!haveUpdate) {
+ LOCK(cl->updateMutex);
+ TIMEDWAIT(cl->updateCond, cl->updateMutex, PING_MS);
+ UNLOCK(cl->updateMutex); /* we really needn't lock now. */
+ if (!haveUpdate)
+ rfbSendPing(cl);
+ }
+ }
+
+ /* OK, now, to save bandwidth, wait a little while for more
+ updates to come along. */
+ usleep(cl->screen->rfbDeferUpdateTime * 1000);
+
+ /* Now, get the region we're going to update, and remove
+ it from cl->modifiedRegion _before_ we send the update.
+ That way, if anything that overlaps the region we're sending
+ is updated, we'll be sure to do another update later. */
+ LOCK(cl->updateMutex);
+ updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
+ UNLOCK(cl->updateMutex);
+
+ /* Now actually send the update. */
+ rfbIncrClientRef(cl);
+ rfbSendFramebufferUpdate(cl, updateRegion);
+ rfbDecrClientRef(cl);
+
+ sraRgnDestroy(updateRegion);
+ }
+
+ return NULL;
+}
+
+static void *
+clientInput(void *data)
+{
+ rfbClientPtr cl = (rfbClientPtr)data;
+ pthread_t output_thread;
+ pthread_create(&output_thread, NULL, clientOutput, (void *)cl);
+
+ while (1) {
+ rfbProcessClientMessage(cl);
+ if (cl->sock == -1) {
+ /* Client has disconnected. */
+ break;
+ }
+ }
+
+ /* Get rid of the output thread. */
+ LOCK(cl->updateMutex);
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+ IF_PTHREADS(pthread_join(output_thread, NULL));
+
+ rfbClientConnectionGone(cl);
+
+ return NULL;
+}
+
+static void*
+listenerRun(void *data)
+{
+ rfbScreenInfoPtr rfbScreen=(rfbScreenInfoPtr)data;
+ int client_fd;
+ struct sockaddr_in peer;
+ rfbClientPtr cl;
+ size_t len;
+
+ if (rfbScreen->inetdSock != -1) {
+ cl = rfbNewClient(rfbScreen, rfbScreen->inetdSock);
+ if (cl && !cl->onHold)
+ rfbStartOnHoldClient(cl);
+ else if (rfbScreen->inetdDisconnectHook && !cl)
+ rfbScreen->inetdDisconnectHook();
+ return 0;
+ }
+
+ len = sizeof(peer);
+
+ /* TODO: this thread wont die by restarting the server */
+ while ((client_fd = accept(rfbScreen->rfbListenSock,
+ (struct sockaddr*)&peer, &len)) >= 0) {
+ cl = rfbNewClient(rfbScreen,client_fd);
+ len = sizeof(peer);
+
+ if (cl && !cl->onHold )
+ rfbStartOnHoldClient(cl);
+ }
+ return NULL;
+}
+
+void
+rfbStartOnHoldClient(rfbClientPtr cl)
+{
+ pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
+}
+
+#else
+
+void
+rfbStartOnHoldClient(rfbClientPtr cl)
+{
+ cl->onHold = FALSE;
+}
+
+#endif
+
+void
+rfbRefuseOnHoldClient(rfbClientPtr cl)
+{
+ rfbCloseClient(cl);
+ rfbClientConnectionGone(cl);
+}
+
+static void
+defaultKbdAddEvent(Bool down, KeySym keySym, rfbClientPtr cl)
+{
+}
+
+void
+defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
+{
+ if(x!=cl->screen->cursorX || y!=cl->screen->cursorY) {
+ cl->cursorWasMoved = TRUE;
+ if(cl->screen->cursorIsDrawn)
+ rfbUndrawCursor(cl->screen);
+ LOCK(cl->screen->cursorMutex);
+ if(!cl->screen->cursorIsDrawn) {
+ cl->screen->cursorX = x;
+ cl->screen->cursorY = y;
+ }
+ UNLOCK(cl->screen->cursorMutex);
+ }
+}
+
+void defaultSetXCutText(char* text, int len, rfbClientPtr cl)
+{
+}
+
+/* TODO: add a nice VNC or RFB cursor */
+
+#if defined(WIN32) || defined(sparc) || defined(_AIX) || defined(__osf__)
+static rfbCursor myCursor =
+{
+ "\000\102\044\030\044\102\000",
+ "\347\347\176\074\176\347\347",
+ 8, 7, 3, 3,
+ 0, 0, 0,
+ 0xffff, 0xffff, 0xffff,
+ 0
+};
+#else
+static rfbCursor myCursor =
+{
+ source: "\000\102\044\030\044\102\000",
+ mask: "\347\347\176\074\176\347\347",
+ width: 8, height: 7, xhot: 3, yhot: 3,
+ /*
+ width: 8, height: 7, xhot: 0, yhot: 0,
+ source: "\000\074\176\146\176\074\000",
+ mask: "\176\377\377\377\377\377\176",
+ */
+ foreRed: 0, foreGreen: 0, foreBlue: 0,
+ backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
+ richSource: 0
+};
+#endif
+
+rfbCursorPtr defaultGetCursorPtr(rfbClientPtr cl)
+{
+ return(cl->screen->cursor);
+}
+
+/* response is cl->authChallenge vncEncrypted with passwd */
+Bool defaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
+{
+ int i;
+ char *passwd=vncDecryptPasswdFromFile(cl->screen->rfbAuthPasswdData);
+
+ if(!passwd) {
+ rfbLog("Couldn't read password file: %s\n",cl->screen->rfbAuthPasswdData);
+ return(FALSE);
+ }
+
+ vncEncryptBytes(cl->authChallenge, passwd);
+
+ /* Lose the password from memory */
+ for (i = strlen(passwd); i >= 0; i--) {
+ passwd[i] = '\0';
+ }
+
+ free(passwd);
+
+ if (memcmp(cl->authChallenge, response, len) != 0) {
+ rfbLog("rfbAuthProcessClientMessage: authentication failed from %s\n",
+ cl->host);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+/* for this method, rfbAuthPasswdData is really a pointer to an array
+ of char*'s, where the last pointer is 0. */
+Bool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len)
+{
+ char **passwds;
+
+ for(passwds=(char**)cl->screen->rfbAuthPasswdData;*passwds;passwds++) {
+ vncEncryptBytes(cl->authChallenge, *passwds);
+
+ if (memcmp(cl->authChallenge, response, len) == 0)
+ return(TRUE);
+ }
+
+ rfbLog("rfbAuthProcessClientMessage: authentication failed from %s\n",
+ cl->host);
+ return(FALSE);
+}
+
+void doNothingWithClient(rfbClientPtr cl)
+{
+}
+
+enum rfbNewClientAction defaultNewClientHook(rfbClientPtr cl)
+{
+ return RFB_CLIENT_ACCEPT;
+}
+
+rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
+ int width,int height,int bitsPerSample,int samplesPerPixel,
+ int bytesPerPixel)
+{
+ rfbScreenInfoPtr rfbScreen=malloc(sizeof(rfbScreenInfo));
+ rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
+
+ INIT_MUTEX(logMutex);
+
+ if(width&3)
+ fprintf(stderr,"WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);
+
+ rfbScreen->autoPort=FALSE;
+ rfbScreen->rfbClientHead=0;
+ rfbScreen->rfbPort=5900;
+ rfbScreen->socketInitDone=FALSE;
+
+ rfbScreen->inetdInitDone = FALSE;
+ rfbScreen->inetdSock=-1;
+
+ rfbScreen->udpSock=-1;
+ rfbScreen->udpSockConnected=FALSE;
+ rfbScreen->udpPort=0;
+ rfbScreen->udpClient=0;
+
+ rfbScreen->maxFd=0;
+ rfbScreen->rfbListenSock=-1;
+
+ rfbScreen->httpInitDone=FALSE;
+ rfbScreen->httpPort=0;
+ rfbScreen->httpDir=NULL;
+ rfbScreen->httpListenSock=-1;
+ rfbScreen->httpSock=-1;
+ rfbScreen->httpFP=NULL;
+
+ rfbScreen->desktopName = "LibVNCServer";
+ rfbScreen->rfbAlwaysShared = FALSE;
+ rfbScreen->rfbNeverShared = FALSE;
+ rfbScreen->rfbDontDisconnect = FALSE;
+ rfbScreen->rfbAuthPasswdData = 0;
+
+ rfbScreen->width = width;
+ rfbScreen->height = height;
+ rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel;
+
+ rfbScreen->passwordCheck = defaultPasswordCheck;
+
+ rfbProcessArguments(rfbScreen,argc,argv);
+
+#ifdef WIN32
+ {
+ DWORD dummy=255;
+ GetComputerName(rfbScreen->rfbThisHost,&dummy);
+ }
+#else
+ gethostname(rfbScreen->rfbThisHost, 255);
+#endif
+
+ rfbScreen->paddedWidthInBytes = width*bytesPerPixel;
+
+ /* format */
+
+ format->bitsPerPixel = rfbScreen->bitsPerPixel;
+ format->depth = rfbScreen->depth;
+ format->bigEndian = rfbEndianTest?FALSE:TRUE;
+ format->trueColour = TRUE;
+ rfbScreen->colourMap.count = 0;
+ rfbScreen->colourMap.is16 = 0;
+ rfbScreen->colourMap.data.bytes = NULL;
+
+ if(bytesPerPixel == 1) {
+ format->redMax = 7;
+ format->greenMax = 7;
+ format->blueMax = 3;
+ format->redShift = 0;
+ format->greenShift = 3;
+ format->blueShift = 6;
+ } else {
+ format->redMax = (1 << bitsPerSample) - 1;
+ format->greenMax = (1 << bitsPerSample) - 1;
+ format->blueMax = (1 << bitsPerSample) - 1;
+ if(rfbEndianTest) {
+ format->redShift = 0;
+ format->greenShift = bitsPerSample;
+ format->blueShift = bitsPerSample * 2;
+ } else {
+ if(bytesPerPixel==3) {
+ format->redShift = bitsPerSample*2;
+ format->greenShift = bitsPerSample*1;
+ format->blueShift = 0;
+ } else {
+ format->redShift = bitsPerSample*3;
+ format->greenShift = bitsPerSample*2;
+ format->blueShift = bitsPerSample;
+ }
+ }
+ }
+
+ /* cursor */
+
+ rfbScreen->cursorIsDrawn = FALSE;
+ rfbScreen->dontSendFramebufferUpdate = FALSE;
+ rfbScreen->cursorX=rfbScreen->cursorY=rfbScreen->underCursorBufferLen=0;
+ rfbScreen->underCursorBuffer=NULL;
+ rfbScreen->dontConvertRichCursorToXCursor = FALSE;
+ rfbScreen->cursor = &myCursor;
+ INIT_MUTEX(rfbScreen->cursorMutex);
+
+ IF_PTHREADS(rfbScreen->backgroundLoop = FALSE);
+
+ rfbScreen->rfbDeferUpdateTime=5;
+
+ /* proc's and hook's */
+
+ rfbScreen->kbdAddEvent = defaultKbdAddEvent;
+ rfbScreen->kbdReleaseAllKeys = doNothingWithClient;
+ rfbScreen->ptrAddEvent = defaultPtrAddEvent;
+ rfbScreen->setXCutText = defaultSetXCutText;
+ rfbScreen->getCursorPtr = defaultGetCursorPtr;
+ rfbScreen->setTranslateFunction = rfbSetTranslateFunction;
+ rfbScreen->newClientHook = defaultNewClientHook;
+ rfbScreen->displayHook = 0;
+ rfbScreen->inetdDisconnectHook = 0;
+
+ /* initialize client list and iterator mutex */
+ rfbClientListInit(rfbScreen);
+
+ return(rfbScreen);
+}
+
+void rfbScreenCleanup(rfbScreenInfoPtr rfbScreen)
+{
+ rfbClientIteratorPtr i=rfbGetClientIterator(rfbScreen);
+ rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
+ while(cl1) {
+ cl=rfbClientIteratorNext(i);
+ rfbClientConnectionGone(cl1);
+ cl1=cl;
+ }
+ rfbReleaseClientIterator(i);
+
+ /* TODO: hang up on all clients and free all reserved memory */
+#define FREE_IF(x) if(rfbScreen->x) free(rfbScreen->x)
+ FREE_IF(colourMap.data.bytes);
+ FREE_IF(underCursorBuffer);
+ TINI_MUTEX(rfbScreen->cursorMutex);
+ free(rfbScreen);
+}
+
+void rfbInitServer(rfbScreenInfoPtr rfbScreen)
+{
+#ifdef WIN32
+ WSADATA trash;
+ int i=WSAStartup(MAKEWORD(2,2),&trash);
+#endif
+ rfbInitSockets(rfbScreen);
+ httpInitSockets(rfbScreen);
+}
+
+#ifdef WIN32
+#include <fcntl.h>
+#include <conio.h>
+#include <sys/timeb.h>
+
+void gettimeofday(struct timeval* tv,char* dummy)
+{
+ SYSTEMTIME t;
+ GetSystemTime(&t);
+ tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
+ tv->tv_usec=t.wMilliseconds*1000;
+}
+#endif
+
+void
+rfbProcessEvents(rfbScreenInfoPtr rfbScreen,long usec)
+{
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl,clPrev;
+ struct timeval tv;
+
+ if(usec<0)
+ usec=rfbScreen->rfbDeferUpdateTime*1000;
+
+ rfbCheckFds(rfbScreen,usec);
+ httpCheckFds(rfbScreen);
+#ifdef CORBA
+ corbaCheckFds(rfbScreen);
+#endif
+
+ i = rfbGetClientIterator(rfbScreen);
+ cl=rfbClientIteratorNext(i);
+ while(cl) {
+ if(cl->sock>=0 && (!cl->onHold) && FB_UPDATE_PENDING(cl)) {
+ if(cl->screen->rfbDeferUpdateTime == 0) {
+ rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
+ } else if(cl->startDeferring.tv_usec == 0) {
+ gettimeofday(&cl->startDeferring,NULL);
+ if(cl->startDeferring.tv_usec == 0)
+ cl->startDeferring.tv_usec++;
+ } else {
+ gettimeofday(&tv,NULL);
+ if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
+ || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
+ +(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
+ > cl->screen->rfbDeferUpdateTime) {
+ cl->startDeferring.tv_usec = 0;
+ rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
+ }
+ }
+ }
+ clPrev=cl;
+ cl=rfbClientIteratorNext(i);
+ if(clPrev->sock==-1)
+ rfbClientConnectionGone(clPrev);
+ }
+ rfbReleaseClientIterator(i);
+}
+
+void rfbRunEventLoop(rfbScreenInfoPtr rfbScreen, long usec, Bool runInBackground)
+{
+ if(runInBackground) {
+#ifdef HAVE_PTHREADS
+ pthread_t listener_thread;
+
+ rfbScreen->backgroundLoop = TRUE;
+
+ pthread_create(&listener_thread, NULL, listenerRun, rfbScreen);
+ return;
+#else
+ fprintf(stderr,"Can't run in background, because I don't have PThreads!\n");
+ exit(-1);
+#endif
+ }
+
+ if(usec<0)
+ usec=rfbScreen->rfbDeferUpdateTime*1000;
+
+ while(1)
+ rfbProcessEvents(rfbScreen,usec);
+}
diff --git a/krfb/libvncserver/pnmshow.c b/krfb/libvncserver/pnmshow.c
new file mode 100644
index 00000000..05e45b9c
--- /dev/null
+++ b/krfb/libvncserver/pnmshow.c
@@ -0,0 +1,81 @@
+#include <stdio.h>
+#include "rfb.h"
+#include "keysym.h"
+
+void HandleKey(Bool down,KeySym key,rfbClientPtr cl)
+{
+ if(down && (key==XK_Escape || key=='q' || key=='Q'))
+ rfbCloseClient(cl);
+}
+
+int main(int argc,char** argv)
+{
+ FILE* in=stdin;
+ int i,j,k,width,height,paddedWidth;
+ unsigned char buffer[1024];
+ rfbScreenInfoPtr rfbScreen;
+
+ if(argc>1) {
+ in=fopen(argv[1],"rb");
+ if(!in) {
+ printf("Couldn't find file %s.\n",argv[1]);
+ exit(1);
+ }
+ }
+
+ fgets(buffer,1024,in);
+ if(strncmp(buffer,"P6",2)) {
+ printf("Not a ppm.\n");
+ exit(2);
+ }
+
+ /* skip comments */
+ do {
+ fgets(buffer,1024,in);
+ } while(buffer[0]=='#');
+
+ /* get width & height */
+ sscanf(buffer,"%d %d",&width,&height);
+ fprintf(stderr,"Got width %d and height %d.\n",width,height);
+ fgets(buffer,1024,in);
+
+ /* vncviewers have problems with widths which are no multiple of 4. */
+ paddedWidth = width;
+ if(width&3)
+ paddedWidth+=4-(width&3);
+
+ /* initialize data for vnc server */
+ rfbScreen = rfbGetScreen(&argc,argv,paddedWidth,height,8,3,4);
+ if(argc>1)
+ rfbScreen->desktopName = argv[1];
+ else
+ rfbScreen->desktopName = "Picture";
+ rfbScreen->rfbAlwaysShared = TRUE;
+ rfbScreen->kbdAddEvent = HandleKey;
+
+ /* enable http */
+ rfbScreen->httpDir = "./classes";
+
+ /* allocate picture and read it */
+ rfbScreen->frameBuffer = (char*)malloc(paddedWidth*4*height);
+ fread(rfbScreen->frameBuffer,width*3,height,in);
+ fclose(in);
+
+ /* correct the format to 4 bytes instead of 3 (and pad to paddedWidth) */
+ for(j=height-1;j>=0;j--) {
+ for(i=width-1;i>=0;i--)
+ for(k=2;k>=0;k--)
+ rfbScreen->frameBuffer[(j*paddedWidth+i)*4+k]=
+ rfbScreen->frameBuffer[(j*width+i)*3+k];
+ for(i=width*4;i<paddedWidth*4;i++)
+ rfbScreen->frameBuffer[j*paddedWidth*4+i]=0;
+ }
+
+ /* initialize server */
+ rfbInitServer(rfbScreen);
+
+ /* run event loop */
+ rfbRunEventLoop(rfbScreen,40000,FALSE);
+
+ return(0);
+}
diff --git a/krfb/libvncserver/pnmshow24.c b/krfb/libvncserver/pnmshow24.c
new file mode 100644
index 00000000..3978f00e
--- /dev/null
+++ b/krfb/libvncserver/pnmshow24.c
@@ -0,0 +1,90 @@
+#ifndef ALLOW24BPP
+#error "I need the ALLOW24BPP flag to work"
+#endif
+
+#include <stdio.h>
+#include "rfb.h"
+#include "keysym.h"
+
+void HandleKey(Bool down,KeySym key,rfbClientPtr cl)
+{
+ if(down && (key==XK_Escape || key=='q' || key=='Q'))
+ rfbCloseClient(cl);
+}
+
+int main(int argc,char** argv)
+{
+ FILE* in=stdin;
+ int j,width,height,paddedWidth;
+ unsigned char buffer[1024];
+ rfbScreenInfoPtr rfbScreen;
+
+ if(argc>1) {
+ in=fopen(argv[1],"rb");
+ if(!in) {
+ printf("Couldn't find file %s.\n",argv[1]);
+ exit(1);
+ }
+ }
+
+ fgets(buffer,1024,in);
+ if(strncmp(buffer,"P6",2)) {
+ printf("Not a ppm.\n");
+ exit(2);
+ }
+
+ /* skip comments */
+ do {
+ fgets(buffer,1024,in);
+ } while(buffer[0]=='#');
+
+ /* get width & height */
+ sscanf(buffer,"%d %d",&width,&height);
+ fprintf(stderr,"Got width %d and height %d.\n",width,height);
+ fgets(buffer,1024,in);
+
+ /* vncviewers have problems with widths which are no multiple of 4. */
+ paddedWidth = width;
+
+ /* if your vncviewer doesn't have problems with a width
+ which is not a multiple of 4, you can comment this. */
+ if(width&3)
+ paddedWidth+=4-(width&3);
+
+ /* initialize data for vnc server */
+ rfbScreen = rfbGetScreen(&argc,argv,paddedWidth,height,8,3,3);
+ if(argc>1)
+ rfbScreen->desktopName = argv[1];
+ else
+ rfbScreen->desktopName = "Picture";
+ rfbScreen->rfbAlwaysShared = TRUE;
+ rfbScreen->kbdAddEvent = HandleKey;
+
+ /* enable http */
+ rfbScreen->httpDir = "./classes";
+
+ /* allocate picture and read it */
+ rfbScreen->frameBuffer = (char*)malloc(paddedWidth*3*height);
+ fread(rfbScreen->frameBuffer,width*3,height,in);
+ fclose(in);
+
+ /* pad to paddedWidth */
+ if(width != paddedWidth) {
+ int padCount = 3*(paddedWidth - width);
+ for(j=height-1;j>=0;j--) {
+ memmove(rfbScreen->frameBuffer+3*paddedWidth*j,
+ rfbScreen->frameBuffer+3*width*j,
+ 3*width);
+ memset(rfbScreen->frameBuffer+3*paddedWidth*(j+1)-padCount,
+ 0,padCount);
+ }
+ }
+
+ /* initialize server */
+ rfbInitServer(rfbScreen);
+
+ /* run event loop */
+ rfbRunEventLoop(rfbScreen,40000,FALSE);
+
+ return(0);
+}
diff --git a/krfb/libvncserver/radon.h b/krfb/libvncserver/radon.h
new file mode 100644
index 00000000..6aa5242f
--- /dev/null
+++ b/krfb/libvncserver/radon.h
@@ -0,0 +1,195 @@
+unsigned char radonFontData[2280]={
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 32 */
+0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x10,0x10,0x00,0x00, /* 33 */
+0x00,0x28,0x28,0x28,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 34 */
+0x00,0x44,0x44,0xba,0x44,0x44,0x44,0xba,0x44,0x44,0x00,0x00, /* 35 */
+0x10,0x7e,0x80,0x90,0x80,0x7c,0x02,0x12,0x02,0xfc,0x10,0x00, /* 36 */
+0x00,0x62,0x92,0x94,0x68,0x10,0x2c,0x52,0x92,0x8c,0x00,0x00, /* 37 */
+0x00,0x60,0x90,0x90,0x40,0x20,0x90,0x8a,0x84,0x7a,0x00,0x00, /* 38 */
+0x00,0x10,0x10,0x10,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 39 */
+0x00,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x00,0x00, /* 40 */
+0x00,0x10,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10,0x00,0x00, /* 41 */
+0x00,0x10,0x92,0x54,0x10,0x10,0x54,0x92,0x10,0x00,0x00,0x00, /* 42 */
+0x00,0x00,0x10,0x10,0x10,0xd6,0x10,0x10,0x10,0x00,0x00,0x00, /* 43 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x30,0x00, /* 44 */
+0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, /* 45 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x10,0x00,0x00, /* 46 */
+0x00,0x02,0x02,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x00,0x00, /* 47 */
+0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x82,0x7c,0x00,0x00, /* 48 */
+0x00,0x08,0x28,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00, /* 49 */
+0x00,0xfc,0x02,0x02,0x02,0x7c,0x80,0x80,0x00,0xfe,0x00,0x00, /* 50 */
+0x00,0xfc,0x02,0x02,0x02,0x3c,0x02,0x02,0x02,0xfc,0x00,0x00, /* 51 */
+0x00,0x82,0x82,0x82,0x82,0x7a,0x02,0x02,0x02,0x02,0x00,0x00, /* 52 */
+0x00,0xfe,0x00,0x80,0x80,0x7c,0x02,0x02,0x02,0xfc,0x00,0x00, /* 53 */
+0x00,0x7c,0x80,0x80,0xbc,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 54 */
+0x00,0xfc,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x00, /* 55 */
+0x00,0x7c,0x82,0x82,0x82,0x7c,0x82,0x82,0x82,0x7c,0x00,0x00, /* 56 */
+0x00,0x7c,0x82,0x82,0x82,0x82,0x7a,0x02,0x02,0xfc,0x00,0x00, /* 57 */
+0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x10,0x10,0x00,0x00,0x00, /* 58 */
+0x00,0x00,0x10,0x10,0x00,0x00,0x00,0x10,0x10,0x60,0x00,0x00, /* 59 */
+0x00,0x08,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x08,0x00,0x00, /* 60 */
+0x00,0x00,0x00,0x00,0xfe,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, /* 61 */
+0x00,0x10,0x10,0x08,0x04,0x02,0x04,0x08,0x10,0x10,0x00,0x00, /* 62 */
+0x00,0xfc,0x02,0x02,0x02,0x1c,0x20,0x20,0x00,0x20,0x00,0x00, /* 63 */
+0x00,0x7c,0x82,0x8a,0x92,0x92,0x92,0x8c,0x80,0x7c,0x00,0x00, /* 64 */
+0x00,0x7c,0x82,0x82,0x82,0x82,0xba,0x82,0x82,0x82,0x00,0x00, /* 65 */
+0x00,0xbc,0x82,0x82,0x82,0xbc,0x82,0x82,0x82,0xbc,0x00,0x00, /* 66 */
+0x00,0x7c,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7c,0x00,0x00, /* 67 */
+0x00,0xbc,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0xbc,0x00,0x00, /* 68 */
+0x00,0x7c,0x80,0x80,0x80,0xb8,0x80,0x80,0x80,0x7c,0x00,0x00, /* 69 */
+0x00,0x7c,0x80,0x80,0x80,0xb8,0x80,0x80,0x80,0x80,0x00,0x00, /* 70 */
+0x00,0x7c,0x80,0x80,0x80,0x80,0x9a,0x82,0x82,0x7c,0x00,0x00, /* 71 */
+0x00,0x82,0x82,0x82,0x82,0xba,0x82,0x82,0x82,0x82,0x00,0x00, /* 72 */
+0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 73 */
+0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x84,0x78,0x00,0x00, /* 74 */
+0x00,0x82,0x82,0x82,0x82,0xbc,0x82,0x82,0x82,0x82,0x00,0x00, /* 75 */
+0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7e,0x00,0x00, /* 76 */
+0x00,0x7c,0x82,0x92,0x92,0x92,0x92,0x82,0x82,0x82,0x00,0x00, /* 77 */
+0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x00,0x00, /* 78 */
+0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 79 */
+0x00,0xbc,0x82,0x82,0x82,0xbc,0x80,0x80,0x80,0x80,0x00,0x00, /* 80 */
+0x00,0x7c,0x82,0x82,0x82,0x82,0x8a,0x8a,0x82,0x7c,0x00,0x00, /* 81 */
+0x00,0xbc,0x82,0x82,0x82,0xbc,0x82,0x82,0x82,0x82,0x00,0x00, /* 82 */
+0x00,0x7e,0x80,0x80,0x80,0x7c,0x02,0x02,0x02,0xfc,0x00,0x00, /* 83 */
+0x00,0xfe,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 84 */
+0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 85 */
+0x00,0x82,0x82,0x82,0x82,0x82,0x84,0x88,0x90,0xa0,0x00,0x00, /* 86 */
+0x00,0x82,0x82,0x82,0x82,0x92,0x92,0x92,0x82,0x7c,0x00,0x00, /* 87 */
+0x00,0x82,0x82,0x82,0x82,0x7c,0x82,0x82,0x82,0x82,0x00,0x00, /* 88 */
+0x00,0x82,0x82,0x82,0x82,0x7c,0x00,0x10,0x10,0x10,0x00,0x00, /* 89 */
+0x00,0xfc,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x7e,0x00,0x00, /* 90 */
+0x00,0x1c,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x1c,0x00,0x00, /* 91 */
+0x00,0x80,0x80,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00,0x00, /* 92 */
+0x00,0x38,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x38,0x00,0x00, /* 93 */
+0x00,0x38,0x44,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 94 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00, /* 95 */
+0x00,0x08,0x08,0x08,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 96 */
+0x00,0x00,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 97 */
+0x00,0x00,0x40,0x40,0x5c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 98 */
+0x00,0x00,0x00,0x00,0x3c,0x40,0x40,0x40,0x40,0x3c,0x00,0x00, /* 99 */
+0x00,0x00,0x02,0x02,0x3a,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 100 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x5c,0x40,0x3c,0x00,0x00, /* 101 */
+0x00,0x00,0x0c,0x10,0x10,0x10,0x54,0x10,0x10,0x10,0x00,0x00, /* 102 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3a,0x02,0x3c, /* 103 */
+0x00,0x00,0x40,0x40,0x5c,0x42,0x42,0x42,0x42,0x42,0x00,0x00, /* 104 */
+0x00,0x00,0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00, /* 105 */
+0x00,0x00,0x08,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30, /* 106 */
+0x00,0x00,0x40,0x40,0x42,0x42,0x5c,0x42,0x42,0x42,0x00,0x00, /* 107 */
+0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00, /* 108 */
+0x00,0x00,0x00,0x00,0x7c,0x82,0x92,0x92,0x92,0x92,0x00,0x00, /* 109 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x00,0x00, /* 110 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 111 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x5c,0x40,0x40, /* 112 */
+0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3a,0x02,0x02, /* 113 */
+0x00,0x00,0x00,0x00,0x0c,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 114 */
+0x00,0x00,0x00,0x00,0x3e,0x40,0x3c,0x02,0x02,0x7c,0x00,0x00, /* 115 */
+0x00,0x00,0x10,0x10,0x10,0x54,0x10,0x10,0x10,0x0c,0x00,0x00, /* 116 */
+0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 117 */
+0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x44,0x48,0x50,0x00,0x00, /* 118 */
+0x00,0x00,0x00,0x00,0x92,0x92,0x92,0x92,0x82,0x7c,0x00,0x00, /* 119 */
+0x00,0x00,0x00,0x00,0x42,0x42,0x3c,0x42,0x42,0x42,0x00,0x00, /* 120 */
+0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3a,0x02,0x3c, /* 121 */
+0x00,0x00,0x00,0x00,0x7c,0x02,0x0c,0x30,0x40,0x3e,0x00,0x00, /* 122 */
+0x00,0x1c,0x20,0x20,0x20,0x40,0x20,0x20,0x20,0x1c,0x00,0x00, /* 123 */
+0x00,0x10,0x10,0x10,0x10,0x00,0x10,0x10,0x10,0x10,0x00,0x00, /* 124 */
+0x00,0x38,0x04,0x04,0x04,0x02,0x04,0x04,0x04,0x38,0x00,0x00, /* 125 */
+0x00,0x04,0x38,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 126 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160 */
+0x00,0x10,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 161 */
+0x00,0x00,0x08,0x3e,0x40,0x48,0x48,0x40,0x3e,0x08,0x00,0x00, /* 162 */
+0x00,0x1c,0x20,0x20,0x20,0xa8,0x20,0x20,0x42,0xbc,0x00,0x00, /* 163 */
+0x00,0x00,0x82,0x38,0x44,0x44,0x44,0x38,0x82,0x00,0x00,0x00, /* 164 */
+0x00,0x82,0x82,0x82,0x7c,0x00,0x54,0x10,0x54,0x10,0x00,0x00, /* 165 */
+0x00,0x10,0x10,0x10,0x00,0x00,0x00,0x10,0x10,0x10,0x00,0x00, /* 166 */
+0x00,0x38,0x40,0x38,0x44,0x44,0x44,0x44,0x38,0x04,0x38,0x00, /* 167 */
+0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168 */
+0x00,0x7c,0x82,0x9a,0xa2,0xa2,0xa2,0x9a,0x82,0x7c,0x00,0x00, /* 169 */
+0x38,0x04,0x34,0x44,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00, /* 170 */
+0x00,0x00,0x00,0x24,0x48,0x00,0x48,0x24,0x00,0x00,0x00,0x00, /* 171 */
+0x00,0x00,0x00,0x00,0x00,0xfc,0x02,0x02,0x02,0x00,0x00,0x00, /* 172 */
+0x00,0x00,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00, /* 173 */
+0x00,0x7c,0x82,0x92,0xaa,0xb2,0xaa,0xaa,0x82,0x7c,0x00,0x00, /* 174 */
+0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 175 */
+0x38,0x44,0x44,0x44,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176 */
+0x00,0x10,0x10,0xd6,0x10,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, /* 177 */
+0x38,0x04,0x18,0x20,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 178 */
+0x38,0x04,0x38,0x04,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 179 */
+0x18,0x20,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 180 */
+0x00,0x00,0x00,0x00,0x44,0x44,0x44,0x44,0x44,0x58,0x40,0x40, /* 181 */
+0x00,0x79,0xfa,0xfa,0xfa,0x7a,0x02,0x0a,0x0a,0x0a,0x0a,0x00, /* 182 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00, /* 183 */
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x10,0x00, /* 184 */
+0x08,0x18,0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 185 */
+0x38,0x44,0x44,0x38,0x00,0x7c,0x00,0x00,0x00,0x00,0x00,0x00, /* 186 */
+0x00,0x00,0x00,0x48,0x24,0x00,0x24,0x48,0x00,0x00,0x00,0x00, /* 187 */
+0x20,0xa2,0x22,0x22,0x24,0x08,0x10,0x29,0x49,0x85,0x01,0x01, /* 188 */
+0x20,0xa2,0x22,0x22,0x24,0x08,0x10,0x2e,0x41,0x86,0x08,0x0f, /* 189 */
+0xe0,0x12,0xe2,0x12,0xe4,0x08,0x10,0x29,0x49,0x85,0x01,0x01, /* 190 */
+0x00,0x08,0x00,0x08,0x08,0x70,0x80,0x80,0x80,0x7e,0x00,0x00, /* 191 */
+0x20,0x18,0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 192 */
+0x08,0x30,0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 193 */
+0x38,0x44,0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 194 */
+0x32,0x4c,0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 195 */
+0x6c,0x00,0x00,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 196 */
+0x38,0x44,0x38,0x7c,0x82,0x82,0x82,0xba,0x82,0x82,0x00,0x00, /* 197 */
+0x00,0x77,0x88,0x88,0x88,0x8b,0xa8,0x88,0x88,0x8b,0x00,0x00, /* 198 */
+0x00,0x7c,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x6c,0x10,0x20, /* 199 */
+0x20,0x18,0x00,0x7c,0x80,0x80,0xb8,0x80,0x80,0x7c,0x00,0x00, /* 200 */
+0x08,0x30,0x00,0x7c,0x80,0x80,0xb8,0x80,0x80,0x7c,0x00,0x00, /* 201 */
+0x38,0x44,0x00,0x7c,0x80,0x80,0xb8,0x80,0x80,0x7c,0x00,0x00, /* 202 */
+0x6c,0x00,0x00,0x7c,0x80,0x80,0xb8,0x80,0x80,0x7c,0x00,0x00, /* 203 */
+0x20,0x18,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 204 */
+0x08,0x30,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 205 */
+0x38,0x44,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 206 */
+0x6c,0x00,0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 207 */
+0x00,0xbc,0x82,0x82,0x82,0xb2,0x82,0x82,0x82,0xbc,0x00,0x00, /* 208 */
+0x32,0x4c,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x82,0x00,0x00, /* 209 */
+0x20,0x18,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 210 */
+0x08,0x30,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 211 */
+0x38,0x44,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 212 */
+0x32,0x4c,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 213 */
+0x6c,0x00,0x00,0x7c,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 214 */
+0x00,0x00,0x00,0x00,0x44,0x28,0x00,0x28,0x44,0x00,0x00,0x00, /* 215 */
+0x00,0x7a,0x84,0x82,0x8a,0x92,0xa2,0x82,0x42,0xbc,0x00,0x00, /* 216 */
+0x20,0x18,0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 217 */
+0x08,0x30,0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 218 */
+0x38,0x44,0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 219 */
+0x6c,0x00,0x00,0x82,0x82,0x82,0x82,0x82,0x82,0x7c,0x00,0x00, /* 220 */
+0x08,0xb2,0x82,0x82,0x82,0x7c,0x00,0x10,0x10,0x10,0x00,0x00, /* 221 */
+0x00,0x80,0x80,0xbc,0x82,0x82,0x82,0xbc,0x80,0x80,0x00,0x00, /* 222 */
+0x00,0x3c,0x42,0x42,0x42,0x5c,0x42,0x42,0x42,0x9c,0x00,0x00, /* 223 */
+0x20,0x18,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 224 */
+0x08,0x30,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 225 */
+0x38,0x44,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 226 */
+0x32,0x4c,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 227 */
+0x6c,0x00,0x00,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 228 */
+0x18,0x24,0x18,0x00,0x3c,0x02,0x3a,0x42,0x42,0x3c,0x00,0x00, /* 229 */
+0x00,0x00,0x00,0x00,0x6c,0x12,0x52,0x94,0x90,0x6e,0x00,0x00, /* 230 */
+0x00,0x00,0x00,0x00,0x3c,0x40,0x40,0x40,0x40,0x34,0x08,0x10, /* 231 */
+0x20,0x18,0x00,0x00,0x3c,0x42,0x42,0x5c,0x40,0x3c,0x00,0x00, /* 232 */
+0x08,0x30,0x00,0x00,0x3c,0x42,0x42,0x5c,0x40,0x3c,0x00,0x00, /* 233 */
+0x38,0x44,0x00,0x00,0x3c,0x42,0x42,0x5c,0x40,0x3c,0x00,0x00, /* 234 */
+0x6c,0x00,0x00,0x00,0x3c,0x42,0x42,0x5c,0x40,0x3c,0x00,0x00, /* 235 */
+0x20,0x18,0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 236 */
+0x08,0x30,0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 237 */
+0x38,0x44,0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 238 */
+0x6c,0x00,0x00,0x10,0x00,0x10,0x10,0x10,0x10,0x10,0x00,0x00, /* 239 */
+0x00,0x14,0x08,0x14,0x02,0x3a,0x42,0x42,0x42,0x3c,0x00,0x00, /* 240 */
+0x00,0x32,0x4c,0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x00,0x00, /* 241 */
+0x20,0x18,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 242 */
+0x08,0x30,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 243 */
+0x38,0x44,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 244 */
+0x32,0x4c,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 245 */
+0x6c,0x00,0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 246 */
+0x00,0x00,0x00,0x00,0x38,0x00,0xfe,0x00,0x38,0x00,0x00,0x00, /* 247 */
+0x00,0x00,0x00,0x00,0x3a,0x44,0x4a,0x52,0x22,0x5c,0x00,0x00, /* 248 */
+0x20,0x18,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 249 */
+0x08,0x30,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 250 */
+0x38,0x44,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 251 */
+0x6c,0x00,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, /* 252 */
+0x04,0x18,0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x3a,0x02,0x3c, /* 253 */
+0x00,0x80,0x80,0x9c,0xa2,0x82,0xa2,0x9c,0x80,0x80,0x00,0x00, /* 254 */
+};
+int radonFontMetaData[256*5]={
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,12,0,-2,12,8,12,0,-2,24,8,12,0,-2,36,8,12,0,-2,48,8,12,0,-2,60,8,12,0,-2,72,8,12,0,-2,84,8,12,0,-2,96,8,12,0,-2,108,8,12,0,-2,120,8,12,0,-2,132,8,12,0,-2,144,8,12,0,-2,156,8,12,0,-2,168,8,12,0,-2,180,8,12,0,-2,192,8,12,0,-2,204,8,12,0,-2,216,8,12,0,-2,228,8,12,0,-2,240,8,12,0,-2,252,8,12,0,-2,264,8,12,0,-2,276,8,12,0,-2,288,8,12,0,-2,300,8,12,0,-2,312,8,12,0,-2,324,8,12,0,-2,336,8,12,0,-2,348,8,12,0,-2,360,8,12,0,-2,372,8,12,0,-2,384,8,12,0,-2,396,8,12,0,-2,408,8,12,0,-2,420,8,12,0,-2,432,8,12,0,-2,444,8,12,0,-2,456,8,12,0,-2,468,8,12,0,-2,480,8,12,0,-2,492,8,12,0,-2,504,8,12,0,-2,516,8,12,0,-2,528,8,12,0,-2,540,8,12,0,-2,552,8,12,0,-2,564,8,12,0,-2,576,8,12,0,-2,588,8,12,0,-2,600,8,12,0,-2,612,8,12,0,-2,624,8,12,0,-2,636,8,12,0,-2,648,8,12,0,-2,660,8,12,0,-2,672,8,12,0,-2,684,8,12,0,-2,696,8,12,0,-2,708,8,12,0,-2,720,8,12,0,-2,732,8,12,0,-2,744,8,12,0,-2,756,8,12,0,-2,768,8,12,0,-2,780,8,12,0,-2,792,8,12,0,-2,804,8,12,0,-2,816,8,12,0,-2,828,8,12,0,-2,840,8,12,0,-2,852,8,12,0,-2,864,8,12,0,-2,876,8,12,0,-2,888,8,12,0,-2,900,8,12,0,-2,912,8,12,0,-2,924,8,12,0,-2,936,8,12,0,-2,948,8,12,0,-2,960,8,12,0,-2,972,8,12,0,-2,984,8,12,0,-2,996,8,12,0,-2,1008,8,12,0,-2,1020,8,12,0,-2,1032,8,12,0,-2,1044,8,12,0,-2,1056,8,12,0,-2,1068,8,12,0,-2,1080,8,12,0,-2,1092,8,12,0,-2,1104,8,12,0,-2,1116,8,12,0,-2,1128,8,12,0,-2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1140,8,12,0,-2,1152,8,12,0,-2,1164,8,12,0,-2,1176,8,12,0,-2,1188,8,12,0,-2,1200,8,12,0,-2,1212,8,12,0,-2,1224,8,12,0,-2,1236,8,12,0,-2,1248,8,12,0,-2,1260,8,12,0,-2,1272,8,12,0,-2,1284,8,12,0,-2,1296,8,12,0,-2,1308,8,12,0,-2,1320,8,12,0,-2,1332,8,12,0,-2,1344,8,12,0,-2,1356,8,12,0,-2,1368,8,12,0,-2,1380,8,12,0,-2,1392,8,12,0,-2,1404,8,12,0,-2,1416,8,12,0,-2,1428,8,12,0,-2,1440,8,12,0,-2,1452,8,12,0,-2,1464,8,12,0,-2,1476,8,12,0,-2,1488,8,12,0,-2,1500,8,12,0,-2,1512,8,12,0,-2,1524,8,12,0,-2,1536,8,12,0,-2,1548,8,12,0,-2,1560,8,12,0,-2,1572,8,12,0,-2,1584,8,12,0,-2,1596,8,12,0,-2,1608,8,12,0,-2,1620,8,12,0,-2,1632,8,12,0,-2,1644,8,12,0,-2,1656,8,12,0,-2,1668,8,12,0,-2,1680,8,12,0,-2,1692,8,12,0,-2,1704,8,12,0,-2,1716,8,12,0,-2,1728,8,12,0,-2,1740,8,12,0,-2,1752,8,12,0,-2,1764,8,12,0,-2,1776,8,12,0,-2,1788,8,12,0,-2,1800,8,12,0,-2,1812,8,12,0,-2,1824,8,12,0,-2,1836,8,12,0,-2,1848,8,12,0,-2,1860,8,12,0,-2,1872,8,12,0,-2,1884,8,12,0,-2,1896,8,12,0,-2,1908,8,12,0,-2,1920,8,12,0,-2,1932,8,12,0,-2,1944,8,12,0,-2,1956,8,12,0,-2,1968,8,12,0,-2,1980,8,12,0,-2,1992,8,12,0,-2,2004,8,12,0,-2,2016,8,12,0,-2,2028,8,12,0,-2,2040,8,12,0,-2,2052,8,12,0,-2,2064,8,12,0,-2,2076,8,12,0,-2,2088,8,12,0,-2,2100,8,12,0,-2,2112,8,12,0,-2,2124,8,12,0,-2,2136,8,12,0,-2,2148,8,12,0,-2,2160,8,12,0,-2,2172,8,12,0,-2,2184,8,12,0,-2,2196,8,12,0,-2,2208,8,12,0,-2,2220,8,12,0,-2,2232,8,12,0,-2,2244,8,12,0,-2,2256,8,12,0,-2,2268,8,12,0,-2,0,0,0,0,0,};
+rfbFontData radonFont={radonFontData, radonFontMetaData};
diff --git a/krfb/libvncserver/rfb.h b/krfb/libvncserver/rfb.h
new file mode 100644
index 00000000..53996812
--- /dev/null
+++ b/krfb/libvncserver/rfb.h
@@ -0,0 +1,865 @@
+#ifndef RFB_H
+#define RFB_H
+
+/*
+ * rfb.h - header file for RFB DDX implementation.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#if(defined __cplusplus)
+extern "C"
+{
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+#include "keysym.h"
+
+/* TODO: this stuff has to go into autoconf */
+typedef unsigned char CARD8;
+typedef unsigned short CARD16;
+
+#ifdef LONG64
+typedef unsigned long CARD64;
+typedef unsigned int CARD32;
+#else
+typedef unsigned long CARD32;
+#endif
+
+typedef CARD32 Pixel;
+/* typedef CARD32 KeySym; */
+#ifndef __osf__
+typedef unsigned long KeySym;
+#endif
+#define SIGNED signed
+/* for some strange reason, "typedef signed char Bool;" yields a four byte
+ signed int on IRIX, but only for rfbserver.o!!! */
+#ifdef Bool
+#undef Bool
+#endif
+#define Bool signed char
+#undef FALSE
+#define FALSE 0
+#undef TRUE
+#define TRUE -1
+
+#include "rfbproto.h"
+
+#ifdef __linux__
+#include <endian.h>
+#elif defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/types.h>
+#include <machine/endian.h>
+#ifndef _BYTE_ORDER
+#define _BYTE_ORDER BYTE_ORDER
+#endif
+#ifndef _LITTLE_ENDIAN
+#define _LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+#elif defined (__SVR4) && defined (__sun) /* Solaris */
+#include <sys/types.h>
+#if defined(__sparc)
+ /* SPARC here (big endian) */
+#define _BYTE_ORDER 4321
+#elif defined(__i386)
+#define _BYTE_ORDER 1234
+#else
+#error Solaris 2.5.1 had ppc support did it not? :-)
+#endif
+#undef Bool
+#define Bool char
+#undef SIGNED
+#define SIGNED
+#include <sys/types.h>
+/* typedef unsigned int pthread_t; */
+#elif defined(WIN32)
+#define _LITTLE_ENDIAN 1234
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#undef Bool
+#define Bool int
+#elif defined(_AIX)
+#define _BYTE_ORDER 4321
+#undef Bool
+#define Bool int
+#else
+#ifdef __osf__
+#include <machine/endian.h>
+#define _BYTE_ORDER BYTE_ORDER
+#else
+#include <sys/endian.h>
+#endif
+#endif
+
+#ifndef _BYTE_ORDER
+#define _BYTE_ORDER __BYTE_ORDER
+#endif
+
+#if !defined(_LITTLE_ENDIAN) && defined(__LITTLE_ENDIAN)
+#define _LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+
+#ifdef WIN32
+#include <sys/timeb.h>
+#include <winsock.h>
+#undef SOCKET
+#define SOCKET int
+#else
+#define max(a,b) (((a)>(b))?(a):(b))
+#include <sys/time.h>
+#include <netinet/in.h>
+#define SOCKET int
+#endif
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((in_addr_t) 0xffffffff)
+#endif
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#if 0 /* debugging */
+#define LOCK(mutex) fprintf(stderr,"%s:%d LOCK(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex))
+#define UNLOCK(mutex) fprintf(stderr,"%s:%d UNLOCK(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex))
+#define MUTEX(mutex) int mutex
+#define INIT_MUTEX(mutex) fprintf(stderr,"%s:%d INIT_MUTEX(%s,0x%x)\n",__FILE__,__LINE__,#mutex,&(mutex))
+#define TINI_MUTEX(mutex) fprintf(stderr,"%s:%d TINI_MUTEX(%s)\n",__FILE__,__LINE__,#mutex)
+#define SIGNAL(cond) fprintf(stderr,"%s:%d SIGNAL(%s)\n",__FILE__,__LINE__,#cond)
+#define WAIT(cond,mutex) /* fprintf(stderr,"%s:%d WAIT(%s,%s)\n",__FILE__,__LINE__,#cond,#mutex) */
+#define COND(cond)
+#define INIT_COND(cond) fprintf(stderr,"%s:%d INIT_COND(%s)\n",__FILE__,__LINE__,#cond)
+#define TINI_COND(cond) fprintf(stderr,"%s:%d TINI_COND(%s)\n",__FILE__,__LINE__,#cond)
+#define IF_PTHREADS(x)
+#else
+#define LOCK(mutex) pthread_mutex_lock(&(mutex));
+#define UNLOCK(mutex) pthread_mutex_unlock(&(mutex));
+#define MUTEX(mutex) pthread_mutex_t (mutex)
+#define INIT_MUTEX(mutex) pthread_mutex_init(&(mutex),NULL)
+#define TINI_MUTEX(mutex) pthread_mutex_destroy(&(mutex))
+#define TSIGNAL(cond) pthread_cond_signal(&(cond))
+#define WAIT(cond,mutex) pthread_cond_wait(&(cond),&(mutex))
+#define TIMEDWAIT(cond,mutex,t) {struct timeval tv;\
+ tv.tv_sec = (t) / 1000;\
+ tv.tv_usec = ((t) % 1000) * 1000;\
+ pthread_cond_timedwait(&(cond),&(mutex),&tv);}
+#define COND(cond) pthread_cond_t (cond)
+#define INIT_COND(cond) pthread_cond_init(&(cond),NULL)
+#define TINI_COND(cond) pthread_cond_destroy(&(cond))
+#define IF_PTHREADS(x) x
+#endif
+#else
+#define LOCK(mutex)
+#define UNLOCK(mutex)
+#define MUTEX(mutex)
+#define INIT_MUTEX(mutex)
+#define TINI_MUTEX(mutex)
+#define TSIGNAL(cond)
+#define WAIT(cond,mutex) this_is_unsupported
+#define COND(cond)
+#define INIT_COND(cond)
+#define TINI_COND(cond)
+#define IF_PTHREADS(x)
+#endif
+
+/* end of stuff for autoconf */
+
+/* if you use pthreads, but don't define HAVE_PTHREADS, the structs
+ get all mixed up. So this gives a linker error reminding you to compile
+ the library and your application (at least the parts including rfb.h)
+ with the same support for pthreads. */
+#ifdef HAVE_PTHREADS
+#define rfbInitServer rfbInitServerWithPthreads
+#else
+#define rfbInitServer rfbInitServerWithoutPthreads
+#endif
+
+#define MAX_ENCODINGS 10
+
+struct _rfbClientRec;
+struct _rfbScreenInfo;
+struct rfbCursor;
+
+enum rfbNewClientAction {
+ RFB_CLIENT_ACCEPT,
+ RFB_CLIENT_ON_HOLD,
+ RFB_CLIENT_REFUSE
+};
+
+typedef void (*KbdAddEventProcPtr) (Bool down, KeySym keySym, struct _rfbClientRec* cl);
+typedef void (*KbdReleaseAllKeysProcPtr) (struct _rfbClientRec* cl);
+typedef void (*PtrAddEventProcPtr) (int buttonMask, int x, int y, struct _rfbClientRec* cl);
+typedef void (*SetXCutTextProcPtr) (char* str,int len, struct _rfbClientRec* cl);
+typedef struct rfbCursor* (*GetCursorProcPtr) (struct _rfbClientRec* pScreen);
+typedef Bool (*SetTranslateFunctionProcPtr)(struct _rfbClientRec* cl);
+typedef Bool (*PasswordCheckProcPtr)(struct _rfbClientRec* cl,const char* encryptedPassWord,int len);
+typedef enum rfbNewClientAction (*NewClientHookPtr)(struct _rfbClientRec* cl);
+typedef void (*DisplayHookPtr)(struct _rfbClientRec* cl);
+typedef void (*InetdDisconnectPtr)();
+
+typedef struct {
+ CARD32 count;
+ Bool is16; /* is the data format short? */
+ union {
+ CARD8* bytes;
+ CARD16* shorts;
+ } data; /* there have to be count*3 entries */
+} rfbColourMap;
+
+/*
+ * Per-screen (framebuffer) structure. There can be as many as you wish,
+ * each serving different clients. However, you have to call
+ * rfbProcessEvents for each of these.
+ */
+
+typedef struct _rfbScreenInfo
+{
+ int width;
+ int paddedWidthInBytes;
+ int height;
+ int depth;
+ int bitsPerPixel;
+ int sizeInBytes;
+
+ Pixel blackPixel;
+ Pixel whitePixel;
+
+ /* some screen specific data can be put into a struct where screenData
+ * points to. You need this if you have more than one screen at the
+ * same time while using the same functions.
+ */
+ void* screenData;
+
+ /* The following two members are used to minimise the amount of unnecessary
+ drawing caused by cursor movement. Whenever any drawing affects the
+ part of the screen where the cursor is, the cursor is removed first and
+ then the drawing is done (this is what the sprite routines test for).
+ Afterwards, however, we do not replace the cursor, even when the cursor
+ is logically being moved across the screen. We only draw the cursor
+ again just as we are about to send the client a framebuffer update.
+
+ We need to be careful when removing and drawing the cursor because of
+ their relationship with the normal drawing routines. The drawing
+ routines can invoke the cursor routines, but also the cursor routines
+ themselves end up invoking drawing routines.
+
+ Removing the cursor (rfbUndrawCursor) is eventually achieved by
+ doing a CopyArea from a pixmap to the screen, where the pixmap contains
+ the saved contents of the screen under the cursor. Before doing this,
+ however, we set cursorIsDrawn to FALSE. Then, when CopyArea is called,
+ it sees that cursorIsDrawn is FALSE and so doesn't feel the need to
+ (recursively!) remove the cursor before doing it.
+
+ Putting up the cursor (rfbDrawCursor) involves a call to
+ PushPixels. While this is happening, cursorIsDrawn must be FALSE so
+ that PushPixels doesn't think it has to remove the cursor first.
+ Obviously cursorIsDrawn is set to TRUE afterwards.
+
+ Another problem we face is that drawing routines sometimes cause a
+ framebuffer update to be sent to the RFB client. When the RFB client is
+ already waiting for a framebuffer update and some drawing to the
+ framebuffer then happens, the drawing routine sees that the client is
+ ready, so it calls rfbSendFramebufferUpdate. If the cursor is not drawn
+ at this stage, it must be put up, and so rfbSpriteRestoreCursor is
+ called. However, if the original drawing routine was actually called
+ from within rfbSpriteRestoreCursor or rfbSpriteRemoveCursor we don't
+ want this to happen. So both the cursor routines set
+ dontSendFramebufferUpdate to TRUE, and all the drawing routines check
+ this before calling rfbSendFramebufferUpdate. */
+
+ Bool cursorIsDrawn; /* TRUE if the cursor is currently drawn */
+ Bool dontSendFramebufferUpdate; /* TRUE while removing or drawing the
+ cursor */
+
+ /* additions by libvncserver */
+
+ rfbPixelFormat rfbServerFormat;
+ rfbColourMap colourMap; /* set this if rfbServerFormat.trueColour==FALSE */
+ const char* desktopName;
+ char rfbThisHost[255];
+
+ Bool autoPort;
+ int rfbPort;
+ SOCKET rfbListenSock;
+ int maxSock;
+ int maxFd;
+ fd_set allFds;
+
+ Bool socketInitDone;
+ SOCKET inetdSock;
+ Bool inetdInitDone;
+
+ int udpPort;
+ SOCKET udpSock;
+ struct _rfbClientRec* udpClient;
+ Bool udpSockConnected;
+ struct sockaddr_in udpRemoteAddr;
+
+ int rfbMaxClientWait;
+
+ /* http stuff */
+ Bool httpInitDone;
+ int httpPort;
+ char* httpDir;
+ SOCKET httpListenSock;
+ SOCKET httpSock;
+ FILE* httpFP;
+
+ PasswordCheckProcPtr passwordCheck;
+ void* rfbAuthPasswdData;
+
+ /* this is the amount of milliseconds to wait at least before sending
+ * an update. */
+ int rfbDeferUpdateTime;
+ char* rfbScreen;
+ Bool rfbAlwaysShared;
+ Bool rfbNeverShared;
+ Bool rfbDontDisconnect;
+ struct _rfbClientRec* rfbClientHead;
+
+ /* cursor */
+ int cursorX, cursorY,underCursorBufferLen;
+ char* underCursorBuffer;
+ Bool dontConvertRichCursorToXCursor;
+ struct rfbCursor* cursor;
+
+ /* the frameBufferhas to be supplied by the serving process.
+ * The buffer will not be freed by
+ */
+ char* frameBuffer;
+ KbdAddEventProcPtr kbdAddEvent;
+ KbdReleaseAllKeysProcPtr kbdReleaseAllKeys;
+ PtrAddEventProcPtr ptrAddEvent;
+ SetXCutTextProcPtr setXCutText;
+ GetCursorProcPtr getCursorPtr;
+ SetTranslateFunctionProcPtr setTranslateFunction;
+
+ /* newClientHook is called just after a new client is created */
+ NewClientHookPtr newClientHook;
+ /* displayHook is called just before a frame buffer update */
+ DisplayHookPtr displayHook;
+ /* inetdDisconnectHook is called when the connection has been
+ interrupted before a client could connect. */
+ InetdDisconnectPtr inetdDisconnectHook;
+#ifdef HAVE_PTHREADS
+ MUTEX(cursorMutex);
+ Bool backgroundLoop;
+#endif
+
+} rfbScreenInfo, *rfbScreenInfoPtr;
+
+
+/*
+ * rfbTranslateFnType is the type of translation functions.
+ */
+
+typedef void (*rfbTranslateFnType)(char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height);
+
+
+/*
+ * vncauth.h - describes the functions provided by the vncauth library.
+ */
+
+#define MAXPWLEN 8
+#define CHALLENGESIZE 16
+
+extern int vncEncryptAndStorePasswd(char *passwd, char *fname);
+extern char *vncDecryptPasswdFromFile(char *fname);
+extern void vncRandomBytes(unsigned char *bytes);
+extern void vncEncryptBytes(unsigned char *bytes, char *passwd);
+
+/* region stuff */
+
+struct sraRegion;
+typedef struct sraRegion* sraRegionPtr;
+
+/*
+ * Per-client structure.
+ */
+
+typedef void (*ClientGoneHookPtr)(struct _rfbClientRec* cl);
+typedef void (*NegotiationFinishedHookPtr)(struct _rfbClientRec* cl);
+
+typedef struct _rfbClientRec {
+
+ /* back pointer to the screen */
+ rfbScreenInfoPtr screen;
+
+ /* private data. You should put any application client specific data
+ * into a struct and let clientData point to it. Don't forget to
+ * free the struct via clientGoneHook!
+ *
+ * This is useful if the IO functions have to behave client specific.
+ */
+ void* clientData;
+
+ ClientGoneHookPtr clientGoneHook;
+
+ /* negotiationFinishedHook is called when the negotiation phase has ended */
+ NegotiationFinishedHookPtr negotiationFinishedHook;
+
+ SOCKET sock;
+ char *host;
+
+#ifdef HAVE_PTHREADS
+ pthread_t client_thread;
+#endif
+ /* Possible client states: */
+ enum {
+ RFB_PROTOCOL_VERSION, /* establishing protocol version */
+ RFB_AUTHENTICATION, /* authenticating */
+ RFB_INITIALISATION, /* sending initialisation messages */
+ RFB_NORMAL /* normal protocol messages */
+ } state;
+
+ Bool reverseConnection;
+ Bool onHold;
+ Bool readyForSetColourMapEntries;
+ Bool useCopyRect;
+ int preferredEncoding;
+ int correMaxWidth, correMaxHeight;
+
+ /* The following member is only used during VNC authentication */
+ CARD8 authChallenge[CHALLENGESIZE];
+
+ /* The following members represent the update needed to get the client's
+ framebuffer from its present state to the current state of our
+ framebuffer.
+
+ If the client does not accept CopyRect encoding then the update is
+ simply represented as the region of the screen which has been modified
+ (modifiedRegion).
+
+ If the client does accept CopyRect encoding, then the update consists of
+ two parts. First we have a single copy from one region of the screen to
+ another (the destination of the copy is copyRegion), and second we have
+ the region of the screen which has been modified in some other way
+ (modifiedRegion).
+
+ Although the copy is of a single region, this region may have many
+ rectangles. When sending an update, the copyRegion is always sent
+ before the modifiedRegion. This is because the modifiedRegion may
+ overlap parts of the screen which are in the source of the copy.
+
+ In fact during normal processing, the modifiedRegion may even overlap
+ the destination copyRegion. Just before an update is sent we remove
+ from the copyRegion anything in the modifiedRegion. */
+
+ sraRegionPtr copyRegion; /* the destination region of the copy */
+ int copyDX, copyDY; /* the translation by which the copy happens */
+
+ sraRegionPtr modifiedRegion;
+
+ /* As part of the FramebufferUpdateRequest, a client can express interest
+ in a subrectangle of the whole framebuffer. This is stored in the
+ requestedRegion member. In the normal case this is the whole
+ framebuffer if the client is ready, empty if it's not. */
+
+ sraRegionPtr requestedRegion;
+
+ /* The following member represents the state of the "deferred update" timer
+ - when the framebuffer is modified and the client is ready, in most
+ cases it is more efficient to defer sending the update by a few
+ milliseconds so that several changes to the framebuffer can be combined
+ into a single update. */
+
+ struct timeval startDeferring;
+
+ /* translateFn points to the translation function which is used to copy
+ and translate a rectangle from the framebuffer to an output buffer. */
+
+ rfbTranslateFnType translateFn;
+ char *translateLookupTable;
+ rfbPixelFormat format;
+
+ /*
+ * UPDATE_BUF_SIZE must be big enough to send at least one whole line of the
+ * framebuffer. So for a max screen width of say 2K with 32-bit pixels this
+ * means 8K minimum.
+ */
+
+#define UPDATE_BUF_SIZE 30000
+
+ char updateBuf[UPDATE_BUF_SIZE];
+ int ublen;
+
+ /* statistics */
+
+ int rfbBytesSent[MAX_ENCODINGS];
+ int rfbRectanglesSent[MAX_ENCODINGS];
+ int rfbLastRectMarkersSent;
+ int rfbLastRectBytesSent;
+ int rfbCursorBytesSent;
+ int rfbCursorUpdatesSent;
+ int rfbFramebufferUpdateMessagesSent;
+ int rfbRawBytesEquivalent;
+ int rfbKeyEventsRcvd;
+ int rfbPointerEventsRcvd;
+
+ /* zlib encoding -- necessary compression state info per client */
+
+ struct z_stream_s compStream;
+ Bool compStreamInited;
+ CARD32 zlibCompressLevel;
+
+ /* tight encoding -- preserve zlib streams' state for each client */
+
+ z_stream zsStruct[4];
+ Bool zsActive[4];
+ int zsLevel[4];
+ int tightCompressLevel;
+ int tightQualityLevel;
+
+ /* soft cursor images */
+ unsigned char *softSource;
+ int softSourceLen;
+ rfbSoftCursorSetImage *softCursorImages[rfbSoftCursorMaxImages];
+ int nextUnusedSoftCursorImage;
+
+ Bool enableLastRectEncoding; /* client supports LastRect encoding */
+ Bool enableSoftCursorUpdates; /* client supports softcursor updates */
+ Bool disableBackground; /* client wants to disable background */
+ Bool enableCursorShapeUpdates; /* client supports cursor shape updates */
+ Bool useRichCursorEncoding; /* rfbEncodingRichCursor is preferred */
+ Bool cursorWasChanged; /* cursor shape update should be sent */
+ Bool cursorWasMoved; /* cursor move shape update should be sent */
+#ifdef BACKCHANNEL
+ Bool enableBackChannel;
+#endif
+
+ struct _rfbClientRec *prev;
+ struct _rfbClientRec *next;
+
+#ifdef HAVE_PTHREADS
+ /* whenever a client is referenced, the refCount has to be incremented
+ and afterwards decremented, so that the client is not cleaned up
+ while being referenced.
+ Use the functions rfbIncrClientRef(cl) and rfbDecrClientRef(cl);
+ */
+ int refCount;
+ MUTEX(refCountMutex);
+ COND(deleteCond);
+
+ MUTEX(outputMutex);
+ MUTEX(updateMutex);
+ COND(updateCond);
+#endif
+
+} rfbClientRec, *rfbClientPtr;
+
+/*
+ * This macro is used to test whether there is a framebuffer update needing to
+ * be sent to the client.
+ */
+
+#define FB_UPDATE_PENDING(cl) \
+ ((!(cl)->enableCursorShapeUpdates && !(cl)->screen->cursorIsDrawn) || \
+ ((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) || \
+ !sraRgnEmpty((cl)->copyRegion) || !sraRgnEmpty((cl)->modifiedRegion))
+
+/*
+ * Macros for endian swapping.
+ */
+
+#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+
+#define Swap24(l) ((((l) & 0xff) << 16) | (((l) >> 16) & 0xff) | \
+ (((l) & 0x00ff00)))
+
+#define Swap32(l) (((l) >> 24) | \
+ (((l) & 0x00ff0000) >> 8) | \
+ (((l) & 0x0000ff00) << 8) | \
+ ((l) << 24))
+
+
+extern char rfbEndianTest;
+
+#define Swap16IfLE(s) (rfbEndianTest ? Swap16(s) : (s))
+#define Swap24IfLE(l) (rfbEndianTest ? Swap24(l) : (l))
+#define Swap32IfLE(l) (rfbEndianTest ? Swap32(l) : (l))
+
+/* sockets.c */
+
+extern int rfbMaxClientWait;
+
+extern void rfbInitSockets(rfbScreenInfoPtr rfbScreen);
+extern void rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen);
+extern void rfbCloseClient(rfbClientPtr cl);
+extern int ReadExact(rfbClientPtr cl, char *buf, int len);
+extern int ReadExactTimeout(rfbClientPtr cl, char *buf, int len,int timeout);
+extern int WriteExact(rfbClientPtr cl, const char *buf, int len);
+extern void rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec);
+extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port);
+extern int ConnectToTcpAddr(char* host, int port);
+extern int ListenOnTCPPort(int port);
+extern int ListenOnUDPPort(int port);
+
+/* rfbserver.c */
+
+extern rfbClientPtr pointerClient;
+
+
+/* Routines to iterate over the client list in a thread-safe way.
+ Only a single iterator can be in use at a time process-wide. */
+typedef struct rfbClientIterator *rfbClientIteratorPtr;
+
+extern void rfbClientListInit(rfbScreenInfoPtr rfbScreen);
+extern rfbClientIteratorPtr rfbGetClientIterator(rfbScreenInfoPtr rfbScreen);
+extern rfbClientPtr rfbClientIteratorNext(rfbClientIteratorPtr iterator);
+extern void rfbReleaseClientIterator(rfbClientIteratorPtr iterator);
+
+extern void rfbNewClientConnection(rfbScreenInfoPtr rfbScreen,int sock);
+extern rfbClientPtr rfbNewClient(rfbScreenInfoPtr rfbScreen,int sock);
+extern rfbClientPtr rfbNewUDPClient(rfbScreenInfoPtr rfbScreen);
+extern rfbClientPtr rfbReverseConnection(rfbScreenInfoPtr rfbScreen,char *host, int port);
+extern void rfbClientConnectionGone(rfbClientPtr cl);
+extern void rfbProcessClientMessage(rfbClientPtr cl);
+extern void rfbClientConnFailed(rfbClientPtr cl, char *reason);
+extern void rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen,int sock);
+extern void rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen);
+extern Bool rfbSendPing(rfbClientPtr cl);
+extern Bool rfbSendFramebufferUpdate(rfbClientPtr cl, sraRegionPtr updateRegion);
+extern Bool rfbSendRectEncodingRaw(rfbClientPtr cl, int x,int y,int w,int h);
+extern Bool rfbSendUpdateBuf(rfbClientPtr cl);
+extern void rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len);
+extern Bool rfbSendCopyRegion(rfbClientPtr cl,sraRegionPtr reg,int dx,int dy);
+extern Bool rfbSendLastRectMarker(rfbClientPtr cl);
+extern Bool rfbSendSetColourMapEntries(rfbClientPtr cl, int firstColour, int nColours);
+extern void rfbSendBell(rfbScreenInfoPtr rfbScreen);
+
+void rfbGotXCutText(rfbScreenInfoPtr rfbScreen, char *str, int len);
+
+#ifdef BACKCHANNEL
+extern void rfbSendBackChannel(rfbScreenInfoPtr s,char* message,int len);
+#endif
+
+/* translate.c */
+
+extern Bool rfbEconomicTranslate;
+
+extern void rfbTranslateNone(char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height);
+extern Bool rfbSetTranslateFunction(rfbClientPtr cl);
+extern Bool rfbSetClientColourMap(rfbClientPtr cl, int firstColour, int nColours);
+extern void rfbSetClientColourMaps(rfbScreenInfoPtr rfbScreen, int firstColour, int nColours);
+
+/* httpd.c */
+
+extern int httpPort;
+extern char *httpDir;
+
+extern void httpInitSockets(rfbScreenInfoPtr rfbScreen);
+extern void httpCheckFds(rfbScreenInfoPtr rfbScreen);
+
+
+
+/* auth.c */
+
+extern void rfbAuthNewClient(rfbClientPtr cl);
+extern void rfbAuthProcessClientMessage(rfbClientPtr cl);
+
+
+/* rre.c */
+
+extern Bool rfbSendRectEncodingRRE(rfbClientPtr cl, int x,int y,int w,int h);
+
+
+/* corre.c */
+
+extern Bool rfbSendRectEncodingCoRRE(rfbClientPtr cl, int x,int y,int w,int h);
+
+
+/* hextile.c */
+
+extern Bool rfbSendRectEncodingHextile(rfbClientPtr cl, int x, int y, int w,
+ int h);
+
+
+/* zlib.c */
+
+/* Minimum zlib rectangle size in bytes. Anything smaller will
+ * not compress well due to overhead.
+ */
+#define VNC_ENCODE_ZLIB_MIN_COMP_SIZE (17)
+
+/* Set maximum zlib rectangle size in pixels. Always allow at least
+ * two scan lines.
+ */
+#define ZLIB_MAX_RECT_SIZE (128*256)
+#define ZLIB_MAX_SIZE(min) ((( min * 2 ) > ZLIB_MAX_RECT_SIZE ) ? \
+ ( min * 2 ) : ZLIB_MAX_RECT_SIZE )
+
+extern Bool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w,
+ int h);
+
+
+/* tight.c */
+
+#define TIGHT_DEFAULT_COMPRESSION 6
+
+extern Bool rfbTightDisableGradient;
+
+extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h);
+extern Bool rfbSendRectEncodingTight(rfbClientPtr cl, int x,int y,int w,int h);
+
+
+/* cursor.c */
+
+typedef struct rfbCursor {
+ unsigned char *source; /* points to bits */
+ unsigned char *mask; /* points to bits */
+ unsigned short width, height, xhot, yhot; /* metrics */
+ unsigned short foreRed, foreGreen, foreBlue; /* device-independent colour */
+ unsigned short backRed, backGreen, backBlue; /* device-independent colour */
+ unsigned char *richSource; /* source bytes for a rich cursor */
+} rfbCursor, *rfbCursorPtr;
+
+extern Bool rfbSendCursorShape(rfbClientPtr cl/*, rfbScreenInfoPtr pScreen*/);
+extern Bool rfbSendSoftCursor(rfbClientPtr cl, Bool cursorWasChanged);
+extern unsigned char rfbReverseByte[0x100];
+extern void rfbConvertLSBCursorBitmapOrMask(int width,int height,unsigned char* bitmap);
+extern rfbCursorPtr rfbMakeXCursor(int width,int height,char* cursorString,char* maskString);
+extern char* rfbMakeMaskForXCursor(int width,int height,char* cursorString);
+extern void MakeXCursorFromRichCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor);
+extern void MakeRichCursorFromXCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr cursor);
+extern void MakeSoftCursor(rfbClientPtr cl,rfbCursorPtr cursor);
+extern void rfbFreeCursor(rfbCursorPtr cursor);
+extern void rfbDrawCursor(rfbScreenInfoPtr rfbScreen);
+extern void rfbUndrawCursor(rfbScreenInfoPtr rfbScreen);
+extern void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c,Bool freeOld);
+
+/* cursor handling for the pointer */
+extern void defaultPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl);
+
+/* stats.c */
+
+extern void rfbResetStats(rfbClientPtr cl);
+extern void rfbPrintStats(rfbClientPtr cl);
+
+/* font.c */
+
+typedef struct rfbFontData {
+ unsigned char* data;
+ /*
+ metaData is a 256*5 array:
+ for each character
+ (offset,width,height,x,y)
+ */
+ int* metaData;
+} rfbFontData,* rfbFontDataPtr;
+
+int rfbDrawChar(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,int x,int y,unsigned char c,Pixel colour);
+void rfbDrawString(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,int x,int y,const char* string,Pixel colour);
+/* if colour==backColour, background is transparent */
+int rfbDrawCharWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,int x,int y,unsigned char c,int x1,int y1,int x2,int y2,Pixel colour,Pixel backColour);
+void rfbDrawStringWithClip(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,int x,int y,const char* string,int x1,int y1,int x2,int y2,Pixel colour,Pixel backColour);
+int rfbWidthOfString(rfbFontDataPtr font,const char* string);
+int rfbWidthOfChar(rfbFontDataPtr font,unsigned char c);
+void rfbFontBBox(rfbFontDataPtr font,unsigned char c,int* x1,int* y1,int* x2,int* y2);
+/* this returns the smallest box enclosing any character of font. */
+void rfbWholeFontBBox(rfbFontDataPtr font,int *x1, int *y1, int *x2, int *y2);
+
+/* dynamically load a linux console font (4096 bytes, 256 glyphs a 8x16 */
+rfbFontDataPtr rfbLoadConsoleFont(char *filename);
+/* free a dynamically loaded font */
+void rfbFreeFont(rfbFontDataPtr font);
+
+/* draw.c */
+
+/* You have to call rfbUndrawCursor before using these functions */
+void rfbFillRect(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,Pixel col);
+void rfbDrawPixel(rfbScreenInfoPtr s,int x,int y,Pixel col);
+void rfbDrawLine(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2,Pixel col);
+
+/* selbox.c */
+
+/* this opens a modal select box. list is an array of strings, the end marked
+ with a NULL.
+ It returns the index in the list or -1 if cancelled or something else
+ wasn't kosher. */
+typedef void (*SelectionChangedHookPtr)(int _index);
+extern int rfbSelectBox(rfbScreenInfoPtr rfbScreen,
+ rfbFontDataPtr font, char** list,
+ int x1, int y1, int x2, int y2,
+ Pixel foreColour, Pixel backColour,
+ int border,SelectionChangedHookPtr selChangedHook);
+
+/* cargs.c */
+
+extern void rfbUsage(void);
+extern void rfbPurgeArguments(int* argc,int* position,int count,char *argv[]);
+extern void rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]);
+extern void rfbProcessSizeArguments(int* width,int* height,int* bpp,int* argc, char *argv[]);
+
+/* main.c */
+
+extern void rfbLogEnable(int enabled);
+extern void rfbLog(const char *format, ...);
+extern void rfbLogPerror(const char *str);
+
+void rfbScheduleCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy);
+void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy);
+
+void rfbDoCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy);
+void rfbDoCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy);
+
+void rfbMarkRectAsModified(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2);
+void rfbMarkRegionAsModified(rfbScreenInfoPtr rfbScreen,sraRegionPtr modRegion);
+void doNothingWithClient(rfbClientPtr cl);
+enum rfbNewClientAction defaultNewClientHook(rfbClientPtr cl);
+
+/* to check against plain passwords */
+Bool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len);
+
+/* functions to make a vnc server */
+extern rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
+ int width,int height,int bitsPerSample,int samplesPerPixel,
+ int bytesPerPixel);
+extern void rfbInitServer(rfbScreenInfoPtr rfbScreen);
+extern void rfbScreenCleanup(rfbScreenInfoPtr screenInfo);
+
+/* functions to accept/refuse a client that has been put on hold
+ by a NewClientHookPtr function. Must not be called in other
+ situations. */
+extern void rfbStartOnHoldClient(rfbClientPtr cl);
+extern void rfbRefuseOnHoldClient(rfbClientPtr cl);
+
+/* call one of these two functions to service the vnc clients.
+ usec are the microseconds the select on the fds waits.
+ if you are using the event loop, set this to some value > 0, so the
+ server doesn't get a high load just by listening. */
+
+extern void rfbRunEventLoop(rfbScreenInfoPtr screenInfo, long usec, Bool runInBackground);
+extern void rfbProcessEvents(rfbScreenInfoPtr screenInfo,long usec);
+
+#endif
+
+#if(defined __cplusplus)
+}
+#endif
diff --git a/krfb/libvncserver/rfbproto.h b/krfb/libvncserver/rfbproto.h
new file mode 100644
index 00000000..9f9f6bc9
--- /dev/null
+++ b/krfb/libvncserver/rfbproto.h
@@ -0,0 +1,848 @@
+#ifndef RFBPROTO_H
+#define RFBPROTO_H
+
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * rfbproto.h - header file for the RFB protocol version 3.3
+ *
+ * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed
+ * integer (for n = 8, 16 and 32).
+ *
+ * All multiple byte integers are in big endian (network) order (most
+ * significant byte first). Unless noted otherwise there is no special
+ * alignment of protocol structures.
+ *
+ *
+ * Once the initial handshaking is done, all messages start with a type byte,
+ * (usually) followed by message-specific data. The order of definitions in
+ * this file is as follows:
+ *
+ * (1) Structures used in several types of message.
+ * (2) Structures used in the initial handshaking.
+ * (3) Message types.
+ * (4) Encoding types.
+ * (5) For each message type, the form of the data following the type byte.
+ * Sometimes this is defined by a single structure but the more complex
+ * messages have to be explained by comments.
+ */
+
+
+/*****************************************************************************
+ *
+ * Structures used in several messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify a rectangle. This structure is a multiple of 4
+ * bytes so that it can be interspersed with 32-bit pixel data without
+ * affecting alignment.
+ */
+
+typedef struct {
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbRectangle;
+
+#define sz_rfbRectangle 8
+
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify pixel format.
+ */
+
+typedef struct {
+
+ CARD8 bitsPerPixel; /* 8,16,32 only */
+
+ CARD8 depth; /* 8 to 32 */
+
+ CARD8 bigEndian; /* True if multi-byte pixels are interpreted
+ as big endian, or if single-bit-per-pixel
+ has most significant bit of the byte
+ corresponding to first (leftmost) pixel. Of
+ course this is meaningless for 8 bits/pix */
+
+ CARD8 trueColour; /* If false then we need a "colour map" to
+ convert pixels to RGB. If true, xxxMax and
+ xxxShift specify bits used for red, green
+ and blue */
+
+ /* the following fields are only meaningful if trueColour is true */
+
+ CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the
+ number of bits used for red). Note this
+ value is always in big endian order. */
+
+ CARD16 greenMax; /* similar for green */
+
+ CARD16 blueMax; /* and blue */
+
+ CARD8 redShift; /* number of shifts needed to get the red
+ value in a pixel to the least significant
+ bit. To find the red value from a given
+ pixel, do the following:
+ 1) Swap pixel value according to bigEndian
+ (e.g. if bigEndian is false and host byte
+ order is big endian, then swap).
+ 2) Shift right by redShift.
+ 3) AND with redMax (in host byte order).
+ 4) You now have the red value between 0 and
+ redMax. */
+
+ CARD8 greenShift; /* similar for green */
+
+ CARD8 blueShift; /* and blue */
+
+ CARD8 pad1;
+ CARD16 pad2;
+
+} rfbPixelFormat;
+
+#define sz_rfbPixelFormat 16
+
+
+
+/*****************************************************************************
+ *
+ * Initial handshaking messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Protocol Version
+ *
+ * The server always sends 12 bytes to start which identifies the latest RFB
+ * protocol version number which it supports. These bytes are interpreted
+ * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where
+ * xxx and yyy are the major and minor version numbers (for version 3.3
+ * this is "RFB 003.003\n").
+ *
+ * The client then replies with a similar 12-byte message giving the version
+ * number of the protocol which should actually be used (which may be different
+ * to that quoted by the server).
+ *
+ * It is intended that both clients and servers may provide some level of
+ * backwards compatibility by this mechanism. Servers in particular should
+ * attempt to provide backwards compatibility, and even forwards compatibility
+ * to some extent. For example if a client demands version 3.1 of the
+ * protocol, a 3.0 server can probably assume that by ignoring requests for
+ * encoding types it doesn't understand, everything will still work OK. This
+ * will probably not be the case for changes in the major version number.
+ *
+ * The format string below can be used in sprintf or sscanf to generate or
+ * decode the version string respectively.
+ */
+
+#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
+#define rfbProtocolMajorVersion 3
+#define rfbProtocolMinorVersion 3
+
+typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
+
+#define sz_rfbProtocolVersionMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * Authentication
+ *
+ * Once the protocol version has been decided, the server then sends a 32-bit
+ * word indicating whether any authentication is needed on the connection.
+ * The value of this word determines the authentication scheme in use. For
+ * version 3.0 of the protocol this may have one of the following values:
+ */
+
+#define rfbConnFailed 0
+#define rfbNoAuth 1
+#define rfbVncAuth 2
+
+/*
+ * rfbConnFailed: For some reason the connection failed (e.g. the server
+ * cannot support the desired protocol version). This is
+ * followed by a string describing the reason (where a
+ * string is specified as a 32-bit length followed by that
+ * many ASCII characters).
+ *
+ * rfbNoAuth: No authentication is needed.
+ *
+ * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte
+ * challenge follows, which the client encrypts as
+ * appropriate using the password and sends the resulting
+ * 16-byte response. If the response is correct, the
+ * server sends the 32-bit word rfbVncAuthOK. If a simple
+ * failure happens, the server sends rfbVncAuthFailed and
+ * closes the connection. If the server decides that too
+ * many failures have occurred, it sends rfbVncAuthTooMany
+ * and closes the connection. In the latter case, the
+ * server should not allow an immediate reconnection by
+ * the client.
+ */
+
+#define rfbVncAuthOK 0
+#define rfbVncAuthFailed 1
+#define rfbVncAuthTooMany 2
+
+
+/*-----------------------------------------------------------------------------
+ * Client Initialisation Message
+ *
+ * Once the client and server are sure that they're happy to talk to one
+ * another, the client sends an initialisation message. At present this
+ * message only consists of a boolean indicating whether the server should try
+ * to share the desktop by leaving other clients connected, or give exclusive
+ * access to this client by disconnecting all other clients.
+ */
+
+typedef struct {
+ CARD8 shared;
+} rfbClientInitMsg;
+
+#define sz_rfbClientInitMsg 1
+
+
+/*-----------------------------------------------------------------------------
+ * Server Initialisation Message
+ *
+ * After the client initialisation message, the server sends one of its own.
+ * This tells the client the width and height of the server's framebuffer,
+ * its pixel format and the name associated with the desktop.
+ */
+
+typedef struct {
+ CARD16 framebufferWidth;
+ CARD16 framebufferHeight;
+ rfbPixelFormat format; /* the server's preferred pixel format */
+ CARD32 nameLength;
+ /* followed by char name[nameLength] */
+} rfbServerInitMsg;
+
+#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+
+
+/*
+ * Following the server initialisation message it's up to the client to send
+ * whichever protocol messages it wants. Typically it will send a
+ * SetPixelFormat message and a SetEncodings message, followed by a
+ * FramebufferUpdateRequest. From then on the server will send
+ * FramebufferUpdate messages in response to the client's
+ * FramebufferUpdateRequest messages. The client should send
+ * FramebufferUpdateRequest messages with incremental set to true when it has
+ * finished processing one FramebufferUpdate and is ready to process another.
+ * With a fast client, the rate at which FramebufferUpdateRequests are sent
+ * should be regulated to avoid hogging the network.
+ */
+
+
+
+/*****************************************************************************
+ *
+ * Message types
+ *
+ *****************************************************************************/
+
+/* server -> client */
+
+#define rfbFramebufferUpdate 0
+#define rfbSetColourMapEntries 1
+#define rfbBell 2
+#define rfbServerCutText 3
+#ifdef BACKCHANNEL
+#define rfbBackChannel 15
+#endif
+
+/* client -> server */
+
+#define rfbSetPixelFormat 0
+#define rfbFixColourMapEntries 1 /* not currently supported */
+#define rfbSetEncodings 2
+#define rfbFramebufferUpdateRequest 3
+#define rfbKeyEvent 4
+#define rfbPointerEvent 5
+#define rfbClientCutText 6
+
+
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define rfbEncodingRaw 0
+#define rfbEncodingCopyRect 1
+#define rfbEncodingRRE 2
+#define rfbEncodingCoRRE 4
+#define rfbEncodingHextile 5
+#define rfbEncodingZlib 6
+#define rfbEncodingTight 7
+#define rfbEncodingZlibHex 8
+#ifdef BACKCHANNEL
+#define rfbEncodingBackChannel 15
+#endif
+
+/*
+ * Special encoding numbers:
+ * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels;
+ * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data;
+ * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions;
+ * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet;
+ * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor;
+ * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels.
+ */
+
+#define rfbEncodingCompressLevel0 0xFFFFFF00
+#define rfbEncodingCompressLevel1 0xFFFFFF01
+#define rfbEncodingCompressLevel2 0xFFFFFF02
+#define rfbEncodingCompressLevel3 0xFFFFFF03
+#define rfbEncodingCompressLevel4 0xFFFFFF04
+#define rfbEncodingCompressLevel5 0xFFFFFF05
+#define rfbEncodingCompressLevel6 0xFFFFFF06
+#define rfbEncodingCompressLevel7 0xFFFFFF07
+#define rfbEncodingCompressLevel8 0xFFFFFF08
+#define rfbEncodingCompressLevel9 0xFFFFFF09
+
+#define rfbEncodingXCursor 0xFFFFFF10
+#define rfbEncodingRichCursor 0xFFFFFF11
+#define rfbEncodingSoftCursor 0xFFFFFF12
+
+#define rfbEncodingLastRect 0xFFFFFF20
+#define rfbEncodingBackground 0xFFFFFF25
+
+#define rfbEncodingQualityLevel0 0xFFFFFFE0
+#define rfbEncodingQualityLevel1 0xFFFFFFE1
+#define rfbEncodingQualityLevel2 0xFFFFFFE2
+#define rfbEncodingQualityLevel3 0xFFFFFFE3
+#define rfbEncodingQualityLevel4 0xFFFFFFE4
+#define rfbEncodingQualityLevel5 0xFFFFFFE5
+#define rfbEncodingQualityLevel6 0xFFFFFFE6
+#define rfbEncodingQualityLevel7 0xFFFFFFE7
+#define rfbEncodingQualityLevel8 0xFFFFFFE8
+#define rfbEncodingQualityLevel9 0xFFFFFFE9
+
+
+/*****************************************************************************
+ *
+ * Server -> client message definitions
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdate - a block of rectangles to be copied to the framebuffer.
+ *
+ * This message consists of a header giving the number of rectangles of pixel
+ * data followed by the rectangles themselves. The header is padded so that
+ * together with the type byte it is an exact multiple of 4 bytes (to help
+ * with alignment of 32-bit pixels):
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdate */
+ CARD8 pad;
+ CARD16 nRects;
+ /* followed by nRects rectangles */
+} rfbFramebufferUpdateMsg;
+
+#define sz_rfbFramebufferUpdateMsg 4
+
+/*
+ * Each rectangle of pixel data consists of a header describing the position
+ * and size of the rectangle and a type word describing the encoding of the
+ * pixel data, followed finally by the pixel data. Note that if the client has
+ * not sent a SetEncodings message then it will only receive raw pixel data.
+ * Also note again that this structure is a multiple of 4 bytes.
+ */
+
+typedef struct {
+ rfbRectangle r;
+ CARD32 encoding; /* one of the encoding types rfbEncoding... */
+} rfbFramebufferUpdateRectHeader;
+
+#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Raw Encoding. Pixels are sent in top-to-bottom scanline order,
+ * left-to-right within a scanline with no padding in between.
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CopyRect Encoding. The pixels are specified simply by the x and y position
+ * of the source rectangle.
+ */
+
+typedef struct {
+ CARD16 srcX;
+ CARD16 srcY;
+} rfbCopyRect;
+
+#define sz_rfbCopyRect 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure
+ * giving the number of subrectangles following. Finally the data follows in
+ * the form [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbRectangle>].
+ */
+
+typedef struct {
+ CARD32 nSubrects;
+} rfbRREHeader;
+
+#define sz_rfbRREHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving
+ * the number of subrectangles following. Finally the data follows in the form
+ * [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbCoRRERectangle>]. This means that
+ * the whole rectangle must be at most 255x255 pixels.
+ */
+
+typedef struct {
+ CARD8 x;
+ CARD8 y;
+ CARD8 w;
+ CARD8 h;
+} rfbCoRRERectangle;
+
+#define sz_rfbCoRRERectangle 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels,
+ * starting at the top left going in left-to-right, top-to-bottom order. If
+ * the width of the rectangle is not an exact multiple of 16 then the width of
+ * the last tile in each row will be correspondingly smaller. Similarly if the
+ * height is not an exact multiple of 16 then the height of each tile in the
+ * final row will also be smaller. Each tile begins with a "subencoding" type
+ * byte, which is a mask made up of a number of bits. If the Raw bit is set
+ * then the other bits are irrelevant; w*h pixel values follow (where w and h
+ * are the width and height of the tile). Otherwise the tile is encoded in a
+ * similar way to RRE, except that the position and size of each subrectangle
+ * can be specified in just two bytes. The other bits in the mask are as
+ * follows:
+ *
+ * BackgroundSpecified - if set, a pixel value follows which specifies
+ * the background colour for this tile. The first non-raw tile in a
+ * rectangle must have this bit set. If this bit isn't set then the
+ * background is the same as the last tile.
+ *
+ * ForegroundSpecified - if set, a pixel value follows which specifies
+ * the foreground colour to be used for all subrectangles in this tile.
+ * If this bit is set then the SubrectsColoured bit must be zero.
+ *
+ * AnySubrects - if set, a single byte follows giving the number of
+ * subrectangles following. If not set, there are no subrectangles (i.e.
+ * the whole tile is just solid background colour).
+ *
+ * SubrectsColoured - if set then each subrectangle is preceded by a pixel
+ * value giving the colour of that subrectangle. If not set, all
+ * subrectangles are the same colour, the foreground colour; if the
+ * ForegroundSpecified bit wasn't set then the foreground is the same as
+ * the last tile.
+ *
+ * The position and size of each subrectangle is specified in two bytes. The
+ * Pack macros below can be used to generate the two bytes from x, y, w, h,
+ * and the Extract macros can be used to extract the x, y, w, h values from
+ * the two bytes.
+ */
+
+#define rfbHextileRaw (1 << 0)
+#define rfbHextileBackgroundSpecified (1 << 1)
+#define rfbHextileForegroundSpecified (1 << 2)
+#define rfbHextileAnySubrects (1 << 3)
+#define rfbHextileSubrectsColoured (1 << 4)
+
+#define rfbHextilePackXY(x,y) (((x) << 4) | (y))
+#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1))
+#define rfbHextileExtractX(byte) ((byte) >> 4)
+#define rfbHextileExtractY(byte) ((byte) & 0xf)
+#define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
+#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure
+ * giving the number of bytes following. Finally the data follows is
+ * zlib compressed version of the raw pixel data as negotiated.
+ */
+
+typedef struct {
+ CARD32 nBytes;
+} rfbZlibHeader;
+
+#define sz_rfbZlibHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Tight Encoding. FIXME: Add more documentation.
+ */
+
+#define rfbTightExplicitFilter 0x04
+#define rfbTightFill 0x08
+#define rfbTightJpeg 0x09
+#define rfbTightMaxSubencoding 0x09
+
+/* Filters to improve compression efficiency */
+#define rfbTightFilterCopy 0x00
+#define rfbTightFilterPalette 0x01
+#define rfbTightFilterGradient 0x02
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * XCursor encoding. This is a special encoding used to transmit X-style
+ * cursor shapes from server to clients. Note that for this encoding,
+ * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot
+ * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB
+ * samples are sent after header in the rfbXCursorColors structure. They
+ * denote foreground and background colors of the cursor. If a client
+ * supports only black-and-white cursors, it should ignore these colors and
+ * assume that foreground is black and background is white. Next, two bitmaps
+ * (1 bits per pixel) follow: first one with actual data (value 0 denotes
+ * background color, value 1 denotes foreground color), second one with
+ * transparency data (bits with zero value mean that these pixels are
+ * transparent). Both bitmaps represent cursor data in a byte stream, from
+ * left to right, from top to bottom, and each row is byte-aligned. Most
+ * significant bits correspond to leftmost pixels. The number of bytes in
+ * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor
+ * should be hidden (or default local cursor should be set by the client).
+ */
+
+typedef struct {
+ CARD8 foreRed;
+ CARD8 foreGreen;
+ CARD8 foreBlue;
+ CARD8 backRed;
+ CARD8 backGreen;
+ CARD8 backBlue;
+} rfbXCursorColors;
+
+#define sz_rfbXCursorColors 6
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RichCursor encoding. This is a special encoding used to transmit cursor
+ * shapes from server to clients. It is similar to the XCursor encoding but
+ * uses client pixel format instead of two RGB colors to represent cursor
+ * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader
+ * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h).
+ * After header, two pixmaps follow: first one with cursor image in current
+ * client pixel format (like in raw encoding), second with transparency data
+ * (1 bit per pixel, exactly the same format as used for transparency bitmap
+ * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or
+ * default local cursor should be set by the client).
+ */
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * SoftCursor encoding. This encoding is used to transmit image and position
+ * of the remote cursor. It has two sub-messages: SetImage is used to upload
+ * one of 16 images, and Move selects the image and sets the position of the
+ * cursor.
+ * Each SoftCursor message starts with a CARD8. If it is in the 0-15 range
+ * it specifies the number of the cursor image and is followed by the
+ * rfbSoftCursorMove message. If the given cursor has not been set yet the
+ * message will be ignored. If the first CARD8 is in the 128-143 range it
+ * specifies the cursor that will be set in the following
+ * rfbSoftCursorSetImage message. To hide the cursor send a SetImage
+ * message with width and height 0 and imageLength 0.
+ * SetImage transmits the hotspot coordinates in the x/y fields of the
+ * rfbFramebufferUpdateRectHeader, width and height of the image are in the
+ * header's width and height fields.
+ * Move transmits the pointer coordinates in the w/h fields of the
+ * header, x/y are always 0.
+ */
+
+typedef struct {
+ CARD8 imageIndex;
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+} rfbSoftCursorMove;
+
+typedef struct {
+ CARD8 imageIndex;
+ CARD8 padding;
+ CARD16 imageLength;
+ /*
+ * Followed by an image of the cursor in the client's image format
+ * with the following RLE mask compression. It begins with CARD8 that
+ * specifies the number of mask'ed pixels that will be NOT transmitted.
+ * Then follows a CARD8 that specified by the number of unmask'd pixels
+ * that will be transmitted next. Then a CARD8 with the number of mask'd
+ * pixels and so on.
+ */
+} rfbSoftCursorSetImage;
+
+typedef union {
+ CARD8 type;
+ rfbSoftCursorMove move;
+ rfbSoftCursorSetImage setImage;
+} rfbSoftCursorMsg;
+
+#define rfbSoftCursorMaxImages 16
+#define rfbSoftCursorSetIconOffset 128
+
+
+/*-----------------------------------------------------------------------------
+ * SetColourMapEntries - these messages are only sent if the pixel
+ * format uses a "colour map" (i.e. trueColour false) and the client has not
+ * fixed the entire colour map using FixColourMapEntries. In addition they
+ * will only start being sent after the client has sent its first
+ * FramebufferUpdateRequest. So if the client always tells the server to use
+ * trueColour then it never needs to process this type of message.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbSetColourMapEntriesMsg;
+
+#define sz_rfbSetColourMapEntriesMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * Bell - ring a bell on the client if it has one.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbBell */
+} rfbBellMsg;
+
+#define sz_rfbBellMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ServerCutText - the server has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbServerCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbServerCutTextMsg;
+
+#define sz_rfbServerCutTextMsg 8
+
+#ifdef BACKCHANNEL
+typedef rfbServerCutTextMsg rfbBackChannelMsg;
+#define sz_rfbBackChannelMsg 8
+#endif
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all server->client messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbFramebufferUpdateMsg fu;
+ rfbSetColourMapEntriesMsg scme;
+ rfbBellMsg b;
+ rfbServerCutTextMsg sct;
+} rfbServerToClientMsg;
+
+
+
+/*****************************************************************************
+ *
+ * Message definitions (client -> server)
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * SetPixelFormat - tell the RFB server the format in which the client wants
+ * pixels sent.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetPixelFormat */
+ CARD8 pad1;
+ CARD16 pad2;
+ rfbPixelFormat format;
+} rfbSetPixelFormatMsg;
+
+#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4)
+
+
+/*-----------------------------------------------------------------------------
+ * FixColourMapEntries - when the pixel format uses a "colour map", fix
+ * read-only colour map entries.
+ *
+ * ***************** NOT CURRENTLY SUPPORTED *****************
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFixColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbFixColourMapEntriesMsg;
+
+#define sz_rfbFixColourMapEntriesMsg 6
+
+
+/*-----------------------------------------------------------------------------
+ * SetEncodings - tell the RFB server which encoding types we accept. Put them
+ * in order of preference, if we have any. We may always receive raw
+ * encoding, even if we don't specify it here.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetEncodings */
+ CARD8 pad;
+ CARD16 nEncodings;
+ /* followed by nEncodings * CARD32 encoding types */
+} rfbSetEncodingsMsg;
+
+#define sz_rfbSetEncodingsMsg 4
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdateRequest - request for a framebuffer update. If incremental
+ * is true then the client just wants the changes since the last update. If
+ * false then it wants the whole of the specified rectangle.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdateRequest */
+ CARD8 incremental;
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbFramebufferUpdateRequestMsg;
+
+#define sz_rfbFramebufferUpdateRequestMsg 10
+
+
+/*-----------------------------------------------------------------------------
+ * KeyEvent - key press or release
+ *
+ * Keys are specified using the "keysym" values defined by the X Window System.
+ * For most ordinary keys, the keysym is the same as the corresponding ASCII
+ * value. Other common keys are:
+ *
+ * BackSpace 0xff08
+ * Tab 0xff09
+ * Return or Enter 0xff0d
+ * Escape 0xff1b
+ * Insert 0xff63
+ * Delete 0xffff
+ * Home 0xff50
+ * End 0xff57
+ * Page Up 0xff55
+ * Page Down 0xff56
+ * Left 0xff51
+ * Up 0xff52
+ * Right 0xff53
+ * Down 0xff54
+ * F1 0xffbe
+ * F2 0xffbf
+ * ... ...
+ * F12 0xffc9
+ * Shift 0xffe1
+ * Control 0xffe3
+ * Meta 0xffe7
+ * Alt 0xffe9
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbKeyEvent */
+ CARD8 down; /* true if down (press), false if up */
+ CARD16 pad;
+ CARD32 key; /* key is specified as an X keysym */
+} rfbKeyEventMsg;
+
+#define sz_rfbKeyEventMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * PointerEvent - mouse/pen move and/or button press.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbPointerEvent */
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+ CARD16 x;
+ CARD16 y;
+} rfbPointerEventMsg;
+
+#define rfbButton1Mask 1
+#define rfbButton2Mask 2
+#define rfbButton3Mask 4
+
+#define sz_rfbPointerEventMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * ClientCutText - the client has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbClientCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbClientCutTextMsg;
+
+#define sz_rfbClientCutTextMsg 8
+
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all client->server messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbSetPixelFormatMsg spf;
+ rfbFixColourMapEntriesMsg fcme;
+ rfbSetEncodingsMsg se;
+ rfbFramebufferUpdateRequestMsg fur;
+ rfbKeyEventMsg ke;
+ rfbPointerEventMsg pe;
+ rfbClientCutTextMsg cct;
+} rfbClientToServerMsg;
+
+#endif
diff --git a/krfb/libvncserver/rfbserver.c b/krfb/libvncserver/rfbserver.c
new file mode 100644
index 00000000..78d40798
--- /dev/null
+++ b/krfb/libvncserver/rfbserver.c
@@ -0,0 +1,1590 @@
+/*
+ * rfbserver.c - deal with server-side of the RFB protocol.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfb.h"
+#include "sraRegion.h"
+#ifdef WIN32
+#define write(sock,buf,len) send(sock,buf,len,0)
+#else
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <sys/types.h>
+#ifdef __osf__
+typedef int socklen_t;
+#endif
+
+#ifdef CORBA
+#include <vncserverctrl.h>
+#endif
+
+#ifdef DEBUGPROTO
+#undef DEBUGPROTO
+#define DEBUGPROTO(x) x
+#else
+#define DEBUGPROTO(x)
+#endif
+
+rfbClientPtr pointerClient = NULL; /* Mutex for pointer events */
+
+static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
+static void rfbProcessClientNormalMessage(rfbClientPtr cl);
+static void rfbProcessClientInitMessage(rfbClientPtr cl);
+
+#ifdef HAVE_PTHREADS
+void rfbIncrClientRef(rfbClientPtr cl)
+{
+ LOCK(cl->refCountMutex);
+ cl->refCount++;
+ UNLOCK(cl->refCountMutex);
+}
+
+void rfbDecrClientRef(rfbClientPtr cl)
+{
+ LOCK(cl->refCountMutex);
+ cl->refCount--;
+ if(cl->refCount<=0) /* just to be sure also < 0 */
+ TSIGNAL(cl->deleteCond);
+ UNLOCK(cl->refCountMutex);
+}
+#else
+void rfbIncrClientRef(rfbClientPtr cl)
+{
+}
+
+void rfbDecrClientRef(rfbClientPtr cl)
+{
+}
+#endif
+
+MUTEX(rfbClientListMutex);
+
+struct rfbClientIterator {
+ rfbClientPtr next;
+ rfbScreenInfoPtr screen;
+};
+
+void
+rfbClientListInit(rfbScreenInfoPtr rfbScreen)
+{
+ rfbScreen->rfbClientHead = NULL;
+ INIT_MUTEX(rfbClientListMutex);
+}
+
+rfbClientIteratorPtr
+rfbGetClientIterator(rfbScreenInfoPtr rfbScreen)
+{
+ rfbClientIteratorPtr i =
+ (rfbClientIteratorPtr)malloc(sizeof(struct rfbClientIterator));
+ i->next = 0;
+ i->screen = rfbScreen;
+ return i;
+}
+
+rfbClientPtr
+rfbClientIteratorNext(rfbClientIteratorPtr i)
+{
+ if(i->next == 0) {
+ LOCK(rfbClientListMutex);
+ i->next = i->screen->rfbClientHead;
+ UNLOCK(rfbClientListMutex);
+ } else {
+ IF_PTHREADS(rfbClientPtr cl = i->next);
+ i->next = i->next->next;
+ IF_PTHREADS(rfbDecrClientRef(cl));
+ }
+
+#ifdef HAVE_PTHREADS
+ while(i->next && i->next->sock<0)
+ i->next = i->next->next;
+ if(i->next)
+ rfbIncrClientRef(i->next);
+#endif
+
+ return i->next;
+}
+
+void
+rfbReleaseClientIterator(rfbClientIteratorPtr iterator)
+{
+ IF_PTHREADS(if(iterator->next) rfbDecrClientRef(iterator->next));
+}
+
+
+/*
+ * rfbNewClientConnection is called from sockets.c when a new connection
+ * comes in.
+ */
+
+void
+rfbNewClientConnection(rfbScreen,sock)
+ rfbScreenInfoPtr rfbScreen;
+ int sock;
+{
+ rfbClientPtr cl;
+
+ cl = rfbNewClient(rfbScreen,sock);
+#ifdef CORBA
+ if(cl!=NULL)
+ newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE),1,1,1);
+#endif
+}
+
+
+/*
+ * rfbReverseConnection is called by the CORBA stuff to make an outward
+ * connection to a "listening" RFB client.
+ */
+
+rfbClientPtr
+rfbReverseConnection(rfbScreen,host, port)
+ rfbScreenInfoPtr rfbScreen;
+ char *host;
+ int port;
+{
+ int sock;
+ rfbClientPtr cl;
+
+ if ((sock = rfbConnect(rfbScreen, host, port)) < 0)
+ return (rfbClientPtr)NULL;
+
+ cl = rfbNewClient(rfbScreen, sock);
+
+ if (cl) {
+ cl->reverseConnection = TRUE;
+ }
+
+ return cl;
+}
+
+
+/*
+ * rfbNewClient is called when a new connection has been made by whatever
+ * means.
+ */
+
+static rfbClientPtr
+rfbNewTCPOrUDPClient(rfbScreen,sock,isUDP)
+ rfbScreenInfoPtr rfbScreen;
+ int sock;
+ Bool isUDP;
+{
+ rfbProtocolVersionMsg pv;
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr cl,cl_;
+ struct sockaddr_in addr;
+ size_t addrlen = sizeof(struct sockaddr_in);
+ int i;
+
+ cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1);
+
+ cl->screen = rfbScreen;
+ cl->sock = sock;
+ rfbResetStats(cl);
+
+ if(isUDP) {
+ rfbLog(" accepted UDP client\n");
+ } else {
+ getpeername(sock, (struct sockaddr *)&addr, &addrlen);
+ cl->host = strdup(inet_ntoa(addr.sin_addr));
+
+ rfbLog(" other clients:\n");
+ iterator = rfbGetClientIterator(rfbScreen);
+ while ((cl_ = rfbClientIteratorNext(iterator)) != NULL) {
+ rfbLog(" %s\n",cl_->host);
+ }
+ rfbReleaseClientIterator(iterator);
+
+ FD_SET(sock,&(rfbScreen->allFds));
+ rfbScreen->maxFd = max(sock,rfbScreen->maxFd);
+
+ INIT_MUTEX(cl->outputMutex);
+ INIT_MUTEX(cl->refCountMutex);
+ INIT_COND(cl->deleteCond);
+
+ cl->state = RFB_PROTOCOL_VERSION;
+
+ cl->reverseConnection = FALSE;
+ cl->readyForSetColourMapEntries = FALSE;
+ cl->useCopyRect = FALSE;
+ cl->preferredEncoding = rfbEncodingRaw;
+ cl->correMaxWidth = 48;
+ cl->correMaxHeight = 48;
+
+ cl->copyRegion = sraRgnCreate();
+ cl->copyDX = 0;
+ cl->copyDY = 0;
+
+ cl->modifiedRegion =
+ sraRgnCreateRect(0,0,rfbScreen->width,rfbScreen->height);
+
+ INIT_MUTEX(cl->updateMutex);
+ INIT_COND(cl->updateCond);
+
+ cl->requestedRegion = sraRgnCreate();
+
+ cl->format = cl->screen->rfbServerFormat;
+ cl->translateFn = rfbTranslateNone;
+ cl->translateLookupTable = NULL;
+
+ LOCK(rfbClientListMutex);
+
+ IF_PTHREADS(cl->refCount = 0);
+ cl->next = rfbScreen->rfbClientHead;
+ cl->prev = NULL;
+ if (rfbScreen->rfbClientHead)
+ rfbScreen->rfbClientHead->prev = cl;
+
+ rfbScreen->rfbClientHead = cl;
+ UNLOCK(rfbClientListMutex);
+
+ cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
+ cl->tightQualityLevel = -1;
+ for (i = 0; i < 4; i++)
+ cl->zsActive[i] = FALSE;
+
+ cl->enableCursorShapeUpdates = FALSE;
+ cl->useRichCursorEncoding = FALSE;
+ cl->enableLastRectEncoding = FALSE;
+ cl->disableBackground = FALSE;
+
+ cl->compStreamInited = FALSE;
+ cl->compStream.total_in = 0;
+ cl->compStream.total_out = 0;
+ cl->compStream.zalloc = Z_NULL;
+ cl->compStream.zfree = Z_NULL;
+ cl->compStream.opaque = Z_NULL;
+
+ cl->zlibCompressLevel = 5;
+
+ sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion,
+ rfbProtocolMinorVersion);
+
+ if (WriteExact(cl, pv, sz_rfbProtocolVersionMsg) < 0) {
+ rfbLogPerror("rfbNewClient: write");
+ rfbCloseClient(cl);
+ /* TODO: memory leak here (cl is never freed)
+ * can rfbClientConnectionGone called at this time?
+ * tim@tjansen.de
+ */
+ return NULL;
+ }
+ }
+
+ cl->clientData = NULL;
+ cl->clientGoneHook = doNothingWithClient;
+ cl->negotiationFinishedHook = doNothingWithClient;
+ switch (cl->screen->newClientHook(cl)) {
+ case RFB_CLIENT_ON_HOLD:
+ cl->onHold = TRUE;
+ break;
+ case RFB_CLIENT_ACCEPT:
+ cl->onHold = FALSE;
+ break;
+ case RFB_CLIENT_REFUSE:
+ rfbCloseClient(cl);
+ rfbClientConnectionGone(cl);
+ cl = NULL;
+ break;
+ }
+ return cl;
+}
+
+rfbClientPtr
+rfbNewClient(rfbScreen,sock)
+ rfbScreenInfoPtr rfbScreen;
+ int sock;
+{
+ return(rfbNewTCPOrUDPClient(rfbScreen,sock,FALSE));
+}
+
+rfbClientPtr
+rfbNewUDPClient(rfbScreen)
+ rfbScreenInfoPtr rfbScreen;
+{
+ return((rfbScreen->udpClient=
+ rfbNewTCPOrUDPClient(rfbScreen,rfbScreen->udpSock,TRUE)));
+}
+
+/*
+ * rfbClientConnectionGone is called from sockets.c just after a connection
+ * has gone away.
+ */
+
+void
+rfbClientConnectionGone(cl)
+ rfbClientPtr cl;
+{
+ int i;
+
+ LOCK(rfbClientListMutex);
+
+ if (cl->prev)
+ cl->prev->next = cl->next;
+ else
+ cl->screen->rfbClientHead = cl->next;
+ if (cl->next)
+ cl->next->prev = cl->prev;
+
+#ifdef HAVE_PTHREADS
+ LOCK(cl->refCountMutex);
+ if(cl->refCount) {
+ UNLOCK(cl->refCountMutex);
+ WAIT(cl->deleteCond,cl->refCountMutex);
+ } else {
+ UNLOCK(cl->refCountMutex);
+ }
+#endif
+
+ if(cl->sock>=0)
+ FD_CLR(cl->sock,&(cl->screen->allFds));
+
+ cl->clientGoneHook(cl);
+
+ rfbLog("Client %s gone\n",cl->host);
+ free(cl->host);
+
+ /* Release the compression state structures if any. */
+ if ( cl->compStreamInited ) {
+ deflateEnd( &(cl->compStream) );
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (cl->zsActive[i])
+ deflateEnd(&cl->zsStruct[i]);
+ }
+
+ if (pointerClient == cl)
+ pointerClient = NULL;
+
+ sraRgnDestroy(cl->modifiedRegion);
+
+ UNLOCK(rfbClientListMutex);
+
+ if (cl->translateLookupTable) free(cl->translateLookupTable);
+
+ TINI_COND(cl->updateCond);
+ TINI_MUTEX(cl->updateMutex);
+
+ LOCK(cl->outputMutex);
+ TINI_MUTEX(cl->outputMutex);
+
+#ifdef CORBA
+ destroyConnection(cl);
+#endif
+
+ rfbPrintStats(cl);
+
+ free(cl);
+}
+
+
+/*
+ * rfbProcessClientMessage is called when there is data to read from a client.
+ */
+
+void
+rfbProcessClientMessage(cl)
+ rfbClientPtr cl;
+{
+ switch (cl->state) {
+ case RFB_PROTOCOL_VERSION:
+ rfbProcessClientProtocolVersion(cl);
+ return;
+ case RFB_AUTHENTICATION:
+ rfbAuthProcessClientMessage(cl);
+ return;
+ case RFB_INITIALISATION:
+ rfbProcessClientInitMessage(cl);
+ return;
+ default:
+ rfbProcessClientNormalMessage(cl);
+ return;
+ }
+}
+
+
+/*
+ * rfbProcessClientProtocolVersion is called when the client sends its
+ * protocol version.
+ */
+
+static void
+rfbProcessClientProtocolVersion(cl)
+ rfbClientPtr cl;
+{
+ rfbProtocolVersionMsg pv;
+ int n, major_, minor_;
+ char failureReason[256];
+
+ if ((n = ReadExact(cl, pv, sz_rfbProtocolVersionMsg)) <= 0) {
+ if (n == 0)
+ rfbLog("rfbProcessClientProtocolVersion: client gone\n");
+ else
+ rfbLogPerror("rfbProcessClientProtocolVersion: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ pv[sz_rfbProtocolVersionMsg] = 0;
+ if (sscanf(pv,rfbProtocolVersionFormat,&major_,&minor_) != 2) {
+ rfbLog("rfbProcessClientProtocolVersion: not a valid RFB client\n");
+ rfbCloseClient(cl);
+ return;
+ }
+ rfbLog("Protocol version %d.%d\n", major_, minor_);
+
+ if (major_ != rfbProtocolMajorVersion) {
+ /* Major version mismatch - send a ConnFailed message */
+
+ rfbLog("Major version mismatch\n");
+ sprintf(failureReason,
+ "RFB protocol version mismatch - server %d.%d, client %d.%d",
+ rfbProtocolMajorVersion,rfbProtocolMinorVersion,major_,minor_);
+ rfbClientConnFailed(cl, failureReason);
+ return;
+ }
+
+ if (minor_ != rfbProtocolMinorVersion) {
+ /* Minor version mismatch - warn but try to continue */
+ rfbLog("Ignoring minor version mismatch\n");
+ }
+
+ rfbAuthNewClient(cl);
+}
+
+
+/*
+ * rfbClientConnFailed is called when a client connection has failed either
+ * because it talks the wrong protocol or it has failed authentication.
+ */
+
+void
+rfbClientConnFailed(cl, reason)
+ rfbClientPtr cl;
+ char *reason;
+{
+ char *buf;
+ int len = strlen(reason);
+
+ buf = (char *)malloc(8 + len);
+ ((CARD32 *)buf)[0] = Swap32IfLE(rfbConnFailed);
+ ((CARD32 *)buf)[1] = Swap32IfLE(len);
+ memcpy(buf + 8, reason, len);
+
+ if (WriteExact(cl, buf, 8 + len) < 0)
+ rfbLogPerror("rfbClientConnFailed: write");
+ free(buf);
+ rfbCloseClient(cl);
+}
+
+
+/*
+ * rfbProcessClientInitMessage is called when the client sends its
+ * initialisation message.
+ */
+
+static void
+rfbProcessClientInitMessage(cl)
+ rfbClientPtr cl;
+{
+ rfbClientInitMsg ci;
+ char buf[256];
+ rfbServerInitMsg *si = (rfbServerInitMsg *)buf;
+ int len, n;
+ rfbClientIteratorPtr iterator;
+ rfbClientPtr otherCl;
+
+ if ((n = ReadExact(cl, (char *)&ci,sz_rfbClientInitMsg)) <= 0) {
+ if (n == 0)
+ rfbLog("rfbProcessClientInitMessage: client gone\n");
+ else
+ rfbLogPerror("rfbProcessClientInitMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ si->framebufferWidth = Swap16IfLE(cl->screen->width);
+ si->framebufferHeight = Swap16IfLE(cl->screen->height);
+ si->format = cl->screen->rfbServerFormat;
+ si->format.redMax = Swap16IfLE(si->format.redMax);
+ si->format.greenMax = Swap16IfLE(si->format.greenMax);
+ si->format.blueMax = Swap16IfLE(si->format.blueMax);
+
+ if (strlen(cl->screen->desktopName) > 128) /* sanity check on desktop name len */
+ ((char*)cl->screen->desktopName)[128] = 0;
+
+ strcpy(buf + sz_rfbServerInitMsg, cl->screen->desktopName);
+ len = strlen(buf + sz_rfbServerInitMsg);
+ si->nameLength = Swap32IfLE(len);
+
+ if (WriteExact(cl, buf, sz_rfbServerInitMsg + len) < 0) {
+ rfbLogPerror("rfbProcessClientInitMessage: write");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ cl->state = RFB_NORMAL;
+
+ if (!cl->reverseConnection &&
+ (cl->screen->rfbNeverShared || (!cl->screen->rfbAlwaysShared && !ci.shared))) {
+
+ if (cl->screen->rfbDontDisconnect) {
+ iterator = rfbGetClientIterator(cl->screen);
+ while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) {
+ if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) {
+ rfbLog("-dontdisconnect: Not shared & existing client\n");
+ rfbLog(" refusing new client %s\n", cl->host);
+ rfbCloseClient(cl);
+ rfbReleaseClientIterator(iterator);
+ return;
+ }
+ }
+ rfbReleaseClientIterator(iterator);
+ } else {
+ iterator = rfbGetClientIterator(cl->screen);
+ while ((otherCl = rfbClientIteratorNext(iterator)) != NULL) {
+ if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) {
+ rfbLog("Not shared - closing connection to client %s\n",
+ otherCl->host);
+ rfbCloseClient(otherCl);
+ }
+ }
+ rfbReleaseClientIterator(iterator);
+ }
+ }
+}
+
+
+/*
+ * rfbProcessClientNormalMessage is called when the client has sent a normal
+ * protocol message.
+ */
+
+static void
+rfbProcessClientNormalMessage(cl)
+ rfbClientPtr cl;
+{
+ int n=0;
+ rfbClientToServerMsg msg;
+ char *str;
+
+ if ((n = ReadExact(cl, (char *)&msg, 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ switch (msg.type) {
+
+ case rfbSetPixelFormat:
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbSetPixelFormatMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel;
+ cl->format.depth = msg.spf.format.depth;
+ cl->format.bigEndian = (msg.spf.format.bigEndian ? TRUE : FALSE);
+ cl->format.trueColour = (msg.spf.format.trueColour ? TRUE : FALSE);
+ cl->format.redMax = Swap16IfLE(msg.spf.format.redMax);
+ cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax);
+ cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax);
+ cl->format.redShift = msg.spf.format.redShift;
+ cl->format.greenShift = msg.spf.format.greenShift;
+ cl->format.blueShift = msg.spf.format.blueShift;
+
+ cl->readyForSetColourMapEntries = TRUE;
+ cl->screen->setTranslateFunction(cl);
+
+ return;
+
+
+ case rfbFixColourMapEntries:
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbFixColourMapEntriesMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+ rfbLog("rfbProcessClientNormalMessage: %s",
+ "FixColourMapEntries unsupported\n");
+ rfbCloseClient(cl);
+ return;
+
+
+ case rfbSetEncodings:
+ {
+ int i;
+ CARD32 enc;
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbSetEncodingsMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings);
+
+ cl->preferredEncoding = -1;
+ cl->useCopyRect = FALSE;
+ cl->enableCursorShapeUpdates = FALSE;
+ cl->enableLastRectEncoding = FALSE;
+ cl->disableBackground = FALSE;
+
+ for (i = 0; i < msg.se.nEncodings; i++) {
+ if ((n = ReadExact(cl, (char *)&enc, 4)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+ enc = Swap32IfLE(enc);
+
+ switch (enc) {
+
+ case rfbEncodingCopyRect:
+ cl->useCopyRect = TRUE;
+ break;
+ case rfbEncodingRaw:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using raw encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingRRE:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using rre encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingCoRRE:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using CoRRE encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingHextile:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using hextile encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingZlib:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using zlib encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingTight:
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = enc;
+ rfbLog("Using tight encoding for client %s\n",
+ cl->host);
+ }
+ break;
+ case rfbEncodingXCursor:
+ if (cl->enableSoftCursorUpdates)
+ break;
+ if(!cl->screen->dontConvertRichCursorToXCursor) {
+ rfbLog("Enabling X-style cursor updates for client %s\n",
+ cl->host);
+ cl->enableCursorShapeUpdates = TRUE;
+ cl->cursorWasChanged = TRUE;
+ }
+ break;
+ case rfbEncodingRichCursor:
+ rfbLog("Enabling full-color cursor updates for client "
+ "%s\n", cl->host);
+ if (cl->enableSoftCursorUpdates)
+ break;
+ cl->enableCursorShapeUpdates = TRUE;
+ cl->useRichCursorEncoding = TRUE;
+ cl->cursorWasChanged = TRUE;
+ break;
+ case rfbEncodingSoftCursor:
+ rfbLog("Enabling soft cursor updates for client "
+ "%s\n", cl->host);
+ cl->enableSoftCursorUpdates = TRUE;
+ cl->cursorWasChanged = TRUE;
+ cl->cursorWasMoved = TRUE;
+ cl->enableCursorShapeUpdates = FALSE;
+ cl->useRichCursorEncoding = FALSE;
+ break;
+ case rfbEncodingLastRect:
+ if (!cl->enableLastRectEncoding) {
+ rfbLog("Enabling LastRect protocol extension for client "
+ "%s\n", cl->host);
+ cl->enableLastRectEncoding = TRUE;
+ }
+ break;
+ case rfbEncodingBackground:
+ rfbLog("Disabling background for client "
+ "%s\n", cl->host);
+ cl->disableBackground = TRUE;
+ break;
+#ifdef BACKCHANNEL
+ case rfbEncodingBackChannel:
+ if (!cl->enableBackChannel) {
+ rfbLog("Enabling BackChannel protocol extension for "
+ "client %s\n", cl->host);
+ cl->enableBackChannel = TRUE;
+ }
+ break;
+#endif
+ default:
+ if ( enc >= (CARD32)rfbEncodingCompressLevel0 &&
+ enc <= (CARD32)rfbEncodingCompressLevel9 ) {
+ cl->zlibCompressLevel = enc & 0x0F;
+ cl->tightCompressLevel = enc & 0x0F;
+ rfbLog("Using compression level %d for client %s\n",
+ cl->tightCompressLevel, cl->host);
+ } else if ( enc >= (CARD32)rfbEncodingQualityLevel0 &&
+ enc <= (CARD32)rfbEncodingQualityLevel9 ) {
+ cl->tightQualityLevel = enc & 0x0F;
+ rfbLog("Using image quality level %d for client %s\n",
+ cl->tightQualityLevel, cl->host);
+ } else
+ rfbLog("rfbProcessClientNormalMessage: ignoring unknown "
+ "encoding type %d\n", (int)enc);
+ }
+ }
+
+ if (cl->preferredEncoding == -1) {
+ cl->preferredEncoding = rfbEncodingRaw;
+ }
+
+ cl->negotiationFinishedHook(cl);
+
+ return;
+ }
+
+
+ case rfbFramebufferUpdateRequest:
+ {
+ sraRegionPtr tmpRegion;
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ tmpRegion =
+ sraRgnCreateRect(Swap16IfLE(msg.fur.x),
+ Swap16IfLE(msg.fur.y),
+ Swap16IfLE(msg.fur.x)+Swap16IfLE(msg.fur.w),
+ Swap16IfLE(msg.fur.y)+Swap16IfLE(msg.fur.h));
+
+ LOCK(cl->updateMutex);
+ sraRgnOr(cl->requestedRegion,tmpRegion);
+
+ if (!cl->readyForSetColourMapEntries) {
+ /* client hasn't sent a SetPixelFormat so is using server's */
+ cl->readyForSetColourMapEntries = TRUE;
+ if (!cl->format.trueColour) {
+ if (!rfbSetClientColourMap(cl, 0, 0)) {
+ sraRgnDestroy(tmpRegion);
+ UNLOCK(cl->updateMutex);
+ return;
+ }
+ }
+ }
+
+ if (!msg.fur.incremental) {
+ sraRgnOr(cl->modifiedRegion,tmpRegion);
+ sraRgnSubtract(cl->copyRegion,tmpRegion);
+ }
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+
+ sraRgnDestroy(tmpRegion);
+
+ return;
+ }
+
+ case rfbKeyEvent:
+
+ cl->rfbKeyEventsRcvd++;
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbKeyEventMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ cl->screen->kbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), cl);
+ return;
+
+
+ case rfbPointerEvent:
+
+ cl->rfbPointerEventsRcvd++;
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbPointerEventMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ if (pointerClient && (pointerClient != cl))
+ return;
+
+ if (msg.pe.buttonMask == 0)
+ pointerClient = NULL;
+ else
+ pointerClient = cl;
+
+ cl->screen->ptrAddEvent(msg.pe.buttonMask,
+ Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl);
+ return;
+
+
+ case rfbClientCutText:
+
+ if ((n = ReadExact(cl, ((char *)&msg) + 1,
+ sz_rfbClientCutTextMsg - 1)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ rfbCloseClient(cl);
+ return;
+ }
+
+ msg.cct.length = Swap32IfLE(msg.cct.length);
+
+ str = (char *)malloc(msg.cct.length);
+
+ if ((n = ReadExact(cl, str, msg.cct.length)) <= 0) {
+ if (n != 0)
+ rfbLogPerror("rfbProcessClientNormalMessage: read");
+ free(str);
+ rfbCloseClient(cl);
+ return;
+ }
+
+ cl->screen->setXCutText(str, msg.cct.length, cl);
+
+ free(str);
+ return;
+
+
+ default:
+
+ rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n",
+ msg.type);
+ rfbLog(" ... closing connection\n");
+ rfbCloseClient(cl);
+ return;
+ }
+}
+
+
+/*
+ * rfbSendPing - send an empty framebuffer request
+ */
+
+Bool
+rfbSendPing(cl)
+ rfbClientPtr cl;
+{
+ rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf;
+ cl->rfbFramebufferUpdateMessagesSent++;
+ fu->type = rfbFramebufferUpdate;
+ fu->nRects = Swap16IfLE((CARD16)0);
+ cl->ublen = sz_rfbFramebufferUpdateMsg;
+ return TRUE;
+}
+
+
+/*
+ * rfbSendFramebufferUpdate - send the currently pending framebuffer update to
+ * the RFB client.
+ * givenUpdateRegion is not changed.
+ */
+
+Bool
+rfbSendFramebufferUpdate(cl, givenUpdateRegion)
+ rfbClientPtr cl;
+ sraRegionPtr givenUpdateRegion;
+{
+ sraRectangleIterator* i;
+ sraRect rect;
+ int nUpdateRegionRects;
+ rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf;
+ sraRegionPtr updateRegion,updateCopyRegion,tmpRegion;
+ int dx, dy;
+ Bool sendCursorShape = FALSE;
+ int sendSoftCursorRects = 0;
+
+ if(cl->screen->displayHook)
+ cl->screen->displayHook(cl);
+
+ /*
+ * If this client understands cursor shape updates, cursor should be
+ * removed from the framebuffer. Otherwise, make sure it's put up.
+ */
+
+ if (cl->enableCursorShapeUpdates) {
+ if (cl->screen->cursorIsDrawn) {
+ rfbUndrawCursor(cl->screen);
+ }
+ if (!cl->screen->cursorIsDrawn && cl->cursorWasChanged &&
+ cl->readyForSetColourMapEntries)
+ sendCursorShape = TRUE;
+ }
+ else if (cl->enableSoftCursorUpdates) {
+ if (cl->screen->cursorIsDrawn) {
+ rfbUndrawCursor(cl->screen);
+ }
+ if (cl->cursorWasChanged)
+ sendSoftCursorRects=2;
+ else if (cl->cursorWasMoved)
+ sendSoftCursorRects=1;
+ } else {
+ if (!cl->screen->cursorIsDrawn) {
+ rfbDrawCursor(cl->screen);
+ }
+ }
+
+ LOCK(cl->updateMutex);
+
+ /*
+ * The modifiedRegion may overlap the destination copyRegion. We remove
+ * any overlapping bits from the copyRegion (since they'd only be
+ * overwritten anyway).
+ */
+
+ sraRgnSubtract(cl->copyRegion,cl->modifiedRegion);
+
+ /*
+ * The client is interested in the region requestedRegion. The region
+ * which should be updated now is the intersection of requestedRegion
+ * and the union of modifiedRegion and copyRegion. If it's empty then
+ * no update is needed.
+ */
+
+ updateRegion = sraRgnCreateRgn(givenUpdateRegion);
+ sraRgnOr(updateRegion,cl->copyRegion);
+ if(!sraRgnAnd(updateRegion,cl->requestedRegion) &&
+ !(sendCursorShape || sendSoftCursorRects)) {
+ sraRgnDestroy(updateRegion);
+ UNLOCK(cl->updateMutex);
+ return TRUE;
+ }
+
+ /*
+ * We assume that the client doesn't have any pixel data outside the
+ * requestedRegion. In other words, both the source and destination of a
+ * copy must lie within requestedRegion. So the region we can send as a
+ * copy is the intersection of the copyRegion with both the requestedRegion
+ * and the requestedRegion translated by the amount of the copy. We set
+ * updateCopyRegion to this.
+ */
+
+ updateCopyRegion = sraRgnCreateRgn(cl->copyRegion);
+ sraRgnAnd(updateCopyRegion,cl->requestedRegion);
+ tmpRegion = sraRgnCreateRgn(cl->requestedRegion);
+ sraRgnOffset(tmpRegion,cl->copyDX,cl->copyDY);
+ sraRgnAnd(updateCopyRegion,tmpRegion);
+ sraRgnDestroy(tmpRegion);
+ dx = cl->copyDX;
+ dy = cl->copyDY;
+
+ /*
+ * Next we remove updateCopyRegion from updateRegion so that updateRegion
+ * is the part of this update which is sent as ordinary pixel data (i.e not
+ * a copy).
+ */
+
+ sraRgnSubtract(updateRegion,updateCopyRegion);
+
+ /*
+ * Finally we leave modifiedRegion to be the remainder (if any) of parts of
+ * the screen which are modified but outside the requestedRegion. We also
+ * empty both the requestedRegion and the copyRegion - note that we never
+ * carry over a copyRegion for a future update.
+ */
+
+
+ sraRgnOr(cl->modifiedRegion,cl->copyRegion);
+ sraRgnSubtract(cl->modifiedRegion,updateRegion);
+ sraRgnSubtract(cl->modifiedRegion,updateCopyRegion);
+
+ /* sraRgnMakeEmpty(cl->requestedRegion); */
+ sraRgnMakeEmpty(cl->copyRegion);
+ cl->copyDX = 0;
+ cl->copyDY = 0;
+
+ UNLOCK(cl->updateMutex);
+
+ /*
+ * Now send the update.
+ */
+
+ cl->rfbFramebufferUpdateMessagesSent++;
+
+ if (cl->preferredEncoding == rfbEncodingCoRRE) {
+ nUpdateRegionRects = 0;
+
+ for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
+ int x = rect.x1;
+ int y = rect.y1;
+ int w = rect.x2 - x;
+ int h = rect.y2 - y;
+ nUpdateRegionRects += (((w-1) / cl->correMaxWidth + 1)
+ * ((h-1) / cl->correMaxHeight + 1));
+ }
+ } else if (cl->preferredEncoding == rfbEncodingZlib) {
+ nUpdateRegionRects = 0;
+
+ for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
+ int x = rect.x1;
+ int y = rect.y1;
+ int w = rect.x2 - x;
+ int h = rect.y2 - y;
+ nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1);
+ }
+ } else if (cl->preferredEncoding == rfbEncodingTight) {
+ nUpdateRegionRects = 0;
+
+ for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
+ int x = rect.x1;
+ int y = rect.y1;
+ int w = rect.x2 - x;
+ int h = rect.y2 - y;
+ int n = rfbNumCodedRectsTight(cl, x, y, w, h);
+ if (n == 0) {
+ nUpdateRegionRects = 0xFFFF;
+ break;
+ }
+ nUpdateRegionRects += n;
+ }
+ } else {
+ nUpdateRegionRects = sraRgnCountRects(updateRegion);
+ }
+
+ fu->type = rfbFramebufferUpdate;
+ if (nUpdateRegionRects != 0xFFFF) {
+ fu->nRects = Swap16IfLE((CARD16)(sraRgnCountRects(updateCopyRegion)
+ + nUpdateRegionRects
+ + !!sendCursorShape + sendSoftCursorRects));
+ } else {
+ fu->nRects = 0xFFFF;
+ }
+ cl->ublen = sz_rfbFramebufferUpdateMsg;
+
+ if (sendCursorShape) {
+ cl->cursorWasChanged = FALSE;
+ if (!rfbSendCursorShape(cl)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ }
+
+ if (sendSoftCursorRects) {
+ if (!rfbSendSoftCursor(cl, cl->cursorWasChanged)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ cl->cursorWasChanged = FALSE;
+ cl->cursorWasMoved = FALSE;
+ }
+
+ if (!sraRgnEmpty(updateCopyRegion)) {
+ if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) {
+ sraRgnDestroy(updateRegion);
+ sraRgnDestroy(updateCopyRegion);
+ return FALSE;
+ }
+ }
+
+ sraRgnDestroy(updateCopyRegion);
+
+ for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){
+ int x = rect.x1;
+ int y = rect.y1;
+ int w = rect.x2 - x;
+ int h = rect.y2 - y;
+
+ cl->rfbRawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader
+ + w * (cl->format.bitsPerPixel / 8) * h);
+
+ switch (cl->preferredEncoding) {
+ case rfbEncodingRaw:
+ if (!rfbSendRectEncodingRaw(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ case rfbEncodingRRE:
+ if (!rfbSendRectEncodingRRE(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ case rfbEncodingCoRRE:
+ if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ case rfbEncodingHextile:
+ if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ case rfbEncodingZlib:
+ if (!rfbSendRectEncodingZlib(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ case rfbEncodingTight:
+ if (!rfbSendRectEncodingTight(cl, x, y, w, h)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+ break;
+ }
+ }
+
+ if ( nUpdateRegionRects == 0xFFFF &&
+ !rfbSendLastRectMarker(cl) ) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+
+ if (!rfbSendUpdateBuf(cl)) {
+ sraRgnDestroy(updateRegion);
+ return FALSE;
+ }
+
+ sraRgnDestroy(updateRegion);
+ return TRUE;
+}
+
+
+/*
+ * Send the copy region as a string of CopyRect encoded rectangles.
+ * The only slightly tricky thing is that we should send the messages in
+ * the correct order so that an earlier CopyRect will not corrupt the source
+ * of a later one.
+ */
+
+Bool
+rfbSendCopyRegion(cl, reg, dx, dy)
+ rfbClientPtr cl;
+ sraRegionPtr reg;
+ int dx, dy;
+{
+ int x, y, w, h;
+ rfbFramebufferUpdateRectHeader rect;
+ rfbCopyRect cr;
+ sraRectangleIterator* i;
+ sraRect rect1;
+
+ /* printf("copyrect: "); sraRgnPrint(reg); putchar('\n');fflush(stdout); */
+ i = sraRgnGetReverseIterator(reg,dx>0,dy>0);
+
+ while(sraRgnIteratorNext(i,&rect1)) {
+ x = rect1.x1;
+ y = rect1.y1;
+ w = rect1.x2 - x;
+ h = rect1.y2 - y;
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingCopyRect);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cr.srcX = Swap16IfLE(x - dx);
+ cr.srcY = Swap16IfLE(y - dy);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&cr, sz_rfbCopyRect);
+ cl->ublen += sz_rfbCopyRect;
+
+ cl->rfbRectanglesSent[rfbEncodingCopyRect]++;
+ cl->rfbBytesSent[rfbEncodingCopyRect]
+ += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect;
+
+ }
+
+ return TRUE;
+}
+
+/*
+ * Send a given rectangle in raw encoding (rfbEncodingRaw).
+ */
+
+Bool
+rfbSendRectEncodingRaw(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+ int nlines;
+ int bytesPerLine = w * (cl->format.bitsPerPixel / 8);
+ char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
+ + (x * (cl->screen->bitsPerPixel / 8)));
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingRaw);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cl->rfbRectanglesSent[rfbEncodingRaw]++;
+ cl->rfbBytesSent[rfbEncodingRaw]
+ += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h;
+
+ nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;
+
+ while (TRUE) {
+ if (nlines > h)
+ nlines = h;
+
+ (*cl->translateFn)(cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat),
+ &cl->format, fbptr, &cl->updateBuf[cl->ublen],
+ cl->screen->paddedWidthInBytes, w, nlines);
+
+ cl->ublen += nlines * bytesPerLine;
+ h -= nlines;
+
+ if (h == 0) /* rect fitted in buffer, do next one */
+ return TRUE;
+
+ /* buffer full - flush partial rect and do another nlines */
+
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+
+ fbptr += (cl->screen->paddedWidthInBytes * nlines);
+
+ nlines = (UPDATE_BUF_SIZE - cl->ublen) / bytesPerLine;
+ if (nlines == 0) {
+ rfbLog("rfbSendRectEncodingRaw: send buffer too small for %d "
+ "bytes per line\n", bytesPerLine);
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+ }
+}
+
+
+
+/*
+ * Send an empty rectangle with encoding field set to value of
+ * rfbEncodingLastRect to notify client that this is the last
+ * rectangle in framebuffer update ("LastRect" extension of RFB
+ * protocol).
+ */
+
+Bool
+rfbSendLastRectMarker(cl)
+ rfbClientPtr cl;
+{
+ rfbFramebufferUpdateRectHeader rect;
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.encoding = Swap32IfLE(rfbEncodingLastRect);
+ rect.r.x = 0;
+ rect.r.y = 0;
+ rect.r.w = 0;
+ rect.r.h = 0;
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cl->rfbLastRectMarkersSent++;
+ cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader;
+
+ return TRUE;
+}
+
+
+/*
+ * Send the contents of cl->updateBuf. Returns 1 if successful, -1 if
+ * not (errno should be set).
+ */
+
+Bool
+rfbSendUpdateBuf(cl)
+ rfbClientPtr cl;
+{
+ if(cl->sock<0)
+ return FALSE;
+
+ if (WriteExact(cl, cl->updateBuf, cl->ublen) < 0) {
+ rfbLogPerror("rfbSendUpdateBuf: write");
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+
+ cl->ublen = 0;
+ return TRUE;
+}
+
+/*
+ * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the
+ * client, using values from the currently installed colormap.
+ */
+
+Bool
+rfbSendSetColourMapEntries(cl, firstColour, nColours)
+ rfbClientPtr cl;
+ int firstColour;
+ int nColours;
+{
+ char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2];
+ rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf;
+ CARD16 *rgb = (CARD16 *)(&buf[sz_rfbSetColourMapEntriesMsg]);
+ rfbColourMap* cm = &cl->screen->colourMap;
+
+ int i, len;
+
+ scme->type = rfbSetColourMapEntries;
+
+ scme->firstColour = Swap16IfLE(firstColour);
+ scme->nColours = Swap16IfLE(nColours);
+
+ len = sz_rfbSetColourMapEntriesMsg;
+
+ for (i = 0; i < nColours; i++) {
+ if(i<(int)cm->count) {
+ if(cm->is16) {
+ rgb[i*3] = Swap16IfLE(cm->data.shorts[i*3]);
+ rgb[i*3+1] = Swap16IfLE(cm->data.shorts[i*3+1]);
+ rgb[i*3+2] = Swap16IfLE(cm->data.shorts[i*3+2]);
+ } else {
+ rgb[i*3] = Swap16IfLE(cm->data.bytes[i*3]);
+ rgb[i*3+1] = Swap16IfLE(cm->data.bytes[i*3+1]);
+ rgb[i*3+2] = Swap16IfLE(cm->data.bytes[i*3+2]);
+ }
+ }
+ }
+
+ len += nColours * 3 * 2;
+
+ if (WriteExact(cl, buf, len) < 0) {
+ rfbLogPerror("rfbSendSetColourMapEntries: write");
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * rfbSendBell sends a Bell message to all the clients.
+ */
+
+void
+rfbSendBell(rfbScreenInfoPtr rfbScreen)
+{
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl;
+ rfbBellMsg b;
+
+ i = rfbGetClientIterator(rfbScreen);
+ while((cl=rfbClientIteratorNext(i))) {
+ b.type = rfbBell;
+ if (WriteExact(cl, (char *)&b, sz_rfbBellMsg) < 0) {
+ rfbLogPerror("rfbSendBell: write");
+ rfbCloseClient(cl);
+ }
+ }
+ rfbReleaseClientIterator(i);
+}
+
+
+/*
+ * rfbSendServerCutText sends a ServerCutText message to all the clients.
+ */
+
+void
+rfbSendServerCutText(rfbScreenInfoPtr rfbScreen,char *str, int len)
+{
+ rfbClientPtr cl;
+ rfbServerCutTextMsg sct;
+ rfbClientIteratorPtr iterator;
+
+ iterator = rfbGetClientIterator(rfbScreen);
+ while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
+ sct.type = rfbServerCutText;
+ sct.length = Swap32IfLE(len);
+ if (WriteExact(cl, (char *)&sct,
+ sz_rfbServerCutTextMsg) < 0) {
+ rfbLogPerror("rfbSendServerCutText: write");
+ rfbCloseClient(cl);
+ continue;
+ }
+ if (WriteExact(cl, str, len) < 0) {
+ rfbLogPerror("rfbSendServerCutText: write");
+ rfbCloseClient(cl);
+ }
+ }
+ rfbReleaseClientIterator(iterator);
+}
+
+/*****************************************************************************
+ *
+ * UDP can be used for keyboard and pointer events when the underlying
+ * network is highly reliable. This is really here to support ORL's
+ * videotile, whose TCP implementation doesn't like sending lots of small
+ * packets (such as 100s of pen readings per second!).
+ */
+
+unsigned char ptrAcceleration = 50;
+
+void
+rfbNewUDPConnection(rfbScreenInfoPtr rfbScreen, int sock)
+{
+ if (write(sock, &ptrAcceleration, 1) < 0) {
+ rfbLogPerror("rfbNewUDPConnection: write");
+ }
+}
+
+/*
+ * Because UDP is a message based service, we can't read the first byte and
+ * then the rest of the packet separately like we do with TCP. We will always
+ * get a whole packet delivered in one go, so we ask read() for the maximum
+ * number of bytes we can possibly get.
+ */
+
+void
+rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen)
+{
+ int n;
+ rfbClientPtr cl=rfbScreen->udpClient;
+ rfbClientToServerMsg msg;
+
+ if((!cl) || cl->onHold)
+ return;
+
+ if ((n = read(rfbScreen->udpSock, (char *)&msg, sizeof(msg))) <= 0) {
+ if (n < 0) {
+ rfbLogPerror("rfbProcessUDPInput: read");
+ }
+ rfbDisconnectUDPSock(rfbScreen);
+ return;
+ }
+
+ switch (msg.type) {
+
+ case rfbKeyEvent:
+ if (n != sz_rfbKeyEventMsg) {
+ rfbLog("rfbProcessUDPInput: key event incorrect length\n");
+ rfbDisconnectUDPSock(rfbScreen);
+ return;
+ }
+ cl->screen->kbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), cl);
+ break;
+
+ case rfbPointerEvent:
+ if (n != sz_rfbPointerEventMsg) {
+ rfbLog("rfbProcessUDPInput: ptr event incorrect length\n");
+ rfbDisconnectUDPSock(rfbScreen);
+ return;
+ }
+ cl->screen->ptrAddEvent(msg.pe.buttonMask,
+ Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), cl);
+ break;
+
+ default:
+ rfbLog("rfbProcessUDPInput: unknown message type %d\n",
+ msg.type);
+ rfbDisconnectUDPSock(rfbScreen);
+ }
+}
+
+#ifdef BACKCHANNEL
+void rfbSendBackChannel(rfbScreenInfoPtr rfbScreen,char* str,int len)
+{
+ rfbClientPtr cl;
+ rfbBackChannelMsg sct;
+ rfbClientIteratorPtr iterator;
+
+ iterator = rfbGetClientIterator(rfbScreen);
+ while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
+ if (cl->enableBackChannel) {
+ sct.type = rfbBackChannel;
+ sct.length = Swap32IfLE(len);
+ if (WriteExact(cl, (char *)&sct,
+ sz_rfbBackChannelMsg) < 0) {
+ rfbLogPerror("rfbSendBackChannel: write");
+ rfbCloseClient(cl);
+ continue;
+ }
+ if (WriteExact(cl, str, len) < 0) {
+ rfbLogPerror("rfbSendBackChannel: write");
+ rfbCloseClient(cl);
+ }
+ }
+ }
+ rfbReleaseClientIterator(iterator);
+}
+#endif
diff --git a/krfb/libvncserver/rre.c b/krfb/libvncserver/rre.c
new file mode 100644
index 00000000..9a552cb0
--- /dev/null
+++ b/krfb/libvncserver/rre.c
@@ -0,0 +1,322 @@
+/*
+ * rre.c
+ *
+ * Routines to implement Rise-and-Run-length Encoding (RRE). This
+ * code is based on krw's original javatel rfbserver.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+/*
+ * rreBeforeBuf contains pixel data in the client's format.
+ * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is
+ * larger than the raw data or if it exceeds rreAfterBufSize then
+ * raw encoding is used instead.
+ */
+
+static int rreBeforeBufSize = 0;
+static char *rreBeforeBuf = NULL;
+
+static int rreAfterBufSize = 0;
+static char *rreAfterBuf = NULL;
+static int rreAfterBufLen;
+
+static int subrectEncode8(CARD8 *data, int w, int h);
+static int subrectEncode16(CARD16 *data, int w, int h);
+static int subrectEncode32(CARD32 *data, int w, int h);
+static CARD32 getBgColour(char *data, int size, int bpp);
+
+
+/*
+ * rfbSendRectEncodingRRE - send a given rectangle using RRE encoding.
+ */
+
+Bool
+rfbSendRectEncodingRRE(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+ rfbRREHeader hdr;
+ int nSubrects;
+ int i;
+ char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
+ + (x * (cl->screen->bitsPerPixel / 8)));
+
+ int maxRawSize = (cl->screen->width * cl->screen->height
+ * (cl->format.bitsPerPixel / 8));
+
+ if (rreBeforeBufSize < maxRawSize) {
+ rreBeforeBufSize = maxRawSize;
+ if (rreBeforeBuf == NULL)
+ rreBeforeBuf = (char *)malloc(rreBeforeBufSize);
+ else
+ rreBeforeBuf = (char *)realloc(rreBeforeBuf, rreBeforeBufSize);
+ }
+
+ if (rreAfterBufSize < maxRawSize) {
+ rreAfterBufSize = maxRawSize;
+ if (rreAfterBuf == NULL)
+ rreAfterBuf = (char *)malloc(rreAfterBufSize);
+ else
+ rreAfterBuf = (char *)realloc(rreAfterBuf, rreAfterBufSize);
+ }
+
+ (*cl->translateFn)(cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat),
+ &cl->format, fbptr, rreBeforeBuf,
+ cl->screen->paddedWidthInBytes, w, h);
+
+ switch (cl->format.bitsPerPixel) {
+ case 8:
+ nSubrects = subrectEncode8((CARD8 *)rreBeforeBuf, w, h);
+ break;
+ case 16:
+ nSubrects = subrectEncode16((CARD16 *)rreBeforeBuf, w, h);
+ break;
+ case 32:
+ nSubrects = subrectEncode32((CARD32 *)rreBeforeBuf, w, h);
+ break;
+ default:
+ rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel);
+ exit(1);
+ }
+
+ if (nSubrects < 0) {
+
+ /* RRE encoding was too large, use raw */
+
+ return rfbSendRectEncodingRaw(cl, x, y, w, h);
+ }
+
+ cl->rfbRectanglesSent[rfbEncodingRRE]++;
+ cl->rfbBytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader
+ + sz_rfbRREHeader + rreAfterBufLen);
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader
+ > UPDATE_BUF_SIZE)
+ {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingRRE);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ hdr.nSubrects = Swap32IfLE(nSubrects);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbRREHeader);
+ cl->ublen += sz_rfbRREHeader;
+
+ for (i = 0; i < rreAfterBufLen;) {
+
+ int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
+
+ if (i + bytesToCopy > rreAfterBufLen) {
+ bytesToCopy = rreAfterBufLen - i;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen], &rreAfterBuf[i], bytesToCopy);
+
+ cl->ublen += bytesToCopy;
+ i += bytesToCopy;
+
+ if (cl->ublen == UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ * subrectEncode() encodes the given multicoloured rectangle as a background
+ * colour overwritten by single-coloured rectangles. It returns the number
+ * of subrectangles in the encoded buffer, or -1 if subrect encoding won't
+ * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The
+ * single-colour rectangle partition is not optimal, but does find the biggest
+ * horizontal or vertical rectangle top-left anchored to each consecutive
+ * coordinate position.
+ *
+ * The coding scheme is simply [<bgcolour><subrect><subrect>...] where each
+ * <subrect> is [<colour><x><y><w><h>].
+ */
+
+#define DEFINE_SUBRECT_ENCODE(bpp) \
+static int \
+subrectEncode##bpp(data,w,h) \
+ CARD##bpp *data; \
+ int w; \
+ int h; \
+{ \
+ CARD##bpp cl; \
+ rfbRectangle subrect; \
+ int x,y; \
+ int i,j; \
+ int hx=0,hy,vx=0,vy; \
+ int hyflag; \
+ CARD##bpp *seg; \
+ CARD##bpp *line; \
+ int hw,hh,vw,vh; \
+ int thex,they,thew,theh; \
+ int numsubs = 0; \
+ int newLen; \
+ CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \
+ \
+ *((CARD##bpp*)rreAfterBuf) = bg; \
+ \
+ rreAfterBufLen = (bpp/8); \
+ \
+ for (y=0; y<h; y++) { \
+ line = data+(y*w); \
+ for (x=0; x<w; x++) { \
+ if (line[x] != bg) { \
+ cl = line[x]; \
+ hy = y-1; \
+ hyflag = 1; \
+ for (j=y; j<h; j++) { \
+ seg = data+(j*w); \
+ if (seg[x] != cl) {break;} \
+ i = x; \
+ while ((seg[i] == cl) && (i < w)) i += 1; \
+ i -= 1; \
+ if (j == y) vx = hx = i; \
+ if (i < vx) vx = i; \
+ if ((hyflag > 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \
+ } \
+ vy = j-1; \
+ \
+ /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \
+ * We'll choose the bigger of the two. \
+ */ \
+ hw = hx-x+1; \
+ hh = hy-y+1; \
+ vw = vx-x+1; \
+ vh = vy-y+1; \
+ \
+ thex = x; \
+ they = y; \
+ \
+ if ((hw*hh) > (vw*vh)) { \
+ thew = hw; \
+ theh = hh; \
+ } else { \
+ thew = vw; \
+ theh = vh; \
+ } \
+ \
+ subrect.x = Swap16IfLE(thex); \
+ subrect.y = Swap16IfLE(they); \
+ subrect.w = Swap16IfLE(thew); \
+ subrect.h = Swap16IfLE(theh); \
+ \
+ newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \
+ if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \
+ return -1; \
+ \
+ numsubs += 1; \
+ *((CARD##bpp*)(rreAfterBuf + rreAfterBufLen)) = cl; \
+ rreAfterBufLen += (bpp/8); \
+ memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbRectangle); \
+ rreAfterBufLen += sz_rfbRectangle; \
+ \
+ /* \
+ * Now mark the subrect as done. \
+ */ \
+ for (j=they; j < (they+theh); j++) { \
+ for (i=thex; i < (thex+thew); i++) { \
+ data[j*w+i] = bg; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ \
+ return numsubs; \
+}
+
+DEFINE_SUBRECT_ENCODE(8)
+DEFINE_SUBRECT_ENCODE(16)
+DEFINE_SUBRECT_ENCODE(32)
+
+
+/*
+ * getBgColour() gets the most prevalent colour in a byte array.
+ */
+static CARD32
+getBgColour(data,size,bpp)
+ char *data;
+ int size;
+ int bpp;
+{
+
+#define NUMCLRS 256
+
+ static int counts[NUMCLRS];
+ int i,j,k;
+
+ int maxcount = 0;
+ CARD8 maxclr = 0;
+
+ if (bpp != 8) {
+ if (bpp == 16) {
+ return ((CARD16 *)data)[0];
+ } else if (bpp == 32) {
+ return ((CARD32 *)data)[0];
+ } else {
+ rfbLog("getBgColour: bpp %d?\n",bpp);
+ exit(1);
+ }
+ }
+
+ for (i=0; i<NUMCLRS; i++) {
+ counts[i] = 0;
+ }
+
+ for (j=0; j<size; j++) {
+ k = (int)(((CARD8 *)data)[j]);
+ if (k >= NUMCLRS) {
+ rfbLog("getBgColour: unusual colour = %d\n", k);
+ exit(1);
+ }
+ counts[k] += 1;
+ if (counts[k] > maxcount) {
+ maxcount = counts[k];
+ maxclr = ((CARD8 *)data)[j];
+ }
+ }
+
+ return maxclr;
+}
diff --git a/krfb/libvncserver/selbox.c b/krfb/libvncserver/selbox.c
new file mode 100644
index 00000000..6cdb4590
--- /dev/null
+++ b/krfb/libvncserver/selbox.c
@@ -0,0 +1,301 @@
+#include <ctype.h>
+#include "rfb.h"
+#include "keysym.h"
+
+typedef struct {
+ rfbScreenInfoPtr screen;
+ rfbFontDataPtr font;
+ char** list;
+ int listSize;
+ int selected;
+ int displayStart;
+ int x1,y1,x2,y2,textH,pageH;
+ int xhot,yhot;
+ int buttonWidth,okBX,cancelBX,okX,cancelX,okY;
+ Bool okInverted,cancelInverted;
+ int lastButtons;
+ Pixel colour,backColour;
+ SelectionChangedHookPtr selChangedHook;
+ enum { SELECTING, OK, CANCEL } state;
+} rfbSelectData;
+
+static const char okStr[] = "OK";
+static const char cancelStr[] = "Cancel";
+
+static void selPaintButtons(rfbSelectData* m,Bool invertOk,Bool invertCancel)
+{
+ rfbScreenInfoPtr s = m->screen;
+ Pixel bcolour = m->backColour;
+ Pixel colour = m->colour;
+
+ rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour);
+
+ if(invertOk) {
+ rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour);
+ rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,
+ m->x1,m->okY-m->textH,m->x2,m->okY,
+ bcolour,colour);
+ } else
+ rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour);
+
+ if(invertCancel) {
+ rfbFillRect(s,m->cancelBX,m->okY-m->textH,
+ m->cancelBX+m->buttonWidth,m->okY,colour);
+ rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,
+ cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY,
+ bcolour,colour);
+ } else
+ rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour);
+
+ m->okInverted = invertOk;
+ m->cancelInverted = invertCancel;
+}
+
+/* line is relative to displayStart */
+static void selPaintLine(rfbSelectData* m,int line,Bool invert)
+{
+ int y1 = m->y1+line*m->textH, y2 = y1+m->textH;
+ if(y2>m->y2)
+ y2=m->y2;
+ rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour);
+ if(m->displayStart+line<m->listSize)
+ rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot,
+ m->list[m->displayStart+line],
+ m->x1,y1,m->x2,y2,
+ invert?m->backColour:m->colour,
+ invert?m->backColour:m->colour);
+}
+
+static void selSelect(rfbSelectData* m,int _index)
+{
+ int delta;
+
+ if(_index==m->selected || _index<0 || _index>=m->listSize)
+ return;
+
+ if(m->selected>=0)
+ selPaintLine(m,m->selected-m->displayStart,FALSE);
+
+ if(_index<m->displayStart || _index>=m->displayStart+m->pageH) {
+ /* targetLine is the screen line in which the selected line will
+ be displayed.
+ targetLine = m->pageH/2 doesn't look so nice */
+ int targetLine = m->selected-m->displayStart;
+ int lineStart,lineEnd;
+
+ /* scroll */
+ if(_index<targetLine)
+ targetLine = _index;
+ else if(_index+m->pageH-targetLine>=m->listSize)
+ targetLine = _index+m->pageH-m->listSize;
+ delta = _index-(m->displayStart+targetLine);
+
+ if(delta>-m->pageH && delta<m->pageH) {
+ if(delta>0) {
+ lineStart = m->pageH-delta;
+ lineEnd = m->pageH;
+ rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH,
+ 0,-delta*m->textH);
+ } else {
+ lineStart = 0;
+ lineEnd = -delta;
+ rfbDoCopyRect(m->screen,
+ m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2,
+ 0,-delta*m->textH);
+ }
+ } else {
+ lineStart = 0;
+ lineEnd = m->pageH;
+ }
+ m->displayStart += delta;
+ for(delta=lineStart;delta<lineEnd;delta++)
+ if(delta!=_index)
+ selPaintLine(m,delta,FALSE);
+ }
+
+ m->selected = _index;
+ selPaintLine(m,m->selected-m->displayStart,TRUE);
+
+ if(m->selChangedHook)
+ m->selChangedHook(_index);
+
+ /* todo: scrollbars */
+}
+
+static void selKbdAddEvent(Bool down,KeySym keySym,rfbClientPtr cl)
+{
+ if(down) {
+ if(keySym>' ' && keySym<0xff) {
+ int i;
+ rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
+ char c = tolower(keySym);
+
+ for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++);
+ if(!m->list[i])
+ for(i=0;i<m->selected && tolower(m->list[i][0])!=c;i++);
+ selSelect(m,i);
+ } else if(keySym==XK_Escape) {
+ rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
+ m->state = CANCEL;
+ } else if(keySym==XK_Return) {
+ rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
+ m->state = OK;
+ } else {
+ rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
+ int curSel=m->selected;
+ if(keySym==XK_Up) {
+ if(curSel>0)
+ selSelect(m,curSel-1);
+ } else if(keySym==XK_Down) {
+ if(curSel+1<m->listSize)
+ selSelect(m,curSel+1);
+ } else {
+ if(keySym==XK_Page_Down) {
+ if(curSel+m->pageH<m->listSize)
+ selSelect(m,curSel+m->pageH);
+ else
+ selSelect(m,m->listSize-1);
+ } else if(keySym==XK_Page_Up) {
+ if(curSel-m->pageH>=0)
+ selSelect(m,curSel-m->pageH);
+ else
+ selSelect(m,0);
+ }
+ }
+ }
+ }
+}
+
+static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)
+{
+ rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
+ if(y<m->okY && y>=m->okY-m->textH) {
+ if(x>=m->okBX && x<m->okBX+m->buttonWidth) {
+ if(!m->okInverted)
+ selPaintButtons(m,TRUE,FALSE);
+ if(buttonMask)
+ m->state = OK;
+ } else if(x>=m->cancelBX && x<m->cancelBX+m->buttonWidth) {
+ if(!m->cancelInverted)
+ selPaintButtons(m,FALSE,TRUE);
+ if(buttonMask)
+ m->state = CANCEL;
+ } else if(m->okInverted || m->cancelInverted)
+ selPaintButtons(m,FALSE,FALSE);
+ } else {
+ if(m->okInverted || m->cancelInverted)
+ selPaintButtons(m,FALSE,FALSE);
+ if(!m->lastButtons && buttonMask) {
+ if(x>=m->x1 && x<m->x2 && y>=m->y1 && y<m->y2)
+ selSelect(m,m->displayStart+(y-m->y1)/m->textH);
+ }
+ }
+ m->lastButtons = buttonMask;
+
+ /* todo: scrollbars */
+}
+
+static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl)
+{
+ return(0);
+}
+
+int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
+ char** list,
+ int x1,int y1,int x2,int y2,
+ Pixel colour,Pixel backColour,
+ int border,SelectionChangedHookPtr selChangedHook)
+{
+ int bpp = rfbScreen->bitsPerPixel/8;
+ char* frameBufferBackup;
+ void* screenDataBackup = rfbScreen->screenData;
+ KbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent;
+ PtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent;
+ GetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr;
+ DisplayHookPtr displayHookBackup = rfbScreen->displayHook;
+ rfbSelectData selData;
+ int i,j,k;
+ int fx1,fy1,fx2,fy2; /* for font bbox */
+
+ if(list==0 || *list==0)
+ return(-1);
+
+ rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2);
+ selData.textH = fy2-fy1;
+ /* I need at least one line for the choice and one for the buttons */
+ if(y2-y1<selData.textH*2+3*border)
+ return(-1);
+ selData.xhot = -fx1;
+ selData.yhot = -fy2;
+ selData.x1 = x1+border;
+ selData.y1 = y1+border;
+ selData.y2 = y2-selData.textH-3*border;
+ selData.x2 = x2-2*border;
+ selData.pageH = (selData.y2-selData.y1)/selData.textH;
+
+ i = rfbWidthOfString(font,okStr);
+ j = rfbWidthOfString(font,cancelStr);
+ selData.buttonWidth= k = 4*border+(i<j)?j:i;
+ selData.okBX = x1+(x2-x1-2*k)/3;
+ if(selData.okBX<x1+border) /* too narrow! */
+ return(-1);
+ selData.cancelBX = x1+k+(x2-x1-2*k)*2/3;
+ selData.okX = selData.okBX+(k-i)/2;
+ selData.cancelX = selData.cancelBX+(k-j)/2;
+ selData.okY = y2-border;
+
+ rfbUndrawCursor(rfbScreen);
+ frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1));
+
+ selData.state = SELECTING;
+ selData.screen = rfbScreen;
+ selData.font = font;
+ selData.list = list;
+ selData.colour = colour;
+ selData.backColour = backColour;
+ for(i=0;list[i];i++);
+ selData.selected = i;
+ selData.listSize = i;
+ selData.displayStart = i;
+ selData.lastButtons = 0;
+ selData.selChangedHook = selChangedHook;
+
+ rfbScreen->screenData = &selData;
+ rfbScreen->kbdAddEvent = selKbdAddEvent;
+ rfbScreen->ptrAddEvent = selPtrAddEvent;
+ rfbScreen->getCursorPtr = selGetCursorPtr;
+ rfbScreen->displayHook = 0;
+
+ /* backup screen */
+ for(j=0;j<y2-y1;j++)
+ memcpy(frameBufferBackup+j*(x2-x1)*bpp,
+ rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
+ (x2-x1)*bpp);
+
+ /* paint list and buttons */
+ rfbFillRect(rfbScreen,x1,y1,x2,y2,colour);
+ selPaintButtons(&selData,FALSE,FALSE);
+ selSelect(&selData,0);
+
+ /* modal loop */
+ while(selData.state == SELECTING)
+ rfbProcessEvents(rfbScreen,20000);
+
+ /* copy back screen data */
+ for(j=0;j<y2-y1;j++)
+ memcpy(rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
+ frameBufferBackup+j*(x2-x1)*bpp,
+ (x2-x1)*bpp);
+ free(frameBufferBackup);
+ rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2);
+ rfbScreen->screenData = screenDataBackup;
+ rfbScreen->kbdAddEvent = kbdAddEventBackup;
+ rfbScreen->ptrAddEvent = ptrAddEventBackup;
+ rfbScreen->getCursorPtr = getCursorPtrBackup;
+ rfbScreen->displayHook = displayHookBackup;
+
+ if(selData.state==CANCEL)
+ selData.selected=-1;
+ return(selData.selected);
+}
+
diff --git a/krfb/libvncserver/sockets.c b/krfb/libvncserver/sockets.c
new file mode 100644
index 00000000..69c4810a
--- /dev/null
+++ b/krfb/libvncserver/sockets.c
@@ -0,0 +1,592 @@
+/*
+ * sockets.c - deal with TCP & UDP sockets.
+ *
+ * This code should be independent of any changes in the RFB protocol. It just
+ * deals with the X server scheduling stuff, calling rfbNewClientConnection and
+ * rfbProcessClientMessage to actually deal with the protocol. If a socket
+ * needs to be closed for any reason then rfbCloseClient should be called, and
+ * this in turn will call rfbClientConnectionGone. To make an active
+ * connection out, call rfbConnect - note that this does _not_ call
+ * rfbNewClientConnection.
+ *
+ * This file is divided into two types of function. Those beginning with
+ * "rfb" are specific to sockets using the RFB protocol. Those without the
+ * "rfb" prefix are more general socket routines (which are used by the http
+ * code).
+ *
+ * Thanks to Karl Hakimian for pointing out that some platforms return EAGAIN
+ * not EWOULDBLOCK.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef WIN32
+#pragma warning (disable: 4018 4761)
+#define close closesocket
+#define read(sock,buf,len) recv(sock,buf,len,0)
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define ETIMEDOUT WSAETIMEDOUT
+#define write(sock,buf,len) send(sock,buf,len,0)
+#else
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#endif
+#if defined(__osf__)
+typedef int socklen_t;
+#endif
+#if defined(__linux__) && defined(NEED_TIMEVAL)
+struct timeval
+{
+ long int tv_sec,tv_usec;
+}
+;
+#endif
+#include <fcntl.h>
+#include <errno.h>
+
+#ifdef USE_LIBWRAP
+#include <syslog.h>
+#include <tcpd.h>
+int allow_severity=LOG_INFO;
+int deny_severity=LOG_WARNING;
+#endif
+
+#include "rfb.h"
+
+/*#ifndef WIN32
+int max(int i,int j) { return(i<j?j:i); }
+#endif
+*/
+
+int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has
+ gone away - needed to stop us hanging */
+
+/*
+ * rfbInitSockets sets up the TCP and UDP sockets to listen for RFB
+ * connections. It does nothing if called again.
+ */
+
+void
+rfbInitSockets(rfbScreenInfoPtr rfbScreen)
+{
+ if (rfbScreen->socketInitDone)
+ return;
+
+ rfbScreen->socketInitDone = TRUE;
+
+ if (rfbScreen->inetdSock != -1) {
+ const int one = 1;
+
+#ifndef WIN32
+ if (fcntl(rfbScreen->inetdSock, F_SETFL, O_NONBLOCK) < 0) {
+ rfbLogPerror("fcntl");
+ exit(1);
+ }
+#endif
+
+ if (setsockopt(rfbScreen->inetdSock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ rfbLogPerror("setsockopt");
+ exit(1);
+ }
+
+ FD_ZERO(&(rfbScreen->allFds));
+ FD_SET(rfbScreen->inetdSock, &(rfbScreen->allFds));
+ rfbScreen->maxFd = rfbScreen->inetdSock;
+ return;
+ }
+
+ if(rfbScreen->autoPort) {
+ int i;
+ rfbLog("Autoprobing TCP port \n");
+
+ for (i = 5900; i < 6000; i++) {
+ if ((rfbScreen->rfbListenSock = ListenOnTCPPort(i)) >= 0) {
+ rfbScreen->rfbPort = i;
+ break;
+ }
+ }
+
+ if (i >= 6000) {
+ rfbLogPerror("Failure autoprobing");
+ exit(1);
+ }
+
+ rfbLog("Autoprobing selected port %d\n", rfbScreen->rfbPort);
+ FD_ZERO(&(rfbScreen->allFds));
+ FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds));
+ rfbScreen->maxFd = rfbScreen->rfbListenSock;
+ }
+ else if(rfbScreen->rfbPort>0) {
+ rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->rfbPort);
+
+ if ((rfbScreen->rfbListenSock = ListenOnTCPPort(rfbScreen->rfbPort)) < 0) {
+ rfbLogPerror("ListenOnTCPPort");
+ exit(1);
+ }
+
+ FD_ZERO(&(rfbScreen->allFds));
+ FD_SET(rfbScreen->rfbListenSock, &(rfbScreen->allFds));
+ rfbScreen->maxFd = rfbScreen->rfbListenSock;
+ }
+
+ if (rfbScreen->udpPort != 0) {
+ rfbLog("rfbInitSockets: listening for input on UDP port %d\n",rfbScreen->udpPort);
+
+ if ((rfbScreen->udpSock = ListenOnUDPPort(rfbScreen->udpPort)) < 0) {
+ rfbLogPerror("ListenOnUDPPort");
+ exit(1);
+ }
+ FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds));
+ rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd);
+ }
+}
+
+
+/*
+ * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB
+ * socket(s). If there is input to process, the appropriate function in the
+ * RFB server code will be called (rfbNewClientConnection,
+ * rfbProcessClientMessage, etc).
+ */
+
+void
+rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec)
+{
+ int nfds;
+ fd_set fds;
+ struct timeval tv;
+ struct sockaddr_in addr;
+ size_t addrlen = sizeof(addr);
+ char buf[6];
+ const int one = 1;
+ int sock;
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl;
+
+ if (!rfbScreen->inetdInitDone && rfbScreen->inetdSock != -1) {
+ rfbNewClientConnection(rfbScreen,rfbScreen->inetdSock);
+ rfbScreen->inetdInitDone = TRUE;
+ }
+
+ memcpy((char *)&fds, (char *)&(rfbScreen->allFds), sizeof(fd_set));
+ tv.tv_sec = 0;
+ tv.tv_usec = usec;
+ nfds = select(rfbScreen->maxFd + 1, &fds, NULL, NULL /* &fds */, &tv);
+ if (nfds == 0) {
+ return;
+ }
+ if (nfds < 0) {
+#ifdef WIN32
+ errno = WSAGetLastError();
+#endif
+ rfbLogPerror("rfbCheckFds: select");
+ return;
+ }
+
+ if (rfbScreen->rfbListenSock != -1 && FD_ISSET(rfbScreen->rfbListenSock, &fds)) {
+
+ if ((sock = accept(rfbScreen->rfbListenSock,
+ (struct sockaddr *)&addr, &addrlen)) < 0) {
+ rfbLogPerror("rfbCheckFds: accept");
+ return;
+ }
+
+#ifndef WIN32
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ rfbLogPerror("rfbCheckFds: fcntl");
+ close(sock);
+ return;
+ }
+#endif
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ rfbLogPerror("rfbCheckFds: setsockopt");
+ close(sock);
+ return;
+ }
+
+#ifdef USE_LIBWRAP
+ if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr),
+ STRING_UNKNOWN)) {
+ rfbLog("Rejected connection from client %s\n",
+ inet_ntoa(addr.sin_addr));
+ close(sock);
+ return;
+ }
+#endif
+
+ rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr));
+
+ rfbNewClient(rfbScreen,sock);
+
+ FD_CLR(rfbScreen->rfbListenSock, &fds);
+ if (--nfds == 0)
+ return;
+ }
+
+ if ((rfbScreen->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) {
+ if(!rfbScreen->udpClient)
+ rfbNewUDPClient(rfbScreen);
+ if (recvfrom(rfbScreen->udpSock, buf, 1, MSG_PEEK,
+ (struct sockaddr *)&addr, &addrlen) < 0) {
+ rfbLogPerror("rfbCheckFds: UDP: recvfrom");
+ rfbDisconnectUDPSock(rfbScreen);
+ rfbScreen->udpSockConnected = FALSE;
+ } else {
+ if (!rfbScreen->udpSockConnected ||
+ (memcmp(&addr, &rfbScreen->udpRemoteAddr, addrlen) != 0))
+ {
+ /* new remote end */
+ rfbLog("rfbCheckFds: UDP: got connection\n");
+
+ memcpy(&rfbScreen->udpRemoteAddr, &addr, addrlen);
+ rfbScreen->udpSockConnected = TRUE;
+
+ if (connect(rfbScreen->udpSock,
+ (struct sockaddr *)&addr, addrlen) < 0) {
+ rfbLogPerror("rfbCheckFds: UDP: connect");
+ rfbDisconnectUDPSock(rfbScreen);
+ return;
+ }
+
+ rfbNewUDPConnection(rfbScreen,rfbScreen->udpSock);
+ }
+
+ rfbProcessUDPInput(rfbScreen);
+ }
+
+ FD_CLR(rfbScreen->udpSock, &fds);
+ if (--nfds == 0)
+ return;
+ }
+
+ i = rfbGetClientIterator(rfbScreen);
+ while((cl = rfbClientIteratorNext(i))) {
+ if (cl->onHold)
+ continue;
+ if (FD_ISSET(cl->sock, &fds) && FD_ISSET(cl->sock, &(rfbScreen->allFds)))
+ rfbProcessClientMessage(cl);
+ }
+ rfbReleaseClientIterator(i);
+}
+
+
+void
+rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen)
+{
+ rfbScreen->udpSockConnected = FALSE;
+}
+
+
+
+void
+rfbCloseClient(cl)
+ rfbClientPtr cl;
+{
+ LOCK(cl->updateMutex);
+ if (cl->sock != -1) {
+ FD_CLR(cl->sock,&(cl->screen->allFds));
+ shutdown(cl->sock,SHUT_RDWR);
+ close(cl->sock);
+ cl->sock = -1;
+ }
+ TSIGNAL(cl->updateCond);
+ UNLOCK(cl->updateMutex);
+}
+
+
+/*
+ * rfbConnect is called to make a connection out to a given TCP address.
+ */
+
+int
+rfbConnect(rfbScreen, host, port)
+ rfbScreenInfoPtr rfbScreen;
+ char *host;
+ int port;
+{
+ int sock;
+ int one = 1;
+
+ rfbLog("Making connection to client on host %s port %d\n",
+ host,port);
+
+ if ((sock = ConnectToTcpAddr(host, port)) < 0) {
+ rfbLogPerror("connection failed");
+ return -1;
+ }
+
+#ifndef WIN32
+ if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
+ rfbLogPerror("fcntl failed");
+ close(sock);
+ return -1;
+ }
+#endif
+
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&one, sizeof(one)) < 0) {
+ rfbLogPerror("setsockopt failed");
+ close(sock);
+ return -1;
+ }
+
+ /* AddEnabledDevice(sock); */
+ FD_SET(sock, &rfbScreen->allFds);
+ rfbScreen->maxFd = max(sock,rfbScreen->maxFd);
+
+ return sock;
+}
+
+/*
+ * ReadExact reads an exact number of bytes from a client. Returns 1 if
+ * those bytes have been read, 0 if the other end has closed, or -1 if an error
+ * occurred (errno is set to ETIMEDOUT if it timed out).
+ * timeout is the timeout in ms, 0 for no timeout.
+ */
+
+int
+ReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout)
+{
+ int sock = cl->sock;
+ int n;
+ fd_set fds;
+ struct timeval tv;
+ int to = 20000;
+ if (timeout)
+ to = timeout;
+
+ while (len > 0) {
+ n = read(sock, buf, len);
+
+ if (n > 0) {
+
+ buf += n;
+ len -= n;
+
+ } else if (n == 0) {
+
+ return 0;
+
+ } else {
+#ifdef WIN32
+ errno = WSAGetLastError();
+#endif
+ if (errno != EWOULDBLOCK && errno != EAGAIN) {
+ return n;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ tv.tv_sec = to / 1000;
+ tv.tv_usec = (to % 1000) * 1000;
+ n = select(sock+1, &fds, NULL, &fds, &tv);
+ if (n < 0) {
+ rfbLogPerror("ReadExact: select");
+ return n;
+ }
+ if ((n == 0) && timeout) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
+
+int ReadExact(rfbClientPtr cl,char* buf,int len)
+{
+ return ReadExactTimeout(cl, buf, len, 0);
+}
+
+/*
+ * WriteExact writes an exact number of bytes to a client. Returns 1 if
+ * those bytes have been written, or -1 if an error occurred (errno is set to
+ * ETIMEDOUT if it timed out).
+ */
+
+int
+WriteExact(cl, buf, len)
+ rfbClientPtr cl;
+ const char *buf;
+ int len;
+{
+ int sock = cl->sock;
+ int n;
+ fd_set fds;
+ struct timeval tv;
+ int totalTimeWaited = 0;
+
+ LOCK(cl->outputMutex);
+ while (len > 0) {
+ n = write(sock, buf, len);
+
+ if (n > 0) {
+
+ buf += n;
+ len -= n;
+
+ } else if (n == 0) {
+
+ rfbLog("WriteExact: write returned 0?\n");
+ exit(1);
+
+ } else {
+#ifdef WIN32
+ errno = WSAGetLastError();
+#endif
+ if (errno != EWOULDBLOCK && errno != EAGAIN) {
+ UNLOCK(cl->outputMutex);
+ return n;
+ }
+
+ /* Retry every 5 seconds until we exceed rfbMaxClientWait. We
+ need to do this because select doesn't necessarily return
+ immediately when the other end has gone away */
+
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+ n = select(sock+1, NULL, &fds, NULL /* &fds */, &tv);
+ if (n < 0) {
+ rfbLogPerror("WriteExact: select");
+ UNLOCK(cl->outputMutex);
+ return n;
+ }
+ if (n == 0) {
+ totalTimeWaited += 5000;
+ if (totalTimeWaited >= rfbMaxClientWait) {
+ errno = ETIMEDOUT;
+ UNLOCK(cl->outputMutex);
+ return -1;
+ }
+ } else {
+ totalTimeWaited = 0;
+ }
+ }
+ }
+ UNLOCK(cl->outputMutex);
+ return 1;
+}
+
+int
+ListenOnTCPPort(port)
+ int port;
+{
+ struct sockaddr_in addr;
+ int sock;
+ int one = 1;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ /* addr.sin_addr.s_addr = interface.s_addr; */
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof(one)) < 0) {
+ close(sock);
+ return -1;
+ }
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(sock);
+ return -1;
+ }
+ if (listen(sock, 5) < 0) {
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int
+ConnectToTcpAddr(host, port)
+ char *host;
+ int port;
+{
+ struct hostent *hp;
+ int sock;
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+
+ if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
+ {
+ if (!(hp = gethostbyname(host))) {
+ errno = EINVAL;
+ return -1;
+ }
+ addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr;
+ }
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ if (connect(sock, (struct sockaddr *)&addr, (sizeof(addr))) < 0) {
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int
+ListenOnUDPPort(port)
+ int port;
+{
+ struct sockaddr_in addr;
+ int sock;
+ int one = 1;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ /* addr.sin_addr.s_addr = interface.s_addr; */
+ addr.sin_addr.s_addr = INADDR_ANY;
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ return -1;
+ }
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&one, sizeof(one)) < 0) {
+ return -1;
+ }
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ return -1;
+ }
+
+ return sock;
+}
diff --git a/krfb/libvncserver/sraRegion.c b/krfb/libvncserver/sraRegion.c
new file mode 100644
index 00000000..315ce5b1
--- /dev/null
+++ b/krfb/libvncserver/sraRegion.c
@@ -0,0 +1,889 @@
+/* -=- sraRegion.c
+ * Copyright (c) 2001 James "Wez" Weatherall, Johannes E. Schindelin
+ *
+ * A general purpose region clipping library
+ * Only deals with rectangular regions, though.
+ */
+
+#include "rfb.h"
+#include "sraRegion.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/* -=- Internal Span structure */
+
+struct sraRegion;
+
+typedef struct sraSpan {
+ struct sraSpan *_next;
+ struct sraSpan *_prev;
+ int start;
+ int end;
+ struct sraRegion *subspan;
+} sraSpan;
+
+typedef struct sraRegion {
+ sraSpan front;
+ sraSpan back;
+} sraSpanList;
+
+/* -=- Span routines */
+
+sraSpanList *sraSpanListDup(const sraSpanList *src);
+void sraSpanListDestroy(sraSpanList *list);
+
+static sraSpan *
+sraSpanCreate(int start, int end, const sraSpanList *subspan) {
+ sraSpan *item = (sraSpan*)malloc(sizeof(sraSpan));
+ item->_next = item->_prev = NULL;
+ item->start = start;
+ item->end = end;
+ item->subspan = sraSpanListDup(subspan);
+ return item;
+}
+
+static sraSpan *
+sraSpanDup(const sraSpan *src) {
+ sraSpan *span;
+ if (!src) return NULL;
+ span = sraSpanCreate(src->start, src->end, src->subspan);
+ return span;
+}
+
+static void
+sraSpanInsertAfter(sraSpan *newspan, sraSpan *after) {
+ newspan->_next = after->_next;
+ newspan->_prev = after;
+ after->_next->_prev = newspan;
+ after->_next = newspan;
+}
+
+static void
+sraSpanInsertBefore(sraSpan *newspan, sraSpan *before) {
+ newspan->_next = before;
+ newspan->_prev = before->_prev;
+ before->_prev->_next = newspan;
+ before->_prev = newspan;
+}
+
+static void
+sraSpanRemove(sraSpan *span) {
+ span->_prev->_next = span->_next;
+ span->_next->_prev = span->_prev;
+}
+
+static void
+sraSpanDestroy(sraSpan *span) {
+ if (span->subspan) sraSpanListDestroy(span->subspan);
+ free(span);
+}
+
+#ifdef DEBUG
+static void
+sraSpanCheck(const sraSpan *span, const char *text) {
+ /* Check the span is valid! */
+ if (span->start == span->end) {
+ printf(text);
+ printf(":%d-%d\n", span->start, span->end);
+ }
+}
+#endif
+
+/* -=- SpanList routines */
+
+static void sraSpanPrint(const sraSpan *s);
+
+static void
+sraSpanListPrint(const sraSpanList *l) {
+ sraSpan *curr;
+ if (!l) {
+ printf("NULL");
+ return;
+ }
+ curr = l->front._next;
+ printf("[");
+ while (curr != &(l->back)) {
+ sraSpanPrint(curr);
+ curr = curr->_next;
+ }
+ printf("]");
+}
+
+void
+sraSpanPrint(const sraSpan *s) {
+ printf("(%d-%d)", (s->start), (s->end));
+ if (s->subspan)
+ sraSpanListPrint(s->subspan);
+}
+
+static sraSpanList *
+sraSpanListCreate(void) {
+ sraSpanList *item = (sraSpanList*)malloc(sizeof(sraSpanList));
+ item->front._next = &(item->back);
+ item->front._prev = NULL;
+ item->back._prev = &(item->front);
+ item->back._next = NULL;
+ return item;
+}
+
+sraSpanList *
+sraSpanListDup(const sraSpanList *src) {
+ sraSpanList *newlist;
+ sraSpan *newspan, *curr;
+
+ if (!src) return NULL;
+ newlist = sraSpanListCreate();
+ curr = src->front._next;
+ while (curr != &(src->back)) {
+ newspan = sraSpanDup(curr);
+ sraSpanInsertBefore(newspan, &(newlist->back));
+ curr = curr->_next;
+ }
+
+ return newlist;
+}
+
+void
+sraSpanListDestroy(sraSpanList *list) {
+ sraSpan *curr, *next;
+ while (list->front._next != &(list->back)) {
+ curr = list->front._next;
+ next = curr->_next;
+ sraSpanRemove(curr);
+ sraSpanDestroy(curr);
+ curr = next;
+ }
+ free(list);
+}
+
+static void
+sraSpanListMakeEmpty(sraSpanList *list) {
+ sraSpan *curr, *next;
+ while (list->front._next != &(list->back)) {
+ curr = list->front._next;
+ next = curr->_next;
+ sraSpanRemove(curr);
+ sraSpanDestroy(curr);
+ curr = next;
+ }
+ list->front._next = &(list->back);
+ list->front._prev = NULL;
+ list->back._prev = &(list->front);
+ list->back._next = NULL;
+}
+
+static Bool
+sraSpanListEqual(const sraSpanList *s1, const sraSpanList *s2) {
+ sraSpan *sp1, *sp2;
+
+ if (!s1) {
+ if (!s2) {
+ return 1;
+ } else {
+ printf("sraSpanListEqual:incompatible spans (only one NULL!)\n");
+ return FALSE;
+ }
+ }
+
+ sp1 = s1->front._next;
+ sp2 = s2->front._next;
+ while ((sp1 != &(s1->back)) &&
+ (sp2 != &(s2->back))) {
+ if ((sp1->start != sp2->start) ||
+ (sp1->end != sp2->end) ||
+ (!sraSpanListEqual(sp1->subspan, sp2->subspan))) {
+ return 0;
+ }
+ sp1 = sp1->_next;
+ sp2 = sp2->_next;
+ }
+
+ if ((sp1 == &(s1->back)) && (sp2 == &(s2->back))) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static Bool
+sraSpanListEmpty(const sraSpanList *list) {
+ return (list->front._next == &(list->back));
+}
+
+static unsigned long
+sraSpanListCount(const sraSpanList *list) {
+ sraSpan *curr = list->front._next;
+ unsigned long count = 0;
+ while (curr != &(list->back)) {
+ if (curr->subspan) {
+ count += sraSpanListCount(curr->subspan);
+ } else {
+ count += 1;
+ }
+ curr = curr->_next;
+ }
+ return count;
+}
+
+static void
+sraSpanMergePrevious(sraSpan *dest) {
+ sraSpan *prev = dest->_prev;
+
+ while ((prev->_prev) &&
+ (prev->end == dest->start) &&
+ (sraSpanListEqual(prev->subspan, dest->subspan))) {
+ /*
+ printf("merge_prev:");
+ sraSpanPrint(prev);
+ printf(" & ");
+ sraSpanPrint(dest);
+ printf("\n");
+ */
+ dest->start = prev->start;
+ sraSpanRemove(prev);
+ sraSpanDestroy(prev);
+ prev = dest->_prev;
+ }
+}
+
+static void
+sraSpanMergeNext(sraSpan *dest) {
+ sraSpan *next = dest->_next;
+ while ((next->_next) &&
+ (next->start == dest->end) &&
+ (sraSpanListEqual(next->subspan, dest->subspan))) {
+/*
+ printf("merge_next:");
+ sraSpanPrint(dest);
+ printf(" & ");
+ sraSpanPrint(next);
+ printf("\n");
+ */
+ dest->end = next->end;
+ sraSpanRemove(next);
+ sraSpanDestroy(next);
+ next = dest->_next;
+ }
+}
+
+static void
+sraSpanListOr(sraSpanList *dest, const sraSpanList *src) {
+ sraSpan *d_curr, *s_curr;
+ int s_start, s_end;
+
+ if (!dest) {
+ if (!src) {
+ return;
+ } else {
+ printf("sraSpanListOr:incompatible spans (only one NULL!)\n");
+ return;
+ }
+ }
+
+ d_curr = dest->front._next;
+ s_curr = src->front._next;
+ s_start = s_curr->start;
+ s_end = s_curr->end;
+ while (s_curr != &(src->back)) {
+
+ /* - If we are at end of destination list OR
+ If the new span comes before the next destination one */
+ if ((d_curr == &(dest->back)) ||
+ (d_curr->start >= s_end)) {
+ /* - Add the span */
+ sraSpanInsertBefore(sraSpanCreate(s_start, s_end,
+ s_curr->subspan),
+ d_curr);
+ if (d_curr != &(dest->back))
+ sraSpanMergePrevious(d_curr);
+ s_curr = s_curr->_next;
+ s_start = s_curr->start;
+ s_end = s_curr->end;
+ } else {
+
+ /* - If the new span overlaps the existing one */
+ if ((s_start < d_curr->end) &&
+ (s_end > d_curr->start)) {
+
+ /* - Insert new span before the existing destination one? */
+ if (s_start < d_curr->start) {
+ sraSpanInsertBefore(sraSpanCreate(s_start,
+ d_curr->start,
+ s_curr->subspan),
+ d_curr);
+ sraSpanMergePrevious(d_curr);
+ }
+
+ /* Split the existing span if necessary */
+ if (s_end < d_curr->end) {
+ sraSpanInsertAfter(sraSpanCreate(s_end,
+ d_curr->end,
+ d_curr->subspan),
+ d_curr);
+ d_curr->end = s_end;
+ }
+ if (s_start > d_curr->start) {
+ sraSpanInsertBefore(sraSpanCreate(d_curr->start,
+ s_start,
+ d_curr->subspan),
+ d_curr);
+ d_curr->start = s_start;
+ }
+
+ /* Recursively OR subspans */
+ sraSpanListOr(d_curr->subspan, s_curr->subspan);
+
+ /* Merge this span with previous or next? */
+ if (d_curr->_prev != &(dest->front))
+ sraSpanMergePrevious(d_curr);
+ if (d_curr->_next != &(dest->back))
+ sraSpanMergeNext(d_curr);
+
+ /* Move onto the next pair to compare */
+ if (s_end > d_curr->end) {
+ s_start = d_curr->end;
+ d_curr = d_curr->_next;
+ } else {
+ s_curr = s_curr->_next;
+ s_start = s_curr->start;
+ s_end = s_curr->end;
+ }
+ } else {
+ /* - No overlap. Move to the next destination span */
+ d_curr = d_curr->_next;
+ }
+ }
+ }
+}
+
+static Bool
+sraSpanListAnd(sraSpanList *dest, const sraSpanList *src) {
+ sraSpan *d_curr, *s_curr, *d_next;
+
+ if (!dest) {
+ if (!src) {
+ return 1;
+ } else {
+ printf("sraSpanListAnd:incompatible spans (only one NULL!)\n");
+ return FALSE;
+ }
+ }
+
+ d_curr = dest->front._next;
+ s_curr = src->front._next;
+ while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) {
+
+ /* - If we haven't reached a destination span yet then move on */
+ if (d_curr->start >= s_curr->end) {
+ s_curr = s_curr->_next;
+ continue;
+ }
+
+ /* - If we are beyond the current destination span then remove it */
+ if (d_curr->end <= s_curr->start) {
+ sraSpan *next = d_curr->_next;
+ sraSpanRemove(d_curr);
+ sraSpanDestroy(d_curr);
+ d_curr = next;
+ continue;
+ }
+
+ /* - If we partially overlap a span then split it up or remove bits */
+ if (s_curr->start > d_curr->start) {
+ /* - The top bit of the span does not match */
+ d_curr->start = s_curr->start;
+ }
+ if (s_curr->end < d_curr->end) {
+ /* - The end of the span does not match */
+ sraSpanInsertAfter(sraSpanCreate(s_curr->end,
+ d_curr->end,
+ d_curr->subspan),
+ d_curr);
+ d_curr->end = s_curr->end;
+ }
+
+ /* - Now recursively process the affected span */
+ if (!sraSpanListAnd(d_curr->subspan, s_curr->subspan)) {
+ /* - The destination subspan is now empty, so we should remove it */
+ sraSpan *next = d_curr->_next;
+ sraSpanRemove(d_curr);
+ sraSpanDestroy(d_curr);
+ d_curr = next;
+ } else {
+ /* Merge this span with previous or next? */
+ if (d_curr->_prev != &(dest->front))
+ sraSpanMergePrevious(d_curr);
+
+ /* - Move on to the next span */
+ d_next = d_curr;
+ if (s_curr->end >= d_curr->end) {
+ d_next = d_curr->_next;
+ }
+ if (s_curr->end <= d_curr->end) {
+ s_curr = s_curr->_next;
+ }
+ d_curr = d_next;
+ }
+ }
+
+ while (d_curr != &(dest->back)) {
+ sraSpan *next = d_curr->_next;
+ sraSpanRemove(d_curr);
+ sraSpanDestroy(d_curr);
+ d_curr=next;
+ }
+
+ return !sraSpanListEmpty(dest);
+}
+
+static Bool
+sraSpanListSubtract(sraSpanList *dest, const sraSpanList *src) {
+ sraSpan *d_curr, *s_curr;
+
+ if (!dest) {
+ if (!src) {
+ return 1;
+ } else {
+ printf("sraSpanListSubtract:incompatible spans (only one NULL!)\n");
+ return FALSE;
+ }
+ }
+
+ d_curr = dest->front._next;
+ s_curr = src->front._next;
+ while ((s_curr != &(src->back)) && (d_curr != &(dest->back))) {
+
+ /* - If we haven't reached a destination span yet then move on */
+ if (d_curr->start >= s_curr->end) {
+ s_curr = s_curr->_next;
+ continue;
+ }
+
+ /* - If we are beyond the current destination span then skip it */
+ if (d_curr->end <= s_curr->start) {
+ d_curr = d_curr->_next;
+ continue;
+ }
+
+ /* - If we partially overlap the current span then split it up */
+ if (s_curr->start > d_curr->start) {
+ sraSpanInsertBefore(sraSpanCreate(d_curr->start,
+ s_curr->start,
+ d_curr->subspan),
+ d_curr);
+ d_curr->start = s_curr->start;
+ }
+ if (s_curr->end < d_curr->end) {
+ sraSpanInsertAfter(sraSpanCreate(s_curr->end,
+ d_curr->end,
+ d_curr->subspan),
+ d_curr);
+ d_curr->end = s_curr->end;
+ }
+
+ /* - Now recursively process the affected span */
+ if ((!d_curr->subspan) || !sraSpanListSubtract(d_curr->subspan, s_curr->subspan)) {
+ /* - The destination subspan is now empty, so we should remove it */
+ sraSpan *next = d_curr->_next;
+ sraSpanRemove(d_curr);
+ sraSpanDestroy(d_curr);
+ d_curr = next;
+ } else {
+ /* Merge this span with previous or next? */
+ if (d_curr->_prev != &(dest->front))
+ sraSpanMergePrevious(d_curr);
+ if (d_curr->_next != &(dest->back))
+ sraSpanMergeNext(d_curr);
+
+ /* - Move on to the next span */
+ if (s_curr->end > d_curr->end) {
+ d_curr = d_curr->_next;
+ } else {
+ s_curr = s_curr->_next;
+ }
+ }
+ }
+
+ return !sraSpanListEmpty(dest);
+}
+
+/* -=- Region routines */
+
+sraRegion *
+sraRgnCreate() {
+ return (sraRegion*)sraSpanListCreate();
+}
+
+sraRegion *
+sraRgnCreateRect(int x1, int y1, int x2, int y2) {
+ sraSpanList *vlist, *hlist;
+ sraSpan *vspan, *hspan;
+
+ /* - Build the horizontal portion of the span */
+ hlist = sraSpanListCreate();
+ hspan = sraSpanCreate(x1, x2, NULL);
+ sraSpanInsertAfter(hspan, &(hlist->front));
+
+ /* - Build the vertical portion of the span */
+ vlist = sraSpanListCreate();
+ vspan = sraSpanCreate(y1, y2, hlist);
+ sraSpanInsertAfter(vspan, &(vlist->front));
+
+ sraSpanListDestroy(hlist);
+
+ return (sraRegion*)vlist;
+}
+
+sraRegion *
+sraRgnCreateRgn(const sraRegion *src) {
+ return (sraRegion*)sraSpanListDup((sraSpanList*)src);
+}
+
+void
+sraRgnDestroy(sraRegion *rgn) {
+ sraSpanListDestroy((sraSpanList*)rgn);
+}
+
+void
+sraRgnMakeEmpty(sraRegion *rgn) {
+ sraSpanListMakeEmpty((sraSpanList*)rgn);
+}
+
+/* -=- Boolean Region ops */
+
+Bool
+sraRgnAnd(sraRegion *dst, const sraRegion *src) {
+ return sraSpanListAnd((sraSpanList*)dst, (sraSpanList*)src);
+}
+
+void
+sraRgnOr(sraRegion *dst, const sraRegion *src) {
+ sraSpanListOr((sraSpanList*)dst, (sraSpanList*)src);
+}
+
+Bool
+sraRgnSubtract(sraRegion *dst, const sraRegion *src) {
+ return sraSpanListSubtract((sraSpanList*)dst, (sraSpanList*)src);
+}
+
+void
+sraRgnOffset(sraRegion *dst, int dx, int dy) {
+ sraSpan *vcurr, *hcurr;
+
+ vcurr = ((sraSpanList*)dst)->front._next;
+ while (vcurr != &(((sraSpanList*)dst)->back)) {
+ vcurr->start += dy;
+ vcurr->end += dy;
+
+ hcurr = vcurr->subspan->front._next;
+ while (hcurr != &(vcurr->subspan->back)) {
+ hcurr->start += dx;
+ hcurr->end += dx;
+ hcurr = hcurr->_next;
+ }
+
+ vcurr = vcurr->_next;
+ }
+}
+
+sraRegion *sraRgnBBox(const sraRegion *src) {
+ int xmin=((unsigned int)(int)-1)>>1,ymin=xmin,xmax=1-xmin,ymax=xmax;
+ sraSpan *vcurr, *hcurr;
+
+ if(!src)
+ return sraRgnCreate();
+
+ vcurr = ((sraSpanList*)src)->front._next;
+ while (vcurr != &(((sraSpanList*)src)->back)) {
+ if(vcurr->start<ymin)
+ ymin=vcurr->start;
+ if(vcurr->end>ymax)
+ ymax=vcurr->end;
+
+ hcurr = vcurr->subspan->front._next;
+ while (hcurr != &(vcurr->subspan->back)) {
+ if(hcurr->start<xmin)
+ xmin=hcurr->start;
+ if(hcurr->end>xmax)
+ xmax=hcurr->end;
+ hcurr = hcurr->_next;
+ }
+
+ vcurr = vcurr->_next;
+ }
+
+ if(xmax<xmin || ymax<ymin)
+ return sraRgnCreate();
+
+ return sraRgnCreateRect(xmin,ymin,xmax,ymax);
+}
+
+Bool
+sraRgnPopRect(sraRegion *rgn, sraRect *rect, unsigned long flags) {
+ sraSpan *vcurr, *hcurr;
+ sraSpan *vend, *hend;
+ Bool right2left = flags & 2;
+ Bool bottom2top = flags & 1;
+
+ /* - Pick correct order */
+ if (bottom2top) {
+ vcurr = ((sraSpanList*)rgn)->back._prev;
+ vend = &(((sraSpanList*)rgn)->front);
+ } else {
+ vcurr = ((sraSpanList*)rgn)->front._next;
+ vend = &(((sraSpanList*)rgn)->back);
+ }
+
+ if (vcurr != vend) {
+ rect->y1 = vcurr->start;
+ rect->y2 = vcurr->end;
+
+ /* - Pick correct order */
+ if (right2left) {
+ hcurr = vcurr->subspan->back._prev;
+ hend = &(vcurr->subspan->front);
+ } else {
+ hcurr = vcurr->subspan->front._next;
+ hend = &(vcurr->subspan->back);
+ }
+
+ if (hcurr != hend) {
+ rect->x1 = hcurr->start;
+ rect->x2 = hcurr->end;
+
+ sraSpanRemove(hcurr);
+ sraSpanDestroy(hcurr);
+
+ if (sraSpanListEmpty(vcurr->subspan)) {
+ sraSpanRemove(vcurr);
+ sraSpanDestroy(vcurr);
+ }
+
+#if 0
+ printf("poprect:(%dx%d)-(%dx%d)\n",
+ rect->x1, rect->y1, rect->x2, rect->y2);
+#endif
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+unsigned long
+sraRgnCountRects(const sraRegion *rgn) {
+ unsigned long count = sraSpanListCount((sraSpanList*)rgn);
+ return count;
+}
+
+Bool
+sraRgnEmpty(const sraRegion *rgn) {
+ return sraSpanListEmpty((sraSpanList*)rgn);
+}
+
+/* iterator stuff */
+sraRectangleIterator *sraRgnGetIterator(sraRegion *s)
+{
+ /* these values have to be multiples of 4 */
+#define DEFSIZE 4
+#define DEFSTEP 8
+ sraRectangleIterator *i =
+ (sraRectangleIterator*)malloc(sizeof(sraRectangleIterator));
+ if(!i)
+ return NULL;
+
+ /* we have to recurse eventually. So, the first sPtr is the pointer to
+ the sraSpan in the first level. the second sPtr is the pointer to
+ the sraRegion.back. The third and fourth sPtr are for the second
+ recursion level and so on. */
+ i->sPtrs = (sraSpan**)malloc(sizeof(sraSpan*)*DEFSIZE);
+ if(!i->sPtrs) {
+ free(i);
+ return NULL;
+ }
+ i->ptrSize = DEFSIZE;
+ i->sPtrs[0] = &(s->front);
+ i->sPtrs[1] = &(s->back);
+ i->ptrPos = 0;
+ i->reverseX = 0;
+ i->reverseY = 0;
+ return i;
+}
+
+sraRectangleIterator *sraRgnGetReverseIterator(sraRegion *s,Bool reverseX,Bool reverseY)
+{
+ sraRectangleIterator *i = sraRgnGetIterator(s);
+ if(reverseY) {
+ i->sPtrs[1] = &(s->front);
+ i->sPtrs[0] = &(s->back);
+ }
+ i->reverseX = reverseX;
+ i->reverseY = reverseY;
+ return(i);
+}
+
+static Bool sraReverse(sraRectangleIterator *i)
+{
+ return( ((i->ptrPos&2) && i->reverseX) ||
+ (!(i->ptrPos&2) && i->reverseY));
+}
+
+static sraSpan* sraNextSpan(sraRectangleIterator *i)
+{
+ if(sraReverse(i))
+ return(i->sPtrs[i->ptrPos]->_prev);
+ else
+ return(i->sPtrs[i->ptrPos]->_next);
+}
+
+Bool sraRgnIteratorNext(sraRectangleIterator* i,sraRect* r)
+{
+ /* is the subspan finished? */
+ while(sraNextSpan(i) == i->sPtrs[i->ptrPos+1]) {
+ i->ptrPos -= 2;
+ if(i->ptrPos < 0) /* the end */
+ return(0);
+ }
+
+ i->sPtrs[i->ptrPos] = sraNextSpan(i);
+
+ /* is this a new subspan? */
+ while(i->sPtrs[i->ptrPos]->subspan) {
+ if(i->ptrPos+2 > i->ptrSize) { /* array is too small */
+ i->ptrSize += DEFSTEP;
+ i->sPtrs = (sraSpan**)realloc(i->sPtrs, sizeof(sraSpan*)*i->ptrSize);
+ }
+ i->ptrPos =+ 2;
+ if(sraReverse(i)) {
+ i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->back._prev;
+ i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->front);
+ } else {
+ i->sPtrs[i->ptrPos] = i->sPtrs[i->ptrPos-2]->subspan->front._next;
+ i->sPtrs[i->ptrPos+1] = &(i->sPtrs[i->ptrPos-2]->subspan->back);
+ }
+ }
+
+ if((i->ptrPos%4)!=2) {
+ printf("sraRgnIteratorNext: offset is wrong (%d%%4!=2)\n",i->ptrPos);
+ return FALSE;
+ }
+
+ r->y1 = i->sPtrs[i->ptrPos-2]->start;
+ r->y2 = i->sPtrs[i->ptrPos-2]->end;
+ r->x1 = i->sPtrs[i->ptrPos]->start;
+ r->x2 = i->sPtrs[i->ptrPos]->end;
+
+ return(-1);
+}
+
+void sraRgnReleaseIterator(sraRectangleIterator* i)
+{
+ free(i->sPtrs);
+ free(i);
+}
+
+void
+sraRgnPrint(const sraRegion *rgn) {
+ sraSpanListPrint((sraSpanList*)rgn);
+}
+
+Bool
+sraClipRect(int *x, int *y, int *w, int *h,
+ int cx, int cy, int cw, int ch) {
+ if (*x < cx) {
+ *w -= (cx-*x);
+ *x = cx;
+ }
+ if (*y < cy) {
+ *h -= (cy-*y);
+ *y = cy;
+ }
+ if (*x+*w > cx+cw) {
+ *w = (cx+cw)-*x;
+ }
+ if (*y+*h > cy+ch) {
+ *h = (cy+ch)-*y;
+ }
+ return (*w>0) && (*h>0);
+}
+
+Bool
+sraClipRect2(int *x, int *y, int *x2, int *y2,
+ int cx, int cy, int cx2, int cy2) {
+ if (*x < cx)
+ *x = cx;
+ if (*y < cy)
+ *y = cy;
+ if (*x >= cx2)
+ *x = cx2-1;
+ if (*y >= cy2)
+ *y = cy2-1;
+ if (*x2 <= cx)
+ *x2 = cx+1;
+ if (*y2 <= cy)
+ *y2 = cy+1;
+ if (*x2 > cx2)
+ *x2 = cx2;
+ if (*y2 > cy2)
+ *y2 = cy2;
+ return (*x2>*x) && (*y2>*y);
+}
+
+/* test */
+
+#ifdef SRA_TEST
+/* pipe the output to sort|uniq -u and you'll get the errors. */
+int main(int argc, char** argv)
+{
+ sraRegionPtr region, region1, region2;
+ sraRectangleIterator* i;
+ sraRect rect;
+ Bool b;
+
+ region = sraRgnCreateRect(10, 10, 600, 300);
+ region1 = sraRgnCreateRect(40, 50, 350, 200);
+ region2 = sraRgnCreateRect(0, 0, 20, 40);
+
+ sraRgnPrint(region);
+ printf("\n[(10-300)[(10-600)]]\n\n");
+
+ b = sraRgnSubtract(region, region1);
+ printf("%s ",b?"true":"false");
+ sraRgnPrint(region);
+ printf("\ntrue [(10-50)[(10-600)](50-200)[(10-40)(350-600)](200-300)[(10-600)]]\n\n");
+
+ sraRgnOr(region, region2);
+ printf("%ld\n6\n\n", sraRgnCountRects(region));
+
+ i = sraRgnGetIterator(region);
+ while(sraRgnIteratorNext(i, &rect))
+ printf("%dx%d+%d+%d ",
+ rect.x2-rect.x1,rect.y2-rect.y1,
+ rect.x1,rect.y1);
+ sraRgnReleaseIterator(i);
+ printf("\n20x10+0+0 600x30+0+10 590x10+10+40 30x150+10+50 250x150+350+50 590x100+10+200 \n\n");
+
+ i = sraRgnGetReverseIterator(region,1,0);
+ while(sraRgnIteratorNext(i, &rect))
+ printf("%dx%d+%d+%d ",
+ rect.x2-rect.x1,rect.y2-rect.y1,
+ rect.x1,rect.y1);
+ sraRgnReleaseIterator(i);
+ printf("\n20x10+0+0 600x30+0+10 590x10+10+40 250x150+350+50 30x150+10+50 590x100+10+200 \n\n");
+
+ i = sraRgnGetReverseIterator(region,1,1);
+ while(sraRgnIteratorNext(i, &rect))
+ printf("%dx%d+%d+%d ",
+ rect.x2-rect.x1,rect.y2-rect.y1,
+ rect.x1,rect.y1);
+ sraRgnReleaseIterator(i);
+ printf("\n590x100+10+200 250x150+350+50 30x150+10+50 590x10+10+40 600x30+0+10 20x10+0+0 \n\n");
+
+ sraRgnDestroy(region);
+ sraRgnDestroy(region1);
+ sraRgnDestroy(region2);
+
+ return(0);
+}
+#endif
diff --git a/krfb/libvncserver/sraRegion.h b/krfb/libvncserver/sraRegion.h
new file mode 100644
index 00000000..9526ac37
--- /dev/null
+++ b/krfb/libvncserver/sraRegion.h
@@ -0,0 +1,65 @@
+#ifndef SRAREGION_H
+#define SRAREGION_H
+
+/* -=- SRA - Simple Region Algorithm
+ * A simple rectangular region implementation.
+ * Copyright (c) 2001 James "Wez" Weatherall, Johannes E. Schindelin
+ */
+
+/* -=- sraRect */
+
+typedef struct _rect {
+ int x1;
+ int y1;
+ int x2;
+ int y2;
+} sraRect;
+
+typedef struct sraRegion sraRegion;
+
+/* -=- Region manipulation functions */
+
+extern sraRegion *sraRgnCreate();
+extern sraRegion *sraRgnCreateRect(int x1, int y1, int x2, int y2);
+extern sraRegion *sraRgnCreateRgn(const sraRegion *src);
+
+extern void sraRgnDestroy(sraRegion *rgn);
+extern void sraRgnMakeEmpty(sraRegion *rgn);
+extern Bool sraRgnAnd(sraRegion *dst, const sraRegion *src);
+extern void sraRgnOr(sraRegion *dst, const sraRegion *src);
+extern Bool sraRgnSubtract(sraRegion *dst, const sraRegion *src);
+
+extern void sraRgnOffset(sraRegion *dst, int dx, int dy);
+
+extern Bool sraRgnPopRect(sraRegion *region, sraRect *rect,
+ unsigned long flags);
+
+extern unsigned long sraRgnCountRects(const sraRegion *rgn);
+extern Bool sraRgnEmpty(const sraRegion *rgn);
+
+extern sraRegion *sraRgnBBox(const sraRegion *src);
+
+/* -=- rectangle iterator */
+
+typedef struct sraRectangleIterator {
+ Bool reverseX,reverseY;
+ int ptrSize,ptrPos;
+ struct sraSpan** sPtrs;
+} sraRectangleIterator;
+
+extern sraRectangleIterator *sraRgnGetIterator(sraRegion *s);
+extern sraRectangleIterator *sraRgnGetReverseIterator(sraRegion *s,Bool reverseX,Bool reverseY);
+extern Bool sraRgnIteratorNext(sraRectangleIterator *i,sraRect *r);
+extern void sraRgnReleaseIterator(sraRectangleIterator *i);
+
+void sraRgnPrint(const sraRegion *s);
+
+/* -=- Rectangle clipper (for speed) */
+
+extern Bool sraClipRect(int *x, int *y, int *w, int *h,
+ int cx, int cy, int cw, int ch);
+
+extern Bool sraClipRect2(int *x, int *y, int *x2, int *y2,
+ int cx, int cy, int cx2, int cy2);
+
+#endif
diff --git a/krfb/libvncserver/stats.c b/krfb/libvncserver/stats.c
new file mode 100644
index 00000000..7774d2fa
--- /dev/null
+++ b/krfb/libvncserver/stats.c
@@ -0,0 +1,103 @@
+/*
+ * stats.c
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfb.h"
+
+static const char* encNames[] = {
+ "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile",
+ "zlib", "tight", "[encoding 8]", "[encoding 9]"
+};
+
+
+void
+rfbResetStats(rfbClientPtr cl)
+{
+ int i;
+ for (i = 0; i < MAX_ENCODINGS; i++) {
+ cl->rfbBytesSent[i] = 0;
+ cl->rfbRectanglesSent[i] = 0;
+ }
+ cl->rfbLastRectMarkersSent = 0;
+ cl->rfbLastRectBytesSent = 0;
+ cl->rfbCursorBytesSent = 0;
+ cl->rfbCursorUpdatesSent = 0;
+ cl->rfbFramebufferUpdateMessagesSent = 0;
+ cl->rfbRawBytesEquivalent = 0;
+ cl->rfbKeyEventsRcvd = 0;
+ cl->rfbPointerEventsRcvd = 0;
+}
+
+void
+rfbPrintStats(rfbClientPtr cl)
+{
+ int i;
+ int totalRectanglesSent = 0;
+ int totalBytesSent = 0;
+
+ rfbLog("Statistics:\n");
+
+ if ((cl->rfbKeyEventsRcvd != 0) || (cl->rfbPointerEventsRcvd != 0))
+ rfbLog(" key events received %d, pointer events %d\n",
+ cl->rfbKeyEventsRcvd, cl->rfbPointerEventsRcvd);
+
+ for (i = 0; i < MAX_ENCODINGS; i++) {
+ totalRectanglesSent += cl->rfbRectanglesSent[i];
+ totalBytesSent += cl->rfbBytesSent[i];
+ }
+
+ totalRectanglesSent += (cl->rfbCursorUpdatesSent +
+ cl->rfbLastRectMarkersSent);
+ totalBytesSent += (cl->rfbCursorBytesSent + cl->rfbLastRectBytesSent);
+
+ rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n",
+ cl->rfbFramebufferUpdateMessagesSent, totalRectanglesSent,
+ totalBytesSent);
+
+ if (cl->rfbLastRectMarkersSent != 0)
+ rfbLog(" LastRect markers %d, bytes %d\n",
+ cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent);
+
+ if (cl->rfbCursorUpdatesSent != 0)
+ rfbLog(" cursor shape updates %d, bytes %d\n",
+ cl->rfbCursorUpdatesSent, cl->rfbCursorBytesSent);
+
+ for (i = 0; i < MAX_ENCODINGS; i++) {
+ if (cl->rfbRectanglesSent[i] != 0)
+ rfbLog(" %s rectangles %d, bytes %d\n",
+ encNames[i], cl->rfbRectanglesSent[i], cl->rfbBytesSent[i]);
+ }
+
+ if ((totalBytesSent - cl->rfbBytesSent[rfbEncodingCopyRect]) != 0) {
+ rfbLog(" raw bytes equivalent %d, compression ratio %f\n",
+ cl->rfbRawBytesEquivalent,
+ (double)cl->rfbRawBytesEquivalent
+ / (double)(totalBytesSent
+ - cl->rfbBytesSent[rfbEncodingCopyRect]-
+ cl->rfbCursorBytesSent -
+ cl->rfbLastRectBytesSent));
+ }
+}
diff --git a/krfb/libvncserver/storepasswd.c b/krfb/libvncserver/storepasswd.c
new file mode 100644
index 00000000..1470e4db
--- /dev/null
+++ b/krfb/libvncserver/storepasswd.c
@@ -0,0 +1,46 @@
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+void usage(void)
+{
+ printf("\nusage: storepasswd <password> <filename>\n\n");
+
+ printf("Stores a password in encrypted format.\n");
+ printf("The resulting file can be used with the -rfbauth argument to OSXvnc.\n\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3)
+ usage();
+
+ if (vncEncryptAndStorePasswd(argv[1], argv[2]) != 0) {
+ printf("storing password failed.\n");
+ return 1;
+ } else {
+ printf("storing password succeeded.\n");
+ return 0;
+ }
+}
diff --git a/krfb/libvncserver/tableinit24.c b/krfb/libvncserver/tableinit24.c
new file mode 100644
index 00000000..f1e63a50
--- /dev/null
+++ b/krfb/libvncserver/tableinit24.c
@@ -0,0 +1,157 @@
+/*
+ 24 bit
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+static void
+rfbInitOneRGBTable24 (CARD8 *table, int inMax, int outMax, int outShift,int swap);
+
+
+static void
+rfbInitColourMapSingleTable24(char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out,rfbColourMap* colourMap)
+{
+ CARD32 i, r, g, b, outValue;
+ CARD8 *t;
+ CARD8 c;
+ unsigned int nEntries = 1 << in->bitsPerPixel;
+ int shift = colourMap->is16?16:8;
+
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * 3 + 1);
+ t = (CARD8 *)*table;
+
+ for (i = 0; i < nEntries; i++) {
+ r = g = b = 0;
+ if(i < colourMap->count) {
+ if(colourMap->is16) {
+ r = colourMap->data.shorts[3*i+0];
+ g = colourMap->data.shorts[3*i+1];
+ b = colourMap->data.shorts[3*i+2];
+ } else {
+ r = colourMap->data.bytes[3*i+0];
+ g = colourMap->data.bytes[3*i+1];
+ b = colourMap->data.bytes[3*i+2];
+ }
+ }
+ outValue = ((((r * (1 + out->redMax)) >> shift) << out->redShift) |
+ (((g * (1 + out->greenMax)) >> shift) << out->greenShift) |
+ (((b * (1 + out->blueMax)) >> shift) << out->blueShift));
+ *(CARD32*)&t[3*i] = outValue;
+ if(!rfbEndianTest)
+ memmove(t+3*i,t+3*i+1,3);
+ if (out->bigEndian != in->bigEndian) {
+ c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c;
+ }
+ }
+}
+
+/*
+ * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour
+ * translation.
+ */
+
+static void
+rfbInitTrueColourSingleTable24 (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ int i,outValue;
+ int inRed, inGreen, inBlue, outRed, outGreen, outBlue;
+ CARD8 *t;
+ CARD8 c;
+ int nEntries = 1 << in->bitsPerPixel;
+
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * 3 + 1);
+ t = (CARD8 *)*table;
+
+ for (i = 0; i < nEntries; i++) {
+ inRed = (i >> in->redShift) & in->redMax;
+ inGreen = (i >> in->greenShift) & in->greenMax;
+ inBlue = (i >> in->blueShift) & in->blueMax;
+
+ outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax;
+ outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax;
+ outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax;
+
+ outValue = ((outRed << out->redShift) |
+ (outGreen << out->greenShift) |
+ (outBlue << out->blueShift));
+ *(CARD32*)&t[3*i] = outValue;
+ if(!rfbEndianTest)
+ memmove(t+3*i,t+3*i+1,3);
+ if (out->bigEndian != in->bigEndian) {
+ c = t[3*i]; t[3*i] = t[3*i+2]; t[3*i+2] = c;
+ }
+ }
+}
+
+
+/*
+ * rfbInitTrueColourRGBTables sets up three separate lookup tables for the
+ * red, green and blue values.
+ */
+
+static void
+rfbInitTrueColourRGBTables24 (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ CARD8 *redTable;
+ CARD8 *greenTable;
+ CARD8 *blueTable;
+
+ if (*table) free(*table);
+ *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3)
+ * 3 + 1);
+ redTable = (CARD8 *)*table;
+ greenTable = redTable + 3*(in->redMax + 1);
+ blueTable = greenTable + 3*(in->greenMax + 1);
+
+ rfbInitOneRGBTable24 (redTable, in->redMax, out->redMax,
+ out->redShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTable24 (greenTable, in->greenMax, out->greenMax,
+ out->greenShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTable24 (blueTable, in->blueMax, out->blueMax,
+ out->blueShift, (out->bigEndian != in->bigEndian));
+}
+
+static void
+rfbInitOneRGBTable24 (CARD8 *table, int inMax, int outMax, int outShift,
+ int swap)
+{
+ int i;
+ int nEntries = inMax + 1;
+ CARD32 outValue;
+ CARD8 c;
+
+ for (i = 0; i < nEntries; i++) {
+ outValue = ((i * outMax + inMax / 2) / inMax) << outShift;
+ *(CARD32 *)&table[3*i] = outValue;
+ if(!rfbEndianTest)
+ memmove(table+3*i,table+3*i+1,3);
+ if (swap) {
+ c = table[3*i]; table[3*i] = table[3*i+2];
+ table[3*i+2] = c;
+ }
+ }
+}
diff --git a/krfb/libvncserver/tableinitcmtemplate.c b/krfb/libvncserver/tableinitcmtemplate.c
new file mode 100644
index 00000000..2d10ea57
--- /dev/null
+++ b/krfb/libvncserver/tableinitcmtemplate.c
@@ -0,0 +1,84 @@
+/*
+ * tableinitcmtemplate.c - template for initialising lookup tables for
+ * translation from a colour map to true colour.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with a different definition of the macro OUT.
+ * For each value of OUT, this file defines a function which allocates an
+ * appropriately sized lookup table and initialises it.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#if !defined(OUT)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define OUT_T CONCAT2E(CARD,OUT)
+#define SwapOUT(x) CONCAT2E(Swap,OUT(x))
+#define rfbInitColourMapSingleTableOUT \
+ CONCAT2E(rfbInitColourMapSingleTable,OUT)
+
+static void
+rfbInitColourMapSingleTableOUT(char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out,rfbColourMap* colourMap)
+{
+ CARD32 i, r, g, b;
+ OUT_T *t;
+ CARD32 nEntries = 1 << in->bitsPerPixel;
+ int shift = colourMap->is16?16:8;
+
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * sizeof(OUT_T));
+ t = (OUT_T *)*table;
+
+ for (i = 0; i < nEntries; i++) {
+ r = g = b = 0;
+ if(i < colourMap->count) {
+ if(colourMap->is16) {
+ r = colourMap->data.shorts[3*i+0];
+ g = colourMap->data.shorts[3*i+1];
+ b = colourMap->data.shorts[3*i+2];
+ } else {
+ r = colourMap->data.bytes[3*i+0];
+ g = colourMap->data.bytes[3*i+1];
+ b = colourMap->data.bytes[3*i+2];
+ }
+ }
+ t[i] = ((((r * (1 + out->redMax)) >> shift) << out->redShift) |
+ (((g * (1 + out->greenMax)) >> shift) << out->greenShift) |
+ (((b * (1 + out->blueMax)) >> shift) << out->blueShift));
+#if (OUT != 8)
+ if (out->bigEndian != in->bigEndian) {
+ t[i] = SwapOUT(t[i]);
+ }
+#endif
+ }
+}
+
+#undef OUT_T
+#undef SwapOUT
+#undef rfbInitColourMapSingleTableOUT
diff --git a/krfb/libvncserver/tableinittctemplate.c b/krfb/libvncserver/tableinittctemplate.c
new file mode 100644
index 00000000..93223d97
--- /dev/null
+++ b/krfb/libvncserver/tableinittctemplate.c
@@ -0,0 +1,142 @@
+/*
+ * tableinittctemplate.c - template for initialising lookup tables for
+ * truecolour to truecolour translation.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with a different definition of the macro OUT.
+ * For each value of OUT, this file defines two functions for initialising
+ * lookup tables. One is for truecolour translation using a single lookup
+ * table, the other is for truecolour translation using three separate
+ * lookup tables for the red, green and blue values.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#if !defined(OUT)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define OUT_T CONCAT2E(CARD,OUT)
+#define SwapOUT(x) CONCAT2E(Swap,OUT(x))
+#define rfbInitTrueColourSingleTableOUT \
+ CONCAT2E(rfbInitTrueColourSingleTable,OUT)
+#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUT)
+#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUT)
+
+static void
+rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift,
+ int swap);
+
+
+/*
+ * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour
+ * translation.
+ */
+
+static void
+rfbInitTrueColourSingleTableOUT (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ int i;
+ int inRed, inGreen, inBlue, outRed, outGreen, outBlue;
+ OUT_T *t;
+ int nEntries = 1 << in->bitsPerPixel;
+
+ if (*table) free(*table);
+ *table = (char *)malloc(nEntries * sizeof(OUT_T));
+ t = (OUT_T *)*table;
+
+ for (i = 0; i < nEntries; i++) {
+ inRed = (i >> in->redShift) & in->redMax;
+ inGreen = (i >> in->greenShift) & in->greenMax;
+ inBlue = (i >> in->blueShift) & in->blueMax;
+
+ outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax;
+ outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax;
+ outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax;
+
+ t[i] = ((outRed << out->redShift) |
+ (outGreen << out->greenShift) |
+ (outBlue << out->blueShift));
+#if (OUT != 8)
+ if (out->bigEndian != in->bigEndian) {
+ t[i] = SwapOUT(t[i]);
+ }
+#endif
+ }
+}
+
+
+/*
+ * rfbInitTrueColourRGBTables sets up three separate lookup tables for the
+ * red, green and blue values.
+ */
+
+static void
+rfbInitTrueColourRGBTablesOUT (char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out)
+{
+ OUT_T *redTable;
+ OUT_T *greenTable;
+ OUT_T *blueTable;
+
+ if (*table) free(*table);
+ *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3)
+ * sizeof(OUT_T));
+ redTable = (OUT_T *)*table;
+ greenTable = redTable + in->redMax + 1;
+ blueTable = greenTable + in->greenMax + 1;
+
+ rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax,
+ out->redShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax,
+ out->greenShift, (out->bigEndian != in->bigEndian));
+ rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax,
+ out->blueShift, (out->bigEndian != in->bigEndian));
+}
+
+static void
+rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift,
+ int swap)
+{
+ int i;
+ int nEntries = inMax + 1;
+
+ for (i = 0; i < nEntries; i++) {
+ table[i] = ((i * outMax + inMax / 2) / inMax) << outShift;
+#if (OUT != 8)
+ if (swap) {
+ table[i] = SwapOUT(table[i]);
+ }
+#endif
+ }
+}
+
+#undef OUT_T
+#undef SwapOUT
+#undef rfbInitTrueColourSingleTableOUT
+#undef rfbInitTrueColourRGBTablesOUT
+#undef rfbInitOneRGBTableOUT
diff --git a/krfb/libvncserver/tabletrans24template.c b/krfb/libvncserver/tabletrans24template.c
new file mode 100644
index 00000000..44a37cb7
--- /dev/null
+++ b/krfb/libvncserver/tabletrans24template.c
@@ -0,0 +1,281 @@
+/*
+ * tabletranstemplate.c - template for translation using lookup tables.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with different definitions of the macros IN and OUT.
+ *
+ * For each pair of values IN and OUT, this file defines two functions for
+ * translating a given rectangle of pixel data. One uses a single lookup
+ * table, and the other uses three separate lookup tables for the red, green
+ * and blue values.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#if !defined(BPP)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#if BPP == 24
+
+/*
+ * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data
+ * using a single lookup table.
+ */
+
+static void
+rfbTranslateWithSingleTable24to24 (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ CARD8 *ip = (CARD8 *)iptr;
+ CARD8 *op = (CARD8 *)optr;
+ int ipextra = bytesBetweenInputLines - width * 3;
+ CARD8 *opLineEnd;
+ CARD8 *t = (CARD8 *)table;
+ int shift = rfbEndianTest?0:8;
+ CARD8 c;
+
+ while (height > 0) {
+ opLineEnd = op + width*3;
+
+ while (op < opLineEnd) {
+ *(CARD32*)op = t[((*(CARD32 *)ip)>>shift)&0x00ffffff];
+ if(!rfbEndianTest)
+ memmove(op,op+1,3);
+ if (out->bigEndian != in->bigEndian) {
+ c = op[0]; op[0] = op[2]; op[2] = c;
+ }
+ op += 3;
+ ip += 3;
+ }
+
+ ip += ipextra;
+ height--;
+ }
+}
+
+/*
+ * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data
+ * using three separate lookup tables for the red, green and blue values.
+ */
+
+static void
+rfbTranslateWithRGBTables24to24 (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ CARD8 *ip = (CARD8 *)iptr;
+ CARD8 *op = (CARD8 *)optr;
+ int ipextra = bytesBetweenInputLines - width*3;
+ CARD8 *opLineEnd;
+ CARD8 *redTable = (CARD8 *)table;
+ CARD8 *greenTable = redTable + 3*(in->redMax + 1);
+ CARD8 *blueTable = greenTable + 3*(in->greenMax + 1);
+ CARD32 outValue,inValue;
+ int shift = rfbEndianTest?0:8;
+
+ while (height > 0) {
+ opLineEnd = op+3*width;
+
+ while (op < opLineEnd) {
+ inValue = ((*(CARD32 *)ip)>>shift)&0x00ffffff;
+ outValue = (redTable[(inValue >> in->redShift) & in->redMax] |
+ greenTable[(inValue >> in->greenShift) & in->greenMax] |
+ blueTable[(inValue >> in->blueShift) & in->blueMax]);
+ memcpy(op,&outValue,3);
+ op += 3;
+ ip+=3;
+ }
+ ip += ipextra;
+ height--;
+ }
+}
+
+#else
+
+#define IN_T CONCAT2E(CARD,BPP)
+#define OUT_T CONCAT2E(CARD,BPP)
+#define rfbTranslateWithSingleTable24toOUT \
+ CONCAT4E(rfbTranslateWithSingleTable,24,to,BPP)
+#define rfbTranslateWithSingleTableINto24 \
+ CONCAT4E(rfbTranslateWithSingleTable,BPP,to,24)
+#define rfbTranslateWithRGBTables24toOUT \
+ CONCAT4E(rfbTranslateWithRGBTables,24,to,BPP)
+#define rfbTranslateWithRGBTablesINto24 \
+ CONCAT4E(rfbTranslateWithRGBTables,BPP,to,24)
+
+/*
+ * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data
+ * using a single lookup table.
+ */
+
+static void
+rfbTranslateWithSingleTable24toOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ CARD8 *ip = (CARD8 *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines - width*3;
+ OUT_T *opLineEnd;
+ OUT_T *t = (OUT_T *)table;
+ int shift = rfbEndianTest?0:8;
+
+ while (height > 0) {
+ opLineEnd = op + width;
+
+ while (op < opLineEnd) {
+ *(op++) = t[((*(CARD32 *)ip)>>shift)&0x00ffffff];
+ ip+=3;
+ }
+
+ ip += ipextra;
+ height--;
+ }
+}
+
+
+/*
+ * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data
+ * using three separate lookup tables for the red, green and blue values.
+ */
+
+static void
+rfbTranslateWithRGBTables24toOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ CARD8 *ip = (CARD8 *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines - width*3;
+ OUT_T *opLineEnd;
+ OUT_T *redTable = (OUT_T *)table;
+ OUT_T *greenTable = redTable + in->redMax + 1;
+ OUT_T *blueTable = greenTable + in->greenMax + 1;
+ CARD32 inValue;
+ int shift = rfbEndianTest?0:8;
+
+ while (height > 0) {
+ opLineEnd = &op[width];
+
+ while (op < opLineEnd) {
+ inValue = ((*(CARD32 *)ip)>>shift)&0x00ffffff;
+ *(op++) = (redTable[(inValue >> in->redShift) & in->redMax] |
+ greenTable[(inValue >> in->greenShift) & in->greenMax] |
+ blueTable[(inValue >> in->blueShift) & in->blueMax]);
+ ip+=3;
+ }
+ ip += ipextra;
+ height--;
+ }
+}
+
+/*
+ * rfbTranslateWithSingleTableINto24 translates a rectangle of pixel data
+ * using a single lookup table.
+ */
+
+static void
+rfbTranslateWithSingleTableINto24 (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ CARD8 *op = (CARD8 *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ CARD8 *opLineEnd;
+ CARD8 *t = (CARD8 *)table;
+
+ while (height > 0) {
+ opLineEnd = op + width * 3;
+
+ while (op < opLineEnd) {
+ memcpy(op,&t[3*(*(ip++))],3);
+ op += 3;
+ }
+
+ ip += ipextra;
+ height--;
+ }
+}
+
+
+/*
+ * rfbTranslateWithRGBTablesINto24 translates a rectangle of pixel data
+ * using three separate lookup tables for the red, green and blue values.
+ */
+
+static void
+rfbTranslateWithRGBTablesINto24 (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ CARD8 *op = (CARD8 *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ CARD8 *opLineEnd;
+ CARD8 *redTable = (CARD8 *)table;
+ CARD8 *greenTable = redTable + 3*(in->redMax + 1);
+ CARD8 *blueTable = greenTable + 3*(in->greenMax + 1);
+ CARD32 outValue;
+
+ while (height > 0) {
+ opLineEnd = op+3*width;
+
+ while (op < opLineEnd) {
+ outValue = (redTable[(*ip >> in->redShift) & in->redMax] |
+ greenTable[(*ip >> in->greenShift) & in->greenMax] |
+ blueTable[(*ip >> in->blueShift) & in->blueMax]);
+ memcpy(op,&outValue,3);
+ op += 3;
+ ip++;
+ }
+ ip += ipextra;
+ height--;
+ }
+}
+
+#undef IN_T
+#undef OUT_T
+#undef rfbTranslateWithSingleTable24toOUT
+#undef rfbTranslateWithRGBTables24toOUT
+#undef rfbTranslateWithSingleTableINto24
+#undef rfbTranslateWithRGBTablesINto24
+
+#endif
diff --git a/krfb/libvncserver/tabletranstemplate.c b/krfb/libvncserver/tabletranstemplate.c
new file mode 100644
index 00000000..0aafff0f
--- /dev/null
+++ b/krfb/libvncserver/tabletranstemplate.c
@@ -0,0 +1,117 @@
+/*
+ * tabletranstemplate.c - template for translation using lookup tables.
+ *
+ * This file shouldn't be compiled. It is included multiple times by
+ * translate.c, each time with different definitions of the macros IN and OUT.
+ *
+ * For each pair of values IN and OUT, this file defines two functions for
+ * translating a given rectangle of pixel data. One uses a single lookup
+ * table, and the other uses three separate lookup tables for the red, green
+ * and blue values.
+ *
+ * I know this code isn't nice to read because of all the macros, but
+ * efficiency is important here.
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#if !defined(IN) || !defined(OUT)
+#error "This file shouldn't be compiled."
+#error "It is included as part of translate.c"
+#endif
+
+#define IN_T CONCAT2E(CARD,IN)
+#define OUT_T CONCAT2E(CARD,OUT)
+#define rfbTranslateWithSingleTableINtoOUT \
+ CONCAT4E(rfbTranslateWithSingleTable,IN,to,OUT)
+#define rfbTranslateWithRGBTablesINtoOUT \
+ CONCAT4E(rfbTranslateWithRGBTables,IN,to,OUT)
+
+/*
+ * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data
+ * using a single lookup table.
+ */
+
+static void
+rfbTranslateWithSingleTableINtoOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ OUT_T *opLineEnd;
+ OUT_T *t = (OUT_T *)table;
+
+ while (height > 0) {
+ opLineEnd = op + width;
+
+ while (op < opLineEnd) {
+ *(op++) = t[*(ip++)];
+ }
+
+ ip += ipextra;
+ height--;
+ }
+}
+
+
+/*
+ * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data
+ * using three separate lookup tables for the red, green and blue values.
+ */
+
+static void
+rfbTranslateWithRGBTablesINtoOUT (char *table, rfbPixelFormat *in,
+ rfbPixelFormat *out,
+ char *iptr, char *optr,
+ int bytesBetweenInputLines,
+ int width, int height)
+{
+ IN_T *ip = (IN_T *)iptr;
+ OUT_T *op = (OUT_T *)optr;
+ int ipextra = bytesBetweenInputLines / sizeof(IN_T) - width;
+ OUT_T *opLineEnd;
+ OUT_T *redTable = (OUT_T *)table;
+ OUT_T *greenTable = redTable + in->redMax + 1;
+ OUT_T *blueTable = greenTable + in->greenMax + 1;
+
+ while (height > 0) {
+ opLineEnd = &op[width];
+
+ while (op < opLineEnd) {
+ *(op++) = (redTable[(*ip >> in->redShift) & in->redMax] |
+ greenTable[(*ip >> in->greenShift) & in->greenMax] |
+ blueTable[(*ip >> in->blueShift) & in->blueMax]);
+ ip++;
+ }
+ ip += ipextra;
+ height--;
+ }
+}
+
+#undef IN_T
+#undef OUT_T
+#undef rfbTranslateWithSingleTableINtoOUT
+#undef rfbTranslateWithRGBTablesINtoOUT
diff --git a/krfb/libvncserver/tight.c b/krfb/libvncserver/tight.c
new file mode 100644
index 00000000..8b57f82a
--- /dev/null
+++ b/krfb/libvncserver/tight.c
@@ -0,0 +1,1809 @@
+/*
+ * tight.c
+ *
+ * Routines to implement Tight Encoding
+ */
+
+/*
+ * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*#include <stdio.h>*/
+#include "rfb.h"
+
+#ifdef WIN32
+#define XMD_H
+#undef FAR
+#define NEEDFAR_POINTERS
+#endif
+
+#include <jpeglib.h>
+
+/* Note: The following constant should not be changed. */
+#define TIGHT_MIN_TO_COMPRESS 12
+
+/* The parameters below may be adjusted. */
+#define MIN_SPLIT_RECT_SIZE 4096
+#define MIN_SOLID_SUBRECT_SIZE 2048
+#define MAX_SPLIT_TILE_SIZE 16
+
+/* May be set to TRUE with "-lazytight" Xvnc option. */
+Bool rfbTightDisableGradient = FALSE;
+
+/* This variable is set on every rfbSendRectEncodingTight() call. */
+static Bool usePixelFormat24;
+
+
+/* Compression level stuff. The following array contains various
+ encoder parameters for each of 10 compression levels (0..9).
+ Last three parameters correspond to JPEG quality levels (0..9). */
+
+typedef struct TIGHT_CONF_s {
+ int maxRectSize, maxRectWidth;
+ int monoMinRectSize, gradientMinRectSize;
+ int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel;
+ int gradientThreshold, gradientThreshold24;
+ int idxMaxColorsDivisor;
+ int jpegQuality, jpegThreshold, jpegThreshold24;
+} TIGHT_CONF;
+
+static TIGHT_CONF tightConf[10] = {
+ { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 20, 10000, 23000 },
+ { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 30, 8000, 18000 },
+ { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 40, 6500, 15000 },
+ { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 50, 5000, 12000 },
+ { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 55, 4000, 10000 },
+ { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 60, 3000, 8000 },
+ { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 65, 2000, 5000 },
+ { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
+ { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
+ { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
+};
+
+static int compressLevel;
+static int qualityLevel;
+
+/* Stuff dealing with palettes. */
+
+typedef struct COLOR_LIST_s {
+ struct COLOR_LIST_s *next;
+ int idx;
+ CARD32 rgb;
+} COLOR_LIST;
+
+typedef struct PALETTE_ENTRY_s {
+ COLOR_LIST *listNode;
+ int numPixels;
+} PALETTE_ENTRY;
+
+typedef struct PALETTE_s {
+ PALETTE_ENTRY entry[256];
+ COLOR_LIST *hash[256];
+ COLOR_LIST list[256];
+} PALETTE;
+
+static int paletteNumColors, paletteMaxColors;
+static CARD32 monoBackground, monoForeground;
+static PALETTE palette;
+
+/* Pointers to dynamically-allocated buffers. */
+
+static int tightBeforeBufSize = 0;
+static char *tightBeforeBuf = NULL;
+
+static int tightAfterBufSize = 0;
+static char *tightAfterBuf = NULL;
+
+static int *prevRowBuf = NULL;
+
+
+/* Prototypes for static functions. */
+
+static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 colorValue, int *w_ptr, int *h_ptr);
+static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 colorValue,
+ int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr);
+static Bool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 *colorPtr, Bool needSameColor);
+static Bool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 *colorPtr, Bool needSameColor);
+static Bool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 *colorPtr, Bool needSameColor);
+static Bool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h,
+ CARD32 *colorPtr, Bool needSameColor);
+
+static Bool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h);
+static Bool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h);
+static Bool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h);
+
+static Bool SendSolidRect (rfbClientPtr cl);
+static Bool SendMonoRect (rfbClientPtr cl, int w, int h);
+static Bool SendIndexedRect (rfbClientPtr cl, int w, int h);
+static Bool SendFullColorRect (rfbClientPtr cl, int w, int h);
+static Bool SendGradientRect (rfbClientPtr cl, int w, int h);
+
+static Bool CompressData(rfbClientPtr cl, int streamId, int dataLen,
+ int zlibLevel, int zlibStrategy);
+static Bool SendCompressedData(rfbClientPtr cl, int compressedLen);
+
+static void FillPalette8(int count);
+static void FillPalette16(int count);
+static void FillPalette32(int count);
+
+static void PaletteReset(void);
+static int PaletteInsert(CARD32 rgb, int numPixels, int bpp);
+
+static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count);
+
+static void EncodeIndexedRect16(CARD8 *buf, int count);
+static void EncodeIndexedRect32(CARD8 *buf, int count);
+
+static void EncodeMonoRect8(CARD8 *buf, int w, int h);
+static void EncodeMonoRect16(CARD8 *buf, int w, int h);
+static void EncodeMonoRect32(CARD8 *buf, int w, int h);
+
+static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h);
+static void FilterGradient16(rfbClientPtr cl, CARD16 *buf, rfbPixelFormat *fmt, int w, int h);
+static void FilterGradient32(rfbClientPtr cl, CARD32 *buf, rfbPixelFormat *fmt, int w, int h);
+
+static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
+static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
+static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
+static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h);
+
+static Bool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h,
+ int quality);
+static void PrepareRowForJpeg(rfbClientPtr cl, CARD8 *dst, int x, int y, int count);
+static void PrepareRowForJpeg24(rfbClientPtr cl, CARD8 *dst, int x, int y, int count);
+static void PrepareRowForJpeg16(rfbClientPtr cl, CARD8 *dst, int x, int y, int count);
+static void PrepareRowForJpeg32(rfbClientPtr cl, CARD8 *dst, int x, int y, int count);
+
+static void JpegInitDestination(j_compress_ptr cinfo);
+static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo);
+static void JpegTermDestination(j_compress_ptr cinfo);
+static void JpegSetDstManager(j_compress_ptr cinfo);
+
+
+/*
+ * Tight encoding implementation.
+ */
+
+int
+rfbNumCodedRectsTight(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ int maxRectSize, maxRectWidth;
+ int subrectMaxWidth, subrectMaxHeight;
+
+ /* No matter how many rectangles we will send if LastRect markers
+ are used to terminate rectangle stream. */
+ if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE)
+ return 0;
+
+ maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize;
+ maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth;
+
+ if (w > maxRectWidth || w * h > maxRectSize) {
+ subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ subrectMaxHeight = maxRectSize / subrectMaxWidth;
+ return (((w - 1) / maxRectWidth + 1) *
+ ((h - 1) / subrectMaxHeight + 1));
+ } else {
+ return 1;
+ }
+}
+
+Bool
+rfbSendRectEncodingTight(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ int nMaxRows;
+ CARD32 colorValue;
+ int dx, dy, dw, dh;
+ int x_best, y_best, w_best, h_best;
+ char *fbptr;
+
+ compressLevel = cl->tightCompressLevel;
+ qualityLevel = cl->tightQualityLevel;
+
+ if ( cl->format.depth == 24 && cl->format.redMax == 0xFF &&
+ cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) {
+ usePixelFormat24 = TRUE;
+ } else {
+ usePixelFormat24 = FALSE;
+ }
+
+ if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE)
+ return SendRectSimple(cl, x, y, w, h);
+
+ /* Make sure we can write at least one pixel into tightBeforeBuf. */
+
+ if (tightBeforeBufSize < 4) {
+ tightBeforeBufSize = 4;
+ if (tightBeforeBuf == NULL)
+ tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
+ else
+ tightBeforeBuf = (char *)realloc(tightBeforeBuf,
+ tightBeforeBufSize);
+ }
+
+ /* Calculate maximum number of rows in one non-solid rectangle. */
+
+ {
+ int maxRectSize, maxRectWidth, nMaxWidth;
+
+ maxRectSize = tightConf[compressLevel].maxRectSize;
+ maxRectWidth = tightConf[compressLevel].maxRectWidth;
+ nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ nMaxRows = maxRectSize / nMaxWidth;
+ }
+
+ /* Try to find large solid-color areas and send them separately. */
+
+ for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
+
+ /* If a rectangle becomes too large, send its upper part now. */
+
+ if (dy - y >= nMaxRows) {
+ if (!SendRectSimple(cl, x, y, w, nMaxRows))
+ return 0;
+ y += nMaxRows;
+ h -= nMaxRows;
+ }
+
+ dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
+ MAX_SPLIT_TILE_SIZE : (y + h - dy);
+
+ for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
+
+ dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
+ MAX_SPLIT_TILE_SIZE : (x + w - dx);
+
+ if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) {
+
+ /* Get dimensions of solid-color area. */
+
+ FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y),
+ colorValue, &w_best, &h_best);
+
+ /* Make sure a solid rectangle is large enough
+ (or the whole rectangle is of the same color). */
+
+ if ( w_best * h_best != w * h &&
+ w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
+ continue;
+
+ /* Try to extend solid rectangle to maximum size. */
+
+ x_best = dx; y_best = dy;
+ ExtendSolidArea(cl, x, y, w, h, colorValue,
+ &x_best, &y_best, &w_best, &h_best);
+
+ /* Send rectangles at top and left to solid-color area. */
+
+ if ( y_best != y &&
+ !SendRectSimple(cl, x, y, w, y_best-y) )
+ return FALSE;
+ if ( x_best != x &&
+ !rfbSendRectEncodingTight(cl, x, y_best,
+ x_best-x, h_best) )
+ return FALSE;
+
+ /* Send solid-color rectangle. */
+
+ if (!SendTightHeader(cl, x_best, y_best, w_best, h_best))
+ return FALSE;
+
+ fbptr = (cl->screen->frameBuffer +
+ (cl->screen->paddedWidthInBytes * y_best) +
+ (x_best * (cl->screen->bitsPerPixel / 8)));
+
+ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,
+ &cl->format, fbptr, tightBeforeBuf,
+ cl->screen->paddedWidthInBytes, 1, 1);
+
+ if (!SendSolidRect(cl))
+ return FALSE;
+
+ /* Send remaining rectangles (at right and bottom). */
+
+ if ( x_best + w_best != x + w &&
+ !rfbSendRectEncodingTight(cl, x_best+w_best, y_best,
+ w-(x_best-x)-w_best, h_best) )
+ return FALSE;
+ if ( y_best + h_best != y + h &&
+ !rfbSendRectEncodingTight(cl, x, y_best+h_best,
+ w, h-(y_best-y)-h_best) )
+ return FALSE;
+
+ /* Return after all recursive calls are done. */
+
+ return TRUE;
+ }
+
+ }
+
+ }
+
+ /* No suitable solid-color rectangles found. */
+
+ return SendRectSimple(cl, x, y, w, h);
+}
+
+static void
+FindBestSolidArea(cl, x, y, w, h, colorValue, w_ptr, h_ptr)
+ rfbClientPtr cl;
+ int x, y, w, h;
+ CARD32 colorValue;
+ int *w_ptr, *h_ptr;
+{
+ int dx, dy, dw, dh;
+ int w_prev;
+ int w_best = 0, h_best = 0;
+
+ w_prev = w;
+
+ for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
+
+ dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
+ MAX_SPLIT_TILE_SIZE : (y + h - dy);
+ dw = (w_prev > MAX_SPLIT_TILE_SIZE) ?
+ MAX_SPLIT_TILE_SIZE : w_prev;
+
+ if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE))
+ break;
+
+ for (dx = x + dw; dx < x + w_prev;) {
+ dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ?
+ MAX_SPLIT_TILE_SIZE : (x + w_prev - dx);
+ if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE))
+ break;
+ dx += dw;
+ }
+
+ w_prev = dx - x;
+ if (w_prev * (dy + dh - y) > w_best * h_best) {
+ w_best = w_prev;
+ h_best = dy + dh - y;
+ }
+ }
+
+ *w_ptr = w_best;
+ *h_ptr = h_best;
+}
+
+static void
+ExtendSolidArea(cl, x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr)
+ rfbClientPtr cl;
+ int x, y, w, h;
+ CARD32 colorValue;
+ int *x_ptr, *y_ptr, *w_ptr, *h_ptr;
+{
+ int cx, cy;
+
+ /* Try to extend the area upwards. */
+ for ( cy = *y_ptr - 1;
+ cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
+ cy-- );
+ *h_ptr += *y_ptr - (cy + 1);
+ *y_ptr = cy + 1;
+
+ /* ... downwards. */
+ for ( cy = *y_ptr + *h_ptr;
+ cy < y + h &&
+ CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE);
+ cy++ );
+ *h_ptr += cy - (*y_ptr + *h_ptr);
+
+ /* ... to the left. */
+ for ( cx = *x_ptr - 1;
+ cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
+ cx-- );
+ *w_ptr += *x_ptr - (cx + 1);
+ *x_ptr = cx + 1;
+
+ /* ... to the right. */
+ for ( cx = *x_ptr + *w_ptr;
+ cx < x + w &&
+ CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE);
+ cx++ );
+ *w_ptr += cx - (*x_ptr + *w_ptr);
+}
+
+static Bool
+CheckSolidTile(cl, x, y, w, h, colorPtr, needSameColor)
+ rfbClientPtr cl;
+ int x, y, w, h;
+ CARD32 *colorPtr;
+ Bool needSameColor;
+{
+ switch(cl->screen->rfbServerFormat.bitsPerPixel) {
+ case 32:
+ return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor);
+ case 16:
+ return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor);
+ default:
+ return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor);
+ }
+}
+
+#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
+ \
+static Bool \
+CheckSolidTile##bpp(cl, x, y, w, h, colorPtr, needSameColor) \
+ rfbClientPtr cl; \
+ int x, y, w, h; \
+ CARD32 *colorPtr; \
+ Bool needSameColor; \
+{ \
+ CARD##bpp *fbptr; \
+ CARD##bpp colorValue; \
+ int dx, dy; \
+ \
+ fbptr = (CARD##bpp *) \
+ &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * (bpp/8)]; \
+ \
+ colorValue = *fbptr; \
+ if (needSameColor && (CARD32)colorValue != *colorPtr) \
+ return FALSE; \
+ \
+ for (dy = 0; dy < h; dy++) { \
+ for (dx = 0; dx < w; dx++) { \
+ if (colorValue != fbptr[dx]) \
+ return FALSE; \
+ } \
+ fbptr = (CARD##bpp *)((CARD8 *)fbptr + cl->screen->paddedWidthInBytes); \
+ } \
+ \
+ *colorPtr = (CARD32)colorValue; \
+ return TRUE; \
+}
+
+DEFINE_CHECK_SOLID_FUNCTION(8)
+DEFINE_CHECK_SOLID_FUNCTION(16)
+DEFINE_CHECK_SOLID_FUNCTION(32)
+
+static Bool
+SendRectSimple(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ int maxBeforeSize, maxAfterSize;
+ int maxRectSize, maxRectWidth;
+ int subrectMaxWidth, subrectMaxHeight;
+ int dx, dy;
+ int rw, rh;
+
+ maxRectSize = tightConf[compressLevel].maxRectSize;
+ maxRectWidth = tightConf[compressLevel].maxRectWidth;
+
+ maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8);
+ maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
+
+ if (tightBeforeBufSize < maxBeforeSize) {
+ tightBeforeBufSize = maxBeforeSize;
+ if (tightBeforeBuf == NULL)
+ tightBeforeBuf = (char *)malloc(tightBeforeBufSize);
+ else
+ tightBeforeBuf = (char *)realloc(tightBeforeBuf,
+ tightBeforeBufSize);
+ }
+
+ if (tightAfterBufSize < maxAfterSize) {
+ tightAfterBufSize = maxAfterSize;
+ if (tightAfterBuf == NULL)
+ tightAfterBuf = (char *)malloc(tightAfterBufSize);
+ else
+ tightAfterBuf = (char *)realloc(tightAfterBuf,
+ tightAfterBufSize);
+ }
+
+ if (w > maxRectWidth || w * h > maxRectSize) {
+ subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
+ subrectMaxHeight = maxRectSize / subrectMaxWidth;
+
+ for (dy = 0; dy < h; dy += subrectMaxHeight) {
+ for (dx = 0; dx < w; dx += maxRectWidth) {
+ rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx;
+ rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
+ if (!SendSubrect(cl, x+dx, y+dy, rw, rh))
+ return FALSE;
+ }
+ }
+ } else {
+ if (!SendSubrect(cl, x, y, w, h))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static Bool
+SendSubrect(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ char *fbptr;
+ Bool success = FALSE;
+
+ /* Send pending data if there is more than 128 bytes. */
+ if (cl->ublen > 128) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ if (!SendTightHeader(cl, x, y, w, h))
+ return FALSE;
+
+ fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
+ + (x * (cl->screen->bitsPerPixel / 8)));
+
+ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,
+ &cl->format, fbptr, tightBeforeBuf,
+ cl->screen->paddedWidthInBytes, w, h);
+
+ paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor;
+ if ( paletteMaxColors < 2 &&
+ w * h >= tightConf[compressLevel].monoMinRectSize ) {
+ paletteMaxColors = 2;
+ }
+ switch (cl->format.bitsPerPixel) {
+ case 8:
+ FillPalette8(w * h);
+ break;
+ case 16:
+ FillPalette16(w * h);
+ break;
+ default:
+ FillPalette32(w * h);
+ }
+
+ switch (paletteNumColors) {
+ case 0:
+ /* Truecolor image */
+ if (DetectSmoothImage(cl, &cl->format, w, h)) {
+ if (qualityLevel != -1) {
+ success = SendJpegRect(cl, x, y, w, h,
+ tightConf[qualityLevel].jpegQuality);
+ } else {
+ success = SendGradientRect(cl, w, h);
+ }
+ } else {
+ success = SendFullColorRect(cl, w, h);
+ }
+ break;
+ case 1:
+ /* Solid rectangle */
+ success = SendSolidRect(cl);
+ break;
+ case 2:
+ /* Two-color rectangle */
+ success = SendMonoRect(cl, w, h);
+ break;
+ default:
+ /* Up to 256 different colors */
+ if ( paletteNumColors > 96 &&
+ qualityLevel != -1 && qualityLevel <= 3 &&
+ DetectSmoothImage(cl, &cl->format, w, h) ) {
+ success = SendJpegRect(cl, x, y, w, h,
+ tightConf[qualityLevel].jpegQuality);
+ } else {
+ success = SendIndexedRect(cl, w, h);
+ }
+ }
+ return success;
+}
+
+static Bool
+SendTightHeader(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingTight);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ cl->rfbRectanglesSent[rfbEncodingTight]++;
+ cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader;
+
+ return TRUE;
+}
+
+/*
+ * Subencoding implementations.
+ */
+
+static Bool
+SendSolidRect(cl)
+ rfbClientPtr cl;
+{
+ int len;
+
+ if (usePixelFormat24) {
+ Pack24(cl, tightBeforeBuf, &cl->format, 1);
+ len = 3;
+ } else
+ len = cl->format.bitsPerPixel / 8;
+
+ if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4);
+ memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len);
+ cl->ublen += len;
+
+ cl->rfbBytesSent[rfbEncodingTight] += len + 1;
+
+ return TRUE;
+}
+
+static Bool
+SendMonoRect(cl, w, h)
+ rfbClientPtr cl;
+ int w, h;
+{
+ int streamId = 1;
+ int paletteLen, dataLen;
+
+ if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
+ 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ /* Prepare tight encoding header. */
+ dataLen = (w + 7) / 8;
+ dataLen *= h;
+
+ cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+ cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
+ cl->updateBuf[cl->ublen++] = 1;
+
+ /* Prepare palette, convert image. */
+ switch (cl->format.bitsPerPixel) {
+
+ case 32:
+ EncodeMonoRect32((CARD8 *)tightBeforeBuf, w, h);
+
+ ((CARD32 *)tightAfterBuf)[0] = monoBackground;
+ ((CARD32 *)tightAfterBuf)[1] = monoForeground;
+ if (usePixelFormat24) {
+ Pack24(cl, tightAfterBuf, &cl->format, 2);
+ paletteLen = 6;
+ } else
+ paletteLen = 8;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen);
+ cl->ublen += paletteLen;
+ cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen;
+ break;
+
+ case 16:
+ EncodeMonoRect16((CARD8 *)tightBeforeBuf, w, h);
+
+ ((CARD16 *)tightAfterBuf)[0] = (CARD16)monoBackground;
+ ((CARD16 *)tightAfterBuf)[1] = (CARD16)monoForeground;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4);
+ cl->ublen += 4;
+ cl->rfbBytesSent[rfbEncodingTight] += 7;
+ break;
+
+ default:
+ EncodeMonoRect8((CARD8 *)tightBeforeBuf, w, h);
+
+ cl->updateBuf[cl->ublen++] = (char)monoBackground;
+ cl->updateBuf[cl->ublen++] = (char)monoForeground;
+ cl->rfbBytesSent[rfbEncodingTight] += 5;
+ }
+
+ return CompressData(cl, streamId, dataLen,
+ tightConf[compressLevel].monoZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static Bool
+SendIndexedRect(cl, w, h)
+ rfbClientPtr cl;
+ int w, h;
+{
+ int streamId = 2;
+ int i, entryLen;
+
+ if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 +
+ paletteNumColors * cl->format.bitsPerPixel / 8 >
+ UPDATE_BUF_SIZE ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ /* Prepare tight encoding header. */
+ cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+ cl->updateBuf[cl->ublen++] = rfbTightFilterPalette;
+ cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1);
+
+ /* Prepare palette, convert image. */
+ switch (cl->format.bitsPerPixel) {
+
+ case 32:
+ EncodeIndexedRect32((CARD8 *)tightBeforeBuf, w * h);
+
+ for (i = 0; i < paletteNumColors; i++) {
+ ((CARD32 *)tightAfterBuf)[i] =
+ palette.entry[i].listNode->rgb;
+ }
+ if (usePixelFormat24) {
+ Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors);
+ entryLen = 3;
+ } else
+ entryLen = 4;
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen);
+ cl->ublen += paletteNumColors * entryLen;
+ cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen;
+ break;
+
+ case 16:
+ EncodeIndexedRect16((CARD8 *)tightBeforeBuf, w * h);
+
+ for (i = 0; i < paletteNumColors; i++) {
+ ((CARD16 *)tightAfterBuf)[i] =
+ (CARD16)palette.entry[i].listNode->rgb;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2);
+ cl->ublen += paletteNumColors * 2;
+ cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2;
+ break;
+
+ default:
+ return FALSE; /* Should never happen. */
+ }
+
+ return CompressData(cl, streamId, w * h,
+ tightConf[compressLevel].idxZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static Bool
+SendFullColorRect(cl, w, h)
+ rfbClientPtr cl;
+ int w, h;
+{
+ int streamId = 0;
+ int len;
+
+ if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */
+ cl->rfbBytesSent[rfbEncodingTight]++;
+
+ if (usePixelFormat24) {
+ Pack24(cl, tightBeforeBuf, &cl->format, w * h);
+ len = 3;
+ } else
+ len = cl->format.bitsPerPixel / 8;
+
+ return CompressData(cl, streamId, w * h * len,
+ tightConf[compressLevel].rawZlibLevel,
+ Z_DEFAULT_STRATEGY);
+}
+
+static Bool
+SendGradientRect(cl, w, h)
+ rfbClientPtr cl;
+ int w, h;
+{
+ int streamId = 3;
+ int len;
+
+ if (cl->format.bitsPerPixel == 8)
+ return SendFullColorRect(cl, w, h);
+
+ if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ if (prevRowBuf == NULL)
+ prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int));
+
+ cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4;
+ cl->updateBuf[cl->ublen++] = rfbTightFilterGradient;
+ cl->rfbBytesSent[rfbEncodingTight] += 2;
+
+ if (usePixelFormat24) {
+ FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h);
+ len = 3;
+ } else if (cl->format.bitsPerPixel == 32) {
+ FilterGradient32(cl, (CARD32 *)tightBeforeBuf, &cl->format, w, h);
+ len = 4;
+ } else {
+ FilterGradient16(cl, (CARD16 *)tightBeforeBuf, &cl->format, w, h);
+ len = 2;
+ }
+
+ return CompressData(cl, streamId, w * h * len,
+ tightConf[compressLevel].gradientZlibLevel,
+ Z_FILTERED);
+}
+
+static Bool
+CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy)
+ rfbClientPtr cl;
+ int streamId, dataLen, zlibLevel, zlibStrategy;
+{
+ z_streamp pz;
+ int err;
+
+ if (dataLen < TIGHT_MIN_TO_COMPRESS) {
+ memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen);
+ cl->ublen += dataLen;
+ cl->rfbBytesSent[rfbEncodingTight] += dataLen;
+ return TRUE;
+ }
+
+ pz = &cl->zsStruct[streamId];
+
+ /* Initialize compression stream if needed. */
+ if (!cl->zsActive[streamId]) {
+ pz->zalloc = Z_NULL;
+ pz->zfree = Z_NULL;
+ pz->opaque = Z_NULL;
+
+ err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS,
+ MAX_MEM_LEVEL, zlibStrategy);
+ if (err != Z_OK)
+ return FALSE;
+
+ cl->zsActive[streamId] = TRUE;
+ cl->zsLevel[streamId] = zlibLevel;
+ }
+
+ /* Prepare buffer pointers. */
+ pz->next_in = (Bytef *)tightBeforeBuf;
+ pz->avail_in = dataLen;
+ pz->next_out = (Bytef *)tightAfterBuf;
+ pz->avail_out = tightAfterBufSize;
+
+ /* Change compression parameters if needed. */
+ if (zlibLevel != cl->zsLevel[streamId]) {
+ if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) {
+ return FALSE;
+ }
+ cl->zsLevel[streamId] = zlibLevel;
+ }
+
+ /* Actual compression. */
+ if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK ||
+ pz->avail_in != 0 || pz->avail_out == 0 ) {
+ return FALSE;
+ }
+
+ return SendCompressedData(cl, tightAfterBufSize - pz->avail_out);
+}
+
+static Bool SendCompressedData(cl, compressedLen)
+ rfbClientPtr cl;
+ int compressedLen;
+{
+ int i, portionLen;
+
+ cl->updateBuf[cl->ublen++] = compressedLen & 0x7F;
+ cl->rfbBytesSent[rfbEncodingTight]++;
+ if (compressedLen > 0x7F) {
+ cl->updateBuf[cl->ublen-1] |= 0x80;
+ cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F;
+ cl->rfbBytesSent[rfbEncodingTight]++;
+ if (compressedLen > 0x3FFF) {
+ cl->updateBuf[cl->ublen-1] |= 0x80;
+ cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF;
+ cl->rfbBytesSent[rfbEncodingTight]++;
+ }
+ }
+
+ portionLen = UPDATE_BUF_SIZE;
+ for (i = 0; i < compressedLen; i += portionLen) {
+ if (i + portionLen > compressedLen) {
+ portionLen = compressedLen - i;
+ }
+ if (cl->ublen + portionLen > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen);
+ cl->ublen += portionLen;
+ }
+ portionLen = UPDATE_BUF_SIZE;
+ cl->rfbBytesSent[rfbEncodingTight] += compressedLen;
+ return TRUE;
+}
+
+/*
+ * Code to determine how many different colors used in rectangle.
+ */
+
+static void
+FillPalette8(count)
+ int count;
+{
+ CARD8 *data = (CARD8 *)tightBeforeBuf;
+ CARD8 c0, c1;
+ int i, n0, n1;
+
+ paletteNumColors = 0;
+
+ c0 = data[0];
+ for (i = 1; i < count && data[i] == c0; i++);
+ if (i == count) {
+ paletteNumColors = 1;
+ return; /* Solid rectangle */
+ }
+
+ if (paletteMaxColors < 2)
+ return;
+
+ n0 = i;
+ c1 = data[i];
+ n1 = 0;
+ for (i++; i < count; i++) {
+ if (data[i] == c0) {
+ n0++;
+ } else if (data[i] == c1) {
+ n1++;
+ } else
+ break;
+ }
+ if (i == count) {
+ if (n0 > n1) {
+ monoBackground = (CARD32)c0;
+ monoForeground = (CARD32)c1;
+ } else {
+ monoBackground = (CARD32)c1;
+ monoForeground = (CARD32)c0;
+ }
+ paletteNumColors = 2; /* Two colors */
+ }
+}
+
+#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \
+ \
+static void \
+FillPalette##bpp(count) \
+ int count; \
+{ \
+ CARD##bpp *data = (CARD##bpp *)tightBeforeBuf; \
+ CARD##bpp c0, c1, ci; \
+ int i, n0, n1, ni; \
+ \
+ c0 = data[0]; \
+ for (i = 1; i < count && data[i] == c0; i++); \
+ if (i >= count) { \
+ paletteNumColors = 1; /* Solid rectangle */ \
+ return; \
+ } \
+ \
+ if (paletteMaxColors < 2) { \
+ paletteNumColors = 0; /* Full-color encoding preferred */ \
+ return; \
+ } \
+ \
+ n0 = i; \
+ c1 = data[i]; \
+ n1 = 0; \
+ for (i++; i < count; i++) { \
+ ci = data[i]; \
+ if (ci == c0) { \
+ n0++; \
+ } else if (ci == c1) { \
+ n1++; \
+ } else \
+ break; \
+ } \
+ if (i >= count) { \
+ if (n0 > n1) { \
+ monoBackground = (CARD32)c0; \
+ monoForeground = (CARD32)c1; \
+ } else { \
+ monoBackground = (CARD32)c1; \
+ monoForeground = (CARD32)c0; \
+ } \
+ paletteNumColors = 2; /* Two colors */ \
+ return; \
+ } \
+ \
+ PaletteReset(); \
+ PaletteInsert (c0, (CARD32)n0, bpp); \
+ PaletteInsert (c1, (CARD32)n1, bpp); \
+ \
+ ni = 1; \
+ for (i++; i < count; i++) { \
+ if (data[i] == ci) { \
+ ni++; \
+ } else { \
+ if (!PaletteInsert (ci, (CARD32)ni, bpp)) \
+ return; \
+ ci = data[i]; \
+ ni = 1; \
+ } \
+ } \
+ PaletteInsert (ci, (CARD32)ni, bpp); \
+}
+
+DEFINE_FILL_PALETTE_FUNCTION(16)
+DEFINE_FILL_PALETTE_FUNCTION(32)
+
+
+/*
+ * Functions to operate with palette structures.
+ */
+
+#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
+#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
+
+static void
+PaletteReset(void)
+{
+ paletteNumColors = 0;
+ memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *));
+}
+
+static int
+PaletteInsert(rgb, numPixels, bpp)
+ CARD32 rgb;
+ int numPixels;
+ int bpp;
+{
+ COLOR_LIST *pnode;
+ COLOR_LIST *prev_pnode = NULL;
+ int hash_key, idx, new_idx, count;
+
+ hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
+
+ pnode = palette.hash[hash_key];
+
+ while (pnode != NULL) {
+ if (pnode->rgb == rgb) {
+ /* Such palette entry already exists. */
+ new_idx = idx = pnode->idx;
+ count = palette.entry[idx].numPixels + numPixels;
+ if (new_idx && palette.entry[new_idx-1].numPixels < count) {
+ do {
+ palette.entry[new_idx] = palette.entry[new_idx-1];
+ palette.entry[new_idx].listNode->idx = new_idx;
+ new_idx--;
+ }
+ while (new_idx && palette.entry[new_idx-1].numPixels < count);
+ palette.entry[new_idx].listNode = pnode;
+ pnode->idx = new_idx;
+ }
+ palette.entry[new_idx].numPixels = count;
+ return paletteNumColors;
+ }
+ prev_pnode = pnode;
+ pnode = pnode->next;
+ }
+
+ /* Check if palette is full. */
+ if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) {
+ paletteNumColors = 0;
+ return 0;
+ }
+
+ /* Move palette entries with lesser pixel counts. */
+ for ( idx = paletteNumColors;
+ idx > 0 && palette.entry[idx-1].numPixels < numPixels;
+ idx-- ) {
+ palette.entry[idx] = palette.entry[idx-1];
+ palette.entry[idx].listNode->idx = idx;
+ }
+
+ /* Add new palette entry into the freed slot. */
+ pnode = &palette.list[paletteNumColors];
+ if (prev_pnode != NULL) {
+ prev_pnode->next = pnode;
+ } else {
+ palette.hash[hash_key] = pnode;
+ }
+ pnode->next = NULL;
+ pnode->idx = idx;
+ pnode->rgb = rgb;
+ palette.entry[idx].listNode = pnode;
+ palette.entry[idx].numPixels = numPixels;
+
+ return (++paletteNumColors);
+}
+
+
+/*
+ * Converting 32-bit color samples into 24-bit colors.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void Pack24(cl, buf, fmt, count)
+ rfbClientPtr cl;
+ char *buf;
+ rfbPixelFormat *fmt;
+ int count;
+{
+ CARD32 *buf32;
+ CARD32 pix;
+ int r_shift, g_shift, b_shift;
+
+ buf32 = (CARD32 *)buf;
+
+ if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) {
+ r_shift = fmt->redShift;
+ g_shift = fmt->greenShift;
+ b_shift = fmt->blueShift;
+ } else {
+ r_shift = 24 - fmt->redShift;
+ g_shift = 24 - fmt->greenShift;
+ b_shift = 24 - fmt->blueShift;
+ }
+
+ while (count--) {
+ pix = *buf32++;
+ *buf++ = (char)(pix >> r_shift);
+ *buf++ = (char)(pix >> g_shift);
+ *buf++ = (char)(pix >> b_shift);
+ }
+}
+
+
+/*
+ * Converting truecolor samples into palette indices.
+ */
+
+#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \
+ \
+static void \
+EncodeIndexedRect##bpp(buf, count) \
+ CARD8 *buf; \
+ int count; \
+{ \
+ COLOR_LIST *pnode; \
+ CARD##bpp *src; \
+ CARD##bpp rgb; \
+ int rep = 0; \
+ \
+ src = (CARD##bpp *) buf; \
+ \
+ while (count--) { \
+ rgb = *src++; \
+ while (count && *src == rgb) { \
+ rep++, src++, count--; \
+ } \
+ pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \
+ while (pnode != NULL) { \
+ if ((CARD##bpp)pnode->rgb == rgb) { \
+ *buf++ = (CARD8)pnode->idx; \
+ while (rep) { \
+ *buf++ = (CARD8)pnode->idx; \
+ rep--; \
+ } \
+ break; \
+ } \
+ pnode = pnode->next; \
+ } \
+ } \
+}
+
+DEFINE_IDX_ENCODE_FUNCTION(16)
+DEFINE_IDX_ENCODE_FUNCTION(32)
+
+#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \
+ \
+static void \
+EncodeMonoRect##bpp(buf, w, h) \
+ CARD8 *buf; \
+ int w, h; \
+{ \
+ CARD##bpp *ptr; \
+ CARD##bpp bg; \
+ unsigned int value, mask; \
+ int aligned_width; \
+ int x, y, bg_bits; \
+ \
+ ptr = (CARD##bpp *) buf; \
+ bg = (CARD##bpp) monoBackground; \
+ aligned_width = w - w % 8; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (x = 0; x < aligned_width; x += 8) { \
+ for (bg_bits = 0; bg_bits < 8; bg_bits++) { \
+ if (*ptr++ != bg) \
+ break; \
+ } \
+ if (bg_bits == 8) { \
+ *buf++ = 0; \
+ continue; \
+ } \
+ mask = 0x80 >> bg_bits; \
+ value = mask; \
+ for (bg_bits++; bg_bits < 8; bg_bits++) { \
+ mask >>= 1; \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ } \
+ *buf++ = (CARD8)value; \
+ } \
+ \
+ mask = 0x80; \
+ value = 0; \
+ if (x >= w) \
+ continue; \
+ \
+ for (; x < w; x++) { \
+ if (*ptr++ != bg) { \
+ value |= mask; \
+ } \
+ mask >>= 1; \
+ } \
+ *buf++ = (CARD8)value; \
+ } \
+}
+
+DEFINE_MONO_ENCODE_FUNCTION(8)
+DEFINE_MONO_ENCODE_FUNCTION(16)
+DEFINE_MONO_ENCODE_FUNCTION(32)
+
+
+/*
+ * ``Gradient'' filter for 24-bit color samples.
+ * Should be called only when redMax, greenMax and blueMax are 255.
+ * Color components assumed to be byte-aligned.
+ */
+
+static void
+FilterGradient24(cl, buf, fmt, w, h)
+ rfbClientPtr cl;
+ char *buf;
+ rfbPixelFormat *fmt;
+ int w, h;
+{
+ CARD32 *buf32;
+ CARD32 pix32;
+ int *prevRowPtr;
+ int shiftBits[3];
+ int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3];
+ int prediction;
+ int x, y, c;
+
+ buf32 = (CARD32 *)buf;
+ memset (prevRowBuf, 0, w * 3 * sizeof(int));
+
+ if (!cl->screen->rfbServerFormat.bigEndian == !fmt->bigEndian) {
+ shiftBits[0] = fmt->redShift;
+ shiftBits[1] = fmt->greenShift;
+ shiftBits[2] = fmt->blueShift;
+ } else {
+ shiftBits[0] = 24 - fmt->redShift;
+ shiftBits[1] = 24 - fmt->greenShift;
+ shiftBits[2] = 24 - fmt->blueShift;
+ }
+
+ for (y = 0; y < h; y++) {
+ for (c = 0; c < 3; c++) {
+ pixUpper[c] = 0;
+ pixHere[c] = 0;
+ }
+ prevRowPtr = prevRowBuf;
+ for (x = 0; x < w; x++) {
+ pix32 = *buf32++;
+ for (c = 0; c < 3; c++) {
+ pixUpperLeft[c] = pixUpper[c];
+ pixLeft[c] = pixHere[c];
+ pixUpper[c] = *prevRowPtr;
+ pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF);
+ *prevRowPtr++ = pixHere[c];
+
+ prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c];
+ if (prediction < 0) {
+ prediction = 0;
+ } else if (prediction > 0xFF) {
+ prediction = 0xFF;
+ }
+ *buf++ = (char)(pixHere[c] - prediction);
+ }
+ }
+ }
+}
+
+
+/*
+ * ``Gradient'' filter for other color depths.
+ */
+
+#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \
+ \
+static void \
+FilterGradient##bpp(cl, buf, fmt, w, h) \
+ rfbClientPtr cl; \
+ CARD##bpp *buf; \
+ rfbPixelFormat *fmt; \
+ int w, h; \
+{ \
+ CARD##bpp pix, diff; \
+ Bool endianMismatch; \
+ int *prevRowPtr; \
+ int maxColor[3], shiftBits[3]; \
+ int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \
+ int prediction; \
+ int x, y, c; \
+ \
+ memset (prevRowBuf, 0, w * 3 * sizeof(int)); \
+ \
+ endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
+ \
+ maxColor[0] = fmt->redMax; \
+ maxColor[1] = fmt->greenMax; \
+ maxColor[2] = fmt->blueMax; \
+ shiftBits[0] = fmt->redShift; \
+ shiftBits[1] = fmt->greenShift; \
+ shiftBits[2] = fmt->blueShift; \
+ \
+ for (y = 0; y < h; y++) { \
+ for (c = 0; c < 3; c++) { \
+ pixUpper[c] = 0; \
+ pixHere[c] = 0; \
+ } \
+ prevRowPtr = prevRowBuf; \
+ for (x = 0; x < w; x++) { \
+ pix = *buf; \
+ if (endianMismatch) { \
+ pix = Swap##bpp(pix); \
+ } \
+ diff = 0; \
+ for (c = 0; c < 3; c++) { \
+ pixUpperLeft[c] = pixUpper[c]; \
+ pixLeft[c] = pixHere[c]; \
+ pixUpper[c] = *prevRowPtr; \
+ pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
+ *prevRowPtr++ = pixHere[c]; \
+ \
+ prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \
+ if (prediction < 0) { \
+ prediction = 0; \
+ } else if (prediction > maxColor[c]) { \
+ prediction = maxColor[c]; \
+ } \
+ diff |= ((pixHere[c] - prediction) & maxColor[c]) \
+ << shiftBits[c]; \
+ } \
+ if (endianMismatch) { \
+ diff = Swap##bpp(diff); \
+ } \
+ *buf++ = diff; \
+ } \
+ } \
+}
+
+DEFINE_GRADIENT_FILTER_FUNCTION(16)
+DEFINE_GRADIENT_FILTER_FUNCTION(32)
+
+
+/*
+ * Code to guess if given rectangle is suitable for smooth image
+ * compression (by applying "gradient" filter or JPEG coder).
+ */
+
+#define JPEG_MIN_RECT_SIZE 4096
+
+#define DETECT_SUBROW_WIDTH 7
+#define DETECT_MIN_WIDTH 8
+#define DETECT_MIN_HEIGHT 8
+
+static int
+DetectSmoothImage (cl, fmt, w, h)
+ rfbClientPtr cl;
+ rfbPixelFormat *fmt;
+ int w, h;
+{
+ long avgError;
+
+ if ( cl->screen->rfbServerFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 ||
+ w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) {
+ return 0;
+ }
+
+ if (qualityLevel != -1) {
+ if (w * h < JPEG_MIN_RECT_SIZE) {
+ return 0;
+ }
+ } else {
+ if ( rfbTightDisableGradient ||
+ w * h < tightConf[compressLevel].gradientMinRectSize ) {
+ return 0;
+ }
+ }
+
+ if (fmt->bitsPerPixel == 32) {
+ if (usePixelFormat24) {
+ avgError = DetectSmoothImage24(cl, fmt, w, h);
+ if (qualityLevel != -1) {
+ return (avgError < tightConf[qualityLevel].jpegThreshold24);
+ }
+ return (avgError < tightConf[compressLevel].gradientThreshold24);
+ } else {
+ avgError = DetectSmoothImage32(cl, fmt, w, h);
+ }
+ } else {
+ avgError = DetectSmoothImage16(cl, fmt, w, h);
+ }
+ if (qualityLevel != -1) {
+ return (avgError < tightConf[qualityLevel].jpegThreshold);
+ }
+ return (avgError < tightConf[compressLevel].gradientThreshold);
+}
+
+static unsigned long
+DetectSmoothImage24 (cl, fmt, w, h)
+ rfbClientPtr cl;
+ rfbPixelFormat *fmt;
+ int w, h;
+{
+ int off;
+ int x, y, d, dx, c;
+ int diffStat[256];
+ int pixelCount = 0;
+ int pix, left[3];
+ unsigned long avgError;
+
+ /* If client is big-endian, color samples begin from the second
+ byte (offset 1) of a 32-bit pixel value. */
+ off = (fmt->bigEndian != 0);
+
+ memset(diffStat, 0, 256*sizeof(int));
+
+ y = 0, x = 0;
+ while (y < h && x < w) {
+ for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) {
+ for (c = 0; c < 3; c++) {
+ left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF;
+ }
+ for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) {
+ for (c = 0; c < 3; c++) {
+ pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF;
+ diffStat[abs(pix - left[c])]++;
+ left[c] = pix;
+ }
+ pixelCount++;
+ }
+ }
+ if (w > h) {
+ x += h;
+ y = 0;
+ } else {
+ x = 0;
+ y += w;
+ }
+ }
+
+ if (diffStat[0] * 33 / pixelCount >= 95)
+ return 0;
+
+ avgError = 0;
+ for (c = 1; c < 8; c++) {
+ avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
+ if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2)
+ return 0;
+ }
+ for (; c < 256; c++) {
+ avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c);
+ }
+ avgError /= (pixelCount * 3 - diffStat[0]);
+
+ return avgError;
+}
+
+#define DEFINE_DETECT_FUNCTION(bpp) \
+ \
+static unsigned long \
+DetectSmoothImage##bpp (cl, fmt, w, h) \
+ rfbClientPtr cl; \
+ rfbPixelFormat *fmt; \
+ int w, h; \
+{ \
+ Bool endianMismatch; \
+ CARD##bpp pix; \
+ int maxColor[3], shiftBits[3]; \
+ int x, y, d, dx, c; \
+ int diffStat[256]; \
+ int pixelCount = 0; \
+ int sample, sum, left[3]; \
+ unsigned long avgError; \
+ \
+ endianMismatch = (!cl->screen->rfbServerFormat.bigEndian != !fmt->bigEndian); \
+ \
+ maxColor[0] = fmt->redMax; \
+ maxColor[1] = fmt->greenMax; \
+ maxColor[2] = fmt->blueMax; \
+ shiftBits[0] = fmt->redShift; \
+ shiftBits[1] = fmt->greenShift; \
+ shiftBits[2] = fmt->blueShift; \
+ \
+ memset(diffStat, 0, 256*sizeof(int)); \
+ \
+ y = 0, x = 0; \
+ while (y < h && x < w) { \
+ for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \
+ pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d]; \
+ if (endianMismatch) { \
+ pix = Swap##bpp(pix); \
+ } \
+ for (c = 0; c < 3; c++) { \
+ left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \
+ } \
+ for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \
+ pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \
+ if (endianMismatch) { \
+ pix = Swap##bpp(pix); \
+ } \
+ sum = 0; \
+ for (c = 0; c < 3; c++) { \
+ sample = (int)(pix >> shiftBits[c] & maxColor[c]); \
+ sum += abs(sample - left[c]); \
+ left[c] = sample; \
+ } \
+ if (sum > 255) \
+ sum = 255; \
+ diffStat[sum]++; \
+ pixelCount++; \
+ } \
+ } \
+ if (w > h) { \
+ x += h; \
+ y = 0; \
+ } else { \
+ x = 0; \
+ y += w; \
+ } \
+ } \
+ \
+ if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \
+ return 0; \
+ \
+ avgError = 0; \
+ for (c = 1; c < 8; c++) { \
+ avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
+ if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \
+ return 0; \
+ } \
+ for (; c < 256; c++) { \
+ avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \
+ } \
+ avgError /= (pixelCount - diffStat[0]); \
+ \
+ return avgError; \
+}
+
+DEFINE_DETECT_FUNCTION(16)
+DEFINE_DETECT_FUNCTION(32)
+
+
+/*
+ * JPEG compression stuff.
+ */
+
+static struct jpeg_destination_mgr jpegDstManager;
+static Bool jpegError;
+static int jpegDstDataLen;
+
+static Bool
+SendJpegRect(cl, x, y, w, h, quality)
+ rfbClientPtr cl;
+ int x, y, w, h;
+ int quality;
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ CARD8 *srcBuf;
+ JSAMPROW rowPointer[1];
+ int dy;
+
+ if (cl->screen->rfbServerFormat.bitsPerPixel == 8)
+ return SendFullColorRect(cl, w, h);
+
+ srcBuf = (CARD8 *)malloc(w * 3);
+ if (srcBuf == NULL) {
+ return SendFullColorRect(cl, w, h);
+ }
+ rowPointer[0] = srcBuf;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE);
+
+ JpegSetDstManager (&cinfo);
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ for (dy = 0; dy < h; dy++) {
+ PrepareRowForJpeg(cl, srcBuf, x, y + dy, w);
+ jpeg_write_scanlines(&cinfo, rowPointer, 1);
+ if (jpegError)
+ break;
+ }
+
+ if (!jpegError)
+ jpeg_finish_compress(&cinfo);
+
+ jpeg_destroy_compress(&cinfo);
+ free(srcBuf);
+
+ if (jpegError)
+ return SendFullColorRect(cl, w, h);
+
+ if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4);
+ cl->rfbBytesSent[rfbEncodingTight]++;
+
+ return SendCompressedData(cl, jpegDstDataLen);
+}
+
+static void
+PrepareRowForJpeg(cl, dst, x, y, count)
+ rfbClientPtr cl;
+ CARD8 *dst;
+ int x, y, count;
+{
+ if (cl->screen->rfbServerFormat.bitsPerPixel == 32) {
+ if ( cl->screen->rfbServerFormat.redMax == 0xFF &&
+ cl->screen->rfbServerFormat.greenMax == 0xFF &&
+ cl->screen->rfbServerFormat.blueMax == 0xFF ) {
+ PrepareRowForJpeg24(cl, dst, x, y, count);
+ } else {
+ PrepareRowForJpeg32(cl, dst, x, y, count);
+ }
+ } else {
+ /* 16 bpp assumed. */
+ PrepareRowForJpeg16(cl, dst, x, y, count);
+ }
+}
+
+static void
+PrepareRowForJpeg24(cl, dst, x, y, count)
+ rfbClientPtr cl;
+ CARD8 *dst;
+ int x, y, count;
+{
+ CARD32 *fbptr;
+ CARD32 pix;
+
+ fbptr = (CARD32 *)
+ &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + x * 4];
+
+ while (count--) {
+ pix = *fbptr++;
+ *dst++ = (CARD8)(pix >> cl->screen->rfbServerFormat.redShift);
+ *dst++ = (CARD8)(pix >> cl->screen->rfbServerFormat.greenShift);
+ *dst++ = (CARD8)(pix >> cl->screen->rfbServerFormat.blueShift);
+ }
+}
+
+#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \
+ \
+static void \
+PrepareRowForJpeg##bpp(cl, dst, x, y, count) \
+ rfbClientPtr cl; \
+ CARD8 *dst; \
+ int x, y, count; \
+{ \
+ CARD##bpp *fbptr; \
+ CARD##bpp pix; \
+ int inRed, inGreen, inBlue; \
+ \
+ fbptr = (CARD##bpp *) \
+ &cl->screen->frameBuffer[y * cl->screen->paddedWidthInBytes + \
+ x * (bpp / 8)]; \
+ \
+ while (count--) { \
+ pix = *fbptr++; \
+ \
+ inRed = (int) \
+ (pix >> cl->screen->rfbServerFormat.redShift & cl->screen->rfbServerFormat.redMax); \
+ inGreen = (int) \
+ (pix >> cl->screen->rfbServerFormat.greenShift & cl->screen->rfbServerFormat.greenMax); \
+ inBlue = (int) \
+ (pix >> cl->screen->rfbServerFormat.blueShift & cl->screen->rfbServerFormat.blueMax); \
+ \
+ *dst++ = (CARD8)((inRed * 255 + cl->screen->rfbServerFormat.redMax / 2) / \
+ cl->screen->rfbServerFormat.redMax); \
+ *dst++ = (CARD8)((inGreen * 255 + cl->screen->rfbServerFormat.greenMax / 2) / \
+ cl->screen->rfbServerFormat.greenMax); \
+ *dst++ = (CARD8)((inBlue * 255 + cl->screen->rfbServerFormat.blueMax / 2) / \
+ cl->screen->rfbServerFormat.blueMax); \
+ } \
+}
+
+DEFINE_JPEG_GET_ROW_FUNCTION(16)
+DEFINE_JPEG_GET_ROW_FUNCTION(32)
+
+/*
+ * Destination manager implementation for JPEG library.
+ */
+
+static void
+JpegInitDestination(j_compress_ptr cinfo)
+{
+ jpegError = FALSE;
+ jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
+ jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
+}
+
+static boolean
+JpegEmptyOutputBuffer(j_compress_ptr cinfo)
+{
+ jpegError = TRUE;
+ jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf;
+ jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize;
+
+ return TRUE;
+}
+
+static void
+JpegTermDestination(j_compress_ptr cinfo)
+{
+ jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer;
+}
+
+static void
+JpegSetDstManager(j_compress_ptr cinfo)
+{
+ jpegDstManager.init_destination = JpegInitDestination;
+ jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer;
+ jpegDstManager.term_destination = JpegTermDestination;
+ cinfo->dest = &jpegDstManager;
+}
+
diff --git a/krfb/libvncserver/translate.c b/krfb/libvncserver/translate.c
new file mode 100644
index 00000000..d6021d57
--- /dev/null
+++ b/krfb/libvncserver/translate.c
@@ -0,0 +1,484 @@
+/*
+ * translate.c - translate between different pixel formats
+ */
+
+/*
+ * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
+ * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.
+ * All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfb.h"
+#include "sraRegion.h"
+
+static void PrintPixelFormat(rfbPixelFormat *pf);
+static Bool rfbSetClientColourMapBGR233(rfbClientPtr cl);
+
+Bool rfbEconomicTranslate = FALSE;
+
+/*
+ * Some standard pixel formats.
+ */
+
+static const rfbPixelFormat BGR233Format = {
+ 8, 8, 0, 1, 7, 7, 3, 0, 3, 6, 0, 0
+};
+
+
+/*
+ * Macro to compare pixel formats.
+ */
+
+#define PF_EQ(x,y) \
+ ((x.bitsPerPixel == y.bitsPerPixel) && \
+ (x.depth == y.depth) && \
+ ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \
+ (x.trueColour == y.trueColour) && \
+ (!x.trueColour || ((x.redMax == y.redMax) && \
+ (x.greenMax == y.greenMax) && \
+ (x.blueMax == y.blueMax) && \
+ (x.redShift == y.redShift) && \
+ (x.greenShift == y.greenShift) && \
+ (x.blueShift == y.blueShift))))
+
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#define CONCAT4(a,b,c,d) a##b##c##d
+#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d)
+
+#undef OUT
+#undef IN
+
+#define OUT 8
+#include "tableinitcmtemplate.c"
+#include "tableinittctemplate.c"
+#define IN 8
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 16
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 32
+#include "tabletranstemplate.c"
+#undef IN
+#undef OUT
+
+#define OUT 16
+#include "tableinitcmtemplate.c"
+#include "tableinittctemplate.c"
+#define IN 8
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 16
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 32
+#include "tabletranstemplate.c"
+#undef IN
+#undef OUT
+
+#define OUT 32
+#include "tableinitcmtemplate.c"
+#include "tableinittctemplate.c"
+#define IN 8
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 16
+#include "tabletranstemplate.c"
+#undef IN
+#define IN 32
+#include "tabletranstemplate.c"
+#undef IN
+#undef OUT
+
+#ifdef ALLOW24BPP
+#define COUNT_OFFSETS 4
+#define BPP2OFFSET(bpp) ((bpp)/8-1)
+#include "tableinit24.c"
+#define BPP 8
+#include "tabletrans24template.c"
+#undef BPP
+#define BPP 16
+#include "tabletrans24template.c"
+#undef BPP
+#define BPP 24
+#include "tabletrans24template.c"
+#undef BPP
+#define BPP 32
+#include "tabletrans24template.c"
+#undef BPP
+#else
+#define COUNT_OFFSETS 3
+#define BPP2OFFSET(bpp) ((int)(bpp)/16)
+#endif
+
+typedef void (*rfbInitCMTableFnType)(char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out,rfbColourMap* cm);
+typedef void (*rfbInitTableFnType)(char **table, rfbPixelFormat *in,
+ rfbPixelFormat *out);
+
+rfbInitCMTableFnType rfbInitColourMapSingleTableFns[COUNT_OFFSETS] = {
+ rfbInitColourMapSingleTable8,
+ rfbInitColourMapSingleTable16,
+#ifdef ALLOW24BPP
+ rfbInitColourMapSingleTable24,
+#endif
+ rfbInitColourMapSingleTable32
+};
+
+rfbInitTableFnType rfbInitTrueColourSingleTableFns[COUNT_OFFSETS] = {
+ rfbInitTrueColourSingleTable8,
+ rfbInitTrueColourSingleTable16,
+#ifdef ALLOW24BPP
+ rfbInitTrueColourSingleTable24,
+#endif
+ rfbInitTrueColourSingleTable32
+};
+
+rfbInitTableFnType rfbInitTrueColourRGBTablesFns[COUNT_OFFSETS] = {
+ rfbInitTrueColourRGBTables8,
+ rfbInitTrueColourRGBTables16,
+#ifdef ALLOW24BPP
+ rfbInitTrueColourRGBTables24,
+#endif
+ rfbInitTrueColourRGBTables32
+};
+
+rfbTranslateFnType rfbTranslateWithSingleTableFns[COUNT_OFFSETS][COUNT_OFFSETS] = {
+ { rfbTranslateWithSingleTable8to8,
+ rfbTranslateWithSingleTable8to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithSingleTable8to24,
+#endif
+ rfbTranslateWithSingleTable8to32 },
+ { rfbTranslateWithSingleTable16to8,
+ rfbTranslateWithSingleTable16to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithSingleTable16to24,
+#endif
+ rfbTranslateWithSingleTable16to32 },
+#ifdef ALLOW24BPP
+ { rfbTranslateWithSingleTable24to8,
+ rfbTranslateWithSingleTable24to16,
+ rfbTranslateWithSingleTable24to24,
+ rfbTranslateWithSingleTable24to32 },
+#endif
+ { rfbTranslateWithSingleTable32to8,
+ rfbTranslateWithSingleTable32to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithSingleTable32to24,
+#endif
+ rfbTranslateWithSingleTable32to32 }
+};
+
+rfbTranslateFnType rfbTranslateWithRGBTablesFns[COUNT_OFFSETS][COUNT_OFFSETS] = {
+ { rfbTranslateWithRGBTables8to8,
+ rfbTranslateWithRGBTables8to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithRGBTables8to24,
+#endif
+ rfbTranslateWithRGBTables8to32 },
+ { rfbTranslateWithRGBTables16to8,
+ rfbTranslateWithRGBTables16to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithRGBTables16to24,
+#endif
+ rfbTranslateWithRGBTables16to32 },
+#ifdef ALLOW24BPP
+ { rfbTranslateWithRGBTables24to8,
+ rfbTranslateWithRGBTables24to16,
+ rfbTranslateWithRGBTables24to24,
+ rfbTranslateWithRGBTables24to32 },
+#endif
+ { rfbTranslateWithRGBTables32to8,
+ rfbTranslateWithRGBTables32to16,
+#ifdef ALLOW24BPP
+ rfbTranslateWithRGBTables32to24,
+#endif
+ rfbTranslateWithRGBTables32to32 }
+};
+
+
+
+/*
+ * rfbTranslateNone is used when no translation is required.
+ */
+
+void
+rfbTranslateNone(char *table, rfbPixelFormat *in, rfbPixelFormat *out,
+ char *iptr, char *optr, int bytesBetweenInputLines,
+ int width, int height)
+{
+ int bytesPerOutputLine = width * (out->bitsPerPixel / 8);
+
+ while (height > 0) {
+ memcpy(optr, iptr, bytesPerOutputLine);
+ iptr += bytesBetweenInputLines;
+ optr += bytesPerOutputLine;
+ height--;
+ }
+}
+
+
+/*
+ * rfbSetTranslateFunction sets the translation function.
+ */
+
+Bool
+rfbSetTranslateFunction(cl)
+ rfbClientPtr cl;
+{
+ rfbLog("Pixel format for client %s:\n",cl->host);
+ PrintPixelFormat(&cl->format);
+
+ /*
+ * Check that bits per pixel values are valid
+ */
+
+ if ((cl->screen->rfbServerFormat.bitsPerPixel != 8) &&
+ (cl->screen->rfbServerFormat.bitsPerPixel != 16) &&
+#ifdef ALLOW24BPP
+ (cl->screen->rfbServerFormat.bitsPerPixel != 24) &&
+#endif
+ (cl->screen->rfbServerFormat.bitsPerPixel != 32))
+ {
+ rfbLog("%s: server bits per pixel not 8, 16 or 32 (is %d)\n",
+ "rfbSetTranslateFunction",
+ cl->screen->rfbServerFormat.bitsPerPixel);
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+
+ if ((cl->format.bitsPerPixel != 8) &&
+ (cl->format.bitsPerPixel != 16) &&
+#ifdef ALLOW24BPP
+ (cl->format.bitsPerPixel != 24) &&
+#endif
+ (cl->format.bitsPerPixel != 32))
+ {
+ rfbLog("%s: client bits per pixel not 8, 16 or 32\n",
+ "rfbSetTranslateFunction");
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+
+ if (!cl->format.trueColour && (cl->format.bitsPerPixel != 8)) {
+ rfbLog("rfbSetTranslateFunction: client has colour map "
+ "but %d-bit - can only cope with 8-bit colour maps\n",
+ cl->format.bitsPerPixel);
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+
+ /*
+ * bpp is valid, now work out how to translate
+ */
+
+ if (!cl->format.trueColour) {
+ /*
+ * truecolour -> colour map
+ *
+ * Set client's colour map to BGR233, then effectively it's
+ * truecolour as well
+ */
+
+ if (!rfbSetClientColourMapBGR233(cl))
+ return FALSE;
+
+ cl->format = BGR233Format;
+ }
+
+ /* truecolour -> truecolour */
+
+ if (PF_EQ(cl->format,cl->screen->rfbServerFormat)) {
+
+ /* client & server the same */
+
+ rfbLog("no translation needed\n");
+ cl->translateFn = rfbTranslateNone;
+ return TRUE;
+ }
+
+ if ((cl->screen->rfbServerFormat.bitsPerPixel < 16) ||
+ ((!cl->screen->rfbServerFormat.trueColour || !rfbEconomicTranslate) &&
+ (cl->screen->rfbServerFormat.bitsPerPixel == 16))) {
+
+ /* we can use a single lookup table for <= 16 bpp */
+
+ cl->translateFn = rfbTranslateWithSingleTableFns
+ [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)]
+ [BPP2OFFSET(cl->format.bitsPerPixel)];
+
+ if(cl->screen->rfbServerFormat.trueColour)
+ (*rfbInitTrueColourSingleTableFns
+ [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat), &cl->format);
+ else
+ (*rfbInitColourMapSingleTableFns
+ [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat), &cl->format,&cl->screen->colourMap);
+
+ } else {
+
+ /* otherwise we use three separate tables for red, green and blue */
+
+ cl->translateFn = rfbTranslateWithRGBTablesFns
+ [BPP2OFFSET(cl->screen->rfbServerFormat.bitsPerPixel)]
+ [BPP2OFFSET(cl->format.bitsPerPixel)];
+
+ (*rfbInitTrueColourRGBTablesFns
+ [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable,
+ &(cl->screen->rfbServerFormat), &cl->format);
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ * rfbSetClientColourMapBGR233 sets the client's colour map so that it's
+ * just like an 8-bit BGR233 true colour client.
+ */
+
+static Bool
+rfbSetClientColourMapBGR233(cl)
+ rfbClientPtr cl;
+{
+ char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2];
+ rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf;
+ CARD16 *rgb = (CARD16 *)(&buf[sz_rfbSetColourMapEntriesMsg]);
+ int i, len;
+ int r, g, b;
+
+ if (cl->format.bitsPerPixel != 8 ) {
+ rfbLog("%s: client not 8 bits per pixel\n",
+ "rfbSetClientColourMapBGR233");
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+
+ scme->type = rfbSetColourMapEntries;
+
+ scme->firstColour = Swap16IfLE(0);
+ scme->nColours = Swap16IfLE(256);
+
+ len = sz_rfbSetColourMapEntriesMsg;
+
+ i = 0;
+
+ for (b = 0; b < 4; b++) {
+ for (g = 0; g < 8; g++) {
+ for (r = 0; r < 8; r++) {
+ rgb[i++] = Swap16IfLE(r * 65535 / 7);
+ rgb[i++] = Swap16IfLE(g * 65535 / 7);
+ rgb[i++] = Swap16IfLE(b * 65535 / 3);
+ }
+ }
+ }
+
+ len += 256 * 3 * 2;
+
+ if (WriteExact(cl, buf, len) < 0) {
+ rfbLogPerror("rfbSetClientColourMapBGR233: write");
+ rfbCloseClient(cl);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* this function is not called very often, so it needn't be
+ efficient. */
+
+/*
+ * rfbSetClientColourMap is called to set the client's colour map. If the
+ * client is a true colour client, we simply update our own translation table
+ * and mark the whole screen as having been modified.
+ */
+
+Bool
+rfbSetClientColourMap(cl, firstColour, nColours)
+ rfbClientPtr cl;
+ int firstColour;
+ int nColours;
+{
+ if (cl->screen->rfbServerFormat.trueColour || !cl->readyForSetColourMapEntries) {
+ return TRUE;
+ }
+
+ if (nColours == 0) {
+ nColours = cl->screen->colourMap.count;
+ }
+
+ if (cl->format.trueColour) {
+ (*rfbInitColourMapSingleTableFns
+ [BPP2OFFSET(cl->format.bitsPerPixel)]) (&cl->translateLookupTable,
+ &cl->screen->rfbServerFormat, &cl->format,&cl->screen->colourMap);
+
+ sraRgnDestroy(cl->modifiedRegion);
+ cl->modifiedRegion =
+ sraRgnCreateRect(0,0,cl->screen->width,cl->screen->height);
+
+ return TRUE;
+ }
+
+ return rfbSendSetColourMapEntries(cl, firstColour, nColours);
+}
+
+
+/*
+ * rfbSetClientColourMaps sets the colour map for each RFB client.
+ */
+
+void
+rfbSetClientColourMaps(rfbScreen, firstColour, nColours)
+ rfbScreenInfoPtr rfbScreen;
+ int firstColour;
+ int nColours;
+{
+ rfbClientIteratorPtr i;
+ rfbClientPtr cl;
+
+ i = rfbGetClientIterator(rfbScreen);
+ while((cl = rfbClientIteratorNext(i)))
+ rfbSetClientColourMap(cl, firstColour, nColours);
+ rfbReleaseClientIterator(i);
+}
+
+static void
+PrintPixelFormat(pf)
+ rfbPixelFormat *pf;
+{
+ if (pf->bitsPerPixel == 1) {
+ rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n",
+ (pf->bigEndian ? "most" : "least"));
+ } else {
+ rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth,
+ ((pf->bitsPerPixel == 8) ? ""
+ : (pf->bigEndian ? ", big endian" : ", little endian")));
+ if (pf->trueColour) {
+ rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n",
+ pf->redMax, pf->greenMax, pf->blueMax,
+ pf->redShift, pf->greenShift, pf->blueShift);
+ } else {
+ rfbLog(" uses a colour map (not true colour).\n");
+ }
+ }
+}
diff --git a/krfb/libvncserver/vncauth.c b/krfb/libvncserver/vncauth.c
new file mode 100644
index 00000000..ef27e356
--- /dev/null
+++ b/krfb/libvncserver/vncauth.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * vncauth.c - Functions for VNC password management and authentication.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#ifdef WIN32
+#define srandom srand
+#define random rand
+#else
+#include <sys/time.h>
+#endif
+#include "rfb.h"
+#include "d3des.h"
+
+
+/*
+ * We use a fixed key to store passwords, since we assume that our local
+ * file system is secure but nonetheless don't want to store passwords
+ * as plaintext.
+ */
+
+unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7};
+
+
+/*
+ * Encrypt a password and store it in a file. Returns 0 if successful,
+ * 1 if the file could not be written.
+ */
+
+int
+vncEncryptAndStorePasswd(char *passwd, char *fname)
+{
+ FILE *fp;
+ unsigned int i;
+ unsigned char encryptedPasswd[8];
+
+ if ((fp = fopen(fname,"w")) == NULL) return 1;
+
+ /* windows security sux */
+#ifndef WIN32
+ chmod(fname, S_IRUSR|S_IWUSR);
+#endif
+
+ /* pad password with nulls */
+
+ for (i = 0; i < 8; i++) {
+ if (i < strlen(passwd)) {
+ encryptedPasswd[i] = passwd[i];
+ } else {
+ encryptedPasswd[i] = 0;
+ }
+ }
+
+ /* Do encryption in-place - this way we overwrite our copy of the plaintext
+ password */
+
+ deskey(fixedkey, EN0);
+ des(encryptedPasswd, encryptedPasswd);
+
+ for (i = 0; i < 8; i++) {
+ putc(encryptedPasswd[i], fp);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+
+/*
+ * Decrypt a password from a file. Returns a pointer to a newly allocated
+ * string containing the password or a null pointer if the password could
+ * not be retrieved for some reason.
+ */
+
+char *
+vncDecryptPasswdFromFile(char *fname)
+{
+ FILE *fp;
+ int i, ch;
+ unsigned char *passwd = (unsigned char *)malloc(9);
+
+ if ((fp = fopen(fname,"r")) == NULL) return NULL;
+
+ for (i = 0; i < 8; i++) {
+ ch = getc(fp);
+ if (ch == EOF) {
+ fclose(fp);
+ return NULL;
+ }
+ passwd[i] = ch;
+ }
+
+ fclose(fp);
+
+ deskey(fixedkey, DE1);
+ des(passwd, passwd);
+
+ passwd[8] = 0;
+
+ return (char *)passwd;
+}
+
+
+/*
+ * Generate CHALLENGESIZE random bytes for use in challenge-response
+ * authentication.
+ */
+
+void
+vncRandomBytes(unsigned char *bytes)
+{
+ int i;
+ unsigned int seed = (unsigned int) time(0);
+
+ srandom(seed);
+ for (i = 0; i < CHALLENGESIZE; i++) {
+ bytes[i] = (unsigned char)(random() & 255);
+ }
+}
+
+
+/*
+ * Encrypt CHALLENGESIZE bytes in memory using a password.
+ */
+
+void
+vncEncryptBytes(unsigned char *bytes, char *passwd)
+{
+ unsigned char key[8];
+ unsigned int i;
+
+ /* key is simply password padded with nulls */
+
+ for (i = 0; i < 8; i++) {
+ if (i < strlen(passwd)) {
+ key[i] = passwd[i];
+ } else {
+ key[i] = 0;
+ }
+ }
+
+ deskey(key, EN0);
+
+ for (i = 0; i < CHALLENGESIZE; i += 8) {
+ des(bytes+i, bytes+i);
+ }
+}
diff --git a/krfb/libvncserver/vncev.c b/krfb/libvncserver/vncev.c
new file mode 100644
index 00000000..ba00f9c8
--- /dev/null
+++ b/krfb/libvncserver/vncev.c
@@ -0,0 +1,119 @@
+/* This program is a simple server to show events coming from the client */
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include "rfb.h"
+#include "default8x16.h"
+
+char f[640*480];
+char* keys[0x400];
+
+int hex2number(unsigned char c)
+{
+ if(c>'f') return(-1);
+ else if(c>'F')
+ return(10+c-'a');
+ else if(c>'9')
+ return(10+c-'A');
+ else
+ return(c-'0');
+}
+
+void read_keys()
+{
+ int i,j,k;
+ char buffer[1024];
+ FILE* keysyms=fopen("keysym.h","r");
+
+ memset(keys,0,0x400*sizeof(char*));
+
+ if(!keysyms)
+ return;
+
+ while(!feof(keysyms)) {
+ fgets(buffer,1024,keysyms);
+ if(!strncmp(buffer,"#define XK_",strlen("#define XK_"))) {
+ for(i=strlen("#define XK_");buffer[i] && buffer[i]!=' '
+ && buffer[i]!='\t';i++);
+ if(buffer[i]==0) /* don't support wrapped lines */
+ continue;
+ buffer[i]=0;
+ for(i++;buffer[i] && buffer[i]!='0';i++);
+ if(buffer[i]==0 || buffer[i+1]!='x') continue;
+ for(j=0,i+=2;(k=hex2number(buffer[i]))>=0;i++)
+ j=j*16+k;
+ if(keys[j&0x3ff]) {
+ char* x=malloc(1+strlen(keys[j&0x3ff])+1+strlen(buffer+strlen("#define ")));
+ strcpy(x,keys[j&0x3ff]);
+ strcat(x,",");
+ strcat(x,buffer+strlen("#define "));
+ free(keys[j&0x3ff]);
+ keys[j&0x3ff]=x;
+ } else
+ keys[j&0x3ff] = strdup(buffer+strlen("#define "));
+ }
+
+ }
+ fclose(keysyms);
+}
+
+int lineHeight=16,lineY=480-16;
+void output(rfbScreenInfoPtr s,char* line)
+{
+ rfbDoCopyRect(s,0,0,640,480-lineHeight,0,-lineHeight);
+ rfbDrawString(s,&default8x16Font,10,lineY,line,0x01);
+ fprintf(stderr,"%s\n",line);
+}
+
+void dokey(Bool down,KeySym k,rfbClientPtr cl)
+{
+ char buffer[1024];
+
+ sprintf(buffer,"%s: %s (0x%x)",
+ down?"down":"up",keys[k&0x3ff]?keys[k&0x3ff]:"",k);
+ output(cl->screen,buffer);
+}
+
+void doptr(int buttonMask,int x,int y,rfbClientPtr cl)
+{
+ char buffer[1024];
+ if(buttonMask) {
+ sprintf(buffer,"Ptr: mouse button mask 0x%x at %d,%d",buttonMask,x,y);
+ output(cl->screen,buffer);
+ }
+
+}
+
+void newclient(rfbClientPtr cl)
+{
+ char buffer[1024];
+ struct sockaddr_in addr;
+ int len=sizeof(addr),ip;
+
+ getpeername(cl->sock,&addr,&len);
+ ip=ntohl(addr.sin_addr.s_addr);
+ sprintf(buffer,"Client connected from ip %d.%d.%d.%d",
+ (ip>>24)&0xff,(ip>>16)&0xff,(ip>>8)&0xff,ip&0xff);
+ output(cl->screen,buffer);
+}
+
+int main(int argc,char** argv)
+{
+ rfbScreenInfoPtr s=rfbGetScreen(&argc,argv,640,480,8,1,1);
+ s->colourMap.is16=FALSE;
+ s->colourMap.count=2;
+ s->colourMap.data.bytes="\xd0\xd0\xd0\x30\x01\xe0";
+ s->rfbServerFormat.trueColour=FALSE;
+ s->frameBuffer=f;
+ s->kbdAddEvent=dokey;
+ s->ptrAddEvent=doptr;
+ s->newClientHook=newclient;
+
+ memset(f,0,640*480);
+ read_keys();
+ rfbInitServer(s);
+
+ while(1) {
+ rfbProcessEvents(s,999999);
+ }
+}
diff --git a/krfb/libvncserver/x11vnc.c b/krfb/libvncserver/x11vnc.c
new file mode 100644
index 00000000..4e298f4a
--- /dev/null
+++ b/krfb/libvncserver/x11vnc.c
@@ -0,0 +1,578 @@
+/* This file (x11vnc.c) is part of LibVNCServer.
+ It is a small clone of x0rfbserver by HexoNet, demonstrating the
+ capabilities of LibVNCServer.
+*/
+
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/extensions/XTest.h>
+#ifndef NO_SHM
+#include <X11/extensions/XShm.h>
+#include <sys/shm.h>
+#endif
+#define KEYSYM_H
+#undef Bool
+#define KeySym RFBKeySym
+#include "rfb.h"
+
+Display *dpy = 0;
+int window;
+int c=0,blockLength = 32;
+int tileX=0,tileY=0,tileWidth=32,tileHeight=32*2,dontTile=TRUE;
+Bool gotInput = FALSE;
+Bool viewOnly = FALSE;
+Bool sharedMode = FALSE;
+
+Bool disconnectAfterFirstClient = TRUE;
+
+/* keyboard handling */
+#define KBDDEBUG
+
+char modifiers[0x100];
+KeyCode keycodes[0x100],leftShiftCode,rightShiftCode,altGrCode;
+
+void init_keycodes()
+{
+ KeySym key,*keymap;
+ int i,j,minkey,maxkey,syms_per_keycode;
+
+ memset(modifiers,-1,sizeof(modifiers));
+
+ XDisplayKeycodes(dpy,&minkey,&maxkey);
+ keymap=XGetKeyboardMapping(dpy,minkey,(maxkey - minkey + 1),&syms_per_keycode);
+
+#ifdef KBDDEBUG
+ fprintf(stderr,"minkey=%d, maxkey=%d, syms_per_keycode=%d\n",
+ minkey,maxkey,syms_per_keycode);
+#endif
+ for (i = minkey; i <= maxkey; i++)
+ for(j=0;j<syms_per_keycode;j++) {
+ key=keymap[(i-minkey)*syms_per_keycode+j];
+#ifdef KBDDEBUG
+ fprintf(stderr,"keymap(i=0x%x,j=%d)==0x%lx\n",i,j,key);
+#endif
+ if(key>=' ' && key<0x100 && i==XKeysymToKeycode(dpy,key)) {
+ keycodes[key]=i;
+ modifiers[key]=j;
+#ifdef KBDDEBUG
+ fprintf(stderr,"key 0x%lx (%c): keycode=0x%x, modifier=%d\n",
+ key,(char)key,i,j);
+#endif
+ }
+ }
+
+ leftShiftCode=XKeysymToKeycode(dpy,XK_Shift_L);
+ rightShiftCode=XKeysymToKeycode(dpy,XK_Shift_R);
+ altGrCode=XKeysymToKeycode(dpy,XK_Mode_switch);
+
+#ifdef KBDDEBUG
+ fprintf(stderr,"leftShift=0x%x, rightShift=0x%x, altGr=0x%x\n",
+ leftShiftCode,rightShiftCode,altGrCode);
+#endif
+
+ XFree ((char *) keymap);
+}
+
+static Bool shutDownServer=0;
+
+/* the hooks */
+
+void clientGone(rfbClientPtr cl)
+{
+ shutDownServer=-1;
+}
+
+enum rfbNewClientAction newClient(rfbClientPtr cl)
+{
+ if(disconnectAfterFirstClient)
+ cl->clientGoneHook = clientGone;
+ if(viewOnly)
+ cl->clientData = (void*)-1;
+ else
+ cl->clientData = (void*)0;
+ return(RFB_CLIENT_ACCEPT);
+}
+
+#define LEFTSHIFT 1
+#define RIGHTSHIFT 2
+#define ALTGR 4
+char ModifierState = 0;
+
+/* this function adjusts the modifiers according to mod (as from modifiers) and ModifierState */
+
+void tweakModifiers(char mod,Bool down)
+{
+ Bool isShift=ModifierState&(LEFTSHIFT|RIGHTSHIFT);
+#ifdef KBDDEBUG
+ fprintf(stderr,"tweakModifiers: 0x%x %s\n",
+ mod,down?"down":"up");
+#endif
+ if(mod<0) return;
+ if(isShift && mod!=1) {
+ if(ModifierState&LEFTSHIFT)
+ XTestFakeKeyEvent(dpy,leftShiftCode,!down,CurrentTime);
+ if(ModifierState&RIGHTSHIFT)
+ XTestFakeKeyEvent(dpy,rightShiftCode,!down,CurrentTime);
+ }
+ if(!isShift && mod==1)
+ XTestFakeKeyEvent(dpy,leftShiftCode,down,CurrentTime);
+
+ if(ModifierState&ALTGR && mod!=2)
+ XTestFakeKeyEvent(dpy,altGrCode,!down,CurrentTime);
+ if(!(ModifierState&ALTGR) && mod==2)
+ XTestFakeKeyEvent(dpy,altGrCode,down,CurrentTime);
+}
+
+void keyboard(Bool down,KeySym keySym,rfbClientPtr cl)
+{
+ if(((int)cl->clientData)==-1) return; /* viewOnly */
+
+#define ADJUSTMOD(sym,state) \
+ if(keySym==sym) { if(down) ModifierState|=state; else ModifierState&=~state; }
+
+ ADJUSTMOD(XK_Shift_L,LEFTSHIFT)
+ ADJUSTMOD(XK_Shift_R,RIGHTSHIFT)
+ ADJUSTMOD(XK_Mode_switch,ALTGR)
+
+#ifdef KBDDEBUG
+ fprintf(stderr,"keyboard: down=%s, keySym=0x%lx (%s), ModState=0x%x\n",
+ down?"down":"up",keySym,XKeysymToString(keySym),ModifierState);
+#endif
+
+ if(keySym>=' ' && keySym<0x100) {
+ KeyCode k;
+ if(down)
+ tweakModifiers(modifiers[keySym],True);
+ //tweakModifiers(modifiers[keySym],down);
+ //k = XKeysymToKeycode( dpy,keySym );
+ k = keycodes[keySym];
+ if(k!=NoSymbol) {
+ XTestFakeKeyEvent(dpy,k,down,CurrentTime);
+ gotInput = TRUE;
+ }
+ if(down)
+ tweakModifiers(modifiers[keySym],False);
+ gotInput = TRUE;
+ } else {
+ KeyCode k = XKeysymToKeycode( dpy,keySym );
+ if(k!=NoSymbol) {
+ XTestFakeKeyEvent(dpy,k,down,CurrentTime);
+ gotInput = TRUE;
+ }
+ }
+}
+
+int oldButtonMask = 0;
+
+void mouse(int buttonMask,int x,int y,rfbClientPtr cl)
+{
+ int i=0;
+
+ if(((int)cl->clientData)==-1) return; /* viewOnly */
+
+ XTestFakeMotionEvent(dpy,0,x,y,CurrentTime );
+ while(i<5) {
+ if ((oldButtonMask&(1<<i))!=(buttonMask&(1<<i)))
+ XTestFakeButtonEvent(dpy,i+1,(buttonMask&(1<<i))?True:False,CurrentTime);
+ i++;
+ }
+ oldButtonMask = buttonMask;
+ //fprintf(stderr,"-");
+ gotInput = TRUE;
+}
+
+/* the X11 interaction */
+
+#ifndef NO_SHM
+Bool useSHM = TRUE;
+XShmSegmentInfo shminfo;
+#else
+Bool useSHM = FALSE;
+#endif
+
+void getImage(int bpp,Display *dpy,int xscreen,XImage **i,int x,int y,int width,int height)
+{
+ if(width<=0) width=DisplayWidth(dpy,xscreen);
+ if(height<=0) height=DisplayHeight(dpy,xscreen);
+ if(useSHM && bpp>0) {
+ static Bool firstTime = TRUE;
+ if(firstTime) {
+ firstTime = FALSE;
+ *i = XShmCreateImage(dpy,
+ DefaultVisual( dpy, xscreen ),
+ bpp,
+ ZPixmap,
+ NULL,
+ &shminfo,
+ width,height);
+
+ if(*i == 0) {
+ useSHM = FALSE;
+ getImage(bpp,dpy,xscreen,i,x,y,width,height);
+ return;
+ }
+
+ shminfo.shmid = shmget( IPC_PRIVATE,
+ (*i)->bytes_per_line * (*i)->height,
+ IPC_CREAT | 0777 );
+ shminfo.shmaddr = (*i)->data = (char *) shmat( shminfo.shmid, 0, 0 );
+ shminfo.readOnly = False;
+ XShmAttach( dpy, &shminfo );
+ }
+
+ if(x==0 && y==0 && width==DisplayWidth(dpy,xscreen) && height==DisplayHeight(dpy,xscreen))
+ XShmGetImage(dpy,window,*i,0,0,AllPlanes);
+ else
+ XGetSubImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap,*i,0,0);
+ } else {
+ *i = XGetImage(dpy,window,x,y,width,height,AllPlanes,ZPixmap );
+ }
+}
+
+void checkForImageUpdates(rfbScreenInfoPtr s,char *b,int rowstride,int x,int y,int width,int height)
+{
+ Bool changed;
+ int i,j,k,l1,l2,x1,y1;
+ int bpp=s->bitsPerPixel/8;
+
+ for(j=0;j<height;j+=blockLength)
+ for(i=0;i<width;i+=blockLength) {
+ y1=j+blockLength; if(y1>height) y1=height;
+ x1=i+blockLength; if(x1>width) x1=width;
+ y1*=rowstride;
+ x1*=bpp;
+ changed=FALSE;
+ for(l1=j*rowstride,l2=(j+y)*s->paddedWidthInBytes+x*bpp;l1<y1;l1+=rowstride,l2+=s->paddedWidthInBytes)
+ for(k=i*bpp;k<x1;k++)
+ if(s->frameBuffer[l2+k]!=b[l1+k]) {
+ // fprintf(stderr,"changed: %d, %d\n",k,l);
+ changed=TRUE;
+ goto changed_p;
+ }
+ if(changed) {
+ changed_p:
+ for(l1+=i*bpp,l2+=i*bpp;l1<y1;l1+=rowstride,l2+=s->paddedWidthInBytes)
+ memcpy(/*b+l,*/s->frameBuffer+l2,b+l1,x1-i*bpp);
+ rfbMarkRectAsModified(s,x+i,y+j,x+i+blockLength,y+j+blockLength);
+ }
+ }
+}
+
+int probeX=0,probeY=0;
+
+void probeScreen(rfbScreenInfoPtr s,int xscreen)
+{
+ int i,j,/*pixel,i1,*/j1,
+ bpp=s->rfbServerFormat.bitsPerPixel/8,/*mask=(1<<bpp)-1,*/
+ rstride=s->paddedWidthInBytes;
+ XImage* im;
+ //fprintf(stderr,"/%d,%d",probeX,probeY);
+#if 0
+ probeX++;
+ if(probeX>=tileWidth) {
+ probeX=0;
+ probeY++;
+ if(probeY>=tileHeight)
+ probeY=0;
+ }
+#else
+ probeX=(rand()%tileWidth);
+ probeY=(rand()%tileHeight);
+#endif
+
+ for(j=probeY;j<s->height;j+=tileHeight)
+ for(i=0/*probeX*/;i<s->width;i+=tileWidth) {
+ im=XGetImage(dpy,window,i,j,tileWidth/*1*/,1,AllPlanes,ZPixmap);
+ /* for(i1=0;i1<bpp && im->data[i1]==(s->frameBuffer+i*bpp+j*rstride)[i1];i1++);
+ if(i1<bpp) { */
+ if(memcmp(im->data,s->frameBuffer+i*bpp+j*rstride,tileWidth*bpp)) {
+ /* do update */
+ int x=i/*-probeX*/,w=(x+tileWidth>s->width)?s->width-x:tileWidth,
+ y=j-probeY,h=(y+tileHeight>s->height)?s->height-y:tileHeight;
+
+ XDestroyImage(im);
+ //getImage(bpp,dpy,xscreen,&im,x,y,w,h);
+ //fprintf(stderr,"GetImage(%d,%d,%d,%d)",x,y,w,h);
+ im = XGetImage(dpy,window,x,y,w,h,AllPlanes,ZPixmap );
+ for(j1=0;j1<h;j1++)
+ memcpy(s->frameBuffer+x*bpp+(y+j1)*rstride,
+ im->data+j1*im->bytes_per_line,bpp*w);
+ //checkForImageUpdates(s,im->data,rstride,x,y,w,h);
+ //if(0 && !useSHM)
+ XDestroyImage(im);
+ //memcpy(s->frameBuffer+i*bpp+j*rstride,&pixel,bpp);
+ rfbMarkRectAsModified(s,x,y,x+w,y+h);
+ //fprintf(stderr,"%d:%d:%x\n",i,j,pixel);
+ //fprintf(stderr,"*");
+ } else
+ XDestroyImage(im);
+ }
+}
+
+#define LOCAL_CONTROL
+
+#ifdef LOCAL_CONTROL
+#include "1instance.c"
+#endif
+
+/* the main program */
+
+int main(int argc,char** argv)
+{
+ //Screen *sc;
+ //Colormap cm;
+ XImage *framebufferImage;
+ char *backupImage;
+ int xscreen,i;
+ rfbScreenInfoPtr screen;
+ int maxMsecsToConnect = 5000; /* a maximum of 5 seconds to connect */
+ int updateCounter; /* about every 50 ms a screen update should be made. */
+
+#ifdef LOCAL_CONTROL
+ char message[1024];
+ single_instance_struct single_instance = { "/tmp/x11vnc_control" };
+
+ open_control_file(&single_instance);
+#endif
+
+ for(i=argc-1;i>0;i--)
+#ifdef LOCAL_CONTROL
+ if(i<argc-1 && !strcmp(argv[i],"-toggleviewonly")) {
+ snprintf(message, sizeof(message), "t%s",argv[i+1]);
+ send_message(&single_instance,message);
+ exit(0);
+ } else if(!strcmp(argv[i],"-listclients")) {
+ fprintf(stderr,"list clients\n");
+ send_message(&single_instance,"l");
+ exit(0);
+ } else
+#ifdef BACKCHANNEL
+ if(i<argc-1 && !strcmp(argv[i],"-backchannel")) {
+ snprintf(message, sizeof(message), "b%s",argv[i+1]);
+ send_message(&single_instance,message);
+ exit(0);
+ } else
+#endif
+#endif
+ if(i<argc-1 && strcmp(argv[i],"-display")==0) {
+ fprintf(stderr,"Using display %s\n",argv[i+1]);
+ dpy = XOpenDisplay(argv[i+1]);
+ if(dpy==0) {
+ fprintf(stderr,"Couldn't connect to display \"%s\".\n",argv[i+1]);
+ exit(1);
+ }
+ } else if(i<argc-1 && strcmp(argv[i],"-wait4client")==0) {
+ maxMsecsToConnect = atoi(argv[i+1]);
+ } else if(i<argc-1 && strcmp(argv[i],"-update")==0) {
+ updateCounter = atoi(argv[i+1]);
+ } else if(strcmp(argv[i],"-noshm")==0) {
+ useSHM = FALSE;
+ } else if(strcmp(argv[i],"-runforever")==0) {
+ disconnectAfterFirstClient = FALSE;
+ } else if(strcmp(argv[i],"-tile")==0) {
+ dontTile=FALSE;
+ } else if(strcmp(argv[i],"-viewonly")==0) {
+ viewOnly=TRUE;
+ } else if(strcmp(argv[i],"-shared")==0) {
+ sharedMode=TRUE;
+ }
+
+ updateCounter = dontTile?20:1;
+
+ if(dpy==0)
+ dpy = XOpenDisplay("");
+ if(dpy==0) {
+ fprintf(stderr,"Couldn't open display!\n");
+ exit(2);
+ }
+
+ xscreen = DefaultScreen(dpy);
+ window = RootWindow(dpy,xscreen);
+ //XTestGrabControl(dpy,True);
+
+ init_keycodes();
+
+ getImage(0,dpy,xscreen,&framebufferImage,0,0,-1,-1);
+
+ screen = rfbGetScreen(&argc,argv,framebufferImage->width,
+ framebufferImage->height,
+ framebufferImage->bits_per_pixel,
+ 8,
+ framebufferImage->bits_per_pixel/8);
+
+ screen->paddedWidthInBytes = framebufferImage->bytes_per_line;
+
+ screen->rfbServerFormat.bitsPerPixel = framebufferImage->bits_per_pixel;
+ screen->rfbServerFormat.depth = framebufferImage->depth;
+ //rfbEndianTest = framebufferImage->bitmap_bit_order != MSBFirst;
+ screen->rfbServerFormat.trueColour = TRUE;
+
+ if ( screen->rfbServerFormat.bitsPerPixel == 8 ) {
+ if(CellsOfScreen(ScreenOfDisplay(dpy,xscreen))) {
+ XColor color[256];
+ int i;
+ screen->colourMap.count = 256;
+ screen->rfbServerFormat.trueColour = FALSE;
+ screen->colourMap.is16 = TRUE;
+ for(i=0;i<256;i++)
+ color[i].pixel=i;
+ XQueryColors(dpy,DefaultColormap(dpy,xscreen),color,256);
+ screen->colourMap.data.shorts = (short*)malloc(3*sizeof(short)*screen->colourMap.count);
+ for(i=0;i<screen->colourMap.count;i++) {
+ screen->colourMap.data.shorts[i*3+0] = color[i].red;
+ screen->colourMap.data.shorts[i*3+1] = color[i].green;
+ screen->colourMap.data.shorts[i*3+2] = color[i].blue;
+ }
+ } else {
+ screen->rfbServerFormat.redShift = 0;
+ screen->rfbServerFormat.greenShift = 2;
+ screen->rfbServerFormat.blueShift = 5;
+ screen->rfbServerFormat.redMax = 3;
+ screen->rfbServerFormat.greenMax = 7;
+ screen->rfbServerFormat.blueMax = 3;
+ }
+ } else {
+ screen->rfbServerFormat.redShift = 0;
+ if ( framebufferImage->red_mask )
+ while ( ! ( framebufferImage->red_mask & (1 << screen->rfbServerFormat.redShift) ) )
+ screen->rfbServerFormat.redShift++;
+ screen->rfbServerFormat.greenShift = 0;
+ if ( framebufferImage->green_mask )
+ while ( ! ( framebufferImage->green_mask & (1 << screen->rfbServerFormat.greenShift) ) )
+ screen->rfbServerFormat.greenShift++;
+ screen->rfbServerFormat.blueShift = 0;
+ if ( framebufferImage->blue_mask )
+ while ( ! ( framebufferImage->blue_mask & (1 << screen->rfbServerFormat.blueShift) ) )
+ screen->rfbServerFormat.blueShift++;
+ screen->rfbServerFormat.redMax = framebufferImage->red_mask >> screen->rfbServerFormat.redShift;
+ screen->rfbServerFormat.greenMax = framebufferImage->green_mask >> screen->rfbServerFormat.greenShift;
+ screen->rfbServerFormat.blueMax = framebufferImage->blue_mask >> screen->rfbServerFormat.blueShift;
+ }
+
+ backupImage = malloc(screen->height*screen->paddedWidthInBytes);
+ memcpy(backupImage,framebufferImage->data,screen->height*screen->paddedWidthInBytes);
+
+ screen->frameBuffer = backupImage;
+ screen->cursor = 0;
+ screen->newClientHook = newClient;
+
+ screen->kbdAddEvent = keyboard;
+ screen->ptrAddEvent = mouse;
+
+ if(sharedMode) {
+ screen->rfbAlwaysShared = TRUE;
+ }
+
+ screen->rfbDeferUpdateTime = 1;
+ updateCounter /= screen->rfbDeferUpdateTime;
+
+ rfbInitServer(screen);
+
+ c=0;
+ while(1) {
+ if(screen->rfbClientHead)
+ maxMsecsToConnect = 1<<16;
+ else {
+ maxMsecsToConnect -= screen->rfbDeferUpdateTime;
+ if(maxMsecsToConnect<0) {
+ fprintf(stderr,"Maximum time to connect reached. Exiting.\n");
+ XTestDiscard(dpy);
+ exit(2);
+ }
+ }
+
+#ifdef LOCAL_CONTROL
+ if(get_next_message(message,1024,&single_instance,50)) {
+ if(message[0]=='l' && message[1]==0) {
+ rfbClientPtr cl;
+ int i;
+ for(i=0,cl=screen->rfbClientHead;cl;cl=cl->next,i++)
+ fprintf(stderr,"%02d: %s\n",i,cl->host);
+ } else if(message[0]=='t') {
+ rfbClientPtr cl;
+ for(cl=screen->rfbClientHead;cl;cl=cl->next)
+ if(!strcmp(message+1,cl->host)) {
+ cl->clientData=(void*)((cl->clientData==0)?-1:0);
+ break;
+ }
+ }
+#ifdef BACKCHANNEL
+ else if(message[0]=='b')
+ rfbSendBackChannel(screen,message+1,strlen(message+1));
+#endif
+ }
+#endif
+
+ rfbProcessEvents(screen,-1);
+ if(shutDownServer) {
+ free(backupImage);
+ rfbScreenCleanup(screen);
+ XFree(dpy);
+#ifndef NO_SHM
+ XShmDetach(dpy,framebufferImage);
+#endif
+ exit(0);
+ }
+
+ if(dontTile) {
+ if(gotInput) {
+ gotInput = FALSE;
+ c=updateCounter;
+ } else if(screen->rfbClientHead && c++>updateCounter) {
+ c=0;
+ //fprintf(stderr,"*");
+ if(!useSHM)
+ framebufferImage->f.destroy_image(framebufferImage);
+ if(dontTile) {
+ getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,0,0,screen->width,screen->height);
+ checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line,
+ 0,0,screen->width,screen->height);
+ } else {
+ /* old tile code. Eventually to be removed (TODO) */
+ char isRightEdge = tileX+tileWidth>=screen->width;
+ char isLowerEdge = tileY+tileHeight>=screen->height;
+ getImage(screen->rfbServerFormat.bitsPerPixel,dpy,xscreen,&framebufferImage,tileX,tileY,
+ isRightEdge?screen->width-tileX:tileWidth,
+ isLowerEdge?screen->height-tileY:tileHeight);
+ checkForImageUpdates(screen,framebufferImage->data,framebufferImage->bytes_per_line,
+ tileX,tileY,
+ isRightEdge?screen->width-tileX:tileWidth,
+ isLowerEdge?screen->height-tileY:tileHeight);
+ if(isRightEdge) {
+ tileX=0;
+ if(isLowerEdge)
+ tileY=0;
+ else
+ tileY+=tileHeight;
+ } else
+ tileX+=tileWidth;
+ }
+ }
+ } else if(c++>updateCounter) {
+ c=0;
+ probeScreen(screen,xscreen);
+ }
+
+#ifdef WRITE_SNAPS
+ {
+ int i,j,r,g,b;
+ FILE* f=fopen("test.pnm","wb");
+ fprintf(f,"P6\n%d %d\n255\n",screen->width,screen->height);
+ for(j=0;j<screen->height;j++)
+ for(i=0;i<screen->width;i++) {
+ //r=screen->frameBuffer[j*screen->paddedWidthInBytes+i*2];
+ r=framebufferImage->data[j*screen->paddedWidthInBytes+i*2];
+ fputc(((r>>screen->rfbServerFormat.redShift)&screen->rfbServerFormat.redMax)*255/screen->rfbServerFormat.redMax,f);
+ fputc(((r>>screen->rfbServerFormat.greenShift)&screen->rfbServerFormat.greenMax)*255/screen->rfbServerFormat.greenMax,f);
+ fputc(((r>>screen->rfbServerFormat.blueShift)&screen->rfbServerFormat.blueMax)*255/screen->rfbServerFormat.blueMax,f);
+ }
+ fclose(f);
+ }
+#endif
+ }
+#ifndef NO_SHM
+ //XShmDetach(dpy,framebufferImage);
+#endif
+
+ return(0);
+}
diff --git a/krfb/libvncserver/zippy.c b/krfb/libvncserver/zippy.c
new file mode 100644
index 00000000..2f35059d
--- /dev/null
+++ b/krfb/libvncserver/zippy.c
@@ -0,0 +1,182 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <rfb.h>
+#include <keysym.h>
+#include "radon.h"
+
+int maxx=400, maxy=400, bpp=4;
+/* odd maxx doesn't work (vncviewer bug) */
+
+/* Here we create a structure so that every client has it's own pointer */
+
+/* turns the framebuffer black */
+void blank_framebuffer(char* frame_buffer, int x1, int y1, int x2, int y2);
+/* displays a red bar, a green bar, and a blue bar */
+void draw_primary_colors (char* frame_buffer, int x1, int y1, int x2, int y2);
+void linecount (char* frame_buffer);
+/* handles mouse events */
+void on_mouse_event (int buttonMask,int x,int y,rfbClientPtr cl);
+/* handles keyboard events */
+void on_key_press (Bool down,KeySym key,rfbClientPtr cl);
+
+int main (int argc, char **argv)
+{
+ int i;
+ rfbScreenInfoPtr server;
+
+ rfbProcessSizeArguments(&maxx,&maxy,&bpp,&argc,argv);
+
+ server = rfbGetScreen (&argc, argv, maxx, maxy, 8, 3, bpp);
+ server->desktopName = "Zippy das wundersquirrel\'s VNC server";
+ server->frameBuffer = (char*)malloc(maxx*maxy*bpp);
+ server->rfbAlwaysShared = TRUE;
+ server->kbdAddEvent = on_key_press;
+ server->ptrAddEvent = on_mouse_event;
+
+ rfbInitServer (server);
+
+ blank_framebuffer(server->frameBuffer, 0, 0, maxx, maxy);
+ rfbRunEventLoop (server, -1, FALSE);
+ free(server->frameBuffer);
+ rfbScreenCleanup (server);
+ return 0;
+}
+
+void blank_framebuffer(char* frame_buffer, int x1, int y1, int x2, int y2)
+{
+ int i;
+ for (i=0; i < maxx * maxy * bpp; i++) frame_buffer[i]=(char) 0;
+}
+
+void draw_primary_colors (char* frame_buffer, int x1, int y1, int x2, int y2)
+{
+ int i, j, current_pixel;
+ for (i=y1; i < y2; i++){
+ for (j=x1; j < x2; j++) {
+ current_pixel = (i*x2 + j) * bpp;
+ if (i < y2 ) {
+ frame_buffer[current_pixel+0] = (char) 128;
+ frame_buffer[current_pixel+1] = (char) 0;
+ frame_buffer[current_pixel+2] = (char) 0;
+ }
+ if (i < y2/3*2) {
+ frame_buffer[current_pixel+0] = (char) 0;
+ frame_buffer[current_pixel+1] = (char) 128;
+ frame_buffer[current_pixel+2] = (char) 0;
+ }
+ if (i < y2/3) {
+ frame_buffer[current_pixel+0] = (char) 0;
+ frame_buffer[current_pixel+1] = (char) 0;
+ frame_buffer[current_pixel+2] = (char) 128;
+ }
+ }
+ }
+ }
+
+/* Dscho's versions (slower, but works for bpp != 3 or 4) */
+void draw_primary_colours_generic(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2)
+{
+ rfbPixelFormat f=s->rfbServerFormat;
+ int i,j;
+ for(j=y1;j<y2;j++)
+ for(i=x1;i<x2;i++)
+ if(j<y1*2/3+y2/3)
+ rfbDrawPixel(s,i,j,f.redMax<<f.redShift);
+ else if(j<y1/3+y2*2/3)
+ rfbDrawPixel(s,i,j,f.greenMax<<f.greenShift);
+ else
+ rfbDrawPixel(s,i,j,f.blueMax<<f.blueShift);
+}
+
+void draw_primary_colours_generic_fast(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2)
+{
+ rfbPixelFormat f=s->rfbServerFormat;
+ int i,j,y3=(y1*2+y2)/3,y4=(y1+y2*2)/3;
+ /* draw first pixel */
+ rfbDrawPixel(s,x1,y1,f.redMax<<f.redShift);
+ rfbDrawPixel(s,x1,y3,f.greenMax<<f.greenShift);
+ rfbDrawPixel(s,x1,y4,f.blueMax<<f.blueShift);
+ /* then copy stripes */
+ for(j=0;j<y2-y4;j++)
+ for(i=x1;i<x2;i++) {
+#define ADDR(x,y) s->frameBuffer+(x)*bpp+(y)*s->paddedWidthInBytes
+ memcpy(ADDR(i,j+y1),ADDR(x1,y1),bpp);
+ memcpy(ADDR(i,j+y3),ADDR(x1,y3),bpp);
+ memcpy(ADDR(i,j+y4),ADDR(x1,y4),bpp);
+ }
+}
+
+void draw_primary_colours_generic_ultrafast(rfbScreenInfoPtr s,int x1,int y1,int x2,int y2)
+{
+ rfbPixelFormat f=s->rfbServerFormat;
+ int i,j,y3=(y1*2+y2)/3,y4=(y1+y2*2)/3;
+ /* fill rectangles */
+ rfbFillRect(s,x1,y1,x2,y3,f.redMax<<f.redShift);
+ rfbFillRect(s,x1,y3,x2,y4,f.greenMax<<f.greenShift);
+ rfbFillRect(s,x1,y4,x2,y2,f.blueMax<<f.blueShift);
+}
+
+void linecount (char* frame_buffer)
+{
+ int i,j,k, current_pixel;
+ for (i=maxy-4; i>maxy-20; i-=4)
+ for (j=0; j<4; j++) for (k=0; k < maxx; k++) {
+ current_pixel = (i*j*maxx + k) * bpp;
+ if (i%2 == 0) {
+ frame_buffer[current_pixel+0] = (char) 0;
+ frame_buffer[current_pixel+1] = (char) 0;
+ frame_buffer[current_pixel+2] = (char) 128;
+ }
+
+ if (i%2 == 1) {
+ frame_buffer[current_pixel+0] = (char) 128;
+ frame_buffer[current_pixel+1] = (char) 0;
+ frame_buffer[current_pixel+2] = (char) 0;
+ }
+ }
+
+}
+
+
+void on_key_press (Bool down,KeySym key,rfbClientPtr cl)
+{
+ if (down) //or else the action occurs on both the press and depress
+ switch (key) {
+
+ case XK_b:
+ case XK_B:
+ rfbUndrawCursor(cl->screen);
+ blank_framebuffer(cl->screen->frameBuffer, 0, 0, maxx, maxy);
+ rfbDrawString(cl->screen,&radonFont,20,maxy-20,"Hello, World!",0xffffff);
+ rfbMarkRectAsModified(cl->screen,0, 0,maxx,maxy);
+ fprintf (stderr, "Framebuffer blanked\n");
+ break;
+ case XK_p:
+ case XK_P:
+ rfbUndrawCursor(cl->screen);
+ /* draw_primary_colors (cl->screen->frameBuffer, 0, 0, maxx, maxy); */
+ draw_primary_colours_generic_ultrafast (cl->screen, 0, 0, maxx, maxy);
+ rfbMarkRectAsModified(cl->screen,0, 0,maxx,maxy);
+ fprintf (stderr, "Primary colors displayed\n");
+ break;
+ case XK_Q:
+ case XK_q:
+ fprintf (stderr, "Exiting now\n");
+ exit(0);
+ case XK_C:
+ case XK_c:
+ rfbUndrawCursor(cl->screen);
+ rfbDrawString(cl->screen,&radonFont,20,100,"Hello, World!",0xffffff);
+ rfbMarkRectAsModified(cl->screen,0, 0,maxx,maxy);
+ break;
+ default:
+ fprintf (stderr, "The %c key was pressed\n", (char) key);
+ }
+}
+
+
+void on_mouse_event (int buttonMask,int x,int y,rfbClientPtr cl)
+{
+ printf("buttonMask: %i\n"
+ "x: %i\n" "y: %i\n", buttonMask, x, y);
+}
diff --git a/krfb/libvncserver/zlib.c b/krfb/libvncserver/zlib.c
new file mode 100644
index 00000000..1eac366d
--- /dev/null
+++ b/krfb/libvncserver/zlib.c
@@ -0,0 +1,304 @@
+/*
+ * zlib.c
+ *
+ * Routines to implement zlib based encoding (deflate).
+ */
+
+/*
+ * Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
+ * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
+ *
+ * This 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.
+ *
+ * This software 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ *
+ * For the latest source code, please check:
+ *
+ * http://www.developVNC.org/
+ *
+ * or send email to feedback@developvnc.org.
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+/*
+ * zlibBeforeBuf contains pixel data in the client's format.
+ * zlibAfterBuf contains the zlib (deflated) encoding version.
+ * If the zlib compressed/encoded version is
+ * larger than the raw data or if it exceeds zlibAfterBufSize then
+ * raw encoding is used instead.
+ */
+
+static int zlibBeforeBufSize = 0;
+static char *zlibBeforeBuf = NULL;
+
+static int zlibAfterBufSize = 0;
+static char *zlibAfterBuf = NULL;
+static int zlibAfterBufLen;
+
+/*
+ * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib
+ * rectangle encoding.
+ */
+
+Bool
+rfbSendOneRectEncodingZlib(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ rfbFramebufferUpdateRectHeader rect;
+ rfbZlibHeader hdr;
+ int deflateResult;
+ int previousOut;
+ int i;
+ char *fbptr = (cl->screen->frameBuffer + (cl->screen->paddedWidthInBytes * y)
+ + (x * (cl->screen->bitsPerPixel / 8)));
+
+ int maxRawSize;
+ int maxCompSize;
+
+ maxRawSize = (cl->screen->width * cl->screen->height
+ * (cl->format.bitsPerPixel / 8));
+
+ if (zlibBeforeBufSize < maxRawSize) {
+ zlibBeforeBufSize = maxRawSize;
+ if (zlibBeforeBuf == NULL)
+ zlibBeforeBuf = (char *)malloc(zlibBeforeBufSize);
+ else
+ zlibBeforeBuf = (char *)realloc(zlibBeforeBuf, zlibBeforeBufSize);
+ }
+
+ /* zlib compression is not useful for very small data sets.
+ * So, we just send these raw without any compression.
+ */
+ if (( w * h * (cl->screen->bitsPerPixel / 8)) <
+ VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) {
+
+ int result;
+
+ /* The translation function (used also by the in raw encoding)
+ * requires 4/2/1 byte alignment in the output buffer (which is
+ * updateBuf for the raw encoding) based on the bitsPerPixel of
+ * the viewer/client. This prevents SIGBUS errors on some
+ * architectures like SPARC, PARISC...
+ */
+ if (( cl->format.bitsPerPixel > 8 ) &&
+ ( cl->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ result = rfbSendRectEncodingRaw(cl, x, y, w, h);
+
+ return result;
+
+ }
+
+ /*
+ * zlib requires output buffer to be slightly larger than the input
+ * buffer, in the worst case.
+ */
+ maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12;
+
+ if (zlibAfterBufSize < maxCompSize) {
+ zlibAfterBufSize = maxCompSize;
+ if (zlibAfterBuf == NULL)
+ zlibAfterBuf = (char *)malloc(zlibAfterBufSize);
+ else
+ zlibAfterBuf = (char *)realloc(zlibAfterBuf, zlibAfterBufSize);
+ }
+
+ /*
+ * Convert pixel data to client format.
+ */
+ (*cl->translateFn)(cl->translateLookupTable, &cl->screen->rfbServerFormat,
+ &cl->format, fbptr, zlibBeforeBuf,
+ cl->screen->paddedWidthInBytes, w, h);
+
+ cl->compStream.next_in = ( Bytef * )zlibBeforeBuf;
+ cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8);
+ cl->compStream.next_out = ( Bytef * )zlibAfterBuf;
+ cl->compStream.avail_out = maxCompSize;
+ cl->compStream.data_type = Z_BINARY;
+
+ /* Initialize the deflation state. */
+ if ( cl->compStreamInited == FALSE ) {
+
+ cl->compStream.total_in = 0;
+ cl->compStream.total_out = 0;
+ cl->compStream.zalloc = Z_NULL;
+ cl->compStream.zfree = Z_NULL;
+ cl->compStream.opaque = Z_NULL;
+
+ deflateInit2( &(cl->compStream),
+ cl->zlibCompressLevel,
+ Z_DEFLATED,
+ MAX_WBITS,
+ MAX_MEM_LEVEL,
+ Z_DEFAULT_STRATEGY );
+ /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */
+ /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */
+ cl->compStreamInited = TRUE;
+
+ }
+
+ previousOut = cl->compStream.total_out;
+
+ /* Perform the compression here. */
+ deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH );
+
+ /* Find the total size of the resulting compressed data. */
+ zlibAfterBufLen = cl->compStream.total_out - previousOut;
+
+ if ( deflateResult != Z_OK ) {
+ rfbLog("zlib deflation error: %s\n", cl->compStream.msg);
+ return FALSE;
+ }
+
+ /* Note that it is not possible to switch zlib parameters based on
+ * the results of the compression pass. The reason is
+ * that we rely on the compressor and decompressor states being
+ * in sync. Compressing and then discarding the results would
+ * cause lose of synchronization.
+ */
+
+ /* Update statics */
+ cl->rfbRectanglesSent[rfbEncodingZlib]++;
+ cl->rfbBytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader
+ + sz_rfbZlibHeader + zlibAfterBufLen);
+
+ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader
+ > UPDATE_BUF_SIZE)
+ {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+
+ rect.r.x = Swap16IfLE(x);
+ rect.r.y = Swap16IfLE(y);
+ rect.r.w = Swap16IfLE(w);
+ rect.r.h = Swap16IfLE(h);
+ rect.encoding = Swap32IfLE(rfbEncodingZlib);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
+ sz_rfbFramebufferUpdateRectHeader);
+ cl->ublen += sz_rfbFramebufferUpdateRectHeader;
+
+ hdr.nBytes = Swap32IfLE(zlibAfterBufLen);
+
+ memcpy(&cl->updateBuf[cl->ublen], (char *)&hdr, sz_rfbZlibHeader);
+ cl->ublen += sz_rfbZlibHeader;
+
+ for (i = 0; i < zlibAfterBufLen;) {
+
+ int bytesToCopy = UPDATE_BUF_SIZE - cl->ublen;
+
+ if (i + bytesToCopy > zlibAfterBufLen) {
+ bytesToCopy = zlibAfterBufLen - i;
+ }
+
+ memcpy(&cl->updateBuf[cl->ublen], &zlibAfterBuf[i], bytesToCopy);
+
+ cl->ublen += bytesToCopy;
+ i += bytesToCopy;
+
+ if (cl->ublen == UPDATE_BUF_SIZE) {
+ if (!rfbSendUpdateBuf(cl))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+
+}
+
+
+/*
+ * rfbSendRectEncodingZlib - send a given rectangle using one or more
+ * Zlib encoding rectangles.
+ */
+
+Bool
+rfbSendRectEncodingZlib(cl, x, y, w, h)
+ rfbClientPtr cl;
+ int x, y, w, h;
+{
+ int maxLines;
+ int linesRemaining;
+ rfbRectangle partialRect;
+
+ partialRect.x = x;
+ partialRect.y = y;
+ partialRect.w = w;
+ partialRect.h = h;
+
+ /* Determine maximum pixel/scan lines allowed per rectangle. */
+ maxLines = ( ZLIB_MAX_SIZE(w) / w );
+
+ /* Initialize number of scan lines left to do. */
+ linesRemaining = h;
+
+ /* Loop until all work is done. */
+ while ( linesRemaining > 0 ) {
+
+ int linesToComp;
+
+ if ( maxLines < linesRemaining )
+ linesToComp = maxLines;
+ else
+ linesToComp = linesRemaining;
+
+ partialRect.h = linesToComp;
+
+ /* Encode (compress) and send the next rectangle. */
+ if ( ! rfbSendOneRectEncodingZlib( cl,
+ partialRect.x,
+ partialRect.y,
+ partialRect.w,
+ partialRect.h )) {
+
+ return FALSE;
+ }
+
+ /* Technically, flushing the buffer here is not extrememly
+ * efficient. However, this improves the overall throughput
+ * of the system over very slow networks. By flushing
+ * the buffer with every maximum size zlib rectangle, we
+ * improve the pipelining usage of the server CPU, network,
+ * and viewer CPU components. Insuring that these components
+ * are working in parallel actually improves the performance
+ * seen by the user.
+ * Since, zlib is most useful for slow networks, this flush
+ * is appropriate for the desired behavior of the zlib encoding.
+ */
+ if (( cl->ublen > 0 ) &&
+ ( linesToComp == maxLines )) {
+ if (!rfbSendUpdateBuf(cl)) {
+
+ return FALSE;
+ }
+ }
+
+ /* Update remaining and incremental rectangle location. */
+ linesRemaining -= linesToComp;
+ partialRect.y += linesToComp;
+
+ }
+
+ return TRUE;
+
+}
+
+
diff --git a/krfb/srvloc/Makefile.am b/krfb/srvloc/Makefile.am
new file mode 100644
index 00000000..162e2062
--- /dev/null
+++ b/krfb/srvloc/Makefile.am
@@ -0,0 +1,17 @@
+METASOURCES = AUTO
+
+# Code
+noinst_LTLIBRARIES = libsrvloc.la
+
+libsrvloc_la_SOURCES = kserviceregistry.cpp uuid.cpp \
+ kinetinterface.cpp kinetinterfacewatcher.cpp \
+ getifaddrs.cpp
+
+libsrvloc_la_LIBADD = $(LIB_QT) $(LIB_KDECORE) $(LIB_SLP)
+libsrvloc_la_LDFLAGS = $(all_libraries) -no-undefined
+noinst_HEADERS = kserviceregistry.h uuid.h \
+ getifaddrs.h kinetinterface.h kinetinterfacewatcher.h
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+
diff --git a/krfb/srvloc/getifaddrs.cpp b/krfb/srvloc/getifaddrs.cpp
new file mode 100644
index 00000000..cff9e81e
--- /dev/null
+++ b/krfb/srvloc/getifaddrs.cpp
@@ -0,0 +1,261 @@
+/* getifaddrs -- get names and addresses of all network interfaces
+ Copyright (C) 1999,2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.
+
+ The GNU C 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/**
+ * 02-12-26, tim@tjansen.de: put in kde_ namespace, C++ fixes,
+ * included ifreq.h
+ * removed glibc dependencies
+ */
+
+#include "config.h"
+
+#ifndef HAVE_GETIFADDRS
+
+#include "getifaddrs.h"
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#ifndef IF_NAMESIZE
+#define IF_NAMESIZE IFNAMSIZ
+#endif
+
+#ifdef SIOCGIFCONF
+
+#define old_siocgifconf 0
+
+static inline void
+__ifreq (struct ifreq **ifreqs, int *num_ifs, int sockfd)
+{
+ int fd = sockfd;
+ struct ifconf ifc;
+ int rq_len;
+ int nifs;
+# define RQ_IFS 4
+
+ if (fd < 0)
+ fd = socket (AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ {
+ *num_ifs = 0;
+ *ifreqs = NULL;
+ return;
+ }
+
+ ifc.ifc_buf = NULL;
+
+ /* We may be able to get the needed buffer size directly, rather than
+ guessing. */
+ if (! old_siocgifconf)
+ {
+ ifc.ifc_buf = NULL;
+ ifc.ifc_len = 0;
+ if (ioctl (fd, SIOCGIFCONF, &ifc) < 0 || ifc.ifc_len == 0)
+ {
+ rq_len = RQ_IFS * sizeof (struct ifreq);
+ }
+ else
+ rq_len = ifc.ifc_len;
+ }
+ else
+ rq_len = RQ_IFS * sizeof (struct ifreq);
+
+ /* Read all the interfaces out of the kernel. */
+ while (1)
+ {
+ ifc.ifc_len = rq_len;
+ ifc.ifc_buf = (char*) realloc (ifc.ifc_buf, ifc.ifc_len);
+ if (ifc.ifc_buf == NULL || ioctl (fd, SIOCGIFCONF, &ifc) < 0)
+ {
+ if (ifc.ifc_buf)
+ free (ifc.ifc_buf);
+
+ if (fd != sockfd)
+ close (fd);
+
+ *num_ifs = 0;
+ *ifreqs = NULL;
+ return;
+ }
+
+ if (!old_siocgifconf || ifc.ifc_len < rq_len)
+ break;
+
+ rq_len *= 2;
+ }
+
+ nifs = ifc.ifc_len / sizeof (struct ifreq);
+
+ if (fd != sockfd)
+ close (fd);
+
+ *num_ifs = nifs;
+ *ifreqs = (ifreq*)realloc (ifc.ifc_buf, nifs * sizeof (struct ifreq));
+}
+
+static inline struct ifreq *
+__if_nextreq (struct ifreq *ifr)
+{
+ return ifr + 1;
+}
+
+static inline void
+__if_freereq (struct ifreq *ifreqs, int num_ifs)
+{
+ free (ifreqs);
+}
+
+/* Create a linked list of `struct kde_ifaddrs' structures, one for each
+ network interface on the host machine. If successful, store the
+ list in *IFAP and return 0. On errors, return -1 and set `errno'. */
+int
+kde_getifaddrs (struct kde_ifaddrs **ifap)
+{
+ /* This implementation handles only IPv4 interfaces.
+ The various ioctls below will only work on an AF_INET socket.
+ Some different mechanism entirely must be used for IPv6. */
+ int fd = socket (AF_INET, SOCK_DGRAM, 0);
+ struct ifreq *ifreqs;
+ int nifs;
+
+ if (fd < 0)
+ return -1;
+
+ __ifreq (&ifreqs, &nifs, fd);
+ if (ifreqs == NULL) /* XXX doesn't distinguish error vs none */
+ {
+ close (fd);
+ return -1;
+ }
+
+ /* Now we have the list of interfaces and each one's address.
+ Put it into the expected format and fill in the remaining details. */
+ if (nifs == 0)
+ *ifap = NULL;
+ else
+ {
+ struct Storage
+ {
+ struct kde_ifaddrs ia;
+ struct sockaddr addr, netmask, broadaddr;
+ char name[IF_NAMESIZE];
+ } *storage;
+ struct ifreq *ifr;
+ int i;
+
+ storage = (Storage*) malloc (nifs * sizeof storage[0]);
+ if (storage == NULL)
+ {
+ close (fd);
+ __if_freereq (ifreqs, nifs);
+ return -1;
+ }
+
+ i = 0;
+ ifr = ifreqs;
+ do
+ {
+ /* Fill in all pointers to the storage we've already allocated. */
+ storage[i].ia.ifa_next = &storage[i + 1].ia;
+ storage[i].ia.ifa_addr = &storage[i].addr;
+ storage[i].ia.ifa_netmask = &storage[i].netmask;
+ storage[i].ia.ifa_broadaddr = &storage[i].broadaddr; /* & dstaddr */
+
+ /* Now copy the information we already have from SIOCGIFCONF. */
+ storage[i].ia.ifa_name = strncpy (storage[i].name, ifr->ifr_name,
+ sizeof storage[i].name);
+ storage[i].addr = ifr->ifr_addr;
+
+ /* The SIOCGIFCONF call filled in only the name and address.
+ Now we must also ask for the other information we need. */
+
+ if (ioctl (fd, SIOCGIFFLAGS, ifr) < 0)
+ break;
+ storage[i].ia.ifa_flags = ifr->ifr_flags;
+
+ ifr->ifr_addr = storage[i].addr;
+
+ if (ioctl (fd, SIOCGIFNETMASK, ifr) < 0)
+ break;
+ storage[i].netmask = ifr->ifr_netmask;
+
+ if (ifr->ifr_flags & IFF_BROADCAST)
+ {
+ ifr->ifr_addr = storage[i].addr;
+ if (ioctl (fd, SIOCGIFBRDADDR, ifr) < 0)
+ break;
+ storage[i].broadaddr = ifr->ifr_broadaddr;
+ }
+ else if (ifr->ifr_flags & IFF_POINTOPOINT)
+ {
+ ifr->ifr_addr = storage[i].addr;
+ if (ioctl (fd, SIOCGIFDSTADDR, ifr) < 0)
+ break;
+ storage[i].broadaddr = ifr->ifr_dstaddr;
+ }
+ else
+ /* Just 'cause. */
+ memset (&storage[i].broadaddr, 0, sizeof storage[i].broadaddr);
+
+ storage[i].ia.ifa_data = NULL; /* Nothing here for now. */
+
+ ifr = __if_nextreq (ifr);
+ } while (++i < nifs);
+ if (i < nifs) /* Broke out early on error. */
+ {
+ close (fd);
+ free (storage);
+ __if_freereq (ifreqs, nifs);
+ return -1;
+ }
+
+ storage[i - 1].ia.ifa_next = NULL;
+
+ *ifap = &storage[0].ia;
+
+ close (fd);
+ __if_freereq (ifreqs, nifs);
+ }
+
+ return 0;
+}
+
+void
+kde_freeifaddrs (struct kde_ifaddrs *ifa)
+{
+ free (ifa);
+}
+
+#else
+int kde_getifaddrs(struct kde_ifaddrs **) {
+ return 1;
+}
+void kde_freeifaddrs(struct kde_ifaddrs *) {
+}
+struct { } kde_ifaddrs;
+
+#endif
+
+#endif
diff --git a/krfb/srvloc/getifaddrs.h b/krfb/srvloc/getifaddrs.h
new file mode 100644
index 00000000..65d40c01
--- /dev/null
+++ b/krfb/srvloc/getifaddrs.h
@@ -0,0 +1,96 @@
+/* ifaddrs.h -- declarations for getting network interface addresses
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C 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.
+
+ The GNU C 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 the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/**
+ * 02-12-26, tim@tjansen.de: added kde_ prefix, fallback-code,
+ * removed glibs dependencies
+ */
+
+#include "config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <sys/socket.h>
+#include <net/if.h>
+
+#ifdef HAVE_GETIFADDRS
+ #include <ifaddrs.h>
+
+#define kde_getifaddrs(a) getifaddrs(a)
+#define kde_freeifaddrs(a) freeifaddrs(a)
+#define kde_ifaddrs ifaddrs
+
+#else
+
+#ifndef _GETIFADDRS_H
+#define _GETIFADDRS_H
+
+
+#include <sys/socket.h>
+
+/* The `getifaddrs' function generates a linked list of these structures.
+ Each element of the list describes one network interface. */
+struct kde_ifaddrs
+{
+ struct kde_ifaddrs *ifa_next; /* Pointer to the next structure. */
+
+ char *ifa_name; /* Name of this network interface. */
+ unsigned int ifa_flags; /* Flags as from SIOCGIFFLAGS ioctl. */
+
+ struct sockaddr *ifa_addr; /* Network address of this interface. */
+ struct sockaddr *ifa_netmask; /* Netmask of this interface. */
+ union
+ {
+ /* At most one of the following two is valid. If the IFF_BROADCAST
+ bit is set in `ifa_flags', then `ifa_broadaddr' is valid. If the
+ IFF_POINTOPOINT bit is set, then `ifa_dstaddr' is valid.
+ It is never the case that both these bits are set at once. */
+ struct sockaddr *ifu_broadaddr; /* Broadcast address of this interface. */
+ struct sockaddr *ifu_dstaddr; /* Point-to-point destination address. */
+ } ifa_ifu;
+ /* These very same macros are defined by <net/if.h> for `struct ifaddr'.
+ So if they are defined already, the existing definitions will be fine. */
+# ifndef ifa_broadaddr
+# define ifa_broadaddr ifa_ifu.ifu_broadaddr
+# endif
+# ifndef ifa_dstaddr
+# define ifa_dstaddr ifa_ifu.ifu_dstaddr
+# endif
+
+ void *ifa_data; /* Address-specific data (may be unused). */
+};
+
+
+/* Create a linked list of `struct kde_ifaddrs' structures, one for each
+ network interface on the host machine. If successful, store the
+ list in *IFAP and return 0. On errors, return -1 and set `errno'.
+
+ The storage returned in *IFAP is allocated dynamically and can
+ only be properly freed by passing it to `freeifaddrs'. */
+extern int kde_getifaddrs (struct kde_ifaddrs **__ifap);
+
+/* Reclaim the storage allocated by a previous `getifaddrs' call. */
+extern void kde_freeifaddrs (struct kde_ifaddrs *__ifa);
+
+#endif
+
+#endif
+
diff --git a/krfb/srvloc/kinetinterface.cpp b/krfb/srvloc/kinetinterface.cpp
new file mode 100644
index 00000000..7ba5c15b
--- /dev/null
+++ b/krfb/srvloc/kinetinterface.cpp
@@ -0,0 +1,277 @@
+/*
+ * Represents an Inet interface
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kinetinterface.h"
+
+#include "getifaddrs.h"
+
+#include <netinet/in.h>
+#include <ksockaddr.h>
+
+
+class KInetInterfacePrivate {
+public:
+ QString name;
+ int flags;
+ KInetSocketAddress *address;
+ KInetSocketAddress *netmask;
+ KInetSocketAddress *broadcast;
+ KInetSocketAddress *destination;
+
+ KInetInterfacePrivate() :
+ flags(0),
+ address(0),
+ netmask(0),
+ broadcast(0),
+ destination(0) {
+ }
+
+ KInetInterfacePrivate(const QString _name,
+ int _flags,
+ KInetSocketAddress *_address,
+ KInetSocketAddress *_netmask,
+ KInetSocketAddress *_broadcast,
+ KInetSocketAddress *_destination) :
+ name(_name),
+ flags(_flags),
+ address(_address),
+ netmask(_netmask),
+ broadcast(_broadcast),
+ destination(_destination) {
+ }
+
+ ~KInetInterfacePrivate() {
+ if (address)
+ delete address;
+ if (netmask)
+ delete netmask;
+ if (broadcast)
+ delete broadcast;
+ if (destination)
+ delete destination;
+ }
+
+ KInetInterfacePrivate& operator =(const KInetInterfacePrivate& i) {
+ name = i.name;
+ flags = i.flags;
+ if (i.address)
+ address = new KInetSocketAddress(*i.address);
+ else
+ address = 0;
+ if (i.netmask)
+ netmask = new KInetSocketAddress(*i.netmask);
+ else
+ netmask = 0;
+ if (i.broadcast)
+ broadcast = new KInetSocketAddress(*i.broadcast);
+ else
+ broadcast = 0;
+ if (i.destination)
+ destination = new KInetSocketAddress(*i.destination);
+ else
+ destination = 0;
+ return *this;
+ }
+
+};
+
+
+KInetInterface::KInetInterface() :
+ d(0) {
+}
+
+KInetInterface::KInetInterface(const QString &_name,
+ int _flags,
+ KInetSocketAddress *_address,
+ KInetSocketAddress *_netmask,
+ KInetSocketAddress *_broadcast,
+ KInetSocketAddress *_destination) {
+ d = new KInetInterfacePrivate(_name, _flags,
+ _address, _netmask,
+ _broadcast, _destination);
+}
+
+KInetInterface::KInetInterface(const KInetInterface &i) :
+ d(0) {
+ if (!i.d)
+ return;
+
+ d = new KInetInterfacePrivate();
+ *d = *i.d;
+}
+
+KInetInterface::~KInetInterface() {
+ if (d)
+ delete d;
+}
+
+KInetInterface& KInetInterface::operator =(const KInetInterface& i) {
+ if (this == &i)
+ return *this;
+
+ if (d)
+ delete d;
+ d = 0;
+ if (!i.d)
+ return *this;
+
+ d = new KInetInterfacePrivate();
+ *d = *i.d;
+ return *this;
+}
+
+bool KInetInterface::isValid() const {
+ return d == 0;
+}
+
+QString KInetInterface::displayName() const {
+ return d->name;
+}
+
+QString KInetInterface::name() const {
+ return d->name;
+}
+
+int KInetInterface::flags() const {
+ return d->flags;
+}
+
+KInetSocketAddress *KInetInterface::address() const {
+ return d->address;
+}
+
+KInetSocketAddress *KInetInterface::netmask() const {
+ return d->netmask;
+}
+
+KInetSocketAddress *KInetInterface::broadcastAddress() const {
+ return d->broadcast;
+}
+
+KInetSocketAddress *KInetInterface::destinationAddress() const {
+ return d->destination;
+}
+
+KInetSocketAddress *KInetInterface::getPublicInetAddress() {
+ QValueVector<KInetInterface> v = getAllInterfaces(true);
+
+ // TODO: first step: take the default route interface
+
+ // try to find point-2-point interface, because it may be
+ // a dial-up connection. Take it.
+ QValueVector<KInetInterface>::const_iterator it = v.begin();
+ while (it != v.end()) {
+ if (((*it).flags() & (PointToPoint | Up | Running)) &&
+ (!((*it).flags() & Loopback)) &&
+ (*it).address() &&
+ ((*it).address()->family() == AF_INET))
+ return new KInetSocketAddress(*(*it).address());
+ it++;
+ }
+
+ // otherwise, just take the first non-loopback interface
+ it = v.begin();
+ while (it != v.end()) {
+ if (((*it).flags() & (Up | Running)) &&
+ (!((*it).flags() & Loopback)) &&
+ (*it).address() &&
+ ((*it).address()->family() == AF_INET))
+ return new KInetSocketAddress(*(*it).address());
+ it++;
+ }
+
+ // ok, giving up, try to take loopback
+ it = v.begin();
+ while (it != v.end()) {
+ if (((*it).flags() & (Up | Running)) &&
+ (*it).address())
+ return new KInetSocketAddress(*(*it).address());
+ it++;
+ }
+
+ // not even loopback..
+ return 0;
+}
+
+namespace {
+ KInetSocketAddress *createAddress(struct sockaddr *a) {
+ if (!a)
+ return 0;
+ else if (a->sa_family == AF_INET)
+ return new KInetSocketAddress((struct sockaddr_in*) a,
+ sizeof(struct sockaddr_in));
+#ifdef AF_INET6
+ else if (a->sa_family == AF_INET6)
+ return new KInetSocketAddress((struct sockaddr_in6*) a,
+ sizeof(struct sockaddr_in6));
+#endif
+ else
+ return 0;
+ }
+
+ int convertFlags(int flags) {
+ int r = 0;
+ if (flags & IFF_UP)
+ r |= KInetInterface::Up;
+ if (flags & IFF_BROADCAST)
+ r |= KInetInterface::Broadcast;
+ if (flags & IFF_LOOPBACK)
+ r |= KInetInterface::Loopback;
+ if (flags & IFF_POINTOPOINT)
+ r |= KInetInterface::PointToPoint;
+ if (flags & IFF_RUNNING)
+ r |= KInetInterface::Running;
+ if (flags & IFF_MULTICAST)
+ r |= KInetInterface::Multicast;
+
+ return r;
+ }
+}
+
+QValueVector<KInetInterface> KInetInterface::getAllInterfaces(bool includeLoopback) {
+ struct kde_ifaddrs *ads;
+ struct kde_ifaddrs *a;
+ QValueVector<KInetInterface> r;
+ if (kde_getifaddrs(&ads))
+ return r;
+
+ a = ads;
+ while (a) {
+ if ((a->ifa_flags & IFF_LOOPBACK) && !includeLoopback) {
+ a = a->ifa_next;
+ continue;
+ }
+ r.push_back(KInetInterface(QString::fromUtf8(a->ifa_name),
+ convertFlags(a->ifa_flags),
+ createAddress(a->ifa_addr),
+ createAddress(a->ifa_netmask),
+ (a->ifa_flags & IFF_BROADCAST) ?
+ createAddress(a->ifa_broadaddr) : 0,
+ (a->ifa_flags & IFF_POINTOPOINT) ?
+ createAddress(a->ifa_dstaddr) : 0));
+ a = a->ifa_next;
+ }
+
+ kde_freeifaddrs(ads);
+ return r;
+}
+
diff --git a/krfb/srvloc/kinetinterface.h b/krfb/srvloc/kinetinterface.h
new file mode 100644
index 00000000..30005547
--- /dev/null
+++ b/krfb/srvloc/kinetinterface.h
@@ -0,0 +1,186 @@
+/*
+ * Represents an Inet interface
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef KINETINTERFACE_H
+#define KINETINTERFACE_H
+
+#include <qvaluevector.h>
+#include <qcstring.h>
+#include <qstring.h>
+
+
+class KInetInterfacePrivate;
+class KInetSocketAddress;
+
+/**
+ * An Internet (IPv4 or IPv6) network interface.
+ *
+ * Represents a snapshot of the network interface's state. It is
+ * not possible to modify the interface using this class, only
+ * to read it. Note that the interfaces can change it any time
+ * (for example when the internet connection goes up or down),
+ * so when you use it in a server you may want to use it together
+ * with a @ref KInetInterfaceWatcher.
+ * Use @ref getAllInterfaces() to get all interfaces of a system.
+ *
+ * @author Tim Jansen <tim@tjansen.de>
+ * @short Represents an IPv4 or IPv6 Network Interface
+ * @see KInetInterfaceWatcher
+ * @since 3.2
+ */
+class KInetInterface {
+private:
+ KInetInterface(const QString &name,
+ int flags,
+ KInetSocketAddress *address,
+ KInetSocketAddress *netmask,
+ KInetSocketAddress *broadcast,
+ KInetSocketAddress *destination);
+
+public:
+ /**
+ * Default constructor. Creates an uninitialized KInetInterface.
+ * @see isValid()
+ */
+ KInetInterface();
+
+ /**
+ * Copy constructor. Makes a deep copy.
+ * @param i the KInetInterface to copy
+ */
+ KInetInterface(const KInetInterface &i);
+
+ /**
+ * Destructor
+ */
+ virtual ~KInetInterface();
+
+ /**
+ * Assignment, makes a deep copy of @p i.
+ * @param i the KInetInterface to copy
+ * @return this KInetInterface
+ */
+ KInetInterface& operator =(const KInetInterface& i);
+
+ /**
+ * Checks whether the object is valid. Only interfaces that
+ * have been created using the default constructor are invalid.
+ * @return true if valid, false if invalid
+ */
+ bool isValid() const;
+
+ /**
+ * Returns a user-readable name of the interface, if available.
+ * Otherwise it returns the same value as @ref name().
+ * @return the display name of the interface
+ * @see name()
+ */
+ QString displayName() const;
+
+ /**
+ * Returns the name of the interface, e.g. 'eth0'.
+ * @return the name of the interface
+ * @see displayName()
+ */
+ QString name() const;
+
+ /**
+ * Flags describing the interface. These flags
+ * can be ORed.
+ */
+ enum Flags {
+ Up = 1, ///< Interface is up.
+ Broadcast = 2, ///< Broadcast address (@ref broadcastAddress()) is valid..
+ Loopback = 8, ///< Interface is a loopback interface.
+ PointToPoint = 16, ///< Interface is a point-to-point interface.
+ Running = 128, ///< Interface is running.
+ Multicast = 65536 ///< Interface is multicast-capable.
+ };
+
+ /**
+ * A set of ORed flags describing the interface. See
+ * @ref Flags for description of the flags.
+ * @return the ORed @ref Flags of the interface
+ */
+ int flags() const;
+
+ /**
+ * Returns the address of the interface.
+ * The returned object is valid as long as this object
+ * exists.
+ * @return the address of this interface, can be 0 if the interface
+ * does not have an address
+ */
+ KInetSocketAddress *address() const;
+
+ /**
+ * Returns the netmask of the interface.
+ * The returned object is valid as long as this object
+ * exists.
+ * @return the netmask of this interface, can be 0 if the interface
+ * does not have an address
+ */
+ KInetSocketAddress *netmask() const;
+
+ /**
+ * Returns the broadcast address of the interface.
+ * The returned object is valid as long as this object
+ * exists.
+ * @return the broadcast address of this interface. Can be 0 if
+ * the interface is a peer-to-peer interface (like PPP)
+ */
+ KInetSocketAddress *broadcastAddress() const;
+
+ /**
+ * Returns the destination / peer address of the interface.
+ * It is used for peer-to-peer interfaces like PPP.
+ * The returned object is valid as long as this object
+ * exists.
+ * @return the destination address of this interface. Can be 0
+ * if the interface is not a peer-to-peer interface
+ */
+ KInetSocketAddress *destinationAddress() const;
+
+ /**
+ * Tries to guess the public internet address of this computer.
+ * This is not always successful, especially when the computer
+ * is behind a firewall or NAT gateway. In the worst case, it
+ * returns localhost.
+ * @return a KInetAddress object that contains the best match.
+ * The caller takes ownership of the object and is
+ * responsible for freeing it
+ */
+ static KInetSocketAddress *getPublicInetAddress();
+
+ /**
+ * Returns all active interfaces of the system.
+ *
+ * @param includeLoopback if true, include the loopback interface's
+ * name
+ * @return the list of IP addresses
+ */
+ static QValueVector<KInetInterface> getAllInterfaces(bool includeLoopback = false);
+
+private:
+ KInetInterfacePrivate* d;
+};
+
+#endif
diff --git a/krfb/srvloc/kinetinterfacewatcher.cpp b/krfb/srvloc/kinetinterfacewatcher.cpp
new file mode 100644
index 00000000..31b972f5
--- /dev/null
+++ b/krfb/srvloc/kinetinterfacewatcher.cpp
@@ -0,0 +1,59 @@
+/*
+ * Watches Inet interfaces
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "kinetinterfacewatcher.h"
+#include "kinetinterfacewatcher.moc"
+
+class KInetInterfaceWatcherPrivate {
+public:
+ QString interface;
+ int minInterval; // not used yet, but my be when a daemon watches
+
+
+ KInetInterfaceWatcherPrivate(const QString &iface,
+ int minIntv) :
+ interface(iface),
+ minInterval(minIntv) {
+ }
+};
+
+/*
+ * or all network interfaces.
+ * @param interface the name of the interface to watch (e.g.'eth0')
+ * or QString::null to watch all interfaces
+ * @param minInterval the minimum interval between two checks in
+ * seconds. Be careful not to check too often, to
+ * avoid unneccessary wasting of CPU time
+ */
+KInetInterfaceWatcher::KInetInterfaceWatcher(const QString &interface,
+ int minInterval) {
+ d = new KInetInterfaceWatcherPrivate(interface, minInterval);
+}
+
+QString KInetInterfaceWatcher::interface() const {
+ return d->interface;
+}
+
+KInetInterfaceWatcher::~KInetInterfaceWatcher() {
+ delete d;
+}
+
diff --git a/krfb/srvloc/kinetinterfacewatcher.h b/krfb/srvloc/kinetinterfacewatcher.h
new file mode 100644
index 00000000..3651b87d
--- /dev/null
+++ b/krfb/srvloc/kinetinterfacewatcher.h
@@ -0,0 +1,122 @@
+/*
+ * Watches Inet interfaces
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef KINETINTERFACEWATCHER_H
+#define KINETINTERFACEWATCHER_H
+
+#include <kinetinterface.h>
+#include <qobject.h>
+#include <qvaluevector.h>
+#include <qcstring.h>
+#include <qstring.h>
+
+
+class KInetInterfaceWatcherPrivate;
+
+
+/**
+ * KInetInterfaceWatcher can watch the state of one or all
+ * of the system's network interfaces.
+ * The watcher will emit the signal @ref changed() when an
+ * interface changed or a interface has been added or removed.
+ *
+ * @author Tim Jansen <tim@tjansen.de>
+ * @short Watches the state of the network interfaces
+ * @see KInetInterface
+ * @since 3.2
+ */
+class KInetInterfaceWatcher : public QObject {
+ Q_OBJECT
+public:
+ /**
+ * Creates a new KInetInterfaceWatcher. Before you can use it,
+ * you must @ref start() it.
+ *
+ * @param interface the name of the interface to watch (e.g.'eth0')
+ * or QString::null to watch all interfaces
+ * @param minInterval the minimum interval between two checks in
+ * seconds. Be careful not to check too often, to
+ * avoid unneccessary wasting of CPU time
+ */
+ KInetInterfaceWatcher(const QString &interface = QString::null,
+ int minInterval = 60);
+
+ /**
+ * Returns the name of the interface that is being watched.
+ * @return the name of the interface, or QString::null if all
+ * interfaces are watched
+ */
+ QString interface() const;
+
+ /**
+ * Starts the KInetInterfaceWatcher. It watches either one
+ * or all network interfaces. When one of them changed.
+ * it emits a @ref changed() signal.
+ * @param interface the name of the interface to watch (e.g.'eth0')
+ * or QString::null to watch all interfaces
+ * @param minInterval the minimum interval between two checks in
+ * seconds. Be careful not to check too often, to
+ * avoid unneccessary wasting of CPU time
+ * @see changed()
+ * @see stop()
+ */
+ void start(const QString &interface = QString::null,
+ int minInterval = 60);
+
+ /**
+ * Stops watching the interfaces.
+ * @see start()
+ */
+ void stop();
+
+ /**
+ * Destructor
+ */
+ virtual ~KInetInterfaceWatcher();
+
+signals:
+ /**
+ * Emitted when one or more watched interfaces have changed. The
+ * @p interfaceName is the name of the interface being watched, not
+ * the interface that has changed (because more than one interface
+ * may have changed).
+ * A change occurred, when
+ * @li a new interface has been added (when watching a single interface,
+ * only when an interface of that name has been added)
+ * @li an interface has been removed (when watching a single interface,
+ * only when this interface has been removed)
+ * @li the address or netmask of the interface changed
+ *
+ * No change will be emitted when the broadcast address or destination
+ * address has changed.
+ *
+ * @param interfaceName the name of the interface that is watched,
+ * by the emitter, or QString::null if all
+ * interfaces are being watched
+ * @see start()
+ */
+ void changed(QString interfaceName);
+
+private:
+ KInetInterfaceWatcherPrivate* d;
+};
+
+#endif
diff --git a/krfb/srvloc/kserviceregistry.cpp b/krfb/srvloc/kserviceregistry.cpp
new file mode 100644
index 00000000..1a14f9b0
--- /dev/null
+++ b/krfb/srvloc/kserviceregistry.cpp
@@ -0,0 +1,181 @@
+/*
+ * Interface to register SLP services.
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+/**
+ * TODO: see below..
+ */
+
+#include "config.h"
+#include "kserviceregistry.h"
+#include <kdebug.h>
+
+
+#ifdef HAVE_SLP
+#include <slp.h>
+
+class KServiceRegistryPrivate {
+public:
+ KServiceRegistryPrivate(const QString &lang) :
+ m_opened(false),
+ m_lang(lang) {
+ }
+ bool ensureOpen();
+
+ bool m_opened;
+ QString m_lang;
+
+ SLPHandle m_handle;
+ friend void KServiceRegistryRegReport(SLPHandle slp,
+ SLPError errcode,
+ void* cookie);
+ bool m_cbSuccess;
+};
+
+void KServiceRegistryRegReport(SLPHandle,
+ SLPError errcode,
+ void* cookie) {
+ KServiceRegistryPrivate *s = (KServiceRegistryPrivate*) cookie;
+ s->m_cbSuccess = (errcode == SLP_OK);
+ if (errcode < 0)
+ kdDebug() << "KServiceRegistry: error in callback:" << errcode <<endl;
+}
+
+
+KServiceRegistry::KServiceRegistry(const QString &lang) {
+ d = new KServiceRegistryPrivate(lang);
+}
+
+KServiceRegistry::~KServiceRegistry() {
+ if (d->m_opened)
+ SLPClose(d->m_handle);
+ delete d;
+}
+
+bool KServiceRegistryPrivate::ensureOpen() {
+ SLPError e;
+
+ if (m_opened)
+ return true;
+
+ e = SLPOpen(m_lang.latin1(), SLP_FALSE, &m_handle);
+ if (e != SLP_OK) {
+ kdDebug() << "KServiceRegistry: error while opening:" << e <<endl;
+ return false;
+ }
+ m_opened = true;
+ return true;
+}
+
+bool KServiceRegistry::available() {
+ return d->ensureOpen();
+}
+
+bool KServiceRegistry::registerService(const QString &serviceURL,
+ QString attributes,
+ unsigned short lifetime) {
+ if (!d->ensureOpen())
+ return false;
+
+ d->m_cbSuccess = true;
+ SLPError e = SLPReg(d->m_handle,
+ serviceURL.latin1(),
+ lifetime > 0 ? lifetime : SLP_LIFETIME_MAXIMUM,
+ 0,
+ attributes.isNull() ? "" : attributes.latin1(),
+ SLP_TRUE,
+ KServiceRegistryRegReport,
+ d);
+ if (e != SLP_OK) {
+ kdDebug() << "KServiceRegistry: error in registerService:" << e <<endl;
+ return false;
+ }
+ return d->m_cbSuccess;
+}
+
+bool KServiceRegistry::registerService(const QString &serviceURL,
+ QMap<QString,QString> attributes,
+ unsigned short lifetime) {
+ if (!d->ensureOpen())
+ return false;
+// TODO: encode strings in map?
+ QString s;
+ QMap<QString,QString>::iterator it = attributes.begin();
+ while (it != attributes.end()) {
+ if (!s.isEmpty())
+ s += ",";
+ s += QString("(%1=%2)").arg(it.key()).arg(it.data());
+ it++;
+ }
+ return registerService(serviceURL, s, lifetime);
+}
+
+void KServiceRegistry::unregisterService(const QString &serviceURL) {
+ if (!d->m_opened)
+ return;
+ SLPDereg(d->m_handle, serviceURL.latin1(),
+ KServiceRegistryRegReport,
+ d);
+}
+
+QString KServiceRegistry::encodeAttributeValue(const QString &value) {
+ char *n;
+ if (SLPEscape(value.latin1(), &n, SLP_TRUE) == SLP_OK) {
+ QString r(n);
+ SLPFree(n);
+ return r;
+ }
+ return QString::null;
+}
+
+#else
+
+KServiceRegistry::KServiceRegistry(const QString &lang) :
+ d(0) {
+}
+
+KServiceRegistry::~KServiceRegistry() {
+}
+
+bool KServiceRegistry::available() {
+ return false;
+}
+
+bool KServiceRegistry::registerService(const QString &, QString, unsigned short ) {
+ return false;
+}
+
+bool KServiceRegistry::registerService(const QString &, QMap<QString,QString>, unsigned short) {
+ return false;
+}
+
+void KServiceRegistry::unregisterService(const QString &) {
+}
+
+QString KServiceRegistry::encodeAttributeValue(const QString &value) {
+ return value;
+}
+
+#endif
+
+QString KServiceRegistry::createCommaList(const QStringList &values) {
+ return values.join(",");
+}
+
+
+
diff --git a/krfb/srvloc/kserviceregistry.h b/krfb/srvloc/kserviceregistry.h
new file mode 100644
index 00000000..f017b129
--- /dev/null
+++ b/krfb/srvloc/kserviceregistry.h
@@ -0,0 +1,161 @@
+/*
+ * Interface to register SLP services.
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * TODO: put private variables and SLP-dependencies in private class
+ */
+
+#ifndef __KSERVICEREGISTRY_H
+#define __KSERVICEREGISTRY_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qmap.h>
+
+class KServiceRegistryPrivate;
+
+/**
+ * KServiceRegistry allows you to announce your service using SLP (RFC 2608).
+ *
+ * Use KServiceRegistry to announce a service. The service will be valid
+ * until its lifespan ends, you unregister it or delete the KServiceRegistry,
+ * whatever comes first.
+ * A service will be described using a Service URL and an optional list of
+ * attributes. The syntax for a simple Service URL is
+ * <pre>
+ * service:%srvtype%://%addrspec%
+ * </pre>
+ * where "%srvtype%" specifies the protocol and "%addrspec5" the address.
+ * Examples of valid Service URLs are "service:http://www.kde.org",
+ * "service:lpr://printer.example.org/myqueure" and
+ * "service:http://your.server.org:8080".
+ * To provide more information about your service than just the protocol,
+ * port and address you can use abstract service types.
+ * A Service URL that uses an abstract service type has the form
+ * <pre>
+ * service:%abstract-type%:%concrete-type%
+ * </pre>
+ * where %abstract-type% describes the kind of service and %concrete-type% specifies
+ * the protocol and address. Examples are
+ * "service:printer:lpr://printer.example.com/lp0" and
+ * "service:printer:ipp://printer.example.com/".
+ * Note that you cannot invent you own types but only take those that are
+ * registered at IANA. To create your own service type you must become a naming
+ * authority. Then you can use service types of the form
+ * "%srvtype%.%naming-authority%". Assuming that "kde" is the id of a
+ * IANA-registered naming authority, a valid Service URL would be
+ * "service:weatherp.kde://weather.example.com". You can find more information
+ * about Service URLs in the IETF RFCs 2608 and 3224.
+ * Additionally each service can have one or more attributes to carry
+ * additional information about the service. Attributes are a simple pair of
+ * strings, one for the attributes name and one for the value. They can be
+ * used, for example, to describe the type of a printer or the email address
+ * of an administrator.
+ *
+ * Service templates can be used to describe an abstract type, including the
+ * syntax of the concrete type and the attributes. The use of service
+ * templates is described in RFC 2609, you can find the existing service
+ * templates at http://www.iana.org/assignments/svrloc-templates.htm .
+ *
+ * Example:
+ * <pre>
+ * KServiceRegistry ksr;
+ * KInetAddress kia = KInetAddress->getLocalAddress();
+ * ksr.registerService(QString("service:remotedesktop.kde:vnc://%1:0").arg(kia->nodeName()),
+ * "(type=shared)");
+ * delete kia;
+ * </pre>
+ *
+ * @version $Id$
+ * @author Tim Jansen, tim@tjansen.de
+ */
+class KServiceRegistry {
+ public:
+ /**
+ * Creates a new service registration instance for the given
+ * language.
+ * @param lang the language as two letter code, or QString::null for the
+ * system default
+ */
+ KServiceRegistry(const QString &lang = QString::null);
+ virtual ~KServiceRegistry();
+
+ /**
+ * Returns true if service registration is generally possible.
+ * Reasons for a failure could be that the SLP libraries are not
+ * installed or no SA daemon (slpd) is installed
+ * @return true if service registration seems to be possible
+ */
+ bool available();
+
+ /**
+ * Creates a comma-separated string of lists, as required by many functions.
+ * @param map the items of this list will be converted
+ * @return the comma-separated list
+ */
+ static QString createCommaList(const QStringList &values);
+
+ /**
+ * Encodes an QString for use as a attribute value. This will escape
+ * all characters that are not allowed. This method is only available
+ * when a SLP library is available, otherwise it will return the
+ * given value.
+ * @param value the value string to encode
+ * @return the encoded value string
+ */
+ static QString encodeAttributeValue(const QString &value);
+
+ /**
+ * Registers the given service.
+ * @param serviceURL the service URL to register
+ * @param attributes a list of the attributes to register, encoded in
+ * the format "(attr1=val1),(attr2=val2),(attr3=val3)"
+ * Use an empty string if you dont want to set attributes
+ * @param lifetime the lifetime of the service in seconds, or 0 for infinite
+ * @return true if successful, false otherwise. False usually means that no
+ * SA daemon (slpd) is running.
+ */
+ bool registerService(const QString &serviceURL,
+ QString attributes = QString::null,
+ unsigned short lifetime = 0);
+
+ /**
+ * Registers the given service.
+ * @param serviceURL the service URL to register
+ * @param attributes a map of all attributes
+ * @param lifetime the lifetime of the service in seconds, or 0 for infinite
+ * @return true if successful, false otherwise. False usually means that no
+ * SA daemon is running (slpd).
+ */
+ bool registerService(const QString &serviceURL,
+ QMap<QString,QString> attributes,
+ unsigned short lifetime = 0);
+
+ /**
+ * Unregisters the given service.
+ * @param serviceURL the service URL to unregister
+ */
+ void unregisterService(const QString &serviceURL);
+
+ private:
+ KServiceRegistryPrivate *d;
+};
+
+#endif
diff --git a/krfb/srvloc/uuid.cpp b/krfb/srvloc/uuid.cpp
new file mode 100644
index 00000000..61f78d86
--- /dev/null
+++ b/krfb/srvloc/uuid.cpp
@@ -0,0 +1,245 @@
+/*
+ * libuuid - library for generating UUIDs
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ * Copyright (C) 2002 Tim Jansen
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU
+ * Library General Public License.
+ * %End-Header%
+ *
+ * 2002-12-15, tim@tjansen.de:
+ * merged all *.c files,
+ * replaced all function that are not needed to generate a time uuid,
+ * added createUUID()
+ */
+
+#include "uuid.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <time.h>
+
+#ifndef _SVID_SOURCE
+#define _SVID_SOURCE
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_SRANDOM
+#define srand(x) srandom(x)
+#define rand() random()
+#endif
+
+typedef unsigned char uuid_t[16];
+typedef unsigned char __u8;
+typedef unsigned short __u16;
+typedef unsigned int __u32;
+
+
+struct uuid {
+ __u32 time_low;
+ __u16 time_mid;
+ __u16 time_hi_and_version;
+ __u16 clock_seq;
+ __u8 node[6];
+};
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+ const __u8 *ptr = in;
+ __u32 tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_low = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_mid = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->time_hi_and_version = tmp;
+
+ tmp = *ptr++;
+ tmp = (tmp << 8) | *ptr++;
+ uu->clock_seq = tmp;
+
+ memcpy(uu->node, ptr, 6);
+}
+
+static int get_random_fd(void)
+{
+ struct timeval tv;
+ static int fd = -2;
+ int i;
+
+ if (fd == -2) {
+ gettimeofday(&tv, 0);
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1)
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+ }
+ /* Crank the random number generator a few times */
+ gettimeofday(&tv, 0);
+ for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+ rand();
+ return fd;
+}
+
+
+/*
+ * Generate a series of random bytes. Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+static void get_random_bytes(void *buf, int nbytes)
+{
+ int i, fd = get_random_fd();
+ int lose_counter = 0;
+ char *cp = (char *) buf;
+
+ if (fd >= 0) {
+ while (nbytes > 0) {
+ i = read(fd, cp, nbytes);
+ if (i <= 0) {
+ if (lose_counter++ > 16)
+ break;
+ continue;
+ }
+ nbytes -= i;
+ cp += i;
+ lose_counter = 0;
+ }
+ }
+
+ /* XXX put something better here if no /dev/random! */
+ for (i = 0; i < nbytes; i++)
+ *cp++ = rand() & 0xFF;
+ return;
+}
+
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+static int get_clock(__u32 *clock_high, __u32 *clock_low, __u16 *ret_clock_seq)
+{
+ static int adjustment = 0;
+ static struct timeval last = {0, 0};
+ static __u16 clock_seq;
+ struct timeval tv;
+ unsigned long long clock_reg;
+
+try_again:
+ gettimeofday(&tv, 0);
+ if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+ get_random_bytes(&clock_seq, sizeof(clock_seq));
+ clock_seq &= 0x1FFF;
+ last = tv;
+ last.tv_sec--;
+ }
+ if ((tv.tv_sec < last.tv_sec) ||
+ ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec < last.tv_usec))) {
+ clock_seq = (clock_seq+1) & 0x1FFF;
+ adjustment = 0;
+ last = tv;
+ } else if ((tv.tv_sec == last.tv_sec) &&
+ (tv.tv_usec == last.tv_usec)) {
+ if (adjustment >= MAX_ADJUSTMENT)
+ goto try_again;
+ adjustment++;
+ } else {
+ adjustment = 0;
+ last = tv;
+ }
+
+ clock_reg = tv.tv_usec*10 + adjustment;
+ clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
+ clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
+
+ *clock_high = clock_reg >> 32;
+ *clock_low = clock_reg;
+ *ret_clock_seq = clock_seq;
+ return 0;
+}
+
+static void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+ __u32 tmp;
+ unsigned char *out = ptr;
+
+ tmp = uu->time_low;
+ out[3] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[2] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[1] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[0] = (unsigned char) tmp;
+
+ tmp = uu->time_mid;
+ out[5] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[4] = (unsigned char) tmp;
+
+ tmp = uu->time_hi_and_version;
+ out[7] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[6] = (unsigned char) tmp;
+
+ tmp = uu->clock_seq;
+ out[9] = (unsigned char) tmp;
+ tmp >>= 8;
+ out[8] = (unsigned char) tmp;
+
+ memcpy(out+10, uu->node, 6);
+}
+
+static void uuid_generate_time(uuid_t out)
+{
+ static unsigned char node_id[6];
+ struct uuid uu;
+ __u32 clock_mid;
+
+ get_random_bytes(node_id, 6);
+ get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
+ uu.clock_seq |= 0x8000;
+ uu.time_mid = (__u16) clock_mid;
+ uu.time_hi_and_version = (clock_mid >> 16) | 0x1000;
+ memcpy(uu.node, node_id, 6);
+ uuid_pack(&uu, out);
+}
+
+static void uuid_unparse(const uuid_t uu, char *out)
+{
+ struct uuid uuid;
+
+ uuid_unpack(uu, &uuid);
+ sprintf(out,
+ "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+ uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+ uuid.node[0], uuid.node[1], uuid.node[2],
+ uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+QString createUUID() {
+ char s[37];
+ uuid_t uu;
+ uuid_generate_time(uu);
+ uuid_unparse(uu, s);
+ return QString(s);
+}
+
diff --git a/krfb/srvloc/uuid.h b/krfb/srvloc/uuid.h
new file mode 100644
index 00000000..c566a7db
--- /dev/null
+++ b/krfb/srvloc/uuid.h
@@ -0,0 +1,29 @@
+/*
+ * Micro UUID library, based on libuuid
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ * Copyright (C) 2002 Tim Jansen <tim@tjansen.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UUID_H
+#define UUID_H
+
+#include <qstring.h>
+
+QString createUUID();
+
+#endif /* UUID_H */
diff --git a/ksirc/Artistic b/ksirc/Artistic
new file mode 100644
index 00000000..11f4d82d
--- /dev/null
+++ b/ksirc/Artistic
@@ -0,0 +1,131 @@
+
+
+
+
+ The "Artistic License"
+
+ Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+ "Package" refers to the collection of files distributed by the
+ Copyright Holder, and derivatives of that collection of files
+ created through textual modification.
+
+ "Standard Version" refers to such a Package if it has not been
+ modified, or has been modified in accordance with the wishes
+ of the Copyright Holder as specified below.
+
+ "Copyright Holder" is whoever is named in the copyright or
+ copyrights for the package.
+
+ "You" is you, if you're thinking about copying or distributing
+ this Package.
+
+ "Reasonable copying fee" is whatever you can justify on the
+ basis of media cost, duplication charges, time of people involved,
+ and so on. (You will not be required to justify it to the
+ Copyright Holder, but only to the computing community at large
+ as a market that must bear the fee.)
+
+ "Freely Available" means that no fee is charged for the item
+ itself, though there may be fees involved in handling the item.
+ It also means that recipients of the item may redistribute it
+ under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder. A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+ a) place your modifications in the Public Domain or otherwise make them
+ Freely Available, such as by posting said modifications to Usenet or
+ an equivalent medium, or placing the modifications on a major archive
+ site such as uunet.uu.net, or by allowing the Copyright Holder to include
+ your modifications in the Standard Version of the Package.
+
+ b) use the modified Package only within your corporation or organization.
+
+ c) rename any non-standard executables so the names do not conflict
+ with standard executables, which must also be provided, and provide
+ a separate manual page for each non-standard executable that clearly
+ documents how it differs from the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+ a) distribute a Standard Version of the executables and library files,
+ together with instructions (in the manual page or equivalent) on where
+ to get the Standard Version.
+
+ b) accompany the distribution with the machine-readable source of
+ the Package with your modifications.
+
+ c) give non-standard executables non-standard names, and clearly
+ document the differences in manual pages (or equivalent), together
+ with instructions on where to get the Standard Version.
+
+ d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package. You may charge any fee you choose for support of this
+Package. You may not charge a fee for this Package itself. However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own. You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package. If such scripts or library files are aggregated with this
+Package via the so-called "undump" or "unexec" methods of producing a
+binary executable image, then distribution of such an image shall
+neither be construed as a distribution of this Package nor shall it
+fall under the restrictions of Paragraphs 3 and 4, provided that you do
+not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other
+languages) supplied by you and linked into this Package in order to
+emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution. Such use shall not be
+construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+ The End
diff --git a/ksirc/ChangeLog b/ksirc/ChangeLog
new file mode 100644
index 00000000..808b9e8c
--- /dev/null
+++ b/ksirc/ChangeLog
@@ -0,0 +1,270 @@
+1998-03-29 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Fixed problem with "/names" opening a zillion windows and
+ joining every channel in existance.
+
+ * toplevel.cpp: re-writting parse-input to do better checking and
+ error handling. Now uses an internal EString, a QString class
+ which emits exceptions on error conditions. Currently only 1
+ exception exists, the estringOutOfBounds exception to indicate
+ find returned a value out of the bounds of the string. Other
+ exception handling include failure cases like parse errors.
+
+ * Added better puke support and more widgets. Currently 8
+ diffrent widgets exist, and 1 layout manager.
+
+1998-03-13 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Nick list sorting works much better, still broken when
+ there's only a couple of entries, but it's better.
+
+ * Fixed /names problem.
+
+1998-03-07 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Majour update comes in the form of the puke sub directory. This
+ contains the starting frame work for full control of ksirc from
+ perl scripts. Right now it implements a small widget set (ha!
+ small!! a widget, a frame and a lineedit). Ground work is all set
+ to start adding more. :)
+
+ * puke/pwidget.pm (close): Wow it's been a while since this was
+ updated =)
+
+
+
+1998-02-17 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Default rules added:
+ + Filter ksirc colours
+ + Filter mirc colours
+ + Colourize nicks
+ + Colourize your nick
+
+ * KSPrefs/defaultfilters.cpp: Added default filters
+ configure/prefrences dialog.
+
+ * baserules.cpp (defaultRules): Added basic default filter rules.
+
+1998-02-16 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Numberous other changes in toplevel.cpp to support changes to
+ nick list.
+
+ * alistbox.cpp (nickListItem): New nick list item keeps all nicks
+ in special items with setable colours and op/voice status falgs.
+ (findNick): New findNick() function allows quick searchin of nick
+ list via binary search.
+ (item): Returns nickListItem now.
+
+1998-02-07 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Misc bug fixes and updates.
+
+1998-02-01 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * KSCutDialog/KSCutDialog.cpp (scrollToBot): Added copy button,
+ and made it scroll to the bottom better.
+
+ * ahistlineedit.cpp (mousePressEvent): Fixed paste problem, added
+ copy button for poor soles who have problem with highlighting and
+ copying.
+
+ * dsirc (donumeric): Fixed dcc chat messages going to the wrong window.
+
+1998-01-30 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * When talking to a nick doesn't show nick list.
+
+ * toplevel.cpp (KSircTopLevel): Fixed a few bugs with drawing and
+ reszing.
+
+ * New freeze slot for irc listitems, should speed up global
+ changes, etc.
+
+ * Misc changes and fixes.
+
+ * toplevel.cpp (parse_input): If we see a nick change that's
+ pertinent to us, ie we're talking to a nick and he changes nicks,
+ try and follow the nick change.
+
+1998-01-28 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * toplevel.cpp (parse_input): Clears screan before displaying the
+ prompt. Needed minor changes everywhere.
+ (parse_input): Added ssfe 'R' message to rejoin a channel.
+
+ * servercontroller.cpp (saveProperties): Added KDE session
+ management.
+
+1998-01-25 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Handles +v on nicks in channels.
+
+ * Big fixes throughout.
+
+ * irclistbox.cpp (updateScrollBars): General cleanup of scroll
+ bars, they work correctly now.
+
+1998-01-24 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Ran ksirc through gprof and inlined a few small functions to
+ help in performance.
+
+ * toplevel.cpp: Fixed Menubar being offset.
+
+ * irclistitem.cpp (setupPainterText): Majour update to painting
+ routine. Now pre draws the "line" in a QPixmap buffer, and simply
+ dumps the pixmap to the screen in a paintevent. Essentially
+ double buffered, much faster, very nice.
+
+1998-01-21 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * toplevel.cpp (UserParseMenu): User menus no longer support %s,
+ but rather the perl variable $$dest_nick, this means you can now
+ use any sirc variable available, and as many times as you like.
+ This is going to break a lot of things!
+
+ * servercontroller.cpp (ProcMessage): Disabled Connections->Join
+ Channel when last server connection is closed.
+ Added icon, thanks to Druppy for making it.
+
+ * toplevel.cpp (showTicker): Fixed bug in starting ticker, was -5
+ instead of -i.
+
+ * KSTicker/ksticker.cpp (timerEvent): Added colour printing
+ support and double support. Little cleaner, looks ok.
+
+1998-01-20 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * ksircprocess.cpp (KSircProcess): Added sirc.help.gz and
+ SIRCLIBDIR infor so help now works correctly!!!! ADDED /help!!
+
+ * Moved all UserMenu support into the prefrences list box, and all
+ that it entails. Not 100% clean, but it's pretty good.
+
+ * toplevel.cpp (control_message): Added support for changing the
+ font size of the line edit.
+
+ * cleaned up support for the user menu in toplevel, and remove all
+ the extra classes.
+
+1998-01-18 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * KSPrefs/ksprefs.cpp: New Preferences and settings window. The
+ basic window and idea has been set out, it simply needs more pages
+ plugged into it now.
+
+ * ksircprocess.cpp (KSircProcess): Added support for the new
+ startup settings, etc.
+
+ * ksircprocess/servercontroller/toplevel: Now starts with channel
+ name !no_channel so that deletes go through correctly. (we never
+ delete !default).
+
+ * servercontroller.h: Updated and fixed so the window resizes
+ correctly.
+
+1998-01-15 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Ticker now starts with data right away, heck, I just plug it
+ with the last 5 lines!
+
+ * FilterRuleEditor.cpp (moveRule): Fixed error with $$var being
+ nuked.
+
+ * Updated the entire messaging system between ServerController and
+ ksirc process, they can now talk freely without a zillion signals
+ and slots. Communications is fully bidirectional and should work
+ well.
+
+ * toplevel.cpp (parse_input): Now deals with ssfe#p correctly!!!
+ Finally!!!! (prompts, for say nicks and passwords, try /oper :) )
+
+1998-01-13 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * servercontroller.cpp (notify_nick_online): Added grahical notify
+ list. It uses the server controller and adds an Online brancs
+ with the nicks of people currently online on each server.
+
+1998-01-12 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Fixed filters.pl so being "away" doesn't mess up the lag'o'meter.
+
+ * Updated docs added keys.help and updates servercontroller for it.
+
+1998-01-10 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * toplevel.cpp (KSircTopLevel): Menus updated and working correctly.
+
+ * filters.pl (hook_ctcp_lag): Added feature for lag monitoring up
+ in the upper right hand corner. Menus are broken though :(
+
+1998-01-09 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * toplevel.cpp (pasteToWindow): Fixed ut & paste to work correctly.
+
+1998-01-02 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * alistbox.cpp (inSort): Fixed sorting routine. Stripped it made
+ it work, it's faster, etc. SHould work better.
+
+ * toplevel.cpp: Added new Willy power tab completion.
+
+1998-01-01 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Various bug fixes as per the BUGLIST.
+
+ * Fixed scroll to bottom, no longer scroll to bottom when you're
+ scrolled up, except when you hit enter.
+
+1997-12-31 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Fixed mem leak in KSTicker. Leaked 1 byte per message.
+
+ * Cleaned up the cut&paste code, made it a little consistend with X.
+
+1997-12-30 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Added support for multiple op/deop'ings. Seems ok, except for
+ when a non-op changes their nick they might be shown as an op.
+ Looking into it.
+
+ * Generic bug fixing and re-orginization since the 26th. Check
+ the cvs logs.
+
+ * Added full cut&paste support. Seems to work ok.
+
+1997-12-26 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * toplevel.cpp (resizeEvent): fixed resize problem.
+ (parse_input): Added `sirc clear handeling.
+ (parse_input): I think I fixed the auto create auto joining windows
+
+ * Added filter rule control mechanisim where each module is
+ queired and can suply it's own filter rules. Usefull for DCC and
+ bolding etc. Be carefull with toplevel, since there's multiple
+ instances each will suppy it's rule, and should ONLY be used if
+ each toplevel needs a filter rule.
+
+ * irclistitem.cpp (colourDrawText): Added ~~ to escape and print a
+ single ~. ~~ is always garunteed to print ~.
+
+ * ioBroadcast.cpp (defaultRules): Added a a default rule to seek
+ out stray ~[bcui]nick@someplace due to bad idents and to escape
+ the ~.
+
+1997-12-25 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Added listing of OPs at the top of list box with a seperator
+ dividing ops from non-ops. Toplevel now tracks nick change and op
+ status.
+
+1997-12-24 Andrew Stanley-Jones <asj@chowtown.cban.com>
+
+ * Added code to look fro same fg and bg and change the bg so you
+ can see the text.
+
+ * Cleaned up nick completion, etc.
+
+$Revision$
diff --git a/ksirc/FAQ b/ksirc/FAQ
new file mode 100644
index 00000000..8166b0e5
--- /dev/null
+++ b/ksirc/FAQ
@@ -0,0 +1,73 @@
+
+FAQ answers:
+
+*** Why KSirc:
+*** Do I need KDE and the QT libraries installed?!?!?
+*** Why use QT/KDE instead of GTK or other toolkits?
+*** Filters in under 25 words:
+*** How do I Query Someone? (private convertation)
+*** What are all the keystrokes?
+*** How does nick completion work?
+
+*** Why KSirc:
+
+There's already a lot of good irc clients, so why write another one?
+In my opinion X lacked a flexible and script-able irc client. While
+"xterm -e irc" works well, it doesn't take advantage of a graphical
+environment. On the other hand, most X irc clients focus on the
+graphical part and never really develop the scripting side very well.
+KSirc tries to solve these two problems. It uses sirc and as such
+provides VERY powerful perl scripting while providing the X interface.
+
+*** Do I need KDE and the QT libraries installed?!?!?
+
+No.
+
+Static binary versions are installed and work fine. If you want to
+compile the source though, you'll need QT and KDE installed.
+
+
+*** Why use QT/KDE instead of GTK or other toolkits?
+
+At the time KSirc started QT/KDE provided the best possible graphical
+interface and programming environment in my opinion. GTK while
+potentially a useful toolkit in the future doesn't provide a stable
+working environment at the moment. Plus I don't like what GTK looks
+like.
+
+I once started writing a client in Xforms, but it wasn't flexible
+enough and turned me against most GUI C programming.
+
+
+*** Filters in under 25 words:
+
+Filters allow you to modify, mangle and redirect irc messages. You
+can send messages which mean a certain format or style into different
+windows (for oper's filter out NickServ kill's etc)
+
+README.filters contains a detailed explanation and examples on how to
+use filters.
+
+*** How do I Query Someone? (private convertation)
+
+/join <nick>
+
+or
+
+/join =<nick> for a dcc chat, make sure to open the dcc chat first
+
+*** What are all the keystrokes?
+
+Ctrl-Enter - go backwards through nicks that have messaged you
+Ctrl-Shift-Enter - goes forwards
+
+Shft-PageUp/PageDown - scroll forwards and backwards
+
+*** How does nick completion work?
+
+part-nick: at the beginning of the line is expanded to nick:
+
+::part-nick anywhere in the line is expanded to nick. If part-nick is
+not found the :: is removed and part-nick remains.
+
+
diff --git a/ksirc/FilterRuleEditor.cpp b/ksirc/FilterRuleEditor.cpp
new file mode 100644
index 00000000..08ac6584
--- /dev/null
+++ b/ksirc/FilterRuleEditor.cpp
@@ -0,0 +1,244 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: FilterRuleEditor.cpp
+ Last generated: Mon Dec 15 18:14:27 1997
+
+ *********************************************************************/
+
+#include "FilterRuleEditor.h"
+
+#include <qregexp.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+#include <qlineedit.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+
+FilterRuleEditor::FilterRuleEditor
+(
+ QWidget* parent,
+ const char* name
+)
+ : KDialogBase( parent, name, true, i18n( "Edit Filter Rules" ),
+ Close, Close, true )
+{
+ filter = new FilterRuleWidget( this, name );
+
+ setMainWidget( filter );
+
+ updateListBox();
+ newHighlight(0);
+
+ // ### split "OkPressed()" into 2 slots
+ connect( filter->ModifyButton, SIGNAL(clicked()), SLOT(OkPressed()) );
+ connect( filter->InsertButton, SIGNAL(clicked()), SLOT(OkPressed()) );
+
+ connect( filter->NewButton, SIGNAL(clicked()), SLOT(newRule()) );
+ connect( filter->DeleteButton, SIGNAL(clicked()), SLOT(deleteRule()) );
+
+ connect( filter->UpButton, SIGNAL(clicked()), SLOT(raiseRule()) );
+ connect( filter->DownButton, SIGNAL(clicked()), SLOT(lowerRule()) );
+
+ connect( filter->RuleList, SIGNAL(highlighted(int)), SLOT(newHighlight(int)) );
+ connect( filter->RuleList, SIGNAL(selected(int)), SLOT(newHighlight(int)) );
+
+
+ filter->RuleList->setHScrollBarMode( QListBox::AlwaysOff );
+ filter->RuleList->setMultiSelection( FALSE );
+
+ filter->DownButton->setPixmap( BarIcon( "down", KIcon::SizeSmall ) );
+ filter->UpButton->setPixmap( BarIcon( "up", KIcon::SizeSmall ) );
+}
+
+
+FilterRuleEditor::~FilterRuleEditor()
+{
+}
+
+void FilterRuleEditor::newRule()
+{
+ filter->LineTitle->setText( QString::null );
+ filter->LineSearch->setText( QString::null );
+ filter->LineFrom->setText( QString::null );
+ filter->LineTo->setText( QString::null );
+ filter->LineTitle->setFocus();
+
+ filter->InsertButton->setEnabled( true );
+ filter->ModifyButton->setEnabled( false );
+}
+
+void FilterRuleEditor::OkPressed()
+{
+ int number, after;
+ KConfig *kConfig = kapp->config();
+
+ if(filter->InsertButton->isEnabled()){
+ number = kConfig->readNumEntry("Rules", 0) + 1;
+ after = number - 1;
+ kConfig->writeEntry("Rules", number);
+ }
+ else if(filter->ModifyButton->isEnabled()){
+ number = filter->RuleList->currentItem() + 1;
+ after = number - 1;
+ }
+ else{
+ return;
+ }
+
+ QString name = filter->LineTitle->text();
+ QString search = filter->LineSearch->text();
+ QString from = filter->LineFrom->text();
+ QString to = filter->LineTo->text();
+
+ if( name.isEmpty() || search.isEmpty() || from.isEmpty() || to.isEmpty() ){
+ KMessageBox::error(this, i18n("Cannot create the rule since not\n"
+ "all the fields are filled in."), i18n("Error"));
+
+ }
+ else{
+ kConfig->setGroup("FilterRules");
+ QString key;
+ key.sprintf("name-%d", number);
+ kConfig->writeEntry(key, convertSpecial(name));
+ key.sprintf("search-%d", number);
+ kConfig->writeEntry(key, convertSpecial(search));
+ key.sprintf("from-%d", number);
+ kConfig->writeEntry(key, convertSpecial(from));
+ key.sprintf("to-%d", number);
+ kConfig->writeEntry(key, convertSpecial(to));
+ // kConfig->sync();
+ updateListBox(after);
+ }
+}
+
+void FilterRuleEditor::updateListBox(int citem )
+{
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ int number = kConfig->readNumEntry("Rules", 0);
+ filter->RuleList->clear();
+
+ for(; number > 0; number--){
+ QString key;
+ key.sprintf("name-%d", number);
+ filter->RuleList->insertItem(kConfig->readEntry(key), 0);
+ }
+ if(filter->RuleList->count() > 0)
+ filter->RuleList->setCurrentItem(citem);
+ filter->RuleList->repaint();
+
+ filter->DeleteButton->setEnabled( filter->RuleList->currentItem() > -1 );
+ filter->ModifyButton->setEnabled( filter->RuleList->currentItem() > -1 );
+ filter->InsertButton->setEnabled( false );
+ filter->NewButton->setEnabled( true );
+}
+
+void FilterRuleEditor::moveRule(int from, int to)
+{
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ QString src;
+ QString dest;
+ src.sprintf("name-%d", from);
+ dest.sprintf("name-%d", to);
+ kConfig->writeEntry(dest, kConfig->readEntry(src));
+ kConfig->deleteEntry(src);
+ src.sprintf("search-%d", from);
+ dest.sprintf("search-%d", to);
+ kConfig->writeEntry(dest, kConfig->readEntry(src));
+ kConfig->deleteEntry(src);
+ src.sprintf("from-%d", from);
+ dest.sprintf("from-%d", to);
+ kConfig->writeEntry(dest, kConfig->readEntry(src));
+ kConfig->deleteEntry(src);
+ src.sprintf("to-%d", from);
+ dest.sprintf("to-%d", to);
+ kConfig->writeEntry(dest, kConfig->readEntry(src));
+ kConfig->deleteEntry(src);
+}
+
+void FilterRuleEditor::deleteRule()
+{
+ int number = filter->RuleList->currentItem();
+
+ if( number >= 0){
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ int max = kConfig->readNumEntry("Rules");
+ for(int i = number+2; i <= max; i++){
+ moveRule(i, i-1);
+ }
+ max--;
+ kConfig->writeEntry("Rules", max);
+
+ updateListBox();
+ }
+}
+
+void FilterRuleEditor::newHighlight(int i)
+{
+ i++;
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ QString key;
+ key.sprintf("name-%d", i);
+ filter->LineTitle->setText(convertSpecialBack(kConfig->readEntry(key)));
+ key.sprintf("search-%d", i);
+ filter->LineSearch->setText(convertSpecialBack(kConfig->readEntry(key)));
+ key.sprintf("from-%d", i);
+ filter->LineFrom->setText(convertSpecialBack(kConfig->readEntry(key)));
+ key.sprintf("to-%d", i);
+ filter->LineTo->setText(convertSpecialBack(kConfig->readEntry(key)));
+}
+
+void FilterRuleEditor::raiseRule()
+{
+ int item = filter->RuleList->currentItem();
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ int max = kConfig->readNumEntry("Rules");
+ if(item > 0){
+ moveRule(item, max+1);
+ moveRule(item+1, item);
+ moveRule(max+1, item+1);
+ updateListBox(item - 1);
+ }
+}
+
+void FilterRuleEditor::lowerRule()
+{
+ int item = filter->RuleList->currentItem();
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ int max = kConfig->readNumEntry("Rules");
+ if(item < max-1){
+ moveRule(item+2, max+1);
+ moveRule(item+1, item+2);
+ moveRule(max+1, item+1);
+ updateListBox(item+1);
+ }
+}
+
+QString FilterRuleEditor::convertSpecial(QString str)
+{
+ str.replace(QRegExp("\\$"), "$$");
+ return str;
+}
+
+QString FilterRuleEditor::convertSpecialBack(QString str)
+{
+ str.replace(QRegExp("\\$\\$"), "$");
+ return str;
+}
+
+#include "FilterRuleEditor.moc"
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
+
diff --git a/ksirc/FilterRuleEditor.dlg b/ksirc/FilterRuleEditor.dlg
new file mode 100644
index 00000000..5461d839
--- /dev/null
+++ b/ksirc/FilterRuleEditor.dlg
@@ -0,0 +1,215 @@
+DlgEdit:v2.0:Dialog:
+Dialog {
+ ClassHeader {FilterRuleEditor.h}
+ ClassSource {FilterRuleEditor.cpp}
+ ClassName {FilterRuleEditor}
+ DataHeader {FilterRuleEditorData.h}
+ DataSource {FilterRuleEditorData.cpp}
+ DataName {FilterRuleEditorData}
+ WindowBaseClass {QDialog}
+ IsModal {FALSE}
+ WindowCaption {Edit Filter Rules}
+ WindowFlags {118784}
+}
+WidgetLayout {
+InitialPos {-1 -1}
+Size {510 250}
+MinSize {0 0}
+MaxSize {32767 32767}
+Grid {10}
+
+Label {
+ Text {Description:}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {140 10 80 30}
+ Name {Label_1}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+LineEdit {
+ Text {}
+ EchoMode {Normal}
+ MaxLength {80}
+ FrameShown {TRUE}
+ Rect {220 10 280 30}
+ Name {LineEdit_1}
+ Variable {LineTitle}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+Label {
+ Text {Match:}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {140 50 70 30}
+ Name {Label_4}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+LineEdit {
+ Text {}
+ EchoMode {Normal}
+ MaxLength {80}
+ FrameShown {TRUE}
+ Rect {220 50 280 30}
+ Name {LineEdit_2}
+ Variable {LineSearch}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+Label {
+ Text {From:}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {140 90 80 30}
+ Name {Label_7}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+Label {
+ Text {To:}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {140 130 70 30}
+ Name {Label_9}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+LineEdit {
+ Text {}
+ EchoMode {Normal}
+ MaxLength {32767}
+ FrameShown {TRUE}
+ Rect {220 90 280 30}
+ Name {LineEdit_3}
+ Variable {LineFrom}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+LineEdit {
+ Text {}
+ EchoMode {Normal}
+ MaxLength {80}
+ FrameShown {TRUE}
+ Rect {220 130 280 30}
+ Name {LineEdit_4}
+ Variable {LineTo}
+ LayoutStatus {NoLayout}
+ MinimumSize {0 0}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {TRUE}
+ Text {&Modify}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {390 170 110 30}
+ Name {PushButton_1}
+ Variable {ApplyButton}
+ Signal {[Protected] clicked --> OkPressed ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {&Close}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {390 210 110 30}
+ Name {PushButton_2}
+ Signal {[Protected] clicked --> closePressed ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {&New Rule}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {270 170 110 30}
+ Name {PushButton_7}
+ Signal {[Protected] clicked --> newRule ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {&Delete}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {150 170 110 30}
+ Name {PushButton_8}
+ Variable {deleteButton}
+ Signal {[Protected] clicked --> deleteRule ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {Up}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {10 210 55 30}
+ Name {PushButton_9}
+ Signal {[Protected] clicked --> raiseRule ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {Down}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {75 210 55 30}
+ Name {PushButton_10}
+ Signal {[Protected] clicked --> lowerRule ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+ListBox {
+ DragSelect {FALSE}
+ AutoScroll {FALSE}
+ ScrollBar {FALSE}
+ AutoScrollBar {TRUE}
+ BottomScrollBar {FALSE}
+ AutoBottomScrollBar {FALSE}
+ SmoothScrolling {FALSE}
+ MultiSelection {FALSE}
+ Style {51}
+ LineWidth {2}
+ Rect {10 10 120 190}
+ Name {ListBox_2}
+ Variable {RuleList}
+ Signal {[Protected] highlighted --> newHighlight (int)}
+ Signal {[Protected] selected --> newHighlight (int)}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+}
diff --git a/ksirc/FilterRuleEditor.h b/ksirc/FilterRuleEditor.h
new file mode 100644
index 00000000..c9cf0437
--- /dev/null
+++ b/ksirc/FilterRuleEditor.h
@@ -0,0 +1,53 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: FilterRuleEditor.h
+ Last generated: Mon Dec 15 18:14:27 1997
+
+ *********************************************************************/
+
+#ifndef FilterRuleEditor_h
+#define FilterRuleEditor_h
+
+#include <qstring.h>
+
+#include <kdialogbase.h>
+
+#include "FilterRuleWidget.h"
+
+class FilterRuleEditor : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ FilterRuleEditor
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~FilterRuleEditor();
+
+protected slots:
+
+ virtual void newRule();
+ virtual void OkPressed();
+ virtual void deleteRule();
+ virtual void newHighlight(int);
+ virtual void raiseRule();
+ virtual void lowerRule();
+
+protected:
+ void updateListBox(int citem = 0);
+ void moveRule(int, int);
+ QString convertSpecial(QString);
+ QString convertSpecialBack(QString);
+
+ FilterRuleWidget *filter;
+};
+#endif // FilterRuleEditor_included
+
+// vim:tabstop=2:shiftwidth=2:expandtab:cinoptions=(s,U1,m1
+
diff --git a/ksirc/FilterRuleWidget.ui b/ksirc/FilterRuleWidget.ui
new file mode 100644
index 00000000..2ac7f6d4
--- /dev/null
+++ b/ksirc/FilterRuleWidget.ui
@@ -0,0 +1,239 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>FilterRuleWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>FilterRuleWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>541</width>
+ <height>229</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>Layout9</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>InsertButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Insert</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="0">
+ <property name="name">
+ <cstring>DeleteButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>NewButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="0" column="1">
+ <property name="name">
+ <cstring>ModifyButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Modify</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="2" colspan="1">
+ <property name="name">
+ <cstring>Layout3</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>DownButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>UpButton</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>4</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QListBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>RuleList</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>Spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Preferred</enum>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="0" column="1">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Details</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>LineTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>D&amp;escription:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>LineTitle</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>TextLabel4</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;To:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>LineTo</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>LineTo</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>LineSearch</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>LineFrom</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>M&amp;atch:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>LineSearch</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;From:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>LineFrom</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSOpenkSirc/Makefile.am b/ksirc/KSOpenkSirc/Makefile.am
new file mode 100644
index 00000000..3af4d5f2
--- /dev/null
+++ b/ksirc/KSOpenkSirc/Makefile.am
@@ -0,0 +1,26 @@
+KDE_CXXFLAGS = $(USE_RTTI) $(USE_EXCEPTIONS) -UQT_NO_ASCII_CAST
+
+# set the include path for X, qt and KDE
+INCLUDES= $(all_includes)
+
+####### This part is very pws specific
+# you can add here more. This one gets installed
+noinst_LTLIBRARIES = libksopenksirc.la
+
+# Which sources should be compiled for dialog
+
+libksopenksirc_la_SOURCES = \
+ open_ksirc.cpp\
+ open_ksircData.ui\
+ serverFileParser.cpp\
+ enter_combo.cpp
+
+# this option you can leave out. Just, if you use "make dist", you need it
+noinst_HEADERS = open_ksirc.h open_ksircData.h serverDataType.h serverFileParser.h enter_combo.h
+
+# just to make sure, automake makes them
+METASOURCES = AUTO
+
+misc_DATA = servers.txt servers.ini
+miscdir = $(kde_datadir)/ksirc
+
diff --git a/ksirc/KSOpenkSirc/convert-mIRC b/ksirc/KSOpenkSirc/convert-mIRC
new file mode 100755
index 00000000..85550dd3
--- /dev/null
+++ b/ksirc/KSOpenkSirc/convert-mIRC
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+
+while ($line = <STDIN>) {
+ if ($line =~ /.*=(Random .*)SERVER:(.*):(.*)GROUP:.*/) {
+ $service = 'Random';
+ $servername = $1;
+ $server = $2;
+ $ports = $3;
+ print "$service\:$servername\:$server\:$ports\:\n";
+ }
+ elsif ($line =~ /.*=(.*): (.*)SERVER:(.*):(.*)GROUP:.*/) {
+ $service = $1;
+ $servername = $2;
+ $server = $3;
+ $ports = $4;
+
+ print "$service\:$servername\:$server\:$ports\:\n";
+ }
+}
diff --git a/ksirc/KSOpenkSirc/enter_combo.cpp b/ksirc/KSOpenkSirc/enter_combo.cpp
new file mode 100644
index 00000000..3394423d
--- /dev/null
+++ b/ksirc/KSOpenkSirc/enter_combo.cpp
@@ -0,0 +1,10 @@
+#include "enter_combo.h"
+
+void EnterCombo::keyPressEvent( QKeyEvent *e ){
+ if(e->key() == Key_Return || e->key() == Key_Enter)
+ emit(enterPressed());
+ else
+ QComboBox::keyPressEvent(e);
+}
+
+#include "enter_combo.moc"
diff --git a/ksirc/KSOpenkSirc/enter_combo.h b/ksirc/KSOpenkSirc/enter_combo.h
new file mode 100644
index 00000000..f0752a2d
--- /dev/null
+++ b/ksirc/KSOpenkSirc/enter_combo.h
@@ -0,0 +1,37 @@
+#ifndef ENTER_COMBO_H
+#define ENTER_COMBO_H
+
+#include <qcombobox.h>
+#include <qevent.h>
+#include <qkeycode.h>
+#include <qlineedit.h>
+
+#undef KeyPress // X headers...
+
+class EnterCombo : public QComboBox {
+ Q_OBJECT
+public:
+ EnterCombo ( QWidget * parent=0, const char * name=0 )
+ : QComboBox(TRUE, parent, name)
+ {
+ }
+ EnterCombo ( bool rw, QWidget * parent=0, const char * name=0 )
+ : QComboBox(rw, parent, name)
+ {
+ QKeyEvent ke(QEvent::KeyPress, SHIFT|Key_Home, 0, 0);
+ keyPressEvent(&ke);
+ }
+
+ virtual void show(){
+ QComboBox::show();
+ lineEdit()->selectAll();
+ }
+
+signals:
+ void enterPressed();
+
+protected:
+ virtual void keyPressEvent( QKeyEvent *e );
+};
+
+#endif
diff --git a/ksirc/KSOpenkSirc/open_ksirc.cpp b/ksirc/KSOpenkSirc/open_ksirc.cpp
new file mode 100644
index 00000000..5a4a4799
--- /dev/null
+++ b/ksirc/KSOpenkSirc/open_ksirc.cpp
@@ -0,0 +1,377 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: open_ksirc.cpp
+ Last generated: Wed Jul 29 16:41:26 1998
+
+ *********************************************************************/
+
+#include "open_ksirc.h"
+#include "serverDataType.h"
+#include "serverFileParser.h"
+#include "enter_combo.h"
+#include "../ksircserver.h"
+#include <qlistbox.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qdict.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kmessagebox.h>
+#include <kmdcodec.h>
+
+QPtrList<Server> Groups;
+
+#undef Inherited
+#define Inherited open_ksircData
+
+open_ksirc::open_ksirc
+(
+ QWidget* parent,
+ const char* name
+)
+ :
+ Inherited( parent, name, true )
+{
+ setCaption( i18n("Connect to Server") );
+
+ // check for existance of ~/.kde/share/apps/ksirc/servers.txt
+ // if it don't exist use $KDEDIR/share/apps/ksirc/servers.txt
+ // changes are written to ~/.kde/share/apps/ksirc/servers.txt
+
+ QString filename = locate("appdata", "servers.txt");
+ serverFileParser::readDatafile( filename );
+
+ Groups.setAutoDelete(TRUE);
+
+ // TODO add "Recent" to global listing servers here..
+ // Now we read in the Recent group from the config file
+ // remove all recent servers first
+
+ for(Server *s = Groups.first(); s != 0x0; s = Groups.next()){
+ if(s->group() == i18n("Recent")){
+ Groups.remove();
+ }
+ }
+
+ // Add current ones
+ KConfig *conf = kapp->config();
+ conf->setGroup("ServerList");
+ CheckB_StorePassword->setChecked( conf->readBoolEntry("StorePasswords") );
+ QStringList recent = conf->readListEntry("RecentServers");
+ for(QStringList::ConstIterator it = recent.begin(); it != recent.end(); ++it){
+ if(conf->hasGroup("RecentServer-" + *it)){
+ conf->setGroup("RecentServer-" + *it);
+ QPtrList<port> rp;
+ rp.inSort(new port(conf->readEntry("Port", "6667")));
+ QString password = decryptPassword(conf->readEntry("Password"));
+ bool ssl = conf->readBoolEntry("SSL");
+ Groups.insert(0, new Server(i18n("Recent"), *it, rp,
+ i18n("Recent Server"), "", password,
+ ssl));
+ }
+ else {
+ QStringList info = QStringList::split(":", *it);
+ if (info.isEmpty())
+ continue;
+ QString name = info[0];
+ QPtrList<port> rp;
+ if (info.count() > 1)
+ rp.inSort(new port(info[1]));
+ else
+ rp.inSort(new port("6667"));
+ QString password;
+ if (info.count() > 2)
+ password = decryptPassword(info[2]);
+
+ conf->setGroup("RecentServer-" + name);
+ conf->writeEntry("Port", rp.first()->portnum());
+ conf->writeEntry("Password", encryptPassword(password));
+ conf->writeEntry("SSL", false);
+
+ Groups.insert(0, new Server(i18n("Recent"), name, rp,
+ i18n("Recent Server"), "", password));
+ }
+ }
+
+ ComboB_ServerName->setAutoCompletion( TRUE );
+ ComboB_ServerPort->setAutoCompletion( TRUE );
+
+ insertGroupList();
+
+ QString blah = i18n("Recent");
+ setGroup(blah);
+
+ connect(ComboB_ServerGroup, SIGNAL(activated( const QString& )),
+ this, SLOT(setGroup( const QString& )));
+ connect(ComboB_ServerName, SIGNAL(activated( const QString& )),
+ this, SLOT(setServer( const QString& )));
+
+ connect(PB_Connect, SIGNAL(clicked()), this, SLOT(clickConnect()));
+ connect(PB_Edit, SIGNAL(clicked()), this, SLOT(clickEdit()));
+ connect(PB_Cancel, SIGNAL(clicked()), this, SLOT(clickCancel()));
+
+ PB_Connect->setDefault(TRUE);
+ PB_Connect->setAutoDefault(TRUE);
+ PB_Edit->setEnabled(false); // Not yet implemented.
+
+ ComboB_ServerName->setFocus();
+ connect(ComboB_ServerName, SIGNAL(enterPressed()), this, SLOT(clickConnect()));
+}
+
+// insert a sorted list of groups into ComboB_ServerGroup, note that
+// we want to get the recent servers in first so we insert "Recent"
+// in first, then we want Random.
+
+void open_ksirc::insertGroupList()
+{
+ QStrList tempgroups;
+ Server *serv;
+
+ for ( serv=Groups.first(); serv != 0; serv=Groups.next() ) {
+ if (tempgroups.find(serv->group()) == -1)
+ tempgroups.inSort( serv->group() );
+ }
+
+ ComboB_ServerGroup->insertItem(i18n( "Recent") );
+ ComboB_ServerGroup->insertItem(i18n( "Random") );
+ for (const char* t = tempgroups.first(); t; t = tempgroups.next()) {
+ ComboB_ServerGroup->insertItem( t );
+ }
+}
+
+// insert a sorted list of servers from the group passed as an arg
+// into ComboB_ServerName, if a list is already there delete it.
+// note this does not check for multiple occurrances of the same server
+
+void open_ksirc::insertServerList( const char * group )
+{
+ QListBox *newListBox = new QListBox();
+ Server *serv;
+
+ for ( serv=Groups.first(); serv != 0; serv=Groups.next() ) {
+ if ( !qstrcmp(serv->group(), group) ) {
+ newListBox->insertItem( serv->server(), 0 );
+ }
+ }
+
+ ComboB_ServerName->setListBox(newListBox);
+// ComboB_ServerName->setCurrentItem( 0 ); // this don't work =(
+ if (newListBox->count() > 0)
+ ComboB_ServerName->setEditText( newListBox->text( 0 ) );
+}
+
+// insert a sorted list of ports from the server passed as an arg
+// into ComboB_ServerPort, if a list is already there delete it.
+// note that this only takes the first occurrance if there is two
+// entiies with the same server.
+
+void open_ksirc::setServer( const QString &serveraddress )
+{
+ QListBox *newListBox = new QListBox();
+ Server *serv;
+ QPtrList<port> portlist;
+ port *p;
+ bool defaultport = FALSE;
+
+ for ( serv=Groups.first(); serv != 0; serv=Groups.next() ) {
+ if (serv->server() == serveraddress) {
+ setServerDesc( serv->serverdesc() );
+ portlist = serv->ports();
+ for ( p=portlist.last(); p != 0; p=portlist.prev() ) {
+ newListBox->insertItem( p->portnum() );
+ if (strcmp(p->portnum(), "6667") == 0)
+ defaultport = TRUE;
+ }
+ LineE_Password->setText( serv->password() );
+ CheckB_StorePassword->setEnabled( !serv->password().isEmpty() );
+ CheckB_UseSSL->setChecked(serv->usessl());
+ break;
+ }
+ }
+ ComboB_ServerPort->setListBox(newListBox);
+// ComboB_ServerPort->setCurrentItem( 0 ); // doesn't work
+ if (defaultport) {
+ ComboB_ServerPort->setEditText("6667");
+ } else {
+ if (newListBox->count() > 0)
+ ComboB_ServerPort->setEditText( newListBox->text( 0 ) );
+ }
+}
+
+// Sets the server description if the isn't one set it to "Not Available"
+
+void open_ksirc::setServerDesc( QString description )
+{
+ if (description.isNull() || description.isEmpty()) {
+ Label_ServerDesc->setText( i18n("Not available"));
+ } else {
+ Label_ServerDesc->setText( description );
+ }
+}
+
+// This has got nothing to do with real encryption, it just scrambles
+// the password a little bit for saving it into the config file.
+// A random string of the same length as the password in UTF-8 is generated
+// and then each byte of the UTF-8 password is xored with the corresponding
+// byte of the random string. The returned value is a base64 encoding of
+// that random string followed by the scrambled password.
+QString open_ksirc::encryptPassword( const QString &password )
+{
+ QCString utf8 = password.utf8();
+ // Can contain NULL bytes after XORing
+ unsigned int utf8Length(utf8.length());
+ QByteArray result(utf8Length << 1);
+ memcpy(result.data(), kapp->randomString(utf8Length).latin1(), utf8Length);
+ for (unsigned int i = 0; i < utf8Length; ++i)
+ result[i + utf8Length] = utf8[i] ^ result[i];
+ return QString::fromLatin1(KCodecs::base64Encode(result));
+}
+
+QString open_ksirc::decryptPassword( const QString &scrambled )
+{
+ QByteArray base64, orig;
+ base64.duplicate(scrambled.latin1(), scrambled.length());
+ KCodecs::base64Decode(base64, orig);
+ QCString result;
+ for (unsigned int i = 0; i < (orig.size() >> 1); ++i)
+ result += orig[i] ^ orig[i + (orig.size() >> 1)];
+ return QString::fromUtf8(result);
+}
+
+void open_ksirc::setGroup( const QString &group )
+{
+ insertServerList( group );
+ if (ComboB_ServerName->count() > 0) {
+ QString blah = QString(ComboB_ServerName->text( 0 ));
+ setServer(blah);
+ } else {
+ setServerDesc( "" );
+ ComboB_ServerPort->setEditText("6667");
+ ComboB_ServerPort->insertItem("6667");
+ }
+ if(ComboB_ServerPort->currentText() == 0x0){
+ ComboB_ServerPort->setEditText("6667");
+ ComboB_ServerPort->insertItem("6667");
+ }
+}
+
+void open_ksirc::clickConnect()
+{
+ if ( ComboB_ServerName->currentText().isEmpty() )
+ {
+ KMessageBox::information( this, i18n( "Please enter a server name." ) );
+ return;
+ }
+
+ QString server;
+ QString port;
+ QString script;
+ Server *serv;
+ KConfig *conf = kapp->config();
+
+ hide();
+
+ server = ComboB_ServerName->currentText();
+ port = ComboB_ServerPort->currentText();
+
+ for ( serv=Groups.first(); serv != 0; serv=Groups.next() ) {
+ if (strcmp(serv->server(), server) == 0) {
+ script = serv->script();
+ }
+ break;
+ }
+
+ if(server.length() == 0)
+ reject();
+
+ if(port.isEmpty())
+ port = "6667";
+
+ QString plain, scrambled;
+ if (!LineE_Password->text().isEmpty())
+ {
+ plain = LineE_Password->text();
+ if (CheckB_StorePassword->isChecked())
+ scrambled = encryptPassword(LineE_Password->text());
+ }
+
+ conf->setGroup("ServerList");
+ conf->writeEntry("StorePasswords", CheckB_StorePassword->isChecked());
+ QStringList recent = conf->readListEntry("RecentServers");
+ if(recent.contains(server)){
+ QStringList::Iterator it = recent.find(server);
+ recent.remove(it);
+ }
+
+ /*
+ * This is legacy code only
+ */
+ //str is now "server:port"
+ //plain is now "server:port" or "server:port:pass" if a password was entered
+ //scrambled is now "server:port" or "server:port:scrambledpass" if
+ //a password was given and "store password" is checked
+
+ for (QStringList::Iterator it = recent.begin(); it != recent.end(); ) {
+ if ((*it).startsWith(server)) // ignore password
+ {
+ QStringList::Iterator del = it++;
+ recent.remove(del);
+ }
+ else
+ ++it;
+ }
+
+ recent.prepend(server);
+ conf->writeEntry("RecentServers", recent);
+
+ conf->setGroup("RecentServer-" + server);
+ conf->writeEntry("Port", port);
+ conf->writeEntry("Password", scrambled);
+ conf->writeEntry("SSL", CheckB_UseSSL->isChecked());
+
+ conf->sync();
+
+ KSircServer kss(server,
+ port,
+ script,
+ plain,
+ CheckB_UseSSL->isChecked());
+
+ // emit open_ksircprocess( server, port, script );
+ emit open_ksircprocess(kss);
+
+ accept();
+}
+
+void open_ksirc::clickCancel()
+{
+ reject();
+}
+
+void open_ksirc::clickEdit()
+{
+ // TODO open new server editor
+}
+
+void open_ksirc::passwordChanged( const QString& password )
+{
+ CheckB_StorePassword->setEnabled( !password.isEmpty() );
+}
+
+open_ksirc::~open_ksirc()
+{
+ Groups.clear();
+}
+
+#include "open_ksirc.moc"
+
+// vim: ts=2 sw=2 et
diff --git a/ksirc/KSOpenkSirc/open_ksirc.h b/ksirc/KSOpenkSirc/open_ksirc.h
new file mode 100644
index 00000000..81145fca
--- /dev/null
+++ b/ksirc/KSOpenkSirc/open_ksirc.h
@@ -0,0 +1,53 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: open_ksirc.h
+ Last generated: Wed Jul 29 16:41:26 1998
+
+ *********************************************************************/
+
+#ifndef open_ksirc_included
+#define open_ksirc_included
+
+#include "open_ksircData.h"
+
+#include <qdict.h>
+
+class KSircServer;
+
+class open_ksirc : public open_ksircData
+{
+ Q_OBJECT
+
+public:
+
+ open_ksirc
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~open_ksirc();
+
+protected slots:
+ void setGroup( const QString& );
+ void setServer( const QString& );
+ void clickConnect();
+ void clickCancel();
+ void clickEdit();
+ void passwordChanged( const QString& );
+
+private:
+ void insertGroupList();
+ void insertServerList( const char * );
+ void setServerDesc( QString );
+ QString encryptPassword( const QString & );
+ QString decryptPassword( const QString & );
+
+signals:
+// void open_ksircprocess( const char *, int, const char * );
+ void open_ksircprocess( KSircServer & );
+
+};
+#endif // open_ksirc_included
diff --git a/ksirc/KSOpenkSirc/open_ksircData.ui b/ksirc/KSOpenkSirc/open_ksircData.ui
new file mode 100644
index 00000000..ae97f341
--- /dev/null
+++ b/ksirc/KSOpenkSirc/open_ksircData.ui
@@ -0,0 +1,337 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>open_ksircData</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>593</width>
+ <height>196</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Server/Quick connect to:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ComboB_ServerName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="4">
+ <property name="name">
+ <cstring>TextLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ComboB_ServerPort</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ComboB_ServerGroup</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Choose a server for an IRC Network</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Usually IRC Servers are connected to a net (IRCNet, Freenode, etc.). Here, you can select the closest server for your favorite network.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>TextLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Group:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ComboB_ServerGroup</cstring>
+ </property>
+ </widget>
+ <widget class="EnterCombo" row="1" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>ComboB_ServerName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="focusPolicy">
+ <enum>StrongFocus</enum>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enter/choose a server to connect to</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If you selected an IRC Network in &lt;i&gt;"Group"&lt;/i&gt;, this window shows all of its servers. If you did not choose a group, you can enter your own here or select one of the recently used ones (&lt;i&gt;"Quick Connect"&lt;/i&gt;).</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="1" column="4">
+ <property name="name">
+ <cstring>ComboB_ServerPort</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Choose a server port</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Using &lt;i&gt;"6667"&lt;/i&gt; or &lt;i&gt;"6666"&lt;/i&gt; here is safe in most cases. Only use other values if you have been told so.</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>GroupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Server Description</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This is the description of the server currently selected</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>Label_ServerDesc</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop|AlignLeft</set>
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>GroupBox1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Server Access</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>TextLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>Pass&amp;word:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>LineE_Password</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="0" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>LineE_Password</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="echoMode">
+ <enum>Password</enum>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>CheckB_UseSSL</cstring>
+ </property>
+ <property name="text">
+ <string>Use SS&amp;L</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This will use a secure connection to the server. This must be supported by the server.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="2">
+ <property name="name">
+ <cstring>CheckB_StorePassword</cstring>
+ </property>
+ <property name="text">
+ <string>S&amp;tore password</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This will cause your server password to be stored on your disk.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QPushButton" row="3" column="4">
+ <property name="name">
+ <cstring>PB_Cancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Cancel Connect</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Cancel Connect</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="3">
+ <property name="name">
+ <cstring>PB_Connect</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>C&amp;onnect</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Connect to the selected server</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Connect to the server given in &lt;i&gt;"Server / Quick Connect to:"&lt;/i&gt; on the port given in &lt;i&gt;"Port:"&lt;/i&gt;.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>PB_Edit</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Edit Servers</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="3" column="0">
+ <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>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>EnterCombo</class>
+ <header location="local">enter_combo.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>1</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="646">789c6dd2c10ac2300c00d07bbf2234b7229d1be245fc04c5a3201e4615f430059d0711ff5ddb2e6bb236ec90eed134cb5a19d8ef36602af5ecdbfeeac05dda0798d3abebde87e3faa374d3807fa0d633a52d38d8de6f679fe33fc776e196f53cd010188256a3600a292882096246517815ca99884606e18044a3a40d91824820924265a7923a2e8bcd05f33db1173e002913175f2a6be6d3294871a2d95fa00e8a94ee017b69d339d90df1e77c57ea072ede6758</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>LineE_Password</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>Form1</receiver>
+ <slot>passwordChanged(const QString&amp;)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>ComboB_ServerGroup</tabstop>
+ <tabstop>ComboB_ServerName</tabstop>
+ <tabstop>ComboB_ServerPort</tabstop>
+ <tabstop>LineE_Password</tabstop>
+ <tabstop>CheckB_StorePassword</tabstop>
+ <tabstop>PB_Edit</tabstop>
+ <tabstop>PB_Connect</tabstop>
+ <tabstop>PB_Cancel</tabstop>
+</tabstops>
+<slots>
+ <slot access="protected">passwordChanged(const QString &amp;)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSOpenkSirc/serverDataType.h b/ksirc/KSOpenkSirc/serverDataType.h
new file mode 100644
index 00000000..80b94e27
--- /dev/null
+++ b/ksirc/KSOpenkSirc/serverDataType.h
@@ -0,0 +1,56 @@
+#ifndef SERVERDATATYPE_H
+#define SERVERDATATYPE_H
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+class port
+{
+
+public:
+ port( const QString &portnum ) { p=portnum; }
+ QString portnum() const { return p; }
+
+private:
+ QString p;
+
+};
+
+
+class Server
+{
+
+public:
+ Server( const QString &group,
+ const QString &server,
+ QPtrList<port> ports,
+ const QString &serverdesc,
+ const QString &script,
+ const QString &password = QString::null,
+ bool dossl = false
+ ) {
+ g=group; s=server; p=ports; sd=serverdesc; sc=script;
+ pass=password;
+ ssl = dossl;
+ p.setAutoDelete(TRUE);
+ }
+ QString group() const { return g; }
+ QString server() const { return s; }
+ QPtrList<port> ports() const { return p; }
+ QString serverdesc() const { return sd; }
+ QString script() const { return sc; }
+ QString password() const { return pass; }
+ bool usessl() const { return ssl; }
+
+private:
+ QString g;
+ QString s;
+ QPtrList<port> p;
+ QString sd;
+ QString sc;
+ QString pass;
+ bool ssl;
+
+};
+
+#endif
diff --git a/ksirc/KSOpenkSirc/serverFileParser.cpp b/ksirc/KSOpenkSirc/serverFileParser.cpp
new file mode 100644
index 00000000..8e6ba80e
--- /dev/null
+++ b/ksirc/KSOpenkSirc/serverFileParser.cpp
@@ -0,0 +1,114 @@
+#include "serverFileParser.h"
+#include "serverDataType.h"
+#include <qfile.h>
+#include <stdlib.h>
+
+#include <kstandarddirs.h>
+#include <kdebug.h>
+
+extern QPtrList<Server> Groups;
+
+// Opens, reads and parses server information from a server file,
+// sets the information in the global variable and returns 1 if
+// sucessful, takes a filename as an argument.
+
+int serverFileParser::readDatafile( const char *fileName )
+{
+ Groups.setAutoDelete( TRUE );
+ Groups.clear();
+ QFile serverFile( fileName );
+ if ( !serverFile.open( IO_ReadOnly ) )
+ return -1;
+
+ QTextStream fileStream(&serverFile);
+
+ // the file is layed out as follows:
+ // service:servername:serveraddress:ports:script:
+ // so we parse it this way
+
+ while( !fileStream.eof() ) {
+ QString str = fileStream.readLine();
+ const char *strC = str.ascii();
+ char *token;
+ char groupC[1024], servernameC[1024], serveraddressC[1024], portsC[1024];
+ int pos = 0;
+ QString group;
+ QString servername;
+ QString serveraddress;
+ QPtrList<port> ports;
+ QString script;
+
+ QString buf;
+ QString portbuff;
+
+ pos = sscanf(strC, "%1023[^:]:%1024[^:]:%1023[^:]:%1023[^:]:", groupC, servernameC, serveraddressC, portsC);
+ if(pos != 4){
+ kdWarning() << "Failed to parse servers.txt on line: " << strC << ". Invalid format" << endl;
+ return 0;
+ }
+ group = groupC;
+ servername = servernameC;
+ serveraddress = serveraddressC;
+ token = strtok(portsC, ",");
+ while(token != NULL){
+ ports.inSort(new port(token));
+ token = strtok(NULL, ",");
+ }
+
+ /*
+ for( uint loc = 0; loc <= str.length(); loc++ ) {
+ if ( str[loc] == ':' || loc == str.length()) {
+ switch(pos) {
+ case 0: // service
+ group = buf.copy();
+ break;
+ case 1: // server name
+ servername = buf.copy();
+ break;
+ case 2: // server address
+ serveraddress = buf.copy();
+ break;
+ case 3: // port listing
+ for ( uint portloc = 0; portloc <= buf.length(); portloc++ ) {
+ if (buf[portloc] == ',' || portloc == buf.length()) {
+ if (!portbuff.isEmpty())
+ ports.inSort( new port(portbuff));
+ portbuff.truncate( 0 );
+ } else {
+ portbuff += buf[portloc];
+ }
+ }
+ break;
+ default: // script
+ script = buf.copy();
+ }
+ pos++;
+ buf.truncate( 0 );
+ portbuff.truncate( 0 );
+ } else {
+ buf += str[loc];
+ }
+ } // for loop
+ */
+
+
+ Groups.inSort( new Server(group, serveraddress, ports,
+ servername, script) );
+
+ } // while loop
+
+ serverFile.close();
+ return 1;
+
+}
+
+// Writes the data stored in the global variable to the server file,
+// returns 1 if sucessful, takes a filename as an argument, uhh NOT.
+// we want to write it ~/.kde/share/apps/ksirc/servers.txt
+//
+
+int writeDataFile()
+{
+ QString filename(KGlobal::dirs()->saveLocation("appdata")+"servers.txt");
+ return 1;
+}
diff --git a/ksirc/KSOpenkSirc/serverFileParser.h b/ksirc/KSOpenkSirc/serverFileParser.h
new file mode 100644
index 00000000..f3fd67e1
--- /dev/null
+++ b/ksirc/KSOpenkSirc/serverFileParser.h
@@ -0,0 +1,13 @@
+#ifndef serverFileParser_included
+#define serverFileParser_included
+
+#include <qobject.h>
+
+class serverFileParser
+{
+ public:
+ static int readDatafile( const char *fileName );
+ static int writeDataFile();
+};
+
+#endif
diff --git a/ksirc/KSOpenkSirc/servers.ini b/ksirc/KSOpenkSirc/servers.ini
new file mode 100644
index 00000000..8f3de1ee
--- /dev/null
+++ b/ksirc/KSOpenkSirc/servers.ini
@@ -0,0 +1,362 @@
+; Please report errors and broken servers to servers@mirc.com
+; June 12th, 2001
+
+[servers]
+n0=Random US DALnet serverSERVER:irc.dal.net:6662,6663,6664GROUP:01
+n1=Random EU DALnet serverSERVER:irc.eu.dal.net:6667GROUP:02
+n2=Random US EFnet serverSERVER:us.rr.efnet.net:6667GROUP:03
+n3=Random EU EFnet serverSERVER:eu.rr.efnet.net:6667GROUP:04
+n4=Random CA EFnet serverSERVER:ca.rr.efnet.net:6667GROUP:05
+n5=Random AU EFnet serverSERVER:au.rr.efnet.net:6667GROUP:06
+n6=Random US IRCnet serverSERVER:us.ircnet.org:6665,6666,6668GROUP:07
+n7=Random EU IRCnet serverSERVER:eu.ircnet.org:6665,6666,6668GROUP:08
+n8=Random AU IRCnet serverSERVER:au.ircnet.org:6667GROUP:09
+n9=Random US Undernet serverSERVER:us.undernet.org:6667GROUP:10
+n10=Random EU Undernet serverSERVER:eu.undernet.org:6667GROUP:11
+n11=Accessirc: Random serverSERVER:irc.accessirc.net:6667GROUP:Accessirc
+n12=Acestar: Random serverSERVER:irc.acestar.org:6667GROUP:Acestar
+n13=Action-IRC: Random serverSERVER:irc.action-irc.net:6660,6662,6664GROUP:Action-IRC
+n14=Afternet: Random serverSERVER:irc.afternet.org:6667GROUP:Afternet
+n15=Alternativenet: Random serverSERVER:irc.altnet.org:6667GROUP:Alternativenet
+n16=AnotherNet: Random serverSERVER:irc.another.net:6667,7000GROUP:Anothernet
+n17=ArabChat: Random serverSERVER:irc.arabchat.org:6660,6661,6662GROUP:ArabChat
+n18=AstroLink: Random serverSERVER:irc.astrolink.org:6660,6662,6663GROUP:AstroLink
+n19=Asylumnet: Random serverSERVER:irc.asylum-net.org:6661,6664,7000GROUP:Asylumnet
+n20=Austnet: Random AU serverSERVER:au.austnet.org:6667GROUP:Austnet
+n21=Austnet: Random NZ serverSERVER:nz.austnet.org:6667GROUP:Austnet
+n22=Austnet: Random SG serverSERVER:sg.austnet.org:6667GROUP:Austnet
+n23=Austnet: Random US serverSERVER:us.austnet.org:6667GROUP:Austnet
+n24=AwesomeChat: Random serverSERVER:irc.awesomechat.net:6662,6665,6666GROUP:AwesomeChat
+n25=Axenet: Random serverSERVER:irc.axenet.org:6661,6664,6665GROUP:Axenet
+n26=BeyondIRC: Random serverSERVER:irc.beyondirc.net:6660,6662,6664GROUP:Beyondirc
+n27=Blabbernet: Random serverSERVER:irc.blabber.net:6667,7000GROUP:Blabbernet
+n28=Blitzed: Random serverSERVER:irc.blitzed.org:6667,7000GROUP:Blitzed
+n29=Brasirc: Random serverSERVER:irc.brasirc.net:6666,6667GROUP:Brasirc
+n30=Brasirc: BR, PA, BelemSERVER:irc.libnet.com.br:6666,6668,7777GROUP:Brasirc
+n31=Brasirc: BR, SC, FlorianopolisSERVER:irc.globalite.com.br:6667GROUP:Brasirc
+n32=Brasnet: Random European serverSERVER:eu.brasnet.org:6665,6666,6668GROUP:Brasnet
+n33=Brasnet: Random serverSERVER:irc.brasnet.org:6665,6666,6668GROUP:Brasnet
+n34=Brasnet: Random US serverSERVER:us.brasnet.org:6665,6666,6668GROUP:Brasnet
+n35=Bulgaria: Random serverSERVER:irc.bulgaria.org:6666,6668,6669GROUP:Bulgaria
+n36=CCnet: Random serverSERVER:irc.cchat.net:6667,7000GROUP:CCnet
+n37=CCnet: US, TX, DallasSERVER:irc2.cchat.net:6667,7000GROUP:CCnet
+n38=ChatArea: Random serverSERVER:irc.chatarea.net:6667GROUP:ChatArea
+n39=Chatcafe: Random serverSERVER:irc.chatcafe.net:6667GROUP:Chatcafe
+n40=ChatCentral2: Random serverSERVER:irc.cc2.org:6660,6661,6663GROUP:ChatCentral2
+n41=ChatCircuit: Random serverSERVER:irc.chatcircuit.com:6668GROUP:ChatCircuit
+n42=Chatlink: Random serverSERVER:irc.chatlink.org:6667GROUP:Chatlink
+n43=Chatnet: Random AU serverSERVER:au.chatnet.org:6667GROUP:Chatnet
+n44=Chatnet: Random EU serverSERVER:eu.chatnet.org:6667GROUP:Chatnet
+n45=Chatnet: Random US serverSERVER:us.chatnet.org:6667GROUP:Chatnet
+n46=Chatpinoy: Random serverSERVER:irc.chatpinoy.com:6667GROUP:Chatpinoy
+n47=ChatPR: Random serverSERVER:irc.chatpr.org:6667GROUP:ChatPR
+n48=Chatroom: Random serverSERVER:irc.chatroom.org:6667GROUP:Chatroom
+n49=Chatsolutions: Random serverSERVER:irc.chatsolutions.org:6667GROUP:Chatsolutions
+n50=Chatster: Random serverSERVER:irc.chatster.org:6667GROUP:Chatster
+n51=ChatX: Random serverSERVER:irc.chatx.net:6667GROUP:ChatX
+n52=CNN: CNN News discussionsSERVER:chat.cnn.com:6668,6669,7000GROUP:CNN
+n53=Coolchat: Random serverSERVER:irc.coolchat.net:6667GROUP:Coolchat
+n54=Criten: Random serverSERVER:irc.criten.net:6667GROUP:Criten
+n55=Cyberchat: Random serverSERVER:irc.cyberchat.org:6667,6668GROUP:Cyberchat
+n56=CyGanet: Random serverSERVER:irc.cyga.net:6667GROUP:CyGanet
+n57=DALnet: AS, MY, Kuala LumpurSERVER:coins.dal.net:6663,6664,6668GROUP:DALnet
+n58=DALnet: AU, AdelaideSERVER:ozbytes.dal.net:6667,7000GROUP:DALnet
+n59=DALnet: CA, BC, VancouverSERVER:vancouver.dal.net:6661,6662,6666GROUP:DALnet
+n60=DALnet: EU, DE, FrankfurtSERVER:nexgo.de.eu.dal.net:6664,6665,6669GROUP:DALnet
+n61=DALnet: EU, NO, OsloSERVER:powertech.no.eu.dal.net:6666,6667,7000GROUP:DALnet
+n62=DALnet: EU, NO, TromsoSERVER:viking.dal.net:6666,6668,6669GROUP:DALnet
+n63=DALnet: EU, SE, GoteborgSERVER:ced.dal.net:6667,7000GROUP:DALnet
+n64=DALnet: EU, SE, StockholmSERVER:paranoia.dal.net:6661,6662,6669GROUP:DALnet
+n65=DALnet: EU, UK, LondonSERVER:defiant.dal.net:6668,6669,7001GROUP:DALnet
+n66=DALnet: US, FL, HollywoodSERVER:sodre.dal.net:6663,6666,6668GROUP:DALnet
+n67=DALnet: US, IN, HebronSERVER:hebron.dal.net:6661,6664,6669GROUP:DALnet
+n68=DALnet: US, MA, NorwoodSERVER:twisted.dal.net:6663,6665,6669GROUP:DALnet
+n69=DALnet: US, MD, ManchesterSERVER:qis.md.us.dal.net:6660,6664,6668GROUP:DALnet
+n70=DALnet: US, NY, New York CitySERVER:liberty.dal.net:6662,6666,6669GROUP:DALnet
+n71=DALnet: US, OH, ToledoSERVER:glass.dal.net:6660,6661,6664GROUP:DALnet
+n72=DALnet: US, OK, TulsaSERVER:webzone.dal.net:6665,6666,6669GROUP:DALnet
+n73=Darkfire: Random serverSERVER:irc.darkfire.net:6667,7000,8000GROUP:Darkfire
+n74=Darkfyre: Random serverSERVER:irc.darkfyre.net:6667GROUP:Darkfyre
+n75=DarkMyst: Random serverSERVER:irc.darkmyst.org:6667GROUP:DarkMyst
+n76=DarkServ: Random serverSERVER:irc.darkserv.net:6664,6665,6666GROUP:DarkServ
+n77=Darktree: Random serverSERVER:irc.darktree.net:6667GROUP:Darktree
+n78=Deepspace: Disability networkSERVER:irc.deepspace.org:6667GROUP:Deepspace
+n79=Different: Random serverSERVER:irc.different.net:6667GROUP:Different
+n80=Digarix: Random serverSERVER:irc.digarix.net:6667GROUP:Digarix
+n81=Digatech: Random serverSERVER:irc.digatech.net:6667GROUP:Digatech
+n82=Digitalirc: Random serverSERVER:irc.digitalirc.net:6667GROUP:Digitalirc
+n83=Dobbernet: Random serverSERVER:irc.dobber.net:6667GROUP:Dobbernet
+n84=DragonLynk: Random serverSERVER:irc.dragonlynk.net:6661,6665,6666GROUP:DragonLynk
+n85=Dreamcast: Random serverSERVER:irc0.dreamcast.com:6667GROUP:Dreamcast
+n86=Dreamnet: Random serverSERVER:irc.dreamnet.org:6664,6665,6666GROUP:Dreamnet
+n87=DwarfStar: Random serverSERVER:irc.dwarfstar.net:6667GROUP:Dwarfstar
+n88=Dynastynet: Random serverSERVER:irc.dynastynet.net:6667GROUP:Dynastynet
+n89=EFnet: CA, AB, EdmontonSERVER:irc.powersurfr.com:6667GROUP:EFnet
+n90=EFnet: CA, ON, TorontoSERVER:irc2.magic.ca:6667GROUP:EFnet
+n91=EFnet: CA, QB, MontrealSERVER:irc.etsmtl.ca:6667GROUP:EFnet
+n92=EFnet: EU, FI, HelsinkiSERVER:efnet.cs.hut.fi:6667GROUP:EFnet
+n93=EFnet: EU, FR, ParisSERVER:irc.isdnet.fr:6667,6668,6669GROUP:EFnet
+n94=EFnet: EU, NL, AmsterdamSERVER:efnet.vuurwerk.nl:6667GROUP:EFnet
+n95=EFnet: EU, NO, HomelienSERVER:irc.homelien.no:6666,7000,7001GROUP:EFnet
+n96=EFnet: EU, RU, MoscowSERVER:irc.rt.ru:6662,6665,6668GROUP:EFnet
+n97=EFnet: EU, SE, DalarnaSERVER:irc.du.se:6666,6668,6669GROUP:EFnet
+n98=EFnet: EU, SE, SwedenSERVER:irc.light.se:6667GROUP:EFnet
+n99=EFnet: EU, UK, DemonSERVER:efnet.demon.co.uk:6665,6666,6668GROUP:EFnet
+n100=EFnet: EU, UK, LondonSERVER:irc.ins.net.uk:6665,6666,6668GROUP:EFnet
+n101=EFnet: ME, IL, InterSERVER:irc.inter.net.il:6665,6666,6668GROUP:EFnet
+n102=EFnet: US, CA, Los AngelesSERVER:irc.west.gblx.net:6667GROUP:EFnet
+n103=EFnet: US, CA, San Luis ObispoSERVER:irc.prison.net:6666,6667GROUP:EFnet
+n104=EFnet: US, CA, StanfordSERVER:irc.stanford.edu:6667GROUP:EFnet
+n105=EFnet: US, CO, ColoradoSERVER:irc.colorado.edu:5555,6665,6666GROUP:EFnet
+n106=EFnet: US, GA, Atlanta (Emory)SERVER:irc.emory.edu:6667GROUP:EFnet
+n107=EFnet: US, GA, Atlanta (Mindspring)SERVER:irc.mindspring.com:6660,6663,6668GROUP:EFnet
+n108=EFnet: US, IL, ChicagoSERVER:irc.mcs.net:6666,6667,6668GROUP:EFnet
+n109=EFnet: US, IL, ChicagoSERVER:irc.plur.net:6667GROUP:EFnet
+n110=EFnet: US, MI, Ann ArborSERVER:irc.umich.edu:6667GROUP:EFnet
+n111=EFnet: US, MN, Twin CitiesSERVER:irc.umn.edu:6665,6666,6668GROUP:EFnet
+n112=EFnet: US, NY, New YorkSERVER:irc.east.gblx.net:6667GROUP:EFnet
+n113=EgyptianIRC: Random serverSERVER:irc.egyptianirc.net:6667,6668,6669GROUP:EgyptianIRC
+n114=EntertheGame: Random serverSERVER:irc.enterthegame.com:6667,6668,6669GROUP:EntertheGame
+n115=Escaped: Random serverSERVER:irc.escaped.net:6661,6665,6668GROUP:Escaped
+n116=Esprit: Random serverSERVER:irc.esprit.net:6667GROUP:Esprit
+n117=euIRC: Random serverSERVER:irc.euirc.net:6665,6666,6668GROUP:euIRC
+n118=ExodusIRC: Random serverSERVER:irc.exodusirc.net:6662,6663,6664GROUP:ExodusIRC
+n119=FDFnet: Random serverSERVER:irc.fdf.net:6666,6668,9999GROUP:FDFnet
+n120=FEFnet: Random serverSERVER:irc.fef.net:6667GROUP:FEFnet
+n121=Forestnet: Random serverSERVER:irc.forestnet.org:6667,7000GROUP:Forestnet
+n122=FreedomChat: Random serverSERVER:chat.freedomchat.net:6667GROUP:FreedomChat
+n123=FunNet: Random serverSERVER:irc.funnet.org:6667GROUP:FunNet
+n124=Galaxynet: Random serverSERVER:irc.galaxynet.org:6662,6663,6666GROUP:GalaxyNet
+n125=Galaxynet: AU, NZ, AucklandSERVER:auckland.nz.galaxynet.org:6661,6666,6668GROUP:GalaxyNet
+n126=Galaxynet: EU, BE, OnlineSERVER:online.be.galaxynet.org:6664,6666,6668GROUP:GalaxyNet
+n127=Galaxynet: EU, SE, VltmediaSERVER:vltmedia.se.galaxynet.org:6662,6664,6666GROUP:GalaxyNet
+n128=Galaxynet: US, FL, FloridaSERVER:gymnet.us.galaxynet.org:6663,6666,6668GROUP:GalaxyNet
+n129=Galaxynet: US, WA, SeattleSERVER:freei.us.galaxynet.org:6661,6665,6666GROUP:GalaxyNet
+n130=Gamesnet: Random east US serverSERVER:east.gamesnet.net:6667GROUP:Gamesnet
+n131=Gamesnet: Random west US serverSERVER:west.gamesnet.net:6667GROUP:Gamesnet
+n132=GizNet: Random serverSERVER:irc.giznet.com:6666,6668,6669GROUP:GizNet
+n133=Globalchat: Random serverSERVER:irc.globalchat.org:6667GROUP:Globalchat
+n134=Grnet: Random EU serverSERVER:gr.irc.gr:6667,7000GROUP:GRnet
+n135=Grnet: Random serverSERVER:srv.irc.gr:6667,7000GROUP:GRnet
+n136=Grnet: Random US serverSERVER:us.irc.gr:6667,7000GROUP:GRnet
+n137=HabberNet: Random serverSERVER:irc.habber.net:6667GROUP:HabberNet
+n138=HanIRC: Random serverSERVER:irc.hanirc.org:6667GROUP:HanIRC
+n139=Hellenicnet: Random serverSERVER:irc.mirc.gr:6667,7000GROUP:Hellenicnet
+n140=Hybnet: Random serverSERVER:irc.hybnet.net:6667GROUP:Hybnet
+n141=ICQnet: Random serverSERVER:irc.icq.com:6667GROUP:ICQnet
+n142=Infatech: Random serverSERVER:irc.infatech.net:6661,6663,6666GROUP:Infatech
+n143=Infinity: Random serverSERVER:irc.infinity-irc.org:6667GROUP:Infinity
+n144=Infomatrix: Random serverSERVER:irc.infomatrix.net:6667GROUP:Infomatrix
+n145=IRC-Hispano: Random serverSERVER:irc.irc-hispano.org:6667GROUP:IRC-Hispano
+n146=IRChat: Random serverSERVER:irc.irchat.net:6661,6662,6666GROUP:IRChat
+n147=IRChat-br: Random serverSERVER:irc.irchat.com.br:6667GROUP:IRChat-br
+n148=IRCLink: EU, NO, AlesundSERVER:alesund.no.eu.irclink.net:6667,6668,6669GROUP:IRCLink
+n149=IRCLink: EU, NO, OsloSERVER:frogn.no.eu.irclink.net:6667,6668,6669GROUP:IRCLink
+n150=IRCLink: US, SC, Rock HillSERVER:rockhill.sc.us.irclink.net:6667,6668,6669GROUP:IRCLink
+n151=IRCnet: EU, IT, RandomSERVER:irc.ircd.it:6665,6666,6668GROUP:IRCnet
+n152=IRCnet: AS, JP, TokyoSERVER:irc.tokyo.wide.ad.jp:6667GROUP:IRCnet
+n153=IRCnet: AU, MelbourneSERVER:yoyo.cc.monash.edu.au:6668,6669,9990GROUP:IRCnet
+n154=IRCnet: AU, SydneySERVER:irc.usyd.edu.au:6667GROUP:IRCnet
+n155=IRCnet: EU, AT, LinzSERVER:linz.irc.at:6666,6667,6668GROUP:IRCnet
+n156=IRCnet: EU, AT, WienSERVER:vienna.irc.at:6666,6668,6669GROUP:IRCnet
+n157=IRCnet: EU, BE, BrusselsSERVER:irc.belnet.be:6667GROUP:IRCnet
+n158=IRCnet: EU, BE, ZaventemSERVER:ircnet.wanadoo.be:6661,6664,6665GROUP:IRCnet
+n159=IRCnet: EU, CZ, PragueSERVER:irc.felk.cvut.cz:6667GROUP:IRCnet
+n160=IRCnet: EU, DE, BerlinSERVER:irc.fu-berlin.de:6665,6666,6668GROUP:IRCnet
+n161=IRCnet: EU, DE, DusseldorfSERVER:irc.freenet.de:6665,6666,6668GROUP:IRCnet
+n162=IRCnet: EU, DE, StuttgartSERVER:irc.belwue.de:6665,6666,6668GROUP:IRCnet
+n163=IRCnet: EU, DK, CopenhagenSERVER:irc.ircnet.dk:6667GROUP:IRCnet
+n164=IRCnet: EU, EE, TallinnSERVER:irc.estpak.ee:6666,6667,6668GROUP:IRCnet
+n165=IRCnet: EU, FI, EspooSERVER:irc.funet.fi:6661,6663,6668GROUP:IRCnet
+n166=IRCnet: EU, FI, HelsinkiSERVER:irc.cs.hut.fi:6667GROUP:IRCnet
+n167=IRCnet: EU, FR, NiceSERVER:irc.eurecom.fr:6667GROUP:IRCnet
+n168=IRCnet: EU, FR, ParisSERVER:irc.fr.ircnet.net:6667GROUP:IRCnet
+n169=IRCnet: EU, GR, ThessalonikiSERVER:irc.ee.auth.gr:6666,6668,6669GROUP:IRCnet
+n170=IRCnet: EU, HU, BudapestSERVER:irc.elte.hu:6667GROUP:IRCnet
+n171=IRCnet: EU, IL, HaifaSERVER:ircnet.netvision.net.il:6661,6662,6663GROUP:IRCnet
+n172=IRCnet: EU, IS, ReykjavikSERVER:irc.ircnet.is:6663,6664,6668GROUP:IRCnet
+n173=IRCnet: EU, IS, ReykjavikSERVER:irc.ircnet.is:6661,6662,6666GROUP:IRCnet
+n174=IRCnet: EU, IT, RomeSERVER:irc.tin.it:6665,6666,6668GROUP:IRCnet
+n175=IRCnet: EU, LV, RigaSERVER:irc.telia.lv:6666,6668,6669GROUP:IRCnet
+n176=IRCnet: EU, NL, Amsterdam (nlnet)SERVER:irc.nl.uu.net:6661,6662,6663GROUP:IRCnet
+n177=IRCnet: EU, NL, Amsterdam (xs4all)SERVER:irc.xs4all.nl:6663,6664,6665GROUP:IRCnet
+n178=IRCnet: EU, NL, EnschedeSERVER:irc.snt.utwente.nl:6661,6662,6666GROUP:IRCnet
+n179=IRCnet: EU, NL, NijmegenSERVER:irc.sci.kun.nl:6663,6664,6668GROUP:IRCnet
+n180=IRCnet: EU, NO, OsloSERVER:irc.ifi.uio.no:6667GROUP:IRCnet
+n181=IRCnet: EU, NO, TrondheimSERVER:irc.pvv.ntnu.no:6667GROUP:IRCnet
+n182=IRCnet: EU, PL, WarsawSERVER:warszawa.irc.pl:6666,6667,6668GROUP:IRCnet
+n183=IRCnet: EU, RU, MoscowSERVER:irc.msu.ru:6667GROUP:IRCnet
+n184=IRCnet: EU, SE, LuleaSERVER:irc.ludd.luth.se:6665,6666,6668GROUP:IRCnet
+n185=IRCnet: EU, UK, London (Btnet)SERVER:chat.bt.net:6662,6663,6668GROUP:IRCnet
+n186=IRCnet: EU, UK, London (Demon)SERVER:ircnet.demon.co.uk:6665,6666,6668GROUP:IRCnet
+n187=IRCnet: EU, UK, London (Netcom)SERVER:irc.netcom.net.uk:6660,6661,6662GROUP:IRCnet
+n188=IRCnet: EU, UK, Warrington (u-net)SERVER:irc.u-net.com:6660,6661,6665GROUP:IRCnet
+n189=IRCnet: US, NY, New YorkSERVER:irc.stealth.net:6663,6664,6665GROUP:IRCnet
+n190=Irctoo: Random serverSERVER:irc.irctoo.net:6667GROUP:Irctoo
+n191=IRCworld: Random serverSERVER:irc.ircworld.org:6667GROUP:IRCworld
+n192=Israelnet: Random serverSERVER:irc.israel.net:6667GROUP:Israelnet
+n193=K0wNet: Random serverSERVER:irc.k0w.net:6661,6665,6668GROUP:K0wNet
+n194=Kewl.org: Random serverSERVER:irc.kewl.org:6667GROUP:KewlOrg
+n195=Kewl.org: EU, FR, NanterreSERVER:nanterre.fr.eu.kewl.org:6667GROUP:KewlOrg
+n196=Kewl.org: EU, UK, LondonSERVER:london.uk.eu.kewl.org:6667GROUP:KewlOrg
+n197=Kidsworld: US, CO, DenverSERVER:denver.co.us.kidsworld.org:6666,6668,6669GROUP:KidsWorld
+n198=Kidsworld: US, MD, BaltimoreSERVER:baltimore.md.us.kidsworld.org:6666,6668,6669GROUP:KidsWorld
+n199=KissLand: Random serverSERVER:irc.kissland.com:6667GROUP:KissLand
+n200=Knightnet: AF, ZA, DurbanSERVER:orc.dbn.za.knightnet.net:6667,5555GROUP:Knightnet
+n201=Knightnet: US, CA, GoldengateSERVER:goldengate.ca.us.knightnet.net:6667,5555GROUP:Knightnet
+n202=KreyNet: Random serverSERVER:irc.krey.net:6667GROUP:Kreynet
+n203=Krushnet: Random serverSERVER:irc.krushnet.org:6667GROUP:Krushnet
+n204=LagNet: Random serverSERVER:irc.lagnet.org.za:6667GROUP:LagNet
+n205=LagNet: AF, ZA, Cape TownSERVER:reaper.lagnet.org.za:6667GROUP:LagNet
+n206=LagNet: AF, ZA, JohannesburgSERVER:mystery.lagnet.org.za:6667GROUP:LagNet
+n207=Librenet: Random serverSERVER:irc.librenet.net:6667GROUP:Librenet
+n208=Lunarnetirc: Random serverSERVER:irc.lunarnetirc.org:6667GROUP:Lunarnetirc
+n209=Lunatics: Random serverSERVER:irc.lunatics.net:6667,6668,6669GROUP:Lunatics
+n210=MagicStar: Random serverSERVER:irc.magicstar.net:6667GROUP:MagicStar
+n211=MavraNet: Random serverSERVER:irc.mavra.net:6662,6663,6664GROUP:MavraNet
+n212=MediaDriven: Random serverSERVER:irc.mediadriven.com:6667,6668,6669GROUP:MediaDriven
+n213=Messique: Random serverSERVER:irc.messique.org:6661,6664,6666GROUP:Messique
+n214=Millenia: Random serverSERVER:irc.millenia.org:6664,6665,6666GROUP:Millenia
+n215=Mozilla: Random serverSERVER:irc.mozilla.org:6667,7000GROUP:Mozilla
+n216=Mysteria: Random serverSERVER:irc.mysteria.net:6667,7000GROUP:Mysteria
+n217=Mystical: Random serverSERVER:irc.mystical.net:6667,7000GROUP:Mystical
+n218=NdrsNet: Random serverSERVER:irc.ndrsnet.com:6666,6668,6669GROUP:NdrsNet
+n219=Net-France: Random serverSERVER:irc.net-france.com:6667GROUP:Net-France
+n220=Nevernet: Random serverSERVER:irc.nevernet.net:6667GROUP:Nevernet
+n221=Newnet: Random serverSERVER:irc.newnet.net:6665,6666,6667GROUP:Newnet
+n222=Newnet: EU, DE, TrustedSERVER:irc.trusted-network.de:6666,7000GROUP:Newnet
+n223=Newnet: EU, UK, OasisSERVER:irc.oasis-net.net:6666,7000GROUP:Newnet
+n224=Newnet: NZ, AucklandSERVER:irc.uplink.net.nz:6661,6662,6666GROUP:Newnet
+n225=Newnet: US, CA, FlagglerockSERVER:irc.fragglerock.org:6666,7000GROUP:Newnet
+n226=Newnet: US, MA, ChelmsfordSERVER:irc.chelmsford.com:6660,6665,6668GROUP:Newnet
+n227=Newnet: US, VA, RandolphSERVER:irc.rma.edu:6665,6666,6668GROUP:Newnet
+n228=Nexusirc: Random serverSERVER:irc.nexusirc.org:6667GROUP:Nexusirc
+n229=Nightstar: Random serverSERVER:irc.nightstar.net:6665,6666,6668GROUP:NightStar
+n230=NitroNet: Random serverSERVER:irc.nitro.net:6667GROUP:NitroNet
+n231=Novernet: Random serverSERVER:irc.novernet.com:6665,6666,6668GROUP:Novernet
+n232=Novernet: US, GA, AugustaSERVER:irc.c-plusnet.com:6666,6668,6669GROUP:Novernet
+n233=Novernet: US, MN, MinneapolisSERVER:chat.novernet.com:6667GROUP:Novernet
+n234=Othernet: Random serverSERVER:irc.othernet.org:6667GROUP:Othernet
+n235=Othernet: US, FL, MiamiSERVER:miami.fl.us.othernet.org:6667GROUP:Othernet
+n236=Othernet: US, MO, StLouisSERVER:stlouis.mo.us.othernet.org:6667GROUP:Othernet
+n237=Otherside: Random serverSERVER:irc.othersideirc.net:6667GROUP:OtherSide
+n238=Outsiderz: Random serverSERVER:irc.outsiderz.com:6667GROUP:Outsiderz
+n239=OzChat: Random serverSERVER:irc.ozchat.org:6667GROUP:OzChat
+n240=OzOrg: AU, AdelaideSERVER:chariot.adelaide.oz.org:6666,6668,7000GROUP:OzOrg
+n241=OzOrg: AU, PerthSERVER:iinet.perth.oz.org:6667GROUP:OzOrg
+n242=OzOrg: AU, SydneySERVER:aussie.sydney.oz.org:6668,6669,7000GROUP:OzOrg
+n243=Pinoycentral: Random serverSERVER:chat.abs-cbn.com:6667GROUP:Pinoycentral
+n244=Planetarion: Random serverSERVER:irc.planetarion.com:6667GROUP:Planetarion
+n245=PowerChat: Random serverSERVER:irc.powerchat.org:6954,6956,6999GROUP:PowerChat
+n246=Pseudonet: Random serverSERVER:irc.pseudonet.org:6667GROUP:Pseudonet
+n247=PTlink: Random serverSERVER:irc.ptlink.net:6667GROUP:PTlink
+n248=PTnet: EU, PT, FaroSERVER:ualg.ptnet.org:6667GROUP:PTnet
+n249=PTnet: EU, PT, LisboaSERVER:telepac2.ptnet.org:6667GROUP:PTnet
+n250=QChat: Random serverSERVER:irc.qchat.net:6667GROUP:QChat
+n251=QuakeNet: Random serverSERVER:irc.quakenet.eu.org:6667,6668,6669GROUP:QuakeNet
+n252=Raptanet: Random serverSERVER:irc.rapta.net:6667GROUP:Raptanet
+n253=Raptornet: Random serverSERVER:irc.raptornet.org:6667GROUP:Raptornet
+n254=Realirc: Random serverSERVER:irc.realirc.org:6667GROUP:Realirc
+n255=Rebelchat: Random serverSERVER:irc.rebelchat.org:6667GROUP:Rebelchat
+n256=Red-Latina: Random serverSERVER:irc.red-latina.org:6667GROUP:Red-Latina
+n257=Red-Latina: NA, MX, SanJoseSERVER:irc.dalsom.net:6666,6667GROUP:Red-Latina
+n258=RedeBrasil: BR, CE, CearaSERVER:irc.linline.com.br:6666,7000,7001GROUP:RedeBrasil
+n259=RedeBrasil: BR, PE, RecifeSERVER:irc.hotlink.com.br:6665,6666,7000GROUP:RedeBrasil
+n260=RedeBrasil: BR, PR, ParanaSERVER:irc.intermam.com.br:6660,6661,6662GROUP:RedeBrasil
+n261=RedeBrasil: BR, SP, Sao PauloSERVER:irc.aserver.com.br:6662,6666,7000GROUP:RedeBrasil
+n262=RedeSul: BR, PR, MaringaSERVER:irc.wnet.com.br:6667GROUP:RedeSul
+n263=RedeSul: BR, SC, BlumenauSERVER:irc.braznet.com.br:6667,9001GROUP:RedeSul
+n264=RedLatona: Random serverSERVER:irc.redlatona.net:6667,6668GROUP:RedLatona
+n265=RekorNet: Random serverSERVER:irc.rekor.net:6665,6668,6669GROUP:RekorNet
+n266=Relicnet: Random serverSERVER:irc.relic.net:6667GROUP:Relicnet
+n267=Relicnet: US, MA, CycloneSERVER:cyclone.us.relic.net:6661,6662,6663GROUP:Relicnet
+n268=Risanet: Random serverSERVER:irc.risanet.com:6667,6668,6669GROUP:Risanet
+n269=Rusnet: EU, RU, TomskSERVER:irc.tsk.ru:7771,7773,7774GROUP:Rusnet
+n270=Rusnet: EU, RU, VladivostokSERVER:irc.vladivostok.ru:6669,7771,7774GROUP:Rusnet
+n271=Rusnet: EU, UA, KievSERVER:irc.kar.net:6669,7770,7774GROUP:Rusnet
+n272=Sandnet: Random serverSERVER:irc.sandnet.net:6660,6662,6668GROUP:Sandnet
+n273=Sandnet: US, CA, MysticSERVER:mystic.tides.sandnet.net:6662,6665,6666GROUP:Sandnet
+n274=Sandnet: US, NJ, DuneBuggySERVER:dunebuggy.nj.sandnet.net:6661,6662,6666GROUP:Sandnet
+n275=Scunc: Random serverSERVER:irc.scunc.net:6667GROUP:Scunc
+n276=SexNet: Random serverSERVER:irc.sexnet.org:6667GROUP:SexNet
+n277=SgNet: Random serverSERVER:irc.sgnet.org:6661,6663,6668GROUP:SgNet
+n278=ShadowFire: Random serverSERVER:irc.shadowfire.org:6667GROUP:ShadowFire
+n279=ShadowWorld: Random serverSERVER:irc.shadowworld.net:6667GROUP:shadowWorld
+n280=SideNet: Random serverSERVER:irc.sidenet.org:6661,6663,6666GROUP:SideNet
+n281=Skyyenet: US, VA, ArlingtonSERVER:arlington.va.us.skyyenet.org:6667GROUP:Skyyenet
+n282=Slashnet: Random serverSERVER:irc.slashnet.org:6667GROUP:Slashnet
+n283=Solarchat: Random serverSERVER:irc.solarchat.net:6667,7000GROUP:Solarchat
+n284=Sorcerynet: Random serverSERVER:irc.sorcery.net:6667,7000,9000GROUP:Sorcery
+n285=Sorcerynet: EU, SE, KarlskronaSERVER:nexus.sorcery.net:6667,7000,9000GROUP:Sorcery
+n286=Sorcerynet: US, CA, Palo AltoSERVER:kechara.sorcery.net:6667,7000,9000GROUP:Sorcery
+n287=Spamnet: Random serverSERVER:irc.spamnet.org:6667,6668,6669GROUP:Spamnet
+n288=StarChat: Random serverSERVER:irc.starchat.net:6668,6669,7000GROUP:StarChat
+n289=StarChat: AU, QLD, SouthernCrossSERVER:boomer.qld.au.starchat.net:6668,6669,7000GROUP:StarChat
+n290=StarChat: EU, NO, AskerSERVER:reality.no.eu.starchat.net:6668,6669,7000GROUP:StarChat
+n291=StarChat: US, CA, San JoseSERVER:sand.ca.us.starchat.net:6668,6669,7000GROUP:StarChat
+n292=StarLink-irc: Random serverSERVER:irc.starlink-irc.org:6667GROUP:starlink-irc
+n293=StarLink-irc: US, MI, RochesterSERVER:rochester.mi.us.starlink-irc.org:6667GROUP:Starlink-irc
+n294=StarLink-irc: US, TX, HoustonSERVER:houston.tx.us.starlink-irc.org:6667GROUP:Starlink-irc
+n295=StarLink Org: US, CO, DenverSERVER:denver.co.us.starlink.org:6660,6662,6666GROUP:StarlinkOrg
+n296=StarLink Org: US, NC, DurhamSERVER:durham-r.nc.us.starlink.org:6661,6663,6668GROUP:StarlinkOrg
+n297=StarLink Org: US, TX, WichitaFallsSERVER:wichitafalls.tx.us.starlink.org:6666,6667,6668GROUP:StarlinkOrg
+n298=StarWars-IRC: Random serverSERVER:irc.starwars-irc.net:6663,6664,6665GROUP:StarWars-IRC
+n299=Stormdancing: Random serverSERVER:irc.stormdancing.net:6664,6668,7000GROUP:Stormdancing
+n300=Sub-city: Random serverSERVER:irc.sub-city.net:6668,6669,7000GROUP:Sub-city
+n301=Superchat: Random serverSERVER:irc.superchat.org:6660,6664,6665GROUP:Superchat
+n302=Superonline: Random serverSERVER:irc.superonline.com:6663,6664,6669GROUP:Superonline
+n303=Sysopnet: Random serverSERVER:irc.sysopnet.org:6666,6667,6668GROUP:Sysopnet
+n304=Techdreams: Random serverSERVER:irc.techdreams.net:6667GROUP:Techdreams
+n305=Telstra: Random serverSERVER:irc.telstra.com:6667,6668,6669GROUP:Telstra
+n306=TR-net: EU, TR, AnkaraSERVER:irc.dominet.com.tr:6667GROUP:TR-net
+n307=TR-net: EU, Tr, IstanbulSERVER:irc.teklan.com.tr:6667GROUP:TR-net
+n308=TRcom: Random serverSERVER:irc.trcom.net:6666,6668,6669GROUP:TRcom
+n309=Tri-net: Random serverSERVER:irc.tri-net.org:6667GROUP:Tri-net
+n310=UltraIRC: Random serverSERVER:irc.ultrairc.net:6667GROUP:UltraIRC
+n311=Undernet: CA, ON, TorontoSERVER:toronto.on.ca.undernet.org:6661,6662,6666GROUP:Undernet
+n312=Undernet: CA, QC, MontrealSERVER:montreal.qu.ca.undernet.org:6660,6661,6664GROUP:Undernet
+n313=Undernet: EU, AT, GrazSERVER:graz.at.eu.undernet.org:6661,6663,6668GROUP:Undernet
+n314=Undernet: EU, BE, AntwerpSERVER:flanders.be.eu.undernet.org:6660,6662,6663GROUP:Undernet
+n315=Undernet: EU, BE, BrusselsSERVER:brussels.be.eu.undernet.org:6661,6663,6668GROUP:Undernet
+n316=Undernet: EU, FR, CaenSERVER:caen.fr.eu.undernet.org:6666,6668,6669GROUP:undernet
+n317=Undernet: EU, FR, ParisSERVER:paris.fr.eu.undernet.org:6662,6663,6666GROUP:Undernet
+n318=Undernet: EU, NL, DiemenSERVER:diemen.nl.eu.undernet.org:6665,6666,6668GROUP:Undernet
+n319=Undernet: EU, NL, HaarlemSERVER:haarlem.nl.eu.undernet.org:6660,6662,6666GROUP:Undernet
+n320=Undernet: EU, NO, OsloSERVER:oslo.no.eu.undernet.org:6662,6664,6668GROUP:Undernet
+n321=Undernet: EU, SE, StockholmSERVER:stockholm.se.eu.undernet.org:6661,6662,6664GROUP:Undernet
+n322=Undernet: EU, UK, LondonSERVER:london.uk.eu.undernet.org:6666,6668,6669GROUP:Undernet
+n323=Undernet: EU, UK, SurreySERVER:surrey.uk.eu.undernet.org:6662,6665,6666GROUP:Undernet
+n324=Undernet: US, CA, San DiegoSERVER:sandiego.ca.us.undernet.org:6665,6666,6668GROUP:Undernet
+n325=Undernet: US, DC, WashingtonSERVER:washington.dc.us.undernet.org:6660,6665,6668GROUP:Undernet
+n326=Undernet: US, KS, ManhattanSERVER:manhattan.ks.us.undernet.org:6662,6664,6665GROUP:Undernet
+n327=Undernet: US, NV, Las VegasSERVER:lasvegas.nv.us.undernet.org:6662,6663,6668GROUP:Undernet
+n328=Undernet: US, TX, AustinSERVER:austin.tx.us.undernet.org:6661,6663,6664GROUP:Undernet
+n329=Undernet: US, UT, SaltlakeSERVER:saltlake.ut.us.undernet.org:6663,6665,6666GROUP:Undernet
+n330=Undernet: US, VA, ArlingtonSERVER:arlington.va.us.undernet.org:6661,6663,6665GROUP:Undernet
+n331=Undernet: US, VA, McLeanSERVER:mclean.va.us.undernet.org:6666,6668,6669GROUP:Undernet
+n332=UnderZ: Random serverSERVER:irc.underz.org:6667,6668GROUP:UnderZ
+n333=UnionLatina: Random serverSERVER:irc.unionlatina.org:6667GROUP:UnionLatina
+n334=UnitedChat Net: Random serverSERVER:irc.unitedchat.net:6667GROUP:UnitedchatNet
+n335=UnitedChat Org: Random serverSERVER:irc.unitedchat.org:6667GROUP:UnitedchatOrg
+n336=Univers: Random serverSERVER:irc.univers.org:6665,6666,6668GROUP:Univers
+n337=Vidgamechat: Random serverSERVER:irc.vidgamechat.com:6667GROUP:Vidgamechat
+n338=Virtuanet: Random serverSERVER:irc.virtuanet.org:6662,6666,6669GROUP:Virtuanet
+n339=Vitamina: Random serverSERVER:irc-rr.vitamina.ca:6667GROUP:Vitamina
+n340=Warpednet: Random serverSERVER:irc.warped.net:6667GROUP:Warpednet
+n341=Webnet: Random serverSERVER:irc.webchat.org:6668,6669,7000GROUP:Webnet
+n342=Webnet: US, AL, DothanSERVER:wiregrass.al.us.webchat.org:6668,6669,7000GROUP:Webnet
+n343=Webnet: US, CA, Santa ClaraSERVER:webmaster.ca.us.webchat.org:6662,6663,6664GROUP:Webnet
+n344=Webnet: US, MA, BostonSERVER:greennet.ma.us.webchat.org:6668,6669,7000GROUP:Webnet
+n345=WonKnet: Random serverSERVER:irc.wonk.net:6668,6669,7000GROUP:Wonknet
+n346=WorldIRC: Random serverSERVER:irc.worldirc.org:6661,6662,6665GROUP:WorldIRC
+n347=Xevion: Random serverSERVER:irc.xevion.net:6667,7000GROUP:Xevion
+n348=XNet: Random serverSERVER:irc.xnet.org:6667GROUP:XNet
+n349=XWorld: Random serverSERVER:irc.xworld.org:6667GROUP:XWorld
+n350=ZAnet Net: AF, ZA, CI (lia)SERVER:lia.zanet.net:6667GROUP:ZAnetNet
+n351=ZAnet Net: AF, ZA, MWeb (timewiz)SERVER:timewiz.zanet.net:6667GROUP:ZAnetNet
+n352=ZAnet Org: AF, ZA, Cape Town (gaspode)SERVER:gaspode.zanet.org.za:6667GROUP:ZAnetOrg
+n353=ZAnet Org: AF, ZA, Johannesburg (is)SERVER:is.zanet.org.za:6667GROUP:ZAnetOrg
+n354=ZAnet Org: AF, ZA, Midrand (ethereal)SERVER:ethereal.zanet.org.za:6660,6666GROUP:ZAnetOrg
+n355=ZiRC: Random serverSERVER:irc.zirc.org:6660,6662,6665GROUP:ZiRC
+n356=ZUHnet: Random serverSERVER:irc.zuh.net:6667GROUP:ZUHnet
+n357=Zurna: Random serverSERVER:irc.zurna.net:6667GROUP:Zurna
diff --git a/ksirc/KSOpenkSirc/servers.txt b/ksirc/KSOpenkSirc/servers.txt
new file mode 100644
index 00000000..a61b0199
--- /dev/null
+++ b/ksirc/KSOpenkSirc/servers.txt
@@ -0,0 +1,654 @@
+Random:Random AU EFnet server:au.rr.efnet.net:6667:
+Random:Random AU IRCnet server:au.ircnet.org:6667:
+Random:Random CA EFnet server:ca.rr.efnet.net:6667:
+Random:Random EFnet server:irc.chat.org:6667:
+Random:Random EU DALnet server:irc.eu.dal.net:6667:
+Random:Random EU DALnet server:irc.eu.dal.net:7000:
+Random:Random EU EFnet server:eu.rr.efnet.net:6667:
+Random:Random EU IRCnet server:eu.ircnet.org:6665,6666,6668:
+Random:Random EU Undernet server:eu.undernet.org:6667:
+Random:Random Freenode server:irc.freenode.net:6667:
+Random:Random US DALnet server:irc.dal.net:6662,6663,6664:
+Random:Random US DALnet server:irc.dal.net:7000:
+Random:Random US EFnet server:us.rr.efnet.net:6667:
+Random:Random US IRCnet server:us.ircnet.org:6665,6666,6668:
+Random:Random US Undernet server:us.undernet.org:6667:
+Accessirc:Random server:irc.accessirc.net:6667:
+Acestar:Random server:irc.acestar.org:6667:
+Action-IRC:Random server:irc.action-irc.net:6660,6662,6664:
+Afternet:Random server:irc.afternet.org:6667:
+Afternet:US, CA, San Diego:blacklodge.c2.org:6667:
+Afternet:US, LA, Ruston:scorpion.latech.edu:6667:
+Afternet:US, OH, Columbus:irc.coldfusion.net:6667:
+Afternet:US, OR, Portland:agora.rdrop.com:6667:
+Alternativenet:Random server:irc.altnet.org:6667:
+AnotherNet:Random server:irc.another.net:6667,7000:
+Anothernet:US, CA, Los Angeles:sunrise.ca.us.another.net:6667:
+Anothernet:US, CA, Morgan Hill:neato.ca.us.another.net:6667:
+Anothernet:US, VT, Burlington:together.vt.us.another.net:6667:
+ArabChat:Random server:irc.arabchat.org:6660,6661,6662:
+AstroLink:Random server:irc.astrolink.org:6660,6662,6663:
+Asylumnet:Random server:irc.asylum-net.org:6661,6664,7000:
+Austnet:AS, Singapore:ntu.sg.austnet.org:6667:
+Austnet:AU, Melbourne:aussie.au.austnet.org:6667,6668:
+Austnet:AU, Sydney:sydney.au.austnet.org:6667,6668:
+Austnet:CA, ON, Toronto:insane.ca.austnet.org:6667,6668:
+Austnet:Random AU server:au.austnet.org:6667:
+Austnet:Random NZ server:nz.austnet.org:6667:
+Austnet:Random SG server:sg.austnet.org:6667:
+Austnet:Random US server:us.austnet.org:6667:
+Austnet:US, CA, Santa Clara:webmaster.us.austnet.org:6667,6668:
+Austnet:US, FL, Wizard:wizard.us.austnet.org:6667,6668:
+Austnet:US, PA, Wallaby:wallaby.us.austnet.org:6667,6668:
+AwesomeChat:Random server:irc.awesomechat.net:6662,6665,6666:
+Axenet:Random server:irc.axenet.org:6661,6664,6665:
+BeyondIRC:Random server:irc.beyondirc.net:6660,6662,6664:
+BeyondIRC:US, AL, Snowhill:irc.snowhill.com:6667,6668,6669,6670:
+BeyondIRC:US, CA, Los Angeles:babylon.beyondirc.net:6667,6668,6669:
+BeyondIRC:US, MO, Jefferson City:irc.telcentral.com:6667:
+Blabbernet:Random server:irc.blabber.net:6667,7000:
+Blitzed:Random server:irc.blitzed.org:6667,7000:
+BrasIRC:Brazil, PI, Teresina:irc.ranet.com.br:6667:
+BrasIRC:Brazil, PR, Curitiba:irc2.kanopus.com.br:6667:
+BrasIRC:Brazil, RJ, Rio de Janeiro:irc.ism.com.br:6667:
+BrasIRC:Brazil, SP, Santos:irc.iron.com.br:6667:
+BrasIRC:Brazil, Starcom:irc.stc.com.br:6667:
+BrasIRC:US, FL, Miami:irc2.sodre.net:6667:
+Brasilnet:Brazil, BA, Bahia:irc.magiclink.com.br:6667,6668,6669,7000,7001,7002:
+Brasilnet:Brazil, BA, Salvador:irc.nway.com.br:6667,6668,6669,7000,7001,7002:
+Brasilnet:Brazil, GO, Brasilia (ab):irc.abordo.com.br:6667,6668,6669,7000,7001,7002:
+Brasilnet:Brazil, GO, Brasilia (pc):irc.persocom.com.br:6667,6668,6669,7000,7001,7002:
+Brasilnet:Brazil, PE, Caruaru:irc.netstage.com.br:6667,6668,6669,7000,7001,7002:
+Brasilnet:Brazil, PE, Recife:irc.elogica.com.br:6667,6668,6669,7000,7001,7002:
+Brasirc:BR, PA, Belem:irc.libnet.com.br:6666,6668,7777:
+Brasirc:BR, SC, Florianopolis:irc.globalite.com.br:6667:
+Brasirc:Random server:irc.brasirc.net:6666,6667:
+Brasnet:Brazil, DF, Brasilia:irc.americasnet.com.br:6667:
+Brasnet:Brazil, RJ, Rio de Janeiro:irc.pontocom.com.br:6667:
+Brasnet:Brazil, SP, Campinas:irc.correionet.com.br:6667:
+Brasnet:Brazil, SP, Sao Paulo:irc.cpunet.com.br:6667:
+Brasnet:Random European server:eu.brasnet.org:6665,6666,6668:
+Brasnet:Random US server:us.brasnet.org:6665,6666,6668:
+Brasnet:Random server:irc.brasnet.org:6665,6666,6668:
+Brasnet:US, Americasnet:americasnet.usa.brasnet.org:6667:
+Bulgaria:Random server:irc.bulgaria.org:6666,6668,6669:
+CCnet:Random server:irc.cchat.net:6667,7000:
+CCnet:US, TX, Dallas:irc2.cchat.net:6667,7000:
+CNN:CNN News discussions:chat.cnn.com:6668,6669,7000:
+ChatArea:Random server:irc.chatarea.net:6667:
+ChatCentral2:Random server:irc.cc2.org:6660,6661,6663:
+ChatCircuit:Random server:irc.chatcircuit.com:6668:
+ChatPR:Random server:irc.chatpr.org:6667:
+ChatX:Random server:irc.chatx.net:6667:
+Chatcafe:Random server:irc.chatcafe.net:6667:
+Chatlink:Random server:irc.chatlink.org:6667:
+Chatnet:EU, Norway:Frogn.NO.ChatNet.Org:6667:
+Chatnet:Random AU server:au.chatnet.org:6667:
+Chatnet:Random EU server:eu.chatnet.org:6667:
+Chatnet:Random US server:us.chatnet.org:6667:
+Chatnet:US, CA, Los Angeles:LosAngeles.CA.US.ChatNet.Org:6667:
+Chatnet:US, CA, San Francisco:SF.CA.US.Chatnet.Org:6667:
+Chatnet:US, CA, Walnut Creek:WalnutCreek.CA.US.ChatNet.Org:6667:
+Chatnet:US, FL, Pensacola:Pensacola.FL.US.ChatNet.Org:6666,6667,6668,6669:
+Chatnet:US, ID, Pocatello:Pocatello.IS.US.ChatNet.Org:6667:
+Chatnet:US, KY, Louisville:Louisville.KY.US.ChatNet.Org:6667:
+Chatnet:US, MS, Tupelo:Tupelo.MS.US.Chatnet.Org:6666,6667,6668,6669:
+Chatnet:US, OK, Stillwater:Stillwater.OK.US.ChatNet.Org:6667:
+Chatnet:US, OR, Portland:Portland.OR.US.Chatnet.Org:6666,6667,6668,6669:
+Chatnet:US, SC, Rock Hill:RockHill.SC.US.ChatNet.Org:6666,6667,6668,6669:
+Chatnet:US, UT, Salt Lake City:SLC.UT.US.Chatnet.Org:6666,6667,6668,6669:
+Chatpinoy:Random server:irc.chatpinoy.com:6667:
+Chatroom:Random server:irc.chatroom.org:6667:
+Chatsolutions:Random server:irc.chatsolutions.org:6667:
+Chatster:Random server:irc.chatster.org:6667:
+Cobranet:CA, AK, Specific:Specific.AK.US.Cobra.Net:6667:
+Cobranet:CA, ON, StrathRoy:StrathRoy.ON.CA.Cobra.Net:6667:
+Cobranet:EU, UK, London:London.UK.EU.Cobra.Net:6667:
+Cobranet:US, RI, Providence:Providence.RI.US.Cobra.Net:6667:
+Coolchat:Random server:irc.coolchat.net:6667:
+Criten:Random server:irc.criten.net:6667:
+CyGanet:Random server:irc.cyga.net:6667:
+Cyberchat:Random server:irc.cyberchat.org:6667,6668:
+DALnet:AS, MY, Kuala Lumpur:coins.dal.net:6663,6664,6668:
+DALnet:AU, Adelaide:ozbytes.dal.net:6667,7000:
+DALnet:AU, New South Wales:bunyip.DAL.net:7000,6665,6666,6668,6669:
+DALnet:CA, BC, Vancouver:vancouver.dal.net:6661,6662,6666:
+DALnet:Canada, Edmonton Alb.:raptor.DAL.net:6661,6662,6663,7000:
+DALnet:Canada, Toronto:toronto.DAL.net:7000,7001,7002:
+DALnet:EU, DE, Frankfurt:nexgo.de.eu.dal.net:6664,6665,6669:
+DALnet:EU, FI, Espoo:xgw.DAL.net:6668,7000:
+DALnet:EU, NO, Oslo:powertech.no.eu.dal.net:6666,6667,7000:
+DALnet:EU, NO, Tromso:viking.dal.net:6666,6668,6669:
+DALnet:EU, SE, Goteborg:ced.dal.net:6667,7000:
+DALnet:EU, SE, Stockholm:paranoia.dal.net:6661,6662,6669:
+DALnet:EU, UK, Bristol:liberator.DAL.net:6668,6669,7000:
+DALnet:EU, UK, London:defiant.dal.net:6668,6669,7001:
+DALnet:EU, UK, London:defiant.dal.net:7000,7001,7002,7003:
+DALnet:US, CA, Davis:davis.DAL.net:6668,6669,7000:
+DALnet:US, CA, El Segundo:cyberverse.DAL.net:6667,7000:
+DALnet:US, CA, San Diego:voyager.DAL.net:6666,6667,6668,6669,7000:
+DALnet:US, CA, Santa Clara:spider.DAL.net:6668,6669,7000,7777:
+DALnet:US, CA, Sunnyvale:mindijari.DAL.net:6667,6668,6669,7000:
+DALnet:US, FL, Hollywood:sodre.dal.net:6663,6666,6668:
+DALnet:US, FL, Jacksonville:centurion.dal.net:6668,7000,7500:
+DALnet:US, IN, Hebron:hebron.dal.net:6661,6662,6663,7000:
+DALnet:US, IN, Hebron:hebron.dal.net:6661,6664,6669:
+DALnet:US, MA, Norwood:twisted.dal.net:6663,6665,6669:
+DALnet:US, MD, Manchester:qis.md.us.dal.net:6660,6664,6668:
+DALnet:US, MO, St.Louis:stlouis.DAL.net:6665,6666,6667,7000:
+DALnet:US, NC, Charlotte:uncc.DAL.net:6667,6668,6669,7000:
+DALnet:US, NY, New York City:liberty.dal.net:6662,6666,6669:
+DALnet:US, OH, Cleveland:barovia.dal.net:6661,6662,6663,7000:
+DALnet:US, OH, Toledo:glass.DAL.net:6667,7000:
+DALnet:US, OH, Toledo:glass.dal.net:6660,6661,6664:
+DALnet:US, OK, Tulsa:webzone.dal.net:6664,6665,6668,7000:
+DALnet:US, OK, Tulsa:webzone.dal.net:6665,6666,6669:
+DarkMyst:Random server:irc.darkmyst.org:6667:
+DarkServ:Random server:irc.darkserv.net:6664,6665,6666:
+Darkfire:Random server:irc.darkfire.net:6667,7000,8000:
+Darkfyre:Random server:irc.darkfyre.net:6667:
+Darktree:Random server:irc.darktree.net:6667:
+Deepspace:Disability network:irc.deepspace.org:6667:
+Different:Random server:irc.different.net:6667:
+Digarix:Random server:irc.digarix.net:6667:
+Digatech:Random server:irc.digatech.net:6667:
+Digitalirc:Random server:irc.digitalirc.net:6667:
+Dobbernet:Random server:irc.dobber.net:6667:
+DragonLynk:Random server:irc.dragonlynk.net:6661,6665,6666:
+Dreamcast:Random server:irc0.dreamcast.com:6667:
+Dreamnet:Random server:irc.dreamnet.org:6664,6665,6666:
+DwarfStar:Random server:irc.dwarfstar.net:6667:
+Dynastynet:Random server:irc.dynastynet.net:6667:
+EFnet:Australia, Telstra:efnet.telstra.net.au:6667:
+EFnet:CA, AB, Edmonton:irc.powersurfr.com:6667:
+EFnet:CA, ON, Toronto:irc2.magic.ca:6667:
+EFnet:CA, QB, Montreal:irc.etsmtl.ca:6667:
+EFnet:Canada, Cadvision:irc.cadvision.ab.ca:6667:
+EFnet:Canada, Internex:irc.io.org:6667:
+EFnet:Canada, Magic:irc.magic.mb.ca:6667:
+EFnet:Canada, McGill:irc.mcgill.ca:6667:
+EFnet:Canada, Mun:irc.cs.mun.ca:6667:
+EFnet:Canada, Polymtl:irc.polymtl.ca:6667:
+EFnet:Canada, Portal:portal.mbnet.mb.ca:6667:
+EFnet:Canada, Vianet:irc.vianet.on.ca:6667:
+EFnet:Canada, Yorku:irc.yorku.ca:6667:
+EFnet:EU, FI, Helsinki:efnet.cs.hut.fi:6667:
+EFnet:EU, FR, Paris:irc.isdnet.fr:6667,6668,6669:
+EFnet:EU, France:irc.ec-lille.fr:6667:
+EFnet:EU, Israel (ibm):irc.ibm.net.il:6667:
+EFnet:EU, Israel (tau):irc.tau.ac.il:6667:
+EFnet:EU, Israel (tech):irc.technion.ac.il:6667:
+EFnet:EU, NL, Amsterdam:efnet.vuurwerk.nl:6667:
+EFnet:EU, NO, Homelien:irc.homelien.no:6666,7000,7001:
+EFnet:EU, Netherlands:irc.nijenrode.nl:6667:
+EFnet:EU, Norway:irc.homelien.no:6667:
+EFnet:EU, RU, Moscow:irc.rt.ru:6662,6665,6668:
+EFnet:EU, SE, Dalarna:irc.du.se:6666,6668,6669:
+EFnet:EU, SE, Sweden:irc.light.se:6667:
+EFnet:EU, Sweden (gu):irc.gd.gu.se:6667:
+EFnet:EU, Sweden (lth):irc.df.lth.se:6667:
+EFnet:EU, UK, Bofh:irc.bofh.co.uk:6667:
+EFnet:EU, UK, Demon:efnet.demon.co.uk:6665,6666,6668,6669:
+EFnet:EU, UK, Demon:efnet.demon.co.uk:6665,6666,6668:
+EFnet:EU, UK, London:irc.ins.net.uk:6665,6666,6668:
+EFnet:ME, IL, Inter:irc.inter.net.il:6665,6666,6668:
+EFnet:US, AZ, Phoenix:irc.primenet.com:6667:
+EFnet:US, AZ, Tucson:irc.blackened.com:6667:
+EFnet:US, America OnLine:irc02.irc.aol.com:6667:
+EFnet:US, CA, Los Angeles:irc.west.gblx.net:6667:
+EFnet:US, CA, San Diego:irc.cerf.net:6667:
+EFnet:US, CA, San Luis Obispo:irc.prison.net:6666,6667:
+EFnet:US, CA, Stanford:irc.stanford.edu:6667:
+EFnet:US, CO, Colorado:irc.colorado.edu:5555,6665,6666:
+EFnet:US, CO, Colorado:irc.colorado.edu:6667:
+EFnet:US, FL, Miami:opus.bridge.net:6667:
+EFnet:US, GA, Atlanta (Emory):irc.emory.edu:6667:
+EFnet:US, GA, Atlanta (Mindspring):irc.mindspring.com:6660,6663,6668:
+EFnet:US, GA, Atlanta:irc.emory.edu:6667:
+EFnet:US, IL, Chicago (ais):irc.ais.net:6667:
+EFnet:US, IL, Chicago (mcs):irc.mcs.net:6667:
+EFnet:US, IL, Chicago:irc.mcs.net:6666,6667,6668:
+EFnet:US, IL, Chicago:irc.plur.net:6667:
+EFnet:US, MI, Ann Arbor:irc.umich.edu:6667:
+EFnet:US, MN, Twin Cities:irc.umn.edu:6665,6666,6668:
+EFnet:US, MO, Missouri:irc.mo.net:6667:
+EFnet:US, NY, New York:irc.east.gblx.net:6667:
+EFnet:US, Netcom0:ircd.netcom.com:6667:
+EFnet:US, Netcom1:irc.netcom.com:6667:
+EFnet:US, Netcom2:irc2.netcom.com:6667:
+EFnet:US, OK, Oklahoma:irc.ionet.net:6667:
+EFnet:US, PA, Ivyland:irc.voicenet.com:6667:
+EFnet:US, TX, Houston (Phoenix):irc.phoenix.net:6667:
+EFnet:US, TX, Houston (University):irc.tech.uh.edu:6667:
+EFnet:US, VA, Herndon:irc.psinet.com:6667:
+EFnet:US, WA, Seattle:irc.sprynet.com:6667:
+EICN:AU, Perth:Perth.WA.AU.EarthInt.Net:6667:
+EICN:EU, FI, Espoo:FOO.FI.EarthInt.Net:6667:
+EICN:US, FL, Tampa:Shadow.FL.US.EarthInt.Net:6667:
+EgyptianIRC:Random server:irc.egyptianirc.net:6667,6668,6669:
+EntertheGame:Random server:irc.enterthegame.com:6667,6668,6669:
+Escaped:Random server:irc.escaped.net:6661,6665,6668:
+Esprit:Random server:irc.esprit.net:6667:
+ExodusIRC:Random server:irc.exodusirc.net:6662,6663,6664:
+FDFnet:Random server:irc.fdf.net:6666,6668,9999:
+FEFnet:Random server:irc.fef.net:6667:
+FEFnet:UK, Quantum:quantum.uk.fef.net:6667:
+FEFnet:US, AZ, Phoenix:stf.fef.net:6667:
+FEFnet:US, CA, San Jose:vendetta.fef.net:6667:
+FEFnet:US, DC, Auburn:blizzard.fef.net:6667:
+FEFnet:US, NY, Long Island:liii.fef.net:6667:
+Forestnet:Random server:irc.forestnet.org:6667,7000:
+FreedomChat:Random server:chat.freedomchat.net:6667:
+Freenode:Random server:irc.freenode.net:6667:
+FunNet:Random server:irc.funnet.org:6667:
+GalaxyNet:AS, Singapore:pacific.sg.galaxynet.org:6667:
+GalaxyNet:AU, NZ, Auckland:auckland.nz.galaxynet.org:6666,6667,6668:
+GalaxyNet:Canada, Vancouver:vancouver.bc.ca.galaxynet.org:6667:
+GalaxyNet:EU, NO, Oslo:oslo.no.galaxynet.org:6666,6667,6668:
+GalaxyNet:US, Atlanta:atlanta.ga.us.galaxynet.org:6667:
+Galaxynet:AU, NZ, Auckland:auckland.nz.galaxynet.org:6661,6666,6668:
+Galaxynet:EU, BE, Online:online.be.galaxynet.org:6664,6666,6668:
+Galaxynet:EU, SE, Vltmedia:vltmedia.se.galaxynet.org:6662,6664,6666:
+Galaxynet:Random server:irc.galaxynet.org:6662,6663,6666:
+Galaxynet:US, FL, Florida:gymnet.us.galaxynet.org:6663,6666,6668:
+Galaxynet:US, WA, Seattle:freei.us.galaxynet.org:6661,6665,6666:
+Gamesnet:Random east US server:east.gamesnet.net:6667:
+Gamesnet:Random west US server:west.gamesnet.net:6667:
+GammaNet:US, Alfheim:Alfheim.NJ.US.gamma.net:6667:
+GammaNet:US, Chromium:Chromium.VA.US.gamma.net:6667:
+GammaNet:US, Galaxy:Galaxy.CT.US.gamma.net:6667:
+GammaNet:US, Wildstar:Wildstar.OK.US.gamma.net:6667:
+GizNet:Random server:irc.giznet.com:6666,6668,6669:
+Globalchat:Random server:irc.globalchat.org:6667:
+Grnet:Random EU server:gr.irc.gr:6667,7000:
+Grnet:Random US server:us.irc.gr:6667,7000:
+Grnet:Random server:srv.irc.gr:6667,7000:
+HabberNet:Random server:irc.habber.net:6667:
+HanIRC:Random server:irc.hanirc.org:6667:
+Hellenicnet:Random server:irc.mirc.gr:6667,7000:
+Hybnet:Random server:irc.hybnet.net:6667:
+ICQnet:Random server:irc.icq.com:6667:
+IRC-Hispano:Random server:irc.irc-hispano.org:6667:
+IRCLink:EU, NO, Alesund:alesund.no.eu.irclink.net:6667,6668,6669:
+IRCLink:EU, NO, Oslo:frogn.no.eu.irclink.net:6667,6668,6669:
+IRCLink:US, SC, Rock Hill:rockhill.sc.us.irclink.net:6667,6668,6669:
+IRCNet:AF, ZA, Sprint:irc-2.sprintlink.co.za:6667:
+IRCNet:AU, Dfat:irc.dfat.gov.au:6667:
+IRCNet:AU, Monash:yoyo.cc.monash.edu.au:6667:
+IRCNet:AU, Telstra:flute.telstra.net.au:6667:
+IRCNet:AU, Usyd:irc.usyd.edu.au:6667:
+IRCNet:CA, Quebec, Montreal:irc.openface.ca:6667:
+IRCNet:EU, Austria, Vienna:irc.wu-wien.ac.at:6667:
+IRCNet:EU, BE, Belnet:irc.belnet.be:6667:
+IRCNet:EU, DE, Berlin:irc.fu-berlin.de:6667:
+IRCNet:EU, DE, Muenchen:irc.informatik.tu-muenchen.de:6667:
+IRCNet:EU, DE, Stuttgart:irc.rus.uni-stuttgart.de:6667:
+IRCNet:EU, ES, Gondwana:gondwana.upc.es:6667:
+IRCNet:EU, FI, Espoo:irc.funet.fi:6667:
+IRCNet:EU, FI, Kuopio:irc.pspt.fi:6667:
+IRCNet:EU, FR, Eurecom:irc.eurecom.fr:6667:
+IRCNet:EU, FR, Lille:irc.ec-lille.fr:6667:
+IRCNet:EU, FR, Lyon:irc.univ-lyon1.fr:6667:
+IRCNet:EU, FR, Nice:irc.eurecom.fr:6667:
+IRCNet:EU, FR, Paris (Enst):irc.enst.fr:6667:
+IRCNet:EU, FR, Paris (Poly):sil.polytechnique.fr:6667:
+IRCNet:EU, FR, Poly:sil.polytechnique.fr:6667:
+IRCNet:EU, IS, Reykjavik:irc.isnet.is:6667:
+IRCNet:EU, IT, Pisa:irc.ccii.unipi.it:6667:
+IRCNet:EU, NL, Amsterdam:irc.xs4all.nl:6667:
+IRCNet:EU, NL, Nijmegen:irc2.sci.kun.nl:6667:
+IRCNet:EU, SE, Lulea:irc.ludd.luth.se:6667:
+IRCNet:EU, UK, London (BT):chat.btinternet.com:6667:
+IRCNet:EU, UK, London (Demon):ircnet.demon.co.uk:6665,6666,6668,6669:
+IRCNet:EU, UK, London (Imperial):stork.doc.ic.ac.uk:6667:
+IRCNet:EU, UK, London (Netcom):irc.netcom.net.uk:6667:
+IRCNet:US, AOL:irc02.irc.aol.com:6666:
+IRCNet:US, CA, Santa Clara:irc.aimnet.com:6667:
+IRCNet:US, IL, Chicago:irc.anet-chi.com:6667:
+IRCNet:US, MI, Taylor:irc.webbernet.net:6667:
+IRCNet:US, NY, New York:irc.stealth.net:6667:
+IRCNet:US, WA, Seattle:ircnet.sprynet.com:6667:
+IRChat-br:Random server:irc.irchat.com.br:6667:
+IRChat:Random server:irc.irchat.net:6661,6662,6666:
+IRCnet:AS, JP, Tokyo:irc.tokyo.wide.ad.jp:6667:
+IRCnet:AU, Melbourne:yoyo.cc.monash.edu.au:6668,6669,9990:
+IRCnet:AU, Sydney:irc.usyd.edu.au:6667:
+IRCnet:EU, AT, Linz:linz.irc.at:6666,6667,6668:
+IRCnet:EU, AT, Wien:vienna.irc.at:6666,6668,6669:
+IRCnet:EU, BE, Brussels:irc.belnet.be:6667:
+IRCnet:EU, BE, Zaventem:ircnet.wanadoo.be:6661,6664,6665:
+IRCnet:EU, CZ, Prague:irc.felk.cvut.cz:6667:
+IRCnet:EU, DE, Berlin:irc.fu-berlin.de:6665,6666,6668:
+IRCnet:EU, DE, Dusseldorf:irc.freenet.de:6665,6666,6668:
+IRCnet:EU, DE, Stuttgart:irc.belwue.de:6665,6666,6668:
+IRCnet:EU, DK, Copenhagen:irc.ircnet.dk:6667:
+IRCnet:EU, EE, Tallinn:irc.estpak.ee:6666,6667,6668:
+IRCnet:EU, FI, Espoo:irc.funet.fi:6661,6663,6668:
+IRCnet:EU, FI, Helsinki:irc.cs.hut.fi:6667:
+IRCnet:EU, FR, Nice:irc.eurecom.fr:6667:
+IRCnet:EU, FR, Paris:irc.fr.ircnet.net:6667:
+IRCnet:EU, GR, Thessaloniki:irc.ee.auth.gr:6666,6668,6669:
+IRCnet:EU, HU, Budapest:irc.elte.hu:6667:
+IRCnet:EU, IL, Haifa:ircnet.netvision.net.il:6661,6662,6663:
+IRCnet:EU, IS, Reykjavik:irc.ircnet.is:6661,6662,6666:
+IRCnet:EU, IS, Reykjavik:irc.ircnet.is:6663,6664,6668:
+IRCnet:EU, IT, Random:irc.ircd.it:6665,6666,6668:
+IRCnet:EU, IT, Rome:irc.tin.it:6665,6666,6668:
+IRCnet:EU, LV, Riga:irc.telia.lv:6666,6668,6669:
+IRCnet:EU, NL, Amsterdam (nlnet):irc.nl.uu.net:6661,6662,6663:
+IRCnet:EU, NL, Amsterdam (xs4all):irc.xs4all.nl:6663,6664,6665:
+IRCnet:EU, NL, Enschede:irc.snt.utwente.nl:6661,6662,6666:
+IRCnet:EU, NL, Nijmegen:irc.sci.kun.nl:6663,6664,6668:
+IRCnet:EU, NO, Oslo:irc.ifi.uio.no:6667:
+IRCnet:EU, NO, Trondheim:irc.pvv.ntnu.no:6667:
+IRCnet:EU, PL, Warsaw:warszawa.irc.pl:6666,6667,6668:
+IRCnet:EU, RU, Moscow:irc.msu.ru:6667:
+IRCnet:EU, SE, Lulea:irc.ludd.luth.se:6665,6666,6668:
+IRCnet:EU, UK, London (Btnet):chat.bt.net:6662,6663,6668:
+IRCnet:EU, UK, London (Demon):ircnet.demon.co.uk:6665,6666,6668:
+IRCnet:EU, UK, London (Netcom):irc.netcom.net.uk:6660,6661,6662:
+IRCnet:EU, UK, Warrington (u-net):irc.u-net.com:6660,6661,6665:
+IRCnet:US, NY, New York:irc.stealth.net:6663,6664,6665:
+IRCworld:Random server:irc.ircworld.org:6667:
+Icenet:AU, Perth:ois.perth.au.icenet.org:6667:
+Icenet:AU, Sydney:zip.syd.au.icenet.org:6667:
+Icenet:SA, Johannesburg:Snowflake.Jhb.Za.IceNet.Org:6667:
+Icenet:US, CA, San Diego:slurpee.ca.us.icenet.org:6667:
+Icenet:US, MO, Kansas City:frozen.mo.us.icenet.org:6667:
+Icenet:US, NC, Charlotte:WebServe.Nc.Us.IceNet.Org:6667:
+Icenet:US, NY, Warwick:Warwick.Ny.Us.IceNet.Org:6667:
+Icenet:US, WI, Ashland:Dockernet.Wi.Us.IceNet.Org:6667:
+Idealnet:US, Boston:irc.aasp.net:6667:
+Idealnet:US, New Jersey:irc.cps.k12.ny.us:6667:
+Idealnet:US, Tennesse:irc.pov.net:6667:
+Infatech:Random server:irc.infatech.net:6661,6663,6666:
+Infinity:Random server:irc.infinity-irc.org:6667:
+Infomatrix:Random server:irc.infomatrix.net:6667:
+IniQuity:EU, SE, Stockholm:StockHolm.SE.EU.iNiQuiTY.NeT:6667:
+IniQuity:US, KY, Louisville:Louisville.ky.us.Iniquity.net:6667:
+IniQuity:US, PA, Belle-Vernon:Belle-Vernon.PA.US.iNiQuiTY.Net:6667:
+IniQuity:US, WI, Milwaukee:Milwaukee.WI.US.iNiQuiTY.Net:6667:
+Irctoo:Random server:irc.irctoo.net:6667:
+Israelnet:Random server:irc.israel.net:6667:
+K0wNet:Random server:irc.k0w.net:6661,6665,6668:
+Kewl.org:EU, FR, Nanterre:nanterre.fr.eu.kewl.org:6667:
+Kewl.org:EU, UK, London:london.uk.eu.kewl.org:6667:
+Kewl.org:Random server:irc.kewl.org:6667:
+Kidsworld:EU, UK, Notts:notts.uk.eu.Kidsworld.org:6664,6666,6668:
+Kidsworld:US, CA, Los Angeles:Losangeles.ca.us.Kidsworld.org:6664,6666,6668:
+Kidsworld:US, CO, Denver:denver.co.us.kidsworld.org:6666,6668,6669:
+Kidsworld:US, IN, Jeffersonville:Jeffersonville.In.us.Kidsworld.org:6664,6666,6668:
+Kidsworld:US, MD, Baltimore:Baltimore.MD.US.Kidsworld.org:6664,6666,6668:
+Kidsworld:US, MD, Baltimore:baltimore.md.us.kidsworld.org:6666,6668,6669:
+Kidsworld:US, NY, New York:CCSD.NY.us.Kidsworld.org:6664,6666,6668:
+KissLand:Random server:irc.kissland.com:6667:
+Knightnet:AF, ZA, Durban:orc.dbn.za.knightnet.net:6667,5555:
+Knightnet:US, CA, Goldengate:goldengate.ca.us.knightnet.net:6667,5555:
+KreyNet:Random server:irc.krey.net:6667:
+Krushnet:Random server:irc.krushnet.org:6667:
+LagNet:AF, South Africa, Cape Town:babylon5.lagnet.org.za:6667:
+LagNet:AF, South Africa, Johannesburg:halcyon.lagnet.org.za:6667:
+LagNet:AF, South Africa, Pretoria:splash.lagnet.org.za:6667:
+LagNet:AF, ZA, Cape Town:reaper.lagnet.org.za:6667:
+LagNet:AF, ZA, Johannesburg:mystery.lagnet.org.za:6667:
+LagNet:Random server:irc.lagnet.org.za:6667:
+Librenet:Random server:irc.librenet.net:6667:
+Lunarnetirc:Random server:irc.lunarnetirc.org:6667:
+Lunatics:Random server:irc.lunatics.net:6667,6668,6669:
+MagicStar:Random server:irc.magicstar.net:6667:
+MavraNet:Random server:irc.mavra.net:6662,6663,6664:
+MediaDriven:Random server:irc.mediadriven.com:6667,6668,6669:
+Messique:Random server:irc.messique.org:6661,6664,6666:
+Millenia:Random server:irc.millenia.org:6664,6665,6666:
+Mozilla:Random server:irc.mozilla.org:6667,7000:
+Mysteria:Random server:irc.mysteria.net:6667,7000:
+Mystical:Random server:irc.mystical.net:6667,7000:
+NdrsNet:Random server:irc.ndrsnet.com:6666,6668,6669:
+Net-France:Random server:irc.net-france.com:6667:
+Nevernet:Random server:irc.nevernet.net:6667:
+NewNet:AF, South Africa:irc.sprintlink.co.za:6667:
+NewNet:EU, NL, Amsterdam:irc.desk.nl:6660,6663,6666,6669:
+NewNet:US, AZ, Phoenix:irc2.inficad.com:6667:
+NewNet:US, FL, Gainsville:irc.aohell.org:6667:
+NewNet:US, MD, Ocean City:irc.beachin.net:6667:
+NewNet:US, MI, Detroit:irc.tyme.net:6667:
+NewNet:US, NY, Buffalo:irc.localnet.com:6667:
+NewNet:US, NY, Freedom:vader.institute.wnyric.org:6667:
+NewNet:US, NY, Ysp:irc.ysp.com:6667:
+NewNet:US, TX, Houston:irc.localhost.net:6667:
+NewNet:US, WA, Seattle:irc.eskimo.com:6667:
+NewNet:US, WA, Silverdale:irc.tscnet.com:6667:
+Newnet:EU, DE, Trusted:irc.trusted-network.de:6666,7000:
+Newnet:EU, UK, Oasis:irc.oasis-net.net:6666,7000:
+Newnet:NZ, Auckland:irc.uplink.net.nz:6661,6662,6666:
+Newnet:Random server:irc.newnet.net:6665,6666,6667:
+Newnet:US, CA, Flagglerock:irc.fragglerock.org:6666,7000:
+Newnet:US, MA, Chelmsford:irc.chelmsford.com:6660,6665,6668:
+Newnet:US, VA, Randolph:irc.rma.edu:6665,6666,6668:
+Nexusirc:Random server:irc.nexusirc.org:6667:
+Nightstar:Random server:irc.nightstar.net:6665,6666,6668:
+NitroNet:Random server:irc.nitro.net:6667:
+Novernet:Random server:irc.novernet.com:6665,6666,6668:
+Novernet:US, GA, Augusta:irc.c-plusnet.com:6666,6668,6669:
+Novernet:US, MN, Minneapolis:chat.novernet.com:6667:
+Othernet:Random server:irc.othernet.org:6667:
+Othernet:US, FL, Miami:miami.fl.us.othernet.org:6667:
+Othernet:US, MO, StLouis:stlouis.mo.us.othernet.org:6667:
+Otherside:Random server:irc.othersideirc.net:6667:
+Outsiderz:Random server:irc.outsiderz.com:6667:
+OzChat:Random server:irc.ozchat.org:6667:
+OzOrg:AU, Adelaide:chariot.adelaide.oz.org:6666,6668,7000:
+OzOrg:AU, Goldcoast:onthenet.goldcoast.oz.org:6667:
+OzOrg:AU, Perth:iinet.perth.oz.org:6667:
+OzOrg:AU, Sydney (aussie):aussie.sydney.oz.org:6667:
+OzOrg:AU, Sydney (mpx):mpx.sydney.oz.org:6667:
+OzOrg:AU, Sydney:aussie.sydney.oz.org:6668,6669,7000:
+OzOrg:AU, Wollongong:wollongong.oz.org:6667:
+OzOrg:US, CA, Davis:davis.oz.org:6667:
+PTlink:Random server:irc.ptlink.net:6667:
+PTnet:EU, PT, Faro:ualg.ptnet.org:6667:
+PTnet:EU, PT, Lisboa:telepac2.ptnet.org:6667:
+ParaBolax:US, MA, Westminster:irc.parabolax.com:6667:
+ParaBolax:US, MI, Farmington Hills:irc.redhot.parabolax.com:6667:
+ParaBolax:US, NY, Madison:irc.bluewonder.parabolax.com:6667:
+Pinoycentral:Random server:chat.abs-cbn.com:6667:
+Planetarion:Random server:irc.planetarion.com:6667:
+PowerChat:Random server:irc.powerchat.org:6954,6956,6999:
+Pseudonet:Random server:irc.pseudonet.org:6667:
+QChat:Random server:irc.qchat.net:6667:
+QuakeNet:Random server:irc.quakenet.eu.org:6667,6668,6669:
+Raptanet:Random server:irc.rapta.net:6667:
+Raptornet:Random server:irc.raptornet.org:6667:
+Realirc:Random server:irc.realirc.org:6667:
+Rebelchat:Random server:irc.rebelchat.org:6667:
+Red-Latina:NA, MX, SanJose:irc.dalsom.net:6666,6667:
+Red-Latina:Random server:irc.red-latina.org:6667:
+RedLatina:NA, Mexico, Guadalajara:irc.udg.mx:6667:
+RedLatina:NA, Mexico, Mexico, D.F:yuju.anahuac.mx:6667:
+RedLatina:NA, Mexico, Morelos:academ02.mor.itesm.mx:6667:
+RedLatina:NA, México, Aguascalientes:sundi.basico.uaa.mx:6667:
+RedLatona:Random server:irc.redlatona.net:6667,6668:
+RedeBrasil:BR, CE, Ceara:irc.linline.com.br:6666,7000,7001:
+RedeBrasil:BR, PE, Recife:irc.hotlink.com.br:6665,6666,7000:
+RedeBrasil:BR, PR, Parana:irc.intermam.com.br:6660,6661,6662:
+RedeBrasil:BR, SP, Sao Paulo:irc.aserver.com.br:6662,6666,7000:
+RedeSul:BR, PR, Maringa:irc.wnet.com.br:6667:
+RedeSul:BR, SC, Blumenau:irc.braznet.com.br:6667,9001:
+RekorNet:Random server:irc.rekor.net:6665,6668,6669:
+Relicnet:Random server:irc.relic.net:6667:
+Relicnet:US, MA, Cyclone:cyclone.us.relic.net:6661,6662,6663:
+Risanet:Random server:irc.risanet.com:6667,6668,6669:
+Rusnet:EU, RU, Tomsk:irc.tsk.ru:7771,7773,7774:
+Rusnet:EU, RU, Vladivostok:irc.vladivostok.ru:6669,7771,7774:
+Rusnet:EU, UA, Kiev:irc.kar.net:6669,7770,7774:
+SandNet:FL, Ocala:ocala.fl.us.sandnet.org:7000:
+SandNet:GA, Atlanta:atlanta.ga.us.sandnet.org:6667:
+SandNet:MO, Stlouis:stlouis.mo.us.sandnet.org:6667,7000:
+SandNet:TX, Laredo:laredo.tx.us.sandnet.org:6667:
+Sandnet:Random server:irc.sandnet.net:6660,6662,6668:
+Sandnet:US, CA, Mystic:mystic.tides.sandnet.net:6662,6665,6666:
+Sandnet:US, NJ, DuneBuggy:dunebuggy.nj.sandnet.net:6661,6662,6666:
+Scunc:Random server:irc.scunc.net:6667:
+SexNet:Random server:irc.sexnet.org:6667:
+SgNet:Random server:irc.sgnet.org:6661,6663,6668:
+ShadowFire:Random server:irc.shadowfire.org:6667:
+ShadowWorld:Random server:irc.shadowworld.net:6667:
+SideNet:Random server:irc.sidenet.org:6661,6663,6666:
+Skyyenet:US, VA, Arlington:arlington.va.us.skyyenet.org:6667:
+Slashnet:Random server:irc.slashnet.org:6667:
+Solarchat:Random server:irc.solarchat.net:6667,7000:
+SorceryNet:CA, ON, Jordan:blackmagic.sorcery.net:9000:
+SorceryNet:EU, DE, Hamburg:atlantis.sorcery.net:9000:
+SorceryNet:EU, NL, Amsterdam:tardis.sorcery.net:9000:
+SorceryNet:US, MA, Cambridge:kechara.sorcery.net:9000:
+SorceryNet:US, MD, Clinton:quasar.sorcery.net:9000:
+Sorcerynet:EU, SE, Karlskrona:nexus.sorcery.net:6667,7000,9000:
+Sorcerynet:Random server:irc.sorcery.net:6667,7000,9000:
+Sorcerynet:US, CA, Palo Alto:kechara.sorcery.net:6667,7000,9000:
+Spamnet:Random server:irc.spamnet.org:6667,6668,6669:
+StarChat:AU, QLD, SouthernCross:boomer.qld.au.starchat.net:6668,6669,7000:
+StarChat:EU, NO, Asker:reality.no.eu.starchat.net:6668,6669,7000:
+StarChat:Random server:irc.starchat.net:6668,6669,7000:
+StarChat:US, CA, San Jose:sand.ca.us.starchat.net:6668,6669,7000:
+StarLink Org:US, CO, Denver:denver.co.us.starlink.org:6660,6662,6666:
+StarLink Org:US, NC, Durham:durham-r.nc.us.starlink.org:6661,6663,6668:
+StarLink Org:US, TX, WichitaFalls:wichitafalls.tx.us.starlink.org:6666,6667,6668:
+StarLink-irc:Random server:irc.starlink-irc.org:6667:
+StarLink-irc:US, MI, Rochester:rochester.mi.us.starlink-irc.org:6667:
+StarLink-irc:US, TX, Houston:houston.tx.us.starlink-irc.org:6667:
+StarLink:EU, NL, Utrecht:Utrecht.NL.EU.StarLink.Org:6667:
+StarLink:US, CO, Aspen:Aspen.CO.US.StarLink.Org:6667:
+StarLink:US, CO, Denver:Denver.CO.US.StarLink.Org:6667:
+StarLink:US, FL, Miami:Miami.FL.US.StarLink.Org:6667:
+StarLink:US, SC, Rockhill:RockHill.SC.US.StarLink.Org:6667:
+StarLinkIRC:US, GA, Atlanta:Atlanta.GA.US.Starlink-IRC.Org:6667:
+StarLinkIRC:US, MI, Rochester:Rochester.MI.US.Starlink-IRC.Org:6667:
+StarLinkIRC:US, OH, Cleveland:Cleveland.OH.US.Starlink-IRC.Org:6667:
+StarNet:US, FL, Jacksonville:irc.starnet.org:6667:
+StarNet:US, NY, New york:ny.us.starnet.org:6660,6662,6664,6666,6668:
+StarNet:US, OH, Cincinatti:oh.us.starnet.org:6662,6664,6666,6667:
+StarNet:US, TX, San Antonio:irc.xtatix.com:6667:
+StarWars-IRC:Random server:irc.starwars-irc.net:6663,6664,6665:
+StormNet:NZ, Auckland:auckland.nz.stormnet.org:6667:
+StormNet:US, CA, Valencia:valencia.ca.us.stormnet.org:6667:
+StormNet:US, FL, Brandon:brandon.fl.us.stormnet.org:6667:
+StormNet:US, IN, Indianapolis:indianapolis.in.us.stormnet.org:6667:
+StormNet:US, NC, Durham:durham.nc.us.stormnet.org:6667:
+Stormdancing:Random server:irc.stormdancing.net:6664,6668,7000:
+Sub-city:Random server:irc.sub-city.net:6668,6669,7000:
+Superchat:Random server:irc.superchat.org:6660,6664,6665:
+Superchat:US, GA, Cartersville:superchat.innerx.net:6660,6662,6664,6666,6668:
+Superchat:US, LA, Houma:chat.cajun.net:6660,6662,6664,6666,6668:
+Superchat:US, NJ, New Brunswick:irc.superlink.net:6660,6662,6664,6666,6668:
+Superchat:US, PA, Pittsburgh:irc.pitnet.net:6660,6662,6664,6666,6668:
+Superchat:US, TX, Dallas:irc.airmail.net:6660,6662,6664,6666,6668:
+Superonline:Random server:irc.superonline.com:6663,6664,6669:
+Sysopnet:Random server:irc.sysopnet.org:6666,6667,6668:
+TR-net:EU, TR, Ankara:irc.dominet.com.tr:6667:
+TR-net:EU, Tr, Istanbul:irc.teklan.com.tr:6667:
+TRcom:Random server:irc.trcom.net:6666,6668,6669:
+Techdreams:Random server:irc.techdreams.net:6667:
+Telstra:Random server:irc.telstra.com:6667,6668,6669:
+Tri-net:Random server:irc.tri-net.org:6667:
+UltraIRC:Random server:irc.ultrairc.net:6667:
+UltraNet:EU, FR, Neuilly:Neuilly.FR.EU.UltraNET.Org:6668:
+UltraNet:EU, UK, London:London.UK.EU.UltraNET.Org:6667:
+UltraNet:US, MS, Vicksburg:Vicksburg.MS.US.UltraNET.Org:6667:
+UltraNet:US, PA, Philadelphia:Philadelphia.PA.US.Ultranet.Org:6667:
+UnderZ:Random server:irc.underz.org:6667,6668:
+Undergrid:US, AS, Little Rock:tomes.undergrid.com:6665,6667,6669,7000,10000:
+Undergrid:US, CA, Chatsworth:ribbit.undergrid.com:10000:
+Undergrid:US, IA, Hawarden:starwars.undergrid.com:667,7000,10000:
+Undergrid:US, OK, Oklahoma City:thor.undergrid.com:6667,7000,10000:
+Undernet:CA, ON, Toronto:toronto.on.ca.undernet.org:6661,6662,6666:
+Undernet:CA, QC, Montreal:montreal.qu.ca.undernet.org:6660,6661,6664:
+Undernet:Canada, Montreal:montreal.qu.ca.undernet.org:6667,6668,6669,7777:
+Undernet:Canada, Toronto:toronto.on.ca.undernet.org:6667,6668,6669,7777:
+Undernet:Canada, Vancouver:vancouver.bc.ca.undernet.org:6666,6667,6668,6669:
+Undernet:EU, AT, Graz:graz.at.eu.undernet.org:6661,6663,6668:
+Undernet:EU, BE, Antwerp:flanders.be.eu.undernet.org:6660,6662,6663:
+Undernet:EU, BE, Brussels:brussels.be.eu.undernet.org:6661,6663,6668:
+Undernet:EU, DE, Regensburg:regensburg.de.eu.undernet.org:6667,6668:
+Undernet:EU, FI, Espoo:espoo.fi.eu.undernet.org:6667,6668:
+Undernet:EU, FR, Caen:caen.fr.eu.undernet.org:6666,6668,6669:
+Undernet:EU, FR, Caen:caen.fr.eu.undernet.org:6667,6668:
+Undernet:EU, FR, Paris:paris.fr.eu.undernet.org:6662,6663,6666:
+Undernet:EU, NL, Amsterdam:amsterdam.nl.eu.undernet.org:6667,6668,6669,7000:
+Undernet:EU, NL, Diemen:diemen.nl.eu.undernet.org:6665,6666,6668:
+Undernet:EU, NL, Diemen:diemen.nl.eu.undernet.org:6667,6668,6669,7000:
+Undernet:EU, NL, Haarlem:haarlem.nl.eu.undernet.org:6660,6662,6666:
+Undernet:EU, NO, Oslo:oslo.no.eu.undernet.org:6662,6664,6668:
+Undernet:EU, NO, Oslo:oslo.no.eu.undernet.org:6667,6668:
+Undernet:EU, SE, Lulea:lulea.se.eu.undernet.org:6667,7000,7777:
+Undernet:EU, SE, Stockholm:stockholm.se.eu.undernet.org:6661,6662,6664:
+Undernet:EU, UK, London:london.uk.eu.undernet.org:6666,6667:
+Undernet:EU, UK, London:london.uk.eu.undernet.org:6666,6668,6669:
+Undernet:EU, UK, Surrey:surrey.uk.eu.undernet.org:6662,6665,6666:
+Undernet:NZ, Auckland:auckland.nz.undernet.org:6667:
+Undernet:US, AZ, Phoenix:phoenix.az.us.undernet.org:6667,6668,6669:
+Undernet:US, America OnLine:irc01.irc.aol.com:6667:
+Undernet:US, CA, San Diego:sandiego.ca.us.undernet.org:6665,6666,6668:
+Undernet:US, CA, SanDiego:sandiego.ca.us.undernet.org:6667,6668,7000,7777:
+Undernet:US, DC, Washington:washington.dc.us.undernet.org:6660,6665,6668:
+Undernet:US, DC, Washington:washington.dc.us.undernet.org:6661,6663,6665,6667:
+Undernet:US, IL, Chicago:chicago.il.us.Undernet.org:6667,6668,6669,7777:
+Undernet:US, KS, Manhattan:manhattan.ks.us.undernet.org:6662,6664,6665:
+Undernet:US, KS, Manhattan:manhattan.ks.us.undernet.org:6667,6668,7000,7777:
+Undernet:US, MA, Lowell:lowell.ma.us.undernet.org:6667,6668,6669,7777:
+Undernet:US, MO, Springfield:springfield.mo.us.undernet.org:6667,6668,6669,7000:
+Undernet:US, MO, St.Louis:stlouis.mo.us.undernet.org:6666,6667,6668,6669:
+Undernet:US, NV, Las Vegas:lasvegas.nv.us.undernet.org:6662,6663,6668:
+Undernet:US, PA , Pittsburg:pittsburgh.pa.us.undernet.org:6667,6668,6669:
+Undernet:US, TX, Austin:austin.tx.us.undernet.org:6661,6663,6664:
+Undernet:US, TX, Dallas:dallas.tx.us.undernet.org:6667,6668,6669:
+Undernet:US, UT, Saltlake:saltlake.ut.us.undernet.org:6663,6665,6666:
+Undernet:US, VA, Arlington:arlington.va.us.undernet.org:6661,6663,6665:
+Undernet:US, VA, Blacksburg:blacksburg.va.us.undernet.org:6667,6668,7000,7777:
+Undernet:US, VA, McLean:mclean.va.us.undernet.org:6666,6668,6669:
+UnionLatina:Random server:irc.unionlatina.org:6667:
+UnitedChat Net:Random server:irc.unitedchat.net:6667:
+UnitedChat Org:Random server:irc.unitedchat.org:6667:
+Univers:Random server:irc.univers.org:6665,6666,6668:
+Vidgamechat:Random server:irc.vidgamechat.com:6667:
+Virtuanet:Random server:irc.virtuanet.org:6662,6666,6669:
+Vitamina:Random server:irc-rr.vitamina.ca:6667:
+Warpednet:Random server:irc.warped.net:6667:
+WebNet:US, CA, Santa Clara:webmaster.ca.us.webchat.org:7000:
+WebNet:US, IL, Geneva:inil.il.us.webchat.org:7000:
+WebNet:US, MN, St.Paul:mwweb.oh.us.webchat.org:7000:
+WebNet:US, OK, Tulsa:webzone.ok.us.webchat.org:7000:
+WebNet:US, WA, Seattle:prostar.wa.us.webchat.org:7000:
+Webnet:Random server:irc.webchat.org:6668,6669,7000:
+Webnet:US, AL, Dothan:wiregrass.al.us.webchat.org:6668,6669,7000:
+Webnet:US, CA, Santa Clara:webmaster.ca.us.webchat.org:6662,6663,6664:
+Webnet:US, MA, Boston:greennet.ma.us.webchat.org:6668,6669,7000:
+WonKnet:Random server:irc.wonk.net:6668,6669,7000:
+WorldIRC:Random server:irc.worldirc.org:6661,6662,6665:
+WorldIRC:US, IL, Chicago:Chicago.Il.Us.WorldIRC.Org:6667:
+WorldIRC:US, MD, Baltimore:Baltimore.Md.US.WorldIRC.Org:6667:
+WorldIRC:US, MI, GrandRapids:GrandRapids.Mi.Us.WorldIRC.Org:6667:
+WorldIRC:US, OK, Norman:Norman.Ok.US.WorldIRC.Org:6667:
+Wwfin:US, FL, Gainesville:irc.afn.org:6667:
+Wwfin:US, IL, Champaign:prairienet.org:6667:
+Wwfin:US, MI, Grand Rapids:irc.grfn.org:6667:
+Wwfin:US, MI, Holland:irc.macatawa.org:6667:
+XNet:Random server:irc.xnet.org:6667:
+XWorld:EU, DE, Wesel:startrek.xworld.org:6667:
+XWorld:Random server:irc.xworld.org:6667:
+XWorld:US, FL, Pensacola:llama.xworld.org:6667:
+XWorld:US, KS ,Kansas City:irc.xworld.org:6667:
+XWorld:US, SC, Rockhill:chat.xworld.org:6667:
+Xevion:Random server:irc.xevion.net:6667,7000:
+ZAnet Net:AF, ZA, CI (lia):lia.zanet.net:6667:
+ZAnet Net:AF, ZA, MWeb (timewiz):timewiz.zanet.net:6667:
+ZAnet Org:AF, ZA, Cape Town (gaspode):gaspode.zanet.org.za:6667:
+ZAnet Org:AF, ZA, Johannesburg (is):is.zanet.org.za:6667:
+ZAnet Org:AF, ZA, Midrand (ethereal):ethereal.zanet.org.za:6660,6666:
+ZAnet:AF, ZA, Cape Town:gaspode.zanet.org.za:6667:
+ZAnet:AF, ZA, Cape Town:guru.zanet.org.za:6667:
+ZAnet:AF, ZA, Johannesburg:is.zanet.org.za:6667:
+ZAnet:AF, ZA, Midrand:ibi.zanet.org.za:6667:
+ZUHnet:Random server:irc.zuh.net:6667:
+ZiRC:Random server:irc.zirc.org:6660,6662,6665:
+Zurna:Random server:irc.zurna.net:6667:
+euIRC:Random server:irc.euirc.net:6665,6666,6668:
diff --git a/ksirc/KSPrefs/Makefile.am b/ksirc/KSPrefs/Makefile.am
new file mode 100644
index 00000000..64d38261
--- /dev/null
+++ b/ksirc/KSPrefs/Makefile.am
@@ -0,0 +1,50 @@
+
+INCLUDES= -I$(srcdir)/.. $(all_includes)
+
+noinst_LTLIBRARIES = libksprefs.la
+
+libksprefs_la_SOURCES = \
+ ksprefs.cpp \
+ page_colorsbase.ui\
+ page_generalbase.ui\
+ page_rmbmenubase.ui \
+ page_servchanbase.ui \
+ page_startupbase.ui \
+ page_colors.cpp \
+ page_general.cpp \
+ page_rmbmenu.cpp \
+ page_servchan.cpp \
+ page_startup.cpp \
+ page_font.cpp \
+ page_autoconnectbase.ui \
+ page_autoconnect.cpp \
+ page_irccolorsbase.ui\
+ page_irccolors.cpp \
+ page_looknfeelbase.ui \
+ page_looknfeel.cpp \
+ page_shortcutsbase.ui \
+ page_shortcuts.cpp
+
+METASOURCES = AUTO
+
+noinst_HEADERS = ksprefs.h \
+ page_colorsbase.h \
+ page_generalbase.h \
+ page_rmbmenubase.h \
+ page_servchanbase.h \
+ page_startupbase.h \
+ page_colors.h \
+ page_general.h \
+ page_rmbmenu.h \
+ page_servchan.h \
+ page_startup.h \
+ page_font.h \
+ page_autoconnectbase.h \
+ page_autoconnect.h \
+ page_irccolors.h \
+ page_irccolorsbase.h \
+ page_looknfeel.h \
+ page_looknfeelbase.h \
+ page_shortcutsbase.h \
+ page_shortcuts.h
+
diff --git a/ksirc/KSPrefs/ksprefs.cpp b/ksirc/KSPrefs/ksprefs.cpp
new file mode 100644
index 00000000..2bf97e84
--- /dev/null
+++ b/ksirc/KSPrefs/ksprefs.cpp
@@ -0,0 +1,244 @@
+/***************************************************************************
+ * *
+ * 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 <qlayout.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+
+#include <kdebug.h>
+
+#include "ksprefs.moc"
+#include "page_general.h"
+#include "page_colors.h"
+#include "page_startup.h"
+#include "page_rmbmenu.h"
+#include "page_servchan.h"
+#include "page_font.h"
+#include "page_autoconnect.h"
+#include "page_irccolors.h"
+#include "page_looknfeel.h"
+#include "page_shortcuts.h"
+
+KSPrefs::KSPrefs(QWidget * parent, const char * name):
+ KDialogBase(KDialogBase::IconList, i18n("Configure KSirc"),
+ KDialogBase::Help | KDialogBase::Ok | KDialogBase::Apply | KDialogBase::Cancel | KDialogBase::Default,
+ KDialogBase::Ok, parent, name)
+{
+ setWFlags( getWFlags() | WDestructiveClose );
+ QFrame *itemLooknFeel= addPage( i18n( "Look and Feel" ), i18n( "Controls how kSirc looks" ), BarIcon( "ksirc", KIcon::SizeMedium ) );
+ QFrame *itemGeneral = addPage( i18n( "General" ), i18n( "General KSirc Settings" ), BarIcon( "ksirc", KIcon::SizeMedium ) );
+ QFrame *itemStartup = addPage( i18n( "Startup" ), i18n( "KSirc Startup Settings" ), BarIcon( "gear", KIcon::SizeMedium ) );
+ QFrame *itemColors = addPage( i18n( "Colors" ), i18n( "KSirc Color Settings" ), BarIcon( "colors", KIcon::SizeMedium ) );
+ QFrame *itemIRCColors = addPage( i18n( "IRC Colors" ), i18n( "KSirc IRC Color Settings" ), BarIcon( "colors", KIcon::SizeMedium ) );
+
+ QFrame *itemRMBMenu = addPage( i18n( "User Menu" ), i18n( "User Menu Configuration" ), BarIcon( "", KIcon::SizeMedium ) );
+ QFrame *itemServChan = addPage( i18n( "Server/Channel" ), i18n( "Server/Channel Configuration" ), BarIcon( "", KIcon::SizeMedium ) );
+ QFrame *itemAutoConnect = addPage( i18n( "Auto Connect" ), i18n( "Auto Connect Configuration" ), BarIcon( "", KIcon::SizeMedium ) );
+ QFrame *itemFont = addPage( i18n( "Fonts" ), i18n( "Font Settings" ), BarIcon( "fonts", KIcon::SizeMedium ) );
+ QFrame *itemShortcuts = addPage( i18n( "Shortcuts" ), i18n( "Shortcut Configuration" ), BarIcon( "", KIcon::SizeMedium ) );
+
+ QVBoxLayout *fontTopLayout = new QVBoxLayout( itemFont, 0, 6 );
+
+
+ QVBoxLayout *looknfeelTopLayout= new QVBoxLayout( itemLooknFeel, 0, 6 );
+ QVBoxLayout *generalTopLayout = new QVBoxLayout( itemGeneral, 0, 6 );
+ QVBoxLayout *startupTopLayout = new QVBoxLayout( itemStartup, 0, 6 );
+ QVBoxLayout *colorsTopLayout = new QVBoxLayout( itemColors, 0, 6 );
+ QVBoxLayout *ircColorsTopLayout = new QVBoxLayout( itemIRCColors, 0, 6 );
+
+ QVBoxLayout *rmbMenuTopLayout = new QVBoxLayout( itemRMBMenu, 0, 6 );
+ QVBoxLayout *servChanTopLayout = new QVBoxLayout( itemServChan, 0, 6 );
+ QVBoxLayout *autoConnectTopLayout = new QVBoxLayout( itemAutoConnect, 0, 6 );
+ QVBoxLayout *autoShortcutsTopLayout = new QVBoxLayout( itemShortcuts, 0, 6 );
+
+ pageLooknFeel= new PageLooknFeel( itemLooknFeel );
+ pageGeneral = new PageGeneral( itemGeneral );
+ pageStartup = new PageStartup( itemStartup );
+ pageColors = new PageColors( itemColors );
+ pageIRCColors = new PageIRCColors( itemIRCColors );
+ pageFont = new PageFont( itemFont );
+ pageRMBMenu = new PageRMBMenu( itemRMBMenu );
+ pageServChan = new PageServChan( itemServChan );
+ pageAutoConnect = new PageAutoConnect( itemAutoConnect );
+ pageShortcuts = new PageShortcuts( itemShortcuts );
+
+ connect(pageLooknFeel, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageGeneral, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageStartup, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageColors, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageIRCColors, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageFont, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageRMBMenu, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageServChan, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageAutoConnect, SIGNAL( modified() ), SLOT( modified() ) );
+ connect(pageShortcuts, SIGNAL( modified() ), SLOT( modified() ) );
+
+ connect(this, SIGNAL( applyClicked() ), this, SLOT( saveConfig() ) );
+ connect(this, SIGNAL( okClicked() ), this, SLOT( saveConfig() ) );
+ connect(this, SIGNAL( defaultClicked() ), this, SLOT(defaultConfig() ) );
+
+ looknfeelTopLayout->addWidget( pageLooknFeel );
+ generalTopLayout->addWidget( pageGeneral );
+ startupTopLayout->addWidget( pageStartup );
+ colorsTopLayout->addWidget( pageColors );
+ ircColorsTopLayout->addWidget( pageIRCColors );
+ fontTopLayout->addWidget( pageFont );
+ rmbMenuTopLayout->addWidget( pageRMBMenu );
+ servChanTopLayout->addWidget( pageServChan );
+ autoConnectTopLayout->addWidget( pageAutoConnect );
+ autoShortcutsTopLayout->addWidget( pageShortcuts );
+ //enableButtonSeperator( true );
+
+ readConfig();
+ enableButtonApply( false );
+
+ pSCDirty = 0;
+ pACDirty = 0;
+}
+
+KSPrefs::~KSPrefs()
+{
+}
+
+void KSPrefs::readConfig()
+{
+ // apply by calling readConfig in each page
+
+ pageLooknFeel->readConfig();
+ pageGeneral->readConfig();
+ pageColors->readConfig();
+ pageIRCColors->readConfig();
+ pageStartup->readConfig();
+ pageFont->readConfig();
+ pageRMBMenu->readConfig();
+ pageServChan->readConfig();
+ pageAutoConnect->readConfig();
+ pageShortcuts->readConfig();
+
+}
+
+void KSPrefs::saveConfig()
+{
+ // apply by calling saveConfig in each page
+ // use setDirty flag for each page and
+ // emit update() appropriate
+
+ if ( dirty & KSOptions::General )
+ pageGeneral->saveConfig();
+ if ( dirty & KSOptions::Startup )
+ pageStartup->saveConfig();
+ if ( dirty & KSOptions::Colors )
+ pageColors->saveConfig();
+ if ( dirty & KSOptions::Colors )
+ pageIRCColors->saveConfig();
+ if ( dirty & KSOptions::Colors )
+ pageFont->saveConfig();
+ if ( dirty & KSOptions::RMBMenu )
+ pageRMBMenu->saveConfig();
+ if ( dirty & pSCDirty )
+ pageServChan->saveConfig();
+ if ( dirty & pACDirty )
+ pageAutoConnect->saveConfig();
+ if ( dirty & pLFDirty )
+ pageLooknFeel->saveConfig();
+ if ( dirty & pShortDirty )
+ pageShortcuts->saveConfig();
+
+
+ ksopts->save( dirty );
+ emit update( dirty );
+ enableButtonApply( false );
+}
+
+
+void KSPrefs::defaultConfig()
+{
+ // apply by calling defaultConfig in current page
+
+ switch (activePageIndex())
+ {
+ case 0:
+ kdDebug(5008) << "Doing looknfeel" << endl;
+ pageLooknFeel->defaultConfig();
+ break;
+ case 1:
+ kdDebug(5008) << "Doing general" << endl;
+ pageGeneral->defaultConfig();
+ break;
+ case 2:
+ kdDebug(5008) << "Doing startup" << endl;
+ pageStartup->defaultConfig();
+ break;
+ case 3:
+ kdDebug(5008) << "Doing colors" << endl;
+ pageColors->defaultConfig();
+ break;
+ case 4:
+ kdDebug(5008) << "Doing IRC colors" << endl;
+ pageIRCColors->defaultConfig();
+ break;
+ case 5:
+ kdDebug(5008) << "Doing RMBMenu" << endl;
+ pageRMBMenu->defaultConfig();
+ break;
+ case 6:
+ kdDebug(5008) << "Doing ServerChan" << endl;
+ pageServChan->defaultConfig();
+ break;
+ case 7:
+ kdDebug(5008) << "Doing AutoConnect" << endl;
+ pageAutoConnect->defaultConfig();
+ case 8:
+ kdDebug(5008) << "Doing font" << endl;
+ pageFont->defaultConfig();
+ break;
+ case 9:
+ kdDebug(5008) << "Doing shortcuts" << endl;
+ pageShortcuts->defaultConfig();
+ break;
+
+ }
+}
+
+void KSPrefs::modified()
+{
+ if ( sender() == pageGeneral )
+ dirty |= static_cast<int>( KSOptions::General );
+ if ( sender() == pageStartup ) {
+ dirty |= static_cast<int>( KSOptions::Startup );
+ dirty |= static_cast<int>( KSOptions::Servers );
+ }
+ if ( sender() == pageColors )
+ dirty |= static_cast<int>( KSOptions::Colors );
+ if ( sender() == pageIRCColors )
+ dirty |= static_cast<int>( KSOptions::Colors );
+ if ( sender() == pageFont )
+ dirty |= static_cast<int>( KSOptions::Colors );
+ if ( sender() == pageRMBMenu )
+ dirty |= static_cast<int>( KSOptions::RMBMenu );
+ if ( sender() == pageServChan ) {
+ dirty |= 1;
+ pSCDirty = 1;
+ }
+ if ( sender() == pageAutoConnect ) {
+ dirty |= 1;
+ pACDirty = 1;
+ }
+ if ( sender() == pageLooknFeel ) {
+ dirty |= 1;
+ pLFDirty = 1;
+ }
+ if ( sender() == pageShortcuts ) {
+ dirty |= 1;
+ pShortDirty = 1;
+ }
+
+ enableButtonApply( dirty );
+}
diff --git a/ksirc/KSPrefs/ksprefs.h b/ksirc/KSPrefs/ksprefs.h
new file mode 100644
index 00000000..6c41fd9a
--- /dev/null
+++ b/ksirc/KSPrefs/ksprefs.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ * *
+ * 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 KSPREFS_H
+#define KSPREFS_H
+
+#include <kdialogbase.h>
+
+class PageGeneral;
+class PageColors;
+class PageIRCColors;
+class PageStartup;
+class PageRMBMenu;
+class PageServChan;
+class PageAutoConnect;
+class PageLooknFeel;
+class PageShortcuts;
+
+class PageFont; /* For the font settings */
+
+
+class KSPrefs : public KDialogBase
+{
+
+Q_OBJECT
+public:
+ KSPrefs(QWidget * parent = 0, const char * name = 0);
+ ~KSPrefs();
+
+public slots:
+ void saveConfig();
+ void defaultConfig();
+ void readConfig();
+ void modified();
+
+signals:
+ void update(int);
+
+private:
+ PageColors *pageColors;
+ PageIRCColors *pageIRCColors;
+ PageGeneral *pageGeneral;
+ PageRMBMenu *pageRMBMenu;
+ PageStartup *pageStartup;
+ PageServChan *pageServChan;
+ PageAutoConnect *pageAutoConnect;
+ PageLooknFeel *pageLooknFeel;
+ PageShortcuts *pageShortcuts;
+
+ int pSCDirty;
+ int pACDirty;
+ int pLFDirty;
+ int pShortDirty;
+
+ PageFont *pageFont; /* Font settings page */
+ int dirty;
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_autoconnect.cpp b/ksirc/KSPrefs/page_autoconnect.cpp
new file mode 100644
index 00000000..a6532915
--- /dev/null
+++ b/ksirc/KSPrefs/page_autoconnect.cpp
@@ -0,0 +1,309 @@
+/***************************************************************************
+ * *
+ * 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 "page_autoconnect.h"
+
+
+#include <qregexp.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <qcheckbox.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <knuminput.h>
+
+#define NAME 0
+#define PK 1
+#define PASS 2
+#define SSL 3
+
+PageAutoConnect::PageAutoConnect( QWidget *parent, const char *name ) : PageAutoConnectBase( parent, name)
+{
+ KLVAutoConnect->setSorting( 0 );
+ //KLVAutoConnect->header()->hide();
+
+ AddPB->setEnabled(FALSE);
+ DeletePB->setEnabled(FALSE);
+ PortKI->setValue(6667);
+}
+
+PageAutoConnect::~PageAutoConnect()
+{
+}
+
+void PageAutoConnect::saveConfig()
+{
+
+ KConfig *conf = kapp->config();
+ conf->setGroup("AutoConnect");
+
+ QStringList servers;
+
+ for(QListViewItem *it = KLVAutoConnect->firstChild();
+ it != 0;
+ it = it->nextSibling()){
+
+ QString server = it->text(NAME);
+
+ if(it->text(PK).length() > 0)
+ server += ":" + it->text(1);
+ if(it->text(SSL).length() > 0)
+ server += " (SSL)";
+ if(it->text(PASS).length() > 0)
+ server += QString(" (pass: %1)").arg(it->text(PASS));
+
+ servers << server;
+
+ QStringList channels;
+
+ for(QListViewItem *ch = it->firstChild();
+ ch != 0;
+ ch = ch->nextSibling()){
+ QString channel;
+
+ channel = ch->text(NAME);
+
+ if(ch->text(PK).length() > 0)
+ channel += QString(" (key: %1)").arg(ch->text(PK));
+
+ channels << channel;
+
+ }
+
+ channels.sort();
+ conf->writeEntry(server, channels);
+
+ }
+
+ servers.sort();
+
+ conf->writeEntry("Servers", servers);
+
+}
+
+void PageAutoConnect::readConfig()
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("AutoConnect");
+ QStringList servers = conf->readListEntry("Servers");
+ servers.sort();
+ QStringList::ConstIterator ser = servers.begin();
+ for( ; ser != servers.end(); ser++){
+ QStringList channels = conf->readListEntry(*ser);
+ QString server = *ser;
+ QString port = "6667";
+ QString ssl = QString::null;
+ QString pass = QString::null;
+
+ QRegExp rx("(.+) \\(SSL\\)(.*)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1) + rx.cap(3);
+ ssl = i18n("SSL");
+ }
+ rx.setPattern("(.+) \\(pass: (\\S+)\\)(.*)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1) + rx.cap(3);
+ pass = rx.cap(2);
+ }
+ rx.setPattern("([^: ]+):(\\d+)");
+ if(rx.search(server) >= 0){
+ kdDebug(5008) << server << ": Has port:" << rx.cap(2) << endl;
+ server = rx.cap(1);
+ port = rx.cap(2);
+ }
+ kdDebug(5008) << server << ": Done " << port << " " << ssl << " " << pass << endl;
+ QListViewItem *s = new QListViewItem(KLVAutoConnect, server, port, pass, ssl);
+ s->setOpen(TRUE);
+ channels.sort();
+ QStringList::ConstIterator chan = channels.begin();
+ for(; chan != channels.end(); chan++){
+ QString channel = *chan;
+ QString key = QString::null;
+ QRegExp crx("(.+) \\(key: (\\S+)\\)");
+ if(crx.search(channel) >= 0){
+ channel = crx.cap(1);
+ key = crx.cap(2);
+ }
+ new QListViewItem(s, channel, key);
+ }
+ }
+}
+
+void PageAutoConnect::defaultConfig()
+{
+ KLVAutoConnect->clear();
+ readConfig( );
+}
+
+void PageAutoConnect::changed()
+{
+ emit modified();
+}
+
+void PageAutoConnect::add_pressed()
+{
+ int fnd = 0;
+ QListViewItem *s = 0;
+
+ s = KLVAutoConnect->selectedItem();
+ if(!s){ /* new item */
+ QString server = ServerLE->text();
+ QString ssl = QString::null;
+ QString port;
+
+ port.setNum(PortKI->value());
+ if(sslCB->isChecked())
+ ssl = i18n("SSL");
+
+ s = new QListViewItem(KLVAutoConnect, server, port, PassLE->text(), ssl);
+ s->setOpen(TRUE);
+ s = new QListViewItem(s, ChannelLE->text(), KeyLE->text());
+ KLVAutoConnect->setCurrentItem(s);
+ }
+ else { /* update the existing one */
+ QListViewItem *parent;
+ QListViewItem *child;
+
+ if(s->parent()){
+ parent = s->parent();
+ child = s;
+ }
+ else {
+ parent = s;
+ child = 0x0;
+ }
+
+ parent->setText(NAME, ServerLE->text());
+ parent->setText(PK, QString("%1").arg(PortKI->value()));
+ parent->setText(PASS, PassLE->text());
+ if(sslCB->isChecked())
+ parent->setText(SSL, i18n("SSL"));
+ else
+ parent->setText(SSL, QString::null);
+
+ if(child){
+ child->setText(NAME, ChannelLE->text());
+ child->setText(PK, KeyLE->text());
+ }
+ else {
+ if(ChannelLE->text().length() > 0){
+ fnd = 0;
+ QListViewItem *c = parent->firstChild();
+ for( ; c != 0 && fnd == 0; c = c->nextSibling()){
+ if(c->text(NAME) == ChannelLE->text()){
+ c->setText(PK, KeyLE->text());
+ fnd = 1;
+ }
+ }
+ if(fnd == 0){
+ new QListViewItem(parent, ChannelLE->text(), KeyLE->text());
+ }
+ }
+ }
+
+ }
+ changed();
+}
+
+void PageAutoConnect::new_pressed()
+{
+ AddPB->setText(i18n("&Add"));
+ ServerLE->clear();
+ ChannelLE->clear();
+ sslCB->setChecked(false);
+ PassLE->clear();
+ KeyLE->clear();
+ KLVAutoConnect->clearSelection();
+ PortKI->setValue(6667) ;
+}
+
+void PageAutoConnect::delete_pressed()
+{
+ for(QListViewItem *it = KLVAutoConnect->firstChild();
+ it != 0;
+ it = it->nextSibling()){
+
+ if(it->text(NAME) == ServerLE->text()){
+ if(ChannelLE->text().isEmpty() == FALSE){
+ for(QListViewItem *ch = it->firstChild();
+ ch != 0;
+ ch = ch->nextSibling()){
+ if(ch->text(NAME) == ChannelLE->text()){
+ delete ch;
+ changed();
+ ChannelLE->clear();
+ ServerLE->clear();
+ return;
+ }
+ }
+ }
+ else {
+ delete it;
+ changed();
+ ServerLE->clear();
+ return;
+ }
+ }
+ }
+ changed();
+}
+
+void PageAutoConnect::kvl_clicked(QListViewItem *it)
+{
+ if(it != 0){
+ if(it->parent() != 0){
+ ChannelLE->setText(it->text(NAME));
+ KeyLE->setText(it->text(PK));
+ AddPB->setText(i18n("&Update"));
+ /*
+ * Move it to the parent to setup parent/server
+ * values. This save writing this code
+ * in two places.
+ */
+ it = it->parent();
+ }
+ else {
+ AddPB->setText(i18n("&Update/Add"));
+ ChannelLE->clear();
+ KeyLE->clear();
+ }
+
+ if(it->parent() == 0){
+ ServerLE->setText(it->text(NAME));
+ PortKI->setValue(it->text(PK).toInt());
+ PassLE->setText(it->text(PASS));
+ if(it->text(SSL).length() > 0)
+ sslCB->setChecked(true);
+ else
+ sslCB->setChecked(false);
+ }
+ /*
+ * Make sure to do this after changing all the fields
+ */
+ AddPB->setEnabled(false);
+ DeletePB->setEnabled(true);
+
+
+ }
+ else {
+ AddPB->setEnabled(false);
+ DeletePB->setEnabled(false);
+ }
+ changed();
+}
+
+void PageAutoConnect::item_changed() {
+ AddPB->setEnabled(true);
+}
+
+#include "page_autoconnect.moc"
+
diff --git a/ksirc/KSPrefs/page_autoconnect.h b/ksirc/KSPrefs/page_autoconnect.h
new file mode 100644
index 00000000..efd9a4ed
--- /dev/null
+++ b/ksirc/KSPrefs/page_autoconnect.h
@@ -0,0 +1,36 @@
+#ifndef PAGEAUTOCONNECT_H
+#define PAGEAUTOCONNECT_H
+#include "page_autoconnectbase.h"
+
+#include "ksopts.h"
+
+class PageAutoConnect : public PageAutoConnectBase
+{
+ Q_OBJECT
+
+public:
+ PageAutoConnect( QWidget* parent = 0, const char* name = 0);
+ ~PageAutoConnect();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig();
+
+signals:
+ void modified();
+
+public slots:
+ virtual void changed();
+
+protected slots:
+ virtual void item_changed();
+
+ virtual void add_pressed();
+ virtual void new_pressed();
+ virtual void delete_pressed();
+
+ virtual void kvl_clicked(QListViewItem *);
+
+};
+
+#endif // PAGEAUTOCONNECT_H
diff --git a/ksirc/KSPrefs/page_autoconnectbase.ui b/ksirc/KSPrefs/page_autoconnectbase.ui
new file mode 100644
index 00000000..71aafb49
--- /dev/null
+++ b/ksirc/KSPrefs/page_autoconnectbase.ui
@@ -0,0 +1,391 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PageAutoConnectBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageAutoConnectBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>551</width>
+ <height>460</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Auto Connect</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Auto Connect List</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Port/Key</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Server Password</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>SSL</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>KLVAutoConnect</cstring>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>true</bool>
+ </property>
+ <property name="fullWidth">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox12</cstring>
+ </property>
+ <property name="title">
+ <string>Auto Connect Setup</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout13</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>textLabelSever</cstring>
+ </property>
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>ServerLE</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabelPort</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Port:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>PortKI</cstring>
+ </property>
+ </widget>
+ <widget class="KIntNumInput">
+ <property name="name">
+ <cstring>PortKI</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabelPass</cstring>
+ </property>
+ <property name="text">
+ <string>Server password:</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>PassLE</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout15</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabelSSL</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>PortKI</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>sslCB</cstring>
+ </property>
+ <property name="text">
+ <string>Use SS&amp;L</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabelChan</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Channel:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ChannelLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>ChannelLE</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <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>textLabelKey</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Key:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>KeyLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>KeyLE</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>NewPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>AddPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>DeletePB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>ServerLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>PortKI</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>PassLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>sslCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>ChannelLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>KeyLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>item_changed()</slot>
+ </connection>
+ <connection>
+ <sender>NewPB</sender>
+ <signal>pressed()</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>new_pressed()</slot>
+ </connection>
+ <connection>
+ <sender>AddPB</sender>
+ <signal>pressed()</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>add_pressed()</slot>
+ </connection>
+ <connection>
+ <sender>DeletePB</sender>
+ <signal>pressed()</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>delete_pressed()</slot>
+ </connection>
+ <connection>
+ <sender>KLVAutoConnect</sender>
+ <signal>clicked(QListViewItem*)</signal>
+ <receiver>PageAutoConnectBase</receiver>
+ <slot>kvl_clicked(QListViewItem*)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>KLVAutoConnect</tabstop>
+ <tabstop>ServerLE</tabstop>
+ <tabstop>PortKI</tabstop>
+ <tabstop>PassLE</tabstop>
+ <tabstop>sslCB</tabstop>
+ <tabstop>ChannelLE</tabstop>
+ <tabstop>KeyLE</tabstop>
+ <tabstop>NewPB</tabstop>
+ <tabstop>AddPB</tabstop>
+ <tabstop>DeletePB</tabstop>
+</tabstops>
+<slots>
+ <slot>item_changed()</slot>
+ <slot>new_pressed()</slot>
+ <slot>add_pressed()</slot>
+ <slot>delete_pressed()</slot>
+ <slot>KLVAutoConnect_clicked(QListViewItem*)</slot>
+ <slot>kcl_clicked(QListViewItem *)</slot>
+ <slot>kvl_clicked(QListViewItem*)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/KSPrefs/page_colors.cpp b/ksirc/KSPrefs/page_colors.cpp
new file mode 100644
index 00000000..f6d68a52
--- /dev/null
+++ b/ksirc/KSPrefs/page_colors.cpp
@@ -0,0 +1,295 @@
+/***************************************************************************
+ * *
+ * 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 "page_colors.h"
+
+#include <qcheckbox.h>
+#include <qlineedit.h>
+#include <qradiobutton.h>
+#include <qlabel.h>
+
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kcolorbutton.h>
+#include <kdebug.h>
+
+PageColors::PageColors( QWidget *parent, const char *name ) : PageColorsBase( parent, name)
+{
+ changing = 0;
+ m_dcol.setAutoDelete(true);
+}
+
+PageColors::~PageColors()
+{
+}
+
+void PageColors::saveConfig()
+{
+ ksopts->backgroundColor = backCBtn->color();
+ ksopts->selBackgroundColor = selBackCBtn->color();
+ ksopts->selForegroundColor = selForeCBtn->color();
+ ksopts->errorColor = errorCBtn->color();
+ ksopts->infoColor = infoCBtn->color();
+ ksopts->textColor = genericTextCBtn->color();
+ ksopts->channelColor = chanMsgCBtn->color();
+ ksopts->linkColor = linkCBtn->color();
+
+ ksopts->ownNickColor = ownNickCBtn->color();
+ ksopts->ownNickBold = ownNickBoldCB->isChecked();
+ ksopts->ownNickRev = ownNickRevCB->isChecked();
+ ksopts->ownNickUl = ownNickUlCB->isChecked();
+
+ if(fixedOtherColRB->isChecked()){
+ ksopts->nickColourization = false;
+ ksopts->nickForeground = nickFGCBtn->color();
+ ksopts->nickBackground = nickBGCBtn->color();
+ }
+ else if(autoOtherColRB->isChecked()){
+ ksopts->nickColourization = true;
+ ksopts->nickForeground = QColor();
+ ksopts->nickBackground = QColor();
+ }
+ else {
+ ksopts->nickColourization = false;
+ ksopts->nickForeground = QColor();
+ ksopts->nickBackground = QColor();
+ }
+
+ ksopts->msgContainNick = ownContainNickCBtn->color();
+ if(msg1LE->text().length() > 0){
+ ksopts->msg1Contain = msg1CBtn->color();
+ ksopts->msg1String = msg1LE->text();
+ ksopts->msg1Regex = msg1Regex->isChecked();
+ }
+ else {
+ ksopts->msg1Contain = QColor();
+ ksopts->msg1String = msg1LE->text();
+ ksopts->msg1Regex = false;
+ }
+ if(msg2LE->text().length() > 0){
+ ksopts->msg2Contain = msg2CBtn->color();
+ ksopts->msg2String = msg2LE->text();
+ ksopts->msg2Regex = msg2Regex->isChecked();
+ }
+ else {
+ ksopts->msg2Contain = QColor();
+ ksopts->msg2String = msg2LE->text();
+ ksopts->msg2Regex = false;
+ }
+
+ ksopts->ksircColors = allowKSircColorsCB->isChecked();
+ ksopts->mircColors = allowMIRCColorsCB->isChecked();
+ ksopts->colourTheme = themeLB->currentText();
+
+ KConfig *conf = kapp->config();
+ QDictIterator<KSOColors> it(m_dcol);
+ QStringList names;
+ for(; it.current(); ++it){
+ names << it.currentKey();
+ conf->setGroup("ColourSchemes-"+ it.currentKey());
+ conf->writeEntry("Background", it.current()->backgroundColor);
+ conf->writeEntry("SelBackground", it.current()->selBackgroundColor);
+ conf->writeEntry("SelForeground", it.current()->selForegroundColor);
+ conf->writeEntry("Error", it.current()->errorColor);
+ conf->writeEntry("Info", it.current()->infoColor);
+ conf->writeEntry("Text", it.current()->textColor);
+ conf->writeEntry("Channel", it.current()->channelColor);
+ conf->writeEntry("Link", it.current()->linkColor);
+ conf->writeEntry("OwnNick", it.current()->ownNickColor);
+ conf->writeEntry("NickForeground", it.current()->nickForeground);
+ conf->writeEntry("NickBackground", it.current()->nickBackground);
+ }
+ conf->setGroup("ColourSchemes");
+ conf->writeEntry("Names", names);
+
+}
+
+void PageColors::readConfig( const KSOColors *opts )
+{
+ backCBtn->setColor( opts->backgroundColor );
+ selBackCBtn->setColor( opts->selBackgroundColor );
+ selForeCBtn->setColor( opts->selForegroundColor );
+ errorCBtn->setColor( opts->errorColor );
+ infoCBtn->setColor( opts->infoColor );
+ genericTextCBtn->setColor( opts->textColor );
+ chanMsgCBtn->setColor( opts->channelColor );
+ linkCBtn->setColor( opts->linkColor );
+
+ ownNickCBtn->setColor( opts->ownNickColor );
+ ownNickBoldCB->setChecked( opts->ownNickBold );
+ ownNickRevCB->setChecked( opts->ownNickRev );
+ ownNickUlCB->setChecked( opts->ownNickUl );
+
+ if( opts->nickColourization ){
+ autoOtherColRB->setChecked( true );
+ nickFGCBtn->setColor( QColor() );
+ nickBGCBtn->setColor( QColor() );
+
+ }
+ else if( opts->nickForeground.isValid() ||
+ opts->nickBackground.isValid() ){
+ fixedOtherColRB->setChecked( true );
+ nickFGCBtn->setColor( opts->nickForeground );
+ nickBGCBtn->setColor( opts->nickBackground );
+ }
+ else {
+ noOtherColRB->setChecked( true );
+ nickFGCBtn->setColor( QColor() );
+ nickBGCBtn->setColor( QColor() );
+
+ }
+
+ ownContainNickCBtn->setColor( opts->msgContainNick );
+ msg1CBtn->setColor( opts->msg1Contain );
+ msg1LE->setText( opts->msg1String );
+ msg1Regex->setChecked( opts->msg1Regex );
+ msg2CBtn->setColor( opts->msg2Contain );
+ msg2LE->setText( opts->msg2String );
+ msg2Regex->setChecked( opts->msg2Regex );
+
+ coloursSetEnable();
+
+ allowKSircColorsCB->setChecked( opts->ksircColors );
+ allowMIRCColorsCB->setChecked( opts->mircColors );
+
+ KConfig *conf = kapp->config();
+ conf->setGroup("ColourSchemes");
+ themeLB->clear();
+ QStringList names = conf->readListEntry("Names");
+ if(names.contains("Custom")){
+ names.remove(names.find("Custom"));
+ }
+ names.prepend("Custom");
+ themeLB->insertStringList(names);
+ if(themeLB->findItem(ksopts->colourTheme, Qt::ExactMatch))
+ themeLB->setCurrentItem(themeLB->findItem(ksopts->colourTheme, Qt::ExactMatch));
+ else
+ themeLB->setCurrentItem(0);
+ themeLE->setText(themeLB->currentText());
+
+ m_dcol.clear();
+
+ QStringList::Iterator it = names.begin();
+ for( ; it != names.end(); ++it){
+ conf->setGroup("ColourSchemes-"+ *it);
+ m_dcol.insert(*it, new KSOColors);
+ m_dcol[*it]->backgroundColor = conf->readColorEntry( "Background");
+ m_dcol[*it]->selBackgroundColor = conf->readColorEntry( "SelBackground");
+ m_dcol[*it]->selForegroundColor = conf->readColorEntry( "SelForeground");
+ m_dcol[*it]->errorColor = conf->readColorEntry( "Error");
+ m_dcol[*it]->infoColor = conf->readColorEntry( "Info");
+ m_dcol[*it]->textColor = conf->readColorEntry( "Text");
+ m_dcol[*it]->channelColor = conf->readColorEntry( "Channel");
+ m_dcol[*it]->ownNickColor = conf->readColorEntry( "OwnNick");
+ m_dcol[*it]->nickForeground = conf->readColorEntry( "NickForeground");
+ m_dcol[*it]->nickBackground = conf->readColorEntry( "NickBackground");
+ m_dcol[*it]->linkColor = conf->readColorEntry("Link");
+ }
+}
+
+void PageColors::defaultConfig()
+{
+ KSOColors opts;
+ readConfig( &opts );
+}
+
+void PageColors::changed()
+{
+ if(changing == 0)
+ themeLB->setSelected(0, TRUE);
+ coloursSetEnable();
+ emit modified();
+}
+
+void PageColors::theme_clicked(QListBoxItem *li)
+{
+ if(li == 0x0)
+ return;
+
+ QString name = li->text();
+
+ changing = 1;
+ backCBtn->setColor( m_dcol[name]->backgroundColor );
+ selBackCBtn->setColor( m_dcol[name]->selBackgroundColor );
+ selForeCBtn->setColor( m_dcol[name]->selForegroundColor );
+ errorCBtn->setColor( m_dcol[name]->errorColor );
+ infoCBtn->setColor( m_dcol[name]->infoColor );
+ genericTextCBtn->setColor( m_dcol[name]->textColor );
+ chanMsgCBtn->setColor( m_dcol[name]->channelColor );
+ linkCBtn->setColor( m_dcol[name]->linkColor );
+
+ ownNickCBtn->setColor( m_dcol[name]->ownNickColor );
+ nickFGCBtn->setColor( m_dcol[name]->nickForeground );
+ nickBGCBtn->setColor( m_dcol[name]->nickBackground );
+ changing = 0;
+
+ themeLE->setText(li->text());
+}
+
+void PageColors::themeNewPB_clicked()
+{
+ themeLE->clear();
+}
+
+void PageColors::themeAddPB_clicked()
+{
+ QString name = themeLE->text();
+
+ kdDebug(5008) << "Got add: " << themeLB->currentText() << endl;
+
+ m_dcol.replace(name, new KSOColors());
+
+ m_dcol[name]->backgroundColor = backCBtn->color();
+ m_dcol[name]->selBackgroundColor = selBackCBtn->color();
+ m_dcol[name]->selForegroundColor = selForeCBtn->color();
+ m_dcol[name]->errorColor = errorCBtn->color();
+ m_dcol[name]->infoColor = infoCBtn->color();
+ m_dcol[name]->textColor = genericTextCBtn->color();
+ m_dcol[name]->channelColor = chanMsgCBtn->color();
+ m_dcol[name]->linkColor = linkCBtn->color();
+ m_dcol[name]->ownNickColor = ownNickCBtn->color();
+ m_dcol[name]->nickForeground = nickFGCBtn->color();
+ m_dcol[name]->nickBackground = nickBGCBtn->color();
+
+ if(themeLB->findItem(name, Qt::ExactMatch) == 0){
+ themeLB->insertItem(name);
+ }
+
+ themeLB->setCurrentItem(themeLB->findItem(name, Qt::ExactMatch));
+
+}
+
+void PageColors::themeDelPB_clicked()
+{
+ m_dcol.remove(themeLB->currentText());
+ themeLB->removeItem(themeLB->currentItem());
+}
+
+void PageColors::theme_sel()
+{
+ themeLE->setText(themeLB->currentText());
+}
+
+void PageColors::coloursSetEnable()
+{
+ nickFGCBtn->setEnabled( fixedOtherColRB->isChecked() );
+ nickBGCBtn->setEnabled( fixedOtherColRB->isChecked() );
+ nickFGColorLabel->setEnabled( fixedOtherColRB->isChecked() );
+ nickBGColorLabel->setEnabled( fixedOtherColRB->isChecked() );
+
+ bool msgEn = msg1LE->text().length() > 0;
+ msg1CBtn->setEnabled(msgEn);
+ msg1Regex->setEnabled(msgEn);
+ msgEn = msg2LE->text().length() > 0;
+ msg2CBtn->setEnabled(msgEn);
+ msg2Regex->setEnabled(msgEn);
+
+}
+
+#include "page_colors.moc"
diff --git a/ksirc/KSPrefs/page_colors.h b/ksirc/KSPrefs/page_colors.h
new file mode 100644
index 00000000..188fe0a8
--- /dev/null
+++ b/ksirc/KSPrefs/page_colors.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_COLORS_H
+#define PAGE_COLORS_H
+
+#include "page_colorsbase.h"
+#include "ksopts.h"
+
+#include <qlistbox.h>
+#include <qstringlist.h>
+#include <qdict.h>
+
+class PageColors : public PageColorsBase
+{
+Q_OBJECT
+
+public:
+ PageColors( QWidget *parent = 0, const char *name = 0 );
+ ~PageColors();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOColors * = ksopts );
+
+protected slots:
+ void changed();
+
+public slots:
+ virtual void theme_clicked(QListBoxItem*);
+ virtual void themeNewPB_clicked();
+ virtual void themeAddPB_clicked();
+ virtual void themeDelPB_clicked();
+ virtual void theme_sel();
+
+signals:
+ void modified();
+
+private:
+ void coloursSetEnable();
+
+private:
+ int changing;
+ QDict<KSOColors> m_dcol;
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_colorsbase.ui b/ksirc/KSPrefs/page_colorsbase.ui
new file mode 100644
index 00000000..498c9c23
--- /dev/null
+++ b/ksirc/KSPrefs/page_colorsbase.ui
@@ -0,0 +1,1063 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>PageColorsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageColorsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>617</width>
+ <height>545</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Colors</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget2</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Scheme</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>chatColorsGB</cstring>
+ </property>
+ <property name="title">
+ <string>Chat Colors</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>chanMsgLabel</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;hannel messages:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>chanMsgCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>genericTextLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Generic text:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>errorLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Errors:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>errorCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>infoLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Info:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>infoCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>backgrndLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Background:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>backCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>backgrndLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Links:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>backCBtn</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="4">
+ <property name="name">
+ <cstring>Spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KColorButton" row="2" column="3">
+ <property name="name">
+ <cstring>linkCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="3">
+ <property name="name">
+ <cstring>backCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="3">
+ <property name="name">
+ <cstring>errorCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>backgrndLabel_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Selection backgr&amp;ound:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>backCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>infoCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>chanMsgCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>selBackCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="3">
+ <property name="name">
+ <cstring>selForeCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>backgrndLabel_2_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Sele&amp;ction foreground:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>backCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="5">
+ <property name="name">
+ <cstring>nickFGColorCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Use background color for links</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox11</cstring>
+ </property>
+ <property name="title">
+ <string>Sample Color Themes</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLineEdit" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>themeLE</cstring>
+ </property>
+ </widget>
+ <widget class="QListBox" row="1" column="0" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>themeLB</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="1">
+ <property name="name">
+ <cstring>themeNewPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="1">
+ <property name="name">
+ <cstring>themeAddPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="3" column="1">
+ <property name="name">
+ <cstring>themeDelPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Delete</string>
+ </property>
+ </widget>
+ <spacer row="4" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>tab</cstring>
+ </property>
+ <attribute name="title">
+ <string>&amp;Highlighting</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox5</cstring>
+ </property>
+ <property name="title">
+ <string>Your Nick</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nickMatchLabel_2_2</cstring>
+ </property>
+ <property name="text">
+ <string>Color: </string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ownNickCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>ownNickCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ownNickBoldCB</cstring>
+ </property>
+ <property name="text">
+ <string>Bold</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ownNickRevCB</cstring>
+ </property>
+ <property name="text">
+ <string>Reverse</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>ownNickUlCB</cstring>
+ </property>
+ <property name="text">
+ <string>Underline</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>202</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="title">
+ <string>Other Nicks</string>
+ </property>
+ <property name="exclusive">
+ <bool>true</bool>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton" row="0" column="0" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>noOtherColRB</cstring>
+ </property>
+ <property name="text">
+ <string>N&amp;o nick colors</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="1" column="0" rowspan="1" colspan="6">
+ <property name="name">
+ <cstring>autoOtherColRB</cstring>
+ </property>
+ <property name="text">
+ <string>Au&amp;to nick colorization</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>fixedOtherColRB</cstring>
+ </property>
+ <property name="text">
+ <string>Fi&amp;xed</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>nickFGColorLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Foreground:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickFGCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="2">
+ <property name="name">
+ <cstring>nickFGCBtn</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="3">
+ <property name="name">
+ <cstring>nickBGColorLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Backg&amp;round:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickBGCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="4">
+ <property name="name">
+ <cstring>nickBGCBtn</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer row="2" column="5">
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>83</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>nickGB</cstring>
+ </property>
+ <property name="title">
+ <string>Highlight Messages</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout12</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nickMatchLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>Containing &amp;your nick:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ownNickCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>ownContainNickCBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>169</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nickMatchLabel_2_3</cstring>
+ </property>
+ <property name="text">
+ <string>Containing:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ownNickCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>msg1LE</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>msg1CBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>msg1Regex</cstring>
+ </property>
+ <property name="text">
+ <string>Regex</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>104</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout10</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>nickMatchLabel_2_3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Containing:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>ownNickCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>msg2LE</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton">
+ <property name="name">
+ <cstring>msg2CBtn</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>msg2Regex</cstring>
+ </property>
+ <property name="text">
+ <string>Regex</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10_3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>104</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>colorCodesGB</cstring>
+ </property>
+ <property name="title">
+ <string>Color Codes</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allowKSircColorsCB</cstring>
+ </property>
+ <property name="text">
+ <string>Strip &amp;kSirc color codes</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>allowMIRCColorsCB</cstring>
+ </property>
+ <property name="text">
+ <string>Strip &amp;mIRC color codes</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer8_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>allowKSircColorsCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>allowMIRCColorsCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>backCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>chanMsgCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>errorCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>genericTextCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>infoCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>linkCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>nickBGCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>nickFGCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>nickFGColorCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>ownContainNickCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>themeAddPB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>themeAddPB_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>themeDelPB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>themeDelPB_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>themeLB</sender>
+ <signal>clicked(QListBoxItem*)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>theme_clicked(QListBoxItem*)</slot>
+ </connection>
+ <connection>
+ <sender>themeLB</sender>
+ <signal>selectionChanged()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>theme_sel()</slot>
+ </connection>
+ <connection>
+ <sender>themeLE</sender>
+ <signal>returnPressed()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>themeAddPB_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>themeNewPB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>themeNewPB_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>selBackCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>selForeCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>ownNickCBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>ownNickBoldCB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>ownNickRevCB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>ownNickUlCB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>noOtherColRB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>autoOtherColRB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>fixedOtherColRB</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg1CBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg1LE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg1Regex</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg2CBtn</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg2Regex</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>msg2LE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>genericTextCBtn</tabstop>
+ <tabstop>errorCBtn</tabstop>
+ <tabstop>infoCBtn</tabstop>
+ <tabstop>backCBtn</tabstop>
+ <tabstop>chanMsgCBtn</tabstop>
+ <tabstop>linkCBtn</tabstop>
+ <tabstop>selBackCBtn</tabstop>
+ <tabstop>selForeCBtn</tabstop>
+ <tabstop>nickFGCBtn</tabstop>
+ <tabstop>nickBGCBtn</tabstop>
+ <tabstop>nickFGColorCB</tabstop>
+ <tabstop>ownContainNickCBtn</tabstop>
+ <tabstop>allowKSircColorsCB</tabstop>
+ <tabstop>allowMIRCColorsCB</tabstop>
+ <tabstop>themeLE</tabstop>
+ <tabstop>themeLB</tabstop>
+ <tabstop>themeNewPB</tabstop>
+ <tabstop>themeAddPB</tabstop>
+ <tabstop>themeDelPB</tabstop>
+</tabstops>
+<slots>
+ <slot>theme_clicked(QListBoxItem*)</slot>
+ <slot>themeNewPB_clicked()</slot>
+ <slot>themeDelPB_clicked()</slot>
+ <slot>themeAddPB_clicked()</slot>
+ <slot>theme_sel()</slot>
+ <slot>setColourEnabled()</slot>
+ <slot>coloursSetEnable()</slot>
+ <slot>changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSPrefs/page_font.cpp b/ksirc/KSPrefs/page_font.cpp
new file mode 100644
index 00000000..e9f9e0e6
--- /dev/null
+++ b/ksirc/KSPrefs/page_font.cpp
@@ -0,0 +1,38 @@
+#include "page_font.h"
+#include "qapplication.h"
+PageFont::PageFont( QWidget *parent, const char *name ) :
+ QWidget( parent, name)
+{
+ layout = new QHBoxLayout(this);
+ fontchooser = new KFontChooser(this);
+ layout->addWidget(fontchooser);
+ connect(fontchooser,SIGNAL(fontSelected ( const QFont&)), this, SLOT(update()));
+}
+
+PageFont::~PageFont( )
+{
+
+}
+
+void PageFont::update( void ){
+ emit modified();
+}
+
+void PageFont::saveConfig( void )
+{
+ ksopts->defaultFont = fontchooser->font();
+ QApplication::setFont(fontchooser->font(), true, "KSirc::TextView" );
+}
+
+void PageFont::readConfig( const KSOColors * opts )
+{
+ /* Just set the font from the preferences */
+ fontchooser->setFont( opts->defaultFont );
+}
+
+void PageFont::defaultConfig( void )
+{
+ fontchooser->setFont( ksopts->defaultFont );
+}
+
+#include "page_font.moc"
diff --git a/ksirc/KSPrefs/page_font.h b/ksirc/KSPrefs/page_font.h
new file mode 100644
index 00000000..0d2fa673
--- /dev/null
+++ b/ksirc/KSPrefs/page_font.h
@@ -0,0 +1,62 @@
+#ifndef _PAGE_FONT
+#define _PAGE_FONT
+#include <kfontdialog.h> // For the font selection widget
+#include <qlayout.h> // For the layout
+#include "ksopts.h" // For storing the info.
+
+/**
+ * A page for the preferences dialog to set the standard font
+ *
+ * @author Markus Weimer <markus.weimer@web.de>
+ */
+class PageFont : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Create the Widget
+ */
+ PageFont( QWidget *parent = 0, const char *name = 0 );
+
+
+ /*
+ * Standard destructor
+ */
+ ~PageFont();
+
+
+ /**
+ * Save the config set by the user to the global ksopts object.
+ *
+ */
+ void saveConfig( void );
+
+
+ /**
+ * Reset the current user config to the one stored in the global
+ * ksopts object.
+ *
+ */
+ void defaultConfig( void );
+
+
+ /**
+ * Read the configuration from the given KSOptions object
+ *
+ * @param opts the KSOptions object to modify
+ */
+ void readConfig( const KSOColors* opts=ksopts );
+
+ public slots:
+ void update( void );
+
+ signals:
+ void modified();
+
+ private:
+ KFontChooser* fontchooser; /** The font choosing widget from kdelib */
+ QHBoxLayout* layout;
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_general.cpp b/ksirc/KSPrefs/page_general.cpp
new file mode 100644
index 00000000..d6256e2c
--- /dev/null
+++ b/ksirc/KSPrefs/page_general.cpp
@@ -0,0 +1,117 @@
+/***************************************************************************
+ * *
+ * 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 <qcheckbox.h>
+#include <qspinbox.h>
+
+#include <klocale.h>
+#include <kcharsets.h>
+#include "page_general.h"
+#include "../servercontroller.h"
+
+PageGeneral::PageGeneral( QWidget *parent, const char *name ) : PageGeneralBase( parent, name)
+{
+}
+
+PageGeneral::~PageGeneral()
+{
+}
+
+void PageGeneral::saveConfig()
+{
+ ksopts->runDocked = dockedOnlyCB->isChecked();
+ servercontroller::self()->checkDocking();
+ ksopts->autoCreateWin = autoCreateWindowCB->isChecked();
+ ksopts->autoCreateWinForNotice = autoCreateWindowForNoticeCB->isChecked();
+ ksopts->nickCompletion = nickCompletionCB->isChecked();
+ ksopts->displayTopic = displayTopicCB->isChecked();
+ ksopts->colorPicker = colorPickerPopupCB->isChecked();
+ ksopts->autoRejoin = autoRejoinCB->isChecked();
+ ksopts->oneLineEntry = oneLineEditCB->isChecked();
+ ksopts->useColourNickList = useColourNickListCB->isChecked();
+ ksopts->dockPopups = dockPopupsCB->isChecked();
+ ksopts->autoSaveHistory = autoSaveHistoryCB->isChecked();
+ ksopts->windowLength = historySB->value();
+
+ ksopts->channel["global"]["global"].timeStamp = timeStampCB->isChecked();
+ ksopts->channel["global"]["global"].beepOnMsg = beepCB->isChecked();
+ ksopts->channel["global"]["global"].topicShow = showTopicCB->isChecked();
+ ksopts->channel["global"]["global"].logging = enLoggingCB->isChecked();
+ ksopts->channel["global"]["global"].encoding = encodingsCB->currentText();
+ ksopts->channel["global"]["global"].filterJoinPart = joinPartCB->isChecked();
+
+ if(applyGloballyCB->isChecked()){
+ ksopts->applyChannelGlobal();
+ }
+
+ ksopts->publicAway = publicAway->isChecked();
+}
+
+void PageGeneral::readConfig( const KSOGeneral *opts )
+{
+ dockedOnlyCB->setChecked( opts->runDocked );
+ autoCreateWindowCB->setChecked( opts->autoCreateWin );
+ autoCreateWindowForNoticeCB->setChecked( opts->autoCreateWinForNotice );
+ nickCompletionCB->setChecked( opts->nickCompletion );
+ displayTopicCB->setChecked( opts->displayTopic );
+ colorPickerPopupCB->setChecked( opts->colorPicker );
+ autoRejoinCB->setChecked( opts->autoRejoin );
+ oneLineEditCB->setChecked( opts->oneLineEntry );
+ useColourNickListCB->setChecked( opts->useColourNickList );
+ dockPopupsCB->setChecked( opts->dockPopups );
+ dockPopupsCB->setEnabled(dockedOnlyCB->isChecked());
+
+ autoSaveHistoryCB->setChecked( opts->autoSaveHistory );
+
+ historySB->setValue( opts->windowLength );
+
+ timeStampCB->setChecked( ksopts->channel["global"]["global"].timeStamp );
+ beepCB->setChecked( ksopts->channel["global"]["global"].beepOnMsg );
+ showTopicCB->setChecked( ksopts->channel["global"]["global"].topicShow );
+ enLoggingCB->setChecked( ksopts->channel["global"]["global"].logging );
+ joinPartCB->setChecked( ksopts->channel["global"]["global"].filterJoinPart );
+
+ publicAway->setChecked( ksopts->publicAway );
+
+ encodingsCB->clear();
+
+
+ QStringList encodings = KGlobal::charsets()->descriptiveEncodingNames();
+
+ // remove utf16/ucs2 as it just doesn't work for IRC
+ QStringList::Iterator encodingIt = encodings.begin();
+ while ( encodingIt != encodings.end() ) {
+ if ( ( *encodingIt ).find( "utf16" ) != -1 ||
+ ( *encodingIt ).find( "iso-10646" ) != -1 )
+ encodingIt = encodings.remove( encodingIt );
+ else
+ ++encodingIt;
+ }
+ encodings.prepend( i18n( "Default" ) );
+ encodingsCB->insertStringList(encodings);
+ int eindex = encodings.findIndex(ksopts->channel["global"]["global"].encoding);
+ if(eindex == -1)
+ encodingsCB->setCurrentItem(0);
+ else
+ encodingsCB->setCurrentItem(eindex);
+}
+
+void PageGeneral::defaultConfig()
+{
+ KSOGeneral opts;
+ readConfig( &opts );
+}
+
+void PageGeneral::changed()
+{
+ dockPopupsCB->setEnabled(dockedOnlyCB->isChecked());
+ emit modified();
+}
+
+#include "page_general.moc"
diff --git a/ksirc/KSPrefs/page_general.h b/ksirc/KSPrefs/page_general.h
new file mode 100644
index 00000000..ecf515ca
--- /dev/null
+++ b/ksirc/KSPrefs/page_general.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_GENERAL_H
+#define PAGE_GENERAL_H
+
+#include "page_generalbase.h"
+#include "ksopts.h"
+
+class PageGeneral : public PageGeneralBase
+{
+Q_OBJECT
+
+public:
+ PageGeneral( QWidget *parent = 0, const char *name = 0 );
+ ~PageGeneral();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOGeneral * = ksopts );
+
+signals:
+ void modified();
+
+public slots:
+ virtual void changed();
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_generalbase.ui b/ksirc/KSPrefs/page_generalbase.ui
new file mode 100644
index 00000000..d3b4db06
--- /dev/null
+++ b/ksirc/KSPrefs/page_generalbase.ui
@@ -0,0 +1,540 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>PageGeneralBase</class>
+<widget class="QFrame">
+ <property name="name">
+ <cstring>PageGeneralBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>443</width>
+ <height>489</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>425</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>General</string>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>miscGB</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Global Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="5" column="1">
+ <property name="name">
+ <cstring>Layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>historyItemsLabel</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Histor&amp;y length:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>historySB</cstring>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>historySB</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> lines</string>
+ </property>
+ <property name="specialValueText">
+ <string>unlimited</string>
+ </property>
+ <property name="maxValue">
+ <number>1000</number>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Stores up to this many lines of chat from each window as history</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Stores up to this many lines of chat from each window, allowing you to scroll upwards and see what has already been said.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>publicAway</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Announce away messages</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>See the messages when a user selects the away option</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is checked, you will see the messages when a user selects the away option. By default this option is not checked.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>autoCreateWindowCB</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;uto create window</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Will auto create a window for each user who sends a /msg to you</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If selected, KSirc will automatically create a new window for each user who sends a /msg command to you. If not selected, any text sent to you with /msg is displayed in the current window and you can use /query username to create a window to chat to that user.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>autoCreateWindowForNoticeCB</cstring>
+ </property>
+ <property name="text">
+ <string>Auto create &amp;on notice</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>autoRejoinCB</cstring>
+ </property>
+ <property name="text">
+ <string>Auto-re&amp;join</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Rejoin channels automatically if you are disconnected.</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If selected, it allows you to rejoin channels automatically if you are disconnected.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="1">
+ <property name="name">
+ <cstring>dockPopupsCB</cstring>
+ </property>
+ <property name="text">
+ <string>Dock &amp;passive popups</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="0">
+ <property name="name">
+ <cstring>displayTopicCB</cstring>
+ </property>
+ <property name="text">
+ <string>D&amp;isplay topic in caption</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Display the topic of the current channel in the window caption</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Displays the topic of the current channel in the window caption. If not selected, the topic is only displayed inside the window.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="1">
+ <property name="name">
+ <cstring>colorPickerPopupCB</cstring>
+ </property>
+ <property name="text">
+ <string>Color pi&amp;cker popup</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Allow you to get the color pickup dialog with Ctrl K</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If selected, a popup window from which to select the color of your text is presented when you press Ctrl K. If not, you have to type the color codes manually.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="0">
+ <property name="name">
+ <cstring>oneLineEditCB</cstring>
+ </property>
+ <property name="text">
+ <string>One line te&amp;xt entry box</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="1">
+ <property name="name">
+ <cstring>useColourNickListCB</cstring>
+ </property>
+ <property name="text">
+ <string>Us&amp;e color nick list</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Use the colors set in the Colors tab of the Configure KSirc dialog for coloring the nicknames</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If selected, it will use the colors set in the Colors tab of the Configure KSirc dialog for coloring the nicknames.</string>
+ </property>
+ </widget>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>16</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>nickCompletionCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nick completion</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Switch nickname completion on</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If selected, switches nickname completion on. Nickname completion works as follows: Type the first letters of a user's nickname, press the Tab key, the text you typed will be completed to match the username, including changes in capitalization if necessary.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0">
+ <property name="name">
+ <cstring>dockedOnlyCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Dock in system tray</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Put the KSirc icon in the system tray</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>This allows KSirc to be docked in the system tray. By default this is not enabled. When KSirc is docked in the system tray, you are able to access several options by clicking on the KSirc icon. When you close KSirc window, the icon stays in the systray until you quit KSirc.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="0">
+ <property name="name">
+ <cstring>autoSaveHistoryCB</cstring>
+ </property>
+ <property name="text">
+ <string>Auto save history</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox4</cstring>
+ </property>
+ <property name="title">
+ <string>Per Channel Options</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0">
+ <property name="name">
+ <cstring>timeStampCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Time stamp</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Add the time and date on the left of each message</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Prepends each thing said in the channel with the time it was said, in the form [HH:MM:SS].</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>applyGloballyCB</cstring>
+ </property>
+ <property name="text">
+ <string>O&amp;verride existing channel options</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The settings in this tab will be applied and each channel settings will be ignored</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>If this is selected, the settings in this tab will override each channel's options so these settings will be applied in each channel, independently of your channel settings in the Channel menu. This setting will only work until next time you open the configuration dialog and it will be reset unchecked then; this is because you probably do not want to override the existing channels options all the time.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="1">
+ <property name="name">
+ <cstring>showTopicCB</cstring>
+ </property>
+ <property name="text">
+ <string>Sho&amp;w topic</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Displays the channel topic on top</string>
+ </property>
+ <property name="whatsThis" stdset="0">
+ <string>Displays the channel topic on top of each channel window.</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="0">
+ <property name="name">
+ <cstring>beepCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Beep on change</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="0">
+ <property name="name">
+ <cstring>joinPartCB</cstring>
+ </property>
+ <property name="text">
+ <string>Hide part/join messages</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="1">
+ <property name="name">
+ <cstring>enLoggingCB</cstring>
+ </property>
+ <property name="text">
+ <string>Enable lo&amp;gging</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget" row="4" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>encodingsL</cstring>
+ </property>
+ <property name="text">
+ <string>Default en&amp;coding:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>encodingsCB</cstring>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>encodingsCB</cstring>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+ </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>31</width>
+ <height>71</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>autoCreateWindowCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>nickCompletionCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>displayTopicCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>beepCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>colorPickerPopupCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>autoRejoinCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>historySB</sender>
+ <signal>valueChanged(int)</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>publicAway</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>showTopicCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>enLoggingCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>timeStampCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>applyGloballyCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>oneLineEditCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>useColourNickListCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>autoCreateWindowForNoticeCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>dockedOnlyCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>dockPopupsCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>encodingsCB</sender>
+ <signal>activated(int)</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>joinPartCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageGeneralBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>autoCreateWindowCB</tabstop>
+ <tabstop>nickCompletionCB</tabstop>
+ <tabstop>displayTopicCB</tabstop>
+ <tabstop>colorPickerPopupCB</tabstop>
+ <tabstop>autoRejoinCB</tabstop>
+ <tabstop>publicAway</tabstop>
+ <tabstop>historySB</tabstop>
+ <tabstop>timeStampCB</tabstop>
+ <tabstop>beepCB</tabstop>
+ <tabstop>showTopicCB</tabstop>
+ <tabstop>enLoggingCB</tabstop>
+</tabstops>
+<slots>
+ <slot access="protected">setPreviewPixmap(bool)</slot>
+ <slot access="protected">showWallpaperPixmap(const QString &amp;)</slot>
+ <slot>changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSPrefs/page_irccolors.cpp b/ksirc/KSPrefs/page_irccolors.cpp
new file mode 100644
index 00000000..aa6ff351
--- /dev/null
+++ b/ksirc/KSPrefs/page_irccolors.cpp
@@ -0,0 +1,181 @@
+/***************************************************************************
+ * *
+ * 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 "page_irccolors.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <kconfig.h>
+#include <kcolorbutton.h>
+
+PageIRCColors::PageIRCColors( QWidget *parent, const char *name ) : PageIRCColorsBase( parent, name)
+{
+}
+
+PageIRCColors::~PageIRCColors()
+{
+}
+
+void PageIRCColors::saveConfig()
+{
+ ksopts->ircColors[0] = CBtn_0->color();
+ ksopts->ircColors[1] = CBtn_1->color();
+ ksopts->ircColors[2] = CBtn_2->color();
+ ksopts->ircColors[3] = CBtn_3->color();
+ ksopts->ircColors[4] = CBtn_4->color();
+ ksopts->ircColors[5] = CBtn_5->color();
+ ksopts->ircColors[6] = CBtn_6->color();
+ ksopts->ircColors[7] = CBtn_7->color();
+ ksopts->ircColors[8] = CBtn_8->color();
+ ksopts->ircColors[9] = CBtn_9->color();
+ ksopts->ircColors[10] = CBtn_10->color();
+ ksopts->ircColors[11] = CBtn_11->color();
+ ksopts->ircColors[12] = CBtn_12->color();
+ ksopts->ircColors[13] = CBtn_13->color();
+ ksopts->ircColors[14] = CBtn_14->color();
+ ksopts->ircColors[15] = CBtn_15->color();
+
+ ksopts->nickHighlight[0] = CBox_0->isChecked();
+ ksopts->nickHighlight[1] = CBox_1->isChecked();
+ ksopts->nickHighlight[2] = CBox_2->isChecked();
+ ksopts->nickHighlight[3] = CBox_3->isChecked();
+ ksopts->nickHighlight[4] = CBox_4->isChecked();
+ ksopts->nickHighlight[5] = CBox_5->isChecked();
+ ksopts->nickHighlight[6] = CBox_6->isChecked();
+ ksopts->nickHighlight[7] = CBox_7->isChecked();
+ ksopts->nickHighlight[8] = CBox_8->isChecked();
+ ksopts->nickHighlight[9] = CBox_9->isChecked();
+ ksopts->nickHighlight[10] = CBox_10->isChecked();
+ ksopts->nickHighlight[11] = CBox_11->isChecked();
+ ksopts->nickHighlight[12] = CBox_12->isChecked();
+ ksopts->nickHighlight[13] = CBox_13->isChecked();
+ ksopts->nickHighlight[14] = CBox_14->isChecked();
+ ksopts->nickHighlight[15] = CBox_15->isChecked();
+
+
+}
+
+void PageIRCColors::readConfig( const KSOColors *opts )
+{
+ CBtn_0->setColor(opts->ircColors[0]);
+ CBtn_1->setColor(opts->ircColors[1]);
+ CBtn_2->setColor(opts->ircColors[2]);
+ CBtn_3->setColor(opts->ircColors[3]);
+ CBtn_4->setColor(opts->ircColors[4]);
+ CBtn_5->setColor(opts->ircColors[5]);
+ CBtn_6->setColor(opts->ircColors[6]);
+ CBtn_7->setColor(opts->ircColors[7]);
+ CBtn_8->setColor(opts->ircColors[8]);
+ CBtn_9->setColor(opts->ircColors[9]);
+ CBtn_10->setColor(opts->ircColors[10]);
+ CBtn_11->setColor(opts->ircColors[11]);
+ CBtn_12->setColor(opts->ircColors[12]);
+ CBtn_13->setColor(opts->ircColors[13]);
+ CBtn_14->setColor(opts->ircColors[14]);
+ CBtn_15->setColor(opts->ircColors[15]);
+
+ CBox_0->setChecked(opts->nickHighlight[0]);
+ CBox_1->setChecked(opts->nickHighlight[1]);
+ CBox_2->setChecked(opts->nickHighlight[2]);
+ CBox_3->setChecked(opts->nickHighlight[3]);
+ CBox_4->setChecked(opts->nickHighlight[4]);
+ CBox_5->setChecked(opts->nickHighlight[5]);
+ CBox_6->setChecked(opts->nickHighlight[6]);
+ CBox_7->setChecked(opts->nickHighlight[7]);
+ CBox_8->setChecked(opts->nickHighlight[8]);
+ CBox_9->setChecked(opts->nickHighlight[9]);
+ CBox_10->setChecked(opts->nickHighlight[10]);
+ CBox_11->setChecked(opts->nickHighlight[11]);
+ CBox_12->setChecked(opts->nickHighlight[12]);
+ CBox_13->setChecked(opts->nickHighlight[13]);
+ CBox_14->setChecked(opts->nickHighlight[14]);
+ CBox_15->setChecked(opts->nickHighlight[15]);
+
+ TL_0->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_1->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_2->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_3->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_4->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_5->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_6->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_7->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_8->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_9->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_10->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_11->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_12->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_13->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_14->setPaletteBackgroundColor(opts->backgroundColor);
+ TL_15->setPaletteBackgroundColor(opts->backgroundColor);
+
+ TL_0->setPaletteForegroundColor(opts->ircColors[0]);
+ TL_1->setPaletteForegroundColor(opts->ircColors[1]);
+ TL_2->setPaletteForegroundColor(opts->ircColors[2]);
+ TL_3->setPaletteForegroundColor(opts->ircColors[3]);
+ TL_4->setPaletteForegroundColor(opts->ircColors[4]);
+ TL_5->setPaletteForegroundColor(opts->ircColors[5]);
+ TL_6->setPaletteForegroundColor(opts->ircColors[6]);
+ TL_7->setPaletteForegroundColor(opts->ircColors[7]);
+ TL_8->setPaletteForegroundColor(opts->ircColors[8]);
+ TL_9->setPaletteForegroundColor(opts->ircColors[9]);
+ TL_10->setPaletteForegroundColor(opts->ircColors[10]);
+ TL_11->setPaletteForegroundColor(opts->ircColors[11]);
+ TL_12->setPaletteForegroundColor(opts->ircColors[12]);
+ TL_13->setPaletteForegroundColor(opts->ircColors[13]);
+ TL_14->setPaletteForegroundColor(opts->ircColors[14]);
+ TL_15->setPaletteForegroundColor(opts->ircColors[15]);
+
+ TL_0->setFont(ksopts->defaultFont);
+ TL_1->setFont(ksopts->defaultFont);
+ TL_2->setFont(ksopts->defaultFont);
+ TL_3->setFont(ksopts->defaultFont);
+ TL_4->setFont(ksopts->defaultFont);
+ TL_5->setFont(ksopts->defaultFont);
+ TL_6->setFont(ksopts->defaultFont);
+ TL_7->setFont(ksopts->defaultFont);
+ TL_8->setFont(ksopts->defaultFont);
+ TL_9->setFont(ksopts->defaultFont);
+ TL_10->setFont(ksopts->defaultFont);
+ TL_11->setFont(ksopts->defaultFont);
+ TL_12->setFont(ksopts->defaultFont);
+ TL_13->setFont(ksopts->defaultFont);
+ TL_14->setFont(ksopts->defaultFont);
+ TL_15->setFont(ksopts->defaultFont);
+}
+
+void PageIRCColors::defaultConfig()
+{
+ KSOColors opts;
+ readConfig( &opts );
+}
+
+void PageIRCColors::changed()
+{
+ TL_0->setPaletteForegroundColor(CBtn_0->color());
+ TL_1->setPaletteForegroundColor(CBtn_1->color());
+ TL_2->setPaletteForegroundColor(CBtn_2->color());
+ TL_3->setPaletteForegroundColor(CBtn_3->color());
+ TL_4->setPaletteForegroundColor(CBtn_4->color());
+ TL_5->setPaletteForegroundColor(CBtn_5->color());
+ TL_6->setPaletteForegroundColor(CBtn_6->color());
+ TL_7->setPaletteForegroundColor(CBtn_7->color());
+ TL_8->setPaletteForegroundColor(CBtn_8->color());
+ TL_9->setPaletteForegroundColor(CBtn_9->color());
+ TL_10->setPaletteForegroundColor(CBtn_10->color());
+ TL_11->setPaletteForegroundColor(CBtn_11->color());
+ TL_12->setPaletteForegroundColor(CBtn_12->color());
+ TL_13->setPaletteForegroundColor(CBtn_13->color());
+ TL_14->setPaletteForegroundColor(CBtn_14->color());
+ TL_15->setPaletteForegroundColor(CBtn_15->color());
+ emit modified();
+}
+
+
+#include "page_irccolors.moc"
+
diff --git a/ksirc/KSPrefs/page_irccolors.h b/ksirc/KSPrefs/page_irccolors.h
new file mode 100644
index 00000000..a34045c3
--- /dev/null
+++ b/ksirc/KSPrefs/page_irccolors.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_IRCCOLORS_H
+#define PAGE_IRCCOLORS_H
+
+#include "page_irccolorsbase.h"
+#include "ksopts.h"
+
+class PageIRCColors : public PageIRCColorsBase
+{
+Q_OBJECT
+
+public:
+ PageIRCColors( QWidget *parent = 0, const char *name = 0 );
+ ~PageIRCColors();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOColors * = ksopts );
+
+protected slots:
+ void changed();
+
+public slots:
+
+signals:
+ void modified();
+
+private:
+
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_irccolorsbase.ui b/ksirc/KSPrefs/page_irccolorsbase.ui
new file mode 100644
index 00000000..96fc18cc
--- /dev/null
+++ b/ksirc/KSPrefs/page_irccolorsbase.ui
@@ -0,0 +1,1275 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PageIRCColorsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageIRCColorsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>429</width>
+ <height>552</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>IRC Colors</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel5</cstring>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;This selection allows you to control what the colors displayed inline in the channel look like. These colors are used for both mIRC style colors in channels and colorful nicks. The sample box beside the button gives you an example of what it will look like in the channel. The checkbox controls if the color is used for the colorful nick features. Checked means use it.&lt;/p&gt;</string>
+ </property>
+ </widget>
+ <widget class="QTabWidget">
+ <property name="name">
+ <cstring>tabWidget</cstring>
+ </property>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>dark</cstring>
+ </property>
+ <attribute name="title">
+ <string>Dark Colors</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox16</cstring>
+ </property>
+ <property name="title">
+ <string>Dark Colors</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_2</cstring>
+ </property>
+ <property name="text">
+ <string>Black:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>CBtn_1</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>TL_1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Black&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>genericTextLabel</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>White:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>CBtn_0</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TL_0</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;White&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_3</cstring>
+ </property>
+ <property name="text">
+ <string>Dark blue:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>CBtn_2</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TL_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Dark Blue&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_5</cstring>
+ </property>
+ <property name="text">
+ <string>Red:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="4" column="1">
+ <property name="name">
+ <cstring>CBtn_4</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>TL_4</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Red&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_4</cstring>
+ </property>
+ <property name="text">
+ <string>Dark green:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>CBtn_3</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TL_3</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Dark Green&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_6</cstring>
+ </property>
+ <property name="text">
+ <string>Brown:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="5" column="1">
+ <property name="name">
+ <cstring>CBtn_5</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>TL_5</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Brown&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_7</cstring>
+ </property>
+ <property name="text">
+ <string>Magenta:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="6" column="1">
+ <property name="name">
+ <cstring>CBtn_6</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="2">
+ <property name="name">
+ <cstring>TL_6</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Magenta&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_8</cstring>
+ </property>
+ <property name="text">
+ <string>Orange:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="7" column="1">
+ <property name="name">
+ <cstring>CBtn_7</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="2">
+ <property name="name">
+ <cstring>TL_7</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Orange&lt;p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="3">
+ <property name="name">
+ <cstring>CBox_0</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="3">
+ <property name="name">
+ <cstring>CBox_1</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="3">
+ <property name="name">
+ <cstring>CBox_2</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="3">
+ <property name="name">
+ <cstring>CBox_3</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="3">
+ <property name="name">
+ <cstring>CBox_4</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="3">
+ <property name="name">
+ <cstring>CBox_5</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="3">
+ <property name="name">
+ <cstring>CBox_6</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="3">
+ <property name="name">
+ <cstring>CBox_7</cstring>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer10</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>31</width>
+ <height>41</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="QWidget">
+ <property name="name">
+ <cstring>light</cstring>
+ </property>
+ <attribute name="title">
+ <string>Light Colors</string>
+ </attribute>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>ircColorsGB</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>IRC Channel Colors</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_11</cstring>
+ </property>
+ <property name="text">
+ <string>Dark cyan:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_12</cstring>
+ </property>
+ <property name="text">
+ <string>Cyan:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_13</cstring>
+ </property>
+ <property name="text">
+ <string>Blue:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_14</cstring>
+ </property>
+ <property name="text">
+ <string>Purple:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_15</cstring>
+ </property>
+ <property name="text">
+ <string>Gray:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_16</cstring>
+ </property>
+ <property name="text">
+ <string>Light gray:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="0" column="1">
+ <property name="name">
+ <cstring>CBtn_8</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="1" column="1">
+ <property name="name">
+ <cstring>CBtn_9</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="2" column="1">
+ <property name="name">
+ <cstring>CBtn_10</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="3" column="1">
+ <property name="name">
+ <cstring>CBtn_11</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="4" column="1">
+ <property name="name">
+ <cstring>CBtn_12</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="5" column="1">
+ <property name="name">
+ <cstring>CBtn_13</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="6" column="1">
+ <property name="name">
+ <cstring>CBtn_14</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="KColorButton" row="7" column="1">
+ <property name="name">
+ <cstring>CBtn_15</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>60</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_10</cstring>
+ </property>
+ <property name="text">
+ <string>Green:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>TL_8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Yellow&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>TL_9</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Green&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>TL_11</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Cyan&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>TL_10</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Dark Cyan&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="4" column="2">
+ <property name="name">
+ <cstring>TL_12</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Blue&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="5" column="2">
+ <property name="name">
+ <cstring>TL_13</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Purple&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="6" column="2">
+ <property name="name">
+ <cstring>TL_14</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Gray&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="7" column="2">
+ <property name="name">
+ <cstring>TL_15</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>LineEditPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="text">
+ <string>&lt;p align="center"&gt;Light Gray&lt;/p&gt;</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="3">
+ <property name="name">
+ <cstring>CBox_8</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="1" column="3">
+ <property name="name">
+ <cstring>CBox_9</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="2" column="3">
+ <property name="name">
+ <cstring>CBox_10</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="3" column="3">
+ <property name="name">
+ <cstring>CBox_11</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="4" column="3">
+ <property name="name">
+ <cstring>CBox_12</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="5" column="3">
+ <property name="name">
+ <cstring>CBox_13</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="6" column="3">
+ <property name="name">
+ <cstring>CBox_14</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="7" column="3">
+ <property name="name">
+ <cstring>CBox_15</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>genericTextLabel_9</cstring>
+ </property>
+ <property name="text">
+ <string>Yellow:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>genericTextCBtn</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer14</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>121</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>CBtn_8</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_9</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_10</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_11</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_12</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_13</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_14</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_15</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_0</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_1</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_2</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_3</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_4</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_5</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_6</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBtn_7</sender>
+ <signal>changed(const QColor&amp;)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_0</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_1</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_2</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_3</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_4</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_5</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_6</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_7</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_8</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_9</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_10</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_11</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_12</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_13</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_14</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>CBox_15</sender>
+ <signal>stateChanged(int)</signal>
+ <receiver>PageIRCColorsBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>chanaged()</slot>
+ <slot>changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+ <includehint>kcolorbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/KSPrefs/page_looknfeel.cpp b/ksirc/KSPrefs/page_looknfeel.cpp
new file mode 100644
index 00000000..931a897c
--- /dev/null
+++ b/ksirc/KSPrefs/page_looknfeel.cpp
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * *
+ * 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 <qlabel.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+
+#include <kstandarddirs.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include "page_looknfeel.h"
+
+PageLooknFeel::PageLooknFeel( QWidget *parent, const char *name ) : PageLooknFeelBase( parent, name)
+{
+ modePreview->setPixmap(QPixmap(locate("data","ksirc/pics/sdi.png")));
+ wallpaperPathLE->fileDialog()->setFilter( "*.jpg *.png *.gif" );
+}
+
+PageLooknFeel::~PageLooknFeel()
+{
+}
+
+void PageLooknFeel::saveConfig()
+{
+ if ( mdiCB->isChecked() ) ksopts->displayMode = KSOptions::MDI;
+ if ( sdiCB->isChecked() ) ksopts->displayMode = KSOptions::SDI;
+
+ ksopts->backgroundFile = wallpaperPathLE->url();
+
+}
+
+void PageLooknFeel::readConfig( const KSOGeneral *opts )
+{
+ if ( opts->displayMode == KSOptions::MDI )
+ mdiCB->setChecked( true );
+ else
+ sdiCB->setChecked( true );
+
+ wallpaperPathLE->setURL( opts->backgroundFile );
+}
+
+void PageLooknFeel::defaultConfig()
+{
+ KSOGeneral opts;
+ readConfig( &opts );
+}
+
+void PageLooknFeel::setPreviewPixmap( bool isSDI )
+{
+ if (isSDI == true)
+ modePreview->setPixmap( QPixmap( locate("data", "ksirc/pics/sdi.png" ) ) );
+ else
+ modePreview->setPixmap( QPixmap( locate("data", "ksirc/pics/mdi.png" ) ) );
+}
+
+void PageLooknFeel::showWallpaperPixmap( const QString &url )
+{
+ wallpaperPreview->setPixmap( QPixmap( url ) );
+}
+
+void PageLooknFeel::changed()
+{
+ emit modified();
+}
+
+#include "page_looknfeel.moc"
diff --git a/ksirc/KSPrefs/page_looknfeel.h b/ksirc/KSPrefs/page_looknfeel.h
new file mode 100644
index 00000000..a5c7f96f
--- /dev/null
+++ b/ksirc/KSPrefs/page_looknfeel.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_LOOKNFEEL_H
+#define PAGE_LOOKNFEEL_H
+
+#include "page_looknfeelbase.h"
+#include "ksopts.h"
+
+class PageLooknFeel : public PageLooknFeelBase
+{
+Q_OBJECT
+
+public:
+ PageLooknFeel( QWidget *parent = 0, const char *name = 0 );
+ ~PageLooknFeel();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOGeneral * = ksopts );
+
+signals:
+ void modified();
+
+public slots:
+ virtual void setPreviewPixmap( bool isSDI );
+ virtual void showWallpaperPixmap( const QString &url );
+ virtual void changed();
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_looknfeelbase.ui b/ksirc/KSPrefs/page_looknfeelbase.ui
new file mode 100644
index 00000000..6aee3a14
--- /dev/null
+++ b/ksirc/KSPrefs/page_looknfeelbase.ui
@@ -0,0 +1,323 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>PageLooknFeelBase</class>
+<widget class="QFrame">
+ <property name="name">
+ <cstring>PageLooknFeelBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>425</width>
+ <height>452</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>425</width>
+ <height>450</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>LooknFeel</string>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>windowModeGB</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>Window Mode</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer row="4" column="0">
+ <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>31</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QRadioButton" row="3" column="0">
+ <property name="name">
+ <cstring>mdiCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Paged MDI mode (XChat)</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>Spacer1_2</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>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>wmLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Choose your favorite window mode:</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ <widget class="QRadioButton" row="2" column="0">
+ <property name="name">
+ <cstring>sdiCB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;SDI mode (old behavior)</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="buttonGroupId">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QFrame" row="1" column="1" rowspan="4" colspan="1">
+ <property name="name">
+ <cstring>Frame3</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>modePreview</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>100</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>wallpaperGB</cstring>
+ </property>
+ <property name="title">
+ <string>Wallpaper</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>wallpaperPathLE</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>25</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>30</height>
+ </size>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>Frame3_2</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Raised</enum>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>wallpaperPreview</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>100</width>
+ <height>100</height>
+ </rect>
+ </property>
+ <property name="scaledContents">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>sdiCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageLooknFeelBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>sdiCB</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>PageLooknFeelBase</receiver>
+ <slot>setPreviewPixmap(bool)</slot>
+ </connection>
+ <connection>
+ <sender>mdiCB</sender>
+ <signal>clicked()</signal>
+ <receiver>PageLooknFeelBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>wallpaperPathLE</sender>
+ <signal>textChanged(const QString &amp;)</signal>
+ <receiver>PageLooknFeelBase</receiver>
+ <slot>showWallpaperPixmap(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>wallpaperPathLE</sender>
+ <signal>textChanged(const QString &amp;)</signal>
+ <receiver>PageLooknFeelBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>sdiCB</tabstop>
+ <tabstop>wallpaperPathLE</tabstop>
+</tabstops>
+<slots>
+ <slot access="protected">setPreviewPixmap(bool)</slot>
+ <slot access="protected">showWallpaperPixmap(const QString &amp;)</slot>
+ <slot>changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/KSPrefs/page_rmbmenu.cpp b/ksirc/KSPrefs/page_rmbmenu.cpp
new file mode 100644
index 00000000..7baf5da5
--- /dev/null
+++ b/ksirc/KSPrefs/page_rmbmenu.cpp
@@ -0,0 +1,206 @@
+/***************************************************************************
+ * *
+ * 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.
+ * *
+ ***************************************************************************/
+
+#include "page_rmbmenu.h"
+
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qpushbutton.h>
+
+#include <kdebug.h>
+
+#include "../usercontrolmenu.h"
+
+
+
+PageRMBMenu::PageRMBMenu( QWidget *parent, const char *name ) : PageRMBMenuBase( parent, name)
+{
+ UserControlMenu *ucm;
+
+ UserControlMenu::parseKConfig();
+
+ commandLB->clear();
+ for(ucm = UserControlMenu::UserMenu.first();
+ ucm != 0;
+ ucm = UserControlMenu::UserMenu.next()){
+
+ if(ucm->type == UserControlMenu::Seperator){
+ commandLB->insertItem("--------------", -1);
+ }
+ else{
+ commandLB->insertItem(ucm->title, -1);
+ }
+ }
+
+ changeItemPB->hide();
+
+ connect(commandLB, SIGNAL(highlighted( int )),
+ this, SLOT(highlighted( int )));
+ connect(moveUpPB, SIGNAL(clicked()),
+ this, SLOT(moveDown()));
+ connect(moveDownPB, SIGNAL(clicked()),
+ this, SLOT(moveUp()));
+
+ connect(insertSeperatorPB, SIGNAL(clicked()),
+ this, SLOT(insSeperator()));
+
+ connect(insertItemPB, SIGNAL(clicked()),
+ this, SLOT(insCommand()));
+
+ connect(deleteItemPB, SIGNAL(clicked()),
+ this, SLOT(delCommand()));
+
+}
+
+PageRMBMenu::~PageRMBMenu()
+{
+}
+
+void PageRMBMenu::saveConfig()
+{
+ UserControlMenu::writeKConfig();
+}
+
+void PageRMBMenu::readConfig( const KSORMBMenu * )
+{
+}
+
+void PageRMBMenu::defaultConfig()
+{
+}
+
+void PageRMBMenu::highlighted(int index)
+{
+ UserControlMenu *ucm;
+
+ ucm = UserControlMenu::UserMenu.at(index);
+
+ if(ucm == 0)
+ return;
+
+ if(ucm->type == UserControlMenu::Seperator){
+ entryLE->setEnabled(false);
+ commandLE->setEnabled(false);
+ opEnableCB->setEnabled(false);
+ opEnableCB->setChecked(false);
+ changeItemPB->setEnabled(false);
+ }
+ else{
+ entryLE->setEnabled(true);
+ commandLE->setEnabled(true);
+ opEnableCB->setEnabled(true);
+ opEnableCB->setChecked(true);
+ changeItemPB->setEnabled(true);
+
+ entryLE->setText(ucm->title);
+ commandLE->setText(ucm->action);
+ opEnableCB->setChecked(ucm->op_only);
+ }
+
+ if(index == 0){
+ moveUpPB->setEnabled(true);
+ moveDownPB->setEnabled(false);
+ }
+ else if((uint)index == (commandLB->count()-1)){
+ moveUpPB->setEnabled(false);
+ moveDownPB->setEnabled(true);
+ }
+ else {
+ moveUpPB->setEnabled(true);
+ moveDownPB->setEnabled(true);
+ }
+}
+
+void PageRMBMenu::moveUp()
+{
+ int item = commandLB->currentItem();
+
+ QString txt = commandLB->text(item);
+ commandLB->removeItem(item);
+
+ commandLB->insertItem(txt, item-1);
+ commandLB->setCurrentItem(item-1);
+
+ UserControlMenu *ucm = UserControlMenu::UserMenu.take(item);
+ UserControlMenu::UserMenu.insert(item-1,ucm);
+
+ highlighted(item-1);
+ emit modified();
+}
+
+void PageRMBMenu::moveDown()
+{
+ int item = commandLB->currentItem();
+
+ QString txt = commandLB->text(item);
+ commandLB->removeItem(item);
+
+ commandLB->insertItem(txt, item+1);
+ commandLB->setCurrentItem(item+1);
+
+ UserControlMenu *ucm = UserControlMenu::UserMenu.take(item);
+ UserControlMenu::UserMenu.insert(item+1,ucm);
+
+ highlighted(item+1);
+ emit modified();
+}
+
+void PageRMBMenu::insSeperator()
+{
+ int item = commandLB->currentItem();
+
+ QString txt = commandLB->text(item);
+
+ commandLB->insertItem("--------------", item);
+ commandLB->setCurrentItem(item);
+
+ UserControlMenu::UserMenu.insert(item,new UserControlMenu); // Defaults to a separator
+
+ highlighted(item);
+ emit modified();
+}
+
+void PageRMBMenu::insCommand()
+{
+ int item = commandLB->currentItem();
+
+ QString te = entryLE->text();
+ QString ce = commandLE->text();
+
+ commandLB->insertItem(te, item);
+ commandLB->setCurrentItem(item);
+
+ UserControlMenu::UserMenu.insert(item,
+ new UserControlMenu(
+ te,
+ ce,
+ 0x0,
+ UserControlMenu::Text
+ )); // Defaults to a separator
+
+ highlighted(item);
+ emit modified();
+}
+
+void PageRMBMenu::delCommand()
+{
+ int item = commandLB->currentItem();
+
+ QString txt = commandLB->text(item);
+ commandLB->removeItem(item);
+
+ UserControlMenu::UserMenu.remove(item);
+
+ highlighted(item);
+ emit modified();
+}
+
+
+#include "page_rmbmenu.moc"
diff --git a/ksirc/KSPrefs/page_rmbmenu.h b/ksirc/KSPrefs/page_rmbmenu.h
new file mode 100644
index 00000000..cb1de421
--- /dev/null
+++ b/ksirc/KSPrefs/page_rmbmenu.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_RMBMENU_H
+#define PAGE_RMBMENU_H
+
+#include "page_rmbmenubase.h"
+#include "ksopts.h"
+
+class PageRMBMenu : public PageRMBMenuBase
+{
+Q_OBJECT
+
+public:
+ PageRMBMenu( QWidget *parent = 0, const char *name = 0 );
+ ~PageRMBMenu();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSORMBMenu * = ksopts );
+protected slots:
+ void highlighted(int);
+ void moveUp();
+ void moveDown();
+ void insSeperator();
+ void insCommand();
+ void delCommand();
+signals:
+ void modified();
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_rmbmenubase.ui b/ksirc/KSPrefs/page_rmbmenubase.ui
new file mode 100644
index 00000000..1ef1e053
--- /dev/null
+++ b/ksirc/KSPrefs/page_rmbmenubase.ui
@@ -0,0 +1,252 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PageRMBMenuBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageRMBMenuBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>399</width>
+ <height>465</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Nick Option Menu</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>explLabel</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="text">
+ <string>This page allows configuration of the RMB Menu for the nicklist located on the right. You can define names for certain actions. Look at the predefined commands to learn how it works.</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring></cstring>
+ </property>
+ <property name="wordwrap" stdset="0">
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout28</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>commandLB</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout26</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>entryNameLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Entry name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>entryLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>entryLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>commandLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Associated co&amp;mmand:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>commandLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>commandLE</cstring>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>opEnableCB</cstring>
+ </property>
+ <property name="text">
+ <string>Onl&amp;y enable on Op status</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout24</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>moveUpPB</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Move Down</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>moveDownPB</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Move Up</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>10</width>
+ <height>21</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>insertSeperatorPB</cstring>
+ </property>
+ <property name="text">
+ <string>Insert &amp;Separator</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>insertItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Insert Command</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>changeItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>M&amp;odify</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>deleteItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>De&amp;lete Selected Command</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<includes>
+ <include location="global" impldecl="in declaration">kseparator.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSPrefs/page_servchan.cpp b/ksirc/KSPrefs/page_servchan.cpp
new file mode 100644
index 00000000..4689bd2a
--- /dev/null
+++ b/ksirc/KSPrefs/page_servchan.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+ * *
+ * 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 "page_servchan.h"
+
+#include <qlistbox.h>
+#include <qlineedit.h>
+#include <qpushbutton.h>
+#include <kapplication.h>
+#include <kconfig.h>
+
+PageServChan::PageServChan( QWidget *parent, const char *name ) : PageServChanBase( parent, name)
+{
+ connect(serverDeleteItemPB, SIGNAL(pressed()), this, SLOT(deletePressedSL()));
+ connect(ServerAddItemPB, SIGNAL(pressed()), this, SLOT(addPressedSL()));
+ connect(chanDeleteItmPB, SIGNAL(pressed()), this, SLOT(deletePressedCL()));
+ connect(ChanAddItemPB, SIGNAL(pressed()), this, SLOT(addPressedCL()));
+
+}
+
+PageServChan::~PageServChan()
+{
+}
+
+void PageServChan::saveConfig()
+{
+ QStringList sLB;
+ uint i;
+ for(i = 0; i < serverLB->count(); i++){
+ QString txt = serverLB->text(i);
+ if(!txt.isNull())
+ sLB << txt;
+ }
+ KConfig *conf = kapp->config();
+ conf->setGroup("ServerList");
+ conf->writeEntry("RecentServers", sLB);
+
+ QStringList sCL;
+ for(i = 0; i < channelLB->count(); i++){
+ QString txt = channelLB->text(i);
+ if(!txt.isNull())
+ sCL << txt;
+ }
+ conf->setGroup("Recent");
+ conf->writeEntry("Channels", sCL);
+}
+
+void PageServChan::readConfig( const KSOServChan * )
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup("ServerList");
+ QStringList recent = conf->readListEntry("RecentServers");
+ recent.sort();
+ serverLB->insertStringList(recent);
+ conf->setGroup("Recent");
+ recent = conf->readListEntry("Channels");
+ recent.sort();
+ channelLB->insertStringList(recent);
+}
+
+void PageServChan::defaultConfig()
+{
+ readConfig();
+}
+
+void PageServChan::deletePressedSL() {
+ int item = serverLB->currentItem();
+ if(item >= 0){
+ serverLB->removeItem(item);
+ }
+ emit modified();
+}
+
+void PageServChan::addPressedSL() {
+ uint i;
+ QString txt = LineEdit6->text();
+
+ for(i = 0; i < serverLB->count(); i++){
+ if(txt == serverLB->text(i)){
+ qWarning("Server already in the list!");
+ return;
+ }
+ }
+
+ serverLB->insertItem(txt);
+ serverLB->sort();
+ LineEdit6->clear();
+ emit modified();
+ for(i = 0; i < serverLB->count(); i++){
+ if(txt == serverLB->text(i)){
+ serverLB->setCurrentItem(i);
+ serverLB->ensureCurrentVisible();
+ return;
+ }
+ }
+}
+
+void PageServChan::deletePressedCL() {
+ int item = channelLB->currentItem();
+ if(item >= 0){
+ channelLB->removeItem(item);
+ }
+ emit modified();
+}
+
+void PageServChan::addPressedCL() {
+ uint i;
+ QString txt = LineEdit6_2->text();
+
+ for(i = 0; i < channelLB->count(); i++){
+ if(txt == channelLB->text(i)){
+ qWarning("Server already in the list!");
+ return;
+ }
+ }
+
+ channelLB->insertItem(txt);
+ channelLB->sort();
+ LineEdit6_2->clear();
+ emit modified();
+ for(i = 0; i < channelLB->count(); i++){
+ if(txt == channelLB->text(i)){
+ channelLB->setCurrentItem(i);
+ channelLB->ensureCurrentVisible();
+ return;
+ }
+ }
+}
+#include "page_servchan.moc"
diff --git a/ksirc/KSPrefs/page_servchan.h b/ksirc/KSPrefs/page_servchan.h
new file mode 100644
index 00000000..07429f2b
--- /dev/null
+++ b/ksirc/KSPrefs/page_servchan.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_SERVCHAN_H
+#define PAGE_SERVCHAN_H
+
+#include "page_servchanbase.h"
+#include "ksopts.h"
+
+class PageServChan : public PageServChanBase
+{
+Q_OBJECT
+
+public:
+ PageServChan( QWidget *parent = 0, const char *name = 0 );
+ ~PageServChan();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOServChan * = ksopts );
+
+signals:
+ void modified();
+
+public slots:
+ void deletePressedSL();
+ void addPressedSL();
+ void deletePressedCL();
+ void addPressedCL();
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_servchanbase.ui b/ksirc/KSPrefs/page_servchanbase.ui
new file mode 100644
index 00000000..b24ce9dc
--- /dev/null
+++ b/ksirc/KSPrefs/page_servchanbase.ui
@@ -0,0 +1,172 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>PageServChanBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageServChanBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>382</width>
+ <height>465</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Server/Channels</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox33</cstring>
+ </property>
+ <property name="title">
+ <string>Server</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>serverLB</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout15</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>serverDeleteItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>De&amp;lete Server From List</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>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>ServerAddItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>Add &amp;Server to List</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>LineEdit6</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>GroupBox34</cstring>
+ </property>
+ <property name="title">
+ <string>Channels</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QListBox">
+ <property name="name">
+ <cstring>channelLB</cstring>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout15_2</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>chanDeleteItmPB</cstring>
+ </property>
+ <property name="text">
+ <string>D&amp;elete Channel From List</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>Spacer2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>ChanAddItemPB</cstring>
+ </property>
+ <property name="text">
+ <string>Add Cha&amp;nnel to List</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>LineEdit6_2</cstring>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSPrefs/page_shortcuts.cpp b/ksirc/KSPrefs/page_shortcuts.cpp
new file mode 100644
index 00000000..45a83beb
--- /dev/null
+++ b/ksirc/KSPrefs/page_shortcuts.cpp
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * *
+ * 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 <qlayout.h>
+#include <qgroupbox.h>
+
+#include <kglobalaccel.h>
+
+#include <kkeydialog.h>
+#include "page_shortcuts.h"
+#include "../servercontroller.h"
+
+PageShortcuts::PageShortcuts( QWidget *parent, const char *name ) : PageShortcutsBase( parent, name)
+{
+ globalGB->setColumnLayout( 0, Qt::Horizontal );
+
+ m_key = new KKeyChooser(servercontroller::self()->getGlobalAccel(), globalGB);
+ connect(m_key, SIGNAL(keyChange()), this, SLOT(changed()));
+ globalGB->layout()->add(m_key);
+}
+
+PageShortcuts::~PageShortcuts()
+{
+}
+
+void PageShortcuts::saveConfig()
+{
+ m_key->commitChanges();
+ servercontroller::self()->getGlobalAccel()->writeSettings();
+ servercontroller::self()->getGlobalAccel()->updateConnections();
+}
+
+void PageShortcuts::readConfig( const KSOGeneral *opts )
+{
+ servercontroller::self()->getGlobalAccel()->readSettings();
+}
+
+void PageShortcuts::defaultConfig()
+{
+ KSOGeneral opts;
+ readConfig( &opts );
+ m_key->allDefault();
+}
+
+
+void PageShortcuts::changed()
+{
+ emit modified();
+}
+
+#include "page_shortcuts.moc"
diff --git a/ksirc/KSPrefs/page_shortcuts.h b/ksirc/KSPrefs/page_shortcuts.h
new file mode 100644
index 00000000..4c16aa35
--- /dev/null
+++ b/ksirc/KSPrefs/page_shortcuts.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_SHORTCUTS_H
+#define PAGE_SHORTCUTS_H
+
+#include "page_shortcutsbase.h"
+#include "ksopts.h"
+
+class KKeyChooser;
+
+class PageShortcuts : public PageShortcutsBase
+{
+Q_OBJECT
+
+public:
+ PageShortcuts( QWidget *parent = 0, const char *name = 0 );
+ ~PageShortcuts();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOGeneral * = ksopts );
+
+signals:
+ void modified();
+
+public slots:
+ virtual void changed();
+
+private:
+ KKeyChooser *m_key;
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_shortcutsbase.ui b/ksirc/KSPrefs/page_shortcutsbase.ui
new file mode 100644
index 00000000..57f21647
--- /dev/null
+++ b/ksirc/KSPrefs/page_shortcutsbase.ui
@@ -0,0 +1,57 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PageShortcutsBase</class>
+<widget class="QFrame">
+ <property name="name">
+ <cstring>PageShortcutslBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>425</width>
+ <height>452</height>
+ </rect>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>425</width>
+ <height>450</height>
+ </size>
+ </property>
+ <property name="caption">
+ <string>Shortcuts</string>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>globalGB</cstring>
+ </property>
+ <property name="title">
+ <string>Global Shortcuts</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+</connections>
+<tabstops>
+</tabstops>
+<slots>
+ <slot access="protected">setPreviewPixmap(bool)</slot>
+ <slot access="protected">showWallpaperPixmap(const QString &amp;)</slot>
+ <slot>changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/ksirc/KSPrefs/page_startup.cpp b/ksirc/KSPrefs/page_startup.cpp
new file mode 100644
index 00000000..060da44d
--- /dev/null
+++ b/ksirc/KSPrefs/page_startup.cpp
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * *
+ * 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 <qlineedit.h>
+#include <qpushbutton.h>
+#include <qlistbox.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <keditlistbox.h>
+
+#include <kdebug.h>
+
+#include "page_startup.h"
+
+PageStartup::PageStartup( QWidget *parent, const char *name ) : PageStartupBase( parent, name)
+{
+ notifyLB->upButton()->hide();
+ notifyLB->downButton()->hide();
+ serverLB->upButton()->hide();
+ serverLB->downButton()->hide();
+
+ QListBox *lb = serverLB->listBox();
+ connect(lb, SIGNAL(highlighted(int)),
+ this, SLOT(clickedLB(int)));
+
+ changing = false;
+
+}
+
+PageStartup::~PageStartup()
+{
+}
+
+void PageStartup::saveConfig()
+{
+ KSOServer glb = ksopts->server["global"];
+ QStringList items = serverLB->items();
+
+ ksopts->server.clear();
+
+ QStringList::iterator it = items.begin();
+ for( ; it != items.end(); ++it){
+ ksopts->server[*it] = server[*it];
+ }
+ if(!ksopts->server.contains("global")){
+ ksopts->server["global"] = glb;
+ }
+
+}
+
+void PageStartup::readConfig( const KSOptions *opts )
+{
+ server = opts->server;
+
+ changing = true;
+ ServerOpMap::Iterator it;
+ for ( it = server.begin(); it != server.end(); ++it ) {
+ if(it.data().globalCopy == false)
+ serverLB->insertItem(it.key());
+ }
+ QListBoxItem *item = serverLB->listBox()->findItem("global");
+ serverLB->listBox()->setSelected(item, true);
+ changing = false;
+ clickedLB(serverLB->listBox()->index(item));
+
+}
+
+void PageStartup::defaultConfig()
+{
+ KSOptions opts;
+ readConfig( &opts );
+}
+
+void PageStartup::changed()
+{
+ emit modified();
+
+ QString ser = serverLB->currentText();
+ if(ser.isEmpty())
+ return;
+
+ if(changing)
+ return;
+
+ kdDebug(5008) << "got changed for: " << ser <<endl;
+
+ server[ser].nick = nickLE->text();
+ server[ser].altNick = altNickLE->text();
+ server[ser].realName = rnLE->text();
+ server[ser].userID = uiLE->text();
+
+ server[ser].notifyList.clear();
+ for ( int i = 0; i < notifyLB->count(); ++i)
+ server[ser].notifyList.append( notifyLB->text( i ) );
+ server[ser].globalCopy = false;
+
+}
+
+void PageStartup::clickedLB(int index)
+{
+
+ QString text = serverLB->text(index);
+ if(!server.contains(text)){
+ server[text] = server["global"];
+ server[text].globalCopy = true;
+ }
+
+ changing = true;
+ notifyLB->clear();
+
+ nickLE->setText( server[text].nick );
+ altNickLE->setText( server[text].altNick );
+ rnLE->setText( server[text].realName );
+ uiLE->setText( server[text].userID );
+ notifyLB->insertStringList( server[text].notifyList );
+ changing = false;
+}
+
+#include "page_startup.moc"
diff --git a/ksirc/KSPrefs/page_startup.h b/ksirc/KSPrefs/page_startup.h
new file mode 100644
index 00000000..ecac8305
--- /dev/null
+++ b/ksirc/KSPrefs/page_startup.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ * *
+ * 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 PAGE_STARTUP_H
+#define PAGE_STARTUP_H
+
+#include "page_startupbase.h"
+#include "ksopts.h"
+
+class PageStartup : public PageStartupBase
+{
+Q_OBJECT
+
+public:
+ PageStartup( QWidget *parent = 0, const char *name = 0 );
+ ~PageStartup();
+
+ void saveConfig();
+ void defaultConfig();
+ void readConfig( const KSOptions * = ksopts );
+
+signals:
+ void modified();
+
+protected slots:
+ void changed();
+ void clickedLB(int index);
+
+private:
+ ServerOpMap server;
+ bool changing;
+};
+
+#endif
diff --git a/ksirc/KSPrefs/page_startupbase.ui b/ksirc/KSPrefs/page_startupbase.ui
new file mode 100644
index 00000000..1a248806
--- /dev/null
+++ b/ksirc/KSPrefs/page_startupbase.ui
@@ -0,0 +1,197 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PageStartupBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PageStartupBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>420</width>
+ <height>578</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>StartUp</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="KEditListBox">
+ <property name="name">
+ <cstring>serverLB</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Server</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>nickGB</cstring>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>32767</width>
+ <height>32767</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Name Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>nickLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>altNickLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>rnLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>nickLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Nick name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>nickLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>anLabel</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;lternative nick:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>altNickLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>uiLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>uiLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;User ID:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rnLE</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>rnLabel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Real name:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>rnLE</cstring>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="KEditListBox">
+ <property name="name">
+ <cstring>notifyLB</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Notify List</string>
+ </property>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>nickLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>altNickLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>notifyLB</sender>
+ <signal>changed()</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>rnLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>uiLE</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>changed()</slot>
+ </connection>
+ <connection>
+ <sender>serverLB</sender>
+ <signal>changed()</signal>
+ <receiver>PageStartupBase</receiver>
+ <slot>server_changed()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>changed()</slot>
+ <slot>server_changed()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>keditlistbox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>keditlistbox.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/KSProgress/Makefile.am b/ksirc/KSProgress/Makefile.am
new file mode 100644
index 00000000..b24cc64f
--- /dev/null
+++ b/ksirc/KSProgress/Makefile.am
@@ -0,0 +1,11 @@
+KDE_CXXFLAGS = $(USE_RTTI) $(USE_EXCEPTIONS)
+
+INCLUDES= $(all_includes)
+
+noinst_LTLIBRARIES = libksprogress.la
+
+libksprogress_la_SOURCES = ksprogress.cpp ksprogressdata.cpp
+libksprogress_la_METASOURCES = AUTO
+
+noinst_HEADERS = ksprogress.h ksprogressdata.h
+
diff --git a/ksirc/KSProgress/ksprogress.cpp b/ksirc/KSProgress/ksprogress.cpp
new file mode 100644
index 00000000..369b0742
--- /dev/null
+++ b/ksirc/KSProgress/ksprogress.cpp
@@ -0,0 +1,60 @@
+/**********************************************************************
+
+
+ *********************************************************************/
+
+#include "ksprogress.h"
+
+#undef Inherited
+#define Inherited ksprogressData
+
+KSProgress::KSProgress
+(
+ QWidget* parent,
+ const char* name
+)
+ :
+ Inherited( parent, name )
+{
+ setCaption("KSProgress");
+ id = "";
+ canceled = FALSE;
+}
+
+
+void KSProgress::setID(QString _id)
+{
+ id = _id;
+}
+
+void KSProgress::setRange(int minValue, int maxValue)
+{
+ progress->setRange(minValue, maxValue);
+}
+
+void KSProgress::setTopText(QString text)
+{
+ fileName->setText(text);
+}
+
+void KSProgress::setBotText(QString text)
+{
+ transferStatus->setText(text);
+}
+
+void KSProgress::setValue(int value)
+{
+ progress->setValue(value);
+}
+
+void KSProgress::cancelPressed()
+{
+ canceled = TRUE;
+ emit cancel();
+ emit cancel(id);
+}
+
+KSProgress::~KSProgress()
+{
+}
+#include "ksprogress.moc"
diff --git a/ksirc/KSProgress/ksprogress.dlg b/ksirc/KSProgress/ksprogress.dlg
new file mode 100644
index 00000000..5484c4d6
--- /dev/null
+++ b/ksirc/KSProgress/ksprogress.dlg
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<QtArch type="Dialog" version="2.1" nested="true" >
+ <Dialog supertype="true" nested="true" >
+ <DialogCommon nested="true" >
+ <SourceDir >.</SourceDir>
+ <ClassHeader >ksprogress.h</ClassHeader>
+ <ClassSource >ksprogress.cpp</ClassSource>
+ <ClassName >KSProgress</ClassName>
+ <DataHeader >ksprogressdata.h</DataHeader>
+ <DataSource >ksprogressdata.cpp</DataSource>
+ <DataName >ksprogressData</DataName>
+ <WindowBaseClass >QFrame</WindowBaseClass>
+ <ClassHeader >ksprogress.h</ClassHeader>
+ </DialogCommon>
+ <WidgetLayout nested="true" >
+ <WidgetLayoutCommon nested="true" >
+ <InitialPos type="qpoint" complex="true" nested="true" >
+ <X type="integer">-1</X>
+ <Y type="integer">-1</Y>
+ </InitialPos>
+ <Size type="qsize" complex="true" nested="true" >
+ <Height type="integer">130</Height>
+ <Width type="integer">250</Width>
+ </Size>
+ <MinSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">140</Height>
+ <Width type="integer">250</Width>
+ </MinSize>
+ <MaxSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">140</Height>
+ <Width type="integer">250</Width>
+ </MaxSize>
+ <GridSize type="integer">10</GridSize>
+ </WidgetLayoutCommon>
+ <Widgets list="true" nested="true" >
+ <User nested="true" >
+ <UserClassHeader >kprogress.h</UserClassHeader>
+ <UserClassName >KProgress</UserClassName>
+ <DlgWidget nested="true" >
+ <Rect type="qrect" complex="true" nested="true" >
+ <Height type="integer">20</Height>
+ <Width type="integer">230</Width>
+ <X type="integer">10</X>
+ <Y type="integer">60</Y>
+ </Rect>
+ <Name >User_1</Name>
+ <Variable >progress</Variable>
+ <SignalConnection list="true" nested="true" >
+ </SignalConnection>
+ <MinimumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">10</Height>
+ <Width type="integer">10</Width>
+ </MinimumSize>
+ <MaximumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">32767</Height>
+ <Width type="integer">32767</Width>
+ </MaximumSize>
+ <Enabled type="boolean">true</Enabled>
+ <FocusPolicy >NoFocus</FocusPolicy>
+ <BackgroundMode >PaletteBackground</BackgroundMode>
+ <BackgroundOrigin >WidgetOrigin</BackgroundOrigin>
+ </DlgWidget>
+ </User>
+ <PushButton nested="true" >
+ <Button nested="true" >
+ <Text >&amp;Cancel</Text>
+ <DlgWidget nested="true" >
+ <Rect type="qrect" complex="true" nested="true" >
+ <Height type="integer">30</Height>
+ <Width type="integer">100</Width>
+ <X type="integer">70</X>
+ <Y type="integer">90</Y>
+ </Rect>
+ <Name >PushButton_1</Name>
+ <SignalConnection list="true" nested="true" >
+ <Signal >[Protected] clicked --&gt; cancelPressed ()</Signal>
+ </SignalConnection>
+ <MinimumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">10</Height>
+ <Width type="integer">10</Width>
+ </MinimumSize>
+ <MaximumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">32767</Height>
+ <Width type="integer">32767</Width>
+ </MaximumSize>
+ <Enabled type="boolean">true</Enabled>
+ <FocusPolicy >TabFocus</FocusPolicy>
+ <BackgroundMode >PaletteButton</BackgroundMode>
+ <BackgroundOrigin >WidgetOrigin</BackgroundOrigin>
+ </DlgWidget>
+ </Button>
+ </PushButton>
+ <Label nested="true" >
+ <Text >fileName</Text>
+ <Alignment >AlignLeft|AlignVCenter|ExpandTabs</Alignment>
+ <Indent type="integer">-1</Indent>
+ <TextFormat type="qstring">AutoText</TextFormat>
+ <Frame nested="true" >
+ <LineWidth type="integer">1</LineWidth>
+ <DlgWidget nested="true" >
+ <Rect type="qrect" complex="true" nested="true" >
+ <Height type="integer">20</Height>
+ <Width type="integer">230</Width>
+ <X type="integer">10</X>
+ <Y type="integer">10</Y>
+ </Rect>
+ <Name >Label_1</Name>
+ <Variable >fileName</Variable>
+ <SignalConnection list="true" nested="true" >
+ </SignalConnection>
+ <MinimumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">10</Height>
+ <Width type="integer">10</Width>
+ </MinimumSize>
+ <MaximumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">32767</Height>
+ <Width type="integer">32767</Width>
+ </MaximumSize>
+ <Enabled type="boolean">true</Enabled>
+ <FocusPolicy >NoFocus</FocusPolicy>
+ <BackgroundMode >PaletteBackground</BackgroundMode>
+ <BackgroundOrigin >WidgetOrigin</BackgroundOrigin>
+ </DlgWidget>
+ </Frame>
+ </Label>
+ <Label nested="true" >
+ <Text >XferStatus</Text>
+ <Alignment >AlignLeft|AlignVCenter|ExpandTabs</Alignment>
+ <Indent type="integer">-1</Indent>
+ <TextFormat type="qstring">AutoText</TextFormat>
+ <Frame nested="true" >
+ <LineWidth type="integer">1</LineWidth>
+ <DlgWidget nested="true" >
+ <Rect type="qrect" complex="true" nested="true" >
+ <Height type="integer">20</Height>
+ <Width type="integer">230</Width>
+ <X type="integer">10</X>
+ <Y type="integer">35</Y>
+ </Rect>
+ <Name >Label_2</Name>
+ <Variable >transferStatus</Variable>
+ <SignalConnection list="true" nested="true" >
+ </SignalConnection>
+ <MinimumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">10</Height>
+ <Width type="integer">10</Width>
+ </MinimumSize>
+ <MaximumSize type="qsize" complex="true" nested="true" >
+ <Height type="integer">32767</Height>
+ <Width type="integer">32767</Width>
+ </MaximumSize>
+ <Enabled type="boolean">true</Enabled>
+ <FocusPolicy >NoFocus</FocusPolicy>
+ <BackgroundMode >PaletteBackground</BackgroundMode>
+ <BackgroundOrigin >WidgetOrigin</BackgroundOrigin>
+ </DlgWidget>
+ </Frame>
+ </Label>
+ </Widgets>
+ <TabOrder list="true" nested="true" >
+ <Widget type="qstring">PushButton_1</Widget>
+ </TabOrder>
+ <Layout list="true" nested="true" >
+ </Layout>
+ </WidgetLayout>
+ </Dialog>
+</QtArch>
diff --git a/ksirc/KSProgress/ksprogress.h b/ksirc/KSProgress/ksprogress.h
new file mode 100644
index 00000000..c3feb2c6
--- /dev/null
+++ b/ksirc/KSProgress/ksprogress.h
@@ -0,0 +1,47 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ksprogress.h
+ Last generated: Thu Dec 18 08:01:39 1997
+
+ *********************************************************************/
+
+#ifndef KSProgress_included
+#define KSProgress_included
+
+#include "ksprogressdata.h"
+
+class KSProgress : public ksprogressData
+{
+ Q_OBJECT
+
+public:
+
+ KSProgress
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~KSProgress();
+
+ virtual void setID(QString);
+ virtual void setRange(int minValue, int maxValue);
+ virtual void setTopText(QString text);
+ virtual void setBotText(QString text);
+
+public slots:
+ virtual void setValue(int value);
+ virtual void cancelPressed();
+
+signals:
+ void cancel();
+ void cancel(QString);
+
+private:
+ QString id;
+ bool canceled;
+
+};
+#endif // KSProgress_included
diff --git a/ksirc/KSProgress/ksprogressdata.cpp b/ksirc/KSProgress/ksprogressdata.cpp
new file mode 100644
index 00000000..0feb524b
--- /dev/null
+++ b/ksirc/KSProgress/ksprogressdata.cpp
@@ -0,0 +1,72 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ksprogressdata.cpp
+ Last generated: Thu Dec 18 09:09:53 1997
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#include "ksprogressdata.h"
+
+#undef Inherited
+#define Inherited QFrame
+
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#include <klocale.h>
+
+ksprogressData::ksprogressData
+(
+ QWidget* parent,
+ const char* name
+)
+ :
+ Inherited( parent, name, 0 )
+{
+ progress = new KProgress( this, "User_1" );
+ progress->setGeometry( 10, 60, 230, 20 );
+ progress->setMinimumSize( 10, 10 );
+ progress->setMaximumSize( 32767, 32767 );
+
+ QPushButton* dlgedit_PushButton_1;
+ dlgedit_PushButton_1 = new KPushButton( KStdGuiItem::cancel(), this, "PushButton_1" );
+ dlgedit_PushButton_1->setGeometry( 70, 90, 100, 30 );
+ dlgedit_PushButton_1->setMinimumSize( 10, 10 );
+ dlgedit_PushButton_1->setMaximumSize( 32767, 32767 );
+ connect( dlgedit_PushButton_1, SIGNAL(clicked()), SLOT(cancelPressed()) );
+ dlgedit_PushButton_1->setAutoRepeat( FALSE );
+
+ fileName = new QLabel( this, "Label_1" );
+ fileName->setGeometry( 10, 10, 230, 20 );
+ fileName->setMinimumSize( 10, 10 );
+ fileName->setMaximumSize( 32767, 32767 );
+ fileName->setText( i18n("File Name") );
+ fileName->setAlignment( 289 );
+ fileName->setMargin( -1 );
+
+ transferStatus = new QLabel( this, "Label_2" );
+ transferStatus->setGeometry( 10, 35, 230, 20 );
+ transferStatus->setMinimumSize( 10, 10 );
+ transferStatus->setMaximumSize( 32767, 32767 );
+ transferStatus->setText( i18n("Transfer Status") );
+ transferStatus->setAlignment( 289 );
+ transferStatus->setMargin( -1 );
+
+ resize( 250,130 );
+ setMinimumSize( 250, 140 );
+ setMaximumSize( 250, 140 );
+}
+
+
+ksprogressData::~ksprogressData()
+{
+}
+void ksprogressData::cancelPressed()
+{
+}
+#include "ksprogressdata.moc"
diff --git a/ksirc/KSProgress/ksprogressdata.h b/ksirc/KSProgress/ksprogressdata.h
new file mode 100644
index 00000000..a2282baf
--- /dev/null
+++ b/ksirc/KSProgress/ksprogressdata.h
@@ -0,0 +1,48 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ksprogressdata.h
+ Last generated: Thu Dec 18 09:09:53 1997
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#ifndef ksprogressData_included
+#define ksprogressData_included
+
+#include <qframe.h>
+#include <qlabel.h>
+#include "kprogress.h"
+
+class ksprogressData : public QFrame
+{
+ Q_OBJECT
+
+public:
+
+ ksprogressData
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~ksprogressData();
+
+public slots:
+
+
+protected slots:
+
+ virtual void cancelPressed();
+
+protected:
+ KProgress* progress;
+ QLabel* fileName;
+ QLabel* transferStatus;
+
+};
+
+#endif // ksprogressData_included
diff --git a/ksirc/KSTicker/Makefile.am b/ksirc/KSTicker/Makefile.am
new file mode 100644
index 00000000..ab937def
--- /dev/null
+++ b/ksirc/KSTicker/Makefile.am
@@ -0,0 +1,20 @@
+INCLUDES= $(all_includes)
+
+noinst_LTLIBRARIES = libksticker.la
+
+libksticker_la_SOURCES = ksticker.cpp speeddialog.cpp speeddialogData.cpp \
+ kspainter.cpp
+
+noinst_HEADERS = ksticker.h speeddialog.h speeddialogData.h \
+ kspainter.h
+
+METASOURCES = AUTO
+
+
+check_PROGRAMS = ksttest
+
+ksttest_SOURCES = ksttest.cpp
+ksttest_LDADD = libksticker.la ../ksopts.lo ../nickColourMaker.lo $(LIB_KDEUI)
+ksttest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+
diff --git a/ksirc/KSTicker/kspainter.cpp b/ksirc/KSTicker/kspainter.cpp
new file mode 100644
index 00000000..fe5fbf65
--- /dev/null
+++ b/ksirc/KSTicker/kspainter.cpp
@@ -0,0 +1,186 @@
+#include "kspainter.h"
+#include "../ksopts.h"
+#include <stdlib.h>
+
+const int KSPainter::optcolours = 8;
+const int KSPainter::startspecial = 16;
+const int KSPainter::maxcolour = 16+optcolours;
+
+const QColor KSPainter::brown ( 165, 42, 42 );
+const QColor KSPainter::orange ( 255, 165, 0 );
+const QColor KSPainter::lightCyan( 224, 255, 255 );
+const QColor KSPainter::lightBlue( 173, 216, 230 );
+const QColor KSPainter::pink ( 255, 192, 203 );
+
+QColor KSPainter::num2colour[KSPainter::maxcolour] = { Qt::white,
+ Qt::black,
+ Qt::darkBlue,
+ Qt::darkGreen,
+ Qt::red,
+ brown,
+ Qt::darkMagenta,
+ orange,
+ Qt::yellow,
+ Qt::green,
+ Qt::darkCyan,
+ Qt::cyan,
+ Qt::blue,
+ pink,
+ Qt::gray,
+ Qt::lightGray };
+
+void KSPainter::initOptColours()
+{
+ num2colour[startspecial+0] = ksopts->textColor;
+ num2colour[startspecial+1] = ksopts->infoColor;
+ num2colour[startspecial+2] = ksopts->channelColor;
+ num2colour[startspecial+3] = ksopts->errorColor;
+ num2colour[startspecial+4] = ksopts->ownNickColor;
+ num2colour[startspecial+5] = ksopts->nickForeground;
+ num2colour[startspecial+6] = ksopts->nickBackground;
+ num2colour[startspecial+7] = ksopts->backgroundColor;
+}
+
+int KSPainter::colour2num(const QColor &colour)
+{
+ for(int i = 0; i < maxcolour; i++)
+ if(num2colour[i] == colour)
+ return i;
+
+ return -1;
+}
+
+void KSPainter::colourDrawText(QPainter *p, int startx, int starty,
+ char *str, int length)
+{
+ int offset = 0;
+ int pcolour;
+ char buf[3];
+ int loc = 0, i;
+ buf[2] = 0;
+ bool ReverseText = FALSE;
+
+ // Default pen (colour)
+
+ const QPen qpDefPen = p->pen();
+
+ for(loc = 0; (str[loc] != 0x00) && (loc != length) ; loc++){
+ if(str[loc] == 0x03 || str[loc] == '~'){
+ i = loc;
+ p->drawText(startx, starty, str + offset, i-offset);
+ startx += p->fontMetrics().width(str + offset, i-offset);
+ offset = i;
+ // lastp = i;
+ if((str[i+1] >= 0x30) && (str[i+1] <= 0x39)){
+ i++;
+ buf[0] = str[i];
+ i++;
+ if((str[i] >= 0x30) && (str[i] <= 0x39)){
+ buf[1] = str[i];
+ i++;
+ }
+ else{
+ buf[1] = 0;
+ }
+
+ pcolour = atoi(buf);
+ if(pcolour < maxcolour)
+ p->setPen(num2colour[pcolour]);
+ else
+ i = loc;
+ if(str[i] == ','){
+ i++;
+ if((str[i] >= 0x30) && (str[i] <= 0x39)){
+ buf[0] = str[i];
+ i++;
+ if((str[i] >= 0x30) && (str[i] <= 0x39)){
+ buf[1] = str[i];
+ i++;
+ }
+ else{
+ buf[1] = 0;
+ }
+ int bcolour = atoi(buf);
+ if(pcolour == bcolour){
+ if(bcolour + 1 < maxcolour)
+ bcolour += 1;
+ else
+ bcolour -= 1;
+ }
+ if(bcolour < maxcolour ){
+ p->setBackgroundColor(num2colour[bcolour]);
+ p->setBackgroundMode(Qt::OpaqueMode);
+ }
+
+ }
+ }
+ }
+ else if(str[i] == 0x03){
+ i++;
+ p->setPen(qpDefPen);
+ p->setBackgroundMode(Qt::TransparentMode);
+ }
+ else if((str[i] == '~') && ((str[i+1] >= 0x61) || (str[i+1] <= 0x7a))){
+ QFont fnt = p->font();
+ QColor temppen;
+ switch(str[i+1]){
+ case 'c':
+ p->setPen(qpDefPen);
+ p->setBackgroundMode(Qt::TransparentMode);
+ break;
+ case 'C':
+ p->setPen(qpDefPen);
+ p->setBackgroundMode(Qt::TransparentMode);
+ fnt.setBold(FALSE);
+ fnt.setItalic(FALSE);
+ fnt.setUnderline(FALSE);
+ ReverseText = TRUE; // Force reverse removed, all fall through.
+ // FALL THROUGH.
+ case 'r':
+ if(ReverseText == TRUE) {
+ ReverseText = FALSE;
+ p->setBackgroundMode(Qt::TransparentMode);
+ }
+ else {
+ ReverseText = TRUE;
+ p->setBackgroundMode(Qt::OpaqueMode);
+ }
+ temppen = p->pen().color();
+ p->setPen( p->backgroundColor() );
+ p->setBackgroundColor( temppen );
+ break;
+ case 'b':
+ if(fnt.bold() == TRUE)
+ fnt.setBold(FALSE);
+ else
+ fnt.setBold(TRUE);
+ break;
+ case 'i':
+ if(fnt.italic() == TRUE)
+ fnt.setItalic(FALSE);
+ else
+ fnt.setItalic(TRUE);
+ break;
+ case 'u':
+ if(fnt.underline() == TRUE)
+ fnt.setUnderline(FALSE);
+ else
+ fnt.setUnderline(TRUE);
+ break;
+ case '~':
+ loc++; // Skip ahead 2 characters
+ break;
+ default:
+ i-=1;
+ offset -= 1;
+ }
+ p->setFont(fnt);
+ i += 2;
+ }
+ offset += i - loc;
+ }
+ }
+ p->drawText(startx, starty, str + offset, loc-offset);
+
+}
+
diff --git a/ksirc/KSTicker/kspainter.h b/ksirc/KSTicker/kspainter.h
new file mode 100644
index 00000000..21f9495d
--- /dev/null
+++ b/ksirc/KSTicker/kspainter.h
@@ -0,0 +1,25 @@
+#ifndef KSPAINTER_H
+#define KSPAINTER_H
+
+#include <qpainter.h>
+#include <qobject.h>
+
+class KSPainter
+{
+public:
+ static void initOptColours();
+ static void colourDrawText(QPainter *p, int startx, int starty, char *str, int length = -1);
+ static const int startspecial;
+ static const int maxcolour;
+ static const int optcolours;
+ static QColor num2colour[16+8];
+ static int colour2num(const QColor &colour);
+
+ static const QColor brown;
+ static const QColor orange;
+ static const QColor lightCyan;
+ static const QColor lightBlue;
+ static const QColor pink;
+};
+
+#endif
diff --git a/ksirc/KSTicker/ksticker.cpp b/ksirc/KSTicker/ksticker.cpp
new file mode 100644
index 00000000..dc65118a
--- /dev/null
+++ b/ksirc/KSTicker/ksticker.cpp
@@ -0,0 +1,548 @@
+#include <stdlib.h>
+#include <qpainter.h>
+#include <qframe.h>
+#include <qpaintdevice.h>
+#include <qcursor.h>
+#include <qtooltip.h>
+#include <qregexp.h>
+
+#include <kapplication.h>
+#include <kfontdialog.h>
+#include <klocale.h>
+
+#include "ksticker.h"
+#include "speeddialog.h"
+
+#include "kspainter.h"
+#include "../ksopts.h"
+#include "../nickColourMaker.h"
+
+#include <kconfig.h>
+#include <kwin.h>
+#include <kdebug.h>
+
+KSTicker::KSTicker(QWidget * parent, const char * name, WFlags f)
+: QFrame(parent, name, f)
+{
+
+ pHeight = 1;
+
+ pic = new QPixmap(); // create pic map here, resize it later though.
+ // pic->setBackgroundMode(TransparentMode);
+
+ KConfig *conf = kapp->config();
+ conf->setGroup("KSTicker");
+ bScrollConstantly = conf->readNumEntry("ScollConst", FALSE);
+
+ bAtEnd = FALSE;
+ setFont(conf->readFontEntry("Font", &ksopts->defaultFont));
+ ourFont = font(); //QFont("Courier");
+ setFont(ourFont);
+ setMinimumSize(100, 10);
+ setFixedHeight((fontMetrics().height()+fontMetrics().descent()*2)*pHeight);
+ descent = fontMetrics().descent();
+ onechar = fontMetrics().width("X");
+
+ pic->resize(width() + onechar, height());
+ pic->fill(backgroundColor());
+
+ tickStep = 2;
+ cOffset = 0;
+
+ tickRate = 30;
+
+ currentChar = 0;
+ chars = this->width() / onechar;
+
+ popup = new QPopupMenu();
+ popup->insertItem(i18n( "Font..." ), this, SLOT(fontSelector()));
+ popup->insertItem(i18n( "Scroll Rate..." ), this, SLOT(scrollRate()));
+ iScrollItem = popup->insertItem(i18n( "Scroll Constantly" ), this, SLOT(scrollConstantly()));
+ popup->setItemChecked(iScrollItem, bScrollConstantly);
+ popup->insertSeparator();
+ popup->insertItem(i18n( "Return to Normal Mode" ), this, SIGNAL(doubleClick()));
+
+ currentStr = "";
+
+ /*
+ * Tell KWM to use only minimum decurations
+ */
+ // ### FIXME: port to kwin
+ //KWM::setDecoration(winId(), KWM::tinyDecoration);
+ // KWin::setType(winId(), NET::Override);
+
+ /*
+ * Setup basic colours and background status info for ticker.
+ */
+
+ KSPainter::initOptColours();
+
+ bold = FALSE;
+ underline = FALSE;
+ italics = FALSE;
+ defbg = ksopts->backgroundColor;
+ deffg = ksopts->textColor;
+ setBackgroundColor(defbg);
+ bg = ksopts->backgroundColor;
+ fg = ksopts->textColor;
+
+}
+
+KSTicker::~KSTicker()
+{
+ killTimers();
+ delete pic;
+}
+
+void KSTicker::show()
+{
+ /*
+ * Tell KWM to use only minimum decurations
+ */
+ // ### FIXME: port to kwin
+ //KWM::setDecoration(winId(), KWM::tinyDecoration);
+ QSize size = this->size();
+ size.setHeight(QFontMetrics(font()).height()+10);
+ setFixedHeight(QFontMetrics(font()).height()+10);
+ resize(size);
+
+ QFrame::show();
+ if(currentStr.length() != 0)
+ startTicker();
+ currentChar = 0;
+ repaint(TRUE);
+}
+
+void KSTicker::hide()
+{
+ killTimers();
+ QFrame::hide();
+}
+
+void KSTicker::iconify()
+{
+ QFrame::showMinimized();
+ killTimers();
+}
+
+void KSTicker::setString(QString str)
+{
+ strlist.clear();
+ strlist.append(str);
+ repaint(TRUE);
+ startTicker();
+}
+
+void KSTicker::mergeString(QString str, QColor c)
+{
+ int num = KSPainter::colour2num(c);
+
+ if(num != -1){
+ str.prepend(QString("~%1").arg(num));
+ }
+ mergeString(str);
+
+}
+
+void KSTicker::mergeString(QString str)
+{
+ QRegExp rx("~n(\\S+)~n");
+ if(rx.search(str) >= 0){
+ int value = nickColourMaker::colourMaker()->findIdx(rx.cap(1));
+ if(value >= 0){
+ QString newText = QString("~%1\\1~c").arg(value);
+ str.replace(rx, newText);
+ }
+ }
+
+ str.append("~C ");
+ strlist.append(str);
+
+ if(strlist.count() > 5){
+ int found = 0;
+ QStringList::Iterator it = strlist.begin();
+ for(; it != strlist.end(); it++){
+ if(((*it).find(ksopts->server["global"].nick, 0, FALSE) == -1) &&
+ ((*it).find(ksopts->server["global"].altNick, 0, FALSE) == -1)){
+ strlist.remove(it);
+ found = 1;
+ break;
+ }
+ else {
+ }
+ }
+ if(found == 0){
+ strlist.pop_front();
+ }
+ }
+
+ if(bScrollConstantly == FALSE)
+ startTicker();
+
+ QStringList sep = QStringList::split(" ", stripCols(str));
+ int len = 0;
+ QString brok;
+ QStringList::Iterator it = sep.begin();
+ for(; it != sep.end(); ++it) {
+ brok += *it + " ";
+ len += (*it).length();
+ if(len >= 50){
+ brok += "\n";
+ len = 0;
+ }
+ }
+ if(brok.endsWith("\n"))
+ brok.truncate(brok.length()-1);
+
+ tipbuffer.append(brok);
+ while(tipbuffer.count() > 10)
+ tipbuffer.pop_front();
+ QString tip = tipbuffer.join("\n");
+
+// QToolTip::remove(this);
+ QToolTip::add(this, tip);
+
+
+}
+
+void KSTicker::timerEvent(QTimerEvent *)
+{
+ if((uint)currentChar >= currentStr.length()){
+ if(strlist.isEmpty()){
+ if(bScrollConstantly == TRUE){
+ currentStr = strbuffer.first();
+ strbuffer.append(strbuffer.first());
+ strbuffer.pop_front();
+ currentChar = 0;
+ }
+ else{
+ stopTicker();
+ return;
+ }
+ }
+ else {
+ currentStr = strlist.first();
+ strlist.pop_front();
+ strbuffer.append(currentStr);
+ while(strbuffer.count() > 10)
+ strbuffer.pop_front();
+ currentChar = 0;
+ }
+ }
+
+ bAtEnd = FALSE;
+ static BGMode bgmode = TransparentMode;
+
+ bitBlt(pic, -tickStep, 0, pic);
+ QPainter p(pic);
+
+ cOffset += tickStep;
+ if(cOffset >= onechar){
+ int step = 1; // Used to check if we did anything, and hence
+ // catch ~c~c type things. Set to 1 to start loop
+ while(((currentStr[currentChar] == '~') || (currentStr[currentChar] == 0x03))
+ && (step > 0)){
+ step = 1; // reset in case it's our second, or more loop.
+ QString text = currentStr.mid(currentChar);
+ QString buf = "";
+
+ if((text[step] >= '0') &&
+ (text[step] <= '9')) {
+ buf += text[step];
+ step++;
+ if((text[step] >= '0') &&
+ (text[step] <= '9')) {
+ buf += text[step];
+ step++;
+ }
+ int col = buf.toInt();
+ if((col >= 0) || (col <= KSPainter::maxcolour)){
+ fg = KSPainter::num2colour[col];
+ }
+ bg = defbg;
+ buf = "";
+ if(text[step] == ','){
+ step++;
+ if((text[step] >= '0') &&
+ (text[step] <= '9')) {
+ buf += text[step];
+ step++;
+ if((text[step] >= '0') &&
+ (text[step] <= '9')) {
+ buf += text[step];
+ step++;
+ }
+ int col = buf.toInt();
+ if((col >= 0) || (col <= KSPainter::maxcolour)){
+ bg = KSPainter::num2colour[col];
+ bgmode = OpaqueMode;
+ }
+ }
+ }
+ else{
+ bgmode = TransparentMode;
+ }
+ }
+ else {
+ switch(text[step].latin1()){
+ case 'c':
+ fg = deffg;
+ bg = defbg;
+ step++;
+ break;
+ case 'C':
+ fg = deffg;
+ bg = defbg;
+ bold = FALSE;
+ underline = FALSE;
+ italics = FALSE;
+ step++;
+ break;
+ case '#':
+ fg.setNamedColor(text.mid(step, 7));
+ step += 7;
+ break;
+ case 'b':
+ bold == TRUE ? bold = FALSE : bold = TRUE;
+ step++;
+ break;
+ case 'u':
+ underline == TRUE ? underline = FALSE : underline = TRUE;
+ step++;
+ break;
+ case 'i':
+ italics == TRUE ? italics = FALSE : italics = TRUE;
+ step++;
+ break;
+ case 'n':
+ fg = ksopts->nickForeground;
+ bg = ksopts->nickBackground;
+ step++;
+ break;
+ case 'o':
+ fg = ksopts->msgContainNick;
+ step++;
+ break;
+ case '~':
+ currentChar++; // Move ahead 1, but...
+ step = 0; // Don't loop on next ~.
+ break;
+ default:
+ if(currentStr[currentChar] == 0x03){
+ fg = deffg;
+ bg = defbg;
+ }
+ else
+ step = 0;
+ }
+ }
+ currentChar += step;
+ }
+ if((uint)currentChar >= currentStr.length()){ // Bail out if we're
+ // at the end of the string.
+ return;
+ }
+
+
+ QFont fnt = font();
+ fnt.setBold(bold);
+ fnt.setUnderline(underline);
+ fnt.setItalic(italics);
+ p.setFont(fnt);
+
+ p.setPen(fg);
+ p.setBackgroundColor(bg);
+ p.setBackgroundMode(OpaqueMode);
+ p.drawText(this->width() - cOffset + onechar, // remove -onechar.
+ this->height() / 4 + p.fontMetrics().height() / 2,
+ currentStr.mid(currentChar, 1),
+ 1);
+ currentChar++; // Move ahead to next character.
+ cOffset -= onechar; // Set offset back one.
+ }
+ p.end();
+ bitBlt(this, 0, descent, pic);
+}
+
+void KSTicker::paintEvent( QPaintEvent *)
+{
+ if(isVisible() == FALSE)
+ return;
+ bitBlt(this, 0, descent, pic);
+}
+
+void KSTicker::resizeEvent( QResizeEvent *e)
+{
+ QFrame::resizeEvent(e);
+ onechar = fontMetrics().width("X");
+ chars = this->width() / onechar;
+ killTimers();
+ QPixmap *new_pic = new QPixmap(width() + onechar, height());
+ new_pic->fill(backgroundColor());
+ bitBlt(new_pic,
+ new_pic->width() - pic->width(), 0,
+ pic, 0, 0,
+ pic->width(), pic->height(),
+ CopyROP, TRUE);
+ delete pic;
+ pic = new_pic;
+ // if(ring.length() > (uint) chars)
+ startTicker();
+}
+
+void KSTicker::closeEvent( QCloseEvent *)
+{
+ emit closing();
+ killTimers();
+ // delete this;
+}
+
+void KSTicker::startTicker()
+{
+ killTimers();
+ startTimer(tickRate);
+}
+
+void KSTicker::stopTicker()
+{
+ killTimers();
+}
+
+void KSTicker::mouseDoubleClickEvent( QMouseEvent * )
+{
+ emit doubleClick();
+}
+
+void KSTicker::mousePressEvent( QMouseEvent *e)
+{
+ if(e->button() == RightButton){
+ popup->popup(this->cursor().pos());
+ }
+ else{
+ QFrame::mousePressEvent(e);
+ }
+}
+void KSTicker::fontSelector()
+{
+ int result = KFontDialog::getFont( ourFont, true );
+ if ( result == KFontDialog::Accepted ) {
+ updateFont(ourFont);
+ }
+}
+
+void KSTicker::scrollRate()
+{
+ SpeedDialog *sd = new SpeedDialog(tickRate, tickStep);
+ sd->setLimit(5, 200, 1, onechar);
+ connect(sd, SIGNAL(stateChange(int, int)),
+ this, SLOT(setSpeed(int, int)));
+ sd->show();
+}
+
+void KSTicker::scrollConstantly()
+{
+ bScrollConstantly = !bScrollConstantly;
+ popup->setItemChecked(iScrollItem, bScrollConstantly);
+ if(bScrollConstantly == TRUE)
+ startTicker();
+ KConfig *conf = kapp->config();
+ conf->setGroup("KSTicker");
+ conf->writeEntry("ScollConst", bScrollConstantly);
+ conf->sync();
+}
+
+void KSTicker::updateFont(const QFont &font){
+ setFont(font);
+ setFixedHeight((fontMetrics().height()+fontMetrics().descent()*2)*pHeight);
+ resize(fontMetrics().width("X")*chars,
+ (fontMetrics().height()+fontMetrics().descent())*pHeight);
+ KConfig *conf = kapp->config();
+ conf->setGroup("KSTicker");
+ conf->writeEntry("Font", font);
+ conf->sync();
+
+}
+
+void KSTicker::setSpeed(int _tickRate, int _tickStep){
+ tickRate = _tickRate;
+ tickStep = _tickStep;
+ startTicker();
+}
+
+void KSTicker::speed(int *_tickRate, int *_tickStep){
+ *_tickRate = tickRate;
+ *_tickStep = tickStep;
+}
+
+void KSTicker::setBackgroundColor ( const QColor &c )
+{
+ QFrame::setBackgroundColor(c);
+ pic->fill(c);
+ bitBlt(this, 0,0, pic);
+ defbg = backgroundColor();
+ bg = backgroundColor();
+}
+
+void KSTicker::setPalette ( const QPalette & p )
+{
+ QFrame::setPalette(p);
+
+ pic->fill(backgroundColor());
+ bitBlt(this, 0,0, pic);
+ defbg = backgroundColor();
+ bg = backgroundColor();
+ deffg = backgroundColor();
+ fg = foregroundColor();
+}
+
+QString KSTicker::stripCols( QString str )
+{
+ uint i = 0;
+ QString ret;
+ for(i = 0; i < str.length(); i++){
+ if(((str[i] == '~') || (str[i] == 0x03))){
+ if((str[i+1] >= '0') &&
+ (str[i+1] <= '9')) {
+ i+=1;
+ if((str[i+1] >= '0') &&
+ (str[i+1] <= '9')) {
+ i+=1;
+ }
+ if(str[i+1] == ','){
+ i+=1;
+ if((str[i+1] >= '0') &&
+ (str[i+1] <= '9')) {
+ i+=1;
+ if((str[i+1] >= '0') &&
+ (str[i+1] <= '9')) {
+ i+=1;
+ }
+ }
+ }
+ }
+ else {
+ switch(str[i+1].latin1()){
+ case 'c':
+ case 'C':
+ case 'b':
+ case 'u':
+ case 'i':
+ case 'n':
+ case 'o':
+ i+= 1;
+ break;
+ case '~':
+ i+= 0;
+ break;
+ default:
+ ret.append(str[i]);
+ }
+ }
+ }
+ else {
+ ret.append(str[i]);
+ }
+ }
+ return ret;
+}
+#include "ksticker.moc"
diff --git a/ksirc/KSTicker/ksticker.h b/ksirc/KSTicker/ksticker.h
new file mode 100644
index 00000000..d11d8a0f
--- /dev/null
+++ b/ksirc/KSTicker/ksticker.h
@@ -0,0 +1,106 @@
+#ifndef KSTICKER_H
+#define KSTICKER_H
+
+#include <qobject.h>
+#include <qframe.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qpopupmenu.h>
+
+class SInfo {
+public:
+ int length;
+};
+
+class KSTicker : public QFrame
+{
+ Q_OBJECT
+
+public:
+ KSTicker(QWidget * parent=0, const char * name=0, WFlags f=0);
+ virtual ~KSTicker();
+
+ void setString(QString);
+ void mergeString(QString);
+ void mergeString(QString, QColor);
+
+ virtual void show();
+ virtual void hide();
+
+ void speed(int *, int *);
+
+ virtual void setBackgroundColor ( const QColor & );
+ virtual void setPalette ( const QPalette & p );
+
+signals:
+ void doubleClick();
+ void closing();
+
+public slots:
+ virtual void setSpeed(int, int);
+
+protected slots:
+ virtual void fontSelector();
+ virtual void scrollRate();
+ virtual void updateFont(const QFont &font);
+ virtual void scrollConstantly();
+
+protected:
+ virtual void timerEvent ( QTimerEvent * );
+ virtual void paintEvent ( QPaintEvent * );
+ virtual void resizeEvent( QResizeEvent * );
+ virtual void closeEvent( QCloseEvent * );
+ virtual void mouseDoubleClickEvent( QMouseEvent * );
+ virtual void mousePressEvent ( QMouseEvent * );
+ virtual void iconify();
+
+
+private:
+
+ QString stripCols(QString);
+
+ QStringList strlist; /* everything left to parse */
+ QStringList strbuffer; /* fifo of the last 10 lines to scroll in scroll constantly mode */
+ QStringList tipbuffer; /* 5 lines for the tooltip */
+ QString currentStr; /* the string we are currently parsing */
+
+ QFont ourFont;
+
+ int onechar;
+ int chars;
+ int descent;
+
+ int tickStep;
+ int cOffset;
+
+ int tickRate;
+
+ int pHeight;
+
+ int currentChar;
+
+ void startTicker();
+ void stopTicker();
+
+ bool bScrollConstantly;
+ int iScrollItem;
+ bool bAtEnd;
+
+ QPixmap *pic;
+
+ QPopupMenu *popup;
+
+ /*
+ * Drawing settings and variables
+ */
+ bool bold;
+ bool underline;
+ bool italics;
+ QColor defbg;
+ QColor deffg;
+ QColor bg;
+ QColor fg;
+};
+
+#endif // KSTICKER_H
+
diff --git a/ksirc/KSTicker/ksttest.cpp b/ksirc/KSTicker/ksttest.cpp
new file mode 100644
index 00000000..81373bd8
--- /dev/null
+++ b/ksirc/KSTicker/ksttest.cpp
@@ -0,0 +1,93 @@
+#include <kapplication.h>
+#include <kconfig.h>
+#include <qapplication.h>
+#include <qsocketnotifier.h>
+#include <qregexp.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+#include <unistd.h>
+
+#include "ksticker.h"
+#include "ksttest.h"
+#include "../ksopts.h"
+
+KConfig *kConfig;
+
+StdInTicker::StdInTicker()
+ : KSTicker()
+{
+ kConfig->setGroup("defaults");
+ QFont font;
+ font = kConfig->readFontEntry("font");
+ font.setFixedPitch(TRUE);
+ setFont(font);
+ setSpeed(kConfig->readNumEntry("tick", 30),
+ kConfig->readNumEntry("step", 3));
+}
+
+StdInTicker::~StdInTicker()
+{
+ int tick, step;
+ speed(&tick, &step);
+ kConfig->setGroup("defaults");
+ kConfig->writeEntry("font", KSTicker::font());
+ kConfig->writeEntry("tick", tick);
+ kConfig->writeEntry("step", step);
+ kConfig->writeEntry("text", colorGroup().text() );
+ kConfig->writeEntry("background", colorGroup().background() );
+ kConfig->sync();
+}
+
+void StdInTicker::readsocket(int socket)
+{
+ char buf[1024];
+ int bytes = read(socket, buf, 1024);
+ if(bytes){
+ QCString str(buf, bytes);
+ str.replace(QRegExp("\n"), " // ");
+ mergeString(str);
+ }
+}
+
+void StdInTicker::end()
+{
+ delete this;
+}
+
+void StdInTicker::closeEvent ( QCloseEvent *e )
+{
+ KSTicker::closeEvent(e);
+ delete this;
+}
+
+
+int main(int argc, char **argv){
+ KAboutData aboutData( "ksirc", I18N_NOOP("KSirc"),
+ "2.0.0", "", KAboutData::License_Artistic,
+ I18N_NOOP("(c) 1997-2002, Andrew Stanley-Jones"));
+ aboutData.addAuthor("Andrew Stanley-Jones",I18N_NOOP("Original Author"), "asj-ksirc@cban.com");
+ KCmdLineArgs::init( argc, argv, &aboutData );
+
+ KApplication a(argc, argv);
+
+ kConfig = a.config();
+
+ // Options
+ KSOptions opts;
+ opts.load();
+
+ StdInTicker *kst = new StdInTicker();
+ QSocketNotifier *sn = new QSocketNotifier(0, QSocketNotifier::Read);
+ QObject::connect(sn, SIGNAL(activated(int)),
+ kst, SLOT(readsocket(int)));
+ QObject::connect(kst, SIGNAL(doubleClick()), kst, SLOT(end()));
+ QObject::connect(kst, SIGNAL(closing()), kst, SLOT(end()));
+ a.setMainWidget(kst);
+ kst->show();
+ return a.exec();
+}
+
+#include "ksttest.moc"
+
diff --git a/ksirc/KSTicker/ksttest.h b/ksirc/KSTicker/ksttest.h
new file mode 100644
index 00000000..9d166b66
--- /dev/null
+++ b/ksirc/KSTicker/ksttest.h
@@ -0,0 +1,24 @@
+#ifndef _KST_STD_IN_
+#define _KST_STD_IN_
+
+
+#include "ksticker.h"
+
+class StdInTicker : public KSTicker
+{
+ Q_OBJECT
+public:
+ StdInTicker();
+ ~StdInTicker();
+
+protected:
+ void closeEvent ( QCloseEvent * );
+
+public slots:
+ void readsocket(int socket);
+ void end();
+
+};
+
+#endif
+
diff --git a/ksirc/KSTicker/libksticker.c b/ksirc/KSTicker/libksticker.c
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/ksirc/KSTicker/libksticker.c
diff --git a/ksirc/KSTicker/speeddialog.cpp b/ksirc/KSTicker/speeddialog.cpp
new file mode 100644
index 00000000..eb1ff462
--- /dev/null
+++ b/ksirc/KSTicker/speeddialog.cpp
@@ -0,0 +1,61 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: speeddialog.cpp
+ Last generated: Sun Dec 21 08:52:31 1997
+
+ *********************************************************************/
+
+#include <klocale.h>
+
+#include "speeddialog.h"
+
+SpeedDialog::SpeedDialog
+(
+ int tick,
+ int step,
+ QWidget* parent,
+ const char* name
+)
+ : speeddialogData( parent, name )
+{
+ setCaption(i18n( "Speed Setup") );
+ connect(sliderTick, SIGNAL(valueChanged(int)),
+ lcdTick, SLOT(display(int)));
+ connect(sliderStep, SIGNAL(valueChanged(int)),
+ lcdStep, SLOT(display(int)));
+ lcdTick->display(tick);
+ sliderTick->setValue(tick);
+ lcdStep->display(step);
+ sliderStep->setValue(step);
+}
+
+
+SpeedDialog::~SpeedDialog()
+{
+}
+
+
+void SpeedDialog::updateTick(int tick)
+{
+ emit stateChange(tick, sliderStep->value());
+}
+
+void SpeedDialog::updateStep(int step)
+{
+ emit stateChange(sliderTick->value(), step);
+}
+
+void SpeedDialog::terminate()
+{
+ delete this;
+}
+
+void SpeedDialog::setLimit(int tmin, int tmax, int smin, int smax)
+{
+ sliderTick->setRange(tmin, tmax);
+ sliderStep->setRange(smin, smax);
+}
+
+#include "speeddialog.moc"
diff --git a/ksirc/KSTicker/speeddialog.dlg b/ksirc/KSTicker/speeddialog.dlg
new file mode 100644
index 00000000..00e26c67
--- /dev/null
+++ b/ksirc/KSTicker/speeddialog.dlg
@@ -0,0 +1,117 @@
+DlgEdit:v1.2:Dialog:
+Dialog {
+ ClassHeader {speeddialog.h}
+ ClassSource {speeddialog.cpp}
+ ClassName {SpeedDialog}
+ DataHeader {speeddialogData.h}
+ DataSource {speeddialogData.cpp}
+ DataName {speeddialogData}
+ WindowBaseClass {QDialog}
+ IsModal {FALSE}
+ WindowFlags {0}
+}
+WidgetLayout {
+InitialPos {-1 -1}
+Size {270 120}
+MinSize {0 0}
+MaxSize {32767 32767}
+Grid {10}
+
+Slider {
+ Orientation {Horizontal}
+ MinValue {10}
+ MaxValue {200}
+ Initial {30}
+ LineStep {10}
+ PageStep {50}
+ Tracking {TRUE}
+ Tickmarks {NoMarks}
+ TickInterval {0}
+ Rect {90 10 100 20}
+ Name {Slider_1}
+ Variable {sliderTick}
+ Signal {[Protected] valueChanged --> updateTick (int)}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Slider {
+ Orientation {Horizontal}
+ MinValue {1}
+ MaxValue {10}
+ Initial {3}
+ LineStep {1}
+ PageStep {2}
+ Tracking {TRUE}
+ Tickmarks {NoMarks}
+ TickInterval {0}
+ Rect {90 45 100 20}
+ Name {Slider_2}
+ Variable {sliderStep}
+ Signal {[Protected] valueChanged --> updateStep (int)}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+LCDNumber {
+ Digits {3}
+ SmallDecimalPoint {FALSE}
+ Mode {Dec}
+ SegmentStyle {Outline}
+ Style {33}
+ Rect {200 5 60 30}
+ Name {LCDNumber_1}
+ Variable {lcdTick}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+LCDNumber {
+ Digits {3}
+ SmallDecimalPoint {FALSE}
+ Mode {Dec}
+ SegmentStyle {Outline}
+ Style {33}
+ Rect {200 40 60 30}
+ Name {LCDNumber_2}
+ Variable {lcdStep}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Label {
+ Text {Tick Interval}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {10 5 80 30}
+ Name {Label_1}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Label {
+ Text {Step Size}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {10 40 80 30}
+ Name {Label_2}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {&Close}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {140 80 120 30}
+ Name {PushButton_2}
+ Signal {[Protected] pressed --> terminate ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Layout {None}
+}
diff --git a/ksirc/KSTicker/speeddialog.h b/ksirc/KSTicker/speeddialog.h
new file mode 100644
index 00000000..4efdb2a8
--- /dev/null
+++ b/ksirc/KSTicker/speeddialog.h
@@ -0,0 +1,42 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: speeddialog.h
+ Last generated: Sun Dec 21 08:52:31 1997
+
+ *********************************************************************/
+
+#ifndef SpeedDialog_included
+#define SpeedDialog_included
+
+#include "speeddialogData.h"
+
+class SpeedDialog : public speeddialogData
+{
+ Q_OBJECT
+
+public:
+
+ SpeedDialog
+ (
+ int tick,
+ int step,
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~SpeedDialog();
+ void setLimit(int, int, int, int);
+
+signals:
+ void stateChange(int, int);
+
+protected slots:
+ virtual void updateTick(int);
+ virtual void updateStep(int);
+ virtual void terminate();
+
+
+};
+#endif // SpeedDialog_included
diff --git a/ksirc/KSTicker/speeddialogData.cpp b/ksirc/KSTicker/speeddialogData.cpp
new file mode 100644
index 00000000..0dbb1c0b
--- /dev/null
+++ b/ksirc/KSTicker/speeddialogData.cpp
@@ -0,0 +1,107 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: speeddialogData.cpp
+ Last generated: Sun Dec 21 09:13:46 1997
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#include "speeddialogData.h"
+
+#include <qlabel.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+#include <qlayout.h>
+#include <kdialog.h>
+
+speeddialogData::speeddialogData ( QWidget* parent, const char* name )
+ :QDialog( parent, name, 0 )
+{
+ QGridLayout *grid = new QGridLayout( this, 3, 2 , KDialog::marginHint(), KDialog::spacingHint());
+ sliderTick = new QSlider( this, "Slider_1" );
+ grid->addWidget( sliderTick, 0, 1 );
+ connect( sliderTick, SIGNAL(valueChanged(int)), SLOT(updateTick(int)) );
+ sliderTick->setOrientation( QSlider::Horizontal );
+ sliderTick->setRange( 10, 200 );
+ sliderTick->setSteps( 10, 50 );
+ sliderTick->setValue( 30 );
+ sliderTick->setTracking( TRUE );
+ sliderTick->setTickmarks( QSlider::NoMarks );
+ sliderTick->setTickInterval( 0 );
+
+ sliderStep = new QSlider( this, "Slider_2" );
+ grid->addWidget( sliderStep, 1, 1 );
+
+ connect( sliderStep, SIGNAL(valueChanged(int)), SLOT(updateStep(int)) );
+ sliderStep->setOrientation( QSlider::Horizontal );
+ sliderStep->setRange( 1, 10 );
+ sliderStep->setSteps( 1, 2 );
+ sliderStep->setValue( 3 );
+ sliderStep->setTracking( TRUE );
+ sliderStep->setTickmarks( QSlider::NoMarks );
+ sliderStep->setTickInterval( 0 );
+
+ lcdTick = new QLCDNumber( this, "LCDNumber_1" );
+ lcdTick->setFrameStyle( 33 );
+ lcdTick->setSmallDecimalPoint( FALSE );
+ lcdTick->setNumDigits( 3 );
+ lcdTick->setMode( QLCDNumber::DEC );
+ lcdTick->setSegmentStyle( QLCDNumber::Outline );
+ grid->addWidget( lcdTick, 0, 2 );
+
+ lcdStep = new QLCDNumber( this, "LCDNumber_2" );
+ lcdStep->setFrameStyle( 33 );
+ lcdStep->setSmallDecimalPoint( FALSE );
+ lcdStep->setNumDigits( 3 );
+ lcdStep->setMode( QLCDNumber::DEC );
+ lcdStep->setSegmentStyle( QLCDNumber::Outline );
+ grid->addWidget( lcdStep, 1, 2 );
+
+
+ QLabel* dlgedit_Label_1;
+ dlgedit_Label_1 = new QLabel( this, "Label_1" );
+ dlgedit_Label_1->setText( i18n("Tick interval:") );
+ dlgedit_Label_1->setAlignment( 289 );
+ dlgedit_Label_1->setMargin( -1 );
+ grid->addWidget( dlgedit_Label_1, 0, 0 );
+
+
+ QLabel* dlgedit_Label_2;
+ dlgedit_Label_2 = new QLabel( this, "Label_2" );
+ dlgedit_Label_2->setText( i18n("Step size:") );
+ dlgedit_Label_2->setAlignment( 289 );
+ dlgedit_Label_2->setMargin( -1 );
+ grid->addWidget( dlgedit_Label_2, 1, 0 );
+
+
+ QPushButton* dlgedit_PushButton_2;
+ dlgedit_PushButton_2 = new KPushButton( KStdGuiItem::close(), this, "PushButton_2" );
+ connect( dlgedit_PushButton_2, SIGNAL(pressed()), SLOT(terminate()) );
+// dlgedit_PushButton_2->setAutoRepeat( FALSE );
+// dlgedit_PushButton_2->setAutoResize( FALSE );
+ grid->addWidget( dlgedit_PushButton_2, 2, 2 );
+
+ //resize( 270,120 );
+ setMinimumSize( 0, 0 );
+ setMaximumSize( 32767, 32767 );
+}
+
+
+speeddialogData::~speeddialogData()
+{
+}
+void speeddialogData::updateTick(int)
+{
+}
+void speeddialogData::updateStep(int)
+{
+}
+void speeddialogData::terminate()
+{
+}
+#include "speeddialogData.moc"
diff --git a/ksirc/KSTicker/speeddialogData.h b/ksirc/KSTicker/speeddialogData.h
new file mode 100644
index 00000000..6e9f06f9
--- /dev/null
+++ b/ksirc/KSTicker/speeddialogData.h
@@ -0,0 +1,51 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: speeddialogData.h
+ Last generated: Sun Dec 21 09:13:46 1997
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#ifndef speeddialogData_included
+#define speeddialogData_included
+
+#include <qdialog.h>
+#include <qslider.h>
+#include <qlcdnumber.h>
+
+class speeddialogData : public QDialog
+{
+ Q_OBJECT
+
+public:
+
+ speeddialogData
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~speeddialogData();
+
+public slots:
+
+
+protected slots:
+
+ virtual void terminate();
+ virtual void updateTick(int);
+ virtual void updateStep(int);
+
+protected:
+ QSlider* sliderTick;
+ QSlider* sliderStep;
+ QLCDNumber* lcdTick;
+ QLCDNumber* lcdStep;
+
+};
+
+#endif // speeddialogData_included
diff --git a/ksirc/Makefile.am b/ksirc/Makefile.am
new file mode 100644
index 00000000..103472c1
--- /dev/null
+++ b/ksirc/Makefile.am
@@ -0,0 +1,146 @@
+
+KDE_CXXFLAGS = $(USE_RTTI) $(USE_EXCEPTIONS)# -UQT_NO_ASCII_CAST
+
+# set the include path for X, qt and KDE
+INCLUDES= -IKSOpenkSirc -IKSPrefs $(all_includes)
+
+# claim, which subdirectories you want to install
+SUBDIRS = KSTicker KSProgress KSPrefs KSOpenkSirc img icons
+
+METASOURCES = AUTO
+
+check_PROGRAMS = testview dccMgrTest
+
+testview_SOURCES = kstextviewtest.cpp
+testview_LDADD = kstextview.lo dccNew.lo dccNewbase.lo alistbox.lo objFinder.lo ksopts.lo nickColourMaker.lo $(LIB_KUTILS) $(LIB_KDEUI)
+testview_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+dccMgrTest_SOURCES = dccMgrTest.cpp
+dccMgrTest_LDADD = dccManager.lo dccManagerbase.lo dccNew.lo dccNewbase.lo alistbox.lo objFinder.lo ksopts.lo nickColourMaker.lo $(LIB_KUTILS) $(LIB_KDEUI)
+dccMgrTest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = ksirc.la
+
+noinst_PROGRAMS = getdate
+
+# Which sources should be compiled for ksirc.
+
+ksirc_la_SOURCES = \
+ toplevel.cpp \
+ charSelector.cpp \
+ chanButtons.cpp \
+ ksirc.cpp \
+ alistbox.cpp \
+ ahistlineedit.cpp \
+ iocontroller.cpp \
+ NewWindowDialog.cpp \
+ messageReceiver.cpp \
+ ioBroadcast.cpp \
+ ksircprocess.cpp \
+ servercontroller.cpp \
+ FilterRuleWidget.ui FilterRuleEditor.cpp \
+ ioDiscard.cpp \
+ ioDCC.cpp \
+ ioLAG.cpp \
+ ioNotify.cpp \
+ ssfeprompt.cpp ssfepromptdata.cpp \
+ usercontrolmenu.cpp \
+ baserules.cpp \
+ chanparser.cpp \
+ objFinder.cpp \
+ displayMgrSDI.cpp \
+ displayMgrMDI.cpp \
+ mditoplevel.cpp \
+ ksopts.cpp \
+ topic.cpp \
+ ksview.cpp \
+ logfile.cpp \
+ kstextview.cpp \
+ colorpicker.cpp \
+ ksparser.cpp \
+ dccManagerbase.ui \
+ dccManager.cpp \
+ dccToplevel.cpp \
+ dockservercontroller.cpp \
+ nickColourMaker.cpp \
+ dccNewbase.ui \
+ dccNew.cpp
+
+# the library search path
+ksirc_la_LDFLAGS = $(all_libraries) -module -avoid-version
+ksirc_la_COMPILE_FIRST = cdate.h
+
+all_ksirc_libraries = \
+ ./KSPrefs/libksprefs.la \
+ ./KSProgress/libksprogress.la \
+ ./KSOpenkSirc/libksopenksirc.la \
+ ./KSTicker/libksticker.la \
+ $(LIB_KIO) $(LIBDL)
+
+# the libraries to link against. Be aware of the order. First the libraries,
+# that depend on the following ones.
+ksirc_la_LIBADD = $(all_ksirc_libraries)
+
+kdoc:
+ kdoc -ddoc ksirc *.h
+
+getdate_SOURCES = getdate.c
+
+cdate.h: getdate
+ ./getdate > cdate.h
+
+# this option you can leave out. Just, if you use "make dist", you need it
+#
+# DSIRC is on the bottom so make dist includes it!!!!!!!!!
+#
+noinst_HEADERS = \
+ FilterRuleEditor.h \
+ control_message.h \
+ FilterRuleWidget.h \
+ messageReceiver.h \
+ ioBroadcast.h \
+ ioDCC.h \
+ usercontrolmenu.h \
+ ioDiscard.h \
+ ahistlineedit.h \
+ iocontroller.h \
+ servercontroller.h \
+ alistbox.h \
+ chanparser.h \
+ toplevel.h \
+ ksircprocess.h \
+ cdate.h \
+ ioLAG.h \
+ ioNotify.h \
+ ssfeprompt.h \
+ ssfepromptdata.h \
+ objFinder.h \
+ baserules.h \
+ displayMgr.h \
+ displayMgrSDI.h \
+ NewWindowDialog.h \
+ ksopts.h \
+ ksview.h \
+ boundscheckingarray.h \
+ ksparser.h \
+ dccManager.h \
+ dccToplevel.h \
+ dockservercontroller.h \
+ dccNew.h
+
+messages: rc.cpp
+ $(EXTRACTRC) KSOpenkSirc/*.ui >> rc.cpp
+ $(EXTRACTRC) KSPrefs/*.ui >> rc.cpp
+ $(XGETTEXT) *.cpp */*.cpp -o $(podir)/ksirc.pot
+
+bin_SCRIPTS = dsirc
+
+xdg_apps_DATA = ksirc.desktop
+
+misc_DATA = ksirc.pl filters.pl autodcc.pl sirc.help.gz relnotes eventsrc
+miscdir = $(kde_datadir)/ksirc
+
+conf_DATA = ksircrc
+confdir = $(kde_confdir)
diff --git a/ksirc/NewWindowDialog.cpp b/ksirc/NewWindowDialog.cpp
new file mode 100644
index 00000000..c4af16f1
--- /dev/null
+++ b/ksirc/NewWindowDialog.cpp
@@ -0,0 +1,73 @@
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <klineedit.h>
+#include "NewWindowDialog.h"
+
+NewWindowDialog::NewWindowDialog(const KSircChannel &channelInfo, QWidget * parent, const char * name)
+ : KDialogBase(parent, name, true, i18n("New Window For"), Ok|Cancel, Ok, true),
+ m_channelInfo(channelInfo)
+{
+ QHBox * w = makeHBoxMainWidget();
+
+ QLabel * l = new QLabel(i18n("C&hannel/Nick:"), w);
+
+ m_combo = new KHistoryCombo(w);
+ m_combo->setFocus();
+
+ // we don't need duplicated channel into the list
+ m_combo->setDuplicatesEnabled( false );
+
+ l->setBuddy(m_combo);
+
+ QLabel * l2 = new QLabel(i18n("&Key:"), w);
+ m_le = new KLineEdit(w);
+ m_le->setEnabled(false);
+ l2->setBuddy(m_le);
+
+ connect(
+ m_combo, SIGNAL(activated(const QString &)),
+ m_combo, SLOT(addToHistory(const QString &)));
+ connect( m_combo->lineEdit(), SIGNAL(textChanged ( const QString & )),
+ this, SLOT( slotTextChanged( const QString &)));
+
+ KConfig *kConfig = kapp->config();
+ KConfigGroupSaver saver(kConfig, "Recent");
+ m_combo->setHistoryItems(kConfig->readListEntry("Channels"));
+ slotTextChanged( m_combo->lineEdit()->text());
+}
+
+NewWindowDialog::~NewWindowDialog()
+{
+ KConfig *kConfig = kapp->config();
+ KConfigGroupSaver saver(kConfig, "Recent");
+ kConfig->writeEntry("Channels", m_combo->historyItems());
+}
+
+void NewWindowDialog::slotTextChanged( const QString &text)
+{
+ enableButtonOK( !text.isEmpty() );
+
+ if(text[0] == "#" || text[0] == "&")
+ m_le->setEnabled(true);
+ else
+ m_le->setEnabled(false);
+}
+
+
+ void
+NewWindowDialog::slotOk()
+{
+ m_channelInfo.setChannel(m_combo->lineEdit()->text().lower());
+ if(m_le->isEnabled())
+ m_channelInfo.setKey(m_le->text());
+ emit(openTopLevel(m_channelInfo));
+ KDialogBase::slotOk();
+}
+
+#include "NewWindowDialog.moc"
+
diff --git a/ksirc/NewWindowDialog.h b/ksirc/NewWindowDialog.h
new file mode 100644
index 00000000..37449e0e
--- /dev/null
+++ b/ksirc/NewWindowDialog.h
@@ -0,0 +1,35 @@
+#ifndef NEW_WINDOW_DIALOG_H
+#define NEW_WINDOW_DIALOG_H
+
+#include <kdialogbase.h>
+
+#include "ksircchannel.h"
+
+class KHistoryCombo;
+class KLineEdit;
+
+class NewWindowDialog : public KDialogBase
+{
+ Q_OBJECT
+
+ public:
+
+ NewWindowDialog(const KSircChannel &, QWidget * parent = 0, const char * name = 0);
+ ~NewWindowDialog();
+
+ protected slots:
+
+ void slotOk();
+ void slotTextChanged( const QString &);
+ signals:
+
+ void openTopLevel(const KSircChannel &);
+
+ private:
+
+ KHistoryCombo * m_combo;
+ KLineEdit * m_le;
+ KSircChannel m_channelInfo;
+};
+
+#endif
diff --git a/ksirc/advfollow.pl b/ksirc/advfollow.pl
new file mode 100644
index 00000000..c7254641
--- /dev/null
+++ b/ksirc/advfollow.pl
@@ -0,0 +1,125 @@
+#
+#
+&print("*** Loading advance follow...");
+
+@K_VAL_COL = (0,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+
+print "*** Using colours:";
+foreach (@K_VAL_COL) {
+ print "~$_ $_";
+}
+print "\n";
+
+%K_FOLLOW = ();
+
+$KADV_ON = 1;
+
+sub sum_nick {
+ my $n = shift;
+ my $sum = 0;
+
+ $n =~ s/^(.*)[-_|].*$/$1/;
+
+ foreach (split(//, $n)) {
+ $sum += ord;
+ }
+ return $sum;
+
+}
+
+sub hook_ksircadvfollow {
+ if($_[0] =~ /<~n(\w+)~c>/){
+ my $n = $1;
+ return if $n eq $nick;
+ if(!defined($K_FOLLOW{$n})){
+ $K_FOLLOW{$n} = $K_VAL_COL [ &sum_nick($n) % $#K_VAL_COL];
+ }
+ my $c = $K_FOLLOW{$n};
+ $_[0] =~ s/<~n\w+~c>/<~$c$n~c>/;
+ }
+ elsif ($_[0] =~ /~\* (\w+) \w+/) {
+ my $n = $1;
+ return if $n eq $nick;
+ if(!defined($K_FOLLOW{$n})){
+ $K_FOLLOW{$n} = $K_VAL_COL [ &sum_nick($n) % $#K_VAL_COL];
+ }
+ my $c = $K_FOLLOW{$n};
+ $_[0] =~ s/\* \w+ (\w+)/\* ~$c$n~c $1/;
+ }
+}
+addhook("print", "ksircadvfollow");
+
+$K_ADV_LENGTH = 0;
+
+sub kadv_save {
+ &kadv_load();
+ return if($K_ADV_LENGTH == (scalar %K_FOLLOW));
+
+ open(SAVE, ">$ENV{HOME}/.adv_follow") || return;
+ my $n, $v;
+ while(($n, $v) = each %K_FOLLOW){
+ print SAVE "$n\t$v\n";
+ }
+ close SAVE;
+ $K_ADV_LENGTH = scalar %K_FOLLOW;
+}
+
+sub kadv_load {
+ open(SAVE, "<$ENV{HOME}/.adv_follow") || return;
+ while(<SAVE>){
+ chomp;
+ my ($n, $v) = split(/\t/, $_);
+ if(!defined($K_FOLLOW{$n})){
+ $K_FOLLOW{$n} = $v;
+ }
+
+ }
+ close SAVE;
+}
+
+&kadv_load();
+sub kadv_timer_save {
+ &timer(300, "&kadv_timer_save()", 324325);
+ &kadv_save();
+}
+&timer(300, "&kadv_timer_save()", 324325);
+
+
+sub cmd_afflush {
+ %K_FOLLOW = ();
+}
+
+addhelp("afflush", "Usaage: afflush\nDelete all coloured nicks");
+addcmd("afflush");
+
+
+sub cmd_afnick {
+ &getarg;
+ my $n = $newarg;
+ &getarg;
+ if($newarg){
+ $K_FOLLOW{$n} = $newarg;
+ }
+ else {
+ $K_FOLLOW{$n} = $K_VAL_COL [int (rand scalar (@K_VAL_COL))];
+ }
+}
+
+addhelp("afnick", "Usage: afnick nick <col>\nResets the colour for nick. If col is specefied it is set to col. Random otherwise.");
+addcmd("afnick");
+
+sub cmd_afoff {
+ remhook("print", "ksircadvfollow");
+}
+addhelp("afoff", "Usage: afoff\nTurn off advance follow");
+addcmd("afoff");
+
+sub cmd_afon {
+ addhook("print", "ksircadvfollow");
+}
+addhelp("afon", "Usage: afon\nTurn on advange follow");
+addcmd("afon");
+
+print "*** Advance follow Loaded\n";
+print "*** New commands: /afflush /afnick /afoff /afon\n";
+
diff --git a/ksirc/ahistlineedit.cpp b/ksirc/ahistlineedit.cpp
new file mode 100644
index 00000000..72e6da35
--- /dev/null
+++ b/ksirc/ahistlineedit.cpp
@@ -0,0 +1,437 @@
+/************************************************************************
+
+ AHistLineEdit
+
+ $$Id$$
+
+ An extended KLineEdit with history scroll back and focus controls.
+
+ Function:
+
+ Each time a line is saved, to a max of 256, when the user hits enter.
+ The arrow keys are used to scroll through the history list, rolling
+ around at the end.
+
+ When focus is gained or lost it emits the appropriate signals. This
+ is so the toplevel can track who has focus.
+
+ signals:
+ gotFocus: duh!
+
+ lostFocus: no shit sherlock
+
+ Implementation:
+
+ protected:
+
+ keyPressEvent: Filter key presses looking for up arrow, down
+ arrow or enter. UpArrow saves the current line at the end then
+ scroll. No more processing. DownArrow does the opposite. Enter
+ saves the line, but then passes on the event for normal
+ processing.
+
+ focusInEvent: emits needed signal
+ focusOutEvent: ditto
+
+ Variables:
+ QStrList: current list of history items.
+ current: what I think is the current list item.
+
+*************************************************************************/
+
+
+#include "ahistlineedit.h"
+#include "colorpicker.h"
+#include "ksopts.h"
+#include <qtextedit.h>
+#include <qlayout.h>
+#include <qapplication.h>
+#include <qclipboard.h>
+#include <qpen.h>
+#include <qpainter.h>
+#include <kdebug.h>
+#include <kstdaction.h>
+#include <kstdaccel.h>
+#include <kshortcut.h>
+
+
+aHistLineEdit::aHistLineEdit(QWidget *parent, const char *name)
+ : QTextEdit(parent, name)
+{
+
+ m_drawrect = false;
+ m_height = 0;
+ current = hist.append(QString::null); // Set the current as blank
+ setWrapPolicy(QTextEdit::AtWordOrDocumentBoundary);
+ setVScrollBarMode( AlwaysOff );
+ setHScrollBarMode( AlwaysOff );
+
+// connect( this, SIGNAL( returnPressed () ), this, SLOT( slotReturn() ) );
+ connect( this, SIGNAL( textChanged () ), this, SLOT( slotMaybeResize() ) );
+
+ setTabChangesFocus(true);
+ setTextFormat(PlainText);
+
+ slotMaybeResize(); // setup initial size.
+
+ setLineWidth(2);
+
+}
+
+void aHistLineEdit::setCursorPosition ( int index )
+{
+ QTextEdit::setCursorPosition(0, index);
+}
+
+int aHistLineEdit::cursorPosition() const
+{
+ int par, index;
+ QTextEdit::getCursorPosition(&par, &index);
+ return index;
+}
+
+void aHistLineEdit::slotMaybeResize()
+{
+/*
+ if(QTextEdit::text().contains("\n")){
+ setText(text());
+ setCursorPosition(text().length());
+ }
+*/
+
+ if(text().length() > IRC_SAFE_MAX_LINE){
+ if(m_drawrect == false){
+ m_drawrect = true;
+ repaint();
+ }
+ }
+ else {
+ if(m_drawrect == true){
+ m_drawrect = false;
+ repaint();
+ }
+ }
+
+ QFontMetrics metrics( currentFont() );
+ // int h = metrics.height() * lines();
+ int h = metrics.lineSpacing() * lines()+8;
+ // only grow if we are less than 1/4 the size of the toplevel
+ if(h > (topLevelWidget()->height() >> 2)) {
+ if(this != topLevelWidget()) {
+ h = topLevelWidget()->height() >> 2;
+ setVScrollBarMode( Auto );
+ }
+ }
+ else {
+ setVScrollBarMode( AlwaysOff );
+ }
+ if(h != m_height){
+ m_height = h;
+ QSize s = size();
+ s.setHeight(h);
+ resize(s);
+ setFixedHeight( h );
+ QLayout *l = topLevelWidget()->layout();
+ if(l){
+ l->invalidate();
+ l->activate();
+ }
+ emit resized();
+ }
+}
+
+void aHistLineEdit::keyPressEvent( QKeyEvent *e )
+{
+ bool accept = true;
+ bool ignore = false;
+ bool callKeyPressEvent = false;
+
+// kdDebug(5008) << "Got key: " << e->text() << "/" << e->key() << "-" << e->state() << endl;
+
+ if ( ! (e->key() == Key_Tab || e->key() == Key_Shift || e->state() == ShiftButton || e->key() == 0)) {
+// kdDebug(5008) << "Sending notTab() " << e->text() << endl;
+ emit notTab();
+ }
+
+ // those keycodes correspond to the ones in ksirc.pl in sub hook_fixcolours
+ if ( e->state() == ControlButton ) {
+ QString s = text();
+ int curPos = cursorPosition();
+ switch ( e->key() ) {
+ case Key_K:
+ if ( ksopts->colorPicker )
+ ColourPickerPopUp();
+ else
+ {
+ s.insert( cursorPosition(), 0x03 );
+ setText(s);
+ setCursorPosition(curPos + 1);
+ }
+ break;
+ case Key_I:
+ s.insert( cursorPosition(), "~i");
+ setText(s);
+ setCursorPosition(curPos + 2);
+ break;
+ case Key_Return:
+ case Key_Enter:
+ doEnterKey();
+ callKeyPressEvent = false;
+ break;
+ default:
+ accept = false;
+ ignore = false;
+ callKeyPressEvent = true;
+ }
+ }
+ else if(e->state() == 0){
+ switch(e->key()){
+ case Key_Return:
+ case Key_Enter:
+ doEnterKey();
+ callKeyPressEvent = false;
+ break;
+ case Key_Up:
+ if(ksopts->oneLineEntry){
+ if ((*current) != text()) { // change in hist <-> text() then save text
+ *current = text();
+ }
+ if (current == hist.begin()) { // if at begin of list then wrap around
+ current = hist.fromLast();
+ } else --current; // previous entry
+ setText(*current);
+ setCursorPosition((*current).length());
+ break;
+ }
+ case Key_Down:
+ if(ksopts->oneLineEntry){
+ if ((*current) != text()) { // change in hist <-> text() then save text
+ *current = text();
+ }
+ if (current == hist.fromLast()) { // if at end of list then wrap around
+ current = hist.begin();
+ } else ++current; // next entry
+ setText(*current);
+ setCursorPosition((*current).length());
+ break;
+ }
+ default:
+ accept = false;
+ ignore = false;
+ callKeyPressEvent = true;
+ }
+ }
+ // QLineEdit falsely converts Alt+C to the character 'c', so to work around
+ // this, we just don't pass any Alt+? key on to the keyPressEvent() method.
+ else if (e->state() == AltButton) {
+ switch(e->key()){
+ case Key_Return:
+ case Key_Enter:
+ doEnterKey();
+ callKeyPressEvent = false;
+ break;
+ case Key_Up:
+ if ((*current) != text()) { // change in hist <-> text() then save text
+ *current = text();
+ }
+ if (current == hist.begin()) { // if at begin of list then wrap around
+ current = hist.fromLast();
+ } else --current; // previous entry
+ setText(*current);
+ setCursorPosition((*current).length());
+ break;
+ case Key_Down:
+ if ((*current) != text()) { // change in hist <-> text() then save text
+ *current = text();
+ }
+ if (current == hist.fromLast()) { // if at end of list then wrap around
+ current = hist.begin();
+ } else ++current; // next entry
+ setText(*current);
+ setCursorPosition((*current).length());
+ break;
+ default:
+ accept = false;
+ callKeyPressEvent = true;
+ }
+ }
+ else if (e->state() == ShiftButton){
+ switch(e->key()){
+ case Key_Return:
+ case Key_Enter:
+ doEnterKey();
+ callKeyPressEvent = false;
+ break;
+ default:
+ accept = false;
+ ignore = false;
+ callKeyPressEvent = true;
+ }
+ }
+ else {
+ switch(e->key()){
+ case Key_Return:
+ case Key_Enter:
+ doEnterKey();
+ callKeyPressEvent = false;
+ break;
+ default:
+ accept = false;
+ ignore = false;
+ callKeyPressEvent = true;
+
+ }
+ }
+
+// kdDebug(5008) << "Accept: " << accept << " Ignore: " << ignore << " callKPE: " << callKeyPressEvent << endl;
+ if ( accept ) {
+ e->accept();
+ }
+ else {
+ if ( ignore ) {
+ e->ignore();
+ }
+ else {
+ if ( callKeyPressEvent ) {
+ QTextEdit::keyPressEvent(e);
+ }
+ }
+ }
+}
+
+bool aHistLineEdit::processKeyEvent( QKeyEvent *e )
+{
+ /*
+ * Only put key sequences in here you
+ * want us to ignore and to pass upto
+ * parent widgets for handling
+ */
+
+ bool eat = false;
+
+// kdDebug(5008) << "Key: " << KShortcut( KKey( e ) ).toString() << " StdAccel: " << KStdAccel::paste().toString() << endl;
+
+ if ( KStdAccel::paste().contains(KKey( e ))) {
+ e->ignore();
+ eat = true;
+ }
+
+ return eat;
+}
+
+void aHistLineEdit::ColourPickerPopUp()
+{
+ ColorPicker picker( this );
+ if ( picker.exec() == QDialog::Accepted )
+ {
+ QString s = text();
+ // int curPos = cursorPosition();
+ int curPos, p;
+ getCursorPosition(&p, &curPos);
+ QString colString = picker.colorString();
+ colString.prepend( 0x03 );
+ s.insert( curPos, colString );
+ setText( s );
+ setCursorPosition( curPos + colString.length() );
+ }
+}
+
+void aHistLineEdit::focusInEvent(QFocusEvent *e)
+{
+ QTextEdit::focusInEvent(e);
+ emit gotFocus();
+}
+
+void aHistLineEdit::focusOutEvent(QFocusEvent *e)
+{
+ QTextEdit::focusOutEvent(e);
+ emit lostFocus();
+}
+#if 0
+void aHistLineEdit::mousePressEvent ( QMouseEvent *e )
+{
+ if(e->button() == MidButton){
+ /*
+ * emit pasteText(QApplication::clipboard()->text(QClipboard::Selection));
+ */
+ }
+ else{
+ QTextEdit::mousePressEvent(e);
+ }
+}
+#endif
+bool aHistLineEdit::eventFilter( QObject *o, QEvent *e )
+{
+ if ( o == this && e->type() == QEvent::AccelOverride )
+ if(processKeyEvent( static_cast<QKeyEvent*>( e ) ) == true)
+ return true;
+
+ return QTextEdit::eventFilter( o, e );
+}
+
+QString aHistLineEdit::text() const
+{
+ QString s = QTextEdit::text();
+ s.remove("\n");
+ return s;
+}
+
+// Called upon MMB on the lineedit
+void aHistLineEdit::paste()
+{
+ /* we let the top level take it */
+ if(ksopts->oneLineEntry) {
+ emit pasteText(QApplication::clipboard()->text(QClipboard::Selection));
+ }
+ else {
+ QString paste = QApplication::clipboard()->text(QClipboard::Selection);
+ paste.replace("\n", " ~ ");
+ insert(paste);
+ }
+}
+
+void aHistLineEdit::paintEvent ( QPaintEvent *p )
+{
+ QTextEdit::paintEvent(p);
+
+ if(m_drawrect == true){
+ QPainter paint(this);
+ QPen pen = paint.pen();
+ pen.setWidth(5);
+ pen.setStyle(Qt::SolidLine);
+ pen.setColor(palette().active().highlight());
+ paint.setPen(pen);
+ QRect r = frameRect();
+ paint.drawRect(r);
+ }
+
+}
+
+void aHistLineEdit::doEnterKey()
+{
+ // strategy: always append an empty line to the end
+ if ((*current).isEmpty()) { // current is empty
+ if (!text().isEmpty()) {
+ // text() has something -> store it in current and add a new empty one
+ *current = text();
+ hist.append(QString::null); // always add empty line at end
+ if (hist.count() >= 256) { // if appended check if it will go beyond the max number of entries
+ hist.remove(hist.begin()); // if so then delete the first entry
+ }
+ }
+ } else { // current has content
+ if (!text().isEmpty()) {
+ // both !isEmpty() and != -> append at end
+ current = hist.fromLast(); // goto last entry which is empty
+ *current = text(); // change content to text()
+ hist.append(QString::null); // always add empty line at end
+ if (hist.count() >= 256) { // if appended check if it will go beyond the max number of entries
+ hist.remove(hist.begin()); // if so then delete the first entry
+ }
+ }
+ }
+ current = hist.fromLast(); // set current history item back to the last item in the list
+ emit gotReturnPressed();
+}
+
+#include "ahistlineedit.moc"
diff --git a/ksirc/ahistlineedit.h b/ksirc/ahistlineedit.h
new file mode 100644
index 00000000..a273791f
--- /dev/null
+++ b/ksirc/ahistlineedit.h
@@ -0,0 +1,56 @@
+#ifndef AHISTLINEEDIT_H
+#define AHISTLINEEDIT_H
+
+#include <qtextedit.h>
+#include <qstringlist.h>
+#include <qsize.h>
+
+class aHistLineEdit : public QTextEdit
+{
+Q_OBJECT
+public:
+ aHistLineEdit(QWidget *parent = 0, const char *name = 0);
+
+ int cursorPosition () const;
+ void setCursorPosition(int);
+
+ QString text() const;
+
+signals:
+ // Don't use returnPressed from the textEdit
+ void gotReturnPressed();
+ void gotFocus();
+ void lostFocus();
+ void pasteText(const QString&);
+ void notTab();
+ void resized();
+
+public slots:
+ virtual void paste();
+ virtual void slotMaybeResize();
+
+protected:
+ bool processKeyEvent( QKeyEvent * );
+ virtual void keyPressEvent ( QKeyEvent * );
+ virtual void focusInEvent ( QFocusEvent * );
+ virtual void focusOutEvent ( QFocusEvent * );
+// virtual void mousePressEvent ( QMouseEvent * );
+
+ virtual bool eventFilter( QObject *o, QEvent *e );
+
+ virtual void paintEvent ( QPaintEvent * );
+
+private:
+
+ void doEnterKey();
+
+ QStringList hist;
+ QStringList::Iterator current;
+ void ColourPickerPopUp();
+ int m_height;
+
+ bool m_drawrect;
+
+};
+
+#endif
diff --git a/ksirc/alistbox.cpp b/ksirc/alistbox.cpp
new file mode 100644
index 00000000..9b632676
--- /dev/null
+++ b/ksirc/alistbox.cpp
@@ -0,0 +1,508 @@
+/*******************************************************************
+
+ aListBox
+
+ $$Id$$
+
+ List box that outputs a right click mouse so I can popup a qpopupmenu.
+
+ Does special sorting, and maintains a two part list, one for ops,
+ other for users.
+
+ nothing special.
+
+*******************************************************************/
+
+#include "alistbox.h"
+#include "nickColourMaker.h"
+
+#include <qdragobject.h>
+#include <qpainter.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kurldrag.h>
+
+aListBox::aListBox(QWidget *parent, const char *name )
+ : QListBox(parent,name)
+{
+ clear();
+ p_scroll = palette().copy();
+ setAcceptDrops( true );
+ connect(this, SIGNAL(selected (const QString&)),
+ this, SIGNAL(selectedNick(const QString&)));
+
+ m_nickListDirty = true;
+
+ updateNickPrefixWidth();
+
+ connect( this, SIGNAL( contextMenuRequested( QListBoxItem *, const QPoint & ) ),
+ this, SLOT( reEmitContextMenuRequest( QListBoxItem * ) ) );
+}
+
+aListBox::~aListBox()
+{
+}
+
+void aListBox::reEmitContextMenuRequest( QListBoxItem *item )
+{
+ emit contextMenuRequested( index( item ) );
+}
+
+void aListBox::clear()
+{
+ QListBox::clear();
+}
+
+
+void aListBox::inSort ( nickListItem *lbi)
+{
+ int insert;
+ bool found;
+ insert = searchFor(lbi->text(), found, lbi->op());
+ if(found == TRUE){
+ //kdDebug(5008) << lbi->text() << " is already in nick list!" << endl;
+ return;
+ }
+ insertItem(lbi, insert);
+// for(uint index = 0; index < count(); index++){
+// debug("%d is %s", index, text(index));
+// }
+
+ m_nickListDirty = true;
+}
+
+void aListBox::inSort ( QString text, bool top)
+{
+ nickListItem *nli = new nickListItem();
+ nli->setText(text);
+ if(top == TRUE)
+ nli->setOp(TRUE);
+ inSort(nli);
+}
+
+int aListBox::findSep()
+{
+ uint i = 0;
+ for(; i < count(); i++)
+ if(item(i)->op() == FALSE)
+ break; // stop now
+
+ return i;
+
+}
+int aListBox::searchFor(const QString &nick, bool &found, bool top)
+{
+ int min = 0, max = 0;
+ int current = 0, compare = 0;
+ int real_max = 0;
+ int insert;
+
+ found = FALSE;
+
+ // If there's nothing in the list, don't try and search it, etc
+
+ if(count() == 0){
+ insert = 0;
+ }
+ else{
+ int sep = findSep();
+ if(sep >= 0){
+ if(top == TRUE){
+ min = 0;
+ max = (sep >= 1) ? sep - 1 : 0;
+ }
+ else{
+ min = sep;
+ max = count() - 1;
+ }
+ }
+ else
+ current = -1;
+
+ real_max = max;
+ current = (min + max)/2; // + (max-min)%2;
+ insert = current;
+ int last_current = -1;
+ uint loop = 0; // Most loops should be log_2 count(), but...
+ do {
+ if(current == last_current){
+// debug("Insert looping on %s", nick.data());
+ // current++;
+ break; // we're looping, so stop
+ }
+ if(current >= max)
+ break; // Don't go too far
+ last_current = current;
+
+ compare = text(current).lower().compare(nick.lower());
+ if(compare < 0){
+ min = current;
+ insert = current + 1;
+// debug("1 < 0: %s is greater then: %s, min: %d max: %d current: %d", nick.data(), text(current), min, max, current);
+ }
+ else if(compare > 0){
+ max = current;
+ insert = current;
+// debug("1 > 0: %s is less then: %s, min: %d max: %d current: %d", nick.data(), text(current), min, max, current);
+ }
+ else {// We got a match?
+ insert = current;
+ min = current;
+ found = TRUE;
+ break;
+ }
+ current = (min + max)/2;
+ loop++; // Infinite loop detector increment
+ } while(max != min && loop < count());
+
+ if(current >= real_max - 1){
+ compare = text(real_max).lower().compare(nick.lower());
+ if(compare < 0){
+ min = current;
+ insert = real_max + 1;
+// debug("End check got one!");
+ }
+ else if (compare == 0){// We got a match
+ insert = real_max + 1;
+ min = real_max;
+ found = TRUE;
+ }
+ }
+
+ // Sanity check
+ if((top == TRUE && insert > sep) ||
+ (top == FALSE && insert < sep)){
+ insert = sep;
+ }
+
+ if(loop == count())
+ {
+// debug("Loop inifitly on: %s", nick.data());
+ }
+
+ if(found == TRUE){
+// debug("Found %s", nick.data());
+ return min; // We found one, so return the number found
+ }
+ }
+// debug("%s is at %d", nick.data(), insert);
+ return insert;
+
+}
+bool aListBox::isTop(int index)
+{
+ if(index >= findSep())
+ return FALSE;
+ else
+ return TRUE;
+}
+
+int aListBox::findNick(const QString &str)
+{
+ bool found;
+ int index;
+ index = searchFor(str, found, TRUE);
+ if(found == TRUE)
+ return index;
+ index = searchFor(str, found, FALSE);
+ if(found == TRUE)
+ return index;
+// debug("Did not find: %s", str.data());
+ return -1;
+}
+
+nickListItem *aListBox::item(int index){
+ return (nickListItem *) QListBox::item(index);
+}
+
+void aListBox::dragMoveEvent( QDragMoveEvent *e )
+{
+ bool ok = (count() > 0 && KURLDrag::canDecode( e ));
+
+ if(!ok)
+ ok = QTextDrag::canDecode(e);
+
+ e->accept( ok );
+ if ( ok )
+ setCurrentItem( itemAt( e->pos() ));
+}
+
+
+void aListBox::dropEvent( QDropEvent *e )
+{
+ QListBoxItem *item = itemAt( e->pos() );
+ if ( !item )
+ return;
+
+ setCurrentItem( item );
+
+ QStringList urls;
+ KURLDrag::decodeLocalFiles( e, urls );
+
+ QString text;
+
+ if ( !urls.isEmpty() )
+ emit urlsDropped( urls, item->text() );
+ else if(QTextDrag::decode(e, text)){
+ emit textDropped( item, text);
+ }
+}
+
+bool aListBox::needNickPrefix() const
+{
+ if ( m_nickListDirty ) {
+ updateNeedNickPrefixFlag();
+ const_cast<aListBox *>( this )->updateNickPrefixWidth();
+ }
+
+ return m_needNickPrefix;
+}
+
+void aListBox::updateNeedNickPrefixFlag() const
+{
+ m_needNickPrefix = false;
+
+ if(ksopts->useColourNickList == false){
+ QListBoxItem *item = firstItem();
+ for (; item; item = item->next() )
+ {
+ nickListItem *nickItem = static_cast<nickListItem *>( item );
+ if ( nickItem->op() ||
+ nickItem->voice() ||
+ nickItem->away() ||
+ nickItem->ircOp() )
+ {
+ m_needNickPrefix = true;
+ break;
+ }
+ }
+ }
+
+ m_nickListDirty = false;
+}
+
+void aListBox::fontChange( const QFont &f )
+{
+ QListBox::fontChange( f );
+ updateNickPrefixWidth();
+}
+
+void aListBox::updateNickPrefixWidth()
+{
+ QFontMetrics metrics( font() );
+
+ m_nickPrefixWidth = 0;
+
+ if(ksopts->useColourNickList == false){
+ nickListItem *item = static_cast<nickListItem *>( firstItem() );
+ for ( ; item; item = static_cast<nickListItem *>( item->next() ) )
+ m_nickPrefixWidth = kMax( m_nickPrefixWidth, metrics.width( item->nickPrefix() ) );
+
+ // padding
+ }
+ m_nickPrefixWidth += metrics.width( " " );
+}
+
+void aListBox::clearAdvOps()
+{
+ nickListItem *item = static_cast<nickListItem *>( firstItem() );
+ for ( ; item; item = static_cast<nickListItem *>( item->next() ) ){
+ if(item->away() || item->ircOp()){
+ item->setIrcOp(false);
+ item->setAway(false);
+ updateItem(item);
+ }
+ }
+ triggerUpdate(false);
+ m_nickListDirty = true;
+
+}
+
+nickListItem::nickListItem()
+ : QListBoxItem()
+{
+ is_op = FALSE;
+ is_voice = FALSE;
+ is_away = FALSE;
+ is_ircop = FALSE;
+ forcedCol = 0x0;
+}
+
+nickListItem::nickListItem(const nickListItem &old)
+ : QListBoxItem()
+{
+ is_op = old.is_op;
+ is_voice = old.is_voice;
+ is_away = old.is_away;
+ is_ircop = old.is_ircop;
+ string = old.string;
+ forcedCol = old.forcedCol;
+}
+
+nickListItem::~nickListItem()
+{
+ string.truncate(0);
+}
+
+void nickListItem::setOp(bool _op)
+{
+ is_op = _op;
+ if ( listBox() )
+ static_cast<aListBox *>( listBox() )->setNickListDirty();
+}
+
+void nickListItem::setVoice(bool _voice)
+{
+ is_voice = _voice;
+ if ( listBox() )
+ static_cast<aListBox *>( listBox() )->setNickListDirty();
+}
+void nickListItem::setAway(bool _away)
+{
+ is_away = _away;
+ if ( listBox() )
+ static_cast<aListBox *>( listBox() )->setNickListDirty();
+}
+
+void nickListItem::setIrcOp(bool _ircop)
+{
+ is_ircop = _ircop;
+ if ( listBox() )
+ static_cast<aListBox *>( listBox() )->setNickListDirty();
+}
+
+void nickListItem::forceColour(const QColor *col)
+{
+ forcedCol = col;
+}
+
+void nickListItem::paint(QPainter *p)
+{
+ QFontMetrics fm = p->fontMetrics();
+ int yPos; // vertical text position
+
+ int nickPosX = 3;
+ yPos = fm.ascent() + fm.leading()/2;
+ QPen pen = p->pen();
+ QFont font = p->font();
+ if(ksopts->useColourNickList == true) {
+ if(ksopts->nickColourization){
+
+ if(!isSelected()) {
+ if(forcedCol && forcedCol->isValid())
+ p->setPen(*forcedCol);
+ else
+ p->setPen(nickColourMaker::colourMaker()->findFg(text()));
+ }
+ else {
+ p->setPen(ksopts->selForegroundColor);
+ }
+
+ if(is_voice == TRUE){
+ QPen open = p->pen();
+ p->setPen(ksopts->channelColor);
+ p->drawText( nickPosX, yPos, "+" );
+ nickPosX += fm.width( "+" );
+ p->setPen(open);
+// p->fillRect(0,0, listBox()->maxItemWidth(), height(listBox()), QBrush(ksopts->channelColor, Qt::Dense7Pattern));
+ }
+ if(is_op == TRUE) {
+ QPen open = p->pen();
+ p->setPen(ksopts->errorColor);
+ p->drawText( nickPosX, yPos, "@" );
+ nickPosX += fm.width( "@" );
+ p->setPen(open);
+// p->fillRect(0,0, listBox()->maxItemWidth(), height(listBox()), QBrush(ksopts->errorColor, Qt::Dense7Pattern));
+ }
+ if(is_away == TRUE)
+ p->setPen(p->pen().color().dark(150));
+ if(is_ircop == TRUE){
+ QPen open = p->pen();
+ p->setPen(ksopts->errorColor);
+ p->drawText( nickPosX, yPos, "*" );
+ nickPosX += fm.width( "*" );
+ p->setPen(open);
+
+ }
+
+ }
+ else {
+ if(is_voice == TRUE)
+ p->setPen(ksopts->channelColor);
+ if(is_op == TRUE)
+ p->setPen(ksopts->errorColor);
+ if(is_away == TRUE)
+ p->setPen(p->pen().color().dark(150));
+ if(is_ircop == TRUE){
+ QFont bfont = font;
+ bfont.setBold(TRUE);
+ p->setFont(bfont);
+ }
+ }
+ }
+ else {
+ }
+
+ if(ksopts->useColourNickList == false) {
+ aListBox *lb = static_cast<aListBox *>( listBox() );
+
+ if ( lb->needNickPrefix() )
+ {
+ p->drawText( 3, yPos, nickPrefix() );
+
+ nickPosX += lb->nickPrefixWidth();
+ }
+ }
+ p->drawText( nickPosX, yPos, text() );
+
+ p->setPen(pen);
+ p->setFont(font);
+}
+
+QString nickListItem::nickPrefix() const
+{
+ QString prefix;
+
+ if ( voice() )
+ prefix += aListBox::nickPrefixVoice();
+ if ( op() )
+ prefix += aListBox::nickPrefixOp();
+ if ( away() )
+ prefix += aListBox::nickPrefixAway();
+ if ( ircOp() )
+ prefix += aListBox::nickPrefixIrcOp();
+
+ if ( !prefix.isEmpty() )
+ prefix.prepend( "+" );
+
+ return prefix;
+}
+
+int nickListItem::height(const QListBox *lb ) const
+{
+ return lb->fontMetrics().lineSpacing() + 1;
+}
+
+int nickListItem::width(const QListBox *lb ) const
+{
+ return
+ static_cast<const aListBox *>( lb )->nickPrefixWidth() +
+ lb->fontMetrics().width( text() ) + 6;
+}
+
+const QPixmap* nickListItem::pixmap() const
+{
+ return 0l;
+}
+
+nickListItem &nickListItem::operator= (const nickListItem &nli)
+{
+ string = nli.string;
+ is_op = nli.is_op;
+ is_voice = nli.is_voice;
+ is_away = nli.is_away;
+ is_ircop = nli.is_ircop;
+ return (*this);
+}
+#include "alistbox.moc"
diff --git a/ksirc/alistbox.h b/ksirc/alistbox.h
new file mode 100644
index 00000000..0f818bba
--- /dev/null
+++ b/ksirc/alistbox.h
@@ -0,0 +1,112 @@
+#ifndef ALISTBOX_H
+#define ALISTBOX_H
+
+#include <qlistbox.h>
+#include "ksopts.h"
+
+class nickListItem : public QListBoxItem
+{
+ public:
+ nickListItem();
+ nickListItem(const nickListItem &old);
+ virtual ~nickListItem();
+
+ virtual int height ( const QListBox * ) const;
+ virtual int width ( const QListBox * ) const;
+ QString text () const { return string; }
+ const QPixmap* pixmap () const;
+
+ bool op() const { return is_op; }
+ bool voice() const { return is_voice; }
+ bool away() const { return is_away; }
+ bool ircOp() const { return is_ircop; }
+
+ void setOp(bool _op = FALSE);
+ void setVoice(bool _voice = FALSE);
+ void setAway(bool _away = FALSE);
+ void setIrcOp(bool _ircop = FALSE);
+
+ void setText(const QString &str) { string = str; }
+
+ QString nickPrefix() const;
+
+ void forceColour(const QColor *col);
+
+ nickListItem &operator= ( const nickListItem & nli );
+
+protected:
+ virtual void paint ( QPainter * );
+
+ private:
+ bool is_op:1;
+ bool is_voice:1;
+ bool is_away:1;
+ bool is_ircop:1;
+
+ QString string;
+ const QColor *forcedCol;
+};
+
+class aListBox : public QListBox
+{
+ Q_OBJECT
+
+public:
+ aListBox(QWidget *parent = 0, const char *name = 0);
+
+ virtual ~aListBox();
+
+ void clear();
+
+ void inSort ( nickListItem *);
+ void inSort ( QString text, bool top=FALSE);
+
+ nickListItem *item(int index);
+
+ bool isTop(int index);
+
+ int findNick(const QString &str);
+
+ void clearAdvOps();
+
+ bool needNickPrefix() const;
+
+ void setNickListDirty()
+ { m_nickListDirty = true; }
+
+ unsigned short int nickPrefixWidth() const
+ { return m_nickPrefixWidth; }
+
+ virtual void fontChange( const QFont &f );
+
+ static QString nickPrefixOp() { return QString::fromLatin1( "o" ); }
+ static QString nickPrefixVoice() { return QString::fromLatin1( "v" ); }
+ static QString nickPrefixAway() { return QString::fromLatin1( "a" ); }
+ static QString nickPrefixIrcOp() { return QString::fromLatin1( "O" ); }
+
+signals:
+ void contextMenuRequested(int index);
+ void selectedNick(const QString&);
+ void urlsDropped( const QStringList& urls, const QString& nick );
+ void textDropped( const QListBoxItem *item, const QString& text );
+
+protected:
+ virtual int findSep();
+ virtual int searchFor(const QString &nick, bool &found, bool top);
+ virtual void dragMoveEvent( QDragMoveEvent * );
+ virtual void dropEvent( QDropEvent * );
+
+private slots:
+ void reEmitContextMenuRequest( QListBoxItem *item );
+
+private:
+ void updateNeedNickPrefixFlag() const;
+ void updateNickPrefixWidth();
+
+ QPalette p_scroll;
+ mutable bool m_nickListDirty;
+ mutable bool m_needNickPrefix;
+ int m_nickPrefixWidth;
+};
+
+#endif
diff --git a/ksirc/autodcc.pl b/ksirc/autodcc.pl
new file mode 100644
index 00000000..f075cbcd
--- /dev/null
+++ b/ksirc/autodcc.pl
@@ -0,0 +1,71 @@
+#
+#
+&print("*** Loading auto dcc...");
+
+%A_DCC = ();
+
+sub cmd_anick {
+ &getarg;
+ $A_DCC{$newarg} = 1;
+ &print("*\cbI\cb* Added $newarg to auto dcc");
+}
+
+&addcmd("anick");
+
+sub cmd_dnick {
+ &getarg;
+ delete $A_DCC{$newarg};
+ &print("*\cbI\cb* Removed $newarg from auto dcc");
+}
+
+&addcmd("dnick");
+
+sub cmd_lnick {
+ my $key;
+ my $str;
+ foreach $key (keys %A_DCC){
+ $str .= "$key ";
+ }
+ &print("*\cbI\cb* Nicks allowed to auto dcc: $str");
+}
+
+&addcmd("lnick");
+
+sub set_autoresume {
+ $set{'AUTORESUME'}="on", $a_autoresume=1 if $_[0] =~ /^on$/i;
+ $set{'AUTORESUME'}="off", $a_autoresume=0 if $_[0] =~ /^off$/i;
+}
+&addset("autoresume");
+$set{"AUTORESUME"}="on";
+$n_autoresume=1;
+
+my %A_AUTOSTART = ();
+
+sub hook_adcc_do {
+ my $cmd = shift;
+
+ if($cmd eq "SEND"){
+ if($A_DCC{$who} == 1){
+ my $host = shift;
+ my $dport = shift;
+ my $file = shift;
+ my $size = shift;
+ if(-e $file){
+ if(($n_autoresume == 1) && ((-s $file) < $size)){
+ &docommand("resume $who $file");
+ $A_AUTOSTART{$dport} = "dcc get $who $file";
+ }
+ else {
+ &tell("*E* Autodcc rejected. File exists and no auto resume, or size mismatch");
+ }
+ }
+ else {
+ &docommand("dcc get $who $file");
+ }
+ }
+ }
+}
+
+&addhook("dcc_request", "adcc_do");
+
+
diff --git a/ksirc/baserules.cpp b/ksirc/baserules.cpp
new file mode 100644
index 00000000..11e5850c
--- /dev/null
+++ b/ksirc/baserules.cpp
@@ -0,0 +1,167 @@
+
+#include "baserules.h"
+#include "ksopts.h"
+#include "ksircprocess.h"
+
+#include <qregexp.h>
+
+#include <stdio.h>
+
+void KSMBaseRules::sirc_receive(QCString, bool)
+{
+}
+
+void KSMBaseRules::control_message(int, QString)
+{
+}
+
+filterRuleList *KSMBaseRules::defaultRules()
+{
+ filterRule *fr;
+ filterRuleList *frl = new filterRuleList();
+ frl->setAutoDelete(TRUE);
+ if( ksopts->ksircColors ){
+ // 3 stage rule to filter:
+ // ~blah~<something> stuff with ~ here and ~ there
+ // Here's the path follows
+ // =>~blah~;;;<something> with ~ here and ~ there
+ // =>~blah~;;;<something> with ~~ here and ~~ there
+ // =>~blah~<something> with ~~ here and ~ there
+ // Basic problem was getting it not to change ~blah~ into ~~blah~~
+ fr = new filterRule();
+ fr->desc = "Add marker to second ~";
+ fr->search = "^~\\S+~";
+ fr->from = "^~(\\S+)~";
+ fr->to = "~;;;$1~;;;";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Escape kSirc Control Codes";
+ fr->search = "~";
+ fr->from = "(?g)~(?!;;;)";
+ fr->to = "$1~~";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Remove marker to second";
+ fr->search = "^~;;;\\S+~;;;";
+ fr->from = "^~;;;(\\S+)~;;;";
+ fr->to = "~$1~";
+ frl->append(fr);
+
+ }
+ else{
+ // If we don't escape all ~'s at least esacpe the ones in
+ // email/part joins etc.
+ fr = new filterRule();
+ fr->desc = "Search for dump ~'s";
+ fr->search = "\\W~\\S+@\\S+\\W";
+ fr->from = "~(\\S+@)";
+ fr->to = "~~$1";
+ frl->append(fr);
+ }
+ if(ksopts->mircColors){
+ fr = new filterRule();
+ fr->desc = "Remove mirc Colours";
+ fr->search = "\\x03";
+ fr->from = "(?g)\\x03(?:\\d{0,2},{0,1}\\d{0,2})";
+ fr->to = "";
+ frl->append(fr);
+ }
+ if( ksopts->nickForeground.isValid() || ksopts->nickColourization){
+ fr = new filterRule();
+ fr->desc = "Highlight nicks in colour";
+ fr->search = "(?:~\\S+~)[<>|\\[\\]\\-]\\S+[<>|\\[\\]\\-]";
+ fr->from = "^((?:~\\S+~))([<>|\\[\\]\\-])(\\S+)([<>|\\[\\]\\-])";
+ fr->to = "$1$2~n$3~n$4";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Highlight nicks in colour in CTCP ACTION";
+ fr->search = "(?:~\\S+~)\\* (\\S+)";
+ fr->from = "^((?:~\\S+~))\\* (\\S+)";
+ fr->to = "$1* ~n$2~n";
+ frl->append(fr);
+ }
+ if( ksopts->msgContainNick.isValid() ){
+ //QString nick = ksopts->nick;
+ QString nick = ksircProcess()->getNick();
+ if ( !nick.isEmpty() ) {
+ if(nick.length() > 83){
+ qDebug("Nick too long");
+ nick.truncate( 83 );
+ }
+ /*
+ * Since the nick is used in the regex we must
+ * escape all regex words
+ */
+ nick = QRegExp::escape(nick);
+
+ sprintf(match_us,
+ "(?i)<\\S+>.*\\s%s(,.*|\02:.*|:.*|\\s.*|$)", nick.latin1());
+ sprintf(to_us,
+ "$1~o");//, ksopts->ownNickColor);
+ fr = new filterRule();
+ fr->desc = "Highlight our nick";
+ fr->search = match_us;
+ fr->from = "(<\\S+>)";
+ fr->to = to_us;
+ frl->append(fr);
+ }
+ }
+ if( ksopts->msg1Contain.isValid() ){
+ //QString nick = ksopts->nick;
+ /*
+ * Since the nick is used in the regex we must
+ * escape all regex words
+ */
+ QString msg = ksopts->msg1String;
+
+ if(msg.length() > 0){
+ if(ksopts->msg1Regex == false)
+ msg = QRegExp::escape(msg);
+
+ snprintf(msg1_match, 100,
+ "(?i)<\\S+>.*\\s%s(,.*|\02:.*|:.*|\\s.*|$)", msg.latin1());
+ snprintf(msg1_to, 100,
+ "$1~%s", ksopts->msg1Contain.name().latin1());//, ksopts->ownNickColor);
+ fr = new filterRule();
+ fr->desc = "Highlight our msg1";
+ fr->search = msg1_match;
+ fr->from = "(<\\S+>)";
+ fr->to = msg1_to;
+ frl->append(fr);
+ }
+ }
+ if( ksopts->msg2Contain.isValid() ){
+ //QString nick = ksopts->nick;
+ /*
+ * Since the nick is used in the regex we must
+ * escape all regex words
+ */
+ QString msg = ksopts->msg2String;
+
+ if(msg.length() > 0){
+ if(ksopts->msg2Regex == false)
+ msg = QRegExp::escape(msg);
+
+ snprintf(msg2_match, 100,
+ "(?i)<\\S+>.*\\s%s(,.*|\02:.*|:.*|\\s.*|$)", msg.latin1());
+ snprintf(msg2_to, 100,
+ "$1~%s", ksopts->msg2Contain.name().latin1());//, ksopts->ownNickColor);
+ fr = new filterRule();
+ fr->desc = "Highlight our msg2";
+ fr->search = msg2_match;
+ fr->from = "(<\\S+>)";
+ fr->to = msg2_to;
+ frl->append(fr);
+ }
+ }
+
+ // Default rules alays in place
+ fr = new filterRule();
+ fr->desc = "Remove Just bold in parts and joins";
+ fr->search = "\\*\\x02\\S+\\x02\\*";
+ fr->from = "\\*\\x02(\\S+)\\x02\\*";
+ fr->to = "\\*$1\\*";
+ frl->append(fr);
+
+ return frl;
+}
diff --git a/ksirc/baserules.h b/ksirc/baserules.h
new file mode 100644
index 00000000..690ed28e
--- /dev/null
+++ b/ksirc/baserules.h
@@ -0,0 +1,35 @@
+#ifndef KBASERULES_H
+#define KBASERULES_H
+
+#include "messageReceiver.h"
+
+class KSMBaseRules : public KSircMessageReceiver
+{
+public:
+ KSMBaseRules(KSircProcess *proc)
+ : KSircMessageReceiver(proc)
+ {
+ broadcast = FALSE;
+ }
+ virtual ~KSMBaseRules()
+ {
+ }
+
+ virtual void sirc_receive(QCString str, bool broadcast);
+
+ virtual void control_message(int, QString);
+
+ filterRuleList *defaultRules();
+
+private:
+ bool broadcast;
+ char to[101];
+ char match_us[101];
+ char to_us[101];
+ char msg1_match[101];
+ char msg1_to[101];
+ char msg2_match[101];
+ char msg2_to[101];
+};
+
+#endif
diff --git a/ksirc/boundscheckingarray.h b/ksirc/boundscheckingarray.h
new file mode 100644
index 00000000..0146efde
--- /dev/null
+++ b/ksirc/boundscheckingarray.h
@@ -0,0 +1,67 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+#ifndef __boundscheckingarray__
+#define __boundscheckingarray__
+
+#include <assert.h>
+#include <qvaluevector.h>
+
+/* I wish QValueVector would check bounds upon extraction.. */
+
+template <typename T, const unsigned int size>
+class BoundsCheckingArray
+{
+public:
+ BoundsCheckingArray() {}
+ BoundsCheckingArray( const BoundsCheckingArray<T, size> &rhs )
+ { (*this) = rhs; }
+ BoundsCheckingArray &operator=( const BoundsCheckingArray<T, size> &rhs )
+ {
+ unsigned int i = 0;
+ for (; i < size; ++i )
+ m_data[ i ] = rhs.m_data[ i ];
+ return *this;
+ }
+
+ T &operator[]( unsigned int index )
+ {
+ assert( index < size );
+ return m_data[ index ];
+ }
+
+ const T &operator[]( unsigned int index ) const
+ {
+ assert( index < size );
+ return m_data[ index ];
+ }
+
+ QValueVector<T> toValueVector() const
+ {
+ QValueVector<T> vector( size );
+ for ( unsigned int i = 0; i < size; ++i )
+ vector[ i ] = m_data[ i ];
+ return vector;
+ }
+
+private:
+ T m_data[ size ];
+};
+
+#endif
+
diff --git a/ksirc/chanButtons.cpp b/ksirc/chanButtons.cpp
new file mode 100644
index 00000000..f40c7176
--- /dev/null
+++ b/ksirc/chanButtons.cpp
@@ -0,0 +1,268 @@
+/* mode buttons for ksirc - Robbie Ward <linuxphreak@gmx.co.uk>*/
+
+#include "chanButtons.h"
+#include "chanButtons.moc"
+
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <knuminput.h>
+#include <kstdguiitem.h>
+#include <kpushbutton.h>
+
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qlineedit.h>
+
+#include "ksircprocess.h"
+
+static void makeSquare(QWidget *w)
+{
+ QSize sh = w->sizeHint();
+ int s = sh.height();
+ w->setFixedSize(s,s);
+}
+
+chanButtons::chanButtons(KSircProcess *proc, QWidget *parent, const char *name)
+: QWidget(parent, name),
+ m_proc(proc)
+{
+ Popupmenu = new KPopupMenu( this );
+ Popupmenu->insertTitle(i18n("Channel Modes"));
+ toggleMenu[0] = Popupmenu->insertItem(i18n("i (invite-only)"), this, SLOT(invite()));
+ toggleMenu[1] = Popupmenu->insertItem(i18n("l (limited users)"), this, SLOT(limited()));
+ toggleMenu[2] = Popupmenu->insertItem(i18n("k (key to join)"), this, SLOT(key()));
+ toggleMenu[3] = Popupmenu->insertItem(i18n("s (secret)"), this, SLOT(secret()));
+ Popupmenu->insertSeparator();
+ Popupmenu->insertTitle(i18n("User Modes"));
+ toggleMenu[4] = Popupmenu->insertItem(i18n("i (be invisible)"), this, SLOT(invisible()));
+ toggleMenu[5] = Popupmenu->insertItem(i18n("w (receive wallops)"), this, SLOT(wallops()));
+ toggleMenu[6] = Popupmenu->insertItem(i18n("s (get server notices)"), this, SLOT(serverNotices()));
+
+ layout = new QHBoxLayout(this);
+ layout->setSpacing(0);
+ layout->setMargin(0);
+
+ protectButton = new QPushButton(i18n("T"), this);
+ protectButton->setToggleButton(true);
+ makeSquare(protectButton);
+ QToolTip::add(protectButton, i18n("Only op'ed users can change the topic"));
+ connect(protectButton, SIGNAL(clicked()), this, SLOT(protectMode()));
+ layout->addWidget(protectButton);
+
+ outsideButton = new QPushButton(i18n("N"), this);
+ outsideButton->setToggleButton(true);
+ makeSquare(outsideButton);
+ QToolTip::add(outsideButton, i18n("No outside messages"));
+ connect(outsideButton, SIGNAL(clicked()), this, SLOT(outsideMode()));
+ layout->addWidget(outsideButton);
+
+ moderateButton = new QPushButton(i18n("M"), this);
+ moderateButton->setToggleButton(true);
+ makeSquare(moderateButton);
+ QToolTip::add(moderateButton, i18n("Only op'ed users and voiced users (+v) can speak"));
+ connect(moderateButton, SIGNAL(clicked()), this, SLOT(moderateMode()));
+ layout->addWidget(moderateButton);
+
+ menuButton = new QPushButton(i18n("..."), this);
+ makeSquare(menuButton);
+ menuButton->setFixedWidth(menuButton->width()*2);
+ menuButton->setPopup(Popupmenu);
+ QToolTip::add(menuButton, i18n("More mode commands"));
+ layout->addWidget(menuButton);
+}
+
+void chanButtons::invite()
+{
+ if (Popupmenu->isItemChecked(toggleMenu[0])) {
+ Popupmenu->setItemChecked(toggleMenu[0], false);
+ emit mode(QString("-i"), 0);
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[0], true);
+ emit mode(QString("+i"), 0);
+ }
+}
+
+void chanButtons::limited()
+{
+ chanDialog = new chanbuttonsDialog(chanbuttonsDialog::limited);
+ chanDialog->exec();
+ if (chanDialog->sendLimitedUsers() == 0) {
+ Popupmenu->setItemChecked(toggleMenu[1], false);
+ emit mode(QString("-l"), 0);
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[1], true);
+ emit mode(QString("+l %1").arg(chanDialog->sendLimitedUsers()), 0);
+ }
+ delete chanDialog;
+}
+
+void chanButtons::key()
+{
+ chanDialog = new chanbuttonsDialog(chanbuttonsDialog::key);
+ if (Popupmenu->isItemChecked(toggleMenu[2])) {
+ chanDialog->exec();
+ Popupmenu->setItemChecked(toggleMenu[2], false);
+ emit mode(QString("-k %1").arg(chanDialog->sendKey()), 0);
+ }
+ else {
+ chanDialog->exec();
+ Popupmenu->setItemChecked(toggleMenu[2], true);
+ emit mode(QString("+k %1").arg(chanDialog->sendKey()), 0);
+ }
+ delete chanDialog;
+}
+
+void chanButtons::secret()
+{
+ if (Popupmenu->isItemChecked(toggleMenu[3])) {
+ Popupmenu->setItemChecked(toggleMenu[3], false);
+ emit mode(QString("-s"), 0);
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[3], true);
+ emit mode(QString("+s"), 0);
+ }
+}
+
+void chanButtons::invisible()
+{
+ if (Popupmenu->isItemChecked(toggleMenu[4])) {
+ Popupmenu->setItemChecked(toggleMenu[4], false);
+ emit mode(QString("-i"), 1, m_proc->getNick());
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[4], true);
+ emit mode(QString("+i"), 1, m_proc->getNick());
+ }
+}
+
+void chanButtons::wallops()
+{
+ if (Popupmenu->isItemChecked(toggleMenu[5])) {
+ Popupmenu->setItemChecked(toggleMenu[5], false);
+ emit mode(QString("-w"), 1, m_proc->getNick());
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[5], true);
+ emit mode(QString("+w"), 1, m_proc->getNick());
+ }
+}
+
+void chanButtons::serverNotices()
+{
+ if (Popupmenu->isItemChecked(toggleMenu[6])) {
+ Popupmenu->setItemChecked(toggleMenu[6], false);
+ emit mode(QString("-s"), 1, m_proc->getNick());
+ }
+ else {
+ Popupmenu->setItemChecked(toggleMenu[6], true);
+ emit mode(QString("+s"), 1, m_proc->getNick());
+ }
+}
+
+void chanButtons::protectMode()
+{
+ if (protectButton->isOn())
+ emit mode(QString("+t"), 0);
+ else
+ emit mode(QString("-t"), 0);
+}
+
+void chanButtons::outsideMode()
+{
+ if (outsideButton->isOn())
+ emit mode(QString("+n"), 0);
+ else
+ emit mode(QString("-n"), 0);
+}
+
+void chanButtons::moderateMode()
+{
+ if (moderateButton->isOn())
+ emit mode(QString("+m"), 0);
+ else
+ emit mode(QString("-m"), 0);
+}
+
+void chanButtons::setButtonsEnabled(bool enabled)
+{
+ // Set all the channel mode entries disabled if non-op else enable them
+ protectButton->setEnabled(enabled);
+ outsideButton->setEnabled(enabled);
+ moderateButton->setEnabled(enabled);
+ Popupmenu->setItemEnabled(toggleMenu[0], enabled);
+ Popupmenu->setItemEnabled(toggleMenu[1], enabled);
+ Popupmenu->setItemEnabled(toggleMenu[2], enabled);
+ Popupmenu->setItemEnabled(toggleMenu[3], enabled);
+}
+
+void chanButtons::setMenuItemMode(int id, bool value)
+{
+ Popupmenu->setItemChecked(toggleMenu[id], value);
+}
+
+chanbuttonsDialog::chanbuttonsDialog(const type &modeType, QWidget *parent,
+ const char *name, bool modal) : KDialog(parent, name, modal)
+{
+ m_sendKey = "";
+ m_sendLimitedUsers = 0;
+ resize(190, 82);
+ setCaption(i18n("Limit Number of Users"));
+ LimitedLayout = new QVBoxLayout(this);
+ LimitedLayout->setSpacing(9);
+ LimitedLayout->setMargin(11);
+
+ switch (modeType) {
+ case limited:
+ SpinBox = new KIntSpinBox(this);
+ LimitedLayout->addWidget(SpinBox);
+ break;
+ case key:
+ EditBox = new QLineEdit(this);
+ LimitedLayout->addWidget(EditBox);
+ break;
+ }
+
+ Layout = new QHBoxLayout;
+ Layout->setSpacing(6);
+ Layout->setMargin(0);
+
+ okButton = new KPushButton(KStdGuiItem::ok(), this);
+ okButton->setSizePolicy(QSizePolicy((QSizePolicy::SizeType)1, (QSizePolicy::SizeType)1,
+ okButton->sizePolicy().hasHeightForWidth()));
+ Layout->addWidget(okButton);
+ switch (modeType) {
+ case limited:
+ connect(okButton, SIGNAL(clicked()), SLOT(limitedUsers()));
+ break;
+ case key:
+ connect(okButton, SIGNAL(clicked()), SLOT(keyString()));
+ break;
+ }
+
+ Layout->addWidget(okButton);
+
+ cancelButton = new KPushButton(KStdGuiItem::cancel(), this);
+ cancelButton->setSizePolicy(QSizePolicy((QSizePolicy::SizeType)1, (QSizePolicy::SizeType)1,
+ cancelButton->sizePolicy().hasHeightForWidth()));
+ Layout->addWidget(cancelButton);
+ connect(cancelButton, SIGNAL(clicked()), SLOT(reject()));
+ LimitedLayout->addLayout(Layout);
+
+ QSpacerItem *spacer = new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
+ LimitedLayout->addItem(spacer);
+}
+
+void chanbuttonsDialog::keyString()
+{
+ m_sendKey = EditBox->text();
+ accept();
+}
+
+void chanbuttonsDialog::limitedUsers()
+{
+ m_sendLimitedUsers = SpinBox->value();
+ accept();
+}
diff --git a/ksirc/chanButtons.h b/ksirc/chanButtons.h
new file mode 100644
index 00000000..2b7edc3a
--- /dev/null
+++ b/ksirc/chanButtons.h
@@ -0,0 +1,85 @@
+/* mode buttons for ksirc - Robbie Ward <linuxphreak@gmx.co.uk>*/
+
+#ifndef CHANBUTTONS_H
+#define CHANBUTTONS_H
+
+#include <qwidget.h>
+#include <qpushbutton.h>
+#include <kdialog.h>
+
+class QHBoxLayout;
+class QVBoxLayout;
+class KPopupMenu;
+class chanbuttonsDialog;
+class KIntSpinBox;
+class QLineEdit;
+class KSircProcess;
+class KPushButton;
+
+class chanButtons : public QWidget
+{
+ Q_OBJECT
+ friend class KSircTopLevel;
+ public:
+ chanButtons(KSircProcess *proc, QWidget* parent=0, const char* name=0);
+ ~chanButtons() {};
+
+ void setProtectMode(bool value) { protectButton->setOn(value); }
+ void setModerateMode(bool value) { moderateButton->setOn(value); }
+ void setNooutsideMode(bool value) { outsideButton->setOn(value); }
+ void setButtonsEnabled(bool);
+ void setMenuItemMode(int, bool);
+
+ signals:
+ void mode(QString, int, QString nick = QString::null); /// int == 0 channel mode, int == 1 user nick mode
+
+ private slots:
+ void protectMode();
+ void outsideMode();
+ void moderateMode();
+ void invite();
+ void limited();
+ void key();
+ void secret();
+ void invisible();
+ void wallops();
+ void serverNotices();
+
+ private:
+ int toggleMenu[7];
+ QHBoxLayout *layout;
+ QPushButton *protectButton;
+ QPushButton *moderateButton;
+ QPushButton *outsideButton;
+ QPushButton *menuButton;
+ KPopupMenu *Popupmenu;
+ chanbuttonsDialog *chanDialog;
+ KSircProcess *m_proc;
+};
+
+class chanbuttonsDialog : public KDialog
+{
+ Q_OBJECT
+ friend class chanButtons;
+ public:
+ enum type { limited, key };
+ chanbuttonsDialog(const type &modeType, QWidget *parent=0, const char *name=0, bool modal=true);
+ ~chanbuttonsDialog() {};
+
+ private slots:
+ void keyString();
+ void limitedUsers();
+ QString sendKey() { return m_sendKey; }
+ int sendLimitedUsers() { return m_sendLimitedUsers; };
+
+ private:
+ QString m_sendKey;
+ int m_sendLimitedUsers;
+ QVBoxLayout *LimitedLayout;
+ QHBoxLayout *Layout;
+ KIntSpinBox *SpinBox;
+ QLineEdit *EditBox;
+ KPushButton *okButton;
+ KPushButton *cancelButton;
+};
+#endif
diff --git a/ksirc/chanparser.cpp b/ksirc/chanparser.cpp
new file mode 100644
index 00000000..8429a6f5
--- /dev/null
+++ b/ksirc/chanparser.cpp
@@ -0,0 +1,1047 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "alistbox.h"
+#include "chanparser.h"
+#include "ksopts.h"
+#include "control_message.h"
+#include "ssfeprompt.h"
+#include "toplevel.h"
+#include "ksircprocess.h"
+#include "ksview.h"
+
+#include <stdio.h>
+
+#include <qregexp.h>
+#include <qapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <qptrlist.h>
+
+// Static parser table is "initialized"
+QDict<parseFunc> ChannelParser::parserTable;
+
+
+ChannelParser::ChannelParser(KSircTopLevel *_top)
+{
+ top = _top;
+
+ /*
+ * Initial helper variables
+ */
+ prompt_active = false;
+ current_item = -1;
+ top_item = 0;
+
+ if(parserTable.isEmpty() == TRUE){
+ parserTable.setAutoDelete(TRUE);
+ parserTable.insert("`l`", new parseFunc(&ChannelParser::parseSSFEClear));
+ parserTable.insert("`s`", new parseFunc(&ChannelParser::parseSSFEStatus));
+ parserTable.insert("`i`", new parseFunc(&ChannelParser::parseSSFEInit));
+ parserTable.insert("`t`", new parseFunc(&ChannelParser::parseSSFEMsg));
+ parserTable.insert("`o`", new parseFunc(&ChannelParser::parseSSFEOut));
+ parserTable.insert("`p`", new parseFunc(&ChannelParser::parseSSFEPrompt));
+ parserTable.insert("`P`", new parseFunc(&ChannelParser::parseSSFEPrompt));
+ parserTable.insert("`R`", new parseFunc(&ChannelParser::parseSSFEReconnect));
+ // The rest are *** info message
+ parserTable.insert("***", new parseFunc(&ChannelParser::parseINFOInfo));
+ parserTable.insert("*E*", new parseFunc(&ChannelParser::parseINFOError));
+ parserTable.insert("*!*", new parseFunc(&ChannelParser::parseINFONicks)); // Normal
+ parserTable.insert("*C*", new parseFunc(&ChannelParser::parseINFONicks)); // 1st line
+ parserTable.insert("*c*", new parseFunc(&ChannelParser::parseINFONicks)); // Last line
+ parserTable.insert("*#*", new parseFunc(&ChannelParser::parseINFONicks)); // Non enhanced
+ parserTable.insert("*$*", new parseFunc(&ChannelParser::parseINFONicks)); // Enhanced turned off
+ parserTable.insert("*>*", new parseFunc(&ChannelParser::parseINFOJoin));
+ parserTable.insert("*<*", new parseFunc(&ChannelParser::parseINFOPart));
+ parserTable.insert("*N*", new parseFunc(&ChannelParser::parseINFOChangeNick));
+ parserTable.insert("*+*", new parseFunc(&ChannelParser::parseINFOMode));
+ parserTable.insert("*T*", new parseFunc(&ChannelParser::parseINFOTopic));
+ // End of info message
+ parserTable.insert("* ", new parseFunc(&ChannelParser::parseCTCPAction));
+ }
+
+}
+
+parseResult *ChannelParser::parse(QString string)
+{
+ // for older Qts
+
+ parseFunc *pf;
+ if(string.length() < 3){
+ return new parseError(string, QString("Dumb string, too short"));
+ }
+
+ /**
+ * Start pre-parsing the strings to make them fit the 3 character
+ * parser codes, etc
+ */
+
+ /*
+ * SSFE control messages are too long, so we convert the
+ * messges into a 3 character code, `#ssfe#\S becomes `\S`
+ */
+
+ if ((string[0] == '`') & (string.length() > 7))
+ {
+ QString prefix = "`"+string[7]+"`";
+ string = prefix + QString(" ") + string.mid(8).stripWhiteSpace();
+ }
+ else if((string[0] == '*') && (string[1] == ' ')) {
+ string.insert(1, ' ');
+ }
+ // Pre-parsing is now complete
+
+ pf = parserTable[string.mid(0, 3)];
+ if(pf != 0x0){
+ parseResult *result = (this->*(pf->parser))(string);
+
+ if (result)
+ return result;
+ }
+ else
+ {
+ // debug("No handler for: %s", string.data());
+ }
+
+ // Little bit of past parsing to catch one we've missed
+ if((string[0] == '*') && (string[2] == '*')) {
+ string.remove(0, 3);
+ return new parseSucc(string, ksopts->infoColor, "user|servinfo");
+ }
+ // If it's unkown we just fall out of the function
+ return 0;
+}
+
+parseResult * ChannelParser::parseSSFEClear(QString string)
+{
+// top->mainw->clear();
+ top->clearWindow();
+// top->mainw->repaint(TRUE);
+ string.truncate(0);
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEStatus(QString string)
+{
+ string.remove(0, 12); // strip off the first 12 characters "<junk> [sirc] "
+ if(string.length() == 0)
+ return new parseError("", i18n("Unable to parse status string"));
+
+ //kdDebug(5008) << "String: " << string << endl;
+ QRegExp rx("(\\S+).*\\(*([+-]*[+-\\w\\d]*)\\)*.*on (\\S+) \\((\\S+)\\)");
+ if(rx.search(string) == -1){
+ return new parseError("", i18n("Unable to parse status (no known format) string"));
+ }
+
+ QString nick = rx.cap(1);
+ QString modes = rx.cap(2);
+ QString chan = rx.cap(3);
+ QString chanmode = rx.cap(4);
+
+ /*
+ * fix up modes which may have gotten the (away) part
+ */
+ if(modes.contains("away")){
+ modes = "";
+ }
+
+ bool away = false;
+ if(string.contains("(away)"))
+ away = true;
+
+ if(away){
+ chan.prepend(i18n("Away-"));
+ }
+
+ nickListItem *nickItem = top->nicks->item( top->nicks->findNick( nick ) );
+ if ( nickItem ) {
+ if(nickItem->away() != away){
+ nickItem->setAway( away );
+ top->nicks->viewport()->repaint( top->nicks->itemRect( nickItem ), false );
+ }
+ nickItem->forceColour(&ksopts->ownNickColor);
+ }
+
+ top->ksircProcess()->setNick(nick);
+ if (chanmode.findRev("t") != -1)
+ top->channelButtons->setProtectMode(true);
+ else top->channelButtons->setProtectMode(false);
+ if (chanmode.findRev("m") != -1)
+ top->channelButtons->setModerateMode(true);
+ else top->channelButtons->setModerateMode(false);
+ if (chanmode.findRev("n") != -1)
+ top->channelButtons->setNooutsideMode(true);
+ else top->channelButtons->setNooutsideMode(false);
+ if (chanmode.findRev("i") != -1)
+ top->channelButtons->setMenuItemMode(0, true);
+ else top->channelButtons->setMenuItemMode(0, false);
+ if (chanmode.findRev("s") != -1)
+ top->channelButtons->setMenuItemMode(3, true);
+ else top->channelButtons->setMenuItemMode(3, false);
+
+ if (modes.findRev("i") != -1)
+ top->channelButtons->setMenuItemMode(4, true);
+ else top->channelButtons->setMenuItemMode(4, false);
+ if (modes.findRev("w") != -1)
+ top->channelButtons->setMenuItemMode(5, true);
+ else top->channelButtons->setMenuItemMode(5, false);
+ if (modes.findRev("s") != -1)
+ top->channelButtons->setMenuItemMode(6, true);
+ else top->channelButtons->setMenuItemMode(6, false);
+
+ QString status_line = QString("%1 (%2) %3 (%4) ").arg(chan).arg(chanmode).arg(nick).arg(modes);
+
+ /*
+ * Go srearching for key and limit messages
+ */
+ QRegExp rxKey("<key: (\\S+)>");
+ if(rxKey.search(string) >= 0){
+ top->channelButtons->setMenuItemMode(2, true);
+ status_line += QString("<key: %1>").arg(rxKey.cap(1));
+ }
+ else {
+ top->channelButtons->setMenuItemMode(2, false);
+ }
+
+ QRegExp rxLimit("<limit: (\\S+)>");
+ if(rxLimit.search(string) >= 0){
+ top->channelButtons->setMenuItemMode(1, true);
+ status_line += QString("<limit: %1>").arg(rxLimit.cap(1));
+ }
+ else {
+ top->channelButtons->setMenuItemMode(1, false);
+ }
+
+ if(ksopts->displayTopic){
+ if(top->topic().length() > 0)
+ status_line += "T: " + top->topic();
+ else
+ status_line += "T: " + i18n("<No Topic Set>");
+ }
+
+ if(top->caption != status_line){
+ if(nick[0] == '@' || (nick[0] == '*' && nick[1] == '@')) {
+ // If we're an op,,
+ // update the nicks popup menu
+ top->channelButtons->setButtonsEnabled(true); // set the buttons enabled if were an op
+ top->opami = TRUE;
+ } // opami = true sets us to an op
+ else {
+ top->channelButtons->setButtonsEnabled(false); // set the buttons enabled if were an op
+ top->opami = FALSE;
+ } // FALSE, were not an ops
+ top->UserUpdateMenu(); // update the menu
+ top->setCaption(status_line);
+ top->setIconText(status_line);
+ if(top->ticker) {
+ top->ticker->setCaption(status_line);
+ }
+ top->caption = status_line; // Make copy so we're not
+ // constantly changing the title bar
+ }
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEInit(QString)
+{
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEOut(QString)
+{
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEMsg(QString string)
+{
+
+ if(string.length() > 100)
+ return new parseError(QString::null, i18n("String length for nick is greater than 100 characters. This is unacceptably long."));
+
+ int l = string.length();
+ if (l <= 0)
+ return new parseError(string, i18n("String not long enough"));
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+
+parseResult * ChannelParser::parseSSFEPrompt(QString string)
+{
+
+ if(prompt_active == FALSE){
+ QString prompt, caption;
+ ssfePrompt *sp;
+
+ // Flush the screen.
+ // First remove the prompt message from the Buffer.
+ // (it's garunteed to be the first one)
+ top->LineBuffer.remove( *top->LineBuffer.begin() );
+ top->Buffer = FALSE;
+ top->sirc_receive(QString(""));
+
+ // "'[pP]' " gives 4 spaces
+ if(string.length() < 5)
+ caption = i18n("");
+ else
+ caption = string.mid(3);
+ prompt_active = TRUE;
+
+ // If we use this, then it blows up
+ // if we haven't popped up on the remote display yet.
+
+ KSirc::TextParagIterator it = top->mainw->firstParag();
+ QString last;
+ while(it.atEnd() == FALSE) {
+ last = it.plainText();
+ ++it;
+ }
+
+ if(last[0] == '['){ /* strip time stamp */
+ prompt = last.mid(last.find(' '));
+ }
+ else {
+ prompt = last;
+ }
+
+ sp = new ssfePrompt(prompt, 0);
+ sp->setCaption(caption);
+ if(string[1] == 'P')
+ sp->setPassword(TRUE);
+ sp->exec();
+ // cerr << "Entered: " << sp->text() << endl;
+ prompt = sp->text();
+ prompt += "\n";
+ emit top->outputUnicodeLine(prompt);
+ delete sp;
+ prompt_active = FALSE;
+ }
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseSSFEReconnect(QString)
+{
+
+ if(top->channelInfo().channel()[0] == '#' ||
+ top->channelInfo().channel()[0] == '&'){
+ QString str = "/join " + QString(top->channelInfo().channel()) + "\n";
+ emit top->outputUnicodeLine(str);
+ }
+
+ return new parseSucc(QString::null); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFOInfo(QString string)
+{
+ string.remove(0, 3); // takes off the junk
+
+ return new parseSucc(string, ksopts->infoColor, "user|servinfo"); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFOError(QString string)
+{
+ string.remove(0, 3); // strip the junk
+
+ return new parseSucc(string,ksopts->errorColor, "user|error"); // Null string, don't display anything
+}
+
+parseResult * ChannelParser::parseINFONicks(QString in_string)
+{
+ QString string = in_string;
+ QString channel_name;
+ bool clear_box = FALSE;
+
+ // Check to see if it's a continued line
+ if(string[1] == 'C'){
+ string[1] = '!';
+ clear_box = TRUE;
+ }
+ if(string[1] == '#'){
+ string[1] = '!';
+ clear_box = FALSE;
+ }
+ else if(string[1] == 'c'){
+ if(current_item > 0)
+ top->nicks->setCurrentItem(current_item);
+ top->nicks->setTopItem(top_item);
+ top->nicks->repaint(TRUE);
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+ }
+ else if(string[1] == '$'){
+ top->nicks->clearAdvOps();
+ //kdDebug(5008) << "Turning off advanced ops" << endl;
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+ }
+
+
+ // Get the channel name portion of the string
+ // Search for the first space, since : can be embeded into channel names.
+ //count = sscanf(string, "*!* Users on %100[^ ]", channelInfo().channel());
+ // *!* Users on #TEST: boren asj asj_
+ QRegExp rx("\\*\\S\\* Users on (\\S+): (.+)");
+ if(rx.search(string) == -1){
+ return new parseError(string, i18n("Could not find channel name"));
+ }
+ channel_name = rx.cap(1);
+
+
+ if (channel_name.lower() != top->channelInfo().channel().lower()){
+ string.remove(0,3);
+ return new parseSucc(string,ksopts->infoColor,"user|misc4");
+ }
+
+ if(clear_box == TRUE){
+ current_item = top->nicks->currentItem();
+ top_item = top->nicks->topItem();
+ top->nicks->clear();
+ }
+
+ //int start = string.find(": ", 0, FALSE); // Find start of nicks
+ //if (start < 0)
+ // return new parseError(string, i18n("Could not find start of nicks"));
+ //
+ //place_holder = new char[string.length()];
+ //strcpy(place_holder, string.ascii()+start+2);
+ //nick = strtok(place_holder, " ");
+ // while(nick != 0x0){ // While there's nick to go...
+ QStringList nicks = QStringList::split(QRegExp("\\s+"), rx.cap(2));
+
+ for ( QStringList::Iterator it = nicks.begin(); it != nicks.end(); ++it ) {
+ QString nick = *it;
+ nickListItem *irc = new nickListItem();
+
+ bool done = FALSE;
+ uint i;
+
+ for(i = 0; i < nick.length();i++){
+ switch(nick[0].unicode()){
+ case '@':
+ irc->setOp(TRUE);
+ nick.remove(0,1);
+ break;
+ case '+':
+ irc->setVoice(TRUE);
+ nick.remove(0,1);
+ break;
+ case '#':
+ irc->setAway(TRUE);
+ nick.remove(0,1);
+ break;
+ case '*':
+ irc->setIrcOp(TRUE);
+ nick.remove(0,1);
+ break;
+ default:
+ done = TRUE;
+ }
+ if(done == TRUE)
+ break;
+ }
+
+ if(nick == top->ksircProcess()->getNick()){
+ irc->forceColour(&ksopts->ownNickColor);
+ }
+ irc->setText(nick);
+ top->nicks->inSort(irc);
+ }
+
+ return new parseSucc(QString::null); // Parsing ok, don't print anything though
+}
+
+parseResult * ChannelParser::parseINFOJoin(QString string)
+{
+ string.remove(0, 4); // strip *>* to save a few compares
+ // You have joined channel #Linux
+ QRegExp rx("You have joined channel (\\S+)");
+ if(rx.search(string) != -1){
+ //QString channel = rx.cap(1).lower();
+ QString channel = rx.cap(1);
+ //kdDebug(5008) << "Channel: " << channel << endl;
+
+ if(top->channelInfo().channel() != channel) {
+ KSircChannel ci(top->channelInfo().server(), channel);
+ kdDebug(5008) << "Warning: we got a channel join yet me don't belong to it!!! Assuming no key!" << endl;
+ kdDebug(5008) << "String was: " << string << endl;
+ kdDebug(5008) << "We think the channel is: " << channel << " we are: " << top->channelInfo().channel()<< endl;
+ emit top->open_toplevel(ci);
+ }
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
+ }
+
+ // reef-diddy (nenernener@xnet-3B34A9E2.snet.net) has joined channel #aquaria
+ rx.setPattern("(\\S+) .+ has joined channel (\\S+)");
+ if(rx.search(string) != -1){
+ QString nick = rx.cap(1);
+ QString channel = rx.cap(2).lower();
+ //kdDebug(5008) << "Channel: " << channel << " nick: " << nick << endl;
+ if(top->channelInfo().channel().lower() != channel){
+ return new parseWrongChannel(" " + string, ksopts->errorColor, "user|join");
+ }
+ // nicks->insertItem(s3, 0); // add the sucker
+ top->nicks->inSort(nick);
+ top->addCompleteNick(nick);
+ highlightNick(string, nick);
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|join");
+ }
+
+ return 0; // ??
+}
+
+parseResult * ChannelParser::parseINFOPart(QString string)
+{
+
+ bool foundNick = false;
+ QString pixname = "user|kick";
+ QString nick("");
+
+ string.remove(0, 4); // clear junk
+
+ // Multiple type of parts, a signoff or a /part
+ // Each get's get nick in a diffrent localtion
+ // Set we search and find the nick and the remove it from the nick list
+ // 1. /quit, signoff, nick after "^Singoff: "
+ // 2. /part, leave the channek, nick after "has left \w+$"
+ // 3. /kick, kicked off the channel, nick after "kicked off \w+$"
+ //
+ // Signoff: looser
+ QRegExp rx("Signoff: (\\S+)");
+ if(rx.search(string) != -1) {
+ nick = rx.cap(1);
+ foundNick = true;
+ pixname = "user|X";
+ highlightNick(string, nick);
+ }
+ /*
+ * Check for "You" before everyone else or else the next
+ * case will match it
+ * You have left channel <channel>
+ */
+ rx.setPattern("You have left channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ QString channel = rx.cap(1);
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ QApplication::postEvent(top, new QCloseEvent());
+ // WE'RE DEAD
+ return new parseSucc(QString::null);
+ }
+ pixname = "user|part";
+ }
+ /*
+ * Same as above, check your own state first
+ * You have been kicked off channel <channel>
+ */
+ rx.setPattern("You have been kicked off channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ QString channel = rx.cap(1);
+ if(top->channelInfo().channel().lower() != channel.lower())
+ return new parseWrongChannel(string, ksopts->errorColor, "user|kick");
+ if (ksopts->autoRejoin == TRUE)
+ {
+ QString str = QString("/join %1\n").arg(top->channelInfo().channel());
+ emit top->outputUnicodeLine(str);
+ /* if(top->ticker)
+ top->ticker->show();
+ else*/
+ top->show();
+ }
+ else
+ {
+ if(top->KickWinOpen != false)
+ return new parseError(" " + string, i18n("Kick window open"));
+ top->KickWinOpen = true;
+ int result = KMessageBox::questionYesNo(top, string, i18n("You Have Been Kicked"), i18n("Rejoin"), i18n("Leave"));
+ if (result == KMessageBox::Yes)
+ {
+ QString str = QString("/join %1\n").arg(top->channelInfo().channel());
+ emit top->outputUnicodeLine(str);
+ /* if(top->ticker)
+ * top->ticker->show();
+ * else*/
+ top->show();
+ return new parseJoinPart(" " + string, ksopts->channelColor, "user|kick");
+ }
+ else
+ {
+ // WE'RE DEAD
+ QApplication::postEvent(top, new QCloseEvent());
+ top->KickWinOpen = false;
+ }
+ }
+ pixname = "user|kick";
+ }
+ /*
+ * <nick> has left channel <channel>
+ */
+ rx.setPattern("(\\S+) has left channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ nick = rx.cap(1);
+ QString channel = rx.cap(2);
+// kdDebug(5008) << "Nick: " << nick << " Channel: " << channel << " top: " << top->channelInfo().channel() << endl;
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ foundNick = true;
+ }
+ else{
+ return new parseWrongChannel(QString::null);
+ }
+ pixname = "user|part";
+ highlightNick(string, nick);
+ }
+ /*
+ * "<nick> has been kicked off channel <channel>"
+ */
+ rx.setPattern("(\\S+) has been kicked off channel (\\S+)");
+ if((foundNick == false) && (rx.search(string) != -1)) {
+ nick = rx.cap(1);
+ QString channel = rx.cap(2);
+ if(top->channelInfo().channel().lower() == channel.lower()) {
+ foundNick = true;
+ } else {
+ return new parseWrongChannel(QString::null);
+ }
+ highlightNick(string, nick);
+ pixname = "user|kick";
+ }
+
+ if (foundNick) {
+
+ top->removeCompleteNick(nick);
+
+ int index = top->nicks->findNick(nick);
+ if(index >= 0){
+ top->nicks->removeItem(index);
+ return new parseJoinPart(" " + string, ksopts->channelColor, pixname);
+ }
+ else {
+ return new parseJoinPart(QString::null);
+ }
+ }
+ else {
+ return new parseError(" " + string, i18n("Failed to parse part/kick/leave/quit message"));
+ }
+
+ return 0;
+}
+
+parseResult * ChannelParser::parseINFOChangeNick(QString string)
+{
+ //char old_nick[101], new_nick[101];
+ QString old_nick, new_nick;
+
+ string.remove(0, 4); // Remove the leading *N* and space
+
+ /*
+ * *N* asj_ is now known as bleh
+ */
+ //kdDebug(5008) << "Nick change: " << string << endl;
+ QRegExp rx("(\\S+) is now known as (\\S+)");
+ if(rx.search(string) == -1){
+ if(string.contains("already taken")){
+ return new parseSucc(" " + string, ksopts->errorColor, "user|error");
+ }
+
+ return new parseError(i18n("Unable to parse: %1").arg(string), i18n("Unable to parse change nick code"));
+ }
+
+ old_nick = rx.cap(1);
+ new_nick = rx.cap(2);
+
+ // If we have a window open talking to the nick
+ // Change the nick to the new one.
+ if((top->channelInfo().channel()[0] != '#' || top->channelInfo().channel()[0] != '&') &&
+ (top->channelInfo().channel() == old_nick)){
+ if(!top->ksircProcess()->mrList()[new_nick.lower()]){
+ top->control_message(CHANGE_CHANNEL, new_nick.lower());
+ }
+ }
+
+ highlightNick(string, old_nick);
+ highlightNick(string, new_nick);
+
+ // search the list for the nick and remove it
+ // since the list is source we should do a binary search...
+ int found = top->nicks->findNick(old_nick);
+ if(found >= 0){ // If the nick's in the nick list, change it and display the change
+ // save current selection
+ int selection = top->nicks->currentItem();
+
+ // Get the old item, and create a new one
+ nickListItem *it = top->nicks->item(found);
+ nickListItem *irc = new nickListItem(*it);
+ irc->setText(new_nick);
+
+ top->nicks->removeItem(found); // remove old nick
+ top->nicks->inSort(irc);
+
+ top->changeCompleteNick(old_nick, new_nick);
+
+ top->nicks->setCurrentItem(selection);
+ top->nicks->repaint(TRUE);
+ // We're done, so let's finish up
+ return new parseSucc(" " + string, ksopts->channelColor, "user|join");
+ }
+ else {
+ if(top->channelInfo().channel() == new_nick ||
+ top->channelInfo().channel() == old_nick)
+ return new parseSucc(" " + string, ksopts->channelColor, "user|elipsis");
+ else
+ return new parseSucc(QString::null);
+
+ }
+
+ // warning("Toplevel-N: nick change search failed on %s", s3.data());
+ // return new parseSucc(QString::null);
+}
+
+class mode_info {
+public:
+ mode_info(bool op, QChar mode, QString arg);
+
+ bool op() const;
+ QChar mode() const;
+ QString arg() const;
+
+private:
+ const bool m_op;
+ const QChar m_mode;
+ const QString m_arg;
+};
+
+mode_info::mode_info(bool op, QChar mode, QString arg) :
+ m_op(op),
+ m_mode(mode),
+ m_arg(arg)
+{
+}
+
+bool mode_info::op() const {
+ return m_op;
+}
+
+QChar mode_info::mode() const {
+ return m_mode;
+}
+
+QString mode_info::arg() const {
+ return m_arg;
+}
+
+
+parseResult * ChannelParser::parseINFOMode(QString string)
+{
+ // Basic idea here is simple, go through the mode change and
+ // assign each mode a + or a - and an argument or "" if there is
+ // none. After that each mode change it looked at to see if
+ // we should handle it in any special way.
+
+ // Strip off leading sirc info
+
+ string.remove(0, 4);
+
+
+ /*
+ * 1k is pretty safe since KProcess returns 1 k blocks, and lines don't get split between reads. This is emprical
+ */
+ QString modes, args, channel;
+ int found = 0;
+
+ if(string.find("for user") >= 0)
+ return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
+
+ /*
+ * We need to 2 scanf's, one for the case of arguments, and one for no args.
+ */
+ QRegExp rx("Mode change \"(\\S+) *([^\"]*)\" on channel (\\S+)");
+ if(rx.search(string) >= 0){
+ modes = rx.cap(1);
+ args = rx.cap(2);
+ channel = rx.cap(3);
+ found = 1;
+ }
+
+ rx.setPattern("Mode for channel (\\S+) is \"([^\" ]+)\"");
+ if(found == 0 &&
+ rx.search(string) >= 0){
+ channel = rx.cap(1);
+ modes = rx.cap(2);
+ found = 1;
+ }
+
+ rx.setPattern("Your user mode is");
+ if(found == 0 &&
+ rx.search(string) >= 0){
+ /*
+ * Don't parse user mode requests
+ */
+ return new parseSucc(" " + string, ksopts->infoColor, "user|mode");
+ }
+
+
+ if(found == 0)
+ return new parseError(" Failed to parse mode change: " + string, QString::null);
+
+ /*
+ * op specifie if it's a + or -. tru is + false is -
+ */
+ bool op = true;
+ /*
+ * arglist is the list of argument
+ * we use the itirator to tstep through the list
+ * as need be
+ */
+ QStringList arglist = QStringList::split(" ", args);
+ QStringList::Iterator ai = arglist.begin();
+
+ /*
+ * the ptr list structure contains the parsed contents
+ */
+ QPtrList<const mode_info> pmList;
+ pmList.setAutoDelete(true);
+
+ for(uint pos = 0; pos < modes.length(); pos++){
+ switch(modes.at(pos).unicode()){
+ case '+':
+ op = true;
+ break;
+ case '-':
+ op = false;
+ break;
+ case 'l': // Chan limits
+ /*
+ * -l doesn't take any arguments, so just add the mode and break
+ * +l otoh does, so read the argument
+ */
+ if(op == false){
+ pmList.append(new mode_info(op, 'l', QString::null));
+ break;
+ }
+ case 'o': // Op, arg is the nick
+ case 'v': // Voice, arg is the nick
+ case 'b': // Ban, arg is mask banned
+ case 'k': // kcik, arg is nick
+ if(ai == NULL)
+ return new parseError(i18n("Unable to parse mode change: %1").arg(string), QString::null);
+ pmList.append(new mode_info(op, modes.at(pos), *ai));
+ ai++;
+ break;
+ case 'i': // Invite only
+ case 'n': // No message to chan
+ case 'p': // Private
+ case 'm': // Moderated
+ case 's': // Secret
+ case 't': // Topic setable by ops
+ case 'R': // (Dalnet) only registered may join
+ case 'r': // (Dalnet) only registered may join or something
+ /*
+ * Mode changes which don't take args
+ */
+ pmList.append(new mode_info(op, modes.at(pos), QString::null));
+ break;
+ default:
+ kdDebug(5008) << "Unknown mode change: " << modes.mid(pos, 1) << " Assume no args" << endl;
+ pmList.append(new mode_info(op, modes.at(pos), QString::null));
+ }
+ }
+ // We have the modes set in mode and arg, now we go though
+ // looking at each mode seeing if we should handle it.
+ bool mode_o_plus = false;
+ bool mode_o_minus = false;
+ bool mode_b_plus = false;
+ bool mode_b_minus = false;
+
+ QPtrListIterator<const mode_info> it(pmList);
+ const mode_info *mi;
+ while ( (mi = it.current()) != 0 ) {
+ ++it;
+ /*
+ * Look at the second character, it's uniq, check for +,- latter
+ */
+ if(mi->mode().unicode() == 'o'){
+ mode_o_plus = mi->op();
+ mode_o_minus = !mi->op();
+
+ if(top->ksircProcess()->getNick() == mi->arg())
+ top->channelButtons->setButtonsEnabled(mi->op());
+
+ if(mi->arg().length() == 0){
+ qWarning("Invalid nick in +/- o mode change");
+ continue;
+ }
+
+ int offset = top->nicks->findNick(mi->arg());
+ if(offset >= 0){
+ nickListItem *irc = new nickListItem();
+ *irc = *top->nicks->item(offset);
+ top->nicks->removeItem(offset); // remove old nick
+ irc->setOp(mi->op());
+ // add new nick in sorted pass,with colour
+ top->nicks->inSort(irc);
+ top->nicks->repaint(TRUE);
+ }
+ else{
+ kdDebug(5008) << "Toplevel+o: nick search failed on " << mi->arg() << endl;
+ }
+ }
+ else if(mi->mode() == 't'){
+ if(mi->op())
+ top->channelButtons->setProtectMode(true); // set on
+ else
+ top->channelButtons->setProtectMode(false); // set off
+ }
+ else if(mi->mode() == 'm'){
+ if(mi->op())
+ top->channelButtons->setModerateMode(true); // set on
+ else
+ top->channelButtons->setModerateMode(false); // set off
+ }
+ else if(mi->mode() == 'n'){
+ if(mi->op())
+ top->channelButtons->setNooutsideMode(true); // set on
+ else
+ top->channelButtons->setNooutsideMode(false); // set off
+ }
+ else if(mi->mode() == 'v'){
+ bool voice;
+ if(mi->op())
+ voice = TRUE;
+ else
+ voice = FALSE;
+
+ if(mi->arg().length() == 0){
+ qWarning("Invalid nick in +-v mode change");
+ continue;
+ }
+
+ int offset = top->nicks->findNick(mi->arg());
+ if(offset >= 0){
+ nickListItem *irc = new nickListItem();
+ *irc = *top->nicks->item(offset);
+ top->nicks->removeItem(offset); // remove old nick
+ irc->setVoice(voice) ;
+ // add new nick in sorted pass,with colour
+ top->nicks->inSort(irc);
+ top->nicks->repaint();
+ }
+ }
+ else if(mi->mode() == 'b'){
+ if(mi->op())
+ mode_b_plus = true;
+ else
+ mode_b_minus = true;
+ }
+ else if(mi->mode() == 'k'){
+ if(mi->op()){
+ if(mi->arg().length() == 0){
+ qWarning("Invalid +k mode set, no argument!");
+ continue;
+ }
+ top->m_channelInfo.setKey(mi->arg());
+ }
+ else {
+ top->m_channelInfo.setKey(""); /* no key set anymore */
+ }
+ }
+ else{
+ QChar c(mi->mode());
+ QString m(&c, 1);
+ QString o;
+ if(mi->op())
+ o = "+";
+ else
+ o = "-";
+ kdDebug(5008) << "Did not handle: " << o << m << " arg: " << mi->arg() << endl;
+ }
+ }
+ /*
+ * We're all done, so output the message and be done with it
+ */
+ QString pixname = "user|mode";
+
+ if(mode_o_plus)
+ pixname = "user|oplus";
+ else if(mode_o_minus)
+ pixname = "user|ominus";
+ else if(mode_b_plus)
+ pixname ="user|bplus";
+ else if(mode_b_minus)
+ pixname = "user|bminus";
+
+ return new parseSucc(" " + string, ksopts->infoColor, pixname);
+}
+
+parseResult * ChannelParser::parseCTCPAction(QString string)
+{
+
+ string.remove(0, 2); // * <something> use fancy * pixmap. Remove 2, leave one for space after te *
+ // why? looks cool for dorks
+ return new parseSucc(string, ksopts->textColor, "user|action");
+}
+
+parseResult * ChannelParser::parseINFOTopic(QString string)
+{
+
+ int found = 0;
+
+ string.remove(0, 4); // Remove the leading *T* and space
+
+ //kdDebug(5008) << "Topic parser: " << string << endl;
+
+ // Topic for #boo: this is a nice test
+ QRegExp rx( "Topic for (\\S+): (.*)" );
+ if(rx.search( string ) != -1){
+ QString channel = rx.cap(1);
+ QString topic = rx.cap(2);
+ topic.replace( QRegExp( "~~" ), "~" );
+
+ /*
+ * check where it's going.
+ * topic's maybe for other channels since they have no channel routing
+ * information, so route it to the right place if need be.
+ * If we're not on the channnel just fall through and display it
+ * on our channel, maybe the user asked for a topic of a different channel
+ */
+
+ if(channel.lower() != top->channelInfo().channel().lower()){
+ if(top->ksircProcess()->mrList()[channel.lower()]){
+ KSircTopLevel *t = dynamic_cast<KSircTopLevel *>(top->ksircProcess()->mrList()[channel.lower()]);
+ if(t)
+ t->setTopic(topic);
+ }
+ }
+ else {
+ //kdDebug(5008) << "New topic: " << topic << endl;
+ top->setTopic( topic );
+ }
+ found = 1;
+ }
+
+ rx.setPattern("(\\S+) has changed the topic on channel (\\S+) to (.+)");
+ if(found == 0 && rx.search(string) != -1){
+ QString nick = rx.cap(1);
+ QString channel = rx.cap(2);
+ //kdDebug(5008) << "Channel: " << channel << endl;
+ if(top->channelInfo().channel().lower() == channel.lower()){
+ QString topic = rx.cap(3);
+ //kdDebug(5008) << "Topic: " << topic << endl;
+ topic.replace(QRegExp("~~"), "~");
+ /*
+ * topic is in double quotes, so remove them
+ */
+ top->setTopic( topic.mid(1, topic.length()-2) );
+ QString cmd = "/eval &dostatus();\n";
+ top->sirc_write(cmd);
+ }
+ highlightNick(string, nick);
+ }
+ return new parseSucc(" " + string, ksopts->infoColor, "user|topic");
+}
+
+
+void ChannelParser::highlightNick(QString &string, QString &nick)
+{
+ QRegExp rx(QString("(^|\\s+)%1(\\s+|$)").arg(QRegExp::escape(nick)));
+ string.replace(rx, "\\1~n" + nick + "~n\\2");
+}
+
diff --git a/ksirc/chanparser.h b/ksirc/chanparser.h
new file mode 100644
index 00000000..ef130680
--- /dev/null
+++ b/ksirc/chanparser.h
@@ -0,0 +1,289 @@
+#ifndef CHAN_PARSER_H
+#define CHAN_PARSER_H
+
+#include <qcolor.h>
+#include <qdict.h>
+
+/*
+ * This file defines the parser, and all exceptions generated
+ * by the toplevel parse_input function.
+ */
+
+class ChannelParser; // Main class defined
+class QString;
+class KSircTopLevel;
+
+class parseResult
+{
+public:
+ parseResult() { }
+ virtual ~parseResult() { }
+ // Empty
+};
+
+/*
+ * Parsing is ok, this is the string to display
+ */
+class parseSucc : public parseResult
+{
+public:
+ QString string; // String to display
+ QColor colour;
+ QString pm;
+
+ parseSucc(const QString &_s, const QColor &_c = QColor(), const QString &_pm = QString::null){
+ string = _s;
+ colour = _c;
+ pm = _pm;
+ }
+};
+
+/*
+ * parseError is a fatal error message.
+ * arg0: if it is not empty, it will get displayed to the channel screen (in error colour with mad smile)
+ * arg1: if none empty goes to stderr
+ */
+class parseError : public parseResult
+{
+public:
+ QString str;
+ QString err;
+
+ parseError(const QString &_s, const QString &_e)
+ {
+ str = _s;
+ err = _e;
+ }
+};
+
+class parseWrongChannel : public parseSucc
+{
+public:
+ parseWrongChannel(const QString &_s, const QColor &_c = QColor(), const QString &_pm = QString::null)
+ : parseSucc(_s, _c, _pm)
+ {
+ }
+
+};
+
+class parseJoinPart : public parseSucc
+{
+public:
+ parseJoinPart(const QString &_s, const QColor &_c = QColor(), const QString &_pm = QString::null)
+ : parseSucc(_s, _c, _pm)
+ {
+ }
+
+};
+
+class infoFoundNick {
+public:
+ char nick[101];
+ infoFoundNick(char *_n){
+ strncpy(nick, _n, 100);
+ nick[100]='\0';
+ }
+};
+
+class badModeChangeError // Mode change barfed up, exit out
+{
+public:
+ QString str;
+ char *msg;
+
+ badModeChangeError(QString _str, char *_msg)
+ {
+ str = _str;
+ msg = _msg;
+ }
+};
+
+class wrongChannelError // Message was not intended for us, display str and continue
+{
+public:
+ int display;
+
+ wrongChannelError(int _display)
+ {
+ display = _display;
+ }
+};
+
+class doneModes // Finished parsing modes from the extModes string
+{
+public:
+ doneModes(int /*i*/)
+ {
+ }
+};
+
+// End of exceptions
+
+class parseFunc
+{
+public:
+ parseResult *(ChannelParser::*parser)(QString);
+ parseFunc(parseResult *(ChannelParser::*_parser)(QString)){
+ parser = _parser;
+ }
+
+private:
+ parseFunc(); // Disable the default no argument constructor
+};
+
+
+class ChannelParser {
+
+public:
+ /**
+ * ChannelParser takes a KSircTopLevel as it's first argument so
+ * we can refrence it latter.
+ *
+ * NOTE: the KSircTopLevel befriends the parser so we can have access to.
+ * NOTE2: don't abuse this you little wanker.
+ */
+ ChannelParser(KSircTopLevel *_top);
+
+
+ /**
+ * parse() thanks the string to be parsed and parses it.
+ * It returns a class of type 'parseResult' or 0.
+ */
+ parseResult *parse(QString string);
+
+private:
+ KSircTopLevel *top;
+
+ /**
+ * These are helper variables used internally in the parsing functions
+ */
+
+ bool prompt_active;
+ int current_item;
+ int top_item;
+
+ /**
+ * The parser Table holds a list of functions with parse
+ * sepecific commands and arguments. The key for the dict is the
+ * first 3 characters of the "search key" for the string type.
+ *
+ * Even through the parserTable is static, the functions called manipulate
+ * this object.
+ *
+ * The functions do the following:
+ * - On succesfull compleion: generte a parseSucc exception
+ * - On failure generate: parseFailed, etc
+ */
+
+ static QDict<parseFunc> parserTable;
+
+ /*
+ * Note regarding ssfe control messages:
+ *
+ * They are converted from the form `#ssfe#<COMMAND> to `<COMMAND>`
+ * t/m is converted to t
+ */
+
+ /**
+ * Start of the praser function definitions
+ *
+ * Each function returns void (since they never return it does matter)
+ * 1 argument, the string to parse
+ *
+ */
+
+ /**
+ * SSFE clear 'l' function, clears main window
+ */
+ parseResult * parseSSFEClear(QString string);
+
+ /**
+ * SSFE Status is used to update caption, and op status
+ *
+ */
+ parseResult * parseSSFEStatus(QString string);
+
+ /**
+ * SSFE Init is called by ssfe when it's time to init and setup
+ */
+ parseResult * parseSSFEInit(QString string);
+
+ /**
+ * SSFE msg is called for each /msg sent to a diffrent user
+ */
+ parseResult * parseSSFEMsg(QString string);
+
+ /**
+ * SSFE Out, not used
+ */
+ parseResult * parseSSFEOut(QString string);
+
+ /**
+ * SSFE Prompt, same function used for p and P, gives a password prompt
+ */
+ parseResult * parseSSFEPrompt(QString string);
+
+ /**
+ * SSFE Reconnect called when (re)connected to a server
+ */
+ parseResult * parseSSFEReconnect(QString string);
+
+ /**
+ * That's all the SSFE control messages, the rest are info *\S* message
+ */
+
+ /**
+ * *** is a generic infomation messge
+ */
+ parseResult * parseINFOInfo(QString string);
+
+ /**
+ * *E* is a error message
+ */
+ parseResult * parseINFOError(QString string);
+
+ /**
+ * *#* is a list of nicks, used to update the nick list if it's for
+ * the current channel.
+ */
+ parseResult * parseINFONicks(QString in_string);
+
+ /**
+ * *>* is a join message. If it's for the current channel
+ */
+ parseResult * parseINFOJoin(QString string);
+
+ /**
+ * *<* is a part message, if it's for the current channel remove it.
+ * There's multiple part types, so parsing it more complicated.
+ */
+ parseResult * parseINFOPart(QString string);
+
+ /**
+ * *N* Is a nick change, update the nick list if needed
+ * Nick changes go outto all windows, so the nick may not be on
+ * out current channel.
+ */
+ parseResult * parseINFOChangeNick(QString string);
+
+ /*
+ * *M* is a mode change. Parsing is mode changes is probably the most
+ * complicated of all the parsings
+ */
+ parseResult * parseINFOMode(QString string);
+
+ /*
+ * * is a ctcp actiion. Simply print a pretty * for the user
+ */
+ parseResult * parseCTCPAction(QString string);
+
+ /*
+ * *T* is a topic message, catch it and update the status is required
+ */
+ parseResult * parseINFOTopic(QString string);
+
+private:
+ void highlightNick(QString &string, QString &nick);
+};
+
+#endif
diff --git a/ksirc/charSelector.cpp b/ksirc/charSelector.cpp
new file mode 100644
index 00000000..6cfe09a4
--- /dev/null
+++ b/ksirc/charSelector.cpp
@@ -0,0 +1,77 @@
+/* ascii table for ksirc - Robbie Ward <linuxphreak@gmx.co.uk>*/
+
+#include "charSelector.h"
+#include "charSelector.moc"
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+
+charSelector::charSelector(QWidget *parent, const char* name)
+ : KDialog(parent, name, false)
+{
+ testLayout = new QVBoxLayout(this);
+ testLayout->setSpacing( spacingHint() );
+ testLayout->setMargin( marginHint() );
+
+ charSelect = new KCharSelect(this, QCString(name) + "_kcharselector", "", 0);
+ testLayout->addWidget(charSelect);
+ charSelect->installEventFilter(this);
+
+ connect(charSelect, SIGNAL(doubleClicked()), SLOT(insertText()));
+
+ QHBoxLayout *buttonLayout = new QHBoxLayout;
+ buttonLayout->setSpacing( spacingHint() );
+
+ insertButton = new QPushButton(i18n("&Insert Char"), this);
+ connect(insertButton, SIGNAL(clicked()), SLOT(insertText()));
+ buttonLayout->addWidget(insertButton);
+
+ QSpacerItem *spacer = new QSpacerItem(50, 20, QSizePolicy::Expanding, QSizePolicy::Expanding);
+ buttonLayout->addItem(spacer);
+
+ testLayout->addLayout(buttonLayout);
+}
+
+charSelector::~charSelector()
+{
+ delete charSelect;
+ charSelect = 0x0;
+}
+
+void charSelector::insertText()
+{
+ emit clicked();
+}
+
+void charSelector::setFont( const QString &font )
+{
+ charSelect->setFont(font);
+}
+
+void charSelector::reject()
+{
+ KDialog::reject();
+ close();
+}
+
+void charSelector::keyPressEvent(QKeyEvent *e)
+{
+ KDialog::keyPressEvent(e);
+}
+
+bool charSelector::eventFilter ( QObject *, QEvent * e )
+{
+ if ( e->type() == QEvent::AccelOverride ) {
+ // special processing for key press
+ QKeyEvent *k = (QKeyEvent *)e;
+ if(k->key() == Key_Escape){
+ keyPressEvent(k);
+ return TRUE; // eat event
+ }
+ }
+ // standard event processing
+ return FALSE;
+}
+
diff --git a/ksirc/charSelector.h b/ksirc/charSelector.h
new file mode 100644
index 00000000..927cb5e5
--- /dev/null
+++ b/ksirc/charSelector.h
@@ -0,0 +1,42 @@
+/* ascii table for ksirc - Robbie Ward <linuxphreak@gmx.co.uk>*/
+
+#ifndef CHARSELECTOR_H
+#define CHARSELECTOR_H
+
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kcharselect.h>
+
+class QVBoxLayout;
+class QPushButton;
+
+class charSelector : public KDialog
+{
+ Q_OBJECT
+ public:
+ charSelector(QWidget *parent=0, const char *name=0);
+ ~charSelector();
+
+ virtual bool eventFilter ( QObject * watched, QEvent * e );
+
+ public slots:
+ void insertText();
+ QString currentText() { return charSelect->chr(); }
+
+ void setFont( const QString &font );
+
+ protected slots:
+ virtual void reject();
+
+ signals:
+ void clicked();
+
+ protected:
+ virtual void keyPressEvent(QKeyEvent *e);
+
+ private:
+ KCharSelect *charSelect;
+ QVBoxLayout* testLayout;
+ QPushButton *insertButton;
+};
+#endif
diff --git a/ksirc/colorpicker.cpp b/ksirc/colorpicker.cpp
new file mode 100644
index 00000000..57b3de60
--- /dev/null
+++ b/ksirc/colorpicker.cpp
@@ -0,0 +1,342 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "colorpicker.h"
+#include "ksopts.h"
+
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qvbox.h>
+#include <qstyle.h>
+#include <qlineedit.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+
+#include <klocale.h>
+
+ColorPicker::ColorPicker( QWidget *parent, const char *name )
+ : KDialogBase( parent, name, true /*modal*/, i18n( "Pick Color" ),
+ KDialogBase::Ok | KDialogBase::Cancel,
+ KDialogBase::Cancel ),
+ m_foregroundColor( -1 ), m_backgroundColor( -1 )
+{
+ QVBox *mainWidget = makeVBoxMainWidget();
+
+ QWidget *sampleBox = new QWidget( mainWidget );
+ QHBoxLayout *sampleLayout = new QHBoxLayout( sampleBox );
+
+ QLabel *preview = new QLabel( i18n( "Preview:" ), sampleBox );
+ sampleLayout->addWidget( preview );
+
+ m_sample = new QLineEdit( i18n( "Sample Text" ), sampleBox );
+ m_sample->setFocusPolicy( NoFocus );
+ m_sample->setSizePolicy( QSizePolicy( QSizePolicy::Minimum,
+ m_sample->sizePolicy().verData() ) );
+ sampleLayout->addWidget( m_sample );
+ sampleLayout->addStretch();
+
+ QHBox *box = new QHBox( mainWidget );
+ QLabel *description = new QLabel( i18n( "&Foreground:" ), box );
+ ColorBar *foregroundColor = new ColorBar( ksopts->ircColors.toValueVector(), box );
+ description->setBuddy( foregroundColor );
+
+ box = new QHBox( mainWidget );
+ description = new QLabel( i18n( "&Background:" ), box );
+ ColorBar *backgroundColor = new ColorBar( ksopts->ircColors.toValueVector(), box );
+ description->setBuddy( backgroundColor );
+
+ QPushButton *ok = actionButton( KDialogBase::Ok );
+ QPushButton *cancel = actionButton( KDialogBase::Cancel );
+
+ setTabOrder( foregroundColor, backgroundColor );
+ setTabOrder( backgroundColor, ok );
+ setTabOrder( ok, cancel );
+
+ ok->setAutoDefault( false );
+ cancel->setAutoDefault( false );
+
+ connect( foregroundColor, SIGNAL( colorPicked( int ) ),
+ this, SLOT( setForegroundColor( int ) ) );
+ connect( backgroundColor, SIGNAL( colorPicked( int ) ),
+ this, SLOT( setBackgroundColor( int ) ) );
+
+ ok->setEnabled( false );
+
+ updateSample();
+}
+
+QString ColorPicker::colorString() const
+{
+ assert( m_foregroundColor != -1 );
+ QString res( QString::number( m_foregroundColor ) );
+ if ( m_backgroundColor != -1 )
+ {
+ res += ',';
+ res += QString::number( m_backgroundColor );
+ }
+ return res;
+}
+
+void ColorPicker::setForegroundColor( int col )
+{
+ QPushButton * ok =actionButton( KDialogBase::Ok );
+ assert( ok );
+ ok->setEnabled( true );
+
+ m_foregroundColor = col;
+ updateSample();
+}
+
+void ColorPicker::setBackgroundColor( int col )
+{
+ m_backgroundColor = col;
+ updateSample();
+}
+
+void ColorPicker::updateSample()
+{
+ QColorGroup cg( colorGroup() );
+
+ QColor col = ksopts->textColor;
+ if ( m_foregroundColor != -1 )
+ col = ksopts->ircColors[ m_foregroundColor ];
+
+ cg.setColor( QColorGroup::Foreground, col );
+ cg.setColor( QColorGroup::Text, col );
+
+ if ( m_backgroundColor != -1 )
+ {
+ col = ksopts->ircColors[ m_backgroundColor ];
+ cg.setColor( QColorGroup::Background, col );
+ cg.setColor( QColorGroup::Base, col );
+ }
+
+ m_sample->setPalette( QPalette( cg, cg, cg ) );
+}
+
+ColorBar::ColorBar( const QValueVector<QColor> &colors, QWidget *parent,
+ const char *name )
+ : QFrame( parent, name, WStaticContents | WRepaintNoErase ),
+ m_currentCell( -1 ), m_focusedCell( - 1 ), m_colors( colors ),
+ m_cellSize( 0 )
+{
+ setFrameStyle( StyledPanel | Sunken );
+
+ updateCellSize();
+
+ setFocusPolicy( StrongFocus );
+}
+
+void ColorBar::drawContents( QPainter *p )
+{
+ int x = contentsRect().x();
+ int y = contentsRect().y();
+ for ( unsigned int i = 0; i < m_colors.size(); ++i, x += m_cellSize )
+ {
+ bool isCurrentCell = ( m_currentCell != -1 &&
+ i == static_cast<uint>( m_currentCell ) );
+ bool isFocusedCell = ( m_focusedCell != -1 &&
+ i == static_cast<uint>( m_focusedCell ) );
+ drawCell( p, x, y, m_colors[ i ], QString::number( i ),
+ isFocusedCell, isCurrentCell );
+ }
+}
+
+void ColorBar::keyPressEvent( QKeyEvent *ev )
+{
+ if ( m_focusedCell == -1 ) {
+ QFrame::keyPressEvent( ev );
+ return;
+ }
+
+ switch ( ev->key() )
+ {
+ case Key_Left:
+ if ( m_focusedCell > 1 )
+ m_focusedCell--;
+ update();
+ ev->accept();
+ return;
+ case Key_Right:
+ if ( static_cast<uint>( m_focusedCell ) < m_colors.size() - 1 )
+ m_focusedCell++;
+ update();
+ ev->accept();
+ return;
+ case Key_Enter:
+ case Key_Return:
+ case Key_Space:
+ setCurrentCell( m_focusedCell );
+ update();
+ ev->accept();
+ return;
+ default: break;
+ }
+
+ QFrame::keyPressEvent( ev );
+}
+
+void ColorBar::focusInEvent( QFocusEvent *ev )
+{
+ if ( ev->reason() == QFocusEvent::Tab ||
+ ev->reason() == QFocusEvent::Backtab )
+ m_focusedCell = 0;
+ QFrame::focusInEvent( ev );
+}
+
+void ColorBar::focusOutEvent( QFocusEvent *ev )
+{
+ if ( ev->reason() == QFocusEvent::Tab ||
+ ev->reason() == QFocusEvent::Backtab ||
+ ev->reason() == QFocusEvent::Mouse )
+ m_focusedCell = -1;
+ QFrame::focusOutEvent( ev );
+}
+
+void ColorBar::fontChange( const QFont &oldFont )
+{
+ updateCellSize();
+ QFrame::fontChange( oldFont );
+}
+
+void ColorBar::styleChange( QStyle &oldStyle )
+{
+ updateCellSize();
+ QFrame::styleChange( oldStyle );
+}
+
+bool ColorBar::focusNextPrevChild( bool next )
+{
+ if ( next )
+ {
+ assert( m_focusedCell != -1 );
+
+ if ( static_cast<uint>( m_focusedCell ) < m_colors.size() - 1 )
+ {
+ m_focusedCell++;
+ update();
+ return true;
+ }
+ return QFrame::focusNextPrevChild( next );
+ }
+
+ if ( m_focusedCell > 1 )
+ {
+ m_focusedCell--;
+ update();
+ return true;
+ }
+
+ return QFrame::focusNextPrevChild( next );
+}
+
+void ColorBar::mousePressEvent( QMouseEvent *ev )
+{
+ const QPoint &p = ev->pos();
+ if ( contentsRect().contains( p ) )
+ {
+ m_focusedCell = p.x() / m_cellSize;
+ update();
+ }
+
+ QFrame::mousePressEvent( ev );
+}
+
+void ColorBar::mouseReleaseEvent( QMouseEvent *ev )
+{
+ if ( m_focusedCell != -1 )
+ {
+ setCurrentCell( m_focusedCell );
+ update();
+ }
+ QFrame::mouseReleaseEvent( ev );
+}
+
+void ColorBar::updateCellSize()
+{
+ setLineWidth( style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ) );
+
+ QFontMetrics metrics( font() );
+
+ m_cellSize = metrics.width( QString::number( m_colors.size() ) ) +
+ ( s_indicatorSize * 2 ) +
+ ( s_focusSize * 2 ) +
+ ( s_innerMargin * 2 );
+
+ setFixedSize( QSize( ( m_colors.size() * m_cellSize ) + ( frameWidth() * 2 ),
+ m_cellSize + ( frameWidth() * 2 ) ) );
+}
+
+void ColorBar::setCurrentCell( int cell )
+{
+ m_currentCell = cell;
+ emit colorPicked( cell );
+}
+
+void ColorBar::drawCell( QPainter *p, int x, int y, const QColor &color,
+ const QString &text, bool isFocusedCell,
+ bool isCurrentCell )
+{
+ p->fillRect( x, y, m_cellSize, m_cellSize, color );
+
+ QColor penColor = black;
+ // ### hack
+ if ( color.red() < 127 && color.green() < 127 && color.blue() < 127 )
+ penColor = white;
+
+ p->setPen( penColor );
+
+ if ( isCurrentCell )
+ {
+ p->fillRect( x, y, m_cellSize, s_indicatorSize, penColor );
+ p->fillRect( x, y + s_indicatorSize,
+ s_indicatorSize, m_cellSize - ( 2 * s_indicatorSize ), penColor );
+ p->fillRect( x, y + m_cellSize - s_indicatorSize,
+ m_cellSize, s_indicatorSize, penColor );
+ p->fillRect( x + m_cellSize - s_indicatorSize, y + s_indicatorSize,
+ s_indicatorSize, m_cellSize - ( 2 * s_indicatorSize ), penColor );
+ }
+
+ if ( isFocusedCell )
+ {
+ int focusRectSize = m_cellSize - ( 2 * s_indicatorSize );
+
+ p->fillRect( x + s_indicatorSize, y + s_indicatorSize,
+ focusRectSize, s_focusSize, penColor );
+ p->fillRect( x + s_indicatorSize, y + s_indicatorSize + s_focusSize,
+ s_focusSize, focusRectSize - ( 2 * s_focusSize ), penColor );
+ p->fillRect( x + s_indicatorSize,
+ y + m_cellSize - s_indicatorSize - s_focusSize,
+ focusRectSize, s_focusSize, penColor );
+ p->fillRect( x + m_cellSize - s_indicatorSize - s_focusSize,
+ y + s_indicatorSize + s_focusSize,
+ s_focusSize, focusRectSize - ( 2 * s_focusSize ), penColor );
+ }
+
+ QFontMetrics metrics( p->font() );
+
+ int offset = ( m_cellSize / 2 ) - ( metrics.width( text ) / 2 );
+ p->drawText( x + offset, y + s_focusSize + s_indicatorSize +
+ + metrics.ascent(), text );
+}
+
+#include "colorpicker.moc"
+
+/* vim: et sw=4
+ */
diff --git a/ksirc/colorpicker.h b/ksirc/colorpicker.h
new file mode 100644
index 00000000..67d199ae
--- /dev/null
+++ b/ksirc/colorpicker.h
@@ -0,0 +1,91 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+#ifndef __colorpicker_h__
+#define __colorpicker_h__
+
+#include <qvaluevector.h>
+#include <qcolor.h>
+
+#include <kdialogbase.h>
+
+class QLineEdit;
+
+class ColorPicker : public KDialogBase
+{
+ Q_OBJECT
+public:
+ ColorPicker( QWidget *parent, const char *name = 0 );
+
+ QString colorString() const;
+
+private slots:
+ void setForegroundColor( int col );
+ void setBackgroundColor( int col );
+
+private:
+ void updateSample();
+
+ QLineEdit *m_sample;
+ int m_foregroundColor;
+ int m_backgroundColor;
+};
+
+class ColorBar : public QFrame
+{
+ Q_OBJECT
+public:
+ ColorBar( const QValueVector<QColor> &colors, QWidget *parent, const char *name = 0 );
+
+signals:
+ void colorPicked( int col );
+
+protected:
+ virtual void drawContents( QPainter *p );
+ virtual void keyPressEvent( QKeyEvent *ev );
+ virtual void focusInEvent( QFocusEvent *ev );
+ virtual void focusOutEvent( QFocusEvent *ev );
+ virtual void fontChange( const QFont &oldFont );
+ virtual void styleChange( QStyle &oldStyle );
+ virtual bool focusNextPrevChild( bool next );
+ virtual void mousePressEvent( QMouseEvent *ev );
+ virtual void mouseReleaseEvent( QMouseEvent *ev );
+
+private:
+ void updateCellSize();
+ void setCurrentCell( int cell );
+
+ void drawCell( QPainter *p, int x, int y, const QColor &color,
+ const QString &text, bool isFocusedCell,
+ bool isCurrentCell );
+
+ int m_currentCell;
+ int m_focusedCell;
+ const QValueVector<QColor> m_colors;
+ int m_cellSize;
+
+ // ### style me
+ static const int s_indicatorSize = 2;
+ static const int s_focusSize = 1;
+ static const int s_innerMargin = 1;
+};
+
+#endif
+
+/* vim: et sw=4
+ */
diff --git a/ksirc/control_message.h b/ksirc/control_message.h
new file mode 100644
index 00000000..cae8dd1b
--- /dev/null
+++ b/ksirc/control_message.h
@@ -0,0 +1,15 @@
+#ifndef CONTROL_MESSAGE_H
+#define CONTROL_MESSAGE_H
+
+ // 001 is change channel
+ // 002 is stop writting to mainw
+ // 003 is resume updating mainw and flush buffer
+
+#define CHANGE_CHANNEL 001
+#define STOP_UPDATES 002
+#define RESUME_UPDATES 003
+#define REREAD_CONFIG 004
+#define SET_LAG 005
+#define RESET_NOTIF 006
+
+#endif
diff --git a/ksirc/dccManager.cpp b/ksirc/dccManager.cpp
new file mode 100644
index 00000000..44d85dee
--- /dev/null
+++ b/ksirc/dccManager.cpp
@@ -0,0 +1,403 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Andrew Stanley-Jones <asj-kde@cban.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+
+#include "dccManager.h"
+#include "dccNew.h"
+
+#include <qobject.h>
+#include <qsignal.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+
+#define COL_FILE 1
+#define COL_WHO 0
+#define COL_STAT 2
+#define COL_SIZE 3
+#define COL_CPS 4
+#define COL_PER 5
+
+dccItem::dccItem( KListView *parent, dccManager *manager, enum dccType type, const QString &file, const QString& who, enum dccStatus status, unsigned int size )
+ : QObject(), KListViewItem(parent), m_who(who), m_file(file), m_type(type)
+{
+ m_percent = 0;
+ m_status = status;
+ m_size = size;
+ m_stime = 0;
+ m_lasttime = 0;
+ m_manager = manager;
+
+ setText(COL_FILE, file);
+ setText(COL_WHO, who);
+ setText(COL_STAT, enumToStatus(status));
+ if(m_type == dccChat)
+ setText(COL_SIZE, "");
+ else
+ setText(COL_SIZE, QString("%1").arg(size));
+ setText(COL_PER, "");
+
+}
+
+dccItem::dccItem( KListViewItem *parent, dccManager *manager, enum dccType type, const QString &file, const QString& who, enum dccStatus status, unsigned int size )
+: QObject(), KListViewItem(parent), m_who(who), m_file(file), m_type(type)
+{
+ m_percent = 0;
+ m_status = status;
+ m_size = size;
+ m_stime = 0;
+ m_manager = manager;
+
+ setText(COL_FILE, file);
+ setText(COL_WHO, who);
+ setText(COL_STAT, enumToStatus(status));
+ if(type != dccChat)
+ setText(COL_SIZE, QString("%1").arg(size));
+ setText(COL_PER, "");
+
+}
+
+dccItem::~dccItem()
+{
+}
+
+QString dccItem::enumToStatus(enum dccStatus status)
+{
+ QString str;
+ switch(status){
+ case dccRecving:
+ str = i18n("Receiving");
+ break;
+ case dccGotOffer:
+ str = i18n("Got Offer");
+ break;
+ case dccSentOffer:
+ str = i18n("Sent Offer");
+ break;
+ case dccWaitOnResume:
+ str = i18n("Resume Requested");
+ break;
+ case dccResumed:
+ str = i18n("Did Resume");
+ break;
+ case dccSending:
+ str = i18n("Sending");
+ break;
+ case dccOpen:
+ str = i18n("dcc status", "Open");
+ break;
+ case dccDone:
+ str = i18n("Done");
+ break;
+ case dccCancel:
+ str = i18n("Canceled");
+ break;
+ case dccError:
+ str = i18n("Error");
+ break;
+ default:
+ str = i18n("Unknown State");
+ break;
+ }
+ return str;
+}
+
+void dccItem::setWhoPostfix(const QString &post) {
+ m_post = post;
+ setText(COL_WHO, QString("%1 %2").arg(m_who).arg(post));
+}
+
+void dccItem::changeFilename(const QString &file) {
+ setText(COL_FILE, file);
+ m_file = file;
+}
+
+void dccItem::changeWho(const QString &who) {
+ setText(COL_WHO, who);
+ m_who = who;
+}
+
+void dccItem::changeStatus(enum dccStatus status)
+{
+ m_manager->doChanged();
+ setText(COL_STAT, enumToStatus(status));
+ m_status = status;
+ emit statusChanged(this);
+}
+
+void dccItem::setReceivedBytes(int bytes)
+{
+ int per;
+ time_t ctime = time(NULL);
+
+ if(m_stime == 0)
+ m_stime = ctime-1;
+
+ if(m_size)
+ per = (100*bytes)/m_size;
+ else
+ per = 100;
+
+ if((per != (int)m_percent) ||
+ (ctime >= (m_lasttime + 2))
+ ){
+ m_lasttime = ctime;
+ setText(COL_SIZE, QString("%1/%2").arg(bytes).arg(m_size));
+ setText(COL_PER, QString("%1%").arg(per));
+ m_percent = per;
+ if(m_status == dccResumed)
+ m_stime = 0; /* if we are got a resume request don't update CPS, reset it */
+ else
+ setText(COL_CPS, QString("%1").arg(1.0*bytes/(time(NULL) - m_stime), 2));
+ }
+
+
+}
+
+void dccItem::doRename()
+{
+
+ if(type() == dccGet){
+ setRenameEnabled(COL_FILE, true);
+ startRename(COL_FILE);
+ }
+ else if(type() == dccChat){
+ setText(COL_WHO, m_who);
+ setRenameEnabled(COL_WHO, true);
+ startRename(COL_WHO);
+ }
+
+}
+
+void dccItem::okRename(int col)
+{
+ KListViewItem::okRename(col);
+ if(type() == dccGet){
+ QString oldfile = m_file;
+ changeFilename(text(COL_FILE));
+ emit itemRenamed(this, m_who, oldfile);
+ setRenameEnabled(COL_FILE, false);
+ }
+ else if(type() == dccChat){
+ QString oldwho = m_who;
+ changeWho(text(COL_WHO));
+ emit itemRenamed(this, oldwho, m_file);
+ setRenameEnabled(COL_WHO, false);
+ setWhoPostfix(m_post);
+ }
+}
+
+void dccItem::cancelRename(int col)
+{
+ KListViewItem::cancelRename(col);
+ if(type() == dccChat){
+ setWhoPostfix(m_post);
+ }
+}
+
+dccManager::dccManager( QWidget *parent, const char *name ) : dccManagerbase( parent, name)
+{
+ dccNewDialog = 0x0;
+
+ m_getit = new KListViewItem(klvBox, i18n("Get"));
+ m_sendit = new KListViewItem(klvBox, i18n("Send"));
+ m_chatit = new KListViewItem(klvBox, i18n("Chat"));
+
+ m_getit->setOpen(true);
+ m_sendit->setOpen(true);
+ m_chatit->setOpen(true);
+
+ m_getit->setSelectable(false);
+ m_sendit->setSelectable(false);
+ m_chatit->setSelectable(false);
+
+// connect(klvBox, SIGNAL(clicked(QListViewItem *)),
+// this, SLOT(getSelChange(QListViewItem *)));
+ connect(klvBox, SIGNAL(currentChanged(QListViewItem *)),
+ this, SLOT(getSelChange(QListViewItem *)));
+
+ klvBox->setCurrentItem(m_chatit);
+ getSelChange(klvBox->currentItem());
+}
+
+dccManager::~dccManager()
+{
+}
+
+dccItem *dccManager::newSendItem(QString file, QString who, enum dccItem::dccStatus status, unsigned int size)
+{
+ emit changed(false, i18n("dcc activity"));
+ dccItem *it = new dccItem(m_sendit, this, dccItem::dccSend, file, who, status, size);
+ connect(it, SIGNAL(statusChanged(QListViewItem *)),
+ this, SLOT(getSelChange(QListViewItem *)));
+ return it;
+}
+
+dccItem *dccManager::newGetItem(QString file, QString who, enum dccItem::dccStatus status, unsigned int size)
+{
+ emit changed(false, i18n("dcc activity"));
+ dccItem *it = new dccItem(m_getit, this, dccItem::dccGet, file, who, status, size);
+ connect(it, SIGNAL(statusChanged(QListViewItem *)),
+ this, SLOT(getSelChange(QListViewItem *)));
+ return it;
+
+}
+
+dccItem *dccManager::newChatItem(QString who, enum dccItem::dccStatus status)
+{
+ emit changed(false, i18n("dcc activity"));
+ dccItem *it = new dccItem(m_chatit, this, dccItem::dccChat, "", who, status, 0);
+ connect(it, SIGNAL(statusChanged(QListViewItem *)),
+ this, SLOT(getSelChange(QListViewItem *)));
+ return it;
+
+}
+
+void dccManager::kpbNew_clicked()
+{
+ if(dccNewDialog){
+ dccNewDialog->show();
+ dccNewDialog->raise();
+ return;
+ }
+
+ dccNewDialog = new dccNew();
+ dccNewDialog->show();
+
+ connect(dccNewDialog, SIGNAL(accepted(int, QString, QString)),
+ this, SLOT(dccNewAccepted(int, QString, QString)));
+
+}
+
+void dccManager::dccNewAccepted(int type, QString nick, QString file){
+ kdDebug(5008) << "Type: " << type << " nick: " << nick << " file: " << file << endl;
+ if(type == dccNew::Chat){
+ QCString cstr = QCString("/dcc chat ") + nick.latin1() + "\n";
+ kdDebug(5008) << "Output: " << cstr << endl;
+ emit outputLine(cstr);
+ }
+ else if(type == dccNew::Send){
+ QCString cstr = QCString("/dcc send ") + nick.latin1() + " " + file.latin1() + "\n";
+ emit outputLine(cstr);
+ }
+ delete dccNewDialog;
+ dccNewDialog = 0x0;
+}
+
+void dccManager::kpbConnect_clicked()
+{
+ dccItem *it = dynamic_cast<dccItem *>(klvBox->currentItem());
+ if(it){
+ emit dccConnectClicked(it);
+ }
+
+}
+void dccManager::kpbResume_clicked()
+{
+ dccItem *it = dynamic_cast<dccItem *>(klvBox->currentItem());
+ if(it){
+ emit dccResumeClicked(it);
+ }
+}
+void dccManager::kpbRename_clicked()
+{
+ dccItem *it = dynamic_cast<dccItem *>(klvBox->currentItem());
+ if(it){
+ emit dccRenameClicked(it);
+ }
+}
+void dccManager::kpbAbort_clicked()
+{
+ dccItem *it = dynamic_cast<dccItem *>(klvBox->currentItem());
+ if(it){
+ emit dccAbortClicked(it);
+ }
+}
+
+void dccManager::getSelChange(QListViewItem *_i) {
+ kpbAbort->setEnabled(false);
+ kpbResume->setEnabled(false);
+ kpbRename->setEnabled(false);
+ kpbConnect->setEnabled(false);
+
+ if(_i == 0)
+ return;
+ dccItem *it = dynamic_cast<dccItem *>(_i);
+ if(!it)
+ return;
+
+ /*
+ * Everything is off by default
+ * We need to turn on what we want
+ */
+ switch(it->type()){
+ case dccItem::dccChat:
+ kdDebug(5008) << "is a chat" << endl;
+ kpbAbort->setEnabled(true);
+ switch(it->status()){
+ case dccItem::dccGotOffer:
+ kpbConnect->setEnabled(true);
+ break;
+ case dccItem::dccOpen:
+ kpbRename->setEnabled(true);
+ break;
+ default:
+ break;
+ }
+ break;
+ case dccItem::dccGet:
+ kdDebug(5008) << "is a get" << endl;
+ kpbAbort->setEnabled(true);
+ switch(it->status()){
+ case dccItem::dccGotOffer:
+ case dccItem::dccWaitOnResume:
+ case dccItem::dccResumed:
+ kpbResume->setEnabled(true);
+ kpbConnect->setEnabled(true);
+ kpbRename->setEnabled(true);
+ break;
+ default:
+ break;
+ }
+ break;
+ case dccItem::dccSend:
+ kdDebug(5008) << "is a send" << endl;
+ kpbAbort->setEnabled(true);
+ break;
+ default:
+ break;
+ }
+
+
+}
+void dccManager::sendSelChange(QListViewItem *) {
+ /*
+ if(_i == 0)
+ return;
+ dccItem *it = static_cast<dccItem *>(_i);
+
+ kdDebug(5008) << "got: " << it->who() << " file: " << it->file() << endl;
+
+ switch(it->status()){
+ case dccItem::dccSending:
+ case dccItem::dccRecving:
+ kpbSendStop->setText(i18n("Abort"));
+ break;
+ case dccItem::dccOffer:
+ kpbSendStop->setText(i18n("Forget"));
+ break;
+ case dccItem::dccDone:
+ kpbSendStop->setText(i18n("Clear"));
+ break;
+ }
+ */
+
+}
+
+
+#include "dccManager.moc"
+
diff --git a/ksirc/dccManager.h b/ksirc/dccManager.h
new file mode 100644
index 00000000..41412479
--- /dev/null
+++ b/ksirc/dccManager.h
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * *
+ * 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 _DCCMANAGER_H
+#define _DCCMANAGER_H
+
+#include <time.h>
+
+#include <qobject.h>
+
+#include "dccManagerbase.h"
+#include "klistview.h"
+
+
+class dccManager;
+class QSignal;
+class QObject;
+
+class dccItem : public QObject,
+ public KListViewItem
+{
+Q_OBJECT
+public:
+ enum dccStatus {
+ dccRecving,
+ dccOpen,
+ dccWaitOnResume,
+ dccResumed,
+ dccSentOffer,
+ dccGotOffer,
+ dccSending,
+ dccDone,
+ dccCancel,
+ dccError
+ };
+
+ enum dccType {
+ dccGet,
+ dccChat,
+ dccSend
+ };
+
+ dccItem( KListView *, dccManager *, enum dccType, const QString &file, const QString &who, enum dccStatus, unsigned int size );
+ dccItem( KListViewItem *, dccManager *, enum dccType, const QString &file, const QString &who, enum dccStatus, unsigned int size );
+ virtual ~dccItem();
+
+ void changeStatus(enum dccStatus);
+ void setReceivedBytes(int bytes);
+
+ void setWhoPostfix(const QString &post);
+ void changeFilename(const QString &file);
+ void changeWho(const QString &who);
+
+ const QString who() { return m_who; }
+ const QString file() { return m_file; }
+ enum dccStatus status() { return m_status; }
+ enum dccType type() { return m_type; }
+ double getPercent() { return m_percent; }
+
+ void doRename();
+
+signals:
+ void statusChanged(QListViewItem *);
+ void itemRenamed(dccItem *, QString oldNick, QString oldFile);
+
+protected:
+ QString enumToStatus(enum dccStatus);
+
+ virtual void okRename ( int col );
+ virtual void cancelRename ( int col );
+
+private:
+
+ dccManager *m_manager;
+ QString m_who;
+ QString m_file;
+ QString m_post;
+ int m_percent;
+ unsigned int m_size;
+ enum dccStatus m_status;
+ const enum dccType m_type;
+ time_t m_stime;
+ time_t m_lasttime;
+};
+
+
+class dccNew;
+
+class dccManager : public dccManagerbase
+{
+Q_OBJECT
+
+public:
+ dccManager( QWidget *parent = 0, const char *name = 0 );
+ ~dccManager();
+
+ dccItem *newSendItem(QString file, QString who, enum dccItem::dccStatus, unsigned int size);
+ dccItem *newGetItem(QString file, QString who, enum dccItem::dccStatus, unsigned int size);
+ dccItem *newChatItem(QString who, enum dccItem::dccStatus);
+
+ void doChanged() { emit changed(false, QString("dcc activity")); };
+
+public slots:
+ virtual void kpbNew_clicked();
+ virtual void kpbConnect_clicked();
+ virtual void kpbResume_clicked();
+ virtual void kpbRename_clicked();
+ virtual void kpbAbort_clicked();
+
+signals:
+ void outputLine(QCString);
+ void changed(bool, QString);
+ virtual void dccConnectClicked(dccItem *);
+ virtual void dccResumeClicked(dccItem *);
+ virtual void dccRenameClicked(dccItem *);
+ virtual void dccAbortClicked(dccItem *);
+
+protected slots:
+ virtual void getSelChange(QListViewItem *);
+ virtual void sendSelChange(QListViewItem *);
+ virtual void dccNewAccepted(int type, QString nick, QString file);
+
+private:
+
+ KListViewItem *m_getit;
+ KListViewItem *m_sendit;
+ KListViewItem *m_chatit;
+
+ dccNew *dccNewDialog;
+};
+
+#endif
diff --git a/ksirc/dccManagerbase.ui b/ksirc/dccManagerbase.ui
new file mode 100644
index 00000000..b925b1b2
--- /dev/null
+++ b/ksirc/dccManagerbase.ui
@@ -0,0 +1,202 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>dccManagerbase</class>
+<widget class="QFrame">
+ <property name="name">
+ <cstring>dccManagerbase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>717</width>
+ <height>318</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>DCC Manager</string>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KListView">
+ <column>
+ <property name="text">
+ <string>Who</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>File</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <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>Size</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>KB/s</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Progress</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>klvBox</cstring>
+ </property>
+ <property name="resizeMode">
+ <enum>AllColumns</enum>
+ </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>kpbNew</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;New...</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kpbConnect</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Connect</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kpbResume</cstring>
+ </property>
+ <property name="text">
+ <string>R&amp;esume</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kpbRename</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Rename</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>kpbAbort</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Disconnect</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>kpbRename</sender>
+ <signal>clicked()</signal>
+ <receiver>dccManagerbase</receiver>
+ <slot>kpbRename_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>kpbConnect</sender>
+ <signal>clicked()</signal>
+ <receiver>dccManagerbase</receiver>
+ <slot>kpbConnect_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>kpbResume</sender>
+ <signal>clicked()</signal>
+ <receiver>dccManagerbase</receiver>
+ <slot>kpbResume_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>kpbAbort</sender>
+ <signal>clicked()</signal>
+ <receiver>dccManagerbase</receiver>
+ <slot>kpbAbort_clicked()</slot>
+ </connection>
+ <connection>
+ <sender>kpbNew</sender>
+ <signal>clicked()</signal>
+ <receiver>dccManagerbase</receiver>
+ <slot>kpbNew_clicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>kpbResume_clicked()</slot>
+ <slot>kpbConnect_clicked()</slot>
+ <slot>kpbRename_clicked()</slot>
+ <slot>kpbAbort_clicked()</slot>
+ <slot>kpbNew_clicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>klistview.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/dccMgrTest.cpp b/ksirc/dccMgrTest.cpp
new file mode 100644
index 00000000..77d5fdac
--- /dev/null
+++ b/ksirc/dccMgrTest.cpp
@@ -0,0 +1,44 @@
+#include <kapplication.h>
+
+#include "dccManager.h"
+
+class Timer : public QObject
+{
+public:
+ Timer( dccManager *view )
+ : m_view( view )
+ {
+ m_it = m_view->newSendItem("test", "boren", dccItem::dccSentOffer, 123523);
+ m_view->newSendItem("test2", "boren", dccItem::dccSentOffer, 123523);
+ m_view->newSendItem("test3", "boren", dccItem::dccSentOffer, 123523);
+ m_view->newGetItem("test4", "boren", dccItem::dccGotOffer, 123523);
+ m_view->newGetItem("test4", "boren", dccItem::dccGotOffer, 123523);
+ m_view->newGetItem("test4", "boren", dccItem::dccGotOffer, 123523);
+ bytes = 0;
+ startTimer( 100 );
+ }
+ void timerEvent( QTimerEvent * )
+ {
+ m_it->changeStatus(dccItem::dccSending);
+ bytes += 1000;
+ m_it->setReceivedBytes(bytes);
+
+ }
+private:
+ dccManager *m_view;
+ int bytes;
+ dccItem *m_it;
+};
+
+
+int main(int argc, char **argv){
+ KApplication a(argc, argv, "dccMgrTest" );
+
+ dccManager kst(0, "toplevel");
+ Timer t(&kst);
+
+ a.setMainWidget(&kst);
+ kst.show();
+ return a.exec();
+}
+
diff --git a/ksirc/dccNew.cpp b/ksirc/dccNew.cpp
new file mode 100644
index 00000000..fe44fae6
--- /dev/null
+++ b/ksirc/dccNew.cpp
@@ -0,0 +1,162 @@
+/***************************************************************************
+ * *
+ * 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; version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qstringlist.h>
+#include <qlistbox.h>
+
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+
+#include "alistbox.h"
+#include "dccNew.h"
+#include "objFinder.h"
+
+dccNew::dccNew( QWidget *parent, const char *name, int type, QString nick )
+ : dccNewBase( parent, name)
+{
+
+ QColorGroup cg_mainw = kapp->palette().active();
+ cg_mainw.setColor(QColorGroup::Base, ksopts->backgroundColor);
+ cg_mainw.setColor(QColorGroup::Text, ksopts->textColor);
+ cg_mainw.setColor(QColorGroup::Link, ksopts->linkColor);
+ cg_mainw.setColor(QColorGroup::Highlight, ksopts->selBackgroundColor);
+ cg_mainw.setColor(QColorGroup::HighlightedText, ksopts->selForegroundColor);
+ nickList->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+
+ QStringList allalist = objFinder::allObjects().grep(I18N_NOOP("aListBox::"));
+
+ for ( QStringList::Iterator it = allalist.begin();
+ it != allalist.end();
+ ++it ) {
+ QString name = (*it).section("::", 1);
+ kdDebug(5008) << "Looking at: " << *it << "/" << name << endl;
+
+ aListBox *a = static_cast<aListBox *>(objFinder::find(name.latin1(), "aListBox"));
+ if(a){
+ QListBoxItem *i;
+ for(i = a->firstItem(); i != 0x0; i = i->next()){
+ nickListItem *it = new nickListItem(*a->item(a->index(i)));
+ nickList->inSort(it);
+ }
+ }
+ else {
+ kdDebug(5008) << "Didn't find: " << name << endl;
+ }
+ }
+
+ KCompletion *comp = cbNicks->completionObject();
+
+ QListBoxItem *i;
+ for(i = nickList->firstItem(); i != 0x0; i = i->next()){
+ comp->addItem(i->text());
+ cbNicks->insertItem(i->text());
+ }
+ cbNicks->setCurrentText(nick);
+
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("dccNew");
+
+ bool chatChecked = kConfig->readBoolEntry("chatChecked", false);
+
+ /*
+ * allow type to override
+ * the config setting
+ */
+ if(type == Chat){
+ chatChecked = true;
+ }
+ else if(type == Send){
+ chatChecked = false;
+ }
+
+ if(chatChecked) {
+ rbChat->setChecked(true);
+ chatClicked();
+ }
+ else {
+ rbFileSend->setChecked(true);
+ fileSendClicked();
+ }
+
+ connect(nickList, SIGNAL(highlighted(const QString &)),
+ cbNicks, SLOT(setEditText(const QString &)));
+
+ connect(pbCancel, SIGNAL(clicked()),
+ this, SLOT(reject()));
+
+ connect(pbSend, SIGNAL(clicked()),
+ this, SLOT(accept()));
+
+
+}
+
+dccNew::~dccNew()
+{
+}
+
+
+void dccNew::chatClicked()
+{
+ gbFile->setEnabled(false);
+}
+
+void dccNew::fileSendClicked()
+{
+ gbFile->setEnabled(true);
+}
+
+void dccNew::sendClicked()
+{
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("dccNew");
+ kConfig->writeEntry("chatChecked",rbChat->isChecked());
+ int type = Chat;
+ if(rbFileSend->isChecked())
+ type = Send;
+ emit accepted(type, cbNicks->currentText(), leFile->text());
+}
+
+void dccNew::fileClicked()
+{
+ QString file =
+ KFileDialog::getOpenFileName();
+
+ leFile->setText(file);
+}
+
+QString dccNew::getFile() {
+ return leFile->text() ;
+}
+
+QString dccNew::getNick() {
+ return cbNicks->currentText();
+}
+
+int dccNew::getType() {
+ int type = Chat; if(rbFileSend->isChecked()) type = Send;
+ return type;
+}
+
+void dccNew::reject()
+{
+ emit accepted(-1, QString::null, QString::null);
+}
+
+
+#include "dccNew.moc"
diff --git a/ksirc/dccNew.h b/ksirc/dccNew.h
new file mode 100644
index 00000000..e0adcf80
--- /dev/null
+++ b/ksirc/dccNew.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * *
+ * 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 version 2 of the License. *
+ * *
+ ***************************************************************************/
+
+#ifndef DCC_NEW_H
+#define DCC_NEW_H
+
+#include "dccNewbase.h"
+
+class dccNew : public dccNewBase
+{
+Q_OBJECT
+
+public:
+ dccNew( QWidget *parent = 0,
+ const char *name = 0,
+ int type = -1,
+ QString nick = QString::null );
+ ~dccNew();
+
+ enum { Chat, Send };
+
+ QString getFile();
+ QString getNick();
+ int getType();
+
+
+signals:
+ void modified();
+ void accepted(int type, QString nick, QString file);
+
+protected slots:
+ virtual void chatClicked();
+ virtual void fileSendClicked();
+ virtual void sendClicked();
+ virtual void fileClicked();
+ virtual void reject();
+};
+
+#endif
diff --git a/ksirc/dccNewbase.ui b/ksirc/dccNewbase.ui
new file mode 100644
index 00000000..32106e48
--- /dev/null
+++ b/ksirc/dccNewbase.ui
@@ -0,0 +1,237 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>dccNewBase</class>
+<author>Andrew Stanley-Jones</author>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>dccNewBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>424</width>
+ <height>386</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>New DCC</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QButtonGroup">
+ <property name="name">
+ <cstring>buttonGroup1</cstring>
+ </property>
+ <property name="frameShape">
+ <enum>GroupBoxPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Sunken</enum>
+ </property>
+ <property name="title">
+ <string>DCC Type</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbFileSend</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;File send</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rbChat</cstring>
+ </property>
+ <property name="text">
+ <string>C&amp;hat</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>51</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Nick</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="aListBox">
+ <property name="name">
+ <cstring>nickList</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>100</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>cbNicks</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>gbFile</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>File Name</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>leFile</cstring>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbFile</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;...</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbSend</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Send</string>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pbCancel</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Cancel</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+ <customwidget>
+ <class>aListBox</class>
+ <header location="local">alistbox.h</header>
+ <sizehint>
+ <width>-1</width>
+ <height>-1</height>
+ </sizehint>
+ <container>0</container>
+ <sizepolicy>
+ <hordata>5</hordata>
+ <verdata>5</verdata>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ <pixmap>image0</pixmap>
+ </customwidget>
+</customwidgets>
+<images>
+ <image name="image0">
+ <data format="XPM.GZ" length="4462">789c9d97c76e24490e86effd1442f3d65870d2451a0ce6206f5adeb4cc620f8c34f2553225b5a4c1befb46927fe6a1d4c0ccac4287fa8a0c26834193f5dbb785b3fd9d856fbf7d799ec9ecba5ea8afe469e15bf3727ffffeeffffcf1e797af49b2d0ffc7d142f2f55f5fbe1ecc16ea85dde9a4ed81290045faa77ca49cf4ab67ba1e3953969173651ab9d4fdf1c8a27c3872ad7c3c72d3b32c2a67c3f3444636fbef23eb7e590267e68fcc4656395d8dacf6d938ef97eaef289781cddebdb2846fccff44b989ba58e3411f3dc75158e6df1d3889539517ca49bf54fe43398d1dec4f46567f685fd9c539f43fc025f80c1c3ce8d93f28e77185f8bf0c6cfae4c0b5f9c3c6e5c0542a4b5876fe13708df39d2bd7716372aa8c93c4e4723bb2f977aadc26ceec4bdb7310e6b0bfab9c2445ecd49f1370694cebca65d260ffa6711a41aef14d24e94cee6be3348e0ae5e9c8765f07ca3e8db17f0f9c825794eb3489351fe9bb7217e4969f29388bd51ee9fda5715a19730696b852fea95c6471647ca95cf64bcf43e0cef4657b647bfe6acf213d6bb32f9a9f5992b5a64f9a8f990b6cf9ecc15d6cf9acf6b2da55a8a75cb973dee4b2d1b38b5c05de02434e87e01aacfb351df5bebddea74b5c67f9c795711ea15e357e2ecde358fb87efc0a867df8cac728a074e62e527706afb59f3cf6583be5c28bb3c35f693812d1fbdc6dbe57966fec932d899ffa4f7e38adca17e34beaeca4b9c271a18f1d5fc739257b0d70d9c68be7bed7fcee7827ab91cd8e4740cf6b19d4ffb87ab07b9bf516e72d4a3ac821b9cef6e60d84f47367ded2fae1d9f7704f6163f79000ffde27660f387ed3c5dbf54dfeebb0bf6acbe6b706bfa5ef32f8f8b18f5bf0f463f20edb77956a4e68fbc80b3c4facd163847be6bfde72ec86dbeac82715fbc0286be683cf3bc081d44f7df80d344fb0b6b7de64581fb916765297263d6fccd9b7ee97e566e8b06cfdb1f59cf2bda2f8bbc2c11df0c5c41aefdb328ca22b1feb0012eedbce24736f91b18fb796d64eb87cb60817dcdafb07d906bfd14d22f659d8785ef97b2f6d7b2df6ef1d6fb2babaac6f92e8c25b27ecc1f239b7f3a5fca5a8678ea7c2d9bc0e6ef1238b3fca2c7814d5f4cbf9334b5f833384bed3c7a5f5514f4adbe2ec07962e7590457f06f6f649b977afe2a16f42f5e070bfc591e18f7a5f1a812a9e0df39d827da4fbd8c6ccfd7785569bf945f953371a9d5a3de67950bfa2d75c63eb2fb15bdcfaaf011e6c7263846bf8f4636f91618f3c7d3c0f047fb69550efab2074e12bd6fd27957553ec33c5b043bcc7f9d0795f818fdb501a3df8ae673e507ff69021eea37063bc45ffb73550736ff36c0a84f3e0317a86f8b5f139e6ffe1f821de6d9127898ff3be00a7c3ab2cd03e3d697560ff40016e47b3bb2e96bbd559d8f32bbff4b7065f193042c98873acf240af6adbf3c83c5f2954bb0c7bcd77c107d81d2fdebc63e33ff640aaecc9e1c8151df5c803dfaadc64b52dfa03ed64636ffb4ff4b2867f453ede7227586fcd3f9264ded91fffafe236d5da37fe83c91ceb7b9e5b7f6731ff92ed7f747d6fbf7e185cf58347e3e6932e47b3430fa893edf87d795c2fc3f003b67f5f902cec17abfde0dfa3c053b3c5ffb832fc2eb8fddcf233887fc195c801f46b6f3cdc02558fba72f7d2d1a7f7e321ee58fe0cad86bbff24dd3e2fe74fef836b0c6eb60d62fa6bf5e07b3419f853dd7dc70fb8bd5f1255fd90ed30f9f3c5ff30ddff21ddff384a7fcc08ffcc4cf61cdf8855ff9e79c7e1db4dff89d3f7891977899577895d7789d377893b7f83b6fcfe937bc13b477798ff7f9800ff928ac633ee11f7cca6761d7f99c7e1b3cb908da11c7413be1943376e153cc39175c72f549ff9e17c31744429e6a6aa8a58e2ee98aaee9866e7f617fc24b7417a4f734a1293dd0233dd133cd8285177aa5f9f3b63ca5377aa78f607b919668995682e62aadd17ab0b1419b9ff41f682b48bed336edd02eed05ed7d5ea3033a0cdf1ed1f127fd273ae123fa41a774a6b685cee982228a837e42e927fd47caf8985c38651eb40b2ac38e4a5842658a97fab33fd248cb87d2c9a55cc9b5dcc82d1fc99ddccb44a6f230af2f8ff224cf417f262ff22a3fe54ddee543166549966545567f617f4dd66523dc6b2c9bb225df655b7682f692ecca9eeccfe97772c09b722847722c27c1f3eb70f66bf9116c9fca999ccbc59cfe25bf4a143a5ef8992561324a78bd92522acf9ebc78efe7cf7b153276db37bef59dbff457fedadff85b7f27abfede4ffcd4cf9ff76faeff4fffefeff8c7f5fedfdfbffc0fa355c495</data>
+ </image>
+</images>
+<connections>
+ <connection>
+ <sender>rbChat</sender>
+ <signal>clicked()</signal>
+ <receiver>dccNewBase</receiver>
+ <slot>chatClicked()</slot>
+ </connection>
+ <connection>
+ <sender>rbFileSend</sender>
+ <signal>clicked()</signal>
+ <receiver>dccNewBase</receiver>
+ <slot>fileSendClicked()</slot>
+ </connection>
+ <connection>
+ <sender>pbSend</sender>
+ <signal>clicked()</signal>
+ <receiver>dccNewBase</receiver>
+ <slot>sendClicked()</slot>
+ </connection>
+ <connection>
+ <sender>pbFile</sender>
+ <signal>clicked()</signal>
+ <receiver>dccNewBase</receiver>
+ <slot>fileClicked()</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>chatClicked()</slot>
+ <slot>radioButton4_clicked()</slot>
+ <slot>fileSendClicked()</slot>
+ <slot>send()</slot>
+ <slot>sendClicked()</slot>
+ <slot>fileClicked()</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>alistbox.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/ksirc/dccToplevel.cpp b/ksirc/dccToplevel.cpp
new file mode 100644
index 00000000..5e5437d2
--- /dev/null
+++ b/ksirc/dccToplevel.cpp
@@ -0,0 +1,49 @@
+
+#include <qvbox.h>
+#include <qpopupmenu.h>
+
+#include <kaction.h>
+#include <kstdaction.h>
+#include <klocale.h>
+#include <kmenubar.h>
+
+#include "dccManager.h"
+#include "dccToplevel.h"
+#include "displayMgr.h"
+
+extern DisplayMgr *displayMgr;
+
+#define DTL_WINDOW_ID 10
+
+dccTopLevel::dccTopLevel(QWidget *parent, const char *name)
+ : KMainWindow(parent, name, Qt::WDestructiveClose)
+{
+ m_mgr = new dccManager(this, QCString(QObject::name()) + "_dccManager");
+// m_mgr->show();
+ setCentralWidget(m_mgr);
+
+ connect(m_mgr, SIGNAL(changed(bool, QString)), this, SIGNAL(changed(bool, QString)));
+
+ QPopupMenu *win = new QPopupMenu(this, QCString(QObject::name()) + "_popup_window");
+ KAction *act = KStdAction::close(this, SLOT( close() ), actionCollection() );
+ act->plug(win);
+
+ menuBar()->insertItem(i18n("&File"), win, DTL_WINDOW_ID, -1);
+ menuBar()->setAccel(Key_F, DTL_WINDOW_ID);
+
+
+}
+
+dccTopLevel::~dccTopLevel()
+{
+}
+
+void dccTopLevel::close()
+{
+
+ displayMgr->hide(this);
+
+}
+
+#include "dccToplevel.moc"
+
diff --git a/ksirc/dccToplevel.h b/ksirc/dccToplevel.h
new file mode 100644
index 00000000..db9f3f67
--- /dev/null
+++ b/ksirc/dccToplevel.h
@@ -0,0 +1,33 @@
+
+#ifndef _DCCTOPLEVEL_H_
+#define _DCCTOPLEVEL_H_
+
+#include <kmainwindow.h>
+
+class dccManager;
+
+
+class dccTopLevel : public KMainWindow
+{
+Q_OBJECT
+public:
+
+ dccTopLevel(QWidget *parent = 0, const char *name = 0);
+ virtual ~dccTopLevel();
+
+ dccManager *mgr() { return m_mgr; }
+
+signals:
+ void changeChannel(const QString&,const QString&);
+ void changed(bool, QString);
+
+
+public slots:
+ void close(void);
+
+private:
+ dccManager *m_mgr;
+};
+
+#endif
+
diff --git a/ksirc/default.pl b/ksirc/default.pl
new file mode 100644
index 00000000..7b42dd18
--- /dev/null
+++ b/ksirc/default.pl
@@ -0,0 +1,867 @@
+$n_isaway='';
+$n_svefile=$ENV{"HOME"}."/.n0thing-pl.sve";
+&docommand("^alias fing ctcp \$0 finger");
+&docommand("^alias ver ctcp \$0 version");
+&docommand("^alias tim ctcp \$0 time");
+&docommand("^alias wallop wallops");
+&docommand("^alias printuh set printuh");
+&docommand("^alias printchan set printchan");
+&docommand("^alias localhost set localhost");
+&docommand("^alias logfile set logfile");
+&docommand("^alias log set log");
+&docommand("^alias ex system");
+&docommand("^alias ls system ls -al");
+&docommand("^alias cat system cat");
+&docommand("^alias rm system rm");
+&docommand("^alias ps system ps");
+&docommand("^alias mail system mail");
+&docommand("^alias setfing set finger");
+sub cmd_open {
+ &getarg;
+ $newarg=$talkchannel unless $newarg;
+ &tell("*\cbE\cb* Huh? what channel?"), return unless $newarg;
+ $newarg =~ tr/A-Z/a-z/;
+ &sl("MODE $newarg -lsmpik ".$chankey{$newarg});
+}
+&addcmd("open");
+sub cmd_l {
+ if ($talkchannel) {
+ &sl("LIST $talkchannel");
+ &sl("NAMES $talkchannel");
+ }
+}
+&addcmd("l");
+sub cmd_a {
+ if ($n_lastmsg) {
+ &msg($n_lastmsg, $args);
+ } else {
+ &tell("*\cbE\cb* How about sending a first /m to someone?");
+ }
+}
+&addcmd("a");
+sub cmd_pj {
+ if ($talkchannel) {
+ &sl("PART $talkchannel");
+ &sl("JOIN $talkchannel");
+ }
+}
+&addcmd("pj");
+&docommand("^alias ds dcc send");
+&docommand("^alias dl dcc list");
+&docommand("^alias cdc dcc close chat");
+&docommand("^alias cdg dcc close get");
+&docommand("^alias cds dcc close send");
+&docommand("^alias dcrename dcc rchat");
+&docommand("^alias dcren dcc rchat");
+&docommand("^alias rdc dcc rchat");
+sub cmd_dc {
+ &getarg;
+ if ($newarg) {
+ &docommand("dcc chat $newarg");
+ } elsif ($n_lastdc) {
+ &docommand("dcc chat $n_lastdc");
+ $n_lastdc='';
+ }
+}
+&addcmd("dc");
+sub cmd_dg {
+ &getarg;
+ if ($newarg) {
+ &docommand("dcc get $newarg $args");
+ } elsif ($n_lastdcc) {
+ &docommand("dcc get $n_lastdcc");
+ $n_lastdcc='';
+ }
+}
+&addcmd("dg");
+sub hook_disc {
+ if ($n_rejoinchannels) {
+ $n_old_when=time;
+ $n_old_umode=$umode;
+ @n_old_channels=grep(!&eq($_, $talkchannel), @channels);
+ push(@n_old_channels, $talkchannel) if $talkchannel;
+ %n_old_chankey=%chankey;
+ }
+ &docommand("server 1"), $n_lastreconnect=time if
+ $n_reconnect && time-$n_lastreconnect>15;
+}
+&addhook("disconnect", "disc");
+sub hook_reconnect {
+ if ($n_rejoinchannels && ($n_old_umode || @n_old_channels) &&
+ time <= $n_old_when+15) {
+ $n_old_umode =~ tr/o//d;
+ &sl("MODE $nick +$n_old_umode") if $n_old_umode ne '';
+ local($chans, $keys)=('', '');
+ foreach $c (keys (%n_old_chankey)) {
+ $chans .= $c.",";
+ $keys .= $n_old_chankey{$c}.",";
+ }
+ foreach $c (@n_old_channels) {
+ $c =~ tr/A-Z/a-z/;
+ $chans .= $c."," unless defined($n_old_chankey{$c});
+ }
+ $chans =~ s/,$//;
+ $keys =~ s/,$//;
+ &sl("JOIN $chans $keys") if $chans;
+ $n_old_umode='';
+ @n_old_channels=();
+ %n_old_chankey=();
+ }
+}
+&addhook("001", "reconnect");
+sub hook_ctcp {
+ if ($_[1] eq 'DCC') {
+ $n_lastdcc=$who if $_[2] =~ /^SEND /;
+ $n_lastdc=$who if $_[2] =~ /^CHAT /;
+ }
+}
+&addhook("ctcp", "ctcp");
+sub hook_kicked {
+ local($kicked, $where_from, $reason)=@_;
+ if ($n_autorejoin && &eq($kicked, $nick)) {
+ &sl("JOIN $where_from $chankey{$where_from}");
+ }
+}
+&addhook("kick", "kicked");
+sub hook_uhnotify {
+ unless ($n_slowserver) {
+ &userhost($_[0], "&tell(\"*\cb)\cb* Signon by $_[0] (\$user\\\@\$host) detected!\");", "");
+ $silent=1;
+ }
+}
+&addhook("notify_signon", "uhnotify");
+sub hook_lastmsg {
+ $n_lastmsg=$_[0] unless $_[0] =~ /^[\#\+]/;
+}
+&addhook("send_text", "lastmsg");
+&addhook("send_action", "lastmsg");
+sub hook_away {
+ if ($n_awayonce && $_[0] =~ /^\S+\s+(\S+)\s/) {
+ local($l)=$1;
+ $l =~ tr/A-Z/a-z/;
+ $silent=1 if $n_aways{$l} eq $_[0];
+ $n_aways{$l}=$_[0];
+ }
+}
+&addhook("301", "away");
+sub hook_whois {
+ if ($_[0] =~ /^\S+\s+(\S+)\s/) {
+ local($l)=$1;
+ $l =~ tr/A-Z/a-z/;
+ delete $n_aways{$l};
+ }
+}
+&addhook("311", "whois");
+sub set_autorejoin {
+ $set{'AUTOREJOIN'}="on", $n_autorejoin=1 if $_[0] =~ /^on$/i;
+ $set{'AUTOREJOIN'}="off", $n_autorejoin=0 if $_[0] =~ /^off$/i;
+}
+&addset("autorejoin");
+$set{"AUTOREJOIN"}="on";
+$n_autorejoin=1;
+&docommand("^alias rejoin set autorejoin");
+sub set_rejoinchannels {
+ $set{'REJOINCHANNELS'}="on", $n_rejoinchannels=1 if $_[0] =~ /^on$/i;
+ $set{'REJOINCHANNELS'}="off", $n_rejoinchannels=0 if $_[0] =~ /^off$/i;
+}
+&addset("rejoinchannels");
+$set{"REJOINCHANNELS"}="on";
+$n_rejoinchannels=1;
+&docommand("^alias rejoinchan set rejoinchannels");
+sub set_reconnect {
+ $set{'RECONNECT'}="on", $n_reconnect=1 if $_[0] =~ /^on$/i;
+ $set{'RECONNECT'}="off", $n_reconnect=0 if $_[0] =~ /^off$/i;
+}
+&addset("reconnect");
+$set{"RECONNECT"}="on";
+$n_reconnect=1;
+&docommand("^alias reconnect set reconnect");
+sub set_awayonce {
+ $set{'AWAYONCE'}="on", $n_awayonce=1 if $_[0] =~ /^on$/i;
+ $set{'AWAYONCE'}="off", $n_awayonce=0 if $_[0] =~ /^off$/i;
+ %n_awayonce=();
+}
+&addset("awayonce");
+$set{"AWAYONCE"}="on";
+$n_awayonce=1;
+&docommand("^alias awayonce set awayonce");
+sub set_slowserver {
+ $set{'SLOWSERVER'}="on", $n_slowserver=1 if $_[0] =~ /^on$/i;
+ $set{'SLOWSERVER'}="off", $n_slowserver=0 if $_[0] =~ /^off$/i;
+}
+&addset("slowserver");
+$set{"SLOWSERVER"}="off";
+$n_slowserver=0;
+&docommand("^alias ss set slowserver");
+sub set_wallv {
+ $set{"WALLV"}="on", $n_wallv=1 if $_[0] =~ /^on$/i;
+ $set{"WALLV"}="off", $n_wallv=0 if $_[0] =~ /^off$/i;
+}
+&addset("wallv");
+$set{"WALLV"}="off";
+$n_wallv=0;
+&docommand("^alias wallv set wallv");
+sub set_quitmsg {
+ $n_quitmsg=$set{"QUITMSG"}=$_[0];
+}
+&addset("quitmsg");
+$set{"QUITMSG"}=$n_quitmsg='';
+&docommand("^alias setquit set quitmsg");
+sub set_autoop {
+ $set{"AUTOOP"}="off", $n_autoops=0 if $_[0] =~ /^(off|none)$/i;
+ $set{"AUTOOP"}="on", $n_autoops=1 if $_[0] =~ /^(on|all)$/i;
+ $set{"AUTOOP"}="oppass", $n_autoops=2 if $_[0] =~ /^op(pass)?$/i;
+}
+&addset("autoop");
+$set{"AUTOOP"}="on";
+$n_autoops=1;
+&docommand("^alias autoop set autoop");
+sub set_opdelay {
+ $set{"OPDELAY"}=$n_opdelay=$_[0] if $_[0] =~ /^\d+$/;
+}
+&addset("opdelay");
+$set{"OPDELAY"}="on";
+$n_opdelay=5;
+&docommand("^alias delay set opdelay");
+sub set_oppass {
+ $set{"OPPASS"}=$_[0];
+}
+&addset("oppass");
+$set{"OPPASS"}="";
+&docommand("^alias oppass set oppass");
+sub cmd_ig {
+ &docommand("ignore $args"), return if (!$args || $args =~ /^-/);
+ &getarg;
+ if ($newarg =~ /\@/) {
+ $n_lastignore=$newarg;
+ &docommand("ignore $newarg");
+ } else {
+ &userhost($newarg, "\$host=&n_hostpat(\$host); \$user =~ s/^\\~//; \$n_lastignore=\"*\$user\\\@\$host\"; &docommand(\"ignore *\$user\\\@\$host\");");
+ }
+}
+&addcmd("ig");
+sub cmd_sig {
+ &getarg;
+ &userhost($newarg, "\$host=&n_hostpat(\$host); \$n_lastignore=\"*\\\@\$host\"; &docommand(\"ignore *\\\@\$host\");");
+}
+&addcmd("sig");
+sub cmd_unig {
+ &getarg;
+ if ($newarg eq '') {
+ &docommand("ignore -$n_lastignore") if $n_lastignore ne '';
+ } elsif ($newarg =~ /\@/) {
+ &docommand("ignore -$newarg");
+ } else {
+ &userhost($newarg, "\$host=&n_hostpat(\$host); \$user =~ s/^\\~//; &docommand(\"ignore -*\$user\\\@\$host\");");
+ }
+}
+&addcmd("unig");
+sub n_hostpat {
+ if ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
+ return "${1}.${2}.${3}.*";
+ } elsif ($_[0] =~ /^([^. \t]+)\.([^. \t]+)\.(.*)$/) {
+ return "*.${2}.${3}";
+ } else {
+ return $_[0];
+ }
+}
+sub cmd_kb {
+ &getarg;
+ local($why)=($args);
+ $why="game over, man. game over." unless $why;
+ &tell("*\cb0\cb* Must specify a nick to kickban"), return unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ &userhost($newarg, "\$host=&n_hostpat(\$host); \$user =~ s/^\\~//;
+&sl(\"MODE $talkchannel -o+b $newarg *!*\$user\\\@\$host\");
+&sl(\"KICK $talkchannel $newarg :$why\");");
+&addcmd("kb");
+sub cmd_ksb {
+ &getarg;
+ local($why)=($args);
+ $why="d0rks suck big m00se ballZ" unless $why;
+ &tell("*\cb0\cb* Must specify a nick to kicksiteban"), return unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ &userhost($newarg, "\$host=&n_hostpat(\$host);
+&sl(\"MODE $talkchannel -o+b $newarg *!*\\\@\$host\");
+&sl(\"KICK $talkchannel $newarg :$why\");");
+}
+&addcmd("ksb");
+sub cmd_ban {
+ &tell("*\cb0\cb* Must specify a nick to ban"), return unless $args;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ while (&getarg, $newarg ne '') {
+ &userhost($newarg, "\$host=&n_hostpat(\$host); \$user =~ s/^\\~//;
+ &sl(\"MODE $talkchannel +b *!*\$user\\\@\$host\");");
+ }
+}
+&addcmd("ban");
+sub cmd_sban {
+ &getarg;
+ &tell("*\cb0\cb* Must specify a nick to site ban"), return unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ &userhost($newarg, "\$host=&n_hostpat(\$host);
+&sl(\"MODE $talkchannel +b *!*\\\@\$host\");");
+}
+&addcmd("sban");
+sub n_unbans {
+ local($n, $l)=('0', '');
+ foreach (@n0thing_banlist) {
+ if ($n<4) {
+ $l.=" ".$_;
+ $n++;
+ } else {
+ &sl("MODE $n0thing_channel -bbbb $l");
+ $l=$_;
+ $n=1;
+ }
+ }
+ &sl("MODE $n0thing_channel -".("b" x $n)." $l");
+}
+sub cmd_sb {
+ &dosplat;
+ &getarg;
+ $newarg=$talkchannel unless $newarg;
+ return unless $newarg;
+ $n_svbanchannel=$newarg;
+ @n_svbanlist=();
+ $n_nxbannb=1;
+ &sl("MODE $newarg b");
+}
+&addcmd("sb");
+sub hook_nbbans {
+ local($c, $theban, $by)=((split(/ +/, $_[0]))[1, 2, 3]);
+ if ($n_nxbannb && &eq($c, $n_svbanchannel)) {
+ push(@n_svbanlist, $theban);
+ &tell("*\cbb\cb* $c $n_nxbannb: $theban $by");
+ $silent=1;
+ $n_nxbannb++;
+ }
+}
+&addhook("367", "nbbans");
+sub hook_endofbans {
+ $n_nxbannb=0;
+}
+&addhook("368", "endofbans");
+sub cmd_cb {
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ &addhook("367", "onecbban");
+ &addhook("368", "donebans");
+ @n0thing_banlist=();
+ $n0thing_channel=$talkchannel;
+ &sl("MODE $talkchannel b");
+}
+&addcmd("cb");
+sub hook_onecbban {
+ local($theban)=((split(/ +/, $_[0]))[2]);
+ push(@n0thing_banlist, $theban);
+ $silent=1;
+}
+sub hook_donebans {
+ &remhook("367", "onecbban");
+ &remhook("367", "oneubban");
+ &remhook("368", "donebans");
+ &n_unbans;
+}
+sub cmd_unban {
+ &getarg;
+ &tell("*\cb0\cb* Must specify a nick or address to unban"), return
+ unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ if ($newarg =~ /^\d+$/) {
+ $n0thing_channel=$talkchannel;
+ @n0thing_banlist=();
+ while(1) {
+ $newarg--;
+ &tell("*\cb0\cb* No banlist available for $talkchannel"), return
+ unless &eq($talkchannel, $n_svbanchannel);
+ &tell("*\cb0\cb* No such ban"), return unless $n_svbanlist[$newarg];
+ push(@n0thing_banlist, $n_svbanlist[$newarg]);
+ &getarg;
+ last unless $newarg;
+ &tell("*\cb0\cb* Cannot mix nicks and numbers"), last
+ unless $newarg =~ /^\d+$/;
+ }
+ &n_unbans;
+ } else {
+ @n0thing_banlist=();
+ $n0thing_channel=$talkchannel;
+ if ($newarg =~ /\@/) {
+ $newarg="*!".$newarg unless $newarg =~ /\!/;
+ $n_pat=$newarg;
+ &addhook("367", "oneubban");
+ &addhook("368", "donebans");
+ &sl("MODE $talkchannel b");
+ } else {
+ &userhost($newarg, "&n_dounban;");
+ }
+ }
+}
+&addcmd("unban");
+&docommand("^alias ub unban");
+sub n_dounban {
+ $n_pat=$who."!".$user."\@".$host;
+ &addhook("368", "donebans");
+ @n0thing_banlist=();
+ $n0thing_channel=$talkchannel;
+ &sl("MODE $talkchannel b");
+}
+sub hook_oneubban {
+ local($theban)=((split(/ +/, $_[0]))[2]);
+ local($tb0)=($theban);
+ &slashify($theban);
+ push(@n0thing_banlist, $tb0) if ($n_pat =~ /^${theban}$/i);
+ $silent=1;
+}
+sub cmd_fkb {
+ &getarg;
+ &tell("*\cb0\cb* Must specify a nick to filter kick ban"), return unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ &userhost($newarg, "\$host=&n_hostpat(\$host);
+&sl(\"MODE $talkchannel +b *!*\\\@\$host\"); &docommand(\"fk \$host $args\");");
+}
+&addcmd("fkb");
+sub cmd_fk {
+ &getarg;
+ &tell("*\cb0\cb* Must specify a pattern to kick"), return unless $newarg;
+ &tell("*\cb0\cb* You're not on a channel"), return unless $talkchannel;
+ $n0thing_channel=$talkchannel;
+ $n0thing_kickpat=$newarg;
+ &slashify($n0thing_kickpat);
+ $n0thing_kickmsg=$args || "though names may change each face retains the mask it wore";
+ &addhook("352", "dofklist");
+ &addhook("315", "donefk");
+ &sl("WHO $talkchannel");
+}
+&addcmd("fk");
+sub hook_dofklist {
+ local($a)=@_;
+ local($c, $c, $u, $h, $s, $n)=split(/ +/, $a);
+ (($n."!".$u."\@".$h) =~ /${n0thing_kickpat}/i) && $n ne $nick &&
+ &sl("KICK $n0thing_channel $n :$n0thing_kickmsg");
+ $silent=1;
+}
+sub hook_donefk {
+ &remhook("352", "dofklist");
+ &remhook("315", "donefk");
+}
+sub cmd_setaway {
+ &me("is away - $args - messages will be logged") if $talkchannel;
+ push(@n_savemsg, "-- set away on " . &date(time) . " -- $args");
+ %n_awaysent=();
+ $args="\cb\cb" if $args eq '';
+ &sl("AWAY :$args");
+ $n_isaway=1;
+}
+&addcmd("setaway");
+sub cmd_qsetaway {
+ push(@n_savemsg, "-- set away on " . &date(time) . " -- $args");
+ %n_awaysent=();
+ $args="\cb\cb" if $args eq '';
+ &sl("AWAY :$args");
+ $n_isaway=1;
+}
+&addcmd("qsetaway");
+ if ($n_isaway) {
+ local($sec, $min, $hour)=localtime(time);
+ local($w)=$who;
+ $min="0".$min if $min =~ /^\d$/;
+ push(@n_savemsg, ":\cb${who}\cb!${user}\@${host} (${hour}:${min}): $_[0]\n");
+ $w =~ tr/A-Z/a-z/;
+ &sl("NOTICE $who :Your messages are being logged - n0thing.pl"),
+ $n_awaysent{$w}=1 unless $n_awaysent{$w};
+ }
+}
+&addhook("msg", "messages");
+sub hook_dmessages {
+ if ($n_isaway) {
+ local($sec, $min, $hour)=localtime(time);
+ $min="0".$min if $min =~ /^\d$/;
+ push(@n_savemsg, "=\cb".$_[0]."\cb (${hour}:${min})= ".$_[1]."\n");
+ }
+}
+&addhook("dcc_chat", "dmessages");
+sub cmd_setback {
+ $n_isaway='';
+ &sl("AWAY");
+ foreach (@n_savemsg) {
+ &tell($_);
+ }
+ if ($#n_savemsg>0) {
+ &getuserline("*** Forget saved messages (y/n)? ", "clear? ");
+ @n_savemsg=() if /^y/i;
+ } else {
+ @n_savemsg=();
+ }
+}
+&addcmd("setback");
+sub cmd_clearmes {
+ $n_isaway='';
+ @n_savemsg=();
+}
+&addcmd("clearmes");
+sub cmd_wall {
+ &tell("*\cbE\cb* No text to send"), return unless $args;
+ &tell("*\cbE\cb* You're not on a channel"), return unless $talkchannel;
+ $wallop_text=$args;
+ $wallop_channel=$talkchannel;
+ &addhook("353", "wallopnames");
+ &sl("NAMES $talkchannel");
+}
+&addcmd("wall");
+
+sub hook_wallopnames {
+ local($wallop, $n, $s, @wallop)=("", 0, 0, split(/ +/, $_[0]));
+ shift @wallop; shift @wallop; shift @wallop;
+ $wallop[0] =~ s/^://;
+ foreach (@wallop) {
+ $wallop.=$_.",", $n++ if s/^\@// || ($n_wallv && s/^\+//) || &eq($_, $nick);
+ if ($n>=9) {
+ $wallop =~ s/,$//;
+ &sl("NOTICE $wallop :\cb[WallOp/$wallop_channel]\cb $wallop_text");
+ $n=0;
+ $s=1;
+ $wallop='';
+ }
+ }
+ if ($wallop eq '' && !$s) {
+ &tell("*\cbE\cb* No ops to wallop");
+ } elsif ($wallop ne '') {
+ $wallop =~ s/,$//;
+ &sl("NOTICE $wallop :\cb[WallOp/$wallop_channel]\cb $wallop_text");
+ }
+ &remhook("353", "wallopnames");
+ $silent=1;
+}
+sub n_addwaitop {
+ local($nck, $channel)=@_;
+ if ($n_opdelay) {
+ local($n)=($nck);
+ $channel =~ tr/A-Z/a-z/;
+ $n =~ s/(\W)/\\$1/g;
+ if ($n_waitops{$channel} !~ /:${n}:/i) {
+ $n_waitops{$channel} || ($n_waitops{$channel}=":");
+ $n_waitops{$channel}.=$nck.":";
+ }
+ unless ($n_timerdue) {
+ $n_timerdue=1;
+ &timer($n_opdelay, "&n_doautoops");
+ }
+ } else {
+ &sl("MODE $channel +o $nck");
+ }
+}
+
+sub n_remwaitop {
+ local($nck, $channel)=@_;
+ $channel =~ tr/A-Z/a-z/;
+ $nck =~ s/(\W)/\\$1/g;
+ $n_waitops{$channel} =~ s/:${nck}:/:/gi;
+ delete $n_waitops{$channel} if $n_waitops{$channel} eq ":";
+}
+
+sub n_doautoops {
+ local($l);
+ foreach $c (keys(%n_waitops)) {
+ if ($n_waitops{$c} ne ":" && $n_waitops{$c}) {
+ $l=$n_waitops{$c};
+ $l =~ s/^://;
+ $l =~ s/:$//;
+ $l =~ tr/:/ /;
+ &docommand("o ".$c." ".$l);
+ }
+ delete $n_waitops{$c};
+ }
+ $n_timerdue='';
+}
+
+sub hook_n_nickchange {
+ local($oldnick)=$who;
+ $oldnick =~ s/(\W)/\\$1/g;
+ foreach $c (keys(%n_waitops)) {
+ $n_waitops{$c} =~ s/:${oldnick}:/:$_[0]:/gi;
+ }
+}
+&addhook("nick", "n_nickchange");
+
+sub hook_n_parted {
+ &n_remwaitop($who, $_[0]);
+}
+&addhook("leave", "n_parted");
+
+sub hook_n_quitted {
+ local($w)=$who;
+ $w =~ s/(\W)/\\$1/g;
+ foreach $c (keys(%n_waitops)) {
+ $n_waitops{$c} =~ s/:${w}:/:/gi;
+ delete $n_waitops{$c} if $n_waitops{$c} eq ":";
+ }
+}
+&addhook("signoff", "n_quitted");
+
+sub hook_n_modechange {
+# we only deal with +o, +oo and the like; the worst that can happen
+# is that we op someone again
+ local ($l)=$_[1];
+ return unless $_[0] =~ /^[\#\&\+]/;
+ return unless $l =~ s/^\+o+\s+//;
+ foreach $n (split(/ /, $l)) {
+ &n_remwaitop($n, $_[0]);
+ }
+}
+&addhook("mode", "n_modechange");
+
+sub slashify {
+ $_[0] =~ s/([^\\])\./$1\\./g;
+ $_[0] =~ s/\*/\.\*/g;
+ $_[0] =~ s/([^\.\*\?\\\w])/\\$1/g;
+ $_[0] =~ tr/?/./;
+}
+sub autoopped {
+ local($nuh, $chan, $asked)=@_;
+# chan = what chan for, $asked = whether it was asked
+ local($p);
+ foreach $p (@n_autooplist) {
+ return 1 if ($asked.":".$chan.":".$nuh) =~ /^${p}$/i;
+ }
+ return '';
+}
+
+sub addtoops {
+ local($chan, $auto)=@_;
+ if (&autoopped($who."!".$user."\@".$host, $chan, 1)) {
+ &tell("*\cb0\cb* $who is already on your list");
+ } else {
+ $host=&n_hostpat($host);
+ $user =~ s/^\~//;
+ local($pat)=("*!*".$user."\@".$host);
+ &tell("*\cb0\cb* Adding $who as $pat to your autoop list for $chan");
+ &sl("NOTICE $who :You have been added to ${nick}'s autoop list for ".
+ ($chan eq '*' ? "all channels" : "$chan"));
+ &sl("NOTICE $who :You should feel *very* honored");
+ &slashify($pat);
+ &slashify($chan);
+ push(@n_autooplist, $auto.":".$chan.":".$pat);
+ }
+}
+
+sub cmd_addop {
+ if ($args =~ /^([01])\s+(\S+)\s+(\S+)\s*$/) {
+ local($auto, $chan, $uh)=($1, $2, $3);
+ if ($chan ne '*' && $chan !~ /^[#&]/) {
+ &tell("*\cbE\cb* Invalid channel - use '*' or a #channel name");
+ return;
+ }
+ $chan =~ s/\'//g;
+ $uh="*!".$uh if $uh =~ /\@/ && $uh !~ /\!/;
+ $auto=($auto==0 ? "1" : ".");
+ if ($uh =~ /\!.*\@/) {
+ if (&autoopped($uh, $chan, 1)) {
+ &tell("*\cb0\cb* $uh is already on your list");
+ return;
+ }
+ &tell("*\cb0\cb* Adding $uh to your auto-op list for $chan");
+ &slashify($uh);
+ &slashify($chan);
+ push(@n_autooplist, $auto.":".$chan.":".$uh);
+ } else {
+ &userhost($uh, "&addtoops('$chan', '$auto');");
+ }
+ } else {
+ &tell("*\cb0\cb* Format: /addop (0 or 1) (#chan or *) (nick or user\@host)");
+ }
+}
+&addcmd("addop");
+
+sub hook_joined {
+ local($c)=($_[0]);
+ $c =~ tr/A-Z/a-z/;
+ &n_addwaitop($who, $c) if ($n_autoops==1 && $haveops{$c} &&
+ &autoopped($who."!".$user."\@".$host, $c, 0));
+}
+&addhook("join", "joined");
+
+sub hook_opbeg {
+ if ($_[1] =~ /^op$/i && $n_autoops && &eq($nick, $_[0])) {
+ local($chn, $pass)=split(/ +/, $_[2]);
+ $chn =~ tr/A-Z/a-z/;
+ &sl("MODE $chn +o $who")
+ if $haveops{$chn} && $pass eq $set{"OPPASS"} &&
+ &autoopped($who."!".$user."\@".$host, $chn, 1);
+ }
+}
+&addhook("ctcp", "opbeg");
+
+sub cmd_listop {
+ local($n, $p, $q, $c, $a, $ch)=(0);
+ &getarg;
+ $ch=$newarg || '*';
+ &tell("*\cb0\cb* That's the people in your autoop list:");
+ foreach $p (@n_autooplist) {
+ ($a, $c, $q)=split(/:/, $p);
+ $c =~ s/\\//g;
+ $c =~ s/\.\*/*/g;
+ next if (!&eq($c, $ch) && $c ne '*' && $ch ne '*');
+ $n++;
+ $q =~ s/\\//g;
+ $q =~ s/\.\*/*/g;
+ &tell("*\cb0\cb* ".($a eq '.' ? "1 " : "0 ").$c." ".$q);
+ }
+ &tell("*\cb0\cb* which makes.. uhm... $n people, I think");
+}
+&addcmd("listop");
+
+sub rem_matches {
+ local($nuh, $ent)=@_;
+ $ent=$' if $ent =~ /:.*:/;
+ return 1 if $nuh =~ /${ent}$/i;
+ # not exactly the same as slashify
+ $nuh =~ s/\*/\.\*/g;
+ $nuh =~ s/([^\\])\.([^\*])/$1\\\\*\\.$2/g;
+ $nuh =~ s/([^\.\*\?\\\w])/\\\\*\\$1/g;
+ $nuh =~ tr/?/./;
+ return ($ent =~ /${nuh}$/i);
+}
+
+sub n_remop {
+ local($nuh)=($who."!".$user."\@".$host);
+ @n_autooplist=grep(!&rem_matches($nuh, $_), @n_autooplist);
+}
+
+sub cmd_remop {
+ &getarg;
+ if (!$newarg) {
+ &tell("*\cb0\cb* Must specify a nick or address");
+ } elsif ($newarg =~ /[\@\*]/) {
+ # $newarg='*!'.$newarg if ($newarg !~ /\!/);
+ &tell("*\cb0\cb* Time to remove $newarg from the autoop list");
+ @n_autooplist=grep(!&rem_matches($newarg, $_), @n_autooplist);
+ } else {
+ &tell("*\cb0\cb* Time to remove $newarg from the autoop list");
+ &userhost($newarg, "&n_remop;");
+ }
+}
+&addcmd("remop");
+
+sub cmd_clearop {
+ @n_autooplist=();
+ &tell("*\cb0\cb* Auto-op list cleared!");
+}
+&addcmd("clearop");
+sub cmd_sve {
+ local($p);
+ if (!open(SVE, "> ".$n_svefile)) {
+ &tell("*\cbE\cb* can't write to save file");
+ return;
+ }
+ print SVE $n_autoops+0, "\n";
+ print SVE ($n_autorejoin ? "1" : "0"), "\n";
+ print SVE "\n"; # deprecated, pong reply
+ print SVE $set{"FINGER"}, "\n";
+ print SVE join(" ", keys(%notify)), "\n";
+ print SVE join(" ", @n_autooplist), "\n";
+ print SVE ($n_slowserver ? "1" : "0"), "\n";
+ print SVE $n_opdelay, "\n";
+ print SVE $n_quitmsg, "\n";
+ print SVE "^\n"; # deprecated, command char
+ print SVE ($n_reconnect ? "1" : "0"), "\n";
+ print SVE $set{"AWAYONCE"}, "\n";
+ print SVE $set{"REJOINCHANNELS"}, "\n";
+ print SVE $set{"CTCP"}, "\n";
+ print SVE $set{"PRINTCHAN"}, "\n";
+ print SVE $set{"PRINTUH"}, "\n";
+ print SVE $set{"SENDAHEAD"}, "\n";
+ print SVE $set{"USERINFO"}, "\n";
+ print SVE $set{"WALLV"}, "\n";
+ print SVE $set{"OPPASS"}, "\n";
+ print SVE $set{"IRCNAME"}, "\n";
+ close SVE;
+ &tell("*\cb0\cb* save completed!");
+}
+&addcmd("sve");
+
+sub cmd_quit {
+ if ($args eq '') {
+ &docommand("/QUIT $n_quitmsg");
+ } else {
+ &docommand("/QUIT $args");
+ }
+}
+&addcmd("quit");
+&docommand("^alias bye quit");
+&docommand("^alias exit quit");
+&docommand("^alias signoff quit");
+if (open(NOTHING, "< ".$n_svefile)) {
+ local($n, $l);
+
+ chop($l=<NOTHING>);
+ &doset("AUTOOP", ($l ? "on" : "off"));
+
+ chop($l=<NOTHING>);
+ &doset("AUTOREJOIN", ($l ? "on" : "off"));
+
+ <NOTHING>;
+
+ chop($l=<NOTHING>);
+ &doset("FINGER", $l) if $l ne '';
+
+ chop($l=<NOTHING>);
+ %notify=();
+ foreach $n (split(' ', $l)) {
+ &docommand("^NOTIFY ".$n);
+ }
+
+ chop($l=<NOTHING>);
+ @n_autooplist=split(' ', $l);
+ foreach (@n_autooplist) {
+ $_=".:.*:".$_ unless /:.*:/;
+ }
+
+ chop($l=<NOTHING>);
+ &doset("SLOWSERVER", ($l ? "on" : "off"));
+
+ chop($l=<NOTHING>);
+ $l=5 if $l !~ /^\d+$/;
+ &doset("OPDELAY", $l);
+
+ chop($l=<NOTHING>);
+ &doset("QUITMSG", $l);
+
+ <NOTHING>;
+
+ chop($l=<NOTHING>);
+ $l=1 unless defined $l;
+ &doset("RECONNECT", ($l ? "on" : "off"));
+
+ chop($l=<NOTHING>);
+ &doset("AWAYONCE", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("REJOINCHANNELS", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("CTCP", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("PRINTCHAN", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("PRINTUH", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("SENDAHEAD", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("USERINFO", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("WALLV", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("OPPASS", $l) if $l;
+
+ chop($l=<NOTHING>);
+ &doset("IRCNAME", $l) if $l;
+
+ close(NOTHING);
+}
+
diff --git a/ksirc/displayMgr.h b/ksirc/displayMgr.h
new file mode 100644
index 00000000..e0838ba0
--- /dev/null
+++ b/ksirc/displayMgr.h
@@ -0,0 +1,28 @@
+#ifndef DISPLAYMGR_H
+#define DISPLAYMGR_H
+
+#include <qstring.h>
+
+class QWidget;
+
+class DisplayMgr {
+public:
+ virtual ~DisplayMgr() {};
+
+ virtual void newTopLevel(QWidget *, bool show = false) = 0;
+
+ virtual void removeTopLevel(QWidget *) = 0;
+
+ virtual void show(QWidget *) = 0;
+
+ virtual void hide(QWidget *) = 0;
+
+ virtual void raise(QWidget *, bool takefocus = false) = 0;
+
+ virtual void setCaption(QWidget *, const QString&) = 0;
+
+};
+
+
+#endif
+
diff --git a/ksirc/displayMgrMDI.cpp b/ksirc/displayMgrMDI.cpp
new file mode 100644
index 00000000..ae1e5b39
--- /dev/null
+++ b/ksirc/displayMgrMDI.cpp
@@ -0,0 +1,306 @@
+#include "displayMgrMDI.h"
+#include "toplevel.h"
+
+#include <unistd.h>
+
+#include <assert.h>
+#include <qpopupmenu.h>
+#include <kwin.h>
+#include <kaccel.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <kmenubar.h>
+
+DisplayMgrMDI::DisplayMgrMDI()
+{
+}
+
+DisplayMgrMDI::~DisplayMgrMDI()
+{
+ kdDebug(5008) << "~DisplayMgrMDI in" << endl;
+ if ( m_topLevel )
+ delete static_cast<MDITopLevel *>( m_topLevel );
+ kdDebug(5008) << "~DisplayMgrMDI out" << endl;
+}
+
+#define DMM_MDI_ID 2351
+#define DMM_DEATCH_ID 50
+#define DMM_MOVEL_ID 51
+#define DMM_MOVER_ID 52
+
+void DisplayMgrMDI::newTopLevel( QWidget *w, bool show )
+{
+ topLevel()->addWidget( w, show );
+ if(w->inherits("KSircTopLevel")){
+ KSircTopLevel *t = static_cast<KSircTopLevel *>(w);
+ connect(m_topLevel->tabWidget(), SIGNAL(currentChanged(QWidget *)), t, SLOT(focusChange(QWidget *)));
+ }
+ if(w->inherits("KMainWindow")){
+ KMainWindow *t = static_cast<KMainWindow *>(w);
+
+ QMenuBar *cmenu = t->menuBar();
+ if(cmenu){
+ QPopupMenu *m = new QPopupMenu(t, QCString(t->name()) + "_popup_MDI");
+ m->setCheckable(true);
+ m->insertItem(i18n("Detach Window"), this, SLOT(reparentReq()), 0, DMM_DEATCH_ID, 0);
+ m->insertSeparator(0);
+ m->insertItem(i18n("Move Tab Left"), this, SLOT(moveWindowLeft()), ALT+SHIFT+Key_Left, DMM_MOVEL_ID, 0);
+ m->insertItem(i18n("Move Tab Right"), this, SLOT(moveWindowRight()), ALT+SHIFT+Key_Right, DMM_MOVER_ID, 0);
+ /*
+ * By using an posisiton of 4 this works for KSircTopLevel
+ * and DCCTopLevel but will cause problems when we have
+ * new top level windows with different menus.
+ * BEWARE THIS IS BAD, we should have a better way of doing this
+ */
+ cmenu->insertItem(i18n("&Window"), m, DMM_MDI_ID, 4);
+ cmenu->setAccel(Key_M, DMM_MDI_ID);
+
+ QPopupMenu *sm = new QPopupMenu(t, "settings" );
+
+ KToggleAction *showmenu = KStdAction::showMenubar( 0, 0, t->actionCollection() );
+ showmenu->plug( sm );
+ connect( showmenu, SIGNAL(toggled(bool)), cmenu, SLOT(setShown(bool)) );
+
+ KSelectAction *selectTabbar = new KSelectAction(i18n("&Tab Bar"), 0, this, "tabbar" );
+ QStringList tabbaritems;
+ tabbaritems << i18n("&Top") << i18n("&Bottom");
+ selectTabbar->setItems(tabbaritems);
+ selectTabbar->setCurrentItem(1);
+ selectTabbar->plug( sm );
+ connect( selectTabbar, SIGNAL(activated(int)), this, SLOT(setTabPosition(int)) );
+
+ KToggleAction *showfull = KStdAction::fullScreen( 0, 0, t->actionCollection(), t );
+ showfull->plug( sm );
+ connect( showfull, SIGNAL(toggled(bool)), this, SLOT(setFullScreen(bool)) );
+
+ cmenu->insertItem( i18n("&Settings"), sm, -1, 5 );
+ }
+ }
+ topLevel()->show();
+}
+
+void DisplayMgrMDI::removeTopLevel(QWidget *w )
+{
+ if ( !m_topLevel )
+ return;
+
+ assert( w );
+
+ kdDebug(5008) << "DisplayMgrMDI: got removeToplevel" << endl;
+
+ m_topLevel->removeWidget( w );
+
+ if ( m_topLevel->widgets().count() == 0 ) {
+ kdDebug(5008) << "DisplayMgrMDI: count == 0 nuking all" << endl;
+ if ( !m_topLevel->closing() ) {
+ kdDebug(5008) << "DisplayMgrMDI: delete m_topLevel" << endl;
+ delete static_cast<MDITopLevel *>( m_topLevel );
+ }
+ m_topLevel = 0;
+ }
+ else if(m_topLevel->tabWidget()->count() == 0){
+ m_topLevel->hide();
+ }
+}
+
+void DisplayMgrMDI::show(QWidget *w)
+{
+ if ( !m_topLevel )
+ return;
+
+ m_topLevel->showWidget(w);
+ m_topLevel->show();
+}
+
+void DisplayMgrMDI::hide(QWidget *w)
+{
+ if ( !m_topLevel )
+ return;
+
+ m_topLevel->hideWidget(w);
+ if(m_topLevel->tabWidget()->count() == 0){
+ m_topLevel->hide();
+ }
+}
+
+void DisplayMgrMDI::raise(QWidget *w, bool takefocus)
+{
+ assert( m_topLevel );
+ if(takefocus){
+ KWin::setCurrentDesktop(KWin::KWin::windowInfo(m_topLevel->winId()).desktop());
+
+ m_topLevel->show();
+ m_topLevel->raise();
+ m_topLevel->setActiveWindow();
+#if KDE_IS_VERSION(3,1,92)
+ KWin::activateWindow(m_topLevel->winId());
+#else
+ KWin::setActiveWindow(m_topLevel->winId());
+#endif
+ }
+ m_topLevel->tabWidget()->showPage( w );
+}
+
+
+void DisplayMgrMDI::setCaption(QWidget *w, const QString& cap)
+{
+ assert( m_topLevel );
+
+ w->setCaption(cap);
+
+ QString esc = cap;
+ esc.replace("&", "&&");
+ kdDebug(5008) << "Set caption: " << esc << endl;
+
+ m_topLevel->tabWidget()->setTabLabel( w, esc );
+}
+
+void DisplayMgrMDI::slotCycleTabsLeft()
+{
+
+ assert( m_topLevel );
+
+ m_topLevel->previous();
+}
+
+void DisplayMgrMDI::slotCycleTabsRight()
+{
+
+ assert( m_topLevel );
+
+ m_topLevel->next();
+}
+
+void DisplayMgrMDI::reparentReq()
+{
+ kdDebug(5008) << "got reparent" << endl;
+
+ QWidget *o = kapp->focusWidget();
+ QWidget *s;
+
+ if(o == NULL){
+ kdDebug(5008) << "Kapp says no widget has focus!" << endl;
+ o = kapp->activeWindow();
+
+ if(o->inherits("KMainWindow") == false)
+ return;
+
+ s = o;
+ }
+ else {
+
+ kdDebug(5008) << "QWidget is: " << o->className() << " name: " << o->name("none give") << endl;
+
+
+ s = o;
+ while(s->parentWidget()){
+ kdDebug(5008) << "Got a: " << s->className() << endl;
+ s = s->parentWidget();
+ if(s->inherits("KMainWindow"))
+ break;
+
+ }
+ }
+
+
+// QWidget *s = m_topLevel->tabWidget()->currentPage(); can't do this since you can never reattach
+ if(s){
+ KMainWindow *kst = static_cast<KMainWindow *>(s);
+ kdDebug(5008) << "Top is: " << kst->name("none give") <<endl;
+
+ QMenuData *tmenu = kst->menuBar();
+ if(tmenu){
+ QMenuItem *menui = tmenu->findItem(DMM_MDI_ID);
+ if(menui){
+ QMenuData *cmenu = menui->popup();
+ if(cmenu->findItem(DMM_DEATCH_ID) && cmenu->isItemChecked(DMM_DEATCH_ID)){
+ kst->reparent( topLevel()->tabWidget(), 0, QPoint( 0, 0 ), true );
+ show(kst);
+ cmenu->setItemChecked(DMM_DEATCH_ID, false);
+ }
+ else {
+ hide(kst);
+ kst->reparent( 0, QPoint(0,0), true );
+ cmenu->setItemChecked(DMM_DEATCH_ID, true);
+ }
+ }
+ }
+ }
+}
+
+MDITopLevel *DisplayMgrMDI::topLevel()
+{
+ if ( !m_topLevel )
+ {
+ m_topLevel = new MDITopLevel(0x0, "MDITopLevel");
+ m_topLevel->show();
+
+ KAccel *a = new KAccel( m_topLevel );
+ a->insert( "cycle left", i18n("Cycle left"), QString::null, ALT+Key_Left, ALT+Key_Left, this, SLOT(slotCycleTabsLeft()) );
+ a->insert( "cycle right", i18n("Cycle right"), QString::null, ALT+Key_Right, ALT+Key_Right, this, SLOT(slotCycleTabsRight()) );
+ }
+
+ return m_topLevel;
+}
+
+void DisplayMgrMDI::setTabPosition( int idx ) {
+
+ switch ( idx ) {
+ case 0:
+ m_topLevel->tabWidget()->setTabPosition(QTabWidget::Top);
+ break;
+ case 1:
+ m_topLevel->tabWidget()->setTabPosition(QTabWidget::Bottom);
+ break;
+ }
+}
+
+
+void DisplayMgrMDI::setFullScreen( bool full ) {
+ if ( full )
+ m_topLevel->showFullScreen();
+ else
+ m_topLevel->showNormal();
+}
+
+
+
+void DisplayMgrMDI::moveWindowLeft() {
+ moveWindow(-1);
+}
+
+void DisplayMgrMDI::moveWindowRight() {
+ moveWindow(1);
+}
+
+void DisplayMgrMDI::moveWindow(int step)
+{
+ if(m_topLevel->tabWidget()->count() == 0){
+ return;
+ }
+
+ QWidget *w = m_topLevel->tabWidget()->currentPage();
+ if(w != 0x0){
+ int index = m_topLevel->tabWidget()->currentPageIndex();
+ index += step;
+ if(index < 0)
+ return;
+ if(index >= m_topLevel->tabWidget()->count())
+ return;
+
+ m_topLevel->setUpdatesEnabled(false);
+ m_topLevel->hideWidget(w);
+ int space = w->caption().find(" ");
+ QString esc = space < 1 ? w->caption():w->caption().left(space);
+ esc.replace("&", "&&");
+ kdDebug(5008) << "Insert tab: " << esc << endl;
+ m_topLevel->tabWidget()->insertTab( w, esc, index );
+ m_topLevel->tabWidget()->showPage( w );
+ m_topLevel->setUpdatesEnabled(true);
+ }
+}
+
+#include "displayMgrMDI.moc"
diff --git a/ksirc/displayMgrMDI.h b/ksirc/displayMgrMDI.h
new file mode 100644
index 00000000..81528421
--- /dev/null
+++ b/ksirc/displayMgrMDI.h
@@ -0,0 +1,48 @@
+#ifndef __displaymgrmdi_h__
+#define __displaymgrmdi_h__
+
+#include "displayMgr.h"
+#include "mditoplevel.h"
+
+class DisplayMgrMDI : public QObject, public DisplayMgr
+{
+ Q_OBJECT
+public:
+ DisplayMgrMDI();
+ virtual ~DisplayMgrMDI();
+
+ virtual void newTopLevel(QWidget *, bool show = FALSE);
+ virtual void removeTopLevel(QWidget *);
+
+ virtual void show(QWidget *);
+ virtual void hide(QWidget *);
+ virtual void raise(QWidget *, bool takefocus = false);
+
+ virtual void setCaption(QWidget *, const QString&);
+
+public slots:
+ virtual void slotCycleTabsLeft();
+ virtual void slotCycleTabsRight();
+
+protected slots:
+ void reparentReq();
+ void moveWindowLeft();
+ void moveWindowRight();
+ void setTabPosition(int);
+ void setFullScreen(bool);
+
+protected:
+ void moveWindow(int step);
+ void refreshMenuEntry();
+
+private:
+ MDITopLevel *topLevel();
+
+ QGuardedPtr<MDITopLevel> m_topLevel;
+ int m_moveRightTab, m_moveLeftTab;
+ QPopupMenu *m_popupWindows;
+};
+
+
+#endif
+
diff --git a/ksirc/displayMgrSDI.cpp b/ksirc/displayMgrSDI.cpp
new file mode 100644
index 00000000..ee5de75b
--- /dev/null
+++ b/ksirc/displayMgrSDI.cpp
@@ -0,0 +1,40 @@
+#include "displayMgrSDI.h"
+
+#include <qwidget.h>
+
+DisplayMgrSDI::DisplayMgrSDI()
+{
+}
+
+DisplayMgrSDI::~DisplayMgrSDI(){
+}
+
+void DisplayMgrSDI::newTopLevel(QWidget *w, bool show){
+ if(show == TRUE)
+ w->show();
+}
+
+void DisplayMgrSDI::removeTopLevel(QWidget *){
+}
+
+void DisplayMgrSDI::show(QWidget *w){
+ w->show();
+}
+
+void DisplayMgrSDI::hide(QWidget *w){
+ w->hide();
+}
+
+void DisplayMgrSDI::raise(QWidget *w, bool takefocus){
+ w->show();
+ w->raise();
+ if(takefocus)
+ w->setActiveWindow();
+
+}
+
+
+void DisplayMgrSDI::setCaption(QWidget *w, const QString& cap){
+ w->setCaption(cap);
+}
+
diff --git a/ksirc/displayMgrSDI.h b/ksirc/displayMgrSDI.h
new file mode 100644
index 00000000..04bf21fe
--- /dev/null
+++ b/ksirc/displayMgrSDI.h
@@ -0,0 +1,25 @@
+#ifndef DISPLAYMGRSDI_H
+#define DISPLAYMGRSDI_H
+
+#include "displayMgr.h"
+
+class DisplayMgrSDI : public DisplayMgr {
+public:
+ DisplayMgrSDI();
+ virtual ~DisplayMgrSDI();
+
+ virtual void newTopLevel(QWidget *, bool show = FALSE);
+ virtual void removeTopLevel(QWidget *);
+
+ virtual void show(QWidget *);
+ virtual void hide(QWidget *);
+ virtual void raise(QWidget *, bool takefocus = false);
+
+ virtual void setCaption(QWidget *, const QString&);
+
+private:
+};
+
+
+#endif
+
diff --git a/ksirc/dockservercontroller.cpp b/ksirc/dockservercontroller.cpp
new file mode 100644
index 00000000..f83f5ada
--- /dev/null
+++ b/ksirc/dockservercontroller.cpp
@@ -0,0 +1,451 @@
+/**********************************************************************
+
+ $$Id$$
+
+ Dock Server Controller. Displays server connection window, and makes
+ new server connection on demand.
+
+ *********************************************************************/
+
+#include "dockservercontroller.h"
+
+#include <qpopupmenu.h>
+#include <qwidget.h>
+#include <qapplication.h>
+#include <qregexp.h>
+#include <qtimer.h>
+#include <qcursor.h>
+#include <kpassivepopup.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kwin.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kpassivepopup.h>
+#include <kmessagebox.h>
+#include <kglobalaccel.h>
+#include <kkeydialog.h>
+#include <kdeversion.h>
+
+#include "KSPrefs/ksprefs.h"
+
+#include "servercontroller.h"
+#include "toplevel.h"
+#include "ksopts.h"
+
+#include "objFinder.h"
+#include "displayMgr.h"
+#include "objFinder.h"
+
+#include "dccNew.h"
+
+extern DisplayMgr *displayMgr;
+
+dscNickInfo::dscNickInfo(QString nick, QString server) :
+ m_nick(nick),
+ m_server(server)
+{
+}
+
+dscNickInfo::~dscNickInfo()
+{
+}
+
+
+dockServerController::dockServerController(servercontroller *sc, QWidget *parent, const char *_name)
+: KSystemTray(parent, _name),
+ m_sc(sc)
+{
+
+ m_nicks.setAutoDelete(true);
+ KPopupMenu *pop = contextMenu();
+ pop->setName("dockServerController_menu_pop");
+
+#ifndef NDEBUG
+ pop->insertItem(i18n("Dump Object Tree"), sc, SLOT(dump_obj()));
+ pop->insertItem(i18n("Server Debug Window"), sc, SLOT(server_debug()));
+ pop->insertSeparator();
+#endif
+
+
+ pop->insertItem(SmallIcon( "filter" ), i18n("&Filter Rule Editor..."),
+ m_sc, SLOT(filter_rule_editor()));
+
+ KStdAction::preferences(m_sc, SLOT(general_prefs()),
+ m_sc->actionCollection())->plug(pop);
+ KStdAction::configureNotifications(m_sc, SLOT(notification_prefs()), actionCollection())->plug(pop);
+
+ pop->insertSeparator();
+ pop->insertItem(i18n("New &Server..."),
+ m_sc, SLOT(new_connection()));
+ pop->insertItem(i18n("&Do Autoconnect..."), m_sc, SLOT(start_autoconnect_check()));
+ connect(this, SIGNAL(quitSelected()), m_sc, SLOT(endksirc()));
+
+#if KDE_IS_VERSION(3,1,92)
+ m_pic_dock = KSystemTray::loadIcon( "ksirc" );
+#else
+ m_pic_dock = UserIcon("ksirc_dock");
+#endif
+ m_pic_info = UserIcon("info");
+
+ if ( !m_pic_dock.isNull() )
+ setPixmap( m_pic_dock );
+
+ mainPop = 0x0;
+ m_mainPopVisible = false;
+ createMainPopup();
+
+ m_blinkTimer = new QTimer( this );
+ connect( m_blinkTimer, SIGNAL(timeout()), this, SLOT( blinkDockedIcon() ) );
+ m_blinkStatus = false;
+ m_blinkActive = false;
+
+ servercontroller::self()
+ ->getGlobalAccel()
+ ->insert("Raise Last Window", i18n("Raise Last Window"),
+ i18n("If someone said your nick in a window, this action "
+ "will make that window active for you."),
+ ALT+CTRL+Key_A, KKey::QtWIN+CTRL+Key_A, this,
+ SLOT(raiseLastActiveWindow()));
+ servercontroller::self()
+ ->getGlobalAccel()
+ ->insert("Clear Blink", i18n("Clear Blinking Dock Icon"),
+ i18n("If the dock icon is blinking, but you don't want "
+ "to go to the window this will clear the blinking."),
+ ALT+CTRL+Key_Down, KKey::QtWIN+CTRL+Key_Down, this,
+ SLOT(blinkClear()));
+
+// mainPop = new KPopupMenu(this, "dockServerController_main_pop");
+// mainPop->setTitle(i18n("KSirc Dock Menu"));
+
+}
+
+
+
+dockServerController::~dockServerController()
+{
+kdDebug(5008) << "~dockServerController in" << endl;
+ m_sc = 0x0;
+kdDebug(5008) << "~dockServerController out" << endl;
+}
+
+int dockServerController::intoPopupSorted(QString str, QPopupMenu *what)
+{
+ uint i = 0;
+
+ for(i = 0; i < mainPop->count(); i++){
+ if(mainPop->text(mainPop->idAt(i)) > str){
+ break;
+ }
+ }
+ return mainPop->insertItem(str, what, -1, i);
+
+}
+
+void dockServerController::mainPopShow()
+{
+ m_mainPopVisible = true;
+}
+
+void dockServerController::mainPopHide()
+{
+ m_mainPopVisible = false;
+}
+
+void dockServerController::createMainPopup()
+{
+ if(m_mainPopVisible == true)
+ return;
+
+ if(mainPop)
+ delete mainPop;
+ mainPop = new KPopupMenu(this, "dockservercontrller_main_pop");
+ connect(mainPop, SIGNAL(activated(int)),
+ this, SLOT(mainActivated(int)));
+ connect(mainPop, SIGNAL(aboutToShow()),
+ this, SLOT(mainPopShow()));
+ connect(mainPop, SIGNAL(aboutToHide()),
+ this, SLOT(mainPopHide()));
+
+
+ QDictIterator<dscNickInfo> it( m_nicks );
+ for( ; it.current(); ++it){
+ KPopupMenu *sub = new KPopupMenu(mainPop);
+ if(it.current()->status() == dscNickInfo::isOnline){
+ sub->insertItem(i18n("Came Online: ") + it.current()->online().toString("hh:mm"));
+ if( ! it.current()->offline().isNull() ){
+ sub->insertItem(i18n("Last Offline: ") + it.current()->offline().toString("hh:mm"));
+ }
+ int pid = sub->insertItem(i18n("Ping"));
+ int wid = sub->insertItem(i18n("Whois"));
+ int cid = sub->insertItem(i18n("Chat"));
+ int dcid = sub->insertItem(i18n("DCC Chat"));
+
+ int id = intoPopupSorted(it.current()->server() +" -> " + it.current()->nick() + " " + i18n("online"), sub);
+ sub->setItemParameter(pid, id);
+ sub->setItemParameter(cid, id);
+ sub->setItemParameter(wid, id);
+ sub->setItemParameter(dcid, id);
+ connect(sub, SIGNAL(activated(int)),
+ this, SLOT(subItemActivated(int)));
+ }
+ else {
+ sub->insertItem(i18n("Went Offline: ") + it.current()->offline().toString("hh:mm"));
+ if( ! it.current()->online().isNull() ){
+ sub->insertItem(i18n("Last Online: ") + it.current()->online().toString("hh:mm"));
+ }
+
+ intoPopupSorted(it.current()->server() +" -> " + it.current()->nick() + i18n(" offline"), sub);
+ }
+ }
+ if(m_blink_reason.count() > 0){
+ mainPop->insertSeparator();
+ for ( QStringList::Iterator it = m_blink_reason.begin(); it != m_blink_reason.end(); ++it ) {
+ mainPop->insertItem(*it);
+ }
+ }
+ if(mainPop->count() == 0){
+ mainPop->insertItem(i18n("Help on Notify Popup..."), this, SLOT(helpNotice()));
+ mainPop->insertItem(i18n("Configure Notify..."), this, SLOT(configNotify()));
+ }
+}
+
+void dockServerController::subItemActivated(int sub_id)
+{
+ const QPopupMenu *sub = dynamic_cast<const QPopupMenu *>(sender());
+ if(!sub){
+ kdDebug(5008) << "subItemActivated by non QPopupMenu" << endl;
+ return;
+ }
+ int main_id = sub->itemParameter(sub_id);
+ QString mainText = mainPop->text(main_id);
+ if(mainText.isNull()){
+ kdDebug(5008) << "failed to get mainPop text" << endl;
+ }
+ QRegExp rx("(\\S+) -> (\\S+) ");
+ if(rx.search(mainText) >= 0){
+ QString mserver = rx.cap(1);
+ QString mnick = rx.cap(2);
+
+ QString ns = mserver + "/" + mnick;
+
+ if(m_nicks[ns]){
+ kdDebug(5008) << "Got " << sub->text(sub_id) << " request for " << ns << endl;
+ QString cmd = sub->text(sub_id);
+ QString server = m_nicks[ns]->server();
+ QString nick = m_nicks[ns]->nick();
+
+ if(cmd == i18n("Chat")){
+ servercontroller::self()->new_toplevel(KSircChannel(server, nick));
+ }
+ else {
+ KSircProcess *kp = servercontroller::self()->processes().find(server);
+ if(kp){
+ KSircTopLevel *wm = dynamic_cast<KSircTopLevel *>(kp->getWindowList().find("!default"));
+ if(wm){
+ if(cmd == i18n("Ping")){
+ wm->sirc_line_return("/ping " + nick + "\n");
+ } else if(cmd == i18n("Whois")){
+ wm->sirc_line_return("/whois " + nick + "\n");
+ } else if(cmd == i18n("DCC Chat")){
+ wm->sirc_line_return("/dcc chat " + nick + "\n");
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
+
+void dockServerController::mousePressEvent( QMouseEvent *e )
+{
+ if(e->button() == LeftButton){
+ showPopupMenu(mainPop);
+ }
+ else {
+ KSystemTray::mousePressEvent(e);
+ }
+}
+
+void dockServerController::showPopupMenu( QPopupMenu *menu )
+{
+ Q_ASSERT( menu != 0L );
+
+ menu->popup(QCursor::pos());
+
+}
+
+void dockServerController::nickOnline(QString server, QString nick)
+{
+ QString sn = server + "/" + nick;
+ if(!m_nicks[sn])
+ m_nicks.insert(sn, new dscNickInfo(nick, server));
+ m_nicks[sn]->setOnline();
+ createMainPopup();
+}
+
+void dockServerController::nickOffline(QString server, QString nick)
+{
+ QString sn = server + "/" + nick;
+ if(!m_nicks[sn])
+ m_nicks.insert(sn, new dscNickInfo(nick, server));
+ m_nicks[sn]->setOffline();
+ createMainPopup();
+}
+
+void dockServerController::serverOpen(QString)
+{
+}
+
+void dockServerController::serverClose(QString server)
+{
+ QDictIterator<dscNickInfo> it( m_nicks );
+ while(it.current()){
+ if(it.current()->server() == server)
+ m_nicks.remove(it.currentKey());
+ else
+ ++it;
+ }
+ createMainPopup();
+}
+
+void dockServerController::startBlink(const QString& reason, const QString& text)
+{
+ if(m_blinkActive == false){
+ // change icon (to "someone's talking to you" icon)
+ setPixmap( m_pic_info );
+ m_blinkStatus = true;
+ m_blinkActive = true;
+ m_blinkTimer->start( 500 ); // half a second
+ }
+ if(!reason.isNull()){
+ QString br(reason);
+ if(text.isNull() && ksopts->runDocked && ksopts->dockPopups)
+ KPassivePopup::message(QString("Notify: %1").arg(reason), this);
+ else {
+ QStringList sl;
+ QString cutup = text;
+ int i = 0;
+ br = reason + " " + text.left(50);
+ if(text.length() > 50)
+ br.append("...");
+ while(!cutup.isEmpty() && i++ < 3){
+ sl.append(cutup.left(50));
+ cutup.remove(0, 50);
+ }
+ cutup = sl.join("\n");
+ cutup.truncate(cutup.length()-1);
+ if(ksopts->runDocked && ksopts->dockPopups)
+ KPassivePopup::message(QString("%1").arg(reason), cutup, this);
+ }
+ m_blink_reason.append(br);
+ createMainPopup();
+ kdDebug(5008) << "Blink reason: " << br << endl;
+
+ QRegExp rx("(\\S+) -> (\\S+)");
+ if(rx.search(reason) >= 0){
+ QString server = rx.cap(1);
+ QString nick = rx.cap(2);
+
+ m_last_server = server;
+ m_last_nick = nick;
+ }
+ }
+}
+
+void dockServerController::stopBlink(const QString& reason, bool clear)
+{
+ if(clear == true){
+ m_blinkActive = false;
+ m_blinkTimer->stop();
+ setPixmap( m_pic_dock );
+ m_blink_reason.clear();
+ }
+ else {
+ if(!reason.isNull())
+ m_blink_reason.remove(reason);
+ }
+ createMainPopup();
+}
+
+void dockServerController::blinkClear()
+{
+ m_sc->resetNotification();
+}
+
+void dockServerController::mainActivated(int id)
+{
+ QRegExp rx("(\\S+) -> (\\S+) ");
+ if(rx.search(mainPop->text(id)) >= 0){
+ QString server = rx.cap(1);
+ QString nick = rx.cap(2);
+
+ raiseWindow(server, nick);
+
+ }
+}
+
+void dockServerController::raiseLastActiveWindow()
+{
+ if((!m_last_server.isNull()) && (!m_last_nick.isNull()))
+ raiseWindow(m_last_server, m_last_nick);
+}
+
+void dockServerController::blinkDockedIcon()
+{
+ m_blinkStatus = !m_blinkStatus;
+ setPixmap( m_blinkStatus ? m_pic_info : m_pic_dock );
+}
+
+void dockServerController::helpNotice()
+{
+ KMessageBox::information(0x0,
+ i18n("This popup menu can show a list of "
+ "people you have in your notify "
+ "list, and their status. You can configure "
+ "this list by going to Configure KSirc->"
+ "Startup->Notify and adding people to the "
+ "list. This will take effect the next "
+ "time you connect to a server. "
+ "This message appears when "
+ "there is nothing in your notify "
+ "list or when no one in your "
+ "list is online."
+ ),
+ i18n("Help for Notification Popup"));
+
+}
+
+void dockServerController::configNotify()
+{
+ KSPrefs *kp = new KSPrefs();
+ connect(kp, SIGNAL(update(int)),
+ m_sc, SLOT(configChange()));
+ kp->resize(550, 450);
+ kp->showPage(2); /* Show auto connect page */
+ kp->show();
+
+}
+
+
+void dockServerController::raiseWindow(QString server, QString name)
+{
+ QCString txt = server.utf8() + "_" + name.utf8() + "_toplevel";
+ QWidget *obj = dynamic_cast<QWidget *>( objFinder::find(txt, "KSircTopLevel"));
+ if(obj == 0x0){
+ txt = server.utf8() + "_!" + name.utf8() + "_toplevel";
+ obj = dynamic_cast<QWidget *>( objFinder::find(txt, "KSircTopLevel"));
+ }
+
+ if(obj != 0x0){
+ displayMgr->raise(obj, true);
+ }
+ else {
+ kdWarning() << "Did not find widget ptr to raise it" << endl;
+ }
+}
+
+#include "dockservercontroller.moc"
+
diff --git a/ksirc/dockservercontroller.h b/ksirc/dockservercontroller.h
new file mode 100644
index 00000000..5c5bdb59
--- /dev/null
+++ b/ksirc/dockservercontroller.h
@@ -0,0 +1,110 @@
+
+#ifndef dockservercontroller_included
+#define dockservercontroller_included
+
+#include <ksystemtray.h>
+#include <qdict.h>
+#include <qstring.h>
+#include <qdatetime.h>
+#include <qpixmap.h>
+#include <qstringlist.h>
+
+class QPopupMenu;
+class QWidget;
+class servercontroller;
+class QMouseEvent;
+class KPopupMenu;
+class QTimer;
+
+class dscNickInfo
+{
+public:
+ dscNickInfo(QString nick, QString server);
+ ~dscNickInfo();
+
+ enum status {
+ isOnline,
+ isOffline
+ };
+
+ QString nick() { return m_nick; }
+ QString server() { return m_server; }
+
+ void setOnline() { m_status = isOnline; m_online = QTime::currentTime(); }
+ const QTime &online() { return m_online; }
+
+ void setOffline() { m_status = isOffline; m_offline = QTime::currentTime(); }
+ const QTime offline() { return m_offline; }
+
+ void setStatus(enum status stat) { m_status = stat; }
+ enum status status() { return m_status; }
+
+private:
+ QString m_nick;
+ QString m_server;
+ enum status m_status;
+ QTime m_online;
+ QTime m_offline;
+
+};
+
+class dockServerController : public KSystemTray
+{
+ Q_OBJECT
+public:
+ dockServerController(servercontroller *_sc, QWidget *parent = 0x0, const char *_name = 0x0);
+ ~dockServerController();
+
+ void startBlink(const QString& reason = QString::null, const QString& text = QString::null);
+ void stopBlink(const QString& reason = QString::null, bool clear = false);
+
+ void nickOnline(QString server, QString nick);
+ void nickOffline(QString server, QString nick);
+
+ void serverOpen(QString server);
+ void serverClose(QString server);
+
+protected:
+ void mousePressEvent( QMouseEvent *);
+ virtual void showPopupMenu( QPopupMenu *);
+
+public slots:
+ void raiseLastActiveWindow();
+
+protected slots:
+ void subItemActivated(int);
+ void blinkDockedIcon(void);
+ void blinkClear(void);
+ void mainActivated(int id);
+ void helpNotice();
+ void configNotify();
+
+ void mainPopShow();
+ void mainPopHide();
+
+private:
+ int intoPopupSorted(QString, QPopupMenu *);
+ void createMainPopup();
+ void raiseWindow(QString server, QString name);
+ QDict<dscNickInfo> m_nicks;
+ servercontroller *m_sc;
+ KPopupMenu *mainPop;
+
+ QString m_last_nick;
+ QString m_last_server;
+
+ QPixmap m_pic_dock;
+ QPixmap m_pic_info;
+
+ bool m_mainPopVisible;
+
+ bool m_blinkActive;
+ bool m_blinkStatus; // true: blue icon, false: normal icon
+ QTimer* m_blinkTimer;
+
+ QStringList m_blink_reason;
+
+};
+
+#endif
+
diff --git a/ksirc/dsirc b/ksirc/dsirc
new file mode 100755
index 00000000..c6e0b63c
--- /dev/null
+++ b/ksirc/dsirc
@@ -0,0 +1,2721 @@
+#!/usr/bin/perl
+
+# dsirc: dumb-mode small irc client in perl
+# by orabidoo <roger.espel.llima@pobox.com>
+#
+# Copyright (C) 1995-1997 Roger Espel Llima
+#
+# for a full-screen termcap interface, use this with ssfe
+#
+# use: dsirc [options] [nick [server[:port[:password]]]]
+# options are:
+# -p = specify port number
+# -i = specify IRCNAME
+# -n = specify nickname (quite useless as an option)
+# -s = specify server (quite useless as an option)
+# -l = specify file to be loaded instead of ~/.sircrc.pl
+# -L = specify file to be loaded instead of ~/.sircrc
+# -H = specify virtual host to bind to
+# -q = don't load ~/.sircrc or ~/.sircrc.pl
+# -Q = don't load system sircrc or sircrc.pl
+# -R = run in restricted (secure) mode
+# -r = raw mode (no control-char filtering)
+# -8 = 8-bit mode
+# -S = connect using SSL
+
+# 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. See the file LICENSE for more details.
+#
+# If you make improvements to sirc, please send me the modifications
+# (context diffs appreciated) and they might make it to the next release.
+#
+# For bug reports, comments, questions, email roger.espel.llima@pobox.com
+#
+# You can always find the latest version of sirc at the following URL:
+# http://www.eleves.ens.fr:8080/home/espel/sirc/sirc.html
+
+# Concerning the use in ksirc you'll find a mail from the author below:
+#
+# Subject: Re: dsirc in kde
+# Date: Thu, 7 Sep 2000 13:16:30 -0400
+# From: Roger Espel Llima <espel@iagora.net>
+# To: Harri Porten <porten@kde.org>
+#
+# On Thu, Sep 07, 2000 at 07:12:33PM +0200, Harri Porten wrote:
+# [....]
+# > Ok. Your dsirc script is used in ksirc. I haven't checked how it is
+# > invoked and what legal ramifications that would have licensing wise but
+# > I would like to "officially" ask you anyway:
+# >
+# > Do you have oppose to your code being used this way in the past and in
+# > the future ? Do you "forgive" us [for use in prev. versions of KDE] ? :)
+#
+# I "officially" find it perfectly fine that dsirc is used in KDE. I knew
+# of ksirc when it started, and found it very flattering that someone
+# would write 200k of C++ to interface with my 62k of perl :=)
+
+$version='2.211';
+$date='10 Mar 1998';
+$add_ons='';
+
+$libdir=$ENV{"SIRCLIB"} || ".";
+push(@INC, $libdir, $ENV{"HOME"});
+@loadpath=($ENV{"HOME"}."/.sirc", $libdir, ".");
+$ENV{"SIRCWAIT"} or $ready=1;
+
+$|=1;
+
+$publicAway = 1;
+
+if (!eval "require 'getopts.pl';") {
+ print "\n\n\
+Your perl interpreter is *really* screwed up: the getopts.pl library is not
+even there! Have you even bothered to run 'install'?\n";
+ exit;
+}
+
+if ($] >= 5 && (eval "use Socket;", $@ eq '')) {
+ $sock6 = eval ("require Socket6;") and eval("use Socket6;");
+} elsif (-f "$libdir/sircsock.ph") {
+ do "$libdir/sircsock.ph";
+} elsif (-f $ENV{'HOME'}."/sircsock.ph") {
+ do $ENV{'HOME'}."/sircsock.ph";
+} elsif (!eval "require 'sys/socket.ph';") {
+ print "\n\n\
+Your perl installation is wrong somewhere, the sys/socket.ph include file
+couldn't be found. Have you even bothered to run 'install'?\n";
+ exit;
+}
+
+$hasPOSIX = 1;
+eval "use POSIX;";
+if($@) {
+ $hasPOSIX = 0;
+ print "*** No Posix library, falling back to blocking IO (dcc will suck)\n";
+}
+
+
+&Getopts('n:s:p:u:i:l:L:H:rqQR78S');
+
+%set=("LOGFILE", "", "LOG", "off", "PRINTUH", "none", "PRINTCHAN", "off",
+ "LOCALHOST", "", "CTCP", "noflood", "SENDAHEAD", 4096,
+ "USERINFO", "", "FINGER", "", "IRCNAME", "", "EIGHT_BIT", "on",
+ "LOADPATH", join(":", @loadpath), "CTRL_T", "/next");
+
+$raw_mode=$opt_r || (!-t STDOUT);
+$ansi=!$raw_mode && $ENV{"TERM"} =~ /^vt|^xterm|^ansi/i;
+$server=$opt_s || $ARGV[1] || $ENV{"SIRCSERVER"} || $ENV{"IRCSERVER"} ||
+ "irc.primenet.com";
+$port0=$opt_p || $ENV{"SIRCPORT"} || $ENV{"IRCPORT"} || 6667;
+$username=$opt_u || $ENV{"SIRCUSER"} || $ENV{"IRCUSER"} || (getpwuid($<))[0] ||
+ $ENV{"USER"} || "blah";
+$set{"IRCNAME"}=$opt_i || $ENV{"SIRCNAME"} || $ENV{"IRCNAME"} || "sirc user";
+$nick=$opt_n || $ARGV[0] || $ENV{"SIRCNICK"} || $ENV{"IRCNICK"} || $username;
+$set{"FINGER"}=$ENV{"IRCFINGER"} || "keep your fingers to yourself";
+$set{"USERINFO"}=$ENV{"USERINFO"} || "yep, I'm a user";
+if ($server =~ /^\[([^\]]+)\]:([0-9]*):?([^:]*)$/
+ or $server =~ /^([^:]+):([0-9]*):?([^:]*)$/)
+{
+ ($server, $port, $pass)=($1, $2, $3);
+}
+$port || ($port=$port0);
+$server0=$server1=$server;
+$port0=$port1=$port;
+$pass0=$pass1=$pass;
+$initfile=$opt_l || $ENV{"SIRCRCPL"} || $ENV{'HOME'}."/.sircrc.pl"
+ if $opt_l || !$opt_q;
+$sysinit=$libdir."/sircrc.pl" if $libdir ne '.' && !$opt_Q;
+$rcfile=$opt_L || $ENV{"SIRCRC"} || $ENV{'HOME'}."/.sircrc"
+ if $opt_L || !$opt_q;
+$sysrc=$libdir."/sircrc" if $libdir ne '.' && !$opt_Q;
+$set{"LOGFILE"}=$logfile=$ENV{'HOME'}."/sirc.log";
+$opt_8 || ($set{"EIGHT_BIT"}="off");
+$restrict=$opt_R;
+$set{"LOCALHOST"}=$opt_H || $ENV{"SIRCHOST"} || $ENV{"IRCHOST"} ||
+ $ENV{"LOCALHOST"} || "";
+$SSL=$opt_S;
+
+@ARGV=(); # ignore any more arguments
+
+if (open(H, "$libdir/sirc.help") || ((-f "$libdir/sirc.help.gz") &&
+ open(H, "gzip -cd $libdir/sirc.help.gz |"))) {
+ @help=<H>;
+ close H;
+ foreach (@help) {
+ chop;
+ s/\$version/$version/g;
+ s/\$date/$date/g;
+ }
+} else {
+ print "*** Warning: help file ($libdir/sirc.help) not found!\n";
+}
+$floodtimer=0;
+
+sub exit {
+ &dohooks("quit");
+ &sl("QUIT :using sirc version $version$add_ons") if $connected;
+ close LOG if $logging;
+ exit 0;
+}
+
+$SIG{'PIPE'}='IGNORE';
+$SIG{'QUIT'}='IGNORE';
+$SIG{'INT'}='exit';
+$SIG{'TERM'}='exit'; # KSIRC MOD
+
+sub eq {
+ local($a, $b)=@_;
+ $a =~ tr/A-Z/a-z/;
+ $b =~ tr/A-Z/a-z/;
+ return ($a eq $b);
+}
+
+sub tilde {
+ $_[0] =~ s|^\~(\w+)|(getpwnam($1))[7]|e;
+ $_[0] =~ s/^\~/$ENV{'HOME'}/;
+ $_[0]="." if $_[0] eq '';
+}
+
+sub sigquit {
+ # really ugly hack, but it works...
+ &dohooks("quit");
+ close($trysock);
+}
+
+sub resolve {
+ if ($sock6) {
+ my $addr = $_[0];
+ if ("$addr" =~ /^\d+$/)
+ {
+ $addr = pack("N", $addr);
+ my @i = unpack("C4", $addr);
+ $addr = "$i[0].$i[1].$i[2].$i[3]";
+ }
+ return getaddrinfo($addr, $_[1], $_[2] || &AF_UNSPEC, &SOCK_STREAM);
+ }
+ my $addr;
+ if ($_[0] =~ /^\d+$/) {
+ $addr = pack("N", $_[0]+0);
+ } elsif ($_[0] =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) {
+ $addr = pack("c4", $1, $2, $3, $4);
+ } else {
+ $addr=(gethostbyname($_[0]))[4];
+ return -1 unless (defined($addr));
+ }
+ return (&AF_INET, &SOCK_STREAM, 0, pack_sockaddr_in($_[1], $addr), undef);
+}
+
+$nextfh="sircblah000";
+sub newfh {
+ return ++$nextfh;
+}
+
+sub connect {
+ $_[0]=&newfh;
+ local($fh, $host, $port)=@_;
+ my @res = resolve($host, $port);
+ &tell("*\cbE\cb* Hostname `$host' not found"), return -1 if scalar(@res) < 5;
+ $family = -1;
+ my $bindfailed;
+ while (scalar(@res) >= 5) {
+ ($family, my ($socktype, $proto, $addr), undef, @res) = @res;
+ &print("*\cbE\cb* Out of file descriptors: $!"), return -2
+ unless socket($fh, $family, $socktype, $proto);
+
+ $bindfailed = undef;
+ if ($set{"LOCALHOST"}) {
+ # once again, DCC only does ipv4
+ $bindaddr = (&resolve($set{"LOCALHOST"}, 0, &AF_INET))[3];
+ $bindfailed = 1 unless bind($fh, $bindaddr);
+ }
+
+ $trysock=$fh;
+ $SIG{'QUIT'}='sigquit';
+ $SIG{'QUIT'}='IGNORE', last if connect($fh, $addr);
+ $SIG{'QUIT'}='IGNORE';
+ $family = -1;
+ }
+ &print("*\cbE\cb* Can't connect to host: $!"), return -3 if $family == -1;
+ # Tried to just check for $family != &AF_INET where needed, but
+ # that segfaulted perl (!), guess it's a bug in Socket6.pm, but I won't try
+ # to debug that. (malte)
+ $ipv6 = 1 if ($sock6 && $family == &AF_INET6);
+ &tell("*\cbE\cb* Warning: can't bind to sirc host: ".$set{'LOCALHOST'})
+ if $bindfailed;
+
+ if ($ipv6 != 1)
+ {
+ $bindaddr=getsockname($fh) unless $bindaddr;
+ }
+ select($fh); $|=1; select(STDOUT);
+ return 1;
+}
+
+sub connectSSL {
+ eval "use IO::Socket::SSL;";
+
+ if($@){
+ &tell("Can't load SSL socket library, perl does not support SSL!");
+ &tell("To use SSL you must install the IO::Socket::SSL perl library");
+ &tell("Try as root: perl -MCPAN -e 'install IO::Socket::SSL'");
+ &tell("Giving up connect");
+ return 0;
+ }
+ local($fh, $host, $port)=@_;
+ &tell("*** Doing SSL server connect...");
+ $fh = new IO::Socket::SSL("$host:$port");
+ if(defined $fh){
+ $_[0] = $fh;
+ select($fh); $|=1; select(STDOUT);
+ return 1;
+ }
+ else {
+ warn "*** I encountered a problem: ($!) ",
+ &IO::Socket::SSL::errstr();
+ warn "*** Invalid hostname or port?\n";
+ return -1;
+ }
+}
+
+sub sel_nbconnecthandler {
+ local($fh) = $_[0];
+ &remwsel($fh);
+ $!="";
+ my $res = unpack("i", getsockopt("$fh", SOL_SOCKET(), SO_ERROR()) || die "Failed to get sockopt: $!");
+ select($fh); $|=1; select(STDOUT);
+ &{$nbconnectlist{$fh}{"callback"}}($fh, $res);
+ $nbconnectlist{$fh} = undef;
+}
+
+#
+# Non blocking connect
+# arguments are: filehandle(returned), host, port, callback function.
+#
+
+sub connectnb {
+ if($hasPOSIX == 0){
+ my $cb = $_[3];
+ $_[3] = undef;
+ my $ret = &connect(@_);
+ if($ret == 1){
+ &$cb($_[0], 0);
+ }
+ else {
+ &$cb($_[0], -1);
+ }
+ return $ret;
+
+ }
+ $_[0]=&newfh;
+ local($fh, $host, $port, $callback)=@_;
+ my @res = resolve($host, $port);
+ &tell("*\cbE\cb* Hostname `$host' not found"), return -1 if scalar(@res) < 5;
+
+ while (scalar(@res) >= 5) {
+ ($family, my ($socktype, $proto, $addr), undef, @res) = @res;
+ &print("*\cbE\cb* Out of file descriptors: $!"), return -2
+ unless socket($fh, $family, $socktype, $proto);
+
+ fcntl($fh, F_SETFL(), O_NONBLOCK());
+ &addwsel($fh, "nbconnecthandler", 0);
+ if(connect($fh, $addr)){
+ &$callback($fh, 0);
+ }
+ else {
+ if($! == EINPROGRESS()){
+ $nbconnectlist{$fh}{"callback"} = $callback;
+ }
+ else {
+ &print("*\cbI\cb* got other error $!");
+ return -1;
+ }
+ }
+ }
+ return 1;
+}
+
+sub listen {
+ $_[0]=&newfh;
+ local($fh, $port)=@_;
+ local($thisend);
+
+ &tell("\cbE\cb* first set your ipv4 hostname with /set LOCALHOST <hostname>"), return 0
+ unless (length $bindaddr);
+
+
+# XXX: don't use ipv6 for the time being as ipv6 and dcc don't mix
+# if ($ipv6) {
+ # XXX: substr() hack to avoid problems on some Linux systems
+# (undef, my $addr) = unpack_sockaddr_in6(substr($bindaddr, 0, 24));
+# $thisend = pack_sockaddr_in6($port, $addr);
+# } else {
+ (undef, my $addr) = unpack_sockaddr_in($bindaddr);
+ $thisend = pack_sockaddr_in($port, $addr);
+# }
+ &tell("*\cbE\cb* Out of file descriptors"), return 0
+ unless socket($fh, &AF_INET, &SOCK_STREAM, 0);
+ &tell("*\cbE\cb* Can't bind local socket!"), close $fh, return 0
+ unless bind($fh, $thisend);
+ &tell("*\cbE\cb* Can't listen to socket!"), close $fh, return
+ unless listen($fh, 5);
+ $ipv6=0;
+ return getsockname($fh);
+}
+
+sub accept {
+ $_[0]=&newfh;
+ return (accept($_[0], $_[1]), close($_[1]))[0];
+}
+
+sub bindtoserver {
+ @channels=(); $talkchannel='';
+ %mode=(); $umode=''; %limit=(); %haveops=(); %chankey=(); $away='';
+ $listmin=0; $listmax=100000; $listpat='';
+ @waituh=(); @douh=(); @erruh=(); $invited='';
+ &dostatus;
+ &tell("*** Connecting to $server, port $port...");
+ if($SSL == 1){
+ sleep 10, &bindtoserver if &connectSSL($S, $server, $port) < 0;
+ } else {
+ sleep 10, &bindtoserver if &connect($S, $server, $port) < 0;
+ }
+ $connected=1;
+ $server1=$server;
+ $port1=$port;
+ $pass1=$pass;
+ &sl("PASS $pass") if $pass;
+ &sl("USER $username blah blah :".$set{'IRCNAME'});
+ &sl("NICK $nick");
+ @channels=(); $talkchannel=''; %mode=(); $umode=''; %limit=();
+ %haveops=(); %chankey=();
+}
+
+sub gl {
+ if ($buffer{$_[0]} =~ /^([^\n\r]*)\r?\n\r?/) {
+ $buffer{$_[0]}=$';
+ $_=$1."\n";
+ return 1;
+ }
+ local($buf)='';
+ # &tell("About to sysread: $_[0]");
+ if (sysread($_[0], $buf, 4096)) {
+ $buffer{$_[0]}.=$buf;
+ if ($buffer{$_[0]} =~ /^([^\n\r]*)\r?\n\r?/) {
+ $buffer{$_[0]}=$';
+ $_=$1."\n";
+ return 1;
+ }
+ return '';
+ }
+ $_='';
+ return 1;
+}
+
+sub sl {
+ $logging && print LOG "<<".$_[0]."\n";
+ if(!print $S $_[0]."\n"){
+ &print("*\cbE\cb* Error writing to server: $!");
+ &tell("*\cbE\cb* Connection to server lost");
+ close($S);
+ delete $buffer{$S};
+ $connected=0;
+ &dohooks("disconnect");
+ &bindtoserver;
+ }
+ elsif (time-$floodtimer < 1){
+ select(undef, undef, undef, 0.5);
+ }
+ $floodtimer=time;
+}
+
+sub dostatus {
+ return unless $ssfe;
+ local($t, $s)=($talkchannel, " [sirc] ");
+ my($i);
+ for($i=0; $i<=$#channels; $i++){
+ $s = " [sirc] ";
+ $t = $channels[$i];
+ $t =~ tr/A-Z/a-z/;
+ $s.="*" if $umode =~ /o/;
+ $s.="\@" if $t && $haveops{$t};
+ $s.=$nick;
+ $s.=" (+$umode)" if $umode;
+ $s.=" [query: ${query}]" if $query;
+ $s.=" (away)" if $away;
+ if ($talkchannel ne '') {
+ $s.=" on $t (+$mode{$t})";
+ $s.=" <key: $chankey{$t}>" if $chankey{$t};
+ $s.=" <limit: $limit{$t}>" if $limit{$t};
+ }
+ &dohooks("status", $s);
+# $laststatus=$s, print "~${t}~`#ssfe#s$s\n" if $laststatus ne $s;
+ $laststatus=$s;
+ $logging && print LOG "** ~${t}~`#ssfe#s$s\n";
+ print "~${t}~`#ssfe#s$s\n";
+ }
+}
+
+$bold="\c[[1m";
+$underline="\c[[4m";
+$reverse="\c[[7m";
+$normal="\c[[m";
+$cls="\c[[H\c[[2J";
+
+sub enhance {
+ local($what)=@_;
+ $what =~ tr/\c@-\c^/@-^/;
+ return "\cv${what}\cv";
+}
+
+sub print {
+ local($skip, $what)=(0, @_);
+ &dohooks("print", $what);
+ return if $skip;
+ $what =~ s/\s+$//;
+ # thanks to Toy (wacren@obspm.fr) for this translation
+ $what =~ tr/\x80-\xff/\x00-\x1f !cLxY|$_ca<\-\-R_o+23\'mp.,1o>123?AAAAAAACEEEEIIIIDNOOOOO*0UUUUYPBaaaaaaaceeeeiiiidnooooo:0uuuuypy/
+ if $set{"EIGHT_BIT"} ne 'on';
+ $logging && print LOG "-> " . $what."\n";
+ if ($raw_mode) {
+ print $what, "\n" || &exit;
+ } elsif ($ansi) {
+ # this is buggy if you combine effects
+ $what =~ s/([\ca\cc-\ch\cj-\cu\cw-\c^])/&enhance($1)/eg;
+ while ($what =~ /\cb/) {
+ ($what =~ s/\cb([^\cb]*)\cb/$bold$1$normal/) ||
+ $what =~ s/\cb/$bold/g;
+ }
+ while ($what =~ /\c_/) {
+ ($what =~ s/\c_([^\c_]*)\c_/$underline$1$normal/) ||
+ $what =~ s/\c_/$underline/g;
+ }
+ while ($what =~ /\cv/) {
+ ($what =~ s/\cv([^\cv]*)\cv/$reverse$1$normal/) ||
+ $what =~ s/\cv/$reverse/g;
+ }
+ print $what, $normal, "\n" || &exit;
+ } else {
+ $what =~ tr/\ca-\ch\cj-\c_//d;
+ print $what, "\n" || &exit;
+ }
+}
+
+sub tell {
+ $silent || &print;
+}
+
+sub dohooks {
+ $hooktype=shift;
+ local(@hl);
+ eval "\@hl=\@${hooktype}_hooks;";
+ foreach $h (@hl) {
+ eval { &$h(@_); };
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in $hooktype hook &$h: $@")
+ if $@ ne '';
+ }
+}
+
+sub dcerror {
+ local($fh, $n)=($_[0], $dcnick{$_[0]});
+ &dohooks("chat_disconnect", $n);
+ &tell("*\cbE\cb* DCC chat with $n lost");
+ &tell("~!dcc~Closing DCC CHAT with who: $n");
+ close($fh);
+ $n =~ tr/A-Z/a-z/;
+ delete $dcnick{$fh};
+ delete $dcvol{$n};
+ delete $dcfh{$n};
+ delete $buffer{$fh};
+}
+
+sub dgsclose {
+ local($sfh, $rfh, $type, $err)=@_;
+ &dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$rfh}, $dtransferred{$sfh},
+ time-$dstarttime{$rfh}, $rfh);
+ &tell("*\cbD\cb* DCC $type with $dnick{$sfh} ($dfile{$rfh}) terminated; $dtransferred{$sfh} bytes transferred in ".(time-$dstarttime{$rfh}). " seconds");
+ &tell("~!dcc~DCC $type terminated who: $dnick{$sfh} file: $dfile{$rfh} reason: $err");
+ close($sfh);
+ close($rfh);
+ delete $dgrfh{$sfh};
+ delete $dsrfh{$sfh};
+ delete $dfile{$rfh};
+ delete $dstarttime{$rfh};
+ delete $dtransferred{$sfh};
+ delete $dsoffset{$sfh};
+ delete $dsport{$sfh};
+ delete $dsresumedb{$sfh};
+ delete $dgxferadd{$sfh};
+ delete $dnick{$sfh};
+}
+
+sub msg {
+ local($towho, $what)=@_;
+ print "`#ssfe#t/m $towho \n" if $ssfe && !&eq($towho, $talkchannel);
+ if ($towho =~ s/^=//) {
+ local($n, $fh)=($towho);
+ $n =~ tr/A-Z/a-z/;
+ $fh=$dcfh{$n};
+ if ($fh) {
+ (print $fh $what."\n") || &dcerror($fh);
+ $dcvol{$n}+=length($what);
+ &dohooks("send_dcc_chat", $towho, $what);
+ &tell("~=${towho}~|\cb$towho\cb| $what"); #KSIRC MOD
+ } else {
+ &tell("*\cbE\cb* No active DCC chat with $towho");
+ }
+ } elsif ($connected>1) {
+ $what=substr($what, 0, 485);
+ &dohooks("send_text", $towho, $what);
+ if (&eq($towho, $talkchannel) && !$printchan) {
+ &tell("~${towho}~<${nick}> $what"); # KSIRC MOD
+ } elsif ($towho =~ /^[\&\#\+]/) {
+ &tell("~${towho}~<$nick> $what"); #KSIRC MOD
+ } else {
+ &tell("~${towho}~>${nick}< $what"); #KSIRC MOD
+ }
+ &sl("PRIVMSG $towho :$what");
+ } else {
+ &tell("*** You're not connected to a server");
+ }
+}
+
+sub say {
+ if ($query)
+ {
+ &msg($query, @_);
+ }
+ elsif ($talkchannel) {
+ &msg($talkchannel, @_);
+ } else {
+ &tell("*\cbE\cb* Not on a channel");
+ }
+}
+
+sub notice {
+ local($towho, $what)=@_;
+ $what=substr($what, 0, 485);
+ &dohooks("send_notice", $towho, $what);
+ &tell("~${towho}~-> -~n${towho}~n- $what");
+ &sl("NOTICE $towho :$what");
+}
+
+sub describe {
+ local($towho, $what)=@_;
+ $what=substr($what, 0, 480);
+ &dohooks("send_action", $towho, $what);
+ if (&eq($towho, $talkchannel) && !$printchan) {
+ &tell("~${towho}~* $nick $what"); # KSIRC MOD
+ } elsif ($towho =~ /^[\#\&\+]/) {
+ &tell("~${towho}~* $nick $what"); # KSIRC MOD
+ } else {
+ &tell("~${towho}~* $nick $what"); #KSIRC MOD
+# &tell("~${towho}~*-> \cb${towho}\cb: $nick $what"); #KSIRC MOD
+ }
+ &sl("PRIVMSG $towho :\caACTION".($what eq "" ? "" : " ").$what."\ca");
+}
+
+sub me {
+ if ($talkchannel) {
+ &describe($talkchannel, @_);
+ } else {
+ &tell("*\cbE\cb* Not on a channel");
+ }
+}
+
+sub yetonearg {
+ ($newarg, $args)=split(/ +/, $args, 2);
+ $args =~ s/^://;
+}
+
+sub getarg {
+ ($newarg, $args)=split(/ +/, $args, 2);
+}
+
+@weekdays=("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
+@months=("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
+ "Nov", "Dec");
+
+sub date {
+ local($sec, $min, $hour, $mday, $mon, $year, $wday)=localtime($_[0]);
+ return sprintf("$weekdays[$wday] $months[$mon] $mday %.2d:%.2d:%.2d %d",
+ $hour, $min, $sec, $year+1900);
+}
+
+sub reply {
+ return if $set{"CTCP"} eq 'noreply';
+ if ($lastrep<time-10) {
+ $lastrep=time;
+ $nreps=1;
+ } else {
+ return if $nreps++>=2 && $set{"CTCP"} eq 'noflood';
+ }
+ &sl("NOTICE $who :\ca$_[0]\ca");
+}
+
+sub ctcp {
+ local($towho, $to, $what)=$_[0];
+ ($what, $args)=split(/ +/, $_[1], 2);
+ $what =~ tr/a-z/A-Z/;
+ &dohooks("ctcp", $towho, $what, $args);
+ return if $skip;
+ local($a)=$args;
+ $a && ($a=' '.$a);
+ $to = (&eq($towho, $nick) ? "you" : $towho);
+
+ &tell("~$to~*** $who$puh1 did a CTCP $what$a to $to")
+ unless $what =~ /^(ACTION|PING|DCC|VERSION)$/;
+ if ($what eq 'ACTION') {
+ &dohooks("action", $towho, $args);
+ if (&eq($towho, $nick)) {
+ &tell("~$who~* \cb${who}\cb$puh1 $args"); # KSIRC MOD
+ } elsif (&eq($towho, $talkchannel) && !$printchan) {
+ &tell("~$towho~* $who $args"); #KSIRC MOD
+ } else {
+ &tell("~$towho~* $who$puh2 $args"); #KSIRC MOD
+ }
+ } elsif ($what eq 'TIME') {
+ &reply("TIME ".&date(time));
+ } elsif ($what eq 'CLIENTINFO') {
+ &reply("CLIENTINFO ACTION, CLIENTINFO, DCC, ECHO, ERRMSG, FINGER, PING, TIME, USERINFO, VERSION");
+ } elsif ($what eq 'FINGER') {
+ &reply("FINGER ".$set{"FINGER"});
+ } elsif ($what eq 'USERINFO') {
+ &reply("USERINFO ".$set{"USERINFO"});
+ } elsif ($what eq 'VERSION') {
+ local($u)=$add_ons;
+ $u =~ s/^\+//;
+ $u =~ s/\+/ + /g;
+ $u=" -- using $u" if $u;
+ if($to eq 'you'){
+ &tell("~$who~*** $who$puh1 did a CTCP $what$a to $to")
+ }
+ else {
+ &tell("~$to~*** $who$puh1 did a CTCP $what$a to $to")
+ }
+ &reply("VERSION sirc $version, a \cbperl\cb client$u");
+ } elsif ($what eq 'PING') {
+ &reply("PING $args");
+ &tell("*** $who$puh1 did a CTCP PING to $to"); #KSIRC
+ } elsif ($what eq 'ECHO' || $what eq 'ERRMSG') {
+ &reply("$what $args");
+ } elsif ($what eq 'DCC') {
+ &getarg;
+ if ($newarg eq 'CHAT' || $newarg eq 'SEND' && !$restrict) {
+ local($dfile, $dhost, $dport, $dsize)=split(/ +/, $args, 4);
+ $dfile=$1 if $dfile =~ m|/([^/]*)$|;
+ $dfile =~ s/^\./_/;
+ if ($dhost==2130706433 || !$dport>1024 || $dhost !~ /^\d+$/ ||
+ $dport !~ /^\d+$/) {
+ &tell("*\cbE\cb* DCC $newarg ($dfile) from $who$puh1 rejected");
+ } elsif ($newarg eq 'CHAT' && grep (&eq($who, $dcwait{$_}),
+ keys(%dcwait))) {
+ &tell("*\cbD\cb* DCC chat already requested from $who, connecting...");
+ my ($wfh)=(grep(&eq($dcwait{$_}, $who), keys(%dcwait)));
+ my ($n, $fh)=$who;
+ delete $dcwait{$wfh};
+ close($wfh);
+ my $w = $who;
+ my $cb = sub {
+ my ($lfh, $lres) = @_;
+ if($lres != 0){
+ &tell("*\cbD\cb* DCC CHAT with $w failed: " . strerror($lres));
+ &tell("~!dcc~DCC CHAT failed who: $who reason: " . strerror($lres));
+ close($lfh);
+ return;
+ }
+ $dcnick{$lfh}=$w;
+ &tell("*\cbD\cb* DCC CHAT with $w established");
+ &tell("~!dcc~DCC CHAT established who: $w");
+ $n =~ tr/A-Z/a-z/;
+ $dcvol{$n}=0;
+ $dcfh{$n}=$lfh;
+ print "`#ssfe#t/m =$w \n" if $ssfe;
+ };
+ if(&connectnb($fh, $dhost, $dport, $cb) < 1) {
+ return;
+ }
+ } elsif ($newarg eq 'CHAT' && grep(&eq($who, $_), keys(%dcfh))) {
+ &tell("*\cbD\cb* DCC chat from $who$puh1 ignored (already established)");
+ } else {
+ #&tell("*\cbD\cb* DCC $newarg ($dfile) from $who$puh1 ".
+ # ($dsize ? "(size: $dsize) " : "")."[$dhost, $dport]");
+ my $ip = inet_ntoa(pack("N", $dhost));
+ if ($newarg eq 'CHAT') {
+ &tell("~!dcc~DCC CHAT OFFERED who: $who$puh1 ip: $ip port: $dport");
+ $dcoffered{$who}="$dhost $dport";
+ &dohooks("dcc_request", "CHAT", $dhost, $dport);
+ } else {
+ my $index = 1; # KSIRC MOD - Make the file name unique
+ UNIQ: {
+ foreach $i (keys(%dgoffered)) {
+ my($h, $p, $f) = split(/ /, $i);
+ if (&eq($f, $dfile)) {
+ $dfile =~ s/(.*)\.\d+$/$1/;
+ $dfile .= ".$index";
+ $index++;
+ redo UNIQ;
+ }
+ }
+ }
+ &tell("~!dcc~INBOUND DCC SEND who: $who$puh1 file: $dfile size: $dsize ip: $ip port: $dport");
+
+ $dgoffered{"$dhost $dport $dfile"}=$who;
+ &dohooks("dcc_request", "SEND", $dhost, $dport, $dfile, $dsize);
+ }
+ }
+ } else {
+ &tell("*** $who$puh1 did a CTCP ${what}$a to $to");
+ }
+ }
+}
+
+sub doset {
+ local($var, $val)=@_;
+ $var =~ tr/a-z/A-Z/;
+ $val="" unless defined($val);
+ if ($var eq 'PRINTUH') {
+ $set{$var}="all" if $val =~ /^(on|all)$/i;
+ $set{$var}="some" if $val =~ /^some$/i;
+ $set{$var}="none" if $val =~ /^(off|none)$/i;
+ } elsif ($var eq 'PRINTCHAN') {
+ $set{$var}="on", $printchan=1 if $val =~ /^on$/i;
+ $set{$var}="off", $printchan=0 if $val =~ /^off$/i;
+ } elsif ($var eq 'CTCP') {
+ $val =~ tr/A-Z/a-z/;
+ $set{$var}=$val if $val =~ /^(none|all)$/;
+ $set{$var}="noreply" if $val =~ /^(noreply|off)$/;
+ $set{$var}="noflood" if $val =~ /^(noflood|on)$/;
+ } elsif ($var eq 'SENDAHEAD') {
+ $set{$var}=$val if $val =~ /^\d+$/ && $val<=65536;
+ } elsif ($var eq 'USERINFO') {
+ $set{$var}=$val;
+ } elsif ($var eq 'FINGER') {
+ $set{$var}=$val;
+ } elsif ($var eq 'IRCNAME') {
+ $set{$var}=$val;
+ } elsif ($var eq 'EIGHT_BIT') {
+ $val =~ tr/A-Z/a-z/;
+ $set{$var}=$val if $val =~ /^(on|off)$/;
+ } elsif ($var eq 'LOCALHOST') {
+ &restrict || return;
+ # IPV6: DCC is always ipv4 :(
+ local($ad) = (&resolve($val, 0, &AF_INET))[3];
+ $set{$var}=$val, $bindaddr=$ad if $ad;
+ } elsif ($var eq 'LOADPATH') {
+ @loadpath=split(/:/, $val);
+ foreach (@loadpath) {
+ &tilde($_);
+ }
+ $set{$var}=join(":", @loadpath);
+ } elsif ($var eq 'CTRL_T') {
+ $set{$var}=$val;
+ print "`#ssfe#T$val\n" if $ssfe;
+ } elsif ($var eq 'LOGFILE') {
+ &restrict || return;
+ &tilde($val);
+ $logfile=$set{$var}=$val;
+ } elsif ($var eq 'LOG') {
+ &restrict || return;
+ if ($val =~ /^on$/i) {
+ $logging && close LOG;
+ if (open(LOG,
+ ($logfile =~ /\.gz$/ ? "| gzip >> $logfile" : ">> $logfile"))) {
+ $logging=1;
+ $set{$var}="on";
+ select(LOG); $|=1; select(STDOUT);
+ print LOG "*\cbL\cb* IRC log started on ".&date(time)."\n";
+ } else {
+ $logging='';
+ $set{$var}="off";
+ &tell("*\cbE\cb* Can't write to logfile $logfile");
+ }
+ } elsif ($val =~ /^off$/i) {
+ print LOG "*\cbL\cb* Log ended on ".&date(time)."\n", close LOG
+ if $logging;
+ $logging='';
+ $set{$var}="off";
+ }
+ } elsif (defined($sets{$var})) {
+ local($f)=$sets{$var};
+ eval { &$f($val); };
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in SET $var hook: $@") if $@ ne '';
+ }
+}
+
+sub ctcpreply {
+ local($ctcp, $rest)=split(/ +/, $_[1], 2);
+ $ctcp =~ tr/a-z/A-Z/;
+ &dohooks("ctcp_reply", $_[0], $ctcp, $rest);
+ $rest=(time-$rest)." seconds" if $ctcp eq 'PING';
+ if (&eq($_[0], $nick)) {
+ &tell("*** CTCP $ctcp reply from $who$puh1: $rest");
+ } else {
+ &tell("*** CTCP $ctcp reply to $_[0] from $who$puh2: $rest");
+ }
+}
+
+sub load {
+ local($f)=@_;
+ &tilde($f);
+ if ($f !~ /\//) {
+ foreach (@loadpath) {
+ $f="$_/$f", last if -f "$_/$f";
+ $f="$_/${f}.pl", last if $f !~ /\.pl$/ && -f "$_/${f}.pl";
+ }
+ } else {
+ $f.=".pl" if -f "${f}.pl" && !-f $f;
+ }
+ if ($f =~ /\// && -f $f) {
+ do $f;
+ $@ =~ s/\n$//, &tell("*\cbE\cb* Load error in $f: $@") if $@ ne '';
+ } else {
+ &tell("*\cbE\cb* $f: File not found");
+ }
+}
+
+sub restrict {
+ &tell("*\cbE\cb* Command not available"), return 0 if $restrict;
+ 1;
+}
+
+sub dosplat {
+ $args =~ s/^\s*\*($|\s)/${talkchannel}${1}/ if $talkchannel;
+}
+
+sub expand {
+ if ($_[0] eq '$') {
+ return '$';
+ } elsif ($_[0] =~ /^(\d+)$/) {
+ return (split(/ +/, $args))[$1];
+ } elsif ($_[0] =~ /^(\d+)-$/) {
+ return (split(/ +/, $args, 1+$1))[$1];
+ } else {
+ return eval "\$$_[0]";
+ }
+}
+
+$recdepth=0;
+$maxrecursion=20;
+
+sub docommand {
+ local($line)=@_;
+ local($recdepth)=$recdepth+1;
+ &print("*\cbE\cb* Max recursion exceeded!"), return
+ if $recdepth > $maxrecursion;
+ local($noalias)=($line =~ s/^\///);
+ local($silent)=1 if $line =~ s/^\^//;
+ local($cmd, $args)=split(/ +/, $line, 2);
+ $cmd =~ tr/a-z/A-Z/;
+ if (!$noalias && defined($aliases{$cmd})) {
+ $line=$aliases{$cmd};
+ $line.=($args ne '' ? " ".$args : "")
+ unless ($line =~ s/\$(\$|\d+-?|\w+)/&expand($1)/eg);
+ $line =~ s/^\///;
+ $noalias=1 if $line =~ s/^\///;
+ $silent=1 if $line =~ s/^\^//;
+ ($cmd, $args)=split(/ +/, $line, 2);
+ $cmd =~ tr/a-z/A-Z/;
+ }
+ if (!$noalias && defined($cmds{$cmd})) {
+ eval $cmds{$cmd};
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in command $cmd: $@") if $@ ne '';
+ } elsif ($cmd eq 'ALIAS') {
+ &getarg;
+ if ($newarg =~ /^-/) {
+ local($a)=$';
+ if ($a eq '') {
+ %aliases=();
+ &tell("*** All aliases removed");
+ } else {
+ $a =~ tr/a-z/A-Z/;
+ delete $aliases{$a};
+ &tell("*** Alias $a removed");
+ }
+ } elsif ($newarg ne '') {
+ $newarg =~ tr/a-z/A-Z/;
+ if ($args ne '') {
+ $aliases{$newarg}=$args;
+ &tell("*** $newarg aliased to $args");
+ } else {
+ if (defined($aliases{$newarg})) {
+ &tell("*** $newarg is aliased to: $aliases{$newarg}");
+ } else {
+ &tell("*** $newarg: no such alias");
+ }
+ }
+ } else {
+ foreach $a (sort(keys(%aliases))) {
+ &tell("*** $a is aliased to $aliases{$a}");
+ }
+ }
+ } elsif ($cmd eq 'SET') {
+ &getarg;
+ local($s)=$newarg;
+ $s =~ tr/a-z/A-Z/;
+ if ($s =~ s/^-//) {
+ &tell("*** No such variable $s"), return unless defined($set{$s});
+ &doset($s, "");
+ &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
+ } elsif ($s ne '') {
+ &tell("*** No such variable $s"), return unless defined($set{$s});
+ &doset($s, $args) if $args ne '';
+ &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
+ } else {
+ foreach $s (sort(keys (%set))) {
+ &tell("*** $s is ".($set{$s} ne '' ? "set to $set{$s}" : "unset"));
+ }
+ }
+ } elsif ($cmd eq 'NOTIFY' || $cmd eq 'N') {
+ if ($args eq '-') {
+ &tell("*** Notify list cleared");
+ my($value);
+ while(($_, $value) = each %notify){ # Remove all nicks
+ &tell("*\cb(\cb* Signoff by $_ detected!"); # KSIRC MOD
+ }
+ %notify=();
+ } elsif ($args eq '') {
+ local($l)='';
+ foreach (grep($notify{$_}, keys %notify)) {
+ &tell("*** Currently present: $l"), $l='' if length($l)>450;
+ &tell("*\cb)\cb* Signon by $_ detected!"); # KSIRC MOD
+ $l.=$_." ";
+ }
+ $l && &tell("*** Currently present: $l");
+ $l='';
+ foreach (grep(!$notify{$_}, keys %notify)) {
+ &tell("*** Currently absent: $l"), $l='' if length($l)>450;
+ &tell("*\cb(\cb* Signoff by $_ detected!"); # KSIRC MOD
+ $l.=$_." ";
+ }
+ $l && &tell("*** Currently absent: $l");
+ } else {
+ local($w, $n);
+ foreach $w (split(/ +/, $args)) {
+ if ($w =~ s/^-//) {
+ ($n)=(grep(&eq($_, $w), keys(%notify)), '');
+ $n ne '' && delete $notify{$n};
+ &tell("*** $w removed from notify list");
+ &tell("*\cb(\cb* Signoff by $w detected!"); # KSIRC MOD
+ } else {
+ $notify{$w}='0';
+ &tell("*** $w added to notify list");
+ $newisons=1;
+ }
+ }
+ }
+ } elsif ($cmd eq 'IGNORE' || $cmd eq 'IG') {
+ &getarg;
+ if ($newarg eq '-') {
+ @ignore=();
+ &tell("*** Ignore list cleared");
+ } elsif ($newarg eq '') {
+ local($p);
+ &tell("*** You're ignoring:");
+ foreach (@ignore) {
+ $p=$_;
+ $p =~ s/\\//g;
+ $p =~ s/\.\*/*/g;
+ &tell("*** $p");
+ }
+ } else {
+ local($d, $p)=('');
+ $d=1 if $newarg =~ s/^-//;
+ if ($newarg =~ /\!.*\@/) {
+ } elsif ($newarg !~ /[\@\!]/) {
+ $newarg.="!*";
+ } elsif ($newarg =~ /\@/) {
+ $newarg="*!".$newarg;
+ } else {
+ $newarg.="\@*";
+ }
+ $p=$newarg;
+ $newarg =~ s/([^\\])\./$1\\./g;
+ $newarg =~ s/\*/\.\*/g;
+ $newarg =~ s/([^\.\*\\\w])/\\$1/g;
+ if ($d) {
+ &tell("*** Removing $p from the ignore list");
+ @ignore=grep(!&eq($_, $newarg), @ignore);
+ } else {
+ &tell("*** Ignoring $p ... what a relief!");
+ push(@ignore, $newarg);
+ }
+ }
+ } elsif ($cmd eq 'ECHO') {
+ &print($args);
+ } elsif ($cmd eq 'CLEAR' || $cmd eq 'CL') {
+ print $cls if $ansi;
+ print "`#ssfe#l\n" if $ssfe;
+ } elsif ($cmd eq 'EVAL') {
+ &restrict || return;
+ eval ($args);
+ $@ =~ s/\n$//, &tell("*\cbE\cb* eval error: $@") if $@ ne '';
+ } elsif ($cmd eq 'HELP') {
+ &tell("*\cbH\cb* Help not available"), return unless @help;
+ $args='main' if $args =~ /^\s*$/;
+ $args =~ s/ *$//;
+ local($found)='';
+ foreach (@help) {
+ if (/^\@/) {
+ last if $found;
+ if (&eq($_, "\@$args")) {
+ $found=1;
+ &tell("*\cbH\cb* Help on $args") if $args ne 'main';
+ }
+ } else {
+ &tell("*\cbH\cb* $_") if $found;
+ }
+ }
+ &tell("*\cbH\cb* Unknown help topic; try /help") unless $found;
+ } elsif ($cmd eq 'LOAD') {
+ &restrict || return;
+ &getarg;
+ &tell("*\cbE\cb* Yeah, but what?"), return if $newarg eq '';
+ &load($newarg);
+ } elsif ($cmd eq 'VERSION') {
+ &tell("*** \cbsirc\cb version $version, written in \cbperl\cb by \cborabidoo\cb");
+ $_=$add_ons;
+ s/^\+//;
+ s/\+/, /g;
+ &tell("*** add-ons: $_") if $_;
+ $connected==2 && &sl("VERSION $args");
+ } elsif ($cmd eq 'CD') {
+ &restrict || return;
+ &getarg;
+ if ($newarg ne '') {
+ &tilde($newarg);
+ chdir($newarg) || &tell("*\cbE\cb* Can't chdir to $newarg");
+ }
+ local($cwd); chop($cwd=`pwd`);
+ &tell("*** Current directory is $cwd");
+ } elsif ($cmd eq 'SYSTEM') {
+ &restrict || return;
+ system($args);
+ } elsif ($cmd eq 'BYE' || $cmd eq 'QUIT' || $cmd eq 'EXIT' ||
+ $cmd eq 'SIGNOFF') {
+ $args || ($args="using sirc version $version$add_ons");
+ &dohooks("quit");
+ &sl("QUIT :$args") if $connected;
+ &exit;
+ } elsif ($cmd eq 'SERVER') {
+ $args=$1 if $args =~ /^\s*(.*)\s*$/;
+ $args="$server0:$port0:$pass0" if $args eq '0';
+ $args="$server1:$port1:$pass1" if $args eq '1';
+ if ($args eq '') {
+ &tell($connected ? "*** Your current server is $server" :
+ "*** You're not connected to a server");
+ } else {
+ ($server, $port, $pass)=split(/[\s:]+/, $args);
+ $server=$', $nick=$1 if $server =~ /^([^\@]+)\@/;
+ $port || ($port=$port0);
+ &sl("QUIT :changing servers"), close $S, delete $buffer{$S} if $connected;
+ $connected=0;
+ }
+ } elsif ($cmd eq 'MSG' || $cmd eq 'M') {
+ &dosplat;
+ if ($args) {
+ ($newarg, $args)=split(/ /, $args, 2);
+ &msg($newarg, $args);
+ } else {
+ &tell("*\cbE\cb* You must specify a nick or channel!");
+ }
+ } elsif ($cmd eq 'QUERY' || $cmd eq 'Q') {
+ if ($args) {
+ $args =~ s/\s+$//;
+ $query=$args;
+ &tell("*** Starting conversation with $query");
+ &dostatus;
+ } elsif ($query) {
+ &tell("*** Ending conversation with $query");
+ $query='';
+ &dostatus;
+ } else {
+ &tell("*** You aren't querying anyone :p");
+ }
+ } elsif ($cmd eq 'DCC') {
+ &getarg;
+ if ($newarg =~ /^chat$/i) {
+ &getarg;
+ local($n)=grep(&eq($newarg, $_), keys(%dcoffered));
+ if ($n) {
+ local($dcadr, $dcport)=split(/ +/, $dcoffered{$n});
+ local($fh);
+ delete $dcoffered{$n};
+ my $w = $n;
+ my $cb = sub {
+ my ($lfh, $lres) = @_;
+ if($lres != 0){
+ &tell("*\cbD\cb* DCC CHAT with $w failed: " . strerror($lres));
+ &tell("~!dcc~DCC CHAT failed who: $w reason: " . strerror($lres));
+ close($lfh);
+ return;
+ }
+ $dcnick{$lfh}=$w;
+ &tell("*\cbD\cb* DCC CHAT with $w established");
+ &tell("~!dcc~DCC CHAT established who: $w");
+ print "`#ssfe#t/m =$w \n" if $ssfe;
+ my $n = $w;
+ $n =~ tr/A-Z/a-z/;
+ $dcvol{$n}=0;
+ $dcfh{$n}=$fh;
+ };
+ if(&connectnb($fh, $dcadr, $dcport, $cb) < 1){
+ return;
+ }
+ } elsif (grep (&eq($newarg, $dcwait{$_}), keys(%dcwait))) {
+ &tell("*\cbE\cb* DCC CHAT request to $newarg already sent");
+ } elsif (grep(&eq($newarg, $dcnick{$_}), keys(%dcnick))) {
+ &tell("*\cbE\cb* DCC CHAT with $newarg already established");
+ } elsif ($newarg) {
+ &tell("*** You're not connected to a server"), return if $connected<2;
+ &tell("*** Don't be antisocial!"), return if &eq($newarg, $nick);
+ local($mynumber, $myport, $fh);
+ my $sockaddr = &listen($fh) or return;
+ if ($ipv6) {
+ # XXX: substr is used in order to avoid dying on Linux with older
+ # glibc that lacks the scope field from sockaddr_in6 but the kernel
+ # has it and returns it from getsockname()
+ ($myport, undef) = unpack_sockaddr_in6(substr($sockaddr, 0, 24));
+ $mynumber = '0';
+ } else {
+ ($myport, $mynumber) = unpack_sockaddr_in(&listen($fh)) or return;
+ $mynumber = unpack("N", $mynumber);
+ }
+ $dcwait{$fh}=$newarg;
+ &sl("PRIVMSG $newarg :\caDCC CHAT chat $mynumber $myport\ca");
+ &dohooks("send_ctcp", $newarg, "DCC CHAT chat $mynumber $myport");
+ &tell("*\cbD\cb* Sent DCC CHAT request to $newarg");
+ &tell("~!dcc~DCC CHAT SEND who: $newarg");
+ } else {
+ &tell("*** I need a nick");
+ }
+ } elsif ($newarg =~ /^rchat$/i) {
+ &getarg;
+ local($n)=$newarg;
+ &getarg;
+ if ($newarg) {
+ local($fh)=grep(&eq($dcnick{$_}, $n), keys(%dcnick));
+ if( ! $fh){
+ &tell("*\cbE\cb* No DCC CHAT established with $n");
+ &tell("~!dcc~No DCC CHAT established who: $n");
+ return;
+ }
+ &tell("*\cbE\cb* DCC CHAT already established with $newarg"), return
+ if grep(&eq($dcnick{$_}, $newarg), keys(%dcnick));
+ &tell("*\cbD\cb* DCC CHAT with $n renamed to $newarg");
+ &tell("~!dcc~DCC CHAT renamed who: $n to: $newarg");
+ $dcnick{$fh}=$newarg;
+ $n =~ tr/A-Z/a-z/;
+ $newarg =~ tr/A-Z/a-z/;
+ $dcfh{$newarg}=$dcfh{$n};
+ $dcvol{$newarg}=$dcvol{$n};
+ delete $dcfh{$n};
+ delete $dcvol{$n};
+ } else {
+ &tell("*** I need *two* nicks");
+ }
+ } elsif ($newarg =~ /^close$/i) {
+ &getarg;
+ if ($newarg =~ /^chat$/i) {
+ &getarg;
+ local($n)=$newarg;
+ $newarg =~ tr/A-Z/a-z/;
+ local($fh)=$dcfh{$newarg};
+ local($nn)=(grep(&eq($_, $newarg), keys(%dcoffered)));
+ if ($nn) {
+ &tell("*\cbD\cb* Forgetting offered DCC CHAT from $nn");
+ &tell("~!dcc~Closing DCC CHAT who: $nn");
+ delete $dcoffered{$nn};
+ if($no_reject == 0){
+ $who = $nn;
+ &reply("DCC REJECT CHAT chat");
+ }
+ $no_reject = 0;
+ } elsif ($fh) {
+ &dohooks("chat_disconnect", $n);
+ &tell("*\cbD\cb* Closing DCC CHAT connection with $n");
+ &tell("~!dcc~Closing DCC CHAT who: $n");
+ close($fh);
+ delete $dcnick{$fh};
+ delete $dcvol{$newarg};
+ delete $dcfh{$newarg};
+ delete $buffer{$fh};
+ if($no_reject == 0){
+ $who = $n;
+ &reply("DCC REJECT CHAT chat");
+ }
+ $no_reject = 0;
+
+ } elsif (($fh)=grep(&eq($dcwait{$_}, $n), keys (%dcwait)), $fh) {
+ close($fh);
+ delete $dcwait{$fh};
+ &tell("*\cbD\cb* Closing listening DCC CHAT with $n");
+ &tell("~!dcc~Closing DCC CHAT who: $n");
+ if($no_reject == 0){
+ $who = $n;
+ &reply("DCC REJECT CHAT chat");
+ }
+ $no_reject = 0;
+ } else {
+ if($n){
+ &tell("*\cbE\cb* No DCC CHAT connection with $n");
+ &tell("~!dcc~No DCC CHAT connection who: $n");
+ }
+ }
+ } elsif ($newarg =~ /^get$/i) {
+ &getarg;
+ my $arg = $newarg;
+ local($found)='';
+ foreach $i (keys(%dgoffered)) {
+ if (&eq($dgoffered{$i}, $newarg) && (!$args ||
+ &eq($args, (split(/ +/, $i))[2]))) {
+ &tell("*\cbE\cb* Forgetting pending DCC GET from $newarg");
+ my($host, $port, $file) = split(/ /, $i);
+ &tell("~!dcc~Closing DCC GET connection with who: $newarg file: $file"); # KSIRC MOD
+ delete $dgoffered{$i};
+ $found=1;
+ if($no_reject == 0){
+ $who = $newarg;
+ &reply("DCC REJECT GET $file");
+ }
+ $no_reject = 0;
+ }
+ }
+ foreach $sfh (grep(&eq($newarg, $dnick{$_}), keys(%dnick))) {
+ if (!$found && $dgrfh{$sfh}) {
+ local($fh)=$dgrfh{$sfh};
+ my($file)=$dfile{$fh};
+ next if $args && ($args ne $dfile{$fh});
+ &dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$fh},
+ $dtransferred{$sfh}, time-$dstarttime{$fh}, $fh);
+
+ &tell("*\cbE\cb* Closing DCC GET connection with: $newarg ($file)"); # KSIRC MOD
+ &tell("~!dcc~Closing DCC GET connection with who: $newarg file: $file"); # KSIRC MOD
+ $found=1;
+ close $sfh;
+ close $fh;
+ delete $dgrfh{$sfh};
+ delete $dfile{$fh};
+ delete $dstarttime{$fh};
+ delete $dtransferred{$sfh};
+ delete $dgxferadd{$sfh};
+ delete $dnick{$sfh};
+ if($no_reject == 0){
+ $who = $newarg;
+ &reply("DCC REJECT GET $file");
+ }
+ $no_reject = 0;
+ }
+ }
+ if( ! $found){
+ &tell("*\cbE\cb* No DCC GET connection with $newarg for $arg");
+ &tell("~!dcc~No DCC GET connection who: $newarg file: $arg");
+ }
+ } elsif ($newarg =~ /^send$/i) {
+ &getarg;
+ local($n, $found, $fh)=($newarg, '');
+ &getarg;
+ my $arg = $newarg;
+ $newarg =~ s/(\W)/\\$1/g;
+ foreach $sfh (keys(%dswait), keys(%dsrfh)) {
+ next unless &eq($dnick{$sfh}, $n);
+ $fh=$dswait{$sfh} || $dsrfh{$sfh} || next;
+ if ($newarg eq '' || $dfile{$fh} =~ /^${newarg}$/ ||
+ $dfile{$fh} =~ /\/${newarg}$/) {
+ #&tell("*\cbD\cb* DCC SEND connection with $n closed");
+ #my($file)=$dfile{$fh};
+ #&tell("~!dcc~Closing DCC SEND connection with who: $n file: $file"); # KSIRC MOD
+ #&dohooks("dcc_disconnect", $dnick{$sfh}, $dfile{$fh},
+ # $dtransferred{$sfh}, time-$dstarttime{$fh}, $fh);
+ #close($sfh);
+ #close($fh);
+ #delete $dswait{$sfh};
+ #delete $dsrfh{$sfh};
+ #delete $dfile{$fh};
+ #delete $dstarttime{$fh};
+ #delete $dtransferred{$sfh};
+ #delete $dsoffset{$sfh};
+ #delete $dsport{$sfh};
+ #delete $dsresumedb{$sfh};
+ #delete $dgxferadd{$sfh};
+ #delete $dnick{$sfh};
+ if($no_reject == 0){
+ $who = $n;
+ &reply("DCC REJECT SEND $dfile{$fh}");
+ }
+ $no_reject = 0;
+
+ if($dstarttime{$fh} == undef) {
+ $dstarttime{$fh} = time;
+ }
+ &dgsclose($sfh, $fh, "SEND", "CLOSE");
+
+ $found=1;
+ }
+ }
+ if(!$found){
+ &tell("*\cbE\cb* No DCC SEND connection with $n for $arg");
+ &tell("~!dcc~No DCC SEND connection with who: $n file: $arg");
+ }
+ } else {
+ &tell("*\cbE\cb* Unknown DCC type");
+ }
+ } elsif ($newarg =~ /^rename$/i) {
+ local($found, $n);
+ &getarg;
+ $n=$newarg;
+ &getarg;
+ $args=$newarg, $newarg='' if $args eq '';
+ &tell("*\cbE\cb* I need a filename :p"), return if $args eq '';
+ &tilde($args);
+ foreach $i (keys(%dgoffered)) {
+ if (&eq($dgoffered{$i}, $n) && (!$newarg ||
+ &eq($newarg, (split(/ +/, $i))[2]))) {
+ local($m, $p, $f)=split(/ +/, $i);
+ delete $dgoffered{$i};
+ $dgoffered{"$m $p $args"}=$n;
+ &tell("*\cbD\cb* Renaming \"$f\" (offered by $n) to \"$args\"");
+ $found=1;
+ last;
+ }
+ }
+ &tell("*\cbE\cb* No such file offered by $n") unless $found;
+ } elsif ($newarg =~ /^get$/i) {
+ &getarg;
+ local($n)=grep((&eq($newarg, $dgoffered{$_}) && (!$args ||
+ &eq($args, (split(/ +/, $_))[2]))),
+ keys(%dgoffered));
+ if ($n) {
+ my($dgadr, $dgport, $file)=split(/ +/, $n);
+ my($fh, $sfh);
+ my $offset = 0;
+ $n=(delete $dgoffered{$n});
+ $fh=&newfh;
+ if($dgresume{$dgport} && $dgresume{$dgport}{"GotReply"}){
+ &print("*\cbE\cb* Can't write to file $file"), return unless open($fh, ">> $file");
+ seek($fh, $dgresume{$dgport}{"pos"}, SEEK_SET);
+ $offset = $dgresume{$dgport}{"pos"};
+ delete $dgresume{$dgport};
+ }
+ else {
+ &print("*\cbE\cb* Can't write to file $file"), return unless open($fh, "> $file");
+ }
+ my $who = $n;
+ my $cb = sub {
+ my ($lfh, $lres) = @_;
+ if($lres != 0){
+ &tell("*\cbD\cb* DCC GET connection with $who ($file) failed: " . strerror($lres));
+ &tell("~!dcc~DCC GET failed who: $who file: $file reason: " . strerror($lres));
+ close($lfh);
+ return;
+ }
+ $dgrfh{$lfh}=$fh;
+ $dnick{$lfh}=$who;
+ $dfile{$fh}=$file;
+ $dstarttime{$fh}=time;
+ $dtransferred{$lfh}=0;
+ $dgxferadd{$lfh}=$offset;
+ &tell("*\cbD\cb* DCC GET connection with $who established");
+ &tell("~!dcc~DCC GET established who: $who file: $file");
+ &dohooks("dcc_get", $who, $file, $fh);
+ };
+ if(&connectnb($sfh, $dgadr, $dgport, $cb) < 1){
+ return;
+ }
+ } else {
+ if ($newarg) {
+ &tell("*\cbE\cb* No pending DCC GET from $newarg");
+ } else {
+ &tell("*\cbE\cb* Uhm, who from?");
+ }
+ }
+ } elsif ($newarg =~ /^list$/i || $newarg eq '') {
+ &tell("*\cbD\cb* List of DCC connections:");
+ foreach $n (keys(%dcfh)) {
+ &tell("*\cbD\cb* Established DCC CHAT with $n ($dcvol{$n} bytes)");
+ }
+ foreach $n (keys(%dcoffered)) {
+ my ($pip, $port) = split(/ /, $dcoffered{$n});
+ my $ip = inet_ntoa(pack("N", $pip));
+ &tell("*\cbD\cb* DCC CHAT offered by $n ($ip:$port)");
+ }
+ foreach $f (keys(%dcwait)) {
+ &tell("*\cbD\cb* DCC CHAT offered to $dcwait{$f}");
+ }
+ foreach $i (keys(%dgoffered)) {
+ my ($pip, $port, $file) = split(/ /, $i);
+ my $ip = inet_ntoa(pack("N", $pip));
+ &tell("*\cbD\cb* DCC GET \"$file\" ($ip:$port) offered by $dgoffered{$i}");
+ }
+ foreach $s (keys(%dgrfh)) {
+ local($f)=$dgrfh{$s};
+ &tell("*\cbD\cb* DCC GET \"$dfile{$f}\" established with $dnick{$s}, $dtransferred{$s} bytes read in ".(time-$dstarttime{$f})." seconds.");
+ }
+ foreach $s (keys(%dswait)) {
+ local($f)=$dswait{$s};
+ &tell("*\cbD\cb* DCC SEND \"$dfile{$f}\" offered to $dnick{$s}");
+ }
+ foreach $s (keys(%dsrfh)) {
+ local($f)=$dsrfh{$s};
+ &tell("*\cbD\cb* DCC SEND \"$dfile{$f}\" established with $dnick{$s}, $dtransferred{$s} bytes sent in ".(time-$dstarttime{$f})." seconds.");
+ }
+ } elsif ($newarg =~ /^send$/i) {
+ &tell("*** You're not connected to a server"), return if $connected<2;
+ &restrict || return;
+ local(($n),($f)) = $args =~ /^(.+?) (.+)/;
+ local($tf, $mynumber, $sz, $fh, $myport, $lfh)=($f);
+ &tilde($f);
+ while (my($fh, $ni) = each %dnick ) {
+ if(&eq($n, $ni)){
+ my $lfh = $dswait{$fh};
+ if(&eq($dfile{$lfh}, $f)){
+ &tell("*\cbE\cb* DCC Send already pending of $f to $n");
+ return;
+ }
+ if($dsrfh{$fh}){
+ &tell("*\cbE\cb* DCC Send already in progress $f to $n");
+ return;
+
+ }
+ }
+ }
+ $fh=&newfh;
+ &tell("*\cbE\cb* Can't open file $f"), return unless open($fh, "<$f");
+ my $sockaddr = &listen($lfh) or (close $fh, return);
+ if ($ipv6) {
+ # XXX: substr is used in order to avoid dying on Linux with older
+ # glibc that lacks the scope field from sockaddr_in6 but the kernel
+ # has it and returns it from getsockname()
+ ($myport, undef) = unpack_sockaddr_in6(substr($sockaddr, 0, 24));
+ $mynumber = 0;
+ } else {
+ ($myport, $mynumber) = unpack_sockaddr_in($sockaddr);
+ $mynumber = unpack("N", $mynumber);
+ }
+ $dswait{$lfh}=$fh;
+ $tf=$1 if $f =~ m|/([^/]*)$|;
+ $sz=(-s $f);
+ $tf =~ s/ /_/g; # we have to convert spaces in the filename to underscores
+ &sl("PRIVMSG $n :\caDCC SEND $tf $mynumber $myport $sz\ca");
+ &dohooks("send_ctcp", $n, "DCC SEND $tf $mynumber $myport $sz");
+ &dohooks("dcc_send", $n, $f, $sz, $fh);
+ #&tell("*\cbD\cb* Sent DCC SEND request to $n ($f,$sz)");
+ &tell("~!dcc~Sent DCC SEND request to who: $n file: $f size: $sz");
+ $dfile{$fh}=$f;
+ $dswait{$lfh}=$fh;
+ $dnick{$lfh}=$n;
+ $dsport{$lfh}=$myport;
+ $dsoffset{$lfh}=0;
+ } else {
+ &tell("*** I can \"only\" do DCC CHAT, RCHAT, GET, SEND, CLOSE, RENAME and LIST, *sheesh*");
+ }
+ } elsif ($cmd eq 'QUOTE') { #KSIRC MOD
+ $args ne '' && &sl($args); #Allow this even if not connected to talk to proxies
+ } elsif ($connected<2) {
+ &tell("*** You're not connected to a server");
+ } elsif ($cmd eq 'AWAY') {
+ &sl($args ? "AWAY :$args" : "AWAY");
+ my $oldchannel = $talkchannel;
+ if ( $publicAway == 1 ) {
+ foreach $talkchannel (@channels) {
+ &me($args ? "is away: $args" : "is back");
+ }
+ }
+ $talkchannel = $oldchannel;
+ } elsif ($cmd eq 'NEXT') {
+ if ($#channels>0) {
+ $talkchannel=shift(@channels);
+ push(@channels, $talkchannel);
+ !$ssfe && &tell("*** Talking to $talkchannel now");
+ &dostatus;
+ }
+ } elsif ($cmd eq 'SAY' || $cmd eq '') {
+ &say($args);
+ } elsif ($cmd eq 'NOTICE' || $cmd eq 'NO') {
+ &dosplat;
+ if ($args) {
+ ($newarg, $args)=split(/ /, $args, 2);
+ &notice($newarg, $args);
+ } else {
+ &tell("*\cbE\cb* You must specify a nick or channel!");
+ }
+ } elsif ($cmd eq 'DESCRIBE' || $cmd eq 'DE') {
+ &dosplat;
+ if ($args) {
+ ($newarg, $args)=split(/ /, $args, 2);
+ &describe($newarg, $args);
+ } else {
+ &tell("*\cbE\cb* You must specify a nick or channel!");
+ }
+ } elsif ($cmd eq 'KICK' || $cmd eq 'K') {
+ &dosplat;
+ &getarg;
+ local($c)=$talkchannel;
+ if ($newarg =~ /^[\#\&\+]/) {
+ $c=$newarg;
+ &getarg;
+ }
+ if ($newarg) {
+ $args || ($args=$nick);
+ &sl("KICK $c $newarg :$args");
+ } else {
+ &tell("*\cbE\cb* You must specify a nick!");
+ }
+ } elsif ($cmd eq 'DISCONNECT' || $cmd eq 'DIS') {
+ &tell("*** Disconnecting from $server");
+ close($S);
+ delete $buffer{$S};
+ $connected=0;
+ &dohooks("disconnect");
+ &bindtoserver;
+
+ } elsif ($cmd eq 'INVITE' || $cmd eq 'INV' || $cmd eq 'I') {
+ local(@ns)=split(/ +/, $args);
+ local($l, $c)=(pop(@ns), $talkchannel);
+ if ($l =~ /^[\#\&\+]/) {
+ $c=$l;
+ } else {
+ $l && push(@ns, $l);
+ }
+ foreach (@ns) {
+ &sl("INVITE $_ $c");
+ }
+ } elsif ($cmd eq 'CTCP') {
+ &dosplat;
+ if ($args) {
+ &getarg;
+ local($towho)=$newarg;
+ &getarg;
+ $newarg =~ tr/a-z/A-Z/;
+ $args=" ".$args if $args ne '';
+ &sl("PRIVMSG $towho :\ca$newarg$args\ca");
+ &dohooks("send_ctcp", $towho, $newarg.$args);
+ &tell("*** Sending a CTCP $newarg$args to $towho");
+ } else {
+ &tell("*\cbE\cb* You must specify a nick or channel!");
+ }
+ } elsif ($cmd eq 'PING' || $cmd eq 'P') {
+ &dosplat;
+ if ($args) {
+ &getarg;
+ local($t)=time;
+ &sl("PRIVMSG $newarg :\caPING $t\ca");
+ &dohooks("send_ctcp", $newarg, "PING $t");
+ &tell("*** Sending a CTCP PING to $newarg");
+ } else {
+ &tell("*\cbE\cb* You must specify a nick or channel!");
+ }
+ } elsif ($cmd eq 'ME') {
+ if ($talkchannel) {
+ &describe($talkchannel, $args);
+ } else {
+ &tell("*\cbE\cb* Not on a channel");
+ }
+ } elsif ($cmd eq 'TOPIC' || $cmd eq 'T') {
+ &dosplat;
+ local($c)=$talkchannel;
+ if ($args =~ /^[\#\&\+]/) {
+ &getarg;
+ $c=$newarg;
+ }
+ if ($args) {
+ &sl("TOPIC $c :$args");
+ } else {
+ &sl("TOPIC $c");
+ }
+ } elsif ($cmd eq 'LEAVE' || $cmd eq 'PART' || $cmd eq 'HOP') {
+ &dosplat;
+ $args=$talkchannel if $args eq '';
+ &sl("PART $args");
+ } elsif ($cmd eq 'LL') {
+ if ($talkchannel) {
+ &sl("WHO $talkchannel");
+ } else {
+ &tell("*\cbE\cb* Not on a channel");
+ }
+ } elsif ($cmd eq 'O' || $cmd eq 'OP') {
+ local($c, $n, $l)=($talkchannel, 0, '');
+ &getarg, $c=$newarg if ($args =~ /^[\#\&\+]/);
+ local(@ppl)=split(/ +/, $args);
+ foreach (@ppl) {
+ if ($n<4) {
+ $l .= " ".$_;
+ $n++;
+ } else {
+ &sl("MODE $c +oooo $l");
+ $l=$_;
+ $n=1;
+ }
+ }
+ $l && &sl("MODE $c +oooo $l");
+ } elsif ($cmd eq 'D' || $cmd eq 'DEOP') {
+ local($c, $n, $l)=($talkchannel, 0, '');
+ &getarg, $c=$newarg if ($args =~ /^[\#\&\+]/);
+ local(@ppl)=split(/ +/, $args);
+ foreach (@ppl) {
+ if ($n<4) {
+ $l .= " ".$_;
+ $n++;
+ } else {
+ &sl("MODE $c -oooo $l");
+ $l=$_;
+ $n=1;
+ }
+ }
+ $l && &sl("MODE $c -oooo $l");
+ } elsif ($cmd eq 'W' || $cmd eq 'WHOIS') {
+ &sl($args eq '' ? "WHOIS $nick" : "WHOIS $args");
+ } elsif ($cmd eq 'WI') {
+ &getarg;
+ $newarg=$nick if $newarg eq '';
+ &sl("WHOIS $newarg $newarg");
+ } elsif ($cmd eq 'WHO') {
+ &dosplat;
+ if ($args =~ /^[\s\*]*$/) {
+ &tell("*** Uhm, better not");
+ } else {
+ &sl("WHO $args");
+ }
+ } elsif ($cmd eq 'JOIN' || $cmd eq 'J') {
+ $args=$invited if $args eq '';
+ if ($args !~ /^[\#\&\+]/) {
+ $query = $args;
+ }
+ elsif (grep(&eq($_, $args), @channels)) {
+# &tell("*** Talking to $args now"); # KSIRC MOD
+ $talkchannel=$args;
+ $query = "";
+ &dostatus;
+ } else {
+ &sl("JOIN $args");
+ }
+ } elsif ($cmd eq 'UMODE') {
+ &sl("MODE $nick $args");
+ } elsif ($cmd eq 'MO') {
+ if ($talkchannel) {
+ &sl("MODE $talkchannel $args");
+ } else {
+ &tell("*\cbE\cb* You're not on any channel anyway");
+ }
+ } elsif ($cmd eq 'LIST') {
+ &dosplat;
+ $listmin=0;
+ $listmax=100000;
+ $listpat='';
+ if ($args =~ /\*/ || $args =~ /-m[ia][nx]\s/i) {
+ while (&getarg, $newarg ne '') {
+ if ($newarg =~ /^-min$/i) {
+ &getarg;
+ $listmin=$newarg if $newarg>0;
+ } elsif ($newarg =~ /^-max$/i) {
+ &getarg;
+ $listmax=$newarg if $newarg>0;
+ } else {
+ $newarg =~ s/([^\\])\./$1\\./g;
+ $newarg =~ s/\*/\.\*/g;
+ $newarg =~ s/([^\.\*\\\w])/\\$1/g;
+ $listpat=$newarg;
+ }
+ }
+ &sl("LIST");
+ } else {
+ &sl($line);
+ }
+ } elsif ($cmd eq 'RPING') {
+ &getarg;
+ &sl("RPING $newarg ".time);
+ } elsif ($cmd eq 'KILL') {
+ &getarg;
+ if ($newarg) {
+ $args || ($args=$nick);
+ &sl("KILL $newarg :$args");
+ } else {
+ &tell("*\cbE\cb* You must specify a nick!");
+ }
+ } elsif ($cmd eq 'MODE' || $cmd eq 'NAMES') {
+ &dosplat;
+ &sl("$cmd $args");
+ } elsif ($cmd eq 'OPER') {
+ &getarg;
+ $newarg=$nick unless $newarg;
+ &getuserpass("Oper password? ", "Passwd: "), $args=$_ unless $args;
+ &sl("OPER $newarg $args");
+ } elsif ($cmd eq 'CONNECT') {
+ &getarg;
+ local($srv)=$newarg;
+ &getarg;
+ if ($args) {
+ &sl("CONNECT $srv $newarg $args");
+ } else {
+ &sl("CONNECT $srv 6667 $newarg");
+ }
+ } elsif ($cmd eq 'SQUIT') {
+ &getarg;
+ &sl("SQUIT $newarg :$args");
+ } elsif ($cmd eq 'WHOWAS' || $cmd eq 'ADMIN' || $cmd eq 'STATS' ||
+ $cmd eq 'INFO' || $cmd eq 'LUSERS' || $cmd eq 'SQUIT' ||
+ $cmd eq 'REHASH' || $cmd eq 'DIE' || $cmd eq 'LINKS' ||
+ $cmd eq 'NOTE' || $cmd eq 'WALLOPS' || $cmd eq 'NICK' ||
+ $cmd eq 'MOTD' || $cmd eq 'TIME' || $cmd eq 'TRACE' ||
+ $cmd eq 'USERS' || $cmd eq 'SILENCE' || $cmd eq 'MAP' ||
+ $cmd eq 'UPING') {
+ &sl($line);
+ } else {
+ # Unknown command sucks. People want to use extensions like /nickserv, which works
+ # on some servers (Simon)
+ &sl($line);
+# &tell("*\cbE\cb* Unknown command: $cmd");
+ }
+}
+
+sub douserline {
+ local($skip, $line)=(0, @_);
+ if ($line =~ /^\@ssfe\@/) {
+ $ssfe=$raw_mode=1;
+ $add_ons.="+ssfe";
+ &dostatus;
+ } else {
+ &dohooks("command", $line);
+ return if $skip;
+ if ($line =~ s/^\///) {
+ &docommand($line);
+ } elsif ($query ne '') {
+ &msg($query, $line);
+ } else {
+ &say($line);
+ }
+ }
+}
+
+$ssfe_getline="`#ssfe#p";
+sub getuserline {
+ local($skip)='';
+ &dohooks("input", $_[0], $_[1]);
+ return if $skip;
+ print $_[0];
+ print "\n" if $raw_mode;
+ print $ssfe_getline.$_[1]."\n" if $ssfe;
+ while (($_=<STDIN>) ne '') {
+ if (/^\@ssfe\@/) {
+ $ssfe || ($add_ons.="+ssfe");
+ $ssfe=$raw_mode=1;
+ &dostatus;
+ } else {
+ &exit if $_ eq '';
+ chop;
+ return;
+ }
+ }
+ &exit;
+}
+
+sub getuserpass {
+ local($ssfe_getline)="`#ssfe#P";
+ &getuserline;
+}
+
+%cmds=();
+sub addcmd {
+ local($cmd)=$_[0];
+ $cmd =~ tr/a-z/A-Z/;
+ $cmds{$cmd}="&cmd_".$_[0].";";
+}
+
+sub addhelp {
+ local($cmd, $txt)=@_;
+ $cmd =~ tr/A-Z/a-z/;
+ foreach (reverse(split(/\n/, $txt))) {
+ s/\$v/$version/g;
+ s/\$d/$date/g;
+ unshift (@help, $_);
+ }
+ unshift(@help, "\@".$cmd);
+}
+
+sub addset {
+ local($var)=$_[0];
+ $var =~ tr/a-z/A-Z/;
+ $sets{$var}="set_".$_[0];
+}
+
+sub addsel {
+ $buf_fds{$_[0]}="sel_".$_[1] if $_[2];
+ $sel_fds{$_[0]}="sel_".$_[1] unless $_[2];
+}
+
+sub remsel {
+ delete $buf_fds{$_[0]};
+ delete $sel_fds{$_[0]};
+}
+
+sub addwsel {
+ $sel_w_fds{$_[0]}="sel_".$_[1];
+}
+
+sub remwsel {
+ delete $sel_w_fds{$_[0]};
+}
+
+@hooks=("action", "ctcp", "ctcp_reply", "dcc_chat", "dcc_request", "input",
+ "invite", "join", "kick", "leave", "mode", "msg", "nick", "notice",
+ "server_notice", "notify_signoff", "notify_signon", "public",
+ "raw_irc", "send_action", "send_dcc_chat", "send_text", "send_notice",
+ "signoff", "topic", "disconnect", "status", "print", "command",
+ "chat_disconnect", "dcc_disconnect", "send_ctcp",
+ "dcc_send", "dcc_send_status", "dcc_get", "dcc_get_status", "quit",
+ "pong"); # ksirc additions
+
+sub addhook {
+ local($type, $name)=@_;
+ $type =~ tr/A-Z/a-z/;
+ $name="hook_".$name;
+ if ($type =~ /^\d\d\d$/ || grep(($_ eq $type), @hooks)) {
+ ($type =~ /^\d\d\d$/) && ($type="num_".$type);
+ eval "*ugly_hack_hooks=*${type}_hooks;";
+ unless (grep(($_ eq $name), @ugly_hack_hooks)) {
+ push(@ugly_hack_hooks, $name);
+ }
+ } else {
+ &tell("*\cbE\cb* $type: no such hook");
+ }
+}
+
+sub remhook {
+ local($type, $name)=@_;
+ $type =~ tr/A-Z/a-z/;
+ $name="hook_".$name;
+ if ($type =~ /^\d\d\d$/ || grep(($_ eq $type), @hooks)) {
+ ($type =~ /^\d\d\d$/) && ($type="num_".$type);
+ eval "*ugly_hack_hooks=*${type}_hooks;";
+ @ugly_hack_hooks=grep(($_ ne $name), @ugly_hack_hooks);
+ } else {
+ &tell("*\cbE\cb* $type: no such hook");
+ }
+}
+
+sub userhost {
+ push (@waituh, $_[0]);
+ push (@douh, $_[1]);
+ push (@erruh, $_[2]);
+ &sl("USERHOST $_[0]");
+}
+
+sub deltimer {
+ local($ref)=$_[0];
+ local($i);
+ if ($#trefs>=0 && $ref!=0) {
+ # delete the timer if it exists
+ for ($i=0; $i<=$#trefs; $i++) {
+ if ($trefs[$i]==$ref) {
+ splice(@trefs,$i,1);
+ splice(@timers,$i,1);
+ splice(@timeactions,$i,1);
+ last;
+ }
+ }
+ }
+}
+
+sub timer {
+ local(@r, @t, @a)=();
+ local($t)=$_[0]+time;
+ local($ref)=$_[2] || 0;
+ &deltimer($ref) if $ref;
+ while ($#timers>=0 && $timers[0]<=$t) {
+ push (@r, shift(@trefs));
+ push (@t, shift(@timers));
+ push (@a, shift(@timeactions));
+ }
+ @trefs=(@r, $ref, @trefs);
+ @timers=(@t, $t, @timers);
+ @timeactions=(@a, $_[1], @timeactions);
+}
+
+sub disappeared {
+ local($n)=(grep(&eq($_, $_[0]), keys(%notify)));
+ if ($n ne '' && $notify{$n}>0) {
+ local($silent)=0;
+ &dohooks("notify_signoff", $_[0]);
+ &tell("*\cb(\cb* Signoff by $_[0] detected");
+ $notify{$n}=0;
+ }
+}
+
+sub appeared {
+ local($t, $n)=(time, grep(&eq($_, $_[0]), keys(%notify)));
+ if ($n ne '') {
+ if ($notify{$n}==0) {
+ local($silent)=0;
+ &dohooks("notify_signon", $_[0]);
+ &tell("*\cb)\cb* Signon by $_[0] detected!");
+ }
+ else {
+# &tell("*\cb(\cb* Signoff by $_[0] detected!");
+ }
+ $notify{$n}=$t;
+ }
+}
+
+$lastsendison=0;
+sub send_isons {
+ local($l)='';
+ foreach (keys %notify) {
+ &sl("ISON : $l"), $l='' if (length($l)>500);
+ $l.=$_." ";
+ }
+ &sl("ISON :$l") if $l;
+ $lastsendison=time;
+ $newisons='';
+ $checkisons=1;
+}
+
+sub signoffs {
+ foreach (keys %notify) {
+ if ($notify{$_}>0 && $notify{$_}<$lastsendison) {
+ $notify{$_}=0;
+ local($silent)=0;
+ &dohooks("notify_signoff", $_);
+ &tell("*\cb(\cb* Signoff by $_ detected");
+ }
+ }
+ $checkisons='';
+}
+
+sub modestripper {
+ local($chnl, $what)=@_;
+ $chnl =~ tr/A-Z/a-z/;
+ local($how, $modes, @args)=('+', split(/ +/, $what));
+ foreach $m (split(//, $modes)) {
+ if ($m =~ /[\-\+]/) {
+ $how=$m;
+ } elsif ($m =~ /[vb]/) {
+ shift(@args);
+ } elsif ($m eq 'k') {
+ $how eq '+' ? ($chankey{$chnl}=$args[0]) : delete $chankey{$chnl};
+ shift(@args);
+ } elsif ($m eq 'l') {
+ $how eq '+' ? ($limit{$chnl}=shift(@args)) : delete $limit{$chnl};
+ } elsif ($m eq 'o') {
+ $haveops{$chnl}=($how eq '+') if (&eq(shift(@args), $nick));
+ } else {
+ $mode{$chnl} =~ s/$m//g;
+ $mode{$chnl}.=$m if $how eq '+';
+ }
+ }
+}
+
+sub umodechange {
+ local($what)=@_;
+ local($how)='+';
+ foreach $m (split(//, $what)) {
+ if ($m =~ /[\-\+]/) {
+ $how=$m;
+ } else {
+ $umode =~ s/$m//g;
+ $umode.=$m if ($how eq '+' && $m !~ /\s/);
+ }
+ }
+}
+
+sub ignored {
+ foreach (@ignore) {
+ return 1 if $_[0] =~ /^${_}$/;
+ }
+ return '';
+}
+
+sub dorcfile {
+ return if !open(RCFILE, "<$_[0]");
+ while (<RCFILE>) {
+ chop;
+ s/^\///;
+ next if /^\#/;
+ &docommand($_) if $_;
+ $silent=$skip='';
+ }
+ close RCFILE;
+}
+
+sub loadrc {
+ $rcloaded=1;
+ $sysrc && &dorcfile($sysrc);
+ $rcfile && &dorcfile($rcfile);
+}
+
+sub selline {
+ $leftover=0;
+ $rin=$rout="\0" x 32;
+ $win=$wout="\0" x 32;
+ foreach ($S, 'STDIN', keys(%dcnick), keys(%buf_fds)) {
+ $leftover=1, return $_ if $buffer{$_} =~ /\n/;
+ }
+ foreach ('STDIN', keys(%dcnick), keys(%dcwait), keys(%dgrfh), keys(%dswait),
+ keys(%dsrfh), keys(%sel_fds), keys(%buf_fds)) {
+ vec($rin, fileno($_), 1)=1;
+ }
+ foreach (keys(%sel_w_fds)){
+ vec($win, fileno($_), 1)=1;
+ }
+ vec($rin, fileno($S), 1)=1 if $connected;
+ if ($#timers<0 || $timers[0]>time+30) {
+ select($rout=$rin, $wout=$win, undef, 30);
+ } elsif ($timers[0]<=time) {
+ select($rout=$rin, $wout=$win, undef, 0);
+ } else {
+ select($rout=$rin, $wout=$win, undef, $timers[0]-time);
+ }
+}
+
+sub getnick {
+ if ($ENV{'BACKUPNICK'} && !($nick eq $ENV{'BACKUPNICK'})) {
+ $nick=$ENV{'BACKUPNICK'};
+ } else {
+ &getuserline("Pick a nick: ", "Nick: ");
+ $nick=$_;
+ }
+ &sl("NICK $nick");
+ &dostatus;
+}
+
+sub donumeric {
+ local($from)=($who eq $myserver ? '' : " (from ${who})");
+ if ($cmd eq '401') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cb?\cb* Cannot find $newarg on irc$from");
+ } elsif ($cmd eq '402') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cb?\cb* $newarg: no such server$from");
+ } elsif ($cmd eq '403') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cb?\cb* $newarg: no such channel$from");
+ } elsif ($cmd eq '406') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cb?\cb* $newarg: there was no such nickname$from");
+ } elsif ($cmd eq '421') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cb?\cb* $newarg: unknown command$from");
+ } elsif ($cmd =~ /^4[012]/) {
+ $args =~ s/^[^:]*://;
+ &tell("*** $args$from");
+ } elsif ($cmd eq '431') {
+ &tell("*** Was expecting a nickname somewhere...");
+ &getnick if $connected<2;
+ } elsif ($cmd eq '432') {
+ if ($connected==2) {
+ &tell("*\cbN\cb* Invalid nickname, you're still \"$nick\"");
+ } else {
+ &tell("*\cbN\cb* Invalid nickname!");
+ &getnick;
+ }
+ } elsif ($cmd eq '433') {
+ if ($connected==2) {
+ &tell("*\cbN\cb* Nick already taken, you're still \"$nick\"");
+ } else {
+ &tell("*\cbN\cb* Nick already taken!");
+ &getnick;
+ }
+ } elsif ($cmd eq '441') {
+ local($g, $w, $c)=split(/ +/, $args);
+ &tell("*\cbE\cb* $w is not on channel $c$from");
+ } elsif ($cmd eq '442') {
+ local($w, $c)=split(/ +/, $args);
+# &tell("*\cbE\cb* You're not on channel $c$from"); # KSIRC MOD
+ } elsif ($cmd eq '443') {
+ local($w, $o, $c)=split(/ +/, $args);
+ &tell("*\cbE\cb* $o is already on channel $c$from");
+ } elsif ($cmd eq '465') {
+ &tell("*\cbE\cb* You are banned from this server$from");
+ } elsif ($cmd eq '461') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cbE\cb* The command $newarg needs more arguments than that$from");
+ } elsif ($cmd =~ /^47[1345]$/) {
+ &yetonearg;
+ &yetonearg;
+ local($r);
+ if ($cmd eq '471') {
+ $r="channel is full";
+ } elsif ($cmd eq '473') {
+ $r="channel is invite-only";
+ } elsif ($cmd eq '474') {
+ $r="banned from channel";
+ } else {
+ $r="bad channel key";
+ }
+ &tell("*\cbE\cb* Can't join $newarg: ${r}$from");
+ } elsif ($cmd eq '301') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*** $newarg is away: $args");
+ } elsif ($cmd eq '302') {
+ &yetonearg;
+ &yetonearg;
+ local($n, $do, $err)=(shift(@waituh), shift(@douh), shift(@erruh));
+ if ($newarg =~ /^([^\s\*=]+)[\*]?=([\-+])/) {
+ $who=$1;
+ local($adr)=$';
+ if ($adr =~ /\@/) {
+ $user=$`;
+ $host=$';
+ } else {
+ $user=$host='';
+ }
+ if (&eq($who, $n)) {
+ eval $do;
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in userhost: $@") if $@ ne '';
+ } else {
+ &tell("*\cbE\cb* userhost returned for unexpected nick $who");
+ }
+ } else {
+ if (defined($err)) {
+ eval $err;
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in userhost: $@") if $@ ne '';
+ } else {
+ &tell("*\cb?\cb* Cannot find $n on irc");
+ }
+ }
+ } elsif ($cmd eq '303') {
+ &yetonearg;
+ local($n);
+ foreach $n (split(/ +/, $args)) {
+ &appeared($n);
+ }
+ } elsif ($cmd eq '305') {
+ &tell("*** You are no longer marked as away");
+ $away='';
+ &dostatus;
+ } elsif ($cmd eq '306') {
+ &tell("*** You are marked as being away");
+ $away=1;
+ &dostatus;
+ } elsif ($cmd eq '311') {
+ local($g, $n, $u, $m, $g, $r)=split(/ +/, $args, 6);
+ $r =~ s/^://;
+ &tell("*** $n is $u\@$m ($r)");
+ } elsif ($cmd eq '312') {
+ &yetonearg;
+ &yetonearg;
+ &yetonearg;
+ local($s)=$newarg;
+ &tell("*** on IRC via server $s ($args)");
+ } elsif ($cmd eq '313') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*** $newarg $args");
+ } elsif ($cmd eq '314') {
+ local($g, $n, $u, $m, $g, $r)=split(/ +/, $args, 6);
+ $r =~ s/^://;
+ &tell("*** $n was $u\@$m ($r)");
+ } elsif ($cmd eq '317') {
+ &yetonearg;
+ &yetonearg;
+ local($n)=$newarg;
+ &yetonearg;
+ if ($newarg>=3600) {
+ &tell("*** $n has been idle for ".int($newarg/3600)." hours, ".
+ int(($newarg%3600)/60)." minutes and ".
+ ($newarg%60)." seconds");
+ } elsif ($newarg>=60) {
+ &tell("*** $n has been idle for ".int($newarg/60)." minutes and ".
+ ($newarg%60)." seconds");
+ } else {
+ &tell("*** $n has been idle for $newarg seconds");
+ }
+ } elsif ($cmd eq '319') {
+ local($g, $g, $c)=split(/ +/, $args, 3);
+ $c =~ s/^://;
+ &tell("*** on channels: $c");
+ } elsif ($cmd eq '322') {
+ local($g, $c, $n, $r)=split(/ +/, $args, 4);
+ $r =~ s/^://;
+ $n>=$listmin && $n <=$listmax && (!$listpat || $c =~ /^${listpat}$/i)
+ && &tell(sprintf("*** %-10s %-5s %s", $c, $n, $r));
+ } elsif ($cmd eq '323') {
+ $listmin=0;
+ $listmax=100000;
+ $listpat='';
+ } elsif ($cmd eq '324') {
+ local($g, $c, $m)=split(/ +/, $args, 3);
+ $m =~ s/^://;
+ $m =~ s/ $//;
+ $c =~ tr/A-Z/a-z/;
+ if (grep(&eq($_, $c), @channels)) {
+ if (defined($mode{$c})) {
+ &tell("*\cb+\cb* Mode for channel $c is \"$m\"");
+ } else {
+ $mode{$c}='';
+ }
+ &modestripper($c, $m);
+ &dostatus;
+ } else {
+ &tell("*\cb+\cb* Mode for channel $c is \"$m\"");
+ }
+ } elsif ($cmd eq '329') {
+ &yetonearg;
+ &yetonearg;
+ local($c)=$newarg;
+ &yetonearg;
+ local($t)=($newarg ? ("created " . &date($newarg)) : "0 TS");
+ &tell("*** $c : $t");
+ } elsif ($cmd eq '331') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cbT\cb* No topic is set on channel $newarg");
+ } elsif ($cmd eq '332') {
+ &yetonearg;
+ &yetonearg;
+ &tell("*\cbT\cb* Topic for $newarg: $args");
+ } elsif ($cmd eq '333') {
+ local($g, $c, $n, $t)=split(/ +/, $args, 4);
+ local($d)=&date($t);
+ &tell("*\cbT\cb* Topic for $c set by $n on $d");
+ } elsif ($cmd eq '318' || $cmd eq '315' || $cmd eq '369' ||
+ $cmd eq '321' || $cmd eq '376' || # KSIRC MOD
+ $cmd eq '365' || $cmd eq '368' || $cmd eq '374' ||
+ $cmd eq '219' || $cmd eq '007') {
+ #nothing!
+ } elsif ($cmd eq '341') {
+ local($g, $n, $c)=split(/ +/, $args, 3);
+ &tell("*\cbI\cb* Inviting $n to channel $c");
+ } elsif ($cmd eq '352') {
+ local($g, $c, $u, $m, $s, $n, $st, $g, $i)=split(/ +/, $args, 9);
+ &tell(sprintf("%-10s %-9s %4s %s\@%s (%s)", $c, $n, $st, $u, $m, $i));
+ } elsif ($cmd eq '353') {
+ local($g, $m, $c, $r)=split(/ +/, $args, 4);
+ local($n)=$nick;
+ $n =~ s/(\W)/\\$1/g;
+ $r =~ s/^://;
+ if($DSIRC_NAMES eq ''){ #KSIRC MOD
+ &tell("*I* Users on $c: $r"); # KSIRC MOD
+ $DSIRC_NAMES = $c; # KSIRC MOD
+ } # KSIRC MOD
+ else { # KSIRC MOD
+ &tell("*\cbI\cb* Users on $c: $r"); # KSIRC MOD
+ } # KSIRC MOD
+ $c =~ tr/A-Z/a-z/;
+ $haveops{$c}=1 if ($r =~ /\@${n}( |$)/i);
+ &dostatus if &eq($c, $talkchannel);
+ } elsif ($cmd eq '366'){ # KSIRC MOD
+ #&tell("*I* Users on $DSIRC_NAMES:"); # KSIRC MOD
+ $DSIRC_NAMES = ''; # KSIRC MOD
+ } elsif ($cmd eq '221') {
+ &yetonearg;
+ &tell("*\cb+\cb* Your user mode is \"$args\"");
+ } elsif ($cmd eq '200') {
+ local($b, $l, $v, $n, $s)=split(/ +/, $args);
+ $s =~ s/^://;
+ &tell("*** $l $who ($v) ==> $n $s");
+ } elsif ($cmd eq '205') {
+ local($b, $u, $h, $n)=split(/ +/, $args);
+ $n =~ s/^://;
+ &tell("*** $u [$h] ==> $n");
+ } elsif ($cmd =~ /^20/) {
+ local($b, $t, $n, $r)=split(/ +/, $args, 4);
+ &tell("*** $t [$n] ==> $r");
+ } elsif ($cmd eq '375' || $cmd eq '372' || $cmd =~ /^25/) {
+ &yetonearg;
+ &tell("*** $args");
+ } elsif ($cmd eq '379' ) { # RPL_FORWARD (Simon)
+ &yetonearg;
+ local( $from_channel, $to_channel ) = split( / +/, $args );
+ &tell("~$from_channel~*\cb<\cb* You have left channel $from_channel");
+ } else {
+ &yetonearg;
+ #$args =~ s/ :/ /;
+ &tell("*** $args$from");
+ }
+}
+
+# main prog
+
+print "`#ssfe#i\n" unless (-t STDOUT);
+&tell("*** Welcome to \cbsirc\cb version $version; type /help for help");
+
+&load($sysinit) if $sysinit ne '' && -f $sysinit;
+&load($initfile) if !$restrict && $initfile ne '' && -f $initfile;
+
+while (1) {
+ &bindtoserver, undef $ready if $ready;
+ $silent=$skip='';
+ if ($connected==2) {
+ $time=time;
+ &loadrc unless $rcloaded;
+ &send_isons
+ if $time>=$lastsendison+90 || ($newisons && $time>=$lastsendison+10);
+ &signoffs if $checkisons && ($time>=$lastsendison+30);
+ }
+ $fh=&selline;
+ foreach $rfh (keys (%buf_fds)) {
+ if (vec($rout, fileno($rfh), 1) || ($leftover && $fh eq $rfh)) {
+ &gl($rfh) || next;
+ local($line, $h)=($_, $buf_fds{$rfh});
+ delete $buf_fds{$rfh}, delete $buffer{$rfh}, close($rfh) if $_ eq '';
+ eval { &$h($line); };
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in buffered fd hook &$h: $@")
+ if $@ ne '';
+ }
+ }
+ foreach $rfh (keys (%sel_fds)) {
+ if (vec($rout, fileno($rfh), 1)) {
+ local($h)=$sel_fds{$rfh};
+ eval { &$h($rfh); }; #KSIRC MOD
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in unbuffered fd hook &$h: $@")
+ if $@ ne '';
+ }
+ }
+ foreach $rfh (keys (%sel_w_fds)) {
+ if (vec($wout, fileno($rfh), 1)) {
+ local($h)=$sel_w_fds{$rfh};
+ eval { &$h($rfh); }; #KSIRC MOD
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in unbuffered fd hook &$h: $@")
+ if $@ ne '';
+ }
+ }
+ foreach $rfh (keys (%dcnick)) {
+ if (vec($rout, fileno($rfh), 1) || ($leftover && $fh eq $rfh)) {
+ &gl($rfh) || next;
+ &dcerror($rfh), next if $_ eq '';
+ chop;
+ local($who, $what)=($dcnick{$rfh}, $_);
+ $dcvol{$dcnick{$rfh}}+=length($what);
+ print "`#ssfe#t/m =$who \n" if $ssfe;
+ print "`#ssfe#o=${who}= $what\n" if $ssfe;
+ &dohooks("dcc_chat", $who, $what);
+ &tell("~=${who}~=\cb${who}\cb= $what"); # KSIRC MOD
+ $silent='';
+ }
+ }
+ foreach $rfh (keys (%dcwait)) {
+ if (vec($rout, fileno($rfh), 1)) {
+ local($n, $fh);
+ my $paddr;
+ if ($paddr = &accept($fh, $rfh)) {
+ select($fh); $|=1; select(STDOUT);
+ my($port,$iaddr) = sockaddr_in($paddr);
+ my $ip = inet_ntoa($iaddr);
+ $n=$dcwait{$rfh};
+ $dcnick{$fh}=$n;
+ $n =~ tr/A-Z/a-z/;
+ $dcvol{$n}=0;
+ $dcfh{$n}=$fh;
+ &tell("*\cbD\cb* DCC CHAT connection with $n established");
+ &tell("~!dcc~DCC CHAT inbound established who: $n ip: $ip");
+ print "`#ssfe#t/m =$n \n" if $ssfe;
+ }
+ delete $dcwait{$rfh};
+ }
+ }
+ foreach $sfh (keys (%dswait)) {
+ local($rfh, $fh)=$dswait{$sfh};
+ if (vec($rout, fileno($sfh), 1)) {
+ my $paddr;
+ if ($paddr = &accept($fh, $sfh)) {
+ my($port,$iaddr) = sockaddr_in($paddr);
+ my $ip = inet_ntoa($iaddr);
+ select($fh); $|=1; select(STDOUT);
+ $dsrfh{$fh}=$rfh;
+ $dstarttime{$rfh}=time;
+ $dtransferred{$fh}=0;
+ $dnick{$fh}=$dnick{$sfh};
+ $dsoffset{$fh}=$dsoffset{$sfh};
+ &tell("*\cbD\cb* DCC SEND connection with $dnick{$sfh}/$ip ($dfile{$rfh}) established");
+ &tell("~!dcc~DCC SEND established who: $dnick{$sfh} file: $dfile{$rfh} ip: $ip");
+ }
+ delete $dnick{$sfh};
+ delete $dswait{$sfh};
+ delete $dsoffset{$sfh};
+ delete $dsport{$sfh};
+ }
+ }
+ foreach $sfh (keys (%dgrfh)) {
+ local($rfh)=$dgrfh{$sfh};
+ if (vec($rout, fileno($sfh), 1)) {
+ local($a, $buf)=(0, '');
+ $a=sysread($sfh, $buf, 4096);
+ if ($a) {
+ $dtransferred{$sfh}+=$a;
+ &dohooks("dcc_get_status", $dfile{$rfh}, $dtransferred{$sfh}, $rfh);
+ # &tell("*\cbD\cb* DCC GET read: $dfile{$rfh} bytes: $dtransferred{$sfh}"); # KSIRC MOD FOR 971217
+ my $b = $dtransferred{$sfh}+$dgxferadd{$sfh};
+ &tell("~!dcc~DCC GET read: $dfile{$rfh} who: $dnick{$sfh} bytes: $b"); # KSIRC MOD FOR 971217
+ print $rfh $buf;
+ print $sfh pack("N", $b); # used to be just $dtransfered but most seem to want xfet + offset
+ } else {
+ &dgsclose($sfh, $rfh, "GET", "OK");
+ }
+ }
+ }
+ foreach $sfh (keys (%dsrfh)) {
+ local($rfh)=$dsrfh{$sfh};
+ if (vec($rout, fileno($sfh), 1) || !$dtransferred{$sfh}) {
+ local($ack, $csa, $buf, $b, $l, $w)=(0, '', '');
+ if ($dtransferred{$sfh}) {
+ &dgsclose($sfh, $rfh, "SEND", "Protocol Error"), next if sysread($sfh, $b, 4)!=4;
+ $ack=unpack("N", $b);
+ }
+ if($ack > ($dtransferred{$sfh} + $dsoffset{$sfh})){
+ my $v = $dtransferred{$sfh} + $dsoffset{$sfh};
+ &tell("*\cbD\cb* DCC transfer protocol failure! $ack $dtransferred{$sfh} $dsoffset{$sfh} $v");
+ &dgsclose($sfh, $rfh, "SEND", "Protocol Out of Sync");
+ next;
+ }
+ #
+ # When you do a dcc resume the ack value returned from the
+ # remote client is not well defined. Two different values
+ # are used, the current number of bytes transfered, or
+ # the current location in the file. We try to detech
+ # which type of ack we got and we adjust our math
+ # according so we keep up nice packet sizes.
+ # xchat can't seem to take > 4k packets after a resume
+ # and it causes the backoff to ack a little funny, but
+ # it's not our fault!
+ #
+ if($dsoffset{$sfh} && ($ack != 0) && ($dsresumedb{$sfh} == undef)) {
+ if($ack > $dsoffset{$sfh}){
+ $dsresumedb{$sfh} = 1;
+ }
+ else {
+ $dsresumedb{$sfh} = 2;
+ }
+ #&print("*** Resume style is: $dsresumedb{$sfh}");
+
+ }
+ if($dsoffset{$sfh} && ($dsresumedb{$sfh} == 1)){
+ $csa=$set{"SENDAHEAD"}-($dtransferred{$sfh}+$dsoffset{$sfh})+$ack;
+ }
+ else {
+ $csa=$set{"SENDAHEAD"}-$dtransferred{$sfh}+$ack;
+ }
+ #&print("*** CSA is: $csa ack: $ack dt: $dtransferred{$sfh} $dsoffset{$sfh}");
+ next if $csa<0;
+ $l=read($rfh, $buf, 512+$csa);
+ $w=syswrite($sfh, $buf, $l) if $l;
+ &dohooks("dcc_send_status", $dfile{$rfh}, $dtransferred{$sfh}, $rfh);
+ # &tell("*\cbD\cb* DCC SEND write: $dfile{$rfh} bytes: $dtransferred{$sfh}"); # KSIRC MOD FOR 971218
+ my $sz = $dtransferred{$sfh}+$dsoffset{$sfh};
+ &tell("~!dcc~DCC SEND write: $dfile{$rfh} who: $dnick{$sfh} bytes: $sz"); # KSIRC MOD FOR 971218
+ next if $l==0 && $ack<$dtransferred{$sfh};
+ $dtransferred{$sfh}+=$w;
+ &dgsclose($sfh, $rfh, "SEND", "OK"), next if ($w<$l || $l==0);
+ }
+ }
+ while ($#timers>=0 && $timers[0]<=time) {
+ shift (@timers);
+ shift (@trefs);
+ eval shift (@timeactions);
+ $@ =~ s/\n$//, &tell("*\cbE\cb* error in timer: $@") if $@ ne '';
+ }
+ if (vec($rout, fileno(STDIN), 1) || ($leftover && $fh eq 'STDIN')) {
+ &gl('STDIN') || next;
+ &exit if $_ eq '';
+ chop;
+ $logging && print LOG "<- " . $_ . "\n";
+ &douserline($_) if $_ ne '';
+ }
+ if ($connected && (($leftover && $fh eq $S) || vec($rout, fileno($S), 1))) {
+ &gl($S) || next;
+ if ($_ eq '') {
+ &tell("*\cbE\cb* Connection to server lost");
+ close($S);
+ delete $buffer{$S};
+ $connected=0;
+ &dohooks("disconnect");
+ &bindtoserver;
+ next;
+ }
+ chop;
+ $logging && print LOG ">> " . $_ . "\n";
+ $serverline=$_;
+ $_=$server." ".$_ unless /^:/;
+ ($who, $cmd, $args)=split(/ /, $_, 3);
+ $cmd =~ tr/a-z/A-Z/;
+ $who =~ s/^://;
+ $args =~ s/^://;
+ $user=$host=$puh1=$puh2='';
+ if ($who =~ /^([^!@ ]+)!([^@ ]+)@([^ ]+)$/) {
+ ($who, $user, $host) = ($1, $2, $3);
+ $puh1="!$user\@$host" if $set{"PRINTUH"} ne 'none';
+ $puh2=$puh1 if $set{"PRINTUH"} eq 'all';
+ }
+ &dohooks("raw_irc", $cmd, $args);
+ next if $skip;
+ next if (($cmd eq 'PRIVMSG' || $cmd eq 'NOTICE') &&
+ &ignored("$who!$user\@$host"));
+ if ($cmd eq '001') {
+ $connected=2;
+ $myserver=$who;
+ ($nick)=split(/ /, $args, 2);
+ }
+ if ($cmd =~ /^\d\d\d$/) {
+ &dohooks("num_".$cmd, $args);
+ next if $skip;
+ &donumeric;
+ } elsif ($cmd eq 'PING') {
+ &sl("PONG $args");
+ } elsif ($cmd eq 'PRIVMSG') {
+ &yetonearg;
+ if ($args =~ /^\001([^\001]*)\001$/ && $set{'CTCP'} ne 'none') {
+ &ctcp($newarg, $1);
+ } elsif (!$printchan && &eq($newarg, $talkchannel)) {
+ &dohooks("public", $newarg, $args);
+ &tell("~${newarg}~<${who}> $args"); # MOD FOR KSIRC
+ } elsif ($newarg =~ /^[\#\&\+]/) {
+ &dohooks("public", $newarg, $args);
+ &tell("~${newarg}~<${who}> $args"); # MOD FOR KSIRC
+ } elsif (&eq ($newarg, $nick)) {
+ print "`#ssfe#t/m $who \n" if $ssfe;
+ print "`#ssfe#o[$who$puh1] $args\n" if $ssfe;
+ &dohooks("msg", $args);
+ &tell("~${who}~[\cb${who}\cb${puh1}] $args"); # MOD FOR KSIRC
+ } else {
+ &tell("~${who}~[\cb${who}\cb${puh1}\cb] $args"); # MOD FOR KSIRC
+ }
+ } elsif ($cmd eq 'NOTICE') {
+ &yetonearg;
+ if ($args =~ /^\001([^\001]*)\001$/) {
+ &ctcpreply($newarg, $1);
+ } elsif ($newarg =~ /^[\#\&\+]/) {
+ &dohooks("notice", $newarg, $args);
+ &tell("~${newarg}~-${who}- $args"); # MOD FOR KSIRC
+ } elsif ($who =~ /\./) {
+ &dohooks("server_notice", $args);
+ $args="*** ".$args unless ($args =~ /^\*/);
+ &tell($args);
+ } elsif (&eq($newarg, $nick)) {
+ &dohooks("notice", $newarg, $args);
+ &tell("~${who}~-\cb${who}\cb${puh1}- $args"); # MOD FOR KSIRC
+ } else {
+ &dohooks("notice", $newarg, $args);
+ &tell("~${who}~-\cb$who$puh1\cb- $args"); # MOD FOR KSIRC
+ }
+ $newarg =~ s/\cg.*//; # ircnet kludge
+ } elsif ($cmd eq 'KICK') {
+ &yetonearg;
+ local($channel)=$newarg;
+ &yetonearg;
+ $args=$who unless $args;
+ if (&eq($nick, $newarg)) {
+ &tell("~${channel}~*\cb<\cb* You have been kicked off channel $channel by $who$puh2 ($args)"); # MOD FOR KSIRC
+ @channels=grep(!&eq($_, $channel), @channels);
+ if (@channels) {
+ $talkchannel=$channels[$#channels];
+ } else {
+ $talkchannel='';
+ }
+ $channel =~ tr/A-Z/a-z/;
+ &dohooks("kick", $newarg, $channel, $args);
+ delete $mode{$channel};
+ delete $limit{$channel};
+ delete $haveops{$channel};
+ delete $chankey{$channel};
+ $talkchannel && !$ssfe && &tell("*** Talking to $talkchannel now");
+ &dostatus;
+ } else {
+ &dohooks("kick", $newarg, $channel, $args);
+ &tell("~${channel}~*\cb<\cb* $newarg has been kicked off channel $channel by $who$puh2 ($args)"); # MOD FOR KSIRC
+ }
+ } elsif ($cmd eq 'PART') {
+ &yetonearg;
+ if (&eq($who, $nick)) {
+ #&tell("~!all~*\cb<\cb* You have left channel $newarg"); # MOD FOR KSIRC
+ @channels=grep(!&eq($_, $newarg), @channels);
+ if (@channels) {
+ $talkchannel=$channels[$#channels];
+ } else {
+ $talkchannel='';
+ }
+ $newarg =~ tr/A-Z/a-z/;
+ delete $mode{$newarg};
+ delete $limit{$newarg};
+ delete $haveops{$newarg};
+ delete $chankey{$newarg};
+ &dohooks("leave", $newarg);
+ $talkchannel && !$ssfe && &tell("*** Talking to $talkchannel now");
+ &dostatus;
+ } else {
+ &dohooks("leave", $newarg);
+ &tell("~${newarg}~*\cb<\cb* $who$puh2 has left channel $newarg"); # MOD FOR KSIRC
+ }
+ } elsif ($cmd eq 'JOIN') {
+ &yetonearg;
+ if (&eq($nick, $who)) {
+ $newarg =~ tr/A-Z/a-z/;
+ push(@channels, $newarg);
+ $talkchannel=$newarg;
+ &dohooks("join", $newarg);
+ &dostatus;
+ &tell("~${newarg}~*\cb>\cb* You have joined channel $newarg"); # MOD FOR KSIRC
+ &sl("MODE $newarg");
+ } else {
+ &dohooks("join", $newarg);
+ &tell("~${newarg}~*\cb>\cb* $who ($user\@$host) has joined channel $newarg"); # MOD FOR KSIRC
+ }
+ &appeared($who);
+ } elsif ($cmd eq 'NICK') {
+ &yetonearg;
+ if (&eq($nick, $who)) {
+ $oldnick = $nick;
+ $nick=$newarg;
+ &dohooks("nick", $newarg);
+ $who=$newarg;
+ &dostatus;
+ &tell("~!all~*\cbN\cb* $oldnick is now known as $newarg");
+ } else {
+ &dohooks("nick", $newarg);
+ &tell("~!all~*\cbN\cb* $who$puh2 is now known as $newarg"); # MOD FOR KSIRC
+ }
+ } elsif ($cmd eq 'MODE') {
+ &yetonearg;
+ $args =~ s/ $//;
+ if ($newarg =~ /^[\#\&\+]/) {
+ &modestripper($newarg, $args);
+ &dohooks("mode", $newarg, $args);
+ &dostatus;
+ &tell("~${newarg}~*\cb+\cb* Mode change \"$args\" on channel $newarg by $who$puh2"); # MOD FOR KSIRC
+ } else {
+ local($towho)=$newarg;
+ &yetonearg;
+ &umodechange($newarg), &dostatus if &eq($towho, $nick);
+ &dohooks("mode", $towho, $newarg);
+ &tell("*\cb+\cb* Mode change \"$newarg\" for user $towho by $who"); # MOD FOR KSIRC
+ }
+ } elsif ($cmd eq 'KILL') {
+ &yetonearg;
+ local($n)=$newarg;
+ $args || ($args=$who);
+ &tell("~${newarg}~*\cb<\cb* $n got killed by $who$puh1 ($args)"); # MOD FOR KSIRC
+ } elsif ($cmd eq 'INVITE') {
+ &yetonearg;
+ &yetonearg;
+ &dohooks("invite", $newarg);
+ $invited=$newarg;
+ &tell("~!default~*\cbI\cb* $who$puh1 invites you to channel $newarg"); # MOD FOR KSIRC
+ } elsif ($cmd eq 'TOPIC') {
+ &yetonearg;
+ &dohooks("topic", $newarg, $args);
+ &tell("~${newarg}~*\cbT\cb* $who$puh2 has changed the topic on channel $newarg to \"$args\""); # MOD FOR KSIRC
+ } elsif ($cmd eq 'SILENCE') {
+ &tell("*** Silence $args");
+ } elsif ($cmd eq 'PONG') {
+ &dohooks("pong", $args);
+ } elsif ($cmd eq 'QUIT') {
+ &dohooks("signoff", $args);
+ &tell("~!all~*\cb<\cb* Signoff: $who$puh2 ($args)"); # MOD FOR KSIRC
+ &disappeared($who);
+ } elsif ($cmd eq 'WALLOPS') {
+ &tell("!$who$puh2! ".$args);
+ } elsif ($cmd eq 'RPONG') {
+ local($n, $t, $ms, $ts)=split(/ +/, $args);
+ $ts =~ s/^://;
+ &tell("*** RPONG: $who - $t: $ms ms, ".time-$ts." sec");
+ } else {
+ &tell("*** The server says: $serverline");
+ }
+ }
+}
+
diff --git a/ksirc/eventsrc b/ksirc/eventsrc
new file mode 100644
index 00000000..56031051
--- /dev/null
+++ b/ksirc/eventsrc
@@ -0,0 +1,364 @@
+[!Global!]
+IconName=ksirc
+Comment=KSirc
+Comment[af]=Ksirc
+Comment[bn]=কে-à¦à¦¸-আর-আই-সি
+Comment[hi]=के-à¤à¤¸à¤†à¤ˆà¤†à¤°à¤¸à¥€
+Comment[sv]=Ksirc
+
+[ChannelChanged]
+Name=Channel event
+Name[af]=Kanaal gebeurtenis
+Name[ar]=حدث القناة
+Name[be]=ÐŸÐ°Ð´Ð·ÐµÑ Ð½Ð° канале
+Name[bg]=Има Ñъбитие в канала
+Name[bn]=চà§à¦¯à¦¾à¦¨à§‡à¦² ঘটনা
+Name[bs]=Događaj na kanalu
+Name[ca]=Esdeveniment del canal
+Name[cs]=Událost v kanálu
+Name[cy]=Digwyddiad sianel
+Name[da]=Kanalbegivenhed
+Name[de]=Kanalereignis
+Name[el]=Γεγονός καναλιοÏ
+Name[eo]=Kanala evento
+Name[es]=Evento de canal
+Name[et]=Kanali sündmus
+Name[eu]=Kanal gertaera
+Name[fa]=رویداد کانال
+Name[fi]=Kanavatapahtuma
+Name[fr]=Événement de canal
+Name[gl]=Evento de canle
+Name[he]=×ירוע של הערוץ
+Name[hi]=चैनल घटना
+Name[hr]=Događaj na kanalu
+Name[hu]=Csatornaesemény
+Name[is]=Atburður á rás
+Name[it]=Canale evento
+Name[ja]=ãƒãƒ£ãƒãƒ«ã‚¤ãƒ™ãƒ³ãƒˆ
+Name[ka]=áƒáƒ áƒ®áƒ˜áƒ¡ მáƒáƒ•áƒšáƒ”ნáƒ
+Name[kk]=Ðрнадағы оқиға
+Name[km]=ព្រឹážáŸ’ážáž·áž€áž¶ážšážŽáŸâ€‹áž–្រឹážáŸ’ážáž·áž€áž¶ážšážŽáŸ
+Name[lt]=Įvykis kanale
+Name[mk]=ÐаÑтан на каналот
+Name[mn]=Сувгийн үйл Ñвдал
+Name[mt]=Avveniment tal-kanal
+Name[nb]=Kanalhendelse
+Name[nds]=Kanaalbegeefnis
+Name[ne]=चà¥à¤¯à¤¾à¤¨à¤² घटना
+Name[nl]=Kanaalgebeurtenis
+Name[nn]=Kanalhending
+Name[nso]=Tiragalo ya kanale
+Name[pa]=ਚੈਨਲ ਘਟਨਾ
+Name[pl]=Zdarzenie na kanale
+Name[pt]=Evento do canal
+Name[pt_BR]=Evento do Canal
+Name[ru]=Событие в канале
+Name[se]=Kanáladáhpáhus
+Name[sk]=Udalosť na kanály
+Name[sl]=Dogodek kanala
+Name[sr]=Догађај на каналу
+Name[sr@Latn]=Događaj na kanalu
+Name[sv]=Kanalhändelse
+Name[ta]=தட நிகழà¯à®µà¯
+Name[tg]=ҲодиÑаи канал
+Name[th]=เหตุà¸à¸²à¸£à¸“์ของช่องสนทนา
+Name[tr]=Kanal olayı
+Name[uk]=ÐŸÐ¾Ð´Ñ–Ñ Ð² каналі
+Name[ven]=Vhutambo ha Channel
+Name[xh]=Itshanele yesiganeko
+Name[zh_CN]=通é“事件
+Name[zh_HK]=é »é“事件
+Name[zh_TW]=é »é“事件
+Name[zu]=Uhlelo lwe-Shaneli
+Comment=IRC channel event
+Comment[af]=Irc kanaal gebeurtenis
+Comment[ar]=IRC حدث قناة
+Comment[be]=ÐŸÐ°Ð´Ð·ÐµÑ Ð½Ð° канале IRC
+Comment[bg]=Има Ñъбитие в канала
+Comment[bn]=আই-আর-সি চà§à¦¯à¦¾à¦¨à§‡à¦² ঘটনা
+Comment[bs]=Događaj na IRC kanalu
+Comment[ca]=Esdeveniment del canal d'IRC
+Comment[cs]=Událost IRC kanálu
+Comment[cy]=Digwyddiad sianel IRC
+Comment[da]=IRC-kanalbegivenhed
+Comment[de]=IRC Kanalereignis
+Comment[el]=Γεγονός ÎºÎ±Î½Î±Î»Î¹Î¿Ï IRC
+Comment[eo]=IRC-kanalo
+Comment[es]=Evento de canal IRC
+Comment[et]=IRC kanali sündmus
+Comment[eu]=IRC kanal gertaera
+Comment[fa]=رویداد کانال IRC
+Comment[fi]=IRC-kanavatapahtuma
+Comment[fr]=Événement de canal IRC
+Comment[gl]=Evento na canle de IRC
+Comment[he]=×ירוע של ערוץ IRC
+Comment[hi]=IRC चैनल घटना
+Comment[hr]=Događaj IRC kanala
+Comment[hu]=IRC csatornaesemény
+Comment[is]=IRC rásar-atburður
+Comment[it]=Evento canale IRC
+Comment[ja]=IRC ãƒãƒ£ãƒ³ãƒãƒ«ã‚¤ãƒ™ãƒ³ãƒˆ
+Comment[ka]=IRC áƒáƒ áƒ®áƒ˜áƒ¡ მáƒáƒ•áƒšáƒ”ნáƒ
+Comment[kk]=IRC арнаÑындағы оқиға
+Comment[km]=ព្រឹážáŸ’ážáž·áž€áž¶ážšážŽáŸâ€‹áž†áž¶áž“ែល IRC
+Comment[lt]=IRC kanalo įvykis
+Comment[mk]=ÐаÑтан на IRC-канал
+Comment[mn]=IRC Ñувагийн үйл Ñвдал
+Comment[ms]=acara saluaran IRC
+Comment[mt]=Avveniment ta' kanal tal-IRC
+Comment[nb]=IRC-kanalhendelse
+Comment[nds]=IRC-Kanaalbegeefnis
+Comment[ne]=आइआरसी चà¥à¤¯à¤¾à¤¨à¤² घटना
+Comment[nl]=IRC-kanaalgebeurtenis
+Comment[nn]=IRC-kanalhending
+Comment[nso]=Tiragalo ya kanale ya IRC
+Comment[pa]=IRC ਚੈਨਲ ਘਟਨਾ
+Comment[pl]=Zdarzenie na kanale IRC
+Comment[pt]=Evento no canal de IRC
+Comment[pt_BR]=Evento no Canal IRC
+Comment[ru]=Событие на канале IRC
+Comment[se]=IRC-kanáldáhpáhus
+Comment[sk]=IRC udalosť na kanály
+Comment[sl]=Dogodek kanala IRC
+Comment[sr]=Догађај IRC канала
+Comment[sr@Latn]=Događaj IRC kanala
+Comment[sv]=Händelse i IRC-kanal
+Comment[ta]=IRC தட நிகழà¯à®µà¯
+Comment[tg]=рӯйдодҳои канали IRC
+Comment[th]=เหตุà¸à¸²à¸£à¸“์ในห้อง IRC
+Comment[tr]=IRC kanalı olayı
+Comment[uk]=ÐŸÐ¾Ð´Ñ–Ñ Ð² каналі IRC
+Comment[ven]=Vhutambo ha channel ya IRC
+Comment[xh]=Itshanele ye IRC yesiganeko
+Comment[zh_CN]=IRC 通é“事件
+Comment[zh_HK]=IRC é »é“事件
+Comment[zh_TW]=IRC é »é“事件
+Comment[zu]=Uhlelo lweshaneli IRC
+default_presentation=1
+default_sound=KDE_Pop.ogg
+
+[ChannelPersonal]
+Name=Personal message in channel
+Name[af]=Persoonlike boodskap in kanaal
+Name[ar]=رسالة شخصية ÙÙŠ القناة
+Name[be]=Прыватнае паведамленне ў канале
+Name[bg]=ÐÑкой Ñпомена пÑевдонима ви
+Name[bn]=চà§à¦¯à¦¾à¦¨à§‡à¦²à§‡ বà§à¦¯à¦•à§à¦¤à¦¿à¦—ত বারà§à¦¤à¦¾
+Name[bs]=LiÄna poruka na kanalu
+Name[ca]=Missatge personal en el canal
+Name[cs]=Osobní zpráva v kanálu
+Name[cy]=Neges personol yn y sianel
+Name[da]=Personlig besked i kanal
+Name[de]=Persönliche Nachricht im Kanal
+Name[el]=ΠÏοσωπικό μήνυμα στο κανάλι
+Name[eo]=Persona mesaÄo en kanalo
+Name[es]=Mensaje personal en canal
+Name[et]=Erasõnum kanalis
+Name[eu]=Mezu pertsonala kanalan
+Name[fa]=پیامهای شخصی در کانال
+Name[fi]=Henkilökohtainen viesti kanavalla
+Name[fr]=Message personnel dans le canal
+Name[gl]=Mensaxe personal na canle
+Name[he]=הודעה ×ישית בערוץ
+Name[hi]=चैनल में निजी संदेश
+Name[hr]=Osobna poruka u kanalu
+Name[hu]=Személyes üzenet a csatornán
+Name[is]=Persónulegt skeyti á rás
+Name[it]=Messaggio personale nel canale
+Name[ja]=ãƒãƒ£ãƒãƒ«å†…ã®å€‹äººãƒ¡ãƒƒã‚»ãƒ¼ã‚¸
+Name[ka]=áƒáƒ áƒ®áƒ¨áƒ˜ პირáƒáƒ“ი შეტყáƒáƒ‘ინებáƒ
+Name[kk]=Ðрнада жеке хабарлама
+Name[km]=សារ​ផ្ទាល់​ážáŸ’លួន​នៅ​ក្នុង​ឆានែល
+Name[lt]=Asmeninė žinutė kanale
+Name[mk]=Лична порака на каналот
+Name[mn]=Суваг дахь хувийн илгÑÑвÑÑ€
+Name[ms]=Mesej peribadi di saluran
+Name[mt]=Messaġġ personali fil-kanal
+Name[nb]=Personlig melding i kanalen
+Name[nds]=Persöönliche Naricht in den Klöön-Kanaal
+Name[ne]=चà¥à¤¯à¤¾à¤¨à¤²à¤®à¤¾ वà¥à¤¯à¤•à¥à¤¤à¤¿à¤—त सनà¥à¤¦à¥‡à¤¶
+Name[nl]=Persoonlijk bericht in kanaal
+Name[nn]=Personleg melding i kanalen
+Name[nso]=Molaetsa wa botho kanaleng
+Name[pa]=ਚੈਨਲ ਵਿੱਚ ਨਿੱਜੀ ਸà©à¨¨à©‡à¨¹à¨¾
+Name[pl]=Wiadomość osobista na kanale
+Name[pt]=Mensagem pessoal no canal
+Name[pt_BR]=Mensagem Pessoal no canal
+Name[ru]=Получено личное Ñообщение по каналу
+Name[se]=Persuvnnalaš diehtu kanálas
+Name[sk]=Osobná správa na kanály
+Name[sl]=Osebno sporoÄilo na kanalu
+Name[sr]=Лична порука на каналу
+Name[sr@Latn]=LiÄna poruka na kanalu
+Name[sv]=Personligt meddelande i kanal
+Name[ta]=தடதà¯à®¤à®¿à®²à¯ சொநà¯à®¤ செயà¯à®¤à®¿
+Name[tg]=Паёми шахÑÓ£ дар канал
+Name[th]=ข้อความส่วนตัวในช่องสนทนา
+Name[tr]=Kanal kişisel mesajı
+Name[uk]=ПерÑональне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² каналі
+Name[ven]=Mulaedza wa vhune kha channel
+Name[xh]=Umyalezo ongobuqu okwi tshanele
+Name[zh_CN]=通é“中的个人消æ¯
+Name[zh_HK]=é »é“中的個人訊æ¯
+Name[zh_TW]=é »é“中的個人訊æ¯
+Name[zu]=Umlayezo wakho useshanelini
+Comment=Someone mentioned your nick on IRC
+Comment[af]=Iemand genoem jou by op Irc
+Comment[ar]=شخص ما ذكر اسمك على الايرسي
+Comment[be]=Ðехта напіÑаў вашую мÑнушку Ñž IRC
+Comment[bg]=ÐÑкой Ñпомена пÑевдонима ви
+Comment[bn]=কেউ à¦à¦•à¦œà¦¨ আই-আর-সিতে আপনার ডাকনাম উলà§à¦²à§‡à¦– করল
+Comment[bs]=Neko je spomenuo vaš nick na IRCu
+Comment[ca]=Algú a mencionat el vostre sobrenom a l'IRC
+Comment[cs]=Někdo zmínil vaši přezdívku na IRC
+Comment[cy]=Mae rhywun wedi sôn am eich ffugenw ar IRC
+Comment[da]=Nogen nævnte dit alias på IRC
+Comment[de]=Jemand hat Ihren Spitznamen im IRC erwähnt
+Comment[el]=Κάποιος ανέφεÏε το ψευδώνυμό σας στο IRC
+Comment[eo]=Iu diris vian nomon en IRC-Babilejo
+Comment[es]=Alguien mencionó su apodo en IRC
+Comment[et]=Keegi mainis sinu hüüdnime IRCs
+Comment[eu]=Norbaitek zure goitizena idatzi du IRC-an
+Comment[fa]=شخصی لقب شما را در IRC ذکر کرد
+Comment[fi]=Joku mainitsi lempinimesi IRC:ssä
+Comment[fr]=Quelqu'un a mentionné votre pseudo sur IRC
+Comment[gl]=Alguén mencionou o teu alcume no IRC
+Comment[he]=מישהו הזכיר ×ת הכינוי שלך ב-IRC
+Comment[hi]=संदेश मे किसी ने आपका निक उलà¥à¤²à¥‡à¤– किया
+Comment[hr]=Netko je spomenuo vaš nickname na IRC-u
+Comment[hu]=Valaki említette az IRC-n az Ön becenevét
+Comment[is]=Einhver minntist á þig á Irkinu
+Comment[it]=Qualcuno ha menzionato il tuo nick su IRC
+Comment[ja]=誰ã‹ãŒ IRC ã§ã‚ãªãŸã®ãƒ‹ãƒƒã‚¯ãƒãƒ¼ãƒ ã«è¨€åŠã—ã¾ã—ãŸ
+Comment[ka]=ვიღáƒáƒªáƒáƒ› áƒáƒ®áƒ¡áƒ”ნრთქვენი მეტსáƒáƒ®áƒ”ლი IRC-ზე
+Comment[kk]=IRC арнаÑында біреу Сізді атады
+Comment[km]=មាន​គáŸâ€‹áž“ិយាយ​អំពី​សម្មážáž·áž“ាម​របស់​អ្នក​នៅ​លើ IRC
+Comment[lt]=Kažkas paminėjo Jūsų slapyvardį IRC
+Comment[mk]=Ðекој го Ñпоменува вашиот прекар на IRC
+Comment[mn]=IRC-д Ñ…Ñн нÑгÑн нь таны никийг нÑрлÑв
+Comment[ms]=Seseorang melibatkan nama samaran anda di dalam IRC
+Comment[mt]=Xi ħadd semma ismek fuq l-IRC
+Comment[nb]=Noen nevnte ditt kallenavn på IRC
+Comment[nds]=Een hett Dien Ökelnaam bi't Klönen nöömt
+Comment[ne]=कसैले तपाईà¤à¤•à¥‹ आइआरसी मा उपनाम उलà¥à¤²à¥‡à¤– गरà¥à¤¯à¥‹
+Comment[nl]=Iemand noemde uw bijnaam op IRC
+Comment[nn]=Nokon nemnde kallenamnet ditt på IRC
+Comment[nso]=Yo mongwe o boletse leina la gago la metlae go IRC
+Comment[pl]=Ktoś wspomniał Twój przydomek
+Comment[pt]=Alguém mencionou o seu nome no IRC
+Comment[pt_BR]=Alguém mencionou seu apelido no IRC
+Comment[ru]=Кто-то упомÑнул ваше Ð¸Ð¼Ñ Ð² IRC
+Comment[se]=Giinu namuhii du gohÄÄodannama IRC:as
+Comment[sk]=Niekto zmenil vašu prezývku na IRC
+Comment[sl]=Nekdo je omenil vaš vzdevek na IRC-u
+Comment[sr]=Ðеко је Ñпоменуо ваш надимак на IRC-у
+Comment[sr@Latn]=Neko je spomenuo vaš nadimak na IRC-u
+Comment[sv]=Någon nämnde ditt smeknamn på IRC
+Comment[ta]=யாரோ à®’à®°à¯à®µà®°à¯ உஙà¯à®•à®³à¯ பà¯à®©à¯ˆà®ªà¯à®ªà¯†à®¯à®°à¯ˆ IRC யில௠கà¯à®±à®¿à®ªà¯à®ªà®¿à®Ÿà¯à®Ÿà¯à®³à¯à®³à®¾à®°à¯
+Comment[tg]=КаÑе ники шуморо дар IRC ёд овард
+Comment[th]=มีบางคนพาดพิงถึงคุณบน IRC
+Comment[tr]=IRC'de takma adınızdan söz eden birisi
+Comment[uk]=ХтоÑÑŒ згадав ваше прізвиÑько в IRC
+Comment[ven]=Munwe o bula tshikhethekanyi kha IRC yanu
+Comment[xh]=Omnye umntu uchaze igama lakho lesiteketiso kwi-IRC
+Comment[zh_CN]=有人在 IRC 中æ到了您的昵称
+Comment[zh_HK]=æŸäººåœ¨ IRC æ到您的暱稱
+Comment[zh_TW]=æŸäººåœ¨ IRC æ到您的暱稱
+Comment[zu]=kukhona osho igama lakho lokudlala kwi IRC
+default_presentation=1
+default_sound=KDE_Beep.ogg
+
+[BeepReceived]
+Name=Beep received
+Name[be]=Ðтрыманы Ñігнал
+Name[bg]=Звуков Ñигнал
+Name[bn]=বিপ গà§à¦°à¦¹à¦£ করল
+Name[bs]=Primljen beep
+Name[ca]=S'ha rebut un bip
+Name[cs]=Obdrženo pípnutí
+Name[da]=Lydsignal modtaget
+Name[de]=Signalton empfangen
+Name[el]=ΠαÏαλαβή ηχητικής ειδοποίησης
+Name[eo]=Hupo ricevita
+Name[es]=Pitido recibido
+Name[et]=Saabus piiks
+Name[eu]=Bip-a jaso da
+Name[fa]=بوق دریاÙت شد
+Name[fr]=Bip reçu
+Name[ga]=Fuarthas bíp
+Name[gl]=Recibiuse unha badalada
+Name[he]=התקבל ביפ
+Name[hu]=Csipogás érkezett
+Name[is]=Hljóðmerki móttekið
+Name[it]=Ricevuto un bip
+Name[ja]=呼ã³å‡ºã—ã‚’å—ã‘ã¾ã—ãŸ
+Name[ka]=შეტყáƒáƒ‘ინებრმáƒáƒ•áƒ˜áƒ“áƒ
+Name[kk]=Қоңырау Ñоғылды
+Name[km]=បាន​ទទួល​សំឡáŸáž„​ប៊ីប
+Name[lt]=Gautas skambutis
+Name[nb]=Tut mottat
+Name[nds]=Signaaltoon kregen
+Name[ne]=बीप पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो
+Name[nl]=Geluidsignaal ontvangen
+Name[nn]=Pip motteke
+Name[pl]=Otrzymano brzęczyk
+Name[pt]=Apito recebido
+Name[pt_BR]=Beep recebido
+Name[ru]=Получен Ñигнал
+Name[sk]=Prijaté pípnutie
+Name[sl]=Prejet pisk
+Name[sr]=Примљен је бип
+Name[sr@Latn]=Primljen je bip
+Name[sv]=Ljudsignal mottagen
+Name[tr]=Zil alındı
+Name[uk]=Отримано гудок
+Name[zh_CN]=收到了呼å«
+Name[zh_HK]=收到響è²
+Name[zh_TW]=收到嗶è²
+Comment=A beep has been received from the server
+Comment[be]=Ðтрыманы Ñігнал ад Ñервера
+Comment[bg]=ПриÑтигна звуков Ñигнал от Ñървъра
+Comment[bn]=সারà§à¦­à¦¾à¦° থেকে à¦à¦•à¦Ÿà¦¿ বিপ গà§à¦°à¦¹à¦£ করা হয়েছে
+Comment[bs]=Primljen je beep sa servera
+Comment[ca]=S'ha rebut un bip des del servidor
+Comment[cs]=Bylo obdrženo pípnutí ze serveru
+Comment[da]=Et lydsignal er modtaget fra serveren.
+Comment[de]=Von diesem Server wurde ein Signalton empfangen.
+Comment[el]=Μια ηχητική ειδοποίηση παÏαλήφθηκε από τον εξυπηÏετητή
+Comment[es]=Se ha recibido un pitido del servidor
+Comment[et]=Serverilt saabus piiks
+Comment[eu]=Bip bat jaso da zerbitzaritik
+Comment[fa]=یک بوق از کارساز دریاÙت شد
+Comment[fr]=Un bip a été reçu du serveur
+Comment[gl]=Recibiuse unha badalada dende o servidor
+Comment[he]=התקבל ביפ מהשרת
+Comment[hu]=Csipogás érkezett a kiszolgálóról
+Comment[is]=Hljóðmerki hefur verið móttekið frá þjóninum
+Comment[it]=È stato ricevuto un bip dal server
+Comment[ja]=サーãƒã‹ã‚‰å‘¼ã³å‡ºã—ã‚’å—ã‘ã¾ã—ãŸ
+Comment[ka]=სერვერიდáƒáƒœ შეტყáƒáƒ‘ინებრმáƒáƒ•áƒ˜áƒ“áƒ
+Comment[kk]=Серверден қоңырау келді
+Comment[km]=បាន​ទទួល​សំឡáŸáž„​ប៊ីប​មួយ​ពី​ម៉ាស៊ីន​បម្រើ
+Comment[lt]=IÅ¡ serverio gautas skambutis
+Comment[nb]=Et tut er mottatt fra tjeneren
+Comment[nds]=Signaaltoon vun den Server kregen
+Comment[ne]=सरà¥à¤­à¤°à¤¬à¤¾à¤Ÿ बीप पà¥à¤°à¤¾à¤ªà¥à¤¤ भयो
+Comment[nl]=Geluidssignaal van de server ontvangen
+Comment[nn]=Eit pip vart motteke frå tenaren
+Comment[pl]=Otrzymano brzęczyk z serwera
+Comment[pt]=Foi recebido um apito do servidor
+Comment[pt_BR]=Um beep foi recebido do servidor
+Comment[ru]=Получен звуковой Ñигнал Ñ Ñервера
+Comment[sk]=Prijalo sa pípnutie zo servera
+Comment[sl]=Od strežnika ste prejeli pisk
+Comment[sr]=Звучни Ñигнал је примљен Ñа Ñервера
+Comment[sr@Latn]=ZvuÄni signal je primljen sa servera
+Comment[sv]=En ljudsignal har tagits emot från servern
+Comment[tr]=Sunucudan bir bip alındı
+Comment[uk]=Отримано гудок з Ñервера
+Comment[zh_CN]=收到了æœåŠ¡å™¨çš„呼å«
+Comment[zh_HK]=從伺æœå™¨æ”¶åˆ°éŸ¿è²
+Comment[zh_TW]=從伺æœå™¨ä¸Šæ”¶åˆ°å—¶è²
+default_presentation=1
+default_sound=KDE_Beep.ogg
+
diff --git a/ksirc/filters.pl b/ksirc/filters.pl
new file mode 100644
index 00000000..f7359714
--- /dev/null
+++ b/ksirc/filters.pl
@@ -0,0 +1,171 @@
+#
+# Structure format is:
+# $var[filter_number]{field}
+#
+# Defined fields: (* is required)
+# SEARCH(*): search filter
+# FROM(*): intput to subtitution regex
+# TO(*): resultant to substitution regex
+# DESC: description of filter rule
+#
+
+$#KSIRC_FILTER = 0;
+
+print "*** Loading filter parser...\n";
+
+$ksirc_dump = 0;
+
+sub hook_ksircfilter {
+ my($i) = 0;
+ for(; $i <= $#KSIRC_FILTER; $i++){
+ eval{
+ if($_[0] =~ m/$KSIRC_FILTER[$i]{'SEARCH'}/){
+ $_[0] =~ s/$KSIRC_FILTER[$i]{'FROM'}/$KSIRC_FILTER[$i]{'TO'}/eeg;
+ }
+ };
+ if ( $@ ){
+ (my $error = $@ ) =~ s/before HERE.*//;
+ my $desc = $KSIRC_FILTER[$i]{'DESC'};
+ splice(@KSIRC_FILTER, $i--, 1);
+ &tell("*\cbE\cb* Filter $desc disabled due to error: $error");
+ }
+ }
+}
+
+print "*** Filter Parser Loaded\n";
+
+addhook("print", "ksircfilter");
+
+sub cmd_ksircprintrule {
+ my($i) = 0;
+ for(; $i <= $#KSIRC_FILTER; $i++){
+ print STDOUT "*** Rule $i " . $KSIRC_FILTER[$i]{DESC} . ": if ". $KSIRC_FILTER[$i]{SEARCH} . " then ". $KSIRC_FILTER[$i]{FROM} . " -> " . $KSIRC_FILTER[$i]{TO} . "\n";
+ }
+}
+
+addcmd("ksircprintrule");
+&docommand("^alias prule ksircprintrule");
+
+#
+# Addrule command takes 4 or arguments seperated by " key==value !!! key2==value2 ||| etc"
+# 1. Name of rule
+# 2. Pattern to search for
+# 3. Substitution to take
+# 4. Sub to make
+
+sub cmd_ksircappendrule {
+ my($rule, %PARSED);
+ foreach $rule (split(/ !!! /, $args)){
+ my($key,$value) = split(/==/, $rule);
+ $PARSED{$key} = $value;
+ }
+ if($PARSED{'DESC'} && $PARSED{'SEARCH'} && $PARSED{'FROM'} && $PARSED{'TO'}){
+ my($i) = $#KSIRC_FILTER + 1;
+ my($key, $value);
+ while(($key, $value) = each %PARSED){
+ $KSIRC_FILTER[$i]{$key} = $value;
+ }
+# &tell("*** Added rule: " . $KSIRC_FILTER[$i]{'DESC'} . "\n");
+ }
+ else{
+ print "*E* Parse Error in Rule, format is: name !!! search !!! from !!! to\n";
+ }
+}
+
+addcmd("ksircappendrule");
+&docommand("^alias arule ksircappendrule");
+
+sub cmd_ksircclearrule {
+ @KSIRC_FILTER = ();
+# &tell("*** ALL FILTER RULES ERASED - DEFAULTS ADDED\n");
+}
+
+&addcmd("ksircclearrule");
+&docommand("^alias crule ksircclearrule");
+&docommand("^crule");
+
+sub cmd_ksircdelrule {
+ if($args =~ /^\d+$/){
+ splice(@KSIRC_FILTER, $args, 1);
+ print STDOUT "*** Deleted rule: $args\n";
+ }
+ else {
+ print STDOUT "*E* Syntax is ksircdelrule <rule number>\n";
+ }
+}
+
+&addcmd("ksircdelrule");
+&docommand("^alias drule ksircdelrule");
+
+sub hook_pong_lag {
+ if($_[0] =~ /.*:LAG ([0-9.]+)/){
+ my($_l) = $1;
+ my($_t) = kgettimeofday();
+ $_t -= $_l;
+ #print "*** Diff: $_t\n";
+ print "~!lag~*L* " . $_t . "\n";
+ }
+}
+
+&addhook("pong", "pong_lag");
+
+sub cmd_lag {
+ my($_t) = kgettimeofday();
+ # &docommand("^ctcp $nick LAG $_t");
+ &docommand("^quote PING :LAG $_t");
+ my $c;
+ foreach $c (@channels){
+ if($WHO_IGNORE{uc("$c")} != 1){
+ &docommand("^extnames $c");
+ }
+ }
+}
+
+
+eval {
+require 'sys/syscall.ph';
+};
+
+if(! defined(&SYS_gettimeofday)){
+ if(open(SYSCALL, "/usr/include/sys/syscall.h")){
+ my(@line) = grep(/define\s+SYS_gettimeofday/, <SYSCALL>);
+ close SYSCALL;
+ print "*E* Strange syscall.h, SYS_gettimeofday defined more than once!\n" if $#line > 1;
+ my($line) = "@line";
+ if($line =~ /SYS_gettimeofday\s+(\d+)/){
+ eval "sub SYS_gettimeofday () {$1;}";
+ print "*** Set time of day to: $1\n";
+ }
+ }
+
+ if(! defined(&SYS_gettimeofday)){
+ eval 'sub SYS_gettimeofday () {78;}';
+ print "*E* Unable to find SYS_gettimeofday, using LINUX default!\n";
+ }
+}
+
+
+sub kgettimeofday {
+ local($failed) = 0;
+ local($TIMEVAL_T) = "LL";
+ local($start) = pack($TIMEVAL_T, ());
+ eval{
+ (syscall( &SYS_gettimeofday, $start, 0) != -1) or $failed = 1;
+ };
+ if(!$failed){
+ my(@start) = unpack($TIMEVAL_T, $start);
+ $start[1] /= 1000000;
+ return $start[0] + $start[1];
+ }
+ else {
+ return time();
+ }
+}
+
+&addcmd("lag");
+
+sub hook_ksirc_notify_connected {
+ &print('~!all~`#ssfe#R reconnected');
+}
+
+&addhook("376", "ksirc_notify_connected"); # join on the "end of MOTD"
diff --git a/ksirc/getdate.c b/ksirc/getdate.c
new file mode 100644
index 00000000..a0ccb561
--- /dev/null
+++ b/ksirc/getdate.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+#include <time.h>
+
+int main() {
+ printf("#define COMPILE_DATE %d\n", (int) time(NULL));
+ return 0;
+}
diff --git a/ksirc/icons/Makefile.am b/ksirc/icons/Makefile.am
new file mode 100644
index 00000000..6cebb136
--- /dev/null
+++ b/ksirc/icons/Makefile.am
@@ -0,0 +1,5 @@
+# Directory where to install them
+ksircicondir = $(kde_datadir)/ksirc/icons
+ksircicon_ICON = AUTO
+
+KDE_ICON = ksirc
diff --git a/ksirc/icons/X.png b/ksirc/icons/X.png
new file mode 100644
index 00000000..382679c1
--- /dev/null
+++ b/ksirc/icons/X.png
Binary files differ
diff --git a/ksirc/icons/cr22-action-info.png b/ksirc/icons/cr22-action-info.png
new file mode 100644
index 00000000..3626ba7e
--- /dev/null
+++ b/ksirc/icons/cr22-action-info.png
Binary files differ
diff --git a/ksirc/icons/cr22-action-ksirc_dock.png b/ksirc/icons/cr22-action-ksirc_dock.png
new file mode 100644
index 00000000..34704755
--- /dev/null
+++ b/ksirc/icons/cr22-action-ksirc_dock.png
Binary files differ
diff --git a/ksirc/icons/ctcpping.png b/ksirc/icons/ctcpping.png
new file mode 100644
index 00000000..23ace0f1
--- /dev/null
+++ b/ksirc/icons/ctcpping.png
Binary files differ
diff --git a/ksirc/icons/error.png b/ksirc/icons/error.png
new file mode 100644
index 00000000..a2b60e4d
--- /dev/null
+++ b/ksirc/icons/error.png
Binary files differ
diff --git a/ksirc/icons/hi16-app-ksirc.png b/ksirc/icons/hi16-app-ksirc.png
new file mode 100644
index 00000000..b29cb89a
--- /dev/null
+++ b/ksirc/icons/hi16-app-ksirc.png
Binary files differ
diff --git a/ksirc/icons/hi22-app-ksirc.png b/ksirc/icons/hi22-app-ksirc.png
new file mode 100644
index 00000000..637db592
--- /dev/null
+++ b/ksirc/icons/hi22-app-ksirc.png
Binary files differ
diff --git a/ksirc/icons/hi32-app-ksirc.png b/ksirc/icons/hi32-app-ksirc.png
new file mode 100644
index 00000000..05068df6
--- /dev/null
+++ b/ksirc/icons/hi32-app-ksirc.png
Binary files differ
diff --git a/ksirc/icons/hi48-app-ksirc.png b/ksirc/icons/hi48-app-ksirc.png
new file mode 100644
index 00000000..025b2e2f
--- /dev/null
+++ b/ksirc/icons/hi48-app-ksirc.png
Binary files differ
diff --git a/ksirc/icons/hi64-app-ksirc.png b/ksirc/icons/hi64-app-ksirc.png
new file mode 100644
index 00000000..3f88f7f4
--- /dev/null
+++ b/ksirc/icons/hi64-app-ksirc.png
Binary files differ
diff --git a/ksirc/img/Makefile.am b/ksirc/img/Makefile.am
new file mode 100644
index 00000000..fdd16a3e
--- /dev/null
+++ b/ksirc/img/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS = emoticons
+
+# List of files to install
+emo_DATA = server.xpm channel.xpm channels.xpm \
+ ksirc_a.xpm ksirc_b.xpm \
+ arrow.png greenpin.png madsmiley.png smiley.png sadsmiley.png \
+ blueball.png star.png bluepin.png info.png info1.png \
+ mini-run.png ksirc.png sdi.png mdi.png \
+ dcc.png kick.png mode.png quit.png \
+ X.png dccget.png minus.png notice.png servinfo.png \
+ action.png dccsend.png ominus.png topic.png \
+ bminus.png elipsis.png oplus.png \
+ bplus.png error.png part.png \
+ ctcpping.png join.png plus.png
+
+# Directory where to install them
+emodir = $(kde_datadir)/ksirc/pics
+
diff --git a/ksirc/img/X.png b/ksirc/img/X.png
new file mode 100644
index 00000000..acc082dd
--- /dev/null
+++ b/ksirc/img/X.png
Binary files differ
diff --git a/ksirc/img/action.png b/ksirc/img/action.png
new file mode 100644
index 00000000..5b5463cb
--- /dev/null
+++ b/ksirc/img/action.png
Binary files differ
diff --git a/ksirc/img/arrow.png b/ksirc/img/arrow.png
new file mode 100644
index 00000000..a24c7cc7
--- /dev/null
+++ b/ksirc/img/arrow.png
Binary files differ
diff --git a/ksirc/img/blueball.png b/ksirc/img/blueball.png
new file mode 100644
index 00000000..308ef21f
--- /dev/null
+++ b/ksirc/img/blueball.png
Binary files differ
diff --git a/ksirc/img/bluepin.png b/ksirc/img/bluepin.png
new file mode 100644
index 00000000..4117235b
--- /dev/null
+++ b/ksirc/img/bluepin.png
Binary files differ
diff --git a/ksirc/img/bminus.png b/ksirc/img/bminus.png
new file mode 100644
index 00000000..b1baa0e0
--- /dev/null
+++ b/ksirc/img/bminus.png
Binary files differ
diff --git a/ksirc/img/bplus.png b/ksirc/img/bplus.png
new file mode 100644
index 00000000..169cc218
--- /dev/null
+++ b/ksirc/img/bplus.png
Binary files differ
diff --git a/ksirc/img/channel.xpm b/ksirc/img/channel.xpm
new file mode 100644
index 00000000..fdd11fe2
--- /dev/null
+++ b/ksirc/img/channel.xpm
@@ -0,0 +1,36 @@
+/* XPM */
+static char * people_xpm[] = {
+"24 24 9 1",
+" c None",
+". c #000000",
+"+ c #0000FF",
+"@ c #00C300",
+"# c #0000C0",
+"$ c #00C000",
+"% c #008000",
+"& c #FF0000",
+"* c #C00000",
+" .... .... ",
+" .++++. .@@@@. ",
+" .+++++#. .@$$$$%. ",
+" .+.++.#. .@.$$.%. ",
+" .+.++.#. .@.$$.%. ",
+" .+++++#. .... .@$$$$%. ",
+" .####. .&&&&. .%%%%. ",
+" .... .&&&&&*. .... ",
+" .++++..&.&&.*..@@@@. ",
+" .+++++#.&.&&.*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#..****..@$$$$%. ",
+" .+++++#. .... .@$$$$%. ",
+" .+++++#..&&&&..@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .+++++#.&&&&&*.@$$$$%. ",
+" .######.&&&&&*.%%%%%%. ",
+" .......&&&&&*....... ",
+" .******. ",
+" ........ "};
diff --git a/ksirc/img/channels.xpm b/ksirc/img/channels.xpm
new file mode 100644
index 00000000..cf0c4020
--- /dev/null
+++ b/ksirc/img/channels.xpm
@@ -0,0 +1,60 @@
+/* XPM */
+static char *channels[]={
+"22 22 35 1",
+". c None",
+"E c #3b200a",
+"D c #402611",
+"y c #523418",
+"w c #572c07",
+"d c #585858",
+"B c #592d08",
+"t c #5e513e",
+"x c #6bb0af",
+"g c #6e6c6e",
+"G c #6f6d6f",
+"e c #706e70",
+"s c #714d2b",
+"j c #757375",
+"m c #75b4b4",
+"k c #777477",
+"i c #777577",
+"r c #7e6f58",
+"l c #88bdbc",
+"C c #92c1c0",
+"F c #939393",
+"c c #949394",
+"b c #949494",
+"a c #969696",
+"o c #9e9e9e",
+"n c #a0a0a0",
+"h c #bebbf4",
+"# c #c3c3c3",
+"f c #c7c3ff",
+"A c #d16810",
+"z c #d17c31",
+"v c #e18a3c",
+"u c #f1c38c",
+"p c #f4bbf4",
+"q c #ffc3ff",
+"......................",
+"......................",
+"......................",
+"......................",
+".#abbbbbc#............",
+".addddddde............",
+".bdfffffdg............",
+".bdfffffdg............",
+".bdfffffdg............",
+".bdhhhhhdg............",
+".bdddddddg............",
+".#ijjjjjk#............",
+"....lm................",
+"....lm......#noooooo#.",
+"....lm......bdddddddg.",
+"....lm......bdpppqpdg.",
+"....rs......bdqqqqqdg.",
+"...tuvwxxxxxbdqqqqqdg.",
+"...yzABCCCCCbdqqqqqdg.",
+"....DE......FdddddddG.",
+"............#egggggG#.",
+"......................"};
diff --git a/ksirc/img/ctcpping.png b/ksirc/img/ctcpping.png
new file mode 100644
index 00000000..0164dffa
--- /dev/null
+++ b/ksirc/img/ctcpping.png
Binary files differ
diff --git a/ksirc/img/dcc.png b/ksirc/img/dcc.png
new file mode 100644
index 00000000..038bdca2
--- /dev/null
+++ b/ksirc/img/dcc.png
Binary files differ
diff --git a/ksirc/img/dccget.png b/ksirc/img/dccget.png
new file mode 100644
index 00000000..27539e0b
--- /dev/null
+++ b/ksirc/img/dccget.png
Binary files differ
diff --git a/ksirc/img/dccsend.png b/ksirc/img/dccsend.png
new file mode 100644
index 00000000..dbc183b6
--- /dev/null
+++ b/ksirc/img/dccsend.png
Binary files differ
diff --git a/ksirc/img/elipsis.png b/ksirc/img/elipsis.png
new file mode 100644
index 00000000..09d0e8af
--- /dev/null
+++ b/ksirc/img/elipsis.png
Binary files differ
diff --git a/ksirc/img/emoticons/Makefile.am b/ksirc/img/emoticons/Makefile.am
new file mode 100644
index 00000000..5e68be4a
--- /dev/null
+++ b/ksirc/img/emoticons/Makefile.am
@@ -0,0 +1,5 @@
+
+emodir = $(kde_datadir)/ksirc/pics/emoticons
+emo_DATA = biggrin.png clown.png cry.png devil.png frown.png \
+ heart.png loveit.png puh.png puh2.png redface.png sadley.png \
+ slime.png smile.png wink.png yummie.png
diff --git a/ksirc/img/emoticons/biggrin.png b/ksirc/img/emoticons/biggrin.png
new file mode 100644
index 00000000..b560b7ba
--- /dev/null
+++ b/ksirc/img/emoticons/biggrin.png
Binary files differ
diff --git a/ksirc/img/emoticons/clown.png b/ksirc/img/emoticons/clown.png
new file mode 100644
index 00000000..c6d78469
--- /dev/null
+++ b/ksirc/img/emoticons/clown.png
Binary files differ
diff --git a/ksirc/img/emoticons/cry.png b/ksirc/img/emoticons/cry.png
new file mode 100644
index 00000000..c2bf24da
--- /dev/null
+++ b/ksirc/img/emoticons/cry.png
Binary files differ
diff --git a/ksirc/img/emoticons/devil.png b/ksirc/img/emoticons/devil.png
new file mode 100644
index 00000000..2024b36c
--- /dev/null
+++ b/ksirc/img/emoticons/devil.png
Binary files differ
diff --git a/ksirc/img/emoticons/frown.png b/ksirc/img/emoticons/frown.png
new file mode 100644
index 00000000..1086d6b7
--- /dev/null
+++ b/ksirc/img/emoticons/frown.png
Binary files differ
diff --git a/ksirc/img/emoticons/heart.png b/ksirc/img/emoticons/heart.png
new file mode 100644
index 00000000..8f453cb6
--- /dev/null
+++ b/ksirc/img/emoticons/heart.png
Binary files differ
diff --git a/ksirc/img/emoticons/loveit.png b/ksirc/img/emoticons/loveit.png
new file mode 100644
index 00000000..e99f0921
--- /dev/null
+++ b/ksirc/img/emoticons/loveit.png
Binary files differ
diff --git a/ksirc/img/emoticons/puh.png b/ksirc/img/emoticons/puh.png
new file mode 100644
index 00000000..54a9dba1
--- /dev/null
+++ b/ksirc/img/emoticons/puh.png
Binary files differ
diff --git a/ksirc/img/emoticons/puh2.png b/ksirc/img/emoticons/puh2.png
new file mode 100644
index 00000000..4fade60e
--- /dev/null
+++ b/ksirc/img/emoticons/puh2.png
Binary files differ
diff --git a/ksirc/img/emoticons/redface.png b/ksirc/img/emoticons/redface.png
new file mode 100644
index 00000000..8d1872e7
--- /dev/null
+++ b/ksirc/img/emoticons/redface.png
Binary files differ
diff --git a/ksirc/img/emoticons/sadley.png b/ksirc/img/emoticons/sadley.png
new file mode 100644
index 00000000..6fd82d98
--- /dev/null
+++ b/ksirc/img/emoticons/sadley.png
Binary files differ
diff --git a/ksirc/img/emoticons/slime.png b/ksirc/img/emoticons/slime.png
new file mode 100644
index 00000000..1b515eb5
--- /dev/null
+++ b/ksirc/img/emoticons/slime.png
Binary files differ
diff --git a/ksirc/img/emoticons/smile.png b/ksirc/img/emoticons/smile.png
new file mode 100644
index 00000000..bd4ee806
--- /dev/null
+++ b/ksirc/img/emoticons/smile.png
Binary files differ
diff --git a/ksirc/img/emoticons/wink.png b/ksirc/img/emoticons/wink.png
new file mode 100644
index 00000000..727c9bdc
--- /dev/null
+++ b/ksirc/img/emoticons/wink.png
Binary files differ
diff --git a/ksirc/img/emoticons/yummie.png b/ksirc/img/emoticons/yummie.png
new file mode 100644
index 00000000..4cad21a3
--- /dev/null
+++ b/ksirc/img/emoticons/yummie.png
Binary files differ
diff --git a/ksirc/img/error.png b/ksirc/img/error.png
new file mode 100644
index 00000000..3809edbf
--- /dev/null
+++ b/ksirc/img/error.png
Binary files differ
diff --git a/ksirc/img/greenpin.png b/ksirc/img/greenpin.png
new file mode 100644
index 00000000..ac8a7622
--- /dev/null
+++ b/ksirc/img/greenpin.png
Binary files differ
diff --git a/ksirc/img/info.png b/ksirc/img/info.png
new file mode 100644
index 00000000..b0da7ff5
--- /dev/null
+++ b/ksirc/img/info.png
Binary files differ
diff --git a/ksirc/img/info1.png b/ksirc/img/info1.png
new file mode 100644
index 00000000..41c0d7f0
--- /dev/null
+++ b/ksirc/img/info1.png
Binary files differ
diff --git a/ksirc/img/join.png b/ksirc/img/join.png
new file mode 100644
index 00000000..d80b36a6
--- /dev/null
+++ b/ksirc/img/join.png
Binary files differ
diff --git a/ksirc/img/kick.png b/ksirc/img/kick.png
new file mode 100644
index 00000000..ef8501f5
--- /dev/null
+++ b/ksirc/img/kick.png
Binary files differ
diff --git a/ksirc/img/ksirc.png b/ksirc/img/ksirc.png
new file mode 100644
index 00000000..0a9a902e
--- /dev/null
+++ b/ksirc/img/ksirc.png
Binary files differ
diff --git a/ksirc/img/ksirc_a.xpm b/ksirc/img/ksirc_a.xpm
new file mode 100644
index 00000000..79286a2d
--- /dev/null
+++ b/ksirc/img/ksirc_a.xpm
@@ -0,0 +1,46 @@
+/* XPM */
+static char * ksirc2_xpm[] = {
+"32 32 11 1",
+" c None",
+". c #303030",
+"+ c #00FF00",
+"@ c #008200",
+"# c #0000FF",
+"$ c #00C300",
+"% c #0000C7",
+"& c #000086",
+"* c #000000",
+"= c #FFFF00",
+"- c #C7C300",
+" ....... ",
+" ..++++++@.. ",
+" .++++++++###. ",
+" .#++++++$@####. ",
+" .##+++++++$@####. ",
+" .###@++++++$@#####. ",
+" .$###+++$$+@#####+. ",
+" .#$###@+$@#$######$#. ",
+" .$@####@+##########@. ",
+" .%######@$@########%. ",
+" .%%########@######%%. ",
+" .%%%#######+$$###%%%. ",
+" .&%%%#####$+++$%%%%&. ",
+" .&$%%%%%##@$++$#%%%&. ",
+" .&$@%%%%%%&$$@%%%&. ",
+" .&&@%%%%%%@$@%%%&&. ",
+" .&&%%%%%%$@%%%&&. ",
+" *****&&&%%@@%%&&&***** ",
+" **=====**&&&&&&&&**=====** ",
+" *=========*&&&&&&*=========* ",
+" *===========*....*===========* ",
+" *=========*=* *==*=====*==* ",
+"*-========**==* *==**=====**==*",
+"*-========**==* *==*=======*==*",
+"*-============* *=============*",
+"*-============* *=============*",
+"*-=======*====* *===*=====.===*",
+" *-======***** *-==*===.===* ",
+" *-======* *-===*..===-* ",
+" *--====-* *--=====--* ",
+" **-----* **-----** ",
+" ***** ***** "};
diff --git a/ksirc/img/ksirc_b.xpm b/ksirc/img/ksirc_b.xpm
new file mode 100644
index 00000000..1741d641
--- /dev/null
+++ b/ksirc/img/ksirc_b.xpm
@@ -0,0 +1,48 @@
+/* XPM */
+static char * ksirc_xpm[] = {
+"32 32 13 1",
+" c None",
+". c #303030",
+"+ c #00FF00",
+"@ c #008200",
+"# c #0000FF",
+"$ c #00C300",
+"% c #0000C7",
+"& c #000086",
+"* c #000000",
+"= c #DFDFDF",
+"- c #A6A2A6",
+"; c #C7C3FF",
+"> c #FFC3FF",
+" ....... ",
+" ..++++++@.. ",
+" .++++++++###. ",
+" .#++++++$@####. ",
+" .##+++++++$@####. ",
+" .###@++++++$@#####. ",
+" .$###+++$$+@#####+. ",
+" .#$###@+$@#$######$#. ",
+" .$@####@+##########@. ",
+" .%######@$@########%. ",
+" .%%########@######%%. ",
+" .%%%#######+$$###%%%. ",
+" .&%%%#####$+++$%%%%&. ",
+" .&$%%%%%##@$++$#%%%&. ",
+" .&$@%%%%%%&$$@%%%&. ",
+" .&&@%%%%%%@$@%%%&&. ",
+" .&&%%%%%%$@%%%&&. ",
+" .&&&&%%@@%&&&&. ",
+" .&&&&&&&&&&&. ",
+" ..&&&&&&&.. ",
+" ********* ....... ********* ",
+" *========-* *========-* ",
+" *=*******-* *=*******-* ",
+" *=*;;;;;*-* *=*>>>>>*-* ",
+" *=*;;;;;*-* *=*>>>>>*-* ",
+" *=*;;;;;*-* *=*>>>>>*-* ",
+" *=*******-* *=*******-* ",
+" *---------* *---------* ",
+" *********** *********** ",
+"*==========-* *==========-* ",
+"*-----------* *-----------* ",
+" *********** *********** "};
diff --git a/ksirc/img/ksirc_dock.png b/ksirc/img/ksirc_dock.png
new file mode 100644
index 00000000..bcc6ec8f
--- /dev/null
+++ b/ksirc/img/ksirc_dock.png
Binary files differ
diff --git a/ksirc/img/ksirc_dock.xpm b/ksirc/img/ksirc_dock.xpm
new file mode 100644
index 00000000..802b8927
--- /dev/null
+++ b/ksirc/img/ksirc_dock.xpm
@@ -0,0 +1,192 @@
+/* XPM */
+static char * ksirc_dock_xpm[] = {
+"22 22 167 2",
+" c None",
+". c #DD3232",
+"+ c #E03333",
+"@ c #C01111",
+"# c #810000",
+"$ c #E03535",
+"% c #FEA8A8",
+"& c #FE7C7C",
+"* c #EE5050",
+"= c #B01313",
+"- c #E03D3D",
+"; c #FF7C7C",
+"> c #FF7E7E",
+", c #F97878",
+"' c #C03636",
+") c #C42525",
+"! c #F25959",
+"~ c #F97171",
+"{ c #EB7070",
+"] c #A33131",
+"^ c #AC1F1F",
+"/ c #B82B2B",
+"( c #982626",
+"_ c #560E0E",
+": c #2E2F2F",
+"< c #1F2020",
+"[ c #1B1C1C",
+"} c #080808",
+"| c #2C2C2C",
+"1 c #737373",
+"2 c #505050",
+"3 c #515151",
+"4 c #292929",
+"5 c #030305",
+"6 c #121B6B",
+"7 c #232430",
+"8 c #525252",
+"9 c #5F5F5F",
+"0 c #696969",
+"a c #555553",
+"b c #0E1128",
+"c c #142081",
+"d c #162391",
+"e c #14228F",
+"f c #16228F",
+"g c #131F89",
+"h c #09156F",
+"i c #1D299A",
+"j c #5058BF",
+"k c #5E66CA",
+"l c #152084",
+"m c #1F2134",
+"n c #575757",
+"o c #676767",
+"p c #717171",
+"q c #5C5C5B",
+"r c #0E0F1F",
+"s c #0C1358",
+"t c #0D1565",
+"u c #0C1564",
+"v c #0E1665",
+"w c #0C1460",
+"x c #060D4C",
+"y c #18249B",
+"z c #4D54BC",
+"A c #3E46AA",
+"B c #0D177A",
+"C c #000859",
+"D c #242633",
+"E c #606060",
+"F c #6F6F6F",
+"G c #797979",
+"H c #0C0C0C",
+"I c #02117B",
+"J c #1D29A1",
+"K c #232D9F",
+"L c #030F7B",
+"M c #000952",
+"N c #2E2E2F",
+"O c #666665",
+"P c #747474",
+"Q c #7E7E7E",
+"R c #626262",
+"S c #0D0D0D",
+"T c #192599",
+"U c #0C166F",
+"V c #01107E",
+"W c #111E97",
+"X c #0A1A98",
+"Y c #000F77",
+"Z c #343434",
+"` c #787878",
+" . c #818181",
+".. c #0B0B0B",
+"+. c #11209C",
+"@. c #0E1877",
+"#. c #010634",
+"$. c #000E70",
+"%. c #0C1A8E",
+"&. c #1724A6",
+"*. c #091A9A",
+"=. c #353433",
+"-. c #6B6B69",
+";. c #838382",
+">. c #61605F",
+",. c #0A0A09",
+"'. c #1728AE",
+"). c #1726A2",
+"!. c #09136C",
+"~. c #010635",
+"{. c #000B56",
+"]. c #020F75",
+"^. c #19259A",
+"/. c #404BCC",
+"(. c #3846C8",
+"_. c #222A84",
+":. c #2E325D",
+"<. c #585C79",
+"[. c #666A83",
+"}. c #6E728A",
+"|. c #4E5372",
+"1. c #12195B",
+"2. c #1F2EAE",
+"3. c #2B39C2",
+"4. c #1C2797",
+"5. c #060F5E",
+"6. c #000A58",
+"7. c #020E6F",
+"8. c #242E95",
+"9. c #6B74C8",
+"0. c #8089E1",
+"a. c #737EE1",
+"b. c #6D78DF",
+"c. c #6874DD",
+"d. c #6470DD",
+"e. c #5865DD",
+"f. c #4753D3",
+"g. c #323DB3",
+"h. c #131C78",
+"i. c #010846",
+"j. c #000842",
+"k. c #010A58",
+"l. c #0D1369",
+"m. c #1E2478",
+"n. c #293484",
+"o. c #313B8C",
+"p. c #323D90",
+"q. c #313C91",
+"r. c #212D89",
+"s. c #0F176E",
+"t. c #050B51",
+"u. c #121739",
+"v. c #24294F",
+"w. c #252B55",
+"x. c #292F58",
+"y. c #1D2143",
+"z. c #020416",
+"A. c #1E1E1D",
+"B. c #4A4A49",
+"C. c #585858",
+"D. c #5D5D5D",
+"E. c #353534",
+"F. c #030303",
+"G. c #1D1D1D",
+"H. c #262626",
+"I. c #242424",
+"J. c #0E0E0E",
+" ",
+" . + @ # ",
+" $ % & * = ",
+" - ; > , ' ",
+" ) ! ~ { ] ",
+" ^ / ( _ ",
+" ",
+" : < [ } ",
+" | 1 2 3 4 5 ",
+" 6 7 8 9 0 a b c d e f g h ",
+" i j k l m n o p q r s t u v w x ",
+" y z A B C D E F G 9 H ",
+" I J K L M N O P Q R S T U ",
+" V W X Y Z 0 ` .R .. +.@.#. ",
+" $.%.&.*. =.-.G ;.>.,. '.).!.~. ",
+" {.].^./.(._.:.<.[.}.|.1.2.3.4.5. ",
+" 6.7.8.9.0.a.b.c.d.e.f.g.h.i. ",
+" j.k.l.m.n.o.p.q.r.s.t. ",
+" u.v.w.x.y.z. ",
+" A.B.C.D.E.F. ",
+" G.H.I.J. ",
+" "};
diff --git a/ksirc/img/madsmiley.png b/ksirc/img/madsmiley.png
new file mode 100644
index 00000000..a8836f3e
--- /dev/null
+++ b/ksirc/img/madsmiley.png
Binary files differ
diff --git a/ksirc/img/mdi.png b/ksirc/img/mdi.png
new file mode 100644
index 00000000..9c053670
--- /dev/null
+++ b/ksirc/img/mdi.png
Binary files differ
diff --git a/ksirc/img/mini-run.png b/ksirc/img/mini-run.png
new file mode 100644
index 00000000..f9047029
--- /dev/null
+++ b/ksirc/img/mini-run.png
Binary files differ
diff --git a/ksirc/img/minus.png b/ksirc/img/minus.png
new file mode 100644
index 00000000..d451898d
--- /dev/null
+++ b/ksirc/img/minus.png
Binary files differ
diff --git a/ksirc/img/misc1.png b/ksirc/img/misc1.png
new file mode 100644
index 00000000..e5db7943
--- /dev/null
+++ b/ksirc/img/misc1.png
Binary files differ
diff --git a/ksirc/img/misc2.png b/ksirc/img/misc2.png
new file mode 100644
index 00000000..9037026d
--- /dev/null
+++ b/ksirc/img/misc2.png
Binary files differ
diff --git a/ksirc/img/misc3.png b/ksirc/img/misc3.png
new file mode 100644
index 00000000..a3085c8d
--- /dev/null
+++ b/ksirc/img/misc3.png
Binary files differ
diff --git a/ksirc/img/misc4.png b/ksirc/img/misc4.png
new file mode 100644
index 00000000..59dca5a9
--- /dev/null
+++ b/ksirc/img/misc4.png
Binary files differ
diff --git a/ksirc/img/misc5.png b/ksirc/img/misc5.png
new file mode 100644
index 00000000..4f4207d3
--- /dev/null
+++ b/ksirc/img/misc5.png
Binary files differ
diff --git a/ksirc/img/misc6.png b/ksirc/img/misc6.png
new file mode 100644
index 00000000..38cbd349
--- /dev/null
+++ b/ksirc/img/misc6.png
Binary files differ
diff --git a/ksirc/img/misc7.png b/ksirc/img/misc7.png
new file mode 100644
index 00000000..ba68e214
--- /dev/null
+++ b/ksirc/img/misc7.png
Binary files differ
diff --git a/ksirc/img/misc8.png b/ksirc/img/misc8.png
new file mode 100644
index 00000000..7ec56e20
--- /dev/null
+++ b/ksirc/img/misc8.png
Binary files differ
diff --git a/ksirc/img/misc9.png b/ksirc/img/misc9.png
new file mode 100644
index 00000000..d6776689
--- /dev/null
+++ b/ksirc/img/misc9.png
Binary files differ
diff --git a/ksirc/img/miscA.png b/ksirc/img/miscA.png
new file mode 100644
index 00000000..289040b5
--- /dev/null
+++ b/ksirc/img/miscA.png
Binary files differ
diff --git a/ksirc/img/mode.png b/ksirc/img/mode.png
new file mode 100644
index 00000000..bde2461f
--- /dev/null
+++ b/ksirc/img/mode.png
Binary files differ
diff --git a/ksirc/img/notice.png b/ksirc/img/notice.png
new file mode 100644
index 00000000..07f27e2d
--- /dev/null
+++ b/ksirc/img/notice.png
Binary files differ
diff --git a/ksirc/img/ominus.png b/ksirc/img/ominus.png
new file mode 100644
index 00000000..a69be335
--- /dev/null
+++ b/ksirc/img/ominus.png
Binary files differ
diff --git a/ksirc/img/oplus.png b/ksirc/img/oplus.png
new file mode 100644
index 00000000..25e248ff
--- /dev/null
+++ b/ksirc/img/oplus.png
Binary files differ
diff --git a/ksirc/img/part.png b/ksirc/img/part.png
new file mode 100644
index 00000000..dd1fe2bc
--- /dev/null
+++ b/ksirc/img/part.png
Binary files differ
diff --git a/ksirc/img/plus.png b/ksirc/img/plus.png
new file mode 100644
index 00000000..211bdc6f
--- /dev/null
+++ b/ksirc/img/plus.png
Binary files differ
diff --git a/ksirc/img/ppl.png b/ksirc/img/ppl.png
new file mode 100644
index 00000000..f5585b07
--- /dev/null
+++ b/ksirc/img/ppl.png
Binary files differ
diff --git a/ksirc/img/quit.png b/ksirc/img/quit.png
new file mode 100644
index 00000000..4aa8a09a
--- /dev/null
+++ b/ksirc/img/quit.png
Binary files differ
diff --git a/ksirc/img/sadsmiley.png b/ksirc/img/sadsmiley.png
new file mode 100644
index 00000000..11bd7d4c
--- /dev/null
+++ b/ksirc/img/sadsmiley.png
Binary files differ
diff --git a/ksirc/img/sdi.png b/ksirc/img/sdi.png
new file mode 100644
index 00000000..aa39c8c5
--- /dev/null
+++ b/ksirc/img/sdi.png
Binary files differ
diff --git a/ksirc/img/server.xpm b/ksirc/img/server.xpm
new file mode 100644
index 00000000..89bd5ac9
--- /dev/null
+++ b/ksirc/img/server.xpm
@@ -0,0 +1,320 @@
+/* XPM */
+static char *server[]={
+"22 22 295 2",
+"Qt c None",
+".m c #06a66a",
+"#6 c #078c5c",
+"#N c #079161",
+"bk c #079e54",
+"bM c #08905e",
+".N c #089161",
+"bN c #089261",
+".1 c #089262",
+"bB c #08955e",
+"#M c #08955f",
+".y c #089561",
+".M c #08965f",
+"#L c #089759",
+"bA c #08975e",
+"#f c #08975f",
+"bm c #08995a",
+"bz c #08995c",
+".x c #089a5b",
+".L c #089b59",
+".0 c #089c58",
+"#v c #089c59",
+"bl c #089d57",
+"#e c #089d58",
+"a8 c #08a050",
+"b2 c #0a8b56",
+"#K c #0a9a4d",
+"bO c #0b8a61",
+".z c #0b9161",
+".K c #0ba14c",
+".w c #0bae5e",
+"b1 c #0c8864",
+"#u c #0ca34b",
+"a7 c #0ca44a",
+"#d c #0ea349",
+".Z c #0ea34a",
+"#5 c #118c70",
+"a6 c #12a545",
+"bH c #147a84",
+"b3 c #147f64",
+".2 c #148c58",
+"#g c #148c59",
+"#w c #168b58",
+".O c #188c58",
+"aQ c #19a742",
+".v c #1abe52",
+"#O c #1b8a56",
+"#7 c #1d7f55",
+"bL c #1daaaa",
+"bj c #1dac81",
+"cg c #1e7e98",
+"cs c #1e86b9",
+".l c #1ec9ae",
+"a5 c #1fa952",
+"by c #20b5a2",
+"#4 c #219390",
+"b0 c #22a9d7",
+"ce c #258c9f",
+".J c #25a741",
+"bt c #26858f",
+"#J c #269d53",
+"cf c #277562",
+"#t c #29a940",
+".Y c #2da940",
+".k c #2dd2c2",
+"bd c #2e8089",
+"#3 c #2e9f90",
+"#c c #2fa940",
+"aP c #2faa3f",
+"bK c #32c5fc",
+"bG c #389098",
+"#1 c #39a43c",
+"az c #3cab3c",
+"bw c #3ccefb",
+"#8 c #3d7360",
+"#2 c #3dbaa0",
+".n c #3eaa80",
+"aO c #3eac3e",
+"bx c #3fd1fc",
+"cJ c #403f3f",
+"a. c #446b74",
+"cI c #454545",
+"#h c #458653",
+"aN c #45ab3b",
+"bX c #464646",
+".3 c #478753",
+"#x c #488553",
+"#I c #49a738",
+"bh c #4ad7ff",
+"cH c #4b4b4b",
+"bi c #4bd1ed",
+"#P c #4d8352",
+"cF c #4f4e4e",
+"ar c #4f4f4f",
+"ay c #4fb03c",
+"#R c #506763",
+"bs c #509da4",
+"a1 c #515151",
+".I c #52b03a",
+"a4 c #52c9c5",
+"cB c #535453",
+"aM c #53ad70",
+".j c #54dbeb",
+"aq c #555555",
+"ap c #565656",
+"bc c #57989f",
+".X c #57b038",
+".u c #57d074",
+"ca c #585858",
+"co c #595959",
+"ax c #59b23a",
+"#s c #59b83d",
+"aI c #5a5a5a",
+"au c #5aa64a",
+"cb c #5b5b5b",
+"ag c #5bac47",
+"#i c #5d7454",
+"at c #5db695",
+"#0 c #5eac70",
+"#b c #5eb941",
+"a2 c #5ed3f5",
+"ao c #5f5f5f",
+"av c #5fb038",
+"aw c #5fb538",
+"cE c #606160",
+"a3 c #60dcfe",
+"aK c #61d1ed",
+"an c #646464",
+"bF c #64a8af",
+"#Z c #65a846",
+".i c #65dfd0",
+"cA c #666665",
+"#Q c #667653",
+".W c #66ad33",
+".t c #66c552",
+"#y c #677a53",
+"cG c #686767",
+"#9 c #687659",
+"ct c #696969",
+"#H c #69c03d",
+"aL c #69cace",
+"af c #6ab936",
+"ah c #6cc4ad",
+"am c #6d6d6d",
+"bu c #6d9ca0",
+"bI c #6d9ea2",
+"bV c #6e9fa3",
+"be c #6f989d",
+"al c #707070",
+".H c #70c33f",
+"ak c #717171",
+"c# c #727272",
+"cp c #747474",
+"ae c #75bb34",
+"bU c #77a1a5",
+"bD c #77abb0",
+".A c #77b99c",
+".h c #78cec1",
+"aY c #79969a",
+"ab c #79bd31",
+"#r c #79cc48",
+"aa c #79cc52",
+"bZ c #79ccf3",
+"#Y c #7abf2e",
+"bE c #7bb5bb",
+"ad c #7bbd32",
+"ac c #7cbe31",
+".s c #7ccd7d",
+".C c #7cd5ed",
+"cy c #7d7d7d",
+"## c #7fb64d",
+"cd c #7fbfe0",
+"#a c #80ca72",
+"bY c #828282",
+"aJ c #848383",
+"ch c #848384",
+"c. c #848484",
+".o c #84bfa9",
+"br c #85bbc0",
+"#X c #85c22c",
+"aj c #868686",
+"cr c #86b8cb",
+".q c #86dee6",
+"bT c #87a9ac",
+".P c #87b091",
+".r c #87dec2",
+"b9 c #898989",
+".V c #89c431",
+"bJ c #89d1ef",
+"#. c #8ab755",
+"cq c #8ab9d0",
+"aX c #8ba5a7",
+"a# c #8c9a9e",
+"#W c #8dc42a",
+"#q c #8dc980",
+"b6 c #8e8e8e",
+"bS c #8eadb0",
+"bb c #8ebabe",
+"#U c #8ec929",
+"b8 c #8f8f8f",
+"#V c #8fc529",
+"bR c #90acae",
+"b5 c #939394",
+"#T c #93e270",
+"cK c #959493",
+"cx c #959595",
+".Q c #95d5e5",
+"#G c #95e077",
+"bv c #96d5ed",
+".G c #96d973",
+"b7 c #979797",
+"bC c #97afb1",
+"a0 c #999999",
+"b4 c #9a9a9a",
+".6 c #9aeaf1",
+"aZ c #9babad",
+"bf c #9c9c9c",
+"bg c #9ce1f2",
+"bP c #9d9d9d",
+"bW c #9db0b2",
+".9 c #9dc7a4",
+"as c #9e9e9e",
+"bp c #9ec3c7",
+"cC c #9f9f9f",
+".4 c #9fab98",
+"#j c #9faba6",
+"aA c #a0a0a0",
+".U c #a0cf27",
+"bn c #a1a1a1",
+"aW c #a1b9bb",
+"#D c #a1cb19",
+"a9 c #a3a2a2",
+"ck c #a3a3a3",
+"#C c #a3cd18",
+"aS c #a4a4a4",
+"bq c #a4cccf",
+"#A c #a4e6b9",
+"bo c #a5bcbf",
+".T c #a7c719",
+"#E c #a7d954",
+".F c #a8e27d",
+".D c #a9efcf",
+"cm c #acacac",
+"cz c #adadad",
+"bQ c #adbaba",
+"#F c #ade977",
+"aH c #aeaeae",
+"#z c #aeb1a1",
+"aV c #aec6c9",
+"cc c #afaead",
+"b. c #afc7c9",
+".g c #afd3d3",
+"#p c #afe1c2",
+".f c #b0d2d0",
+"cD c #b2b2b2",
+"ba c #b4d1d4",
+".p c #b4e4f0",
+"cv c #b5b5b5",
+"aU c #b5cdce",
+".8 c #b5da82",
+"b# c #b6d0d3",
+"#B c #b6e137",
+"cj c #b7b7b7",
+"#o c #b7e4be",
+"cw c #b8b8b8",
+"cn c #b9b9b9",
+".5 c #b9e0e9",
+".R c #bbf2b0",
+"cl c #bcbcbc",
+"cu c #bfbfbf",
+".B c #c1d8e0",
+".e c #c1ded3",
+"#n c #c1e22b",
+".E c #c1f263",
+"aR c #c3c2c1",
+"ci c #c5c5c6",
+".S c #c5e157",
+"#l c #c5f6a6",
+".7 c #c6e35a",
+".d c #c7e3e3",
+"#k c #c8edea",
+"aG c #cacaca",
+".c c #cbe3ec",
+"#S c #ccd2d4",
+"aT c #cddbdc",
+".b c #cde5ec",
+".a c #cfe5ea",
+"#m c #d2e91d",
+".# c #d3dfdb",
+"ai c #d6d5d4",
+"aF c #d6d6d6",
+"aB c #dcdcdc",
+"aE c #dedede",
+"aD c #e7e7e7",
+"aC c #ececec",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQt.#.a.b.c.d.eQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt.f.g.h.i.j.k.l.m.n.oQtQtQtQtQtQt",
+"QtQtQtQtQt.p.q.r.s.t.u.v.w.x.y.z.AQtQtQtQtQt",
+"QtQtQt.B.C.D.E.F.G.H.I.J.K.L.M.N.O.PQtQtQtQt",
+"QtQtQt.Q.R.S.T.U.V.W.X.Y.Z.0.M.1.2.3.4QtQtQt",
+"QtQt.5.6.7.8.9#.###a#b#c#d#e#f.1#g#h#i#jQtQt",
+"QtQt#k#l#m#n#o#p#q#r#s#t#u#v.M.1#w#x#y#zQtQt",
+"Qt#A#A#B#C#D#E#F#G#H#I#J#K#L#M#N#O#P#Q#R#SQt",
+"Qt#A#T#U#V#W#X#Y#Z#0#1#2#3#4#5#6#7#8#9a.a#Qt",
+"Qt#AaaabacadaeafagahaiajakalamanaoapaqarasQt",
+"Qt#AatauavawaxayazaiaAaBaCaCaDaEaFaGaHaIaJQt",
+"Qt.caKaLaMaNaOaPaQaRaSaTaUaVaWaXaYaZa0a1aJQt",
+"Qt.ca2a3a4a5a6a7a8aRa9b.b#babbbcbdbebfa1aJQt",
+"QtQtbgbhbibjbkblbmaRbnbobpbqbrbsbtbubfa1aJQt",
+"QtQtbvbwbxbybzbAbBaRaAbCbDbEbFbGbHbIbfa1aJQt",
+"QtQtQtbJbKbLbMbNbOaRbPbQbRbSbTbUbVbWa0bXbYQt",
+"QtQtQtQtbZb0b1b2b3aRb4b5b6b7b4b8b9c.c#cacbcc",
+"QtQtQtQtQtcdcecfcgchcicjckclcicmasbfcnb8cocp",
+"QtQtQtQtQtQtcqcrcsctcucjcvcwcncxc#cyczcAcBcp",
+"QtQtQtQtQtQtQtQtQtaocCcxcxcxcxckcDczb4cEcFcc",
+"QtQtQtQtQtQtQtQtQtcGcHcIcIcIcIcIcIcIcIcJcKQt"};
diff --git a/ksirc/img/servinfo.png b/ksirc/img/servinfo.png
new file mode 100644
index 00000000..65ca0284
--- /dev/null
+++ b/ksirc/img/servinfo.png
Binary files differ
diff --git a/ksirc/img/smiley.png b/ksirc/img/smiley.png
new file mode 100644
index 00000000..d57f7844
--- /dev/null
+++ b/ksirc/img/smiley.png
Binary files differ
diff --git a/ksirc/img/star.png b/ksirc/img/star.png
new file mode 100644
index 00000000..c0086462
--- /dev/null
+++ b/ksirc/img/star.png
Binary files differ
diff --git a/ksirc/img/start.xpm b/ksirc/img/start.xpm
new file mode 100644
index 00000000..7c54b4f4
--- /dev/null
+++ b/ksirc/img/start.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char * start_xpm[] = {
+"22 22 4 1",
+" c None",
+". c #000000000000",
+"X c #FFFFFFFFFFFF",
+"o c #820782078207",
+" ",
+" ",
+" ",
+" ",
+" ",
+" . . ",
+" . .. ",
+" . .X. ",
+" . .XX........ ",
+" . .XX XXXXXXX. ",
+" ..XX . ",
+" . .X . ",
+" . o.X ........ ",
+" . o.X.ooooooo ",
+" . o..o ",
+" . o.o ",
+" oo ",
+" o ",
+" ",
+" ",
+" ",
+" "};
diff --git a/ksirc/img/stop.xpm b/ksirc/img/stop.xpm
new file mode 100644
index 00000000..571cf656
--- /dev/null
+++ b/ksirc/img/stop.xpm
@@ -0,0 +1,120 @@
+/* XPM */
+static char *stop[]={
+"17 17 100 2",
+"Qt c None",
+".U c #1a1414",
+".1 c #1e1414",
+".I c #291414",
+"#E c #2f1414",
+".x c #341414",
+".n c #3b1414",
+"#w c #411414",
+".f c #541a16",
+".e c #5c1d18",
+".d c #5c1f1a",
+".c c #612621",
+".8 c #642722",
+".T c #671414",
+".a c #672f2a",
+"#v c #6a1414",
+".# c #6a3936",
+"#H c #6b1414",
+".b c #703b36",
+"#D c #761414",
+".0 c #7a1414",
+".7 c #7d1414",
+".H c #8e1414",
+".w c #901414",
+"#G c #911414",
+"#u c #961414",
+".m c #a15c54",
+".Z c #a61414",
+".G c #a71414",
+"#o c #ae7269",
+"#C c #b41414",
+".6 c #b51414",
+"#F c #b51616",
+"#B c #b71414",
+"#i c #b81515",
+"#A c #ba1414",
+".v c #bb4744",
+"#n c #bc1414",
+"#z c #be2727",
+".Y c #c0231c",
+"#x c #c0231f",
+".S c #c12c24",
+"#t c #c13228",
+"#p c #c15350",
+"#y c #c2271f",
+".5 c #c23333",
+"#s c #c55047",
+"#d c #c55252",
+"#k c #c56058",
+".u c #c56460",
+"#c c #c65451",
+"#m c #c75950",
+"#r c #c85148",
+".R c #ca544e",
+"#q c #ca5953",
+"#f c #ca655d",
+".t c #ca7169",
+"#b c #cb635c",
+".4 c #cb655e",
+"#. c #cb6661",
+"#h c #ce6f67",
+".F c #d16e66",
+".s c #d27e78",
+".O c #d46b66",
+".Q c #d46c67",
+"#l c #d47d77",
+".E c #d4817c",
+".W c #d77570",
+".l c #d7a29c",
+".y c #d97570",
+".r c #d98680",
+"#j c #d9a8a2",
+".K c #da7d7a",
+".3 c #da7f7b",
+".q c #da807c",
+".M c #db746f",
+".p c #db7975",
+".P c #dc7875",
+"#g c #dc7a78",
+"## c #dc7b77",
+".C c #dc7d78",
+".A c #dd7772",
+"#a c #dd7978",
+".X c #de7a78",
+".D c #de7e7a",
+".L c #df7e7d",
+".z c #df7f7b",
+".B c #df8580",
+".k c #e6b9b5",
+"#e c #e7b8b4",
+".j c #eabeb9",
+".9 c #eabfbb",
+".2 c #f1c8c6",
+".i c #f3d7d5",
+".V c #f3d8d7",
+".h c #f3dada",
+".g c #f6e7e7",
+".J c #f6eceb",
+".N c #fef3f3",
+".o c #ffffff",
+"QtQtQtQt.#.a.b.c.d.e.fQtQtQtQtQtQt",
+"QtQtQt.#.g.h.i.j.k.l.m.nQtQtQtQtQt",
+"QtQt.#.o.p.q.r.s.t.u.v.w.xQtQtQtQt",
+"Qt.#.o.y.z.A.B.C.D.E.F.G.H.IQtQtQt",
+".#.J.K.L.M.N.O.P.Q.N.R.S.G.T.UQtQt",
+".#.V.W.X.N.N.N.Q.N.N.N.Y.Z.0.1QtQt",
+".#.2.Q.3.Q.N.N.N.N.N.4.5.6.7.1QtQt",
+".8.9#.###a.Q.N.N.N#b#c#d.6.7.1QtQt",
+".d#e#f#g.Q.N.N.N.N.N#h#i.Z.7.1QtQt",
+".e#j#k#l.N.N.N#m.N.N.N#n.Z.0.1QtQt",
+".f#o#p#q#r.N#m#s#t.N.6.Z#u#v.UQtQt",
+"Qt#w.w.G#x#y#z#A#B#C.Z#u#D#EQtQtQt",
+"QtQt.x.H.G.6#F.6.Z.Z#G#D#EQtQtQtQt",
+"QtQtQt.x#H.0.7.7.7.0#v#EQtQtQtQtQt",
+"QtQtQtQt.U.1.1.1.1.1.UQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt"};
diff --git a/ksirc/img/tile.xpm b/ksirc/img/tile.xpm
new file mode 100644
index 00000000..ada13ff9
--- /dev/null
+++ b/ksirc/img/tile.xpm
Binary files differ
diff --git a/ksirc/img/topic.png b/ksirc/img/topic.png
new file mode 100644
index 00000000..3984ca18
--- /dev/null
+++ b/ksirc/img/topic.png
Binary files differ
diff --git a/ksirc/ioBroadcast.cpp b/ksirc/ioBroadcast.cpp
new file mode 100644
index 00000000..9bac4e30
--- /dev/null
+++ b/ksirc/ioBroadcast.cpp
@@ -0,0 +1,101 @@
+/**********************************************************************
+
+ The IO Broadcaster
+
+ $$Id$$
+
+ The IO broadcaster bassed on the ksircmessage receiver takes a
+ message and send it all ksircmessage receivers, except of course it
+ self.
+
+ It does the same with control_messages.
+
+ Implementation:
+
+ Make a QDictIterator, iterate over the windows sedning to each
+ broadcaster that's not itself.
+
+ *** NOTE! don't have 2 broadcasters or else they'll broadcast forever!
+
+**********************************************************************/
+
+#include "ioBroadcast.h"
+#include "ksircprocess.h"
+
+
+KSircIOBroadcast::~KSircIOBroadcast()
+{
+}
+
+void KSircIOBroadcast::sirc_receive(QCString str, bool)
+{
+
+ QDictIterator<KSircMessageReceiver> it(proc->getWindowList());
+
+ KSircMessageReceiver *dflt = (proc->getWindowList())["!default"];
+ if(dflt->getBroadcast() == TRUE)
+ dflt->sirc_receive(str, true);
+
+ it.toFirst();
+
+ while(it.current()){
+ if((it.current()->getBroadcast() == TRUE) && (it.current() != dflt))
+ it.current()->sirc_receive(str, true);
+ ++it;
+ }
+
+}
+
+void KSircIOBroadcast::control_message(int command, QString str)
+{
+
+ QDictIterator<KSircMessageReceiver> it(proc->getWindowList());
+
+ it.toFirst();
+
+ while(it.current()){
+ if(it.current() != this)
+ it.current()->control_message(command, str);
+ ++it;
+ }
+}
+
+
+filterRuleList *KSircIOBroadcast::defaultRules()
+{
+ filterRule *fr;
+ filterRuleList *frl = new filterRuleList();
+ frl->setAutoDelete(TRUE);
+ fr = new filterRule();
+ fr->desc = "Inverse to KSIRC inverse";
+ fr->search = ".*";
+ fr->from = "(?g)\\x16";
+ fr->to = "~r";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Underline to KSIRC underline";
+ fr->search = ".*";
+ fr->from = "(?g)\\x1f";
+ fr->to = "~u";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Bold to KSIRC bold";
+ fr->search = ".*";
+ fr->from = "(?g)\\x02";
+ fr->to = "~b";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Beep to KSIRC beep";
+ fr->search = ".*";
+ fr->from = "(?g)\\x07";
+ fr->to = "~g";
+ frl->append(fr);
+ fr = new filterRule();
+ fr->desc = "Ordinary to KSIRC ordinary";
+ fr->search = ".*";
+ fr->from = "(?g)\\x0f";
+ fr->to = "~c";
+ frl->append(fr);
+ return frl;
+
+}
diff --git a/ksirc/ioBroadcast.h b/ksirc/ioBroadcast.h
new file mode 100644
index 00000000..5be56390
--- /dev/null
+++ b/ksirc/ioBroadcast.h
@@ -0,0 +1,25 @@
+#ifndef KIOBROADCAST_H
+#define KIOBROADCAST_H
+
+#include "messageReceiver.h"
+
+class KSircIOBroadcast : public KSircMessageReceiver
+{
+public:
+ KSircIOBroadcast(KSircProcess *_proc) : KSircMessageReceiver(_proc)
+ {
+ proc = _proc;
+ setBroadcast(FALSE);
+ }
+ virtual ~KSircIOBroadcast();
+
+ virtual void sirc_receive(QCString str, bool broadcast);
+ virtual void control_message(int, QString);
+
+ virtual filterRuleList *defaultRules();
+
+private:
+ KSircProcess *proc;
+};
+
+#endif
diff --git a/ksirc/ioDCC.cpp b/ksirc/ioDCC.cpp
new file mode 100644
index 00000000..7c576904
--- /dev/null
+++ b/ksirc/ioDCC.cpp
@@ -0,0 +1,643 @@
+/**********************************************************************
+
+ The IO DCC Window
+
+ $$Id$$
+
+ Display DCC progress, etc. This in the future should be expanded.
+
+**********************************************************************/
+
+#include "ioDCC.h"
+#include "ksircprocess.h"
+#include "displayMgr.h"
+#include "control_message.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kpassivepopup.h>
+
+#include <qregexp.h>
+
+extern DisplayMgr *displayMgr;
+
+KSircIODCC::KSircIODCC(KSircProcess *_proc) :
+ QObject(),
+ KSircMessageReceiver(_proc)
+{
+ proc = _proc;
+ setBroadcast(FALSE);
+ mgr = new dccTopLevel(0x0, "dccTopLevel Manager");
+ displayMgr->newTopLevel(mgr, FALSE);
+ displayMgr->setCaption(mgr, proc->serverName() + i18n(" DCC Controller"));
+
+ connect(mgr->mgr(), SIGNAL(dccConnectClicked(dccItem *)), this, SLOT(dccConnectClicked(dccItem *)));
+ connect(mgr->mgr(), SIGNAL(dccResumeClicked(dccItem *)), this, SLOT(dccResumeClicked(dccItem *)));
+ connect(mgr->mgr(), SIGNAL(dccRenameClicked(dccItem *)), this, SLOT(dccRenameClicked(dccItem *)));
+ connect(mgr->mgr(), SIGNAL(dccAbortClicked(dccItem *)), this, SLOT(dccAbortClicked(dccItem *)));
+ connect(mgr->mgr(), SIGNAL(outputLine(QCString)), this, SIGNAL(outputLine(QCString)));
+}
+
+
+KSircIODCC::~KSircIODCC()
+{
+ // displayMgr->removeTopLevel(mgr);
+ if(mgr)
+ delete (dccTopLevel *) mgr;
+}
+
+void KSircIODCC::sirc_receive(QCString str, bool )
+{
+ if(!mgr)
+ return;
+ // Parse the string to find out what type it is.
+ // Note the order here.
+ // Most people tend to receive files, so let's
+ // parse the byte xfered messages first since there's lot's of them.
+ // The we get lots of send bytexfer messages so parse them second.
+ // Then we look at the one time start/stop messages. They only arrive
+ // once in a long long time (compared to the byte messages) so if it takes
+ // a few extra clock cycles to find them, who cares?
+ if(str.find("DCC GET read:", 0) != -1){ /*fold01*/
+ QRegExp rx("read: (.+) who: (.+) bytes: (.*)");
+ if(rx.search(str)){
+ QString key = QString("%1/%2").arg(rx.cap(1)).arg(rx.cap(2));
+ uint bytesXfer = rx.cap(3).toUInt();
+
+ //
+ // Only update the display for 1% intervals.
+ // LastSize + 1%Size must be less than the total xfered bytes.
+ //
+ if(DCCGetItems[key]){
+ DCCGetItems[key]->setReceivedBytes(bytesXfer/1024);
+ }
+ }
+ }
+ else if(str.find("DCC SEND write:", 0) != -1){ /*fold01*/
+ QRegExp rx("write: (.+) who: (.+) bytes: (.*)");
+ if(rx.search(str)){
+ QString key = QString("%1/%2").arg(rx.cap(1)).arg(rx.cap(2));
+ uint bytesXfer = rx.cap(3).toUInt();
+
+ if(DCCSendItems[key]){
+ DCCSendItems[key]->setReceivedBytes(bytesXfer/1024);
+ }
+ }
+ }
+ else if(str.find("INBOUND DCC SEND", 0) != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) file: (.+) size: (.*) ip: (.+) port: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString size = rx.cap(3);
+ QString ip = rx.cap(4);
+ //QSTring port = rx.cap(5);
+
+ int fileSize = size.toInt(); // Bytes per step
+ fileSize /= 1024;
+
+ // setup dcc dialog
+ displayMgr->show(mgr);
+ dccItem *it = mgr->mgr()->newGetItem(filename, who, dccItem::dccGotOffer, fileSize);
+ connect(it, SIGNAL(itemRenamed(dccItem *, QString, QString)),
+ this, SLOT(dccRenameDone(dccItem *, QString, QString)));
+ it->setWhoPostfix("(" + ip + ")");
+
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ /*
+ * don't add duplicates, this cuases real headaches
+ * for both us and the user.
+ */
+ QString renamed;
+ dccItem *old = DCCGetItems.take(key);
+ int i;
+ for(i = 0; i <= (int) DCCGetItems.count()+1; i++){
+ renamed = QString("%1 (finished %2)/%3").arg(filename).arg(i+1).arg(old->who());
+ if( DCCGetItems[renamed] == 0x0)
+ break;
+ }
+ old->changeFilename(QString("%1 (finished %2)").arg(filename).arg(i+1));
+ DCCGetItems.insert(renamed, it);
+ }
+
+ DCCGetItems.insert(key, it);
+ }
+ else {
+ kdDebug(5008) << "DCC SEND was unable to parse: " << str << endl;
+ }
+ }
+ else if(str.find("Sent DCC SEND", 0) != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) file: (.+) size: (.*)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString size = rx.cap(3);
+ int fileSize = size.toInt(); // Bytes per step
+ fileSize /= 1024;
+
+ // setup dcc dialog
+ displayMgr->show(mgr);
+ dccItem *it = mgr->mgr()->newSendItem(filename, who, dccItem::dccSentOffer, fileSize);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCSendItems[key]){
+ /*
+ * don't add duplicates, this cuases real headaches
+ * for both us and the user.
+ * We can get these on repeated sends or old files,
+ * just renamed them, their finished anyways
+ */
+ QString renamed;
+ dccItem *old = DCCSendItems.take(key);
+ int i;
+ for(i = 0; i <= (int) DCCSendItems.count()+1; i++){
+ renamed = QString("%1 (sent %2)/%3").arg(filename).arg(i+1).arg(old->who());
+ if( DCCSendItems[renamed] == 0x0)
+ break;
+ }
+ old->changeFilename(QString("%1 (sent %2)").arg(filename).arg(i+1));
+ DCCSendItems.insert(renamed, it);
+ }
+ DCCSendItems.insert(key, it);
+ }
+ else {
+ kdDebug(5008) << "DCC SENT was unable to parse: " << str << endl;
+ }
+ }
+ else if(str.find("DCC CHAT OFFERED", 0) != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) ip: (.+) port: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString ip = rx.cap(2);
+ QString port = rx.cap(3);
+ // setup dcc dialog
+ displayMgr->show(mgr);
+ dccItem *it = mgr->mgr()->newChatItem(who, dccItem::dccGotOffer);
+ connect(it, SIGNAL(itemRenamed(dccItem *, QString, QString)),
+ this, SLOT(dccRenameDone(dccItem *, QString, QString)));
+ it->setWhoPostfix("(" + ip + ")");
+ DCCChatItems.insert(who, it);
+ }
+ else {
+ kdDebug(5008) << "DCC CHAT SEND was unable to parse: " << str << endl;
+ }
+ }
+ else if(str.find("DCC CHAT SEND", 0) != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+
+ // setup dcc dialog
+ displayMgr->show(mgr);
+ dccItem *it = mgr->mgr()->newChatItem(who, dccItem::dccSentOffer);
+ connect(it, SIGNAL(itemRenamed(dccItem *, QString, QString)),
+ this, SLOT(dccRenameDone(dccItem *, QString, QString)));
+ DCCChatItems.insert(who, it);
+ }
+ else {
+ kdDebug(5008) << "DCC CHAT SEND was unable to parse: " << str << endl;
+ }
+ }
+ else if(str.find("DCC SEND terminated") != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) file: (.+) reason: (.*)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString error = rx.cap(3);
+ enum dccItem::dccStatus status = dccItem::dccDone;
+
+ if(error == "CLOSE"){
+ status = dccItem::dccCancel;
+ }
+ if(error != "OK"){
+ status = dccItem::dccError;
+ KPassivePopup::message(i18n("DCC SEND with %1 for %2 failed because of %3").arg(who).arg(filename).arg(error), mgr->mgr());
+ }
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCSendItems[key]){
+ kdDebug(5008) << "SendPercent: " << DCCSendItems[key]->getPercent() << endl;
+ if((status == dccItem::dccDone) && (DCCSendItems[key]->getPercent() < 100))
+ status = dccItem::dccError;
+ DCCSendItems[key]->changeStatus(status);
+ }
+ }
+ }
+ else if(str.find ("DCC GET terminated") != -1){ /*fold01*/
+ kdDebug(5008) << "Term: " << str << endl;
+ QRegExp rx("who: (.+) file: (.+) reason: (.*)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString error = rx.cap(3);
+ enum dccItem::dccStatus status = dccItem::dccDone;
+
+ if(error != "OK"){
+ status = dccItem::dccError;
+ KPassivePopup::message(i18n("DCC GET with %1 for %2 failed because of %3").arg(who).arg(filename).arg(error), mgr->mgr());
+ }
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ kdDebug(5008) << "GetPercent: " << DCCGetItems[key]->getPercent() << endl;
+ if((status == dccItem::dccDone) && (DCCGetItems[key]->getPercent() < 100))
+ status = dccItem::dccError;
+ DCCGetItems[key]->changeStatus(status);
+ }
+ }
+ else {
+ kdDebug(5008) << "DCC Get term failed to parse: " << str << endl;
+ }
+ }
+ else if(str.find("DCC GET resumed") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ dccItem *it = DCCGetItems[key];
+ kdDebug(5008) << "Got DCC GET resumed message..." << it->file() << endl;
+ if(it->status() == dccItem::dccWaitOnResume)
+ dccConnectClicked(it);
+ it->changeStatus(dccItem::dccResumed);
+ }
+ }
+ }
+ else if(str.find("DCC SEND resumed") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCSendItems[key]){
+ DCCSendItems[key]->changeStatus(dccItem::dccResumed);
+ }
+ }
+ }
+ else if(str.find("DCC GET established") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ DCCGetItems[key]->changeStatus(dccItem::dccRecving);
+ }
+ }
+ }
+ else if(str.find("DCC SEND established") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+) ip: (\\S+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString ip = rx.cap(3);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCSendItems[key]){
+ DCCSendItems[key]->setWhoPostfix("(" + ip + ")");
+ DCCSendItems[key]->changeStatus(dccItem::dccSending);
+ }
+ }
+ }
+ else if(str.find("DCC CHAT established") != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ if(DCCChatItems[who]){
+ DCCChatItems[who]->changeStatus(dccItem::dccOpen);
+ proc->new_toplevel(KSircChannel(proc->serverName(), QString("=") + who.lower()));
+ }
+ }
+ }
+ else if(str.find("DCC CHAT inbound established") != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) ip: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString ip = rx.cap(2);
+ if(DCCChatItems[who]){
+ DCCChatItems[who]->setWhoPostfix("(" + ip + ")");
+ DCCChatItems[who]->changeStatus(dccItem::dccOpen);
+ proc->new_toplevel(KSircChannel(proc->serverName(), QString("=") + who.lower()));
+ }
+ }
+ }
+ else if(str.find("DCC GET failed") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+) reason: (.*)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString error = rx.cap(3);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ DCCGetItems[key]->changeStatus(dccItem::dccError);
+ }
+ KPassivePopup::message(i18n("DCC Get with %1 for %2 failed because of %3").arg(who).arg(filename).arg(error), mgr->mgr());
+ }
+ }
+ else if(str.find("DCC CHAT failed") != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) reason: (.*)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString error = rx.cap(2);
+ if(DCCChatItems[who]){
+ DCCChatItems[who]->changeStatus(dccItem::dccError);
+ }
+ KPassivePopup::message(i18n("DCC Chat with %1 failed because of %2").arg(who).arg(error), mgr->mgr());
+ }
+ }
+ else if(str.find("DCC CHAT renamed") != -1){ /*FOLD01*/
+ QRegExp rx("who: (.+) to: (.+)");
+ if(rx.search(str)){
+ QString oldwho = rx.cap(1);
+ QString newwho = rx.cap(2);
+
+ QString oldwin = "=" + oldwho;
+ if(proc->getWindowList().find(oldwin)){
+ proc->getWindowList().find(oldwin)->control_message(CHANGE_CHANNEL,QString("=" +newwho).lower());
+ }
+ }
+ }
+ else if(str.find("Closing DCC GET") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+ QString key = QString("%1/%2").arg(filename).arg(who);
+ if(DCCGetItems[key]){
+ DCCGetItems[key]->changeStatus(dccItem::dccCancel);
+ }
+ }
+ }
+// else if(str.find("Closing DCC SEND") != -1){ /*FOLD01*/
+// QRegExp rx("who: (.+) file: (.+)");
+// if(rx.search(str)){
+// QString who = rx.cap(1);
+// QString filename = rx.cap(2);
+// QString key = QString("%1/%2").arg(filename).arg(who);
+// if(DCCSendItems[key]){
+// DCCSendItems[key]->changeStatus(dccItem::dccCancel);
+// }
+// }
+// }
+ else if(str.find("Closing DCC CHAT") != -1){ /*fold01*/
+ QRegExp rx("who: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ if(DCCChatItems[who]){
+ DCCChatItems[who]->changeStatus(dccItem::dccDone);
+ }
+ }
+ }
+ else if(str.find("No DCC SEND") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+
+ //QString key = QString("%1/%2").arg(filename).arg(who);
+
+ QPtrList<dccItem> toDel;
+ QDict<dccItem> new_list;
+ QDictIterator<dccItem> it( DCCSendItems );
+ for(;it.current(); ++it){
+ if((it.current()->who() == who) &&
+ (it.current()->file() == filename)
+ ){
+ toDel.append(it.current());
+ }
+ else {
+ new_list.insert(it.currentKey(), it.current());
+ }
+ }
+ DCCSendItems = new_list;
+ toDel.setAutoDelete(true);
+ toDel.clear();
+ }
+ }
+ else if(str.find("No DCC GET") != -1){ /*fold01*/
+ QRegExp rx("who: (.+) file: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+ QString filename = rx.cap(2);
+
+ QPtrList<dccItem> toDel;
+ QDict<dccItem> new_list;
+ QDictIterator<dccItem> it( DCCGetItems );
+ for(;it.current(); ++it){
+ if((it.current()->who() == who) &&
+ (it.current()->file() == filename)
+ ){
+ toDel.append(it.current());
+ }
+ else {
+ new_list.insert(it.currentKey(), it.current());
+ }
+ }
+ DCCGetItems = new_list;
+ toDel.setAutoDelete(true);
+ toDel.clear();
+ }
+ }
+ else if(str.find("No DCC CHAT") != -1){ /*fold01*/
+ QRegExp rx("who: (.+)");
+ if(rx.search(str)){
+ QString who = rx.cap(1);
+
+ QPtrList<dccItem> toDel;
+ QDict<dccItem> new_list;
+ QDictIterator<dccItem> it( DCCChatItems );
+ for(;it.current(); ++it){
+ if(it.current()->who() == who){
+ toDel.append(it.current());
+ }
+ else {
+ new_list.insert(it.currentKey(), it.current());
+ }
+ }
+ DCCChatItems = new_list;
+ toDel.setAutoDelete(true);
+ toDel.clear();
+ }
+ }
+ else{ /*FOLD01*/
+ proc->getWindowList()["!default"]->sirc_receive(str);
+ }
+
+}
+
+void KSircIODCC::control_message(int, QString)
+{
+}
+
+void KSircIODCC::cancelTransfer(QString filename)
+{
+ //if(DlgList[filename]){
+ //emit outputLine(DCCStatus[filename]->cancelMessage.ascii());
+ //delete DlgList[filename];
+ //DlgList.remove(filename);
+ //delete DCCStatus[filename];
+ //DCCStatus.remove(filename);
+ //}
+}
+
+void KSircIODCC::getFile()
+{
+ /*
+ QString text = pending->fileListing()->text(pending->fileListing()->currentItem());
+ int pos = text.find(" ", 0);
+ QString nick = text.mid(0, pos);
+ pos = text.find(" ", pos+1) + 1;
+ QString filename = text.mid(pos, text.length() - pos);
+ //if(DlgList[filename]->isVisible() == FALSE)
+ // DlgList[filename]->show();
+ QString command = "/dcc get " + nick + " " + filename + "\n";
+ emit outputLine(command.ascii());
+ for(uint i = 0; i < pending->fileListing()->count(); i++)
+ if(QString(pending->fileListing()->text(i)) == (nick + " offered " + filename))
+ pending->fileListing()->removeItem(i);
+
+ if(pending->fileListing()->count() == 0)
+ pending->hide();
+ */
+}
+
+void KSircIODCC::forgetFile()
+{
+ /*
+ QString text = pending->fileListing()->text(pending->fileListing()->currentItem());
+ int pos = text.find(" ", 0);
+ QString nick = text.mid(0, pos);
+ pos = text.find(" ", pos+1) + 1;
+ QString filename = text.mid(pos, text.length() - pos);
+ QString command = "/dcc close get " + nick + " " + filename + "\n";
+ emit outputLine(command.ascii());
+ for(uint i = 0; i < pending->fileListing()->count(); i++)
+ if(QString(pending->fileListing()->text(i)) == (nick + " offered " + filename))
+ pending->fileListing()->removeItem(i);
+
+ if(pending->fileListing()->count() == 0)
+ pending->hide();
+ */
+
+}
+
+filterRuleList *KSircIODCC::defaultRules()
+{
+
+// filterRule *fr;
+ filterRuleList *frl = new filterRuleList();
+ frl->setAutoDelete(TRUE);
+ /*
+ fr = new filterRule();
+ fr->desc = "Capture DCC IO Messages";
+ fr->search = "^\\*D\\*";
+ fr->from = "^";
+ fr->to = "~!dcc~";
+ frl->append(fr);
+ */
+ return frl;
+
+}
+
+void KSircIODCC::dccConnectClicked(dccItem *it)
+{
+ QString str;
+ kdDebug(5008) << "Got connect click on " << it->who() << " " << it->type() << endl;
+ switch(it->type()){
+ case dccItem::dccGet:
+ str = "/dcc get " + it->who() + " " + it->file() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ case dccItem::dccChat:
+ str = "/dcc chat " + it->who() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ default:
+ break;
+ }
+
+}
+void KSircIODCC::dccResumeClicked(dccItem *it)
+{
+ QString str;
+ kdDebug(5008) << "Got resume click on " << it->who() << " " << it->type() << endl;
+ switch(it->type()){
+ case dccItem::dccGet:
+ it->changeStatus(dccItem::dccWaitOnResume);
+ str = "/resume " + it->who() + " " + it->file() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ default:
+ break;
+ }
+
+}
+void KSircIODCC::dccRenameClicked(dccItem *it)
+{
+ it->doRename();
+}
+void KSircIODCC::dccAbortClicked(dccItem *it)
+{
+ QString str;
+ switch(it->status()){
+ case dccItem::dccDone:
+ case dccItem::dccCancel:
+ case dccItem::dccError:
+ switch(it->type()) {
+ case dccItem::dccGet:
+ DCCGetItems.remove(QString("%1/%2").arg(it->file()).arg(it->who()));
+ break;
+ case dccItem::dccSend:
+ DCCSendItems.remove(QString("%1/%2").arg(it->file()).arg(it->who()));
+ break;
+ case dccItem::dccChat:
+ DCCChatItems.remove(it->who());
+ break;
+ }
+ delete it;
+ break;
+ default:
+ switch(it->type()) {
+ case dccItem::dccGet:
+ str = "/dcc close get " + it->who() + " " + it->file() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ case dccItem::dccSend:
+ str = "/dcc close send " + it->who() + " " + it->file() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ case dccItem::dccChat:
+ str = "/dcc close chat " + it->who() + "\n";
+ emit outputLine(str.ascii());
+ break;
+ }
+ }
+}
+
+void KSircIODCC::dccRenameDone(dccItem *it, QString oldWho, QString oldFile)
+{
+ if(it->type() == dccItem::dccGet){
+ QString str = QString("/dcc rename %1 %2 %3\n").arg(oldWho).arg(oldFile).arg(it->file());
+ QString key = QString("%1/%2").arg(oldFile).arg(oldWho);
+ if(DCCGetItems[key]){
+ DCCGetItems.take(key);
+ QString newkey = QString("%1/%2").arg(it->file()).arg(it->who());
+ DCCGetItems.insert(newkey, it);
+ }
+ emit outputLine(str.ascii());
+ }
+ else if(it->type() == dccItem::dccChat){
+ if(DCCChatItems[oldWho]){
+ DCCChatItems.take(oldWho);
+ DCCChatItems.insert(it->who(), it);
+ }
+ QString str = QString("/dcc rchat %1 %2\n").arg(oldWho).arg(it->who());
+ emit outputLine(str.ascii());
+
+ }
+}
+
+void KSircIODCC::showMgr()
+{
+ displayMgr->show(mgr);
+}
+
+#include "ioDCC.moc"
diff --git a/ksirc/ioDCC.h b/ksirc/ioDCC.h
new file mode 100644
index 00000000..76a93941
--- /dev/null
+++ b/ksirc/ioDCC.h
@@ -0,0 +1,65 @@
+#ifndef KIODCCC_H
+#define KIODCCC_H
+
+#include <qdict.h>
+#include <qobject.h>
+#include <qguardedptr.h>
+
+#include "dccManager.h"
+#include "dccToplevel.h"
+#include "messageReceiver.h"
+
+class KSProgress;
+
+struct DCCInfo {
+ QString nick;
+ QString cancelMessage;
+ int LastSize;
+ int PercentSize;
+ bool inProgress;
+};
+
+class KSircIODCC : public QObject,
+ public KSircMessageReceiver
+{
+ Q_OBJECT
+public:
+ KSircIODCC(KSircProcess *_proc);
+ virtual ~KSircIODCC();
+
+ virtual void sirc_receive(QCString, bool broadcast);
+ virtual void control_message(int, QString);
+ virtual filterRuleList *defaultRules();
+
+ virtual void showMgr();
+
+protected slots:
+ void cancelTransfer(QString);
+ void getFile();
+ void forgetFile();
+
+ void dccConnectClicked(dccItem *);
+ void dccResumeClicked(dccItem *);
+ void dccRenameClicked(dccItem *);
+ void dccAbortClicked(dccItem *);
+
+ void dccRenameDone(dccItem *, QString, QString);
+
+signals:
+ void outputLine(QCString);
+
+private:
+// QDict<QProgressDialog> DlgList;
+ //QDict<KSProgress> DlgList;
+ //QDict<DCCInfo> DCCStatus;
+ QDict<dccItem> DCCGetItems;
+ QDict<dccItem> DCCSendItems;
+ QDict<dccItem> DCCChatItems;
+
+ //dccDialog *pending;
+ QGuardedPtr<dccTopLevel> mgr;
+
+ KSircProcess *proc;
+};
+
+#endif
diff --git a/ksirc/ioDiscard.cpp b/ksirc/ioDiscard.cpp
new file mode 100644
index 00000000..7ce4173e
--- /dev/null
+++ b/ksirc/ioDiscard.cpp
@@ -0,0 +1,23 @@
+/**********************************************************************
+
+ The IO Discarder
+
+ $$Id$$
+
+ Simple rule, junk EVERYTHING!!!
+
+**********************************************************************/
+
+#include "ioDiscard.h"
+
+KSircIODiscard::~KSircIODiscard()
+{
+}
+
+void KSircIODiscard::sirc_receive(QCString, bool)
+{
+}
+
+void KSircIODiscard::control_message(int, QString)
+{
+}
diff --git a/ksirc/ioDiscard.h b/ksirc/ioDiscard.h
new file mode 100644
index 00000000..e15c62da
--- /dev/null
+++ b/ksirc/ioDiscard.h
@@ -0,0 +1,20 @@
+#ifndef KIODISCARD_H
+#define KIODISCARD_H
+
+#include "messageReceiver.h"
+
+class KSircIODiscard : public KSircMessageReceiver
+{
+public:
+ KSircIODiscard(KSircProcess *_proc) : KSircMessageReceiver(_proc)
+ {
+ }
+ virtual ~KSircIODiscard();
+
+ virtual void sirc_receive(QCString, bool broadcast);
+ virtual void control_message(int, QString);
+
+private:
+};
+
+#endif
diff --git a/ksirc/ioLAG.cpp b/ksirc/ioLAG.cpp
new file mode 100644
index 00000000..50f3f082
--- /dev/null
+++ b/ksirc/ioLAG.cpp
@@ -0,0 +1,61 @@
+/**********************************************************************
+
+ The IO LAG Controller
+
+ $$Id$$
+
+**********************************************************************/
+
+#include "ioLAG.h"
+#include "control_message.h"
+#include "ksircprocess.h"
+
+#include <kdebug.h>
+
+KSircIOLAG::KSircIOLAG(KSircProcess *_proc)
+ : QObject(),
+ KSircMessageReceiver(_proc)
+{
+ proc = _proc;
+ setBroadcast(FALSE);
+ startTimer(30000);
+// startTimer(5000);
+//(proc->getWindowList())["!all"]->control_message(SET_LAG, "99");
+}
+
+
+KSircIOLAG::~KSircIOLAG()
+{
+ killTimers();
+}
+
+void KSircIOLAG::sirc_receive(QCString str, bool)
+{
+
+ if(str.contains("*L*")){
+ int s1, s2;
+ s1 = str.find("*L* ") + 4;
+ s2 = str.length();
+ if(s1 < 0 || s2 < 0){
+ kdDebug(5008) << "Lag mesage broken: " << str << endl;
+ return;
+ }
+ QString lag = str.mid(s1, s2 - s1);
+ // cerr << "Lag: " << str << endl;
+ // cerr << "Setting lag to: " << lag << endl;
+ (proc->getWindowList())["!all"]->control_message(SET_LAG, lag);
+ }
+
+}
+
+void KSircIOLAG::control_message(int, QString)
+{
+}
+
+void KSircIOLAG::timerEvent ( QTimerEvent * )
+{
+ QCString cmd = "/lag\n";
+ emit outputLine(cmd);
+}
+
+#include "ioLAG.moc"
diff --git a/ksirc/ioLAG.h b/ksirc/ioLAG.h
new file mode 100644
index 00000000..1c3bc00d
--- /dev/null
+++ b/ksirc/ioLAG.h
@@ -0,0 +1,31 @@
+#ifndef KIOLAG_H
+#define KIOLAG_H
+
+#include <qobject.h>
+
+#include "messageReceiver.h"
+
+class KSircIOLAG : public QObject,
+ public KSircMessageReceiver
+{
+ Q_OBJECT
+public:
+ KSircIOLAG(KSircProcess *_proc);
+ virtual ~KSircIOLAG();
+
+ virtual void sirc_receive(QCString, bool broadcast);
+ virtual void control_message(int, QString);
+
+protected slots:
+
+protected:
+ virtual void timerEvent ( QTimerEvent * );
+
+signals:
+ void outputLine(QCString);
+
+private:
+ KSircProcess *proc;
+};
+
+#endif
diff --git a/ksirc/ioNotify.cpp b/ksirc/ioNotify.cpp
new file mode 100644
index 00000000..14c6e9e8
--- /dev/null
+++ b/ksirc/ioNotify.cpp
@@ -0,0 +1,75 @@
+/**********************************************************************
+
+ IO Notify Messanger
+
+ $$Id$$
+
+**********************************************************************/
+
+#include "ioNotify.h"
+#include "ksircprocess.h"
+
+#include <kdebug.h>
+
+KSircIONotify::KSircIONotify(KSircProcess *_proc)
+ : QObject(),
+ KSircMessageReceiver(_proc)
+{
+ proc = _proc;
+ setBroadcast(FALSE);
+}
+
+
+KSircIONotify::~KSircIONotify()
+{
+}
+
+void KSircIONotify::sirc_receive(QCString str, bool)
+{
+ if(str.contains("*)*")){
+ int s1, s2;
+ s1 = str.find("Signon by") + 10;
+ s2 = str.find(" ", s1);
+ if(s1 < 0 || s2 < 0){
+ kdDebug(5008) << "Nick Notify mesage broken: " << str << endl;
+ return;
+ }
+ QString nick = str.mid(s1, s2 - s1);
+ emit notify_online(nick);
+ }
+ else if(str.contains("*(*")){
+ int s1, s2;
+ s1 = str.find("Signoff by") + 11;
+ s2 = str.find(" ", s1);
+ if(s1 < 0 || s2 < 0){
+ kdDebug(5008) << "Nick Notify mesage broken: " << str << endl;
+ return;
+ }
+ QString nick = str.mid(s1, s2 - s1);
+ emit notify_offline(nick);
+ }
+ else{
+ proc->getWindowList()["!default"]->sirc_receive(str);
+ kdDebug(5008) << "Nick Notifer got " << str << endl;
+ }
+}
+
+void KSircIONotify::control_message(int, QString)
+{
+}
+
+
+filterRuleList *KSircIONotify::defaultRules()
+{
+ filterRule *fr;
+ filterRuleList *frl = new filterRuleList();
+ frl->setAutoDelete(TRUE);
+ fr = new filterRule();
+ fr->desc = "Send Nick Notifies to notifier parser";
+ fr->search = "^\\*\\S?[\\(\\)]\\S?\\* ";
+ fr->from = "^";
+ fr->to = "~!notify~";
+ frl->append(fr);
+ return frl;
+}
+#include "ioNotify.moc"
diff --git a/ksirc/ioNotify.h b/ksirc/ioNotify.h
new file mode 100644
index 00000000..e95edabe
--- /dev/null
+++ b/ksirc/ioNotify.h
@@ -0,0 +1,31 @@
+#ifndef KIONOTIFY_H
+#define KIONOTIFY_H
+
+#include <qobject.h>
+
+#include "messageReceiver.h"
+
+class KSircIONotify : public QObject,
+ public KSircMessageReceiver
+{
+ Q_OBJECT
+public:
+ KSircIONotify(KSircProcess *_proc);
+ virtual ~KSircIONotify();
+
+ virtual void sirc_receive(QCString, bool broadcast);
+ virtual void control_message(int, QString);
+
+ virtual filterRuleList *defaultRules();
+
+signals:
+ virtual void notify_online(QString);
+ virtual void notify_offline(QString);
+
+protected slots:
+
+private:
+ KSircProcess *proc;
+};
+
+#endif
diff --git a/ksirc/iocontroller.cpp b/ksirc/iocontroller.cpp
new file mode 100644
index 00000000..61b0b49c
--- /dev/null
+++ b/ksirc/iocontroller.cpp
@@ -0,0 +1,426 @@
+/***********************************************************************
+
+ IO Controller Object
+
+ $$Id$$
+
+ Main io controller. Reads and writes strings to sirc. Input
+ received in the following 2 formats:
+
+ 1. ~window name~<message>
+ 2. <message>
+
+ each is handled diffrently. The window name is extracted from #1 and
+ if the window exists the message portion is sent to it. If the
+ window doesn't exist, or case 2 is found the window is sent to the
+ control window "!default". !default is NOT a constant window but
+ rather follows focus arround. This is the easiest way to solve the
+ problem of output to commands that don't have a fixed destination
+ window. (/whois, /help, etc )
+
+ Implementation:
+
+ Friends with KSircProcess allows easy access to TopList. Makes sence
+ and means that IOController has access to TopList, etc. The two work
+ closely together.
+
+ Variables:
+ holder: used to hold partital lines between writes.
+ *proc: the acutally sirc client process.
+ *ksircproc: the companion ksircprocess GUI controller
+ stdout_notif: access to SocketNotifier, why is this global to the
+ class?
+ counter: existance counter.
+
+ Functions:
+ public:
+ KSircIOController(KProcess*, KSircProcess*):
+ - Object constructor takes two arguements the KProcess
+ that holds a running copy of sirc.
+ - KSircProcess is saved for access latter to TopList.
+ - The receivedStdout signal from KProcess is connected to
+ stdout_read and the processExited is connected to the sircDied
+ slot.
+
+ ~KSircIOController: does nothing at this time.
+
+ public slots:
+ stdout_read(KProcess *, _buffer, buflen):
+ - Called by kprocess when data arrives.
+ - This function does all the parsing and sending of messages
+ to each window.
+
+ stderr_read(KProcess*, _buffer, buflen):
+ - Should be called for stderr data, not connected, does
+ nothing.
+
+ stdin_write(QString):
+ - Slot that get's connected to by KSircProcess to each
+ window. QString shold be written un touched! Let the
+ writter figure out what ever he wants to do.
+
+ sircDied:
+ - Should restart sirc or something....
+ - Becarefull not to get it die->start->die->... etc
+
+***********************************************************************/
+
+#include <config.h>
+
+#include "ksopts.h"
+#include "control_message.h"
+#include "iocontroller.h"
+#include "ksircprocess.h"
+#include "messageReceiver.h"
+#include "ksopts.h"
+
+#include <qlistbox.h>
+#include <qtextcodec.h>
+#include <kcharsets.h>
+#include <kglobal.h>
+#include <qpopupmenu.h>
+
+#include <kdebug.h>
+#include <kdeversion.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kfiledialog.h>
+
+int KSircIOController::counter = 0;
+
+KSircIOController::KSircIOController(KProcess *_proc, KSircProcess *_ksircproc)
+ : QObject()
+{
+
+ counter++;
+
+ proc = _proc; // save proc
+ ksircproc = _ksircproc; // save ksircproce
+
+ send_buf = 0x0;
+ m_debugLB = 0;
+
+ // Connect the data arrived
+ // to sirc receive for adding
+ // the main text window
+ connect(proc, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ this, SLOT(stdout_read(KProcess*, char*, int)));
+
+ // Connect the stderr data
+ // to sirc receive for adding
+ // the main text window
+ connect(proc, SIGNAL(receivedStderr(KProcess *, char *, int)),
+ this, SLOT(stderr_read(KProcess*, char*, int)));
+
+ connect(proc, SIGNAL(processExited(KProcess *)),
+ this, SLOT(sircDied(KProcess *)));
+ // Notify on sirc dying
+ connect(proc, SIGNAL(wroteStdin(KProcess*)),
+ this, SLOT(procCTS(KProcess*)));
+ proc_CTS = TRUE;
+#if 0
+ showDebugTraffic(true);
+#endif
+}
+
+void my_print(const char *c){
+ while(*c != 0x0){
+ if(*c & 0x80)
+ fprintf(stderr, "<%02X>", 0xff & *c);
+ else
+ fprintf(stderr, "%c", *c);
+ c++;
+ }
+ fprintf(stderr, "\n");
+}
+
+void KSircIOController::stdout_read(KProcess *, char *_buffer, int buflen)
+{
+
+ /*
+
+ Main reader reads from sirc and ships it out to the right
+ (hopefully) window.
+
+ Problem trying to solve:
+
+ _buffer holds upto 1024 (it seems) block of data. We need to
+ take it, split into lines figure out where each line should go,
+ and ship it there.
+
+ We watch for broken end of lines, ie partial lines and reattach
+ them to the front on the next read.
+
+ We also stop all processing in the windows while writting the
+ lines.
+
+ Implementation:
+
+ Variables:
+ _buffer original buffer, holds just, icky thing, NOT NULL terminated!
+ buf: new clean just the right size buf that is null terminated.
+ pos, pos2, pos3 used to cut the string up into peices, etc.
+ name: destination window.
+ line: line to ship out.
+
+ Steps:
+ 1. read and copy buffer, make sure it's valid.
+ 2. If we're holding a broken line, append it.
+ 3. Check for a broken line, and save the end if it is.
+ 4. Stop updates in all windows.
+ 5. Step through the data and send the lines out to the right
+ window.
+ 6. Cleanup and continue.
+
+ */
+
+ int pos,pos2,pos3;
+ QCString name, line;
+
+ assert(_buffer != 0);
+ assert(buflen > 0);
+
+ QCString buffer(_buffer, buflen+1);
+ //fprintf(stderr, "first print: \n");
+ //my_print(buffer);
+ //kdDebug(5008) << "<-- read: " << buffer << endl;
+ name = "!default";
+
+
+ if(holder.length() > 0){
+ buffer.prepend(holder);
+ holder.truncate(0);
+ }
+
+ if(buffer[buffer.length()-1] != '\n'){
+ pos = buffer.findRev('\n');
+ if(pos != -1){
+ holder = buffer.right(buffer.length()-(pos+1));
+ buffer.truncate(pos+1);
+ }
+ else {
+ /* there is _NO_ linefeeds in this line at all, means we're
+ * only part of a string, buffer it all!!
+ * (lines are linefeed delimeted)
+ */
+ holder = buffer;
+ return;
+ }
+ }
+
+ pos = pos2 = 0;
+
+ KSircMessageReceiver * rec = ksircproc->TopList["!all"];
+
+ if (0 == rec)
+ {
+ return;
+ }
+
+ rec->control_message(STOP_UPDATES, "");
+ if(m_debugLB)
+ m_debugLB->setUpdatesEnabled(false);
+
+ do{
+ pos2 = buffer.find('\n', pos);
+
+ if(pos2 == -1)
+ pos2 = buffer.length();
+
+ line = buffer.mid(pos, pos2 - pos);
+ if(m_debugLB){
+ QString s = QString::fromUtf8(line);
+ m_debugLB->insertItem(s);
+ }
+
+ //kdDebug(5008) << "Line: " << line << endl;
+
+ if((line.length() > 0) && (line[0] == '~')){
+ pos3 = line.find('~', 1);
+ if(pos3 > 1){
+ name = line.mid(1,pos3-1).lower();
+ name = name.lower();
+ line.remove(0, pos3+1);
+ }
+ }
+ QString enc = KGlobal::charsets()->encodingForName( ksopts->channel["global"]["global"].encoding );
+ QTextCodec *qtc = KGlobal::charsets()->codecForName( enc );
+ QString qsname = qtc->toUnicode(name);
+ /*
+ char *b = qstrdup(line);
+ kdDebug(5008) << "----------------------------------------" << endl;
+ kdDebug(5008) << "Line: " << b << endl;
+ fprintf(stderr, "My_print: " ); my_print(b);
+ fprintf(stderr, "fprintf: %s\n", (const char *)b);
+ kdDebug(5008) << "Codec: " << qtc->name() << " (" << ksopts->channel["global"]["global"].encoding << ")" << " Name: " << name << " qsname: " << qsname << endl;
+ kdDebug(5008) << "Line(de): " << qtc->toUnicode(b) << endl;
+ kdDebug(5008) << "----------------------------------------" << endl;
+ */
+
+ if(!(ksircproc->TopList[qsname])){
+ // Ignore ssfe control messages with `
+ // we left channel, don't open a window for a control message
+ bool noticeCreate = true;
+ if(ksopts->autoCreateWinForNotice == false && (line[0] == '-' || line[0] == '*'))
+ noticeCreate = false;
+ if(ksopts->autoCreateWin == TRUE && line[0] != '`' && line[1] != '#' && line[1] != '&' && noticeCreate) {
+ //kdDebug(5008) << "Creating window for: " << qsname << " because of: " << line.data() << endl;
+ ksircproc->new_toplevel(KSircChannel(ksircproc->serverName(), qsname));
+ }
+ if (!ksircproc->TopList[qsname]) {
+ qsname = "!default";
+ if(line[0] == '`')
+ qsname = "!discard";
+ }
+ assert(ksircproc->TopList[qsname]);
+ }
+
+ ksircproc->TopList[qsname]->sirc_receive(line);
+
+
+ pos = pos2+1;
+ } while((uint) pos < buffer.length());
+
+ ksircproc->TopList["!all"]->control_message(RESUME_UPDATES, "");
+ if(m_debugLB){
+ m_debugLB->triggerUpdate(true);
+ m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight()-m_debugLB->visibleHeight());
+ m_debugLB->setUpdatesEnabled(true);
+ m_debugLB->triggerUpdate(false);
+ }
+
+
+}
+
+KSircIOController::~KSircIOController()
+{
+ delete m_debugLB;
+}
+
+void KSircIOController::stderr_read(KProcess *p, char *b, int l)
+{
+ stdout_read(p, b, l);
+}
+
+void KSircIOController::stdin_write(QCString s)
+{
+ if (!proc->isRunning())
+ {
+ kdDebug(5008) << "writing to a dead process! (" << s << ")\n";
+ return;
+ }
+
+ //kdDebug(5008) << "--> wrote: " << s;
+ buffer += s;
+ //fprintf(stderr, "Buffer output: ");
+ //my_print(buffer);
+
+ if(proc_CTS == TRUE){
+ int len = buffer.length();
+ if(send_buf != 0x0){
+ qWarning("KProcess barfed in all clear signal again");
+ delete[] send_buf;
+ }
+ send_buf = new char[len];
+ memcpy(send_buf, buffer.data(), len);
+ if(proc->writeStdin(send_buf, len) == FALSE){
+ kdDebug(5008) << "Failed to write but CTS HIGH! Setting low!: " << s << endl;
+ }
+ else{
+ if(m_debugLB){
+ QString s = QString::fromUtf8(buffer);
+ m_debugLB->insertItem(s);
+ m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight());
+ }
+ buffer.truncate(0);
+ }
+ proc_CTS = FALSE;
+ }
+
+ if(buffer.length() > 5000){
+ kdDebug(5008) << "IOController: KProcess barfing again!\n";
+ }
+ // write(sirc_stdin, s, s.length());
+
+}
+
+void KSircIOController::sircDied(KProcess *process)
+{
+ if ( process->exitStatus() == 0 )
+ return;
+ kdDebug(5008) << "IOController: KProcess died!\n";
+ ksircproc->TopList["!all"]->sirc_receive("*E* DSIRC IS DEAD");
+ ksircproc->TopList["!all"]->sirc_receive("*E* KSIRC WINDOW HALTED");
+ ksircproc->TopList["!all"]->sirc_receive( QCString( "*E* Tried to run: " ) + KGlobal::dirs()->findExe("dsirc").ascii() + QCString( "\n" ) );
+ ksircproc->TopList["!all"]->sirc_receive("*E* DID YOU READ THE INSTALL INTRUCTIONS?");
+}
+
+void KSircIOController::procCTS ( KProcess *)
+{
+ proc_CTS = true;
+ delete[] send_buf;
+ send_buf = 0x0;
+ if(!buffer.isEmpty()){
+ QCString str = "";
+ stdin_write(str);
+ }
+}
+
+void KSircIOController::showContextMenuOnDebugWindow(QListBoxItem *, const QPoint &pos)
+{
+ if (!m_debugLB)
+ return;
+
+ QPopupMenu popup(m_debugLB);
+ popup.insertItem("Save Contents to File...", 1);
+ if (popup.exec( pos ) != 1)
+ return;
+
+ QString path = KFileDialog::getSaveFileName();
+ if (path.isEmpty())
+ return;
+
+ QFile file(path);
+ if (!file.open(IO_WriteOnly))
+ return;
+
+ QTextStream stream(&file);
+
+ for (uint i = 0; i < m_debugLB->count(); ++i)
+ stream << m_debugLB->text(i) << endl;
+}
+
+void KSircIOController::showDebugTraffic(bool show)
+{
+ kdDebug(5008) << "Got show request: " << show << endl;
+ if(m_debugLB == 0 && show == true){
+ m_debugLB = new QListBox(0x0, QCString(this->name()) + "_debugWindow");
+ m_debugLB->resize(600, 300);
+ m_debugLB->show();
+ connect(m_debugLB,SIGNAL(contextMenuRequested(QListBoxItem *,const QPoint &)),
+ this,SLOT(showContextMenuOnDebugWindow(QListBoxItem *,const QPoint &)));
+ }
+ else if(m_debugLB != 0 && show == false){
+ delete m_debugLB;
+ m_debugLB = 0x0;
+ }
+
+}
+
+bool KSircIOController::isDebugTraffic()
+{
+ if(m_debugLB != 0)
+ return true;
+ else
+ return false;
+}
+
+void KSircIOController::appendDebug(QString s)
+{
+ if(m_debugLB){
+ m_debugLB->insertItem(s);
+ m_debugLB->setContentsPos( 0, m_debugLB->contentsHeight()-m_debugLB->visibleHeight());
+ }
+}
+
+#include "iocontroller.moc"
diff --git a/ksirc/iocontroller.h b/ksirc/iocontroller.h
new file mode 100644
index 00000000..76c97e18
--- /dev/null
+++ b/ksirc/iocontroller.h
@@ -0,0 +1,54 @@
+#ifndef IOCONTROL_H
+#define IOCONTROL_H
+
+#include <qobject.h>
+
+class KProcess;
+class KSircProcess;
+class QListBox;
+class QListBoxItem;
+
+class KSircIOController : public QObject
+{
+
+ Q_OBJECT
+ friend class KSircProcess;
+public:
+ KSircIOController(KProcess *, KSircProcess *);
+ virtual ~KSircIOController();
+
+ void showDebugTraffic(bool show);
+ bool isDebugTraffic();
+
+public slots:
+ virtual void stdout_read(KProcess *proc, char *_buffer, int buflen);
+ virtual void stderr_read(KProcess *proc, char *_buffer, int buflen);
+
+ virtual void stdin_write(QCString);
+
+ virtual void sircDied(KProcess *);
+
+ virtual void appendDebug(QString);
+
+
+protected slots:
+ virtual void procCTS(KProcess *);
+
+private slots:
+ void showContextMenuOnDebugWindow( QListBoxItem *, const QPoint &pos );
+
+private:
+ QCString holder;
+ bool proc_CTS;
+ KProcess *proc;
+ KSircProcess *ksircproc;
+ char *send_buf;
+
+ QCString buffer;
+
+ static int counter;
+
+ QListBox *m_debugLB;
+};
+
+#endif
diff --git a/ksirc/ksirc.cpp b/ksirc/ksirc.cpp
new file mode 100644
index 00000000..eddb450a
--- /dev/null
+++ b/ksirc/ksirc.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************
+
+ Main KSirc start
+
+ $$Id$$
+
+ Main start file that defines 3 global vars, etc
+
+*************************************************************************/
+
+/*
+ * Needed items
+ * 4. Send a /quit and/or kill dsirc on exit
+ * */
+
+#include <stdlib.h>
+
+#include <qsessionmanager.h>
+
+#include <kuniqueapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+//#include "cdate.h"
+#include "ksopts.h"
+#include "servercontroller.h"
+#include "version.h"
+
+static const char description[] =
+ I18N_NOOP("KDE IRC client");
+
+//QDict<KSircTopLevel> TopList;
+//QDict<KSircMessageReceiver> TopList;
+
+class KCmdLineOptions options[] =
+{
+ { "nick <nickname>", I18N_NOOP( "Nickname to use" ), 0 } ,
+ { "server <server>", I18N_NOOP( "Server to connect to on startup" ), 0 },
+ { "channel <#channel>", I18N_NOOP( "Channel to connect to on startup" ), 0 },
+ { "o", 0, 0 },
+ { "noautoconnect", I18N_NOOP( "Do not autoconnect on startup" ), 0 },
+ KCmdLineLastOption
+};
+
+class KSircSessionManaged : public KSessionManaged
+{
+public:
+ KSircSessionManaged() {}
+
+ virtual bool commitData( QSessionManager &sm )
+ {
+ servercontroller *controller = servercontroller::self();
+ if ( !controller || !sm.allowsInteraction() ) return true;
+
+ // if the controller is hidden KMWSessionManaged won't send the fake close event.
+ // we want it in any way however.
+ if ( controller->isHidden() ) {
+ QCloseEvent e;
+ QApplication::sendEvent( controller, &e );
+ }
+
+ return true;
+ }
+};
+
+extern "C" KDE_EXPORT int kdemain( int argc, char ** argv )
+{
+ KAboutData aboutData( "ksirc", I18N_NOOP("KSirc"),
+ KSIRC_VERSION, description, KAboutData::License_Artistic,
+ I18N_NOOP("(c) 1997-2002, The KSirc Developers"));
+ aboutData.addAuthor("Andrew Stanley-Jones",I18N_NOOP("Original Author"), "asj-ksirc@cban.com");
+ aboutData.addAuthor("Waldo Bastian",0, "bastian@kde.org");
+ aboutData.addAuthor("Carsten Pfeiffer",0, "pfeiffer@kde.org");
+ aboutData.addAuthor("Malte Starostik",0, "malte@kde.org");
+ aboutData.addAuthor("Daniel Molkentin",0, "molkentin@kde.org");
+ aboutData.addAuthor("Simon Hausmann",0, "hausmann@kde.org");
+ aboutData.addAuthor("Alyssa Mejawohld", I18N_NOOP("Icons Author"), "amejawohld@bellsouth.net");
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions( options );
+ KUniqueApplication::addCmdLineOptions();
+
+ if (!KUniqueApplication::start()) {
+ exit(0);
+ }
+
+ // Start the KDE application
+ KUniqueApplication app;
+
+ KSircSessionManaged sm;
+
+ // Options
+ KSOptions opts;
+ opts.load();
+
+ servercontroller *sc = new servercontroller(0, "servercontroller");
+ app.setMainWidget(sc);
+
+ if (KMainWindow::canBeRestored(1))
+ {
+ sc->restore(1, false );
+ }
+ else
+ { // no Session management -> care about docking, geometry, etc.
+
+ if( !opts.geometry.isEmpty() )
+ sc->setGeometry( opts.geometry );
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ QCString nickName = args->getOption( "nick" );
+ QCString server = args->getOption( "server" );
+ QCString channel = args->getOption( "channel" );
+
+ if ( !nickName.isEmpty() )
+ ksopts->server["global"].nick = nickName;
+
+ if ( !server.isEmpty() ) {
+ QString ser = QString::fromLocal8Bit( server );
+ QString port = "6667";
+ if(ser.contains(":")){
+ port = ser.section(":", 1, 1);
+ ser = ser.section(":", 0, 0);
+ }
+ KSircServer kss(ser, port, "", "", false);
+ sc->new_ksircprocess( kss );
+ if ( !channel.isEmpty() ) {
+ QStringList channels = QStringList::split( ',', QString::fromLocal8Bit( channel ) );
+ QStringList::ConstIterator it = channels.begin();
+ QStringList::ConstIterator end = channels.end();
+ for ( ; it != end; ++it ) {
+ sc->new_toplevel( KSircChannel(ser, *it), true );
+ }
+ }
+ }
+ else {
+ if(args->isSet("autoconnect") == TRUE){
+ sc->start_autoconnect();
+ }
+ }
+
+ args->clear();
+ }
+
+ return app.exec();
+}
+
+/* vim: et sw=4
+ */
diff --git a/ksirc/ksirc.desktop b/ksirc/ksirc.desktop
new file mode 100644
index 00000000..1289552e
--- /dev/null
+++ b/ksirc/ksirc.desktop
@@ -0,0 +1,87 @@
+[Desktop Entry]
+GenericName=IRC Client
+GenericName[af]=Irc Kliënt
+GenericName[ar]=عميل IRC
+GenericName[be]=Кліент IRC
+GenericName[bg]=Клиент за IRC
+GenericName[bn]=আই-আর-সি কà§à¦²à¦¾à§Ÿà§‡à¦¨à§à¦Ÿ
+GenericName[br]=Kliant IRC
+GenericName[ca]=Client IRC
+GenericName[cs]=IRC klient
+GenericName[cy]=Dibynnydd IRC
+GenericName[da]=IRC-klient
+GenericName[de]=IRC Programm
+GenericName[el]=Πελάτης IRC
+GenericName[eo]=IRC-kliento
+GenericName[es]=Cliente de IRC
+GenericName[et]=IRC klient
+GenericName[eu]=IRC bezeroa
+GenericName[fa]=کارخواه IRC
+GenericName[fi]=IRC-asiakasohjelma
+GenericName[fr]=Client IRC
+GenericName[ga]=Cliant IRC
+GenericName[gl]=Cliente de IRC
+GenericName[he]=לקוח IRC
+GenericName[hi]=आईआरसी कà¥à¤²à¤¾à¤à¤‚ट
+GenericName[hr]=IRC klijent
+GenericName[hu]=IRC-kliens
+GenericName[is]=IRC forrit
+GenericName[it]=Client IRC
+GenericName[ja]=IRC クライアント
+GenericName[ka]=IRC კლიენტი
+GenericName[kk]=IRC клиенті
+GenericName[km]=កម្មវិធី IRC
+GenericName[lt]=IRC klientas
+GenericName[lv]=IRC Klients
+GenericName[mk]=Клиент за IRC
+GenericName[mn]=IRC Ñ…ÑÑ€ÑглÑгч
+GenericName[ms]=Klien IRC
+GenericName[mt]=Klijent IRC
+GenericName[nb]=IRC-klient
+GenericName[nds]=IRC-Klöönprogramm
+GenericName[ne]=आइआरसी कà¥à¤²à¤¾à¤‡à¤¨à¥à¤Ÿ
+GenericName[nl]=IRC-client
+GenericName[nn]=IRC-klient
+GenericName[nso]=Moreki wa IRC
+GenericName[pa]=IRC ਕਲਾਂਇਟ
+GenericName[pl]=Klient IRC
+GenericName[pt]=Cliente de IRC
+GenericName[pt_BR]=Cliente IRC
+GenericName[ro]=Un client IRC
+GenericName[ru]=Клиент IRC
+GenericName[se]=IRC-klienta
+GenericName[sk]=IRC klient
+GenericName[sl]=Odjemalec za IRC
+GenericName[sr]=IRC клијент
+GenericName[sr@Latn]=IRC klijent
+GenericName[sv]=IRC-klient
+GenericName[ta]=IRC நிரலà¯
+GenericName[tg]=Мизоҷи IRC
+GenericName[th]=ไคลเอนต์ IRC
+GenericName[tr]=IRC Programı
+GenericName[uk]=Клієнт IRC
+GenericName[uz]=IRC klienti
+GenericName[uz@cyrillic]=IRC клиенти
+GenericName[ven]=Mushumisani wa IRC
+GenericName[wa]=Cliyint IRC
+GenericName[xh]=Umxhasi we IRC
+GenericName[zh_CN]=IRC 客户程åº
+GenericName[zh_HK]=IRC 客戶端程å¼
+GenericName[zh_TW]=IRC 客戶端程å¼
+GenericName[zu]=Umthengi we IRC
+SwallowExec=
+Name=KSirc
+Name[af]=Ksirc
+Name[bn]=কে-à¦à¦¸-আর-আই-সি
+Name[eo]=IRC-kliento
+Name[hi]=के-à¤à¤¸à¤†à¤ˆà¤†à¤°à¤¸à¥€
+Name[sv]=Ksirc
+MimeType=
+Exec=ksirc %i %m
+Icon=ksirc
+Path=
+Type=Application
+Terminal=false
+X-KDE-StartupNotify=true
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Network;IRCClient;
diff --git a/ksirc/ksirc.pl b/ksirc/ksirc.pl
new file mode 100644
index 00000000..a1768f63
--- /dev/null
+++ b/ksirc/ksirc.pl
@@ -0,0 +1,781 @@
+#
+# $$Id$$
+#
+
+&addhelp("server",
+"\cbAdded by KSirc.pl\cb
+Usage: server server [port] [password]
+Connects to a new server opening it in a new window.
+If server name is prefixed with a + then an ssl connection
+is used. Port and password are optional.");
+
+&addhelp("Ban",
+"\cbAdded by KSirc.pl\cb
+Usage: BAN <nickname>
+bans the specified user on the current channel. Only channel operators
+can use this command. Bans the user in the form *!*user\@hostmask.
+hostmask is xxx.xxx.xxx.* if the hostname is in dotted quad form, otherwise
+it is *.domain.com
+See Also: UNBAN, CLRBAN, BANLIST");
+
+&addhelp("UnBan",
+"\cbAdded by KSirc.pl\cb
+Usage: UNBAN <nickname>
+Unbans the specified user on the given channel. Only channel operators
+can use this command.
+See Also: BAN, CLRBAN, BANLIST");
+
+&addhelp("ClrBan",
+"\cbAdded by KSirc.pl\cb
+Usage: CLRBAN [<#channel>]
+Removes ALL bans from the given channel. Only channel operators can use
+this command. The channel defaults to your current one.
+See Also: MODE [-b], BAN, UNBAN, CLRBAN, BANLIST");
+
+&addhelp("BanList",
+"\cbAdded by KSirc.pl\cb
+Usage: BANLIST [<#channel>]
+Shows all bans on the given channel. The channel defaults to your current one.
+See Also: BAN, UNBAN, CLRBAN");
+
+&addhelp("FC",
+"\cbAdded by KSirc.pl\cb
+Usage: FC [<#channel>] <Filter> <command>
+Does /names on the given channel. Uses the current channel if none
+specified. Does a userhost on each person received. if their name
+(in form nick!user\@host) matches your filter (in perl regex form)
+then command is executed, in command $1 is expanded to the nick of
+the matchee.
+Examples:
+ /fc #dragonrealm *!*\@*.com msg $1 you are on a host that ends in .com
+ /fc *!*\@((\\d+)\\.){3}\\.(\\d+) say $1 has a numeric host.");
+
+&addhelp("Pig",
+"\cbAdded by KSirc.pl\cb
+Usage: PIG <message>
+Translates your message into piglatin and says it on the current channel.");
+
+&addhelp("WallOP",
+"\cbAdded by KSirc.pl\cb
+Usage: WALLOP [<#channel>] <message>
+Sends a message to all of the channel operators on the given channel.
+Defaults the the current channel.");
+
+&addhelp("Amarok",
+"\cbAdded by KSirc.pl\cb
+Usage: AMAROK
+Sends a message to the current channel saying what are you playing in amarok.");
+
+sub cmd_wallop {
+ &getarg;
+ unless ($newarg =~ /^#/) {
+ $args = $newarg." ".$args;
+ $newarg = $talkchannel;
+ }
+ &notice("\@$newarg","[KSirc-Wall/$newarg]: $args");
+}
+&addcmd("wallop");
+
+
+sub modeb {
+ ($which) = @_;
+ $user =~ s/^~//;
+ if (length($user) > 8) {
+ $user = substr($user,0, 7);
+ $user .= "*";
+ }
+ @quad = split(/\./, $host);
+ if ($host =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
+ pop @quad;
+ $toban = join('.', @quad);
+ $toban .= ".*";
+ } else {
+ $toban = "*".$quad[@quad-2].".".$quad[@quad-1];
+ }
+ &docommand("mode $talkchannel ${which}b *!*$user*\@$toban");
+}
+
+sub cmd_ban {
+ &getarg;
+ if ($newarg) {
+ &userhost($newarg, "&modeb(\"+\");");
+ } else {
+ &tell("*\cbE\cb* Usage: /ban <nick>.");
+ }
+}
+&addcmd("ban");
+
+sub cmd_unban {
+ &getarg;
+ if ($newarg) {
+ &userhost($newarg, "&modeb(\"-\");");
+ } else {
+ &tell("*\cbE\cb* Usage: /unban <nick>.");
+ }
+}
+&addcmd("unban");
+
+sub cmd_banlist {
+ &getarg;
+ $newarg = $talkchannel unless $newarg;
+ &docommand("mode $newarg b");
+ &addhook("367", "banlist");
+ &addhook("368","rmbanlist");
+}
+&addcmd("banlist");
+
+sub hook_banlist {
+ $silent = 1;
+ my (undef, $channel, $mask, $banner, $time) = split(/ +/, $_[0]);
+ &print("~!default~*** \cb$mask\cb banned from \cb$channel\cb by \cb$banner\cb on \cb" . localtime($time). "\cb");
+}
+
+sub hook_rmbanlist {
+ &remhook("367","banlist");
+ &remhook("368","rmbanlist");
+}
+
+sub cmd_k {
+ &getarg;
+ $args = "You have been kicked by a KSirc user." unless $args;
+ if ($newarg) {
+ &docommand("kick $talkchannel $newarg $args");
+ } else {
+ &tell("*\cbE\cb* Usage: /k <nick> [reason]");
+ }
+}
+&addcmd("k");
+
+sub cmd_kb {
+ &getarg;
+ if ($newarg) {
+ &docommand("ban $newarg");
+ &docommand("k $newarg $args");
+ } else {
+ &tell("*\cbE\cb* Usage: /kb <nick> [reason]");
+ }
+}
+&addcmd("kb");
+
+sub cmd_clrban {
+ &getarg;
+ $newarg = $talkchannel unless $newarg;
+ &addhook("367", "tban");
+ &addhook("368","rm367");
+ &docommand("mode $newarg b");
+}
+&addcmd("clrban");
+
+sub hook_tban {
+ $silent = 1;
+ my ($shit, $channel, $mask, $banner, $time) = split(/ +/, $_[0]);
+ push @bans, $mask;
+ if (@bans == 6) {
+ &print("mode $channel -bbbbbb @bans");
+ @bans = ();
+ }
+}
+
+sub hook_rm367 {
+ @bans = ();
+ &remhook("367","tban");
+ &remhook("368","rm367");
+}
+
+sub hook_disconnectd {
+ &docommand("server 1");
+}
+&addhook("disconnect","disconnectd");
+
+#sub hook_kickd {
+# &docommand("join $_[1]") if $_[0] eq $nick;
+#}
+#&addhook("kick","kickd");
+
+sub cmd_fcmd {
+ ($names,$mask,$command) = split(/ /, $args,3);
+ $mask =~ s/\!/\!/;
+ $mask =~ s/\@/\@/;
+ $mask =~ s/\./\\./g;
+ $mask =~ s/\*/.*/g;
+ &addhook("353","filtercommand");
+ &addhook("366","removefiltercommand");
+ &tell("\t\cb~4Matching /$mask/i on $names...");
+ &docommand("names $names");
+}
+&addcmd("fcmd");
+
+sub hook_filtercommand {
+ ($shit, $_[0]) = split(/:/, $_[0]);
+ my @names = split(/ /, $_[0]);
+ for (@names) {
+ $_ =~ s/^\@//;
+ &userhost($_, "&dofilter");
+ }
+ $silent=1;
+}
+
+sub dofilter {
+ $s = "$who\!$user\@$host";
+ #&tell("$s =~ /$mask/");
+ if ($s =~ /$mask/i) {
+ $d = $command;
+ $d =~ s/\$1/$who/;
+ &docommand($d);
+ }
+}
+
+sub hook_removefiltercommand {
+ &remhook("353","filtercommand");
+ &remhook("366","removefiltercommand");
+ &tell("*\cbI\cb* Filter on $names, /$mask/i, Done.");
+}
+
+sub cmd_fc {
+ my ($mask, $cmd) = split(/ /, $args, 2);
+ &docommand("fcmd $talkchannel $mask $cmd");
+}
+&addcmd("fc");
+
+sub cmd_pig {
+ $_[0] =~ s/^pig //i;
+ &say(&topiglatin($_[0]));
+}
+&addcmd("pig");
+
+sub topiglatin {
+@words = split(/ /, $_[0]);
+for (@words) {
+ if ($_ =~ /^([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])([aeiouAEIOU])/) {
+ $_ .= substr($_,0,1)."ay";
+ $_ = substr($_,1);
+ } elsif ($_ =~ /^([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])([bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ])/) {
+ $_ .= $1.$2."ay";
+ $_ = substr($_,2);
+ } elsif ($_ =~ /^[aeiouAEIOU]/) {
+ $_ .= "way";
+ } else {
+ print "shouldn't print me\n";
+ }
+}
+return "@words";
+}
+
+&addhelp("follow",
+"\cbAdded by KSirc.pl\cb
+Usage: follow <nick>
+Highlight <nick> in colour when ever they say anything");
+
+&addhelp("unfollow",
+"\cbAdded by KSirc.pl\cb
+Usage: unfollow <nick>
+Stop highlighting the nick");
+
+&addhelp("showfollows",
+"\cbAdded by KSirc.pl\cb
+Usage: showfollows
+Shows who you are currently following");
+
+#### Start follow script from caracas
+
+&addcmd ('follow');
+&addcmd ('unfollow');
+&addcmd ('showfollows');
+
+
+@follow_colors = ( '~1', '~2', '~3', '~4', '~5', '~6', '~7', '~8', '~9', '~10', '~11', '~12', '~13', '~14', '~15' );
+undef %following;
+
+
+sub cmd_follow
+{
+ my ($fnick) = shift;
+ my ($color);
+
+ $fnick =~ s/^follow\s+//;
+ $fnick =~ tr/A-Z/a-z/;
+ if (defined ($following{$fnick}))
+ {
+ &tell ("*\cbI\cb* Already following " . $following{$fnick});
+ }
+ else
+ {
+ $color = $follow_colors [int (rand scalar (@follow_colors))];
+ &docommand ("^ksircappendrule DESC==Follow $fnick !!! " .
+ "SEARCH==<\\S*$fnick\\S*> !!! FROM==<\\S*($fnick)\\S*> !!! TO==\"<$color\$1~c>\"");
+ $following{$fnick} = "${color}${fnick}~c";
+ &tell ("*\cbI\cb* Following ${color}$fnick~c ...");
+ }
+}
+
+
+sub cmd_unfollow
+{
+ my ($fnick) = shift;
+ my ($filter);
+
+ $fnick =~ s/^unfollow\s+//;
+ $fnick =~ tr/A-Z/a-z/;
+ for ($filter = 0; $filter <= $#KSIRC_FILTER; $filter++)
+ {
+ if ($KSIRC_FILTER [$filter]{'DESC'} =~ /Follow $fnick/i)
+ {
+ &docommand ("^ksircdelrule $filter");
+ delete ($following{$fnick});
+ &tell ("*\cbI\cb* $fnick no longer followed");
+ return;
+ }
+ }
+ &tell ("*\cbI\cb* Wasn't following $fnick");
+}
+
+
+sub cmd_showfollows
+{
+ my ($fnick);
+
+ if (scalar (keys %following) > 0)
+ {
+ foreach $fnick (sort keys %following)
+ {
+ &tell ("*\cbI\cb* Following " . $following {$fnick});
+ }
+ }
+ else
+ {
+ &tell ("\*cbI\cb* Not currently following anyone");
+ }
+}
+
+#### End follow
+
+sub cmd_refresh
+{
+ &tell("*** Refresh nick list");
+ &docommand("^extnames_forget $dest_chan");
+ &docommand("extnames $dest_chan");
+}
+&addcmd("refresh");
+
+sub hook_url_who_list {
+ my @info = split(/\s+/, $_[0]);
+ &print("*I* URL: http://$info[3]:<port>/");
+ $silent = 1;
+}
+
+sub hook_url_end_who {
+ &remhook("352", "url_who_list");
+ &remhook("315", "url_end_who");
+ $args = "";
+}
+
+&addhelp("url",
+"\cbAdded by KSirc.pl\cb
+Usage: URL
+Prints out your url");
+
+
+sub cmd_url
+{
+ &addhook("352", "url_who_list");
+ &addhook("315", "url_end_who");
+ &sl("who :$nick");
+}
+&addcmd("url");
+
+%WHO_IGNORE = "";
+
+sub cmd_extnames
+{
+ &dosplat;
+ &getarg;
+ $newarg =~ tr/A-Z/a-z/;
+ return if $WHO_IGNORE{$newarg} == 1;
+ if($who_active == 0){
+ &addhook("352", "ksirc_who_list");
+ &addhook("315", "ksirc_who_end");
+ }
+ &sl("who :$newarg");
+ $who_active++;
+ $WHO_INFO{$newarg} = "";
+ $WHO_TIME{$newarg} = 0;
+ $WHO_COUNT{$newarg} = 0;
+}
+&addcmd("extnames");
+
+sub hook_ksirc_who_end {
+ $who_active--;
+ if($who_active == 0){
+ &remhook("352", "ksirc_who_list");
+ &remhook("315", "ksirc_who_end");
+ }
+ my @info = split(/\s+/, $_[0]);
+ # 0: our nick
+ # 1: channel
+ # 2 Onwards: misc info
+ $info[1] =~ tr/A-Z/a-z/;
+ chop($WHO_INFO{$info[1]}); # Remove trailing space
+ my $c = ($WHO_TIME{$info[1]} == 0) ? "C" : "!";
+ if(length($WHO_INFO{$info[1]}) > 0){
+ &print("~$info[1]~*$c* Users on $info[1]: $WHO_INFO{$info[1]}");
+ $WHO_COUNT{$info[1]}++;
+ }
+ &print("~$info[1]~*c* Done Parsing Who");
+
+
+ # print "*I* Parsing: extnames done, $info[1], count: " . $WHO_COUNT{$info[1]} . "\n";
+ if($WHO_COUNT{$info[1]} > 25){
+ if($WHO_IGNORE{$info[1]} != 1){
+ $WHO_IGNORE{$info[1]} = 1;
+ &print('*$*' . " Extended nick list info turned off for $info[1], too many people in channel\n");
+ &print("*I* Extended nick list info turned off for $info[1], too many people in channel\n");
+ }
+ }
+ else {
+ $WHO_IGNORE{$info[1]} = 0;
+ }
+
+ delete($WHO_COUNT{$info[1]});
+ delete($WHO_INFO{$info[1]});
+ delete($WHO_TIME{$info[1]});
+ $args = "";
+}
+
+sub hook_ksirc_who_list {
+ my @info = split(/\s+/, $_[0]);
+ # 0: our nick
+ # 1: channel
+ # 2: ident
+ # 3: one server
+ # 4: another server
+ # 5: nick
+ # 6: status
+ # 7: rest is useless
+ $silent = 1;
+ my $who_nick = $info[5];
+ # print "*I* Parsing: $_[0], info6: $info[6]\n";
+ if($info[6] =~ /G/){
+ $who_nick = "#" . $who_nick;
+ }
+ if($info[6] =~ /\+/){
+ $who_nick = "+" . $who_nick;
+ }
+ if($info[6] =~ /\@/){
+ $who_nick = "@" . $who_nick;
+ }
+ if($info[6] =~ /\*/){
+ $who_nick = "*" . $who_nick;
+ }
+ $info[1] =~ tr/A-Z/a-z/;
+ $WHO_COUNT{$info[1]}++;
+
+ $WHO_INFO{$info[1]} .= $who_nick . " ";
+ if(length($WHO_INFO{$info[1]}) > 350){
+ my $c = ($WHO_TIME{$info[1]} == 0) ? "C" : "!";
+ &print("~$info[1]~*$c* Users on $info[1]: $WHO_INFO{$info[1]}");
+ $WHO_INFO{$info[1]} = "";
+ $WHO_TIME{$info[1]}++;
+ }
+}
+
+sub cmd_extnames_forget
+{
+ &dosplat;
+ &getarg;
+ $newarg =~ tr/A-Z/a-z/;
+ $WHO_IGNORE{$newarg} = 0;
+}
+
+&addcmd("extnames_forget");
+
+sub hook_nicks_on_join {
+ my $channel = shift;
+ if(&eq($who, $nick)){
+ &docommand("^extnames_forget $channel");
+ }
+ &docommand("^extnames $channel");
+}
+
+addhook("join", "nicks_on_join");
+
+
+&tell("*** \0032,4\cbLoaded KSirc.pl\003");
+&tell("*** \00313,3\cbWith: Super Willy Enhancements, LotR's exec\003");
+sub cmd_exec {
+
+ my $how, $to;
+
+ &getarg;
+ $how = "x";
+ if (&eq($newarg, "-OUT")) { $how = 'c'; }
+ if (&eq($newarg, "-MSG")) { $how = 'm'; &getarg; $to = $newarg; }
+ if (&eq($newarg, "-NOTICE")) { $how = 'n'; &getarg; $to = $newarg; }
+ if ($how eq "x") { $args = $newarg . " " . $args; }
+ open (CMD, "$args|");
+ while (<CMD>) {
+ chomp;
+ if ($how eq 'c') {
+ &say(" $_");
+ } elsif ($how eq 'm') {
+ &msg($to, $_);
+ } elsif ($how eq 'n') {
+ &notice($to, $_);
+ } else {
+ print "$_\n";
+ }
+ }
+ close CMD;
+}
+
+&addcmd("exec");
+&addhelp("exec", "Usage: EXEC <shell commands>\n" .
+ "EXEC -OUT <shell commands]\n" .
+ "EXEC -MSG <nickname> <shell commands>]\n" .
+ "EXEC -NOTICE <nickname> <shell commands>]\n" );
+
+$k_highlight = 1;
+
+if(open(FH, "<$HOME/.ksirc_highlight")){
+ chomp($k_highlight = <FH>);
+ close(FH);
+}
+
+sub hook_fixcolours {
+ if($k_highlight == 1){
+ $_[1] =~ s/(^|\s)\*([^*]+?)\*($|\s)/$1\002$2\002$3/g;
+ $_[1] =~ s/(^|\s)_([^_]+?)_($|\s)/$1\037$2\037$3/g;
+ $_[1] =~ s/(^|\s)#([^#]+?)#($|\s)/$1\026$2\026$3/g;
+ }
+}
+
+&addhook("send_text", "fixcolours");
+
+sub cmd_dishighlight {
+
+ print "*I* Highlight parsing: ";
+ if($k_highlight == 0) {
+ $k_highlight = 1;
+ print "Enabled\n";
+ }
+ else {
+ $k_highlight = 0;
+ print "Disabled\n";
+ }
+
+ if(open(FH, ">$ENV{HOME}/.ksirc_highlight")){
+ print FH "$k_highlight\n";
+ close(FH);
+ }
+ else {
+ print "*E* Can't save highlight state $!\n";
+ }
+
+}
+
+&addcmd("dishighlight");
+&addhelp("dishighlight", "Usage: dishighlight\n" .
+ "Toggles the convertion of *bold* into \cbbold\cb\n" .
+ "and _underline_ into \c_underline\c_ and #reverse#\n" .
+ "into \cvreverse\cv. It saves the state into\n" .
+ "~/.ksirc_highlight for convenience");
+
+sub cmd_help {
+ &tell("*\cbH\cb* Help not available"), return unless @help;
+ my $found ='';
+
+ &getarg;
+ if($newarg =~ /^\s*$/){
+ my $line = '';
+ my %once;
+ foreach (@help) {
+ if (/^\@/) {
+ if (&eq($_, "\@main")) {
+ $found=1;
+ &tell("*\cbH\cb* Help on $newarg") if $newarg ne 'main'; # KSIRC MOD
+ }
+ else {
+ $found=0;
+ }
+ }
+ else {
+ &tell("*\cbH\cb* $_") if $found;
+ }
+ }
+ foreach (@help) {
+ if(/^\@/){
+ if(!&eq($_, "\@main")){
+ $found = 0;
+ my $cmd = /\@(\S+)/;
+ next if $once{$1};
+ $once{$1} = 1;
+ $line .= "$1 " . " "x(15-length("$1 ")); # KSIRC MOD
+ if(length($line) > 50){
+ &tell("*\cbH\cb* $line");
+ $line = "";
+ }
+ }
+ }
+ }
+ &tell("*\cbH\cb* $line");
+ $found=1;
+ }
+ else{
+ $newarg =~ s/ *$//;
+ foreach (@help) {
+ if (/^\@/) {
+ last if $found;
+ if (&eq($_, "\@$newarg")) {
+ $found=1;
+ &tell("*\cbH\cb* Help on $newarg") if $newarg ne 'main';
+ }
+ } else {
+ &tell("*\cbH\cb* $_") if $found;
+ }
+ }
+ } # KSIRC MOD
+ &tell("*\cbH\cb* Unknown help topic; try /help") unless $found;
+}
+
+&addcmd("help");
+
+
+# New DCC resume/get features
+
+
+my %A_RESUME_WAIT = ();
+
+sub hook_ctcp_resume_reply {
+ my $towho = shift;
+ my $what = shift;
+ my $args = shift;
+
+ if($what eq 'DCC'){
+ my ($which, $file, $port, $pos) = split(/ +/, $args);
+ &tell("Got which: $which");
+ if($which eq 'ACCEPT'){
+ # &print("Got resume from $who port $port file: $file pos: $pos args: $args");
+ if($A_RESUME_WAIT{$port}){
+ &print("*\cbI\cb* DCC Resume with $who accepted");
+ &tell("~!dcc~DCC GET resumed who: $who file: " . $dgresume{$port}{"file"});
+ &tell("~!dcc~DCC GET read: " . $dgresume{$port}{"file"} . " bytes: " . $dgresume{$port}{"pos"}); # KSIRC MOD FOR 971217
+ $dgresume{$port}{"GotReply"} = 1;
+ delete $A_RESUME_WAIT{$port};
+ $skip = 1;
+
+ if($A_AUTOSTART{$port}){
+ &docommand($A_AUTOSTART{$port});
+ delete $A_AUTOSTART{$port};
+ }
+ }
+ }
+ elsif($which eq 'RESUME'){
+ my($lfh, $myport);
+ while(($lfh, $myport) = each %dsport){
+ if($port == $myport){
+ $skip = 1;
+ my $size = (-s $dfile{$dswait{$lfh}});
+ if($pos < $size){
+ seek($dswait{$lfh}, $pos, SEEK_SET);
+ $dsoffset{$lfh} = $pos;
+ &docommand("ctcp $who DCC ACCEPT $file $port $pos");
+ &tell("~!dcc~DCC SEND resumed who: $who file: " . $dfile{$dswait{$lfh}});
+ &tell("~!dcc~DCC SEND write: " . $dfile{$dswait{$lfh}} . " bytes: " . $pos); # KSIRC MOD FOR 971217
+ }
+ else {
+ &tell("*\cbE\cb* Got DCC resume with invalid size from $who for " . $dfile{$dswait{$lfh}});
+ }
+ }
+ }
+ }
+ }
+}
+
+&addhook("ctcp", "ctcp_resume_reply");
+
+sub hook_ctcp_reject_reply {
+ my $towho = shift;
+ my $what = shift;
+ my $args = shift;
+
+ if($what eq 'DCC'){
+ my ($which, $type, $file) = split(/ +/, $args);
+ if($which eq 'REJECT'){
+ &tell("Got reject");
+ if($type eq 'CHAT'){
+ $no_reject = 1;
+ &tell("*\cbI\cb* DCC CHAT with $who rejected");
+ &docommand("/dcc close chat $who");
+ $skip = 1;
+ }
+ elsif($type eq 'GET'){
+ $no_reject = 1;
+ &tell("*\cbI\cb* DCC GET rejected by $who for $file");
+ &docommand("/dcc close get $who $file");
+ $skip = 1;
+ }
+ elsif($type eq 'SEND'){
+ $no_reject = 1;
+ &tell("*\cbI\cb* DCC SEND rejected by $who file $file");
+ &docommand("/dcc close send $who $file");
+ $skip = 1;
+ }
+ }
+ }
+}
+
+&addhook("ctcp_reply", "ctcp_reject_reply");
+
+sub cmd_resume {
+ &getarg;
+ my $who = $newarg;
+ &getarg;
+ my $file = $newarg;
+
+ foreach $i (keys(%dgoffered)) {
+ my($h, $p, $f) = split(/ /, $i);
+ if (&eq($f, $file) && &eq($dgoffered{$i}, $who)) {
+ if(my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
+ $atime,$mtime,$ctime,$blksize,$blocks)
+ = stat($file)){
+ &print("*\cbI\cb* Doing resume for $f with $who ($p)");
+ &docommand("ctcp $who DCC RESUME $f $p $size");
+ $dgresume{$p}{"pos"} = $size;
+ $dgresume{$p}{"file"} = $f;
+ $dgresume{$p}{"who"} = $who;
+ $A_RESUME_WAIT{$p} = 1;
+ }
+ else {
+ &print("*\cbE\cb* Error getting file ($file) size: $!");
+ }
+ }
+ }
+}
+
+&addcmd("resume");
+
+
+sub cmd_amarok
+{
+ $dcop=`dcop amarok`;
+ if(! ($dcop =~ /player/) ) {
+ &print("Error: Amarok is *not* running");
+ return;
+ }
+
+ $dcop=`dcop amarok player isPlaying`;
+ if(! ($dcop =~ /true/) ) {
+ &print("Amarok is not playing anything");
+ return
+ }
+
+ $output='is playing "$dcop" with Amarok';
+ $dcop= `dcop amarok player nowPlaying` ;
+$dcop =~ s/^\s+//;
+$dcop =~ s/\s+$//;
+ $output=~ s/(\$\w+)/$1/eeg;
+ &me( $output );
+}
+&addcmd("amarok");
+
+
+
diff --git a/ksirc/ksircchannel.h b/ksirc/ksircchannel.h
new file mode 100644
index 00000000..1913b530
--- /dev/null
+++ b/ksirc/ksircchannel.h
@@ -0,0 +1,35 @@
+#ifndef KSIRCCHANNEL_H
+#define KSIRCCHANNEL_H
+
+#include <qstring.h>
+
+class KSircChannel
+{
+
+public:
+ KSircChannel( const QString server,
+ const QString channel,
+ const QString key = QString::null,
+ const QString encoding = QString::null
+ ) :
+ m_server(server), m_channel(channel),
+ m_key(key), m_encoding(encoding) {}
+
+ const QString server() const { return m_server; }
+ const QString channel() const { return m_channel; }
+ const QString key() const { return m_key; }
+ const QString encoding() const { return m_encoding; }
+
+ void setChannel(QString channel) { m_channel = channel; }
+ void setKey(QString key) { m_key = key; }
+ void setEncoding(QString encoding) { m_encoding = encoding; }
+
+private:
+ const QString m_server; /* channels can never change server */
+ QString m_channel;
+ QString m_key;
+ QString m_encoding;
+
+};
+
+#endif
diff --git a/ksirc/ksircprocess.cpp b/ksirc/ksircprocess.cpp
new file mode 100644
index 00000000..1fff6807
--- /dev/null
+++ b/ksirc/ksircprocess.cpp
@@ -0,0 +1,663 @@
+/*************************************************************************
+
+ KSircProcess, sirc controller
+
+ $$Id$$
+
+ KSircProcess cerate and controls toplevel widgets and sirc process'.
+ Each sirc process has 1 and only 1 KSircProcess to control it. KSirc
+ process passes all IO to IOController which is it's friend.
+
+ Interface:
+
+ public:
+ KSircProcess(*server=0L, *parent=0, *name=0)
+ server: is the name of the server to connect to. It must be
+ provided or else start sirc will barf. :(
+ parent: parent window, this _should_ be null
+ name: name, passed to QObject...
+
+ ~KSirProcess:
+ kill the sirc process, and iocontrollller, emit delete_toplevel
+
+ getWindowList:
+ returns the TopList, see bellow.
+
+ Signals:
+ made_toplevel(server, window)
+ made a new toplevel window for the "server" we are connected to
+ with "window" as the title.
+
+ dalete_toplevel(server, window)
+ delete toplevel with server and called window. If we emit null
+ as the window name it means to destroy all info about the
+ server and ksircprocess.
+
+ changeChannel(server, old_name, new_name)
+ toplevel with old_name has been changed to new_name and all
+ future refrences will use new_name.
+
+ public slots:
+ new_toplevel(window):
+ create a new window with name window. This MAY only change the
+ name of an existing window that's now idle.
+
+ close_topevel(KsircTopLevel*, window):
+ deletes all refrences to window and if needed finds a new
+ default toplevel.
+
+ default_window(KSricTopLevel*):
+ KSircTopLevel is requesting change to !default. Be carefull
+ with this one.
+
+ recvChangeChannel(old, new):
+ window old is changing to new. emit ChangeChannel with server
+ name added. Without server name we can uniqely id the window. :(
+
+ Implementation:
+
+ Bassic process is to create a new KSircProcess and it takes care of
+ the rest. It emits signals for each new window and every time a
+ window is delete so you can update external display (like
+ servercontroller uses).
+
+ Startup:
+
+ 1. Creates a case insensitive TopList. This is a list of ALL
+ KSircReceivers under control of this server, and includes such
+ items as "!all" and "!default". All !name are control windows.
+
+ 2. Forks off a KProcess for sirc and passes it over to IOController
+ which grabs and control's it's IO.
+
+ 3. It then opens a "!default" window. This will receive all
+ initial input and such. It WILL change it's name on the first
+ join.
+
+ 4. The IO broadcast object is created and setup.
+
+ 5. everything is put into run mode.
+
+
+ Operation, see code bellow for inline comments.
+
+*************************************************************************/
+
+
+
+#include "baserules.h"
+#include "ksopts.h"
+#include "control_message.h"
+#include "displayMgr.h"
+#include "ioBroadcast.h"
+#include "ioDCC.h"
+#include "ioDiscard.h"
+#include "ioLAG.h"
+#include "ioNotify.h"
+#include "iocontroller.h"
+#include "ksircprocess.h"
+#include "objFinder.h"
+#include "servercontroller.h"
+#include "toplevel.h"
+#include "version.h"
+#include "KSProgress/ksprogress.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include <qtimer.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <kstandarddirs.h>
+
+
+extern DisplayMgr *displayMgr;
+
+KSircProcess::KSircProcess( QString &server_id, KSircServer &kss, QObject * parent, const char * name )
+ : QObject(parent, name), m_kss(kss), m_serverid(server_id)
+{
+
+ proc = new KProcess();
+
+#ifndef NDEBUG
+ if(getuid() != 0)
+ proc->setRunPrivileged(true); /* make ksirc run under gdb as a user */
+#endif
+
+ //server = qstrdup(kss.server());
+
+ QDict<KSircMessageReceiver> nTopList(17, FALSE);
+ TopList = nTopList;
+ // TopList.setAutoDelete(TRUE)
+
+ auto_create_really = FALSE;
+
+ // Create the ksopts server structure
+ ksopts->serverSetup(kss);
+
+ // Setup the environment for KSirc
+ QString qsNick, qsRealname, qsUserID, qsAltNick;
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("StartUp");
+ qsNick = ksopts->serv(kss).nick;
+ qsAltNick = ksopts->serv(kss).altNick;
+ qsRealname = ksopts->serv(kss).realName;
+ qsUserID = ksopts->serv(kss).userID;
+ kdDebug(5008) << "qsNick: " << qsNick << " qsAltNick: " << qsAltNick << " qsRealname: " << qsRealname << "qsUserID: " << qsUserID << endl;
+
+ m_nick = qsNick;
+
+ if((qsNick.isEmpty() == FALSE)){
+ proc->setEnvironment("SIRCNICK", qsNick);
+ }
+ if((qsAltNick.isEmpty() == FALSE)){
+ proc->setEnvironment("BACKUPNICK", qsAltNick);
+ }
+ if((qsRealname.isEmpty() == FALSE)){
+ proc->setEnvironment("SIRCNAME", qsRealname);
+ }
+ if((qsUserID.isEmpty() == FALSE)){
+ proc->setEnvironment("SIRCUSER", qsUserID);
+ kdDebug(5008) << "Set SIRCUSER to: " << qsUserID << endl;
+ }
+
+ proc->setEnvironment("SIRCLIB", KGlobal::dirs()->findResourceDir("appdata", "ksirc.pl"));
+ proc->setEnvironment("SIRCWAIT", "1");
+
+ QString env = locate("appdata", "ksircrc");
+ if (!env.isEmpty())
+ proc->setEnvironment("SIRCRC", env);
+ env = locate("appdata", "ksircrc.pl");
+ if (!env.isEmpty())
+ proc->setEnvironment("SIRCRCPL", env);
+
+ // Setup the proc now, so iocontroller can use it. It's latter
+ // though. started bellow though.
+
+ proc->setName(QCString(name) + "_kprocess");
+ objFinder::insert(proc);
+// insertChild(proc);
+
+ // pass the server string using an environment variable, because it might contain
+ // a password that could be spyed out, as the commandline option is readable to others.
+ // Fixes 47157.
+ proc->setEnvironment( "SIRCSERVER", "[" + kss.server() + "]:" + kss.port() + ":" + kss.password());
+
+ QString sslopt;
+ if(kss.usessl())
+ sslopt = "-S";
+ *proc << "perl" << KGlobal::dirs()->findExe("dsirc") << "-8" << "-r" << sslopt;
+
+ // Finally start the iocontroller.
+
+ iocontrol = new KSircIOController(proc, this);
+ iocontrol->setName(QCString(name) + "_iocontrol");
+
+ // Create toplevel before iocontroller so it has somewhere to write stuff.
+
+ running_window = TRUE; // True so we do create the default
+ default_follow_focus = TRUE;
+ KSircChannel ci(kss.server(), "!no_channel");
+ new_toplevel(ci, true); //
+ TopList.replace("!default", TopList[ci.channel()]);
+
+ running_window = FALSE; // set false so next changes the first name
+
+ // Write default commands, and open default windows.
+
+ TopList.insert("!all", new KSircIOBroadcast(this));
+ TopList.insert("!discard", new KSircIODiscard(this));
+
+ KSircIODCC *dcc = new KSircIODCC(this);
+ TopList.insert("!dcc", dcc);
+ dcc = static_cast<KSircIODCC *>( TopList["!dcc"] ); // g++ bug
+ connect(dcc, SIGNAL(outputLine(QCString)),
+ iocontrol, SLOT(stdin_write(QCString)));
+
+ KSircIOLAG *lag = new KSircIOLAG(this);
+ TopList.insert("!lag", lag);
+ lag = static_cast<KSircIOLAG*>( TopList["!lag"] ); // g++ bug!
+ connect(lag, SIGNAL(outputLine(QCString)),
+ iocontrol, SLOT(stdin_write(QCString)));
+
+ KSircIONotify *notify = new KSircIONotify(this);
+ TopList.insert("!notify", notify);
+ notify = static_cast<KSircIONotify *>( TopList["!notify"] ); // g++ bug
+ connect(notify, SIGNAL(notify_online(QString)),
+ this, SLOT(notify_forw_online(QString)));
+ connect(notify, SIGNAL(notify_offline(QString)),
+ this, SLOT(notify_forw_offline(QString)));
+
+ TopList.insert("!base_rules", new KSMBaseRules(this));
+
+ // Now that all windows are up, start sirc.
+
+ proc->start(KProcess::NotifyOnExit, KProcess::All);
+ // Intial commands to load ASAP.
+ // turn on sirc ssfe mode
+ QCString command = "/eval $ssfe=1\n";
+ iocontrol->stdin_write(command);
+
+ command = "/eval $version .= \"+KSIRC/" + QCString(KSIRC_VERSION) + "\"\n";
+ iocontrol->stdin_write(command);
+ command = "/load " + locate("appdata", "filters.pl").local8Bit() + "\n";
+ iocontrol->stdin_write(command);
+ command = "/load " + locate("appdata", "ksirc.pl").local8Bit() + "\n";
+ iocontrol->stdin_write(command);
+ /*
+ command = "/load " + locate("appdata", "puke.pl") + "\n";
+ iocontrol->stdin_write(command);
+ command = "/load " + locate("appdata", "dcc_status.pm") + "\n";
+ iocontrol->stdin_write(command);
+ */
+ command = "/eval $ready = 1\n";
+ iocontrol->stdin_write(command);
+
+
+ // Load all the filter rules. Must be after /load filtes.pl so all
+ // the functions are available
+
+ filters_update();
+
+ // We do this after filters_update() since filters_update loads the
+ // require notify filters, etc.
+
+ command = "/notify ";
+ command += ksopts->serv(kss).notifyList.join(" ").latin1();
+ command += "\n";
+ kdDebug(5008) << "Notify: " << command << endl;
+ iocontrol->stdin_write(command);
+
+}
+
+KSircProcess::~KSircProcess()
+{
+ cleanup();
+}
+
+QPtrList<KSircMessageReceiver> KSircProcess::messageReceivers() const
+{
+ QPtrList<KSircMessageReceiver> res;
+ res.setAutoDelete( false );
+ QDictIterator<KSircMessageReceiver> it( TopList );
+ for (; it.current(); ++it )
+ if ( it.currentKey() != "!default" &&
+ it.currentKey() != "!no_channel" )
+ res.append( it.current() );
+ return res;
+}
+
+const QDict<KSircMessageReceiver> &KSircProcess::mrList() const
+{
+ return TopList;
+}
+
+void KSircProcess::cleanup()
+{
+ if(TopList["!default"]){
+ TopList.remove("!default"); // remove default so we don't delete it twice.
+ }
+
+ TopList.setAutoDelete(true);
+ TopList.clear();
+
+ emit ProcMessage(m_serverid, ProcCommand::procClose, QString());
+
+ // Do closing down commands, this should release all puke widgets
+#if 0
+ dsirc does this on SIGTERM (malte)
+ QString quit_cmd = "/eval &dohooks(\"quit\");\n";
+ proc->writeStdin(quit_cmd.ascii(), quit_cmd.length());
+ sleep(1);
+#endif
+ if(proc->isRunning()){
+ proc->kill(SIGTERM);
+ }
+
+ delete proc; // Delete process, seems to kill sirc, good.
+ delete iocontrol; // Take out io controller
+// delete []server;
+
+ proc = 0L;
+ iocontrol = 0L;
+// server = 0L;
+}
+
+void KSircProcess::new_toplevel(const KSircChannel &channelInfo, bool safe)
+{
+ static time_t last_window_open = 0;
+ static int number_open = 0;
+ static bool flood_dlg = FALSE;
+
+ if(running_window == FALSE){ // If we're not fully running, reusing
+ // !default window for next chan.
+ running_window = TRUE;
+ // insert and remove is done as a side effect of the control_message call
+ // TopList.insert(str, TopList["!no_channel"]);
+ // TopList.remove("!no_channel"); // We're no longer !no_channel
+ TopList["!no_channel"]->control_message(CHANGE_CHANNEL, channelInfo.server() + "!!!" + channelInfo.channel() + "!!!" + channelInfo.key());
+ }
+ else if(TopList.find(channelInfo.channel()) == 0x0){ // If the window doesn't exist, continue
+ // If AutoCreate windows is on, let's make sure we're not being flooded.
+ if(ksopts->autoCreateWin == TRUE && safe == false){
+ time_t current_time = time(NULL);
+ if((channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&') &&
+ ((current_time - last_window_open) < 5)){
+ if(number_open > 4 && flood_dlg == FALSE){
+ flood_dlg = TRUE;
+ int res = KMessageBox::warningYesNo(0,
+ i18n("5 Channel windows were opened "
+ "in less than 5 seconds. Someone "
+ "may be trying to flood your X server "
+ "with windows.\n"
+ "Shall I turn off AutoCreate windows?"),
+ i18n("Flood Warning"), i18n("Turn Off"), i18n("Keep Enabled"));
+ switch(res) {
+ case KMessageBox::Yes:
+ emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString());
+ }
+ last_window_open = current_time;
+ number_open = 0;
+ }
+ else{
+ // Joining channels can't be a flood, can it?
+ if(channelInfo.channel()[0] != '#' || channelInfo.channel()[0] != '&')
+ if(!safe)
+ number_open++;
+ }
+ flood_dlg = FALSE;
+ }
+ else{
+ last_window_open = current_time;
+ }
+ }
+
+ // Create a new toplevel, and add it to the toplist.
+ // TopList is a list of KSircReceivers so we still need wm.
+ KSircTopLevel *wm = new KSircTopLevel(this, channelInfo, (serverID() +"_" + channelInfo.channel()).ascii() );
+ TopList.insert(channelInfo.channel(), wm);
+
+ // Connect needed signals. For a message window we never want it
+ // becomming the default so we ignore focusIn events into it.
+ connect(wm, SIGNAL(outputLine(QCString)),
+ iocontrol, SLOT(stdin_write(QCString)));
+ connect(wm, SIGNAL(open_toplevel(const KSircChannel &)),
+ this,SLOT(new_toplevel (const KSircChannel &)));
+ connect(wm, SIGNAL(closing(KSircTopLevel *, QString)),
+ this,SLOT(close_toplevel(KSircTopLevel *, QString)));
+ connect(wm, SIGNAL(currentWindow(KSircTopLevel *)),
+ this,SLOT(default_window(KSircTopLevel *)));
+ connect(wm, SIGNAL(changeChannel(const QString &, const QString &)),
+ this,SLOT(recvChangeChannel(const QString &, const QString &)));
+ connect(wm, SIGNAL(destroyed(QObject *)),
+ this,SLOT(clean_toplevel(QObject *)));
+ connect( wm, SIGNAL( requestQuit( const QCString& ) ),
+ SLOT( request_quit( const QCString& ) ) );
+
+ default_window(wm); // Set it to the default window.
+ emit ProcMessage(serverID(), ProcCommand::addTopLevel, channelInfo.channel());
+
+ displayMgr->newTopLevel(wm, TRUE);
+ displayMgr->setCaption(wm, channelInfo.channel());
+// displayMgr->show(wm);
+ wm->lineEdit()->setFocus(); // Give focus back to the linee, someone takes it away on new create
+ }
+ else {
+ QWidget *w = dynamic_cast<QWidget *>(TopList.find(channelInfo.channel()));
+ if(w)
+ displayMgr->raise(w);
+ }
+}
+
+void KSircProcess::close_toplevel(KSircTopLevel *wm, QString name)
+{
+ if(auto_create_really == TRUE)
+ turn_on_autocreate();
+
+ kdDebug(5008) << "KSP: get close_toplevel: " << name << endl;
+
+ // the removeTopLevel below also deletes the mditoplevel (in case
+ // we are using mdi) , which deletes its children, which deletes
+ // 'wm' , so watch out not to delete twice! (Simon)
+ QGuardedPtr<KSircTopLevel> guardedwm = wm;
+ // Do this now or we get junk left on the screen
+ displayMgr->removeTopLevel(wm);
+
+ while(TopList.remove(name)); // In case multiple copies exist remove them all
+
+ bool isDefault = (wm == TopList["!default"]);
+
+ // Ok, now if we just deleted the default we have a problem, we need
+ // a new default. BUT don't make the default "!all" or !message.
+ // So let's go grab a default, and make sure it's not "!" control
+ // object.
+
+ QDictIterator<KSircMessageReceiver> it(TopList);
+ for(;it.current() && it.currentKey().startsWith("!"); ++it);
+
+ if (!it.current())
+ {
+ // No top-level windows left.
+ QCString command = "/quit\n"; // "/signoff" ?
+ iocontrol->stdin_write(command); // kill sirc
+ kdDebug(5008) << "KSP closing: " << m_kss.server() << endl;
+ delete guardedwm;
+ delete this; // Delete ourself, WARNING MUST RETURN SINCE WE NO
+ // LONGER EXIST!!!!
+ return; // ^^^^^^^^^^^^^^^
+ }
+
+ if (isDefault)
+ TopList.replace("!default", it.current());
+
+ // Let's let em know she's deleted!
+ if(ksopts->autoCreateWin == TRUE){
+ emit ProcMessage(serverID(), ProcCommand::turnOffAutoCreate, QString());
+ QTimer::singleShot(5000, this, SLOT(turn_on_autocreate()));
+ auto_create_really = TRUE;
+ }
+ else{
+ auto_create_really = FALSE;
+ }
+
+ delete guardedwm;
+ emit ProcMessage(serverID(), ProcCommand::deleteTopLevel, name);
+}
+
+void KSircProcess::clean_toplevel(QObject *clean){
+ if(!clean){
+ qWarning("Passed null to cleaner!!");
+ return;
+ }
+ bool cont = FALSE;
+ do{
+ cont = FALSE;
+ QDictIterator<KSircMessageReceiver> it(TopList);
+ while(it.current() != 0x0){
+ if((QObject *)it.current() == clean){
+ QString key = it.currentKey();
+ while(TopList[key] != 0x0){
+ TopList.remove(key);
+ }
+ cont = TRUE;
+ break;
+ }
+ ++it;
+ }
+ } while(cont == TRUE);
+}
+
+void KSircProcess::request_quit( const QCString& command )
+{
+ iocontrol->stdin_write( command );
+ // Since removing the toplevels will delete the one that emitted this
+ // signal as well, we need to defer this a little (malte)
+ QTimer::singleShot( 0, this, SLOT( do_quit() ) );
+}
+
+void KSircProcess::do_quit()
+{
+ for ( QDictIterator< KSircMessageReceiver > it( TopList ); it.current(); ++it )
+ {
+ if ( it.currentKey() == "!default" ) continue;
+ if ( KSircTopLevel* topLevel = dynamic_cast< KSircTopLevel* >( it.current() ) )
+ {
+ QGuardedPtr< KSircTopLevel > guardedTL = topLevel;
+ displayMgr->removeTopLevel( topLevel );
+ delete guardedTL;
+ }
+ else delete it.current();
+ }
+ // cleanup() would otherwise delete them a second time
+ TopList.clear();
+ delete this;
+}
+
+void KSircProcess::default_window(KSircTopLevel *w)
+{
+
+ //
+ // If we want to track the default as it goes around, change the
+ // window on focus changes.
+ //
+
+ if(w && (default_follow_focus == TRUE))
+ TopList.replace("!default", w);
+
+}
+
+void KSircProcess::recvChangeChannel(const QString &old_chan, const QString &new_chan)
+{
+ //
+ // Channel changed name, add our own name and off we go.
+ // ServerController needs our name so it can have a uniq handle for
+ // the window name.
+ //
+
+ if(TopList[old_chan]) {
+ kdDebug(5008) << "In change channel, found it" << endl;
+ TopList.insert(new_chan, TopList.take(old_chan));
+ }
+ else {
+ kdDebug(5008) << "In change channel, didn;t find it" << endl;
+ }
+ emit ProcMessage(serverID(), ProcCommand::changeChannel,
+ old_chan + " " + new_chan);
+}
+
+void KSircProcess::filters_update()
+{
+ QString command, next_part, key, data;
+ command = "/crule\n";
+ iocontrol->stdin_write(command.ascii());
+ QDictIterator<KSircMessageReceiver> it(TopList);
+ KSircMessageReceiver *cur, *br;
+ filterRuleList *frl;
+ filterRule *fr;
+ cur = TopList["!base_rules"];
+ br = cur;
+ while(cur){
+ frl = cur->defaultRules();
+ for ( fr=frl->first(); fr != 0; fr=frl->next() ){
+ command.truncate(0);
+ command += "/ksircappendrule DESC==";
+ command += fr->desc;
+ command += " !!! SEARCH==";
+ command += fr->search;
+ command += " !!! FROM==";
+ command += fr->from;
+ command += " !!! TO==\"";
+ command += fr->to;
+ command += "\"\n";
+ iocontrol->stdin_write(command.local8Bit());
+ }
+ delete frl;
+ ++it;
+ cur = it.current();
+ if(cur == br){
+ ++it;
+ cur = it.current();
+ }
+ }
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("FilterRules");
+ int max = kConfig->readNumEntry("Rules", 0);
+ for(int number = 1; number <= max; number++){
+ command.truncate(0);
+ key.sprintf("name-%d", number);
+ next_part.sprintf("/ksircappendrule DESC==%s !!! ", kConfig->readEntry(key).ascii());
+ command += next_part;
+ key.sprintf("search-%d", number);
+ next_part.sprintf("SEARCH==%s !!! ", kConfig->readEntry(key).ascii());
+ command += next_part;
+ key.sprintf("from-%d", number);
+ next_part.sprintf("FROM==%s !!! ", kConfig->readEntry(key).ascii());
+ command += next_part;
+ key.sprintf("to-%d", number);
+ next_part.sprintf("TO==\"%s\"\n", kConfig->readEntry(key).ascii());
+ command += next_part;
+ iocontrol->stdin_write(command.ascii());
+ }
+}
+
+
+void KSircProcess::notify_forw_online(QString nick)
+{
+ emit ProcMessage(serverID(), ProcCommand::nickOnline, nick);
+}
+
+void KSircProcess::notify_forw_offline(QString nick)
+{
+ emit ProcMessage(serverID(), ProcCommand::nickOffline, nick);
+}
+
+void KSircProcess::ServMessage(QString dst_server, int command, QString args)
+{
+ if(dst_server.isEmpty() || (dst_server == serverID())){
+ switch(command){
+ case ServCommand::updateFilters:
+ filters_update();
+ break;
+ default:
+ kdDebug(5008) << "Unkown command: " << command << " to " << command << " args " << args << endl;
+ break;
+ }
+ }
+}
+
+void KSircProcess::turn_on_autocreate()
+{
+ emit ProcMessage(serverID(), ProcCommand::turnOnAutoCreate, QString());
+ auto_create_really = FALSE;
+}
+
+void KSircProcess::setNick(const QString nick)
+{
+ QString new_nick = nick;
+ while (!new_nick.isEmpty() &&
+ (new_nick[0].latin1() == '@' || new_nick[0].latin1() == '*'))
+ new_nick.remove(0, 1);
+ if(new_nick != m_nick){
+ m_nick = new_nick;
+ /*
+ * redo the filter rules since they use
+ * our nick
+ */
+ kdDebug(5008) << "Redo filters" << endl;
+ filters_update();
+ }
+
+}
+
+const QString KSircProcess::getNick() const
+{
+ return m_nick;
+}
+
+#include "ksircprocess.moc"
+
+// vim: ts=4 sw=4 et
diff --git a/ksirc/ksircprocess.h b/ksirc/ksircprocess.h
new file mode 100644
index 00000000..9a4cf7c3
--- /dev/null
+++ b/ksirc/ksircprocess.h
@@ -0,0 +1,92 @@
+#ifndef KSIRCPROCESS
+#define KSIRCPROCESS
+
+class KSircProcess;
+class KSircTopLevel;
+
+#include <qdict.h>
+#include <qobject.h>
+
+#include "iocontroller.h"
+#include "ksircserver.h"
+#include "ksircchannel.h"
+
+class KProcess;
+class KSircIOController;
+class KSircMessageReceiver;
+class KSircIOController;
+
+class KSircProcess : public QObject
+{
+ Q_OBJECT
+ friend class KSircIOController;
+public:
+ KSircProcess(QString &server_id, KSircServer &kss, QObject * parent=0, const char * name=0);
+ virtual ~KSircProcess();
+
+ QDict<KSircMessageReceiver> &getWindowList()
+ {
+ return TopList;
+ }
+
+ QPtrList<KSircMessageReceiver> messageReceivers() const;
+ const QDict<KSircMessageReceiver> &mrList() const;
+
+ QString serverName() const { return m_kss.server(); }
+ QString serverPort() const { return m_kss.port(); }
+ QString serverID() const { return m_serverid; }
+
+ KSircIOController *getIOController() { return iocontrol; }
+
+ const QString getNick() const;
+ void setNick(const QString nick);
+
+
+signals:
+ void ProcMessage(QString, int, QString);
+
+public slots:
+ virtual void ServMessage(QString server, int command, QString args);
+ virtual void close_toplevel(KSircTopLevel *, QString);
+ virtual void default_window(KSircTopLevel *);
+ virtual void recvChangeChannel(const QString &, const QString &);
+ virtual void new_toplevel(const KSircChannel &, bool safe = false);
+
+ virtual void filters_update();
+
+ virtual void notify_forw_online(QString);
+ virtual void notify_forw_offline(QString);
+
+protected slots:
+ virtual void clean_toplevel(QObject *clean);
+ virtual void request_quit( const QCString& command );
+ virtual void do_quit();
+
+ virtual void turn_on_autocreate();
+
+
+private:
+ void base_filters();
+ void cleanup();
+
+ KSircIOController *iocontrol;
+ KProcess *proc;
+
+ QDict<KSircMessageReceiver> TopList;
+
+ bool running_window;
+ bool default_follow_focus;
+ bool auto_create_really;
+
+ KSircServer m_kss;
+ const QString m_serverid;
+
+ /*
+ * m_nick is our nick as reported by the lower levels
+ */
+ QString m_nick;
+
+// char *server;
+};
+
+#endif
diff --git a/ksirc/ksircrc b/ksirc/ksircrc
new file mode 100644
index 00000000..548160e5
--- /dev/null
+++ b/ksirc/ksircrc
@@ -0,0 +1,62 @@
+[Colours]
+Background=234,234,234
+chan=57,36,162
+error=255,0,0
+info=0,0,255
+kcolour=true
+mcolour=false
+nickbcolour=-1
+nickfcolour=1
+text=0,0,0
+uscolour=6
+
+[ColourSchemes]
+Names=boren,wheels,danimo
+
+[ColourSchemes-boren]
+Background=0,0,0
+SelForeground=0,0,0
+SelBackground=255,255,255
+Channel=0,192,0
+Error=255,0,0
+Info=0,0,255
+NickBackground=invalid
+NickForeground=255,128,0
+OwnNick=255,0,0
+Text=0,192,192
+Link=201,97,205
+
+[ColourSchemes-wheels]
+Background=45,53,59
+SelForeground=45,53,59
+SelBackground=129,154,170
+Channel=72,46,204
+Error=255,0,0
+Info=0,0,255
+NickBackground=invalid
+NickForeground=165,155,161
+OwnNick=128,0,128
+Text=129,154,170
+Link=0,0,255
+
+[ColourSchemes-danimo]
+Background=0,0,0
+SelBackground=255,255,255
+SelForeground=0,0,0
+Channel=49,222,238
+Error=255,0,0
+Info=49,222,238
+NickBackground=invalid
+NickForeground=255,255,255
+OwnNick=243,255,7
+Text=255,255,255
+Link=49,222,238
+
+[General]
+AutoCreateWin=true
+AutoRejoin=true
+BeepNotify=true
+ColourPicker=true
+DisplayTopic=true
+NickCompletion=true
+WindowLength=200
diff --git a/ksirc/ksircserver.h b/ksirc/ksircserver.h
new file mode 100644
index 00000000..ec63e1f6
--- /dev/null
+++ b/ksirc/ksircserver.h
@@ -0,0 +1,34 @@
+#ifndef KSIRCSERVER_H
+#define KSIRCSERVER_H
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+class KSircServer
+{
+
+public:
+ KSircServer( const QString &server,
+ const QString &port,
+ const QString &script = QString::null,
+ const QString &password = QString::null,
+ bool ssl = false ) :
+ m_server(server), m_port(port), m_script(script),
+ m_password(password), m_ssl(ssl) {}
+
+ const QString server() const { return m_server; }
+ const QString port() const { return m_port; }
+ const QString script() const { return m_script; }
+ const QString password() const { return m_password; }
+ bool usessl() const { return m_ssl; }
+
+private:
+ const QString m_server;
+ const QString m_port;
+ const QString m_script;
+ const QString m_password;
+ const bool m_ssl;
+
+};
+
+#endif
diff --git a/ksirc/ksopts.cpp b/ksirc/ksopts.cpp
new file mode 100644
index 00000000..11b25ad8
--- /dev/null
+++ b/ksirc/ksopts.cpp
@@ -0,0 +1,542 @@
+
+#include <kapplication.h>
+#include <kconfig.h>
+
+#if KDE_IS_VERSION(3,1,92)
+#include <kuser.h>
+#else
+#include <unistd.h>
+#include <pwd.h>
+#endif
+
+#include <kdebug.h>
+
+#include "ksopts.h"
+
+
+KSOptions *KSOptions::s_options = 0;
+bool KSOChannel::applyGlobally = false;
+
+KSOGeneral::KSOGeneral()
+{
+ displayMode = MDI;
+ autoCreateWin = true;
+ autoCreateWinForNotice = false;
+ nickCompletion = true;
+ displayTopic = true;
+ oneLineEntry = true;
+ runDocked = false;
+// timeStamp = true;
+// beepNotify = true;
+ colorPicker = true;
+ autoRejoin = false;
+ windowLength = 200;
+// logging = false;
+// beepOnMsg = false;
+ publicAway = true;
+ // topicShow = true;
+ useColourNickList = false;
+ dockPopups = true;
+ autoSaveHistory = true;
+
+}
+
+QPixmap KSOGeneral::backgroundPixmap()
+{
+ if ( m_backgroundPixmap.isNull() && !backgroundFile.isEmpty() )
+ m_backgroundPixmap = QPixmap( backgroundFile );
+ return m_backgroundPixmap;
+}
+
+KSOStartup::KSOStartup()
+{
+}
+
+KSOServer::KSOServer()
+{
+#if KDE_IS_VERSION(3,1,92)
+ KUser user;
+ nick = user.loginName();
+ userID = user.loginName();
+ realName = user.fullName();
+#else
+ struct passwd *pw = getpwuid( getuid() );
+ if ( pw )
+ {
+ nick = QString::fromLocal8Bit( pw->pw_name );
+ userID = QString::fromLocal8Bit( pw->pw_name );
+ realName = QString::fromLocal8Bit( pw->pw_gecos ).section( ',', 0, 0 );
+ }
+#endif
+}
+
+
+KSOColors::KSOColors()
+{
+ textColor = Qt::black;
+ linkColor = Qt::blue;
+ infoColor = Qt::blue;
+ channelColor = Qt::green;
+ errorColor = Qt::red;
+
+ ksircColors = true;
+ mircColors = false;
+ nickColourization = false;
+ ownNickBold = false;
+ ownNickUl = true;
+ ownNickRev = false;
+
+ msg1Regex = false;
+ msg2Regex = false;
+
+ backgroundColor = kapp->palette().color( QPalette::Active, QColorGroup::Mid );
+ selBackgroundColor = kapp->palette().color( QPalette::Active, QColorGroup::Highlight );
+ selForegroundColor = kapp->palette().color( QPalette::Active, QColorGroup::HighlightedText );
+
+ ircColors[0] = Qt::white;
+ ircColors[1] = Qt::black;
+ ircColors[2] = Qt::darkBlue;
+ ircColors[3] = Qt::darkGreen;
+ ircColors[4] = Qt::red;
+ ircColors[5] = QColor ( 165, 42, 42 );
+ ircColors[6] = Qt::darkMagenta;
+ ircColors[7] = QColor( 255, 0x7b, 00 );
+ ircColors[8] = Qt::yellow;
+ ircColors[9] = Qt::green;
+ ircColors[10] = Qt::darkCyan;
+ ircColors[11] = Qt::cyan;
+ ircColors[12] = Qt::blue;
+ ircColors[13] = QColor( 255, 0, 255 );
+ ircColors[14] = Qt::gray;
+ ircColors[15] = Qt::lightGray;
+
+ uint i;
+ for(i = 0; i < numColors; i++){
+ nickHighlight[i] = true;
+ }
+
+ colourTheme = "Custom";
+}
+
+KSORMBMenu::KSORMBMenu()
+{
+}
+
+KSOServChan::KSOServChan()
+{
+}
+
+KSOChannel::KSOChannel()
+{
+ timeStamp = false;
+ beepNotify = false;
+ beepOnMsg = false;
+ logging = false;
+ topicShow = true;
+ filterJoinPart = false;
+ applyGlobally = false;
+ encoding = "Default";
+}
+
+void KSOptions::load( int sections )
+{
+ KConfig *conf = kapp->config();
+ conf->setGroup( "ServerController" );
+ geometry = conf->readRectEntry( "Size" );
+
+ // Using the fields itself as default values as they are already
+ // set to the defaults.
+ if ( sections & General )
+ {
+ conf->setGroup( "General" );
+ int mode = conf->readNumEntry( "DisplayMode", static_cast<int>(displayMode) );
+ if (mode == 0)
+ displayMode = SDI;
+ else
+ displayMode = MDI;
+
+ autoCreateWin = conf->readBoolEntry( "AutoCreateWin", autoCreateWin );
+ autoCreateWinForNotice = conf->readBoolEntry( "AutoCreateWinForNotice", autoCreateWinForNotice );
+ nickCompletion = conf->readBoolEntry( "NickCompletion", nickCompletion );
+ displayTopic = conf->readBoolEntry( "DisplayTopic", displayTopic );
+ oneLineEntry = conf->readBoolEntry( "OneLineEntry", oneLineEntry );
+ runDocked = conf->readBoolEntry( "runDocked", runDocked );
+// timeStamp = conf->readBoolEntry( "TimeStamp", timeStamp );
+// beepNotify = conf->readBoolEntry( "BeepNotify", beepNotify );
+ colorPicker = conf->readBoolEntry( "ColourPicker", colorPicker );
+ autoRejoin = conf->readBoolEntry( "AutoRejoin", autoRejoin );
+ windowLength = conf->readNumEntry( "WindowLength", windowLength );
+ backgroundFile = conf->readPathEntry( "BackgroundFile", backgroundFile );
+// logging = conf->readBoolEntry( "Logging", logging );
+// beepOnMsg = conf->readBoolEntry( "BeepOnMessage", false );
+ publicAway = conf->readBoolEntry( "PublicAway", publicAway );
+ // topicShow = conf->readBoolEntry("TopicShow", topicShow);
+ useColourNickList = conf->readBoolEntry("ColourNickList", useColourNickList);
+ dockPopups = conf->readBoolEntry("dockPopups", dockPopups);
+ autoSaveHistory = conf->readBoolEntry("autoSaveHistory", autoSaveHistory);
+ }
+
+ if ( sections & Startup )
+ {
+ conf->setGroup( "StartUp" );
+ }
+
+
+ if ( sections & Colors )
+ {
+ if ( !conf->hasGroup( "ColorScheme" ) && conf->hasGroup( "Colours" ) )
+ {
+ conf->setGroup( "Colours" );
+ textColor = conf->readColorEntry( "text", &textColor );
+ infoColor = conf->readColorEntry( "info", &infoColor );
+ channelColor = conf->readColorEntry( "chan", &channelColor );
+ errorColor = conf->readColorEntry( "error", &errorColor );
+ int c = conf->readNumEntry( "uscolour", -1 );
+ if ( c >= 0 && c < 16 )
+ ownNickColor = ircColors[c];
+ c = conf->readNumEntry( "nickfcolour", -1 );
+ if ( c >= 0 && c < 16 )
+ nickForeground = ircColors[c];
+ c = conf->readNumEntry( "nickbcolour", -1 );
+ if ( c >= 0 && c < 16 )
+ nickBackground = ircColors[c];
+ backgroundColor = conf->readColorEntry( "Background", &backgroundColor );
+ }
+ else
+ {
+ conf->setGroup( "ColorScheme" );
+ textColor = conf->readColorEntry( "Text", &textColor );
+ linkColor = conf->readColorEntry( "Link", &linkColor );
+ infoColor = conf->readColorEntry( "Info", &infoColor );
+ channelColor = conf->readColorEntry( "Channel", &channelColor );
+ errorColor = conf->readColorEntry( "Error", &errorColor );
+ ownNickColor = conf->readColorEntry( "OwnNick", &ownNickColor );
+ ownNickBold = conf->readBoolEntry( "OwnNickBold", ownNickBold );
+ ownNickUl = conf->readBoolEntry( "OwnNickUnderline", ownNickUl );
+ ownNickRev = conf->readBoolEntry( "OwnNickReverse", ownNickRev );
+ msgContainNick = conf->readColorEntry( "msgContainNick", &ownNickColor ); // default to the ownNick colour
+ msg1Contain = conf->readColorEntry( "msg1Contain", &msg1Contain );
+ msg1String = conf->readEntry( "msg1String", msg1String );
+ msg1Regex = conf->readBoolEntry( "msg1Regex", msg1Regex );
+ msg2Contain = conf->readColorEntry( "msg2Contain", &msg2Contain );
+ msg2String = conf->readEntry( "msg2String", msg2String );
+ msg2Regex = conf->readBoolEntry( "msg2Regex", msg2Regex );
+ nickForeground = conf->readColorEntry( "NickForeground", &nickForeground );
+ nickBackground = conf->readColorEntry( "NickBackground", &nickBackground );
+ backgroundColor = conf->readColorEntry( "Background", &backgroundColor );
+ selBackgroundColor = conf->readColorEntry( "SelBackground", &selBackgroundColor );
+ selForegroundColor = conf->readColorEntry( "SelForeground", &selForegroundColor );
+ ksircColors = conf->readBoolEntry( "ksircColors", ksircColors );
+ mircColors = conf->readBoolEntry( "mircColors", mircColors );
+ nickColourization = conf->readBoolEntry( "nickColourization", mircColors );
+ colourTheme = conf->readEntry("ColourTheme", colourTheme );
+ for ( int i = 0; i < 16; ++i ) {
+ ircColors[i] =
+ conf->readColorEntry(QString::fromLatin1( "IRC-%1" ).arg( i ),
+ &ircColors[i] );
+ nickHighlight[i] =
+ conf->readBoolEntry(QString::fromLatin1( "nickHighlight-%1" ).arg( i ),
+ &nickHighlight[i] );
+ }
+ }
+ conf->setGroup( "GlobalOptions" );
+ QFont df = kapp->font(0);
+ defaultFont = conf->readFontEntry( "MainFont", &df);
+ QApplication::setFont( defaultFont, true, "KSirc::TextView" );
+ }
+ if ( sections & Channels )
+ {
+ conf->setGroup( "Channel" );
+
+ /*
+ * do global first
+ */
+ if(channel.contains("global") == FALSE){
+ KSOChannel global;
+ ChannelOpMap coMap;
+ channel.insert(QString("global"), coMap);
+ channel["global"].insert("global", global);
+ channel["global"]["global"].server = "global";
+ channel["global"]["global"].channel = "global";
+ channel["global"]["global"].timeStamp = conf->readBoolEntry("globalglobalTimeStamp", false);
+ channel["global"]["global"].beepNotify = conf->readBoolEntry("globalglobalBeepNotify", false);
+ channel["global"]["global"].beepOnMsg = conf->readBoolEntry("globalglobalBeepOnMsg", false);
+ channel["global"]["global"].logging = conf->readBoolEntry("globalglobalLogging", false);
+ channel["global"]["global"].topicShow = conf->readBoolEntry("globalglobalTopicShow", true);
+ channel["global"]["global"].filterJoinPart = conf->readBoolEntry("globalglobalFilterJoinPart", false);
+ channel["global"]["global"].applyGlobally = conf->readBoolEntry("globalglobalApplyGlobally", false);
+ channel["global"]["global"].encoding = conf->readEntry("globalglobalEncoding", "Default");
+ }
+
+ QDateTime now(QDateTime::currentDateTime());
+ QStringList servers = conf->readListEntry("Servers");
+ QStringList::ConstIterator ser = servers.begin();
+ for( ; ser != servers.end(); ser++){
+ ChannelOpMap coMap;
+ channel.insert(*ser, coMap);
+ QStringList channels = conf->readListEntry(*ser);
+ QStringList::ConstIterator chan = channels.begin();
+ for( ; chan != channels.end(); chan++){
+ QDateTime lastUsed = conf->readDateTimeEntry(*ser + *chan + "lastUsed", &now);
+ if((lastUsed.daysTo(now) < 30 )||
+ (*chan == "global" && *ser == "global"))
+ {
+ KSOChannel ksoc;
+ ksoc = channel["global"]["global"];
+ ksoc.server = *ser;
+ ksoc.channel = *chan;
+ ksoc.timeStamp = conf->readBoolEntry(*ser + *chan + "TimeStamp", ksoc.timeStamp);
+ ksoc.beepNotify = conf->readBoolEntry(*ser + *chan + "BeepNotify", ksoc.beepNotify);
+ ksoc.beepOnMsg = conf->readBoolEntry(*ser + *chan + "BeepOnMsg", ksoc.beepOnMsg);
+ ksoc.logging = conf->readBoolEntry(*ser + *chan + "Logging", ksoc.logging);
+ ksoc.topicShow = conf->readBoolEntry(*ser + *chan + "TopicShow", ksoc.topicShow);
+ ksoc.encoding = conf->readEntry(*ser + *chan + "Encoding", ksoc.encoding);
+ ksoc.filterJoinPart = conf->readBoolEntry(*ser + *chan + "FilterJoinPart", ksoc.filterJoinPart);
+ ksoc.lastUsed = lastUsed;
+ channel[*ser].insert(*chan, ksoc);
+ }
+ else { /* expire junk */
+ conf->deleteEntry(*ser + *chan + "TimeStamp");
+ conf->deleteEntry(*ser + *chan + "BeepNotify");
+ conf->deleteEntry(*ser + *chan + "BeepOnMsg");
+ conf->deleteEntry(*ser + *chan + "Logging");
+ conf->deleteEntry(*ser + *chan + "TopicShow");
+ conf->deleteEntry(*ser + *chan + "Encoding");
+ conf->deleteEntry(*ser + *chan + "FilterJoinPart");
+ conf->deleteEntry(*ser + *chan + "lastUsed");
+ }
+ }
+ }
+ }
+ if ( sections & Servers )
+ {
+ conf->setGroup( "Server" );
+
+ /*
+ * do global first
+ */
+ if(server.contains("global") == FALSE){
+ KSOServer global;
+ server.insert(QString("global"), global);
+ server["global"].server = "global";
+ server["global"].globalCopy = false;
+
+ conf->setGroup( "StartUp" );
+ server["global"].nick = conf->readEntry("Nick");
+ server["global"].altNick = conf->readEntry("altNick");
+ server["global"].realName = conf->readEntry("RealName");
+ server["global"].userID = conf->readEntry("userID");
+ server["global"].notifyList = conf->readListEntry("NotifyList");
+
+ conf->setGroup( "Server" );
+ }
+
+ kdDebug(5008) << "Load: in Server" << endl;
+
+ QStringList servers = conf->readListEntry("Servers");
+
+ QStringList::ConstIterator ser = servers.begin();
+ for( ; ser != servers.end(); ser++){
+ KSOServer ksos;
+ ksos = server["global"];
+ ksos.server = *ser;
+ ksos.globalCopy = false;
+ ksos.nick = conf->readEntry(*ser + "nick");
+ ksos.altNick = conf->readEntry(*ser + "altNick");
+ ksos.realName = conf->readEntry(*ser + "realName");
+ ksos.userID = conf->readEntry(*ser + "userID");
+ ksos.notifyList = conf->readListEntry(*ser + "notifyList");
+ kdDebug(5008) << *ser << ": nick: " << ksos.nick << " altNick: " << ksos.altNick << endl;
+ server.insert(*ser, ksos);
+ }
+ }
+ kdDebug(5008) << "Done full load" << endl;
+}
+
+void KSOptions::save( int sections )
+{
+ KConfig *conf = kapp->config();
+
+ if ( sections & General )
+ {
+ conf->setGroup( "General" );
+ conf->writeEntry( "DisplayMode", static_cast<int>(displayMode) );
+ conf->writeEntry( "AutoCreateWin", autoCreateWin );
+ conf->writeEntry( "AutoCreateWinForNotice", autoCreateWinForNotice );
+ conf->writeEntry( "NickCompletion", nickCompletion );
+ conf->writeEntry( "DisplayTopic", displayTopic );
+ conf->writeEntry( "OneLineEntry", oneLineEntry );
+ conf->writeEntry( "runDocked", runDocked );
+// conf->writeEntry( "TimeStamp", timeStamp );
+// conf->writeEntry( "BeepNotify", beepNotify );
+ conf->writeEntry( "ColourPicker", colorPicker );
+ conf->writeEntry( "AutoRejoin", autoRejoin );
+ conf->writeEntry( "WindowLength", windowLength );
+ conf->writePathEntry( "BackgroundFile", backgroundFile );
+// conf->writeEntry( "Logging", logging );
+// conf->writeEntry( "BeepOnMessage", beepOnMsg );
+ conf->writeEntry( "PublicAway", publicAway );
+ conf->writeEntry( "ColourNickList", useColourNickList );
+ conf->writeEntry( "dockPopups", dockPopups );
+ conf->writeEntry( "autoSaveHistory", autoSaveHistory );
+ }
+
+ if ( sections & Startup )
+ {
+ conf->setGroup( "StartUp" );
+ /*
+ conf->writeEntry( "Nick", nick );
+ conf->writeEntry( "AlternativeNick", altNick );
+ conf->writeEntry( "RealName", realName );
+ conf->writeEntry( "userID", userID );
+ conf->writeEntry( "NotifyList", notifyList );
+ */
+ }
+
+ if ( sections & Colors )
+ {
+ conf->setGroup( "ColorScheme" );
+ conf->writeEntry( "Text", textColor );
+ conf->writeEntry( "Link", linkColor );
+ conf->writeEntry( "Info", infoColor );
+ conf->writeEntry( "Channel", channelColor );
+ conf->writeEntry( "Error", errorColor );
+ conf->writeEntry( "OwnNick", ownNickColor );
+ conf->writeEntry( "OwnNickBold", ownNickBold );
+ conf->writeEntry( "OwnNickUnderline", ownNickUl );
+ conf->writeEntry( "OwnNickReverse", ownNickRev );
+ conf->writeEntry( "msgContainNick", msgContainNick );
+ conf->writeEntry( "msg1Contain", msg1Contain );
+ conf->writeEntry( "msg1String", msg1String );
+ conf->writeEntry( "msg1Regex", msg1Regex );
+ conf->writeEntry( "msg2Contain", msg2Contain );
+ conf->writeEntry( "msg2String", msg2String );
+ conf->writeEntry( "msg2Regex", msg2Regex );
+ conf->writeEntry( "NickForeground", nickForeground );
+ conf->writeEntry( "NickBackground", nickBackground );
+ conf->writeEntry( "Background", backgroundColor );
+ conf->writeEntry( "SelBackground", selBackgroundColor );
+ conf->writeEntry( "SelForeground", selForegroundColor );
+ conf->writeEntry( "ksircColors", (bool) ksircColors );
+ conf->writeEntry( "mircColors", mircColors );
+ conf->writeEntry( "nickColourization", nickColourization );
+ conf->writeEntry( "ColourTheme", colourTheme );
+ for ( int i = 0; i < 16; ++i ) {
+ conf->writeEntry( QString::fromLatin1( "IRC-%1" ).arg( i ), ircColors[i] );
+ conf->writeEntry( QString::fromLatin1( "nickHighlight-%1" ).arg( i ), nickHighlight[i] );
+ }
+ /* Store the font setting */
+ conf->setGroup( "GlobalOptions" );
+ conf->writeEntry("MainFont", defaultFont);
+ /* end of storing the font settings */
+ }
+ if ( sections & Channels )
+ {
+ QStringList servers;
+
+ conf->deleteGroup( "Channel" );
+
+ conf->setGroup( "Channel" );
+ ServerChannelOpMap::Iterator ser;
+ for( ser = channel.begin(); ser != channel.end(); ++ser ){
+ QStringList channels;
+
+ servers << ser.key();
+
+ ChannelOpMap::Iterator chan;
+ for( chan = ser.data().begin(); chan != ser.data().end(); ++chan ){
+ channels << chan.key();
+ conf->writeEntry(ser.key() + chan.key() + "TimeStamp", chan.data().timeStamp);
+ conf->writeEntry(ser.key() + chan.key() + "BeepNotify", chan.data().beepNotify);
+ conf->writeEntry(ser.key() + chan.key() + "BeepOnMsg", chan.data().beepOnMsg);
+ conf->writeEntry(ser.key() + chan.key() + "Logging", chan.data().logging);
+ conf->writeEntry(ser.key() + chan.key() + "TopicShow", chan.data().topicShow);
+ conf->writeEntry(ser.key() + chan.key() + "Encoding", chan.data().encoding);
+ conf->writeEntry(ser.key() + chan.key() + "FilterJoinPart", chan.data().filterJoinPart);
+ conf->writeEntry(ser.key() + chan.key() + "lastUsed", chan.data().lastUsed);
+ }
+ conf->writeEntry(ser.key(), channels);
+ }
+ conf->writeEntry("Servers", servers);
+ conf->writeEntry("globalglobalApplyGlobally", channel["global"]["global"].applyGlobally);
+ }
+ if ( sections & Servers )
+ {
+ QStringList servers;
+
+ conf->deleteGroup( "Servers" );
+
+ conf->setGroup( "Server" );
+ ServerOpMap::Iterator ser;
+ for( ser = server.begin(); ser != server.end(); ++ser ){
+ QStringList channels;
+
+ if(ser.data().globalCopy == true)
+ continue;
+
+ servers << ser.key();
+
+ conf->writeEntry(ser.key() + "nick", ser.data().nick);
+ conf->writeEntry(ser.key() + "altNick", ser.data().altNick);
+ conf->writeEntry(ser.key() + "realName", ser.data().realName);
+ conf->writeEntry(ser.key() + "userID", ser.data().userID);
+ conf->writeEntry(ser.key() + "notifyList", ser.data().notifyList);
+ }
+ conf->writeEntry("Servers", servers);
+ }
+
+ conf->sync();
+}
+
+void KSOptions::channelSetup(QString serv, QString chan)
+{
+ if(channel.contains(serv) == FALSE){
+ ChannelOpMap coMap;
+ channel.insert(serv, coMap);
+ }
+ if(channel[serv].contains(chan) == FALSE){
+ KSOChannel ksoc;
+ ksoc = channel["global"]["global"];
+ channel[serv].insert(chan, ksoc);
+ channel[serv][chan].server = serv;
+ channel[serv][chan].channel = chan;
+ channel[serv][chan].lastUsed = QDateTime::currentDateTime();
+ }
+ else {
+ channel[serv][chan].lastUsed = QDateTime::currentDateTime();
+ }
+}
+
+void KSOptions::applyChannelGlobal(void)
+{
+ ServerChannelOpMap::Iterator ser;
+ for( ser = channel.begin(); ser != channel.end(); ++ser ){
+ ChannelOpMap::Iterator chan;
+ for( chan = ser.data().begin(); chan != ser.data().end(); ++chan ){
+ if((chan.key() == "global") && (ser.key() == "global"))
+ continue;
+
+ chan.data() = channel["global"]["global"];
+ }
+ }
+
+}
+
+void KSOptions::serverSetup(QString serv)
+{
+ if(server.contains(serv) == FALSE){
+ KSOServer ksos;
+ ksos = server["global"];
+ server.insert(serv, ksos);
+ server[serv].server = serv;
+ server[serv].globalCopy = true;
+ server[serv].lastUsed = QDateTime::currentDateTime();
+ }
+ else {
+ server[serv].lastUsed = QDateTime::currentDateTime();
+ }
+}
+
+
+/* vim: et sw=4 ts=4
+ */
diff --git a/ksirc/ksopts.h b/ksirc/ksopts.h
new file mode 100644
index 00000000..5739f6fd
--- /dev/null
+++ b/ksirc/ksopts.h
@@ -0,0 +1,218 @@
+
+#ifndef _KSOPTS_H_
+#define _KSOPTS_H_
+#include <qmap.h>
+#include <qdict.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qrect.h>
+#include <qpixmap.h>
+#include <qdatetime.h>
+
+#define IRC_SAFE_MAX_LINE 450
+
+#include "boundscheckingarray.h"
+#include "ksircchannel.h"
+#include "ksircserver.h"
+
+// Central place to hold KSirc's options
+// and manage default values. The base classes
+// are only used directly by the prefs dialog.
+// The constructors initialize all options with default values.
+class KSOGeneral
+{
+public:
+ KSOGeneral();
+
+ enum { SDI, MDI } displayMode;
+ bool autoCreateWin : 1;
+ bool autoCreateWinForNotice : 1;
+ bool nickCompletion : 1;
+ bool displayTopic : 1;
+ bool oneLineEntry : 1;
+ bool runDocked : 1;
+// bool timeStamp : 1;
+// bool beepNotify : 1;
+ bool colorPicker : 1;
+ bool autoRejoin : 1;
+// bool beepOnMsg : 1;
+// bool logging : 1;
+ bool publicAway : 1;
+ bool useColourNickList : 1;
+ // bool topicShow : 1;
+ bool dockPopups : 1;
+ bool autoSaveHistory : 1;
+ int windowLength;
+ QString backgroundFile;
+
+ QPixmap backgroundPixmap();
+
+private:
+ QPixmap m_backgroundPixmap;
+};
+
+class KSOChannel
+{
+public:
+ KSOChannel();
+
+ QString server;
+ QString channel;
+
+ bool timeStamp : 1;
+ bool beepNotify : 1;
+ bool beepOnMsg : 1;
+ bool logging : 1;
+ bool topicShow : 1;
+ bool filterJoinPart : 1;
+ QString encoding;
+
+ QDateTime lastUsed;
+
+ static bool applyGlobally;
+
+
+
+};
+
+class KSOServer
+{
+public:
+ KSOServer();
+
+ QString server;
+ bool globalCopy;
+
+ QString nick;
+ QString altNick;
+ QString realName;
+ QString userID;
+ QStringList notifyList;
+
+ QDateTime lastUsed;
+};
+
+class KSOStartup
+{
+public:
+ KSOStartup();
+
+ QRect geometry;
+// QString nick;
+// QString altNick;
+// QString realName;
+// QString userID;
+// QStringList notifyList;
+};
+
+class KSOColors
+{
+public:
+ enum { numColors = 16 };
+
+ KSOColors();
+
+ QColor textColor;
+ QColor linkColor;
+ QColor infoColor;
+ QColor channelColor;
+ QColor errorColor;
+ QColor ownNickColor;
+ bool ownNickBold : 1;
+ bool ownNickUl : 1;
+ bool ownNickRev : 1;
+
+ QColor msgContainNick; // needs config + impl
+ QColor msg1Contain; // needs config
+ QString msg1String; // needs config
+ bool msg1Regex;
+ QColor msg2Contain; // need config
+ QString msg2String; // needs config
+ bool msg2Regex;
+
+ QColor nickForeground;
+ QColor nickBackground;
+ QColor backgroundColor;
+ QColor selBackgroundColor;
+ QColor selForegroundColor;
+ //QColor ircColors[numColors];
+ BoundsCheckingArray<QColor, numColors> ircColors;
+ BoundsCheckingArray<bool, numColors> nickHighlight;
+
+ QString colourTheme;
+
+ QFont defaultFont;
+ bool ksircColors : 1;
+ bool mircColors : 1;
+ bool nickColourization : 1;
+
+};
+
+class KSORMBMenu
+{
+public:
+ KSORMBMenu();
+};
+
+class KSOServChan
+{
+public:
+ KSOServChan();
+};
+
+typedef QMap<QString, KSOChannel> ChannelOpMap;
+typedef QMap<QString, ChannelOpMap> ServerChannelOpMap;
+typedef QMap<QString, KSOServer> ServerOpMap;
+
+class KSOptions
+ : public KSOGeneral,
+ public KSOStartup,
+ public KSOColors,
+ public KSORMBMenu,
+ public KSOServChan
+{
+public:
+ KSOptions() { s_options = this; }
+
+ void load( int sections = -1 );
+ void save( int sections = -1 );
+
+ static KSOptions *options() { return s_options; }
+
+ ServerChannelOpMap channel;
+ ServerOpMap server;
+
+ KSOChannel &chan( const KSircChannel &chanInfo )
+ {
+ return channel[chanInfo.server()][chanInfo.channel()];
+ }
+
+ KSOServer &serv( const KSircServer &servInfo )
+ {
+ return server[servInfo.server()];
+ }
+
+ void channelSetup(QString serv, QString chan);
+ void applyChannelGlobal(void);
+
+ void serverSetup(QString server);
+ void serverSetup( const KSircServer &servInfo )
+ {
+ serverSetup(servInfo.server());
+ }
+
+ enum {
+ All = -1, General = 1, Startup = 2,
+ Colors = 4, RMBMenu = 8, ServChan = 16,
+ Channels = 32, Servers = 64
+ };
+
+private:
+ static KSOptions *s_options;
+};
+
+#define ksopts (KSOptions::options())
+
+#endif
diff --git a/ksirc/ksparser.cpp b/ksirc/ksparser.cpp
new file mode 100644
index 00000000..587a0723
--- /dev/null
+++ b/ksirc/ksparser.cpp
@@ -0,0 +1,192 @@
+/* This file is part of ksirc
+ Copyright (c) 2001 Malte Starostik <malte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// $Id$
+
+#include <qstring.h>
+#include <qcolor.h>
+#include <qregexp.h>
+
+#include <kdebug.h>
+
+#include "ksopts.h"
+#include "ksparser.h"
+
+QString KSParser::parse( const QString &message )
+{
+ QString res;
+ m_tags.clear();
+ m_attributes.clear();
+ m_beeped = false;
+
+ for (unsigned int i = 0; i < message.length();)
+ {
+ QChar ch = message[i++];
+ if (ch.latin1() == 0x03 || ch == '~' && i < message.length())
+ {
+ QChar next = message[ i++ ];
+ if (next.latin1() >= 0x30 && next.latin1() <= 0x39)
+ {
+ int fg = -1, len;
+ int bg = -1;
+ QRegExp colorRex("^[0-9]+");
+ if (colorRex.search(message.mid(--i)) >= 0)
+ {
+ len = colorRex.matchedLength();
+ fg = message.mid(i, len).toInt();
+ i += len;
+ }
+ if (message[i] == ',')
+ {
+ if (colorRex.search(message.mid(++i)) >= 0)
+ {
+ len = colorRex.matchedLength();
+ bg = message.mid(i, len).toInt();
+ i += len;
+ }
+ }
+ QString at;
+ QColor c = ircColor(fg);
+ if ( c.isValid() ) {
+ at += QString( "color=\"%1\" " ).arg( c.name() );
+ }
+
+ c = ircColor(bg);
+ if ( c.isValid() ) {
+ at += QString( "bgcolor=\"%1\" " ).arg( c.name() );
+ }
+ if(at.length() > 0){
+ res += pushTag( "font", at );
+ }
+
+ }
+ else if (ch.latin1() == 0x03) {
+ res += popTag( "font" );
+ /*
+ * We moved it forward looking for the number, back
+ * up or we miss a character. Don't do
+ * res += next since we need to actaully parse that one
+ */
+ --i;
+ }
+ else if (ch == '~')
+ {
+ switch (next)
+ {
+ case 'c':
+ res += popTag( "font" );
+ break;
+ case 'C':
+ res += popAll();
+ break;
+ case 'r':
+ res += toggleTag( "r" );
+ break;
+ case 's': break;
+ case 'b':
+ res += toggleTag( "b" );
+ break;
+ case 'g':
+ m_beeped = true;
+ break;
+ case 'u':
+ res += toggleTag( "u" );
+ break;
+ case 'n':
+ res += toggleTag( "font", QString( "color=\"%1\"" ).arg( ksopts->nickForeground.name() ) );
+ break;
+ case 'o':
+ res += pushTag( "font", QString( "color=\"%1\"" ).arg( ksopts->msgContainNick.name() ) );
+ break;
+ case '#':
+ res += pushTag( "font", QString( "color=\"#%1\"" ).arg( message.mid(i, 6) ));
+ i+=6;
+ break;
+ case '~':
+ res += ch;
+ break;
+ default:
+ res += ch;
+ res += next;
+ break;
+ }
+ }
+ }
+ else
+ res += ch;
+
+ }
+
+ res.append( popAll() );
+
+ return res;
+}
+
+QString KSParser::pushTag(const QString &tag, const QString &attributes)
+{
+ QString res;
+ m_tags.push(tag);
+ if (!m_attributes.contains(tag))
+ m_attributes.insert(tag, attributes);
+ else if (!attributes.isEmpty())
+ m_attributes.replace(tag, attributes);
+ res.append("<" + tag);
+ if (!m_attributes[tag].isEmpty())
+ res.append(" " + m_attributes[tag]);
+ return res + ">";
+}
+
+QString KSParser::popTag(const QString &tag)
+{
+ if (!m_tags.contains(tag))
+ return QString::null;
+
+ QString res;
+ QValueStack<QString> savedTags;
+ while (m_tags.top() != tag)
+ {
+ savedTags.push(m_tags.pop());
+ res.append("</" + savedTags.top() + ">");
+ }
+ res.append("</" + m_tags.pop() + ">");
+ m_attributes.remove(tag);
+ while (!savedTags.isEmpty())
+ res.append(pushTag(savedTags.pop()));
+ return res;
+}
+
+QString KSParser::toggleTag(const QString &tag, const QString &attributes)
+{
+ return m_attributes.contains(tag) ? popTag(tag) : pushTag(tag, attributes);
+}
+
+QString KSParser::popAll()
+{
+ QString res;
+ while (!m_tags.isEmpty())
+ res.append("</" + m_tags.pop() + ">");
+ m_attributes.clear();
+ return res;
+}
+
+QColor KSParser::ircColor(int code)
+{
+ if (code >= 0 && code < 16)
+ return ksopts->ircColors[code];
+ return QColor();
+}
diff --git a/ksirc/ksparser.h b/ksirc/ksparser.h
new file mode 100644
index 00000000..e7d2647f
--- /dev/null
+++ b/ksirc/ksparser.h
@@ -0,0 +1,43 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+#ifndef __ksparser_h__
+#define __ksparser_h__
+
+#include <qstring.h>
+#include <qmap.h>
+#include <qcolor.h>
+#include <qvaluestack.h>
+
+// Helper class to parse IRC colour/style codes and convert them to
+// richtext. The parser maintains an internal stack of the styles
+// applied because the IRC message could contain sequences as
+// (bold)Hello (red)World(endbold)! (blue)blue text
+// which needs to be converted to
+// <b>Hello </b><font color="red"><b>World</b>! </font><font color="blue">blue text</font>
+// to get correctly nested tags. (malte)
+class KSParser
+{
+public:
+ QString parse(const QString &);
+ bool beeped() const { return m_beeped; }
+
+private:
+ QString pushTag(const QString &, const QString & = QString::null);
+ QString popTag(const QString &);
+ QString toggleTag(const QString &, const QString & = QString::null);
+ QString popAll();
+ QColor ircColor(int);
+
+private:
+ QValueStack<QString> m_tags;
+ QMap<QString, QString> m_attributes;
+ bool m_beeped;
+};
+
+#endif
+
+
diff --git a/ksirc/kstextview.cpp b/ksirc/kstextview.cpp
new file mode 100644
index 00000000..aedf31ad
--- /dev/null
+++ b/ksirc/kstextview.cpp
@@ -0,0 +1,2269 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "kstextview.h"
+#include "stringparserstate.h"
+
+#include <qpainter.h>
+#include <qvaluestack.h>
+#include <qdragobject.h>
+#include <qtimer.h>
+#include <qclipboard.h>
+#include <qdict.h>
+#include <kcharsets.h>
+#include <kapplication.h>
+#include <kmimesourcefactory.h>
+#include <kcursor.h>
+#include <kurldrag.h>
+#include <kdebug.h>
+#include <assert.h>
+
+using namespace KSirc;
+
+typedef StringParserState<QChar> ParsingState;
+
+static const int PaintBufferExtend = 128;
+
+// temporary(!)
+static QDict<QPixmap> *ksTextViewPixmapDict = 0;
+static void cleanupKSTextViewPixmapDict()
+{
+ delete ksTextViewPixmapDict;
+ ksTextViewPixmapDict = 0;
+}
+
+QPixmap ksTextViewLoadPixmap( const QString &icon )
+{
+ if ( !ksTextViewPixmapDict )
+ {
+ ksTextViewPixmapDict = new QDict<QPixmap>;
+ ksTextViewPixmapDict->setAutoDelete( true );
+ qAddPostRoutine( cleanupKSTextViewPixmapDict );
+ }
+
+ QPixmap *pix = ksTextViewPixmapDict->find( icon );
+ if ( !pix )
+ {
+ QImage img;
+
+ const QMimeSource *src = kapp->mimeSourceFactory()->data( icon, QString::null );
+ if ( !src || !QImageDrag::decode( src, img ) || img.isNull() )
+ return QPixmap();
+
+ pix = new QPixmap( img );
+ ksTextViewPixmapDict->insert( icon, pix );
+ }
+ return *pix;
+}
+
+Item::Item( TextParag *parag, const ItemProperties &props )
+ : m_extendsDirty( true ), m_minWidth( -1 ), m_width( - 1 ),
+ m_height( - 1 ), m_selection( NoSelection ), m_line( 0 ),
+ m_parag( parag ), m_props( props )
+{
+}
+
+Item::~Item()
+{
+}
+
+int Item::width() const
+{
+ if ( m_extendsDirty )
+ {
+ calcExtends();
+ m_extendsDirty = false;
+ }
+ return m_width;
+}
+
+int Item::minWidth() const
+{
+ if ( m_extendsDirty )
+ {
+ calcExtends();
+ m_extendsDirty = false;
+ }
+ return m_minWidth;
+}
+
+int Item::height() const
+{
+ if ( m_extendsDirty )
+ {
+ calcExtends();
+ m_extendsDirty = false;
+ }
+ return m_height;
+}
+
+Item *Item::breakLine( int )
+{
+ return 0; // can't break by default...
+}
+
+int Item::calcSelectionOffset( int x )
+{
+ return x; // default ...
+}
+
+void Item::selectionOffsets( int &startOffset, int &endOffset )
+{
+ m_parag->textView()->selectionOffsets( startOffset, endOffset );
+}
+
+int Item::maxSelectionOffset() const
+{
+ return text().len - 1;
+}
+
+StringPtr Item::text() const
+{
+ return StringPtr();
+}
+
+void Item::setProps( const ItemProperties &props )
+{
+ m_props = props;
+ m_extendsDirty = true;
+}
+
+Item *Item::create( TextParag *parag, const Token &tok, const ItemProperties &props )
+{
+ assert( tok.id != Token::TagClose );
+
+ if ( tok.id == Token::Text )
+ return new TextChunk( parag, tok.value, props );
+
+ if ( tok.value == "img" )
+ {
+ QString url = CONSTSTRING( tok.attributes[ "src" ] );
+ if ( url.isEmpty() )
+ return 0;
+
+ QPixmap pixmap = ksTextViewLoadPixmap( url );
+ if ( pixmap.isNull() )
+ return 0;
+
+ return new ImageItem( parag, pixmap );
+ }
+
+ return 0;
+}
+
+void Item::setLine(TextLine *line)
+{
+ m_line = line;
+}
+
+TextChunk::TextChunk( TextParag *parag, const StringPtr &text, const ItemProperties &props )
+ : Item( parag, props ), m_text( text ), m_originalTextLength( text.len ),
+ m_metrics( props.font ), m_parent(0)
+{
+}
+
+void TextChunk::paint( QPainter &p )
+{
+ p.setFont( m_props.font );
+
+ if ( m_selection == NoSelection )
+ paintText( p, 0, m_text );
+ else
+ paintSelection( p );
+}
+
+Item *TextChunk::breakLine( int width )
+{
+ ParsingState state( m_text.ptr, m_text.len );
+
+ const int requestedWidth = width;
+ const int spaceWidth = m_metrics.width( ' ' );
+
+ state.skip( ' ' );
+
+ if ( state.atEnd() ) // eh?
+ return 0;
+
+ StringPtr firstWord; firstWord.ptr = state.current();
+ firstWord.len = state.advanceTo( ' ' );
+
+ const int firstWordWidth = m_metrics.width( CONSTSTRING( firstWord ) );
+
+ if ( !state.atBegin() ) // some leading spaces?
+ width -= spaceWidth;
+
+ width -= firstWordWidth;
+
+ if ( width < 0 ) {
+ StringPtr rightHandSide = breakInTheMiddle( requestedWidth );
+ if ( rightHandSide.isNull() )
+ return 0;
+ return hardBreak( rightHandSide );
+ }
+
+ while ( !state.atEnd() )
+ {
+ bool spaceSeen = state.skip( ' ' ) > 0;
+
+ if ( state.atEnd() )
+ break;
+
+ StringPtr word; word.ptr = state.current();
+ word.len = state.advanceTo( ' ' );
+
+ const int wordWidth = m_metrics.width( CONSTSTRING( word ) );
+
+ if ( spaceSeen )
+ {
+ width -= spaceWidth;
+ spaceSeen = false;
+ }
+
+ width -= wordWidth;
+ if ( width > 0 )
+ continue;
+
+ StringPtr split( word.ptr, state.end() - word.ptr );
+ return hardBreak( split );
+ }
+ return 0;
+}
+
+Item::LayoutResetStatus TextChunk::resetLayout()
+{
+
+ if ( m_originalTextLength == 0 )
+ {
+ if ( m_parent )
+ {
+ if ( m_selection == SelectionStart )
+ m_parent->mergeSelection( this, m_parag->textView()->selectionStart() );
+ else if ( m_selection == SelectionEnd )
+ m_parent->mergeSelection( this, m_parag->textView()->selectionEnd() );
+ else if ( m_selection == SelectionBoth )
+ {
+ m_parent->mergeSelection( this, m_parag->textView()->selectionStart() );
+ m_parent->mergeSelection( this, m_parag->textView()->selectionEnd() );
+ }
+ }
+
+ return DeleteItem;
+ }
+
+ m_extendsDirty |= ( m_text.len != m_originalTextLength );
+
+ m_text.len = m_originalTextLength;
+ return KeepItem;
+}
+
+int TextChunk::calcSelectionOffset( int x )
+{
+ // ### how to optimize?
+
+ QConstString tmp( m_text.ptr, m_text.len );
+ const QString &s = tmp.string();
+
+ uint i = 0;
+ int px = 0;
+ for (; i < m_text.len; ++i )
+ {
+ const int partialWidth = m_metrics.width( s, i + 1 );
+ if ( px <= x && x <= partialWidth )
+ return i;
+ px = partialWidth;
+ }
+
+ kdDebug(5008) << "calcSelectionOffset bug width:" << width() << " partialWidth: " << m_metrics.width( s, i + 1 )<< endl;
+ //assert( false );
+ return m_text.len-1;
+}
+
+StringPtr TextChunk::text() const
+{
+ return m_text;
+}
+
+void TextChunk::setProps( const ItemProperties &props )
+{
+ Item::setProps( props );
+ m_metrics = QFontMetrics( props.font );
+}
+
+void TextChunk::calcExtends() const
+{
+ QConstString tmp( m_text.ptr, m_text.len );
+ const QString &text = tmp.string();
+
+ m_width = m_metrics.width( text );
+ m_height = m_metrics.lineSpacing();
+
+ //m_minWidth = 0;
+
+ m_minWidth = m_metrics.charWidth( text, 1 );
+
+ /*
+ ParsingState state( m_text.ptr, m_text.len );
+
+ state.skip( ' ' );
+
+ if ( state.atEnd() ) // eh?
+ return;
+
+ StringPtr firstWord; firstWord.ptr = state.current();
+ firstWord.len = state.advanceTo( ' ' );
+
+ m_minWidth = m_metrics.width( CONSTSTRING( firstWord ) );
+ */
+}
+
+StringPtr TextChunk::breakInTheMiddle( int width )
+{
+ QConstString tmp( m_text.ptr, m_text.len );
+ const QString &s = tmp.string();
+
+ uint i = 0;
+ for (; i < m_text.len; ++i )
+ {
+ const int partialWidth = m_metrics.width( s, i + 1 );
+ if ( partialWidth >= width ) {
+ if ( i == 0 )
+ return StringPtr();
+ return StringPtr( m_text.ptr + i, m_text.len - i );
+ }
+ }
+
+ return StringPtr();
+}
+
+Item *TextChunk::hardBreak( const StringPtr &rightHandSide )
+{
+ TextChunk *chunk = new TextChunk( m_parag, rightHandSide, m_props );
+ chunk->m_originalTextLength = 0; // ### hack... You make the last line dynamic so if it's 1 word it doesn't chop itself up
+ if(m_parent == 0x0)
+ chunk->m_parent = this;
+ else
+ chunk->m_parent = m_parent;
+
+ m_text.len = rightHandSide.ptr - m_text.ptr;
+ m_extendsDirty = true;
+
+ SelectionPoint *selection = 0;
+ if ( m_selection == SelectionStart )
+ selection = m_parag->textView()->selectionStart();
+ else if ( m_selection == SelectionEnd )
+ selection = m_parag->textView()->selectionEnd();
+ else if ( m_selection == SelectionBoth ) {
+ SelectionPoint *selStart = m_parag->textView()->selectionStart();
+ SelectionPoint *selEnd = m_parag->textView()->selectionEnd();
+
+ if ( selStart->offset >= m_text.len ) {
+ selStart->offset -= m_text.len;
+ selEnd->offset -= m_text.len;
+ selStart->item = selEnd->item = chunk;
+ chunk->setSelectionStatus( m_selection );
+ m_selection = NoSelection;
+ } else if ( selEnd->offset >= m_text.len ) {
+ selEnd->offset -= m_text.len;
+ selEnd->item = chunk;
+ chunk->setSelectionStatus( SelectionEnd );
+ m_selection = SelectionStart;
+ }
+ }
+
+ if ( selection && selection->offset >= m_text.len ) {
+ selection->offset -= m_text.len;
+ selection->item = chunk;
+ chunk->setSelectionStatus( m_selection );
+ m_selection = NoSelection;
+ }
+
+ return chunk;
+}
+
+void TextChunk::paintSelection( QPainter &p )
+{
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ selectionOffsets( selectionStart, selectionEnd );
+
+ if ( m_selection == SelectionStart )
+ {
+ const int width = paintText( p, 0, StringPtr( m_text.ptr, selectionStart ) );
+ paintSelection( p, width, StringPtr( m_text.ptr + selectionStart,
+ m_text.len - selectionStart ) );
+ }
+ else if ( m_selection == InSelection )
+ paintSelection( p, 0, m_text );
+ else if ( m_selection == SelectionEnd )
+ {
+ const int width = paintSelection( p, 0, StringPtr( m_text.ptr, selectionEnd + 1 ) );
+ paintText( p, width, StringPtr( m_text.ptr + selectionEnd + 1,
+ m_text.len - selectionEnd - 1 ) );
+ }
+ else if ( m_selection == SelectionBoth )
+ {
+ int width = paintText( p, 0, StringPtr( m_text.ptr, selectionStart ) );
+ width += paintSelection( p, width, StringPtr( m_text.ptr + selectionStart,
+ selectionEnd - selectionStart + 1 ) );
+ paintText( p, width, StringPtr( m_text.ptr + selectionEnd + 1,
+ m_text.len - selectionEnd - 1 ) );
+ }
+}
+
+int TextChunk::paintSelection( QPainter &p, int x, const StringPtr &text )
+{
+ QConstString constString( text.ptr, text.len );
+ const QString &str = constString.string();
+
+ const int width = m_metrics.width( str );
+
+ const QColorGroup &cg = m_parag->textView()->colorGroup();
+
+ if (m_props.bgSelColor.isValid())
+ p.fillRect( x, 0, width, height(), m_props.bgSelColor );
+ else
+ p.fillRect( x, 0, width, height(), cg.highlight() );
+
+ if (m_props.selColor.isValid())
+ p.setPen( m_props.selColor );
+ else
+ p.setPen( cg.highlightedText() );
+
+ p.drawText( x, m_metrics.ascent(), str );
+
+ return width;
+}
+
+int TextChunk::paintText( QPainter &p, int x, const StringPtr &text )
+{
+ QConstString constString( text.ptr, text.len );
+ const QString &str = constString.string();
+
+ const int width = m_metrics.width( str );
+
+ if ( m_props.bgColor.isValid() )
+ p.fillRect( x, 0, width, height(), m_props.bgColor );
+
+ if ( m_props.color.isValid() )
+ p.setPen( m_props.color );
+ else
+ p.setPen( m_parag->textView()->foregroundColor() );
+
+ p.drawText( x, m_metrics.ascent(), str );
+
+ return width;
+}
+
+void TextChunk::mergeSelection( TextChunk *child, SelectionPoint *selection )
+{
+ selection->offset += child->m_text.ptr - m_text.ptr;
+
+ if(selection->offset > m_originalTextLength){
+ kdDebug(5008) << "Child: " << child->m_text.toQString() << " Parent: " << m_text.toQString() << endl;
+ kdDebug(5008) << "Length all wrong!" << endl;
+ //assert(0);
+ }
+
+ selection->item = this;
+
+ if ( ( m_selection == SelectionStart && child->selectionStatus() == SelectionEnd ) ||
+ ( m_selection == SelectionEnd && child->selectionStatus() == SelectionStart ) )
+ m_selection = SelectionBoth;
+ else
+ m_selection = child->selectionStatus();
+}
+
+ImageItem::ImageItem( TextParag *parag, const QPixmap &pixmap )
+ : Item( parag ), m_pixmap( pixmap )
+{
+}
+
+void ImageItem::paint( QPainter &painter )
+{
+ int y = 0;
+ if(m_line) {
+ y = (m_line->maxHeight() - m_pixmap.height())/2;
+ }
+
+ if ( m_selection != NoSelection ) {
+ int h;
+
+ if(m_line)
+ h = m_line->maxHeight();
+ else
+ h = height();
+
+ if (m_props.bgSelColor.isValid())
+ painter.fillRect( 0, 0, width(), h, m_props.bgSelColor );
+ else {
+ const QColorGroup &cg = m_parag->textView()->colorGroup();
+ painter.fillRect( 0, 0, width(), h, cg.highlight() );
+ }
+ }
+
+ painter.drawPixmap( 0, y, m_pixmap );
+}
+
+Item::LayoutResetStatus ImageItem::resetLayout()
+{
+ // nothin' to do
+ return KeepItem;
+}
+
+void ImageItem::calcExtends() const
+{
+ m_width = m_minWidth = m_pixmap.width();
+ m_height = m_pixmap.height();
+}
+
+Tokenizer::Tokenizer( PString &text )
+ : m_text( text.data ), m_tags( text.tags ),
+ m_textBeforeFirstTagProcessed( false ), m_done( false )
+{
+ //qDebug( "Tokenizer::Tokenizer( %s )", m_text.ascii() );
+
+ m_lastTag = m_tags.begin();
+
+ if ( !m_tags.isEmpty() ) {
+ if ( ( *m_tags.begin() ).type != TagIndex::Open ) {
+ qDebug( "something went awfully wrong! bailing out with an assertion" );
+ qDebug( "text input was: %s", text.data.ascii() );
+ }
+ assert( ( *m_tags.begin() ).type == TagIndex::Open );
+ }
+}
+
+Tokenizer::PString Tokenizer::preprocess( const QString &richText )
+{
+ PString result;
+ result.data = richText;
+ result.tags = scanTagIndices( result.data );
+ resolveEntities( result.data, result.tags );
+ return result;
+}
+
+QString Tokenizer::convertToRichText( const PString &ptext )
+{
+ if ( ptext.tags.isEmpty() )
+ return ptext.data;
+
+ QString result = ptext.data;
+ uint i = 0;
+ TagIndexList tags = ptext.tags;
+ TagIndexList::Iterator it = tags.begin();
+ TagIndexList::Iterator end = tags.end();
+
+ for (; i < result.length(); ++i )
+ {
+ if ( it != end && i == (*it).index )
+ {
+ ++it;
+ continue;
+ }
+
+ unsigned short indexAdjustment = 0;
+ const QChar ch = result[ i ];
+ // ### incomplete!
+ /* this doesn't work quite right (when resolving back
+ * KCharSet's fromEntity breaks)
+ if ( ch == '<' || ch == '>' || ch == '&' )
+ {
+ QString entity = KGlobal::charsets()->toEntity( ch );
+ indexAdjustment = entity.length() - 1;
+ result.replace( i, 1, entity );
+ }
+ */
+ if ( ch == '<' )
+ {
+ result.replace( i, 1, "&lt;" );
+ indexAdjustment = 3;
+ }
+ else if ( ch == '>' )
+ {
+ result.replace( i, 1, "&gt;" );
+ indexAdjustment = 3;
+ }
+ else if ( ch == '&' )
+ {
+ result.replace( i, 1, "&amp;" );
+ indexAdjustment = 4;
+ }
+
+ if ( indexAdjustment > 0 )
+ {
+ TagIndexList::Iterator tmpIt = it;
+ for (; tmpIt != end; ++tmpIt )
+ (*tmpIt).index += indexAdjustment;
+ }
+ }
+
+ return result;
+}
+
+bool Tokenizer::parseNextToken( Token &tok )
+{
+ if ( m_done )
+ {
+ //qDebug( "Tokenizer: premature end" );
+ return false;
+ }
+
+ if ( m_tags.isEmpty() )
+ {
+ tok.id = Token::Text;
+ tok.attributes.clear();
+ tok.value = StringPtr( m_text );
+ m_done = true;
+ return true;
+ }
+
+ TagIndexList::ConstIterator it = m_lastTag;
+ ++it;
+
+ if ( it == m_tags.end() )
+ {
+ m_done = true;
+
+ const uint idx = (*m_lastTag).index + 1;
+ if ( idx >= m_text.length() )
+ return false;
+
+ tok.id = Token::Text;
+ tok.value = StringPtr( m_text.unicode() + idx,
+ m_text.length() - idx );
+ tok.attributes.clear();
+ return true;
+ }
+
+ // text before first tag opening?
+ if ( m_lastTag == m_tags.begin() &&
+ (*m_lastTag).index > 0 &&
+ !m_textBeforeFirstTagProcessed )
+ {
+ tok.id = Token::Text;
+ tok.attributes.clear();
+ tok.value = StringPtr( m_text.unicode(),
+ (*m_lastTag).index );
+
+ m_textBeforeFirstTagProcessed = true;
+ return true;
+ }
+
+ const uint index = (*it).index;
+ const int type = (*it).type;
+ const uint lastIndex = (*m_lastTag).index;
+ const uint lastType = (*m_lastTag).type;
+
+ assert( lastIndex < index );
+
+ // a tag
+ if ( lastType == TagIndex::Open &&
+ type == TagIndex::Close )
+ {
+ const QChar *tagStart = m_text.unicode() + lastIndex + 1;
+ uint tagLen = ( index - 1 ) - ( lastIndex + 1 ) + 1;
+ // </bleh> ?
+ if ( *tagStart == '/' )
+ {
+ ++tagStart;
+ --tagLen;
+ tok.id = Token::TagClose;
+ }
+ else
+ tok.id = Token::TagOpen;
+
+ parseTag( StringPtr( tagStart, tagLen ), tok.value, tok.attributes );
+
+ m_lastTag = it;
+ return true;
+ }
+ // text
+ else if ( lastType == TagIndex::Close &&
+ type == TagIndex::Open )
+ {
+ tok.id = Token::Text;
+ tok.attributes.clear();
+ tok.value = StringPtr( m_text.unicode() + lastIndex + 1,
+ ( index - 1 ) - lastIndex );
+
+ m_lastTag = it;
+ return true;
+ }
+ else {
+ qDebug( "EEK, this should never happen. input text was: %s", m_text.ascii() );
+ assert( false );
+ }
+
+ return false;
+}
+
+Tokenizer::TagIndexList Tokenizer::scanTagIndices( const QString &text )
+{
+ const QChar *start = text.unicode();
+ const QChar *p = start;
+ const QChar *endP = p + text.length();
+ bool quoted = false;
+ bool inTag = false;
+
+ TagIndexList tags;
+
+ for (; p < endP; ++p )
+ {
+ const QChar ch = *p;
+ if ( ch == '"' && inTag )
+ {
+ quoted = quoted ? false : true;
+ continue;
+ }
+
+ if( quoted )
+ continue;
+
+ if ( ch == '<' )
+ {
+ inTag = true;
+ tags.append( TagIndex( p - start, TagIndex::Open ) );
+ continue;
+ }
+ else if ( ch == '>' )
+ {
+ inTag = false;
+ tags.append( TagIndex( p - start, TagIndex::Close ) );
+ continue;
+ }
+ }
+
+ return tags;
+}
+
+void Tokenizer::resolveEntities( QString &text, TagIndexList &tags )
+{
+ const QChar *p = text.unicode();
+ const QChar *endP = p + text.length();
+ uint i = 0;
+ bool scanForSemicolon = false;
+ const QChar *ampersand = 0;
+ TagIndexList::Iterator tagInfoIt = tags.begin();
+ TagIndexList::Iterator tagsEnd = tags.end();
+
+ for (; p < endP; ++p, ++i )
+ {
+ if ( tagInfoIt != tagsEnd &&
+ i > (*tagInfoIt).index )
+ ++tagInfoIt;
+
+ const QChar ch = *p;
+
+ if ( ch == '&' ) {
+ ampersand = p;
+ scanForSemicolon = true;
+ continue;
+ }
+
+ if ( ch != ';' || !scanForSemicolon )
+ continue;
+
+ assert( ampersand );
+
+ scanForSemicolon = false;
+
+ const QChar *entityBegin = ampersand + 1;
+
+ const uint entityLength = p - entityBegin;
+ if ( entityLength == 0 )
+ continue;
+
+ const QChar entityValue = KCharsets::fromEntity( QConstString( entityBegin, entityLength ).string() );
+ if ( entityValue.isNull() )
+ continue;
+
+ const uint ampersandPos = ampersand - text.unicode();
+
+ text[ ampersandPos ] = entityValue;
+ text.remove( ampersandPos + 1, entityLength + 1 );
+ i = ampersandPos;
+ p = text.unicode() + i;
+ endP = text.unicode() + text.length();
+ ampersand = 0;
+
+ uint adjustment = entityLength + 1;
+ TagIndexList::Iterator it = tagInfoIt;
+ for (; it != tags.end(); ++it )
+ (*it).index -= adjustment;
+ }
+}
+
+void Tokenizer::parseTag( const StringPtr &text,
+ StringPtr &tag,
+ AttributeMap &attributes )
+{
+ assert( text.len > 0 );
+
+ attributes.clear();
+ tag = StringPtr();
+
+ const QChar *p = text.ptr;
+ const QChar *endP = p + text.len;
+ const QChar *start = p;
+
+ int state = ScanForName;
+
+ StringPtr key;
+
+ while ( p < endP )
+ {
+ const QChar ch = *p;
+
+ if ( ch == ' ' )
+ {
+ start = ++p;
+ continue;
+ }
+
+ if ( state == ScanForEqual )
+ {
+ if ( ch == '=' )
+ {
+ state = ScanForValue;
+ ++p;
+ continue;
+ }
+ state = ScanForName;
+ }
+
+ if ( state == ScanForValue )
+ {
+ if ( ch == '=' ) // eh?
+ {
+ qDebug( "EH?" );
+ ++p;
+ continue;
+ }
+
+ if ( key.isNull() )
+ {
+ qDebug( "Tokenizer: Error, attribute value without key." );
+ // reset
+ state = ScanForName;
+ ++p;
+ continue;
+ }
+
+ start = 0x0;
+ if ( *p == '"' )
+ {
+ ++p;
+ start = p;
+ while ( p < endP && *p != '"' ) {
+ ++p;
+ }
+ }
+ else {
+ while ( p < endP && *p != ' ' && *p != '>') {
+ if(!start)
+ start = p;
+ ++p;
+ }
+ }
+
+ if(start == 0x0) {
+ state = ScanForName;
+ qDebug( "Never found start \" in tag." );
+ ++p;
+ continue;
+ }
+
+ const QChar *valueEnd = p;
+
+ StringPtr value = StringPtr( start, valueEnd - start );
+ attributes[ key ] = value;
+
+ if(*p == '"')
+ ++p; // move p beyond the last "
+ state = ScanForName;
+ continue;
+ }
+
+ if ( state == ScanForName )
+ {
+ while ( p < endP && *p != ' ' && *p != '=' ){
+ ++p;
+ }
+
+ key = StringPtr( start, p - start );
+
+ if ( tag.isNull() )
+ tag = key;
+ else
+ attributes[ key ] = StringPtr();
+
+ state = ScanForEqual;
+ continue;
+ }
+
+ assert( false ); // never reached.
+ }
+
+/*
+ kdDebug(5008) << "tagName: " << tag.toQString() << endl;
+ AttributeMap::ConstIterator it = attributes.begin();
+ for (; it != attributes.end(); ++it )
+ kdDebug(5008) << "attribute: " << it.key().toQString() <<
+ " -> " << it.data().toQString() << endl;
+*/
+}
+
+ItemProperties::ItemProperties()
+ : reversed( false )
+{
+}
+
+ItemProperties::ItemProperties( const QFont &defaultFont )
+ : font( defaultFont ), reversed( false )
+{
+}
+
+ItemProperties::ItemProperties( const ItemProperties &other,
+ const Token &token,
+ TextView *textView )
+ : attributes( token.attributes )
+{
+ // inherit
+ font = other.font;
+ color = other.color;
+ bgColor = other.bgColor;
+ bgSelColor = other.bgSelColor;
+ selColor = other.selColor;
+ reversed = other.reversed;
+
+ if ( token.value == "b" )
+ font.setBold( true );
+ else if ( token.value == "i" )
+ font.setItalic( true );
+ else if ( token.value == "u" )
+ font.setUnderline( true );
+ else if ( token.value == "r" ) {
+ reversed = true;
+ if(other.bgColor.isValid())
+ color = other.bgColor;
+ else
+ color = textView->paletteBackgroundColor();
+
+ if(other.color.isValid())
+ bgColor = other.color;
+ else
+ bgColor = textView->foregroundColor();
+ }
+ else if ( token.value == "font" )
+ {
+ StringPtr colAttr = attributes[ "color" ];
+ if ( !colAttr.isNull() )
+ {
+ QColor col( CONSTSTRING( colAttr ) );
+ if ( col.isValid() ){
+ if(!reversed)
+ color = col;
+ else
+ bgColor = col;
+ }
+ }
+ colAttr = attributes[ "bgcolor" ];
+ if ( !colAttr.isNull() )
+ {
+ QColor col( CONSTSTRING( colAttr ) );
+ if ( col.isValid() ) {
+ if(!reversed)
+ bgColor = col;
+ else
+ color = col;
+ }
+ }
+ }
+ else if ( token.value == "a" )
+ {
+ color = textView->linkColor();
+ font.setUnderline( true );
+ }
+}
+
+ItemProperties::ItemProperties( const ItemProperties &rhs )
+{
+ ( *this ) = rhs;
+}
+
+ItemProperties &ItemProperties::operator=( const ItemProperties &rhs )
+{
+ font = rhs.font;
+ color = rhs.color;
+ bgColor = rhs.bgColor;
+ bgSelColor = rhs.bgSelColor;
+ selColor = rhs.selColor;
+ reversed = rhs.reversed;
+ attributes = rhs.attributes;
+ return *this;
+}
+
+void ItemProperties::updateFont( const QFont &newFont )
+{
+ QFont f = newFont;
+ f.setUnderline( font.underline() );
+ f.setBold( font.bold() );
+ f.setItalic( font.italic() );
+ font = f;
+}
+
+TextLine::TextLine()
+ : m_maxHeight( 0 )
+{
+ m_items.setAutoDelete( true );
+}
+
+TextLine::TextLine( const QPtrList<Item> &items )
+ : m_maxHeight( 0 )
+{
+ m_items.setAutoDelete( true );
+
+ assert( !items.autoDelete() );
+
+ QPtrListIterator<Item> it( items );
+ for (; it.current(); ++it )
+ appendItem( it.current(), UpdateMaxHeight );
+}
+
+QString TextLine::updateSelection( const SelectionPoint &start, const SelectionPoint &end )
+{
+ QString selectedText;
+
+ // fixes a crash where because of an empty list i becomes null
+ if ( m_items.isEmpty() )
+ return QString::null;
+
+ if ( start.line == this )
+ {
+ const int idx = m_items.findRef( start.item );
+ assert( idx != -1 );
+ }
+ else
+ m_items.first();
+
+ Item *i = m_items.current();
+
+ Item *lastItem = 0;
+
+ if ( end.line == this )
+ {
+ const int oldCurrent = m_items.at();
+
+ const int idx = m_items.findRef( end.item );
+ assert( idx != -1 );
+ lastItem = m_items.next();
+
+ m_items.at( oldCurrent );
+ }
+
+ for (; i != lastItem && i!=0L; i = m_items.next() )
+ {
+ if ( i == start.item )
+ {
+ i->setSelectionStatus( Item::SelectionStart );
+
+ StringPtr txt = i->text();
+ if ( !txt.isNull() )
+ selectedText += QString( txt.ptr + start.offset,
+ txt.len - start.offset );
+ }
+ else if ( i == end.item )
+ {
+ i->setSelectionStatus( Item::SelectionEnd );
+
+ StringPtr txt = i->text();
+ if ( !txt.isNull() )
+ selectedText += QString( txt.ptr, end.offset + 1 );
+ }
+ else
+ {
+ i->setSelectionStatus( Item::InSelection );
+
+ selectedText += i->text().toQString();
+ }
+ }
+
+ return selectedText;
+}
+
+void TextLine::clearSelection()
+{
+ Item *i = m_items.first();
+ for (; i; i = m_items.next() )
+ i->setSelectionStatus( Item::NoSelection );
+}
+
+void TextLine::appendItem( Item *i, int layoutUpdatePolicy )
+{
+ m_items.append( i );
+ i->setLine(this);
+
+ if ( layoutUpdatePolicy == UpdateMaxHeight )
+ m_maxHeight = kMax( m_maxHeight, i->height() );
+}
+
+Item *TextLine::resetLayout( QPtrList<Item> &remainingItems)
+{
+ Item *lastLineItem = m_items.getLast();
+ Item *it = m_items.first();
+ // We have to use the tobeDel structure or we call resetLayout on the Item
+ // twice for each item we want to delete
+ QPtrList<Item> tobeDel;
+ while ( it )
+ {
+ if ( it->resetLayout() == Item::KeepItem )
+ remainingItems.append( m_items.take() );
+ else
+ tobeDel.append( m_items.take() );
+
+ it = m_items.current();
+ }
+ m_items = tobeDel;
+ return lastLineItem;
+}
+
+void TextLine::paint( QPainter &p, int y )
+{
+ QPtrListIterator<Item> it( m_items );
+ int x = 0;
+ for (; it.current(); ++it )
+ {
+ p.translate( x, y );
+ it.current()->paint( p );
+ p.translate( -x, -y );
+ x += it.current()->width();
+ }
+}
+
+Item *TextLine::itemAt( int px, SelectionPoint *selectionInfo,
+ Item::SelectionAccuracy accuracy )
+{
+ QPtrListIterator<Item> it( m_items );
+ int x = 0;
+ int width = 0;
+ for (; it.current(); ++it )
+ {
+ width = it.current()->width();
+ if ( x < px && px < ( x + width ) )
+ {
+ Item *i = it.current();
+ if ( selectionInfo )
+ {
+ selectionInfo->pos.setX( x );
+ selectionInfo->offset = i->calcSelectionOffset( px - x );
+ selectionInfo->item = i;
+ selectionInfo->line = this;
+ }
+ return i;
+ }
+ x += width;
+ }
+
+ if ( accuracy == Item::SelectFuzzy && selectionInfo &&
+ !m_items.isEmpty() && width > 0 )
+ {
+ Item *i = m_items.getLast();
+ selectionInfo->pos.setX( x - width );
+ selectionInfo->offset = i->maxSelectionOffset();
+ selectionInfo->item = i;
+ selectionInfo->line = this;
+ }
+
+ return 0;
+}
+
+QString TextLine::plainText() const
+{
+ QString res;
+ QPtrListIterator<Item> it( m_items );
+ for (; it.current(); ++it )
+ res += it.current()->text().toQString();
+ return res;
+}
+
+void TextLine::fontChange( const QFont &newFont )
+{
+ QPtrListIterator<Item> it( m_items );
+ for (; it.current(); ++it )
+ {
+ ItemProperties props = it.current()->props();
+ props.updateFont( newFont );
+ it.current()->setProps( props );
+ }
+}
+
+TextParag::TextParag( TextView *textView, const QString &richText )
+ : m_layouted( false ), m_height( 0 ), m_minWidth( 0 ),
+ m_textView( textView )
+{
+ setRichText( richText );
+ m_lines.setAutoDelete(true);
+}
+
+TextParag::~TextParag()
+{
+}
+
+void TextParag::layout( int width )
+{
+ QPtrList<Item> items;
+
+ TextLine *row = m_lines.first();
+
+ for(;row;row = m_lines.next()){
+ row->resetLayout( items );
+ }
+
+ m_lines.clear();
+
+ // all text chunks are now in a flat list. break them into
+ // pieces of lists of chunks, so they fit with the given width
+
+ m_height = 0;
+ m_minWidth = 0;
+
+ int remainingWidth = width;
+
+ SelectionPoint *selStart = m_textView->selectionStart();
+ SelectionPoint *selEnd = m_textView->selectionEnd();
+ assert( selStart && selEnd );
+
+ QPtrListIterator<Item> it( items );
+ while ( it.current() )
+ {
+ m_minWidth = kMax( m_minWidth, it.current()->minWidth() );
+
+ Item *item = it.current();
+ int itemWidth = item->width();
+
+ if ( remainingWidth >= itemWidth )
+ {
+ remainingWidth -= itemWidth;
+ ++it;
+ continue;
+ }
+
+ Item *newChunk = 0;
+
+ if ( itemWidth > item->minWidth() )
+ newChunk = item->breakLine( remainingWidth );
+
+ if ( newChunk || it.atFirst() )
+ ++it;
+
+ TextLine *line = new TextLine;
+
+ Item *next = it.current();
+
+ items.first();
+
+ while ( items.current() != next )
+ {
+ Item *i = items.take();
+
+ if ( selStart->item == i )
+ selStart->line = line;
+ else if ( selEnd->item == i )
+ selEnd->line = line;
+
+ line->appendItem( i, TextLine::UpdateMaxHeight );
+ }
+
+ assert( !line->isEmpty() );
+
+ m_height += line->maxHeight();
+ m_lines.append( line );
+
+ if ( newChunk )
+ items.prepend( newChunk );
+
+ it.toFirst();
+ remainingWidth = width;
+ }
+
+ // append what's left
+ if ( items.count() > 0 )
+ {
+ TextLine *line = new TextLine( items );
+ m_height += line->maxHeight();
+ m_lines.append( line );
+
+ if ( selStart->parag == this ||
+ selEnd->parag == this )
+ {
+ // ### move to TextLine?
+ QPtrListIterator<Item> it( line->iterator() );
+ for (; it.current(); ++it )
+ {
+ if ( selStart->item == it.current() )
+ selStart->line = line;
+ if ( selEnd->item == it.current() )
+ selEnd->line = line;
+ }
+ }
+ }
+
+ m_layouted = true;
+}
+
+void TextParag::paint( QPainter &p, int y, int maxY )
+{
+ TextLine *row = m_lines.first();
+ for (; row; row = m_lines.next() )
+ {
+ if( (y + row->maxHeight()) >= 0 )
+ row->paint( p, y );
+ y += row->maxHeight();
+ if( y > maxY )
+ break;
+ }
+}
+
+Item *TextParag::itemAt( int px, int py, SelectionPoint *selectionInfo,
+ Item::SelectionAccuracy accuracy )
+{
+ int y = 0;
+ int height = 0;
+ TextLine *row = m_lines.first();
+ for (; row; row = m_lines.next() )
+ {
+ height = row->maxHeight();
+ if ( y <= py && py <= ( y + height ) )
+ {
+ Item *i = row->itemAt( px, selectionInfo, accuracy );
+ if ( selectionInfo )
+ {
+ selectionInfo->pos.setY( y );
+ selectionInfo->parag = this;
+ }
+ return i;
+ }
+ y += height;
+ }
+
+ if ( accuracy == Item::SelectFuzzy && selectionInfo && !m_lines.isEmpty() )
+ {
+ TextLine *row = m_lines.getLast();
+ row->itemAt( px, selectionInfo, accuracy );
+
+ selectionInfo->pos.setY( y - height );
+ selectionInfo->parag = this;
+ }
+
+ return 0;
+}
+
+QString TextParag::updateSelection( const SelectionPoint &start, const SelectionPoint &end )
+{
+ QString selectedText;
+
+ // sanity check
+ // (don't put it lower because it changes the current list item)
+ if ( end.parag == this )
+ assert( m_lines.findRef( end.line ) != -1 );
+
+ if ( start.parag == this )
+ {
+ int idx = m_lines.findRef( start.line );
+ assert( idx != -1 );
+ }
+ else
+ m_lines.first();
+
+ TextLine *line = m_lines.current();
+
+ TextLine *lastLine = m_lines.getLast();
+
+ if ( end.parag == this )
+ lastLine = end.line;
+
+ for (; line != lastLine; line = m_lines.next() )
+ selectedText += line->updateSelection( start, end );
+
+ if ( lastLine )
+ selectedText += lastLine->updateSelection( start, end );
+
+ return selectedText;
+}
+
+void TextParag::clearSelection()
+{
+ // ### optimize, add 'selectionDirty' flag to TextLine and TextParag!
+ TextLine *line = m_lines.first();
+ for (; line; line = m_lines.next() )
+ line->clearSelection();
+}
+
+void TextParag::setRichText( const QString &richText )
+{
+ m_layouted = false;
+ m_height = 0;
+ m_minWidth = 0;
+
+ // ### FIXME SELECTION!!!!!!!!!
+ if ( m_textView->selectionStart()->parag == this ||
+ m_textView->selectionEnd()->parag == this )
+ m_textView->clearSelection();
+
+ m_lines.clear();
+
+ m_processedRichText = Tokenizer::preprocess( richText );
+
+ Tokenizer tokenizer( m_processedRichText );
+ Token tok;
+ Token lastTextToken;
+
+ QValueStack<Tag> tagStack;
+
+ TextLine *line = new TextLine;
+
+ while ( tokenizer.parseNextToken( tok ) )
+ {
+ if ( tok.id == Token::TagOpen )
+ {
+ ItemProperties oldProps( m_textView->font() );
+ if ( !tagStack.isEmpty() )
+ oldProps = tagStack.top().props;
+
+ // ...bleh<foo>... -> finish off 'bleh' first
+ if ( lastTextToken.id != -1 )
+ {
+ Item *item = Item::create( this, lastTextToken, oldProps );
+ if ( item )
+ line->appendItem( item );
+ lastTextToken = Token();
+ }
+
+ ItemProperties props( oldProps, tok, m_textView );
+ tagStack.push( Tag( tok.value, props ) );
+
+ Item *item = Item::create( this, tok, props );
+ if ( item )
+ line->appendItem( item );
+
+ continue;
+ }
+ else if ( tok.id == Token::TagClose )
+ {
+ assert( !tagStack.isEmpty() );
+
+ Tag tag = tagStack.pop();
+
+ if( !( tok.value == tag.name ) ) {
+ kdDebug(5008) << "ASSERT failed! tok.value=" << tok.value.toQString() << " tag.name=" << tag.name.toQString() << endl;
+ kdDebug(5008) << "while parsing " << richText << endl;
+ }
+
+ // ...foo</bleh>... -> finish off 'foo'
+ if ( !lastTextToken.value.isNull() )
+ {
+ Item *item = Item::create( this, lastTextToken, tag.props );
+ if ( item )
+ line->appendItem( item );
+ }
+
+ lastTextToken = Token();
+ }
+ else
+ lastTextToken = tok;
+ }
+
+ // some plain text at the very end, outside of any tag?
+ if ( !lastTextToken.value.isNull() )
+ {
+ Item *item = Item::create( this, lastTextToken );
+ if ( item )
+ line->appendItem( item );
+ }
+
+ m_lines.append( line );
+}
+
+QString TextParag::plainText() const
+{
+ QString result;
+ QPtrListIterator<TextLine> it( m_lines );
+ for (; it.current(); ++it )
+ result += it.current()->plainText();
+ return result;
+}
+
+void TextParag::fontChange( const QFont &newFont )
+{
+ QPtrListIterator<TextLine> it( m_lines );
+ for (; it.current(); ++it )
+ it.current()->fontChange( newFont );
+}
+
+ContentsPaintAlgorithm::ContentsPaintAlgorithm( const QPtrListIterator<TextParag> &paragIt,
+ QWidget *viewport, QPixmap &paintBuffer,
+ QPainter &painter, int clipX, int clipY,
+ int clipHeight )
+ : m_paragIt( paragIt ), m_viewport( viewport ), m_paintBuffer( paintBuffer ),
+ m_painter( painter ), m_clipX( clipX ), m_clipY( clipY ), m_clipHeight( clipHeight ),
+ m_overshoot( 0 )
+{
+}
+
+int ContentsPaintAlgorithm::goToFirstVisibleParagraph()
+{
+ int y = 0;
+
+ while ( y < m_clipY && m_paragIt.current() ) {
+ y += m_paragIt.current()->height();
+ ++m_paragIt;
+ }
+
+ y = adjustYAndIterator( y, y, m_clipY );
+ return y;
+}
+
+int ContentsPaintAlgorithm::paint( QPainter &bufferedPainter, int currentY )
+{
+ const int startY = currentY;
+
+ int nextY = startY + PaintBufferExtend;
+
+ if ( !m_paragIt.current() )
+ return nextY;
+
+ while ( currentY < nextY && m_paragIt.current() ) {
+
+ TextParag *parag = m_paragIt.current();
+
+ //kdDebug(5008) << "Painting[" << currentY << "/" << parag->height() << "]: " << parag->plainText() << endl;
+
+ int drawPos = currentY;
+ int newY = parag->height();
+
+ if(m_overshoot != 0) {
+ drawPos = currentY - parag->height() + m_overshoot;
+ newY = m_overshoot;
+ m_overshoot = 0;
+ }
+
+ parag->paint( bufferedPainter, drawPos, nextY );
+ currentY += newY;
+
+ ++m_paragIt;
+ }
+
+ nextY = adjustYAndIterator( startY, currentY, nextY );
+ return nextY;
+}
+
+int ContentsPaintAlgorithm::adjustYAndIterator( int , int currentY, int nextY )
+{
+ // nothing to adjust?
+ if ( currentY <= nextY || m_paragIt.atFirst() )
+ return currentY;
+
+ if ( m_paragIt.current() )
+ --m_paragIt;
+ else
+ m_paragIt.toLast();
+
+ m_overshoot = currentY - nextY;
+ if(m_overshoot < 0)
+ m_overshoot = 0;
+
+ return nextY;
+}
+
+void ContentsPaintAlgorithm::paint()
+{
+ int y = goToFirstVisibleParagraph();
+
+ int yEnd = m_clipY + m_clipHeight;
+
+ while ( y < yEnd )
+ {
+ m_paintBuffer.fill( m_viewport, 0, y );
+ QPainter bufferedPainter( &m_paintBuffer );
+ bufferedPainter.translate( -m_clipX, -y );
+ int nextY = paint( bufferedPainter, y );
+
+
+ bufferedPainter.end();
+
+ m_painter.drawPixmap( m_clipX, y, m_paintBuffer );
+
+ y = nextY;
+ }
+}
+
+TextView::TextView( QWidget *parent, const char *name )
+ : QScrollView( parent, name, WRepaintNoErase ),
+ m_paintBuffer( PaintBufferExtend, PaintBufferExtend ),
+ m_selectionEndBeforeStart( false ), m_mousePressed( false ),
+ m_mmbPressed( false ),
+ m_linkColor( Qt::blue ), m_height(-1), m_inScroll(false),
+ m_lastScroll(0)
+{
+ m_parags.setAutoDelete( true );
+ viewport()->setBackgroundMode( PaletteBase );
+ viewport()->setMouseTracking( true );
+ m_autoScrollTimer = new QTimer( this );
+ connect(verticalScrollBar(), SIGNAL(valueChanged( int ) ),
+ this, SLOT(scrolling( int )));
+
+ setDragAutoScroll( false );
+
+// setStaticBackground( true );
+}
+
+TextView::~TextView()
+{
+}
+
+void TextView::drawContents( QPainter *painter, int clipX, int clipY, int , int clipHeight )
+{
+ if ( m_parags.isEmpty() )
+ return;
+
+ if ( m_paintBuffer.width() != visibleWidth() )
+ m_paintBuffer.resize( visibleWidth(), PaintBufferExtend );
+
+ QPtrListIterator<TextParag> paragIt( m_parags );
+
+ ContentsPaintAlgorithm( paragIt,
+ viewport(), m_paintBuffer, *painter, clipX, clipY,
+ clipHeight )
+ .paint();
+}
+
+void TextView::viewportResizeEvent( QResizeEvent *ev )
+{
+ QScrollView::viewportResizeEvent(ev);
+
+ if ( ev->size().width() != ev->oldSize().width() )
+ layout();
+
+ int newdiff = ev->size().height() - ev->oldSize().height();
+ setContentsPos( 0, contentsY()-newdiff );
+
+ if(m_lastScroll == newdiff){
+ m_inScroll = false;
+ m_lastScroll = 0;
+ }
+
+ scrollToBottom();
+}
+
+void TextView::scrolling( int value )
+{
+ int tl = m_height-visibleHeight();
+ int offset = 25;
+
+ TextParag *parag = m_parags.last();
+ if(parag){
+ if(parag->height() > offset)
+ offset = parag->height();
+ }
+
+ if((tl - value) > offset)
+ m_inScroll = true;
+ else
+ m_inScroll = false;
+
+ m_lastScroll = tl-value;
+
+}
+
+void TextView::scrollToBottom( bool force )
+{
+ bool scroll = true;
+ if(force == true){
+ scroll = true;
+ }
+ else {
+ if(m_inScroll){
+ scroll = false;
+ }
+ else {
+ if(m_mousePressed == true){
+ scroll = false;
+ }
+ else {
+ scroll = true;
+ }
+ }
+ }
+
+ if(scroll == true)
+ setContentsPos( 0, m_height-visibleHeight() );
+}
+
+void TextView::clear()
+{
+ stopAutoScroll();
+ clearSelection();
+ m_parags.clear();
+ layout();
+ viewport()->erase();
+}
+
+TextParagIterator TextView::appendParag( const QString &richText )
+{
+ TextParag *parag = new TextParag( this, richText );
+ m_parags.append( parag );
+ layout( false );
+ scrollToBottom();
+ QPtrListIterator<TextParag> it( m_parags );
+ it.toLast();
+ return TextParagIterator( it );
+}
+
+bool TextView::removeParag( const TextParagIterator &parag )
+{
+
+ if ( parag.atEnd() )
+ return false;
+
+ TextParag *paragPtr = parag.m_paragIt.current();
+ const int idx = m_parags.findRef( paragPtr );
+ if ( idx == -1 )
+ return false;
+
+ if ( m_selectionStart.parag == paragPtr ||
+ m_selectionEnd.parag == paragPtr )
+ clearSelection( false );
+
+ int height = paragPtr->height();
+ m_parags.removeRef( paragPtr );
+
+ if(m_selectionStart.item != 0)
+ m_selectionStart.pos.ry() -= height;
+ if(m_selectionEnd.item != 0)
+ m_selectionEnd.pos.ry() -= height;
+
+ //layout( false );
+ contentsChange(-height, true);
+
+
+ if ( isUpdatesEnabled() )
+ updateContents();
+
+ return true;
+}
+
+void TextView::clearSelection( bool repaint )
+{
+ m_selectionStart = SelectionPoint();
+ m_selectionEnd = SelectionPoint();
+ m_selectionEndBeforeStart = false;
+ m_selectedText = QString::null;
+ clearSelectionInternal();
+ if ( repaint )
+ updateContents();
+}
+
+TextParagIterator TextView::firstParag() const
+{
+ return TextParagIterator( QPtrListIterator<TextParag>( m_parags ) );
+}
+
+QString TextView::plainText() const
+{
+ if ( m_parags.isEmpty() )
+ return QString::null;
+
+ QString result;
+ QPtrListIterator<TextParag> paragIt( m_parags );
+ while ( paragIt.current() )
+ {
+ result += paragIt.current()->plainText();
+ ++paragIt;
+ if ( paragIt.current() )
+ result += '\n';
+ }
+
+ return result;
+}
+
+QColor TextView::linkColor() const
+{
+ return m_linkColor;
+}
+
+void TextView::setLinkColor( const QColor &linkColor )
+{
+ m_linkColor = linkColor;
+}
+
+void TextView::copy()
+{
+ QApplication::clipboard()->setText( m_selectedText );
+}
+
+void TextView::clearSelectionInternal()
+{
+ m_selectionEndBeforeStart = false;
+ TextParag *p = m_parags.first();
+ for (; p; p = m_parags.next() )
+ p->clearSelection();
+}
+
+void TextView::contentsMousePressEvent( QMouseEvent *ev )
+{
+ if ( ev->button() & RightButton ) {
+ emitLinkClickedForMouseEvent( ev );
+ return;
+ }
+
+ if ( !( ev->button() & LeftButton ) && !(ev->button() & MidButton ) )
+ return;
+
+ clearSelection( true );
+ SelectionPoint p;
+ Item *itemUnderMouse = itemAt( ev->pos(), &p, Item::SelectFuzzy );
+ if ( p.item && ( ev->button() & LeftButton ) ) {
+ m_selectionMaybeStart = p;
+ p.item->setSelectionStatus( Item::NoSelection );
+ }
+ if ( TextChunk *text = dynamic_cast<TextChunk *>( itemUnderMouse ) ) {
+ StringPtr href = text->props().attributes[ "href" ];
+ if ( !href.isNull() ) {
+ m_dragStartPos = ev->pos();
+ m_dragURL = href.toQString();
+ if ( ev->button() & LeftButton )
+ m_mousePressed = true;
+ else
+ m_mmbPressed = true;
+ }
+ }
+}
+
+void TextView::contentsMouseMoveEvent( QMouseEvent *ev )
+{
+ if ( m_mousePressed && ev->state() == NoButton )
+ {
+ m_mousePressed = false;
+ m_mmbPressed = false;
+ }
+
+ if ( m_mousePressed && !m_dragURL.isEmpty() &&
+ ( m_dragStartPos - ev->pos() ).manhattanLength() > QApplication::startDragDistance() ) {
+
+ m_mousePressed = false;
+ m_dragStartPos = QPoint();
+
+ startDrag();
+
+ m_dragURL = QString::null;
+ return;
+ }
+
+
+ SelectionPoint p;
+ Item *i = itemAt( ev->pos(), &p, Item::SelectFuzzy );
+ if ( !i && !p.item )
+ return;
+
+ if ( (ev->state() & LeftButton && m_selectionStart.item && p.item) ||
+ (ev->state() & LeftButton && m_selectionMaybeStart.item && p.item))
+ {
+
+ if(m_selectionMaybeStart.item != 0){
+ m_selectionStart = m_selectionMaybeStart;
+ m_selectionMaybeStart = SelectionPoint();
+ }
+
+ m_selectionEnd = p;
+
+ clearSelectionInternal();
+
+ updateSelectionOrder();
+
+ SelectionPoint start = m_selectionStart;
+ SelectionPoint end = m_selectionEnd;
+
+ if ( m_selectionEndBeforeStart )
+ {
+ if ( start.item == end.item )
+ {
+ if ( start.offset > end.offset )
+ qSwap( start.offset, end.offset );
+ }
+ else
+ qSwap( start, end );
+ }
+
+ m_selectedText = updateSelection( start, end );
+
+ emit selectionChanged();
+
+ updateContents();
+
+ startAutoScroll();
+
+ return;
+ }
+ else if ( i )
+ {
+ TextChunk *text = dynamic_cast<TextChunk *>( i );
+ if ( text )
+ {
+ StringPtr href = text->props().attributes[ "href" ];
+ if ( !href.isNull() )
+ {
+ viewport()->setCursor( KCursor::handCursor() );
+ return;
+ }
+ }
+ }
+
+ QCursor c = KCursor::arrowCursor();
+ if ( viewport()->cursor().handle() != c.handle() )
+ viewport()->setCursor( c );
+}
+
+void TextView::contentsMouseReleaseEvent( QMouseEvent *ev )
+{
+ stopAutoScroll();
+
+ bool clicked = (m_mousePressed || m_mmbPressed) &&
+ (m_dragStartPos - ev->pos()).manhattanLength() < QApplication::startDragDistance();
+ m_mousePressed = false;
+ m_mmbPressed = false;
+ m_dragStartPos = QPoint();
+ m_dragURL = QString::null;
+
+ m_selectionMaybeStart = SelectionPoint();
+
+ if ( (ev->button() & Qt::LeftButton) && !m_selectedText.isEmpty() )
+ QApplication::clipboard()->setText( m_selectedText, QClipboard::Selection );
+
+ if ( clicked ) {
+ emitLinkClickedForMouseEvent( ev );
+ return;
+ }
+
+ if (ev->button() & Qt::MidButton)
+ {
+ emit pasteReq( KApplication::clipboard()->text( QClipboard::Selection ) );
+ return;
+ }
+}
+
+void TextView::fontChange( const QFont & )
+{
+ QPtrListIterator<TextParag> it( m_parags );
+ for (; it.current(); ++it )
+ it.current()->fontChange( font() );
+
+ layout( true );
+}
+
+void TextView::startDrag()
+{
+ QDragObject *dragObj = dragObject( m_dragURL );
+ if ( !dragObj )
+ return;
+
+ stopAutoScroll();
+
+ dragObj->drag();
+}
+
+QDragObject *TextView::dragObject( const QString &dragURL )
+{
+#if KDE_IS_VERSION(3,1,92)
+ return new KURLDrag( KURL( dragURL ), viewport() );
+#else
+ return KURLDrag::newDrag( KURL( dragURL ), viewport() );
+#endif
+}
+
+void TextView::autoScroll()
+{
+ QPoint cursor = viewport()->mapFromGlobal( QCursor::pos() );
+
+ QPoint contentsPos = viewportToContents( cursor );
+
+ cursor.rx() -= viewport()->x();
+ cursor.ry() -= viewport()->y();
+
+ if ( ( cursor.x() < 0 || cursor.x() > visibleWidth() ) ||
+ ( cursor.y() < 0 || cursor.y() > visibleHeight() ) ) {
+ ensureVisible( contentsPos.x(), contentsPos.y(),
+ 0, 5 );
+ }
+}
+
+void TextView::emitLinkClickedForMouseEvent( QMouseEvent *ev )
+{
+ TextChunk *text = dynamic_cast<TextChunk *>( itemAt( ev->pos() ) );
+ if ( !text )
+ return;
+
+ StringPtr href = text->props().attributes[ "href" ];
+ if ( href.isNull() )
+ return;
+
+ emit linkClicked( ev, CONSTSTRING( href ) );
+}
+
+void TextView::startAutoScroll()
+{
+ if(m_autoScrollTimer->isActive() == false){
+ connect( m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( autoScroll() ) );
+ m_autoScrollTimer->start( 75, false );
+ }
+}
+
+void TextView::stopAutoScroll()
+{
+ disconnect( m_autoScrollTimer, SIGNAL( timeout() ),
+ this, SLOT( autoScroll() ) );
+ m_autoScrollTimer->stop();
+}
+
+void TextView::selectionOffsets( int &startOffset, int &endOffset )
+{
+ assert( m_selectionStart.item );
+
+ if ( m_selectionEndBeforeStart )
+ {
+ startOffset = m_selectionEnd.offset;
+ endOffset = m_selectionStart.offset;
+ }
+ else
+ {
+ startOffset = m_selectionStart.offset;
+ endOffset = m_selectionEnd.offset;
+ }
+
+ if ( m_selectionStart.item == m_selectionEnd.item && startOffset > endOffset )
+ qSwap( startOffset, endOffset );
+}
+
+void TextView::updateSelectionOrder()
+{
+ int start = m_selectionStart.pos.y();
+ int end = m_selectionEnd.pos.y();
+
+ if ( start == end )
+ {
+ start = m_selectionStart.pos.x();
+ end = m_selectionEnd.pos.x();
+
+ if ( start == end )
+ {
+ start = m_selectionStart.offset;
+ end = m_selectionEnd.offset;
+ }
+ }
+
+ m_selectionEndBeforeStart = end < start;
+}
+
+SelectionPoint *TextView::selectionStart()
+{
+ return m_selectionEndBeforeStart ? &m_selectionEnd : &m_selectionStart;
+}
+
+SelectionPoint *TextView::selectionEnd()
+{
+ return m_selectionEndBeforeStart ? &m_selectionStart : &m_selectionEnd;
+}
+
+QString TextView::updateSelection( const SelectionPoint &start, const SelectionPoint &end )
+{
+ QString selectedText;
+
+ if ( start.item == end.item )
+ {
+ Item *i = start.item;
+
+
+ if ( start.offset == end.offset )
+ {
+ if ( start.pos.x() == end.pos.x() )
+ {
+ i->setSelectionStatus( Item::NoSelection );
+ return QString::null;
+ }
+
+ i->setSelectionStatus( Item::SelectionBoth );
+
+ // ### ugly
+ const TextChunk *t = dynamic_cast<TextChunk *>( i );
+ if ( t )
+ {
+ StringPtr text = t->text();
+ selectedText = QString( text.ptr + start.offset, 1 );
+ }
+ }
+ else
+ {
+ i->setSelectionStatus( Item::SelectionBoth );
+
+ // ### ugly
+ TextChunk *t = dynamic_cast<TextChunk *>( i );
+ if ( t )
+ {
+ StringPtr text = t->text();
+ if (end.offset > start.offset)
+ selectedText = QString( text.ptr + start.offset,
+ end.offset - start.offset + 1 );
+ else
+ selectedText = QString( text.ptr + end.offset,
+ start.offset - end.offset + 1 );
+
+ }
+ }
+ }
+ else
+ {
+ assert( m_parags.findRef( end.parag ) != -1 );
+ const int idx = m_parags.findRef( start.parag );
+ assert( idx != -1 );
+
+ TextParag *p = m_parags.current();
+ for (; p && p != end.parag; p = m_parags.next() )
+ {
+ selectedText += p->updateSelection( start, end );
+ selectedText += '\n';
+ }
+
+ if ( p )
+ selectedText += p->updateSelection( start, end );
+ }
+
+ return selectedText;
+}
+
+void TextView::layout( bool force )
+{
+ int height = 0;
+ int contentsWidth = visibleWidth();
+ int width = contentsWidth;
+
+ QPtrListIterator<TextParag> it( m_parags );
+ for (; it.current(); ++it )
+ {
+ if ( !it.current()->isLayouted() || force )
+ it.current()->layout( width );
+
+ height += it.current()->height();
+ contentsWidth = kMax( contentsWidth, it.current()->minWidth() );
+ }
+
+ if ( m_selectionStart.item && m_selectionEnd.item )
+ updateSelection( *selectionStart(), *selectionEnd() );
+
+ m_height = height;
+ resizeContents( contentsWidth, height );
+}
+
+void TextView::contentsChange(int heightChange, bool force)
+{
+ if(m_height == -1){
+ layout(force);
+ }
+ else {
+ m_height += heightChange;
+ resizeContents( visibleWidth(), m_height );
+ }
+
+ if ( m_selectionStart.item && m_selectionEnd.item )
+ updateSelection( *selectionStart(), *selectionEnd() );
+
+
+}
+
+Item *TextView::itemAt( const QPoint &pos, SelectionPoint *selectionInfo,
+ Item::SelectionAccuracy accuracy )
+{
+ int px = pos.x();
+ int py = pos.y();
+
+ int y = 0;
+ int height = 0;
+ QPtrListIterator<TextParag> it( m_parags );
+ for (; it.current(); ++it )
+ {
+ height = it.current()->height();
+ if ( y <= py && py <= ( y + height ) )
+ {
+ Item *res = it.current()->itemAt( px, py - y, selectionInfo, accuracy );
+ if ( selectionInfo )
+ {
+ selectionInfo->pos.ry() += y;
+ selectionInfo->pos.rx() = px;
+ }
+ return res;
+ }
+ y += height;
+ }
+
+ if ( accuracy == Item::SelectFuzzy && selectionInfo && !m_parags.isEmpty() )
+ {
+ TextParag *parag = m_parags.getLast();
+ parag->itemAt( px, height - 1, selectionInfo, accuracy );
+
+ selectionInfo->pos.ry() += y - height;
+ selectionInfo->pos.rx() = px;
+ }
+
+ return 0;
+}
+
+QString TextParagIterator::richText() const
+{
+ if ( atEnd() )
+ return QString::null;
+
+ return Tokenizer::convertToRichText( m_paragIt.current()->processedRichText() );
+}
+
+void TextParagIterator::setRichText( const QString &richText )
+{
+ if ( atEnd() )
+ return;
+
+ m_paragIt.current()->setRichText( richText );
+
+ TextView *textView = m_paragIt.current()->textView();
+ textView->layout( false );
+
+ if ( textView->isUpdatesEnabled() )
+ textView->updateContents();
+}
+
+QString TextParagIterator::plainText() const
+{
+ if ( atEnd() )
+ return QString::null;
+
+ return m_paragIt.current()->plainText();
+}
+
+#include "kstextview.moc"
+
+/*
+ * vim: et sw=4
+ */
diff --git a/ksirc/kstextview.h b/ksirc/kstextview.h
new file mode 100644
index 00000000..7d29cbe9
--- /dev/null
+++ b/ksirc/kstextview.h
@@ -0,0 +1,577 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+#ifndef __kstextview_h__
+#define __kstextview_h__
+
+#include <qscrollview.h>
+#include <qpen.h>
+#include <qptrlist.h>
+#include <qvaluelist.h>
+#include <qmap.h>
+#include <qpixmap.h>
+
+class QTimer;
+class QDragObject;
+
+namespace KSirc
+{
+
+class TextView;
+
+struct StringPtr
+{
+ StringPtr() : ptr( 0 ), len( 0 ) {}
+ StringPtr( const QChar *_ptr, uint _len )
+ : ptr( _ptr ), len( _len ) {}
+ explicit StringPtr( const QString &s ) // use with care!
+ : ptr( s.unicode() ), len( s.length() ) {}
+
+ inline bool isNull() const { return ptr == 0; }
+
+ // makes deep copy
+ inline QString toQString() const
+ { return ( ptr && len > 0 ) ? QString( ptr, len ) : QString::null; }
+
+ const QChar *ptr;
+ uint len;
+};
+
+#define CONSTSTRING( substr ) QConstString( substr.ptr, substr.len ).string()
+
+inline bool operator<( const StringPtr &s1, const StringPtr &s2 )
+{
+ return CONSTSTRING( s1 ) < CONSTSTRING( s2 );
+}
+
+inline bool operator==( const StringPtr &s1, const StringPtr &s2 )
+{
+ return CONSTSTRING( s1 ) == CONSTSTRING( s2 );
+}
+
+inline bool operator==( const StringPtr &s1, const char *s2 )
+{
+ return CONSTSTRING( s1 ) == s2;
+}
+
+class AttributeMap : public QMap<StringPtr, StringPtr>
+{
+public:
+ AttributeMap() {}
+ AttributeMap( const AttributeMap &rhs ) : QMap<StringPtr, StringPtr>( rhs ) {}
+ AttributeMap &operator=( const AttributeMap &rhs )
+ { QMap<StringPtr, StringPtr>::operator=( rhs ); return *this; }
+
+ // helper for 'const char *' key...
+ ConstIterator findAttribute( const char *key ) const
+ {
+ QString qkey( key );
+ return find( StringPtr( qkey ) );
+ }
+ Iterator findAttribute( const char *key )
+ {
+ QString qkey( key );
+ return find( StringPtr( qkey ) );
+ }
+
+ StringPtr operator[]( const char *key ) const
+ {
+ ConstIterator it = findAttribute( key );
+ if ( it == end() )
+ return StringPtr();
+ return it.data();
+ }
+ StringPtr &operator[]( const StringPtr &key )
+ {
+ return QMap<StringPtr, StringPtr>::operator[]( key );
+ }
+};
+
+struct Token
+{
+ Token() : id( -1 ) {}
+
+ enum Id { TagOpen, Text, TagClose };
+ int id;
+ StringPtr value;
+ AttributeMap attributes;
+};
+
+struct ItemProperties
+{
+ ItemProperties();
+ ItemProperties( const QFont &defaultFont );
+ ItemProperties( const ItemProperties &other,
+ const Token &token,
+ TextView *textView );
+ ItemProperties( const ItemProperties &rhs );
+ ItemProperties &operator=( const ItemProperties &rhs );
+
+ void updateFont( const QFont &newFont );
+
+ // these three are inherited/merged
+ QFont font;
+ QColor color;
+ QColor selColor;
+ QColor bgColor;
+ QColor bgSelColor;
+ bool reversed;
+ // ### todo: inherit these, too
+ AttributeMap attributes;
+};
+
+class TextParag;
+class TextLine;
+class SelectionPoint;
+
+class Item
+{
+public:
+ enum LayoutResetStatus { DeleteItem, KeepItem };
+ enum SelectionStatus { SelectionStart = 0, InSelection, SelectionEnd, SelectionBoth,
+ NoSelection };
+ enum SelectionAccuracy { SelectExact, SelectFuzzy };
+
+ Item( TextParag *parag, const ItemProperties &props = ItemProperties() );
+
+ virtual ~Item();
+
+ virtual const char *type() { return "Item"; }
+
+ virtual void paint( QPainter &painter ) = 0;
+
+ int width() const;
+
+ int minWidth() const;
+
+ int height() const;
+
+ virtual Item *breakLine( int width );
+
+ virtual LayoutResetStatus resetLayout() = 0;
+
+ virtual int calcSelectionOffset( int x );
+
+ void setSelectionStatus( SelectionStatus status ) { m_selection = status; }
+
+ SelectionStatus selectionStatus() const { return m_selection; }
+
+ void selectionOffsets( int &startOffset, int &endOffset );
+
+ int maxSelectionOffset() const;
+
+ void setLine(TextLine *line);
+
+ // ###
+ virtual StringPtr text() const;
+
+ virtual void setProps( const ItemProperties &props );
+ ItemProperties &props() { return m_props; }
+
+ static Item *create( TextParag *parag, const Token &tok,
+ const ItemProperties &props = ItemProperties() );
+
+protected:
+ mutable bool m_extendsDirty;
+ mutable int m_minWidth;
+ mutable int m_width;
+ mutable int m_height;
+
+ virtual void calcExtends() const = 0;
+
+ SelectionStatus m_selection;
+ TextLine *m_line;
+ TextParag *m_parag;
+ ItemProperties m_props;
+};
+
+class TextChunk : public Item
+{
+public:
+ TextChunk( TextParag *parag, const StringPtr &text, const ItemProperties &props );
+
+ virtual const char *type() { return "TextChunk"; }
+
+ virtual void paint( QPainter &painter );
+
+ virtual Item *breakLine( int width );
+
+ virtual LayoutResetStatus resetLayout();
+
+ virtual int calcSelectionOffset( int x );
+
+ virtual StringPtr text() const;
+
+ virtual void setProps( const ItemProperties &props );
+
+protected:
+ virtual void calcExtends() const;
+
+private:
+ StringPtr breakInTheMiddle( int width );
+ Item *hardBreak( const StringPtr &rightHandSide );
+
+ void paintSelection( QPainter &p );
+ int paintSelection( QPainter &p, int x, const StringPtr &text );
+ int paintText( QPainter &p, int x, const StringPtr &text );
+
+ void mergeSelection( TextChunk *child, SelectionPoint *selection );
+
+ StringPtr m_text;
+ uint m_originalTextLength;
+ QFontMetrics m_metrics;
+ class TextChunk *m_parent;
+};
+
+class ImageItem : public Item
+{
+public:
+ ImageItem( TextParag *parag, const QPixmap &pixmap );
+
+ virtual const char *type() { return "Image"; }
+
+ virtual void paint( QPainter &painter );
+
+ virtual LayoutResetStatus resetLayout();
+
+protected:
+ virtual void calcExtends() const;
+
+private:
+ QPixmap m_pixmap;
+};
+
+class Tokenizer
+{
+public:
+ struct TagIndex
+ {
+ enum Type { Open, Close };
+ TagIndex() : index( 0 ), type( -1 ) {}
+ TagIndex( int _index, int _type )
+ : index( _index ), type( _type ) {}
+ uint index;
+ int type;
+ };
+ typedef QValueList<TagIndex> TagIndexList;
+
+ // preprocessed string
+ struct PString
+ {
+ QString data;
+ TagIndexList tags;
+ };
+
+ Tokenizer( PString &text );
+
+ static PString preprocess( const QString &richText );
+
+ static QString convertToRichText( const PString &ptext );
+
+ bool parseNextToken( Token &tok );
+
+private:
+ void parseTag( const StringPtr &text,
+ StringPtr &tag,
+ AttributeMap &attributes );
+
+ static TagIndexList scanTagIndices( const QString &text );
+ static void resolveEntities( QString &text, TagIndexList &tags );
+
+ enum TagParsingState { ScanForName, ScanForEqual, ScanForValue };
+
+ QString &m_text;
+ TagIndexList m_tags;
+ TagIndexList::ConstIterator m_lastTag;
+ bool m_textBeforeFirstTagProcessed;
+ bool m_done;
+
+ Tokenizer( const Tokenizer & );
+ Tokenizer &operator=( const Tokenizer & );
+};
+
+class SelectionPoint;
+
+class TextLine
+{
+public:
+ enum LayoutPolicy { NoUpdate, UpdateMaxHeight };
+
+ TextLine();
+ // tranfers ownership of items! make sure that 'items' does not
+ // have autodeletion enabled!
+ TextLine( const QPtrList<Item> &items );
+
+ int maxHeight() const { return m_maxHeight; }
+
+ QString updateSelection( const SelectionPoint &start, const SelectionPoint &end );
+ void clearSelection();
+
+ // transfers ownership
+ void appendItem( Item *i, int layoutUpdatePolicy = NoUpdate );
+
+ bool isEmpty() const { return m_items.isEmpty(); }
+
+ Item *resetLayout( QPtrList<Item> &remainingItems);
+
+ void paint( QPainter &p, int y );
+
+ Item *itemAt( int px, SelectionPoint *selectionInfo,
+ Item::SelectionAccuracy accuracy = Item::SelectExact );
+
+ QPtrListIterator<Item> iterator() const { return QPtrListIterator<Item>( m_items ); }
+
+ QString plainText() const;
+
+ void fontChange( const QFont &newFont );
+
+private:
+ QPtrList<Item> m_items;
+ int m_maxHeight;
+};
+
+class SelectionPoint;
+
+class TextParag
+{
+public:
+ TextParag( TextView *textView, const QString &richText );
+
+ ~TextParag();
+
+ void layout( int width );
+
+ void paint( QPainter &p, int y, int maxY );
+
+ inline void setLayouted( bool l ) { m_layouted = l; }
+ inline bool isLayouted() const { return m_layouted; }
+
+ inline int minWidth() const { return m_minWidth; }
+ inline int height() const { return m_height; }
+
+ Item *itemAt( int px, int py, SelectionPoint *selectionInfo,
+ Item::SelectionAccuracy accuracy = Item::SelectExact );
+
+ TextView *textView() const { return m_textView; }
+
+ QString updateSelection( const SelectionPoint &start, const SelectionPoint &end );
+
+ void clearSelection();
+
+ void setRichText( const QString &richText );
+
+ Tokenizer::PString processedRichText() const { return m_processedRichText; }
+
+ QString plainText() const;
+
+ void fontChange( const QFont &newFont );
+
+private:
+ Tokenizer::PString m_processedRichText;
+ QPtrList<TextLine> m_lines;
+ bool m_layouted;
+ int m_height;
+ int m_minWidth;
+ TextView *m_textView;
+
+ struct Tag
+ {
+ Tag() {}
+ Tag( const StringPtr &_name, const ItemProperties &_props )
+ : name( _name ), props( _props ) {}
+
+ StringPtr name;
+ ItemProperties props;
+ };
+
+ TextParag( const TextParag & );
+ TextParag &operator=( const TextParag & );
+};
+
+struct SelectionPoint
+{
+ SelectionPoint() : item( 0 ), line( 0 ), parag( 0 ), offset( 0 ) {}
+ Item *item;
+ TextLine *line;
+ TextParag *parag;
+ uint offset;
+ QPoint pos;
+};
+
+class TextParagIterator
+{
+ friend class TextView;
+public:
+ TextParagIterator( const TextParagIterator &rhs )
+ : m_paragIt( rhs.m_paragIt ) {}
+ TextParagIterator &operator=( const TextParagIterator &rhs )
+ { m_paragIt = rhs.m_paragIt; return *this; }
+
+ QString richText() const;
+ void setRichText( const QString &richText );
+
+ QString plainText() const;
+
+ bool atEnd() const { return m_paragIt.current() == 0; }
+
+ TextParagIterator &operator++() { ++m_paragIt; return *this; }
+ TextParagIterator &operator++( int steps ) { m_paragIt += steps; return *this; }
+ TextParagIterator &operator--() { --m_paragIt; return *this; }
+ TextParagIterator &operator--( int steps ) { m_paragIt -= steps; return *this; }
+
+protected:
+ TextParagIterator( const QPtrListIterator<TextParag> &paragIt )
+ : m_paragIt( paragIt ) {}
+
+private:
+ QPtrListIterator<TextParag> m_paragIt;
+};
+
+class ContentsPaintAlgorithm
+{
+public:
+ ContentsPaintAlgorithm( const QPtrListIterator<TextParag> &paragIt,
+ QWidget *viewport, QPixmap &paintBuffer,
+ QPainter &painter, int clipX, int clipY, int clipHeight );
+
+ void paint();
+
+private:
+ int goToFirstVisibleParagraph();
+ int paint( QPainter &bufferedPainter, int currentY );
+ int adjustYAndIterator( int startY, int currentY, int nextY );
+
+ QPtrListIterator<TextParag> m_paragIt;
+ QWidget *m_viewport;
+ QPixmap &m_paintBuffer;
+ QPainter &m_painter;
+ int m_clipX, m_clipY, m_clipHeight;
+ int m_overshoot;
+};
+
+class TextView : public QScrollView
+{
+ Q_OBJECT
+ friend class Item;
+ friend class TextChunk;
+ friend class TextParag;
+ friend class TextParagIterator;
+public:
+ TextView( QWidget *parent, const char *name = 0 );
+ virtual ~TextView();
+
+ virtual void clear();
+
+ TextParagIterator appendParag( const QString &richText );
+
+ bool removeParag( const TextParagIterator &parag );
+
+ void clearSelection( bool repaint = false ); // ### re-consider the repaint arg...
+
+ QString selectedText() const { return m_selectedText; }
+
+ TextParagIterator firstParag() const;
+
+ QString plainText() const;
+
+ QColor linkColor() const;
+ void setLinkColor( const QColor &linkColor );
+
+ void scrollToBottom( bool force = false );
+
+signals:
+ void selectionChanged();
+ void pasteReq(const QString&);
+ void linkClicked( const QMouseEvent *ev, const QString &url );
+
+public slots:
+ void copy();
+
+protected slots:
+ void scrolling(int value);
+
+protected:
+ virtual void viewportResizeEvent( QResizeEvent *ev );
+ virtual void drawContents( QPainter *p, int cx, int cy, int cw, int ch );
+ virtual void contentsMousePressEvent( QMouseEvent *ev );
+ virtual void contentsMouseMoveEvent( QMouseEvent *ev );
+ virtual void contentsMouseReleaseEvent( QMouseEvent *ev );
+ virtual void fontChange( const QFont & );
+
+ virtual void startDrag();
+
+ virtual QDragObject *dragObject( const QString &dragURL );
+
+private slots:
+ void autoScroll();
+
+private:
+ void emitLinkClickedForMouseEvent( QMouseEvent *ev );
+
+ void startAutoScroll();
+
+ void stopAutoScroll();
+
+ void selectionOffsets( int &startOffset, int &endOffset );
+
+ void updateSelectionOrder();
+
+ QString updateSelection( const SelectionPoint &start, const SelectionPoint &end );
+
+ SelectionPoint *selectionStart();
+ SelectionPoint *selectionEnd();
+
+ void layout( bool force = true );
+
+ Item *itemAt( const QPoint &pos, SelectionPoint *selectionInfo = 0,
+ Item::SelectionAccuracy accuracy = Item::SelectExact );
+
+ void clearSelectionInternal();
+
+ void contentsChange(int heightChange, bool force = false);
+
+ QPtrList<TextParag> m_parags;
+ QPixmap m_paintBuffer;
+
+ SelectionPoint m_selectionMaybeStart;
+ SelectionPoint m_selectionStart;
+ SelectionPoint m_selectionEnd;
+ bool m_selectionEndBeforeStart;
+
+ QTimer *m_autoScrollTimer;
+
+ QString m_selectedText;
+
+ QPoint m_dragStartPos;
+ QString m_dragURL;
+ bool m_mousePressed : 1;
+ bool m_mmbPressed : 1;
+ QColor m_linkColor;
+ QColor m_selectionBackgroundColor;
+
+ int m_height;
+ bool m_inScroll;
+ int m_lastScroll;
+};
+
+} // namespace KSirc
+
+#endif
+/*
+ * vim: et sw=4
+ */
diff --git a/ksirc/kstextviewtest.cpp b/ksirc/kstextviewtest.cpp
new file mode 100644
index 00000000..f3326351
--- /dev/null
+++ b/ksirc/kstextviewtest.cpp
@@ -0,0 +1,51 @@
+#include <kapplication.h>
+
+#include "kstextview.h"
+
+
+class Timer : public QObject
+{
+public:
+ Timer( KSirc::TextView *view )
+ : m_view( view ), m_lines( 0 )
+ {
+ //startTimer( 5000 );
+ }
+ void timerEvent( QTimerEvent * )
+ {
+ m_lines++;
+ if ( m_lines > 15 )
+ m_view->removeParag( m_view->firstParag() );
+ m_view->appendParag( QString::fromLatin1( "<font color=#0000ff>color test</font><font color=#00ff00>color test</font><font color=#ff0000>color test</font><font color=#000088>color test</font><font color=#008800>color test</font><font color=#880000>color test</font><font color=#0000ff>color test</font><font color=#02ff00>color test</font><font color=#2000ff>color test</font>" ) );
+ m_view->appendParag( QString::fromLatin1( "<r>aaaaaa</r> bbbbbb cccccccc ddddddddd eeeeeee " ) );
+ m_view->setContentsPos( 0, m_view->contentsHeight() );
+ }
+private:
+ KSirc::TextView *m_view;
+ int m_lines;
+};
+
+int main( int argc, char **argv )
+{
+ KApplication app( argc, argv, "kstextviewtest" );
+
+ KSirc::TextView view( 0 );
+ app.setMainWidget( &view );
+
+ view.show();
+ view.appendParag( QString::fromLatin1( "<b>bold</b>Normal<i>Italic hey!</i><r>reverse!</r>" ) );
+ view.appendParag( QString::fromLatin1( "bleh foo hey bar schubidu bliep blop" ) );
+ view.appendParag( QString::fromLatin1( "more longish paragraphs .. yaddayadda yadda" ) );
+ view.appendParag( QString::fromLatin1( "&lt;tronical&gt; hah! &amp;" ) );
+ view.appendParag( QString::fromLatin1( "<a href=\"http://www.kde.org/\">url</a>" ) );
+ view.appendParag( QString::fromLatin1( "<font color=#0000ff bgcolor=#ff0000>color test</font>" ) );
+ view.appendParag( QString::fromLatin1( "<font color=\"#00ff00\">another color test</font>" ) );
+ view.appendParag( QString::fromLatin1( "<font color=\"#00ff00\" bgcolor=\"#ff0000\">another color test</font>" ) );
+ view.appendParag( QString::fromLatin1( "<i bleh>simple attr test</i><b leh foo=15 doo=\"&amp; > 123 \" oof>complicated attr test</b>" ) );
+ view.appendParag( QString::fromLatin1( "\"<b doo=\"&amp; > 123 \" oof>\"third attr test 2</b>\"" ) );
+ view.appendParag( QString::fromLatin1( "aaaaaa bbbbbb cccccccc ddddddddd eeeeeee" ) );
+ Timer t( &view );
+
+ return app.exec();
+}
+
diff --git a/ksirc/ksview.cpp b/ksirc/ksview.cpp
new file mode 100644
index 00000000..76ec57d8
--- /dev/null
+++ b/ksirc/ksview.cpp
@@ -0,0 +1,344 @@
+/* This file is part of ksirc
+ Copyright (c) 2001 Malte Starostik <malte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// $Id$
+
+#include <qclipboard.h>
+#include <qdatetime.h>
+#include <qregexp.h>
+#include <qdragobject.h>
+#include <qvaluestack.h>
+#include <qstylesheet.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <krun.h>
+#include <kpopupmenu.h>
+#include <kstringhandler.h>
+#include <knotifyclient.h>
+#include <kfiledialog.h>
+#include <kio/job.h>
+#include <kurldrag.h>
+
+#include "ksopts.h"
+#include "ksview.moc"
+#include "ksparser.h"
+#include "nickColourMaker.h"
+#include "ksircprocess.h"
+
+KSircView::KSircView(KSircProcess *proc, QWidget *parent, const char *name)
+ : KSirc::TextView(parent, name), m_proc(proc)
+{
+ m_acceptFiles = false;
+ m_timestamps = false;
+ viewport()->setAcceptDrops(true);
+ clear();
+ connect( this, SIGNAL( linkClicked( const QMouseEvent *, const QString & ) ),
+ this, SLOT( anchorClicked( const QMouseEvent *, const QString & ) ) );
+
+ QPixmap background = ksopts->backgroundPixmap();
+ if ( !background.isNull() )
+ viewport()->setBackgroundPixmap( background );
+
+ setLinkColor( ksopts->linkColor );
+}
+
+KSircView::~KSircView()
+{
+}
+
+void KSircView::clear()
+{
+ m_lines = 0;
+ m_timeStamps.clear();
+ KSirc::TextView::clear();
+}
+
+QString KSircView::makeTimeStamp()
+{
+ QTime now = QTime::currentTime();
+ QString timeStamp = QString::fromLatin1( "[%1:%2:%3] " )
+ .arg( QString::number( now.hour() ).rightJustify( 2, '0' ) )
+ .arg( QString::number( now.minute() ).rightJustify( 2, '0' ) )
+ .arg( QString::number( now.second() ).rightJustify( 2, '0' ) );
+ return timeStamp;
+}
+
+void KSircView::saveURL( const QString &url )
+{
+ KURL kurl( url );
+
+ KFileDialog *dlg = new KFileDialog( QString::null, QString::null /*filter*/, this /*parent*/, "filedialog" /*name*/, true /*modal*/ );
+
+ dlg->setKeepLocation( true );
+
+ dlg->setCaption( i18n( "Save As" ) );
+
+ if ( !kurl.fileName().isEmpty() )
+ dlg->setSelection( kurl.fileName() );
+
+ if ( dlg->exec() ) {
+ KURL destURL( dlg->selectedURL() );
+ if ( destURL.isValid() ) {
+ KIO::Job *job = KIO::copy( kurl, destURL );
+ job->setAutoErrorHandlingEnabled( true );
+ }
+ }
+
+ delete dlg;
+}
+
+QString KSircView::addLine(const QString &pixmap, const QColor &color, const QString &_text)
+{
+ //kdDebug(5008) << "Start Text: " << _text << endl;
+
+ QString richText( "<font color=\"%1\">" );
+ richText = richText.arg( color.name() );
+
+ if ( !pixmap.isEmpty() )
+ richText.prepend( QString::fromLatin1( "<img src=\"%1\"></img>" ).arg( pixmap ) );
+
+ QString timeStamp = QString::fromLatin1( "<font color=\"%1\">%2</font>" )
+ .arg( ksopts->textColor.name() )
+ .arg( makeTimeStamp() );
+ m_timeStamps.append(timeStamp);
+ if ( m_timestamps )
+ richText.prepend( timeStamp );
+
+ QString text = QStyleSheet::escape(_text);
+
+ // ### a bit of a hack: turn '&lt;nick&gt; message' into
+ // <span>&lt;nick&gt;<span> message' . span itself isn't supported but it
+ // enforces the creation of separate item objects and hence separate
+ // drawing of '<nick>' and 'message' , which is needed for BiDi users,
+ // according to UV Kochavi <uv1st@yahoo.com> , to prevent output like
+ // '<nick message<' , which is supposedly a bug in Qt's reordering. The
+ // same is done for [nick] and >nick< to catch queries.
+ QRegExp bidiRe( "^(&lt;\\S+&gt;)(.+)$" );
+ text.replace( bidiRe, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+ QRegExp bidiRe2( "^(\\[\\S+\\])(.+)$" );
+ text.replace( bidiRe2, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+ QRegExp bidiRe3( "^(&gt;\\S+&lt;)(.+)$" );
+ text.replace( bidiRe3, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+
+ QRegExp nickCol( "~n(.+)~n" );
+ nickCol.setMinimal(true);
+ int pos;
+
+ while( (pos = nickCol.search(text)) >= 0 ){
+ //kdDebug(5008) << "Found nick: " << nickCol.cap(1) << endl;
+ QString newText = nickCol.cap(1);
+ if( nickCol.cap(1) != m_proc->getNick()){
+ QColor col = nickColourMaker::colourMaker()->findFg(nickCol.cap(1));
+
+ newText.prepend(QString("<font color=\"%1\">").arg(col.name()));
+ newText.append("</font>");
+ } else {
+ QColor col = ksopts->ownNickColor.name();
+ if( ! col.isValid())
+ nickColourMaker::colourMaker()->findFg(nickCol.cap(1));
+ newText.prepend(QString("<font color=\"%1\">").arg(col.name()));
+ newText.append("</font>");
+ if(ksopts->ownNickBold){
+ newText.prepend("<b>");
+ newText.append("</b>");
+ }
+ if(ksopts->ownNickUl){
+ newText.prepend("<u>");
+ newText.append("</u>");
+ }
+ if(ksopts->ownNickRev){
+ newText.prepend("<r>");
+ newText.append("</r>");
+ }
+ }
+ text.replace(pos, nickCol.matchedLength(), newText);
+ }
+
+ //kdDebug(5008) << "After Text: " << text << endl;
+
+ KSParser parser;
+ richText += parser.parse( text );
+
+ richText += "</font>";
+
+
+ //kdDebug(5008) << "Text: " << _text << endl;
+
+
+ richText = KStringHandler::tagURLs( richText );
+ //kdDebug(5008) << "Rich text: " << richText << endl;
+
+ KSirc::TextParagIterator parag = appendParag( richText );
+
+ m_lines++;
+ if ( ksopts->windowLength && m_lines > ksopts->windowLength )
+ {
+ while ( m_lines > ksopts->windowLength )
+ {
+ removeParag( firstParag() );
+ m_timeStamps.remove( m_timeStamps.begin() );
+ m_lines--;
+ }
+ }
+
+ if (parser.beeped()) {
+ KNotifyClient::event(winId(), QString::fromLatin1("BeepReceived"),
+ i18n("Beep Received"));
+ }
+
+ QString logText = parag.plainText();
+ // append timestamp if it's not already there
+ if ( ! m_timestamps )
+ logText.prepend( makeTimeStamp() );
+
+ return logText + '\n';
+}
+
+void KSircView::addRichText(const QString &_text)
+{
+ //kdDebug(5008) << "Start Text: " << _text << endl;
+
+ QString text = _text;
+
+ QRegExp re("^(<font color=\"[^\"]+\">\\[[0-9:]+\\] </font>)");
+ QString timeStamp;
+
+ if(re.search(text) >= 0){
+ timeStamp = re.cap(1);
+ }
+ else {
+ timeStamp = QString::fromLatin1( "<font color=\"%1\">%2</font>" )
+ .arg( ksopts->textColor.name() )
+ .arg( makeTimeStamp() );
+ if ( m_timestamps )
+ text.prepend( timeStamp );
+ }
+ m_timeStamps.append(timeStamp);
+
+ KSirc::TextParagIterator parag = appendParag( text );
+
+ m_lines++;
+ if ( ksopts->windowLength && m_lines > ksopts->windowLength )
+ {
+ while ( m_lines > ksopts->windowLength )
+ {
+ removeParag( firstParag() );
+ m_timeStamps.remove( m_timeStamps.begin() );
+ m_lines--;
+ }
+ }
+
+}
+
+void KSircView::enableTimeStamps(bool enable)
+{
+ if(enable == m_timestamps)
+ return;
+ setUpdatesEnabled( false );
+ m_timestamps = enable;
+ KSirc::TextParagIterator paragIt = firstParag();
+ QStringList::ConstIterator timeStampIt = m_timeStamps.begin();
+ for (; !paragIt.atEnd(); ++paragIt, ++timeStampIt )
+ {
+ QString text = paragIt.richText();
+ if ( enable )
+ text.prepend( *timeStampIt );
+ else
+ text.remove( 0, (*timeStampIt).length() );
+ paragIt.setRichText( text );
+ }
+ setUpdatesEnabled( true );
+ updateContents();
+}
+
+void KSircView::anchorClicked(const QMouseEvent *ev, const QString &url)
+{
+ if ( (ev->button() & LeftButton) && (ev->state() & ShiftButton ) )
+ saveURL( url );
+ else if ( (ev->button() & LeftButton) || (ev->button() & MidButton) )
+ {
+ openBrowser( url );
+ }
+ else if ( ev->button() & RightButton )
+ {
+ static const int openURLID = 0;
+ static const int copyLinkLocationID = 1;
+
+ // Adding a nice contextmenu
+ KPopupMenu* menu = new KPopupMenu( this );
+ menu->insertTitle( i18n( "URL" ) );
+ menu->insertItem( i18n("Open URL"), openURLID );
+ menu->insertItem( i18n("Copy Link Address"), copyLinkLocationID );
+ switch( menu->exec( ( ev->globalPos() ) ) )
+ {
+ case openURLID :
+ openBrowser( url );
+ break;
+ case copyLinkLocationID :
+ copyLinkToClipboard( url );
+ break;
+ default:
+ break;
+ }
+ delete menu;
+ }
+}
+
+void KSircView::openBrowser(const QString &url )
+{
+ (void) new KRun( KURL( url.startsWith("www") ? QString::fromLatin1("http://") + url : url));
+}
+
+void KSircView::copyLinkToClipboard( const QString &url )
+{
+ QApplication::clipboard()->setText( url, QClipboard::Clipboard );
+}
+
+QColor KSircView::ircColor(int code)
+{
+ if (code >= 0 && code < 16)
+ return ksopts->ircColors[code];
+ return QColor();
+}
+
+void KSircView::contentsDragEnterEvent(QDragEnterEvent* event)
+{
+ event->accept((QTextDrag::canDecode(event) ||
+ (m_acceptFiles && KURLDrag::canDecode(event))) &&
+ (!event->source() || event->source() != viewport()));
+}
+
+void KSircView::contentsDragMoveEvent(QDragMoveEvent* event)
+{
+ event->accept(!event->source() || event->source() != viewport());
+}
+
+void KSircView::contentsDropEvent(QDropEvent* event)
+{
+ QStringList urls;
+ QString text;
+
+ if (m_acceptFiles && KURLDrag::decodeLocalFiles(event, urls))
+ emit urlsDropped(urls);
+ else if (QTextDrag::decode(event, text))
+ emit textDropped(text);
+}
+
+// vim: ts=4 sw=4 noet
diff --git a/ksirc/ksview.h b/ksirc/ksview.h
new file mode 100644
index 00000000..b1f3766a
--- /dev/null
+++ b/ksirc/ksview.h
@@ -0,0 +1,74 @@
+#ifndef ksview_h
+#define ksview_h
+
+/* This file is part of ksirc
+ Copyright (c) 2001 Malte Starostik <malte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// $Id$
+//
+#include "kstextview.h"
+
+class KSircProcess;
+
+class KSircView : public KSirc::TextView
+{
+ Q_OBJECT
+public:
+ KSircView(KSircProcess *proc, QWidget *parent, const char *name);
+ virtual ~KSircView();
+
+ virtual void clear();
+ // returns the plain-text string added (for logging)
+ QString addLine(const QString &pixmap, const QColor &color, const QString &text);
+ void addRichText(const QString &_text);
+ void enableTimeStamps(bool enable);
+
+ void setAcceptFiles(bool _acceptFiles) { m_acceptFiles = _acceptFiles; }
+signals:
+ void urlsDropped(const QStringList&);
+ void textDropped(const QString&);
+
+protected:
+ virtual void contentsDragEnterEvent(QDragEnterEvent *);
+ virtual void contentsDragMoveEvent(QDragMoveEvent *);
+ virtual void contentsDropEvent(QDropEvent *);
+
+private:
+ virtual bool linksEnabled() const { return true; }
+ void openBrowser( const QString &url );
+ void copyLinkToClipboard( const QString &url );
+ static QString makeTimeStamp();
+
+ void saveURL( const QString &url );
+
+private slots:
+ void anchorClicked(const QMouseEvent *ev, const QString &url);
+
+ QColor ircColor(int);
+
+private:
+ int m_lines;
+ QStringList m_timeStamps;
+ bool m_acceptFiles;
+ bool m_timestamps;
+ KSircProcess *m_proc;
+};
+
+// vim: ts=4 sw=4 noet
+
+#endif
diff --git a/ksirc/logfile.cpp b/ksirc/logfile.cpp
new file mode 100644
index 00000000..6a15e51e
--- /dev/null
+++ b/ksirc/logfile.cpp
@@ -0,0 +1,93 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+
+#include "logfile.h"
+
+#include <assert.h>
+
+#include <qdir.h>
+
+#include <kstandarddirs.h>
+
+LogFile::LogFile( const QString &channel, const QString &server )
+ : m_channel( channel ), m_server( server ), m_file( new QFile() ),
+ m_flushTimerId( -1 )
+{
+}
+
+LogFile::~LogFile()
+{
+ closeLog();
+ delete m_file;
+}
+
+void LogFile::open()
+{
+ int suffix = 1;
+
+ m_file->setName( makeLogFileName( m_channel, m_server ) );
+
+ while ( !m_file->open( IO_WriteOnly | IO_Append ) && suffix < 16000 ) // arbitrary limit ;)
+ {
+ m_file->setName( makeLogFileName( m_channel, m_server, suffix ) );
+ suffix++;
+ }
+
+ assert( m_file->isOpen() == true );
+
+ log( QString::fromLatin1( "### Log session started at " )
+ + QDateTime::currentDateTime().toString()
+ + QString::fromLatin1( "###\n" ) );
+}
+
+void LogFile::closeLog()
+{
+ log( QString::fromLatin1( "### Log session terminated at " )
+ + QDateTime::currentDateTime().toString()
+ + QString::fromLatin1( "###\n" ) );
+
+ if ( m_flushTimerId != -1 )
+ killTimer( m_flushTimerId );
+
+ m_file->close();
+}
+
+void LogFile::log( const QString &message )
+{
+ m_file->writeBlock( message.local8Bit(), message.length() );
+
+ if ( m_flushTimerId == -1 )
+ m_flushTimerId = startTimer( 60000 ); // flush each minute
+}
+
+void LogFile::timerEvent( QTimerEvent * )
+{
+ if ( m_file )
+ m_file->flush();
+
+ killTimer( m_flushTimerId );
+ m_flushTimerId = -1;
+}
+
+QString LogFile::makeLogFileName( const QString &channel, const QString &server, int suffix )
+{
+ QString res = channel + '_';
+
+ QDate dt = QDate::currentDate();
+ QString dateStr;
+ dateStr.sprintf( "%.4d_%.2d_%.2d_", dt.year(), dt.month(), dt.day() );
+ res += dateStr;
+
+ res += server;
+
+ res += ".log";
+
+ if ( suffix > -1 )
+ res += '.' + QString::number( suffix );
+
+ return locateLocal( "appdata", "logs/" + res );
+}
diff --git a/ksirc/logfile.h b/ksirc/logfile.h
new file mode 100644
index 00000000..337b9912
--- /dev/null
+++ b/ksirc/logfile.h
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+#ifndef __logfile_h__
+#define __logfile_h__
+
+#include <qobject.h>
+
+class QFile;
+
+class LogFile : public QObject
+{
+public:
+ LogFile( const QString &channel, const QString &server );
+ virtual ~LogFile();
+
+ void open();
+
+ void closeLog();
+
+ void log( const QString &message );
+
+protected:
+ virtual void timerEvent( QTimerEvent * );
+
+private:
+ QString m_channel;
+ QString m_server;
+
+ QFile *m_file;
+
+ int m_flushTimerId;
+
+ QString makeLogFileName( const QString &channel, const QString &server, int suffix = -1 );
+};
+
+#endif
diff --git a/ksirc/mditoplevel.cpp b/ksirc/mditoplevel.cpp
new file mode 100644
index 00000000..3f35d464
--- /dev/null
+++ b/ksirc/mditoplevel.cpp
@@ -0,0 +1,260 @@
+
+#include <qguardedptr.h>
+
+#include <qtabbar.h>
+
+#include <kapplication.h>
+#include <kiconloader.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+#include <kaction.h>
+
+#include "mditoplevel.h"
+#include "toplevel.h"
+#include "servercontroller.h"
+
+#define ID_CLOSE 5
+
+void KSTabWidget::mousePressEvent(QMouseEvent *e)
+{
+ if(e->button() == RightButton){
+ QPoint p = tabBar()->mapFromParent(e->pos());
+ QTab *tab = tabBar()->selectTab(p);
+ if(tab){
+ int id = tab->identifier();
+ emit showContexMenu(page(id), tabBar()->mapToGlobal(p));
+ }
+ }
+}
+
+MDITopLevel::MDITopLevel(QWidget *parent, const char *name)
+ : KMainWindow(parent, name)
+{
+ m_closing = false;
+
+ m_tab = new KSTabWidget( this );
+ m_tab->setTabPosition( QTabWidget::Bottom );
+
+ setCentralWidget( m_tab );
+
+ connect( m_tab, SIGNAL( currentChanged( QWidget * ) ),
+ this, SLOT( slotCurrentChanged( QWidget * ) ) );
+
+ connect( m_tab, SIGNAL( showContexMenu(QWidget *, const QPoint &) ),
+ this, SLOT( slotShowContexMenu(QWidget *, const QPoint &) ) );
+
+ KConfig *config = kapp->config();
+ config->setGroup("MDI");
+ QSize s( 600, 360 );
+ resize(config->readSizeEntry( "TopLevelSize", &s ));
+
+ m_dirtyIcon = UserIcon( "star" );
+ m_addressedIcon = UserIcon( "info" );
+
+ m_pop = new KPopupMenu(m_tab, "");
+ m_pop->insertItem( SmallIcon("fileclose"), i18n("Close"), this, SLOT( slotCloseLastWid() ));
+
+}
+
+MDITopLevel::~MDITopLevel()
+{
+ kdDebug(5008) << "~MDITopLevel in" << endl;
+
+ KConfig *config = kapp->config();
+ config->setGroup( "MDI" );
+ config->writeEntry( "TopLevelSize", this->size() );
+ config->sync();
+
+ QPtrListIterator<QWidget> it( m_tabWidgets );
+ for (; it.current(); ++it )
+ it.current()->disconnect( this, 0 );
+ kdDebug(5008) << "~MDITopLevel out" << endl;
+
+}
+
+void MDITopLevel::addWidget( QWidget *widget, bool show )
+{
+ if ( m_tabWidgets.containsRef( widget ) )
+ return;
+
+ kdDebug(5008) << "In add widget" << endl;
+
+ widget->reparent( m_tab, 0, QPoint( 0, 0 ), show );
+
+ if(show == TRUE){
+ showWidget(widget);
+ }
+
+ m_tabWidgets.append( widget );
+
+ connect( widget, SIGNAL( destroyed() ) ,
+ this, SLOT( slotWidgetDestroyed() ) );
+
+ connect( widget, SIGNAL( changeChannel( const QString &, const QString & ) ),
+ this, SLOT( slotChangeChannelName( const QString &, const QString & ) ) );
+
+ widget->installEventFilter( this );
+
+ connect( widget, SIGNAL( changed( bool, QString ) ),
+ this, SLOT( slotMarkPageDirty( bool ) ) );
+}
+
+void MDITopLevel::removeWidget( QWidget *widget )
+{
+ if (m_closing)
+ return;
+ m_tabWidgets.removeRef( widget );
+ removeFromAddressedList( widget );
+ m_tab->removePage( widget );
+ widget->removeEventFilter( this );
+ widget->disconnect( this, 0 );
+}
+
+void MDITopLevel::hideWidget( QWidget *widget )
+{
+ m_tab->removePage( widget );
+ widget->hide();
+}
+
+void MDITopLevel::showWidget( QWidget *widget )
+{
+ if(m_tab->indexOf(widget) == -1){
+ int space = widget->caption().find(" ");
+ QString cap = space < 1 ? widget->caption():widget->caption().left(space);
+ m_tab->addTab( widget, cap);
+ m_tab->showPage( widget );
+ m_tab->setCurrentPage( m_tab->indexOf(widget) );
+ }
+}
+
+void MDITopLevel::next()
+{
+ if (m_tab->currentPageIndex() < m_tab->count() - 1)
+ m_tab->setCurrentPage(m_tab->currentPageIndex() + 1);
+ else
+ m_tab->setCurrentPage(0);
+}
+
+void MDITopLevel::previous()
+{
+ if (m_tab->currentPageIndex() > 0)
+ m_tab->setCurrentPage(m_tab->currentPageIndex() - 1);
+ else
+ m_tab->setCurrentPage(m_tab->count() - 1);
+}
+
+void MDITopLevel::closeEvent( QCloseEvent *ev )
+{
+ m_closing = true;
+ // Don't use iterators on a list while deleting elements
+ // from it (Antonio)
+ int i = 0;
+ kdDebug(5008) << "Mdi got close event " << endl;
+ while ( m_tabWidgets.count() && (i++ < 100000)) {
+ kdDebug(5008) << "MDITopLevel trying to close: " << m_tabWidgets.first()->name() << endl;
+ QGuardedPtr<QWidget> w = m_tabWidgets.take(0);
+ w->show();
+ w->close( false );
+ if(w)
+ delete (QWidget *)w;
+ }
+
+ KMainWindow::closeEvent( ev );
+ m_closing = false;
+}
+
+void MDITopLevel::slotWidgetDestroyed()
+{
+ const QWidget *widget = static_cast<const QWidget *>( sender() );
+
+ m_tabWidgets.removeRef( widget );
+ removeFromAddressedList( widget );
+}
+
+bool MDITopLevel::eventFilter( QObject *obj, QEvent *ev )
+{
+ if ( ev->type() != QEvent::CaptionChange )
+ return false;
+
+ QWidget *widget = dynamic_cast<QWidget *>( obj );
+
+ if ( !widget || !m_tabWidgets.containsRef( widget ) )
+ return false;
+
+ if ( m_tab->currentPage() == widget )
+ setPlainCaption( widget->caption() );
+
+ return false;
+}
+
+void MDITopLevel::slotCurrentChanged( QWidget *page )
+{
+
+ m_tab->setTabIconSet( page, QIconSet() );
+ removeFromAddressedList( page );
+
+ setPlainCaption( page->QWidget::caption() );
+
+ KSircTopLevel *kst = dynamic_cast<KSircTopLevel *>( page );
+ if ( !kst )
+ return;
+ kst->lineEdit()->setFocus();
+}
+
+void MDITopLevel::slotMarkPageDirty( bool addressed )
+{
+ // This is called when a line appeared in this channel.
+ // addressed is true if it was addressed to the user
+ KMainWindow *window = dynamic_cast<KMainWindow *>( const_cast<QObject *>( sender() ) );
+
+ if ( !window )
+ return;
+
+ if ( window != m_tab->currentPage() )
+ {
+ if ( m_addressed.containsRef( window ) )
+ addressed = true;
+ else if ( addressed ) {
+ m_addressed.append( window );
+ }
+ m_tab->setTabIconSet( window, addressed ? m_addressedIcon : m_dirtyIcon );
+ }
+}
+
+void MDITopLevel::slotChangeChannelName( const QString &, const QString &channelName )
+{
+ KMainWindow *window = dynamic_cast<KMainWindow *>( const_cast<QObject *>( sender() ) );
+
+ if ( !window )
+ return;
+
+ QString esc = channelName;
+ esc.replace("&", "&&");
+ m_tab->setTabLabel( window, esc );
+ removeFromAddressedList( window );
+}
+
+void MDITopLevel::removeFromAddressedList( const QWidget* w )
+{
+ // If this tab was showing a "you're being talked to" icon, remove it
+ // and tell the servercontroller (so that it can update the docked icon).
+ m_addressed.removeRef( w );
+}
+
+void MDITopLevel::slotShowContexMenu(QWidget *w, const QPoint &p)
+{
+ m_last_pop_wid = w;
+ m_pop->popup(p);
+}
+
+void MDITopLevel::slotCloseLastWid()
+{
+ m_last_pop_wid->close();
+
+}
+
+#include "mditoplevel.moc"
+
+// vim: ts=4 sw=4 et
diff --git a/ksirc/mditoplevel.h b/ksirc/mditoplevel.h
new file mode 100644
index 00000000..7f58619c
--- /dev/null
+++ b/ksirc/mditoplevel.h
@@ -0,0 +1,75 @@
+#ifndef __mditoplevel_h__
+#define __mditoplevel_h__
+
+#include <qptrlist.h>
+#include <qtabwidget.h>
+
+#include <kmainwindow.h>
+
+class KSTabWidget : public QTabWidget
+{
+ Q_OBJECT
+public:
+ KSTabWidget ( QWidget * parent = 0, const char * name = 0, WFlags f = 0 )
+ : QTabWidget(parent, name, f)
+ {
+ }
+
+protected:
+ virtual void mousePressEvent(QMouseEvent *e);
+
+signals:
+ void showContexMenu(QWidget *, const QPoint &);
+
+};
+
+class QLabel;
+class KPopupMenu;
+
+class MDITopLevel : public KMainWindow
+{
+ Q_OBJECT
+public:
+ MDITopLevel(QWidget *parent = 0, const char *name = 0);
+ virtual ~MDITopLevel();
+
+ void addWidget( QWidget *widget, bool show );
+
+ void removeWidget( QWidget *widget );
+
+ QTabWidget *tabWidget() const { return m_tab; }
+ void next();
+ void previous();
+
+ QPtrList<QWidget> &widgets() { return m_tabWidgets; }
+
+ void hideWidget( QWidget *widget );
+ void showWidget( QWidget *widget );
+
+ virtual bool eventFilter( QObject *obj, QEvent *ev );
+ bool closing() const { return m_closing; }
+
+protected:
+ void removeFromAddressedList( const QWidget* w );
+ virtual void closeEvent( QCloseEvent *ev );
+
+private slots:
+ void slotWidgetDestroyed();
+ void slotCurrentChanged( QWidget *page );
+ void slotMarkPageDirty( bool );
+ void slotChangeChannelName( const QString &, const QString &newName );
+ void slotShowContexMenu(QWidget *, const QPoint &);
+ void slotCloseLastWid();
+
+private:
+ KSTabWidget *m_tab;
+ KPopupMenu *m_pop;
+ QWidget *m_last_pop_wid;
+ QPtrList<QWidget> m_tabWidgets;
+ QPtrList<QWidget> m_addressed;
+ QPixmap m_dirtyIcon;
+ QPixmap m_addressedIcon;
+ bool m_closing;
+};
+
+#endif
diff --git a/ksirc/messageReceiver.cpp b/ksirc/messageReceiver.cpp
new file mode 100644
index 00000000..f2ac6ee0
--- /dev/null
+++ b/ksirc/messageReceiver.cpp
@@ -0,0 +1,73 @@
+#include "messageReceiver.h"
+
+#include <qtextcodec.h>
+#include <kcharsets.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <assert.h>
+
+KSircMessageReceiver::KSircMessageReceiver( KSircProcess * _proc )
+{
+ proc = _proc;
+ broadcast = TRUE;
+}
+
+KSircMessageReceiver::~KSircMessageReceiver()
+{
+}
+
+void KSircMessageReceiver::setBroadcast(bool bd)
+{
+ broadcast = bd;
+}
+
+bool KSircMessageReceiver::getBroadcast()
+{
+ return broadcast;
+}
+
+filterRuleList *KSircMessageReceiver::defaultRules()
+{
+ return new filterRuleList();
+}
+
+UnicodeMessageReceiver::UnicodeMessageReceiver( KSircProcess *process )
+ : KSircMessageReceiver( process ), m_encoder( 0 )
+{
+}
+
+void UnicodeMessageReceiver::sirc_receive( QCString str, bool broadcast )
+{
+ assert( encoder() );
+ //kdDebug(5008) << "Encoder: " << encoder()->name() << endl;
+ //kdDebug(5008) << "It gives us: " << encoder()->toUnicode( str ).utf8() << endl;
+ sirc_receive( encoder()->toUnicode( str ), broadcast );
+}
+
+void UnicodeMessageReceiver::setEncoding( const QString &encoding )
+{
+ m_encoding = encoding;
+ m_encoder = 0;
+}
+
+QString UnicodeMessageReceiver::encoding() const
+{
+ if ( m_encoding.isEmpty() )
+ return QTextCodec::codecForLocale()->mimeName();
+ return m_encoding;
+}
+
+QTextCodec *UnicodeMessageReceiver::encoder() const
+{
+ if ( m_encoding.isEmpty() ) {
+ return QTextCodec::codecForLocale();
+ }
+
+ if ( !m_encoder ) {
+ const_cast<UnicodeMessageReceiver *>( this )->m_encoder = KGlobal::charsets()->codecForName( m_encoding );
+ assert( m_encoder );
+ }
+
+ return m_encoder;
+}
+
diff --git a/ksirc/messageReceiver.h b/ksirc/messageReceiver.h
new file mode 100644
index 00000000..a86817b0
--- /dev/null
+++ b/ksirc/messageReceiver.h
@@ -0,0 +1,62 @@
+
+#ifndef KMESSAGERECEIVER_H
+#define KMESSAGERECEIVER_H
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+class KSircProcess;
+class QTextCodec;
+
+struct filterRule {
+ const char *desc;
+ const char *search;
+ const char *from;
+ const char *to;
+};
+
+typedef QPtrList<filterRule> filterRuleList;
+
+class KSircMessageReceiver
+{
+public:
+ KSircMessageReceiver(KSircProcess *_proc);
+ virtual ~KSircMessageReceiver();
+
+ virtual void sirc_receive(QCString str, bool broadcast = false) = 0;
+
+ virtual void control_message(int, QString) = 0;
+
+ bool getBroadcast();
+ void setBroadcast(bool bd);
+
+ virtual filterRuleList *defaultRules();
+
+ KSircProcess *ksircProcess() const { return proc; }
+
+private:
+ KSircProcess *proc;
+ bool broadcast;
+
+};
+
+class UnicodeMessageReceiver : public KSircMessageReceiver
+{
+public:
+ UnicodeMessageReceiver( KSircProcess *process );
+
+ virtual void sirc_receive(QCString str, bool broadcast = false);
+ virtual void sirc_receive(QString str, bool broadcast = false) = 0;
+
+protected:
+ void setEncoding( const QString &encoding );
+ QString encoding() const;
+
+ QTextCodec *encoder() const;
+
+private:
+ QString m_encoding;
+ QTextCodec *m_encoder;
+};
+
+#endif
diff --git a/ksirc/nickColourMaker.cpp b/ksirc/nickColourMaker.cpp
new file mode 100644
index 00000000..22c09b66
--- /dev/null
+++ b/ksirc/nickColourMaker.cpp
@@ -0,0 +1,86 @@
+/* This file is part of ksirc
+ Copyright (c) 2003 Andrew Stanley-Jones <asj@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// $Id$
+
+#include <qstring.h>
+#include <qcolor.h>
+#include <qregexp.h>
+#include <kdebug.h>
+
+#include "nickColourMaker.h"
+#include "ksopts.h"
+
+nickColourMaker *nickColourMaker::s_ncm = 0;
+
+nickColourMaker::nickColourMaker()
+{
+ s_ncm = this;
+}
+
+int nickColourMaker::findIdx(QString nick) const
+{
+ unsigned int value = 0;
+ uint i;
+ ushort c;
+
+ if(ksopts->nickColourization == false)
+ return -1;
+
+ for(i = 0; i < nick.length(); i++){
+ c = nick[i].unicode();
+ switch(c){
+ case '|':
+ case '-':
+ case '_':
+ if(i != 0)
+ i = nick.length();
+ else
+ value += c;
+ break;
+ default:
+ value += c;
+ }
+ }
+ value %= ksopts->numColors;
+ uint start = value;
+ while(ksopts->nickHighlight[value] == false){
+ value = (value + 1) % ksopts->numColors;
+ if(start == value)
+ return -1;
+
+ }
+
+
+ return value;
+
+}
+
+QColor nickColourMaker::findFg(QString nick) const
+{
+
+ int value = findIdx(nick);
+
+ if(value >= 0)
+ return ksopts->ircColors[value];
+ else if(ksopts->nickForeground.isValid())
+ return ksopts->nickForeground;
+ else
+ return ksopts->textColor;
+}
+
diff --git a/ksirc/nickColourMaker.h b/ksirc/nickColourMaker.h
new file mode 100644
index 00000000..239a7935
--- /dev/null
+++ b/ksirc/nickColourMaker.h
@@ -0,0 +1,44 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Andrew Stanley-Jones
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GPL.
+*/
+#ifndef __nickcolourmaker_h__
+#define __nickcolourmaker_h__
+
+#include <qdict.h>
+#include <qstring.h>
+#include <qmap.h>
+#include <qcolor.h>
+#include "ksopts.h"
+
+struct nickColourInfo
+{
+ nickColourInfo(int _fg) { fg = _fg; }
+ int fg;
+};
+
+// Helper class to parse IRC colour nicks in the
+// irc channel and the nick list
+class nickColourMaker
+{
+public:
+ nickColourMaker();
+
+ int findIdx(QString nick) const;
+ QColor findFg(QString nick) const;
+ QColor operator[]( QString nick ) const
+ {
+ return findFg(nick);
+ }
+
+ static nickColourMaker *colourMaker() { return s_ncm; }
+
+
+private:
+ static nickColourMaker *s_ncm;
+ QDict<nickColourInfo> m_nicks;
+};
+
+#endif
diff --git a/ksirc/objFinder.cpp b/ksirc/objFinder.cpp
new file mode 100644
index 00000000..4625b6cf
--- /dev/null
+++ b/ksirc/objFinder.cpp
@@ -0,0 +1,160 @@
+#include "objFinder.h"
+
+#include <qapplication.h>
+#include <qobjectlist.h>
+#include <qwidgetlist.h>
+
+#include <stdlib.h>
+#include <time.h>
+
+#include <kdebug.h>
+
+QDict<QObject> *objFinder::objList = new QDict<QObject>;
+
+/*
+ * So we can connect to the slots, etc
+ */
+objFinder *objFind = new objFinder();
+
+objFinder::objFinder()
+ : QObject()
+{
+}
+
+objFinder::~objFinder()
+{
+}
+
+void objFinder::insert(QObject *obj, const char *key){
+ QString name;
+
+ if(obj == 0x0){
+ qWarning("objFinder: Passed Null Object");
+ return;
+ }
+
+ if(key != 0){
+ name = key;
+ }
+ else {
+ name = obj->name();
+ if(name == 0){
+ name = randString();
+ }
+ }
+ objList->insert(name, obj);
+ connect(obj, SIGNAL(destroyed()),
+ objFind, SLOT(objDest()));
+
+ emit objFind->inserted(obj);
+}
+
+QObject *objFinder::find(const char *name, const char *inherits){
+ QObject *found;
+ QDictIterator<QObject> it(*objList);
+ uint len = strlen(name);
+ while(it.current()){
+ if(len == strlen(it.current()->name()) &&
+ strcmp(it.current()->name(), name) == 0)
+ return it.current();
+ QObjectList *qobl = it.current()->queryList(inherits, name, FALSE);
+ QObjectListIt itql( *qobl );
+ if(itql.current() != 0x0){
+ found = itql.current();
+ delete qobl;
+ return found;
+ }
+ delete qobl;
+ ++it;
+ }
+ QWidgetList *all = QApplication::allWidgets();
+ QWidgetListIt itW(*all);
+ while(itW.current()){
+ if(len == strlen(itW.current()->name()) &&
+ strcmp(itW.current()->name(), name) == 0){
+ if(inherits != 0x0 && itW.current()->inherits(inherits) == FALSE){
+ ++itW;
+ continue;
+ }
+ found = itW.current();
+ delete all;
+ return found;
+ }
+ ++itW;
+ }
+ delete all;
+
+ return 0x0;
+}
+
+void objFinder::dumpTree(){
+ QDictIterator<QObject> it(*objList);
+ while(it.current()){
+ it.current()->dumpObjectTree();
+ ++it;
+ }
+ QWidgetList *all = QApplication::allWidgets();
+ QWidgetListIt itW(*all);
+ while(itW.current()){
+ kdDebug(5008) << itW.current()->className() << "::" << itW.current()->name("unnamed") << endl;
+ ++itW;
+ }
+
+}
+
+QStringList objFinder::allObjects(){
+ QStringList allNames;
+ QDictIterator<QObject> it(*objList);
+ while(it.current()){
+ QObjectList *qobl = it.current()->queryList(); // Matches everything
+ QObjectListIt itql( *qobl );
+ while(itql.current()){
+ QString name;
+ name = itql.current()->className();
+ name += "::";
+ name += itql.current()->name("unnamed");
+ allNames.append(name);
+ ++itql;
+ }
+ delete qobl;
+ ++it;
+ }
+ QWidgetList *all = QApplication::allWidgets();
+ QWidgetListIt itW(*all);
+ while(itW.current()){
+ QString name;
+ name = itW.current()->className();
+ name += "::";
+ name += itW.current()->name("unnamed");
+ allNames.append(name);
+ ++itW;
+ }
+ delete all;
+ return allNames;
+}
+
+QString objFinder::randString(){
+ static bool runSrand = 0;
+ QString str = "";
+ if(runSrand == 0){
+ srand(time(NULL));
+ }
+ for(int i = 0; i <= 8; i++){
+ str.insert(0, (char) (1+(int) (94.0*rand()/(RAND_MAX+1.0))) + 0x20);
+ }
+ return str;
+}
+
+void objFinder::objDest(){
+ if(sender() == 0x0){
+ return;
+ }
+ QDictIterator<QObject> it(*objList);
+ while(it.current()){
+ if(it.current() == sender()){
+ objList->remove(it.currentKey());
+ }
+ ++it;
+ }
+}
+#include "objFinder.moc"
diff --git a/ksirc/objFinder.h b/ksirc/objFinder.h
new file mode 100644
index 00000000..8777dd68
--- /dev/null
+++ b/ksirc/objFinder.h
@@ -0,0 +1,41 @@
+#ifndef OBJFINGER_H
+#define OBJFINGER_H
+
+#include <qobject.h>
+#include <qdict.h>
+#include <qstringlist.h>
+
+class objFinder : public QObject {
+ Q_OBJECT
+public:
+
+ objFinder();
+ virtual ~objFinder();
+
+ static void insert(QObject *obj, const char *key = 0);
+ static QObject *find(const char *name, const char *inherits);
+ static void dumpTree();
+ static QStringList allObjects();
+
+signals:
+ void inserted(QObject *obj);
+
+protected slots:
+ void objDest();
+
+private:
+ /*
+ * Don't allow the object to be created, all it's members are static
+ */
+
+ static QString randString();
+
+
+ static QDict<QObject> *objList;
+
+};
+
+extern objFinder *objFind;
+
+#endif
+
diff --git a/ksirc/openksircproc.dlg b/ksirc/openksircproc.dlg
new file mode 100644
index 00000000..1378f569
--- /dev/null
+++ b/ksirc/openksircproc.dlg
@@ -0,0 +1,76 @@
+DlgEdit:v2.0:Dialog:
+Dialog {
+ ClassHeader {open_ksirc.h}
+ ClassSource {open_ksirc.cpp}
+ ClassName {open_ksirc}
+ DataHeader {open_ksircData.h}
+ DataSource {open_ksircData.cpp}
+ DataName {open_ksircData}
+ WindowBaseClass {QDialog}
+ IsModal {FALSE}
+ WindowCaption {Connect to Server}
+ WindowFlags {45056}
+}
+WidgetLayout {
+InitialPos {-1 -1}
+Size {400 90}
+MinSize {400 90}
+MaxSize {400 90}
+Grid {10}
+
+Label {
+ Text {Server:}
+ Alignment {33}
+ AutoResize {FALSE}
+ Margin {-1}
+ Rect {10 10 100 30}
+ Name {Label_1}
+ Font {"helvetica" 12 63 0 0 0 0 0 0}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {TRUE}
+ Text {Ok}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {200 50 190 30}
+ Name {PushButton_1}
+ Variable {okButton}
+ Font {"helvetica" 12 63 0 0 0 0 0 0}
+ Signal {[Protected] clicked --> create_toplevel ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+PushButton {
+ ToggleButton {FALSE}
+ Default {FALSE}
+ AutoDefault {FALSE}
+ Text {Cancel}
+ AutoRepeat {FALSE}
+ AutoResize {FALSE}
+ Rect {10 50 180 30}
+ Name {PushButton_2}
+ Signal {[Protected] clicked --> terminate ()}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+ComboBox {
+ Style {ReadWrite}
+ SizeLimit {10}
+ Policy {NoInsertion}
+ AutoResize {FALSE}
+ Rect {70 10 320 30}
+ Name {ComboBox_1}
+ Variable {nameSLE}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Layout {None}
+}
diff --git a/ksirc/puke/HOWTO-PUKE.pod b/ksirc/puke/HOWTO-PUKE.pod
new file mode 100644
index 00000000..cf44e77e
--- /dev/null
+++ b/ksirc/puke/HOWTO-PUKE.pod
@@ -0,0 +1,325 @@
+=head1 OVERVIEW
+
+This document describes how to write puke addons and additional
+widgets. It assumes a good knowledge of C++, perl and X/Qt workings
+under Linux.
+
+=head1 1. Description and Background
+
+=over 6
+
+Puke's a generic protocol allowing dsirc to communicate with ksirc.
+Communications works over a unix domain socket between multiple
+client dsirc process's to a single ksirc process. All communications
+is done via a variable length message with the following layout:
+
+=begin text
+
+struct PukeMessage {
+unsigned int iHeader;
+int iCommand;
+int iWinId;
+int iArg;
+int iTextSize;
+char *cArg;
+}
+
+=end text
+
+None of the fields except for iCommand, iWinId and iHeader have any restrictions
+on their content and may contain arbitrary values. iCommand and iWinId must
+contain an int and it used by ksirc to determine the destination and
+handler of the actual command. (and of course it's meaning). iHeader is a
+fixed pattern used to identify the start of a header message should it loose
+syncronization. The current pattern used it 2863311530, which is:
+10101010101010101010101010101010.
+
+=item Internal handling by kSirc:
+
+Messages are received by a generic handler, PukeController where the message
+is passed to the iWinId's messageDipatcher for final processing. The
+iWinId of 1 through 10 are reserved for internal use, and 1 is
+currently set at the window ID for the PukeController itself.
+
+Connect a signal to PukeControllers writeBuffer (signal's generally
+called outputMessage) and pass the fd and PukeMessage to be sent to
+the client. No parsing of the output message is done.
+
+=item Internal handling by dsirc:
+
+All received messages are handled by an internal callback methods. 3
+sets of callbacks are checked for handlers in the following order:
+
+$PUKE_HANDLER{$cmd}{$winid}
+$PUKE_HANDLER{-$cmd}{$winid}
+$PUKE_DEF_HANDLER{$cmd}
+
+If no handler is found an error is printed.
+
+Output is handled by the PukeSendMessage function. PBase defines an
+alternate routine sendMessage which should be a lot friendlier.
+
+=head1 2. How to create a new widget
+
+There are 2 parts to creating a widget, the C++ code and the
+supporting perl5-oop object.
+
+=head2 2.1 C++ Widget code
+
+The C++ code must be able to hand all required settings and messages
+for the widget. Each new widget iherites it's parent and so forth
+allowing for a nice oop layout. The widget structure the author is
+following is the same as Qt's. Their seems to work well, why
+re-invent the wheel?
+
+=item 2.1.1 General Layout, etc
+
+Figure where your new widget goes in the heirachy. If it's a simple
+Qt widget, I recommend using their existing layout. Man pages list
+what widgets inherit what.
+
+The general idea behind the widget layout should be to be to provide
+all the functionality of the widget to dsirc perl script. Incoming
+messages are handled via the messageHandler and ALL messages should
+return an ACK with current state info.
+
+New widgets are created as shared objects and loaded on the fly. This
+means you don't need to recompile ksirc to use new widgets etc.
+
+Generally you'll have to inherit PWidget at a minimum.
+
+Functions you HAVE TO overrite:
+
+B<1. createWidget>
+
+This function creates a new widget of your type and returns a
+*PWidget.
+
+B<2. messageHandler>
+
+This function receives ALL your commands.
+
+B<3. widget() and setWidget(YourWidget *)>
+
+These set and return your widget.
+
+
+If you care about inheritance, which you should, all these functions
+should be virtual. (Since we are using pointers to PWidget's
+everywhere, it's a good bet you want your children's overriden
+functions called, not yours)
+
+The structure internally will have to hold a local copy of the widget,
+and connect to it's destroy signal so you can know when it has been
+destroyed.
+
+=item 2.1.2 createWidget
+
+createWidget is defined as:
+
+PWidget *createWidget(widgetId *pwi, PWidget *parent);
+
+It is called everytime a new widget of yours is required. The
+widgetId will be the identifier for your widget and must be kept for
+all future commands. PWidget::setWidgetId(pwi) should be called to
+set the widget id. The *parent is the parent of the current widget.
+Generally PWidget->widget() is passed to the contructor of your
+widget. If it's 0, there is no parent. Simeplfied code for a the
+PFrame is:
+
+=begin text
+
+extern "C" {
+PWidget *createWidget(widgetId *pwi, PWIdget *parent);
+}
+
+PWidget *createWidget(widgetId *pwi, PWIdget *parent){
+ QFrame *f;
+ PFrame *pf = new PFrame(parent);
+ if(parent != 0){
+ f = new QFrame(parent->widget());
+ }
+ else{
+ f = new QPFrame();
+ }
+ pf->setWidget(f);
+ pf->setWidgetId(pwi);
+ return pf;
+}
+
+=end text
+
+Note: you have to check parent for null since calling NULL->widget()
+results in Bad Things (tm).
+
+=item 2.1.3 messageHandler
+
+This receives all commands, etc. It should process required commands,
+if a command is unkown pass it to the parent. PFrame example:
+
+=begin text
+
+class PFrame : public PWidget
+...
+void messageHandler(int fd, PukeMessage *pm);
+...
+
+void PFrame::messageHandler(int fd, PukeMessage *pm) {
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_QFRAME_SET_FRAME:
+ widget()->setFrameStyle(pm->iArg);
+ pmRet.iCommand = PUKE_QFRAME_SET_FRAME_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->frameStyle();
+ pmRet.cArg[0] = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+=end text
+
+=item 2.1.4 widget and setWidget
+
+Both these functions should be overriden and return your widget type,
+and set your widget. For setWidget you should connect required
+signals and eventFilters you are using.
+
+Make sure to call the parents setWidget in setWidget so it can connect
+filters etc.
+
+BEWARE: You might get the widget into setWidget being null (from the
+destructor).
+
+Another PFrame example (APE ;) ):
+
+=begin text
+void PFrame::setWidget(QFrame *_f)
+{
+ frame = _f;
+ PWidget::setWidget(_f);
+
+}
+
+
+QFrame *PFrame::widget()
+{
+ return frame;
+}
+
+=end text
+
+=item 2.1.5 Destructor
+
+Ok, unfortunaly since we have this internal widget floating arround
+the destructor has to a little maigc.
+
+Call the destructor as such:
+
+delete widget();
+setWidget(0);
+
+This will clear the widget from now and all parents and delete it.
+you never want it deleted twice. (deleting 0 won't hurt)
+
+=head2 2.2 The Perl code
+
+Most of the perl oop is pretty straight forward, command simply issue
+a require sendMessage and off everything goes. There's one problem.
+
+You can't get information back on the current read cycle. Huh? I can
+hear most people saying. It means say someone wanted to do $widget =
+$widget->height() and you didn't have the height information locally,
+there's no way to get the information and return it to them. Why? You
+issue a sendMessage(...) but until dsirc returns to the main select()
+loop, we never know there's more to read. We can't return to the main
+select loop until we return from our current function. What does this
+mean? We have to store all the information locally.
+
+This also brings up another intresting aspect. Sometimes a widget may
+depend on a prior command before it can complete. This is the purpose
+of canRun function, and onNext. It's use will have to be explained
+latter.
+
+To help with this problem, pbase.pm sets up a fairly complicated set
+of message and event queues. Be warned when you issue a sendMessage,
+it might not get sent right away.
+
+I'll provide example bellow of how I've done certain functions, this
+is certainly not the only way to do it. Feel free to use any format
+you like aslong as it get's the job done.
+
+Ok, so how do we do this?
+
+=item 2.2.1 Perl oop? where do I start?
+
+Read the perltoot and perlobj man pages.
+
+=item 2.2.2 What to inherit etc.
+
+You probably want to inherit the same object as your C function does.
+At very least you'll want to inherit PWidget.
+
+=item 2.2.3 new and DESTROY
+
+Your new function should look something like (APE?):
+
+=begin text
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_FRAME;
+
+ if($class eq 'PFrame'){
+ $self->create();
+ }
+
+ return $self;
+}
+
+=end text
+
+$self is the blessed variable and it returned from the super class.
+You should always do it this way. Setting widgetType defines the type
+of widget, and needs to be set before calling create.
+
+If we are creating an object of our own class we call create() which
+acutally sends out the correct creation messages, etc. You can
+override the create function, but do be warned it might not be a good
+idea. Make sure you understand what and how it does it first!
+
+=item 2.2.4 sendMessage
+
+sendMessage is the main form of communicating with kSirc. Generable
+arguments are:
+
+=begin text
+
+sendMessage('iCommand' => command number,
+ 'iArg' => interger argument,
+ 'cArg' => character string,
+ 'CallBack' => pointer to sub, generally sub{...}
+ );
+
+=end text
+
+You'll note it's a hash so order doesn't count. The callback is a 1
+shot call back (should be) so don't count on it getting hit twice.
+The call back's first argument will be a \%ARGS with the return
+arguments.
+
+=item 2.2.5 Final notes on perl
+
+Most of the function calls will just send out messages, etc. For call
+backs the general form I've found works well is: sub {$self->blah()}.
+
+=head1 3. Interfacing with the kSirc's main windows and functions
+
+NOT implemented yet.
+
+=back
diff --git a/ksirc/puke/Makefile.am b/ksirc/puke/Makefile.am
new file mode 100644
index 00000000..de25d641
--- /dev/null
+++ b/ksirc/puke/Makefile.am
@@ -0,0 +1,43 @@
+KDE_CXXFLAGS = $(USE_RTTI) $(USE_EXCEPTIONS) -UQT_NO_ASCII_CAST
+
+INCLUDES= $(all_includes)
+
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libpuke.la
+
+libpuke_la_SOURCES = pwidget.cpp pobject.cpp \
+ playout.cpp controller.cpp pframe.cpp \
+ plined.cpp pbutton.cpp ppushbt.cpp \
+ pprogress.cpp ptablevw.cpp plistbox.cpp \
+ plabel.cpp pmenudta.cpp ppopmenu.cpp \
+ palistbox.cpp ptabdialog.cpp \
+ pkfiledialog.cpp pobjfinder.cpp
+
+$(srcdir)/HOWTO-PUKE.txt: HOWTO-PUKE.pod
+ pod2text $(srcdir)/HOWTO-PUKE.pod > HOWTO-PUKE.txt
+
+# no install headers
+
+noinst_HEADERS = \
+ commands.h plined.h pwidget.h widgethdlr.h \
+ controller.h pframe.h pmessage.h HOWTO-PUKE.pod \
+ commands-handler.pl pboxlayout.pm pwidget.pm \
+ commands-perl.pl pframe.pm test.pl convert_commands.pl \
+ plined.pm tester.pl pbase.pm puke.pl ppushbt.h \
+ pbutton.h pprogress.h ptablevw.h plistbox.h \
+ plabel.h pobject.h playout.h pmenudta.h ppopmenu.h \
+ palistbox.h ptabdialog.h
+
+# Datafiles to install / uninstall : *.pl *.pm
+perl_DATA = commands-handler.pl commands-perl.pl convert_commands.pl \
+ puke.pl small.pl test.pl tester.pl \
+ dcc_progress.pm dcc_status.pm load_all.pm \
+ palistbox.pm pbase.pm pboxlayout.pm pbutton.pm pframe.pm \
+ pkfiledialog.pm plabel.pm plined.pm plistbox.pm pmenudta.pm \
+ ppopmenu.pm pprogress.pm ppushbt.pm ptabdialog.pm ptablevw.pm \
+ pwidget.pm pobjfinder.pm
+
+# Where to install them
+perldir = $(kde_datadir)/ksirc
+
diff --git a/ksirc/puke/commands-handler.pl b/ksirc/puke/commands-handler.pl
new file mode 100644
index 00000000..191353cf
--- /dev/null
+++ b/ksirc/puke/commands-handler.pl
@@ -0,0 +1,61 @@
+sub puke_invalid_cmd {
+ print "*E* Puke: Invalid command 0 ack'ed\n";
+ #
+ # Stop waiting, things are messed up
+ #
+ $wait = 0;
+}
+$PUKE_DEF_HANDLER{"$PUKE_INVALID"} = \&puke_invalid_cmd;
+
+
+#sub puke_widget_create_ack {
+# my %ARG = %{$_[0]};
+#
+# $ARG{cArg} =~ /^(.{8,8})/;
+# my $string = $1;
+#
+# if($PUKE_CREATOR{$string}){
+# &{$PUKE_CREATOR{$string}}(%ARG); # added %ARG
+# }
+# else {
+# print "*E* Widget created: $string but no handler\n";
+# }
+#}
+
+#$PUKE_DEF_HANDLER{"$PUKE_WIDGET_CREATE_ACK"} = \&puke_widget_create_ack;
+#$PUKE_DEF_HANDLER{"$PUKE_LAYOUT_NEW_ACK"} = \&puke_widget_create_ack;
+
+
+# By default we ignore all the EVENT's we get sent at us.
+
+$PUKE_DEF_HANDLER{"$PUKE_WIDGET_EVENT_NONE"} = sub {};
+$PUKE_DEF_HANDLER{"$PUKE_WIDGET_EVENT_TIME"} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_MOUSEDBLCLICK} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_MOUSEMOVE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_KEYPRESS} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_KEYRELEASE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_KEYPRESS} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_FOCUSIN} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_FOCUSOUT} = sub {};
+$PUKE_DEF_HANDLER{"$PUKE_WIDGET_EVENT_ENTER"} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_LEAVE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_PAINT} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_MOVE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_RESIZE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_CREATE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_DESTORY} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_SHOW} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_HIDE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_CLOSE} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_EVENT_TIMER} = sub {};
+
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_RESIZE_ACK} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_REPAINT_ACK} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_MOVE_ACK} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_SHOW_ACK} = sub {};
+$PUKE_DEF_HANDLER{$PUKE_WIDGET_HIDE_ACK} = sub {};
+
+$PUKE_DEF_HANDLER{$PUKE_LINED_SET_TEXT_ACK} = sub {};
+1;
diff --git a/ksirc/puke/commands-perl.pl b/ksirc/puke/commands-perl.pl
new file mode 100644
index 00000000..1499daa9
--- /dev/null
+++ b/ksirc/puke/commands-perl.pl
@@ -0,0 +1,605 @@
+$PUKE_INVALID = 0;
+$PUKE_NAME2NUM{'PUKE_INVALID'} = 0;
+$PUKE_NUM2NAME{'0'} = 'PUKE_INVALID';
+$PUKE_SETUP = 1;
+$PUKE_NAME2NUM{'PUKE_SETUP'} = 1;
+$PUKE_NUM2NAME{'1'} = 'PUKE_SETUP';
+$PUKE_SETUP_ACK = -1;
+$PUKE_NAME2NUM{'PUKE_SETUP_ACK'} = -1;
+$PUKE_NUM2NAME{'-1'} = 'PUKE_SETUP_ACK';
+$PUKE_ECHO = 5;
+$PUKE_NAME2NUM{'PUKE_ECHO'} = 5;
+$PUKE_NUM2NAME{'5'} = 'PUKE_ECHO';
+$PUKE_ECHO_ACK = -5;
+$PUKE_NAME2NUM{'PUKE_ECHO_ACK'} = -5;
+$PUKE_NUM2NAME{'-5'} = 'PUKE_ECHO_ACK';
+$PUKE_EVENT_UNKOWN = -999;
+$PUKE_NAME2NUM{'PUKE_EVENT_UNKOWN'} = -999;
+$PUKE_NUM2NAME{'-999'} = 'PUKE_EVENT_UNKOWN';
+$PUKE_DUMPTREE = 997;
+$PUKE_NAME2NUM{'PUKE_DUMPTREE'} = 997;
+$PUKE_NUM2NAME{'997'} = 'PUKE_DUMPTREE';
+$PUKE_DUMPTREE_ACK = -997;
+$PUKE_NAME2NUM{'PUKE_DUMPTREE_ACK'} = -997;
+$PUKE_NUM2NAME{'-997'} = 'PUKE_DUMPTREE_ACK';
+$PUKE_RELEASEWIDGET = 996;
+$PUKE_NAME2NUM{'PUKE_RELEASEWIDGET'} = 996;
+$PUKE_NUM2NAME{'996'} = 'PUKE_RELEASEWIDGET';
+$PUKE_RELEASEWIDGET_ACK = -996;
+$PUKE_NAME2NUM{'PUKE_RELEASEWIDGET_ACK'} = -996;
+$PUKE_NUM2NAME{'-996'} = 'PUKE_RELEASEWIDGET_ACK';
+$PUKE_FETCHWIDGET = 998;
+$PUKE_NAME2NUM{'PUKE_FETCHWIDGET'} = 998;
+$PUKE_NUM2NAME{'998'} = 'PUKE_FETCHWIDGET';
+$PUKE_FETCHWIDGET_ACK = -998;
+$PUKE_NAME2NUM{'PUKE_FETCHWIDGET_ACK'} = -998;
+$PUKE_NUM2NAME{'-998'} = 'PUKE_FETCHWIDGET_ACK';
+$PUKE_WIDGET_CREATE = 1000;
+$PUKE_NAME2NUM{'PUKE_WIDGET_CREATE'} = 1000;
+$PUKE_NUM2NAME{'1000'} = 'PUKE_WIDGET_CREATE';
+$PUKE_WIDGET_CREATE_ACK = -1000;
+$PUKE_NAME2NUM{'PUKE_WIDGET_CREATE_ACK'} = -1000;
+$PUKE_NUM2NAME{'-1000'} = 'PUKE_WIDGET_CREATE_ACK';
+$PUKE_WIDGET_DELETE = 1001;
+$PUKE_NAME2NUM{'PUKE_WIDGET_DELETE'} = 1001;
+$PUKE_NUM2NAME{'1001'} = 'PUKE_WIDGET_DELETE';
+$PUKE_WIDGET_DELETE_ACK = -1001;
+$PUKE_NAME2NUM{'PUKE_WIDGET_DELETE_ACK'} = -1001;
+$PUKE_NUM2NAME{'-1001'} = 'PUKE_WIDGET_DELETE_ACK';
+$PUKE_WIDGET_SHOW = 1002;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SHOW'} = 1002;
+$PUKE_NUM2NAME{'1002'} = 'PUKE_WIDGET_SHOW';
+$PUKE_WIDGET_SHOW_ACK = -1002;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SHOW_ACK'} = -1002;
+$PUKE_NUM2NAME{'-1002'} = 'PUKE_WIDGET_SHOW_ACK';
+$PUKE_WIDGET_HIDE = 1003;
+$PUKE_NAME2NUM{'PUKE_WIDGET_HIDE'} = 1003;
+$PUKE_NUM2NAME{'1003'} = 'PUKE_WIDGET_HIDE';
+$PUKE_WIDGET_HIDE_ACK = -1003;
+$PUKE_NAME2NUM{'PUKE_WIDGET_HIDE_ACK'} = -1003;
+$PUKE_NUM2NAME{'-1003'} = 'PUKE_WIDGET_HIDE_ACK';
+$PUKE_WIDGET_REPAINT = 1005;
+$PUKE_NAME2NUM{'PUKE_WIDGET_REPAINT'} = 1005;
+$PUKE_NUM2NAME{'1005'} = 'PUKE_WIDGET_REPAINT';
+$PUKE_WIDGET_REPAINT_ACK = -1005;
+$PUKE_NAME2NUM{'PUKE_WIDGET_REPAINT_ACK'} = -1005;
+$PUKE_NUM2NAME{'-1005'} = 'PUKE_WIDGET_REPAINT_ACK';
+$PUKE_WIDGET_UPDATE = 1010;
+$PUKE_NAME2NUM{'PUKE_WIDGET_UPDATE'} = 1010;
+$PUKE_NUM2NAME{'1010'} = 'PUKE_WIDGET_UPDATE';
+$PUKE_WIDGET_UPDATE_ACK = -1010;
+$PUKE_NAME2NUM{'PUKE_WIDGET_UPDATE_ACK'} = -1010;
+$PUKE_NUM2NAME{'-1010'} = 'PUKE_WIDGET_UPDATE_ACK';
+$PUKE_WIDGET_RESIZE = 1015;
+$PUKE_NAME2NUM{'PUKE_WIDGET_RESIZE'} = 1015;
+$PUKE_NUM2NAME{'1015'} = 'PUKE_WIDGET_RESIZE';
+$PUKE_WIDGET_RESIZE_ACK = -1015;
+$PUKE_NAME2NUM{'PUKE_WIDGET_RESIZE_ACK'} = -1015;
+$PUKE_NUM2NAME{'-1015'} = 'PUKE_WIDGET_RESIZE_ACK';
+$PUKE_WIDGET_EVENT_NONE = -1020;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_NONE'} = -1020;
+$PUKE_NUM2NAME{'-1020'} = 'PUKE_WIDGET_EVENT_NONE';
+$PUKE_WIDGET_EVENT_TIMER = -1021;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_TIMER'} = -1021;
+$PUKE_NUM2NAME{'-1021'} = 'PUKE_WIDGET_EVENT_TIMER';
+$PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS = -1022;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS'} = -1022;
+$PUKE_NUM2NAME{'-1022'} = 'PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS';
+$PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE = -1023;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE'} = -1023;
+$PUKE_NUM2NAME{'-1023'} = 'PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE';
+$PUKE_WIDGET_EVENT_MOUSEDBLCLICK = -1024;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_MOUSEDBLCLICK'} = -1024;
+$PUKE_NUM2NAME{'-1024'} = 'PUKE_WIDGET_EVENT_MOUSEDBLCLICK';
+$PUKE_WIDGET_EVENT_MOUSEMOVE = -1025;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_MOUSEMOVE'} = -1025;
+$PUKE_NUM2NAME{'-1025'} = 'PUKE_WIDGET_EVENT_MOUSEMOVE';
+$PUKE_WIDGET_EVENT_KEYPRESS = -1026;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_KEYPRESS'} = -1026;
+$PUKE_NUM2NAME{'-1026'} = 'PUKE_WIDGET_EVENT_KEYPRESS';
+$PUKE_WIDGET_EVENT_KEYRELEASE = -1027;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_KEYRELEASE'} = -1027;
+$PUKE_NUM2NAME{'-1027'} = 'PUKE_WIDGET_EVENT_KEYRELEASE';
+$PUKE_WIDGET_EVENT_FOCUSIN = -1028;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_FOCUSIN'} = -1028;
+$PUKE_NUM2NAME{'-1028'} = 'PUKE_WIDGET_EVENT_FOCUSIN';
+$PUKE_WIDGET_EVENT_FOCUSOUT = -1029;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_FOCUSOUT'} = -1029;
+$PUKE_NUM2NAME{'-1029'} = 'PUKE_WIDGET_EVENT_FOCUSOUT';
+$PUKE_WIDGET_EVENT_ENTER = -1030;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_ENTER'} = -1030;
+$PUKE_NUM2NAME{'-1030'} = 'PUKE_WIDGET_EVENT_ENTER';
+$PUKE_WIDGET_EVENT_LEAVE = -1031;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_LEAVE'} = -1031;
+$PUKE_NUM2NAME{'-1031'} = 'PUKE_WIDGET_EVENT_LEAVE';
+$PUKE_WIDGET_EVENT_PAINT = -1032;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_PAINT'} = -1032;
+$PUKE_NUM2NAME{'-1032'} = 'PUKE_WIDGET_EVENT_PAINT';
+$PUKE_WIDGET_EVENT_MOVE = -1033;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_MOVE'} = -1033;
+$PUKE_NUM2NAME{'-1033'} = 'PUKE_WIDGET_EVENT_MOVE';
+$PUKE_WIDGET_EVENT_RESIZE = -1034;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_RESIZE'} = -1034;
+$PUKE_NUM2NAME{'-1034'} = 'PUKE_WIDGET_EVENT_RESIZE';
+$PUKE_WIDGET_EVENT_CREATE = -1035 ;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_CREATE'} = -1035 ;
+$PUKE_NUM2NAME{'-1035 '} = 'PUKE_WIDGET_EVENT_CREATE';
+$PUKE_WIDGET_EVENT_DESTROY = -1036;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_DESTROY'} = -1036;
+$PUKE_NUM2NAME{'-1036'} = 'PUKE_WIDGET_EVENT_DESTROY';
+$PUKE_WIDGET_EVENT_SHOW = -1037 ;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_SHOW'} = -1037 ;
+$PUKE_NUM2NAME{'-1037 '} = 'PUKE_WIDGET_EVENT_SHOW';
+$PUKE_WIDGET_EVENT_HIDE = -1038 ;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_HIDE'} = -1038 ;
+$PUKE_NUM2NAME{'-1038 '} = 'PUKE_WIDGET_EVENT_HIDE';
+$PUKE_WIDGET_EVENT_CLOSE = -1039 ;
+$PUKE_NAME2NUM{'PUKE_WIDGET_EVENT_CLOSE'} = -1039 ;
+$PUKE_NUM2NAME{'-1039 '} = 'PUKE_WIDGET_EVENT_CLOSE';
+$PUKE_WIDGET_MOVE = 1050;
+$PUKE_NAME2NUM{'PUKE_WIDGET_MOVE'} = 1050;
+$PUKE_NUM2NAME{'1050'} = 'PUKE_WIDGET_MOVE';
+$PUKE_WIDGET_MOVE_ACK = -1050;
+$PUKE_NAME2NUM{'PUKE_WIDGET_MOVE_ACK'} = -1050;
+$PUKE_NUM2NAME{'-1050'} = 'PUKE_WIDGET_MOVE_ACK';
+$PUKE_WIDGET_LOAD = 1055;
+$PUKE_NAME2NUM{'PUKE_WIDGET_LOAD'} = 1055;
+$PUKE_NUM2NAME{'1055'} = 'PUKE_WIDGET_LOAD';
+$PUKE_WIDGET_LOAD_ACK = -1055;
+$PUKE_NAME2NUM{'PUKE_WIDGET_LOAD_ACK'} = -1055;
+$PUKE_NUM2NAME{'-1055'} = 'PUKE_WIDGET_LOAD_ACK';
+$PUKE_WIDGET_UNLOAD = 1060;
+$PUKE_NAME2NUM{'PUKE_WIDGET_UNLOAD'} = 1060;
+$PUKE_NUM2NAME{'1060'} = 'PUKE_WIDGET_UNLOAD';
+$PUKE_WIDGET_UNLOAD_ACK = -1060;
+$PUKE_NAME2NUM{'PUKE_WIDGET_UNLOAD_ACK'} = -1060;
+$PUKE_NUM2NAME{'-1060'} = 'PUKE_WIDGET_UNLOAD_ACK';
+$PUKE_WIDGET_SETMINSIZE = 1065;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETMINSIZE'} = 1065;
+$PUKE_NUM2NAME{'1065'} = 'PUKE_WIDGET_SETMINSIZE';
+$PUKE_WIDGET_SETMINSIZE_ACK = -1065;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETMINSIZE_ACK'} = -1065;
+$PUKE_NUM2NAME{'-1065'} = 'PUKE_WIDGET_SETMINSIZE_ACK';
+$PUKE_WIDGET_SETMAXSIZE = 1070;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETMAXSIZE'} = 1070;
+$PUKE_NUM2NAME{'1070'} = 'PUKE_WIDGET_SETMAXSIZE';
+$PUKE_WIDGET_SETMAXSIZE_ACK = -1070;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETMAXSIZE_ACK'} = -1070;
+$PUKE_NUM2NAME{'-1070'} = 'PUKE_WIDGET_SETMAXSIZE_ACK';
+$PUKE_WIDGET_SETCAPTION = 1075;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETCAPTION'} = 1075;
+$PUKE_NUM2NAME{'1075'} = 'PUKE_WIDGET_SETCAPTION';
+$PUKE_WIDGET_SETCAPTION_ACK = -1075;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SETCAPTION_ACK'} = -1075;
+$PUKE_NUM2NAME{'-1075'} = 'PUKE_WIDGET_SETCAPTION_ACK';
+$PUKE_WIDGET_GET_BACKGROUND_COLOUR = 1080;
+$PUKE_NAME2NUM{'PUKE_WIDGET_GET_BACKGROUND_COLOUR'} = 1080;
+$PUKE_NUM2NAME{'1080'} = 'PUKE_WIDGET_GET_BACKGROUND_COLOUR';
+$PUKE_WIDGET_GET_BACKGROUND_COLOUR_ACK = -1080;
+$PUKE_NAME2NUM{'PUKE_WIDGET_GET_BACKGROUND_COLOUR_ACK'} = -1080;
+$PUKE_NUM2NAME{'-1080'} = 'PUKE_WIDGET_GET_BACKGROUND_COLOUR_ACK';
+$PUKE_WIDGET_SET_BACKGROUND_COLOUR = 1085;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_COLOUR'} = 1085;
+$PUKE_NUM2NAME{'1085'} = 'PUKE_WIDGET_SET_BACKGROUND_COLOUR';
+$PUKE_WIDGET_SET_BACKGROUND_COLOUR_ACK = -1085;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_COLOUR_ACK'} = -1085;
+$PUKE_NUM2NAME{'-1085'} = 'PUKE_WIDGET_SET_BACKGROUND_COLOUR_ACK';
+$PUKE_WIDGET_SET_BACKGROUND_PIXMAP = 1086;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_PIXMAP'} = 1086;
+$PUKE_NUM2NAME{'1086'} = 'PUKE_WIDGET_SET_BACKGROUND_PIXMAP';
+$PUKE_WIDGET_SET_BACKGROUND_PIXMAP_ACK = -1086;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_PIXMAP_ACK'} = -1086;
+$PUKE_NUM2NAME{'-1086'} = 'PUKE_WIDGET_SET_BACKGROUND_PIXMAP_ACK';
+$PUKE_WIDGET_SET_BACKGROUND_MODE = 1087;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_MODE'} = 1087;
+$PUKE_NUM2NAME{'1087'} = 'PUKE_WIDGET_SET_BACKGROUND_MODE';
+$PUKE_WIDGET_SET_BACKGROUND_MODE_ACK = -1087;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_BACKGROUND_MODE_ACK'} = -1087;
+$PUKE_NUM2NAME{'-1087'} = 'PUKE_WIDGET_SET_BACKGROUND_MODE_ACK';
+$PUKE_WIDGET_SET_ENABLED = 1090;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_ENABLED'} = 1090;
+$PUKE_NUM2NAME{'1090'} = 'PUKE_WIDGET_SET_ENABLED';
+$PUKE_WIDGET_SET_ENABLED_ACK = -1090;
+$PUKE_NAME2NUM{'PUKE_WIDGET_SET_ENABLED_ACK'} = -1090;
+$PUKE_NUM2NAME{'-1090'} = 'PUKE_WIDGET_SET_ENABLED_ACK';
+$PUKE_WIDGET_RECREATE = 1091;
+$PUKE_NAME2NUM{'PUKE_WIDGET_RECREATE'} = 1091;
+$PUKE_NUM2NAME{'1091'} = 'PUKE_WIDGET_RECREATE';
+$PUKE_WIDGET_RECREATE_ACK = -1091;
+$PUKE_NAME2NUM{'PUKE_WIDGET_RECREATE_ACK'} = -1091;
+$PUKE_NUM2NAME{'-1091'} = 'PUKE_WIDGET_RECREATE_ACK';
+$PUKE_QFRAME_SET_FRAME = 1100;
+$PUKE_NAME2NUM{'PUKE_QFRAME_SET_FRAME'} = 1100;
+$PUKE_NUM2NAME{'1100'} = 'PUKE_QFRAME_SET_FRAME';
+$PUKE_QFRAME_SET_FRAME_ACK = -1100;
+$PUKE_NAME2NUM{'PUKE_QFRAME_SET_FRAME_ACK'} = -1100;
+$PUKE_NUM2NAME{'-1100'} = 'PUKE_QFRAME_SET_FRAME_ACK';
+$PUKE_QFRAME_SET_LINEWIDTH = 1105;
+$PUKE_NAME2NUM{'PUKE_QFRAME_SET_LINEWIDTH'} = 1105;
+$PUKE_NUM2NAME{'1105'} = 'PUKE_QFRAME_SET_LINEWIDTH';
+$PUKE_QFRAME_SET_LINEWIDTH_ACK = -1105;
+$PUKE_NAME2NUM{'PUKE_QFRAME_SET_LINEWIDTH_ACK'} = -1105;
+$PUKE_NUM2NAME{'-1105'} = 'PUKE_QFRAME_SET_LINEWIDTH_ACK';
+$PUKE_LINED_SET_MAXLENGTH = 1200;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_MAXLENGTH'} = 1200;
+$PUKE_NUM2NAME{'1200'} = 'PUKE_LINED_SET_MAXLENGTH';
+$PUKE_LINED_SET_MAXLENGTH_ACK = -1200;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_MAXLENGTH_ACK'} = -1200;
+$PUKE_NUM2NAME{'-1200'} = 'PUKE_LINED_SET_MAXLENGTH_ACK';
+$PUKE_LINED_SET_ECHOMODE = 1205;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_ECHOMODE'} = 1205;
+$PUKE_NUM2NAME{'1205'} = 'PUKE_LINED_SET_ECHOMODE';
+$PUKE_LINED_SET_ECHOMODE_ACK = -1205;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_ECHOMODE_ACK'} = -1205;
+$PUKE_NUM2NAME{'-1205'} = 'PUKE_LINED_SET_ECHOMODE_ACK';
+$PUKE_LINED_SET_TEXT = 1210;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_TEXT'} = 1210;
+$PUKE_NUM2NAME{'1210'} = 'PUKE_LINED_SET_TEXT';
+$PUKE_LINED_SET_TEXT_ACK = -1210;
+$PUKE_NAME2NUM{'PUKE_LINED_SET_TEXT_ACK'} = -1210;
+$PUKE_NUM2NAME{'-1210'} = 'PUKE_LINED_SET_TEXT_ACK';
+$PUKE_LINED_GET_TEXT = 1215;
+$PUKE_NAME2NUM{'PUKE_LINED_GET_TEXT'} = 1215;
+$PUKE_NUM2NAME{'1215'} = 'PUKE_LINED_GET_TEXT';
+$PUKE_LINED_GET_TEXT_ACK = -1215;
+$PUKE_NAME2NUM{'PUKE_LINED_GET_TEXT_ACK'} = -1215;
+$PUKE_NUM2NAME{'-1215'} = 'PUKE_LINED_GET_TEXT_ACK';
+$PUKE_LINED_RETURN_PRESSED = 1220;
+$PUKE_NAME2NUM{'PUKE_LINED_RETURN_PRESSED'} = 1220;
+$PUKE_NUM2NAME{'1220'} = 'PUKE_LINED_RETURN_PRESSED';
+$PUKE_LINED_RETURN_PRESSED_ACK = -1220;
+$PUKE_NAME2NUM{'PUKE_LINED_RETURN_PRESSED_ACK'} = -1220;
+$PUKE_NUM2NAME{'-1220'} = 'PUKE_LINED_RETURN_PRESSED_ACK';
+$PUKE_BUTTON_SET_TEXT = 1300;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_TEXT'} = 1300;
+$PUKE_NUM2NAME{'1300'} = 'PUKE_BUTTON_SET_TEXT';
+$PUKE_BUTTON_SET_TEXT_ACK = -1300;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_TEXT_ACK'} = -1300;
+$PUKE_NUM2NAME{'-1300'} = 'PUKE_BUTTON_SET_TEXT_ACK';
+$PUKE_BUTTON_SET_PIXMAP = 1305;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_PIXMAP'} = 1305;
+$PUKE_NUM2NAME{'1305'} = 'PUKE_BUTTON_SET_PIXMAP';
+$PUKE_BUTTON_SET_PIXMAP_ACK = -1305;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_PIXMAP_ACK'} = -1305;
+$PUKE_NUM2NAME{'-1305'} = 'PUKE_BUTTON_SET_PIXMAP_ACK';
+$PUKE_BUTTON_SET_AUTORESIZE = 1310;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_AUTORESIZE'} = 1310;
+$PUKE_NUM2NAME{'1310'} = 'PUKE_BUTTON_SET_AUTORESIZE';
+$PUKE_BUTTON_SET_AUTORESIZE_ACK = -1310;
+$PUKE_NAME2NUM{'PUKE_BUTTON_SET_AUTORESIZE_ACK'} = -1310;
+$PUKE_NUM2NAME{'-1310'} = 'PUKE_BUTTON_SET_AUTORESIZE_ACK';
+$PUKE_BUTTON_PRESSED = 1350;
+$PUKE_NAME2NUM{'PUKE_BUTTON_PRESSED'} = 1350;
+$PUKE_NUM2NAME{'1350'} = 'PUKE_BUTTON_PRESSED';
+$PUKE_BUTTON_PRESSED_ACK = -1350;
+$PUKE_NAME2NUM{'PUKE_BUTTON_PRESSED_ACK'} = -1350;
+$PUKE_NUM2NAME{'-1350'} = 'PUKE_BUTTON_PRESSED_ACK';
+$PUKE_BUTTON_RELEASED = 1351;
+$PUKE_NAME2NUM{'PUKE_BUTTON_RELEASED'} = 1351;
+$PUKE_NUM2NAME{'1351'} = 'PUKE_BUTTON_RELEASED';
+$PUKE_BUTTON_RELEASED_ACK = -1351;
+$PUKE_NAME2NUM{'PUKE_BUTTON_RELEASED_ACK'} = -1351;
+$PUKE_NUM2NAME{'-1351'} = 'PUKE_BUTTON_RELEASED_ACK';
+$PUKE_BUTTON_CLICKED = 1352;
+$PUKE_NAME2NUM{'PUKE_BUTTON_CLICKED'} = 1352;
+$PUKE_NUM2NAME{'1352'} = 'PUKE_BUTTON_CLICKED';
+$PUKE_BUTTON_CLICKED_ACK = -1352;
+$PUKE_NAME2NUM{'PUKE_BUTTON_CLICKED_ACK'} = -1352;
+$PUKE_NUM2NAME{'-1352'} = 'PUKE_BUTTON_CLICKED_ACK';
+$PUKE_BUTTON_TOGGLED = 1353;
+$PUKE_NAME2NUM{'PUKE_BUTTON_TOGGLED'} = 1353;
+$PUKE_NUM2NAME{'1353'} = 'PUKE_BUTTON_TOGGLED';
+$PUKE_BUTTON_TOGGLED_ACK = -1353;
+$PUKE_NAME2NUM{'PUKE_BUTTON_TOGGLED_ACK'} = -1353;
+$PUKE_NUM2NAME{'-1353'} = 'PUKE_BUTTON_TOGGLED_ACK';
+$PUKE_KSPROGRESS_SET_RANGE = 1400;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_RANGE'} = 1400;
+$PUKE_NUM2NAME{'1400'} = 'PUKE_KSPROGRESS_SET_RANGE';
+$PUKE_KSPROGRESS_SET_RANGE_ACK = -1400;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_RANGE_ACK'} = -1400;
+$PUKE_NUM2NAME{'-1400'} = 'PUKE_KSPROGRESS_SET_RANGE_ACK';
+$PUKE_KSPROGRESS_SET_TOPTEXT = 1410;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_TOPTEXT'} = 1410;
+$PUKE_NUM2NAME{'1410'} = 'PUKE_KSPROGRESS_SET_TOPTEXT';
+$PUKE_KSPROGRESS_SET_TOPTEXT_ACK = -1410;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_TOPTEXT_ACK'} = -1410;
+$PUKE_NUM2NAME{'-1410'} = 'PUKE_KSPROGRESS_SET_TOPTEXT_ACK';
+$PUKE_KSPROGRESS_SET_BOTTEXT = 1415;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_BOTTEXT'} = 1415;
+$PUKE_NUM2NAME{'1415'} = 'PUKE_KSPROGRESS_SET_BOTTEXT';
+$PUKE_KSPROGRESS_SET_BOTTEXT_ACK = -1415;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_BOTTEXT_ACK'} = -1415;
+$PUKE_NUM2NAME{'-1415'} = 'PUKE_KSPROGRESS_SET_BOTTEXT_ACK';
+$PUKE_KSPROGRESS_SET_VALUE = 1420;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_VALUE'} = 1420;
+$PUKE_NUM2NAME{'1420'} = 'PUKE_KSPROGRESS_SET_VALUE';
+$PUKE_KSPROGRESS_SET_VALUE_ACK = -1420;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_SET_VALUE_ACK'} = -1420;
+$PUKE_NUM2NAME{'-1420'} = 'PUKE_KSPROGRESS_SET_VALUE_ACK';
+$PUKE_KSPROGRESS_CANCEL = 1425;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_CANCEL'} = 1425;
+$PUKE_NUM2NAME{'1425'} = 'PUKE_KSPROGRESS_CANCEL';
+$PUKE_KSPROGRESS_CANCEL_ACK = -1425;
+$PUKE_NAME2NUM{'PUKE_KSPROGRESS_CANCEL_ACK'} = -1425;
+$PUKE_NUM2NAME{'-1425'} = 'PUKE_KSPROGRESS_CANCEL_ACK';
+$PUKE_LISTBOX_INSERT = 1600;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT'} = 1600;
+$PUKE_NUM2NAME{'1600'} = 'PUKE_LISTBOX_INSERT';
+$PUKE_LISTBOX_INSERT_ACK = -1600;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT_ACK'} = -1600;
+$PUKE_NUM2NAME{'-1600'} = 'PUKE_LISTBOX_INSERT_ACK';
+$PUKE_LISTBOX_INSERT_SORT = 1605;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT_SORT'} = 1605;
+$PUKE_NUM2NAME{'1605'} = 'PUKE_LISTBOX_INSERT_SORT';
+$PUKE_LISTBOX_INSERT_SORT_ACK = -1605;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT_SORT_ACK'} = -1605;
+$PUKE_NUM2NAME{'-1605'} = 'PUKE_LISTBOX_INSERT_SORT_ACK';
+$PUKE_LISTBOX_INSERT_PIXMAP = 1610;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT_PIXMAP'} = 1610;
+$PUKE_NUM2NAME{'1610'} = 'PUKE_LISTBOX_INSERT_PIXMAP';
+$PUKE_LISTBOX_INSERT_PIXMAP_ACK = -1610;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_INSERT_PIXMAP_ACK'} = -1610;
+$PUKE_NUM2NAME{'-1610'} = 'PUKE_LISTBOX_INSERT_PIXMAP_ACK';
+$PUKE_LISTBOX_HIGHLIGHT = 1615;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_HIGHLIGHT'} = 1615;
+$PUKE_NUM2NAME{'1615'} = 'PUKE_LISTBOX_HIGHLIGHT';
+$PUKE_LISTBOX_HIGHLIGHT_ACK = -1615;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_HIGHLIGHT_ACK'} = -1615;
+$PUKE_NUM2NAME{'-1615'} = 'PUKE_LISTBOX_HIGHLIGHT_ACK';
+$PUKE_LISTBOX_REMOVE = 1620;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_REMOVE'} = 1620;
+$PUKE_NUM2NAME{'1620'} = 'PUKE_LISTBOX_REMOVE';
+$PUKE_LISTBOX_REMOVE_ACK = -1620;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_REMOVE_ACK'} = -1620;
+$PUKE_NUM2NAME{'-1620'} = 'PUKE_LISTBOX_REMOVE_ACK';
+$PUKE_LISTBOX_GETTEXT = 1625;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_GETTEXT'} = 1625;
+$PUKE_NUM2NAME{'1625'} = 'PUKE_LISTBOX_GETTEXT';
+$PUKE_LISTBOX_GETTEXT_ACK = -1625 ;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_GETTEXT_ACK'} = -1625 ;
+$PUKE_NUM2NAME{'-1625 '} = 'PUKE_LISTBOX_GETTEXT_ACK';
+$PUKE_LISTBOX_CLEAR = 1630;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_CLEAR'} = 1630;
+$PUKE_NUM2NAME{'1630'} = 'PUKE_LISTBOX_CLEAR';
+$PUKE_LISTBOX_CLEAR_ACK = -1630;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_CLEAR_ACK'} = -1630;
+$PUKE_NUM2NAME{'-1630'} = 'PUKE_LISTBOX_CLEAR_ACK';
+$PUKE_LISTBOX_SET_SCROLLBAR = 1635;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SET_SCROLLBAR'} = 1635;
+$PUKE_NUM2NAME{'1635'} = 'PUKE_LISTBOX_SET_SCROLLBAR';
+$PUKE_LISTBOX_SET_SCROLLBAR_ACK = -1635;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SET_SCROLLBAR_ACK'} = -1635;
+$PUKE_NUM2NAME{'-1635'} = 'PUKE_LISTBOX_SET_SCROLLBAR_ACK';
+$PUKE_LISTBOX_SET_AUTO_SCROLLBAR = 1636;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SET_AUTO_SCROLLBAR'} = 1636;
+$PUKE_NUM2NAME{'1636'} = 'PUKE_LISTBOX_SET_AUTO_SCROLLBAR';
+$PUKE_LISTBOX_SET_AUTO_SCROLLBAR_ACK = -1636;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SET_AUTO_SCROLLBAR_ACK'} = -1636;
+$PUKE_NUM2NAME{'-1636'} = 'PUKE_LISTBOX_SET_AUTO_SCROLLBAR_ACK';
+$PUKE_LISTBOX_HIGHLIGHTED = 1690;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_HIGHLIGHTED'} = 1690;
+$PUKE_NUM2NAME{'1690'} = 'PUKE_LISTBOX_HIGHLIGHTED';
+$PUKE_LISTBOX_HIGHLIGHTED_ACK = -1690;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_HIGHLIGHTED_ACK'} = -1690;
+$PUKE_NUM2NAME{'-1690'} = 'PUKE_LISTBOX_HIGHLIGHTED_ACK';
+$PUKE_LISTBOX_SELECTED = 1690;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SELECTED'} = 1690;
+$PUKE_NUM2NAME{'1690'} = 'PUKE_LISTBOX_SELECTED';
+$PUKE_LISTBOX_SELECTED_ACK = -1690;
+$PUKE_NAME2NUM{'PUKE_LISTBOX_SELECTED_ACK'} = -1690;
+$PUKE_NUM2NAME{'-1690'} = 'PUKE_LISTBOX_SELECTED_ACK';
+$PUKE_LABEL_SETTEXT = 1700;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETTEXT'} = 1700;
+$PUKE_NUM2NAME{'1700'} = 'PUKE_LABEL_SETTEXT';
+$PUKE_LABEL_SETTEXT_ACK = -1700;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETTEXT_ACK'} = -1700;
+$PUKE_NUM2NAME{'-1700'} = 'PUKE_LABEL_SETTEXT_ACK';
+$PUKE_LABEL_SETPIXMAP = 1705;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETPIXMAP'} = 1705;
+$PUKE_NUM2NAME{'1705'} = 'PUKE_LABEL_SETPIXMAP';
+$PUKE_LABEL_SETPIXMAP_ACK = -1705;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETPIXMAP_ACK'} = -1705;
+$PUKE_NUM2NAME{'-1705'} = 'PUKE_LABEL_SETPIXMAP_ACK';
+$PUKE_LABEL_SETMOVIE = 1710;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETMOVIE'} = 1710;
+$PUKE_NUM2NAME{'1710'} = 'PUKE_LABEL_SETMOVIE';
+$PUKE_LABEL_SETMOVIE_ACK = -1710;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETMOVIE_ACK'} = -1710;
+$PUKE_NUM2NAME{'-1710'} = 'PUKE_LABEL_SETMOVIE_ACK';
+$PUKE_LABEL_SETALIGNMENT = 1715;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETALIGNMENT'} = 1715;
+$PUKE_NUM2NAME{'1715'} = 'PUKE_LABEL_SETALIGNMENT';
+$PUKE_LABEL_SETALIGNMENT_ACK = -1715;
+$PUKE_NAME2NUM{'PUKE_LABEL_SETALIGNMENT_ACK'} = -1715;
+$PUKE_NUM2NAME{'-1715'} = 'PUKE_LABEL_SETALIGNMENT_ACK';
+$PUKE_MENUDATA_INSERT_TEXT = 1800;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_INSERT_TEXT'} = 1800;
+$PUKE_NUM2NAME{'1800'} = 'PUKE_MENUDATA_INSERT_TEXT';
+$PUKE_MENUDATA_INSERT_TEXT_ACK = -1800;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_INSERT_TEXT_ACK'} = -1800;
+$PUKE_NUM2NAME{'-1800'} = 'PUKE_MENUDATA_INSERT_TEXT_ACK';
+$PUKE_MENUDATA_INSERT_PIXMAP = 1801;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_INSERT_PIXMAP'} = 1801;
+$PUKE_NUM2NAME{'1801'} = 'PUKE_MENUDATA_INSERT_PIXMAP';
+$PUKE_MENUDATA_INSERT_PIXMAP_ACK = -1801;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_INSERT_PIXMAP_ACK'} = -1801;
+$PUKE_NUM2NAME{'-1801'} = 'PUKE_MENUDATA_INSERT_PIXMAP_ACK';
+$PUKE_POPUPMENU_ACTIVATED = 1805;
+$PUKE_NAME2NUM{'PUKE_POPUPMENU_ACTIVATED'} = 1805;
+$PUKE_NUM2NAME{'1805'} = 'PUKE_POPUPMENU_ACTIVATED';
+$PUKE_POPUPMENU_ACTIVATED_ACK = -1805;
+$PUKE_NAME2NUM{'PUKE_POPUPMENU_ACTIVATED_ACK'} = -1805;
+$PUKE_NUM2NAME{'-1805'} = 'PUKE_POPUPMENU_ACTIVATED_ACK';
+$PUKE_POPUPMENU_POPUP_CURRENT = 1810;
+$PUKE_NAME2NUM{'PUKE_POPUPMENU_POPUP_CURRENT'} = 1810;
+$PUKE_NUM2NAME{'1810'} = 'PUKE_POPUPMENU_POPUP_CURRENT';
+$PUKE_POPUPMENU_POPUP_CURRENT_ACK = -1810;
+$PUKE_NAME2NUM{'PUKE_POPUPMENU_POPUP_CURRENT_ACK'} = -1810;
+$PUKE_NUM2NAME{'-1810'} = 'PUKE_POPUPMENU_POPUP_CURRENT_ACK';
+$PUKE_MENUDATA_REMOVE_ITEM = 1815;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_REMOVE_ITEM'} = 1815;
+$PUKE_NUM2NAME{'1815'} = 'PUKE_MENUDATA_REMOVE_ITEM';
+$PUKE_MENUDATA_REMOVE_ITEM_ACK = -1815;
+$PUKE_NAME2NUM{'PUKE_MENUDATA_REMOVE_ITEM_ACK'} = -1815;
+$PUKE_NUM2NAME{'-1815'} = 'PUKE_MENUDATA_REMOVE_ITEM_ACK';
+$PUKE_ALISTBOX_ISTOP = 1900;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_ISTOP'} = 1900;
+$PUKE_NUM2NAME{'1900'} = 'PUKE_ALISTBOX_ISTOP';
+$PUKE_ALISTBOX_ISTOP_ACK = -1900;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_ISTOP_ACK'} = -1900;
+$PUKE_NUM2NAME{'-1900'} = 'PUKE_ALISTBOX_ISTOP_ACK';
+$PUKE_ALISTBOX_SMALL_HIGHLIGHT = 1905;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_SMALL_HIGHLIGHT'} = 1905;
+$PUKE_NUM2NAME{'1905'} = 'PUKE_ALISTBOX_SMALL_HIGHLIGHT';
+$PUKE_ALISTBOX_SMALL_HIGHLIGHT_ACK = -1905;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_SMALL_HIGHLIGHT_ACK'} = -1905;
+$PUKE_NUM2NAME{'-1905'} = 'PUKE_ALISTBOX_SMALL_HIGHLIGHT_ACK';
+$PUKE_ALISTBOX_BIG_HIGHLIGHT = 1910;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_BIG_HIGHLIGHT'} = 1910;
+$PUKE_NUM2NAME{'1910'} = 'PUKE_ALISTBOX_BIG_HIGHLIGHT';
+$PUKE_ALISTBOX_BIG_HIGHLIGHT_ACK = -1910;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_BIG_HIGHLIGHT_ACK'} = -1910;
+$PUKE_NUM2NAME{'-1910'} = 'PUKE_ALISTBOX_BIG_HIGHLIGHT_ACK';
+$PUKE_ALISTBOX_FIND_NICK = 1915;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_FIND_NICK'} = 1915;
+$PUKE_NUM2NAME{'1915'} = 'PUKE_ALISTBOX_FIND_NICK';
+$PUKE_ALISTBOX_FIND_NICK_ACK = -1915;
+$PUKE_NAME2NUM{'PUKE_ALISTBOX_FIND_NICK_ACK'} = -1915;
+$PUKE_NUM2NAME{'-1915'} = 'PUKE_ALISTBOX_FIND_NICK_ACK';
+$PUKE_TABDIALOG_ADDTAB = 2001;
+$PUKE_NAME2NUM{'PUKE_TABDIALOG_ADDTAB'} = 2001;
+$PUKE_NUM2NAME{'2001'} = 'PUKE_TABDIALOG_ADDTAB';
+$PUKE_TABDIALOG_ADDTAB_ACK = -2001;
+$PUKE_NAME2NUM{'PUKE_TABDIALOG_ADDTAB_ACK'} = -2001;
+$PUKE_NUM2NAME{'-2001'} = 'PUKE_TABDIALOG_ADDTAB_ACK';
+$PUKE_KSIRCLISTBOX_TOBOTTOM = 2100;
+$PUKE_NAME2NUM{'PUKE_KSIRCLISTBOX_TOBOTTOM'} = 2100;
+$PUKE_NUM2NAME{'2100'} = 'PUKE_KSIRCLISTBOX_TOBOTTOM';
+$PUKE_KSIRCLISTBOX_TOBOTTOM_ACK = -2100;
+$PUKE_NAME2NUM{'PUKE_KSIRCLISTBOX_TOBOTTOM_ACK'} = -2100;
+$PUKE_NUM2NAME{'-2100'} = 'PUKE_KSIRCLISTBOX_TOBOTTOM_ACK';
+$PWIDGET_OBJECT = 1;
+$PUKE_NAME2NUM{'PWIDGET_OBJECT'} = 1;
+$PUKE_NUM2NAME{'1'} = 'PWIDGET_OBJECT';
+$PWIDGET_WIDGET = 2;
+$PUKE_NAME2NUM{'PWIDGET_WIDGET'} = 2;
+$PUKE_NUM2NAME{'2'} = 'PWIDGET_WIDGET';
+$PWIDGET_FRAME = 3;
+$PUKE_NAME2NUM{'PWIDGET_FRAME'} = 3;
+$PUKE_NUM2NAME{'3'} = 'PWIDGET_FRAME';
+$PWIDGET_LINED = 4;
+$PUKE_NAME2NUM{'PWIDGET_LINED'} = 4;
+$PUKE_NUM2NAME{'4'} = 'PWIDGET_LINED';
+$PWIDGET_BUTTON = 5;
+$PUKE_NAME2NUM{'PWIDGET_BUTTON'} = 5;
+$PUKE_NUM2NAME{'5'} = 'PWIDGET_BUTTON';
+$PWIDGET_PUSHBT = 6;
+$PUKE_NAME2NUM{'PWIDGET_PUSHBT'} = 6;
+$PUKE_NUM2NAME{'6'} = 'PWIDGET_PUSHBT';
+$PWIDGET_KSPROGRESS = 7;
+$PUKE_NAME2NUM{'PWIDGET_KSPROGRESS'} = 7;
+$PUKE_NUM2NAME{'7'} = 'PWIDGET_KSPROGRESS';
+$PWIDGET_TABLEVW = 8;
+$PUKE_NAME2NUM{'PWIDGET_TABLEVW'} = 8;
+$PUKE_NUM2NAME{'8'} = 'PWIDGET_TABLEVW';
+$PWIDGET_LISTBOX = 9;
+$PUKE_NAME2NUM{'PWIDGET_LISTBOX'} = 9;
+$PUKE_NUM2NAME{'9'} = 'PWIDGET_LISTBOX';
+$PWIDGET_LABEL = 10;
+$PUKE_NAME2NUM{'PWIDGET_LABEL'} = 10;
+$PUKE_NUM2NAME{'10'} = 'PWIDGET_LABEL';
+$POBJECT_LAYOUT = 11;
+$PUKE_NAME2NUM{'POBJECT_LAYOUT'} = 11;
+$PUKE_NUM2NAME{'11'} = 'POBJECT_LAYOUT';
+$PWIDGET_MENUDATA = 12;
+$PUKE_NAME2NUM{'PWIDGET_MENUDATA'} = 12;
+$PUKE_NUM2NAME{'12'} = 'PWIDGET_MENUDATA';
+$PWIDGET_POPMENU = 13;
+$PUKE_NAME2NUM{'PWIDGET_POPMENU'} = 13;
+$PUKE_NUM2NAME{'13'} = 'PWIDGET_POPMENU';
+$PWIDGET_ALISTBOX = 14;
+$PUKE_NAME2NUM{'PWIDGET_ALISTBOX'} = 14;
+$PUKE_NUM2NAME{'14'} = 'PWIDGET_ALISTBOX';
+$PWIDGET_KSIRCLISTBOX = 15;
+$PUKE_NAME2NUM{'PWIDGET_KSIRCLISTBOX'} = 15;
+$PUKE_NUM2NAME{'15'} = 'PWIDGET_KSIRCLISTBOX';
+$PWIDGET_TABDIALOG = 16;
+$PUKE_NAME2NUM{'PWIDGET_TABDIALOG'} = 16;
+$PUKE_NUM2NAME{'16'} = 'PWIDGET_TABDIALOG';
+$PWIDGET_KFILEDIALOG = 17;
+$PUKE_NAME2NUM{'PWIDGET_KFILEDIALOG'} = 17;
+$PUKE_NUM2NAME{'17'} = 'PWIDGET_KFILEDIALOG';
+$PWIDGET_OBJFINDER = 18;
+$PUKE_NAME2NUM{'PWIDGET_OBJFINDER'} = 18;
+$PUKE_NUM2NAME{'18'} = 'PWIDGET_OBJFINDER';
+$PUKE_LAYOUT_NEW = 11000;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_NEW'} = 11000;
+$PUKE_NUM2NAME{'11000'} = 'PUKE_LAYOUT_NEW';
+$PUKE_LAYOUT_NEW_ACK = -11000;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_NEW_ACK'} = -11000;
+$PUKE_NUM2NAME{'-11000'} = 'PUKE_LAYOUT_NEW_ACK';
+$PUKE_LAYOUT_ADDLAYOUT = 11005;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDLAYOUT'} = 11005;
+$PUKE_NUM2NAME{'11005'} = 'PUKE_LAYOUT_ADDLAYOUT';
+$PUKE_LAYOUT_ADDLAYOUT_ACK = -11005;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDLAYOUT_ACK'} = -11005;
+$PUKE_NUM2NAME{'-11005'} = 'PUKE_LAYOUT_ADDLAYOUT_ACK';
+$PUKE_LAYOUT_ADDWIDGET = 11010;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDWIDGET'} = 11010;
+$PUKE_NUM2NAME{'11010'} = 'PUKE_LAYOUT_ADDWIDGET';
+$PUKE_LAYOUT_ADDWIDGET_ACK = -11010;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDWIDGET_ACK'} = -11010;
+$PUKE_NUM2NAME{'-11010'} = 'PUKE_LAYOUT_ADDWIDGET_ACK';
+$PUKE_LAYOUT_ADDSTRUT = 11015;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDSTRUT'} = 11015;
+$PUKE_NUM2NAME{'11015'} = 'PUKE_LAYOUT_ADDSTRUT';
+$PUKE_LAYOUT_ADDSTRUT_ACK = -11015;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ADDSTRUT_ACK'} = -11015;
+$PUKE_NUM2NAME{'-11015'} = 'PUKE_LAYOUT_ADDSTRUT_ACK';
+$PUKE_LAYOUT_ACTIVATE = 11020;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ACTIVATE'} = 11020;
+$PUKE_NUM2NAME{'11020'} = 'PUKE_LAYOUT_ACTIVATE';
+$PUKE_LAYOUT_ACTIVATE_ACK = -11020;
+$PUKE_NAME2NUM{'PUKE_LAYOUT_ACTIVATE_ACK'} = -11020;
+$PUKE_NUM2NAME{'-11020'} = 'PUKE_LAYOUT_ACTIVATE_ACK';
+$PUKE_CONTROLLER = 1;
+$PUKE_NAME2NUM{'PUKE_CONTROLLER'} = 1;
+$PUKE_NUM2NAME{'1'} = 'PUKE_CONTROLLER';
+$PUKE_KBFD_SET_PATH = 2200;
+$PUKE_NAME2NUM{'PUKE_KBFD_SET_PATH'} = 2200;
+$PUKE_NUM2NAME{'2200'} = 'PUKE_KBFD_SET_PATH';
+$PUKE_BFD_SET_PATH_ACK = -2200;
+$PUKE_NAME2NUM{'PUKE_BFD_SET_PATH_ACK'} = -2200;
+$PUKE_NUM2NAME{'-2200'} = 'PUKE_BFD_SET_PATH_ACK';
+$PUKE_KBFD_SET_FILTER = 2201;
+$PUKE_NAME2NUM{'PUKE_KBFD_SET_FILTER'} = 2201;
+$PUKE_NUM2NAME{'2201'} = 'PUKE_KBFD_SET_FILTER';
+$PUKE_KBFD_SET_FILTER_ACK = -2201;
+$PUKE_NAME2NUM{'PUKE_KBFD_SET_FILTER_ACK'} = -2201;
+$PUKE_NUM2NAME{'-2201'} = 'PUKE_KBFD_SET_FILTER_ACK';
+$PUKE_KBFD_SET_SELECTION = 2202;
+$PUKE_NAME2NUM{'PUKE_KBFD_SET_SELECTION'} = 2202;
+$PUKE_NUM2NAME{'2202'} = 'PUKE_KBFD_SET_SELECTION';
+$PUKE_KBFD_SET_SELECTION_ACK = -2202;
+$PUKE_NAME2NUM{'PUKE_KBFD_SET_SELECTION_ACK'} = -2202;
+$PUKE_NUM2NAME{'-2202'} = 'PUKE_KBFD_SET_SELECTION_ACK';
+$PUKE_KBFD_FILE_SELECTED = 2203;
+$PUKE_NAME2NUM{'PUKE_KBFD_FILE_SELECTED'} = 2203;
+$PUKE_NUM2NAME{'2203'} = 'PUKE_KBFD_FILE_SELECTED';
+$PUKE_KBFD_FILE_SELECTED_ACK = -2203;
+$PUKE_NAME2NUM{'PUKE_KBFD_FILE_SELECTED_ACK'} = -2203;
+$PUKE_NUM2NAME{'-2203'} = 'PUKE_KBFD_FILE_SELECTED_ACK';
+$PUKE_OBJFINDER_ALLOBJECTS = 2300;
+$PUKE_NAME2NUM{'PUKE_OBJFINDER_ALLOBJECTS'} = 2300;
+$PUKE_NUM2NAME{'2300'} = 'PUKE_OBJFINDER_ALLOBJECTS';
+$PUKE_OBJFINDER_ALLOBJECTS_ACK = -2300;
+$PUKE_NAME2NUM{'PUKE_OBJFINDER_ALLOBJECTS_ACK'} = -2300;
+$PUKE_NUM2NAME{'-2300'} = 'PUKE_OBJFINDER_ALLOBJECTS_ACK';
+$PUKE_OBJFINDER_NEWOBJECT = 2301;
+$PUKE_NAME2NUM{'PUKE_OBJFINDER_NEWOBJECT'} = 2301;
+$PUKE_NUM2NAME{'2301'} = 'PUKE_OBJFINDER_NEWOBJECT';
+$PUKE_OBJFINDER_NEWOBJECT_ACK = -2301;
+$PUKE_NAME2NUM{'PUKE_OBJFINDER_NEWOBJECT_ACK'} = -2301;
+$PUKE_NUM2NAME{'-2301'} = 'PUKE_OBJFINDER_NEWOBJECT_ACK';
+
+1;
diff --git a/ksirc/puke/commands.h b/ksirc/puke/commands.h
new file mode 100644
index 00000000..3e45d8ba
--- /dev/null
+++ b/ksirc/puke/commands.h
@@ -0,0 +1,1052 @@
+
+// 0 is special "invalid character"
+// Value > 0 indicates from dsirc -> ksirc
+// Value < 0 indicates from ksirc -> dsirc
+
+// Desc: INVALID command
+// iWinId: not defined
+// iArg: not defined
+// cArg: not defined
+#define PUKE_INVALID 0
+
+// Desc: associantes server name with fd.
+// iWinId: not defined, but pass unchanged
+// iArg: not defined
+// cArg: name of the server
+#define PUKE_SETUP 1
+
+// Desc: replies to make sure association was valid
+// iWinId: not defined, but pass unchanged
+// iArg: size of Message
+// cArg: not defined
+#define PUKE_SETUP_ACK -1
+
+
+// Desc: sends the ack back. Used for doing actions after returning to select.
+// iWinId: window id
+// iArg: not defined
+// cArg: not define
+#define PUKE_ECHO 5
+
+// Desc: sends the ack back. Used for doing actions after returning to select.
+// iWinId: window id
+// iArg: not defined
+// cArg: not define
+#define PUKE_ECHO_ACK -5
+
+
+// From ksirc to sirc, event command unkown.
+// There should be somewhere better for this!!!
+
+#define PUKE_EVENT_UNKOWN -999
+
+// Dumps object tree
+// iWinId: puke controller
+// iArg: not defined
+// cArg: undef
+#define PUKE_DUMPTREE 997
+
+// Desc: dump object tree ack
+// iWinId: undef
+// iArg: undef
+// cArg: undef
+#define PUKE_DUMPTREE_ACK -997
+
+// Release a widget without delete'ing it, used after fetching a widget
+// iWinId: window id
+// iArg: undef
+// cArg: undef
+#define PUKE_RELEASEWIDGET 996
+
+// Desc: release ack
+// iWinId: window id
+// iArg: undef
+// cArg: undef
+#define PUKE_RELEASEWIDGET_ACK -996
+
+
+// Fetch widgets
+// Desc: get widget from parent
+// iWinId: not defined
+// iArg: not defined
+// cArg: 2 feilds, tab sperated, must be returned unchanged. 1. random string. 3. Object Name.
+#define PUKE_FETCHWIDGET 998
+
+// Desc: Fetch widget ack
+// iWinId: new widget id
+// iArg: not defined
+// cArg: 2 feilds, tab sperated, must be returned unchanged. 1. random string. 3. Object Name.
+#define PUKE_FETCHWIDGET_ACK -998
+
+
+// Widget commands starts at 1000 and end at 10000
+
+// Desc: create new widget
+// iWinId: parent widget id
+// iArg: widget type as defined by PWIDGET_*
+// cArg: Must be return unchanged
+#define PUKE_WIDGET_CREATE 1000
+
+// Desc: ack for newly created new widget
+// iWinId: new widget Id, 0 if failed
+// iArg: widget type as defined by PWIDGET_*
+// cArg: Returned unchanged
+#define PUKE_WIDGET_CREATE_ACK -1000
+
+
+// Desc: shows requested widget
+// iWinId: widget to show
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_DELETE 1001
+
+// Desc: ack for show requested widget
+// iWinId: widget to show
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_DELETE_ACK -1001
+
+// Desc: shows requested widget
+// iWinId: widget to show
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_SHOW 1002
+
+// Desc: ack for show requested widget
+// iWinId: widget to show
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_SHOW_ACK -1002
+
+// Desc: hidess requested widget
+// iWinId: widget to hide
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_HIDE 1003
+
+// Desc: ack for hide requested widget
+// iWinId: widget to hide
+// iArg: not defined
+// cArg: not define
+#define PUKE_WIDGET_HIDE_ACK -1003
+
+// Desc: repaint widget
+// iWinId: widget to repaint
+// iArg: erase, 0 for false, 1 for true
+// cArg: no defines
+#define PUKE_WIDGET_REPAINT 1005
+
+// Desc: repaint widget ack
+// iWinId: widget to repaint
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_REPAINT_ACK -1005
+
+// Desc: update widget on next event loop
+// iWinId: widget to repaint
+// iArg: erase, 0 for false, 1 for true
+// cArg: no defines
+#define PUKE_WIDGET_UPDATE 1010
+
+// Desc: repaint widget ack
+// iWinId: widget to repaint
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_UPDATE_ACK -1010
+
+// Desc: resize the widget
+// iWinId: widget to repaint
+// iArg: width in the low 16 bit, height in next 16 bits
+// cArg: not defined
+#define PUKE_WIDGET_RESIZE 1015
+
+// Desc: repaint widget ack
+// iWinId: widget to repaint
+// iArg: new widget in lower 16 bit, new height in lower 16 bits
+// cArg: not defined
+#define PUKE_WIDGET_RESIZE_ACK -1015
+
+// -1020 to -1040 defines QEvent types
+// All arguments are 0 unless otherwise stated
+#define PUKE_WIDGET_EVENT_NONE -1020
+// iArg: timerId
+#define PUKE_WIDGET_EVENT_TIMER -1021
+// All EVENT_MOUSE are:
+// cArg: cast to int[25], int[0] = x(), int[1] = y(), int[2] = button()
+// int[3] = state()
+#define PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS -1022
+#define PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE -1023
+#define PUKE_WIDGET_EVENT_MOUSEDBLCLICK -1024
+#define PUKE_WIDGET_EVENT_MOUSEMOVE -1025
+// All EVENT_KEY
+// cArg: cast to int[25], int[0] = key(), int[1] = ascii(), int[2] = state()
+#define PUKE_WIDGET_EVENT_KEYPRESS -1026
+#define PUKE_WIDGET_EVENT_KEYRELEASE -1027
+// All EVENT_FOCUS
+// cArg[0] = gotFocus()
+// cArg[1] = lostFocus()
+#define PUKE_WIDGET_EVENT_FOCUSIN -1028
+#define PUKE_WIDGET_EVENT_FOCUSOUT -1029
+#define PUKE_WIDGET_EVENT_ENTER -1030
+#define PUKE_WIDGET_EVENT_LEAVE -1031
+// Paint event
+#define PUKE_WIDGET_EVENT_PAINT -1032
+// cArg: int[0] = pos()->x() int[1] = pos->y()
+// int[2] = oldPos->x() int[3] = oldPos->y()
+#define PUKE_WIDGET_EVENT_MOVE -1033
+// cArg: int[0] = size()->width() int[1] = size()->height()
+// int[2] = oldSize()->width() int[3] = oldSize()->height()
+#define PUKE_WIDGET_EVENT_RESIZE -1034
+#define PUKE_WIDGET_EVENT_CREATE -1035
+#define PUKE_WIDGET_EVENT_DESTROY -1036
+#define PUKE_WIDGET_EVENT_SHOW -1037
+#define PUKE_WIDGET_EVENT_HIDE -1038
+#define PUKE_WIDGET_EVENT_CLOSE -1039
+
+// Desc: move widget
+// iWinId: widget id
+// iArg: new location, lower short is x, upper short is y
+// cArg: not define
+#define PUKE_WIDGET_MOVE 1050
+
+// Desc: move widget
+// iWinId: widget id
+// iArg: new location, lower short is x, upper short is y
+// cArg: not define
+#define PUKE_WIDGET_MOVE_ACK -1050
+
+// Desc: open and load library file
+// iWinid: not defined
+// iArg: type of widget
+// cArg: file name
+#define PUKE_WIDGET_LOAD 1055
+
+// Desc: ack the open library file
+// iWinid: not defined
+// iArg: widget number
+// cArg: not defined
+#define PUKE_WIDGET_LOAD_ACK -1055
+
+// Desc: unload library file
+// iWinid: not define
+// iArg: type of widget to unload
+// cArg: no define
+#define PUKE_WIDGET_UNLOAD 1060
+
+// Desc: ack open and load library file
+// iWinid: not defined
+// iArg: not defined
+// cArg: no defined
+#define PUKE_WIDGET_UNLOAD_ACK -1060
+
+// Desc: set's a a minimum size
+// iWinid: window id
+// iArg: 2 ints, lower is width , upper is height
+// cArg: not define
+#define PUKE_WIDGET_SETMINSIZE 1065
+
+// Desc: ack for set size
+// iWinid: window id
+// iArg: 2 ints, lower is new width, upper is new height
+// cArg: not defined
+#define PUKE_WIDGET_SETMINSIZE_ACK -1065
+
+// Desc: set's a a minimum size
+// iWinid: window id
+// iArg: 2 ints, lower is width , upper is height
+// cArg: not define
+#define PUKE_WIDGET_SETMAXSIZE 1070
+
+// Desc: ack for set size
+// iWinid: window id
+// iArg: 2 ints, lower is new width, upper is new height
+// cArg: not defined
+#define PUKE_WIDGET_SETMAXSIZE_ACK -1070
+
+// Desc: set widget caption
+// iWinid: window id
+// iArg: not defined
+// cArg: widget caption
+#define PUKE_WIDGET_SETCAPTION 1075
+
+// Desc: ack for set caption
+// iWinid: window id
+// iArg: not define
+// cArg: new caption
+#define PUKE_WIDGET_SETCAPTION_ACK -1075
+
+// Desc: get background colour
+// iWinid: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_GET_BACKGROUND_COLOUR 1080
+
+// Desc: get background colour ack
+// iWinid: window id
+// iArg: not defined
+// cArg: 3 ints packed in rbg combo
+#define PUKE_WIDGET_GET_BACKGROUND_COLOUR_ACK -1080
+
+// Desc: set background colour
+// iWinid: window id
+// iArg: not defined
+// cArg: 3 ints packed in rbg combo
+#define PUKE_WIDGET_SET_BACKGROUND_COLOUR 1085
+
+// Desc: set background colour ack
+// iWinid: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_SET_BACKGROUND_COLOUR_ACK -1085
+
+// Desc: set background pixmap
+// iWinid: window id
+// iArg: not defined
+// cArg: path name to pixmap
+#define PUKE_WIDGET_SET_BACKGROUND_PIXMAP 1086
+
+// Desc: set background pixmap ack
+// iWinid: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_SET_BACKGROUND_PIXMAP_ACK -1086
+
+// Desc: set background mode
+// iWinid: window id
+// iArg: mode
+// cArg: not defined
+#define PUKE_WIDGET_SET_BACKGROUND_MODE 1087
+
+// Desc: set background mode ack
+// iWinid: window id
+// iArg: new mode
+// cArg: not defined
+#define PUKE_WIDGET_SET_BACKGROUND_MODE_ACK -1087
+
+// Desc: set widget enabled or disabled
+// iWinid: window id
+// iArg: 0 for disabled, 1 for enabled
+// cArg: not defined
+#define PUKE_WIDGET_SET_ENABLED 1090
+
+// Desc: ack for enable/disable
+// iWinid: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_WIDGET_SET_ENABLED_ACK -1090
+
+// Desc: recreate widget with new parent, etc
+// IwinId: window id
+// iArg: window ID for new parent, 0x0 for no parent, toplevel
+// cArg: 3 packed ints, 0 = x, 1 = y, 2 = showit
+#define PUKE_WIDGET_RECREATE 1091
+
+// Desc: ACK recreate widget with new parent, etc
+// IwinId: window id
+// iArg: 0 (for all ok, other for error)
+// cArg: 0
+#define PUKE_WIDGET_RECREATE_ACK -1091
+
+// QFrame gets 1100
+
+// 1100 defines QFrame
+// Desc: set Frame style
+// iWinId: widget to chanse
+// iArg: frame style to set.
+// cArg: no define
+#define PUKE_QFRAME_SET_FRAME 1100
+
+// Desc: get/ack Frame style
+// iWinId: widget to changed
+// iArg: frame style.
+// cArg: no define
+#define PUKE_QFRAME_SET_FRAME_ACK -1100
+
+// Desc: set Frame line width
+// iWinId: widget to chanse
+// iArg: newline width.
+// cArg: no define
+#define PUKE_QFRAME_SET_LINEWIDTH 1105
+
+// Desc: get/ack Frame line width
+// iWinId: widget to changed
+// iArg: line width.
+// cArg: no define
+#define PUKE_QFRAME_SET_LINEWIDTH_ACK -1105
+
+//----------------------------------------------------
+// 1200 is a QLineEdit
+
+// Desc: set max line length
+// iWinId: widget to change
+// iArg: max line length
+// cArg: not defined
+#define PUKE_LINED_SET_MAXLENGTH 1200
+
+// Desc: set max line length
+// iWinId: widget to change
+// iArg: new max line length
+// cArg: not defined
+#define PUKE_LINED_SET_MAXLENGTH_ACK -1200
+
+// Desc: set echo mode, normal password, no echo, etc
+// iWinId: widget to change
+// iArg: echo mode
+// cArg: not defined
+#define PUKE_LINED_SET_ECHOMODE 1205
+
+// Desc: ack for set
+// iWinId: widget to change
+// iArg: current echo mode
+// cArg: not define
+#define PUKE_LINED_SET_ECHOMODE_ACK -1205
+
+// Desc: set text contents of widget
+// iWinId: widget to change
+// iArg: not defined
+// cArg: text to set widget too
+#define PUKE_LINED_SET_TEXT 1210
+
+// Desc: ack for set
+// iWinId: widget to change
+// iArg: not define
+// cArg: current test
+#define PUKE_LINED_SET_TEXT_ACK -1210
+
+// Desc: get current text
+// iWinId: widget to change
+// iArg:not define
+// cArg: not defined
+#define PUKE_LINED_GET_TEXT 1215
+
+// Desc: returned info for get text
+// iWinId: widget to change
+// iArg: not defined
+// cArg: text in widget
+#define PUKE_LINED_GET_TEXT_ACK -1215
+
+// Desc: return was pressed, action not defined
+// iWinId:
+// iArg:
+// cArg:
+#define PUKE_LINED_RETURN_PRESSED 1220
+
+// Desc: return was pressed, return information
+// iWinId: widget pressed in
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LINED_RETURN_PRESSED_ACK -1220
+
+//------------------------------------------------
+
+// Desc: set's button's current text
+// iWinId: window id
+// iArg: not defined
+// cArg: text
+#define PUKE_BUTTON_SET_TEXT 1300
+
+// Desc: ack for set text
+// iWinId: widget id
+// iArg: not defined
+// cArg: text
+#define PUKE_BUTTON_SET_TEXT_ACK -1300
+
+// Desc: set button to pixmap
+// iWinId: window id
+// iArg: not defined
+// cArg: path to pixmap
+#define PUKE_BUTTON_SET_PIXMAP 1305
+
+// Desc: ack for set pixmap
+// iWinId: widget id
+// iArg: pixmap()->isNull()
+// cArg: not defined
+#define PUKE_BUTTON_SET_PIXMAP_ACK -1305
+
+// Desc: set if button resizes with content changes
+// iWinId: window id
+// iArg: reisze, 0 false, 1 true
+// cArg: not defined
+#define PUKE_BUTTON_SET_AUTORESIZE 1310
+
+// Desc: ack for autreisze
+// iWinId: widget id
+// iArg: autoresize()
+// cArg: not defined
+#define PUKE_BUTTON_SET_AUTORESIZE_ACK -1310
+
+// Desc: button was pressed, not used
+// iWinId: not defined
+// iArg: not defined
+// cArg: not define
+#define PUKE_BUTTON_PRESSED 1350
+
+// Desc: signal button was pressed
+// iWinId: winid
+// iArg: not define
+// cArg: not defined
+#define PUKE_BUTTON_PRESSED_ACK -1350
+
+// Desc: button was relased, not used
+// iWinId: not defined
+// iArg: not defined
+// cArg: not define
+#define PUKE_BUTTON_RELEASED 1351
+
+// Desc: signal button was released
+// iWinId: winid
+// iArg: not define
+// cArg: not defined
+#define PUKE_BUTTON_RELEASED_ACK -1351
+
+// Desc: button was clicked, not used
+// iWinId: not defined
+// iArg: not defined
+// cArg: not define
+#define PUKE_BUTTON_CLICKED 1352
+
+// Desc: signal button was clicked
+// iWinId: winid
+// iArg: not defined
+// cArg: not defined
+#define PUKE_BUTTON_CLICKED_ACK -1352
+
+// Desc: button was toggled, not used
+// iWinId: not defined
+// iArg: not defined
+// cArg: not define
+#define PUKE_BUTTON_TOGGLED 1353
+
+// Desc: signal button was toggled
+// iWinId: winid
+// iArg: not define
+// cArg: not defined
+#define PUKE_BUTTON_TOGGLED_ACK -1353
+
+
+//------------------------------------------------
+
+// Desc: set's the range of the control
+// iWinId: window id
+// iArg: two packed short ints. Lower is lower, upper is upper
+// cArg: not define
+#define PUKE_KSPROGRESS_SET_RANGE 1400
+
+// Desc: ack set's the range of the control
+// iWinId: window id
+// iArg: not define
+// cArg: not defined
+#define PUKE_KSPROGRESS_SET_RANGE_ACK -1400
+
+// Desc: set the top text line
+// iWinId: window id
+// iArg: not define
+// cArg: top line
+#define PUKE_KSPROGRESS_SET_TOPTEXT 1410
+
+// Desc: ack set the top text line
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_KSPROGRESS_SET_TOPTEXT_ACK -1410
+
+// Desc: set the bottom text line
+// iWinId: window id
+// iArg: not define
+// cArg: bottom line
+#define PUKE_KSPROGRESS_SET_BOTTEXT 1415
+
+// Desc: ack set the top text line
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_KSPROGRESS_SET_BOTTEXT_ACK -1415
+
+// Desc: set status value
+// iWinId: window id
+// iArg: not define
+// cArg: current value
+#define PUKE_KSPROGRESS_SET_VALUE 1420
+
+// Desc: ack set the top text line
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_KSPROGRESS_SET_VALUE_ACK -1420
+
+
+// Desc: cancel pressed, typically only ack sent.
+// iWinId: window id
+// iArg: not define
+// cArg: current value
+#define PUKE_KSPROGRESS_CANCEL 1425
+
+// Desc: send message saying cancel button was pressed
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_KSPROGRESS_CANCEL_ACK -1425
+
+// QTable View at 1500
+
+// none yet
+
+// QListBox at 1600
+
+// Desc: insert string item
+// iWinId: window id
+// iArg: number of items now in list
+// cArg: undef
+#define PUKE_LISTBOX_INSERT 1600
+
+// Desc: insert string item ack
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_INSERT_ACK -1600
+
+
+// Desc: insert string item in sorted order
+// iWinId: window id
+// iArg: number of items in list
+// cArg: undef
+#define PUKE_LISTBOX_INSERT_SORT 1605
+
+// Desc: ack insert string item in sorted order
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_INSERT_SORT_ACK -1605
+
+// Desc: insert pixmap
+// iWinId: window id
+// iArg: not defined
+// cArg: file name
+#define PUKE_LISTBOX_INSERT_PIXMAP 1610
+
+// Desc: ack insert pixmap
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_INSERT_PIXMAP_ACK -1610
+
+// Desc: highligth number
+// iWinId: window id
+// iArg: item index to highlight
+// cArg: undef
+#define PUKE_LISTBOX_HIGHLIGHT 1615
+
+// Desc: ack
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_HIGHLIGHT_ACK -1615
+
+// Desc: remove number
+// iWinId: window id
+// iArg: item index to remove
+// cArg: undef
+#define PUKE_LISTBOX_REMOVE 1620
+
+// Desc: ack
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_REMOVE_ACK -1620
+
+// Desc: Get text from item number
+// iWinId: window id
+// iArg: list item
+// cArg: not defined
+#define PUKE_LISTBOX_GETTEXT 1625
+
+// Desc: ack for get text with cArg set to text
+// iWinId: window id
+// iArg: 1 for sucsess, 0 for failure
+// cArg: text
+#define PUKE_LISTBOX_GETTEXT_ACK -1625
+
+// Desc: Clear list box
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_CLEAR 1630
+
+// Desc: ack for clear list box
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_CLEAR_ACK -1630
+
+// Desc: set scroll bar
+// iWinId: window id
+// iArg: bool, enable/disable
+// cArg: undef
+#define PUKE_LISTBOX_SET_SCROLLBAR 1635
+
+// Desc: set scroll bar ack
+// iWinId: window id
+// iArg: undef
+// cArg: undef
+#define PUKE_LISTBOX_SET_SCROLLBAR_ACK -1635
+
+// Desc: set auto scroll bar
+// iWinId: window id
+// iArg: bool, enable/disable
+// cArg: undef
+#define PUKE_LISTBOX_SET_AUTO_SCROLLBAR 1636
+
+// Desc: set auto scroll bar ack
+// iWinId: window id
+// iArg: undef
+// cArg: undef
+#define PUKE_LISTBOX_SET_AUTO_SCROLLBAR_ACK -1636
+
+// Desc: item highlighted, not used signal to dsirc
+// iWinId: undef
+// iArg: undef
+// cArg: undef
+#define PUKE_LISTBOX_HIGHLIGHTED 1690
+
+// Desc: item highlighted
+// iWinId: window id
+// iArg: index
+// cArg: contents
+#define PUKE_LISTBOX_HIGHLIGHTED_ACK -1690
+
+// Desc: item selected
+// iWinId: window id
+// iArg: index
+// cArg: contents
+#define PUKE_LISTBOX_SELECTED 1690
+
+// Desc: send message saying cancel button was pressed
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LISTBOX_SELECTED_ACK -1690
+
+// *** 1700 goes to the Plabel
+
+// Desc: Set label to text in carg
+// iWinId: window id
+// iArg: not defined
+// cArg: text to be set to
+#define PUKE_LABEL_SETTEXT 1700
+
+// Desc: Set label to text in carg, ack
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LABEL_SETTEXT_ACK -1700
+
+// Desc: Set label pixmap specified by filane name
+// iWinId: window id
+// iArg: not defined
+// cArg: filena,me for pixmap
+#define PUKE_LABEL_SETPIXMAP 1705
+
+// Desc: Set label pixmap specified by filane name ACK
+// iWinId: window id
+// iArg: not defined
+// cArg: not defined
+#define PUKE_LABEL_SETPIXMAP_ACK -1705
+
+// Desc: Set label to movie specified by filename
+// iWinId: window id
+// iArg: not defined
+// cArg: movie's filename
+#define PUKE_LABEL_SETMOVIE 1710
+
+// Desc: Set label to movie specified by filename, ACK
+// iWinId: window id
+// iArg: not defined
+// cArg: not deefine
+#define PUKE_LABEL_SETMOVIE_ACK -1710
+
+// Desc: Set label's allignment
+// iWinId: window id
+// iArg: alignment
+// cArg: not defined
+#define PUKE_LABEL_SETALIGNMENT 1715
+
+// Desc: Set label's allignment, ACK
+// iWinId: window id
+// iArg: alignment
+// cArg: not defined
+#define PUKE_LABEL_SETALIGNMENT_ACK -1715
+
+// -----------------------------------------------------------------------
+// PMenuData
+
+// Desc: Inserts newtext menu item
+// iWinId: window id
+// iArg: keyboard accelerator (-1 for none)
+// cArg: Text
+#define PUKE_MENUDATA_INSERT_TEXT 1800
+
+// Desc: ack for insert
+// iWinId: window id
+// iArg: object id
+// cArg: undef
+#define PUKE_MENUDATA_INSERT_TEXT_ACK -1800
+
+// Desc: Inserts new pixmap into menu
+// iWinId: window id
+// iArg: keyboard accelerator (-1 for none)
+// cArg: Text
+#define PUKE_MENUDATA_INSERT_PIXMAP 1801
+
+// Desc: ack for insert
+// iWinId: window id
+// iArg: object id
+// cArg: undef
+#define PUKE_MENUDATA_INSERT_PIXMAP_ACK -1801
+
+
+// Desc: Item got activated, not used, only ack
+// iWinId: undef
+// iArg: undef
+// cArg: uidef
+#define PUKE_POPUPMENU_ACTIVATED 1805
+
+// Desc: Item got activate
+// iWinId: window id
+// iArg: item id
+// cArg: undef
+#define PUKE_POPUPMENU_ACTIVATED_ACK -1805
+
+// Desc: Popup to current cursor position
+// iWinId: menu id
+// iArg: undef
+// cArg: uidef
+#define PUKE_POPUPMENU_POPUP_CURRENT 1810
+
+// Desc: Ack for popup
+// iWinId: window id
+// iArg: 1 for success
+// cArg: undef
+#define PUKE_POPUPMENU_POPUP_CURRENT_ACK -1810
+
+// Desc: Remvoes an item from a popup menu
+// iWinId: menu id
+// iArg: undef
+// cArg: uidef
+#define PUKE_MENUDATA_REMOVE_ITEM 1815
+
+// Desc: Ack for remove item
+// iWinId: window id
+// iArg: 1 for success
+// cArg: undef
+#define PUKE_MENUDATA_REMOVE_ITEM_ACK -1815
+
+// -----------------------------------------------------------------------
+// PAMenuData
+
+// Desc: is the item in the top of the list?
+// iWinId: window id
+// iArg: index
+// cArg: not defined
+#define PUKE_ALISTBOX_ISTOP 1900
+
+// Desc: ack for istop
+// iWinId: window id
+// iArg: 1 for top, 0 for bottom
+// cArg: not defined
+#define PUKE_ALISTBOX_ISTOP_ACK -1900
+
+// Desc: set green highligh (voice)
+// iWinId: window id
+// iArg: highligh, 1 for set, 0 for not
+// cArg: string
+#define PUKE_ALISTBOX_SMALL_HIGHLIGHT 1905
+
+// Desc: ack for set green highligh
+// iWinId: window id
+// iArg: 0 for success, 1 for failure
+// cArg: not defined
+#define PUKE_ALISTBOX_SMALL_HIGHLIGHT_ACK -1905
+
+// Desc: set red highligh and raise to the top (op)
+// iWinId: window id
+// iArg: highlight, 1 for set, 0 for remove
+// cArg: string
+#define PUKE_ALISTBOX_BIG_HIGHLIGHT 1910
+
+// Desc: ack for set red and raise
+// iWinId: window id
+// iArg: 0 for success, 1 for failure
+// cArg: not defined
+#define PUKE_ALISTBOX_BIG_HIGHLIGHT_ACK -1910
+
+// Desc: finds nick index
+// iWinId: window id
+// iArg: not defined
+// cArg: string
+#define PUKE_ALISTBOX_FIND_NICK 1915
+
+// Desc: ack for find nick
+// iWinId: window id
+// iArg: index
+// cArg: not defined
+#define PUKE_ALISTBOX_FIND_NICK_ACK -1915
+
+// -----------------------------------------------------------------------
+// PTabDialog
+
+// Desc: add Widget defined by iArg's widget id
+// iWinId: window id
+// iArg: widget to add, widget id, in current fd
+// cArg: not defined
+#define PUKE_TABDIALOG_ADDTAB 2001
+
+// Desc: ack for istop
+// iWinId: window id
+// iArg: 1 for failure, 0 for success
+// cArg: not defined
+#define PUKE_TABDIALOG_ADDTAB_ACK -2001
+
+// -----------------------------------------------------------------------
+// PKSircListBox
+
+// Desc: is the item in the top of the list?
+// iWinId: window id
+// iArg: index
+// cArg: not defined
+#define PUKE_KSIRCLISTBOX_TOBOTTOM 2100
+
+// Desc: ack for istop
+// iWinId: window id
+// iArg: 1 for top, 0 for bottom
+// cArg: not defined
+#define PUKE_KSIRCLISTBOX_TOBOTTOM_ACK -2100
+
+// 2200 for PKFileDialog
+// 2300 for PObjFinder
+
+
+// ***********************************************************************
+// ***********************************************************************
+
+//
+// Base commands are done, next describes Widget's
+//
+
+// OBJECT base class
+#define PWIDGET_OBJECT 1
+
+// WIDGET defines a base QWidget class
+#define PWIDGET_WIDGET 2
+
+// FRAME defines a base class
+#define PWIDGET_FRAME 3
+
+// LINED defines the simple SLE
+#define PWIDGET_LINED 4
+
+// BUTTON defines the abstract class QButton
+#define PWIDGET_BUTTON 5
+
+// PUSH defined the push button
+#define PWIDGET_PUSHBT 6
+
+// Progress window for dcc and stuff
+#define PWIDGET_KSPROGRESS 7
+
+// Table view, not really used for much, base class for lots
+#define PWIDGET_TABLEVW 8
+
+// List box
+#define PWIDGET_LISTBOX 9
+
+// Label
+#define PWIDGET_LABEL 10
+
+// BoxLayout
+#define POBJECT_LAYOUT 11
+
+// MenuData
+#define PWIDGET_MENUDATA 12
+
+// PopupMenu
+
+#define PWIDGET_POPMENU 13
+
+// PAListBox
+
+#define PWIDGET_ALISTBOX 14
+
+// PKSircListBox
+
+#define PWIDGET_KSIRCLISTBOX 15
+
+// PTabDialog
+
+#define PWIDGET_TABDIALOG 16
+
+// PKFileDialog
+
+#define PWIDGET_KFILEDIALOG 17
+
+// PObjFinder
+
+#define PWIDGET_OBJFINDER 18
+
+
+// Group layout commands exist between 10000 and 11000
+
+// Desc: create new box layout
+// iWinId: PWidget parent
+// iArg: 2 shorts, short[0] direction, short[1] border
+// cArg: random character string
+#define PUKE_LAYOUT_NEW 11000
+// Desc: ack for box layout
+// iWinId: Layout ID.
+// iArg: not define
+// cArg: same random m character string as PUKE_LAYOUT_NEW
+#define PUKE_LAYOUT_NEW_ACK -11000
+
+#define PUKE_LAYOUT_ADDLAYOUT 11005
+#define PUKE_LAYOUT_ADDLAYOUT_ACK -11005
+
+// Desc: add widget into layout manager
+// iWinId: Layout Manager to add widget too
+// iArg: Widget Id to be added
+// cArg: 2 characters, char[0] strech, char[1] alignment
+#define PUKE_LAYOUT_ADDWIDGET 11010
+// Desc: ack for add widget
+// iWinId: Layout manager
+// iArg: not defined
+// cArg: not define
+#define PUKE_LAYOUT_ADDWIDGET_ACK -11010
+
+// Desc: adds a strut for the current box, ie can'be be bigger then int
+// iWinId: Layout Manager to add strut
+// iArg: strut size
+// cArg: not define
+#define PUKE_LAYOUT_ADDSTRUT 11015
+
+// Desc: ack for strut add widget
+// iWinId: Layout manager
+// iArg: not defined
+// cArg: not define
+#define PUKE_LAYOUT_ADDSTRUT_ACK -11015
+
+// Desc: activates layout management, like show() for widget
+// iWinId: Layout Manager to activate
+// iArg: undef
+// cArg: undef
+#define PUKE_LAYOUT_ACTIVATE 11020
+
+// Desc: ack for strut add widget
+// iWinId: Layout manager
+// iArg: 1 on failure, 0 on success
+// cArg: not define
+#define PUKE_LAYOUT_ACTIVATE_ACK -11020
+
+/*
+ * ----------------------------------------------------------------------
+ * persistant objects
+ */
+
+#define PUKE_CONTROLLER 1
diff --git a/ksirc/puke/controller.cpp b/ksirc/puke/controller.cpp
new file mode 100644
index 00000000..1e798be7
--- /dev/null
+++ b/ksirc/puke/controller.cpp
@@ -0,0 +1,974 @@
+#include <config.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <iostream>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+
+using namespace std; // iostream.h include cstring which puts strlen into
+ // std:: namespace, which breaks Qt's strlen() call
+ // in qcstring.h (in gcc3's libstdc++)
+
+#include <klibloader.h>
+#include <kdebug.h>
+
+
+#include "controller.h"
+
+#include "../../config.h"
+#include "../objFinder.h"
+
+#include "palistbox.h"
+#include "pbutton.h"
+#include "pframe.h"
+#include "pkfiledialog.h"
+#include "plabel.h"
+#include "playout.h"
+#include "plined.h"
+#include "plistbox.h"
+#include "pmenudta.h"
+#include "pmessage.h"
+#include "pobject.h"
+#include "pobjfinder.h"
+#include "ppopmenu.h"
+#include "pprogress.h"
+#include "ppushbt.h"
+#include "ptabdialog.h"
+#include "ptablevw.h"
+#include "pwidget.h"
+
+#undef DEBUG
+
+uint PukeController::uiBaseWinId = 10; // Gives a little seperation from the controller id
+
+PukeController::PukeController(QString sock, QObject *parent, const char *name) : PObject( parent, name )
+{
+ int len, prev_umask;
+ struct sockaddr_un unix_addr;
+
+ running = FALSE; // Running has to be true before we do any work
+ bClosing = FALSE; // we're not trying to close so set this false.
+
+ // Set the umask to something sane that doesn't allow others to take over ksirc
+ prev_umask = umask(0177);
+
+ if(sock.length() == 0){
+ qsPukeSocket = getenv("HOME");
+ if(qsPukeSocket.length() == 0){
+ qsPukeSocket = "/tmp";
+ }
+ qsPukeSocket += "/.ksirc.socket";
+ }
+ else{
+ qsPukeSocket = sock;
+ }
+
+ unlink(qsPukeSocket);
+ iListenFd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(iListenFd < 0){
+ perror("PUKE: Unix Domain Socket create failed");
+ return;
+ }
+ memset(&unix_addr, 0, sizeof(unix_addr));
+ unix_addr.sun_family = AF_UNIX;
+ strlcpy(unix_addr.sun_path, qsPukeSocket, sizeof(unix_addr.sun_path));
+
+ len = sizeof(unix_addr.sun_family) + qsPukeSocket.length();
+#if defined(__FreeBSD__)
+ if( bind(iListenFd, (struct sockaddr *) &unix_addr, len+1) < 0){
+#else
+ if( bind(iListenFd, (struct sockaddr *) &unix_addr, len) < 0){
+#endif
+ perror("PUKE: Could not bind to Unix Domain Socket");
+ return;
+ }
+
+ if(listen(iListenFd, 5) < 0){
+ perror("PUKE: Could not listen for inbound connections");
+ return;
+ }
+
+ running = TRUE;
+
+ fcntl(iListenFd, F_SETFL, O_NONBLOCK); // Set it non-block so that
+ // accept() never blocks.
+
+ qsnListen = new QSocketNotifier(iListenFd, QSocketNotifier::Read, this, QString(name) + "_iListen");
+ connect(qsnListen, SIGNAL(activated(int)),
+ this, SLOT(NewConnect(int)));
+
+ connect(objFind, SIGNAL(inserted(QObject *)),
+ this, SLOT(slotInserted(QObject *)));
+
+ qidConnectFd.setAutoDelete(TRUE);
+
+ qidCommandTable.setAutoDelete(TRUE);
+
+ /*
+ * Setup widget data trees
+ */
+ WidgetList.setAutoDelete(TRUE);
+ revWidgetList.setAutoDelete(TRUE);
+ widgetCF.setAutoDelete(TRUE);
+
+ /*
+ * Connect outputMessage to the acutal write buffer function
+ * outputMessage signals from pobjects are chained until they finally reach us.
+ */
+ connect(this, SIGNAL(outputMessage(int, PukeMessage *)),
+ this, SLOT(writeBuffer(int, PukeMessage *)));
+
+ initHdlr(); // Setup message command handlers.
+
+ // Set umask back so it doesn't affect dcc's and so forth.
+ umask(prev_umask);
+
+ /*
+ * We are a PObject so do some init code
+ */
+ // We're always terminate by someone else so set manTerm() right now
+ manTerm();
+ setWidget(0x0);
+
+}
+
+void
+PukeController::slotInserted(QObject *obj)
+{
+ emit inserted(obj);
+}
+
+PukeController::~PukeController()
+{
+ close(iListenFd);
+ disconnect(this); // We call disconnect this so don't listen to our own destroy() signal go out
+ unlink(qsPukeSocket);
+}
+
+QStrList PukeController::allObjects()
+{
+ return objFinder::allObjects();
+}
+
+void PukeController::NewConnect(int)
+{
+ int cfd;
+ ksize_t len = 0;
+ struct sockaddr_un unix_addr;
+
+ cfd = accept(iListenFd, (struct sockaddr *)&unix_addr, &len);
+ if(cfd < 0){
+ perror("PUKE: NewConnect fired, but no new connect");
+ return;
+ }
+ fcntl(cfd, F_SETFL, O_NONBLOCK); // Set it non-block so that
+ // cfd() never blocks.
+
+ fdStatus *fds = new fdStatus();
+ fds->sr = new QSocketNotifier(cfd, QSocketNotifier::Read, this);
+ fds->sw = new QSocketNotifier(cfd, QSocketNotifier::Write, this);
+ connect(fds->sr, SIGNAL(activated(int)),
+ this, SLOT(Traffic(int)));
+ connect(fds->sw, SIGNAL(activated(int)),
+ this, SLOT(Writeable(int)));
+ qidConnectFd.insert(cfd, fds);
+ qsnListen->setEnabled(TRUE);
+
+ /*
+ * Now we add ourselves as a client to the fd so we can process messages going to us
+ */
+ WidgetS *ws = new WidgetS;
+ ws->pwidget = this;
+ ws->type = 1;
+ insertPObject(cfd, ControllerWinId, ws);
+
+}
+
+
+void PukeController::Writeable(int fd)
+{
+ if(qidConnectFd[fd]){
+ qidConnectFd[fd]->writeable = TRUE;
+ qidConnectFd[fd]->sw->setEnabled(FALSE);
+ //
+ // Insert buffer flushing code here.
+ //
+ }
+ else{
+ kdDebug(5008) << "PUKE: Unkonwn fd: " << fd << endl;
+ }
+}
+
+void PukeController::writeBuffer(int fd, PukeMessage *message)
+{
+ if(qidConnectFd[fd]){
+ // if(qidConnectFd[fd]->writeable == FALSE){
+ // kdDebug(5008) << "PUKE: Writing to FD that's not writeable: " << fd << endl;
+ // }
+ if(message != 0){
+ int bytes = 0;
+ message->iHeader = iPukeHeader;
+ if(message->iTextSize == 0 || message->cArg == 0){
+ message->iTextSize = 0;
+ message->cArg = 0;
+#ifdef DEBUG
+ printf("Traffic on: %d <= %d %d %d %d 0x%x\n",
+ fd,
+ message->iCommand,
+ message->iWinId,
+ message->iArg,
+ message->iTextSize,
+ message->cArg);
+#endif
+ bytes = write(fd, message, 5 * sizeof(int));
+ }
+ else{
+ /*
+ struct OutMessageS {
+ unsigned int iHeader;
+ int iCommand;
+ int iWinId;
+ int iArg;
+ int iTextSize;
+ char cArg[message->iTextSize];
+ } OutMessage;
+ OutMessage.iHeader = iPukeHeader;
+ OutMessage.iCommand = message->iCommand;
+ OutMessage.iWinId = message->iWinId;
+ OutMessage.iArg = message->iArg;
+ OutMessage.iTextSize = message->iTextSize;
+ memcpy(OutMessage.cArg, message->cArg, OutMessage.iTextSize);
+ // OutMessage.cArg[OutMessage.iTextSize] = 0; // Don't need to null out the last character
+ bytes = write(fd, &OutMessage, 5*sizeof(int) + (OutMessage.iTextSize) * sizeof(char));
+ */
+#ifdef DEBUG
+ printf("Traffic on: %d <= %d %d %d %d 0x%x\n",
+ fd,
+ message->iCommand,
+ message->iWinId,
+ message->iArg,
+ message->iTextSize,
+ message->cArg);
+#endif /* DEBUG */
+
+ struct iovec iov[2];
+ iov[0].iov_base = (char *) message;
+ iov[0].iov_len = 5*sizeof(int);
+ iov[1].iov_base = (char *) message->cArg;
+ iov[1].iov_len = message->iTextSize;
+ bytes = writev(fd, iov, 2);
+ }
+ // kdDebug(5008) << "Wrote: " << bytes << endl;
+ if(bytes <= 0){
+ switch(errno){
+ case EAGAIN: // Don't do anything for try again
+ break;
+// default:
+// perror("Puke: write on socket failed");
+ // Don't call closefd() since deletes are called on write's
+ // since write is being called from the destructors, etc of
+ // the widgets. (bad things happend when you call write
+ // then your return; path ceasaes to exist.
+ // closefd(fd);
+ }
+ }
+ }
+ }
+ else{
+ closefd(fd);
+ kdDebug(5008) << "PUKE: Attempt to write to unkown fd:" << fd << endl;
+ }
+}
+
+void PukeController::Traffic(int fd)
+{
+ PukeMessage pm;
+ int bytes = -1;
+ memset(&pm, 0, sizeof(pm));
+ while((bytes = read(fd, &pm, 5*sizeof(int))) > 0){
+ if(bytes != 5*sizeof(int)){
+ kdDebug(5008) << "Short message, Got: " << bytes << " Wanted: " << sizeof(PukeMessage) << " NULL Padded" << endl;
+ }
+#ifdef DEBUG
+ printf("Traffic on: %d => %d %d %d %d",
+ fd,
+ pm.iCommand,
+ pm.iWinId,
+ pm.iArg,
+ pm.iTextSize);
+ if(pm.iCommand == 0x0){
+ abort();
+ }
+#endif /* DEBUG */
+ if(pm.iHeader != iPukeHeader){
+ qWarning("Invalid packet received, discarding!");
+ return;
+ }
+ if(pm.iTextSize > 0){
+ pm.cArg = new char[pm.iTextSize + 1];
+ read(fd, pm.cArg, pm.iTextSize * sizeof(char));
+ pm.cArg[pm.iTextSize] = 0x0; // Null terminate the string.
+// printf(" %s\n", pm.cArg);
+ }
+ else {
+ pm.cArg = 0;
+// printf("\n");
+ }
+ MessageDispatch(fd, &pm);
+ delete[] pm.cArg; // Free up cArg is used
+ memset(&pm, 0, 5*sizeof(int));
+ }
+ if(bytes <= 0){ // Shutdown the socket!
+ switch(errno){
+ case EAGAIN: // Don't do anything for try again
+ break;
+ // case 0:
+ // break; // We just read nothing, don't panic
+ case EIO:
+ case EISDIR:
+ case EBADF:
+ case EINVAL:
+ case EFAULT:
+ default:
+ // perror("PukeController: read failed");
+ closefd(fd);
+ close(fd);
+ }
+ }
+ else{
+ qidConnectFd[fd]->sr->setEnabled(TRUE);
+ }
+}
+
+
+void PukeController::ServMessage(QString, int, QString)
+{
+
+}
+
+// Message Dispatcher is in messagedispatcher.cpp
+
+
+void PukeController::MessageDispatch(int fd, PukeMessage *pm)
+{
+ try {
+
+ /*
+ * Get the object id, this may produce a errorNuSuchWidget
+ */
+ PObject *obj = id2pobject(fd, pm->iWinId);
+
+ /*
+ * Call the message hanlder for the widget
+ */
+ obj->messageHandler(fd, pm);
+ }
+ catch(errorNoSuchWidget &err){
+ PukeMessage pmRet;
+ pmRet.iCommand = PUKE_INVALID;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+ catch (errorCommandFailed &err){
+ PukeMessage pmRet;
+ pmRet.iCommand = err.command();
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = err.iarg();
+ pmRet.iTextSize = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+}
+
+void PukeController::initHdlr()
+{
+
+ widgetCreate *wc;
+
+ wc = new widgetCreate;
+ wc->wc = PWidget::createWidget;
+ widgetCF.insert(PWIDGET_WIDGET, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PObject::createWidget;
+ widgetCF.insert(PWIDGET_OBJECT, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PLayout::createWidget;
+ widgetCF.insert(POBJECT_LAYOUT, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PFrame::createWidget;
+ widgetCF.insert(PWIDGET_FRAME, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PLineEdit::createWidget;
+ widgetCF.insert(PWIDGET_LINED, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PButton::createWidget;
+ widgetCF.insert(PWIDGET_BUTTON, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PPushButton::createWidget;
+ widgetCF.insert(PWIDGET_PUSHBT, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PProgress::createWidget;
+ widgetCF.insert(PWIDGET_KSPROGRESS, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PTableView::createWidget;
+ widgetCF.insert(PWIDGET_TABLEVW, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PListBox::createWidget;
+ widgetCF.insert(PWIDGET_LISTBOX, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PLabel::createWidget;
+ widgetCF.insert(PWIDGET_LABEL, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PPopupMenu::createWidget;
+ widgetCF.insert(PWIDGET_POPMENU, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PAListBox::createWidget;
+ widgetCF.insert(PWIDGET_ALISTBOX, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PTabDialog::createWidget;
+ widgetCF.insert(PWIDGET_TABDIALOG, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PKFileDialog::createWidget;
+ widgetCF.insert(PWIDGET_KFILEDIALOG, wc);
+
+ wc = new widgetCreate;
+ wc->wc = PObjFinder::createWidget;
+ widgetCF.insert(PWIDGET_OBJFINDER, wc);
+
+ // Each function handler gets an entry in the qidCommandTable
+ commandStruct *cs;
+
+
+ // Invalid is the default invalid handler
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeInvalid;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_INVALID, cs);
+
+
+ // Setup's handled by the setup handler
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeSetup;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_SETUP, cs);
+
+ // We don't receive PUKE_SETUP_ACK's we send them.
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeInvalid;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_SETUP_ACK, cs);
+
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeEcho;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_ECHO, cs);
+
+ // Fetch widget gets the requested widget from the ServerController
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeFetchWidget;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_FETCHWIDGET, cs);
+
+ // Fetch widget gets the requested widget from the ServerController
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeDumpTree;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_DUMPTREE, cs);
+
+ // Fetch widget gets the requested widget from the ServerController
+ cs = new commandStruct;
+ cs->cmd = &PukeController::hdlrPukeDeleteWidget;
+ cs->library = 0;
+ qidCommandTable.insert(PUKE_WIDGET_DELETE, cs);
+
+}
+
+// Start message handlers
+
+void PukeController::hdlrPukeInvalid(int fd, PukeMessage *)
+{
+ PukeMessage pmOut;
+ memset(&pmOut, 0, sizeof(pmOut));
+ this->writeBuffer(fd, &pmOut);
+}
+
+
+void PukeController::hdlrPukeSetup(int fd, PukeMessage *pm)
+{
+ PukeMessage pmOut;
+ memset(&pmOut, 0, sizeof(pmOut));
+ pmOut.iCommand = PUKE_SETUP_ACK;
+ pmOut.iArg = 1;
+ if((strlen(pm->cArg) > 0) &&
+ (this->qidConnectFd[fd] != NULL)){
+ this->qidConnectFd[fd]->server = qstrdup(pm->cArg);
+ pmOut.iWinId = pm->iWinId;
+ pmOut.iArg = sizeof(PukeMessage) - sizeof(char *);
+ }
+ this->writeBuffer(fd, &pmOut);
+}
+
+void PukeController::hdlrPukeEcho(int fd, PukeMessage *pm)
+{
+ PukeMessage pmOut;
+ memcpy(&pmOut, pm, sizeof(PukeMessage));
+ pmOut.iCommand = PUKE_ECHO_ACK;
+ pmOut.iWinId = pm->iWinId;
+ pmOut.iArg = pm->iArg;
+ this->writeBuffer(fd, &pmOut);
+}
+
+void PukeController::hdlrPukeDumpTree(int fd, PukeMessage *pm)
+{
+ objFinder::dumpTree();
+
+ PukeMessage pmOut;
+ memcpy(&pmOut, pm, sizeof(PukeMessage));
+ pmOut.iCommand = -pm->iCommand;
+ pmOut.iWinId = pm->iWinId;
+ pmOut.iArg = pm->iArg;
+ this->writeBuffer(fd, &pmOut);
+}
+
+
+void PukeController::hdlrPukeFetchWidget(int fd, PukeMessage *pm)
+{
+ widgetId wIret;
+
+ /*
+ * The parent widget ID and type are packed into the iArg
+ * the pattern is 2 shorts.
+ */
+
+ int iParent=-1, iType=-1;
+
+ char rand[50],name[50];
+ int found = sscanf(pm->cArg, "%d\t%d\t%49s\t%49s", &iParent, &iType, rand, name);
+ if(found != 4){
+ throw(errorCommandFailed(PUKE_INVALID,6));
+ }
+
+ uiBaseWinId++; // Get a new base win id
+
+ // wIret holds the current widget id for the new widget
+ wIret.iWinId = uiBaseWinId;
+ wIret.fd = fd;
+
+ // CreateArgs arg = CreateArgs(this, pm, &wIret, parent)
+ CreateArgs arg(this, pm, &wIret, 0);
+
+ // Let's go looking for the widget
+ // Match any class with the right name
+ QObject *obj = 0x0;
+ if(parent() && (strcmp(name, parent()->name()) == 0)){
+ obj = parent();
+ }
+ else {
+ obj = objFinder::find(name, 0x0);
+ if(obj == 0){
+ wIret.fd = 0;
+ wIret.iWinId = 0;
+ throw(errorCommandFailed(PUKE_INVALID,5));
+ }
+
+ }
+
+ arg.fetchedObj = obj;
+
+ WidgetS *ws = new WidgetS;
+ ws->pwidget = (widgetCF[iType]->wc)(arg);
+ if (ws->pwidget->hasError())
+ {
+ throw(errorCommandFailed(PUKE_INVALID, 0));
+ }
+ ws->type = iType;
+
+ connect(ws->pwidget, SIGNAL(outputMessage(int, PukeMessage*)),
+ this, SIGNAL(outputMessage(int, PukeMessage*)));
+
+ // insertPBoject(fd, uiBaseWinId, ws);
+ // The widget list has to exist since we have ourselves in the list
+ // WidgetList[wIret.fd]->insert(wIret.iWinId, ws);
+ insertPObject(wIret.fd, wIret.iWinId, ws);
+
+ PukeMessage pmRet;
+ pmRet.iCommand = PUKE_WIDGET_CREATE_ACK;
+ pmRet.iWinId = wIret.iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = pm->iTextSize;
+ pmRet.cArg = pm->cArg;
+ emit outputMessage(fd, &pmRet);
+
+}
+
+void PukeController::hdlrPukeDeleteWidget(int fd, PukeMessage *pm)
+{
+ widgetId wI;
+ wI.fd = fd;
+ wI.iWinId = pm->iWinId;
+
+ if(pm->iWinId == ControllerWinId) // Don't try and delete ourselves
+ throw(errorCommandFailed(PUKE_INVALID, INVALID_DEL_NO_CONTROL));
+
+ /*
+ QIntDict<WidgetS> *qidWS = WidgetList[fd];
+ if(qidWS == 0){
+ kdDebug(5008) << "WidgetRunner:: no such set of widget descriptors?" << endl;
+ throw(errorCommandFailed(PUKE_INVALID, INVALID_DEL_NO_SUCH_CONNECTION));
+ }
+ if(qidWS->find(wI.iWinId)){
+ // Remove the list item then delete the widget. This will stop
+ // the destroyed signal from trying to remove it again.
+ PObject *pw = qidWS->find(wI.iWinId)->pwidget;
+ qidWS->remove(wI.iWinId);
+ delete pw; pw = 0;
+ pmRet.iCommand = PUKE_WIDGET_DELETE_ACK;
+ }
+ else {
+ qWarning("WidgetRunner: no such widget: %d", wI.iWinId);
+ throw(errorCommandFailed(PUKE_INVALID, INVALID_DEL_NO_SUCH_WIDGET));
+ }
+ */
+
+ if(checkWidgetId(&wI) == FALSE){
+ qWarning("WidgetRunner: no such widget: %d", wI.iWinId);
+ throw(errorCommandFailed(PUKE_INVALID, INVALID_DEL_NO_SUCH_WIDGET));
+ }
+
+ WidgetList[fd]->find(wI.iWinId)->pwidget->manTerm();
+ delete WidgetList[fd]->find(wI.iWinId)->pwidget;
+
+ PukeMessage pmRet = *pm;
+ pmRet.iCommand = PUKE_WIDGET_DELETE_ACK;
+
+ emit outputMessage(fd, &pmRet);
+}
+
+void PukeController::closefd(int fd)
+{
+ if(bClosing == TRUE)
+ return;
+ bClosing = TRUE;
+ if(qidConnectFd[fd] == NULL){
+ kdDebug(5008) << "PukeController: Connect table NULL, closed twice?" << endl;
+ return;
+ }
+ // Shutof the listener before closing the socket, just in case.
+ qidConnectFd[fd]->sr->setEnabled(FALSE); // Shut them off
+ qidConnectFd[fd]->sw->setEnabled(FALSE);
+ delete qidConnectFd[fd]->sr;
+ delete qidConnectFd[fd]->sw;
+ qidConnectFd[fd]->server.truncate(0);
+ qidConnectFd.remove(fd);
+ close(fd);
+
+ /*
+ * Now let's remove all traces of the widgets
+ */
+ QIntDict<WidgetS> *qidWS = WidgetList[fd];
+ if(qidWS == 0){
+ kdDebug(5008) << "WidgetRunner:: Close called twice?" << endl;
+ bClosing = FALSE;
+ return;
+ }
+
+ qidWS->remove(PUKE_CONTROLLER);
+
+ do {
+
+ QIntDictIterator<WidgetS> it(*qidWS);
+ if(it.count() == 0){
+ kdDebug(5008) << "WidgetRunner: nothing left to delete\n" << endl;
+ break;
+ }
+
+ PObject *po = 0x0;
+ while(it.current()){
+ /*
+ * Delete all the layouts first
+ *
+ */
+ if(it.current()->type == POBJECT_LAYOUT){
+ po = it.current()->pwidget;
+ break;
+ }
+ ++it;
+ }
+
+ if(po != 0x0){
+ po->manTerm();
+ delete po;
+ continue;
+ }
+
+ /*
+ * reset
+ */
+ it.toFirst();
+ po = it.current()->pwidget;
+ po->manTerm();
+ delete po;
+
+
+ } while (qidWS->count() > 0);
+
+ WidgetList.remove(fd);
+ bClosing = FALSE;
+}
+
+bool PukeController::checkWidgetId(widgetId *pwi)
+{
+ if(WidgetList[pwi->fd] != NULL)
+ if(WidgetList[pwi->fd]->find(pwi->iWinId) != NULL)
+ return TRUE;
+
+ return FALSE;
+}
+
+PObject *PukeController::id2pobject(widgetId *pwi){
+ if(checkWidgetId(pwi) == TRUE){
+ return WidgetList[pwi->fd]->find(pwi->iWinId)->pwidget;
+ }
+ throw(errorNoSuchWidget(*pwi));
+ return 0; // never reached
+}
+
+PObject *PukeController::id2pobject(int fd, int iWinId){
+ widgetId wi;
+ wi.fd = fd;
+ wi.iWinId = iWinId;
+
+ return id2pobject(&wi);
+}
+
+PWidget *PukeController::id2pwidget(widgetId *pwi){
+ PObject *obj = id2pobject(pwi);
+ if(obj->widget()->isWidgetType())
+ return (PWidget *) obj;
+ else
+ throw(errorNoSuchWidget(*pwi));
+ return NULL;
+}
+void PukeController::insertPObject(int fd, int iWinId, WidgetS *obj){
+ // If no widget list exists for this fd, create one
+ if(WidgetList[fd] == NULL){
+ QIntDict<WidgetS> *qidWS = new QIntDict<WidgetS>;
+ qidWS->setAutoDelete(TRUE);
+ WidgetList.insert(fd, qidWS);
+ }
+ // Set main widget structure list
+ WidgetList[fd]->insert(iWinId, obj);
+
+ // Set reverse list used durring delete to remove the widget
+ widgetId *pwi = new widgetId;
+ pwi->fd = fd;
+ pwi->iWinId = iWinId;
+ char key[keySize];
+ memset(key, 0, keySize);
+ sprintf(key, "%p", obj->pwidget);
+ revWidgetList.insert(key, pwi);
+
+ // Now connect to the destroyed signal so we can remove the object from the lists
+ // Once it is deleted
+ connect(obj->pwidget, SIGNAL(destroyed()),
+ this, SLOT(pobjectDestroyed()));
+}
+
+void PukeController::pobjectDestroyed(){
+
+ char key[keySize];
+ memset(key, 0, keySize);
+ sprintf(key, "%p", this->sender());
+
+ widgetId *pwi = revWidgetList[key];
+
+ if(pwi == NULL){
+ kdDebug(5008) << "Someone broke the rules for pwi: " << pwi->fd << ", " << pwi->iWinId << endl;
+ return;
+ }
+
+ if(checkWidgetId(pwi) == TRUE){
+ WidgetList[pwi->fd]->remove(pwi->iWinId);
+ }
+ else {
+ kdDebug(5008) << "Someone stole pwi: " << pwi->fd << ", " << pwi->iWinId << endl;
+ }
+
+ pwi = 0x0; // remove deletes pwi
+ revWidgetList.remove(key);
+
+}
+
+void PukeController::messageHandler(int fd, PukeMessage *pm) {
+ widgetId wI, wIret;
+ wI.fd = fd;
+ wI.iWinId = pm->iWinId;
+
+ commandStruct *cs;
+
+ cs = qidCommandTable[pm->iCommand];
+
+ if(cs != NULL){
+ (this->*(cs->cmd))(fd,pm);
+ }
+ else if(pm->iCommand == PUKE_WIDGET_CREATE){
+ wIret = wI;
+ wIret.iWinId = createWidget(wI, pm).iWinId; // Create the acutal pw
+
+ PukeMessage pmRet;
+ pmRet.iCommand = PUKE_WIDGET_CREATE_ACK;
+ pmRet.iWinId = wIret.iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = strdup(pm->cArg);
+ pmRet.iTextSize = strlen(pm->cArg);
+ emit outputMessage(fd, &pmRet);
+ free(pmRet.cArg);
+ }
+ else if(pm->iCommand == PUKE_WIDGET_LOAD){
+ PukeMessage pmRet = *pm;
+ KLibrary *library;
+ PObject *(*wc)(CreateArgs &ca);
+ widgetCreate *wC;
+
+ if(widgetCF[pm->iArg]){
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iTextSize = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+
+ if(pm->iTextSize == 0){
+ emit(errorCommandFailed(-pm->iCommand, 1));
+ return;
+ }
+
+ QString libName = "ksirc/lib"+QString::fromLatin1(pm->cArg);
+ if (libName.right(3) == ".so")
+ libName = libName.left(libName.length()-2)+"la";
+
+ library = KLibLoader::self()->library(libName);
+ if (!library) {
+ emit(errorCommandFailed(-pm->iCommand, 1));
+ return;
+ }
+ wc = (PObject *(*)(CreateArgs &ca) )
+ library->symbol("createWidget");
+
+ wC = new widgetCreate;
+ wC->wc = wc;
+ wC->library = library;
+ widgetCF.insert(pm->iArg, wC);
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iTextSize = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else if(pm->iCommand == PUKE_WIDGET_UNLOAD){
+ if(widgetCF[pm->iArg]){
+// delete widgetCF[pm->iArg]->library;
+ widgetCF.remove(pm->iArg);
+ pm->iCommand = -pm->iCommand;
+ emit outputMessage(fd, pm);
+ }
+ }
+ else{
+ if(checkWidgetId(&wI) == TRUE){
+ WidgetList[wI.fd]->find(wI.iWinId)->pwidget->messageHandler(fd, pm);
+ }
+ else{
+ PukeMessage pmRet;
+ pmRet.iCommand = PUKE_INVALID;
+ pmRet.iWinId = wI.iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ }
+}
+
+widgetId PukeController::createWidget(widgetId wI, PukeMessage *pm)
+{
+ widgetId wIret;
+ PWidget *parent = 0; // Defaults to no parent
+ WidgetS *ws = new WidgetS;
+
+ /*
+ * The parent widget ID and type are packed into the iArg
+ * the pattern is 2 shorts.
+ */
+
+ int iParent, iType;
+ int found = sscanf(pm->cArg, "%d\t%d", &iParent, &iType);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,7));
+
+ wI.iWinId = iParent; // wI is the identifier for the parent widget
+
+ if(widgetCF[iType] == NULL){ // No such widget, bail out.
+ wIret.fd = 0;
+ wIret.iWinId = 0;
+ throw(errorCommandFailed(PUKE_INVALID,1));
+ }
+
+ uiBaseWinId++; // Get a new base win id
+
+ // wIret holds the current widget id for the new widget
+ wIret.iWinId = uiBaseWinId;
+ wIret.fd = wI.fd;
+
+ if(checkWidgetId(&wI) == TRUE){
+ PObject *obj = WidgetList[wI.fd]->find(wI.iWinId)->pwidget;
+ if(obj->widget()->isWidgetType() == FALSE){
+ throw(errorCommandFailed(PUKE_INVALID, 0));
+ }
+ parent = (PWidget *) obj;
+ }
+
+ // CreateArgs arg = CreateArgs(this, pm, &wIret, parent)
+ CreateArgs arg(this, pm, &wIret, parent);
+ ws->pwidget = (widgetCF[iType]->wc)(arg);
+ if (ws->pwidget->hasError())
+ {
+ throw(errorCommandFailed(PUKE_INVALID, 0));
+ }
+ ws->type = iType;
+
+ connect(ws->pwidget, SIGNAL(outputMessage(int, PukeMessage*)),
+ this, SIGNAL(outputMessage(int, PukeMessage*)));
+
+ // insertPBoject(fd, uiBaseWinId, ws);
+ // The widget list has to exist since we have ourselves in the list
+ insertPObject(wIret.fd, wIret.iWinId, ws);
+// WidgetList[wIret.fd]->insert(wIret.iWinId, ws);
+ return wIret;
+}
+
+
+#include "controller.moc"
+
diff --git a/ksirc/puke/controller.h b/ksirc/puke/controller.h
new file mode 100644
index 00000000..80728224
--- /dev/null
+++ b/ksirc/puke/controller.h
@@ -0,0 +1,211 @@
+/*
+ * Main controller for:
+ *
+ * PUKE = Perl gUi Kontrol Environment
+ *
+ */
+
+#ifndef PUKE_CONTROLLER_H
+#define PUKE_CONTROLLER_H
+
+#include <qobject.h>
+#include <qsocketnotifier.h>
+#include <qstring.h>
+#include <qdict.h>
+#include <qintdict.h>
+#include <qstrlist.h>
+
+#include "pmessage.h"
+#include "pobject.h"
+#include "pwidget.h"
+#include "commands.h"
+
+class PukeController;
+class KLibrary;
+
+typedef struct {
+ QString server;
+ bool writeable;
+ QSocketNotifier *sr,*sw;
+} fdStatus;
+
+
+struct commandStruct {
+ void (PukeController::*cmd)(int, PukeMessage*);
+ KLibrary *library;
+};
+
+typedef struct {
+ PObject *pwidget; // The widget
+ int type; // The type so casting is "safer"
+} WidgetS; // WidgetStruct
+
+typedef struct {
+ PObject *(*wc)(CreateArgs &ca);
+ KLibrary *library;
+} widgetCreate;
+
+class errorNoSuchWidget {
+public:
+ errorNoSuchWidget(widgetId &_wi)
+ {
+ wi = _wi;
+ }
+
+ widgetId &widgetIden() {
+ return wi;
+ }
+private:
+ widgetId wi;
+};
+
+class errorCommandFailed {
+public:
+ errorCommandFailed(int _command, int _iarg){
+ __command = _command;
+ __iarg = _iarg;
+ }
+
+ int command() { return __command; }
+ int iarg() { return __iarg; }
+
+private:
+ int __command, __iarg;
+};
+
+#define INVALID_DEL_NO_CONTROL 100
+#define INVALID_DEL_NO_SUCH_CONNECTION 101
+#define INVALID_DEL_NO_SUCH_WIDGET 102
+
+class PukeController : public PObject
+{
+ Q_OBJECT
+public:
+ PukeController(QString socket = "", QObject *parent=0, const char *name=0);
+ virtual ~PukeController();
+ bool running;
+
+ /**
+ * Verifies the widgetId exists and is a valid widget.
+ * True is valid, false if invalid.
+ */
+ bool checkWidgetId(widgetId *pwI);
+
+ /**
+ * id2pobject takes a window id and returns the reuired object
+ * it throw an errorNoSuchWidget on failures
+ */
+ PObject *id2pobject(int fd, int iWinId);
+ PObject *id2pobject(widgetId *pwi);
+ /**
+ * Return a PWidget if it's a widget, throws an exception if not found
+ */
+ PWidget *id2pwidget(widgetId *pwi);
+
+ QStrList allObjects();
+
+signals:
+ void PukeMessages(QString server, int command, QString args);
+ void inserted(QObject *);
+
+public slots:
+ void ServMessage(QString, int, QString);
+
+protected slots:
+ void Traffic(int);
+ void Writeable(int);
+ void NewConnect(int);
+ void slotInserted(QObject *obj);
+
+ /**
+ * When we delete a widget, this removes it from our internal
+ * list of widgets. We never remove a widget ourselfs, we call delete
+ * and this function removes it.
+ */
+ void pobjectDestroyed();
+
+ /**
+ * Fd to write to
+ * PukeMessage message to be written, if null buffer is flushed.
+ */
+ void writeBuffer(int fd, PukeMessage *message);
+
+
+private:
+ QString qsPukeSocket;
+ int iListenFd;
+ bool bClosing; // Set true if we are closing, we don't try and close twice at the same time.
+ QSocketNotifier *qsnListen;
+ QIntDict<fdStatus> qidConnectFd;
+
+ /**
+ * Controller ID is defined as 1
+ */
+ enum { ControllerWinId = PUKE_CONTROLLER };
+
+
+ // List of widgets and the fle descriptors they belong too
+ QIntDict<QIntDict<WidgetS> > WidgetList;
+ // I use a char * key that's the %p (hex address) of the pwidget
+ QDict<widgetId> revWidgetList;
+ enum { keySize = 10 };
+
+ // Funtions used to create new widget
+ QIntDict<widgetCreate> widgetCF; // widgetCreatingFuntion List
+
+ QIntDict<commandStruct> qidCommandTable;
+
+ void initHdlr();
+
+ void closefd(int fd);
+
+ void MessageDispatch(int fd, PukeMessage *pm);
+
+ /**
+ * WinId comes from a static unsigned int we increment for each new window
+ */
+ static uint uiBaseWinId;
+
+ /**
+ * Create new Widget, returns new iWinId for it.
+ * Takes server fd and parent winid, and type as arguments
+ */
+ widgetId createWidget(widgetId wI, PukeMessage *pm);
+
+ /**
+ * Used to process messages going to controller, winId #1
+ *
+ */
+ void messageHandler(int fd, PukeMessage *pm);
+
+ /**
+ * NOT APPLICAABLE
+ */
+ void setWidget(QObject *) { }
+ /**
+ * NOT APPLICAABLE
+ */
+ virtual QObject *widget() { return 0x0; }
+
+ /**
+ * Inserts a PObject into our internal list
+ */
+ void insertPObject(int fd, int iWinId, WidgetS *obj);
+
+ /**
+ * Closes a widget, checking for sanity
+ */
+// void closeWidget(widgetId wI);
+
+ // Message handlers
+ void hdlrPukeSetup(int fd, PukeMessage *pm);
+ void hdlrPukeInvalid(int fd, PukeMessage *pm);
+ void hdlrPukeEcho(int fd, PukeMessage *pm);
+ void hdlrPukeDumpTree(int fd, PukeMessage *pm);
+ void hdlrPukeFetchWidget(int fd, PukeMessage *pm);
+ void hdlrPukeDeleteWidget(int fd, PukeMessage *pm);
+
+};
+
+#endif
+
diff --git a/ksirc/puke/convert_commands.pl b/ksirc/puke/convert_commands.pl
new file mode 100755
index 00000000..98ad7e35
--- /dev/null
+++ b/ksirc/puke/convert_commands.pl
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+open(CMD, "cat commands.h *-cmd.h|");
+open(CMDP, ">commands-perl.pl");
+while(<CMD>){
+ if(/#define (\S+) (.+)/){
+ print CMDP "\$${1} = $2;\n";
+ print CMDP "\$PUKE_NAME2NUM\{'$1'\} = $2;\n";
+ print CMDP "\$PUKE_NUM2NAME\{'$2'\} = '$1';\n";
+ }
+}
+
+print CMDP "\n1;\n";
diff --git a/ksirc/puke/dcc_progress.pm b/ksirc/puke/dcc_progress.pm
new file mode 100644
index 00000000..a21ee26e
--- /dev/null
+++ b/ksirc/puke/dcc_progress.pm
@@ -0,0 +1,55 @@
+
+&docommand("/load pprogress.pm");
+
+package DCCProgress;
+
+@ISA = qw(PProgress);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->create();
+
+ $self->installHandler($::PUKE_KSPROGRESS_CANCEL_ACK,
+ sub {$self->cancelPressed(@_)});
+
+ return $self;
+}
+
+
+sub setRange {
+ my $self = shift;
+ my $min = shift;
+ my $max = shift;
+
+ $self->{'min'} = $min;
+ $self->{'max'} = $max;
+ $self->{'step'} = ($max - $min) / 100;
+ $self->{'step'} = 10240 if $self->{'step'} == 0; # if sized messed don't divide by 0 :)
+ $self->PProgress::setRange(0, 100);
+}
+
+sub setValue {
+ my $self = shift;
+
+ my $value = shift;
+
+ my $steps = int($value / $self->{'step'});
+
+ $self->PProgress::setValue($steps);
+}
+
+sub setCancel {
+ my $self = shift;
+ $self->{'cancelMessage'} = shift;
+}
+
+sub cancelPressed {
+ my $self = shift;
+
+ &::docommand($self->{'cancelMessage'});
+ $self->hide;
+
+} \ No newline at end of file
diff --git a/ksirc/puke/dcc_status.pm b/ksirc/puke/dcc_status.pm
new file mode 100644
index 00000000..65bc1b07
--- /dev/null
+++ b/ksirc/puke/dcc_status.pm
@@ -0,0 +1,525 @@
+#use lib "/opt/kde/share/apps/ksirc";
+
+&docommand("/load pbase.pm");
+&docommand("/load pwidget.pm");
+&docommand("/load pframe.pm");
+&docommand("/load ptablevw.pm");
+&docommand("/load plistbox.pm");
+&docommand("/load pbutton.pm");;
+&docommand("/load pboxlayout.pm");
+&docommand("/load plabel.pm");
+&docommand("/load pbutton.pm");
+&docommand("/load ppushbt.pm");
+&docommand("/load plined.pm");
+&docommand("/load pkfiledialog.pm");
+&docommand("/load ppopmenu.pm");
+
+&docommand("/load dcc_progress.pm");
+
+&docommand("/load plined.pm");
+&docommand("/load pprogress.pm");
+
+use POSIX qw(getcwd);
+use strict;
+
+package DCCSendDialog;
+use vars qw(@ISA);
+@ISA = qw(PFrame);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+ $self->create();
+
+ my $gm_main = new PBoxLayout($self, $PBoxLayout::TopToBottom, 5);
+
+ my $gm_to = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+ $gm_main->addLayout($gm_to);
+
+ my $label_to = new PLabel($self);
+ $label_to->setText("To Nick:");
+ $label_to->setMaximumSize(1000,30);
+ $label_to->setMinimumSize(50,30);
+ $gm_to->addWidget($label_to, 0, $PBoxLayout::AlignCenter);
+
+ my $line_to = new PLineEdit($self);
+ $gm_to->addWidget($line_to, 5, $PBoxLayout::AlignCenter);
+
+ my $gm_file = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+ $gm_main->addLayout($gm_file);
+
+ my $label_file = new PLabel($self);
+ $label_file->setText("Filename:");
+ $label_file->setMaximumSize(1000,30);
+ $label_file->setMinimumSize(50,30);
+ $gm_file->addWidget($label_file, 0, $PBoxLayout::AlignCenter);
+
+ my $line_file = new PLineEdit($self);
+ $gm_file->addWidget($line_file, 5, $PBoxLayout::AlignLeft);
+
+ my $button_file = new PPushButton($self);
+ $button_file->setText("&Browse");
+ $button_file->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{$self->browseClicked});
+ $gm_file->addWidget($button_file, 2, $PBoxLayout::AlignRight);
+
+ my $button_send = new PPushButton($self);
+ $button_send->setText("&Send");
+ $button_send->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{$self->sendClicked});
+ $gm_main->addWidget($button_send, 4, $PBoxLayout::AlignRight);
+
+ @$self{'gm_main', 'gm_to', 'gm_file', 'label_to', 'line_to', 'label_file', 'line_file', 'button_file', 'button_send'}
+ = ($gm_main, $gm_to, $gm_file, $label_to, $line_to, $label_file, $line_file, $button_file, $button_send);
+
+ print "*I* Finished creating DCCSend\n";
+
+ $self->{fileDialog} = new PKFileDialog();
+ my $dlg = $self->{fileDialog};
+ $dlg->setDir(POSIX::getcwd());
+ $dlg->installHandler($::PUKE_KBFD_FILE_SELECTED_ACK, sub{$self->fileSelected(shift())});
+
+ $self->setMinimumSize(450, 110);
+ $self->setMaximumSize(2000, 2000);
+ $self->resize(450, 110);
+
+ return $self;
+
+}
+
+sub browseClicked {
+ my $self = shift;
+
+ $self->{fileDialog}->show();
+
+}
+
+sub fileSelected {
+ my $self = shift;
+
+ my $hargs = shift;
+
+ $self->{fileDialog}->hide();
+
+ my $file = $hargs->{'cArg'};
+
+ if($file ne ''){
+ $self->{'line_file'}->setText($file);
+ }
+}
+
+sub sendClicked {
+ my $self = shift;
+
+ my $to_nick = $self->{'line_to'}->text();
+ my $to_file = $self->{'line_file'}->text();
+
+ if($to_nick eq '' || $to_file eq ''){
+ return;
+ }
+
+ &::docommand("dcc send $to_nick $to_file");
+ $self->hide();
+}
+
+
+use vars qw(@ISA $KSIRC_DCC %KSIRC_DCC);
+
+package DCCStatus;
+use vars qw(@ISA);
+@ISA = qw(PFrame);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+ $self->create();
+
+ my $gm_main = new PBoxLayout($self, $PBoxLayout::TopToBottom, 5);
+
+ my $label = new PLabel($self);
+ $label->setText("Pending DCC");
+ $label->setMaximumSize(1000,20);
+ $gm_main->addWidget($label, 5, $PBoxLayout::AlignCenter);
+ my $lb = new PListBox($self);
+ $gm_main->addWidget($lb, 5, $PBoxLayout::AlignCenter);
+
+ my $gm_but1 = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+ $gm_main->addLayout($gm_but1);
+
+ my $button_get = new PPushButton($self);
+ $button_get->setText("&Open Connection");
+ $button_get->setMaximumSize(1000,30);
+ $button_get->setMinimumSize(10,30);
+ $button_get->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{$self->openClicked});
+ $gm_but1->addWidget($button_get, 5);
+
+ my $button_forget = new PPushButton($self);
+ $button_forget->setText("&Forget Connection");
+ $button_forget->setMaximumSize(1000,30);
+ $button_forget->setMinimumSize(10,30);
+ $button_forget->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{$self->forgetClicked});
+ $gm_but1->addWidget($button_forget, 5);
+
+ my $gm_but2 = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+ $gm_main->addLayout($gm_but2);
+
+ my $button_send = new PPushButton($self);
+ $button_send->setText("&Send File");
+ $button_send->setMaximumSize(1000,30);
+ $button_send->setMinimumSize(10,30);
+ $button_send->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{$self->sendClicked});
+ $gm_but2->addWidget($button_send, 5);
+
+ $gm_main->activate();
+
+ $self->setMinimumSize(400, 275);
+ $self->setMaximumSize(2000, 2000);
+ $self->resize(400, 275);
+
+ @$self{'gm_main', 'label1', 'lb', 'button_get', 'button_chat', 'button_forget', 'gm_but1', 'gm_but2', 'button_send' }
+ = ( $gm_main, $label, $lb, $button_get, $button_send, $button_forget, $gm_but1, $gm_but2, $button_send );
+
+
+ print "*I* Finished creating DCCStatus\n";
+
+ return $self;
+}
+
+sub addItem {
+ my $self = shift;
+
+ my %hargs = @_;
+
+ my $line = $hargs{'line'};
+
+ if($self->{'lines'}->{$line}){
+ return -1;
+ }
+
+ $self->{'lines'}->{$line}->{'OpenCode'} = $hargs{'open'};
+ $self->{'lines'}->{$line}->{'ForgetCode'} = $hargs{'forget'};
+
+ $self->{'lb'}->insertText($line, -1);
+ if($self->{'lb'}->currentText() eq ''){
+ $self->{'lb'}->setCurrentItem(0);
+ }
+
+ return 0;
+}
+
+sub openClicked {
+ my $self = shift;
+
+ my $line = $self->{'lb'}->currentText();
+ if($line eq ''){
+ return;
+ }
+
+ &{$self->{'lines'}->{$line}->{'OpenCode'}};
+ $self->{'lb'}->removeItem($self->{'lb'}->current());
+ if($self->{'lb'}->{count} == 0){
+ $self->hide();
+ }
+
+ delete $self->{'lines'}->{$line};
+
+}
+
+sub forgetClicked {
+ my $self = shift;
+
+ my $line = $self->{'lb'}->currentText();
+ if($line eq ''){
+ return;
+ }
+
+ &{$self->{'lines'}->{$line}->{'ForgetCode'}};
+ $self->{'lb'}->removeItem($self->{'lb'}->current());
+ if($self->{'lb'}->{count} == 0){
+ $self->hide();
+ }
+
+
+ delete $self->{'lines'}->{$line};
+
+}
+
+sub sendClicked {
+ my $self = shift;
+
+ if(!$self->{sendDialog}){
+ my $dlg = new DCCSendDialog();
+ if($dlg == undef){
+ &print("*E* Could not load DCCSendDialog");
+ return;
+ }
+ $self->{sendDialog} = $dlg;
+ }
+
+ $self->{sendDialog}->show();
+}
+
+sub DESTROY {
+ print "*E* Destroying dcc status widget\n";
+ shift()->close();
+}
+
+sub close {
+ my $self = shift;
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_DELETE,
+ 'CallBack' => sub {},
+ 'WaitFor' => 1);
+ # $self->{'gm_main'}->DESTROY;
+ #elete $self->{'gm_main'};
+}
+
+package DCCPopupMenu;
+use vars qw(@ISA);
+@ISA = qw(PPopupMenu);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+ $self->create();
+ return $self;
+}
+
+sub insertText {
+ my $self = shift;
+
+ my $id = $self->SUPER::insertText(@_);
+
+ my @arr;
+
+ if(ref($self->{'Ids'}) ne 'ARRAY'){
+ $self->{'Ids'} = \@arr;
+ }
+
+ @arr = @{$self->{'Ids'}};
+
+ $arr[$#arr+1] = $id;
+
+ $self->{'Ids'} = \@arr;
+
+ return $id;
+}
+
+sub DESTROY {
+ my $self = shift;
+
+ my @arr = @{$self->{'Ids'}};
+ my $id;
+
+ foreach $id (@arr) {
+ $self->removeItem($id);
+ }
+
+ $self->sendMessage('iCommand' => $::PUKE_RELEASEWIDGET,
+ 'CallBack' => sub {});
+
+
+}
+
+
+package main;
+use vars qw($KSIRC_DCC %KSIRC_DCC $who $KSIRC_DCCSTATUS $silent $nick $KSIRC_POPSC $KSIRC_POPDOCK);
+
+sub hook_ksirc_dcc_request {
+ my($type) = shift;
+ my($machine) = shift;
+ my($port) = shift;
+ my($file) = shift;
+ my($size) = shift;
+ my($mwho) = $who;
+ if($KSIRC_DCCSTATUS == undef){
+ $KSIRC_DCCSTATUS = new DCCStatus;
+ $KSIRC_DCCSTATUS->resize(400, 275);
+ }
+
+ if($type eq 'SEND'){
+ my $open = sub {
+ &docommand("/dcc get $mwho $file");
+ };
+ my $forget = sub {
+ &docommand("/dcc close get $mwho $file");
+ };
+ $::KSIRC_FILE_SIZES{$file} = $size;
+ $KSIRC_DCCSTATUS->addItem('line' => "SEND: $who offered $file at size $size",
+ 'open' => $open,
+ 'forget' => $forget);
+ }
+ elsif($type eq 'CHAT'){
+ $KSIRC_DCCSTATUS->addItem('line' => "CHAT: $who",
+ 'open' => sub { &docommand("/dcc chat $mwho"); },
+ 'forget' => sub { &docommand("/dcc close chat $mwho"); });
+
+ }
+ $KSIRC_DCCSTATUS->show;
+
+}
+
+&addhook("dcc_request", "ksirc_dcc_request");
+
+sub hook_ksirc_dcc_send {
+ my $nick = shift;
+ my $file = shift;
+ my $size = shift;
+ my $fh = shift;
+
+ # print "*I* Starting dcc into with: $nick, $file, $size, $fh\n";
+
+ my($window) = new DCCProgress;
+ $size = 10240 if $size == 0;
+ $window->setRange(0, $size);
+ $window->setCaption("$file=>$nick");
+ $window->setTopText("Sending: $file Size: $size");
+ $window->setBotText("Status: pending");
+ $window->setCancel("dcc close send $nick $file");
+ $KSIRC_DCC{$fh}{$file}{'Window'} = $window;
+ $KSIRC_DCC{$fh}{$file}{'StartTime'} = time() - 1;
+ $window->show;
+}
+
+&addhook("dcc_send", "ksirc_dcc_send");
+
+sub hook_ksirc_dcc_send_status {
+ my $file = shift;
+ my $bytes = shift;
+ my $fh = shift;
+
+ my $window = $KSIRC_DCC{$fh}{$file}{'Window'};
+ if($window == undef){
+ my($window) = new DCCProgress;
+ $window->setRange(0, 1);
+ $window->setCaption("$file=>$nick");
+ $window->setTopText("Sending: $file Size: Unkown");
+ $window->setCancel("dcc close send $nick $file");
+ $KSIRC_DCC{$fh}{$file}{'Window'} = $window;
+ $KSIRC_DCC{$fh}{$file}{'StartTime'} = time() - 1;
+ $window->show;
+ }
+ $window->setBotText("BPS: " . int($bytes/(time() - $KSIRC_DCC{$fh}{$file}{'StartTime'})));
+ $window->setValue($bytes);
+}
+
+&addhook("dcc_send_status", "ksirc_dcc_send_status");
+
+sub hook_ksirc_dcc_get {
+ my $nick = shift;
+ my $file = shift;
+ my $fh = shift;
+
+ my $size = $::KSIRC_FILE_SIZES{$file};
+
+#print "*I* Starting dcc into with: $nick, $file, $size, $fh\n";
+
+ if($KSIRC_DCC{$fh}{$file}{'Window'} == undef){
+ my($window) = new DCCProgress;
+ $size = 10240 if $size == 0;
+ $window->setRange(0, $size);
+ $window->setCaption("$file<=$nick");
+ $window->setTopText("Receiver: $file Size: $size");
+ $window->setBotText("Status: pending");
+ $window->setCancel("dcc close get $nick $file");
+ $KSIRC_DCC{$fh}{$file}{'Window'} = $window;
+ $KSIRC_DCC{$fh}{$file}{'StartTime'} = time() - 1;
+ $window->show;
+ }
+}
+
+&addhook("dcc_get", "ksirc_dcc_get");
+
+sub hook_ksirc_dcc_get_status {
+ my $file = shift;
+ my $bytes = shift;
+ my $fh = shift;
+
+ my $window = $KSIRC_DCC{$fh}{$file}{'Window'};
+ if($window == undef){
+ my($window) = new DCCProgress;
+ $window->setRange(0, 1);
+ $window->setCaption("$file<=$nick");
+ $window->setTopText("Receiver: $file Size: Unkown");
+ $window->setBotText("Status: pending");
+ $window->setCancel("dcc close get $nick $file");
+ $KSIRC_DCC{$fh}{$file}{'Window'} = $window;
+ $KSIRC_DCC{$fh}{$file}{'StartTime'} = time() - 1;
+ $window->show;
+ }
+ $window->setBotText("BPS: " . int($bytes/(time() - $KSIRC_DCC{$fh}{$file}{'StartTime'})));
+ $window->setValue($bytes);
+}
+
+&addhook("dcc_get_status", "ksirc_dcc_get_status");
+
+
+sub hook_ksirc_dcc_disconnect {
+ my $nick = shift;
+ my $file = shift;
+ my $bytes = shift;
+ my $time = shift;
+ my $fh = shift;
+
+ if($fh){
+ my $window = $KSIRC_DCC{$fh}{$file}{'Window'};
+ $window->close();
+ delete $KSIRC_DCC{$fh}{$file};
+ delete $KSIRC_DCC{$fh};
+ print "*D* DCC transfer with $nick ($file) terminated; $bytes transferred in $time seconds (" . int(($bytes/($time+1))/1024) . "KBps)";
+ $silent = 1;
+ }
+}
+
+addhook("dcc_disconnect", "ksirc_dcc_disconnect");
+
+
+
+&print("*I* Done DCC Status");
+#$::test = new DCCStatus;
+#$::test->resize(400, 275);
+#$::test->show();
+
+sub popup_dccstatus{
+ if($KSIRC_DCCSTATUS == undef){
+ $KSIRC_DCCSTATUS = new DCCStatus;
+ $KSIRC_DCCSTATUS->resize(400, 275);
+ }
+ $KSIRC_DCCSTATUS->show();
+}
+
+sub popup_dccsend{
+ if($KSIRC_DCCSTATUS == undef){
+ $KSIRC_DCCSTATUS = new DCCStatus;
+ $KSIRC_DCCSTATUS->resize(400, 275);
+ }
+ $KSIRC_DCCSTATUS->sendClicked();
+}
+
+=cut
+if(!$KSIRC_POPSC){
+ $KSIRC_POPSC = new DCCPopupMenu();
+ if($KSIRC_POPSC->fetchWidget("servercontroller_menu_file") >= 0){
+ my $id_control = $KSIRC_POPSC->insertText("Show DCC Control ($::server)");
+ my $id_send = $KSIRC_POPSC->insertText("Show DCC Send ($::server)");
+ $KSIRC_POPSC->installMenu($id_control, sub{&popup_dccstatus();});
+ $KSIRC_POPSC->installMenu($id_send, sub{&popup_dccsend();});
+ }
+}
+
+if(!$KSIRC_POPDOCK){
+ $KSIRC_POPDOCK = new DCCPopupMenu();
+ if($KSIRC_POPDOCK->fetchWidget("dockServerController_menu_pop") >= 0){
+ my $id_control = $KSIRC_POPDOCK->insertText("Show DCC Control ($::server)");
+ my $id_send = $KSIRC_POPDOCK->insertText("Show DCC Send ($::server)");
+ $KSIRC_POPDOCK->installMenu($id_control, sub { &popup_dccstatus(); } );
+ $KSIRC_POPDOCK->installMenu($id_send, sub { &popup_dccsend(); } );
+ }
+}
+=cut
+
+sub hook_quit_release {
+ $KSIRC_POPDOCK->DESTROY();
+ $KSIRC_POPSC->DESTROY();
+}
+
+&addhook("quit", "quit_release");
+
+1;
diff --git a/ksirc/puke/load_all.pm b/ksirc/puke/load_all.pm
new file mode 100644
index 00000000..26594c0f
--- /dev/null
+++ b/ksirc/puke/load_all.pm
@@ -0,0 +1,14 @@
+&docommand("/load pbase.pm");
+&docommand("/load pwidget.pm");
+&docommand("/load pframe.pm");
+&docommand("/load pboxlayout.pm");
+&docommand("/load plined.pm");
+&docommand("/load pbutton.pm");
+&docommand("/load ppushbt.pm");
+&docommand("/load pprogress.pm");
+&docommand("/load ptablevw.pm");
+&docommand("/load plistbox.pm");
+&docommand("/load ppopmenu.pm");
+&docommand("/load ptabdialog.pm");
+&docommand("/load pkfiledialog.pm");
+&docommand("/load pobjfinder.pm");
diff --git a/ksirc/puke/palistbox.cpp b/ksirc/puke/palistbox.cpp
new file mode 100644
index 00000000..8f3f22f1
--- /dev/null
+++ b/ksirc/puke/palistbox.cpp
@@ -0,0 +1,156 @@
+#include <kdebug.h>
+
+
+#include "palistbox.h"
+
+PObject *
+PAListBox::createWidget(CreateArgs &ca)
+{
+ PAListBox *plb = new PAListBox(ca.parent);
+ aListBox *lb;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("aListBox") == TRUE){
+ lb = (aListBox *) ca.fetchedObj;
+ plb->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ lb = new aListBox((QWidget *) ca.parent->widget());
+ else
+ lb = new aListBox();
+ plb->setWidget(lb);
+ plb->setWidgetId(ca.pwI);
+ return plb;
+}
+
+
+PAListBox::PAListBox(PObject *parent)
+ : PListBox(parent)
+{
+ // kdDebug(5008) << "PListBox PListBox called" << endl;
+ lb = 0;
+ setWidget(lb);
+}
+
+PAListBox::~PAListBox()
+{
+ // kdDebug(5008) << "PListBox: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ lb=0; // Set it to 0
+ setWidget(lb); // Now set all widget() calls to 0.
+ */
+}
+
+void PAListBox::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_LISTBOX_INSERT_SORT:
+ if(!checkWidget())
+ return;
+
+ widget()->inSort(pm->cArg, (bool) pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->count();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_ALISTBOX_ISTOP:
+ if(!checkWidget())
+ return;
+
+ pmRet.iArg = widget()->isTop(pm->iArg);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_ALISTBOX_FIND_NICK:
+ if(!checkWidget())
+ return;
+
+ pmRet.iArg = widget()->findNick(pm->cArg);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_ALISTBOX_SMALL_HIGHLIGHT:
+ {
+ if(!checkWidget())
+ return;
+
+ int index = widget()->findNick(pm->cArg);
+ nickListItem *item = new nickListItem();
+ *item = *widget()->item(index);
+ widget()->removeItem(index);
+ item->setVoice(pm->iArg);
+ widget()->inSort(item);
+ widget()->repaint(TRUE);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ case PUKE_ALISTBOX_BIG_HIGHLIGHT:
+ {
+ if(!checkWidget())
+ return;
+
+ int index = widget()->findNick(pm->cArg);
+ nickListItem *item = new nickListItem();
+ *item = *widget()->item(index);
+ widget()->removeItem(index);
+ item->setOp(pm->iArg);
+ widget()->inSort(item);
+ widget()->repaint(TRUE);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+
+ default:
+ PListBox::messageHandler(fd, pm);
+ }
+}
+
+void PAListBox::setWidget(QObject *_lb)
+{
+ if(_lb != 0 && _lb->inherits("aListBox") == FALSE)
+ {
+ errorInvalidSet(_lb);
+ return;
+ }
+
+ lb = (aListBox *) _lb;
+ if(lb != 0){
+ }
+ PListBox::setWidget(lb);
+
+}
+
+
+aListBox *PAListBox::widget()
+{
+ return lb;
+}
+
+bool PAListBox::checkWidget(){
+ if(widget() == 0){
+ kdDebug(5008) << "PAListBox: No Widget set" << endl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#include "palistbox.moc"
+
diff --git a/ksirc/puke/palistbox.h b/ksirc/puke/palistbox.h
new file mode 100644
index 00000000..0731bef9
--- /dev/null
+++ b/ksirc/puke/palistbox.h
@@ -0,0 +1,35 @@
+#ifndef PALISTBOX_H
+#define PALISTBOX_H
+
+class PAListBox;
+
+#include "../alistbox.h"
+#include "pmessage.h"
+#include "plistbox.h"
+#include "controller.h"
+
+class PAListBox : public PListBox
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PAListBox ( PObject * parent );
+ virtual ~PAListBox ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_lb = 0x0);
+ virtual aListBox *widget();
+
+public slots:
+
+protected:
+ bool checkWidget();
+
+private:
+ aListBox *lb;
+
+};
+
+#endif
diff --git a/ksirc/puke/palistbox.pm b/ksirc/puke/palistbox.pm
new file mode 100644
index 00000000..e1dcb691
--- /dev/null
+++ b/ksirc/puke/palistbox.pm
@@ -0,0 +1,117 @@
+
+&::PukeSendMessage($::PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $::PWIDGET_ALISTBOX,
+ "palistbox.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PAListBox Load failed!\n";
+ }
+ }
+ );
+
+
+package PAListBox;
+@ISA = qw(PListBox);
+use strict;
+
+if($PListBox::usage == undef){
+ $PListBox::usage = 0;
+}
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_ALISTBOX;
+
+ if($class eq 'PAListBox'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_LISTBOX_SELECTED_ACK, sub{$self->selected(@_)});
+
+ return $self;
+
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->SUPER::DESTROY(@_);
+ $PAListBox::usage--;
+ if($PAListBox::usage == 0){
+ &::PukeSendMessage($::PUKE_WIDGET_UNLOAD,
+ 0,
+ $::PWIDGET_ALISTBOX,
+ "",
+ sub {}
+ );
+
+ }
+}
+
+sub inSort {
+ my $self = shift;
+ my $text = shift;
+ my $top = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_INSERT_SORT,
+ 'cArg' => $text,
+ 'iArg' => $text >= 1 ? 1 : 0,
+ 'CallBack' => sub {});
+
+}
+
+sub isTop {
+ my $self = shift;
+ my $text = shift;
+
+ my %ret = $self->sendMessage('iCommand' => $::PUKE_ALISTBOX_ISTOP,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+ return $ret{'iArg'};
+
+}
+
+sub findNick {
+ my $self = shift;
+ my $text = shift;
+
+ my %ret = $self->sendMessage('iCommand' => $::PUKE_ALISTBOX_FIND_NICK,
+ 'cArg' => $text,
+ 'CallBack' => sub {},
+ 'WaitFor' => 1);
+
+ return $ret{'iArg'};
+
+}
+
+sub smallHighligh {
+ my $self = shift;
+ my $text = shift;
+ my $highlight = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_ALISTBOX_SMALL_HIGHLIGHT,
+ 'cArg' => $text,
+ 'iArg' => $highlight,
+ 'CallBack' => sub {});
+
+}
+
+sub bigHighligh {
+ my $self = shift;
+ my $text = shift;
+ my $highlight = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_ALISTBOX_BIG_HIGHLIGHT,
+ 'cArg' => $text,
+ 'iArg' => $highlight,
+ 'CallBack' => sub {});
+
+}
+
+
+package main;
+
+1;
diff --git a/ksirc/puke/pbase.pm b/ksirc/puke/pbase.pm
new file mode 100644
index 00000000..b8345cdb
--- /dev/null
+++ b/ksirc/puke/pbase.pm
@@ -0,0 +1,265 @@
+
+package PBase;
+use Carp;
+#use Data::Dumper;
+use strict;
+
+$PBase::NO_WIDGET = -1;
+
+$::AlignLeft = 0x0001;
+$::AlignRight = 0x0002;
+$::AlignHCenter = 0x0004;
+$::AlignTop = 0x0008;
+$::AlignBottom = 0x0010;
+$::AlignVCenter = 0x0020;
+$::AlignCenter = $::AlignVCenter | $::AlignHCenter;
+
+sub sendMessage {
+ my $self = shift;
+
+ my %ARG = @_;
+ $ARG{"iWinId"} = $self->{iWinId} if($ARG{"iWinId"} == undef);
+
+ return &::PukeSendMessage($ARG{"iCommand"},
+ $ARG{"iWinId"},
+ $ARG{"iArg"},
+ $ARG{"cArg"},
+ $ARG{"CallBack"},
+ $ARG{"WaitFor"}
+ );
+}
+
+sub rndchr {
+ my $string = "";
+ my $i;
+ for($i = 0; $i < 8; $i++){
+ $string .= chr(int(rand(93)) + 0x21); # 0x21 since we don't want spaces and 0x20 is space.
+ }
+ return $string;
+}
+
+sub new {
+ my $class = shift;
+ my $parent = $_[$#_];
+
+ my $self = {};
+
+
+# print "Parent: $parent\n";
+
+ bless($self, $class);
+
+ $parent = 0 if($parent == undef);
+
+ $self->{iWinId} = -1;
+ $self->{Parent} = $parent if $parent != 0;
+ $self->{initId} = $self->rndchr();
+ $self->{widgetType} = $PBase::NO_WIDGET;
+ $self->{cmdQueue} = ();
+
+ if($::PUKE_FETCH_WIDGET == 1) {
+ $self->{Fetch} = 1;
+ }
+
+ # $self->installHandler($::PUKE_WIDGET_DELETE_ACK, sub{$self->DESTROY});
+
+ return $self;
+
+}
+
+sub create {
+ my $self = shift;
+
+ if($self->{widgetType} == undef ||
+ $self->{widgetType} == $PBase::NO_WIDGET) {
+ print("*E* PBase: Widget type was undefined, $self is really broken\n");
+ print("*E* PBase: Giving up\n");
+ return;
+ }
+
+ my $parent = $self->{Parent} ? $self->{Parent}->{iWinId} : 0;
+
+ # print "*I* Createing widget of type: " . $self->{widgetType} . " with parent " . $parent . "\n";
+
+ $self->{runable} = 1;
+
+ my $carg = $parent . "\t" . $self->{widgetType} . "\t" . $self->{initId};
+
+ my %REPLY = $self->sendMessage('iCommand' => $::PUKE_WIDGET_CREATE,
+ 'iWinId' => $::PUKE_CONTROLLER,
+ 'cArg' => $carg,
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+ if($REPLY{iWinId} <= 0){
+ print "*E* Widget Create Failed!\n";
+ }
+
+ $self->ackWinId(%REPLY);
+
+ $self->clearQueue();
+ # $self->setRunable(0);
+}
+
+sub fetchWidget {
+ my $self = shift;
+
+ $self->{objName} = shift;
+ my $regex = shift;
+
+# $self->sendMessage('iCommand' => $::PUKE_WIDGET_DELETE,
+# 'CallBack' => sub { print "Deleted\n"; });
+
+ $regex = "0" if($regex eq '');
+ my $carg = $regex . "\t" . $self->{widgetType} . "\t" . $self->{initId} . "\t" . $self->{objName};
+
+ my %REPLY = $self->sendMessage('iCommand' => $::PUKE_FETCHWIDGET,
+ 'iWinId' => $::PUKE_CONTROLLER,
+ 'cArg' => $carg,
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+ if($REPLY{iWinId} <= 0){
+ print "*E* Widget Fetch Failed!\n";
+ return -1;
+ }
+ my $winid;
+ my $cmd;
+ foreach $cmd (values %::PUKE_W_HANDLER){
+ next unless ref $cmd eq 'CODE';
+ foreach $winid (values %{$::PUKE_W_HANDLER{$cmd}}){
+ if($winid == $self->{'iWinId'}){
+ $::PUKE_W_HANDLER{$cmd}{$REPLY{iWinId}} = $::PUKE_W_HANDLER{$cmd}{$self->{iWinId}};
+ delete $::PUKE_W_HANDLER{$cmd}{$self->{iWinId}};
+ }
+ }
+ }
+
+ $self->ackWinId(%REPLY);
+ $self->{'Fetch'} = 1;
+ # $self->setRunable(0);
+ return 0;
+}
+
+sub releaseWidget {
+ my $self = shift;
+ $self->sendMessage('iCommand' => $::PUKE_RELEASEWIDGET,
+ 'CallBack' => sub {});
+}
+
+sub treeInfo {
+ my $self = shift;
+
+ my %REPLY = $self->sendMessage('iCommand' => $::PUKE_DUMPTREE,
+ 'iWinId' => $::PUKE_CONTROLLER,
+ 'CallBack' => sub { },
+ 'WaitFor' => 0);
+
+}
+
+
+sub DESTROY {
+ my $self = shift;
+
+ # print "*I* Widget Deleted\n";
+ eval{ $self->hide(); }; # Hide doesn't exist for all PBase's
+
+ # $self->setRunable(1);
+
+ delete($::PBASE_IMORTALS{$self->{IMMORTAL}});
+
+ if($self->{'Fetch'} != 1 && $self->{DESTROYED} != 1 && $self->{Parent} == 0){
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_DELETE,
+ 'CallBack' => sub {});
+ }
+
+ if($self->{'Fetch'} == 1){
+ $self->sendMessage('iCommand' => $::PUKE_RELEASEWIDGET,
+ 'CallBack' => sub {});
+
+ }
+
+ # $self->setRunable(0);
+ $self->{iWinId} = -1;
+ $self->{DESTROYED} = 1;
+
+}
+
+sub close {
+ my $self = shift;
+
+ $self->hide();
+
+ $self->DESTROY;
+
+}
+
+sub ackWinId {
+ my $self = shift;
+ my %ARG = @_;
+
+ if($ARG{'iWinId'} <= 1){
+ die("Failed on ack'ing Window ID, stopping!");
+ }
+
+ $self->{iWinId} = $ARG{'iWinId'};
+}
+
+
+sub installHandler {
+ my $self = shift;
+
+ my $command = shift;
+ my $handler = shift;
+
+ my $cmd =
+ sub {
+ $::PUKE_W_HANDLER{$command}{$self->{iWinId}} = $handler;
+ };
+
+ if($self->{iWinId} == -1){
+ $self->addQueue($cmd);
+ }
+ else{
+ &$cmd();
+ }
+
+}
+
+sub onNext {
+ my $self = shift;
+
+ my $cb = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_ECHO,
+ 'iArg' => 0,
+ 'iWinId' => $self->{iWinId},
+ 'cArg' => "",
+ 'CallBack' => $cb);
+}
+
+sub immortal {
+ my $self = shift;
+ $self->{IMMORTAL} = &rndchr;
+ $::PBASE_IMORTALS{$self->{IMMORTAL}} = $self;
+ return $self;
+}
+
+sub addQueue {
+ my $self = shift;
+
+ push(@{$self->{cmdQueue}}, shift());
+}
+
+sub clearQueue {
+ my $self = shift;
+
+ my $cmd;
+ while($cmd = pop(@{$self->{cmdQueue}})){
+ &$cmd;
+ }
+}
+
+package main;
+
+1;
diff --git a/ksirc/puke/pboxlayout.pm b/ksirc/puke/pboxlayout.pm
new file mode 100644
index 00000000..505ddc0d
--- /dev/null
+++ b/ksirc/puke/pboxlayout.pm
@@ -0,0 +1,201 @@
+
+package PBoxLayout;
+@ISA = qw(PBase);
+use strict;
+
+# setup default handlers
+
+$PBoxLayout::LeftToRight = 0;
+$PBoxLayout::RightToLeft = 1;
+$PBoxLayout::TopToBottom = 2;
+$PBoxLayout::BottomToTop = 3;
+
+$PBoxLayout::AlignLeft = 0x0001;
+$PBoxLayout::AlignRight = 0x0002;
+$PboxLayout::AlignHCenter = 0x0004;
+$PBoxLayout::AlignTop = 0x0008;
+$PBoxLayout::AlignBottom = 0x0010;
+$PBoxLayout::AlignVCenter = 0x0020;
+$PBoxLayout::AlignCenter = $PBoxLayout::AlignVCenter |
+ $PBoxLayout::AlignHCenter;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ my $widget = shift;
+
+ # print "Widget: " . ref($widget) . "\n";
+
+ # if(ref($widget) eq ''){
+ # print "*E* Error Creating PBoxLayout, did not give valid parent\n";
+ # return;
+ #}
+ # elsif(ref($widget) eq 'PBoxLayout'){
+ # $self->{Parent} = $widget;
+ # $self->{ParentType} = 'Layout';
+ # $self->{Direction} = shift;
+ # $self->{Border} = shift;
+ # $self->{Added} = 0;
+ #}
+ # else{
+ if(ref($widget) ne ''){
+ # print "*\cbI\cb* Generic Non-topleve layout type\n";
+ $self->{Parent} = $widget;
+ $self->{ParentType} = 'Widget';
+ $self->{Direction} = shift;
+ $self->{Border} = shift;
+ $self->{Added} = 1;
+ }
+ else{
+ $self->{Parent} = undef;
+ $self->{ParentType} = 'Layout';
+ $self->{Direction} = $widget;
+ $self->{Border} = shift;
+ $self->{Added} = 0;
+
+ }
+
+ $self->{IAmALayout} = 1;
+ $self->{Widgets} = ();
+
+ $self->create();
+
+ return $self;
+
+}
+
+sub create {
+ my $self = shift;
+
+ #
+ # PLayout redefines create since it uses a special cArg
+ #
+ my($paren_id) = 0;
+ $paren_id = $self->{Parent}->{iWinId} if $self->{Parent} != -1;
+
+ if($paren_id eq ''){
+ $paren_id = "0";
+ }
+
+ my $carg = $paren_id . "\t" . $::POBJECT_LAYOUT . "\t" . $self->{Direction} . "\t" . $self->{Border} . "\t" . $self->{initId},
+
+ my %REPLY;
+ %REPLY = $self->sendMessage('iCommand' => $::PUKE_WIDGET_CREATE,
+ 'iWinId' => $::PUKE_CONTROLLER,
+ 'cArg' => $carg,
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+ $self->ackWinId(%REPLY);
+
+}
+
+sub addWidget {
+ my $self = shift;
+
+ my $widget = shift;
+ my $stretch = shift;
+ my $align = shift;
+
+ if($self->{Added} == 0){
+ print "*E* Burp: Can't add widget without first being added to parent layout\n";
+ return;
+ }
+
+ $align = $PBoxLayout::AlignCenter if $align == undef;
+ $stretch = 0 if $stretch == undef;
+
+ # $widget->immortal(); # If it's a widget, it cannot be deleted
+ if($widget->{iWinId} <= 0){
+ print "*E* Trying to add invalid widget " . ref($widget) . "\n";
+ return;
+ }
+
+ my $cArg = pack("CC", $stretch, $align);
+
+ $self->sendMessage('iCommand' => $::PUKE_LAYOUT_ADDWIDGET,
+ 'iArg' => $widget->{iWinId},
+ 'cArg' => $cArg,
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+ $self->{Widgets}->[ $#{$self->{Widgets}} + 1] = $widget;
+
+}
+
+sub addLayout {
+ my $self = shift;
+
+ if($self->{Added} == 0){
+ print "*E* Burp: Can't add layout without first being added to parent layout\n";
+ }
+
+
+ my $layout = shift;
+ if(ref($layout) ne 'PBoxLayout'){
+ print "*E* Passed non layout type to addLayout\n";
+ return 1;
+ }
+
+ if($layout->{iWinId} <= 0){
+ print "*E* Trying to add invalid layout " . ref($layout) . "\n";
+ return;
+ }
+
+
+ # make sure we can run, and the widget we want to add can run.
+ # my @ARG = ($layout);
+ #$self->canRun($self, \&PBoxLayout::addLayout, \@ARG) || return;
+ #$layout->canRun($self, \&PBoxLayout::addLayout, \@ARG) || return;
+
+ my %REPLY = $self->sendMessage('iCommand' => $::PUKE_LAYOUT_ADDLAYOUT,
+ 'iWinId' => $self->{iWinId},
+ 'iArg' => $layout->{iWinId},
+ 'cArg' => pack("C", 0),
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+ # print "*I* Adding layout\n";
+ if($REPLY{'iArg'} != 0){
+ print "*E* AddLayout call failed\n";
+ }
+ else{
+ # print "*I* Added new Layout for " . $layout->{iWinId} . "\n";
+ $layout->{Added} = 1;
+ }
+
+}
+
+sub DESTROY {
+ my $self = shift;
+
+ # print "*I* Layout Deleted\n";
+
+ # if($self->{DESTROYED} != 1){
+ # $self->sendMessage('iCommand' => $::PUKE_WIDGET_DELETE,
+ # 'CallBack' => sub { print "Deleted\n"; });
+ #}
+
+ $self->{iWinId} = -1;
+ $self->{DESTROYED} = 1;
+
+}
+
+sub activate {
+ my $self = shift;
+
+ if($self->{ParentType} != 'Widget'){
+ print "*E* Only call for TopLevel managers\n";
+ return;
+ }
+
+ $self->sendMessage('iCommand' => $::PUKE_LAYOUT_ACTIVATE,
+ 'CallBack' => sub { },
+ 'WaitFor' => 1);
+
+}
+
+
+package main;
+1;
diff --git a/ksirc/puke/pbutton.cpp b/ksirc/puke/pbutton.cpp
new file mode 100644
index 00000000..309f2ac7
--- /dev/null
+++ b/ksirc/puke/pbutton.cpp
@@ -0,0 +1,157 @@
+#include <stdio.h>
+
+
+#include <qpixmap.h>
+
+#include <kdebug.h>
+
+#include "pbutton.h"
+
+PObject *
+PButton::createWidget(CreateArgs &ca)
+{
+ PButton *pb = new PButton(ca.parent);
+ QButton *qb;
+ if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ qb = new QButton((QWidget *) ca.parent->widget());
+ else
+ qb = new QButton();
+ pb->setWidget(qb);
+ pb->setWidgetId(ca.pwI);
+ return pb;
+}
+
+
+PButton::PButton(PObject *parent)
+ : PWidget(parent)
+{
+ // kdDebug(5008) << "PLineEdit PLineEdit called" << endl;
+ button = 0;
+ setWidget(0);
+}
+
+PButton::~PButton()
+{
+ // kdDebug(5008) << "PLineEdit: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ button=0; // Set it to 0
+ setWidget(button); // Now set all widget() calls to 0.
+ */
+}
+
+void PButton::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_BUTTON_SET_TEXT:
+ if(checkWidget() == FALSE)
+ return;
+
+ widget()->setText(pm->cArg); // set the text
+
+ pmRet.iCommand = - pm->iCommand;// Create ack
+ pmRet.iWinId = pm->iWinId;
+ pmRet.cArg = (char*) widget()->text().ascii(); // It's const, but we don't mess with it anyways
+ pmRet.iTextSize = strlen(pmRet.cArg);
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_BUTTON_SET_PIXMAP:
+ if(checkWidget() == FALSE)
+ return;
+
+ widget()->setPixmap(QPixmap(pm->cArg));
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->pixmap()->isNull();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_BUTTON_SET_AUTORESIZE:
+ if(checkWidget() == FALSE)
+ return;
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = - pm->iWinId;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+void PButton::setWidget(QObject *_qb)
+{
+ if(_qb != 0 && _qb->inherits("QButton") == FALSE)
+ {
+ errorInvalidSet(_qb);
+ return;
+ }
+
+ button = (QButton *) _qb;
+ if(button != 0){
+ connect(button, SIGNAL(pressed()),
+ this, SLOT(buttonPressed()));
+ connect(button, SIGNAL(released()),
+ this, SLOT(buttonReleased()));
+ connect(button, SIGNAL(clicked()),
+ this, SLOT(buttonClicked()));
+ connect(button, SIGNAL(toggled(bool)),
+ this, SLOT(buttonToggled(bool)));
+
+ }
+ PWidget::setWidget(_qb);
+
+}
+
+
+QButton *PButton::widget()
+{
+ return button;
+}
+
+void PButton::buttonMessage(int iCommand)
+{
+ PukeMessage pmRet;
+
+ pmRet.iCommand = iCommand;
+ pmRet.iArg = 0;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.cArg = 0;
+
+ emit outputMessage(widgetIden().fd, &pmRet);
+}
+
+void PButton::buttonPressed()
+{
+ buttonMessage(PUKE_BUTTON_PRESSED_ACK);
+}
+
+void PButton::buttonReleased()
+{
+ buttonMessage(PUKE_BUTTON_RELEASED_ACK);
+}
+
+void PButton::buttonClicked()
+{
+ buttonMessage(PUKE_BUTTON_CLICKED_ACK);
+}
+
+void PButton::buttonToggled(bool)
+{
+ buttonMessage(PUKE_BUTTON_TOGGLED_ACK);
+}
+
+bool PButton::checkWidget()
+{
+ if(widget() == 0){
+ kdDebug(5008) << "PButton: No Widget set" << endl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#include "pbutton.moc"
+
diff --git a/ksirc/puke/pbutton.h b/ksirc/puke/pbutton.h
new file mode 100644
index 00000000..e43bd681
--- /dev/null
+++ b/ksirc/puke/pbutton.h
@@ -0,0 +1,40 @@
+#ifndef PBUTTON_H
+#define PBUTTON_H
+
+class PButton;
+
+#include <qbutton.h>
+#include "pmessage.h"
+#include "pwidget.h"
+#include "pobject.h"
+#include "controller.h"
+
+
+class PButton : public PWidget
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PButton ( PObject * parent );
+ virtual ~PButton ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_qb = 0x0);
+ virtual QButton *widget();
+
+public slots:
+ void buttonPressed();
+ void buttonReleased();
+ void buttonClicked();
+ void buttonToggled(bool);
+
+private:
+ QButton *button;
+
+ void buttonMessage(int iCommand);
+ bool checkWidget();
+};
+
+#endif
diff --git a/ksirc/puke/pbutton.pm b/ksirc/puke/pbutton.pm
new file mode 100644
index 00000000..81e524fc
--- /dev/null
+++ b/ksirc/puke/pbutton.pm
@@ -0,0 +1,79 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_BUTTON,
+ "pbutton.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PButton Load failed!\n";
+ }
+ }
+ );
+
+package PButton;
+@ISA = qw(PWidget);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_BUTTON;
+
+ if($class eq 'PButton'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_BUTTON_PRESSED_ACK, sub{});
+ $self->installHandler($::PUKE_BUTTON_RELEASED_ACK, sub{});
+ $self->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub{});
+ $self->installHandler($::PUKE_BUTTON_TOGGLED_ACK, sub{});
+
+ return $self;
+
+}
+
+sub setText {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->{text} = $text;
+
+ $self->sendMessage('iCommand' => $::PUKE_BUTTON_SET_TEXT,
+ 'cArg' => $text,
+ 'CallBack' => sub {}
+ );
+}
+
+sub setPixmap {
+ my $self = shift;
+
+ my $file = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_BUTTON_SET_PIXMAP,
+ 'cArg' => $file,
+ 'CallBack' =>
+ sub {
+ my $arg = shift();
+ # if($arg == undef){ return; }
+ #my %ARG = %{shift()};
+ #if($ARG{'iArg'} == 0){
+ #print "*E* Pixmap set failed\n";
+ #}
+ }
+ );
+}
+
+sub setAutoResize {
+ my $self = shift;
+
+ my $resize = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_BUTTON_SET_PIXMAP,
+ 'iArg' => $resize,
+ 'CallBack' => sub {}
+ );
+}
+
+package main;
diff --git a/ksirc/puke/pframe.cpp b/ksirc/puke/pframe.cpp
new file mode 100644
index 00000000..fcff1d96
--- /dev/null
+++ b/ksirc/puke/pframe.cpp
@@ -0,0 +1,84 @@
+#include "pframe.h"
+
+
+PObject *
+PFrame::createWidget(CreateArgs &ca)
+{
+ PFrame *pw = new PFrame(ca.parent);
+ QFrame *tf;
+ if(ca.parent != 0 &&
+ ca.parent->widget()->isWidgetType() == TRUE)
+ tf = new QFrame((QWidget *) ca.parent->widget());
+ else
+ tf = new QFrame();
+ pw->setWidget(tf);
+ pw->setWidgetId(ca.pwI);
+ pw->setPukeController(ca.pc);
+ return pw;
+}
+
+
+PFrame::PFrame( PObject *parent)
+ : PWidget(parent)
+{
+ // kdDebug(5008) << "PFrame PFrame called" << endl;
+ frame = 0;
+}
+
+PFrame::~PFrame()
+{
+ // kdDebug(5008) << "PFrame: in destructor" << endl;
+ /*
+ delete frame; // Delete the frame
+ frame=0; // Set it to 0
+ setWidget(frame); // Now set all widget() calls to 0.
+ */
+}
+
+void PFrame::messageHandler(int fd, PukeMessage *pm)
+{
+ // kdDebug(5008) << "PFrame handler called" << endl;
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_QFRAME_SET_FRAME:
+ widget()->setFrameStyle(pm->iArg);
+ pmRet.iCommand = PUKE_QFRAME_SET_FRAME_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->frameStyle();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_QFRAME_SET_LINEWIDTH:
+ widget()->setLineWidth(pm->iArg);
+ pmRet.iCommand = PUKE_QFRAME_SET_LINEWIDTH_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->lineWidth();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+void PFrame::setWidget(QObject *w)
+{
+ if(w != 0 && w->inherits("QFrame") == FALSE)
+ {
+ errorInvalidSet(w);
+ return;
+ }
+
+ frame = (QFrame *) w;
+ PWidget::setWidget((QWidget *) w);
+}
+
+
+QFrame *PFrame::widget()
+{
+ // kdDebug(5008) << "PFrame widget called" << endl;
+ return frame;
+}
+
+#include "pframe.moc"
+
diff --git a/ksirc/puke/pframe.h b/ksirc/puke/pframe.h
new file mode 100644
index 00000000..e98750aa
--- /dev/null
+++ b/ksirc/puke/pframe.h
@@ -0,0 +1,28 @@
+#ifndef PFRAME_H
+#define PFRAME_H
+
+class PFrame;
+
+#include <qframe.h>
+#include "pmessage.h"
+#include "pwidget.h"
+
+class PFrame : public PWidget
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PFrame ( PObject * parent );
+ virtual ~PFrame ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *w = 0x0);
+ virtual QFrame *widget();
+
+private:
+ QFrame *frame;
+};
+
+#endif
diff --git a/ksirc/puke/pframe.pm b/ksirc/puke/pframe.pm
new file mode 100644
index 00000000..f851cdb8
--- /dev/null
+++ b/ksirc/puke/pframe.pm
@@ -0,0 +1,57 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_FRAME,
+ "pframe.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PFrame Load failed!\n";
+ }
+ }
+ );
+
+package PFrame;
+@ISA = qw(PWidget);
+use strict;
+
+$PFrame::NoFrame = 0;
+$PFrame::Box = 0x0001;
+$PFrame::Panel = 0x0002;
+$PFrame::WinPanel = 0x0003;
+$PFrame::HLine = 0x0004;
+$PFrame::VLine = 0x0005;
+$PFrame::MShape = 0x000f;
+$PFrame::Plain = 0x0010;
+$PFrame::Raised = 0x0020;
+$PFrame::Sunken = 0x0030;
+$PFrame::MShadow = 0x00f0;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_FRAME;
+
+ if($class eq 'PFrame'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub setFrameStyle {
+ my $self = shift;
+
+ my $frame = shift;
+ my $repaint = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_QFRAME_SET_FRAME,
+ 'iArg' => $frame,
+ 'CallBack' => sub {});
+
+ $self->repaint(1) if($repaint == 1);
+
+}
+
+package main;
diff --git a/ksirc/puke/pkfiledialog-cmd.h b/ksirc/puke/pkfiledialog-cmd.h
new file mode 100644
index 00000000..6aebd59e
--- /dev/null
+++ b/ksirc/puke/pkfiledialog-cmd.h
@@ -0,0 +1,56 @@
+#ifndef PKFILEDIALOG_CMD_H
+#define PKFILEDIALOG_CMD_H
+
+/*
+ * We get 2200
+ */
+
+// Desc: sets the current directory
+// iWinId: widget
+// iArg: not defined
+// cArg: path
+#define PUKE_KBFD_SET_PATH 2200
+
+// Desc: ack for the set
+// iWinId: widget
+// iArg: not defined
+// cArg: actual path
+#define PUKE_BFD_SET_PATH_ACK -2200
+
+// Desc: sets the current filter
+// iWinId: widget
+// iArg: not defined
+// cArg: filter, as defined in KFileBaseDialog::setFilter
+#define PUKE_KBFD_SET_FILTER 2201
+
+// Desc: ack for the set
+// iWinId: widget
+// iArg: not defined
+// cArg: not define
+#define PUKE_KBFD_SET_FILTER_ACK -2201
+
+// Desc: sets the current file
+// iWinId: widget
+// iArg: not defined
+// cArg: filename (does it need the full path?)
+#define PUKE_KBFD_SET_SELECTION 2202
+
+// Desc: ack for the set
+// iWinId: widget
+// iArg: not defined
+// cArg: not define
+#define PUKE_KBFD_SET_SELECTION_ACK -2202
+
+// Desc: ack for current file select
+// iWinId: not defined
+// iArg: not defined
+// cArg: not defined
+#define PUKE_KBFD_FILE_SELECTED 2203
+
+// Desc: ack for current file select
+// iWinId: widget
+// iArg: not defined
+// cArg: filename
+#define PUKE_KBFD_FILE_SELECTED_ACK -2203
+
+#endif
diff --git a/ksirc/puke/pkfiledialog.cpp b/ksirc/puke/pkfiledialog.cpp
new file mode 100644
index 00000000..c99aea9f
--- /dev/null
+++ b/ksirc/puke/pkfiledialog.cpp
@@ -0,0 +1,123 @@
+#include <stdio.h>
+
+
+#include <kdebug.h>
+
+#include "pkfiledialog.h"
+#include "pkfiledialog-cmd.h"
+
+PObject *
+PKFileDialog::createWidget(CreateArgs &ca)
+{
+ PKFileDialog *pw = new PKFileDialog(ca.parent);
+ KFileDialog *kfbd;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("KFileDialog") == TRUE){
+ kfbd = (KFileDialog *) ca.fetchedObj;
+ pw->setDeleteAble(FALSE);
+ }
+ else // Never takes a parent in Puke
+ kfbd = new KFileDialog("/", QString::null, 0L, "PukeKFileDialog", TRUE);
+ pw->setWidget(kfbd);
+ pw->setWidgetId(ca.pwI);
+ return pw;
+}
+
+
+PKFileDialog::PKFileDialog(PObject *parent)
+ : PWidget(parent)
+{
+ kfbd = 0;
+ setWidget(kfbd);
+}
+
+PKFileDialog::~PKFileDialog()
+{
+ // kdDebug(5008) << "PLineEdit: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ kfbd=0; // Set it to 0
+ setWidget(kfbd); // Now set all widget() calls to 0.
+ */
+}
+
+void PKFileDialog::messageHandler(int fd, PukeMessage *pm)
+{
+ QString selFile;
+ PukeMessage pmRet;
+ if(widget() == 0){
+ kdDebug(5008) << "PKFileDialog: No Widget set" << endl;
+ return;
+ }
+ switch(pm->iCommand){
+ case PUKE_KBFD_SET_PATH:
+ ((KFileDialog*)widget())->setURL(KURL(pm->cArg));
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = widget()->baseURL().path().length();
+#warning check if the cast is okay
+ pmRet.cArg = (char*) widget()->baseURL().path().ascii();
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_KBFD_SET_FILTER:
+ widget()->setFilter(pm->cArg);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_KBFD_SET_SELECTION:
+ widget()->setSelection(pm->cArg);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ selFile = widget()->selectedURL().path();
+ pmRet.iTextSize = selFile.length();
+#warning check if the cast is okay
+ pmRet.cArg = (char*) selFile.ascii();
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_SHOW:
+ widget()->exec();
+ pmRet.iCommand = PUKE_KBFD_FILE_SELECTED_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = new char[selFile.length()];
+ selFile = widget()->selectedURL().path();
+ // #### HPB: using strlen() 'cause we want the length of the .ascii()
+ // string. We should probably replace in the future.
+ memcpy(pmRet.cArg, selFile.ascii(), strlen(selFile.ascii()));
+ pmRet.iTextSize = selFile.length();
+ emit outputMessage(widgetIden().fd, &pmRet);
+ delete pmRet.cArg;
+ break;
+
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+void PKFileDialog::setWidget(QObject *_kbfd)
+{
+ if(_kbfd != 0 && _kbfd->inherits("KFileBaseDialog") == FALSE)
+ {
+ errorInvalidSet(_kbfd);
+ return;
+ }
+
+ kfbd = (KFileDialog *) _kbfd;
+ PWidget::setWidget(kfbd);
+}
+
+
+KFileDialog *PKFileDialog::widget()
+{
+ return kfbd;
+}
+
+#include "pkfiledialog.moc"
diff --git a/ksirc/puke/pkfiledialog.h b/ksirc/puke/pkfiledialog.h
new file mode 100644
index 00000000..c8dbea3f
--- /dev/null
+++ b/ksirc/puke/pkfiledialog.h
@@ -0,0 +1,31 @@
+#ifndef PKFILEDIALOG_H
+#define PKFILEDIALOG_H
+
+class PKFileDialog;
+
+#include <kfiledialog.h>
+#include "pmessage.h"
+#include "pwidget.h"
+#include "controller.h"
+
+class PKFileDialog : public PWidget
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PKFileDialog ( PObject * parent );
+ virtual ~PKFileDialog ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_f = 0x0);
+ virtual KFileDialog *widget();
+
+public slots:
+
+private:
+ KFileDialog *kfbd;
+};
+
+#endif
diff --git a/ksirc/puke/pkfiledialog.pm b/ksirc/puke/pkfiledialog.pm
new file mode 100644
index 00000000..6da8c229
--- /dev/null
+++ b/ksirc/puke/pkfiledialog.pm
@@ -0,0 +1,76 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $PWIDGET_KFILEDIALOG,
+ "pkfiledialog.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PKFileDialog Load failed!\n";
+ }
+ }
+ );
+
+use strict;
+
+package PKFileDialog;
+use vars qw(@ISA);
+@ISA = qw(PWidget);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_KFILEDIALOG;
+
+ if($class eq 'PKFileDialog'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_KBFD_FILE_SELECTED_ACK, sub{$self->fileSelected(shift())});
+
+ return $self;
+
+}
+
+sub setDir {
+ my $self = shift;
+
+ my $dir = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KBFD_SET_PATH,
+ 'cArg' => $dir,
+ 'CallBack' => sub{});
+
+}
+
+sub setFilter {
+ my $self = shift;
+
+ my $filter = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KBFD_SET_FILTER,
+ 'cArg' => $filter,
+ 'CallBack' => sub{});
+
+}
+
+sub setSelected {
+ my $self = shift;
+
+ my $sel = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KBFD_SET_SELECTION,
+ 'cArg' => $sel,
+ 'CallBack' => sub{});
+
+}
+
+sub fileSelected {
+ my $self = shift;
+ my $rargs = shift;
+
+ &::print("*I* File Selected: " . $rargs->{'cArg'});
+
+}
+
+package main;
diff --git a/ksirc/puke/plabel.cpp b/ksirc/puke/plabel.cpp
new file mode 100644
index 00000000..17ca851d
--- /dev/null
+++ b/ksirc/puke/plabel.cpp
@@ -0,0 +1,127 @@
+#include <qmovie.h>
+
+
+#include <kdebug.h>
+
+#include "plabel.h"
+
+PObject *
+PLabel::createWidget(CreateArgs &ca)
+{
+ PLabel *pw = new PLabel(ca.parent);
+ QLabel *le;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("QLabel") == TRUE){
+ le = (QLabel *) ca.fetchedObj;
+ pw->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ le = new QLabel((QWidget *) ca.parent->widget());
+ else
+ le = new QLabel((QWidget *)0L);
+ pw->setWidget(le);
+ pw->setWidgetId(ca.pwI);
+ return pw;
+}
+
+
+PLabel::PLabel(PObject *parent)
+ : PFrame(parent)
+{
+ // kdDebug(5008) << "PLabel PLabel called" << endl;
+ label = 0;
+ setWidget(label);
+}
+
+PLabel::~PLabel()
+{
+ // kdDebug(5008) << "PLabel: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ label=0; // Set it to 0
+ setWidget(label); // Now set all widget() calls to 0.
+ */
+}
+
+void PLabel::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_LABEL_SETTEXT:
+ if(!checkWidget())
+ return;
+
+ widget()->setText(pm->cArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LABEL_SETPIXMAP:
+ if(!checkWidget())
+ return;
+
+ widget()->setPixmap(QPixmap(pm->cArg));
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LABEL_SETMOVIE:
+ if(!checkWidget())
+ return;
+
+ widget()->setMovie(QMovie(pm->cArg));
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LABEL_SETALIGNMENT:
+ if(!checkWidget())
+ return;
+
+ widget()->setAlignment(pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PFrame::messageHandler(fd, pm);
+ }
+}
+
+void PLabel::setWidget(QObject *_l)
+{
+ if(_l != 0 && _l->inherits("QLabel") == FALSE)
+ {
+ errorInvalidSet(_l);
+ return;
+ }
+
+ label = (QLabel *) _l;
+ PWidget::setWidget(_l);
+
+}
+
+
+QLabel *PLabel::widget()
+{
+ return label;
+}
+
+bool PLabel::checkWidget(){
+ if(widget() == 0){
+ kdDebug(5008) << "PLabel: No Widget set" << endl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+#include "plabel.moc"
+
diff --git a/ksirc/puke/plabel.h b/ksirc/puke/plabel.h
new file mode 100644
index 00000000..c53705b1
--- /dev/null
+++ b/ksirc/puke/plabel.h
@@ -0,0 +1,34 @@
+#ifndef PLABEL_H
+#define PLABEL_H
+
+class PLabel;
+
+#include <qlabel.h>
+#include "pmessage.h"
+#include "pframe.h"
+#include "controller.h"
+
+class PLabel : public PFrame
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PLabel ( PObject * parent );
+ virtual ~PLabel ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_obj = 0);
+ virtual QLabel *widget();
+
+public slots:
+
+protected:
+ bool checkWidget();
+
+private:
+ QLabel *label;
+};
+
+#endif
diff --git a/ksirc/puke/plabel.pm b/ksirc/puke/plabel.pm
new file mode 100644
index 00000000..bc557b5e
--- /dev/null
+++ b/ksirc/puke/plabel.pm
@@ -0,0 +1,105 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $PWIDGET_LABEL,
+ "plabel.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PLabel Load failed!\n";
+ }
+ }
+ );
+
+package PLabel;
+@ISA = qw(PFrame);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_LABEL;
+
+ if($class eq 'PLabel'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub setText {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->{text} = $text;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LABEL_SETTEXT,
+ 'iArg' => 0,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub setPixmap {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->{text} = "***PIXMAP***" . $text;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LABEL_SETPIXMAP,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub setMovie {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->{text} = "***MOVIE***" . $text;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LABEL_SETMOVIE,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+
+sub text {
+ my $self = shift;
+
+ return $self->{text};
+}
+
+
+
+sub setAlignment {
+ my $self = shift;
+
+ my $align = shift;
+
+
+ $self->{align} = $align;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LABEL_SETALIGNMENT,
+ 'iArg' => $align,
+ 'CallBack' => sub {});
+
+}
+
+
+
+package main;
+
diff --git a/ksirc/puke/playout.cpp b/ksirc/puke/playout.cpp
new file mode 100644
index 00000000..9690f418
--- /dev/null
+++ b/ksirc/puke/playout.cpp
@@ -0,0 +1,157 @@
+#include <stdio.h>
+
+
+#include "playout.h"
+#include "commands.h"
+
+PLayout::PLayout(QObject *pobject)
+ : PObject(pobject)
+{
+ // Connect slots as needed
+ setWidget();
+}
+
+PLayout::~PLayout()
+{
+ // kdDebug(5008) << "PObject: in destructor" << endl;
+ /*
+ delete widget();
+ layout = 0;
+ setWidget();
+ */
+}
+
+PObject *PLayout::createWidget(CreateArgs &ca)
+{
+ PLayout *pw = new PLayout(ca.parent);
+ QBoxLayout *qbl;
+ int direction, border, iType, iParent;
+ // Retreive the border and direction information out of the
+ // carg string
+ if(sscanf(ca.pm->cArg, "%d\t%d\t%d\t%d", &iParent, &iType, &direction, &border) < 4)
+ throw(errorCommandFailed(-ca.pm->iCommand, -1));
+
+ if((ca.parent != 0) &&
+ (ca.parent->widget()->isWidgetType() == TRUE)){
+ qbl = new QBoxLayout((QWidget *) ca.parent->widget(), (QBoxLayout::Direction) direction, border);
+ // kdDebug(5008) << "Creating layout with parent: " << parent.iWinId << endl;
+
+ }
+ else{
+ qbl = new QBoxLayout((QBoxLayout::Direction) direction, border);
+ // kdDebug(5008) << "Creating layout NO PARENT" << endl;
+ }
+ pw->setWidget(qbl);
+ pw->setWidgetId(ca.pwI);
+ pw->setPukeController(ca.pc);
+ return pw;
+}
+
+void PLayout::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+
+// kdDebug(5008) << "In PLayout: " << pm->iCommand << endl;
+
+ if(pm->iCommand == PUKE_LAYOUT_ADDWIDGET){
+ if(pm->iTextSize != 2*sizeof(char)){
+ qWarning("PLayout/addwidget: incorrent cArg size, bailing out. Needed: %u wanted: %d\n", sizeof(int), pm->iTextSize);
+ pmRet.iCommand = PUKE_LAYOUT_ADDWIDGET_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 1;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+ widgetId wiWidget;
+ wiWidget.fd = fd;
+ wiWidget.iWinId = pm->iArg;
+ PWidget *pw = controller()->id2pwidget(&wiWidget);
+ // kdDebug(5008) << "Adding widget with stretch: " << (int) pm->cArg[0] << " and align: " << // (int) pm->cArg[1] << endl;
+ widget()->addWidget(pw->widget(), pm->cArg[0], pm->cArg[1]);
+
+ pmRet.iCommand = PUKE_LAYOUT_ADDWIDGET_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else if(pm->iCommand == PUKE_LAYOUT_ADDLAYOUT){
+ if(pm->iTextSize != sizeof(char)){
+ qWarning("PLayout: incorrent cArg size, bailing out. Needed: %u wanted: %d\n", sizeof(int), pm->iTextSize);
+ pmRet.iCommand = PUKE_LAYOUT_ADDLAYOUT_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 1;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+ PObject *pld = controller()->id2pobject(fd, pm->iWinId);
+ PObject *pls = controller()->id2pobject(fd, pm->iArg);
+ if( (pld->widget()->inherits("QBoxLayout") == FALSE) || (pls->widget()->inherits("QBoxLayout") == FALSE))
+ throw(errorCommandFailed(PUKE_LAYOUT_ADDLAYOUT_ACK, 1));
+ PLayout *plbd = (PLayout *) pld;
+ PLayout *plbs = (PLayout *) pls;
+ plbd->widget()->addLayout(plbs->widget(), pm->cArg[0]);
+
+ pmRet.iCommand = PUKE_LAYOUT_ADDLAYOUT_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else if(pm->iCommand == PUKE_LAYOUT_ADDSTRUT){
+ PObject *po = controller()->id2pobject(fd, pm->iWinId);
+ if(po->widget()->inherits("PBoxLayout") != TRUE)
+ throw(errorCommandFailed(PUKE_LAYOUT_ADDSTRUT_ACK, 1));
+ PLayout *pl = (PLayout *) po;
+
+ pl->widget()->addStrut(pm->iArg);
+
+ pmRet.iCommand = PUKE_LAYOUT_ADDSTRUT_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else if(pm->iCommand == PUKE_LAYOUT_ACTIVATE){
+ PObject *po = controller()->id2pobject(fd, pm->iWinId);
+ if(po->widget()->inherits("PBoxLayout") != TRUE)
+ throw(errorCommandFailed(PUKE_LAYOUT_ACTIVATE_ACK, 1));
+ PLayout *pl = (PLayout *) po;
+
+ pmRet.iArg = 0; // setup failure case
+ pl->widget()->activate();
+
+ pmRet.iCommand = PUKE_LAYOUT_ACTIVATE_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else {
+ PObject::messageHandler(fd, pm);
+ }
+
+}
+
+void PLayout::setWidget(QObject *_layout)
+{
+ // kdDebug(5008) << "PObject setwidget called" << endl;
+ if(_layout != 0 && _layout->inherits("QBoxLayout") == FALSE)
+ {
+ errorInvalidSet(_layout);
+ return;
+ }
+
+ layout = (QBoxLayout *) _layout;
+ PObject::setWidget(_layout);
+
+}
+
+QBoxLayout *PLayout::widget()
+{
+ return layout;
+}
+
+
+#include "playout.moc"
+
diff --git a/ksirc/puke/playout.h b/ksirc/puke/playout.h
new file mode 100644
index 00000000..4aec01a1
--- /dev/null
+++ b/ksirc/puke/playout.h
@@ -0,0 +1,42 @@
+#ifndef PLAYOUT_H
+#define PLAYOUT_H
+
+class PLayout;
+
+#include <qobject.h>
+#include <qlayout.h>
+#include "pmessage.h"
+#include "pobject.h"
+
+class PLayout : public PObject
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PLayout(QObject *parent = 0);
+ virtual ~PLayout();
+
+ /**
+ * Handles messages from dsirc
+ * PObject can't get messages so return an error
+ */
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ /**
+ * Sets the current opbect
+ */
+ virtual void setWidget(QObject *qb = 0x0);
+
+ /**
+ * Returns the current object
+ */
+ virtual QBoxLayout *widget();
+
+private:
+
+ QBoxLayout *layout;
+
+};
+
+#endif
diff --git a/ksirc/puke/plined.cpp b/ksirc/puke/plined.cpp
new file mode 100644
index 00000000..3f3e6a09
--- /dev/null
+++ b/ksirc/puke/plined.cpp
@@ -0,0 +1,153 @@
+#include <stdio.h>
+
+
+#include <kdebug.h>
+
+#include "plined.h"
+
+PObject *
+PLineEdit::createWidget(CreateArgs &ca)
+{
+ PLineEdit *pw = new PLineEdit(ca.parent);
+ QLineEdit *le;
+ if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ le = new QLineEdit((QWidget *) ca.parent->widget());
+ else
+ le = new QLineEdit(0L);
+ pw->setWidget(le);
+ pw->setWidgetId(ca.pwI);
+ return pw;
+}
+
+
+PLineEdit::PLineEdit(PObject *parent)
+ : PWidget(parent)
+{
+ // kdDebug(5008) << "PLineEdit PLineEdit called" << endl;
+ lineedit = 0;
+ setWidget(lineedit);
+}
+
+PLineEdit::~PLineEdit()
+{
+ // kdDebug(5008) << "PLineEdit: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ lineedit=0; // Set it to 0
+ setWidget(lineedit); // Now set all widget() calls to 0.
+ */
+}
+
+void PLineEdit::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_LINED_SET_MAXLENGTH:
+ if(widget() == 0){
+ kdDebug(5008) << "PLineEdit: No Widget set" << endl;
+ return;
+ }
+ widget()->setMaxLength(pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->maxLength();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LINED_SET_ECHOMODE:
+ if(widget() == 0){
+ kdDebug(5008) << "PLineEdit: No Widget set" << endl;
+ return;
+ }
+ widget()->setEchoMode((QLineEdit::EchoMode) pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->echoMode();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LINED_SET_TEXT:
+ if(widget() == 0){
+ kdDebug(5008) << "PLineEdit: No Widget set" << endl;
+ return;
+ }
+ kdDebug(5008) << "PukeLine Edit: Got: " << pm->cArg << endl;
+ widget()->setText(pm->cArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = strlen(widget()->text());
+ pmRet.cArg = new char[strlen(widget()->text())+1];
+ strcpy(pmRet.cArg, widget()->text());
+ emit outputMessage(fd, &pmRet);
+ delete[] pmRet.cArg;
+ break;
+ case PUKE_LINED_GET_TEXT:
+ if(widget() == 0){
+ kdDebug(5008) << "PLineEdit: No Widget set" << endl;
+ return;
+ }
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = strlen(widget()->text());
+ pmRet.cArg = new char[strlen(widget()->text())+1];
+ strcpy(pmRet.cArg, widget()->text());
+ emit outputMessage(fd, &pmRet);
+ delete[] pmRet.cArg;
+ break;
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+void PLineEdit::setWidget(QObject *_le)
+{
+ if(_le != 0 && _le->inherits("QLineEdit") == FALSE)
+ {
+ errorInvalidSet(_le);
+ return;
+ }
+
+ lineedit = (QLineEdit *) _le;
+ if(lineedit != 0){
+ connect(lineedit, SIGNAL(textChanged(const char *)),
+ this, SLOT(updateText(const char *)));
+ connect(lineedit, SIGNAL(returnPressed()),
+ this, SLOT(returnPress()));
+ }
+ PWidget::setWidget(_le);
+
+}
+
+
+QLineEdit *PLineEdit::widget()
+{
+ return lineedit;
+}
+
+void PLineEdit::updateText(const char *text){
+ PukeMessage pmRet;
+
+ pmRet.iCommand = PUKE_LINED_GET_TEXT_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = strlen(text);
+ pmRet.cArg = new char[strlen(text)+1];
+ strcpy(pmRet.cArg, text);
+ emit outputMessage(widgetIden().fd, &pmRet);
+ delete[] pmRet.cArg;
+}
+
+void PLineEdit::returnPress() {
+ PukeMessage pmRet;
+
+ pmRet.iCommand = PUKE_LINED_RETURN_PRESSED_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(widgetIden().fd, &pmRet);
+}
+
+#include "plined.moc"
+
diff --git a/ksirc/puke/plined.h b/ksirc/puke/plined.h
new file mode 100644
index 00000000..78f04bdf
--- /dev/null
+++ b/ksirc/puke/plined.h
@@ -0,0 +1,33 @@
+#ifndef PLINEEDIT_H
+#define PLINEEDIT_H
+
+class PLineEdit;
+
+#include <qlineedit.h>
+#include "pmessage.h"
+#include "pwidget.h"
+#include "controller.h"
+
+class PLineEdit : public PWidget
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PLineEdit ( PObject * parent );
+ virtual ~PLineEdit ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_f);
+ virtual QLineEdit *widget();
+
+public slots:
+ void updateText(const char *);
+ void returnPress();
+
+private:
+ QLineEdit *lineedit;
+};
+
+#endif
diff --git a/ksirc/puke/plined.pm b/ksirc/puke/plined.pm
new file mode 100644
index 00000000..fbff6f96
--- /dev/null
+++ b/ksirc/puke/plined.pm
@@ -0,0 +1,84 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $PWIDGET_LINED,
+ "plined.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PLineEdit Load failed!\n";
+ }
+ }
+ );
+
+package PLineEdit;
+@ISA = qw(PWidget);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_LINED;
+ $self->{maxLength} = -1;
+
+ if($class eq 'PLineEdit'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_WIDGET_EVENT_TIMER, sub{});
+ $self->installHandler($::PUKE_LINED_GET_TEXT_ACK, sub{
+ my %ARG = %{shift()};
+ $ARG{'cArg'} =~ s/^([^\000]*).*/$1/;
+ $self->{text} = $ARG{'cArg'};});
+
+ return $self;
+
+}
+
+sub setMaxLength {
+ my $self = shift;
+
+ my $length = shift;
+
+ $self->{maxLength} = $length;
+ $self->sendMessage('iCommand' => $::PUKE_LINED_SET_MAXLENGTH,
+ 'iArg' => $length,
+ 'CallBack' => sub {my %ARG = %{shift()};
+ $self->{maxLength} = $ARG{'iArg'};});
+
+}
+
+sub setEchoMode {
+ my $self = shift;
+
+ my $mode = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_LINED_SET_ECHOMODE,
+ 'iArg' => $mode,
+ 'CallBack' => sub {my %ARG = %{shift()};
+ $self->{echoMode} = $ARG{'iArg'};});
+
+}
+
+sub setText {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->{text} = $text;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LINED_SET_TEXT,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub text {
+ my $self = shift;
+
+ return $self->{text};
+}
+
+package main;
diff --git a/ksirc/puke/plistbox.cpp b/ksirc/puke/plistbox.cpp
new file mode 100644
index 00000000..482e08c7
--- /dev/null
+++ b/ksirc/puke/plistbox.cpp
@@ -0,0 +1,224 @@
+#include <kdebug.h>
+
+
+#include "plistbox.h"
+
+PObject *
+PListBox::createWidget(CreateArgs &ca)
+{
+ PListBox *plb = new PListBox(ca.parent);
+ QListBox *lb;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("QListBox") == TRUE){
+ lb = (QListBox *) ca.fetchedObj;
+ plb->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ lb = new QListBox((QWidget *) ca.parent->widget());
+ else
+ lb = new QListBox();
+ plb->setWidget(lb);
+ plb->setWidgetId(ca.pwI);
+ return plb;
+}
+
+
+PListBox::PListBox(PObject *parent)
+ : PFrame(parent)
+{
+ // kdDebug(5008) << "PListBox PListBox called" << endl;
+ lb = 0;
+ setWidget(lb);
+}
+
+PListBox::~PListBox()
+{
+ // kdDebug(5008) << "PListBox: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ lb=0; // Set it to 0
+ setWidget(lb); // Now set all widget() calls to 0.
+ */
+}
+
+void PListBox::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_LISTBOX_INSERT:
+ if(!checkWidget())
+ return;
+
+ widget()->insertItem(pm->cArg, pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->count();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_INSERT_SORT:
+ if(!checkWidget())
+ return;
+
+ widget()->insertItem(pm->cArg);
+ widget()->sort();
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->count();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_INSERT_PIXMAP:
+ if(!checkWidget())
+ return;
+
+ widget()->insertItem(QPixmap(pm->cArg), pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->count();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_HIGHLIGHT:
+ if(!checkWidget())
+ return;
+
+ widget()->setCurrentItem(pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->currentItem();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_REMOVE:
+ if(!checkWidget())
+ return;
+
+ widget()->removeItem(pm->iArg);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_GETTEXT:
+ if(!checkWidget())
+ return;
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ if(widget()->text(pm->iArg) != 0x0){
+ pmRet.iArg = 1;
+ pmRet.iTextSize = strlen(widget()->text(pm->iArg));
+ pmRet.cArg = new char[strlen(widget()->text(pm->iArg))+1];
+ strcpy(pmRet.cArg, widget()->text(pm->iArg));
+ emit outputMessage(fd, &pmRet);
+ delete[] pmRet.cArg;
+ }
+ else{
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ break;
+ case PUKE_LISTBOX_SET_SCROLLBAR:
+ widget()->setVScrollBarMode( (bool) pm->iArg ? QListBox::AlwaysOn : QListBox::AlwaysOff );
+ pmRet.iCommand = PUKE_LISTBOX_SET_SCROLLBAR_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_SET_AUTO_SCROLLBAR:
+ widget()->setVScrollBarMode( (bool) pm->iArg ? QListBox::Auto : QListBox::AlwaysOff );
+ pmRet.iCommand = PUKE_LISTBOX_SET_AUTO_SCROLLBAR_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_LISTBOX_CLEAR:
+ widget()->clear();
+ pmRet.iCommand = PUKE_LISTBOX_CLEAR_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PFrame::messageHandler(fd, pm);
+ }
+}
+
+void PListBox::setWidget(QObject *_lb)
+{
+ if(_lb != 0 && _lb->inherits("QListBox") == FALSE)
+ {
+ errorInvalidSet(_lb);
+ return;
+ }
+
+ lb = (QListBox *) _lb;
+ if(lb != 0){
+ connect(lb, SIGNAL(highlighted(int)),
+ this, SLOT(highlighted(int)));
+ connect(lb, SIGNAL(selected(int)),
+ this, SLOT(selected(int)));
+ }
+ PFrame::setWidget(lb);
+
+}
+
+
+QListBox *PListBox::widget()
+{
+ return lb;
+}
+
+
+void PListBox::highlighted(int index) {
+ PukeMessage pmRet;
+
+ kdDebug(5008) << "Got highlight" << endl;
+ pmRet.iCommand = PUKE_LISTBOX_HIGHLIGHTED_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = index;
+ if(widget()->text(index) != 0){
+ pmRet.iTextSize = strlen(widget()->text(index));
+ pmRet.cArg = new char[strlen(widget()->text(index)) + 1];
+ strcpy(pmRet.cArg, widget()->text(index));
+ }
+ else
+ pmRet.cArg = 0;
+ emit outputMessage(widgetIden().fd, &pmRet);
+ delete[] pmRet.cArg;
+}
+
+void PListBox::selected(int index) {
+ PukeMessage pmRet;
+
+ kdDebug(5008) << "Got selected" << endl;
+ pmRet.iCommand = PUKE_LISTBOX_SELECTED_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = index;
+ if(widget()->text(index) != 0){
+ pmRet.iTextSize = strlen(widget()->text(index));
+ pmRet.cArg = new char[strlen(widget()->text(index)) + 1];
+ strcpy(pmRet.cArg, widget()->text(index));
+ }
+ else
+ pmRet.cArg = 0;
+ emit outputMessage(widgetIden().fd, &pmRet);
+ delete[] pmRet.cArg;
+}
+
+bool PListBox::checkWidget(){
+ if(widget() == 0){
+ kdDebug(5008) << "PListBox: No Widget set" << endl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#include "plistbox.moc"
+
diff --git a/ksirc/puke/plistbox.h b/ksirc/puke/plistbox.h
new file mode 100644
index 00000000..cad454a8
--- /dev/null
+++ b/ksirc/puke/plistbox.h
@@ -0,0 +1,37 @@
+#ifndef PLISTBOX_H
+#define PLISTBOX_H
+
+class PListBox;
+
+#include <qlistbox.h>
+#include "pmessage.h"
+#include "ptablevw.h"
+#include "controller.h"
+
+class PListBox : public PFrame
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PListBox ( PObject * parent );
+ virtual ~PListBox ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_lb);
+ virtual QListBox *widget();
+
+public slots:
+ void highlighted(int);
+ void selected(int);
+
+protected:
+ bool checkWidget();
+
+private:
+ QListBox *lb;
+
+};
+
+#endif
diff --git a/ksirc/puke/plistbox.pm b/ksirc/puke/plistbox.pm
new file mode 100644
index 00000000..2e7af417
--- /dev/null
+++ b/ksirc/puke/plistbox.pm
@@ -0,0 +1,196 @@
+
+&::PukeSendMessage($::PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $::PWIDGET_LISTBOX,
+ "plistbox.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PListBox Load failed!\n";
+ }
+ }
+ );
+
+
+package PListBox;
+@ISA = qw(PTableView);
+use strict;
+
+if($PListBox::usage == undef){
+ $PListBox::usage = 0;
+}
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_LISTBOX;
+
+ if($class eq 'PListBox'){
+ $self->create();
+ }
+
+ $self->{count} = 0;
+ $self->{items} = ();
+
+ $self->installHandler($::PUKE_LISTBOX_SELECTED_ACK, sub{$self->selected(@_)});
+
+ return $self;
+
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->SUPER::DESTROY(@_);
+ $PListBox::usage--;
+ if($PListBox::usage == 0){
+ &::PukeSendMessage($::PUKE_WIDGET_UNLOAD,
+ 0,
+ $::PWIDGET_LISTBOX,
+ "",
+ sub {}
+ );
+
+ }
+}
+
+sub insertText {
+ my $self = shift;
+
+ my $text = shift;
+ my $index = shift;
+ my $rindex = $index;
+
+ if($index < 0 || $index >= $self->{count}){
+ $rindex = $self->{count};
+ }
+
+ $self->{count} ++;
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_INSERT,
+ 'iArg' => $rindex,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub text {
+ my $self = shift;
+ my $index = shift;
+
+ my %arg = $self->sendMessage('iCommand' => $::PUKE_LISTBOX_GETTEXT,
+ 'iArg' => $index,
+ 'WaitFor' => 1);
+
+ if($arg{'iArg'} != 1){
+ return undef;
+ }
+ $arg{'cArg'} =~ s/\000//g;
+ return $arg{'cArg'};
+}
+
+sub insertPixmap {
+ my $self = shift;
+
+ my $file = shift;
+ my $index = shift;
+ my $rindex = $index;
+
+ if($index < 0 || $index >= $self->{count}){
+ $rindex = $self->{count};
+ }
+ # $self->{items}->[$rindex] = "***PIXMAP***" . $file;
+ $self->{count} ++;
+
+
+ # Don't need the ouput since GET_TEXT_ACK will be called and
+ # we'll set it there
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_INSERT_PIXMAP,
+ 'iArg' => $rindex,
+ 'cArg' => $file,
+ 'CallBack' => sub {});
+
+}
+sub selected {
+ my $self = shift;
+ my %ARGS = %{shift()};
+
+ $self->{current} = $ARGS{'iArg'};
+ $ARGS{'cArg'} =~ s/\000//g;
+ $self->{currentText} = $ARGS{'cArg'};
+}
+
+sub current {
+ my $self = shift;
+ return $self->{current};
+}
+
+sub currentText {
+ my $self = shift;
+ return $self->text($self->{current});
+}
+
+sub setCurrentItem {
+ my $self = shift;
+
+ my $index = shift;
+ my $rindex = $index;
+
+ # Async call be default, no need to wait result
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_HIGHLIGHT,
+ 'iArg' => $index,
+ 'CallBack' => sub {});
+
+
+}
+
+sub removeItem {
+ my $self = shift;
+
+ my $index = shift;
+
+ $self->{count} --;
+
+ # Async call be default, no need to wait result
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_REMOVE,
+ 'iArg' => $index,
+ 'CallBack' => sub {});
+
+
+}
+
+sub setScrollBar {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_SET_SCROLLBAR,
+ 'iArg' => shift(),
+ 'CallBack' => sub {});
+
+}
+
+sub setAutoScrollBar {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_SET_AUTO_SCROLLBAR,
+ 'iArg' => shift(),
+ 'CallBack' => sub {});
+
+}
+
+sub clear {
+ my $self = shift;
+
+ $self->{count} = 0;
+ $self->{items} = ();
+
+ $self->sendMessage('iCommand' => $::PUKE_LISTBOX_CLEAR,
+ 'CallBack' => sub {});
+
+}
+
+
+
+package main;
+
+1;
diff --git a/ksirc/puke/pmenudta.cpp b/ksirc/puke/pmenudta.cpp
new file mode 100644
index 00000000..30b375fc
--- /dev/null
+++ b/ksirc/puke/pmenudta.cpp
@@ -0,0 +1,79 @@
+#include "pmenudta.h"
+#include "pobject.h"
+#include <qpopupmenu.h>
+
+
+PMenuData::PMenuData(PObject *_child)
+: PObject()
+{
+ child = _child;
+ connect(this, SIGNAL(outputMessage(int, PukeMessage*)),
+ child, SIGNAL(outputMessage(int, PukeMessage*)));
+}
+
+PMenuData::~PMenuData()
+{
+ // We don't nuke anything since we're kind of abstract and we let the parent take care of it
+}
+
+void PMenuData::messageHandler(int , PukeMessage *)
+{
+ // We don't do anything since we should never ben involked directly
+}
+
+bool PMenuData::menuMessageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ int id = 0;
+ switch(pm->iCommand){
+ case PUKE_MENUDATA_INSERT_TEXT:
+ {
+ QPopupMenu *widget = (QPopupMenu *) child->widget();
+ id = widget->insertItem(pm->cArg);
+ if(pm->iArg > 0){
+ widget->setAccel(pm->iArg, id);
+ }
+
+ pmRet.iCommand = PUKE_MENUDATA_INSERT_TEXT_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = id;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ case PUKE_MENUDATA_INSERT_PIXMAP:
+ {
+ QPopupMenu *widget = (QPopupMenu *) child->widget();
+ id = widget->insertItem(QPixmap(pm->cArg));
+ if(pm->iArg > 0){
+ widget->setAccel(pm->iArg, id);
+ }
+
+ pmRet.iCommand = PUKE_MENUDATA_INSERT_PIXMAP_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = id;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ case PUKE_MENUDATA_REMOVE_ITEM:
+ {
+ QPopupMenu *widget = (QPopupMenu *) child->widget();
+ widget->removeItem(pm->iArg);
+
+ pmRet.iCommand = PUKE_MENUDATA_REMOVE_ITEM_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = pm->iArg;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+#include "pmenudta.moc"
+
diff --git a/ksirc/puke/pmenudta.h b/ksirc/puke/pmenudta.h
new file mode 100644
index 00000000..0c3bfc6a
--- /dev/null
+++ b/ksirc/puke/pmenudta.h
@@ -0,0 +1,57 @@
+#ifndef PMENUDATA_H
+#define PMENUDATA_H
+
+class PMenuData;
+
+#include <qobject.h>
+#include <qmenudata.h>
+#include "pmessage.h"
+#include "pframe.h"
+#include "pobject.h"
+#include "controller.h"
+
+/**
+ * Little helper class here gives us access to needed info inside
+ * QMenuData
+ */
+
+class PMenuDataHelper : public QMenuData
+{
+public:
+ PMenuDataHelper(QMenuData &qmd) {
+ memcpy(this, &qmd, sizeof(QMenuData));
+ }
+ int active() {
+ return QMenuData::actItem;
+ }
+ int actItem;
+};
+
+/**
+ * We're subclased off QMenuData so we can access it's internal proteted vars
+ * We do not initialize NOR create it!!!!
+ */
+
+class PMenuData : public PObject
+{
+ Q_OBJECT
+public:
+ PMenuData (PObject *_child);
+ virtual ~PMenuData ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+ virtual bool menuMessageHandler(int fd, PukeMessage *pm);
+
+// virtual void setWidget(QMenuData *_qmd);
+ // virtual QMenuData *widget();
+
+ virtual int activeItem()
+ {
+ return PMenuDataHelper(*((QMenuData *) child->widget())).active();
+ }
+
+ private:
+ PObject *child;
+};
+
+#endif
diff --git a/ksirc/puke/pmenudta.pm b/ksirc/puke/pmenudta.pm
new file mode 100644
index 00000000..f98acf91
--- /dev/null
+++ b/ksirc/puke/pmenudta.pm
@@ -0,0 +1,60 @@
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_MENUDATA,
+ "pmenudta.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PMenuData Load failed!\n";
+ }
+ }
+ );
+
+package PMenuData;
+use strict;
+
+#
+# Only methods, constructor must create $self elsewhere and must of PBase type
+#
+
+sub new {
+ print "*E* Can't call new for this class\n";
+}
+
+sub insertText {
+ my $self = shift;
+
+ my $text = shift;
+
+ my %ARG = $self->sendMessage('iCommand' => $::PUKE_MENUDATA_INSERT_TEXT,
+ 'cArg' => $text,
+ 'WaitFor' => 1);
+
+ return $ARG{'iArg'};
+}
+
+sub insertPixmap {
+ my $self = shift;
+
+ my $text = shift;
+
+ my %ARG = $self->sendMessage('iCommand' => $::PUKE_MENUDATA_INSERT_PIXMAP,
+ 'cArg' => $text,
+ 'WaitFor' => 1);
+
+ return $ARG{'iArg'};
+
+}
+
+sub removeItem {
+ my $self = shift;
+
+ my $id = shift;
+
+ my %ARG = $self->sendMessage('iCommand' => $::PUKE_MENUDATA_REMOVE_ITEM,
+ 'iArg' => $id);
+
+}
+
+package main;
+
+1;
diff --git a/ksirc/puke/pmessage.h b/ksirc/puke/pmessage.h
new file mode 100644
index 00000000..5c777b24
--- /dev/null
+++ b/ksirc/puke/pmessage.h
@@ -0,0 +1,38 @@
+#ifndef PUKE_MESSAGE_H
+#define PUKE_MESSAGE_H
+
+typedef struct {
+ unsigned int iHeader; // Filled in durring in PukeController, do not set
+ int iCommand;
+ int iWinId;
+ int iArg;
+ int iTextSize; // Size of the text message that follows
+ char *cArg;
+} PukeMessage;
+
+typedef struct {
+ int fd;
+ int iWinId;
+} widgetId;
+
+const uint iPukeHeader = 42U;
+
+#if 0
+class errorInvalidSet {
+public:
+ errorInvalidSet(QObject *_from, const char *_to)
+ : __from(_from), __to(_to)
+ {
+ }
+
+ QObject *from() { return __from; }
+ const char *to() { return __to; }
+
+private:
+ QObject *__from;
+ const char *__to;
+
+};
+#endif
+
+#endif
diff --git a/ksirc/puke/pobject.cpp b/ksirc/puke/pobject.cpp
new file mode 100644
index 00000000..f2481a04
--- /dev/null
+++ b/ksirc/puke/pobject.cpp
@@ -0,0 +1,138 @@
+#include "pobject.h"
+#include "commands.h"
+
+
+PObject::PObject(QObject *pobject, const char *name)
+ : QObject(pobject, name)
+{
+ // Connect slots as needed
+ obj = 0;
+ setWidget(0);
+ manualTerm = FALSE;
+ deleteAble = TRUE;
+ m_hasError = false;
+}
+
+PObject::~PObject()
+{
+ if(isDeleteAble())
+ delete widget();
+ obj = 0;
+ setWidget(0);
+}
+
+PObject *PObject::createWidget(CreateArgs &ca)
+{
+ PObject *pw = new PObject(ca.parent);
+ QObject *o;
+ if(ca.parent != 0)
+ o = new QObject(ca.parent->widget());
+ else
+ o = new QObject();
+ pw->setWidget(o);
+ pw->setWidgetId(ca.pwI);
+ pw->setPukeController(ca.pc);
+ return pw;
+}
+
+void PObject::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ if(pm->iCommand == PUKE_WIDGET_DELETE){
+ /**
+ * Emit the ack before the delete since we don't exist afterwards.
+ */
+ pmRet.iCommand = PUKE_WIDGET_DELETE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+
+ manTerm();
+ delete this;
+ }
+ if(pm->iCommand == PUKE_RELEASEWIDGET){
+ /**
+ * Emit the ack before the delete since we don't exist afterwards.
+ */
+ pmRet.iCommand = PUKE_RELEASEWIDGET_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+
+ /**
+ * By setting the widget to 0 we loose the pointer and then don't delete it
+ */
+ setWidget(0);
+ manTerm();
+ delete this;
+ }
+ else {
+ qWarning("PObject: Unkown Command: %d", pm->iCommand);
+ pmRet.iCommand = PUKE_INVALID;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+}
+
+void PObject::setWidget(QObject *_o)
+{
+ // Disconnect everything from the object we where listning too
+ // Just in case it fires something off, we don't want to get it
+ if(widget() != 0){
+ disconnect(widget(), SIGNAL(destroyed()),
+ this, SLOT(swidgetDestroyed()));
+ }
+
+ obj = _o;
+ if(obj != 0){
+ connect(widget(), SIGNAL(destroyed()),
+ this, SLOT(swidgetDestroyed()));
+ }
+}
+
+QObject *PObject::widget()
+{
+ // kdDebug(5008) << "PObject widget called" << endl;
+ return obj;
+}
+
+void PObject::setWidgetId(widgetId *pwI)
+{
+ wI = *pwI;
+ // kdDebug(5008) << "PObject: set widget id " << wI.iWinId << endl;
+}
+
+widgetId PObject::widgetIden()
+{
+ // kdDebug(5008) << "PObject: called widget id " << wI.iWinId << endl;
+ return wI;
+}
+
+void PObject::swidgetDestroyed(){
+ setWidget(0x0);
+ if(manualTerm == FALSE){
+ manTerm();
+ delete this;
+ }
+}
+
+PukeController *PObject::controller() {
+
+ return pController;
+}
+
+void PObject::manTerm() {
+ manualTerm = TRUE;
+}
+
+void PObject::errorInvalidSet(QObject *_w)
+{
+ m_error = QString("Tried setting a %1 to %2").arg(_w->className()).arg(className());
+ m_hasError = true;
+}
+#include "pobject.moc"
+
diff --git a/ksirc/puke/pobject.h b/ksirc/puke/pobject.h
new file mode 100644
index 00000000..8e5c7c5a
--- /dev/null
+++ b/ksirc/puke/pobject.h
@@ -0,0 +1,134 @@
+#ifndef POBJECT_H
+#define POBJECT_H
+
+class PObject;
+class PukeController;
+class CreateArgs;
+
+#include <qobject.h>
+#include "pmessage.h"
+
+
+class CreateArgs {
+public:
+ CreateArgs(PukeController *_pc, PukeMessage *_pm, widgetId *_pwI, PObject *_parent){
+ pc = _pc;
+ pwI = _pwI;
+ parent = _parent;
+ pm = _pm;
+ fetchedObj = 0;
+ }
+ PukeController *pc;
+ widgetId *pwI;
+ PObject *parent;
+ PukeMessage *pm;
+
+ /**
+ * name of the widget which was fetched from kSirc, this has to be set explicitly
+ */
+ QObject *fetchedObj;
+};
+
+class PObject : public QObject
+{
+ Q_OBJECT
+ public:
+ PObject(QObject *parent = 0, const char *name = 0);
+ virtual ~PObject();
+
+ /**
+ * Creates a new QObject and returns a PObject
+ */
+ static PObject *createWidget(CreateArgs &ca);
+
+ /**
+ * Handles messages from dsirc
+ * PObject can't get messages so return an error
+ */
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ /**
+ * Sets the current opbect
+ */
+ virtual void setWidget(QObject *w);
+
+ /**
+ * Returns the current object
+ */
+ virtual QObject *widget();
+
+ /**
+ * Sets the window id
+ */
+ virtual void setWidgetId(widgetId *pwI);
+ /**
+ * Returns the current window identifier
+ */
+ virtual widgetId widgetIden();
+
+ /**
+ * Set's the puke controller for the widget
+ */
+ void setPukeController(PukeController *pc){
+ pController = pc;
+ }
+
+ /**
+ * If we cannot delete the widget, check this (ie fetched widgets)
+ */
+ bool isDeleteAble(){
+ return deleteAble;
+ }
+
+ /**
+ * Returns if an error was encountered.
+ */
+ bool hasError() {
+ return m_hasError;
+ }
+
+ /**
+ * Returns error description
+ */
+ QString error() {
+ m_hasError = false;
+ return m_error;
+ }
+
+ /**
+ * Set this for fetched widgets and such that cannot be deleted
+ */
+ void setDeleteAble(bool _d){
+ deleteAble = _d;
+ }
+ /**
+ * Before deleting the widget, call manTerm() to signal manual
+ * termination of the widget
+ */
+ void manTerm();
+
+
+ signals:
+ void outputMessage(int fd, PukeMessage *pm);
+ void widgetDestroyed(widgetId wI);
+
+ protected slots:
+ void swidgetDestroyed();
+
+protected:
+ PukeController *controller();
+ void errorInvalidSet(QObject *_w);
+
+private:
+ QObject *obj;
+ PukeController *pController;
+ widgetId wI;
+
+ bool manualTerm;
+ bool deleteAble;
+ QString m_error;
+ bool m_hasError;
+};
+
+#include "controller.h"
+#endif
diff --git a/ksirc/puke/pobjfinder-cmd.h b/ksirc/puke/pobjfinder-cmd.h
new file mode 100644
index 00000000..4f89cfec
--- /dev/null
+++ b/ksirc/puke/pobjfinder-cmd.h
@@ -0,0 +1,32 @@
+#ifndef POBJFINDER_CMD_H
+#define POBJFINDER_CMD_H
+
+/*
+ * We get 2300
+ */
+
+// Desc: fetchs all object names that kSirc knows about
+// iWinId: widget
+// iArg: not defined
+// cArg: path
+#define PUKE_OBJFINDER_ALLOBJECTS 2300
+
+// Desc: ack for the fetch
+// iWinId: widget
+// iArg: not defined
+// cArg: new line seperated list of widgets className::name
+#define PUKE_OBJFINDER_ALLOBJECTS_ACK -2300
+
+// Desc: signal for creation of a new object, not used, only ack
+// iWinId: not defined
+// iArg: not defined
+// cArg: not defined
+#define PUKE_OBJFINDER_NEWOBJECT 2301
+
+// Desc: signal for new widget created
+// iWinId: widget
+// iArg: not defined
+// cArg: className::name of new object
+#define PUKE_OBJFINDER_NEWOBJECT_ACK -2301
+
+#endif
diff --git a/ksirc/puke/pobjfinder.cpp b/ksirc/puke/pobjfinder.cpp
new file mode 100644
index 00000000..0f510ea4
--- /dev/null
+++ b/ksirc/puke/pobjfinder.cpp
@@ -0,0 +1,86 @@
+#include "pobjfinder.h"
+#include "pobjfinder-cmd.h"
+
+
+PObject *
+PObjFinder::createWidget(CreateArgs &ca)
+{
+ PObjFinder *pw = new PObjFinder(ca.parent);
+ pw->setWidget(0x0);
+ pw->setWidgetId(ca.pwI);
+ pw->setPukeController(ca.pc);
+ return pw;
+}
+
+
+PObjFinder::PObjFinder(PObject *parent)
+ : PObject(parent)
+{
+ // We don't actually encase a widget since all the ObjFinder interface
+ // is static
+ setWidget(0x0);
+ connect(controller(), SIGNAL(inserted(QObject *)),
+ this, SLOT(newObject(QObject *)));
+}
+
+PObjFinder::~PObjFinder()
+{
+}
+
+void PObjFinder::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_OBJFINDER_ALLOBJECTS:
+ {
+ QString qscArg;
+ QStrList allObj = controller()->allObjects();
+ for(uint i = 0; i <= allObj.count(); i++){
+ qscArg += allObj.at(i);
+ qscArg += "\n";
+ }
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = qstrdup(qscArg);
+ pmRet.iTextSize = qscArg.length();
+ emit outputMessage(fd, &pmRet);
+ delete pmRet.cArg;
+ break;
+ }
+ default:
+ PObject::messageHandler(fd, pm);
+ }
+}
+
+void PObjFinder::setWidget(QObject *_obj)
+{
+ PObject::setWidget(_obj);
+}
+
+
+objFinder *PObjFinder::widget()
+{
+ return 0x0;
+}
+
+
+void PObjFinder::newObject(QObject *name){
+ QString info;
+
+ info = name->className();
+ info += "::";
+ info += name->name("unnamed");
+
+ PukeMessage pmRet;
+ pmRet.iCommand = - PUKE_OBJFINDER_NEWOBJECT_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = qstrdup(info);
+ pmRet.iTextSize = info.length();
+ emit outputMessage(widgetIden().fd, &pmRet);
+ delete pmRet.cArg;
+}
+
+#include "pobjfinder.moc"
diff --git a/ksirc/puke/pobjfinder.h b/ksirc/puke/pobjfinder.h
new file mode 100644
index 00000000..9149aa86
--- /dev/null
+++ b/ksirc/puke/pobjfinder.h
@@ -0,0 +1,33 @@
+#ifndef POBJFINDER_H
+#define POBJFINDER_H
+
+#include "pobject.h"
+#include "../objFinder.h"
+#include "pmessage.h"
+#include "controller.h"
+
+
+class PObjFinder : public PObject
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PObjFinder ( PObject * parent );
+ virtual ~PObjFinder ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_obj = 0);
+ virtual objFinder *widget();
+
+public slots:
+ virtual void newObject(QObject *obj);
+
+protected:
+ bool checkWidget();
+
+private:
+};
+
+#endif
diff --git a/ksirc/puke/pobjfinder.pm b/ksirc/puke/pobjfinder.pm
new file mode 100644
index 00000000..6cf6c5bb
--- /dev/null
+++ b/ksirc/puke/pobjfinder.pm
@@ -0,0 +1,89 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $PWIDGET_OBJFINDER,
+ "pobjfinder.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PLabel Load failed!\n";
+ }
+ }
+ );
+
+package PObjFinder;
+@ISA = qw(PBase);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_OBJFINDER;
+
+ if($class eq 'PObjFinder'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_OBJFINDER_NEWOBJECT, sub {$self->newObject(shift())});
+
+ return $self;
+
+}
+
+sub newObject {
+}
+
+sub interAllObjects {
+ my $self = shift;
+ my %REPLY = $self->sendMessage('iCommand' => $::PUKE_OBJFINDER_ALLOBJECTS,
+ 'WaitFor' => 1);
+
+ return $REPLY{'cArg'};
+
+}
+
+sub allObjects {
+ my $self = shift;
+ my $line = $self->interAllObjects();
+
+ $line =~ s/\S+::unnamed//gm;
+
+ return $line;
+
+}
+
+
+sub allObjectsHash {
+ my $self = shift;
+
+ my $line = $self->allObjects();
+
+ my $widget;
+ my %hash;
+
+ foreach $widget (split(/\n/, $line)){
+ $hash{$widget} = 1;
+ }
+
+ return \%hash;
+
+}
+
+sub allObjectsArray {
+ my $self = shift;
+
+ my $line = $self->allObjects();
+
+ my $widget;
+ my @arr;
+
+ foreach $widget (split(/\n/, $line)){
+ next if $widget eq '';
+ $arr[$#arr+1] = $widget;
+ }
+
+ return @arr;
+}
+
+package main;
+
diff --git a/ksirc/puke/ppopmenu.cpp b/ksirc/puke/ppopmenu.cpp
new file mode 100644
index 00000000..4c07d800
--- /dev/null
+++ b/ksirc/puke/ppopmenu.cpp
@@ -0,0 +1,120 @@
+#include <kdebug.h>
+#include <qcursor.h>
+
+#include "ppopmenu.h"
+
+PObject *
+PPopupMenu::createWidget(CreateArgs &ca)
+{
+ PPopupMenu *pm = new PPopupMenu(ca.parent);
+ QPopupMenu *qpm;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("QPopupMenu") == TRUE){
+ qpm= (QPopupMenu *) ca.fetchedObj;
+ pm->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ qpm = new QPopupMenu((QWidget *) ca.parent->widget());
+ else
+ qpm = new QPopupMenu();
+ pm->setWidget(qpm);
+ pm->setWidgetId(ca.pwI);
+ return pm;
+}
+
+
+PPopupMenu::PPopupMenu(PObject *parent)
+ : PFrame(parent)
+{
+ // kdDebug(5008) << "PLineEdit PLineEdit called" << endl;
+ menu = 0;
+ setWidget(menu);
+ pmd = new PMenuData(this);
+}
+
+PPopupMenu::~PPopupMenu()
+{
+ // kdDebug(5008) << "PLineEdit: in destructor" << endl;
+/* delete widget(); // Delete the frame
+ menu = 0; // Set it to 0
+ setWidget(menu); // Now set all widget() calls to 0.
+*/
+ delete pmd;
+}
+
+void PPopupMenu::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ /*
+ case PUKE_LINED_SET_MAXLENGTH:
+ if(widget() == 0){
+ kdDebug(5008) << "PLineEdit: No Widget set" << endl;
+ return;
+ }
+ widget()->setMaxLength(pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = widget()->maxLength();
+ emit outputMessage(fd, &pmRet);
+ break;
+ */
+ case PUKE_POPUPMENU_POPUP_CURRENT:
+ if(widget() == 0){
+ kdDebug(5008) << "PPopupMenu: No Widget set" << endl;
+ return;
+ }
+
+ widget()->popup(QCursor::pos(), 0);
+
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 1;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+
+ default:
+ if(pmd->menuMessageHandler(fd, pm) == FALSE) // Call pmd's even filter
+ PFrame::messageHandler(fd, pm);
+ }
+}
+
+void PPopupMenu::setWidget(QObject *_menu)
+{
+ if(_menu != 0 && _menu->inherits("QPopupMenu") == FALSE)
+ {
+ errorInvalidSet(_menu);
+ return;
+ }
+
+ menu = (QPopupMenu *) _menu;
+ if(menu != 0x0){
+ connect(menu, SIGNAL(activated(int)),
+ this, SLOT(got_activated(int)));
+ }
+ PFrame::setWidget(menu);
+}
+
+
+QPopupMenu *PPopupMenu::widget()
+{
+ return menu;
+}
+
+void PPopupMenu::got_activated(int itemId){
+ qWarning("Item got activated: %d", itemId);
+
+ widgetId wI;
+ PukeMessage pmRet;
+
+ wI = widgetIden();
+ pmRet.iCommand = PUKE_POPUPMENU_ACTIVATED_ACK;
+ pmRet.iWinId = wI.iWinId;
+ pmRet.iArg = itemId;
+ pmRet.cArg = 0;
+ emit outputMessage(wI.fd, &pmRet);
+
+}
+
+#include "ppopmenu.moc"
+
diff --git a/ksirc/puke/ppopmenu.h b/ksirc/puke/ppopmenu.h
new file mode 100644
index 00000000..9044b935
--- /dev/null
+++ b/ksirc/puke/ppopmenu.h
@@ -0,0 +1,36 @@
+#ifndef PPOPMENU_H
+#define PPOPMENU_H
+
+class PPopupMenu;
+
+#include <qpopupmenu.h>
+#include "pobject.h"
+#include "pmessage.h"
+
+#include "ptablevw.h"
+#include "pmenudta.h"
+
+class PPopupMenu : public PFrame
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PPopupMenu(PObject *child);
+ virtual ~PPopupMenu();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_menu);
+ virtual QPopupMenu *widget();
+
+protected slots:
+ void got_activated ( int itemId );
+
+private:
+ QPopupMenu *menu;
+ PMenuData *pmd;
+
+};
+
+#endif
diff --git a/ksirc/puke/ppopmenu.pm b/ksirc/puke/ppopmenu.pm
new file mode 100644
index 00000000..c1c011fa
--- /dev/null
+++ b/ksirc/puke/ppopmenu.pm
@@ -0,0 +1,65 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_POPMENU,
+ "ppopmenu.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PPopMenu Load failed!\n";
+ }
+ }
+ );
+
+package PPopupMenu;
+@ISA = qw(PTableView PMenuData);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_POPMENU;
+
+ if($class eq 'PPopupMenu'){
+ $self->create();
+ }
+
+ $self->installHandler($::PUKE_POPUPMENU_ACTIVATED_ACK,
+ sub {$self->activated(@_)});
+
+ return $self;
+
+}
+
+sub activated {
+ my $self = shift;
+
+ my %ARG = %{shift()};
+
+ if($self->{'menu_id'}{$ARG{'iArg'}}){
+ &{$self->{'menu_id'}{$ARG{'iArg'}}}(%ARG);
+ }
+ else {
+ # There's not handler, don't make noise, since if we used fetchWidget()
+ # We don't handlers for everything
+ # &::tell("*E* No handler for id: $ARG{iArg}\n");
+ }
+}
+
+sub installMenu {
+ my $self = shift;
+
+ my $id = shift;
+ my $func = shift;
+ $self->{'menu_id'}{$id} = $func;
+}
+
+sub popupAtCurrent {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_POPUPMENU_POPUP_CURRENT,
+ 'CallBack' => sub {});
+}
+
+
+package main;
diff --git a/ksirc/puke/pprogress.cpp b/ksirc/puke/pprogress.cpp
new file mode 100644
index 00000000..75c7fa4b
--- /dev/null
+++ b/ksirc/puke/pprogress.cpp
@@ -0,0 +1,133 @@
+#include <stdio.h>
+
+
+#include <kdebug.h>
+
+#include "pprogress.h"
+
+PObject *
+PProgress::createWidget(CreateArgs &ca)
+{
+ PProgress *pw = new PProgress(ca.parent);
+ KSProgress *ksp;
+ if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ ksp = new KSProgress((QWidget *) ca.parent->widget());
+ else
+ ksp = new KSProgress();
+ pw->setWidget(ksp);
+ pw->setWidgetId(ca.pwI);
+ return pw;
+}
+
+
+PProgress::PProgress( PObject *parent)
+ : PWidget(parent)
+{
+ // kdDebug(5008) << "PProgress PProgress called" << endl;
+ ksp = 0;
+}
+
+PProgress::~PProgress()
+{
+ // kdDebug(5008) << "PProgress: in destructor" << endl;
+ /*
+ delete widget(); // Delete the frame
+ ksp=0; // Set it to 0
+ setWidget(ksp); // Now set all widget() calls to 0.
+ */
+}
+
+void PProgress::messageHandler(int fd, PukeMessage *pm)
+{
+ // kdDebug(5008) << "PProgress handler called" << endl;
+ PukeMessage pmRet;
+ if(widget() == 0){
+ qWarning("Null widget");
+ return;
+ }
+ switch(pm->iCommand){
+ case PUKE_KSPROGRESS_SET_RANGE:
+ {
+ int start=0, stop=1;
+ int found = sscanf(pm->cArg, "%d\t%d", &start, &stop);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,13));
+ if(start >= stop){
+ stop = start+1;
+ start = 0;
+ }
+
+ widget()->setRange(start, stop);
+ pmRet.iCommand = PUKE_KSPROGRESS_SET_RANGE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ case PUKE_KSPROGRESS_SET_TOPTEXT:
+ widget()->setTopText(QString(pm->cArg));
+ pmRet.iCommand = PUKE_KSPROGRESS_SET_TOPTEXT;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_KSPROGRESS_SET_BOTTEXT:
+ widget()->setBotText(QString(pm->cArg));
+ pmRet.iCommand = PUKE_KSPROGRESS_SET_BOTTEXT;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_KSPROGRESS_SET_VALUE:
+ widget()->setValue(pm->iArg);
+ pmRet.iCommand = PUKE_KSPROGRESS_SET_VALUE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ default:
+ PWidget::messageHandler(fd, pm);
+ }
+}
+
+void PProgress::cancelPressed(){
+ kdDebug(5008) << "Cancel Pressed" << endl;
+ PukeMessage pmRet;
+ pmRet.iCommand = PUKE_KSPROGRESS_CANCEL_ACK;
+ pmRet.iWinId = widgetIden().iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(widgetIden().fd, &pmRet);
+}
+
+void PProgress::setWidget(QObject *_f)
+{
+ // kdDebug(5008) << "PProgress setWidget called" << endl;
+ if(_f != 0 && _f->inherits("KSProgress") == FALSE)
+ {
+ errorInvalidSet(_f);
+ return;
+ }
+
+ ksp = (KSProgress *) _f;
+ if(widget() != 0){
+ connect(widget(), SIGNAL(cancel()),
+ this, SLOT(cancelPressed()));
+ }
+ PWidget::setWidget(ksp);
+
+}
+
+
+KSProgress *PProgress::widget()
+{
+ // kdDebug(5008) << "PProgress widget called" << endl;
+ return ksp;
+}
+
+#include "pprogress.moc"
+
diff --git a/ksirc/puke/pprogress.h b/ksirc/puke/pprogress.h
new file mode 100644
index 00000000..f81fd976
--- /dev/null
+++ b/ksirc/puke/pprogress.h
@@ -0,0 +1,32 @@
+#ifndef PPROGRESS_H
+#define PPROGRESS_H
+
+class PProgress;
+
+#include "../KSProgress/ksprogress.h"
+#include "pmessage.h"
+//#include "pwidget.h"
+#include "pobject.h"
+#include "controller.h"
+
+class PProgress : public PWidget {
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PProgress ( PObject * parent = 0);
+ virtual ~PProgress ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_f);
+ virtual KSProgress *widget();
+
+protected slots:
+ void cancelPressed();
+
+private:
+ KSProgress *ksp;
+};
+
+#endif
diff --git a/ksirc/puke/pprogress.pm b/ksirc/puke/pprogress.pm
new file mode 100644
index 00000000..b889483e
--- /dev/null
+++ b/ksirc/puke/pprogress.pm
@@ -0,0 +1,86 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_KSPROGRESS,
+ "pprogress.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PProgress Load failed!\n";
+ }
+ }
+ );
+
+package PProgress;
+@ISA = qw(PWidget);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_KSPROGRESS;
+
+ $self->installHandler($::PUKE_KSPROGRESS_CANCEL_ACK,
+ sub {$self->cancelPressed(@_)});
+
+
+ if($class eq 'PProgress'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub setTopText {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KSPROGRESS_SET_TOPTEXT,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub setBotText {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KSPROGRESS_SET_BOTTEXT,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub setRange {
+ my $self = shift;
+
+ my $lower = shift;
+ my $upper = shift;
+
+ my $carg = "$lower\t$upper";
+
+ $self->sendMessage('iCommand' => $::PUKE_KSPROGRESS_SET_RANGE,
+ 'cArg' => $carg,
+ 'CallBack' => sub {});
+
+}
+
+sub setValue {
+ my $self = shift;
+
+ my $value = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_KSPROGRESS_SET_VALUE,
+ 'iArg' => $value,
+ 'CallBack' => sub {});
+
+}
+
+sub cancelPressed {
+ print "*E* Cancel pressed\n";
+}
+
+package main;
diff --git a/ksirc/puke/ppushbt.cpp b/ksirc/puke/ppushbt.cpp
new file mode 100644
index 00000000..48cf6121
--- /dev/null
+++ b/ksirc/puke/ppushbt.cpp
@@ -0,0 +1,70 @@
+#include "ppushbt.h"
+#include <stdio.h>
+
+
+PObject *
+PPushButton::createWidget(CreateArgs &ca)
+{
+ PPushButton *pb = new PPushButton(ca.parent);
+ QPushButton *qb;
+ if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ qb = new QPushButton((QWidget *) ca.parent->widget());
+ else
+ qb = new QPushButton(0L);
+ pb->setWidget(qb);
+ pb->setWidgetId(ca.pwI);
+ return pb;
+}
+
+
+PPushButton::PPushButton(PObject *parent)
+ : PButton(parent)
+{
+ // kdDebug(5008) << "PLineEdit PLineEdit called" << endl;
+ button = 0;
+ setWidget(button);
+}
+
+PPushButton::~PPushButton()
+{
+ // kdDebug(5008) << "PLineEdit: in destructor" << endl;
+/* delete widget(); // Delete the frame
+ button=0; // Set it to 0
+ setWidget(button); // Now set all widget() calls to 0.
+ */
+}
+
+void PPushButton::messageHandler(int fd, PukeMessage *pm)
+{
+// PukeMessage pmRet;
+ switch(pm->iCommand){
+ default:
+ PButton::messageHandler(fd, pm);
+ }
+}
+
+void PPushButton::setWidget(QObject *_qb)
+{
+ if(_qb != 0 && _qb->inherits("QPushButton") == FALSE)
+ {
+ errorInvalidSet(_qb);
+ return;
+ }
+
+ button = (QPushButton *) _qb;
+ if(_qb != 0){
+ }
+ PButton::setWidget(_qb);
+
+}
+
+
+QPushButton *PPushButton::widget()
+{
+ return button;
+}
+
+
+
+#include "ppushbt.moc"
+
diff --git a/ksirc/puke/ppushbt.h b/ksirc/puke/ppushbt.h
new file mode 100644
index 00000000..87336679
--- /dev/null
+++ b/ksirc/puke/ppushbt.h
@@ -0,0 +1,33 @@
+#ifndef PPUSHBUTTON_H
+#define PPUSHBUTTON_H
+
+class PPushButton;
+
+#include <qpushbutton.h>
+#include "pmessage.h"
+#include "pbutton.h"
+#include "pobject.h"
+#include "controller.h"
+
+class PPushButton : public PButton
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PPushButton ( PObject * parent );
+ virtual ~PPushButton ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_b = 0);
+ virtual QPushButton *widget();
+
+public slots:
+
+private:
+ QPushButton *button;
+
+};
+
+#endif
diff --git a/ksirc/puke/ppushbt.pm b/ksirc/puke/ppushbt.pm
new file mode 100644
index 00000000..c1d00349
--- /dev/null
+++ b/ksirc/puke/ppushbt.pm
@@ -0,0 +1,30 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_PUSHBT,
+ "ppushbt.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PPushButton Load failed!\n";
+ }
+ }
+ );
+
+package PPushButton;
+@ISA = qw(PButton);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_PUSHBT;
+
+ if($class eq 'PPushButton'){
+ $self->create();
+ }
+ return $self;
+
+}
+
+package main;
diff --git a/ksirc/puke/ptabdialog.cpp b/ksirc/puke/ptabdialog.cpp
new file mode 100644
index 00000000..b0e3ba2c
--- /dev/null
+++ b/ksirc/puke/ptabdialog.cpp
@@ -0,0 +1,101 @@
+#include <stdio.h>
+
+
+#include "ptabdialog.h"
+#include "commands.h"
+#include <iostream>
+
+PObject *
+PTabDialog::createWidget(CreateArgs &ca)
+{
+ PTabDialog *ptd = new PTabDialog(ca.parent);
+ QTabDialog *qtd;
+ // Retreive the border and direction information out of the
+ // carg string
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("QTabDialog") == TRUE){
+ qtd = (QTabDialog *) ca.fetchedObj;
+ ptd->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ qtd = new QTabDialog((QWidget *) ca.parent->widget());
+ else
+ qtd = new QTabDialog();
+ ptd->setWidget(qtd);
+ ptd->setWidgetId(ca.pwI);
+ ptd->setPukeController(ca.pc);
+ return ptd;
+}
+
+PTabDialog::PTabDialog(QObject *)
+ : PWidget()
+{
+ // Connect slots as needed
+ setWidget(0);
+}
+
+PTabDialog::~PTabDialog()
+{
+ // kdDebug(5008) << "PObject: in destructor" << endl;
+ /*
+ delete widget();
+ tab = 0;
+ setWidget(0);
+ */
+}
+
+void PTabDialog::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+
+ if(pm->iCommand == PUKE_TABDIALOG_ADDTAB){
+ if(!(pm->iTextSize > 0)){
+ qWarning("QTabDialog/addtab: incorrent cArg size, bailing out. Needed: >0 got: %d\n", pm->iTextSize);
+ pmRet.iCommand = PUKE_TABDIALOG_ADDTAB_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 1;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ return;
+ }
+ widgetId wiWidget;
+ wiWidget.fd = fd;
+ wiWidget.iWinId = pm->iArg;
+ PWidget *pw = controller()->id2pwidget(&wiWidget);
+ // kdDebug(5008) << "Adding widget with stretch: " << (int) pm->cArg[0] << " and align: " << // (int) pm->cArg[1] << endl;
+ widget()->addTab(pw->widget(), pm->cArg);
+
+ pmRet.iCommand = PUKE_TABDIALOG_ADDTAB_ACK; // ack the add widget
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ else {
+ PWidget::messageHandler(fd, pm);
+ }
+
+}
+
+void PTabDialog::setWidget(QObject *tb)
+{
+ if(tb != 0 && tb->inherits("QTabDialog") == FALSE)
+ {
+ errorInvalidSet(tb);
+ return;
+ }
+
+ tab = (QTabDialog *) tb;
+ if(tab != 0){
+ }
+ PObject::setWidget(tb);
+
+}
+
+QTabDialog *PTabDialog::widget()
+{
+ // kdDebug(5008) << "PObject widget called" << endl;
+ return tab;
+}
+
+#include "ptabdialog.moc"
+
diff --git a/ksirc/puke/ptabdialog.h b/ksirc/puke/ptabdialog.h
new file mode 100644
index 00000000..6e669074
--- /dev/null
+++ b/ksirc/puke/ptabdialog.h
@@ -0,0 +1,41 @@
+#ifndef PTABDIALOG_H
+#define PTABDIALOG_H
+
+class PTabDialog;
+
+#include <qtabdialog.h>
+#include "pmessage.h"
+#include "pwidget.h"
+
+class PTabDialog : public PWidget
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PTabDialog(QObject *parent = 0);
+ virtual ~PTabDialog();
+
+ /**
+ * Handles messages from dsirc
+ * PObject can't get messages so return an error
+ */
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ /**
+ * Sets the current opbect
+ */
+ virtual void setWidget(QObject *tb);
+
+ /**
+ * Returns the current object
+ */
+ virtual QTabDialog *widget();
+
+private:
+
+ QTabDialog *tab;
+
+};
+
+#endif
diff --git a/ksirc/puke/ptabdialog.pm b/ksirc/puke/ptabdialog.pm
new file mode 100644
index 00000000..65435fb9
--- /dev/null
+++ b/ksirc/puke/ptabdialog.pm
@@ -0,0 +1,49 @@
+
+&::PukeSendMessage($PUKE_WIDGET_LOAD,
+ $PUKE_CONTROLLER,
+ $PWIDGET_TABDIALOG,
+ "ptabdialog.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PTabDialog Load failed!\n";
+ }
+ }
+ );
+
+package PTabDialog;
+@ISA = qw(PWidget);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_TABDIALOG;
+
+ if($class eq 'PTabDialog'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub addTab {
+ my $self = shift;
+
+ my $widget = shift;
+ my $text = shift;
+
+ if($widget->{iWinId} <= 0){
+ print "*E* Trying to add invalid widget " . ref($widget) . "\n";
+ return;
+ }
+
+ $self->sendMessage('iCommand' => $::PUKE_TABDIALOG_ADDTAB,
+ 'iArg' => $widget->{iWinId},
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+package main;
diff --git a/ksirc/puke/ptablevw.cpp b/ksirc/puke/ptablevw.cpp
new file mode 100644
index 00000000..1dba614b
--- /dev/null
+++ b/ksirc/puke/ptablevw.cpp
@@ -0,0 +1,76 @@
+#include "ptablevw.h"
+
+
+PObject *
+PTableView::createWidget(CreateArgs &ca)
+{
+ qWarning("Table View is abstract class, cannot create an object from it!!!");
+ PTableView *pt = new PTableView(ca.parent);
+ pt->setWidget(0);
+ pt->setWidgetId(ca.pwI);
+ return pt;
+}
+
+
+PTableView::PTableView(PObject *parent)
+ : PFrame(parent)
+{
+ // kdDebug(5008) << "PTableView PTableView called" << endl;
+ tbl = 0;
+ setWidget(tbl);
+}
+
+PTableView::~PTableView()
+{
+ // don't delete the widget since none acutally exists.
+ // delete widget(); // Delete the frame
+ // tbl=0; // Set it to 0
+// setWidget(tbl); // Now set all widget() calls to 0.
+}
+
+void PTableView::messageHandler(int fd, PukeMessage *pm)
+{
+// PukeMessage pmRet;
+ switch(pm->iCommand){
+ /*
+ case PUKE_LINED_SET_MAXLENGTH:
+ if(widget() == 0){
+ kdDebug(5008) << "PTableView: No Widget set" << endl;
+ return;
+ }
+ widget()->setMaxLength(pm->iArg);
+ pmRet.iCommand = - pm->iCommand;
+ pmRet.iWinId = - pm->iWinId;
+ pmRet.iArg = widget()->maxLength();
+ emit outputMessage(fd, &pmRet);
+ break;
+ */
+ default:
+ PFrame::messageHandler(fd, pm);
+ }
+}
+
+void PTableView::setWidget(QObject *_tbv)
+{
+ if(_tbv != 0 && _tbv->inherits("QGridView") == FALSE)
+ {
+ errorInvalidSet(_tbv);
+ return;
+ }
+
+ tbl = (QGridView *) _tbv;
+ if(tbl != 0){
+ }
+ PFrame::setWidget(_tbv);
+
+}
+
+
+QGridView *PTableView::widget()
+{
+ return tbl;
+}
+
+
+#include "ptablevw.moc"
+
diff --git a/ksirc/puke/ptablevw.h b/ksirc/puke/ptablevw.h
new file mode 100644
index 00000000..33ba4015
--- /dev/null
+++ b/ksirc/puke/ptablevw.h
@@ -0,0 +1,33 @@
+#ifndef PTABLEVIEW_H
+#define PTABLEVIEW_H
+
+class PTableView;
+
+#include <qgridview.h>
+
+#include "pmessage.h"
+#include "pframe.h"
+#include "controller.h"
+
+// Init and setup code
+class PTableView : public PFrame
+{
+ Q_OBJECT
+public:
+ static PObject *createWidget(CreateArgs &ca);
+
+ PTableView ( PObject * parent );
+ virtual ~PTableView ();
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *_tbl);
+ virtual QGridView *widget();
+
+public slots:
+
+private:
+ QGridView *tbl;
+};
+
+#endif
diff --git a/ksirc/puke/ptablevw.pm b/ksirc/puke/ptablevw.pm
new file mode 100644
index 00000000..297d7b29
--- /dev/null
+++ b/ksirc/puke/ptablevw.pm
@@ -0,0 +1,57 @@
+
+&::PukeSendMessage($::PUKE_WIDGET_LOAD,
+ $::PUKE_CONTROLLER,
+ $::PWIDGET_TABLEVW,
+ "ptablevw.so",
+ sub { my %ARG = %{shift()};
+ if($ARG{'iArg'} == 1){
+ print "*E* PTableView Load failed!\n";
+ }
+ }
+ );
+
+
+package PTableView;
+@ISA = qw(PFrame);
+use strict;
+
+if($PTableView::usage == undef){
+ $PTableView::usage = 0;
+}
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ if($PTableView::usage == 0){
+ }
+ $PTableView::usage++;
+
+ $self->{widgetType} = $::PWIDGET_TABLEVW;
+
+ if($class eq 'PTableView'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub DESTROY {
+ my $self = shift;
+ $self->SUPER::DESTROY(@_);
+ $PTableView::usage--;
+ if($PTableView::usage == 0){
+ # &::PukeSendMessage($::PUKE_WIDGET_UNLOAD,
+ # 0,
+ # $::PWIDGET_TABLEVW,
+ # "",
+ # sub {}
+ # );
+
+ }
+}
+
+
+package main;
+1;
diff --git a/ksirc/puke/puke.pl b/ksirc/puke/puke.pl
new file mode 100644
index 00000000..f0d2c7fd
--- /dev/null
+++ b/ksirc/puke/puke.pl
@@ -0,0 +1,225 @@
+use Socket;
+use Fcntl;
+
+#
+# Clean up if this is the second load.
+#
+# Don't close anything so we can be loaded twice.
+#
+#if($PUKEFd != undef){
+# &remsel($PUKEFd);
+# close($PUKEFd);
+# sleep(1);
+# $PUKEFd = undef;
+#}
+
+#
+# Puke timeout waiting for messages
+$PUKE_TIMEOUT = 10;
+
+#
+# Setup flag fo syncronous operation
+# 1 for sync
+# 0 for async/fly by the seat of your pants
+#
+$SYNC = 0;
+
+#
+# Setup debugging logger, comment out for production use
+#
+$DEBUG = 0;
+if($DEBUG){
+ open(LOG, ">msg-log") || warn "Failed to open log file: $!\n";
+ select(LOG); $| = 1; select(STDOUT);
+ print LOG "Start time: ". `date`;
+}
+
+
+
+#
+# Multi operation level handler, winId Based.
+#
+# PUKE_HANDLER{Cmd}{winId} = sub();
+
+%PUKE_HANDLER = ();
+
+#
+# Default handler is called if no handler defined
+# Default handlers defined in commands-handler.pl
+# Single level PUKE_DEF_HANDLER{$cmd};
+#
+
+#%PUKE_DEF_HANDLER = ();
+
+#require 'commands-perl.pl';
+&docommand("/load commands-perl.pl");
+#require 'commands-handler.pl';
+&docommand("/load commands-handler.pl");
+
+$PukeHeader = 42; # Alternating 1010 for 32 bits
+$PukePacking = "Iiiiia*"; # 4 ints, followed by any number of of characters
+$PukeMSize = length(pack($PukePacking, $PukeHeader, 0, 0, 0, 0, ""));
+
+if(!$ENV{'PUKE_SOCKET'}) {
+ $sock = $ENV{'HOME'} . "/.ksirc.socket";
+}
+else {
+ $sock = $ENV{'PUKE_SOCKET'};
+}
+
+if($PUKEFd == undef){
+ $PUKEFd = &newfh;
+ $proto = getprotobyname('tcp');
+ socket($PUKEFd, PF_UNIX, SOCK_STREAM, 0) || print "PUKE: Sock failed: $!\n";
+ $sun = sockaddr_un($sock);
+ print "*P* PUKE: Connecting to $sock\n";
+ connect($PUKEFd,$sun) || (die "Puke: Connect failed: $!\n",$PUKEFailed=1);
+ select($PUKEFd); $| = 1; select(STDOUT);
+ #fcntl($PUKEFd, F_SETFL, O_NONBLOCK);
+}
+
+# Arg1: Command
+# Arg2: WinId
+# Arg3: iArg
+# Arg4: cArg
+sub PukeSendMessage {
+ my($cmd, $winid, $iarg, $carg, $handler, $waitfor) = @_;
+ # print("PUKE: cArg message too long $cArg\n") if(length($carg) > 50);
+ $PUKE_HANDLER{$cmd}{$winid} = $handler if $handler != undef;
+ my $msg = pack($PukePacking, $PukeHeader, $cmd, $winid, $iarg, length($carg), $carg);
+ syswrite($PUKEFd, $msg, length($msg));
+ # print STDERR "*** " . $msg . "\n";
+ print LOG kgettimeofday() . " SEND message: CMD: $PUKE_NUM2NAME{$cmd} WIN: $winid IARG: $iarg LEN: " . length($carg) . " CARG: $carg\n" if $DEBUG;
+ if($SYNC == 1 || $waitfor == 1){
+ return &sel_PukeRecvMessage(1, $winid, -$cmd, $carg);
+ }
+ return ();
+}
+
+sub sel_PukeRecvMessage {
+ ($wait, $wait_winid, $wait_cmd, $wait_carg) = @_;
+ my($m);
+ my($cmd, $winid, $iarg, $carg, $junk);
+
+ while(1){
+ my $old_a = $SIG{'alarm'};
+ $SIG{'alarm'} = sub { die "alarm\n"; };
+ my $old_time = alarm($PUKE_TIMEOUT);
+ eval {
+ $len = sysread($PUKEFd, $m, $PukeMSize);
+ };
+ if($@){
+ print "*E* Timeout waiting for data for first sysread\n";
+ $SIG{ALRM} = $old_a;
+ alarm($old_time);
+ return;
+ }
+ $SIG{ALRM} = $old_a;
+ alarm($old_time);
+
+ if($len== 0){
+ &remsel($PUKEFd);
+ close($PUKEFd);
+ return;
+ }
+ # print "Length: $len " . length($m) . "\n";
+ ($header, $cmd, $winid, $iarg, $length, $carg) = unpack($PukePacking, $m);
+ if($header != $PukeHeader){
+ print("*E* Invalid message received! Discarding! Got: $header wanted: $PukeHeader\n");
+ # return;
+ }
+ if($length > 0){
+ my $old_a = $SIG{'alarm'};
+ $SIG{'alarm'} = sub { die "alarm\n"; };
+ my $old_time = alarm($PUKE_TIMEOUT);
+ eval {
+ $clen = sysread($PUKEFd, $m2, $length);
+ };
+ if($@){
+ print "*E* Timeout waiting for cArg data\n";
+ }
+ $SIG{ALRM} = $old_a;
+ alarm($old_time);
+
+ if($length != $clen){
+ print "\n*E* Warning: wanted to read: $length got $clen\n";
+ }
+ $m .= $m2;
+ ($header, $cmd, $winid, $iarg, $length, $carg) = unpack($PukePacking, $m);
+ }
+ # print("PUKE: Got => $PUKE_NUM2NAME{$cmd}/$cmd\n");
+ # print("PUKE: Got: $cmd, $winid, $iarg, $length, $carg\n");
+ # print("\n");
+ if($winid == undef){ $winid = 0; }
+ $blah = $carg;
+ $blah =~ s/\000//g;
+ print LOG kgettimeofday() . " GOT message: CMD: $PUKE_NUM2NAME{$cmd} WIN: $winid IARG: $iarg LEN: $length CARG: $blah\n" if $DEBUG;
+ #
+ # Check both $cmd and the correct reply -$cmd
+ #
+ my(%ARG) = ('iCommand' => $cmd,
+ 'iWinId' => $winid,
+ 'iArg' => $iarg,
+ 'cArg' => $carg);
+
+ # print "*I* Def handler: $PUKE_DEF_HANDLER{$cmd}\n";
+
+ if($wait == 1 && $winid == $wait_winid && $wait_cmd == $cmd){
+ print LOG kgettimeofday() . " WAIT message: CMD: $PUKE_NUM2NAME{$cmd} WIN: $winid IARG: $iarg LEN: $length CARG: $blah\n" if $DEBUG;
+ ($wait, $wait_winid, $wait_cmd, $wait_carg) = ();
+ return %ARG;
+ }
+
+ if($PUKE_HANDLER{-$cmd}{$winid}){ # one shot/command handler
+ &{$PUKE_HANDLER{-$cmd}{$winid}}(\%ARG);
+ } elsif ($PUKE_HANDLER{$cmd}{$winid}){
+ &{$PUKE_HANDLER{$cmd}{$winid}}(\%ARG);
+ } elsif ($PUKE_W_HANDLER{$cmd}{$winid}) { # widget specific handler
+ &{$PUKE_W_HANDLER{$cmd}{$winid}}(\%ARG);
+ } elsif ($PUKE_DEF_HANDLER{"$cmd"}) {# catch all
+ &{$PUKE_DEF_HANDLER{"$cmd"}}(\%ARG);
+ }
+ else {
+ #
+ # If there was no handler this is a widget creation falling throuhg
+ #
+
+ if($wait == 1 && (substr($wait_carg,0,7) eq substr($carg,0,7))){
+ print LOG kgettimeofday() . " WAI2 message: CMD: $PUKE_NUM2NAME{$cmd} WIN: $winid IARG: $iarg LEN: $length CARG: $blah\n" if $DEBUG;
+ ($wait, $wait_winid, $wait_cmd, $wait_carg) = ();
+ return %ARG;
+ }
+ # No handler at all, unkown reply
+ print("*E* PUKE: Got unkown command: $cmd/$PUKE_NUM2NAME{$cmd}\n");
+ # print("PUKE: Got: $cmd, $winid, $iarg, $carg\n");
+ }
+
+ #
+ # If we're not waiting for a message, return
+ #
+ if(!$wait){
+ ($wait, $wait_winid, $wait_cmd, $wait_carg) = ();
+ return ();
+ }
+
+ my($rin, $rout) =('', '');
+ vec($rin,fileno($PUKEFd),1) = 1;
+ $nfound = select($rout=$rin, undef, undef, 1);
+ if($nfound < 1){
+ print "*E* PUKE: Timed out waiting for reply, returning null\n";
+ print LOG kgettimeofday() . " FAIL message: CMD: $PUKE_NUM2NAME{$wait_cmd} WIN: $wait_winid IARG: ### LEN: $length CARG: $wait_carg\n" if $DEBUG;
+ return ();
+ }
+ }
+}
+
+&addsel($PUKEFd, "PukeRecvMessage", 0);
+
+# Basics are up and running, now init Puke/Ksirc Interface.
+
+my(%ARG) = &PukeSendMessage($PUKE_SETUP, $::PUKE_CONTROLLER, 0, $server, undef, 1);
+
+$PukeMSize = $ARG{'iArg'};
+print "*P* Puke: Initial Setup complete\n";
+print "*P* Puke: Communications operational\n";
+
diff --git a/ksirc/puke/pwidget.cpp b/ksirc/puke/pwidget.cpp
new file mode 100644
index 00000000..d254832b
--- /dev/null
+++ b/ksirc/puke/pwidget.cpp
@@ -0,0 +1,492 @@
+#include "pwidget.h"
+#include "commands.h"
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <qpixmap.h>
+
+PWidget::PWidget(PObject *)
+ : PObject()
+{
+ // kdDebug(5008) << "PWidget constructor called" << endl;
+
+ w = 0;
+ setWidget(0);
+
+ eventList[0] = &PWidget::eventNone;
+ eventList[1] = &PWidget::eventTimer;
+ eventList[2] = &PWidget::eventMouse;
+ eventList[3] = &PWidget::eventMouse;
+ eventList[4] = &PWidget::eventMouse;
+ eventList[5] = &PWidget::eventMouse;
+ eventList[6] = &PWidget::eventKey;
+ eventList[7] = &PWidget::eventKey;
+ eventList[8] = &PWidget::eventFocus;
+ eventList[9] = &PWidget::eventFocus;
+ eventList[10] = &PWidget::eventFocus;
+ eventList[11] = &PWidget::eventFocus;
+ eventList[12] = &PWidget::eventPaint;
+ eventList[13] = &PWidget::eventMove;
+ eventList[14] = &PWidget::eventResize;
+ eventList[15] = &PWidget::eventNone;
+ eventList[16] = &PWidget::eventNone;
+ eventList[17] = &PWidget::eventNone;
+ eventList[18] = &PWidget::eventNone;
+ eventList[19] = &PWidget::eventNone;
+
+ // Connect slots as needed
+
+}
+
+PWidget::~PWidget()
+{
+ // kdDebug(5008) << "PWidget: in destructor" << endl;
+ /*
+ delete widget();
+ w = 0;
+ setWidget(0);
+ */
+}
+
+PObject *PWidget::createWidget(CreateArgs &ca)
+{
+ PWidget *pw = new PWidget();
+ QWidget *tw;
+ if(ca.fetchedObj != 0 && ca.fetchedObj->inherits("QWidget") == TRUE){
+ tw = (QWidget *) ca.fetchedObj;
+ pw->setDeleteAble(FALSE);
+ }
+ else if(ca.parent != 0 && ca.parent->widget()->isWidgetType() == TRUE)
+ tw = new QWidget((QWidget *) ca.parent->widget());
+ else
+ tw = new QWidget();
+ pw->setWidget(tw);
+ pw->setWidgetId(ca.pwI);
+ pw->setPukeController(ca.pc);
+ return pw;
+}
+
+void PWidget::messageHandler(int fd, PukeMessage *pm)
+{
+ PukeMessage pmRet;
+ switch(pm->iCommand){
+ case PUKE_WIDGET_SHOW:
+ widget()->show();
+ pmRet.iCommand = PUKE_WIDGET_SHOW_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_HIDE:
+ widget()->hide();
+ pmRet.iCommand = PUKE_WIDGET_HIDE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_REPAINT:
+ widget()->repaint(pm->iArg);
+ pmRet.iCommand = PUKE_WIDGET_REPAINT_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_RESIZE:
+ {
+ int x, y;
+ int found = sscanf(pm->cArg, "%d\t%d", &x, &y);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,10));
+
+ widget()->resize(x, y);
+
+ pmRet.iCommand = PUKE_WIDGET_RESIZE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = pm->iArg;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ break;
+ case PUKE_WIDGET_MOVE:
+ {
+ int x, y;
+ int found = sscanf(pm->cArg, "%d\t%d", &x, &y);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,10));
+
+ // kdDebug(5008) << "Moving to: " << pm->iArg << " => " << pos[0] << " " << pos[1] << endl;
+ widget()->move(x, y);
+ pmRet.iCommand = PUKE_WIDGET_MOVE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ break;
+ case PUKE_WIDGET_SETMINSIZE:
+ {
+ int x, y;
+ int found = sscanf(pm->cArg, "%d\t%d", &x, &y);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,11));
+
+ widget()->setMinimumSize(x, y);
+
+ pmRet.iCommand = PUKE_WIDGET_SETMINSIZE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ }
+ break;
+ case PUKE_WIDGET_SETMAXSIZE:
+ {
+ int x, y;
+ int found = sscanf(pm->cArg, "%d\t%d", &x, &y);
+ if(found != 2)
+ throw(errorCommandFailed(PUKE_INVALID,12));
+
+ widget()->setMaximumSize(x, y);
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = pm->iArg;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ case PUKE_WIDGET_SETCAPTION:
+ widget()->setCaption(pm->cArg);
+ pmRet.iCommand = PUKE_WIDGET_SETCAPTION_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.iTextSize = strlen(widget()->caption());
+ pmRet.cArg = strdup(widget()->caption());
+ emit outputMessage(fd, &pmRet);
+ free(pmRet.cArg);
+ break;
+ case PUKE_WIDGET_GET_BACKGROUND_COLOUR:
+ {
+ pmRet.cArg = new char[15];
+ pmRet.iTextSize = 15;
+ QColor back = widget()->backgroundColor();
+ sprintf(pmRet.cArg, "%d,%d,%d", back.red(), back.green(), back.blue());
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ emit outputMessage(fd, &pmRet);
+ delete pmRet.cArg;
+ break;
+ }
+ case PUKE_WIDGET_SET_BACKGROUND_PIXMAP:
+ widget()->setBackgroundPixmap(QPixmap(pm->cArg));
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_SET_BACKGROUND_MODE:
+ widget()->setBackgroundMode((QWidget::BackgroundMode) pm->iArg);
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = (int) widget()->backgroundMode();
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+
+ case PUKE_WIDGET_SET_BACKGROUND_COLOUR:
+ {
+ int *pos;
+ pos = (int *) pm->cArg;
+ QColor bg(pos[0], pos[1], pos[2]);
+ QColorGroup cg = QColorGroup(widget()->colorGroup().foreground(),
+ bg,
+ widget()->colorGroup().light(),
+ widget()->colorGroup().dark(),
+ widget()->colorGroup().mid(),
+ widget()->colorGroup().text(),
+ bg);
+ widget()->setPalette(QPalette(cg,cg,cg));
+
+ pmRet.iCommand = -pm->iCommand;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+
+ case PUKE_WIDGET_SET_ENABLED:
+ widget()->setEnabled((bool) pm->iArg);
+ pmRet.iCommand = PUKE_WIDGET_SET_ENABLED_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ case PUKE_WIDGET_RECREATE:
+ {
+ QWidget *nparent = 0x0;
+ if(pm->iArg != 0x0){
+ widgetId wiWidget;
+ wiWidget.fd = fd;
+ wiWidget.iWinId = pm->iArg;
+ nparent = controller()->id2pwidget(&wiWidget)->widget();
+ }
+ if(pm->iTextSize != 3*sizeof(int)){
+ throw(errorCommandFailed(pm->iCommand, pm->iArg));
+ }
+ int *point_show = (int *) pm->cArg;
+
+ qWarning("Recreate: %d %d %d", point_show[0], point_show[1], point_show[3]);
+
+ widget()->reparent(nparent, (WFlags) 0, QPoint(point_show[0], point_show[1]), point_show[3]);
+
+ pmRet.iCommand = PUKE_WIDGET_RECREATE_ACK;
+ pmRet.iWinId = pm->iWinId;
+ pmRet.iArg = 0;
+ pmRet.cArg = 0;
+ emit outputMessage(fd, &pmRet);
+ break;
+ }
+ default:
+ PObject::messageHandler(fd, pm);
+ }
+}
+
+void PWidget::setWidget(QObject *_w)
+{
+ if(_w != 0 && _w->inherits("QWidget") == FALSE)
+ {
+ errorInvalidSet(_w);
+ return;
+ }
+
+ w = (QWidget *) _w;
+ if(w != 0){
+ widget()->installEventFilter(this);
+ }
+ PObject::setWidget(_w);
+}
+
+QWidget *PWidget::widget()
+{
+ // kdDebug(5008) << "PWidget widget called" << endl;
+ return w;
+}
+
+// PWidget specific
+bool PWidget::eventFilter(QObject *o, QEvent *e)
+{
+ if(e->type() < 20 && e->type() >= 0){
+ (this->*(eventList[e->type()]))(o,e);
+ }
+ else{
+ PukeMessage pm;
+ widgetId wI;
+
+ wI = widgetIden();
+ pm.iCommand = PUKE_EVENT_UNKOWN;
+ pm.iWinId = wI.iWinId;
+ pm.iArg = e->type();
+ pm.cArg = 0;
+ emit outputMessage(wI.fd, &pm);
+
+ }
+
+ return false;
+}
+
+void PWidget::eventNone(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ // kdDebug(5008) << "PWidget: eventNone" << endl;
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1030 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+ pm.cArg = 0;
+
+ emit outputMessage(wI.fd, &pm);
+
+}
+
+void PWidget::eventTimer(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ QTimerEvent *et = (QTimerEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = PUKE_WIDGET_EVENT_TIMER;
+ pm.iWinId = wI.iWinId;
+ pm.iArg = et->timerId();
+ pm.cArg = 0;
+
+ emit outputMessage(wI.fd, &pm);
+
+}
+
+void PWidget::eventMouse(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ QMouseEvent *me = (QMouseEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+
+ // special cArg handling
+ pm.iTextSize = 4*sizeof(int);
+ int *icArg = new int[4];
+ icArg[0] = me->x();
+ icArg[1] = me->y();
+ icArg[2] = me->button();
+ icArg[3] = me->state();
+ pm.cArg = (char *) icArg;
+
+ emit outputMessage(wI.fd, &pm);
+
+ delete[] icArg;
+
+}
+
+void PWidget::eventKey(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ QKeyEvent *ke = (QKeyEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+
+ // special cArg handling
+ pm.iTextSize = 3*sizeof(int);
+ int *icArg = new int[3];
+ icArg[0] = ke->key();
+ icArg[1] = ke->ascii();
+ icArg[2] = ke->state();
+ pm.cArg = (char *) icArg;
+
+ emit outputMessage(wI.fd, &pm);
+
+ delete[] icArg;
+
+}
+
+void PWidget::eventFocus(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ // kdDebug(5008) << "PWidget: eventFocus" << endl;
+
+ QFocusEvent *fe = (QFocusEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+
+ pm.cArg = new char[2];
+ pm.iTextSize = 2*sizeof(char);
+ pm.cArg[0] = fe->gotFocus();
+ pm.cArg[1] = fe->lostFocus();
+
+ emit outputMessage(wI.fd, &pm);
+
+ delete[] pm.cArg;
+}
+
+void PWidget::eventPaint(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+ pm.cArg = 0;
+
+ emit outputMessage(wI.fd, &pm);
+
+}
+
+void PWidget::eventMove(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ QMoveEvent *me = (QMoveEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+
+ // special cArg handling
+ pm.iTextSize = 4*sizeof(int);
+ int *icArg = new int[4];
+ icArg[0] = me->pos().x();
+ icArg[1] = me->pos().y();
+ icArg[2] = me->oldPos().x();
+ icArg[3] = me->oldPos().y();
+ pm.cArg = (char *) icArg;
+
+ emit outputMessage(wI.fd, &pm);
+
+ delete[] icArg;
+
+}
+
+void PWidget::eventResize(QObject *, QEvent *e)
+{
+ PukeMessage pm;
+ widgetId wI;
+
+ QResizeEvent *re = (QResizeEvent *)(e);
+
+ wI = widgetIden();
+ pm.iCommand = - e->type() - 1020; // 1020 offset for events
+ pm.iWinId = wI.iWinId;
+ pm.iArg = 0;
+
+ // special cArg handling
+ pm.iTextSize = 4*sizeof(int);
+ int *icArg = new int[4];
+ icArg[0] = re->size().height();
+ icArg[1] = re->size().width();
+ icArg[2] = re->oldSize().height();
+ icArg[3] = re->oldSize().width();
+ pm.cArg = (char *) icArg;
+
+ emit outputMessage(wI.fd, &pm);
+
+ delete[] icArg;
+
+}
+
+
+#include "pwidget.moc"
+
diff --git a/ksirc/puke/pwidget.h b/ksirc/puke/pwidget.h
new file mode 100644
index 00000000..f7f748fb
--- /dev/null
+++ b/ksirc/puke/pwidget.h
@@ -0,0 +1,45 @@
+#ifndef PWIDGET_H
+#define PWIDGET_H
+
+class PWidget;
+
+#include <qobject.h>
+#include <qwidget.h>
+#include "pobject.h"
+#include "pmessage.h"
+
+class PWidget : public PObject
+{
+ Q_OBJECT
+ public:
+ PWidget(PObject *parent = 0);
+ virtual ~PWidget();
+
+ // Init and setup code
+ static PObject *createWidget(CreateArgs &ca);
+
+ virtual void messageHandler(int fd, PukeMessage *pm);
+
+ virtual void setWidget(QObject *w = 0x0);
+ virtual QWidget *widget();
+
+ protected:
+ bool eventFilter(QObject *o, QEvent *e);
+
+ private:
+ QWidget *w;
+
+ void (PWidget::*eventList[20])(QObject *, QEvent *);
+
+ void eventNone(QObject *, QEvent *);
+ void eventTimer(QObject *, QEvent *);
+ void eventMouse(QObject *, QEvent *);
+ void eventKey(QObject *, QEvent *);
+ void eventFocus(QObject *, QEvent *);
+ void eventPaint(QObject *, QEvent *);
+ void eventMove(QObject *, QEvent *);
+ void eventResize(QObject *, QEvent *);
+
+};
+
+#endif
diff --git a/ksirc/puke/pwidget.pm b/ksirc/puke/pwidget.pm
new file mode 100644
index 00000000..5a73d393
--- /dev/null
+++ b/ksirc/puke/pwidget.pm
@@ -0,0 +1,231 @@
+
+package PWidget;
+@ISA = qw(PBase);
+use strict;
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->{widgetType} = $::PWIDGET_WIDGET;
+
+ # Default handlers
+ $self->installHandler($::PUKE_WIDGET_EVENT_RESIZE,
+ sub {$self->resizeEvent(@_)});
+ $self->installHandler($::PUKE_WIDGET_EVENT_PAINT,
+ sub {$self->paintEvent(@_)});
+ $self->installHandler($::PUKE_WIDGET_EVENT_MOVE,
+ sub {$self->moveEvent(@_)});
+ $self->installHandler($::PUKE_EVENT_UNKOWN,
+ sub {$self->miscEvent(@_)});
+ # Examplesto listen for mouse events
+ # $self->installHandler($::PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS,
+ # sub {$self->mousePressEvent(@_)});
+ #$self->installHandler($::PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE,
+ # sub {$self->mouseReleaseEvent(@_)});
+
+
+
+ if($class eq 'PWidget'){
+ $self->create();
+ }
+
+ return $self;
+
+}
+
+sub close {
+ my $self = shift;
+
+ $self->hide();
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_DELETE,
+ 'CallBack' => sub {});
+
+ $self->{DESTROYED} = 1;
+
+}
+
+sub show {
+ my $self = shift;
+
+ # make sure we are runable then show continue.
+ my @ARG = ();
+ # $self->canRun($self, \&PWidget::show, \@ARG) || return;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SHOW);
+}
+
+sub hide {
+ my $self = shift;
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_HIDE);
+}
+
+sub repaint {
+ my $self = shift;
+
+ my $erase = shift; # 1 for erase and reapint
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_REPAINT,
+ 'iArg' => $erase);
+}
+
+sub resize {
+ my $self = shift;
+
+ my $width = shift;
+ my $height = shift;
+
+ my $carg = "$width\t$height";
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_RESIZE,
+ 'cArg' => $carg);
+
+}
+
+sub move {
+ my $self = shift;
+
+ my $x = shift;
+ my $y = shift;
+
+ my $carg = "$x\t$y";
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_MOVE,
+ 'cArg' => $carg);
+
+}
+
+sub setMinimumSize {
+ my $self = shift;
+
+ my $w = shift;
+ my $h = shift;
+
+ my $carg = "$w\t$h";
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SETMINSIZE,
+ 'cArg' => $carg,
+ 'CallBack' => sub {});
+
+}
+
+sub setMaximumSize {
+ my $self = shift;
+
+ my $w = shift;
+ my $h = shift;
+
+ my $carg = "$w\t$h";
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SETMAXSIZE,
+ 'cArg' => $carg,
+ 'CallBack' => sub {});
+
+}
+
+
+sub setCaption {
+ my $self = shift;
+
+ my $text = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SETCAPTION,
+ 'cArg' => $text,
+ 'CallBack' => sub {});
+
+}
+
+sub resizeEvent {
+ my $self = shift;
+
+ my %ARG = %{shift()};
+
+ my($h, $w, $oh, $ow) = unpack("iiii", $ARG{'cArg'});
+ $self->{'height'} = $h;
+ $self->{'width'} = $w;
+
+}
+
+sub paintEvent {
+}
+
+sub moveEvent {
+ my $self = shift;
+
+ my %ARG = %{shift()};
+
+ my($x, $y, $ox, $oy) = unpack("iiii", $ARG{'cArg'});
+ $self->{'x'} = $x;
+ $self->{'y'} = $y;
+
+}
+
+sub miscEvent {
+}
+
+sub backgroundColor {
+ my $self = shift;
+
+ my %arg = $self->sendMessage('iCommand' => $::PUKE_WIDGET_GET_BACKGROUND_COLOUR,
+ 'WaitFor' => 1);
+
+ # print "Got: " . $arg{'cArg'} . "\n";
+
+ $arg{'cArg'} =~ s/\000//g;
+ my ($r, $g, $b) = split(/,/, $arg{'cArg'});
+
+ return ($r, $g, $b);
+}
+
+sub setBackgroundColor {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SET_BACKGROUND_COLOUR,
+ 'cArg' => pack("i3", shift(), shift(), shift()),
+ 'CallBack' => sub {});
+
+}
+
+sub setBackgroundPixmap {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SET_BACKGROUND_PIXMAP,
+ 'cArg' => shift(),
+ 'CallBack' => sub {});
+}
+
+sub setBackgroundMode {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SET_BACKGROUND_MODE,
+ 'iArg' => shift(),
+ 'CallBack' => sub {});
+}
+
+
+sub setEnabled {
+ my $self = shift;
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_SET_ENABLED,
+ 'iArg' => shift(),
+ 'CallBack' => sub {});
+
+}
+
+sub recreate {
+ my $self = shift;
+
+ my $nparent = shift;
+ my $wflags = shift; # Not used!
+
+ $self->sendMessage('iCommand' => $::PUKE_WIDGET_RECREATE,
+ 'iArg' => $nparent ? $nparent->{'iWinId'} : 0,
+ 'cArg' => pack("iii", shift(), shift(), shift()),
+ 'CallBack' => sub {});
+
+}
+
+package main;
+
+1;
diff --git a/ksirc/puke/small.pl b/ksirc/puke/small.pl
new file mode 100644
index 00000000..7e7b251d
--- /dev/null
+++ b/ksirc/puke/small.pl
@@ -0,0 +1,65 @@
+&docommand("/load pbase.pm");
+&docommand("/load pwidget.pm");
+&docommand("/load pframe.pm");
+&docommand("/load pboxlayout.pm");
+&docommand("/load plined.pm");
+&docommand("/load pbutton.pm");
+&docommand("/load ppushbt.pm");
+&docommand("/load pprogress.pm");
+&docommand("/load ptablevw.pm");
+&docommand("/load plistbox.pm");
+&docommand("/load plabel.pm");
+
+$pw = new PWidget;
+$pw->resize(250,500);
+
+$pf = new PFrame($pw);
+$pf->setFrameStyle($PFrame::Panel|$PFrame::Sunken, 1);
+$pf->move(50,50);
+$pf->resize(150,400);
+$pf->setMinimumSize(50,50);
+
+$pf2 = new PFrame($pw);
+$pf2->setFrameStyle($PFrame::Panel|$PFrame::Raised, 1);
+$pf2->setMinimumSize(50,50);
+
+
+$pl = new PListBox($pw);
+$pl->move(50,50);
+$pl->resize(300, 50);
+$pl->setMinimumSize(50,50);
+
+$label = new PLabel($pw);
+$label->setText("Label");
+$label->setMinimumSize(50,50);
+
+$pb = new PBoxLayout($pw, $PBoxLayout::TopToBottom, 5);
+$pb2 = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+$pb3 = new PBoxLayout($PBoxLayout::TopToBottom, 5);
+$pb->addLayout($pb2);
+$pb->addLayout($pb3);
+$pb->addWidget($pf, 1, $PBoxLayout::AlignCenter);
+$pb2->addWidget($pf2, 10, $PBoxLayout::AlignCenter);
+$pb2->addWidget($pl, 10, $PBoxLayout::AlignCenter);
+$pb2->addWidget($label, 10, $PBoxLayout::AlignCenter);
+
+$pb4 = new PBoxLayout($PBoxLayout::RightToLeft, 1);
+$pb3->addLayout($pb4);
+
+$pb4->addWidget($pf2, 0, $PBoxLayout::AlignCenter);
+
+$pbutton = new PPushButton($pw);
+$pbutton->setText("Hello");
+#$pbutton->setPixmap("/opt/kde/share/icons/ksirc.gif");
+$pbutton->setMaximumSize(50,50);
+$pbutton->setMinimumSize(50,50);
+$pb->addWidget($pbutton, 10, $PBoxLayout::AlignCenter);
+
+$pline = new PLineEdit($pw);
+$pline->setMinimumSize(50, 30);
+$pline->resize(200, 20);
+$pline->setMaximumSize(1000, 30);
+$pline->setText("Hello!");
+$pb->addWidget($pline, 0);
+
+$pw->show; \ No newline at end of file
diff --git a/ksirc/puke/test.pl b/ksirc/puke/test.pl
new file mode 100644
index 00000000..4b4c3169
--- /dev/null
+++ b/ksirc/puke/test.pl
@@ -0,0 +1,63 @@
+&docommand("/load pbase.pm");
+&docommand("/load pwidget.pm");
+&docommand("/load pframe.pm");
+&docommand("/load pboxlayout.pm");
+&docommand("/load plined.pm");
+&docommand("/load pbutton.pm");
+&docommand("/load ppushbt.pm");
+&docommand("/load pprogress.pm");
+&docommand("/load ptablevw.pm");
+&docommand("/load plistbox.pm");
+
+$pw = new PWidget;
+$pw->resize(250,500);
+
+$pf = new PFrame($pw);
+$pf->setFrameStyle($PFrame::Panel|$PFrame::Sunken, 1);
+$pf->move(50,50);
+$pf->resize(150,400);
+
+$pf2 = new PListBox($pw);
+#$pf2->setFrameStyle($PFrame::Box|$PFrame::Raised, 1);
+$pf2->move(50,50);
+$pf2->resize(50,300);
+
+$pb = new PBoxLayout($pw, $PBoxLayout::TopToBottom, 5);
+$pb->addWidget($pf, 10);
+$pb->addWidget($pf2, 5);
+
+$pb2 = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+$pb->addLayout($pb2, 0);
+
+$pl = new PLineEdit($pw);
+$pl->setMinimumSize(30,50);
+$pl->setMaximumSize(30,1000);
+$pl->setText("Blah blah");
+#$pl->resize(50,75);
+$pb2->addWidget($pl, 0, $PBoxLayout::AlignCenter);
+
+$pp = new PPushButton($pw);
+$pp->setMinimumSize(65,65);
+$pp->setMaximumSize(65,65);
+$pp->setPixmap("/opt/kde/share/icons/ksirc.gif");
+$pp->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub
+ {
+ print "*I* Clocked!\n";
+ $pw->close;
+ }
+ );
+$pb2->addWidget($pp, 0, $PBoxLayout::AlignRight);
+
+for(my $i = 0; $i < 100; $i++){
+ $pf2->insertPixmap("/opt/kde/share/icons/ksirc.gif", -1);
+ $pf2->insertText("Test $i", -1);
+}
+
+$pp = new PProgress;
+
+$pw->onNext(sub{$pw->show()});
+$pp->onNext(sub{$pw->show(); $pp->show()});
+#$pw->show();
+#$pf->show();
+#$pf2->show();
+
diff --git a/ksirc/puke/tester.pl b/ksirc/puke/tester.pl
new file mode 100755
index 00000000..d3b25462
--- /dev/null
+++ b/ksirc/puke/tester.pl
@@ -0,0 +1,28 @@
+#!/usr/bin/perl
+
+$sock = $ENV{'HOME'} . "/.ksirc.socket";
+
+use Socket;
+
+$proto = getprotobyname('tcp');
+socket(fd, PF_UNIX, SOCK_STREAM, 0) || die "Sock failed: $!\n";
+$sun = sockaddr_un($sock);
+print "Connecting to $sock\n";
+connect(fd,$sun) || die "Connect failed: $!\n";
+
+select(fd); $| = 1; select(STDOUT);
+
+while(1){
+ print "Command: ";
+ chomp($cmd = <STDIN>);
+ print "WinId: ";
+ chomp($winid = <STDIN>);
+ print "iArg: ";
+ chomp($iarg = <STDIN>);
+ print "cArg: ";
+ chomp($carg = <STDIN>);
+ $m = pack("iiia50xx", $cmd, $winid, $iarg, $carg);
+ print fd $m;
+
+}
+
diff --git a/ksirc/puke/user_monitor.ks b/ksirc/puke/user_monitor.ks
new file mode 100644
index 00000000..a0b2a56a
--- /dev/null
+++ b/ksirc/puke/user_monitor.ks
@@ -0,0 +1,440 @@
+&docommand("/load pbase.pm");
+&docommand("/load pwidget.pm");
+&docommand("/load pframe.pm");
+&docommand("/load ptablevw.pm");
+&docommand("/load plistbox.pm");
+&docommand("/load pboxlayout.pm");
+&docommand("/load plabel.pm");
+&docommand("/load pmenudta.pm");
+&docommand("/load ppopmenu.pm");
+&docommand("/load pbutton.pm");
+&docommand("/load ppushbt.pm");
+&docommand("/load palistbox.pm");
+&docommand("/load plined.pm");
+
+use Net::SMTP;
+
+my $WHO = "$ENV{HOME}/who_online.pl";
+my $INFO = "$ENV{HOME}/mysql_fetch";
+
+my %ALLOW_MULT = ();
+$ALLOW_MULT{'asj'} = 1;
+$ALLOW_MULT{'administrator'} = 1;
+
+@PAGE_PPL = ('andrew', 'lee', 'william', 'seamus', 'gerry', 'jason', 'derik');
+
+$PUKE_DEF_HANDLER{-999} = sub {};
+
+package UserList;
+
+@ISA = qw(PFrame);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+
+ $self->create();
+
+ my $gm = new PBoxLayout($self, $PBoxLayout::TopToBottom, 5);
+
+ my $list_box = new PListBox($self);
+ $gm->addWidget($list_box, 5);
+
+ my $event_list = new PListBox($self);
+ $event_list->setMaximumSize(110, 2000);
+ $event_list->setMinimumSize(110, 1);
+ # $event_list->setFrameStyle($PFrame::Box|$PFrame::Raised);
+ $event_list->setFrameStyle(0);
+ $event_list->setBackgroundColor($self->backgroundColor());
+ # Turn off the widget so people can't click in it
+ $event_list->setEnabled(0);
+ $event_list->setAutoScrollBar(0);
+ $event_list->setScrollBar(0);
+ $gm->addWidget($event_list, 5);
+
+ my $gm_but = new PBoxLayout($PBoxLayout::LeftToRight, 5);
+ $gm->addLayout($gm_but, 5);
+
+ my $refresh_but = new PPushButton($self);
+ $refresh_but->setMaximumSize(25, 2000);
+ $refresh_but->setMinimumSize(25, 25);
+ $refresh_but->installHandler($::PUKE_BUTTON_PRESSED_ACK, sub { } );
+ $refresh_but->installHandler($::PUKE_BUTTON_RELEASED_ACK, sub { } );
+ $refresh_but->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub { &::docommand("refresh_users"); } );
+ $refresh_but->setText("&Refresh Users");
+ $gm_but->addWidget($refresh_but, 5);
+
+ my $page_dialog = new pageDialog();
+ $page_dialog->resize(265, 250);
+
+ my $page_but = new PPushButton($self);
+ $page_but->setMaximumSize(25, 2000);
+ $page_but->setMinimumSize(25, 25);
+ $page_but->installHandler($::PUKE_BUTTON_PRESSED_ACK, sub { } );
+ $page_but->installHandler($::PUKE_BUTTON_RELEASED_ACK, sub { } );
+ $page_but->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub { $self->{'page_dialog'}->show(); } );
+ $page_but->setText("&Page");
+ $gm_but->addWidget($page_but, 5);
+
+
+ my $user_count = new PLabel($self);
+ $user_count->setMaximumSize(25, 2000);
+ $user_count->setMinimumSize(25, 25);
+ $user_count->setFrameStyle($PFrame::Box|$PFrame::Raised);
+ $gm->addWidget($user_count, 5);
+
+ $user_count->setText($list_box->{count});
+
+ my $menu = new PPopupMenu();
+ my $menu_online = $menu->insertText("Online Time");
+ $menu->installMenu($menu_online, sub { &::say("online " . $self->{'list_box'}->currentText() . "\n"); });
+ my $menu_info = $menu->insertText("User Information");
+ $menu->installMenu($menu_info, sub { my $user = $self->{'list_box'}->currentText(); print `$INFO $user`; });
+ my $menu_info2 = $menu->insertText("Connection Information");
+ $menu->installMenu($menu_info2, sub { my $exec = "$WHO info " . $self->{'list_box'}->currentText(); print `$exec`; });
+
+ my $menu_rem = $menu->insertText("Remove User From List");
+ $menu->installMenu($menu_rem, sub {
+ my $user = $self->{'list_box'}->currentText();
+ my $count = $self->{'list_box'}->current();
+ $self->{'list_box'}->removeItem($count);
+ if($users_online->{$user} > 0){
+ $users_online->{$user}--;
+ }
+ if($users_online->{$user} == 0){
+ delete $users_online->{$user};
+ }
+ });
+ my $menu_kill = $menu->insertText("Disconnect User");
+ $menu->installMenu($menu_kill, sub { my $exec = "$WHO reset " . $self->{'list_box'}->currentText(); print "Running: $exec\n"; system($exec); });
+
+ # Install listner for mouse events
+
+ $list_box->installHandler($::PUKE_WIDGET_EVENT_MOUSEBUTTONPRESS,
+ sub {$self->popupMenu(@_)});
+ $list_box->installHandler($::PUKE_WIDGET_EVENT_MOUSEBUTTONRELEASE,
+ sub {$self->popdownMenu(@_)});
+
+
+ @$self{'gm', 'list_box', 'user_count', 'max', 'event_list', 'menu', 'page_dialog', 'page_but', 'gm_but'} = ($gm, $list_box, $user_count, 0, $event_list, $menu, $page_dialog, $page_but, $gm_but);
+
+ return $self;
+
+}
+
+sub DESTROY {
+ # $self->hide();
+ $self->{'gm'}->DESTROY;
+ delete $self->{'gm'};
+ delete $self->{'gm2'};
+ $self->{'list_box'}->DESTROY;
+ delete $self->{'list_box'};
+ $self->{'use_count'}->DESTROY;
+ delete $self->{'use_count'};
+
+ $self->SUPER::DESTROY();
+}
+
+sub popupMenu {
+ my $self = shift;
+ my $arg = shift;
+
+ my($x, $y, $b, $s) = unpack("i4", $arg->{'cArg'});
+ # print "$x $y $b $s\n";
+
+ # Only popup for button 2
+ $self->{'menu'}->popupAtCurrent() if $b == 2;
+}
+
+sub popdownMenu {
+ my $self = shift;
+
+ $self->{'menu'}->hide();
+}
+
+sub addEvent {
+ my $self = shift;
+
+ my $event_list = $self->{'event_list'};
+
+ while($event_list->{count} >= 10){
+ $event_list->removeItem($event_list->{count});
+ }
+
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+
+ my $text = sprintf("%02d:%02d:%02d " . shift(), $hour, $min, $sec);
+
+ $event_list->insertText($text, 0);
+}
+
+sub insertText {
+ my $self = shift;
+
+ $self->{'list_box'}->insertText(@_);
+
+ if($self->{'list_box'}->{count} > $self->{'max'}){
+ $self->{'max'} = $self->{'list_box'}->{count};
+ }
+
+ $self->{'user_count'}->setText("C: " . $self->{'list_box'}->{count} . " M: " . $self->{'max'});
+}
+
+sub removeItem {
+ my $self = shift;
+
+ $self->{'list_box'}->removeItem(@_);
+
+ $self->{'user_count'}->setText("C: " . $self->{'list_box'}->{count} . " M: " . $self->{'max'});
+}
+
+sub text {
+ my $self = shift;
+
+ return $self->{'list_box'}->text(@_);
+}
+
+sub count {
+ my $self = shift;
+
+ return $self->{'list_box'}->{count};
+}
+
+sub AUTOLOAD {
+ my $self = shift;
+
+ return if $AUTOLOAD =~ /::DESTROY$/;
+
+ $AUTOLOAD =~ /.+::(.+)/;
+ return $self->{'list_box'}->$1(@_);
+}
+
+package pageDialog;
+
+@ISA = qw(PFrame);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new($class, @_);
+ $self->create();
+
+ my $gm = new PBoxLayout($self, $PBoxLayout::TopToBottom, 5);
+
+ my $ppl_label = new PLabel($self);
+ $ppl_label->setText("Person to page:");
+ $ppl_label->setMaximumSize(25, 2000);
+ $ppl_label->setMinimumSize(25, 25);
+ $gm->addWidget($ppl_label, 5);
+
+ my $page_ppl = new PListBox($self);
+ $gm->addWidget($page_ppl, 5);
+
+ my $msg_label = new PLabel($self);
+ $msg_label->setText("Message to page:");
+ $msg_label->setMaximumSize(25, 2000);
+ $msg_label->setMinimumSize(25, 25);
+ $gm->addWidget($msg_label, 5);
+
+ my $page_msg = new PLineEdit($self);
+ $page_msg->setMaximumSize(25, 2000);
+ $page_msg->setMinimumSize(25, 25);
+ $page_msg->setMaxLength(49);
+ $page_msg->installHandler($::PUKE_EVENT_UNKOWN, sub {});
+ $gm->addWidget($page_msg, 5);
+
+ my $send_but = new PPushButton($self);
+ $send_but->setMaximumSize(25, 2000);
+ $send_but->setMinimumSize(25, 25);
+ $send_but->installHandler($::PUKE_BUTTON_PRESSED_ACK, sub { } );
+ $send_but->installHandler($::PUKE_BUTTON_RELEASED_ACK, sub { } );
+ $send_but->installHandler($::PUKE_BUTTON_CLICKED_ACK, sub { &::docommand("msg #polarcom page " . $self->{'page_ppl'}->currentText() . " " . $self->{'page_msg'}->text()); $self->hide() } );
+ $send_but->setText("&Send");
+ $gm->addWidget($send_but, 5);
+
+ $self->setCaption("Page User");
+
+ @$self{'gm', 'page_ppl', 'page_msg', 'send_but', 'msg_label', 'ppl_label'} =
+ ($gm, $page_ppl, $page_msg, $send_but, $msg_label, $ppl_label);
+
+ return $self;
+
+}
+
+sub show {
+ my $self = shift;
+ my $page_ppl = $self->{'page_ppl'};
+
+ $self->hide();
+
+ my $c = $page_ppl->current();
+
+ $page_ppl->clear();
+ my $person;
+ foreach $person (@main::PAGE_PPL) {
+ $page_ppl->insertText($person, -1);
+ }
+ $page_ppl->setCurrentItem($c);
+
+ $self->resize(265, 250);
+ $self->recreate(0, 0, 265, 250, 1);
+
+# $self->SUPER::show();
+#
+# $self->resize(265,250);
+# $self->move(400,270);
+
+}
+
+
+package main;
+
+
+if($online == undef){
+ $online = new UserList();
+ $online->setCaption("Users Online");
+ $online->show();
+ $online->resize(196,740);
+ $online->move(823,0);
+
+ eval {
+ $main::polar = new PWidget();
+ $main::polar->fetchWidget("199.247.156.200_toplevel");
+ $main::polar->move(0,0);
+ $main::polar->resize(816, 740);
+ };
+ if($@) {
+ eval {
+ $main::polar = new PWidget();
+ $main::polar->fetchWidget("199.247.156.200_#polarcom_toplevel");
+ $main::polar->move(0,0);
+ $main::polar->resize(816, 740);
+ };
+ }
+ $users_online = {};
+}
+
+sub hook_online_mon {
+ my $channel = shift;
+ my $msg = shift;
+ return unless $channel eq '#polarcom';
+ return unless $msg =~ /Login|Logoff/;
+ return if $msg =~ /administrator/;
+
+ if($msg =~ /ice: Login (\S+)/){
+ my $nick = $1;
+ #return if $nick =~ /administrator/;
+ $nick =~ s/(\S+)\@\S+/$1/g;
+ $online->addEvent("On: $nick");
+ if($users_online->{$nick} > 0){
+ my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
+ my $date;
+ chomp($date = `date`);
+ my $times = $users_online->{$nick} + 1;
+ &say("$date Duplicate login for $nick, logged on $times times") if (($main::nick eq 'action') || ($nick eq 'administrator'));
+ $online->addEvent("Duplicate: $nick");
+ if($ALLOW_MULT{$nick} != 1){
+ my $exec = "$WHO reset " . $nick;
+ print "*I* Running: $exec\n";
+ system($exec);
+ my $smtp = new Net::SMTP->new;
+ $smtp->mail("sysadmin\@polarcom.com");
+ $smtp->to("$nick\@polarcom.com", "sysadmin\@polarcom.com");
+ $smtp->data();
+ $smtp->datasend("To: $nick\@polarcom.com\n");
+ $smtp->datasend("From: Polarcom System Admin <sysadmin\@polarcom.com>\n");
+ $smtp->datasend("Subject: Security Alert\n\n\n");
+ $smtp->datasend("To: $nick\n");
+ $smtp->datasend("Your account was simultaneously access by 2 or more users.\n");
+ $smtp->datasend("The second user was automatically terminated.\n\n");
+ $smtp->datasend("If you have any question, please email sysadmin\@polarcom.com\n");
+ $smtp->datasend("-- Security Monitor\n");
+ $smtp->datasend();
+ $smtp->quit();
+ }
+ else {
+ print "*I* $nick not being removed, in exclude list\n";
+ }
+ }
+ # Make the list sorted
+ my $i = 0;
+ SEARCH: while($online->text($i) ne undef){
+
+ if(($online->text($i) cmp $nick) >= 0){
+ last SEARCH;
+ }
+ $i++;
+ }
+ $online->insertText($nick, $i);
+ $users_online->{$nick}++;
+ }
+ elsif($msg =~ /ice: Logoff (\S+)/){
+ my $i = 0;
+ my $nick = $1;
+ $nick =~ s/(\S+)\@\S+/$1/g;
+ # print "Trying to logoff: $nick\n";
+ $online->addEvent("Off: $nick");
+ if($users_online->{$nick} > 0){
+ # print "$nick in list\n";
+ $users_online->{$nick}--;
+ if($users_online->{$nick} == 0){
+ delete $users_online->{$nick};
+ }
+ while($online->text($i) ne undef){
+ if($online->text($i) eq $nick){
+ # print "Removing $i for $nick which is really: " . $online->text($i) . "\n";
+ $online->removeItem($i);
+ return;
+ }
+ $i++;
+ }
+ }
+ }
+}
+
+&addhook("public", "online_mon");
+
+sub hook_get_users {
+ my $mesg = shift;
+
+ if($mesg =~ /^send users online/){
+ my $reply = 'Online: ';
+ for($i = 0; $i <= $online->count(); $i++){
+ $reply .= $online->text($i) . " ";
+ }
+ &msg($who, $reply);
+ }
+ if($mesg = /Online: (.+)\s+$/){
+ &tell("*I* Updating user list from $who");
+ $online->addEvent("Update user list: $who");
+ my $users = $1;
+ while($online->count() > 0){
+ $online->removeItem(0);
+ }
+ $users_online = {};
+ foreach $user (split(/\s+/, $users)){
+ $users_online->{$user}++;
+ $online->insertText($user, -1);
+ }
+ }
+}
+
+&addhook("msg", "get_users");
+
+sub cmd_refresh_users {
+ $online->clear();
+ $users_online = {};
+ my $output = `$WHO list`;
+ my @users = split(/\n/, $output);
+ # while($online->count() > 0){
+ # $online->removeItem(0);
+ #}
+ foreach $user (@users){
+ $user =~ s/(\S+)\@\S+/$1/g;
+ next if $user eq '';
+ next if $user =~ /administrator/;
+ $users_online->{$user}++;
+ $online->insertText($user, -1);
+ }
+}
+
+&addcmd("refresh_users");
diff --git a/ksirc/puke/widgethdlr.h b/ksirc/puke/widgethdlr.h
new file mode 100644
index 00000000..6c3e93c7
--- /dev/null
+++ b/ksirc/puke/widgethdlr.h
@@ -0,0 +1,3 @@
+void hdlrCreateWidget(int fd, PukeMessage *pm);
+void hdlrShowWidget(int fd, PukeMessage *pm);
+
diff --git a/ksirc/relnotes b/ksirc/relnotes
new file mode 100644
index 00000000..6207f2f9
--- /dev/null
+++ b/ksirc/relnotes
@@ -0,0 +1,107 @@
+~b~12,4 kSirc Release Notes for 981220
+
+Work over the past month or so has concentrated in 2 majour areas,
+memory consumption and Puke. There's also been some minor features
+etc.
+
+~2Misc Features:
+
+~4~b*~C If you bring up a menu, ctrl+letter generates an on the fly accelerator
+~4~b*~C Scroll back size if setable through the Prefrences
+~4~b*~C Setable background pixmap, it ~b~ureally~b~u sucks
+~4~b*~C New icons for the server controller/docking. Thanks mosfet
+
+~2Memory:
+
+~4~b*~C The last few release kSirc has had a gradual memory leak, fixed.
+~4~b*~C Created KPlunger a generic memory debugger and leak tracker for C++.
+~4~b*~C Fixed a bunch of bufffer problems which caused kSirc die on non-intel
+~4~b*~C Reduced memory footprint, and effeciency.
+
+~2Puke:
+~4~b*~C Reduced endian dependance
+~4~b*~C Lots to come soon
+
+~b~12,4 kSirc Release Notes for 981107
+
+This release is mostly a bug fix release.
+
+Bugs Fixed:
+
+~4~b*~C ~3Title Bar now works with not umode set, away, etc
+~4~b*~C ~3Puke now works on most machines I can find. This means you can stop whinning about dcc being broken.
+~4~b*~C ~3Puke's now pretty much portable.
+~4~b*~C ~3Lots of little bugs all over the place.
+
+~b~12,4 kSirc Release Notes for 981025
+
+~2New Features:
+
+~4~b*~C ~3Mini Web Server
+~4~b*~C ~3MDI Mode (very experimental)
+~4~b*~C ~3Better Caption support with optional Topic
+~4~b*~C ~3New Server Picker, thanks Atko
+~4~b*~C ~3Selecting windows in SC brings them to foreground
+~4~b*~C ~3Release notes are displayed to the screen on first run
+~4~b*~C ~3Improved online help with /help
+
+
+~b~12,4 Web Server Notes
+
+Credit: Thanks to Roberto for the PWS config dialog,
+99.9% of the work is his.
+
+The web server setup is a graphical configuration
+for the freely available web server called
+mathopd. It's run/controlled via kSirc and
+I've some fun running it.
+
+Couple notes to remember, you cannot use ports
+< 1024. I'm not going to run anything as root,
+since running a web server is dangerous enough,
+much less giving potential root access away.
+
+/url command will show you a valid url
+for you sure. Replace <port> with the port(s)
+that the web server is running on.
+
+~b~12,4 MDI Mode
+
+Credit: Timothy Whitfield, ported the MDI interface
+from kEirc into a generic MDI interface for KDE.
+
+Now, if you want to use it, here's a bunch of
+problems I've already seen:
+
+1. Acelerator keys are all messed up. pgup/dn
+is a good example. It just doesn't work.
+
+2. If you minimize something, you have to
+double click on the channel name in the server
+controller to bring it back.
+
+3. You cannot minimize the server controller.
+
+4. You can drag windows off the screen in never
+never land.
+
+I'm personally not a big fan of the MDI interface
+but I'll work on making it work right as time permits.
+
+~b~12,4 OTHER Stuff
+
+There's been a bunch of bug fixes everywhere.
+
+Cut & paste now cleans up correctly most of the time now.
+
+New release notes box, I think it's useful. What do
+you think?
+
+Caption should be more useful now. I think this is the
+best way to display the topic on the screen.
+
+Server controller is in and needs some more work. Basic
+idea works well though.
+
+/help now displayes all possible functions, instead of just
+built in ones. Shows all kSirc commands as well.
diff --git a/ksirc/servercontroller.cpp b/ksirc/servercontroller.cpp
new file mode 100644
index 00000000..0b03d1ab
--- /dev/null
+++ b/ksirc/servercontroller.cpp
@@ -0,0 +1,978 @@
+ /**********************************************************************
+
+ Server Controller
+
+ $$Id$$
+
+ Main Server Controller. Displays server connection window, and makes
+ new server connection on demand.
+
+ Signals: NONE
+
+ Slots:
+
+ new_connection(): Creates popup asking for new connection
+
+ new_ksircprocess(QString):
+ Args:
+ QString: new server name or IP to connect to.
+ Action:
+ Creates a new sirc process and window !default connected to the
+ server. Does nothing if a server connection already exists.
+
+ add_toplevel(QString parent, QString child):
+ Args:
+ parent: the server name that the new channel is being joined on
+ child: the new channel name
+ Action:
+ Adds "child" to the list of joined channles in the main
+ window. Always call this on new window creation!
+
+ delete_toplevel(QString parent, QString child):
+ Args:
+ parent: the server name of which channel is closing
+ child: the channle that is closing. IFF Emtpy, parent is
+ deleted.
+ Action:
+ Deletes the "child" window from the list of connections. If
+ the child is Empty the whole tree is removed since it is assumed
+ the parent has disconnected and is closing.
+
+ new_channel: Creates popup asking for new channel name
+
+ new_toplevel(QString str):
+ Args:
+ str: name of the new channel to be created
+ Action:
+ Sends a signal to the currently selected server in the tree
+ list and join the requested channel. Does nothing if nothing
+ is selected in the tree list.
+
+ recvChangeChanel(QString parent, QString old, QString new):
+ Args:
+ parent: parent server connection
+ old: the old name for the window
+ new: the new name for the window
+ Action:
+ Changes the old window name to the new window name in the tree
+ list box. Call for all name change!
+
+ *********************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "servercontroller.h"
+#include "KSOpenkSirc/open_ksirc.h"
+#include "NewWindowDialog.h"
+#include "ksopts.h"
+#include "control_message.h"
+#include "FilterRuleEditor.h"
+#include "../config.h"
+#include "version.h"
+#include "KSPrefs/ksprefs.h"
+#include "toplevel.h"
+#include "ksircserver.h"
+#include "nickColourMaker.h"
+
+#include <stdlib.h>
+
+#include "objFinder.h"
+#include <qlabel.h>
+#include <qregexp.h>
+
+#include <kmenubar.h>
+#include <kconfig.h>
+#include <kfontdialog.h>
+#include <kiconloader.h>
+#include <kwin.h>
+#include <kdebug.h>
+#include <kstdaccel.h>
+#include <kstandarddirs.h>
+#include <khelpmenu.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <knotifydialog.h>
+#include <netwm.h>
+#include <kpassivepopup.h>
+#include <kglobalaccel.h>
+#include <kstdguiitem.h>
+
+#include <qfile.h>
+
+#include "displayMgrSDI.h"
+#include "displayMgrMDI.h"
+
+#include "dockservercontroller.h"
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <klocale.h>
+#include <kapplication.h>
+#include <kpopupmenu.h>
+
+DisplayMgr *displayMgr;
+
+servercontroller *servercontroller::s_self = 0;
+
+servercontroller::servercontroller( QWidget*, const char* name )
+ : KMainWindow( 0, name )
+{
+ we_are_exiting = false;
+ m_notificationCount = 0;
+
+ m_ncm = new nickColourMaker();
+
+ MenuBar = menuBar();
+ KWin::setIcons( winId(), kapp->icon(), kapp->miniIcon() );
+
+ s_self = this;
+
+ switch (ksopts->displayMode)
+ {
+ case KSOptions::SDI:
+ displayMgr = new DisplayMgrSDI();
+ break;
+ case KSOptions::MDI:
+ displayMgr = new DisplayMgrMDI();
+ break;
+ }
+
+ sci = new scInside(this, QCString(name) + "_mainview");
+ setCentralWidget(sci);
+
+ sci->setFrameStyle(QFrame::Box | QFrame::Raised);
+ ConnectionTree = sci->ConnectionTree;
+
+ connect(ConnectionTree, SIGNAL(clicked( QListViewItem * )),
+ this, SLOT(WindowSelected(QListViewItem *)));
+
+ setFrameBorderWidth(5);
+
+ QPopupMenu *file = new QPopupMenu(this, QCString(name) + "_menu_file");
+ KStdAction::quit(this, SLOT(endksirc()), actionCollection())->plug(file);
+#ifndef NDEBUG
+ file->insertItem(i18n("Dump Object Tree"), this, SLOT(dump_obj()));
+ file->insertItem(i18n("Server Debug Window"), this, SLOT(server_debug()));
+#endif
+ MenuBar->insertItem(i18n("&File"), file);
+
+ connections = new QPopupMenu(this, QCString(name) + "_menu_connections");
+
+ server_id = connections->insertItem(i18n("&New Server..."), this, SLOT(new_connection()), Key_F2 );
+ join_id = connections->insertItem(i18n("&Join Channel..."), this, SLOT(new_channel()), Key_F3 );
+ connections->insertSeparator();
+ connections->insertItem(i18n("&Do Autoconnect..."), this, SLOT(start_autoconnect_check()));
+ connections->setItemEnabled(join_id, FALSE);
+ MenuBar->insertItem(i18n("&Connections"), connections);
+
+ options = new QPopupMenu(this, QCString(name) + "_menu_options");
+ options->setCheckable(TRUE);
+
+ options->insertItem(SmallIcon( "filter" ), i18n("&Filter Rule Editor..."),
+ this, SLOT(filter_rule_editor()));
+ options->insertSeparator();
+ KStdAction::configureNotifications(this, SLOT(notification_prefs()), actionCollection())->plug(options);
+
+ KStdAction::preferences(this, SLOT(general_prefs()), actionCollection())->plug(options);
+
+ MenuBar->insertItem(i18n("&Settings"), options);
+
+ KHelpMenu *help = new KHelpMenu( this, kapp->aboutData() );
+ MenuBar->insertItem( KStdGuiItem::help().text(), help->menu() );
+
+ m_kga = new KGlobalAccel(this, "globalAccess");
+ m_kga->insert("New Server", i18n("New Server"),
+ i18n("This action allows you to open a new server more easily "
+ "when in docked mode, since you don't need to click on the "
+ "dock icon."),
+ ALT+CTRL+Key_C, KKey::QtWIN+CTRL+Key_C, this,
+ SLOT(new_connection()));
+
+ open_toplevels = 0;
+
+ pic_server = UserIcon("server");
+ pic_gf = UserIcon("ksirc_a");
+ pic_run = UserIcon("mini-run");
+ pic_ppl = UserIcon("channels");
+ pic_icon = UserIcon("ksirc_b");
+
+ setCaption( i18n("Server Control") );
+ KWin::setIcons(winId(), pic_icon, pic_server);
+
+ resize( 450,200 );
+
+ dockWidget = new dockServerController(this, 0x0, "servercontroller_dock");
+ KWin::setSystemTrayWindowFor( dockWidget->winId(), winId() );
+
+ m_kga->readSettings();
+ m_kga->updateConnections();
+
+ checkDocking();
+}
+
+
+servercontroller::~servercontroller()
+{
+ kdDebug(5008) << "~servercontroller in" << endl;
+ s_self = 0;
+ delete m_ncm;
+ kdDebug(5008) << "~servercontroller out" << endl;
+}
+
+void servercontroller::checkDocking()
+{
+ if(ksopts->runDocked == true){
+ dockWidget->show();
+ hide();
+ m_kga->setEnabled(true);
+ }
+ else {
+ dockWidget->hide();
+ show();
+ m_kga->setEnabled(false);
+ }
+
+}
+
+void servercontroller::new_connection()
+{
+ open_ksirc *w = new open_ksirc(); // Create new ksirc popup
+ connect(w, SIGNAL(open_ksircprocess(KSircServer &)), // connected ok to process
+ this, SLOT(new_ksircprocess(KSircServer &))); // start
+ w->exec(); // show the sucker!
+ delete w;
+}
+
+void servercontroller::new_ksircprocess(KSircServer &kss)
+{
+ QString server_id;
+ int id = 1;
+ if(kss.server().isEmpty()) // nothing entered, nothing done
+ return;
+ server_id = kss.server();
+ while(proc_list[server_id]){ // if it already exists, quit
+ server_id = QString("%1 %2").arg(kss.server()).arg(id++);
+ }
+
+ // Insert new base
+ QListViewItem *rootItem = new QListViewItem( ConnectionTree, server_id );
+ rootItem->setPixmap( 0, pic_server );
+ rootItem->setOpen( true );
+
+ // We do no_channel here since proc emits the signal in the
+ // constructor, and we can't connect to before then, so we have to
+ // do the dirty work here.
+ ProcMessage(server_id, ProcCommand::addTopLevel, QString("no_channel"));
+
+ KSircProcess *proc = new KSircProcess(server_id, kss, 0, (QString(name()) + "_" + server_id + "_ksp").ascii() ); // Create proc
+ //this->insertChild(proc); // Add it to out inheritance tree so we can retreive child widgets from it.
+ objFinder::insert(proc);
+ proc_list.insert(server_id, proc); // Add proc to hash
+ connect(proc, SIGNAL(ProcMessage(QString, int, QString)),
+ this, SLOT(ProcMessage(QString, int, QString)));
+ connect(this, SIGNAL(ServMessage(QString, int, QString)),
+ proc, SLOT(ServMessage(QString, int, QString)));
+
+ if(!ConnectionTree->currentItem()){ // If nothing's highlighted
+ ConnectionTree->setCurrentItem(rootItem); // highlight it.
+ }
+
+ connections->setItemEnabled(join_id, TRUE);
+
+ dockWidget->serverOpen(server_id);
+}
+
+void servercontroller::new_channel()
+{
+ QString server;
+ QListViewItem *citem = ConnectionTree->currentItem(); // get item
+ if(citem){ // if it exist, ie something is highlighted, continue
+ if(proc_list[citem->text(0)]){ // If it's a match with a server, ok
+ server = citem->text(0);
+ }
+ // Otherwise, check the parent to see it's perhaps a server.
+ else if ( citem->parent() ) {
+ if(proc_list[citem->parent()->text(0)]){
+ server = citem->parent()->text(0);
+ }
+ }
+ }
+
+ if(server.isEmpty())
+ return;
+
+ KSircChannel ci(server, QString::null);
+ NewWindowDialog w(ci);
+ connect(&w, SIGNAL(openTopLevel(const KSircChannel &)),
+ this, SLOT(new_toplevel(const KSircChannel &)));
+ w.exec();
+}
+
+void servercontroller::new_toplevel(const KSircChannel &channelInfo)
+{
+ new_toplevel(channelInfo, false);
+}
+
+void servercontroller::new_toplevel(const KSircChannel &channelInfo, bool safe)
+{
+ if(proc_list[channelInfo.server()]){ // If it's a match with a server, ok
+ proc_list[channelInfo.server()]->new_toplevel(channelInfo, safe);
+ }
+}
+
+void servercontroller::ToggleAutoCreate()
+{
+ ksopts->autoCreateWin = !ksopts->autoCreateWin;
+}
+
+void servercontroller::general_prefs()
+{
+ KSPrefs *kp = new KSPrefs();
+ connect(kp, SIGNAL(update(int)),
+ this, SLOT(configChange()));
+ kp->resize(550, 450);
+ kp->show();
+}
+
+void servercontroller::notification_prefs()
+{
+ KNotifyDialog::configure(this, "Notification Configuration Dialog");
+}
+
+void servercontroller::dump_obj()
+{
+
+ objFinder::dumpTree();
+
+}
+void servercontroller::server_debug()
+{
+ QListViewItem *citem = ConnectionTree->currentItem(); // get item
+ if(citem){ // if it exist, ie something is highlighted, continue
+ QString server;
+ if(proc_list[citem->text(0)]){ // If it's a match with a server, ok
+ server = citem->text(0);
+ }
+ else if ( citem->parent() ) {
+ if(proc_list[citem->parent()->text(0)]){
+ server = citem->parent()->text(0);
+ }
+ }
+
+ if( !server.isNull() ){
+ bool sh = proc_list[server]->getIOController()->isDebugTraffic();
+ proc_list[server]->getIOController()->showDebugTraffic(!sh);
+ }
+ }
+
+
+}
+
+void servercontroller::filter_rule_editor()
+{
+ FilterRuleEditor *fe = new FilterRuleEditor();
+ connect(fe, SIGNAL(destroyed()),
+ this, SLOT(slot_filters_update()));
+ fe->show();
+}
+
+void servercontroller::font_update(const QFont &font)
+{
+ ksopts->defaultFont = font;
+/* configChange(); */
+
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("GlobalOptions");
+ kConfig->sync();
+ QApplication::setFont( font, true, "KSirc::TextView" );
+}
+
+void servercontroller::configChange()
+{
+ QDictIterator<KSircProcess> it( proc_list );
+ while(it.current()){
+ it.current()->filters_update();
+ it.current()->getWindowList()["!all"]->control_message(REREAD_CONFIG, "");
+ ++it;
+ }
+ m_kga->readSettings();
+ m_kga->updateConnections();
+}
+
+void servercontroller::ProcMessage(QString server, int command, QString args)
+{
+ QListViewItem *serverItem = 0L;
+ QListViewItem *item = ConnectionTree->firstChild();
+ while ( item ) {
+ if ( !item->parent() && item->text(0) == server ) {
+ serverItem = item;
+ break;
+ }
+ item = item->nextSibling();
+ }
+
+ if ( !serverItem ) {
+ kdDebug(5008) << "* ProcMessage for non-existant server?! - " << server<< endl;
+ return;
+ }
+
+
+ switch(command){
+
+
+ // Nick offline and online both remove the nick first.
+ // We remove the nick in case of an online so that we don't get
+ // duplicates.
+ // Args == nick comming on/offline.
+ case ProcCommand::nickOffline:
+ {
+ QListViewItem *online_item = findChild(serverItem, i18n("Online"));
+ if(online_item){
+ item = findChild(online_item, args);
+ delete item;
+ if(online_item->childCount() == 0)
+ delete online_item;
+ if(ksopts->runDocked && ksopts->dockPopups)
+ KPassivePopup::message(i18n("%1 just went offline on %2").arg(args).arg(server), dockWidget);
+ }
+ dockWidget->nickOffline(server, args);
+ break;
+ }
+ case ProcCommand::nickOnline:
+ {
+ QListViewItem *online_item = findChild(serverItem, i18n("Online"));
+ if(!online_item){
+ online_item = new QListViewItem(serverItem, i18n("Online"));
+ online_item->setPixmap( 0, pic_gf );
+ online_item->setOpen( true );
+ }
+ else {
+ item = findChild(online_item, args);
+ if( item ){
+ delete item;
+ }
+ }
+ item = new QListViewItem(online_item, args);
+ item->setPixmap( 0, pic_run );
+ if(ksopts->runDocked && ksopts->dockPopups)
+ KPassivePopup::message(i18n("%1 just came online on %2").arg(args).arg(server), dockWidget);
+ dockWidget->nickOnline(server, args);
+ break;
+ }
+ /*
+ // Add new channel, first add the parent to the path
+ path.push(&server);
+ path.push(&online);
+ path.push(&args);
+ // Remove old one if it's there
+ ConnectionTree->removeItem(&path); // Remove the item
+ path.pop();
+ // add a new child item with parent as its parent
+ ConnectionTree->addChildItem(args, pic_run, &path);
+ if (kSircConfig->BeepNotify) {
+ KNotifyClient::beep();
+ }
+ break;
+ */
+
+ /**
+ * Args:
+ * parent: the server name that the new channel is being joined on
+ * child: the new channel name
+ * Action:
+ * Adds "child" to the list of joined channles in the main
+ * window. Always call this on new window creation!
+ */
+ case ProcCommand::addTopLevel:
+ // Add new channel
+ if(args[0] == '!')
+ args.remove(0, 1); // If the first char is !, it's control, remove it
+ // add a new child item with parent as it's parent
+ item = new QListViewItem( serverItem, args );
+ item->setPixmap( 0, pic_ppl );
+
+ open_toplevels++;
+ break;
+ /**
+ * Args:
+ * parent: the server name of which channel is closing
+ * child: the channle that is closing. IFF Emtpy, parent is
+ * deleted.
+ * Action:
+ * Deletes the "child" window from the list of connections. If
+ * the child is Empty the whole tree is removed since it is assumed
+ * the parent has disconnected and is closing.
+ */
+ case ProcCommand::deleteTopLevel:
+ // If the child is emtpy, delete the whole tree, otherwise just the child
+ if(args[0] == '!')
+ args.remove(0, 1); // If the first char is !, it's control, remove it
+
+ item = findChild( serverItem, args );
+ delete item;
+ if ( serverItem->childCount() == 0 )
+ delete serverItem;
+
+ open_toplevels--;
+ break;
+
+ /**
+ * Args:
+ * parent: parent server connection
+ * old: the old name for the window
+ * new: the new name for the window
+ * Action:
+ * Changes the old window name to the new window name in the tree
+ * list box. Call for all name change!
+ */
+ case ProcCommand::changeChannel:
+ {
+ char *new_s, *old_s;
+ new_s = new char[args.length()+1];
+ old_s = new char[args.length()+1];
+ sscanf(args.ascii(), "%s %s", old_s, new_s);
+ // If the channel has a !, it's a control channel, remove the !
+ if(old_s[0] == '!')
+ // Even though, we want strlen() -1 characters, strlen doesn't
+ // include the \0, so we need to copy one more. -1 + 1 = 0.
+ memmove(old_s, old_s+1, strlen(old_s));
+ if(new_s[0] == '!')
+ memmove(new_s, new_s+1, strlen(new_s)); // See above for strlen()
+
+ item = findChild( serverItem, old_s );
+ delete item;
+ item = new QListViewItem( serverItem, new_s );
+ item->setPixmap( 0, pic_ppl );
+
+ delete[] new_s;
+ delete[] old_s;
+ }
+ break;
+ case ProcCommand::procClose:
+ dockWidget->serverClose(server);
+ delete serverItem;
+ proc_list.remove(server); // Remove process entry while we are at it
+ if(proc_list.count() == 0){
+ ConnectionTree->clear();
+ connections->setItemEnabled(join_id, FALSE);
+ }
+ break;
+ case ProcCommand::turnOffAutoCreate:
+ if (ksopts->autoCreateWin) {
+ ToggleAutoCreate();
+ }
+ break;
+ case ProcCommand::turnOnAutoCreate:
+ if (!ksopts->autoCreateWin) {
+ ToggleAutoCreate();
+ }
+ break;
+ default:
+ kdDebug(5008) << "Unkown command: [" << command << "] from "
+ << server
+ << " " << args << endl;
+ }
+}
+
+void servercontroller::slot_filters_update()
+{
+ emit ServMessage(QString(), ServCommand::updateFilters, QString());
+}
+
+void servercontroller::saveGlobalProperties(KConfig *ksc)
+{
+ // ksc hos the K Session config
+ // ksp == current KSircProcess
+ // ksm == current KSircMessageReceiver
+
+ // Ignore all !<name> windows
+
+ QString group = ksc->group();
+
+ ksc->setGroup( "KSircSession" );
+ SessionConfigMap::ConstIterator it = m_sessionConfig.begin();
+ for (; it != m_sessionConfig.end(); ++it ) {
+
+ ChannelSessionInfoList infoList = *it;
+
+ QStringList channels;
+ QString port = "6667";
+ QStringList desktops;
+
+ for ( ChannelSessionInfoList::ConstIterator sessionInfoIt = infoList.begin();
+ sessionInfoIt != infoList.end(); ++sessionInfoIt ) {
+ channels << ( *sessionInfoIt ).name;
+ port = ( *sessionInfoIt ).port;
+ desktops << QString::number( ( *sessionInfoIt ).desktop );
+ }
+
+ KConfigGroup( ksc, "KSircSession" ).writeEntry( it.key(), channels );
+ KConfigGroup( ksc, "KSircSessionPort" ).writeEntry( it.key(), port );
+ KConfigGroup( ksc, "KSircSessionDesktopNumbers" ).writeEntry( it.key(), desktops );
+ }
+
+ ksc->setGroup("ServerController");
+// ksc->writeEntry("Docked", !isVisible());
+ ksc->writeEntry("Size", geometry());
+ ksc->setGroup(group);
+}
+
+void servercontroller::readGlobalProperties(KConfig *ksc)
+{
+ QString group = ksc->group();
+
+ // ksc == K Session Config
+
+ // KMainWindow silently disables our menubar, when we quit in a docked
+ // state, so we have to force showing it here.
+ menuBar()->show();
+
+ // commented in for testing...
+ ksc->setGroup( "KSircSession" );
+ QMap<QString,QString> keyMap = ksc->entryMap( ksc->group() );
+ QMap<QString,QString>::Iterator it = keyMap.begin();
+
+ while(it != keyMap.end()) {
+ QString server = it.key();
+ QString port = KConfigGroup( ksc, "KSircSessionPort" ).readEntry( server );
+ // debug("%s", it.key().latin1());
+ KSircServer kss(server, port);
+ new_ksircprocess( kss ); // sets up proc_list
+ QStringList channels = ksc->readListEntry( server );
+
+ QStringList desktops = KConfigGroup( ksc, "KSircSessionDesktopNumbers" ).readListEntry( server );
+
+ for(uint i = 0; i < channels.count(); i++){
+
+ QString channel = channels[ i ];
+
+ proc_list[ server ]->new_toplevel( KSircChannel(server, channel), true );
+
+ KSircTopLevel *topLevel = dynamic_cast<KSircTopLevel *>( proc_list[ server ]->getWindowList()[ channel ] );
+ if ( !topLevel || !topLevel->isTopLevel() )
+ continue;
+
+ QStringList::ConstIterator desktopNumberIt = desktops.at( i );
+ if ( desktopNumberIt == desktops.end() )
+ continue;
+
+ int desktop = ( *desktopNumberIt ).toInt();
+ if ( desktop == -1 )
+ continue;
+#ifdef Q_WS_X11
+ NETWinInfo winInfo( qt_xdisplay(), topLevel->winId(), qt_xrootwin(), NET::WMDesktop );
+ winInfo.setDesktop( desktop );
+#endif
+ }
+ ++it;
+ }
+
+ QRect geom;
+
+// ksc->setGroup("ServerController");
+// bool docked = ksc->readBoolEntry("Docked", FALSE);
+// if ( !docked )
+ // show();
+ if(ksopts->runDocked == false)
+ show();
+
+ geom = ksc->readRectEntry("Size");
+ if(! geom.isEmpty())
+ setGeometry(geom);
+
+ ksc->setGroup(group);
+}
+
+void servercontroller::saveSessionConfig()
+{
+ QDictIterator<KSircProcess> ksp(proc_list);
+ for (; ksp.current(); ++ksp ) {
+ ChannelSessionInfoList channels;
+
+ QDictIterator<KSircMessageReceiver> ksm(ksp.current()->getWindowList());
+ for (; ksm.current(); ++ksm )
+ if(ksm.currentKey()[0] != '!') { // Ignore !ksm's (system created)
+ ChannelSessionInfo sessionInfo;
+
+ sessionInfo.name = ksm.currentKey();
+ sessionInfo.port = ksp.current()->serverPort();
+ KSircTopLevel *topLev = dynamic_cast<KSircTopLevel *>( ksm.current() );
+ if ( topLev && topLev->isTopLevel() ) {
+#ifdef Q_WS_X11
+ NETWinInfo winInfo( qt_xdisplay(), topLev->winId(), qt_xrootwin(), NET::WMDesktop );
+ sessionInfo.desktop = winInfo.desktop();
+#endif
+ }
+
+ channels << sessionInfo;
+ }
+
+ if ( !channels.isEmpty() )
+ m_sessionConfig[ ksp.currentKey() ] = channels;
+ }
+}
+
+void servercontroller::showEvent( QShowEvent *e )
+{
+ QWidget::showEvent( e );
+ if ( !e->spontaneous() )
+ saveDockingStatus();
+}
+
+void servercontroller::hideEvent( QHideEvent * )
+{
+ /*
+ QWidget::hideEvent( e );
+ if ( !e->spontaneous() )
+ saveDockingStatus();
+ if(QWidget::isMinimized()){
+ hide();
+ KWin::setState(winId(), NET::SkipTaskbar);
+ }
+ */
+}
+
+void servercontroller::saveDockingStatus()
+{
+ if ( we_are_exiting ) // we are hidden by closeEvent
+ return;
+
+// KConfig *kConfig = kapp->config();
+// KConfigGroupSaver s( kConfig, "ServerController" );
+// kConfig->writeEntry("Docked", !isVisible());
+// kConfig->sync();
+}
+
+void servercontroller::endksirc(){
+ kapp->config()->sync();
+ exit(0);
+}
+
+void servercontroller::closeEvent( QCloseEvent *e )
+{
+ we_are_exiting = true;
+ saveSessionConfig();
+ KMainWindow::closeEvent( e );
+}
+
+void servercontroller::WindowSelected(QListViewItem *item)
+{
+ if ( !item )
+ return;
+
+ QListViewItem *parent_server = item->parent();
+ if(!parent_server)
+ return;
+
+ QString txt = QString(parent_server->text(0)) + "_" + item->text(0) + "_toplevel";
+ QWidget *obj = dynamic_cast<QWidget *>( objFinder::find(txt.utf8(), "KSircTopLevel"));
+ if(obj == 0x0){
+ txt =QString(parent_server->text(0)) + "_!" + item->text(0) + "_toplevel";
+ obj = dynamic_cast<QWidget *>( objFinder::find(txt.utf8(), "KSircTopLevel"));
+ }
+
+ if(obj != 0x0){
+ displayMgr->raise(obj);
+ }
+ else {
+ kdWarning() << "Did not find widget ptr to raise it" << endl;
+ }
+}
+
+
+QListViewItem * servercontroller::findChild( QListViewItem *parent,
+ const QString& text )
+{
+ if ( !parent || parent->childCount() == 0 ) {
+ return 0L;
+ }
+
+ QListViewItem *item = parent->firstChild();
+ while ( item ) {
+ if ( item->text(0) == text ) {
+ return item;
+ }
+ item = item->nextSibling();
+ }
+
+ return 0L;
+}
+
+void servercontroller::increaseNotificationCount(const QString& reason, const QString& text)
+{
+ dockWidget->startBlink(reason, text);
+ m_notificationCount++;
+}
+
+void servercontroller::decreaseNotificationCount(QString reason)
+{
+ m_notificationCount--;
+ if ( m_notificationCount == 0 )
+ {
+ dockWidget->stopBlink(reason, true);
+ }
+ else {
+ dockWidget->stopBlink(reason, false);
+ }
+}
+
+void servercontroller::resetNotification()
+{
+ m_notificationCount = 0;
+ dockWidget->stopBlink(QString::null, true);
+ QDictIterator<KSircProcess> it( proc_list );
+ while(it.current()){
+ it.current()->filters_update();
+ it.current()->getWindowList()["!all"]->control_message(RESET_NOTIF, "");
+ ++it;
+ }
+}
+
+void servercontroller::do_autoconnect()
+{
+ static int stime = 0;
+ static int ctime = 0;
+ int loop;
+
+ kdDebug(5008) << "Doing AUTOCONNECT" << endl;
+
+ KConfig *conf = kapp->config();
+ conf->setGroup("AutoConnect");
+ QStringList servers = conf->readListEntry("Servers");
+ servers.sort();
+ QStringList::ConstIterator ser = servers.begin();
+
+ loop = 0;
+
+ for( ; ser != servers.end(); ser++){
+ if(loop++ == stime){
+ stime++;
+ QString server = *ser;
+ QString port = "6667";
+ bool usessl = false;
+ QString pass = QString::null;
+
+ QRegExp rx("(.+) \\(SSL\\)(.*)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1) + rx.cap(3);
+ usessl = true;
+ }
+ rx.setPattern("(.+) \\(pass: (\\S+)\\)(.*)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1) + rx.cap(3);
+ pass = rx.cap(2);
+ }
+ rx.setPattern("([^: ]+):(\\d+)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1);
+ port = rx.cap(2);
+ }
+ kdDebug(5008) << server << ": Done " << port << " " << usessl << " " << pass << endl;
+ KSircServer kss(server, port, "", pass, usessl);
+ new_ksircprocess(kss);
+ return;
+ }
+ }
+
+ loop = 0;
+
+ ser = servers.begin();
+ for( ; ser != servers.end(); ser++){
+ QStringList channels = conf->readListEntry(*ser);
+ if(channels.empty() == FALSE){
+ channels.sort();
+ QStringList::ConstIterator chan = channels.begin();
+ for(; chan != channels.end(); chan++){
+ if(loop++ == ctime){
+ ctime++;
+ QString channel = *chan;
+ QString key = QString::null;
+ QRegExp crx("(.+) \\(key: (\\S+)\\)");
+ if(crx.search(channel) >= 0){
+ channel = crx.cap(1);
+ key = crx.cap(2);
+ }
+ QString server = *ser;
+
+ QRegExp rx("^([^ :]+)");
+ if(rx.search(server) >= 0){
+ server = rx.cap(1);
+ }
+ kdDebug(5008) << server << ": Channed: " << channel << " key: " << key << endl;
+ new_toplevel(KSircChannel(server, channel, key), true);
+ return;
+ }
+ }
+ }
+ }
+
+ ctime = 0;
+ stime = 0;
+ at->stop();
+ delete at;
+
+}
+
+void servercontroller::start_autoconnect() {
+ at = new QTimer(this);
+ connect(at, SIGNAL(timeout()), this, SLOT(do_autoconnect()));
+ at->start(250, FALSE);
+}
+
+void servercontroller::start_autoconnect_check() {
+
+ KConfig *conf = kapp->config();
+ conf->setGroup("AutoConnect");
+ QStringList servers = conf->readListEntry("Servers");
+
+ if(servers.count() == 0){
+ KSPrefs *kp = new KSPrefs();
+ connect(kp, SIGNAL(update(int)),
+ this, SLOT(configChange()));
+ kp->resize(550, 450);
+ kp->showPage(7); /* Show auto connect page */
+ kp->show();
+ }
+ else {
+ at = new QTimer(this);
+ connect(at, SIGNAL(timeout()), this, SLOT(do_autoconnect()));
+ at->start(250, FALSE);
+ }
+
+}
+
+
+scInside::scInside ( QWidget * parent, const char * name, WFlags
+ f )
+ : QFrame(parent, name, f)
+{
+ ASConn = new QLabel(i18n("Active server connections:"), this, "servercontroller_label");
+ QFont asfont = ASConn->font();
+ asfont.setBold(TRUE);
+ ASConn->setFont(asfont);
+
+ ConnectionTree = new KListView(this, "connectiontree");
+ ConnectionTree->addColumn(QString::null);
+ ConnectionTree->setRootIsDecorated( true );
+ ConnectionTree->setSorting( 0 );
+ ConnectionTree->header()->hide();
+}
+
+scInside::~scInside()
+{
+ delete ASConn;
+ delete ConnectionTree;
+}
+
+void scInside::resizeEvent ( QResizeEvent *e )
+{
+ QFrame::resizeEvent(e);
+ ASConn->setGeometry(10,10, width() - 20,
+ ASConn->fontMetrics().height()+5);
+ ConnectionTree->setGeometry(10, 10 + ASConn->height(),
+ width() - 20, height() - 20 - ASConn->height());
+}
+
+#include "servercontroller.moc"
diff --git a/ksirc/servercontroller.dlg b/ksirc/servercontroller.dlg
new file mode 100644
index 00000000..7e515bdc
--- /dev/null
+++ b/ksirc/servercontroller.dlg
@@ -0,0 +1,33 @@
+DlgEdit:v2.0:Dialog:
+Dialog {
+ ClassHeader {servercontroller.h}
+ ClassSource {servercontroller.cpp}
+ ClassName {servercontroller}
+ DataHeader {servercontrollerData.h}
+ DataSource {servercontrollerData.cpp}
+ DataName {servercontrollerData}
+ WindowBaseClass {Custom}
+ CustomBase {KSircControl}
+ CustomBaseHeader {control.h}
+ WindowCaption {Server Control}
+ WindowFlags {233472}
+}
+WidgetLayout {
+InitialPos {-1 -1}
+Size {410 200}
+MinSize {0 0}
+MaxSize {32767 32767}
+Grid {10}
+
+User {
+ UserClassHeader {kmenubar.h}
+ UserClassName {KMenuBar}
+ Rect {0 0 410 30}
+ Name {User_2}
+ Variable {MenuBar}
+ LayoutStatus {NoLayout}
+ MinimumSize {10 10}
+ MaximumSize {32767 32767}
+}
+Layout {None}
+}
diff --git a/ksirc/servercontroller.h b/ksirc/servercontroller.h
new file mode 100644
index 00000000..54d0292c
--- /dev/null
+++ b/ksirc/servercontroller.h
@@ -0,0 +1,267 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: servercontroller.h
+ Last generated: Sat Nov 29 08:50:19 1997
+
+ Now Under CVS control.
+
+ $$Id$$
+
+ *********************************************************************/
+
+#ifndef servercontroller_included
+#define servercontroller_included
+
+class servercontroller;
+class dockServerController;
+class ServMessage;
+class ProcCommand;
+
+#include <qdict.h>
+#include <qpixmap.h>
+#include <qheader.h>
+#include <qtimer.h>
+
+#include <klistview.h>
+#include <kmainwindow.h>
+
+#include "ksircprocess.h"
+#include "ksircchannel.h"
+
+//#include "puke/controller.h"
+class QLabel;
+class KMenuBar;
+class KSircServer;
+class KGlobalAccel;
+class nickColourMaker;
+class dockServerController;
+
+class ProcCommand // ServerController message
+{
+ public:
+ static enum {
+ addTopLevel,
+ deleteTopLevel,
+ procClose,
+ newChannel,
+ changeChannel,
+ nickOnline,
+ nickOffline,
+ turnOffAutoCreate,
+ turnOnAutoCreate
+ } command;
+};
+
+
+class ServCommand // ServerController message
+{
+ public:
+ static enum {
+ updateFilters,
+ updatePrefs
+ } command;
+};
+
+class scInside : QFrame
+{
+ Q_OBJECT
+ friend class servercontroller;
+ public:
+ scInside ( QWidget *parent = 0L, const char * name = 0, WFlags f=0 );
+ ~scInside();
+
+ protected:
+ virtual void resizeEvent ( QResizeEvent * );
+
+ private:
+ KListView *ConnectionTree;
+ QLabel *ASConn;
+
+};
+
+class servercontroller : public KMainWindow
+{
+ Q_OBJECT
+ friend class dockServerController;
+public:
+
+ servercontroller ( QWidget* parent = 0L, const char* name = NULL );
+ virtual ~servercontroller();
+
+ const QDict<KSircProcess> &processes() const { return proc_list; }
+
+ static servercontroller *self() { return s_self; }
+
+ /**
+ * Someone is talking to the user (blue icon), notify him (using the docked icon).
+ */
+ void increaseNotificationCount(const QString& reason = QString::null, const QString& text = QString::null);
+
+ /**
+ * The channel in which the user was talked to, has been read.
+ * -> decrease count of blue icons.
+ */
+ void decreaseNotificationCount(QString reason = QString::null);
+
+ /**
+ * This resets all notificaiton counts and allows new ones
+ * this is used if we don't want to give the window
+ * focus to reset focus
+ */
+ void resetNotification();
+
+ void checkDocking();
+
+ KGlobalAccel *getGlobalAccel(){ return m_kga; }
+
+signals:
+ /**
+ * Filter rules have changed, need to re-read and update.
+ */
+ virtual void filters_update();
+
+ void ServMessage(QString server, int command, QString args);
+
+public slots:
+ // All slots are described in servercontroll.cpp file
+ /**
+ * Does auto-joins on start up
+ */
+ virtual void do_autoconnect();
+ /**
+ * Creates popup asking for new connection
+ */
+ virtual void new_connection();
+ /**
+ * Args:
+ * QString: new server name or IP to connect to.
+ * Action:
+ * Creates a new sirc process and window !default connected to the
+ * server. Does nothing if a server connection already exists.
+ */
+ // virtual void new_ksircprocess(QString);
+ virtual void new_ksircprocess(KSircServer &);
+ /**
+ * Creates popup asking for new channel name
+ */
+ virtual void new_channel();
+ /**
+ * Args:
+ * str: name of the new channel to be created
+ * server: name of the server channel is created on
+ * Action:
+ * opens a new toplevel on the requested channel and server
+ */
+ virtual void new_toplevel(const KSircChannel &channel);
+ virtual void new_toplevel(const KSircChannel &channel, bool safe);
+ /**
+ * Action:
+ * Notify all ksircprocess' to update filters
+ */
+ virtual void slot_filters_update();
+ virtual void ToggleAutoCreate();
+
+ /**
+ * Action: Popup a general preferences window which allows various
+ * settings, etc.
+ */
+ virtual void general_prefs();
+ /**
+ * Opens the dialog that lets the user configure system notifications
+ */
+ virtual void notification_prefs();
+ virtual void font_update(const QFont&);
+ virtual void filter_rule_editor();
+ virtual void configChange();
+
+ virtual void ProcMessage(QString server, int command, QString args);
+ /**
+ * On quit we sync the config to disk and exit
+ */
+ virtual void endksirc();
+ /**
+ * Start auto-connect
+ */
+ void start_autoconnect();
+ /**
+ * Start auto-connect with check
+ */
+ void start_autoconnect_check();
+
+
+ QListViewItem * findChild( QListViewItem *parent, const QString& text );
+
+protected slots:
+ void WindowSelected(QListViewItem *);
+
+ void dump_obj();
+ void server_debug();
+
+protected:
+
+ virtual void showEvent( QShowEvent *e );
+ virtual void hideEvent( QHideEvent *e );
+ virtual void closeEvent( QCloseEvent * );
+ void saveDockingStatus();
+
+ void saveGlobalProperties(KConfig *);
+ void readGlobalProperties(KConfig *);
+
+private:
+ void saveSessionConfig();
+
+ // La raison d'etre. We don't run ConnectionTree ourselves, but
+ // we get it from our helper class scInside.
+ KListView *ConnectionTree;
+
+ scInside *sci;
+
+ // Menubar for the top.
+ KMenuBar *MenuBar;
+
+ // Hold a list of all KSircProcess's for access latter. Index by server
+ // name
+ QDict<KSircProcess> proc_list;
+ QPopupMenu *options, *connections;
+ int join_id, server_id;
+
+ KGlobalAccel *m_kga;
+
+ int open_toplevels;
+
+ QPixmap pic_icon;
+ QPixmap pic_server;
+ QPixmap pic_gf;
+ QPixmap pic_run;
+ QPixmap pic_ppl;
+
+// PukeController *PukeC;
+
+ // Holds dockable widget
+ dockServerController *dockWidget;
+ bool we_are_exiting;
+
+ // Docked icon notification
+ int m_notificationCount;
+ struct ChannelSessionInfo
+ {
+ ChannelSessionInfo()
+ : desktop( -1 ) {}
+ QString name;
+ QString port;
+ int desktop;
+ };
+ typedef QValueList<ChannelSessionInfo> ChannelSessionInfoList;
+
+ typedef QMap<QString, ChannelSessionInfoList> SessionConfigMap;
+ SessionConfigMap m_sessionConfig;
+
+ static servercontroller *s_self;
+
+ QTimer *at;
+
+ nickColourMaker *m_ncm;
+};
+#endif // servercontroller_included
diff --git a/ksirc/sirc.help.gz b/ksirc/sirc.help.gz
new file mode 100644
index 00000000..fb5bce09
--- /dev/null
+++ b/ksirc/sirc.help.gz
Binary files differ
diff --git a/ksirc/ssfeprompt.cpp b/ksirc/ssfeprompt.cpp
new file mode 100644
index 00000000..46e68909
--- /dev/null
+++ b/ksirc/ssfeprompt.cpp
@@ -0,0 +1,54 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ssfeprompt.cpp
+ Last generated: Thu Jan 15 20:49:56 1998
+
+ *********************************************************************/
+
+#include "ssfeprompt.h"
+#include <klocale.h>
+
+#undef Inherited
+#define Inherited ssfepromptdata
+
+ssfePrompt::ssfePrompt
+(
+ QString prompttext,
+ QWidget* parent,
+ const char* name
+)
+ :
+ Inherited( parent, name )
+{
+ setCaption( i18n("Prompt") );
+ prompt->setText(prompttext);
+ SLine->setFocus();
+}
+
+
+ssfePrompt::~ssfePrompt()
+{
+}
+
+void ssfePrompt::terminate()
+{
+ done(0);
+}
+
+QString ssfePrompt::text()
+{
+ QString text;
+ text = SLine->text();
+ return text;
+}
+
+void ssfePrompt::setPassword(bool pass)
+{
+ if(pass == TRUE)
+ SLine->setEchoMode(QLineEdit::Password);
+ else
+ SLine->setEchoMode(QLineEdit::Normal);
+}
+#include "ssfeprompt.moc"
diff --git a/ksirc/ssfeprompt.h b/ksirc/ssfeprompt.h
new file mode 100644
index 00000000..ff777912
--- /dev/null
+++ b/ksirc/ssfeprompt.h
@@ -0,0 +1,37 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ssfeprompt.h
+ Last generated: Thu Jan 15 20:49:55 1998
+
+ *********************************************************************/
+
+#ifndef ssfePrompt_included
+#define ssfePrompt_included
+
+#include "ssfepromptdata.h"
+
+class ssfePrompt : public ssfepromptdata
+{
+ Q_OBJECT
+
+public:
+
+ ssfePrompt
+ (
+ QString prompttext,
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~ssfePrompt();
+
+ QString text();
+ void setPassword(bool);
+
+protected slots:
+ virtual void terminate();
+
+};
+#endif // ssfePrompt_included
diff --git a/ksirc/ssfepromptdata.cpp b/ksirc/ssfepromptdata.cpp
new file mode 100644
index 00000000..ce1dc368
--- /dev/null
+++ b/ksirc/ssfepromptdata.cpp
@@ -0,0 +1,68 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ssfepromptdata.cpp
+ Last generated: Thu Jan 15 21:21:29 1998
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#include "ssfepromptdata.h"
+
+#undef Inherited
+#define Inherited QDialog
+
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+ssfepromptdata::ssfepromptdata
+(
+ QWidget* parent,
+ const char* name
+)
+ :
+ Inherited( parent, name, TRUE, 20480 )
+{
+ prompt = new QLabel( this, "Label_1" );
+ prompt->setGeometry( 10, 10, 220, 30 );
+ prompt->setMinimumSize( 10, 10 );
+ prompt->setMaximumSize( 32767, 32767 );
+ prompt->setText( "" );
+ prompt->setAlignment( 289 );
+ prompt->setMargin( -1 );
+
+ SLine = new QLineEdit( this, "LineEdit_1" );
+ SLine->setGeometry( 240, 10, 100, 30 );
+ SLine->setMinimumSize( 10, 10 );
+ SLine->setMaximumSize( 32767, 32767 );
+ connect( SLine, SIGNAL(returnPressed()), SLOT(terminate()) );
+ SLine->setText( "" );
+ SLine->setMaxLength( 32767 );
+ SLine->setEchoMode( QLineEdit::Normal );
+ SLine->setFrame( TRUE );
+
+ QPushButton* dlgedit_PushButton_1;
+ dlgedit_PushButton_1 = new KPushButton( KStdGuiItem::ok(), this, "PushButton_1" );
+ dlgedit_PushButton_1->setGeometry( 240, 50, 100, 30 );
+ dlgedit_PushButton_1->setMinimumSize( 10, 10 );
+ dlgedit_PushButton_1->setMaximumSize( 32767, 32767 );
+ connect( dlgedit_PushButton_1, SIGNAL(clicked()), SLOT(terminate()) );
+ dlgedit_PushButton_1->setAutoRepeat( FALSE );
+ dlgedit_PushButton_1->setAutoDefault( TRUE );
+
+ resize( 350,90 );
+ setMinimumSize( 350, 90 );
+ setMaximumSize( 350, 90 );
+}
+
+
+ssfepromptdata::~ssfepromptdata()
+{
+}
+void ssfepromptdata::terminate()
+{
+}
+#include "ssfepromptdata.moc"
diff --git a/ksirc/ssfepromptdata.h b/ksirc/ssfepromptdata.h
new file mode 100644
index 00000000..c4b6a4e6
--- /dev/null
+++ b/ksirc/ssfepromptdata.h
@@ -0,0 +1,47 @@
+/**********************************************************************
+
+ --- Qt Architect generated file ---
+
+ File: ssfepromptdata.h
+ Last generated: Thu Jan 15 21:21:29 1998
+
+ DO NOT EDIT!!! This file will be automatically
+ regenerated by qtarch. All changes will be lost.
+
+ *********************************************************************/
+
+#ifndef ssfepromptdata_included
+#define ssfepromptdata_included
+
+#include <qdialog.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+
+class ssfepromptdata : public QDialog
+{
+ Q_OBJECT
+
+public:
+
+ ssfepromptdata
+ (
+ QWidget* parent = NULL,
+ const char* name = NULL
+ );
+
+ virtual ~ssfepromptdata();
+
+public slots:
+
+
+protected slots:
+
+ virtual void terminate();
+
+protected:
+ QLabel* prompt;
+ QLineEdit* SLine;
+
+};
+
+#endif // ssfepromptdata_included
diff --git a/ksirc/stringparserstate.h b/ksirc/stringparserstate.h
new file mode 100644
index 00000000..cd9220a4
--- /dev/null
+++ b/ksirc/stringparserstate.h
@@ -0,0 +1,71 @@
+#ifndef __stringparserstate_h__
+#define __stringparserstate_h__
+
+// ### optimize me: it's stupid to do a linear search for each
+// atEnd() invocation. This should be done once in the ctor and
+// end() should be set appropriately
+template <typename CharType, typename _SizeType = size_t>
+class StringParserState
+{
+public:
+ typedef CharType ValueType;
+ typedef const CharType * ConstIterator;
+ typedef _SizeType SizeType;
+ typedef QValueList<ValueType> ValueList;
+
+ StringParserState( ConstIterator start, SizeType maxSteps,
+ const ValueList &optionalEndItems = ValueList() )
+ {
+ m_begin = start;
+ m_current = start;
+ m_end = start + maxSteps;
+ m_endItems = optionalEndItems;
+ }
+
+ void operator++() { ++m_current; }
+
+ bool atBegin() const
+ {
+ return m_current == m_begin;
+ }
+
+ bool atEnd() const
+ {
+ if ( m_current >= m_end )
+ return true;
+ return m_endItems.findIndex( currentValue() ) != -1;
+ }
+
+ ConstIterator current() const { return m_current; }
+ ValueType currentValue() const { return *current(); }
+
+ ConstIterator begin() const { return m_begin; }
+ ConstIterator end() const { return m_end; }
+
+ SizeType stepsLeft() const { return m_end - m_current; }
+
+ SizeType advanceTo( ValueType val )
+ {
+ ConstIterator start = m_current;
+ while ( !atEnd() && currentValue() != val )
+ ++m_current;
+ return m_current - start;
+ }
+
+ SizeType skip( ValueType valueToIgnore )
+ {
+ ConstIterator start = m_current;
+ while ( !atEnd() && currentValue() == valueToIgnore )
+ ++m_current;
+ return m_current - start;
+ }
+
+private:
+ ConstIterator m_begin;
+ ConstIterator m_current;
+ ConstIterator m_end;
+ ValueList m_endItems;
+};
+
+#endif
+
diff --git a/ksirc/test/nicklist.pl b/ksirc/test/nicklist.pl
new file mode 100644
index 00000000..948429eb
--- /dev/null
+++ b/ksirc/test/nicklist.pl
@@ -0,0 +1,77 @@
+sub rndchr {
+ my $string = "";
+ for(my $i = 0; $i < 8; $i++){
+ $string .= chr(int(rand(26)) + 97); # More or less the alpahbet
+ }
+ return $string;
+}
+
+srand(time());
+
+&timer(1, "&next_one", 1);
+
+$state = 0;
+$max_nicks = 100;
+$min_nicks = 5;
+$num_nicks = 0;
+%list_nicks = ();
+$repeat = 100000;
+$count = 0;
+
+@state = (\&join, \&part);
+
+$line = "~#test~*#* Users on #test:";
+for($i = 0; $i < $max_nicks; $i++){
+ my($mynick) = rndchr();
+ $list_nicks{$mynick} = 1;
+ $is_op = rand(100);
+ if($is_op > 50){
+ $mynick = "@" . $mynick;
+ }
+ $line .= " " . $mynick;
+ $num_nicks ++;
+}
+print "$line\n";
+
+sub next_one {
+ for(my($i) = 0; $i < 200; $i++){
+ $goto_state = int(rand($#state+1));
+ &{$state[$goto_state]};
+ }
+ if($count++ < $repeat){
+ &timer(1, "&next_one", 1);
+ }
+}
+
+sub join{
+ return if $num_nicks > $max_nicks;
+ my($mynick) = rndchr();
+ $list_nicks{$mynick} = 1;
+ print("~#test~*>* $mynick (blah\@blah) has joined channel #test\n");
+ $is_op = rand(100);
+ if($is_op > 75){
+ print "~#test~*+* Mode change \"+o $mynick\" on channel #test by ChanServ\n";
+ }
+ $is_voice = rand(100);
+ if($is_voice > 40){
+ print "~#test~*+* Mode change \"+v $mynick\" on channel #test by ChanServ\n";
+ }
+ $num_nicks ++;
+}
+
+sub part{
+ return if $num_nicks < $min_nicks;
+ AGAIN: {
+ my($times) = int(rand($num_nicks));
+ for($i = 0; $i<= $times; $i++){
+ ($mynick, $value) = each(%list_nicks);
+ }
+ return if $mynick eq '';
+ }
+ next AGAIN if $value != 1;
+ $list_nicks{$mynick} = 0;
+ print("~#test~*<* $mynick has left channel #test\n");
+
+ delete $list_nicks{$mynick};
+ $num_nicks --;
+}
diff --git a/ksirc/test/tester.pl b/ksirc/test/tester.pl
new file mode 100644
index 00000000..2815a3a1
--- /dev/null
+++ b/ksirc/test/tester.pl
@@ -0,0 +1,65 @@
+sub rndchr {
+ my $string = "";
+ for(my $i = 0; $i < 8; $i++){
+ $string .= chr(int(rand(26)) + 97); # More or less the alpahbet
+ }
+ return $string;
+}
+
+srand(time());
+
+&timer(1, "&next_one", 1);
+
+$state = 0;
+$max_win = 5;
+$win_open = 0;
+%wins = {};
+$repeat = 1000;
+$count = 0;
+
+
+sub next_one {
+ #
+ # State 0 is open window
+ #
+ if($state == 0){
+ $winnum = int(rand($max_win));
+ $winname = "#" . rndchr();
+ $wins{$winname} = 1;
+ print("~$winname~*>* You have joined channel $winname\n");
+ &docommand("/join $winname");
+ $state = 1;
+ $win_open ++;
+ }
+ #
+ # State 1 is print stuff to channel
+ #
+ elsif($state == 1){
+ while(($winname, $value) = each(%wins)){
+ &msg("$winname", rndchr());
+ &notice("$winname", rndchr());
+ }
+ $state = 2;
+ }
+ #
+ # State 2 is close window
+ #
+ elsif($state == 2){
+ if($win_open > $max_win){
+ $times = int(rand($win_open-1));
+ for($i = 0; $i<= $times; $i++){
+ ($winname, $value) = each(%wins);
+ }
+ print("~#test~ <boren> leaving $winname\n");
+ delete($wins{$winname});
+ print("~!all~*<* You have left channel $winname\n");
+ &docommand("/part $winname");
+ $win_open --;
+ }
+ $state = 0;
+ }
+
+ if($count++ < $repeat){
+ &timer(5, "&next_one", 1);
+ }
+} \ No newline at end of file
diff --git a/ksirc/topic.cpp b/ksirc/topic.cpp
new file mode 100644
index 00000000..9e0d8f3e
--- /dev/null
+++ b/ksirc/topic.cpp
@@ -0,0 +1,245 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+
+
+#include "topic.h"
+
+#include <qapplication.h>
+#include <qtimer.h>
+#include <qregexp.h>
+#include <qlayout.h>
+#include <qtextedit.h>
+#include <qtooltip.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <krun.h>
+#include <kpopupmenu.h>
+#include <kstringhandler.h>
+#include <kfiledialog.h>
+#include <kio/job.h>
+
+#include "ksopts.h"
+#include "ksparser.h"
+
+
+KSircTopic::KSircTopic( QWidget *parent, const char *name )
+ : KActiveLabel( parent, name )
+{
+ m_editor = 0;
+ m_doEdit = false;
+ m_height = 0;
+// setBackgroundColor( colorGroup().light() );
+ setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
+ setTextFormat( RichText );
+ setWordWrap(QTextEdit::NoWrap);
+ doResize();
+// setAlignment( alignment() | WordBreak );
+}
+
+void KSircTopic::setText( const QString &_text)
+{
+ m_text = _text; /* save a raw copy for us */
+ QString text = _text;
+
+ QString richText( "<font color=\"%1\">" );
+ richText = richText.arg( ksopts->textColor.name() );
+
+ text.replace('&', "&amp;");
+ text.replace('<', "&lt;");
+ text.replace('>', "&gt;");
+
+ text.replace('~', "~~");
+
+ // ### a bit of a hack: turn '&lt;nick&gt; message' into
+ // <span>&lt;nick&gt;<span> message' . span itself isn't supported but it
+ // enforces the creation of separate item objects and hence separate
+ // drawing of '<nick>' and 'message' , which is needed for BiDi users,
+ // according to UV Kochavi <uv1st@yahoo.com> , to prevent output like
+ // '<nick message<' , which is supposedly a bug in Qt's reordering. The
+ // same is done for [nick] and >nick< to catch queries.
+ QRegExp bidiRe( "^(&lt;\\S+&gt;)(.+)$" );
+ text.replace( bidiRe, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+ QRegExp bidiRe2( "^(\\[\\S+\\])(.+)$" );
+ text.replace( bidiRe2, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+ QRegExp bidiRe3( "^(&gt;\\S+&lt;)(.+)$" );
+ text.replace( bidiRe3, QString::fromLatin1( "<span>\\1</span>\\2" ) );
+
+ KSParser parser;
+ richText += parser.parse( text );
+ richText += "</font>";
+
+ richText = KStringHandler::tagURLs( richText );
+ KActiveLabel::setText(richText);
+
+ doResize();
+
+}
+
+void KSircTopic::contentsMouseDoubleClickEvent( QMouseEvent * )
+{
+ m_doEdit = true;
+}
+
+void KSircTopic::contentsMouseReleaseEvent( QMouseEvent *e )
+{
+ if ( m_doEdit ) {
+ m_doEdit = false;
+
+ if ( m_editor )
+ return;
+
+ doResize();
+
+ m_editor = new KSircTopicEditor( this );
+
+ connect( m_editor, SIGNAL( returnPressed() ),
+ this, SLOT( setNewTopic() ) );
+ connect( m_editor, SIGNAL( resized() ),
+ this, SLOT( slotEditResized() ) );
+ connect( m_editor, SIGNAL( destroyed() ),
+ this, SLOT( doResize() ) );
+
+ /*
+ * If you don't do this order
+ * the size really breaks and you get
+ * a huge widget
+ */
+ m_editor->setGeometry( geometry() );
+ m_editor->setFocus();
+ m_editor->show();
+
+ m_editor->setText( m_text );
+ QToolTip::remove(this);
+
+ }
+ KActiveLabel::contentsMouseReleaseEvent(e);
+}
+
+void KSircTopic::setNewTopic()
+{
+ QString topic = m_editor->text().stripWhiteSpace();
+
+ /*
+ * don't change the channel display
+ * test since if it is set we'll get it
+ * from the server. If we can't set the topic
+ * don't make it look like it was set
+ */
+ QTimer::singleShot( 0, m_editor, SLOT( close() ) );
+ disconnect( m_editor, SIGNAL( resized() ),
+ this, SLOT( slotEditResized() ) );
+ doResize();
+ emit topicChange( topic );
+}
+
+void KSircTopic::slotEditResized( )
+{
+ setFixedHeight( m_editor->height() );
+}
+
+
+void KSircTopic::doResize()
+{
+
+ int h;
+ QFontMetrics metrics( currentFont() );
+
+ h = metrics.lineSpacing()+8;
+ m_height = h;
+ setFixedHeight( h );
+
+
+ QToolTip::remove(this);
+ QStringList sep = QStringList::split(" ", m_text);
+ int len = 0;
+ QString brok;
+ QStringList::Iterator it = sep.begin();
+ for(; it != sep.end(); ++it) {
+ brok += *it + " ";
+ len += (*it).length();
+ if(len >= 50){
+ brok += "\n";
+ len = 0;
+ }
+ }
+
+ QToolTip::add(this, brok);
+
+}
+
+void KSircTopic::fontChange(QFont &f)
+{
+ KActiveLabel::fontChange(f);
+ doResize();
+}
+
+KSircTopicEditor::KSircTopicEditor( QWidget *parent, const char *name )
+ : QTextEdit( parent, name )
+{
+ setWFlags( WDestructiveClose );
+ setFocusPolicy( QWidget::ClickFocus );
+ connect( this, SIGNAL( textChanged () ), this, SLOT( slotMaybeResize() ) );
+}
+
+void KSircTopicEditor::keyPressEvent( QKeyEvent *ev )
+{
+ if ( ev->key() == Key_Escape )
+ {
+ ev->accept();
+ QTimer::singleShot( 0, this, SLOT( close() ) );
+ return;
+ }
+ else if ( ev->key() == Key_Return )
+ {
+ ev->accept();
+ emit returnPressed();
+ return;
+ }
+ QTextEdit::keyPressEvent( ev );
+}
+
+void KSircTopicEditor::focusOutEvent( QFocusEvent * )
+{
+ // we don't want to quit editing when someone brings up QLE's popup
+ // menu
+ if ( QFocusEvent::reason() == QFocusEvent::Popup )
+ {
+ QWidget *focusw = qApp->focusWidget();
+ if ( focusw && m_popup && focusw == m_popup )
+ return;
+ }
+
+ QTimer::singleShot( 0, this, SLOT( close() ) );
+}
+
+QPopupMenu *KSircTopicEditor::createPopupMenu( const QPoint &pos )
+{
+ QPopupMenu *popup = QTextEdit::createPopupMenu( pos );
+ m_popup = popup;
+ return popup;
+}
+
+void KSircTopicEditor::slotMaybeResize()
+{
+ if(text().contains("\n")){
+ QString s = text();
+ s.replace('\n', " ");
+ setText(s);
+ setCursorPosition(0, s.length());
+ }
+
+ QFontMetrics metrics( currentFont() );
+
+ int h = metrics.lineSpacing() * lines()+8;
+ setFixedHeight( h );
+ emit resized();
+}
+
+#include "topic.moc"
diff --git a/ksirc/topic.h b/ksirc/topic.h
new file mode 100644
index 00000000..73191128
--- /dev/null
+++ b/ksirc/topic.h
@@ -0,0 +1,70 @@
+/* This file is part of the KDE project
+ Copyright (C) 2001 Simon Hausmann <hausmann@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the Artistic License.
+*/
+#ifndef __topic_h__
+#define __topic_h__
+
+#include <kactivelabel.h>
+#include <qtextedit.h>
+#include <qlineedit.h>
+#include <qpopupmenu.h>
+#include <qguardedptr.h>
+
+class KSircTopicEditor;
+
+class KSircTopic : public KActiveLabel
+{
+ Q_OBJECT
+public:
+ KSircTopic( QWidget *parent, const char *name = 0 );
+
+public slots:
+ virtual void setText( const QString &);
+
+signals:
+ void topicChange( const QString &newTopic );
+
+protected:
+ virtual void contentsMouseDoubleClickEvent( QMouseEvent * );
+ virtual void contentsMouseReleaseEvent( QMouseEvent * );
+ virtual void fontChange(QFont &);
+
+private slots:
+ void setNewTopic();
+ void slotEditResized();
+ void doResize();
+
+private:
+
+
+ QGuardedPtr<KSircTopicEditor> m_editor;
+ bool m_doEdit;
+ int m_height;
+ QString m_text;
+};
+
+class KSircTopicEditor : public QTextEdit
+{
+ Q_OBJECT
+public:
+ KSircTopicEditor( QWidget *parent, const char *name = 0 );
+
+signals:
+ void resized();
+
+public slots:
+ virtual void slotMaybeResize();
+
+protected:
+ virtual void keyPressEvent( QKeyEvent *ev );
+ virtual void focusOutEvent( QFocusEvent * );
+ virtual QPopupMenu *createPopupMenu( const QPoint &pos );
+
+private:
+ QGuardedPtr<QPopupMenu> m_popup;
+};
+
+#endif
diff --git a/ksirc/toplevel.cpp b/ksirc/toplevel.cpp
new file mode 100644
index 00000000..f064a291
--- /dev/null
+++ b/ksirc/toplevel.cpp
@@ -0,0 +1,1848 @@
+/**********************************************************************
+
+ TopLevel IRC Channel/query Window
+
+ $$Id$$
+
+ This is the main window with which the user interacts. It handles
+ both normal channel conversations and private conversations.
+
+ 2 classes are defined, the UserControlMenu and KSircToplevel. The
+ user control menu is used as a list of user defineable menus used by
+ KSircToplevel.
+
+ KSircTopLevel:
+
+ Signals:
+
+ outputLine(QString &):
+ output_toplevel(QString):
+
+ closing(KSircTopLevel *, QString channel):
+
+ changeChannel(QString old, QString new):
+
+ currentWindow(KSircTopLevel *):
+
+ Slots:
+
+
+
+ *********************************************************************/
+
+#include "toplevel.h"
+#include "alistbox.h"
+#include "chanparser.h"
+#include "ksopts.h"
+#include "control_message.h"
+#include "displayMgr.h"
+#include "NewWindowDialog.h"
+#include "usercontrolmenu.h"
+#include "topic.h"
+#include "charSelector.h"
+#include "ksview.h"
+#include "logfile.h"
+#include "servercontroller.h"
+#include "ioDCC.h"
+
+#include "KSTicker/ksticker.h"
+
+#include <stdlib.h>
+
+#include <qaccel.h>
+#include <qclipboard.h>
+#include <qregexp.h>
+#include <qcursor.h>
+#include <qtimer.h>
+#include <qlayout.h>
+#include <qtextcodec.h>
+#include <qvbox.h>
+#include <qlabel.h>
+
+#include <kmenubar.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kwin.h>
+#include <knotifyclient.h>
+#include <kpopupmenu.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <kaction.h>
+#include <kcharsets.h>
+#include <kglobalsettings.h>
+#include <kstdguiitem.h>
+
+extern DisplayMgr *displayMgr;
+//QPopupMenu *KSircTopLevel::user_controls = 0L;
+QPtrList<UserControlMenu> *KSircTopLevel::user_menu = 0L;
+QStringList KSircTopLevel::cmd_menu;
+
+void
+KSircTopLevel::initColors()
+{
+ QColorGroup cg_mainw = kapp->palette().active();
+ cg_mainw.setColor(QColorGroup::Base, ksopts->backgroundColor);
+ cg_mainw.setColor(QColorGroup::Text, ksopts->textColor);
+ cg_mainw.setColor(QColorGroup::Link, ksopts->linkColor);
+ cg_mainw.setColor(QColorGroup::Highlight, ksopts->selBackgroundColor);
+ cg_mainw.setColor(QColorGroup::HighlightedText, ksopts->selForegroundColor);
+ mainw->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+ nicks->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+ linee->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+ lag->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+ cg_mainw.setColor(QColorGroup::Background, ksopts->backgroundColor);
+ cg_mainw.setColor(QColorGroup::Foreground, ksopts->textColor);
+ ksTopic->setPalette(QPalette(cg_mainw,cg_mainw, cg_mainw));
+ selector->setFont( ksopts->defaultFont.family() );
+ mainw->setFont( ksopts->defaultFont );
+ nicks->setFont( ksopts->defaultFont );
+ linee->setFont( ksopts->defaultFont );
+ ksTopic->setFont( ksopts->defaultFont );
+
+}
+
+KSircTopLevel::KSircTopLevel(KSircProcess *_proc, const KSircChannel &channelInfo, const char * name)
+ : KMainWindow(0, name, 0/*no WDestructiveClose !*/),
+ UnicodeMessageReceiver(_proc),
+ lastBeep( QTime::currentTime() ),
+ m_channelInfo(channelInfo)
+
+{
+ // prevent us from being quitted when closing a channel-window. Only
+ // closing the servercontroller shall quit.
+ // KMainWindow will deref() us in closeEvent
+ kapp->ref();
+
+
+ m_topic = QString::null;
+
+ QCString kstl_name = QCString(QObject::name()) + "_" + "toplevel";
+ setName(kstl_name);
+
+ if(!m_channelInfo.channel().isEmpty()) {
+ setCaption(m_channelInfo.channel());
+ caption = m_channelInfo.channel();
+ }
+ else
+ {
+ caption = QString::null;
+ }
+
+ Buffer = FALSE;
+
+ have_focus = 0;
+ tab_pressed = -1; // Tab (nick completion not pressed yet)
+ tab_start = -1;
+ tab_end = -1;
+
+ m_gotMsgWithoutFocus = false;
+
+ KickWinOpen = false;
+ current_size = size();
+
+ ksopts->channelSetup(ksircProcess()->serverName(), m_channelInfo.channel());
+ m_channelInfo.setEncoding(ksopts->chan(m_channelInfo).encoding);
+
+ selector = new charSelector();
+ connect(selector, SIGNAL(clicked()), this, SLOT(insertText()));
+ selector->setFont(ksopts->defaultFont.family());
+
+ file = new QPopupMenu(this, QCString(QObject::name()) + "_popup_file");
+ file->setCheckable(true);
+
+ KAction *act = KStdAction::openNew( this, SLOT( newWindow() ), actionCollection() );
+ act->plug( file );
+ file->insertItem(i18n("New Ser&ver..."), servercontroller::self(), SLOT(new_connection()), Key_F2);
+ file->insertSeparator();
+ file->insertItem(i18n("&DCC Manager..."), this, SLOT(showDCCMgr()));
+ file->insertItem(i18n("&Save to Logfile..."), this, SLOT(saveCurrLog()), CTRL + Key_S);
+
+ tsitem = file->insertItem(i18n("Time St&amp"), this, SLOT(toggleTimestamp()), CTRL + Key_T);
+ file->setItemChecked(tsitem, ksopts->chan(m_channelInfo).timeStamp);
+
+ fjpitem = file->insertItem(i18n("Hide Join/Part Messages"), this, SLOT(toggleFilterJoinPart()));
+ file->setItemChecked(fjpitem, ksopts->chan(m_channelInfo).filterJoinPart);
+
+ file->insertItem(i18n("Character &Table"), selector, SLOT(show()), CTRL + Key_H);
+ beepitem = file->insertItem(i18n("N&otify on Change"), this, SLOT(toggleBeep()), CTRL + Key_P);
+ file->setItemChecked(beepitem, ksopts->chan(m_channelInfo).beepOnMsg);
+
+ encodingAction = new KSelectAction( i18n( "&Encoding" ), 0, this );
+ connect( encodingAction, SIGNAL( activated() ), this, SLOT( setEncoding() ) );
+ QStringList encodings = KGlobal::charsets()->descriptiveEncodingNames();
+
+ topicitem = file->insertItem(i18n("S&how Topic"), this, SLOT(toggleTopic()), CTRL + Key_O);
+ if (isPrivateChat() || m_channelInfo.channel().startsWith("!no_channel")) {
+ file->setItemEnabled(topicitem, false);
+ }
+ else {
+ file->setItemChecked(topicitem, ksopts->chan(m_channelInfo).topicShow);
+ }
+
+ tickeritem = file->insertItem(i18n("Ticker &Mode"), this, SLOT(toggleTicker()));
+
+ // remove utf16/ucs2 as it just doesn't work for IRC
+ QStringList::Iterator encodingIt = encodings.begin();
+ while ( encodingIt != encodings.end() ) {
+ if ( ( *encodingIt ).find( "utf16" ) != -1 ||
+ ( *encodingIt ).find( "iso-10646" ) != -1 )
+ encodingIt = encodings.remove( encodingIt );
+ else
+ ++encodingIt;
+ }
+ encodings.prepend( i18n( "Default" ) );
+
+ encodingAction->setItems( encodings );
+ encodingAction->plug( file );
+
+ int eindex = encodings.findIndex(ksopts->chan(m_channelInfo).encoding);
+ if(eindex == -1)
+ encodingAction->setCurrentItem( 0 );
+ else
+ encodingAction->setCurrentItem(eindex);
+ setEncoding();
+
+ file->insertSeparator();
+ act = KStdAction::close( this, SLOT( terminate() ), actionCollection() );
+ act->plug( file );
+
+ kmenu = menuBar();
+ kmenu->insertItem(i18n("&Channel"), file, KST_CHANNEL_ID, -1);
+ kmenu->setAccel(Key_F, KST_CHANNEL_ID);
+
+ /*
+ * Ok, let's look at the basic widget "layout"
+ * Everything belongs to q QFrame F, this is use so we
+ * can give the KApplication a single main client widget, which is needs.
+ *
+ * A QVbox and a QHbox is used to ctronl the 3 sub widget
+ * The Modified QListBox is then added side by side with the User list box.
+ * The SLE is then fit bello.
+ */
+
+ // kstInside does not setup fonts, etc, it simply handles sizing
+
+ top = new QVBox( this );
+
+ ksTopic = new KSircTopic( top );
+ ksTopic->setFont(ksopts->defaultFont);
+ connect( ksTopic, SIGNAL( topicChange( const QString & ) ),
+ this, SLOT( setTopicIntern( const QString & ) ) );
+
+ QCString kstn = QCString(QObject::name()) + "_";
+
+ pan = new QSplitter(QSplitter::Horizontal, top, kstn + "splitter");
+#if KDE_IS_VERSION(3,1,92)
+ pan->setOpaqueResize( KGlobalSettings::opaqueResize() );
+#else
+ pan->setOpaqueResize( true );
+#endif
+
+ mainw = new KSircView(ksircProcess(), pan, kstn + "KSircView" );
+ mainw->setFocusPolicy(QWidget::NoFocus);
+
+ nicks_box = new QVBox(pan);
+
+ channelButtons = new chanButtons(ksircProcess(), nicks_box);
+ connect(channelButtons, SIGNAL(mode(QString, int, QString)),
+ this, SLOT(setMode(QString, int, QString)));
+
+ nicks = new aListBox(nicks_box, kstn + "aListBox");
+ nicks->setFocusPolicy(QWidget::NoFocus);
+ //nicks->hide(); // default = only the main widget
+
+ lag = new QLabel(nicks_box);
+ lag->setFrameStyle(QFrame::Panel|QFrame::Sunken);
+ lag->setAlignment(Qt::AlignCenter | Qt::SingleLine);
+ lag->setText(i18n("Lag: Wait"));
+
+
+ QValueList<int> sizes;
+ sizes << int(width()*0.85) << int(width()*0.15);
+ pan->setSizes(sizes);
+ pan->setResizeMode( mainw, QSplitter::Stretch );
+ pan->setResizeMode( nicks_box, QSplitter::Stretch );
+
+// f = new kstInside(top, QString(QObject::name()) + "_" + "kstIFrame");
+ top->setStretchFactor(pan, 1);
+
+ setCentralWidget(top); // Tell the KApplication what the main widget is.
+
+ logFile = 0;
+ if ( ksopts->chan(m_channelInfo).logging && (m_channelInfo.channel() != "!no_channel" ))
+ {
+ logFile = new LogFile( m_channelInfo.channel(), m_channelInfo.server() );
+ logFile->open();
+ }
+
+ // get basic variable
+
+// mainw = f->mainw;
+// nicks = f->nicks;
+ // pan = f->pan;
+
+ clearWindow();
+
+ if(isPrivateChat()){
+ KConfig conf("ksirc/winlog/" + channelInfo.server() + "-" + channelInfo.channel(),
+ false, false, "data");
+ QString group = "Message-History";
+
+ if(conf.hasGroup( group )){
+ conf.setGroup( group );
+
+ QStringList strlist = conf.readListEntry("History");
+ if(strlist.count() > 0){
+ mainw->enableTimeStamps(true);
+ for ( QStringList::Iterator it = strlist.begin();
+ it != strlist.end();
+ ++it ) {
+ mainw->addRichText(*it);
+ }
+ }
+ strlist.clear();
+ }
+ }
+
+ mainw->enableTimeStamps(ksopts->chan(m_channelInfo).timeStamp);
+
+ edit = new QPopupMenu(this);
+ act = KStdAction::copy( mainw, SLOT( copy() ), actionCollection() );
+ act->plug( edit );
+ act = KStdAction::paste( this, SLOT( pasteToWindow() ), actionCollection() );
+ act->plug( edit );
+ edit->insertItem(i18n("C&lear Window"), this, SLOT(clearWindow()), CTRL + Key_L);
+ kmenu->insertItem(i18n("&Edit"), edit, -1, -1);
+
+ linee = new aHistLineEdit(top, "");
+
+ initColors();
+
+ // ksb_main->addWidget(linee, mainw->width());
+// ksb_main->insertItem(, KSB_MAIN_LAG, true);
+
+ // don't show the nick lists in a private chat or the default window
+ if (isPrivateChat() || m_channelInfo.channel().startsWith("!no_channel"))
+ {
+ nicks_box->hide();
+ ksTopic->hide();
+ // nicks->hide();
+ // lag->hide();
+ // channelButtons->hide();
+ }
+ else
+ {
+ nicks_box->show();
+
+ if(file->isItemChecked(topicitem)){
+ ksTopic->show();
+// channelButtons->show();
+ }
+ else{
+ ksTopic->hide();
+ // channelButtons->hide();
+ }
+ // channelButtons->show();
+ // lag->show();
+ // nicks->show();
+ }
+
+
+ connect(mainw, SIGNAL(pasteReq( const QString& )),
+ this, SLOT( slotTextDropped( const QString& )));
+
+ nicks->setFont(ksopts->defaultFont);
+
+ // setup line editor
+
+ linee->setFocusPolicy(QWidget::StrongFocus);
+ linee->setFont(ksopts->defaultFont);
+
+ if(ksopts->oneLineEntry == true) {
+ linee->setWordWrap(QTextEdit::NoWrap);
+ }
+ else {
+ linee->setWordWrap(QTextEdit::WidgetWidth);
+ }
+
+ connect(linee, SIGNAL(gotFocus()),
+ this, SLOT(gotFocus()));
+ connect(linee, SIGNAL(lostFocus()),
+ this, SLOT(lostFocus()));
+ connect(linee, SIGNAL(pasteText(const QString&)),
+ this, SLOT(slotTextDropped(const QString&)));
+ connect(linee, SIGNAL(notTab()),
+ this, SLOT(lineeNotTab()));
+
+ connect( linee, SIGNAL( gotReturnPressed() ),
+ this, SLOT( returnPressed() ) );
+
+ linee->setFocus(); // Give SLE focus
+ linee->slotMaybeResize();
+
+ lines = 0; // Set internal line counter to 0
+
+ ticker = 0x0;
+
+ /*
+ * Set generic run time variables
+ *
+ */
+
+ opami = FALSE;
+ continued_line = FALSE;
+// on_root = FALSE;
+
+ /*
+ * Load basic pics and images
+ * This should use on of the KDE finder commands
+ */
+
+ KWin::setIcons(winId(), kapp->icon(), kapp->miniIcon());
+
+ /*
+ * Create our basic parser object
+ */
+
+ ChanParser = new ChannelParser(this);
+
+
+ /*
+ * Create the user Controls popup menu, and connect it with the
+ * nicks list on the lefthand side (highlighted()).
+ *
+ */
+
+ if(user_menu == 0)
+ user_menu = UserControlMenu::parseKConfig();
+
+ user_controls = new QPopupMenu(this);
+ kmenu->insertItem(i18n("&Users"), user_controls);
+
+ command = new QPopupMenu(this);
+
+ setupCommandMenu();
+
+ int i = 0;
+ QDict<QPopupMenu> cml;
+ for(QStringList::Iterator it = cmd_menu.begin();
+ it != cmd_menu.end();
+ ++it){
+ QString top, item;
+ top = (*it).section('/', 0, 0);
+ item = (*it).section('/', 1, 1);
+ if(!cml[top]) {
+ cml.insert(top, new QPopupMenu(this));
+ command->insertItem(top, cml[top]);
+ }
+ cml[top]->insertItem(item, this, SLOT(cmd_process(int)), 0, i);
+
+ i++;
+ }
+
+ kmenu->insertItem(i18n("C&ommand"), command);
+
+ kmenu->insertItem( KStdGuiItem::help().text(), helpMenu( QString::null, false ));
+
+ connect(user_controls, SIGNAL(activated(int)),
+ this, SLOT(UserParseMenu(int)));
+
+ connect(nicks, SIGNAL(contextMenuRequested(int)), this,
+ SLOT(UserSelected(int)));
+ connect(nicks, SIGNAL(selectedNick(const QString &)),
+ this, SLOT(openQueryFromNick(const QString &)));
+ connect(nicks, SIGNAL(mouseButtonClicked ( int, QListBoxItem *, const QPoint &)),
+ this, SLOT(pasteToNickList(int, QListBoxItem *, const QPoint &)));
+ connect(nicks, SIGNAL(textDropped( const QListBoxItem *, const QString& )),
+ this, SLOT(dndTextToNickList(const QListBoxItem *, const QString&)));
+
+ UserUpdateMenu(); // Must call to update Popup.
+
+ accel = new QAccel(this, "accel");
+
+ accel->connectItem(accel->insertItem(SHIFT + Key_PageUp),
+ this,
+ SLOT(AccelScrollUpPage()));
+ accel->connectItem(accel->insertItem(SHIFT + Key_PageDown),
+ this,
+ SLOT(AccelScrollDownPage()));
+
+ /*
+ * Pageup/dn
+ * Added for stupid wheel mice
+ */
+
+ accel->connectItem(accel->insertItem(Key_PageUp),
+ this,
+ SLOT(AccelScrollUpPage()));
+ accel->connectItem(accel->insertItem(Key_PageDown),
+ this,
+ SLOT(AccelScrollDownPage()));
+
+ /*
+ * These are not presently implemented, so let's not fill the logs.
+ accel->connectItem(accel->insertItem(CTRL + Key_Return),
+ this,
+ SLOT(AccelPriorMsgNick()));
+
+ accel->connectItem(accel->insertItem(CTRL + SHIFT + Key_Return),
+ this,
+ SLOT(AccelNextMsgNick()));
+ */
+
+ accel->connectItem(accel->insertItem(Key_Tab), // adds TAB accelerator
+ this, // connected to the main
+ SLOT(TabNickCompletionNormal())); // TabNickCompletion() slot
+ accel->connectItem(accel->insertItem(SHIFT+Key_Tab), // adds TAB accelerator
+ this, // connected to the main
+ SLOT(TabNickCompletionShift())); // TabNickCompletion() slot
+ accel->connectItem(accel->insertItem(CTRL + Key_N),
+ this, SLOT(newWindow()));
+// accel->connectItem(accel->insertItem(CTRL + Key_S),
+// this, SLOT(toggleTimestamp()));
+
+ // Drag & Drop
+ connect( mainw, SIGNAL( textDropped(const QString&) ),
+ SLOT( slotTextDropped(const QString&) ));
+ connect( mainw, SIGNAL( urlsDropped(const QStringList&) ),
+ SLOT( slotDropURLs(const QStringList&) ));
+ connect( nicks, SIGNAL( urlsDropped( const QStringList&, const QString& )),
+ SLOT( slotDccURLs( const QStringList&, const QString& )));
+ connect( this, SIGNAL( changed(bool, QString) ), this, SLOT( doChange(bool, QString) ));
+
+ mainw->setAcceptFiles( isPrivateChat() );
+ resize(600, 360);
+
+}
+
+KSircTopLevel::~KSircTopLevel()
+{
+ // Cleanup and shutdown
+ // if(this == ksircProcess()->getWindowList()["default"])
+ // write(sirc_stdin, "/quit\n", 7); // tell dsirc to close
+ //
+ // if(ksircProcess()->getWindowList()[m_channelInfo.channel()])
+ // ksircProcess()->getWindowList().remove(m_channelInfo.channel());
+
+ // if((m_channelInfo.channel()[0] == '#') || (m_channelInfo.channel()[0] == '&')){
+ // QString str = QString("/part ") + m_channelInfo.channel() + "\n";
+ // emit outputLine(str);
+ // }
+
+ kdDebug(5008) << "~KSircTopLevel in" << endl;
+ if ( ksopts->autoSaveHistory )
+ {
+ if ( isPublicChat() ) {
+ kdDebug(5008) << "*** parting channel: " << m_channelInfo.channel() << endl;
+ QString str = QString("/part ") + m_channelInfo.channel() + "\n";
+ emit outputUnicodeLine(str);
+ }
+ else {
+ QStringList strlist;
+
+ mainw->addLine("user|X", ksopts->channelColor, " Saved log of previous messages");
+
+ mainw->enableTimeStamps(true);
+
+ for(KSirc::TextParagIterator ksit = mainw->firstParag();
+ ksit.atEnd() == 0;
+ ++ksit) {
+ QString rt = ksit.richText();
+ if(rt.contains("<img src=\"user|servinfo\">"))
+ continue;
+
+ kdDebug(5008) << rt << endl;
+
+ strlist += rt;
+
+ }
+
+ KConfig conf("ksirc/winlog/" + channelInfo().server() +
+ "-" + channelInfo().channel(),
+ false, false, "data");
+ QString group = "Message-History";
+
+ conf.setGroup( group );
+
+ conf.writeEntry("History", strlist);
+ conf.sync();
+
+ }
+ }
+ delete ticker;
+ delete user_controls;
+ delete ChanParser;
+ delete selector;
+ delete channelButtons;
+ delete logFile;
+ kdDebug(5008) << "~KSircToplevel out" << endl;
+}
+
+void KSircTopLevel::setMode(QString mode, int mode_type, QString currentNick)
+{
+ QString command;
+ if (mode_type == 0)
+ command = QString::fromLatin1("/mode %1 %2\n").arg(m_channelInfo.channel()).arg(mode);
+ else
+ command = QString::fromLatin1("/mode %1 %2\n").arg(currentNick).arg(mode);
+ sirc_write(command);
+ linee->setFocus();
+}
+
+void KSircTopLevel::setEncoding()
+{
+ int index = encodingAction->currentItem();
+ if ( index == 0 ) {// default (locale) encoding
+ ksopts->chan(m_channelInfo).encoding = QString::null;
+ UnicodeMessageReceiver::setEncoding( QString::null );
+ }
+ else {
+ ksopts->chan(m_channelInfo).encoding = encodingAction->currentText();
+ UnicodeMessageReceiver::setEncoding( KGlobal::charsets()->encodingForName( encodingAction->currentText() ) );
+ }
+ ksopts->save(KSOptions::Channels);
+}
+
+void KSircTopLevel::setupCommandMenu()
+{
+ if(cmd_menu.empty() == true){
+ cmd_menu.append(i18n("Help") + "/" + "help");
+ cmd_menu.append(i18n("Client") + "/" + "alias");
+ cmd_menu.append(i18n("User") + "/" + "away");
+ cmd_menu.append(i18n("Client") + "/" + "bye");
+ cmd_menu.append(i18n("Client") + "/" + "cd");
+ cmd_menu.append(i18n("Basic") + "/" + "ctcp");
+ cmd_menu.append(i18n("Basic") + "/" + "dcc");
+ cmd_menu.append(i18n("Operator") + "/" + "deop");
+ cmd_menu.append(i18n("User") + "/" + "describe");
+ cmd_menu.append(i18n("Client") + "/" + "eval");
+ cmd_menu.append(i18n("Client") + "/" + "exit");
+ cmd_menu.append(i18n("Client") + "/" + "hop");
+ cmd_menu.append(i18n("Basic") + "/" + "ignore");
+ cmd_menu.append(i18n("Server") + "/" + "info");
+ cmd_menu.append(i18n("Channel") + "/" + "invite" + "/" + "*chan*");
+ cmd_menu.append(i18n("Basic") + "/" + "join");
+ cmd_menu.append(i18n("Operator") + "/" + "kick");
+ cmd_menu.append(i18n("Oper") + "/" + "kill");
+ cmd_menu.append(i18n("Basic") + "/" + "leave");
+ cmd_menu.append(i18n("Server") + "/" + "links");
+ cmd_menu.append(i18n("Basic") + "/" + "list");
+ cmd_menu.append(i18n("Basic") + "/" + "ll");
+ cmd_menu.append(i18n("Client") + "/" + "load");
+ cmd_menu.append(i18n("Server") + "/" + "lusers");
+ cmd_menu.append(i18n("Server") + "/" + "map");
+ cmd_menu.append(i18n("Basic") + "/" + "me");
+ cmd_menu.append(i18n("Channel") + "/" + "mode" + "/" + "*chan*");
+ cmd_menu.append(i18n("Server") + "/" + "motd");
+ cmd_menu.append(i18n("Basic") + "/" + "msg");
+ cmd_menu.append(i18n("Basic") + "/" + "nick");
+ cmd_menu.append(i18n("Basic") + "/" + "notice");
+ cmd_menu.append(i18n("Basic") + "/" + "notify");
+ cmd_menu.append(i18n("Operator") + "/" + "op");
+ cmd_menu.append(i18n("Oper") + "/" + "oper");
+ cmd_menu.append(i18n("Basic") + "/" + "query");
+ cmd_menu.append(i18n("Channel") + "/" + "part"+ "/" + "*chan*");
+ cmd_menu.append(i18n("Basic") + "/" + "ping");
+ cmd_menu.append(i18n("Client") + "/" + "quit");
+ cmd_menu.append(i18n("Server") + "/" + "quote");
+ cmd_menu.append(i18n("Oper") + "/" + "rping");
+ cmd_menu.append(i18n("Oper") + "/" + "rehash");
+ cmd_menu.append(i18n("Basic") + "/" + "say");
+ cmd_menu.append(i18n("Server") + "/" + "stats");
+ cmd_menu.append(i18n("Client") + "/" + "system");
+ cmd_menu.append(i18n("Server") + "/" + "time");
+ cmd_menu.append(i18n("Channel") + "/" + "topic" + "/" + "*chan*");
+ cmd_menu.append(i18n("Client") + "/" + "version");
+ cmd_menu.append(i18n("Oper") + "/" + "wallops");
+ cmd_menu.append(i18n("Channel") + "/" + "who" + "/" + "*chan*");
+ cmd_menu.append(i18n("Basic") + "/" + "whois");
+ cmd_menu.append(i18n("Basic") + "/" + "whowas");
+ cmd_menu.append(i18n("Basic") + "/" + "wi");
+ cmd_menu.append(i18n("Help") + "/" + "newuser");
+ cmd_menu.append(i18n("Channel") + "/" + "ban");
+ cmd_menu.append(i18n("Channel") + "/" + "unban");
+ cmd_menu.append(i18n("Channel") + "/" + "clrban" + "/" + "*chan*");
+ cmd_menu.append(i18n("Channel") + "/" + "banlist" + "/" + "*chan*");
+ cmd_menu.append(i18n("Basic") + "/" + "pig");
+ cmd_menu.append(i18n("Channel") + "/" + "wallop");
+ cmd_menu.append(i18n("Client") + "/" + "exec");
+ cmd_menu.append(i18n("Basic") + "/" + "url");
+ cmd_menu.sort();
+ }
+}
+
+void KSircTopLevel::insertText()
+{
+ linee->insert(selector->currentText());
+}
+
+void KSircTopLevel::show()
+{
+ KMainWindow::show();
+ linee->setFocus();
+ mainw->scrollToBottom(true);
+}
+
+enum {
+ KSTUp = 1,
+ KSTDown = 2
+};
+
+void KSircTopLevel::TabNickCompletionShift()
+{
+ TabNickCompletion(KSTDown);
+}
+
+void KSircTopLevel::TabNickCompletionNormal()
+{
+ TabNickCompletion(KSTUp);
+}
+
+void KSircTopLevel::TabNickCompletion(int dir)
+{
+ /*
+ * Gets current text from lined find the last item and try and perform
+ * a nick completion, then return and reset the line.
+ */
+
+ int start, end;
+ int extra = 0;
+ bool first = false;
+ QString s;
+
+ if(tab_pressed == -1){
+ s = linee->text();
+ tab_saved = s;
+ end = linee->cursorPosition() - 1;
+ start = s.findRev(" ", end, FALSE);
+ tab_start = start;
+ tab_end = end;
+ first = true;
+ }
+ else{
+ s = tab_saved;
+ start = tab_start;
+ end = tab_end;
+ }
+
+ if(dir == KSTDown){
+ if(tab_pressed > 0)
+ tab_pressed -= 1;
+ }
+ else {
+ tab_pressed += 1;
+ }
+
+
+ if(s.length() == 0){
+ if(tab_nick.isEmpty()) {
+ KNotifyClient::beep();
+ lineeNotTab();
+ return;
+ }
+ QString line = tab_nick + ": "; // tab_nick holds the last night since we haven't overritten it yet.
+ linee->setText(line);
+ linee->setCursorPosition(line.length());
+ connect(linee, SIGNAL(notTab()),
+ this, SLOT(lineeNotTab()));
+ return;
+ }
+
+ if (start == -1) {
+ tab_nick = findNick(s.mid(0, end+1), tab_pressed);
+
+ if(first && (tab_nick.isNull() == TRUE)){
+ KNotifyClient::beep();
+ lineeNotTab();
+ return;
+ }
+ else if(tab_nick.isNull() == TRUE){
+ tab_pressed -= 1;
+ tab_nick = findNick(s.mid(0, end+1), tab_pressed);
+ }
+ s.replace(0, end + 1, "");
+ if(s[0] == ':')
+ s.replace(0, 2, "");
+ s.prepend(tab_nick + ": ");
+ extra = 2;
+ }
+ else {
+ tab_nick = findNick(s.mid(start + 1, end - start), tab_pressed);
+ if(first && (tab_nick.isNull() == TRUE)){
+ KNotifyClient::beep();
+ lineeNotTab();
+ return;
+ }
+ else if(tab_nick.isNull() == TRUE){
+ tab_pressed -= 1;
+ tab_nick = findNick(s.mid(start + 1, end - start), tab_pressed);
+ }
+
+ if((uint) end == s.length() - 1){ /* if we're at the end add a space */
+ s.replace(start + 1, end - start, tab_nick + " ");
+ extra = 1;
+ }
+ else {
+ s.replace(start + 1, end - start, tab_nick);
+ extra = 0;
+ }
+ }
+
+ int tab = tab_pressed;
+
+ linee->setText(s);
+ linee->setCursorPosition(start + tab_nick.length() + 1 + extra);
+
+ tab_pressed = tab; // setText causes lineeTextChanged to get called and erase tab_pressed
+
+ connect(linee, SIGNAL(notTab()),
+ this, SLOT(lineeNotTab()));
+
+}
+
+void KSircTopLevel::sirc_receive(QString str, bool broadcast)
+{
+
+ /*
+ * read and parse output from dsirc.
+ * call reader, split the read input into lines, parse the lines
+ * then print line by line into the main text area.
+ *
+ * PROBLEMS: if a line terminates in mid line, it will get borken oddly
+ *
+ */
+
+ if(!Buffer){
+ if( !str.isEmpty() ){
+ LineBuffer.append( BufferedLine( str, broadcast ) );
+ }
+
+ bool addressed = false;
+ BufferedLine line;
+
+ // be careful not to use a QValueList iterator here, as it is possible
+ // that we enter a local event loop (think of the ssfeprompt dialog!)
+ // which might trigger a socketnotifier activation which in turn
+ // might result in the execution of code that modifies the LineBuffer,
+ // which would invalidate iterators (Simon)
+ while ( LineBuffer.begin() != LineBuffer.end() )
+ {
+ QString plainText("");
+ line = *LineBuffer.begin();
+ LineBuffer.remove( LineBuffer.begin() );
+
+ // Get the need list box item, with colour, etc all set
+ if (parse_input(line.message, plainText))
+ {
+ // If we should add anything, add it.
+ // Don't announce server messages as they are
+ // spread through all channels anyway
+
+ bool addressedLine = false;
+
+ if(line.message.contains( ">~o")){ /* highlighted with our nick */
+ addressedLine = true;
+ }
+
+ // detect /msg's -- needed when auto-create-window is off
+ if ( line.message.find( QRegExp( "^\\[~b.+~b\\].+$" ) ) == 0 )
+ addressedLine = true;
+
+ if ( addressedLine == true && line.message.startsWith("* " + ksircProcess()->getNick()))
+ addressedLine = false;
+
+ if ( addressedLine )
+ addressed = true;
+
+ if ( !line.wasBroadcast ) {
+ // This line is addressed to me if it contains my nick, or is in a private chat
+ emit changed( addressedLine || isPrivateChat(), plainText );
+ }
+ }
+ }
+ LineBuffer.clear(); // Clear it since it's been added
+ }
+ else{
+ LineBuffer.append( BufferedLine( str, broadcast ) );
+ }
+
+ QValueList<int> list;
+ QValueList<int>::iterator it;
+ QString values;
+ list = pan->sizes();
+ it = list.begin();
+ while( it != list.end()){
+ values += QString("%1 ").arg(*it);
+ ++it;
+ }
+}
+void KSircTopLevel::sirc_line_return(const QString &text)
+{
+
+ /* Take line from SLE, and output if to dsirc */
+
+ QString s = text;
+
+ if(s.length() == 0)
+ return;
+
+ tab_pressed = -1; // new line, zero the counter.
+ s += '\n'; // Append a need carriage return :)
+
+ sirc_write(s);
+
+ linee->setText("");
+ linee->slotMaybeResize();
+
+}
+
+void KSircTopLevel::sirc_write(const QString &str)
+{
+ /*
+ * Parse line forcommand we handle
+ */
+ QString command = str, plain = str.lower().simplifyWhiteSpace();
+ if(plain.startsWith("/join ") ||
+ plain.startsWith("/j ") ||
+ plain.startsWith("/query ")) {
+
+ QString s = plain.mid(plain.find(' ')+1);
+
+ QStringList channels = QStringList::split(",", s);
+ QStringList::ConstIterator it = channels.begin();
+
+ for( ; it != channels.end(); ++it){
+ QString name = *it;
+ //kdDebug(5008) << "Doing " << name << endl;
+ QRegExp rx("(\\S+)\\s*(\\S*)");
+ rx.search(name);
+ KSircChannel ci(m_channelInfo.server(),
+ rx.cap(1), // channel
+ rx.cap(2)); // optional key
+ linee->setText(QString::null);
+ emit open_toplevel(ci);
+/* if(name[0] != '#'){
+ emit open_toplevel(name);
+ linee->setText(QString::null);
+ }
+ else {
+ emit outputUnicodeLine(plain + "\n");
+ emit open_toplevel(encoder()->fromUnicode(name));
+ }
+*/
+ }
+ // Finish sending /join
+ return;
+ }
+ else if(plain.startsWith("/server ")) {
+ plain.simplifyWhiteSpace();
+ QRegExp rx("/server (\\S+) *(\\S*) *(\\S*)");
+ if(rx.search(str) >= 0){
+ QString server = rx.cap(1);
+ QString port = rx.cap(2);
+ QString pass = rx.cap(3);
+
+ bool ssl = false;
+ if(server.startsWith("+")){
+ server.replace(0, 1, "");
+ ssl = true;
+ }
+
+ KSircServer serv(server, port, QString::null, pass, ssl);
+ servercontroller::self()->new_ksircprocess(serv);
+
+ return;
+ }
+ }
+ else if(plain.startsWith("/part") ||
+ plain.startsWith("/leave") ||
+ plain.startsWith("/hop")) {
+ QApplication::postEvent(this, new QCloseEvent()); // WE'RE DEAD
+ linee->setText(QString::null);
+ return;
+ }
+ else if( plain.startsWith( "/bye" ) ||
+ plain.startsWith( "/exit" ) ||
+ plain.startsWith( "/quit" )) {
+ linee->setText( QString::null );
+ emit requestQuit( command.ascii() );
+ return;
+ }
+ else if ( plain.startsWith( "/away" ) ) {
+ QString awayEvalCommand = QString::fromLatin1( "/eval $publicAway = %1\n" ).arg( ksopts->publicAway ? '1' : '0' );
+ emit outputUnicodeLine( awayEvalCommand );
+ }
+
+ //
+ // Look at the command, if we're assigned a channel name, default
+ // messages, etc to the right place. This include /me, etc
+ //
+
+ if(!isSpecialWindow()) { // channel or private chat
+ if(plain[0].latin1() != '/'){
+ command.prepend(QString::fromLatin1("/msg %1 ").arg(m_channelInfo.channel()));
+ }
+ else if(plain.startsWith("/me ")) {
+ command.remove(0, 3);
+ command.prepend(QString("/de ") + m_channelInfo.channel());
+ }
+ }
+
+ // Write out line
+
+ emit outputUnicodeLine(command);
+ /*
+ * as opposed to what some might think
+ * it doesn't matter when you call stb
+ */
+ mainw->scrollToBottom(true);
+}
+
+bool KSircTopLevel::parse_input(const QString &string, QString &plainText)
+{
+ /*
+ * Parsing routines are broken into 3 majour sections
+ *
+ * 1. Search and replace evil characters. The string is searched for
+ * each character in turn (evil[i]), and the character string in
+ * evil_rep[i] is replaced.
+ *
+ * 2. Parse control messages, add pixmaps, and colourize required
+ * lines. Caption is also set as required.
+ *
+ * 3. Create and return the ircListItem.
+ *
+ */
+
+ /*
+ * No-output get's set to 1 if we should ignore the line
+ */
+
+ /*
+ * This is the second generation parsing code.
+ * Each line type is determined by the first 3 characters on it.
+ * Each line type is then sent to a parsing function.
+ */
+ parseResult *pResult = ChanParser->parse(string);
+
+ parseSucc *item = dynamic_cast<parseSucc *>(pResult);
+ parseError *err = dynamic_cast<parseError *>(pResult);
+
+ QString logString;
+
+ if(item)
+ {
+ parseJoinPart *jp = dynamic_cast<parseJoinPart *>(pResult);
+ if(jp &&
+ ksopts->chan(m_channelInfo).filterJoinPart){
+ delete pResult;
+ return true;
+ }
+ else if (!item->string.isEmpty()) {
+ logString = mainw->
+ addLine( item->pm, item->colour, item->string );
+ if(ticker != NULL){
+ ticker->mergeString(item->string, item->colour);
+ }
+ } else {
+ delete pResult;
+ return false;
+ }
+ }
+ else if (err)
+ {
+ if(err->err.isEmpty() == FALSE)
+ {
+ kdWarning() << err->err << ": " << string << endl;
+ delete pResult;
+ return false;
+ }
+ if (!err->str.isEmpty()) {
+ logString = mainw->addLine( "user|error", ksopts->errorColor, err->str );
+ }
+
+ }
+ else
+ {
+ logString = mainw->addLine( QString::null, ksopts->textColor, string );
+ if(ticker != NULL){
+ ticker->mergeString(string);
+ }
+ // If it contains our nick, move the speaker to the top
+ // of the nick completion list
+ if(string.contains("~o")){
+ QRegExp rx("<(\\S+)>");
+ if ( (rx.search(logString) >= 0) &&
+ (rx.cap(1) != ksircProcess()->getNick()) ) {
+ //kdDebug(5008) << "addCompleteNick: " << rx.cap(1) << endl;
+ addCompleteNick( rx.cap(1) );
+ }
+ }
+
+ }
+ delete pResult;
+
+ if ( !logString.isEmpty() && logFile )
+ logFile->log( logString );
+
+ if(!plainText.isNull())
+ plainText = logString;
+
+ return true;
+}
+
+void KSircTopLevel::returnPressed()
+{
+
+ QString s = linee->text();
+
+ uint i;
+ QChar c;
+ for(i = 0; (c = s[i]); i++){
+ switch(c.unicode()){
+ case 0336:
+ s[i] = 002;
+ break;
+ case 0327:
+ s[i] = 037;
+ break;
+ case 0237:
+ s[i] = 026;
+ break;
+ case 0252:
+ s[i] = 003;
+ break;
+ }
+ }
+
+ while(s.length() > IRC_SAFE_MAX_LINE){
+ int wrap = s.findRev(' ', IRC_SAFE_MAX_LINE);
+ if(wrap == -1)
+ wrap = IRC_SAFE_MAX_LINE;
+ sirc_line_return(s.left(wrap));
+ s = s.mid(wrap + 1);
+ }
+ if(!s.stripWhiteSpace().isEmpty())
+ sirc_line_return(s);
+ else {
+ linee->setText("");
+ linee->slotMaybeResize();
+ }
+}
+
+void KSircTopLevel::UserSelected(int index)
+{
+ if(index >= 0)
+ user_controls->popup(this->cursor().pos());
+}
+
+void KSircTopLevel::UserParseMenu(int id)
+{
+ if(nicks->currentItem() < 0)
+ return;
+
+ QString s;
+ s = QString("/eval $dest_nick='%1';\n").arg(nicks->text(nicks->currentItem()));
+ sirc_write(s);
+ // set $$dest_chan variable
+ s = QString("/eval $dest_chan='%1';\n").arg(m_channelInfo.channel());
+ sirc_write(s);
+ QString action = user_menu->at(id)->action;
+ if (action.length() && action[0] == '/')
+ action.remove(0, 1);
+ s = QString("/eval &docommand(eval{\"%1\"});\n").arg(action);
+ s.replace(QRegExp("\\$\\$"), "$");
+ sirc_write(s);
+}
+
+void KSircTopLevel::UserUpdateMenu()
+{
+ int i = 0;
+ UserControlMenu *ucm;
+
+ user_controls->clear();
+ for(ucm = user_menu->first(); ucm != 0; ucm = user_menu->next(), i++){
+ if(ucm->type == UserControlMenu::Seperator){
+ user_controls->insertSeparator();
+ }
+ else{
+ user_controls->insertItem(ucm->title, i);
+ if(ucm->accel)
+ user_controls->setAccel(i, ucm->accel);
+ if((ucm->op_only == TRUE) && (opami == FALSE))
+ user_controls->setItemEnabled(i, FALSE);
+ }
+ }
+}
+
+void KSircTopLevel::AccelScrollDownPage()
+{
+ mainw->verticalScrollBar()->addPage();
+}
+
+void KSircTopLevel::AccelScrollUpPage()
+{
+ mainw->verticalScrollBar()->subtractPage();
+}
+
+void KSircTopLevel::newWindow()
+{
+ NewWindowDialog w(KSircChannel(m_channelInfo.server(), QString::null));
+ connect(&w, SIGNAL(openTopLevel(const KSircChannel &)), SIGNAL(open_toplevel(const KSircChannel &)));
+ w.exec();
+}
+
+void KSircTopLevel::closeEvent(QCloseEvent *e)
+{
+ KMainWindow::closeEvent( e );
+ e->accept();
+
+ //Let's not part the channel till we are acutally delete.
+ // We should always get a close event, *BUT* we will always be deleted.
+ // if( isPublicChat() ) {
+ // QString str = QString("/part ") + m_channelInfo.channel() + "\n";
+ // emit outputLine(str);
+ // }
+
+ // Hide ourselves until we finally die
+ hide();
+ qApp->flushX();
+ // Let's say we're closing
+ kdDebug(5008) << "Toplevel closing: " << m_channelInfo.channel() << endl;
+ emit closing(this, m_channelInfo.channel());
+}
+
+void KSircTopLevel::focusChange(QWidget *w)
+{
+ QWidget *me = this;
+ if(w == me){
+ gotFocus();
+ }
+ else {
+ lostFocus();
+ }
+}
+
+void KSircTopLevel::gotFocus()
+{
+ if(isVisible() == TRUE){
+ if(have_focus == 0){
+ if(m_channelInfo.channel()[0] != '!' ){
+ QString str = QString("/join %1").arg(m_channelInfo.channel());
+ if(m_channelInfo.key().length() > 0){
+ str.append(" " + m_channelInfo.key());
+ }
+ kdDebug(5008) << "Doing join: " << str << " / " << m_channelInfo.channel() << endl;
+ str.append("\n");
+ emit outputUnicodeLine(str);
+ }
+ have_focus = 1;
+ emit currentWindow(this);
+ }
+ }
+ if(m_gotMsgWithoutFocus == true){
+ m_gotMsgWithoutFocus = false;
+ servercontroller::self()->decreaseNotificationCount(QString("%1 -> %2 got message").arg(ksircProcess()->serverID()).arg(m_channelInfo.channel()));
+ }
+
+}
+
+void KSircTopLevel::lostFocus()
+{
+ if(have_focus == 1){
+ have_focus = 0;
+ }
+}
+
+void KSircTopLevel::control_message(int command, QString str)
+{
+ switch(command){
+ case CHANGE_CHANNEL: // 001 is defined as changeChannel
+ {
+ KSircChannel ci(m_channelInfo.server(), QString::null);
+ QRegExp rx("([^!]+)!+([^!]+)!*([^!]*)");
+ if(rx.search(str) < 0){
+ ci.setChannel(str);
+ }
+ else{
+ ci.setChannel(rx.cap(2));
+ ci.setKey(rx.cap(3));
+ }
+ //kdDebug(5008) << "CHANGE NAME: " << ci.channel() << " key: " << ci.key() << endl;
+ emit changeChannel(m_channelInfo.channel(), ci.channel());
+ ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
+ m_channelInfo.setChannel(ci.channel());
+ m_channelInfo.setKey(ci.key());
+ m_channelInfo.setEncoding(ksopts->chan(m_channelInfo).encoding);
+ if ( !logFile && ksopts->chan(m_channelInfo).logging )
+ {
+ logFile = new LogFile( m_channelInfo.channel(), ksircProcess()->serverName() );
+ logFile->open();
+ }
+ setName(m_channelInfo.server().utf8() + "_" + m_channelInfo.channel().utf8() + "_" + "toplevel");
+ pan->setName(QCString(QObject::name()) + "_" + "QSplitter");
+ kmenu->setName(QCString(QObject::name()) + "_ktoolframe");
+ linee->setName(QCString(QObject::name()) + "_" + "LineEnter");
+ kmenu->show();
+ setCaption(m_channelInfo.channel());
+ emit currentWindow(this);
+
+ if(m_channelInfo.channel()[0] == '#' || m_channelInfo.channel()[0] == '&'){
+ QString str = QString("/join %1 %2\n").arg(m_channelInfo.channel()).arg(m_channelInfo.key());
+ kdDebug(5008) << "Doing /join " << m_channelInfo.channel() << " :" << str << endl;
+ emit outputUnicodeLine(str);
+ emit outputLine("/eval $query=''\n");
+ }
+ else if (m_channelInfo.channel()[0] != '!')
+ {
+ emit outputUnicodeLine(QString("/eval $query='%1'\n").arg(m_channelInfo.channel()));
+ }
+
+ bool isPrivate = isPrivateChat();
+
+ mainw->setAcceptFiles( isPrivate );
+
+ if ( isPrivate )
+ {
+ nicks_box->hide();
+ ksTopic->hide();
+ file->setItemEnabled(topicitem, false);
+ }
+ else
+ {
+ file->setItemEnabled(topicitem, true);
+ file->setItemChecked(topicitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow);
+ if(file->isItemChecked(topicitem)){
+ ksTopic->show();
+ // channelButtons->show();
+ }
+ nicks_box->show();
+ pan->show();
+// channelButtons->show();
+// nicks->show();
+// lag->show();
+ }
+ if(layout()) {
+ //kdDebug(5008) << "Redoeing layout" << endl;
+ layout()->invalidate();
+ layout()->activate();
+ }
+ else {
+ //kdDebug(5008) << "No layout" << endl;
+ }
+ pan->refresh();
+
+ ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
+ file->setItemChecked(topicitem, ksopts->chan(m_channelInfo).topicShow);
+ file->setItemChecked(beepitem, ksopts->chan(m_channelInfo).beepOnMsg);
+ file->setItemChecked(tsitem, ksopts->chan(m_channelInfo).timeStamp);
+ file->setItemChecked(fjpitem, ksopts->chan(m_channelInfo).filterJoinPart);
+ int eindex = encodingAction->items().findIndex(ksopts->chan(m_channelInfo).encoding);
+ //kdDebug(5008) << "in change channel we want: " << ksopts->chan(m_channelInfo).encoding << " eindex: " << eindex << endl;
+ encodingAction->setCurrentItem(eindex < 0 ? 0 : eindex);
+ setEncoding();
+ mainw->enableTimeStamps(ksopts->chan(m_channelInfo).timeStamp);
+
+ break;
+ }
+ case STOP_UPDATES:
+ Buffer = TRUE;
+ break;
+ case RESUME_UPDATES:
+ Buffer = FALSE;
+ if(LineBuffer.isEmpty() == FALSE)
+ sirc_receive(QString::null);
+ break;
+ case REREAD_CONFIG:
+ emit freezeUpdates(TRUE); // Stop the list boxes update
+ selector->setFont( ksopts->defaultFont.family() );
+ mainw->setFont( ksopts->defaultFont );
+ nicks->setFont( ksopts->defaultFont );
+ linee->setFont( ksopts->defaultFont );
+ ksTopic->setFont( ksopts->defaultFont );
+ UserUpdateMenu(); // Must call to update Popup.
+ emit freezeUpdates(FALSE); // Stop the list boxes update
+ initColors();
+ ksopts->channelSetup(m_channelInfo.server(), m_channelInfo.channel());
+ file->setItemChecked(topicitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow);
+ file->setItemChecked(beepitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg);
+ file->setItemChecked(tsitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp);
+ {
+ int eindex = encodingAction->items().findIndex(ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].encoding);
+ //kdDebug(5008) << "in re-readconfig we want: " << ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].encoding << " eindex: " << eindex << endl;
+ encodingAction->setCurrentItem(eindex < 0 ? 0 : eindex);
+ setEncoding();
+ }
+ mainw->enableTimeStamps(ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp);
+ if(ksopts->oneLineEntry == true) {
+ linee->setWordWrap(QTextEdit::NoWrap);
+ }
+ else {
+ linee->setWordWrap(QTextEdit::WidgetWidth);
+ }
+ mainw->scrollToBottom(true);
+ update();
+ break;
+ case SET_LAG:
+ if(str.isNull() == FALSE){
+ bool ok = TRUE;
+
+ str.truncate(6);
+ double lag = str.toDouble(&ok);
+ if(ok == TRUE){
+ lag -= (lag*100.0 - int(lag*100.0))/100.0;
+ QString s_lag;
+ s_lag.sprintf("Lag: %.2f", lag);
+ this->lag->setText(s_lag);
+ }
+ else{
+ this->lag->setText(str);
+ }
+ }
+ break;
+ case RESET_NOTIF:
+ m_gotMsgWithoutFocus = 0;
+ break;
+ default:
+ kdDebug(5008) << "Unkown control message: " << str << endl;
+ }
+}
+
+void KSircTopLevel::setTopic( const QString &topic )
+{
+ m_topic = topic;
+ ksTopic->setText( topic );
+}
+
+void KSircTopLevel::toggleTimestamp()
+{
+ ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp = !ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp;
+ ksopts->save(KSOptions::Channels);
+ mainw->enableTimeStamps( ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp );
+ file->setItemChecked( tsitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].timeStamp );
+}
+
+void KSircTopLevel::toggleFilterJoinPart()
+{
+ ksopts->chan(m_channelInfo).filterJoinPart = !ksopts->chan(m_channelInfo).filterJoinPart;
+ ksopts->save(KSOptions::Channels);
+ file->setItemChecked( fjpitem, ksopts->chan(m_channelInfo).filterJoinPart );
+}
+
+QString KSircTopLevel::findNick(QString part, uint which)
+{
+ QStringList matches;
+
+ for (QStringList::ConstIterator it = completeNicks.begin();
+ it != completeNicks.end();
+ ++it)
+ {
+ if ((*it).left(part.length()).lower() == part.lower() && nicks->findNick(*it) >= 0) {
+ matches.append(*it);
+ }
+ }
+
+ for(uint i=0; i < nicks->count(); i++){
+ if (matches.contains(nicks->text(i)))
+ continue;
+ if(nicks->text(i).length() >= part.length()){
+ if(nicks->text(i).lower().startsWith( part.lower())){
+ if(nicks->text(i) != ksircProcess()->getNick() ){ // Don't match your own nick
+ matches.append(nicks->text(i));
+ }
+ }
+ }
+ }
+ if(matches.count() > 0){
+ if(which < matches.count())
+ return *(matches.at(which));
+ else
+ return QString::null;
+ }
+ return QString::null;
+
+}
+
+void KSircTopLevel::removeCompleteNick(const QString &nick)
+{
+ QStringList::Iterator it = completeNicks.find(nick);
+ if (it != completeNicks.end())
+ completeNicks.remove(it);
+}
+
+void KSircTopLevel::addCompleteNick(const QString &nick)
+{
+ removeCompleteNick(nick);
+ completeNicks.prepend(nick);
+}
+
+void KSircTopLevel::changeCompleteNick(const QString &oldNick, const QString &newNick)
+{
+ QStringList::Iterator it = completeNicks.find(oldNick);
+ if (it != completeNicks.end())
+ *it = newNick;
+}
+
+void KSircTopLevel::openQueryFromNick(const QString &nick)
+{
+ KSircChannel ci(m_channelInfo.server(), nick.lower());
+ emit open_toplevel(ci);
+}
+
+void KSircTopLevel::pasteToWindow()
+{
+ // Ctrl-V
+ //kdDebug(5008) << "Going to paste: " << KApplication::clipboard()->text( QClipboard::Clipboard ) << endl;
+ slotTextDropped(KApplication::clipboard()->text( QClipboard::Clipboard ) );
+}
+
+void KSircTopLevel::pasteToNickList(int button,
+ QListBoxItem *item,
+ const QPoint &)
+{
+ if(button == Qt::MidButton && item){
+ KSircChannel ci(m_channelInfo.server(), item->text().lower());
+ emit open_toplevel(ci);
+
+ QStringList lines = QStringList::split( '\n',
+ KApplication::clipboard()->text( QClipboard::Selection ),
+ true );
+ QStringList::ConstIterator it = lines.begin();
+ QStringList::ConstIterator end = lines.end();
+ for (; it != end; ++it )
+ {
+ if ((*it).isEmpty())
+ continue;
+
+ QString str =
+ QString("/msg ") + item->text().lower() + " " +
+ *it + "\n";
+ emit outputUnicodeLine(str);
+
+ }
+ }
+
+}
+
+void KSircTopLevel::dndTextToNickList(const QListBoxItem *item,
+ const QString& text)
+{
+ if(item){
+ KSircChannel ci(m_channelInfo.server(), item->text().lower());
+ emit open_toplevel(ci);
+
+ QStringList lines = QStringList::split( '\n',
+ text,
+ true );
+ QStringList::ConstIterator it = lines.begin();
+ QStringList::ConstIterator end = lines.end();
+ for (; it != end; ++it )
+ {
+ if ((*it).isEmpty())
+ continue;
+
+ QString str =
+ QString("/msg ") + item->text().lower() + " " +
+ *it + "\n";
+ emit outputUnicodeLine(str);
+
+ }
+ }
+
+}
+
+enum KSircTopLevelCommands{ Ask, Parse, Escape };
+void KSircTopLevel::slotTextDropped( const QString& _text )
+{
+ if (_text.isEmpty())
+ return;
+ QString text = linee->text();
+ int curPos = linee->cursorPosition();
+
+ text = text.mid( 0, curPos ) + _text + text.mid( curPos );
+
+ if (text[text.length()-1] != '\n')
+ text += "\n";
+ int lines = text.contains("\n");
+ int approx_lines = text.length() / 75;
+ if ( lines > approx_lines )
+ approx_lines = lines;
+ if (lines > 4) {
+ int result = KMessageBox::warningContinueCancel(this,
+ i18n("You are about to send %1 lines of text.\nDo you really want to send that much?").arg(approx_lines),
+ QString::null, i18n("Send"));
+ if (result != KMessageBox::Continue)
+ {
+// linee->setText("");
+ return;
+ }
+ }
+
+ tab_pressed = -1;
+ if(lines > 1){
+ linee->setUpdatesEnabled(FALSE);
+
+ QStringList lines = QStringList::split( '\n', text, true );
+ QStringList::ConstIterator it = lines.begin();
+ QStringList::ConstIterator end = lines.end();
+ KSircTopLevelCommands commands = Ask;
+ for (; it != end; ++it )
+ {
+ if ((*it).isEmpty())
+ continue;
+ QString line = *it;
+ if ( line[0].latin1() == '/' )
+ {
+ if ( commands == Ask )
+ switch ( KMessageBox::questionYesNo( this, i18n(
+ "The text you pasted contains lines that start with /.\n"
+ "Should they be interpreted as IRC commands?" ), QString::null, i18n("Interpret"), i18n("Do Not Interpret") ) )
+ {
+ case KMessageBox::Yes:
+ commands = Parse;
+ break;
+ case KMessageBox::No:
+ commands = Escape;
+ break;
+ }
+ if ( commands == Escape )
+ line.prepend( "/say " );
+ }
+ linee->setText( line );
+ sirc_line_return(line);
+ }
+
+ linee->setText("");
+ linee->setUpdatesEnabled(TRUE);
+ linee->update();
+ }
+ else{
+ text.replace(QRegExp("\n"), "");
+ linee->setText(text);
+ linee->setCursorPosition( curPos + _text.length() );
+ }
+}
+
+void KSircTopLevel::clearWindow()
+{
+ kdDebug(5008) << "Doing clear on main window" << endl;
+ mainw->clear();
+}
+
+void KSircTopLevel::lineeNotTab()
+{
+ tab_pressed = -1;
+ disconnect(linee, SIGNAL(notTab()),
+ this, SLOT(lineeNotTab()));
+ addCompleteNick(tab_nick);
+}
+
+void KSircTopLevel::toggleRootWindow()
+{
+}
+
+bool KSircTopLevel::event( QEvent *e)
+{
+ if(e->type() == QEvent::ApplicationPaletteChange ||
+ e->type() == QEvent::ApplicationFontChange)
+ {
+ QTimer::singleShot(750, this, SLOT(initColors()));
+ initColors();
+ }
+ return KMainWindow::event(e);
+}
+
+void KSircTopLevel::saveCurrLog()
+{
+ KURL url = KURL( KFileDialog::getSaveFileName(QString::null,
+ "*.log", 0L,
+ i18n("Save Chat/Query Logfile")));
+ if( url.isEmpty())
+ return;
+
+ KTempFile temp;
+ QTextStream *str = temp.textStream();
+
+ *str << mainw->plainText();
+
+ temp.close();
+#if KDE_IS_VERSION(3,1,92)
+ KIO::NetAccess::upload(temp.name(), url, this);
+#else
+ KIO::NetAccess::upload(temp.name(), url);
+#endif
+}
+
+
+void KSircTopLevel::slotDropURLs( const QStringList& urls )
+{
+ if ( !isPrivateChat() )
+ return;
+
+ slotDccURLs( urls, m_channelInfo.channel() );
+}
+
+// sends the list of urls to $dest_nick
+void KSircTopLevel::slotDccURLs( const QStringList& urls, const QString& nick )
+{
+ if ( urls.isEmpty() || nick.isEmpty() )
+ return;
+
+ QStringList::ConstIterator it = urls.begin();
+ // QString s("/eval &docommand(eval{\"dcc send " + nick + " %1\"});\n");
+ QString s("/dcc send " + nick + " %1\n");
+ for ( ; it != urls.end(); ++it ) {
+ QString file( *it );
+ kdDebug(5008) << "........ " << file << endl;
+ if ( !file.isEmpty() )
+ sirc_write(s.arg( file ));
+ }
+}
+
+bool KSircTopLevel::isPrivateChat() const
+{
+ return ((m_channelInfo.channel()[0] != '!') && (m_channelInfo.channel()[0] != '&') &&
+ (m_channelInfo.channel()[0] != '#'));
+}
+
+bool KSircTopLevel::isPublicChat() const
+{
+ return ((m_channelInfo.channel()[0] == '#') || (m_channelInfo.channel()[0] == '&'));
+}
+
+bool KSircTopLevel::isSpecialWindow() const
+{
+ return (m_channelInfo.channel()[0] == '!');
+}
+
+void KSircTopLevel::outputUnicodeLine( const QString &message )
+{
+ emit outputLine( encoder()->fromUnicode( message ) );
+}
+
+void KSircTopLevel::setTopicIntern( const QString &topic )
+{
+ QString command = QString::fromLatin1( "/topic %1 %2\n" ).arg( m_channelInfo.channel() ).arg( topic );
+ sirc_write( command );
+ linee->setFocus();
+}
+
+void KSircTopLevel::doChange(bool pers, QString text)
+{
+ QTime ctime = QTime::currentTime();
+ if (ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg) {
+ // beep only every 2 seconds otherwise it'll get very noisy in the room ;)
+ if ( QABS( lastBeep.secsTo( ctime ) ) > 1 ) {
+ if ( pers ) {
+ int winId = !ticker ? topLevelWidget()->winId() : ticker->winId();
+ KNotifyClient::event(winId,
+ QString::fromLatin1("ChannelPersonal"),
+ i18n("Your nick appeared on channel %1").arg(m_channelInfo.channel()));
+ } else {
+ int winId = !ticker ? topLevelWidget()->winId() : ticker->winId();
+ KNotifyClient::event(winId,
+ QString::fromLatin1("ChannelChanged"),
+ i18n("Channel %1 changed").arg(m_channelInfo.channel()));
+ }
+ lastBeep = ctime;
+ }
+ }
+
+ if(have_focus == 0 && pers == true && m_gotMsgWithoutFocus == false){
+ m_gotMsgWithoutFocus = true;
+ servercontroller::self()->increaseNotificationCount(QString("%1 -> %2").arg(ksircProcess()->serverID()).arg(m_channelInfo.channel()), text);
+ }
+}
+
+void KSircTopLevel::toggleBeep()
+{
+ ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg = !ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg;
+ ksopts->save(KSOptions::Channels);
+ file->setItemChecked(beepitem, ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].beepOnMsg);
+}
+
+void KSircTopLevel::toggleTopic()
+{
+ if(file->isItemChecked(topicitem)){
+ ksTopic->hide();
+// channelButtons->hide();
+ ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow = false;
+ }
+ else {
+ ksTopic->show();
+// channelButtons->show();
+ ksopts->channel[m_channelInfo.server()][m_channelInfo.channel()].topicShow = true;
+ }
+ file->setItemChecked(topicitem, !file->isItemChecked(topicitem));
+ ksopts->save(KSOptions::Channels);
+}
+
+void KSircTopLevel::toggleTicker()
+{
+ //kdDebug(5008) << "Got toggle ticker" << endl;
+ if(ticker){
+ show();
+ displayMgr->show(this);
+ linee->setFocus(); // Give focus back to the linee, someone takes it away on new create
+ tickerpoint = ticker->pos();
+ tickersize = ticker->size();
+ delete ticker;
+ ticker = 0x0;
+ }
+ else {
+ ticker = new KSTicker(0x0, m_channelInfo.channel().utf8() + "_ticker");
+ ticker->setCaption(m_channelInfo.channel());
+ if(tickerpoint.isNull() == false)
+ ticker->move(tickerpoint);
+ if(tickersize.isEmpty() == false)
+ ticker->resize(tickersize);
+ ticker->show();
+ displayMgr->hide(this);
+
+ connect(ticker, SIGNAL(doubleClick()), this, SLOT(toggleTicker()));
+ }
+}
+
+bool KSircTopLevel::atBottom()
+{
+ return mainw->verticalScrollBar()->maxValue() - mainw->verticalScrollBar()->value() < 20;
+}
+
+void KSircTopLevel::cmd_process(int id)
+{
+ if(cmd_menu.at(id) != cmd_menu.end()) {
+ QString item, arg, out;
+ item = cmd_menu[id].section('/', 1, 1);
+ arg = cmd_menu[id].section('/', 2, 2);
+
+ out = "/" + item;
+
+ if(arg == "*chan*")
+ out.append(" " + m_channelInfo.channel());
+
+ out.append(" ");
+
+ linee->insertAt(out, 0, 0);
+ linee->setCursorPosition(linee->cursorPosition() + out.length());
+ }
+}
+
+void KSircTopLevel::showDCCMgr()
+{
+ KSircMessageReceiver *kmr = ksircProcess()->mrList().find("!dcc");
+ if(kmr){
+ KSircIODCC *dcc = static_cast<KSircIODCC *>(kmr);
+ if(dcc){
+ dcc->showMgr();
+ }
+ }
+}
+
+#include "toplevel.moc"
+
+// vim: ts=2 sw=2 et
diff --git a/ksirc/toplevel.h b/ksirc/toplevel.h
new file mode 100644
index 00000000..ef8d4a57
--- /dev/null
+++ b/ksirc/toplevel.h
@@ -0,0 +1,535 @@
+#ifndef KSIRCTOPLEVEL_H
+#define KSIRCTOPLEVEL_H
+
+#include <qdatetime.h>
+#include <kmainwindow.h>
+
+#include "ahistlineedit.h"
+#include "messageReceiver.h"
+#include "chanButtons.h"
+#include "KSTicker/ksticker.h"
+#include "ksircchannel.h"
+
+#define KST_CHANNEL_ID 2
+
+class KSircTopic;
+class QSplitter;
+class KSircView;
+class aListBox;
+class UserControlMenu;
+class ChannelParser;
+class charSelector;
+class LogFile;
+class KSelectAction;
+class QLabel;
+class QVBox;
+class QListBoxItem;
+
+class QPopupMenu;
+
+class KSircTopLevel : public KMainWindow,
+ public UnicodeMessageReceiver
+{
+ Q_OBJECT
+ friend class ChannelParser;
+public:
+ /**
+ * Constructor, needs the controlling ksircprocess passed to it, so
+ * we can make the ksircmessage receiver happy.
+ */
+ KSircTopLevel(KSircProcess *_proc, const KSircChannel &channelInfo, const char *
+ name=0);
+ /**
+ * Destructor, destroys itself.
+ */
+ ~KSircTopLevel();
+
+ /**
+ * Reimplement show() to popup the menu bars and toolbar items
+ */
+ virtual void show();
+
+ /**
+ * Line recieved that should be printed on the screen. Unparsed, and
+ * ready processing. This is a single line, and it NOT \n
+ * terminated.
+ */
+ virtual void sirc_receive(QString str, bool broadcast = false);
+
+ /**
+ * Reimplement the ksircmessagereceiver control messages. These
+ * are parsed and dealt with quickly.
+ */
+ virtual void control_message(int, QString);
+
+ QWidget *lineEdit() const { return linee; }
+
+ void setTopic( const QString &topic );
+ QString topic() const { return m_topic; }
+
+ /**
+ * This returns the current channel information for a toplevel
+ */
+ const KSircChannel &channelInfo() const { return m_channelInfo; }
+
+signals:
+ /**
+ * signals thats the toplevel widget wishes to
+ * output a new line. The line is correctly
+ * linefeed terminated, etc.
+ */
+ void outputLine(QCString);
+ /**
+ * open a new toplevel widget with for the
+ * channel/user QString.
+ */
+ void open_toplevel(const KSircChannel &);
+ /**
+ * emittedon shutdown, indicating that
+ * this window is closing. Refrence to outselves
+ * is include.
+ */
+ void closing(KSircTopLevel *, QString channel);
+ /**
+ * emitted when the user typed /quit, will disconnect from this
+ * server and close all toplevels belonging to it.
+ */
+ void requestQuit( const QCString& command );
+ /**
+ * emitted when we change channel name
+ * on the fly. old is the old channel name, new
+ * is the new one.
+ */
+ void changeChannel(const QString &oldName, const QString &newName);
+ /**
+ * emitted to say that KSircTopLevel
+ * (this) is now the window with focus. Used by
+ * servercontroller to set the !default window.
+ */
+ void currentWindow(KSircTopLevel *);
+ /**
+ * Stop updating list item sizes, majour changes comming through
+ *
+ */
+ void freezeUpdates(bool);
+ /**
+ * Emitted when a new message appeared in the irc window
+ */
+ void changed(bool, QString);
+
+public slots:
+ /**
+ * When enter is pressed, we read the SLE and output the results
+ * after processing via emitting outputLine.
+ */
+ virtual void sirc_line_return(const QString &s);
+
+ /**
+ * Clears the message window
+ */
+ void clearWindow();
+
+ /**
+ * If the focus shifts this should get called with
+ * the widget that just got focus. Used in MDI modes
+ * where the toplevel doesn't get focus in/out events.
+ */
+ void focusChange(QWidget *w);
+
+protected slots:
+ /**
+ * When the rightMouse button is pressed in the nick list, popup
+ * the User popup menu at his cursor location.
+ */
+ void UserSelected(int index);
+ /**
+ * Menu items was selected, now read the UserControlMenu and
+ * reupdate the required menus.
+ */
+ void UserParseMenu(int id);
+ /**
+ * Page down accel key. Tells the mainw to scroll down 1 page.
+ */
+ void AccelScrollDownPage();
+ /**
+ * Page Up accell key. Tells the mainw to scroll down 1 page.
+ */
+ void AccelScrollUpPage();
+ /**
+ * Slot to termiate (close) the window.
+ */
+ void terminate() { close( true /*alsoDelete*/ ); }
+ /**
+ * Called when the user menu is finished and the popup menu needs
+ * to be updated.
+ */
+ void UserUpdateMenu();
+ /**
+ * Open the new channel/window selector.
+ */
+ void newWindow();
+ /**
+ * We've received focus, let's make us the default, and issue a
+ * /join such that we default to the right channel.
+ */
+ void gotFocus();
+ /**
+ * We've lost focus, set ourselves as loosing focus.
+ */
+ void lostFocus();
+ /**
+ * Create and popup the ticker. Make sure to restore it to the
+ * last position is was at.
+ */
+// void showTicker();
+ /**
+ * toggle the timestamp from the channel window menu (and with keyaccel)
+ */
+ void toggleTimestamp();
+ /**
+ * toggle the filtering of join/part message
+ */
+ void toggleFilterJoinPart();
+
+ /**
+ * toggle the beep from the channel window menu (and with keyaccel)
+ */
+ void toggleBeep();
+
+ /**
+ * show the dcc manager
+ */
+ void showDCCMgr();
+
+ /**
+ * Delete the ticker and ppoup the main window
+ */
+// void unHide();
+ /**
+ * On a TAB key press we call TabNickCompletion which
+ * reads the last thing in linee matches it with a nick and
+ * puts it back into the line.
+ */
+ void TabNickCompletionShift();
+ /**
+ * On a TAB key press we call TabNickCompletion which
+ * reads the last thing in linee matches it with a nick and
+ * puts it back into the line.
+ */
+ void TabNickCompletionNormal();
+
+ /**
+ * Signals a Line Change in linee
+ */
+ void lineeNotTab();
+ /**
+ * Move the display to or from the root window
+ *
+ */
+ void toggleRootWindow();
+ /**
+ * On a middle mouse button press we call pasteToWindow which
+ * reads the clip board and pastes into the main listbox.
+ */
+ void pasteToWindow();
+ /**
+ * On a midle mouse button press to the nick list we open
+ * a query window and paste the text there
+ */
+ void pasteToNickList(int button, QListBoxItem *item, const QPoint &pos);
+ /**
+ * dnd paste to nick list
+ */
+ void dndTextToNickList(const QListBoxItem *itom, const QString& text);
+ /**
+ * open a toplevel on double click
+ */
+ void openQueryFromNick(const QString &);
+
+ /**
+ * Some text was dropped on the listbox
+ */
+ void slotTextDropped(const QString&);
+
+ /**
+ * Calls slotDccURLs with the current nick we're talking to
+ */
+ void slotDropURLs(const QStringList& urls);
+
+ /**
+ * Sends the list of urls to nick
+ */
+ void slotDccURLs(const QStringList& urls, const QString& nick );
+
+ /**
+ * Re-apply color settings.
+ */
+ void initColors();
+
+ /**
+ * Dumps current content of mainw into a logfile
+ */
+ void saveCurrLog();
+
+ /**
+ * Makes a beep when a change happens (if the user has this on)
+ * the bool is TRUE if changed line contained user's nick
+ */
+ void doChange(bool, QString);
+
+ /**
+ * show the ticker window
+ */
+ void toggleTicker();
+
+ /**
+ * ksirc command menu clicked on
+ */
+ void cmd_process(int id);
+
+protected:
+ /**
+ * On a TAB key press we call TabNickCompletion which
+ * reads the last thing in linee matches it with a nick and
+ * puts it back into the line.
+ */
+ void TabNickCompletion(int dir);
+
+ /**
+ * Make sure to update colors correctly.
+ */
+ virtual bool event( QEvent *e);
+
+ /**
+ * Redfine closeEvent to emit closing and delete itself.
+ */
+ virtual void closeEvent(QCloseEvent *);
+ /**
+ * Searches through the nick list and finds the nick with the best match.
+ * which, arg2, selects nicks other than the first shouldarg1 match more
+ * than 1 nick.
+ */
+ virtual QString findNick(QString, uint which = 0);
+ /**
+ * remove a nick from the prefered in nick completion list
+ */
+ virtual void removeCompleteNick(const QString &);
+ /**
+ * Adds a nick to the list of nicks to prefer in nick completion
+ * or moves it to the top of the list if it was already there
+ */
+ virtual void addCompleteNick(const QString &);
+ /**
+ * Changes a nick in the completion list if it has been listed,
+ * otherwise does nothing
+ */
+ virtual void changeCompleteNick(const QString &, const QString &);
+
+ virtual void setupCommandMenu();
+
+ /**
+ * Returns true if this window is a private conversation (query or dcc chat)
+ *, instead of a public chat in a channel or a special window.
+ */
+ bool isPrivateChat() const;
+
+ /**
+ * Returns true if this is a public channel window
+ */
+ bool isPublicChat() const;
+
+ /**
+ * Returns true if this is a special window
+ */
+ bool isSpecialWindow() const;
+
+private slots:
+ void setTopicIntern( const QString &topic );
+ void insertText();
+ void setMode(QString, int, QString currentNick=0);
+ void setEncoding();
+ void returnPressed();
+ void toggleTopic();
+
+private:
+
+ void outputUnicodeLine( const QString &message );
+
+ bool atBottom();
+
+ bool continued_line;
+ charSelector *selector;
+ chanButtons *channelButtons;
+ QSplitter *pan;
+ QVBox *nicks_box;
+ QVBox *top;
+ KMenuBar *kmenu;
+ QLabel *lag;
+ enum {PING = 10, TOPIC = 20};
+ KSircView *mainw;
+ aHistLineEdit *linee;
+ aListBox *nicks;
+
+ //QString m_currentNick;
+
+ //KSircProcess *proc;
+
+ int lines;
+
+ struct BufferedLine
+ {
+ BufferedLine() { wasBroadcast = false; }
+ BufferedLine( const QString &msg, bool broadc )
+ { message = msg; wasBroadcast = broadc; }
+ bool operator==( const BufferedLine &other )
+ { return message == other.message &&
+ wasBroadcast == other.wasBroadcast; }
+
+ QString message;
+ bool wasBroadcast;
+ };
+
+ bool Buffer;
+ QValueList<BufferedLine> LineBuffer;
+
+ // QPopupMenu's used for the menubar
+ QPopupMenu *file, *edit, *command;
+
+ bool parse_input(const QString &string, QString &plainText);
+ void sirc_write(const QString &str);
+
+ QPopupMenu *user_controls;
+ static QPtrList<UserControlMenu> *user_menu;
+ int opami;
+
+ QAccel *accel;
+
+ /**
+ * The channel name that we belong too.
+ */
+ //QString channel_name;
+
+ /**
+ * Caption at the top of the window.
+ */
+ QString caption;
+ /**
+ * Current topic for the channel
+ */
+ QString m_topic;
+
+ /**
+ * Does the window have focus? 1 when yes, 0 when no.
+ */
+ int have_focus;
+
+ /**
+ * Number of time tab has been pressed. Each time it's pressed
+ * roll through the list of matching nicks.
+ */
+ int tab_pressed;
+ /**
+ * When tabs pressed save the line for use at a latter date.
+ * tab_nick holds the last nick found so when a blank tab is pressed
+ * we pop the last niick and ": " up.
+ */
+ QString tab_saved, tab_nick;
+ int tab_start, tab_end;
+
+ // Ticker specific variables.
+
+ /**
+ * Main pointer to ksticker. Caution, it doesn't always exist!
+ * Set to 0 ifwhen you delete it.
+ */
+ KSTicker *ticker;
+ /**
+ * Size and position of the main window. Main window is returns this
+ * this position when it reappears.
+ */
+ QRect myrect;
+ /**
+ * Position of the main window
+ */
+ QPoint mypoint;
+ /**
+ * Ticker's size and geometry
+ */
+ QRect tickerrect;
+ /**
+ * Tickers position
+ */
+ QPoint tickerpoint;
+ QSize tickersize;
+ /**
+ * Do we have a kick window open?
+ * Remember not to open 2
+ */
+ bool KickWinOpen;
+
+ /**
+ * Hold an internal parser object which does all our parsing for us.
+ * Since parsing an intergral part of TopLevel, it's also a friend
+ * of ours
+ */
+ ChannelParser *ChanParser;
+
+ /**
+ * QSize maintains size information in case it changes somehow
+ */
+ QSize current_size;
+
+ /**
+ * ID of the timestamp menu item, to use in (un)checking it from slot
+ */
+ int tsitem;
+
+ /**
+ * ID of the filter join/part menu item, to use in (un)checking it from slot
+ */
+ int fjpitem;
+
+ /**
+ * ID of the beep menu item, to use in (un)checking it from slot
+ */
+ int beepitem;
+
+ /**
+ * ID of the toggle topic menu item, to use in (un)checking it from slot
+ */
+ int topicitem;
+
+ /**
+ * ID of the show/hide ticker menu item
+ */
+ int tickeritem;
+
+ /**
+ * List of nicks already used in nick completion to give them
+ * higher priority
+ */
+ QStringList completeNicks;
+
+ /**
+ * The time of the last beep to prevent your soundcard from detonating
+ * in high traffic channels
+ */
+ QTime lastBeep;
+
+ KSircTopic *ksTopic;
+
+ LogFile *logFile;
+
+ KSelectAction *encodingAction;
+
+ static QStringList cmd_menu;
+
+ bool m_gotMsgWithoutFocus;
+
+ KSircChannel m_channelInfo;
+
+};
+
+#endif
+
+// vim: ts=2 sw=2 et
diff --git a/ksirc/usercontrolmenu.cpp b/ksirc/usercontrolmenu.cpp
new file mode 100644
index 00000000..ca35e094
--- /dev/null
+++ b/ksirc/usercontrolmenu.cpp
@@ -0,0 +1,167 @@
+#include "usercontrolmenu.h"
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+QPtrList<UserControlMenu> UserControlMenu::UserMenu;
+
+UserControlMenu::UserControlMenu(const QString& _title,
+ const QString& _action,
+ int _accel,
+ int _type,
+ bool _op_only)
+{
+ title = _title;
+ action = _action;
+ accel = _accel;
+ type = (itype) _type;
+ op_only = _op_only;
+}
+
+UserControlMenu::~UserControlMenu()
+{
+}
+
+QPtrList<UserControlMenu> *UserControlMenu::parseKConfig()
+{
+
+ // Reset the UserMenu to nothing before we start.
+
+ UserMenu.clear();
+
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("UserMenu");
+ int items = kConfig->readNumEntry("Number");
+ if(items == 0){
+
+ // We found nothing, so let's use some defaults.
+
+ UserMenu.setAutoDelete(TRUE);
+ UserMenu.append(new UserControlMenu(i18n("&Refresh Nicks"),
+ "refresh",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu);
+ UserMenu.append(new UserControlMenu(i18n("&Follow"),
+ "follow $$dest_nick",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu(i18n("&UnFollow"),
+ "unfollow $$dest_nick",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu); // Defaults to a separator
+ UserMenu.append(new UserControlMenu(i18n("&Whois"),
+ "/whois $$dest_nick",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu(i18n("&Ping"),
+ "/ping $$dest_nick",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu(i18n("V&ersion"),
+ "/ctcp $$dest_nick VERSION",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu); // Defaults to a separator
+ UserMenu.append(new UserControlMenu(i18n("&Abuse"),
+ "/me slaps $$dest_nick around with a small 50lb Unix Manual",
+ 0, UserControlMenu::Text));
+ UserMenu.append(new UserControlMenu); // Defaults to a separator
+ UserMenu.append(new UserControlMenu(i18n("&Kick"),
+ "/kick $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu(i18n("&Ban"),
+ "/ban $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu(i18n("U&nBan"),
+ "/unban $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu());
+ UserMenu.append(new UserControlMenu(i18n("&Op"),
+ "/op $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu(i18n("&Deop"),
+ "/deop $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu());
+ UserMenu.append(new UserControlMenu(i18n("&Voice"),
+ "/mode $$dest_chan +v $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ UserMenu.append(new UserControlMenu(i18n("Devo&ice"),
+ "/mode $$dest_chan -v $$dest_nick",
+ 0,
+ UserControlMenu::Text,
+ TRUE));
+ }
+ else{
+ QString key, cindex, title, action;
+ int accel, type, oponly;
+ for(int i = 0; i < items; i++){
+ cindex.sprintf("%d", i);
+ key = "MenuType-" + cindex;
+ type = kConfig->readNumEntry(key);
+ if(type == UserControlMenu::Seperator)
+ UserMenu.append(new UserControlMenu());
+ else if(type == UserControlMenu::Text){
+ key = "MenuTitle-" + cindex;
+ title = kConfig->readEntry(key);
+ key = "MenuAction-" + cindex;
+ action = kConfig->readEntry(key);
+ key = "MenuAccel-" + cindex;
+ accel = kConfig->readNumEntry(key);
+ key = "MenuOpOnly-" + cindex;
+ oponly = kConfig->readNumEntry(key);
+
+ UserMenu.append(new UserControlMenu(title, action, accel, type, (bool) oponly));
+ }
+ }
+ }
+
+ return &UserMenu;
+
+}
+
+void UserControlMenu::writeKConfig()
+{
+ KConfig *kConfig = kapp->config();
+ kConfig->setGroup("UserMenu");
+
+ int items = (int) UserMenu.count();
+
+ kConfig->writeEntry("Number", items);
+
+ QString key;
+ QString cindex;
+ UserControlMenu *ucm;
+ int type;
+
+ for(int i = 0; i < items; i++){
+ ucm = UserMenu.at(i);
+ cindex.sprintf("%d", i);
+ key = "MenuType-" + cindex;
+ type = ucm->type;
+ kConfig->writeEntry(key, (int) type);
+ // Do nothing for a seperator since it defaults across
+ if(type == UserControlMenu::Text){
+ key = "MenuTitle-" + cindex;
+ kConfig->writeEntry(key, ucm->title);
+ key = "MenuAction-" + cindex;
+ kConfig->writeEntry(key, ucm->action);
+ key = "MenuAccel-" + cindex;
+ kConfig->writeEntry(key, (int) ucm->accel);
+ key = "MenuOpOnly-" + cindex;
+ kConfig->writeEntry(key, (int) ucm->op_only);
+ }
+ }
+ kConfig->sync();
+
+}
+
diff --git a/ksirc/usercontrolmenu.h b/ksirc/usercontrolmenu.h
new file mode 100644
index 00000000..1f7a983a
--- /dev/null
+++ b/ksirc/usercontrolmenu.h
@@ -0,0 +1,52 @@
+#ifndef USERCONTROLLERMENU_H
+#define USERCONTROLLERMENU_H
+
+#include <qstring.h>
+#include <qptrlist.h>
+
+class UserControlMenu {
+ public:
+ /**
+ Basic constructor, everything defaults to a Seperator. All data
+ is public so there is no problems changing it in the future.
+ Data is copied internally and deleted when finished.
+ */
+ UserControlMenu(const QString& _title = 0,
+ const QString& _action = 0,
+ int _accel = 0,
+ int _type = 0,
+ bool _op_only = FALSE);
+
+ ~UserControlMenu();
+
+ static QPtrList<UserControlMenu> UserMenu;
+
+ static QPtrList<UserControlMenu> *parseKConfig();
+ static void writeKConfig();
+
+ /**
+ * title in the popup menu
+ */
+ QString title;
+ /**
+ * Action to preform, must be of the format "blah lbah %s blah"
+ * where %s will be expanded into the selected nick.
+ */
+ QString action;
+
+ /**
+ * Accelerator key, currently does nothing.
+ */
+ int accel;
+ /**
+ * is this function only available to ops?
+ */
+ bool op_only;
+ /**
+ * What type of menu item is this? a Seperator of Text?
+ */
+ enum itype { Seperator, Text } type;
+};
+
+
+#endif
diff --git a/ksirc/version.h b/ksirc/version.h
new file mode 100644
index 00000000..7f49130c
--- /dev/null
+++ b/ksirc/version.h
@@ -0,0 +1,6 @@
+#ifndef KSIRC_VERSION_H
+#define KSIRC_VERSION_H
+
+#define KSIRC_VERSION "1.3.12"
+
+#endif
diff --git a/ktalkd/ChangeLog b/ktalkd/ChangeLog
new file mode 100644
index 00000000..c2e26b51
--- /dev/null
+++ b/ktalkd/ChangeLog
@@ -0,0 +1,532 @@
+2002-12-03 David Faure <faure@kde.org>
+
+ * *: Merged with netkit-ntalk-0.17-pre20000412/talkd, including:
+ - many security improvements (checks for bad things received in the packets etc.)
+ - protocol-related checks and fixes
+ This also brings the code a bit closer to the initial talkd again,
+ for easier maintainance.
+ * kotalkd: Got rid of it (the same daemon can handle both).
+
+2000-12-14 Danny Tholen <danny@mailmij.org>
+
+ * announce.cpp: Improvement for error cases. If the announce program
+ can't open the display, or doesn't exist, or doesn't behave properly,
+ fallback on text announce. For this, the pipe is made non-blocking and
+ the exit code of the announce program is checked. It also means we don't
+ wait forever on the announce program if it doesn't send the expected '#'.
+
+2000-03-04 David Faure <faure@kde.org>
+
+ * talkd.cpp, ktalkdrc: Some fixes for KDE2 (KInstance, kvt->konsole...
+
+2000-03-04 David Smith <dsmith@algonet.se>
+
+ * kcmktalkd/* : Ported to KDE2 kcontrol architecture
+
+2000-01-26 David Faure <faure@kde.org>
+
+ * readcfg++.cpp, announce.cpp : remove HAVE_FUNC_SETENV tests and
+ putenv(), since ktalkd can now use kdecore's fake for setenv if setenv
+ is not available. Thanks to Stefan.Becker@nokia.com.
+
+2000-01-18 David Faure <faure@kde.org>
+
+ * unixsock.cpp : Remove unix socket even if ktalk isn't running
+ Thanks to Daniël Mantione <daniel@deadlock.et.tudelft.nl> for reporting & testing
+
+1999-05-09 David Faure <faure@kde.org> (1.5.2)
+
+ * find_user.cpp : Check for ':' in ut_host, otherwise it's not a display.
+ Thanks to Manuel Cepedello Boiso <manuel@cauchy.fie.us.es> for reporting & testing
+
+1999-02-27 David Faure <faure@kde.org>
+
+ * announce.cpp, ktalkdlg.cpp : Use standard output instead of stderr,
+ for ktalkdlg->ktalkd communication.
+ Solves problem when Qt/KDE prints a warning before the '#'.
+ Thanks to Andrew Standley-Jones for his help (on irc).
+
+1999-02-03 David Faure <faure@kde.org> (1.5.1)
+
+ * readcfg++.cpp : Fixed answmach message stopping at first empty line
+
+1999-02-02 David Faure <faure@kde.org>
+
+ * kotalkd/vsnprintf.c, SNPRINTF_MISSING : new files for systems
+ without snprintf (e.g. Solaris). vsnprintf.c is _not_ to be
+ compiled on systems with a snprintf implementation.
+
+ * *lsm: Upgraded version number to 1.5.1 (!) in order to be above
+ the old talkd-1.4.4 that I released two years ago and that people
+ keep sending me bug reports about !
+
+1998-12-02 David Faure <faure@kde.org> (0.9.1)
+
+ * ktalkd/readc*.cpp: Fixed strncpy length arg for NEU_forwardmethod
+
+1998-11-15 David Faure <faure@kde.org>
+
+ * ktalkd/readc*.cpp, ktalkd/options.*, ktalkd/process.cpp: Added
+ NEU forward method, for multi-networked hosts with forwards
+
+ * ktalkd/includ.h: Improved used of <paths.h> and default settings
+
+ * mail.local/mail.local.c: Used HAVE_VSNPRINTF instead of
+ hardcoded list of OSes.
+
+1998-11-11 David Faure <faure@kde.org> (0.9.0)
+
+ * ktalkd/talkconn.cpp: Moved static init to TalkConnection::init()
+ Added reply address detection for multiple networked hosts.
+ Thanks to Burkhard Lehner for the method getReplyAddr().
+
+ * acinclude.m4.in (non-KDE version) : Used /usr/local as
+ default_prefix for non-KDE systems.
+
+ * ktalkd/readconf.cpp: Fixed read_user_config (bug with ForwardMethod)
+ Speeds up configuration file parsing.
+
+1998-11-03 David Faure <faure@kde.org>
+
+ * ktalkd/process.cpp: Fixed a bug, when user config file doesn't
+ exist. Reported by valankar@bigfoot.com.
+
+1998-10-02 David Faure <faure@kde.org> (0.8.9)
+
+ * mail.local/mail.local.c: Started using config.h
+
+1998-09-29 David Faure <faure@kde.org>
+
+ * ktalkd/unixsock.cpp: Support for multiple-display ktalk.
+
+1998-09-13 David Faure <faure@kde.org>
+
+ * ktalkd/find_user.cpp: Converted the get_display function to use
+ ifstream instead of read() and realloc()...
+
+ * ktalkd/announce.cpp: Do setuid from the beginning of announce().
+ Solves socket permissions for sendToKtalk and speeds up X announce.
+
+ * ktalkd/readcfg++.cpp: Handles two user config files :
+ ktalkdrc & ktalkannouncerc
+
+ * ktalkd/read*: Removed *kdebindir from readconf.h and readconf.cpp.
+ Internal to readcfg++.cpp.
+
+ * ktalkd/options.*: Now a structure instead of a class.
+
+1998-09-07 David Faure <faure@kde.org>
+
+ * ktalkd/announce.cpp: No text announce on a xterm if X announce was ok
+
+1998-09-03 David Faure <faure@kde.org> (0.8.8)
+
+ * ktalkd/unixsock.cpp: Added chmod => ktalk can write to the socket.
+
+1998-09-02 David Faure <faure@kde.org>
+
+ * ktalkd/unixsock.cpp, .h: New. Direct communication with ktalk.
+ * ktalkd/announce.cpp: Call sendToKtalk.
+
+1998-08-29 David Faure <faure@kde.org>
+
+ * ktalkd/options.cpp: Default values moved from .h to .cpp.
+ Makes -ansi happier.
+
+1998-08-23 David Faure <faure@kde.org>
+
+ * ktalkd/process.cpp (process_request): print_response enabled again.
+
+ * ktalkd/find_user.cpp: Removed the 'break;' for xdm, and didn't
+ override tty. This way, you can have both announcements (text & X).
+
+1998-08-19 David Faure <faure@kde.org> (0.8.7)
+
+ * ktalkd/mail.local: mail.local is back in the distrib. Has been
+ forgotten since 0.7.0 !! I added a README.mail.local to explain
+ its purpose.
+
+ * ktalkd/announce.cpp: Added text announce in addition to X announce.
+
+ * ktalkd/process.cpp: Removed check for the family field of addr. Oops.
+
+ * doc/en/*, *: Updated my email address from
+ <david.faure@insa-lyon.fr> to <faure@kde.org>
+
+1998-08-15 David Faure <faure@kde.org> (0.8.6)
+
+ * ktalkd/machines/talkconn.cpp: Added check for remote protocol.
+ This means that it is now possible to forward to an otalk machine ...
+
+ * ktalk/machines/forwmach.cpp: ... and/or from an otalk machine.
+
+ The first one who sends me an email after testing both, wins :)
+
+ * ktalkd/machines/check_protocol.cpp: Removed. Integrated into
+ talkconn.cpp. Thanks to Burkhard Lehner for the example code.
+
+ * ktalk/machines/answmach.cpp: Added a sleep(1) for not logged/NEU.
+
+ * ktalkd/print.cpp: Used c++ overriding to name "message()" the
+ former message_s() et message2().
+
+1998-08-12 David Faure <faure@kde.org> (0.8.5)
+
+ * ktalkd/find_user.cpp: A nasty bug with unsigned int fixed.
+ Thanks to Rolf Offermanns who found it.
+ Uncommented the use of ut_host for PTYs (ex : xterms).
+ Exit the loop if XDM login found (highest priority).
+
+ * kotalkd/includ.h: Added a simpler version of includ.h.
+
+1998-08-10 David Faure <faure@kde.org> (0.8.4)
+
+ * ktalkd/machines/talkconn.cpp: Support for otalk. (not finished).
+
+ * kotalkd/*.c: Just send to ktalkd, which responds itself.
+
+ * ktalkd/process.cpp: Handle otalk packets (with vers=0)
+
+1998-08-09 David Faure <faure@kde.org>
+
+ * kotalkd/kotalkd.c: Dies if ktalk protocol detection (-> ntalk detected).
+
+ * ktalkd/machines/forwmach.cpp: Now forwards DELETEs too. (cf. sig_handler).
+
+ * ktalkd/machines/forwmach.cpp: Final cleanup improved. (for forwmachines).
+
+ * ktalkd/options.cpp: Created, to hold systemwide options.
+
+ * ktalkd/*.c: Converted to c++ all c files.
+
+1998-08-07 David Faure <faure@kde.org> (0.8.3)
+
+ * ktalkd/doc/en/Makefile.am: index.html -> ktalkd.html
+
+1998-08-07 David Faure <faure@kde.org> (0.8.2)
+
+ * ../acincktalk.m4: Bug fix for the bug fix. Linux detection ok.
+
+1998-08-02 David Faure <faure@kde.org>
+
+ * doc/en/ktalkd.sgml: Converted all documentation to sgml. Phew.
+
+ * kcmktalkd/forwmachpage.cpp: i18n'ed the explanation for forwards
+
+1998-07-31 David Faure <faure@kde.org>
+
+ * ../acincktalk.m4: More output printed out and a bug fix
+
+1998-07-30 David Faure <faure@kde.org> (0.8.1)
+
+ * kotalkd/*.c: New way to support otalk protocol : forward everything
+ to local ntalk daemon (possibly ktalkd, but any other should work too)
+
+ * ktalkd/machines/talkconn.cpp (listen): Use SOMAXCONN as arg to listen
+
+1998-07-27 David Faure <faure@kde.org> (0.8.0)
+
+ * kotalkd/: Created to support otalk protocol. No new source
+ files. Everything is links in it, except Makefile.am and all
+ generated files.
+
+1998-07-26 David Faure <faure@kde.org>
+
+ * ktalkd/threads.c: Created to manage children processes (register, wait, ...)
+ No more zombie processes waiting 1mn30s to be acknowledged. :)
+
+ * ktalkd/machines/forwmach.*: FWT. Lots of bug fixed. Fully tested now.
+
+ * ktalkd/machines/talkconn.*: Bug fixing.
+
+1998-07-24 David Faure <faure@kde.org>
+
+ * ktalkd/machines/forwmach.*: Created the Forwarding machine. FWA. FWR.
+
+ * kcmktalkd/*: Added the 'forward' configuration page.
+
+ * ktalkd/*: Reverted most of the patch from Enrico Scholz.
+ The forwarding machine is now used for NEU if NEUBehaviour=1.
+
+1998-07-18 David Faure <faure@kde.org>
+
+ * ktalkd/readcfg++.h: Removed. Now in readconf.h
+
+1998-07-15 David Faure <faure@kde.org> (0.7.0)
+
+ * ktalkd/machines/talkconn.cpp (set_edit_chars): At last ! Fixed the
+ bug in answmach banners, which first appeared 8 months ago,
+ erasing half of some lines !
+
+ * ktalkd/table.c, ktalkd/print.c: Improved logs.
+
+ * ktalkd/process.c: Bug fixed : insert_table called even for NEU.
+
+ * ktalkd/machines/*: Converted the answering machine to C++.
+ Split into 3 classes.
+ TalkConnection : Handles the protocol.
+ TalkMachine : Generic talk machine.
+ AnswMachine : Answering machine. Inherits from TalkMachine.
+
+1998-07-08 David Faure <faure@kde.org> (0.6.2)
+
+ * ktalkd/*, ktalkdlg.cpp: Applied patch for NEUBehaviour=1
+ by Enrico Scholz <enrico.scholz@wirtschaft.tu-chemnitz.de>
+
+ * answmach/init_disp.c: Handle VWERASE if not defined (for AIX)
+
+1998-07-06 David Faure <faure@kde.org>
+
+ * ktalkd/announce.c: Small bug fix in text announcement (remotename).
+
+1998-06-15 David Faure <faure@kde.org>
+
+ * ktalkd/find_user.c: Added blank after display, needed by announce.c
+
+ * kcmktalkd/answmachpage.cpp: Override help() to display ktalkd's help.
+
+1998-06-13 David Faure <faure@kde.org> (0.6.1)
+
+ * includ.h, talkd.h: took talkd.h from ktalk. ktalkd doesn't use the
+ system one anymore.
+
+ * answmach/look_up.c: use sockaddr instead of osockaddr
+
+ * acincktalk.m4, configure.in.1: removed the check for osockaddr
+
+ * ktalkd/talkd.c: Use sys/params.h where available, for hostname length
+
+1998-06-11 David Faure <faure@kde.org> (0.6.0)
+
+ * ktalkd/: Reorganised the directory structure, to ship ktalkd
+ with ktalkdlg and kcmktalkd in a single package.
+
+ * talkd.c: Caller's hostname was limited to 32 ! Now 256.
+
+ * Makefile.am: rewrote some of them, for non-KDE users.
+
+1998-06-09 David Faure <faure@kde.org> (0.5.7)
+
+ * kcmktalkd: First release of the configuration dialog
+
+1998-05-16 David Faure <faure@kde.org> (0.5.6)
+
+ * announce.c: Announces with ktalkdlg on ALL displays where the
+ user is found. It works !
+
+ * readcfg++.cpp (init_user_config):
+ Added setenv("HOME",...) because the kdelibs rely on that to find
+ the user config file.
+
+1998-05-15 David Faure <faure@kde.org> (0.5.5)
+
+ * doc/: Made HTML documentation. Phew. Done.
+
+ * readcfg++.cpp (init_user_config):
+ Removed the un-necessary looking in pwd file.
+ KDElibs do that for us :)
+
+ * find_user.c:
+ Disabled X processes scanning if uid < 10. (Security hole)
+ Corrected a bug (S_ISCHR() is now "& 020") which didn't let you talk
+ to tty2 if tty1 was "mesg n".
+
+ * announce.c: open user config file before calling announce_proc, so
+ that it *is* closed now. It wasn't because of 'return'.
+
+
+1998-05-06 David Faure <faure@kde.org>
+
+ * announce.c: now the non-KDE sound works with and without option.
+ * acinclude.m4.in - the ktalkd one : some more corrections for
+ working without X.
+
+1998-04-28 David Faure <faure@kde.org> (0.5.4)
+
+ * acinclude.m4.in - the one from ktalkd package, not the kdenonbeta one
+ rewrote X detection, so that it works also without X :)
+
+1998-04-20 David Faure <faure@kde.org> (0.5.3)
+
+ * Makefile.am: Removed -lkdeui, not needed.
+ * io.c: Corrected an awful bug
+ (NEUBanner displayed instead of OPTinvitelines)
+ * readcfg++.cpp: Added missing "/" before 'ktalkdrc'
+
+1998-04-19 David Faure <faure@kde.org> (0.5.2)
+
+ * readcfg++.cpp: added check for user config file.
+ (was created with root permission otherwise)
+ * acinclude.m4.in - the ktalkd one: updated
+
+1998-04-15 David Faure <faure@kde.org> (0.5.1)
+
+ ktalkd doesn't use anymore kdedir() because it's protected.
+ It sets now $KDEBINDIR, for portable ktalkdrc files. KDEBINDIR
+ is determined by a call to the kde_bindir() function.
+ Makefile doesn't define TALKD_CONF anymore for compilation :
+ ktalkd now opens ktalkdrc from KApplication::kde_configdir().
+
+1998-03-26 David Faure <faure@kde.org> (0.5.0)
+ ktalkdrc: Extprg is now $KDEDIR/bin/ktalkdlg.
+ KDEDIR is now set when reading global configuration file.
+ Default value for Extprg includes $KDEDIR.
+
+1998-03-25 David Faure <faure@kde.org> (0.4.8)
+ KDEDIR is now set by ktalkd, not by ktalkdlg.
+ ktalkdrc*:removed path in front of sound files. ktalkdlg now finds them
+ in kde_sounddir().
+ Added option ExtPrg. Set to ktalkdlg (default) or ktalk.
+
+1998-03-14 David Faure <faure@kde.org> (0.4.7)
+ Made 2 packages out of ktalkd : ktalkd and ktalkdlg (new name for
+ atdlg), so that Burkhard Lehner <b_lehner@informatik.uni-kl.de>
+ can improve it to communicate with ktalk.
+
+1998-03-13 David Faure <faure@kde.org> (0.4.6)
+ Changed S_MESSG size. (Too little for mail first line, if NEU)
+ Used mkstemp instead of popen for the message left to the
+ answering machine. Added option EmptyMail, to avoid getting
+ empty mails.
+
+1998-03-10 David Faure <faure@kde.org> (0.4.5)
+ Renamed debug to debug_mode (debug exists in qt)
+ Updated configure.in and acinclude.m4 to match CVS ones.
+ Same for ltconfig, ltmain.sh, ...
+ Added new translations (es, it) and changed po structure.
+ Added NEUBanner* options to ktalkdrc.
+
+1998-02-27 D.F. (thanks to Juraj Bednar <bednar@isternet.sk>) (0.4.4)
+ Removed ktalkd-0.4.x/protocols from the distribution. Caused a
+ bug when compiling.
+
+1998-02-15 D.F. (thanks to B. Lehner<b_lehner@informatik.uni-kl.de>) (0.4.3)
+ added checks for paths.h and protocols/talkd.h (for Solaris)
+ Added default paths (/dev/ and /var/run/utmp) if paths.h not found
+ Included talkd.h in the distribution, for Solaris which doesn't have it
+ Changed AC_CHECK_OSOCKADDR to use this file if protocols/talkd.h absent
+
+1998-02-04 David Faure <faure@kde.org> (0.4.2)
+ Added user option Answmach.
+ Moved the daemon to $KDEDIR/bin. One must now change inetd.conf
+ (this way, rpms won't conflict with standard ones, and the old
+ in.ntalkd will remain available). Should I deviate 'talk' protocol
+ too (in addition to 'ntalk') ?
+ Made installation NOT overwrite actual sitewide config file.
+ Merged and updated READMEs.
+
+1998-02-03 David Faure <faure@kde.org> (0.4.1)
+ Added -rpath option, as it's necessary for ktalkd if $KDEDIR/lib is
+ not set in ld.so.conf (LD_LIBRARY_PATH not read by a daemon)
+
+1998-02-03 David Faure <faure@kde.org> (0.4.0)
+ Corrected bugs related to new acinclude.m4 : compiling without X
+ and without KDE is possible again.
+ Added memcpy for structs.
+ Made atdlg re-write ktalkdrc for user if necessary.
+
+1998-01-29 David Faure <faure@kde.org> (0.3.4)
+ Removed answinfo var., added return val to announce and process_request.
+ Non existent user (NEU) support : either launch answmach or do nothing.
+ (set it in systemwide ktalkdrc). Don't sleep() before answering if NEU
+ or not logged.
+
+1998-01-26 David Faure <faure@kde.org> (0.3.3)
+ Deutsch translation added by J. Mertin <smurphy@stardust.phantasia.org>
+ Enabled atdlg without sound, following option set.
+ Removed nasty \r\n, not needed.
+ Made banner arrive 16 chars at a time, not the whole at once !
+
+1998-01-25 David Faure <faure@kde.org> (0.3.2)
+ Changed default configuration : ktalkd.wav will be installed
+ in $KDEDIR/share/apps/ktalkd. ktalkdrc points to it.
+ Made package install_root capable (e.g. for building rpms).
+
+1998-01-25 David Faure <faure@kde.org> (0.3.1)
+ atdlg will now play sound itself, using libmediatool.
+ Typos and bugs corrected.
+
+1998-01-24 still me ... :) (0.3.0)
+ Added internationalization to atdlg.cpp. Had to change atdlg params.
+ User must set language in ktalkdrc.
+ Added more user options to ktalkdrc_user: Sound, SoundPlayer, SoundFile.
+ Updated configure.in and acinclude.m4 to stick to kdenetwork as much
+ as possible.
+
+1998-01-14 David Faure <faure@kde.org> (0.2.5)
+ Moved -DHAVE_KDE from config.h (was a bad hack) to Makefile.am
+ Added AC_CHECK_GETDOMAINNAME and AC_CHECK_GETHOSTNAME...
+ and some other little changes to get closer to autoconf stuff from
+ kdenetwork, for future integration. Worked on BSD portability.
+
+1998-01-12 David Faure <faure@kde.org> (0.2.4)
+ Changed acinclude.m4, to remove NULL, and to make check for osockaddr
+ work better under bsd... Also removed NULL from anywhere in the code.
+
+1998-01-11 David Faure <faure@kde.org> (0.2.3)
+ Added a macro in acinclude.m4, to check for sockaddr and osockaddr.
+
+1998-01-11 David Faure <faure@kde.org> (0.2.2)
+ Wrote a new way of finding users, in addition to reading utmp,
+ which reads /proc to find $DISPLAY of processes. (Linux only).
+
+1998-01-08 David Faure <faure@kde.org> (0.2.1)
+ Removed MSG_EOR as it used in BSD with another meaning.
+
+1997-12-19 David Faure <faure@kde.org> (0.2.0)
+ Merged patch from Ralph Weichert (check for libbsd, needed under libc5)
+
+1997-12-16 David Faure <faure@kde.org> (0.1.9)
+ Fixed link command (back to $(LINK), not $(CXXLINK).
+ Fixed process.c (config.h not included => NEW_FIND_USER not defined)
+ Wrote includ.h to resolve struct definitions problems.
+ Started user config file processing (~/.kde/share/config/ktalkdrc)
+
+1997-12-13 David Faure <faure@kde.org> (0.1.8)
+ Improved configure.in, acinclude.m4, and ktalkd/Makefile.am, to
+ * find out where to install the daemon
+ * compile even without X, Qt, and KDE
+ * remove jpeg/gif dependencies
+
+1997-12-12 David Faure <faure@kde.org> (0.1.7)
+ Small bugs correction.
+
+1997-12-08 David Faure and Ralph Weichert (0.1.6)
+ Added autoconf and automake support. Added support for glibc.
+ Back to c compiling, except for .cpp files, of course.
+
+1997-12-02 David Faure <faure@kde.org> (0.1.5)
+ Read KDE configuration file, $KDEDIR/share/config/ktalkdrc, both by
+ atdlg and ktalkd, in readcfg++.cpp. Made all daemon compile with g++.
+ (Is this right ?)
+
+1997-11-23 David Faure <faure@kde.org> (0.1.4)
+ Re-wrote announcement by answering machine. One line at a time, not
+ one char at a time.
+
+1997-11-22 David Faure <faure@kde.org> (0.1.3)
+ Re-wrote process_etc_file, to read talkd.conf sequentially.
+
+1997-11-21 David Faure <faure@kde.org> (0.1.2c)
+ Merged patch from <Ralph.Weichert@physik.th-darmstadt.de> :
+ Used KDE libs in atdlg. User configurable talk client.
+ Merged patch from Bruce Gingery <bruce@gtcs.com> :
+ User configurable To: E-Mail address
+
+1997-10-25 David Faure <faure@kde.org> (0.1.2b)
+ Made atdlg finish after RING_WAIT seconds, so that the re-announce
+ will display another window (=> compatibility with other clients than
+ ktalk)
+
+1997-10-22 David Faure <faure@kde.org> (0.1.2)
+ Added to ktalkd (see README for description):
+ * sound capability
+ * configuration (/etc/talkd.conf)
+ * answering machine
+
+1997-05-14 R. (0.1.1)
+ Improved (I hope) X11 recognition:
+ Local XDM logins (depends on sessreg)
+ Read $DISPLAY variable for PTY logins
+ atdlg is run as user and can use MIT-magic-cookies
+ atdlg will run talk program
diff --git a/ktalkd/Makefile.am b/ktalkd/Makefile.am
new file mode 100644
index 00000000..5a89fd1a
--- /dev/null
+++ b/ktalkd/Makefile.am
@@ -0,0 +1,29 @@
+## -*- makefile -*-
+# Ktalkd - Main Makefile.am
+
+if KDE_INSTALLED
+kde_SUBDIRS = kcmktalkd ktalkdlg
+endif
+SUBDIRS = ktalkd mail.local $(kde_SUBDIRS)
+
+EXTRA_DIST = ChangeLog TODO ktalkd.lsm
+
+install-data-local:
+ @echo "**************************************************************************"
+ @echo
+ @echo "Don't forget to update /etc/inetd.conf :"
+ @echo
+ @echo "For example, on a linux system, if kde stays in "$(prefix)", set it to :"
+ @echo "talk dgram udp wait root /usr/sbin/tcpd "$(bindir)"/ktalkd"
+ @echo "ntalk dgram udp wait root /usr/sbin/tcpd "$(bindir)"/ktalkd"
+ @echo
+ @echo "Alternatively, you can use the script post-install.sh, to do the job"
+ @echo
+ @echo "Anyway, you'll have to restart inetd after this."
+ @echo "On most linux system, do a 'killall -HUP inetd'"
+ @echo
+ @echo "**************************************************************************"
+
+messages:
+ $(XGETTEXT) */*.cpp -o $(podir)/kcmktalkd.pot
+
diff --git a/ktalkd/SNPRINTF_MISSING b/ktalkd/SNPRINTF_MISSING
new file mode 100644
index 00000000..95e85366
--- /dev/null
+++ b/ktalkd/SNPRINTF_MISSING
@@ -0,0 +1,21 @@
+ktalkd needs the snprintf function, but some system don't have it (some
+Solaris versions, for instance).
+
+If snprintf is missing on your system, you can do the following :
+
+* compile ktalkd/kotalkd/vsnprintf.c, by entering 'make vsnprintf.o' in this
+directory. (there might be warnings, don't be afraid of them...)
+* if it compiles
+ - add vsnprintf.o to the kotalkd_OBJECTS line in ktalkd/kotalkd/Makefile
+ - copy vsnprintf.o to ktalkd/ktalkd
+ - add vsnprintf.o to the cond0ktalkd_OBJECTS line in ktalkd/ktalkd/Makefile
+ then run make from the toplevel directory again.
+
+Many thanks to Olof S Kylander <olof@frozenriver.com> for providing vsnprintf.c
+and to Bert Haverkamp <b.r.j.haverkamp@its.tudelft.nl> for reporting bugs in this readme.
+
+I know, the really good approach would be to include the missing
+automatically, which can be done. But for this I need to know if
+vsnprintf.c compiles on all architectures where snprintf is missing.
+If yes, I'll make the code in vsnprintf included automatically (in kdecore/fakes.cpp)
+
diff --git a/ktalkd/TODO b/ktalkd/TODO
new file mode 100644
index 00000000..07fb44bc
--- /dev/null
+++ b/ktalkd/TODO
@@ -0,0 +1,16 @@
+
+TODO:
+ - a 'not here' option, which launches the answmach at once (if set to on).
+ (or refuse the talks otherwise).
+ This way, when you leave, you switch 'on' the answmach.
+ - insensitive case and long logins supported for user names (big problem).
+ - a kdelnk file, not for launching ktalkd, but to make kdehelp's
+ helpindex find ktalkd - how to avoid it to appear in the K menu ?
+
+I thought also of a kde app. swallowed in kpanel, to choose options like
+ "refuse talks ('not here')", "activate/desactivate answering machine"
+ to launch a talk client, and to set up forwardings.
+But you can add to the panel a button to launch kcmktalkd, which does
+ all this except launch talk sessions.
+
+Last updated 30-Sep-1998
diff --git a/ktalkd/configure.in.in b/ktalkd/configure.in.in
new file mode 100644
index 00000000..60a7df9c
--- /dev/null
+++ b/ktalkd/configure.in.in
@@ -0,0 +1,73 @@
+## ktalkd specific checks
+## David Faure <faure@kde.org>
+
+AC_DEFUN([AC_FIND_USER_METHOD],
+[
+AC_MSG_CHECKING(ktalkd find_user method)
+if test -n "`echo $target_os | grep linux`" ; then
+ if test -d /proc; then
+ AC_DEFINE(PROC_FIND_USER, 1, [/proc exists])
+
+## Sufficient if all xdm and kdm would call sessreg to log the user into utmp
+ AC_DEFINE(UTMP_AND_PROC_FIND_USER, 1, [kdm/xmd log user])
+
+## Waiting for this, here is complement, looking for DISPLAY set in any process
+## in /proc that the user owns
+ AC_DEFINE(ALL_PROCESSES_AND_PROC_FIND_USER, 1, [whatever])
+
+ AC_MSG_RESULT(using /proc.)
+ else
+ AC_MSG_RESULT(/proc not found, using utmp.)
+ fi
+else
+ AC_MSG_RESULT(not a linux system, using utmp.)
+fi
+
+])
+
+AC_FIND_USER_METHOD
+
+# Define a symbol, to know that we're compiling WITH kde.
+# (Separate distributions of ktalkd can compile without KDE)
+AM_CONDITIONAL(KDE_INSTALLED, test "$have_kde" = "yes")
+
+dnl Check for utmp file
+AC_CHECK_UTMP_FILE([], [DO_NOT_COMPILE="$DO_NOT_COMPILE ktalkd"])
+
+AC_LANG_C
+dnl Checks for libraries.
+AC_CHECK_LIB(bsd, bsd_ioctl, [LIBBSD="-lbsd"]) dnl for Linux with libc5
+AC_SUBST(LIBBSD)
+
+AC_CHECK_HEADERS(sgtty.h bsd/sgtty.h sys/select.h)
+
+AC_HEADER_TIME
+
+dnl check for this stupid scandir constness problem
+AC_LANG_CPLUSPLUS
+save_CXXFLAGS="$CXXFLAGS"
+dnl for some reason CXXFLAGS contains $(KDE_CXXFLAGS) at this point. Argl.
+CXXFLAGS="-Wall -W"
+if test "$GCC" = "yes"; then
+CXXFLAGS="$CXXFLAGS -pedantic-errors"
+fi
+AC_MSG_CHECKING(whether the third argument of scandir needs const)
+AC_CACHE_VAL(ac_cv_scandir_const,
+[
+AC_TRY_COMPILE([
+#include <dirent.h>
+int select_process(const struct dirent *) { return 0; }
+],
+[
+ struct dirent **namelist;
+ (void) scandir("/proc", &namelist, select_process, 0 /*no sort*/);
+],
+ac_cv_scandir_const=yes,
+ac_cv_scandir_const=no)
+])
+AC_MSG_RESULT($ac_cv_scandir_const)
+
+if eval "test \"`echo $ac_cv_scandir_const`\" = yes"; then
+ AC_DEFINE(SCANDIR_NEEDS_CONST, 1, [Define if third argument of scandir needs const])
+fi
+CXXFLAGS="$save_CXXFLAGS"
diff --git a/ktalkd/kcmktalkd/Makefile.am b/ktalkd/kcmktalkd/Makefile.am
new file mode 100644
index 00000000..21fddc81
--- /dev/null
+++ b/ktalkd/kcmktalkd/Makefile.am
@@ -0,0 +1,15 @@
+xdg_apps_DATA = kcmktalkd.desktop
+KDE_ICON = ktalkd
+
+INCLUDES = $(all_includes)
+METASOURCES=AUTO
+
+kde_module_LTLIBRARIES = kcm_ktalkd.la
+
+kcm_ktalkd_la_SOURCES = main.cpp soundpage.cpp answmachpage.cpp \
+ forwmachpage.cpp
+kcm_ktalkd_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kcm_ktalkd_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO)
+
+#for extra warnings during compilation :
+# AMDEFS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
diff --git a/ktalkd/kcmktalkd/answmachpage.cpp b/ktalkd/kcmktalkd/answmachpage.cpp
new file mode 100644
index 00000000..5df2bee3
--- /dev/null
+++ b/ktalkd/kcmktalkd/answmachpage.cpp
@@ -0,0 +1,265 @@
+/*
+ * answmachpage.cpp - Answering machine settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "answmachpage.h"
+
+
+#include <ksimpleconfig.h>
+
+#include <stdlib.h>
+#include <klocale.h> // for getenv
+
+KAnswmachPageConfig::KAnswmachPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ }
+
+ answmach_cb = new QCheckBox(i18n("&Activate answering machine"), this);
+ answmach_cb->adjustSize();
+ mail_edit = new QLineEdit(this);
+ mail_edit->adjustSize();
+ mail_edit->setMinimumWidth(150);
+ mail_label = new QLabel(mail_edit,i18n("&Mail address:"),this);
+ mail_label->adjustSize();
+ mail_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ subj_edit = new QLineEdit(this);
+ subj_edit->adjustSize();
+ subj_edit->setMinimumWidth(150);
+ subj_label = new QLabel(subj_edit, i18n("Mail s&ubject:"),this);
+ subj_label->adjustSize();
+ subj_label->setAlignment( ShowPrefix | AlignVCenter );
+ subj_tip = new QLabel(i18n("Use %s for the caller name"),this);
+ subj_tip->setAlignment( ShowPrefix );
+
+ head_edit = new QLineEdit(this);
+ head_edit->adjustSize();
+ head_edit->setMinimumWidth(150);
+ head_label = new QLabel(head_edit, i18n("Mail &first line:"),this);
+ head_label->adjustSize();
+ head_label->setAlignment( ShowPrefix | AlignVCenter );
+ head_tip = new QLabel(
+ i18n("Use first %s for caller name, and second %s for caller hostname"),
+ this);
+ head_tip->setAlignment( ShowPrefix );
+
+ emptymail_cb = new QCheckBox(i18n("&Receive a mail even if no message left"), this);
+ emptymail_cb->adjustSize();
+
+ msg_ml = new QMultiLineEdit(this);
+ msg_ml->adjustSize();
+ msg_ml->setMinimumWidth(150);
+ msg_label = new QLabel(msg_ml, i18n("&Banner displayed on answering machine startup:"),this);
+ msg_label->adjustSize();
+ msg_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ int h = 10 + answmach_cb->height() + mail_edit->height() +
+ subj_edit->height() + subj_tip->height() + head_edit->height()
+ + head_tip->height() + emptymail_cb->height() +
+ msg_label->height() + msg_ml->height() + 30;
+ setMinimumSize(400, h); // 400 : otherwise, buttons may overlap
+ msg_default = new QString(i18n("The person you are asking to talk with is not answering.\n"
+"Please leave a message to be delivered via email.\n"
+"Just start typing and when you have finished, exit normally."));
+
+ load();
+
+ connect(answmach_cb, SIGNAL(clicked()), this, SLOT(answmachOnOff()));
+
+ // Emit changed(true) when anything changes
+ connect(answmach_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(mail_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(subj_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(head_edit, SIGNAL(textChanged(const QString&)),
+ this, SLOT(slotChanged()));
+ connect(emptymail_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(msg_ml, SIGNAL(textChanged()), this, SLOT(slotChanged()));
+
+}
+
+KAnswmachPageConfig::~KAnswmachPageConfig( ) {
+ if (delete_config) delete config;
+ delete answmach_cb;
+ delete mail_label;
+ delete mail_edit;
+ delete subj_label;
+ delete subj_edit;
+ delete subj_tip;
+ delete head_label;
+ delete head_edit;
+ delete head_tip;
+ delete emptymail_cb;
+ delete msg_label;
+ delete msg_ml;
+ delete msg_default;
+}
+
+void KAnswmachPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+void KAnswmachPageConfig::resizeEvent(QResizeEvent *) {
+
+ int h_txt = answmach_cb->height(); // taken for the general label height
+ int h_edt = mail_edit->height(); // taken for the general QLineEdit height
+ int spc = h_txt / 3;
+ int w = rect().width();
+
+ int leftedits = mail_label->width();
+ if ( subj_label->width() > leftedits )
+ leftedits = subj_label->width();
+ if ( head_label->width() > leftedits )
+ leftedits = head_label->width();
+ leftedits += 20;
+
+ int h = 10 + spc*2;
+ answmach_cb->move(10, h);
+ h += h_txt+spc;
+ mail_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ mail_label->move(10, h);
+ mail_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+
+ subj_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ subj_label->move(10, h);
+ subj_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+ subj_tip->setFixedWidth(w-20);
+ subj_tip->move(leftedits, h);
+ h += h_txt+spc;
+
+ head_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ head_label->move(10, h);
+ head_edit->setGeometry(leftedits, h, w-leftedits-10, h_edt);
+ h += h_edt+spc;
+ head_tip->setFixedWidth(w-20);
+ head_tip->move(leftedits, h);
+ h += h_txt+spc;
+
+ emptymail_cb->move(10,h);
+ h += h_txt+spc;
+
+ msg_label->move(10, h);
+ h += h_txt+spc;
+
+ msg_ml->setGeometry(10, h, w-20, height() - h - 10);
+ h += h_edt+spc;
+}
+
+void KAnswmachPageConfig::answmachOnOff()
+{
+ bool b = answmach_cb->isChecked();
+ mail_label->setEnabled(b);
+ mail_edit->setEnabled(b);
+ subj_label->setEnabled(b);
+ subj_edit->setEnabled(b);
+ subj_tip->setEnabled(b);
+ head_label->setEnabled(b);
+ head_edit->setEnabled(b);
+ head_tip->setEnabled(b);
+ emptymail_cb->setEnabled(b);
+ msg_label->setEnabled(b);
+ msg_ml->setEnabled(b);
+}
+
+void KAnswmachPageConfig::defaults() {
+
+ answmach_cb->setChecked(true);
+
+ mail_edit->setText(getenv("REPLYTO"));
+ subj_edit->setText(i18n("Message from %s"));
+ head_edit->setText(i18n("Message left on the answering machine, by %s@%s"));
+
+ emptymail_cb->setChecked(true);
+ msg_ml->setText(*msg_default);
+
+ // Activate things according to configuration
+ answmachOnOff();
+
+}
+
+void KAnswmachPageConfig::load() {
+
+ config->setGroup("ktalkd");
+
+ answmach_cb->setChecked(config->readBoolEntry("Answmach",true));
+
+ mail_edit->setText(config->readEntry("Mail",getenv("REPLYTO")));
+ subj_edit->setText(config->readEntry("Subj",i18n("Message from %s")));
+ head_edit->setText(config->readEntry("Head",
+ i18n("Message left on the answering machine, by %s@%s")));
+
+ emptymail_cb->setChecked(config->readBoolEntry("EmptyMail",true));
+
+ msg_ml->clear();
+ char m[]="Msg1"; // used as key to read configuration
+ QString msg;
+ while (!(msg=config->readEntry(m)).isNull())
+ {
+ msg_ml->append(msg);
+ ++m[3];
+ }
+
+ if (m[3]=='1') // nothing in the config file
+ msg_ml->setText(*msg_default);
+
+ // Activate things according to configuration
+ answmachOnOff();
+
+ emit changed(false);
+}
+
+void KAnswmachPageConfig::save() {
+
+ config->setGroup("ktalkd");
+ config->writeEntry("Answmach", answmach_cb->isChecked());
+ config->writeEntry("Mail",mail_edit->text());
+ config->writeEntry("Subj",subj_edit->text());
+ config->writeEntry("Head",head_edit->text());
+ config->writeEntry("EmptyMail", emptymail_cb->isChecked());
+ char m[]="Msg1"; // used as key to read configuration
+ int linenr=0;
+ QString msg;
+ while ((linenr<8) && (linenr<msg_ml->numLines()))
+ {
+ config->writeEntry(m,msg_ml->textLine(linenr));
+ ++m[3]; ++linenr;
+ }
+ config->deleteEntry(m,false/*non localized*/);
+ // necessary to avoid old msg lines to reappear after
+ // deleting some.
+
+ config->sync();
+}
+
+#include "answmachpage.moc"
diff --git a/ktalkd/kcmktalkd/answmachpage.h b/ktalkd/kcmktalkd/answmachpage.h
new file mode 100644
index 00000000..e1615364
--- /dev/null
+++ b/ktalkd/kcmktalkd/answmachpage.h
@@ -0,0 +1,83 @@
+/*
+ * answmachpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KANSWMACHPAGE_H__
+#define __KCONTROL_KANSWMACHPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qmultilineedit.h>
+#include <qpushbutton.h>
+#include <qbutton.h>
+#include <qcheckbox.h>
+
+#include <kcmodule.h>
+
+class KSimpleConfig;
+
+class KAnswmachPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KAnswmachPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0 );
+ ~KAnswmachPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+
+private slots:
+ void answmachOnOff();
+ void slotChanged();
+
+private:
+ KSimpleConfig *config;
+ bool delete_config;
+
+ QCheckBox *answmach_cb;
+ QLabel *mail_label;
+ QLineEdit *mail_edit;
+ QLabel *subj_label;
+ QLineEdit *subj_edit;
+ QLabel *subj_tip;
+ QLabel *head_label;
+ QLineEdit *head_edit;
+ QLabel *head_tip;
+ QCheckBox *emptymail_cb;
+ QLabel *msg_label;
+ QMultiLineEdit *msg_ml;
+
+ QString *msg_default;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/cr128-app-ktalkd.png b/ktalkd/kcmktalkd/cr128-app-ktalkd.png
new file mode 100644
index 00000000..8d9c97f7
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr128-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr16-app-ktalkd.png b/ktalkd/kcmktalkd/cr16-app-ktalkd.png
new file mode 100644
index 00000000..fd36501c
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr16-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr22-app-ktalkd.png b/ktalkd/kcmktalkd/cr22-app-ktalkd.png
new file mode 100644
index 00000000..9ac54546
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr22-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr32-app-ktalkd.png b/ktalkd/kcmktalkd/cr32-app-ktalkd.png
new file mode 100644
index 00000000..896c9be3
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr32-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/cr48-app-ktalkd.png b/ktalkd/kcmktalkd/cr48-app-ktalkd.png
new file mode 100644
index 00000000..0ad792c7
--- /dev/null
+++ b/ktalkd/kcmktalkd/cr48-app-ktalkd.png
Binary files differ
diff --git a/ktalkd/kcmktalkd/forwmachpage.cpp b/ktalkd/kcmktalkd/forwmachpage.cpp
new file mode 100644
index 00000000..5d82afed
--- /dev/null
+++ b/ktalkd/kcmktalkd/forwmachpage.cpp
@@ -0,0 +1,183 @@
+/*
+ * forwmachpage.cpp - Forwarding machine settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "forwmachpage.h"
+
+#include <ksimpleconfig.h>
+#include <klocale.h>
+
+KForwmachPageConfig::KForwmachPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ }
+ forwmach_cb = new QCheckBox(i18n("Activate &forward"), this);
+ forwmach_cb->adjustSize();
+ address_edit = new QLineEdit(this);
+ address_edit->adjustSize();
+ address_edit->setMinimumWidth(150);
+ address_label = new QLabel(address_edit,i18n("&Destination (user or user@host):"),this);
+ address_label->adjustSize();
+ address_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ method_combo = new QComboBox(this);
+ method_combo->insertItem("FWA");
+ method_combo->insertItem("FWR");
+ method_combo->insertItem("FWT");
+ method_combo->adjustSize();
+ method_combo->setMinimumWidth(80);
+ method_label = new QLabel(method_combo, i18n("Forward &method:"),this);
+ method_label->adjustSize();
+ method_label->setAlignment( ShowPrefix | AlignVCenter );
+
+ expl_label = new QLabel(i18n(
+"FWA: Forward announcement only. Direct connection. Not recommended.\n\
+FWR: Forward all requests, changing info when necessary. Direct connection.\n\
+FWT: Forward all requests and handle the talk request. No direct connection.\n\
+\n\
+Recommended use: FWT if you want to use it behind a firewall (and if ktalkd\n\
+can access both networks). Otherwise choose FWR.\n\
+\n\
+See Help for further explanation.\n\
+"),this);
+ expl_label->adjustSize();
+
+ int h = 10 + forwmach_cb->height() + address_edit->height() +
+ method_combo->height()+expl_label->height()+30;
+ setMinimumSize(400, h); // 400: otherwise, buttons may overlap
+
+ load();
+
+ connect(forwmach_cb, SIGNAL(clicked()), this, SLOT(forwmachOnOff()));
+
+ // emit changed(true) on changes
+ connect(forwmach_cb, SIGNAL(clicked()), this, SLOT(slotChanged()));
+ connect(address_edit, SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+ connect(method_combo, SIGNAL(activated(int)), this, SLOT(slotChanged()));
+}
+
+KForwmachPageConfig::~KForwmachPageConfig( ) {
+ if (delete_config) delete config;
+
+ /* I've been told that this is not necessary as
+ they will be deleted by Qt. But, well... */
+ delete forwmach_cb;
+ delete address_label;
+ delete address_edit;
+ delete method_label;
+ delete method_combo;
+ delete expl_label;
+}
+
+void KForwmachPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+void KForwmachPageConfig::resizeEvent(QResizeEvent *) {
+ int h_txt = forwmach_cb->height(); // taken for the general label height
+ int h_edt = address_edit->height(); // taken for the general QLineEdit height
+ int spc = h_txt / 3;
+ int w = rect().width();
+
+ int h = 10 + spc*2;
+ forwmach_cb->move(10, h);
+ h += h_txt+spc;
+ address_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ address_label->move(10, h);
+ int w_label = address_label->width()+20;
+ address_edit->setGeometry(w_label, h, w-w_label-10, h_edt);
+ h += h_edt+spc;
+
+ method_label->setFixedHeight(h_edt); // same size -> vertical center aligned
+ method_label->move(10, h);
+ method_combo->move(w_label, h);
+ h += h_edt+spc;
+
+ expl_label->move(10,h);
+
+}
+
+void KForwmachPageConfig::forwmachOnOff() {
+ bool b = forwmach_cb->isChecked();
+ address_label->setEnabled(b);
+ address_edit->setEnabled(b);
+ method_label->setEnabled(b);
+ method_combo->setEnabled(b);
+ expl_label->setEnabled(b);
+}
+
+void KForwmachPageConfig::defaults() {
+
+ forwmach_cb->setChecked(false);
+ method_combo->setCurrentItem(1);
+ address_edit->setText("");
+
+ // Activate things according to configuration
+ forwmachOnOff();
+
+}
+
+void KForwmachPageConfig::load() {
+
+ config->setGroup("ktalkd");
+
+ QString forward = config->readEntry("Forward","unset");
+ forwmach_cb->setChecked(forward!="unset");
+ if (forward != "unset" )
+ address_edit->setText(forward);
+ else
+ address_edit->setText("");
+
+ QString forwardMethod = config->readEntry("ForwardMethod","FWR");
+ for (int i=0; i<method_combo->count(); i++)
+ if (forwardMethod == method_combo->text(i))
+ method_combo->setCurrentItem(i);
+
+ // Activate things according to configuration
+ forwmachOnOff();
+
+ emit changed(false);
+}
+
+void KForwmachPageConfig::save() {
+
+ config->setGroup("ktalkd");
+
+ if (forwmach_cb->isChecked())
+ {
+ config->writeEntry("Forward",address_edit->text());
+ } else
+ config->deleteEntry("Forward", false /*non localized*/);
+ config->writeEntry("ForwardMethod",method_combo->currentText());
+
+ config->sync();
+}
+
+#include "forwmachpage.moc"
diff --git a/ktalkd/kcmktalkd/forwmachpage.h b/ktalkd/kcmktalkd/forwmachpage.h
new file mode 100644
index 00000000..5db2a7ea
--- /dev/null
+++ b/ktalkd/kcmktalkd/forwmachpage.h
@@ -0,0 +1,75 @@
+/*
+ * forwmachpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KFORWMACHPAGE_H__
+#define __KCONTROL_KFORWMACHPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qpushbutton.h>
+#include <qbutton.h>
+#include <qcheckbox.h>
+
+#include <kcmodule.h>
+
+class KSimpleConfig;
+
+class KForwmachPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KForwmachPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0);
+ ~KForwmachPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *e);
+
+private slots:
+ void forwmachOnOff();
+ void slotChanged();
+
+private:
+ KSimpleConfig *config;
+ bool delete_config;
+
+ QCheckBox *forwmach_cb;
+ QLabel *address_label;
+ QLineEdit *address_edit;
+ QLabel *method_label;
+ QComboBox * method_combo;
+ QLabel *expl_label;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/kcmktalkd.desktop b/ktalkd/kcmktalkd/kcmktalkd.desktop
new file mode 100644
index 00000000..22422eb4
--- /dev/null
+++ b/ktalkd/kcmktalkd/kcmktalkd.desktop
@@ -0,0 +1,207 @@
+[Desktop Entry]
+Icon=ktalkd
+Type=Application
+DocPath=kcontrol/kcmtalkd/index.html
+Exec=kcmshell kcmktalkd
+
+X-KDE-ModuleType=Library
+X-KDE-Library=ktalkd
+X-KDE-FactoryName=ktalkd
+
+Name=Local Network Chat
+Name[af]=Plaaslike Netwerk Gesels
+Name[ar]=تحادث عبر الشبكة المحلية
+Name[be]=Гутарка па мÑÑцовай Ñетцы
+Name[bg]=Разговор в локална мрежа
+Name[bn]=সà§à¦¥à¦¾à¦¨à§€à§Ÿ নেটওয়ারà§à¦• চà§à¦¯à¦¾à¦Ÿ
+Name[br]=Flapañ rouedad lec'hel
+Name[bs]=Chat u lokalnoj mreži
+Name[ca]=Xat de la xarxa local
+Name[cs]=Rozhovor v lokální síti
+Name[cy]=Sgwrs Rhwydwaith Lleol
+Name[da]=Lokalt netværk-chat
+Name[de]=Lokaler Netzwerk-Chat
+Name[el]=Συνομιλία στο τοπικό δίκτυο
+Name[eo]=Loka reta Babilejo
+Name[es]= Charla red local
+Name[et]=Kohtvõrgu chat
+Name[eu]=Sare lokaleko elkarrizketa
+Name[fa]=گپ شبکۀ محلی
+Name[fi]=Paikallinen verkkokeskustelu
+Name[fr]=Discussion sur Réseau local
+Name[gl]=Chat na rede local
+Name[he]=שיחות ברשת המקומית
+Name[hi]=सà¥à¤¥à¤¾à¤¨à¥€à¤¯ नेटवरà¥à¤• गपशप
+Name[hr]=Razgovor u lokalnoj mreži
+Name[hu]=Csevegés a helyi hálózaton
+Name[is]=Staðarnetsspjall
+Name[it]=Chat rete locale
+Name[ja]=ローカルãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒãƒ£ãƒƒãƒˆ
+Name[ka]=ლáƒáƒ™áƒáƒšáƒ£áƒ áƒ˜ ქსელის ჩáƒáƒ¢áƒ˜
+Name[kk]=Жергілікті желідегі әңгіме дүкені
+Name[km]=ជជែក​កំសាន្ដ​ក្នុង​បណ្ដាញ​មូលដ្ឋាន
+Name[lt]=Vietinio tinklo pokalbiai
+Name[mk]=Разговор на локална мрежа
+Name[mn]=Дотоод ÑүлжÑÑнд чалчих
+Name[ms]=Borak Jaringan Setempat
+Name[nb]=Lokalt nettverksprat
+Name[nds]=Lokaalnettwark-Klönen
+Name[ne]=सà¥à¤¥à¤¾à¤¨à¥€à¤¯ सञà¥à¤œà¤¾à¤² कà¥à¤°à¤¾à¤•à¤¾à¤¨à¥€
+Name[nl]=Gesprek over lokaal netwerk
+Name[nn]=Lokal nettverksprat
+Name[nso]=Poledisano ya Kgokagano ya Selegae
+Name[pa]=ਲੋਕਲ ਨੈੱਟਵਰਕ ਗੱਲਬਾਤ
+Name[pl]=Pogawędka w sieci lokalnej
+Name[pt]=Conversação na Rede Local
+Name[pt_BR]=Bate-papo de Rede Local
+Name[ru]=Чат в локальной Ñети
+Name[se]=Báikkalaš fierpmádatbuillardeapmi
+Name[sk]=Rozhovor na lokálnej sieti
+Name[sl]=Klepet prek lokalnega omrežja
+Name[sr]=ЋаÑкање у локалној мрежи
+Name[sr@Latn]=Ćaskanje u lokalnoj mreži
+Name[sv]=Chatt via lokalt nätverk
+Name[ta]=உளà¯à®³à®• பிணைய அரடà¯à®Ÿà¯ˆ
+Name[tg]=Чати Шабакаи Маҳаллӣ
+Name[th]=à¸à¸²à¸£à¸ªà¸™à¸—นาบนเครือข่ายท้องถิ่น
+Name[tr]=Yerel AÄŸ Sohbeti
+Name[uk]=Балачка в локальній мережі
+Name[ven]=U davhidzana ha vhukwamani ha tsini
+Name[xh]=Umsebenzi womnatha Wobulali Wokuncokola
+Name[zh_CN]=局域网èŠå¤©
+Name[zh_HK]=å€åŸŸç¶²è·¯èŠå¤©
+Name[zh_TW]=å€åŸŸç¶²è·¯èŠå¤©å®¤
+Name[zu]=Oluseduze Uxhumaniso olusakazekile
+
+Comment=Talk daemon configuration
+Comment[af]=Praat bediener opstelling
+Comment[ar]=اعدادات مراقب Talh
+Comment[az]=Talk quraşdırması
+Comment[be]=ÐаÑтаўленне ÑервіÑа гутаркі
+Comment[bg]=ÐаÑтройване на демона за разговор в локална мрежа
+Comment[bn]=টক(Talk) ডিমন কনফিগারেশন
+Comment[br]=Kefluniadur an diaoul Talk
+Comment[bs]=Podešavanje Talk demona
+Comment[ca]=Configuració del dimoni talk
+Comment[cs]=Nastavení talk démona
+Comment[cy]=Ffurfweddiad daemon sgwrs
+Comment[da]=Talkdæmon-indstilling
+Comment[de]=Einrichtung des Talk-Dienstes
+Comment[el]=ΡÏθμιση του δαίμονα talk
+Comment[eo]=Agordo de la "Talk"-demono
+Comment[es]=Configuración del demonio Talk
+Comment[et]=Talk deemoni seadistamine
+Comment[eu]=Elkarrizketa deabruaren konfigurazioa
+Comment[fa]=پیکربندی شبح Ú¯Ùتگو
+Comment[fi]=Talk-palvelimen asetukset
+Comment[fr]=Configuration du démon Talk
+Comment[ga]=Cumraíocht deamhan comhrá
+Comment[gl]=Configuración do demo talk
+Comment[he]=שינוי הגדרות תהליך הרקע Talk
+Comment[hi]=टाक डेमन कॉनà¥à¤«à¤¼à¤¿à¤—रेशन
+Comment[hr]=Postava talk daemona
+Comment[hu]=A Talk szolgáltatás beállításai
+Comment[id]=Konfigurasi daemon Talk
+Comment[is]=Stillingar samtalsþjóns
+Comment[it]=Configurazione demone talk
+Comment[ja]=Talk デーモンã®è¨­å®š
+Comment[ka]=სáƒáƒ£áƒ‘რის დემáƒáƒœáƒ˜áƒ¡ კáƒáƒœáƒ¤áƒ˜áƒ’ურáƒáƒªáƒ˜áƒ
+Comment[kk]=Talk қызметінің параметрлері
+Comment[km]=ការ​កំណážáŸ‹â€‹ážšáž…នា​សម្ពáŸáž“្ធ​ដáŸáž˜áž·áž“​សន្ទនា
+Comment[ko]=ì´ì•¼ê¸° ë°ëª¬ 설정
+Comment[lt]=Pokalbių tarnybos derinimas
+Comment[mk]=Конфигурација на даемонот за разговарање
+Comment[mn]=Talk демон тохируулга
+Comment[ms]=Penyelarasan daemon Talk
+Comment[mt]=Konfigurazzjoni tad-daemon Talk
+Comment[nb]=Oppsett av Talk-nissen
+Comment[nds]=Talkdämoon instellen
+Comment[ne]=डेइमन कनà¥à¤«à¤¿à¤—रेसन बोलà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Talk-daemon instellen
+Comment[nn]=Oppsett av Talk-nissen
+Comment[nso]=Peakanyo ya daemon ya polelo
+Comment[pa]=ਗੱਲਬਾਤ ਡੈਮਨ ਸੰਰਚਨਾ
+Comment[pl]=Konfiguracja demona talk
+Comment[pt]=Configuração do servidor do talk
+Comment[pt_BR]=Configuração do servidor talk
+Comment[ro]=Configurează demonul "talk"
+Comment[ru]=Параметры Ñлужбы Talk
+Comment[se]=Heivet talk-duogášprográmma
+Comment[sk]=Nastavenie talk démona
+Comment[sl]=Nastavitev strežnika za talk
+Comment[sr]=Подешавање „Talk“ демона
+Comment[sr@Latn]=Podešavanje „Talk“ demona
+Comment[sv]=Anpassa talk-demon
+Comment[ta]=Talk daemon வடிவமைபà¯à®ªà¯
+Comment[tg]=Батанзимдарории азозили Talk
+Comment[th]=ปรับà¹à¸•à¹ˆà¸‡à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œ Talk
+Comment[tr]=Talk sunucu programı yapılandırması
+Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´ÐµÐ¼Ð¾Ð½Ñƒ Talk
+Comment[ven]=Nzudzanyo ya daemon yau amba
+Comment[xh]=Uqwalaselo lwenkcoko ye daemon
+Comment[zh_CN]=Talk 守护进程é…ç½®
+Comment[zh_HK]=Talk 系統程å¼è¨­å®š
+Comment[zh_TW]=Talk 伺æœç¨‹å¼çµ„æ…‹
+Comment[zu]=Inhlanganiselo lwedaemon yenkulumo
+
+Keywords=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[af]=talk,announcement,client,sound,answering,mail,caller,banner,forward,bestemming
+Keywords[ar]=تحدث,اعلان,عميل,صوت,استجابة,بريد,متصل,شعار,ارسال,اتجاه
+Keywords[az]=talk,e'lan,alıcı,Səs,cavablama,poçt,çağıran,bayraq,yolla,hədəf
+Keywords[be]=гутарка,кліент,гук,адказ,прызначÑнне,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[bg]=разговор, локална, мрежа, чат, клиент, колега, talk, announcement, client, sound, answering, mail, caller, banner, forward, destination
+Keywords[bs]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,zvuk,klijent,odgovor,odredište
+Keywords[ca]=parlar,notificació,client,so,contestar,correu,qui truca,rètol,enviar,destí
+Keywords[cs]=Talk,Rozhovor,Oznámení,Klient,Zvuk,Odpovídání,PoÅ¡ta,Volající,Banner,PÅ™edání,UrÄení
+Keywords[cy]=siarad,cyhoeddiad,dibynnydd,swn,ateb,ebost,ymwelwr,baner,ymlaen,cyrchfan
+Keywords[da]=talk,annoncering,klient,lyd,svar,post,opringer,banner,fremad,destination
+Keywords[de]=Talk,Ankündigung,Client,Sound,Antwort,Mail,Banner,Forward,Weiterleitung
+Keywords[el]=ομιλία,ανακοίνωση,πελάτης,ήχος,απάντηση,mail,καλών,banner,Ï€Ïοώθηση,Ï€ÏοοÏισμός
+Keywords[eo]=parolo,anonco,babilo,kliento,sonoro,respondo,retpoÅto,alvokanto,flago,plusendo,celo
+Keywords[es]=hablar,notificación,cliente,sonido,contestar,correo,llamador,rótulo,enviar,destino
+Keywords[et]=talk,teadaanne,klient,heli,vastamine,meil,helistaja,bänner,edasisuunamine, adressaat
+Keywords[eu]=elkarrizketa,berri-ematea,bezeroa,soinua,erantzungailu automatikoa,posta,deitzailea,titularra,birbidali,helburua
+Keywords[fa]=Ú¯Ùتگو، اعلان، کارخواه، صوت، پاسخ، نامه، شمارۀ گیرنده، عنوان‌سازی، پیش‌سو، مقصد
+Keywords[fi]=talk,julkistus,asiakas,ääni,vastaus,sähköposti,soittaja,mainos,uudelleenohjaa,kohde
+Keywords[fr]=talk,discussion,annonce,bavardage,client,son,réponse,mail,courrier électronique,message,appel,bannière,envoi,destination
+Keywords[gl]=talk,anuncio,cliente,son,resposta,correo
+Keywords[he]=שיחות,הכרזה,לקוח,צליל,תשובה,דו×ר,מתקשר,כתובית,העברה,יעד,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[hi]=टाक,घोषणा,कà¥à¤²à¤¾à¤à¤‚ट,धà¥à¤µà¤¨à¤¿,उतà¥à¤¤à¤°,मेल,कालर,धà¥à¤µà¤œ,फारवरà¥à¤¡,गंतवà¥à¤¯
+Keywords[hu]=talk,bejelentés,kliens,hang,válasz,e-mail,hívó,üdvözlő szöveg,továbbítás,cél
+Keywords[is]=talk,tilkynningar,biðlari,hljóð,svörun,svara,póstur,mail,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[it]=talk,annuncio,client,suono,risposte,posta,chiamante,messaggio,inoltra,destinazione
+Keywords[ja]=トーク,アナウンス,クライアント,音,answering,メール,呼出å´,ãƒãƒŠãƒ¼,å‰ã¸,è¡Œãå…ˆ
+Keywords[km]=ជជែក​គ្នា,ប្រកាស,ភ្ញៀវ,សំឡáŸáž„,ឆ្លើយឆ្លង,សំបុážáŸ’ážš,អ្នកហៅ,បដា,ទៅ​មុáž,ទិសដៅ
+Keywords[ko]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,ì´ì•¼ê¸°,토í¬,톡,알림,알려줌,í´ë¼ì´ì–¸íŠ¸,소리,답신,댓거리,답신,편지,부른ì´,콜,깃발,기,배너,전달,í¬ì›Œë“œ
+Keywords[lt]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,kalbėti,kalbėtis,pokalbis,anonsas,pranešimas,paskelbimas,panešti,paskelbti,klientas,garsas,atsakyti,atsakymas,atsiliepti,paštas,laiškas,skambintojas,skydelis,persiųsti,persiuntimas,gavėjas
+Keywords[lv]=runa,paziņojums,klients,skaņa,atbildÄ“Å¡ana,pasts,saucÄ“js,banners,pÄrsÅ«tÄ«t,adresÄts
+Keywords[mk]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,разговарање,објавување,клиент,звук,одговор,пошта,повикувач,банер,препрати,одредиште
+Keywords[ms]=cakap,pengumuman,klien,bunyi,jawapan,mel,pemanggil,pemidang,destinasi,talk,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[mt]=talk,annunzja,announcement,client,sound,answering,mail,caller,banner,forward,destination
+Keywords[nb]=talk,kunngjøring,klient,lyd,svar,post,oppringing,fane,videresend,mål
+Keywords[nds]=talk,Mellen,client,Klang,antern,Antermaschien,Nettpost,Anroper,Startnaricht,wiederledden,Teel
+Keywords[ne]=बोलचाल,घोषणा,कà¥à¤²à¤¾à¤‡à¤¨à¥à¤Ÿ,धà¥à¤µà¤¨à¤¿,जवाफ,मेल,कलर,बà¥à¤¯à¤¾à¤¨à¤°,अगाडि,गनà¥à¤¤à¤¬à¥à¤¯
+Keywords[nl]=talk,aankondiging,client,sound,geluid,beantwoorden,mail,caller,banner,banier,bestemming,forward,doorsturen,destination,antwoordapparaat
+Keywords[nn]=talk,kunngjering,klient,lyd,svar,post,oppringing,vidaresend,mål
+Keywords[nso]=polelo,tsebiso,moreki,lesata,araba,poso,mmitsi,banner,pele,mafelelo a leeto
+Keywords[pl]=rozmowa,ogłoszenie,klient,dźwięk,odpowiadanie,mail,dzwoniący,banner,podaj dalej,cel
+Keywords[pt]=talk,anúncio,cliente,som,resposta,correio,chamador,mensagem,reenviar,destino
+Keywords[pt_BR]=talk,anúncio,cliente,som,resposta,e-mail,correio,chamador,banner,repassar, destino
+Keywords[ro]=convorbire,talk,anunţ,client,sunet,răspuns,apel,mail,apelant,apelat,înaintare,destinatar
+Keywords[se]=talk,almmuheapmi,klienta,jietna,vástideapmi,boasta,sádde viidáset,ulbmil
+Keywords[sk]=talk,oznámenie, klient,zvuk,odpoveÄ,mail,poÅ¡ta,volajúci,cieľ,predaÅ¥ Äalej
+Keywords[sl]=talk,govor,objava,odjemalec,zvok,odgovarjanje,pošta,klicatelj,reklama,posredovanje,cilj
+Keywords[sr]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,разговор,објава,клијент,звук,одговор,одговарање,пошта,позивалац,проÑлеђивање,одредиште
+Keywords[sr@Latn]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,razgovor,objava,klijent,zvuk,odgovor,odgovaranje,pošta,pozivalac,prosleđivanje,odredište
+Keywords[sv]=talk,annonsering,klient,ljud,svar,e-post,uppringare,rubrik,vidarebefordran,destination
+Keywords[ta]=பேசà¯à®šà¯, அறிவிபà¯à®ªà¯, கிளையனà¯, ஒலி, பதிலளிதà¯à®¤à®²à¯, மினà¯à®©à®žà¯à®šà®²à¯, அழைபà¯à®ªà¯, பேனரà¯, à®®à¯à®©à¯à®©à®©à¯à®ªà¯à®ªà¯, சேரà¯à®®à®¿à®Ÿà®®à¯
+Keywords[tr]=talk,duyuru,istemci,Ses,cevaplama,posta,çağıran,bayrak,gönder,hedef
+Keywords[uk]=розмова,оголошеннÑ,клієнт,звук,відповідь,пошта,заÑтавка,переÑлати,talk,призначеннÑ
+Keywords[ven]=amba,divhadza,mushumisani,mubvumo,phindulo,zwamarifhi,muvhidzi,mupandeli,phanda, vhuyo
+Keywords[xh]=ncokola,isaziso,umxhasi,isandi,iyaphendula,iposi,umbizi,iceba lelaphu elinomyalezo,phambili,indawo ephelela kuyo
+Keywords[zh_CN]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,交谈,通知,客户程åº,声音,应答,邮件,æ示,呼å«è€…,转å‘,目标
+Keywords[zh_HK]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,èŠå¤©,公佈,客戶端,è²éŸ³,答覆,郵件,呼å«è€…,目的地
+Keywords[zh_TW]=talk,announcement,client,sound,answering,mail,caller,banner,forward,destination,èŠå¤©,公佈,客戶端,è²éŸ³,答覆,郵件,呼å«è€…,目的地
+Keywords[zu]=khuluma,isaziso,umthengi,umsindo,iyaphendula,iposi,umbizi,ibhodi elinomyalezo,phambili,indawo ophikekelekuyo
+
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
diff --git a/ktalkd/kcmktalkd/main.cpp b/ktalkd/kcmktalkd/main.cpp
new file mode 100644
index 00000000..f43fecff
--- /dev/null
+++ b/ktalkd/kcmktalkd/main.cpp
@@ -0,0 +1,113 @@
+/*
+ main.cpp - The KControl module for ktalkd
+
+ Copyright (C) 1998 by David Faure, faure@kde.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "main.h"
+#include "soundpage.h"
+#include "answmachpage.h"
+#include "forwmachpage.h"
+#include <ksimpleconfig.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <qtabwidget.h>
+#include <qlayout.h>
+
+KTalkdConfigModule::KTalkdConfigModule(QWidget *parent, const char *name)
+ : KCModule(parent, name)
+{
+ config = new KSimpleConfig("ktalkdrc");
+ announceconfig = new KSimpleConfig("ktalkannouncerc");
+
+ QVBoxLayout *layout = new QVBoxLayout(this);
+
+ tab = new QTabWidget(this);
+
+ layout->addWidget(tab);
+
+ soundpage = new KSoundPageConfig(this, "soundpage", config, announceconfig);
+ answmachpage = new KAnswmachPageConfig(this, "answmachpage", config);
+ forwmachpage = new KForwmachPageConfig(this, "forwmachpage", config);
+
+ tab->addTab(soundpage, i18n("&Announcement"));
+ tab->addTab(answmachpage, i18n("Ans&wering Machine"));
+ tab->addTab(forwmachpage, i18n("forward call", "&Forward"));
+
+ connect(soundpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ connect(answmachpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ connect(forwmachpage, SIGNAL(changed(bool)), this, SIGNAL(changed(bool)));
+ }
+
+KTalkdConfigModule::~KTalkdConfigModule() {
+ delete config;
+ delete announceconfig;
+ }
+
+void KTalkdConfigModule::defaults()
+{
+ if (soundpage) soundpage->defaults();
+ if (answmachpage) answmachpage->defaults();
+ if (forwmachpage) forwmachpage->defaults();
+}
+
+void KTalkdConfigModule::save()
+{
+ if (soundpage) soundpage->save();
+ if (answmachpage) answmachpage->save();
+ if (forwmachpage) forwmachpage->save();
+}
+
+void KTalkdConfigModule::load()
+{
+ if (soundpage) soundpage->load();
+ if (answmachpage) answmachpage->load();
+ if (forwmachpage) forwmachpage->load();
+}
+
+void KTalkdConfigModule::resizeEvent(QResizeEvent *)
+{
+ tab->setGeometry(0,0,width(),height());
+}
+
+extern "C"
+{
+ KDE_EXPORT KCModule *create_ktalkd(QWidget *parent, const char *)
+ {
+ return new KTalkdConfigModule(parent, "kcmktalkd");
+}
+
+ KDE_EXPORT KCModule *create_ktalkd_answmach(QWidget *parent, const char *)
+{
+ return new KAnswmachPageConfig(parent, "kcmktalkd");
+ }
+
+ KDE_EXPORT KCModule *create_ktalkd_sound(QWidget *parent, const char *)
+ {
+ return new KSoundPageConfig(parent, "kcmktalkd");
+ }
+
+ KDE_EXPORT KCModule *create_ktalkd_forwmach(QWidget *parent, const char *)
+ {
+ return new KForwmachPageConfig(parent, "kcmktalkd");
+ }
+}
+
+#include "main.moc"
+
diff --git a/ktalkd/kcmktalkd/main.h b/ktalkd/kcmktalkd/main.h
new file mode 100644
index 00000000..4b7ec823
--- /dev/null
+++ b/ktalkd/kcmktalkd/main.h
@@ -0,0 +1,64 @@
+/*
+ main.cpp - The KControl module for ktalkd
+
+ Copyright (C) 1998 by David Faure, faure@kde.org
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef __kcmktalkd_main_h
+#define __kcmktalkd_main_h
+
+#include <kcmodule.h>
+
+class QTabWidget;
+
+class KSimpleConfig;
+
+class KSoundPageConfig;
+class KAnswmachPageConfig;
+class KForwmachPageConfig;
+
+class KTalkdConfigModule : public KCModule
+{
+ Q_OBJECT
+
+public:
+
+ KTalkdConfigModule(QWidget *parent, const char *name);
+ virtual ~KTalkdConfigModule();
+
+ //void init();
+ void load();
+ void save();
+ void defaults();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+
+private:
+ KSimpleConfig *config;
+ KSimpleConfig *announceconfig;
+
+ QTabWidget *tab;
+
+ KSoundPageConfig *soundpage;
+ KAnswmachPageConfig *answmachpage;
+ KForwmachPageConfig *forwmachpage;
+};
+
+#endif
+
diff --git a/ktalkd/kcmktalkd/soundpage.cpp b/ktalkd/kcmktalkd/soundpage.cpp
new file mode 100644
index 00000000..bbd2431c
--- /dev/null
+++ b/ktalkd/kcmktalkd/soundpage.cpp
@@ -0,0 +1,330 @@
+/*
+ * soundpage.cpp - Sound settings for KTalkd
+ *
+ * Copyright (C) 1998 David Faure, faure@kde.org
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "soundpage.h"
+#include <config.h>
+
+#include <stdlib.h> //for setenv
+
+#include <qgroupbox.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qcheckbox.h>
+
+#include <klineedit.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kaudioplayer.h>
+#include <kmessagebox.h>
+#include <kurlrequester.h>
+#include <kurldrag.h>
+#include <kdialog.h>
+
+/* Lots of stuff taken from syssound.cpp */
+
+KSoundPageConfig::KSoundPageConfig( QWidget *parent, const char* name,
+ KSimpleConfig *_config, KSimpleConfig *_announceconfig)
+ : KCModule (parent, name)
+{
+ if (!_config) {
+ delete_config = true;
+ config = new KSimpleConfig("ktalkdrc");
+ announceconfig = new KSimpleConfig("");
+ }
+ else {
+ delete_config = false;
+ config = _config;
+ announceconfig = _announceconfig;
+ }
+
+ QBoxLayout* toplay = new QVBoxLayout(this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+
+ QGroupBox* extprg_box = new QGroupBox(this);
+ extprg_box->setColumnLayout( 0, Qt::Horizontal );
+ toplay->addWidget(extprg_box);
+
+ QGridLayout* l = new QGridLayout(extprg_box->layout());
+
+ extprg_edit = new KURLRequester(extprg_box);
+ l->addWidget(extprg_edit, 2, 4);
+
+ extprg_label = new QLabel(extprg_edit,i18n("&Announcement program:"), extprg_box);
+ l->addWidget(extprg_label, 2, 2);
+
+ client_edit = new KURLRequester(extprg_box);
+ l->addWidget(client_edit, 4, 4);
+
+ client_label = new QLabel(client_edit,i18n("&Talk client:"), extprg_box);
+ l->addWidget(client_label, 4, 2);
+
+ toplay->addSpacing(10);
+
+ sound_cb = new QCheckBox(i18n("&Play sound"), this);
+ toplay->addWidget(sound_cb);
+
+ QGroupBox* sound_box = new QGroupBox(this);
+ toplay->addWidget(sound_box);
+
+ QBoxLayout* lay = new QVBoxLayout(sound_box, 10, 10);
+
+ int edit_h = client_edit->height(); // The height of a QLineEdit
+
+ sound_list = new QListBox(sound_box);
+ sound_list->setMinimumHeight( 3 * edit_h );
+ sound_list->setAcceptDrops(true);
+ sound_list->installEventFilter(this);
+
+ sound_label = new QLabel(sound_list,i18n("&Sound file:"), sound_box);
+ lay->addWidget(sound_label);
+
+ QBoxLayout* l2 = new QHBoxLayout(lay, 10);
+ l2->addWidget(sound_list);
+
+ btn_test = new QPushButton(i18n("&Test"), sound_box);
+ l2->addWidget(btn_test);
+
+ sound_tip = new QLabel(
+ i18n("Additional WAV files can be dropped onto the sound list."),
+ sound_box);
+
+ lay->addWidget(sound_tip);
+
+ QStringList strlist( KGlobal::dirs()->findAllResources( "sound" ) );
+ sound_list->insertStringList( strlist );
+
+ load();
+
+ connect(sound_cb, SIGNAL(clicked()), this, SLOT(soundOnOff()));
+ connect(btn_test, SIGNAL(clicked()), this, SLOT(playCurrentSound()));
+
+ // emit changed(true) on changes
+ connect(extprg_edit->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+ connect(client_edit->lineEdit(), SIGNAL(textChanged(const QString&)), this, SLOT(slotChanged()));
+}
+
+KSoundPageConfig::~KSoundPageConfig( ) {
+ if (delete_config) {
+ delete config;
+ delete announceconfig;
+ }
+ delete extprg_label;
+ delete extprg_edit;
+ delete client_label;
+ delete client_edit;
+ delete sound_cb;
+ delete sound_label;
+ delete sound_list;
+ delete sound_tip;
+ delete btn_test;
+
+}
+
+void KSoundPageConfig::slotChanged() {
+ emit changed(true);
+}
+
+bool KSoundPageConfig::eventFilter(QObject* /*o*/, QEvent* e)
+{
+ if (e->type() == QEvent::DragEnter) {
+ sound_listDragEnterEvent((QDragEnterEvent *) e);
+ return true;
+ }
+
+ if (e->type() == QEvent::Drop) {
+ sound_listDropEvent((QDropEvent *) e);
+ return true;
+ }
+
+ return false;
+}
+
+
+void KSoundPageConfig::sound_listDragEnterEvent(QDragEnterEvent* e)
+{
+ e->accept(KURLDrag::canDecode(e));
+}
+
+void KSoundPageConfig::sound_listDropEvent(QDropEvent* e){
+
+ KURL::List list;
+ // This should never happen, but anyway...
+ if(!KURLDrag::decode(e, list))
+ return;
+
+ // For now, we do only accept FILES ending with .wav...
+ for( KURL::List::ConstIterator it = list.begin();
+ it != list.end(); ++it)
+ {
+ const KURL &url = *it;
+
+ if (!url.isLocalFile()) { // for now, only file URLs are supported
+
+ KMessageBox::sorry(this,
+ i18n("This type of URL is currently unsupported "\
+ "by the KDE system sound module."),
+ i18n("Unsupported URL"));
+
+ }
+ else
+ { // Now check for the ending ".wav"
+
+ if (url.path().right(4).upper() != ".WAV") {
+ QString msg = i18n("%1\ndoes not appear "\
+ "to be a WAV file.").arg(url.path());
+
+ KMessageBox::sorry(this, msg, i18n("Improper File Extension"));
+
+ }
+ else
+ { // Hurra! Finally we've got a WAV file to add to the list
+
+ if (!addToSound_List(url.path())) {
+ // did not add file because it is already in the list
+ QString msg = i18n("The file %1 is already in the list").arg(url.path());
+
+ KMessageBox::information(this, msg, i18n("File Already in List"));
+
+ }
+ }
+ }
+ }
+}
+
+int KSoundPageConfig::findInSound_List(QString sound) {
+// Searches for <sound> in sound_list. Returns position or -1 if not found
+
+ bool found = false;
+
+ int i = 0;
+ int len = sound_list->count();
+
+ while ((!found) && (i < len)) {
+
+ found = sound == sound_list->text(i);
+ i++;
+ }
+ return (found ? i-1 : -1);
+}
+
+bool KSoundPageConfig::addToSound_List(QString sound){
+// Add "sound" to the sound list, but only if it is not already there
+
+ bool found = (findInSound_List(sound) != -1);
+ if (!found) { // Fine, the sound is not already in the sound list!
+
+ QString *tmp = new QString(sound); // take a copy...
+ sound_list->insertItem(*tmp);
+ sound_list->setTopItem(sound_list->count()-1);
+
+ slotChanged();
+ }
+
+ return !found;
+}
+
+void KSoundPageConfig::playCurrentSound()
+{
+ QString hlp, sname;
+ int soundno;
+
+ soundno = sound_list->currentItem();
+ if (soundno != -1) {
+ sname = sound_list->text(soundno);
+ if (sname[0] != '/')
+ KAudioPlayer::play(locate("sound", sname));
+ else
+ KAudioPlayer::play(sname);
+ }
+}
+
+void KSoundPageConfig::soundOnOff()
+{
+ bool b = sound_cb->isChecked();
+ sound_label->setEnabled(b);
+ sound_list->setEnabled(b);
+ btn_test->setEnabled(b);
+ sound_tip->setEnabled(b);
+
+ slotChanged();
+}
+
+void KSoundPageConfig::defaults() {
+
+ extprg_edit->lineEdit()->setText(KStandardDirs::findExe("ktalkdlg"));
+ client_edit->lineEdit()->setText(KStandardDirs::findExe("konsole")+" -e talk");
+ // will be ktalk when ktalk is in CVS.
+ sound_cb->setChecked(true);
+
+ // Activate things according to configuration
+ soundOnOff();
+}
+
+void KSoundPageConfig::load() {
+
+ config->setGroup("ktalkd");
+ announceconfig->setGroup("ktalkannounce");
+
+ setenv("KDEBINDIR",QFile::encodeName(KStandardDirs::kde_default("exe")),false/*don't overwrite*/);
+ // for the first reading of the config file
+
+ extprg_edit->lineEdit()->setText(config->readPathEntry("ExtPrg",
+ KStandardDirs::findExe("ktalkdlg")));
+ client_edit->lineEdit()->setText(announceconfig->readPathEntry("talkprg",
+ KStandardDirs::findExe("konsole")+" -e talk")); // will be ktalk when ktalk is in CVS
+
+ bool b = announceconfig->readBoolEntry("Sound",true/*default value*/);
+ sound_cb->setChecked(b);
+
+ const QString soundFile = announceconfig->readPathEntry("SoundFile");
+ if (!soundFile.isEmpty())
+ {
+ int pos = findInSound_List(soundFile);
+ if (pos != -1) sound_list->setSelected(pos,true);
+ else {
+ addToSound_List(soundFile);
+ sound_list->setSelected(sound_list->count()-1,true);
+ }
+ } else { sound_list->setSelected(0,true); }
+
+ // Activate things according to configuration
+ soundOnOff();
+
+ emit changed(false);
+}
+
+void KSoundPageConfig::save() {
+
+ config->setGroup("ktalkd");
+ config->writePathEntry("ExtPrg", extprg_edit->lineEdit()->text());
+ config->sync();
+ announceconfig->setGroup("ktalkannounce");
+ announceconfig->writePathEntry("talkprg", client_edit->lineEdit()->text());
+ announceconfig->writeEntry("Sound", sound_cb->isChecked());
+ announceconfig->writePathEntry("SoundFile",sound_list->text(sound_list->currentItem()));
+ announceconfig->sync();
+}
+
+#include "soundpage.moc"
diff --git a/ktalkd/kcmktalkd/soundpage.h b/ktalkd/kcmktalkd/soundpage.h
new file mode 100644
index 00000000..b5f713e4
--- /dev/null
+++ b/ktalkd/kcmktalkd/soundpage.h
@@ -0,0 +1,90 @@
+/*
+ * soundpage.h
+ *
+ * Copyright (c) 1998 David Faure
+ *
+ * Requires the Qt widget libraries, available at no cost at
+ * http://www.troll.no/
+ *
+ * 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.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KCONTROL_KSOUNDPAGE_H__
+#define __KCONTROL_KSOUNDPAGE_H__
+
+#include <qdir.h>
+/* has to be before everything because of #define Unsorted 0 in X11/X.h !! */
+
+#include <qobject.h>
+
+#include <kcmodule.h>
+
+
+class QDragMoveEvent;
+class QDragEnterEvent;
+class QDropEvent;
+
+class KSimpleConfig;
+class KURLRequester;
+class QCheckBox;
+class QLabel;
+class QListBox;
+class QPushButton;
+
+class KSoundPageConfig : public KCModule
+{
+ Q_OBJECT
+
+public:
+ KSoundPageConfig( QWidget *parent=0, const char* name=0,
+ KSimpleConfig *config=0, KSimpleConfig *announceconfig=0);
+ ~KSoundPageConfig( );
+
+ void load();
+ void save();
+ void defaults();
+
+ bool eventFilter(QObject* o, QEvent* e);
+
+private slots:
+ void soundOnOff();
+ void playCurrentSound();
+ void slotChanged();
+
+ // Sound DnD
+ void sound_listDragEnterEvent(QDragEnterEvent* e);
+ void sound_listDropEvent(QDropEvent* e);
+
+private:
+ KSimpleConfig *config;
+ KSimpleConfig *announceconfig;
+ bool delete_config;
+
+ QLabel *extprg_label;
+ KURLRequester *extprg_edit;
+ QLabel *client_label;
+ KURLRequester *client_edit;
+ QCheckBox *sound_cb;
+ QLabel *sound_label;
+ QListBox *sound_list;
+ QLabel *sound_tip;
+ QPushButton *btn_test;
+
+ int findInSound_List(QString sound);
+ bool addToSound_List(QString sound);
+};
+
+#endif
+
diff --git a/ktalkd/ktalkd/.talkdrc b/ktalkd/ktalkd/.talkdrc
new file mode 100644
index 00000000..b3804dc3
--- /dev/null
+++ b/ktalkd/ktalkd/.talkdrc
@@ -0,0 +1,78 @@
+# .talkdrc Config file for ktalkd when used without X/KDE.
+
+# If your E-Mail inbox is not <logid@localhost> (where logid is the
+# name you use to log in), uncomment the next line and place the
+# E-Mail address of your inbox there.
+#Mail:
+
+# Set to 1 to activate answering machine.
+# Will work only if your administrator has enabled it.
+Answmach: 1
+
+# Change this to customize the message displayed by the answering machine
+# when you receive a request while you're away. You may have up to 9 lines.
+Msg1: Hello. You're connected with the talk program answering machine.
+Msg2: I'm away from the computer at the moment.
+Msg3: Please leave a message and quit normally at the end of it.
+Msg4: - -
+Msg5: There's no way to delete across lines. Even if your talk program
+Msg6: allows you to cursor-around. Please use only normal keys and
+Msg7: backspace. Otherwise your note may be unreadable.
+
+# Subject of the mail you'll receive. '%s' will be replaced by the name of
+# the caller, qualified with their hostname.dom, presuming that they have
+# valid DNS.
+Subj: %s tried to "talk" you.
+
+# First line of the mail you'll receive. '%s' will be replaced by the
+# complete address of the caller.
+Head: Message left in the answering machine, by %s:
+
+# Do you wish to receive an empty mail if the caller didn't leave any message ?
+# (If "1", you'll only know who called you)
+EmptyMail: 1
+
+# Set this to 'off' if all you want is a beep to notify you of talk
+# requests, to 'on' if you want to play an audio file instead.
+Sound: on
+
+# Define this to the full path of the sound file you wish to
+# have played when you receive talk requests. It may be of
+# any format, as long as the player defined below is capable
+# of playing it.
+SoundFile: /usr/lib/talkd/talk.wav
+
+# Set this to the command you will be using to play audio
+# files. This can be any .wav, .au, .snd or whatever player,
+# just so long as it will play the format that your chosen
+# audio file is in.
+SoundPlayer: /usr/local/bin/wavplay
+SoundPlayerOpt: -q
+# ==> SoundPlayer + SoundPlayerOpt = /usr/local/bin/wavplay -q
+
+########### Edit below to set up a forward ###########
+
+# Enable forward by uncommenting and editing this line
+#Forward: user@host
+
+# Choose forward method :
+# None is perfect, they all have pros (+) and cons (-).
+#
+# FWA : Forward announcement only. Direct connection. Not recommended.
+# (+) You know who is the caller, but
+# (-) Caller will have to respond to an announcement from you. Annoying.
+# (-) Don't use if you have an answering machine on your 'away' location
+# (The answering machine can't popup an announcement, it would be confusing!)
+#
+# FWR : Forward all requests, changing info when necessary. Direct connection.
+# (+) Caller won't know that you're away, but
+# (-) You won't really know who's the caller - only his username,
+# (so you might see "talk from Wintalk@my_host")
+#
+# FWT : Forward all requests and take the talk. No direct connection.
+# (+) Same as above, but works also if you and caller can't be in direct
+# contact one with the other (e.g. firewall).
+# (+) You'll be told who's really talking to you when you accept the talk
+# (-) But as in FWR, you won't know his machine name in the announcement
+#
+#ForwardMethod: FWR
diff --git a/ktalkd/ktalkd/Makefile.am b/ktalkd/ktalkd/Makefile.am
new file mode 100644
index 00000000..7338b06f
--- /dev/null
+++ b/ktalkd/ktalkd/Makefile.am
@@ -0,0 +1,41 @@
+## -*- makefile -*-
+# Ktalkd - Makefile.am
+
+SUBDIRS = machines
+
+bin_PROGRAMS = ktalkd
+EXTRA_HEADERS = readcfg++.h
+INCLUDES = $(all_includes)
+ktalkd_DEPS = machines/libmach.a
+
+#for extra warnings during compilation :
+#KDE_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
+
+###########################################################
+
+# Config file location
+TALKD_CONF = $(kde_confdir)/ktalkdrc
+TALKD_CONF_NAME = ktalkdrc
+AM_CPPFLAGS = -DHAVE_KDE
+ktalkd_SOURCES = print.c repairs.c \
+ announce.cpp process.cpp readcfg++.cpp table.cpp talkd.cpp \
+ find_user.cpp threads.cpp options.cpp unixsock.cpp
+ktalkd_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+ktalkd_LDADD = machines/libmach.a $(LIBBSD) $(LIB_KDECORE) $(LIBSOCKET)
+
+###########################################################
+
+
+EXTRA_DIST = .talkdrc talkd.conf ktalkdrc
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(kde_confdir)
+ $(mkinstalldirs) $(DESTDIR)$(kde_sounddir)
+ $(INSTALL_DATA) $(srcdir)/ktalkd.wav $(DESTDIR)$(kde_sounddir)
+ @echo "**************************************************************************"
+ @echo
+ @if [ -f $(DESTDIR)$(TALKD_CONF) ]; then \
+ echo "Please check $(TALKD_CONF) to be up-to-date."; \
+ else \
+ $(INSTALL_DATA) $(srcdir)/$(TALKD_CONF_NAME) $(DESTDIR)$(TALKD_CONF); \
+ fi
diff --git a/ktalkd/ktalkd/announce.cpp b/ktalkd/ktalkd/announce.cpp
new file mode 100644
index 00000000..d21f27f4
--- /dev/null
+++ b/ktalkd/ktalkd/announce.cpp
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/* Autoconf: */
+#include <config.h>
+
+// strange symbol for HP-UX. It should not hurt on other systems.
+#ifndef notdef
+#define notdef 1
+#endif
+
+#include "includ.h"
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <time.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <netdb.h>
+#include "proto.h"
+#include "announce.h"
+#include "readconf.h"
+#include "defs.h"
+#include "threads.h"
+#include "unixsock.h"
+
+#ifdef HAVE_SGTTY_H
+#include <sgtty.h>
+#else
+#ifdef HAVE_BSD_SGTTY_H
+#include <bsd/sgtty.h>
+#endif
+#endif
+
+#define ktalkdmax(a,b) ( (a) > (b) ? (a) : (b) )
+#define N_LINES 5
+
+/*
+ * Announce an invitation to talk.
+ *
+ * Because the tty driver insists on attaching a terminal-less
+ * process to any terminal that it writes on, we must fork a child
+ * to protect ourselves
+ */
+int announce(NEW_CTL_MSG *request, const char *remote_machine, char *disp, int
+ usercfg, char * callee) {
+
+ int pid, status;
+ int returncode;
+
+ if (strstr(remote_machine,"\033")) {
+ syslog(LOG_WARNING,
+ "blocked VT100 FLASH BOMB to user: %s (from field in packet contains bomb)",
+ request->r_name);
+ } else if (strstr(request->l_name,"\033")) {
+ syslog(LOG_WARNING,
+ "blocked VT100 FLASH BOMB to user: %s (apparently from: %s)",
+ request->r_name, remote_machine);
+ } else {
+ if ((pid = fork())) {
+ /* we are the parent, so wait for the child */
+ if (pid == -1) /* the fork failed */
+ return (FAILED);
+ status = wait_process(pid);
+ if ((status&0377) > 0) /* we were killed by some signal */
+ return (FAILED);
+ /* Get the second byte, this is the exit/return code */
+ return ((status >> 8) & 0377);
+ }
+ /* we are the child, go and do it */
+
+ struct passwd * pw_buf;
+ /** Change our uid. Once and for all that follows. */
+ pw_buf = getpwnam(request->r_name);
+
+ /* XXX: Not good
+ * You should also set the supplementary groups using
+ * setgroups/initgroups
+ */
+ if (setgid(pw_buf -> pw_gid) || setuid(pw_buf -> pw_uid)) {
+ syslog(LOG_WARNING, "Warning: setgid/setuid as %s: %s", request->r_name, strerror(errno));
+ _exit(1);
+ }
+
+ /** Also set $HOME once and for all that follows (not used for text
+ announce, but doesn't harm) */
+ setenv("HOME", pw_buf -> pw_dir, 1);
+
+#ifdef HAVE_KDE
+ returncode = try_Xannounce(request, remote_machine, disp, usercfg, callee);
+ if (returncode != SUCCESS)
+#endif
+ returncode = print_std_mesg( request, remote_machine, usercfg, 0 );
+ _exit(returncode);
+
+ }
+ return (FAILED);
+}
+
+#ifdef HAVE_KDE
+
+int try_Xannounce(NEW_CTL_MSG *request, const char *remote_machine,
+ char *disp, int usercfg, char * callee) {
+ int readPipe[2];
+ char ch_aux;
+ int pid;
+ struct timeval clock;
+ struct timezone zone;
+ struct tm *localclock;
+ char line_buf[N_CHARS];
+
+ char * adisp_begin, *disp_ptr, * disp_end;
+ char extprg [S_MESSG]; /* Program used for notification. */
+ int Xannounceok = 0; /* Set to 1 if at least one Xannounceok succeeded */
+
+#ifdef PROC_FIND_USER
+ if ((Options.XAnnounce) && (strlen(disp) >= 2)) {
+#else
+ if ((Options.XAnnounce) && ((int) request->r_tty[3] > (int) 'f')) {
+#endif /* PROC_FIND_USER */
+
+ /*
+ * He is in X (probably :-) -> try to connect with external program
+ */
+
+ /* No more needed : ktalkdlg will play sound itself.
+ Other external programs can do whatever they want...
+ Maybe a config for this could be useful to know whether the
+ extprg handles the sound announcement...
+ sound_or_beep(usercfg); */
+
+ gettimeofday(&clock, &zone);
+ localclock = localtime( (const time_t *) &clock.tv_sec );
+ (void)snprintf(line_buf, N_CHARS, "%s@%s", request->l_name,
+ remote_machine);
+
+#ifndef DONT_TRY_KTALK // never defined. Define if you want...
+ Xannounceok = sendToKtalk ( request->r_name, line_buf );
+ if (!Xannounceok) {
+#endif
+ /* XXX: I assume extprg comes from a user config file? Then
+ * strcpy is quire a bad idea
+ */
+ if ((!usercfg) || (!read_user_config("ExtPrg", extprg, S_MESSG)))
+ /* try to read extprg from user config file, otherwise : */
+ strcpy(extprg,Options.extprg); /* take default */
+
+ /* disp can be a LIST of displays, such as ":0 :1", or
+ ":0 hostname:0". Let's announce on ALL displays found.
+ disp_end is the end of the display list (saved because we'll
+ insert zeros).
+ adisp_begin will point to a display in the list.
+ disp_ptr will go char by char through disp.*/
+
+ adisp_begin=disp;
+ disp_end=disp+strlen(disp);
+ for (disp_ptr=disp; disp_ptr<disp_end; disp_ptr++) {
+ if (*disp_ptr==' ') { /* the final display will be taken also,
+ as a final space has been inserted in
+ find_X_process */
+ *disp_ptr='\0'; /* mark the end of the display name in the
+ list */
+
+ pipe( readPipe );
+ switch (pid = fork()) {
+ case -1: {
+ syslog(LOG_ERR,"Announce : Fork failed ! - %s", strerror(errno));
+ return ( FAILED );
+ }
+ case 0: {
+
+ close(readPipe[0]); // we do not need to read the pipe in the child
+
+ /* set DISPLAY */
+ setenv("DISPLAY", adisp_begin, 1);
+
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG, "Trying to run '%s' at '%s' as '%s'", extprg,
+ getenv("DISPLAY"), request->r_name );
+ if (callee) ktalk_debug("With mention of callee : %s",callee);
+ }
+ /*
+ * point stdout of external program to the pipe
+ * announce a talk request by execing external program
+ */
+ dup2( readPipe[1], STDOUT_FILENO );
+
+ if (callee)
+ execl( extprg, extprg, line_buf, callee, (void *)0 );
+ else
+ execl( extprg, extprg, line_buf, (void *)0 );
+
+ /*
+ * failure - tell it to father
+ * exit with status=42
+ */
+ syslog(LOG_WARNING, "error while execl(%s): %s", extprg, strerror(errno));
+ _exit( 42 );
+ }
+ default: {
+ /* Register the new process for waitpid */
+ new_process();
+ /*
+ * I don't know any way how to find that external program run ok
+ * so parent returns always SUCCESS
+ * Well I know a solution - read the pipe :-) - It blocks, so
+ * we can't wait the reaction of the user
+ *
+ * Danny: made the pipe non blocking and check for exit-status
+ */
+
+ int exited = 0, c = 0;
+ int status = -1; // Set to an unlikely value (no program returns -1, Right?)
+ ch_aux='0'; // initialise ch_aux
+
+ /* make the pipe non-blocking:
+ * does this work on systems other than BSD&Linux??
+ */
+ if (fcntl(readPipe[0], F_SETFL, O_NONBLOCK)== -1)
+ ktalk_debug("Failed to make pipe non-blocking!!");
+
+ /* Stop this loop when:
+ * the child exited with a value > 0 AND the pipe is empty
+ * Maybe we can add a time-out? (what is reasonable)
+ */
+ while (c > 0 || (exited < 1)){
+ if ( exited < 1 )
+ {
+ status = wait_process(pid);
+ exited += (WIFEXITED(status) || WIFSIGNALED(status)); // non-zero when exited OR non-caught signal
+ }
+ c = read( readPipe[0], &ch_aux, 1 ); // this is many times -1 because no data in pipe
+
+ if (ch_aux == '#') Xannounceok = 1;
+ }
+
+ /* Check whether the # was returned, if not, return the exit status
+ * 0 means: exit normally, but no # received-> do a text announce as well.
+ * 1 can be for instance: cannot open X display
+ * 42 is: failure to start external program.
+ */
+ if (Xannounceok == 1) {
+ ktalk_debug("OK - Child process responded");
+ } else
+ ktalk_debug("External program failed with exit status: %i", WEXITSTATUS(status));
+
+ // close the pipes: (is this necessary?):
+ close( readPipe[0] );
+ close( readPipe[1] );
+
+ } /* default */
+ } /* switch */
+ adisp_begin=disp_ptr+1;
+ } /* if */
+ } /* for */
+#ifndef DONT_TRY_KTALK
+ } // if
+#endif
+
+ if (Xannounceok) {
+#ifndef NO_TEXT_ANNOUNCE_IF_X
+ // never defined. Define it if you don't want text announce in
+ // addition to X announce
+ if ((request->r_tty[0]!='\0') && ((int)request->r_tty[3]<(int)'f')) {
+ // Not a pseudo terminal (such as an xterm, ...)
+ ktalk_debug("Doing also text announce on %s",request->r_tty);
+ print_std_mesg(request, remote_machine, usercfg, 1 /*force no sound*/);
+ }
+#endif
+ return (SUCCESS);
+ }
+ }
+ return (FAILED);
+}
+#endif /* HAVE_KDE */
+
+/*
+ * See if the user is accepting messages. If so, announce that
+ * a talk is requested.
+ */
+
+int print_std_mesg( NEW_CTL_MSG *request, const char *remote_machine, int
+ usercfg, int force_no_sound ) {
+
+ char full_tty[32];
+ FILE *tf;
+ struct stat stbuf;
+
+ (void)snprintf(full_tty, sizeof(full_tty), "%s/%s", _PATH_DEV, request->r_tty);
+ if (access(full_tty, 0) != 0)
+ return (FAILED);
+ if ((tf = fopen(full_tty, "w")) == 0)
+ return (PERMISSION_DENIED);
+ /*
+ * On first tty open, the server will have
+ * it's pgrp set, so disconnect us from the
+ * tty before we catch a signal.
+ */
+ ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0);
+ if (fstat(fileno(tf), &stbuf) < 0)
+ return (PERMISSION_DENIED);
+ if ((stbuf.st_mode&020) == 0)
+ return (PERMISSION_DENIED);
+ print_mesg(tf, request, remote_machine, usercfg, force_no_sound);
+ fclose(tf);
+ return (SUCCESS);
+}
+
+/*
+ * Build a block of characters containing the message.
+ * It is sent blank filled and in a single block to
+ * try to keep the message in one piece if the recipient
+ * in in vi at the time
+ */
+void print_mesg(FILE * tf, NEW_CTL_MSG * request, const char *
+ remote_machine, int usercfg, int force_no_sound)
+{
+ struct timeval clock;
+ struct timezone zone;
+ struct tm *localclock;
+ char line_buf[N_LINES][N_CHARS];
+ char buffer [N_CHARS];
+ int sizes[N_LINES];
+ char big_buf[N_LINES*N_CHARS];
+ char *bptr, *lptr;
+ int i, j, max_size;
+
+ char * remotemach = new char[strlen(remote_machine)+1];
+ strcpy(remotemach,remote_machine);
+ /* We have to duplicate it because param_remote_machine is contained
+ in the hostent structure, and will be erased by gethostbyname. */
+
+ char localname[SYS_NMLN];
+ strcpy(localname,gethostbyname(Options.hostname)->h_name);
+ /* We have to duplicate localname also. Same reasons as above ! */
+
+ const char *remotename = gethostbyname(remotemach)->h_name;
+
+ i = 0;
+ max_size = 0;
+ gettimeofday(&clock, &zone);
+ localclock = localtime( (const time_t *) &clock.tv_sec );
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, Options.announce1,
+ localclock->tm_hour , localclock->tm_min );
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ snprintf(buffer, N_CHARS, "%s@%s", request->l_name, remotename);
+ snprintf(line_buf[i], N_CHARS, Options.announce2, buffer);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+
+ if (!(strcmp(localname,remotename))) {
+ snprintf(line_buf[i], N_CHARS, Options.announce3, request->l_name);
+ } else {
+ snprintf(line_buf[i], N_CHARS, Options.announce3, buffer);
+ }
+
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ (void)snprintf(line_buf[i], N_CHARS, " ");
+ sizes[i] = strlen(line_buf[i]);
+ sizes[i] = strlen(line_buf[i]);
+ max_size = ktalkdmax(max_size, sizes[i]);
+ i++;
+ bptr = big_buf;
+ if (!force_no_sound) /* set if a X announce has been done */
+ if (sound_or_beep(usercfg)) /* if no sound then : */
+ *bptr++ = ''; /* send something to wake them up */
+ *bptr++ = '\r'; /* add a \r in case of raw mode */
+ *bptr++ = '\n';
+ for (i = 0; i < N_LINES; i++) {
+ /* copy the line into the big buffer */
+ lptr = line_buf[i];
+ while (*lptr != '\0')
+ *(bptr++) = *(lptr++);
+ /* pad out the rest of the lines with blanks */
+ for (j = sizes[i]; j < max_size + 2; j++)
+ *(bptr++) = ' ';
+ *(bptr++) = '\r'; /* add a \r in case of raw mode */
+ *(bptr++) = '\n';
+ }
+ *bptr = '\0';
+ fprintf(tf, "%s", big_buf);
+ fflush(tf);
+ ioctl(fileno(tf), TIOCNOTTY, (struct sgttyb *) 0);
+ delete remotemach;
+}
+
+int play_sound(int usercfg)
+{
+ int pid, status, returncode;
+ char sSoundPlayer[S_CFGLINE], sSoundFile[S_CFGLINE];
+ char sSoundPlayerOpt[S_CFGLINE];
+ /* We must absolutely read the configuration before forking,
+ because the father will close the file soon !! */
+ if ((!usercfg) || (!read_user_config("SoundPlayer",sSoundPlayer,S_CFGLINE)))
+ strcpy(sSoundPlayer,Options.soundplayer);
+ ktalk_debug(sSoundPlayer);
+ if ((!usercfg) || (!read_user_config("SoundPlayerOpt",sSoundPlayerOpt,S_CFGLINE)))
+ strcpy(sSoundPlayerOpt,Options.soundplayeropt);
+ ktalk_debug(sSoundPlayerOpt);
+ if ((!usercfg) || (!read_user_config("SoundFile",sSoundFile,S_CFGLINE)))
+ strcpy(sSoundFile,Options.soundfile);
+ ktalk_debug(sSoundFile);
+
+ if ((pid = fork())) {
+ /* we are the parent, so wait for the child */
+ if (pid == -1) /* the fork failed */
+ {
+ syslog(LOG_ERR,"Fork before play_sound : FAILED.");
+ return (FAILED);
+ }
+ status = wait_process(pid);
+ if ((status&0377) > 0) /* we were killed by some signal */
+ return (FAILED);
+ /* Get the second byte, this is the exit/return code */
+ return ((status >> 8) & 0377);
+ }
+ /* we are the child, go and do it */
+
+ if (strlen(sSoundPlayerOpt)>0)
+ returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundPlayerOpt,sSoundFile, (void *)0);
+ else
+ returncode = execl(sSoundPlayer,sSoundPlayer/*arg0*/,sSoundFile, (void *)0);
+
+ ktalk_debug(strerror(errno));
+ syslog(LOG_ERR,"ERROR PLAYING SOUND FILE !!!");
+ syslog(LOG_ERR, "%s,%s,%s",sSoundPlayer,sSoundFile,sSoundPlayerOpt);
+ syslog(LOG_ERR,"RETURN-CODE : %d",returncode);
+
+ _exit(returncode);
+ /*NOTREACHED*/
+ return 0;
+}
+
+/*
+ * Calls play_sound if necessary. Returns 1 if a tty beep is needed.
+ */
+int sound_or_beep(int usercfg)
+{
+ int bSound;
+ int ret;
+ if ((!usercfg) || (!read_bool_user_config("Sound",&bSound)))
+ bSound=Options.sound;
+
+ ktalk_debug("Sound option in sound_or_beep : %d",bSound);
+
+ if (bSound)
+ {
+ /* try to play sound, otherwise beeps (if not in X) */
+ ret = play_sound(usercfg); /* 1 = it didn't work. ^G needed */
+ }
+ else ret = 1; /* ^G needed */
+ return ret;
+}
diff --git a/ktalkd/ktalkd/announce.h b/ktalkd/ktalkd/announce.h
new file mode 100644
index 00000000..7411cdb4
--- /dev/null
+++ b/ktalkd/ktalkd/announce.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+#ifndef ANNOUNCE_H
+#define ANNOUNCE_H
+
+int announce(NEW_CTL_MSG * request, const char *remote_machine, char *disp, int usercfg, char * callee);
+
+#ifdef HAVE_KDE
+int try_Xannounce(NEW_CTL_MSG *request, const char *remote_machine,
+ char *disp, int usercfg, char * callee);
+#endif
+
+int print_std_mesg(NEW_CTL_MSG *request, const char *remote_machine, int usercfg, int force_no_sound);
+void print_mesg(FILE * tf,NEW_CTL_MSG * request, const char * remote_machine, int usercfg, int force_no_sound);
+int sound_or_beep(int usercfg);
+
+/* Maximum char length for announcement lines */
+#define N_CHARS 120
+
+#endif
diff --git a/ktalkd/ktalkd/defs.h b/ktalkd/ktalkd/defs.h
new file mode 100644
index 00000000..de4a96a9
--- /dev/null
+++ b/ktalkd/ktalkd/defs.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef __DEFS_H
+#define __DEFS_H
+
+#include "includ.h"
+
+/* Configuration file-read buffer sizes */
+#define S_CFGLINE 80
+
+ /* from answmach/io.c */
+#define S_MESSG 120
+#define S_COMMAND 100
+
+#define S_INVITE_LINES 200
+
+#define DISPLAYS_LIST_MAX 200
+#define DISPLAY_MAX 50
+
+/*
+ * Invitation displayed by the answering machine when the caller connects.
+ * Add \n at the end of any line you want it to display,
+ * and \ at the end of any line of the definition of INVITE_LINES
+ * They are not necessarily the same ...
+ *
+ * ANNOUNCE* are the 3 lines displayed on the terminal / on
+ * the console, to announce the request.
+ */
+
+#define ANNOUNCE1 "Message from Talk_Daemon at %d:%02d ..."
+#define ANNOUNCE2 "talk: connection requested by %s."
+#define ANNOUNCE3 "talk: respond with: talk %s"
+#define INVITE_LINES "Hello. You're connected to a talk answering \
+machine.\nThe person you have paged isn't there at the moment.\n\
+Please leave a message and quit normally when finished.\n"
+
+/*Please check that INVITE_LINES is max S_INVITE_LINES chars*/
+
+/* Default message if N.E.U (non existent user) called */
+#define NEU_BANNER1 "The person you're asking to talk with is unknown at this host."
+#define NEU_BANNER2 "You may have mistyped the name, or network address. Try again"
+#define NEU_BANNER3 "or leave a message which will be sent to the system administrator."
+
+/* return value from process_request : */
+#define PROC_REQ_OK 0
+#define PROC_REQ_ERR 1
+#define PROC_REQ_FORWMACH 2
+#define PROC_REQ_ANSWMACH 3
+#define PROC_REQ_ANSWMACH_NOT_LOGGED 4
+#define PROC_REQ_ANSWMACH_NOT_HERE 5
+
+/* Min value to launch answer machine : */
+#define PROC_REQ_MIN_A 3
+
+#include "options.h"
+
+#endif /* __DEFS_H */
diff --git a/ktalkd/ktalkd/find_user.cpp b/ktalkd/ktalkd/find_user.cpp
new file mode 100644
index 00000000..948a4dba
--- /dev/null
+++ b/ktalkd/ktalkd/find_user.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "includ.h"
+#include <sys/param.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#define USE_UT_HOST
+#ifndef UT_HOSTSIZE
+#define UT_HOSTSIZE 12 /*whatever*/
+#undef USE_UT_HOST
+#endif
+
+#include <pwd.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <fstream>
+
+#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
+#include <dirent.h>
+#include <ctype.h>
+#include <errno.h>
+#endif
+
+#include "proto.h"
+#include "defs.h"
+
+#ifdef PROC_FIND_USER
+
+#define DISPLAYS_LIST_MAX 200
+#define DISPLAY_MAX 50
+
+/* get DISPLAY variable of a process */
+char *get_display(pid_t pid) {
+ static char buf[1024];
+
+ sprintf(buf, "/proc/%d/environ", pid);
+
+ std::ifstream ifstr(buf);
+ if (ifstr.fail()) return 0;
+ /* ktalk_debug("reading %s...", fname); */
+
+ char * dpy = 0;
+ while (!ifstr.eof()) {
+ ifstr.getline(buf, sizeof buf - 1, '\0');
+ if (ifstr.fail()) {
+ syslog(LOG_ERR, "getline: %s", strerror(errno));
+ return 0; /* read error */
+ }
+ if (!strncmp("DISPLAY=", buf, 8)) {
+ int l = strlen(buf + 8);
+ if( l >= DISPLAY_MAX )
+ return 0;
+ if (l >= 2 && l < (int) sizeof buf - 1) {
+ dpy = new char[ l+2 ];
+ strcpy(dpy," ");
+ strcat(dpy, buf + 8); /* returns " "+dpy */
+ /* ktalk_debug("- %s",dpy); */
+ }
+ break;
+ }
+ }
+ return dpy;
+}
+
+/* As utmp isn't reliable (neither xdm nor kdm logs into it ! :( ),
+ we have to look at processes directly. /proc helps a lot, under linux.
+ How to do it under other unixes ? */
+#ifdef ALL_PROCESSES_AND_PROC_FIND_USER
+
+/* awful global variable, but how to pass it to select_process() otherwise ?
+ scandir() doesn't allow this, of course... */
+unsigned int user_uid;
+
+/* selection function used by scandir */
+int select_process(
+#ifdef SCANDIR_NEEDS_CONST
+const struct dirent *direntry
+#else
+struct dirent *direntry
+#endif
+)
+{
+ /* returns 1 if username owns <direntry> */
+ struct stat statbuf;
+
+ /* make absolute path (would sprintf be better ?)*/
+ char abspath[20]="/proc/";
+ if( strlen( direntry->d_name ) > 12 )
+ return 0;
+ strcat(abspath,direntry->d_name);
+
+ if (isdigit(direntry->d_name[0])) { /* starts with [0-9]*/
+ if (!stat(abspath, &statbuf)) { /* if exists */
+ if (S_ISDIR(statbuf.st_mode)) { /* and is a directory and */
+ if (statbuf.st_uid == user_uid) /* is owned by user_uid*/
+ {
+ /* We have to force errno=0, because otherwise, scandir will stop ! */
+ /* the problem is that glibc sets errno in getpwnam and syslog
+ (glibc-2.0.5c/6, with libstdc++ 2.7.2) */
+ errno=0;
+ return 1;
+ }
+ /* else ktalk_debug("st_uid=%d", statbuf.st_uid); */
+ } /* else ktalk_debug("st_mode=%d", statbuf.st_mode); */
+ } else ktalk_debug("stat error : %s", strerror(errno));
+ }
+
+ errno=0;
+ return 0;
+}
+
+/* scan /proc for any process owned by 'name'.
+ If DISPLAY is set, set 'disp'.
+
+ Called only if no X utmp entry found. */
+
+/*
+ * Major memory leak, never frees the memory allocated by scandir
+ * (why not use readdir() in the first place?
+ * Major buffer overflow: If I set my DISPLAY variable to a
+ * 1024 bytes string, it'll overflow displays_list.
+ */
+
+int find_X_process(char *name, char *disp) {
+ char displays_list[DISPLAYS_LIST_MAX] = " "; /* yes, one space */
+ char * dispwithblanks;
+ struct dirent **namelist;
+
+ struct passwd * pw = getpwnam(name);
+ ktalk_debug("find_X_process");
+ /* find uid */
+ if ((pw) && ((user_uid=pw->pw_uid)>10))
+ { /* uid<10 : no X detection because any suid program will be taken
+ as owned by root, not by its real owner */
+ /* scan /proc */
+ int n = scandir("/proc", &namelist, select_process, 0 /*no sort*/);
+ if (n < 0)
+ ktalk_debug("scandir: %s", strerror(errno));
+ else
+ while(n--)
+ {
+ /* find DISPLAY */
+ dispwithblanks = get_display(atoi(namelist[n]->d_name));
+ if (dispwithblanks) {
+ /* This way, if :0.0 is in the list, :0 is not inserted */
+ /* XXX Yes, but if I have two displays, one foo:0.0
+ * and one bar:0.0, then bar:0 is not caught by the
+ * check below. But this is just peanuts.
+ */
+ if (!strstr(displays_list,dispwithblanks))
+ { /* not already in the list? */
+ char * pointlocation=strstr(dispwithblanks,".");
+ if (pointlocation) *pointlocation='\0';
+ if (!strstr(displays_list,dispwithblanks)
+ && strlen(dispwithblanks)+strlen(displays_list)<DISPLAYS_LIST_MAX)
+ { /* display up to the '.' mustn't be already in the list */
+ strcat(displays_list,dispwithblanks+1); /* insert display (no ' ') */
+ strcat(displays_list," "); /* and a blank */
+ }
+ } /* if strtsr */
+ delete dispwithblanks;
+ } /* if dispwithblanks */
+ } /* while */
+ if (strlen(displays_list)>1)
+ {
+ strcpy(disp,displays_list+1); /* removes the leading white space
+ but leave the final one, needed by announce.c */
+ return 1;
+ }
+ } /* if pw */
+ return 0;
+}
+
+#endif /* ALL_PROCESSES_AND_PROC_FIND_USER */
+
+#ifdef UTMP_AND_PROC_FIND_USER
+
+/*
+ * Search utmp for the local user
+ *
+ * Priorities:
+ * login from xdm
+ * login from pseudo terminal with $DISPLAY set
+ * login from pseudo terminal
+ * other login
+ */
+#define PRIO_LOGIN 1 /* user is logged in */
+#define PRIO_PTY 2 /* user is logged in on a
+ pseudo terminal */
+#define PRIO_DISPLAY 3 /* user is logged in on a
+ pseudo terminal and has
+ $DISPLAY set. */
+#define PRIO_XDM 4 /* user is logged in from xdm */
+
+#define SCMPN(a, b) strncmp(a, b, sizeof (a))
+
+int find_user(char *name, char *tty, char *disp) {
+ struct utmp *ubuf;
+ int prio = 0, status = NOT_HERE;
+ struct stat statb;
+ char ftty[20+UT_LINESIZE];
+ char *ntty, *dpy;
+ char ttyFound[UT_LINESIZE] = "";
+ char dispFound[DISPLAYS_LIST_MAX] = "";
+
+ strcpy(ftty, _PATH_DEV);
+ ntty = ftty + strlen(ftty);
+ setutent();
+ while ((ubuf = getutent())) {
+ if ((ubuf->ut_type == USER_PROCESS) &&
+ (!SCMPN(ubuf->ut_name, name))) {
+
+ if (*tty == '\0') { /* no particular tty was requested */
+
+ if (Options.XAnnounce && ubuf->ut_line[0] == ':') {
+ /* this is a XDM login (really?). GREAT! */
+ syslog(LOG_DEBUG, "XDM login: %s at %s", name, ubuf->ut_line);
+ status = SUCCESS;
+ if (prio < PRIO_XDM) {
+ strcpy(dispFound, ubuf->ut_line);
+ strcat(dispFound, " ");
+ prio = PRIO_XDM;
+ }
+ continue;
+ }
+
+ strcpy(ntty, ubuf->ut_line);
+ if (stat(ftty, &statb) != 0 || (!(statb.st_mode & 020)))
+ {
+ ktalk_debug("Permission denied on %s", ntty);
+ continue; /* not a char dev */
+ }
+
+ /* device exists and is a character device */
+ status = SUCCESS;
+ if (Options.debug_mode) syslog(LOG_DEBUG, "Found %s at %s", name, ubuf->ut_line);
+ if (prio < PRIO_LOGIN) {
+ prio = PRIO_LOGIN;
+ strcpy(ttyFound, ubuf->ut_line);
+ *dispFound = '\0';
+ }
+
+ /* the following code is Linux specific...
+ * is there a portable way to
+ * 1) determine if a device is a pseudo terminal and
+ * 2) get environment variables of an arbitrary process?
+ */
+ if (strncmp("tty", ubuf->ut_line, 3) != 0 ||
+ !strchr("pqrstuvwxyzabcde", ubuf->ut_line[3]))
+ continue; /* not a pty */
+
+ /* device is a pseudo terminal (ex : a xterm) */
+ if (Options.debug_mode) syslog(LOG_DEBUG, "PTY %s, ut_host=%s",
+ ubuf->ut_line, ubuf->ut_host);
+ if (prio < PRIO_PTY) {
+ prio = PRIO_PTY;
+ strcpy(ttyFound, ubuf->ut_line);
+ strcpy(dispFound, ubuf->ut_host);
+ strcat(dispFound, " ");
+ }
+
+ dpy = get_display(ubuf->ut_pid);
+ if (!dpy) continue; /* DISPLAY not set or empty */
+
+ /* $DISPLAY is set. */
+ if (Options.debug_mode) syslog(LOG_DEBUG, "Found display %s on %s",
+ dpy, ubuf->ut_line);
+ if (prio < PRIO_DISPLAY) {
+ prio = PRIO_DISPLAY;
+ strcpy(ttyFound, ubuf->ut_line);
+ strcpy(dispFound, dpy+1); /*no space*/
+ strcat(dispFound, " ");
+ }
+ delete dpy;
+ continue;
+ }
+ if (!strcmp(ubuf->ut_line, tty)) {
+ strcpy(ttyFound, ubuf->ut_line);
+ status = SUCCESS;
+ break;
+ }
+ }
+ }
+ endutent();
+
+ ktalk_debug("End of Utmp reading");
+#if defined(HAVE_KDE) && defined(ALL_PROCESSES_AND_PROC_FIND_USER)
+ if (Options.XAnnounce && prio < PRIO_DISPLAY)
+ if (find_X_process(name, dispFound))
+ { ktalk_debug(dispFound); status=SUCCESS; }
+#endif
+ if (status == SUCCESS) {
+ (void) strcpy(tty, ttyFound);
+ (void) strcpy(disp, dispFound);
+ if (Options.debug_mode)
+ syslog(LOG_DEBUG, "Returning tty '%s', display '%s'", ttyFound, dispFound);
+ } else ktalk_debug("Returning status %d",status);
+ return (status);
+}
+
+#endif /*UTMP_AND_PROC_FIND_USER*/
+
+#else /*not PROC_FIND_USER*/
+
+int find_user(char *name, char *tty, char *disp) {
+
+ struct utmp ubuf;
+ int status;
+ FILE *fd;
+ struct stat statb;
+ char ftty[20+UT_LINESIZE];
+ char ttyFound[UT_LINESIZE] = "";
+ char dispFound[UT_HOSTSIZE+1] = "";
+
+ if (!(fd = fopen(_PATH_UTMP, "r"))) {
+ fprintf(stderr, "talkd: can't read %s.\n", _PATH_UTMP);
+ return (FAILED);
+ }
+#define SCMPN(a, b) strncmp(a, b, sizeof (a))
+ status = NOT_HERE;
+ (void) strcpy(ftty, _PATH_DEV);
+ while (fread((char *) &ubuf, sizeof ubuf, 1, fd) == 1) {
+ if (!SCMPN(ubuf.ut_name, name)) {
+ if (*tty == '\0') {
+ /* no particular tty was requested */
+ (void) strcpy(ftty+5, ubuf.ut_line);
+ if (stat(ftty,&statb) == 0) {
+ if (!(statb.st_mode & 020)) /* ?character device? */
+ continue;
+ (void) strcpy(ttyFound, ubuf.ut_line);
+#ifdef USE_UT_HOST
+ (void) strcpy(dispFound, ubuf.ut_host);
+ strcat(dispFound, " ");
+#endif
+ status = SUCCESS;
+
+ syslog(LOG_DEBUG, "%s", ttyFound);
+ if ((int) ttyFound[3] > (int) 'f') {
+#ifdef USE_UT_HOST
+ if (Options.debug_mode) {
+ syslog(LOG_DEBUG, "I wanna this:%s", ttyFound);
+ syslog(LOG_DEBUG, "ut_host=%s", ubuf.ut_host);
+ syslog(LOG_DEBUG, "%s", ubuf.ut_line);
+ }
+#endif
+ break;
+ }
+ }
+ }
+ else if (!strcmp(ubuf.ut_line, tty)) {
+ status = SUCCESS;
+ break;
+ }
+ }
+ }
+ fclose(fd);
+ if (status == SUCCESS) {
+ (void) strcpy(tty, ttyFound);
+ (void) strcpy(disp, dispFound);
+ }
+ return (status);
+}
+#endif /*PROC_FIND_USER*/
diff --git a/ktalkd/ktalkd/find_user.h b/ktalkd/ktalkd/find_user.h
new file mode 100644
index 00000000..bb9394a2
--- /dev/null
+++ b/ktalkd/ktalkd/find_user.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "includ.h"
+
+int find_user(char * name, char * tty, char *disp);
+
+
diff --git a/ktalkd/ktalkd/includ.h b/ktalkd/ktalkd/includ.h
new file mode 100644
index 00000000..34971c51
--- /dev/null
+++ b/ktalkd/ktalkd/includ.h
@@ -0,0 +1,79 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef __INCLUD_H
+#define __INCLUD_H "$Id"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include "prot_talkd.h"
+#define NEW_NAME_SIZE NAME_SIZE
+#define NEW_CTL_MSG CTL_MSG
+#define NEW_CTL_RESPONSE CTL_RESPONSE
+#include "otalkd.h"
+
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifndef UT_LINESIZE
+#define UT_LINESIZE 12
+#endif
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef _PATH_UTMP
+#define _PATH_UTMP UTMP
+#endif
+#ifndef _PATH_DEV
+#define _PATH_DEV "/dev/"
+#endif
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp/"
+#endif
+
+#endif /* __INCLUD_H */
diff --git a/ktalkd/ktalkd/ktalkd.wav b/ktalkd/ktalkd/ktalkd.wav
new file mode 100644
index 00000000..f8d4292d
--- /dev/null
+++ b/ktalkd/ktalkd/ktalkd.wav
Binary files differ
diff --git a/ktalkd/ktalkd/ktalkdrc b/ktalkd/ktalkd/ktalkdrc
new file mode 100644
index 00000000..ee6292d8
--- /dev/null
+++ b/ktalkd/ktalkd/ktalkdrc
@@ -0,0 +1,73 @@
+[ktalkd]
+# Administrator config file for ktalkd.
+# There is now a configuration dialog box (see "Settings" menu) for the user's
+# ktalkdrc. But how to provide a nice way to set system-wide parameters ?
+
+
+###### SECTION 1 : Administrator settings
+
+# Mail sender (can be mail.local, sendmail(preferred), qmail(as good:)) )
+# mail.local is included in the dist., just in case you don't have sendmail
+MailProg=$KDEBINDIR/mail.local
+
+# What should I do if somebody tries to talk to a non-existent user ?
+# 0 = Launch answer machine saying 'non-existent user...'
+# and 'I' receive the message (to know that it happened)
+# 1 = 'I' take the talk. (The names of caller & callee appear to 'I')
+# 2 = Do nothing. ('Not logged' will appear to caller).
+NEUBehaviour=2
+
+# (Multi-user secured host : set Behaviour=2).
+# (Multi-user host : set Behaviour=0 and User=root or postmaster)
+# (Almost single-user networked PC : set Behaviour=1 and User=your_user_name)
+
+# If you choose 0, then you can set the following
+# (no internationalization possible : this file is manually read by ktalkd)
+NEUBanner1=The person you're asking to talk with is unknown at this host.
+NEUBanner2=You may have mistyped the name, or network address. Try again
+NEUBanner3=or leave a message which will be sent to the system administrator.
+
+# Who is 'I' ? (=> who should take the talk / receive the message)
+NEUUser=
+
+# If NEUBehaviour is 1 ('I' take the talk), which forward method to use ?
+# Choose FWT if your machine is on two separate networks, and if you (NEUUser)
+# plan to set a forward. FWR is ok (and lighter) in all other cases.
+NEUForwardMethod=FWR
+
+
+##### SECTION 2 : Default values that users can overwrite
+
+# Set to 1 to activate answering machine
+# Warning : if set to 0, no user will be able to activate it.
+# (The value won't overwrite this one).
+Answmach=1
+
+# Set this to 1 to enable X-aware announcement. (Why not ?)
+XAnnounce=1
+
+# External program launched by ktalkd to notify a talk. It can be :
+# ktalkdlg, a pop-up KDE dialog box, which can launch any talk client
+# or ktalk, the KDE talk client.
+# Will be launched with the caller's address as parameter
+# KDEBINDIR will be set by ktalkd.
+ExtPrg=$KDEBINDIR/ktalkdlg
+
+# Which talk client should ktalkdlg launch ? (Default value)
+# The default is "$KDEBINDIR/konsole -e talk". If ktalk is installed,
+# use "$KDEBINDIR/ktalk"
+# KDEBINDIR will be set by ktalkd.
+talkprg=$KDEBINDIR/konsole -e talk
+
+# Set to 1 to enable talk notifications with sound.
+Sound=1
+# Default sound file - ktalkd.wav is included
+SoundFile=ktalkd.wav
+
+# Do you wish to receive an empty mail if the caller didn't leave any message ?
+# (If "1", you'll only know who called you)
+EmptyMail=1
+
+# Time in seconds between "Ringing your party again" and launching answering
+# machine (not very important) (can't be overridden by users)
+Time=10
diff --git a/ktalkd/ktalkd/machines/Makefile.am b/ktalkd/ktalkd/machines/Makefile.am
new file mode 100644
index 00000000..73484dfa
--- /dev/null
+++ b/ktalkd/ktalkd/machines/Makefile.am
@@ -0,0 +1,14 @@
+## -*- makefile -*-
+# Ktalkd - answmach/Makefile.am
+
+##########################################################
+####### Paths
+
+AM_LIBS = $(LIBSOCKET)
+noinst_LIBRARIES = libmach.a
+
+libmach_a_SOURCES = answmach.cpp forwmach.cpp talkconn.cpp
+noinst_HEADERS = answmach.h forwmach.h talkconn.h
+
+#for extra warnings during compilation :
+#AM_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
diff --git a/ktalkd/ktalkd/machines/answmach.cpp b/ktalkd/ktalkd/machines/answmach.cpp
new file mode 100644
index 00000000..0e36c3e2
--- /dev/null
+++ b/ktalkd/ktalkd/machines/answmach.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "answmach.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <time.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <netdb.h>
+#include "../defs.h"
+#include "../proto.h"
+#include "../readconf.h"
+
+#define A_LONG_TIME 10000000 /* seconds before timeout */
+
+/** AnswMachine constructor */
+AnswMachine::AnswMachine(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ int _mode)
+{
+ /* Copy the answering machine mode */
+ mode = _mode;
+ /* Copy the local user name (i.e. the callee, existent or not) */
+ strcpy(local_user, r_name);
+ /* Copy the caller's machine address */
+ caller_machine_addr = r_addr;
+
+ /* Create a talk connection */
+ talkconn = new TalkConnection(r_addr,
+ l_name, /* the caller (remote) */
+ local_user,
+ ntalkProtocol); /* the callee (local) */
+ if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ {
+ /* The caller is trying to talk to somebody this system doesn't know.
+ We can display a NEU banner (non-existent user) and take a message
+ for Options.NEU_user (root?). */
+ strncpy(NEUperson,local_user,NEW_NAME_SIZE); /* the person the talk was aimed to */
+ NEUperson[ NEW_NAME_SIZE - 1 ] = '\0';
+ strncpy(local_user,Options.NEU_user,NEW_NAME_SIZE); /* for mail address, config file... */
+ local_user[ NEW_NAME_SIZE - 1 ] = '\0';
+ } else *NEUperson='\0';
+}
+
+AnswMachine::~AnswMachine()
+{
+ delete talkconn;
+}
+
+int AnswMachine::LaunchIt(const char * key)
+{
+ int launchIt = 1;
+ if (usercfg) {
+ read_bool_user_config(key,&launchIt);
+ /* if (launchIt==0)
+ debug("Not launched. Option set to 0 : ", key);*/
+ }
+ return launchIt;
+}
+
+void AnswMachine::start()
+{
+ /* Only wait if somebody could possibly answer.
+ (The 'ringing your party again' has just been displayed,
+ we want to leave a second chance for the callee to answer.)
+
+ If NEU/not logged, start quickly. (Wait just a little for the
+ LEAVE_INVITE to come.) */
+ sleep( (mode==PROC_REQ_ANSWMACH) ? Options.time_before_answmach : 1 );
+
+ usercfg = init_user_config(local_user);
+
+ if (LaunchIt("Answmach"))
+ {
+ talkconn->open_sockets();
+
+ if (talkconn->look_for_invite(1/*mandatory*/))
+ /* otherwise, either the caller gave up before we
+ started or the callee answered ... */
+ {
+ /* There was an invitation waiting for us,
+ * so connect with the other (hopefully waiting) party */
+ if (talkconn->connect())
+ {
+ /* send the first 3 chars, machine dependent */
+ talkconn->set_edit_chars();
+
+ /* Do the talking */
+ talk();
+ }
+ }
+ }
+ if (usercfg) end_user_config();
+}
+
+static char * shell_quote(const char *s)
+{
+ char *result;
+ char *p;
+ p = result = (char *) malloc(strlen(s)*5+1);
+ while(*s)
+ {
+ if (*s == '\'')
+ {
+ *p++ = '\'';
+ *p++ = '"';
+ *p++ = *s++;
+ *p++ = '"';
+ *p++ = '\'';
+ }
+ else
+ {
+ *p++ = *s++;
+ }
+ }
+ *p = '\0';
+ return result;
+}
+
+/** The actual talking (user to answering machine) */
+void AnswMachine::talk()
+{
+ char command[S_COMMAND];
+ char messg_myaddr [S_MESSG];
+ struct hostent *hp;
+ FILE * fd = 0; /* file descriptor, to write the message */
+ char customline[S_CFGLINE];
+
+ char fname[ 256 ];
+ int fildes;
+ int oldumask = umask( 066 );
+ /* set permissions for temp file to rw- --- --- */
+ int emptymail; /* 1 if empty mail allowed */
+
+ int something_entered;
+
+ hp = gethostbyaddr((char *)&caller_machine_addr, sizeof (struct in_addr), AF_INET);
+ if (hp == (struct hostent *)0)
+ TalkConnection::p_error("Answering machine : Remote machine unknown.");
+
+ if ((!usercfg) || (!read_user_config("Mail",messg_myaddr,S_MESSG-1))) {
+ strncpy(messg_myaddr, local_user, S_MESSG);
+ messg_myaddr[ S_MESSG - 1 ] = '\0';
+ }
+
+ sprintf(fname, _PATH_TMP"/ktalkdXXXXXX"); // ### should use mkstemps
+ if ((fildes = mkstemp(fname)) == -1 || (fd = fdopen(fildes, "w+")) == 0) {
+ TalkConnection::p_error("Unable to open temporary file");
+ }
+
+ umask(oldumask);
+
+ write_headers(fd, hp, messg_myaddr, usercfg);
+
+ /* read other options before setting usercfg to 0, below. */
+ if ((!usercfg) || (!read_bool_user_config("EmptyMail",&emptymail)))
+ /* try from user config file, otherwise default : */
+ emptymail = 1;
+
+ /* debug("Connection established"); */
+
+ if (usercfg) {
+ if (!read_user_config("Msg1",customline,S_CFGLINE-1))
+ { /* debug("Error reading Msg1");*/ end_user_config(); usercfg=0; }
+ }
+
+ /* No user-config'ed banner */
+ if (!usercfg)
+ { /* => Display Options.invitelines */
+ talkconn->write_banner(Options.invitelines);
+ }
+ else if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ { /* => Display Options.NEUBanner* */
+ talkconn->write_banner(Options.NEUBanner1);
+ talkconn->write_banner(Options.NEUBanner2);
+ talkconn->write_banner(Options.NEUBanner3);
+ } else {
+ int linenr = 1;
+ /* number of the Msg[1-*] line. is set to 0 after displaying banner*/
+ char m[]="Msg1"; /* used as key to read configuration */
+
+ while (linenr) /* still something to write to the caller */
+ {
+ talkconn->write_banner(customline);
+
+ /* read next line in custom file. */
+ m[3]=(++linenr)+'0';
+ if (!read_user_config(m,customline,S_CFGLINE-1))
+ linenr=0; /* end of message */
+ }
+ }
+ /* Banner displayed. Let's take the message. */
+ something_entered = read_message(fd);
+ fclose(fd);
+
+ if (something_entered || emptymail)
+ { /* Don't send empty message, except if 'EmptyMail' has been set */
+ int retcode;
+ char *quoted_fname;
+ char *quoted_messg_myaddr;
+ quoted_fname = shell_quote(fname);
+ quoted_messg_myaddr = shell_quote(messg_myaddr);
+
+ snprintf(command,S_COMMAND,"cat '%s' | %s '%s'",quoted_fname,Options.mailprog,quoted_messg_myaddr);
+
+ free(quoted_fname);
+ free(quoted_messg_myaddr);
+
+ /* XXX:
+ * It looks to me as if we're still running as root when we
+ * get here. So using system() is an extremely _bad_ idea,
+ * especially when we use a user-specified address. What
+ * keeps the user from specifying `rm -rf /` as his
+ * address? Yo, way cool.
+ * DF: doesn't shell_quote address this issue? */
+ retcode = system(command);
+ if ((retcode==127) || (retcode==-1))
+ syslog(LOG_ERR,"system() error : %s", strerror(errno));
+ else if (retcode!=0)
+ syslog(LOG_WARNING,"%s : %s", command, strerror(errno));
+ }
+ (void)unlink(fname);
+}
+
+void AnswMachine::write_headers(FILE * fd, struct hostent * hp, char *
+ messg_myaddr, int usercfg)
+{
+ char messg [S_MESSG];
+ char messg_tmpl [S_MESSG];
+ char * r_user = talkconn->get_caller_name();
+
+ /* if using mail.local, set 'Date:' and 'From:', because they will be missing otherwise */
+ int ismaillocal = (strstr(Options.mailprog,"mail.local")!=NULL);
+ if (ismaillocal)
+ /* should we check only the end of the name ? */
+ {
+ time_t tmp = time(0);
+ snprintf(messg,S_MESSG,"Date: %s",ctime(&tmp)); /* \n is included in ctime */
+ fwrite(messg,strlen(messg),1,fd); /* Date */
+
+ snprintf(messg,S_MESSG,"From: %s@%s\n",r_user,hp->h_name);
+ fwrite(messg,strlen(messg),1,fd); /* From */
+ }
+
+ snprintf(messg,S_MESSG,"To: %s\n",messg_myaddr);
+ fwrite(messg,strlen(messg),1,fd); /* To */
+
+ if ((!usercfg) || (!read_user_config("Subj",messg_tmpl,S_CFGLINE)))
+ /* try from user config file, otherwise default subject: */
+ strcpy(messg_tmpl,"Message from %s");
+ snprintf(messg,S_MESSG,messg_tmpl,r_user);
+ fwrite("Subject: ",9,1,fd);
+ fwrite(messg,strlen(messg),1,fd); /* Subject */
+ fwrite("\n",1,1,fd);
+
+ if (!ismaillocal)
+ { /* No need to set Reply-To if From has been set correctly */
+ snprintf(messg,S_MESSG,"Reply-To: %s@%s\n",r_user,hp->h_name);
+ fwrite(messg,strlen(messg),1,fd); /* Reply-To */
+ }
+
+ fwrite("\n",1,1,fd); /* empty line -> end of headers */
+
+ if ((!usercfg) || (!read_user_config("Head",messg_tmpl,S_CFGLINE)))
+ /* try from user config file, otherwise default headline: */
+ strcpy(messg_tmpl,"Message left in the answering machine, by %s@%s");
+ snprintf(messg,S_MESSG,messg_tmpl,r_user,hp->h_name);
+
+ if (mode==PROC_REQ_ANSWMACH_NOT_HERE)
+ {
+ char tmp[ NEW_NAME_SIZE + 10 ];
+ snprintf(tmp, NEW_NAME_SIZE + 9, " => '%s'", NEUperson);
+ if( strlen(tmp)+strlen(messg) < S_MESSG )
+ strcat(messg,tmp);
+ }
+ fwrite(messg,strlen(messg),1,fd); /* First line of the message */
+ fwrite("\n\n",2,1,fd);
+}
+
+int AnswMachine::read_message(FILE * fd) // returns 1 if something has been entered
+{
+ int nb;
+ fd_set read_template, read_set;
+ int pos = 0; // position on the line. left=0.
+ int something = 0; // nothing entered by caller up to now.
+ struct timeval wait;
+ char buff[BUFSIZ];
+ char line[80] = ""; // buffer for current line
+ char char_erase = talkconn->get_char_erase();
+
+ FD_ZERO(&read_template);
+ FD_SET(talkconn->get_sockt(), &read_template);
+
+ for (;;) {
+ read_set = read_template;
+ wait.tv_sec = A_LONG_TIME;
+ wait.tv_usec = 0;
+
+ nb = select(32, &read_set, 0, 0, &wait);
+ if (nb <= 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ read_set = read_template;
+ continue;
+ } /* panic, we don't know what happened */
+ TalkConnection::p_error("Unexpected error from select");
+ }
+ if (FD_ISSET(talkconn->get_sockt(), &read_set)) {
+ int i;
+ /* There is data on sockt */
+ nb = read(talkconn->get_sockt(), buff, sizeof buff);
+ if (nb <= 0) {
+ /* debug("Connection closed. Exiting"); */
+ break;
+ }
+ something = 1;
+ for (i=0; i<nb; i++ ) {
+ if ((buff[i]==char_erase) && (pos>0)) /* backspace */
+ pos--;
+ else {
+ if (pos == 79) {
+ fwrite(line,pos,1,fd);
+ pos = 0;
+ }
+ line[pos++]=buff[i];
+ if (buff[i]=='\n') {
+ fwrite(line,pos,1,fd);
+ pos = 0;
+ }
+ }
+ }
+ } /* if read_set ... */
+ }
+ if (pos>0) { line[pos++]='\n'; fwrite(line,pos,1,fd); }
+ /* last line */
+ return something; // 1 if something entered.
+}
+
+/** Create and start a new answering machine from the given info */
+void AnswMachine::launchAnswMach(NEW_CTL_MSG msginfo, int mode)
+ {
+ if ((fork()) == 0) /* let's fork to let the daemon process other messages */
+ {
+
+#define satosin(sa) ((struct sockaddr_in *)(sa))
+
+ AnswMachine * am = new AnswMachine(
+ (satosin(&msginfo.ctl_addr))->sin_addr, /* Caller's machine address */
+ msginfo.r_name,
+ msginfo.l_name,
+ mode);
+ am->start();
+ delete am;
+
+ // exit the child
+ exit(-1);
+ }
+ }
+
diff --git a/ktalkd/ktalkd/machines/answmach.h b/ktalkd/ktalkd/machines/answmach.h
new file mode 100644
index 00000000..7bae8d17
--- /dev/null
+++ b/ktalkd/ktalkd/machines/answmach.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#include "../includ.h"
+#include "talkconn.h"
+#include <stdio.h>
+
+/** Implements the answering machine. */
+class AnswMachine
+{
+ public:
+ /** Constructor.
+ * @param r_addr Remote machine IP address
+ * @param r_name Remote user name
+ * @param l_name Local user name
+ * @param _mode Answering machine mode, cf defs.h */
+ AnswMachine(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ int _mode);
+
+ /** Destructor. */
+ virtual ~AnswMachine();
+
+ /** Launch the machine */
+ virtual void start();
+
+ /** Create and start a new answering machine from the given info */
+ static void launchAnswMach(NEW_CTL_MSG msginfo, int mode);
+
+ protected:
+
+ /** Read usercfg file to know if user wants it to be launched */
+ int LaunchIt(const char * key);
+
+ int read_message(FILE * fd); // message to mail
+ void write_headers(FILE * fd, struct hostent * hp, char *
+ messg_myaddr, int usercfg); // mail headers
+
+ /** Do the actual talk. */
+ void talk();
+
+ // Protected members
+ /** Answering machine mode */
+ int mode;
+ /** Talk Connection to the caller */
+ TalkConnection * talkconn;
+ /** Local user name (for config file. Is also the default mail addr) */
+ char local_user[NEW_NAME_SIZE];
+ /** Non-existent user name, to be written in the mail. */
+ char NEUperson[NEW_NAME_SIZE];
+ /** Caller's machine address */
+ struct in_addr caller_machine_addr;
+ /** User config file */
+ int usercfg;
+};
diff --git a/ktalkd/ktalkd/machines/forwmach.cpp b/ktalkd/ktalkd/machines/forwmach.cpp
new file mode 100644
index 00000000..4d6b2146
--- /dev/null
+++ b/ktalkd/ktalkd/machines/forwmach.cpp
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "../includ.h"
+#include "forwmach.h"
+#include <stdio.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <syslog.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include "../proto.h"
+#include "../defs.h"
+#include "../readconf.h"
+#include "../process.h"
+#include "../threads.h"
+
+ForwMachine * ForwMachine::pForwMachine = 0L;
+
+void sig_handler(int signum);
+
+ForwMachine::ForwMachine(const NEW_CTL_MSG * mp,
+ char * forward,
+ char * _forwardMethod,
+ int c_id_num) : pid(0), caller_id_num(c_id_num)
+{
+ // Store callee as 'local_user'
+ strncpy(local_user, mp->r_name, NEW_NAME_SIZE);
+ local_user[ NEW_NAME_SIZE - 1 ] = '\0';
+ // -1 is to be sure to have a '\0' at the end
+ // Store caller's name
+ strncpy(caller_username, mp->l_name, NEW_NAME_SIZE);
+ caller_username[ NEW_NAME_SIZE - 1 ] = '\0';
+ // Store caller's protocol
+ callerProtocol = (mp->vers==0) ? talkProtocol : ntalkProtocol;
+
+ // Forward method : from string to enumerate
+ if (!strcmp(_forwardMethod,"FWA"))
+ forwardMethod = FWA;
+ else if (!strcmp(_forwardMethod,"FWR"))
+ forwardMethod = FWR;
+ else if (!strcmp(_forwardMethod,"FWT"))
+ forwardMethod = FWT;
+ else syslog(LOG_ERR,"Unknown forward method : %s",_forwardMethod);
+
+ // Get answerer machine address and username
+ if (getNames(forward)) {
+ ktalk_debug("-- Talking to %s",answ_user);
+ ktalk_debug("-- On %s",answ_machine_name);
+ // Create a new talk connection, to the answerer ...
+ tcAnsw = new TalkConnection(answ_machine_addr,
+ answ_user,
+ // from caller's username
+ (char *) mp->l_name,
+ noProtocol); // to be checked
+ tcAnsw->open_sockets();
+ // and from here if FWT or ...
+ if (forwardMethod != FWT) {
+ //from the caller if FWA or FWR
+ tcAnsw->set_addr(&mp->addr);
+ // but WE DO NOT CHANGE THE ctl_addr, we want the response !!
+ }
+ // Store caller's ctl_addr (to respond to its announce)
+ caller_ctl_addr = mp->ctl_addr;
+ // And his machine addr (to send a LOOK_UP)
+ caller_machine_addr = ((struct sockaddr_in * )(&mp->ctl_addr))->sin_addr;
+ }
+}
+
+ForwMachine::~ForwMachine()
+{
+ delete answ_machine_name;
+ delete tcAnsw;
+ if (pid) kill(pid,SIGTERM);
+}
+
+/** Fills private fields from forward
+ * @param forward user@host to forward the talk */
+int ForwMachine::getNames(char * forward)
+{ /* taken from old get_names.c */
+ register char *cp;
+
+ /* strip out the machine name of the target */
+ for (cp = forward; *cp && !strchr("@:!.", *cp); cp++)
+ ;
+ if (*cp == '\0') {
+ /* this is a forward to a local user */
+ strncpy(answ_user, forward, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(Options.hostname)+1];
+ strcpy(answ_machine_name, Options.hostname); /* set by the daemon */
+ } else {
+ if (*cp == '@') {
+ /* user@host */
+ *cp++ = '\0';
+ strncpy(answ_user, forward, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(cp)+1];
+ strcpy(answ_machine_name, cp);
+ } else {
+ /* host.user or host!user or host:user */
+ *cp++ = '\0';
+ strncpy(answ_user, cp, NEW_NAME_SIZE);
+ answ_user[ NEW_NAME_SIZE -1 ] = '\0';
+ answ_machine_name = new char[strlen(forward)+1];
+ strcpy(answ_machine_name, forward);
+ }
+ }
+
+ struct hostent * hp = gethostbyname(answ_machine_name);
+ if (!hp) {
+ syslog(LOG_ERR, "gethostbyname for %s: %s", answ_machine_name, strerror(errno));
+ return 0;
+ }
+ memcpy(&answ_machine_addr, hp->h_addr, hp->h_length);
+ return 1;
+}
+
+int ForwMachine::isLookupForMe(const NEW_CTL_MSG * mp)
+{
+ /** We want to check if this LOOK_UP concerns this forwmachine.
+ * It does if :
+ mp->l_name = answ_user
+ mp->r_name = caller_username
+ mp->addr.sin_addr is 0.0.0.0, can't be tested ...
+ mp->ctl_addr.sin_addr could be tested but how ?
+ */
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG,"-- mp->l_name : '%s' answerer : '%s' mp->r_name : '%s' caller : '%s'",
+ mp->l_name, answ_user, mp->r_name, caller_username);
+ }
+
+ return ( (!strcmp(mp->l_name, answ_user)) &&
+ (!strcmp(mp->r_name, caller_username)) );
+
+}
+
+char * ForwMachine::findMatch(NEW_CTL_MSG * mp)
+{
+ /** Check if there is a forwarding machine on this host,
+ * matching answerer = r_name and caller = l_name
+ * Then return the initial callee (local_user), to display in ktalkdlg
+ * This is used by local forwards, and therefore also if NEUBehaviour=1 */
+ if (Options.debug_mode)
+ {
+ syslog(LOG_DEBUG,"-- mp->l_name : '%s' caller : '%s' mp->r_name : '%s' answerer : '%s'",
+ mp->l_name, caller_username, mp->r_name, answ_user);
+ }
+ if ((!strcmp(mp->l_name, caller_username)) &&
+ (!strcmp(mp->r_name, answ_user)) )
+ return local_user;
+ return NULL;
+}
+
+int ForwMachine::transmit_chars(int sockt1, int sockt2, unsigned char * buf)
+{
+ int nb = read(sockt1, buf, BUFSIZ);
+ if (nb <= 0) {
+ return 0; // finished.
+ }
+ write(sockt2, buf, nb);
+ if ((nb <= 0) && (errno != EINTR)) {
+ syslog(LOG_ERR,"Unexpected error in write to socket");
+ }
+ return nb;
+}
+
+void ForwMachine::connect_FWT(TalkConnection * tcCaller)
+{
+ /** FWT : This is the method in which we take the connection to both
+ * clients and send each character received from one side to the other
+ * side. This allows to pass a firewall for instance. */
+ /* debug("-- connect_FWT : Waiting for connection from Answerer (%s)", answ_user); */
+ if (tcAnsw->accept())
+ {
+ /* debug("-- connect_FWT : Trying to connect to Caller (%s)",caller_username); */
+ if (tcCaller->connect())
+ {
+ /*
+ debug("-- connect_FWT : Connected to caller (%s)", caller_username);
+ debug("-- connect_FWT : Connected to both. Let's go");
+ */
+ int socktC = tcCaller->get_sockt();
+ int socktA = tcAnsw->get_sockt();
+ int max_sockt = (socktC>socktA) ? socktC : socktA;
+ unsigned char buf[BUFSIZ];
+ fd_set read_mask;
+ int nb;
+ int nbtot = 0;
+ for (;;) {
+ FD_ZERO(&read_mask);
+ FD_SET(socktA, &read_mask); // wait on both connections
+ FD_SET(socktC, &read_mask);
+ nb = select(max_sockt+1, &read_mask, NULL, NULL, NULL); // no timeout
+ if (nb <= 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ /* panic, we don't know what happened */
+ TalkConnection::p_error("Unexpected error from select");
+ }
+ if (FD_ISSET(socktA, &read_mask)) {
+ /* There is data on sockt A */
+ nb = transmit_chars(socktA, socktC, buf);
+ if (nb==0) return ;
+ }
+ if (FD_ISSET(socktC, &read_mask)) {
+ /* There is data on sockt C */
+ nb = transmit_chars(socktC, socktA, buf);
+ if (nb==0) return ;
+ nbtot += nb;
+ if (nbtot == 3) // just after the 3 edit chars
+ {
+ struct hostent * hp = gethostbyaddr((char *)&caller_machine_addr,
+ sizeof (struct in_addr), AF_INET);
+ if (hp != (struct hostent *)0) {
+ // Write first line for answerer.
+ // i18n() missing
+ sprintf((char *)buf, "Speaking to %s@%s\n", caller_username, hp->h_name);
+ write(socktA, (char *)buf, strlen((char *)buf));
+ } else ktalk_debug("-- ERROR : Unable to resolve caller_machine_addr !");
+ }
+ }
+ } // for
+ } else syslog(LOG_ERR,"-- FWT : Caller connected, but not answerer !");
+ } else syslog(LOG_ERR,"-- FWT : Caller did not connect !");
+}
+
+void ForwMachine::sendResponse(const struct talk_addr target, NEW_CTL_RESPONSE * rp)
+{
+ if (rp->vers == 0) { // otalk protocol (internal coding for it)
+ rp->vers /*type in otalk*/ = rp->type;
+ rp->type /*answer in otalk*/ = rp->answer;
+ }
+ int cc = sendto(1 /*talkd_sockt*/, (char *) rp,
+ sizeof (NEW_CTL_RESPONSE), 0, (struct sockaddr *)&target,
+ sizeof (struct talk_addr));
+ if (cc != sizeof (NEW_CTL_RESPONSE))
+ syslog(LOG_WARNING, "sendto: %s", strerror(errno));
+}
+
+/** processAnnounce is done by a child (to let the daemon process other
+ * messages, including ours). Then the child is left running (he only knows the
+ * value of answ_id_num) and waits for SIGDELETE to use this value. */
+void ForwMachine::processAnnounce()
+{
+ if ((pid=fork())==0) // store pid in the parent
+ {
+ // Send announce to the answerer, and wait for response
+ ktalk_debug("-------------- ForwMachine : sending ANNOUNCE to %s",answ_user);
+ tcAnsw->ctl_transact(ANNOUNCE, caller_id_num);
+ // Copy answer and id_num from the response struct
+ ktalk_debug("-------------- ForwMachine : got a response");
+ NEW_CTL_RESPONSE rp; // build our response struct
+ tcAnsw->getResponseItems(&rp.answer, &answ_id_num, 0L);
+ // answ_id_num stores id_num for delete.
+ rp.type = ANNOUNCE;
+ rp.vers = TALK_VERSION;
+ rp.id_num = htonl(our_id_num);
+
+ ktalk_debug("Storing response id_num %d",answ_id_num);
+ // Now send the response to the caller
+ print_response("-- => response (processAnnounce)", &rp);
+ sendResponse(caller_ctl_addr, &rp);
+ // -- Now wait for SIGDELETE
+
+ // store static ref to this forwmachine in this child.
+ pForwMachine = this;
+ // register signal hander
+ if (signal(SIGDELETE,&sig_handler)==SIG_ERR) ktalk_debug("ERROR for SIGUSR2");
+ ktalk_debug("Signal handler registered. Waiting...");
+ // infinite loop waiting for signals
+ while(1)
+ sleep(100);
+ }
+ ktalk_debug("Forwmachine started for Announce (now) and Delete (later). pid : %d",pid);
+ // new_process(); // We DON'T register new process.
+ // in case of re-announce, this forwmach will be forgotten.
+ // we don't want ktalkd to wait infinitely for it to die, it won't.
+}
+
+/** Process the lookup in a child process. The current running child can't do
+ * it with a signal, but we need the answerer's ctl_addr to respond... */
+void ForwMachine::processLookup(const NEW_CTL_MSG * mp)
+{
+ if (fork()==0)
+ { // here we are the child
+ ktalk_debug("------------- Got LOOKUP : send it to caller (%s)", caller_username);
+ // Let's send a LOOK_UP on caller's machine, to make sure he still
+ // wants to speak to the callee...
+ TalkConnection * tcCaller = new TalkConnection(caller_machine_addr,
+ caller_username,
+ local_user,
+ callerProtocol);
+ tcCaller->open_sockets();
+ tcCaller->look_for_invite(0/*no error if no invite*/);
+ NEW_CTL_RESPONSE rp;
+ tcCaller->getResponseItems(&rp.answer, &rp.id_num, &rp.addr);
+ ktalk_debug("------------- Done. Forward response to answerer");
+
+ rp.type = LOOK_UP;
+ rp.vers = mp->vers;
+ rp.id_num = htonl(rp.id_num);
+ // Now send the response to the answerer
+ if (forwardMethod == FWR)
+ {
+ // with caller's addr copied in the NEW_CTL_RESPONSE (if FWR),
+ // so that they can talk to each other.
+ /* rp.addr filled by getResponseItems */
+ rp.addr.ta_family = htons(rp.addr.ta_family);
+ }
+ else // FWT. (FWA doesn't let us get the LOOK_UP)
+ {
+ // in this case, we copy in the NEW_CTL_RESPONSE the address
+ // of the connection socket set up here for the answerer
+ rp.addr = tcAnsw->get_addr();
+ rp.addr.ta_family = htons(AF_INET);
+ }
+ print_response("-- => response (processLookup)", &rp);
+ if (forwardMethod == FWT)
+ tcAnsw->listen(); // start listening before we send the response,
+ // just in case the answerer is very fast (ex: answ mach)
+ sendResponse(mp->ctl_addr, &rp);
+ if (forwardMethod == FWT)
+ connect_FWT(tcCaller);
+ delete tcCaller;
+ _exit(0);
+ }
+ new_process();
+}
+
+/** Done by the forwmachine child that processed the ANNOUNCE. (He know answ_id_num)
+ * Exits at the end of the method */
+void ForwMachine::processDelete()
+{
+ // Send DELETE to the answerer, and don't wait for response
+ ktalk_debug("-------------- ForwMachine : sending DELETE to %s",answ_user);
+ ktalk_debug("Using resp->id_num %d",answ_id_num);
+ tcAnsw->ctl_transact(DELETE, answ_id_num);
+ _exit(0); // We exit the child, we have finished.
+}
+
+// Static functions
+
+int ForwMachine::forwMachProcessLookup(TABLE_ENTRY * table, const NEW_CTL_MSG * mp)
+{
+ /** This goes through the table, looking for non-NULL fwm entries.
+ * After a cast to (ForwMachine *), the fwm entries allows us to
+ * speak to currently availabe ForwMachines, to handle correctly
+ * this LOOK_UP */
+ ktalk_debug("-- forwMachProcessLookup(mp,rp)");
+ TABLE_ENTRY *ptr;
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ForwMachine * fwm = (ForwMachine *) ptr->fwm;
+ if (fwm->isLookupForMe(mp)) {
+ ktalk_debug("-- Found match : id %d", ptr->request.id_num);
+ fwm->processLookup(mp);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/** Check if there is a forwarding machine on this machine,
+ * matching answerer = r_name and caller = l_name
+ * Then set callee to the initial callee, to display in ktalkdlg */
+char * ForwMachine::forwMachFindMatch(TABLE_ENTRY * table, NEW_CTL_MSG * mp)
+{
+ ktalk_debug("-- forwMachFindMatch(mp)");
+ TABLE_ENTRY *ptr;
+ char * callee;
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ForwMachine * fwm = (ForwMachine *) ptr->fwm;
+ callee = fwm->findMatch(mp);
+ if (callee) {
+ ktalk_debug("-- Found match : id %d", ptr->request.id_num);
+ return callee;
+ }
+ }
+ }
+ return NULL;
+}
+
+void sig_handler(int signum)
+{
+ ktalk_debug("SIGNAL received : %d",signum);
+ ForwMachine * fwm = ForwMachine::getForwMachine();
+ fwm->processDelete();
+}
+
+void ForwMachine::start(int o_id_num)
+{
+ our_id_num = o_id_num;
+ processAnnounce();
+
+}
+
diff --git a/ktalkd/ktalkd/machines/forwmach.h b/ktalkd/ktalkd/machines/forwmach.h
new file mode 100644
index 00000000..8ad067f9
--- /dev/null
+++ b/ktalkd/ktalkd/machines/forwmach.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#ifndef FORWMACH_H
+#define FORWMACH_H
+
+#include "../includ.h"
+#include "../table.h"
+#include "talkconn.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <signal.h>
+
+// Strangely enough, SIGUSR1 doesn't get caught ... Why ??
+
+#define SIGDELETE SIGUSR2
+
+/* Forwarding machine scheme : (ANNOUNCE request only)
+
+ Caller (client) ForwMach--- Answerer (client)
+ \ | \
+ \ | \
+ Caller (daemon) ----ktalkd ------Answerer (daemon)
+
+ Caller always talks to us using ntalk (if otalk then kotalkd translates it)
+ But answerer might be using otalk. Let's check for it in processLookup.
+ */
+/** Implements the forwarding machine. */
+class ForwMachine
+{
+
+ public:
+ /** Constructor.
+ * @param mp Request received
+ * @param forward User@host to forward the talk
+ * @param _forwardMethod 3 letters to choose the method (see .talkdrc)
+ * @param c_id_num announce id_num, got from caller
+ * @param o_id_num our id_num in our table
+ * */
+ ForwMachine(const NEW_CTL_MSG * mp,
+ char * forward,
+ char * _forwardMethod,
+ int c_id_num);
+
+ /** Destructor. */
+ virtual ~ForwMachine();
+
+ // Child methods
+
+ /** Process the incoming ANNOUNCE request. */
+ void processAnnounce();
+
+ /** Processes the LOOK_UP request from answerer. */
+ void processLookup(const NEW_CTL_MSG * mp);
+
+ /** Processes the DELETE request from caller. Called by sig_handler. */
+ void processDelete();
+
+ // Parent methods
+
+ /** Send a DELETE signal to the child */
+ void sendDelete() { kill(pid, SIGDELETE); pid = 0; }
+
+ /** Checks if a LOOK_UP concerns this Forwarding Machine */
+ int isLookupForMe(const NEW_CTL_MSG * mp);
+ /** Used by forwMachFindMatch for NEUuser or local forward */
+ char * findMatch(NEW_CTL_MSG * mp);
+
+ /** Calls processLookup after finding the correct forwmachine instance in the table */
+ static int forwMachProcessLookup(TABLE_ENTRY * table, const NEW_CTL_MSG * mp);
+
+ /** Tries to find a match in the table for the given REQUEST structure.
+ * @see findMatch */
+ static char * forwMachFindMatch(TABLE_ENTRY * table, NEW_CTL_MSG * mp);
+
+ /** Get static ref to current ForwMachine. For sig_handler */
+ static ForwMachine * getForwMachine() { return pForwMachine; }
+
+ /** Start the ForwMachine process. Processes the announcement and waits for signals
+ * @param o_id_num Our id num in our table. */
+ void start(int o_id_num);
+
+ protected:
+ /** Static ref to current forwmachine. For sig_handler */
+ static ForwMachine * pForwMachine;
+
+ /** Pid of the child forwmachine */
+ int pid;
+
+ /** Fills privates fields from forward
+ * @param forward user@host to forward the talk */
+ int getNames(char * forward);
+
+ /** Respond to caller's daemon
+ * @param target the caller's machine address
+ * @param rp the response to send to it */
+ void sendResponse(const struct talk_addr target, NEW_CTL_RESPONSE * rp);
+
+ /** FWT Method : transmit characters from sockt1 to sockt2 */
+ int transmit_chars(int sockt1, int sockt2, unsigned char * buf);
+
+ /** FWT Method : we want to connect to both sides */
+ void connect_FWT(TalkConnection * tcCaller);
+
+ /** Method for the forwarding. */
+ enum {FWA, FWR, FWT} forwardMethod;
+
+ /** Answerer user name */
+ char answ_user[NEW_NAME_SIZE];
+ /** Answerer machine name */
+ char * answ_machine_name;
+ /** Answerer machine address */
+ struct in_addr answ_machine_addr;
+ /** Talk Connection to the answerer */
+ TalkConnection * tcAnsw;
+ /** id_num for the announce on answerer's machine. */
+ uint32_t answ_id_num;
+
+ /** Local user name, the original 'callee' */
+ char local_user[NEW_NAME_SIZE];
+ /** Our id_num (in our table) */
+ int our_id_num;
+
+ /** Caller's user name */
+ char caller_username[NEW_NAME_SIZE];
+ /** Caller's ctl_addr*/
+ struct talk_addr caller_ctl_addr;
+ /** Caller's machine address */
+ struct in_addr caller_machine_addr;
+ /** Caller's announce id_num */
+ int caller_id_num;
+ /** Caller's protocol */
+ ProtocolType callerProtocol;
+};
+#endif
diff --git a/ktalkd/ktalkd/machines/talkconn.cpp b/ktalkd/ktalkd/machines/talkconn.cpp
new file mode 100644
index 00000000..2836bc62
--- /dev/null
+++ b/ktalkd/ktalkd/machines/talkconn.cpp
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * This file handles haggling with the various talk daemons to
+ * get a socket to talk to. sockt is opened and connected in
+ * the progress
+ */
+
+#include "talkconn.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <termios.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <sys/time.h>
+#include <time.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "../proto.h"
+#include "../defs.h" // for hostname
+
+#ifndef SOMAXCONN
+#warning SOMAXCONN not defined in your headers
+#define SOMAXCONN 5
+#endif
+
+#define CTL_WAIT 2 /* time to wait for a response, in seconds */
+
+struct in_addr TalkConnection::defaultReplyAddr;
+short int TalkConnection::talkDaemonPort; // Port number of talk demon (517)
+short int TalkConnection::ntalkDaemonPort; // Port number of ntalk demon (518)
+
+
+TalkConnection::TalkConnection(struct in_addr caller_machine_addr,
+ char * r_name,
+ char * local_user,
+ ProtocolType _protocol) :
+ protocol(_protocol), his_machine_addr(caller_machine_addr), ctl_sockt(-1), sockt(-1)
+{
+ my_machine_addr = getReplyAddr(his_machine_addr);
+
+ new_msg.vers = TALK_VERSION;
+ new_msg.pid = htonl (getpid ()); // is it necessary ?
+ *new_msg.r_tty = '\0';
+ old_msg.pid = htonl (getpid ()); // is it necessary ?
+ *old_msg.r_tty = '\0';
+
+ strncpy(new_msg.l_name,local_user,NEW_NAME_SIZE);
+ new_msg.l_name[NEW_NAME_SIZE-1]='\0';
+ strncpy(new_msg.r_name,r_name,NEW_NAME_SIZE);
+ new_msg.r_name[NEW_NAME_SIZE-1]='\0';
+ strncpy(old_msg.l_name,local_user,OLD_NAME_SIZE);
+ old_msg.l_name[OLD_NAME_SIZE-1]='\0';
+ strncpy(old_msg.r_name,r_name,OLD_NAME_SIZE);
+ old_msg.r_name[OLD_NAME_SIZE-1]='\0';
+
+}
+
+TalkConnection::~TalkConnection()
+{
+ close_sockets();
+}
+
+void TalkConnection::init()
+{
+ /* look up the address of the local host */
+ struct hostent *hp = gethostbyname(Options.hostname);
+ if (!hp) {
+ syslog(LOG_ERR, "GetHostByName failed for %s.",Options.hostname);
+ exit(-1);
+ }
+ memcpy(&defaultReplyAddr, hp->h_addr, hp->h_length);
+
+ /* find the server's ports */
+ struct servent * sp = getservbyname("talk", "udp");
+ if (sp == 0)
+ syslog(LOG_ERR, "talkconnection: talk/udp: service is not registered.\n");
+ talkDaemonPort = sp->s_port; // already in network byte order
+
+ sp = getservbyname("ntalk", "udp");
+ if (sp == 0)
+ syslog(LOG_ERR, "talkconnection: ntalk/udp: service is not registered.\n");
+ ntalkDaemonPort = sp->s_port; // already in network byte order
+}
+
+int TalkConnection::open_socket (struct sockaddr_in *addr, int type)
+{
+ addr->sin_family = AF_INET;
+ addr->sin_addr = my_machine_addr;
+ addr->sin_port = 0;
+ int newSocket = socket (PF_INET, type, 0);
+ if (newSocket <= 0)
+ p_error ("Unable to open a new socket!");
+
+ ksize_t length = sizeof (*addr);
+ if (bind (newSocket, (struct sockaddr *) addr, length) != 0) {
+ ::close (newSocket);
+ p_error ("Error binding socket!");
+ }
+ if (getsockname (newSocket, (struct sockaddr *) addr, &length) == -1) {
+ ::close (newSocket);
+ p_error ("New socket has a bad address!");
+ }
+ return newSocket;
+}
+
+void TalkConnection::open_sockets()
+{
+ struct sockaddr_in ctl_addr;
+ struct sockaddr_in my_addr;
+
+ /* open the ctl socket */
+ ctl_sockt = open_socket(&ctl_addr, SOCK_DGRAM);
+ /* store its address */
+ set_ctl_addr((const struct talk_addr *)&ctl_addr);
+
+ /* open the text socket */
+ sockt = open_socket(&my_addr, SOCK_STREAM);
+ /* store its address */
+ set_addr((const struct talk_addr *)&my_addr);
+}
+
+/* Tries to find out the correct IP address that the daemon at host
+ "destination" has to respond to - code borrowed from ktalk, thanks Burkhard ! */
+struct in_addr TalkConnection::getReplyAddr (struct in_addr destination) {
+
+ in_addr *result;
+ unsigned char *help1;
+ unsigned char *help2;
+
+ /* disabled caching - I don't have QIntDict ...
+ result = replyAddrList [(long) destination.s_addr];
+ if (result) {
+ return *result;
+ }
+ */
+ int testsock, i;
+ result = new (struct in_addr);
+ struct sockaddr_in client, daemon;
+ for (i = 0; i < 2; i++) {
+ client.sin_family = daemon.sin_family = AF_INET;
+ client.sin_addr.s_addr = htonl (INADDR_ANY);
+ client.sin_port = htons (0);
+ daemon.sin_addr = destination;
+ daemon.sin_port = i ? ntalkDaemonPort : talkDaemonPort;
+
+ // Connect to the daemon socket address
+ // On some UNIXes (such as Linux) this works and sets the IP address queried
+ // by getsockname to the local machine address used to reach the daemon.
+ // If it doesn't work (e.g. on SunOS and Solaris), the default machine
+ // address is used instead.
+ ksize_t length = sizeof (daemon);
+ if ((testsock = socket (AF_INET, SOCK_DGRAM, 0)) >= 0 &&
+ bind (testsock, (struct sockaddr *) &client, sizeof (client)) == 0 &&
+ ::connect (testsock, (struct sockaddr *) &daemon,
+ sizeof (daemon)) == 0 &&
+ getsockname (testsock, (struct sockaddr *) &client, &length) != -1 &&
+ client.sin_addr.s_addr != htonl (INADDR_ANY))
+ {
+ *result = client.sin_addr;
+ ktalk_debug("Found reply address");
+ ::close (testsock);
+ break;
+ }
+ if (testsock >= 0) ::close (testsock);
+ }
+ if (i == 2) {
+ *result = defaultReplyAddr;
+ ktalk_debug("Couldn't find reply address, using default");
+ }
+ if (Options.debug_mode) {
+ help1 = (unsigned char *) &destination;
+ help2 = (unsigned char *) result;
+ syslog ( LOG_DEBUG,
+ "detected reply address for %d.%d.%d.%d: %d.%d.%d.%d",
+ help1 [0], help1 [1], help1 [2], help1 [3],
+ help2 [0], help2 [1], help2 [2], help2 [3]);
+ /* replyAddrList.insert ((long) destination.s_addr, result); disabled */
+ }
+ return *result;
+}
+/* QIntDict <in_addr> TalkConnection::replyAddrList; */
+
+/** Check the remote protocol. Result stored in <protocol>.
+ * @return 1 if succeeded to find at least 1 protocol */
+void TalkConnection::findProtocol() {
+
+ ktalk_debug("Remote protocol unknown. Trying to find it. findProtocol()");
+ /* The existing ctl_sockt will be used for ntalk */
+ int new_socket = ctl_sockt;
+ /* We need a new SOCK_DGRAM socket for otalk */
+ struct sockaddr_in old_ctl_addr;
+ int old_socket = open_socket(&old_ctl_addr, SOCK_DGRAM);
+
+ /* Fill the old_msg return-address to match the address of old_socket */
+ old_msg.ctl_addr = *(struct talk_addr *)&old_ctl_addr;
+ old_msg.ctl_addr.ta_family = htons(AF_INET);
+
+ /* Prepare two LOOK_UP ctl messages */
+ old_msg.type = LOOK_UP;
+ old_msg.id_num = htonl(0L);
+ new_msg.type = LOOK_UP;
+ new_msg.id_num = htonl(0L);
+ char svg_r_name[NEW_NAME_SIZE]; // Save the real r_name
+ strcpy(svg_r_name, new_msg.r_name);
+ strcpy(old_msg.r_name, "ktalk");
+ strcpy(new_msg.r_name, "ktalk");
+
+ struct sockaddr_in daemon;
+ daemon.sin_family = AF_INET;
+ daemon.sin_addr = his_machine_addr;
+
+ /* Prepare the variables used for reading on sockets */
+ fd_set read_mask, ctl_mask;
+ int nready=0, cc;
+ struct timeval wait;
+
+ FD_ZERO(&ctl_mask);
+ FD_SET(new_socket, &ctl_mask);
+ FD_SET(old_socket, &ctl_mask);
+ int max_socket = (new_socket > old_socket) ? new_socket : old_socket;
+
+ /* Method : we send the two packets to the two remote daemons.
+ We wait for the first one correct answer, and then we stop everything.
+ If a wrong answer comes, ignore it (but note that we got it).
+ If no answer (or two wrong answers), retry, up to 3 times. */
+
+ for (int retry = 0; (retry < 3) && (protocol==noProtocol); retry ++)
+ {
+ ktalk_debug("Send packets. Retry = %d",retry);
+ /* Send the messages */
+ daemon.sin_port = ntalkDaemonPort;
+ int len = sendto (new_socket, (char *) &new_msg, sizeof new_msg, 0,
+ (struct sockaddr *) &daemon, sizeof daemon);
+ if (len != sizeof new_msg)
+ syslog(LOG_ERR, "findProtocol: sendto() for ntalk failed!");
+
+ daemon.sin_port = talkDaemonPort;
+ len = sendto (old_socket, (char *) &old_msg, sizeof old_msg, 0,
+ (struct sockaddr *) &daemon, sizeof daemon);
+ if (len != sizeof old_msg)
+ syslog(LOG_ERR, "findProtocol: sendto() for otalk failed!");
+
+ do {
+ /* Wait for response */
+ read_mask = ctl_mask;
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ nready = ::select(max_socket+1, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ // Timeout. Let's exit this loop and retry sending.
+ break;
+ }
+ if (nready == 0) ktalk_debug("select returned 0 ! ");
+
+ /* Analyze response */
+ if (FD_ISSET(old_socket, &read_mask)) {
+ ktalk_debug("Reading on old_socket");
+ cc = ::recv(old_socket, (char *)&old_resp, sizeof (old_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ // Wrong answer (e.g. too short).
+ } else
+ if (old_resp.type == LOOK_UP) protocol=talkProtocol; // FOUND
+ }
+ if (FD_ISSET(new_socket, &read_mask)) {
+ ktalk_debug("Reading on new_socket");
+ cc = ::recv(new_socket, (char *)&new_resp, sizeof (new_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ // Wrong answer (e.g. too short). Note ntalk answered
+ } else
+ if ((new_resp.type == LOOK_UP) && (new_resp.vers == TALK_VERSION))
+ protocol=ntalkProtocol;
+ }
+ } while (protocol==noProtocol);
+ // wait for a time out, or ok.
+ } // for
+
+ /* restore the real r_name */
+ strncpy(old_msg.r_name, svg_r_name, OLD_NAME_SIZE);
+ strncpy(new_msg.r_name, svg_r_name, NEW_NAME_SIZE);
+ /* restore old.ctl_addr */
+ old_msg.ctl_addr = new_msg.ctl_addr;
+ ::close(old_socket);
+ ktalk_debug("Exiting findProtocol");
+ if (protocol==ntalkProtocol) ktalk_debug("Exiting findProtocol with protocol = NTALK");
+ else if (protocol==talkProtocol) ktalk_debug("Exiting findProtocol with protocol = NTALK");
+ else p_error("FATAL : no protocol found.");
+}
+
+void TalkConnection::set_addr(const struct talk_addr * addr)
+{
+ old_msg.addr = *addr;
+ old_msg.addr.ta_family = htons(AF_INET);
+ new_msg.addr = *addr;
+ new_msg.addr.ta_family = htons(AF_INET);
+}
+
+void TalkConnection::set_ctl_addr(const struct talk_addr * ctl_addr)
+{
+ old_msg.ctl_addr = *ctl_addr;
+ old_msg.ctl_addr.ta_family = htons(AF_INET);
+ new_msg.ctl_addr = *ctl_addr;
+ new_msg.ctl_addr.ta_family = htons(AF_INET);
+}
+
+void TalkConnection::close_sockets()
+{
+ if (sockt!=-1) { close(sockt); sockt = -1; }
+ if (ctl_sockt!=-1) { close(ctl_sockt); ctl_sockt = -1; }
+}
+
+/*
+ * SOCKDGRAM is unreliable, so we must repeat messages if we have
+ * not received an acknowledgement within a reasonable amount
+ * of time
+ */
+void TalkConnection::ctl_transact(int type, int id_num)
+{
+
+ if (protocol == noProtocol)
+ /** We've been so far, but we still don't know which protocol to use.
+ * Let's check it, the way ktalk does. */
+ findProtocol();
+
+ fd_set read_mask, ctl_mask;
+ int nready=0, cc, size, ok=0;
+ struct timeval wait;
+ struct sockaddr_in daemon_addr;
+ char * msg;
+
+ if (protocol == talkProtocol) {
+ old_msg.type = type;
+ old_msg.id_num = htonl(id_num);
+ msg = (char *)&old_msg;
+ size = sizeof old_msg;
+ } else {
+ new_msg.type = type;
+ new_msg.id_num = htonl(id_num);
+ msg = (char *)&new_msg;
+ size = sizeof new_msg;
+ print_request("ctl_transact: ",&new_msg);
+ }
+
+ daemon_addr.sin_family = AF_INET;
+ daemon_addr.sin_addr = his_machine_addr;
+ daemon_addr.sin_port = (protocol == talkProtocol) ? talkDaemonPort : ntalkDaemonPort;
+ FD_ZERO(&ctl_mask);
+ FD_SET(ctl_sockt, &ctl_mask);
+
+ /* Keep sending the message until a response of
+ * the proper type is obtained.
+ */
+ do {
+ /* resend message until a response is obtained */
+ do {
+ cc = sendto(ctl_sockt, msg, size, 0,
+ (struct sockaddr *)&daemon_addr,
+ sizeof (daemon_addr));
+ if (cc != size) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on write to talk daemon");
+ }
+ read_mask = ctl_mask;
+ wait.tv_sec = CTL_WAIT;
+ wait.tv_usec = 0;
+ nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
+ if (nready < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error waiting for daemon response");
+ }
+ if (nready == 0) ktalk_debug("select returned 0 ! ");
+ } while (nready == 0);
+ /*
+ * Keep reading while there are queued messages
+ * (this is not necessary, it just saves extra
+ * request/acknowledgements being sent)
+ */
+ do {
+ if (protocol == talkProtocol)
+ cc = ::recv(ctl_sockt, (char *)&old_resp, sizeof (old_resp), 0);
+ else
+ cc = ::recv(ctl_sockt, (char *)&new_resp, sizeof (new_resp), 0);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Error on read from talk daemon");
+ }
+ read_mask = ctl_mask;
+ /* an immediate poll */
+ timerclear(&wait);
+ nready = ::select(ctl_sockt+1, &read_mask, 0, 0, &wait);
+ if (protocol == talkProtocol) ok = (old_resp.type == type);
+ else ok = ((new_resp.type == type) && (new_resp.vers == TALK_VERSION));
+ } while (nready > 0 && (!ok));
+ } while (!ok);
+ if (protocol == talkProtocol) {
+ old_resp.id_num = ntohl(old_resp.id_num);
+ old_resp.addr.ta_family = ntohs(old_resp.addr.ta_family);
+ } else {
+ new_resp.id_num = ntohl(new_resp.id_num);
+ new_resp.addr.ta_family = ntohs(new_resp.addr.ta_family);
+ }
+}
+
+/** Look for an invitation on remote machine */
+int TalkConnection::look_for_invite(int mandatory)
+{
+ /* Check for invitation on caller's machine */
+ ctl_transact(LOOK_UP, 0);
+
+ uint8_t answer;
+ uint32_t id_num;
+ getResponseItems(&answer, &id_num, &lookup_addr);
+
+ if (!mandatory) return 0;
+
+ /* the switch is for later options, such as multiple invitations */
+ switch (answer) {
+
+ case SUCCESS:
+ new_msg.id_num = htonl(id_num);
+ old_msg.id_num = htonl(id_num);
+ ktalk_debug("TalkConnection::look_for_invite : got SUCCESS");
+ if (lookup_addr.ta_family != AF_INET)
+ p_error("Response uses invalid network address");
+ return (1);
+
+ default:
+ /* there wasn't an invitation waiting for us */
+ ktalk_debug("TalkConnection::look_for_invite : didn't get SUCCESS");
+ return (0);
+ }
+}
+
+/** Prepare to accept a connection from another talk client */
+void TalkConnection::listen()
+{
+ if (::listen(sockt, SOMAXCONN) != 0)
+ p_error("Error on attempt to listen for caller");
+}
+
+/** Accept a connection from another talk client */
+int TalkConnection::accept()
+{
+ int accept_sockt;
+ while ((accept_sockt = ::accept(sockt, 0, 0)) < 0) {
+ if (errno == EINTR)
+ continue;
+ p_error("Unable to connect with your party");
+ }
+ ::close(sockt);
+ sockt = accept_sockt;
+ return sockt;
+}
+
+/** Connect to another talk client. */
+int TalkConnection::connect()
+{
+ ktalk_debug("Waiting to connect");
+ do {
+ errno = 0;
+ if (::connect(sockt, (const sockaddr *)(&lookup_addr), sizeof (struct talk_addr)) != -1)
+ return 1;
+ } while (errno == EINTR);
+ if (errno == ECONNREFUSED) {
+ /*
+ * The caller gave up, but his invitation somehow
+ * was not cleared. Clear it and initiate an
+ * invitation. (We know there are no newer invitations,
+ * the talkd works LIFO.)
+ */
+ ktalk_debug("ECONNREFUSED");
+ ctl_transact(DELETE, 0);
+ close_sockets();
+ return 0;
+ }
+ p_error("Unable to connect with initiator");
+ /*NOTREACHED*/
+ return 0;
+}
+
+/** Trade edit characters with the other talk. By agreement
+ * the first three characters each talk transmits after
+ * connection are the three edit characters.
+ * A normal talk client uses tcgetattr() to get the chars,
+ * but the daemon isn't connected to a terminal, so we can't call it.
+ * We just send dummy chars, to disable control chars. */
+void TalkConnection::set_edit_chars()
+{
+ char buf[3];
+ int cc;
+ buf[0] = buf[1] = buf[2] = (char)0xff;
+ /* Write our config to the caller */
+ cc = write(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ /* Read the caller configuration */
+ cc = read(sockt, buf, sizeof(buf));
+ if (cc != sizeof(buf) )
+ p_error("Lost the connection");
+ char_erase = buf[0]; // store it in TalkConnection
+}
+
+void TalkConnection::write_banner(char * banner)
+{ /* writes the message 'banner', null-terminated */
+ int count = strlen(banner);
+ int nbsent;
+ char * str = banner;
+ /* message_d("Count : %d.",count); */
+ while (count>0) {
+ /* let's send 16 -bytes-max packets */
+ if (count>=16) nbsent = write(sockt,str,16);
+ else nbsent = write(sockt,str,count);
+ count -= nbsent;
+ str += nbsent;
+ fsync(sockt);
+ }
+ write(sockt,"\n",1);
+}
+
+void TalkConnection::getResponseItems(uint8_t * answer, uint32_t * id_num, struct talk_addr * addr) {
+ if (protocol == talkProtocol) {
+ if (answer) *answer = old_resp.answer;
+ if (id_num) *id_num = old_resp.id_num;
+ if (addr) *addr = old_resp.addr;
+ } else {
+ if (answer) *answer = new_resp.answer;
+ if (id_num) *id_num = new_resp.id_num;
+ if (addr) *addr = new_resp.addr;
+ }
+}
+
+/** p_error prints the system error message in the log
+ * and then exits. */
+void TalkConnection::p_error(const char *str)
+{
+ syslog(LOG_ERR, "%s", str);
+ _exit(0);
+}
diff --git a/ktalkd/ktalkd/machines/talkconn.h b/ktalkd/ktalkd/machines/talkconn.h
new file mode 100644
index 00000000..dd08e10a
--- /dev/null
+++ b/ktalkd/ktalkd/machines/talkconn.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+#ifndef TALKCONNECTION_H
+#define TALKCONNECTION_H
+
+#include "../includ.h"
+
+enum ProtocolType {noProtocol, talkProtocol, ntalkProtocol};
+
+class TalkConnection
+{
+ public:
+
+ /** Global initialization. To be called once. */
+ static void init();
+
+ /** Create a talk connection.
+ * @param r_addr Remote machine IP address
+ * @param r_name Remote user name
+ * @param l_name Local user name
+ * @param _protocol Caller's protocol */
+ TalkConnection(struct in_addr r_addr,
+ char * r_name,
+ char * l_name,
+ ProtocolType _protocol);
+
+ /** Destructor. Closes the sockets if opened.*/
+ ~TalkConnection();
+
+ /** Create the sockets */
+ void open_sockets();
+ /** Close the sockets */
+ void close_sockets();
+
+ /** Methods for talking to remote daemon */
+ void ctl_transact(int type, int id_num);
+ int look_for_invite(int mandatory);
+
+ /** Connect with address given back in response to LOOK_UP. */
+ int connect();
+ /** Prepare to accept a connection from another talk client */
+ void listen();
+ /** Accept a connection from another talk client */
+ int accept();
+
+ /** Exchange the first 3 characters, which are edit characters */
+ void set_edit_chars();
+
+ /** Write data into the socket, by 16 char blocks. */
+ void write_banner(char * banner);
+
+ // Methods to retrieve some information
+ /** Returns the erase char used by the caller. */
+ char get_char_erase() { return char_erase; }
+ /** Returns the caller's name. */
+ char * get_caller_name() { return new_msg.r_name; } // idem in old_msg
+ /** Returns socket, for reading or writing */
+ int get_sockt() { return sockt; }
+ /** Returns response answer and id_num. Allows protocol independence.
+ * Each param can be null (0L). Then it isn't filled.*/
+ void getResponseItems(uint8_t * answer, uint32_t * id_num, struct talk_addr * addr);
+ /** Returns connection socket address. For FWT. */
+ const struct talk_addr get_addr() { return new_msg.addr; } // idem in old_msg
+
+ // Methods to cheat with this talk connection
+ // Used by the forwarding machine
+ void set_addr(const struct talk_addr * addr);
+ void set_ctl_addr(const struct talk_addr * ctl_addr);
+
+ /** Prints the system error message in the log and exits the current thread */
+ static void p_error(const char * str);
+
+ protected:
+
+ /** Used by open_sockets. */
+ int open_socket (struct sockaddr_in *addr, int type);
+ /** Check remote protocol. Used by ctl_transact. */
+ void findProtocol();
+ /** Find the correct IP address that the daemon at host
+ "destination" has to respond to */
+ static struct in_addr getReplyAddr (struct in_addr destination);
+ static struct in_addr defaultReplyAddr;
+
+ static short int talkDaemonPort; // Port number of talk demon (517)
+ static short int ntalkDaemonPort; // Port number of ntalk demon (518)
+
+ ProtocolType protocol;
+
+ /* inet addresses of the two machines */
+ struct in_addr my_machine_addr;
+ struct in_addr his_machine_addr;
+
+ int ctl_sockt;
+ int sockt;
+
+ OLD_CTL_MSG old_msg; // holds interesting data
+ NEW_CTL_MSG new_msg; // holds interesting data
+ OLD_CTL_RESPONSE old_resp; // only convenience structure for responses
+ NEW_CTL_RESPONSE new_resp; // only convenience structure for responses
+ struct talk_addr lookup_addr; // address returned by LOOKUP. Points to xxx_resp.addr
+
+ char char_erase;
+};
+
+#endif
diff --git a/ktalkd/ktalkd/options.cpp b/ktalkd/ktalkd/options.cpp
new file mode 100644
index 00000000..5596f8ef
--- /dev/null
+++ b/ktalkd/ktalkd/options.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright 1999-2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include "defs.h"
+
+struct sOptions Options = {
+/* answmach :*/ 1,
+/* time_before_answmach :*/ 20,
+/* sound :*/ 0,
+/* XAnnounce :*/ 1,
+/* soundfile :*/ {""},
+/* soundplayer :*/ {""},
+/* soundplayeropt :*/ {""},
+/* announce1 :*/ {ANNOUNCE1},
+/* announce2 :*/ {ANNOUNCE2},
+/* announce3 :*/ {ANNOUNCE3},
+/* invitelines :*/ {INVITE_LINES},
+/* mailprog :*/ {"mail.local"},
+/* NEU_behaviour :*/ 2,
+/* NEU_user :*/ {""},
+/* NEUBanner1 :*/ {NEU_BANNER1},
+/* NEUBanner2 :*/ {NEU_BANNER2},
+/* NEUBanner3 :*/ {NEU_BANNER3},
+/* NEU_forwardmethod :*/ {"FWR"},
+/* extprg :*/ {""},
+/* hostname :*/ {""},
+/* debug_mode :*/ 0
+};
diff --git a/ktalkd/ktalkd/options.h b/ktalkd/ktalkd/options.h
new file mode 100644
index 00000000..42b72350
--- /dev/null
+++ b/ktalkd/ktalkd/options.h
@@ -0,0 +1,71 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+/** This struct handles global options. Not user ones */
+
+#include <sys/utsname.h>
+#ifndef SYS_NMLN
+#define SYS_NMLN 65 /* max hostname size */
+#endif
+
+struct sOptions
+{
+ int answmach; /* used in talkd.cpp */
+ int time_before_answmach; /* in machines/answmach.cpp */
+ /* used in announce.cpp : */
+ int sound;
+ int XAnnounce;
+ char soundfile [S_CFGLINE];
+ char soundplayer [S_CFGLINE];
+ char soundplayeropt [S_CFGLINE];
+ char announce1 [S_CFGLINE];
+ char announce2 [S_CFGLINE];
+ char announce3 [S_CFGLINE];
+ char invitelines [S_INVITE_LINES];/* used in machines/answmach.cpp */
+ char mailprog [S_CFGLINE]; /* used in machines/answmach.cpp */
+ int NEU_behaviour;
+ char NEU_user[S_CFGLINE];
+ char NEUBanner1 [S_CFGLINE];
+ char NEUBanner2 [S_CFGLINE];
+ char NEUBanner3 [S_CFGLINE];
+ char NEU_forwardmethod [5];
+ char extprg [S_CFGLINE];
+ // No really an option, but it's useful to have it here :
+ char hostname[SYS_NMLN];
+ int debug_mode;
+};
+
+extern struct sOptions Options;
+
+#endif
diff --git a/ktalkd/ktalkd/otalkd.h b/ktalkd/ktalkd/otalkd.h
new file mode 100644
index 00000000..cacb17ca
--- /dev/null
+++ b/ktalkd/ktalkd/otalkd.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright 2002 David Faure <faure@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifndef OTALKD_H
+#define OTALKD_H
+
+#include "prot_talkd.h"
+
+#define OLD_NAME_SIZE 9
+
+/* Control Message structure for old talk protocol (earlier than BSD4.2) */
+
+typedef struct {
+ char type; /* request type, see below */
+ char l_name [OLD_NAME_SIZE]; /* caller's name */
+ char r_name [OLD_NAME_SIZE]; /* callee's name */
+ char pad;
+ int id_num; /* message id */
+ int pid; /* caller's process id */
+ char r_tty [TTY_SIZE]; /* callee's tty name */
+ struct talk_addr addr; /* socket address for connection */
+ struct talk_addr ctl_addr; /* control socket address */
+} OLD_CTL_MSG;
+
+/* Control Response structure for old talk protocol (earlier than BSD4.2) */
+
+typedef struct {
+ char type; /* type of request message, see below */
+ char answer; /* response to request message, see below */
+ char pad [2];
+ int id_num; /* message id */
+ struct talk_addr addr; /* address for establishing conversation */
+} OLD_CTL_RESPONSE;
+
+#endif
diff --git a/ktalkd/ktalkd/print.c b/ktalkd/ktalkd/print.c
new file mode 100644
index 00000000..ea8fe95c
--- /dev/null
+++ b/ktalkd/ktalkd/print.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/* debug print routines */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <config.h>
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <errno.h>
+#include "prot_talkd.h"
+#include "proto.h"
+
+#ifndef _PATH_VAR_LOG
+#define _PATH_VAR_LOG "/var/log/"
+#endif
+
+#define NTYPES 4
+static const char *types[NTYPES] = {
+ "LEAVE_INVITE",
+ "LOOK_UP",
+ "DELETE",
+ "ANNOUNCE"
+};
+
+#define NANSWERS 9
+static const char *answers[NANSWERS] = {
+ "SUCCESS",
+ "NOT_HERE",
+ "FAILED",
+ "MACHINE_UNKNOWN",
+ "PERMISSION_DENIED",
+ "UNKNOWN_REQUEST",
+ "BADVERSION",
+ "BADADDR",
+ "BADCTLADDR"
+};
+
+static int logging, badpackets;
+static int logfd, packfd;
+
+void
+set_debug(int l, int b)
+{
+ const char *file;
+ logging = l;
+ badpackets = b;
+ if (logging) {
+ file = _PATH_VAR_LOG "talkd.log";
+ logfd = open(file, O_WRONLY|O_APPEND);
+ if (logfd<0) {
+ syslog(LOG_WARNING, "%s: %s", file, strerror(errno));
+ logging = 0;
+ }
+ }
+ if (badpackets) {
+ file = _PATH_VAR_LOG "talkd.packets";
+ packfd = open(file, O_WRONLY|O_APPEND);
+ if (packfd<0) {
+ syslog(LOG_WARNING, "%s: %s", file, strerror(errno));
+ badpackets = 0;
+ }
+ }
+}
+
+/****************** ktalkd addition ************/
+
+/* print_addr is a debug print routine for sockaddr_in structures.
+ * Call with a structure in network byte order.
+ * @param cp a string to identify the log output
+ * @param addr the address to read
+ * This code is obviously NOT IPv6 ready :)
+ */
+static void print_addr(const char *cp, struct sockaddr_in * addr)
+{
+ int a0,a1,a2,a3;
+ unsigned int s_add = addr->sin_addr.s_addr;
+ char buf[1024];
+ a0 = s_add % 256L;
+ s_add /= 256L;
+ a1 = s_add % 256L;
+ s_add /= 256L;
+ a2 = s_add % 256L;
+ s_add /= 256L;
+ a3 = s_add % 256L;
+ snprintf(buf, sizeof(buf), "%s: addr = %d.%d.%d.%d port = %o, family = %o",
+ cp, a0, a1, a2, a3, ntohs(addr->sin_port), ntohs(addr->sin_family));
+ write(logfd, buf, strlen(buf));
+}
+
+/**********************************************/
+
+static const char *
+print_type(int type)
+{
+ static char rv[80];
+ if (type > NTYPES) {
+ snprintf(rv, sizeof(rv), "type %d", type);
+ return rv;
+ }
+ return types[type];
+}
+
+static const char *
+print_answer(int answer)
+{
+ static char rv[80];
+ if (answer > NANSWERS) {
+ snprintf(rv, sizeof(rv), "answer %d", answer);
+ return rv;
+ }
+ return answers[answer];
+}
+
+void
+print_request(const char *cp, const CTL_MSG *mp)
+{
+ char lu[NAME_SIZE+1], ru[NAME_SIZE+1], tt[TTY_SIZE+1];
+ char buf[1024];
+ const char *tp;
+ if (!logging) return;
+
+ tp = print_type(mp->type);
+ strncpy(lu, mp->l_name, sizeof(lu));
+ strncpy(ru, mp->r_name, sizeof(ru));
+ strncpy(tt, mp->r_tty, sizeof(tt));
+ lu[sizeof(lu)-1]=0;
+ ru[sizeof(ru)-1]=0;
+ tt[sizeof(tt)-1]=0;
+
+ snprintf(buf, sizeof(buf),
+ "%s: %s: id %u, l_user %s, r_user %s, r_tty %s\n",
+ cp, tp, mp->id_num, lu, ru, tt);
+ write(logfd, buf, strlen(buf));
+ print_addr(" addr", (struct sockaddr_in *)&mp->addr);
+ print_addr(" ctl_addr", (struct sockaddr_in *)&mp->ctl_addr);
+}
+
+void
+print_response(const char *cp, const CTL_RESPONSE *rp)
+{
+ char buf[1024];
+ const char *tp, *ap;
+ if (!logging) return;
+
+ tp = print_type(rp->type);
+ ap = print_answer(rp->answer);
+
+ snprintf(buf, sizeof(buf),
+ "%s: %s <-- %s, id %d\n",
+ cp, tp, ap, ntohl(rp->id_num));
+ write(logfd, buf, strlen(buf));
+ if ((rp->type == LOOK_UP) && (rp->answer == SUCCESS))
+ print_addr(" resp addr", (struct sockaddr_in *)&rp->addr);
+}
+
+void
+ktalk_debug(const char *format, ...)
+{
+ char buf[1024];
+ va_list ap;
+ if (!logging) return;
+
+ va_start(ap, format);
+ vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ write(logfd, buf, strlen(buf));
+}
+
+void
+print_broken_packet(const char *pack, size_t len, struct sockaddr_in *from)
+{
+ size_t i;
+ char tmp[4], buf[128];
+ if (!badpackets) return;
+ snprintf(buf, sizeof(buf), "From: %s [%u]",
+ inet_ntoa(from->sin_addr), from->sin_addr.s_addr);
+ write(packfd, buf, strlen(buf));
+ for (i=0; i<len; i++) {
+ if (i%24 == 0) write(packfd, "\n ", 5);
+ snprintf(tmp, sizeof(tmp), "%02x ", (unsigned char)pack[i]);
+ write(packfd, tmp, strlen(tmp));
+ }
+ write(packfd, "\n", 1);
+}
diff --git a/ktalkd/ktalkd/process.cpp b/ktalkd/ktalkd/process.cpp
new file mode 100644
index 00000000..6c638d57
--- /dev/null
+++ b/ktalkd/ktalkd/process.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California,
+ * (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * process.cpp handles the requests, which can be of three types:
+ * ANNOUNCE - announce to a user that a talk is wanted
+ * LEAVE_INVITE - insert the request into the table
+ * LOOK_UP - look up to see if a request is waiting in
+ * in the table for the local user
+ * DELETE - delete invitation
+ */
+
+#include "includ.h"
+#include <sys/param.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <netdb.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "process.h"
+#include "proto.h"
+#include "announce.h"
+#include "find_user.h"
+#include "table.h"
+#include "readconf.h"
+#include "defs.h"
+#include "machines/forwmach.h"
+
+extern KTalkdTable * ktable;
+
+int prepare_response(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp)
+{
+ rp->type = mp->type;
+ rp->id_num = htonl(0);
+ rp->vers = mp->vers;
+ if ((mp->vers != 0) && (mp->vers != TALK_VERSION)) {
+ syslog(LOG_WARNING, "Bad protocol version %d", mp->vers);
+ rp->answer = BADVERSION;
+ return 0;
+ }
+ mp->id_num = ntohl(mp->id_num);
+#if 0 // not in the original talkd anymore?
+ mp->addr.ta_family = ntohs(mp->addr.ta_family);
+ mp->ctl_addr.ta_family = ntohs(mp->ctl_addr.ta_family);
+ if (mp->ctl_addr.ta_family != AF_INET) {
+ syslog(LOG_WARNING, "Bad address, family %d",
+ mp->ctl_addr.ta_family);
+ rp->answer = BADCTLADDR;
+ return 0;
+ }
+#endif
+ mp->pid = ntohl(mp->pid);
+ return 1; /* Ok */
+}
+
+int process_request(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost)
+{
+ NEW_CTL_MSG *ptr;
+ int ret;
+ int usercfg = 0; /* set if a user config file exists */
+
+ print_request("process_request", mp);
+
+ if (!prepare_response(mp, rp))
+ return PROC_REQ_ERR;
+
+ /* Ensure null-termination */
+ mp->l_name[sizeof(mp->l_name)-1] = 0;
+ mp->r_name[sizeof(mp->r_name)-1] = 0;
+ mp->r_tty[sizeof(mp->r_tty)-1] = 0;
+
+ ret = PROC_REQ_OK;
+
+ switch (mp->type) {
+
+ case ANNOUNCE:
+ /* Open user config file. */
+ usercfg = init_user_config(mp->r_name);
+ ret = do_announce(mp, rp, theirhost, usercfg);
+ if (usercfg) end_user_config();
+
+ /* Store in table if normal announce or answmach replacing it.
+ Not if re-announce, nor if error, nor for forwarding machine */
+ if ((ret == PROC_REQ_OK) || (ret == PROC_REQ_ANSWMACH_NOT_LOGGED)
+ || (ret == PROC_REQ_ANSWMACH_NOT_HERE))
+ ktable->insert_table(mp, rp, 0L);
+
+ case LEAVE_INVITE:
+ ptr = ktable->find_request(mp);
+ if (ptr != (NEW_CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ } else
+ ktable->insert_table(mp, rp, 0L);
+ break;
+
+ case LOOK_UP:
+ ptr = ktable->find_match(mp);
+ if (ptr != (NEW_CTL_MSG *)0) {
+ rp->id_num = htonl(ptr->id_num);
+ rp->addr = ptr->addr;
+ rp->addr.ta_family = htons(ptr->addr.ta_family);
+ rp->answer = SUCCESS;
+ } else {
+ if (ForwMachine::forwMachProcessLookup(ktable->getTable(), mp)) {
+ ret = PROC_REQ_FORWMACH; // Don't send any response, forwmach will do it
+ } else
+ rp->answer = NOT_HERE;
+ }
+ break;
+
+ case DELETE:
+ rp->answer = ktable->delete_invite(mp->id_num);
+ break;
+
+ default:
+ rp->answer = UNKNOWN_REQUEST;
+ break;
+ }
+ if (ret != PROC_REQ_FORWMACH)
+ print_response("=> response", rp);
+ if (mp->vers == 0) { // it's kotalkd talking to us.
+ // Let's prepare an OTALK response, shifting the first 2 fields
+ rp->vers /*type in otalk*/ = rp->type;
+ rp->type /*answer in otalk*/ = rp->answer;
+ }
+ return ret;
+}
+
+int do_announce(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost, int usercfg)
+{
+ NEW_CTL_MSG *ptr;
+ int result;
+ char disp[DISPLAYS_LIST_MAX];
+ char forward[S_CFGLINE], forwardMethod[4];
+ char * callee;
+ ForwMachine * fwm;
+
+ /* Check if already in the table */
+ ptr = ktable->find_request(mp);
+
+ /* TODO use Voodo #1 from current process.c for byte-swapping stuff */
+
+ if ((ptr != NULL) && (
+ (mp->id_num <= ptr->id_num) || (mp->id_num == (uint32_t)~0x0L))) {
+ /* a duplicated request, so ignore it */
+ ktalk_debug("dupannounce %d", mp->id_num);
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = SUCCESS;
+ return PROC_REQ_ERR;
+
+ } else {
+ if (ptr == (NEW_CTL_MSG *) 0) { /* Not already in the table */
+ /* see if a forward has been set up */
+ if ( (usercfg)
+ && (read_user_config("Forward", forward, S_CFGLINE))
+ && (read_user_config("ForwardMethod", forwardMethod, 4 )) )
+ {
+ fwm = new ForwMachine(mp, forward, forwardMethod, mp->id_num);
+ /* Store in table, because :
+ (Any method) : this allows to detect dupannounces.
+ (FWR & FWT) : we'll receive the LOOK_UP */
+ ktable->insert_table(mp, 0L, fwm);
+ fwm->start(mp->id_num);
+ return PROC_REQ_FORWMACH;
+ }
+ }
+
+ /* see if the user is logged */
+ char tty[ UT_LINESIZE ]; // mp->r_tty may be smaller then UT_LINESIZE
+ *tty = '\0';
+ result = find_user(mp->r_name, tty, disp);
+ strlcpy( mp->r_tty, tty, sizeof( mp->r_tty ));
+
+ ktalk_debug("find_user : result = %d",result);
+
+ if (result != SUCCESS) {
+ ktalk_debug("Couldn t find user ...");
+ if (result == NOT_HERE)
+ { /* Not here ? -> Start answering machine ! */
+ if (getpwnam(mp->r_name)) /* Does the user exist ? */
+ { /* Yes ! -> SUCCESS. */
+ ktalk_debug("Not logged.");
+ rp->answer = SUCCESS;
+ endpwent();
+ return PROC_REQ_ANSWMACH_NOT_LOGGED; /* answer machine. */
+ } else
+ { /* Non-existent user ... */
+ endpwent();
+ /* output an error into the logs */
+
+ syslog(LOG_ERR,"User unknown : %s.",mp->r_name);
+ syslog(LOG_ERR,"The caller is : %s.",mp->l_name);
+
+ switch (Options.NEU_behaviour) {
+ case 2: /* Paranoid setting. Do nothing. */
+ ktalk_debug("Paranoid setting. Do nothing.");
+ rp->answer = NOT_HERE;
+ return PROC_REQ_ERR;
+ case 0: /* Launch answering machine. */
+ ktalk_debug("Not here.");
+ rp->answer = SUCCESS;
+ return PROC_REQ_ANSWMACH_NOT_HERE;
+ case 1: /* NEU_user will take the talk. */
+ ktalk_debug("Not here. I ll take the talk.");
+ fwm = new ForwMachine(mp, Options.NEU_user,
+ Options.NEU_forwardmethod, mp->id_num);
+ /* store in table, because we'll receive the LOOK_UP */
+ ktable->insert_table(mp, 0L, fwm);
+ fwm->start(mp->id_num);
+ return PROC_REQ_FORWMACH;
+ } /* switch */
+ } /* getpwnam */
+ } /* result */
+ else {
+ ktalk_debug("not SUCCESS, nor NOT_HERE");
+ rp->answer = result; /* not SUCCESS, nor NOT_HERE*/
+ return PROC_REQ_ERR;
+ }
+ }
+
+ /* Check if there is a forwarding machine on this machine,
+ matching answerer = r_name and caller = l_name
+ Then set callee to the initial callee, to display in ktalkdlg */
+ callee = ForwMachine::forwMachFindMatch(ktable->getTable(), mp);
+
+ if (ptr == (NEW_CTL_MSG *) 0) { /* Not already in the table => announce */
+ rp->answer = announce(mp, theirhost, disp, usercfg, callee);
+ if (rp->answer == PERMISSION_DENIED) return PROC_REQ_ERR;
+ ktalk_debug("Announce done.");
+ return PROC_REQ_OK;
+ } else {
+ /* This is an explicit re-announce, so update the id_num
+ * field to avoid duplicates and re-announce the talk. */
+ int new_id_num = ktable->new_id();
+ if (Options.debug_mode)
+ syslog(LOG_DEBUG, "reannounce : updating id %d to id %d",
+ ptr->id_num, new_id_num);
+ ptr->id_num = new_id_num; /* update in the table */
+ rp->id_num = htonl(ptr->id_num);
+ rp->answer = announce(mp, theirhost, disp, usercfg, callee);
+ return PROC_REQ_ANSWMACH;
+ }
+ }
+}
diff --git a/ktalkd/ktalkd/process.h b/ktalkd/ktalkd/process.h
new file mode 100644
index 00000000..090824ff
--- /dev/null
+++ b/ktalkd/ktalkd/process.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include "includ.h"
+
+int process_request(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost );
+int do_announce(NEW_CTL_MSG *mp, NEW_CTL_RESPONSE *rp, const char* theirhost, int usercfg);
+
diff --git a/ktalkd/ktalkd/prot_talkd.h b/ktalkd/ktalkd/prot_talkd.h
new file mode 100644
index 00000000..eca1e64b
--- /dev/null
+++ b/ktalkd/ktalkd/prot_talkd.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ * @(#)talkd.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _PROTOCOLS_TALKD_H
+#define _PROTOCOLS_TALKD_H
+
+/*
+ * This describes the protocol used by the talk server and clients.
+ *
+ * The talk server acts a repository of invitations, responding to
+ * requests by clients wishing to rendezvous for the purpose of
+ * holding a conversation. In normal operation, a client, the caller,
+ * initiates a rendezvous by sending a CTL_MSG to the server of
+ * type LOOK_UP. This causes the server to search its invitation
+ * tables to check if an invitation currently exists for the caller
+ * (to speak to the callee specified in the message). If the lookup
+ * fails, the caller then sends an ANNOUNCE message causing the server
+ * to broadcast an announcement on the callee's login ports requesting
+ * contact. When the callee responds, the local server uses the
+ * recorded invitation to respond with the appropriate rendezvous
+ * address and the caller and callee client programs establish a
+ * stream connection through which the conversation takes place.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <sys/types.h>
+
+/*
+ * This is a copy of 4.3BSD struct sockaddr.
+ */
+struct talk_addr {
+ uint16_t ta_family;
+ uint16_t ta_port;
+ uint32_t ta_addr;
+ uint32_t ta_junk1;
+ uint32_t ta_junk2;
+};
+
+
+/*
+ * Client->server request message format.
+ */
+#define NAME_SIZE 12
+#define TTY_SIZE 16
+
+typedef struct {
+ uint8_t vers; /* protocol version */
+ uint8_t type; /* request type, see below */
+ uint8_t answer; /* not used */
+ uint8_t pad;
+ uint32_t id_num; /* message id */
+ struct talk_addr addr; /* address of client tcp port */
+ struct talk_addr ctl_addr; /* address of client udp port */
+ uint32_t pid; /* caller's process id */
+ char l_name[NAME_SIZE];/* caller's name */
+ char r_name[NAME_SIZE];/* callee's name */
+ char r_tty[TTY_SIZE];/* callee's tty name */
+} CTL_MSG;
+
+/*
+ * Server->client response message format.
+ */
+typedef struct {
+ uint8_t vers; /* protocol version */
+ uint8_t type; /* type of request message, see below */
+ uint8_t answer; /* respose to request message, see below */
+ uint8_t pad;
+ uint32_t id_num; /* message id */
+ struct talk_addr addr; /* address for establishing conversation */
+} CTL_RESPONSE;
+
+#define TALK_VERSION 1 /* protocol version */
+
+/* message type values */
+#define LEAVE_INVITE 0 /* leave invitation with server */
+#define LOOK_UP 1 /* check for invitation by callee */
+#define DELETE 2 /* delete invitation by caller */
+#define ANNOUNCE 3 /* announce invitation by caller */
+
+/* answer values */
+#define SUCCESS 0 /* operation completed properly */
+#define NOT_HERE 1 /* callee not logged in */
+#define FAILED 2 /* operation failed for unexplained reason */
+#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */
+#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */
+#define UNKNOWN_REQUEST 5 /* request has invalid type value */
+#define BADVERSION 6 /* request has invalid protocol version */
+#define BADADDR 7 /* request has invalid addr value */
+#define BADCTLADDR 8 /* request has invalid ctl_addr value */
+
+/*
+ * Operational parameters.
+ * RING_WAIT should be 10's of seconds less than MAX_LIFE
+ */
+#define MAX_LIFE 60 /* max time daemon saves invitations */
+#define RING_WAIT 30 /* time to wait before resending invitation */
+
+#endif /* !_PROTOCOLS_TALKD_H */
diff --git a/ktalkd/ktalkd/proto.h b/ktalkd/ktalkd/proto.h
new file mode 100644
index 00000000..7679a9f2
--- /dev/null
+++ b/ktalkd/ktalkd/proto.h
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 1983, 1993
+ *· The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* quirks for repairs.c */
+
+#define QUIRK_NONE 0
+#define QUIRK_OTALK 1
+
+struct sockaddr_in;
+
+extern char ourhostname[];
+
+/* print.c */
+void print_request(const char *cp, const CTL_MSG *mp);
+void print_response(const char *cp, const CTL_RESPONSE *rp);
+void print_broken_packet(const char *pack, size_t len, struct sockaddr_in *);
+void ktalk_debug(const char *fmt, ...);
+void set_debug(int logging, int badpackets);
+
+/* table.c */
+/*void insert_table(CTL_MSG *request, CTL_RESPONSE *response);
+ CTL_MSG *find_request(CTL_MSG *request);
+ CTL_MSG *find_match(CTL_MSG *request);
+*/
+
+/* repairs.c */
+uint32_t byte_swap32(uint32_t);
+int rationalize_packet(char *buf, size_t len, size_t maxlen,
+ struct sockaddr_in *);
+size_t irrationalize_reply(char *buf, size_t maxbuf, int quirk);
+
+/* other */
+/*int announce(CTL_MSG *request, const char *remote_machine);
+void process_request(CTL_MSG *mp, CTL_RESPONSE *rp, const char *fromhost);
+int new_id(void);
+int delete_invite(unsigned id_num);
+*/
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ktalkd/ktalkd/readcfg++.cpp b/ktalkd/ktalkd/readcfg++.cpp
new file mode 100644
index 00000000..07a10dc2
--- /dev/null
+++ b/ktalkd/ktalkd/readcfg++.cpp
@@ -0,0 +1,261 @@
+/* This file is part of ktalkd
+ *
+ * Copyright (C) 1997 David Faure (faure@kde.org)
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+/*
+ * Routines for reading configuration from KDE configuration
+ * for ktalkd.
+ */
+
+/* Unix includes */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+
+/* KDE & Qt includes */
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kinstance.h>
+#include <qfile.h>
+
+/* Ktalkd includes */
+#include "readconf.h"
+#include "defs.h"
+#include <config.h>
+
+#include "proto.h"
+
+/** Converts a string s into a boolean. Handles 0/1, on/off, true/false.
+ * (case insensitive) */
+int booleanresult(const char * s)
+{
+ if (strlen(s)==1)
+ { return atoi(s); }
+ else if ((!strncasecmp(s,"on",2))||(!strncasecmp(s,"true",4))) {return 1;}
+ else if ((!strncasecmp(s,"off",3))||(!strncasecmp(s,"false",5))){return 0;}
+ else {
+ syslog(LOG_ERR,"Wrong boolean value %s in ktalkdrc",s);
+ return 0;
+ }
+}
+
+/* User configuration file, ktalkdrc in localconfigdir(). */
+KConfig * ktalkdcfg = 0;
+/* User config file for talk announces, ktalkannouncerc in localconfigdir(). */
+KConfig * ktkanncfg = 0;
+
+/* Initiate user-config-file reading. */
+int init_user_config(const char * l_name)
+{
+ struct passwd * pw = getpwnam(l_name);
+
+ if (!pw) return 0;
+ else {
+ struct stat buf;
+ QString cfgFileName, tkannFileName;
+
+ /* Set $HOME, because KInstance uses it */
+ setenv("HOME",pw->pw_dir,1 /* overwrite */);
+ unsetenv("KDEHOME");
+ unsetenv("XAUTHORITY");
+ktalk_debug("%s",pw->pw_dir);
+ KInstance tmpInstance("tmpInstance");
+ cfgFileName = locateLocal("config", "ktalkdrc", &tmpInstance );
+ tkannFileName = locateLocal("config", "ktalkannouncerc", &tmpInstance);
+ endpwent();
+ktalk_debug("%s","endpwent");
+
+//WABA: New KConfig should be able to handle this gracefully:
+ if (stat(QFile::encodeName(tkannFileName),&buf)!=-1) {
+ // check if it exists, 'cause otherwise it would be created empty with
+ // root as owner !
+ ktkanncfg = new KConfig(tkannFileName);
+ ktkanncfg -> setGroup("ktalkannounce");
+ ktkanncfg -> setDollarExpansion(true);
+ } else ktkanncfg = 0L;
+ if (stat(QFile::encodeName(cfgFileName),&buf)!=-1) {
+ // check if it exists, 'cause otherwise it would be created empty with
+ // root as owner !
+ ktalkdcfg = new KConfig(cfgFileName);
+ ktalkdcfg -> setGroup("ktalkd");
+ ktalkdcfg -> setDollarExpansion(true);
+ ktalk_debug("User config file ok");
+ } else {
+ ktalkdcfg = 0L;
+ ktalk_debug("No user config file %s !",cfgFileName.ascii());
+ }
+ktalk_debug("%s","done");
+ return ((ktkanncfg != 0L) || (ktalkdcfg != 0L));
+ /* Return true if at least one file exists */
+ }
+}
+
+/*
+ * Read one entry in user-config-file
+ */
+
+int read_user_config(const char * key, char * result, int max)
+{
+ KConfig * cfg;
+ if (!strncmp(key,"Sound",5))
+ // Any key starting with Sound is in ktalkannouncerc
+ // talkprg is there too, but we don't care about it here
+ cfg = ktkanncfg;
+ else
+ cfg = ktalkdcfg;
+
+ if (!cfg) return 0; // file doesn't exist
+ QString Qresult;
+ if ((Qresult = cfg -> readEntry(key, "unset")) != "unset")
+ {
+ qstrncpy( result, Qresult.ascii(), max);
+
+ if (Options.debug_mode) syslog(LOG_DEBUG,"User option %s : %s", key, result);
+ return 1;
+ }
+ else
+ {
+ ktalk_debug("User option %s NOT found", key);
+ return 0;
+ }
+}
+
+int read_bool_user_config(const char * key, int * result)
+{
+ char msgtmpl[S_CFGLINE];
+ int ret = read_user_config(key, msgtmpl, S_CFGLINE); // ret=1 if found
+ if (ret!=0) *result = booleanresult(msgtmpl);
+ return ret;
+}
+
+// Close user-config-file and destroys objects used.
+
+void end_user_config()
+{
+ if (ktalkdcfg) delete ktalkdcfg;
+ if (ktkanncfg) delete ktkanncfg;
+ ktalkdcfg = 0L;
+ ktkanncfg = 0L;
+}
+
+// System configuration file
+
+int process_config_file(void)
+{
+ // Where is ktalkdlg installed ?
+ QString ktalkdlg_dir = locate("exe", "ktalkdlg");
+ ktalkdlg_dir.truncate( ktalkdlg_dir.findRev('/') );
+ // Has to be done, for any $KDEBINDIR in ktalkdrc.
+ setenv("KDEBINDIR", QFile::encodeName(ktalkdlg_dir), 0/*don't overwrite*/);
+
+ KConfig * syscfg = new KConfig( "ktalkdrc" );
+
+ syscfg -> setGroup("ktalkd");
+ syscfg -> setDollarExpansion(true);
+
+ QString result;
+
+#define found(k) (!(result = syscfg -> readEntry(k)).isEmpty())
+ // QString cfgStr = cfgStr0.stripWhiteSpace();
+
+ if (found("AnswMach")) {
+ Options.answmach=booleanresult(result.ascii());
+ ktalk_debug("AnswMach : %d",Options.answmach);}
+
+ if (found("XAnnounce")) {
+ Options.XAnnounce=booleanresult(result.ascii());
+ ktalk_debug("XAnnounce : %d",Options.XAnnounce); }
+
+ if (found("Time")) {
+ Options.time_before_answmach=result.toInt();
+ ktalk_debug("Time : %d",Options.time_before_answmach); }
+
+ if (found("Sound")) {
+ Options.sound=booleanresult(result.ascii());
+ ktalk_debug("Sound : %d",Options.sound); }
+
+ if (found("SoundFile")) {
+ qstrncpy(Options.soundfile,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundFile = %s",Options.soundfile); }
+
+ if (found("SoundPlayer")) {
+ qstrncpy(Options.soundplayer,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundPlayer = %s",Options.soundplayer); }
+
+ if (found("SoundPlayerOpt")) {
+ qstrncpy(Options.soundplayeropt,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("SoundPlayerOpt = %s",Options.soundplayeropt); }
+
+ if (found("MailProg")) {
+ qstrncpy(Options.mailprog,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("Mail prog = %s",Options.mailprog); }
+
+ /* text based announcement */
+ if (found("Announce1")) { qstrncpy(Options.announce1,result.local8Bit(),S_CFGLINE); }
+ if (found("Announce2")) { qstrncpy(Options.announce2,result.local8Bit(),S_CFGLINE); }
+ if (found("Announce3")) { qstrncpy(Options.announce3,result.local8Bit(),S_CFGLINE); }
+
+ if (found("NEUUser")) {
+ qstrncpy(Options.NEU_user,result.local8Bit(),S_CFGLINE);
+ ktalk_debug("NEUUser = %s", Options.NEU_user);
+ }
+ if (found("NEUBehaviour")) {
+ Options.NEU_behaviour=result.toInt();
+ ktalk_debug("NEUBehaviour : %d",Options.NEU_behaviour);
+ }
+ if (found("NEUForwardMethod")) {
+ qstrncpy(Options.NEU_forwardmethod,result.ascii(),5);
+ ktalk_debug("NEUForwardMethod = %s", Options.NEU_forwardmethod);
+ }
+
+ if (found("ExtPrg")) {
+ qstrncpy(Options.extprg,QFile::encodeName(result),S_CFGLINE);
+ ktalk_debug("Ext prg = %s",Options.extprg); }
+ else { /* has to work even without config file at all */
+ KStandardDirs stddirs;
+ qstrncpy(Options.extprg, QFile::encodeName(stddirs.findResource("exe","ktalkdlg")), S_CFGLINE-1);
+ }
+
+ delete syscfg;
+ ktalk_debug("End of global configuration");
+ return 1;
+}
+
+
diff --git a/ktalkd/ktalkd/readconf.cpp b/ktalkd/ktalkd/readconf.cpp
new file mode 100644
index 00000000..7b48b0c1
--- /dev/null
+++ b/ktalkd/ktalkd/readconf.cpp
@@ -0,0 +1,188 @@
+/*
+ * readconf.cpp
+ *
+ * Routines for reading talkd.conf and .talkdrc
+ *
+ * Copyright 1999-2002, David Faure, faure@kde.org
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include "proto.h"
+#include "readconf.h"
+#include "defs.h"
+
+FILE * fd;
+
+int booleanresult(const char * s)
+{
+ if (strlen(s)==1)
+ { return atoi(s); }
+ else if ((!strncasecmp(s,"on",2))||(!strncasecmp(s,"true",4))) {return 1;}
+ else if ((!strncasecmp(s,"off",3))||(!strncasecmp(s,"false",5))){return 0;}
+ else {
+ syslog(LOG_ERR,"Wrong boolean value %s in %s",s,TALKD_CONF);
+ return 0;
+ }
+}
+
+/* obsolete routine, but still used for .talkdrc */
+int read_user_config(const char * key, char * result, int max)
+{
+ char * value;
+ char * buff = new char[S_CFGLINE];
+ char * ret;
+ fseek(fd,0,SEEK_SET);
+ do {
+ ret = fgets(buff,S_CFGLINE,fd);
+ } while ((ret) && (strncasecmp(buff,key,strlen(key))));
+ if (ret) {
+ value = strchr(buff,':')+1;
+ while (isspace(*value)) value++; /* get rid of spaces, tabs... */
+ strncpy(result,value,max);
+ result[max-1]='\0'; /* in case it was longer than max chars */
+ char * lastchar = result + strlen(result) - 1; /* points to last char */
+ if (*lastchar=='\n') *lastchar = '\0'; /* get rid of \n */
+ }
+ delete buff;
+ if (Options.debug_mode) {
+ if (ret)
+ syslog(LOG_DEBUG,"read_user_config : %s, %s",key,result);
+ else
+ syslog(LOG_DEBUG,"read_user_config : %s -> not found",key);
+ }
+ return (ret) ? 1 : 0;
+}
+
+int read_bool_user_config(const char * key, int * result)
+{
+ char msgtmpl[S_CFGLINE];
+ int ret = read_user_config(key, msgtmpl, S_CFGLINE); // ret=1 if found
+ if (ret!=0) *result = booleanresult(msgtmpl);
+ return ret;
+}
+
+int init_user_config(const char * l_name)
+{
+#define S_MSGPATH 50
+ char msgpath[S_MSGPATH];
+ struct passwd * pw = getpwnam(l_name);
+ if (!pw) return 0;
+ else {
+ snprintf(msgpath, S_MSGPATH, "%s/.talkdrc", pw->pw_dir);
+ endpwent();
+ fd=fopen(msgpath, "r");
+ return (fd != 0);
+ }
+}
+
+void end_user_config()
+{
+ fclose(fd);
+}
+
+/* routine for reading talkd.conf */
+
+int process_config_file(void)
+{
+ FILE * fd = fopen(TALKD_CONF,"r");
+ char * ret;
+ char buff[S_CFGLINE];
+ char * result;
+
+#define found(k) (!strncasecmp(buff,k,strlen(k)))
+
+ if (!fd) { return 0; }
+ do {
+ ret = fgets(buff,S_CFGLINE,fd);
+ if ((ret) && (*buff!='#') && ((result = strchr(buff,':')))) {
+ result++;
+ while (isspace(*result))
+ result++; /* get rid of spaces, tabs... */
+ result[strlen(result)-1]='\0'; /* get rid of \n */
+
+ if (found("AnswMach:")) {
+ Options.answmach=booleanresult(result);
+ debug("AnswMach : %d",Options.answmach);}
+
+ if (found("XAnnounce:")) {
+ Options.XAnnounce=booleanresult(result);
+ debug("XAnnounce : %d",Options.XAnnounce); }
+
+ if (found("Time:")) {
+ Options.time_before_answmach=atoi(result);
+ debug("Time : %d",Options.time_before_answmach); }
+
+ if (found("Sound:")) {
+ Options.sound=booleanresult(result);
+ debug("Sound : %d",Options.sound); }
+
+ if (found("SoundFile:")) {
+ strncpy(Options.soundfile,result,S_CFGLINE);
+ debug("SoundFile =");debug(Options.soundfile); }
+
+ if (found("SoundPlayer:")) {
+ strncpy(Options.soundplayer,result,S_CFGLINE);
+ debug("SoundPlayer ="); debug(result); }
+
+ if (found("SoundPlayerOpt:")) {
+ strncpy(Options.soundplayeropt,result,S_CFGLINE);
+ debug("SoundPlayerOpt ="); debug(result); }
+
+ if (found("MailProg:")) {
+ strncpy(Options.mailprog,result,S_CFGLINE);
+ debug("Mail prog ="); debug(result); }
+
+ /* text based announcement */
+ if (found("Announce1")) { strncpy(Options.announce1,result,S_CFGLINE); }
+ if (found("Announce2")) { strncpy(Options.announce2,result,S_CFGLINE); }
+ if (found("Announce3")) { strncpy(Options.announce3,result,S_CFGLINE); }
+
+ if (found("NEUUser")) { strncpy(Options.NEU_user,result,S_INVITE_LINES); }
+ if (found("NEUBehaviour")) { Options.NEU_behaviour=booleanresult(result); }
+ if (found("NEUBanner1")) { strncpy(Options.NEUBanner1,result,S_CFGLINE); }
+ if (found("NEUBanner2")) { strncpy(Options.NEUBanner2,result,S_CFGLINE); }
+ if (found("NEUBanner3")) { strncpy(Options.NEUBanner3,result,S_CFGLINE); }
+ if (found("NEUForwardMethod")) { strncpy(Options.NEU_forwardmethod,result,5); }
+
+ }
+ } while (ret);
+ fclose(fd);
+ return 1;
+}
diff --git a/ktalkd/ktalkd/readconf.h b/ktalkd/ktalkd/readconf.h
new file mode 100644
index 00000000..b80b6a02
--- /dev/null
+++ b/ktalkd/ktalkd/readconf.h
@@ -0,0 +1,69 @@
+/* This file is part of ktalkd
+ *
+ * Copyright (C) 1997 David Faure (faure@kde.org)
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/** Warning to ktalkd hackers : this file is the definition of a
+ * generic interface for reading systemwide and user configuration.
+ * Two files implement it : readconf.cpp for non-KDE users
+ * and readcfg++.cpp for KDE users.
+ */
+
+ /**
+ * Initiate user-config-file reading.
+ *
+ * @param l_name User name
+ */
+int init_user_config(const char * l_name);
+
+ /**
+ * Read one entry in user-config-file.
+ *
+ * @param key Key, such as "Mail", "Subj", "Head", "Msg1".."Msg3"
+ */
+int read_user_config(const char * key, char * result, int max);
+
+ /**
+ * Read a boolean entry in user-config-file.
+ *
+ * @param key Key, such as "Sound"
+ */
+int read_bool_user_config(const char * key, int * result);
+
+ /**
+ * Close user-config-file and destroys objects used.
+ */
+void end_user_config();
+
+ /**
+ * Read all site-wide configuration in one pass
+ */
+int process_config_file(void);
diff --git a/ktalkd/ktalkd/repairs.c b/ktalkd/ktalkd/repairs.c
new file mode 100644
index 00000000..9473a473
--- /dev/null
+++ b/ktalkd/ktalkd/repairs.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright 1998 David A. Holland.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder(s) nor the names of their
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+char repairs_rcsid[] =
+ "$Id$";
+
+/*
+ * Most, but not quite all, of the voodoo for detecting and handling
+ * malformed packets goes here.
+ *
+ * Basically anything which we can detect by examining the packet we
+ * try to do in rationalize_packet(). We return a quirk code for what
+ * we did so we can send back replies that will make sense to the other
+ * end. That's irrationalize_reply().
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <syslog.h>
+#include "prot_talkd.h"
+#include "proto.h"
+
+uint32_t
+byte_swap32(uint32_t k)
+{
+ return (k<<24) | ((k&0xff00) << 8) | ((k>>8) & 0xff00) | (k>>24);
+}
+
+static uint16_t
+byte_swap16(uint16_t k)
+{
+ return (k<<8) | (k>>8);
+}
+
+/***************************************************/
+
+/*
+ * probe for strings that are meaningful in talkd packets.
+ * rejects all control characters and delete. newlines and tabs have
+ * no business in tty names or usernames.
+ */
+static int probe_string(const char *buf, size_t len) {
+ size_t i;
+ int ch;
+ for (i=0; i<len; i++) {
+ if (buf[i]==0) return 0; /* success */
+ ch = (unsigned char) buf[i];
+ if ((ch&127) < 32 || (ch&127)==127) return -1;
+ }
+ return -1; /* no null-terminator, assume it's not a string */
+}
+
+/*
+ * Check if an address from a talk packet matches the actual sender
+ * address. If it doesn't, it's a good bet it's not the right packet format.
+ * Allow assorted endianness though.
+ * In an ideal world we'd save the endianness info for use elsewhere instead
+ * of reprobing it, but oh well.
+ */
+static int probe_addr(struct talk_addr *ta, struct sockaddr_in *sn) {
+ uint16_t family = sn->sin_family;
+ uint16_t xfamily = byte_swap16(family);
+ uint16_t port = sn->sin_port;
+ uint16_t xport = byte_swap16(port);
+ uint32_t addr = sn->sin_addr.s_addr;
+ uint32_t xaddr = byte_swap32(addr);
+
+ if (ta->ta_family != family && ta->ta_family != xfamily) return -1;
+ if (ta->ta_port != port && ta->ta_port != xport) return -1;
+ if (ta->ta_addr != addr && ta->ta_addr != xaddr) return -1;
+ return 0;
+}
+
+/***************************************************/
+
+/*
+ * warning warning: in some cases this packet may need compiler
+ * pragmas to force the compiler to not pad it. shouldn't with
+ * gcc though.
+ */
+
+#define OTALK_PACKET_SIZE 76
+
+#define OLD_NAME_SIZE 9
+struct otalk_packet {
+ char type;
+ char l_name[OLD_NAME_SIZE];
+ char r_name[OLD_NAME_SIZE];
+ char filler;
+ uint32_t id_num;
+ uint32_t pid;
+ char r_tty[TTY_SIZE];
+ struct talk_addr addr;
+ struct talk_addr ctl_addr;
+};
+
+struct otalk_reply {
+ char type;
+ char answer;
+ uint16_t filler;
+ uint32_t id_num;
+ struct talk_addr addr;
+};
+
+/* additional types */
+#define OLD_DELETE_INVITE 4
+#define OLD_AUTO_LOOK_UP 5
+#define OLD_AUTO_DELETE 6
+
+static int probe_otalk_packet(char *buf, size_t len, size_t maxlen,
+ struct sockaddr_in *sn)
+{
+ struct otalk_packet otp;
+ CTL_MSG m;
+
+ ktalk_debug("Probing for QUIRK_OTALK\n");
+
+ if (sizeof(otp)!=OTALK_PACKET_SIZE) {
+ syslog(LOG_ERR, "QUIRK_OTALK: struct otalk_packet padding "
+ "is wrong\n");
+ return -1;
+ }
+
+ if (len!=sizeof(otp)) {
+ ktalk_debug("QUIRK_OTALK: wrong size\n");
+ return -1;
+ }
+
+ memcpy(&otp, buf, len);
+ if (probe_string(otp.l_name, sizeof(otp.l_name))) {
+ ktalk_debug("QUIRK_OTALK: l_name not a string\n");
+ return -1;
+ }
+ if (probe_string(otp.r_name, sizeof(otp.r_name))) {
+ ktalk_debug("QUIRK_OTALK: r_name not a string\n");
+ return -1;
+ }
+ if (probe_string(otp.r_tty, sizeof(otp.r_tty))) {
+ ktalk_debug("QUIRK_OTALK: r_tty not a string\n");
+ return -1;
+ }
+ if (probe_addr(&otp.ctl_addr, sn)) {
+ ktalk_debug("QUIRK_OTALK: addresses do not match\n");
+ return -1;
+ }
+
+ switch (otp.type) {
+ case LEAVE_INVITE:
+ case LOOK_UP:
+ case DELETE:
+ case ANNOUNCE:
+ break;
+ /* I'm not sure these will work. */
+ case OLD_DELETE_INVITE: otp.type = DELETE; break;
+ case OLD_AUTO_LOOK_UP: otp.type = LOOK_UP; break;
+ case OLD_AUTO_DELETE: otp.type = DELETE; break;
+ default:
+ ktalk_debug("QUIRK_OTALK: invalid type field\n");
+ return -1;
+ }
+
+ if (OLD_NAME_SIZE >= NAME_SIZE) {
+ syslog(LOG_ERR, "QUIRK_OTALK: OLD_NAME_SIZE >= NAME_SIZE\n");
+ syslog(LOG_ERR, "QUIRK_OTALK: fix repairs.c and recompile\n");
+ return -1;
+ }
+ if (maxlen < sizeof(CTL_MSG)) {
+ syslog(LOG_ERR, "QUIRK_OTALK: maxlen too small; enlarge "
+ "inbuf[] in talkd.c and recompile\n");
+ return -1;
+ }
+
+ m.vers = TALK_VERSION;
+ m.type = otp.type;
+ m.answer = 0;
+ m.pad = 0;
+ m.id_num = otp.id_num;
+ m.addr = otp.addr;
+ m.ctl_addr = otp.ctl_addr;
+ m.pid = otp.pid;
+ memcpy(m.l_name, otp.l_name, OLD_NAME_SIZE);
+ m.l_name[OLD_NAME_SIZE] = 0;
+ memcpy(m.r_name, otp.r_name, OLD_NAME_SIZE);
+ m.r_name[OLD_NAME_SIZE] = 0;
+ memcpy(m.r_tty, otp.r_tty, TTY_SIZE);
+ m.r_tty[TTY_SIZE-1] = 0;
+ memcpy(buf, &m, sizeof(m));
+ return 0;
+}
+
+static size_t do_otalk_reply(char *buf, size_t maxlen) {
+ struct otalk_reply or;
+ CTL_RESPONSE *r = (CTL_RESPONSE *)buf;
+ if (sizeof(or) > maxlen) {
+ syslog(LOG_ERR, "QUIRK_OTALK: reply: maxlen too small; "
+ "enlarge buf[] in send_packet and recompile\n");
+ return sizeof(CTL_RESPONSE);
+ }
+
+ /*
+ * If we changed the type above, this might break. Should we encode
+ * it in the quirk code?
+ */
+ or.type = r->type;
+ or.answer = r->answer;
+ or.filler = 0;
+ or.id_num = r->id_num;
+ or.addr = r->addr;
+ memcpy(buf, &or, sizeof(or));
+ return sizeof(or);
+}
+
+/***************************************************/
+
+
+/*
+ * Return 0 if the packet's normal, -1 if we can't figure it out,
+ * otherwise a quirk code from the quirk list.
+ *
+ * For now, we don't support any quirks. Need more data.
+ */
+int
+rationalize_packet(char *buf, size_t len, size_t mlen, struct sockaddr_in *sn)
+{
+ if (len == sizeof(CTL_MSG)) {
+ return 0;
+ }
+
+ ktalk_debug("Malformed packet (length %u)\n", len);
+
+ if (probe_otalk_packet(buf, len, mlen, sn)==0) {
+ ktalk_debug("Using QUIRK_OTALK\n");
+ return QUIRK_OTALK;
+ }
+ return -1;
+}
+
+size_t
+irrationalize_reply(char *buf, size_t maxlen, int quirk)
+{
+ switch (quirk) {
+ case QUIRK_NONE: return sizeof(CTL_RESPONSE);
+ case QUIRK_OTALK: return do_otalk_reply(buf, maxlen);
+ }
+ /* ? */
+ return 0;
+}
diff --git a/ktalkd/ktalkd/table.cpp b/ktalkd/ktalkd/table.cpp
new file mode 100644
index 00000000..1a3713e3
--- /dev/null
+++ b/ktalkd/ktalkd/table.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/*
+ * Routines to handle insertion, deletion, etc on the table
+ * of requests kept by the daemon. Nothing fancy here, linear
+ * search on a double-linked list. A time is kept with each
+ * entry so that overly old invitations can be eliminated.
+ */
+
+#include "table.h"
+#include <sys/param.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include "proto.h"
+#include "defs.h"
+#include "machines/forwmach.h"
+
+#define MAX_ID 16000 /* << 2^15 so I don't have sign troubles */
+
+#define NIL ((TABLE_ENTRY *)0)
+
+/*
+ * Look in the table for an invitation that matches the current
+ * request looking for an invitation
+ */
+NEW_CTL_MSG * KTalkdTable::find_match(NEW_CTL_MSG *request)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ (void) gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ ktalk_debug("deleting expired entry : id %d",
+ ptr->request.id_num);
+ delete_entry(ptr);
+ continue;
+ }
+ if ((strcmp(request->l_name, ptr->request.r_name) == 0) &&
+ (strcmp(request->r_name, ptr->request.l_name) == 0) &&
+ (ptr->request.type == LEAVE_INVITE) && (ptr->fwm == 0L))
+ { /* Not the forw. machines : they aren't stored to match LOOK_UPs */
+ ktalk_debug("Found match : id %d", ptr->request.id_num);
+ return (&ptr->request);
+ }
+ }
+ return ((NEW_CTL_MSG *)0);
+}
+
+/*
+ * Look for an identical request, as opposed to a complimentary
+ * one as find_match does
+ */
+NEW_CTL_MSG * KTalkdTable::find_request(NEW_CTL_MSG *request)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ (void) gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ /*
+ * See if this is a repeated message, and check for
+ * out of date entries in the table while we are it.
+ */
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if ((ptr->time - current_time) > MAX_LIFE) {
+ /* the entry is too old */
+ ktalk_debug("deleting expired entry : id %d",
+ ptr->request.id_num);
+ delete_entry(ptr);
+ continue;
+ }
+ if (strcmp(request->r_name, ptr->request.r_name) == 0 &&
+ strcmp(request->l_name, ptr->request.l_name) == 0 &&
+ request->type == ptr->request.type &&
+ request->pid == ptr->request.pid) {
+ /* update the time if we 'touch' it */
+ ptr->time = current_time;
+ ktalk_debug("Found identical request : id %d", ptr->request.id_num);
+ return (&ptr->request);
+ }
+ }
+ return ((NEW_CTL_MSG *)0);
+}
+
+void KTalkdTable::insert_table(NEW_CTL_MSG *request, NEW_CTL_RESPONSE *response, ForwMachine * fwm)
+{
+ register TABLE_ENTRY *ptr;
+ time_t current_time;
+
+ gettimeofday(&tp, &txp);
+ current_time = tp.tv_sec;
+ request->id_num = new_id();
+ ktalk_debug("Stored as id %d",request->id_num);
+ if (response != 0L) response->id_num = htonl(request->id_num);
+ /* insert a new entry into the top of the list */
+ ptr = new TABLE_ENTRY;
+ if (ptr == NIL) {
+ syslog(LOG_ERR, "insert_table: Out of memory");
+ _exit(1);
+ }
+ ptr->fwm = fwm;
+ ptr->time = current_time;
+ ptr->request = *request;
+ ptr->next = table;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr;
+ ptr->last = NIL;
+ table = ptr;
+}
+
+/*
+ * Generate a unique non-zero sequence number
+ */
+int KTalkdTable::new_id()
+{
+ static int current_id = 0;
+
+ current_id = (current_id + 1) % MAX_ID;
+ /* 0 is reserved, helps to pick up bugs */
+ if (current_id == 0)
+ current_id = 1;
+ return (current_id);
+}
+
+/*
+ * Delete the invitation with id 'id_num'
+ */
+int KTalkdTable::delete_invite(uint32_t id_num)
+{
+ register TABLE_ENTRY *ptr;
+
+ ptr = table;
+ ktalk_debug("delete_invite(%d)", id_num);
+ for (ptr = table; ptr != NIL; ptr = ptr->next) {
+ if (ptr->request.id_num == id_num)
+ break;
+ }
+ if (ptr != NIL) {
+ if (ptr->fwm) {
+ ptr->fwm->sendDelete(); // Calls processDelete() in the child process.
+ }
+ ktalk_debug("Deleted : id %d", ptr->request.id_num);
+ delete_entry(ptr);
+ return (SUCCESS);
+ }
+ return (NOT_HERE);
+}
+
+/*
+ * Classic delete from a double-linked list
+ */
+void KTalkdTable::delete_entry(register TABLE_ENTRY *ptr)
+{
+ if (table == ptr)
+ table = ptr->next;
+ else if (ptr->last != NIL)
+ ptr->last->next = ptr->next;
+ if (ptr->next != NIL)
+ ptr->next->last = ptr->last;
+ delete ptr;
+}
+
+KTalkdTable::~KTalkdTable()
+{
+ register TABLE_ENTRY *ptr;
+ ptr = table;
+ ktalk_debug("final_clean()");
+ for (ptr = table; ptr != 0L; ptr = ptr->next) {
+ if (ptr->fwm != 0L)
+ {
+ ktalk_debug("CLEAN : Found a forwarding machine to clean : id %d",ptr->request.id_num);
+ delete ptr->fwm;
+ }
+ ktalk_debug("CLEAN : Deleting id %d", ptr->request.id_num);
+ delete_entry(ptr);
+ }
+}
diff --git a/ktalkd/ktalkd/table.h b/ktalkd/ktalkd/table.h
new file mode 100644
index 00000000..edeba232
--- /dev/null
+++ b/ktalkd/ktalkd/table.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1997 David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ *
+ */
+#ifndef TABLE_H
+#define TABLE_H
+
+#include "includ.h"
+#include <sys/time.h>
+#include <time.h>
+
+class ForwMachine;
+
+typedef struct table_entry TABLE_ENTRY;
+
+struct table_entry {
+ NEW_CTL_MSG request;
+ long time;
+ ForwMachine * fwm;
+ TABLE_ENTRY *next;
+ TABLE_ENTRY *last;
+};
+
+class KTalkdTable
+{
+ public:
+ KTalkdTable() : table (0L) {};
+ ~KTalkdTable();
+ void insert_table(NEW_CTL_MSG *request, NEW_CTL_RESPONSE *response, ForwMachine * fwm);
+ int delete_invite(uint32_t id_num);
+ NEW_CTL_MSG * find_match(NEW_CTL_MSG *request);
+ NEW_CTL_MSG * find_request(NEW_CTL_MSG *request);
+ int new_id();
+ TABLE_ENTRY * getTable() { return table; }
+
+ private:
+ void delete_entry(register TABLE_ENTRY *ptr);
+
+ struct timeval tp;
+ struct timezone txp;
+
+ TABLE_ENTRY *table;
+};
+#endif
diff --git a/ktalkd/ktalkd/talkd.conf b/ktalkd/ktalkd/talkd.conf
new file mode 100644
index 00000000..17deef07
--- /dev/null
+++ b/ktalkd/ktalkd/talkd.conf
@@ -0,0 +1,82 @@
+# /etc/talkd.conf
+#
+# Administrator config file for ktalkd, the talk daemon by David Faure
+#
+# Wait for actual ktalkd to terminate before changes are taken into account.
+#
+# Boolean values can be 0/1, off/on, false/true
+# Syntax : "key: value" (no space between key and ':')
+# If a key is absent, the value set at compile time will be set.
+
+###### SECTION 1 : Administrator settings
+
+# Mail sender (can be mail.local, sendmail(preferred), qmail(as good:)) )
+# mail.local is included in the dist., just in case you don't have sendmail
+MailProg: mail.local
+
+# What should I do if somebody tries to talk to a non-existent user ?
+# 0 : Launch answer machine saying 'non-existent user...'
+# and 'I' receive the message (to know that it happened)
+# 1 : 'I' take the talk. (The names of caller & callee appear to 'I')
+# 2 : Do nothing. ('Not logged' will appear to caller).
+NEUBehaviour: 2
+
+# (Multi-user secured host : set Behaviour: 2).
+# (Multi-user host : set Behaviour: 0 and User: root or postmaster)
+# (Almost single-user networked PC : set Behaviour: 1 and User: your_user_name)
+
+# If you choose 0, then you can set the following
+# (no internationalization possible : this file is manually read by ktalkd)
+NEUBanner1: The person you're asking to talk with is unknown at this host.
+NEUBanner2: You may have mistyped the name, or network address. Try again
+NEUBanner3: or leave a message which will be sent to the system administrator.
+
+# Who is 'I' ? (=> who should take the talk / receive the message)
+NEUUser:
+
+# If NEUBehaviour is 1 ('I' take the talk), which forward method to use ?
+# Choose FWT if your machine is on two separate networks, and if you (NEUUser)
+# plan to set a forward. FWR is ok (and lighter) in all other cases.
+NEUForwardMethod: FWR
+
+
+##### SECTION 2 : Default values that users can overwrite
+
+# Set to 1 to activate answering machine
+# Warning : if set to 0, no user will be able to activate it.
+# (The value won't overwrite this one).
+Answmach: 1
+
+# Set this to 1 to enable X-aware announcement.
+# (Currently impossible without KDE)
+XAnnounce: 0
+
+# Set this to 'off' if all you want is a beep to notify you of talk
+# requests, to 'on' if you want to play an audio file instead.
+Sound: off
+
+# Define this to the full path of the sound file you wish to
+# have played when you receive talk requests. It may be of
+# any format, as long as the player defined below is capable
+# of playing it.
+SoundFile: /usr/lib/talkd/talk.wav
+
+# Set this to the command you will be using to play audio
+# files. This can be any .wav, .au, .snd or whatever player,
+# just so long as it will play the format that your chosen
+# audio file is in.
+SoundPlayer: /usr/local/bin/wavplay
+SoundPlayerOpt: -q
+# ==> SoundPlayer + SoundPlayerOpt = /usr/local/bin/wavplay -q
+
+# Contents of the talk request, 3 lines.
+# First one must include %d:%02d for the time of the request
+# Second one must include a '%s' for the caller name and machine
+# Third one must include %s, for caller address
+Announce1: Message from Talk_Daemon at %d:%02d ...
+Announce2: talk: connection requested by %s.
+Announce3: talk: respond with: talk %s
+
+# Time in seconds between "Ringing your party again" and launching answering
+# machine (not very important) (can't be overridden by users)
+Time: 10
diff --git a/ktalkd/ktalkd/talkd.cpp b/ktalkd/ktalkd/talkd.cpp
new file mode 100644
index 00000000..0f2f27a0
--- /dev/null
+++ b/ktalkd/ktalkd/talkd.cpp
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+char copyright[] =
+ "@(#) Copyright (c) 1983 Regents of the University of California.\n"
+ "All rights reserved.\n";
+
+/*
+ * From: @(#)talkd.c 5.8 (Berkeley) 2/26/91
+ */
+
+#include "includ.h"
+
+/*
+ * talkd - internet talk daemon
+ * loops waiting for and processing requests until idle for a while,
+ * then exits.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <signal.h>
+#include <syslog.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+/*#include <stdio.h>*/
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+
+#include <sys/utsname.h>
+
+#include "proto.h"
+#include "process.h"
+#include "readconf.h"
+#include "defs.h"
+#include "threads.h"
+#include "table.h"
+#include "machines/answmach.h"
+#include "machines/talkconn.h"
+
+#include <kinstance.h>
+KTalkdTable * ktable;
+
+#define TIMEOUT 20
+ /* TIMEOUT was 30, but has been reduced to remove the
+ zombie process (answering machine) as soon as possible */
+#define MAXIDLE 120
+ /* #define MAXIDLE 30 for debugging purposes */
+
+#define NB_MAX_CHILD 20 /* Max number of answering / forwarding machines at a time */
+
+#if !defined(MAXHOSTNAMELEN)
+#define MAXHOSTNAMELEN 64
+#endif
+/*char ourhostname[MAXHOSTNAMELEN];*/
+
+static time_t lastmsgtime;
+
+static void
+timeout(int ignore)
+{
+ (void)ignore;
+
+ int ok_exit = ack_process();
+ if (ok_exit && (time(0) - lastmsgtime >= MAXIDLE)) {
+ delete ktable;
+ _exit(0);
+ }
+
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+}
+
+/*
+ * Returns true if the address belongs to the local host. If it's
+ * not a loopback address, try binding to it.
+ */
+static int
+is_local_address(uint32_t addr)
+{
+ struct sockaddr_in sn;
+ int sock, ret;
+ if (addr == htonl(INADDR_LOOPBACK)) {
+ return 1;
+ }
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock<0) {
+ syslog(LOG_WARNING, "socket: %s", strerror(errno));
+ return 0;
+ }
+ memset(&sn, 0, sizeof(sn));
+ sn.sin_family = AF_INET;
+ sn.sin_port = htons(0);
+ sn.sin_addr.s_addr = addr;
+ ret = bind(sock, (struct sockaddr *)&sn, sizeof(sn));
+ close(sock);
+ return ret==0;
+}
+
+static void
+send_packet(CTL_RESPONSE *response, struct sockaddr_in *sn, int quirk)
+{
+ char buf[2*sizeof(CTL_RESPONSE)];
+ size_t sz = sizeof(CTL_RESPONSE);
+ int cc, err=0;
+
+ memcpy(buf, response, sz);
+ if (quirk) {
+ sz = irrationalize_reply(buf, sizeof(buf), quirk);
+ }
+ while (sz > 0) {
+ cc = sendto(1, buf, sz, 0, (struct sockaddr *)sn, sizeof(*sn));
+ if (cc<0) {
+ syslog(LOG_WARNING, "sendto: %s", strerror(errno));
+ if (err) return;
+ err = 1;
+ }
+ else sz -= cc;
+ }
+}
+
+/*
+ * Issue an error packet. Should not assume anything other than the
+ * header part (the uint8_t's) of mp is valid, and assume as little
+ * as possible about that, since it might have been a packet we
+ * couldn't dequirk.
+ */
+static void
+send_reject_packet(CTL_MSG *mp, struct sockaddr_in *sn, int code, int quirk)
+{
+ CTL_RESPONSE rsp;
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.vers = TALK_VERSION;
+ rsp.type = mp->type;
+ rsp.answer = code;
+ send_packet(&rsp, sn, quirk);
+}
+
+static void
+do_one_packet(void)
+{
+ char inbuf[2*sizeof(CTL_MSG)];
+ int quirk = 0;
+ CTL_RESPONSE response;
+ CTL_MSG *mp;
+ char theirhost[MAXHOSTNAMELEN];
+ const char *theirip;
+
+ struct hostent *hp;
+ struct sockaddr_in sn;
+ int cc, i, ok;
+ socklen_t addrlen;
+ int ret_value = PROC_REQ_OK; /* return value from process_request */
+
+ addrlen = sizeof(sn);
+ cc = recvfrom(0, inbuf, sizeof(inbuf), 0,
+ (struct sockaddr *)&sn, &addrlen);
+ if (cc<0) {
+ if (errno==EINTR || errno==EAGAIN) {
+ return;
+ }
+ syslog(LOG_WARNING, "recvfrom: %s", strerror(errno));
+ return;
+ }
+
+ /*
+ * This should be set on any input, even trash, because even
+ * trash input will cause us to be restarted if we exit.
+ */
+ lastmsgtime = time(NULL);
+
+ if (addrlen!=sizeof(sn)) {
+ syslog(LOG_WARNING, "recvfrom: bogus address length");
+ return;
+ }
+ if (sn.sin_family!=AF_INET) {
+ syslog(LOG_WARNING, "recvfrom: bogus address family");
+ return;
+ }
+
+ /*
+ * If we get here we have an address we can reply to, although
+ * it may not be good for much. If possible, reply to it, because
+ * if we just drop the packet the remote talk client will keep
+ * throwing junk at us.
+ */
+ theirip = inet_ntoa(sn.sin_addr);
+ mp = (CTL_MSG *)inbuf;
+
+ /*
+ * Check they're not being weenies.
+ * We should look into using libwrap here so hosts.deny works.
+ * Wrapping talkd with tcpd isn't very useful.
+ */
+ hp = gethostbyaddr((char *)&sn.sin_addr, sizeof(struct in_addr),
+ AF_INET);
+ if (hp == NULL) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+ strncpy(theirhost, hp->h_name, sizeof(theirhost));
+ theirhost[sizeof(theirhost)-1] = 0;
+
+ hp = gethostbyname(theirhost);
+ if (hp == NULL) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+
+ for (i=ok=0; hp->h_addr_list[i] && !ok; i++) {
+ if (!memcmp(hp->h_addr_list[i], &sn.sin_addr,
+ sizeof(sn.sin_addr))) ok = 1;
+ }
+ if (!ok) {
+ syslog(LOG_WARNING, "%s: bad dns", theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, 0);
+ return;
+ }
+
+ /*
+ * Try to straighten out bad packets.
+ */
+ quirk = rationalize_packet(inbuf, cc, sizeof(inbuf), &sn);
+ if (quirk<0) {
+ print_broken_packet(inbuf, cc, &sn);
+ syslog(LOG_WARNING, "%s (%s): unintelligible packet",
+ theirhost, theirip);
+ send_reject_packet(mp, &sn, UNKNOWN_REQUEST, 0);
+ return;
+ }
+
+ /*
+ * Make sure we know what we're getting into.
+ */
+ if (mp->vers!=TALK_VERSION) {
+ syslog(LOG_WARNING, "%s (%s): bad protocol version %d",
+ theirhost, theirip, mp->vers);
+ send_reject_packet(mp, &sn, BADVERSION, 0);
+ return;
+ }
+
+ /*
+ * LEAVE_INVITE messages should only come from localhost.
+ * Of course, old talk clients send from our hostname's IP
+ * rather than localhost, complicating the issue...
+ */
+ if (mp->type==LEAVE_INVITE && !is_local_address(sn.sin_addr.s_addr)) {
+ syslog(LOG_WARNING, "%s (%s) sent invite packet",
+ theirhost, theirip);
+ send_reject_packet(mp, &sn, MACHINE_UNKNOWN, quirk);
+ return;
+ }
+
+ /*
+ * Junk the reply address they reported for themselves. Write
+ * the real one over it because announce.c gets it from there.
+ */
+ mp->ctl_addr.ta_family = AF_INET;
+ mp->ctl_addr.ta_port = sn.sin_port;
+ mp->ctl_addr.ta_addr = sn.sin_addr.s_addr;
+
+ /*
+ * Since invite messages only come from localhost, and nothing
+ * but invite messages use the TCP address, force it to be our
+ * address.
+ *
+ * Actually, if it's a local address, leave it alone. talk has
+ * to play games to figure out the right interface address to
+ * use, and we don't want to get into that - they can work it
+ * out, but we can't since we don't know who they're trying to
+ * talk to.
+ *
+ * If it's not a local address, someone's trying to play games
+ * with us. Rather than trying to pick a local address to use,
+ * reject the packet.
+ */
+ if (mp->type==LEAVE_INVITE) {
+ mp->addr.ta_family = AF_INET;
+ if (!is_local_address(mp->addr.ta_addr)) {
+ syslog(LOG_WARNING,
+ "invite packet had bad return address");
+ send_reject_packet(mp, &sn, BADADDR, quirk);
+ return;
+ }
+ }
+ else {
+ /* non-invite packets don't use this field */
+ memset(&mp->addr, 0, sizeof(mp->addr));
+ }
+
+ ret_value = process_request(mp, &response, theirhost);
+
+ if (ret_value != PROC_REQ_FORWMACH)
+ {
+ /* can block here, is this what I want? */
+ send_packet(&response, &sn, quirk);
+ }
+
+ if (Options.answmach && (ret_value>=PROC_REQ_MIN_A))
+ {
+ ktalk_debug("Launch answer machine, mode %d.", ret_value);
+ AnswMachine::launchAnswMach(*mp, ret_value);
+ new_process();
+ }
+}
+
+/* the following copies defaults values to 'Options.*' variables so that
+ * configuration file can overwrite them */
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sn;
+ socklen_t sz = sizeof(sn);
+ int do_debug=0, do_badpackets=0, ch;
+
+ new KInstance("ktalkd"); // for KConfig and friends
+ ktable = new KTalkdTable();
+
+ /* make sure we're a daemon */
+ if (getsockname(0, (struct sockaddr *)&sn, &sz)) {
+ const char *msg = strerror(errno);
+ write(2, msg, strlen(msg));
+ exit(1);
+ }
+
+ openlog(*argv, LOG_PID, LOG_DAEMON);
+
+ /* get local machine name */
+ // using 'uname' instead of 'gethostname', as suggested by Stephan Kulow
+ struct utsname buf;
+ if (uname (&buf) == -1) {
+ syslog (LOG_ERR, "Unable to get name of local host : %s", strerror(errno));
+ exit(1);
+ }
+ strcpy(Options.hostname, buf.nodename);
+
+ /* if (gethostname(Options.hostname, sizeof (Options.hostname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ exit(1);
+ }*/
+ if (chdir(_PATH_DEV) < 0) {
+ syslog(LOG_ERR, "chdir: %s: %s", _PATH_DEV, strerror(errno));
+ exit(1);
+ }
+ while ((ch = getopt(argc, argv, "dp"))!=-1) {
+ switch (ch) {
+ case 'd':
+ Options.debug_mode = 1;
+ do_debug=1; break;
+ case 'p': do_badpackets=1; break;
+ }
+ }
+ set_debug(do_debug, do_badpackets);
+
+ TalkConnection::init(); /* global initialization */
+ process_config_file(); /* read configuration */
+
+ signal(SIGALRM, timeout);
+ alarm(TIMEOUT);
+ for (;;) {
+ do_one_packet();
+ }
+/* return 0; <--- unreachable because of the above loop */
+}
diff --git a/ktalkd/ktalkd/threads.cpp b/ktalkd/ktalkd/threads.cpp
new file mode 100644
index 00000000..09d5bb4c
--- /dev/null
+++ b/ktalkd/ktalkd/threads.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include "includ.h"
+#include "proto.h"
+
+static int nb_child_to_wait=0;
+
+#undef DEBUG_THREADS
+
+/** Register a new process (by just increasing the number of children */
+void new_process()
+{
+ nb_child_to_wait++;
+#ifdef DEBUG_THREADS
+ debug("New Child. Nb child to wait : %d",nb_child_to_wait);
+#endif
+}
+
+/** Wait for a given process - not registered - and see if others exited */
+int wait_process(int pid)
+{
+ int val, status;
+ do {
+ val = wait(&status);
+ if (val == -1) {
+ if (errno == EINTR)
+ continue;
+ /* shouldn't happen */
+ syslog(LOG_WARNING, "announce: wait: %s", strerror(errno));
+ return 1;
+ }
+ if (val != pid) {
+ nb_child_to_wait--;
+#ifdef DEBUG_THREADS
+ debug("Child exited. Nb child to wait : %d",nb_child_to_wait);
+#endif
+ }
+ } while (val != pid);
+ return status;
+}
+
+/** Wait for children (if any) to exit. Return 1 if no more child to wait */
+int ack_process()
+{
+ if (nb_child_to_wait>0)
+ {
+ int pid;
+ do {
+ pid = waitpid(-1,0,WNOHANG);
+ if (pid==-1)
+ syslog(LOG_ERR,"Timeout. Error waiting for child.");
+ if ((pid!=0) & (nb_child_to_wait>0)) {
+ nb_child_to_wait--;
+#ifdef DEBUG_THREADS
+ debug("Child exited as expected. Nb child to wait : %d",nb_child_to_wait);
+#endif
+ }
+ } while ((pid>0) && (nb_child_to_wait>0));
+ return 0;
+ } else return 1; /* ok for exiting */
+}
+
diff --git a/ktalkd/ktalkd/threads.h b/ktalkd/ktalkd/threads.h
new file mode 100644
index 00000000..5d76e54e
--- /dev/null
+++ b/ktalkd/ktalkd/threads.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1998 David Faure.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+/** Register a new process (by just increasing the number of children */
+void new_process();
+/** Wait for a given process - not registered - and see if others exited */
+int wait_process(int pid);
+/** Wait for children (if any) to exit. Return 1 if no more child to wait */
+int ack_process();
diff --git a/ktalkd/ktalkd/unixsock.cpp b/ktalkd/ktalkd/unixsock.cpp
new file mode 100644
index 00000000..34272405
--- /dev/null
+++ b/ktalkd/ktalkd/unixsock.cpp
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1998 Burkhard Lehner and David Faure
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * (BSD License, from kdelibs/doc/common/bsd-license.html)
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <time.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <qstrlist.h>
+#include <qfile.h>
+
+#include "includ.h"
+#include "proto.h"
+#include "announce.h" // for N_CHARS
+
+bool sendToKtalk (const char *username, const char *announce)
+/*
+ sends an announcement to all running ktalk clients.
+ username: name of the user who shall receive the announce
+ announce: name and IP address of the one who requests the talk
+
+ return value: TRUE if at least one ktalk was found and running
+ FALSE otherwise
+*/
+
+{
+ // WABA: Disabled for reasons outlined below by XXX.
+ return FALSE;
+#if 0
+ // Create a socket
+ int sock;
+ if ((sock = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0) return FALSE;
+ // Bind it to a temporary file in /tmp
+ struct sockaddr_un tempAddr;
+ tempAddr.sun_family = AF_UNIX;
+ if (tmpnam (tempAddr.sun_path) == 0 ||
+ bind (sock, (struct sockaddr *) &tempAddr, sizeof (tempAddr)) == -1) {
+ close (sock);
+ debug("Couldn't create temporary socket!");
+ return FALSE;
+ }
+
+ // find sockets of running ktalk clients
+ QString tempDir = "/tmp";
+ QString templ = QString ("ktalk-") + username + "-";
+ bool announceok = FALSE;
+ char buffer [N_CHARS+2];
+ buffer [0] = 1;
+ strcpy (buffer + 1, announce); // announce is at most N_CHARS long.
+ unsigned int announcelen = strlen(buffer);
+ unsigned int len;
+
+ DIR *dir = opendir (QFile::encodeName(tempDir));
+ struct dirent *entry;
+ QStrList dirList;
+ struct sockaddr_un ktalkAddr;
+ ktalkAddr.sun_family = AF_UNIX;
+ while ((entry = readdir (dir))) {
+ // send announce to each of them
+
+ // XXX: What happens if user okir does this:
+ // cd /tmp
+ // ln kfm_foo_bar_baz ktalk-joedoe-blablabla
+ // when I now try to talk to joedoe, the name@host string
+ // is sent to the kfm socket (which is not owned
+ // by me but someone else!)
+ //
+ // Besides, this approach allows me to snoop on the
+ // talk requests other users receive; if I want to find
+ // out who's talking to janet, all I need is to write
+ // a small app that listens on /tmp/ktalk-janet-foo
+ //
+ if (templ == QFile::decodeName(entry->d_name).left(templ.length())) {
+ QString path = tempDir + "/" + QFile::decodeName(entry->d_name);
+ strncpy (ktalkAddr.sun_path, QFile::encodeName(path), sizeof (ktalkAddr.sun_path));
+ len = sendto (sock, buffer, announcelen, 0,
+ (struct sockaddr *) &ktalkAddr, sizeof (ktalkAddr));
+ if (len == announcelen)
+ announceok = TRUE;
+ }
+ }
+ closedir (dir);
+ if (!announceok) {
+ close (sock);
+ unlink (tempAddr.sun_path);
+ return FALSE;
+ }
+ // at least one accepted the packet, wait for response :
+ bool result = FALSE;
+ fd_set readFDs;
+ FD_ZERO (&readFDs);
+ FD_SET (sock, &readFDs);
+ char answer;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; // Wait for answer at most 0.5 seconds
+ if ( (select (sock + 1, &readFDs, 0, 0, &timeout) > 0) &&
+ (recv (sock, &answer, 1, 0) == 1) ) {
+ result = ( answer == 42 ); // Answer from ktalk has to be 42.
+ }
+ close (sock);
+ unlink (tempAddr.sun_path);
+ debug("Announce to ktalk : result = %d",result);
+ return result;
+#endif
+}
diff --git a/ktalkd/ktalkd/unixsock.h b/ktalkd/ktalkd/unixsock.h
new file mode 100644
index 00000000..719c667a
--- /dev/null
+++ b/ktalkd/ktalkd/unixsock.h
@@ -0,0 +1,6 @@
+#ifndef UNIXSOCK_H
+#define UNIXSOCK_H
+
+bool sendToKtalk (const char *username, const char *announce);
+
+#endif
diff --git a/ktalkd/ktalkdlg/Makefile.am b/ktalkd/ktalkdlg/Makefile.am
new file mode 100644
index 00000000..d6d9104c
--- /dev/null
+++ b/ktalkd/ktalkdlg/Makefile.am
@@ -0,0 +1,15 @@
+## -*- makefile -*-
+# Ktalkdlg - Makefile.am
+
+EXTRA_DIST =
+
+bin_PROGRAMS = ktalkdlg
+ktalkdlg_SOURCES = ktalkdlg.cpp
+
+INCLUDES = $(all_includes)
+ktalkdlg_LDFLAGS= $(all_libraries) $(KDE_RPATH)
+ktalkdlg_LDADD = $(LIB_KDEUI)
+
+#for extra warnings during compilation :
+#AM_CXXFLAGS = -ansi -pedantic -D_POSIX_SOURCE -D_BSD_SOURCE
+
diff --git a/ktalkd/ktalkdlg/ktalkdlg.cpp b/ktalkd/ktalkdlg/ktalkdlg.cpp
new file mode 100644
index 00000000..f347330e
--- /dev/null
+++ b/ktalkd/ktalkdlg/ktalkdlg.cpp
@@ -0,0 +1,168 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <config.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <qmessagebox.h>
+#include <qfile.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kcmdlineargs.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+
+#include <kaudioplayer.h>
+#include <klocale.h>
+
+#define RING_WAIT 30
+#define MAX_DLG_LIFE RING_WAIT
+/* so that the dialog lasts exactly the time of an announce */
+
+class TimeoutDialog : public QMessageBox {
+ public:
+ TimeoutDialog (int timeout_ms,
+ const QString& caption, const QString &text, Icon icon,
+ int button0, int button1, int button2,
+ QWidget *parent=0, const char *name=0, bool modal=TRUE,
+ WFlags f=WStyle_DialogBorder ):
+ QMessageBox (caption, text, icon, button0, button1, button2,
+ parent, name, modal, f)
+ {startTimer (timeout_ms);}
+
+ ~TimeoutDialog ()
+ {killTimers ();}
+
+ virtual void timerEvent (QTimerEvent *)
+ {killTimers (); done (Rejected);}
+};
+
+static KCmdLineOptions option[] =
+{
+ { "+user@host", I18N_NOOP("Caller identification"), 0 },
+ { "+[callee]", I18N_NOOP("Name of the callee, if he doesn't exist on this system (we're taking his call)"), 0 },
+ KCmdLineLastOption
+};
+
+static const char description[] =
+ I18N_NOOP("Dialog box for incoming talk requests");
+
+static const char version[] = "v1.5.2";
+
+int main (int argc, char **argv)
+{
+ KCmdLineArgs::init(argc, argv, "ktalkdlg", description, version );
+ KCmdLineArgs::addCmdLineOptions( option );
+ KLocale::setMainCatalogue( "kcmktalkd" );
+ KApplication a;
+
+ struct timeval clock;
+ struct timezone zone;
+ gettimeofday (&clock, &zone);
+ struct tm *localclock = localtime ((const time_t *) &clock.tv_sec);
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if (args->count() == 0)
+ KCmdLineArgs::usage(i18n("'user@host' expected."));
+
+ QString s;
+ s.sprintf ("%d:%02d", localclock->tm_hour, localclock->tm_min);
+ s = i18n ("Message from talk demon at ") + s + " ...\n" +
+ i18n ("Talk connection requested by ") + args->arg(0);
+
+ if ( args->count() == 2 )
+ {
+ s += '\n';
+ QString callee = args->arg(1);
+ s += i18n ("for user %1").arg( callee.isEmpty() ? i18n("<nobody>") : callee );
+ }
+
+ s += ".";
+
+ TimeoutDialog dialog (MAX_DLG_LIFE * 1000,
+ i18n ("Talk requested..."), s,
+ QMessageBox::Information,
+ QMessageBox::Yes | QMessageBox::Default,
+ QMessageBox::No | QMessageBox::Escape,
+ 0 );
+ dialog.setButtonText( QMessageBox::Yes, i18n ("Respond") );
+ dialog.setButtonText( QMessageBox::No, i18n ("Ignore") );
+
+ a.setTopWidget (&dialog);
+
+ // don't erase this! - ktalkd waits for it!
+ printf("#\n");
+ fflush(stdout);
+
+ KConfig *cfg = new KConfig ( "ktalkannouncerc" );
+ cfg->setGroup ("ktalkannounce");
+ bool bSound = cfg->readNumEntry ("Sound", 0);
+
+ if (bSound) {
+ QString soundFile = cfg->readPathEntry ("SoundFile");
+ if (soundFile[0] != '/')
+ soundFile = locate( "sound", soundFile );
+
+ if (!soundFile.isEmpty ()) {
+ KAudioPlayer::play (soundFile);
+ }
+ }
+ //if (!audio) a.beep (); // If no audio is played (whatever reason), beep!
+
+ int result = dialog.exec ();
+ if (result == QMessageBox::Yes) {
+ dialog.killTimers ();
+ kdDebug() << "Running talk client..." << endl;
+
+ QString konsole = locate("exe", "konsole");
+ QString konsole_dir = konsole;
+ konsole_dir.truncate( konsole.findRev('/') );
+ setenv("KDEBINDIR", QFile::encodeName(konsole_dir).data(), 0/*don't overwrite*/);
+ QString cmd0 = cfg->readPathEntry ("talkprg", konsole + " -e talk");
+
+ QString cmd = cmd0.stripWhiteSpace();
+ cmd += " '";
+ cmd += args->arg(0);
+ cmd += "' &";
+
+ kdDebug() << cmd << endl;
+
+ // Open /dev/null for stdin, stdout and stderr:
+ int fd=open("/dev/null", O_RDWR);
+ for (int i = 0; i <= 2; i++) {
+ dup2(fd, i);
+ }
+
+ /* XXX: The sender's name or hostname may contain `rm -rf .`
+ * That's why it's bad to use system()
+ */
+ system (QFile::encodeName(cmd));
+ kapp->quit();
+ }
+
+ return 0;
+}
diff --git a/ktalkd/mail.local/Makefile.am b/ktalkd/mail.local/Makefile.am
new file mode 100644
index 00000000..15a44399
--- /dev/null
+++ b/ktalkd/mail.local/Makefile.am
@@ -0,0 +1,8 @@
+## -*- makefile -*-
+# Ktalkd - mail.local/Makefile.am
+
+LDADD = $(LIBSOCKET)
+bin_PROGRAMS = mail.local
+mail_local_SOURCES = mail.local.c
+noinst_HEADERS = pathnames.h
+
diff --git a/ktalkd/mail.local/README.mail.local b/ktalkd/mail.local/README.mail.local
new file mode 100644
index 00000000..57e932bc
--- /dev/null
+++ b/ktalkd/mail.local/README.mail.local
@@ -0,0 +1,12 @@
+A note about mail.local :
+
+This program is used by the answering machine to let the callee a mail.
+
+It is supplied in case you don't have sendmail nor qmail.
+It is used by the default system-wide configuration file, so that ktalkd works
+for anybody.
+
+If this program doesn't compile, or if you don't want to install it,
+or also if you want to receive the mail on another machine,
+just edit the config file (ktalkdrc if KDE installed, otherwise talkd.conf)
+so that MailProg is set to either sendmail or qmail (in sendmail mode).
diff --git a/ktalkd/mail.local/mail.local.c b/ktalkd/mail.local/mail.local.c
new file mode 100644
index 00000000..9c90d272
--- /dev/null
+++ b/ktalkd/mail.local/mail.local.c
@@ -0,0 +1,983 @@
+/*
+ *
+ SENDMAIL LICENSE
+
+The following license terms and conditions apply, unless a different
+license is obtained from Sendmail, Inc., 6425 Christie Ave, Fourth Floor,
+Emeryville, CA 94608, or by electronic mail at license@sendmail.com.
+
+License Terms:
+
+Use, Modification and Redistribution (including distribution of any
+modified or derived work) in source and binary forms is permitted only if
+each of the following conditions is met:
+
+1. Redistributions qualify as "freeware" or "Open Source Software" under
+ one of the following terms:
+
+ (a) Redistributions are made at no charge beyond the reasonable cost of
+ materials and delivery.
+
+ (b) Redistributions are accompanied by a copy of the Source Code or by an
+ irrevocable offer to provide a copy of the Source Code for up to three
+ years at the cost of materials and delivery. Such redistributions
+ must allow further use, modification, and redistribution of the Source
+ Code under substantially the same terms as this license. For the
+ purposes of redistribution "Source Code" means the complete compilable
+ and linkable source code of sendmail including all modifications.
+
+2. Redistributions of source code must retain the copyright notices as they
+ appear in each source code file, these license terms, and the
+ disclaimer/limitation of liability set forth as paragraph 6 below.
+
+3. Redistributions in binary form must reproduce the Copyright Notice,
+ these license terms, and the disclaimer/limitation of liability set
+ forth as paragraph 6 below, in the documentation and/or other materials
+ provided with the distribution. For the purposes of binary distribution
+ the "Copyright Notice" refers to the following language:
+ "Copyright (c) 1998-2002 Sendmail, Inc. All rights reserved."
+
+4. Neither the name of Sendmail, Inc. nor the University of California nor
+ the names of their contributors may be used to endorse or promote
+ products derived from this software without specific prior written
+ permission. The name "sendmail" is a trademark of Sendmail, Inc.
+
+5. All redistributions must comply with the conditions imposed by the
+ University of California on certain embedded code, whose copyright
+ notice and conditions for redistribution are as follows:
+
+ (a) Copyright (c) 1988, 1993 The Regents of the University of
+ California. All rights reserved.
+
+ (b) Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ (i) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ (ii) Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ (iii) Neither the name of the University nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+6. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY
+ SENDMAIL, INC. AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ NO EVENT SHALL SENDMAIL, INC., THE REGENTS OF THE UNIVERSITY OF
+ CALIFORNIA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+ */
+
+#if defined(LIBM_SCCS) && !defined(lint)
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif
+
+/*
+ * This is not intended to compile on System V derived systems
+ * such as Solaris or HP-UX, since they use a totally different
+ * approach to mailboxes (essentially, they have a setgid program
+ * rather than setuid, and they rely on the ability to "give away"
+ * files to do their work). IT IS NOT A BUG that this doesn't
+ * compile on such architectures.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h> /* autoconf */
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef EX_OK
+# undef EX_OK /* unistd.h may have another use for this */
+#endif
+
+#ifdef _UNIXWARE
+ #define EX_OK 0 /* successful termination */
+ #define EX__BASE 64 /* base value for error messages */
+ #define EX_USAGE 64 /* command line usage error */
+ #define EX_DATAERR 65 /* data format error */
+ #define EX_NOINPUT 66 /* cannot open input */
+ #define EX_NOUSER 67 /* addressee unknown */
+ #define EX_NOHOST 68 /* host name unknown */
+ #define EX_UNAVAILABLE 69 /* service unavailable */
+ #define EX_SOFTWARE 70 /* internal software error */
+ #define EX_OSERR 71 /* system error (e.g., can't fork) */
+ #define EX_OSFILE 72 /* critical OS file missing */
+ #define EX_CANTCREAT 73 /* can't create (user) output file */
+ #define EX_IOERR 74 /* input/output error */
+ #define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
+ #define EX_PROTOCOL 76 /* remote error in protocol */
+ #define EX_NOPERM 77 /* permission denied */
+ #define EX_CONFIG 78 /* configuration error */
+ #define EX__MAX 78 /* maximum listed value */
+#else
+ #include <sysexits.h>
+#endif
+
+#include <ctype.h>
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#ifndef HAVE_VSNPRINTF
+#include "../kotalkd/vsnprintf.c"
+#endif
+
+#if (defined(sun) && defined(__svr4__)) || defined(__SVR4) || defined(_UNIXWARE)
+ #define USE_LOCKF 1
+ #define USE_SETEUID 1
+ #define _PATH_MAILDIR "/var/mail"
+#endif
+
+#if defined(_SCO_DS)
+ #define USE_LOCKF 1
+#endif
+
+#if defined(_AIX)
+ #define USE_LOCKF 1
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(ultrix)
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(__osf__)
+ #define USE_VSYSLOG 0
+#endif
+
+#if defined(NeXT)
+ #include <libc.h>
+ #define _PATH_MAILDIR "/usr/spool/mail"
+ #define __dead /* empty */
+ #define S_IRUSR S_IREAD
+ #define S_IWUSR S_IWRITE
+#endif
+
+/*
+ * If you don't have flock, you could try using lockf instead.
+ */
+
+#if defined(USE_LOCKF) || !defined(HAVE_FLOCK)
+ #define flock(a, b) lockf(a, b, 0)
+ #undef LOCK_EX
+ #define LOCK_EX F_LOCK
+#endif
+
+#ifndef USE_VSYSLOG
+ #define USE_VSYSLOG 1
+#endif
+
+#ifdef BSD4_4
+# include "pathnames.h"
+# define USE_SETEUID
+#endif
+
+#ifndef __P
+# ifdef __STDC__
+# define __P(protos) protos
+# else
+# define __P(protos) ()
+# define const
+# endif
+#endif
+#ifndef __dead
+# if defined(__GNUC__) && (__GNUC__ < 2 || __GNUC_MINOR__ < 5) && !defined(__STRICT_ANSI__)
+# define __dead __volatile
+# else
+# define __dead
+# endif
+#endif
+
+#ifndef _BSD_VA_LIST_
+# define _BSD_VA_LIST_ va_list
+#endif
+
+#if !defined(BSD4_4) && !defined(linux) && !defined(_UNIXWARE)
+extern char *strerror __P((int));
+extern FILE *fdopen __P((int, const char *));
+#endif
+
+/*
+ * If you don't have setreuid, and you have saved uids, and you have
+ * a seteuid() call that doesn't try to emulate using setuid(), then
+ * you can try defining USE_SETEUID.
+ */
+#ifdef USE_SETEUID
+# define setreuid(r, e) seteuid(e)
+#endif
+
+#ifndef _PATH_LOCTMP
+# define _PATH_LOCTMP "/tmp/local.XXXXXX"
+#endif
+#ifndef _PATH_MAILDIR
+# define _PATH_MAILDIR "/var/spool/mail"
+#endif
+
+#ifndef S_ISREG
+# define S_ISREG(mode) (((mode) & _S_IFMT) == S_IFREG)
+#endif
+#define KDEMAXPATHLEN 136
+
+int eval = EX_OK; /* sysexits.h error value. */
+
+void deliver __P((int, char *));
+void e_to_sys __P((int));
+__dead void err __P((const char *, ...));
+void notifybiff __P((char *));
+int store __P((char *));
+void usage __P((void));
+void vwarn __P((const char *, _BSD_VA_LIST_));
+void warn __P((const char *, ...));
+void lockmbox __P((char *));
+void unlockmbox __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pw;
+ int ch, fd;
+ uid_t uid;
+ char *from;
+ extern char *optarg;
+ extern int optind;
+
+ /* make sure we have some open file descriptors */
+ for (fd = 10; fd < 30; fd++)
+ (void) close(fd);
+
+ /* use a reasonable umask */
+ (void) umask(0077);
+
+#ifdef LOG_MAIL
+ openlog("mail.local", 0, LOG_MAIL);
+#else
+ openlog("mail.local", 0);
+#endif
+
+ from = 0;
+ while ((ch = getopt(argc, argv, "df:r:")) != EOF)
+ switch(ch) {
+ case 'd': /* Backward compatible. */
+ break;
+ case 'f':
+ case 'r': /* Backward compatible. */
+ if (from != 0) {
+ warn("multiple -f options");
+ usage();
+ }
+ from = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!*argv)
+ usage();
+
+ /*
+ * If from not specified, use the name from getlogin() if the
+ * uid matches, otherwise, use the name from the password file
+ * corresponding to the uid.
+ */
+ uid = getuid();
+ if (!from && (!(from = getlogin()) ||
+ !(pw = getpwnam(from)) || pw->pw_uid != uid))
+ from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
+
+ /*
+ * There is no way to distinguish the error status of one delivery
+ * from the rest of the deliveries. So, if we failed hard on one
+ * or more deliveries, but had no failures on any of the others, we
+ * return a hard failure. If we failed temporarily on one or more
+ * deliveries, we return a temporary failure regardless of the other
+ * failures. This results in the delivery being reattempted later
+ * at the expense of repeated failures and multiple deliveries.
+ */
+ for (fd = store(from); *argv; ++argv)
+ deliver(fd, *argv);
+ exit(eval);
+}
+
+int
+store(from)
+ char *from;
+{
+ FILE *fp = 0;
+ time_t tval;
+ int fd, eline;
+ char line[2048];
+ char tmpbuf[sizeof _PATH_LOCTMP + 1];
+
+ strcpy(tmpbuf, _PATH_LOCTMP);
+ if ((fd = mkstemp(tmpbuf)) == -1 || (fp = fdopen(fd, "w+")) == 0) {
+ e_to_sys(errno);
+ err("unable to open temporary file");
+ }
+ (void)unlink(tmpbuf);
+
+ (void)time(&tval);
+ (void)fprintf(fp, "From %s %s", from, ctime(&tval));
+
+ line[0] = '\0';
+ for (eline = 1; fgets(line, sizeof(line), stdin);) {
+ if (line[0] == '\n')
+ eline = 1;
+ else {
+ if (eline && line[0] == 'F' &&
+ !memcmp(line, "From ", 5))
+ (void)putc('>', fp);
+ eline = 0;
+ }
+ (void)fprintf(fp, "%s", line);
+ if (ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ }
+
+ /* If message not newline terminated, need an extra. */
+ if (!strchr(line, '\n'))
+ (void)putc('\n', fp);
+ /* Output a newline; note, empty messages are allowed. */
+ (void)putc('\n', fp);
+
+ if (fflush(fp) == EOF || ferror(fp)) {
+ e_to_sys(errno);
+ err("temporary file write error");
+ }
+ return (fd);
+}
+
+void
+deliver(fd, name)
+ int fd;
+ char *name;
+{
+ struct stat fsb, sb;
+ struct passwd *pw;
+ int mbfd, nr, nw, off;
+ char *p;
+ char biffmsg[100], buf[8*1024], path[KDEMAXPATHLEN];
+ off_t curoff;
+
+ /*
+ * Disallow delivery to unknown names -- special mailboxes can be
+ * handled in the sendmail aliases file.
+ */
+ if (!(pw = getpwnam(name))) {
+ if (eval != EX_TEMPFAIL)
+ eval = EX_UNAVAILABLE;
+ warn("unknown name: %s", name);
+ return;
+ }
+ endpwent();
+
+ /*
+ * Keep name reasonably short to avoid buffer overruns.
+ * This isn't necessary on BSD because of the proper
+ * definition of snprintf(), but it can cause problems
+ * on other systems.
+ * Also, clear out any bogus characters.
+ */
+
+ if (strlen(name) > 40)
+ name[40] = '\0';
+ for (p = name; *p != '\0'; p++)
+ {
+ if (!isascii(*p))
+ *p &= 0x7f;
+ else if (!isprint(*p))
+ *p = '.';
+ }
+
+ (void)snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR, name);
+
+ /*
+ * If the mailbox is linked or a symlink, fail. There's an obvious
+ * race here, that the file was replaced with a symbolic link after
+ * the lstat returned, but before the open. We attempt to detect
+ * this by comparing the original stat information and information
+ * returned by an fstat of the file descriptor returned by the open.
+ *
+ * NB: this is a symptom of a larger problem, that the mail spooling
+ * directory is writeable by the wrong users. If that directory is
+ * writeable, system security is compromised for other reasons, and
+ * it cannot be fixed here.
+ *
+ * If we created the mailbox, set the owner/group. If that fails,
+ * just return. Another process may have already opened it, so we
+ * can't unlink it. Historically, binmail set the owner/group at
+ * each mail delivery. We no longer do this, assuming that if the
+ * ownership or permissions were changed there was a reason.
+ *
+ * XXX
+ * open(2) should support flock'ing the file.
+ */
+tryagain:
+ lockmbox(path);
+ if (lstat(path, &sb)) {
+ mbfd = open(path,
+ O_APPEND|O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
+ if (mbfd == -1) {
+ if (errno == EEXIST)
+ goto tryagain;
+ } else if (fchown(mbfd, pw->pw_uid, pw->pw_gid)) {
+ e_to_sys(errno);
+ warn("chown %u.%u: %s", pw->pw_uid, pw->pw_gid, name);
+ goto err1;
+ }
+ } else if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) {
+ e_to_sys(errno);
+ warn("%s: irregular file", path);
+ goto err0;
+ } else if (sb.st_uid != pw->pw_uid) {
+ eval = EX_CANTCREAT;
+ warn("%s: wrong ownership (%d)", path, sb.st_uid);
+ goto err0;
+ } else {
+ mbfd = open(path, O_APPEND|O_WRONLY, 0);
+ if (mbfd != -1 &&
+ (fstat(mbfd, &fsb) || fsb.st_nlink != 1 ||
+ !S_ISREG(fsb.st_mode) || sb.st_dev != fsb.st_dev ||
+ sb.st_ino != fsb.st_ino || sb.st_uid != fsb.st_uid)) {
+ eval = EX_CANTCREAT;
+ warn("%s: file changed after open", path);
+ goto err1;
+ }
+ }
+
+ if (mbfd == -1) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err0;
+ }
+
+ /* Wait until we can get a lock on the file. */
+ if (flock(mbfd, LOCK_EX)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err1;
+ }
+
+ /* Get the starting offset of the new message for biff. */
+ curoff = lseek(mbfd, (off_t)0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof(biffmsg),
+ sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
+ name, curoff);
+
+ /* Copy the message into the file. */
+ if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+ goto err1;
+ }
+ if (setreuid(0, pw->pw_uid) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, %d): %s (r=%d, e=%d)",
+ pw->pw_uid, strerror(errno), getuid(), geteuid());
+ goto err1;
+ }
+#ifdef DEBUG
+ printf("new euid = %d\n", geteuid());
+#endif
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; off += nw)
+ if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ goto err3;
+ }
+ if (nr < 0) {
+ e_to_sys(errno);
+ warn("temporary file: %s", strerror(errno));
+ goto err3;
+ }
+
+ /* Flush to disk, don't wait for update. */
+ if (fsync(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+err3:
+ if (setreuid(0, 0) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, 0): %s", strerror(errno));
+ }
+#ifdef DEBUG
+ printf("reset euid = %d\n", geteuid());
+#endif
+ (void)ftruncate(mbfd, curoff);
+err1: (void)close(mbfd);
+err0: unlockmbox();
+ return;
+ }
+
+ /* Close and check -- NFS doesn't write until the close. */
+ if (close(mbfd)) {
+ e_to_sys(errno);
+ warn("%s: %s", path, strerror(errno));
+ unlockmbox();
+ return;
+ }
+
+ if (setreuid(0, 0) < 0) {
+ e_to_sys(errno);
+ warn("setreuid(0, 0): %s", strerror(errno));
+ }
+#ifdef DEBUG
+ printf("reset euid = %d\n", geteuid());
+#endif
+ unlockmbox();
+ notifybiff(biffmsg);
+}
+
+/*
+ * user.lock files are necessary for compatibility with other
+ * systems, e.g., when the mail spool file is NFS exported.
+ * Alas, mailbox locking is more than just a local matter.
+ * EPA 11/94.
+ */
+
+char lockname[KDEMAXPATHLEN];
+int locked = 0;
+
+void
+lockmbox(path)
+ char *path;
+{
+ int statfailed = 0;
+
+ if (locked)
+ return;
+ snprintf(lockname, sizeof(lockname), "%s.lock", path);
+ for (;; sleep(5)) {
+ int fd;
+ struct stat st;
+ time_t now;
+
+ fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT, 0);
+ if (fd >= 0) {
+ locked = 1;
+ close(fd);
+ return;
+ }
+ if (stat(lockname, &st) < 0) {
+ if (statfailed++ > 5)
+ return;
+ continue;
+ }
+ statfailed = 0;
+ time(&now);
+ if (now < st.st_ctime + 300)
+ continue;
+ unlink(lockname);
+ }
+}
+
+void
+unlockmbox()
+{
+ if (!locked)
+ return;
+ unlink(lockname);
+ locked = 0;
+}
+
+void
+notifybiff(msg)
+ char *msg;
+{
+ static struct sockaddr_in addr;
+ static int f = -1;
+ struct hostent *hp;
+ struct servent *sp;
+ int len;
+
+ if (!addr.sin_family) {
+ /* Be silent if biff service not available. */
+ if (!(sp = getservbyname("biff", "udp")))
+ return;
+ if (!(hp = gethostbyname("localhost"))) {
+ warn("localhost: %s", strerror(errno));
+ return;
+ }
+ addr.sin_family = hp->h_addrtype;
+ memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+ addr.sin_port = sp->s_port;
+ }
+ if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ warn("socket: %s", strerror(errno));
+ return;
+ }
+ len = strlen(msg) + 1;
+ if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
+ != len)
+ warn("sendto biff: %s", strerror(errno));
+}
+
+void
+usage()
+{
+ eval = EX_USAGE;
+ err("usage: mail.local [-f from] user ...");
+}
+
+#if __STDC__
+__dead void
+err(const char *fmt, ...)
+#else
+__dead void
+err(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+
+ exit(eval);
+}
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vwarn(fmt, ap);
+ va_end(ap);
+}
+
+void
+vwarn(fmt, ap)
+ const char *fmt;
+ _BSD_VA_LIST_ ap;
+{
+ /*
+ * Log the message to stderr.
+ *
+ * Don't use LOG_PERROR as an openlog() flag to do this,
+ * it's not portable enough.
+ */
+ if (eval != EX_USAGE)
+ (void)fprintf(stderr, "mail.local: ");
+ (void)vfprintf(stderr, fmt, ap);
+ (void)fprintf(stderr, "\n");
+
+#if USE_VSYSLOG
+ /* Log the message to syslog. */
+ vsyslog(LOG_ERR, fmt, ap);
+#else
+ {
+ char fmtbuf[10240];
+
+ (void) vsprintf(fmtbuf, fmt, ap);
+ syslog(LOG_ERR, "%s", fmtbuf);
+ }
+#endif
+}
+
+/*
+ * e_to_sys --
+ * Guess which errno's are temporary. Gag me.
+ */
+void
+e_to_sys(num)
+ int num;
+{
+ /* Temporary failures override hard errors. */
+ if (eval == EX_TEMPFAIL)
+ return;
+
+ switch(num) { /* Hopefully temporary errors. */
+#ifdef EAGAIN
+ case EAGAIN: /* Resource temporarily unavailable */
+#endif
+#ifdef EDQUOT
+ case EDQUOT: /* Disc quota exceeded */
+#endif
+#ifdef EBUSY
+ case EBUSY: /* Device busy */
+#endif
+#ifdef EPROCLIM
+ case EPROCLIM: /* Too many processes */
+#endif
+#ifdef EUSERS
+ case EUSERS: /* Too many users */
+#endif
+#ifdef ECONNABORTED
+ case ECONNABORTED: /* Software caused connection abort */
+#endif
+#ifdef ECONNREFUSED
+ case ECONNREFUSED: /* Connection refused */
+#endif
+#ifdef ECONNRESET
+ case ECONNRESET: /* Connection reset by peer */
+#endif
+#ifdef EDEADLK
+ case EDEADLK: /* Resource deadlock avoided */
+#endif
+#ifdef EFBIG
+ case EFBIG: /* File too large */
+#endif
+#ifdef EHOSTDOWN
+ case EHOSTDOWN: /* Host is down */
+#endif
+#ifdef EHOSTUNREACH
+ case EHOSTUNREACH: /* No route to host */
+#endif
+#ifdef EMFILE
+ case EMFILE: /* Too many open files */
+#endif
+#ifdef ENETDOWN
+ case ENETDOWN: /* Network is down */
+#endif
+#ifdef ENETRESET
+ case ENETRESET: /* Network dropped connection on reset */
+#endif
+#ifdef ENETUNREACH
+ case ENETUNREACH: /* Network is unreachable */
+#endif
+#ifdef ENFILE
+ case ENFILE: /* Too many open files in system */
+#endif
+#ifdef ENOBUFS
+ case ENOBUFS: /* No buffer space available */
+#endif
+#ifdef ENOMEM
+ case ENOMEM: /* Cannot allocate memory */
+#endif
+#ifdef ENOSPC
+ case ENOSPC: /* No space left on device */
+#endif
+#ifdef EROFS
+ case EROFS: /* Read-only file system */
+#endif
+#ifdef ESTALE
+ case ESTALE: /* Stale NFS file handle */
+#endif
+#ifdef ETIMEDOUT
+ case ETIMEDOUT: /* Connection timed out */
+#endif
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN && EWOULDBLOCK != EDEADLK
+ case EWOULDBLOCK: /* Operation would block. */
+#endif
+ eval = EX_TEMPFAIL;
+ break;
+ default:
+ eval = EX_UNAVAILABLE;
+ break;
+ }
+}
+
+#if !defined(BSD4_4) && !defined(__osf__) && !defined(__GLIBC__)
+
+char *
+strerror(eno)
+ int eno;
+{
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+ static char ebuf[60];
+
+ if (eno >= 0 && eno <= sys_nerr)
+ return sys_errlist[eno];
+ (void) sprintf(ebuf, "Error %d", eno);
+ return ebuf;
+}
+
+# endif
+
+#if defined(ultrix) || defined(_UNIXWARE)
+
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+
+static int _gettemp();
+
+mkstemp(path)
+ char *path;
+{
+ int fd;
+
+ return (_gettemp(path, &fd) ? fd : -1);
+}
+
+/*
+char *
+mktemp(path)
+ char *path;
+{
+ return(_gettemp(path, (int *)0) ? path : (char *)0);
+}
+*/
+
+static
+_gettemp(path, doopen)
+ char *path;
+ register int *doopen;
+{
+ extern int errno;
+ register char *start, *trv;
+ struct stat sbuf;
+ u_int pid;
+
+ pid = getpid();
+ for (trv = path; *trv; ++trv); /* extra X's get set to 0's */
+ while (*--trv == 'X') {
+ *trv = (pid % 10) + '0';
+ pid /= 10;
+ }
+
+ /*
+ * check the target directory; if you have six X's and it
+ * doesn't exist this runs for a *very* long time.
+ */
+ for (start = trv + 1;; --trv) {
+ if (trv <= path)
+ break;
+ if (*trv == '/') {
+ *trv = '\0';
+ if (stat(path, &sbuf))
+ return(0);
+ if (!S_ISDIR(sbuf.st_mode)) {
+ errno = ENOTDIR;
+ return(0);
+ }
+ *trv = '/';
+ break;
+ }
+ }
+
+ for (;;) {
+ if (doopen) {
+ if ((*doopen =
+ open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
+ return(1);
+ if (errno != EEXIST)
+ return(0);
+ }
+ else if (stat(path, &sbuf))
+ return(errno == ENOENT ? 1 : 0);
+
+ /* tricky little algorithm for backward compatibility */
+ for (trv = start;;) {
+ if (!*trv)
+ return(0);
+ if (*trv == 'z')
+ *trv++ = 'a';
+ else {
+ if (isdigit(*trv))
+ *trv = 'a';
+ else
+ ++*trv;
+ break;
+ }
+ }
+ }
+ /*NOTREACHED*/
+}
+
+#endif
diff --git a/ktalkd/mail.local/pathnames.h b/ktalkd/mail.local/pathnames.h
new file mode 100644
index 00000000..8e439254
--- /dev/null
+++ b/ktalkd/mail.local/pathnames.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/4/93
+ */
+#include <paths.h>
+
+#define _PATH_LOCTMP "/tmp/local.XXXXXX"
diff --git a/lanbrowsing/Makefile.am b/lanbrowsing/Makefile.am
new file mode 100644
index 00000000..4fc6aa8a
--- /dev/null
+++ b/lanbrowsing/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = lisa kcmlisa kio_lan
diff --git a/lanbrowsing/kcmlisa/Makefile.am b/lanbrowsing/kcmlisa/Makefile.am
new file mode 100644
index 00000000..cd9750e9
--- /dev/null
+++ b/lanbrowsing/kcmlisa/Makefile.am
@@ -0,0 +1,18 @@
+kde_module_LTLIBRARIES = kcm_lanbrowser.la
+
+kcm_lanbrowser_la_SOURCES = main.cpp setupwizard.cpp kcmlisa.cpp kcmreslisa.cpp kcmkiolan.cpp portsettingsbar.cpp findnic.cpp
+
+kcm_lanbrowser_la_LDFLAGS = $(all_libraries) $(KDE_PLUGIN) -module
+kcm_lanbrowser_la_LIBADD = -lkdeui
+
+INCLUDES= $(all_includes)
+
+SUBDIRS =
+
+kcm_lanbrowser_la_METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) $(kcm_lanbrowser_la_SOURCES) -o $(podir)/kcmlanbrowser.pot
+
+apps_DATA = kcmkiolan.desktop kcmlisa.desktop kcmreslisa.desktop
+appsdir = $(kde_appsdir)/.hidden
diff --git a/lanbrowsing/kcmlisa/configure.in.in b/lanbrowsing/kcmlisa/configure.in.in
new file mode 100644
index 00000000..e08832eb
--- /dev/null
+++ b/lanbrowsing/kcmlisa/configure.in.in
@@ -0,0 +1,4 @@
+AC_CHECK_FUNCS(getifaddrs getnameinfo)
+AC_CHECK_MEMBERS([struct sockaddr.sa_len],,,[
+#include <sys/types.h>
+#include <sys/socket.h>])
diff --git a/lanbrowsing/kcmlisa/findnic.cpp b/lanbrowsing/kcmlisa/findnic.cpp
new file mode 100644
index 00000000..75f40f0e
--- /dev/null
+++ b/lanbrowsing/kcmlisa/findnic.cpp
@@ -0,0 +1,298 @@
+/*
+ * findnic.cpp
+ *
+ * Copyright (c) 2001 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+
+#ifdef USE_SOLARIS
+/* net/if.h is incompatible with STL on Solaris 2.6 - 2.8, redefine
+ map in the header file because we don't need it. -- Simon Josefsson */
+#define map junkmap
+#endif
+# include <net/if.h>
+#ifdef USE_SOLARIS
+#undef map
+#endif
+
+#include <sys/ioctl.h>
+
+#ifndef HAVE_STRUCT_SOCKADDR_SA_LEN
+ #undef HAVE_GETNAMEINFO
+ #undef HAVE_GETIFADDRS
+#endif
+
+#if defined(HAVE_GETNAMEINFO) && defined(HAVE_GETIFADDRS)
+ #include <ifaddrs.h>
+ #include <netdb.h>
+ #include <qstring.h>
+
+ QString flags_tos (unsigned int flags);
+#endif
+
+#include "findnic.h"
+
+#include <klocale.h>
+#include <ksockaddr.h>
+
+NICList* findNICs()
+{
+ NICList* nl=new NICList;
+ nl->setAutoDelete(true);
+
+#if !defined(HAVE_GETIFADDRS) && !defined(HAVE_GETNAMEINFO)
+
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ char buf[8*1024];
+ struct ifconf ifc;
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_req = (struct ifreq *) buf;
+ int result=ioctl(sockfd, SIOCGIFCONF, &ifc);
+
+ for (char* ptr = buf; ptr < buf + ifc.ifc_len; )
+ {
+ struct ifreq *ifr =(struct ifreq *) ptr;
+ int len = sizeof(struct sockaddr);
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ if (ifr->ifr_addr.sa_len > len)
+ len = ifr->ifr_addr.sa_len; /* length > 16 */
+#endif
+ ptr += sizeof(ifr->ifr_name) + len; /* for next one in buffer */
+
+ int flags;
+ struct sockaddr_in *sinptr;
+ MyNIC *tmp=0;
+ switch (ifr->ifr_addr.sa_family)
+ {
+ case AF_INET:
+ sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
+ flags=0;
+
+ struct ifreq ifcopy;
+ ifcopy=*ifr;
+ result=ioctl(sockfd,SIOCGIFFLAGS,&ifcopy);
+ flags=ifcopy.ifr_flags;
+
+ tmp=new MyNIC;
+ tmp->name=ifr->ifr_name;
+ if ((flags & IFF_UP) == IFF_UP)
+ tmp->state=i18n("Up");
+ else
+ tmp->state=i18n("Down");
+
+ if ((flags & IFF_BROADCAST) == IFF_BROADCAST)
+ tmp->type=i18n("Broadcast");
+ else if ((flags & IFF_POINTOPOINT) == IFF_POINTOPOINT)
+ tmp->type=i18n("Point to Point");
+#ifndef _AIX
+ else if ((flags & IFF_MULTICAST) == IFF_MULTICAST)
+ tmp->type=i18n("Multicast");
+#endif
+ else if ((flags & IFF_LOOPBACK) == IFF_LOOPBACK)
+ tmp->type=i18n("Loopback");
+ else
+ tmp->type=i18n("Unknown");
+
+ tmp->addr=inet_ntoa(sinptr->sin_addr);
+
+ ifcopy=*ifr;
+ result=ioctl(sockfd,SIOCGIFNETMASK,&ifcopy);
+ if (result==0)
+ {
+ sinptr = (struct sockaddr_in *) &ifcopy.ifr_addr;
+ tmp->netmask=inet_ntoa(sinptr->sin_addr);
+ }
+ else
+ tmp->netmask=i18n("Unknown");
+ nl->append(tmp);
+ break;
+
+ default:
+ break;
+ }
+ }
+#else
+ struct ifaddrs *ifap, *ifa;
+ if (getifaddrs(&ifap) != 0) {
+ return nl;
+ }
+
+ MyNIC *tmp=0;
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET6:
+ case AF_INET: {
+ tmp = new MyNIC;
+ tmp->name = ifa->ifa_name;
+
+ char buf[128];
+
+ bzero(buf, 128);
+ getnameinfo(ifa->ifa_addr, ifa->ifa_addr->sa_len, buf, 127, 0, 0, NI_NUMERICHOST);
+ tmp->addr = buf;
+
+ if (ifa->ifa_netmask != NULL) {
+#ifdef Q_OS_FREEBSD
+ struct sockaddr_in *sinptr = (struct sockaddr_in *)ifa->ifa_netmask;
+ tmp->netmask=inet_ntoa(sinptr->sin_addr);
+#else
+ bzero(buf, 128);
+ getnameinfo(ifa->ifa_netmask, ifa->ifa_netmask->sa_len, buf, 127, 0, 0, NI_NUMERICHOST);
+ tmp->netmask = buf;
+#endif
+ }
+
+ if (ifa->ifa_flags & IFF_UP)
+ tmp->state=i18n("Up");
+ else
+ tmp->state=i18n("Down");
+
+ tmp->type = flags_tos(ifa->ifa_flags);
+
+ nl->append(tmp);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ freeifaddrs(ifap);
+#endif
+ return nl;
+}
+
+void suggestSettingsForAddress(const QString& addrMask, LisaConfigInfo& lci)
+{
+ QString ip=addrMask.left(addrMask.find("/"));
+ QString mask=addrMask.mid(addrMask.find("/")+1);
+
+ if (mask[mask.length()-1]==';')
+ mask=mask.left(mask.length()-1);
+ MyNIC tmpNic;
+ KInetSocketAddress::stringToAddr(AF_INET, mask.latin1(), &tmpNic.netmask);
+ KInetSocketAddress::stringToAddr(AF_INET, ip.latin1(), &tmpNic.addr);
+ suggestSettingsForNic(&tmpNic,lci);
+}
+
+void suggestSettingsForNic(MyNIC* nic, LisaConfigInfo& lci)
+{
+ lci.clear();
+ if (nic==0)
+ return;
+ QString address = nic->addr;
+ QString netmask = nic->netmask;
+
+ QString addrMask(address+"/"+netmask+";");
+ struct in_addr tmpaddr;
+ inet_aton(nic->netmask.latin1(), &tmpaddr);
+ unsigned int tmp= ntohl(tmpaddr.s_addr);
+
+ //if the host part is less than 20 bits simply take it
+ //this might be a problem on 64 bit machines
+ if (tmp>0xfffff000)
+ {
+ lci.pingAddresses=addrMask;
+ lci.broadcastNetwork=addrMask;
+ lci.allowedAddresses=addrMask;
+ lci.secondWait=0;
+ lci.secondScan=false;
+ lci.firstWait=30;
+ lci.maxPingsAtOnce=256;
+ lci.updatePeriod=300;
+ lci.useNmblookup=false;
+ lci.unnamedHosts=false;
+ }
+ else
+ {
+ lci.pingAddresses="";
+ lci.broadcastNetwork=addrMask;
+ lci.allowedAddresses=addrMask;
+ lci.secondWait=0;
+ lci.secondScan=false;
+ lci.firstWait=30;
+ lci.maxPingsAtOnce=256;
+ lci.updatePeriod=300;
+ lci.useNmblookup=true;
+ lci.unnamedHosts=false;
+ }
+}
+
+LisaConfigInfo::LisaConfigInfo()
+{
+ clear();
+}
+
+void LisaConfigInfo::clear()
+{
+ pingAddresses="";
+ broadcastNetwork="";
+ allowedAddresses="";
+ secondWait=0;
+ firstWait=0;
+ secondScan=false;
+ maxPingsAtOnce=256;
+ updatePeriod=0;
+ useNmblookup=false;
+ unnamedHosts=false;
+}
+
+#if defined(HAVE_GETNAMEINFO) && defined(HAVE_GETIFADDRS)
+QString flags_tos (unsigned int flags)
+{
+ QString tmp;
+ if (flags & IFF_POINTOPOINT) {
+ tmp += i18n("Point to Point");
+ }
+
+ if (flags & IFF_BROADCAST) {
+ if (tmp.length()) {
+ tmp += QString::fromLatin1(", ");
+ }
+ tmp += i18n("Broadcast");
+ }
+
+ if (flags & IFF_MULTICAST) {
+ if (tmp.length()) {
+ tmp += QString::fromLatin1(", ");
+ }
+ tmp += i18n("Multicast");
+ }
+
+ if (flags & IFF_LOOPBACK) {
+ if (tmp.length()) {
+ tmp += QString::fromLatin1(", ");
+ }
+ tmp += i18n("Loopback");
+ }
+ return tmp;
+}
+#endif
diff --git a/lanbrowsing/kcmlisa/findnic.h b/lanbrowsing/kcmlisa/findnic.h
new file mode 100644
index 00000000..45273749
--- /dev/null
+++ b/lanbrowsing/kcmlisa/findnic.h
@@ -0,0 +1,69 @@
+/*
+ * findnic.h
+ *
+ * Copyright (c) 2001 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FINDNIC_H
+#define FINDNIC_H
+
+#include <qptrlist.h>
+#include <qstring.h>
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct MyNIC
+{
+ QString name;
+ QString addr;
+ QString netmask;
+ QString state;
+ QString type;
+};
+
+struct LisaConfigInfo
+{
+ LisaConfigInfo();
+ void clear();
+ QString pingAddresses;
+ QString broadcastNetwork;
+ QString allowedAddresses;
+ int secondWait;
+ bool secondScan;
+ int firstWait;
+ int maxPingsAtOnce;
+ int updatePeriod;
+ bool useNmblookup;
+ bool unnamedHosts;
+};
+
+
+typedef QPtrList<MyNIC> NICList;
+
+//some plain C-like helper functions
+
+///Return a list with all NIC, which are up and broadcast-able
+NICList* findNICs();
+///Enter some settings into lci which might be appropriate for nic
+void suggestSettingsForNic(MyNIC* nic, LisaConfigInfo& lci);
+
+void suggestSettingsForAddress(const QString& addrMask, LisaConfigInfo& lci);
+
+#endif
+
diff --git a/lanbrowsing/kcmlisa/kcmkiolan.cpp b/lanbrowsing/kcmlisa/kcmkiolan.cpp
new file mode 100644
index 00000000..89ae761e
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmkiolan.cpp
@@ -0,0 +1,103 @@
+/*
+ * kcmkiolan.cpp
+ *
+ * Copyright (c) 2000 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "kcmkiolan.h"
+
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <qlineedit.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kdialog.h>
+
+IOSlaveSettings::IOSlaveSettings(const QString& config, QWidget *parent)
+:KCModule(parent)
+,m_config(config,false,true)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ layout->setAutoAdd(true);
+
+ QGroupBox* group=new QGroupBox(1, Horizontal, i18n("Show Links for Following Services"), this);
+
+ m_ftpSettings=new PortSettingsBar(i18n("FTP (TCP, port 21): "), group);
+ m_httpSettings=new PortSettingsBar(i18n("HTTP (TCP, port 80): "),group);
+ m_nfsSettings=new PortSettingsBar(i18n("NFS (TCP, port 2049): "),group);
+ m_smbSettings=new PortSettingsBar(i18n("Windows shares (TCP, ports 445 and 139):"),group);
+ m_fishSettings=new PortSettingsBar(i18n("Secure Shell/Fish (TCP, port 22): "),group);
+ m_shortHostnames = new QCheckBox(i18n("Show &short hostnames (without domain suffix)"),this);
+
+ QHBox *hbox=new QHBox(this);
+ QLabel *label=new QLabel(i18n("Default LISa server host: "), hbox);
+ m_defaultLisaHostLe=new QLineEdit(hbox);
+ label->setBuddy(m_defaultLisaHostLe);
+
+ QWidget *w=new QWidget(this);
+ layout->setStretchFactor(m_ftpSettings,0);
+ layout->setStretchFactor(m_httpSettings,0);
+ layout->setStretchFactor(m_nfsSettings,0);
+ layout->setStretchFactor(m_smbSettings,0);
+ layout->setStretchFactor(m_shortHostnames,0);
+ layout->setStretchFactor(hbox,0);
+ layout->setStretchFactor(w,1);
+
+ connect(m_ftpSettings,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_httpSettings,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_nfsSettings,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_smbSettings,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_fishSettings,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_shortHostnames,SIGNAL(clicked()),this,SIGNAL(changed()));
+ connect(m_defaultLisaHostLe, SIGNAL(textChanged(const QString&)),this,SIGNAL(changed()));
+}
+
+void IOSlaveSettings::load()
+{
+ kdDebug()<<"IOSlaveSettings::load()"<<endl;
+ m_ftpSettings->setChecked(m_config.readNumEntry("Support_FTP", PORTSETTINGS_CHECK));
+ m_httpSettings->setChecked(m_config.readNumEntry("Support_HTTP", PORTSETTINGS_CHECK));
+ m_nfsSettings->setChecked(m_config.readNumEntry("Support_NFS", PORTSETTINGS_CHECK));
+ m_smbSettings->setChecked(m_config.readNumEntry("Support_SMB", PORTSETTINGS_CHECK));
+ m_fishSettings->setChecked(m_config.readNumEntry("Support_FISH", PORTSETTINGS_CHECK));
+ m_shortHostnames->setChecked(m_config.readBoolEntry("ShowShortHostnames",false));
+// m_rlanSidebar->setChecked(m_config.readEntry("sidebarURL", "lan:/") == "rlan:/" ? true : false );
+ m_defaultLisaHostLe->setText(m_config.readEntry("DefaultLisaHost", "localhost"));
+}
+
+void IOSlaveSettings::save()
+{
+ kdDebug()<<"IOSlaveSettings::save()"<<endl;
+ m_config.writeEntry("AlreadyConfigured",true);
+ m_config.writeEntry("Support_FTP", m_ftpSettings->selected());
+ m_config.writeEntry("Support_HTTP", m_httpSettings->selected());
+ m_config.writeEntry("Support_NFS", m_nfsSettings->selected());
+ m_config.writeEntry("Support_SMB", m_smbSettings->selected());
+ m_config.writeEntry("Support_FISH", m_fishSettings->selected());
+ m_config.writeEntry("ShowShortHostnames",m_shortHostnames->isChecked());
+// m_config.writeEntry("sidebarURL", m_rlanSidebar->isChecked() ? "rlan:/" : "lan:/");
+ m_config.writeEntry("DefaultLisaHost", m_defaultLisaHostLe->text());
+
+ m_config.sync();
+}
+
+#include "kcmkiolan.moc"
+
diff --git a/lanbrowsing/kcmlisa/kcmkiolan.desktop b/lanbrowsing/kcmlisa/kcmkiolan.desktop
new file mode 100644
index 00000000..c15e9724
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmkiolan.desktop
@@ -0,0 +1,140 @@
+[Desktop Entry]
+Type=Application
+Icon=samba
+Exec=kcmshell kcmkiolan
+
+X-KDE-ModuleType=Library
+X-KDE-Library=lanbrowser
+X-KDE-FactoryName=kiolan
+
+Name=LAN KIO Slave
+Name[ar]=الشبكة المحلية KIO Slave
+Name[be]=Модуль kioslave Ð´Ð»Ñ Ð¼ÑÑцовай Ñетцы
+Name[bn]=লà§à¦¯à¦¾à¦¨ কে-আই-ও সà§à¦²à§‡à¦­
+Name[br]=Sklav LAN evit KIO
+Name[bs]=LAN KIO slave
+Name[cs]=LAN KIO slave
+Name[cy]=Gwas KIO LAN
+Name[da]=LAN KIO-slave
+Name[de]=Ein-/Ausgabemodul für LAN
+Name[eo]=LAN-enel-sklavo
+Name[es]=Esclavo de LAN KIO
+Name[et]=Kohtvõrgu KIO moodul
+Name[eu]=LAN KIO morroia
+Name[fi]=LAN-siirräntätyöskentelijä
+Name[fr]=Module KIO LAN
+Name[ga]=Sclábhaí KIO LAN
+Name[gl]=Escravo KIO para redes locáis
+Name[hi]=लैन केआईओ सà¥à¤²à¥‡à¤µ
+Name[hu]=Hálózatböngészés
+Name[is]=LAN KIO þræll
+Name[it]=Slave LAN KIO
+Name[ja]=LAN KIO スレーブ
+Name[km]=KIO Slave បណ្ដាញ​មូលដ្ឋាន
+Name[lt]=LAN antrinÄ— KDE programa
+Name[mn]=Протокол IO LAN
+Name[ms]=Hamba LAN KIO
+Name[nb]=KIO-slave for LAN
+Name[nds]=Nettwark-In-/Utgaavdeenst
+Name[ne]=LAN KIO सà¥à¤²à¤¾à¤­
+Name[nl]=LAN IO Slave
+Name[nn]=LAN-iuslave
+Name[nso]=Lekgoba la LAN KIO
+Name[pa]=LAN KIO ਸਲੇਵ
+Name[pl]=Procedura we/wy dla sieci lokalnej
+Name[pt]='Kioslave' LAN
+Name[ru]=Протокол IO LAN
+Name[se]=LAN KIO-šláva
+Name[sk]=LAN KIO slave
+Name[sl]=KIO Slave za LAN
+Name[sv]=I/O-slav för lokalt nätverk
+Name[ta]=LAN KIO அடிமை
+Name[tg]=Фармонбари Шабакаи Маҳаллии KIO
+Name[uk]=Підлеглий KIO ЛОМ
+Name[zh_CN]=LAN KIO 从属进程
+
+Comment=lan: and rlan: setup
+Comment[ar]=lan: و rlan: تنصيب
+Comment[be]=ÐаÑтаўленне lan: Ñ– rlan:
+Comment[bg]=ÐаÑтройване на протоколите lan: и rlan:
+Comment[bn]=লà§à¦¯à¦¾à¦¨: à¦à¦¬à¦‚ আর-লà§à¦¯à¦¾à¦¨: বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦¨à¦¾
+Comment[br]=kefluniañ lan: ha rlan:
+Comment[bs]=Podešavanje lan: i rlan: URLova
+Comment[ca]=Per l'arranjament de lan: i rlan:
+Comment[cs]=Nastavení lan: and rlan:
+Comment[cy]=gosod lan: a rlan:
+Comment[da]=lan: og rlan: opsætning
+Comment[de]=lan: und rlan: einrichten
+Comment[el]=ΡÏθμιση lan: και rlan:
+Comment[eo]=lan kaj rlan: agordo
+Comment[es]=lan: y rlan: configuración
+Comment[et]=lan: ja rlan: seadistamine
+Comment[eu]=lan: eta rlan: konfigurazioa
+Comment[fa]=شبکۀ محلی: و شبکۀ محلی راه دور: برپایی
+Comment[fi]=lan: ja rlan: asetukset
+Comment[fr]=configuration pour lan: et rlan:
+Comment[ga]=Cumraíocht lan: agus rlan:
+Comment[gl]=Configuración de lan: e rlan:
+Comment[he]=שינוי הגדרות :lan ו-:rlan
+Comment[hi]=लैन: तथा आर-लैन: सेटअप
+Comment[hr]=Podešavanje lan-a i rlan-a
+Comment[hu]=A lan: és az rlan: protokoll beállítása
+Comment[is]=lan: og rlan: uppsetning
+Comment[it]=Configurazione lan: e rlan:
+Comment[ja]=lan 㨠rlan ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
+Comment[ka]=lan: დრrlan: გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=lan: мен rlan: параметрлерді орнату
+Comment[km]=រៀបចំ lan: និង rlan:
+Comment[lt]=lan: ir rlan: nustatymas
+Comment[mk]=ПоÑтавување на lan: и rlan:
+Comment[mn]=lan: ба rlan: тохируулга
+Comment[ms]=lan: dan rlan: pemasangan
+Comment[nb]=Oppsett for lan: og rlan:
+Comment[nds]=lan: un rlan: instellen
+Comment[ne]=lan: र rlan: सेटअप
+Comment[nl]=Instellen van lan: en rlan:
+Comment[nn]=Oppsett av lan: og rlan:
+Comment[nso]=peakanyo ya lan: le rlan:
+Comment[pl]=ustawienia lan: i rlan:
+Comment[pt]=Configuração do lan: e do rlan:
+Comment[pt_BR]=lan: e rlan: configuração
+Comment[ru]=параметры lan: и rlan:
+Comment[se]=Heivet lan: ja rlan:
+Comment[sk]=lan: a rlan: nastavenie
+Comment[sl]=Nastavitve lan: in rlan:
+Comment[sr]=Подешавање lan:-а и rlan:-а
+Comment[sr@Latn]=Podešavanje lan:-a i rlan:-a
+Comment[sv]=Inställning av lan: och rlan:
+Comment[ta]=lan: மறà¯à®±à¯à®®à¯ rlan: அமைபà¯à®ªà¯
+Comment[tg]=барпоÑозии шабакаи маҳаллӣ: ва rlan:
+Comment[tr]=lan: ve rlan: ayarları
+Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ lan: Ñ– rlan:
+Comment[ven]=lan: na rlan: mavhekanyele
+Comment[xh]=lan: kunye ne rlan: ucwangciso
+Comment[zh_CN]=lan: 和 rlan: 设置
+Comment[zh_HK]=lan: 與 rlan: 設定
+Comment[zh_TW]=lan: 與 rlan: 設定
+Comment[zu]=Ilayini yokuyalela ye applet
+
+Keywords=lan
+Keywords[bg]=наÑтройки, локална, мрежа, lan
+Keywords[bn]=লà§à¦¯à¦¾à¦¨
+Keywords[de]=LAN
+Keywords[eo]=reto
+Keywords[fa]=شبکۀ داخلی
+Keywords[fr]=lan,réseau local,réseau
+Keywords[gl]=lan,redes locáis
+Keywords[he]=lan,רשת מקומית
+Keywords[hi]=लैन
+Keywords[lt]=lan,vietinis tinklas
+Keywords[nds]=lan,nettwark
+Keywords[ne]=लà¥à¤¯à¤¾à¤¨
+Keywords[nl]=lan,lokaal netwerk,netwerk
+Keywords[pl]=lan,LAN
+Keywords[ru]=lan, Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ñеть
+Keywords[sk]=lan,sieť
+Keywords[tg]=шабакаи маҳаллӣ
+Keywords[uk]=lan, локальна мережа
+Keywords[uz]=lokal tarmoq
+Keywords[uz@cyrillic]=локал тармоқ
+Keywords[zh_CN]=lan,局域网
diff --git a/lanbrowsing/kcmlisa/kcmkiolan.h b/lanbrowsing/kcmlisa/kcmkiolan.h
new file mode 100644
index 00000000..8711deab
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmkiolan.h
@@ -0,0 +1,53 @@
+/*
+ * kcmkiolan.h
+ *
+ * Copyright (c) 2000 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KCMKIOLAN_H
+#define KCMKIOLAN_H
+
+#include "portsettingsbar.h"
+#include <kcmodule.h>
+#include <kconfig.h>
+
+class QCheckBox;
+class QLineEdit;
+
+class IOSlaveSettings:public KCModule
+{
+ Q_OBJECT
+ public:
+ IOSlaveSettings(const QString& config, QWidget *parent=0);
+ virtual ~IOSlaveSettings() {};
+ void load();
+ void save();
+ signals:
+ void changed();
+ protected:
+ KConfig m_config;
+ QCheckBox *m_shortHostnames;
+ QCheckBox *m_rlanSidebar;
+ PortSettingsBar *m_ftpSettings;
+ PortSettingsBar *m_httpSettings;
+ PortSettingsBar *m_nfsSettings;
+ PortSettingsBar *m_smbSettings;
+ PortSettingsBar *m_fishSettings;
+ QLineEdit *m_defaultLisaHostLe;
+};
+
+#endif
diff --git a/lanbrowsing/kcmlisa/kcmlisa.cpp b/lanbrowsing/kcmlisa/kcmlisa.cpp
new file mode 100644
index 00000000..654c5848
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmlisa.cpp
@@ -0,0 +1,394 @@
+/*
+ * kcmlisa.cpp
+ *
+ * Copyright (c) 2000,2001 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "kcmlisa.h"
+
+#include "findnic.h"
+#include "setupwizard.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <time.h>
+
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qpushbutton.h>
+#include <qgrid.h>
+#include <qvbuttongroup.h>
+
+#include <kapplication.h>
+#include <kprocess.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <keditlistbox.h>
+#include <krestrictedline.h>
+#include <kdialogbase.h>
+#include <ktempfile.h>
+
+#include <kdebug.h>
+
+LisaSettings::LisaSettings(const QString& config, QWidget *parent)
+: KCModule(parent, "kcmlanbrowser")
+,m_config(config,false,true)
+,m_wizard(0)
+,m_configFilename(config)
+,m_changed(false)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ layout->setAutoAdd(true);
+
+ QWidget *dummy(0);
+
+ QVButtonGroup *gb=new QVButtonGroup(i18n("Tell LISa Daemon How to Search for Hosts"),this);
+ gb->setInsideSpacing(10);
+
+ m_useNmblookup=new QCheckBox(i18n("Send &NetBIOS broadcasts using nmblookup"), gb);
+ QToolTip::add(m_useNmblookup,i18n("Only hosts running SMB servers will answer"));
+
+ m_sendPings=new QCheckBox(i18n("Send &pings (ICMP echo packets)"), gb);
+ QToolTip::add(m_sendPings,i18n("All hosts running TCP/IP will answer"));
+
+ QHBox *hbox=new QHBox(gb);
+ hbox->setSpacing(10);
+
+ dummy=new QWidget(hbox);
+ dummy->setMinimumWidth(10);
+ QLabel *label=new QLabel(i18n("To these &IP addresses:"),hbox);
+ QString comment = i18n("Enter all ranges to scan, using the format '192.168.0.1/255.255.255.0;10.0.0.1;255.0.0.0'");
+ QToolTip::add(label,comment);
+ m_pingAddresses=new KRestrictedLine(hbox,"a","0123456789.-/;");
+ QToolTip::add(m_pingAddresses, comment);
+ label->setBuddy(m_pingAddresses);
+
+ QGrid *addressesGrid = new QGrid(2, Qt::Horizontal, this);
+ addressesGrid->setSpacing(10);
+ layout->setStretchFactor(addressesGrid,0);
+
+ label=new QLabel(i18n("&Broadcast network address:"),addressesGrid);
+ comment=i18n("Your network address/subnet mask (e.g. 192.168.0.0/255.255.255.0;)");
+ QToolTip::add(label, comment);
+
+ m_broadcastNetwork=new KRestrictedLine(addressesGrid,"a","0123456789./;");
+ QToolTip::add(m_broadcastNetwork,comment);
+ label->setBuddy(m_broadcastNetwork);
+
+ label=new QLabel(i18n("&Trusted IP addresses:"),addressesGrid);
+ comment = i18n("Usually your network address/subnet mask (e.g. 192.168.0.0/255.255.255.0;)");
+ QToolTip::add(label, comment);
+
+ m_allowedAddresses=new KRestrictedLine(addressesGrid,"a","0123456789./;");
+ QToolTip::add(m_allowedAddresses, comment);
+ label->setBuddy(m_allowedAddresses);
+
+ dummy=new QWidget(this);
+ layout->setStretchFactor(dummy,10);
+
+ hbox = new QHBox(this);
+ hbox->setSpacing(10);
+// m_autoSetup=new QPushButton(i18n("&Guided LISa Setup..."),hbox);
+ m_autoSetup=new QPushButton(i18n("Setup Wizard..."),hbox);
+ m_autoSetup->setFixedWidth( m_autoSetup->sizeHint().width() );
+
+ m_suggestSettings=new QPushButton(i18n("&Suggest Settings"),hbox);
+
+ new QWidget(hbox);
+
+ m_advancedSettingsButton=new QPushButton(i18n("Ad&vanced Settings..."),hbox);
+
+ m_lisaAdvancedDlg=new KDialogBase(0,0,true,i18n("Advanced Settings for LISa"),KDialogBase::Close, KDialogBase::Close);
+ connect(m_advancedSettingsButton,SIGNAL(clicked()),m_lisaAdvancedDlg,SLOT(show()));
+
+ QVBox *vbox=m_lisaAdvancedDlg->makeVBoxMainWidget();
+
+ m_pingNames=new KEditListBox(i18n("&Additionally Check These Hosts"),vbox,"a",false, KEditListBox::Add|KEditListBox::Remove);
+ m_pingNames->setMinimumHeight(180);
+ QToolTip::add(m_pingNames,i18n("The hosts listed here will be pinged"));
+
+ dummy=new QWidget(vbox);
+ dummy->setMinimumHeight(10);
+ m_deliverUnnamedHosts=new QCheckBox(i18n("Show &hosts without DNS names"),vbox);
+
+ QGrid *advGrid = new QGrid(2, Qt::Horizontal, vbox);
+ advGrid->setSpacing(10);
+
+ label=new QLabel(i18n("Host list update interval:"),advGrid);
+ QToolTip::add(label,i18n("Search hosts after this number of seconds"));
+ m_updatePeriod=new QSpinBox(30,1800,10,advGrid);
+ m_updatePeriod->setSuffix(i18n(" sec"));
+ QToolTip::add(m_updatePeriod,i18n("Search hosts after this number of seconds"));
+
+ m_secondScan=new QCheckBox(i18n("Always check twice for hosts when searching"),advGrid);
+ new QWidget(advGrid);
+
+ label=new QLabel(i18n("Wait for replies from hosts after first scan:"),advGrid);
+ QToolTip::add(label,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+ m_firstWait=new QSpinBox(10,1000,50,advGrid);
+ m_firstWait->setSuffix(i18n(" ms"));
+ QToolTip::add(m_firstWait,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+
+ label=new QLabel(i18n("Wait for replies from hosts after second scan:"),advGrid);
+ QToolTip::add(label,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+ m_secondWait=new QSpinBox(0,1000,50,advGrid);
+ m_secondWait->setSuffix(i18n(" ms"));
+ QToolTip::add(m_secondWait,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+
+ label=new QLabel(i18n("Max. number of ping packets to send at once:"),advGrid);
+ m_maxPingsAtOnce=new QSpinBox(8,1024,5,advGrid);
+
+ dummy=new QWidget(advGrid);
+ dummy->setMinimumHeight(10);
+
+ connect(m_secondScan,SIGNAL(toggled(bool)),m_secondWait,SLOT(setEnabled(bool)));
+ connect(m_sendPings,SIGNAL(toggled(bool)),m_pingAddresses,SLOT(setEnabled(bool)));
+
+ connect(m_pingAddresses,SIGNAL(textChanged(const QString&)),this,SIGNAL(changed()));
+ connect(m_allowedAddresses,SIGNAL(textChanged(const QString&)),this,SIGNAL(changed()));
+ connect(m_broadcastNetwork,SIGNAL(textChanged(const QString&)),this,SIGNAL(changed()));
+
+ connect(m_pingAddresses,SIGNAL(returnPressed()),this,SIGNAL(changed()));
+ connect(m_allowedAddresses,SIGNAL(returnPressed()),this,SIGNAL(changed()));
+ connect(m_broadcastNetwork,SIGNAL(returnPressed()),this,SIGNAL(changed()));
+
+ connect(m_sendPings,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_firstWait,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_secondWait,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_maxPingsAtOnce,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_secondScan,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_deliverUnnamedHosts,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_updatePeriod,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_pingNames,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_useNmblookup,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_autoSetup,SIGNAL(clicked()),this,SLOT(autoSetup()));
+ connect(m_suggestSettings,SIGNAL(clicked()),this,SLOT(suggestSettings()));
+ connect(this, SIGNAL(changed()), SLOT(slotChanged()));
+}
+
+void LisaSettings::load()
+{
+ int secondWait=m_config.readNumEntry("SecondWait",-1);
+ if (secondWait<0)
+ {
+ m_secondWait->setValue(300);
+ m_secondScan->setChecked(FALSE);
+ m_secondWait->setEnabled(FALSE);
+ }
+ else
+ {
+ m_secondWait->setValue(secondWait*10);
+ m_secondScan->setChecked(TRUE);
+ m_secondWait->setEnabled(TRUE);
+ }
+ m_deliverUnnamedHosts->setChecked(m_config.readNumEntry("DeliverUnnamedHosts",0));
+
+ m_firstWait->setValue(m_config.readNumEntry("FirstWait",30)*10);
+ m_maxPingsAtOnce->setValue(m_config.readNumEntry("MaxPingsAtOnce",256));
+ m_updatePeriod->setValue(m_config.readNumEntry("UpdatePeriod",300));
+ m_pingAddresses->setText(m_config.readEntry("PingAddresses","192.168.0.0/255.255.255.0;192.168.100.0-192.168.100.254"));
+ m_sendPings->setChecked(!m_pingAddresses->text().isEmpty());
+ m_allowedAddresses->setText(m_config.readEntry("AllowedAddresses","192.168.0.0/255.255.255.0"));
+ m_broadcastNetwork->setText(m_config.readEntry("BroadcastNetwork","192.168.0.0/255.255.255.0"));
+ m_pingNames->clear();
+ m_pingNames->insertStringList(m_config.readListEntry("PingNames",';'));
+ int i=m_config.readNumEntry("SearchUsingNmblookup",1);
+ m_useNmblookup->setChecked(i!=0);
+ m_changed = false;
+}
+
+void LisaSettings::save()
+{
+ if (!m_changed) return;
+
+ if ( getuid()==0)
+ {
+ if (m_secondScan->isChecked())
+ m_config.writeEntry("SecondWait",(m_secondWait->value()+5)/10);
+ else
+ m_config.writeEntry("SecondWait",-1);
+
+ if (m_useNmblookup->isChecked())
+ m_config.writeEntry("SearchUsingNmblookup",1);
+ else
+ m_config.writeEntry("SearchUsingNmblookup",0);
+
+ if (m_deliverUnnamedHosts->isChecked())
+ m_config.writeEntry("DeliverUnnamedHosts",1);
+ else
+ m_config.writeEntry("DeliverUnnamedHosts",0);
+
+ m_config.writeEntry("FirstWait",(m_firstWait->value()+5)/10);
+ m_config.writeEntry("MaxPingsAtOnce",m_maxPingsAtOnce->value());
+ m_config.writeEntry("UpdatePeriod",m_updatePeriod->value());
+ m_config.writeEntry("PingAddresses",m_sendPings->isChecked()?m_pingAddresses->text():"");
+ m_config.writeEntry("AllowedAddresses",m_allowedAddresses->text());
+ m_config.writeEntry("BroadcastNetwork",m_broadcastNetwork->text());
+ QStringList writeStuff;
+ for (int i=0; i<m_pingNames->count(); i++)
+ writeStuff.append(m_pingNames->text(i));
+ m_config.writeEntry("PingNames",writeStuff,';');
+
+ m_config.sync();
+ chmod(QFile::encodeName(m_configFilename),S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ }
+ else
+ {
+ //ok, now it gets harder
+ //we are not root but we want to write into /etc ....
+ //any idea how to do it better ?
+ KTempFile tmp;
+
+ if (tmp.status() == 0 && tmp.textStream())
+ {
+ m_tmpFilename = tmp.name();
+ QTextStream &confStream = *(tmp.textStream());
+ if (m_secondScan->isChecked())
+ confStream<<"SecondWait = "<<(m_secondWait->value()+5)/10<<"\n";
+ else
+ confStream<<"SecondWait = -1\n";
+
+ if (m_useNmblookup->isChecked())
+ confStream<<"SearchUsingNmblookup = 1\n";
+ else
+ confStream<<"SearchUsingNmblookup = 0\n";
+
+ if (m_deliverUnnamedHosts->isChecked())
+ confStream<<"DeliverUnnamedHosts = 1\n";
+ else
+ confStream<<"DeliverUnnamedHosts = 0\n";
+
+ confStream<<"FirstWait = "<< (m_firstWait->value()+5)/10 <<"\n";
+ confStream<<"MaxPingsAtOnce = "<<m_maxPingsAtOnce->value()<<"\n";
+ confStream<<"UpdatePeriod = "<<m_updatePeriod->value()<<"\n";
+ confStream<<"PingAddresses = "<<m_pingAddresses->text().latin1()<<"\n";
+ confStream<<"AllowedAddresses = "<<m_allowedAddresses->text().latin1()<<"\n";
+ confStream<<"BroadcastNetwork = "<<m_broadcastNetwork->text().latin1()<<"\n";
+ QString writeStuff;
+ for (int i=0; i<m_pingNames->count(); i++)
+ writeStuff=writeStuff+m_pingNames->text(i).latin1()+";";
+
+ confStream<<"PingNames = "<<writeStuff.latin1()<<"\n";
+ tmp.close();
+ QString suCommand=QString("cp '%1' '%2'; chmod 644 '%3'; rm -f '%4'").arg(m_tmpFilename).arg(m_configFilename).arg(m_configFilename).arg(m_tmpFilename);
+ KProcess *proc = new KProcess();
+ connect(proc, SIGNAL(processExited(KProcess *)), this, SLOT(saveDone(KProcess *)));
+ *proc<<"kdesu"<<"-c"<<suCommand;
+ KApplication::setOverrideCursor(Qt::waitCursor);
+ setEnabled(false);
+ if ( !proc->start() )
+ delete proc;
+ }
+ else
+ KMessageBox::sorry(0,i18n("Saving the results to %1 failed.").arg(m_configFilename));
+ }
+}
+
+void LisaSettings::suggestSettings()
+{
+ NICList *nics=findNICs();
+ if (nics->count()==0)
+ {
+ KMessageBox::sorry(0,i18n("No network interface cards found."));
+ delete nics;
+ return;
+ }
+ MyNIC *nic=nics->first();
+ LisaConfigInfo lci;
+ suggestSettingsForNic(nic,lci);
+ m_pingAddresses->setText(lci.pingAddresses);
+ m_sendPings->setChecked(!m_pingAddresses->text().isEmpty());
+ m_broadcastNetwork->setText(lci.broadcastNetwork);
+ m_allowedAddresses->setText(lci.allowedAddresses);
+ m_secondWait->setValue(lci.secondWait*10);
+ m_secondScan->setChecked(lci.secondScan);
+ m_secondWait->setEnabled(lci.secondScan);
+ m_firstWait->setValue(lci.firstWait*10);
+ m_maxPingsAtOnce->setValue(lci.maxPingsAtOnce);
+ m_updatePeriod->setValue(lci.updatePeriod);
+ m_useNmblookup->setChecked(lci.useNmblookup);
+ m_deliverUnnamedHosts->setChecked(lci.unnamedHosts);
+
+ if (nics->count()>1)
+ {
+ QString msg(i18n("You have more than one network interface installed.<br>"
+ "Please make sure the suggested settings are correct.<br>"
+ "<br>The following interfaces were found:<br><br>"));
+ //not that easy to handle
+ for (MyNIC* tmp=nics->first(); tmp!=0; tmp=nics->next())
+ {
+ msg+="<b>"+tmp->name+": </b>"+tmp->addr+"/"+tmp->netmask+";<br>";
+ }
+ KMessageBox::information(0,QString("<html>%1</html>").arg(msg));
+ }
+
+ emit changed();
+ delete nics;
+}
+
+void LisaSettings::autoSetup()
+{
+ LisaConfigInfo lci;
+ if (m_wizard==0)
+ m_wizard=new SetupWizard(this,&lci);
+ else
+ m_wizard->clearAll();
+ int result=m_wizard->exec();
+
+ if (result!=QDialog::Accepted)
+ return;
+
+ m_pingAddresses->setText(lci.pingAddresses);
+ m_sendPings->setChecked(!m_pingAddresses->text().isEmpty());
+ m_broadcastNetwork->setText(lci.broadcastNetwork);
+ m_allowedAddresses->setText(lci.allowedAddresses);
+ m_secondWait->setValue(lci.secondWait*10);
+ m_secondScan->setChecked(lci.secondScan);
+ m_secondWait->setEnabled(lci.secondScan);
+ m_firstWait->setValue(lci.firstWait*10);
+ m_maxPingsAtOnce->setValue(lci.maxPingsAtOnce);
+ m_updatePeriod->setValue(lci.updatePeriod);
+ m_useNmblookup->setChecked(lci.useNmblookup);
+ m_deliverUnnamedHosts->setChecked(lci.unnamedHosts);
+
+ emit changed();
+ return;
+}
+
+void LisaSettings::saveDone(KProcess *proc)
+{
+ unlink(QFile::encodeName(m_tmpFilename));
+ KApplication::restoreOverrideCursor();
+ setEnabled(true);
+ KMessageBox::information(0,i18n("The configuration has been saved to /etc/lisarc.\n"
+ "Make sure that the LISa daemon is started,\n e.g. using an init script when booting.\n"
+ "You can find examples and documentation at http://lisa-home.sourceforge.net ."));
+
+ delete(proc);
+}
+
+void LisaSettings::slotChanged()
+{
+ m_changed = true;
+}
+
+#include "kcmlisa.moc"
+
diff --git a/lanbrowsing/kcmlisa/kcmlisa.desktop b/lanbrowsing/kcmlisa/kcmlisa.desktop
new file mode 100644
index 00000000..f995a9cf
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmlisa.desktop
@@ -0,0 +1,122 @@
+[Desktop Entry]
+Type=Application
+Icon=samba
+Exec=kcmshell kcmlisa
+
+X-KDE-ModuleType=Library
+X-KDE-Library=lanbrowser
+X-KDE-FactoryName=lisa
+
+Name=LISa
+Name[hi]=लिसा
+Name[sv]=Lisa
+Name[th]=ลิซา
+
+Comment=Setup LISa
+Comment[ar]=تنصيب LISa
+Comment[be]=ÐаÑтаўленны LISÑ‹
+Comment[bg]=ÐаÑтройване на LISa
+Comment[bn]=LISa বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦¨à¦¾
+Comment[br]=Kefluniañ LISa
+Comment[bs]=Podešavanje LISe
+Comment[ca]=Per l'arranjament de LISa
+Comment[cs]=Nastavit LISa
+Comment[cy]=Gosod LISa
+Comment[da]=Opsætning af LISa
+Comment[de]=LISa einrichten
+Comment[el]=ΡÏθμιση LISa
+Comment[eo]=Agordo de LISa
+Comment[es]=Configuración LISa
+Comment[et]=LISa seadistamine
+Comment[eu]=LISa konfiguratu
+Comment[fa]=برپایی LISa
+Comment[fi]=Aseta LISa
+Comment[fr]=Configuration de LISa
+Comment[ga]=Cumraigh LISa
+Comment[gl]=Configuración de LISa
+Comment[he]=שינוי הגדרות LISa
+Comment[hi]=लिसा सेटअप
+Comment[hr]=Podešavanje LISa-e
+Comment[hu]=A LISa szolgáltatás beállítása
+Comment[is]=Stillingar LISa
+Comment[it]=Impostazioni LISa
+Comment[ja]=LISa ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
+Comment[ka]=LISa-ს გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=LISa баптауы
+Comment[km]=រៀបចំ LISa
+Comment[lt]=Nustatyti LISa
+Comment[mk]=ПоÑтавување на LISa
+Comment[mn]=LISa тохируулга
+Comment[ms]=Pasang LISa
+Comment[nb]=Tilpass LISa
+Comment[nds]=LISa instellen
+Comment[ne]=LISa सेटअप गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=LISa instellen
+Comment[nn]=LISa-oppsett
+Comment[nso]=Beakanya LISa
+Comment[pl]=Ustawienia programu LISa
+Comment[pt]=Configuração do LISa
+Comment[pt_BR]=Configuração de LISa
+Comment[ru]=Параметры LISa
+Comment[se]=Heivet LISa
+Comment[sk]=Nastavenie LISy
+Comment[sl]=Nastavi LISa
+Comment[sr]=ПодеÑите LISa-у
+Comment[sr@Latn]=Podesite LISa-u
+Comment[sv]=Anpassa Lisa
+Comment[ta]=LISa அமைபà¯à®ªà¯à®•à®³à¯
+Comment[tg]=БарпоÑозии LISa
+Comment[th]=ตั้งค่าลิซา
+Comment[tr]=LISa'yı yapılandır
+Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ LISa
+Comment[uz]=LISa'ni oʻrnatish
+Comment[uz@cyrillic]=LISa'ни ўрнатиш
+Comment[ven]=Vhekanyani LISa
+Comment[wa]=Apontiaedje di LISa
+Comment[xh]=Cwangcis i LISa
+Comment[zh_CN]=设置 LISa
+Comment[zh_HK]=設定 LISa
+Comment[zh_TW]=設定 LISa
+Comment[zu]=Lungisa i-LISa
+
+Keywords=lisa,network,smb,ftp,fish,http
+Keywords[be]=Ñетка,lisa,network,smb,ftp,fish,http
+Keywords[bg]=мрежа, Ñамба, наÑтройки, локална, ÑподелÑне, lisa, network, smb, ftp, fish, http
+Keywords[br]=lisa,rouedad,smb,ftp,fish,http
+Keywords[ca]=lisa,xarxa,smb,ftp,fish,http
+Keywords[cs]=lisa,síť,smb,ftp,fish,http
+Keywords[cy]=lisa,rhwydwaith,smb,ftp,fish,http
+Keywords[da]=lisa,netværk,smb,ftp,fish,http
+Keywords[de]=lisa,Netzwerk,smb,ftp,fish,http
+Keywords[el]=lisa,δίκτυο,smb,ftp,fish,http
+Keywords[et]=lisa,võrk,smb,ftp,fish,http
+Keywords[eu]=lisa,sarea,smb,ftp,fish,http
+Keywords[fa]=lisa، شبکه، smb، قاپ، fish، قام
+Keywords[fi]=lisa,verkko,smb,ftp,fish,http
+Keywords[fr]=lisa,réseau,smb,ftp,fish,http
+Keywords[gl]=lisa,rede,smb,ftp,fish,http
+Keywords[he]=lisa,רשת,smb,ftp,fish,http
+Keywords[hi]=लिसा,नेटवरà¥à¤•,à¤à¤¸à¤à¤®à¤¬à¥€,à¤à¤«à¤Ÿà¥€à¤ªà¥€,फिश,à¤à¤šà¤Ÿà¥€à¤Ÿà¥€à¤ªà¥€
+Keywords[hu]=lisa,hálózat,smb,ftp,fish,http
+Keywords[it]=lisa,rete,smb,ftp,fish,http
+Keywords[ja]=lisa,ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯,smb,ftp,fish,http
+Keywords[km]=lisa,បណ្ដាញ,smb,ftp,fish,http
+Keywords[lt]=lisa,network,smb,ftp,fish,http,tinklas
+Keywords[mk]=lisa,network,smb,ftp,fish,http,мрежа
+Keywords[nb]=lisa,nettverk,smb,ftp,fish,http
+Keywords[nds]=lisa,nettwark,smb,ftp,fish,http
+Keywords[ne]=लिसा,सञà¥à¤œà¤¾à¤²,smb,ftp,fish,http
+Keywords[nl]=lisa,netwerk,smb,ftp,fish,http,samba
+Keywords[nn]=lisa,nettverk,smb,ftp,fish,http
+Keywords[pl]=lisa,sieć,smb,ftp,fish,http
+Keywords[pt]=lisa,rede,smb,ftp,fish,http
+Keywords[pt_BR]=lisa,rede,smb,ftp,fish,http
+Keywords[ru]=lisa,Ñеть,smb,ftp,fish,http
+Keywords[sk]=lisa,sieť,smb,ftp,fish,http
+Keywords[sl]=lisa,omrežje,smb,ftp,fish,http
+Keywords[sv]=lisa,nätverk,smb,ftp,fish,http
+Keywords[tr]=lisa,aÄŸ,smb,ftp,fish,http
+Keywords[uk]=lisa,мережа,smb,ftp,fish,http
+Keywords[uz]=lisa,tarmoq,smb,ftp,fish,http
+Keywords[uz@cyrillic]=lisa,тармоқ,smb,ftp,fish,http
+Keywords[zh_CN]=lisa,network,smb,ftp,fish,http,网络
diff --git a/lanbrowsing/kcmlisa/kcmlisa.h b/lanbrowsing/kcmlisa/kcmlisa.h
new file mode 100644
index 00000000..fcda0a89
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmlisa.h
@@ -0,0 +1,79 @@
+/*
+ * kcmlisa.h
+ *
+ * Copyright (c) 2000-2002 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KCMLISA_H
+#define KCMLISA_H
+
+#include <kconfig.h>
+#include <kcmodule.h>
+
+class QPushButton;
+class QCheckBox;
+class QSpinBox;
+class QPushButton;
+class KProcess;
+class KDialogBase;
+class KRestrictedLine;
+class KEditListBox;
+class SetupWizard;
+
+class LisaSettings:public KCModule
+{
+ Q_OBJECT
+ public:
+ LisaSettings(const QString& config, QWidget *parent=0);
+ virtual ~LisaSettings() {};
+ void load();
+ void save();
+ signals:
+ void changed();
+ protected slots:
+ void slotChanged();
+ void autoSetup();
+ void saveDone(KProcess *); // called after the kdesud returns (on save)
+ void suggestSettings();
+ protected:
+ KConfig m_config;
+ QPushButton *m_autoSetup;
+ QCheckBox *m_useNmblookup;
+ QCheckBox *m_sendPings;
+ KRestrictedLine *m_pingAddresses;
+ KEditListBox *m_pingNames;
+ KRestrictedLine *m_allowedAddresses;
+ KRestrictedLine *m_broadcastNetwork;
+ QSpinBox *m_firstWait;
+ QCheckBox *m_secondScan;
+ QSpinBox *m_secondWait;
+ QSpinBox *m_updatePeriod;
+ QCheckBox *m_deliverUnnamedHosts;
+ QSpinBox *m_maxPingsAtOnce;
+ QPushButton* m_suggestSettings;
+ QPushButton *m_advancedSettingsButton;
+ KDialogBase *m_lisaAdvancedDlg;
+
+ SetupWizard *m_wizard;
+ private:
+ QString m_tmpFilename;
+ QString m_configFilename;
+ bool m_changed;
+};
+
+#endif
+
diff --git a/lanbrowsing/kcmlisa/kcmreslisa.cpp b/lanbrowsing/kcmlisa/kcmreslisa.cpp
new file mode 100644
index 00000000..c4f14d1a
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmreslisa.cpp
@@ -0,0 +1,250 @@
+/*
+ * kcmreslisa.cpp
+ *
+ * Copyright (c) 2000-2002 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include "kcmreslisa.h"
+#include "findnic.h"
+
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <qvbox.h>
+#include <qpushbutton.h>
+#include <qgrid.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+#include <qlayout.h>
+#include <qvbuttongroup.h>
+
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <krestrictedline.h>
+#include <keditlistbox.h>
+
+ResLisaSettings::ResLisaSettings(const QString& config, QWidget *parent)
+: KCModule(parent)
+,m_config(config,false,true)
+,m_kiolanConfig("kio_lanrc",false,true)
+,m_advancedSettingsButton(0)
+,m_suggestSettings(0)
+,m_useNmblookup(0)
+,m_pingNames(0)
+,m_allowedAddresses(0)
+,m_firstWait(0)
+,m_secondScan(0)
+,m_secondWait(0)
+,m_updatePeriod(0)
+,m_deliverUnnamedHosts(0)
+,m_maxPingsAtOnce(0)
+,m_reslisaAdvancedDlg(0)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this, KDialog::marginHint(), KDialog::spacingHint());
+ layout->setAutoAdd(true);
+
+ QVButtonGroup *gb=new QVButtonGroup(i18n("Tell ResLISa Daemon How to Search for Hosts"),this);
+ gb->setInsideSpacing(10);
+
+ m_useNmblookup=new QCheckBox(i18n("Send &NetBIOS broadcasts using &nmblookup"),gb);
+ QToolTip::add(m_useNmblookup,i18n("Only hosts running SMB servers will answer"));
+
+ m_pingNames=new KEditListBox(i18n("A&dditionally Check These Hosts"),gb,"a",false, KEditListBox::Add|KEditListBox::Remove);
+ QToolTip::add(m_pingNames,i18n("The hosts listed here will be pinged."));
+
+ QHBox *hbox=new QHBox(this);
+ hbox->setSpacing(10);
+ QLabel *label=new QLabel(i18n("&Trusted addresses:"),hbox);
+ QString comment = i18n("Usually your network address/subnet mask (e.g. 192.168.0.0/255.255.255.0;)");
+ QToolTip::add(label, comment);
+ m_allowedAddresses=new KRestrictedLine(hbox,"a","0123456789./;");
+ QToolTip::add(m_allowedAddresses,comment);
+ label->setBuddy(m_allowedAddresses);
+
+ m_rlanSidebar = new QCheckBox(i18n("Use &rlan:/ instead of lan:/ in Konqueror's navigation panel"), this);
+
+ hbox = new QHBox(this);
+ m_suggestSettings=new QPushButton(i18n("&Suggest Settings"),hbox);
+
+ new QWidget(hbox);
+
+ m_advancedSettingsButton=new QPushButton(i18n("Ad&vanced Settings"),hbox);
+
+
+ m_reslisaAdvancedDlg=new KDialogBase(0,0,true,i18n("Advanced Settings for ResLISa"),KDialogBase::Close, KDialogBase::Close);
+ connect(m_advancedSettingsButton,SIGNAL(clicked()),m_reslisaAdvancedDlg,SLOT(show()));
+
+ QVBox *vbox=m_reslisaAdvancedDlg->makeVBoxMainWidget();
+
+ m_deliverUnnamedHosts=new QCheckBox(i18n("Show &hosts without DNS names"),vbox);
+
+ QGrid *advGrid = new QGrid(2, Qt::Horizontal, vbox);
+ advGrid->setSpacing(10);
+
+ label=new QLabel(i18n("Host list update interval:"),advGrid);
+ QToolTip::add(label,i18n("Search hosts after this number of seconds"));
+ m_updatePeriod=new QSpinBox(30,1800,10,advGrid);
+ m_updatePeriod->setSuffix(i18n(" sec"));
+ QToolTip::add(m_updatePeriod,i18n("Search hosts after this number of seconds"));
+
+ m_secondScan=new QCheckBox(i18n("Always check twice for hosts when searching"),advGrid);
+ new QWidget(advGrid);
+
+ label=new QLabel(i18n("Wait for replies from hosts after first scan:"),advGrid);
+ QToolTip::add(label,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+ m_firstWait=new QSpinBox(10,1000,50,advGrid);
+ m_firstWait->setSuffix(i18n(" ms"));
+ QToolTip::add(m_firstWait,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+
+ label=new QLabel(i18n("Wait for replies from hosts after second scan:"),advGrid);
+ QToolTip::add(label,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+ m_secondWait=new QSpinBox(0,1000,50,advGrid);
+ m_secondWait->setSuffix(i18n(" ms"));
+ QToolTip::add(m_secondWait,i18n("How long to wait for replies to the ICMP echo requests from hosts"));
+
+ label=new QLabel(i18n("Max. number of ping packets to send at once:"),advGrid);
+ m_maxPingsAtOnce=new QSpinBox(8,1024,5,advGrid);
+
+ QWidget *dummy=new QWidget(advGrid);
+ dummy->setMinimumHeight(10);
+
+ connect(m_secondScan,SIGNAL(toggled(bool)),m_secondWait,SLOT(setEnabled(bool)));
+
+ connect(m_allowedAddresses,SIGNAL(textChanged(const QString&)),this,SIGNAL(changed()));
+
+ connect(m_allowedAddresses,SIGNAL(returnPressed()),this,SIGNAL(changed()));
+
+ connect(m_firstWait,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_secondWait,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_maxPingsAtOnce,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_secondScan,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_deliverUnnamedHosts,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_updatePeriod,SIGNAL(valueChanged(int)),this,SIGNAL(changed()));
+ connect(m_pingNames,SIGNAL(changed()),this,SIGNAL(changed()));
+ connect(m_useNmblookup,SIGNAL(toggled(bool)),this,SIGNAL(changed()));
+ connect(m_suggestSettings,SIGNAL(clicked()),this,SLOT(suggestSettings()));
+ connect(m_rlanSidebar,SIGNAL(clicked()),this,SIGNAL(changed()));
+
+ load();
+}
+
+void ResLisaSettings::load()
+{
+ int secondWait=m_config.readNumEntry("SecondWait",-1);
+ if (secondWait<0)
+ {
+ m_secondWait->setValue(300);
+ m_secondScan->setChecked(FALSE);
+ m_secondWait->setEnabled(FALSE);
+ }
+ else
+ {
+ m_secondWait->setValue(secondWait*10);
+ m_secondScan->setChecked(TRUE);
+ m_secondWait->setEnabled(TRUE);
+ };
+ m_deliverUnnamedHosts->setChecked(m_config.readNumEntry("DeliverUnnamedHosts",0));
+
+ m_firstWait->setValue(m_config.readNumEntry("FirstWait",30)*10);
+ m_maxPingsAtOnce->setValue(m_config.readNumEntry("MaxPingsAtOnce",256));
+ m_updatePeriod->setValue(m_config.readNumEntry("UpdatePeriod",300));
+ m_allowedAddresses->setText(m_config.readEntry("AllowedAddresses","192.168.0.0/255.255.255.0"));
+
+ int i=m_config.readNumEntry("SearchUsingNmblookup",1);
+ m_useNmblookup->setChecked(i!=0);
+ m_pingNames->clear();
+ m_pingNames->insertStringList(m_config.readListEntry("PingNames",';'));
+
+ m_rlanSidebar->setChecked(m_kiolanConfig.readEntry("sidebarURL", "lan:/") == "rlan:/" ? true : false );
+}
+
+void ResLisaSettings::save()
+{
+ if (m_secondScan->isChecked())
+ m_config.writeEntry("SecondWait",(m_secondWait->value()+5)/10);
+ else
+ m_config.writeEntry("SecondWait",-1);
+
+ if (m_useNmblookup->isChecked())
+ m_config.writeEntry("SearchUsingNmblookup",1);
+ else
+ m_config.writeEntry("SearchUsingNmblookup",0);
+
+ if (m_deliverUnnamedHosts->isChecked())
+ m_config.writeEntry("DeliverUnnamedHosts",1);
+ else
+ m_config.writeEntry("DeliverUnnamedHosts",0);
+
+ m_config.writeEntry("FirstWait",(m_firstWait->value()+5)/10);
+ m_config.writeEntry("MaxPingsAtOnce",m_maxPingsAtOnce->value());
+ m_config.writeEntry("UpdatePeriod",m_updatePeriod->value());
+ m_config.writeEntry("AllowedAddresses",m_allowedAddresses->text());
+ //m_config.writeEntry("BroadcastNetwork",m_broadcastNetwork->text());
+ QStringList writeStuff;
+ for (int i=0; i<m_pingNames->count(); i++)
+ writeStuff.append(m_pingNames->text(i));
+ m_config.writeEntry("PingNames",writeStuff,';');
+
+ m_config.sync();
+
+ m_kiolanConfig.writeEntry("sidebarURL", m_rlanSidebar->isChecked() ? "rlan:/" : "lan:/");
+ m_kiolanConfig.sync();
+
+}
+
+void ResLisaSettings::suggestSettings()
+{
+ NICList* nics=findNICs();
+ if (nics->count()==0)
+ {
+ //ok, easy one :-)
+ KMessageBox::sorry(0,i18n("It appears you do not have any network interfaces installed on your system."));
+ delete nics;
+ return;
+ }
+
+ MyNIC *nic=nics->first();
+ QString address = nic->addr;
+ QString netmask = nic->netmask;
+ m_allowedAddresses->setText(address+"/"+netmask+";");
+ m_secondWait->setValue(0);
+ m_secondScan->setChecked(FALSE);
+ m_secondWait->setEnabled(FALSE);
+ m_firstWait->setValue(300);
+ m_maxPingsAtOnce->setValue(256);
+ m_updatePeriod->setValue(300);
+ m_useNmblookup->setChecked(true);
+ if (nics->count()>1)
+ {
+ QString msg(i18n("You have more than one network interface installed.<br>"
+ "Please make sure the suggested settings are correct.<br>"
+ "<br>The following interfaces were found:<br><br>"));
+ //not that easy to handle
+ for (MyNIC* tmp=nics->first(); tmp!=0; tmp=nics->next())
+ {
+ msg+="<b>"+tmp->name+": </b>"+tmp->addr+"/"+tmp->netmask+";<br>";
+ }
+ KMessageBox::information(0,QString("<html>%1</html>").arg(msg));
+ }
+ KMessageBox::information(0,QString("<html>%1</html>").arg(i18n("The ResLISa daemon is now configured "
+ "correctly, hopefully.<br>Make sure that the reslisa binary is installed <i>suid root</i>.")));
+
+ emit changed();
+ delete nics;
+}
+
+#include "kcmreslisa.moc"
+
diff --git a/lanbrowsing/kcmlisa/kcmreslisa.desktop b/lanbrowsing/kcmlisa/kcmreslisa.desktop
new file mode 100644
index 00000000..47d54652
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmreslisa.desktop
@@ -0,0 +1,123 @@
+[Desktop Entry]
+Type=Application
+Icon=samba
+Exec=kcmshell kcmreslisa
+
+X-KDE-ModuleType=Library
+X-KDE-Library=lanbrowser
+X-KDE-FactoryName=reslisa
+
+Name=ResLISa
+Name[hi]=रेस-लिसा
+Name[sv]=ResLisa
+Name[zh_TW]=ResLISaName=ResLISa
+
+Comment=Setup ResLISa
+Comment[ar]=تنصيب ResLISa
+Comment[be]=ÐаÑтаўленне ResLISÑ‹
+Comment[bg]=ÐаÑтройване на ResLISa
+Comment[bn]=ResLISa বà§à¦¯à¦¬à¦¸à§à¦¥à¦¾à¦ªà¦¨à¦¾
+Comment[br]=Kefluniañ ResLISa
+Comment[bs]=Podešavanje ResLISe
+Comment[ca]=Per l'arranjament de ResLISa
+Comment[cs]=Nastavit ResLISa
+Comment[cy]=Gosod ResLISa
+Comment[da]=Opsætning af ResLISa
+Comment[de]=ResLISa einrichten
+Comment[el]=ΡÏθμιση ResLISa
+Comment[eo]=Agordo de ResLISa
+Comment[es]=Configuración ResLISa
+Comment[et]=ResLISa seadistamine
+Comment[eu]=ResLISa konfiguratu
+Comment[fa]=برپایی ResLISa
+Comment[fi]=Aseta ResLISa
+Comment[fr]=Configuration de ResLISa
+Comment[ga]=Cumraigh ResLISa
+Comment[gl]=Configuración de ResLISa
+Comment[he]=שינוי הגדרות ResLISa
+Comment[hi]=सेटअप रेस-लिसा
+Comment[hr]=Podešavanje ResLISa-e
+Comment[hu]=A ResLISa szolgáltatás beállítása
+Comment[is]=Stillingar ResLISa
+Comment[it]=Impostazioni ResLISa
+Comment[ja]=ResLISa ã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—
+Comment[ka]=ResLISa გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=ResLISa баптауы
+Comment[km]=រៀបចំ ResLISa
+Comment[lt]=Nustatyti ResLISa
+Comment[mk]=ПоÑтавување на ResLISa
+Comment[mn]=ResLISa тохируулга
+Comment[ms]=Pasang ResLISa
+Comment[nb]=Tilpass ResLISa
+Comment[nds]=ResLISa instellen
+Comment[ne]=ResLIsa सेटअप गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=ResLISa instellen
+Comment[nn]=ResLISa-oppsett
+Comment[nso]=Beakanya ResLISa
+Comment[pl]=Ustawienia programu ResLISa
+Comment[pt]=Configuração do ResLISa
+Comment[pt_BR]=Configuração de ResLISa
+Comment[ru]=Параметры ResLISa
+Comment[se]=Heivet ResLISa
+Comment[sk]=Nastavenie ResLISy
+Comment[sl]=Nastavi ResLISa
+Comment[sr]=ПодеÑите ResLISa-у
+Comment[sr@Latn]=Podesite ResLISa-u
+Comment[sv]=Anpassa ResLisa
+Comment[ta]=ResLISa அமைபà¯à®ªà¯
+Comment[tg]=БарпоÑозии ResLISa
+Comment[th]=ตั้งค่า ResLISa
+Comment[tr]=ResLISa'yı yapılandır
+Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ResLISa
+Comment[uz]=ResLISa'ni oʻrnatish
+Comment[uz@cyrillic]=ResLISa'ни ўрнатиш
+Comment[ven]=Vhekanyani ResLISa
+Comment[wa]=Apontiaedje di ResLISa
+Comment[xh]=Cwangcisa ResLISa
+Comment[zh_CN]=设置 ResLISa
+Comment[zh_HK]=設定 ResLISa
+Comment[zh_TW]=設定 ResLISa
+Comment[zu]=Lungisa i-ResLISa
+
+
+Keywords=reslisa,network,smb,ftp,fish,http
+Keywords[be]=Ñетка,reslisa,network,smb,ftp,fish,http
+Keywords[bg]=мрежа, Ñамба, наÑтройки, локална, ÑподелÑне, reslisa, network, smb, ftp, fish, http
+Keywords[br]=reslisa,rouedad,smb,ftp,fish,http
+Keywords[ca]=reslisa,xarxa,smb,ftp,fish,http
+Keywords[cs]=reslisa,síť,smb,ftp,fish,http
+Keywords[cy]=reslisa,rhwydwaith,smb,ftp,fish,http
+Keywords[da]=reslisa,netværk,smb,ftp,fish,http
+Keywords[de]=reslisa,Netzwerk,smb,ftp,fish,http
+Keywords[el]=reslisa,δίκτυο,smb,ftp,fish,http
+Keywords[et]=reslisa,võrk,smb,ftp,fish,http
+Keywords[eu]=reslisa,sarea,smb,ftp,fish,http
+Keywords[fa]=reslisa، شبکه، smb، قاپ، fish، قام
+Keywords[fi]=reslisa,verkko,smb,ftp,fish,http
+Keywords[fr]=reslisa,réseau,smb,ftp,fish,http
+Keywords[gl]=reslisa,redes,smb,ftp,fish,http
+Keywords[he]=reslisa,רשת,smb,ftp,fish,http
+Keywords[hi]=रेसलिसा,नेटवरà¥à¤•,à¤à¤¸à¤à¤®à¤¬à¥€,à¤à¤«à¤Ÿà¥€à¤ªà¥€,फिश,à¤à¤šà¤Ÿà¥€à¤Ÿà¥€à¤ªà¥€
+Keywords[hu]=reslisa,hálózat,smb,ftp,fish,http
+Keywords[it]=reslisa,rete,smb,ftp,fish,http
+Keywords[ja]=reslisa,ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯,smb,ftp,fish,http
+Keywords[km]=reslisa,បណ្ដាញ,smb,ftp,fish,http
+Keywords[lt]=reslisa,network,smb,ftp,fish,http,tinklas
+Keywords[mk]=reslisa,network,smb,ftp,fish,http,мрежа
+Keywords[nb]=reslisa,nettverk,smb,ftp,fish,http
+Keywords[nds]=reslisa,nettwark,smb,ftp,fish,http
+Keywords[ne]=रेसलिसा,सञà¥à¤œà¤¾à¤²,smb,ftp,fish,http
+Keywords[nl]=reslisa,netwerk,smb,ftp,fish,http,samba
+Keywords[nn]=reslisa,nettverk,smb,ftp,fish,http
+Keywords[pl]=reslisa,sieć,smb,ftp,fish,http
+Keywords[pt]=reslisa,rede,smb,ftp,fish,http
+Keywords[pt_BR]=reslisa,rede,smb,ftp,fish,http
+Keywords[ru]=reslisa,Ñеть,smb,ftp,fish,http
+Keywords[sk]=reslisa,sieť,smb,ftp,fish,http
+Keywords[sl]=reslisa,omrežje,smb,ftp,fish,http
+Keywords[sv]=reslisa,nätverk,smb,ftp,fish,http
+Keywords[tr]=reslisa,aÄŸ,smb,ftp,fish,http
+Keywords[uk]=reslisa,мережа,smb,ftp,fish,http
+Keywords[uz]=reslisa,tarmoq,smb,ftp,fish,http
+Keywords[uz@cyrillic]=reslisa,тармоқ,smb,ftp,fish,http
+Keywords[zh_CN]=reslisa,network,smb,ftp,fish,http,网络
diff --git a/lanbrowsing/kcmlisa/kcmreslisa.h b/lanbrowsing/kcmlisa/kcmreslisa.h
new file mode 100644
index 00000000..ee8734df
--- /dev/null
+++ b/lanbrowsing/kcmlisa/kcmreslisa.h
@@ -0,0 +1,67 @@
+/*
+ * kcmreslisa.h
+ *
+ * Copyright (c) 2000-2002 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef KCMRESLISA_H
+#define KCMRESLISA_H
+
+#include <kcmodule.h>
+#include <kconfig.h>
+
+
+class QSpinBox;
+class QCheckBox;
+class QPushButton;
+class KEditListBox;
+class KDialogBase;
+class KRestrictedLine;
+
+class ResLisaSettings:public KCModule
+{
+ Q_OBJECT
+ public:
+ ResLisaSettings(const QString& config, QWidget *parent=0);
+ virtual ~ResLisaSettings() {}
+ void load();
+ void save();
+ signals:
+ void changed();
+ protected slots:
+ void suggestSettings();
+ protected:
+ KConfig m_config;
+ KConfig m_kiolanConfig;
+ QPushButton* m_advancedSettingsButton;
+ QPushButton* m_suggestSettings;
+ QCheckBox* m_useNmblookup;
+ KEditListBox *m_pingNames;
+
+ KRestrictedLine *m_allowedAddresses;
+ QSpinBox *m_firstWait;
+ QCheckBox *m_secondScan;
+ QSpinBox *m_secondWait;
+ QSpinBox *m_updatePeriod;
+ QCheckBox *m_deliverUnnamedHosts;
+ QSpinBox *m_maxPingsAtOnce;
+ QCheckBox* m_rlanSidebar;
+ KDialogBase* m_reslisaAdvancedDlg;
+};
+
+#endif
+
diff --git a/lanbrowsing/kcmlisa/main.cpp b/lanbrowsing/kcmlisa/main.cpp
new file mode 100644
index 00000000..c9d9b6b2
--- /dev/null
+++ b/lanbrowsing/kcmlisa/main.cpp
@@ -0,0 +1,49 @@
+/*
+ * main.cpp for lisa,reslisa,kio_lan and kio_rlan kcm module
+ *
+ * Copyright (C) 2000,2001,2005 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <qdir.h>
+#include <qdatetime.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "kcmlisa.h"
+#include "kcmreslisa.h"
+#include "kcmkiolan.h"
+
+extern "C"
+{
+ KDE_EXPORT KCModule *create_lisa(QWidget *parent, const char *)
+ {
+ return new LisaSettings("/etc/lisarc", parent);
+ }
+ KDE_EXPORT KCModule *create_reslisa(QWidget *parent, const char *)
+ {
+ return new ResLisaSettings(QDir::homeDirPath()+"/.reslisarc", parent);
+ }
+ KDE_EXPORT KCModule *create_kiolan(QWidget *parent, const char *)
+ {
+ return new IOSlaveSettings("kio_lanrc", parent);
+ }
+}
+
diff --git a/lanbrowsing/kcmlisa/portsettingsbar.cpp b/lanbrowsing/kcmlisa/portsettingsbar.cpp
new file mode 100644
index 00000000..a6bc94d9
--- /dev/null
+++ b/lanbrowsing/kcmlisa/portsettingsbar.cpp
@@ -0,0 +1,51 @@
+/*
+ * portsettingsbar.cpp
+ *
+ * Copyright (c) 2000, 2005 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "portsettingsbar.h"
+#include <klocale.h>
+
+#include <qlabel.h>
+
+PortSettingsBar::PortSettingsBar(const QString& title, QWidget *parent)
+:QHBox(parent)
+{
+ QLabel* label=new QLabel(title, this);
+ m_box=new QComboBox(this);
+ label->setBuddy(m_box);
+
+ m_box->insertItem(i18n("Check Availability"));
+ m_box->insertItem(i18n("Always"));
+ m_box->insertItem(i18n("Never"));
+
+ connect(m_box,SIGNAL(activated(int)),this,SIGNAL(changed()));
+}
+
+int PortSettingsBar::selected() const
+{
+ return m_box->currentItem();
+}
+
+void PortSettingsBar::setChecked(int what)
+{
+ m_box->setCurrentItem(what);
+}
+
+#include "portsettingsbar.moc"
+
diff --git a/lanbrowsing/kcmlisa/portsettingsbar.h b/lanbrowsing/kcmlisa/portsettingsbar.h
new file mode 100644
index 00000000..9cc92e02
--- /dev/null
+++ b/lanbrowsing/kcmlisa/portsettingsbar.h
@@ -0,0 +1,45 @@
+/*
+ * portsettingsbar.h
+ *
+ * Copyright (c) 2000, 2005 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PORTSETTINGSBAR_H
+#define PORTSETTINGSBAR_H
+
+#include <qhbox.h>
+#include <qcombobox.h>
+
+#define PORTSETTINGS_CHECK 0
+#define PORTSETTINGS_PROVIDE 1
+#define PORTSETTINGS_DISABLE 2
+
+class PortSettingsBar:public QHBox
+{
+ Q_OBJECT
+ public:
+ PortSettingsBar(const QString& title, QWidget *parent=0);
+ virtual ~PortSettingsBar() {};
+ int selected() const;
+ void setChecked(int what);
+ signals:
+ void changed();
+ protected:
+ QComboBox *m_box;
+};
+
+#endif
diff --git a/lanbrowsing/kcmlisa/setupwizard.cpp b/lanbrowsing/kcmlisa/setupwizard.cpp
new file mode 100644
index 00000000..7ff91f8f
--- /dev/null
+++ b/lanbrowsing/kcmlisa/setupwizard.cpp
@@ -0,0 +1,569 @@
+/*
+ * setupwizard.cpp
+ *
+ * Copyright (C) 2001 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "setupwizard.h"
+
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kdialog.h>
+#include <ksockaddr.h>
+#include <kdebug.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+SetupWizard::SetupWizard(QWidget* parent, LisaConfigInfo* configInfo)
+ :QWizard(parent,"hallo",true)
+ ,m_page1(0)
+ ,m_noNicPage(0)
+ ,m_multiNicPage(0)
+ ,m_searchPage(0)
+ ,m_addressesPage(0)
+ ,m_allowedAddressesPage(0)
+ ,m_bcastPage(0)
+ ,m_intervalPage(0)
+ ,m_advancedPage(0)
+ ,m_finalPage(0)
+ ,m_nicListBox(0)
+ ,m_trustedHostsLabel(0)
+ ,m_ping(0)
+ ,m_nmblookup(0)
+ ,m_pingAddresses(0)
+ ,m_allowedAddresses(0)
+ ,m_bcastAddress(0)
+ ,m_manualAddress(0)
+ ,m_updatePeriod(0)
+ ,m_deliverUnnamedHosts(0)
+ ,m_firstWait(0)
+ ,m_maxPingsAtOnce(0)
+ ,m_secondScan(0)
+ ,m_secondWait(0)
+ ,m_nics(0)
+ ,m_configInfo(configInfo)
+{
+ QString title( i18n("LISa Network Neighborhood Setup") );
+ setCaption(title);
+
+ m_configInfo->clear();
+ setupPage1();
+ addPage( m_page1, title);
+
+ setupAdvancedSettingsPage();
+ addPage( m_advancedPage, i18n("Advanced Settings"));
+
+}
+
+SetupWizard::~SetupWizard()
+{}
+
+void SetupWizard::clearAll()
+{
+ showPage(m_page1);
+ if (m_nicListBox)
+ m_nicListBox->clear();
+
+ if (m_manualAddress)
+ m_manualAddress->setText("");
+
+ if (m_ping)
+ m_ping->setChecked(false);
+ if (m_nmblookup)
+ m_nmblookup->setChecked(false);
+
+ if (m_pingAddresses)
+ m_pingAddresses->setText("");
+ if (m_allowedAddresses)
+ m_allowedAddresses->setText("");
+ if (m_bcastAddress)
+ m_bcastAddress->setText("");
+ if (m_updatePeriod)
+ m_updatePeriod->setValue(300);
+
+ if (m_deliverUnnamedHosts)
+ m_deliverUnnamedHosts->setChecked(false);
+ if (m_firstWait)
+ m_firstWait->setValue(10);
+ if (m_maxPingsAtOnce)
+ m_maxPingsAtOnce->setValue(256);
+ if (m_secondScan)
+ m_secondScan->setChecked(false);
+ if (m_secondWait)
+ {
+ m_secondWait->setEnabled(false);
+ m_secondWait->setValue(0);
+ }
+
+ if (m_nics)
+ {
+ delete m_nics;
+ m_nics=0;
+ }
+}
+
+void SetupWizard::setupPage1()
+{
+ m_page1 = new QVBox( this );
+
+ new QLabel(i18n("<qt><p>This wizard will ask you a few questions about your network.</p> "
+ "<p>Usually you can simply keep the suggested settings.</p> "
+ "<p>After you have finished the wizard, you will be able to browse and use "
+ "shared resources on your LAN, not only Samba/Windows shares, but also "
+ "FTP, HTTP and NFS resources exactly the same way.</p> "
+ "<p>Therefore you need to setup the <i>LAN Information Server</i> (LISa) on your machine. "
+ "Think of the LISa server as an FTP or HTTP server; "
+ "it has to be run by root, it should be started during the boot process and "
+ "only one LISa server can run on one machine.</qt>"),
+ m_page1);
+
+ QWidget *dummy=new QWidget(m_page1);
+ m_page1->setStretchFactor(dummy,10);
+ m_page1->setSpacing(KDialog::spacingHint());
+ m_page1->setMargin(KDialog::marginHint());
+
+ setNextEnabled( m_page1, true );
+ setHelpEnabled( m_page1, false );
+}
+
+void SetupWizard::setupMultiNicPage()
+{
+ m_multiNicPage=new QVBox(this);
+ new QLabel(i18n("<qt><p>More than one network interface card was found on your system.</p>"
+ "<p>Please choose the one to which your LAN is connected.</p></qt>"),
+ m_multiNicPage);
+
+ m_multiNicPage->setMargin(KDialog::marginHint());
+ m_multiNicPage->setSpacing(KDialog::spacingHint());
+
+ m_nicListBox=new QListBox(m_multiNicPage);
+ m_nicListBox->setSelectionMode(QListBox::Single);
+ setHelpEnabled( m_multiNicPage, false );
+}
+
+void SetupWizard::setupNoNicPage()
+{
+ m_noNicPage=new QVBox(this);
+ new QLabel(i18n("<qt><p><b>No network interface card was found on your system.</b></p>"
+ "<p>Possible reason: no network card is installed. You probably want to cancel now "
+ "or enter your IP address and network manually</p>"
+ "Example: <code>192.168.0.1/255.255.255.0</code>.</qt>"),
+ m_noNicPage);
+
+ m_noNicPage->setMargin(KDialog::marginHint());
+ m_noNicPage->setSpacing(KDialog::spacingHint());
+ m_manualAddress=new KRestrictedLine(m_noNicPage,"0123456789./");
+ connect(m_manualAddress,SIGNAL(textChanged(const QString&)),this,SLOT(checkIPAddress(const QString&)));
+ QWidget *dummy=new QWidget(m_noNicPage);
+ m_noNicPage->setStretchFactor(dummy,10);
+
+ setNextEnabled(m_noNicPage, false);
+ setHelpEnabled(m_noNicPage, false);
+}
+
+
+void SetupWizard::setupSearchPage()
+{
+ m_searchPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("There are two ways LISa can search hosts on your network."),m_searchPage);
+ info->setTextFormat(Qt::RichText);
+ m_ping=new QCheckBox(i18n("Send pings"), m_searchPage);
+ info=new QLabel(i18n("All hosts with TCP/IP will respond,<br>"
+ "whether or not they are samba servers.<br>"
+ "Don\'t use it if your network is very large, i.e. more than 1000 hosts.<br>"),m_searchPage);
+ m_nmblookup=new QCheckBox(i18n("Send NetBIOS broadcasts"),m_searchPage);
+ info=new QLabel(i18n("You need to have the samba package (nmblookup) installed.<br>"
+ "Only samba/windows servers will respond.<br>"
+ "This method is not very reliable.<br>"
+ "You should enable it if you are part of a large network."),m_searchPage);
+ info->setTextFormat(Qt::RichText);
+ QWidget *dummy=new QWidget(m_searchPage);
+ m_searchPage->setStretchFactor(dummy,10);
+ m_searchPage->setSpacing(KDialog::spacingHint());
+ m_searchPage->setMargin(KDialog::marginHint());
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"),m_searchPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+
+ setHelpEnabled( m_searchPage, false );
+}
+
+void SetupWizard::setupAddressesPage()
+{
+ m_addressesPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("All IP addresses included in the specified range will be pinged.<br>"
+ "If you are part of a small network, e.g. with network mask 255.255.255.0<br>"
+ "use your IP address/network mask.<br>"),m_addressesPage);
+ info->setTextFormat(Qt::RichText);
+ m_pingAddresses=new KRestrictedLine(m_addressesPage,"0123456789./;-");
+ info=new QLabel(i18n("<br>There are four ways to specify address ranges:<br>"
+ "1. IP address/network mask, like <code>192.168.0.0/255.255.255.0;</code><br>"
+ "2. single IP addresses, like <code>10.0.0.23;</code><br>"
+ "3. continuous ranges, like <code>10.0.1.0-10.0.1.200;</code><br>"
+ "4. ranges for each part of the address, like <code>10-10.1-5.1-25.1-3;</code><br>"
+ "You can also enter combinations of 1 to 4, separated by \";\", like<br>"
+ "<code>192.168.0.0/255.255.255.0;10.0.0.0;10.0.1.1-10.0.1.100;</code><br>"),m_addressesPage);
+ info->setAlignment(AlignLeft|AlignVCenter|WordBreak);
+ QWidget *dummy=new QWidget(m_addressesPage);
+ m_addressesPage->setStretchFactor(dummy,10);
+ m_addressesPage->setSpacing(KDialog::spacingHint());
+ m_addressesPage->setMargin(KDialog::marginHint());
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"), m_addressesPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+
+ setHelpEnabled( m_addressesPage, false );
+}
+
+void SetupWizard::setupAllowedPage()
+{
+ m_allowedAddressesPage=new QVBox(this);
+ QLabel* info=new QLabel(i18n("This is a security related setting.<br>"
+ "It provides a simple IP address based way to specify \"trusted\" hosts.<br>"
+ "Only hosts which fit into the addresses given here are accepted by LISa as clients. "
+ "The list of hosts published by LISa will also only contain hosts which fit into this scheme.<br>"
+ "Usually you enter your IP address/network mask here."),m_allowedAddressesPage);
+ info->setAlignment(AlignLeft|AlignVCenter|WordBreak);
+ m_allowedAddresses=new KRestrictedLine(m_allowedAddressesPage,"0123456789./-;");
+ m_trustedHostsLabel=new QLabel(m_allowedAddressesPage);
+
+ QWidget *dummy=new QWidget(m_allowedAddressesPage);
+ m_allowedAddressesPage->setStretchFactor(dummy,10);
+ m_allowedAddressesPage->setSpacing(KDialog::spacingHint());
+ m_allowedAddressesPage->setMargin(KDialog::marginHint());
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"), m_allowedAddressesPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+
+ setHelpEnabled( m_allowedAddressesPage, false );
+}
+
+void SetupWizard::setupBcastPage()
+{
+ m_bcastPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("<br>Enter your IP address and network mask here, like <code>192.168.0.1/255.255.255.0</code>"),m_bcastPage);
+ info->setAlignment(AlignLeft|AlignVCenter|WordBreak);
+ m_bcastAddress=new KRestrictedLine(m_bcastPage,"0123456789./");
+ info=new QLabel(i18n("<br>To reduce the network load, the LISa servers in one network<br>"
+ "cooperate with each other. Therefore you have to enter the broadcast<br>"
+ "address here. If you are connected to more than one network, choose <br>"
+ "one of the broadcast addresses."),m_bcastPage);
+ info->setAlignment(AlignLeft|AlignVCenter|WordBreak);
+ QWidget *dummy=new QWidget(m_bcastPage);
+ m_bcastPage->setStretchFactor(dummy,10);
+ m_bcastPage->setSpacing(KDialog::spacingHint());
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"), m_bcastPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+
+ m_bcastPage->setSpacing(KDialog::spacingHint());
+ m_bcastPage->setMargin(KDialog::marginHint());
+ setHelpEnabled( m_bcastPage, false );
+}
+
+void SetupWizard::setupUpdateIntervalPage()
+{
+ m_intervalPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("<br>Enter the interval after which LISa, if busy, will update its host list."),m_intervalPage);
+ info->setTextFormat(Qt::RichText);
+
+ m_updatePeriod=new QSpinBox(300,1800,10,m_intervalPage);
+ m_updatePeriod->setSuffix(i18n(" sec"));
+
+ info=new QLabel(i18n("<br>Please note that the update interval will grow automatically by "
+ "up to 16 times the value you enter here, if nobody accesses the LISa server. "
+ "So if you enter 300 sec = 5 min here, this does not mean that LISa will ping "
+ "your whole network every 5 minutes. The interval will increase up to 16 x 5 min = 80 min."),m_intervalPage);
+ info->setAlignment(AlignLeft|AlignVCenter|WordBreak);
+ QWidget *dummy=new QWidget(m_intervalPage);
+ m_intervalPage->setStretchFactor(dummy,10);
+ m_intervalPage->setSpacing(KDialog::spacingHint());
+ m_intervalPage->setMargin(KDialog::marginHint());
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"), m_intervalPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+ info->setTextFormat(Qt::RichText);
+
+ setHelpEnabled( m_intervalPage, false );
+}
+
+void SetupWizard::setupAdvancedSettingsPage()
+{
+ m_advancedPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("This page contains several settings you usually only<br>"
+ "need if LISa doesn't find all hosts in your network."),m_advancedPage);
+ info->setTextFormat(Qt::RichText);
+ m_deliverUnnamedHosts=new QCheckBox(i18n("Re&port unnamed hosts"),m_advancedPage);
+ info=new QLabel(i18n("Should hosts for which LISa can\'t resolve the name be included in the host list?<br>"),m_advancedPage);
+
+ QHBox* hbox=new QHBox(m_advancedPage);
+
+ info=new QLabel(i18n("Wait for replies after first scan"),hbox);
+ m_firstWait=new QSpinBox(10,1000,50,hbox);
+ m_firstWait->setSuffix(i18n(" ms"));
+ info=new QLabel(i18n("How long should LISa wait for answers to pings?<br>"
+ "If LISa doesn\'t find all hosts, try to increase this value.<br>"),m_advancedPage);
+
+ hbox=new QHBox(m_advancedPage);
+ info=new QLabel(i18n("Max. number of pings to send at once"),hbox);
+ info->setTextFormat(Qt::RichText);
+ m_maxPingsAtOnce=new QSpinBox(8,1024,5,hbox);
+ info=new QLabel(i18n("How many ping packets should LISa send at once?<br>"
+ "If LISa doesn't find all hosts you could try to decrease this value.<br>"),m_advancedPage);
+
+ m_secondScan=new QCheckBox(i18n("Al&ways scan twice"),m_advancedPage);
+
+ hbox=new QHBox(m_advancedPage);
+// hbox->setSpacing(10); // WTF?
+ info=new QLabel(i18n("Wait for replies after second scan"),hbox);
+ info->setTextFormat(Qt::RichText);
+ m_secondWait=new QSpinBox(0,1000,50,hbox);
+ m_secondWait->setSuffix(i18n(" ms"));
+ info=new QLabel(i18n("If LISa doesn't find all hosts, enable this option."),m_advancedPage);
+
+ //this would make the dialog to large
+ //m_advancedPage->setSpacing(KDialog::spacingHint());
+ //m_advancedPage->setMargin(KDialog::marginHint());
+
+ info=new QLabel(i18n("<b>If unsure, keep it as is.</b>"), m_advancedPage);
+ info->setAlignment(AlignRight|AlignVCenter);
+
+ connect(m_secondScan,SIGNAL(toggled(bool)),m_secondWait,SLOT(setEnabled(bool)));
+ setHelpEnabled( m_advancedPage, false );
+}
+
+void SetupWizard::setupFinalPage()
+{
+ m_finalPage=new QVBox(this);
+ QLabel *info=new QLabel(i18n("<br>Your LAN browsing has been successfully set up.<br><br>"
+ "Make sure that the LISa server is started during the "
+ "boot process. How this is done depends on your "
+ "distribution and OS. Usually you have to insert it somewhere "
+ "in a boot script under <code>/etc</code>.<br>"
+ "Start the LISa server as root and without any command line options.<br>"
+ "The config file will now be saved to <code>/etc/lisarc</code>.<br>"
+ "To test the server, try <code>lan:/</code> in Konqueror.<br><br>"
+ "If you have problems or suggestions, visit http://lisa-home.sourceforge.net."),m_finalPage);
+ info->setTextFormat(Qt::RichText);
+ QWidget *dummy=new QWidget(m_finalPage);
+ m_finalPage->setStretchFactor(dummy,10);
+ m_finalPage->setSpacing(KDialog::spacingHint());
+ m_finalPage->setMargin(KDialog::marginHint());
+
+ setHelpEnabled( m_finalPage, false );
+}
+
+void SetupWizard::next()
+{
+ if (currentPage()==m_page1)
+ {
+ if (m_noNicPage==0)
+ setupRest();
+
+ setAppropriate(m_noNicPage,false);
+ setAppropriate(m_multiNicPage,false);
+ if (m_nics!=0)
+ delete m_nics;
+ m_nics=findNICs();
+
+ if (m_nics->count()==0)
+ {
+ setAppropriate(m_noNicPage,true);
+ }
+ else if (m_nics->count()==1)
+ {
+ //still easy
+ //if the host part is less than 20 bits simply take it
+ MyNIC *nic=m_nics->first();
+ LisaConfigInfo lci;
+ suggestSettingsForNic(nic,lci);
+ applyLisaConfigInfo(lci);
+ }
+ else
+ {
+ //more than one nic
+ setAppropriate(m_multiNicPage,true);
+ m_nicListBox->clear();
+ for (MyNIC *nic=m_nics->first(); nic!=0; nic=m_nics->next())
+ {
+ QString tmp=nic->name+": "+nic->addr+"/"+nic->netmask+";";
+ m_nicListBox->insertItem(tmp);
+ }
+ m_nicListBox->setSelected(0,true);
+ }
+ }
+ else if (currentPage()==m_multiNicPage)
+ {
+ QString nic=m_nicListBox->currentText();
+ unsigned int i=0;
+ for (i=0; i<m_nicListBox->count(); i++)
+ {
+ if (m_nicListBox->isSelected(i))
+ {
+ nic=m_nicListBox->text(i);
+ break;
+ }
+ }
+ MyNIC* thisNic=m_nics->at(i);
+ LisaConfigInfo lci;
+ suggestSettingsForNic(thisNic,lci);
+ applyLisaConfigInfo(lci);
+ }
+ else if (currentPage()==m_noNicPage)
+ {
+ LisaConfigInfo lci;
+ suggestSettingsForAddress(m_manualAddress->text(),lci);
+ applyLisaConfigInfo(lci);
+ }
+ else if (currentPage()==m_searchPage)
+ setAppropriate(m_addressesPage, m_ping->isChecked());
+ else if (currentPage()==m_intervalPage)
+ {
+ if (m_finalPage==0)
+ {
+ setupFinalPage();
+ addPage( m_finalPage, i18n("Congratulations!"));
+ }
+ setAppropriate(m_advancedPage, m_ping->isChecked());
+ }
+ QWizard::next();
+}
+
+void SetupWizard::showPage(QWidget* page)
+{
+ if (page==m_noNicPage)
+ {
+ m_manualAddress->setFocus();
+ setNextEnabled(m_noNicPage, false);
+ setHelpEnabled(m_noNicPage, false);
+ }
+ else if (page==m_multiNicPage)
+ m_nicListBox->setFocus();
+ else if ( page == m_searchPage)
+ m_ping->setFocus();
+ else if (page==m_addressesPage)
+ m_pingAddresses->setFocus();
+ else if (page==m_allowedAddressesPage)
+ {
+ QString text;
+ if (m_ping->isChecked())
+ text+=i18n("You can use the same syntax as on the previous page.<br>");
+ else
+ text+=i18n("There are three ways to specify IP addresses:<br>"
+ "1. IP address/network mask, like<code> 192.168.0.0/255.255.255.0;</code><br>"
+ "2. continuous ranges, like<code> 10.0.1.0-10.0.1.200;</code><br>"
+ "3. single IP addresses, like<code> 10.0.0.23;</code><br>"
+ "You can also enter combinations of 1 to 3, separated by \";\", <br>"
+ "like<code> 192.168.0.0/255.255.255.0;10.0.0.0;10.0.1.1-10.0.1.100;</code><br>");
+ m_trustedHostsLabel->setText(text);
+ m_allowedAddresses->setFocus();
+ }
+ else if (page==m_bcastPage)
+ m_bcastAddress->setFocus();
+ else if (page==m_intervalPage)
+ m_updatePeriod->setFocus();
+ else if (page==m_advancedPage)
+ m_deliverUnnamedHosts->setFocus();
+ else if (page==m_finalPage)
+ setFinishEnabled(m_finalPage,true);
+
+ QWizard::showPage(page);
+}
+
+void SetupWizard::setupRest()
+{
+ removePage(m_advancedPage);
+
+ setupMultiNicPage();
+ addPage( m_multiNicPage, i18n("Multiple Network Interfaces Found"));
+
+ setupNoNicPage();
+ addPage( m_noNicPage, i18n("No Network Interface Found"));
+ setNextEnabled(m_noNicPage, false);
+ setHelpEnabled(m_noNicPage, false);
+
+ setupSearchPage();
+ addPage( m_searchPage, i18n("Specify Search Method"));
+
+ setupAddressesPage();
+ addPage( m_addressesPage, i18n("Specify Address Range LISa Will Ping"));
+
+ setupAllowedPage();
+ addPage( m_allowedAddressesPage, i18n("\"Trusted\" Hosts"));
+
+ setupBcastPage();
+ addPage( m_bcastPage, i18n("Your Broadcast Address"));
+
+ setupUpdateIntervalPage();
+ addPage( m_intervalPage, i18n("LISa Update Interval"));
+
+ addPage( m_advancedPage, i18n("Advanced Settings"));
+}
+
+void SetupWizard::accept()
+{
+ if (m_ping->isChecked())
+ {
+ m_configInfo->pingAddresses=m_pingAddresses->text();
+ m_configInfo->secondScan=m_secondScan->isChecked();
+ if (m_configInfo->secondScan)
+ m_configInfo->secondWait=(m_secondWait->value()+5)/10;
+ else
+ m_configInfo->secondWait=0;
+ m_configInfo->firstWait=(m_firstWait->value()+5)/10;
+ }
+ else
+ {
+ m_configInfo->pingAddresses="";
+ m_configInfo->secondScan=false;
+ m_configInfo->secondWait=0;
+ m_configInfo->firstWait=30;
+ }
+
+ m_configInfo->broadcastNetwork=m_bcastAddress->text();
+ m_configInfo->allowedAddresses=m_allowedAddresses->text();
+ m_configInfo->maxPingsAtOnce=m_maxPingsAtOnce->value();
+ m_configInfo->updatePeriod=m_updatePeriod->value();
+ m_configInfo->useNmblookup=m_nmblookup->isChecked();
+ m_configInfo->unnamedHosts=m_deliverUnnamedHosts->isChecked();
+ QWizard::accept();
+}
+
+void SetupWizard::checkIPAddress(const QString& addr)
+{
+ QString address=addr.simplifyWhiteSpace();
+ QRegExp regex("^\\d+\\.\\d+\\.\\d+\\.\\d+\\s*/\\s*\\d+\\.\\d+\\.\\d+\\.\\d+$");
+ setNextEnabled(m_noNicPage, (regex.search(address,0)!=-1));
+// setNextEnabled(m_noNicPage, (regex.find(address,0)!=-1));
+}
+
+void SetupWizard::applyLisaConfigInfo(LisaConfigInfo& lci)
+{
+ m_ping->setChecked(!lci.pingAddresses.isEmpty());
+ m_pingAddresses->setText(lci.pingAddresses);
+ m_nmblookup->setChecked(lci.useNmblookup);
+ m_allowedAddresses->setText(lci.allowedAddresses);
+ m_bcastAddress->setText(lci.broadcastNetwork);
+ m_updatePeriod->setValue(lci.updatePeriod);
+ m_deliverUnnamedHosts->setChecked(lci.unnamedHosts);
+ m_firstWait->setValue(lci.firstWait*10);
+ m_maxPingsAtOnce->setValue(lci.maxPingsAtOnce);
+ m_secondWait->setValue(lci.secondWait*10);
+ m_secondScan->setChecked(lci.secondScan);
+ m_secondWait->setEnabled(lci.secondScan);
+}
+
+#include "setupwizard.moc"
+
diff --git a/lanbrowsing/kcmlisa/setupwizard.h b/lanbrowsing/kcmlisa/setupwizard.h
new file mode 100644
index 00000000..a64cfaa2
--- /dev/null
+++ b/lanbrowsing/kcmlisa/setupwizard.h
@@ -0,0 +1,103 @@
+/*
+ * main.cpp for lisa,reslisa,kio_lan and kio_rlan kcm module
+ *
+ * Copyright (C) 2000 Alexander Neundorf <neundorf@kde.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LISA_SETUPWIZARD_H
+#define LISA_SETUPWIZARD_H
+
+#include "kcmlisa.h"
+#include "kcmreslisa.h"
+#include "kcmkiolan.h"
+
+#include "findnic.h"
+
+#include <qspinbox.h>
+#include <qcheckbox.h>
+#include <krestrictedline.h>
+#include <qwizard.h>
+#include <kglobal.h>
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qlistbox.h>
+#include <qvbox.h>
+
+#include <kcmodule.h>
+
+
+class SetupWizard:public QWizard
+{
+ Q_OBJECT
+ public:
+ SetupWizard(QWidget* parent, LisaConfigInfo* configInfo);
+ ~SetupWizard();
+ virtual void showPage(QWidget* page);
+ void clearAll();
+ protected slots:
+ virtual void next();
+ virtual void accept();
+ void checkIPAddress(const QString& address);
+ protected:
+ void applyLisaConfigInfo(LisaConfigInfo& lci);
+
+ void setupPage1();
+ void setupNoNicPage();
+ void setupMultiNicPage();
+ void setupSearchPage();
+ void setupAddressesPage();
+ void setupAllowedPage();
+ void setupBcastPage();
+ void setupUpdateIntervalPage();
+ void setupAdvancedSettingsPage();
+ void setupFinalPage();
+ void setupRest();
+
+ QVBox* m_page1;
+ QVBox* m_noNicPage;
+ QVBox* m_multiNicPage;
+ QVBox* m_searchPage;
+ QVBox* m_addressesPage;
+ QVBox* m_allowedAddressesPage;
+ QVBox* m_bcastPage;
+ QVBox* m_intervalPage;
+ QVBox* m_advancedPage;
+ QVBox* m_finalPage;
+
+
+ QListBox *m_nicListBox;
+ QLabel *m_trustedHostsLabel;
+ QCheckBox *m_ping;
+ QCheckBox *m_nmblookup;
+ KRestrictedLine* m_pingAddresses;
+ KRestrictedLine* m_allowedAddresses;
+ KRestrictedLine* m_bcastAddress;
+ KRestrictedLine* m_manualAddress;
+ QSpinBox* m_updatePeriod;
+ QCheckBox* m_deliverUnnamedHosts;
+ QSpinBox* m_firstWait;
+ QSpinBox* m_maxPingsAtOnce;
+ QCheckBox* m_secondScan;
+ QSpinBox* m_secondWait;
+
+ NICList* m_nics;
+ LisaConfigInfo* m_configInfo;
+};
+
+
+#endif
+
diff --git a/lanbrowsing/kio_lan/AUTHORS b/lanbrowsing/kio_lan/AUTHORS
new file mode 100644
index 00000000..062f9d6c
--- /dev/null
+++ b/lanbrowsing/kio_lan/AUTHORS
@@ -0,0 +1,2 @@
+Written and maintained by:
+Alexander Neundorf, neundorf@kde.org
diff --git a/lanbrowsing/kio_lan/Makefile.am b/lanbrowsing/kio_lan/Makefile.am
new file mode 100644
index 00000000..e0f76e4b
--- /dev/null
+++ b/lanbrowsing/kio_lan/Makefile.am
@@ -0,0 +1,31 @@
+## Makefile.am of kdebase/kioslave/man
+
+INCLUDES= $(all_includes)
+
+####### Files
+
+kde_module_LTLIBRARIES = kio_lan.la
+
+kio_lan_la_SOURCES = kio_lan.cpp
+kio_lan_la_LIBADD = $(LIB_KIO)
+kio_lan_la_LDFLAGS = -module -avoid-version -no-undefined $(all_libraries) $(KDE_RPATH)
+
+noinst_HEADERS = kio_lan.h
+
+kdelnk_DATA = lan.protocol rlan.protocol
+kdelnkdir = $(kde_servicesdir)
+
+remote_DATA = lan.desktop
+remotedir = $(kde_datadir)/konqueror/dirtree/remote
+
+remoteio_DATA = lan.desktop
+remoteiodir = $(kde_datadir)/remoteview
+
+konq_sidebartree_DATA = lisa.desktop
+konq_sidebartreedir = $(kde_datadir)/konqsidebartng/virtual_folders/services
+
+METASOURCES = AUTO
+
+messages:
+ $(XGETTEXT) *.cpp -o $(podir)/kio_lan.pot
+
diff --git a/lanbrowsing/kio_lan/README b/lanbrowsing/kio_lan/README
new file mode 100644
index 00000000..0a49f83c
--- /dev/null
+++ b/lanbrowsing/kio_lan/README
@@ -0,0 +1,16 @@
+This is a brandnew ioslave for my brandnew LISa daemon/server.
+It provides something like a network neighbourhood
+only relying on the TCP/IP protocol stack for KDE. Enter lan:/ to see it.
+Read the README for lisa/reslisa.
+LISa/resLISa is in the subdir lisa/.
+Maybe you have to adjust the Makefile a little bit, it currently doesn't
+use the automake/configure stuff.
+On some systems (e.g. Solaris) you will have to add some libraries, like
+-lnsl, I think.
+
+Copy this directory e.g. under kdebase/kioslave and enter it in
+kdebase/kioslave/Makefile.am in the SUBDIR line. The rerun make in
+kdebase/kioslave.
+
+Alexander Neundorf
+<neundorf@kde.org>
diff --git a/lanbrowsing/kio_lan/TODO b/lanbrowsing/kio_lan/TODO
new file mode 100644
index 00000000..8640402e
--- /dev/null
+++ b/lanbrowsing/kio_lan/TODO
@@ -0,0 +1,3 @@
+mainly testing, I think
+
+Alex
diff --git a/lanbrowsing/kio_lan/kio_lan.cpp b/lanbrowsing/kio_lan/kio_lan.cpp
new file mode 100644
index 00000000..06368b8d
--- /dev/null
+++ b/lanbrowsing/kio_lan/kio_lan.cpp
@@ -0,0 +1,794 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000,2001 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kinstance.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kprocess.h>
+
+#include <qfile.h>
+
+#include <iostream>
+#include <string.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <pwd.h>
+
+#include "kio_lan.h"
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+#define PORTSETTINGS_CHECK 0
+#define PORTSETTINGS_PROVIDE 1
+#define PORTSETTINGS_DISABLE 2
+
+using namespace KIO;
+
+#ifndef SHUT_RDWR
+#define SHUT_RDWR 2
+#endif
+
+extern "C" { KDE_EXPORT int kdemain(int argc, char **argv); }
+
+int kdemain( int argc, char **argv )
+{
+ KInstance instance( "kio_lan" );
+
+ if (argc != 4)
+ {
+ fprintf(stderr, "Usage: kio_lan protocol domain-socket1 domain-socket2\n");
+ exit(-1);
+ }
+ int isLanIoslave=(strcmp("lan",argv[1])==0);
+
+ // Trigger creation to make sure we pick up KIOSK settings correctly.
+ (void)KGlobal::dirs();
+ (void)KGlobal::locale();
+ (void)KGlobal::config();
+
+ kdDebug(7101) << "LAN: kdemain: starting" << endl;
+
+ LANProtocol slave(isLanIoslave, argv[2], argv[3]);
+ slave.dispatchLoop();
+ return 0;
+}
+
+LANProtocol::LANProtocol(int isLanIoslave, const QCString &pool, const QCString &app )
+:TCPSlaveBase(7741,isLanIoslave?"lan":"rlan", pool, app)
+,m_currentHost("")
+,m_port(7741)
+,m_maxAge(15*60)
+,m_isLanIoslave(isLanIoslave?true:false)
+{
+ KConfig *config=KGlobal::config();
+
+ m_protocolInfo[KIOLAN_FTP].enabled=config->readNumEntry("Support_FTP",PORTSETTINGS_CHECK);
+ m_protocolInfo[KIOLAN_HTTP].enabled=config->readNumEntry("Support_HTTP",PORTSETTINGS_CHECK);
+ m_protocolInfo[KIOLAN_NFS].enabled=config->readNumEntry("Support_NFS",PORTSETTINGS_CHECK);
+ m_protocolInfo[KIOLAN_SMB].enabled=config->readNumEntry("Support_SMB",PORTSETTINGS_CHECK);
+ m_protocolInfo[KIOLAN_FISH].enabled=config->readNumEntry("Support_FISH",PORTSETTINGS_CHECK);
+
+ m_defaultLisaHost=config->readEntry("DefaultLisaHost", "localhost");
+ m_shortHostnames=config->readBoolEntry("ShowShortHostnames",false);
+ m_maxAge=config->readNumEntry("MaxAge",15)*60;
+ if (m_maxAge<0) m_maxAge=0;
+
+ strcpy(m_protocolInfo[KIOLAN_NFS].name,"NFS");
+ strcpy(m_protocolInfo[KIOLAN_FTP].name,"FTP");
+ strcpy(m_protocolInfo[KIOLAN_SMB].name,"SMB");
+ strcpy(m_protocolInfo[KIOLAN_HTTP].name,"HTTP");
+ strcpy(m_protocolInfo[KIOLAN_FISH].name,"FISH");
+
+ // Now we check for port 445 for SMB/CIFS also. But we call both entries
+ // SMB. Clients will see only one SMB folder, though, whichever
+ // port (or both) is detected. The smb ioslave should be able
+ // to figure out which port to actually use.
+
+ m_protocolInfo[KIOLAN_NFS].ports.push_back(2049);
+ m_protocolInfo[KIOLAN_FTP].ports.push_back(21);
+ m_protocolInfo[KIOLAN_SMB].ports.push_back(445);
+ m_protocolInfo[KIOLAN_SMB].ports.push_back(139);
+ m_protocolInfo[KIOLAN_HTTP].ports.push_back(80);
+ m_protocolInfo[KIOLAN_FISH].ports.push_back(22);
+
+ m_hostInfoCache.setAutoDelete(true);
+}
+
+LANProtocol::~LANProtocol()
+{
+ m_hostInfoCache.clear();
+}
+
+int LANProtocol::readDataFromServer()
+{
+ if (m_isLanIoslave)
+ return lanReadDataFromServer();
+ else
+ return rlanReadDataFromServer();
+ return 0;
+}
+
+int LANProtocol::lanReadDataFromServer()
+{
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer() host: "<<m_currentHost<<" port: "<<m_port<<endl;
+ if (!connectToHost(m_currentHost.latin1(), m_port, false))
+ {
+ error(ERR_SLAVE_DEFINED, i18n("<qt>The Lisa daemon does not appear to be running.<p>"
+ "In order to use the LAN Browser the Lisa daemon must be "
+ "installed and activated by the system administrator."));
+ return 0;
+ }
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer() connected"<<endl;
+
+ int receivedBytes(0);
+ char* receiveBuffer(0);
+ char tmpBuf[64*1024];
+ int bytesRead(0);
+ do
+ {
+ fd_set tmpFDs;
+ FD_ZERO(&tmpFDs);
+ FD_SET(m_iSock,&tmpFDs);
+ timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ select(m_iSock+1,&tmpFDs,0,0,&tv);
+ if (FD_ISSET(m_iSock,&tmpFDs))
+ {
+ bytesRead=read(tmpBuf,64*1024);
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: read "<<bytesRead<<" bytes"<<endl;
+
+ if (bytesRead>0)
+ {
+ char *newBuf=new char[receivedBytes+bytesRead];
+ if (receiveBuffer!=0) memcpy(newBuf,receiveBuffer,receivedBytes);
+ memcpy(newBuf+receivedBytes,tmpBuf,bytesRead);
+ receivedBytes+=bytesRead;
+ if (receiveBuffer!=0) delete [] receiveBuffer;
+ receiveBuffer=newBuf;
+ }
+ }
+ } while (bytesRead>0);
+ closeDescriptor();
+ if ((bytesRead<0) || (receivedBytes<4))
+ {
+ delete [] receiveBuffer;
+ error(ERR_INTERNAL_SERVER,i18n("Received unexpected data from %1").arg(m_currentHost));
+ return 0;
+ }
+
+ UDSEntry entry;
+
+ char *currentBuf=receiveBuffer;
+ int bytesLeft=receivedBytes;
+ //this should be large enough for a name
+ char tmpName[4*1024];
+ //this should be large enough for the hostname
+ char tmpHostname[4*1024];
+ while (bytesLeft>0)
+ {
+ int tmpIP=2;
+ tmpName[0]='\0';
+ if ((memchr(currentBuf,0,bytesLeft)==0) || (memchr(currentBuf,int('\n'),bytesLeft)==0))
+ {
+ delete [] receiveBuffer;
+ error(ERR_INTERNAL_SERVER,i18n("Received unexpected data from %1").arg(m_currentHost));
+ return 0;
+ }
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: processing "<<currentBuf;
+ //since we check for 0 and \n with memchr() we can be sure
+ //at this point that tmpBuf is correctly terminated
+ int length=strlen(currentBuf)+1;
+ if (length<(4*1024))
+ sscanf(currentBuf,"%u %s\n",&tmpIP,tmpName);
+ else
+ {
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: buffer overflow attempt detected, aborting"<<endl;
+ break;
+ }
+
+ bytesLeft-=length;
+ currentBuf+=length;
+ if ((bytesLeft==0) && ((tmpIP==0) ||(tmpIP==1)) && (strstr(tmpName,"succeeded")!=0))
+ {
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: succeeded"<<endl;
+ }
+ else if (tmpIP!=2)
+ {
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: listing host: "<<tmpName<<" with ip: "<<tmpIP<<endl;
+ UDSAtom atom;
+
+ atom.m_uds = KIO::UDS_NAME;
+ if (m_shortHostnames)
+ {
+ if (inet_addr(tmpName)!=-1)
+ atom.m_str=tmpName;
+ else
+ {
+ sscanf(tmpName,"%[^.]",tmpHostname);
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: Hostname of " << tmpName << " is " << tmpHostname << "\n";
+ atom.m_str = tmpHostname;
+ }
+ }
+ else
+ atom.m_str = tmpName;
+
+ entry.append( atom );
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 1024;
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = S_IRUSR | S_IRGRP | S_IROTH ;
+ //atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR; // it is always a directory
+ entry.append( atom );
+ listEntry(entry,false);
+ }
+ }
+
+ listEntry( entry, true ); // ready
+ delete [] receiveBuffer;
+ return 1;
+}
+
+int LANProtocol::rlanReadDataFromServer()
+{
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer"<<endl;
+
+ int sockFD=socket(AF_LOCAL, SOCK_STREAM, 0);
+ sockaddr_un addr;
+ memset((char*)&addr,0,sizeof(addr));
+ addr.sun_family=AF_LOCAL;
+ QCString socketname="/tmp/resLisa-";
+
+ struct passwd *user = getpwuid( getuid() );
+ if ( user )
+ socketname+=user->pw_name;
+ else
+ //should never happen
+ socketname+="???";
+
+ strlcpy(addr.sun_path,socketname,sizeof(addr.sun_path));
+ int result=::connect(sockFD,(sockaddr*)&addr, sizeof(addr));
+
+ kdDebug(7101)<<"readDataFromServer(): result: "<<result<<" name: "<<addr.sun_path<<" socket: "<<sockFD<<endl;
+
+ if (result!=0)
+ {
+ ::close(sockFD);
+ KProcess proc;
+ proc<<"reslisa";
+
+ bool ok=proc.start(KProcess::DontCare);
+ if (!ok)
+ {
+ error( ERR_CANNOT_LAUNCH_PROCESS, "reslisa" );
+ return 0;
+ }
+ //wait a moment
+ //reslisa starts kde-config, then does up to 64
+ //name lookups and then starts to ping
+ //results won't be available before this is done
+ kdDebug(7101)<<"sleeping..."<<endl;
+ ::sleep(1);
+ kdDebug(7101)<<"sleeping again..."<<endl;
+ ::sleep(5);
+ kdDebug(7101)<<"woke up "<<endl;
+ sockFD=socket(AF_LOCAL, SOCK_STREAM, 0);
+
+ memset((char*)&addr,0,sizeof(addr));
+ addr.sun_family=AF_LOCAL;
+ strlcpy(addr.sun_path,socketname,sizeof(addr.sun_path));
+
+ kdDebug(7101)<<"connecting..."<<endl;
+ result=::connect(sockFD,(sockaddr*)&addr, sizeof(addr));
+ kdDebug(7101)<<"readDataFromServer() after starting reslisa: result: "<<result<<" name: "<<addr.sun_path<<" socket: "<<sockFD<<endl;
+ if (result!=0)
+ {
+ error( ERR_CANNOT_OPEN_FOR_READING, socketname );
+ return 0;
+ }
+ kdDebug(7101)<<"succeeded :-)"<<endl;
+ }
+
+ int receivedBytes(0);
+ char* receiveBuffer(0);
+ char tmpBuf[64*1024];
+ int bytesRead(0);
+ do
+ {
+ fd_set tmpFDs;
+ FD_ZERO(&tmpFDs);
+ FD_SET(sockFD,&tmpFDs);
+ timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ select(sockFD+1,&tmpFDs,0,0,&tv);
+ if (FD_ISSET(sockFD,&tmpFDs))
+ {
+ bytesRead=::read(sockFD,tmpBuf,64*1024);
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer: read "<<bytesRead<<" bytes"<<endl;
+
+ if (bytesRead>0)
+ {
+ char *newBuf=new char[receivedBytes+bytesRead];
+ if (receiveBuffer!=0) memcpy(newBuf,receiveBuffer,receivedBytes);
+ memcpy(newBuf+receivedBytes,tmpBuf,bytesRead);
+ receivedBytes+=bytesRead;
+ if (receiveBuffer!=0) delete [] receiveBuffer;
+ receiveBuffer=newBuf;
+ }
+ }
+ } while (bytesRead>0);
+ ::close(sockFD);
+
+
+ if ((bytesRead<0) || (receivedBytes<4))
+ {
+ delete [] receiveBuffer;
+ error(ERR_CANNOT_OPEN_FOR_READING,socketname);
+ return 0;
+ }
+
+ UDSEntry entry;
+
+ char *currentBuf=receiveBuffer;
+ int bytesLeft=receivedBytes;
+ //this should be large enough for a name
+ char tmpName[4*1024];
+ //this should be large enough for the hostname
+ char tmpHostname[4*1024];
+ while (bytesLeft>0)
+ {
+ int tmpIP=2;
+ tmpName[0]='\0';
+ if ((memchr(currentBuf,0,bytesLeft)==0) || (memchr(currentBuf,int('\n'),bytesLeft)==0))
+ {
+ delete [] receiveBuffer;
+ error(ERR_INTERNAL_SERVER,i18n("Received unexpected data from %1").arg(socketname));
+ return 0;
+ }
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer: processing "<<currentBuf;
+ //since we check for 0 and \n with memchr() we can be sure
+ //at this point that tmpBuf is correctly terminated
+ int length=strlen(currentBuf)+1;
+ if (length<(4*1024))
+ sscanf(currentBuf,"%u %s\n",&tmpIP,tmpName);
+ else
+ {
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer: buffer overflow attempt detected, aborting"<<endl;
+ break;
+ }
+
+ bytesLeft-=length;
+ currentBuf+=length;
+ if ((bytesLeft==0) && ((tmpIP==0) ||(tmpIP==1)) && (strstr(tmpName,"succeeded")!=0))
+ {
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer: succeeded"<<endl;
+ }
+ else if (tmpIP!=2)
+ {
+ kdDebug(7101)<<"RLANProtocol::readDataFromServer: listing host: "<<tmpName<<" with ip: "<<tmpIP<<endl;
+ UDSAtom atom;
+
+ atom.m_uds = KIO::UDS_NAME;
+ if (m_shortHostnames)
+ {
+ if (inet_addr(tmpName)!=-1)
+ atom.m_str=tmpName;
+ else
+ {
+ sscanf(tmpName,"%[^.]",tmpHostname);
+ kdDebug(7101)<<"LANProtocol::lanReadDataFromServer: Hostname of " << tmpName << " is " << tmpHostname << "\n";
+ atom.m_str = tmpHostname;
+ }
+ }
+ else
+ atom.m_str = tmpName;
+ entry.append( atom );
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 1024;
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = S_IRUSR | S_IRGRP | S_IROTH ;
+ //atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR; // it is always a directory
+ entry.append( atom );
+ listEntry(entry,false);
+ }
+ }
+
+ listEntry( entry, true ); // ready
+ delete [] receiveBuffer;
+ return 1;
+}
+
+int LANProtocol::checkHost(const QString& host)
+{
+ kdDebug(7101)<<"LAN::checkHost() "<<host<<endl;
+
+ QString hostUpper=host.upper();
+ HostInfo* hostInfo=m_hostInfoCache[hostUpper];
+ if (hostInfo!=0)
+ {
+ kdDebug(7101)<<"LAN::checkHost() getting from cache"<<endl;
+ //this entry is too old, we delete it !
+ if ((time(0)-hostInfo->created)>m_maxAge)
+ {
+ kdDebug(7101)<<"LAN::checkHost() cache content too old, deleting it"<<endl;
+ m_hostInfoCache.remove(hostUpper);
+ hostInfo=0;
+ }
+ }
+ if (hostInfo==0)
+ {
+ hostInfo=new HostInfo;
+ in_addr ip;
+
+ struct hostent *hp=gethostbyname(host.latin1());
+ if (hp==0)
+ {
+ error( ERR_UNKNOWN_HOST, host.latin1() );
+ delete hostInfo;
+ return 0;
+ }
+ memcpy(&ip, hp->h_addr, sizeof(ip));
+
+ for (int i=0; i<KIOLAN_MAX; i++)
+ {
+ int result(0);
+ if (m_protocolInfo[i].enabled==PORTSETTINGS_DISABLE)
+ result=0;
+ else if (m_protocolInfo[i].enabled==PORTSETTINGS_PROVIDE)
+ result=1;
+ else if (m_protocolInfo[i].enabled==PORTSETTINGS_CHECK)
+ result=checkPort(m_protocolInfo[i].ports,ip);
+
+ //host not reachable
+ if (result==-1)
+ {
+ delete hostInfo;
+ hostInfo=0;
+ error( ERR_UNKNOWN_HOST, host.latin1() );
+ return 0;
+ }
+ hostInfo->services[i]=result;
+ }
+ hostInfo->created=time(0);
+ m_hostInfoCache.insert(hostUpper,hostInfo);
+ }
+ //here hostInfo is always != 0
+ if (hostInfo==0)
+ {
+ error( ERR_INTERNAL, "hostInfo==0" );
+ return 0;
+ }
+
+ UDSEntry entry;
+ for (int i=0; i<KIOLAN_MAX; i++)
+ {
+ if (hostInfo->services[i]==1)
+ {
+ kdDebug(7101)<<"LAN::checkHost(): Host ["<<hostUpper<<"] Service ["<<m_protocolInfo[i].name<<"]"<<endl;
+ UDSAtom atom;
+ // name
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = m_protocolInfo[i].name;
+ entry.append( atom );
+ // size
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 1024;
+ entry.append(atom);
+ // access permissions
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = S_IRUSR | S_IRGRP | S_IROTH ;
+ //atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ entry.append(atom);
+ // file type
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ if (strcmp(m_protocolInfo[i].name,"HTTP")==0)
+ {
+ // normal file -- if we called stat(2) on this,
+ // this flag would be set in the st_mode field of struct stat
+ atom.m_long=S_IFREG;
+ entry.append(atom);
+
+ // also define the mime-type for this file
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str="text/html";
+ entry.append( atom );
+ }
+ else
+ {
+ // directory -- if we called stat(2) on this, then this
+ // flag would be set in the st_mode field of the struct stat
+ atom.m_long = S_IFDIR; // it is always a directory
+ entry.append(atom);
+
+ // also set the mime-type
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str="inode/directory";
+ entry.append( atom );
+ }
+ listEntry(entry,false);
+ }
+ }
+ listEntry( entry, true ); // ready
+ return 1;
+}
+
+// Check if a service is running on a host with IP address 'ip'
+// by checking all the ports in '_ports', using a non-blocking connect.
+// Right now -- assume if *any* of these ports are active,
+// the service is active.
+int LANProtocol::checkPort( QValueVector<int>& _ports, in_addr ip )
+{
+ int _port=0;
+ struct sockaddr_in to_scan;
+
+ to_scan.sin_family = AF_INET;
+ to_scan.sin_addr = ip;
+
+ for (QValueVector<int>::iterator i= _ports.begin(); i != _ports.end(); i++)
+ {
+ _port=(*i);
+ kdDebug(7101)<<"LANProtocol::checkPort: "<<_port<<endl;
+ to_scan.sin_port = htons(_port);
+ // open a TCP socket
+ int mysocket = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (mysocket< 0 )
+ {
+ std::cerr << "LanProt::checkPort: Error while opening Socket" << std::endl;
+ ::close( mysocket );
+ return 0;
+ }
+ //make the socket non blocking
+ long int options = O_NONBLOCK | ::fcntl(mysocket, F_GETFL);
+ if (::fcntl( mysocket, F_SETFL, options )!=0)
+ {
+ std::cerr << "LanProt::checkPort: Error making it nonblocking"<< std::endl;
+ ::close( mysocket );
+ return 0;
+ }
+ int result=connect( mysocket, (struct sockaddr *) &to_scan, sizeof( to_scan ));
+ //it succeeded immediately
+ if (result==0)
+ {
+ std::cerr<<"LANProtocol::checkPort("<<_port<<") connect succeeded immediately"<<std::endl;
+ ::shutdown( mysocket, SHUT_RDWR );
+ return 1;
+ }
+ //it failed
+ if ((result<0) && (errno!=EINPROGRESS))
+ {
+ // errno was not EINPROGRESS, so there is some serious problem
+ ::shutdown( mysocket, SHUT_RDWR );
+ // maybe some other port will work
+ continue;
+ }
+ // last errno was EINPROGRESS, so we should select() on socket
+ // and wait for the final verdict
+ timeval tv;
+ tv.tv_sec=5;
+ tv.tv_usec=0;
+ fd_set rSet, wSet;
+ FD_ZERO(&rSet);
+ FD_SET(mysocket,&rSet);
+ wSet=rSet;
+ //timeout or error
+ result=select(mysocket+1,&rSet,&wSet,0,&tv);
+ ::shutdown( mysocket, SHUT_RDWR );
+ if (result==1)
+ return 1;
+ }
+ // gosh, no port worked
+ return 0;
+
+}
+
+void LANProtocol::setHost( const QString& host, int port, const QString& , const QString& )
+{
+ if (m_isLanIoslave)
+ {
+ m_currentHost=host;
+ if (port==0)
+ m_port=7741;
+ else
+ m_port=port;
+ kdDebug(7101)<<"LANProtocol::setHost: "<<m_currentHost<<endl;
+ }
+ else
+ {
+ if (!host.isEmpty())
+ error(ERR_MALFORMED_URL,i18n("No hosts allowed in rlan:/ URL"));
+ }
+}
+
+void LANProtocol::mimetype( const KURL& url)
+{
+ kdDebug(7101)<<"LANProtocol::mimetype -"<<url.prettyURL()<<"-"<<endl;
+ QString path( QFile::encodeName(url.path()));
+ QStringList pathList=QStringList::split( "/",path);
+ if ((pathList.count()==2) && (pathList[1].upper()=="HTTP"))
+ {
+ //kdDebug(7101)<<"LANProtocol::mimeType text/html"<<endl;
+ mimeType("text/html");
+ }
+ else
+ {
+ mimeType("inode/directory");
+ //kdDebug(7101)<<"LANProtocol::mimeType inode/directory"<<endl;
+ }
+ finished();
+}
+
+void LANProtocol::listDir( const KURL& _url)
+{
+ KURL url(_url);
+ QString path( QFile::encodeName(url.path()));
+ if (path.isEmpty())
+ {
+ url.setPath("/");
+ redirection(url);
+ finished();
+ return;
+ }
+ if ((m_currentHost.isEmpty()) && (m_isLanIoslave))
+ {
+ url.setHost(m_defaultLisaHost);
+ redirection(url);
+ finished();
+ return;
+ }
+ kdDebug(7101)<<"LANProtocol::listDir: host: "<<m_currentHost<<" port: "<<m_port<<" path: "<<path<<endl;
+ QStringList pathList=QStringList::split("/", path);
+ kdDebug(7101)<<"parts are: "<<endl;
+ for (QStringList::Iterator it=pathList.begin(); it !=pathList.end(); it++)
+ kdDebug(7101)<<"-"<<(*it)<<"-"<<endl;
+ if (pathList.count()>2)
+ {
+ kdDebug(7101)<<"LANProtocol::listDir: too deep path: "<<pathList.count()<<endl;
+ error(ERR_DOES_NOT_EXIST,_url.prettyURL());
+ return;
+ }
+ int succeeded(0);
+ if (path=="/")
+ {
+ //get the stuff from the netland server
+ succeeded=readDataFromServer();
+ }
+ else if (pathList.count()==1)
+ {
+ //check the ports and stuff
+ succeeded=checkHost(pathList[0]);
+ }
+ else
+ {
+ kdDebug(7101)<<"LANProtocol::listDir: trying to redirect"<<endl;
+ int isSupportedProtocol(0);
+ for (int i=0; i<KIOLAN_MAX; i++)
+ {
+ if (pathList[1].upper()==m_protocolInfo[i].name)
+ {
+ isSupportedProtocol=m_protocolInfo[i].enabled;
+ break;
+ }
+ }
+ if (isSupportedProtocol==PORTSETTINGS_DISABLE)
+ {
+ kdDebug(7101)<<"LANProtocol::listDir: protocol not enabled "<<endl;
+ error(ERR_DOES_NOT_EXIST,_url.prettyURL());
+ return;
+ }
+ //redirect it
+ KURL newUrl(pathList[1]+"://"+pathList[0]);
+ redirection(newUrl);
+ succeeded=1;
+ }
+ if (succeeded) finished();
+}
+
+void LANProtocol::stat( const KURL & url)
+{
+ kdDebug(7101)<<"LANProtocol::stat: "<<endl;
+ UDSEntry entry;
+ UDSAtom atom;
+
+ atom.m_uds = KIO::UDS_NAME;
+ atom.m_str = url.path();
+ entry.append( atom );
+ atom.m_uds = KIO::UDS_SIZE;
+ atom.m_long = 1024;
+ entry.append(atom);
+
+ atom.m_uds = KIO::UDS_ACCESS;
+ atom.m_long = S_IRUSR | S_IRGRP | S_IROTH ;
+ //atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
+ entry.append(atom);
+
+
+ QString path( QFile::encodeName(url.path()));
+ QStringList pathList=QStringList::split( "/",path);
+ if ((pathList.count()==2) && (pathList[1].upper()=="HTTP"))
+ {
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long=S_IFREG;
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str="text/html";
+ //kdDebug(7101)<<"LANProtocol::stat: http is reg file"<<endl;
+ entry.append( atom );
+ }
+ else
+ {
+ atom.m_uds = KIO::UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR; // it is always a directory
+ entry.append(atom);
+ atom.m_uds = KIO::UDS_MIME_TYPE;
+ atom.m_str="inode/directory";
+ //kdDebug(7101)<<"LANProtocol::stat: is dir"<<endl;
+ entry.append( atom );
+ }
+
+ statEntry( entry );
+ finished();
+}
+
+void LANProtocol::get(const KURL& url )
+{
+ QString path(QFile::encodeName(url.path()));
+
+ kdDebug(7101)<<"LANProtocol::get: "<<path<<endl;
+ QStringList pathList=QStringList::split("/", path);
+ kdDebug(7101)<<"parts are: "<<endl;
+ for (QStringList::Iterator it=pathList.begin(); it !=pathList.end(); it++)
+ kdDebug(7101)<<"-"<<(*it)<<"-"<<endl;
+ if ((pathList.count()!=2) || (pathList[1].upper()!="HTTP"))
+ {
+ kdDebug(7101)<<"LANProtocol::get: invalid url: "<<pathList.count()<<endl;
+ error(ERR_DOES_NOT_EXIST,url.prettyURL());
+ return;
+ }
+ KURL newUrl("http://"+pathList[0]);
+ redirection(newUrl);
+ finished();
+ return;
+}
diff --git a/lanbrowsing/kio_lan/kio_lan.h b/lanbrowsing/kio_lan/kio_lan.h
new file mode 100644
index 00000000..26bff398
--- /dev/null
+++ b/lanbrowsing/kio_lan/kio_lan.h
@@ -0,0 +1,87 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Alexander Neundorf <neundorf@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KIO_XLAN_H
+#define KIO_XLAN_H
+
+#include <kio/slavebase.h>
+#include <kio/tcpslavebase.h>
+#include <kio/global.h>
+
+#include <qvaluevector.h>
+#include <qcstring.h>
+#include <qstring.h>
+#include <qdict.h>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+
+#define KIOLAN_HTTP 0
+#define KIOLAN_FTP 1
+#define KIOLAN_SMB 2
+#define KIOLAN_NFS 3
+#define KIOLAN_FISH 4
+#define KIOLAN_MAX 5
+
+#define NAMELEN 8
+
+struct MyProtocolInfo
+{
+ int enabled;
+ QValueVector<int> ports;
+ //this should be large enough for things like "FTP" and so on
+ char name[NAMELEN];
+};
+
+struct HostInfo
+{
+ time_t created;
+ int services[KIOLAN_MAX];
+};
+
+class LANProtocol : public KIO::TCPSlaveBase
+{
+ public:
+ LANProtocol (int isLanIoSlave, const QCString &pool, const QCString &app );
+ virtual ~LANProtocol();
+
+ virtual void setHost( const QString& host, int port, const QString& user, const QString& pass );
+ virtual void mimetype( const KURL& );
+
+ virtual void listDir( const KURL& url);
+ virtual void stat( const KURL & url);
+ virtual void get( const KURL& url );
+
+ protected:
+ QDict<HostInfo> m_hostInfoCache;
+ int readDataFromServer();
+ int lanReadDataFromServer();
+ int rlanReadDataFromServer();
+ int checkHost(const QString& host);
+ int checkPort(QValueVector<int>& _ports, in_addr ip);
+ QString m_currentHost;
+ unsigned short int m_port;
+ MyProtocolInfo m_protocolInfo[KIOLAN_MAX];
+ int m_maxAge;
+ bool m_isLanIoslave;
+ bool m_shortHostnames;
+ QString m_defaultLisaHost;
+};
+
+#endif
diff --git a/lanbrowsing/kio_lan/lan.desktop b/lanbrowsing/kio_lan/lan.desktop
new file mode 100644
index 00000000..cb4716db
--- /dev/null
+++ b/lanbrowsing/kio_lan/lan.desktop
@@ -0,0 +1,81 @@
+[Desktop Entry]
+Type=Link
+DocPath=lisa/index.html
+URL=lan:/
+Icon=network_local
+Name=Local Network
+Name[af]=Plaaslike Netwerk
+Name[ar]=الشبكة المحلية
+Name[az]=Yerli Şəbəkə
+Name[be]=ÐœÑÑÑ†Ð¾Ð²Ð°Ñ Ñетка
+Name[bg]=Локална мрежа
+Name[bn]=সà§à¦¥à¦¾à¦¨à§€à§Ÿ নেটওয়ারà§à¦•
+Name[br]=Rouedad lec'hel
+Name[bs]=Lokalna mreža
+Name[ca]=Xarxa local
+Name[cs]=Lokální síť
+Name[cy]=Rhwydwaith Lleol
+Name[da]=Lokalt netværk
+Name[de]=Lokales Netzwerk
+Name[el]=Τοπικό δίκτυο
+Name[eo]=Loka reto
+Name[es]=Red local
+Name[et]=Kohalik võrk
+Name[eu]=Sare lokala
+Name[fa]=شبکۀ محلی
+Name[fi]=Paikallisverkko
+Name[fr]=Réseau local
+Name[ga]=Gréasán Logánta
+Name[gl]=Rede Local
+Name[he]=רשת מקומית
+Name[hi]=सà¥à¤¥à¤¾à¤¨à¥€à¤¯ नेटवरà¥à¤•
+Name[hr]=Lokalna mreža
+Name[hu]=Helyi hálózat
+Name[id]=Jaringan lokal
+Name[is]=Staðarnet
+Name[it]=Rete locale
+Name[ja]=ローカルãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯
+Name[ka]=ლáƒáƒ™áƒáƒšáƒ£áƒ áƒ˜ ქსელი
+Name[kk]=Жергілікті желі
+Name[km]=បណ្ដាញ​មូលដ្ឋាន
+Name[ko]=지역 네트워í¬
+Name[lt]=Vietinis tinklas
+Name[lv]=LokÄlais TÄ«kls
+Name[mk]=Локална мрежа
+Name[mn]=Дотоод ÑүлжÑÑ
+Name[ms]=Jaringan Setempat
+Name[mt]=Network Lokali
+Name[nb]=Lokalt nettverk
+Name[nds]=Lokaal Nettwark
+Name[ne]=सà¥à¤¥à¤¾à¤¨à¥€à¤¯ सञà¥à¤œà¤¾à¤²
+Name[nl]=Lokaal netwerk
+Name[nn]=Lokalt nettverk
+Name[nso]=Kgokagano Selegae
+Name[pa]=ਲੋਕਲ ਨੈੱਟਵਰਕ
+Name[pl]=Sieć lokalna
+Name[pt]=Rede Local
+Name[pt_BR]=Rede local
+Name[ro]=Reţea locală
+Name[ru]=Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ñеть
+Name[se]=Báikkálaš fierpmádat
+Name[sk]=Lokálna sieť
+Name[sl]=Krajevno omrežje
+Name[sr]=Локална мрежа
+Name[sr@Latn]=Lokalna mreža
+Name[sv]=Lokalt nätverk
+Name[ta]=உளà¯à®³à®• பிணையமà¯
+Name[tg]=Шабакаи Маҳаллӣ
+Name[th]=เครือข่ายท้องถิ่น
+Name[tr]=Yerel AÄŸ
+Name[uk]=Локальна мережа
+Name[uz]=Lokal tarmoq
+Name[uz@cyrillic]=Локал тармоқ
+Name[ven]=Vhukwamani ha tsini
+Name[wa]=Rantoele locåle
+Name[xh]=Umsebenzi womnatha Wobulali
+Name[zh_CN]=局域网
+Name[zh_HK]=å€åŸŸç¶²çµ¡
+Name[zh_TW]=å€åŸŸç¶²è·¯
+Name[zu]=Oluseduze Uxhumaniso olusakazekile
+Open=false
+X-KDE-TreeModule=Directory
diff --git a/lanbrowsing/kio_lan/lan.protocol b/lanbrowsing/kio_lan/lan.protocol
new file mode 100644
index 00000000..ba72a216
--- /dev/null
+++ b/lanbrowsing/kio_lan/lan.protocol
@@ -0,0 +1,15 @@
+[Protocol]
+exec=kio_lan
+protocol=lan
+input=none
+output=filesystem
+listing=Name,Type
+reading=true
+writing=false
+makedir=false
+deleting=false
+linking=false
+moving=false
+DocPath=kioslave/lan.html
+Icon=network_local
+Class=:local
diff --git a/lanbrowsing/kio_lan/lisa.desktop b/lanbrowsing/kio_lan/lisa.desktop
new file mode 100644
index 00000000..207ee3e5
--- /dev/null
+++ b/lanbrowsing/kio_lan/lisa.desktop
@@ -0,0 +1,66 @@
+[Desktop Entry]
+Type=Link
+URL=lan:/
+Icon=network
+Name=LAN Browser
+Name[be]=ÐглÑдальнік мÑÑцовай Ñеткі
+Name[bg]=Браузър на локалната мрежа
+Name[bn]=লà§à¦¯à¦¾à¦¨ বà§à¦°à¦¾à¦‰à¦œà¦¾à¦°
+Name[br]=Furcher LAN
+Name[bs]=LAN preglednik
+Name[ca]=Navegador LAN
+Name[cs]=Prohlížení lokální sítě
+Name[da]=LAN-søger
+Name[de]=LAN durchsuchen
+Name[el]=ΠεÏιηγητής LAN
+Name[eo]=Rigardilo por loka reto
+Name[es]=Navegador de red
+Name[et]=Kohtvõrgu sirvija
+Name[eu]=LAN arakatzailea
+Name[fa]=مرورگر شبکۀ داخلی
+Name[fi]=Lähiverkon selain
+Name[fr]=Navigateur réseau
+Name[ga]=Brabhsálaí an Ghréasáin Logánta
+Name[gl]=Explorador LAN
+Name[he]=LAN דפדפן
+Name[hr]=Preglednik LAN-a
+Name[hu]=Hálózatböngésző
+Name[is]=Netflakkari
+Name[it]=Navigazione della rete locale
+Name[ja]=LAN ブラウザ
+Name[ka]=LAN ბრáƒáƒ£áƒ–ერი
+Name[kk]=LAN шолғышы
+Name[km]=កម្មវិធី​រុករក​បណ្ដាញ​មូលដ្ឋាន
+Name[lt]=Vietinio tinklo naršyklė
+Name[lv]=LAN PÄrlÅ«ks
+Name[mk]=Прегледувач на LAN
+Name[nb]=LAN-los
+Name[nds]=Nettwark-Kieker
+Name[ne]=LAN बà¥à¤°à¤¾à¤‰à¤œà¤°
+Name[nn]=LAN-lesar
+Name[pa]=LAN à¨à¨²à¨•à¨¾à¨°à¨¾
+Name[pl]=PrzeglÄ…darka sieci lokalnej
+Name[pt]=Navegador de Rede
+Name[pt_BR]=Navegador LAN
+Name[ru]=Проводник LAN
+Name[se]=LAN-bláđđejeaddji
+Name[sk]=PrehliadaÄ siete
+Name[sl]=Brskalnik po krajevnem omrežju
+Name[sr]=Претраживач LAN-а
+Name[sr@Latn]=PretraživaÄ LAN-a
+Name[sv]=Bläddrare i lokalt nätverk
+Name[ta]=LAN உலாவி
+Name[tg]=БарраÑи Шабакаи Маҳаллӣ
+Name[th]=à¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸à¸”ูระบบà¹à¸¥à¸™
+Name[tr]=LAN Tarayıcı
+Name[uk]=Ðавігатор ЛОМ
+Name[ven]=Buronza ya LAN
+Name[xh]=Umkhangeli zincwadi we LAN
+Name[zh_CN]=局域网æµè§ˆå™¨
+Name[zh_HK]=å€åŸŸç¶²çµ¡ç€è¦½å™¨
+Name[zh_TW]=LAN ç€è¦½å™¨
+Name[zu]=Umbheki zincwadi ze LAN
+Open=false
+X-KDE-TreeModule=Directory
+X-KDE-KonqSidebarModule=konqsidebar_tree
+X-KDE-ConfiguredURL=kio_lanrc:noGroup:sidebarURL
diff --git a/lanbrowsing/kio_lan/rlan.protocol b/lanbrowsing/kio_lan/rlan.protocol
new file mode 100644
index 00000000..de2c0ad4
--- /dev/null
+++ b/lanbrowsing/kio_lan/rlan.protocol
@@ -0,0 +1,15 @@
+[Protocol]
+exec=kio_lan
+protocol=rlan
+input=none
+output=filesystem
+listing=Name,Type
+reading=true
+writing=false
+makedir=false
+deleting=false
+linking=false
+moving=false
+DocPath=kioslave/rlan.html
+Icon=network_local
+Class=:local
diff --git a/lanbrowsing/lisa/AUTHORS b/lanbrowsing/lisa/AUTHORS
new file mode 100644
index 00000000..d13cadc9
--- /dev/null
+++ b/lanbrowsing/lisa/AUTHORS
@@ -0,0 +1,2 @@
+Written and maintained by
+ Alexander Neundorf <neundorf@kde.org>
diff --git a/lanbrowsing/lisa/COPYING b/lanbrowsing/lisa/COPYING
new file mode 100644
index 00000000..ded002ea
--- /dev/null
+++ b/lanbrowsing/lisa/COPYING
@@ -0,0 +1,347 @@
+NOTE! The GPL below is copyrighted by the Free Software Foundation, but
+the instance of code that it refers to (the kde programs) are copyrighted
+by the authors who actually wrote it.
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), 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 Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. 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 program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; 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.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/lanbrowsing/lisa/ChangeLog b/lanbrowsing/lisa/ChangeLog
new file mode 100644
index 00000000..7d0ef7e8
--- /dev/null
+++ b/lanbrowsing/lisa/ChangeLog
@@ -0,0 +1,8 @@
+0.1.3
+-security fixes: fixed LOGNAME vulnerabilty and another possible buffer overflow
+
+Version 0.1.2
+-various fixes and improvements...
+
+Version 0.1
+-initial version
diff --git a/lanbrowsing/lisa/INSTALL b/lanbrowsing/lisa/INSTALL
new file mode 100644
index 00000000..f8bad0c1
--- /dev/null
+++ b/lanbrowsing/lisa/INSTALL
@@ -0,0 +1,176 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/kde/bin', `/usr/local/kde/lib', etc. You can specify an
+installation prefix other than `/usr/local/kde' by giving `configure'
+the option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/lanbrowsing/lisa/Makefile.am b/lanbrowsing/lisa/Makefile.am
new file mode 100644
index 00000000..ff1f8433
--- /dev/null
+++ b/lanbrowsing/lisa/Makefile.am
@@ -0,0 +1,41 @@
+##
+AM_CXXFALGS = -fno-rtti -fno-exceptions
+
+#CXXFLAGS = -fno-rtti -fno-exceptions
+
+bin_PROGRAMS = lisa reslisa
+#
+# Programs to compile, Manpages to install and Versions
+#
+#INCLUDES = $(all_includes)
+
+lisa_SOURCES = \
+addressvalidator.cpp \
+netmanager.cpp \
+netscanner.cpp \
+ipaddress.cpp \
+main.cpp \
+mystring.cpp \
+client.cpp \
+configfile.cpp
+
+reslisa_SOURCES = \
+addressvalidator.cpp \
+netmanager.cpp \
+netscanner.cpp \
+ipaddress.cpp \
+strictmain.cpp \
+mystring.cpp \
+client.cpp \
+configfile.cpp
+
+lisa_LDADD = $(LIBSOCKET)
+lisa_LDFLAGS = $(KDE_EXTRA_RPATH)
+
+reslisa_LDADD = $(LIBSOCKET)
+reslisa_LDFLAGS = $(KDE_EXTRA_RPATH)
+
+misc_DATA = README
+miscdir = $(kde_datadir)/lisa
+EXTRA_DIST=$(misc_DATA)
+
diff --git a/lanbrowsing/lisa/NEWS b/lanbrowsing/lisa/NEWS
new file mode 100644
index 00000000..898a3dab
--- /dev/null
+++ b/lanbrowsing/lisa/NEWS
@@ -0,0 +1 @@
+See ChangeLog.
diff --git a/lanbrowsing/lisa/README b/lanbrowsing/lisa/README
new file mode 100644
index 00000000..c6d5d1bd
--- /dev/null
+++ b/lanbrowsing/lisa/README
@@ -0,0 +1,419 @@
+This is the readme to the Lan Information Server LISa and the Restricted LAN
+Information Server resLISa.
+
++---------------+
+| LISa |
++---------------+
+
+LISa is intended to provide a kind of "network neighbourhood" but only
+relying on the TCP/IP protocol stack, no smb or whatever.
+It is completely independent from KDE/Qt.
+The list of running hosts is provided via TCP port 7741.
+
+LISa supports two ways of finding hosts:
+
+1. you give LISa a range of of IP-addresses, then LISa will send
+ICMP echo requests to all given IP addresses and wait for the answers
+
+2. you can say LISa to execute "nmblookup "*" ", i.e. the command line tool
+nmblookup must be installed, it is part of the samba package.
+nmblookup "*" sends a broadcast to the attached networks and all hosts
+running smb-services will answer this broadcast
+
+lisa and reslisa are distributed under the GNU General Public License.
+
+
+
+How it works
+-------------
+
+In the configuration file you provide a range of IP-addresses which
+LISa should check, wether they are running. In the most simple case
+this could be your network address/subnetmask, then LISa would
+check every possible host of your network wether it is running.
+The hosts are checked using ICMP echo requests. To be able to send and receive
+ICMP echo requests and replies the program has to open a so-called
+"raw socket". Therefor it needs root privileges. This socket is opened
+right after the start of the program, after successfully opening the socket
+root privileges are dropped immediately (see main.cpp and strictmain.cpp).
+If you configure LISa this way, that it also uses nmblookup, it will
+popen("nmblookup \"*\"") and then parse the results.
+
+Since the ICMP requests and the broadcasts can cause some network traffic
+if there are more than one such server running in one network, the servers
+cooperate with each other. Before they start pinging (or nmblookup),
+they send a broadcast on port 7741.
+If somebody answers this broadcast, they will retrieve the complete list
+of running hosts via TCP port 7741 from this host and will not start to
+ping (or nmblookup) theirselves. If nobody answers, the host which sent
+the broadcast will start pinging the hosts (or nmblookup) and then open a
+socket which listens for the mentioned broadcasts. If the host received an
+answer to his broadcast, it won't have the socket for listening to the
+broadcasts open. So usually exactly one of the servers will have this
+socket open and only this one will actually ping (or nmblookup) the hosts.
+In other words, the servers are lazy, they work like "I will only do something
+if nobody else can do it for me".
+
+There is another feature which reduces the network load. Let's say you configured LISa
+to update all 10 minutes. Now you don't access your server very often.
+If nobody accesses the server for the last update period, the server will
+update (either itself or from the one which actually does the work) and then
+double its update period, i.e. the next update will happen after 20 minutes.
+This will happen 4 times, so if nobody accesses the server with update period
+10 minutes for a long time, its update interval will increase up to
+160 minutes, almost three hours. If then somebody accesses the data from the
+server, he will get an old list ( up to 160 minutes old). With accessing
+the server will reset its update interval to its initial value,
+i.e. 10 minutes and immediately start updating if the last update is more
+than these 10 minutes over. This means if you get a very old list, you can try
+some seconds later again and you should get a current version.
+This will have fast effect for the servers, which don't ping (or nmblookup)
+theirselves, since only one user usually accesses them, and it will have
+less effect for the server which does the pinging (or nmblookup), since
+this server is accessed from all other servers in the network.
+
+
+This way it is possible that many hosts in a network run this server, but
+the net load will remain low. For the user it is not necessary to know
+wether there is a server (i.e. a name server or fileserver or whatever)
+in the network which also runs LISa. He can always run LISa locally
+and LISa will detect if there is one, transparently to the user.
+The first client for LISa is an ioslave for KDE2, so the user
+can enter there lan://localhost/ or lan:/, which will both
+contact LISa on the own system.
+If there is a machine which runs all the time and the user knows
+that this machine also runs LISa, he can use his LISa client directly with
+this server (would be with the mentioned ioslave lan://the_server_name/).
+
+If you don't want that your LISa takes part in the broadcasting, but always
+does the pinging itself, make it use another port with the
+command line option --port or -p.
+This is not recommended !
+
+If you send SIGHUP to LISa, it will reread its configfile.
+If you send SIGUSR1 to LISa, it will print some status information to stdout.
+
+The data provided over the socket has a simple format:
+
+<decimal ip address in network byte order><one space 0x20><full name of the host><a terminating '\0'><newline '\n'>
+and the last line
+0 succeeded<'\n'>
+
+e.g.
+
+"17302538 some_host.whatever.de
+18285834 linux.whatever.de
+17827082 nameserver.whatever.de
+0 succeeded
+"
+
+This should make it easy parseable.
+
+If there are very strict security rules in your network, some people
+might consider the pinging a potential attack. If you
+have problems with this, try the restricted version, resLISa.
+
++------------------+
+| resLISa |
++------------------+
+
+If you hav very strict security rules in your network or you don't want to
+have another port open or whatever, you can use resLISa.
+
+With resLISa you can't ping whole networks and address ranges, you can give
+resLISa up to currently 64 hosts by their names in its config file. These
+will be pinged. You are still able to use nmblookup.
+resLISa will also only provide the information over a unix domain socket, i.e.
+not over the network. The name of the socket is "/tmp/resLisa-YourLoginname",
+so resLISa can be safely run by more users on one machine.
+Since it should also not produce a security risk of any kind it is
+safe to install reslisa setuid root. root privileges will be dropped
+right after startup (see strictmain.cpp), they are only needed to create a raw socket
+for sending the ICMP echo requests..
+It will also not send or receive broadcasts.
+The first client for this is also an ioslave for KDE2 (makes rlan:/ in e.g. konqy).
+
+
+
+Configuration
+-------------
+
+Now an example config file:
+
+PingAddresses = 192.168.100.0/255.255.255.0;192.168.100.10-192.168.199.19;192.168.200.1;192-192.168-168.100-199.0-9;
+PingNames = bb_mail;
+AllowedAddresses = 192.168.0.0/255.255.0.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #also try nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+
+
+PingAddresses
+
+This is probably the most important entry.
+Here you say which addresses will be pinged. You can specify multiple
+ranges, they are divided by semicolons.
+
+There are four possible ways to define addresses:
+-net address/network mask: 192.168.100.0/255.255.255.0, i.e. an IP address
+ and the assigned network mask. This doesn't have the real network address
+ and netmask of your machine, it can be less. E.g. if you have
+ 10.0.0.0/255.0.0.0, you could specify 10.1.2.0/255.255.255.0 if you are only
+ interested in these addresses. The combination IP address-network mask
+ must be divided by a slash "/" and the address does not have to be a real
+ network address, it can also be a host address of the desired network,
+ i.e. 10.12.34.67/255.0.0.0 is the same as 10.0.0.0/255.0.0.0 .
+
+-a range of following IP addresses: 192.168.100.10-192.168.199.19, i.e.
+ an IP-address where pinging will start and an IP-address where pinging will end.
+ Both addresses must be divided by a "-".
+ In this example this would produce 199-100+1=100, 100*256=25.600,
+ 25.600+(19-10+1)=25.590 addresses
+
+-an IP-address can be presented by its four decimal numbers, you can specify
+ ranges four each of these four numbers: 192-192.169-171.100-199.0-9
+ In this example all IP addresses with first number 192, second number from
+ 168 to 168, third number from 100 up to 199 and last number from 0 up
+ to 9 will be pinged. This would give 1*1*100*10=1.000 addresses.
+ This is probably only useful in very seldom cases.
+ Here you have to provide ranges for every four numbers, always divided
+ by "-".
+
+-single IP-addresses: 192.168.200.1
+ well, single IP addresses or host names
+
+It is also valid to leave this entry empty.
+
+
+PingNames
+ here you can additionally specify hosts to ping using their names.
+ The names have to be divided by semicolons.
+
+It is also valid to leave this entry empty.
+
+
+AllowedAddresses
+
+ This is also very important. LISa will only ping addresses,
+ accept clients and answer broadcasts from addresses, which are covered by the
+ addresses given in this line. You can add up to 32 network addresses/network masks
+ or single addresses. Divide them by ; and don't put empty space between the
+ addresses !
+ Example: 192.168.0.0/255.255.0.0;192.169.0.0
+ -> a complete network and a single address are valid
+ Always make this as strict as possible, usually
+ your network address/subnetmask is a good choice.
+
+
+BroadcastNetwork
+
+ This entry contains exactly one network address/subnet mask.
+ To this network broadcasts will be sent. Usually this should be your
+ own network address/subnetmask.
+ Example: 192.168.0.0/255.255.0.0
+
+
+SearchUsingNmblookup
+
+ Here you can give 0 or 1.
+ 1 means that LISa will execute "nmblookup "*" " and parse the output
+ from this command. This produces less network traffic than the pinging,
+ but you will only get hosts which have a smb-service running (Windows
+ machines or machines running samba).
+ If you enable this option and also give IP addresses to ping, then nmblookup
+ will be executed first and then the pinging will start.
+ Then only addresses will be pinged, which were not already delivered
+ from nmblookup. This should slightly decrease the network load.
+
+
+FirstWait
+
+ If LISa pings, i.e. if it sends the ICMP echo requests, it sends a bunch
+ of requests at once, and the it will wait for the number of hundredth seconds
+ you specify here. Usually values from 5 to 50 should be good, the maximum
+ is 99 (gives 0.99 seconds, a very long time).
+ Try to make this value as small as possible while still finding all
+ running hosts.
+
+
+SecondWait
+
+ After LISa sent the echo requests the first time, it can be possible
+ that some hosts were not found. To improve the results, LISa can ping a
+ second time. This time it will only ping hosts, from which it didn't receive
+ answers. If you have good results with pinging only once, you can disable
+ the second time with setting SecondWait to -1.
+ Otherwise it might be a good idea to make this value a little bit bigger
+ than the value for FirstWait, since the hosts which were not found
+ on the first try, are probably slower or further away so they might take
+ some milliseconds longer to answer.
+ Usually values from 5 to 50 should be good or -1 to disable the second scan.
+ The maximum is 99 (gives 0.99 seconds, a very long time).
+
+
+UpdatePeriod
+
+ This is the interval after which LISa will update, i.e. ping or nmblookup
+ or get the list of hosts from the LISa server which actually does the pinging.
+ Valid values are between 30 seconds and 1800 seconds (half an hour).
+ If you have a big network, don't make the interval to small (to keep
+ network load low). Values from 300 to 900 seconds (5 to 15 minutes) might be
+ a good idea. Keep in mind that the update period is doubled
+ if nobody accesses the server, up to 4 times, so the interval will become
+ 16 times the value given here and will be reseted to the value given here
+ if somebody accesses the server.
+
+
+DeliverUnnamedHosts
+
+ If an answer to an echo request from an IP address was received, were LISa
+ could not determine a name, it will be only delivered over the port
+ if you set this to 1.
+ I am not really sure if this is a useful feature, but maybe
+ there are some infrastructure devices in your network without assigned names,
+ so they don't have to be published. Set this to 0 if you want to keep them
+ secret ;-)
+ If unsure, say 0.
+
+MaxPingsAtOnce
+
+ When sending the pings (echo requests), LISa sends a bunch of these at once
+ and then waits for the answers. By default there are 256 pings sent at once,
+ usually you should not need the change this value. If you make it much bigger,
+ the internal receive buffers for the answers to the echo requests may become to small,
+ if you make it to small, the updating will be slower.
+
+
+Three different example config files:
+
+You are member of a small network with 24 bit network mask, i.e.
+up to 256 hosts:
+
+PingAddresses = 192.168.100.0/255.255.255.0
+AllowedAddresses = 192.168.100.0/255.255.255.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 0 #don't use nmblookup
+FirstWait = 20 #20 hundredth seconds
+SecondWait = 30 #30 hundredth seconds on the seconds try
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+
+
+You are only interested in hosts running smb services and you don't have
+routers in your network:
+
+AllowedAddresses = 192.168.100.0/255.255.255.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #use nmblookup
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+
+
+The same network, but here both nmblookup and pinging is used.
+
+PingAddresses = 192.168.100.0/255.255.255.0
+PingNames = bb_mail
+AllowedAddresses = 192.168.0.0/255.255.0.0
+BroadcastNetwork = 192.168.100.0/255.255.255.0
+SearchUsingNmblookup = 1 #also try nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 0 #don't publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+
+And now a configuration file for resLISa, PingAddresses is not used by resLISa,
+neither is BroadcastNetwork.
+
+PingNames = bb_mail;some_host;some_other_host
+AllowedAddresses = 192.168.0.0/255.255.0.0
+SearchUsingNmblookup = 1 # use nmblookup
+FirstWait = 30 #30 hundredth seconds
+SecondWait = -1 #only one try
+#SecondWait = 60 #try twice, and the second time wait 0.6 seconds
+UpdatePeriod = 300 #update after 300 secs
+DeliverUnnamedHosts = 1 #also publish hosts without name
+MaxPingsAtOnce = 256 #send up to 256 ICMP echo requests at once
+
+
++----------------------+
+| Installation |
++----------------------+
+
+Both reslisa and lisa open a so called raw socket to send and receive
+ICMP echo requests (pings). To do this, they need root privileges.
+
+lisa offers a service on TCP port 7741, it should be installed by root
+and started when the system comes up, it depends on your distribution
+how to do this.
+
+reslisa is intended to be started per user, it doesn't offer anything to
+the network. It needs to be installed setuid root.
+
+If you use the rlan-ioslave from KDE2, reslisa can be started automatically
+by reslisa.
+
+lisa reads the file lisarc, reslisa reads the file reslisarc.
+If you want to be able to configure both from the KDE Control Center,
+you have to start them using the command line switch -K.
+
+For more information where they look for configuration files read
+the next chapter.
+
+
++--------------------------------------------+
+| Command Line Options and other usage |
++--------------------------------------------+
+
+The following command line options are supported:
+
+-v, --version prints a short version info
+-h, --help gives an overview over teh command line options
+
+Some options regarding search order for the configuration files.
+For reslisa the file is named reslisarc instead lisarc.
+
+-u, --unix search at first for $HOME/.lisarc, then
+ for /etc/lisarc, this is the default behaviour
+
+-k, --kde1 search at first for $HOME/.kde/share/config/lisarc,
+ then for $KDEDIR/share/config/lisarc
+
+-K, --kde2 looks for the file lisarc in every directory
+ returned by running "kde-config --path config"
+
+-c, --config=FILE read this and no other configuration file
+
+
+This one is only available for LISa, not for resLISa.
+
+-p, --port PORTNR start the server om this portnumber
+ if you use this LISa won't be able to
+ cooperate with other LISa's in the network
+
+
+If you send the Hangup-Signal to lisa or reslisa, it will reread its
+configuration file (killall -HUP lisa).
+
+If you send the User1-Signal to lisa or reslisa, it will print some status
+information to the standard output (killall -USR1 lisa). You won't see
+anything if the console from which lisa/reslisa was started has terminated.
+
+
+LISa and resLISa need a libstdc++ (it uses only the string-class from it),
+it *doesn't* need neither Qt nor KDE.
+
+So, that's it for now.
+If you have suggestions, problems or whatever, contact me.
+
+
+Have fun
+Alexander Neundorf
+<neundorf@kde.org>
+
diff --git a/lanbrowsing/lisa/addressvalidator.cpp b/lanbrowsing/lisa/addressvalidator.cpp
new file mode 100644
index 00000000..aa292806
--- /dev/null
+++ b/lanbrowsing/lisa/addressvalidator.cpp
@@ -0,0 +1,228 @@
+/* addressvalidator.cpp
+ *
+ * Copyright (c) 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "addressvalidator.h"
+#include "mystring.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <iostream>
+using namespace std;
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+#define LISA_DEBUG 0
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"AddressValidator::"
+
+AddressValidator::AddressValidator(const MyString& addressSpecs)
+//this is 127.0.0.0
+:localhostNet(htonl(0x7f000000))
+//with mask 255.255.255.0
+,localhostMask(htonl(0xffffff00))
+{
+ clearSpecs();
+ MyString tmp=addressSpecs;
+ setValidAddresses(tmp);
+}
+
+AddressValidator::AddressValidator()
+ //this is 127.0.0.0
+ :localhostNet(htonl(0x7f000000))
+ //with mask 255.255.255.0
+ ,localhostMask(htonl(0xffffff00))
+{
+ clearSpecs();
+}
+
+AddressValidator::~AddressValidator()
+{}
+
+void AddressValidator::configure(Config& config)
+{
+ MyString tmp=stripWhiteSpace(config.getEntry("AllowedAddresses",""));
+ tmp=tmp+";";
+ setValidAddresses(tmp);
+ dcerr<<"configure(): "<<tmp<<std::endl;
+}
+
+
+void AddressValidator::setValidAddresses(MyString addressSpecs)
+{
+ dcerr<<"setValidAddresses"<<std::endl;
+ allowedHosts=addressSpecs;
+ MyString nextPart;
+ while (!addressSpecs.isEmpty())
+ {
+ dcerr<<"setValidAddresses: "<<addressSpecs<<std::endl;
+ int pos=addressSpecs.find(";");
+ nextPart=addressSpecs.left(pos);
+ addressSpecs=addressSpecs.mid(pos+1);
+ dcerr<<"setValidAddresses: nextPart: "<<nextPart<<std::endl;
+ if ((nextPart.contains('.')==3) && (nextPart.contains('-')==0))
+ {
+ addSpec(EXACTADDR_SPEC,inet_addr(nextPart.data()));
+ dcerr<<"setValidAddresses: exact addr: "
+ <<std::ios::hex<<inet_addr(nextPart.data())<<std::ios::dec<<std::endl;
+ }
+ else if ((nextPart.contains('-')==1) && (nextPart.contains('.')==6))
+ {
+ int p2=nextPart.find("-");
+ MyString from=nextPart.left(p2);
+ MyString to=nextPart.mid(p2+1);
+ addSpec(RANGE_SPEC,ntohl(inet_addr(from.data())),ntohl(inet_addr(to.data())));
+ }
+ else if ((nextPart.contains('-')==4) && (nextPart.contains('.')==3))
+ {
+ unsigned int i1=0;
+ unsigned int i2=0;
+ int p2=0;
+ MyString rest=nextPart+'.';
+ MyString digit;
+
+ for (int i=0; i<4; i++)
+ {
+ p2=rest.find("-");
+ digit=rest.left(p2);
+ i1=i1<<8;
+ i1+=atoi(digit.data());
+ rest=rest.mid(p2+1);
+ p2=rest.find(".");
+ digit=rest.left(p2);
+ i2=i2<<8;
+ i2+=atoi(digit.data());
+ rest=rest.mid(p2+1);
+ };
+ addSpec(MULTIRANGE_SPEC,i1,i2);
+ }
+ else
+ {
+ pos=nextPart.find('/');
+ MyString netStr=nextPart.left(pos);
+ MyString maskStr=nextPart.mid(pos+1);
+ int mask=inet_addr(maskStr.data());
+ int net= (inet_addr(netStr.data()) & mask);
+ dcerr<<"setValidAddresses: net/mask: "
+ <<std::ios::hex<<net<<"/"<<mask<<std::ios::dec<<std::endl;
+ addSpec(NETMASK_SPEC,net,mask);
+ }
+ }
+}
+
+void AddressValidator::clearSpecs()
+{
+ allowedHosts="";
+ for (int i=0; i<MAX_SPECS; i++)
+ {
+ specs[i].address=0;
+ specs[i].mask=0;
+ specs[i].typeOfSpec=NO_SPEC;
+ }
+}
+
+void AddressValidator::addSpec(int type, int address, int mask)
+{
+ for (int i=0; i<MAX_SPECS; i++)
+ {
+ if (specs[i].typeOfSpec==NO_SPEC)
+ {
+ specs[i].address=address;
+ specs[i].mask=mask;
+ specs[i].typeOfSpec=type;
+ return;
+ }
+ }
+}
+
+int AddressValidator::isValid(int addressNBO)
+{
+ dcerr<<"isValid: "
+ <<std::ios::hex<<addressNBO<<std::ios::dec<<std::endl;
+ //localhost is always allowed
+ dcerr<<"isValid() local net: "<<
+ std::ios::hex<<localhostNet<<" mask: "<<localhostMask<<" AND: "<<(addressNBO &
+ localhostMask)<<std::ios::dec<<std::endl;
+ if ((addressNBO & localhostMask) == localhostNet)
+ return 1;
+
+ for (int i=0; i<MAX_SPECS; i++)
+ {
+ if (specs[i].typeOfSpec==NO_SPEC)
+ {
+ //since the specifications are always entered from the beginning
+ //of the array, we already passed the last one if we get here
+ //so we can return now "it is invalid !" ;-)
+ return 0;
+ //continue;
+ }
+ else if (specs[i].typeOfSpec==EXACTADDR_SPEC)
+ {
+ dcerr<<"isValid: comparing "
+ <<std::ios::hex<<specs[i].address<<std::ios::dec<<std::endl;
+ if (addressNBO==specs[i].address)
+ {
+ dcerr<<"isValid: exact address"<<std::endl;
+ return 1; // this one is allowed to :-)
+ }
+ }
+ else if (specs[i].typeOfSpec==NETMASK_SPEC)
+ {
+ dcerr<<"isValid: ANDing "<<
+ std::ios::hex<<(addressNBO & specs[i].mask)<<" "<<
+ specs[i].address<<std::ios::dec<<std::endl;
+ if ((addressNBO & specs[i].mask) == specs[i].address)
+ {
+ dcerr<<"isValid: net/mask"<<std::endl;
+ return 1;
+ }
+ }
+ else if (specs[i].typeOfSpec==RANGE_SPEC)
+ {
+ if ((ntohl(addressNBO)>=specs[i].address) && (ntohl(addressNBO)<=specs[i].mask))
+ {
+ dcerr<<"isValid: range"<<std::endl;
+ return 1;
+ }
+ }
+ else if (specs[i].typeOfSpec==MULTIRANGE_SPEC)
+ {
+ unsigned int addr=ntohl(addressNBO);
+ dcerr<<"isValid ntohl="<<hex<<addr<<" addr: "<<specs[i].address<<" ma: "<<specs[i].mask<<dec<<std::endl;
+ unsigned int mask=0x000000ff;
+ int failure=0;
+ for (int j=0; j<4; j++)
+ {
+ dcerr<<"isValid "<<hex<<"mask="<<mask<<" addr="<<(addr&mask)<<" addr="<<(specs[i].address & mask)<<" ma="<<(specs[i].mask&mask)<<std::endl;
+ if (((addr & mask) < (specs[i].address & mask))
+ || ((addr & mask) > (specs[i].mask & mask)))
+ {
+ failure=1;
+ break;
+ }
+ mask=mask<<8;
+ }
+ dcerr<<"isValid: multirange"<<std::endl;
+ if (!failure)
+ return 1;
+ }
+ }
+ //if ((addressNBO==htonl(0x0a040801)) || (addressNBO==htonl(0xc0a80001))) return 0;
+ dcerr<<"isValid: invalid address"<<std::endl;
+ return 0;
+}
+
diff --git a/lanbrowsing/lisa/addressvalidator.h b/lanbrowsing/lisa/addressvalidator.h
new file mode 100644
index 00000000..6f1c723e
--- /dev/null
+++ b/lanbrowsing/lisa/addressvalidator.h
@@ -0,0 +1,55 @@
+/* addressvalidator.h
+ *
+ * Copyright (c) 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef ADDRESSVALIDATOR_H
+#define ADDRESSVALIDATOR_H
+
+#include "lisadefines.h"
+#include "mystring.h"
+#include "configfile.h"
+
+#define NO_SPEC 0
+#define NETMASK_SPEC 1
+#define EXACTADDR_SPEC 2
+#define RANGE_SPEC 3
+#define MULTIRANGE_SPEC 4
+
+struct AddressSpec
+{
+ int address;
+ int mask;
+ int typeOfSpec;
+};
+
+class AddressValidator
+{
+ public:
+ AddressValidator(const MyString& addressSpecs);
+ AddressValidator();
+ ~AddressValidator();
+ void configure(Config& config);
+ void setValidAddresses(MyString addressSpecs);
+ void clearSpecs();
+ int isValid(int addressNBO);
+ MyString validAddresses() {return allowedHosts;};
+ private:
+ int localhostNet;
+ int localhostMask;
+ MyString allowedHosts;
+ void addSpec(int type, int address, int mask=0);
+ AddressSpec specs[MAX_SPECS];
+};
+
+#endif
diff --git a/lanbrowsing/lisa/client.cpp b/lanbrowsing/lisa/client.cpp
new file mode 100644
index 00000000..21069a17
--- /dev/null
+++ b/lanbrowsing/lisa/client.cpp
@@ -0,0 +1,122 @@
+/* client.cpp
+ *
+ * Copyright (c) 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "netmanager.h"
+#include "client.h"
+#include "lisadefines.h"
+
+#include <iostream>
+#include <unistd.h>
+
+#include <strings.h>
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#define LISA_DEBUG 0
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"Client::"
+
+
+Client::Client(NetManager* parent, int socketFD, int closeOnDelete)
+:parentServer(parent)
+,m_fd(socketFD)
+,m_done(0)
+,m_closeOnDelete(closeOnDelete)
+{
+ dcerr<<"ctor created new client"<<std::endl;
+ if (m_fd==-1) m_done=1;
+ m_creationTime=time(0);
+}
+
+Client::Client()
+:parentServer(0)
+,m_fd(-1)
+,m_done(1)
+,m_closeOnDelete(1)
+{
+ m_creationTime=time(0);
+}
+
+Client::Client(const Client& c)
+:parentServer(c.parentServer)
+,m_fd(c.m_fd)
+,m_creationTime(c.m_creationTime)
+,m_done(c.m_done)
+,m_closeOnDelete(1)
+{
+ dcerr<<"Client copy ctor"<<std::endl;
+}
+
+int Client::tryToGetInfo()
+{
+ dcerr<<"tryToGetInfo: m_done: "<<m_done<<std::endl;
+ if (done()) return 1;
+ if (m_fd==-1)
+ {
+ close();
+ return 1;
+ }
+ dcerr<<"tryToGetInfo: calling data.getFile()"<<std::endl;
+ if (!parentServer->isInformed()) return 0;
+ //we fork now, so that writing to the client can't block the server process
+ /* int pid=fork();
+ if (pid==-1)
+ {
+ //parent
+ dcerr<<"NetScanner::scan: error occurred"<<std::endl;
+ return 1;
+ }
+ else if (pid!=0)
+ {
+ //parent
+ return 1;
+ };*/
+ //child
+ //this one does it all :-)
+ dcerr<<"tryToGetInfo: sending data to client"<<std::endl;
+ parentServer->writeDataToFD(fd(),0);
+ close();
+ //exit(0);
+ return 1;
+}
+
+void Client::close()
+{
+ if (m_fd!=-1) ::close(m_fd);
+ m_fd=-1;
+ m_done=1;
+}
+
+void Client::read()
+{
+ dcerr<<"read()"<<std::endl;
+ if (m_fd==-1) return;
+ char buf[1024];
+ //the clients should not send us something !
+ int result=::read(m_fd,buf,1024);
+ if (result>=0) close();
+}
+
+Client::~Client()
+{
+ if (m_closeOnDelete)
+ close();
+}
+
diff --git a/lanbrowsing/lisa/client.h b/lanbrowsing/lisa/client.h
new file mode 100644
index 00000000..efcf5f86
--- /dev/null
+++ b/lanbrowsing/lisa/client.h
@@ -0,0 +1,45 @@
+/* client.h
+ *
+ * Copyright (c) 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef CLIENT_H
+#define CLIENT_H
+
+#include <time.h>
+
+class NetManager;
+
+class Client
+{
+ public:
+ Client(NetManager* parent, int socketFD, int closeOnDelete=1);
+ Client();
+ Client(const Client& c);
+ ~Client();
+ int tryToGetInfo();
+ int done() {return m_done;};
+ int fd() {return m_fd;};
+ void read();
+ void close();
+ time_t age() {return time(0)-m_creationTime;};
+ private:
+ NetManager *parentServer;
+ int m_fd;
+ //if something goes wrong close the connection after a timeout
+ time_t m_creationTime;
+ int m_done;
+ int m_closeOnDelete;
+};
+
+#endif
diff --git a/lanbrowsing/lisa/configfile.cpp b/lanbrowsing/lisa/configfile.cpp
new file mode 100644
index 00000000..5c1e4a65
--- /dev/null
+++ b/lanbrowsing/lisa/configfile.cpp
@@ -0,0 +1,136 @@
+/* configfile.cpp
+ *
+ * Copyright (c) 1998, 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "configfile.h"
+
+#include <iostream>
+#include <fstream>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+#define LISA_DEBUG 0
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"Config::"
+
+
+#define CFGBUFSIZE 16*1024
+
+Config::Config(const MyString& name/*,String path*/)
+{
+ char buff[CFGBUFSIZE],c;
+/* String s,empty="#############################################################################################################################################################";
+ String home=getenv("HOME");
+
+ if (!home.empty()) home+=String("/")+name;
+ if (fexists(home)==0)
+ {
+ home=path+"/"+name;
+ if (fexists(home)==0)
+ {
+ home=name;
+ if (fexists(home)==0) return;
+ };
+ };*/
+ std::ifstream inf(name.data());
+ if (!inf)
+ {
+ std::cout<<"could not open file "<<name<<std::endl;
+ return;
+ }
+ dcerr<<"Config(): opened file "<<name<<std::endl;
+ //read the file
+ char key[CFGBUFSIZE], value[CFGBUFSIZE];
+ do
+ {
+ char* buffStart=buff;
+ //inf.getline(buff,16*1024,'\n');
+ int bufSize(CFGBUFSIZE);
+ int lineBroken(0);
+ do
+ {
+ lineBroken=0;
+ inf.get(buffStart,bufSize,'\n');
+ inf.get(c);
+ int l=strlen(buffStart);
+ if (l==0)
+ break;
+ if (buffStart[l-1]=='\\')
+ {
+ buffStart=buffStart+l-1;
+ bufSize=bufSize+1-l;
+ lineBroken=1;
+ }
+ } while ((lineBroken) && (!inf.eof()));
+ //make it ignore comments
+ char *theChar=strchr(buff,'#');
+ if (theChar!=0)
+ *theChar='\0';
+ //now divide the line into key and value
+ theChar=strchr(buff,'=');
+ if (theChar!=0)
+ {
+ *theChar='\0';
+ key[0]='\0';
+ sscanf(buff,"%8000s",key);
+ //do we have something valid ?
+ if (key[0]!='\0')
+ {
+ //the char behind the = should be at least the terminating \0
+ // so I can be sure to access valid memory here, IMO
+ value[0]='\0';
+
+ strncpy(value,theChar+1,CFGBUFSIZE);
+ if (value[0]!='\0')
+ {
+ //here we can be sure that the list will only contain
+ //strings which are at least one char long
+ dcerr<<"Config(): adding "<<key<<std::endl;
+ m_entries[key]=value;
+ }
+ }
+ }
+ }
+ while (!inf.eof());
+}
+
+MyString Config::getEntry(const char *key, const char* defaultValue)
+{
+ if ((key==0) || (key[0]=='\0'))
+ return defaultValue;
+ if (m_entries.find(key)==m_entries.end())
+ return defaultValue;
+ return m_entries[key];
+}
+
+int Config::getEntry(const char *key, int defaultValue)
+{
+ char def[100];
+ sprintf(def,"%d",defaultValue);
+ MyString tmp=stripWhiteSpace(getEntry(key,def));
+ int i(0);
+ int ok=sscanf(tmp.c_str(),"%d",&i);
+ if (ok==1) return i;
+ return defaultValue;
+}
+
diff --git a/lanbrowsing/lisa/configfile.h b/lanbrowsing/lisa/configfile.h
new file mode 100644
index 00000000..1879e3b9
--- /dev/null
+++ b/lanbrowsing/lisa/configfile.h
@@ -0,0 +1,33 @@
+/* configfile.h
+ *
+ * Copyright (c) 1998,2000 Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef CONFIGFILE_H
+#define CONFIGFILE_H
+
+#include <map>
+
+#include "mystring.h"
+
+class Config
+{
+ public:
+ Config(const MyString& name);
+ MyString getEntry(const char *key, const char* defaultValue);
+ int getEntry(const char *key, int defaultValue);
+ protected:
+ std::map<MyString, MyString> m_entries;
+};
+
+#endif
diff --git a/lanbrowsing/lisa/ipaddress.cpp b/lanbrowsing/lisa/ipaddress.cpp
new file mode 100644
index 00000000..8dc98194
--- /dev/null
+++ b/lanbrowsing/lisa/ipaddress.cpp
@@ -0,0 +1,97 @@
+/* ipaddress.cpp
+ *
+ * Copyright (c) 1998, 1999, Alexander Neundorf, Lukas Pustina
+ * alexander.neundorf@rz.tu-ilmenau.de
+ * lukas@tronet.de
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "ipaddress.h"
+
+#include <iostream>
+
+IPAddress::IPAddress()
+{
+}
+
+IPAddress::IPAddress(const MyString& ip)
+{
+ if (ip.length()==0)
+ s="0.0.0.0";
+ else s=ip;
+ i=string2Int(s);
+ n=string2Struct(s);
+}
+
+IPAddress::IPAddress(unsigned int ip)
+{
+ i=ip;
+ s=int2String(i);
+ n.s_addr=htonl(i);
+ //n=string2Struct(s);
+}
+
+IPAddress::IPAddress(struct in_addr ip)
+{
+ n=ip;
+ s=struct2String(n);
+ i=string2Int(s);
+}
+
+IPAddress::IPAddress(int b1, int b2, int b3, int b4)
+{
+ char tmp[30];
+ sprintf(tmp,"%i.%i.%i.%i",b1,b2,b3,b4);
+ s=tmp;
+ i=string2Int(s);
+ n=string2Struct(s);
+}
+
+
+MyString IPAddress::int2String(unsigned int ip)
+{
+ MyString tmp("");
+ struct in_addr addr;
+ addr.s_addr=htonl(ip);
+ tmp=inet_ntoa(addr);
+ return tmp;
+}
+
+MyString IPAddress::struct2String(struct in_addr ip)
+{
+ return MyString(inet_ntoa(ip));
+}
+
+unsigned int IPAddress::string2Int(MyString ip)
+{
+// struct in_addr addr;
+// inet_aton(ip.c_str(),&addr);
+// cout<<addr.s_addr<<endl;
+// return ntohl(addr.s_addr);
+ return ntohl(inet_addr(ip.c_str()));
+}
+
+struct in_addr IPAddress::string2Struct(MyString ip)
+{
+ struct in_addr tmp;
+// inet_aton(ip.c_str(),&tmp);
+ tmp.s_addr = inet_addr(ip.c_str());
+ return tmp;
+}
+
+void IPAddress::print()
+{
+ std::cout<<"address as string: "<<s<<std::endl;
+ std::cout<<"address in host byte order: "<<std::ios::hex<<i<<std::ios::dec<<std::endl;
+ std::cout<<"address in network byte order: "<<std::ios::hex<<n.s_addr<<std::ios::dec<<std::endl;
+}
+
+
diff --git a/lanbrowsing/lisa/ipaddress.h b/lanbrowsing/lisa/ipaddress.h
new file mode 100644
index 00000000..ecca928a
--- /dev/null
+++ b/lanbrowsing/lisa/ipaddress.h
@@ -0,0 +1,55 @@
+/* ipaddress.h
+ *
+ * Copyright (c) 1998, 1999, Alexander Neundorf, Lukas Pustina
+ * alexander.neundorf@rz.tu-ilmenau.de
+ * lukas@tronet.de
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef IPADDRESS_H
+#define IPADDRESS_H
+
+#include "lisadefines.h"
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include "mystring.h"
+
+class IPAddress
+{
+ public:
+ IPAddress();
+ IPAddress(const MyString& ip);
+ IPAddress(unsigned int ip);
+ IPAddress(struct in_addr ip);
+ IPAddress(int b1, int b2, int b3, int b4);
+ //you know
+ MyString asString() {return s;};
+ //in host byte order
+ unsigned int asInt() {return i;};
+ //in network byte order
+ struct in_addr asStruct() {return n;};
+ /*operator= (IPAddress ip);
+ operator+= (unsigned int add);*/
+ void print();
+ private:
+ MyString int2String(unsigned int ip);
+ MyString struct2String(struct in_addr ip);
+ unsigned int string2Int(MyString ip);
+ struct in_addr string2Struct(MyString ip);
+ MyString s;
+ unsigned int i;
+ struct in_addr n;
+};
+
+#endif
diff --git a/lanbrowsing/lisa/lisadefines.h b/lanbrowsing/lisa/lisadefines.h
new file mode 100644
index 00000000..ac15d2c5
--- /dev/null
+++ b/lanbrowsing/lisa/lisadefines.h
@@ -0,0 +1,19 @@
+#ifndef LISADEFINES_H
+#define LISADEFINES_H
+
+
+#define MYVERSION "0.2.3"
+#define MY_ID 7741
+#define MYPORT 7741
+#define MAX_SPECS 128
+#define SELECT_TIMEOUT 7
+
+#define EXTRACONFIGSTYLE 0
+#define UNIXCONFIGSTYLE 1
+
+#define CONFIGFILEBASENAME "lisarc"
+#define STRICTCONFIGFILEBASENAME "reslisarc"
+
+#define STRICTMODEMAXHOSTS 64
+
+#endif
diff --git a/lanbrowsing/lisa/main.cpp b/lanbrowsing/lisa/main.cpp
new file mode 100644
index 00000000..7de59389
--- /dev/null
+++ b/lanbrowsing/lisa/main.cpp
@@ -0,0 +1,290 @@
+/* main.cpp
+ *
+ * Copyright (c) 1998-2000 Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "lisadefines.h"
+#include "netmanager.h"
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#define LISA_DEBUG 0
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"main "
+
+#include <iostream>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+// detect linux/glibc for the gnu style --args
+#if defined(__linux__) || defined(__linux) || defined(linux)
+# include <features.h>
+# ifdef __GLIBC__
+// only gnu libc has getopt.h... getopt(3) is defined to be in unistd.h
+// by POSIX.2
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+# include <getopt.h>
+# endif // __GLIBC__
+# define GNU_GETOPT
+#endif // linux
+
+
+void printVersion()
+{
+ const char * versionInfo=\
+ "\r\nThis is the LAN Information Server LISa "MYVERSION"\r\n"\
+ "It is free software according the GNU General Public License\r\n"\
+ "Copyright (c) 2000-2003 by Alexander Neundorf\r\n"\
+ "email: neundorf@kde.org\r\n";
+ std::cout<<versionInfo<<std::endl;
+}
+
+void usage()
+{
+ printVersion();
+ const char * usageInfo=\
+ "-v, --version prints out a short version info\n"\
+ "-u, --unix deprecated\n"\
+ "-k, --kde1 deprecated\n"\
+ "-K, --kde2 deprecated\n"\
+ " lisa now looks always first for $(HOME)/.lisarc, then for /etc/lisarc\n"\
+ "-c, --config=FILE read this and no other configuration file\n"\
+ "-q, --quiet start quiet without the greeting message\n"\
+ "-p, --port PORTNR start the server on this portnumber\n"\
+ " if you use this LISa won't be able to\n"\
+ " cooperate with other LISa's in the network\n"\
+ "-h, --help you are currently reading it ;-)\n";
+ std::cout<<usageInfo<<std::endl;
+//I thought this would be the way to check wether long options are supported...
+//#ifndef _GNU_SOURCE
+// std::cout<<"Please note that the long options are not supported on
+// this system"<<std::endl;
+//#endif
+}
+
+NetManager *manager(0);
+
+
+void destruct(int sigNumber)
+{
+ signal(sigNumber,SIG_IGN);
+ dcerr<<"signal caught: "<<sigNumber<<", exiting"<<std::endl;
+ //signal(sigNumber,&destruct);
+ if (manager!=0)
+ manager->~NetManager();
+ exit(0);
+}
+
+void readConfig(int sigNumber)
+{
+ dcerr<<"readConfig(): signal caught: "<<sigNumber<<std::endl;
+ signal(SIGHUP,SIG_IGN);
+ if (manager!=0)
+ manager->readConfig();
+ signal(SIGHUP,&readConfig);
+}
+
+void printState(int sigNumber)
+{
+ dcerr<<"printState(): signal caught: "<<sigNumber<<std::endl;
+ signal(SIGUSR1,SIG_IGN);
+ if (manager!=0)
+ manager->printState();
+ signal(SIGUSR1,&printState);
+}
+
+void setSignalHandler()
+{
+ signal(SIGHUP,&readConfig);
+ signal(SIGUSR1,&printState);
+
+ signal(SIGINT,&destruct);
+ signal(SIGQUIT,&destruct);
+ signal(SIGILL,&destruct);
+ signal(SIGTRAP,&destruct);
+ signal(SIGABRT,&destruct);
+ signal(SIGBUS,&destruct);
+ signal(SIGSEGV,&destruct);
+ signal(SIGUSR2,&destruct);
+ signal(SIGPIPE,&destruct);
+ signal(SIGALRM,&destruct);
+ signal(SIGTERM,&destruct);
+ signal(SIGFPE,&destruct);
+#ifdef SIGPOLL
+ signal(SIGPOLL, &destruct);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, &destruct);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, &destruct);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, &destruct);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, &destruct);
+#endif
+}
+
+#ifdef GNU_GETOPT
+static struct option const long_opts[] =
+{
+ {"version", no_argument, 0, 'v'},
+ {"quiet", no_argument, 0, 'q'},
+ {"unix", no_argument, 0, 'u'},
+ {"kde1", no_argument, 0, 'k'},
+ {"kde2", no_argument, 0, 'K'},
+ {"config", required_argument, 0, 'c'},
+ {"port", required_argument, 0, 'p'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+};
+#endif
+
+int main(int argc, char** argv)
+{
+ int quiet(0);
+ int c(0);
+ int configStyle(UNIXCONFIGSTYLE);
+ MyString configFile;
+ int portToUse(MYPORT);
+
+//I thought this would be the way to check wether long options are supported...
+#ifdef GNU_GETOPT
+ while ((c=getopt_long(argc, argv, "vqukKc:h", long_opts, 0))!=-1)
+#else
+ while ((c=getopt(argc, argv, "vqukKc:h"))!=-1)
+#endif
+
+ {
+ char *endp(0);
+ switch (c)
+ {
+ case 0:
+ break;
+ case 'v':
+ printVersion();
+ exit(0);
+ break;
+ case 'q':
+ quiet=1;
+ break;
+ case 'u':
+ case 'k':
+ case 'K':
+ std::cout<<"\a\nThe command line switches -k, -K, -u and \ntheir long versions "\
+ "--kde1, --kde2 and --unix are not supported anymore.\n"\
+ "Lisa will always first look for $(HOME)/.lisarc , then for /etc/lisarc.\n"\
+ "If your lisa configuration file was created using an older version of \n"\
+ "the KDE control center, copy the /root/.kde/share/config/lisarc to /etc and \n"\
+ "then start lisa without any command line options.\n"<<std::endl;
+ return 0;
+ break;
+
+ case 'c':
+ configFile = optarg;
+ configStyle = EXTRACONFIGSTYLE;
+ break;
+
+ case 'p':
+ portToUse=strtol(optarg,&endp,10);
+ if (endp!=0)
+ {
+ usage();
+ exit(0);
+ }
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ break;
+ }
+ }
+
+ //fork and let the parent exit
+ pid_t pid=fork();
+ if (pid>0)
+ {
+ //this is the parent
+ exit(0);
+ }
+ else if (pid<0)
+ {
+ std::cout<<"could not fork()"<<std::endl;
+ exit(0);
+ }
+ //we will only read/write to/from this socket in the child process
+ int rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
+ if (rawSocket==-1)
+ {
+ std::cout<<"could not create raw socket, root privileges required"<<std::endl;
+ std::cout<<"take a look at the README for more information"<<std::endl;
+ exit(0);
+ }
+ int bufferSize(60*1024);
+ int on(1);
+ setsockopt(rawSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize,
+ sizeof(bufferSize));
+ int result=setsockopt(rawSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on,
+ sizeof(on));
+ dcerr<<"setsockopt returns "<<result<<std::endl;
+ //dropping root privileges
+ //they will be regained once in the child process
+ //for creating a raw socket
+
+ //now dropping root privileges once and ever
+
+
+ setuid(getuid());
+ if (geteuid() != getuid())
+ _exit(255);
+
+ //according to R. Stevens the following three lines
+ //make daemons feel good :)
+ setsid();
+ chdir("/");
+ umask(0);
+
+ dcerr<<"starting, dropped root privileges"<<std::endl;
+ dcerr<<"port: "<<portToUse<<" file: "<<configFile<<std::endl;
+ NetManager netmanager(rawSocket,portToUse,configFile,configStyle,0);
+ manager=&netmanager;
+ dcerr<<"NetManager created"<<std::endl;
+ setSignalHandler();
+
+ netmanager.readConfig();
+ if (netmanager.prepare())
+ {
+ if (!quiet)
+ {
+ printVersion();
+ std::cout<<"\n\rrunning on port "<<portToUse<<"\n\rHave fun ! :-)"<<std::endl;
+ }
+ netmanager.run();
+ };
+ dcerr<<"server finished"<<std::endl;
+}
diff --git a/lanbrowsing/lisa/mystring.cpp b/lanbrowsing/lisa/mystring.cpp
new file mode 100644
index 00000000..2b7af2d2
--- /dev/null
+++ b/lanbrowsing/lisa/mystring.cpp
@@ -0,0 +1,52 @@
+#include "mystring.h"
+
+#include <ctype.h>
+
+//this one is taken from Qt/QCString
+
+MyString stripWhiteSpace(MyString str)
+{
+ if ( str.isEmpty() ) // nothing to do
+ return "";
+
+ char const *s = str.data();
+ MyString result = s;
+ int reslen = result.length();
+ if ( !isspace(s[0]) && !isspace(s[reslen-1]) )
+ return result; // returns a copy
+
+ s = result.data();
+ int start = 0;
+ int end = reslen - 1;
+ while ( isspace(s[start]) ) // skip white space from start
+ start++;
+ if ( s[start] == '\0' )
+ { // only white space
+ result.resize( 1 );
+ return "";
+ }
+
+ while ( end && isspace(s[end]) ) // skip white space from end
+ end--;
+
+ end -= start - 1;
+
+ result=str.mid(start,end);
+ //memmove( result.data(), &s[start], end );
+ //result.resize( end + 1 );
+ return result;
+}
+
+//mainly taken from qcstring
+int MyString::contains(char c)
+{
+ int count = 0;
+ char const *d = c_str();
+ if ( d==0 )
+ return 0;
+ while ( *d )
+ if ( *d++ == c )
+ count++;
+ return count;
+}
+
diff --git a/lanbrowsing/lisa/mystring.h b/lanbrowsing/lisa/mystring.h
new file mode 100644
index 00000000..20d40e59
--- /dev/null
+++ b/lanbrowsing/lisa/mystring.h
@@ -0,0 +1,42 @@
+#ifndef MYSTRING_H
+#define MYSTRING_H
+
+#include <string>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+
+class MyString: public std::string
+{
+ public:
+ MyString(const char c) : std::string(1,c) {};
+ MyString(const char* c)
+ : std::string(c==0?"":c)
+ {};
+ MyString(const std::string& s) : std::string(s) {};
+ MyString() : std::string() {};
+
+ int isEmpty() const {return empty();};
+ MyString mid(int pos, int length=-1) const {return substr(pos,length);};
+ MyString left(int len) const {return substr(0,len);};
+ MyString right(int len) const
+ {
+ if (len<1) return "";
+ else if (len<int(size())) return substr(size()-len);
+ else return (*this);
+ };
+
+ int contains(char c);
+ //char & operator[] (unsigned int i) {return ((string)(*this))[i];}
+ //operator const char* () const {return c_str();}
+ const char* data() const {return c_str();}
+ /*void setNum(int value) {char c[15]; bzero(c,15); sprintf(c,"%d",value); (*this)=c;};
+ void setNum(double value) {char c[25]; bzero(c,25); sprintf(c,"%g",value); (*this)=c;};
+ void simplifyWhiteSpace();*/
+};
+
+//taken from Qt/QCString
+MyString stripWhiteSpace(MyString str);
+
+#endif
+
diff --git a/lanbrowsing/lisa/netmanager.cpp b/lanbrowsing/lisa/netmanager.cpp
new file mode 100644
index 00000000..e652ce97
--- /dev/null
+++ b/lanbrowsing/lisa/netmanager.cpp
@@ -0,0 +1,1058 @@
+ /* netmanager.cpp
+ *
+ * Copyright (c) 2000, Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "config.h"
+
+#include "netmanager.h"
+#include "lisadefines.h"
+
+#include <iostream>
+#include <unistd.h>
+
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <strings.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#ifdef mdcerr
+#undef mdcerr
+#endif
+
+#define LISA_DEBUG 0
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"NetManager "
+#define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetManager "
+
+static void sig_child_handler(int)
+{
+ int saved_errno = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ; // repeat
+
+ errno = saved_errno;
+}
+
+NetManager::NetManager(int& rawSocketFD, int portToUse, MyString configFile, int configStyle, int strictMode)
+:NetScanner(rawSocketFD,strictMode)
+//,validator()
+,m_listenFD(-1)
+,m_bcFD(-1)
+,m_basePort(portToUse)
+,m_pipeFD(-1)
+,m_receiveBuffer(0)
+,m_receivedBytes(0)
+,m_childPid(0)
+,m_lastUpdate(0)
+
+,m_isInformed(0)
+,m_isBeingScanned(0)
+,m_firstRun(1)
+,m_serverServer(0)
+,m_servedThisPeriod(0)
+
+,m_serveCount(0)
+,m_refreshTime(60)
+,m_initialRefreshTime(60)
+,m_increasedRefreshTime(0)
+,m_broadcastAddress(0)
+,m_extraConfigFile(configFile)
+,m_configStyle(configStyle)
+,m_usedConfigFileName("")
+{
+ mdcerr<<"NetManager::NetManager"<<std::endl;
+ m_startedAt=time(0);
+
+ struct sigaction act;
+ act.sa_handler=sig_child_handler;
+ sigemptyset(&(act.sa_mask));
+ sigaddset(&(act.sa_mask), SIGCHLD);
+ // Make sure we don't block this signal. gdb tends to do that :-(
+ sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
+
+ act.sa_flags = SA_NOCLDSTOP;
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+
+ sigaction( SIGCHLD, &act, NULL );
+}
+
+NetManager::~NetManager()
+{
+ mdcerr<<"NetManager destructor ..."<<std::endl;
+ if (m_receiveBuffer!=0) delete [] m_receiveBuffer;
+ ::close(m_listenFD);
+ ::close(m_bcFD);
+}
+
+void NetManager::readConfig()
+{
+ m_usedConfigFileName=getConfigFileName();
+ if (m_usedConfigFileName.isEmpty())
+ {
+ std::cout<<"configfile not found"<<std::endl;
+ std::cout<<"use the command line option --help and \ntake a look at the README for more information"<<std::endl;
+ exit(1);
+ }
+
+ Config config(m_usedConfigFileName);
+ NetManager::configure(config);
+ NetScanner::configure(config);
+ validator.configure(config);
+ //after reading the new configuration we should really update
+ m_lastUpdate=0;
+}
+
+void NetManager::configure(Config& config)
+{
+ m_refreshTime=config.getEntry("UpdatePeriod",300);
+ MyString tmp=stripWhiteSpace(config.getEntry("BroadcastNetwork","0.0.0.0/255.255.255.255;"));
+ tmp=tmp+";";
+ mdcerr<<"NetManager::readConfig: "<<tmp<<std::endl;
+ MyString netAddressStr=tmp.left(tmp.find('/'));
+ tmp=tmp.mid(tmp.find('/')+1);
+ tmp=tmp.left(tmp.find(';'));
+ mdcerr<<"NetManager::readConfig: broadcastNet "<<netAddressStr<<" with mask "<<tmp<<std::endl;
+ int netMask=inet_addr(tmp.c_str());
+ int netAddress=inet_addr(netAddressStr.c_str());
+ m_broadcastAddress= netAddress | (~netMask);
+ mdcerr<<"NetManager::readConfig: net "<<std::ios::hex<<netAddress<<" with mask "<<netMask<<" gives "<<m_broadcastAddress<<std::endl;
+
+ //maybe this way we can avoid that all servers on the net send
+ //their requests synchronously, since now the refreshtime isn't
+ //always the eact value of m_refreshTime, but differs always slightly
+ if ((m_refreshTime%SELECT_TIMEOUT)==0) m_refreshTime+=2;
+ //some limits from half a minute to half an hour
+ if (m_refreshTime<30) m_refreshTime=30;
+ if (m_refreshTime>1800) m_refreshTime=1800;
+ m_initialRefreshTime=m_refreshTime;
+}
+
+int NetManager::prepare()
+{
+ mdcerr<<"NetManager::prepare"<<std::endl;
+
+ ::close(m_listenFD);
+ ::close(m_bcFD);
+ int result(0);
+ if (m_strictMode)
+ {
+ m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, 0);
+ //m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, IPPROTO_TCP);
+ MyString socketName("/tmp/resLisa-");
+ struct passwd *user = getpwuid( getuid() );
+ if ( user )
+ socketName+=user->pw_name;
+ else
+ //should never happen
+ socketName+="???";
+ ::unlink(socketName.data());
+ sockaddr_un serverAddr;
+ if (socketName.length() >= sizeof(serverAddr.sun_path))
+ {
+ std::cout<<"NetManager::prepare: your user name \""<<user->pw_name<<"\" is too long, exiting."<<std::endl;
+ return 0;
+ }
+ memset((void*)&serverAddr, 0, sizeof(serverAddr));
+ serverAddr.sun_family=AF_LOCAL;
+ strncpy(serverAddr.sun_path,socketName.data(),sizeof(serverAddr.sun_path));
+ result=::bind(m_listenFD,(sockaddr*) &serverAddr,sizeof(serverAddr));
+ if (result!=0)
+ {
+ std::cout<<"NetManager::prepare: bind (UNIX socket) failed, errno: "<<errno<<std::endl;
+ return 0;
+ }
+ }
+ else
+ {
+ //create a listening port and listen
+ m_listenFD = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (m_listenFD==-1)
+ {
+ std::cout<<"NetManager::prepare: socket(TCP) failed, errno: "<<errno<<std::endl;
+ return 0;
+ }
+
+ sockaddr_in serverAddress;
+// bzero((char*)&serverAddress, sizeof(serverAddress));
+ memset((void*)&serverAddress, 0, sizeof(serverAddress));
+ serverAddress.sin_family = AF_INET;
+ serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
+ serverAddress.sin_port = htons(m_basePort);
+
+ int on(1);
+ result=setsockopt(m_listenFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
+ if (result!=0)
+ {
+ std::cout<<"NetManager::prepare: setsockopt(SO_REUSEADDR) failed"<<std::endl;
+ return 0;
+ }
+ result=::bind(m_listenFD, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
+ if (result!=0)
+ {
+ std::cout<<"NetManager::prepare: bind (TCP) failed, errno: "<<errno<<std::endl;
+ return 0;
+ }
+ }
+ result=::listen(m_listenFD, 32);
+ if (result!=0)
+ {
+ std::cout<<"NetManager::prepare: listen failed"<<std::endl;
+ return 0;
+ }
+ mdcerr<<"NetManager::prepare: listening on port "<<m_basePort<<"..."<<std::endl;
+
+ return 1;
+}
+
+void NetManager::generateFDset(fd_set *tmpFDs)
+{
+ mdcerr<<"NetManager::generateFDset"<<std::endl;
+
+ FD_ZERO(tmpFDs);
+ FD_SET(m_listenFD,tmpFDs);
+ mdcerr<<"NetManager::generateFDset: adding listen FD="<<m_listenFD<<std::endl;
+// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next())
+ for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++)
+ if (tmpClient->fd()!=-1)
+ {
+ mdcerr<<"NetManager::generateFDset: adding client FD="<<tmpClient->fd()<<std::endl;
+ FD_SET(tmpClient->fd(),tmpFDs);
+ }
+
+ if (m_pipeFD!=-1)
+ {
+ mdcerr<<"NetManager::generateFDset: adding pipeFD="<<m_pipeFD<<std::endl;
+ FD_SET(m_pipeFD,tmpFDs);
+ }
+
+ if ((m_bcFD!=-1) && (!m_strictMode))
+ {
+ mdcerr<<"NetManager::generateFDset: adding m_bcFD="<<m_bcFD<<std::endl;
+ FD_SET(m_bcFD,tmpFDs);
+ }
+}
+
+int NetManager::waitForSomethingToHappen(fd_set *tmpFDs)
+{
+ mdcerr<<"NetManager::waitForSomethingToHappen for 10 seconds"<<std::endl;
+ if (m_firstRun)
+ {
+ tv.tv_sec = 1;
+ m_firstRun=0;
+ }
+ else
+ tv.tv_sec = SELECT_TIMEOUT;
+
+ tv.tv_usec = 0;
+ //generateFDset(tmpFDs);
+ int result=select(getMaxFD(),tmpFDs,0,0,&tv);
+ if (result>0) return 1;
+ else return 0;
+}
+
+int NetManager::getMaxFD()
+{
+ int maxFD(m_listenFD);
+// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next())
+ for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++)
+ if (tmpClient->fd()>maxFD)
+ maxFD=tmpClient->fd();
+
+ if (m_pipeFD>maxFD) maxFD=m_pipeFD;
+ if (m_bcFD>maxFD) maxFD=m_bcFD;
+
+ mdcerr<<"NetManager::getMaxFD()="<<maxFD+1<<std::endl;
+ return maxFD+1;
+}
+
+int fileReadable(const MyString& filename)
+{
+ FILE *file=::fopen(filename.data(), "r");
+ if (file==0)
+ return 0;
+ fclose(file);
+ return 1;
+}
+
+MyString NetManager::getConfigFileName()
+{
+ MyString tmpBase(CONFIGFILEBASENAME);
+
+ if (m_strictMode)
+ tmpBase=STRICTCONFIGFILEBASENAME;
+
+ if (!m_extraConfigFile.isEmpty())
+ m_configStyle=EXTRACONFIGSTYLE;
+
+ MyString tmpFilename;
+ if (m_configStyle==EXTRACONFIGSTYLE)
+ {
+ tmpFilename=m_extraConfigFile;
+ if (fileReadable(tmpFilename))
+ return tmpFilename;
+ return "";
+ }
+ else if (m_configStyle==UNIXCONFIGSTYLE)
+ {
+ tmpFilename=getenv("HOME");
+ tmpFilename+=MyString("/.")+tmpBase;
+ if (fileReadable(tmpFilename))
+ return tmpFilename;
+ tmpFilename="/etc/";
+ tmpFilename+=tmpBase;
+ if (fileReadable(tmpFilename))
+ return tmpFilename;
+ return "";
+ }
+/* else if (m_configStyle==KDE1CONFIGSTYLE)
+ {
+ tmpFilename=getenv("HOME");
+ tmpFilename+=MyString("/.kde/share/config/")+tmpBase;
+ if (fileReadable(tmpFilename))
+ return tmpFilename;
+ tmpFilename=getenv("KDEDIR");
+ tmpFilename+=MyString("/share/config/")+tmpBase;
+ if (fileReadable(tmpFilename))
+ return tmpFilename;
+ return "";
+ }
+ else if (m_configStyle==KDE2CONFIGSTYLE)
+ {
+ FILE *kdeConfig=popen("kde-config --path config","r");
+ if (kdeConfig==0)
+ {
+ std::cout<<"could not execute kde-config, check your KDE 2 installation\n"<<std::endl;
+ return "";
+ };
+ //this should be large enough
+ char buf[4*1024];
+ memset(buf,0,4*1024);
+ fgets(buf,4*1024-1,kdeConfig);
+ // Warning: The return value of plcose may be incorrect due to the
+ // SIGCHLD handler that is installed. Ignore it!
+ pclose(kdeConfig);
+ int length=strlen(buf);
+ if (buf[length-1]=='\n') buf[length-1]='\0';
+ MyString kdeDirs(buf);
+ while (kdeDirs.contains(':'))
+ {
+ MyString nextDir=kdeDirs.left(kdeDirs.find(':'));
+ kdeDirs=kdeDirs.mid(kdeDirs.find(':')+1);
+ nextDir=nextDir+tmpBase;
+ mdcerr<<"trying to open "<<nextDir<<std::endl;
+ if (fileReadable(nextDir))
+ return nextDir;
+ };
+ kdeDirs=kdeDirs+tmpBase;
+ mdcerr<<"trying to open "<<kdeDirs<<std::endl;
+ if (fileReadable(kdeDirs))
+ return kdeDirs;
+ kdeDirs="/etc/";
+ kdeDirs=kdeDirs+tmpBase;
+ if (fileReadable(kdeDirs))
+ return kdeDirs;
+ return "";
+ }*/
+ return "";
+}
+
+int NetManager::run()
+{
+ int continueMainLoop(1);
+ //not forever
+ while(continueMainLoop)
+ {
+ mdcerr<<"\nNetManager::run: next loop: "<<clients.size()<<" clients"<<std::endl;
+ fd_set tmpFDs;
+ generateFDset(&tmpFDs);
+
+ int result=waitForSomethingToHappen(&tmpFDs);
+ mdcerr<<"NetManager::run: something happened..."<<std::endl;
+ //timeout
+ if (result==0)
+ {
+ mdcerr<<"NetManager::run: serverServer=="<<m_serverServer<<std::endl;
+ mdcerr<<"NetManager::run: scanning after timeout"<<std::endl;
+ scan();
+ }
+ else
+ {
+ //a new connection ?
+ if (FD_ISSET(m_listenFD,&tmpFDs))
+ {
+ mdcerr<<"NetManager::run: on m_listenFD"<<std::endl;
+ struct sockaddr_in clientAddress;
+ socklen_t clilen(sizeof(clientAddress));
+// bzero((char*)&clientAddress, clilen);
+ memset((void*)&clientAddress,0,sizeof(clientAddress));
+ int connectionFD=::accept(m_listenFD,(struct sockaddr *) &clientAddress, &clilen);
+ if ((validator.isValid(clientAddress.sin_addr.s_addr)) || (m_strictMode))
+ {
+ mdcerr<<"NetManager::run: adding client"<<std::endl;
+ addClient(connectionFD);
+ m_servedThisPeriod=1;
+ m_refreshTime=m_initialRefreshTime;
+ m_increasedRefreshTime=0;
+ }
+ else
+ {
+ mdcerr<<"NetManager::run: kicking client"<<std::endl;
+ ::close(connectionFD);
+ }
+ }
+ checkClientsAndPipes(&tmpFDs);
+ }
+ serveAndClean();
+ }
+ return 1;
+}
+
+void NetManager::addClient(int socketFD)
+{
+ mdcerr<<"NetManager::addClient on FD="<<socketFD<<std::endl;
+ if (socketFD==-1) return;
+ Client c(this,socketFD,0);
+ clients.push_back(c);
+}
+
+void NetManager::checkClientsAndPipes(fd_set *tmpFDs)
+{
+ mdcerr<<"NetManager::checkClientsAndPipes()"<<std::endl;
+ //actually the clients should not send anything
+// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next())
+ for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++)
+ {
+ mdcerr<<"NetManager::checkClientsAndPipes: checking client"<<std::endl;
+ if (FD_ISSET(tmpClient->fd(),tmpFDs))
+ {
+ mdcerr<<"NetManager::checkClientsAndPipes: client sent something"<<std::endl;
+ tmpClient->read();
+ }
+ }
+
+ //now check wether we received a broadcast
+ //m_bcFD should always be -1 in strictMode
+ if ((m_bcFD!=-1) && (!m_strictMode))
+ {
+ mdcerr<<"NetManager::checkClientsAndPipe: checking bcFD"<<std::endl;
+ //yes !
+ if (FD_ISSET(m_bcFD,tmpFDs))
+ answerBroadcast();
+ }
+
+ //read the stuff from the forked pipe
+ if (m_pipeFD!=-1)
+ {
+ mdcerr<<"NetManager::checkClientsAndPipe: checking pipe"<<std::endl;
+ if (FD_ISSET(m_pipeFD,tmpFDs))
+ {
+ mdcerr<<"NetManager::checkClientsAndPipes: pipe sent something"<<std::endl;
+ int result=readDataFromFD(m_pipeFD);
+ if (result!=1)
+ {
+ ::close(m_pipeFD);
+ m_pipeFD=-1;
+ mdcerr<<"NetManager::checkClientsAndPipes: everything read from pipe from proc "<<m_childPid<<std::endl;
+ processScanResults();
+ }
+ }
+ }
+}
+
+void NetManager::answerBroadcast()
+{
+ //actually we should never get here in strictMode
+ if (m_strictMode) return;
+
+ //this one is called only in checkClientsAndPipes()
+ //if we are sure that we received something on m_bcFD
+ //so we don't need to check here again
+
+ mdcerr<<"NetManager::answerBroadcast: received BC"<<std::endl;
+ struct sockaddr_in sAddr;
+ socklen_t length(sizeof(sockaddr_in));
+ char buf[1024];
+ int result=recvfrom(m_bcFD, (char*)buf, 1024, 0, (sockaddr*)&sAddr,&length);
+ mdcerr<<"NetManager::answerBroadcast: received successfully"<<std::endl;
+ //did recvfrom() succeed ?
+ //our frame is exactly MYFRAMELENGTH bytes big, if the received one has a different size,
+ //it must be something different
+ if (result!=MYFRAMELENGTH) return;
+ //if it has the correct size, it also must have the correct identifier
+ MyFrameType *frame=(MyFrameType*)(void*)buf;
+ if ((ntohl(frame->id)!=MY_ID) ||
+ ((ntohl(frame->unused1)==getpid()) && (ntohl(frame->unused2)==m_startedAt)))
+ {
+ mdcerr<<"NetManager::answerBroadcast: must be the same machine"<<std::endl;
+ return;
+ }
+
+ //mdcerr<<"received "<<ntohl(buf[0])<<" from "<<inet_ntoa(sAddr.sin_addr)<<hex<<" ";
+ //mdcerr<<sAddr.sin_addr.s_addr<<" //"<<ntohl(sAddr.sin_addr.s_addr)<<dec<<std::endl;
+ //will we answer this request ?
+ if (!validator.isValid(sAddr.sin_addr.s_addr))
+ {
+ mdcerr<<"NetManager::answerBroadcast: invalid sender"<<std::endl;
+ return;
+ }
+
+ //create the answering socket
+
+ int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (answerFD==-1)
+ {
+ mdcerr<<"NetManager::answerBroadcast: could not create answering socket"<<std::endl;
+ return;
+ }
+
+ sAddr.sin_family=AF_INET;
+ sAddr.sin_port=htons(m_basePort+1);
+ MyFrameType answerFrame;
+ answerFrame.id=htonl(MY_ID);
+ answerFrame.unused1=0;
+ answerFrame.unused2=0;
+ //don't care about the result
+ mdcerr<<"NetManager::answerBroadcast: sending answer"<<std::endl;
+
+ result=::sendto(answerFD,(char*)&answerFrame,sizeof(answerFrame),0,(sockaddr*)&sAddr,length);
+ mdcerr<<"sent "<<result<<" byte using sendto"<<std::endl;
+ ::close(answerFD);
+ //sent answer
+}
+
+void NetManager::serveAndClean()
+{
+ //try to serve the request
+// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next())
+ for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++)
+ {
+ mdcerr<<"NetManager::serveAndClean: trying to get info"<<std::endl;
+ tmpClient->tryToGetInfo();
+ }
+
+ //try to delete served the clients
+ /** PM: substituted STL code
+ for (Client* tmpClient=clients.first();tmpClient!=0; tmpClient=clients.next())
+ {
+ //if we served the client or if he's already half a minute
+ //connected remove it
+ //this way we get rid of clients if something went wrong and
+ //maybe it's even a security point, I don't know
+ if ((tmpClient->done()) || (tmpClient->age()>30))
+ {
+ mdcerr<<"NetManager::serveAndClean: removing Client"<<std::endl;
+ clients.remove(tmpClient);
+ tmpClient=clients.first();
+ }
+ }*/
+ clients.remove_if(client_is_done());
+}
+
+void NetManager::scan()
+{
+ mdcerr<<"NetManager::scan()"<<std::endl;
+ if (isBeingScanned()) return;
+
+ time_t currentTime=time(0);
+ mdcerr<<"currentTime: "<<currentTime<<" lastUpdate: "<<m_lastUpdate<<std::endl;
+ if ((currentTime-m_lastUpdate)<m_refreshTime) return;
+ mdcerr<<"NetManager::scan: scanning..."<<std::endl;
+
+ m_isBeingScanned=1;
+ int fileDescr[2];
+ ::pipe(fileDescr);
+ mdcerr<<"NetScanner::scan: file-descr[0]: "<<fileDescr[0]<<std::endl;
+ mdcerr<<"NetScanner::scan: file-descr[1]: "<<fileDescr[1]<<std::endl;
+ int pid=fork();
+ if (pid==-1)
+ {
+ mdcerr<<"NetScanner::scan: error occurred"<<std::endl;
+ return;
+ }
+ else if (pid!=0)
+ {
+ //parent
+ ::close(fileDescr[1]);
+ m_pipeFD=fileDescr[0];
+ m_childPid=pid;
+ return;
+ }
+ //child
+ procId="** child ** ";
+ mdcerr<<" NetManager::scan: a child was born"<<std::endl;
+ if (m_strictMode)
+ {
+ mdcerr<<" NetManager::scan: scanning myself in strict mode, becoming serverServer"<<std::endl;
+ doScan();
+ //in the child we don't have to call setServerServer()
+ //since this opens the listening socket, what has to be done
+ //in the parent process
+ m_serverServer=1;
+ }
+ else
+ {
+ int serverAddress=findServerServer();
+ if (serverAddress==0)
+ {
+ mdcerr<<" NetManager::scan: scanning myself, becoming serverServer"<<std::endl;
+ doScan();
+ //in the child we don't have to call setServerServer()
+ //since this opens the listening socket, what has to be done
+ //in the parent process
+ m_serverServer=1;
+ }
+ else
+ {
+ mdcerr<<" NetManager::scan: getting list from serverServer"<<std::endl;
+ getListFromServerServer(serverAddress);
+ m_serverServer=0;
+ }
+ }
+ mdcerr<<" NetScanner::scan: sending information to parent process"<<std::endl;
+ writeDataToFD(fileDescr[1],m_serverServer);
+ mdcerr<<" NetScanner::scan: closed FD: "<<::close(fileDescr[1])<<std::endl;
+ mdcerr<<" NetScanner::scan: exiting now"<<std::endl;
+ ::exit(0);
+}
+
+int NetManager::writeDataToFD(int fd, int serverServer)
+{
+ mdcerr<<" NetManager::writeDataToFD fd="<<fd<<std::endl;
+ m_serveCount++;
+ char buffer[1024];
+
+ int length;
+// for (Node* tmpNode=hostList->first(); tmpNode!=0; tmpNode=hostList->next())
+ for (std::list<Node>::iterator tmpNode=hostList->begin(); tmpNode!=hostList->end(); tmpNode++)
+ {
+ sprintf(buffer,"%u %s\n",tmpNode->ip,tmpNode->name.left(1000).c_str());
+ length=strlen(buffer)+1;
+ const char *currentBuf=buffer;
+ //make sure that everything is written
+ while (length>0)
+ {
+ int result=::write(fd,currentBuf,length);
+ mdcerr<<"NetManager::writeDataToFD: wrote "<<result<<" bytes"<<std::endl;
+ if (result==-1)
+ {
+ perror("hmmpf: ");
+ return 0;
+ }
+ length-=result;
+ currentBuf+=result;
+ }
+ }
+ //and a last line
+ sprintf(buffer,"%d succeeded\n",serverServer);
+ length=strlen(buffer)+1;
+ const char *currentBuf=buffer;
+ //make sure that everything is written
+ while (length>0)
+ {
+ int result=::write(fd,currentBuf,length);
+ if (result==-1) return 0;
+ length-=result;
+ currentBuf+=result;
+ }
+ return 1;
+}
+
+int NetManager::readDataFromFD(int fd)
+{
+ mdcerr<<"NetManager::readDataFromFD"<<std::endl;
+ char tmpBuf[64*1024];
+ int result=::read(fd,tmpBuf,64*1024);
+ mdcerr<<"NetManager::readDataFromFD: read "<<result<<" bytes"<<std::endl;
+ if (result==-1) return -1;
+ if (result==0)
+ {
+ mdcerr<<"NetManager::readDataFromFD: FD was closed"<<std::endl;
+ return 0;
+ }
+ char *newBuf=new char[m_receivedBytes+result];
+ if (m_receiveBuffer!=0) memcpy(newBuf,m_receiveBuffer,m_receivedBytes);
+ memcpy(newBuf+m_receivedBytes,tmpBuf,result);
+ m_receivedBytes+=result;
+ if (m_receiveBuffer!=0) delete [] m_receiveBuffer;
+ m_receiveBuffer=newBuf;
+ // too much data - abort at 2MB to avoid memory exhaustion
+ if (m_receivedBytes>2*1024*1024)
+ return 0;
+
+ return 1;
+}
+
+int NetManager::processScanResults()
+{
+ mdcerr<<"NetManager::processScanResults"<<std::endl;
+ if (m_receiveBuffer==0) return 0;
+ std::list<Node> *newNodes=new std::list<Node>;
+
+ char *tmpBuf=m_receiveBuffer;
+ int bytesLeft=m_receivedBytes;
+ mdcerr<<"m_receivedBytes: "<<m_receivedBytes<<" bytesLeft: "<<bytesLeft<<std::endl;
+ //this should be large enough for a name
+ //and the stuff which is inserted into the buffer
+ //comes only from ourselves ... or attackers :-(
+ char tmpName[1024*4];
+ while (bytesLeft>0)
+ {
+ int tmpIP=2; // well, some impossible IP address, 0 and 1 are already used for the last line of output
+ tmpName[0]='\0';
+ if ((memchr(tmpBuf,0,bytesLeft)==0) || (memchr(tmpBuf,int('\n'),bytesLeft)==0))
+ {
+ delete newNodes;
+ hostList->clear();
+
+ m_lastUpdate=time(0);
+ delete [] m_receiveBuffer;
+ m_receiveBuffer=0;
+ m_receivedBytes=0;
+ m_isInformed=1;
+ m_isBeingScanned=0;
+ return 0;
+ }
+ //mdcerr<<"NetManager::processScanResults: processing -"<<tmpBuf;
+ //since we check for 0 and \n with memchr() we can be sure
+ //at this point that tmpBuf is correctly terminated
+ int length=strlen(tmpBuf)+1;
+ if (length<(4*1024))
+ sscanf(tmpBuf,"%u %s\n",&tmpIP,tmpName);
+
+ bytesLeft-=length;
+ tmpBuf+=length;
+ mdcerr<<"length: "<<length<<" bytesLeft: "<<bytesLeft<<std::endl;
+ if ((bytesLeft==0) && ((tmpIP==0) ||(tmpIP==1)) && (strstr(tmpName,"succeeded")!=0))
+ {
+ mdcerr<<"NetManager::processScanResults: succeeded :-)"<<std::endl;
+ delete hostList;
+ hostList=newNodes;
+
+ m_lastUpdate=time(0);
+ delete [] m_receiveBuffer;
+ m_receiveBuffer=0;
+ m_receivedBytes=0;
+ m_isInformed=1;
+ m_isBeingScanned=0;
+ adjustRefreshTime(tmpIP);
+ enableServerServer(tmpIP);
+ //m_serverServer=tmpIP;
+
+ return 1;
+ }
+ else if (tmpIP!=2)
+ {
+ //mdcerr<<"NetManager::processScanResults: adding host: "<<tmpName<<" with ip: "<<tmpIP<<std::endl;
+ newNodes->push_back(Node(tmpName,tmpIP));
+ }
+ }
+ //something failed :-(
+ delete newNodes;
+ hostList->clear();
+
+ m_lastUpdate=time(0);
+ delete [] m_receiveBuffer;
+ m_receiveBuffer=0;
+ m_receivedBytes=0;
+ m_isInformed=1;
+ m_isBeingScanned=0;
+
+ mdcerr<<"NetScanner::processScanResult: finished"<<std::endl;
+ return 0;
+}
+
+void NetManager::adjustRefreshTime(int serverServer)
+{
+ //we are becoming server server
+ if (((m_serverServer==0) && (serverServer)) || (m_servedThisPeriod))
+ {
+ m_increasedRefreshTime=0;
+ m_refreshTime=m_initialRefreshTime;
+ }
+ //nobody accessed the server since the last update
+ //so increase the refresh time
+ //this should happen more seldom to the serverServer
+ //than to others
+ else
+ {
+ //up to the 16 times refresh time
+ if (m_increasedRefreshTime<4)
+ {
+ m_increasedRefreshTime++;
+ m_refreshTime*=2;
+ };
+ };
+ m_servedThisPeriod=0;
+
+}
+
+void NetManager::enableServerServer(int on)
+{
+ mdcerr<<"NetManager::enableServerServer: "<<on<<std::endl;
+ //in strictMode we don't listen to broadcasts from the network
+ if (m_strictMode) return;
+
+ m_serverServer=on;
+ if (on)
+ {
+ //nothing has to be done
+ if (m_bcFD!=-1) return;
+ // otherwise create the socket which will listen for broadcasts
+ sockaddr_in my_addr;
+ my_addr.sin_family=AF_INET;
+ my_addr.sin_port=htons(m_basePort);
+ my_addr.sin_addr.s_addr=0;
+
+ m_bcFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (m_bcFD==-1)
+ {
+ mdcerr<<"NetManager::enableServerServer: socket() failed"<<std::endl;
+ m_serverServer=0;
+ return;
+ };
+ int on(1);
+ int result=setsockopt(m_bcFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
+ if (result!=0)
+ {
+ mdcerr<<"NetManager::enableServerServer: setsockopt(SO_REUSEADDR) failed"<<std::endl;
+ m_serverServer=0;
+ ::close(m_bcFD);
+ m_bcFD=-1;
+ return;
+ };
+ result=::bind(m_bcFD, (struct sockaddr *) &my_addr, sizeof(my_addr));
+ if (result!=0)
+ {
+ mdcerr<<"NetManager::enableServerServer: bind (UDP) failed"<<std::endl;
+ m_serverServer=0;
+ ::close(m_bcFD);
+ m_bcFD=-1;
+ return;
+ };
+ }
+ else
+ {
+ ::close(m_bcFD);
+ m_bcFD=-1;
+ };
+}
+
+int NetManager::findServerServer()
+{
+ mdcerr<<" NetManager::findServerServer"<<std::endl;
+ //actually this should never be called in strictMode
+ if (m_strictMode) return 0;
+ if (!validator.isValid(m_broadcastAddress))
+ {
+ mdcerr<<" NetManager::findServerServer: invalid broadcastAddress"<<std::endl;
+ return 0;
+ };
+ //create a socket for sending the broadcast
+ //we don't have to set SO_REUSEADDR, since we don't call bind()
+ //to this socket
+ int requestFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (requestFD==-1)
+ {
+ mdcerr<<" NetManager::findServerServer: could not create request socket"<<std::endl;
+ return 0;
+ };
+
+ int on(1);
+ //this is actually the only socket which will send broacasts
+ int result=setsockopt(requestFD, SOL_SOCKET, SO_BROADCAST,(char*)&on, sizeof(on));
+ if (result!=0)
+ {
+ mdcerr<<"setsockopt(SO_BROADCAST) failed"<<std::endl;
+ ::close(requestFD);
+ return 0;
+ };
+
+ //create a socket for receiving the answers
+ int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (answerFD==-1)
+ {
+ mdcerr<<" NetManager::findServerServer"<<std::endl;
+ ::close(requestFD);
+ return 0;
+ };
+
+ //since this socket will be bound, we have to set SO_REUSEADDR
+ result=setsockopt(answerFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
+ if (result!=0)
+ {
+ mdcerr<<"setsockopt(SO_REUSEADDR) failed"<<std::endl;
+ ::close(answerFD);
+ ::close(requestFD);
+ return 0;
+ };
+
+ sockaddr_in my_addr;
+ my_addr.sin_family=AF_INET;
+ my_addr.sin_port=htons(m_basePort+1);
+ my_addr.sin_addr.s_addr=0;
+ //this one has to be bound
+ result=::bind(answerFD, (struct sockaddr *) &my_addr, sizeof(my_addr));
+ if (result!=0)
+ {
+ mdcerr<<"bind (UDP) failed"<<std::endl;
+ ::close(answerFD);
+ ::close(requestFD);
+ return 0;
+ };
+
+ //now send the broadcast
+ struct sockaddr_in sAddr;
+ sAddr.sin_addr.s_addr=m_broadcastAddress;
+ sAddr.sin_family=AF_INET;
+ sAddr.sin_port=htons(m_basePort);
+ socklen_t length(sizeof(sockaddr_in));
+ mdcerr<<" NetManager::findServerServer: broadcasting to: "
+ <<std::ios::hex<<m_broadcastAddress<<std::ios::dec<<std::endl;
+
+ MyFrameType requestFrame;
+ requestFrame.id=htonl(MY_ID);
+ requestFrame.unused1=htonl(getppid());
+ requestFrame.unused2=htonl(m_startedAt);
+
+ result=::sendto(requestFD,(char*)&requestFrame,sizeof(requestFrame),0,(sockaddr*)&sAddr,length);
+ ::close(requestFD);
+ if (result!=MYFRAMELENGTH)
+ {
+ mdcerr<<" NetManager::findServerServer: sent wrong number of bytes: "<<result<<std::endl;
+ ::close(answerFD);
+ return 0;
+ };
+ //wait for an answer
+ struct timeval tv;
+ tv.tv_sec=0;
+ tv.tv_usec=1000*250; //0.1 sec
+ fd_set fdSet;
+ FD_ZERO(&fdSet);
+ FD_SET(answerFD,&fdSet);
+ result=select(answerFD+1,&fdSet,0,0,&tv);
+ if (result<0)
+ {
+ mdcerr<<" NetManager::findServerServer: select() failed: "<<result<<std::endl;
+ mdcerr<<" NetManager::findServerServer: answerFD="<<answerFD<<std::endl;
+ perror("select:");
+ ::close(answerFD);
+ return 0;
+ }
+ if (result==0)
+ {
+ mdcerr<<" NetManager::findServerServer: nobody answered "<<std::endl;
+ ::close(answerFD);
+ return 0;
+ }
+ mdcerr<<"received offer "<<std::endl;
+ struct sockaddr_in addr;
+ length=sizeof(sockaddr_in);
+ char buf[1024];
+ result=recvfrom(answerFD, (char*)buf, 1024, 0, (sockaddr*) &addr,&length);
+ if (result!=MYFRAMELENGTH)
+ {
+ mdcerr<<" NetManager::findServerServer: wrong number of bytes: "<<result<<std::endl;
+ ::close(answerFD);
+ return 0;
+ };
+ MyFrameType *frame=(MyFrameType*)(void*)buf;
+ //wrong identifier ?
+ if (ntohl(frame->id)!=MY_ID)
+ {
+ mdcerr<<" NetManager::findServerServer: wrong id"<<std::endl;
+ ::close(answerFD);
+ return 0;
+ };
+
+ mdcerr<<"received from "<<inet_ntoa(addr.sin_addr)<<std::endl;
+
+ ::close(answerFD);
+ //return the ip of the server server in network byte order
+ return addr.sin_addr.s_addr;
+}
+
+void NetManager::getListFromServerServer( int address)
+{
+ mdcerr<<"NetManager::getListFromServerServer"<<std::endl;
+ //actually we should never get here in strictMode
+ if (m_strictMode) return;
+ //open a tcp socket to the serverserver
+ int serverServerFD=::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (serverServerFD==-1)
+ return;
+ sockaddr_in addr;
+ //we get the address already in network byte order
+ addr.sin_addr.s_addr=address;
+ addr.sin_family=AF_INET;
+ addr.sin_port=htons(m_basePort);
+ int result=::connect(serverServerFD,(sockaddr*)&addr,sizeof(addr));
+ if (result!=0)
+ {
+ ::close(serverServerFD);
+ return;
+ };
+ do
+ {
+ result=readDataFromFD(serverServerFD);
+ } while (result==1);
+ ::close(serverServerFD);
+ processScanResults();
+ mdcerr<<"NetManager::getListFromServerServer succeeded"<<std::endl;
+}
+
+void NetManager::printState()
+{
+ std::cerr<<"LAN Information Server Lisa "MYVERSION"\nAlexander Neundorf <neundorf@kde.org>\n";
+ std::cerr<<"Reading options from config file: "<<m_usedConfigFileName<<std::endl;
+ std::cerr<<"StrictMode: "<<m_strictMode<<std::endl;
+ std::cerr<<"ServerServer: "<<m_serverServer<<std::endl;
+ std::cerr<<"UseNmblookup: "<<m_useNmblookup<<std::endl;
+ std::cerr<<"Pinging: "<<ipRangeStr<<std::endl;
+ std::cerr<<"Allowed hosts: "<<validator.validAddresses()<<std::endl;
+ std::cerr<<"Broadcasting to: "<<std::ios::hex<<ntohl(m_broadcastAddress)<<std::ios::dec<<std::endl;
+ std::cerr<<"Initial update period: "<<m_initialRefreshTime<<" seconds"<<std::endl;
+ std::cerr<<"Current update period: "<<m_refreshTime<<" seconds"<<std::endl;
+ std::cerr<<"Last update: "<<time(0)-m_lastUpdate<<" seconds over"<<std::endl;
+ std::cerr<<"Waiting "<<m_firstWait<<" 1/100th seconds for echo answers on the first try"<<std::endl;
+ std::cerr<<"Waiting "<<m_secondWait<<" 1/100th seconds for echo answers on the second try"<<std::endl;
+ std::cerr<<"Sending "<<m_maxPings<<" echo requests at once"<<std::endl;
+ std::cerr<<"Publishing unnamed hosts: "<<m_deliverUnnamedHosts<<std::endl;
+ std::cerr<<"Already served "<<m_serveCount<<" times"<<std::endl;
+}
+
+//this one is not used at the moment
+/*int NetManager::uptime()
+{
+ return (time(0)-m_startedAt);
+};*/
diff --git a/lanbrowsing/lisa/netmanager.h b/lanbrowsing/lisa/netmanager.h
new file mode 100644
index 00000000..dee5fb27
--- /dev/null
+++ b/lanbrowsing/lisa/netmanager.h
@@ -0,0 +1,109 @@
+/* netmanager.h
+ *
+ * Copyright (c) 1998, 1999, Alexander Neundorf
+ * alexander.neundorf@rz.tu-ilmenau.de
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef NETMANAGER_H
+#define NETMANAGER_H
+
+#include <list>
+
+#include "netscanner.h"
+#include "client.h"
+#include "mystring.h"
+#include "configfile.h"
+#include "lisadefines.h"
+
+struct MyFrameType
+{
+ int id;
+ //keep some room for later extensions
+ int unused1;
+ int unused2;
+ int unused3;
+};
+
+#define MYFRAMELENGTH 16
+
+class NetManager:public NetScanner
+{
+ public:
+ NetManager(int& rawSocketFD, int portToUse, MyString configFile, int configStyle=UNIXCONFIGSTYLE, int strictMode=0);
+ ~NetManager();
+
+ int prepare();
+ int run();
+ int writeDataToFD(int fd, int serverServer);
+
+ void readConfig();
+ void configure(Config& config);
+ int isBeingScanned() {return m_isBeingScanned;};
+ int isInformed() {return m_isInformed;};
+ //int uptime();
+ void printState();
+ protected:
+ int m_listenFD;
+ int m_bcFD;
+ int m_basePort;
+
+ int m_pipeFD;
+ char *m_receiveBuffer;
+ size_t m_receivedBytes;
+
+ struct timeval tv;
+ pid_t m_childPid;
+ time_t m_lastUpdate;
+ time_t m_startedAt;
+
+ int m_isInformed;
+ int m_isBeingScanned;
+ int m_firstRun;
+ int m_serverServer;
+ int m_servedThisPeriod;
+
+ int m_serveCount;
+ int m_refreshTime;
+ int m_initialRefreshTime;
+ int m_increasedRefreshTime;
+ int m_broadcastAddress;
+ std::list<Client> clients;
+ struct client_is_done : std::unary_function<Client, bool>
+ {
+ bool operator() (Client& c)
+ {
+ return (c.done() != 0 || c.age() > 30);
+ }
+ };
+
+ int getMaxFD();
+ void generateFDset(fd_set *tmpFDs);
+ //returns 0 on timeout, otherwise 1
+ int waitForSomethingToHappen(fd_set *tmpFDs);
+ void scan();
+ void addClient(int socketFD);
+ void checkClientsAndPipes(fd_set *tmpFDs);
+ int readDataFromFD(int fd);
+ int processScanResults();
+ int findServerServer();
+ void getListFromServerServer(int address);
+ void enableServerServer(int on);
+ void serveAndClean();
+ void answerBroadcast();
+ void adjustRefreshTime(int serverServer);
+
+ MyString m_extraConfigFile;
+ int m_configStyle;
+ MyString getConfigFileName();
+ MyString m_usedConfigFileName;
+};
+#endif
diff --git a/lanbrowsing/lisa/netscanner.cpp b/lanbrowsing/lisa/netscanner.cpp
new file mode 100644
index 00000000..acf0515c
--- /dev/null
+++ b/lanbrowsing/lisa/netscanner.cpp
@@ -0,0 +1,663 @@
+/* netscanner.cpp
+ *
+ * Copyright (c) 2000, Alexander Neundorf,
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "config.h"
+#include "netscanner.h"
+#include "ipaddress.h"
+#include "lisadefines.h"
+#include <iostream>
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+#define LISA_DEBUG 0
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#ifdef mdcerr
+#undef mdcerr
+#endif
+
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"NetScanner::"
+#define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetScanner::"
+
+#include <stdio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#ifdef __osf__
+#undef BYTE_ORDER
+#define _OSF_SOURCE
+#undef _MACHINE_ENDIAN_H_
+#undef __STDC__
+#define __STDC__ 0
+#include <netinet/ip.h>
+#undef __STDC__
+#define __STDC__ 1
+#endif
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
+struct ICMPEchoRequest
+{
+ unsigned char type;
+ unsigned char code;
+ unsigned short int checkSum;
+ unsigned short id;
+ unsigned short seqNumber;
+};
+
+unsigned short in_cksum(unsigned short *addr, int len)
+{
+ int nleft = len;
+ int sum(0);
+ unsigned short *w = addr;
+ unsigned short answer = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+ * sequential 16 bit words to it, and at the end, fold back all the
+ * carry bits from the top 16 bits into the lower 16 bits.
+ */
+ while (nleft > 1)
+ {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* 4mop up an odd byte, if necessary */
+ if (nleft == 1)
+ {
+ *(unsigned char *)(&answer) = *(unsigned char *)w ;
+ sum += answer;
+ }
+
+ /* 4add back carry outs from top 16 bits to low 16 bits */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return(answer);
+}
+
+
+NetScanner::NetScanner(int& rawSocketFD, int strictMode)
+:procId("")
+,m_firstWait(5)
+,m_secondWait(15)
+,m_strictMode(strictMode)
+,m_rawSocketFD(rawSocketFD)
+,validator()
+,ipRangeStr(";")
+,m_maxPings(256)
+,m_deliverUnnamedHosts(0)
+,m_useNmblookup(0)
+,hostList(0)
+,tmpIPRange("")
+{}
+
+NetScanner::~NetScanner()
+{
+// std::cerr<<"----------- Netscanner dtor "<<std::endl;
+ delete hostList;
+ ::close(m_rawSocketFD);
+}
+
+void addMissingSemicolon(MyString& text)
+{
+ if (text.isEmpty()) return;
+ if (text[text.length()-1]!=';')
+ text+=';';
+}
+
+void NetScanner::configure(Config& config)
+{
+ //ranges are not allowed in strict mode
+ if (!m_strictMode)
+ {
+ ipRangeStr=stripWhiteSpace(config.getEntry("PingAddresses",""));
+ addMissingSemicolon(ipRangeStr);
+ }
+ MyString pingNames=stripWhiteSpace(config.getEntry("PingNames",""));
+ addMissingSemicolon(pingNames);
+ MyString nextName;
+ int semicolonPos=pingNames.find(';');
+ int hostsAdded(0);
+ while (semicolonPos!=-1)
+ {
+ nextName=pingNames.left(semicolonPos);
+ mdcerr<<"configure(): looking up -"<<nextName<<"-"<<std::endl;
+ //now the name lookup
+ in_addr server_addr;
+ hostent *hp=gethostbyname(nextName.data());
+ if (hp!=0)
+ {
+ if ((m_strictMode) && (hostsAdded>=STRICTMODEMAXHOSTS))
+ break;
+ memcpy(&server_addr, hp->h_addr, sizeof(server_addr));
+ char *ip=inet_ntoa(server_addr);
+ mdcerr<<"configure(): looking up "<<nextName<<" gives -"<<ip<<"-"<<std::endl;
+ ipRangeStr=ipRangeStr+ip+';';
+ hostsAdded++;
+ }
+
+ pingNames=pingNames.mid(semicolonPos+1);
+ semicolonPos=pingNames.find(';');
+ }
+ if ((!ipRangeStr.isEmpty()) && (ipRangeStr[0]==';'))
+ ipRangeStr=ipRangeStr.mid(1);
+ m_deliverUnnamedHosts=config.getEntry("DeliverUnnamedHosts",0);
+ m_useNmblookup=config.getEntry("SearchUsingNmblookup",0);
+ m_maxPings=config.getEntry("MaxPingsAtOnce",256);
+ m_firstWait=config.getEntry("FirstWait",5);
+ m_secondWait=config.getEntry("SecondWait",15);
+ if (m_firstWait<1) m_firstWait=1;
+ if (m_maxPings<8) m_maxPings=8;
+ if (m_maxPings>1024) m_maxPings=1024;
+ //on some systems (Solaris ?) select() doesn't work correctly
+ // if the microseconds are more than 1.000.000
+ if (m_firstWait>99) m_firstWait=99;
+ if (m_secondWait>99) m_secondWait=99;
+ mdcerr<<"configure(): "<<ipRangeStr<<std::endl;
+}
+
+struct in_addr NetScanner::getIPfromArray(unsigned int index)
+{
+ //mdcerr<<std::endl<<"*** start ***"<<std::endl;
+ unsigned int tmpIndex(0),indexLeft(index);
+ resetIPRange();
+ MyString tmp(getNextIPRange());
+// mdcerr<<"NetScanner::getIPFromArray: -"<<tmp<<"-"<<std::endl;
+ while (!tmp.isEmpty())
+ {
+ if (tmp.contains('/'))
+ {
+ //mdcerr<<"net/mask combination detected"<<std::endl;
+ MyString netStr(tmp.left(tmp.find("/")));
+ MyString maskStr(tmp.mid(tmp.find("/")+1));
+ unsigned int mask(IPAddress(maskStr).asInt());
+ unsigned int net(IPAddress(netStr).asInt()&mask);
+ if ((~mask)<indexLeft)
+ {
+ indexLeft=indexLeft-(~mask+1);
+ tmpIndex+=(~mask)+1;
+ //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl;
+ }
+ else
+ {
+ net+=indexLeft;
+ return IPAddress(net).asStruct();
+ //return string2Struct(ipInt2String(net));
+ }
+ }
+ else if (tmp.contains('-')==1)
+ {
+ //mdcerr<<"single range detected"<<std::endl;
+ MyString fromIPStr(tmp.left(tmp.find("-")));
+ MyString toIPStr(tmp.mid(tmp.find("-")+1));
+ //mdcerr<<"fromIPStr: "<<fromIPStr<<std::endl;
+ //mdcerr<<"toIPStr: "<<toIPStr<<std::endl;
+ unsigned int fromIP(IPAddress(fromIPStr).asInt());
+ unsigned int toIP(IPAddress(toIPStr).asInt());
+ //unsigned int fromIP(ipString2Int(fromIPStr)), toIP(ipString2Int(toIPStr));
+ //index hinter diesem bereich
+ if ((fromIP+indexLeft)>toIP)
+ {
+ tmpIndex+=1+toIP-fromIP;
+ indexLeft=indexLeft-(1+toIP-fromIP);
+ //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl;
+ }
+ //index in diesem bereich
+ else
+ {
+ fromIP+=indexLeft;
+ return IPAddress(fromIP).asStruct();
+ //return string2Struct(ipInt2String(fromIP));
+ }
+
+ }
+ else if (tmp.contains('-')==4)
+ {
+ //mdcerr<<"multiple range detected"<<std::endl;
+ int cp(tmp.find('-'));
+ int from1(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('.');
+ int to1(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('-');
+ int from2(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('.');
+ int to2(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('-');
+ int from3(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('.');
+ int to3(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ cp=tmp.find('-');
+ int from4(atoi(tmp.left(cp).data()));
+ tmp=tmp.mid(cp+1);
+
+ int to4(atoi(tmp.data()));
+
+ unsigned int count((1+to4-from4)*(1+to3-from3)*(1+to2-from2)*(1+to1-from1));
+ if (count<indexLeft)
+ {
+ tmpIndex+=count;
+ indexLeft-=count;
+ //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl;
+ }
+ else
+ {
+ for (int b1=from1; b1<=to1; b1++)
+ for (int b2=from2; b2<=to2; b2++)
+ for (int b3=from3; b3<=to3; b3++)
+ for (int b4=from4; b4<=to4; b4++)
+ {
+ if (tmpIndex==index)
+ {
+ return IPAddress(b1,b2,b3,b4).asStruct();
+ };
+ tmpIndex++;
+ indexLeft--;
+ //mdcerr<<"i: "<<tmpIndex<<" left:"<<indexLeft<<std::endl;
+ }
+ }
+ }
+ //single IP address
+ else if (tmp.contains('.')==3)
+ {
+ //mdcerr<<"single IP address detected"<<std::endl;
+ //if (tmpIndex==index) return string2Struct(tmp);
+ if (tmpIndex==index) return IPAddress(tmp).asStruct();
+ else
+ {
+ tmpIndex++;
+ indexLeft--;
+ //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl;
+ }
+ }
+ //mdcerr<<"nextIPRange: *"<<tmp<<"*"<<std::endl;
+ tmp=getNextIPRange();
+ }
+ return IPAddress("0.0.0.0").asStruct();
+}
+
+void NetScanner::resetIPRange()
+{
+ tmpIPRange=ipRangeStr;
+}
+
+MyString NetScanner::getNextIPRange()
+{
+ if (tmpIPRange.contains(';')<1) return "";
+ int cp(tmpIPRange.find(';'));
+ MyString tmp(tmpIPRange.left(cp));
+ tmpIPRange=tmpIPRange.mid(cp+1);
+ return tmp;
+}
+
+char* NetScanner::ip2Name(struct in_addr ip)
+{
+ struct hostent *hostname=0;
+ // Set the hostname of node
+ if ( ( hostname = gethostbyaddr( (char *) &ip.s_addr, 4, AF_INET ) ) == 0 )
+ {
+ mdcerr << "ip2Name gethostbyname* error" << std::endl;
+ return inet_ntoa( ip );
+ }
+ mdcerr<<"ip2name -"<<hostname->h_name<<std::endl;
+ return hostname->h_name;
+}
+
+void NetScanner::nmblookupScan(std::list<Node>* newList)
+{
+ mdcerr<<"nmblookupScan()"<<std::endl;
+ //newList->clear();
+ FILE * nmblookupFile=popen("nmblookup \"*\"","r");
+ //no success
+ if (nmblookupFile==0)
+ {
+ mdcerr<<"nmblookupScan(): could not start nmblookup"<<std::endl;
+ return;
+ };
+ MyString dummy("");
+ char *receiveBuffer=0;
+ int bufferSize(0);
+ int nmblookupFd=fileno(nmblookupFile);
+ struct timeval tv;
+ fd_set fds;
+ int done(0);
+ int timeOuts(0);
+ char *tmpBuf=new char[16*1024];
+ do
+ {
+ FD_ZERO(&fds);
+ FD_SET(nmblookupFd,&fds);
+ tv.tv_sec=10;
+ tv.tv_usec=0;
+ int result=select(nmblookupFd+1,&fds,0,0,&tv);
+ //error
+ if (result<0)
+ done=1;
+ //timeout
+ else if (result==0)
+ {
+ timeOuts++;
+ //3 time outs make 30 seconds, this should be *much* more than enough
+ if (timeOuts>=3)
+ done=1;
+ }
+ else if (result>0)
+ {
+ //read stuff
+ int bytesRead=::read(nmblookupFd,tmpBuf,16*1024);
+ //pipe closed
+ if (bytesRead==0)
+ done=1;
+ else
+ {
+ char *newBuf=new char[bufferSize+bytesRead];
+ if (receiveBuffer!=0)
+ {
+ memcpy(newBuf,receiveBuffer,bufferSize);
+ delete [] receiveBuffer;
+ }
+ memcpy(newBuf+bufferSize,tmpBuf,bytesRead);
+ receiveBuffer=newBuf;
+ bufferSize+=bytesRead;
+ }
+ }
+ } while (!done);
+
+ // Warning: The return value of pclose may be incorrect due to the
+ // SIGCHLD handler that is installed. Ignore it!
+ pclose(nmblookupFile);
+
+ delete [] tmpBuf;
+ //we received nothing
+ if (receiveBuffer==0)
+ return;
+
+ //check for a terrminating '\0'
+ if (receiveBuffer[bufferSize-1]=='\0')
+ receiveBuffer[bufferSize-1]='\0';
+
+ //std::cerr<<receiveBuffer<<std::endl;
+
+ tmpBuf=receiveBuffer;
+ int bytesLeft=bufferSize;
+
+ int bufferState=0;
+ while (bytesLeft>0)
+ {
+ //mdcerr<<"bytesLeft: "<<bytesLeft<<" received: "<<bufferSize<<std::endl;
+ //since we added a terminating \0 we can be sure here
+ char *endOfLine=(char*)memchr(tmpBuf,'\n',bytesLeft);
+ //point to the last character
+ if (endOfLine==0)
+ endOfLine=receiveBuffer+bufferSize-1;
+
+ //0-terminate the string
+ *endOfLine='\0';
+ //now tmpBuf to endOfLine is a 0-terminated string
+ int strLength=strlen(tmpBuf);
+ //hmm, if this happens, there must be something really wrong
+ if (strLength>1000)
+ break;
+
+ bytesLeft=bytesLeft-strLength-1;
+
+ if (bufferState==0)
+ {
+ if (isdigit(tmpBuf[0]))
+ bufferState=1;
+ }
+ //yes, no else !
+ if (bufferState==1)
+ {
+ char tmpIP[1024];
+ //std::cerr<<"tmpBuf: -"<<tmpBuf<<"-"<<std::endl;
+ if (sscanf(tmpBuf,"%s *<00>\n",tmpIP) == 1) {
+ mdcerr<<"nmblookupScan: tmpIP: -"<<tmpIP<<"-"<<std::endl;
+ struct sockaddr_in addr;
+ if ((addr.sin_addr.s_addr = inet_addr(tmpIP)) != INADDR_NONE)
+ if (!hostAlreadyInList(addr.sin_addr.s_addr,newList))
+ {
+ if (validator.isValid(addr.sin_addr.s_addr))
+ {
+ mdcerr<<"nmblookupScan: adding "<<inet_ntoa(addr.sin_addr)<<std::endl;
+ newList->push_back(Node(dummy,addr.sin_addr.s_addr));
+ }
+ }
+ }
+ }
+ tmpBuf=endOfLine+1;
+ };
+ mdcerr<<"nmblookupScan: finished"<<std::endl;
+ delete [] receiveBuffer;
+}
+
+void NetScanner::pingScan(std::list<Node>* newList) //the ping-version
+{
+ mdcerr<<"pingScan()"<<std::endl;
+ //newList->clear();
+ MyString dummy("");
+ mdcerr<<"pingScan: m_maxPings: "<<m_maxPings<<std::endl;
+
+ pid_t pid=getpid();
+ ICMPEchoRequest echo;
+ echo.type=ICMP_ECHO;
+ echo.code=0;
+ echo.id=pid;
+ echo.seqNumber=0;
+ echo.checkSum=0;
+ echo.checkSum=in_cksum((unsigned short *)&echo,8);
+
+ char receiveBuf[16*1024];
+ //before we start first read everything what might be in the receive buffer
+ //of the raw socket
+
+ timeval tv1;
+ //wait a moment for answers
+ tv1.tv_sec = 0;
+ tv1.tv_usec = 0;
+ fd_set clearSet;
+ FD_ZERO(&clearSet);
+ FD_SET(m_rawSocketFD,&clearSet);
+ while(select(m_rawSocketFD,&clearSet,0,0,&tv1)>0)
+ {
+ ::recvfrom(m_rawSocketFD,(char*)&receiveBuf,16*1024,0,0,0);
+ tv1.tv_sec = 0;
+ tv1.tv_usec = 0;
+ FD_ZERO(&clearSet);
+ FD_SET(m_rawSocketFD,&clearSet);
+ }
+ //now the buffer should be empty
+
+ //wait a moment for answers
+ tv1.tv_sec = 0;
+ tv1.tv_usec = m_firstWait*10*1000;//0.5 sec
+
+ int loopCount(2);
+ if (m_secondWait<0)
+ loopCount=1;
+ for (int repeatOnce=0; repeatOnce<loopCount; repeatOnce++)
+ {
+ mdcerr<<"******************** starting loop *****************"<<std::endl;
+ unsigned int current(0);
+ int finished(0);
+ while (!finished)
+ {
+ for (int con=0; con<m_maxPings; con++)
+ {
+ struct in_addr tmpIP;
+ do
+ {
+ tmpIP=getIPfromArray(current);
+ current++;
+// mdcerr<<"pingScan(): trying "<<inet_ntoa(tmpIP)<<std::endl;
+ if (hostAlreadyInList(tmpIP.s_addr,newList))
+ mdcerr<<"already in list :-)"<<std::endl;
+ if (!validator.isValid(tmpIP.s_addr))
+ mdcerr<<"pingScan(): invalid IP :-("<<std::endl;
+ //ping only hosts which are allowed to receive the results
+ //and which are not in the list
+ } while ( (tmpIP.s_addr!=0)
+ && ((!validator.isValid(tmpIP.s_addr))
+ || (hostAlreadyInList(tmpIP.s_addr,newList))));
+
+ finished=(tmpIP.s_addr==0);
+ if (!finished)
+ {
+ //send the icmp echo request
+ struct sockaddr_in toAddr;
+ toAddr.sin_family = AF_INET;
+ toAddr.sin_addr = tmpIP;
+ toAddr.sin_port = 0;
+ (void)sendto(m_rawSocketFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr));
+ //int sb=sendto(sockFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr));
+// mdcerr<<"pingScan: pinging "<<inet_ntoa(toAddr.sin_addr)<<std::endl;
+ }
+ else break;
+ }
+ select(0,0,0,0,&tv1);
+ //now read the answers, hopefully
+ struct sockaddr_in fromAddr;
+ socklen_t length(sizeof(fromAddr));
+ int received(0);
+
+ fd_set sockFDset;
+ FD_ZERO(&sockFDset);
+ FD_SET(m_rawSocketFD,&sockFDset);
+ tv1.tv_sec=0;
+ tv1.tv_usec=0;
+ while(select(m_rawSocketFD+1,&sockFDset,0,0,&tv1)>0)
+ {
+ received=recvfrom(m_rawSocketFD, (char*)&receiveBuf, 16*1024, 0,
+ (sockaddr*)&fromAddr, &length);
+ if (received!=-1)
+ {
+// mdcerr<<"pingScan: received from "<<inet_ntoa(fromAddr.sin_addr)<<" "<<received<<" b, ";
+ struct ip *ipFrame=(ip*)&receiveBuf;
+ int icmpOffset=(ipFrame->ip_hl)*4;
+ icmp *recIcmpFrame=(icmp*)(receiveBuf+icmpOffset);
+ int iType=recIcmpFrame->icmp_type;
+ //if its a ICMP echo reply
+ if ((iType==ICMP_ECHOREPLY)
+ //to an echo request we sent
+ && (recIcmpFrame->icmp_id==pid)
+ //and the host is not yet in the list
+ && (!hostAlreadyInList(fromAddr.sin_addr.s_addr,newList)))
+ {
+ //this is an answer to our request :-)
+// mdcerr<<"pingScan: adding "<<inet_ntoa(fromAddr.sin_addr)<<std::endl;
+ newList->push_back(Node(dummy,fromAddr.sin_addr.s_addr));
+ }
+ }
+ tv1.tv_sec=0;
+ tv1.tv_usec=0;
+ FD_ZERO(&sockFDset);
+ FD_SET(m_rawSocketFD,&sockFDset);
+ //FD_SET(sockFD,&sockFDset);
+ }
+ }
+ tv1.tv_sec = 0;
+ tv1.tv_usec = m_secondWait*10*1000;//0.5 sec
+ }
+ mdcerr<<"pingScan() ends"<<std::endl;
+}
+
+void NetScanner::doScan()
+{
+ mdcerr<<"doScan"<<std::endl;
+ //child
+ std::list<Node>* tmpPingList=new std::list<Node>;
+ mdcerr<<"doScan: created list"<<std::endl;
+ if (m_useNmblookup)
+ nmblookupScan(tmpPingList);
+ pingScan(tmpPingList);
+ // get the names from cache or lookup
+ completeNames(tmpPingList);
+ mdcerr<<"doScan: completed names"<<std::endl;
+ if (m_deliverUnnamedHosts==0)
+ removeUnnamedHosts(tmpPingList);
+
+ mdcerr<<"doScan: added hosts"<<std::endl;
+
+ delete hostList;
+ hostList=tmpPingList;
+}
+
+int NetScanner::hostAlreadyInList(int ip, std::list<Node>* nodes)
+{
+ for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++)
+ {
+ if (node->ip==ip)
+ return 1;
+ }
+ return 0;
+}
+
+void NetScanner::removeUnnamedHosts(std::list<Node>* nodes)
+{
+ nodes->remove_if(is_unnamed_node());
+}
+
+void NetScanner::completeNames(std::list<Node>* nodes)
+{
+ struct sockaddr_in tmpAddr;
+ //for every host
+ for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++)
+ {
+ tmpAddr.sin_addr.s_addr=node->ip;
+ mdcerr<<"completeNames: looking up "<<inet_ntoa(tmpAddr.sin_addr)<<std::endl;
+ int done(0);
+ //first look wether we have the name already
+ if (hostList!=0) for (std::list<Node>::iterator oldNode=hostList->begin(); oldNode!=hostList->end(); oldNode++)
+ {
+ if (node->ip==oldNode->ip)
+ {
+ mdcerr<<"completeNames: cached: "<<oldNode->name<<" :-)"<<std::endl;
+ node->name=oldNode->name;
+ done=1;
+ break;
+ }
+ }
+ //otherwise do a name lookup
+ if (!done)
+ {
+ //IPAddress tmpAddress(node->ip);
+ //mdcerr<<"NetScanner::completeNames: doing actual lookup"<<std::endl;
+ node->name=ip2Name(tmpAddr.sin_addr);
+ mdcerr<<"completeNames: resolved: "<<node->name<<std::endl;
+ }
+ }
+}
+
diff --git a/lanbrowsing/lisa/netscanner.h b/lanbrowsing/lisa/netscanner.h
new file mode 100644
index 00000000..a93bf3ee
--- /dev/null
+++ b/lanbrowsing/lisa/netscanner.h
@@ -0,0 +1,102 @@
+/* netscanner.h
+ *
+ * Copyright (c) 1998, 1999, 2000 Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef NETSCANNER_H
+#define NETSCANNER_H
+
+#include <list>
+
+#include "mystring.h"
+#include "addressvalidator.h"
+#include "tcpnode.h"
+#include "configfile.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <time.h>
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+
+class NetScanner
+{
+ public:
+ NetScanner(int& rawSocket, int strictMode);
+ ~NetScanner();
+
+ void doScan();
+
+ void completeNames(std::list<Node>* nodes);
+
+ //using nmblookup to determine the ip-address
+ //MyString ip2NetbiosName(const MyString& target, MyString& groupName);
+
+ char* ip2Name(struct in_addr ip);
+
+ MyString procId;
+ int m_firstWait;
+ int m_secondWait;
+ void configure(Config& config);
+ protected:
+ int m_strictMode;
+ int& m_rawSocketFD;
+ AddressValidator validator;
+ MyString ipRangeStr;
+ MyString nameMask;
+
+ int m_maxPings;
+ int m_deliverUnnamedHosts;
+ int m_useNmblookup;
+ //return ip-address with number index from a virtual array
+ //with all ip-addresses according to ipRangeStr
+ struct in_addr getIPfromArray(unsigned int index);
+
+ std::list<Node>* hostList;
+
+ //needed for getIPfromArray()
+ //contains the part of ipRangeStr, which is not yet parsed
+ MyString tmpIPRange;
+ //has to be called before every first call of getNextIPRange
+ void resetIPRange();
+ //returns the next range/part from tmpIPRange before the next semicolon
+ MyString getNextIPRange();
+
+ void pingScan(std::list<Node>* newList); //the ping-version
+ void nmblookupScan(std::list<Node>* newList); //the nmblookup "*"-version
+ int hostAlreadyInList(int ip, std::list<Node>* nodes);
+ void removeUnnamedHosts(std::list<Node>* nodes);
+
+ struct is_unnamed_node : std::unary_function<Node&, bool>
+ {
+ bool operator() (Node& n)
+ {
+ struct in_addr tmpAddr;
+ tmpAddr.s_addr = n.ip;
+ return (strcmp(inet_ntoa(tmpAddr), n.name.data()) == 0);
+ }
+ };
+};
+
+#endif
diff --git a/lanbrowsing/lisa/strictmain.cpp b/lanbrowsing/lisa/strictmain.cpp
new file mode 100644
index 00000000..a948d6df
--- /dev/null
+++ b/lanbrowsing/lisa/strictmain.cpp
@@ -0,0 +1,261 @@
+/* strictmain.cpp
+ *
+ * Copyright (c) 1998-2000 Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#include "lisadefines.h"
+#include "netmanager.h"
+
+#include <iostream>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+// detect linux/glibc for the gnu style --args
+#if defined(__linux__) || defined(__linux) || defined(linux)
+# include <features.h>
+# ifdef __GLIBC__
+// only gnu libc has getopt.h... getopt(3) is defined to be in unistd.h
+// by POSIX.2
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# endif
+# include <getopt.h>
+# endif // __GLIBC__
+# define GNU_GETOPT
+#endif // linux
+
+#ifdef LISA_DEBUG
+#undef LISA_DEBUG
+#endif
+#define LISA_DEBUG 0
+
+#ifdef dcerr
+#undef dcerr
+#endif
+
+#define dcerr if (LISA_DEBUG==1) std::cerr<<"strictmain "
+
+void printVersion()
+{
+ const char * versionInfo=\
+ "\r\nThis is the restricted LAN Information Server resLISa "MYVERSION"\r\n"\
+ "It is free software according the GNU General Public License\r\n"\
+ "Copyright (c) 2000-2003 by Alexander Neundorf\r\n"\
+ "email: neundorf@kde.org\r\n";
+ std::cout<<versionInfo<<std::endl;
+}
+
+void usage()
+{
+ printVersion();
+ const char * usageInfo=\
+ "-v, --version prints out a short version info\n"\
+ "-u, --unix deprecated\n"\
+ "-k, --kde1 deprecated\n"\
+ "-K, --kde2 deprecated\n"\
+ " reslisa now always looks first for $(HOME)/.reslisarc, then for /etc/reslisarc\"\n"\
+ "-c, --config=FILE read this and no other configuration file\n"\
+ "-q, --quiet start quiet without the greeting message\n"\
+ "-h, --help you are currently reading it ;-)\n";
+ std::cout<<usageInfo<<std::endl;
+//I thought this would be the way to check wether long options are supported...
+//#ifndef _GNU_SOURCE
+// cout<<"Please note that the long options are not supported on this system"<<endl;
+//#endif
+}
+
+void destruct(int sigNumber)
+{
+ signal(sigNumber,SIG_IGN);
+ std::cout<<"signal caught: "<<sigNumber<<", exiting"<<std::endl;
+ //signal(sigNumber,&destruct);
+ exit(0);
+}
+
+NetManager *manager(0);
+
+void readConfig(int sigNumber)
+{
+ std::cout<<"readConfig(): signal caught: "<<sigNumber<<std::endl;
+ signal(SIGHUP,SIG_IGN);
+ if (manager!=0)
+ manager->readConfig();
+ signal(SIGHUP,&readConfig);
+}
+
+void printState(int sigNumber)
+{
+ std::cout<<"printState(): signal caught: "<<sigNumber<<std::endl;
+ signal(SIGUSR1,SIG_IGN);
+ if (manager!=0)
+ manager->printState();
+ signal(SIGUSR1,&printState);
+}
+
+void setSignalHandler()
+{
+ signal(SIGHUP,&readConfig);
+ signal(SIGUSR1,&printState);
+
+ signal(SIGINT,&destruct);
+ signal(SIGQUIT,&destruct);
+ signal(SIGILL,&destruct);
+ signal(SIGTRAP,&destruct);
+ signal(SIGABRT,&destruct);
+ signal(SIGBUS,&destruct);
+ signal(SIGSEGV,&destruct);
+ signal(SIGUSR2,&destruct);
+ signal(SIGPIPE,&destruct);
+ signal(SIGALRM,&destruct);
+ signal(SIGTERM,&destruct);
+ signal(SIGFPE,&destruct);
+#ifdef SIGPOLL
+ signal(SIGPOLL, &destruct);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, &destruct);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, &destruct);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, &destruct);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, &destruct);
+#endif
+}
+
+#ifdef GNU_GETOPT
+static struct option const long_opts[] =
+{
+ {"version", no_argument, 0, 'v'},
+ {"quiet", no_argument, 0, 'q'},
+ {"unix", no_argument, 0, 'u'},
+ {"kde1", no_argument, 0, 'k'},
+ {"kde2", no_argument, 0, 'K'},
+ {"config", required_argument, 0, 'c'},
+ {"help", no_argument, 0, 'h'},
+ {0, 0, 0, 0}
+};
+#endif
+
+int main(int argc, char** argv)
+{
+ int quiet(0);
+ int c(0);
+ int configStyle(UNIXCONFIGSTYLE);
+ MyString configFile;
+ int portToUse(MYPORT);
+
+//I thought this would be the way to check wether long options are supported...
+#ifdef GNU_GETOPT
+ while ((c=getopt_long(argc, argv, "vqukKc:h", long_opts, 0))!=-1)
+#else
+ while ((c=getopt(argc, argv, "vqukKc:h"))!=-1)
+#endif
+ {
+ switch (c)
+ {
+ case 0:
+ break;
+ case 'v':
+ printVersion();
+ exit(0);
+ break;
+ case 'q':
+ quiet=1;
+ break;
+ case 'u':
+ case 'k':
+ case 'K':
+ std::cerr<<"\a\nThe command line switches -k, -K, -u and \ntheir long versions "\
+ "--kde1, --kde2 and --unix are not supported anymore.\n"\
+ "ResLisa will always first look for $(HOME)/.reslisarc , then for /etc/reslisarc.\n"\
+ "If your lisa configuration file was created using an older version of \n"\
+ "the KDE control center, copy the $(HOME)/.kde/share/config/reslisarc to $(HOME)/.reslisarc.\n"<<std::endl;
+ break;
+
+ case 'c':
+ configFile = optarg;
+ configStyle = EXTRACONFIGSTYLE;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ break;
+ }
+ }
+
+ //fork and let the parent exit
+ pid_t pid=fork();
+ if (pid>0)
+ {
+ //this is the parent
+ exit(0);
+ }
+ else if (pid<0)
+ {
+ dcerr<<"could not fork()"<<std::endl;
+ exit(0);
+ }
+ //we will only read/write to/from this socket in the child process
+ int rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
+ if (rawSocket==-1)
+ {
+ std::cout<<"could not create raw socket, root privileges required"<<std::endl;
+ std::cout<<"take a look at the README for more information"<<std::endl;
+ exit(0);
+ }
+ int bufferSize(60*1024);
+ int on(1);
+ setsockopt(rawSocket, SOL_SOCKET, SO_RCVBUF, (char*)&bufferSize,
+ sizeof(bufferSize));
+ int result=setsockopt(rawSocket, SOL_SOCKET, SO_BROADCAST, (char*)&on,
+ sizeof(on));
+ dcerr<<"setsockopt returns "<<result<<std::endl;
+ //dropping root privileges
+ //they will be regained once in the child process
+ //for creating a raw socket
+
+ //now dropping root privileges once and ever
+ setuid(getuid());
+
+ //according to R. Stevens the following three lines
+ //make daemons feel good :)
+ setsid();
+ chdir("/");
+ umask(0);
+
+ dcerr<<"starting, dropped root privileges"<<std::endl;
+ dcerr<<"port: "<<portToUse<<" file: "<<configFile<<std::endl;
+ NetManager netmanager(rawSocket,portToUse,configFile,configStyle,1);
+ manager=&netmanager;
+ dcerr<<"NetManager created"<<std::endl;
+ setSignalHandler();
+
+ netmanager.readConfig();
+ if (netmanager.prepare())
+ {
+ if (!quiet)
+ printVersion();
+ netmanager.run();
+ }
+ dcerr<<"server finished"<<std::endl;
+}
diff --git a/lanbrowsing/lisa/stringlist.cpp b/lanbrowsing/lisa/stringlist.cpp
new file mode 100644
index 00000000..3bf82f9c
--- /dev/null
+++ b/lanbrowsing/lisa/stringlist.cpp
@@ -0,0 +1,110 @@
+#include "simplelist.h"
+
+template <class T>
+SimpleList<T>::SimpleList()
+ :m_list(0)
+ ,m_current(0)
+ ,m_last(0)
+ ,m_size(0)
+{};
+
+template <class T>
+SimpleList<T>::~SimpleList()
+{
+ clear();
+};
+
+template <class T>
+void SimpleList<T>::append(const T& text)
+{
+ if (m_list==0)
+ {
+ m_list=new TemplNode<T>(text);
+ m_last=m_list;
+ }
+ else
+ {
+ m_last->m_next=new TemplNode<T>(text);
+ m_last=m_last->m_next;
+ };
+ m_size++;
+};
+
+template <class T>
+void SimpleList<T>::removeFirst()
+{
+ if (m_list==0) return;
+ TemplNode<T> *first=m_list;
+ m_list=m_list->m_next;
+ m_size--;
+ if (m_list==0)
+ m_last=0;
+ m_current=0;
+ delete first;
+};
+
+template <class T>
+void SimpleList<T>::clear()
+{
+ while (m_list!=0)
+ removeFirst();
+ m_current=0;
+ m_last=0;
+ m_list=0;
+ m_size=0;
+};
+
+template <class T>
+void SimpleList<T>::remove(T* item)
+{
+ if (item==0) return;
+ TemplNode<T>* pre(0);
+ for (T* tmp=first(); tmp!=0; tmp=next())
+ {
+ if (tmp==item)
+ {
+ if (m_current==m_list)
+ {
+ removeFirst();
+ return;
+ }
+ else
+ {
+ TemplNode<T> *succ=m_current->m_next;
+ if (m_current==m_last)
+ m_last=pre;
+ delete m_current;
+ pre->m_next=succ;
+ m_size--;
+ m_current=0;
+
+ };
+ };
+ pre=m_current;
+ };
+
+};
+
+template <class T>
+T* SimpleList<T>::first()
+{
+ m_current=m_list;
+ if (m_list==0)
+ return 0;
+ return &m_current->m_item;
+};
+
+template <class T>
+T* SimpleList<T>::next()
+{
+ if (m_current==0) return 0;
+ m_current=m_current->m_next;
+ if (m_current==0) return 0;
+ return &m_current->m_item;
+};
+
+template <class T>
+int SimpleList<T>::size()
+{
+ return m_size;
+}
diff --git a/lanbrowsing/lisa/stringlist.h b/lanbrowsing/lisa/stringlist.h
new file mode 100644
index 00000000..44f0d0d3
--- /dev/null
+++ b/lanbrowsing/lisa/stringlist.h
@@ -0,0 +1,40 @@
+#ifndef SIMPLELIST_H
+#define SIMPLELIST_H
+
+#include "mystring.h"
+
+template <class T>
+struct TemplNode
+{
+ TemplNode(const T& text)
+ :m_item(text),m_next(0) {};
+ T m_item;
+ TemplNode* m_next;
+};
+
+
+template <class T>
+class SimpleList
+{
+ public:
+ SimpleList();
+ ~SimpleList();
+ void append(const T& item);
+ void clear();
+ int size();
+ T* first();
+ T* next();
+ void removeFirst();
+ void remove(T* item);
+ protected:
+ TemplNode<T>* m_list;
+ TemplNode<T>* m_current;
+ TemplNode<T>* m_last;
+ int m_size;
+};
+
+
+template class SimpleList<int>;
+
+#endif
+
diff --git a/lanbrowsing/lisa/tcpnode.h b/lanbrowsing/lisa/tcpnode.h
new file mode 100644
index 00000000..394ca928
--- /dev/null
+++ b/lanbrowsing/lisa/tcpnode.h
@@ -0,0 +1,29 @@
+/* tcpnode.h
+ *
+ * Copyright (c) 2000 Alexander Neundorf
+ * neundorf@kde.org
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the COPYING file.
+ *
+ * This program 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 General Public License for more details.
+ *
+ */
+
+#ifndef _TCPNODE_H_
+#define _TCPNODE_H_
+
+#include "mystring.h"
+
+struct Node
+{
+ Node(const MyString& n, int ipAddress)
+ :name(n),ip(ipAddress) {};
+ MyString name;
+ unsigned int ip;
+};
+
+#endif
diff --git a/librss/COPYING b/librss/COPYING
new file mode 100644
index 00000000..cca2a5c9
--- /dev/null
+++ b/librss/COPYING
@@ -0,0 +1,20 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/librss/Makefile.am b/librss/Makefile.am
new file mode 100644
index 00000000..fe95004a
--- /dev/null
+++ b/librss/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES = $(all_includes)
+
+lib_LTLIBRARIES = librss.la
+librss_la_METASOURCES = AUTO
+librss_la_SOURCES = article.cpp document.cpp image.cpp textinput.cpp \
+ tools_p.cpp loader.cpp
+librss_la_LDFLAGS = -no-undefined -version-info 1:0 $(all_libraries)
+librss_la_LIBADD = $(LIB_KIO)
+
+librssincludedir = $(includedir)/rss
+librssinclude_HEADERS = article.h document.h global.h image.h textinput.h \
+ loader.h librss.h
+
+check_PROGRAMS = testlibrss
+testlibrss_SOURCES = testlibrss.cpp
+testlibrss_LDFLAGS = $(all_libraries)
+testlibrss_LDADD = librss.la
diff --git a/librss/article.cpp b/librss/article.cpp
new file mode 100644
index 00000000..e283b5c5
--- /dev/null
+++ b/librss/article.cpp
@@ -0,0 +1,146 @@
+/*
+ * article.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "article.h"
+#include "tools_p.h"
+
+#include <krfcdate.h>
+#include <kurl.h>
+#include <kurllabel.h>
+
+#include <qdatetime.h>
+#include <qdom.h>
+
+using namespace RSS;
+
+struct Article::Private : public Shared
+{
+ QString title;
+ KURL link;
+ QString description;
+ QDateTime pubDate;
+ QString guid;
+ bool guidIsPermaLink;
+};
+
+Article::Article() : d(new Private)
+{
+}
+
+Article::Article(const Article &other) : d(0)
+{
+ *this = other;
+}
+
+Article::Article(const QDomNode &node) : d(new Private)
+{
+ QString elemText;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("pubDate"))).isNull()) {
+ time_t _time = KRFCDate::parseDate(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+ if (!(elemText = extractNode(node, QString::fromLatin1("dc:date"))).isNull()) {
+ time_t _time = KRFCDate::parseDateISO8601(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+
+ QDomNode n = node.namedItem(QString::fromLatin1("guid"));
+ if (!n.isNull()) {
+ d->guidIsPermaLink = true;
+ if (n.toElement().attribute(QString::fromLatin1("isPermaLink"), "true") == "false") d->guidIsPermaLink = false;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("guid"))).isNull())
+ d->guid = elemText;
+ }
+}
+
+Article::~Article()
+{
+ if (d->deref())
+ delete d;
+}
+
+QString Article::title() const
+{
+ return d->title;
+}
+
+const KURL &Article::link() const
+{
+ return d->link;
+}
+
+QString Article::description() const
+{
+ return d->description;
+}
+
+QString Article::guid() const
+{
+ return d->guid;
+}
+
+bool Article::guidIsPermaLink() const
+{
+ return d->guidIsPermaLink;
+}
+
+const QDateTime &Article::pubDate() const
+{
+ return d->pubDate;
+}
+
+KURLLabel *Article::widget(QWidget *parent, const char *name) const
+{
+ KURLLabel *label = new KURLLabel(d->link.url(), d->title, parent, name);
+ label->setUseTips(true);
+ if (!d->description.isNull())
+ label->setTipText(d->description);
+
+ return label;
+}
+
+Article &Article::operator=(const Article &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool Article::operator==(const Article &other) const
+{
+ return d->title == other.title() &&
+ d->link == other.link() &&
+ d->description == other.description() &&
+ d->pubDate == other.pubDate() &&
+ d->guid == other.guid() &&
+ d->guidIsPermaLink == other.guidIsPermaLink();
+}
+
+// vim:noet:ts=4
diff --git a/librss/article.h b/librss/article.h
new file mode 100644
index 00000000..865a6bbc
--- /dev/null
+++ b/librss/article.h
@@ -0,0 +1,151 @@
+/*
+ * article.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_ARTICLE_H
+#define LIBRSS_ARTICLE_H
+
+#include "global.h"
+
+class QDateTime;
+class QDomNode;
+template <class>
+class QValueList;
+class QString;
+class QWidget;
+class KURL;
+class KURLLabel;
+
+namespace RSS
+{
+ /**
+ * Represents an article as stored in a RSS file. You don't have to
+ * instantiate one of these yourself, the common way to access instances
+ * is via Document::articles().
+ * @see Document::articles()
+ */
+ class LIBRSS_EXPORT Article
+ {
+ public:
+ /**
+ * A list of articles.
+ */
+ typedef QValueList<Article> List;
+
+ /**
+ * Default constructor.
+ */
+ Article();
+
+ /**
+ * Copy constructor.
+ * @param other The Article object to copy.
+ */
+ Article(const Article &other);
+
+ /**
+ * Constructs an Article from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the Article.
+ */
+ Article(const QDomNode &node);
+
+ /**
+ * Assignment operator.
+ * @param other The Article object to clone.
+ * @return A reference to the cloned Article object.
+ */
+ Article &operator=(const Article &other);
+
+ /**
+ * Compares two articles. Two articles are treated to be identical
+ * if all their properties (title, link, description etc.) are
+ * equal.
+ * @param other The article this article should be compared with.
+ * @return Whether the two articles are equal.
+ */
+ bool operator==(const Article &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The article this article should be compared with.
+ * @return Whether the two articles are unequal.
+ */
+ bool operator!=(const Article &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~Article();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The headline of this article, or QString::null if
+ * no headline was available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A URL referencing the complete text for this article,
+ * or an empty KURL if no link was available.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A story synopsis, or QString::null if no description
+ * was available.
+ */
+ QString description() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return An article GUID (globally unique identifier).
+ */
+ QString guid() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return If this article GUID is permalink. Has no meaning when guid() is QString::null.
+ */
+ bool guidIsPermaLink() const;
+
+ /**
+ * RSS 2.0 and upwards
+ * @return The date when the article was published.
+ */
+ const QDateTime &pubDate() const;
+
+ /**
+ * @param parent The parent widget for the KURLLabel.
+ * @param name A name for the widget which will be used internally.
+ * @return a widget (a KURLLabel in this case) for the Article.
+ * This makes building a user-interface which contains the
+ * information in this Article object more convenient.
+ * The returned KURLLabel's caption will be the title(), clicking
+ * on it will emit the URL link(), and it has a QToolTip attached
+ * to it which displays the description() (in case it has one,
+ * if there is no description, the URL which the label links to
+ * will be used).
+ * Note that you have to delete the KURLLabel object returned by
+ * this method yourself.
+ */
+ KURLLabel *widget(QWidget *parent = 0, const char *name = 0) const;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_ARTICLE_H
+// vim: noet:ts=4
diff --git a/librss/document.cpp b/librss/document.cpp
new file mode 100644
index 00000000..92f4093e
--- /dev/null
+++ b/librss/document.cpp
@@ -0,0 +1,525 @@
+/*
+ * document.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ *
+ */
+#include "document.h"
+#include "article.h"
+#include "image.h"
+#include "textinput.h"
+#include "tools_p.h"
+
+#include <krfcdate.h>
+#include <kurl.h>
+
+#include <qdatetime.h>
+#include <qdom.h>
+#include <qptrlist.h>
+
+using namespace RSS;
+
+struct Document::Private : public Shared
+{
+ Private() : version(v0_90), image(NULL), textInput(NULL), language(en)
+ {
+ }
+
+ ~Private()
+ {
+ delete textInput;
+ delete image;
+ }
+
+ Version version;
+ QString title;
+ QString description;
+ KURL link;
+ Image *image;
+ TextInput *textInput;
+ Article::List articles;
+ Language language;
+ QString copyright;
+ QDateTime pubDate;
+ QDateTime lastBuildDate;
+ QString rating;
+ KURL docs;
+ QString managingEditor;
+ QString webMaster;
+ HourList skipHours;
+ DayList skipDays;
+};
+
+Document::Document() : d(new Private)
+{
+}
+
+Document::Document(const Document &other) : d(0)
+{
+ *this = other;
+}
+
+Document::Document(const QDomDocument &doc) : d(new Private)
+{
+ QString elemText;
+ QDomNode rootNode = doc.documentElement();
+
+ // Determine the version of the present RSS markup.
+ QString attr = rootNode.toElement().attribute(QString::fromLatin1("xmlns"), QString::null);
+ if (!attr.isNull()) {
+ /*
+ * Hardcoding these URLs is actually a bad idea, since the DTD doesn't
+ * dictate a specific namespace. Still, most RSS files seem to use
+ * these two, so I'll go for them now. If it turns out that many
+ * mirrors of this RSS namespace are in use, I'll probably have to
+ * distinguish the RSS versions by analyzing the relationship between
+ * the nodes.
+ */
+ if (attr == QString::fromLatin1("http://my.netscape.com/rdf/simple/0.9/"))
+ d->version = v0_90;
+ else if (attr == QString::fromLatin1("http://purl.org/rss/1.0/")) {
+ d->version = v1_0;
+ }
+ } else {
+ attr = rootNode.toElement().attribute(QString::fromLatin1("version"), QString::null);
+ if (!attr.isNull()) {
+ if (attr == QString::fromLatin1("0.91"))
+ d->version = v0_91;
+ else if (attr == QString::fromLatin1("0.92"))
+ d->version = v0_92;
+ else if (attr == QString::fromLatin1("0.93"))
+ d->version = v0_93;
+ else if (attr == QString::fromLatin1("0.94"))
+ d->version = v0_94;
+ else if (attr == QString::fromLatin1("2.0") || attr == QString::fromLatin1("2"))
+ d->version = v2_0;
+ }
+ }
+ QDomNode channelNode = rootNode.namedItem(QString::fromLatin1("channel"));
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+
+ /* This is ugly but necessary since RSS 0.90 and 1.0 have a different parent
+ * node for <image>, <textinput> and <item> than RSS 0.91-0.94 and RSS 2.0.
+ */
+ QDomNode parentNode;
+ if (d->version == v0_90 || d->version == v1_0)
+ parentNode = rootNode;
+ else
+ parentNode = channelNode;
+
+ QDomNode n = parentNode.namedItem(QString::fromLatin1("image"));
+ if (!n.isNull())
+ d->image = new Image(n);
+
+ n = parentNode.namedItem(QString::fromLatin1("textinput"));
+ if (!n.isNull())
+ d->textInput = new TextInput(n);
+
+ // Our (hopefully faster) version of elementsByTagName()
+ for (n = parentNode.firstChild(); !n.isNull(); n = n.nextSibling()) {
+ const QDomElement e = n.toElement();
+ if (e.tagName() == QString::fromLatin1("item"))
+ d->articles.append(Article(e));
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("copyright"))).isNull())
+ d->copyright = elemText;
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("language"))).isNull()) {
+ if (elemText == QString::fromLatin1("af"))
+ d->language = af;
+ else if (elemText == QString::fromLatin1("sq"))
+ d->language = sq;
+ else if (elemText == QString::fromLatin1("eu"))
+ d->language = eu;
+ else if (elemText == QString::fromLatin1("be"))
+ d->language = be;
+ else if (elemText == QString::fromLatin1("bg"))
+ d->language = bg;
+ else if (elemText == QString::fromLatin1("ca"))
+ d->language = ca;
+ else if (elemText == QString::fromLatin1("zh-cn"))
+ d->language = zh_cn;
+ else if (elemText == QString::fromLatin1("zh-tw"))
+ d->language = zh_tw;
+ else if (elemText == QString::fromLatin1("hr"))
+ d->language = hr;
+ else if (elemText == QString::fromLatin1("cs"))
+ d->language = cs;
+ else if (elemText == QString::fromLatin1("da"))
+ d->language = da;
+ else if (elemText == QString::fromLatin1("nl"))
+ d->language = nl;
+ else if (elemText == QString::fromLatin1("nl-be"))
+ d->language = nl_be;
+ else if (elemText == QString::fromLatin1("nl-nl"))
+ d->language = nl_nl;
+ else if (elemText == QString::fromLatin1("en"))
+ d->language = en;
+ else if (elemText == QString::fromLatin1("en-au"))
+ d->language = en_au;
+ else if (elemText == QString::fromLatin1("en-bz"))
+ d->language = en_bz;
+ else if (elemText == QString::fromLatin1("en-ca"))
+ d->language = en_ca;
+ else if (elemText == QString::fromLatin1("en-ie"))
+ d->language = en_ie;
+ else if (elemText == QString::fromLatin1("en-jm"))
+ d->language = en_jm;
+ else if (elemText == QString::fromLatin1("en-nz"))
+ d->language = en_nz;
+ else if (elemText == QString::fromLatin1("en-ph"))
+ d->language = en_ph;
+ else if (elemText == QString::fromLatin1("en-za"))
+ d->language = en_za;
+ else if (elemText == QString::fromLatin1("en-tt"))
+ d->language = en_tt;
+ else if (elemText == QString::fromLatin1("en-gb"))
+ d->language = en_gb;
+ else if (elemText == QString::fromLatin1("en-us"))
+ d->language = en_us;
+ else if (elemText == QString::fromLatin1("en-zw"))
+ d->language = en_zw;
+ else if (elemText == QString::fromLatin1("fo"))
+ d->language = fo;
+ else if (elemText == QString::fromLatin1("fi"))
+ d->language = fi;
+ else if (elemText == QString::fromLatin1("fr"))
+ d->language = fr;
+ else if (elemText == QString::fromLatin1("fr-be"))
+ d->language = fr_be;
+ else if (elemText == QString::fromLatin1("fr-ca"))
+ d->language = fr_ca;
+ else if (elemText == QString::fromLatin1("fr-fr"))
+ d->language = fr_fr;
+ else if (elemText == QString::fromLatin1("fr-lu"))
+ d->language = fr_lu;
+ else if (elemText == QString::fromLatin1("fr-mc"))
+ d->language = fr_mc;
+ else if (elemText == QString::fromLatin1("fr-ch"))
+ d->language = fr_ch;
+ else if (elemText == QString::fromLatin1("gl"))
+ d->language = gl;
+ else if (elemText == QString::fromLatin1("gd"))
+ d->language = gd;
+ else if (elemText == QString::fromLatin1("de"))
+ d->language = de;
+ else if (elemText == QString::fromLatin1("de-at"))
+ d->language = de_at;
+ else if (elemText == QString::fromLatin1("de-de"))
+ d->language = de_de;
+ else if (elemText == QString::fromLatin1("de-li"))
+ d->language = de_li;
+ else if (elemText == QString::fromLatin1("de-lu"))
+ d->language = de_lu;
+ else if (elemText == QString::fromLatin1("de-ch"))
+ d->language = de_ch;
+ else if (elemText == QString::fromLatin1("el"))
+ d->language = el;
+ else if (elemText == QString::fromLatin1("hu"))
+ d->language = hu;
+ else if (elemText == QString::fromLatin1("is"))
+ d->language = is;
+ else if (elemText == QString::fromLatin1("id"))
+ d->language = id;
+ else if (elemText == QString::fromLatin1("ga"))
+ d->language = ga;
+ else if (elemText == QString::fromLatin1("it"))
+ d->language = it;
+ else if (elemText == QString::fromLatin1("it-it"))
+ d->language = it_it;
+ else if (elemText == QString::fromLatin1("it-ch"))
+ d->language = it_ch;
+ else if (elemText == QString::fromLatin1("ja"))
+ d->language = ja;
+ else if (elemText == QString::fromLatin1("ko"))
+ d->language = ko;
+ else if (elemText == QString::fromLatin1("mk"))
+ d->language = mk;
+ else if (elemText == QString::fromLatin1("no"))
+ d->language = no;
+ else if (elemText == QString::fromLatin1("pl"))
+ d->language = pl;
+ else if (elemText == QString::fromLatin1("pt"))
+ d->language = pt;
+ else if (elemText == QString::fromLatin1("pt-br"))
+ d->language = pt_br;
+ else if (elemText == QString::fromLatin1("pt-pt"))
+ d->language = pt_pt;
+ else if (elemText == QString::fromLatin1("ro"))
+ d->language = ro;
+ else if (elemText == QString::fromLatin1("ro-mo"))
+ d->language = ro_mo;
+ else if (elemText == QString::fromLatin1("ro-ro"))
+ d->language = ro_ro;
+ else if (elemText == QString::fromLatin1("ru"))
+ d->language = ru;
+ else if (elemText == QString::fromLatin1("ru-mo"))
+ d->language = ru_mo;
+ else if (elemText == QString::fromLatin1("ru-ru"))
+ d->language = ru_ru;
+ else if (elemText == QString::fromLatin1("sr"))
+ d->language = sr;
+ else if (elemText == QString::fromLatin1("sk"))
+ d->language = sk;
+ else if (elemText == QString::fromLatin1("sl"))
+ d->language = sl;
+ else if (elemText == QString::fromLatin1("es"))
+ d->language = es;
+ else if (elemText == QString::fromLatin1("es-ar"))
+ d->language = es_ar;
+ else if (elemText == QString::fromLatin1("es-bo"))
+ d->language = es_bo;
+ else if (elemText == QString::fromLatin1("es-cl"))
+ d->language = es_cl;
+ else if (elemText == QString::fromLatin1("es-co"))
+ d->language = es_co;
+ else if (elemText == QString::fromLatin1("es-cr"))
+ d->language = es_cr;
+ else if (elemText == QString::fromLatin1("es-do"))
+ d->language = es_do;
+ else if (elemText == QString::fromLatin1("es-ec"))
+ d->language = es_ec;
+ else if (elemText == QString::fromLatin1("es-sv"))
+ d->language = es_sv;
+ else if (elemText == QString::fromLatin1("es-gt"))
+ d->language = es_gt;
+ else if (elemText == QString::fromLatin1("es-hn"))
+ d->language = es_hn;
+ else if (elemText == QString::fromLatin1("es-mx"))
+ d->language = es_mx;
+ else if (elemText == QString::fromLatin1("es-ni"))
+ d->language = es_ni;
+ else if (elemText == QString::fromLatin1("es-pa"))
+ d->language = es_pa;
+ else if (elemText == QString::fromLatin1("es-py"))
+ d->language = es_py;
+ else if (elemText == QString::fromLatin1("es-pe"))
+ d->language = es_pe;
+ else if (elemText == QString::fromLatin1("es-pr"))
+ d->language = es_pr;
+ else if (elemText == QString::fromLatin1("es-es"))
+ d->language = es_es;
+ else if (elemText == QString::fromLatin1("es-uy"))
+ d->language = es_uy;
+ else if (elemText == QString::fromLatin1("es-ve"))
+ d->language = es_ve;
+ else if (elemText == QString::fromLatin1("sv"))
+ d->language = sv;
+ else if (elemText == QString::fromLatin1("sv-fi"))
+ d->language = sv_fi;
+ else if (elemText == QString::fromLatin1("sv-se"))
+ d->language = sv_se;
+ else if (elemText == QString::fromLatin1("tr"))
+ d->language = tr;
+ else if (elemText == QString::fromLatin1("uk"))
+ d->language = uk;
+ else
+ d->language = UndefinedLanguage;
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("pubDate"))).isNull()) {
+ time_t _time = KRFCDate::parseDate(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("dc:date"))).isNull()) {
+ time_t _time = KRFCDate::parseDateISO8601(elemText);
+ /* \bug This isn't really the right way since it will set the date to
+ * Jan 1 1970, 1:00:00 if the passed date was invalid; this means that
+ * we cannot distinguish between that date, and invalid values. :-/
+ */
+ d->pubDate.setTime_t(_time);
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("lastBuildDate"))).isNull()) {
+ time_t _time = KRFCDate::parseDate(elemText);
+ d->lastBuildDate.setTime_t(_time);
+ }
+
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("rating"))).isNull())
+ d->rating = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("docs"))).isNull())
+ d->docs = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("managingEditor"))).isNull())
+ d->managingEditor = elemText;
+ if (!(elemText = extractNode(channelNode, QString::fromLatin1("webMaster"))).isNull())
+ d->webMaster = elemText;
+
+ n = channelNode.namedItem(QString::fromLatin1("skipHours"));
+ if (!n.isNull())
+ for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement())
+ if (e.tagName() == QString::fromLatin1("hour"))
+ d->skipHours.append(e.text().toUInt());
+
+ n = channelNode.namedItem(QString::fromLatin1("skipDays"));
+ if (!n.isNull()) {
+ Day day;
+ QString elemText;
+ for (QDomElement e = n.firstChild().toElement(); !e.isNull(); e = e.nextSibling().toElement())
+ if (e.tagName() == QString::fromLatin1("day")) {
+ elemText = e.text().lower();
+ if (elemText == QString::fromLatin1("monday"))
+ day = Monday;
+ else if (elemText == QString::fromLatin1("tuesday"))
+ day = Tuesday;
+ else if (elemText == QString::fromLatin1("wednesday"))
+ day = Wednesday;
+ else if (elemText == QString::fromLatin1("thursday"))
+ day = Thursday;
+ else if (elemText == QString::fromLatin1("friday"))
+ day = Friday;
+ else if (elemText == QString::fromLatin1("saturday"))
+ day = Saturday;
+ else if (elemText == QString::fromLatin1("sunday"))
+ day = Sunday;
+ else
+ day = UndefinedDay;
+ if (day != UndefinedDay)
+ d->skipDays.append(day);
+ }
+ }
+}
+
+Document::~Document()
+{
+ if (d->deref())
+ delete d;
+}
+
+Version Document::version() const
+{
+ return d->version;
+}
+
+QString Document::verbVersion() const
+{
+ switch (d->version) {
+ case v0_90: return QString::fromLatin1("0.90");
+ case v0_91: return QString::fromLatin1("0.91");
+ case v0_92: return QString::fromLatin1("0.92");
+ case v0_93: return QString::fromLatin1("0.93");
+ case v0_94: return QString::fromLatin1("0.94");
+ case v1_0: return QString::fromLatin1("1.0");
+ case v2_0: return QString::fromLatin1("2.0");
+ }
+ return QString::null;
+}
+
+QString Document::title() const
+{
+ return d->title;
+}
+
+QString Document::description() const
+{
+ return d->description;
+}
+
+const KURL &Document::link() const
+{
+ return d->link;
+}
+
+Image *Document::image()
+{
+ return d->image;
+}
+
+const Image *Document::image() const
+{
+ return d->image;
+}
+
+TextInput *Document::textInput()
+{
+ return d->textInput;
+}
+
+const TextInput *Document::textInput() const
+{
+ return d->textInput;
+}
+
+const Article::List &Document::articles() const
+{
+ return d->articles;
+}
+
+Language Document::language() const
+{
+ return d->language;
+}
+
+QString Document::copyright() const
+{
+ return d->copyright;
+}
+
+const QDateTime &Document::pubDate() const
+{
+ return d->pubDate;
+}
+
+const QDateTime &Document::lastBuildDate() const
+{
+ return d->lastBuildDate;
+}
+
+QString Document::rating() const
+{
+ return d->rating;
+}
+
+const KURL &Document::docs() const
+{
+ return d->docs;
+}
+
+QString Document::managingEditor() const
+{
+ return d->managingEditor;
+}
+
+QString Document::webMaster() const
+{
+ return d->webMaster;
+}
+
+const HourList &Document::skipHours() const
+{
+ return d->skipHours;
+}
+
+const DayList &Document::skipDays() const
+{
+ return d->skipDays;
+}
+
+Document &Document::operator=(const Document &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+// vim:noet:ts=4
diff --git a/librss/document.h b/librss/document.h
new file mode 100644
index 00000000..90359871
--- /dev/null
+++ b/librss/document.h
@@ -0,0 +1,231 @@
+/*
+ * document.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_DOCUMENT_H
+#define LIBRSS_DOCUMENT_H
+
+#include "article.h"
+#include "global.h"
+
+class QDateTime;
+class QDomDocument;
+
+namespace RSS
+{
+ class Image;
+ class TextInput;
+
+ /**
+ * Represents a RSS document and provides all the features and properties
+ * as stored in it. You usually don't need to instantiate this one yourself
+ * but rather use Loader::loadFrom() to produce a Document object.
+ * @see Loader::loadForm()
+ */
+ class LIBRSS_EXPORT Document
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ Document();
+
+ /**
+ * Copy constructor.
+ * @param other The Document object to copy.
+ */
+ Document(const Document &other);
+
+ /**
+ * Constructs a Document from a piece of XML markup.
+ */
+ Document(const QDomDocument &doc);
+
+ /**
+ * Assignment operator.
+ * @param other The Document object to clone.
+ * @return A reference to the cloned Document object.
+ */
+ Document &operator=(const Document &other);
+
+ /**
+ * Destructor.
+ */
+ ~Document();
+
+ /**
+ * @return The version of this document (one of the values of the
+ * enum RSS::Version). This value can be used to determine which
+ * features this RSS document provides.
+ * @see verbVersion()
+ */
+ Version version() const;
+
+ /**
+ * Convenience method. Differs from version() only in how the result
+ * is returned.
+ * @return A QString representing the verbose version of the
+ * document.
+ * @see version()
+ */
+ QString verbVersion() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The title of the RSS document, or QString::null if no
+ * title was available. This is often the name of the news source
+ * from which the RSS document was retrieved.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The description of the RSS document, or QString::null
+ * if no description was available. This is usually a short slogan
+ * or description of the news source from which the RSS document
+ * was retrieved.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link pointing to some website, or an empty KURL if no
+ * link was available. This URL mostly points to the homepage of
+ * the news site from which the RSS document was retrieved.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return An Image object as stored in the RSS document, or a
+ * null pointer if there was no image available.
+ * @see Image
+ */
+ Image *image();
+
+ /**
+ * A version of the method above, with stricter const-ness.
+ */
+ const Image *image() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A TextInput object as stored in the RSS document, or a
+ * null pointer if there was no text input available.
+ * @see TextInput
+ */
+ TextInput *textInput();
+
+ /**
+ * A version of the method above, with stricter const-ness.
+ */
+ const TextInput *textInput() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A list of Article objects as stored in the RSS document,
+ * or a null pointer if there were no articles available. Every RSS
+ * DTD requires that there is at least one article defined, so a
+ * null pointer indicates an invalid RSS file!
+ * @see Article
+ */
+ const Article::List &articles() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The language used in the RSS document (for the article
+ * headlines etc.). This was originally introduced to assist with
+ * determining the correct page encoding but acts as a solely
+ * optional information in this library since you don't have to care
+ * about the encoding as Unicode is used in the whole library.
+ * @see RSS::Language
+ */
+ Language language() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A copyright of the information contained in the RSS
+ * document, or QString::null if no copyright is available.
+ */
+ QString copyright() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The date when the RSS document was published.
+ */
+ const QDateTime &pubDate() const;
+
+ /**
+ * RSS 0.91 and upwards.
+ * @return The last time the channel was modified.
+ */
+ const QDateTime &lastBuildDate() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A <a href="http://www.w3.org/PICS/#Specs">PICS</a>
+ * rating for this page.
+ */
+ QString rating() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return This tag should contain either a URL that references a
+ * description of the channel, or a pointer to the documentation
+ * for the format used in the RSS file.
+ */
+ const KURL &docs() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The email address of the managing editor of the site,
+ * the person to contact for editorial inquiries. The suggested
+ * format for email addresses in RSS documents is
+ * bull@mancuso.com (Bull Mancuso).
+ * @see webMaster()
+ */
+ QString managingEditor() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The email address of the webmaster for the site, the
+ * person to contact if there are technical problems with the
+ * channel, or QString::null if this information isn't available.
+ * @see managingEditor()
+ */
+ QString webMaster() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A list of hours indicating the hours in the day, GMT,
+ * when the channel is unlikely to be updated. If this item is
+ * omitted, the channel is assumed to be updated hourly. Each
+ * hour should be an integer value between 0 and 23.
+ * @see skipDays()
+ */
+ const HourList &skipHours() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A list of <day>s of the week, in English, indicating
+ * the days of the week when the RSS document will not be updated.
+ * @see skipHours(), DayList, Day
+ */
+ const DayList &skipDays() const;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_DOCUMENT_H
+// vim: noet:ts=4
diff --git a/librss/global.h b/librss/global.h
new file mode 100644
index 00000000..26c34d7e
--- /dev/null
+++ b/librss/global.h
@@ -0,0 +1,138 @@
+/*
+ * global.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_GLOBAL_H
+#define LIBRSS_GLOBAL_H
+
+#include <kdemacros.h>
+
+#define LIBRSS_EXPORT KDE_EXPORT
+
+template <class>
+class QValueList;
+
+namespace RSS
+{
+ /**
+ * Versions currently supported by this library. This enumeration is
+ * subject to be extended in the future and used by Document::version() to
+ * provide an interface to the client using which he can find out what
+ * version the loaded RSS file actually is.
+ */
+ enum Version {
+ v0_90, /// RSS v0.90
+ v0_91, /// RSS v0.91
+ v0_92, /// RSS v0.92
+ v0_93, /// RSS v0.93
+ v0_94, /// RSS v0.94
+ v1_0, /// RSS v1.0
+ v2_0 /// RSS v2.0
+ };
+
+ /**
+ * Possible status values returned by the signal
+ * Loader::loadingComplete().
+ */
+ enum Status {
+ Success, /**
+ * Nothing went wrong so far, but you still have to check
+ * what values are returned by the classes since it's not
+ * guaranteed that the retrieved RSS markup actually
+ * complies to one of the RSS DTDs.*/
+ RetrieveError, /**
+ * Something went wrong while retrieving the RSS data,
+ * this could be a problem while resolving the host name
+ * (assuming the source file loader was used) or a
+ * problem with the program to be executed (in case the
+ * program loader was used.).*/
+ ParseError /**
+ * The overall format of the RSS markup wasn't XML
+ * conform. This only indicates that the data wasn't
+ * valid (for example, if the data returned by a
+ * DataRetriever isn't well-formed XML).
+ * @see DataRetriever */
+ };
+
+ /**
+ * Possible languages which are returned by Document::language().
+ */
+ enum Language {
+ UndefinedLanguage, /** Unknown / undefined language */
+
+ af, /** Afrikaans */ sq, /** Albanian */
+ eu, /** Basque */ be, /** Belarusian */
+ bg, /** Bulgarian */ ca, /** Catalan */
+ zh_cn, /** Chinese (Simplified) */ zh_tw, /** Chinese (Traditional */
+ hr, /** Croatian */ cs, /** Czech */
+ da, /** Danish */ nl, /** Dutch */
+ nl_be, /** Dutch (Belgium) */ nl_nl, /** Dutch (Netherlands) */
+ en, /** English */ en_au, /** English (Australia) */
+ en_bz, /** English (Belize) */ en_ca, /** English (Canada) */
+ en_ie, /** English (Ireland) */ en_jm, /** English (Jamaica) */
+ en_nz, /** English (New Zealand) */ en_ph, /** English (Phillipines) */
+ en_za, /** English (South Africa) */ en_tt, /** English (Trinidad) */
+ en_gb, /** English (Great Britain) */en_us, /** English (United States) */
+ en_zw, /** English (Zimbabwe) */ fo, /** Faeroese */
+ fi, /** Finnish */ fr, /** French */
+ fr_be, /** French (Belgium) */ fr_ca, /** French (Canada) */
+ fr_fr, /** French (France) */ fr_lu, /** French (Luxembourg) */
+ fr_mc, /** French (Monaco) */ fr_ch, /** French (Switzerland) */
+ gl, /** Galician */ gd, /** Gaelic */
+ de, /** German */ de_at, /** German (Austria) */
+ de_de, /** German (Germany) */ de_li, /** German (Liechtenstein) */
+ de_lu, /** German (Luxembourg) */ de_ch, /** German (Switzerland) */
+ el, /** Greek */ hu, /** Hungarian */
+ is, /** Icelandic */ id, /** Indonesian */
+ ga, /** Irish */ it, /** Italian */
+ it_it, /** Italian (Italy) */ it_ch, /** Italian (Switzerland) */
+ ja, /** Japanese */ ko, /** Korean */
+ mk, /** Macedonian */ no, /** Norwegian */
+ pl, /** Polish */ pt, /** Portuguese */
+ pt_br, /** Portuguese (Brazil) */ pt_pt, /** Portuguese (Portugal) */
+ ro, /** Romanian */ ro_mo, /** Romanian (Moldova) */
+ ro_ro, /** Romanian (Romania) */ ru, /** Russian */
+ ru_mo, /** Russian (Moldova) */ ru_ru, /** Russian (Russia) */
+ sr, /** Serbian */ sk, /** Slovak */
+ sl, /** Slovenian */ es, /** Spanish */
+ es_ar, /** Spanish (Argentina) */ es_bo, /** Spanish (Bolivia) */
+ es_cl, /** Spanish (Chile) */ es_co, /** Spanish (Colombia) */
+ es_cr, /** Spanish (Costa Rica) */ es_do, /** Spanish (Dominican Rep.) */
+ es_ec, /** Spanish (Ecuador) */ es_sv, /** Spanish (El Salvador) */
+ es_gt, /** Spanish (Guatemala) */ es_hn, /** Spanish (Honduras) */
+ es_mx, /** Spanish (Mexico) */ es_ni, /** Spanish (Nicaragua) */
+ es_pa, /** Spanish (Panama) */ es_py, /** Spanish (Paraguay) */
+ es_pe, /** Spanish (Peru) */ es_pr, /** Spanish (Puerto Rico) */
+ es_es, /** Spanish (Spain) */ es_uy, /** Spanish (Uruguay) */
+ es_ve, /** Spanish (Venezuela) */ sv, /** Swedish */
+ sv_fi, /** Swedish (Finland) */ sv_se, /** Swedish (Sweden) */
+ tr, /** Turkish */ uk /** Ukranian */
+ };
+
+ /**
+ * Possible values contained in a DayList.
+ */
+ enum Day {
+ UndefinedDay,
+ Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
+ };
+
+ /**
+ * This type is used by Document::skipDays().
+ */
+ typedef QValueList<Day> DayList;
+
+ /**
+ * This type is used by Document::skipHours().
+ */
+ typedef QValueList<unsigned short> HourList;
+}
+
+#endif // LIBRSS_GLOBAL_H
+// vim: noet:ts=4
diff --git a/librss/image.cpp b/librss/image.cpp
new file mode 100644
index 00000000..66e3e135
--- /dev/null
+++ b/librss/image.cpp
@@ -0,0 +1,157 @@
+/*
+ * image.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "image.h"
+#include "tools_p.h"
+
+#include <kio/job.h>
+#include <kurl.h>
+
+#include <qbuffer.h>
+#include <qdom.h>
+#include <qpixmap.h>
+
+using namespace RSS;
+
+struct Image::Private : public Shared
+{
+ Private() : height(31), width(88), pixmapBuffer(NULL)
+ { }
+
+ QString title;
+ KURL url;
+ KURL link;
+ QString description;
+ unsigned int height;
+ unsigned int width;
+ QBuffer *pixmapBuffer;
+};
+
+Image::Image() : QObject(), d(new Private)
+{
+}
+
+Image::Image(const Image &other) : QObject(), d(0)
+{
+ *this = other;
+}
+
+Image::Image(const QDomNode &node) : QObject(), d(new Private)
+{
+ QString elemText;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("url"))).isNull())
+ d->url = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("height"))).isNull())
+ d->height = elemText.toUInt();
+ if (!(elemText = extractNode(node, QString::fromLatin1("width"))).isNull())
+ d->width = elemText.toUInt();
+}
+
+Image::~Image()
+{
+ if (d->deref())
+ {
+ delete d->pixmapBuffer;
+ d->pixmapBuffer=0L;
+ delete d;
+ }
+}
+
+QString Image::title() const
+{
+ return d->title;
+}
+
+const KURL &Image::url() const
+{
+ return d->url;
+}
+
+const KURL &Image::link() const
+{
+ return d->link;
+}
+
+QString Image::description() const
+{
+ return d->description;
+}
+
+unsigned int Image::height() const
+{
+ return d->height;
+}
+
+unsigned int Image::width() const
+{
+ return d->width;
+}
+
+void Image::getPixmap()
+{
+ // Ignore subsequent calls if we didn't finish the previous download.
+ if (d->pixmapBuffer)
+ return;
+
+ d->pixmapBuffer = new QBuffer;
+ d->pixmapBuffer->open(IO_WriteOnly);
+
+ KIO::Job *job = KIO::get(d->url, false, false);
+ connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ this, SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(job, SIGNAL(result(KIO::Job *)), this, SLOT(slotResult(KIO::Job *)));
+}
+
+void Image::slotData(KIO::Job *, const QByteArray &data)
+{
+ d->pixmapBuffer->writeBlock(data.data(), data.size());
+}
+
+void Image::slotResult(KIO::Job *job)
+{
+ QPixmap pixmap;
+ if (!job->error())
+ pixmap = QPixmap(d->pixmapBuffer->buffer());
+ emit gotPixmap(pixmap);
+
+ delete d->pixmapBuffer;
+ d->pixmapBuffer = NULL;
+}
+
+Image &Image::operator=(const Image &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool Image::operator==(const Image &other) const
+{
+ return d->title == other.title() &&
+ d->url == other.url() &&
+ d->description == other.description() &&
+ d->height == other.height() &&
+ d->width == other.width() &&
+ d->link == other.link();
+}
+
+#include "image.moc"
+// vim:noet:ts=4
diff --git a/librss/image.h b/librss/image.h
new file mode 100644
index 00000000..d93c3230
--- /dev/null
+++ b/librss/image.h
@@ -0,0 +1,172 @@
+/*
+ * image.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_IMAGE_H
+#define LIBRSS_IMAGE_H
+
+#include "global.h"
+
+#include <qobject.h>
+
+class QDomNode;
+
+namespace KIO
+{
+ class Job;
+}
+class KURL;
+
+namespace RSS
+{
+ /**
+ * Represents an image as stored in a RSS file. You don't have to
+ * instantiate one of these yourself, the common way to access instances
+ * is via Document::image().
+ * @see Document::image()
+ */
+ class LIBRSS_EXPORT Image : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ Image();
+
+ /**
+ * Copy constructor.
+ * @param other The Image object to copy.
+ */
+ Image(const Image &other);
+
+ /**
+ * Constructs an Image from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the Image.
+ */
+ Image(const QDomNode &node);
+
+ /**
+ * Assignment operator.
+ * @param other The Image object to clone.
+ * @return A reference to the cloned Image object.
+ */
+ Image &operator=(const Image &other);
+
+ /**
+ * Compares two images. Two images are considered identical if
+ * their properties (title, description, link etc.) are identical.
+ * Note that this does not include the actual pixmap data!
+ * @param other The image to compare with.
+ * @return Whether the two images are equal.
+ */
+ bool operator==(const Image &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The image to compared with.
+ * @return Whether the two images are unequal.
+ */
+ bool operator!=(const Image &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~Image();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The 'caption' of this image, or QString::null if no
+ * caption is available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The URL pointing to the file containing the graphic
+ * data (GIF, JPEG or PNG format), or an empty KURL if no URL
+ * is available. You can use getPixmap() and gotPixmap() to have
+ * the Image download the pixmap data itself.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &url() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link to some resource, or an empty KURL of no link is
+ * available. Clicking on the image should lead the user to the
+ * resource referenced by this URL.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return A description of what this picture shows, or
+ * QString::null if no description is available. Useful for
+ * people who deactivated images but want or need to know what is
+ * shown.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The height in pixels as reported by the news site, the
+ * default value is 31 pixels. The RSS 0.91 Specification requires
+ * this value to be between 1 and 400.
+ * '0' if this information isn't available. This is merely provided
+ * for completeness, you should not rely on this value but rather
+ * check what height the QPixmap as returned by gotPixmap()
+ * reports.
+ */
+ unsigned int height() const;
+
+ /**
+ * RSS 0.91 and upwards
+ * @return The width in pixels as reported by the news site, the
+ * default value is 88 pixels. The RSS 0.91 Specification requires
+ * this value to be between 1 and 144.
+ * This is merely provided for completeness, you should not rely
+ * on this value but rather check what width the QPixmap as
+ * returned by gotPixmap() reports.
+ */
+ unsigned int width() const;
+
+ /**
+ * Makes the image download the image data as referenced by the
+ * URL returned by url(). You have to connect to the signal
+ * gotPixmap() first and then call getPixmap().
+ */
+ void getPixmap();
+
+ signals:
+ /**
+ * Emitted when this Image is done downloading the actual graphics
+ * data as referenced by the URL returned by url(). You can trigger
+ * this download by calling getPixmap().
+ * @param pixmap The pixmap as constructed from the data referenced
+ * by the URL returned by link().
+ */
+ void gotPixmap(const QPixmap &pixmap);
+
+ private slots:
+ void slotData(KIO::Job *job, const QByteArray &data);
+ void slotResult(KIO::Job *job);
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_IMAGE_H
+// vim: noet:ts=4
diff --git a/librss/librss.doxyfile b/librss/librss.doxyfile
new file mode 100644
index 00000000..c81ac168
--- /dev/null
+++ b/librss/librss.doxyfile
@@ -0,0 +1,921 @@
+# Doxyfile 1.2.14
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# General configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = librss
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 0.1
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
+# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
+# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
+
+OUTPUT_LANGUAGE = English
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these class will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH =
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower case letters. If set to YES upper case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# users are adviced to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consist of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+
+INPUT_FILTER =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse.
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default)
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default)
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the Html help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
+# or Internet explorer 4.0+). Note that for large projects the tree generation
+# can take a very long time. In such cases it is better to disable this feature.
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = YES
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimised for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assigments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_XML = NO
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line and do not end with a semicolon. Such function macros are typically
+# used for boiler-plate code, and will confuse the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tagfiles.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
+# recommended to install and use dot, since it yield more powerful graphs.
+
+CLASS_DIAGRAMS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are gif, jpg, and png
+# If left blank gif will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_WIDTH = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than
+# this value, doxygen will try to truncate the graph, so that it fits within
+# the specified constraint. Beware that most browsers cannot cope with very
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT = 1024
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermedate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE = NO
+
+# The CGI_NAME tag should be the name of the CGI script that
+# starts the search engine (doxysearch) with the correct parameters.
+# A script with this name will be generated by doxygen.
+
+CGI_NAME = search.cgi
+
+# The CGI_URL tag should be the absolute URL to the directory where the
+# cgi binaries are located. See the documentation of your http daemon for
+# details.
+
+CGI_URL =
+
+# The DOC_URL tag should be the absolute URL to the directory where the
+# documentation is located. If left blank the absolute path to the
+# documentation, with file:// prepended to it, will be used.
+
+DOC_URL =
+
+# The DOC_ABSPATH tag should be the absolute path to the directory where the
+# documentation is located. If left blank the directory on the local machine
+# will be used.
+
+DOC_ABSPATH =
+
+# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
+# is installed.
+
+BIN_ABSPATH = /usr/local/bin/
+
+# The EXT_DOC_PATHS tag can be used to specify one or more paths to
+# documentation generated for other projects. This allows doxysearch to search
+# the documentation for these projects as well.
+
+EXT_DOC_PATHS =
diff --git a/librss/librss.h b/librss/librss.h
new file mode 100644
index 00000000..c9637d0c
--- /dev/null
+++ b/librss/librss.h
@@ -0,0 +1,22 @@
+/*
+ * librss.h
+ *
+ * Copyright (c) 2003 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_LIBRSS_H
+#define LIBRSS_LIBRSS_H
+
+#include "article.h"
+#include "document.h"
+#include "global.h"
+#include "image.h"
+#include "loader.h"
+#include "textinput.h"
+
+#endif // LIBRSS_LIBRSS_H
+// vim: noet:ts=4
diff --git a/librss/loader.cpp b/librss/loader.cpp
new file mode 100644
index 00000000..c38400f4
--- /dev/null
+++ b/librss/loader.cpp
@@ -0,0 +1,285 @@
+/*
+ * loader.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "loader.h"
+#include "document.h"
+
+#include <kio/job.h>
+#include <kprocess.h>
+#include <kurl.h>
+
+#include <qdom.h>
+#include <qbuffer.h>
+#include <qregexp.h>
+
+using namespace RSS;
+
+DataRetriever::DataRetriever()
+{
+}
+
+DataRetriever::~DataRetriever()
+{
+}
+
+struct FileRetriever::Private
+{
+ Private()
+ : buffer(NULL),
+ lastError(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete buffer;
+ }
+
+ QBuffer *buffer;
+ int lastError;
+};
+
+FileRetriever::FileRetriever()
+ : d(new Private)
+{
+}
+
+FileRetriever::~FileRetriever()
+{
+ delete d;
+}
+
+void FileRetriever::retrieveData(const KURL &url)
+{
+ if (d->buffer)
+ return;
+
+ d->buffer = new QBuffer;
+ d->buffer->open(IO_WriteOnly);
+
+ KIO::Job *job = KIO::get(url, false, false);
+ connect(job, SIGNAL(data(KIO::Job *, const QByteArray &)),
+ SLOT(slotData(KIO::Job *, const QByteArray &)));
+ connect(job, SIGNAL(result(KIO::Job *)), SLOT(slotResult(KIO::Job *)));
+ connect(job, SIGNAL(permanentRedirection(KIO::Job *, const KURL &, const KURL &)),
+ SLOT(slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &)));
+}
+
+int FileRetriever::errorCode() const
+{
+ return d->lastError;
+}
+
+void FileRetriever::slotData(KIO::Job *, const QByteArray &data)
+{
+ d->buffer->writeBlock(data.data(), data.size());
+}
+
+void FileRetriever::slotResult(KIO::Job *job)
+{
+ QByteArray data = d->buffer->buffer();
+ data.detach();
+
+ delete d->buffer;
+ d->buffer = NULL;
+
+ d->lastError = job->error();
+ emit dataRetrieved(data, d->lastError == 0);
+}
+
+void FileRetriever::slotPermanentRedirection(KIO::Job *, const KURL &, const KURL &newUrl)
+{
+ emit permanentRedirection(newUrl);
+}
+
+struct OutputRetriever::Private
+{
+ Private() : process(NULL),
+ buffer(NULL),
+ lastError(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete process;
+ delete buffer;
+ }
+
+ KShellProcess *process;
+ QBuffer *buffer;
+ int lastError;
+};
+
+OutputRetriever::OutputRetriever() :
+ d(new Private)
+{
+}
+
+OutputRetriever::~OutputRetriever()
+{
+ delete d;
+}
+
+void OutputRetriever::retrieveData(const KURL &url)
+{
+ // Ignore subsequent calls if we didn't finish the previous job yet.
+ if (d->buffer || d->process)
+ return;
+
+ d->buffer = new QBuffer;
+ d->buffer->open(IO_WriteOnly);
+
+ d->process = new KShellProcess();
+ connect(d->process, SIGNAL(processExited(KProcess *)),
+ SLOT(slotExited(KProcess *)));
+ connect(d->process, SIGNAL(receivedStdout(KProcess *, char *, int)),
+ SLOT(slotOutput(KProcess *, char *, int)));
+ *d->process << url.path();
+ d->process->start(KProcess::NotifyOnExit, KProcess::Stdout);
+}
+
+int OutputRetriever::errorCode() const
+{
+ return d->lastError;
+}
+
+void OutputRetriever::slotOutput(KProcess *, char *data, int length)
+{
+ d->buffer->writeBlock(data, length);
+}
+
+void OutputRetriever::slotExited(KProcess *p)
+{
+ if (!p->normalExit())
+ d->lastError = p->exitStatus();
+
+ QByteArray data = d->buffer->buffer();
+ data.detach();
+
+ delete d->buffer;
+ d->buffer = NULL;
+
+ delete d->process;
+ d->process = NULL;
+
+ emit dataRetrieved(data, p->normalExit() && p->exitStatus() == 0);
+}
+
+struct Loader::Private
+{
+ Private() : retriever(NULL),
+ lastError(0)
+ {
+ }
+
+ ~Private()
+ {
+ delete retriever;
+ }
+
+ DataRetriever *retriever;
+ int lastError;
+};
+
+Loader *Loader::create()
+{
+ return new Loader;
+}
+
+Loader *Loader::create(QObject *object, const char *slot)
+{
+ Loader *loader = create();
+ connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)),
+ object, slot);
+ return loader;
+}
+
+Loader::Loader() : d(new Private)
+{
+}
+
+Loader::~Loader()
+{
+ delete d;
+}
+
+void Loader::loadFrom(const KURL &url, DataRetriever *retriever)
+{
+ if (d->retriever != NULL)
+ return;
+
+ d->retriever = retriever;
+
+ connect(d->retriever, SIGNAL(dataRetrieved(const QByteArray &, bool)),
+ this, SLOT(slotRetrieverDone(const QByteArray &, bool)));
+
+ d->retriever->retrieveData(url);
+}
+
+int Loader::errorCode() const
+{
+ return d->lastError;
+}
+
+void Loader::slotRetrieverDone(const QByteArray &data, bool success)
+{
+ d->lastError = d->retriever->errorCode();
+
+ delete d->retriever;
+ d->retriever = NULL;
+
+ Document rssDoc;
+ Status status = Success;
+
+ if (success) {
+ QDomDocument doc;
+
+ /* Some servers insert whitespace before the <?xml...?> declaration.
+ * QDom doesn't tolerate that (and it's right, that's invalid XML),
+ * so we strip that.
+ */
+
+ const char *charData = data.data();
+ int len = data.count();
+
+ while (len && QChar(*charData).isSpace()) {
+ --len;
+ ++charData;
+ }
+
+ /* It seems that some Microsoft-developed code generates UTF-8 files
+ * with the three leading unicode characters 0357, 0273, 0277. For
+ * an example, check http://msdn.microsoft.com/rss.xml
+ */
+ if (len > 3 && QChar(*charData) == QChar(0357)) {
+ len -= 3;
+ charData += 3;
+ }
+
+ QByteArray tmpData;
+ tmpData.setRawData(charData, len);
+
+ if (doc.setContent(tmpData))
+ rssDoc = Document(doc);
+ else
+ status = ParseError;
+
+ tmpData.resetRawData(charData, len);
+ } else
+ status = RetrieveError;
+
+ emit loadingComplete(this, rssDoc, status);
+
+ delete this;
+}
+
+#include "loader.moc"
+// vim:noet:ts=4
diff --git a/librss/loader.h b/librss/loader.h
new file mode 100644
index 00000000..5f0c1447
--- /dev/null
+++ b/librss/loader.h
@@ -0,0 +1,322 @@
+/*
+ * loader.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_LOADER_H
+#define LIBRSS_LOADER_H
+
+#include "global.h"
+
+class KURL;
+
+#include <qobject.h>
+
+namespace KIO
+{
+ class Job;
+}
+class KProcess;
+
+namespace RSS
+{
+ class Document;
+
+ /**
+ * Abstract baseclass for all data retriever classes. Subclass this to add
+ * a new retrieval algorithm which can then be plugged into the RSS loader.
+ * @see Loader, FileRetriever, OutputRetriever
+ */
+ class DataRetriever : public QObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ DataRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~DataRetriever();
+
+ /**
+ * Retrieve data from the given URL. This method is supposed to get
+ * reimplemented by subclasses. It will be called by the Loader
+ * class in case it needs to retrieve the data.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url) = 0;
+
+ /**
+ * @return An error code which might give a more precise information
+ * about what went wrong in case the 'success' flag returned with
+ * the dataRetrieved() signal was 'false'. Note that the meaning of
+ * the returned integer depends on the actual data retriever.
+ */
+ virtual int errorCode() const = 0;
+
+ signals:
+ /**
+ * Emit this signal to tell the Loader class that the retrieval
+ * process was finished.
+ * @param data Should contain the retrieved data and will get
+ * parsed by the Loader class.
+ * @param success Indicates whether there were any problems during
+ * the retrieval process. Pass 'true' to indicate that everything
+ * went seamlessy, 'false' to tell the Loader that something went
+ * wrong and that the data parameter might contain no or invalid
+ * data.
+ */
+ void dataRetrieved(const QByteArray &data, bool success);
+
+ private:
+ DataRetriever(const DataRetriever &other);
+ DataRetriever &operator=(const DataRetriever &other);
+ };
+
+ /**
+ * Implements a file retriever, to be used with Loader::loadFrom().
+ * @see DataRetriever, Loader::loadFrom()
+ */
+ class LIBRSS_EXPORT FileRetriever : public DataRetriever
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ FileRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~FileRetriever();
+
+ /**
+ * Downloads the file referenced by the given URL and passes it's
+ * contents on to the Loader.
+ * @param url An URL referencing a file which is assumed to
+ * reference valid XML.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url);
+
+ /**
+ * @return The error code for the last process of retrieving data.
+ * The returned numbers correspond directly to the error codes
+ * <a href="http://developer.kde.org/documentation/library/cvs-api/classref/kio/KIO.html#Error">as
+ * defined by KIO</a>.
+ */
+ virtual int errorCode() const;
+
+ signals:
+ /**
+ * Signals a permanent redirection. The redirection itself is
+ * handled internally, so you don't need to call Loader::loadFrom()
+ * with the new URL. This signal is useful in case you want to
+ * notify the user, or adjust a database entry.
+ * @see Loader::loadFrom()
+ */
+ void permanentRedirection(const KURL &url);
+
+ private slots:
+ void slotData(KIO::Job *job, const QByteArray &data);
+ void slotResult(KIO::Job *job);
+ void slotPermanentRedirection(KIO::Job *job, const KURL &fromUrl,
+ const KURL &toUrl);
+
+ private:
+ FileRetriever(const FileRetriever &other);
+ FileRetriever &operator=(const FileRetriever &other);
+
+ struct Private;
+ Private *d;
+ };
+
+ /**
+ * Implements a data retriever which executes a program and stores returned
+ * by the program on stdout. To be used with Loader::loadFrom().
+ * @see DataRetriever, Loader::loadFrom()
+ */
+ class OutputRetriever : public DataRetriever
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Default constructor.
+ */
+ OutputRetriever();
+
+ /**
+ * Destructor.
+ */
+ virtual ~OutputRetriever();
+
+ /**
+ * Executes the program referenced by the given URL and retrieves
+ * the data which the program prints to stdout.
+ * @param url An URL which is supposed to reference an executable
+ * file.
+ * @see Loader::loadFrom()
+ */
+ virtual void retrieveData(const KURL &url);
+
+ /**
+ * @return The error code for the last process of retrieving data.
+ * 0 is returned in case there was no error, otherwise an error
+ * code which depends on the particular program which was run is
+ * returned.
+ */
+ virtual int errorCode() const;
+
+ private slots:
+ void slotOutput(KProcess *process, char *data, int length);
+ void slotExited(KProcess *process);
+
+ private:
+ OutputRetriever(const OutputRetriever &other);
+ OutputRetriever &operator=(const OutputRetriever &other);
+
+ struct Private;
+ Private *d;
+ };
+
+ /**
+ * This class is the preferred way of loading RSS files. Usage is very
+ * straightforward:
+ *
+ * \code
+ * Loader *loader = Loader::create();
+ * connect(loader, SIGNAL(loadingComplete(Loader *, Document, Status)),
+ * this, SLOT(slotLoadingComplete(Loader *, Document, Status)));
+ * loader->loadFrom("http://www.blah.org/foobar.rdf", new FileRetriever);
+ * \endcode
+ *
+ * This creates a Loader object, connects it's loadingComplete() signal to
+ * your custom slot and then makes it load the file
+ * 'http://www.blah.org/foobar.rdf' using the FileRetriever. You could've
+ * done something like this as well:
+ *
+ * \code
+ * // create the Loader, connect it's signal...
+ * loader->loadFrom("/home/myself/some-script.py", new OutputRetriever);
+ * \endcode
+ *
+ * That'd make the Loader use another algorithm for retrieving the RSS data;
+ * 'OutputRetriever' will make it execute the script
+ * '/home/myself/some-script.py' and assume whatever that script prints to
+ * stdout is RSS markup. This is e.g. handy for conversion scripts, which
+ * download a HTML file and convert it's contents into RSS markup.
+ *
+ * No matter what kind of retrieval algorithm you employ, your
+ * 'slotLoadingComplete' method might look like this:
+ *
+ * \code
+ * void MyClass::slotLoadingComplete(Loader *loader, Document doc, Status status)
+ * {
+ * // Note that Loader::~Loader() is private, so you cannot delete Loader instances.
+ * // You don't need to do that anyway since Loader instances delete themselves.
+ *
+ * if (status != RSS::Success)
+ * return;
+ *
+ * QString title = doc.title();
+ * // do whatever you want with the information.
+ * }
+ * \endcode
+ *
+ * \note You have to create a copy of the passed Document instance in
+ * case you want/need to use it after the slot attached to the
+ * loadingComplete signal goes out of scope. This is e.g. the case if you
+ * intend to call getPixmap() on Document::image()!
+ */
+ class LIBRSS_EXPORT Loader : public QObject
+ {
+ Q_OBJECT
+ friend class someClassWhichDoesNotExist;
+ public:
+ /**
+ * Constructs a Loader instance. This is pretty much what the
+ * default constructor would do, except that it ensures that all
+ * Loader instances have been allocated on the heap (this is
+ * required so that Loader's can delete themselves safely after they
+ * emitted the loadingComplete() signal.).
+ * @return A pointer to a new Loader instance.
+ */
+ static Loader *create();
+
+ /**
+ * Convenience method. Does the same as the above method except that
+ * it also does the job of connecting the loadingComplete() signal
+ * to the given slot for you.
+ * @param object A QObject which features the specified slot
+ * @param slot Which slot to connect to.
+ */
+ static Loader *create(QObject *object, const char *slot);
+
+ /**
+ * Loads the RSS file referenced by the given URL using the
+ * specified retrieval algorithm. Make sure that you connected
+ * to the loadingComplete() signal before calling this method so
+ * that you're guaranteed to get notified when the loading finished.
+ * \note A Loader object cannot load from multiple URLs simultaneously;
+ * consequently, subsequent calls to loadFrom will be discarded
+ * silently, only the first loadFrom request will be executed.
+ * @param url An URL referencing the input file.
+ * @param retriever A subclass of DataRetriever which implements a
+ * specialized retrieval behaviour. Note that the ownership of the
+ * retriever is transferred to the Loader, i.e. the Loader will
+ * delete it when it doesn't need it anymore.
+ * @see DataRetriever, Loader::loadingComplete()
+ */
+ void loadFrom(const KURL &url, DataRetriever *retriever);
+
+ /**
+ * Retrieves the error code of the last loading process (if any),
+ * as reported by the employed data retrever.
+ */
+ int errorCode() const;
+
+ signals:
+ /**
+ * This signal gets emitted when the loading process triggered by
+ * calling loadFrom() finished.
+ * @param loader A pointer pointing to the loader object which
+ * emitted this signal; this is handy in case you connect multiple
+ * loaders to a single slot.
+ * @param doc In case status is Success, this parameter holds the
+ * parsed RSS file. In case it's RetrieveError, you should query
+ * loader->errorCode() for the actual error code.
+ * Note that you have to create a copy of the passed Document
+ * instance in case you want/need to use it after the slot attached
+ * to the loadingComplete signal goes out of scope. This is e.g.
+ * the case if you intend to call getPixmap() on Document::image()!
+ * @param status A status byte telling whether there were any problems
+ * while retrieving or parsing the data.
+ * @see Document, Status
+ */
+ void loadingComplete(Loader *loader, Document doc, Status status);
+
+ private slots:
+ void slotRetrieverDone(const QByteArray &data, bool success);
+
+ private:
+ Loader();
+ Loader(const Loader &other);
+ Loader &operator=(const Loader &other);
+ ~Loader();
+
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_LOADER_H
+// vim: noet:ts=4
diff --git a/librss/rss-faq.html b/librss/rss-faq.html
new file mode 100644
index 00000000..9ea92dfb
--- /dev/null
+++ b/librss/rss-faq.html
@@ -0,0 +1,396 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html>
+<head>
+<title>RSS Headline Syndication - Frequently Asked Questions for Content Providers</title>
+<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
+<meta name="description" content="RSS Headline Syndication - Frequently Asked Questions for Content Providers" />
+<meta name="keywords" content="rss faq, rss, faq, rich site summary, rdf site summary, really simple syndication, headline syndication, syndication" />
+<meta name="robots" content="index,follow" />
+<style type="text/css">
+<!--
+td.content
+{
+font-family: sans-serif;
+color: #000000;
+font-size: 10pt;
+font-weight: normal;
+}
+td.contentbold
+{
+font-family: sans-serif;
+color: #000000;
+font-size: 10pt;
+font-weight: bold;
+}
+td.contentsmall
+{
+font-family: sans-serif;
+color: #000000;
+font-size: 8pt;
+font-weight: normal;
+}
+-->
+</style>
+</head>
+<body bgcolor="#ffffff">
+<p align="right">
+<i>
+ This document was taken from <a href="http://www.purplepages.ie/rss/">http://www.purplepages.ie/rss/</a>.<br/>
+ <tt><a href="mailto:raabe@kde.org">Frerich Raabe</a></tt>
+</i>
+</p>
+<table border="0" width="620" cellpadding="1" cellspacing="0">
+ <tr>
+ <td width="620" class="contentbold" colspan="2">RSS Headline Syndication<br /><br />
+ <a name="top">Frequently Asked Questions for Content Providers</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2">
+ <br /><a href="#whatishs">1. What is headline syndication?</a><br />
+ <a href="#rss">2. What is RSS?</a><br />
+ <a href="#whyrss">3. Why syndicate your headlines with RSS?</a><br />
+ <a href="#howrss">4. How can I create an RSS file?</a><br />
+ <a href="#promoterss">5. How can I promote my RSS file?</a><br />
+ <a href="#morerss">6. Where can I find more information about RSS?</a><br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="whatishs">1. What is headline syndication?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Websites that publish new content regularly usually provide a list of news headline style links to their latest content. In addition to displaying these headlines on their own websites, it is very common for publishers to make them available for syndication, so that other websites or applications can also include their headlines.<br /><br />
+ Headline syndication does not deal with the full text of articles, it is simply about syndicating an automatically updating list of headlines, with each headline being a link to the item that it refers to on the publishers website.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="rss">2. What is RSS?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ RSS is the name given to a simple and well-established XML format used to syndicate headlines. Once a website creates an RSS file they have created a means to allow others to syndicate their headlines.<br /><br />
+ The first version of RSS (RSS 0.9) was released by <a href="http://www.netscape.com/">Netscape</a> in March 1999 as a format for adding news channels to their <a href="http://my.netscape.com/">My.Netscape.Com</a> portal. Then in July 1999 Netscape released RSS 0.91, incorporating most of the features of a format called &lt;scriptingNews&gt;, which was created by <a href="http://www.userland.com/">UserLand</a>. Shortly thereafter Netscape discontinued developing the RSS format, however UserLand persisted and RSS continued to grow in strength. In December 2000, the separate RSS-DEV Working Group released RSS 1.0 and Userland announced RSS 0.92. As of April 2001, Userland is now planning RSS 0.93. Although RSS is not clearly an acronym of anything, different people have called it Rich Site Summary, RDF Site Summary and Really Simple Syndication at different times.<br /><br />
+ The lack of clarity in what RSS stands for or which version is the correct one to use can seem confusing to beginners. However these issues don't need to addressed by a website wanting to create an RSS file. RSS is a very well recognised format, in fact it is often referred to as the most successful XML format to date. Some websites have a preference for one version, others create more than one RSS file and support multiple versions and a recent survey suggests that the first two versions of RSS (0.9 and 0.91) are still by far the most popular.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="whyrss">3. Why syndicate your headlines with RSS?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Syndicating headlines is an excellent and cost-effective way of driving traffic to, and increasing brand awareness of, any website that publishes new content regularly.<br /><br />
+ Once a website produces an RSS file they are enabling others to syndicate their headlines, without any further work on their part.<br /><br />
+ The main benefits of creating an RSS file:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">RSS content can be included in customisable online news portals that aggregate RSS headlines like <a href="http://my.userland.com/">My.Userland.Com</a>.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Websites that display news headlines can use an RSS file to incorporate another websites headlines into their own.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">RSS content can be added to personal desktop news reading applications like <a href="http://www.headlineviewer.com/">Headline Viewer</a> or <a href="http://radio.userland.com/">Radio Userland</a>.
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Email newsletter providers could allow users to subscribe to RSS channels. <a href="http://www.xml.com/">XML.com</a> and <a href="http://www.xmltree.com/">XMLTree.com</a> previously offered such a service called Newsboy.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ One positive side effect of producing an RSS file is that it can also be used by headline aggregation services like <a href="http://www.moreover.com/">Moreover.com</a>, who power news portals, specialist news search engines, business intelligence services or provide newsfeeds to websites. Most such companies use crawler-based technologies to aggregate and do not insist upon content being available in RSS, however they do have some requirements which having an RSS file addresses, sparing the need for any work on the part of a website that already publishes its headlines in RSS.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="howrss">4. How can I create an RSS file?</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ RSS is a simple XML format and anyone who has experience in a mark-up language like HTML or XML should find it very easy to create and maintain an RSS file by hand.
+ <br /><br />Many websites prefer to generate their RSS file using a programming language, which involves a little more work to begin with but means that maintenance is no longer an issue.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ In this section:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2">
+ <a href="#specifications">RSS Specifications</a><br />
+ <a href="#validators">RSS Validators</a><br />
+ <a href="#tutorials">RSS Tutorials - The Basics</a><br />
+ <a href="#tutorialsgen">RSS Tutorials - Generating RSS</a><br />
+ <a href="#examples">RSS Examples</a><br />
+ <a href="#tools">RSS Tools &amp; Utilities</a><br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="specifications">RSS Specifications:</a><br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.93</strong> (Planning stage, April 2001)<br /><a href="http://backend.userland.com/rss093">http://backend.userland.com/rss093</a> (Userland)
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.92</strong> (December 2000)<br /><a href="http://backend.userland.com/rss092">http://backend.userland.com/rss092</a> (Userland)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 1.0</strong> (December 2000)<br /><a href="http://groups.yahoo.com/group/rss-dev/files/specification.html">http://groups.yahoo.com/group/rss-dev/files/specification.html</a> (RSS-DEV Working Group)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.91</strong> (July 1999)<br /><a href="http://backend.userland.com/rss091">http://backend.userland.com/rss091</a> (Userland)<br />
+ <a href="http://www.purplepages.ie/RSS/netscape/rss0.91.html">http://www.purplepages.ie/RSS/netscape/rss0.91.html</a> (Netscape)<br />
+ <a href="http://my.netscape.com/publish/formats/rss-spec-0.91.html">http://my.netscape.com/publish/formats/rss-spec-0.91.html</a> (Netscape, Revision 3)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" valign="top"><strong>RSS 0.90</strong> (March 1999)<br /><a href="http://www.purplepages.ie/RSS/netscape/rss0.90.html">http://www.purplepages.ie/RSS/netscape/rss0.90.html</a> (Netscape)<br />
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="validators">RSS Validators</a>:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://aggregator.userland.com/validator">http://aggregator.userland.com/validator</a> (RSS 0.91, RSS 0.92)</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/">http://www.bath.ac.uk/~ccslrd/rss_validator/1.0/</a> (RSS 1.0)</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content"><a href="http://www.bath.ac.uk/~ccslrd/rss_validator/">http://www.bath.ac.uk/~ccslrd/rss_validator/</a> (RSS 0.9)</td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <strong><a name="tutorials">RSS Tutorials - The Basics:</a></strong> (See also <a href="#specifications">RSS Specifications</a>, <a href="#websites">Websites</a>)<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://www.oreillynet.com/pub/a/network/4000/08/25/magazine/rss_tut.html">A step-by-step guide to building an RSS 1.0 document from the O'Reilly Network.</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://publishing.about.com/arts/publishing/library/blrss.htm">An easy to understand introduction to RSS 0.91 from About.com.</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"> - <a href="http://webreference.com/xml/column13/index.html">A comprehensive guide to creating RSS 0.91 files from Webreference.</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="tutorialsgen">RSS Tutorials - Generating RSS</a>:<br />&nbsp;
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Active Server Pages (ASP)<br />
+ <a href="http://www.purplepages.ie/site/articles/article.asp?faq=6&amp;fldAuto=76">An article explaining how RSS files can be generated using ASP.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">Perl<br />
+ <a href="http://www.webtechniques.com/archives/2000/02/eisenzopf/">Jonathan Eisenzopf explains how his XML::RSS module can be used to create an RSS file.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content">PHP<br />
+ <a href="http://linux.gelrevision.nl/php/">phpChannel, a set of two PHP class files to write rss files.</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="tools">RSS Tools &amp; Utilities</a>:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Aaron Swartz provides a useful online utility called <a href="http://logicerror.com/blogifyYourPage">BlogifyYourPage</a>, that makes it easy to produce an RSS 1.0 file for any page.<br /><br />
+ The <a href="http://www.webreference.com/perl/tools/">RSS Channel Editor</a> is a simple Perl CGI script that makes it easy to maintain an RSS channel. It can be used online at Webreference and you can also download the source.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="examples">RSS Examples</a>:
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="http://newsfeeds.manilasites.com/">Newsfeeds</a> reviews sources of RSS files, good examples and ideas you can use in putting together your own feed.<br /><br />
+ <a href="http://www.ourfavoritesongs.com/">OurFavoriteSongs.Com</a> is a source of popular syndicated files, the top picks of <a href="http://radio.userland.com/">Radio Userland</a> users.
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a name="promoterss"><strong>5. How can I promote my RSS file?</strong></a>
+ <br /><br />There are a couple of important places to register RSS files, firstly <a href="http://www.xmltree.com/">XMLTree.com</a>, a specialist directory of XML content, and secondly <a href="http://my.userland.com/">My.Userland.Com</a>. Once an RSS file has been included in these sources it is likely to be found by websites, online news portals or news reading applications seeking RSS content.<br /><br />
+ Websites should also create an information page, about syndicating their headlines. This will make existing users aware that the website has an RSS file so they can add it to their news reading applications or even include it on their own websites.<br /><br />
+ This information page will be indexed by regular search engines and can also be submitted to various niche directories:
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.4freecontent.com/">4FreeContent</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.findsticky.com/">FindSticky</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.freesticky.com/">FreeSticky</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://newsfeeds.manilasites.com/">Newsfeeds</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.purplepages.ie/site/content/">Purple Pages</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.woodoggy.com/">WooDoggy</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ Websites that are interested in having their headlines picked up by organisations that aggregate headline content may also wish to visit:
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.linkyournews.com/">LinkYourNews.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.magportal.com/">MagPortal.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.moreover.com/">Moreover.com</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.newsnow.co.uk/">NewsNow.co.uk</a></td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.newsisfree.com/">NewsIsFree.com</a></td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ <a name="morerss">6. Where can I find more information about RSS?</a><br /><br />
+ <a name="websites">Websites</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.oreillynet.com/rss/">O'Reilly DevCenter RSS</a> - Articles about RSS from the O'Reilly Network.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://blogspace.com/rss/">RSS Info</a> - News and information on the RSS format</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://rsswhys.weblogger.com/">RSS Why?s</a> - A site that aims to objectively and concisely explore all the points surrounding the creation, maintenance, and history of RSS.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.webreference.com/authoring/languages/xml/rss/">WebReference RSS Articles</a> - A collection of RSS articles and resources from Webreference.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ Discussion Lists
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/reallySimpleSyndication">ReallySimpleSyndication</a> - RSS 0.93.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/rss-dev">RSS-DEV</a> - RSS 1.0.</td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://groups.yahoo.com/group/syndication">Syndication</a> - XML syndication, mainly RSS 0.91.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentbold" colspan="2"><br />
+ More RSS FAQs
+ </td>
+ </tr>
+ <tr>
+ <td width="15" class="content" align="center" valign="top">&bull;</td>
+ <td width="605" class="content" colspan="2"><a href="http://www.voidstar.com/rssfaq">RSS FAQ</a> - A detailed RSS FAQ from Julian Bond, readers can also contribute.</td>
+ </tr>
+ <tr>
+ <td width="620" class="content" colspan="2"><br />
+ <a href="#top">top</a>
+ </td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2"><br /><br /><a href="http://www.rssfaq.com/">RSSFAQ</a> Copyright &copy; 2001 Members of the Syndication, RSS-DEV and ReallySimpleSyndication Groups.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2">You may freely copy and distribute this document. Please give acknowledgements if you do.</td>
+ </tr>
+ <tr>
+ <td width="620" class="contentsmall" colspan="2">Last Updated: 24-August-2001 <a href="mailto:alis@purplepages.ie">Alis Marsden</a>.</td>
+ </tr>
+</table>
+</body>
+</html>
diff --git a/librss/testlibrss.cpp b/librss/testlibrss.cpp
new file mode 100644
index 00000000..6b6a83d3
--- /dev/null
+++ b/librss/testlibrss.cpp
@@ -0,0 +1,41 @@
+#include "testlibrss.h"
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+using namespace RSS;
+
+void Tester::test()
+{
+ Loader *loader = Loader::create();
+ connect( loader, SIGNAL( loadingComplete( Loader *, Document, Status ) ),
+ this, SLOT( slotLoadingComplete( Loader *, Document, Status ) ) );
+ loader->loadFrom( "http://sourceforge.net/export/rss2_projnews.php?group_id=29057&rss_fulltext=1", new FileRetriever );
+}
+
+void Tester::slotLoadingComplete( Loader *loader, Document doc, Status status )
+{
+ if ( status == Success )
+ kdDebug() << "Successfully retrieverd '" << doc.title() << "'" << endl;
+
+ if ( status != Success )
+ kdDebug() << "ERROR " << loader->errorCode() << endl;
+
+ kapp->quit();
+}
+
+int main( int argc, char **argv )
+{
+ KAboutData aboutData( "testlibrss", "testlibrss", "0.1" );
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KApplication app;
+
+ Tester tester;
+ tester.test();
+
+ return app.exec();
+}
+
+#include "testlibrss.moc"
diff --git a/librss/testlibrss.h b/librss/testlibrss.h
new file mode 100644
index 00000000..2db10178
--- /dev/null
+++ b/librss/testlibrss.h
@@ -0,0 +1,24 @@
+#ifndef TESTLIBRSS_H
+#define TESTLIBRSS_H
+
+#include <qobject.h>
+
+#include "loader.h"
+#include "document.h"
+#include "global.h"
+
+using RSS::Loader;
+using RSS::Document;
+using RSS::Status;
+
+class Tester : public QObject
+{
+ Q_OBJECT
+ public:
+ void test();
+
+ private slots:
+ void slotLoadingComplete( Loader *loader, Document doc, Status status );
+};
+
+#endif
diff --git a/librss/textinput.cpp b/librss/textinput.cpp
new file mode 100644
index 00000000..71d5e01c
--- /dev/null
+++ b/librss/textinput.cpp
@@ -0,0 +1,96 @@
+/*
+ * textinput.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "textinput.h"
+#include "tools_p.h"
+
+#include <kurl.h>
+
+#include <qdom.h>
+
+using namespace RSS;
+
+struct TextInput::Private : public Shared
+{
+ QString title;
+ QString description;
+ QString name;
+ KURL link;
+};
+
+TextInput::TextInput() : d(new Private)
+{
+}
+
+TextInput::TextInput(const TextInput &other) : d(0)
+{
+ *this = other;
+}
+
+TextInput::TextInput(const QDomNode &node) : d(new Private)
+{
+ QString elemText;
+
+ if (!(elemText = extractNode(node, QString::fromLatin1("title"))).isNull())
+ d->title = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("description"))).isNull())
+ d->description = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("name"))))
+ d->name = elemText;
+ if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
+ d->link = elemText;
+}
+
+TextInput::~TextInput()
+{
+ if (d->deref())
+ delete d;
+}
+
+QString TextInput::title() const
+{
+ return d->title;
+}
+
+QString TextInput::description() const
+{
+ return d->description;
+}
+
+QString TextInput::name() const
+{
+ return d->name;
+}
+
+const KURL &TextInput::link() const
+{
+ return d->link;
+}
+
+TextInput &TextInput::operator=(const TextInput &other)
+{
+ if (this != &other) {
+ other.d->ref();
+ if (d && d->deref())
+ delete d;
+ d = other.d;
+ }
+ return *this;
+}
+
+bool TextInput::operator==(const TextInput &other) const
+{
+ return d->title == other.title() &&
+ d->description == other.description() &&
+ d->name == other.name() &&
+ d->link == other.link();
+}
+
+// vim:noet:ts=4
diff --git a/librss/textinput.h b/librss/textinput.h
new file mode 100644
index 00000000..444e7280
--- /dev/null
+++ b/librss/textinput.h
@@ -0,0 +1,121 @@
+/*
+ * textinput.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_TEXTINPUT_H
+#define LIBRSS_TEXTINPUT_H
+
+#include "global.h"
+
+class KURL;
+
+class QDomNode;
+class QString;
+
+namespace RSS
+{
+ /**
+ * Represents a text input facility as stored in a RSS file for the purpose
+ * of allowing users to submit queries back to the publisher's site. You
+ * don't have to instantiate one of these yourself, the common way to access
+ * instances is via Document::textInput().
+ * @see Document::textInput()
+ */
+ class TextInput
+ {
+ public:
+ /**
+ * Default constructor.
+ */
+ TextInput();
+
+ /**
+ * Copy constructor.
+ * @param other The TextInput object to copy.
+ */
+ TextInput(const TextInput &other);
+
+ /**
+ * Constructs a TextInput from a piece of RSS markup.
+ * @param node A QDomNode which references the DOM leaf to be used
+ * for constructing the TextInput.
+ */
+ TextInput(const QDomNode &node);
+
+ /**
+ * Assignment operator.
+ * @param other The TextInput object to clone.
+ * @return A reference to the cloned TextInput object.
+ */
+ TextInput &operator=(const TextInput &other);
+
+ /**
+ * Compares two text inputs. Two text inputs are considered
+ * identical if their properties (title, description, link etc.)
+ * are identical.
+ * @param other The text input to compare with.
+ * @return Whether the two text inputs are equal.
+ */
+ bool operator==(const TextInput &other) const;
+
+ /**
+ * Convenience method. Simply calls !operator==().
+ * @param other The text input to compared with.
+ * @return Whether the two text inputs are unequal.
+ */
+ bool operator!=(const TextInput &other) const { return !operator==(other); }
+
+ /**
+ * Destructor.
+ */
+ virtual ~TextInput();
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The title (often a label to be used for the input field)
+ * of the text input, or QString::null if no title is available.
+ */
+ QString title() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The description (usually used as a tooltip which appears
+ * if the mouse hovers above the input field for a short time) of
+ * the text input, or QString::null if no description is
+ * available.
+ */
+ QString description() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return The name of the text input (what's this for?) of the
+ * text input, or QString::null, if no name is available.
+ */
+ QString name() const;
+
+ /**
+ * RSS 0.90 and upwards
+ * @return A link to which the contents of the input field should
+ * be sent after the user specified them. This is often a CGI
+ * program on a remote server which evaluates the entered
+ * information. An empty KURL is returned in case no link is
+ * available.
+ * Note that the RSS 0.91 Specification dictates that URLs not
+ * starting with "http://" or "ftp://" are considered invalid.
+ */
+ const KURL &link() const;
+
+ private:
+ struct Private;
+ Private *d;
+ };
+}
+
+#endif // LIBRSS_TEXTINPUT_H
+// vim: noet:ts=4
diff --git a/librss/tools_p.cpp b/librss/tools_p.cpp
new file mode 100644
index 00000000..c88eecac
--- /dev/null
+++ b/librss/tools_p.cpp
@@ -0,0 +1,28 @@
+/*
+ * tools_p.cpp
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#include "tools_p.h"
+
+#include <qdom.h>
+
+QString RSS::extractNode(const QDomNode &parent, const QString &elemName)
+{
+ QDomNode node = parent.namedItem(elemName);
+ if (node.isNull())
+ return QString::null;
+
+ QString result = node.toElement().text().simplifyWhiteSpace();
+ if (result.isEmpty())
+ return QString::null;
+
+ return result;
+}
+
+// vim:noet:ts=4
diff --git a/librss/tools_p.h b/librss/tools_p.h
new file mode 100644
index 00000000..adc508b8
--- /dev/null
+++ b/librss/tools_p.h
@@ -0,0 +1,31 @@
+/*
+ * tools_p.h
+ *
+ * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
+ *
+ * This program 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. For licensing and distribution details, check the
+ * accompanying file 'COPYING'.
+ */
+#ifndef LIBRSS_TOOLS_P_H
+#define LIBRSS_TOOLS_P_H
+
+class QDomNode;
+class QString;
+
+namespace RSS
+{
+ struct Shared
+ {
+ Shared() : count(1) { }
+ void ref() { count++; }
+ bool deref() { return !--count; }
+ unsigned int count;
+ };
+
+ QString extractNode(const QDomNode &parent, const QString &elemName);
+}
+
+#endif // LIBRSS_TOOLS_P_H
+// vim:noet:ts=4
diff --git a/wifi/Makefile.am b/wifi/Makefile.am
new file mode 100644
index 00000000..22c1a4c2
--- /dev/null
+++ b/wifi/Makefile.am
@@ -0,0 +1,56 @@
+# Yow, Emacs, this is a -*- makefile -*-
+
+xdg_apps_DATA = kwifimanager.desktop
+
+EXTRA_DIST = $(xdg_apps_DATA)
+INCLUDES = $(all_includes)
+
+SUBDIRS = kcmwifi kwireless pixmaps locations
+
+bin_PROGRAMS = kwifimanager
+
+kwifimanager_SOURCES = interface_wireless.cpp \
+ interface_wireless_wirelessextensions.cpp \
+ interface_dcop.skel \
+ networkscanning.cpp \
+ kwifimanager.cpp \
+ locator.cpp \
+ main.cpp \
+ picture.cpp \
+ speed.cpp \
+ statistics.cpp \
+ status.cpp \
+ strength.cpp \
+ asusled.cpp
+
+kwifimanager_LDFLAGS = $(all_libraries)
+
+if include_ARTS
+ARTS_LIBS = -lartsflow -lmcop
+endif
+
+kwifimanager_LDADD = $(ARTS_LIBS) -liw $(LIB_KDEUI)
+
+noinst_HEADERS = interface_wireless.h \
+ interface_wireless_wirelessextensions.h \
+ interface_dcop.h \
+ networkscanning.h \
+ kwifimanager.h \
+ locator.h \
+ picture.h \
+ speed.h \
+ statistics.h \
+ status.h \
+ strength.h \
+ asusled.h
+
+METASOURCES = AUTO
+
+KDE_ICON = AUTO
+
+appdir=$(kde_datadir)/kwifimanager
+app_DATA = kwifimanagerui.rc
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kwifimanager.pot
+
diff --git a/wifi/asusled.cpp b/wifi/asusled.cpp
new file mode 100644
index 00000000..19784c16
--- /dev/null
+++ b/wifi/asusled.cpp
@@ -0,0 +1,78 @@
+
+/***************************************************************************
+ led.cpp - description
+ -------------------
+ begin : wo okt 8 2003
+ copyright : (C) 2003 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <fstream>
+#include "asusled.h"
+
+#include <kdebug.h>
+
+Led::Led ()
+{
+ led = new std::ofstream ("/proc/acpi/asus/wled");
+
+ // If there is no LED, just give a one time warning on cerr
+ if (!(*led))
+ {
+ static bool error_given = false;
+ if (!error_given)
+ {
+ kdDebug () <<
+ "No Asus WLAN LED found, disabling LED updates" << endl;
+ error_given = true;
+ }
+ }
+ Off ();
+ // if Asusled not present, state never gets initialised, so we do it once here
+ // so that valgrind doesn't complain.
+ state = false;
+}
+
+Led::~Led ()
+{
+
+ Off ();
+ delete led;
+
+}
+
+
+void
+Led::On (void)
+{
+
+ if (*led)
+ {
+ *led << 1;
+ led->flush ();
+ state = true;
+ }
+
+}
+
+void
+Led::Off (void)
+{
+
+ if (*led)
+ {
+ *led << 0;
+ led->flush ();
+ state = false;
+ }
+
+}
diff --git a/wifi/asusled.h b/wifi/asusled.h
new file mode 100644
index 00000000..40ca079b
--- /dev/null
+++ b/wifi/asusled.h
@@ -0,0 +1,40 @@
+/***************************************************************************
+ led.h - description
+ -------------------
+ begin : wo okt 8 2003
+ copyright : (C) 2003 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 ASUSLED_H
+#define ASUSLED_H
+
+#include <fstream>
+
+/**
+ *@author Roeland Merks
+ */
+
+class Led
+{
+public:
+ Led ();
+ ~Led ();
+ void On (void);
+ void Off (void);
+ bool state;
+
+private:
+ std::ofstream * led;
+};
+
+#endif
diff --git a/wifi/configure.in.in b/wifi/configure.in.in
new file mode 100644
index 00000000..f796f816
--- /dev/null
+++ b/wifi/configure.in.in
@@ -0,0 +1,71 @@
+AC_ARG_WITH(wifi,
+ [AC_HELP_STRING(--with-wifi,
+ [enable support for wireless tools @<:@default=check@:>@])],
+ [], with_wifi=check)
+
+kde_libiw_installed=no
+if test "x$with_wifi" != xno; then
+ AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ AC_MSG_CHECKING([if wireless tools >= 25 are installed])
+ AC_TRY_COMPILE(
+ [
+ #include <iwlib.h>
+ ],
+ [
+ const char *s = PROC_NET_DEV;
+ iw_enum_devices(0, 0, 0, 0);
+ ],
+ kde_libiw_installed=yes
+ AC_MSG_RESULT(yes)
+ ,
+ kde_libiw_installed=no
+ AC_MSG_RESULT(no)
+ )
+
+ AC_MSG_CHECKING([if wireless tools >= 27 are installed])
+ AC_TRY_COMPILE(
+ [
+ #include <iwlib.h>
+ ],
+ [
+ // checking how many arguments does it iw_get_stats want
+ // the result is unimportant
+ struct wireless_info info;
+ char * stupid;
+ iw_get_stats(iw_sockets_open(), stupid, &(info.stats));
+
+ ], AC_MSG_RESULT(no)
+ ,
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_IW_27, 1, [Define if you have iwlib newer than 26])
+
+ AC_MSG_CHECKING([if wireless tools >= 27pre19 are installed])
+ AC_TRY_COMPILE(
+ [
+ #include <iwlib.h>
+ ],
+ [
+ // checking if wireless_info got a b.has_freq field
+ struct wireless_info info;
+ info.b.freq = 1;
+
+ ], [AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_IW_27pre19, 1, [Define if you have iwlib newer than 27pre18])
+
+ ],
+ AC_MSG_RESULT(no)
+ )
+
+ )
+
+ AC_LANG_RESTORE
+
+ if test "x$with_wifi" != xcheck && test "x$kde_libiw_installed" = xno; then
+ AC_MSG_ERROR([--with-wifi was given, but test for wireless-tools failed])
+ fi
+fi
+
+if test "$kde_libiw_installed" = "no"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE wifi"
+fi
diff --git a/wifi/hi128-app-kwifimanager.png b/wifi/hi128-app-kwifimanager.png
new file mode 100644
index 00000000..3403b7d3
--- /dev/null
+++ b/wifi/hi128-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hi16-app-kwifimanager.png b/wifi/hi16-app-kwifimanager.png
new file mode 100644
index 00000000..4578f397
--- /dev/null
+++ b/wifi/hi16-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hi22-app-kwifimanager.png b/wifi/hi22-app-kwifimanager.png
new file mode 100644
index 00000000..79e54eac
--- /dev/null
+++ b/wifi/hi22-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hi32-app-kwifimanager.png b/wifi/hi32-app-kwifimanager.png
new file mode 100644
index 00000000..497a6038
--- /dev/null
+++ b/wifi/hi32-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hi48-app-kwifimanager.png b/wifi/hi48-app-kwifimanager.png
new file mode 100644
index 00000000..61fe614e
--- /dev/null
+++ b/wifi/hi48-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hi64-app-kwifimanager.png b/wifi/hi64-app-kwifimanager.png
new file mode 100644
index 00000000..72bf83c9
--- /dev/null
+++ b/wifi/hi64-app-kwifimanager.png
Binary files differ
diff --git a/wifi/hisc-app-kwifimanager.svgz b/wifi/hisc-app-kwifimanager.svgz
new file mode 100644
index 00000000..867562ee
--- /dev/null
+++ b/wifi/hisc-app-kwifimanager.svgz
Binary files differ
diff --git a/wifi/interface_dcop.h b/wifi/interface_dcop.h
new file mode 100644
index 00000000..9a4149c0
--- /dev/null
+++ b/wifi/interface_dcop.h
@@ -0,0 +1,15 @@
+#ifndef INTERFACE_DCOP_H
+#define INTERFACE_DCOP_H
+
+#include <dcopobject.h>
+
+class dcop_interface : virtual public DCOPObject
+ {
+ K_DCOP
+ k_dcop:
+
+ virtual QString interface() const = 0;
+ };
+
+#endif
+
diff --git a/wifi/interface_wireless.cpp b/wifi/interface_wireless.cpp
new file mode 100644
index 00000000..5ece6301
--- /dev/null
+++ b/wifi/interface_wireless.cpp
@@ -0,0 +1,210 @@
+/***************************************************************************
+ stuff.cpp - description
+ -------------------
+ begin : Sun May 6 2001
+ copyright : (C) 2001 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "interface_wireless.h"
+#include <qdir.h>
+#include <qfile.h>
+#include <qstringlist.h>
+
+#ifndef WITHOUT_ARTS
+#include <arts/artsflow.h>
+#include <arts/connect.h>
+#include <arts/iomanager.h>
+#include <arts/referenceclean.h>
+#endif // WITHOUT_ARTS
+
+#include <iostream>
+#include <string>
+#include <klocale.h>
+#include <kprocio.h>
+#include <kdebug.h>
+#include <qstring.h>
+#include <arpa/inet.h>
+
+Interface_wireless::Interface_wireless (QStringList * ignoreInterfaces)
+{
+ this->ignoreInterfaces = ignoreInterfaces;
+ has_frequency = false;
+ frequency = 0.;
+ has_mode = false;
+ mode = 0;
+ has_key = 0;
+ key = "";
+ key_size = 0;
+ key_flags = 0;
+ essid = "";
+ access_point_address = "";
+ ip_address = "";
+ bitrate = 0.;
+ socket = -1;
+ has_range = false;
+ for (int i = 0; i < MAX_HISTORY; i++)
+ {
+ sigLevel[i] = -255;
+ noiseLevel[i] = -255;
+ qual[i] = -255;
+ valid[i] = false;
+ }
+ current = 0;
+ sigLevelMin = 32000;
+ noiseLevelMin = 32000;
+ sigLevelMax = -32000;
+ noiseLevelMax = -32000;
+
+ already_warned = false;
+}
+
+bool Interface_wireless::get_device_freq (double &freq)
+{
+ if (has_frequency)
+ {
+ freq = frequency;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool Interface_wireless::get_mode (int &ext_mode)
+{
+ if (has_mode)
+ {
+ ext_mode = mode;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool Interface_wireless::get_key (QString & ext_key,
+ int &ext_size,
+ int &ext_flags)
+{
+ if (has_key)
+ {
+ ext_key = key;
+ ext_size = key_size;
+ ext_flags = key_flags;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+QString Interface_wireless::get_essid ()
+{
+ return essid;
+}
+
+bool Interface_wireless::get_has_txpower()
+{
+ return has_txpower;
+}
+
+int Interface_wireless::get_txpower_disabled()
+{
+ return txpower_disabled;
+}
+
+bool Interface_wireless::get_AP_info (QString & mac, QString &/*ip*/)
+{
+ mac = access_point_address;
+ return false;
+}
+
+QString Interface_wireless::get_IP_info ()
+{
+ return ip_address;
+}
+
+double Interface_wireless::get_bitrate ()
+{
+ return bitrate;
+}
+
+QString Interface_wireless::get_interface_name ()
+{
+ return interface_name;
+}
+
+bool Interface_wireless::get_current_quality (int &sig,
+ int &noi,
+ int &qua)
+{
+ if (valid[current])
+ {
+ sig = sigLevel[current];
+ noi = noiseLevel[current];
+ qua = qual[current];
+ return true;
+ }
+ else
+ return false;
+}
+
+#ifndef WITHOUT_ARTS
+
+void
+sinus_wave (double frequency)
+{
+ using namespace Arts;
+ StdIOManager *limiter = new StdIOManager;
+ Dispatcher dispatcher (limiter);
+ MyTimeNotify *zeit = new MyTimeNotify (&dispatcher);
+ Synth_FREQUENCY freq;
+ Synth_WAVE_SIN sin;
+ Synth_PLAY play;
+ setValue (freq, frequency);
+ connect (freq, sin);
+ connect (sin, play, "invalue_left");
+ freq.start ();
+ sin.start ();
+ play.start ();
+ limiter->addTimer (240, zeit);
+ dispatcher.run ();
+ play.stop ();
+ sin.stop ();
+ freq.stop ();
+}
+
+void
+MyTimeNotify::notifyTime ()
+{
+ test->terminate ();
+}
+
+#endif
+
+QString
+whois (const char *MAC_ADR, QStringList APList)
+{
+ for (QStringList::Iterator it = APList.begin (); it != APList.end ();
+ (it++)++)
+ {
+ if ((*it) == (QString) MAC_ADR)
+ return *(++it);
+ }
+ return i18n ("UNKNOWN");
+}
+
+#include "interface_wireless.moc"
diff --git a/wifi/interface_wireless.h b/wifi/interface_wireless.h
new file mode 100644
index 00000000..029a8156
--- /dev/null
+++ b/wifi/interface_wireless.h
@@ -0,0 +1,125 @@
+/***************************************************************************
+ stuff.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 INTERFACE_WIRELESS_H
+#define INTERFACE_WIRELESS_H
+
+#include "config.h"
+
+#include <qobject.h>
+#include <qstring.h>
+#include <qtable.h>
+
+#ifndef WITHOUT_ARTS
+#include <arts/iomanager.h>
+#include <arts/dispatcher.h>
+#endif
+
+const int POLL_DELAY_MS = 250; // how much time between device polls
+const int MAX_HISTORY = 240; // number of device states to be saved
+
+class Interface_wireless:public QObject
+{
+Q_OBJECT public:
+ Interface_wireless (QStringList * ignoreInterfaces);
+ virtual ~ Interface_wireless() { };
+
+ virtual bool get_device_freq (double &freq);
+ // true: device returned valid frequency
+ virtual bool get_mode (int &mode);
+ // true: device returned valid mode info
+ virtual bool get_key (QString & key, int &size, int &flags);
+ // true: device has set up a valid crypto key
+ virtual QString get_essid ();
+ virtual bool get_AP_info (QString & mac, QString & ip);
+ // true: could retrieve IP, false: returned IP info invalid
+ virtual QString get_IP_info ();
+ virtual double get_bitrate ();
+ virtual QString get_interface_name ();
+ // if no device is attached, return empty QString
+ virtual bool get_current_quality (int &sig, int &noi, int &qua);
+ // quality info is only valid when true
+ virtual QTable* get_available_networks () = 0;
+ virtual bool get_has_txpower();
+ virtual int get_txpower_disabled();
+ virtual void setActiveDevice( QString device ) = 0;
+ // stats
+ int sigLevel[MAX_HISTORY];
+ int sigLevelMin, sigLevelMax;
+ int noiseLevel[MAX_HISTORY];
+ int noiseLevelMin, noiseLevelMax;
+ int qual[MAX_HISTORY];
+ bool valid[MAX_HISTORY];
+ int current;
+
+public slots:
+ virtual bool poll_device_info () = 0;
+
+signals:
+ void interfaceChanged ();
+ void strengthChanged ();
+ void statusChanged ();
+ void modeChanged ();
+ void speedChanged ();
+ void essidChanged (QString essid);
+ void txPowerChanged ();
+ void statsUpdated ();
+protected:
+ bool already_warned;
+ // device info
+ bool has_frequency;
+ float frequency;
+ bool has_mode;
+ int mode;
+ bool has_key;
+ QString key;
+ int key_size;
+ int key_flags;
+ QString essid;
+ QString access_point_address;
+ QString ip_address;
+ double bitrate;
+ QString interface_name;
+ int socket;
+ bool has_range;
+ int range;
+ QStringList * ignoreInterfaces;
+ bool has_txpower;
+ int txpower_disabled;
+};
+
+#ifndef WITHOUT_ARTS
+
+void sinus_wave (double frequency);
+
+class MyTimeNotify:public
+ Arts::TimeNotify
+{
+public:
+ Arts::Dispatcher * test;
+ MyTimeNotify (Arts::Dispatcher * siff)
+ {
+ test = siff;
+ };
+ void notifyTime ();
+};
+
+#endif
+
+QString whois (const char *MAC_ADR, QStringList APList);
+
+#endif /* INTERFACE_WIRELESS_H */
diff --git a/wifi/interface_wireless_wirelessextensions.cpp b/wifi/interface_wireless_wirelessextensions.cpp
new file mode 100644
index 00000000..58abe9c9
--- /dev/null
+++ b/wifi/interface_wireless_wirelessextensions.cpp
@@ -0,0 +1,636 @@
+/***************************************************************************
+ interface_wireless_wirelessextensions.cpp - description
+ -------------------
+ begin : Sun May 6 2001
+ copyright : (C) 2001 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 "interface_wireless_wirelessextensions.h"
+#include <iwlib.h>
+#include <qdir.h>
+#include <qfile.h>
+#include <qtable.h>
+
+#include <kmessagebox.h>
+
+#ifndef WITHOUT_ARTS
+#include <arts/artsflow.h>
+#include <arts/connect.h>
+#include <arts/iomanager.h>
+#include <arts/referenceclean.h>
+#endif
+
+#include <iostream>
+#include <string>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kprocio.h>
+#include <kdebug.h>
+#include <qstring.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+
+
+// domi:the wireless-tools people managed to change their interfaces
+// around. Someone with more knowledge of this code should look into
+// moving to use the new iw_range interfaces.
+#ifdef HAVE_IW_27
+
+#ifndef HAVE_IW_27pre19
+#define WIFI_CONFIG(config,field) config.field
+#define WIFI_EXTRACT_EVENT_STREAM(stream, event) \
+ iw_extract_event_stream(stream, event)
+#else
+#define WIFI_CONFIG(config,field) config.b.field
+#define WIFI_EXTRACT_EVENT_STREAM(stream, event) \
+ iw_extract_event_stream(stream, event, WIRELESS_EXT)
+#endif
+
+#define WIFI_GET_STATS(skfd, ifname, stats) \
+ iw_get_stats (skfd, ifname, stats, 0, 0)
+#define WIFI_PRINT_STATS(buff, event) \
+ iw_print_stats(buffer, sizeof(buffer), &event->u.qual, 0, 0);
+
+#else // HAVE_IW_25
+#define WIFI_CONFIG(config,field) config.field
+#define WIFI_EXTRACT_EVENT_STREAM(stream, event) \
+ iw_extract_event_stream(stream, event)
+#define WIFI_GET_STATS(skfd, ifname, stats) \
+ iw_get_stats (skfd, ifname, stats)
+#define WIFI_PRINT_STATS(buff, event) \
+ iw_print_stats(buffer, &event->u.qual, 0, 0);
+#endif
+
+/* ================================== FROM IWCONFIG.C ================================== */
+int
+Interface_wireless_wirelessextensions::get_info (int skfd, const QString& interface,
+ struct wireless_info& info)
+{
+ struct iwreq wrq;
+ char ifname[20];
+ snprintf(ifname, sizeof(ifname), "%s", interface.latin1() );
+ memset (&info, 0, sizeof(info));
+
+ /* Get wireless name */
+ if (iw_get_ext (skfd, ifname, SIOCGIWNAME, &wrq) < 0)
+ {
+ /* If no wireless name : no wireless extensions */
+ /* But let's check if the interface exists at all */
+ struct ifreq ifr;
+
+ strcpy (ifr.ifr_name, ifname);
+ if (ioctl (skfd, SIOCGIFFLAGS, &ifr) < 0)
+ return (-ENODEV);
+ else
+ return (-ENOTSUP);
+ }
+ else
+ {
+ strncpy (WIFI_CONFIG(info,name), wrq.u.name, IFNAMSIZ);
+ WIFI_CONFIG(info,name)[IFNAMSIZ] = '\0';
+ }
+
+ /* Get ranges */
+ if (iw_get_range_info (skfd, ifname, &(info.range)) >= 0)
+ info.has_range = 1;
+
+ /* Get network ID */
+ if (iw_get_ext (skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
+ {
+ WIFI_CONFIG(info,has_nwid) = 1;
+ memcpy (&(WIFI_CONFIG(info,nwid)), &(wrq.u.nwid), sizeof (iwparam));
+ }
+
+ /* Get frequency / channel */
+ if (iw_get_ext (skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
+ {
+ WIFI_CONFIG(info,has_freq) = 1;
+ WIFI_CONFIG(info,freq) = iw_freq2float (&(wrq.u.freq));
+ }
+
+ /* Get sensitivity */
+ if (iw_get_ext (skfd, ifname, SIOCGIWSENS, &wrq) >= 0)
+ {
+ info.has_sens = 1;
+ memcpy (&(info.sens), &(wrq.u.sens), sizeof (iwparam));
+ }
+
+ /* Get encryption information */
+ wrq.u.data.pointer = (caddr_t) WIFI_CONFIG(info,key);
+ wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
+ wrq.u.data.flags = 0;
+ if (iw_get_ext (skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
+ {
+ WIFI_CONFIG(info,has_key) = 1;
+ WIFI_CONFIG(info,key_size) = wrq.u.data.length;
+ WIFI_CONFIG(info,key_flags) = wrq.u.data.flags;
+ }
+
+ /* Get ESSID */
+ /* prepare NULL-terminated buffer in case the string returned by SIOCGIWESSID is NOT NULL-terminated */
+ memset(wrq.u.essid.pointer, '\0', IW_ESSID_MAX_SIZE + 1);
+ wrq.u.essid.pointer = (caddr_t) WIFI_CONFIG(info,essid);
+ wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+ wrq.u.essid.flags = 0;
+ if (iw_get_ext (skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
+ {
+ WIFI_CONFIG(info,has_essid) = 1;
+ WIFI_CONFIG(info,essid_on) = wrq.u.data.flags;
+ }
+
+ /* Get AP address */
+ if (iw_get_ext (skfd, ifname, SIOCGIWAP, &wrq) >= 0)
+ {
+ info.has_ap_addr = 1;
+ memcpy (&(info.ap_addr), &(wrq.u.ap_addr), sizeof (sockaddr));
+ }
+
+ /* Get NickName */
+ wrq.u.essid.pointer = (caddr_t) info.nickname;
+ wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
+ wrq.u.essid.flags = 0;
+ if (iw_get_ext (skfd, ifname, SIOCGIWNICKN, &wrq) >= 0)
+ if (wrq.u.data.length > 1)
+ info.has_nickname = 1;
+
+ /* Get bit rate */
+ if (iw_get_ext (skfd, ifname, SIOCGIWRATE, &wrq) >= 0)
+ {
+ info.has_bitrate = 1;
+ memcpy (&(info.bitrate), &(wrq.u.bitrate), sizeof (iwparam));
+ }
+
+ /* Get RTS threshold */
+ if (iw_get_ext (skfd, ifname, SIOCGIWRTS, &wrq) >= 0)
+ {
+ info.has_rts = 1;
+ memcpy (&(info.rts), &(wrq.u.rts), sizeof (iwparam));
+ }
+
+ /* Get fragmentation threshold */
+ if (iw_get_ext (skfd, ifname, SIOCGIWFRAG, &wrq) >= 0)
+ {
+ info.has_frag = 1;
+ memcpy (&(info.frag), &(wrq.u.frag), sizeof (iwparam));
+ }
+
+ /* Get operation mode */
+ if (iw_get_ext (skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
+ {
+ WIFI_CONFIG(info,mode) = wrq.u.mode;
+ if ((WIFI_CONFIG(info,mode) < IW_NUM_OPER_MODE) && (WIFI_CONFIG(info,mode) >= 0))
+ WIFI_CONFIG(info,has_mode) = 1;
+ }
+
+ /* Get Power Management settings */
+ wrq.u.power.flags = 0;
+ if (iw_get_ext (skfd, ifname, SIOCGIWPOWER, &wrq) >= 0)
+ {
+ info.has_power = 1;
+ memcpy (&(info.power), &(wrq.u.power), sizeof (iwparam));
+ }
+
+#if WIRELESS_EXT > 9
+ /* Get Transmit Power and check if it is disabled */
+ bool emitTXPowerChanged = false;
+ if (iw_get_ext (skfd, ifname, SIOCGIWTXPOW, &wrq) >= 0)
+ {
+ if (txpower_disabled != info.txpower.disabled)
+ {
+ emitTXPowerChanged = true;
+ }
+
+ info.has_txpower = 1;
+ memcpy (&(info.txpower), &(wrq.u.txpower), sizeof (iwparam));
+ }
+ has_txpower = info.has_txpower;
+ txpower_disabled = ( int )info.txpower.disabled;
+ if (emitTXPowerChanged)
+ {
+ emit txPowerChanged();
+ }
+#endif
+
+#if WIRELESS_EXT > 10
+ /* Get retry limit/lifetime */
+ if (iw_get_ext (skfd, ifname, SIOCGIWRETRY, &wrq) >= 0)
+ {
+ info.has_retry = 1;
+ memcpy (&(info.retry), &(wrq.u.retry), sizeof (iwparam));
+ }
+#endif /* WIRELESS_EXT > 10 */
+
+ /* Get stats */
+ if (WIFI_GET_STATS (skfd, (char *) ifname, &(info.stats)) >= 0)
+ {
+ info.has_stats = 1;
+ }
+
+ return (0);
+}
+
+/* ================================ END IWCONFIG.C ================================ */
+
+
+Interface_wireless_wirelessextensions::Interface_wireless_wirelessextensions (QStringList * ignoreInterfaces)
+ : Interface_wireless(ignoreInterfaces)
+{
+}
+
+bool Interface_wireless_wirelessextensions::isSocketOpen()
+{
+ if (socket <= 0)
+ socket = iw_sockets_open ();
+
+ return (socket > 0);
+}
+
+void Interface_wireless_wirelessextensions::setActiveDevice( QString device )
+{
+ kdDebug () << "activating wireless device " << device << endl;
+ interface_name = device;
+
+ if (!isSocketOpen())
+ return;
+
+ emit interfaceChanged ();
+ emit strengthChanged ();
+ emit statusChanged ();
+ emit modeChanged ();
+ emit speedChanged ();
+ valid[current] = false;
+ emit statsUpdated ();
+}
+
+void Interface_wireless_wirelessextensions::setNoActiveDevice( )
+{
+ // interface has disappeared - unplugged?
+ // reset all info except stats
+ has_frequency = false;
+ frequency = 0.0;
+ has_mode = false;
+ mode = 0;
+ has_key = 0;
+ key = QString::null;
+ key_size = 0;
+ key_flags = 0;
+ essid = QString::null;
+ access_point_address = QString::null;
+ ip_address = QString::null;
+ bitrate = 0;
+ has_range = false;
+ has_txpower = false;
+ txpower_disabled = 0;
+
+ // propagate the changes
+ setActiveDevice( QString::null );
+}
+
+QStringList Interface_wireless_wirelessextensions::available_wifi_devices()
+{
+ QFile procnetdev(PROC_NET_DEV);
+ procnetdev.open (IO_ReadOnly);
+
+ kdDebug () << "Autodetecting..." << endl;
+
+ QStringList liste;
+ QString device;
+ while (!procnetdev.atEnd()) {
+ procnetdev.readLine (device, 9999);
+ int pos = device.find (':');
+ if (pos == -1)
+ continue;
+ device = device.left(pos).stripWhiteSpace();
+ if (device.isEmpty())
+ continue;
+ // Some drivers create two interfaces, ethX and wifiX. The
+ // wifiX device are not useful to users because they only
+ // control the physical wlan settings, not the logical ones. If
+ // we find a wifiX device then we move it to the start of the
+ // list which means the first instance of KWifiManger will pick
+ // up the ethX and only the second instance will pick up the
+ // wifiX.
+ if (device.startsWith("wifi"))
+ liste.prepend(device);
+ else
+ liste.append(device);
+ }
+
+
+ if (liste.isEmpty())
+ kdDebug () << "No wireless interface found." << endl;
+
+ return liste;
+}
+
+bool Interface_wireless_wirelessextensions::autodetect_device()
+{
+ if (!isSocketOpen())
+ return false;
+
+ QStringList liste = available_wifi_devices();
+
+ for (QStringList::Iterator it = liste.begin (); it != liste.end (); ++it)
+ {
+ QString device = *it;
+ kdDebug () << "[ " << device << " ] " << endl;
+ wireless_info info;
+ int result = get_info (socket, device, info);
+ if ((result != -ENODEV) && (result != -ENOTSUP) &&
+ (!ignoreInterfaces || ignoreInterfaces->findIndex(device)==-1))
+ {
+ setActiveDevice(device);
+ }
+ }
+
+ if (interface_name.isEmpty())
+ {
+ close(socket);
+ socket = -1;
+ return false;
+ }
+
+ return true;
+}
+
+bool Interface_wireless_wirelessextensions::poll_device_info ()
+{
+ if (current < MAX_HISTORY-1)
+ ++current;
+ else
+ current = 0;
+
+ if (interface_name.isEmpty())
+ if (!autodetect_device())
+ return false;
+
+ wireless_info info;
+ int result = get_info (socket, interface_name, info);
+ if ((result == -ENODEV) || (result == -ENOTSUP))
+ {
+ // interface has disappeared - unplugged?
+ // reset all info except stats
+ setNoActiveDevice();
+
+ close(socket);
+ socket = -1;
+
+ return false;
+ }
+
+ bool
+ emitStatusChanged = false, emitModeChanged = false, emitEssidChanged =
+ false, emitSpeedChanged = false, emitStrengthChanged = false;
+
+ iwstats tempic2;
+ WIFI_GET_STATS (socket, (char *) interface_name.latin1(), &tempic2);
+ has_frequency = WIFI_CONFIG(info,has_freq);
+ if (has_frequency)
+ {
+ if (frequency != WIFI_CONFIG(info,freq))
+ emitStatusChanged = true;
+ frequency = WIFI_CONFIG(info,freq);
+ }
+ has_mode = WIFI_CONFIG(info,has_mode);
+ if (has_mode)
+ {
+ if (mode != WIFI_CONFIG(info,mode))
+ emitModeChanged = true;
+ mode = WIFI_CONFIG(info,mode);
+ }
+ has_key = WIFI_CONFIG(info,has_key);
+ if (has_key)
+ {
+ if ((key != (char *) WIFI_CONFIG(info,key)) || (key_size != WIFI_CONFIG(info,key_size))
+ || (key_flags != WIFI_CONFIG(info,key_flags)))
+ emitStatusChanged = true;
+ key = (char *) WIFI_CONFIG(info,key);
+ key_size = WIFI_CONFIG(info,key_size);
+ key_flags = WIFI_CONFIG(info,key_flags);
+ }
+ if (essid != WIFI_CONFIG(info,essid))
+ {
+ emitStatusChanged = true;
+ emitEssidChanged = true;
+ }
+ essid = (WIFI_CONFIG(info,essid_on) ? WIFI_CONFIG(info,essid) : "any");
+ char
+ ap_addr[256];
+ iw_ether_ntop ( (const ether_addr *) info.ap_addr.sa_data, ap_addr);
+ if (access_point_address != ap_addr)
+ emitStatusChanged = true;
+ access_point_address = ap_addr;
+ if (bitrate != info.bitrate.value)
+ emitSpeedChanged = true;
+ bitrate = info.bitrate.value;
+ has_range = info.has_range;
+
+ sigLevel[current] = tempic2.qual.level;
+ noiseLevel[current] = tempic2.qual.noise;
+
+ if (has_range) {
+
+ range = info.range.max_qual.qual;
+
+ // the following checks adjust values for sig levels that are in dBm
+ // according to iwlib.c. If offset is applied, the resulting
+ // value is negative and the GUI knows it can add the scale
+ // unit dBm.
+
+ if (tempic2.qual.level > info.range.max_qual.level) {
+ sigLevel[current] = tempic2.qual.level - 0x100;
+ noiseLevel[current] = tempic2.qual.noise - 0x100;
+ }
+ }
+
+ if (sigLevel[current] < sigLevelMin)
+ sigLevelMin = sigLevel[current];
+ if (sigLevel[current] > sigLevelMax)
+ sigLevelMax = sigLevel[current];
+
+ if (noiseLevel[current] < noiseLevelMin)
+ noiseLevelMin = noiseLevel[current];
+ if (noiseLevel[current] > noiseLevelMax)
+ noiseLevelMax = noiseLevel[current];
+ int
+ tempqual = tempic2.qual.qual;
+ if (has_range && (range != 0))
+ tempqual = tempqual * 100 / range;
+ if ( qual[current != 0 ? current - 1 : MAX_HISTORY-1] != tempqual)
+ emitStrengthChanged = true;
+
+ qual[current] = ( tempqual <= 100 ? tempqual : 100 );
+ valid[current] = true;
+
+ // try to get our local IP address
+ struct sockaddr *
+ sa;
+ struct sockaddr_in *
+ sin;
+ struct ifreq
+ ifr;
+ /* Copy the interface name into the buffer */
+ strncpy (ifr.ifr_name, interface_name.latin1 (), IFNAMSIZ);
+
+ if (ioctl (socket, SIOCGIFADDR, &ifr) == -1)
+ {
+ if (ip_address != "unavailable")
+ emitStatusChanged = true;
+ ip_address = "unavailable";
+ }
+ /* Now the buffer will contain the information we requested */
+ sa = (struct sockaddr *) &(ifr.ifr_addr);
+ if (sa->sa_family == AF_INET)
+ {
+ sin = (struct sockaddr_in *) sa;
+ if (ip_address != (QString) inet_ntoa (sin->sin_addr))
+ emitStatusChanged = true;
+ ip_address = (QString) inet_ntoa (sin->sin_addr);
+ }
+ else
+ {
+ ip_address = "unavailable";
+ }
+ if (emitStatusChanged)
+ emit statusChanged ();
+ if (emitStrengthChanged)
+ emit strengthChanged ();
+ if (emitModeChanged)
+ emit modeChanged ();
+ if (emitSpeedChanged)
+ emit speedChanged ();
+ if (emitEssidChanged)
+ emit essidChanged (essid);
+ emit statsUpdated ();
+ return true;
+}
+
+QTable* Interface_wireless_wirelessextensions::get_available_networks ()
+{
+
+ networks = new QTable(0,4,0);
+ networks->horizontalHeader()->setLabel( 0, i18n("Network Name") );
+ networks->horizontalHeader()->setLabel( 1, i18n("Mode") );
+ networks->horizontalHeader()->setLabel( 2, i18n("Quality") );
+ networks->horizontalHeader()->setLabel( 3, i18n("WEP") );
+
+ KProcIO *iwlist = new KProcIO;
+ QString iwlist_bin = KStandardDirs::findExe("iwlist");
+ if(iwlist_bin.isEmpty())
+ iwlist_bin = KStandardDirs::findExe("iwlist", "/usr/local/sbin:/usr/sbin:/sbin");
+ if(iwlist_bin.isEmpty())
+ iwlist_bin = "iwlist"; // try our best ;/
+
+ *iwlist << iwlist_bin << interface_name << "scanning";
+
+// connect ( iwlist, SIGNAL ( readReady ( KProcIO * ) ), this, SLOT ( parseScanData ( KProcIO * ) ) );
+
+ if ( !iwlist->start ( KProcess::Block ) )
+ KMessageBox::sorry ( 0, i18n ( "Unable to perform the scan. Please make sure the executable \"iwlist\" is in your $PATH." ),
+ i18n ( "Scanning not possible" ) );
+
+ // this should never happen. But there was a report about Block not being as blocking as it should, so let's be safe about it
+ while (iwlist->isRunning()) sleep ( 1 );
+
+ parseScanData ( iwlist );
+
+ for (int i = 0; i<4; i++ ) networks->adjustColumn(i);
+
+ return networks;
+}
+
+void
+Interface_wireless_wirelessextensions::parseScanData ( KProcIO * iwlist )
+{
+ QString data;
+ int cellcount = 0, iteratecount = 0;
+ bool ignoreRemainingBits = false;
+ while ( iwlist->readln ( data, true ) >= 0 )
+ {
+ kdDebug ( ) << "iwlist: " << data << "\n";
+ if ( data.contains ( "does not support scanning" ) )
+ KMessageBox::sorry ( 0,
+ i18n
+ ( "Your card does not support scanning. The results window will not contain any results." ),
+ i18n ( "Scanning not possible" ) );
+ if ( data.contains ( "Scan completed" ) )
+ cellcount = 0; // at the very beginning of a scan
+ if ( data.contains ( "Cell" ) ) {
+ cellcount++; // new cell discovered
+ networks->setNumRows ( networks->numRows() +1 );
+ ignoreRemainingBits = false;
+ }
+ if ( data.contains ( "ESSID:" ) )
+ {
+ QString ssid = data.mid ( data.find ( "\"" ) + 1, data.length ( ) - data.find ( "\"" ) - 2 );
+ if ((ssid=="") || (ssid==" ")) ssid = "(hidden cell)";
+ networks->setText ( cellcount - 1, 0, ssid );
+ }
+ if ( data.contains ( "Mode:" ) )
+ {
+ if ( data.contains ( "Master" ) )
+ networks->setText ( cellcount - 1, 1, QString ( i18n ( "Managed" ) ) );
+ if ( data.contains ( "Ad-Hoc" ) )
+ networks->setText ( cellcount - 1, 1, QString ( i18n ( "Ad-Hoc" ) ) );
+ // if could be that this cell belongs to an SSID already discovered, or that there are more than one
+ // hidden cells, which doesn't give any new information. So, we first search for duplicates and delete
+ // this row if it's a duplicate. If the same SSID is there once as Managed and once as Ad-Hoc it is no
+ // duplicate of course, but "something completely different"
+ for (iteratecount = 0; iteratecount < cellcount - 1; iteratecount++)
+ {
+ kdDebug() << "Comparing <" << networks->text(cellcount - 1, 0) << "," << networks->text(iteratecount,0) << " and <"
+ << networks->text(cellcount - 1, 1) << "," << networks->text(iteratecount,1) << ">.\n";
+ if ((networks->text(cellcount - 1 , 0) == networks->text(iteratecount,0)) &&
+ (networks->text(cellcount - 1 , 1) == networks->text(iteratecount,1)) )
+ {
+ networks->setNumRows ( networks->numRows() - 1 );
+ cellcount--;
+ ignoreRemainingBits = true;
+ }
+ }
+ }
+ if ( !ignoreRemainingBits && data.contains ( "Encryption key:" ) )
+ {
+ if ( data.contains ( "off" ) )
+ networks->setText ( cellcount - 1, 3, QString ( "off" ) );
+ else
+ networks->setText ( cellcount - 1, 3, QString ( "on" ) );
+ }
+ if ( !ignoreRemainingBits && data.contains ( "Quality:" ) )
+ {
+ QString quality = data.mid ( data.find ( ":" ) + 1, data.find ( "/" ) - data.find ( ":" ) - 1 );
+ networks->setText ( cellcount - 1, 2, quality );
+ }
+
+ if ( !ignoreRemainingBits && data.contains ( "Quality=" ) )
+ {
+ QString quality = data.mid ( data.find ( "=" ) + 1, data.find ( "/" ) - data.find ( "=" ) - 1 );
+ networks->setText ( cellcount - 1, 2, quality );
+ }
+
+ if ( !ignoreRemainingBits && data.contains ( "wpa_ie" ) )
+ {
+ networks->setText ( cellcount - 1, 3, QString ( "WPA" ) );
+ }
+
+ if ( !ignoreRemainingBits && data.contains ( "rsn_ie" ) )
+ {
+ networks->setText ( cellcount - 1, 3, QString ( "WPA2" ) );
+ }
+
+ }
+}
+
+#include "interface_wireless_wirelessextensions.moc"
diff --git a/wifi/interface_wireless_wirelessextensions.h b/wifi/interface_wireless_wirelessextensions.h
new file mode 100644
index 00000000..96d91566
--- /dev/null
+++ b/wifi/interface_wireless_wirelessextensions.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ interface_wireless_wirelessextensions.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 INTERFACE_WIRELESS_WIRELESSEXTENSIONS_H
+#define INTERFACE_WIRELESS_WIRELESSEXTENSIONS_H
+
+#include "config.h"
+
+#include <qstring.h>
+#include <qtable.h>
+
+#ifndef WITHOUT_ARTS
+#include <arts/iomanager.h>
+#include <arts/dispatcher.h>
+#endif
+
+#include "interface_wireless.h"
+
+class QTable;
+class KProcIO;
+
+extern "C"
+{
+#include <iwlib.h>
+}
+
+class Interface_wireless_wirelessextensions:public Interface_wireless
+{
+Q_OBJECT public:
+ Interface_wireless_wirelessextensions (QStringList * ignoreInterfaces);
+ QTable* get_available_networks ();
+
+public:
+ void setActiveDevice( QString device );
+
+public slots:
+ bool poll_device_info ();
+
+private:
+ bool isSocketOpen();
+ void setNoActiveDevice( );
+ QStringList available_wifi_devices();
+ bool autodetect_device();
+
+ QString print_scanning_token (struct iw_event *event);
+ int get_info (int skfd, const QString& interface, struct wireless_info& info);
+ QTable* networks;
+private slots:
+ void parseScanData ( KProcIO * iwlist );
+};
+
+#endif /* INTERFACE_WIRELESS_WIRELESSEXTENSIONS_H */
diff --git a/wifi/kcmwifi/Makefile.am b/wifi/kcmwifi/Makefile.am
new file mode 100644
index 00000000..50a3bc62
--- /dev/null
+++ b/wifi/kcmwifi/Makefile.am
@@ -0,0 +1,19 @@
+## Makefile.am for kcmwifi
+
+INCLUDES= $(all_includes)
+
+kde_module_LTLIBRARIES = kcm_wifi.la
+kcm_wifi_la_SOURCES = wificonfig.cpp kcmwifi.cpp configcrypto.ui \
+ ifconfigpagebase.ui configpower.ui mainconfigbase.ui \
+ mainconfig.cpp ifconfigpage.cpp vendorconfig.cpp
+kcm_wifi_la_LDFLAGS = -module -avoid-version $(all_libraries) -no-undefined
+kcm_wifi_la_LIBADD = $(LIB_KIO)
+
+METASOURCES = AUTO
+
+noinst_HEADERS =
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kcmwifi.pot
+
+xdg_apps_DATA = kcmwifi.desktop
diff --git a/wifi/kcmwifi/configadvanced.ui b/wifi/kcmwifi/configadvanced.ui
new file mode 100644
index 00000000..beeda9fe
--- /dev/null
+++ b/wifi/kcmwifi/configadvanced.ui
@@ -0,0 +1,145 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>Form1</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>Form1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>cmb_channel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>120</x>
+ <y>23</y>
+ <width>99</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lbl_channel</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>9</x>
+ <y>33</y>
+ <width>70</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Channel:</string>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lbl_txPower</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>6</x>
+ <y>60</y>
+ <width>107</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Transmit power:</string>
+ </property>
+ </widget>
+ <widget class="KComboBox">
+ <property name="name">
+ <cstring>cmb_txPower</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>121</x>
+ <y>58</y>
+ <width>99</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_forceAP</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>9</x>
+ <y>91</y>
+ <width>268</width>
+ <height>22</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>Force registration to an access point</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>120</y>
+ <width>40</width>
+ <height>20</height>
+ </rect>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lbl_apMAC</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>64</x>
+ <y>124</y>
+ <width>89</width>
+ <height>17</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>MAC address:</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>kLineEdit1</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>193</x>
+ <y>128</y>
+ <width>140</width>
+ <height>24</height>
+ </rect>
+ </property>
+ </widget>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wifi/kcmwifi/configcrypto.ui b/wifi/kcmwifi/configcrypto.ui
new file mode 100644
index 00000000..7ef18da2
--- /dev/null
+++ b/wifi/kcmwifi/configcrypto.ui
@@ -0,0 +1,365 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ConfigCrypto</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigCrypto</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>360</width>
+ <height>236</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>gb_keys</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Crypto Keys</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_key1</cstring>
+ </property>
+ <property name="text">
+ <string>Key 1:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The first encryption key. 5 or 13 characters are ASCII keys, 10 or 26 characters are hexadecimal keys.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lb_key3</cstring>
+ </property>
+ <property name="text">
+ <string>Key 3:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The third encryption key. 5 or 13 characters are ASCII keys, 10 or 26 characters are hexadecimal keys.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lb_key2</cstring>
+ </property>
+ <property name="text">
+ <string>Key 2:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The second encryption key. 5 or 13 characters are ASCII keys, 10 or 26 characters are hexadecimal keys.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>lb_key4</cstring>
+ </property>
+ <property name="text">
+ <string>Key 4:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>The fourth encryption key. 5 or 13 characters are ASCII keys, 10 or 26 characters are hexadecimal keys.</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="2">
+ <property name="name">
+ <cstring>format_1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;font color="#ff0000"&gt;unrecognized&lt;/font&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>format_2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;font color="#ff0000"&gt;unrecognised&lt;/font&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="0" column="1">
+ <property name="name">
+ <cstring>le_key1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="1" column="1">
+ <property name="name">
+ <cstring>le_key2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="2">
+ <property name="name">
+ <cstring>format_3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;font color="#ff0000"&gt;unrecognised&lt;/font&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="2" column="1">
+ <property name="name">
+ <cstring>le_key3</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="2">
+ <property name="name">
+ <cstring>format_4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;font color="#ff0000"&gt;unrecognised&lt;/font&gt;</string>
+ </property>
+ <property name="textFormat">
+ <enum>RichText</enum>
+ </property>
+ </widget>
+ <widget class="KLineEdit" row="3" column="1">
+ <property name="name">
+ <cstring>le_key4</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>0</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>bg_cryptoMode</cstring>
+ </property>
+ <property name="title">
+ <string>Crypto Mode</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Sets which types of packets the card will accept.</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_openMode</cstring>
+ </property>
+ <property name="text">
+ <string>Ope&amp;n</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Makes the card accept encrypted and unencrypted packets.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_restrictMode</cstring>
+ </property>
+ <property name="text">
+ <string>Restricted</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Makes the card only accept encrypted packets.</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <spacer row="0" column="2">
+ <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>80</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_useKey</cstring>
+ </property>
+ <property name="text">
+ <string>Key to use:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Sets which of the four keys is to be used for transmitting packets.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <item>
+ <property name="text">
+ <string>Key 1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Key 2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Key 3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Key 4</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmb_activeKey</cstring>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>le_key1</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ConfigCrypto</receiver>
+ <slot>slotUpdateKey1Status(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>le_key2</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ConfigCrypto</receiver>
+ <slot>slotUpdateKey2Status(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>le_key3</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ConfigCrypto</receiver>
+ <slot>slotUpdateKey3Status(const QString&amp;)</slot>
+ </connection>
+ <connection>
+ <sender>le_key4</sender>
+ <signal>textChanged(const QString&amp;)</signal>
+ <receiver>ConfigCrypto</receiver>
+ <slot>slotUpdateKey4Status(const QString&amp;)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>cmb_activeKey</tabstop>
+ <tabstop>rb_openMode</tabstop>
+ <tabstop>rb_restrictMode</tabstop>
+ <tabstop>le_key1</tabstop>
+ <tabstop>le_key2</tabstop>
+ <tabstop>le_key3</tabstop>
+ <tabstop>le_key4</tabstop>
+</tabstops>
+<includes>
+ <include location="local" impldecl="in declaration">wificonfig.h</include>
+ <include location="local" impldecl="in implementation">configcrypto.ui.h</include>
+</includes>
+<slots>
+ <slot>slotUpdateKey1Status( const QString &amp; key )</slot>
+ <slot>slotUpdateKey2Status( const QString &amp; key )</slot>
+ <slot>slotUpdateKey3Status( const QString &amp; key )</slot>
+ <slot>slotUpdateKey4Status( const QString &amp; key )</slot>
+</slots>
+<functions>
+ <function specifier="non virtual">load( const IfConfig &amp; ifconfig )</function>
+ <function specifier="non virtual">save( IfConfig &amp; ifconfig )</function>
+</functions>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>klineedit.h</includehint>
+</includehints>
+</UI>
diff --git a/wifi/kcmwifi/configcrypto.ui.h b/wifi/kcmwifi/configcrypto.ui.h
new file mode 100644
index 00000000..6ada4aa4
--- /dev/null
+++ b/wifi/kcmwifi/configcrypto.ui.h
@@ -0,0 +1,157 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+#include <kdebug.h>
+#include "wificonfig.h"
+
+void ConfigCrypto::load( const IfConfig &ifconfig)
+{
+ cmb_activeKey->setCurrentItem( ifconfig.m_activeKey-1 );
+ bg_cryptoMode->setButton( ifconfig.m_cryptoMode );
+
+ le_key1->setText( ifconfig.m_keys[0].key() );
+ le_key2->setText( ifconfig.m_keys[1].key() );
+ le_key3->setText( ifconfig.m_keys[2].key() );
+ le_key4->setText( ifconfig.m_keys[3].key() );
+}
+
+void ConfigCrypto::save( IfConfig &ifconfig )
+{
+ ifconfig.m_activeKey = cmb_activeKey->currentItem()+1;
+ ifconfig.m_cryptoMode = (IfConfig::CryptoMode)bg_cryptoMode->selectedId();
+
+ ifconfig.m_keys[0].setKey( le_key1->text() );
+ ifconfig.m_keys[1].setKey( le_key2->text() );
+ ifconfig.m_keys[2].setKey( le_key3->text() );
+ ifconfig.m_keys[3].setKey( le_key4->text() );
+}
+
+
+void ConfigCrypto::slotUpdateKey1Status(const QString& key)
+{
+ KeyStates status = ::Key::isValid( key );
+ switch (status) {
+ case INVALID:
+ format_1->setText("<font color=\"#ff0000\">unrecognised</font>");
+ break;
+ case EMPTY:
+ format_1->setText("<font color=\"#000000\">slot empty</font>");
+ break;
+ case HEX_64:
+ format_1->setText("<font color=\"#00b000\">WEP 64-Bit hex</font>");
+ break;
+ case HEX_128:
+ format_1->setText("<font color=\"#00b000\">WEP 128-Bit hex</font>");
+ break;
+ case HEX_256:
+ format_1->setText("<font color=\"#00b000\">WEP 256-Bit hex</font>");
+ break;
+ case STRING_64:
+ format_1->setText("<font color=\"#00b000\">WEP 64-Bit string</font>");
+ break;
+ case STRING_128:
+ format_1->setText("<font color=\"#00b000\">WEP 128-Bit string</font>");
+ break;
+ case STRING_256:
+ format_1->setText("<font color=\"#00b000\">WEP 256-Bit string</font>");
+ break;
+ }
+}
+
+void ConfigCrypto::slotUpdateKey2Status(const QString& key)
+{
+ KeyStates status = ::Key::isValid( key );
+ switch (status) {
+ case INVALID:
+ format_2->setText("<font color=\"#ff0000\">unrecognised</font>");
+ break;
+ case EMPTY:
+ format_2->setText("<font color=\"#000000\">slot empty</font>");
+ break;
+ case HEX_64:
+ format_2->setText("<font color=\"#00b000\">WEP 64-Bit hex</font>");
+ break;
+ case HEX_128:
+ format_2->setText("<font color=\"#00b000\">WEP 128-Bit hex</font>");
+ break;
+ case HEX_256:
+ format_2->setText("<font color=\"#00b000\">WEP 256-Bit hex</font>");
+ break;
+ case STRING_64:
+ format_2->setText("<font color=\"#00b000\">WEP 64-Bit string</font>");
+ break;
+ case STRING_128:
+ format_2->setText("<font color=\"#00b000\">WEP 128-Bit string</font>");
+ break;
+ case STRING_256:
+ format_2->setText("<font color=\"#00b000\">WEP 256-Bit string</font>");
+ break;
+ }
+}
+
+void ConfigCrypto::slotUpdateKey3Status(const QString& key)
+{
+ KeyStates status = ::Key::isValid( key );
+ switch (status) {
+ case INVALID:
+ format_3->setText("<font color=\"#ff0000\">unrecognised</font>");
+ break;
+ case EMPTY:
+ format_3->setText("<font color=\"#000000\">slot empty</font>");
+ break;
+ case HEX_64:
+ format_3->setText("<font color=\"#00b000\">WEP 64-Bit hex</font>");
+ break;
+ case HEX_128:
+ format_3->setText("<font color=\"#00b000\">WEP 128-Bit hex</font>");
+ break;
+ case HEX_256:
+ format_3->setText("<font color=\"#00b000\">WEP 256-Bit hex</font>");
+ break;
+ case STRING_64:
+ format_3->setText("<font color=\"#00b000\">WEP 64-Bit string</font>");
+ break;
+ case STRING_128:
+ format_3->setText("<font color=\"#00b000\">WEP 128-Bit string</font>");
+ break;
+ case STRING_256:
+ format_3->setText("<font color=\"#00b000\">WEP 256-Bit string</font>");
+ break;
+ }
+}
+
+void ConfigCrypto::slotUpdateKey4Status(const QString& key)
+{
+ KeyStates status = ::Key::isValid( key );
+ switch (status) {
+ case INVALID:
+ format_4->setText("<font color=\"#ff0000\">unrecognised</font>");
+ break;
+ case EMPTY:
+ format_4->setText("<font color=\"#000000\">slot empty</font>");
+ break;
+ case HEX_64:
+ format_4->setText("<font color=\"#00b000\">WEP 64-Bit hex</font>");
+ break;
+ case HEX_128:
+ format_4->setText("<font color=\"#00b000\">WEP 128-Bit hex</font>");
+ break;
+ case HEX_256:
+ format_4->setText("<font color=\"#00b000\">WEP 256-Bit hex</font>");
+ break;
+ case STRING_64:
+ format_4->setText("<font color=\"#00b000\">WEP 64-Bit string</font>");
+ break;
+ case STRING_128:
+ format_4->setText("<font color=\"#00b000\">WEP 128-Bit string</font>");
+ break;
+ case STRING_256:
+ format_4->setText("<font color=\"#00b000\">WEP 256-Bit string</font>");
+ break;
+ }
+}
diff --git a/wifi/kcmwifi/configpower.ui b/wifi/kcmwifi/configpower.ui
new file mode 100644
index 00000000..fa44c928
--- /dev/null
+++ b/wifi/kcmwifi/configpower.ui
@@ -0,0 +1,158 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ConfigPower</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigPower</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>318</width>
+ <height>186</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>lb_sleepTimeout</cstring>
+ </property>
+ <property name="text">
+ <string>Sleep timeout:</string>
+ </property>
+ <property name="toolTip">
+ <string>Sets how long the card will be offline before looking for new packages again.</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="0" column="1">
+ <property name="name">
+ <cstring>sb_sleepTimeout</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="maxValue">
+ <number>60</number>
+ </property>
+ <property name="suffix">
+ <string> sec</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lb_wakeupPeriod</cstring>
+ </property>
+ <property name="text">
+ <string>Wakeup period:</string>
+ </property>
+ <property name="toolTip">
+ <string>Sets how long the card will be online and looking for new packages before it falls asleep.</string>
+ </property>
+ </widget>
+ <widget class="QButtonGroup" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>bg_packets</cstring>
+ </property>
+ <property name="title">
+ <string>Receive Packets</string>
+ </property>
+ <property name="toolTip">
+ <string>Sets which sort of packets to listen to.</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>bg_allPackets</cstring>
+ </property>
+ <property name="text">
+ <string>All</string>
+ </property>
+ <property name="toolTip">
+ <string>Listen to all packet types.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_unicastOnly</cstring>
+ </property>
+ <property name="text">
+ <string>Unicast only</string>
+ </property>
+ <property name="toolTip">
+ <string>Listen to Unicast packets only.</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>rb_multicastOnly</cstring>
+ </property>
+ <property name="text">
+ <string>Multicast/Broadcast only</string>
+ </property>
+ <property name="toolTip">
+ <string>Listen to Multicast and Broadcast packets only.</string>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="KIntNumInput" row="1" column="1">
+ <property name="name">
+ <cstring>sb_wakeupPeriod</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minValue">
+ <number>0</number>
+ </property>
+ <property name="maxValue">
+ <number>60</number>
+ </property>
+ <property name="suffix">
+ <string> sec</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<includes>
+ <include location="local" impldecl="in declaration">wificonfig.h</include>
+ <include location="local" impldecl="in implementation">configpower.ui.h</include>
+</includes>
+<slots>
+ <slot>load( const IfConfig &amp; ifconfig )</slot>
+ <slot>save( IfConfig &amp; ifconfig )</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/wifi/kcmwifi/configpower.ui.h b/wifi/kcmwifi/configpower.ui.h
new file mode 100644
index 00000000..feb0906f
--- /dev/null
+++ b/wifi/kcmwifi/configpower.ui.h
@@ -0,0 +1,22 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you wish to add, delete or rename functions or slots use
+** Qt Designer which will update this file, preserving your code. Create an
+** init() function in place of a constructor, and a destroy() function in
+** place of a destructor.
+*****************************************************************************/
+
+void ConfigPower::load( const IfConfig &ifconfig)
+{
+ sb_sleepTimeout->setValue(ifconfig.m_sleepTimeout);
+ sb_wakeupPeriod->setValue(ifconfig.m_wakeupPeriod);
+ bg_packets->setButton(ifconfig.m_pmMode);
+}
+
+void ConfigPower::save( IfConfig &ifconfig)
+{
+ ifconfig.m_sleepTimeout = sb_sleepTimeout->value();
+ ifconfig.m_wakeupPeriod = sb_wakeupPeriod->value();
+ ifconfig.m_pmMode = (IfConfig::PowerMode)bg_packets->selectedId();
+}
diff --git a/wifi/kcmwifi/ifconfigpage.cpp b/wifi/kcmwifi/ifconfigpage.cpp
new file mode 100644
index 00000000..a7306255
--- /dev/null
+++ b/wifi/kcmwifi/ifconfigpage.cpp
@@ -0,0 +1,173 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <kdebug.h>
+#include <kdialogbase.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+
+#include <qcheckbox.h>
+#include <qcombobox.h>
+#include <qlabel.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+
+#include "configcrypto.h"
+#include "configpower.h"
+#include "ifconfigpage.h"
+#include "wificonfig.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+
+IfConfigPage::IfConfigPage( int configNum, QWidget *parent, const char *name )
+ : IfConfigPageBase( parent, name ),
+ m_configNum( configNum )
+{
+ connect( cb_pmEnabled, SIGNAL( toggled( bool ) ), SIGNAL( changed() ) );
+ connect( cb_useCrypto, SIGNAL( toggled( bool ) ), SIGNAL( changed() ) );
+ connect( le_networkName, SIGNAL( textChanged( const QString & ) ),
+ SIGNAL( changed() ) );
+ connect( cmb_wifiMode, SIGNAL( activated( int ) ), SIGNAL( changed() ) );
+ connect( cmb_speed, SIGNAL( activated( int ) ), SIGNAL( changed() ) );
+ connect( cb_runScript, SIGNAL( toggled( bool ) ), SIGNAL( changed() ) );
+ connect( url_connectScript, SIGNAL( textChanged( const QString & ) ),
+ SIGNAL( changed() ) );
+ connect( cb_Autodetect, SIGNAL( toggled( bool ) ), SIGNAL( changed() ) );
+ connect( le_interface, SIGNAL( textChanged( const QString & ) ),
+ SIGNAL( changed() ) );
+
+ connect( pb_setupPower, SIGNAL( clicked() ), SLOT( slotSetupPower() ) );
+ connect( pb_setupCrypto, SIGNAL( clicked() ), SLOT( slotSetupCrypto() ) );
+
+ connect( cb_useCrypto, SIGNAL( toggled( bool ) ),
+ pb_setupCrypto, SLOT( setEnabled( bool ) ) );
+ connect( cb_pmEnabled, SIGNAL( toggled( bool ) ),
+ pb_setupPower, SLOT( setEnabled( bool ) ) );
+ connect( cb_runScript, SIGNAL( toggled( bool ) ),
+ lb_connectScript, SLOT( setEnabled( bool ) ) );
+ connect( cb_runScript, SIGNAL( toggled( bool ) ),
+ lb_connectScript, SLOT( setEnabled( bool ) ) );
+ connect( cb_runScript, SIGNAL( toggled( bool ) ),
+ lb_connectScript, SLOT( setEnabled( bool ) ) );
+ connect( cb_Autodetect, SIGNAL( toggled( bool ) ),
+ le_interface, SLOT( setDisabled( bool ) ) );
+
+ connect( cb_Autodetect, SIGNAL( toggled( bool ) ),
+ this, SLOT ( slotResetInterface( bool ) ) );
+}
+
+void IfConfigPage::slotResetInterface( bool checked )
+{
+ if ( checked )
+ {
+ WifiConfig *config = WifiConfig::instance();
+ le_interface->setText( config->autoDetectInterface() );
+ }
+}
+
+void IfConfigPage::load()
+{
+ WifiConfig *config = WifiConfig::instance();
+ IfConfig ifConfig = config->m_ifConfig[ m_configNum ];
+
+ le_networkName->setText( ifConfig.m_networkName );
+ if ( ! ifConfig.m_interface.isEmpty() )
+ {
+ le_interface->setText( ifConfig.m_interface );
+ cb_Autodetect->setChecked( false );
+ }
+ else
+ {
+ if ( geteuid()==0 )
+ le_interface->setText( config->autoDetectInterface() );
+ cb_Autodetect->setChecked( true );
+ }
+
+ cmb_wifiMode->setCurrentItem( ifConfig.m_wifiMode );
+ cmb_speed->setCurrentItem( ifConfig.m_speed );
+ cb_runScript->setChecked( ifConfig.m_runScript );
+ url_connectScript->setURL( ifConfig.m_connectScript );
+ cb_useCrypto->setChecked( ifConfig.m_useCrypto );
+ cb_pmEnabled->setChecked( ifConfig.m_pmEnabled );
+
+ pb_setupCrypto->setEnabled( ifConfig.m_useCrypto );
+ pb_setupPower->setEnabled( ifConfig.m_pmEnabled );
+ lb_connectScript->setEnabled( ifConfig.m_runScript );
+ url_connectScript->setEnabled( ifConfig.m_runScript );
+}
+
+void IfConfigPage::save()
+{
+ WifiConfig *config = WifiConfig::instance();
+ IfConfig &ifconfig = config->m_ifConfig[ m_configNum ];
+
+ ifconfig.m_networkName = le_networkName->text();
+ ifconfig.m_interface = cb_Autodetect->isChecked()? QString::null :
+ le_interface->text();
+ ifconfig.m_wifiMode = ( IfConfig::WifiMode ) cmb_wifiMode->currentItem();
+ ifconfig.m_speed = ( IfConfig::Speed ) cmb_speed->currentItem();
+ ifconfig.m_runScript = cb_runScript->isChecked();
+ ifconfig.m_connectScript = url_connectScript->url();
+ ifconfig.m_useCrypto = cb_useCrypto->isChecked();
+ ifconfig.m_pmEnabled = cb_pmEnabled->isChecked();
+}
+
+void IfConfigPage::slotSetupPower()
+{
+ KDialogBase *dlg = new KDialogBase( this, "ConfigPower", true,
+ i18n( "Configure Power Mode" ), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok );
+ ConfigPower *power = new ConfigPower( dlg, "ConfigPower" );
+
+ WifiConfig *config = WifiConfig::instance();
+ IfConfig &ifconfig = config->m_ifConfig[ m_configNum ];
+ power->load( ifconfig );
+
+ dlg->setMainWidget( power );
+
+ if ( dlg->exec() == KDialog::Accepted )
+ {
+ power->save( ifconfig );
+ emit changed();
+ }
+}
+
+void IfConfigPage::slotSetupCrypto()
+{
+ KDialogBase *dlg = new KDialogBase( this, "ConfigCrypto", true,
+ i18n( "Configure Encryption" ), KDialogBase::Ok|KDialogBase::Cancel,
+ KDialogBase::Ok );
+ ConfigCrypto *crypto = new ConfigCrypto( dlg, "ConfigCrypto" );
+
+ WifiConfig *config = WifiConfig::instance();
+ IfConfig &ifconfig = config->m_ifConfig[ m_configNum ];
+ crypto->load( ifconfig );
+
+ dlg->setMainWidget( crypto );
+
+ if ( dlg->exec() == KDialog::Accepted )
+ {
+ crypto->save( ifconfig );
+ emit changed();
+ }
+}
+
+#include "ifconfigpage.moc"
diff --git a/wifi/kcmwifi/ifconfigpage.h b/wifi/kcmwifi/ifconfigpage.h
new file mode 100644
index 00000000..05ae61c0
--- /dev/null
+++ b/wifi/kcmwifi/ifconfigpage.h
@@ -0,0 +1,51 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef IFCONFIGPAGE_H
+#define IFCONFIGPAGE_H
+
+#include "ifconfigpagebase.h"
+#include "wificonfig.h"
+
+class IfConfigPage : public IfConfigPageBase
+{
+ Q_OBJECT
+
+ public:
+ IfConfigPage( int configNum, QWidget *parent, const char *name );
+ ~IfConfigPage() {}
+
+ void load();
+ void save();
+
+ protected slots:
+ void slotSetupPower();
+ void slotSetupCrypto();
+ void slotResetInterface( bool checked );
+
+ signals:
+ void changed();
+ void setupCrypto();
+ void setupPower();
+
+ private:
+ int m_configNum;
+};
+
+#endif // IFCONFIGPAGE_H
diff --git a/wifi/kcmwifi/ifconfigpagebase.ui b/wifi/kcmwifi/ifconfigpagebase.ui
new file mode 100644
index 00000000..45df9e19
--- /dev/null
+++ b/wifi/kcmwifi/ifconfigpagebase.ui
@@ -0,0 +1,500 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>IfConfigPageBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>IfConfigPageBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>626</width>
+ <height>242</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="0" column="0">
+ <property name="name">
+ <cstring>layout16</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lb_netName</cstring>
+ </property>
+ <property name="text">
+ <string>Network name:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>SSID of the network to connect to. "any" means "arbitrary available network".</string>
+ </property>
+ </widget>
+ <widget class="KLineEdit">
+ <property name="name">
+ <cstring>le_networkName</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lb_ifName</cstring>
+ </property>
+ <property name="text">
+ <string>Interface:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>SSID of the network to connect to. "any" means "arbitrary available network".</string>
+ </property>
+ </widget>
+ <widget class="QLineEdit">
+ <property name="name">
+ <cstring>le_interface</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_Autodetect</cstring>
+ </property>
+ <property name="text">
+ <string>Autodetect</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer16</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>226</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="4" column="0">
+ <property name="name">
+ <cstring>layout13</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lb_connectScript</cstring>
+ </property>
+ <property name="text">
+ <string>Script:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Command to perform after the connection is established, e.g. for obtaining a DHCP lease.</string>
+ </property>
+ </widget>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>url_connectScript</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer18</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>196</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="5" column="0">
+ <property name="name">
+ <cstring>layout11</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_pmEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>Enable power management</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enables advanced power management settings.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pb_setupPower</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Configure...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Clicking this button opens the Power Management menu.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>285</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="2" column="0">
+ <property name="name">
+ <cstring>layout6</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Speed:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Sets the connection speed. Not all cards support this.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Auto</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>1 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>2 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>5.5 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>6 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>9 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>11 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>12 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>18 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>24 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>36 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>48 Mb/s</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>54 Mb/s</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmb_speed</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>190</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>lb_opMode</cstring>
+ </property>
+ <property name="text">
+ <string>Operation mode:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Operation mode of the card. For standard infrastructure networks, "Managed" is appropriate.</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <item>
+ <property name="text">
+ <string>Ad-Hoc</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Managed</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Repeater</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Master</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Secondary</string>
+ </property>
+ </item>
+ <property name="name">
+ <cstring>cmb_wifiMode</cstring>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>413</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="3" column="0">
+ <property name="name">
+ <cstring>layout14</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_runScript</cstring>
+ </property>
+ <property name="text">
+ <string>Execute script on connect</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Activates a script to perform arbitrary operations after associating with the network.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer19</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>131</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget" row="6" column="0">
+ <property name="name">
+ <cstring>layout17</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>cb_useCrypto</cstring>
+ </property>
+ <property name="text">
+ <string>Use encryption</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Enables WEP encryption.</string>
+ </property>
+ </widget>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>pb_setupCrypto</cstring>
+ </property>
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Configure...</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Clicking this button opens the Encryption menu.</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>396</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<connections>
+ <connection>
+ <sender>cb_Autodetect</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>le_interface</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_runScript</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>lb_connectScript</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cb_runScript</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>url_connectScript</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<tabstops>
+ <tabstop>le_networkName</tabstop>
+ <tabstop>cmb_wifiMode</tabstop>
+ <tabstop>cmb_speed</tabstop>
+ <tabstop>cb_runScript</tabstop>
+ <tabstop>url_connectScript</tabstop>
+ <tabstop>cb_pmEnabled</tabstop>
+ <tabstop>pb_setupPower</tabstop>
+ <tabstop>cb_useCrypto</tabstop>
+ <tabstop>pb_setupCrypto</tabstop>
+</tabstops>
+<includes>
+ <include location="global" impldecl="in implementation">kdialog.h</include>
+</includes>
+<layoutdefaults spacing="6" margin="11"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includehints>
+ <includehint>klineedit.h</includehint>
+ <includehint>kurlrequester.h</includehint>
+ <includehint>klineedit.h</includehint>
+ <includehint>kpushbutton.h</includehint>
+</includehints>
+</UI>
diff --git a/wifi/kcmwifi/indent-options b/wifi/kcmwifi/indent-options
new file mode 100644
index 00000000..b79f4fbb
--- /dev/null
+++ b/wifi/kcmwifi/indent-options
@@ -0,0 +1 @@
+-prs -l120
diff --git a/wifi/kcmwifi/kcmwifi.cpp b/wifi/kcmwifi/kcmwifi.cpp
new file mode 100644
index 00000000..55920dad
--- /dev/null
+++ b/wifi/kcmwifi/kcmwifi.cpp
@@ -0,0 +1,382 @@
+/*
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ (C) 2001-2004 by Stefan Winter <mail@stefan-winter.de>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qlayout.h>
+#include <qtabwidget.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+#include <kaboutdata.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kgenericfactory.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocio.h>
+#include <klineedit.h>
+
+#include "kcmwifi.h"
+#include "ifconfigpage.h"
+#include "mainconfig.h"
+#include "configcrypto.h"
+#include "wificonfig.h"
+#include "vendorconfig.h"
+
+typedef KGenericFactory < KCMWifi, QWidget > KWiFiFactory;
+K_EXPORT_COMPONENT_FACTORY (kcm_wifi, KWiFiFactory("kcmwifi") )
+
+KCMWifi::KCMWifi(QWidget * parent, const char *name, const QStringList &)
+ : KCModule (parent, name)
+{
+ tabs = new QTabWidget (this, "tabs");
+
+ m_mainConfig = new MainConfig(this, "m_mainConfig");
+
+ WifiConfig *config = WifiConfig::instance();
+
+ for ( int i=0; i<config->m_numConfigs; ++i )
+ {
+ IfConfigPage *ifConfigPage = new IfConfigPage( i, tabs, "m_configPage" );
+ tabs->addTab(ifConfigPage, i18n ("Config &%1").arg( i+1 ));
+ connect(ifConfigPage, SIGNAL(changed()), SLOT( slotChanged() ) );
+
+ m_ifConfigPage[ i ] = ifConfigPage;
+ }
+
+ QVBoxLayout *top = new QVBoxLayout (this, 0, KDialog::spacingHint());
+ top->addWidget( tabs );
+ top->addWidget( m_mainConfig );
+ top->addStretch();
+
+ connect(m_mainConfig, SIGNAL(changed()), SLOT( slotChanged() ) );
+ connect(m_mainConfig, SIGNAL(activateClicked()), SLOT(slotActivate()));
+
+ if ( geteuid() != 0 )
+ {
+ tabs->setEnabled( false );
+ m_mainConfig->setEnabled( false );
+ }
+ else
+ {
+ KProcess iwconfigtest;
+ iwconfigtest << "iwconfig";
+ if (!iwconfigtest.start(KProcess::DontCare))
+ {
+ KMessageBox::sorry(0, i18n("Error executing iwconfig. WLAN "
+ "configurations can only be altered if the wireless tools are "
+ "properly installed."), i18n ("No Wireless Tools"));
+
+ tabs->setEnabled( false );
+ m_mainConfig->setEnabled( false );
+ }
+ }
+
+ load();
+
+ m_activeVendorCount = 0;
+ VendorConfig vendors( this );
+ vendors.initAll();
+}
+
+
+KCMWifi::~KCMWifi()
+{
+}
+
+void KCMWifi::load()
+{
+ WifiConfig *config = WifiConfig::instance();
+
+ for ( int i=0; i<config->m_numConfigs; ++i )
+ m_ifConfigPage[ i ]->load();
+
+ m_mainConfig->load();
+}
+
+void KCMWifi::save()
+{
+ WifiConfig *config = WifiConfig::instance();
+ for ( int i=0; i<config->m_numConfigs; ++i )
+ m_ifConfigPage[ i ]->save();
+
+ m_mainConfig->save();
+
+ config->save();
+}
+
+void KCMWifi::slotActivate()
+{
+ save();
+ activate();
+}
+
+void KCMWifi::defaults()
+{
+}
+
+void KCMWifi::slotChanged()
+{
+ emit changed( true );
+}
+
+IfConfigPage* KCMWifi::addConfigTab( int count, bool vendor )
+{
+
+ WifiConfig *config = WifiConfig::instance();
+
+ IfConfigPage* retval = NULL;
+
+ if (vendor)
+ {
+ IfConfigPage *ifConfigPage = new IfConfigPage( vendorBase + m_activeVendorCount, tabs, "m_configPage" );
+ tabs->addTab(ifConfigPage, i18n ("Vendor %1").arg( m_activeVendorCount+1 ) );
+ connect(ifConfigPage, SIGNAL(changed()), SLOT( slotChanged() ) );
+ m_ifConfigPage[ vendorBase+m_activeVendorCount ] = ifConfigPage;
+ m_mainConfig->registerConfig( vendorBase+m_activeVendorCount );
+ m_activeVendorCount++;
+ retval = ifConfigPage;
+
+ // these configs are read-only, so disable all widgets
+ // the content shall only be modified by VendorConfig code
+ retval->cb_Autodetect->setEnabled( false );
+ retval->le_networkName->setReadOnly( true );
+ retval->cmb_wifiMode->setEnabled( false );
+ retval->cmb_speed->setEnabled( false );
+ retval->cb_runScript->setEnabled( false );
+ retval->cb_useCrypto->setEnabled( false );
+ retval->cb_pmEnabled->setEnabled( false );
+
+ }
+ else
+ {
+ for ( int i=config->m_numConfigs; i<config->m_numConfigs+count; i++ )
+ {
+ IfConfigPage *ifConfigPage = new IfConfigPage( i, tabs, "m_configPage" );
+ tabs->insertTab(ifConfigPage, i18n ("Config &%1").arg( i+1 ), i );
+ connect(ifConfigPage, SIGNAL(changed()), SLOT( slotChanged() ) );
+ m_ifConfigPage[ i ] = ifConfigPage;
+ m_mainConfig->registerConfig( i+1 );
+ retval = ifConfigPage;
+ }
+ config->m_numConfigs += count;
+ }
+
+ // if more than one was constructed, only the last is returned
+ return retval;
+}
+
+void KCMWifi::delConfigTab( int count )
+{
+ WifiConfig *config = WifiConfig::instance();
+
+ for ( int i=config->m_numConfigs-1; i>(config->m_numConfigs)-count-1; i-- )
+ {
+ tabs->setTabEnabled(m_ifConfigPage[ i ], false);
+ m_ifConfigPage[ i ]->deleteLater();
+
+ m_mainConfig->unregisterConfig( i+1 );
+
+ }
+
+ config->m_numConfigs -= count;
+
+}
+
+void KCMWifi::activate()
+{
+ KProcess proc;
+
+ QStringList failedParts;
+
+ WifiConfig *config = WifiConfig::instance();
+ int index;
+ if ( config->m_presetConfig >= config->m_numConfigs )
+ {
+ index = vendorBase+config->m_presetConfig-config->m_numConfigs;
+ } else
+ {
+ index = config->m_presetConfig;
+ }
+
+ kdDebug() << "I was told to use array slot number " << index << ".\n";
+
+ IfConfig ifconfig = config->m_ifConfig[ index ];
+
+ QString tempInterface;
+
+ if ( ifconfig.m_interface.isEmpty() ) {
+ tempInterface = config->autoDetectInterface();
+ kdDebug() << "Autodetecting interface...\n";
+ } else
+ {
+ tempInterface = ifconfig.m_interface;
+ }
+
+ proc << "ifconfig"
+ << tempInterface
+ << "down";
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (!proc.normalExit()) || (proc.exitStatus() != 0) )
+ failedParts << i18n("Interface could not be shut down. It is likely that your settings have not been applied.");
+
+ proc.clearArguments();
+
+ proc << "iwconfig"
+ << tempInterface;
+ proc << "essid"
+ << ifconfig.m_networkName;
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (!proc.normalExit()) || (proc.exitStatus() != 0) )
+ failedParts << i18n("SSID could not be set.");
+
+ proc.clearArguments();
+
+ proc << "iwconfig"
+ << tempInterface;
+ proc << "mode"
+ << ifconfig.wifimodeAsString();
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (!proc.normalExit()) || (proc.exitStatus() != 0) )
+ failedParts << i18n("Operation mode could not be set.");
+
+ proc.clearArguments();
+
+ proc << "iwconfig"
+ << tempInterface;
+ proc << "rate"
+ << ifconfig.speedAsString();
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (!proc.normalExit()) || (proc.exitStatus() != 0) )
+ failedParts << i18n("Speed settings could not be modified.");
+
+
+ proc.clearArguments();
+
+ proc << "iwconfig"
+ << tempInterface;
+
+ if (!ifconfig.m_useCrypto )
+ {
+ proc << "key"
+ << "off";
+ }
+ else
+ {
+ for ( int i=0; i<4; ++i )
+ {
+ if ( ifconfig.m_keys[ i ].isValid( ifconfig.m_keys[ i ].key() ) > INVALID )
+ {
+ proc << "key"
+ << QString( "[%1]").arg( i+1 )
+ << QString( "%1").arg( ifconfig.m_keys[ i ].rawKey() );
+ }
+ }
+
+ // kdDebug() << "All appropriate keys have been fed into iwconfig.\n";
+
+ if ( ifconfig.activeKey().isValid( ifconfig.activeKey().key() ) > INVALID )
+ {
+ // kdDebug() << "Entering final phase of key setup.\n";
+ proc << "key"
+ << ifconfig.cryptomodeAsString();
+ proc << "key"
+ << QString( "[%1]" ).arg( ifconfig.activeKeyId() );
+ proc << "key"
+ << "on";
+ }
+ }
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (ifconfig.m_useCrypto) && ((!proc.normalExit()) || (proc.exitStatus() != 0)) )
+ failedParts << i18n("Encryption settings could not be set.");
+
+ proc.clearArguments();
+
+ proc << "iwconfig"
+ << tempInterface;
+
+ if ( !ifconfig.m_pmEnabled )
+ {
+ proc << "power"
+ << "off";
+ }
+ else
+ {
+ proc << "power"
+ << "period"
+ << QString( "%1" ).arg( ifconfig.m_wakeupPeriod );
+ proc << "power"
+ << "timeout"
+ << QString( "%1" ).arg( ifconfig.m_sleepTimeout );
+ proc << ifconfig.powermodeAsString();
+ }
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (ifconfig.m_pmEnabled) && ((!proc.normalExit()) || (proc.exitStatus() != 0)) )
+ failedParts << i18n("Power management settings could not be set.");
+
+ proc.clearArguments();
+ proc << "ifconfig"
+ << tempInterface
+ << "up";
+
+ kdDebug() << "Command: " << proc.args() << endl;
+ proc.start (KProcess::Block);
+
+ if ( (!proc.normalExit()) || (proc.exitStatus() != 0) )
+ failedParts << i18n("Interface could not be re-enabled.");
+
+ if ( ifconfig.m_runScript )
+ {
+ proc.clearArguments();
+ proc << QStringList::split( " ", ifconfig.m_connectScript );
+
+ proc.start(KProcess::DontCare);
+ }
+
+if (!failedParts.empty()) KMessageBox::informationList(0,i18n("The following settings could not be applied:"),failedParts);
+
+}
+
+extern "C"
+{
+ void init_wifi()
+ {
+ KCMWifi::activate();
+ }
+}
+
+
+#include "kcmwifi.moc"
diff --git a/wifi/kcmwifi/kcmwifi.desktop b/wifi/kcmwifi/kcmwifi.desktop
new file mode 100644
index 00000000..15d506dc
--- /dev/null
+++ b/wifi/kcmwifi/kcmwifi.desktop
@@ -0,0 +1,162 @@
+[Desktop Entry]
+Icon=kwifimanager
+Type=Application
+Exec=kcmshell kcmwifi
+DocPath=
+X-KDE-ModuleType=Library
+X-KDE-Library=wifi
+X-KDE-RootOnly=true
+Name=Wireless Network
+Name[ar]=شبكة لاسلكية
+Name[bg]=Безжична мрежа
+Name[bn]=ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ নেটওয়ারà§à¦•
+Name[br]=Rouedad hep neud
+Name[bs]=BežiÄna mreža
+Name[ca]=Xarxa sense fils
+Name[cs]=Bezdrátová síť
+Name[cy]=Rhwydwaith Di-Wifren
+Name[da]=Trådløst netværk
+Name[de]=Drahtloses Netzwerk
+Name[el]=ΑσÏÏματο δίκτυο
+Name[eo]=Sendrata reto
+Name[es]=Red inalámbrica
+Name[et]=Traadita võrk
+Name[eu]=Kablerik gabeko sarea
+Name[fa]=شبکۀ بی‌سیم
+Name[fi]=Langaton verkko
+Name[fr]=Réseau sans fil
+Name[ga]=Gréasán gan sreang
+Name[gl]=Rede inalámbrica
+Name[he]=רשת ×לחוטית
+Name[hi]=वायरलेस नेटवरà¥à¤•
+Name[hr]=BežiÄna mreža
+Name[hu]=Vezeték nélküli hálózat
+Name[is]=Þráðlaus nettenging
+Name[it]=Rete senza fili
+Name[ja]=ワイヤレスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯
+Name[ka]=უკáƒáƒ‘ელრქსელი
+Name[kk]=СымÑыз желі
+Name[km]=បណ្ដាញ​ឥážâ€‹ážáŸ’សែ
+Name[lt]=Bevielis tinklas
+Name[mk]=Безжична мрежа
+Name[ms]=Jaringan Tanpa Wayar
+Name[nb]=Trådløst nettverk
+Name[nds]=WLAN-Nettwark
+Name[ne]=तारविहिन सञà¥à¤œà¤¾à¤²
+Name[nl]=Draadloos netwerk
+Name[nn]=Trådlaust nettverk
+Name[pa]=ਬੇਤਾਰ ਨੈੱਟਵਰਕ
+Name[pl]=Sieć bezprzewodowa
+Name[pt]=Rede Sem Fios
+Name[pt_BR]=Rede Sem Fio
+Name[ru]=БеÑÐ¿Ñ€Ð¾Ð²Ð¾Ð´Ð½Ð°Ñ Ñеть
+Name[se]=Jođaskeahttes fierpmádat
+Name[sk]=Bezdrôtová sieť
+Name[sl]=BrezžiÄno omrežje
+Name[sr]=Бежична мрежа
+Name[sr@Latn]=BežiÄna mreža
+Name[sv]=Trådlöst nätverk
+Name[ta]=கமà¯à®ªà®¿à®¯à®¿à®²à¯à®²à®¾ பிணையமà¯
+Name[tg]=Шабакаи Беноқил
+Name[tr]=Telsiz AÄŸ
+Name[uk]=Радіомережа
+Name[uz]=Simsiz tarmoq
+Name[uz@cyrillic]=СимÑиз тармоқ
+Name[zh_CN]=无线网
+Name[zh_HK]=無線網絡
+Name[zh_TW]=無線網路
+Comment=Set up your wireless LAN
+Comment[ar]=ركب شبكة LAN الاسلكية الخاصةبك
+Comment[bg]=ÐаÑтройване на безжична локална мрежа
+Comment[bn]=আপনার ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ লà§à¦¯à¦¾à¦¨ সà§à¦¥à¦¾à¦ªà¦¨ করà§à¦¨
+Comment[br]=Lakaat ho roueded hep neud
+Comment[bs]=Podesite vaÅ¡ bežiÄni LAN
+Comment[ca]=Configureu la vostra xarxa local sense fils
+Comment[cs]=Nastavte si svou bezdrátovou síť
+Comment[cy]=Gosod eich LAN di-wifren
+Comment[da]=Opsætning af trådløst LAN
+Comment[de]=Einrichtung eines drahtlosen Netzwerkes
+Comment[el]=Ρυθμίστε το ασÏÏματο δίκτυό σας
+Comment[es]=Configure su red inalámbrica local
+Comment[et]=Traadita võrgu seadistamine
+Comment[eu]=Konfiguratu zure kablerik gabeko LAN-a
+Comment[fa]=تنظیم شبکۀ داخلی بی‌سیم شما
+Comment[fi]=Aseta langaton lähiverkko
+Comment[fr]=Configuration de votre réseau sans fil
+Comment[gl]=Configura a túa rede inalámbrica
+Comment[he]=הגדרת רשת ×”- LAN ×”×לחוטית שלך
+Comment[hi]=आपके बेतार लैन सेट अप करे
+Comment[hr]=PodeÅ¡avamke vaÅ¡e bežiÄne lokalne mrežu
+Comment[hu]=A vezeték nélküli (WiFi) hálózat beállításai
+Comment[is]=Uppsetning á þráðlausu staðarneti
+Comment[it]=Configura la tua rete LAN senza fili
+Comment[ja]=ワイヤレス LAN をセットアップ
+Comment[ka]=თქვენი უკáƒáƒ‘ელრLAN-ის გáƒáƒ›áƒáƒ áƒ—ვáƒ
+Comment[kk]=СымÑыз желіңізді баптау
+Comment[km]=រៀបចំ​បណ្ដាញ​មូលដ្ឋាន​ឥážâ€‹ážáŸ’សែ​របស់​អ្នក
+Comment[lt]=Nustatyti bevielio ryšio tinklą
+Comment[mk]=ПоÑтавете ја вашата безжична локална мрежа
+Comment[ms]=Pasang LAN tanpa wayar
+Comment[nb]=Sett opp trådløst nettverk
+Comment[nds]=Dien Funk-Nettwark instellen
+Comment[ne]=तपाईà¤à¤•à¥‹ तारविहिन LAN सेटअप गरà¥à¤¨à¥à¤¹à¥‹à¤¸à¥
+Comment[nl]=Stel hier uw draadloos netwerk in
+Comment[nn]=Set opp trådlaust nettverk
+Comment[pl]=Ustawienia sieci bezprzewodowej
+Comment[pt]=Configuração da rede sem fios
+Comment[pt_BR]=Configurar a sua rede local sem fio
+Comment[ru]=ÐаÑтройка беÑпроводной Ñети
+Comment[se]=Heivet iežat jođaskeahttes LAN:a
+Comment[sk]=Nastavte si vašu bezdrôtovu sieť LAN
+Comment[sl]=Nastavite svoje brezžiÄno lokalno omrežje
+Comment[sr]=ПодеÑите вашу бежичну локалну мрежу
+Comment[sr@Latn]=Podesite vaÅ¡u bežiÄnu lokalnu mrežu
+Comment[sv]=Anpassa trådlöst nätverk
+Comment[ta]=உஙà¯à®•à®³à¯ கமà¯à®ªà®¿à®¯à®¿à®²à¯à®²à®¾ LAN ஠அமைகà¯à®•à®¿à®±à®¤à¯
+Comment[tg]=БарпоÑозии шабакаи беноқили маҳаллии шумо
+Comment[tr]=Telsiz ağı yapılandır
+Comment[uk]=ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ñ— радіо мережі
+Comment[uz]=Simsiz tarmoqni oʻrnatish
+Comment[uz@cyrillic]=СимÑиз тармоқни ўрнатиш
+Comment[zh_CN]=设置您的无线局域网
+Comment[zh_HK]=設定您的無線網絡
+Comment[zh_TW]=設定您的無線網路
+Keywords=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[bg]=мрежа, безжична, наÑтройки, локална, ÑподелÑне, network, orinoco, wireless, wlan, wlan-ng
+Keywords[ca]=xarxa,orinoco,sense fils,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[cs]=síť,orinoco,bezdrát,wlan,wlan-ng,wifi,adhoc
+Keywords[da]=network,orinoco,trådløs,wlan,wlan-ng, wifi,Ad-Hoc
+Keywords[de]=Netzwerk,orinoco,drahtlos,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[el]=δίκτυο,orinoco,ασÏÏματο,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[es]=red,orinoco,inalámbrica,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[et]=võrk,orinoco,traadita,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[eu]=sarea,orinoco,kablerik gabe,wlan,wlan-ng,ad-hoc
+Keywords[fa]=شبکه، orinoco، بی‌سیم، شبکۀ محلی بی‌سیم، wlan-ng، wifi، Ad-Hoc
+Keywords[fi]=verkko,orinoco,langaton,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[fr]=network,orinoco,wireless,wlan,wlan-ng,sans fil,Ad-Hoc
+Keywords[gl]=rede,orinoco,sen fíos,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[he]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc, רשת ×לחוטי
+Keywords[hu]=hálózat,orinoco,vezeték nélküli,wlan,wlan-ng,wifi,ad-hoc
+Keywords[is]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc,þráðlaust,net
+Keywords[it]=rete,orinoco,senza fili,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[ja]=ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯,orinoco,ワイヤレス,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[km]=បណ្ដាញ,orinoco,ឥážâ€‹ážáŸ’សែ,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[lt]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc,tinklas,bevielis,bevielis ryšys
+Keywords[nb]=nettverk,orinoco,trådløst,wlan,wlan-ng
+Keywords[nds]=nettwark,orinoco,drahtloos,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[ne]=सञà¥à¤œà¤¾à¤²,ओरिनोको,तारविहिन,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[nl]=netwerk,orinoco,wireless,wlan,wlan-ng,draadloos,wifi,ad-hoc
+Keywords[nn]=nettverk,orinoco,trådlaust,wlan,wlan-ng,wifi,ad-hoc
+Keywords[pl]=sieć,orinoco,bezprzewodowy,bezprzewodowa,wi-fi,wlan,wlan-ng,wifi,ad-hoc,adhoc
+Keywords[pt]=rede,orinoco,wireless,wlan,wlan-ng,sem fios,wifi,ad-hoc
+Keywords[pt_BR]=rede,orinoco,sem fio,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[sl]=omrežje,orinoco,brezžiÄno,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[sr]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc,мрежа,ориноко,бежична,бежично
+Keywords[sr@Latn]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc,mreža,orinoko,bežiÄna,bežiÄno
+Keywords[sv]=nätverk,orinoco,trådlöst,wlan,wlan-ng,wifi,ad-hoc
+Keywords[uk]=мережа,orinoco,радіо,бездротовий,зв'Ñзок,wlan,wlan-ng,wifi,Ad-Hoc
+Keywords[uz]=tarmoq,orinoco,simsiz,wlan,wlan-ng,simsiz tarmoq,wifi,Ad-Hoc
+Keywords[uz@cyrillic]=тармоқ,orinoco,ÑимÑиз,wlan,wlan-ng,ÑимÑиз тармоқ,wifi,Ad-Hoc
+Keywords[zh_CN]=network,orinoco,wireless,wlan,wlan-ng,wifi,Ad-Hoc,网络,无线,无线网络
+
+Categories=Qt;KDE;X-KDE-settings-network;Settings;
diff --git a/wifi/kcmwifi/kcmwifi.h b/wifi/kcmwifi/kcmwifi.h
new file mode 100644
index 00000000..747688ec
--- /dev/null
+++ b/wifi/kcmwifi/kcmwifi.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ (C) 2001 by Stefan Winter <mail@stefan-winter.de>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef KCMWIFI_H
+#define KCMWIFI_H
+
+#include <kcmodule.h>
+
+#include <qstring.h>
+
+class IfConfigPage;
+class MainConfig;
+class QTabWidget;
+class KProcIO;
+
+class KCMWifi : public KCModule
+{
+ Q_OBJECT
+public:
+ KCMWifi (QWidget * parent, const char *name, const QStringList &);
+ ~KCMWifi ();
+
+ void load();
+ void save();
+ void defaults();
+ int buttons();
+
+ static void activate();
+ IfConfigPage* addConfigTab( int count, bool vendor );
+ void delConfigTab( int count );
+
+ static const int vendorBase = 10;
+
+protected slots:
+ void slotActivate();
+ void slotChanged();
+
+private:
+ MainConfig *m_mainConfig;
+ IfConfigPage *m_ifConfigPage[ 15 ]; // that makes 5 vendor configs
+ QTabWidget* tabs;
+ int m_activeVendorCount;
+};
+
+#endif // KCMWIFI_H
diff --git a/wifi/kcmwifi/mainconfig.cpp b/wifi/kcmwifi/mainconfig.cpp
new file mode 100644
index 00000000..506d1f02
--- /dev/null
+++ b/wifi/kcmwifi/mainconfig.cpp
@@ -0,0 +1,108 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ (C) 2001-2004 by Stefan Winter <mail@stefan-winter.de>
+
+ 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.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include "mainconfig.h"
+#include "wificonfig.h"
+#include "kcmwifi.h"
+
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocio.h>
+#include <kpushbutton.h>
+#include <knuminput.h>
+
+#include <qcheckbox.h>
+#include <qfile.h>
+
+MainConfig::MainConfig( QWidget *parent, const char *name )
+ : MainConfigBase( parent, name )
+{
+ WifiConfig *config = WifiConfig::instance();
+
+ for (int i=1;i<=config->m_numConfigs;i++) {
+ cmb_presetConfig->insertItem( i18n( "Config %1" ).arg( i ) );
+ }
+
+
+ connect( cb_usePreset, SIGNAL( toggled( bool ) ), SIGNAL( changed() ) );
+ connect( cmb_presetConfig, SIGNAL( activated( int ) ), SIGNAL( changed() ) );
+ connect( sb_numConfigs, SIGNAL( valueChanged( int ) ), SIGNAL( changed() ) );
+
+ connect( pb_activate, SIGNAL( clicked() ), SIGNAL( activateClicked() ) );
+ connect( sb_numConfigs, SIGNAL( valueChanged( int ) ), SLOT( slotChangeNumConfigs( int ) ) );
+
+}
+
+void MainConfig::load()
+{
+ WifiConfig *config = WifiConfig::instance();
+
+ cb_usePreset->setChecked( config->m_usePreset );
+ cmb_presetConfig->setCurrentItem( config->m_presetConfig );
+ sb_numConfigs->setValue( config->m_numConfigs );
+}
+
+void MainConfig::save()
+{
+ WifiConfig *config = WifiConfig::instance();
+
+ config->m_usePreset = cb_usePreset->isChecked();
+ config->m_presetConfig = cmb_presetConfig->currentItem();
+ config->m_numConfigs = sb_numConfigs->value();
+}
+
+void MainConfig::slotChangeNumConfigs( int newnumber )
+{
+
+ WifiConfig* config = WifiConfig::instance();
+
+ if ( config->m_numConfigs < newnumber ) // number of configs raised
+ {
+ int diff = newnumber - config->m_numConfigs;
+ ( (KCMWifi*)parentWidget() )->addConfigTab( diff, false );
+ }
+ else // number of configs lowered
+ {
+ int diff = config->m_numConfigs - newnumber;
+ ( (KCMWifi*)parentWidget() )->delConfigTab( diff );
+ }
+}
+
+void MainConfig::registerConfig( int number )
+{
+ if ( number >= KCMWifi::vendorBase )
+ {
+ cmb_presetConfig->insertItem( i18n( "Vendor %1" ).arg( number-KCMWifi::vendorBase+1 ) );
+ }
+ else
+ {
+ WifiConfig* config = WifiConfig::instance();
+ cmb_presetConfig->insertItem( i18n( "Config %1" ).arg( number ), number-1 );
+ }
+}
+
+void MainConfig::unregisterConfig( int number )
+{
+ cmb_presetConfig->removeItem( number-1 );
+}
+
+#include "mainconfig.moc"
diff --git a/wifi/kcmwifi/mainconfig.h b/wifi/kcmwifi/mainconfig.h
new file mode 100644
index 00000000..83b014ac
--- /dev/null
+++ b/wifi/kcmwifi/mainconfig.h
@@ -0,0 +1,51 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+ (C) 2001-2004 by Stefan Winter <mail@stefan-winter.de>
+
+ 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.
+
+ This program 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef CONFIGMAIN_H
+#define CONFIGMAIN_H
+
+#include "mainconfigbase.h"
+
+class KProcIO;
+
+class MainConfig : public MainConfigBase
+{
+ Q_OBJECT
+
+ public:
+ MainConfig( QWidget *parent, const char *name=0 );
+ ~MainConfig() {}
+
+ void load();
+ void save();
+
+ void registerConfig( int number );
+ void unregisterConfig( int number );
+
+ signals:
+ void changed();
+ void activateClicked();
+
+ protected slots:
+ void slotChangeNumConfigs( int newnumber );
+};
+
+#endif // CONFIGMAIN_H
+
diff --git a/wifi/kcmwifi/mainconfigbase.ui b/wifi/kcmwifi/mainconfigbase.ui
new file mode 100644
index 00000000..f7ecef24
--- /dev/null
+++ b/wifi/kcmwifi/mainconfigbase.ui
@@ -0,0 +1,166 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>MainConfigBase</class>
+<widget class="QWidget">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>581</width>
+ <height>135</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>grpGeneralSettings</cstring>
+ </property>
+ <property name="title">
+ <string>General Settings</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="KPushButton" row="1" column="2" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>pb_activate</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Activate</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Applies the configuration to the interface.</string>
+ </property>
+ </widget>
+ <spacer row="2" column="2" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>spacerrow2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>146</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>lb_numConfig</cstring>
+ </property>
+ <property name="text">
+ <string>Number of configurations:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Modifies the number of configurations that this tool should provide.</string>
+ </property>
+ </widget>
+ <widget class="KComboBox" row="1" column="1">
+ <property name="name">
+ <cstring>cmb_presetConfig</cstring>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Selects which of the configurations is to be applied.</string>
+ </property>
+ </widget>
+ <widget class="KIntNumInput" row="2" column="1">
+ <property name="name">
+ <cstring>sb_numConfigs</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minValue">
+ <number>1</number>
+ </property>
+ <property name="maxValue">
+ <number>10</number>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>lb_config</cstring>
+ </property>
+ <property name="text">
+ <string>Configuration to load:</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Selects which of the configurations is to be applied.</string>
+ </property>
+ </widget>
+ <spacer row="0" column="3" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>spacer1</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>106</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="4">
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>66</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>cb_usePreset</cstring>
+ </property>
+ <property name="text">
+ <string>Load preset &amp;configuration on startup</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>When this box is checked, your settings are applied when you launch the Control Center module.</string>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>kpushbutton.h</includehint>
+ <includehint>kcombobox.h</includehint>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/wifi/kcmwifi/vendorconfig.cpp b/wifi/kcmwifi/vendorconfig.cpp
new file mode 100644
index 00000000..b237f4cc
--- /dev/null
+++ b/wifi/kcmwifi/vendorconfig.cpp
@@ -0,0 +1,271 @@
+/*
+ Copyright (C) 2005 Stefan Winter <swinter@kde.org>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcombobox.h>
+#include <qcheckbox.h>
+
+#include <kdebug.h>
+#include <klineedit.h>
+#include <kcombobox.h>
+
+#include "kcmwifi.h"
+#include "vendorconfig.h"
+#include "ifconfigpage.h"
+#include "configcrypto.h"
+#include "wificonfig.h"
+
+VendorConfig::VendorConfig ( KCMWifi * caller )
+{
+ m_caller = caller;
+ m_totalConfigs = 0;
+}
+
+void
+VendorConfig::initAll ( )
+{
+ initSuSE_92plus ( );
+ initDebian ( );
+}
+
+void
+VendorConfig::initSuSE_92plus ( )
+{
+
+ /* configurations are located in /etc/sysconfig/network/ifcfg-wlan*
+ there can be more than one if there are multiple wireless interfaces */
+
+ QDir filelist ( "/etc/sysconfig/network", "ifcfg-wlan*" );
+ QStringList files = filelist.entryList ( );
+
+ if ( files.count() == 0 )
+ {
+ return;
+ }
+
+ for ( QStringList::Iterator it = files.begin ( ); it != files.end ( ); ++it )
+ {
+ if ( ( ( *it ).endsWith ( "~" ) ) == false )
+ { // ignore backup files
+ QFile configfile ( QString ( "/etc/sysconfig/network/%1" ).arg ( *it ) );
+ configfile.open ( IO_ReadOnly );
+ kdDebug ( ) << "Opened SuSE configuration file " << configfile.name ( ) << "\n";
+
+ IfConfigPage *newtab = m_caller->addConfigTab ( 1, true );
+ /* this config tab gets automagically stored in an array, the first
+ starting at position vendorBase. We need to construct its power
+ and crypto settings and therefore need the array index, which is
+ vendorBase+m_totalConfigs. It is _essential_ that m_totalConfigs
+ is incremented _after_ successful setup of each given config. */
+
+ // init crypto entries
+ ConfigCrypto *crypto = new ConfigCrypto ( 0, "ConfigCrypto" );
+ WifiConfig *config = WifiConfig::instance ( );
+ IfConfig & ifconfig = config->m_ifConfig[KCMWifi::vendorBase + m_totalConfigs];
+
+ QString buffer;
+ while ( configfile.readLine ( buffer, 255 ) != -1 )
+ {
+ // lines look like WIRELESS_ESSID='bla', so split it into WIRELESS_ESSID and bla
+ QString var = buffer.section ( '=', 0, 0 );
+ QString value = buffer.section ( '=', 1, 1 );
+ value = value.mid ( 1, value.length ( ) - 3 );
+ kdDebug ( ) << "Variable " << var << " contains " << value << ".\n";
+ // evaluate the meaningful lines
+ if ( var == "WIRELESS_ESSID" )
+ {
+ newtab->le_networkName->setText ( value );
+ }
+ else if ( var == "WIRELESS_MODE" )
+ {
+ newtab->cmb_wifiMode->setCurrentItem ( IfConfig::convertToWifiModeFromString ( value ) );
+ }
+ else if ( var == "WIRELESS_BITRATE" )
+ {
+ newtab->cmb_speed->setCurrentItem ( IfConfig::convertToSpeedFromString ( value ) );
+ }
+ else if ( var == "WIRELESS_DEFAULT_KEY" )
+ {
+ crypto->cmb_activeKey->setCurrentItem ( value.toInt ( ) );
+ }
+ else if ( var == "WIRELESS_AUTH_MODE" )
+ {
+ if ( value == "shared" || value == "sharedkey" || value == "restricted" )
+ {
+ crypto->rb_restrictMode->setChecked ( true );
+ newtab->cb_useCrypto->setChecked ( true );
+ }
+ if ( value == "open" || value == "opensystem" || value == "" )
+ {
+ crypto->rb_openMode->setChecked ( true );
+ newtab->cb_useCrypto->setChecked ( false );
+ }
+ }
+ else if ( var == "WIRELESS_KEY_0" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key1->setText ( value );
+ }
+ else if ( var == "WIRELESS_KEY_1" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key2->setText ( value );
+ }
+ else if ( var == "WIRELESS_KEY_2" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key3->setText ( value );
+ }
+ else if ( var == "WIRELESS_KEY_3" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key4->setText ( value );
+ }
+ }
+ newtab->save ( );
+ crypto->save ( ifconfig );
+ configfile.close ( );
+ m_totalConfigs++;
+ }
+ }
+
+}
+
+void
+VendorConfig::initDebian ( )
+{
+ QFile configfile ( QString ( "/etc/network/interfaces" ) );
+ if ( !configfile.open ( IO_ReadOnly ) )
+ {
+ return;
+ }
+
+ kdDebug ( ) << "Opened Debian configuration file " << configfile.name ( ) << "\n";
+
+ IfConfigPage *newtab = m_caller->addConfigTab ( 1, true );
+/* this config tab gets automagically stored in an array, the first
+starting at position vendorBase. We need to construct its power
+and crypto settings and therefore need the array index, which is
+vendorBase+m_totalConfigs. It is _essential_ that m_totalConfigs
+is incremented _after_ successful setup of each given config. */
+// init crypto entries
+ ConfigCrypto *crypto = new ConfigCrypto ( 0, "ConfigCrypto" );
+ WifiConfig *config = WifiConfig::instance ( );
+ IfConfig & ifconfig = config->m_ifConfig[KCMWifi::vendorBase + m_totalConfigs];
+
+ QString buffer;
+ while ( configfile.readLine ( buffer, 255 ) != -1 )
+ {
+// lines look like wireless-defaultkey bla, so split it into wireless-defaultkey and bla
+ QString var = buffer.section ( ' ', 0, 0 );
+ QString value = buffer.section ( ' ', 1, 1 );
+ kdDebug ( ) << "Variable " << var << " contains " << value << ".\n";
+// evaluate the meaningful lines
+ if ( var.startsWith ( "wireless", false ) )
+ {
+ if ( var.endsWith ( "essid" ) )
+ {
+ newtab->le_networkName->setText ( value );
+ }
+
+ else if ( var.endsWith ( "mode" ) && !var.contains ( "key" ) )
+
+ {
+ newtab->cmb_wifiMode->setCurrentItem ( IfConfig::convertToWifiModeFromString ( value ) );
+ }
+
+ else if ( var.endsWith ( "rate" ) )
+
+ {
+ newtab->cmb_speed->setCurrentItem ( IfConfig::convertToSpeedFromString ( value ) );
+ }
+
+ else if ( var.contains ( "key" ) )
+ {
+// Could be any of key, key1, key2, ..., or defaultkey
+ if ( var.contains ( "default" ) )
+ {
+ crypto->cmb_activeKey->setCurrentItem ( value.toInt ( ) );
+ }
+
+ else if ( var.contains ( "mode" ) )
+ {
+ if ( value == "shared" || value == "sharedkey" || value == "restricted" )
+ {
+ crypto->rb_restrictMode->setChecked ( true );
+ newtab->cb_useCrypto->setChecked ( true );
+ }
+ }
+
+ else if ( value == "open" || value == "opensystem" || value == "" )
+ {
+ crypto->rb_openMode->setChecked ( true );
+ newtab->cb_useCrypto->setChecked ( false );
+ }
+
+ else if ( var.right ( 1 ) == "1" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key1->setText ( value );
+ }
+
+ else if ( var.right ( 1 ) == "2" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key2->setText ( value );
+ }
+
+ else if ( var.right ( 1 ) == "3" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key3->setText ( value );
+ }
+
+ else if ( var.right ( 1 ) == "4" )
+ {
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key4->setText ( value );
+ }
+
+ else
+ {
+// does not contain a number. It's the only one.
+ if ( value.startsWith ( "s:" ) )
+ value = value.right ( value.length ( ) - 2 );
+ crypto->le_key1->setText ( value );
+ }
+ }
+ }
+ }
+ newtab->save ( );
+ crypto->save ( ifconfig );
+ configfile.close ( );
+ m_totalConfigs++;
+}
diff --git a/wifi/kcmwifi/vendorconfig.h b/wifi/kcmwifi/vendorconfig.h
new file mode 100644
index 00000000..e9a8a9a2
--- /dev/null
+++ b/wifi/kcmwifi/vendorconfig.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2005 Stefan Winter <swinter@kde.org>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef VENDORCONFIG_H
+#define VENDORCONFIG_H
+
+class KCMWifi;
+
+class VendorConfig {
+ public:
+ VendorConfig( KCMWifi* caller);
+ void initAll ();
+ void initSuSE_92plus ();
+ void initDebian ();
+ protected:
+ KCMWifi* m_caller;
+ int m_totalConfigs;
+};
+
+#endif // VENDORCONFIG_H
diff --git a/wifi/kcmwifi/wificonfig.cpp b/wifi/kcmwifi/wificonfig.cpp
new file mode 100644
index 00000000..c5bafba3
--- /dev/null
+++ b/wifi/kcmwifi/wificonfig.cpp
@@ -0,0 +1,369 @@
+/*
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#include <qfile.h>
+
+#include <ksimpleconfig.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kprocio.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+
+#include "wificonfig.h"
+
+static QStringList speedList;
+static QStringList wifiModeList;
+static QStringList cryptoModeList;
+static QStringList powerModeList;
+
+Key::Key()
+{
+}
+
+Key::Key( const QString &key )
+{
+ setKey( key );
+}
+
+QString Key::rawKey() const
+{
+ QString s = m_key;
+
+ if ( isValid( m_key ) >= STRING_64 )
+ s = "s:" + s;
+
+ return s;
+}
+
+void Key::setKey( const QString &key )
+{
+ m_key = key;
+}
+
+KeyStates Key::isValid ( QString keyCandidate )
+{
+ if (keyCandidate.isEmpty()) {
+ kdDebug() << "Ignoring key: empty.\n";
+ return EMPTY;
+ }
+
+ if (keyCandidate.length() == 5)
+ return STRING_64;
+
+ if (keyCandidate.length() == 10)
+ return HEX_64;
+
+ if (keyCandidate.length() == 13)
+ return STRING_128;
+
+ if (keyCandidate.length() == 26)
+ return HEX_128;
+
+ if (keyCandidate.length() == 32)
+ return STRING_256;
+
+ if (keyCandidate.length() == 64)
+ return HEX_256;
+
+ return INVALID;
+}
+
+IfConfig::IfConfig()
+{
+
+ // initialise all config items...
+
+ m_networkName = "";
+ m_interface = "";
+ m_wifiMode = Managed;
+ m_speed = AUTO;
+ m_runScript = false;
+ m_connectScript = "";
+
+ m_useCrypto = false;
+ m_cryptoMode = Open;
+ m_activeKey = 1;
+ for (int i=0; i<4; i++)
+ m_keys[ i ].setKey( "" );
+
+ m_pmEnabled = false;
+ m_pmMode = AllPackets;
+ m_sleepTimeout = 1;
+ m_wakeupPeriod = 1;
+
+ speedList << "Auto" << "1M" << "2M" << "5.5M" << "6M" << "9M" << "11M" << "12M" << "18M" << "24M" << "36M" << "48M" << "54M";
+ wifiModeList << "Ad-Hoc" << "Managed" << "Repeater"
+ << "Master" << "Secondary";
+ cryptoModeList << "Open" << "Restricted";
+ powerModeList << "All" << "UnicastOnly" << "MulticastOnly";
+}
+
+void IfConfig::load( KConfig *config, int i )
+{
+ QString entry;
+
+ QString group = QString( "Configuration %1" ).arg( i+1 );
+ config->setGroup( group );
+
+ m_networkName = config->readEntry( "NetworkName" );
+ m_interface = config->readEntry( "InterfaceName" );
+
+ entry = config->readEntry( "WifiMode", "Managed" );
+ wifimodeFromString( entry );
+ entry = config->readEntry( "Speed", "Auto" );
+ speedFromString( entry );
+ m_runScript = config->readBoolEntry( "RunScript", false );
+ m_connectScript = config->readEntry( "ScriptName" );
+
+ m_useCrypto = config->readBoolEntry( "UseCrypto", false );
+ entry = config->readEntry( "CryptoMode", "Open" );
+ cryptomodeFromString( entry );
+ m_activeKey = config->readNumEntry( "ActiveKey", 1 );
+ m_keys[ 0 ].setKey( config->readEntry( "Key1" ) );
+ m_keys[ 1 ].setKey( config->readEntry( "Key2" ) );
+ m_keys[ 2 ].setKey( config->readEntry( "Key3" ) );
+ m_keys[ 3 ].setKey( config->readEntry( "Key4" ) );
+
+ m_pmEnabled = config->readBoolEntry( "PMEnabled", false );
+ entry = config->readEntry( "PMMode", "All" );
+ powermodeFromString( entry );
+ m_sleepTimeout = config->readNumEntry( "SleepTimeout", 30 );
+ m_wakeupPeriod = config->readNumEntry( "WakeupPeriod", 20 );
+}
+
+void IfConfig::save( KConfig *config, int i )
+{
+ QString group = QString( "Configuration %1" ).arg( i+1 );
+ config->setGroup( group );
+
+ config->writeEntry( "NetworkName", m_networkName );
+ config->writeEntry( "InterfaceName", m_interface );
+ config->writeEntry( "WifiMode", wifimodeAsString() );
+ config->writeEntry( "Speed", speedAsString() );
+ config->writeEntry( "RunScript", m_runScript );
+ config->writeEntry( "ScriptName", m_connectScript );
+
+ config->writeEntry( "UseCrypto", m_useCrypto );
+ config->writeEntry( "CryptoMode", cryptomodeAsString() );
+ config->writeEntry( "ActiveKey", m_activeKey );
+ config->writeEntry( "Key1", m_keys[ 0 ].key() );
+ config->writeEntry( "Key2", m_keys[ 1 ].key() );
+ config->writeEntry( "Key3", m_keys[ 2 ].key() );
+ config->writeEntry( "Key4", m_keys[ 3 ].key() );
+
+ config->writeEntry( "PMEnabled", m_pmEnabled );
+ config->writeEntry( "PMMode", powermodeAsString() );
+ config->writeEntry( "SleepTimeout", m_sleepTimeout );
+ config->writeEntry( "WakeupPeriod", m_wakeupPeriod );
+}
+
+WifiConfig *WifiConfig::m_instance = 0;
+
+WifiConfig *WifiConfig::instance()
+{
+ if ( m_instance == 0 )
+ m_instance = new WifiConfig();
+
+ return m_instance;
+}
+
+WifiConfig::WifiConfig()
+{
+ //m_config = KGlobal::config();
+ m_config = new KSimpleConfig( "kcmwifirc" );
+
+ load();
+}
+
+WifiConfig::~WifiConfig()
+{
+ delete m_config;
+}
+
+void WifiConfig::load()
+{
+ m_config->setGroup( "General" );
+
+ m_usePreset = m_config->readBoolEntry( "UsePreset", false );
+ m_presetConfig = m_config->readNumEntry( "PresetConfig", 1 )-1;
+ m_numConfigs = m_config->readNumEntry( "NumberConfigs", 4 );
+
+ for ( int i=0; i<m_numConfigs; ++i )
+ {
+ m_ifConfig[ i ].load( m_config, i );
+ }
+}
+
+void WifiConfig::save()
+{
+ m_config->setGroup( "General" );
+
+ m_config->writeEntry( "UsePreset", m_usePreset );
+ m_config->writeEntry( "PresetConfig", m_presetConfig+1 );
+ m_config->writeEntry( "NumberConfigs", m_numConfigs );
+
+ for ( int i=0; i<m_numConfigs; ++i )
+ {
+ m_ifConfig[ i ].save( m_config, i );
+ }
+
+ m_config->sync();
+}
+
+QString WifiConfig::autoDetectInterface()
+{
+ m_detectedInterface.truncate( 0 );
+
+ QFile procFile ( "/proc/net/dev" );
+
+ if ( !procFile.open( IO_ReadOnly ) )
+ {
+ KMessageBox::sorry( 0,
+ i18n( "Unable to autodetect wireless interface." ) );
+ return m_detectedInterface;
+ }
+
+ QStringList list;
+ QString line;
+
+ while ( !procFile.atEnd() )
+ {
+ procFile.readLine( line, 9999 );
+ if ( line.find( ":" ) > 0 )
+ {
+ line.truncate ( line.find( ":" ) );
+ list.append( line.stripWhiteSpace() );
+ }
+ }
+
+ procFile.close();
+
+ if ( list.empty() )
+ {
+ KMessageBox::sorry( 0,
+ i18n( "Unable to autodetect wireless interface." ) );
+ return m_detectedInterface;
+ }
+
+ for ( QStringList::Iterator it = list.begin (); it != list.end (); ++it )
+ {
+ if ( ((*it).contains("wifi"))==0 ) { // if the name is wifiX, ignore
+ KProcIO test;
+ test << "iwconfig";
+ test << *it;
+ connect( &test, SIGNAL( readReady( KProcIO * ) ),
+ this, SLOT( slotTestInterface( KProcIO * ) ) );
+ test.start ( KProcess::Block );
+ }
+ }
+
+ if ( m_detectedInterface.isEmpty() )
+ {
+ KMessageBox::sorry( 0,
+ i18n( "Unable to autodetect wireless interface." ) );
+ return m_detectedInterface;
+ }
+
+ return m_detectedInterface;
+}
+
+void WifiConfig::slotTestInterface( KProcIO *proc )
+{
+ QString output;
+ proc->readln( output );
+ if ( output.find ( "no wireless extensions" ) == -1 )
+ {
+ output.truncate( output.find ( " " ) );
+ m_detectedInterface = output.stripWhiteSpace();
+ }
+}
+
+QString IfConfig::speedAsString()
+{
+ return speedList[ m_speed ];
+}
+
+QString IfConfig::wifimodeAsString()
+{
+ return wifiModeList[ m_wifiMode ];
+}
+
+QString IfConfig::cryptomodeAsString()
+{
+ return cryptoModeList[ m_cryptoMode ];
+}
+
+QString IfConfig::powermodeAsString()
+{
+ return powerModeList[ m_pmMode ];
+}
+
+int IfConfig::activeKeyId()
+{
+ return m_activeKey;
+}
+
+Key IfConfig::activeKey()
+{
+ return m_keys[ m_activeKey-1 ];
+}
+
+IfConfig::Speed IfConfig::convertToSpeedFromString( const QString &s )
+{
+ return ( IfConfig::Speed )speedList.findIndex( s );
+}
+
+void IfConfig::speedFromString( const QString &s )
+{
+ m_speed = convertToSpeedFromString( s );
+}
+
+IfConfig::WifiMode IfConfig::convertToWifiModeFromString( const QString &s )
+{
+ return ( IfConfig::WifiMode )wifiModeList.findIndex( s );
+}
+
+void IfConfig::wifimodeFromString( const QString &s )
+{
+ m_wifiMode = convertToWifiModeFromString( s );
+}
+
+IfConfig::CryptoMode IfConfig::convertToCryptoModeFromString( const QString &s )
+{
+ return ( IfConfig::CryptoMode )cryptoModeList.findIndex( s );
+}
+
+void IfConfig::cryptomodeFromString( const QString &s )
+{
+ m_cryptoMode = convertToCryptoModeFromString( s );
+}
+
+IfConfig::PowerMode IfConfig::convertToPowerModeFromString( const QString &s )
+{
+ return ( IfConfig::PowerMode )powerModeList.findIndex( s );
+}
+
+void IfConfig::powermodeFromString( const QString &s )
+{
+ m_pmMode = convertToPowerModeFromString( s );
+}
+
+#include "wificonfig.moc"
+
diff --git a/wifi/kcmwifi/wificonfig.h b/wifi/kcmwifi/wificonfig.h
new file mode 100644
index 00000000..452732b2
--- /dev/null
+++ b/wifi/kcmwifi/wificonfig.h
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2004 Nadeem Hasan <nhasan@kde.org>
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef WIFICONFIG_H
+#define WIFICONFIG_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+
+#include "kcmwifi.h"
+
+class KConfig;
+class KSimpleConfig;
+
+enum KeyStates { EMPTY=0, INVALID=1, HEX_64=2, HEX_128=3, HEX_256=4, STRING_64=5, STRING_128=6, STRING_256=7 };
+
+class Key
+{
+ public:
+ Key( const QString &key );
+ Key();
+
+ QString key() const { return m_key; }
+ static KeyStates isValid( QString keyCandidate );
+ QString rawKey() const;
+
+ void setKey( const QString &key );
+
+ protected:
+ QString m_key;
+};
+
+typedef QValueList<Key> KeyList;
+
+class IfConfig
+{
+ public:
+ IfConfig();
+
+ enum Speed { AUTO=0, M1, M2, M55, M6, M9, M11, M12, M18, M24, M36, M48, M54 };
+ enum WifiMode { AdHoc=0, Managed, Repeater, Master, Secondary };
+ enum PowerMode { AllPackets=0, UnicastOnly, MulticastOnly };
+ enum CryptoMode { Open=0, Restricted };
+
+ void load( KConfig *config, int i );
+ void save( KConfig *config, int i );
+
+ QString speedAsString();
+ QString wifimodeAsString();
+ QString cryptomodeAsString();
+ QString powermodeAsString();
+ int activeKeyId();
+ Key activeKey();
+
+ static Speed convertToSpeedFromString( const QString &s );
+ static WifiMode convertToWifiModeFromString( const QString &s );
+ static PowerMode convertToPowerModeFromString( const QString &s );
+ static CryptoMode convertToCryptoModeFromString( const QString &s );
+
+ void speedFromString( const QString &s );
+ void wifimodeFromString( const QString &s );
+ void powermodeFromString( const QString &s );
+ void cryptomodeFromString( const QString &s );
+
+ QString m_networkName;
+ QString m_interface;
+ WifiMode m_wifiMode;
+ Speed m_speed;
+ bool m_runScript;
+ QString m_connectScript;
+
+ bool m_useCrypto;
+ CryptoMode m_cryptoMode;
+ int m_activeKey;
+ Key m_keys[ 4 ];
+
+ bool m_pmEnabled;
+ PowerMode m_pmMode;
+ int m_sleepTimeout;
+ int m_wakeupPeriod;
+};
+
+class WifiConfig : QObject
+{
+ Q_OBJECT
+ public:
+ static WifiConfig *instance();
+ QString autoDetectInterface();
+ ~WifiConfig();
+
+ void load();
+ void save();
+
+ IfConfig m_ifConfig[ KCMWifi::vendorBase+5 ];
+ bool m_usePreset;
+ int m_presetConfig;
+ int m_numConfigs;
+
+ private slots:
+ void slotTestInterface( KProcIO *proc );
+
+ private:
+ WifiConfig();
+
+ KSimpleConfig *m_config;
+ static WifiConfig *m_instance;
+ QString m_detectedInterface;
+};
+
+#endif // WIFICONFIG_H
diff --git a/wifi/kwifimanager.cpp b/wifi/kwifimanager.cpp
new file mode 100644
index 00000000..5e64a683
--- /dev/null
+++ b/wifi/kwifimanager.cpp
@@ -0,0 +1,637 @@
+/***************************************************************************
+ kwifimanager.cpp - description
+ -------------------
+ begin : Sam Apr 7 11:44:20 CEST 2001
+ copyright : (C) 2001 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 files for C++
+#include <iostream>
+#include <fstream>
+
+// include files for QT
+#include <qdatetime.h>
+#include <qdir.h>
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qimage.h>
+#include <qbitmap.h>
+#include <qtimer.h>
+#include <qpushbutton.h>
+
+// include files for KDE
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kmenubar.h>
+#include <klocale.h>
+#include <kstdaction.h>
+#include <kstandarddirs.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+#include <kmessagebox.h>
+#include <ksystemtray.h>
+#include <kprocess.h>
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <kdialog.h>
+
+// application specific includes
+#include "asusled.h"
+#include "networkscanning.h"
+#include "kwifimanager.h"
+#include "interface_wireless.h"
+#include "interface_wireless_wirelessextensions.h"
+#include "status.h"
+#include "locator.h"
+#include "picture.h"
+#include "strength.h"
+#include "speed.h"
+#include "statistics.h"
+
+#define ID_STATUS_MSG 1
+
+QStringList APs;
+bool useAlternateStrengthCalc;
+
+KWiFiManagerApp::KWiFiManagerApp (QWidget *, const char *name):
+DCOPObject("dcop_interface"), KMainWindow (0, name), device(0), m_shuttingDown(false), m_iconSize( 22 )
+{
+ statistik = 0;
+ disablePower = 0;
+ useAlternateStrengthCalc = false;
+ init_whois_db ();
+
+ QStringList *ignoreInterfaces;
+ if (kapp->isRestored())
+ ignoreInterfaces = new QStringList();
+ else
+ ignoreInterfaces = new QStringList( usedInterfacesList() );
+
+ // substitute the following line with a different interface if required
+ device = new Interface_wireless_wirelessextensions (ignoreInterfaces);
+
+ //This setting gets here from the kdeglobals config
+ KConfig* config = kapp->config();
+ if (config->hasGroup("System Tray"))
+ config->setGroup("System Tray");
+ m_iconSize=config->readNumEntry("systrayIconWidth", 22);
+
+ /* initView updates the main widget, system tray icon and the ASUS led if applicable
+ it needs to called before initActions() because that uses some icons that get
+ initialized by initView() */
+
+ initView ();
+
+ initActions ();
+
+#if WIRELESS_EXT > 9
+ fileDisableRadio->setEnabled (true);
+#else
+ fileDisableRadio->setEnabled (false);
+#endif
+
+ counter = new QTimer (this);
+ connect (counter, SIGNAL (timeout ()), device, SLOT (poll_device_info()));
+ counter->start (1000);
+
+ tricorder_trigger = new QTimer ();
+ connect (tricorder_trigger, SIGNAL (timeout ()), this,
+ SLOT (tricorder_beep ()));
+ connect (trayicon, SIGNAL (quitSelected ()), this, SLOT (slotFileQuit ()));
+ slotToggleTric(); //set to value saved by KConfig
+
+ setAutoSaveSettings();
+ kapp->setMainWidget(this);
+}
+
+
+void
+KWiFiManagerApp::slotChangeWindowCaption ()
+{
+ if (device->get_interface_name().isEmpty())
+ {
+ this->setCaption (i18n ("No Interface"));
+ }
+ else
+ {
+ this->setCaption (i18n ("Interface %1").
+ arg (device->get_interface_name ()));
+ }
+}
+
+KWiFiManagerApp::~KWiFiManagerApp ()
+{
+ kdDebug () <<
+ "Thank you for using KWiFiManager. You have made a simple program very happy." << endl;
+ delete statistik;
+ delete tricorder_trigger;
+}
+
+void
+KWiFiManagerApp::init_whois_db ()
+{
+ KStandardDirs finder;
+ QString kdedir =
+ finder.findResourceDir ("data", "kwifimanager/locations/NOWHERE.loc");
+ kdedir += "kwifimanager/locations/";
+ QDir *locations = new QDir (kdedir);
+ if (!locations->exists ())
+ kdDebug () <<
+ "A grave error occurred while searching for files with AccessPoint information." << endl;
+ QStringList *entries = new QStringList (locations->entryList ());
+ QString content;
+ QString content2;
+ for (QStringList::Iterator it = entries->begin (); it != entries->end ();
+ it++)
+ {
+ if ((*it) != "." && (*it) != "..")
+ {
+ QFile datei (kdedir + (*it));
+ datei.open (IO_ReadOnly);
+ while (datei.readLine (content, 255) != -1)
+ {
+ QString temp (content);
+ content.truncate (content.find (' '));
+ content = content.upper ();
+ content2 = temp.right (temp.length () - temp.find (' ') - 1);
+ content2.truncate (content2.length () - 1);
+ APs << content << content2;
+ }
+ datei.close ();
+ }
+ }
+ delete entries;
+ delete locations;
+}
+
+void
+KWiFiManagerApp::slotLogESSID (QString essid)
+{
+ QFile log_file( locateLocal( "data", "kwifimanager/kwifimanager.log", true ) );
+ kdDebug() << "Log file is " << log_file.name() << endl;
+ if (!log_file.open (IO_WriteOnly | IO_Append))
+ kdDebug () << "Logging failed!" << endl;
+ QTextStream stream (&log_file);
+ stream << QDateTime::currentDateTime().toString ().latin1 ();
+ stream << " Entering network: " << essid.latin1() << endl;
+ log_file.close ();
+}
+
+void
+KWiFiManagerApp::slotTXPowerChanged ()
+{
+ fileDisableRadio->setChecked( device->get_txpower_disabled() );
+}
+
+void
+KWiFiManagerApp::slotChangeTrayIcon ()
+{
+ QPixmap* ICON;
+ int sig, noi, str;
+ double freq = 0.;
+ bool validdata = device->get_current_quality (sig, noi, str);
+ if ( settingsUseAlternateCalc->isChecked() ) str = sig - noi;
+ int mode;
+ QLabel sstrength2( i18n("N/A"), 0, 0 );
+
+ if ( ( device->get_txpower_disabled() ) || ( !device->get_device_freq ( freq ) ) )
+ {
+ ICON = strength->NOT_CON_OFFLINE_ADHOC;
+ if (led->state == true)
+ led->Off ();
+ }
+ else if (device->get_mode(mode) && mode == 1)
+ {
+ ICON = strength->NOT_CON_OFFLINE_ADHOC;
+ if (led->state == true)
+ led->Off ();
+ }
+ else if (validdata && (str > 45))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->EXCELLENT;
+ if (led->state == false)
+ led->On ();
+ }
+ else if (validdata && (str > 35))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->EXCELLENT;
+ if (led->state == false)
+ led->On ();
+ }
+ else if (validdata && (str > 25))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->EXCELLENT;
+ if (led->state == false)
+ led->On ();
+ }
+ else if (validdata && (str > 15))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->GOOD;
+ if (led->state == false)
+ led->On ();
+ }
+ else if (validdata && (str > 5))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->MARGINAL;
+ if (led->state == false)
+ led->On ();
+ }
+ else if (validdata && (str > 0))
+ {
+ sstrength2.setText( QString::number(str) );
+ ICON = strength->OOR_MINIMUM;
+ if (led->state == true)
+ led->Off ();
+ }
+ else if (((str == 0) && (mode == 2)) || (!validdata))
+ {
+ sstrength2.setText( QString::fromLatin1("0") );
+ ICON = strength->OOR_DOWN;
+ if (led->state == true)
+ led->Off ();
+ }
+ else
+ {
+ ICON = strength->NOT_CON_OFFLINE_ADHOC;
+ if (led->state == true)
+ led->Off ();
+ }
+
+ QPixmap temp (m_iconSize+8, m_iconSize+8);
+ QPainter bla (&temp);
+ bla.fillRect (0, 0, m_iconSize+8, m_iconSize+8, backgroundBrush ());
+ bla.drawPixmap ( QRect(1, 4, m_iconSize+2,m_iconSize+2), *ICON );
+
+ if (showStrength) {
+ static QFont labelfont ( "Helvetica", 10 );
+
+ labelfont.setStyleHint( QFont::SansSerif );
+ labelfont.setStretch( QFont::Condensed );
+ labelfont.setBold( true );
+ sstrength2.setFont( labelfont );
+ sstrength2.setFixedSize( sstrength2.sizeHint() );
+ }
+ QPixmap labeltemp = QPixmap::grabWidget( &sstrength2 );
+ labeltemp.setMask( labeltemp.createHeuristicMask() );
+ bla.drawPixmap (0, 0, labeltemp );
+ temp.setMask (temp.createHeuristicMask() );
+ trayicon->setPixmap ( temp );
+ this->setIcon( temp );
+
+ QToolTip::add( trayicon, "SSID: "+device->get_essid() );
+}
+
+void
+KWiFiManagerApp::initActions ()
+{
+ KConfig* config = kapp->config();
+ if (config->hasGroup("General"))
+ config->setGroup("General");
+ fileDisableRadio = new KToggleAction (i18n ("&Disable Radio"), 0, this,
+ SLOT (slotDisableRadio ()), actionCollection (), "disable_radio");
+ fileDisableRadio->setChecked( false );
+
+ settingsUseAlternateCalc =
+ new KToggleAction (i18n ("&Use Alternate Strength Calculation"), 0, this,
+ SLOT (slotToggleStrengthCalc ()), actionCollection (), "use_alt_calculation");
+
+ settingsUseAlternateCalc->setChecked( config->readBoolEntry("useAlternateStrengthCalculation") );
+ slotToggleStrengthCalc(); //set to value saved by KConfig
+
+ settingsShowStatsNoise =
+ new KToggleAction (i18n ("Show &Noise Graph in Statistics Window"), 0, this,
+ SLOT (slotShowStatsNoise ()), actionCollection (), "show_stats_noise");
+
+ settingsShowStatsNoise->setChecked( config->readBoolEntry("showStatsNoise") );
+ slotShowStatsNoise(); //set to value saved by KConfig
+
+ settingsShowStrengthNumber = new KToggleAction (i18n ("&Show Strength Number in System Tray"), 0, this,
+ SLOT (slotToggleShowStrengthNumber ()), actionCollection (), "show_strength_number_in_tray");
+ settingsShowStrengthNumber->setChecked( config->readBoolEntry("showStrengthNumberInTray") );
+ slotToggleShowStrengthNumber (); //set to value saved by KConfig
+
+ KStdAction::quit (this, SLOT (slotFileQuit ()), actionCollection ());
+
+ new KAction (i18n ("Configuration &Editor..."), 0, this,
+ SLOT (slotStartConfigEditor ()), actionCollection (), "configuration_editor");
+ new KAction (i18n ("Connection &Statistics"), 0, this,
+ SLOT (slotStartStatViewer ()), actionCollection (), "connection_statistics");
+ settingsAcousticScanning = new KToggleAction (i18n ("&Acoustic Scanning"), 0, this,
+ SLOT (slotToggleTric ()),
+ actionCollection (), "acoustic_scanning");
+ settingsAcousticScanning->setChecked( config->readBoolEntry("acousticScanning") );
+#ifdef WITHOUT_ARTS
+ settingsAcousticScanning->setEnabled( false);
+#endif
+ settingsStayInSystrayOnClose = new KToggleAction (i18n ("Stay in System &Tray on Close"), 0, this,
+ SLOT (slotToggleStayInSystray()),
+ actionCollection (), "stay_in_systray_on_close");
+ settingsStayInSystrayOnClose->setChecked( config->readBoolEntry("stayInSystrayOnClose") );
+ createGUI();
+}
+
+
+void
+KWiFiManagerApp::initView ()
+{
+
+ led = new Led ();
+
+ trayicon = new KSystemTray (this);
+ trayicon->show ();
+
+ view = new QWidget (this, "mainwidget");
+ view->setBackgroundMode (PaletteBackground);
+ setCentralWidget (view);
+ QGridLayout *zentrallayout = new QGridLayout (view);
+ zentrallayout->setMargin( KDialog::marginHint() );
+ speedmeter = new Speed (view, device);
+ status = new Status (view, device);
+ location = new Locator (view, device);
+ strength = new Strength (view, device);
+ pictogram = new Picture (view, device);
+ scan = new QPushButton (i18n("Scan for &Networks..."),view);
+ slotChangeWindowCaption ();
+ QToolTip::add (strength, i18n ("The current signal strength"));
+ QToolTip::add (speedmeter, i18n ("The speed at which the wireless LAN card is operating"));
+ QToolTip::add (status, i18n ("Detailed connection status"));
+ QToolTip::add (scan, i18n ("Performs a scan to discover the networks you can log into"));
+ pictogram->setMinimumSize (pictogram->mySizeHint());
+ strength->setMinimumSize (strength->mySizeHint());
+ speedmeter->setMinimumHeight (60);
+ status->setMinimumHeight (60);
+ zentrallayout->addWidget (pictogram, 0, 0);
+ zentrallayout->addWidget (strength, 1, 0);
+ zentrallayout->addWidget (speedmeter, 0, 1);
+ zentrallayout->addMultiCellWidget (status, 1, 2, 1, 1);
+ zentrallayout->addWidget (scan, 2, 0);
+ zentrallayout->addMultiCellWidget (location, 3, 3, 0, 1, Qt::AlignLeft);
+ connect (device, SIGNAL (interfaceChanged ()), this, SLOT (slotChangeWindowCaption ()));
+ connect (device, SIGNAL (strengthChanged ()), this, SLOT (slotChangeTrayIcon ()));
+ connect (device, SIGNAL (strengthChanged ()), strength, SLOT (repaint ()));
+ connect (device, SIGNAL (statusChanged ()), status, SLOT (repaint ()));
+ connect (device, SIGNAL (speedChanged ()), speedmeter, SLOT (repaint ()));
+ connect (device, SIGNAL (modeChanged ()), pictogram, SLOT (repaint ()));
+ connect (device, SIGNAL (essidChanged (QString)), this, SLOT (slotLogESSID (QString)));
+ connect (device, SIGNAL (essidChanged (QString)), location, SLOT (repaint ()));
+ connect (device, SIGNAL (statusChanged ()), location, SLOT (repaint ()));
+ connect (device, SIGNAL (txPowerChanged ()), this, SLOT (slotTXPowerChanged ()));
+ connect (device, SIGNAL (txPowerChanged ()), this, SLOT (slotChangeTrayIcon ()));
+ connect (device, SIGNAL (txPowerChanged ()), pictogram, SLOT (repaint ()));
+ connect (device, SIGNAL (txPowerChanged ()), strength, SLOT (repaint ()));
+ connect (scan, SIGNAL (clicked()), this, SLOT (slotNetworkScan()));
+}
+
+void
+KWiFiManagerApp::slotToggleShowStrengthNumber ()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry( "showStrengthNumberInTray", settingsShowStrengthNumber->isChecked() );
+ if (settingsShowStrengthNumber->isChecked()) { showStrength = true; }
+ else { showStrength = false; }
+ slotChangeTrayIcon ();
+}
+
+void
+KWiFiManagerApp::slotNetworkScan ()
+{
+ scan->setText( i18n( "Scan in progress..." ) );
+ scan->setEnabled ( false );
+ scanwidget = new NetworkScanning(device);
+ scan->setText( i18n( "Scan for &Networks..." ) );
+ scan->setEnabled ( true );
+}
+
+
+void
+KWiFiManagerApp::slotStartStatViewer ()
+{
+ delete statistik;
+ statistik = new Statistics (device, showStatsNoise);
+ statistik->setFixedSize (590, 300);
+ connect(device,SIGNAL(statsUpdated()),statistik,SLOT(repaint()));
+ statistik->show ();
+}
+
+void
+KWiFiManagerApp::slotDisableRadio ()
+{
+ fileDisableRadio->setChecked( device->get_txpower_disabled() );
+
+ if ( disablePower != 0 && disablePower->isRunning() ) {
+ return;
+ }
+ QString interface = device->get_interface_name();
+
+ if (interface.isEmpty()) {
+ return;
+ }
+
+ QString onOrOff;
+ if ( fileDisableRadio->isChecked() ) {
+ onOrOff = "on";
+ } else {
+ onOrOff = "off";
+ }
+
+ disablePower = new KProcess;
+
+ // FIXME my Cisco Aironet 350 has two interfaces eth1 and wifi1,
+ // kwifimanager works on wifi 1 where txpower does not work -- jriddell
+
+ //*disablePower << "kdesu" << "iwconfig" << "eth1" << "txpower" << onOrOff;
+
+ *disablePower << "kdesu" << "iwconfig" << interface << "txpower" << onOrOff;
+ connect( disablePower, SIGNAL(processExited(KProcess*)), this, SLOT(slotDisablePowerProcessExited()) );
+ disablePower->start(KProcess::NotifyOnExit);
+}
+
+void
+KWiFiManagerApp::slotDisablePowerProcessExited()
+{
+ if (! disablePower->normalExit() || disablePower->exitStatus() != 0) {
+ delete disablePower;
+ disablePower = 0;
+ return;
+ }
+
+ if ( fileDisableRadio->isChecked() ) {
+ fileDisableRadio->setChecked(false);
+ } else {
+ fileDisableRadio->setChecked(true);
+ }
+
+ delete disablePower;
+ disablePower = 0;
+
+ fileDisableRadio->setChecked( device->get_txpower_disabled() );
+}
+
+void
+KWiFiManagerApp::slotToggleTric ()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry( "acousticScanning", settingsAcousticScanning->isChecked() );
+ if (settingsAcousticScanning->isChecked())
+ {
+ tricorder_trigger->start (250);
+ }
+ else
+ {
+ tricorder_trigger->stop ();
+ }
+}
+
+void KWiFiManagerApp::slotToggleStrengthCalc ()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry( "useAlternateStrengthCalculation", settingsUseAlternateCalc->isChecked() );
+ useAlternateStrengthCalc = settingsUseAlternateCalc->isChecked();
+}
+
+void KWiFiManagerApp::slotToggleStayInSystray ()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry( "stayInSystrayOnClose", settingsStayInSystrayOnClose->isChecked() );
+}
+
+void
+KWiFiManagerApp::tricorder_beep ()
+{
+ int sig, noi, qual;
+ device->get_current_quality (sig, noi, qual);
+#ifndef WITHOUT_ARTS
+ sinus_wave (150.0 + qual * 20);
+#endif
+ if (qual == 0)
+ {
+ tricorder_trigger->changeInterval (2000);
+ }
+ else if (qual < 10)
+ {
+ tricorder_trigger->changeInterval (1000);
+ }
+ else
+ {
+ tricorder_trigger->changeInterval (500);
+ }
+}
+
+void
+KWiFiManagerApp::slotShowStatsNoise ()
+{
+ KConfig* config = kapp->config();
+ config->setGroup("General");
+ config->writeEntry( "showStatsNoise", settingsShowStatsNoise->isChecked() );
+ showStatsNoise = settingsShowStatsNoise->isChecked();
+}
+
+void
+KWiFiManagerApp::slotFileQuit ()
+{
+ m_shuttingDown = true;
+ kapp->quit();
+}
+
+void
+KWiFiManagerApp::slotStartConfigEditor ()
+{
+ KProcess startConf;
+ startConf << "kdesu" << locate("exe", "kcmshell") << "kcmwifi";
+ startConf.start (KProcess::DontCare);
+}
+
+void KWiFiManagerApp::saveProperties( KConfig* conf)
+{
+ m_startDocked = !isVisible();
+ conf->writeEntry( "startDocked", m_startDocked );
+ conf->writeEntry( "wifidevice", device->get_interface_name() );
+ conf->sync();
+}
+
+void KWiFiManagerApp::readProperties( KConfig* conf)
+{
+ m_startDocked = conf->readBoolEntry( "startDocked", false );
+ if (m_startDocked)
+ show();
+ else
+ hide();
+
+ QString dev = conf->readEntry( "wifidevice" );
+ if (!dev.isEmpty())
+ device->setActiveDevice(dev);
+}
+
+bool
+KWiFiManagerApp::queryClose()
+{
+ if(!m_shuttingDown &&
+ !kapp->sessionSaving() &&
+ settingsStayInSystrayOnClose->isChecked() ) {
+ hide();
+ return false;
+ }
+ return true;
+}
+
+bool
+KWiFiManagerApp::queryExit()
+{
+ // Save settings if auto-save is enabled, and settings have changed
+ if ( settingsDirty() && autoSaveSettings() )
+ saveAutoSaveSettings();
+ return true;
+}
+
+// List of network interfaces used by all running kwifimanager applications
+QStringList usedInterfacesList()
+{
+ // Register with DCOP
+ DCOPClient *client = kapp->dcopClient();
+ client->registerAs( "kwifimanager" );
+ client->setDefaultObject( "dcop_interface" );
+
+ // shamelessly stolen from kdelibs/kio/booksmarks/kbookmarkimporter_crash.cc
+ QStringList ignoreInterfaces;
+ QCStringList apps = client->registeredApplications();
+ for ( QCStringList::Iterator it = apps.begin(); it != apps.end(); ++it )
+ {
+ QCString &clientId = *it;
+ if ( qstrncmp(clientId, "kwifimanager", 12) != 0 )
+ continue;
+
+ DCOPRef ask( clientId, "dcop_interface" );
+ DCOPReply reply = ask.call( "interface()" );
+ QString interface;
+ if ( reply.isValid() ) {
+ QString i = reply;
+ interface = i;
+ }
+ if (!interface.isEmpty())
+ ignoreInterfaces.append(interface);
+ }
+ return ignoreInterfaces;
+}
+
+#include "kwifimanager.moc"
diff --git a/wifi/kwifimanager.desktop b/wifi/kwifimanager.desktop
new file mode 100644
index 00000000..533e4d2b
--- /dev/null
+++ b/wifi/kwifimanager.desktop
@@ -0,0 +1,134 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Exec=kwifimanager -caption "%c" %i %m
+Icon=kwifimanager
+DocPath=kwifimanager/index.html
+Comment=A wireless LAN connection monitor
+Comment[ar]=مراقب أداء وصلة الشبكة المحلية اللاسلكبة
+Comment[bg]=Мониторинг на безжичната локална мрежа
+Comment[bn]=à¦à¦•à¦Ÿà¦¿ ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ লà§à¦¯à¦¾à¦¨ সংযোগ মনিটর
+Comment[bs]=Nadzor bežiÄne LAN konekcije
+Comment[ca]=Un monitor per a connexions LAN sense fils
+Comment[cs]=Monitor bezdrátového připojení
+Comment[cy]=Arsylwydd cysylltiad LAN di-wifr
+Comment[da]=En trådløs LAN-forbindelsesovervåger
+Comment[de]=Monitor zur Ãœberwachung von drahtlosen LAN-Verbindungen
+Comment[el]=ΠαÏακολοÏθηση ασÏÏματης σÏνδεσης δικτÏου
+Comment[es]=Monitor de conexiones de redes inalámbricas
+Comment[et]=Traadita kohtvõrgu ühenduse monitor
+Comment[eu]=Kablerik gabeko LAN konexio monitorea
+Comment[fa]=یک نمایشگر اتصال شبکۀ داخلی بی‌سیم
+Comment[fi]=Langattoman lähiverkon yhteyden valvonta
+Comment[fr]=Un écran de contrôle des connexions locales sans fil
+Comment[gl]=Unha ferramente para a monitorización de redes inalámbricas
+Comment[he]=צג בקרה לחיבורי רשת LAN ×לחוטית
+Comment[hi]=à¤à¤• बेतार लैन कनेकà¥à¤¶à¤¨ मॉनीटर
+Comment[hr]=Nadglednik bežiÄne LAN veze
+Comment[hu]=Kapcsolatkezelő vezeték nélküli hálózatokhoz
+Comment[is]=Vaktskjár á þráðlaust staðarneti
+Comment[it]=Controllo connessione rete LAN senza fili
+Comment[ja]=ワイヤレス LAN 接続モニタ
+Comment[ka]=უკáƒáƒœáƒ”ლრLAN კáƒáƒ•áƒ¨áƒ˜áƒ áƒ˜áƒ¡ მáƒáƒœáƒ˜áƒ¢áƒáƒ áƒ˜
+Comment[kk]=СымÑыз жергілікті желіге қоÑылымды қадағалау
+Comment[km]=កម្មវិធី​ážáŸ’ážšáž½ážáž–áž·áž“áž·ážáŸ’យ​ការ​ážáž—្ជាប់​បណ្ដាញ​មូលដ្ឋាន​ឥážážáŸ’សែ
+Comment[lt]=Bevielio tinklo ryšio stebėjimas
+Comment[mk]=Монитор на поврзувањата на безжичната локална мрежа
+Comment[ms]=Pemerhati sambungan LAN tanpa wayar
+Comment[nb]=Passer på det trådløse nettverket
+Comment[nds]=En Kieker för Funknettwark-Verbinnen
+Comment[ne]=à¤à¤‰à¤Ÿà¤¾ तारविहिन LAN जडान मोनिटर
+Comment[nl]=Een programma voor het observeren van een draadloos lokaal netwerk
+Comment[nn]=Passar på det trådlause nettverket
+Comment[pa]=ਇੱਕ ਬੇਤਾਰ LAN ਕà©à¨¨à©ˆà¨•à¨¸à¨¼à¨¨ ਨਿਗਰਾਨੀ
+Comment[pl]=Program monitorujący połączenia w sieci bezprzewodowej
+Comment[pt]=Um monitor de ligações na rede local sem-fios ('wireless')
+Comment[pt_BR]=Um monitor de conexões LAN sem fio
+Comment[ru]=Монитор ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð¹ радиоÑети
+Comment[se]=Jođaskeahttes LAN-oktavuohta goziheaddji
+Comment[sk]=Sledovanie bezdrôtového pripojenia LAN
+Comment[sl]=Nadzornik brezžiÄne omrežne povezave
+Comment[sr]=Ðадгледач бежичне LAN везе
+Comment[sr@Latn]=NadgledaÄ bežiÄne LAN veze
+Comment[sv]=Övervakning av trådlöst lokalt nätverk
+Comment[ta]=கமà¯à®ªà®¿à®¯à®¿à®²à¯à®²à®¾ LAN இணைபà¯à®ªà¯ கணà¯à®•à®¾à®£à®¿
+Comment[tg]=Дидабони пайваÑтшавии беÑими шабакаи маҳаллӣ
+Comment[tr]=Bir kablosuz yerel ağ bağlantı gözlemcisi
+Comment[uk]=Монітор з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ñ— радіомережі
+Comment[zh_CN]=无线局域网连接监视器
+Comment[zh_HK]=無線網絡連線監視器
+Comment[zh_TW]=無線網路監視器
+Terminal=false
+Name=KWiFiManager
+Name[ar]=إدارة KWiFi
+Name[bn]=কে-ওয়াইফাই-মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦°
+Name[cs]=Správce WIFI
+Name[cy]=KRheolyddDiWi
+Name[de]=KWiFi-Verwaltung
+Name[et]=KWiFi haldur
+Name[gl]=Configuración de KWiFI
+Name[hi]=के-विफि-मैनेजर
+Name[ja]=KWiFi マãƒãƒ¼ã‚¸ãƒ£
+Name[nb]=KWiFiHÃ¥ndterer
+Name[ne]=KWifi पà¥à¤°à¤¬à¤¨à¥à¤§à¤•
+Name[nn]=KWiFiHandterar
+Name[pa]=KWiFi ਮੈਨੇਜਰ
+Name[tr]=KWifi Yöneticisi
+GenericName=Wireless LAN Manager
+GenericName[ar]=إدارة الشبكة المحلية اللاسلكية
+GenericName[bg]=Безжична мрежа
+GenericName[bn]=ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ লà§à¦¯à¦¾à¦¨ মà§à¦¯à¦¾à¦¨à§‡à¦œà¦¾à¦°
+GenericName[br]=Merour ar rouedad hep neud
+GenericName[bs]=Upravitelj bežÄnim LANom
+GenericName[ca]=Gestor LAN sense fils
+GenericName[cs]=Správce bezdrátové LAN
+GenericName[cy]=Rheolydd LAN Di-Wifr
+GenericName[da]=Håndtering af trådløst netværk
+GenericName[de]=Verwaltung für drahtlose LAN-Verbindungen
+GenericName[el]=ΔιαχειÏιστής ασÏÏματου δικτÏου
+GenericName[eo]=Administrilo por sendrata loka reto
+GenericName[es]=Gestor de red inalámbrica
+GenericName[et]=Traadita võrgu haldur
+GenericName[eu]=Kablerik gabeko LAN kudeatzailea
+GenericName[fa]=مدیر شبکۀ داخلی بی‌سیم
+GenericName[fi]=Langattoman lähiverkon hallinta
+GenericName[fr]=Gestionnaire de réseau sans fil
+GenericName[ga]=Bainisteoir Ghréasáin gan sreang
+GenericName[he]=מנהל LAN ×לחוטי
+GenericName[hi]= बेतार लैन मैनेजर
+GenericName[hr]=Upravitelj bežiÄnog LAN-a
+GenericName[hu]=WiFi-kezelő
+GenericName[is]=Fylgist með staðarneti
+GenericName[it]=Gestione LAN senza fili
+GenericName[ja]=ワイヤレス LAN マãƒãƒ¼ã‚¸ãƒ£
+GenericName[ka]=უკáƒáƒ‘ელრLAN მმáƒáƒ áƒ—ველი
+GenericName[kk]=СымÑыз желі менеджері
+GenericName[km]=កម្មវិធី​គ្រប់គ្រង​បណ្ដាញ​មូលដ្ឋាន​ឥážâ€‹ážáŸ’សែ
+GenericName[lt]=Bevielio ryšio kontrolės centras
+GenericName[mk]=Менаџер на безжичната локална мрежа
+GenericName[ms]=Pengurus LAN Tanpa Wayar
+GenericName[nb]=Håndterer for trådløse nettverk
+GenericName[nds]=Funknettwark-Pleger
+GenericName[ne]=तारविहिन LAN पà¥à¤°à¤¬à¤¨à¥à¤§à¤•
+GenericName[nl]=Draadloos netwerkbeheer
+GenericName[nn]=Handsamar for trådlaust nettverk
+GenericName[pa]=ਬੇਤਾਰ LAN ਮੈਨੇਜਰ
+GenericName[pl]=Nadzorca sieci bezprzewodowej
+GenericName[pt]=Gestor de Rede Sem Fios
+GenericName[pt_BR]=Gerenciador de Wireless na LAN
+GenericName[ru]=Менеджер локальной радиоÑети
+GenericName[sk]=Správca bezdrôtovej siete LAN
+GenericName[sl]=Upravljalec brezžiÄnega omrežja
+GenericName[sr]=Управник бежичног LAN-а
+GenericName[sr@Latn]=Upravnik bežiÄnog LAN-a
+GenericName[sv]=Trådlöst nätverkshantering
+GenericName[ta]=கமà¯à®ªà®¿à®¯à®¿à®²à¯à®²à®¾ LAN மேளாளரà¯
+GenericName[tg]=Мудири Беноқили Шабакаи Маҳаллӣ
+GenericName[tr]=Kablosuz Yerel Ağ Yöneticisi
+GenericName[uk]=Менеджер локальної радіомережі
+GenericName[uz]=Simsiz tarmoq boshqaruvchisi
+GenericName[uz@cyrillic]=СимÑиз тармоқ бошқарувчиÑи
+GenericName[zh_CN]=无线局域网管ç†å™¨
+GenericName[zh_HK]=無線網絡管ç†å“¡
+GenericName[zh_TW]=無線網路管ç†è€…
+Categories=Qt;KDE;Network;X-KDE-More;System;Monitor;
diff --git a/wifi/kwifimanager.h b/wifi/kwifimanager.h
new file mode 100644
index 00000000..cdf8c726
--- /dev/null
+++ b/wifi/kwifimanager.h
@@ -0,0 +1,143 @@
+/***************************************************************************
+ kwifimanager.h - a graphical interface for wireless LAN cards
+ -------------------
+ begin : Sam Apr 7 11:44:20 CEST 2001
+ copyright : (C) 2001 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 KWIFIMANAGER_H
+#define KWIFIMANAGER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// include files for Qt
+class QLabel;
+class QTimer;
+class QPixmap;
+class QPushButton;
+class QStringList;
+
+// include files for KDE
+#include <kmainwindow.h>
+class KSystemTray;
+class KToggleAction;
+class KProcess;
+
+// application specific includes
+#include "interface_wireless.h"
+#include "interface_dcop.h"
+class Strength;
+class Status;
+class Picture;
+class Locator;
+class Speed;
+class Statistics;
+class NetworkScanning;
+class Led;
+
+extern QStringList usedInterfacesList();
+
+enum speedlevels
+{ AUTO, M1, M2, M55, M11 };
+enum packetmode
+{ UNI, MULTI, BOTH };
+enum cryptomodes
+{ OPEN, RESTRICTED };
+
+struct configuration_data
+{
+ QString net_name[5];
+ QString disp_name[5];
+ bool managed[5];
+ bool crypto_enabled[5];
+ int active_crypto_key[5];
+ QString active_key_string[5];
+ QString crypto1[5], crypto2[5], crypto3[5], crypto4[5];
+ bool crypto_is_string1[5], crypto_is_string2[5], crypto_is_string3[5],
+ crypto_is_string4[5];
+ cryptomodes crypto_mode[5];
+ bool pm_enabled[5];
+ int sleep_time[5];
+ QString sleep_time_string[5];
+ int wake_time[5];
+ QString wake_time_string[5];
+ speedlevels bitrate[5];
+ packetmode packet[5];
+};
+
+class KWiFiManagerApp:public KMainWindow, virtual public dcop_interface
+{
+Q_OBJECT public:
+ KWiFiManagerApp (QWidget * parent = 0, const char * name = 0);
+ ~KWiFiManagerApp ();
+ QString interface () const { return device ? device->get_interface_name() : QString::null; };
+ public:
+ bool startDocked() { return m_startDocked; }
+ public slots:
+ void slotToggleShowStrengthNumber ();
+ void slotDisableRadio ();
+ void slotFileQuit ();
+ void slotStartConfigEditor ();
+ void slotStartStatViewer ();
+ void slotToggleTric ();
+ void slotToggleStrengthCalc ();
+ void slotToggleStayInSystray ();
+ void slotChangeTrayIcon ();
+ void slotChangeWindowCaption ();
+ void slotLogESSID (QString essid);
+ void slotTXPowerChanged ();
+ void slotShowStatsNoise ();
+ void slotNetworkScan();
+ void tricorder_beep ();
+ void slotDisablePowerProcessExited();
+ virtual bool queryClose();
+ virtual bool queryExit();
+ virtual void readProperties( KConfig* );
+ virtual void saveProperties( KConfig* );
+private:
+ void initActions ();
+ void initView ();
+ Speed *speedmeter;
+ QBoxLayout *bla;
+ QLabel *profil;
+ Status *status;
+ Strength *strength;
+ Picture *pictogram;
+ Locator *location;
+ NetworkScanning* scanwidget;
+ QWidget *view;
+ Statistics *statistik;
+ KToggleAction *fileDisableRadio;
+ KToggleAction *settingsUseAlternateCalc;
+ KToggleAction *settingsStayInSystrayOnClose;
+ KToggleAction *settingsAcousticScanning;
+ KToggleAction *settingsShowStatsNoise;
+ KToggleAction *settingsShowStrengthNumber;
+ KSystemTray *trayicon;
+ void init_whois_db ();
+ QTimer *tricorder_trigger, *counter;
+ QPixmap *pixmap;
+ Interface_wireless *device;
+ QPushButton * scan;
+ Led *led;
+ KProcess* disablePower;
+ bool showStrength;
+ bool showStatsNoise;
+ bool m_startDocked;
+ bool m_shuttingDown;
+ int m_iconSize;
+};
+
+#endif /* KWIFIMANAGER_H */
diff --git a/wifi/kwifimanagerui.rc b/wifi/kwifimanagerui.rc
new file mode 100644
index 00000000..a9408485
--- /dev/null
+++ b/wifi/kwifimanagerui.rc
@@ -0,0 +1,19 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="kwifimanager">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="disable_radio"/>
+ <Action name="connection_statistics"/>
+ </Menu>
+ <Menu name="config"><text>&amp;Settings</text>
+ <Action name="use_alt_calculation"/>
+ <Action name="acoustic_scanning"/>
+ <Action name="show_stats_noise"/>
+ <Separator/>
+ <Action name="stay_in_systray_on_close"/>
+ <Action name="show_strength_number_in_tray"/>
+ <Separator/>
+ <Action name="configuration_editor"/>
+ </Menu>
+</MenuBar>
+</kpartgui>
diff --git a/wifi/kwireless/Makefile.am b/wifi/kwireless/Makefile.am
new file mode 100644
index 00000000..d5b4fcf7
--- /dev/null
+++ b/wifi/kwireless/Makefile.am
@@ -0,0 +1,17 @@
+INCLUDES= $(all_includes)
+METASOURCES = AUTO
+
+lib_LTLIBRARIES = libkwireless.la
+
+libkwireless_la_SOURCES = kwireless.cpp \
+ kwirelesswidget.cpp \
+ linuxwirelesswidget.cpp \
+ propertytablebase.ui propertytable.cpp
+libkwireless_la_LDFLAGS = -module -avoid-version $(all_libraries)
+libkwireless_la_LIBADD = -liw $(LIB_KDEUI)
+
+kwireless_DATA = kwireless.desktop
+kwirelessdir = $(kde_datadir)/kicker/applets
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kwireless.pot
diff --git a/wifi/kwireless/kwireless.cpp b/wifi/kwireless/kwireless.cpp
new file mode 100644
index 00000000..ff584603
--- /dev/null
+++ b/wifi/kwireless/kwireless.cpp
@@ -0,0 +1,89 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#include <kglobal.h>
+#include <klocale.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kmessagebox.h>
+
+#include "kwireless.h"
+#include "kwirelesswidget.h"
+
+
+KWireLess::KWireLess(const QString& configFile, Type type,
+ int actions, QWidget *parent, const char *name)
+ : KPanelApplet(configFile, type, actions, parent, name)
+{
+ ksConfig = config();
+
+ widget = KWireLessWidget::makeWireLessWidget(this);
+ widget->show();
+}
+
+
+KWireLess::~KWireLess()
+{
+}
+
+
+void KWireLess::about()
+{
+ KMessageBox::information
+ (0,
+ i18n("<qt><b>KWireLess</b><br>"
+ "Displays information about wireless network devices.<br />"
+ "KWireLess is licensed to you under the terms of the GPL.<br />"
+ "<i>(C) 2003 Mirko Boehm</i></qt>"),
+ i18n("About KWireLess"));
+}
+
+
+void KWireLess::help()
+{
+// KMessageBox::information(0, i18n("This is a help box"));
+}
+
+
+void KWireLess::preferences()
+{
+// KMessageBox::information(0, i18n("This is a preferences box"));
+}
+
+int KWireLess::widthForHeight(int) const
+{
+ widget->setMode(KWireLessWidget::Vertical);
+ return widget->preferredWidth();
+}
+
+int KWireLess::heightForWidth(int) const
+{
+ widget->setMode(KWireLessWidget::Horizontal);
+ return widget->preferredHeight();
+}
+
+void KWireLess::resizeEvent(QResizeEvent *)
+{
+ widget->setGeometry(0, 0, width(), height());
+}
+
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init( QWidget *parent, const QString configFile)
+ {
+ KGlobal::locale()->insertCatalogue("kwireless");
+ return new KWireLess(configFile, KPanelApplet::Normal,
+ KPanelApplet::About,
+ // | KPanelApplet::Help | KPanelApplet::Preferences,
+ parent, "kwireless");
+ }
+}
+
+#include "kwireless.moc"
diff --git a/wifi/kwireless/kwireless.desktop b/wifi/kwireless/kwireless.desktop
new file mode 100644
index 00000000..8564c742
--- /dev/null
+++ b/wifi/kwireless/kwireless.desktop
@@ -0,0 +1,118 @@
+[Desktop Entry]
+Comment=Displays information about wireless network devices
+Comment[ar]=عرض معلومات أجهزة الشبكة الاسلكية
+Comment[bg]=Данни за уÑтройÑтвата поддържащи безжичната мрежа
+Comment[bn]=ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ নেটওয়ারà§à¦• ডিভাইস সমà§à¦¬à¦¨à§à¦§à§‡ তথà§à¦¯ পà§à¦°à¦¦à¦°à§à¦¶à¦¨ করে
+Comment[bs]=Prikazuje informacije o bežiÄnim mrežnim ureÄ‘ajima
+Comment[ca]=Mostra informació quant als dispositius de la xarxa sense fils
+Comment[cs]=Zobrazuje informace o bezdrátových síťových zařízeních
+Comment[cy]=Dangos gwybodaeth ynglyn â dyfeisiau rhwydwaith di-wifr
+Comment[da]=Giver oplysninger om trådløse netværksenheder
+Comment[de]=Zeigt Informationen zu Geräten für drahtlose Netzwerke an
+Comment[el]=Εμφανίζει πληÏοφοÏίες για συσκευές ασÏÏματου δικτÏου
+Comment[es]=Muestra información acerca de los dispositivos de la red inalámbrica
+Comment[et]=Näitab infot traadita võrgu seadmete kohta
+Comment[eu]=Kablerik gabeko sare gailuen informazioa erakusten du
+Comment[fa]=اطلاعاتی دربارۀ دستگاههای شبکۀ بی‌سیم را نمایش می‌دهد
+Comment[fi]=Tietoja langattoman verkon laitteista
+Comment[fr]=Affiche des informations sur les périphériques réseau sans fil
+Comment[gl]=Información sobre os dispositivos de rede inalámbirca
+Comment[he]=מציג מידע ×•× ×ª×•× ×™× ×¢×œ התקני רשת ×לחוטית
+Comment[hi]=वायरलेस नेटवरà¥à¤• औज़ार के बारे में जानकारी दिखाà¤
+Comment[hr]=Prikazuje informacije o bežiÄnim mrežnim ureÄ‘ajima
+Comment[hu]=Megjeleníti a vezeték nélküli hálózatok csatolókártyáinak jellemzőit
+Comment[is]=Sýnir upplýsingar um tæki fyrir þráðlausar nettengingar
+Comment[it]=Visualizza informazioni sui dispositivi di rete senza fili
+Comment[ja]=ワイヤレスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ãƒ‡ãƒã‚¤ã‚¹ã®æƒ…報を表示
+Comment[ka]=უკáƒáƒ‘ელრქსელების მáƒáƒ¬áƒ§áƒáƒ‘ილáƒáƒ‘ების შესáƒáƒ®áƒ”ბ ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ
+Comment[kk]=СымÑыз желі құрылғылар туралы мәлімет береді
+Comment[km]=បង្ហាញ​ពáŸážáŸŒáž˜áž¶áž“​អំពី​ឧបករណáŸâ€‹áž”ណ្ដាញ​ឥážâ€‹ážáŸ’សែ
+Comment[lt]=Pateikia informaciją apie bevielio ryšio įrangą
+Comment[mk]=Прикажува информации за уредите на безжичната мрежа
+Comment[ms]=Memaparkan maklumat berkaitan peranti jaringan tanpa wayar
+Comment[nb]=Viser informasjon om enheter for trådløst nettverk
+Comment[nds]=Wiest Informatschonen över Funknettwark-Reedschappen
+Comment[ne]=तारविहिन सञà¥à¤œà¤¾à¤² यनà¥à¤¤à¥à¤°à¤¬à¤¾à¤°à¥‡ सूचना पà¥à¤°à¤¦à¤°à¥à¤¶à¤¨ गरà¥à¤¦à¤›
+Comment[nl]=Toont informatie over draadloze netwerkapparaten
+Comment[nn]=Viser informasjon om einingar for trådlause nettverk
+Comment[pl]=Wyświetla informacje o bezprzewodowych kartach sieciowych
+Comment[pt]=Mostra informações sobre os dispositivos de rede sem-fios ('wireless')
+Comment[pt_BR]=Mostra informação sobre dispositivos de rede sem fio
+Comment[ru]=Отображает ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ беÑпроводных Ñетевых уÑтройÑтвах
+Comment[se]=Čájeha dieđuid jođaskeahttes fierpmádatovttadagaid birra
+Comment[sk]=Zobrazí informácie o zariadeniach bezdrôtovej siete
+Comment[sl]=Prikaže informacije o napravah brezžiÄnega omrežja
+Comment[sr]=Приказује информације о бежичним мрежним уређајима
+Comment[sr@Latn]=Prikazuje informacije o bežiÄnim mrežnim ureÄ‘ajima
+Comment[sv]=Visar information om enheter i trådlösa nätverk
+Comment[ta]=கமà¯à®ªà®¿ இலà¯à®²à®¾ செயà¯à®¤à®¿à®¤à¯à®¤à¯Šà®Ÿà®°à¯à®ªà¯ சாதனம௠பறà¯à®±à®¿à®¯ தகவலைகà¯à®•à®¾à®Ÿà¯à®Ÿà¯
+Comment[tg]=Ðхборотро оиди даÑтгоҳҳои беноқили шабакавӣ намоиш медиҳад
+Comment[tr]=Kablosuz ağ aygıtları hakkında gösterim bilgisi
+Comment[uk]=Відображає інформацію про приÑтрої радіомережі
+Comment[uz]=Simsiz tarmoq uskunalari haqida maʼlumotni koʻrsatish
+Comment[uz@cyrillic]=СимÑиз тармоқ уÑкуналари ҳақида маълумотни кўрÑатиш
+Comment[zh_CN]=显示关于无线网络设备的信æ¯
+Comment[zh_HK]=顯示有關無線網絡è£ç½®çš„資訊
+Comment[zh_TW]=顯示無線網路è£ç½®çš„資訊
+Name=Wireless Network Information
+Name[ar]=معلومات الشبكة الاسلكية
+Name[bg]=Данни за безжична мрежа
+Name[bn]=ওয়à§à¦¯à¦¾à¦°à¦²à§‡à¦¸ নেটওয়ারà§à¦• তথà§à¦¯
+Name[br]=Titouroù diwar-benn ar roueded hep neud
+Name[bs]=Informacije o bežiÄnoj mreži
+Name[ca]=Informació de la xarxa sense fils
+Name[cs]=Informace o bezdrátové síti
+Name[cy]=Gwybodaeth Rwydwaith Di-Wifr
+Name[da]=Information om trådløst netværk
+Name[de]=Informationen über drahtlose Netzwerke
+Name[el]=ΠληÏοφοÏίες ασÏÏματου δικτÏου
+Name[eo]=Informoj pri Sendrata Reto
+Name[es]=Información de red inalámbrica
+Name[et]=Traadita võrgu info
+Name[eu]=Kablerik gabeko sarearen informazioa
+Name[fa]=اطلاعات شبکۀ بی‌سیم
+Name[fi]=Langattoman verkon tiedot
+Name[fr]=Informations sur le réseau sans fil
+Name[ga]=Eolas faoi Ghréasán gan sreang
+Name[gl]=Información sobre a rede inalámbrica
+Name[he]=נתוני רשת ×לחוטית
+Name[hi]=वायरलेस नेटवरà¥à¤• जानकारी
+Name[hr]=Informacije o bežiÄnoj mreži
+Name[hu]=A vezeték nélküli hálózat jellemzői
+Name[is]=Upplýsingar um þráðlausa netið
+Name[it]=Informazione rete senza fili
+Name[ja]=ワイヤレスãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æƒ…å ±
+Name[ka]=ინფáƒáƒ áƒ›áƒáƒªáƒ˜áƒ უკáƒáƒ‘ელრქსელის შესáƒáƒ®áƒ”ბ
+Name[kk]=СымÑыз желі мәліметі
+Name[km]=áž–áŸážáŸŒáž˜áž¶áž“​បណ្ដាញ​ឥážâ€‹ážáŸ’សែ
+Name[lt]=Bevielio ryšio tinklo informacija
+Name[mk]=Информации за безжичната мрежа
+Name[ms]=Maklumat Jaringan Tanpa Wayar
+Name[nb]=Informasjon om trådløst nettverk
+Name[nds]=Funknettwark-Informatschonen
+Name[ne]=तारविहिन सञà¥à¤œà¤¾à¤² सूचना
+Name[nl]=Draadloos netwerk-informatie
+Name[nn]=Informasjon om trådlaust nettverk
+Name[pa]=ਬੇਤਾਰ ਨੈੱਟਵਰਕ ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o sieci bezprzewodowej
+Name[pt]=Informação da Rede Sem-Fios ('Wireless')
+Name[pt_BR]=Informação sobre Rede Sem Fio
+Name[ru]=Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ беÑпроводной Ñети
+Name[se]=Jođaskeahttes fierpmádatdieđut
+Name[sk]=Informácie o bezdrôtovej sieti
+Name[sl]=Podatki o brezžiÄnem omrežju
+Name[sr]=Информације о бежичној мрежи
+Name[sr@Latn]=Informacije o bežiÄnoj mreži
+Name[sv]=Information om trådlöst nätverk
+Name[ta]=கமà¯à®ªà®¿à®¯à®¿à®²à¯à®²à®¾ பிணைய தகவலà¯
+Name[tg]=Ðхборот оиди Шабакаи Беноқил
+Name[tr]=Kablosuz AÄŸ Bilgisi
+Name[uk]=Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ радіомережу
+Name[uz]=Simsiz tarmoq haqida maʼlumot
+Name[uz@cyrillic]=СимÑиз тармоқ ҳақида маълумот
+Name[wa]=Informåcion sol rantoele sins fyis
+Name[zh_CN]=无线网络信æ¯
+Name[zh_HK]=無線網絡資訊
+Name[zh_TW]=無線網路資訊
+Icon=kwifimanager
+X-KDE-Library=libkwireless
diff --git a/wifi/kwireless/kwireless.h b/wifi/kwireless/kwireless.h
new file mode 100644
index 00000000..5a4597af
--- /dev/null
+++ b/wifi/kwireless/kwireless.h
@@ -0,0 +1,46 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#ifndef KWIRELESS_H
+#define KWIRELESS_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <kpanelapplet.h>
+#include <qstring.h>
+#include <kconfig.h>
+
+class KWireLessWidget;
+
+class KWireLess : public KPanelApplet
+{
+ Q_OBJECT
+
+public:
+ KWireLess(const QString& configFile, Type t = Normal, int actions = 0,
+ QWidget *parent = 0, const char *name = 0);
+ ~KWireLess();
+
+ virtual int widthForHeight(int height) const;
+ virtual int heightForWidth(int width) const;
+ virtual void about();
+ virtual void help();
+ virtual void preferences();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+
+private:
+ KConfig *ksConfig;
+ KWireLessWidget *widget;
+};
+
+#endif
diff --git a/wifi/kwireless/kwirelesswidget.cpp b/wifi/kwireless/kwirelesswidget.cpp
new file mode 100644
index 00000000..3ae2ff8c
--- /dev/null
+++ b/wifi/kwireless/kwirelesswidget.cpp
@@ -0,0 +1,324 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#include <qtimer.h>
+#include <qpainter.h>
+#include <qpen.h>
+#include <qcolor.h>
+#include <qpointarray.h>
+#include <kglobalsettings.h>
+#include <kdebug.h>
+#include <kpassivepopup.h>
+#include <klocale.h>
+
+#include "kwirelesswidget.h"
+#include "propertytable.h"
+#include "linuxwirelesswidget.h"
+
+extern "C" {
+#include <math.h>
+}
+
+QPtrList<DeviceInfo> KWireLessWidget::deviceInfo;
+QMutex KWireLessWidget::mutex;
+QTimer *KWireLessWidget::timer;
+int KWireLessWidget::m_instances;
+
+DeviceInfo::DeviceInfo(QString _device, QString _essid, QString _encr,
+ float _quality, float _signal, float _noise,
+ int _bitrate)
+ : m_device(_device),
+ m_essid(_essid),
+ m_quality(_quality),
+ m_noise(_noise),
+ m_signal(_signal),
+ m_bitrate(_bitrate),
+ m_encr(_encr)
+{
+}
+
+float DeviceInfo::quality()
+{
+ return m_quality;
+}
+
+QString DeviceInfo::qualityString()
+{
+ return i18n("%1%").arg(QString::number(m_quality*100, 'f', 0));
+}
+
+float DeviceInfo::signal()
+{
+ return m_signal;
+}
+
+QString DeviceInfo::signalString()
+{
+ return i18n("%1%").arg(QString::number(m_signal*100, 'f', 0));
+}
+
+float DeviceInfo::noise()
+{
+ return m_noise ;
+}
+
+QString DeviceInfo::noiseString()
+{
+ return i18n("%1%").arg(QString::number(m_noise*100, 'f', 0));
+}
+
+const QString& DeviceInfo::device()
+{
+ return m_device;
+}
+
+const QString& DeviceInfo::essid()
+{
+ return m_essid;
+}
+
+
+QString DeviceInfo::bitrateString()
+{
+ QString bitrate;
+ QTextOStream b(&bitrate);
+ b.precision(2);
+
+ switch((int)log10(m_bitrate))
+ {
+ case 0:
+ case 1:
+ case 2:
+ b << m_bitrate << " bit/s";
+ break;
+ case 3:
+ case 4:
+ case 5:
+ b << m_bitrate/1000 << " kbit/s";
+ break;
+ case 6:
+ case 7:
+ case 8:
+ b << m_bitrate/1000000 << " Mbit/s";
+ break;
+ case 9:
+ case 10:
+ case 11:
+ b << m_bitrate/1000000000 << " Gbit/s";
+ break;
+ default:
+ b << m_bitrate << " bit/s (!)";
+ };
+ return bitrate;
+}
+
+bool DeviceInfo::usesEncryption()
+{
+ return !m_encr.isEmpty();
+}
+
+QString DeviceInfo::encrString()
+{
+ if(m_encr.isEmpty())
+ {
+ return i18n("unknown");
+ } else {
+ // we rely on the information provided (that is, it has to
+ // translated already!):
+ return m_encr;
+ }
+}
+
+KWireLessWidget::KWireLessWidget(QWidget *parent, const char* name)
+ : QWidget(parent, name),
+ mode(Horizontal),
+ frameWidth(1),
+ qualityBarWidth(6),
+ signalBarWidth(3),
+ noiseBarWidth(3)
+{
+ ++m_instances;
+ deviceInfo.setAutoDelete(true);
+ // set up the poll timer:
+ if(timer == 0)
+ { // this way, only the poll() method of the first instance will
+ // ever be called (intended behaviour):
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), SLOT(poll()));
+ // single shot, will be restartet at the end of poll ()
+ timer->start(100, true);
+ }
+}
+
+KWireLessWidget::~KWireLessWidget()
+{
+ --m_instances;
+}
+
+int KWireLessWidget::preferredHeight()
+{
+ return preferredWidth();
+}
+
+int KWireLessWidget::preferredWidth()
+{
+ return 4*frameWidth + qualityBarWidth + signalBarWidth + noiseBarWidth;
+}
+
+int KWireLessWidget::instances()
+{
+ return m_instances;
+}
+
+void KWireLessWidget::setMode(KWireLessWidget::Mode _mode)
+{
+ mode = _mode;
+}
+
+void KWireLessWidget::mousePressEvent(QMouseEvent *e)
+{
+ if( e->button() == QMouseEvent::LeftButton)
+ {
+ PropertiesDialog dialog(this);
+ connect(this, SIGNAL(updateDeviceInfo(QPtrList<DeviceInfo> *)),
+ &dialog, SLOT(update(QPtrList<DeviceInfo> *)));
+ dialog.exec();
+ }
+}
+
+void KWireLessWidget::paintEvent(QPaintEvent*)
+{
+ // WORK_TO_DO: paint other devices, too:
+ // for quick repaints, we need a buffered painter!
+ int w, h, space;
+ const int bevel = qualityBarWidth/2;
+ QPainter painter(this);
+ QPointArray points;
+ QColor color, brush;
+
+ // do stuff that does not rely on device being valid (e.g.,
+ // non-nil):
+ space = 3 * frameWidth + qualityBarWidth + signalBarWidth + noiseBarWidth+1;
+
+ if(mode == Horizontal)
+ {
+ h = space;
+ w = width();
+ } else {
+ // Vertical
+ h = height();
+ w = space;
+ }
+
+ painter.setPen(QPen(Qt::black, frameWidth));
+ painter.setBrush(KGlobalSettings::baseColor());
+ painter.drawRect(0, 0, w, h);
+
+ int x = qualityBarWidth + frameWidth;
+ mode == Vertical
+ ? painter.drawLine(x, frameWidth, x, h-frameWidth)
+ : painter.drawLine(frameWidth, x, w-frameWidth, x);
+ x += signalBarWidth + frameWidth;
+ mode == Vertical
+ ? painter.drawLine(x, frameWidth, x, h-frameWidth)
+ : painter.drawLine(frameWidth, x, w-frameWidth, x);
+
+ DeviceInfo *device = deviceInfo.getFirst();
+ if(device != 0)
+ {
+ // draw the quality bar:
+ switch((int)(device->quality()*3))
+ {
+ case 0:
+ color = Qt::darkRed;
+ brush = Qt::red;
+ break;
+ case 1:
+ color = Qt::darkYellow;
+ brush = Qt::yellow;
+ break;
+ default:
+ color = Qt::darkGreen;
+ brush = Qt::green;
+ };
+ if(mode == Vertical)
+ {
+ int position = (int)(((float)(h-2*frameWidth))*(1-device->quality())+0.5);
+
+ points.putPoints
+ (0, 4,
+ frameWidth, h - 2*frameWidth,
+ frameWidth, position + bevel/2,
+ qualityBarWidth, QMAX(position - bevel/2, frameWidth),
+ qualityBarWidth, h - 2*frameWidth);
+ } else {
+ int position = (int)(((float)(w-2*frameWidth))*device->quality()+0.5);
+
+ points.putPoints
+ (0, 4,
+ frameWidth, frameWidth,
+ QMIN(position - bevel/2, w-frameWidth), frameWidth,
+ QMIN(position + bevel/2, w-frameWidth), frameWidth+qualityBarWidth-1,
+ frameWidth, frameWidth+qualityBarWidth-1);
+ }
+ painter.setPen(QPen(color));
+ painter.setBrush(brush);
+ painter.drawPolygon(points);
+
+ // draw the signal bar:
+ painter.setPen(QPen(Qt::darkRed));
+ painter.setBrush(Qt::red);
+
+ if(mode == Vertical)
+ {
+ int x = 2 * frameWidth + qualityBarWidth;
+ int h1 = (int)(((float)(h-2*frameWidth))*(1-device->signal())+0.5);
+
+ painter.drawRect(x, h1, signalBarWidth, h - frameWidth - h1);
+ } else {
+ int x = frameWidth;
+ int y = 2 * frameWidth + qualityBarWidth;
+ int w1 = (int)(((float)(w-2*frameWidth))*device->signal()+0.5);
+
+ painter.drawRect(x, y, w1, signalBarWidth);
+ }
+ // draw the noise bar:
+ painter.setPen(QPen(Qt::gray));
+ painter.setBrush(Qt::lightGray);
+
+ if(mode == Vertical)
+ {
+ int x = 3 * frameWidth + qualityBarWidth + signalBarWidth;
+ int h1 = (int)(((float)(h-2*frameWidth))*(1-device->noise())+0.5);
+
+ painter.drawRect(x, h1, signalBarWidth, h - frameWidth - h1);
+ } else {
+ int x = frameWidth;
+ int y = 3 * frameWidth + qualityBarWidth + signalBarWidth;
+ int w1 = (int)(((float)(w-2*frameWidth))*device->noise()+0.5);
+
+ painter.drawRect(x, y, w1, signalBarWidth);
+ }
+ }
+}
+
+KWireLessWidget* KWireLessWidget::makeWireLessWidget(QWidget *parent,
+ const char *name)
+{
+#if 1
+ // defined linuxwirelesswidget
+ return new LinuxWireLessWidget(parent, name);
+#else
+#error KWireLess is not available for your OS
+#endif
+ // just to shut the compiler up:
+ parent = parent; name = name; return 0;
+}
+
+#include "kwirelesswidget.moc"
diff --git a/wifi/kwireless/kwirelesswidget.h b/wifi/kwireless/kwirelesswidget.h
new file mode 100644
index 00000000..5a374d07
--- /dev/null
+++ b/wifi/kwireless/kwirelesswidget.h
@@ -0,0 +1,103 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#ifndef KWIRELESS_WIDGET_H
+#define KWIRELESS_WIDGET_H
+
+#include <qwidget.h>
+#include <qptrlist.h>
+#include <qmutex.h>
+#include <qstringlist.h>
+
+class DeviceInfo
+{
+public:
+ DeviceInfo(QString _device=QString::null, QString _essid=QString::null,
+ QString _encr=QString::null,
+ float _quality=0, float _signal=0, float _noise=0,
+ int _bitrate=0);
+ QString bitrateString();
+ float quality();
+ QString qualityString();
+ float signal();
+ QString signalString();
+ float noise();
+ QString noiseString();
+ const QString& device();
+ const QString& essid();
+ bool usesEncryption();
+ QString encrString();
+protected:
+ QString m_device; // the device name (e.g., eth1)
+ QString m_essid; // the network name
+
+ // all this values are coefficients (values between 0 and 1):
+ float m_quality; // link quality level
+ float m_noise; // the noise level
+ float m_signal; // the signal level
+ // all these values are absolut, e.g., 11000000 = 11MBit/sec:
+ int m_bitrate; // bit rate
+ QString m_encr;
+};
+
+/** This wigdet displays the information about one wireless device.
+ It is supposed to "look good" at about any resolution (to enable
+ it to be used in the panel). */
+
+class KWireLessWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ enum Mode {
+ Horizontal,
+ Vertical
+ };
+ static KWireLessWidget* makeWireLessWidget(QWidget *parent = 0,
+ const char *name = 0);
+protected:
+ KWireLessWidget(QWidget *parent=0, const char* name=0);
+public:
+ ~KWireLessWidget();
+ void setMode(Mode);
+ // These methods return values according to the mode:
+ int preferredHeight();
+ int preferredWidth();
+ int instances();
+protected:
+ void paintEvent(QPaintEvent*);
+ void mousePressEvent(QMouseEvent *e);
+protected:
+ Mode mode;
+ int frameWidth;
+ int qualityBarWidth;
+ int signalBarWidth;
+ int noiseBarWidth;
+ // some static device information
+ // a list of DeviceInfo objects, this needs to be filled by the
+ // poll method:
+ static QPtrList<DeviceInfo> deviceInfo;
+ static QTimer *timer; // the poll timer
+ static QMutex mutex;
+ static int m_instances;
+signals:
+ void updateDeviceInfo(QPtrList<DeviceInfo> *);
+protected slots:
+ /** poll() is system dependent and needs to be implemented by
+ deriving classes.
+ poll() has to put a DeviceInfo object into deviceInfo for each
+ wireless network device on the system. It uses mutex to
+ serialize access to this objects.
+ poll() will be called by a timer. If you cannot implement a
+ KWireLessWidget for your OS this way, please contact me
+ (mirko@kde.org).
+ */
+ virtual void poll() = 0;
+};
+
+#endif // KWIRELESS_WIDGET_H
diff --git a/wifi/kwireless/linuxwirelesswidget.cpp b/wifi/kwireless/linuxwirelesswidget.cpp
new file mode 100644
index 00000000..de35b84b
--- /dev/null
+++ b/wifi/kwireless/linuxwirelesswidget.cpp
@@ -0,0 +1,227 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#include <qtooltip.h>
+#include <qtimer.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <unistd.h> // for ::close
+#include "linuxwirelesswidget.h"
+#include "config.h"
+
+extern "C" {
+#include <iwlib.h>
+// #include <linux/wireless.h>
+}
+
+int LinuxWireLessWidget::socketFD;
+QStringList LinuxWireLessWidget::deviceNames;
+
+LinuxWireLessWidget::LinuxWireLessWidget(QWidget *parent, const char* name)
+ : KWireLessWidget(parent, name),
+ m_number (-1)
+{
+}
+
+LinuxWireLessWidget::~LinuxWireLessWidget()
+{
+ if(instances() == 1)
+ { // I am the only one
+ if(socketFD != 0)
+ {
+ kdDebug() << "KWireLessWidget dtor: closing FD, over and out."
+ << endl;
+ ::close(socketFD); // prevent from using QWidget::close
+ socketFD = 0;
+ }
+ }
+}
+
+
+void LinuxWireLessWidget::poll()
+{
+ // BE AWARE: do not call this method, only the first instance is
+ // supposed to do so, and it has been taken care of!
+ // this will repoll the device names every ReEnumCountDownStart
+ // timer ticks:
+ const int ReEnumCountDownStart = 30;
+ static int ReEnumCountDown;
+ struct wireless_info info;
+ struct wireless_config config;
+ struct iwreq wrq;
+ bool updateNeeded = false;
+ bool updateToolTip = false;
+
+ // get a socket file descriptor:
+ if(socketFD == 0)
+ {
+ socketFD = iw_sockets_open();
+ }
+ if(socketFD == 0)
+ {
+ kdDebug() << "KWireLessWidget ctor: opening socket file descriptor failed."
+ << endl;
+ return;
+ }
+ // ----- query the available devices:
+ mutex.lock(); // querying the device names is protected:
+ if(ReEnumCountDown-- == 0)
+ {
+ ReEnumCountDown = ReEnumCountDownStart;
+ updateToolTip = true;
+
+ // query device information:
+ deviceNames.clear();
+ iw_enum_devices(socketFD, devEnumHandler, 0, 0);
+
+ if (m_number != (int) deviceNames.count())
+ {
+ m_number = deviceNames.count();
+ kdDebug() << "KWireLessWidget::poll: found "
+ << deviceNames.count() << " wireless "
+ << ((deviceNames.count() == 1) ? "device" : "devices")
+ << endl;
+ }
+ }
+
+ // ----- get the device information:
+ QStringList::Iterator it;
+
+ deviceInfo.clear();
+ for(it=deviceNames.begin(); it!=deviceNames.end(); ++it)
+ {
+ // I think it is OK to use this cast, since the QT docs say the
+ // returned pointer is valid until the value of the string is
+ // changed, which will not happen here (horrible cast, anyway
+ // ...):
+ char *device_c_str = (char*)(*it).latin1();
+
+ if(iw_get_basic_config(socketFD, device_c_str, &config) == -1)
+ {
+ kdDebug() << "KWireLessWidget::poll: device "
+ << *it << " does not seem to be a wireless device"
+ << endl;
+ } else {
+ // WORK_TO_DO: decide whether updates are needed or not
+ // create a DeviceInfo object and fill it:
+ QString dev, essid, encr;
+ float quality=0, signal=0, noise=0;
+ int bitrate;
+
+ dev = *it;
+ // get the bitrate:
+ if(iw_get_ext(socketFD, device_c_str, SIOCGIWRATE, &wrq) >=0)
+ {
+ info.has_bitrate = 1;
+ memcpy(&(info.bitrate), &(wrq.u.bitrate), sizeof(iwparam));
+ }
+ bitrate = info.bitrate.value;
+ // get the ranges (needed to translate the absolute values
+ // reported by the driver):
+ if(iw_get_range_info(socketFD, device_c_str, &(info.range)) >= 0)
+ {
+ info.has_range = 1;
+ }
+
+ // get the device statistics:
+#ifdef HAVE_IW_27
+ if(iw_get_stats(socketFD, device_c_str, &(info.stats), &(info.range), info.has_range)>= 0)
+#else
+ if(iw_get_stats(socketFD, device_c_str, &(info.stats)) >= 0)
+#endif
+ {
+ info.has_stats = 1;
+ // get the link quality (logic has been taken from
+ // wireless-tools, iwlib.c):
+
+ int rqn = info.range.max_qual.noise;
+ int rql = info.range.max_qual.level;
+ int rmq = info.range.max_qual.qual;
+
+ // in case the quality levels are zero, do not allow division by zero, and
+ // instead set the max. possible quality range to 255 (max of "unsigned char")
+ if (!rqn) rqn = 255;
+ if (!rql) rql = 255;
+ if (!rmq) rmq = 255;
+
+ bool isAbs; // true if values are relative tp a peak value,
+ // otherwise values are dBm
+ isAbs = (float)info.stats.qual.level > (int)info.range.max_qual.level;
+ if(isAbs)
+ {
+ noise = 1 + ((float)info.stats.qual.noise - 0x100) / rqn;
+ signal = 1 + ((float)info.stats.qual.level - 0x100) / rql;
+ } else {
+ noise = ((float)info.stats.qual.noise) / rqn;
+ signal = ((float)info.stats.qual.level) / rql;
+ }
+
+ quality = ((float)info.stats.qual.qual) / rmq;
+
+ updateNeeded = true;
+ }
+ if(config.has_essid)
+ essid = config.essid;
+ else
+ essid = i18n("<unknown>");
+
+ if (config.has_key && config.key_size>0) {
+ encr = i18n("enabled");
+ } else {
+ // non-root users don't get information about encryption status
+ encr = (getuid() == 0) ? i18n("disabled") : i18n("no information");
+ }
+
+ DeviceInfo *device = new DeviceInfo(dev, essid, encr, quality, signal,
+ noise, bitrate);
+ deviceInfo.append(device);
+ }
+ }
+ mutex.unlock();
+ if(updateNeeded)
+ {
+ emit(updateDeviceInfo(&deviceInfo));
+ repaint(false);
+ }
+ if(updateToolTip)
+ {
+ DeviceInfo *info;
+ QString text;
+
+ for(info=deviceInfo.first(); info; info=deviceInfo.next())
+ {
+ if (!text.isEmpty())
+ text.append('\n');
+ text += i18n("%1: Link Quality %2, Bitrate: %3")
+ .arg(info->device())
+ .arg(info->qualityString())
+ .arg(info->bitrateString());
+ }
+ QToolTip::add(this, text);
+
+ updateToolTip = false;
+ }
+
+ timer->start(330, true); // single shot
+}
+
+// static:
+int LinuxWireLessWidget::devEnumHandler(int skfd, char * ifname, char **, int)
+{
+ struct wireless_config config;
+
+ if(iw_get_basic_config(skfd, ifname, &config) != -1)
+ {
+ deviceNames.append(ifname);
+ }
+ return 0;
+}
+
+
+#include "linuxwirelesswidget.moc"
diff --git a/wifi/kwireless/linuxwirelesswidget.h b/wifi/kwireless/linuxwirelesswidget.h
new file mode 100644
index 00000000..3bbd085c
--- /dev/null
+++ b/wifi/kwireless/linuxwirelesswidget.h
@@ -0,0 +1,36 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#ifndef LINUXWIRELESSWIDGET_H
+#define LINUXWIRELESSWIDGET_H
+
+#include "kwirelesswidget.h"
+
+class LinuxWireLessWidget : public KWireLessWidget
+{
+ Q_OBJECT
+public:
+ LinuxWireLessWidget(QWidget *parent=0, const char* name=0);
+ ~LinuxWireLessWidget();
+protected:
+ /** poll() is supposed to read the information from
+ /proc/net/wireless (Linux) and put it into the data members. */
+ void poll();
+ static int socketFD; // the socket file descriptor to talk to the
+ // driver
+ static QStringList deviceNames;
+ static int devEnumHandler(int skfd,
+ char * ifname,
+ char * args[],
+ int count);
+ /** Store previously found number of wireless devices. */
+ int m_number;
+};
+
+#endif
diff --git a/wifi/kwireless/propertytable.cpp b/wifi/kwireless/propertytable.cpp
new file mode 100644
index 00000000..7011c691
--- /dev/null
+++ b/wifi/kwireless/propertytable.cpp
@@ -0,0 +1,151 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#include <qtable.h>
+#include <qcombobox.h>
+#include <qtimer.h>
+#include <qptrlist.h>
+#include <qtextstream.h>
+#include <kdialogbase.h>
+#include <klocale.h>
+#include "propertytable.h"
+#include "kwirelesswidget.h"
+
+extern "C" {
+#include <math.h>
+}
+
+PropertyTable::PropertyTable(QWidget *parent, const char* name)
+ : PropertyTableBase(parent, name)
+{
+}
+
+PropertyTable::~PropertyTable()
+{
+}
+
+PropertiesDialog::PropertiesDialog(QWidget *parent, const char *name)
+ : KDialogBase(parent, name, true,
+ i18n("Wireless Network Device Properties"),
+ KDialogBase::Ok, KDialogBase::Ok, true),
+ wait(false)
+{
+ table = new PropertyTable(this);
+ setMainWidget(table);
+ table->table->setLeftMargin(0);
+ table->table->setSorting(false);
+ table->table->setReadOnly(true);
+ adjustSize();
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), SLOT(timeout()));
+ timer->start(3000, false);
+ connect(table->cbDeviceSelector, SIGNAL(activated(int)),
+ SLOT(selected(int)));
+}
+
+void PropertiesDialog::update(QPtrList<DeviceInfo> *_info)
+{
+ int selection = table->cbDeviceSelector->currentItem();
+
+ if(wait) return;
+
+ info = _info;
+ DeviceInfo *device;
+ QPtrListIterator<DeviceInfo> it(*info);
+
+ table->cbDeviceSelector->clear();
+
+ while((device = it.current()) != 0)
+ {
+ ++it;
+ table->cbDeviceSelector->insertItem(device->device());
+ }
+
+ if(selection > 0 && selection < table->cbDeviceSelector->count())
+ {
+ selected(selection);
+ } else {
+ if(table->cbDeviceSelector->count() == 0)
+ {
+ selected(-1);
+ } else {
+ selected(0);
+ }
+ }
+
+ table->cbDeviceSelector->setEnabled(info->count()>1);
+
+ wait = true;
+}
+
+void PropertiesDialog::timeout()
+{
+ wait = false;
+}
+
+void PropertiesDialog::selected(int index)
+{
+ DeviceInfo temp;
+ DeviceInfo *device;
+ int count;
+
+ if(index < 0)
+ {
+ device = &temp;
+ } else {
+ device = info->at(index);
+ }
+
+ QString fields[] =
+ {
+ i18n("Device:"),
+ i18n("ESSID (network name):"),
+ i18n("Link quality:"),
+ i18n("Signal strength:"),
+ i18n("Noise level:"),
+ i18n("Bit rate:"),
+ i18n("Encryption:")
+ };
+ const int NoOfFields = sizeof(fields)/sizeof(fields[0]);
+
+ QString values[] =
+ {
+ device->device(),
+ device->essid(),
+ device->qualityString(),
+ device->signalString(),
+ device->noiseString(),
+ device->bitrateString(),
+ device->encrString()
+ };
+ const int NoOfValues = sizeof(values)/sizeof(values[0]);
+
+ if(table->table->numRows() == 0)
+ {
+ table->table->insertRows(0, NoOfFields);
+ // HACK (make more rows visible than the default):
+ resize(size().width(), (int)(1.8*size().height()));
+ }
+
+ for(count = 0; count<NoOfFields; ++count)
+ {
+ table->table->setText(count, 0, fields[count]);
+ }
+
+ for(count = 0; count<NoOfValues; ++count)
+ {
+ table->table->setText(count, 1, values[count]);
+ }
+
+ table->table->adjustColumn(0);
+ table->table->adjustColumn(1);
+
+}
+
+#include "propertytable.moc"
diff --git a/wifi/kwireless/propertytable.h b/wifi/kwireless/propertytable.h
new file mode 100644
index 00000000..4eadc78a
--- /dev/null
+++ b/wifi/kwireless/propertytable.h
@@ -0,0 +1,45 @@
+/*
+ $ Author: Mirko Boehm $
+ $ License: This code is licensed under the LGPL $
+ $ Copyright: (C) 1996-2003, Mirko Boehm $
+ $ Contact: Mirko Boehm <mirko@kde.org>
+ http://www.kde.org
+ http://www.hackerbuero.org $
+*/
+
+#ifndef KWIRELESS_PROPERTYTABLE_H
+#define KWIRELESS_PROPERTYTABLE_H
+
+#include <kdialogbase.h>
+#include <qptrlist.h>
+#include "propertytablebase.h"
+
+class DeviceInfo;
+class QTimer;
+
+class PropertyTable : public PropertyTableBase
+{
+ Q_OBJECT
+public:
+ PropertyTable(QWidget *parent = 0, const char* name = 0);
+ ~PropertyTable();
+};
+
+
+class PropertiesDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ PropertiesDialog(QWidget *parent = 0, const char *name = 0);
+protected:
+ PropertyTable *table;
+ QPtrList<DeviceInfo> *info;
+ bool wait;
+ QTimer *timer;
+public slots:
+ void update(QPtrList<DeviceInfo> *);
+ void timeout();
+ void selected(int);
+};
+
+#endif // KWIRELESS_PROPERTYTABLE_H
diff --git a/wifi/kwireless/propertytablebase.ui b/wifi/kwireless/propertytablebase.ui
new file mode 100644
index 00000000..8f52c58b
--- /dev/null
+++ b/wifi/kwireless/propertytablebase.ui
@@ -0,0 +1,68 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>PropertyTableBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>PropertyTableBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>552</width>
+ <height>439</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Wireless Network Device Properties</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QTable" row="1" column="0" rowspan="1" colspan="2">
+ <column>
+ <property name="text">
+ <string>Property</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>table</cstring>
+ </property>
+ <property name="numRows">
+ <number>0</number>
+ </property>
+ <property name="numCols">
+ <number>2</number>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ <property name="sorting">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>cbDeviceSelector</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>tlDevice</cstring>
+ </property>
+ <property name="text">
+ <string>Network device:</string>
+ </property>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wifi/locations/DE_BW_Karlsruhe_University.loc b/wifi/locations/DE_BW_Karlsruhe_University.loc
new file mode 100644
index 00000000..b485a8d0
--- /dev/null
+++ b/wifi/locations/DE_BW_Karlsruhe_University.loc
@@ -0,0 +1,29 @@
+00:02:2D:0F:87:7A DE, BW, Karlsruhe, Universität, Allg. Verfügungsgebäude
+00:02:2D:03:A2:8C DE, BW, Karlsruhe, Universität, Vorplatz Geb. 30.60
+00:02:2D:0A:45:20 DE, BW, Karlsruhe, Universität, ATIS
+00:60:1D:21:EC:2C DE, BW, Karlsruhe, Universität, HSaFSG
+00:02:2D:0F:88:30 DE, BW, Karlsruhe, Universität, Infogebäude
+00:02:2D:03:A2:26 DE, BW, Karlsruhe, Universität West, Geb. 06.42
+00:02:2D:08:CD:04 DE, BW, Karlsruhe, Universität, Halle Geb. 50.36
+00:02:2D:03:9E:E4 DE, BW, Karlsruhe, Universität, FZU (Nordteil)
+00:02:2D:03:9E:FF DE, BW, Karlsruhe, Universität, FZU (Südteil)
+00:02:2D:0F:88:CC DE, BW, Karlsruhe, Universität, FZU
+00:60:1D:F2:81:85 DE, BW, Karlsruhe, Universität, Geb. 30.70 (Westteil)
+00:60:1D:22:0B:9E DE, BW, Karlsruhe, Universität, Geb. 30.70 (Ostteil)
+00:02:2D:0F:87:B7 DE, BW, Karlsruhe, Universität, Geb. 30.61
+00:02:2D:0F:88:C0 DE, BW, Karlsruhe, Universität, Mensavorplatz
+00:02:2D:0A:E6:24 DE, BW, Karlsruhe, Universität, Mensahalle
+00:60:1D:21:E0:25 DE, BW, Karlsruhe, Universität, Rechenzentrum Untergeschoss
+00:02:2D:03:9F:00 DE, BW, Karlsruhe, Universität, Allg. Verfügungsgebäude -109
+00:02:2D:0A:45:DA DE, BW, Karlsruhe, Universität, Infogebäude, EG West
+00:60:1D:21:DF:7E DE, BW, Karlsruhe, Universität, Infogebäude, 3. OG Ost
+00:02:2D:03:9E:F8 DE, BW, Karlsruhe, Universität, Infogebäude, 3. OG West
+00:60:1D:21:E9:2D DE, BW, Karlsruhe, Universität, Infogebäude, Raum 020
+00:60:1D:21:E9:17 DE, BW, Karlsruhe, Universität, Infogebäude, 1. OG Nord
+00:60:1D:21:EA:F6 DE, BW, Karlsruhe, Universität, Infogebäude, 1. OG Ost
+00:60:1D:21:DE:6E DE, BW, Karlsruhe, Universität, Infogebäude, 2. OG Ost
+00:02:2D:03:A3:16 DE, BW, Karlsruhe, Universität, Infogebäude, 2. OG West
+00:60:1D:21:E0:7F DE, BW, Karlsruhe, Universität, Infogebäude, 2. OG Nord
+00:02:2D:0A:45:58 DE, BW, Karlsruhe, Universität West, Geb. 06.34
+00:02:2D:03:9E:BB DE, BW, Karlsruhe, Universität West, Geb. 06.42
+00:02:2D:0F:86:08 DE, BW, Karlsruhe, Universität, Architekturgebäude, Neuer Hörsaal
diff --git a/wifi/locations/DE_BW_Weissach.loc b/wifi/locations/DE_BW_Weissach.loc
new file mode 100644
index 00000000..dec83a18
--- /dev/null
+++ b/wifi/locations/DE_BW_Weissach.loc
@@ -0,0 +1,2 @@
+00:30:F1:CC:1C:42 DE, BW, Weissach, Hotel am Neuenbuehl, Flur
+00:30:F1:AC:E0:36 DE, BW, Weissach, Hotel am Neuenbuehl, Restaurant
diff --git a/wifi/locations/Makefile.am b/wifi/locations/Makefile.am
new file mode 100644
index 00000000..9bc167e7
--- /dev/null
+++ b/wifi/locations/Makefile.am
@@ -0,0 +1,6 @@
+emo_DATA = DE_BW_Karlsruhe_University.loc \
+ NOWHERE.loc
+
+emodir = $(kde_datadir)/kwifimanager/locations
+
+EXTRA_DIST = $(emo_DATA)
diff --git a/wifi/locations/NOWHERE.loc b/wifi/locations/NOWHERE.loc
new file mode 100644
index 00000000..3dc5d489
--- /dev/null
+++ b/wifi/locations/NOWHERE.loc
@@ -0,0 +1 @@
+00:00:00:00:00:00 N/A
diff --git a/wifi/locator.cpp b/wifi/locator.cpp
new file mode 100644
index 00000000..dc4e4ce1
--- /dev/null
+++ b/wifi/locator.cpp
@@ -0,0 +1,56 @@
+/***************************************************************************
+ locator.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qpainter.h>
+#include "locator.h"
+#include "interface_wireless.h"
+
+extern QStringList APs;
+
+Locator::Locator (QWidget * parent, Interface_wireless * device):QWidget (parent,
+ "KwiFiManager")
+{
+ this->device = device;
+}
+
+void
+Locator::generateLocWidget ()
+{
+ QString mac, ip;
+ device->get_AP_info (mac, ip);
+ sentence =
+ new QLabel (i18n ("AccessPoint: ") + whois (mac.local8Bit (), APs), this);
+ setFixedSize (QSize
+ (sentence->sizeHint ().width () + 2,
+ sentence->sizeHint ().height ()));
+ sentence->
+ setFixedSize (QSize
+ (sentence->sizeHint ().width () + 2,
+ sentence->sizeHint ().height ()));
+ sentence->show ();
+ widgetbild = QPixmap::grabWidget (sentence);
+ delete sentence;
+}
+
+void
+Locator::paintEvent (QPaintEvent *)
+{
+ QPainter *locatorpainter = new QPainter (this);
+ generateLocWidget ();
+ locatorpainter->drawPixmap (0, 0, widgetbild);
+ delete locatorpainter;
+}
diff --git a/wifi/locator.h b/wifi/locator.h
new file mode 100644
index 00000000..af63c70c
--- /dev/null
+++ b/wifi/locator.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ locator.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 LOCATOR_H
+#define LOCATOR_H
+
+#include <qwidget.h>
+#include <qpixmap.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+
+class Interface_wireless;
+
+class Locator:public QWidget
+{
+public:
+ Locator (QWidget * parent, Interface_wireless * device);
+private:
+ void paintEvent (QPaintEvent *);
+ void generateLocWidget ();
+ QPixmap widgetbild;
+ QLabel *sentence;
+ Interface_wireless *device;
+};
+
+#endif
diff --git a/wifi/main.cpp b/wifi/main.cpp
new file mode 100644
index 00000000..4cf6f567
--- /dev/null
+++ b/wifi/main.cpp
@@ -0,0 +1,66 @@
+/***************************************************************************
+ main.cpp - description
+ -------------------
+ begin : Sam Apr 7 11:44:20 CEST 2001
+ copyright : (C) 2001 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <kcmdlineargs.h>
+#include <kaboutdata.h>
+#include <klocale.h>
+#include <kapplication.h>
+
+#include "kwifimanager.h"
+#ifndef VERSION
+#define VERSION "1_2_0"
+#endif
+
+static const char description[] =
+I18N_NOOP ("KWiFiManager - Wireless LAN Manager for KDE");
+
+
+static KCmdLineOptions options[] = {
+ KCmdLineLastOption
+};
+
+int
+main (int argc, char *argv[])
+{
+ KAboutData aboutData ("kwifimanager", I18N_NOOP ("KWiFiManager"), VERSION,
+ description, KAboutData::License_GPL,
+ "(c) 2001-05, Stefan Winter");
+ aboutData.addAuthor ("Stefan Winter", I18N_NOOP("Original Author and Maintainer"), "swinter@kde.org");
+ aboutData.addCredit ("Helge Deller", I18N_NOOP("Lots of Fixes and Optimizations, added Session Management"),
+ "deller@kde.org");
+ KCmdLineArgs::init (argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions (options); // Add our own options.
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs ();
+ args->clear ();
+
+ KApplication app;
+
+ if (app.isRestored ())
+ {
+ RESTORE (KWiFiManagerApp(0,"mainWindow"));
+ KWiFiManagerApp *kwifimanager = (KWiFiManagerApp *) app.mainWidget();
+ if (kwifimanager && kwifimanager->startDocked())
+ kwifimanager->hide();
+ }
+ else
+ {
+ KWiFiManagerApp *kwifimanager = new KWiFiManagerApp (0,"mainWindow");
+ kwifimanager->show();
+ }
+
+ return app.exec ();
+}
diff --git a/wifi/networkscanning.cpp b/wifi/networkscanning.cpp
new file mode 100644
index 00000000..5a15ffbc
--- /dev/null
+++ b/wifi/networkscanning.cpp
@@ -0,0 +1,155 @@
+/***************************************************************************
+ networkscanning.cpp - description
+ -------------------
+ begin : Sam Apr 24 11:44:20 CEST 2005
+ copyright : (C) 2005 by Stefan Winter
+ email : swinter@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include <qstring.h>
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtable.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kmessagebox.h>
+#include <ktempfile.h>
+
+#include "networkscanning.h"
+#include "interface_wireless.h"
+
+NetworkScanning::NetworkScanning (Interface_wireless * dev, QWidget * parent, const char * name ) : QWidget ( parent, name ) {
+ device = dev;
+ networkScan();
+}
+
+void
+NetworkScanning::networkScan ()
+{
+ networks = device->get_available_networks ();
+
+ if ( networks->numRows() > 0 ) {
+
+ networks->setColumnReadOnly( 0, true);
+ networks->setColumnReadOnly( 1, true);
+ networks->setColumnReadOnly( 2, true);
+ networks->setColumnReadOnly( 3, false);
+ for ( int i = 0; i < networks->numRows(); i++) {
+ if ( networks->text( i, 3 ) == i18n( "off" )) networks->setRowReadOnly( i, true );
+ }
+
+ networks->setSelectionMode(QTable::SingleRow);
+ connect(networks,SIGNAL(selectionChanged()),this,SLOT(checkWEP()));
+
+ this->setCaption( i18n( "Scan Results" ) );
+
+ QGridLayout* networkSelectionLayout = new QGridLayout ( this, 2, 3, 0, 5);
+ switchNet = new QPushButton( i18n( "Switch to Network..." ), this );
+ switchNet->setEnabled(false);
+ QPushButton* close = new QPushButton( i18n( "Close" ), this );
+
+ networks->reparent( this, QPoint( 0, 0 ) );
+ networks->setLeftMargin( 0 );
+ networks->verticalHeader()->hide();
+
+ connect ( close, SIGNAL( clicked() ), this, SLOT( hide() ) );
+ connect ( switchNet, SIGNAL( clicked() ), this, SLOT( switchToNetwork() ) );
+ connect ( networks, SIGNAL( currentChanged(int,int)), this, SLOT( checkSettings(int,int)));
+ connect ( networks, SIGNAL( valueChanged(int,int)), this, SLOT( checkSettings(int,int)));
+
+ networkSelectionLayout->addMultiCellWidget( networks, 0, 0, 0, 2 );
+ networkSelectionLayout->addWidget( switchNet, 1, 0 );
+ networkSelectionLayout->addWidget( close, 1, 2 );
+
+ this->show();
+
+ } else
+ {
+ KMessageBox::sorry(0,i18n("The scan is complete, but no networks have been found."),i18n("No Network Available"));
+ }
+}
+
+void NetworkScanning::checkSettings(int row, int)
+{
+ if ((networks->text(row,0)!=i18n("(hidden cell)")) && (checkWEP()!=INVALID)) switchNet->setEnabled(true);
+ else switchNet->setEnabled(false);
+}
+
+WEP_KEY
+NetworkScanning::checkWEP()
+{
+ kdDebug() << "In checkWEP()\n";
+ if ( (networks->text( networks->currentRow() , 3 ) == i18n( "off" )) ||
+ (networks->text( networks->currentRow() , 3 ) == "" ) ) return NONE;
+ if ( (networks->text( networks->currentRow() , 3 ).length()== 5 ) ||
+ (networks->text( networks->currentRow() , 3 ).length()== 13 ) ) return VALID_STRING;
+ if ( (networks->text( networks->currentRow() , 3 ).length()== 10 ) ||
+ (networks->text( networks->currentRow() , 3 ).length()== 26 ) ) return VALID_HEX;
+ return INVALID;
+}
+
+void
+NetworkScanning::switchToNetwork()
+{
+
+ WEP_KEY encryption = checkWEP();
+
+ if (encryption == INVALID) {
+ KMessageBox::sorry(0,i18n( "Aborting network switching due to invalid WEP key specification." ), i18n( "Invalid WEP Key" ));
+ return;
+ }
+
+ QString cmdline;
+
+ KTempFile* tempfile = new KTempFile( QString::null, QString::null, 0700 );
+ QString tempfilename = tempfile->name();
+
+ cmdline = (QString)"ifconfig %1 down\n";
+ cmdline = cmdline.arg( device->get_interface_name() );
+ write( tempfile->handle(), cmdline.ascii(), strlen( cmdline.ascii() ) );
+
+ cmdline = (QString)"iwconfig %1 essid %2 mode %3 enc %4\n";
+ cmdline = cmdline.arg( device->get_interface_name() );
+ cmdline = cmdline.arg( KProcess::quote( networks->text( networks->currentRow(), 0 ) ) );
+
+ QString modetemp;
+ if (networks->text( networks->currentRow(), 1 ) == i18n("Managed") ) modetemp = "Managed"; else modetemp = "Ad-Hoc";
+ cmdline = cmdline.arg( modetemp );
+
+ if ( encryption != NONE ) {
+ cmdline = cmdline.arg( (encryption == VALID_STRING ? "s:" : "" ) + KProcess::quote( networks->text( networks->currentRow(), 3 ) ) );
+ } else {
+ cmdline = cmdline.arg("off");
+ }
+ write( tempfile->handle(), cmdline.ascii(), strlen( cmdline.ascii() ) );
+
+ cmdline = (QString)"ifconfig %1 up\n";
+ cmdline = cmdline.arg( device->get_interface_name() );
+ write( tempfile->handle(), cmdline.ascii(), strlen( cmdline.ascii() ) );
+
+ delete tempfile; // autoDeletion off, so the file remains on disk
+
+ KProcess switchProc;
+ switchProc << "kdesu" << tempfilename;
+ switchProc.start( KProcess::Block );
+
+ remove(tempfilename.ascii());
+
+}
+
+
+#include "networkscanning.moc"
diff --git a/wifi/networkscanning.h b/wifi/networkscanning.h
new file mode 100644
index 00000000..5964db3a
--- /dev/null
+++ b/wifi/networkscanning.h
@@ -0,0 +1,52 @@
+
+/***************************************************************************
+ networkscanning.h - header for the NetworkScanning class
+ -------------------
+ begin : Sam Apr 24 11:44:20 CEST 2005
+ copyright : (C) 2005 by Stefan Winter
+ email : swinter@kde.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef NETWORKSCANNING_H
+#define NETWORKSCANNING_H
+
+#include <qwidget.h>
+
+class QTable;
+class QPushButton;
+class Interface_wireless;
+
+enum WEP_KEY { NONE, VALID_HEX, VALID_STRING, INVALID };
+
+class NetworkScanning : public QWidget
+
+{
+
+ Q_OBJECT
+
+ public:
+ NetworkScanning (Interface_wireless * dev, QWidget * parent = 0, const char * name = 0 );
+
+ protected:
+ void networkScan ();
+ QTable* networks;
+ Interface_wireless * device;
+ QPushButton* switchNet;
+
+ protected slots:
+ void checkSettings(int row, int);
+ WEP_KEY checkWEP();
+ void switchToNetwork();
+
+};
+
+#endif /* NETWORKSCANNING_H */
diff --git a/wifi/picture.cpp b/wifi/picture.cpp
new file mode 100644
index 00000000..11d77cae
--- /dev/null
+++ b/wifi/picture.cpp
@@ -0,0 +1,92 @@
+/***************************************************************************
+ picture.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qwidget.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <kstandarddirs.h>
+#include "picture.h"
+#include "interface_wireless.h"
+
+#define X_OFFSET 20
+#define Y_OFFSET 10
+
+Picture::Picture (QWidget * parent, Interface_wireless * device):QWidget (parent,
+ "picture")
+{
+ this->device = device;
+ KStandardDirs finder;
+ kdedir = finder.findResourceDir ("data", "kwifimanager/pics/no_card.png");
+ kdedir += "kwifimanager/pics/";
+ NO_CARD = new QPixmap (kdedir + "no_card.png");
+ AD_HOC = new QPixmap (kdedir + "ad_hoc.png");
+ ALL_ALONE = new QPixmap (kdedir + "all_alone.png");
+ AP_CONNECT = new QPixmap (kdedir + "ap_connect.png");
+}
+
+QSize Picture::mySizeHint() {
+ /* make this quite general, just in case someone uses different icons */
+ QSize* temp = new QSize(NO_CARD->size());
+ if (AD_HOC->size().width() > temp->width ()) { temp->setWidth (AD_HOC->size().width ()); }
+ if (AD_HOC->size().height() > temp->height()) { temp->setHeight(AD_HOC->size().height()); }
+ if (ALL_ALONE->size().width() > temp->width ()) { temp->setWidth (ALL_ALONE->size().width ()); }
+ if (ALL_ALONE->size().height() > temp->height()) { temp->setHeight(ALL_ALONE->size().height()); }
+ if (AP_CONNECT->size().width() > temp->width ()) { temp->setWidth (AP_CONNECT->size().width ()); }
+ if (AP_CONNECT->size().height() > temp->height()) { temp->setHeight(AP_CONNECT->size().height()); }
+ temp->setWidth (temp->width() + X_OFFSET);
+ temp->setHeight(temp->height() + Y_OFFSET);
+ return *temp;
+}
+
+Picture::~Picture ()
+{
+ delete NO_CARD;
+ delete AD_HOC;
+ delete ALL_ALONE;
+ delete AP_CONNECT;
+
+}
+
+void
+Picture::paintEvent (QPaintEvent *)
+{
+ QPainter *picturepainter = new QPainter (this);
+ double freq;
+ int mode;
+ device->get_mode (mode);
+ int sig, noise, qual;
+ device->get_current_quality (sig, noise, qual);
+ if (!device->get_device_freq (freq) || device->get_txpower_disabled())
+ {
+ picturepainter->drawPixmap (X_OFFSET, Y_OFFSET, *NO_CARD);
+ }
+ else if (mode == 1)
+ {
+ picturepainter->drawPixmap (X_OFFSET, Y_OFFSET, *AD_HOC);
+ }
+ else if (qual == 0)
+ {
+ picturepainter->drawPixmap (X_OFFSET, Y_OFFSET, *ALL_ALONE);
+ }
+ else if (qual > 0)
+ {
+ picturepainter->drawPixmap (X_OFFSET, Y_OFFSET, *AP_CONNECT);
+ }
+ else
+ picturepainter->drawPixmap (X_OFFSET, Y_OFFSET, *NO_CARD);
+ delete picturepainter;
+}
diff --git a/wifi/picture.h b/wifi/picture.h
new file mode 100644
index 00000000..8d9b6bfc
--- /dev/null
+++ b/wifi/picture.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ picture.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 PICTURE_H
+#define PICTURE_H
+
+class Interface_wireless;
+class QSize;
+
+class Picture:public QWidget
+{
+public:
+ Picture (QWidget * parent, Interface_wireless * device);
+ QSize mySizeHint();
+ ~Picture ();
+private:
+ void paintEvent (QPaintEvent *);
+ QPixmap *NO_CARD, *ALL_ALONE, *AD_HOC, *AP_CONNECT;
+ QString kdedir;
+ Interface_wireless *device;
+};
+
+#endif
diff --git a/wifi/pixmaps/Makefile.am b/wifi/pixmaps/Makefile.am
new file mode 100644
index 00000000..66625201
--- /dev/null
+++ b/wifi/pixmaps/Makefile.am
@@ -0,0 +1,14 @@
+emo_DATA = ad_hoc.png \
+ all_alone.png \
+ ap_connect.png \
+ excellent.png \
+ good.png \
+ marginal.png \
+ no_card.png \
+ offline.png \
+ oor_down.png \
+ oor_minimum.png
+
+emodir = $(kde_datadir)/kwifimanager/pics
+
+EXTRA_DIST = $(emo_DATA)
diff --git a/wifi/pixmaps/README b/wifi/pixmaps/README
new file mode 100644
index 00000000..0464e53e
--- /dev/null
+++ b/wifi/pixmaps/README
@@ -0,0 +1,2 @@
+Connection type SVG files and their accompanying PNG files Copyright Jonathan Riddell jr_AT_jriddell.org 2004.
+They may be copied under the LGPL with add-on as found in kdelibs/pics/LICENSE.crystalsvg
diff --git a/wifi/pixmaps/ad_hoc.png b/wifi/pixmaps/ad_hoc.png
new file mode 100644
index 00000000..fb85a078
--- /dev/null
+++ b/wifi/pixmaps/ad_hoc.png
Binary files differ
diff --git a/wifi/pixmaps/ad_hoc.svg b/wifi/pixmaps/ad_hoc.svg
new file mode 100644
index 00000000..25b7a606
--- /dev/null
+++ b/wifi/pixmaps/ad_hoc.svg
@@ -0,0 +1,1102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- This file was created using the SVG export filter from Karbon14, a free vector drawing app. -->
+<!-- It is part of koffice, the free, integrated office suite for KDE (http://www.koffice.org/). -->
+<svg
+ width="60px"
+ height="30.000000pt"
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.40"
+ sodipodi:docname="ad_hoc.svgz"
+ sodipodi:docbase="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics"
+ inkscape:export-filename="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics/ad_hoc.png"
+ inkscape:export-xdpi="115.50000"
+ inkscape:export-ydpi="115.50000"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata
+ id="metadata64">
+ <rdf:RDF
+ id="RDF65">
+ <cc:Work
+ rdf:about=""
+ id="Work66">
+ <dc:format
+ id="format67">image/svg+xml</dc:format>
+ <dc:type
+ id="type69"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.4732670"
+ inkscape:cx="54.239187"
+ inkscape:cy="29.761621"
+ inkscape:window-width="802"
+ inkscape:window-height="538"
+ inkscape:window-x="214"
+ inkscape:window-y="153"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs2">
+ <radialGradient
+ id="defitem14"
+ gradientUnits="userSpaceOnUse"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop46" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop47" />
+ </radialGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem14"
+ id="radialGradient2316"
+ gradientUnits="userSpaceOnUse"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)" />
+ <radialGradient
+ id="defitem17"
+ gradientUnits="userSpaceOnUse"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop55" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop56" />
+ </radialGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem17"
+ id="radialGradient2317"
+ gradientUnits="userSpaceOnUse"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)" />
+ <linearGradient
+ id="defitem22"
+ gradientUnits="userSpaceOnUse"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop70" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop71" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem22"
+ id="linearGradient2318"
+ gradientUnits="userSpaceOnUse"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)" />
+ <linearGradient
+ id="defitem23"
+ gradientUnits="userSpaceOnUse"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop73" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop74" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem23"
+ id="linearGradient2319"
+ gradientUnits="userSpaceOnUse"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)" />
+ <linearGradient
+ id="defitem0"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop4" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop5" />
+ </linearGradient>
+ <linearGradient
+ id="defitem1"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop7" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop8" />
+ </linearGradient>
+ <linearGradient
+ id="defitem2"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop10" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop11" />
+ </linearGradient>
+ <linearGradient
+ id="defitem3"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop13" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="defitem4"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop16" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop17" />
+ </linearGradient>
+ <linearGradient
+ id="defitem5"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop19" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop20" />
+ </linearGradient>
+ <linearGradient
+ id="defitem6"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop22" />
+ <stop
+ stop-color="#646464"
+ offset="1"
+ stop-opacity="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="defitem7"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop25" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop26" />
+ </linearGradient>
+ <linearGradient
+ id="defitem8"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.0818"
+ y2="39.6786">
+ <stop
+ stop-color="#2a2a2a"
+ offset="0"
+ stop-opacity="1"
+ id="stop28" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop29" />
+ </linearGradient>
+ <radialGradient
+ id="defitem9"
+ gradientUnits="userSpaceOnUse"
+ cx="24.5089"
+ cy="28.1715"
+ fx="44.128"
+ fy="61.0801"
+ r="18.9697">
+ <stop
+ stop-color="#a3cdeb"
+ offset="0"
+ stop-opacity="1"
+ id="stop31" />
+ <stop
+ stop-color="#4189dd"
+ offset="1"
+ stop-opacity="1"
+ id="stop32" />
+ </radialGradient>
+ <linearGradient
+ id="defitem10"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop34" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop35" />
+ </linearGradient>
+ <linearGradient
+ id="defitem11"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop37" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop38" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1741"
+ gradientUnits="userSpaceOnUse"
+ cx="31.862440"
+ cy="30.222889"
+ fx="31.862440"
+ fy="30.222889"
+ r="19.767193" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1742"
+ gradientUnits="userSpaceOnUse"
+ x1="20.910299"
+ y1="40.476101"
+ x2="23.115303"
+ y2="38.855164" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem14"
+ id="radialGradient1851"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem17"
+ id="radialGradient1852"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem22"
+ id="linearGradient1853"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem23"
+ id="linearGradient1854"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1855"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1856"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1857"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1858"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1859"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1860"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1861"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1862"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1863"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1864"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1889"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1890"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1891"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1892"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1893"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1894"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1895"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1896"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1897"
+ gradientUnits="userSpaceOnUse"
+ x1="20.910299"
+ y1="40.476101"
+ x2="23.115303"
+ y2="38.855164" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1898"
+ gradientUnits="userSpaceOnUse"
+ cx="31.862440"
+ cy="30.222889"
+ fx="31.862440"
+ fy="30.222889"
+ r="19.767193" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1899"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1900"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1961"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1962"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1963"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1964"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1965"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1966"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1967"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1968"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1969"
+ gradientUnits="userSpaceOnUse"
+ x1="20.910299"
+ y1="40.476101"
+ x2="23.115303"
+ y2="38.855164" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1970"
+ gradientUnits="userSpaceOnUse"
+ cx="31.862440"
+ cy="30.222889"
+ fx="31.862440"
+ fy="30.222889"
+ r="19.767193" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1971"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1972"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem14"
+ id="radialGradient1973"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem17"
+ id="radialGradient1974"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem22"
+ id="linearGradient1975"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem23"
+ id="linearGradient1976"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(0.610854,0.000000,0.000000,-0.610854,19.64182,-45.08786)"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1977"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1978"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1979"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1980"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1981"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1982"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1983"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1984"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1985"
+ gradientUnits="userSpaceOnUse"
+ x1="20.910299"
+ y1="40.476101"
+ x2="23.115303"
+ y2="38.855164" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1986"
+ gradientUnits="userSpaceOnUse"
+ cx="31.862440"
+ cy="30.222889"
+ fx="31.862440"
+ fy="30.222889"
+ r="19.767193" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1987"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1988"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ </defs>
+ <g
+ id="g1901"
+ transform="matrix(0.828345,0.000000,0.000000,0.828345,10.23566,-17.55248)">
+ <g
+ transform="matrix(0.494888,0.000000,0.000000,-0.494888,30.11960,58.94005)"
+ id="g39">
+ <path
+ id="path87"
+ d=""
+ style="fill:#000000" />
+ <g
+ id="g41">
+ <g
+ id="g42" />
+ <g
+ id="g45">
+ <g
+ id="g46">
+ <path
+ id="path1660"
+ d="M 35.889700,35.427300 C 35.342900,35.391900 34.794500,35.288600 34.279700,35.102500 L 6.6818400,25.009500 C 5.8918000,24.723900 5.4991800,24.170400 5.2361300,23.602300 C 4.9730800,23.034200 4.8242500,22.394200 4.7761300,21.762200 C 4.7280100,21.130100 4.7778000,20.490500 4.9075600,19.922000 C 5.0373100,19.353600 5.2026600,18.850900 5.7618400,18.406600 L 21.358700,5.1619700 C 22.961500,3.8885800 25.503800,3.5343200 27.535800,4.2689500 L 55.133700,14.362000 C 55.868100,14.627500 56.252500,15.151100 56.480900,15.660900 C 56.709200,16.170800 56.785000,16.709600 56.809400,17.257500 C 56.833900,17.805400 56.804800,18.364700 56.678000,18.854100 C 56.556400,19.323500 56.382600,19.734400 55.922300,20.126000 L 55.955200,20.153000 L 40.489700,34.182500 L 40.456900,34.182500 L 40.424000,34.209500 C 39.619200,34.848900 38.596700,35.216500 37.532600,35.373100 C 36.998500,35.451800 36.436600,35.462600 35.889700,35.427300 z M 37.138300,33.641200 C 37.859500,33.535100 38.493700,33.305600 38.912600,32.991800 L 54.378000,18.962400 L 54.410900,18.962400 L 54.443700,18.935300 C 54.344600,19.014100 54.525200,18.795000 54.608000,18.475300 C 54.690800,18.155500 54.724900,17.748600 54.706600,17.338700 C 54.688300,16.928800 54.627000,16.518900 54.509400,16.256300 C 54.391800,15.993700 54.245100,15.919100 54.279400,15.931500 L 26.681500,5.8384900 C 25.603100,5.4486100 23.646500,5.7377600 22.804400,6.4067700 L 7.2075500,19.651400 C 7.2693100,19.602400 7.0670200,19.854800 6.9775500,20.246800 C 6.8880800,20.638700 6.8410800,21.156100 6.8789800,21.653900 C 6.9168800,22.151700 7.0523000,22.644600 7.2075500,22.979900 C 7.3628000,23.315200 7.5485300,23.444400 7.5361200,23.440000 L 35.134000,33.533000 C 35.659700,33.723100 36.394000,33.750800 37.138300,33.641200 z "
+ style="fill:#7e7e7e;stroke-width:2.2734101;stroke-linecap:round" />
+ <path
+ id="path1661"
+ d="M 37.028700,34.507200 C 36.122400,34.640600 35.177700,34.598900 34.400100,34.317800 L 7.1089800,24.224700 C 5.5537300,23.662500 5.4898600,19.819400 6.4846900,19.029000 L 22.238100,5.9159700 C 23.460600,4.9447800 25.710000,4.6230700 27.265300,5.1853300 L 54.556400,15.278400 C 55.956500,15.784600 55.838500,19.036200 55.016400,19.689300 L 39.394400,33.587100 C 38.783100,34.072700 37.934900,34.373800 37.028700,34.507200 z "
+ style="fill:url(#linearGradient1961);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1662"
+ d="M 7.0978700,24.212800 L 34.232200,34.376500 C 35.787500,34.938800 38.253800,34.672700 39.476200,33.701500 L 54.395700,20.422300 C 55.618100,19.451100 55.350200,18.216600 53.794900,17.654300 L 26.660600,7.4906200 C 25.105300,6.9283600 22.869100,7.2575700 21.646700,8.2287700 L 6.4971100,21.444800 C 5.2746800,22.416000 5.5426200,23.650500 7.0978700,24.212800 z "
+ style="fill:url(#linearGradient1962);stroke-width:1.4172200;stroke-linecap:round" />
+ <g
+ id="g50">
+ <path
+ id="path1664"
+ d="M 13.464600,20.842300 L 38.049400,30.017400 C 39.328100,30.479700 41.166700,30.209000 42.171800,29.410500 L 48.750800,23.746900 C 49.755900,22.948400 49.535600,21.933400 48.256900,21.471100 L 23.672100,12.296000 C 22.393400,11.833700 20.554800,12.104400 19.549700,12.902900 L 12.970700,18.566500 C 11.965600,19.365000 12.185900,20.380000 13.464600,20.842300 z "
+ style="fill:url(#linearGradient1963);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1665"
+ d="M 14.070400,20.920400 L 37.950400,29.668500 C 39.150000,30.102200 40.874900,29.848300 41.817900,29.099100 L 47.804500,23.826400 C 48.747400,23.077300 48.540700,22.125000 47.341100,21.691300 L 23.461100,12.943100 C 22.261500,12.509400 20.536600,12.763400 19.593600,13.512500 L 13.607000,18.785300 C 12.664100,19.534400 12.870800,20.486700 14.070400,20.920400 z "
+ style="fill:url(#linearGradient1964);stroke-width:1.4172200;stroke-linecap:round" />
+ </g>
+ <g
+ id="g53">
+ <path
+ id="path3151"
+ d="M 32.490900,15.274200 L 39.306300,17.754800 C 39.785000,17.927800 40.473300,17.826500 40.849500,17.527600 L 43.312400,15.407400 C 43.688600,15.108500 43.606200,14.728500 43.127500,14.555400 L 36.312100,12.074900 C 35.833400,11.901800 35.145100,12.003200 34.768900,12.302100 L 32.306000,14.422300 C 31.929800,14.721200 32.012300,15.101200 32.490900,15.274200 z "
+ style="fill:url(#linearGradient1965);stroke-width:0.53053498;stroke-linecap:round" />
+ <path
+ id="path3152"
+ d="M 32.717700,15.303400 L 39.230100,17.690000 C 39.679200,17.852300 40.324900,17.757200 40.677900,17.476800 L 42.919000,15.503000 C 43.272000,15.222500 43.194600,14.866000 42.745500,14.703700 L 36.233100,12.317200 C 35.784000,12.154800 35.138300,12.249900 34.785300,12.530300 L 32.544300,14.504100 C 32.191300,14.784600 32.268600,15.141100 32.717700,15.303400 z "
+ style="fill:url(#linearGradient1966);stroke-width:0.53053498;stroke-linecap:round" />
+ </g>
+ </g>
+ <g
+ id="g56">
+ <path
+ id="path1670"
+ d="M 30.744300,59.532300 C 30.432000,59.561000 30.065700,59.511600 29.733000,59.388200 L 2.6736100,49.864700 C 1.3429500,49.370900 0.47013600,47.759100 0.72888100,46.280700 L 4.6183300,24.096900 C 4.8770800,22.618500 8.1261900,20.721200 10.180300,21.130800 L 37.912100,31.409100 C 39.242700,31.902900 40.115500,33.535300 39.856800,35.013700 L 35.967300,57.197500 C 35.708600,58.675900 31.475300,59.465200 30.744300,59.532300 z "
+ style="fill:url(#linearGradient1967);stroke-width:1.3453600;stroke-linecap:round" />
+ <path
+ id="path1671"
+ d="M 5.8649200,47.969300 C 4.8047100,47.575900 4.1239100,46.306800 4.3327900,45.113300 L 8.1133500,23.511600 C 8.3222400,22.318100 9.3371300,21.678200 10.397300,22.071700 L 37.542200,32.144700 C 38.602400,32.538100 39.283200,33.807200 39.074300,35.000800 L 35.293700,56.602400 C 35.084800,57.796000 34.069900,58.435800 33.009700,58.042400 L 5.8649200,47.969300 z M 7.4174400,45.999000 L 32.294200,55.230400 C 32.787200,55.413300 33.252900,55.119800 33.350000,54.564700 L 36.690800,35.475900 C 36.787900,34.920800 36.475600,34.338600 35.982600,34.155600 L 11.105800,24.924200 C 10.612800,24.741200 10.147100,25.034800 10.050000,25.589800 L 6.7091900,44.678700 C 6.6120500,45.233700 6.9244100,45.816000 7.4174400,45.999000 z "
+ style="fill:url(#linearGradient1968);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ d="M 10.223700,24.596900 L 6.5353900,45.671700 L 33.176200,55.557700 L 36.864500,34.482900 L 10.223700,24.596900 z "
+ id="path59"
+ style="fill:url(#linearGradient1969);fill-opacity:1.0000000;stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ d="M 10.801900,25.674700 L 7.3972700,45.128300 L 32.598000,54.479900 L 36.002700,35.026300 L 10.801900,25.674700 z "
+ id="path60"
+ style="fill:url(#radialGradient1970);fill-opacity:1.0000000;stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ id="path1674"
+ d="M 5.9500300,47.483000 C 5.1201500,47.175000 4.6013000,46.207800 4.7648000,45.273600 L 6.7685700,33.824300 C 7.2127900,33.798400 7.6853400,33.800900 8.1532700,33.798600 L 6.2771800,44.518400 C 6.1352700,45.329200 6.6120600,46.218000 7.3323200,46.485300 L 32.209100,55.716700 C 32.929300,55.984000 33.640100,55.535900 33.782000,54.725000 L 35.782200,43.296000 C 36.209100,43.599200 36.597100,43.914000 37.000300,44.222800 L 34.861700,56.442100 C 34.698200,57.376400 33.924700,57.864000 33.094900,57.556000 L 5.9500300,47.483000 z "
+ style="fill:url(#linearGradient1971);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ id="path1675"
+ d="M 9.0398900,44.874600 C 8.6107900,44.715400 8.3262800,44.201500 8.4081800,43.733500 L 10.491000,31.832300 C 12.953300,36.964200 21.062000,43.028400 31.308400,46.830700 C 31.906000,47.052400 32.479700,47.206700 33.069400,47.405300 L 32.168900,52.550800 C 32.087000,53.018700 31.668300,53.271700 31.239200,53.112500 L 9.0398900,44.874600 z "
+ style="opacity:0.78313255;fill:url(#linearGradient1972);stroke-width:1.3751301;stroke-linecap:round" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1840"
+ transform="translate(-29.26542,106.6279)">
+ <path
+ id="path1715"
+ d="M 49.708973,-74.827841 C 52.537594,-72.279969 54.442970,-67.751708 54.442970,-62.553402 C 54.442970,-57.355095 52.537594,-52.826835 49.708973,-50.279024 C 54.490311,-52.302478 57.844632,-57.037574 57.844632,-62.553402 C 57.844632,-68.069230 54.490311,-72.804326 49.708973,-74.827841 z "
+ style="fill:#007e00;stroke-width:2.0899899;stroke-linecap:round" />
+ <path
+ id="path1717"
+ d="M 47.280707,-69.082332 C 48.785362,-67.727152 49.798830,-65.318494 49.798830,-62.553402 C 49.798830,-59.788371 48.785362,-57.379713 47.280707,-56.024472 C 49.823997,-57.100797 51.608241,-59.619470 51.608241,-62.553402 C 51.608241,-65.487395 49.823997,-68.006068 47.280707,-69.082332 z "
+ style="fill:#007e00;stroke-width:2.0899899;stroke-linecap:round" />
+ <path
+ id="path1721"
+ d="M 52.419821,-72.435065 C 54.063752,-69.775712 55.072882,-66.364092 55.072882,-62.560793 C 55.072882,-58.757494 54.063752,-55.325838 52.419821,-52.666486 C 55.329624,-54.989808 57.207328,-58.544306 57.207328,-62.560793 C 57.207328,-66.577219 55.329624,-70.111804 52.419821,-72.435065 z "
+ style="fill:url(#radialGradient1973);stroke-width:2.0899899;stroke-linecap:round" />
+ <path
+ id="path1726"
+ d="M 48.789210,-67.747249 C 49.611237,-66.377348 50.115802,-64.619860 50.115802,-62.660668 C 50.115802,-60.701415 49.611237,-58.933725 48.789210,-57.563763 C 50.244143,-58.760548 51.183025,-60.591644 51.183025,-62.660668 C 51.183025,-64.729691 50.244143,-66.550464 48.789210,-67.747249 z "
+ style="fill:url(#radialGradient1974);stroke-width:2.0899899;stroke-linecap:round" />
+ <path
+ id="path2442"
+ d="M 52.419821,-72.435065 C 54.063752,-69.775712 55.072882,-66.364092 55.072882,-62.560793 C 55.072882,-58.757494 54.063752,-55.325838 52.419821,-52.666486 C 55.329624,-54.989808 57.207328,-58.544306 57.207328,-62.560793 C 57.207328,-66.577219 55.329624,-70.111804 52.419821,-72.435065 z "
+ style="fill:url(#linearGradient1975);stroke-width:2.0899899;stroke-linecap:round" />
+ <path
+ id="path2444"
+ d="M 48.789210,-67.747249 C 49.611237,-66.377348 50.115802,-64.619860 50.115802,-62.660668 C 50.115802,-60.701415 49.611237,-58.933725 48.789210,-57.563763 C 50.244143,-58.760548 51.183025,-60.591644 51.183025,-62.660668 C 51.183025,-64.729691 50.244143,-66.550464 48.789210,-67.747249 z "
+ style="fill:url(#linearGradient1976);stroke-width:2.0899899;stroke-linecap:round" />
+ </g>
+ <g
+ transform="matrix(0.494888,0.000000,0.000000,-0.494888,-11.86991,58.94005)"
+ id="g1865">
+ <path
+ id="path1866"
+ d=""
+ style="fill:#000000" />
+ <g
+ id="g1867">
+ <g
+ id="g1868" />
+ <g
+ id="g1871">
+ <g
+ id="g1872">
+ <path
+ id="path1873"
+ d="M 35.889700,35.427300 C 35.342900,35.391900 34.794500,35.288600 34.279700,35.102500 L 6.6818400,25.009500 C 5.8918000,24.723900 5.4991800,24.170400 5.2361300,23.602300 C 4.9730800,23.034200 4.8242500,22.394200 4.7761300,21.762200 C 4.7280100,21.130100 4.7778000,20.490500 4.9075600,19.922000 C 5.0373100,19.353600 5.2026600,18.850900 5.7618400,18.406600 L 21.358700,5.1619700 C 22.961500,3.8885800 25.503800,3.5343200 27.535800,4.2689500 L 55.133700,14.362000 C 55.868100,14.627500 56.252500,15.151100 56.480900,15.660900 C 56.709200,16.170800 56.785000,16.709600 56.809400,17.257500 C 56.833900,17.805400 56.804800,18.364700 56.678000,18.854100 C 56.556400,19.323500 56.382600,19.734400 55.922300,20.126000 L 55.955200,20.153000 L 40.489700,34.182500 L 40.456900,34.182500 L 40.424000,34.209500 C 39.619200,34.848900 38.596700,35.216500 37.532600,35.373100 C 36.998500,35.451800 36.436600,35.462600 35.889700,35.427300 z M 37.138300,33.641200 C 37.859500,33.535100 38.493700,33.305600 38.912600,32.991800 L 54.378000,18.962400 L 54.410900,18.962400 L 54.443700,18.935300 C 54.344600,19.014100 54.525200,18.795000 54.608000,18.475300 C 54.690800,18.155500 54.724900,17.748600 54.706600,17.338700 C 54.688300,16.928800 54.627000,16.518900 54.509400,16.256300 C 54.391800,15.993700 54.245100,15.919100 54.279400,15.931500 L 26.681500,5.8384900 C 25.603100,5.4486100 23.646500,5.7377600 22.804400,6.4067700 L 7.2075500,19.651400 C 7.2693100,19.602400 7.0670200,19.854800 6.9775500,20.246800 C 6.8880800,20.638700 6.8410800,21.156100 6.8789800,21.653900 C 6.9168800,22.151700 7.0523000,22.644600 7.2075500,22.979900 C 7.3628000,23.315200 7.5485300,23.444400 7.5361200,23.440000 L 35.134000,33.533000 C 35.659700,33.723100 36.394000,33.750800 37.138300,33.641200 z "
+ style="fill:#7e7e7e;stroke-width:2.2734101;stroke-linecap:round" />
+ <path
+ id="path1874"
+ d="M 37.028700,34.507200 C 36.122400,34.640600 35.177700,34.598900 34.400100,34.317800 L 7.1089800,24.224700 C 5.5537300,23.662500 5.4898600,19.819400 6.4846900,19.029000 L 22.238100,5.9159700 C 23.460600,4.9447800 25.710000,4.6230700 27.265300,5.1853300 L 54.556400,15.278400 C 55.956500,15.784600 55.838500,19.036200 55.016400,19.689300 L 39.394400,33.587100 C 38.783100,34.072700 37.934900,34.373800 37.028700,34.507200 z "
+ style="fill:url(#linearGradient1977);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1875"
+ d="M 7.0978700,24.212800 L 34.232200,34.376500 C 35.787500,34.938800 38.253800,34.672700 39.476200,33.701500 L 54.395700,20.422300 C 55.618100,19.451100 55.350200,18.216600 53.794900,17.654300 L 26.660600,7.4906200 C 25.105300,6.9283600 22.869100,7.2575700 21.646700,8.2287700 L 6.4971100,21.444800 C 5.2746800,22.416000 5.5426200,23.650500 7.0978700,24.212800 z "
+ style="fill:url(#linearGradient1978);stroke-width:1.4172200;stroke-linecap:round" />
+ <g
+ id="g1876">
+ <path
+ id="path1877"
+ d="M 13.464600,20.842300 L 38.049400,30.017400 C 39.328100,30.479700 41.166700,30.209000 42.171800,29.410500 L 48.750800,23.746900 C 49.755900,22.948400 49.535600,21.933400 48.256900,21.471100 L 23.672100,12.296000 C 22.393400,11.833700 20.554800,12.104400 19.549700,12.902900 L 12.970700,18.566500 C 11.965600,19.365000 12.185900,20.380000 13.464600,20.842300 z "
+ style="fill:url(#linearGradient1979);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1878"
+ d="M 14.070400,20.920400 L 37.950400,29.668500 C 39.150000,30.102200 40.874900,29.848300 41.817900,29.099100 L 47.804500,23.826400 C 48.747400,23.077300 48.540700,22.125000 47.341100,21.691300 L 23.461100,12.943100 C 22.261500,12.509400 20.536600,12.763400 19.593600,13.512500 L 13.607000,18.785300 C 12.664100,19.534400 12.870800,20.486700 14.070400,20.920400 z "
+ style="fill:url(#linearGradient1980);stroke-width:1.4172200;stroke-linecap:round" />
+ </g>
+ <g
+ id="g1879">
+ <path
+ id="path1880"
+ d="M 32.490900,15.274200 L 39.306300,17.754800 C 39.785000,17.927800 40.473300,17.826500 40.849500,17.527600 L 43.312400,15.407400 C 43.688600,15.108500 43.606200,14.728500 43.127500,14.555400 L 36.312100,12.074900 C 35.833400,11.901800 35.145100,12.003200 34.768900,12.302100 L 32.306000,14.422300 C 31.929800,14.721200 32.012300,15.101200 32.490900,15.274200 z "
+ style="fill:url(#linearGradient1981);stroke-width:0.53053498;stroke-linecap:round" />
+ <path
+ id="path1881"
+ d="M 32.717700,15.303400 L 39.230100,17.690000 C 39.679200,17.852300 40.324900,17.757200 40.677900,17.476800 L 42.919000,15.503000 C 43.272000,15.222500 43.194600,14.866000 42.745500,14.703700 L 36.233100,12.317200 C 35.784000,12.154800 35.138300,12.249900 34.785300,12.530300 L 32.544300,14.504100 C 32.191300,14.784600 32.268600,15.141100 32.717700,15.303400 z "
+ style="fill:url(#linearGradient1982);stroke-width:0.53053498;stroke-linecap:round" />
+ </g>
+ </g>
+ <g
+ id="g1882">
+ <path
+ id="path1883"
+ d="M 30.744300,59.532300 C 30.432000,59.561000 30.065700,59.511600 29.733000,59.388200 L 2.6736100,49.864700 C 1.3429500,49.370900 0.47013600,47.759100 0.72888100,46.280700 L 4.6183300,24.096900 C 4.8770800,22.618500 8.1261900,20.721200 10.180300,21.130800 L 37.912100,31.409100 C 39.242700,31.902900 40.115500,33.535300 39.856800,35.013700 L 35.967300,57.197500 C 35.708600,58.675900 31.475300,59.465200 30.744300,59.532300 z "
+ style="fill:url(#linearGradient1983);stroke-width:1.3453600;stroke-linecap:round" />
+ <path
+ id="path1884"
+ d="M 5.8649200,47.969300 C 4.8047100,47.575900 4.1239100,46.306800 4.3327900,45.113300 L 8.1133500,23.511600 C 8.3222400,22.318100 9.3371300,21.678200 10.397300,22.071700 L 37.542200,32.144700 C 38.602400,32.538100 39.283200,33.807200 39.074300,35.000800 L 35.293700,56.602400 C 35.084800,57.796000 34.069900,58.435800 33.009700,58.042400 L 5.8649200,47.969300 z M 7.4174400,45.999000 L 32.294200,55.230400 C 32.787200,55.413300 33.252900,55.119800 33.350000,54.564700 L 36.690800,35.475900 C 36.787900,34.920800 36.475600,34.338600 35.982600,34.155600 L 11.105800,24.924200 C 10.612800,24.741200 10.147100,25.034800 10.050000,25.589800 L 6.7091900,44.678700 C 6.6120500,45.233700 6.9244100,45.816000 7.4174400,45.999000 z "
+ style="fill:url(#linearGradient1984);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ d="M 10.223700,24.596900 L 6.5353900,45.671700 L 33.176200,55.557700 L 36.864500,34.482900 L 10.223700,24.596900 z "
+ id="path1885"
+ style="fill:url(#linearGradient1985);fill-opacity:1.0000000;stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ d="M 10.801900,25.674700 L 7.3972700,45.128300 L 32.598000,54.479900 L 36.002700,35.026300 L 10.801900,25.674700 z "
+ id="path1886"
+ style="fill:url(#radialGradient1986);fill-opacity:1.0000000;stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ id="path1887"
+ d="M 5.9500300,47.483000 C 5.1201500,47.175000 4.6013000,46.207800 4.7648000,45.273600 L 6.7685700,33.824300 C 7.2127900,33.798400 7.6853400,33.800900 8.1532700,33.798600 L 6.2771800,44.518400 C 6.1352700,45.329200 6.6120600,46.218000 7.3323200,46.485300 L 32.209100,55.716700 C 32.929300,55.984000 33.640100,55.535900 33.782000,54.725000 L 35.782200,43.296000 C 36.209100,43.599200 36.597100,43.914000 37.000300,44.222800 L 34.861700,56.442100 C 34.698200,57.376400 33.924700,57.864000 33.094900,57.556000 L 5.9500300,47.483000 z "
+ style="fill:url(#linearGradient1987);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ id="path1888"
+ d="M 9.0398900,44.874600 C 8.6107900,44.715400 8.3262800,44.201500 8.4081800,43.733500 L 10.491000,31.832300 C 12.953300,36.964200 21.062000,43.028400 31.308400,46.830700 C 31.906000,47.052400 32.479700,47.206700 33.069400,47.405300 L 32.168900,52.550800 C 32.087000,53.018700 31.668300,53.271700 31.239200,53.112500 L 9.0398900,44.874600 z "
+ style="opacity:0.78313255;fill:url(#linearGradient1988);stroke-width:1.3751301;stroke-linecap:round" />
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/wifi/pixmaps/all_alone.png b/wifi/pixmaps/all_alone.png
new file mode 100644
index 00000000..380f8ba0
--- /dev/null
+++ b/wifi/pixmaps/all_alone.png
Binary files differ
diff --git a/wifi/pixmaps/all_alone.svg b/wifi/pixmaps/all_alone.svg
new file mode 100644
index 00000000..88547f08
--- /dev/null
+++ b/wifi/pixmaps/all_alone.svg
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- This file was created using the SVG export filter from Karbon14, a free vector drawing app. -->
+<!-- It is part of koffice, the free, integrated office suite for KDE (http://www.koffice.org/). -->
+<svg
+ width="60px"
+ height="60px"
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.40"
+ sodipodi:docname="all_alone.svgz"
+ sodipodi:docbase="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ inkscape:export-filename="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics/all_alone.svgz.png"
+ inkscape:export-xdpi="72.000000"
+ inkscape:export-ydpi="72.000000">
+ <metadata
+ id="metadata64">
+ <rdf:RDF
+ id="RDF65">
+ <cc:Work
+ rdf:about=""
+ id="Work66">
+ <dc:format
+ id="format67">image/svg+xml</dc:format>
+ <dc:type
+ id="type69"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="4.9119412"
+ inkscape:cx="50.130660"
+ inkscape:cy="36.091618"
+ inkscape:window-width="640"
+ inkscape:window-height="538"
+ inkscape:window-x="115"
+ inkscape:window-y="139"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs2">
+ <linearGradient
+ id="defitem0"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop4" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop5" />
+ </linearGradient>
+ <linearGradient
+ id="defitem1"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop7" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop8" />
+ </linearGradient>
+ <linearGradient
+ id="defitem2"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop10" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop11" />
+ </linearGradient>
+ <linearGradient
+ id="defitem3"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop13" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="defitem4"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop16" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop17" />
+ </linearGradient>
+ <linearGradient
+ id="defitem5"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop19" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop20" />
+ </linearGradient>
+ <linearGradient
+ id="defitem6"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop22" />
+ <stop
+ stop-color="#646464"
+ offset="1"
+ stop-opacity="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="defitem7"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop25" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop26" />
+ </linearGradient>
+ <linearGradient
+ id="defitem8"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.0818"
+ y2="39.6786">
+ <stop
+ stop-color="#2a2a2a"
+ offset="0"
+ stop-opacity="1"
+ id="stop28" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop29" />
+ </linearGradient>
+ <radialGradient
+ id="defitem9"
+ gradientUnits="userSpaceOnUse"
+ cx="24.5089"
+ cy="28.1715"
+ fx="44.128"
+ fy="61.0801"
+ r="18.9697">
+ <stop
+ stop-color="#a3cdeb"
+ offset="0"
+ stop-opacity="1"
+ id="stop31" />
+ <stop
+ stop-color="#4189dd"
+ offset="1"
+ stop-opacity="1"
+ id="stop32" />
+ </radialGradient>
+ <linearGradient
+ id="defitem10"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop34" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop35" />
+ </linearGradient>
+ <linearGradient
+ id="defitem11"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop37" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop38" />
+ </linearGradient>
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1741"
+ gradientUnits="userSpaceOnUse"
+ cx="31.862440"
+ cy="30.222889"
+ fx="31.862440"
+ fy="30.222889"
+ r="19.767193" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1742"
+ gradientUnits="userSpaceOnUse"
+ x1="20.910299"
+ y1="40.476101"
+ x2="23.115303"
+ y2="38.855164" />
+ </defs>
+ <g
+ transform="scale(1, -1) translate(0, -60)"
+ id="g39">
+ <path
+ id="path87"
+ fill="#000000"
+ d="" />
+ <g
+ id="g41">
+ <g
+ id="g42" />
+ <g
+ id="g45">
+ <g
+ id="g46">
+ <path
+ id="path1660"
+ fill="#7e7e7e"
+ stroke-width="2.27341"
+ stroke-linecap="round"
+ d="M35.8897 35.4273C35.3429 35.3919 34.7945 35.2886 34.2797 35.1025L6.68184 25.0095C5.8918 24.7239 5.49918 24.1704 5.23613 23.6023C4.97308 23.0342 4.82425 22.3942 4.77613 21.7622C4.72801 21.1301 4.7778 20.4905 4.90756 19.922C5.03731 19.3536 5.20266 18.8509 5.76184 18.4066L21.3587 5.16197C22.9615 3.88858 25.5038 3.53432 27.5358 4.26895L55.1337 14.362C55.8681 14.6275 56.2525 15.1511 56.4809 15.6609C56.7092 16.1708 56.785 16.7096 56.8094 17.2575C56.8339 17.8054 56.8048 18.3647 56.678 18.8541C56.5564 19.3235 56.3826 19.7344 55.9223 20.126L55.9552 20.153L40.4897 34.1825L40.4569 34.1825L40.424 34.2095C39.6192 34.8489 38.5967 35.2165 37.5326 35.3731C36.9985 35.4518 36.4366 35.4626 35.8897 35.4273ZM37.1383 33.6412C37.8595 33.5351 38.4937 33.3056 38.9126 32.9918L54.378 18.9624L54.4109 18.9624L54.4437 18.9353C54.3446 19.0141 54.5252 18.795 54.608 18.4753C54.6908 18.1555 54.7249 17.7486 54.7066 17.3387C54.6883 16.9288 54.627 16.5189 54.5094 16.2563C54.3918 15.9937 54.2451 15.9191 54.2794 15.9315L26.6815 5.83849C25.6031 5.44861 23.6465 5.73776 22.8044 6.40677L7.20755 19.6514C7.26931 19.6024 7.06702 19.8548 6.97755 20.2468C6.88808 20.6387 6.84108 21.1561 6.87898 21.6539C6.91688 22.1517 7.0523 22.6446 7.20755 22.9799C7.3628 23.3152 7.54853 23.4444 7.53612 23.44L35.134 33.533C35.6597 33.7231 36.394 33.7508 37.1383 33.6412Z" />
+ <path
+ id="path1661"
+ fill="url(#defitem0)"
+ stroke-width="1.41722"
+ stroke-linecap="round"
+ d="M37.0287 34.5072C36.1224 34.6406 35.1777 34.5989 34.4001 34.3178L7.10898 24.2247C5.55373 23.6625 5.48986 19.8194 6.48469 19.029L22.2381 5.91597C23.4606 4.94478 25.71 4.62307 27.2653 5.18533L54.5564 15.2784C55.9565 15.7846 55.8385 19.0362 55.0164 19.6893L39.3944 33.5871C38.7831 34.0727 37.9349 34.3738 37.0287 34.5072Z" />
+ <path
+ id="path1662"
+ fill="url(#defitem1)"
+ stroke-width="1.41722"
+ stroke-linecap="round"
+ d="M7.09787 24.2128L34.2322 34.3765C35.7875 34.9388 38.2538 34.6727 39.4762 33.7015L54.3957 20.4223C55.6181 19.4511 55.3502 18.2166 53.7949 17.6543L26.6606 7.49062C25.1053 6.92836 22.8691 7.25757 21.6467 8.22877L6.49711 21.4448C5.27468 22.416 5.54262 23.6505 7.09787 24.2128Z" />
+ <g
+ id="g50">
+ <path
+ id="path1664"
+ fill="url(#defitem2)"
+ stroke-width="1.41722"
+ stroke-linecap="round"
+ d="M13.4646 20.8423L38.0494 30.0174C39.3281 30.4797 41.1667 30.209 42.1718 29.4105L48.7508 23.7469C49.7559 22.9484 49.5356 21.9334 48.2569 21.4711L23.6721 12.296C22.3934 11.8337 20.5548 12.1044 19.5497 12.9029L12.9707 18.5665C11.9656 19.365 12.1859 20.38 13.4646 20.8423Z" />
+ <path
+ id="path1665"
+ fill="url(#defitem3)"
+ stroke-width="1.41722"
+ stroke-linecap="round"
+ d="M14.0704 20.9204L37.9504 29.6685C39.15 30.1022 40.8749 29.8483 41.8179 29.0991L47.8045 23.8264C48.7474 23.0773 48.5407 22.125 47.3411 21.6913L23.4611 12.9431C22.2615 12.5094 20.5366 12.7634 19.5936 13.5125L13.607 18.7853C12.6641 19.5344 12.8708 20.4867 14.0704 20.9204Z" />
+ </g>
+ <g
+ id="g53">
+ <path
+ id="path3151"
+ fill="url(#defitem4)"
+ stroke-width="0.530535"
+ stroke-linecap="round"
+ d="M32.4909 15.2742L39.3063 17.7548C39.785 17.9278 40.4733 17.8265 40.8495 17.5276L43.3124 15.4074C43.6886 15.1085 43.6062 14.7285 43.1275 14.5554L36.3121 12.0749C35.8334 11.9018 35.1451 12.0032 34.7689 12.3021L32.306 14.4223C31.9298 14.7212 32.0123 15.1012 32.4909 15.2742Z" />
+ <path
+ id="path3152"
+ fill="url(#defitem5)"
+ stroke-width="0.530535"
+ stroke-linecap="round"
+ d="M32.7177 15.3034L39.2301 17.69C39.6792 17.8523 40.3249 17.7572 40.6779 17.4768L42.919 15.503C43.272 15.2225 43.1946 14.866 42.7455 14.7037L36.2331 12.3172C35.784 12.1548 35.1383 12.2499 34.7853 12.5303L32.5443 14.5041C32.1913 14.7846 32.2686 15.1411 32.7177 15.3034Z" />
+ </g>
+ </g>
+ <g
+ id="g56">
+ <path
+ id="path1670"
+ fill="url(#defitem6)"
+ stroke-width="1.34536"
+ stroke-linecap="round"
+ d="M30.7443 59.5323C30.432 59.561 30.0657 59.5116 29.733 59.3882L2.67361 49.8647C1.34295 49.3709 0.470136 47.7591 0.728881 46.2807L4.61833 24.0969C4.87708 22.6185 8.12619 20.7212 10.1803 21.1308L37.9121 31.4091C39.2427 31.9029 40.1155 33.5353 39.8568 35.0137L35.9673 57.1975C35.7086 58.6759 31.4753 59.4652 30.7443 59.5323Z" />
+ <path
+ id="path1671"
+ fill="url(#defitem7)"
+ stroke-width="1.37513"
+ stroke-linecap="round"
+ d="M5.86492 47.9693C4.80471 47.5759 4.12391 46.3068 4.33279 45.1133L8.11335 23.5116C8.32224 22.3181 9.33713 21.6782 10.3973 22.0717L37.5422 32.1447C38.6024 32.5381 39.2832 33.8072 39.0743 35.0008L35.2937 56.6024C35.0848 57.796 34.0699 58.4358 33.0097 58.0424L5.86492 47.9693ZM7.41744 45.999L32.2942 55.2304C32.7872 55.4133 33.2529 55.1198 33.35 54.5647L36.6908 35.4759C36.7879 34.9208 36.4756 34.3386 35.9826 34.1556L11.1058 24.9242C10.6128 24.7412 10.1471 25.0348 10.05 25.5898L6.70919 44.6787C6.61205 45.2337 6.92441 45.816 7.41744 45.999Z" />
+ <path
+ fill="url(#defitem8)"
+ stroke-width="1.30139"
+ stroke-linecap="round"
+ d="M10.2237 24.5969L6.53539 45.6717L33.1762 55.5577L36.8645 34.4829L10.2237 24.5969Z"
+ id="path59"
+ style="fill:url(#linearGradient1742);fill-opacity:1.0;" />
+ <path
+ fill="url(#defitem9)"
+ stroke-width="1.30139"
+ stroke-linecap="round"
+ d="M10.8019 25.6747L7.39727 45.1283L32.598 54.4799L36.0027 35.0263L10.8019 25.6747Z"
+ id="path60"
+ style="fill:url(#radialGradient1741);fill-opacity:1.0;" />
+ <path
+ id="path1674"
+ fill="url(#defitem10)"
+ stroke-width="1.37513"
+ stroke-linecap="round"
+ d="M5.95003 47.483C5.12015 47.175 4.6013 46.2078 4.7648 45.2736L6.76857 33.8243C7.21279 33.7984 7.68534 33.8009 8.15327 33.7986L6.27718 44.5184C6.13527 45.3292 6.61206 46.218 7.33232 46.4853L32.2091 55.7167C32.9293 55.984 33.6401 55.5359 33.782 54.725L35.7822 43.296C36.2091 43.5992 36.5971 43.914 37.0003 44.2228L34.8617 56.4421C34.6982 57.3764 33.9247 57.864 33.0949 57.556L5.95003 47.483Z" />
+ <path
+ id="path1675"
+ fill="url(#defitem11)"
+ stroke-width="1.37513"
+ stroke-linecap="round"
+ d="M9.03989 44.8746C8.61079 44.7154 8.32628 44.2015 8.40818 43.7335L10.491 31.8323C12.9533 36.9642 21.062 43.0284 31.3084 46.8307C31.906 47.0524 32.4797 47.2067 33.0694 47.4053L32.1689 52.5508C32.087 53.0187 31.6683 53.2717 31.2392 53.1125L9.03989 44.8746Z"
+ style="opacity:0.78313253;" />
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/wifi/pixmaps/ap_connect.png b/wifi/pixmaps/ap_connect.png
new file mode 100644
index 00000000..bfcd1075
--- /dev/null
+++ b/wifi/pixmaps/ap_connect.png
Binary files differ
diff --git a/wifi/pixmaps/ap_connect.svg b/wifi/pixmaps/ap_connect.svg
new file mode 100644
index 00000000..569ac48c
--- /dev/null
+++ b/wifi/pixmaps/ap_connect.svg
@@ -0,0 +1,1105 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- This file was created using the SVG export filter from Karbon14, a free vector drawing app. -->
+<!-- It is part of koffice, the free, integrated office suite for KDE (http://www.koffice.org/). -->
+<svg
+ width="60px"
+ height="35.000000pt"
+ id="svg1"
+ sodipodi:version="0.32"
+ inkscape:version="0.40"
+ sodipodi:docname="ap_connect.svgz"
+ sodipodi:docbase="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics"
+ inkscape:export-filename="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics/ap_connect.png"
+ inkscape:export-xdpi="99.000000"
+ inkscape:export-ydpi="99.000000"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <metadata
+ id="metadata134">
+ <rdf:RDF
+ id="RDF135">
+ <cc:Work
+ rdf:about=""
+ id="Work136">
+ <dc:format
+ id="format137">image/svg+xml</dc:format>
+ <dc:type
+ id="type139"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="3.4732669"
+ inkscape:cx="93.441743"
+ inkscape:cy="18.645880"
+ inkscape:window-width="777"
+ inkscape:window-height="538"
+ inkscape:window-x="239"
+ inkscape:window-y="153"
+ inkscape:current-layer="svg1" />
+ <defs
+ id="defs2">
+ <linearGradient
+ id="defitem0"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop4" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop5" />
+ </linearGradient>
+ <linearGradient
+ id="defitem1"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop7" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop8" />
+ </linearGradient>
+ <linearGradient
+ id="defitem2"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop10" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop11" />
+ </linearGradient>
+ <linearGradient
+ id="defitem3"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop13" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="defitem4"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop16" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop17" />
+ </linearGradient>
+ <linearGradient
+ id="defitem5"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop19" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop20" />
+ </linearGradient>
+ <linearGradient
+ id="defitem6"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop22" />
+ <stop
+ stop-color="#646464"
+ offset="1"
+ stop-opacity="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="defitem7"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop25" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop26" />
+ </linearGradient>
+ <linearGradient
+ id="defitem8"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.1153"
+ y2="38.8552">
+ <stop
+ stop-color="#2a2a2a"
+ offset="0"
+ stop-opacity="1"
+ id="stop28" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop29" />
+ </linearGradient>
+ <radialGradient
+ id="defitem9"
+ gradientUnits="userSpaceOnUse"
+ cx="31.8624"
+ cy="30.2229"
+ fx="31.8624"
+ fy="30.2229"
+ r="19.7672">
+ <stop
+ stop-color="#a3cdeb"
+ offset="0"
+ stop-opacity="1"
+ id="stop31" />
+ <stop
+ stop-color="#4189dd"
+ offset="1"
+ stop-opacity="1"
+ id="stop32" />
+ </radialGradient>
+ <linearGradient
+ id="defitem10"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop34" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop35" />
+ </linearGradient>
+ <linearGradient
+ id="defitem11"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop37" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop38" />
+ </linearGradient>
+ <linearGradient
+ id="defitem12"
+ gradientUnits="userSpaceOnUse"
+ x1="40.4771"
+ y1="26.4264"
+ x2="40.4771"
+ y2="12.8913">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop40" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop41" />
+ </linearGradient>
+ <radialGradient
+ id="defitem13"
+ gradientUnits="userSpaceOnUse"
+ cx="40.5327"
+ cy="-6.37103"
+ fx="40.5327"
+ fy="-6.37103"
+ r="9.8821">
+ <stop
+ stop-color="#cdcdcd"
+ offset="0"
+ stop-opacity="1"
+ id="stop43" />
+ <stop
+ stop-color="#000000"
+ offset="1"
+ stop-opacity="1"
+ id="stop44" />
+ </radialGradient>
+ <radialGradient
+ id="defitem14"
+ gradientUnits="userSpaceOnUse"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop46" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop47" />
+ </radialGradient>
+ <radialGradient
+ id="defitem15"
+ gradientUnits="userSpaceOnUse"
+ cx="24.0401"
+ cy="16.8777"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop49" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop50" />
+ </radialGradient>
+ <radialGradient
+ id="defitem16"
+ gradientUnits="userSpaceOnUse"
+ cx="31.6735"
+ cy="24.0431"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.458">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop52" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop53" />
+ </radialGradient>
+ <radialGradient
+ id="defitem17"
+ gradientUnits="userSpaceOnUse"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871">
+ <stop
+ stop-color="#00f200"
+ offset="0"
+ stop-opacity="1"
+ id="stop55" />
+ <stop
+ stop-color="#00a300"
+ offset="1"
+ stop-opacity="1"
+ id="stop56" />
+ </radialGradient>
+ <linearGradient
+ id="defitem18"
+ gradientUnits="userSpaceOnUse"
+ x1="40.9397"
+ y1="6.4937"
+ x2="40.8771"
+ y2="-1.15177">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop58" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop59" />
+ </linearGradient>
+ <linearGradient
+ id="defitem19"
+ gradientUnits="userSpaceOnUse"
+ x1="40.657"
+ y1="30.2051"
+ x2="40.7319"
+ y2="28.1208">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop61" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop62" />
+ </linearGradient>
+ <linearGradient
+ id="defitem20"
+ gradientUnits="userSpaceOnUse"
+ x1="40.8941"
+ y1="25.4297"
+ x2="40.8794"
+ y2="26.8204">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop64" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop65" />
+ </linearGradient>
+ <radialGradient
+ id="defitem21"
+ gradientUnits="userSpaceOnUse"
+ cx="48.8515"
+ cy="-3.19751"
+ fx="48.8515"
+ fy="-3.19751"
+ r="1.2989">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop67" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop68" />
+ </radialGradient>
+ <linearGradient
+ id="defitem22"
+ gradientUnits="userSpaceOnUse"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop70" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop71" />
+ </linearGradient>
+ <linearGradient
+ id="defitem23"
+ gradientUnits="userSpaceOnUse"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop73" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop74" />
+ </linearGradient>
+ <linearGradient
+ id="defitem24"
+ gradientUnits="userSpaceOnUse"
+ x1="31.1293"
+ y1="36.6559"
+ x2="31.1293"
+ y2="30.6237">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop76" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop77" />
+ </linearGradient>
+ <linearGradient
+ id="defitem25"
+ gradientUnits="userSpaceOnUse"
+ x1="23.6276"
+ y1="43.4914"
+ x2="23.6276"
+ y2="30.9297">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop79" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117021"
+ id="stop80" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem12"
+ id="linearGradient1241"
+ gradientUnits="userSpaceOnUse"
+ x1="40.4771"
+ y1="26.4264"
+ x2="40.4771"
+ y2="12.8913" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem13"
+ id="radialGradient1242"
+ gradientUnits="userSpaceOnUse"
+ cx="40.5327"
+ cy="-6.37103"
+ fx="40.5327"
+ fy="-6.37103"
+ r="9.8821" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem14"
+ id="radialGradient1243"
+ gradientUnits="userSpaceOnUse"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem17"
+ id="radialGradient1246"
+ gradientUnits="userSpaceOnUse"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem18"
+ id="linearGradient1247"
+ gradientUnits="userSpaceOnUse"
+ x1="40.9397"
+ y1="6.4937"
+ x2="40.8771"
+ y2="-1.15177" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem19"
+ id="linearGradient1248"
+ gradientUnits="userSpaceOnUse"
+ x1="40.657"
+ y1="30.2051"
+ x2="40.7319"
+ y2="28.1208" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem20"
+ id="linearGradient1249"
+ gradientUnits="userSpaceOnUse"
+ x1="40.8941"
+ y1="25.4297"
+ x2="40.8794"
+ y2="26.8204" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem21"
+ id="radialGradient1250"
+ gradientUnits="userSpaceOnUse"
+ cx="48.8515"
+ cy="-3.19751"
+ fx="48.8515"
+ fy="-3.19751"
+ r="1.2989" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem22"
+ id="linearGradient1251"
+ gradientUnits="userSpaceOnUse"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem23"
+ id="linearGradient1252"
+ gradientUnits="userSpaceOnUse"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1255"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1256"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1257"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1258"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1259"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1260"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1261"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1262"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1263"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.1153"
+ y2="38.8552" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1264"
+ gradientUnits="userSpaceOnUse"
+ cx="31.8624"
+ cy="30.2229"
+ fx="31.8624"
+ fy="30.2229"
+ r="19.7672" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1265"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1266"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1438"
+ gradientUnits="userSpaceOnUse"
+ x1="13.0416"
+ y1="33.4683"
+ x2="35.7962"
+ y2="12.9124" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem1"
+ id="linearGradient1439"
+ gradientUnits="userSpaceOnUse"
+ x1="22.5629"
+ y1="27.4437"
+ x2="40.2475"
+ y2="9.38352" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem2"
+ id="linearGradient1440"
+ gradientUnits="userSpaceOnUse"
+ x1="43.9104"
+ y1="16.4535"
+ x2="19.2773"
+ y2="14.5777" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem3"
+ id="linearGradient1441"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9082"
+ y1="22.3925"
+ x2="26.2153"
+ y2="15.4384" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem4"
+ id="linearGradient1442"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem5"
+ id="linearGradient1443"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1444"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem7"
+ id="linearGradient1445"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient1446"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.1153"
+ y2="38.8552" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem9"
+ id="radialGradient1447"
+ gradientUnits="userSpaceOnUse"
+ cx="31.8624"
+ cy="30.2229"
+ fx="31.8624"
+ fy="30.2229"
+ r="19.7672" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem10"
+ id="linearGradient1448"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient1449"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem12"
+ id="linearGradient1450"
+ gradientUnits="userSpaceOnUse"
+ x1="40.4771"
+ y1="26.4264"
+ x2="40.4771"
+ y2="12.8913" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem13"
+ id="radialGradient1451"
+ gradientUnits="userSpaceOnUse"
+ cx="40.5327"
+ cy="-6.37103"
+ fx="40.5327"
+ fy="-6.37103"
+ r="9.8821" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem14"
+ id="radialGradient1452"
+ gradientUnits="userSpaceOnUse"
+ cx="58.403"
+ cy="16.0261"
+ fx="9.67146"
+ fy="50.7123"
+ r="18.0973" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem17"
+ id="radialGradient1453"
+ gradientUnits="userSpaceOnUse"
+ cx="49.8929"
+ cy="23.6043"
+ fx="9.67146"
+ fy="50.7123"
+ r="7.7871" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem18"
+ id="linearGradient1454"
+ gradientUnits="userSpaceOnUse"
+ x1="40.9397"
+ y1="6.4937"
+ x2="40.8771"
+ y2="-1.15177" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem19"
+ id="linearGradient1455"
+ gradientUnits="userSpaceOnUse"
+ x1="40.657"
+ y1="30.2051"
+ x2="40.7319"
+ y2="28.1208" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem20"
+ id="linearGradient1456"
+ gradientUnits="userSpaceOnUse"
+ x1="40.8941"
+ y1="25.4297"
+ x2="40.8794"
+ y2="26.8204" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#defitem21"
+ id="radialGradient1457"
+ gradientUnits="userSpaceOnUse"
+ cx="48.8515"
+ cy="-3.19751"
+ fx="48.8515"
+ fy="-3.19751"
+ r="1.2989" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem22"
+ id="linearGradient1458"
+ gradientUnits="userSpaceOnUse"
+ x1="56.5467"
+ y1="43.2785"
+ x2="56.5467"
+ y2="34.3363" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem23"
+ id="linearGradient1459"
+ gradientUnits="userSpaceOnUse"
+ x1="49.4575"
+ y1="36.4366"
+ x2="49.4575"
+ y2="31.9398" />
+ </defs>
+ <g
+ id="g1390"
+ transform="translate(0.000000,-13.99650)">
+ <g
+ id="g82"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,22.45389,52.73176)">
+ <path
+ id="path87"
+ d=""
+ style="fill:#000000" />
+ <g
+ id="g84">
+ <g
+ id="g85" />
+ <g
+ id="g88">
+ <g
+ id="g89">
+ <path
+ id="path1660"
+ d="M 35.889700,35.427300 C 35.342900,35.391900 34.794500,35.288600 34.279700,35.102500 L 6.6818400,25.009500 C 5.8918000,24.723900 5.4991800,24.170400 5.2361300,23.602300 C 4.9730800,23.034200 4.8242500,22.394200 4.7761300,21.762200 C 4.7280100,21.130100 4.7778000,20.490500 4.9075600,19.922000 C 5.0373100,19.353600 5.2026600,18.850900 5.7618400,18.406600 L 21.358700,5.1619700 C 22.961500,3.8885800 25.503800,3.5343200 27.535800,4.2689500 L 55.133700,14.362000 C 55.868100,14.627500 56.252500,15.151100 56.480900,15.660900 C 56.709200,16.170800 56.785000,16.709600 56.809400,17.257500 C 56.833900,17.805400 56.804800,18.364700 56.678000,18.854100 C 56.556400,19.323500 56.382600,19.734400 55.922300,20.126000 L 55.955200,20.153000 L 40.489700,34.182500 L 40.456900,34.182500 L 40.424000,34.209500 C 39.619200,34.848900 38.596700,35.216500 37.532600,35.373100 C 36.998500,35.451800 36.436600,35.462600 35.889700,35.427300 z M 37.138300,33.641200 C 37.859500,33.535100 38.493700,33.305600 38.912600,32.991800 L 54.378000,18.962400 L 54.410900,18.962400 L 54.443700,18.935300 C 54.344600,19.014100 54.525200,18.795000 54.608000,18.475300 C 54.690800,18.155500 54.724900,17.748600 54.706600,17.338700 C 54.688300,16.928800 54.627000,16.518900 54.509400,16.256300 C 54.391800,15.993700 54.245100,15.919100 54.279400,15.931500 L 26.681500,5.8384900 C 25.603100,5.4486100 23.646500,5.7377600 22.804400,6.4067700 L 7.2075500,19.651400 C 7.2693100,19.602400 7.0670200,19.854800 6.9775500,20.246800 C 6.8880800,20.638700 6.8410800,21.156100 6.8789800,21.653900 C 6.9168800,22.151700 7.0523000,22.644600 7.2075500,22.979900 C 7.3628000,23.315200 7.5485300,23.444400 7.5361200,23.440000 L 35.134000,33.533000 C 35.659700,33.723100 36.394000,33.750800 37.138300,33.641200 z "
+ style="fill:#7e7e7e;stroke-width:2.2734101;stroke-linecap:round" />
+ <path
+ id="path1661"
+ d="M 37.028700,34.507200 C 36.122400,34.640600 35.177700,34.598900 34.400100,34.317800 L 7.1089800,24.224700 C 5.5537300,23.662500 5.4898600,19.819400 6.4846900,19.029000 L 22.238100,5.9159700 C 23.460600,4.9447800 25.710000,4.6230700 27.265300,5.1853300 L 54.556400,15.278400 C 55.956500,15.784600 55.838500,19.036200 55.016400,19.689300 L 39.394400,33.587100 C 38.783100,34.072700 37.934900,34.373800 37.028700,34.507200 z "
+ style="fill:url(#linearGradient1438);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1662"
+ d="M 7.0978700,24.212800 L 34.232200,34.376500 C 35.787500,34.938800 38.253800,34.672700 39.476200,33.701500 L 54.395700,20.422300 C 55.618100,19.451100 55.350200,18.216600 53.794900,17.654300 L 26.660600,7.4906200 C 25.105300,6.9283600 22.869100,7.2575700 21.646700,8.2287700 L 6.4971100,21.444800 C 5.2746800,22.416000 5.5426200,23.650500 7.0978700,24.212800 z "
+ style="fill:url(#linearGradient1439);stroke-width:1.4172200;stroke-linecap:round" />
+ <g
+ id="g93">
+ <path
+ id="path1664"
+ d="M 13.464600,20.842300 L 38.049400,30.017400 C 39.328100,30.479700 41.166700,30.209000 42.171800,29.410500 L 48.750800,23.746900 C 49.755900,22.948400 49.535600,21.933400 48.256900,21.471100 L 23.672100,12.296000 C 22.393400,11.833700 20.554800,12.104400 19.549700,12.902900 L 12.970700,18.566500 C 11.965600,19.365000 12.185900,20.380000 13.464600,20.842300 z "
+ style="fill:url(#linearGradient1440);stroke-width:1.4172200;stroke-linecap:round" />
+ <path
+ id="path1665"
+ d="M 14.070400,20.920400 L 37.950400,29.668500 C 39.150000,30.102200 40.874900,29.848300 41.817900,29.099100 L 47.804500,23.826400 C 48.747400,23.077300 48.540700,22.125000 47.341100,21.691300 L 23.461100,12.943100 C 22.261500,12.509400 20.536600,12.763400 19.593600,13.512500 L 13.607000,18.785300 C 12.664100,19.534400 12.870800,20.486700 14.070400,20.920400 z "
+ style="fill:url(#linearGradient1441);stroke-width:1.4172200;stroke-linecap:round" />
+ </g>
+ <g
+ id="g96">
+ <path
+ id="path3151"
+ d="M 32.490900,15.274200 L 39.306300,17.754800 C 39.785000,17.927800 40.473300,17.826500 40.849500,17.527600 L 43.312400,15.407400 C 43.688600,15.108500 43.606200,14.728500 43.127500,14.555400 L 36.312100,12.074900 C 35.833400,11.901800 35.145100,12.003200 34.768900,12.302100 L 32.306000,14.422300 C 31.929800,14.721200 32.012300,15.101200 32.490900,15.274200 z "
+ style="fill:url(#linearGradient1442);stroke-width:0.53053498;stroke-linecap:round" />
+ <path
+ id="path3152"
+ d="M 32.717700,15.303400 L 39.230100,17.690000 C 39.679200,17.852300 40.324900,17.757200 40.677900,17.476800 L 42.919000,15.503000 C 43.272000,15.222500 43.194600,14.866000 42.745500,14.703700 L 36.233100,12.317200 C 35.784000,12.154800 35.138300,12.249900 34.785300,12.530300 L 32.544300,14.504100 C 32.191300,14.784600 32.268600,15.141100 32.717700,15.303400 z "
+ style="fill:url(#linearGradient1443);stroke-width:0.53053498;stroke-linecap:round" />
+ </g>
+ </g>
+ <g
+ id="g99">
+ <path
+ id="path1670"
+ d="M 30.744300,59.532300 C 30.432000,59.561000 30.065700,59.511600 29.733000,59.388200 L 2.6736100,49.864700 C 1.3429500,49.370900 0.47013600,47.759100 0.72888100,46.280700 L 4.6183300,24.096900 C 4.8770800,22.618500 8.1261900,20.721200 10.180300,21.130800 L 37.912100,31.409100 C 39.242700,31.902900 40.115500,33.535300 39.856800,35.013700 L 35.967300,57.197500 C 35.708600,58.675900 31.475300,59.465200 30.744300,59.532300 z "
+ style="fill:url(#linearGradient1444);stroke-width:1.3453600;stroke-linecap:round" />
+ <path
+ id="path1671"
+ d="M 5.8649200,47.969300 C 4.8047100,47.575900 4.1239100,46.306800 4.3327900,45.113300 L 8.1133500,23.511600 C 8.3222400,22.318100 9.3371300,21.678200 10.397300,22.071700 L 37.542200,32.144700 C 38.602400,32.538100 39.283200,33.807200 39.074300,35.000800 L 35.293700,56.602400 C 35.084800,57.796000 34.069900,58.435800 33.009700,58.042400 L 5.8649200,47.969300 z M 7.4174400,45.999000 L 32.294200,55.230400 C 32.787200,55.413300 33.252900,55.119800 33.350000,54.564700 L 36.690800,35.475900 C 36.787900,34.920800 36.475600,34.338600 35.982600,34.155600 L 11.105800,24.924200 C 10.612800,24.741200 10.147100,25.034800 10.050000,25.589800 L 6.7091900,44.678700 C 6.6120500,45.233700 6.9244100,45.816000 7.4174400,45.999000 z "
+ style="fill:url(#linearGradient1445);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ id="path59"
+ d="M 10.223700,24.596900 L 6.5353900,45.671700 L 33.176200,55.557700 L 36.864500,34.482900 L 10.223700,24.596900 z "
+ style="fill:url(#linearGradient1446);stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ id="path60"
+ d="M 10.801900,25.674700 L 7.3972700,45.128300 L 32.598000,54.479900 L 36.002700,35.026300 L 10.801900,25.674700 z "
+ style="fill:url(#radialGradient1447);stroke-width:1.3013901;stroke-linecap:round" />
+ <path
+ id="path1674"
+ d="M 5.9500300,47.483000 C 5.1201500,47.175000 4.6013000,46.207800 4.7648000,45.273600 L 6.7685700,33.824300 C 7.2127900,33.798400 7.6853400,33.800900 8.1532700,33.798600 L 6.2771800,44.518400 C 6.1352700,45.329200 6.6120600,46.218000 7.3323200,46.485300 L 32.209100,55.716700 C 32.929300,55.984000 33.640100,55.535900 33.782000,54.725000 L 35.782200,43.296000 C 36.209100,43.599200 36.597100,43.914000 37.000300,44.222800 L 34.861700,56.442100 C 34.698200,57.376400 33.924700,57.864000 33.094900,57.556000 L 5.9500300,47.483000 z "
+ style="fill:url(#linearGradient1448);stroke-width:1.3751301;stroke-linecap:round" />
+ <path
+ id="path1675"
+ d="M 9.0398900,44.874600 C 8.6107900,44.715400 8.3262800,44.201500 8.4081800,43.733500 L 10.491000,31.832300 C 12.953300,36.964200 21.062000,43.028400 31.308400,46.830700 C 31.906000,47.052400 32.479700,47.206700 33.069400,47.405300 L 32.168900,52.550800 C 32.087000,53.018700 31.668300,53.271700 31.239200,53.112500 L 9.0398900,44.874600 z "
+ style="fill:url(#linearGradient1449);fill-opacity:0.78313297;stroke-width:1.3751301;stroke-linecap:round" />
+ </g>
+ </g>
+ </g>
+ </g>
+ <g
+ id="g1367">
+ <path
+ id="rect1702"
+ d="M 38.408800,27.199800 L 43.049500,27.199800 L 43.049500,6.7812000 L 38.408800,6.7812000 L 38.408800,27.199800 z "
+ style="fill:#000000;stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1727"
+ d="M 39.715100,27.199800 L 41.021300,27.199800 L 41.021300,7.6062000 L 39.715100,7.6062000 L 39.715100,27.199800 z "
+ style="fill:url(#linearGradient1450);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1693"
+ d="M 40.895600,8.0447000 C 35.100000,8.0447000 30.403800,2.9155000 30.403800,-3.4043300 C 30.403800,-5.9790800 35.100000,-8.0687400 40.895600,-8.0687400 C 46.691200,-8.0687400 51.402500,-5.9790800 51.402500,-3.4043300 C 51.402500,2.9155000 46.691200,8.0447000 40.895600,8.0447000 z "
+ style="fill:#000000;stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1071"
+ d="M 49.928800,-3.1594100 C 49.928800,-5.3738900 45.887900,-7.1690900 40.903200,-7.1690900 C 35.918500,-7.1690900 31.877600,-5.3738900 31.877600,-3.1594100 C 31.877600,-0.94492000 35.918500,0.85030000 40.903200,0.85030000 C 45.887900,0.85030000 49.928800,-0.94492000 49.928800,-3.1594100 z "
+ style="fill:#8c8c8c;stroke-width:0.88503402;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1731"
+ d="M 40.896500,7.0951000 C 35.783900,7.0951000 31.641300,2.5705000 31.641300,-3.0045000 C 31.641300,-5.2757900 35.783900,-7.1191500 40.896500,-7.1191500 C 46.009000,-7.1191500 50.165100,-5.2757900 50.165100,-3.0045000 C 50.165100,2.5705000 46.009000,7.0951000 40.896500,7.0951000 z "
+ style="fill:url(#radialGradient1451);fill-opacity:0.84739000;stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1715"
+ d="M 49.221500,48.685900 C 53.852100,44.514900 56.971300,37.101900 56.971300,28.592000 C 56.971300,20.082100 53.852100,12.669100 49.221500,8.4982000 C 57.048800,11.810700 62.540000,19.562300 62.540000,28.592000 C 62.540000,37.621700 57.048800,45.373300 49.221500,48.685900 z "
+ style="fill:#007e00;stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1717"
+ d="M 45.246300,39.280200 C 47.709500,37.061700 49.368600,33.118600 49.368600,28.592000 C 49.368600,24.065500 47.709500,20.122400 45.246300,17.903800 C 49.409800,19.665800 52.330700,23.789000 52.330700,28.592000 C 52.330700,33.395100 49.409800,37.518300 45.246300,39.280200 z "
+ style="fill:#007e00;stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1721"
+ d="M 53.659300,44.768800 C 56.350500,40.415300 58.002500,34.830300 58.002500,28.604100 C 58.002500,22.377900 56.350500,16.760100 53.659300,12.406600 C 58.422800,16.210000 61.496700,22.028900 61.496700,28.604100 C 61.496700,35.179200 58.422800,40.965500 53.659300,44.768800 z "
+ style="fill:url(#radialGradient1452);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1726"
+ d="M 47.715800,37.094600 C 49.061500,34.852000 49.887500,31.974900 49.887500,28.767600 C 49.887500,25.560200 49.061500,22.666400 47.715800,20.423700 C 50.097600,22.382900 51.634600,25.380500 51.634600,28.767600 C 51.634600,32.154700 50.097600,35.135400 47.715800,37.094600 z "
+ style="fill:url(#radialGradient1453);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1701"
+ d="M 43.977600,27.663900 C 43.977600,25.870800 42.540300,24.415500 40.769400,24.415500 C 38.998500,24.415500 37.561300,25.870800 37.561300,27.663900 C 37.561300,29.457000 38.998500,30.912300 40.769400,30.912300 C 42.540300,30.912300 43.977600,29.457000 43.977600,27.663900 z "
+ style="fill:#000000;stroke-width:1.6529100;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1730"
+ d="M 47.389300,1.9190000 C 47.389300,-0.71143000 44.501700,-2.8438000 40.939700,-2.8438000 C 37.377700,-2.8438000 34.490100,-0.71143000 34.490100,1.9190000 C 34.490100,4.5494000 37.377700,6.6817000 40.939700,6.6817000 C 44.501700,6.6817000 47.389300,4.5494000 47.389300,1.9190000 z "
+ style="fill:url(#linearGradient1454);stroke-width:3.3847101;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1733"
+ d="M 43.130000,28.871900 C 43.130000,28.084100 42.072500,27.444800 40.769400,27.444800 C 39.466400,27.444800 38.408800,28.084100 38.408800,28.871900 C 38.408800,29.659700 39.466400,30.299000 40.769400,30.299000 C 42.072500,30.299000 43.130000,29.659700 43.130000,28.871900 z "
+ style="fill:url(#linearGradient1455);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path1735"
+ d="M 38.604800,27.069200 C 38.567500,26.968500 38.408800,26.915500 38.408800,26.808000 C 38.408800,26.020200 39.457100,25.371100 40.760100,25.371100 C 42.063100,25.371100 43.144000,26.020200 43.144000,26.808000 C 43.144000,26.913800 42.984300,26.969900 42.948000,27.069200 C 42.718700,26.426300 41.891800,25.893600 40.760100,25.893600 C 39.630900,25.893600 38.836700,26.428400 38.604800,27.069200 z "
+ style="fill:url(#linearGradient1456);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path2396"
+ d="M 50.150300,-3.1975200 C 50.150300,-3.8826400 49.568400,-4.4386800 48.851500,-4.4386800 C 48.134500,-4.4386800 47.552600,-3.8826400 47.552600,-3.1975200 C 47.552600,-2.5124000 48.134500,-1.9563600 48.851500,-1.9563600 C 49.568400,-1.9563600 50.150300,-2.5124000 50.150300,-3.1975200 z "
+ style="fill:url(#radialGradient1457);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path2442"
+ d="M 53.659300,44.768800 C 56.350500,40.415300 58.002500,34.830300 58.002500,28.604100 C 58.002500,22.377900 56.350500,16.760100 53.659300,12.406600 C 58.422800,16.210000 61.496700,22.028900 61.496700,28.604100 C 61.496700,35.179200 58.422800,40.965500 53.659300,44.768800 z "
+ style="fill:url(#linearGradient1458);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ <path
+ id="path2444"
+ d="M 47.715800,37.094600 C 49.061500,34.852000 49.887500,31.974900 49.887500,28.767600 C 49.887500,25.560200 49.061500,22.666400 47.715800,20.423700 C 50.097600,22.382900 51.634600,25.380500 51.634600,28.767600 C 51.634600,32.154700 50.097600,35.135400 47.715800,37.094600 z "
+ style="fill:url(#linearGradient1459);stroke-width:2.0899899;stroke-linecap:round"
+ transform="matrix(0.623443,0.000000,0.000000,-0.623443,-17.70872,50.45930)" />
+ </g>
+ </g>
+</svg>
diff --git a/wifi/pixmaps/excellent.png b/wifi/pixmaps/excellent.png
new file mode 100644
index 00000000..cc830954
--- /dev/null
+++ b/wifi/pixmaps/excellent.png
Binary files differ
diff --git a/wifi/pixmaps/good.png b/wifi/pixmaps/good.png
new file mode 100644
index 00000000..9aac8acf
--- /dev/null
+++ b/wifi/pixmaps/good.png
Binary files differ
diff --git a/wifi/pixmaps/marginal.png b/wifi/pixmaps/marginal.png
new file mode 100644
index 00000000..ac852fcd
--- /dev/null
+++ b/wifi/pixmaps/marginal.png
Binary files differ
diff --git a/wifi/pixmaps/no_card.png b/wifi/pixmaps/no_card.png
new file mode 100644
index 00000000..bcc2331f
--- /dev/null
+++ b/wifi/pixmaps/no_card.png
Binary files differ
diff --git a/wifi/pixmaps/no_card.svg b/wifi/pixmaps/no_card.svg
new file mode 100644
index 00000000..7f4a0429
--- /dev/null
+++ b/wifi/pixmaps/no_card.svg
@@ -0,0 +1,885 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ width="48pt"
+ height="35.000000pt"
+ id="svg1101"
+ sodipodi:version="0.32"
+ inkscape:version="0.40"
+ sodipodi:docbase="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics"
+ sodipodi:docname="no_card.svgz"
+ inkscape:export-filename="/mnt/win_d/Graphics/kde/icons/kwifimanager/pics/no_card.png"
+ inkscape:export-xdpi="99.000000"
+ inkscape:export-ydpi="99.000000"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs
+ id="defs3">
+ <linearGradient
+ y2="25.928497"
+ x2="24.164434"
+ y1="5.2444587"
+ x1="10.139539"
+ gradientTransform="scale(0.975894,1.024702)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2319"
+ xlink:href="#XMLID_7_"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient1862">
+ <stop
+ id="stop1864"
+ offset="0.0000000"
+ style="stop-color:#ff6161;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1863"
+ offset="1.0000000"
+ style="stop-color:#cf2533;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <radialGradient
+ r="14.654100"
+ fy="34.776825"
+ fx="25.254200"
+ cy="35.025124"
+ cx="25.747030"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient2317"
+ xlink:href="#linearGradient1862"
+ inkscape:collect="always" />
+ <radialGradient
+ r="13.409894"
+ fy="29.598881"
+ fx="25.835396"
+ cy="29.439672"
+ cx="25.043650"
+ gradientTransform="scale(1.000075,0.999925)"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient2315"
+ xlink:href="#XMLID_7_"
+ inkscape:collect="always" />
+ <linearGradient
+ gradientTransform="matrix(0.999900,-1.010000e-2,1.010000e-2,0.999900,-1.144400,0.354300)"
+ gradientUnits="userSpaceOnUse"
+ id="XMLID_7_"
+ y2="164.30850"
+ x2="33.114700"
+ y1="-141.01460"
+ x1="33.114700">
+ <stop
+ id="stop96"
+ offset="0.0000000"
+ style="stop-color:#ffffff;stop-opacity:1.0000000;" />
+ <stop
+ id="stop3170"
+ offset="0.50000000"
+ style="stop-color:#ffffff;stop-opacity:0.67032969;" />
+ <stop
+ id="stop98"
+ offset="1.0000000"
+ style="stop-color:#ffffff;stop-opacity:0.0000000;" />
+ </linearGradient>
+ <linearGradient
+ y2="15.847512"
+ x2="25.759991"
+ y1="3.3488133"
+ x1="25.496077"
+ gradientTransform="scale(1.000075,0.999925)"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2313"
+ xlink:href="#XMLID_7_"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient1865">
+ <stop
+ id="stop2120"
+ offset="0.0000000"
+ style="stop-color:#cf2533;stop-opacity:1.0000000;" />
+ <stop
+ id="stop1866"
+ offset="1.0000000"
+ style="stop-color:#9f1a34;stop-opacity:1.0000000;" />
+ </linearGradient>
+ <linearGradient
+ y2="11.857447"
+ x2="21.960972"
+ y1="34.629219"
+ x1="21.960972"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient2311"
+ xlink:href="#linearGradient1865"
+ inkscape:collect="always" />
+ <linearGradient
+ id="linearGradient2928">
+ <stop
+ style="stop-color:#418ade;stop-opacity:1.0000000;"
+ offset="0.0000000"
+ id="stop2929" />
+ <stop
+ style="stop-color:#b1c4ea;stop-opacity:1.0000000;"
+ offset="1.0000000"
+ id="stop2930" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient2913">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop2914" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0;"
+ offset="1"
+ id="stop2915" />
+ </linearGradient>
+ <linearGradient
+ id="defitem11"
+ gradientUnits="userSpaceOnUse"
+ x1="16.3795"
+ y1="46.0794"
+ x2="21.7056"
+ y2="37.3341">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop37" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop38" />
+ </linearGradient>
+ <linearGradient
+ id="defitem10"
+ gradientUnits="userSpaceOnUse"
+ x1="19.6704"
+ y1="52.2427"
+ x2="22.0662"
+ y2="37.9851">
+ <stop
+ stop-color="#ffffff"
+ offset="0"
+ stop-opacity="1"
+ id="stop34" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="0.117647"
+ id="stop35" />
+ </linearGradient>
+ <radialGradient
+ id="defitem9"
+ gradientUnits="userSpaceOnUse"
+ cx="24.5089"
+ cy="28.1715"
+ fx="44.128"
+ fy="61.0801"
+ r="18.9697">
+ <stop
+ stop-color="#a3cdeb"
+ offset="0"
+ stop-opacity="1"
+ id="stop31" />
+ <stop
+ stop-color="#4189dd"
+ offset="1"
+ stop-opacity="1"
+ id="stop32" />
+ </radialGradient>
+ <linearGradient
+ id="defitem8"
+ gradientUnits="userSpaceOnUse"
+ x1="20.9103"
+ y1="40.4761"
+ x2="23.0818"
+ y2="39.6786">
+ <stop
+ stop-color="#2a2a2a"
+ offset="0"
+ stop-opacity="1"
+ id="stop28" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop29" />
+ </linearGradient>
+ <linearGradient
+ id="defitem7"
+ gradientUnits="userSpaceOnUse"
+ x1="20.5406"
+ y1="46.7018"
+ x2="24.9239"
+ y2="21.656">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop25" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop26" />
+ </linearGradient>
+ <linearGradient
+ id="defitem6"
+ gradientUnits="userSpaceOnUse"
+ x1="10.4881"
+ y1="51.4421"
+ x2="20.8871"
+ y2="39.295">
+ <stop
+ stop-color="#a3a3a3"
+ offset="0"
+ stop-opacity="1"
+ id="stop22" />
+ <stop
+ stop-color="#646464"
+ offset="1"
+ stop-opacity="1"
+ id="stop23" />
+ </linearGradient>
+ <linearGradient
+ id="defitem5"
+ gradientUnits="userSpaceOnUse"
+ x1="35.2774"
+ y1="15.8545"
+ x2="37.2641"
+ y2="13.2513">
+ <stop
+ stop-color="#809ab8"
+ offset="0"
+ stop-opacity="1"
+ id="stop19" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop20" />
+ </linearGradient>
+ <linearGradient
+ id="defitem4"
+ gradientUnits="userSpaceOnUse"
+ x1="43.8883"
+ y1="13.6313"
+ x2="34.6669"
+ y2="12.9291">
+ <stop
+ stop-color="#606060"
+ offset="0"
+ stop-opacity="1"
+ id="stop16" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop17" />
+ </linearGradient>
+ <linearGradient
+ id="defitem3"
+ gradientUnits="userSpaceOnUse"
+ x1="148.753"
+ y1="385.25"
+ x2="352.345"
+ y2="221.794">
+ <stop
+ stop-color="#00005d"
+ offset="0"
+ stop-opacity="1"
+ id="stop13" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop14" />
+ </linearGradient>
+ <linearGradient
+ id="defitem2"
+ gradientUnits="userSpaceOnUse"
+ x1="42.0318"
+ y1="458.943"
+ x2="303.137"
+ y2="252.238">
+ <stop
+ stop-color="#00005d"
+ offset="0"
+ stop-opacity="1"
+ id="stop10" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop11" />
+ </linearGradient>
+ <linearGradient
+ id="defitem1"
+ gradientUnits="userSpaceOnUse"
+ x1="25.1257"
+ y1="265.422"
+ x2="803.563"
+ y2="-11.1106">
+ <stop
+ stop-color="#00005d"
+ offset="0"
+ stop-opacity="1"
+ id="stop7" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop8" />
+ </linearGradient>
+ <linearGradient
+ id="defitem0"
+ gradientUnits="userSpaceOnUse"
+ x1="90.7316"
+ y1="380.011"
+ x2="874.145"
+ y2="20.7036">
+ <stop
+ stop-color="#464d46"
+ offset="0"
+ stop-opacity="1"
+ id="stop4" />
+ <stop
+ stop-color="#ffffff"
+ offset="1"
+ stop-opacity="1"
+ id="stop5" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1152"
+ gradientUnits="userSpaceOnUse"
+ x1="669.96069"
+ y1="357.63095"
+ x2="504.21884"
+ y2="-29.963737" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1153"
+ gradientUnits="userSpaceOnUse"
+ x1="436.45444"
+ y1="143.63887"
+ x2="264.29764"
+ y2="233.98660" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient1154"
+ gradientUnits="userSpaceOnUse"
+ x1="243.31390"
+ y1="458.89246"
+ x2="144.42444"
+ y2="234.72000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient1155"
+ gradientUnits="userSpaceOnUse"
+ x1="293.13882"
+ y1="354.33441"
+ x2="277.06821"
+ y2="371.91318" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient2291"
+ gradientTransform="scale(1.238101,0.807689)"
+ x1="443.82205"
+ y1="199.66719"
+ x2="430.70648"
+ y2="332.03079"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient2916"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="23.988409"
+ y1="20.002319"
+ x2="21.380413"
+ y2="25.374052"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient2920"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="24.697392"
+ y1="19.856495"
+ x2="15.669713"
+ y2="29.511093"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient2925"
+ gradientUnits="userSpaceOnUse"
+ x1="20.268293"
+ y1="14.901933"
+ x2="17.700712"
+ y2="25.424341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient2927"
+ gradientUnits="userSpaceOnUse"
+ x1="607.75952"
+ y1="377.21552"
+ x2="496.41223"
+ y2="159.41556" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2928"
+ id="linearGradient2931"
+ gradientTransform="scale(1.237999,0.807755)"
+ x1="30.894909"
+ y1="34.588230"
+ x2="23.711727"
+ y2="45.811607"
+ gradientUnits="userSpaceOnUse" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient3185"
+ gradientUnits="userSpaceOnUse"
+ x1="669.96069"
+ y1="357.63095"
+ x2="504.21884"
+ y2="-29.963737" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient3186"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.238101,0.807689)"
+ x1="443.82205"
+ y1="199.66719"
+ x2="430.70648"
+ y2="332.03079" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2928"
+ id="linearGradient3187"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.237999,0.807755)"
+ x1="30.894909"
+ y1="34.588230"
+ x2="23.711727"
+ y2="45.811607" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient3188"
+ gradientUnits="userSpaceOnUse"
+ x1="436.45444"
+ y1="143.63887"
+ x2="264.29764"
+ y2="233.98660" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient3189"
+ gradientUnits="userSpaceOnUse"
+ x1="243.31390"
+ y1="458.89246"
+ x2="144.42444"
+ y2="234.72000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient3190"
+ gradientUnits="userSpaceOnUse"
+ x1="293.13882"
+ y1="354.33441"
+ x2="277.06821"
+ y2="371.91318" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient3191"
+ gradientUnits="userSpaceOnUse"
+ x1="20.268293"
+ y1="14.901933"
+ x2="17.700712"
+ y2="25.424341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient3192"
+ gradientUnits="userSpaceOnUse"
+ x1="607.75952"
+ y1="377.21552"
+ x2="496.41223"
+ y2="159.41556" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient3193"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="24.697392"
+ y1="19.856495"
+ x2="15.669713"
+ y2="29.511093" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient3194"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="23.988409"
+ y1="20.002319"
+ x2="21.380413"
+ y2="25.374052" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient3236"
+ gradientUnits="userSpaceOnUse"
+ x1="669.96069"
+ y1="357.63095"
+ x2="504.21884"
+ y2="-29.963737" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem8"
+ id="linearGradient3237"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.238101,0.807689)"
+ x1="443.82205"
+ y1="199.66719"
+ x2="430.70648"
+ y2="332.03079" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2928"
+ id="linearGradient3238"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.237999,0.807755)"
+ x1="30.894909"
+ y1="34.588230"
+ x2="23.711727"
+ y2="45.811607" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient3239"
+ gradientUnits="userSpaceOnUse"
+ x1="436.45444"
+ y1="143.63887"
+ x2="264.29764"
+ y2="233.98660" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem0"
+ id="linearGradient3240"
+ gradientUnits="userSpaceOnUse"
+ x1="243.31390"
+ y1="458.89246"
+ x2="144.42444"
+ y2="234.72000" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem6"
+ id="linearGradient3241"
+ gradientUnits="userSpaceOnUse"
+ x1="293.13882"
+ y1="354.33441"
+ x2="277.06821"
+ y2="371.91318" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient3242"
+ gradientUnits="userSpaceOnUse"
+ x1="20.268293"
+ y1="14.901933"
+ x2="17.700712"
+ y2="25.424341" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#defitem11"
+ id="linearGradient3243"
+ gradientUnits="userSpaceOnUse"
+ x1="607.75952"
+ y1="377.21552"
+ x2="496.41223"
+ y2="159.41556" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient3244"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="24.697392"
+ y1="19.856495"
+ x2="15.669713"
+ y2="29.511093" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient2913"
+ id="linearGradient3245"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.843884,1.184997)"
+ x1="23.988409"
+ y1="20.002319"
+ x2="21.380413"
+ y2="25.374052" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1865"
+ id="linearGradient3246"
+ gradientUnits="userSpaceOnUse"
+ x1="21.960972"
+ y1="34.629219"
+ x2="21.960972"
+ y2="11.857447" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="linearGradient3247"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.000075,0.999925)"
+ x1="25.496077"
+ y1="3.3488133"
+ x2="25.759991"
+ y2="15.847512" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="radialGradient3248"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.000075,0.999925)"
+ cx="25.043650"
+ cy="29.439672"
+ fx="25.835396"
+ fy="29.598881"
+ r="13.409894" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1862"
+ id="radialGradient3249"
+ gradientUnits="userSpaceOnUse"
+ cx="25.747030"
+ cy="35.025124"
+ fx="25.254200"
+ fy="34.776825"
+ r="14.654100" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="linearGradient3250"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.975894,1.024702)"
+ x1="10.139539"
+ y1="5.2444587"
+ x2="24.164434"
+ y2="25.928497" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1865"
+ id="linearGradient3277"
+ gradientUnits="userSpaceOnUse"
+ x1="21.960972"
+ y1="34.629219"
+ x2="21.960972"
+ y2="11.857447" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="linearGradient3278"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.000075,0.999925)"
+ x1="25.496077"
+ y1="3.3488133"
+ x2="25.759991"
+ y2="15.847512" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="radialGradient3279"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(1.000075,0.999925)"
+ cx="25.043650"
+ cy="29.439672"
+ fx="25.835396"
+ fy="29.598881"
+ r="13.409894" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1862"
+ id="radialGradient3280"
+ gradientUnits="userSpaceOnUse"
+ cx="25.747030"
+ cy="35.025124"
+ fx="25.254200"
+ fy="34.776825"
+ r="14.654100" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#XMLID_7_"
+ id="linearGradient3281"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="scale(0.975894,1.024702)"
+ x1="10.139539"
+ y1="5.2444587"
+ x2="24.164434"
+ y2="25.928497" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="8.0000000"
+ inkscape:cx="14.572426"
+ inkscape:cy="13.438808"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ gridspacingy="1.0000000pt"
+ gridspacingx="1.0000000pt"
+ gridoriginy="0.0000000pt"
+ gridoriginx="0.0000000pt"
+ inkscape:window-width="1024"
+ inkscape:window-height="698"
+ inkscape:window-x="0"
+ inkscape:window-y="49" />
+ <metadata
+ id="metadata4">
+ <rdf:RDF
+ id="RDF5">
+ <cc:Work
+ rdf:about=""
+ id="Work6">
+ <dc:format
+ id="format7">image/svg+xml</dc:format>
+ <dc:type
+ id="type9"
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <g
+ id="g2279">
+ <g
+ id="g2932"
+ transform="translate(-3.092735e-7,-12.50000)">
+ <path
+ style="fill:#666666;fill-opacity:1.0000000;fill-rule:evenodd;stroke-width:1.2500000"
+ d="M 11.625000,13.531250 C 11.461860,13.557604 11.315795,13.647491 11.218750,13.781250 L 2.3437500,26.406250 C 2.2321131,26.547183 2.1865683,26.729362 2.2187500,26.906250 C 2.2184896,26.916665 2.2184896,26.927085 2.2187500,26.937500 C 2.2145733,26.979062 2.2145733,27.020938 2.2187500,27.062500 L 2.2500000,29.781250 C 2.2540731,30.026003 2.4006403,30.245854 2.6250000,30.343750 C 2.6250000,30.343750 13.652146,35.339681 24.687500,40.312500 C 30.205177,42.798910 35.739669,45.263177 39.875000,47.125000 C 41.942665,48.055912 43.639161,48.833638 44.843750,49.375000 C 45.446044,49.645681 45.927843,49.855739 46.250000,50.000000 C 46.411079,50.072130 46.541352,50.150317 46.625000,50.187500 C 47.177819,50.330388 47.517940,50.014116 47.781250,49.687500 L 56.437500,37.406250 C 56.525343,37.289593 56.569568,37.145861 56.562500,37.000000 L 56.500000,35.906250 C 56.487690,35.792614 56.444455,35.684528 56.375000,35.593750 C 56.349101,35.548326 56.317605,35.506331 56.281250,35.468750 C 56.271205,35.457968 56.260782,35.447545 56.250000,35.437500 C 56.195589,35.384818 56.132057,35.342463 56.062500,35.312500 L 23.968750,21.156250 L 23.843750,19.968750 C 23.938430,19.675909 23.805853,19.357723 23.531250,19.218750 L 12.000000,13.593750 C 11.884521,13.534486 11.753457,13.512643 11.625000,13.531250 z "
+ id="path2180"
+ sodipodi:nodetypes="cccccccccccccccccccccccccc" />
+ <path
+ id="path617"
+ d="M 181.94300,222.09000 L 299.82100,387.80300 L 725.20800,199.88100 L 609.89200,35.022200 L 181.94300,222.09000 z "
+ style="fill:url(#linearGradient3236);fill-opacity:1.0000000;fill-rule:evenodd;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ id="path616"
+ d="M 608.18400,32.459200 L 726.06200,199.02600 L 726.91600,184.50500 L 612.45500,22.209200 L 608.18400,32.459200 z "
+ style="fill:#7e7e7e;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ id="path2287"
+ d="M 233.75000,224.08500 L 325.55800,353.34200 L 655.94800,208.38100 L 565.95200,77.916500 L 233.75000,224.08500 z "
+ style="fill:url(#linearGradient3237);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ id="path615"
+ d="M 19.135063,33.909343 L 25.837798,24.472524 L 50.193347,35.212113 L 43.622903,44.737089 L 19.135063,33.909343 z "
+ style="fill:url(#linearGradient3238);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ id="path673"
+ d="M 25.625700,316.47800 L 178.09900,245.15300 C 178.09900,245.15300 180.66100,219.52700 181.08900,219.52700 C 181.51600,219.52700 608.61100,33.313200 608.61100,33.313200 C 608.61100,33.313200 608.18400,18.365200 608.61100,17.938200 C 609.03800,17.511200 26.052800,280.17500 26.052800,280.17500 L 25.625700,316.47800 z "
+ style="fill:url(#linearGradient3239);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ id="path666"
+ d="M 25.625700,319.89500 L 142.65000,486.88900 L 295.12300,412.57400 L 178.95300,245.15300 L 25.625700,319.89500 z "
+ style="fill:url(#linearGradient3240);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ id="path671"
+ d="M 181.51600,220.80800 C 181.94300,221.23600 180.23400,245.58000 180.23400,245.58000 L 294.69600,410.01200 L 297.25800,386.09400 L 181.51600,220.80800 z "
+ style="fill:url(#linearGradient3241);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)" />
+ <path
+ style="fill:#ffffff;fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:6.2500000;stroke-opacity:1.0000000"
+ d="M 2.9135073,26.708568 L 2.7730583,26.897882 L 2.7707160,27.149345 L 14.250000,32.656250 L 14.375000,34.562500 L 14.500000,34.562500 L 46.812500,48.687500 L 46.812500,49.559927 L 46.991632,49.656602 L 47.281250,49.338956 L 47.281250,48.593750 L 47.281250,48.562500 L 55.910156,36.207031 L 55.884830,35.904371 L 55.652344,35.781250 L 46.937500,48.218750 L 14.812500,34.187500 L 14.718750,32.500000 L 14.593750,32.312500 L 2.9135073,26.708568 z "
+ id="path871"
+ sodipodi:nodetypes="cccccccccccccccccccc" />
+ <path
+ style="fill:url(#linearGradient3242);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:1.2500000"
+ d="M 11.718750,14.156250 L 2.8750000,26.750000 C 6.6958210,24.170916 12.161633,22.500000 18.343750,22.500000 C 19.335573,22.500000 20.294955,22.606645 21.250000,22.687500 L 23.250000,19.781250 L 11.718750,14.156250 z "
+ id="path2921" />
+ <path
+ id="path2926"
+ d="M 234.05976,290.20300 L 299.82100,387.80300 L 725.20800,199.88100 L 696.58127,160.92805 C 552.72759,216.40388 359.33718,302.84016 234.05976,290.20300 z "
+ style="fill:url(#linearGradient3243);fill-opacity:1.0000000;fill-rule:evenodd;stroke-width:1.2500000"
+ transform="matrix(7.570141e-2,0.000000,0.000000,-7.570141e-2,0.912349,51.00637)"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="opacity:0.75100404;fill:url(#linearGradient3244);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:6.2500000;stroke-opacity:1.0000000"
+ d="M 14.796461,34.626477 L 14.408491,34.354425 L 23.354534,21.505948 L 23.770124,21.708946 L 14.796461,34.626477 z "
+ id="path877"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:url(#linearGradient3245);fill-opacity:1.0000000;fill-rule:evenodd;stroke:none;stroke-width:6.2500000;stroke-opacity:1.0000000"
+ d="M 14.621401,32.648778 L 14.233431,32.376726 L 23.087682,19.698021 L 23.351355,20.050571 L 14.621401,32.648778 z "
+ id="path876"
+ sodipodi:nodetypes="ccccc" />
+ </g>
+ <path
+ style="fill:#000000;fill-opacity:0.30196080;fill-rule:nonzero;stroke:none;stroke-width:1.1000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:0.15686275;"
+ d="M 24.312500 17.343750 C 23.987616 17.343750 23.649855 17.446542 23.406250 17.687500 L 17.718750 23.406250 C 17.713151 23.411276 17.693007 23.401124 17.687500 23.406250 C 17.659964 23.431878 17.650137 23.472004 17.625000 23.500000 C 17.624566 23.510824 17.624565 23.520426 17.625000 23.531250 C 17.350876 23.875722 17.338357 24.292493 17.468750 24.687500 L 43.562500 36.312500 C 43.502312 36.165290 43.491932 35.993216 43.375000 35.875000 L 38.218750 30.718750 C 38.200985 30.669342 38.187500 30.581876 38.187500 30.531250 C 38.187500 30.486074 38.203602 30.422181 38.218750 30.375000 L 43.375000 25.187500 C 43.856918 24.700290 43.856918 23.893458 43.375000 23.406250 L 37.656250 17.687500 C 37.169039 17.205582 36.362210 17.205582 35.875000 17.687500 L 30.718750 22.843750 C 30.709161 22.851759 30.715932 22.846566 30.687500 22.875000 C 30.640318 22.890148 30.576424 22.906250 30.531250 22.906250 C 30.486075 22.906250 30.422181 22.890148 30.375000 22.875000 C 30.346568 22.846566 30.384589 22.851760 30.375000 22.843750 L 25.187500 17.687500 C 24.943895 17.446541 24.637384 17.343750 24.312500 17.343750 z "
+ id="path2271" />
+ <g
+ id="g2292"
+ transform="matrix(0.735081,0.000000,0.000000,0.735081,10.40225,14.07764)">
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc"
+ id="path2076"
+ d="M 7.9113325,10.909610 C 7.9107405,10.924335 7.9107406,10.939079 7.9113325,10.953804 C 7.3714319,11.632256 7.4081462,12.617271 8.0218179,13.229804 L 15.048692,20.256678 L 15.070789,20.278775 C 15.091397,20.342959 15.114983,20.438291 15.114983,20.499746 C 15.114983,20.568617 15.072860,20.675598 15.048692,20.742814 L 8.0218179,27.769687 C 7.3662191,28.432485 7.3662203,29.537570 8.0218179,30.200367 L 15.799993,37.978542 C 16.462790,38.634139 17.567875,38.634140 18.230672,37.978542 L 25.257546,30.951668 L 25.279643,30.929571 C 25.343827,30.908963 25.439159,30.885377 25.500614,30.885377 C 25.569484,30.885377 25.676466,30.927500 25.743682,30.951668 L 32.770555,37.978542 C 33.433352,38.634140 34.538438,38.634140 35.201235,37.978542 L 42.979409,30.200367 C 43.635008,29.537570 43.635008,28.432484 42.979409,27.769687 L 35.952536,20.742814 C 35.928368,20.675598 35.886245,20.568617 35.886245,20.499746 C 35.886245,20.438291 35.909831,20.342959 35.930439,20.278775 C 35.969120,20.240094 35.941639,20.269723 35.952536,20.256678 L 42.979409,13.229804 C 43.635008,12.567008 43.635008,11.461921 42.979409,10.799125 L 35.201235,3.0209501 C 34.538438,2.3653518 33.433352,2.3653518 32.770555,3.0209501 L 25.743682,10.047824 C 25.730637,10.058720 25.760265,10.031240 25.721585,10.069921 C 25.657400,10.090529 25.562068,10.114115 25.500614,10.114115 C 25.439159,10.114115 25.343827,10.090529 25.279643,10.069921 C 25.240962,10.031240 25.270590,10.058720 25.257546,10.047824 L 18.230672,3.0209501 C 17.567875,2.3653513 16.462790,2.3653525 15.799993,3.0209501 L 8.0439150,10.777028 C 8.0362978,10.783866 8.0293096,10.792152 8.0218179,10.799125 C 7.9843580,10.833990 7.9455278,10.871524 7.9113325,10.909610 z "
+ style="fill:url(#linearGradient3277);fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:1.1000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="path1311"
+ d="M 8.3311771,11.285261 C 7.9627502,11.748234 8.0007976,12.414065 8.4195655,12.832057 L 15.468536,19.881027 C 15.499831,19.912323 15.645313,20.189863 15.645313,20.499746 C 15.645313,20.809628 15.499831,21.087169 15.468536,21.118464 L 8.4195655,28.167435 C 7.9715048,28.620416 7.9715054,29.349639 8.4195655,29.802619 L 16.197740,37.580794 C 16.650721,38.028854 17.379944,38.028855 17.832924,37.580794 L 24.881895,30.531823 C 24.913190,30.500528 25.190731,30.355047 25.500614,30.355047 C 25.810496,30.355047 26.088037,30.500528 26.119332,30.531823 L 33.168303,37.580794 C 33.621283,38.028854 34.350507,38.028854 34.803487,37.580794 L 42.581662,29.802619 C 43.029722,29.349639 43.029722,28.620415 42.581662,28.167435 L 35.532691,21.118464 C 35.501396,21.087169 35.355914,20.809628 35.355914,20.499746 C 35.355914,20.189863 35.501396,19.912323 35.532691,19.881027 L 42.581662,12.832057 C 43.029722,12.379076 43.029722,11.649853 42.581662,11.196872 L 34.803487,3.4186976 C 34.350507,2.9706373 33.621283,2.9706373 33.168303,3.4186976 L 26.119332,10.467668 C 26.088037,10.498963 25.810496,10.644445 25.500614,10.644445 C 25.190731,10.644445 24.913190,10.498963 24.881895,10.467668 L 17.832924,3.4186976 C 17.379944,2.9706369 16.650721,2.9706376 16.197740,3.4186976 L 8.4195655,11.196872 C 8.3885387,11.224729 8.3590340,11.254234 8.3311771,11.285261 z "
+ style="fill:url(#linearGradient3278);fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:2.3250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ id="path2241"
+ d="M 8.3311771,11.285261 C 7.9627502,11.748234 8.0007976,12.414065 8.4195655,12.832057 L 15.468536,19.881027 C 15.499831,19.912323 15.645313,20.189863 15.645313,20.499746 C 15.645313,20.809628 15.499831,21.087169 15.468536,21.118464 L 8.4195655,28.167435 C 7.9715048,28.620416 7.9715054,29.349639 8.4195655,29.802619 L 16.197740,37.580794 C 16.650721,38.028854 17.379944,38.028855 17.832924,37.580794 L 24.881895,30.531823 C 24.913190,30.500528 25.190731,30.355047 25.500614,30.355047 C 25.810496,30.355047 26.088037,30.500528 26.119332,30.531823 L 33.168303,37.580794 C 33.621283,38.028854 34.350507,38.028854 34.803487,37.580794 L 42.581662,29.802619 C 43.029722,29.349639 43.029722,28.620415 42.581662,28.167435 L 35.532691,21.118464 C 35.501396,21.087169 35.355914,20.809628 35.355914,20.499746 C 35.355914,20.189863 35.501396,19.912323 35.532691,19.881027 L 42.581662,12.832057 C 43.029722,12.379076 43.029722,11.649853 42.581662,11.196872 L 34.803487,3.4186976 C 34.350507,2.9706373 33.621283,2.9706373 33.168303,3.4186976 L 26.119332,10.467668 C 26.088037,10.498963 25.810496,10.644445 25.500614,10.644445 C 25.190731,10.644445 24.913190,10.498963 24.881895,10.467668 L 17.832924,3.4186976 C 17.379944,2.9706369 16.650721,2.9706376 16.197740,3.4186976 L 8.4195655,11.196872 C 8.3885387,11.224729 8.3590340,11.254234 8.3311771,11.285261 z "
+ style="fill:url(#radialGradient3279);fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:2.3250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccc"
+ id="rect1301"
+ d="M 9.2371577,12.014464 L 16.286128,19.063435 C 16.963211,19.740518 16.963211,21.258973 16.286128,21.936056 L 9.2371577,28.985027 L 17.015332,36.763202 L 24.064303,29.714231 C 24.741386,29.037148 26.259841,29.037148 26.936924,29.714231 L 33.985895,36.763202 L 41.764070,28.985027 L 34.715099,21.936056 C 34.038016,21.258973 34.038016,19.740518 34.715099,19.063435 L 41.764070,12.014464 L 33.985895,4.2362898 L 26.936924,11.285261 C 26.259841,11.962344 24.741386,11.962344 24.064303,11.285261 L 17.015332,4.2362898 L 9.2371577,12.014464 z "
+ style="fill:url(#radialGradient3280);fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:2.3250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ <path
+ sodipodi:nodetypes="ccccccccccccccccccc"
+ id="path2252"
+ d="M 17.000000,3.0937500 C 16.704449,3.0937501 16.413991,3.1822200 16.187500,3.4062500 L 8.4062500,11.187500 C 8.3752235,11.215357 8.3716069,11.250223 8.3437500,11.281250 C 7.9753234,11.744223 7.9874821,12.425758 8.4062500,12.843750 L 15.468750,19.875000 C 15.500045,19.906296 15.656250,20.190117 15.656250,20.500000 C 15.656250,20.809882 15.500045,21.093705 15.468750,21.125000 L 8.4062500,28.156250 C 7.9581896,28.609231 7.9581899,29.359520 8.4062500,29.812500 L 11.843750,33.250000 C 23.166667,26.397321 31.632440,16.401786 36.812500,5.4062500 L 34.812500,3.4062500 C 34.359521,2.9581897 33.609230,2.9581897 33.156250,3.4062500 L 26.125000,10.468750 C 26.093706,10.500045 25.809882,10.656250 25.500000,10.656250 C 25.190117,10.656250 24.906295,10.500045 24.875000,10.468750 L 17.843750,3.4062500 C 17.617260,3.1822197 17.295551,3.0937499 17.000000,3.0937500 z "
+ style="fill:url(#linearGradient3281);fill-opacity:1.0000000;fill-rule:nonzero;stroke:none;stroke-width:2.3250000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4.0000000;stroke-opacity:1.0000000" />
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/wifi/pixmaps/offline.png b/wifi/pixmaps/offline.png
new file mode 100644
index 00000000..16887f42
--- /dev/null
+++ b/wifi/pixmaps/offline.png
Binary files differ
diff --git a/wifi/pixmaps/oor_down.png b/wifi/pixmaps/oor_down.png
new file mode 100644
index 00000000..ab35304c
--- /dev/null
+++ b/wifi/pixmaps/oor_down.png
Binary files differ
diff --git a/wifi/pixmaps/oor_minimum.png b/wifi/pixmaps/oor_minimum.png
new file mode 100644
index 00000000..8ad659f5
--- /dev/null
+++ b/wifi/pixmaps/oor_minimum.png
Binary files differ
diff --git a/wifi/speed.cpp b/wifi/speed.cpp
new file mode 100644
index 00000000..fdb7256c
--- /dev/null
+++ b/wifi/speed.cpp
@@ -0,0 +1,64 @@
+/***************************************************************************
+ speed.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qpainter.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include "speed.h"
+#include "interface_wireless.h"
+
+Speed::Speed (QWidget * parent, Interface_wireless * device):QWidget (parent,
+ "KWiFiManager")
+{
+ this->device = device;
+}
+
+void
+Speed::paintEvent (QPaintEvent *)
+{
+ QPainter speedpainter (this);
+ QColor farbe;
+ farbe.setRgb (0, 255, 0);
+ if (device->get_bitrate () < 6000000)
+ farbe.setRgb (0, 200, 0);
+ if (device->get_bitrate () < 2500000)
+ farbe.setRgb (255, 255, 0);
+ if (device->get_bitrate () < 1500000)
+ farbe.setRgb (255, 128, 0);
+ speedpainter.drawText (20, 20, i18n ("Connection speed [MBit/s]:"));
+ speedpainter.drawRect (19, 29, 222, 12);
+ if (device->get_bitrate () <= 11000000.)
+ {
+ speedpainter.fillRect (20, 30, (int) device->get_bitrate () / 50000,
+ 10, farbe);
+ speedpainter.drawText (16, 52, "0");
+ speedpainter.drawText (35, 52, "1");
+ speedpainter.drawText (55, 52, "2");
+ speedpainter.drawText (120, 52, "5.5");
+ speedpainter.drawText (230, 52, "11");
+ }
+ else
+ {
+ speedpainter.fillRect (20, 30, (int) device->get_bitrate () / 500000,
+ 10, farbe);
+ speedpainter.drawText (16, 52, "0");
+ speedpainter.drawText (33, 52, "11");
+ speedpainter.drawText (56, 52, "22");
+ speedpainter.drawText (120, 52, "54");
+ speedpainter.drawText (220, 52, "108");
+ };
+}
diff --git a/wifi/speed.h b/wifi/speed.h
new file mode 100644
index 00000000..41f15b70
--- /dev/null
+++ b/wifi/speed.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+ speed.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 SPEED_H
+#define SPEED_H
+
+#include <qwidget.h>
+
+class Interface_wireless;
+
+class Speed:public QWidget
+{
+public:
+ Speed (QWidget * parent, Interface_wireless * device);
+private:
+ void paintEvent (QPaintEvent *);
+ Interface_wireless *device;
+};
+
+#endif
diff --git a/wifi/statistics.cpp b/wifi/statistics.cpp
new file mode 100644
index 00000000..2ad8d393
--- /dev/null
+++ b/wifi/statistics.cpp
@@ -0,0 +1,111 @@
+/***************************************************************************
+ statistics.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qwidget.h>
+#include <qpainter.h>
+#include <klocale.h>
+#include "statistics.h"
+#include "interface_wireless.h"
+
+Statistics::Statistics (Interface_wireless * device, bool showStatsNoise):QWidget (0,
+ "KWiFiManager")
+{
+ this->setCaption (i18n ("Statistics - KWiFiManager"));
+ this->device = device;
+ this->showStatsNoise = showStatsNoise;
+}
+
+void
+Statistics::paintEvent (QPaintEvent *)
+{
+ statpainter = new QPainter (this);
+ QColor farbe (255, 255, 255);
+ statpainter->drawText (40, 30, i18n ("Noise/Signal Level Statistics"));
+ statpainter->drawText (150, 260,
+ i18n ("BLUE = signal level, RED = noise level"));
+ statpainter->drawLine (41, 244, 41, 260);
+ statpainter->drawLine (520, 244, 520, 260);
+ statpainter->drawText (21, 274, i18n ("-240 s"));
+ statpainter->drawText (510, 274, i18n ("now"));
+ statpainter->fillRect (41, 41, 480, 201, farbe);
+ QPointArray datensatz (240);
+ QPointArray datensatz2 (240);
+ // we may need to scale the output to fit into the window, so here we
+ // determine the range of values
+ int bottom =
+ (device->sigLevelMin >
+ device->noiseLevelMin) ? device->noiseLevelMin : device->sigLevelMin;
+ int top =
+ (device->sigLevelMax >
+ device->noiseLevelMax) ? device->sigLevelMax : device->noiseLevelMax;
+ int datarange = top - bottom;
+ statpainter->drawText (10, 50, QString ("%1").arg (top));
+ statpainter->drawText (10, 240, QString ("%1").arg (bottom));
+
+ // if values are all below 0, this indicates proper dBm values
+
+ if (top < 0) {
+ statpainter->drawText (2,65, "dBm");
+ statpainter->drawText (2,255, "dBm");
+ }
+
+ double scaleRatio;
+ if (datarange != 0)
+ {
+ scaleRatio = 200. / datarange;
+ }
+ else
+ {
+ scaleRatio = 1.;
+ }
+ int i = 0;
+ bool atLeastOneValid = false;
+ for (int j = device->current; j < device->current + MAX_HISTORY; j++)
+ {
+ if (device->valid[j % MAX_HISTORY])
+ {
+ atLeastOneValid = true;
+ datensatz.setPoint (i, 40 + (i * 2),
+ 241 -
+ (int) (((device->sigLevel[j % MAX_HISTORY] + device->sigLevel[(j-1) % MAX_HISTORY]) / 2 -
+ bottom) * scaleRatio));
+ datensatz2.setPoint (i, 40 + (i * 2),
+ 241 -
+ (int) (((device->noiseLevel[j % MAX_HISTORY] + device->noiseLevel[(j-1) % MAX_HISTORY]) / 2 -
+ bottom) * scaleRatio));
+ }
+ else
+ {
+ datensatz.setPoint (i, 40 + (i * 2), 241);
+ datensatz2.setPoint (i, 40 + (i * 2), 241);
+ }
+ i++;
+ };
+ // the above point array is only useful if at least one entry has its
+ // valid flag set. This fact is determined in the iteration before.
+ // Only paint the point array if there is one valid entry
+ if (atLeastOneValid)
+ {
+ if (showStatsNoise) {
+ statpainter->setPen (red);
+ statpainter->drawPolyline (datensatz2, 1);
+ }
+ statpainter->setPen (blue);
+ statpainter->drawPolyline (datensatz, 1);
+ }
+ delete statpainter;
+}
diff --git a/wifi/statistics.h b/wifi/statistics.h
new file mode 100644
index 00000000..f8c99dd9
--- /dev/null
+++ b/wifi/statistics.h
@@ -0,0 +1,33 @@
+/***************************************************************************
+ statistics.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 STATISTICS_H
+#define STATISTICS_H
+
+class Interface_wireless;
+
+class Statistics:public QWidget
+{
+public:
+ Statistics (Interface_wireless * device, bool showStatsNoise);
+private:
+ QPainter * statpainter;
+ void paintEvent (QPaintEvent *);
+ Interface_wireless *device;
+ bool showStatsNoise;
+};
+
+#endif
diff --git a/wifi/status.cpp b/wifi/status.cpp
new file mode 100644
index 00000000..b40fa67d
--- /dev/null
+++ b/wifi/status.cpp
@@ -0,0 +1,382 @@
+/***************************************************************************
+ status.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <klocale.h>
+#include "status.h"
+#include "interface_wireless.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+
+Status::Status (QWidget * parent, Interface_wireless * device)
+ : QWidget (parent, "KWiFiManager status")
+{
+ this->device = device;
+}
+
+bool Status::generateStatsWidget ()
+{
+ double
+ freq = 0.;
+ int
+ sig = 0,
+ noi = 0,
+ qual = 0;
+ device->get_current_quality (sig, noi, qual);
+ content =
+ new QGroupBox (2, Qt::Horizontal, i18n ("Status of Active Connection"),
+ this);
+ bool has_freq = device->get_device_freq (freq);
+ if ((!has_freq) || (qual == 0))
+ {
+ stat = new QLabel (i18n ("Searching for network: "), content);
+ }
+ else
+ stat = new QLabel (i18n ("Connected to network: "), content);
+ net_id = new QLabel (device->get_essid (), content);
+ ap_str = new QLabel (i18n ("Access point: "), content);
+ QString
+ mac,
+ ip;
+ device->get_AP_info (mac, ip);
+ if ((mac != "44:44:44:44:44:44") && (mac != "00:00:00:00:00:00")) {
+ ap_ad = new QLabel (mac, content);
+ } else {
+ ap_ad = new QLabel (i18n("- no access point -"),content);
+ }
+ local_ip_string = new QLabel (i18n ("Local IP: "), content);
+ local_ip_content = new QLabel (device->get_IP_info (), content);
+ fr_str = new QLabel (i18n ("Frequency [channel]: "), content);
+ QString
+ temp3 = "%1";
+ QString
+ temp2 = "%1";
+ if (freq >= 1000000000)
+ {
+ if (freq / 1000000. > 5690.)
+ {
+ temp3 = temp3.arg(140);
+ }
+ else if (freq / 1000000. > 5670.)
+ {
+ temp3 = temp3.arg(136);
+ }
+ else if (freq / 1000000. > 5650.)
+ {
+ temp3 = temp3.arg(132);
+ }
+ else if (freq / 1000000. > 5630.)
+ {
+ temp3 = temp3.arg(128);
+ }
+ else if (freq / 1000000. > 5610.)
+ {
+ temp3 = temp3.arg(124);
+ }
+ else if (freq / 1000000. > 5590.)
+ {
+ temp3 = temp3.arg(120);
+ }
+ else if (freq / 1000000. > 5570.)
+ {
+ temp3 = temp3.arg(116);
+ }
+ else if (freq / 1000000. > 5550.)
+ {
+ temp3 = temp3.arg(112);
+ }
+ else if (freq / 1000000. > 5530.)
+ {
+ temp3 = temp3.arg(108);
+ }
+ else if (freq / 1000000. > 5510.)
+ {
+ temp3 = temp3.arg(104);
+ }
+ else if (freq / 1000000. > 5490.)
+ {
+ temp3 = temp3.arg(100);
+ }
+ else if (freq / 1000000. > 5310.)
+ {
+ temp3 = temp3.arg(64);
+ }
+ else if (freq / 1000000. > 5290.)
+ {
+ temp3 = temp3.arg(60);
+ }
+ else if (freq / 1000000. > 5270.)
+ {
+ temp3 = temp3.arg(56);
+ }
+ else if (freq / 1000000. > 5250.)
+ {
+ temp3 = temp3.arg(52);
+ }
+ else if (freq / 1000000. > 5230.)
+ {
+ temp3 = temp3.arg(48);
+ }
+ else if (freq / 1000000. > 5210.)
+ {
+ temp3 = temp3.arg(44);
+ }
+ else if (freq / 1000000. > 5190.)
+ {
+ temp3 = temp3.arg(40);
+ }
+ else if (freq / 1000000. > 5170.)
+ {
+ temp3 = temp3.arg(36);
+ }
+ else if (freq / 1000000. > 2474.)
+ {
+ temp3 = temp3.arg (14);
+ }
+ else if (freq / 1000000. > 2469.)
+ {
+ temp3 = temp3.arg (13);
+ }
+ else if (freq / 1000000. > 2464.)
+ {
+ temp3 = temp3.arg (12);
+ }
+ else if (freq / 1000000. > 2459.)
+ {
+ temp3 = temp3.arg (11);
+ }
+ else if (freq / 1000000. > 2454.)
+ {
+ temp3 = temp3.arg (10);
+ }
+ else if (freq / 1000000. > 2449.)
+ {
+ temp3 = temp3.arg (9);
+ }
+ else if (freq / 1000000. > 2444.)
+ {
+ temp3 = temp3.arg (8);
+ }
+ else if (freq / 1000000. > 2439.)
+ {
+ temp3 = temp3.arg (7);
+ }
+ else if (freq / 1000000. > 2434.)
+ {
+ temp3 = temp3.arg (6);
+ }
+ else if (freq / 1000000. > 2429.)
+ {
+ temp3 = temp3.arg (5);
+ }
+ else if (freq / 1000000. > 2424.)
+ {
+ temp3 = temp3.arg (4);
+ }
+ else if (freq / 1000000. > 2419.)
+ {
+ temp3 = temp3.arg (3);
+ }
+ else if (freq / 1000000. > 2414.)
+ {
+ temp3 = temp3.arg (2);
+ }
+ else if (freq / 1000000. >= 2400.)
+ {
+ temp3 = temp3.arg (1);
+ }
+ else
+ temp3 = temp3.arg ("?");
+ temp2 = temp2.arg (freq / 1000000000.);
+ }
+
+ if (freq < 1000000000)
+ {if (freq >= 140.)
+ {
+ temp2 = temp2.arg (5.700);
+ }
+ else if (freq >= 136.)
+ {
+ temp2 = temp2.arg (5.680);
+ }
+ else if (freq >= 132.)
+ {
+ temp2 = temp2.arg (5.660);
+ }
+ else if (freq >= 128.)
+ {
+ temp2 = temp2.arg (5.640);
+ }
+ else if (freq >= 124.)
+ {
+ temp2 = temp2.arg (5.620);
+ }
+ else if (freq >= 120.)
+ {
+ temp2 = temp2.arg (5.600);
+ }
+ else if (freq >= 116.)
+ {
+ temp2 = temp2.arg (5.580);
+ }
+ else if (freq >= 112.)
+ {
+ temp2 = temp2.arg (5.560);
+ }
+ else if (freq >= 108.)
+ {
+ temp2 = temp2.arg (5.540);
+ }
+ else if (freq >= 104.)
+ {
+ temp2 = temp2.arg (5.520);
+ }
+ else if (freq >= 100.)
+ {
+ temp2 = temp2.arg (5.500);
+ }
+ else if (freq >= 64.)
+ {
+ temp2 = temp2.arg (5.320);
+ }
+ else if (freq >= 60.)
+ {
+ temp2 = temp2.arg (5.300);
+ }
+ else if (freq >= 56.)
+ {
+ temp2 = temp2.arg (5.280);
+ }
+ else if (freq >= 52.)
+ {
+ temp2 = temp2.arg (5.260);
+ }
+ else if (freq >= 48.)
+ {
+ temp2 = temp2.arg (5.240);
+ }
+ else if (freq >= 44.)
+ {
+ temp2 = temp2.arg (5.220);
+ }
+ else if (freq >= 40.)
+ {
+ temp2 = temp2.arg (5.200);
+ }
+ else if (freq >= 36.)
+ {
+ temp2 = temp2.arg (5.180);
+ }
+ else if (freq >= 14.)
+ {
+ temp2 = temp2.arg (2.484);
+ }
+ else if (freq >= 13.)
+ {
+ temp2 = temp2.arg (2.472);
+ }
+ else if (freq >= 12.)
+ {
+ temp2 = temp2.arg (2.467);
+ }
+ else if (freq >= 11.)
+ {
+ temp2 = temp2.arg (2.462);
+ }
+ else if (freq >= 10.)
+ {
+ temp2 = temp2.arg (2.457);
+ }
+ else if (freq >= 9.)
+ {
+ temp2 = temp2.arg (2.452);
+ }
+ else if (freq >= 8.)
+ {
+ temp2 = temp2.arg (2.447);
+ }
+ else if (freq >= 7.)
+ {
+ temp2 = temp2.arg (2.442);
+ }
+ else if (freq >= 6.)
+ {
+ temp2 = temp2.arg (2.437);
+ }
+ else if (freq >= 5.)
+ {
+ temp2 = temp2.arg (2.432);
+ }
+ else if (freq >= 4.)
+ {
+ temp2 = temp2.arg (2.427);
+ }
+ else if (freq >= 3.)
+ {
+ temp2 = temp2.arg (2.422);
+ }
+ else if (freq >= 2.)
+ {
+ temp2 = temp2.arg (2.417);
+ }
+ else if (freq >= 1.)
+ {
+ temp2 = temp2.arg (2.412);
+ }
+ else
+ temp2 = temp2.arg ("?");
+ temp3 = temp3.arg (freq);
+ }
+
+ fr = new QLabel ( has_freq ? temp2 + " [" + temp3 + "]" : "" , content);
+ if (geteuid () == 0)
+ {
+ enc_str = new QLabel (i18n ("Encryption: "), content);
+ QString
+ key;
+ int
+ size = 0,
+ flags = 0;
+ if (device->get_key (key, size, flags))
+ // if ((device->key_flags & IW_ENCODE_DISABLED) || (key_size == 0))
+ // will the following line suffice?
+ if (size == 0)
+ {
+ enc = new QLabel (i18n ("off"), content);
+ }
+ else
+ enc = new QLabel (i18n ("active"), content);
+ }
+ // content->setFixedSize (content->sizeHint ());
+ content->setMinimumSize(content->sizeHint());
+ if ( content->minimumWidth() < 280 ) content->setMinimumWidth( 280 );
+ setFixedSize (content->minimumSize());
+ content->show ();
+ widgetbild = QPixmap::grabWidget (content);
+ delete
+ content;
+ return true;
+}
+
+void
+Status::paintEvent (QPaintEvent *)
+{
+ QPainter *paintArea = new QPainter (this);
+ generateStatsWidget ();
+ paintArea->drawPixmap (0, 0, widgetbild);
+ delete paintArea;
+}
diff --git a/wifi/status.h b/wifi/status.h
new file mode 100644
index 00000000..0c7f0d3e
--- /dev/null
+++ b/wifi/status.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ status.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 STATUS_H
+#define STATUS_H
+
+#include <qwidget.h>
+#include <qpainter.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qpixmap.h>
+
+class Interface_wireless;
+
+class Status:public QWidget
+{
+public:
+ Status (QWidget * parent, Interface_wireless * device);
+private:
+ void paintEvent (QPaintEvent *);
+ bool generateStatsWidget ();
+ QGroupBox *content;
+ QLabel *stat, *net_id, *ap_str, *ap_ad, *fr_str, *fr, *enc_str, *enc,
+ *local_ip_content, *local_ip_string;
+ QPixmap widgetbild, widgetbildalt;
+ Interface_wireless *device;
+};
+
+#endif
diff --git a/wifi/strength.cpp b/wifi/strength.cpp
new file mode 100644
index 00000000..0bb43f6c
--- /dev/null
+++ b/wifi/strength.cpp
@@ -0,0 +1,166 @@
+/***************************************************************************
+ strength.cpp - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 <qwidget.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qlabel.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include "strength.h"
+#include "interface_wireless.h"
+
+extern bool useAlternateStrengthCalc;
+
+Strength::Strength (QWidget * parent, Interface_wireless * device):QWidget (parent,
+ "strength")
+{
+ this->device = device;
+ KStandardDirs finder;
+ kdedir = finder.findResourceDir ("data", "kwifimanager/pics/no_card.png");
+ kdedir += "kwifimanager/pics/";
+ NOT_CON_OFFLINE_ADHOC = new QPixmap (kdedir + "offline.png");
+ EXCELLENT = new QPixmap (kdedir + "excellent.png");
+ GOOD = new QPixmap (kdedir + "good.png");
+ MARGINAL = new QPixmap (kdedir + "marginal.png");
+ OOR_MINIMUM = new QPixmap (kdedir + "oor_minimum.png");
+ OOR_DOWN = new QPixmap (kdedir + "oor_down.png");
+}
+
+QSize Strength::mySizeHint ()
+{
+ /* make this quite general, just in case someone uses different icons */
+ QSize* temp = new QSize(NOT_CON_OFFLINE_ADHOC->size());
+ if (EXCELLENT->size().width() > temp->width ()) { temp->setWidth (EXCELLENT->size().width ()); }
+ if (EXCELLENT->size().height() > temp->height()) { temp->setHeight(EXCELLENT->size().height()); }
+ if (GOOD->size().width() > temp->width ()) { temp->setWidth (GOOD->size().width ()); }
+ if (GOOD->size().height() > temp->height()) { temp->setHeight(GOOD->size().height()); }
+ if (MARGINAL->size().width() > temp->width ()) { temp->setWidth (MARGINAL->size().width ()); }
+ if (MARGINAL->size().height() > temp->height()) { temp->setHeight(MARGINAL->size().height()); }
+ if (OOR_MINIMUM->size().width() > temp->width ()) { temp->setWidth (OOR_MINIMUM->size().width ()); }
+ if (OOR_MINIMUM->size().height() > temp->height()) { temp->setHeight(OOR_MINIMUM->size().height()); }
+ if (OOR_DOWN->size().width() > temp->width ()) { temp->setWidth (OOR_DOWN->size().width ()); }
+ if (OOR_DOWN->size().height() > temp->height()) { temp->setHeight(OOR_DOWN->size().height()); }
+ // add a little more x-space: we need 50 pixels left and 50 pixels right of the icon
+ temp->setWidth (temp->width() + 100);
+ // add a little more y-space: we need 10 pixels above and 20 pixels below the icon
+ temp->setHeight(temp->height() + 30);
+ // up to now: this is the height required to get a decent logo display
+ // now determine height of QLabels to calculate the extra needed height
+ QLabel heightTest( "ABC", 0 );
+ temp->setHeight( temp->height() + 2 * heightTest.sizeHint().height() + 10 );
+ return *temp;
+}
+
+Strength::~Strength ()
+{
+ delete NOT_CON_OFFLINE_ADHOC;
+ delete GOOD;
+ delete MARGINAL;
+ delete OOR_MINIMUM;
+ delete OOR_DOWN;
+}
+
+void
+Strength::paintEvent (QPaintEvent * event)
+{
+ updateInfo (event);
+}
+
+void
+Strength::updateInfo (QPaintEvent *)
+{
+ QPainter *strengthpainter = new QPainter (this);
+ QString sstrength1, sstrength2( i18n("N/A") );
+ int mode;
+ double freq;
+
+ int sig, noi, strength;
+ bool validdata = device->get_current_quality (sig, noi, strength);
+ if (useAlternateStrengthCalc)
+ strength = sig - noi;
+
+ if ( device->get_txpower_disabled() )
+ {
+ sstrength1 = i18n("DISABLED");
+ strengthpainter->drawPixmap (50, 10, *NOT_CON_OFFLINE_ADHOC);
+ }
+ else if (!device->get_device_freq (freq))
+ {
+ sstrength1 = i18n("NOT CONNECTED");
+ strengthpainter->drawPixmap (50, 10, *NOT_CON_OFFLINE_ADHOC);
+ }
+ else if (device->get_mode(mode) && mode == 1)
+ {
+ sstrength1 = i18n("AD-HOC MODE");
+ strengthpainter->drawPixmap (50, 10, *NOT_CON_OFFLINE_ADHOC);
+ }
+ else if (validdata && (strength > 45))
+ {
+ sstrength1 = i18n("ULTIMATE");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *EXCELLENT);
+ }
+ else if (validdata && (strength > 35))
+ {
+ sstrength1 = i18n("TOP");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *EXCELLENT);
+ }
+ else if (validdata && (strength > 25))
+ {
+ sstrength1 = i18n("EXCELLENT");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *EXCELLENT);
+ }
+ else if (validdata && (strength > 15))
+ {
+ sstrength1 = i18n("GOOD");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *GOOD);
+ }
+ else if (validdata && (strength > 5))
+ {
+ sstrength1 = i18n("WEAK");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *MARGINAL);
+ }
+ else if (validdata && (strength > 0))
+ {
+ sstrength1 = i18n("MINIMUM");
+ sstrength2 = QString::number(strength);
+ strengthpainter->drawPixmap (50, 10, *OOR_MINIMUM);
+ }
+ else if (((strength == 0) && (mode == 2)) || (!validdata))
+ {
+ sstrength1 = i18n("OUT OF RANGE");
+ sstrength2 = QString::fromLatin1("0");
+ strengthpainter->drawPixmap (50, 10, *OOR_DOWN);
+ }
+ else
+ {
+ sstrength1 = i18n("N/A");
+ strengthpainter->drawPixmap (50, 10, *NOT_CON_OFFLINE_ADHOC);
+ };
+ sstrength2.prepend( i18n("Signal strength: ") );
+ strengthpainter->drawText (5, 55, sstrength1);
+ strengthpainter->drawText (5, 80, sstrength2);
+ delete strengthpainter;
+}
+
+#include "strength.moc"
diff --git a/wifi/strength.h b/wifi/strength.h
new file mode 100644
index 00000000..b03eb215
--- /dev/null
+++ b/wifi/strength.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+ strength.h - description
+ -------------------
+ begin : Mon Aug 19 2002
+ copyright : (C) 2002 by Stefan Winter
+ email : mail@stefan-winter.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 STRENGTH_H
+#define STRENGTH_H
+#include <qwidget.h>
+
+class Interface_wireless;
+class QSize;
+
+class Strength:public QWidget
+{
+Q_OBJECT
+ public:
+ Strength (QWidget * parent, Interface_wireless * device);
+ QSize mySizeHint();
+ ~Strength ();
+ QPixmap *NOT_CON_OFFLINE_ADHOC, *OOR_DOWN, *OOR_MINIMUM, *MARGINAL, *GOOD, *EXCELLENT;
+ public slots:
+ void updateInfo (QPaintEvent *);
+ private:
+ QString kdedir;
+ void paintEvent (QPaintEvent *);
+ Interface_wireless *device;
+};
+
+#endif